-
Notifications
You must be signed in to change notification settings - Fork 268
/
Copy pathindex.esm.js.map
1 lines (1 loc) · 247 KB
/
index.esm.js.map
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 maxUploadSize: {\n value: 3,\n type: 'number',\n cliName: 'maxUploadSize',\n envLink: 'SERVER_MAX_UPLOAD_SIZE',\n description:\n 'The maximum upload size, in megabytes, for the server'\n \n },\n enable: {\n value: false,\n type: 'boolean',\n envLink: 'SERVER_ENABLE',\n cliName: 'enableServer',\n description:\n 'When set to true, the server starts on the local IP address 0.0.0.0.'\n },\n host: {\n value: '0.0.0.0',\n type: 'string',\n envLink: 'SERVER_HOST',\n description:\n 'The hostname of the server. Additionally, it starts a server on the provided hostname.'\n },\n port: {\n value: 7801,\n type: 'number',\n envLink: 'SERVER_PORT',\n description: 'The server port when enabled.'\n },\n benchmarking: {\n value: false,\n type: 'boolean',\n envLink: 'SERVER_BENCHMARKING',\n cliName: 'serverBenchmarking',\n description:\n 'Indicates whether to display the duration, in milliseconds, of specific actions that occur on the server while serving a request.'\n },\n proxy: {\n host: {\n value: false,\n type: 'string',\n envLink: 'SERVER_PROXY_HOST',\n cliName: 'proxyHost',\n description: 'The host of the proxy server to use, if it exists.'\n },\n port: {\n value: 8080,\n type: 'number',\n envLink: 'SERVER_PROXY_PORT',\n cliName: 'proxyPort',\n description: 'The port of the proxy server to use, if it exists.'\n },\n username: {\n value: false,\n type: 'string',\n envLink: 'SERVER_PROXY_USERNAME',\n cliName: 'proxyUsername',\n description: 'The username for the proxy server, if it exists.'\n },\n password: {\n value: false,\n type: 'string',\n envLink: 'SERVER_PROXY_PASSWORD',\n cliName: 'proxyPassword',\n description: 'The password for the proxy server, if it exists.'\n },\n timeout: {\n value: 5000,\n type: 'number',\n envLink: 'SERVER_PROXY_TIMEOUT',\n cliName: 'proxyTimeout',\n description: 'The timeout for the proxy server to use, if it exists.'\n }\n },\n rateLimiting: {\n enable: {\n value: false,\n type: 'boolean',\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\n cliName: 'enableRateLimiting',\n description: 'Enables rate limiting for the server.'\n },\n maxRequests: {\n value: 10,\n type: 'number',\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\n legacyName: 'rateLimit',\n description: 'The maximum number of requests allowed in one minute.'\n },\n window: {\n value: 1,\n type: 'number',\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\n description: 'The time window, in minutes, for the rate limiting.'\n },\n 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_USERNAME: v.string(),\n SERVER_PROXY_PASSWORD: v.string(),\n SERVER_PROXY_TIMEOUT: v.nonNegativeNum(),\n\n // server rate limiting\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\n SERVER_RATE_LIMITING_MAX_REQUESTS: v.nonNegativeNum(),\n SERVER_RATE_LIMITING_WINDOW: v.nonNegativeNum(),\n SERVER_RATE_LIMITING_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<boolean>} 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<Object>} Promise resolving to the HTTP response object\n * with added 'text' property or rejecting with an error.\n */\nasync function fetch(url, requestOptions = {}) {\n return new Promise((resolve, reject) => {\n const protocol = getProtocol(url);\n\n protocol\n .get(\n url,\n Object.assign(\n {\n headers: {\n 'User-Agent': 'highcharts/export',\n Referer: 'highcharts/export'\n }\n },\n requestOptions || {}\n ),\n (res) => {\n let data = '';\n\n // A chunk of data has been received.\n res.on('data', (chunk) => {\n data += chunk;\n });\n\n // The whole response has been received.\n res.on('end', () => {\n if (!data) {\n reject('Nothing was fetched from the URL.');\n }\n\n res.text = data;\n resolve(res);\n });\n }\n )\n .on('error', (error) => {\n reject(error);\n });\n });\n}\n\n/**\n * Sends a POST request to the specified URL with the provided JSON body using\n * either HTTP or HTTPS protocol.\n *\n * @param {string} url - The URL to send the POST request to.\n * @param {Object} body - The JSON body to include in the POST request\n * (optional, default is an empty object).\n * @param {Object} requestOptions - Options for the HTTP request (optional).\n *\n * @returns {Promise<Object>} 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<string>} 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<string>} 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 { host, port, username, password } = proxyOptions;\n\n // Try to create a Proxy Agent\n if (host && port) {\n try {\n proxyAgent = new HttpsProxyAgent({\n host,\n port,\n ...(username && password ? { username, password } : {})\n });\n } catch (error) {\n throw new ExportError('[cache] Could not create a Proxy Agent.').setError(\n error\n );\n }\n }\n\n // If exists, add proxy agent to request options\n const requestOptions = proxyAgent\n ? {\n agent: proxyAgent,\n timeout: envs.SERVER_PROXY_TIMEOUT\n }\n : {};\n\n const 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<object>} 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<void>} A Promise that resolves once the cache is checked\n * and updated.\n *\n * @throws {ExportError} Throws an ExportError if there is an issue updating\n * or reading the cache.\n */\nexport const checkAndUpdateCache = async (options) => {\n const { highcharts, server } = options;\n const cachePath = join(__dirname, highcharts.cachePath);\n\n let fetchedModules;\n // Prepare paths to manifest and sources from the .cache folder\n const manifestPath = join(cachePath, 'manifest.json');\n const sourcePath = join(cachePath, 'sources.js');\n\n // Create the cache destination if it doesn't exist already\n !existsSync(cachePath) && mkdirSync(cachePath);\n\n // Fetch all the scripts either if manifest.json does not exist\n // or if the forceFetch option is enabled\n if (!existsSync(manifestPath) || highcharts.forceFetch) {\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\n fetchedModules = await updateCache(highcharts, server.proxy, sourcePath);\n } else {\n let requestUpdate = false;\n\n // Read the manifest JSON\n const manifest = JSON.parse(readFileSync(manifestPath));\n\n // Check if the modules is an array, if so, we rewrite it to a map to make\n // it easier to resolve modules.\n if (manifest.modules && Array.isArray(manifest.modules)) {\n const moduleMap = {};\n manifest.modules.forEach((m) => (moduleMap[m] = 1));\n manifest.modules = moduleMap;\n }\n\n const { coreScripts, moduleScripts, indicatorScripts } = highcharts;\n const numberOfModules =\n coreScripts.length + moduleScripts.length + indicatorScripts.length;\n\n // Compare the loaded highcharts config with the contents in cache.\n // If there are changes, fetch requested modules and products,\n // and bake them into a giant blob. Save the blob.\n if (manifest.version !== highcharts.version) {\n log(\n 2,\n '[cache] A Highcharts version mismatch in the cache, need to re-fetch.'\n );\n requestUpdate = true;\n } else if (Object.keys(manifest.modules || {}).length !== numberOfModules) {\n log(\n 2,\n '[cache] The cache and the requested modules do not match, need to re-fetch.'\n );\n requestUpdate = true;\n } else {\n // Check each module, if anything is missing refetch everything\n requestUpdate = (moduleScripts || []).some((moduleName) => {\n if (!manifest.modules[moduleName]) {\n log(\n 2,\n `[cache] The ${moduleName} is missing in the cache, need to re-fetch.`\n );\n return true;\n }\n });\n }\n\n if (requestUpdate) {\n fetchedModules = await updateCache(highcharts, server.proxy, sourcePath);\n } else {\n log(3, '[cache] Dependency cache is up to date, proceeding.');\n\n // Load the sources\n cache.sources = readFileSync(sourcePath, 'utf8');\n\n // Get current modules map\n fetchedModules = manifest.modules;\n\n cache.hcVersion = extractVersion(cache);\n }\n }\n\n // Finally, save the new manifest, which is basically our current config\n // in a slightly different format\n await saveConfigToManifest(highcharts, fetchedModules);\n};\n\nexport const getCachePath = () =>\n join(__dirname, getOptions().highcharts.cachePath);\n\nexport const getCache = () => cache;\n\nexport const highcharts = () => cache.sources;\n\nexport const version = () => cache.hcVersion;\n\nexport default {\n checkAndUpdateCache,\n getCachePath,\n updateVersion,\n getCache,\n highcharts,\n version\n};\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, Highsoft\n\nLicenced under the MIT licence.\n\nAdditionally a valid Highcharts license is required for use.\n\nSee LICENSE file in root for details.\n\n*******************************************************************************/\n\n/* eslint-disable no-undef */\n\n/**\n * Setting the animObject. Called when initing the page.\n */\nexport function setupHighcharts() {\n Highcharts.animObject = function () {\n return { duration: 0 };\n };\n}\n\n/**\n * Creates the actual chart.\n *\n * @param {object} chartOptions - The options for the Highcharts chart.\n * @param {object} options - The export options.\n * @param {boolean} displayErrors - A flag indicating whether to display errors.\n */\nexport async function triggerExport(chartOptions, options, displayErrors) {\n // Display errors flag taken from chart options nad debugger module\n window._displayErrors = displayErrors;\n\n // Get required functions\n const { getOptions, merge, setOptions, wrap } = Highcharts;\n\n // Create a separate object for a potential setOptions usages in order to\n // prevent from polluting other exports that can happen on the same page\n Highcharts.setOptionsObj = merge(false, {}, getOptions());\n\n // By default animation is disabled\n const chart = {\n animation: false\n };\n\n // When straight inject, the size is set through CSS only\n if (options.export.strInj) {\n chart.height = chartOptions.chart.height;\n chart.width = chartOptions.chart.width;\n }\n\n // NOTE: Is this used for anything useful?\n window.isRenderComplete = false;\n wrap(Highcharts.Chart.prototype, 'init', function (proceed, userOptions, cb) {\n // Override userOptions with image friendly options\n userOptions = merge(userOptions, {\n exporting: {\n enabled: false\n },\n plotOptions: {\n series: {\n label: {\n enabled: false\n }\n }\n },\n /* Expects tooltip in userOptions when forExport is true.\n https://github.com/highcharts/highcharts/blob/3ad430a353b8056b9e764aa4e5cd6828aa479db2/js/parts/Chart.js#L241\n */\n tooltip: {}\n });\n\n (userOptions.series || []).forEach(function (series) {\n series.animation = false;\n });\n\n // Add flag to know if chart render has been called.\n if (!window.onHighchartsRender) {\n window.onHighchartsRender = Highcharts.addEvent(this, 'render', () => {\n window.isRenderComplete = true;\n });\n }\n\n proceed.apply(this, [userOptions, cb]);\n });\n\n wrap(Highcharts.Series.prototype, 'init', function (proceed, chart, options) {\n proceed.apply(this, [chart, options]);\n });\n\n // Get the user options\n const userOptions = options.export.strInj\n ? new Function(`return ${options.export.strInj}`)()\n : chartOptions;\n\n // Trigger custom code\n if (options.customLogic.customCode) {\n new Function('options', options.customLogic.customCode)(userOptions);\n }\n\n // Merge the globalOptions, themeOptions, options from the wrapped\n // setOptions function and user options to create the final options object\n const finalOptions = merge(\n false,\n JSON.parse(options.export.themeOptions),\n userOptions,\n // Placed it here instead in the init because of the size issues\n { chart }\n );\n\n const finalCallback = options.customLogic.callback\n ? new Function(`return ${options.customLogic.callback}`)()\n : undefined;\n\n // Set the global options if exist\n const globalOptions = JSON.parse(options.export.globalOptions);\n if (globalOptions) {\n setOptions(globalOptions);\n }\n\n let constr = options.export.constr || 'chart';\n constr = typeof Highcharts[constr] !== 'undefined' ? constr : 'chart';\n\n Highcharts[constr]('container', finalOptions, finalCallback);\n\n // Get the current global options\n const defaultOptions = getOptions();\n\n // Clear it just in case (e.g. the setOptions was used in the customCode)\n for (const prop in defaultOptions) {\n if (typeof defaultOptions[prop] !== 'function') {\n delete defaultOptions[prop];\n }\n }\n\n // Set the default options back\n setOptions(Highcharts.setOptionsObj);\n\n // Empty the custom global options object\n Highcharts.setOptionsObj = {};\n}\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, Highsoft\n\nLicenced under the MIT licence.\n\nAdditionally a valid Highcharts license is required for use.\n\nSee LICENSE file in root for details.\n\n*******************************************************************************/\n\nimport { readFileSync } from 'fs';\nimport path from 'path';\n\nimport puppeteer from 'puppeteer';\n\nimport { getCachePath } from './cache.js';\nimport { getOptions } from './config.js';\nimport { setupHighcharts } from './highcharts.js';\nimport { log, logWithStack } from './logger.js';\nimport { __dirname } from './utils.js';\n\nimport ExportError from './errors/ExportError.js';\n\n// Get the template for the page\nconst template = readFileSync(__dirname + '/templates/template.html', 'utf8');\n\nlet browser;\n\n/**\n * Retrieves the existing Puppeteer browser instance.\n *\n * @returns {Promise<object>} 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<object>} 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<boolean>} 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 && !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 '<div id=\"chart-container\"><div id=\"container\"></div></div>';\n });\n }\n return true;\n }\n } catch (error) {\n logWithStack(\n 2,\n error,\n '[browser] Could not clear the content of the page.'\n );\n }\n\n return false;\n}\n\n/**\n * Adds custom JS and CSS resources to a Puppeteer Page based on the specified\n * options.\n *\n * @param {Object} page - The Puppeteer Page object to which resources will be\n * added.\n * @param {Object} options - All options and configuration.\n *\n * @returns {Promise<Array<Object>>} - 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<Object>} 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 // It would seem like this may fire at the same time or shortly before\n // a page is closed.\n if (page.isClosed()) {\n return;\n }\n\n // TODO: Consider adding a switch here that turns on log(0) logging\n // on page errors.\n await page.$eval(\n '#container',\n (element, errorMessage) => {\n // eslint-disable-next-line no-undef\n if (window._displayErrors) {\n element.innerHTML = errorMessage;\n }\n },\n `<h1>Chart input data error: </h1>${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<Object>} 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<Buffer>} 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<Buffer>} Promise resolving to the PDF buffer.\n */\nconst createPDF = async (\n page,\n height,\n width,\n encoding,\n rasterizationTimeout\n) => {\n await page.emulateMediaType('screen');\n\n return page.pdf({\n // This will remove an extra empty page in PDF exports\n height: height + 1,\n width,\n encoding,\n timeout: rasterizationTimeout || 1500\n });\n};\n\n/**\n * Creates an SVG string by evaluating the outerHTML of the first 'svg' element\n * inside an element with the id 'container'.\n *\n * @param {Object} page - Puppeteer page object.\n *\n * @returns {Promise<string>} 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<void>} 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<string | Buffer | ExportError>} 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('<svg') >= 0 || chart.indexOf('<?xml') >= 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.abs(\n Math.ceil(size.chartHeight || exportOptions.height)\n );\n const viewportWidth = Math.abs(\n Math.ceil(size.chartWidth || exportOptions.width)\n );\n\n // Get the clip region for the page\n const { x, y } = await getClipRegion(page);\n\n // Set the final viewport now that we have the real height\n await page.setViewport({\n height: viewportHeight,\n width: viewportWidth,\n deviceScaleFactor: isSVG ? 1 : parseFloat(exportOptions.scale)\n });\n\n let data;\n // Rasterization process\n if (exportOptions.type === 'svg') {\n // SVG\n data = await createSVG(page);\n } else if (['png', 'jpeg'].includes(exportOptions.type)) {\n // PNG or JPEG\n data = await createImage(\n page,\n exportOptions.type,\n 'base64',\n {\n width: viewportWidth,\n height: viewportHeight,\n x,\n y\n },\n exportOptions.rasterizationTimeout\n );\n } else if (exportOptions.type === 'pdf') {\n // PDF\n data = await createPDF(\n page,\n viewportHeight,\n viewportWidth,\n 'base64',\n exportOptions.rasterizationTimeout\n );\n } else {\n throw new ExportError(\n `[export] Unsupported output format ${exportOptions.type}.`\n );\n }\n\n // Clear previously injected JS and CSS resources\n await clearPageResources(page, injectedResources);\n return data;\n } catch (error) {\n await clearPageResources(page, injectedResources);\n return error;\n }\n};\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, Highsoft\n\nLicenced under the MIT licence.\n\nAdditionally a valid Highcharts license is required for use.\n\nSee LICENSE file in root for details.\n\n*******************************************************************************/\n\nimport cssTemplate from './css.js';\n\nexport default (chart) => `\n<!DOCTYPE html>\n<html lang='en-US'>\n <head>\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n <title>Highcharts Export</title>\n </head>\n <style>\n ${cssTemplate()}\n </style>\n <body>\n <div id=\"chart-container\">\n ${chart}\n </div>\n </body>\n</html>\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 // NOTE: In certain cases acquiring throws a TargetCloseError, which may\n // be caused by two things:\n // - The page is closed and attempted to be reused.\n // - Lost contact with the browser\n // What we're seeing in logs is that successive exports typically\n // succeeds, and the server recovers, indicating that it's likely\n // the first case. This is an attempt at allievating the issue by\n // simply not validating the worker if the page is null or closed.\n //\n // The actual result from when this happened, was that a worker would\n // be completely locked, stopping it from being acquired until\n // its work count reached the limit.\n if (!workerHandle.page || workerHandle.page?.isClosed()) {\n return false;\n }\n\n if (\n poolConfig.workLimit &&\n ++workerHandle.workCount > poolConfig.workLimit\n ) {\n log(\n 3,\n `[pool] Worker failed validation: exceeded work limit (limit is ${poolConfig.workLimit}).`\n );\n return false;\n }\n return true;\n },\n\n /**\n * Destroys a worker entry in the export pool, closing its associated page.\n *\n * @param {Object} workerHandle - The handle to the worker, containing\n * the worker's ID and a reference to the browser page.\n */\n destroy: async (workerHandle) => {\n log(3, `[pool] Destroying pool entry ${workerHandle.id}.`);\n\n if (workerHandle.page && !workerHandle.page.isClosed()) {\n await workerHandle.page.close();\n }\n }\n\n // log: (message, level) => log(1, '[tarn] ' + message)\n};\n\n/**\n * Initializes the export pool with the provided configuration, creating\n * a browser instance and setting up worker resources.\n *\n * @param {Object} config - Configuration options for the export pool along\n * with custom puppeteer arguments for the puppeteer.launch function.\n */\nexport const initPool = async (config) => {\n // For the module scope usage\n poolConfig = config && config.pool ? { ...config.pool } : {};\n\n // 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 const r = await clearPage(resource.page, false);\n log(\n 4,\n `[pool] Releasing a worker with ID ${resource.id}. Clear page status: ${r}.`\n );\n });\n\n pool.on('destroySuccess', (eventId, resource) => {\n log(4, `[pool] Destroyed a worker with ID ${resource.id}.`);\n resource.page = null;\n });\n\n const initialResources = [];\n // Create an initial number of resources\n for (let i = 0; i < poolConfig.minWorkers; i++) {\n try {\n const resource = await pool.acquire().promise;\n initialResources.push(resource);\n } catch (error) {\n logWithStack(2, error, '[pool] Could not create an initial resource.');\n }\n }\n\n // Release the initial number of resources back to the pool\n initialResources.forEach((resource) => {\n pool.release(resource);\n });\n\n log(\n 3,\n `[pool] The pool is ready${initialResources.length ? ` with ${initialResources.length} initial resources waiting.` : '.'}`\n );\n } catch (error) {\n throw new ExportError(\n '[pool] Could not create the pool of workers.'\n ).setError(error);\n }\n};\n\n/**\n * Kills all workers in the pool, destroys the pool, and closes the browser\n * instance.\n *\n * @returns {Promise<void>} 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<Object>} 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 // NOTE: If there's a rasterization timeout, we want need to flush the page.\n // This is because the page may be in a state where it's waiting for\n // the screenshot to finish even though the timeout has occured.\n // Which of course causes a lot of issues with the event system,\n // and page consistency.\n //\n // NOTE: Only page.screenshot will throw this, timeouts for PDF's are\n // handled by the page.pdf function itself.\n //\n // ...yes, this is ugly.\n if (result.message === 'Rasterization timeout') {\n workerHandle.workCount = poolConfig.workLimit + 1;\n workerHandle.page = null;\n }\n\n if (\n result.name === 'TimeoutError' ||\n result.message === 'Rasterization timeout'\n ) {\n throw new ExportError(\n 'Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.'\n ).setError(result);\n } else {\n throw new ExportError(\n (options.payload?.requestId\n ? `For request with ID ${options.payload?.requestId} - `\n : '') + `Error encountered during export: ${exportCounter()}ms.`\n ).setError(result);\n }\n }\n\n // Check the Puppeteer export time\n if (options.server.benchmarking) {\n log(\n 5,\n options.payload?.requestId\n ? `[benchmark] Request with ID ${options.payload?.requestId} -`\n : '[benchmark]',\n `Exported a chart sucessfully: ${exportCounter()}ms.`\n );\n }\n\n // Release the resource back to the pool\n pool.release(workerHandle);\n\n // Used for statistics in averageTime and processedWorkCount, which\n // in turn is used by the /health route.\n const workEnd = new Date().getTime();\n const exportTime = workEnd - workStart;\n stats.timeSpent += exportTime;\n stats.spentAverage = stats.timeSpent / ++stats.performedExports;\n\n log(4, `[pool] Work completed in ${exportTime} ms.`);\n\n // Otherwise return the result\n return {\n result,\n options\n };\n } catch (error) {\n ++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<void>} 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<void>} 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<void>} 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('<svg') >= 0 ||\n stringToExport.indexOf('<?xml') >= 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 <script> tags.\n * This function uses a regular expression to find and remove all\n * occurrences of <script>...</script> 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, {\n ADD_TAGS: ['foreignObject'],\n // Dissalow all xlinks in incoming SVG\n FORBID_ATTR: ['xlink:href']\n });\n}\n\nexport default sanitize;\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, Highsoft\n\nLicenced under the MIT licence.\n\nAdditionally a valid Highcharts license is required for use.\n\nSee LICENSE file in root for details.\n\n*******************************************************************************/\n\nimport { 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 || 400;\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<void>} - 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:\n Content-Type: ${request.headers['content-type']}. \n Chart constructor: ${body.constr}.\n Dimensions: ${body.width}x${body.height} @ ${body.scale} scale.\n Type: ${type}.\n Is SVG set? ${typeof body.svg !== 'undefined'}.\n B64? ${typeof body.b64 !== 'undefined'}.\n No download? ${typeof body.noDownload !== 'undefined'}.\n\n Payload received: ${JSON.stringify(body.infile || body.options || body.data || body.svg)}\n\n `\n );\n\n throw new HttpError(\n \"No correct chart data found. Ensure that you are using either application/json or multipart/form-data headers. If sending JSON, make sure the chart data is in the 'infile', 'options', or 'data' attribute. If sending SVG, ensure it is in the 'svg' attribute.\",\n 400\n );\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', (hadErrors) => {\n if (hadErrors) {\n connectionAborted = true;\n }\n });\n\n log(4, `[export] Got an incoming HTTP request with ID ${uniqueId}.`);\n\n body.constr = (typeof body.constr === 'string' && body.constr) || 'chart';\n\n // Gather and organize options from the payload\n const requestOptions = {\n export: {\n instr,\n type,\n constr: body.constr[0].toLowerCase() + body.constr.substr(1),\n height: body.height,\n width: body.width,\n scale: body.scale || defaultOptions.export.scale,\n globalOptions: isCorrectJSON(body.globalOptions, true),\n themeOptions: isCorrectJSON(body.themeOptions, true)\n },\n customLogic: {\n allowCodeExecution: getAllowCodeExecution(),\n allowFileResources: false,\n resources: isCorrectJSON(body.resources, true),\n callback: body.callback,\n customCode: body.customCode\n }\n };\n\n if (instr) {\n // Stringify JSON with options\n requestOptions.export.instr = optionsStringify(\n instr,\n requestOptions.customLogic.allowCodeExecution\n );\n }\n\n // Merge the request options into default ones\n const options = mergeConfigOptions(defaultOptions, requestOptions);\n\n // Save the JSON if exists\n options.export.options = instr;\n\n // Lastly, add the server specific arguments into options as payload\n options.payload = {\n svg: body.svg || false,\n b64: body.b64 || false,\n noDownload: body.noDownload || false,\n requestId: uniqueId\n };\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// Getting a lot of RangeNotSatisfiableError exception.\n// Even though this is a deprecated options, let's try to set it to false.\napp.use((_req, res, next) => {\n res.set('Accept-Ranges', 'none');\n next();\n});\n\n/**\n * Attach error handlers to the server.\n *\n * @param {http.Server} server - The HTTP/HTTPS server instance.\n */\nconst attachServerErrorHandlers = (server) => {\n server.on('clientError', (error, socket) => {\n logWithStack(\n 1,\n error,\n `[server] Client error: ${error.message}, destroying socket.`\n );\n socket.destroy();\n });\n\n server.on('error', (error) => {\n logWithStack(1, error, `[server] Server error: ${error.message}`);\n });\n\n server.on('connection', (socket) => {\n socket.on('error', (error) => {\n logWithStack(1, error, `[server] Socket error: ${error.message}`);\n });\n });\n};\n\n/**\n * Starts an HTTP server based on the provided configuration. The `serverConfig`\n * object contains all server related properties (see the `server` section\n * in the `lib/schemas/config.js` file for a reference).\n *\n * @param {Object} serverConfig - The server configuration object.\n *\n * @throws {ExportError} - Throws an error if the server cannot be configured\n * and started.\n */\nexport const startServer = async (serverConfig) => {\n try {\n // TODO: Read from config/env\n // NOTE:\n // Too big limits lead to timeouts in the export process when the\n // rasterization timeout is set too low.\n const uploadLimitMiB = serverConfig.maxUploadSize || 3;\n const uploadLimitBytes = uploadLimitMiB * 1024 * 1024;\n\n // Enable parsing of form data (files) with Multer package\n const storage = multer.memoryStorage();\n const upload = multer({\n storage,\n limits: {\n fieldSize: uploadLimitBytes\n }\n });\n\n // Enable body parser\n app.use(express.json({ limit: uploadLimitBytes }));\n app.use(express.urlencoded({ extended: true, limit: uploadLimitBytes }));\n\n // Use only non-file multipart form fields\n app.use(upload.none());\n\n // Stop if not enabled\n if (!serverConfig.enable) {\n return false;\n }\n\n // Listen HTTP server\n if (!serverConfig.ssl.force) {\n // Main server instance (HTTP)\n const httpServer = http.createServer(app);\n\n // Attach error handlers and listen to the server\n attachServerErrorHandlers(httpServer);\n\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 acceptRanges: false\n });\n });\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, Highsoft\n\nLicenced under the MIT licence.\n\nAdditionally a valid Highcharts license is required for use.\n\nSee LICENSE file in root for details.\n\n*******************************************************************************/\n\nimport { 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<Object>} 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","maxUploadSize","cliName","enable","host","port","benchmarking","proxy","username","password","timeout","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","ssl","force","certPath","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","logging","level","file","dest","toConsole","toFile","ui","route","other","nodeEnv","listenToProcessExits","noLogo","hardResetPage","browserShellMode","debug","headless","devtools","listenToConsole","dumpio","slowMo","debuggingPort","promptsConfig","name","message","initial","join","separator","instructions","choices","hint","min","max","round","absoluteProps","nestedArgs","createNestedArgs","obj","propChain","Object","keys","forEach","k","includes","entry","substring","undefined","dotenv","config","v","filterArray","z","string","transform","split","map","trim","filter","length","enum","values","refine","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_USERNAME","SERVER_PROXY_PASSWORD","SERVER_PROXY_TIMEOUT","SERVER_RATE_LIMITING_ENABLE","SERVER_RATE_LIMITING_MAX_REQUESTS","SERVER_RATE_LIMITING_WINDOW","SERVER_RATE_LIMITING_DELAY","SERVER_RATE_LIMITING_TRUST_PROXY","SERVER_RATE_LIMITING_SKIP_KEY","SERVER_RATE_LIMITING_SKIP_TOKEN","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","POOL_MIN_WORKERS","POOL_MAX_WORKERS","POOL_WORK_LIMIT","POOL_ACQUIRE_TIMEOUT","POOL_CREATE_TIMEOUT","POOL_DESTROY_TIMEOUT","POOL_IDLE_TIMEOUT","POOL_CREATE_RETRY_INTERVAL","POOL_REAPER_INTERVAL","POOL_BENCHMARKING","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","LOGGING_TO_CONSOLE","LOGGING_TO_FILE","UI_ENABLE","UI_ROUTE","OTHER_NODE_ENV","OTHER_LISTEN_TO_PROCESS_EXITS","OTHER_NO_LOGO","OTHER_HARD_RESET_PAGE","OTHER_BROWSER_SHELL_MODE","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","headers","Referer","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","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","chart","animation","strInj","isRenderComplete","Chart","proceed","userOptions","cb","exporting","enabled","plotOptions","series","label","tooltip","onHighchartsRender","addEvent","Series","Function","finalOptions","finalCallback","defaultOptions","prop","template","browser","newPage","page","setCacheEnabled","setPageContent","isClosed","$eval","element","errorMessage","innerHTML","setPageEvents","clearPageResources","injectedResources","resource","dispose","evaluate","oldCharts","charts","oldChart","destroy","scriptsToRemove","document","getElementsByTagName","stylesToRemove","linksToRemove","remove","setContent","waitUntil","addScriptTag","path","setAsConfig","puppeteerExport","exportOptions","debugger","isSVG","svgTemplate","injectedJs","js","push","content","isLocal","jsResource","injectedCss","css","cssImports","match","cssImportPath","cssResource","addStyleTag","addPageResources","size","svgElement","querySelector","chartHeight","baseVal","chartWidth","body","style","zoom","margin","viewportHeight","Math","abs","ceil","viewportWidth","x","y","getBoundingClientRect","trunc","getClipRegion","setViewport","deviceScaleFactor","outerHTML","createSVG","encoding","clip","race","screenshot","captureBeyondViewport","fullPage","optimizeForSpeed","quality","omitBackground","_resolve","setTimeout","createImage","emulateMediaType","pdf","createPDF","stats","performedExports","exportAttempts","exportFromSvgAttempts","timeSpent","droppedExports","spentAverage","poolConfig","factory","create","id","uuid","startDate","getTime","workCount","random","validate","workerHandle","close","initPool","puppeteerArgs","enabledDebug","debugOptions","launchOptions","userDataDir","handleSIGINT","handleSIGTERM","handleSIGHUP","waitForInitialPage","defaultViewport","tryCount","open","launch","createBrowser","parseInt","Pool","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","r","hardReset","goto","clearPage","eventId","initialResources","acquire","promise","release","killPool","worker","used","destroyed","connected","closeBrowser","postWork","getPoolInfo","acquireCounter","payload","requestId","workStart","exportCounter","result","exportTime","getPoolInfoJSON","numFree","numUsed","available","pending","numPendingAcquires","pool$1","startExport","settings","endCallback","svg","initExportSettings","exportAsString","input","JSDOM","DOMPurify","sanitize","ADD_TAGS","FORBID_ATTR","doStraightInject","doExport","findChartSize","precision","multiplier","pow","roundNumber","sourceHeight","sourceWidth","param","chartJson","customLogicOptions","allowCodeExecutionScoped","optionsName","stringToExport","chartJSON","intervalIds","clearAllIntervals","clearInterval","logErrorMiddleware","req","next","returnErrorMiddleware","stCode","status","json","rateLimit","app","limitConfig","msg","rateOptions","limiter","windowMs","delayMs","handler","request","format","send","default","skip","query","access_token","use","HttpError","setStatus","vSwitchRoute","post","adminToken","token","newVersion","params","updateVersion","reversedMime","png","jpeg","gif","requestsCounter","beforeRequest","afterRequest","doCallbacks","callbacks","uniqueId","callResponse","exportHandler","stopCounter","connection","remoteAddress","b64","noDownload","connectionAborted","socket","hadErrors","toLowerCase","substr","pattern","isPrivateRangeUrlFound","info","removeAllListeners","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","_req","set","attachServerErrorHandlers","startServer","serverConfig","uploadLimitBytes","storage","multer","memoryStorage","upload","limits","fieldSize","limit","urlencoded","extended","none","httpServer","createServer","listen","cert","fsPromises","readFile","posix","httpsServer","NaN","static","healthRoute","exportRoutes","_request","sendFile","acceptRanges","uiRoute","errorHandler","closeServers","delete","getServers","enableRateLimiting","getExpress","getApp","middlewares","shutdownCleanUp","exitCode","allSettled","exit","index","initExport","loggingOptions","initLogging","code","singleExport","batchExport","batchFunctions","pair","configIndex","findIndex","arg","fileName","loadConfigFile","showUsage","propertiesChain","argumentType","pairArgumentValue","mapToNewConfig","oldOptions","manualConfig","configFileName","configFile","choice","prompts","onSubmit","p","categories","questionsCounter","allQuestions","section","prompt","answer","module","writeFile","printLogo","packageVersion"],"mappings":"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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"}