From bf1775ded6a5ee9f68bb6e1e5c91d5e1ffbad540 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Tue, 17 Dec 2024 19:03:39 +0100 Subject: [PATCH 01/39] feat(plugin-doc-coverage): add mvp version of a plugin doc coverage based on compodoc --- packages/plugin-doc-coverage/README.md | 205 ++++++++++++++++++ packages/plugin-doc-coverage/eslint.config.js | 21 ++ .../mocks/documentation.json | 11 + packages/plugin-doc-coverage/package.json | 47 ++++ packages/plugin-doc-coverage/project.json | 42 ++++ packages/plugin-doc-coverage/src/bin.ts | 3 + packages/plugin-doc-coverage/src/index.ts | 4 + .../plugin-doc-coverage/src/lib/config.ts | 25 +++ .../src/lib/config.unit.test.ts | 52 +++++ .../src/lib/doc-coverage-plugin.ts | 76 +++++++ .../src/lib/doc-coverage-plugin.unit.test.ts | 87 ++++++++ .../src/lib/runner/constants.ts | 11 + .../src/lib/runner/index.ts | 82 +++++++ .../src/lib/runner/runner.integration.test.ts | 80 +++++++ packages/plugin-doc-coverage/tsconfig.json | 23 ++ .../plugin-doc-coverage/tsconfig.lib.json | 16 ++ .../plugin-doc-coverage/tsconfig.test.json | 13 ++ .../vite.config.integration.ts | 29 +++ .../plugin-doc-coverage/vite.config.unit.ts | 31 +++ 19 files changed, 858 insertions(+) create mode 100644 packages/plugin-doc-coverage/README.md create mode 100644 packages/plugin-doc-coverage/eslint.config.js create mode 100644 packages/plugin-doc-coverage/mocks/documentation.json create mode 100644 packages/plugin-doc-coverage/package.json create mode 100644 packages/plugin-doc-coverage/project.json create mode 100644 packages/plugin-doc-coverage/src/bin.ts create mode 100644 packages/plugin-doc-coverage/src/index.ts create mode 100644 packages/plugin-doc-coverage/src/lib/config.ts create mode 100644 packages/plugin-doc-coverage/src/lib/config.unit.test.ts create mode 100644 packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts create mode 100644 packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts create mode 100644 packages/plugin-doc-coverage/src/lib/runner/constants.ts create mode 100644 packages/plugin-doc-coverage/src/lib/runner/index.ts create mode 100644 packages/plugin-doc-coverage/src/lib/runner/runner.integration.test.ts create mode 100644 packages/plugin-doc-coverage/tsconfig.json create mode 100644 packages/plugin-doc-coverage/tsconfig.lib.json create mode 100644 packages/plugin-doc-coverage/tsconfig.test.json create mode 100644 packages/plugin-doc-coverage/vite.config.integration.ts create mode 100644 packages/plugin-doc-coverage/vite.config.unit.ts diff --git a/packages/plugin-doc-coverage/README.md b/packages/plugin-doc-coverage/README.md new file mode 100644 index 000000000..0b4fb33e6 --- /dev/null +++ b/packages/plugin-doc-coverage/README.md @@ -0,0 +1,205 @@ +# @code-pushup/doc-coverage-plugin + +[![npm](https://img.shields.io/npm/v/%40code-pushup%2Fdoc-coverage-plugin.svg)](https://www.npmjs.com/package/@code-pushup/doc-coverage-plugin) +[![downloads](https://img.shields.io/npm/dm/%40code-pushup%2Fdoc-coverage-plugin)](https://npmtrends.com/@code-pushup/doc-coverage-plugin) +[![dependencies](https://img.shields.io/librariesio/release/npm/%40code-pushup%2Fdoc-coverage-plugin)](https://www.npmjs.com/package/@code-pushup/doc-coverage-plugin?activeTab=dependencies) + +📚 **Code PushUp plugin for tracking documentation coverage.** 📝 + +This plugin allows you to measure and track documentation coverage in your TypeScript/JavaScript project. +It analyzes your codebase and checks for documentation on different code elements like classes, functions, interfaces, types, and variables. + +Measured documentation types are mapped to Code PushUp audits in the following way: + +- The value is in range 0-100 and represents the documentation coverage for all passed results (_documented / total_) +- The score is value converted to 0-1 range +- Missing documentation is mapped to issues in the audit details (undocumented classes, functions, interfaces, etc.) + +## Getting started + +1. If you haven't already, install [@code-pushup/cli](../cli/README.md) and create a configuration file. + +2. Install as a dev dependency with your package manager: + + ```sh + npm install --save-dev @code-pushup/doc-coverage-plugin + ``` + + ```sh + yarn add --dev @code-pushup/doc-coverage-plugin + ``` + + ```sh + pnpm add --save-dev @code-pushup/doc-coverage-plugin + ``` + +3. Add Compodoc to your project. You can follow the instructions [here](https://compodoc.app/guides/installation.html). + +4. Add this plugin to the `plugins` array in your Code PushUp CLI config file (e.g. `code-pushup.config.js`). + + Pass the target files to analyze and optionally specify which types of documentation you want to track. + All documentation types are measured by default. If you wish to focus on a subset of offered types, define them in `docTypes`. + + The configuration will look similarly to the following: + + ```js + import docCoveragePlugin from '@code-pushup/doc-coverage-plugin'; + + export default { + // ... + plugins: [ + // ... + await docCoveragePlugin({ + coverageToolCommand: { + command: 'npx', + args: ['compodoc', '-p', 'tsconfig.doc.json', '-e', 'json'], + }, + }), + ], + }; + ``` + +5. (Optional) Reference individual audits or the provided plugin group which you wish to include in custom categories (use `npx code-pushup print-config` to list audits and groups). + + 💡 Assign weights based on what influence each documentation type should have on the overall category score (assign weight 0 to only include as extra info, without influencing category score). + + ```js + export default { + // ... + categories: [ + { + slug: 'documentation', + title: 'Documentation', + refs: [ + { + type: 'group', + plugin: 'doc-coverage', + slug: 'doc-coverage', + weight: 1, + }, + // ... + ], + }, + // ... + ], + }; + ``` + +6. Run the CLI with `npx code-pushup collect` and view or upload report (refer to [CLI docs](../cli/README.md)). + +## About documentation coverage + +Documentation coverage is a metric that indicates what percentage of your code elements have proper documentation. It helps ensure your codebase is well-documented and maintainable. + +The plugin provides a single audit that measures the overall percentage of documentation coverage across your codebase: + +- **Percentage coverage**: Measures how many percent of the codebase have documentation. + +## Plugin architecture + +### Plugin configuration specification + +The plugin accepts the following parameters: + +- (optional) `coverageToolCommand`: If you wish to run your documentation coverage tool (compodoc) to generate the results first, you may define it here. + - `command`: Command to run coverage tool (e.g. `npx`). + - `args`: Arguments to be passed to the coverage tool (e.g. `['compodoc', '-p', 'tsconfig.doc.json', '-e', 'json']`). +- `outputPath`: Path to the documentation.json file. Defaults to `'documentation/documentation.json'`. + +### Audits and group + +This plugin provides a group for convenient declaration in your config. When defined this way, all measured documentation type audits have the same weight. + +```ts + // ... + categories: [ + { + slug: 'documentation', + title: 'Documentation', + refs: [ + { + type: 'group', + plugin: 'doc-coverage', + slug: 'doc-coverage', + weight: 1, + }, + // ... + ], + }, + // ... + ], +``` + +Each documentation type still has its own audit. So when you want to include a subset of documentation types or assign different weights to them, you can do so in the following way: + +```ts + // ... + categories: [ + { + slug: 'documentation', + title: 'Documentation', + refs: [ + { + type: 'audit', + plugin: 'doc-coverage', + slug: 'class-doc-coverage', + weight: 2, + }, + { + type: 'audit', + plugin: 'doc-coverage', + slug: 'function-doc-coverage', + weight: 1, + }, + // ... + ], + }, + // ... + ], +``` + +### Audit output + +The plugin outputs a single audit that measures the overall documentation coverage percentage of your codebase. + +For instance, this is an example of the plugin output: + +```json +{ + "packageName": "@code-pushup/doc-coverage-plugin", + "version": "0.57.0", + "title": "Documentation coverage", + "slug": "doc-coverage", + "icon": "folder-src", + "duration": 920, + "date": "2024-12-17T16:45:28.581Z", + "audits": [ + { + "slug": "percentage-coverage", + "displayValue": "16 %", + "value": 16, + "score": 0.16, + "details": { + "issues": [] + }, + "title": "Percentage of codebase with documentation", + "description": "Measures how many % of the codebase have documentation." + } + ], + "description": "Official Code PushUp documentation coverage plugin.", + "docsUrl": "https://www.npmjs.com/package/@code-pushup/doc-coverage-plugin/", + "groups": [ + { + "slug": "doc-coverage", + "refs": [ + { + "slug": "percentage-coverage", + "weight": 1 + } + ], + "title": "Documentation coverage metrics", + "description": "Group containing all defined documentation coverage types as audits." + } + ] +} +``` diff --git a/packages/plugin-doc-coverage/eslint.config.js b/packages/plugin-doc-coverage/eslint.config.js new file mode 100644 index 000000000..40165321a --- /dev/null +++ b/packages/plugin-doc-coverage/eslint.config.js @@ -0,0 +1,21 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config( + ...baseConfig, + { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + files: ['**/*.json'], + rules: { + '@nx/dependency-checks': 'error', + }, + }, +); diff --git a/packages/plugin-doc-coverage/mocks/documentation.json b/packages/plugin-doc-coverage/mocks/documentation.json new file mode 100644 index 000000000..7a608253c --- /dev/null +++ b/packages/plugin-doc-coverage/mocks/documentation.json @@ -0,0 +1,11 @@ +{ + "coverage": { + "count": 85, + "files": { + "src/app/services/my.service.ts": { + "documented": 17, + "total": 20 + } + } + } +} diff --git a/packages/plugin-doc-coverage/package.json b/packages/plugin-doc-coverage/package.json new file mode 100644 index 000000000..1c2ebe241 --- /dev/null +++ b/packages/plugin-doc-coverage/package.json @@ -0,0 +1,47 @@ +{ + "name": "@code-pushup/doc-coverage-plugin", + "version": "0.57.0", + "description": "Code PushUp plugin for tracking documentation coverage 📚", + "license": "MIT", + "homepage": "https://github.com/code-pushup/cli/tree/main/packages/plugin-doc-coverage#readme", + "bugs": { + "url": "https://github.com/code-pushup/cli/issues?q=is%3Aissue%20state%3Aopen%20type%3ABug%20label%3A\"🧩%20doc-coverage-plugin\"" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/code-pushup/cli.git", + "directory": "packages/plugin-doc-coverage" + }, + "keywords": [ + "CLI", + "Code PushUp", + "plugin", + "automation", + "developer tools", + "conformance", + "documentation coverage", + "documentation", + "docs", + "KPI tracking", + "automated feedback", + "regression guard", + "actionable feedback", + "audit", + "score monitoring" + ], + "publishConfig": { + "access": "public" + }, + "type": "module", + "dependencies": { + "@code-pushup/models": "0.57.0", + "@code-pushup/utils": "0.57.0", + "ansis": "^3.3.0", + "zod": "^3.22.4" + }, + "peerDependenciesMeta": { + "@nx/devkit": { + "optional": true + } + } +} diff --git a/packages/plugin-doc-coverage/project.json b/packages/plugin-doc-coverage/project.json new file mode 100644 index 000000000..ea971b133 --- /dev/null +++ b/packages/plugin-doc-coverage/project.json @@ -0,0 +1,42 @@ +{ + "name": "plugin-doc-coverage", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/plugin-doc-coverage/src", + "projectType": "library", + "targets": { + "build": { + "executor": "@nx/js:tsc", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/packages/plugin-doc-coverage", + "main": "packages/plugin-doc-coverage/src/index.ts", + "tsConfig": "packages/plugin-doc-coverage/tsconfig.lib.json", + "additionalEntryPoints": ["packages/plugin-doc-coverage/src/bin.ts"], + "assets": ["packages/plugin-doc-coverage/*.md"] + } + }, + "lint": { + "executor": "@nx/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": [ + "packages/plugin-doc-coverage/**/*.ts", + "packages/plugin-doc-coverage/package.json" + ] + } + }, + "unit-test": { + "executor": "@nx/vite:test", + "options": { + "configFile": "packages/plugin-doc-coverage/vite.config.unit.ts" + } + }, + "integration-test": { + "executor": "@nx/vite:test", + "options": { + "configFile": "packages/plugin-doc-coverage/vite.config.integration.ts" + } + } + }, + "tags": ["scope:plugin", "type:feature", "publishable"] +} diff --git a/packages/plugin-doc-coverage/src/bin.ts b/packages/plugin-doc-coverage/src/bin.ts new file mode 100644 index 000000000..bf6572a76 --- /dev/null +++ b/packages/plugin-doc-coverage/src/bin.ts @@ -0,0 +1,3 @@ +import { executeRunner } from './lib/runner/index.js'; + +await executeRunner(); diff --git a/packages/plugin-doc-coverage/src/index.ts b/packages/plugin-doc-coverage/src/index.ts new file mode 100644 index 000000000..d26f57375 --- /dev/null +++ b/packages/plugin-doc-coverage/src/index.ts @@ -0,0 +1,4 @@ +import { docCoveragePlugin } from './lib/doc-coverage-plugin.js'; + +export default docCoveragePlugin; +export type { DocCoveragePluginConfig } from './lib/config.js'; diff --git a/packages/plugin-doc-coverage/src/lib/config.ts b/packages/plugin-doc-coverage/src/lib/config.ts new file mode 100644 index 000000000..139f2d77a --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/config.ts @@ -0,0 +1,25 @@ +import { z } from 'zod'; + +export type DocType = 'percentage-coverage'; + +export const docCoveragePluginConfigSchema = z.object({ + coverageToolCommand: z + .object({ + command: z + .string({ description: 'Command to run coverage tool (compodoc).' }) + .min(1), + args: z + .array(z.string(), { + description: 'Arguments to be passed to the coverage tool.', + }) + .optional(), + }) + .optional(), + outputPath: z + .string({ description: 'Path to the documentation.json file.' }) + .default('documentation/documentation.json'), +}); + +export type DocCoveragePluginConfig = z.infer< + typeof docCoveragePluginConfigSchema +>; diff --git a/packages/plugin-doc-coverage/src/lib/config.unit.test.ts b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts new file mode 100644 index 000000000..3964fa186 --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, it } from 'vitest'; +import { + type DocCoveragePluginConfig, + docCoveragePluginConfigSchema, +} from './config.js'; + +describe('docCoveragePluginConfigSchema', () => { + it('accepts a documentation coverage configuration with all entities', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + coverageToolCommand: { + command: 'npx @compodoc/compodoc', + args: ['-p', 'tsconfig.json'], + }, + outputPath: 'documentation/custom-doc.json', + } satisfies DocCoveragePluginConfig), + ).not.toThrow(); + }); + + it('accepts a minimal documentation coverage configuration', () => { + expect(() => + docCoveragePluginConfigSchema.parse({} satisfies DocCoveragePluginConfig), + ).not.toThrow(); + }); + + it('uses default output path when not provided', () => { + const config = {} satisfies DocCoveragePluginConfig; + const parsed = docCoveragePluginConfigSchema.parse(config); + + expect(parsed.outputPath).toBe('documentation/documentation.json'); + }); + + it('throws for missing command in coverageToolCommand', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + coverageToolCommand: { + args: ['-p', 'tsconfig.json'], + }, + }), + ).toThrow('invalid_type'); + }); + + it('accepts empty args in coverageToolCommand', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + coverageToolCommand: { + command: 'npx @compodoc/compodoc', + }, + } satisfies DocCoveragePluginConfig), + ).not.toThrow(); + }); +}); diff --git a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts new file mode 100644 index 000000000..0519eed82 --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts @@ -0,0 +1,76 @@ +import { createRequire } from 'node:module'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import type { Group, PluginConfig } from '@code-pushup/models'; +import { + type DocCoveragePluginConfig, + docCoveragePluginConfigSchema, +} from './config.js'; +import { createRunnerConfig } from './runner/index.js'; + +/** + * Instantiates Code PushUp documentation coverage plugin for core config. + * + * @example + * import docCoveragePlugin from '@code-pushup/doc-coverage-plugin' + * + * export default { + * // ... core config ... + * plugins: [ + * // ... other plugins ... + * await docCoveragePlugin({ + * + * docTypes: ['class', 'function'] + * }) + * ] + * } + * + * @returns Plugin configuration. + */ +export async function docCoveragePlugin( + config: DocCoveragePluginConfig, +): Promise { + const docCoverageConfig = docCoveragePluginConfigSchema.parse(config); + + const audits = [ + { + slug: 'percentage-coverage', + title: 'Percentage of codebase with documentation', + description: 'Measures how many % of the codebase have documentation.', + }, + ]; + + const group: Group = { + slug: 'doc-coverage', + title: 'Documentation coverage metrics', + description: + 'Group containing all defined documentation coverage types as audits.', + refs: audits.map(audit => ({ + ...audit, + weight: 1, + })), + }; + + const runnerScriptPath = path.join( + fileURLToPath(path.dirname(import.meta.url)), + '..', + 'bin.js', + ); + + const packageJson = createRequire(import.meta.url)( + '../../package.json', + ) as typeof import('../../package.json'); + + return { + slug: 'doc-coverage', + title: 'Documentation coverage', + icon: 'folder-src', + description: 'Official Code PushUp documentation coverage plugin.', + docsUrl: 'https://www.npmjs.com/package/@code-pushup/doc-coverage-plugin/', + packageName: packageJson.name, + version: packageJson.version, + audits, + groups: [group], + runner: await createRunnerConfig(runnerScriptPath, docCoverageConfig), + }; +} diff --git a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts new file mode 100644 index 000000000..fbb2c452a --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts @@ -0,0 +1,87 @@ +import { describe, expect, it } from 'vitest'; +import type { RunnerConfig } from '@code-pushup/models'; +import { docCoveragePlugin } from './doc-coverage-plugin.js'; + +vi.mock('./runner/index.ts', () => ({ + createRunnerConfig: vi.fn().mockReturnValue({ + command: 'node', + outputFile: 'runner-output.json', + } satisfies RunnerConfig), +})); + +describe('docCoveragePlugin', () => { + it('should initialise a Documentation coverage plugin', async () => { + await expect( + docCoveragePlugin({ + outputPath: 'documentation/documentation.json', + }), + ).resolves.toStrictEqual( + expect.objectContaining({ + slug: 'doc-coverage', + title: 'Documentation coverage', + audits: expect.any(Array), + groups: expect.any(Array), + runner: expect.any(Object), + }), + ); + }); + + it('should generate percentage coverage audit', async () => { + await expect( + docCoveragePlugin({ + outputPath: 'documentation/documentation.json', + }), + ).resolves.toStrictEqual( + expect.objectContaining({ + audits: [ + { + slug: 'percentage-coverage', + title: 'Percentage of codebase with documentation', + description: expect.stringContaining( + 'how many % of the codebase have documentation', + ), + }, + ], + }), + ); + }); + + it('should provide a documentation coverage group', async () => { + await expect( + docCoveragePlugin({ + outputPath: 'documentation/documentation.json', + }), + ).resolves.toStrictEqual( + expect.objectContaining({ + groups: [ + expect.objectContaining({ + slug: 'doc-coverage', + title: 'Documentation coverage metrics', + refs: [ + expect.objectContaining({ + slug: 'percentage-coverage', + weight: 1, + }), + ], + }), + ], + }), + ); + }); + + it('should include package metadata', async () => { + await expect( + docCoveragePlugin({ + outputPath: 'documentation/documentation.json', + }), + ).resolves.toStrictEqual( + expect.objectContaining({ + icon: 'folder-src', + description: expect.stringContaining('documentation coverage plugin'), + docsUrl: expect.stringContaining('npmjs.com'), + packageName: expect.any(String), + version: expect.any(String), + }), + ); + }); +}); diff --git a/packages/plugin-doc-coverage/src/lib/runner/constants.ts b/packages/plugin-doc-coverage/src/lib/runner/constants.ts new file mode 100644 index 000000000..8b9843ee6 --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/runner/constants.ts @@ -0,0 +1,11 @@ +import path from 'node:path'; +import { pluginWorkDir } from '@code-pushup/utils'; + +export const WORKDIR = pluginWorkDir('doc-coverage'); + +export const PLUGIN_CONFIG_PATH = path.join( + process.cwd(), + WORKDIR, + 'plugin-config.json', +); +export const RUNNER_OUTPUT_PATH = path.join(WORKDIR, 'runner-output.json'); diff --git a/packages/plugin-doc-coverage/src/lib/runner/index.ts b/packages/plugin-doc-coverage/src/lib/runner/index.ts new file mode 100644 index 000000000..638777aaa --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/runner/index.ts @@ -0,0 +1,82 @@ +import { bold } from 'ansis'; +import { writeFile } from 'node:fs/promises'; +import path from 'node:path'; +import type { AuditOutput, RunnerConfig } from '@code-pushup/models'; +import { + ProcessError, + ensureDirectoryExists, + executeProcess, + filePathToCliArg, + readJsonFile, + ui, +} from '@code-pushup/utils'; +import type { DocCoveragePluginConfig } from '../config.js'; +import { PLUGIN_CONFIG_PATH, RUNNER_OUTPUT_PATH } from './constants.js'; + +export { PLUGIN_CONFIG_PATH, RUNNER_OUTPUT_PATH } from './constants.js'; + +export async function executeRunner(): Promise { + const { outputPath, coverageToolCommand } = + await readJsonFile(PLUGIN_CONFIG_PATH); + if (coverageToolCommand != null) { + const { command, args = [] } = coverageToolCommand; + try { + await executeProcess({ command, args }); + } catch (error) { + if (error instanceof ProcessError) { + ui().logger.error(bold('stdout from failed Compodoc process:')); + ui().logger.error(error.stdout); + ui().logger.error(bold('stderr from failed Compodoc process:')); + ui().logger.error(error.stderr); + } + throw new Error( + 'Doc Coverage plugin: Running Compodoc failed. Please check the error above.', + ); + } + } + + try { + // From the output of Compodoc, we can get the coverage percentage. + const docData: { coverage: { count: number } } = + await readJsonFile(outputPath); + const coverage = docData.coverage.count || 0; + + const auditOutputs: AuditOutput[] = [ + { + slug: 'percentage-coverage', + value: coverage, + score: coverage / 100, + displayValue: `${coverage} %`, + }, + ]; + + await ensureDirectoryExists(path.dirname(RUNNER_OUTPUT_PATH)); + await writeFile(RUNNER_OUTPUT_PATH, JSON.stringify(auditOutputs)); + } catch (error) { + if (error instanceof ProcessError) { + ui().logger.error(bold('stdout from failed coverage tool process:')); + ui().logger.error(error.stdout); + ui().logger.error(bold('stderr from failed coverage tool process:')); + ui().logger.error(error.stderr); + + throw new Error( + 'Doc Coverage plugin: Running Compodoc failed. Please check the error above.', + ); + } + } +} + +export async function createRunnerConfig( + scriptPath: string, + config: DocCoveragePluginConfig, +): Promise { + // Create JSON config for executeRunner + await ensureDirectoryExists(path.dirname(PLUGIN_CONFIG_PATH)); + await writeFile(PLUGIN_CONFIG_PATH, JSON.stringify(config)); + + return { + command: 'node', + args: [filePathToCliArg(scriptPath)], + outputFile: RUNNER_OUTPUT_PATH, + }; +} diff --git a/packages/plugin-doc-coverage/src/lib/runner/runner.integration.test.ts b/packages/plugin-doc-coverage/src/lib/runner/runner.integration.test.ts new file mode 100644 index 000000000..f67e5791c --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/runner/runner.integration.test.ts @@ -0,0 +1,80 @@ +import { writeFile } from 'node:fs/promises'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { describe, it } from 'vitest'; +import type { + AuditOutput, + AuditOutputs, + RunnerConfig, +} from '@code-pushup/models'; +import { readJsonFile, removeDirectoryIfExists } from '@code-pushup/utils'; +import type { DocCoveragePluginConfig } from '../config.js'; +import { + PLUGIN_CONFIG_PATH, + RUNNER_OUTPUT_PATH, + WORKDIR, +} from './constants.js'; +import { createRunnerConfig, executeRunner } from './index.js'; + +describe('createRunnerConfig', () => { + it('should create a valid runner config', async () => { + const runnerConfig = await createRunnerConfig('executeRunner.ts', { + coverageToolCommand: { + command: 'npx', + args: ['@compodoc/compodoc', '-p', 'tsconfig.json'], + }, + outputPath: 'documentation/documentation.json', + }); + expect(runnerConfig).toStrictEqual({ + command: 'node', + args: ['"executeRunner.ts"'], + outputFile: expect.stringContaining('runner-output.json'), + }); + }); + + it('should provide plugin config to runner in JSON file', async () => { + await removeDirectoryIfExists(WORKDIR); + + const pluginConfig: DocCoveragePluginConfig = { + coverageToolCommand: { + command: 'npx', + args: ['@compodoc/compodoc', '-p', 'tsconfig.json'], + }, + outputPath: 'documentation/documentation.json', + }; + + await createRunnerConfig('executeRunner.ts', pluginConfig); + + const config = + await readJsonFile(PLUGIN_CONFIG_PATH); + expect(config).toStrictEqual(pluginConfig); + }); +}); + +describe('executeRunner', () => { + it('should successfully execute runner', async () => { + const config: DocCoveragePluginConfig = { + outputPath: path.join( + fileURLToPath(path.dirname(import.meta.url)), + '..', + '..', + '..', + 'mocks', + 'documentation.json', + ), + }; + + await writeFile(PLUGIN_CONFIG_PATH, JSON.stringify(config)); + await executeRunner(); + + const results = await readJsonFile(RUNNER_OUTPUT_PATH); + expect(results).toStrictEqual([ + expect.objectContaining({ + slug: 'percentage-coverage', + score: 0.85, + value: 85, + displayValue: '85 %', + } satisfies AuditOutput), + ]); + }); +}); diff --git a/packages/plugin-doc-coverage/tsconfig.json b/packages/plugin-doc-coverage/tsconfig.json new file mode 100644 index 000000000..893f9a925 --- /dev/null +++ b/packages/plugin-doc-coverage/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "module": "ESNext", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "types": ["vitest"] + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.test.json" + } + ] +} diff --git a/packages/plugin-doc-coverage/tsconfig.lib.json b/packages/plugin-doc-coverage/tsconfig.lib.json new file mode 100644 index 000000000..ef2f7e2b3 --- /dev/null +++ b/packages/plugin-doc-coverage/tsconfig.lib.json @@ -0,0 +1,16 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": [ + "vite.config.unit.ts", + "vite.config.integration.ts", + "src/**/*.test.ts", + "src/**/*.mock.ts", + "mocks/**/*.ts" + ] +} diff --git a/packages/plugin-doc-coverage/tsconfig.test.json b/packages/plugin-doc-coverage/tsconfig.test.json new file mode 100644 index 000000000..9f29d6bb0 --- /dev/null +++ b/packages/plugin-doc-coverage/tsconfig.test.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": ["vitest/globals", "vitest/importMeta", "vite/client", "node"] + }, + "include": [ + "vite.config.unit.ts", + "vite.config.integration.ts", + "mocks/**/*.ts", + "src/**/*.test.ts" + ] +} diff --git a/packages/plugin-doc-coverage/vite.config.integration.ts b/packages/plugin-doc-coverage/vite.config.integration.ts new file mode 100644 index 000000000..b04d62c56 --- /dev/null +++ b/packages/plugin-doc-coverage/vite.config.integration.ts @@ -0,0 +1,29 @@ +/// +import { defineConfig } from 'vite'; +import { tsconfigPathAliases } from '../../tools/vitest-tsconfig-path-aliases.js'; + +export default defineConfig({ + cacheDir: '../../node_modules/.vite/plugin-coverage', + test: { + reporters: ['basic'], + globals: true, + cache: { + dir: '../../node_modules/.vitest', + }, + alias: tsconfigPathAliases(), + pool: 'threads', + poolOptions: { threads: { singleThread: true } }, + coverage: { + reporter: ['text', 'lcov'], + reportsDirectory: '../../coverage/plugin-doc-coverage/integration-tests', + exclude: ['mocks/**', '**/types.ts'], + }, + environment: 'node', + include: ['src/**/*.integration.test.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + globalSetup: ['../../global-setup.ts'], + setupFiles: [ + '../../testing/test-setup/src/lib/console.mock.ts', + '../../testing/test-setup/src/lib/reset.mocks.ts', + ], + }, +}); diff --git a/packages/plugin-doc-coverage/vite.config.unit.ts b/packages/plugin-doc-coverage/vite.config.unit.ts new file mode 100644 index 000000000..3cae73a58 --- /dev/null +++ b/packages/plugin-doc-coverage/vite.config.unit.ts @@ -0,0 +1,31 @@ +/// +import { defineConfig } from 'vite'; +import { tsconfigPathAliases } from '../../tools/vitest-tsconfig-path-aliases.js'; + +export default defineConfig({ + cacheDir: '../../node_modules/.vite/plugin-coverage', + test: { + reporters: ['basic'], + globals: true, + cache: { + dir: '../../node_modules/.vitest', + }, + alias: tsconfigPathAliases(), + pool: 'threads', + poolOptions: { threads: { singleThread: true } }, + coverage: { + reporter: ['text', 'lcov'], + reportsDirectory: '../../coverage/plugin-doc-coverage/unit-tests', + exclude: ['mocks/**', '**/types.ts'], + }, + environment: 'node', + include: ['src/**/*.unit.test.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + globalSetup: ['../../global-setup.ts'], + setupFiles: [ + '../../testing/test-setup/src/lib/cliui.mock.ts', + '../../testing/test-setup/src/lib/fs.mock.ts', + '../../testing/test-setup/src/lib/console.mock.ts', + '../../testing/test-setup/src/lib/reset.mocks.ts', + ], + }, +}); From b07eb8abc972d14aaa24915d7de41da3f39812cc Mon Sep 17 00:00:00 2001 From: Alejandro Date: Tue, 17 Dec 2024 22:49:48 +0100 Subject: [PATCH 02/39] feat: change from compodoc to typedoc --- package-lock.json | 162 ++++++++++++++++++ package.json | 2 + .../mocks/component-mock.ts | 4 + .../plugin-doc-coverage/src/lib/config.ts | 26 ++- .../src/lib/config.unit.test.ts | 50 ++++-- .../src/lib/doc-coverage-plugin.ts | 3 +- .../src/lib/doc-coverage-plugin.unit.test.ts | 8 +- .../src/lib/runner/constants.ts | 33 +++- .../src/lib/runner/index.ts | 93 +++++++--- .../src/lib/runner/runner.integration.test.ts | 62 +++---- .../plugin-doc-coverage/tsconfig.test.json | 3 +- 11 files changed, 346 insertions(+), 100 deletions(-) create mode 100644 packages/plugin-doc-coverage/mocks/component-mock.ts diff --git a/package-lock.json b/package-lock.json index 0459e1180..9138feee2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -97,6 +97,8 @@ "tsconfig-paths": "^4.2.0", "tsx": "^4.19.0", "type-fest": "^4.26.1", + "typedoc": "^0.27.5", + "typedoc-plugin-coverage": "^3.4.0", "typescript": "5.5.4", "typescript-eslint": "^8.18.0", "verdaccio": "^5.32.2", @@ -3233,6 +3235,17 @@ "tslib": "^2.4.0" } }, + "node_modules/@gerrit0/mini-shiki": { + "version": "1.24.4", + "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-1.24.4.tgz", + "integrity": "sha512-YEHW1QeAg6UmxEmswiQbOVEg1CW22b1XUD/lNTliOsu0LD0wqoyleFMnmbTp697QE0pcadQiR5cVtbbAPncvpw==", + "dev": true, + "dependencies": { + "@shikijs/engine-oniguruma": "^1.24.2", + "@shikijs/types": "^1.24.2", + "@shikijs/vscode-textmate": "^9.3.1" + } + }, "node_modules/@graphql-typed-document-node/core": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", @@ -6159,6 +6172,32 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.24.2.tgz", + "integrity": "sha512-ZN6k//aDNWRJs1uKB12pturKHh7GejKugowOFGAuG7TxDRLod1Bd5JhpOikOiFqPmKjKEPtEA6mRCf7q3ulDyQ==", + "dev": true, + "dependencies": { + "@shikijs/types": "1.24.2", + "@shikijs/vscode-textmate": "^9.3.0" + } + }, + "node_modules/@shikijs/types": { + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.24.2.tgz", + "integrity": "sha512-bdeWZiDtajGLG9BudI0AHet0b6e7FbR0EsE4jpGaI0YwHm/XJunI9+3uZnzFtX65gsyJ6ngCIWUfA4NWRPnBkQ==", + "dev": true, + "dependencies": { + "@shikijs/vscode-textmate": "^9.3.0", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.3.1.tgz", + "integrity": "sha512-79QfK1393x9Ho60QFyLti+QfdJzRQCVLFb97kOIV7Eo9vQU/roINgk7m24uv0a7AUvN//RDH36FLjjK48v0s9g==", + "dev": true + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -7296,6 +7335,15 @@ "@types/node": "*" } }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", @@ -7427,6 +7475,12 @@ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true + }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -19654,6 +19708,15 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "dependencies": { + "uc.micro": "^2.0.0" + } + }, "node_modules/load-tsconfig": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", @@ -20146,6 +20209,12 @@ "yallist": "^3.0.2" } }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, "node_modules/luxon": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", @@ -20214,6 +20283,29 @@ "tmpl": "1.0.5" } }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/marky": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.5.tgz", @@ -20234,6 +20326,12 @@ "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", "dev": true }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -22243,6 +22341,15 @@ "node": ">=6" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/puppeteer-core": { "version": "22.15.0", "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.15.0.tgz", @@ -25192,6 +25299,55 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/typedoc": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.27.5.tgz", + "integrity": "sha512-x+fhKJtTg4ozXwKayh/ek4wxZQI/+2hmZUdO2i2NGDBRUflDble70z+ewHod3d4gRpXSO6fnlnjbDTnJk7HlkQ==", + "dev": true, + "dependencies": { + "@gerrit0/mini-shiki": "^1.24.0", + "lunr": "^2.3.9", + "markdown-it": "^14.1.0", + "minimatch": "^9.0.5", + "yaml": "^2.6.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x" + } + }, + "node_modules/typedoc-plugin-coverage": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-coverage/-/typedoc-plugin-coverage-3.4.0.tgz", + "integrity": "sha512-I8fLeQEERncGn4sUlGZ+B1ehx4L7VRwqa3i6AP+PFfvZK0ToXBGkh9sK7xs8l8FLPXq7Cv0yVy4YCEGgWNzDBw==", + "dev": true, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "typedoc": "0.25.x || 0.26.x || 0.27.x" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/typescript": { "version": "5.5.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", @@ -25279,6 +25435,12 @@ "typescript": ">=4.8.4 <5.8.0" } }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true + }, "node_modules/ufo": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", diff --git a/package.json b/package.json index 2782021cf..de4938f30 100644 --- a/package.json +++ b/package.json @@ -110,6 +110,8 @@ "tsconfig-paths": "^4.2.0", "tsx": "^4.19.0", "type-fest": "^4.26.1", + "typedoc": "^0.27.5", + "typedoc-plugin-coverage": "^3.4.0", "typescript": "5.5.4", "typescript-eslint": "^8.18.0", "verdaccio": "^5.32.2", diff --git a/packages/plugin-doc-coverage/mocks/component-mock.ts b/packages/plugin-doc-coverage/mocks/component-mock.ts new file mode 100644 index 000000000..f8008c421 --- /dev/null +++ b/packages/plugin-doc-coverage/mocks/component-mock.ts @@ -0,0 +1,4 @@ +/** Dummy function */ +export function DUMMY_FUNCTION() { + return 'Hello World'; +} diff --git a/packages/plugin-doc-coverage/src/lib/config.ts b/packages/plugin-doc-coverage/src/lib/config.ts index 139f2d77a..052f35c47 100644 --- a/packages/plugin-doc-coverage/src/lib/config.ts +++ b/packages/plugin-doc-coverage/src/lib/config.ts @@ -3,23 +3,21 @@ import { z } from 'zod'; export type DocType = 'percentage-coverage'; export const docCoveragePluginConfigSchema = z.object({ - coverageToolCommand: z - .object({ - command: z - .string({ description: 'Command to run coverage tool (compodoc).' }) - .min(1), - args: z - .array(z.string(), { - description: 'Arguments to be passed to the coverage tool.', - }) - .optional(), + language: z.enum(['javascript', 'typescript'], { + description: 'Programming language of the source code to analyze', + }), + sourceGlob: z + .string({ + description: 'Glob pattern to find source files', + }) + .optional(), + outputFolderPath: z + .string({ + description: 'Path to the output folder', }) .optional(), - outputPath: z - .string({ description: 'Path to the documentation.json file.' }) - .default('documentation/documentation.json'), }); -export type DocCoveragePluginConfig = z.infer< +export type DocCoveragePluginConfig = z.input< typeof docCoveragePluginConfigSchema >; diff --git a/packages/plugin-doc-coverage/src/lib/config.unit.test.ts b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts index 3964fa186..36840ece8 100644 --- a/packages/plugin-doc-coverage/src/lib/config.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts @@ -8,45 +8,57 @@ describe('docCoveragePluginConfigSchema', () => { it('accepts a documentation coverage configuration with all entities', () => { expect(() => docCoveragePluginConfigSchema.parse({ - coverageToolCommand: { - command: 'npx @compodoc/compodoc', - args: ['-p', 'tsconfig.json'], - }, - outputPath: 'documentation/custom-doc.json', + language: 'typescript', + sourceGlob: 'src/**/*.{ts,tsx}', } satisfies DocCoveragePluginConfig), ).not.toThrow(); }); - it('accepts a minimal documentation coverage configuration', () => { + it('accepts minimal configuration with only language', () => { expect(() => - docCoveragePluginConfigSchema.parse({} satisfies DocCoveragePluginConfig), + docCoveragePluginConfigSchema.parse({ + language: 'javascript', + } satisfies DocCoveragePluginConfig), ).not.toThrow(); }); - it('uses default output path when not provided', () => { - const config = {} satisfies DocCoveragePluginConfig; + it('accepts configuration without sourceGlob', () => { + const config = { + language: 'typescript', + } satisfies DocCoveragePluginConfig; const parsed = docCoveragePluginConfigSchema.parse(config); - expect(parsed.outputPath).toBe('documentation/documentation.json'); + expect(parsed.sourceGlob).toBeUndefined(); }); - it('throws for missing command in coverageToolCommand', () => { + it('throws for missing language', () => { expect(() => docCoveragePluginConfigSchema.parse({ - coverageToolCommand: { - args: ['-p', 'tsconfig.json'], - }, + sourceGlob: 'src/**/*.ts', }), ).toThrow('invalid_type'); }); - it('accepts empty args in coverageToolCommand', () => { + it('throws for invalid language', () => { expect(() => docCoveragePluginConfigSchema.parse({ - coverageToolCommand: { - command: 'npx @compodoc/compodoc', - }, - } satisfies DocCoveragePluginConfig), + language: 'python', + sourceGlob: 'src/**/*.py', + }), + ).toThrow('Invalid enum value'); + }); + + it('accepts both typescript and javascript as valid languages', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + language: 'typescript', + }), + ).not.toThrow(); + + expect(() => + docCoveragePluginConfigSchema.parse({ + language: 'javascript', + }), ).not.toThrow(); }); }); diff --git a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts index 0519eed82..c2e55412b 100644 --- a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts +++ b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts @@ -19,8 +19,7 @@ import { createRunnerConfig } from './runner/index.js'; * plugins: [ * // ... other plugins ... * await docCoveragePlugin({ - * - * docTypes: ['class', 'function'] + * language: 'typescript' * }) * ] * } diff --git a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts index fbb2c452a..29c45bcf3 100644 --- a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts @@ -13,7 +13,7 @@ describe('docCoveragePlugin', () => { it('should initialise a Documentation coverage plugin', async () => { await expect( docCoveragePlugin({ - outputPath: 'documentation/documentation.json', + language: 'typescript', }), ).resolves.toStrictEqual( expect.objectContaining({ @@ -29,7 +29,7 @@ describe('docCoveragePlugin', () => { it('should generate percentage coverage audit', async () => { await expect( docCoveragePlugin({ - outputPath: 'documentation/documentation.json', + language: 'typescript', }), ).resolves.toStrictEqual( expect.objectContaining({ @@ -49,7 +49,7 @@ describe('docCoveragePlugin', () => { it('should provide a documentation coverage group', async () => { await expect( docCoveragePlugin({ - outputPath: 'documentation/documentation.json', + language: 'typescript', }), ).resolves.toStrictEqual( expect.objectContaining({ @@ -72,7 +72,7 @@ describe('docCoveragePlugin', () => { it('should include package metadata', async () => { await expect( docCoveragePlugin({ - outputPath: 'documentation/documentation.json', + language: 'typescript', }), ).resolves.toStrictEqual( expect.objectContaining({ diff --git a/packages/plugin-doc-coverage/src/lib/runner/constants.ts b/packages/plugin-doc-coverage/src/lib/runner/constants.ts index 8b9843ee6..9cf24d169 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/constants.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/constants.ts @@ -3,9 +3,40 @@ import { pluginWorkDir } from '@code-pushup/utils'; export const WORKDIR = pluginWorkDir('doc-coverage'); +export const RUNNER_OUTPUT_PATH = path.join(WORKDIR, 'runner-output.json'); + export const PLUGIN_CONFIG_PATH = path.join( process.cwd(), WORKDIR, 'plugin-config.json', ); -export const RUNNER_OUTPUT_PATH = path.join(WORKDIR, 'runner-output.json'); + +export const enum ProgrammingLanguage { + JavaScript = 'javascript', + TypeScript = 'typescript', +} + +export const DEFAULT_SOURCE_GLOB = { + [ProgrammingLanguage.JavaScript]: '"src/**/*.js"', + [ProgrammingLanguage.TypeScript]: '"src/**/*.ts"', +}; + +export const DEFAULT_OUTPUT_FOLDER_PATH = './documentation'; + +export const COMMANDS_FOR_LANGUAGES: Readonly< + Record +> = { + [ProgrammingLanguage.JavaScript]: { + command: 'npx', + args: 'typedoc $sourceGlob --entryPointStrategy expand --plugin typedoc-plugin-coverage --coverageOutputType json --skipErrorChecking --out $outputFolderPath', + }, + [ProgrammingLanguage.TypeScript]: { + command: 'npx', + args: 'typedoc $sourceGlob --entryPointStrategy expand --plugin typedoc-plugin-coverage --coverageOutputType json --skipErrorChecking --out $outputFolderPath', + }, +} as const; + +export type TypedocResult = { + percent: number; + notDocumented: string[]; +}; diff --git a/packages/plugin-doc-coverage/src/lib/runner/index.ts b/packages/plugin-doc-coverage/src/lib/runner/index.ts index 638777aaa..d5f83fc39 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/index.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/index.ts @@ -11,42 +11,79 @@ import { ui, } from '@code-pushup/utils'; import type { DocCoveragePluginConfig } from '../config.js'; -import { PLUGIN_CONFIG_PATH, RUNNER_OUTPUT_PATH } from './constants.js'; +import { + COMMANDS_FOR_LANGUAGES, + DEFAULT_OUTPUT_FOLDER_PATH, + DEFAULT_SOURCE_GLOB, + PLUGIN_CONFIG_PATH, + ProgrammingLanguage, + RUNNER_OUTPUT_PATH, + type TypedocResult, +} from './constants.js'; export { PLUGIN_CONFIG_PATH, RUNNER_OUTPUT_PATH } from './constants.js'; -export async function executeRunner(): Promise { - const { outputPath, coverageToolCommand } = - await readJsonFile(PLUGIN_CONFIG_PATH); - if (coverageToolCommand != null) { - const { command, args = [] } = coverageToolCommand; - try { - await executeProcess({ command, args }); - } catch (error) { - if (error instanceof ProcessError) { - ui().logger.error(bold('stdout from failed Compodoc process:')); - ui().logger.error(error.stdout); - ui().logger.error(bold('stderr from failed Compodoc process:')); - ui().logger.error(error.stderr); - } - throw new Error( - 'Doc Coverage plugin: Running Compodoc failed. Please check the error above.', - ); +/** + * Execute the Typedoc process. + * @param config - The configuration for the Typedoc process. + */ +async function _executeTypedocProcess( + config: DocCoveragePluginConfig, +): Promise { + const { + sourceGlob, + language, + outputFolderPath = DEFAULT_OUTPUT_FOLDER_PATH, + } = config; + const { args: originalArgs } = COMMANDS_FOR_LANGUAGES[language]; + const processedArgs = + language === ProgrammingLanguage.TypeScript + ? originalArgs + .replace('$outputFolderPath', outputFolderPath) + .replace('$sourceGlob', sourceGlob || DEFAULT_SOURCE_GLOB[language]) + : originalArgs; + + try { + await executeProcess({ + command: COMMANDS_FOR_LANGUAGES[language].command, + args: processedArgs.split(' '), + }); + } catch (error) { + if (error instanceof ProcessError) { + ui().logger.error(bold('stdout from failed Typedoc process:')); + ui().logger.error(error.stdout); + ui().logger.error(bold('stderr from failed Typedoc process:')); + ui().logger.error(error.stderr); } + throw new Error( + 'Doc Coverage plugin: Running Typedoc failed. Please check the error above.', + ); } +} +/** + * Process the Typedoc results. + * @param outputFolderPath - The path to the output folder. + */ +async function _processTypedocResults(outputFolderPath: string): Promise { try { - // From the output of Compodoc, we can get the coverage percentage. - const docData: { coverage: { count: number } } = - await readJsonFile(outputPath); - const coverage = docData.coverage.count || 0; - + const docData: TypedocResult = await readJsonFile( + path.join(outputFolderPath, 'coverage.json'), + ); + const coverage = docData.percent || 0; const auditOutputs: AuditOutput[] = [ { slug: 'percentage-coverage', value: coverage, score: coverage / 100, displayValue: `${coverage} %`, + details: { + issues: docData.notDocumented.map(file => ({ + message: 'Missing documentation', + source: { file }, + severity: 'warning', + })), + }, }, ]; @@ -58,7 +95,6 @@ export async function executeRunner(): Promise { ui().logger.error(error.stdout); ui().logger.error(bold('stderr from failed coverage tool process:')); ui().logger.error(error.stderr); - throw new Error( 'Doc Coverage plugin: Running Compodoc failed. Please check the error above.', ); @@ -66,6 +102,15 @@ export async function executeRunner(): Promise { } } +export async function executeRunner(): Promise { + const config = + await readJsonFile(PLUGIN_CONFIG_PATH); + await _executeTypedocProcess(config); + await _processTypedocResults( + config.outputFolderPath || DEFAULT_OUTPUT_FOLDER_PATH, + ); +} + export async function createRunnerConfig( scriptPath: string, config: DocCoveragePluginConfig, diff --git a/packages/plugin-doc-coverage/src/lib/runner/runner.integration.test.ts b/packages/plugin-doc-coverage/src/lib/runner/runner.integration.test.ts index f67e5791c..d7fc8e5e9 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/runner.integration.test.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/runner.integration.test.ts @@ -1,6 +1,4 @@ import { writeFile } from 'node:fs/promises'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; import { describe, it } from 'vitest'; import type { AuditOutput, @@ -19,11 +17,9 @@ import { createRunnerConfig, executeRunner } from './index.js'; describe('createRunnerConfig', () => { it('should create a valid runner config', async () => { const runnerConfig = await createRunnerConfig('executeRunner.ts', { - coverageToolCommand: { - command: 'npx', - args: ['@compodoc/compodoc', '-p', 'tsconfig.json'], - }, - outputPath: 'documentation/documentation.json', + language: 'typescript', + sourceGlob: 'src/**/*.ts', + outputFolderPath: 'documentation', }); expect(runnerConfig).toStrictEqual({ command: 'node', @@ -36,11 +32,9 @@ describe('createRunnerConfig', () => { await removeDirectoryIfExists(WORKDIR); const pluginConfig: DocCoveragePluginConfig = { - coverageToolCommand: { - command: 'npx', - args: ['@compodoc/compodoc', '-p', 'tsconfig.json'], - }, - outputPath: 'documentation/documentation.json', + language: 'typescript', + sourceGlob: 'src/**/*.ts', + outputFolderPath: 'documentation', }; await createRunnerConfig('executeRunner.ts', pluginConfig); @@ -52,29 +46,27 @@ describe('createRunnerConfig', () => { }); describe('executeRunner', () => { - it('should successfully execute runner', async () => { - const config: DocCoveragePluginConfig = { - outputPath: path.join( - fileURLToPath(path.dirname(import.meta.url)), - '..', - '..', - '..', - 'mocks', - 'documentation.json', - ), - }; + it( + 'should successfully execute runner', + async () => { + const config: DocCoveragePluginConfig = { + language: 'typescript', + sourceGlob: '"packages/plugin-doc-coverage/mocks/component-mock.ts"', + }; - await writeFile(PLUGIN_CONFIG_PATH, JSON.stringify(config)); - await executeRunner(); + await writeFile(PLUGIN_CONFIG_PATH, JSON.stringify(config)); + await executeRunner(); - const results = await readJsonFile(RUNNER_OUTPUT_PATH); - expect(results).toStrictEqual([ - expect.objectContaining({ - slug: 'percentage-coverage', - score: 0.85, - value: 85, - displayValue: '85 %', - } satisfies AuditOutput), - ]); - }); + const results = await readJsonFile(RUNNER_OUTPUT_PATH); + expect(results).toStrictEqual([ + expect.objectContaining({ + slug: 'percentage-coverage', + score: 1, + value: 100, + displayValue: '100 %', + } satisfies AuditOutput), + ]); + }, + { timeout: 60 * 1000 }, + ); }); diff --git a/packages/plugin-doc-coverage/tsconfig.test.json b/packages/plugin-doc-coverage/tsconfig.test.json index 9f29d6bb0..05637ee6e 100644 --- a/packages/plugin-doc-coverage/tsconfig.test.json +++ b/packages/plugin-doc-coverage/tsconfig.test.json @@ -8,6 +8,7 @@ "vite.config.unit.ts", "vite.config.integration.ts", "mocks/**/*.ts", - "src/**/*.test.ts" + "src/**/*.test.ts", + "mocks/component-mock.ts" ] } From 0de6d3fb3767c3065990108dbc16902bca00374b Mon Sep 17 00:00:00 2001 From: Alejandro Date: Wed, 18 Dec 2024 03:01:40 +0100 Subject: [PATCH 03/39] feat(plugin-doc-coverage): change to use ts-morph instead of typedoc. MVP --- code-pushup.config.ts | 2 + code-pushup.preset.ts | 28 ++ package-lock.json | 242 ++++++------------ package.json | 3 +- .../mocks/component-mock.ts | 5 +- packages/plugin-doc-coverage/package.json | 3 +- .../plugin-doc-coverage/src/lib/config.ts | 5 - .../src/lib/doc-coverage-plugin.ts | 34 +-- .../src/lib/doc-coverage-plugin.unit.test.ts | 24 -- .../plugin-doc-coverage/src/lib/models.ts | 7 + .../src/lib/runner/doc-processer.ts | 198 ++++++++++++++ .../src/lib/runner/index.ts | 55 ++-- .../src/lib/runner/runner.integration.test.ts | 4 +- .../plugin-doc-coverage/tsconfig.lib.json | 2 +- tsconfig.base.json | 3 + 15 files changed, 362 insertions(+), 253 deletions(-) create mode 100644 packages/plugin-doc-coverage/src/lib/models.ts create mode 100644 packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts diff --git a/code-pushup.config.ts b/code-pushup.config.ts index bd089d884..d1b674a1d 100644 --- a/code-pushup.config.ts +++ b/code-pushup.config.ts @@ -2,6 +2,7 @@ import 'dotenv/config'; import { z } from 'zod'; import { coverageCoreConfigNx, + docCoverageCoreConfig, eslintCoreConfigNx, jsPackagesCoreConfig, lighthouseCoreConfig, @@ -39,4 +40,5 @@ export default mergeConfigs( 'https://github.com/code-pushup/cli?tab=readme-ov-file#code-pushup-cli/', ), await eslintCoreConfigNx(), + await docCoverageCoreConfig(), ); diff --git a/code-pushup.preset.ts b/code-pushup.preset.ts index 74e6b51ce..979610544 100644 --- a/code-pushup.preset.ts +++ b/code-pushup.preset.ts @@ -5,6 +5,8 @@ import type { import coveragePlugin, { getNxCoveragePaths, } from './packages/plugin-coverage/src/index.js'; +import docCoveragePlugin from './packages/plugin-doc-coverage/src/index.js'; +import { docCoverageAudits } from './packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.js'; import eslintPlugin, { eslintConfigFromAllNxProjects, eslintConfigFromNxProject, @@ -82,6 +84,20 @@ export const eslintCategories: CategoryConfig[] = [ }, ]; +export const docCoverageCategories: CategoryConfig[] = [ + { + slug: 'doc-coverage', + title: 'Documentation coverage', + description: 'Measures how much of your code is **documented**.', + refs: docCoverageAudits.map(audit => ({ + weight: 1, + type: 'audit', + plugin: 'doc-coverage', + slug: audit.slug, + })), + }, +]; + export const coverageCategories: CategoryConfig[] = [ { slug: 'code-coverage', @@ -114,6 +130,18 @@ export const lighthouseCoreConfig = async ( }; }; +export const docCoverageCoreConfig = async (): Promise => { + return { + plugins: [ + await docCoveragePlugin({ + language: 'typescript', + sourceGlob: 'packages/**/*.ts', + }), + ], + categories: docCoverageCategories, + }; +}; + export const eslintCoreConfigNx = async ( projectName?: string, ): Promise => { diff --git a/package-lock.json b/package-lock.json index 9138feee2..206de309d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "parse-lcov": "^1.0.4", "semver": "^7.6.3", "simple-git": "^3.26.0", + "ts-morph": "^24.0.0", "tslib": "^2.6.2", "vscode-material-icons": "^0.1.1", "yaml": "^2.5.1", @@ -97,8 +98,6 @@ "tsconfig-paths": "^4.2.0", "tsx": "^4.19.0", "type-fest": "^4.26.1", - "typedoc": "^0.27.5", - "typedoc-plugin-coverage": "^3.4.0", "typescript": "5.5.4", "typescript-eslint": "^8.18.0", "verdaccio": "^5.32.2", @@ -3235,17 +3234,6 @@ "tslib": "^2.4.0" } }, - "node_modules/@gerrit0/mini-shiki": { - "version": "1.24.4", - "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-1.24.4.tgz", - "integrity": "sha512-YEHW1QeAg6UmxEmswiQbOVEg1CW22b1XUD/lNTliOsu0LD0wqoyleFMnmbTp697QE0pcadQiR5cVtbbAPncvpw==", - "dev": true, - "dependencies": { - "@shikijs/engine-oniguruma": "^1.24.2", - "@shikijs/types": "^1.24.2", - "@shikijs/vscode-textmate": "^9.3.1" - } - }, "node_modules/@graphql-typed-document-node/core": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", @@ -6172,32 +6160,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "1.24.2", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.24.2.tgz", - "integrity": "sha512-ZN6k//aDNWRJs1uKB12pturKHh7GejKugowOFGAuG7TxDRLod1Bd5JhpOikOiFqPmKjKEPtEA6mRCf7q3ulDyQ==", - "dev": true, - "dependencies": { - "@shikijs/types": "1.24.2", - "@shikijs/vscode-textmate": "^9.3.0" - } - }, - "node_modules/@shikijs/types": { - "version": "1.24.2", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.24.2.tgz", - "integrity": "sha512-bdeWZiDtajGLG9BudI0AHet0b6e7FbR0EsE4jpGaI0YwHm/XJunI9+3uZnzFtX65gsyJ6ngCIWUfA4NWRPnBkQ==", - "dev": true, - "dependencies": { - "@shikijs/vscode-textmate": "^9.3.0", - "@types/hast": "^3.0.4" - } - }, - "node_modules/@shikijs/vscode-textmate": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.3.1.tgz", - "integrity": "sha512-79QfK1393x9Ho60QFyLti+QfdJzRQCVLFb97kOIV7Eo9vQU/roINgk7m24uv0a7AUvN//RDH36FLjjK48v0s9g==", - "dev": true - }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -7184,6 +7146,30 @@ "node": ">=10.13.0" } }, + "node_modules/@ts-morph/common": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.25.0.tgz", + "integrity": "sha512-kMnZz+vGGHi4GoHnLmMhGNjm44kGtKUXGnOvrKmMwAuvNjM/PgKVGfUnL7IDvK7Jb2QQ82jq3Zmp04Gy+r3Dkg==", + "dependencies": { + "minimatch": "^9.0.4", + "path-browserify": "^1.0.1", + "tinyglobby": "^0.2.9" + } + }, + "node_modules/@ts-morph/common/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", @@ -7335,15 +7321,6 @@ "@types/node": "*" } }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dev": true, - "dependencies": { - "@types/unist": "*" - } - }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", @@ -7475,12 +7452,6 @@ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "dev": true - }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -10852,6 +10823,11 @@ "node": ">= 0.12.0" } }, + "node_modules/code-block-writer": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", + "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==" + }, "node_modules/collect-v8-coverage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", @@ -19708,15 +19684,6 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", - "dev": true, - "dependencies": { - "uc.micro": "^2.0.0" - } - }, "node_modules/load-tsconfig": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", @@ -20209,12 +20176,6 @@ "yallist": "^3.0.2" } }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, "node_modules/luxon": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", @@ -20283,29 +20244,6 @@ "tmpl": "1.0.5" } }, - "node_modules/markdown-it": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", - "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", - "mdurl": "^2.0.0", - "punycode.js": "^2.3.1", - "uc.micro": "^2.1.0" - }, - "bin": { - "markdown-it": "bin/markdown-it.mjs" - } - }, - "node_modules/markdown-it/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/marky": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.5.tgz", @@ -20326,12 +20264,6 @@ "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", "dev": true }, - "node_modules/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", - "dev": true - }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -21772,6 +21704,11 @@ "node": ">= 0.8" } }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" + }, "node_modules/path-exists": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", @@ -22341,15 +22278,6 @@ "node": ">=6" } }, - "node_modules/punycode.js": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", - "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/puppeteer-core": { "version": "22.15.0", "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.15.0.tgz", @@ -24740,6 +24668,42 @@ "integrity": "sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==", "dev": true }, + "node_modules/tinyglobby": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.10.tgz", + "integrity": "sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==", + "dependencies": { + "fdir": "^6.4.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz", + "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tinypool": { "version": "0.8.4", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", @@ -24963,6 +24927,15 @@ "typescript": ">=4.0.0" } }, + "node_modules/ts-morph": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-24.0.0.tgz", + "integrity": "sha512-2OAOg/Ob5yx9Et7ZX4CvTCc0UFoZHwLEJ+dpDPSUi5TgwwlTlX47w+iFRrEwzUZwYACjq83cgjS/Da50Ga37uw==", + "dependencies": { + "@ts-morph/common": "~0.25.0", + "code-block-writer": "^13.0.3" + } + }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -25299,55 +25272,6 @@ "is-typedarray": "^1.0.0" } }, - "node_modules/typedoc": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.27.5.tgz", - "integrity": "sha512-x+fhKJtTg4ozXwKayh/ek4wxZQI/+2hmZUdO2i2NGDBRUflDble70z+ewHod3d4gRpXSO6fnlnjbDTnJk7HlkQ==", - "dev": true, - "dependencies": { - "@gerrit0/mini-shiki": "^1.24.0", - "lunr": "^2.3.9", - "markdown-it": "^14.1.0", - "minimatch": "^9.0.5", - "yaml": "^2.6.1" - }, - "bin": { - "typedoc": "bin/typedoc" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x" - } - }, - "node_modules/typedoc-plugin-coverage": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/typedoc-plugin-coverage/-/typedoc-plugin-coverage-3.4.0.tgz", - "integrity": "sha512-I8fLeQEERncGn4sUlGZ+B1ehx4L7VRwqa3i6AP+PFfvZK0ToXBGkh9sK7xs8l8FLPXq7Cv0yVy4YCEGgWNzDBw==", - "dev": true, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "typedoc": "0.25.x || 0.26.x || 0.27.x" - } - }, - "node_modules/typedoc/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/typescript": { "version": "5.5.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", @@ -25435,12 +25359,6 @@ "typescript": ">=4.8.4 <5.8.0" } }, - "node_modules/uc.micro": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", - "dev": true - }, "node_modules/ufo": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", diff --git a/package.json b/package.json index de4938f30..6366530e8 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "parse-lcov": "^1.0.4", "semver": "^7.6.3", "simple-git": "^3.26.0", + "ts-morph": "^24.0.0", "tslib": "^2.6.2", "vscode-material-icons": "^0.1.1", "yaml": "^2.5.1", @@ -110,8 +111,6 @@ "tsconfig-paths": "^4.2.0", "tsx": "^4.19.0", "type-fest": "^4.26.1", - "typedoc": "^0.27.5", - "typedoc-plugin-coverage": "^3.4.0", "typescript": "5.5.4", "typescript-eslint": "^8.18.0", "verdaccio": "^5.32.2", diff --git a/packages/plugin-doc-coverage/mocks/component-mock.ts b/packages/plugin-doc-coverage/mocks/component-mock.ts index f8008c421..e66f4f859 100644 --- a/packages/plugin-doc-coverage/mocks/component-mock.ts +++ b/packages/plugin-doc-coverage/mocks/component-mock.ts @@ -1,4 +1,7 @@ -/** Dummy function */ +/** + * Dummy function that returns 'Hello World'. + * @returns {string} - The string 'Hello World'. + */ export function DUMMY_FUNCTION() { return 'Hello World'; } diff --git a/packages/plugin-doc-coverage/package.json b/packages/plugin-doc-coverage/package.json index 1c2ebe241..ec8b984a9 100644 --- a/packages/plugin-doc-coverage/package.json +++ b/packages/plugin-doc-coverage/package.json @@ -37,7 +37,8 @@ "@code-pushup/models": "0.57.0", "@code-pushup/utils": "0.57.0", "ansis": "^3.3.0", - "zod": "^3.22.4" + "zod": "^3.22.4", + "ts-morph": "^24.0.0" }, "peerDependenciesMeta": { "@nx/devkit": { diff --git a/packages/plugin-doc-coverage/src/lib/config.ts b/packages/plugin-doc-coverage/src/lib/config.ts index 052f35c47..a55a46137 100644 --- a/packages/plugin-doc-coverage/src/lib/config.ts +++ b/packages/plugin-doc-coverage/src/lib/config.ts @@ -11,11 +11,6 @@ export const docCoveragePluginConfigSchema = z.object({ description: 'Glob pattern to find source files', }) .optional(), - outputFolderPath: z - .string({ - description: 'Path to the output folder', - }) - .optional(), }); export type DocCoveragePluginConfig = z.input< diff --git a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts index c2e55412b..0504c8218 100644 --- a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts +++ b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts @@ -1,7 +1,7 @@ import { createRequire } from 'node:module'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import type { Group, PluginConfig } from '@code-pushup/models'; +import type { PluginConfig } from '@code-pushup/models'; import { type DocCoveragePluginConfig, docCoveragePluginConfigSchema, @@ -26,30 +26,20 @@ import { createRunnerConfig } from './runner/index.js'; * * @returns Plugin configuration. */ + +export const docCoverageAudits = [ + { + slug: 'percentage-coverage', + title: 'Percentage of codebase with documentation', + description: 'Measures how many % of the codebase have documentation.', + }, +]; + export async function docCoveragePlugin( config: DocCoveragePluginConfig, ): Promise { const docCoverageConfig = docCoveragePluginConfigSchema.parse(config); - const audits = [ - { - slug: 'percentage-coverage', - title: 'Percentage of codebase with documentation', - description: 'Measures how many % of the codebase have documentation.', - }, - ]; - - const group: Group = { - slug: 'doc-coverage', - title: 'Documentation coverage metrics', - description: - 'Group containing all defined documentation coverage types as audits.', - refs: audits.map(audit => ({ - ...audit, - weight: 1, - })), - }; - const runnerScriptPath = path.join( fileURLToPath(path.dirname(import.meta.url)), '..', @@ -68,8 +58,8 @@ export async function docCoveragePlugin( docsUrl: 'https://www.npmjs.com/package/@code-pushup/doc-coverage-plugin/', packageName: packageJson.name, version: packageJson.version, - audits, - groups: [group], + audits: docCoverageAudits, + // groups: [group], runner: await createRunnerConfig(runnerScriptPath, docCoverageConfig), }; } diff --git a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts index 29c45bcf3..20e44b1da 100644 --- a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts @@ -20,7 +20,6 @@ describe('docCoveragePlugin', () => { slug: 'doc-coverage', title: 'Documentation coverage', audits: expect.any(Array), - groups: expect.any(Array), runner: expect.any(Object), }), ); @@ -46,29 +45,6 @@ describe('docCoveragePlugin', () => { ); }); - it('should provide a documentation coverage group', async () => { - await expect( - docCoveragePlugin({ - language: 'typescript', - }), - ).resolves.toStrictEqual( - expect.objectContaining({ - groups: [ - expect.objectContaining({ - slug: 'doc-coverage', - title: 'Documentation coverage metrics', - refs: [ - expect.objectContaining({ - slug: 'percentage-coverage', - weight: 1, - }), - ], - }), - ], - }), - ); - }); - it('should include package metadata', async () => { await expect( docCoveragePlugin({ diff --git a/packages/plugin-doc-coverage/src/lib/models.ts b/packages/plugin-doc-coverage/src/lib/models.ts new file mode 100644 index 000000000..ff4605b8c --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/models.ts @@ -0,0 +1,7 @@ +export type UndocumentedItem = { + file: string; + type: string; + name: string; + line: number; + class?: string; +}; diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts new file mode 100644 index 000000000..86d75b343 --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts @@ -0,0 +1,198 @@ +import { Project } from 'ts-morph'; +import type { UndocumentedItem } from '../models.js'; + +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable functional/immutable-data */ +/* eslint-disable @typescript-eslint/max-params */ +/* eslint-disable functional/no-let */ + +export function processDocCoverage(toInclude: string): { + undocumentedItems: UndocumentedItem[]; + coverage: number; +} { + const project = new Project(); + project.addSourceFilesAtPaths(toInclude); + + let itsDocumented = 0; + const undocumentedItems: UndocumentedItem[] = []; + + project.getSourceFiles().forEach(sourceFile => { + if (isTestFile(sourceFile.getFilePath())) { + return; + } + + processClassDeclarations( + sourceFile, + undocumentedItems, + count => (itsDocumented += count), + ); + processDeclarations( + sourceFile, + undocumentedItems, + count => (itsDocumented += count), + ); + }); + + return calculateCoverage(undocumentedItems, itsDocumented); +} + +function isTestFile(filePath: string): boolean { + return filePath.includes('.spec.') || filePath.includes('.test.'); +} + +function addUndocumentedItem( + file: string, + type: string, + name: string, + line: number, +): UndocumentedItem { + return { + file, + type, + name, + line, + }; +} + +function processClassDeclarations( + sourceFile: any, + undocumentedItems: UndocumentedItem[], + onDocumented: (count: number) => void, +): void { + sourceFile.getClasses().forEach((classDeclaration: any) => { + const className = classDeclaration.getName() || 'Anonymous Class'; + const filePath = sourceFile.getFilePath(); + + // Process class itself + if (classDeclaration.getJsDocs().length === 0) { + undocumentedItems.push( + addUndocumentedItem( + filePath, + 'class', + className, + classDeclaration.getStartLineNumber(), + ), + ); + } else { + onDocumented(1); + } + + // Process properties + classDeclaration.getProperties().forEach((property: any) => { + if (property.getJsDocs().length === 0) { + undocumentedItems.push( + addUndocumentedItem( + filePath, + 'property', + property.getName(), + property.getStartLineNumber(), + ), + ); + } else { + onDocumented(1); + } + }); + + // Process methods + classDeclaration.getMethods().forEach((method: any) => { + if (method.getJsDocs().length === 0) { + undocumentedItems.push( + addUndocumentedItem( + filePath, + 'method', + method.getName(), + method.getStartLineNumber(), + ), + ); + } else { + onDocumented(1); + } + }); + }); +} + +function processDeclarations( + sourceFile: any, + undocumentedItems: UndocumentedItem[], + onDocumented: (count: number) => void, +): void { + const filePath = sourceFile.getFilePath(); + + // Process functions + processItems( + sourceFile.getFunctions(), + 'function', + item => item.getName() || 'Anonymous Function', + filePath, + undocumentedItems, + onDocumented, + ); + + // Process variables + sourceFile.getVariableStatements().forEach((statement: any) => { + statement.getDeclarations().forEach((declaration: any) => { + if (statement.getJsDocs().length === 0) { + undocumentedItems.push( + addUndocumentedItem( + filePath, + 'variable', + declaration.getName(), + declaration.getStartLineNumber(), + ), + ); + } else { + onDocumented(1); + } + }); + }); + + // Process interfaces and types + processItems( + sourceFile.getInterfaces(), + 'interface', + item => item.getName(), + filePath, + undocumentedItems, + onDocumented, + ); + processItems( + sourceFile.getTypeAliases(), + 'type', + item => item.getName(), + filePath, + undocumentedItems, + onDocumented, + ); +} + +function processItems( + items: any[], + type: string, + getName: (item: any) => string, + filePath: string, + undocumentedItems: UndocumentedItem[], + onDocumented: (count: number) => void, +): void { + items.forEach(item => { + if (item.getJsDocs().length === 0) { + undocumentedItems.push( + addUndocumentedItem( + filePath, + type, + getName(item), + item.getStartLineNumber(), + ), + ); + } else { + onDocumented(1); + } + }); +} + +function calculateCoverage( + undocumentedItems: UndocumentedItem[], + documented: number, +) { + const coverage = (documented / (documented + undocumentedItems.length)) * 100; + return { undocumentedItems, coverage }; +} diff --git a/packages/plugin-doc-coverage/src/lib/runner/index.ts b/packages/plugin-doc-coverage/src/lib/runner/index.ts index d5f83fc39..23aed7a92 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/index.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/index.ts @@ -5,21 +5,18 @@ import type { AuditOutput, RunnerConfig } from '@code-pushup/models'; import { ProcessError, ensureDirectoryExists, - executeProcess, filePathToCliArg, readJsonFile, ui, } from '@code-pushup/utils'; import type { DocCoveragePluginConfig } from '../config.js'; +import type { UndocumentedItem } from '../models.js'; import { - COMMANDS_FOR_LANGUAGES, - DEFAULT_OUTPUT_FOLDER_PATH, DEFAULT_SOURCE_GLOB, PLUGIN_CONFIG_PATH, - ProgrammingLanguage, RUNNER_OUTPUT_PATH, - type TypedocResult, } from './constants.js'; +import { processDocCoverage } from './doc-processer.js'; export { PLUGIN_CONFIG_PATH, RUNNER_OUTPUT_PATH } from './constants.js'; @@ -29,25 +26,19 @@ export { PLUGIN_CONFIG_PATH, RUNNER_OUTPUT_PATH } from './constants.js'; */ async function _executeTypedocProcess( config: DocCoveragePluginConfig, -): Promise { - const { - sourceGlob, - language, - outputFolderPath = DEFAULT_OUTPUT_FOLDER_PATH, - } = config; - const { args: originalArgs } = COMMANDS_FOR_LANGUAGES[language]; - const processedArgs = - language === ProgrammingLanguage.TypeScript - ? originalArgs - .replace('$outputFolderPath', outputFolderPath) - .replace('$sourceGlob', sourceGlob || DEFAULT_SOURCE_GLOB[language]) - : originalArgs; +): Promise<{ + undocumentedItems: UndocumentedItem[]; + coverage: number; +}> { + const { sourceGlob, language } = config; try { - await executeProcess({ - command: COMMANDS_FOR_LANGUAGES[language].command, - args: processedArgs.split(' '), - }); + return processDocCoverage(sourceGlob || DEFAULT_SOURCE_GLOB[language]); + // console.table(undocumentedItems); + // await executeProcess({ + // command: COMMANDS_FOR_LANGUAGES[language].command, + // args: processedArgs.split(' '), + // }); } catch (error) { if (error instanceof ProcessError) { ui().logger.error(bold('stdout from failed Typedoc process:')); @@ -65,12 +56,11 @@ async function _executeTypedocProcess( * Process the Typedoc results. * @param outputFolderPath - The path to the output folder. */ -async function _processTypedocResults(outputFolderPath: string): Promise { +async function _processTypedocResults( + undocumentedItems: UndocumentedItem[], + coverage: number, +): Promise { try { - const docData: TypedocResult = await readJsonFile( - path.join(outputFolderPath, 'coverage.json'), - ); - const coverage = docData.percent || 0; const auditOutputs: AuditOutput[] = [ { slug: 'percentage-coverage', @@ -78,9 +68,9 @@ async function _processTypedocResults(outputFolderPath: string): Promise { score: coverage / 100, displayValue: `${coverage} %`, details: { - issues: docData.notDocumented.map(file => ({ - message: 'Missing documentation', - source: { file }, + issues: undocumentedItems.map(item => ({ + message: `Missing documentation for a ${item.type}`, + source: { file: item.file, position: { startLine: item.line } }, severity: 'warning', })), }, @@ -105,9 +95,10 @@ async function _processTypedocResults(outputFolderPath: string): Promise { export async function executeRunner(): Promise { const config = await readJsonFile(PLUGIN_CONFIG_PATH); - await _executeTypedocProcess(config); + const processResult = await _executeTypedocProcess(config); await _processTypedocResults( - config.outputFolderPath || DEFAULT_OUTPUT_FOLDER_PATH, + processResult.undocumentedItems, + processResult.coverage, ); } diff --git a/packages/plugin-doc-coverage/src/lib/runner/runner.integration.test.ts b/packages/plugin-doc-coverage/src/lib/runner/runner.integration.test.ts index d7fc8e5e9..49cd966d3 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/runner.integration.test.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/runner.integration.test.ts @@ -19,7 +19,6 @@ describe('createRunnerConfig', () => { const runnerConfig = await createRunnerConfig('executeRunner.ts', { language: 'typescript', sourceGlob: 'src/**/*.ts', - outputFolderPath: 'documentation', }); expect(runnerConfig).toStrictEqual({ command: 'node', @@ -34,7 +33,6 @@ describe('createRunnerConfig', () => { const pluginConfig: DocCoveragePluginConfig = { language: 'typescript', sourceGlob: 'src/**/*.ts', - outputFolderPath: 'documentation', }; await createRunnerConfig('executeRunner.ts', pluginConfig); @@ -51,7 +49,7 @@ describe('executeRunner', () => { async () => { const config: DocCoveragePluginConfig = { language: 'typescript', - sourceGlob: '"packages/plugin-doc-coverage/mocks/component-mock.ts"', + sourceGlob: 'packages/plugin-doc-coverage/mocks/*.ts', }; await writeFile(PLUGIN_CONFIG_PATH, JSON.stringify(config)); diff --git a/packages/plugin-doc-coverage/tsconfig.lib.json b/packages/plugin-doc-coverage/tsconfig.lib.json index ef2f7e2b3..37e86c560 100644 --- a/packages/plugin-doc-coverage/tsconfig.lib.json +++ b/packages/plugin-doc-coverage/tsconfig.lib.json @@ -5,7 +5,7 @@ "declaration": true, "types": ["node"] }, - "include": ["src/**/*.ts"], + "include": ["src/**/*.ts", "src/lib/runner/doc-processer.js"], "exclude": [ "vite.config.unit.ts", "vite.config.integration.ts", diff --git a/tsconfig.base.json b/tsconfig.base.json index d088eca5a..c5cddb98c 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -25,6 +25,9 @@ "@code-pushup/core": ["packages/core/src/index.ts"], "@code-pushup/coverage-plugin": ["packages/plugin-coverage/src/index.ts"], "@code-pushup/eslint-plugin": ["packages/plugin-eslint/src/index.ts"], + "@code-pushup/doc-coverage-plugin": [ + "packages/plugin-doc-coverage/src/index.ts" + ], "@code-pushup/js-packages-plugin": [ "packages/plugin-js-packages/src/index.ts" ], From 164a86a660053563fa101c3b9faf829335e6d884 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Thu, 19 Dec 2024 00:08:02 +0100 Subject: [PATCH 04/39] feat: doc-processer give coverage by type of property. Some tests enhanced and some code improved --- .../mocks/component-mock.ts | 42 ++++ .../plugin-doc-coverage/src/lib/config.ts | 7 +- .../src/lib/config.unit.test.ts | 49 +---- .../src/lib/doc-coverage-plugin.ts | 2 +- .../src/lib/doc-coverage-plugin.unit.test.ts | 12 +- .../plugin-doc-coverage/src/lib/models.ts | 23 +++ .../runner/doc-processer.integration.test.ts | 21 ++ .../src/lib/runner/doc-processer.ts | 191 +++++++++++++----- .../src/lib/runner/index.ts | 110 ++++------ .../src/lib/runner/runner.integration.test.ts | 22 +- 10 files changed, 279 insertions(+), 200 deletions(-) create mode 100644 packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts diff --git a/packages/plugin-doc-coverage/mocks/component-mock.ts b/packages/plugin-doc-coverage/mocks/component-mock.ts index e66f4f859..acd80f441 100644 --- a/packages/plugin-doc-coverage/mocks/component-mock.ts +++ b/packages/plugin-doc-coverage/mocks/component-mock.ts @@ -5,3 +5,45 @@ export function DUMMY_FUNCTION() { return 'Hello World'; } + +export function DUMMY_FUNCTION_2() { + return 'Hello World 2'; +} + +class DummyClass { + /** + * Dummy property that returns 'Hello World 3'. + * @returns {string} - The string 'Hello World 3'. + */ + dummyProperty = 'Hello World 3'; + + /** + * Dummy method that returns 'Hello World 4'. + * @returns {string} - The string 'Hello World 4'. + */ + dummyMethod() { + return 'Hello World 4'; + } + + constructor() { + this.dummyProperty = 'Hello World 3'; + } +} + +export default DummyClass; + +export const variableDummy = 'Hello World 5'; + +export const variableDummy2 = 'Hello World 6'; + +/** Dummy variable that returns 'Hello World 7'. */ +export const variableDummy3 = 'Hello World 7'; + +/** Dummy interface that returns 'Hello World 8'. */ +export interface DummyInterface { + dummyProperty: string; + dummyMethod(): string; +} + +/** Dummy type that returns 'Hello World 9'. */ +export type DummyType = string; diff --git a/packages/plugin-doc-coverage/src/lib/config.ts b/packages/plugin-doc-coverage/src/lib/config.ts index a55a46137..853099796 100644 --- a/packages/plugin-doc-coverage/src/lib/config.ts +++ b/packages/plugin-doc-coverage/src/lib/config.ts @@ -3,16 +3,13 @@ import { z } from 'zod'; export type DocType = 'percentage-coverage'; export const docCoveragePluginConfigSchema = z.object({ - language: z.enum(['javascript', 'typescript'], { - description: 'Programming language of the source code to analyze', - }), sourceGlob: z .string({ description: 'Glob pattern to find source files', }) - .optional(), + .default('src/**/*.{ts,tsx}'), }); -export type DocCoveragePluginConfig = z.input< +export type DocCoveragePluginConfig = z.infer< typeof docCoveragePluginConfigSchema >; diff --git a/packages/plugin-doc-coverage/src/lib/config.unit.test.ts b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts index 36840ece8..4cb85ed8a 100644 --- a/packages/plugin-doc-coverage/src/lib/config.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts @@ -5,60 +5,23 @@ import { } from './config.js'; describe('docCoveragePluginConfigSchema', () => { - it('accepts a documentation coverage configuration with all entities', () => { + it('accepts a valid source glob pattern', () => { expect(() => docCoveragePluginConfigSchema.parse({ - language: 'typescript', sourceGlob: 'src/**/*.{ts,tsx}', } satisfies DocCoveragePluginConfig), ).not.toThrow(); }); - it('accepts minimal configuration with only language', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - language: 'javascript', - } satisfies DocCoveragePluginConfig), - ).not.toThrow(); - }); - - it('accepts configuration without sourceGlob', () => { - const config = { - language: 'typescript', - } satisfies DocCoveragePluginConfig; - const parsed = docCoveragePluginConfigSchema.parse(config); - - expect(parsed.sourceGlob).toBeUndefined(); - }); - - it('throws for missing language', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - sourceGlob: 'src/**/*.ts', - }), - ).toThrow('invalid_type'); + it('not throws for missing sourceGlob', () => { + expect(() => docCoveragePluginConfigSchema.parse({})).not.toThrow(); }); - it('throws for invalid language', () => { + it('throws for invalid sourceGlob type', () => { expect(() => docCoveragePluginConfigSchema.parse({ - language: 'python', - sourceGlob: 'src/**/*.py', + sourceGlob: 123, }), - ).toThrow('Invalid enum value'); - }); - - it('accepts both typescript and javascript as valid languages', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - language: 'typescript', - }), - ).not.toThrow(); - - expect(() => - docCoveragePluginConfigSchema.parse({ - language: 'javascript', - }), - ).not.toThrow(); + ).toThrow('Expected string'); }); }); diff --git a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts index 0504c8218..37d54858e 100644 --- a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts +++ b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts @@ -19,7 +19,7 @@ import { createRunnerConfig } from './runner/index.js'; * plugins: [ * // ... other plugins ... * await docCoveragePlugin({ - * language: 'typescript' + * sourceGlob: 'src/**/*.{ts,tsx}' * }) * ] * } diff --git a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts index 20e44b1da..992f6de6c 100644 --- a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts @@ -12,9 +12,7 @@ vi.mock('./runner/index.ts', () => ({ describe('docCoveragePlugin', () => { it('should initialise a Documentation coverage plugin', async () => { await expect( - docCoveragePlugin({ - language: 'typescript', - }), + docCoveragePlugin({ sourceGlob: 'src/**/*.ts' }), ).resolves.toStrictEqual( expect.objectContaining({ slug: 'doc-coverage', @@ -27,9 +25,7 @@ describe('docCoveragePlugin', () => { it('should generate percentage coverage audit', async () => { await expect( - docCoveragePlugin({ - language: 'typescript', - }), + docCoveragePlugin({ sourceGlob: 'src/**/*.ts' }), ).resolves.toStrictEqual( expect.objectContaining({ audits: [ @@ -47,9 +43,7 @@ describe('docCoveragePlugin', () => { it('should include package metadata', async () => { await expect( - docCoveragePlugin({ - language: 'typescript', - }), + docCoveragePlugin({ sourceGlob: 'src/**/*.ts' }), ).resolves.toStrictEqual( expect.objectContaining({ icon: 'folder-src', diff --git a/packages/plugin-doc-coverage/src/lib/models.ts b/packages/plugin-doc-coverage/src/lib/models.ts index ff4605b8c..dd61e1bdc 100644 --- a/packages/plugin-doc-coverage/src/lib/models.ts +++ b/packages/plugin-doc-coverage/src/lib/models.ts @@ -5,3 +5,26 @@ export type UndocumentedItem = { line: number; class?: string; }; + +export type CoverageByType = { + functions: number; + variables: number; + classes: number; + methods: number; + properties: number; + interfaces: number; + types: number; +}; + +export type CoverageKey = keyof CoverageByType; + +export type DocumentationStats = { + documented: number; + total: number; +}; + +export type CoverageResult = { + undocumentedItems: UndocumentedItem[]; + currentCoverage: number; + coverageByType: CoverageByType; +}; diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts new file mode 100644 index 000000000..94bebac27 --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts @@ -0,0 +1,21 @@ +import { processDocCoverage } from './doc-processer'; + +describe('docProcesser', () => { + it('should successfully get documentation coverage', () => { + const results = processDocCoverage( + 'packages/plugin-doc-coverage/mocks/**/*.ts', + ); + console.log(results); + expect(results).toBeDefined(); + expect(results.currentCoverage).toBe(60); + expect(results.coverageByType).toEqual({ + functions: 50, + variables: 33.33, + classes: 0, + methods: 100, + properties: 100, + interfaces: 100, + types: 100, + }); + }); +}); diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts index 86d75b343..46a2cbacf 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts @@ -1,19 +1,36 @@ import { Project } from 'ts-morph'; -import type { UndocumentedItem } from '../models.js'; +import type { + CoverageByType, + CoverageKey, + CoverageResult, + DocumentationStats, + UndocumentedItem, +} from '../models.js'; /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable functional/immutable-data */ /* eslint-disable @typescript-eslint/max-params */ /* eslint-disable functional/no-let */ -export function processDocCoverage(toInclude: string): { - undocumentedItems: UndocumentedItem[]; - coverage: number; -} { +/** + * Processes documentation coverage for TypeScript files in the specified path + * @param toInclude - The file path pattern to include for documentation analysis + * @returns {CoverageResult} Object containing coverage statistics and undocumented items + */ +export function processDocCoverage(toInclude: string): CoverageResult { const project = new Project(); project.addSourceFilesAtPaths(toInclude); - let itsDocumented = 0; + const stats: Record = { + functions: { documented: 0, total: 0 }, + variables: { documented: 0, total: 0 }, + classes: { documented: 0, total: 0 }, + methods: { documented: 0, total: 0 }, + properties: { documented: 0, total: 0 }, + interfaces: { documented: 0, total: 0 }, + types: { documented: 0, total: 0 }, + }; + const undocumentedItems: UndocumentedItem[] = []; project.getSourceFiles().forEach(sourceFile => { @@ -21,127 +38,146 @@ export function processDocCoverage(toInclude: string): { return; } - processClassDeclarations( - sourceFile, - undocumentedItems, - count => (itsDocumented += count), - ); - processDeclarations( - sourceFile, - undocumentedItems, - count => (itsDocumented += count), - ); + processClassDeclarations(sourceFile, undocumentedItems, stats); + processDeclarations(sourceFile, undocumentedItems, stats); }); - return calculateCoverage(undocumentedItems, itsDocumented); + return { + undocumentedItems, + currentCoverage: calculateOverallCoverage(stats), + coverageByType: calculateCoverageByType(stats), + }; } +/** + * Checks if a file is a test file based on its path + * @param filePath - The path of the file to check + * @returns {boolean} True if the file is a test file, false otherwise + */ function isTestFile(filePath: string): boolean { return filePath.includes('.spec.') || filePath.includes('.test.'); } +/** + * Creates an undocumented item entry + * @param file - The file path where the item was found + * @param type - The type of the undocumented item + * @param name - The name of the undocumented item + * @param line - The line number where the item appears + * @returns {UndocumentedItem} The undocumented item entry + */ function addUndocumentedItem( file: string, - type: string, + type: CoverageKey, name: string, line: number, ): UndocumentedItem { - return { - file, - type, - name, - line, - }; + return { file, type, name, line }; } +/** + * Processes class declarations in a source file and updates documentation statistics + * @param sourceFile - The source file to process + * @param undocumentedItems - Array to store undocumented items found + * @param stats - Object to track documentation statistics + */ function processClassDeclarations( sourceFile: any, undocumentedItems: UndocumentedItem[], - onDocumented: (count: number) => void, + stats: Record, ): void { sourceFile.getClasses().forEach((classDeclaration: any) => { const className = classDeclaration.getName() || 'Anonymous Class'; const filePath = sourceFile.getFilePath(); + stats.classes.total++; - // Process class itself if (classDeclaration.getJsDocs().length === 0) { undocumentedItems.push( addUndocumentedItem( filePath, - 'class', + 'classes', className, classDeclaration.getStartLineNumber(), ), ); } else { - onDocumented(1); + stats.classes.documented++; } // Process properties classDeclaration.getProperties().forEach((property: any) => { + stats.properties.total++; if (property.getJsDocs().length === 0) { undocumentedItems.push( addUndocumentedItem( filePath, - 'property', + 'properties', property.getName(), property.getStartLineNumber(), ), ); } else { - onDocumented(1); + stats.properties.documented++; } }); // Process methods classDeclaration.getMethods().forEach((method: any) => { + stats.methods.total++; if (method.getJsDocs().length === 0) { undocumentedItems.push( addUndocumentedItem( filePath, - 'method', + 'methods', method.getName(), method.getStartLineNumber(), ), ); } else { - onDocumented(1); + stats.methods.documented++; } }); }); } +/** + * Processes declarations (functions, variables, interfaces, and types) in a source file + * @param sourceFile - The source file to process + * @param undocumentedItems - Array to store undocumented items found + * @param stats - Object to track documentation statistics + */ function processDeclarations( sourceFile: any, undocumentedItems: UndocumentedItem[], - onDocumented: (count: number) => void, + stats: Record, ): void { const filePath = sourceFile.getFilePath(); // Process functions processItems( sourceFile.getFunctions(), - 'function', + 'functions', item => item.getName() || 'Anonymous Function', filePath, undocumentedItems, - onDocumented, + stats, ); // Process variables sourceFile.getVariableStatements().forEach((statement: any) => { statement.getDeclarations().forEach((declaration: any) => { + stats.variables.total++; if (statement.getJsDocs().length === 0) { undocumentedItems.push( addUndocumentedItem( filePath, - 'variable', + 'variables', declaration.getName(), declaration.getStartLineNumber(), ), ); } else { - onDocumented(1); + stats.variables.documented++; } }); }); @@ -149,31 +185,41 @@ function processDeclarations( // Process interfaces and types processItems( sourceFile.getInterfaces(), - 'interface', + 'interfaces', item => item.getName(), filePath, undocumentedItems, - onDocumented, + stats, ); processItems( sourceFile.getTypeAliases(), - 'type', + 'types', item => item.getName(), filePath, undocumentedItems, - onDocumented, + stats, ); } +/** + * Generic function to process a collection of items and update documentation statistics + * @param items - Array of items to process + * @param type - The type of items being processed + * @param getName - Function to extract the name from an item + * @param filePath - The path of the file being processed + * @param undocumentedItems - Array to store undocumented items found + * @param stats - Object to track documentation statistics + */ function processItems( items: any[], - type: string, + type: CoverageKey, getName: (item: any) => string, filePath: string, undocumentedItems: UndocumentedItem[], - onDocumented: (count: number) => void, + stats: Record, ): void { items.forEach(item => { + stats[type].total++; if (item.getJsDocs().length === 0) { undocumentedItems.push( addUndocumentedItem( @@ -184,15 +230,60 @@ function processItems( ), ); } else { - onDocumented(1); + stats[type].documented++; } }); } -function calculateCoverage( - undocumentedItems: UndocumentedItem[], - documented: number, -) { - const coverage = (documented / (documented + undocumentedItems.length)) * 100; - return { undocumentedItems, coverage }; +/** + * Calculates the overall documentation coverage percentage + * @param stats - Object containing documentation statistics + * @returns {number} The overall coverage percentage (0-100) + */ +function calculateOverallCoverage( + stats: Record, +): number { + let totalDocumented = 0; + let totalItems = 0; + + Object.values(stats).forEach(({ documented, total }) => { + totalDocumented += documented; + totalItems += total; + }); + + return totalItems === 0 ? 0 : (totalDocumented / totalItems) * 100; +} + +/** + * Calculates documentation coverage percentage for each type + * @param stats - Object containing documentation statistics + * @returns {CoverageByType} Object containing coverage percentages for each type + */ +function calculateCoverageByType( + stats: Record, +): CoverageByType { + const calculatePercentage = (documented: number, total: number) => + total === 0 ? 0 : Number(((documented / total) * 100).toFixed(2)); + + return { + functions: calculatePercentage( + stats.functions.documented, + stats.functions.total, + ), + variables: calculatePercentage( + stats.variables.documented, + stats.variables.total, + ), + classes: calculatePercentage(stats.classes.documented, stats.classes.total), + methods: calculatePercentage(stats.methods.documented, stats.methods.total), + properties: calculatePercentage( + stats.properties.documented, + stats.properties.total, + ), + interfaces: calculatePercentage( + stats.interfaces.documented, + stats.interfaces.total, + ), + types: calculatePercentage(stats.types.documented, stats.types.total), + }; } diff --git a/packages/plugin-doc-coverage/src/lib/runner/index.ts b/packages/plugin-doc-coverage/src/lib/runner/index.ts index 23aed7a92..c56713ada 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/index.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/index.ts @@ -10,35 +10,19 @@ import { ui, } from '@code-pushup/utils'; import type { DocCoveragePluginConfig } from '../config.js'; -import type { UndocumentedItem } from '../models.js'; -import { - DEFAULT_SOURCE_GLOB, - PLUGIN_CONFIG_PATH, - RUNNER_OUTPUT_PATH, -} from './constants.js'; +import type { CoverageResult } from '../models.js'; +import { PLUGIN_CONFIG_PATH, RUNNER_OUTPUT_PATH } from './constants.js'; import { processDocCoverage } from './doc-processer.js'; export { PLUGIN_CONFIG_PATH, RUNNER_OUTPUT_PATH } from './constants.js'; -/** - * Execute the Typedoc process. - * @param config - The configuration for the Typedoc process. - */ -async function _executeTypedocProcess( - config: DocCoveragePluginConfig, -): Promise<{ - undocumentedItems: UndocumentedItem[]; - coverage: number; -}> { - const { sourceGlob, language } = config; - +export async function executeRunner(): Promise { try { - return processDocCoverage(sourceGlob || DEFAULT_SOURCE_GLOB[language]); - // console.table(undocumentedItems); - // await executeProcess({ - // command: COMMANDS_FOR_LANGUAGES[language].command, - // args: processedArgs.split(' '), - // }); + const config = + await readJsonFile(PLUGIN_CONFIG_PATH); + console.log(config.sourceGlob, 'dadawdawd'); + const processResult = processDocCoverage(config.sourceGlob); + await _createFinalReport(processResult); } catch (error) { if (error instanceof ProcessError) { ui().logger.error(bold('stdout from failed Typedoc process:')); @@ -52,61 +36,10 @@ async function _executeTypedocProcess( } } -/** - * Process the Typedoc results. - * @param outputFolderPath - The path to the output folder. - */ -async function _processTypedocResults( - undocumentedItems: UndocumentedItem[], - coverage: number, -): Promise { - try { - const auditOutputs: AuditOutput[] = [ - { - slug: 'percentage-coverage', - value: coverage, - score: coverage / 100, - displayValue: `${coverage} %`, - details: { - issues: undocumentedItems.map(item => ({ - message: `Missing documentation for a ${item.type}`, - source: { file: item.file, position: { startLine: item.line } }, - severity: 'warning', - })), - }, - }, - ]; - - await ensureDirectoryExists(path.dirname(RUNNER_OUTPUT_PATH)); - await writeFile(RUNNER_OUTPUT_PATH, JSON.stringify(auditOutputs)); - } catch (error) { - if (error instanceof ProcessError) { - ui().logger.error(bold('stdout from failed coverage tool process:')); - ui().logger.error(error.stdout); - ui().logger.error(bold('stderr from failed coverage tool process:')); - ui().logger.error(error.stderr); - throw new Error( - 'Doc Coverage plugin: Running Compodoc failed. Please check the error above.', - ); - } - } -} - -export async function executeRunner(): Promise { - const config = - await readJsonFile(PLUGIN_CONFIG_PATH); - const processResult = await _executeTypedocProcess(config); - await _processTypedocResults( - processResult.undocumentedItems, - processResult.coverage, - ); -} - export async function createRunnerConfig( scriptPath: string, config: DocCoveragePluginConfig, ): Promise { - // Create JSON config for executeRunner await ensureDirectoryExists(path.dirname(PLUGIN_CONFIG_PATH)); await writeFile(PLUGIN_CONFIG_PATH, JSON.stringify(config)); @@ -116,3 +49,30 @@ export async function createRunnerConfig( outputFile: RUNNER_OUTPUT_PATH, }; } + +/** + * Create the final report. + * @param coverageResult - The coverage result. + */ +async function _createFinalReport( + coverageResult: CoverageResult, +): Promise { + const auditOutputs: AuditOutput[] = [ + { + slug: 'percentage-coverage', + value: coverageResult.currentCoverage, + score: coverageResult.currentCoverage / 100, + displayValue: `${coverageResult.currentCoverage} %`, + details: { + issues: coverageResult.undocumentedItems.map(item => ({ + message: `Missing documentation for a ${item.type}`, + source: { file: item.file, position: { startLine: item.line } }, + severity: 'warning', + })), + }, + }, + ]; + + await ensureDirectoryExists(path.dirname(RUNNER_OUTPUT_PATH)); + await writeFile(RUNNER_OUTPUT_PATH, JSON.stringify(auditOutputs)); +} diff --git a/packages/plugin-doc-coverage/src/lib/runner/runner.integration.test.ts b/packages/plugin-doc-coverage/src/lib/runner/runner.integration.test.ts index 49cd966d3..8d8fe081a 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/runner.integration.test.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/runner.integration.test.ts @@ -1,10 +1,6 @@ import { writeFile } from 'node:fs/promises'; import { describe, it } from 'vitest'; -import type { - AuditOutput, - AuditOutputs, - RunnerConfig, -} from '@code-pushup/models'; +import type { AuditOutputs, RunnerConfig } from '@code-pushup/models'; import { readJsonFile, removeDirectoryIfExists } from '@code-pushup/utils'; import type { DocCoveragePluginConfig } from '../config.js'; import { @@ -17,7 +13,6 @@ import { createRunnerConfig, executeRunner } from './index.js'; describe('createRunnerConfig', () => { it('should create a valid runner config', async () => { const runnerConfig = await createRunnerConfig('executeRunner.ts', { - language: 'typescript', sourceGlob: 'src/**/*.ts', }); expect(runnerConfig).toStrictEqual({ @@ -31,7 +26,6 @@ describe('createRunnerConfig', () => { await removeDirectoryIfExists(WORKDIR); const pluginConfig: DocCoveragePluginConfig = { - language: 'typescript', sourceGlob: 'src/**/*.ts', }; @@ -46,9 +40,11 @@ describe('createRunnerConfig', () => { describe('executeRunner', () => { it( 'should successfully execute runner', + { + timeout: 60 * 1000, + }, async () => { const config: DocCoveragePluginConfig = { - language: 'typescript', sourceGlob: 'packages/plugin-doc-coverage/mocks/*.ts', }; @@ -56,15 +52,7 @@ describe('executeRunner', () => { await executeRunner(); const results = await readJsonFile(RUNNER_OUTPUT_PATH); - expect(results).toStrictEqual([ - expect.objectContaining({ - slug: 'percentage-coverage', - score: 1, - value: 100, - displayValue: '100 %', - } satisfies AuditOutput), - ]); + expect(results).toBeDefined(); }, - { timeout: 60 * 1000 }, ); }); From 3fe6e3b8cca195650eb6e42645147ac80e121f6c Mon Sep 17 00:00:00 2001 From: Alejandro Date: Fri, 20 Dec 2024 03:35:13 +0100 Subject: [PATCH 05/39] feat(plugin-doc-coverage): improve code fragmentation, tests and models. Solid version working --- code-pushup.config.bundled_oq2x2csd2at.mjs | 1210 +++++++++++++++++ code-pushup.config.ts | 19 +- code-pushup.preset.ts | 53 +- .../mocks/component-mock.spec.ts | 16 + .../mocks/component-mock.ts | 74 +- .../mocks/fixtures/angular/app.component.css | 4 + .../mocks/fixtures/angular/app.component.html | 1 + .../fixtures/angular/app.component.spec.ts | 3 + .../mocks/fixtures/angular/app.component.ts | 18 + .../fixtures/angular/map-event.function.ts | 8 + .../mocks/source-files.mock.ts | 114 ++ .../plugin-doc-coverage/src/lib/config.ts | 9 +- .../src/lib/config.unit.test.ts | 2 +- .../plugin-doc-coverage/src/lib/constants.ts | 68 + .../src/lib/doc-coverage-plugin.ts | 53 +- .../src/lib/doc-coverage-plugin.unit.test.ts | 12 +- .../plugin-doc-coverage/src/lib/models.ts | 31 +- .../doc-processer.integration.test.ts.snap | 139 ++ .../doc-processer.unit.test.ts.snap | 92 ++ .../src/lib/runner/constants.ts | 42 - .../runner/doc-processer.integration.test.ts | 34 +- .../src/lib/runner/doc-processer.ts | 350 ++--- .../src/lib/runner/doc-processer.unit.test.ts | 198 +++ .../runner/doc-processer.unit.test.ts.snap | 125 ++ .../src/lib/runner/index.ts | 78 -- .../src/lib/runner/models.ts | 37 + .../src/lib/runner/runner.integration.test.ts | 58 - .../src/lib/runner/runner.ts | 49 + .../src/lib/runner/utils.ts | 61 + packages/plugin-doc-coverage/src/lib/utils.ts | 46 + .../plugin-doc-coverage/tsconfig.test.json | 3 +- 31 files changed, 2417 insertions(+), 590 deletions(-) create mode 100644 code-pushup.config.bundled_oq2x2csd2at.mjs create mode 100644 packages/plugin-doc-coverage/mocks/component-mock.spec.ts create mode 100644 packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.css create mode 100644 packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.html create mode 100644 packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.spec.ts create mode 100644 packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.ts create mode 100644 packages/plugin-doc-coverage/mocks/fixtures/angular/map-event.function.ts create mode 100644 packages/plugin-doc-coverage/mocks/source-files.mock.ts create mode 100644 packages/plugin-doc-coverage/src/lib/constants.ts create mode 100644 packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.integration.test.ts.snap create mode 100644 packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.unit.test.ts.snap delete mode 100644 packages/plugin-doc-coverage/src/lib/runner/constants.ts create mode 100644 packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts create mode 100644 packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts.snap delete mode 100644 packages/plugin-doc-coverage/src/lib/runner/index.ts create mode 100644 packages/plugin-doc-coverage/src/lib/runner/models.ts delete mode 100644 packages/plugin-doc-coverage/src/lib/runner/runner.integration.test.ts create mode 100644 packages/plugin-doc-coverage/src/lib/runner/runner.ts create mode 100644 packages/plugin-doc-coverage/src/lib/runner/utils.ts create mode 100644 packages/plugin-doc-coverage/src/lib/utils.ts diff --git a/code-pushup.config.bundled_oq2x2csd2at.mjs b/code-pushup.config.bundled_oq2x2csd2at.mjs new file mode 100644 index 000000000..4c033e42d --- /dev/null +++ b/code-pushup.config.bundled_oq2x2csd2at.mjs @@ -0,0 +1,1210 @@ +// code-pushup.config.ts +// packages/utils/src/lib/logging.ts +import isaacs_cliui from '@isaacs/cliui'; +import { cliui } from '@poppinss/cliui'; +// packages/plugin-coverage/src/lib/runner/index.ts +import { bold } from 'ansis'; +// packages/plugin-coverage/src/lib/nx/coverage-paths.ts +import { bold as bold2 } from 'ansis'; +// packages/plugin-lighthouse/src/lib/normalize-flags.ts +import { bold as bold3, yellow } from 'ansis'; +// packages/plugin-lighthouse/src/lib/runner/utils.ts +import { bold as bold7 } from 'ansis'; +// packages/plugin-lighthouse/src/lib/runner/details/details.ts +import { bold as bold6, yellow as yellow2 } from 'ansis'; +// packages/plugin-lighthouse/src/lib/runner/details/item-value.ts +import { bold as bold4 } from 'ansis'; +// packages/plugin-lighthouse/src/lib/runner/details/utils.ts +import { bold as bold5 } from 'ansis'; +// packages/utils/src/lib/reports/utils.ts +import ansis from 'ansis'; +// packages/utils/src/lib/file-system.ts +import { bold as bold8, gray } from 'ansis'; +import { underline } from 'ansis'; +// packages/utils/src/lib/progress.ts +import { black, bold as bold9, gray as gray2, green } from 'ansis'; +// packages/utils/src/lib/reports/log-stdout-summary.ts +import { bold as bold11, cyan, cyanBright, green as green2, red } from 'ansis'; +// packages/utils/src/lib/zod-validation.ts +import { bold as bold12, red as red2 } from 'ansis'; +// packages/plugin-js-packages/src/lib/runner/audit/transform.ts +import { md } from 'build-md'; +// packages/plugin-js-packages/src/lib/runner/outdated/transform.ts +import { md as md2 } from 'build-md'; +import { md as md3 } from 'build-md'; +// packages/utils/src/lib/reports/generate-md-report.ts +import { MarkdownDocument as MarkdownDocument3, md as md6 } from 'build-md'; +// packages/utils/src/lib/reports/formatting.ts +import { MarkdownDocument, md as md4 } from 'build-md'; +// packages/utils/src/lib/reports/generate-md-report-categoy-section.ts +import { MarkdownDocument as MarkdownDocument2, md as md5 } from 'build-md'; +// packages/utils/src/lib/reports/generate-md-reports-diff.ts +import { MarkdownDocument as MarkdownDocument5, md as md8 } from 'build-md'; +// packages/utils/src/lib/reports/generate-md-reports-diff-utils.ts +import { MarkdownDocument as MarkdownDocument4, md as md7 } from 'build-md'; +import { bundleRequire } from 'bundle-require'; +// packages/plugin-lighthouse/src/lib/constants.ts +import { DEFAULT_FLAGS } from 'chrome-launcher/dist/flags.js'; +import 'dotenv/config'; +// packages/plugin-eslint/src/lib/setup.ts +import { ESLint } from 'eslint'; +// packages/plugin-eslint/src/lib/meta/versions/detect.ts +import { ESLint as ESLint2 } from 'eslint'; +// packages/plugin-eslint/src/lib/meta/versions/flat.ts +import { builtinRules } from 'eslint/use-at-your-own-risk'; +// packages/plugin-lighthouse/src/lib/runner/constants.ts +import { defaultConfig } from 'lighthouse'; +import log from 'lighthouse-logger'; +// packages/plugin-lighthouse/src/lib/runner/runner.ts +import { runLighthouse } from 'lighthouse/cli/run.js'; +import desktopConfig from 'lighthouse/core/config/desktop-config.js'; +import experimentalConfig from 'lighthouse/core/config/experimental-config.js'; +import perfConfig from 'lighthouse/core/config/perf-config.js'; +import { MultiProgressBars } from 'multi-progress-bars'; +// packages/utils/src/lib/execute-process.ts +import { spawn } from 'node:child_process'; +// packages/plugin-eslint/src/lib/meta/hash.ts +import { createHash } from 'node:crypto'; +import { writeFile } from 'node:fs/promises'; +// packages/plugin-eslint/src/lib/runner/index.ts +import { writeFile as writeFile2 } from 'node:fs/promises'; +// packages/plugin-js-packages/src/lib/runner/index.ts +import { writeFile as writeFile3 } from 'node:fs/promises'; +// packages/plugin-js-packages/src/lib/package-managers/derive-package-manager.ts +import { readFile } from 'node:fs/promises'; +import { + mkdir, + readFile as readFile2, + readdir, + rm, + stat, +} from 'node:fs/promises'; +// packages/plugin-coverage/src/lib/coverage-plugin.ts +import { createRequire } from 'node:module'; +// packages/plugin-eslint/src/lib/eslint-plugin.ts +import { createRequire as createRequire2 } from 'node:module'; +// packages/plugin-js-packages/src/lib/js-packages-plugin.ts +import { createRequire as createRequire3 } from 'node:module'; +// packages/plugin-lighthouse/src/lib/lighthouse-plugin.ts +import { createRequire as createRequire4 } from 'node:module'; +// packages/plugin-eslint/src/lib/runner/lint.ts +import { platform } from 'node:os'; +// packages/utils/src/lib/transform.ts +import { platform as platform2 } from 'node:os'; +import path4 from 'node:path'; +import path3 from 'node:path'; +// packages/plugin-coverage/src/lib/runner/constants.ts +import path from 'node:path'; +// packages/plugin-coverage/src/lib/runner/lcov/lcov-runner.ts +import path2 from 'node:path'; +import path5 from 'node:path'; +import path8 from 'node:path'; +import path6 from 'node:path'; +import path7 from 'node:path'; +// packages/plugin-eslint/src/lib/nx/utils.ts +import path9 from 'node:path'; +import path14 from 'node:path'; +// packages/plugin-js-packages/src/lib/runner/utils.ts +import path10 from 'node:path'; +import path12 from 'node:path'; +// packages/plugin-js-packages/src/lib/runner/constants.ts +import path11 from 'node:path'; +import path13 from 'node:path'; +import path15 from 'node:path'; +import path16 from 'node:path'; +import path17 from 'node:path'; +import path18 from 'node:path'; +// packages/utils/src/lib/git/git.ts +import path19 from 'node:path'; +import path20 from 'node:path'; +// packages/utils/src/lib/reports/load-report.ts +import path21 from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { fileURLToPath as fileURLToPath2 } from 'node:url'; +import { pathToFileURL } from 'node:url'; +import { fileURLToPath as fileURLToPath3 } from 'node:url'; +// packages/plugin-coverage/src/lib/runner/lcov/parse-lcov.ts +import parseLcovExport from 'parse-lcov'; +import { clean, diff, neq } from 'semver'; +// packages/utils/src/lib/semver.ts +import { rcompare, valid } from 'semver'; +// packages/utils/src/lib/git/git.commits-and-tags.ts +import { simpleGit } from 'simple-git'; +import { simpleGit as simpleGit2 } from 'simple-git'; +// packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts +import { Project } from 'ts-morph'; +// packages/plugin-doc-coverage/src/lib/utils.ts +import { SyntaxKind } from 'typescript'; +import { z as z5 } from 'zod'; +// packages/plugin-coverage/src/lib/config.ts +import { z } from 'zod'; +// packages/plugin-doc-coverage/src/lib/config.ts +import { z as z2 } from 'zod'; +// packages/plugin-eslint/src/lib/config.ts +import { z as z3 } from 'zod'; +// packages/plugin-js-packages/src/lib/config.ts +import { z as z4 } from 'zod'; +import { issueSeveritySchema } from '@code-pushup/models'; +import { DEFAULT_PERSIST_OUTPUT_DIR } from '@code-pushup/models'; +// packages/plugin-lighthouse/src/lib/runner/details/opportunity.type.ts +import { tableSchema as tableSchema2 } from '@code-pushup/models'; +// packages/plugin-lighthouse/src/lib/runner/details/table.type.ts +import { tableSchema } from '@code-pushup/models'; +// packages/utils/src/index.ts +import { exists as exists4 } from '@code-pushup/models'; +// packages/utils/src/lib/formatting.ts +import { + MAX_DESCRIPTION_LENGTH, + MAX_ISSUE_MESSAGE_LENGTH, + MAX_TITLE_LENGTH, +} from '@code-pushup/models'; +import { commitSchema } from '@code-pushup/models'; +import { reportSchema } from '@code-pushup/models'; +import { capitalize } from '@code-pushup/utils'; +import { + ProcessError, + ensureDirectoryExists, + executeProcess, + filePathToCliArg, + readJsonFile, + ui as ui2, +} from '@code-pushup/utils'; +import { pluginWorkDir } from '@code-pushup/utils'; +import { exists, readTextFile, toUnixNewlines, ui } from '@code-pushup/utils'; +// packages/plugin-coverage/src/lib/runner/lcov/transform.ts +import { toNumberPrecision, toOrdinal } from '@code-pushup/utils'; +import { importModule, ui as ui3 } from '@code-pushup/utils'; +import { toArray } from '@code-pushup/utils'; +// packages/plugin-eslint/src/lib/meta/groups.ts +import { objectToKeys, slugify as slugify2 } from '@code-pushup/utils'; +import { slugify } from '@code-pushup/utils'; +// packages/plugin-eslint/src/lib/meta/parse.ts +import { toArray as toArray2 } from '@code-pushup/utils'; +import { + exists as exists2, + findNearestFile, + toArray as toArray3, + ui as ui4, +} from '@code-pushup/utils'; +// packages/plugin-eslint/src/lib/meta/versions/legacy.ts +import { + distinct, + exists as exists3, + toArray as toArray4, + ui as ui5, +} from '@code-pushup/utils'; +import { fileExists } from '@code-pushup/utils'; +// packages/plugin-eslint/src/lib/meta/transform.ts +import { truncateDescription, truncateTitle } from '@code-pushup/utils'; +import { + ensureDirectoryExists as ensureDirectoryExists2, + filePathToCliArg as filePathToCliArg3, + pluginWorkDir as pluginWorkDir2, + readJsonFile as readJsonFile2, +} from '@code-pushup/utils'; +import { + distinct as distinct2, + executeProcess as executeProcess2, + filePathToCliArg as filePathToCliArg2, + toArray as toArray5, +} from '@code-pushup/utils'; +// packages/plugin-eslint/src/lib/runner/transform.ts +import { + compareIssueSeverity, + countOccurrences, + objectToEntries, + pluralizeToken, + truncateIssueMessage, + ui as ui6, +} from '@code-pushup/utils'; +import { + fileExists as fileExists2, + toArray as toArray6, +} from '@code-pushup/utils'; +// packages/plugin-js-packages/src/lib/package-managers/npm/npm.ts +import { objectToKeys as objectToKeys3 } from '@code-pushup/utils'; +import { + crawlFileSystem, + objectFromEntries, + objectToKeys as objectToKeys2, + readJsonFile as readJsonFile3, +} from '@code-pushup/utils'; +// packages/plugin-js-packages/src/lib/package-managers/npm/audit-result.ts +import { objectToEntries as objectToEntries2 } from '@code-pushup/utils'; +// packages/plugin-js-packages/src/lib/package-managers/npm/outdated-result.ts +import { objectToEntries as objectToEntries3 } from '@code-pushup/utils'; +// packages/plugin-js-packages/src/lib/package-managers/pnpm/pnpm.ts +import { objectToKeys as objectToKeys4 } from '@code-pushup/utils'; +// packages/plugin-js-packages/src/lib/package-managers/pnpm/outdated-result.ts +import { objectToEntries as objectToEntries4 } from '@code-pushup/utils'; +// packages/plugin-js-packages/src/lib/package-managers/yarn-classic/audit-result.ts +import { fromJsonLines } from '@code-pushup/utils'; +// packages/plugin-js-packages/src/lib/package-managers/yarn-classic/outdated-result.ts +import { + fromJsonLines as fromJsonLines2, + objectFromEntries as objectFromEntries2, + objectToEntries as objectToEntries5, + objectToKeys as objectToKeys5, +} from '@code-pushup/utils'; +import { + ensureDirectoryExists as ensureDirectoryExists3, + executeProcess as executeProcess3, + filePathToCliArg as filePathToCliArg4, + isPromiseFulfilledResult, + isPromiseRejectedResult, + objectFromEntries as objectFromEntries4, + readJsonFile as readJsonFile4, +} from '@code-pushup/utils'; +import { objectToEntries as objectToEntries6 } from '@code-pushup/utils'; +import { pluginWorkDir as pluginWorkDir3 } from '@code-pushup/utils'; +import { + objectFromEntries as objectFromEntries3, + pluralize, +} from '@code-pushup/utils'; +// packages/plugin-js-packages/src/lib/runner/outdated/constants.ts +import { objectToKeys as objectToKeys6 } from '@code-pushup/utils'; +import { fileExists as fileExists3 } from '@code-pushup/utils'; +// packages/plugin-js-packages/src/lib/package-managers/derive-yarn.ts +import { executeProcess as executeProcess4 } from '@code-pushup/utils'; +import { ui as ui7 } from '@code-pushup/utils'; +import { ensureDirectoryExists as ensureDirectoryExists4 } from '@code-pushup/utils'; +import { + formatReportScore, + importModule as importModule2, + readJsonFile as readJsonFile5, + ui as ui10, +} from '@code-pushup/utils'; +import { ui as ui9 } from '@code-pushup/utils'; +import { + formatBytes as formatBytes2, + formatDuration as formatDuration2, + html as html2, +} from '@code-pushup/utils'; +import { + formatBytes, + formatDuration, + html, + truncateText, + ui as ui8, +} from '@code-pushup/utils'; +// packages/plugin-lighthouse/src/lib/utils.ts +import { filterItemRefsBy, toArray as toArray7 } from '@code-pushup/utils'; + +var coverageTypeSchema = z.enum(['function', 'branch', 'line']); +var coverageResultSchema = z.union([ + z.object({ + resultsPath: z + .string({ + description: 'Path to coverage results for Nx setup.', + }) + .includes('lcov'), + pathToProject: z + .string({ + description: + 'Path from workspace root to project root. Necessary for LCOV reports which provide a relative path.', + }) + .optional(), + }), + z + .string({ + description: 'Path to coverage results for a single project setup.', + }) + .includes('lcov'), +]); +var coveragePluginConfigSchema = z.object({ + coverageToolCommand: z + .object({ + command: z + .string({ description: 'Command to run coverage tool.' }) + .min(1), + args: z + .array(z.string(), { + description: 'Arguments to be passed to the coverage tool.', + }) + .optional(), + }) + .optional(), + coverageTypes: z + .array(coverageTypeSchema, { + description: 'Coverage types measured. Defaults to all available types.', + }) + .min(1) + .default(['function', 'branch', 'line']), + reports: z + .array(coverageResultSchema, { + description: + 'Path to all code coverage report files. Only LCOV format is supported for now.', + }) + .min(1), + perfectScoreThreshold: z + .number({ + description: + 'Score will be 1 (perfect) for this coverage and above. Score range is 0 - 1.', + }) + .gt(0) + .max(1) + .optional(), +}); + +var WORKDIR = pluginWorkDir('coverage'); +var RUNNER_OUTPUT_PATH = path.join(WORKDIR, 'runner-output.json'); +var PLUGIN_CONFIG_PATH = path.join( + process.cwd(), + WORKDIR, + 'plugin-config.json', +); + +var godKnows = parseLcovExport; +var parseLcov = 'default' in godKnows ? godKnows.default : godKnows; + +var docCoveragePluginConfigSchema = z2.object({ + onlyAudits: z2.array(z2.string()).optional(), + sourceGlob: z2 + .array(z2.string()) + .default(['src/**/*.{ts,tsx}', '!**/*.spec.ts', '!**/*.test.ts']), +}); + +// packages/plugin-doc-coverage/src/lib/constants.ts +var PLUGIN_SLUG = 'doc-coverage'; +var AUDITS_MAP = { + 'classes-coverage': { + slug: 'classes-coverage', + title: 'Classes coverage', + description: 'Coverage of classes', + }, + 'methods-coverage': { + slug: 'methods-coverage', + title: 'Methods coverage', + description: 'Coverage of methods', + }, + 'functions-coverage': { + slug: 'functions-coverage', + title: 'Functions coverage', + description: 'Coverage of functions', + }, + 'interfaces-coverage': { + slug: 'interfaces-coverage', + title: 'Interfaces coverage', + description: 'Coverage of interfaces', + }, + 'variables-coverage': { + slug: 'variables-coverage', + title: 'Variables coverage', + description: 'Coverage of variables', + }, + 'properties-coverage': { + slug: 'properties-coverage', + title: 'Properties coverage', + description: 'Coverage of properties', + }, + 'types-coverage': { + slug: 'types-coverage', + title: 'Types coverage', + description: 'Coverage of types', + }, + 'enums-coverage': { + slug: 'enums-coverage', + title: 'Enums coverage', + description: 'Coverage of enums', + }, +}; +var groups = [ + { + slug: 'documentation-coverage', + title: 'Documentation coverage', + description: 'Documentation coverage', + refs: Object.keys(AUDITS_MAP).map(slug => { + switch (slug) { + case 'classes-coverage': + case 'functions-coverage': + case 'methods-coverage': + return { slug, weight: 2 }; + case 'interfaces-coverage': + case 'properties-coverage': + case 'types-coverage': + default: + return { slug, weight: 1 }; + } + }), + }, +]; + +function filterAuditsByPluginConfig(config2) { + const { onlyAudits } = config2; + if (!onlyAudits || onlyAudits.length === 0) { + return Object.values(AUDITS_MAP); + } + return Object.values(AUDITS_MAP).filter(audit => + onlyAudits.includes(audit.slug), + ); +} +function filterGroupsByOnlyAudits(groups2, options) { + const audits2 = filterAuditsByPluginConfig(options); + return groups2 + .map(group => ({ + ...group, + refs: group.refs.filter(ref => + audits2.some(audit => audit.slug === ref.slug), + ), + })) + .filter(group => group.refs.length > 0); +} +function trasformCoverageReportToAudits(coverageResult, options) { + return Object.entries(coverageResult) + .filter( + ([type]) => + !options.onlyAudits?.length || + options.onlyAudits.includes(`${type}-coverage`), + ) + .map(([type, items]) => { + const coverageType = type; + const coverage = items.coverage; + return { + slug: `${coverageType}-coverage`, + value: coverage, + score: coverage / 100, + displayValue: `${coverage} %`, + details: { + issues: items.issues.map(({ file, line }) => ({ + message: 'Missing documentation', + source: { file, position: { startLine: line } }, + severity: 'warning', + })), + }, + }; + }); +} +function getCoverageTypeFromKind(kind) { + switch (kind) { + case SyntaxKind.ClassDeclaration: + return 'classes'; + case SyntaxKind.MethodDeclaration: + return 'methods'; + case SyntaxKind.FunctionDeclaration: + return 'functions'; + case SyntaxKind.InterfaceDeclaration: + return 'interfaces'; + case SyntaxKind.EnumDeclaration: + return 'enums'; + case SyntaxKind.VariableDeclaration: + return 'variables'; + case SyntaxKind.PropertyDeclaration: + return 'properties'; + case SyntaxKind.TypeAliasDeclaration: + return 'types'; + default: + throw new Error(`Unsupported syntax kind: ${kind}`); + } +} + +// packages/plugin-doc-coverage/src/lib/runner/utils.ts +function createEmptyUnprocessedCoverageReport() { + return { + enums: { nodesCount: 0, issues: [] }, + interfaces: { nodesCount: 0, issues: [] }, + types: { nodesCount: 0, issues: [] }, + functions: { nodesCount: 0, issues: [] }, + variables: { nodesCount: 0, issues: [] }, + classes: { nodesCount: 0, issues: [] }, + methods: { nodesCount: 0, issues: [] }, + properties: { nodesCount: 0, issues: [] }, + }; +} +function calculateCoverage2(result) { + return Object.fromEntries( + Object.entries(result).map(([key, value]) => { + const type = key; + return [ + type, + { + coverage: + value.nodesCount === 0 + ? 100 + : (1 - value.issues.length / value.nodesCount) * 100, + issues: value.issues, + nodesCount: value.nodesCount, + }, + ]; + }), + ); +} + +// packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts +function processDocCoverage(config2) { + const project = new Project(); + project.addSourceFilesAtPaths(config2.sourceGlob); + return getUnprocessedCoverageReport(project.getSourceFiles()); +} +function getUnprocessedCoverageReport(sourceFiles) { + const unprocessedCoverageReport = sourceFiles.reduce( + (coverageReportOfAllFiles, sourceFile) => { + const filePath = sourceFile.getFilePath(); + const classes = sourceFile.getClasses(); + const allNodesFromFile = [ + ...sourceFile.getFunctions(), + ...classes, + ...getClassNodes(classes), + ...sourceFile.getTypeAliases(), + ...sourceFile.getEnums(), + ...sourceFile.getInterfaces(), + // ...sourceFile.getVariableStatements().flatMap(statement => statement.getDeclarations()) + ]; + const coverageReportOfCurrentFile = allNodesFromFile.reduce( + (acc, node) => { + const nodeType = getCoverageTypeFromKind(node.getKind()); + acc[nodeType].nodesCount++; + if (node.getJsDocs().length === 0) { + acc[nodeType].issues.push( + getUndocumentedNode( + filePath, + nodeType, + node.getName() || '', + node.getStartLineNumber(), + ), + ); + } + return acc; + }, + createEmptyUnprocessedCoverageReport(), + ); + return mergeCoverageResults( + coverageReportOfAllFiles, + coverageReportOfCurrentFile, + ); + }, + createEmptyUnprocessedCoverageReport(), + ); + return calculateCoverage2(unprocessedCoverageReport); +} +function mergeCoverageResults(results, current) { + return { + ...Object.fromEntries( + Object.entries(results).map(([key, value]) => { + const node = value; + const type = key; + return [ + type, + { + nodesCount: node.nodesCount + current[type].nodesCount, + issues: [...node.issues, ...current[type].issues], + }, + ]; + }), + ), + }; +} +function getClassNodes(classNodes) { + return classNodes.flatMap(classNode => [ + ...classNode.getMethods(), + ...classNode.getProperties(), + ]); +} +function getUndocumentedNode(file, type, name, line) { + return { file, type, name, line }; +} + +// packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts +var PLUGIN_TITLE = 'Documentation coverage'; +var PLUGIN_DESCRIPTION = 'Official Code PushUp documentation coverage plugin.'; +var PLUGIN_DOCS_URL = + 'https://www.npmjs.com/package/@code-pushup/doc-coverage-plugin/'; +async function docCoveragePlugin(config2) { + const docCoverageConfig = docCoveragePluginConfigSchema.parse(config2); + const groupsC = filterGroupsByOnlyAudits(groups, docCoverageConfig); + const auditsC = filterAuditsByPluginConfig(docCoverageConfig); + return { + slug: PLUGIN_SLUG, + title: PLUGIN_TITLE, + icon: 'folder-src', + description: PLUGIN_DESCRIPTION, + docsUrl: PLUGIN_DOCS_URL, + groups: filterGroupsByOnlyAudits(groups, docCoverageConfig), + audits: filterAuditsByPluginConfig(docCoverageConfig), + runner: createRunnerFunction(docCoverageConfig), + }; +} +function createRunnerFunction(config2) { + return () => { + const coverageResult = processDocCoverage(config2); + return trasformCoverageReportToAudits(coverageResult, config2); + }; +} + +// packages/plugin-doc-coverage/src/index.ts +var src_default = docCoveragePlugin; + +var patternsSchema = z3.union([z3.string(), z3.array(z3.string()).min(1)], { + description: + 'Lint target files. May contain file paths, directory paths or glob patterns', +}); +var eslintrcSchema = z3.string({ description: 'Path to ESLint config file' }); +var eslintTargetObjectSchema = z3.object({ + eslintrc: eslintrcSchema.optional(), + patterns: patternsSchema, +}); +var eslintTargetSchema = z3 + .union([patternsSchema, eslintTargetObjectSchema]) + .transform(target => + typeof target === 'string' || Array.isArray(target) + ? { patterns: target } + : target, + ); +var eslintPluginConfigSchema = z3 + .union([eslintTargetSchema, z3.array(eslintTargetSchema).min(1)]) + .transform(toArray); + +// packages/plugin-eslint/src/lib/runner/index.ts +var WORKDIR2 = pluginWorkDir2('eslint'); +var RUNNER_OUTPUT_PATH2 = path7.join(WORKDIR2, 'runner-output.json'); +var PLUGIN_CONFIG_PATH2 = path7.join( + process.cwd(), + WORKDIR2, + 'plugin-config.json', +); + +// packages/plugin-js-packages/src/lib/constants.ts +var defaultAuditLevelMapping = { + critical: 'error', + high: 'error', + moderate: 'warning', + low: 'warning', + info: 'info', +}; + +// packages/plugin-js-packages/src/lib/config.ts +var dependencyGroups = ['prod', 'dev', 'optional']; +var dependencyGroupSchema = z4.enum(dependencyGroups); +var packageCommandSchema = z4.enum(['audit', 'outdated']); +var packageManagerIdSchema = z4.enum([ + 'npm', + 'yarn-classic', + 'yarn-modern', + 'pnpm', +]); +var packageJsonPathSchema = z4 + .union([ + z4.array(z4.string()).min(1), + z4.object({ autoSearch: z4.literal(true) }), + ]) + .describe( + 'File paths to package.json. Looks only at root package.json by default', + ) + .default(['package.json']); +var packageAuditLevels = ['critical', 'high', 'moderate', 'low', 'info']; +var packageAuditLevelSchema = z4.enum(packageAuditLevels); +function fillAuditLevelMapping(mapping) { + return { + critical: mapping.critical ?? defaultAuditLevelMapping.critical, + high: mapping.high ?? defaultAuditLevelMapping.high, + moderate: mapping.moderate ?? defaultAuditLevelMapping.moderate, + low: mapping.low ?? defaultAuditLevelMapping.low, + info: mapping.info ?? defaultAuditLevelMapping.info, + }; +} +var jsPackagesPluginConfigSchema = z4.object({ + checks: z4 + .array(packageCommandSchema, { + description: + 'Package manager commands to be run. Defaults to both audit and outdated.', + }) + .min(1) + .default(['audit', 'outdated']), + packageManager: packageManagerIdSchema + .describe('Package manager to be used.') + .optional(), + dependencyGroups: z4 + .array(dependencyGroupSchema) + .min(1) + .default(['prod', 'dev']), + auditLevelMapping: z4 + .record(packageAuditLevelSchema, issueSeveritySchema, { + description: + 'Mapping of audit levels to issue severity. Custom mapping or overrides may be entered manually, otherwise has a default preset.', + }) + .default(defaultAuditLevelMapping) + .transform(fillAuditLevelMapping), + packageJsonPaths: packageJsonPathSchema, +}); + +function filterAuditResult(result, key, referenceResult) { + if (result.vulnerabilities.length === 0) { + return result; + } + const uniqueResult = result.vulnerabilities.reduce( + (acc, ref) => { + const matchReference = referenceResult ?? acc; + const isMatch = matchReference.vulnerabilities + .map(vulnerability => vulnerability[key]) + .includes(ref[key]); + if (isMatch) { + return { + vulnerabilities: acc.vulnerabilities, + summary: { + ...acc.summary, + [ref.severity]: acc.summary[ref.severity] - 1, + total: acc.summary.total - 1, + }, + }; + } + return { + vulnerabilities: [...acc.vulnerabilities, ref], + summary: acc.summary, + }; + }, + { vulnerabilities: [], summary: result.summary }, + ); + return { + vulnerabilities: uniqueResult.vulnerabilities, + summary: uniqueResult.summary, + }; +} + +// packages/plugin-js-packages/src/lib/package-managers/constants.ts +var COMMON_AUDIT_ARGS = ['audit', '--json']; +var COMMON_OUTDATED_ARGS = ['outdated', '--json']; + +function npmToAuditResult(output) { + const npmAudit = JSON.parse(output); + const vulnerabilities = objectToEntries2(npmAudit.vulnerabilities).map( + ([name, detail]) => { + const advisory = npmToAdvisory(name, npmAudit.vulnerabilities); + return { + name: name.toString(), + severity: detail.severity, + versionRange: detail.range, + directDependency: detail.isDirect ? true : (detail.effects[0] ?? ''), + fixInformation: npmToFixInformation(detail.fixAvailable), + ...(advisory != null && { + title: advisory.title, + url: advisory.url, + }), + }; + }, + ); + return { + vulnerabilities, + summary: npmAudit.metadata.vulnerabilities, + }; +} +function npmToFixInformation(fixAvailable) { + if (typeof fixAvailable === 'boolean') { + return fixAvailable ? 'Fix is available.' : ''; + } + return `Fix available: Update \`${fixAvailable.name}\` to version **${fixAvailable.version}**${fixAvailable.isSemVerMajor ? ' (breaking change).' : '.'}`; +} +function npmToAdvisory( + name, + vulnerabilities, + prevNodes = /* @__PURE__ */ new Set(), +) { + const advisory = vulnerabilities[name]?.via; + if ( + Array.isArray(advisory) && + advisory.length > 0 && + typeof advisory[0] === 'object' + ) { + return { title: advisory[0].title, url: advisory[0].url }; + } + if ( + Array.isArray(advisory) && + advisory.length > 0 && + advisory.every(value => typeof value === 'string') + ) { + let advisoryInfo = null; + let newReferences = []; + let advisoryInfoFound = false; + for (const via of advisory) { + if (!prevNodes.has(via)) { + newReferences.push(via); + } + } + while (newReferences.length > 0 && !advisoryInfoFound) { + const ref = newReferences.pop(); + prevNodes.add(ref); + const result = npmToAdvisory(ref, vulnerabilities, prevNodes); + if (result != null) { + advisoryInfo = { title: result.title, url: result.url }; + advisoryInfoFound = true; + } + } + return advisoryInfo; + } + return null; +} + +function npmToOutdatedResult(output) { + const npmOutdated = JSON.parse(output); + return objectToEntries3(npmOutdated) + .filter(entry => entry[1].current != null) + .map(([name, overview]) => ({ + name, + current: overview.current, + latest: overview.latest, + type: overview.type, + ...(overview.homepage != null && { url: overview.homepage }), + })); +} + +// packages/plugin-js-packages/src/lib/package-managers/npm/npm.ts +var npmDependencyOptions = { + prod: ['--omit=dev', '--omit=optional'], + dev: ['--include=dev', '--omit=optional'], + optional: ['--include=optional', '--omit=dev'], +}; +var npmPackageManager = { + slug: 'npm', + name: 'NPM', + command: 'npm', + icon: 'npm', + docs: { + homepage: 'https://docs.npmjs.com/', + audit: 'https://docs.npmjs.com/cli/commands/npm-audit', + outdated: 'https://docs.npmjs.com/cli/commands/npm-outdated', + }, + audit: { + getCommandArgs: groupDep => [ + ...COMMON_AUDIT_ARGS, + ...npmDependencyOptions[groupDep], + '--audit-level=none', + ], + unifyResult: npmToAuditResult, + // prod dependencies need to be filtered out manually since v10 + postProcessResult: results => { + const depGroups = objectToKeys3(results); + const devFilter = + results.dev && results.prod + ? filterAuditResult(results.dev, 'name', results.prod) + : results.dev; + const optionalFilter = + results.optional && results.prod + ? filterAuditResult(results.optional, 'name', results.prod) + : results.optional; + return { + ...(depGroups.includes('prod') && { prod: results.prod }), + ...(depGroups.includes('dev') && { dev: devFilter }), + ...(depGroups.includes('optional') && { optional: optionalFilter }), + }; + }, + }, + outdated: { + commandArgs: [...COMMON_OUTDATED_ARGS, '--long'], + unifyResult: npmToOutdatedResult, + }, +}; + +var WORKDIR3 = pluginWorkDir3('js-packages'); +var RUNNER_OUTPUT_PATH3 = path11.join(WORKDIR3, 'runner-output.json'); +var PLUGIN_CONFIG_PATH3 = path11.join( + process.cwd(), + WORKDIR3, + 'plugin-config.json', +); + +var outdatedSeverity = { + major: 'error', + premajor: 'info', + minor: 'warning', + preminor: 'info', + patch: 'info', + prepatch: 'info', + prerelease: 'info', +}; +var RELEASE_TYPES = objectToKeys6(outdatedSeverity); + +var DEFAULT_CHROME_FLAGS = [...DEFAULT_FLAGS, '--headless']; +var LIGHTHOUSE_PLUGIN_SLUG = 'lighthouse'; +var LIGHTHOUSE_OUTPUT_PATH = path15.join( + DEFAULT_PERSIST_OUTPUT_DIR, + LIGHTHOUSE_PLUGIN_SLUG, +); + +var { audits, categories } = defaultConfig; +var allRawLighthouseAudits = await Promise.all( + (audits ?? []).map(loadLighthouseAudit), +); +var LIGHTHOUSE_NAVIGATION_AUDITS = allRawLighthouseAudits + .filter( + audit => + audit.meta.supportedModes == null || + (Array.isArray(audit.meta.supportedModes) && + audit.meta.supportedModes.includes('navigation')), + ) + .map(audit => ({ + slug: audit.meta.id, + title: getMetaString(audit.meta.title), + description: getMetaString(audit.meta.description), + })); +var navigationAuditSlugs = new Set( + LIGHTHOUSE_NAVIGATION_AUDITS.map(({ slug }) => slug), +); +var LIGHTHOUSE_GROUPS = Object.entries(categories ?? {}).map( + ([id, category]) => ({ + slug: id, + title: getMetaString(category.title), + ...(category.description && { + description: getMetaString(category.description), + }), + refs: category.auditRefs + .filter(({ id: auditSlug }) => navigationAuditSlugs.has(auditSlug)) + .map(ref => ({ + slug: ref.id, + weight: ref.weight, + })), + }), +); +function getMetaString(value) { + if (typeof value === 'string') { + return value; + } + return value.formattedDefault; +} +async function loadLighthouseAudit(value) { + if (typeof value === 'object' && 'implementation' in value) { + return value.implementation; + } + if (typeof value === 'function') { + return value; + } + const file = typeof value === 'string' ? value : value.path; + const module = await import(`lighthouse/core/audits/${file}.js`); + return module.default; +} +var LIGHTHOUSE_REPORT_NAME = 'lighthouse-report.json'; +var DEFAULT_CLI_FLAGS = { + // default values extracted from + // https://github.com/GoogleChrome/lighthouse/blob/7d80178c37a1b600ea8f092fc0b098029799a659/cli/cli-flags.js#L80 + verbose: false, + saveAssets: false, + chromeFlags: DEFAULT_CHROME_FLAGS, + port: 0, + hostname: '127.0.0.1', + view: false, + channel: 'cli', + // custom overwrites in favour of the plugin + // hide logs by default + quiet: true, + onlyAudits: [], + skipAudits: [], + onlyCategories: [], + output: ['json'], + outputPath: path16.join(LIGHTHOUSE_OUTPUT_PATH, LIGHTHOUSE_REPORT_NAME), +}; + +// packages/plugin-lighthouse/src/lib/normalize-flags.ts +var { onlyCategories, ...originalDefaultCliFlags } = DEFAULT_CLI_FLAGS; +var DEFAULT_LIGHTHOUSE_OPTIONS = { + ...originalDefaultCliFlags, + onlyGroups: onlyCategories, +}; +var lighthouseUnsupportedCliFlags = [ + 'precomputedLanternDataPath', + // Path to the file where precomputed lantern data should be read from. + 'chromeIgnoreDefaultFlags', + // ignore default flags from Lighthouse CLI + // No error reporting implemented as in the source Sentry was involved + // See: https://github.com/GoogleChrome/lighthouse/blob/d8ccf70692216b7fa047a4eaa2d1277b0b7fe947/cli/bin.js#L124 + 'enableErrorReporting', + // enable error reporting + // lighthouse CLI specific debug logs + 'list-all-audits', + // Prints a list of all available audits and exits. + 'list-locales', + // Prints a list of all supported locales and exits. + 'list-trace-categories', + // Prints a list of all required trace categories and exits. +]; +var LIGHTHOUSE_UNSUPPORTED_CLI_FLAGS = new Set(lighthouseUnsupportedCliFlags); + +function lighthouseGroupRef(groupSlug, weight = 1) { + return { + plugin: LIGHTHOUSE_PLUGIN_SLUG, + slug: groupSlug, + type: 'group', + weight, + }; +} + +// code-pushup.preset.ts +var lighthouseCategories = [ + { + slug: 'performance', + title: 'Performance', + refs: [lighthouseGroupRef('performance')], + }, + { + slug: 'a11y', + title: 'Accessibility', + refs: [lighthouseGroupRef('accessibility')], + }, + { + slug: 'best-practices', + title: 'Best Practices', + refs: [lighthouseGroupRef('best-practices')], + }, + { + slug: 'seo', + title: 'SEO', + refs: [lighthouseGroupRef('seo')], + }, +]; +function getDocCoverageCategories(config2) { + return [ + { + slug: 'doc-coverage-cat', + title: 'Documentation coverage', + description: 'Measures how much of your code is **documented**.', + refs: filterGroupsByOnlyAudits(groups, config2).map(group => ({ + weight: 1, + type: 'group', + plugin: PLUGIN_SLUG, + slug: group.slug, + })), + }, + ]; +} +var docCoverageCoreConfig = async config2 => { + return { + plugins: [await src_default(config2)], + categories: getDocCoverageCategories(config2), + }; +}; + +// packages/utils/src/lib/merge-configs.ts +function mergeConfigs(config2, ...configs) { + return configs.reduce( + (acc, obj) => ({ + ...acc, + ...mergeCategories(acc.categories, obj.categories), + ...mergePlugins(acc.plugins, obj.plugins), + ...mergePersist(acc.persist, obj.persist), + ...mergeUpload(acc.upload, obj.upload), + }), + config2, + ); +} +function mergeCategories(a, b) { + if (!a && !b) { + return {}; + } + const mergedMap = /* @__PURE__ */ new Map(); + const addToMap = categories2 => { + categories2.forEach(newObject => { + if (mergedMap.has(newObject.slug)) { + const existingObject = mergedMap.get(newObject.slug); + mergedMap.set(newObject.slug, { + ...existingObject, + ...newObject, + refs: mergeByUniqueCategoryRefCombination( + existingObject?.refs, + newObject.refs, + ), + }); + } else { + mergedMap.set(newObject.slug, newObject); + } + }); + }; + if (a) { + addToMap(a); + } + if (b) { + addToMap(b); + } + return { categories: [...mergedMap.values()] }; +} +function mergePlugins(a, b) { + if (!a && !b) { + return { plugins: [] }; + } + const mergedMap = /* @__PURE__ */ new Map(); + const addToMap = plugins => { + plugins.forEach(newObject => { + mergedMap.set(newObject.slug, newObject); + }); + }; + if (a) { + addToMap(a); + } + if (b) { + addToMap(b); + } + return { plugins: [...mergedMap.values()] }; +} +function mergePersist(a, b) { + if (!a && !b) { + return {}; + } + if (a) { + return b ? { persist: { ...a, ...b } } : {}; + } else { + return { persist: b }; + } +} +function mergeByUniqueCategoryRefCombination(a, b) { + const map = /* @__PURE__ */ new Map(); + const addToMap = refs => { + refs.forEach(ref => { + const uniqueIdentification = `${ref.type}:${ref.plugin}:${ref.slug}`; + if (map.has(uniqueIdentification)) { + map.set(uniqueIdentification, { + ...map.get(uniqueIdentification), + ...ref, + }); + } else { + map.set(uniqueIdentification, ref); + } + }); + }; + if (a) { + addToMap(a); + } + if (b) { + addToMap(b); + } + return [...map.values()]; +} +function mergeUpload(a, b) { + if (!a && !b) { + return {}; + } + if (a) { + return b ? { upload: { ...a, ...b } } : {}; + } else { + return { upload: b }; + } +} + +// code-pushup.config.ts +var envSchema = z5.object({ + CP_SERVER: z5.string().url(), + CP_API_KEY: z5.string().min(1), + CP_ORGANIZATION: z5.string().min(1), + CP_PROJECT: z5.string().min(1), +}); +var { data: env } = await envSchema.safeParseAsync(process.env); +var config = { + ...(env && { + upload: { + server: env.CP_SERVER, + apiKey: env.CP_API_KEY, + organization: env.CP_ORGANIZATION, + project: env.CP_PROJECT, + }, + }), + plugins: [], +}; +var code_pushup_config_default = mergeConfigs( + config, + // await coverageCoreConfigNx(), + // await jsPackagesCoreConfig(), + // await lighthouseCoreConfig( + // 'https://github.com/code-pushup/cli?tab=readme-ov-file#code-pushup-cli/', + // ), + // await eslintCoreConfigNx(), + await docCoverageCoreConfig({ + sourceGlob: ['packages/**/*.ts', '!**/*.spec.ts', '!**/*.test.ts'], + onlyAudits: ['methods-coverage', 'functions-coverage'], + }), +); +export { code_pushup_config_default as default }; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiY29kZS1wdXNodXAuY29uZmlnLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1jb3ZlcmFnZS9zcmMvbGliL2NvdmVyYWdlLXBsdWdpbi50cyIsICJwYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9jb25maWcudHMiLCAicGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvcnVubmVyL2luZGV4LnRzIiwgInBhY2thZ2VzL3BsdWdpbi1jb3ZlcmFnZS9zcmMvbGliL3J1bm5lci9jb25zdGFudHMudHMiLCAicGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvcnVubmVyL2xjb3YvbGNvdi1ydW5uZXIudHMiLCAicGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvcnVubmVyL2xjb3YvcGFyc2UtbGNvdi50cyIsICJwYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXIvbGNvdi90cmFuc2Zvcm0udHMiLCAicGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvbngvY292ZXJhZ2UtcGF0aHMudHMiLCAicGFja2FnZXMvcGx1Z2luLWRvYy1jb3ZlcmFnZS9zcmMvbGliL2NvbmZpZy50cyIsICJwYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWIvY29uc3RhbnRzLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1kb2MtY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXIvZG9jLXByb2Nlc3Nlci50cyIsICJwYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWIvdXRpbHMudHMiLCAicGFja2FnZXMvcGx1Z2luLWRvYy1jb3ZlcmFnZS9zcmMvbGliL3J1bm5lci91dGlscy50cyIsICJwYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWIvZG9jLWNvdmVyYWdlLXBsdWdpbi50cyIsICJwYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9pbmRleC50cyIsICJwYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvZXNsaW50LXBsdWdpbi50cyIsICJwYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvY29uZmlnLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhL2dyb3Vwcy50cyIsICJwYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvbWV0YS9oYXNoLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhL3BhcnNlLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhL3ZlcnNpb25zL2ZsYXQudHMiLCAicGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGEvdmVyc2lvbnMvbGVnYWN5LnRzIiwgInBhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9zZXR1cC50cyIsICJwYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvbWV0YS92ZXJzaW9ucy9kZXRlY3QudHMiLCAicGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGEvdHJhbnNmb3JtLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9ydW5uZXIvaW5kZXgudHMiLCAicGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL3J1bm5lci9saW50LnRzIiwgInBhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9ydW5uZXIvdHJhbnNmb3JtLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9ueC91dGlscy50cyIsICJwYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9qcy1wYWNrYWdlcy1wbHVnaW4udHMiLCAicGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvY29uZmlnLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL2NvbnN0YW50cy50cyIsICJwYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL25wbS9ucG0udHMiLCAicGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcnVubmVyL3V0aWxzLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnMvY29uc3RhbnRzLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnMvbnBtL2F1ZGl0LXJlc3VsdC50cyIsICJwYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL25wbS9vdXRkYXRlZC1yZXN1bHQudHMiLCAicGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy9wbnBtL3BucG0udHMiLCAicGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy9wbnBtL291dGRhdGVkLXJlc3VsdC50cyIsICJwYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL3lhcm4tY2xhc3NpYy9hdWRpdC1yZXN1bHQudHMiLCAicGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy95YXJuLWNsYXNzaWMvb3V0ZGF0ZWQtcmVzdWx0LnRzIiwgInBhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3J1bm5lci9pbmRleC50cyIsICJwYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9ydW5uZXIvYXVkaXQvdHJhbnNmb3JtLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3J1bm5lci9jb25zdGFudHMudHMiLCAicGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcnVubmVyL291dGRhdGVkL3RyYW5zZm9ybS50cyIsICJwYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9ydW5uZXIvb3V0ZGF0ZWQvY29uc3RhbnRzLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnMvZGVyaXZlLXBhY2thZ2UtbWFuYWdlci50cyIsICJwYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL2Rlcml2ZS15YXJuLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvbGlnaHRob3VzZS1wbHVnaW4udHMiLCAicGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9jb25zdGFudHMudHMiLCAicGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9ub3JtYWxpemUtZmxhZ3MudHMiLCAicGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9ydW5uZXIvY29uc3RhbnRzLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL3J1bm5lci50cyIsICJwYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lci91dGlscy50cyIsICJwYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lci9kZXRhaWxzL2RldGFpbHMudHMiLCAicGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9ydW5uZXIvZGV0YWlscy9vcHBvcnR1bml0eS50eXBlLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL2RldGFpbHMvdGFibGUudHlwZS50cyIsICJwYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lci9kZXRhaWxzL2l0ZW0tdmFsdWUudHMiLCAicGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9ydW5uZXIvZGV0YWlscy91dGlscy50cyIsICJwYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3V0aWxzLnRzIiwgImNvZGUtcHVzaHVwLnByZXNldC50cyIsICJwYWNrYWdlcy91dGlscy9zcmMvaW5kZXgudHMiLCAicGFja2FnZXMvdXRpbHMvc3JjL2xpYi9leGVjdXRlLXByb2Nlc3MudHMiLCAicGFja2FnZXMvdXRpbHMvc3JjL2xpYi9yZXBvcnRzL3V0aWxzLnRzIiwgInBhY2thZ2VzL3V0aWxzL3NyYy9saWIvZmlsZS1zeXN0ZW0udHMiLCAicGFja2FnZXMvdXRpbHMvc3JjL2xpYi9mb3JtYXR0aW5nLnRzIiwgInBhY2thZ2VzL3V0aWxzL3NyYy9saWIvbG9nZ2luZy50cyIsICJwYWNrYWdlcy91dGlscy9zcmMvbGliL2dpdC9naXQuY29tbWl0cy1hbmQtdGFncy50cyIsICJwYWNrYWdlcy91dGlscy9zcmMvbGliL3NlbXZlci50cyIsICJwYWNrYWdlcy91dGlscy9zcmMvbGliL2dpdC9naXQudHMiLCAicGFja2FnZXMvdXRpbHMvc3JjL2xpYi90cmFuc2Zvcm0udHMiLCAicGFja2FnZXMvdXRpbHMvc3JjL2xpYi9tZXJnZS1jb25maWdzLnRzIiwgInBhY2thZ2VzL3V0aWxzL3NyYy9saWIvcHJvZ3Jlc3MudHMiLCAicGFja2FnZXMvdXRpbHMvc3JjL2xpYi9yZXBvcnRzL2dlbmVyYXRlLW1kLXJlcG9ydC50cyIsICJwYWNrYWdlcy91dGlscy9zcmMvbGliL3JlcG9ydHMvZm9ybWF0dGluZy50cyIsICJwYWNrYWdlcy91dGlscy9zcmMvbGliL3JlcG9ydHMvZ2VuZXJhdGUtbWQtcmVwb3J0LWNhdGVnb3ktc2VjdGlvbi50cyIsICJwYWNrYWdlcy91dGlscy9zcmMvbGliL3JlcG9ydHMvZ2VuZXJhdGUtbWQtcmVwb3J0cy1kaWZmLnRzIiwgInBhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0cy9nZW5lcmF0ZS1tZC1yZXBvcnRzLWRpZmYtdXRpbHMudHMiLCAicGFja2FnZXMvdXRpbHMvc3JjL2xpYi9yZXBvcnRzL2xvYWQtcmVwb3J0LnRzIiwgInBhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0cy9sb2ctc3Rkb3V0LXN1bW1hcnkudHMiLCAicGFja2FnZXMvdXRpbHMvc3JjL2xpYi96b2QtdmFsaWRhdGlvbi50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9jb2RlLXB1c2h1cC5jb25maWcudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGlcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvY29kZS1wdXNodXAuY29uZmlnLnRzXCI7aW1wb3J0ICdkb3RlbnYvY29uZmlnJztcbmltcG9ydCB7IHogfSBmcm9tICd6b2QnO1xuaW1wb3J0IHsgZG9jQ292ZXJhZ2VDb3JlQ29uZmlnIH0gZnJvbSAnLi9jb2RlLXB1c2h1cC5wcmVzZXQuanMnO1xuaW1wb3J0IHR5cGUgeyBDb3JlQ29uZmlnIH0gZnJvbSAnLi9wYWNrYWdlcy9tb2RlbHMvc3JjL2luZGV4LmpzJztcbmltcG9ydCB7IG1lcmdlQ29uZmlncyB9IGZyb20gJy4vcGFja2FnZXMvdXRpbHMvc3JjL2luZGV4LmpzJztcbi8vIGxvYWQgdXBsb2FkIGNvbmZpZ3VyYXRpb24gZnJvbSBlbnZpcm9ubWVudFxuY29uc3QgZW52U2NoZW1hID0gei5vYmplY3Qoe1xuICBDUF9TRVJWRVI6IHouc3RyaW5nKCkudXJsKCksXG4gIENQX0FQSV9LRVk6IHouc3RyaW5nKCkubWluKDEpLFxuICBDUF9PUkdBTklaQVRJT046IHouc3RyaW5nKCkubWluKDEpLFxuICBDUF9QUk9KRUNUOiB6LnN0cmluZygpLm1pbigxKSxcbn0pO1xuY29uc3QgeyBkYXRhOiBlbnYgfSA9IGF3YWl0IGVudlNjaGVtYS5zYWZlUGFyc2VBc3luYyhwcm9jZXNzLmVudik7XG5cbmNvbnN0IGNvbmZpZzogQ29yZUNvbmZpZyA9IHtcbiAgLi4uKGVudiAmJiB7XG4gICAgdXBsb2FkOiB7XG4gICAgICBzZXJ2ZXI6IGVudi5DUF9TRVJWRVIsXG4gICAgICBhcGlLZXk6IGVudi5DUF9BUElfS0VZLFxuICAgICAgb3JnYW5pemF0aW9uOiBlbnYuQ1BfT1JHQU5JWkFUSU9OLFxuICAgICAgcHJvamVjdDogZW52LkNQX1BST0pFQ1QsXG4gICAgfSxcbiAgfSksXG5cbiAgcGx1Z2luczogW10sXG59O1xuXG5leHBvcnQgZGVmYXVsdCBtZXJnZUNvbmZpZ3MoXG4gIGNvbmZpZyxcbiAgLy8gYXdhaXQgY292ZXJhZ2VDb3JlQ29uZmlnTngoKSxcbiAgLy8gYXdhaXQganNQYWNrYWdlc0NvcmVDb25maWcoKSxcbiAgLy8gYXdhaXQgbGlnaHRob3VzZUNvcmVDb25maWcoXG4gIC8vICAgJ2h0dHBzOi8vZ2l0aHViLmNvbS9jb2RlLXB1c2h1cC9jbGk/dGFiPXJlYWRtZS1vdi1maWxlI2NvZGUtcHVzaHVwLWNsaS8nLFxuICAvLyApLFxuICAvLyBhd2FpdCBlc2xpbnRDb3JlQ29uZmlnTngoKSxcbiAgYXdhaXQgZG9jQ292ZXJhZ2VDb3JlQ29uZmlnKHtcbiAgICBzb3VyY2VHbG9iOiBbJ3BhY2thZ2VzLyoqLyoudHMnLCAnISoqLyouc3BlYy50cycsICchKiovKi50ZXN0LnRzJ10sXG4gICAgb25seUF1ZGl0czogWydtZXRob2RzLWNvdmVyYWdlJywgJ2Z1bmN0aW9ucy1jb3ZlcmFnZSddXG4gIH0pLFxuKTtcbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1jb3ZlcmFnZS9zcmMvbGliL2NvdmVyYWdlLXBsdWdpbi50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYlwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9jb3ZlcmFnZS1wbHVnaW4udHNcIjtpbXBvcnQgeyBjcmVhdGVSZXF1aXJlIH0gZnJvbSAnbm9kZTptb2R1bGUnO1xuaW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB7IGZpbGVVUkxUb1BhdGggfSBmcm9tICdub2RlOnVybCc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0LCBHcm91cCwgUGx1Z2luQ29uZmlnIH0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5pbXBvcnQgeyBjYXBpdGFsaXplIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB7XG4gIHR5cGUgQ292ZXJhZ2VQbHVnaW5Db25maWcsXG4gIHR5cGUgQ292ZXJhZ2VUeXBlLFxuICBjb3ZlcmFnZVBsdWdpbkNvbmZpZ1NjaGVtYSxcbn0gZnJvbSAnLi9jb25maWcuanMnO1xuaW1wb3J0IHsgY3JlYXRlUnVubmVyQ29uZmlnIH0gZnJvbSAnLi9ydW5uZXIvaW5kZXguanMnO1xuaW1wb3J0IHsgY292ZXJhZ2VEZXNjcmlwdGlvbiwgY292ZXJhZ2VUeXBlV2VpZ2h0TWFwcGVyIH0gZnJvbSAnLi91dGlscy5qcyc7XG5cbi8qKlxuICogSW5zdGFudGlhdGVzIENvZGUgUHVzaFVwIGNvZGUgY292ZXJhZ2UgcGx1Z2luIGZvciBjb3JlIGNvbmZpZy5cbiAqXG4gKiBAZXhhbXBsZVxuICogaW1wb3J0IGNvdmVyYWdlUGx1Z2luIGZyb20gJ0Bjb2RlLXB1c2h1cC9jb3ZlcmFnZS1wbHVnaW4nXG4gKlxuICogZXhwb3J0IGRlZmF1bHQge1xuICogICAvLyAuLi4gY29yZSBjb25maWcgLi4uXG4gKiAgIHBsdWdpbnM6IFtcbiAqICAgICAvLyAuLi4gb3RoZXIgcGx1Z2lucyAuLi5cbiAqICAgICBhd2FpdCBjb3ZlcmFnZVBsdWdpbih7XG4gKiAgICAgICByZXBvcnRzOiBbeyByZXN1bHRzUGF0aDogJ2NvdmVyYWdlL2NsaS9sY292LmluZm8nLCBwYXRoVG9Qcm9qZWN0OiAncGFja2FnZXMvY2xpJyB9XVxuICogICAgIH0pXG4gKiAgIF1cbiAqIH1cbiAqXG4gKiBAcmV0dXJucyBQbHVnaW4gY29uZmlndXJhdGlvbi5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNvdmVyYWdlUGx1Z2luKFxuICBjb25maWc6IENvdmVyYWdlUGx1Z2luQ29uZmlnLFxuKTogUHJvbWlzZTxQbHVnaW5Db25maWc+IHtcbiAgY29uc3QgY292ZXJhZ2VDb25maWcgPSBjb3ZlcmFnZVBsdWdpbkNvbmZpZ1NjaGVtYS5wYXJzZShjb25maWcpO1xuXG4gIGNvbnN0IGF1ZGl0cyA9IGNvdmVyYWdlQ29uZmlnLmNvdmVyYWdlVHlwZXMubWFwKFxuICAgICh0eXBlKTogQXVkaXQgPT4gKHtcbiAgICAgIHNsdWc6IGAke3R5cGV9LWNvdmVyYWdlYCxcbiAgICAgIHRpdGxlOiBgJHtjYXBpdGFsaXplKHR5cGUpfSBjb3ZlcmFnZWAsXG4gICAgICBkZXNjcmlwdGlvbjogY292ZXJhZ2VEZXNjcmlwdGlvblt0eXBlXSxcbiAgICB9KSxcbiAgKTtcblxuICBjb25zdCBncm91cDogR3JvdXAgPSB7XG4gICAgc2x1ZzogJ2NvdmVyYWdlJyxcbiAgICB0aXRsZTogJ0NvZGUgY292ZXJhZ2UgbWV0cmljcycsXG4gICAgZGVzY3JpcHRpb246ICdHcm91cCBjb250YWluaW5nIGFsbCBkZWZpbmVkIGNvdmVyYWdlIHR5cGVzIGFzIGF1ZGl0cy4nLFxuICAgIHJlZnM6IGF1ZGl0cy5tYXAoYXVkaXQgPT4gKHtcbiAgICAgIC4uLmF1ZGl0LFxuICAgICAgd2VpZ2h0OlxuICAgICAgICBjb3ZlcmFnZVR5cGVXZWlnaHRNYXBwZXJbXG4gICAgICAgICAgYXVkaXQuc2x1Zy5zbGljZSgwLCBhdWRpdC5zbHVnLmluZGV4T2YoJy0nKSkgYXMgQ292ZXJhZ2VUeXBlXG4gICAgICAgIF0sXG4gICAgfSkpLFxuICB9O1xuXG4gIGNvbnN0IHJ1bm5lclNjcmlwdFBhdGggPSBwYXRoLmpvaW4oXG4gICAgZmlsZVVSTFRvUGF0aChwYXRoLmRpcm5hbWUoaW1wb3J0Lm1ldGEudXJsKSksXG4gICAgJy4uJyxcbiAgICAnYmluLmpzJyxcbiAgKTtcblxuICBjb25zdCBwYWNrYWdlSnNvbiA9IGNyZWF0ZVJlcXVpcmUoaW1wb3J0Lm1ldGEudXJsKShcbiAgICAnLi4vLi4vcGFja2FnZS5qc29uJyxcbiAgKSBhcyB0eXBlb2YgaW1wb3J0KCcuLi8uLi9wYWNrYWdlLmpzb24nKTtcblxuICByZXR1cm4ge1xuICAgIHNsdWc6ICdjb3ZlcmFnZScsXG4gICAgdGl0bGU6ICdDb2RlIGNvdmVyYWdlJyxcbiAgICBpY29uOiAnZm9sZGVyLWNvdmVyYWdlLW9wZW4nLFxuICAgIGRlc2NyaXB0aW9uOiAnT2ZmaWNpYWwgQ29kZSBQdXNoVXAgY29kZSBjb3ZlcmFnZSBwbHVnaW4uJyxcbiAgICBkb2NzVXJsOiAnaHR0cHM6Ly93d3cubnBtanMuY29tL3BhY2thZ2UvQGNvZGUtcHVzaHVwL2NvdmVyYWdlLXBsdWdpbi8nLFxuICAgIHBhY2thZ2VOYW1lOiBwYWNrYWdlSnNvbi5uYW1lLFxuICAgIHZlcnNpb246IHBhY2thZ2VKc29uLnZlcnNpb24sXG4gICAgYXVkaXRzLFxuICAgIGdyb3VwczogW2dyb3VwXSxcbiAgICBydW5uZXI6IGF3YWl0IGNyZWF0ZVJ1bm5lckNvbmZpZyhydW5uZXJTY3JpcHRQYXRoLCBjb3ZlcmFnZUNvbmZpZyksXG4gIH07XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9jb25maWcudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvY29uZmlnLnRzXCI7aW1wb3J0IHsgeiB9IGZyb20gJ3pvZCc7XG5cbmV4cG9ydCBjb25zdCBjb3ZlcmFnZVR5cGVTY2hlbWEgPSB6LmVudW0oWydmdW5jdGlvbicsICdicmFuY2gnLCAnbGluZSddKTtcbmV4cG9ydCB0eXBlIENvdmVyYWdlVHlwZSA9IHouaW5mZXI8dHlwZW9mIGNvdmVyYWdlVHlwZVNjaGVtYT47XG5cbmV4cG9ydCBjb25zdCBjb3ZlcmFnZVJlc3VsdFNjaGVtYSA9IHoudW5pb24oW1xuICB6Lm9iamVjdCh7XG4gICAgcmVzdWx0c1BhdGg6IHpcbiAgICAgIC5zdHJpbmcoe1xuICAgICAgICBkZXNjcmlwdGlvbjogJ1BhdGggdG8gY292ZXJhZ2UgcmVzdWx0cyBmb3IgTnggc2V0dXAuJyxcbiAgICAgIH0pXG4gICAgICAuaW5jbHVkZXMoJ2xjb3YnKSxcbiAgICBwYXRoVG9Qcm9qZWN0OiB6XG4gICAgICAuc3RyaW5nKHtcbiAgICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICAgJ1BhdGggZnJvbSB3b3Jrc3BhY2Ugcm9vdCB0byBwcm9qZWN0IHJvb3QuIE5lY2Vzc2FyeSBmb3IgTENPViByZXBvcnRzIHdoaWNoIHByb3ZpZGUgYSByZWxhdGl2ZSBwYXRoLicsXG4gICAgICB9KVxuICAgICAgLm9wdGlvbmFsKCksXG4gIH0pLFxuICB6XG4gICAgLnN0cmluZyh7XG4gICAgICBkZXNjcmlwdGlvbjogJ1BhdGggdG8gY292ZXJhZ2UgcmVzdWx0cyBmb3IgYSBzaW5nbGUgcHJvamVjdCBzZXR1cC4nLFxuICAgIH0pXG4gICAgLmluY2x1ZGVzKCdsY292JyksXG5dKTtcbmV4cG9ydCB0eXBlIENvdmVyYWdlUmVzdWx0ID0gei5pbmZlcjx0eXBlb2YgY292ZXJhZ2VSZXN1bHRTY2hlbWE+O1xuXG5leHBvcnQgY29uc3QgY292ZXJhZ2VQbHVnaW5Db25maWdTY2hlbWEgPSB6Lm9iamVjdCh7XG4gIGNvdmVyYWdlVG9vbENvbW1hbmQ6IHpcbiAgICAub2JqZWN0KHtcbiAgICAgIGNvbW1hbmQ6IHpcbiAgICAgICAgLnN0cmluZyh7IGRlc2NyaXB0aW9uOiAnQ29tbWFuZCB0byBydW4gY292ZXJhZ2UgdG9vbC4nIH0pXG4gICAgICAgIC5taW4oMSksXG4gICAgICBhcmdzOiB6XG4gICAgICAgIC5hcnJheSh6LnN0cmluZygpLCB7XG4gICAgICAgICAgZGVzY3JpcHRpb246ICdBcmd1bWVudHMgdG8gYmUgcGFzc2VkIHRvIHRoZSBjb3ZlcmFnZSB0b29sLicsXG4gICAgICAgIH0pXG4gICAgICAgIC5vcHRpb25hbCgpLFxuICAgIH0pXG4gICAgLm9wdGlvbmFsKCksXG4gIGNvdmVyYWdlVHlwZXM6IHpcbiAgICAuYXJyYXkoY292ZXJhZ2VUeXBlU2NoZW1hLCB7XG4gICAgICBkZXNjcmlwdGlvbjogJ0NvdmVyYWdlIHR5cGVzIG1lYXN1cmVkLiBEZWZhdWx0cyB0byBhbGwgYXZhaWxhYmxlIHR5cGVzLicsXG4gICAgfSlcbiAgICAubWluKDEpXG4gICAgLmRlZmF1bHQoWydmdW5jdGlvbicsICdicmFuY2gnLCAnbGluZSddKSxcbiAgcmVwb3J0czogelxuICAgIC5hcnJheShjb3ZlcmFnZVJlc3VsdFNjaGVtYSwge1xuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdQYXRoIHRvIGFsbCBjb2RlIGNvdmVyYWdlIHJlcG9ydCBmaWxlcy4gT25seSBMQ09WIGZvcm1hdCBpcyBzdXBwb3J0ZWQgZm9yIG5vdy4nLFxuICAgIH0pXG4gICAgLm1pbigxKSxcbiAgcGVyZmVjdFNjb3JlVGhyZXNob2xkOiB6XG4gICAgLm51bWJlcih7XG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ1Njb3JlIHdpbGwgYmUgMSAocGVyZmVjdCkgZm9yIHRoaXMgY292ZXJhZ2UgYW5kIGFib3ZlLiBTY29yZSByYW5nZSBpcyAwIC0gMS4nLFxuICAgIH0pXG4gICAgLmd0KDApXG4gICAgLm1heCgxKVxuICAgIC5vcHRpb25hbCgpLFxufSk7XG5leHBvcnQgdHlwZSBDb3ZlcmFnZVBsdWdpbkNvbmZpZyA9IHouaW5wdXQ8dHlwZW9mIGNvdmVyYWdlUGx1Z2luQ29uZmlnU2NoZW1hPjtcbmV4cG9ydCB0eXBlIEZpbmFsQ292ZXJhZ2VQbHVnaW5Db25maWcgPSB6LmluZmVyPFxuICB0eXBlb2YgY292ZXJhZ2VQbHVnaW5Db25maWdTY2hlbWFcbj47XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXIvaW5kZXgudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvcnVubmVyXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1jb3ZlcmFnZS9zcmMvbGliL3J1bm5lci9pbmRleC50c1wiO2ltcG9ydCB7IGJvbGQgfSBmcm9tICdhbnNpcyc7XG5pbXBvcnQgeyB3cml0ZUZpbGUgfSBmcm9tICdub2RlOmZzL3Byb21pc2VzJztcbmltcG9ydCBwYXRoIGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0T3V0cHV0cywgUnVubmVyQ29uZmlnIH0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5pbXBvcnQge1xuICBQcm9jZXNzRXJyb3IsXG4gIGVuc3VyZURpcmVjdG9yeUV4aXN0cyxcbiAgZXhlY3V0ZVByb2Nlc3MsXG4gIGZpbGVQYXRoVG9DbGlBcmcsXG4gIHJlYWRKc29uRmlsZSxcbiAgdWksXG59IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgdHlwZSB7IEZpbmFsQ292ZXJhZ2VQbHVnaW5Db25maWcgfSBmcm9tICcuLi9jb25maWcuanMnO1xuaW1wb3J0IHsgYXBwbHlNYXhTY29yZUFib3ZlVGhyZXNob2xkIH0gZnJvbSAnLi4vdXRpbHMuanMnO1xuaW1wb3J0IHsgUExVR0lOX0NPTkZJR19QQVRILCBSVU5ORVJfT1VUUFVUX1BBVEggfSBmcm9tICcuL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgeyBsY292UmVzdWx0c1RvQXVkaXRPdXRwdXRzIH0gZnJvbSAnLi9sY292L2xjb3YtcnVubmVyLmpzJztcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGV4ZWN1dGVSdW5uZXIoKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IHsgcmVwb3J0cywgY292ZXJhZ2VUb29sQ29tbWFuZCwgY292ZXJhZ2VUeXBlcyB9ID1cbiAgICBhd2FpdCByZWFkSnNvbkZpbGU8RmluYWxDb3ZlcmFnZVBsdWdpbkNvbmZpZz4oUExVR0lOX0NPTkZJR19QQVRIKTtcblxuICAvLyBSdW4gY292ZXJhZ2UgdG9vbCBpZiBwcm92aWRlZFxuICBpZiAoY292ZXJhZ2VUb29sQ29tbWFuZCAhPSBudWxsKSB7XG4gICAgY29uc3QgeyBjb21tYW5kLCBhcmdzIH0gPSBjb3ZlcmFnZVRvb2xDb21tYW5kO1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCBleGVjdXRlUHJvY2Vzcyh7IGNvbW1hbmQsIGFyZ3MgfSk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIFByb2Nlc3NFcnJvcikge1xuICAgICAgICB1aSgpLmxvZ2dlci5lcnJvcihib2xkKCdzdGRvdXQgZnJvbSBmYWlsZWQgY292ZXJhZ2UgdG9vbCBwcm9jZXNzOicpKTtcbiAgICAgICAgdWkoKS5sb2dnZXIuZXJyb3IoZXJyb3Iuc3Rkb3V0KTtcbiAgICAgICAgdWkoKS5sb2dnZXIuZXJyb3IoYm9sZCgnc3RkZXJyIGZyb20gZmFpbGVkIGNvdmVyYWdlIHRvb2wgcHJvY2VzczonKSk7XG4gICAgICAgIHVpKCkubG9nZ2VyLmVycm9yKGVycm9yLnN0ZGVycik7XG4gICAgICB9XG5cbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ0NvdmVyYWdlIHBsdWdpbjogUnVubmluZyBjb3ZlcmFnZSB0b29sIGZhaWxlZC4gTWFrZSBzdXJlIGFsbCB5b3VyIHByb3ZpZGVkIHRlc3RzIGFyZSBwYXNzaW5nLicsXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIC8vIENhbGN1bGF0ZSBjb3ZlcmFnZSBmcm9tIExDT1YgcmVzdWx0c1xuICBjb25zdCBhdWRpdE91dHB1dHMgPSBhd2FpdCBsY292UmVzdWx0c1RvQXVkaXRPdXRwdXRzKHJlcG9ydHMsIGNvdmVyYWdlVHlwZXMpO1xuXG4gIGF3YWl0IGVuc3VyZURpcmVjdG9yeUV4aXN0cyhwYXRoLmRpcm5hbWUoUlVOTkVSX09VVFBVVF9QQVRIKSk7XG4gIGF3YWl0IHdyaXRlRmlsZShSVU5ORVJfT1VUUFVUX1BBVEgsIEpTT04uc3RyaW5naWZ5KGF1ZGl0T3V0cHV0cykpO1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlUnVubmVyQ29uZmlnKFxuICBzY3JpcHRQYXRoOiBzdHJpbmcsXG4gIGNvbmZpZzogRmluYWxDb3ZlcmFnZVBsdWdpbkNvbmZpZyxcbik6IFByb21pc2U8UnVubmVyQ29uZmlnPiB7XG4gIC8vIENyZWF0ZSBKU09OIGNvbmZpZyBmb3IgZXhlY3V0ZVJ1bm5lclxuICBhd2FpdCBlbnN1cmVEaXJlY3RvcnlFeGlzdHMocGF0aC5kaXJuYW1lKFBMVUdJTl9DT05GSUdfUEFUSCkpO1xuICBhd2FpdCB3cml0ZUZpbGUoUExVR0lOX0NPTkZJR19QQVRILCBKU09OLnN0cmluZ2lmeShjb25maWcpKTtcblxuICBjb25zdCB0aHJlc2hvbGQgPSBjb25maWcucGVyZmVjdFNjb3JlVGhyZXNob2xkO1xuXG4gIHJldHVybiB7XG4gICAgY29tbWFuZDogJ25vZGUnLFxuICAgIGFyZ3M6IFtmaWxlUGF0aFRvQ2xpQXJnKHNjcmlwdFBhdGgpXSxcbiAgICBvdXRwdXRGaWxlOiBSVU5ORVJfT1VUUFVUX1BBVEgsXG4gICAgLi4uKHRocmVzaG9sZCAhPSBudWxsICYmIHtcbiAgICAgIG91dHB1dFRyYW5zZm9ybTogb3V0cHV0cyA9PlxuICAgICAgICBhcHBseU1heFNjb3JlQWJvdmVUaHJlc2hvbGQob3V0cHV0cyBhcyBBdWRpdE91dHB1dHMsIHRocmVzaG9sZCksXG4gICAgfSksXG4gIH07XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXIvY29uc3RhbnRzLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1jb3ZlcmFnZS9zcmMvbGliL3J1bm5lclwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXIvY29uc3RhbnRzLnRzXCI7aW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB7IHBsdWdpbldvcmtEaXIgfSBmcm9tICdAY29kZS1wdXNodXAvdXRpbHMnO1xuXG5leHBvcnQgY29uc3QgV09SS0RJUiA9IHBsdWdpbldvcmtEaXIoJ2NvdmVyYWdlJyk7XG5leHBvcnQgY29uc3QgUlVOTkVSX09VVFBVVF9QQVRIID0gcGF0aC5qb2luKFdPUktESVIsICdydW5uZXItb3V0cHV0Lmpzb24nKTtcbmV4cG9ydCBjb25zdCBQTFVHSU5fQ09ORklHX1BBVEggPSBwYXRoLmpvaW4oXG4gIHByb2Nlc3MuY3dkKCksXG4gIFdPUktESVIsXG4gICdwbHVnaW4tY29uZmlnLmpzb24nLFxuKTtcblxuZXhwb3J0IGNvbnN0IElOVkFMSURfRlVOQ1RJT05fTkFNRSA9ICcoZW1wdHktcmVwb3J0KSc7XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXIvbGNvdi9sY292LXJ1bm5lci50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXIvbGNvdlwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXIvbGNvdi9sY292LXJ1bm5lci50c1wiO2ltcG9ydCBwYXRoIGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgdHlwZSB7IExDT1ZSZWNvcmQgfSBmcm9tICdwYXJzZS1sY292JztcbmltcG9ydCB0eXBlIHsgQXVkaXRPdXRwdXRzIH0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5pbXBvcnQgeyBleGlzdHMsIHJlYWRUZXh0RmlsZSwgdG9Vbml4TmV3bGluZXMsIHVpIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB0eXBlIHsgQ292ZXJhZ2VSZXN1bHQsIENvdmVyYWdlVHlwZSB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XG5pbXBvcnQgeyBtZXJnZUxjb3ZSZXN1bHRzIH0gZnJvbSAnLi9tZXJnZS1sY292LmpzJztcbmltcG9ydCB7IHBhcnNlTGNvdiB9IGZyb20gJy4vcGFyc2UtbGNvdi5qcyc7XG5pbXBvcnQge1xuICBsY292Q292ZXJhZ2VUb0F1ZGl0T3V0cHV0LFxuICByZWNvcmRUb1N0YXRGdW5jdGlvbk1hcHBlcixcbn0gZnJvbSAnLi90cmFuc2Zvcm0uanMnO1xuaW1wb3J0IHR5cGUgeyBMQ09WU3RhdCwgTENPVlN0YXRzIH0gZnJvbSAnLi90eXBlcy5qcyc7XG5cbi8vIE5vdGU6IGNvbmRpdGlvbiBvciBzdGF0ZW1lbnQgY292ZXJhZ2UgaXMgbm90IHN1cHBvcnRlZCBpbiBMQ09WXG4vLyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy80ODI2MDQzNC9pcy1pdC1wb3NzaWJsZS10by1jaGVjay1jb25kaXRpb24tY292ZXJhZ2Utd2l0aC1nY292XG5cbi8qKlxuICpcbiAqIEBwYXJhbSByZXN1bHRzIFBhdGhzIHRvIExDT1YgcmVzdWx0c1xuICogQHBhcmFtIGNvdmVyYWdlVHlwZXMgdHlwZXMgb2YgY292ZXJhZ2UgdG8gYmUgY29uc2lkZXJlZFxuICogQHJldHVybnMgQXVkaXQgb3V0cHV0cyB3aXRoIGNvbXBsZXRlIGNvdmVyYWdlIGRhdGEuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBsY292UmVzdWx0c1RvQXVkaXRPdXRwdXRzKFxuICByZXN1bHRzOiBDb3ZlcmFnZVJlc3VsdFtdLFxuICBjb3ZlcmFnZVR5cGVzOiBDb3ZlcmFnZVR5cGVbXSxcbik6IFByb21pc2U8QXVkaXRPdXRwdXRzPiB7XG4gIC8vIFBhcnNlIGxjb3YgZmlsZXNcbiAgY29uc3QgbGNvdlJlc3VsdHMgPSBhd2FpdCBwYXJzZUxjb3ZGaWxlcyhyZXN1bHRzKTtcblxuICAvLyBNZXJnZSBtdWx0aXBsZSBjb3ZlcmFnZSByZXBvcnRzIGZvciB0aGUgc2FtZSBmaWxlXG4gIGNvbnN0IG1lcmdlZFJlc3VsdHMgPSBtZXJnZUxjb3ZSZXN1bHRzKGxjb3ZSZXN1bHRzKTtcblxuICAvLyBDYWxjdWxhdGUgY29kZSBjb3ZlcmFnZSBmcm9tIGFsbCBjb3ZlcmFnZSByZXN1bHRzXG4gIGNvbnN0IHRvdGFsQ292ZXJhZ2VTdGF0cyA9IGdldFRvdGFsQ292ZXJhZ2VGcm9tTGNvdlJlY29yZHMoXG4gICAgbWVyZ2VkUmVzdWx0cyxcbiAgICBjb3ZlcmFnZVR5cGVzLFxuICApO1xuXG4gIHJldHVybiBjb3ZlcmFnZVR5cGVzXG4gICAgLm1hcChjb3ZlcmFnZVR5cGUgPT4ge1xuICAgICAgY29uc3Qgc3RhdHMgPSB0b3RhbENvdmVyYWdlU3RhdHNbY292ZXJhZ2VUeXBlXTtcbiAgICAgIGlmICghc3RhdHMpIHtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG4gICAgICByZXR1cm4gbGNvdkNvdmVyYWdlVG9BdWRpdE91dHB1dChzdGF0cywgY292ZXJhZ2VUeXBlKTtcbiAgICB9KVxuICAgIC5maWx0ZXIoZXhpc3RzKTtcbn1cblxuLyoqXG4gKlxuICogQHBhcmFtIHJlc3VsdHMgUGF0aHMgdG8gTENPViByZXN1bHRzXG4gKiBAcmV0dXJucyBBcnJheSBvZiBwYXJzZWQgTENPVlJlY29yZHMuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwYXJzZUxjb3ZGaWxlcyhcbiAgcmVzdWx0czogQ292ZXJhZ2VSZXN1bHRbXSxcbik6IFByb21pc2U8TENPVlJlY29yZFtdPiB7XG4gIGNvbnN0IHBhcnNlZFJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbChcbiAgICByZXN1bHRzLm1hcChhc3luYyByZXN1bHQgPT4ge1xuICAgICAgY29uc3QgcmVzdWx0c1BhdGggPVxuICAgICAgICB0eXBlb2YgcmVzdWx0ID09PSAnc3RyaW5nJyA/IHJlc3VsdCA6IHJlc3VsdC5yZXN1bHRzUGF0aDtcbiAgICAgIGNvbnN0IGxjb3ZGaWxlQ29udGVudCA9IGF3YWl0IHJlYWRUZXh0RmlsZShyZXN1bHRzUGF0aCk7XG4gICAgICBpZiAobGNvdkZpbGVDb250ZW50LnRyaW0oKSA9PT0gJycpIHtcbiAgICAgICAgdWkoKS5sb2dnZXIud2FybmluZyhcbiAgICAgICAgICBgQ292ZXJhZ2UgcGx1Z2luOiBFbXB0eSBsY292IHJlcG9ydCBmaWxlIGRldGVjdGVkIGF0ICR7cmVzdWx0c1BhdGh9LmAsXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgICBjb25zdCBwYXJzZWRSZWNvcmRzID0gcGFyc2VMY292KHRvVW5peE5ld2xpbmVzKGxjb3ZGaWxlQ29udGVudCkpO1xuICAgICAgcmV0dXJuIHBhcnNlZFJlY29yZHMubWFwPExDT1ZSZWNvcmQ+KHJlY29yZCA9PiAoe1xuICAgICAgICAuLi5yZWNvcmQsXG4gICAgICAgIGZpbGU6XG4gICAgICAgICAgdHlwZW9mIHJlc3VsdCA9PT0gJ3N0cmluZycgfHwgcmVzdWx0LnBhdGhUb1Byb2plY3QgPT0gbnVsbFxuICAgICAgICAgICAgPyByZWNvcmQuZmlsZVxuICAgICAgICAgICAgOiBwYXRoLmpvaW4ocmVzdWx0LnBhdGhUb1Byb2plY3QsIHJlY29yZC5maWxlKSxcbiAgICAgIH0pKTtcbiAgICB9KSxcbiAgKTtcbiAgaWYgKHBhcnNlZFJlc3VsdHMubGVuZ3RoICE9PSByZXN1bHRzLmxlbmd0aCkge1xuICAgIHRocm93IG5ldyBFcnJvcignU29tZSBwcm92aWRlZCBMQ09WIHJlc3VsdHMgd2VyZSBub3QgdmFsaWQuJyk7XG4gIH1cblxuICBjb25zdCBmbGF0UmVzdWx0cyA9IHBhcnNlZFJlc3VsdHMuZmxhdCgpO1xuXG4gIGlmIChmbGF0UmVzdWx0cy5sZW5ndGggPT09IDApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0FsbCBwcm92aWRlZCByZXN1bHRzIGFyZSBlbXB0eS4nKTtcbiAgfVxuXG4gIHJldHVybiBmbGF0UmVzdWx0cztcbn1cblxuLyoqXG4gKlxuICogQHBhcmFtIHJlY29yZHMgVGhpcyBmdW5jdGlvbiBhZ2dyZWdhdGVzIGNvdmVyYWdlIHN0YXRzIGZyb20gYWxsIGNvdmVyYWdlIGZpbGVzXG4gKiBAcGFyYW0gY292ZXJhZ2VUeXBlcyBUeXBlcyBvZiBjb3ZlcmFnZSB0byBiZSBnYXRoZXJlZFxuICogQHJldHVybnMgQ29tcGxldGUgY292ZXJhZ2Ugc3RhdHMgZm9yIGFsbCBkZWZpbmVkIHR5cGVzIG9mIGNvdmVyYWdlLlxuICovXG5mdW5jdGlvbiBnZXRUb3RhbENvdmVyYWdlRnJvbUxjb3ZSZWNvcmRzKFxuICByZWNvcmRzOiBMQ09WUmVjb3JkW10sXG4gIGNvdmVyYWdlVHlwZXM6IENvdmVyYWdlVHlwZVtdLFxuKTogTENPVlN0YXRzIHtcbiAgcmV0dXJuIHJlY29yZHMucmVkdWNlPExDT1ZTdGF0cz4oXG4gICAgKGFjYywgcmVwb3J0KSA9PlxuICAgICAgT2JqZWN0LmZyb21FbnRyaWVzKFtcbiAgICAgICAgLi4uT2JqZWN0LmVudHJpZXMoYWNjKSxcbiAgICAgICAgLi4uKFxuICAgICAgICAgIE9iamVjdC5lbnRyaWVzKFxuICAgICAgICAgICAgZ2V0Q292ZXJhZ2VTdGF0c0Zyb21MY292UmVjb3JkKHJlcG9ydCwgY292ZXJhZ2VUeXBlcyksXG4gICAgICAgICAgKSBhcyBbQ292ZXJhZ2VUeXBlLCBMQ09WU3RhdF1bXVxuICAgICAgICApLm1hcCgoW3R5cGUsIHN0YXRzXSk6IFtDb3ZlcmFnZVR5cGUsIExDT1ZTdGF0XSA9PiBbXG4gICAgICAgICAgdHlwZSxcbiAgICAgICAgICB7XG4gICAgICAgICAgICB0b3RhbEZvdW5kOiAoYWNjW3R5cGVdPy50b3RhbEZvdW5kID8/IDApICsgc3RhdHMudG90YWxGb3VuZCxcbiAgICAgICAgICAgIHRvdGFsSGl0OiAoYWNjW3R5cGVdPy50b3RhbEhpdCA/PyAwKSArIHN0YXRzLnRvdGFsSGl0LFxuICAgICAgICAgICAgaXNzdWVzOiBbLi4uKGFjY1t0eXBlXT8uaXNzdWVzID8/IFtdKSwgLi4uc3RhdHMuaXNzdWVzXSxcbiAgICAgICAgICB9LFxuICAgICAgICBdKSxcbiAgICAgIF0pLFxuICAgIHt9LFxuICApO1xufVxuXG4vKipcbiAqIEBwYXJhbSByZWNvcmQgcmVjb3JkIGZpbGUgZGF0YVxuICogQHBhcmFtIGNvdmVyYWdlVHlwZXMgdHlwZXMgb2YgY292ZXJhZ2UgdG8gYmUgZ2F0aGVyZWRcbiAqIEByZXR1cm5zIFJlbGV2YW50IGNvdmVyYWdlIGRhdGEgZnJvbSBvbmUgbGNvdiByZWNvcmQgZmlsZS5cbiAqL1xuZnVuY3Rpb24gZ2V0Q292ZXJhZ2VTdGF0c0Zyb21MY292UmVjb3JkKFxuICByZWNvcmQ6IExDT1ZSZWNvcmQsXG4gIGNvdmVyYWdlVHlwZXM6IENvdmVyYWdlVHlwZVtdLFxuKTogTENPVlN0YXRzIHtcbiAgcmV0dXJuIE9iamVjdC5mcm9tRW50cmllcyhcbiAgICBjb3ZlcmFnZVR5cGVzLm1hcCgoY292ZXJhZ2VUeXBlKTogW0NvdmVyYWdlVHlwZSwgTENPVlN0YXRdID0+IFtcbiAgICAgIGNvdmVyYWdlVHlwZSxcbiAgICAgIHJlY29yZFRvU3RhdEZ1bmN0aW9uTWFwcGVyW2NvdmVyYWdlVHlwZV0ocmVjb3JkKSxcbiAgICBdKSxcbiAgKTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1jb3ZlcmFnZS9zcmMvbGliL3J1bm5lci9sY292L3BhcnNlLWxjb3YudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvcnVubmVyL2xjb3ZcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvcnVubmVyL2xjb3YvcGFyc2UtbGNvdi50c1wiO2ltcG9ydCBwYXJzZUxjb3ZFeHBvcnQgZnJvbSAncGFyc2UtbGNvdic7XG5cbnR5cGUgUGFyc2VMY292Rm4gPSB0eXBlb2YgcGFyc2VMY292RXhwb3J0O1xuXG4vLyB0aGUgcGFyc2UtbGNvdiBleHBvcnQgaXMgaW5jb25zaXN0ZW50IChzb21ldGltZXMgaXQncyAuZGVmYXVsdCwgc29tZXRpbWVzIGl0J3MgLmRlZmF1bHQuZGVmYXVsdClcbmNvbnN0IGdvZEtub3dzID0gcGFyc2VMY292RXhwb3J0IGFzIHVua25vd24gYXNcbiAgfCBQYXJzZUxjb3ZGblxuICB8IHsgZGVmYXVsdDogUGFyc2VMY292Rm4gfTtcblxuZXhwb3J0IGNvbnN0IHBhcnNlTGNvdjogUGFyc2VMY292Rm4gPVxuICAnZGVmYXVsdCcgaW4gZ29kS25vd3MgPyBnb2RLbm93cy5kZWZhdWx0IDogZ29kS25vd3M7XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXIvbGNvdi90cmFuc2Zvcm0udHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvcnVubmVyL2xjb3ZcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvcnVubmVyL2xjb3YvdHJhbnNmb3JtLnRzXCI7aW1wb3J0IHR5cGUgeyBMQ09WUmVjb3JkIH0gZnJvbSAncGFyc2UtbGNvdic7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0T3V0cHV0LCBJc3N1ZSB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgdG9OdW1iZXJQcmVjaXNpb24sIHRvT3JkaW5hbCB9IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgdHlwZSB7IENvdmVyYWdlVHlwZSB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XG5pbXBvcnQgeyBJTlZBTElEX0ZVTkNUSU9OX05BTUUgfSBmcm9tICcuLi9jb25zdGFudHMuanMnO1xuaW1wb3J0IHR5cGUgeyBMQ09WU3RhdCB9IGZyb20gJy4vdHlwZXMuanMnO1xuaW1wb3J0IHsgY2FsY3VsYXRlQ292ZXJhZ2UsIG1lcmdlQ29uc2VjdXRpdmVOdW1iZXJzIH0gZnJvbSAnLi91dGlscy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBsY292UmVwb3J0VG9GdW5jdGlvblN0YXQocmVjb3JkOiBMQ09WUmVjb3JkKTogTENPVlN0YXQge1xuICBjb25zdCB2YWxpZFJlY29yZCA9IHJlbW92ZUVtcHR5UmVwb3J0KHJlY29yZCk7XG5cbiAgcmV0dXJuIHtcbiAgICB0b3RhbEZvdW5kOiB2YWxpZFJlY29yZC5mdW5jdGlvbnMuZm91bmQsXG4gICAgdG90YWxIaXQ6IHZhbGlkUmVjb3JkLmZ1bmN0aW9ucy5oaXQsXG4gICAgaXNzdWVzOlxuICAgICAgdmFsaWRSZWNvcmQuZnVuY3Rpb25zLmhpdCA8IHZhbGlkUmVjb3JkLmZ1bmN0aW9ucy5mb3VuZFxuICAgICAgICA/IHZhbGlkUmVjb3JkLmZ1bmN0aW9ucy5kZXRhaWxzXG4gICAgICAgICAgICAuZmlsdGVyKGRldGFpbCA9PiAhZGV0YWlsLmhpdClcbiAgICAgICAgICAgIC5tYXAoXG4gICAgICAgICAgICAgIChkZXRhaWwpOiBJc3N1ZSA9PiAoe1xuICAgICAgICAgICAgICAgIG1lc3NhZ2U6IGBGdW5jdGlvbiAke2RldGFpbC5uYW1lfSBpcyBub3QgY2FsbGVkIGluIGFueSB0ZXN0IGNhc2UuYCxcbiAgICAgICAgICAgICAgICBzZXZlcml0eTogJ2Vycm9yJyxcbiAgICAgICAgICAgICAgICBzb3VyY2U6IHtcbiAgICAgICAgICAgICAgICAgIGZpbGU6IHZhbGlkUmVjb3JkLmZpbGUsXG4gICAgICAgICAgICAgICAgICBwb3NpdGlvbjogeyBzdGFydExpbmU6IGRldGFpbC5saW5lIH0sXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgfSksXG4gICAgICAgICAgICApXG4gICAgICAgIDogW10sXG4gIH07XG59XG5cbmZ1bmN0aW9uIHJlbW92ZUVtcHR5UmVwb3J0KHJlY29yZDogTENPVlJlY29yZCk6IExDT1ZSZWNvcmQge1xuICBjb25zdCB2YWxpZEZ1bmN0aW9ucyA9IHJlY29yZC5mdW5jdGlvbnMuZGV0YWlscy5maWx0ZXIoXG4gICAgZGV0YWlsID0+IGRldGFpbC5uYW1lICE9PSBJTlZBTElEX0ZVTkNUSU9OX05BTUUsXG4gICk7XG5cbiAgaWYgKHZhbGlkRnVuY3Rpb25zLmxlbmd0aCA9PT0gcmVjb3JkLmZ1bmN0aW9ucy5mb3VuZCkge1xuICAgIHJldHVybiByZWNvcmQ7XG4gIH1cblxuICByZXR1cm4ge1xuICAgIC4uLnJlY29yZCxcbiAgICBmdW5jdGlvbnM6IHtcbiAgICAgIGRldGFpbHM6IHZhbGlkRnVuY3Rpb25zLFxuICAgICAgZm91bmQ6IHZhbGlkRnVuY3Rpb25zLmxlbmd0aCxcbiAgICAgIGhpdDogdmFsaWRGdW5jdGlvbnMucmVkdWNlKFxuICAgICAgICAoYWNjLCBmbikgPT4gYWNjICsgKGZuLmhpdCAhPSBudWxsICYmIGZuLmhpdCA+IDAgPyAxIDogMCksXG4gICAgICAgIDAsXG4gICAgICApLFxuICAgIH0sXG4gIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsY292UmVwb3J0VG9MaW5lU3RhdChyZWNvcmQ6IExDT1ZSZWNvcmQpOiBMQ09WU3RhdCB7XG4gIGNvbnN0IG1pc3NpbmdDb3ZlcmFnZSA9IHJlY29yZC5saW5lcy5oaXQgPCByZWNvcmQubGluZXMuZm91bmQ7XG4gIGNvbnN0IGxpbmVzID0gbWlzc2luZ0NvdmVyYWdlXG4gICAgPyByZWNvcmQubGluZXMuZGV0YWlsc1xuICAgICAgICAuZmlsdGVyKGRldGFpbCA9PiAhZGV0YWlsLmhpdClcbiAgICAgICAgLm1hcChkZXRhaWwgPT4gZGV0YWlsLmxpbmUpXG4gICAgOiBbXTtcblxuICBjb25zdCBsaW5lUG9zaXRpb25zID0gbWVyZ2VDb25zZWN1dGl2ZU51bWJlcnMobGluZXMpO1xuXG4gIHJldHVybiB7XG4gICAgdG90YWxGb3VuZDogcmVjb3JkLmxpbmVzLmZvdW5kLFxuICAgIHRvdGFsSGl0OiByZWNvcmQubGluZXMuaGl0LFxuICAgIGlzc3VlczogbWlzc2luZ0NvdmVyYWdlXG4gICAgICA/IGxpbmVQb3NpdGlvbnMubWFwKChsaW5lUG9zaXRpb24pOiBJc3N1ZSA9PiB7XG4gICAgICAgICAgY29uc3QgbGluZVJlZmVyZW5jZSA9XG4gICAgICAgICAgICBsaW5lUG9zaXRpb24uZW5kID09IG51bGxcbiAgICAgICAgICAgICAgPyBgTGluZSAke2xpbmVQb3NpdGlvbi5zdGFydH0gaXNgXG4gICAgICAgICAgICAgIDogYExpbmVzICR7bGluZVBvc2l0aW9uLnN0YXJ0fS0ke2xpbmVQb3NpdGlvbi5lbmR9IGFyZWA7XG5cbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgbWVzc2FnZTogYCR7bGluZVJlZmVyZW5jZX0gbm90IGNvdmVyZWQgaW4gYW55IHRlc3QgY2FzZS5gLFxuICAgICAgICAgICAgc2V2ZXJpdHk6ICd3YXJuaW5nJyxcbiAgICAgICAgICAgIHNvdXJjZToge1xuICAgICAgICAgICAgICBmaWxlOiByZWNvcmQuZmlsZSxcbiAgICAgICAgICAgICAgcG9zaXRpb246IHtcbiAgICAgICAgICAgICAgICBzdGFydExpbmU6IGxpbmVQb3NpdGlvbi5zdGFydCxcbiAgICAgICAgICAgICAgICBlbmRMaW5lOiBsaW5lUG9zaXRpb24uZW5kLFxuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9O1xuICAgICAgICB9KVxuICAgICAgOiBbXSxcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGxjb3ZSZXBvcnRUb0JyYW5jaFN0YXQocmVjb3JkOiBMQ09WUmVjb3JkKTogTENPVlN0YXQge1xuICByZXR1cm4ge1xuICAgIHRvdGFsRm91bmQ6IHJlY29yZC5icmFuY2hlcy5mb3VuZCxcbiAgICB0b3RhbEhpdDogcmVjb3JkLmJyYW5jaGVzLmhpdCxcbiAgICBpc3N1ZXM6XG4gICAgICByZWNvcmQuYnJhbmNoZXMuaGl0IDwgcmVjb3JkLmJyYW5jaGVzLmZvdW5kXG4gICAgICAgID8gcmVjb3JkLmJyYW5jaGVzLmRldGFpbHNcbiAgICAgICAgICAgIC5maWx0ZXIoZGV0YWlsID0+ICFkZXRhaWwudGFrZW4pXG4gICAgICAgICAgICAubWFwKFxuICAgICAgICAgICAgICAoZGV0YWlsKTogSXNzdWUgPT4gKHtcbiAgICAgICAgICAgICAgICBtZXNzYWdlOiBgJHt0b09yZGluYWwoXG4gICAgICAgICAgICAgICAgICBkZXRhaWwuYnJhbmNoICsgMSxcbiAgICAgICAgICAgICAgICApfSBicmFuY2ggaXMgbm90IHRha2VuIGluIGFueSB0ZXN0IGNhc2UuYCxcbiAgICAgICAgICAgICAgICBzZXZlcml0eTogJ2Vycm9yJyxcbiAgICAgICAgICAgICAgICBzb3VyY2U6IHtcbiAgICAgICAgICAgICAgICAgIGZpbGU6IHJlY29yZC5maWxlLFxuICAgICAgICAgICAgICAgICAgcG9zaXRpb246IHsgc3RhcnRMaW5lOiBkZXRhaWwubGluZSB9LFxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIH0pLFxuICAgICAgICAgICAgKVxuICAgICAgICA6IFtdLFxuICB9O1xufVxuXG5leHBvcnQgY29uc3QgcmVjb3JkVG9TdGF0RnVuY3Rpb25NYXBwZXIgPSB7XG4gIGJyYW5jaDogbGNvdlJlcG9ydFRvQnJhbmNoU3RhdCxcbiAgbGluZTogbGNvdlJlcG9ydFRvTGluZVN0YXQsXG4gIGZ1bmN0aW9uOiBsY292UmVwb3J0VG9GdW5jdGlvblN0YXQsXG59O1xuXG4vKipcbiAqXG4gKiBAcGFyYW0gc3RhdCBjb2RlIGNvdmVyYWdlIHJlc3VsdCBmb3IgYSBnaXZlbiB0eXBlXG4gKiBAcGFyYW0gY292ZXJhZ2VUeXBlIGNvZGUgY292ZXJhZ2UgdHlwZVxuICogQHJldHVybnMgUmVzdWx0IG9mIGNvbXBsZXRlIGNvZGUgY2NvdmVyYWdlIGRhdGEgY292ZXJ0ZWQgdG8gQXVkaXRPdXRwdXRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGxjb3ZDb3ZlcmFnZVRvQXVkaXRPdXRwdXQoXG4gIHN0YXQ6IExDT1ZTdGF0LFxuICBjb3ZlcmFnZVR5cGU6IENvdmVyYWdlVHlwZSxcbik6IEF1ZGl0T3V0cHV0IHtcbiAgY29uc3QgY292ZXJhZ2UgPSBjYWxjdWxhdGVDb3ZlcmFnZShzdGF0LnRvdGFsSGl0LCBzdGF0LnRvdGFsRm91bmQpO1xuICBjb25zdCBNQVhfREVDSU1BTF9QTEFDRVMgPSA0O1xuICBjb25zdCBjb3ZlcmFnZVBlcmNlbnRhZ2UgPSBjb3ZlcmFnZSAqIDEwMDtcblxuICByZXR1cm4ge1xuICAgIHNsdWc6IGAke2NvdmVyYWdlVHlwZX0tY292ZXJhZ2VgLFxuICAgIHNjb3JlOiB0b051bWJlclByZWNpc2lvbihjb3ZlcmFnZSwgTUFYX0RFQ0lNQUxfUExBQ0VTKSxcbiAgICB2YWx1ZTogY292ZXJhZ2VQZXJjZW50YWdlLFxuICAgIGRpc3BsYXlWYWx1ZTogYCR7dG9OdW1iZXJQcmVjaXNpb24oY292ZXJhZ2VQZXJjZW50YWdlLCAxKX0gJWAsXG4gICAgZGV0YWlsczoge1xuICAgICAgaXNzdWVzOiBzdGF0Lmlzc3VlcyxcbiAgICB9LFxuICB9O1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvbngvY292ZXJhZ2UtcGF0aHMudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvbnhcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvbngvY292ZXJhZ2UtcGF0aHMudHNcIjsvLy8gPHJlZmVyZW5jZSB0eXBlcz1cInZpdGVzdFwiIC8+XG5pbXBvcnQgdHlwZSB7XG4gIFByb2plY3RDb25maWd1cmF0aW9uLFxuICBQcm9qZWN0R3JhcGhQcm9qZWN0Tm9kZSxcbiAgVHJlZSxcbn0gZnJvbSAnQG54L2RldmtpdCc7XG5pbXBvcnQgdHlwZSB7IEplc3RFeGVjdXRvck9wdGlvbnMgfSBmcm9tICdAbngvamVzdC9zcmMvZXhlY3V0b3JzL2plc3Qvc2NoZW1hJztcbmltcG9ydCB0eXBlIHsgVml0ZXN0RXhlY3V0b3JPcHRpb25zIH0gZnJvbSAnQG54L3ZpdGUvZXhlY3V0b3JzJztcbmltcG9ydCB7IGJvbGQgfSBmcm9tICdhbnNpcyc7XG5pbXBvcnQgcGF0aCBmcm9tICdub2RlOnBhdGgnO1xuaW1wb3J0IHsgaW1wb3J0TW9kdWxlLCB1aSB9IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgdHlwZSB7IENvdmVyYWdlUmVzdWx0IH0gZnJvbSAnLi4vY29uZmlnLmpzJztcblxuLyoqXG4gKiBAcGFyYW0gdGFyZ2V0cyBueCB0YXJnZXRzIHRvIGJlIHVzZWQgZm9yIG1lYXN1cmluZyBjb3ZlcmFnZSwgdGVzdCBieSBkZWZhdWx0XG4gKiBAcmV0dXJucyBBbiBhcnJheSBvZiBjb3ZlcmFnZSByZXN1bHQgaW5mb3JtYXRpb24gZm9yIHRoZSBjb3ZlcmFnZSBwbHVnaW4uXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXROeENvdmVyYWdlUGF0aHMoXG4gIHRhcmdldHM6IHN0cmluZ1tdID0gWyd0ZXN0J10sXG4gIHZlcmJvc2U/OiBib29sZWFuLFxuKTogUHJvbWlzZTxDb3ZlcmFnZVJlc3VsdFtdPiB7XG4gIGlmICh2ZXJib3NlKSB7XG4gICAgdWkoKS5sb2dnZXIuaW5mbyhcbiAgICAgIGJvbGQoJ1x1RDgzRFx1RENBMSBHYXRoZXJpbmcgY292ZXJhZ2UgZnJvbSB0aGUgZm9sbG93aW5nIG54IHByb2plY3RzOicpLFxuICAgICk7XG4gIH1cblxuICBjb25zdCB7IGNyZWF0ZVByb2plY3RHcmFwaEFzeW5jIH0gPSBhd2FpdCBpbXBvcnQoJ0BueC9kZXZraXQnKTtcbiAgY29uc3QgeyBub2RlcyB9ID0gYXdhaXQgY3JlYXRlUHJvamVjdEdyYXBoQXN5bmMoeyBleGl0T25FcnJvcjogZmFsc2UgfSk7XG5cbiAgY29uc3QgY292ZXJhZ2VSZXN1bHRzID0gYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgdGFyZ2V0cy5tYXAoYXN5bmMgdGFyZ2V0ID0+IHtcbiAgICAgIGNvbnN0IHJlbGV2YW50Tm9kZXMgPSBPYmplY3QudmFsdWVzKG5vZGVzKS5maWx0ZXIoZ3JhcGggPT5cbiAgICAgICAgaGFzTnhUYXJnZXQoZ3JhcGgsIHRhcmdldCksXG4gICAgICApO1xuXG4gICAgICByZXR1cm4gYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICAgIHJlbGV2YW50Tm9kZXMubWFwPFByb21pc2U8Q292ZXJhZ2VSZXN1bHQ+Pihhc3luYyAoeyBuYW1lLCBkYXRhIH0pID0+IHtcbiAgICAgICAgICBjb25zdCBjb3ZlcmFnZVBhdGhzID0gYXdhaXQgZ2V0Q292ZXJhZ2VQYXRoc0ZvclRhcmdldChkYXRhLCB0YXJnZXQpO1xuICAgICAgICAgIGlmICh2ZXJib3NlKSB7XG4gICAgICAgICAgICB1aSgpLmxvZ2dlci5pbmZvKGAtICR7bmFtZX06ICR7dGFyZ2V0fWApO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gY292ZXJhZ2VQYXRocztcbiAgICAgICAgfSksXG4gICAgICApO1xuICAgIH0pLFxuICApO1xuXG4gIGlmICh2ZXJib3NlKSB7XG4gICAgdWkoKS5sb2dnZXIuaW5mbygnXFxuJyk7XG4gIH1cblxuICByZXR1cm4gY292ZXJhZ2VSZXN1bHRzLmZsYXQoKTtcbn1cblxuZnVuY3Rpb24gaGFzTnhUYXJnZXQoXG4gIHByb2plY3Q6IFByb2plY3RHcmFwaFByb2plY3ROb2RlLFxuICB0YXJnZXQ6IHN0cmluZyxcbik6IGJvb2xlYW4ge1xuICByZXR1cm4gcHJvamVjdC5kYXRhLnRhcmdldHMgIT0gbnVsbCAmJiB0YXJnZXQgaW4gcHJvamVjdC5kYXRhLnRhcmdldHM7XG59XG5cbmV4cG9ydCB0eXBlIFZpdGVzdENvdmVyYWdlQ29uZmlnID0ge1xuICB0ZXN0OiB7XG4gICAgY292ZXJhZ2U/OiB7XG4gICAgICByZXBvcnRlcj86IHN0cmluZ1tdO1xuICAgICAgcmVwb3J0c0RpcmVjdG9yeT86IHN0cmluZztcbiAgICB9O1xuICB9O1xufTtcblxuZXhwb3J0IHR5cGUgSmVzdENvdmVyYWdlQ29uZmlnID0ge1xuICBjb3ZlcmFnZURpcmVjdG9yeT86IHN0cmluZztcbiAgY292ZXJhZ2VSZXBvcnRlcnM/OiBzdHJpbmdbXTtcbn07XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRDb3ZlcmFnZVBhdGhzRm9yVGFyZ2V0KFxuICBwcm9qZWN0OiBQcm9qZWN0Q29uZmlndXJhdGlvbixcbiAgdGFyZ2V0OiBzdHJpbmcsXG4pOiBQcm9taXNlPENvdmVyYWdlUmVzdWx0PiB7XG4gIGNvbnN0IHRhcmdldENvbmZpZyA9IHByb2plY3QudGFyZ2V0cz8uW3RhcmdldF07XG5cbiAgaWYgKCF0YXJnZXRDb25maWcpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgTm8gY29uZmlndXJhdGlvbiBmb3VuZCBmb3IgdGFyZ2V0ICR7dGFyZ2V0fSBpbiBwcm9qZWN0ICR7cHJvamVjdC5uYW1lfWAsXG4gICAgKTtcbiAgfVxuXG4gIGlmICh0YXJnZXRDb25maWcuZXhlY3V0b3I/LmluY2x1ZGVzKCdAbngvdml0ZScpKSB7XG4gICAgcmV0dXJuIGdldENvdmVyYWdlUGF0aEZvclZpdGVzdChcbiAgICAgIHRhcmdldENvbmZpZy5vcHRpb25zIGFzIFZpdGVzdEV4ZWN1dG9yT3B0aW9ucyxcbiAgICAgIHByb2plY3QsXG4gICAgICB0YXJnZXQsXG4gICAgKTtcbiAgfVxuXG4gIGlmICh0YXJnZXRDb25maWcuZXhlY3V0b3I/LmluY2x1ZGVzKCdAbngvamVzdCcpKSB7XG4gICAgcmV0dXJuIGdldENvdmVyYWdlUGF0aEZvckplc3QoXG4gICAgICB0YXJnZXRDb25maWcub3B0aW9ucyBhcyBKZXN0RXhlY3V0b3JPcHRpb25zLFxuICAgICAgcHJvamVjdCxcbiAgICAgIHRhcmdldCxcbiAgICApO1xuICB9XG5cbiAgdGhyb3cgbmV3IEVycm9yKFxuICAgIGBVbnN1cHBvcnRlZCBleGVjdXRvciAke3RhcmdldENvbmZpZy5leGVjdXRvcn0uIE9ubHkgQG54L3ZpdGUgYW5kIEBueC9qZXN0IGFyZSBjdXJyZW50bHkgc3VwcG9ydGVkLmAsXG4gICk7XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRDb3ZlcmFnZVBhdGhGb3JWaXRlc3QoXG4gIG9wdGlvbnM6IFZpdGVzdEV4ZWN1dG9yT3B0aW9ucyxcbiAgcHJvamVjdDogUHJvamVjdENvbmZpZ3VyYXRpb24sXG4gIHRhcmdldDogc3RyaW5nLFxuKSB7XG4gIGNvbnN0IHtcbiAgICBkZWZhdWx0OiB7IG5vcm1hbGl6ZVZpdGVDb25maWdGaWxlUGF0aFdpdGhUcmVlIH0sXG4gIH0gPSBhd2FpdCBpbXBvcnQoJ0BueC92aXRlJyk7XG4gIGNvbnN0IGNvbmZpZyA9IG5vcm1hbGl6ZVZpdGVDb25maWdGaWxlUGF0aFdpdGhUcmVlKFxuICAgIC8vIEhBQ0s6IG9ubHkgdHJlZS5leGlzdHMgaXMgY2FsbGVkLCBzbyBpbmplY3RpbmcgZXhpc3RTeW5jIGZyb20gbm9kZTpmcyBpbnN0ZWFkXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9jb25zaXN0ZW50LXR5cGUtYXNzZXJ0aW9uc1xuICAgIHsgZXhpc3RzOiAoYXdhaXQgaW1wb3J0KCdub2RlOmZzJykpLmV4aXN0c1N5bmMgfSBhcyBUcmVlLFxuICAgIHByb2plY3Qucm9vdCxcbiAgICBvcHRpb25zLmNvbmZpZ0ZpbGUsXG4gICk7XG4gIGlmICghY29uZmlnKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYENvdWxkIG5vdCBmaW5kIFZpdGVzdCBjb25maWcgZmlsZSBmb3IgdGFyZ2V0ICR7dGFyZ2V0fSBpbiBwcm9qZWN0ICR7cHJvamVjdC5uYW1lfWAsXG4gICAgKTtcbiAgfVxuXG4gIGNvbnN0IHZpdGVzdENvbmZpZyA9IGF3YWl0IGltcG9ydE1vZHVsZTxWaXRlc3RDb3ZlcmFnZUNvbmZpZz4oe1xuICAgIGZpbGVwYXRoOiBjb25maWcsXG4gICAgZm9ybWF0OiAnZXNtJyxcbiAgfSk7XG5cbiAgY29uc3QgcmVwb3J0c0RpcmVjdG9yeSA9XG4gICAgb3B0aW9ucy5yZXBvcnRzRGlyZWN0b3J5ID8/IHZpdGVzdENvbmZpZy50ZXN0LmNvdmVyYWdlPy5yZXBvcnRzRGlyZWN0b3J5O1xuICBjb25zdCByZXBvcnRlciA9IHZpdGVzdENvbmZpZy50ZXN0LmNvdmVyYWdlPy5yZXBvcnRlcjtcblxuICBpZiAocmVwb3J0c0RpcmVjdG9yeSA9PSBudWxsKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYFZpdGVzdCBjb3ZlcmFnZSBjb25maWd1cmF0aW9uIGF0ICR7Y29uZmlnfSBkb2VzIG5vdCBpbmNsdWRlIGNvdmVyYWdlIHBhdGggZm9yIHRhcmdldCAke3RhcmdldH0gaW4gcHJvamVjdCAke3Byb2plY3QubmFtZX0uIEFkZCB0aGUgcGF0aCB1bmRlciBjb3ZlcmFnZSA+IHJlcG9ydHNEaXJlY3RvcnkuYCxcbiAgICApO1xuICB9XG5cbiAgaWYgKCFyZXBvcnRlcj8uaW5jbHVkZXMoJ2xjb3YnKSkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGBWaXRlc3QgY292ZXJhZ2UgY29uZmlndXJhdGlvbiBhdCAke2NvbmZpZ30gZG9lcyBub3QgaW5jbHVkZSBMQ09WIHJlcG9ydCBmb3JtYXQgZm9yIHRhcmdldCAke3RhcmdldH0gaW4gcHJvamVjdCAke3Byb2plY3QubmFtZX0uIEFkZCAnbGNvdicgZm9ybWF0IHVuZGVyIGNvdmVyYWdlID4gcmVwb3J0ZXIuYCxcbiAgICApO1xuICB9XG5cbiAgaWYgKHBhdGguaXNBYnNvbHV0ZShyZXBvcnRzRGlyZWN0b3J5KSkge1xuICAgIHJldHVybiBwYXRoLmpvaW4ocmVwb3J0c0RpcmVjdG9yeSwgJ2xjb3YuaW5mbycpO1xuICB9XG4gIHJldHVybiB7XG4gICAgcGF0aFRvUHJvamVjdDogcHJvamVjdC5yb290LFxuICAgIHJlc3VsdHNQYXRoOiBwYXRoLmpvaW4ocHJvamVjdC5yb290LCByZXBvcnRzRGlyZWN0b3J5LCAnbGNvdi5pbmZvJyksXG4gIH07XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRDb3ZlcmFnZVBhdGhGb3JKZXN0KFxuICBvcHRpb25zOiBKZXN0RXhlY3V0b3JPcHRpb25zLFxuICBwcm9qZWN0OiBQcm9qZWN0Q29uZmlndXJhdGlvbixcbiAgdGFyZ2V0OiBzdHJpbmcsXG4pIHtcbiAgY29uc3QgeyBqZXN0Q29uZmlnIH0gPSBvcHRpb25zO1xuXG4gIGNvbnN0IHRlc3RDb25maWcgPSBhd2FpdCBpbXBvcnRNb2R1bGU8SmVzdENvdmVyYWdlQ29uZmlnPih7XG4gICAgZmlsZXBhdGg6IGplc3RDb25maWcsXG4gIH0pO1xuICBjb25zdCB7IGNvdmVyYWdlRGlyZWN0b3J5LCBjb3ZlcmFnZVJlcG9ydGVycyB9ID0ge1xuICAgIC4uLnRlc3RDb25maWcsXG4gICAgLi4ub3B0aW9ucyxcbiAgfTtcblxuICBpZiAoY292ZXJhZ2VEaXJlY3RvcnkgPT0gbnVsbCkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGBKZXN0IGNvdmVyYWdlIGNvbmZpZ3VyYXRpb24gYXQgJHtqZXN0Q29uZmlnfSBkb2VzIG5vdCBpbmNsdWRlIGNvdmVyYWdlIHBhdGggZm9yIHRhcmdldCAke3RhcmdldH0gaW4gJHtwcm9qZWN0Lm5hbWV9LiBBZGQgdGhlIHBhdGggdW5kZXIgY292ZXJhZ2VEaXJlY3RvcnkuYCxcbiAgICApO1xuICB9XG5cbiAgaWYgKCFjb3ZlcmFnZVJlcG9ydGVycz8uaW5jbHVkZXMoJ2xjb3YnKSAmJiAhKCdwcmVzZXQnIGluIHRlc3RDb25maWcpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYEplc3QgY292ZXJhZ2UgY29uZmlndXJhdGlvbiBhdCAke2plc3RDb25maWd9IGRvZXMgbm90IGluY2x1ZGUgTENPViByZXBvcnQgZm9ybWF0IGZvciB0YXJnZXQgJHt0YXJnZXR9IGluICR7cHJvamVjdC5uYW1lfS4gQWRkICdsY292JyBmb3JtYXQgdW5kZXIgY292ZXJhZ2VSZXBvcnRlcnMuYCxcbiAgICApO1xuICB9XG5cbiAgaWYgKHBhdGguaXNBYnNvbHV0ZShjb3ZlcmFnZURpcmVjdG9yeSkpIHtcbiAgICByZXR1cm4gcGF0aC5qb2luKGNvdmVyYWdlRGlyZWN0b3J5LCAnbGNvdi5pbmZvJyk7XG4gIH1cbiAgcmV0dXJuIHBhdGguam9pbihwcm9qZWN0LnJvb3QsIGNvdmVyYWdlRGlyZWN0b3J5LCAnbGNvdi5pbmZvJyk7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWIvY29uZmlnLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1kb2MtY292ZXJhZ2Uvc3JjL2xpYlwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWIvY29uZmlnLnRzXCI7aW1wb3J0IHsgeiB9IGZyb20gJ3pvZCc7XG5cbmV4cG9ydCBjb25zdCBkb2NDb3ZlcmFnZVBsdWdpbkNvbmZpZ1NjaGVtYSA9IHoub2JqZWN0KHtcbiAgb25seUF1ZGl0czogei5hcnJheSh6LnN0cmluZygpKS5vcHRpb25hbCgpLFxuICBzb3VyY2VHbG9iOiB6XG4gICAgLmFycmF5KHouc3RyaW5nKCkpXG4gICAgLmRlZmF1bHQoWydzcmMvKiovKi57dHMsdHN4fScsICchKiovKi5zcGVjLnRzJywgJyEqKi8qLnRlc3QudHMnXSksXG59KTtcblxuZXhwb3J0IHR5cGUgRG9jQ292ZXJhZ2VQbHVnaW5Db25maWcgPSB6LmluZmVyPFxuICB0eXBlb2YgZG9jQ292ZXJhZ2VQbHVnaW5Db25maWdTY2hlbWFcbj47IiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWRvYy1jb3ZlcmFnZS9zcmMvbGliL2NvbnN0YW50cy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWRvYy1jb3ZlcmFnZS9zcmMvbGliL2NvbnN0YW50cy50c1wiO2ltcG9ydCB0eXBlIHsgQXVkaXQsIEdyb3VwIH0gZnJvbSBcIkBjb2RlLXB1c2h1cC9tb2RlbHNcIjtcbmltcG9ydCB0eXBlIHsgQXVkaXRTbHVnIH0gZnJvbSBcIi4vbW9kZWxzXCI7XG5cbmV4cG9ydCBjb25zdCBQTFVHSU5fU0xVRyA9ICdkb2MtY292ZXJhZ2UnO1xuXG5leHBvcnQgY29uc3QgQVVESVRTX01BUDogUmVjb3JkPEF1ZGl0U2x1ZywgQXVkaXQ+ID0ge1xuICAgICdjbGFzc2VzLWNvdmVyYWdlJzoge1xuICAgICAgICBzbHVnOiAnY2xhc3Nlcy1jb3ZlcmFnZScsXG4gICAgICAgIHRpdGxlOiAnQ2xhc3NlcyBjb3ZlcmFnZScsXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnQ292ZXJhZ2Ugb2YgY2xhc3NlcycsXG4gICAgfSxcbiAgICAnbWV0aG9kcy1jb3ZlcmFnZSc6IHtcbiAgICAgICAgc2x1ZzogJ21ldGhvZHMtY292ZXJhZ2UnLFxuICAgICAgICB0aXRsZTogJ01ldGhvZHMgY292ZXJhZ2UnLFxuICAgICAgICBkZXNjcmlwdGlvbjogJ0NvdmVyYWdlIG9mIG1ldGhvZHMnLFxuICAgIH0sXG4gICAgJ2Z1bmN0aW9ucy1jb3ZlcmFnZSc6IHtcbiAgICAgICAgc2x1ZzogJ2Z1bmN0aW9ucy1jb3ZlcmFnZScsXG4gICAgICAgIHRpdGxlOiAnRnVuY3Rpb25zIGNvdmVyYWdlJyxcbiAgICAgICAgZGVzY3JpcHRpb246ICdDb3ZlcmFnZSBvZiBmdW5jdGlvbnMnLFxuICAgIH0sXG4gICAgJ2ludGVyZmFjZXMtY292ZXJhZ2UnOiB7XG4gICAgICAgIHNsdWc6ICdpbnRlcmZhY2VzLWNvdmVyYWdlJyxcbiAgICAgICAgdGl0bGU6ICdJbnRlcmZhY2VzIGNvdmVyYWdlJyxcbiAgICAgICAgZGVzY3JpcHRpb246ICdDb3ZlcmFnZSBvZiBpbnRlcmZhY2VzJyxcbiAgICB9LFxuICAgICd2YXJpYWJsZXMtY292ZXJhZ2UnOiB7XG4gICAgICAgIHNsdWc6ICd2YXJpYWJsZXMtY292ZXJhZ2UnLFxuICAgICAgICB0aXRsZTogJ1ZhcmlhYmxlcyBjb3ZlcmFnZScsXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnQ292ZXJhZ2Ugb2YgdmFyaWFibGVzJyxcbiAgICB9LFxuICAgICdwcm9wZXJ0aWVzLWNvdmVyYWdlJzoge1xuICAgICAgICBzbHVnOiAncHJvcGVydGllcy1jb3ZlcmFnZScsXG4gICAgICAgIHRpdGxlOiAnUHJvcGVydGllcyBjb3ZlcmFnZScsXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnQ292ZXJhZ2Ugb2YgcHJvcGVydGllcycsXG4gICAgfSxcbiAgICAndHlwZXMtY292ZXJhZ2UnOiB7XG4gICAgICAgIHNsdWc6ICd0eXBlcy1jb3ZlcmFnZScsXG4gICAgICAgIHRpdGxlOiAnVHlwZXMgY292ZXJhZ2UnLFxuICAgICAgICBkZXNjcmlwdGlvbjogJ0NvdmVyYWdlIG9mIHR5cGVzJyxcbiAgICB9LFxuICAgICdlbnVtcy1jb3ZlcmFnZSc6IHtcbiAgICAgICAgc2x1ZzogJ2VudW1zLWNvdmVyYWdlJyxcbiAgICAgICAgdGl0bGU6ICdFbnVtcyBjb3ZlcmFnZScsXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnQ292ZXJhZ2Ugb2YgZW51bXMnLFxuICAgIH0sXG59IGFzIGNvbnN0O1xuXG5leHBvcnQgY29uc3QgZ3JvdXBzOiBHcm91cFtdID0gW1xuICAgIHtcbiAgICAgICAgc2x1ZzogJ2RvY3VtZW50YXRpb24tY292ZXJhZ2UnLFxuICAgICAgICB0aXRsZTogJ0RvY3VtZW50YXRpb24gY292ZXJhZ2UnLFxuICAgICAgICBkZXNjcmlwdGlvbjogJ0RvY3VtZW50YXRpb24gY292ZXJhZ2UnLFxuICAgICAgICByZWZzOiBPYmplY3Qua2V5cyhBVURJVFNfTUFQKS5tYXAoc2x1ZyA9PiB7XG4gICAgICAgICAgICBzd2l0Y2ggKHNsdWcgYXMgQXVkaXRTbHVnKSB7XG4gICAgICAgICAgICAgICAgY2FzZSAnY2xhc3Nlcy1jb3ZlcmFnZSc6XG4gICAgICAgICAgICAgICAgY2FzZSAnZnVuY3Rpb25zLWNvdmVyYWdlJzpcbiAgICAgICAgICAgICAgICBjYXNlICdtZXRob2RzLWNvdmVyYWdlJzpcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHsgc2x1Zywgd2VpZ2h0OiAyIH1cbiAgICAgICAgICAgICAgICBjYXNlICdpbnRlcmZhY2VzLWNvdmVyYWdlJzpcbiAgICAgICAgICAgICAgICBjYXNlICdwcm9wZXJ0aWVzLWNvdmVyYWdlJzpcbiAgICAgICAgICAgICAgICBjYXNlICd0eXBlcy1jb3ZlcmFnZSc6XG4gICAgICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHsgc2x1Zywgd2VpZ2h0OiAxIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSksXG4gICAgfV07IiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWRvYy1jb3ZlcmFnZS9zcmMvbGliL3J1bm5lci9kb2MtcHJvY2Vzc2VyLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1kb2MtY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWRvYy1jb3ZlcmFnZS9zcmMvbGliL3J1bm5lci9kb2MtcHJvY2Vzc2VyLnRzXCI7aW1wb3J0IHsgQ2xhc3NEZWNsYXJhdGlvbiwgUHJvamVjdCwgU291cmNlRmlsZSB9IGZyb20gJ3RzLW1vcnBoJztcbmltcG9ydCB0eXBlIHsgRG9jQ292ZXJhZ2VQbHVnaW5Db25maWcgfSBmcm9tICcuLi9jb25maWcuanMnO1xuaW1wb3J0IHR5cGUge1xuICBDb3ZlcmFnZVJlc3VsdCxcbiAgQ292ZXJhZ2VUeXBlLFxuICBVbmRvY3VtZW50ZWROb2RlLFxuICBVbnByb2Nlc3NlZENvdmVyYWdlUmVzdWx0XG59IGZyb20gJy4uL21vZGVscy5qcyc7XG5pbXBvcnQgeyBnZXRDb3ZlcmFnZVR5cGVGcm9tS2luZCB9IGZyb20gJy4uL3V0aWxzLmpzJztcbmltcG9ydCB7IGNhbGN1bGF0ZUNvdmVyYWdlLCBjcmVhdGVFbXB0eVVucHJvY2Vzc2VkQ292ZXJhZ2VSZXBvcnQgfSBmcm9tICcuL3V0aWxzLmpzJztcblxuXG4vKiBlc2xpbnQtZGlzYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55ICovXG4vKiBlc2xpbnQtZGlzYWJsZSBmdW5jdGlvbmFsL2ltbXV0YWJsZS1kYXRhICovXG4vKiBlc2xpbnQtZGlzYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbWF4LXBhcmFtcyAqL1xuLyogZXNsaW50LWRpc2FibGUgZnVuY3Rpb25hbC9uby1sZXQgKi9cblxuXG4vKipcbiAqIFByb2Nlc3NlcyBkb2N1bWVudGF0aW9uIGNvdmVyYWdlIGZvciBUeXBlU2NyaXB0IGZpbGVzIGluIHRoZSBzcGVjaWZpZWQgcGF0aFxuICogQHBhcmFtIHRvSW5jbHVkZSAtIFRoZSBmaWxlIHBhdGggcGF0dGVybiB0byBpbmNsdWRlIGZvciBkb2N1bWVudGF0aW9uIGFuYWx5c2lzXG4gKiBAcmV0dXJucyB7Q292ZXJhZ2VSZXN1bHR9IE9iamVjdCBjb250YWluaW5nIGNvdmVyYWdlIHN0YXRpc3RpY3MgYW5kIHVuZG9jdW1lbnRlZCBpdGVtc1xuICovXG5leHBvcnQgZnVuY3Rpb24gcHJvY2Vzc0RvY0NvdmVyYWdlKGNvbmZpZzogRG9jQ292ZXJhZ2VQbHVnaW5Db25maWcpOiBDb3ZlcmFnZVJlc3VsdCB7XG4gIGNvbnN0IHByb2plY3QgPSBuZXcgUHJvamVjdCgpO1xuICBwcm9qZWN0LmFkZFNvdXJjZUZpbGVzQXRQYXRocyhjb25maWcuc291cmNlR2xvYik7XG5cbiAgcmV0dXJuIGdldFVucHJvY2Vzc2VkQ292ZXJhZ2VSZXBvcnQocHJvamVjdC5nZXRTb3VyY2VGaWxlcygpKTtcblxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0VW5wcm9jZXNzZWRDb3ZlcmFnZVJlcG9ydChzb3VyY2VGaWxlczogU291cmNlRmlsZVtdKSB7XG4gIGNvbnN0IHVucHJvY2Vzc2VkQ292ZXJhZ2VSZXBvcnQgPSBzb3VyY2VGaWxlc1xuICAgIC5yZWR1Y2UoKGNvdmVyYWdlUmVwb3J0T2ZBbGxGaWxlcywgc291cmNlRmlsZSkgPT4ge1xuXG4gICAgICAvLyBJbmZvIG9mIHRoZSBmaWxlXG4gICAgICBjb25zdCBmaWxlUGF0aCA9IHNvdXJjZUZpbGUuZ2V0RmlsZVBhdGgoKTtcbiAgICAgIGNvbnN0IGNsYXNzZXMgPSBzb3VyY2VGaWxlLmdldENsYXNzZXMoKTtcblxuICAgICAgLy8gQWxsIG5vZGVzIG9mIHRoZSBmaWxlXG4gICAgICBjb25zdCBhbGxOb2Rlc0Zyb21GaWxlID0gW1xuICAgICAgICAuLi5zb3VyY2VGaWxlLmdldEZ1bmN0aW9ucygpLFxuICAgICAgICAuLi5jbGFzc2VzLFxuICAgICAgICAuLi5nZXRDbGFzc05vZGVzKGNsYXNzZXMpLFxuICAgICAgICAuLi5zb3VyY2VGaWxlLmdldFR5cGVBbGlhc2VzKCksXG4gICAgICAgIC4uLnNvdXJjZUZpbGUuZ2V0RW51bXMoKSxcbiAgICAgICAgLi4uc291cmNlRmlsZS5nZXRJbnRlcmZhY2VzKCksXG4gICAgICAgIC8vIC4uLnNvdXJjZUZpbGUuZ2V0VmFyaWFibGVTdGF0ZW1lbnRzKCkuZmxhdE1hcChzdGF0ZW1lbnQgPT4gc3RhdGVtZW50LmdldERlY2xhcmF0aW9ucygpKVxuICAgICAgXTtcblxuICAgICAgY29uc3QgY292ZXJhZ2VSZXBvcnRPZkN1cnJlbnRGaWxlID0gYWxsTm9kZXNGcm9tRmlsZS5yZWR1Y2UoKGFjYywgbm9kZSkgPT4ge1xuICAgICAgICBjb25zdCBub2RlVHlwZSA9IGdldENvdmVyYWdlVHlwZUZyb21LaW5kKG5vZGUuZ2V0S2luZCgpKTtcbiAgICAgICAgYWNjW25vZGVUeXBlXS5ub2Rlc0NvdW50Kys7XG4gICAgICAgIGlmIChub2RlLmdldEpzRG9jcygpLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgIGFjY1tub2RlVHlwZV0uaXNzdWVzLnB1c2goXG4gICAgICAgICAgICBnZXRVbmRvY3VtZW50ZWROb2RlKGZpbGVQYXRoLCBub2RlVHlwZSwgbm9kZS5nZXROYW1lKCkgfHwgJycsIG5vZGUuZ2V0U3RhcnRMaW5lTnVtYmVyKCkpXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gYWNjO1xuICAgICAgfSwgY3JlYXRlRW1wdHlVbnByb2Nlc3NlZENvdmVyYWdlUmVwb3J0KCkpO1xuXG4gICAgICByZXR1cm4gbWVyZ2VDb3ZlcmFnZVJlc3VsdHMoY292ZXJhZ2VSZXBvcnRPZkFsbEZpbGVzLCBjb3ZlcmFnZVJlcG9ydE9mQ3VycmVudEZpbGUpO1xuICAgIH0sIGNyZWF0ZUVtcHR5VW5wcm9jZXNzZWRDb3ZlcmFnZVJlcG9ydCgpKTtcblxuICByZXR1cm4gY2FsY3VsYXRlQ292ZXJhZ2UodW5wcm9jZXNzZWRDb3ZlcmFnZVJlcG9ydCk7XG5cbn1cblxuZnVuY3Rpb24gbWVyZ2VDb3ZlcmFnZVJlc3VsdHMocmVzdWx0czogVW5wcm9jZXNzZWRDb3ZlcmFnZVJlc3VsdCwgY3VycmVudDogVW5wcm9jZXNzZWRDb3ZlcmFnZVJlc3VsdCkge1xuICByZXR1cm4ge1xuICAgIC4uLk9iamVjdC5mcm9tRW50cmllcyhPYmplY3QuZW50cmllcyhyZXN1bHRzKS5tYXAoKFtrZXksIHZhbHVlXSkgPT4ge1xuICAgICAgY29uc3Qgbm9kZSA9IHZhbHVlIGFzIENvdmVyYWdlUmVzdWx0W0NvdmVyYWdlVHlwZV07XG4gICAgICBjb25zdCB0eXBlID0ga2V5IGFzIENvdmVyYWdlVHlwZTtcbiAgICAgIHJldHVybiBbdHlwZSwge1xuICAgICAgICBub2Rlc0NvdW50OiBub2RlLm5vZGVzQ291bnQgKyBjdXJyZW50W3R5cGVdLm5vZGVzQ291bnQsXG4gICAgICAgIGlzc3VlczogWy4uLm5vZGUuaXNzdWVzLCAuLi5jdXJyZW50W3R5cGVdLmlzc3Vlc10sXG4gICAgICB9XVxuICAgIH0pKVxuICB9IGFzIFVucHJvY2Vzc2VkQ292ZXJhZ2VSZXN1bHQ7XG59XG5cbmZ1bmN0aW9uIGdldENsYXNzTm9kZXMoY2xhc3NOb2RlczogQ2xhc3NEZWNsYXJhdGlvbltdKSB7XG4gIHJldHVybiBjbGFzc05vZGVzLmZsYXRNYXAoY2xhc3NOb2RlID0+IFsuLi5jbGFzc05vZGUuZ2V0TWV0aG9kcygpLCAuLi5jbGFzc05vZGUuZ2V0UHJvcGVydGllcygpXSlcbn1cblxuLyoqXG4gKiBDcmVhdGVzIGFuIHVuZG9jdW1lbnRlZCBpdGVtIGVudHJ5XG4gKiBAcGFyYW0gZmlsZSAtIFRoZSBmaWxlIHBhdGggd2hlcmUgdGhlIGl0ZW0gd2FzIGZvdW5kXG4gKiBAcGFyYW0gdHlwZSAtIFRoZSB0eXBlIG9mIHRoZSB1bmRvY3VtZW50ZWQgaXRlbVxuICogQHBhcmFtIG5hbWUgLSBUaGUgbmFtZSBvZiB0aGUgdW5kb2N1bWVudGVkIGl0ZW1cbiAqIEBwYXJhbSBsaW5lIC0gVGhlIGxpbmUgbnVtYmVyIHdoZXJlIHRoZSBpdGVtIGFwcGVhcnNcbiAqIEByZXR1cm5zIHtVbmRvY3VtZW50ZWROb2RlfSBUaGUgdW5kb2N1bWVudGVkIGl0ZW0gZW50cnlcbiAqL1xuZnVuY3Rpb24gZ2V0VW5kb2N1bWVudGVkTm9kZShmaWxlOiBzdHJpbmcsIHR5cGU6IENvdmVyYWdlVHlwZSwgbmFtZTogc3RyaW5nLCBsaW5lOiBudW1iZXIpOiBVbmRvY3VtZW50ZWROb2RlIHtcbiAgcmV0dXJuIHsgZmlsZSwgdHlwZSwgbmFtZSwgbGluZSB9O1xufVxuXG5cblxuXG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWIvdXRpbHMudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWRvYy1jb3ZlcmFnZS9zcmMvbGliXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1kb2MtY292ZXJhZ2Uvc3JjL2xpYi91dGlscy50c1wiO2ltcG9ydCB0eXBlIHsgQXVkaXQsIEF1ZGl0T3V0cHV0cywgR3JvdXAgfSBmcm9tIFwiQGNvZGUtcHVzaHVwL21vZGVsc1wiO1xuaW1wb3J0IHsgU3ludGF4S2luZCB9IGZyb20gXCJ0eXBlc2NyaXB0XCI7XG5pbXBvcnQgdHlwZSB7IERvY0NvdmVyYWdlUGx1Z2luQ29uZmlnIH0gZnJvbSBcIi4vY29uZmlnXCI7XG5pbXBvcnQgeyBBVURJVFNfTUFQIH0gZnJvbSBcIi4vY29uc3RhbnRzXCI7XG5pbXBvcnQgdHlwZSB7IENvdmVyYWdlUmVzdWx0LCBDb3ZlcmFnZVR5cGUgfSBmcm9tIFwiLi9tb2RlbHNcIjtcblxuLyoqXG4gKiBHZXQgYXVkaXRzIGJhc2VkIG9uIHRoZSBjb25maWd1cmF0aW9uLlxuICogSWYgbm8gYXVkaXRzIGFyZSBzcGVjaWZpZWQsIHJldHVybiBhbGwgYXVkaXRzLlxuICogSWYgYXVkaXRzIGFyZSBzcGVjaWZpZWQsIHJldHVybiBvbmx5IHRoZSBzcGVjaWZpZWQgYXVkaXRzLlxuICogQHBhcmFtIGNvbmZpZyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdC5cbiAqIEByZXR1cm5zIFRoZSBhdWRpdHMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmaWx0ZXJBdWRpdHNCeVBsdWdpbkNvbmZpZyhjb25maWc6IFBpY2s8RG9jQ292ZXJhZ2VQbHVnaW5Db25maWcsICdvbmx5QXVkaXRzJz4pOiBBdWRpdFtdIHtcbiAgICBjb25zdCB7IG9ubHlBdWRpdHMgfSA9IGNvbmZpZ1xuXG4gICAgaWYgKCFvbmx5QXVkaXRzIHx8IG9ubHlBdWRpdHMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgIHJldHVybiBPYmplY3QudmFsdWVzKEFVRElUU19NQVApO1xuICAgIH1cblxuICAgIHJldHVybiBPYmplY3QudmFsdWVzKEFVRElUU19NQVApLmZpbHRlcihhdWRpdCA9PiBvbmx5QXVkaXRzLmluY2x1ZGVzKGF1ZGl0LnNsdWcpKTtcblxufVxuXG4vLyByZXR1cm4gZ3JvdXBzLmZpbHRlcihncm91cCA9PiBncm91cC5yZWZzLnNvbWUocmVmID0+IGF1ZGl0cy5zb21lKGF1ZGl0ID0+IGF1ZGl0LnNsdWcgPT09IHJlZi5zbHVnKSkpO1xuXG4vKipcbiAqIEZpbHRlciBncm91cHMgYnkgdGhlIGF1ZGl0cyB0aGF0IGFyZSBzcGVjaWZpZWQgaW4gdGhlIGNvbmZpZ3VyYXRpb24uXG4gKiBUaGUgZ3JvdXBzIHJlZnMgYXJlIGZpbHRlcmVkIHRvIG9ubHkgaW5jbHVkZSB0aGUgYXVkaXRzIHRoYXQgYXJlIHNwZWNpZmllZCBpbiB0aGUgY29uZmlndXJhdGlvbi5cbiAqIEBwYXJhbSBncm91cHMgLSBUaGUgZ3JvdXBzIHRvIGZpbHRlci5cbiAqIEBwYXJhbSBvcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxuICogQHJldHVybnMgVGhlIGZpbHRlcmVkIGdyb3Vwcy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZpbHRlckdyb3Vwc0J5T25seUF1ZGl0cyhncm91cHM6IEdyb3VwW10sIG9wdGlvbnM6IFBpY2s8RG9jQ292ZXJhZ2VQbHVnaW5Db25maWcsICdvbmx5QXVkaXRzJz4pOiBHcm91cFtdIHtcbiAgICBjb25zdCBhdWRpdHMgPSBmaWx0ZXJBdWRpdHNCeVBsdWdpbkNvbmZpZyhvcHRpb25zKTtcbiAgICByZXR1cm4gZ3JvdXBzXG4gICAgICAgIC5tYXAoZ3JvdXAgPT4gKHtcbiAgICAgICAgICAgIC4uLmdyb3VwLFxuICAgICAgICAgICAgcmVmczogZ3JvdXAucmVmcy5maWx0ZXIocmVmID0+IGF1ZGl0cy5zb21lKGF1ZGl0ID0+IGF1ZGl0LnNsdWcgPT09IHJlZi5zbHVnKSlcbiAgICAgICAgfSkpXG4gICAgICAgIC5maWx0ZXIoZ3JvdXAgPT4gZ3JvdXAucmVmcy5sZW5ndGggPiAwKTs7XG59XG5cblxuLyoqXG4gKiBUcmFuc2Zvcm1zIHRoZSBjb3ZlcmFnZSByZXBvcnQgaW50byBhdWRpdCBvdXRwdXRzLlxuICogQHBhcmFtIGNvdmVyYWdlUmVzdWx0IC0gVGhlIGNvdmVyYWdlIHJlc3VsdCBjb250YWluaW5nIHVuZG9jdW1lbnRlZCBpdGVtcyBhbmQgY292ZXJhZ2Ugc3RhdGlzdGljc1xuICogQHBhcmFtIG9wdGlvbnMgLSBDb25maWd1cmF0aW9uIG9wdGlvbnMgc3BlY2lmeWluZyB3aGljaCBhdWRpdHMgdG8gaW5jbHVkZVxuICogQHJldHVybnMgQXVkaXQgb3V0cHV0cyB3aXRoIGNvdmVyYWdlIHNjb3JlcyBhbmQgZGV0YWlscyBhYm91dCB1bmRvY3VtZW50ZWQgaXRlbXNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHRyYXNmb3JtQ292ZXJhZ2VSZXBvcnRUb0F1ZGl0cyhjb3ZlcmFnZVJlc3VsdDogQ292ZXJhZ2VSZXN1bHQsIG9wdGlvbnM6IFBpY2s8RG9jQ292ZXJhZ2VQbHVnaW5Db25maWcsICdvbmx5QXVkaXRzJz4pOiBBdWRpdE91dHB1dHMge1xuXG4gICAgcmV0dXJuIE9iamVjdC5lbnRyaWVzKGNvdmVyYWdlUmVzdWx0KVxuICAgICAgICAuZmlsdGVyKChbdHlwZV0pID0+ICFvcHRpb25zLm9ubHlBdWRpdHM/Lmxlbmd0aCB8fCBvcHRpb25zLm9ubHlBdWRpdHMuaW5jbHVkZXMoYCR7dHlwZX0tY292ZXJhZ2VgKSlcbiAgICAgICAgLm1hcCgoW3R5cGUsIGl0ZW1zXSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgY292ZXJhZ2VUeXBlID0gdHlwZSBhcyBDb3ZlcmFnZVR5cGU7XG4gICAgICAgICAgICBjb25zdCBjb3ZlcmFnZSA9IGl0ZW1zLmNvdmVyYWdlO1xuXG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgIHNsdWc6IGAke2NvdmVyYWdlVHlwZX0tY292ZXJhZ2VgLFxuICAgICAgICAgICAgICAgIHZhbHVlOiBjb3ZlcmFnZSxcbiAgICAgICAgICAgICAgICBzY29yZTogY292ZXJhZ2UgLyAxMDAsXG4gICAgICAgICAgICAgICAgZGlzcGxheVZhbHVlOiBgJHtjb3ZlcmFnZX0gJWAsXG4gICAgICAgICAgICAgICAgZGV0YWlsczoge1xuICAgICAgICAgICAgICAgICAgICBpc3N1ZXM6IGl0ZW1zLmlzc3Vlcy5tYXAoKHsgZmlsZSwgbGluZSB9KSA9PiAoe1xuICAgICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogJ01pc3NpbmcgZG9jdW1lbnRhdGlvbicsXG4gICAgICAgICAgICAgICAgICAgICAgICBzb3VyY2U6IHsgZmlsZSwgcG9zaXRpb246IHsgc3RhcnRMaW5lOiBsaW5lIH0gfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHNldmVyaXR5OiAnd2FybmluZycsXG4gICAgICAgICAgICAgICAgICAgIH0pKSxcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgfTtcbiAgICAgICAgfSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRDb3ZlcmFnZVR5cGVGcm9tS2luZChraW5kOiBTeW50YXhLaW5kKTogQ292ZXJhZ2VUeXBlIHtcbiAgICBzd2l0Y2ggKGtpbmQpIHtcbiAgICAgICAgY2FzZSBTeW50YXhLaW5kLkNsYXNzRGVjbGFyYXRpb246XG4gICAgICAgICAgICByZXR1cm4gJ2NsYXNzZXMnO1xuICAgICAgICBjYXNlIFN5bnRheEtpbmQuTWV0aG9kRGVjbGFyYXRpb246XG4gICAgICAgICAgICByZXR1cm4gJ21ldGhvZHMnO1xuICAgICAgICBjYXNlIFN5bnRheEtpbmQuRnVuY3Rpb25EZWNsYXJhdGlvbjpcbiAgICAgICAgICAgIHJldHVybiAnZnVuY3Rpb25zJztcbiAgICAgICAgY2FzZSBTeW50YXhLaW5kLkludGVyZmFjZURlY2xhcmF0aW9uOlxuICAgICAgICAgICAgcmV0dXJuICdpbnRlcmZhY2VzJztcbiAgICAgICAgY2FzZSBTeW50YXhLaW5kLkVudW1EZWNsYXJhdGlvbjpcbiAgICAgICAgICAgIHJldHVybiAnZW51bXMnO1xuICAgICAgICBjYXNlIFN5bnRheEtpbmQuVmFyaWFibGVEZWNsYXJhdGlvbjpcbiAgICAgICAgICAgIHJldHVybiAndmFyaWFibGVzJztcbiAgICAgICAgY2FzZSBTeW50YXhLaW5kLlByb3BlcnR5RGVjbGFyYXRpb246XG4gICAgICAgICAgICByZXR1cm4gJ3Byb3BlcnRpZXMnO1xuICAgICAgICBjYXNlIFN5bnRheEtpbmQuVHlwZUFsaWFzRGVjbGFyYXRpb246XG4gICAgICAgICAgICByZXR1cm4gJ3R5cGVzJztcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgVW5zdXBwb3J0ZWQgc3ludGF4IGtpbmQ6ICR7a2luZH1gKTtcbiAgICB9XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWIvcnVubmVyL3V0aWxzLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1kb2MtY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWRvYy1jb3ZlcmFnZS9zcmMvbGliL3J1bm5lci91dGlscy50c1wiO2ltcG9ydCB0eXBlIHsgQ292ZXJhZ2VSZXN1bHQsIENvdmVyYWdlVHlwZSwgVW5wcm9jZXNzZWRDb3ZlcmFnZVJlc3VsdCB9IGZyb20gXCIuLi9tb2RlbHNcIjtcblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUVtcHR5VW5wcm9jZXNzZWRDb3ZlcmFnZVJlcG9ydCgpOiBVbnByb2Nlc3NlZENvdmVyYWdlUmVzdWx0IHtcbiAgICByZXR1cm4ge1xuICAgICAgICBlbnVtczogeyBub2Rlc0NvdW50OiAwLCBpc3N1ZXM6IFtdIH0sXG4gICAgICAgIGludGVyZmFjZXM6IHsgbm9kZXNDb3VudDogMCwgaXNzdWVzOiBbXSB9LFxuICAgICAgICB0eXBlczogeyBub2Rlc0NvdW50OiAwLCBpc3N1ZXM6IFtdIH0sXG4gICAgICAgIGZ1bmN0aW9uczogeyBub2Rlc0NvdW50OiAwLCBpc3N1ZXM6IFtdIH0sXG4gICAgICAgIHZhcmlhYmxlczogeyBub2Rlc0NvdW50OiAwLCBpc3N1ZXM6IFtdIH0sXG4gICAgICAgIGNsYXNzZXM6IHsgbm9kZXNDb3VudDogMCwgaXNzdWVzOiBbXSB9LFxuICAgICAgICBtZXRob2RzOiB7IG5vZGVzQ291bnQ6IDAsIGlzc3VlczogW10gfSxcbiAgICAgICAgcHJvcGVydGllczogeyBub2Rlc0NvdW50OiAwLCBpc3N1ZXM6IFtdIH0sXG4gICAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gY2FsY3VsYXRlQ292ZXJhZ2UocmVzdWx0OiBVbnByb2Nlc3NlZENvdmVyYWdlUmVzdWx0KSB7XG4gICAgcmV0dXJuIE9iamVjdC5mcm9tRW50cmllcyhPYmplY3QuZW50cmllcyhyZXN1bHQpLm1hcCgoW2tleSwgdmFsdWVdKSA9PiB7XG4gICAgICAgIGNvbnN0IHR5cGUgPSBrZXkgYXMgQ292ZXJhZ2VUeXBlO1xuICAgICAgICByZXR1cm4gW3R5cGUsIHtcbiAgICAgICAgICAgIGNvdmVyYWdlOiB2YWx1ZS5ub2Rlc0NvdW50ID09PSAwID8gMTAwIDogKDEgLSB2YWx1ZS5pc3N1ZXMubGVuZ3RoIC8gdmFsdWUubm9kZXNDb3VudCkgKiAxMDAsXG4gICAgICAgICAgICBpc3N1ZXM6IHZhbHVlLmlzc3VlcyxcbiAgICAgICAgICAgIG5vZGVzQ291bnQ6IHZhbHVlLm5vZGVzQ291bnRcbiAgICAgICAgfV1cbiAgICB9KSkgYXMgQ292ZXJhZ2VSZXN1bHQ7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWIvZG9jLWNvdmVyYWdlLXBsdWdpbi50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWRvYy1jb3ZlcmFnZS9zcmMvbGliL2RvYy1jb3ZlcmFnZS1wbHVnaW4udHNcIjtpbXBvcnQgdHlwZSB7IEF1ZGl0T3V0cHV0cywgUGx1Z2luQ29uZmlnLCBSdW5uZXJGdW5jdGlvbiB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHtcbiAgdHlwZSBEb2NDb3ZlcmFnZVBsdWdpbkNvbmZpZyxcbiAgZG9jQ292ZXJhZ2VQbHVnaW5Db25maWdTY2hlbWEsXG59IGZyb20gJy4vY29uZmlnLmpzJztcbmltcG9ydCB7IGdyb3VwcywgUExVR0lOX1NMVUcgfSBmcm9tICcuL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgeyBwcm9jZXNzRG9jQ292ZXJhZ2UgfSBmcm9tICcuL3J1bm5lci9kb2MtcHJvY2Vzc2VyLmpzJztcbmltcG9ydCB7IGZpbHRlckF1ZGl0c0J5UGx1Z2luQ29uZmlnLCBmaWx0ZXJHcm91cHNCeU9ubHlBdWRpdHMsIHRyYXNmb3JtQ292ZXJhZ2VSZXBvcnRUb0F1ZGl0cyB9IGZyb20gJy4vdXRpbHMuanMnO1xuXG5jb25zdCBQTFVHSU5fVElUTEUgPSAnRG9jdW1lbnRhdGlvbiBjb3ZlcmFnZSc7XG5cbmNvbnN0IFBMVUdJTl9ERVNDUklQVElPTiA9ICdPZmZpY2lhbCBDb2RlIFB1c2hVcCBkb2N1bWVudGF0aW9uIGNvdmVyYWdlIHBsdWdpbi4nO1xuXG5jb25zdCBQTFVHSU5fRE9DU19VUkwgPSAnaHR0cHM6Ly93d3cubnBtanMuY29tL3BhY2thZ2UvQGNvZGUtcHVzaHVwL2RvYy1jb3ZlcmFnZS1wbHVnaW4vJztcblxuLyoqXG4gKiBJbnN0YW50aWF0ZXMgQ29kZSBQdXNoVXAgZG9jdW1lbnRhdGlvbiBjb3ZlcmFnZSBwbHVnaW4gZm9yIGNvcmUgY29uZmlnLlxuICpcbiAqIEBleGFtcGxlXG4gKiBpbXBvcnQgZG9jQ292ZXJhZ2VQbHVnaW4gZnJvbSAnQGNvZGUtcHVzaHVwL2RvYy1jb3ZlcmFnZS1wbHVnaW4nXG4gKlxuICogZXhwb3J0IGRlZmF1bHQge1xuICogICAvLyAuLi4gY29yZSBjb25maWcgLi4uXG4gKiAgIHBsdWdpbnM6IFtcbiAqICAgICAvLyAuLi4gb3RoZXIgcGx1Z2lucyAuLi5cbiAqICAgICBhd2FpdCBkb2NDb3ZlcmFnZVBsdWdpbih7XG4gKiAgICAgICBzb3VyY2VHbG9iOiAnc3JjJiM0NzsqKiYjNDc7Ki57dHMsdHN4fSdcbiAqICAgICB9KVxuICogICBdXG4gKiB9XG4gKlxuICogQHJldHVybnMgUGx1Z2luIGNvbmZpZ3VyYXRpb24uXG4gKi9cblxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZG9jQ292ZXJhZ2VQbHVnaW4oY29uZmlnOiBEb2NDb3ZlcmFnZVBsdWdpbkNvbmZpZyk6IFByb21pc2U8UGx1Z2luQ29uZmlnPiB7XG5cblxuICBjb25zdCBkb2NDb3ZlcmFnZUNvbmZpZyA9IGRvY0NvdmVyYWdlUGx1Z2luQ29uZmlnU2NoZW1hLnBhcnNlKGNvbmZpZyk7XG5cblxuICBjb25zdCBncm91cHNDID0gZmlsdGVyR3JvdXBzQnlPbmx5QXVkaXRzKGdyb3VwcywgZG9jQ292ZXJhZ2VDb25maWcpO1xuICBjb25zdCBhdWRpdHNDID0gZmlsdGVyQXVkaXRzQnlQbHVnaW5Db25maWcoZG9jQ292ZXJhZ2VDb25maWcpO1xuXG4gIHJldHVybiB7XG4gICAgc2x1ZzogUExVR0lOX1NMVUcsXG4gICAgdGl0bGU6IFBMVUdJTl9USVRMRSxcbiAgICBpY29uOiAnZm9sZGVyLXNyYycsXG4gICAgZGVzY3JpcHRpb246IFBMVUdJTl9ERVNDUklQVElPTixcbiAgICBkb2NzVXJsOiBQTFVHSU5fRE9DU19VUkwsXG4gICAgZ3JvdXBzOiBmaWx0ZXJHcm91cHNCeU9ubHlBdWRpdHMoZ3JvdXBzLCBkb2NDb3ZlcmFnZUNvbmZpZyksXG4gICAgYXVkaXRzOiBmaWx0ZXJBdWRpdHNCeVBsdWdpbkNvbmZpZyhkb2NDb3ZlcmFnZUNvbmZpZyksXG4gICAgcnVubmVyOiBjcmVhdGVSdW5uZXJGdW5jdGlvbihkb2NDb3ZlcmFnZUNvbmZpZyksXG4gIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVSdW5uZXJGdW5jdGlvbihjb25maWc6IERvY0NvdmVyYWdlUGx1Z2luQ29uZmlnKTogUnVubmVyRnVuY3Rpb24ge1xuICByZXR1cm4gKCk6IEF1ZGl0T3V0cHV0cyA9PiB7XG4gICAgY29uc3QgY292ZXJhZ2VSZXN1bHQgPSBwcm9jZXNzRG9jQ292ZXJhZ2UoY29uZmlnKVxuICAgIHJldHVybiB0cmFzZm9ybUNvdmVyYWdlUmVwb3J0VG9BdWRpdHMoY292ZXJhZ2VSZXN1bHQsIGNvbmZpZyk7XG4gIH07XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9pbmRleC50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyY1wiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9pbmRleC50c1wiO2ltcG9ydCB7IGRvY0NvdmVyYWdlUGx1Z2luIH0gZnJvbSAnLi9saWIvZG9jLWNvdmVyYWdlLXBsdWdpbi5qcyc7XG5cbmV4cG9ydCBkZWZhdWx0IGRvY0NvdmVyYWdlUGx1Z2luO1xuZXhwb3J0IHR5cGUgeyBEb2NDb3ZlcmFnZVBsdWdpbkNvbmZpZyB9IGZyb20gJy4vbGliL2NvbmZpZy5qcyc7XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvZXNsaW50LXBsdWdpbi50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL2VzbGludC1wbHVnaW4udHNcIjtpbXBvcnQgeyBjcmVhdGVSZXF1aXJlIH0gZnJvbSAnbm9kZTptb2R1bGUnO1xuaW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB7IGZpbGVVUkxUb1BhdGggfSBmcm9tICdub2RlOnVybCc7XG5pbXBvcnQgdHlwZSB7IFBsdWdpbkNvbmZpZyB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgdHlwZSBFU0xpbnRQbHVnaW5Db25maWcsIGVzbGludFBsdWdpbkNvbmZpZ1NjaGVtYSB9IGZyb20gJy4vY29uZmlnLmpzJztcbmltcG9ydCB7IGxpc3RBdWRpdHNBbmRHcm91cHMgfSBmcm9tICcuL21ldGEvaW5kZXguanMnO1xuaW1wb3J0IHsgY3JlYXRlUnVubmVyQ29uZmlnIH0gZnJvbSAnLi9ydW5uZXIvaW5kZXguanMnO1xuXG4vKipcbiAqIEluc3RhbnRpYXRlcyBDb2RlIFB1c2hVcCBFU0xpbnQgcGx1Z2luIGZvciB1c2UgaW4gY29yZSBjb25maWcuXG4gKlxuICogQGV4YW1wbGVcbiAqIGltcG9ydCBlc2xpbnRQbHVnaW4gZnJvbSAnQGNvZGUtcHVzaHVwL2VzbGludC1wbHVnaW4nXG4gKlxuICogZXhwb3J0IGRlZmF1bHQge1xuICogICAvLyAuLi4gY29yZSBjb25maWcgLi4uXG4gKiAgIHBsdWdpbnM6IFtcbiAqICAgICAvLyAuLi4gb3RoZXIgcGx1Z2lucyAuLi5cbiAqICAgICBhd2FpdCBlc2xpbnRQbHVnaW4oe1xuICogICAgICAgZXNsaW50cmM6ICcuZXNsaW50cmMuanNvbicsXG4gKiAgICAgICBwYXR0ZXJuczogWydzcmMnLCAndGVzdC8qLnNwZWMuanMnXVxuICogICAgIH0pXG4gKiAgIF1cbiAqIH1cbiAqXG4gKiBAcGFyYW0gY29uZmlnIENvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAqIEByZXR1cm5zIFBsdWdpbiBjb25maWd1cmF0aW9uIGFzIGEgcHJvbWlzZS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGVzbGludFBsdWdpbihcbiAgY29uZmlnOiBFU0xpbnRQbHVnaW5Db25maWcsXG4pOiBQcm9taXNlPFBsdWdpbkNvbmZpZz4ge1xuICBjb25zdCB0YXJnZXRzID0gZXNsaW50UGx1Z2luQ29uZmlnU2NoZW1hLnBhcnNlKGNvbmZpZyk7XG5cbiAgY29uc3QgeyBhdWRpdHMsIGdyb3VwcyB9ID0gYXdhaXQgbGlzdEF1ZGl0c0FuZEdyb3Vwcyh0YXJnZXRzKTtcblxuICBjb25zdCBydW5uZXJTY3JpcHRQYXRoID0gcGF0aC5qb2luKFxuICAgIGZpbGVVUkxUb1BhdGgocGF0aC5kaXJuYW1lKGltcG9ydC5tZXRhLnVybCkpLFxuICAgICcuLicsXG4gICAgJ2Jpbi5qcycsXG4gICk7XG5cbiAgY29uc3QgcGFja2FnZUpzb24gPSBjcmVhdGVSZXF1aXJlKGltcG9ydC5tZXRhLnVybCkoXG4gICAgJy4uLy4uL3BhY2thZ2UuanNvbicsXG4gICkgYXMgdHlwZW9mIGltcG9ydCgnLi4vLi4vcGFja2FnZS5qc29uJyk7XG5cbiAgcmV0dXJuIHtcbiAgICBzbHVnOiAnZXNsaW50JyxcbiAgICB0aXRsZTogJ0VTTGludCcsXG4gICAgaWNvbjogJ2VzbGludCcsXG4gICAgZGVzY3JpcHRpb246ICdPZmZpY2lhbCBDb2RlIFB1c2hVcCBFU0xpbnQgcGx1Z2luJyxcbiAgICBkb2NzVXJsOiAnaHR0cHM6Ly93d3cubnBtanMuY29tL3BhY2thZ2UvQGNvZGUtcHVzaHVwL2VzbGludC1wbHVnaW4nLFxuICAgIHBhY2thZ2VOYW1lOiBwYWNrYWdlSnNvbi5uYW1lLFxuICAgIHZlcnNpb246IHBhY2thZ2VKc29uLnZlcnNpb24sXG5cbiAgICBhdWRpdHMsXG4gICAgZ3JvdXBzLFxuXG4gICAgcnVubmVyOiBhd2FpdCBjcmVhdGVSdW5uZXJDb25maWcocnVubmVyU2NyaXB0UGF0aCwgYXVkaXRzLCB0YXJnZXRzKSxcbiAgfTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9jb25maWcudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9jb25maWcudHNcIjtpbXBvcnQgeyB6IH0gZnJvbSAnem9kJztcbmltcG9ydCB7IHRvQXJyYXkgfSBmcm9tICdAY29kZS1wdXNodXAvdXRpbHMnO1xuXG5jb25zdCBwYXR0ZXJuc1NjaGVtYSA9IHoudW5pb24oW3ouc3RyaW5nKCksIHouYXJyYXkoei5zdHJpbmcoKSkubWluKDEpXSwge1xuICBkZXNjcmlwdGlvbjpcbiAgICAnTGludCB0YXJnZXQgZmlsZXMuIE1heSBjb250YWluIGZpbGUgcGF0aHMsIGRpcmVjdG9yeSBwYXRocyBvciBnbG9iIHBhdHRlcm5zJyxcbn0pO1xuXG5jb25zdCBlc2xpbnRyY1NjaGVtYSA9IHouc3RyaW5nKHsgZGVzY3JpcHRpb246ICdQYXRoIHRvIEVTTGludCBjb25maWcgZmlsZScgfSk7XG5cbmNvbnN0IGVzbGludFRhcmdldE9iamVjdFNjaGVtYSA9IHoub2JqZWN0KHtcbiAgZXNsaW50cmM6IGVzbGludHJjU2NoZW1hLm9wdGlvbmFsKCksXG4gIHBhdHRlcm5zOiBwYXR0ZXJuc1NjaGVtYSxcbn0pO1xudHlwZSBFU0xpbnRUYXJnZXRPYmplY3QgPSB6LmluZmVyPHR5cGVvZiBlc2xpbnRUYXJnZXRPYmplY3RTY2hlbWE+O1xuXG5leHBvcnQgY29uc3QgZXNsaW50VGFyZ2V0U2NoZW1hID0gelxuICAudW5pb24oW3BhdHRlcm5zU2NoZW1hLCBlc2xpbnRUYXJnZXRPYmplY3RTY2hlbWFdKVxuICAudHJhbnNmb3JtKFxuICAgICh0YXJnZXQpOiBFU0xpbnRUYXJnZXRPYmplY3QgPT5cbiAgICAgIHR5cGVvZiB0YXJnZXQgPT09ICdzdHJpbmcnIHx8IEFycmF5LmlzQXJyYXkodGFyZ2V0KVxuICAgICAgICA/IHsgcGF0dGVybnM6IHRhcmdldCB9XG4gICAgICAgIDogdGFyZ2V0LFxuICApO1xuZXhwb3J0IHR5cGUgRVNMaW50VGFyZ2V0ID0gei5pbmZlcjx0eXBlb2YgZXNsaW50VGFyZ2V0U2NoZW1hPjtcblxuZXhwb3J0IGNvbnN0IGVzbGludFBsdWdpbkNvbmZpZ1NjaGVtYSA9IHpcbiAgLnVuaW9uKFtlc2xpbnRUYXJnZXRTY2hlbWEsIHouYXJyYXkoZXNsaW50VGFyZ2V0U2NoZW1hKS5taW4oMSldKVxuICAudHJhbnNmb3JtKHRvQXJyYXkpO1xuZXhwb3J0IHR5cGUgRVNMaW50UGx1Z2luQ29uZmlnID0gei5pbnB1dDx0eXBlb2YgZXNsaW50UGx1Z2luQ29uZmlnU2NoZW1hPjtcblxuZXhwb3J0IHR5cGUgRVNMaW50UGx1Z2luUnVubmVyQ29uZmlnID0ge1xuICB0YXJnZXRzOiBFU0xpbnRUYXJnZXRbXTtcbiAgc2x1Z3M6IHN0cmluZ1tdO1xufTtcbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhL2dyb3Vwcy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvbWV0YVwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvbWV0YS9ncm91cHMudHNcIjtpbXBvcnQgdHlwZSB7IFJ1bGUgfSBmcm9tICdlc2xpbnQnO1xuaW1wb3J0IHR5cGUgeyBHcm91cCwgR3JvdXBSZWYgfSBmcm9tICdAY29kZS1wdXNodXAvbW9kZWxzJztcbmltcG9ydCB7IG9iamVjdFRvS2V5cywgc2x1Z2lmeSB9IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgeyBydWxlVG9TbHVnIH0gZnJvbSAnLi9oYXNoLmpzJztcbmltcG9ydCB7IHR5cGUgUnVsZURhdGEsIHBhcnNlUnVsZUlkIH0gZnJvbSAnLi9wYXJzZS5qcyc7XG5cbnR5cGUgUnVsZVR5cGUgPSBOb25OdWxsYWJsZTxSdWxlLlJ1bGVNZXRhRGF0YVsndHlwZSddPjtcblxuLy8gZG9jcyBvbiBtZXRhLnR5cGU6IGh0dHBzOi8vZXNsaW50Lm9yZy9kb2NzL2xhdGVzdC9leHRlbmQvY3VzdG9tLXJ1bGVzI3J1bGUtc3RydWN0dXJlXG5jb25zdCB0eXBlR3JvdXBzOiBSZWNvcmQ8UnVsZVR5cGUsIE9taXQ8R3JvdXAsICdyZWZzJz4+ID0ge1xuICBwcm9ibGVtOiB7XG4gICAgc2x1ZzogJ3Byb2JsZW1zJyxcbiAgICB0aXRsZTogJ1Byb2JsZW1zJyxcbiAgICBkZXNjcmlwdGlvbjpcbiAgICAgICdDb2RlIHRoYXQgZWl0aGVyIHdpbGwgY2F1c2UgYW4gZXJyb3Igb3IgbWF5IGNhdXNlIGNvbmZ1c2luZyBiZWhhdmlvci4gRGV2ZWxvcGVycyBzaG91bGQgY29uc2lkZXIgdGhpcyBhIGhpZ2ggcHJpb3JpdHkgdG8gcmVzb2x2ZS4nLFxuICB9LFxuICBzdWdnZXN0aW9uOiB7XG4gICAgc2x1ZzogJ3N1Z2dlc3Rpb25zJyxcbiAgICB0aXRsZTogJ1N1Z2dlc3Rpb25zJyxcbiAgICBkZXNjcmlwdGlvbjpcbiAgICAgIFwiU29tZXRoaW5nIHRoYXQgY291bGQgYmUgZG9uZSBpbiBhIGJldHRlciB3YXkgYnV0IG5vIGVycm9ycyB3aWxsIG9jY3VyIGlmIHRoZSBjb2RlIGlzbid0IGNoYW5nZWQuXCIsXG4gIH0sXG4gIGxheW91dDoge1xuICAgIHNsdWc6ICdmb3JtYXR0aW5nJyxcbiAgICB0aXRsZTogJ0Zvcm1hdHRpbmcnLFxuICAgIGRlc2NyaXB0aW9uOlxuICAgICAgJ1ByaW1hcmlseSBhYm91dCB3aGl0ZXNwYWNlLCBzZW1pY29sb25zLCBjb21tYXMsIGFuZCBwYXJlbnRoZXNlcywgYWxsIHRoZSBwYXJ0cyBvZiB0aGUgcHJvZ3JhbSB0aGF0IGRldGVybWluZSBob3cgdGhlIGNvZGUgbG9va3MgcmF0aGVyIHRoYW4gaG93IGl0IGV4ZWN1dGVzLicsXG4gIH0sXG59O1xuXG5leHBvcnQgZnVuY3Rpb24gZ3JvdXBzRnJvbVJ1bGVUeXBlcyhydWxlczogUnVsZURhdGFbXSk6IEdyb3VwW10ge1xuICBjb25zdCBhbGxUeXBlcyA9IG9iamVjdFRvS2V5cyh0eXBlR3JvdXBzKTtcblxuICBjb25zdCBhdWRpdFNsdWdzTWFwID0gcnVsZXMucmVkdWNlPFBhcnRpYWw8UmVjb3JkPFJ1bGVUeXBlLCBzdHJpbmdbXT4+PihcbiAgICAoYWNjLCBydWxlKSA9PlxuICAgICAgcnVsZS5tZXRhLnR5cGUgPT0gbnVsbFxuICAgICAgICA/IGFjY1xuICAgICAgICA6IHtcbiAgICAgICAgICAgIC4uLmFjYyxcbiAgICAgICAgICAgIFtydWxlLm1ldGEudHlwZV06IFtcbiAgICAgICAgICAgICAgLi4uKGFjY1tydWxlLm1ldGEudHlwZV0gPz8gW10pLFxuICAgICAgICAgICAgICBydWxlVG9TbHVnKHJ1bGUpLFxuICAgICAgICAgICAgXSxcbiAgICAgICAgICB9LFxuICAgIHt9LFxuICApO1xuXG4gIHJldHVybiBhbGxUeXBlc1xuICAgIC5tYXAodHlwZSA9PiAoe1xuICAgICAgLi4udHlwZUdyb3Vwc1t0eXBlXSxcbiAgICAgIHJlZnM6XG4gICAgICAgIGF1ZGl0U2x1Z3NNYXBbdHlwZV0/Lm1hcCgoc2x1Zyk6IEdyb3VwUmVmID0+ICh7IHNsdWcsIHdlaWdodDogMSB9KSkgPz9cbiAgICAgICAgW10sXG4gICAgfSkpXG4gICAgLmZpbHRlcihncm91cCA9PiBncm91cC5yZWZzLmxlbmd0aCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBncm91cHNGcm9tUnVsZUNhdGVnb3JpZXMocnVsZXM6IFJ1bGVEYXRhW10pOiBHcm91cFtdIHtcbiAgY29uc3QgY2F0ZWdvcmllc01hcCA9IHJ1bGVzLnJlZHVjZTxSZWNvcmQ8c3RyaW5nLCBSZWNvcmQ8c3RyaW5nLCBzdHJpbmdbXT4+PihcbiAgICAoYWNjLCBydWxlKSA9PiB7XG4gICAgICAvLyBtZXRhLmRvY3MuY2F0ZWdvcnkgc3RpbGwgdXNlZCBieSBzb21lIHBvcHVsYXIgcGx1Z2lucyAoZS5nLiBpbXBvcnQsIHJlYWN0LCBmdW5jdGlvbmFsKVxuICAgICAgY29uc3QgY2F0ZWdvcnkgPSBydWxlLm1ldGEuZG9jcz8uY2F0ZWdvcnk7XG4gICAgICBpZiAoIWNhdGVnb3J5KSB7XG4gICAgICAgIHJldHVybiBhY2M7XG4gICAgICB9XG4gICAgICBjb25zdCB7IHBsdWdpbiA9ICcnIH0gPSBwYXJzZVJ1bGVJZChydWxlLmlkKTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIC4uLmFjYyxcbiAgICAgICAgW3BsdWdpbl06IHtcbiAgICAgICAgICAuLi5hY2NbcGx1Z2luXSxcbiAgICAgICAgICBbY2F0ZWdvcnldOiBbLi4uKGFjY1twbHVnaW5dPy5bY2F0ZWdvcnldID8/IFtdKSwgcnVsZVRvU2x1ZyhydWxlKV0sXG4gICAgICAgIH0sXG4gICAgICB9O1xuICAgIH0sXG4gICAge30sXG4gICk7XG5cbiAgY29uc3QgZ3JvdXBzID0gT2JqZWN0LmVudHJpZXMoY2F0ZWdvcmllc01hcCkuZmxhdE1hcCgoW3BsdWdpbiwgY2F0ZWdvcmllc10pID0+XG4gICAgT2JqZWN0LmVudHJpZXMoY2F0ZWdvcmllcykubWFwKFxuICAgICAgKFtjYXRlZ29yeSwgc2x1Z3NdKTogR3JvdXAgPT4gKHtcbiAgICAgICAgc2x1ZzogYCR7c2x1Z2lmeShwbHVnaW4pfS0ke3NsdWdpZnkoY2F0ZWdvcnkpfWAsXG4gICAgICAgIHRpdGxlOiBgJHtjYXRlZ29yeX0gKCR7cGx1Z2lufSlgLFxuICAgICAgICByZWZzOiBzbHVncy5tYXAoc2x1ZyA9PiAoeyBzbHVnLCB3ZWlnaHQ6IDEgfSkpLFxuICAgICAgfSksXG4gICAgKSxcbiAgKTtcblxuICByZXR1cm4gZ3JvdXBzLnRvU29ydGVkKChhLCBiKSA9PiBhLnNsdWcubG9jYWxlQ29tcGFyZShiLnNsdWcpKTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhL2hhc2gudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGFcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGEvaGFzaC50c1wiO2ltcG9ydCB7IGNyZWF0ZUhhc2ggfSBmcm9tICdub2RlOmNyeXB0byc7XG5pbXBvcnQgeyBzbHVnaWZ5IH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB7IHR5cGUgUnVsZURhdGEsIHJlc29sdmVSdWxlT3B0aW9ucyB9IGZyb20gJy4vcGFyc2UuanMnO1xuXG5leHBvcnQgZnVuY3Rpb24gcnVsZVRvU2x1ZyhydWxlOiBSdWxlRGF0YSk6IHN0cmluZyB7XG4gIHJldHVybiBydWxlSWRUb1NsdWcocnVsZS5pZCwgcmVzb2x2ZVJ1bGVPcHRpb25zKHJ1bGUpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJ1bGVJZFRvU2x1ZyhcbiAgcnVsZUlkOiBzdHJpbmcsXG4gIG9wdGlvbnM6IHVua25vd25bXSB8IHVuZGVmaW5lZCxcbik6IHN0cmluZyB7XG4gIGNvbnN0IHNsdWcgPSBzbHVnaWZ5KHJ1bGVJZCk7XG4gIGlmICghb3B0aW9ucz8ubGVuZ3RoKSB7XG4gICAgcmV0dXJuIHNsdWc7XG4gIH1cbiAgcmV0dXJuIGAke3NsdWd9LSR7anNvbkhhc2gob3B0aW9ucyl9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGpzb25IYXNoKGRhdGE6IHVua25vd24sIGJ5dGVzID0gOCk6IHN0cmluZyB7XG4gIHJldHVybiBjcmVhdGVIYXNoKCdzaGFrZTI1NicsIHsgb3V0cHV0TGVuZ3RoOiBieXRlcyB9KVxuICAgIC51cGRhdGUoSlNPTi5zdHJpbmdpZnkoZGF0YSkgfHwgJ251bGwnKVxuICAgIC5kaWdlc3QoJ2hleCcpO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGEvcGFyc2UudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGFcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGEvcGFyc2UudHNcIjtpbXBvcnQgdHlwZSB7IExpbnRlciwgUnVsZSB9IGZyb20gJ2VzbGludCc7XG5pbXBvcnQgeyB0b0FycmF5IH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcblxuZXhwb3J0IHR5cGUgUnVsZURhdGEgPSB7XG4gIGlkOiBzdHJpbmc7XG4gIG1ldGE6IFJ1bGUuUnVsZU1ldGFEYXRhO1xuICBvcHRpb25zOiB1bmtub3duW10gfCB1bmRlZmluZWQ7XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VSdWxlSWQocnVsZUlkOiBzdHJpbmcpOiB7IHBsdWdpbj86IHN0cmluZzsgbmFtZTogc3RyaW5nIH0ge1xuICBjb25zdCBpID0gcnVsZUlkLnN0YXJ0c1dpdGgoJ0AnKVxuICAgID8gcnVsZUlkLmxhc3RJbmRleE9mKCcvJylcbiAgICA6IHJ1bGVJZC5pbmRleE9mKCcvJyk7XG4gIGlmIChpID09PSAtMSkge1xuICAgIHJldHVybiB7IG5hbWU6IHJ1bGVJZCB9O1xuICB9XG4gIHJldHVybiB7XG4gICAgcGx1Z2luOiBydWxlSWQuc2xpY2UoMCwgaSksXG4gICAgbmFtZTogcnVsZUlkLnNsaWNlKGkgKyAxKSxcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGlzUnVsZU9mZihlbnRyeTogTGludGVyLlJ1bGVFbnRyeTx1bmtub3duW10+KTogYm9vbGVhbiB7XG4gIGNvbnN0IGxldmVsID0gQXJyYXkuaXNBcnJheShlbnRyeSkgPyBlbnRyeVswXSA6IGVudHJ5O1xuXG4gIHN3aXRjaCAobGV2ZWwpIHtcbiAgICBjYXNlIDA6XG4gICAgY2FzZSAnb2ZmJzpcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIGNhc2UgMTpcbiAgICBjYXNlIDI6XG4gICAgY2FzZSAnd2Fybic6XG4gICAgY2FzZSAnZXJyb3InOlxuICAgICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBvcHRpb25zRnJvbVJ1bGVFbnRyeShcbiAgZW50cnk6IExpbnRlci5SdWxlRW50cnk8dW5rbm93bltdPixcbik6IHVua25vd25bXSB7XG4gIHJldHVybiB0b0FycmF5KGVudHJ5KS5zbGljZSgxKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVSdWxlT3B0aW9ucyhydWxlOiBSdWxlRGF0YSk6IHVua25vd25bXSB8IHVuZGVmaW5lZCB7XG4gIGlmIChydWxlLm9wdGlvbnM/Lmxlbmd0aCkge1xuICAgIHJldHVybiBydWxlLm9wdGlvbnM7XG4gIH1cbiAgcmV0dXJuIHJ1bGUubWV0YS5kZWZhdWx0T3B0aW9ucztcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhL3ZlcnNpb25zL2ZsYXQudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGEvdmVyc2lvbnNcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGEvdmVyc2lvbnMvZmxhdC50c1wiO2ltcG9ydCB0eXBlIHsgTGludGVyLCBSdWxlIH0gZnJvbSAnZXNsaW50JztcbmltcG9ydCB7IGJ1aWx0aW5SdWxlcyB9IGZyb20gJ2VzbGludC91c2UtYXQteW91ci1vd24tcmlzayc7XG5pbXBvcnQgcGF0aCBmcm9tICdub2RlOnBhdGgnO1xuaW1wb3J0IHsgcGF0aFRvRmlsZVVSTCB9IGZyb20gJ25vZGU6dXJsJztcbmltcG9ydCB7IGV4aXN0cywgZmluZE5lYXJlc3RGaWxlLCB0b0FycmF5LCB1aSB9IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgdHlwZSB7IEVTTGludFRhcmdldCB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XG5pbXBvcnQgeyBqc29uSGFzaCB9IGZyb20gJy4uL2hhc2guanMnO1xuaW1wb3J0IHtcbiAgdHlwZSBSdWxlRGF0YSxcbiAgaXNSdWxlT2ZmLFxuICBvcHRpb25zRnJvbVJ1bGVFbnRyeSxcbiAgcGFyc2VSdWxlSWQsXG59IGZyb20gJy4uL3BhcnNlLmpzJztcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvYWRSdWxlc0ZvckZsYXRDb25maWcoe1xuICBlc2xpbnRyYyxcbn06IFBpY2s8RVNMaW50VGFyZ2V0LCAnZXNsaW50cmMnPik6IFByb21pc2U8UnVsZURhdGFbXT4ge1xuICBjb25zdCBjb25maWcgPSBlc2xpbnRyY1xuICAgID8gYXdhaXQgbG9hZENvbmZpZ0J5UGF0aChlc2xpbnRyYylcbiAgICA6IGF3YWl0IGxvYWRDb25maWdCeURlZmF1bHRMb2NhdGlvbigpO1xuICBjb25zdCBjb25maWdzID0gdG9BcnJheShjb25maWcpO1xuXG4gIGNvbnN0IHJ1bGVzID0gZmluZEVuYWJsZWRSdWxlc1dpdGhPcHRpb25zKGNvbmZpZ3MpO1xuICByZXR1cm4gcnVsZXNcbiAgICAubWFwKHJ1bGUgPT4ge1xuICAgICAgY29uc3QgbWV0YSA9IGZpbmRSdWxlTWV0YShydWxlLmlkLCBjb25maWdzKTtcbiAgICAgIGlmICghbWV0YSkge1xuICAgICAgICB1aSgpLmxvZ2dlci53YXJuaW5nKGBDYW5ub3QgZmluZCBtZXRhZGF0YSBmb3IgcnVsZSAke3J1bGUuaWR9YCk7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHsgLi4ucnVsZSwgbWV0YSB9O1xuICAgIH0pXG4gICAgLmZpbHRlcihleGlzdHMpO1xufVxuXG50eXBlIEZsYXRDb25maWcgPSBMaW50ZXIuQ29uZmlnIHwgTGludGVyLkNvbmZpZ1tdO1xuXG5hc3luYyBmdW5jdGlvbiBsb2FkQ29uZmlnQnlEZWZhdWx0TG9jYXRpb24oKTogUHJvbWlzZTxGbGF0Q29uZmlnPiB7XG4gIGNvbnN0IGZsYXRDb25maWdGaWxlTmFtZXMgPSBbXG4gICAgJ2VzbGludC5jb25maWcuanMnLFxuICAgICdlc2xpbnQuY29uZmlnLm1qcycsXG4gICAgJ2VzbGludC5jb25maWcuY2pzJyxcbiAgXTtcbiAgY29uc3QgY29uZmlnUGF0aCA9IGF3YWl0IGZpbmROZWFyZXN0RmlsZShmbGF0Q29uZmlnRmlsZU5hbWVzKTtcbiAgaWYgKGNvbmZpZ1BhdGgpIHtcbiAgICByZXR1cm4gbG9hZENvbmZpZ0J5UGF0aChjb25maWdQYXRoKTtcbiAgfVxuICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgW1xuICAgICAgYEVTTGludCBjb25maWcgZmlsZSBub3QgZm91bmQgLSBleHBlY3RlZCAke2ZsYXRDb25maWdGaWxlTmFtZXMuam9pbignLycpfSBpbiAke3Byb2Nlc3MuY3dkKCl9IG9yIHNvbWUgcGFyZW50IGRpcmVjdG9yeWAsXG4gICAgICAnSWYgeW91ciBFU0xpbnQgY29uZmlnIGlzIGluIGEgbm9uLXN0YW5kYXJkIGxvY2F0aW9uLCB1c2UgdGhlIGBlc2xpbnRyY2AgcGFyYW1ldGVyIHRvIHNwZWNpZnkgdGhlIHBhdGguJyxcbiAgICBdLmpvaW4oJ1xcbicpLFxuICApO1xufVxuXG5hc3luYyBmdW5jdGlvbiBsb2FkQ29uZmlnQnlQYXRoKGNvbmZpZ1BhdGg6IHN0cmluZyk6IFByb21pc2U8RmxhdENvbmZpZz4ge1xuICBjb25zdCBhYnNvbHV0ZVBhdGggPSBwYXRoLmlzQWJzb2x1dGUoY29uZmlnUGF0aClcbiAgICA/IGNvbmZpZ1BhdGhcbiAgICA6IHBhdGguam9pbihwcm9jZXNzLmN3ZCgpLCBjb25maWdQYXRoKTtcbiAgY29uc3QgdXJsID0gcGF0aFRvRmlsZVVSTChhYnNvbHV0ZVBhdGgpLnRvU3RyaW5nKCk7XG4gIGNvbnN0IG1vZCA9IChhd2FpdCBpbXBvcnQodXJsKSkgYXMgRmxhdENvbmZpZyB8IHsgZGVmYXVsdDogRmxhdENvbmZpZyB9O1xuICByZXR1cm4gJ2RlZmF1bHQnIGluIG1vZCA/IG1vZC5kZWZhdWx0IDogbW9kO1xufVxuXG5mdW5jdGlvbiBmaW5kRW5hYmxlZFJ1bGVzV2l0aE9wdGlvbnMoXG4gIGNvbmZpZ3M6IExpbnRlci5Db25maWdbXSxcbik6IE9taXQ8UnVsZURhdGEsICdtZXRhJz5bXSB7XG4gIGNvbnN0IGVuYWJsZWRSdWxlcyA9IGNvbmZpZ3NcbiAgICAuZmxhdE1hcCgoeyBydWxlcyB9KSA9PiBPYmplY3QuZW50cmllcyhydWxlcyA/PyB7fSkpXG4gICAgLmZpbHRlcigoWywgZW50cnldKSA9PiBlbnRyeSAhPSBudWxsICYmICFpc1J1bGVPZmYoZW50cnkpKVxuICAgIC5tYXAoKFtpZCwgZW50cnldKSA9PiAoe1xuICAgICAgaWQsXG4gICAgICBvcHRpb25zOiBlbnRyeSA/IG9wdGlvbnNGcm9tUnVsZUVudHJ5KGVudHJ5KSA6IFtdLFxuICAgIH0pKTtcbiAgY29uc3QgdW5pcXVlUnVsZXNNYXAgPSBuZXcgTWFwKFxuICAgIGVuYWJsZWRSdWxlcy5tYXAoKHsgaWQsIG9wdGlvbnMgfSkgPT4gW1xuICAgICAgYCR7aWR9Ojoke2pzb25IYXNoKG9wdGlvbnMpfWAsXG4gICAgICB7IGlkLCBvcHRpb25zIH0sXG4gICAgXSksXG4gICk7XG4gIHJldHVybiBbLi4udW5pcXVlUnVsZXNNYXAudmFsdWVzKCldO1xufVxuXG5mdW5jdGlvbiBmaW5kUnVsZU1ldGEoXG4gIHJ1bGVJZDogc3RyaW5nLFxuICBjb25maWdzOiBMaW50ZXIuQ29uZmlnW10sXG4pOiBSdWxlLlJ1bGVNZXRhRGF0YSB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IHsgcGx1Z2luLCBuYW1lIH0gPSBwYXJzZVJ1bGVJZChydWxlSWQpO1xuICBpZiAoIXBsdWdpbikge1xuICAgIHJldHVybiBmaW5kQnVpbHRpblJ1bGVNZXRhKG5hbWUpO1xuICB9XG4gIHJldHVybiBmaW5kUGx1Z2luUnVsZU1ldGEocGx1Z2luLCBuYW1lLCBjb25maWdzKTtcbn1cblxuZnVuY3Rpb24gZmluZEJ1aWx0aW5SdWxlTWV0YShuYW1lOiBzdHJpbmcpOiBSdWxlLlJ1bGVNZXRhRGF0YSB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IHJ1bGUgPSBidWlsdGluUnVsZXMuZ2V0KG5hbWUpO1xuICByZXR1cm4gcnVsZT8ubWV0YTtcbn1cblxuZnVuY3Rpb24gZmluZFBsdWdpblJ1bGVNZXRhKFxuICBwbHVnaW46IHN0cmluZyxcbiAgbmFtZTogc3RyaW5nLFxuICBjb25maWdzOiBMaW50ZXIuQ29uZmlnW10sXG4pOiBSdWxlLlJ1bGVNZXRhRGF0YSB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IGNvbmZpZyA9IGNvbmZpZ3MuZmluZCgoeyBwbHVnaW5zID0ge30gfSkgPT4gcGx1Z2luIGluIHBsdWdpbnMpO1xuICBjb25zdCBydWxlID0gY29uZmlnPy5wbHVnaW5zPy5bcGx1Z2luXT8ucnVsZXM/LltuYW1lXTtcblxuICBpZiAodHlwZW9mIHJ1bGUgPT09ICdmdW5jdGlvbicpIHtcbiAgICB1aSgpLmxvZ2dlci53YXJuaW5nKFxuICAgICAgYENhbm5vdCBwYXJzZSBtZXRhZGF0YSBmb3IgcnVsZSAke3BsdWdpbn0vJHtuYW1lfSwgcGx1Z2luIHJlZ2lzdGVycyBpdCBhcyBhIGZ1bmN0aW9uYCxcbiAgICApO1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cblxuICByZXR1cm4gcnVsZT8ubWV0YTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhL3ZlcnNpb25zL2xlZ2FjeS50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvbWV0YS92ZXJzaW9uc1wiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvbWV0YS92ZXJzaW9ucy9sZWdhY3kudHNcIjtpbXBvcnQgdHlwZSB7IEVTTGludCwgTGludGVyIH0gZnJvbSAnZXNsaW50JztcbmltcG9ydCB7IGRpc3RpbmN0LCBleGlzdHMsIHRvQXJyYXksIHVpIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB0eXBlIHsgRVNMaW50VGFyZ2V0IH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcbmltcG9ydCB7IHNldHVwRVNMaW50IH0gZnJvbSAnLi4vLi4vc2V0dXAuanMnO1xuaW1wb3J0IHsgdHlwZSBSdWxlRGF0YSwgaXNSdWxlT2ZmLCBvcHRpb25zRnJvbVJ1bGVFbnRyeSB9IGZyb20gJy4uL3BhcnNlLmpzJztcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvYWRSdWxlc0ZvckxlZ2FjeUNvbmZpZyh7XG4gIGVzbGludHJjLFxuICBwYXR0ZXJucyxcbn06IEVTTGludFRhcmdldCk6IFByb21pc2U8UnVsZURhdGFbXT4ge1xuICBjb25zdCBlc2xpbnQgPSBhd2FpdCBzZXR1cEVTTGludChlc2xpbnRyYyk7XG5cbiAgY29uc3QgY29uZmlncyA9IGF3YWl0IHRvQXJyYXkocGF0dGVybnMpLnJlZHVjZShcbiAgICBhc3luYyAoYWNjLCBwYXR0ZXJuKSA9PiBbXG4gICAgICAuLi4oYXdhaXQgYWNjKSxcbiAgICAgIChhd2FpdCBlc2xpbnQuY2FsY3VsYXRlQ29uZmlnRm9yRmlsZShwYXR0ZXJuKSkgYXMgTGludGVyLkxlZ2FjeUNvbmZpZyxcbiAgICBdLFxuICAgIFByb21pc2UucmVzb2x2ZTxMaW50ZXIuTGVnYWN5Q29uZmlnW10+KFtdKSxcbiAgKTtcblxuICBjb25zdCBydWxlc0lkcyA9IGRpc3RpbmN0KFxuICAgIGNvbmZpZ3MuZmxhdE1hcChjb25maWcgPT4gT2JqZWN0LmtleXMoY29uZmlnLnJ1bGVzID8/IHt9KSksXG4gICk7XG4gIGNvbnN0IHJ1bGVzTWV0YSA9IGVzbGludC5nZXRSdWxlc01ldGFGb3JSZXN1bHRzKFtcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L2NvbnNpc3RlbnQtdHlwZS1hc3NlcnRpb25zXG4gICAge1xuICAgICAgbWVzc2FnZXM6IHJ1bGVzSWRzLm1hcChydWxlSWQgPT4gKHsgcnVsZUlkIH0pKSxcbiAgICAgIHN1cHByZXNzZWRNZXNzYWdlczogW10gYXMgTGludGVyLlN1cHByZXNzZWRMaW50TWVzc2FnZVtdLFxuICAgIH0gYXMgRVNMaW50LkxpbnRSZXN1bHQsXG4gIF0pO1xuXG4gIHJldHVybiBjb25maWdzXG4gICAgLmZsYXRNYXAoY29uZmlnID0+IE9iamVjdC5lbnRyaWVzKGNvbmZpZy5ydWxlcyA/PyB7fSkpXG4gICAgLm1hcCgoW2lkLCBlbnRyeV0pOiBSdWxlRGF0YSB8IG51bGwgPT4ge1xuICAgICAgaWYgKGVudHJ5ID09IG51bGwgfHwgaXNSdWxlT2ZmKGVudHJ5KSkge1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHJ1bGVNZXRhID0gcnVsZXNNZXRhW2lkXTtcbiAgICAgIGlmICghcnVsZU1ldGEpIHtcbiAgICAgICAgdWkoKS5sb2dnZXIud2FybmluZyhgTWV0YWRhdGEgbm90IGZvdW5kIGZvciBFU0xpbnQgcnVsZSAke2lkfWApO1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH1cbiAgICAgIC8vIGlnbm9yaW5nIG1ldGEuZGVmYXVsdE9wdGlvbnMgdG8gbWF0Y2ggbGVnYWN5IGNvbmZpZyBoYW5kbGluZyBpbiBjYWxjdWxhdGVDb25maWdGb3JGaWxlXG4gICAgICBjb25zdCB7IGRlZmF1bHRPcHRpb25zOiBfLCAuLi5tZXRhIH0gPSBydWxlTWV0YTtcbiAgICAgIGNvbnN0IG9wdGlvbnMgPSBvcHRpb25zRnJvbVJ1bGVFbnRyeShlbnRyeSk7XG4gICAgICByZXR1cm4geyBpZCwgbWV0YSwgb3B0aW9ucyB9O1xuICAgIH0pXG4gICAgLmZpbHRlcihleGlzdHMpO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL3NldHVwLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYlwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvc2V0dXAudHNcIjtpbXBvcnQgeyBFU0xpbnQgfSBmcm9tICdlc2xpbnQnO1xuaW1wb3J0IHR5cGUgeyBFU0xpbnRUYXJnZXQgfSBmcm9tICcuL2NvbmZpZy5qcyc7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzZXR1cEVTTGludChlc2xpbnRyYzogRVNMaW50VGFyZ2V0Wydlc2xpbnRyYyddKSB7XG4gIGNvbnN0IGVzbGludENvbnN0cnVjdG9yID0gYXdhaXQgbG9hZEVTTGludCgpO1xuICByZXR1cm4gbmV3IGVzbGludENvbnN0cnVjdG9yKHtcbiAgICBvdmVycmlkZUNvbmZpZ0ZpbGU6IGVzbGludHJjLFxuICAgIGVycm9yT25Vbm1hdGNoZWRQYXR0ZXJuOiBmYWxzZSxcbiAgfSk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGxvYWRFU0xpbnQoKSB7XG4gIGNvbnN0IGVzbGludCA9IGF3YWl0IGltcG9ydCgnZXNsaW50Jyk7XG4gIC8vIGxvYWRFU0xpbnQgYWRkZWQgdG8gcHVibGljIEFQSSBpbiB2OSwgc2VsZWN0cyBFU0xpbnQgb3IgTGVnYWN5RVNMaW50IGJhc2VkIG9uIGVudmlyb25tZW50XG4gIGlmICgnbG9hZEVTTGludCcgaW4gZXNsaW50ICYmIHR5cGVvZiBlc2xpbnQubG9hZEVTTGludCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgIHJldHVybiAoYXdhaXQgZXNsaW50LmxvYWRFU0xpbnQoKSkgYXMgdHlwZW9mIEVTTGludDtcbiAgfVxuICByZXR1cm4gRVNMaW50O1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGEvdmVyc2lvbnMvZGV0ZWN0LnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhL3ZlcnNpb25zXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhL3ZlcnNpb25zL2RldGVjdC50c1wiO2ltcG9ydCB7IEVTTGludCB9IGZyb20gJ2VzbGludCc7XG5pbXBvcnQgeyBmaWxlRXhpc3RzIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB0eXBlIHsgQ29uZmlnRm9ybWF0IH0gZnJvbSAnLi9mb3JtYXRzLmpzJztcblxuLy8gcmVsZXZhbnQgRVNMaW50IGRvY3M6XG4vLyAtIGh0dHBzOi8vZXNsaW50Lm9yZy9kb2NzL2xhdGVzdC91c2UvY29uZmlndXJlL2NvbmZpZ3VyYXRpb24tZmlsZXNcbi8vIC0gaHR0cHM6Ly9lc2xpbnQub3JnL2RvY3MvbGF0ZXN0L3VzZS9jb25maWd1cmUvY29uZmlndXJhdGlvbi1maWxlcy1kZXByZWNhdGVkXG4vLyAtIGh0dHBzOi8vZXNsaW50Lm9yZy9kb2NzL3Y4LngvdXNlL2NvbmZpZ3VyZS9jb25maWd1cmF0aW9uLWZpbGVzLW5ld1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZGV0ZWN0Q29uZmlnVmVyc2lvbigpOiBQcm9taXNlPENvbmZpZ0Zvcm1hdD4ge1xuICBpZiAocHJvY2Vzcy5lbnZbJ0VTTElOVF9VU0VfRkxBVF9DT05GSUcnXSA9PT0gJ3RydWUnKSB7XG4gICAgcmV0dXJuICdmbGF0JztcbiAgfVxuICBpZiAocHJvY2Vzcy5lbnZbJ0VTTElOVF9VU0VfRkxBVF9DT05GSUcnXSA9PT0gJ2ZhbHNlJykge1xuICAgIHJldHVybiAnbGVnYWN5JztcbiAgfVxuICBpZiAoRVNMaW50LnZlcnNpb24uc3RhcnRzV2l0aCgnOC4nKSkge1xuICAgIGlmIChhd2FpdCBmaWxlRXhpc3RzKCdlc2xpbnQuY29uZmlnLmpzJykpIHtcbiAgICAgIHJldHVybiAnZmxhdCc7XG4gICAgfVxuICAgIHJldHVybiAnbGVnYWN5JztcbiAgfVxuICByZXR1cm4gJ2ZsYXQnO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGEvdHJhbnNmb3JtLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhL3RyYW5zZm9ybS50c1wiO2ltcG9ydCB0eXBlIHsgQXVkaXQgfSBmcm9tICdAY29kZS1wdXNodXAvbW9kZWxzJztcbmltcG9ydCB7IHRydW5jYXRlRGVzY3JpcHRpb24sIHRydW5jYXRlVGl0bGUgfSBmcm9tICdAY29kZS1wdXNodXAvdXRpbHMnO1xuaW1wb3J0IHsgcnVsZVRvU2x1ZyB9IGZyb20gJy4vaGFzaC5qcyc7XG5pbXBvcnQgdHlwZSB7IFJ1bGVEYXRhIH0gZnJvbSAnLi9wYXJzZS5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBydWxlVG9BdWRpdChydWxlOiBSdWxlRGF0YSk6IEF1ZGl0IHtcbiAgY29uc3QgbmFtZSA9IHJ1bGUuaWQuc3BsaXQoJy8nKS5hdCgtMSkgPz8gcnVsZS5pZDtcbiAgY29uc3QgcGx1Z2luID1cbiAgICBuYW1lID09PSBydWxlLmlkID8gbnVsbCA6IHJ1bGUuaWQuc2xpY2UoMCwgcnVsZS5pZC5sYXN0SW5kZXhPZignLycpKTtcbiAgY29uc3QgcGx1Z2luQ29udGV4dCA9IHBsdWdpbiA/IGAsIGZyb20gXyR7cGx1Z2lufV8gcGx1Z2luYCA6ICcnO1xuXG4gIGNvbnN0IGxpbmVzOiBzdHJpbmdbXSA9IFtcbiAgICBgRVNMaW50IHJ1bGUgKioke25hbWV9Kioke3BsdWdpbkNvbnRleHR9LmAsXG4gICAgLi4uKHJ1bGUub3B0aW9ucz8ubGVuZ3RoID8gWydDdXN0b20gb3B0aW9uczonXSA6IFtdKSxcbiAgICAuLi4ocnVsZS5vcHRpb25zPy5tYXAob3B0aW9uID0+XG4gICAgICBbJ2BgYGpzb24nLCBKU09OLnN0cmluZ2lmeShvcHRpb24sIG51bGwsIDIpLCAnYGBgJ10uam9pbignXFxuJyksXG4gICAgKSA/PyBbXSksXG4gIF07XG5cbiAgcmV0dXJuIHtcbiAgICBzbHVnOiBydWxlVG9TbHVnKHJ1bGUpLFxuICAgIHRpdGxlOiB0cnVuY2F0ZVRpdGxlKHJ1bGUubWV0YS5kb2NzPy5kZXNjcmlwdGlvbiA/PyBuYW1lKSxcbiAgICBkZXNjcmlwdGlvbjogdHJ1bmNhdGVEZXNjcmlwdGlvbihsaW5lcy5qb2luKCdcXG5cXG4nKSksXG4gICAgLi4uKHJ1bGUubWV0YS5kb2NzPy51cmwgJiYge1xuICAgICAgZG9jc1VybDogcnVsZS5tZXRhLmRvY3MudXJsLFxuICAgIH0pLFxuICB9O1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL3J1bm5lci9pbmRleC50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvcnVubmVyXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9ydW5uZXIvaW5kZXgudHNcIjtpbXBvcnQgeyB3cml0ZUZpbGUgfSBmcm9tICdub2RlOmZzL3Byb21pc2VzJztcbmltcG9ydCBwYXRoIGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0LCBBdWRpdE91dHB1dCwgUnVubmVyQ29uZmlnIH0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5pbXBvcnQge1xuICBlbnN1cmVEaXJlY3RvcnlFeGlzdHMsXG4gIGZpbGVQYXRoVG9DbGlBcmcsXG4gIHBsdWdpbldvcmtEaXIsXG4gIHJlYWRKc29uRmlsZSxcbn0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB0eXBlIHsgRVNMaW50UGx1Z2luUnVubmVyQ29uZmlnLCBFU0xpbnRUYXJnZXQgfSBmcm9tICcuLi9jb25maWcuanMnO1xuaW1wb3J0IHsgbGludCB9IGZyb20gJy4vbGludC5qcyc7XG5pbXBvcnQgeyBsaW50UmVzdWx0c1RvQXVkaXRzLCBtZXJnZUxpbnRlck91dHB1dHMgfSBmcm9tICcuL3RyYW5zZm9ybS5qcyc7XG5pbXBvcnQgdHlwZSB7IExpbnRlck91dHB1dCB9IGZyb20gJy4vdHlwZXMuanMnO1xuXG5leHBvcnQgY29uc3QgV09SS0RJUiA9IHBsdWdpbldvcmtEaXIoJ2VzbGludCcpO1xuZXhwb3J0IGNvbnN0IFJVTk5FUl9PVVRQVVRfUEFUSCA9IHBhdGguam9pbihXT1JLRElSLCAncnVubmVyLW91dHB1dC5qc29uJyk7XG5leHBvcnQgY29uc3QgUExVR0lOX0NPTkZJR19QQVRIID0gcGF0aC5qb2luKFxuICBwcm9jZXNzLmN3ZCgpLFxuICBXT1JLRElSLFxuICAncGx1Z2luLWNvbmZpZy5qc29uJyxcbik7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBleGVjdXRlUnVubmVyKCk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCB7IHNsdWdzLCB0YXJnZXRzIH0gPVxuICAgIGF3YWl0IHJlYWRKc29uRmlsZTxFU0xpbnRQbHVnaW5SdW5uZXJDb25maWc+KFBMVUdJTl9DT05GSUdfUEFUSCk7XG5cbiAgY29uc3QgbGludGVyT3V0cHV0cyA9IGF3YWl0IHRhcmdldHMucmVkdWNlKFxuICAgIGFzeW5jIChhY2MsIHRhcmdldCkgPT4gWy4uLihhd2FpdCBhY2MpLCBhd2FpdCBsaW50KHRhcmdldCldLFxuICAgIFByb21pc2UucmVzb2x2ZTxMaW50ZXJPdXRwdXRbXT4oW10pLFxuICApO1xuICBjb25zdCBsaW50UmVzdWx0cyA9IG1lcmdlTGludGVyT3V0cHV0cyhsaW50ZXJPdXRwdXRzKTtcbiAgY29uc3QgZmFpbGVkQXVkaXRzID0gbGludFJlc3VsdHNUb0F1ZGl0cyhsaW50UmVzdWx0cyk7XG5cbiAgY29uc3QgYXVkaXRzID0gc2x1Z3MubWFwKFxuICAgIChzbHVnKTogQXVkaXRPdXRwdXQgPT5cbiAgICAgIGZhaWxlZEF1ZGl0cy5maW5kKGF1ZGl0ID0+IGF1ZGl0LnNsdWcgPT09IHNsdWcpID8/IHtcbiAgICAgICAgc2x1ZyxcbiAgICAgICAgc2NvcmU6IDEsXG4gICAgICAgIHZhbHVlOiAwLFxuICAgICAgICBkaXNwbGF5VmFsdWU6ICdwYXNzZWQnLFxuICAgICAgICBkZXRhaWxzOiB7IGlzc3VlczogW10gfSxcbiAgICAgIH0sXG4gICk7XG5cbiAgYXdhaXQgZW5zdXJlRGlyZWN0b3J5RXhpc3RzKHBhdGguZGlybmFtZShSVU5ORVJfT1VUUFVUX1BBVEgpKTtcbiAgYXdhaXQgd3JpdGVGaWxlKFJVTk5FUl9PVVRQVVRfUEFUSCwgSlNPTi5zdHJpbmdpZnkoYXVkaXRzKSk7XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjcmVhdGVSdW5uZXJDb25maWcoXG4gIHNjcmlwdFBhdGg6IHN0cmluZyxcbiAgYXVkaXRzOiBBdWRpdFtdLFxuICB0YXJnZXRzOiBFU0xpbnRUYXJnZXRbXSxcbik6IFByb21pc2U8UnVubmVyQ29uZmlnPiB7XG4gIGNvbnN0IGNvbmZpZzogRVNMaW50UGx1Z2luUnVubmVyQ29uZmlnID0ge1xuICAgIHRhcmdldHMsXG4gICAgc2x1Z3M6IGF1ZGl0cy5tYXAoYXVkaXQgPT4gYXVkaXQuc2x1ZyksXG4gIH07XG4gIGF3YWl0IGVuc3VyZURpcmVjdG9yeUV4aXN0cyhwYXRoLmRpcm5hbWUoUExVR0lOX0NPTkZJR19QQVRIKSk7XG4gIGF3YWl0IHdyaXRlRmlsZShQTFVHSU5fQ09ORklHX1BBVEgsIEpTT04uc3RyaW5naWZ5KGNvbmZpZykpO1xuXG4gIHJldHVybiB7XG4gICAgY29tbWFuZDogJ25vZGUnLFxuICAgIGFyZ3M6IFtmaWxlUGF0aFRvQ2xpQXJnKHNjcmlwdFBhdGgpXSxcbiAgICBvdXRwdXRGaWxlOiBSVU5ORVJfT1VUUFVUX1BBVEgsXG4gIH07XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvcnVubmVyL2xpbnQudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL3J1bm5lclwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvcnVubmVyL2xpbnQudHNcIjtpbXBvcnQgdHlwZSB7IEVTTGludCwgTGludGVyIH0gZnJvbSAnZXNsaW50JztcbmltcG9ydCB7IHBsYXRmb3JtIH0gZnJvbSAnbm9kZTpvcyc7XG5pbXBvcnQge1xuICBkaXN0aW5jdCxcbiAgZXhlY3V0ZVByb2Nlc3MsXG4gIGZpbGVQYXRoVG9DbGlBcmcsXG4gIHRvQXJyYXksXG59IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgdHlwZSB7IEVTTGludFRhcmdldCB9IGZyb20gJy4uL2NvbmZpZy5qcyc7XG5pbXBvcnQgeyBzZXR1cEVTTGludCB9IGZyb20gJy4uL3NldHVwLmpzJztcbmltcG9ydCB0eXBlIHsgTGludGVyT3V0cHV0LCBSdWxlT3B0aW9uc1BlckZpbGUgfSBmcm9tICcuL3R5cGVzLmpzJztcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxpbnQoe1xuICBlc2xpbnRyYyxcbiAgcGF0dGVybnMsXG59OiBFU0xpbnRUYXJnZXQpOiBQcm9taXNlPExpbnRlck91dHB1dD4ge1xuICBjb25zdCByZXN1bHRzID0gYXdhaXQgZXhlY3V0ZUxpbnQoeyBlc2xpbnRyYywgcGF0dGVybnMgfSk7XG4gIGNvbnN0IGVzbGludCA9IGF3YWl0IHNldHVwRVNMaW50KGVzbGludHJjKTtcbiAgY29uc3QgcnVsZU9wdGlvbnNQZXJGaWxlID0gYXdhaXQgbG9hZFJ1bGVPcHRpb25zUGVyRmlsZShlc2xpbnQsIHJlc3VsdHMpO1xuICByZXR1cm4geyByZXN1bHRzLCBydWxlT3B0aW9uc1BlckZpbGUgfTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZXhlY3V0ZUxpbnQoe1xuICBlc2xpbnRyYyxcbiAgcGF0dGVybnMsXG59OiBFU0xpbnRUYXJnZXQpOiBQcm9taXNlPEVTTGludC5MaW50UmVzdWx0W10+IHtcbiAgLy8gcnVubmluZyBhcyBDTEkgYmVjYXVzZSBFU0xpbnQjbGludEZpbGVzKCkgcnVucyBvdXQgb2YgbWVtb3J5XG4gIGNvbnN0IHsgc3Rkb3V0IH0gPSBhd2FpdCBleGVjdXRlUHJvY2Vzcyh7XG4gICAgY29tbWFuZDogJ25weCcsXG4gICAgYXJnczogW1xuICAgICAgJ2VzbGludCcsXG4gICAgICAuLi4oZXNsaW50cmMgPyBbYC0tY29uZmlnPSR7ZmlsZVBhdGhUb0NsaUFyZyhlc2xpbnRyYyl9YF0gOiBbXSksXG4gICAgICAuLi4odHlwZW9mIGVzbGludHJjID09PSAnb2JqZWN0JyA/IFsnLS1uby1lc2xpbnRyYyddIDogW10pLFxuICAgICAgJy0tbm8tZXJyb3Itb24tdW5tYXRjaGVkLXBhdHRlcm4nLFxuICAgICAgJy0tZm9ybWF0PWpzb24nLFxuICAgICAgLi4udG9BcnJheShwYXR0ZXJucykubWFwKHBhdHRlcm4gPT5cbiAgICAgICAgLy8gZ2xvYnMgbmVlZCB0byBiZSBlc2NhcGVkIG9uIFVuaXhcbiAgICAgICAgcGxhdGZvcm0oKSA9PT0gJ3dpbjMyJyA/IHBhdHRlcm4gOiBgJyR7cGF0dGVybn0nYCxcbiAgICAgICksXG4gICAgXSxcbiAgICBpZ25vcmVFeGl0Q29kZTogdHJ1ZSxcbiAgICBjd2Q6IHByb2Nlc3MuY3dkKCksXG4gIH0pO1xuXG4gIHJldHVybiBKU09OLnBhcnNlKHN0ZG91dCkgYXMgRVNMaW50LkxpbnRSZXN1bHRbXTtcbn1cblxuZnVuY3Rpb24gbG9hZFJ1bGVPcHRpb25zUGVyRmlsZShcbiAgZXNsaW50OiBFU0xpbnQsXG4gIHJlc3VsdHM6IEVTTGludC5MaW50UmVzdWx0W10sXG4pOiBQcm9taXNlPFJ1bGVPcHRpb25zUGVyRmlsZT4ge1xuICByZXR1cm4gcmVzdWx0cy5yZWR1Y2UoYXN5bmMgKGFjYywgeyBmaWxlUGF0aCwgbWVzc2FnZXMgfSkgPT4ge1xuICAgIGNvbnN0IGZpbGVzTWFwID0gYXdhaXQgYWNjO1xuICAgIGNvbnN0IGNvbmZpZyA9IChhd2FpdCBlc2xpbnQuY2FsY3VsYXRlQ29uZmlnRm9yRmlsZShcbiAgICAgIGZpbGVQYXRoLFxuICAgICkpIGFzIExpbnRlci5Db25maWc7XG4gICAgY29uc3QgcnVsZUlkcyA9IGRpc3RpbmN0KFxuICAgICAgbWVzc2FnZXNcbiAgICAgICAgLm1hcCgoeyBydWxlSWQgfSkgPT4gcnVsZUlkKVxuICAgICAgICAuZmlsdGVyKChydWxlSWQpOiBydWxlSWQgaXMgc3RyaW5nID0+IHJ1bGVJZCAhPSBudWxsKSxcbiAgICApO1xuICAgIGNvbnN0IHJ1bGVzTWFwID0gT2JqZWN0LmZyb21FbnRyaWVzKFxuICAgICAgcnVsZUlkcy5tYXAocnVsZUlkID0+IFtcbiAgICAgICAgcnVsZUlkLFxuICAgICAgICB0b0FycmF5KGNvbmZpZy5ydWxlcz8uW3J1bGVJZF0gPz8gW10pLnNsaWNlKDEpLFxuICAgICAgXSksXG4gICAgKTtcbiAgICByZXR1cm4ge1xuICAgICAgLi4uZmlsZXNNYXAsXG4gICAgICBbZmlsZVBhdGhdOiB7XG4gICAgICAgIC4uLmZpbGVzTWFwW2ZpbGVQYXRoXSxcbiAgICAgICAgLi4ucnVsZXNNYXAsXG4gICAgICB9LFxuICAgIH07XG4gIH0sIFByb21pc2UucmVzb2x2ZTxSdWxlT3B0aW9uc1BlckZpbGU+KHt9KSk7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvcnVubmVyL3RyYW5zZm9ybS50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvcnVubmVyXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9ydW5uZXIvdHJhbnNmb3JtLnRzXCI7aW1wb3J0IHR5cGUgeyBMaW50ZXIgfSBmcm9tICdlc2xpbnQnO1xuaW1wb3J0IHR5cGUgeyBBdWRpdE91dHB1dCwgSXNzdWUsIElzc3VlU2V2ZXJpdHkgfSBmcm9tICdAY29kZS1wdXNodXAvbW9kZWxzJztcbmltcG9ydCB7XG4gIGNvbXBhcmVJc3N1ZVNldmVyaXR5LFxuICBjb3VudE9jY3VycmVuY2VzLFxuICBvYmplY3RUb0VudHJpZXMsXG4gIHBsdXJhbGl6ZVRva2VuLFxuICB0cnVuY2F0ZUlzc3VlTWVzc2FnZSxcbiAgdWksXG59IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgeyBydWxlSWRUb1NsdWcgfSBmcm9tICcuLi9tZXRhL2luZGV4LmpzJztcbmltcG9ydCB0eXBlIHsgTGludGVyT3V0cHV0IH0gZnJvbSAnLi90eXBlcy5qcyc7XG5cbnR5cGUgTGludElzc3VlID0gTGludGVyLkxpbnRNZXNzYWdlICYge1xuICBmaWxlUGF0aDogc3RyaW5nO1xufTtcblxuZXhwb3J0IGZ1bmN0aW9uIG1lcmdlTGludGVyT3V0cHV0cyhvdXRwdXRzOiBMaW50ZXJPdXRwdXRbXSk6IExpbnRlck91dHB1dCB7XG4gIHJldHVybiBvdXRwdXRzLnJlZHVjZTxMaW50ZXJPdXRwdXQ+KFxuICAgIChhY2MsIHsgcmVzdWx0cywgcnVsZU9wdGlvbnNQZXJGaWxlIH0pID0+ICh7XG4gICAgICByZXN1bHRzOiBbLi4uYWNjLnJlc3VsdHMsIC4uLnJlc3VsdHNdLFxuICAgICAgcnVsZU9wdGlvbnNQZXJGaWxlOiB7IC4uLmFjYy5ydWxlT3B0aW9uc1BlckZpbGUsIC4uLnJ1bGVPcHRpb25zUGVyRmlsZSB9LFxuICAgIH0pLFxuICAgIHsgcmVzdWx0czogW10sIHJ1bGVPcHRpb25zUGVyRmlsZToge30gfSxcbiAgKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGxpbnRSZXN1bHRzVG9BdWRpdHMoe1xuICByZXN1bHRzLFxuICBydWxlT3B0aW9uc1BlckZpbGUsXG59OiBMaW50ZXJPdXRwdXQpOiBBdWRpdE91dHB1dFtdIHtcbiAgY29uc3QgaXNzdWVzUGVyQXVkaXQgPSByZXN1bHRzXG4gICAgLmZsYXRNYXAoKHsgbWVzc2FnZXMsIGZpbGVQYXRoIH0pID0+XG4gICAgICBtZXNzYWdlcy5tYXAoKG1lc3NhZ2UpOiBMaW50SXNzdWUgPT4gKHsgLi4ubWVzc2FnZSwgZmlsZVBhdGggfSkpLFxuICAgIClcbiAgICAucmVkdWNlPFJlY29yZDxzdHJpbmcsIExpbnRJc3N1ZVtdPj4oKGFjYywgaXNzdWUpID0+IHtcbiAgICAgIGNvbnN0IHsgcnVsZUlkLCBtZXNzYWdlLCBmaWxlUGF0aCB9ID0gaXNzdWU7XG4gICAgICBpZiAoIXJ1bGVJZCkge1xuICAgICAgICB1aSgpLmxvZ2dlci53YXJuaW5nKFxuICAgICAgICAgIGBFU0xpbnQgY29yZSBlcnJvciAtICR7bWVzc2FnZX0gKGZpbGU6ICR7ZmlsZVBhdGh9KWAsXG4gICAgICAgICk7XG4gICAgICAgIHJldHVybiBhY2M7XG4gICAgICB9XG4gICAgICBjb25zdCBvcHRpb25zID0gcnVsZU9wdGlvbnNQZXJGaWxlW2ZpbGVQYXRoXT8uW3J1bGVJZF0gPz8gW107XG4gICAgICBjb25zdCBhdWRpdFNsdWcgPSBydWxlSWRUb1NsdWcocnVsZUlkLCBvcHRpb25zKTtcbiAgICAgIHJldHVybiB7IC4uLmFjYywgW2F1ZGl0U2x1Z106IFsuLi4oYWNjW2F1ZGl0U2x1Z10gPz8gW10pLCBpc3N1ZV0gfTtcbiAgICB9LCB7fSk7XG5cbiAgcmV0dXJuIE9iamVjdC5lbnRyaWVzKGlzc3Vlc1BlckF1ZGl0KS5tYXAoZW50cnkgPT4gdG9BdWRpdCguLi5lbnRyeSkpO1xufVxuXG5mdW5jdGlvbiB0b0F1ZGl0KHNsdWc6IHN0cmluZywgaXNzdWVzOiBMaW50SXNzdWVbXSk6IEF1ZGl0T3V0cHV0IHtcbiAgY29uc3QgYXVkaXRJc3N1ZXMgPSBpc3N1ZXMubWFwKGNvbnZlcnRJc3N1ZSk7XG4gIGNvbnN0IHNldmVyaXR5Q291bnRzID0gY291bnRPY2N1cnJlbmNlcyhcbiAgICBhdWRpdElzc3Vlcy5tYXAoKHsgc2V2ZXJpdHkgfSkgPT4gc2V2ZXJpdHkpLFxuICApO1xuICBjb25zdCBzZXZlcml0aWVzID0gb2JqZWN0VG9FbnRyaWVzKHNldmVyaXR5Q291bnRzKTtcbiAgY29uc3Qgc3VtbWFyeVRleHQgPSBzZXZlcml0aWVzXG4gICAgLnRvU29ydGVkKChhLCBiKSA9PiAtY29tcGFyZUlzc3VlU2V2ZXJpdHkoYVswXSwgYlswXSkpXG4gICAgLm1hcCgoW3NldmVyaXR5LCBjb3VudCA9IDBdKSA9PiBwbHVyYWxpemVUb2tlbihzZXZlcml0eSwgY291bnQpKVxuICAgIC5qb2luKCcsICcpO1xuXG4gIHJldHVybiB7XG4gICAgc2x1ZyxcbiAgICBzY29yZTogTnVtYmVyKGF1ZGl0SXNzdWVzLmxlbmd0aCA9PT0gMCksXG4gICAgdmFsdWU6IGF1ZGl0SXNzdWVzLmxlbmd0aCxcbiAgICBkaXNwbGF5VmFsdWU6IHN1bW1hcnlUZXh0LFxuICAgIGRldGFpbHM6IHtcbiAgICAgIGlzc3VlczogYXVkaXRJc3N1ZXMsXG4gICAgfSxcbiAgfTtcbn1cblxuZnVuY3Rpb24gY29udmVydElzc3VlKGlzc3VlOiBMaW50SXNzdWUpOiBJc3N1ZSB7XG4gIHJldHVybiB7XG4gICAgbWVzc2FnZTogdHJ1bmNhdGVJc3N1ZU1lc3NhZ2UoaXNzdWUubWVzc2FnZSksXG4gICAgc2V2ZXJpdHk6IGNvbnZlcnRTZXZlcml0eShpc3N1ZS5zZXZlcml0eSksXG4gICAgc291cmNlOiB7XG4gICAgICBmaWxlOiBpc3N1ZS5maWxlUGF0aCxcbiAgICAgIC4uLihpc3N1ZS5saW5lID4gMCAmJiB7XG4gICAgICAgIHBvc2l0aW9uOiB7XG4gICAgICAgICAgc3RhcnRMaW5lOiBpc3N1ZS5saW5lLFxuICAgICAgICAgIC4uLihpc3N1ZS5jb2x1bW4gPiAwICYmIHsgc3RhcnRDb2x1bW46IGlzc3VlLmNvbHVtbiB9KSxcbiAgICAgICAgICAuLi4oaXNzdWUuZW5kTGluZSAmJlxuICAgICAgICAgICAgaXNzdWUuZW5kTGluZSA+IDAgJiYge1xuICAgICAgICAgICAgICBlbmRMaW5lOiBpc3N1ZS5lbmRMaW5lLFxuICAgICAgICAgICAgfSksXG4gICAgICAgICAgLi4uKGlzc3VlLmVuZENvbHVtbiAmJlxuICAgICAgICAgICAgaXNzdWUuZW5kQ29sdW1uID4gMCAmJiB7IGVuZENvbHVtbjogaXNzdWUuZW5kQ29sdW1uIH0pLFxuICAgICAgICB9LFxuICAgICAgfSksXG4gICAgfSxcbiAgfTtcbn1cblxuZnVuY3Rpb24gY29udmVydFNldmVyaXR5KHNldmVyaXR5OiBMaW50ZXIuU2V2ZXJpdHkpOiBJc3N1ZVNldmVyaXR5IHtcbiAgc3dpdGNoIChzZXZlcml0eSkge1xuICAgIGNhc2UgMjpcbiAgICAgIHJldHVybiAnZXJyb3InO1xuICAgIGNhc2UgMTpcbiAgICAgIHJldHVybiAnd2FybmluZyc7XG4gICAgY2FzZSAwOlxuICAgICAgLy8gc2hvdWxkbid0IGhhcHBlblxuICAgICAgdGhyb3cgbmV3IEVycm9yKGBVbmV4cGVjdGVkIHNldmVyaXR5ICR7c2V2ZXJpdHl9IGluIEVTTGludCByZXN1bHRzYCk7XG4gIH1cbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9ueC91dGlscy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvbnhcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL254L3V0aWxzLnRzXCI7aW1wb3J0IHR5cGUgeyBQcm9qZWN0Q29uZmlndXJhdGlvbiB9IGZyb20gJ0BueC9kZXZraXQnO1xuaW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB7IGZpbGVFeGlzdHMsIHRvQXJyYXkgfSBmcm9tICdAY29kZS1wdXNodXAvdXRpbHMnO1xuaW1wb3J0IHR5cGUgeyBDb25maWdGb3JtYXQgfSBmcm9tICcuLi9tZXRhL2luZGV4LmpzJztcblxuY29uc3QgRVNMSU5UX0NPTkZJR19FWFRFTlNJT05TOiBSZWNvcmQ8Q29uZmlnRm9ybWF0LCBzdHJpbmdbXT4gPSB7XG4gIC8vIGh0dHBzOi8vZXNsaW50Lm9yZy9kb2NzL2xhdGVzdC91c2UvY29uZmlndXJlL2NvbmZpZ3VyYXRpb24tZmlsZXMjY29uZmlndXJhdGlvbi1maWxlLWZvcm1hdHNcbiAgZmxhdDogWydqcycsICdtanMnLCAnY2pzJ10sXG4gIC8vIGh0dHBzOi8vZXNsaW50Lm9yZy9kb2NzL2xhdGVzdC91c2UvY29uZmlndXJlL2NvbmZpZ3VyYXRpb24tZmlsZXMtZGVwcmVjYXRlZFxuICBsZWdhY3k6IFsnanNvbicsICdqcycsICdjanMnLCAneW1sJywgJ3lhbWwnXSxcbn07XG5jb25zdCBFU0xJTlRfQ09ORklHX05BTUVTOiBSZWNvcmQ8Q29uZmlnRm9ybWF0LCBzdHJpbmdbXT4gPSB7XG4gIC8vIGh0dHBzOi8vZXNsaW50Lm9yZy9kb2NzL2xhdGVzdC91c2UvY29uZmlndXJlL2NvbmZpZ3VyYXRpb24tZmlsZXMjY29uZmlndXJhdGlvbi1maWxlLWZvcm1hdHNcbiAgZmxhdDogWydlc2xpbnQuY29uZmlnJ10sXG4gIC8vIGh0dHBzOi8vZXNsaW50Lm9yZy9kb2NzL2xhdGVzdC91c2UvY29uZmlndXJlL2NvbmZpZ3VyYXRpb24tZmlsZXMtZGVwcmVjYXRlZFxuICBsZWdhY3k6IFsnLmVzbGludHJjJ10sXG59O1xuXG5jb25zdCBDUF9FU0xJTlRfQ09ORklHX05BTUVTOiBSZWNvcmQ8Q29uZmlnRm9ybWF0LCBzdHJpbmdbXT4gPSB7XG4gIGZsYXQ6IFtcbiAgICAnY29kZS1wdXNodXAuZXNsaW50LmNvbmZpZycsXG4gICAgJ2VzbGludC5jb2RlLXB1c2h1cC5jb25maWcnLFxuICAgICdlc2xpbnQuY29uZmlnLmNvZGUtcHVzaHVwJyxcbiAgICAnZXNsaW50LnN0cmljdC5jb25maWcnLFxuICAgICdlc2xpbnQuY29uZmlnLnN0cmljdCcsXG4gIF0sXG4gIGxlZ2FjeTogWydjb2RlLXB1c2h1cC5lc2xpbnRyYycsICcuZXNsaW50cmMuY29kZS1wdXNodXAnLCAnLmVzbGludHJjLnN0cmljdCddLFxufTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGZpbmRDb2RlUHVzaHVwRXNsaW50Q29uZmlnKFxuICBwcm9qZWN0OiBQcm9qZWN0Q29uZmlndXJhdGlvbixcbiAgZm9ybWF0OiBDb25maWdGb3JtYXQsXG4pOiBQcm9taXNlPHN0cmluZyB8IHVuZGVmaW5lZD4ge1xuICByZXR1cm4gZmluZFByb2plY3RGaWxlKHByb2plY3QsIHtcbiAgICBuYW1lczogQ1BfRVNMSU5UX0NPTkZJR19OQU1FU1tmb3JtYXRdLFxuICAgIGV4dGVuc2lvbnM6IEVTTElOVF9DT05GSUdfRVhURU5TSU9OU1tmb3JtYXRdLFxuICB9KTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGZpbmRFc2xpbnRDb25maWcoXG4gIHByb2plY3Q6IFByb2plY3RDb25maWd1cmF0aW9uLFxuICBmb3JtYXQ6IENvbmZpZ0Zvcm1hdCxcbik6IFByb21pc2U8c3RyaW5nIHwgdW5kZWZpbmVkPiB7XG4gIGNvbnN0IG9wdGlvbnMgPSBwcm9qZWN0LnRhcmdldHM/LlsnbGludCddPy5vcHRpb25zIGFzXG4gICAgfCB7IGVzbGludENvbmZpZz86IHN0cmluZyB9XG4gICAgfCB1bmRlZmluZWQ7XG4gIHJldHVybiAoXG4gICAgb3B0aW9ucz8uZXNsaW50Q29uZmlnID8/XG4gICAgKGF3YWl0IGZpbmRQcm9qZWN0RmlsZShwcm9qZWN0LCB7XG4gICAgICBuYW1lczogRVNMSU5UX0NPTkZJR19OQU1FU1tmb3JtYXRdLFxuICAgICAgZXh0ZW5zaW9uczogRVNMSU5UX0NPTkZJR19FWFRFTlNJT05TW2Zvcm1hdF0sXG4gICAgfSkpXG4gICk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRMaW50RmlsZVBhdHRlcm5zKFxuICBwcm9qZWN0OiBQcm9qZWN0Q29uZmlndXJhdGlvbixcbiAgZm9ybWF0OiBDb25maWdGb3JtYXQsXG4pOiBzdHJpbmdbXSB7XG4gIGNvbnN0IG9wdGlvbnMgPSBwcm9qZWN0LnRhcmdldHM/LlsnbGludCddPy5vcHRpb25zIGFzXG4gICAgfCB7IGxpbnRGaWxlUGF0dGVybnM/OiBzdHJpbmcgfCBzdHJpbmdbXSB9XG4gICAgfCB1bmRlZmluZWQ7XG4gIC8vIGxpbnRGaWxlUGF0dGVybnMgZGVmYXVsdHMgdG8gW1wie3Byb2plY3RSb290fVwiXSAtIGh0dHBzOi8vZ2l0aHViLmNvbS9ucndsL254L3B1bGwvMjAzMTNcbiAgY29uc3QgZGVmYXVsdFBhdHRlcm5zID1cbiAgICBmb3JtYXQgPT09ICdsZWdhY3knXG4gICAgICA/IGAke3Byb2plY3Qucm9vdH0vKiovKmAgLy8gZmlsZXMgbm90IGZvbGRlciBuZWVkZWQgZm9yIGxlZ2FjeSBiZWNhdXNlIHJ1bGVzIGRldGVjdGVkIHdpdGggRVNMaW50LmNhbGN1bGF0ZUNvbmZpZ0ZvckZpbGVcbiAgICAgIDogcHJvamVjdC5yb290O1xuICBjb25zdCBwYXR0ZXJucyA9XG4gICAgb3B0aW9ucz8ubGludEZpbGVQYXR0ZXJucyA9PSBudWxsXG4gICAgICA/IFtkZWZhdWx0UGF0dGVybnNdXG4gICAgICA6IHRvQXJyYXkob3B0aW9ucy5saW50RmlsZVBhdHRlcm5zKTtcbiAgaWYgKGZvcm1hdCA9PT0gJ2xlZ2FjeScpIHtcbiAgICByZXR1cm4gW1xuICAgICAgLi4ucGF0dGVybnMsXG4gICAgICAvLyBIQUNLOiBFU0xpbnQuY2FsY3VsYXRlQ29uZmlnRm9yRmlsZSB3b24ndCBmaW5kIHJ1bGVzIGluY2x1ZGVkIG9ubHkgZm9yIHN1YnNldHMgb2YgKi50cyB3aGVuIGdsb2JzIHVzZWRcbiAgICAgIC8vIHNvIHdlIGV4cGxpY2l0bHkgcHJvdmlkZSBhZGRpdGlvbmFsIHBhdHRlcm5zIHVzZWQgYnkgQGNvZGUtcHVzaHVwL2VzbGludC1jb25maWcgdG8gZW5zdXJlIHRob3NlIHJ1bGVzIGFyZSBpbmNsdWRlZFxuICAgICAgLy8gdGhpcyB3b3JrYXJvdW5kIGlzIG9ubHkgbmVjZXNzYXJ5IGZvciBsZWdhY3kgY29uZmlncyAocnVsZXMgYXJlIGRldGVjdGVkIG1vcmUgcmVsaWFibHkgaW4gZmxhdCBjb25maWdzKVxuICAgICAgYCR7cHJvamVjdC5yb290fS8qLnNwZWMudHNgLCAvLyBqZXN0LyogYW5kIHZpdGVzdC8qIHJ1bGVzXG4gICAgICBgJHtwcm9qZWN0LnJvb3R9LyouY3kudHNgLCAvLyBjeXByZXNzLyogcnVsZXNcbiAgICAgIGAke3Byb2plY3Qucm9vdH0vKi5zdG9yaWVzLnRzYCwgLy8gc3Rvcnlib29rLyogcnVsZXNcbiAgICAgIGAke3Byb2plY3Qucm9vdH0vLnN0b3J5Ym9vay9tYWluLnRzYCwgLy8gc3Rvcnlib29rL25vLXVuaW5zdGFsbGVkLWFkZG9ucyBydWxlXG4gICAgXTtcbiAgfVxuICByZXR1cm4gcGF0dGVybnM7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGZpbmRQcm9qZWN0RmlsZShcbiAgcHJvamVjdDogUHJvamVjdENvbmZpZ3VyYXRpb24sXG4gIGZpbGU6IHtcbiAgICBuYW1lczogc3RyaW5nW107XG4gICAgZXh0ZW5zaW9uczogc3RyaW5nW107XG4gIH0sXG4pOiBQcm9taXNlPHN0cmluZyB8IHVuZGVmaW5lZD4ge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZnVuY3Rpb25hbC9uby1sb29wLXN0YXRlbWVudHNcbiAgZm9yIChjb25zdCBuYW1lIG9mIGZpbGUubmFtZXMpIHtcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZnVuY3Rpb25hbC9uby1sb29wLXN0YXRlbWVudHNcbiAgICBmb3IgKGNvbnN0IGV4dCBvZiBmaWxlLmV4dGVuc2lvbnMpIHtcbiAgICAgIGNvbnN0IGZpbGVuYW1lID0gYC4vJHtwcm9qZWN0LnJvb3R9LyR7bmFtZX0uJHtleHR9YDtcbiAgICAgIGlmIChhd2FpdCBmaWxlRXhpc3RzKHBhdGguam9pbihwcm9jZXNzLmN3ZCgpLCBmaWxlbmFtZSkpKSB7XG4gICAgICAgIHJldHVybiBmaWxlbmFtZTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgcmV0dXJuIHVuZGVmaW5lZDtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL2pzLXBhY2thZ2VzLXBsdWdpbi50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYlwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9qcy1wYWNrYWdlcy1wbHVnaW4udHNcIjtpbXBvcnQgeyBjcmVhdGVSZXF1aXJlIH0gZnJvbSAnbm9kZTptb2R1bGUnO1xuaW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB7IGZpbGVVUkxUb1BhdGggfSBmcm9tICdub2RlOnVybCc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0LCBHcm91cCwgUGx1Z2luQ29uZmlnIH0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5pbXBvcnQge1xuICB0eXBlIERlcGVuZGVuY3lHcm91cCxcbiAgdHlwZSBKU1BhY2thZ2VzUGx1Z2luQ29uZmlnLFxuICB0eXBlIFBhY2thZ2VDb21tYW5kLFxuICB0eXBlIFBhY2thZ2VNYW5hZ2VySWQsXG4gIGRlcGVuZGVuY3lHcm91cHMsXG59IGZyb20gJy4vY29uZmlnLmpzJztcbmltcG9ydCB7IGRlcGVuZGVuY3lEb2NzLCBkZXBlbmRlbmN5R3JvdXBXZWlnaHRzIH0gZnJvbSAnLi9jb25zdGFudHMuanMnO1xuaW1wb3J0IHsgcGFja2FnZU1hbmFnZXJzIH0gZnJvbSAnLi9wYWNrYWdlLW1hbmFnZXJzL3BhY2thZ2UtbWFuYWdlcnMuanMnO1xuaW1wb3J0IHsgY3JlYXRlUnVubmVyQ29uZmlnIH0gZnJvbSAnLi9ydW5uZXIvaW5kZXguanMnO1xuaW1wb3J0IHsgbm9ybWFsaXplQ29uZmlnIH0gZnJvbSAnLi91dGlscy5qcyc7XG5cbi8qKlxuICogSW5zdGFudGlhdGVzIENvZGUgUHVzaFVwIEpTIHBhY2thZ2VzIHBsdWdpbiBmb3IgY29yZSBjb25maWcuXG4gKlxuICogQGV4YW1wbGVcbiAqIGltcG9ydCBqc1BhY2thZ2VzUGx1Z2luIGZyb20gJ0Bjb2RlLXB1c2h1cC9qcy1wYWNrYWdlcy1wbHVnaW4nXG4gKlxuICogZXhwb3J0IGRlZmF1bHQge1xuICogICAvLyAuLi4gY29yZSBjb25maWcgLi4uXG4gKiAgIHBsdWdpbnM6IFtcbiAqICAgICAvLyAuLi4gb3RoZXIgcGx1Z2lucyAuLi5cbiAqICAgICBhd2FpdCBqc1BhY2thZ2VzUGx1Z2luKHsgcGFja2FnZU1hbmFnZXI6ICducG0nIH0pXG4gKiAgIF1cbiAqIH1cbiAqXG4gKiBAcmV0dXJucyBQbHVnaW4gY29uZmlndXJhdGlvbi5cbiAqL1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24ganNQYWNrYWdlc1BsdWdpbihcbiAgY29uZmlnPzogSlNQYWNrYWdlc1BsdWdpbkNvbmZpZyxcbik6IFByb21pc2U8UGx1Z2luQ29uZmlnPiB7XG4gIGNvbnN0IHsgcGFja2FnZU1hbmFnZXIsIGNoZWNrcywgZGVwR3JvdXBzLCAuLi5qc1BhY2thZ2VzUGx1Z2luQ29uZmlnUmVzdCB9ID1cbiAgICBhd2FpdCBub3JtYWxpemVDb25maWcoY29uZmlnKTtcblxuICBjb25zdCBydW5uZXJTY3JpcHRQYXRoID0gcGF0aC5qb2luKFxuICAgIGZpbGVVUkxUb1BhdGgocGF0aC5kaXJuYW1lKGltcG9ydC5tZXRhLnVybCkpLFxuICAgICcuLicsXG4gICAgJ2Jpbi5qcycsXG4gICk7XG5cbiAgY29uc3QgcGFja2FnZUpzb24gPSBjcmVhdGVSZXF1aXJlKGltcG9ydC5tZXRhLnVybCkoXG4gICAgJy4uLy4uL3BhY2thZ2UuanNvbicsXG4gICkgYXMgdHlwZW9mIGltcG9ydCgnLi4vLi4vcGFja2FnZS5qc29uJyk7XG5cbiAgcmV0dXJuIHtcbiAgICBzbHVnOiAnanMtcGFja2FnZXMnLFxuICAgIHRpdGxlOiAnSlMgUGFja2FnZXMnLFxuICAgIGljb246IHBhY2thZ2VNYW5hZ2VyLmljb24sXG4gICAgZGVzY3JpcHRpb246XG4gICAgICAnVGhpcyBwbHVnaW4gcnVucyBhdWRpdCB0byB1bmNvdmVyIHZ1bG5lcmFiaWxpdGllcyBhbmQgbGlzdHMgb3V0ZGF0ZWQgZGVwZW5kZW5jaWVzLiBJdCBzdXBwb3J0cyBucG0sIHlhcm4gY2xhc3NpYywgeWFybiBtb2Rlcm4sIGFuZCBwbnBtIHBhY2thZ2UgbWFuYWdlcnMuJyxcbiAgICBkb2NzVXJsOiBwYWNrYWdlTWFuYWdlci5kb2NzLmhvbWVwYWdlLFxuICAgIHBhY2thZ2VOYW1lOiBwYWNrYWdlSnNvbi5uYW1lLFxuICAgIHZlcnNpb246IHBhY2thZ2VKc29uLnZlcnNpb24sXG4gICAgYXVkaXRzOiBjcmVhdGVBdWRpdHMocGFja2FnZU1hbmFnZXIuc2x1ZywgY2hlY2tzLCBkZXBHcm91cHMpLFxuICAgIGdyb3VwczogY3JlYXRlR3JvdXBzKHBhY2thZ2VNYW5hZ2VyLnNsdWcsIGNoZWNrcywgZGVwR3JvdXBzKSxcbiAgICBydW5uZXI6IGF3YWl0IGNyZWF0ZVJ1bm5lckNvbmZpZyhydW5uZXJTY3JpcHRQYXRoLCB7XG4gICAgICAuLi5qc1BhY2thZ2VzUGx1Z2luQ29uZmlnUmVzdCxcbiAgICAgIGNoZWNrcyxcbiAgICAgIHBhY2thZ2VNYW5hZ2VyOiBwYWNrYWdlTWFuYWdlci5zbHVnLFxuICAgICAgZGVwZW5kZW5jeUdyb3VwczogZGVwR3JvdXBzLFxuICAgIH0pLFxuICB9O1xufVxuXG5mdW5jdGlvbiBjcmVhdGVHcm91cHMoXG4gIGlkOiBQYWNrYWdlTWFuYWdlcklkLFxuICBjaGVja3M6IFBhY2thZ2VDb21tYW5kW10sXG4gIGRlcEdyb3VwczogRGVwZW5kZW5jeUdyb3VwW10sXG4pOiBHcm91cFtdIHtcbiAgY29uc3QgcG0gPSBwYWNrYWdlTWFuYWdlcnNbaWRdO1xuICBjb25zdCBzdXBwb3J0ZWRBdWRpdERlcEdyb3VwcyA9XG4gICAgcG0uYXVkaXQuc3VwcG9ydGVkRGVwR3JvdXBzID8/IGRlcGVuZGVuY3lHcm91cHM7XG4gIGNvbnN0IGNvbXBhdGlibGVBdWRpdERlcEdyb3VwcyA9IGRlcEdyb3Vwcy5maWx0ZXIoZ3JvdXAgPT5cbiAgICBzdXBwb3J0ZWRBdWRpdERlcEdyb3Vwcy5pbmNsdWRlcyhncm91cCksXG4gICk7XG5cbiAgY29uc3QgZ3JvdXBzOiBSZWNvcmQ8UGFja2FnZUNvbW1hbmQsIEdyb3VwPiA9IHtcbiAgICBhdWRpdDoge1xuICAgICAgc2x1ZzogYCR7cG0uc2x1Z30tYXVkaXRgLFxuICAgICAgdGl0bGU6IGAke3BtLm5hbWV9IGF1ZGl0YCxcbiAgICAgIGRlc2NyaXB0aW9uOiBgR3JvdXAgY29udGFpbmluZyAke3BtLm5hbWV9IHZ1bG5lcmFiaWxpdGllcy5gLFxuICAgICAgZG9jc1VybDogcG0uZG9jcy5hdWRpdCxcbiAgICAgIHJlZnM6IGNvbXBhdGlibGVBdWRpdERlcEdyb3Vwcy5tYXAoZGVwR3JvdXAgPT4gKHtcbiAgICAgICAgc2x1ZzogYCR7cG0uc2x1Z30tYXVkaXQtJHtkZXBHcm91cH1gLFxuICAgICAgICB3ZWlnaHQ6IGRlcGVuZGVuY3lHcm91cFdlaWdodHNbZGVwR3JvdXBdLFxuICAgICAgfSkpLFxuICAgIH0sXG4gICAgb3V0ZGF0ZWQ6IHtcbiAgICAgIHNsdWc6IGAke3BtLnNsdWd9LW91dGRhdGVkYCxcbiAgICAgIHRpdGxlOiBgJHtwbS5uYW1lfSBvdXRkYXRlZCBkZXBlbmRlbmNpZXNgLFxuICAgICAgZGVzY3JpcHRpb246IGBHcm91cCBjb250YWluaW5nIG91dGRhdGVkICR7cG0ubmFtZX0gZGVwZW5kZW5jaWVzLmAsXG4gICAgICBkb2NzVXJsOiBwbS5kb2NzLm91dGRhdGVkLFxuICAgICAgcmVmczogZGVwR3JvdXBzLm1hcChkZXBHcm91cCA9PiAoe1xuICAgICAgICBzbHVnOiBgJHtwbS5zbHVnfS1vdXRkYXRlZC0ke2RlcEdyb3VwfWAsXG4gICAgICAgIHdlaWdodDogZGVwZW5kZW5jeUdyb3VwV2VpZ2h0c1tkZXBHcm91cF0sXG4gICAgICB9KSksXG4gICAgfSxcbiAgfTtcblxuICByZXR1cm4gY2hlY2tzLm1hcChjaGVjayA9PiBncm91cHNbY2hlY2tdKTtcbn1cblxuZnVuY3Rpb24gY3JlYXRlQXVkaXRzKFxuICBpZDogUGFja2FnZU1hbmFnZXJJZCxcbiAgY2hlY2tzOiBQYWNrYWdlQ29tbWFuZFtdLFxuICBkZXBHcm91cHM6IERlcGVuZGVuY3lHcm91cFtdLFxuKTogQXVkaXRbXSB7XG4gIGNvbnN0IHsgc2x1ZyB9ID0gcGFja2FnZU1hbmFnZXJzW2lkXTtcbiAgcmV0dXJuIGNoZWNrcy5mbGF0TWFwKGNoZWNrID0+IHtcbiAgICBjb25zdCBzdXBwb3J0ZWRBdWRpdERlcEdyb3VwcyA9XG4gICAgICBwYWNrYWdlTWFuYWdlcnNbaWRdLmF1ZGl0LnN1cHBvcnRlZERlcEdyb3VwcyA/PyBkZXBlbmRlbmN5R3JvdXBzO1xuXG4gICAgY29uc3QgY29tcGF0aWJsZURlcEdyb3VwcyA9XG4gICAgICBjaGVjayA9PT0gJ2F1ZGl0J1xuICAgICAgICA/IGRlcEdyb3Vwcy5maWx0ZXIoZ3JvdXAgPT4gc3VwcG9ydGVkQXVkaXREZXBHcm91cHMuaW5jbHVkZXMoZ3JvdXApKVxuICAgICAgICA6IGRlcEdyb3VwcztcblxuICAgIHJldHVybiBjb21wYXRpYmxlRGVwR3JvdXBzLm1hcChkZXBHcm91cCA9PiAoe1xuICAgICAgc2x1ZzogYCR7c2x1Z30tJHtjaGVja30tJHtkZXBHcm91cH1gLFxuICAgICAgdGl0bGU6IGdldEF1ZGl0VGl0bGUoc2x1ZywgY2hlY2ssIGRlcEdyb3VwKSxcbiAgICAgIGRlc2NyaXB0aW9uOiBnZXRBdWRpdERlc2NyaXB0aW9uKGNoZWNrLCBkZXBHcm91cCksXG4gICAgICBkb2NzVXJsOiBkZXBlbmRlbmN5RG9jc1tkZXBHcm91cF0sXG4gICAgfSkpO1xuICB9KTtcbn1cblxuZnVuY3Rpb24gZ2V0QXVkaXRUaXRsZShcbiAgaWQ6IFBhY2thZ2VNYW5hZ2VySWQsXG4gIGNoZWNrOiBQYWNrYWdlQ29tbWFuZCxcbiAgZGVwR3JvdXA6IERlcGVuZGVuY3lHcm91cCxcbikge1xuICBjb25zdCBwbSA9IHBhY2thZ2VNYW5hZ2Vyc1tpZF07XG4gIHJldHVybiBjaGVjayA9PT0gJ2F1ZGl0J1xuICAgID8gYFZ1bG5lcmFiaWxpdGllcyBmb3IgJHtwbS5uYW1lfSAke2RlcEdyb3VwfSBkZXBlbmRlbmNpZXMuYFxuICAgIDogYE91dGRhdGVkICR7cG0ubmFtZX0gJHtkZXBHcm91cH0gZGVwZW5kZW5jaWVzLmA7XG59XG5cbmZ1bmN0aW9uIGdldEF1ZGl0RGVzY3JpcHRpb24oY2hlY2s6IFBhY2thZ2VDb21tYW5kLCBkZXBHcm91cDogRGVwZW5kZW5jeUdyb3VwKSB7XG4gIHJldHVybiBjaGVjayA9PT0gJ2F1ZGl0J1xuICAgID8gYFJ1bnMgc2VjdXJpdHkgYXVkaXQgb24gJHtkZXBHcm91cH0gZGVwZW5kZW5jaWVzLmBcbiAgICA6IGBDaGVja3MgZm9yIG91dGRhdGVkICR7ZGVwR3JvdXB9IGRlcGVuZGVuY2llc2A7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9jb25maWcudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvY29uZmlnLnRzXCI7aW1wb3J0IHsgeiB9IGZyb20gJ3pvZCc7XG5pbXBvcnQgeyB0eXBlIElzc3VlU2V2ZXJpdHksIGlzc3VlU2V2ZXJpdHlTY2hlbWEgfSBmcm9tICdAY29kZS1wdXNodXAvbW9kZWxzJztcbmltcG9ydCB7IGRlZmF1bHRBdWRpdExldmVsTWFwcGluZyB9IGZyb20gJy4vY29uc3RhbnRzLmpzJztcblxuZXhwb3J0IGNvbnN0IGRlcGVuZGVuY3lHcm91cHMgPSBbJ3Byb2QnLCAnZGV2JywgJ29wdGlvbmFsJ10gYXMgY29uc3Q7XG5jb25zdCBkZXBlbmRlbmN5R3JvdXBTY2hlbWEgPSB6LmVudW0oZGVwZW5kZW5jeUdyb3Vwcyk7XG5leHBvcnQgdHlwZSBEZXBlbmRlbmN5R3JvdXAgPSAodHlwZW9mIGRlcGVuZGVuY3lHcm91cHMpW251bWJlcl07XG5cbmNvbnN0IHBhY2thZ2VDb21tYW5kU2NoZW1hID0gei5lbnVtKFsnYXVkaXQnLCAnb3V0ZGF0ZWQnXSk7XG5leHBvcnQgdHlwZSBQYWNrYWdlQ29tbWFuZCA9IHouaW5mZXI8dHlwZW9mIHBhY2thZ2VDb21tYW5kU2NoZW1hPjtcblxuY29uc3QgcGFja2FnZU1hbmFnZXJJZFNjaGVtYSA9IHouZW51bShbXG4gICducG0nLFxuICAneWFybi1jbGFzc2ljJyxcbiAgJ3lhcm4tbW9kZXJuJyxcbiAgJ3BucG0nLFxuXSk7XG5leHBvcnQgdHlwZSBQYWNrYWdlTWFuYWdlcklkID0gei5pbmZlcjx0eXBlb2YgcGFja2FnZU1hbmFnZXJJZFNjaGVtYT47XG5cbmNvbnN0IHBhY2thZ2VKc29uUGF0aFNjaGVtYSA9IHpcbiAgLnVuaW9uKFtcbiAgICB6LmFycmF5KHouc3RyaW5nKCkpLm1pbigxKSxcbiAgICB6Lm9iamVjdCh7IGF1dG9TZWFyY2g6IHoubGl0ZXJhbCh0cnVlKSB9KSxcbiAgXSlcbiAgLmRlc2NyaWJlKFxuICAgICdGaWxlIHBhdGhzIHRvIHBhY2thZ2UuanNvbi4gTG9va3Mgb25seSBhdCByb290IHBhY2thZ2UuanNvbiBieSBkZWZhdWx0JyxcbiAgKVxuICAuZGVmYXVsdChbJ3BhY2thZ2UuanNvbiddKTtcblxuZXhwb3J0IHR5cGUgUGFja2FnZUpzb25QYXRocyA9IHouaW5mZXI8dHlwZW9mIHBhY2thZ2VKc29uUGF0aFNjaGVtYT47XG5cbmV4cG9ydCBjb25zdCBwYWNrYWdlQXVkaXRMZXZlbHMgPSBbXG4gICdjcml0aWNhbCcsXG4gICdoaWdoJyxcbiAgJ21vZGVyYXRlJyxcbiAgJ2xvdycsXG4gICdpbmZvJyxcbl0gYXMgY29uc3Q7XG5jb25zdCBwYWNrYWdlQXVkaXRMZXZlbFNjaGVtYSA9IHouZW51bShwYWNrYWdlQXVkaXRMZXZlbHMpO1xuZXhwb3J0IHR5cGUgUGFja2FnZUF1ZGl0TGV2ZWwgPSB6LmluZmVyPHR5cGVvZiBwYWNrYWdlQXVkaXRMZXZlbFNjaGVtYT47XG5cbmV4cG9ydCB0eXBlIEF1ZGl0U2V2ZXJpdHkgPSBSZWNvcmQ8UGFja2FnZUF1ZGl0TGV2ZWwsIElzc3VlU2V2ZXJpdHk+O1xuXG5leHBvcnQgZnVuY3Rpb24gZmlsbEF1ZGl0TGV2ZWxNYXBwaW5nKFxuICBtYXBwaW5nOiBQYXJ0aWFsPEF1ZGl0U2V2ZXJpdHk+LFxuKTogQXVkaXRTZXZlcml0eSB7XG4gIHJldHVybiB7XG4gICAgY3JpdGljYWw6IG1hcHBpbmcuY3JpdGljYWwgPz8gZGVmYXVsdEF1ZGl0TGV2ZWxNYXBwaW5nLmNyaXRpY2FsLFxuICAgIGhpZ2g6IG1hcHBpbmcuaGlnaCA/PyBkZWZhdWx0QXVkaXRMZXZlbE1hcHBpbmcuaGlnaCxcbiAgICBtb2RlcmF0ZTogbWFwcGluZy5tb2RlcmF0ZSA/PyBkZWZhdWx0QXVkaXRMZXZlbE1hcHBpbmcubW9kZXJhdGUsXG4gICAgbG93OiBtYXBwaW5nLmxvdyA/PyBkZWZhdWx0QXVkaXRMZXZlbE1hcHBpbmcubG93LFxuICAgIGluZm86IG1hcHBpbmcuaW5mbyA/PyBkZWZhdWx0QXVkaXRMZXZlbE1hcHBpbmcuaW5mbyxcbiAgfTtcbn1cblxuZXhwb3J0IGNvbnN0IGpzUGFja2FnZXNQbHVnaW5Db25maWdTY2hlbWEgPSB6Lm9iamVjdCh7XG4gIGNoZWNrczogelxuICAgIC5hcnJheShwYWNrYWdlQ29tbWFuZFNjaGVtYSwge1xuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdQYWNrYWdlIG1hbmFnZXIgY29tbWFuZHMgdG8gYmUgcnVuLiBEZWZhdWx0cyB0byBib3RoIGF1ZGl0IGFuZCBvdXRkYXRlZC4nLFxuICAgIH0pXG4gICAgLm1pbigxKVxuICAgIC5kZWZhdWx0KFsnYXVkaXQnLCAnb3V0ZGF0ZWQnXSksXG4gIHBhY2thZ2VNYW5hZ2VyOiBwYWNrYWdlTWFuYWdlcklkU2NoZW1hXG4gICAgLmRlc2NyaWJlKCdQYWNrYWdlIG1hbmFnZXIgdG8gYmUgdXNlZC4nKVxuICAgIC5vcHRpb25hbCgpLFxuICBkZXBlbmRlbmN5R3JvdXBzOiB6XG4gICAgLmFycmF5KGRlcGVuZGVuY3lHcm91cFNjaGVtYSlcbiAgICAubWluKDEpXG4gICAgLmRlZmF1bHQoWydwcm9kJywgJ2RldiddKSxcbiAgYXVkaXRMZXZlbE1hcHBpbmc6IHpcbiAgICAucmVjb3JkKHBhY2thZ2VBdWRpdExldmVsU2NoZW1hLCBpc3N1ZVNldmVyaXR5U2NoZW1hLCB7XG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ01hcHBpbmcgb2YgYXVkaXQgbGV2ZWxzIHRvIGlzc3VlIHNldmVyaXR5LiBDdXN0b20gbWFwcGluZyBvciBvdmVycmlkZXMgbWF5IGJlIGVudGVyZWQgbWFudWFsbHksIG90aGVyd2lzZSBoYXMgYSBkZWZhdWx0IHByZXNldC4nLFxuICAgIH0pXG4gICAgLmRlZmF1bHQoZGVmYXVsdEF1ZGl0TGV2ZWxNYXBwaW5nKVxuICAgIC50cmFuc2Zvcm0oZmlsbEF1ZGl0TGV2ZWxNYXBwaW5nKSxcbiAgcGFja2FnZUpzb25QYXRoczogcGFja2FnZUpzb25QYXRoU2NoZW1hLFxufSk7XG5cbmV4cG9ydCB0eXBlIEpTUGFja2FnZXNQbHVnaW5Db25maWcgPSB6LmlucHV0PFxuICB0eXBlb2YganNQYWNrYWdlc1BsdWdpbkNvbmZpZ1NjaGVtYVxuPjtcblxuZXhwb3J0IHR5cGUgRmluYWxKU1BhY2thZ2VzUGx1Z2luQ29uZmlnID0gUmVxdWlyZWQ8XG4gIHouaW5mZXI8dHlwZW9mIGpzUGFja2FnZXNQbHVnaW5Db25maWdTY2hlbWE+XG4+O1xuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvY29uc3RhbnRzLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL2NvbnN0YW50cy50c1wiO2ltcG9ydCB0eXBlIHsgSXNzdWVTZXZlcml0eSB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHR5cGUgeyBEZXBlbmRlbmN5R3JvdXAsIFBhY2thZ2VBdWRpdExldmVsIH0gZnJvbSAnLi9jb25maWcuanMnO1xuaW1wb3J0IHR5cGUgeyBEZXBlbmRlbmN5R3JvdXBMb25nIH0gZnJvbSAnLi9ydW5uZXIvb3V0ZGF0ZWQvdHlwZXMuanMnO1xuXG5leHBvcnQgY29uc3QgZGVmYXVsdEF1ZGl0TGV2ZWxNYXBwaW5nOiBSZWNvcmQ8XG4gIFBhY2thZ2VBdWRpdExldmVsLFxuICBJc3N1ZVNldmVyaXR5XG4+ID0ge1xuICBjcml0aWNhbDogJ2Vycm9yJyxcbiAgaGlnaDogJ2Vycm9yJyxcbiAgbW9kZXJhdGU6ICd3YXJuaW5nJyxcbiAgbG93OiAnd2FybmluZycsXG4gIGluZm86ICdpbmZvJyxcbn07XG5cbmV4cG9ydCBjb25zdCBkZXBlbmRlbmN5R3JvdXBUb0xvbmc6IFJlY29yZDxcbiAgRGVwZW5kZW5jeUdyb3VwLFxuICBEZXBlbmRlbmN5R3JvdXBMb25nXG4+ID0ge1xuICBwcm9kOiAnZGVwZW5kZW5jaWVzJyxcbiAgZGV2OiAnZGV2RGVwZW5kZW5jaWVzJyxcbiAgb3B0aW9uYWw6ICdvcHRpb25hbERlcGVuZGVuY2llcycsXG59O1xuXG5leHBvcnQgY29uc3QgZGVwZW5kZW5jeUdyb3VwV2VpZ2h0czogUmVjb3JkPERlcGVuZGVuY3lHcm91cCwgbnVtYmVyPiA9IHtcbiAgLyogZXNsaW50LWRpc2FibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLW1hZ2ljLW51bWJlcnMgKi9cbiAgcHJvZDogODAsXG4gIGRldjogMTUsXG4gIG9wdGlvbmFsOiA1LFxuICAvKiBlc2xpbnQtZW5hYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1tYWdpYy1udW1iZXJzICovXG59O1xuXG5leHBvcnQgY29uc3QgZGVwZW5kZW5jeURvY3M6IFJlY29yZDxEZXBlbmRlbmN5R3JvdXAsIHN0cmluZz4gPSB7XG4gIHByb2Q6ICdodHRwczovL2NsYXNzaWMueWFybnBrZy5jb20vZG9jcy9kZXBlbmRlbmN5LXR5cGVzI3RvYy1kZXBlbmRlbmNpZXMnLFxuICBkZXY6ICdodHRwczovL2NsYXNzaWMueWFybnBrZy5jb20vZG9jcy9kZXBlbmRlbmN5LXR5cGVzI3RvYy1kZXZkZXBlbmRlbmNpZXMnLFxuICBvcHRpb25hbDpcbiAgICAnaHR0cHM6Ly9jbGFzc2ljLnlhcm5wa2cuY29tL2RvY3MvZGVwZW5kZW5jeS10eXBlcyN0b2Mtb3B0aW9uYWxkZXBlbmRlbmNpZXMnLFxufTtcbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnMvbnBtL25wbS50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL25wbVwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL25wbS9ucG0udHNcIjtpbXBvcnQgeyBvYmplY3RUb0tleXMgfSBmcm9tICdAY29kZS1wdXNodXAvdXRpbHMnO1xuaW1wb3J0IHR5cGUgeyBEZXBlbmRlbmN5R3JvdXAgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xuaW1wb3J0IHsgZmlsdGVyQXVkaXRSZXN1bHQgfSBmcm9tICcuLi8uLi9ydW5uZXIvdXRpbHMuanMnO1xuaW1wb3J0IHsgQ09NTU9OX0FVRElUX0FSR1MsIENPTU1PTl9PVVREQVRFRF9BUkdTIH0gZnJvbSAnLi4vY29uc3RhbnRzLmpzJztcbmltcG9ydCB0eXBlIHsgQXVkaXRSZXN1bHRzLCBQYWNrYWdlTWFuYWdlciB9IGZyb20gJy4uL3R5cGVzLmpzJztcbmltcG9ydCB7IG5wbVRvQXVkaXRSZXN1bHQgfSBmcm9tICcuL2F1ZGl0LXJlc3VsdC5qcyc7XG5pbXBvcnQgeyBucG1Ub091dGRhdGVkUmVzdWx0IH0gZnJvbSAnLi9vdXRkYXRlZC1yZXN1bHQuanMnO1xuXG5jb25zdCBucG1EZXBlbmRlbmN5T3B0aW9uczogUmVjb3JkPERlcGVuZGVuY3lHcm91cCwgc3RyaW5nW10+ID0ge1xuICBwcm9kOiBbJy0tb21pdD1kZXYnLCAnLS1vbWl0PW9wdGlvbmFsJ10sXG4gIGRldjogWyctLWluY2x1ZGU9ZGV2JywgJy0tb21pdD1vcHRpb25hbCddLFxuICBvcHRpb25hbDogWyctLWluY2x1ZGU9b3B0aW9uYWwnLCAnLS1vbWl0PWRldiddLFxufTtcblxuZXhwb3J0IGNvbnN0IG5wbVBhY2thZ2VNYW5hZ2VyOiBQYWNrYWdlTWFuYWdlciA9IHtcbiAgc2x1ZzogJ25wbScsXG4gIG5hbWU6ICdOUE0nLFxuICBjb21tYW5kOiAnbnBtJyxcbiAgaWNvbjogJ25wbScsXG4gIGRvY3M6IHtcbiAgICBob21lcGFnZTogJ2h0dHBzOi8vZG9jcy5ucG1qcy5jb20vJyxcbiAgICBhdWRpdDogJ2h0dHBzOi8vZG9jcy5ucG1qcy5jb20vY2xpL2NvbW1hbmRzL25wbS1hdWRpdCcsXG4gICAgb3V0ZGF0ZWQ6ICdodHRwczovL2RvY3MubnBtanMuY29tL2NsaS9jb21tYW5kcy9ucG0tb3V0ZGF0ZWQnLFxuICB9LFxuICBhdWRpdDoge1xuICAgIGdldENvbW1hbmRBcmdzOiBncm91cERlcCA9PiBbXG4gICAgICAuLi5DT01NT05fQVVESVRfQVJHUyxcbiAgICAgIC4uLm5wbURlcGVuZGVuY3lPcHRpb25zW2dyb3VwRGVwXSxcbiAgICAgICctLWF1ZGl0LWxldmVsPW5vbmUnLFxuICAgIF0sXG4gICAgdW5pZnlSZXN1bHQ6IG5wbVRvQXVkaXRSZXN1bHQsXG4gICAgLy8gcHJvZCBkZXBlbmRlbmNpZXMgbmVlZCB0byBiZSBmaWx0ZXJlZCBvdXQgbWFudWFsbHkgc2luY2UgdjEwXG4gICAgcG9zdFByb2Nlc3NSZXN1bHQ6IChyZXN1bHRzOiBBdWRpdFJlc3VsdHMpID0+IHtcbiAgICAgIGNvbnN0IGRlcEdyb3VwcyA9IG9iamVjdFRvS2V5cyhyZXN1bHRzKTtcbiAgICAgIGNvbnN0IGRldkZpbHRlciA9XG4gICAgICAgIHJlc3VsdHMuZGV2ICYmIHJlc3VsdHMucHJvZFxuICAgICAgICAgID8gZmlsdGVyQXVkaXRSZXN1bHQocmVzdWx0cy5kZXYsICduYW1lJywgcmVzdWx0cy5wcm9kKVxuICAgICAgICAgIDogcmVzdWx0cy5kZXY7XG4gICAgICBjb25zdCBvcHRpb25hbEZpbHRlciA9XG4gICAgICAgIHJlc3VsdHMub3B0aW9uYWwgJiYgcmVzdWx0cy5wcm9kXG4gICAgICAgICAgPyBmaWx0ZXJBdWRpdFJlc3VsdChyZXN1bHRzLm9wdGlvbmFsLCAnbmFtZScsIHJlc3VsdHMucHJvZClcbiAgICAgICAgICA6IHJlc3VsdHMub3B0aW9uYWw7XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIC4uLihkZXBHcm91cHMuaW5jbHVkZXMoJ3Byb2QnKSAmJiB7IHByb2Q6IHJlc3VsdHMucHJvZCB9KSxcbiAgICAgICAgLi4uKGRlcEdyb3Vwcy5pbmNsdWRlcygnZGV2JykgJiYgeyBkZXY6IGRldkZpbHRlciB9KSxcbiAgICAgICAgLi4uKGRlcEdyb3Vwcy5pbmNsdWRlcygnb3B0aW9uYWwnKSAmJiB7IG9wdGlvbmFsOiBvcHRpb25hbEZpbHRlciB9KSxcbiAgICAgIH07XG4gICAgfSxcbiAgfSxcbiAgb3V0ZGF0ZWQ6IHtcbiAgICBjb21tYW5kQXJnczogWy4uLkNPTU1PTl9PVVREQVRFRF9BUkdTLCAnLS1sb25nJ10sXG4gICAgdW5pZnlSZXN1bHQ6IG5wbVRvT3V0ZGF0ZWRSZXN1bHQsXG4gIH0sXG59O1xuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcnVubmVyL3V0aWxzLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3J1bm5lclwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9ydW5uZXIvdXRpbHMudHNcIjtpbXBvcnQgcGF0aCBmcm9tICdub2RlOnBhdGgnO1xuaW1wb3J0IHtcbiAgY3Jhd2xGaWxlU3lzdGVtLFxuICBvYmplY3RGcm9tRW50cmllcyxcbiAgb2JqZWN0VG9LZXlzLFxuICByZWFkSnNvbkZpbGUsXG59IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0UmVzdWx0LCBWdWxuZXJhYmlsaXR5IH0gZnJvbSAnLi9hdWRpdC90eXBlcy5qcyc7XG5pbXBvcnQge1xuICB0eXBlIERlcGVuZGVuY3lHcm91cExvbmcsXG4gIHR5cGUgRGVwZW5kZW5jeVRvdGFscyxcbiAgdHlwZSBQYWNrYWdlSnNvbixcbiAgZGVwZW5kZW5jeUdyb3VwTG9uZyxcbn0gZnJvbSAnLi9vdXRkYXRlZC90eXBlcy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBmaWx0ZXJBdWRpdFJlc3VsdChcbiAgcmVzdWx0OiBBdWRpdFJlc3VsdCxcbiAga2V5OiBrZXlvZiBWdWxuZXJhYmlsaXR5LFxuICByZWZlcmVuY2VSZXN1bHQ/OiBBdWRpdFJlc3VsdCxcbik6IEF1ZGl0UmVzdWx0IHtcbiAgaWYgKHJlc3VsdC52dWxuZXJhYmlsaXRpZXMubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGNvbnN0IHVuaXF1ZVJlc3VsdCA9IHJlc3VsdC52dWxuZXJhYmlsaXRpZXMucmVkdWNlPEF1ZGl0UmVzdWx0PihcbiAgICAoYWNjLCByZWYpID0+IHtcbiAgICAgIGNvbnN0IG1hdGNoUmVmZXJlbmNlID0gcmVmZXJlbmNlUmVzdWx0ID8/IGFjYztcbiAgICAgIGNvbnN0IGlzTWF0Y2ggPSBtYXRjaFJlZmVyZW5jZS52dWxuZXJhYmlsaXRpZXNcbiAgICAgICAgLm1hcCh2dWxuZXJhYmlsaXR5ID0+IHZ1bG5lcmFiaWxpdHlba2V5XSlcbiAgICAgICAgLmluY2x1ZGVzKHJlZltrZXldKTtcblxuICAgICAgaWYgKGlzTWF0Y2gpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICB2dWxuZXJhYmlsaXRpZXM6IGFjYy52dWxuZXJhYmlsaXRpZXMsXG4gICAgICAgICAgc3VtbWFyeToge1xuICAgICAgICAgICAgLi4uYWNjLnN1bW1hcnksXG4gICAgICAgICAgICBbcmVmLnNldmVyaXR5XTogYWNjLnN1bW1hcnlbcmVmLnNldmVyaXR5XSAtIDEsXG4gICAgICAgICAgICB0b3RhbDogYWNjLnN1bW1hcnkudG90YWwgLSAxLFxuICAgICAgICAgIH0sXG4gICAgICAgIH07XG4gICAgICB9XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIHZ1bG5lcmFiaWxpdGllczogWy4uLmFjYy52dWxuZXJhYmlsaXRpZXMsIHJlZl0sXG4gICAgICAgIHN1bW1hcnk6IGFjYy5zdW1tYXJ5LFxuICAgICAgfTtcbiAgICB9LFxuICAgIHsgdnVsbmVyYWJpbGl0aWVzOiBbXSwgc3VtbWFyeTogcmVzdWx0LnN1bW1hcnkgfSxcbiAgKTtcblxuICByZXR1cm4ge1xuICAgIHZ1bG5lcmFiaWxpdGllczogdW5pcXVlUmVzdWx0LnZ1bG5lcmFiaWxpdGllcyxcbiAgICBzdW1tYXJ5OiB1bmlxdWVSZXN1bHQuc3VtbWFyeSxcbiAgfTtcbn1cblxuLy8gVE9ETzogdXNlIC5naXRpZ25vcmVcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBmaW5kQWxsUGFja2FnZUpzb24oKTogUHJvbWlzZTxzdHJpbmdbXT4ge1xuICByZXR1cm4gKFxuICAgIGF3YWl0IGNyYXdsRmlsZVN5c3RlbSh7XG4gICAgICBkaXJlY3Rvcnk6ICcuJyxcbiAgICAgIHBhdHRlcm46IC8oXnxbXFxcXC9dKXBhY2thZ2VcXC5qc29uJC8sXG4gICAgfSlcbiAgKS5maWx0ZXIoXG4gICAgZmlsZVBhdGggPT5cbiAgICAgICFmaWxlUGF0aC5zdGFydHNXaXRoKGBub2RlX21vZHVsZXMke3BhdGguc2VwfWApICYmXG4gICAgICAhZmlsZVBhdGguaW5jbHVkZXMoYCR7cGF0aC5zZXB9bm9kZV9tb2R1bGVzJHtwYXRoLnNlcH1gKSAmJlxuICAgICAgIWZpbGVQYXRoLnN0YXJ0c1dpdGgoYC5ueCR7cGF0aC5zZXB9YCksXG4gICk7XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRUb3RhbERlcGVuZGVuY2llcyhcbiAgcGFja2FnZUpzb25QYXRoczogc3RyaW5nW10sXG4pOiBQcm9taXNlPERlcGVuZGVuY3lUb3RhbHM+IHtcbiAgY29uc3QgcGFyc2VkRGVwcyA9IGF3YWl0IFByb21pc2UuYWxsKFxuICAgIHBhY2thZ2VKc29uUGF0aHMubWFwKHJlYWRKc29uRmlsZTxQYWNrYWdlSnNvbj4pLFxuICApO1xuXG4gIGNvbnN0IG1lcmdlZERlcHMgPSBwYXJzZWREZXBzLnJlZHVjZTxSZWNvcmQ8RGVwZW5kZW5jeUdyb3VwTG9uZywgc3RyaW5nW10+PihcbiAgICAoYWNjLCBkZXBNYXBwZXIpID0+XG4gICAgICBvYmplY3RGcm9tRW50cmllcyhcbiAgICAgICAgZGVwZW5kZW5jeUdyb3VwTG9uZy5tYXAoZ3JvdXAgPT4ge1xuICAgICAgICAgIGNvbnN0IGRlcHMgPSBkZXBNYXBwZXJbZ3JvdXBdO1xuICAgICAgICAgIHJldHVybiBbXG4gICAgICAgICAgICBncm91cCxcbiAgICAgICAgICAgIFsuLi5hY2NbZ3JvdXBdLCAuLi4oZGVwcyA9PSBudWxsID8gW10gOiBvYmplY3RUb0tleXMoZGVwcykpXSxcbiAgICAgICAgICBdO1xuICAgICAgICB9KSxcbiAgICAgICksXG4gICAgeyBkZXBlbmRlbmNpZXM6IFtdLCBkZXZEZXBlbmRlbmNpZXM6IFtdLCBvcHRpb25hbERlcGVuZGVuY2llczogW10gfSxcbiAgKTtcbiAgcmV0dXJuIG9iamVjdEZyb21FbnRyaWVzKFxuICAgIG9iamVjdFRvS2V5cyhtZXJnZWREZXBzKS5tYXAoZGVwcyA9PiBbXG4gICAgICBkZXBzLFxuICAgICAgbmV3IFNldChtZXJnZWREZXBzW2RlcHNdKS5zaXplLFxuICAgIF0pLFxuICApO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy9jb25zdGFudHMudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vyc1wiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL2NvbnN0YW50cy50c1wiO2V4cG9ydCBjb25zdCBDT01NT05fQVVESVRfQVJHUyA9IFsnYXVkaXQnLCAnLS1qc29uJ107XG5leHBvcnQgY29uc3QgQ09NTU9OX09VVERBVEVEX0FSR1MgPSBbJ291dGRhdGVkJywgJy0tanNvbiddO1xuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy9ucG0vYXVkaXQtcmVzdWx0LnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnMvbnBtXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnMvbnBtL2F1ZGl0LXJlc3VsdC50c1wiO2ltcG9ydCB7IG9iamVjdFRvRW50cmllcyB9IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0UmVzdWx0LCBWdWxuZXJhYmlsaXR5IH0gZnJvbSAnLi4vLi4vcnVubmVyL2F1ZGl0L3R5cGVzLmpzJztcbmltcG9ydCB0eXBlIHtcbiAgTnBtQWR2aXNvcnksXG4gIE5wbUF1ZGl0UmVzdWx0SnNvbixcbiAgTnBtRml4SW5mb3JtYXRpb24sXG4gIE5wbVZ1bG5lcmFiaWxpdGllcyxcbn0gZnJvbSAnLi90eXBlcy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBucG1Ub0F1ZGl0UmVzdWx0KG91dHB1dDogc3RyaW5nKTogQXVkaXRSZXN1bHQge1xuICBjb25zdCBucG1BdWRpdCA9IEpTT04ucGFyc2Uob3V0cHV0KSBhcyBOcG1BdWRpdFJlc3VsdEpzb247XG5cbiAgY29uc3QgdnVsbmVyYWJpbGl0aWVzID0gb2JqZWN0VG9FbnRyaWVzKG5wbUF1ZGl0LnZ1bG5lcmFiaWxpdGllcykubWFwKFxuICAgIChbbmFtZSwgZGV0YWlsXSk6IFZ1bG5lcmFiaWxpdHkgPT4ge1xuICAgICAgY29uc3QgYWR2aXNvcnkgPSBucG1Ub0Fkdmlzb3J5KG5hbWUsIG5wbUF1ZGl0LnZ1bG5lcmFiaWxpdGllcyk7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBuYW1lOiBuYW1lLnRvU3RyaW5nKCksXG4gICAgICAgIHNldmVyaXR5OiBkZXRhaWwuc2V2ZXJpdHksXG4gICAgICAgIHZlcnNpb25SYW5nZTogZGV0YWlsLnJhbmdlLFxuICAgICAgICBkaXJlY3REZXBlbmRlbmN5OiBkZXRhaWwuaXNEaXJlY3QgPyB0cnVlIDogKGRldGFpbC5lZmZlY3RzWzBdID8/ICcnKSxcbiAgICAgICAgZml4SW5mb3JtYXRpb246IG5wbVRvRml4SW5mb3JtYXRpb24oZGV0YWlsLmZpeEF2YWlsYWJsZSksXG4gICAgICAgIC4uLihhZHZpc29yeSAhPSBudWxsICYmIHtcbiAgICAgICAgICB0aXRsZTogYWR2aXNvcnkudGl0bGUsXG4gICAgICAgICAgdXJsOiBhZHZpc29yeS51cmwsXG4gICAgICAgIH0pLFxuICAgICAgfTtcbiAgICB9LFxuICApO1xuXG4gIHJldHVybiB7XG4gICAgdnVsbmVyYWJpbGl0aWVzLFxuICAgIHN1bW1hcnk6IG5wbUF1ZGl0Lm1ldGFkYXRhLnZ1bG5lcmFiaWxpdGllcyxcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG5wbVRvRml4SW5mb3JtYXRpb24oXG4gIGZpeEF2YWlsYWJsZTogYm9vbGVhbiB8IE5wbUZpeEluZm9ybWF0aW9uLFxuKTogc3RyaW5nIHtcbiAgaWYgKHR5cGVvZiBmaXhBdmFpbGFibGUgPT09ICdib29sZWFuJykge1xuICAgIHJldHVybiBmaXhBdmFpbGFibGUgPyAnRml4IGlzIGF2YWlsYWJsZS4nIDogJyc7XG4gIH1cblxuICByZXR1cm4gYEZpeCBhdmFpbGFibGU6IFVwZGF0ZSBcXGAke2ZpeEF2YWlsYWJsZS5uYW1lfVxcYCB0byB2ZXJzaW9uICoqJHtcbiAgICBmaXhBdmFpbGFibGUudmVyc2lvblxuICB9Kioke2ZpeEF2YWlsYWJsZS5pc1NlbVZlck1ham9yID8gJyAoYnJlYWtpbmcgY2hhbmdlKS4nIDogJy4nfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBucG1Ub0Fkdmlzb3J5KFxuICBuYW1lOiBzdHJpbmcsXG4gIHZ1bG5lcmFiaWxpdGllczogTnBtVnVsbmVyYWJpbGl0aWVzLFxuICBwcmV2Tm9kZXMgPSBuZXcgU2V0PHN0cmluZz4oKSxcbik6IE5wbUFkdmlzb3J5IHwgbnVsbCB7XG4gIGNvbnN0IGFkdmlzb3J5ID0gdnVsbmVyYWJpbGl0aWVzW25hbWVdPy52aWE7XG5cbiAgaWYgKFxuICAgIEFycmF5LmlzQXJyYXkoYWR2aXNvcnkpICYmXG4gICAgYWR2aXNvcnkubGVuZ3RoID4gMCAmJlxuICAgIHR5cGVvZiBhZHZpc29yeVswXSA9PT0gJ29iamVjdCdcbiAgKSB7XG4gICAgcmV0dXJuIHsgdGl0bGU6IGFkdmlzb3J5WzBdLnRpdGxlLCB1cmw6IGFkdmlzb3J5WzBdLnVybCB9O1xuICB9XG5cbiAgLy8gQ3Jvc3MtcmVmZXJlbmNlcyBhbm90aGVyIHZ1bG5lcmFiaWxpdHlcbiAgaWYgKFxuICAgIEFycmF5LmlzQXJyYXkoYWR2aXNvcnkpICYmXG4gICAgYWR2aXNvcnkubGVuZ3RoID4gMCAmJlxuICAgIGFkdmlzb3J5LmV2ZXJ5KCh2YWx1ZSk6IHZhbHVlIGlzIHN0cmluZyA9PiB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnKVxuICApIHtcbiAgICAvKiBlc2xpbnQtZGlzYWJsZSBmdW5jdGlvbmFsL25vLWxldCwgZnVuY3Rpb25hbC9pbW11dGFibGUtZGF0YSwgZnVuY3Rpb25hbC9uby1sb29wLXN0YXRlbWVudHMsIHByZWZlci1jb25zdCAqL1xuICAgIGxldCBhZHZpc29yeUluZm86IE5wbUFkdmlzb3J5IHwgbnVsbCA9IG51bGw7XG4gICAgbGV0IG5ld1JlZmVyZW5jZXM6IHN0cmluZ1tdID0gW107XG4gICAgbGV0IGFkdmlzb3J5SW5mb0ZvdW5kID0gZmFsc2U7XG4gICAgLyogZXNsaW50LWVuYWJsZSBmdW5jdGlvbmFsL25vLWxldCwgcHJlZmVyLWNvbnN0ICovXG5cbiAgICBmb3IgKGNvbnN0IHZpYSBvZiBhZHZpc29yeSkge1xuICAgICAgaWYgKCFwcmV2Tm9kZXMuaGFzKHZpYSkpIHtcbiAgICAgICAgbmV3UmVmZXJlbmNlcy5wdXNoKHZpYSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgd2hpbGUgKG5ld1JlZmVyZW5jZXMubGVuZ3RoID4gMCAmJiAhYWR2aXNvcnlJbmZvRm91bmQpIHtcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tbm9uLW51bGwtYXNzZXJ0aW9uXG4gICAgICBjb25zdCByZWYgPSBuZXdSZWZlcmVuY2VzLnBvcCgpITtcbiAgICAgIHByZXZOb2Rlcy5hZGQocmVmKTtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IG5wbVRvQWR2aXNvcnkocmVmLCB2dWxuZXJhYmlsaXRpZXMsIHByZXZOb2Rlcyk7XG5cbiAgICAgIGlmIChyZXN1bHQgIT0gbnVsbCkge1xuICAgICAgICBhZHZpc29yeUluZm8gPSB7IHRpdGxlOiByZXN1bHQudGl0bGUsIHVybDogcmVzdWx0LnVybCB9O1xuICAgICAgICBhZHZpc29yeUluZm9Gb3VuZCA9IHRydWU7XG4gICAgICB9XG4gICAgfVxuICAgIC8qIGVzbGludC1lbmFibGUgZnVuY3Rpb25hbC9pbW11dGFibGUtZGF0YSwgZnVuY3Rpb25hbC9uby1sb29wLXN0YXRlbWVudHMgKi9cblxuICAgIHJldHVybiBhZHZpc29yeUluZm87XG4gIH1cblxuICByZXR1cm4gbnVsbDtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnMvbnBtL291dGRhdGVkLXJlc3VsdC50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL25wbVwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL25wbS9vdXRkYXRlZC1yZXN1bHQudHNcIjtpbXBvcnQgeyBvYmplY3RUb0VudHJpZXMgfSBmcm9tICdAY29kZS1wdXNodXAvdXRpbHMnO1xuaW1wb3J0IHR5cGUgeyBPdXRkYXRlZFJlc3VsdCB9IGZyb20gJy4uLy4uL3J1bm5lci9vdXRkYXRlZC90eXBlcy5qcyc7XG5pbXBvcnQgdHlwZSB7IE5wbU5vcm1hbGl6ZWRPdmVydmlldywgTnBtT3V0ZGF0ZWRSZXN1bHRKc29uIH0gZnJvbSAnLi90eXBlcy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBucG1Ub091dGRhdGVkUmVzdWx0KG91dHB1dDogc3RyaW5nKTogT3V0ZGF0ZWRSZXN1bHQge1xuICBjb25zdCBucG1PdXRkYXRlZCA9IEpTT04ucGFyc2Uob3V0cHV0KSBhcyBOcG1PdXRkYXRlZFJlc3VsdEpzb247XG4gIC8vIGN1cnJlbnQgbWlnaHQgYmUgbWlzc2luZyBpbiBzb21lIGNhc2VzXG4gIC8vIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzQyMjY3MTAxL25wbS1vdXRkYXRlZC1jb21tYW5kLXNob3dzLW1pc3NpbmctaW4tY3VycmVudC12ZXJzaW9uXG4gIHJldHVybiBvYmplY3RUb0VudHJpZXMobnBtT3V0ZGF0ZWQpXG4gICAgLmZpbHRlcihcbiAgICAgIChlbnRyeSk6IGVudHJ5IGlzIFtzdHJpbmcsIE5wbU5vcm1hbGl6ZWRPdmVydmlld10gPT5cbiAgICAgICAgZW50cnlbMV0uY3VycmVudCAhPSBudWxsLFxuICAgIClcbiAgICAubWFwKChbbmFtZSwgb3ZlcnZpZXddKSA9PiAoe1xuICAgICAgbmFtZSxcbiAgICAgIGN1cnJlbnQ6IG92ZXJ2aWV3LmN1cnJlbnQsXG4gICAgICBsYXRlc3Q6IG92ZXJ2aWV3LmxhdGVzdCxcbiAgICAgIHR5cGU6IG92ZXJ2aWV3LnR5cGUsXG4gICAgICAuLi4ob3ZlcnZpZXcuaG9tZXBhZ2UgIT0gbnVsbCAmJiB7IHVybDogb3ZlcnZpZXcuaG9tZXBhZ2UgfSksXG4gICAgfSkpO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy9wbnBtL3BucG0udHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy9wbnBtXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnMvcG5wbS9wbnBtLnRzXCI7aW1wb3J0IHsgb2JqZWN0VG9LZXlzIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB0eXBlIHsgRGVwZW5kZW5jeUdyb3VwIH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcbmltcG9ydCB7IGZpbHRlckF1ZGl0UmVzdWx0IH0gZnJvbSAnLi4vLi4vcnVubmVyL3V0aWxzLmpzJztcbmltcG9ydCB7IENPTU1PTl9BVURJVF9BUkdTLCBDT01NT05fT1VUREFURURfQVJHUyB9IGZyb20gJy4uL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0UmVzdWx0cywgUGFja2FnZU1hbmFnZXIgfSBmcm9tICcuLi90eXBlcy5qcyc7XG5pbXBvcnQgeyBwbnBtVG9BdWRpdFJlc3VsdCB9IGZyb20gJy4vYXVkaXQtcmVzdWx0LmpzJztcbmltcG9ydCB7IHBucG1Ub091dGRhdGVkUmVzdWx0IH0gZnJvbSAnLi9vdXRkYXRlZC1yZXN1bHQuanMnO1xuXG5jb25zdCBwbnBtRGVwZW5kZW5jeU9wdGlvbnM6IFJlY29yZDxEZXBlbmRlbmN5R3JvdXAsIHN0cmluZ1tdPiA9IHtcbiAgcHJvZDogWyctLXByb2QnLCAnLS1uby1vcHRpb25hbCddLFxuICBkZXY6IFsnLS1kZXYnLCAnLS1uby1vcHRpb25hbCddLFxuICBvcHRpb25hbDogW10sXG59O1xuXG5leHBvcnQgY29uc3QgcG5wbVBhY2thZ2VNYW5hZ2VyOiBQYWNrYWdlTWFuYWdlciA9IHtcbiAgc2x1ZzogJ3BucG0nLFxuICBuYW1lOiAncG5wbScsXG4gIGNvbW1hbmQ6ICdwbnBtJyxcbiAgaWNvbjogJ3BucG0nLFxuICBkb2NzOiB7XG4gICAgaG9tZXBhZ2U6ICdodHRwczovL3BucG0uaW8vcG5wbS1jbGknLFxuICAgIGF1ZGl0OiAnaHR0cHM6Ly9wbnBtLmlvL2NsaS9hdWRpdC8nLFxuICAgIG91dGRhdGVkOiAnaHR0cHM6Ly9wbnBtLmlvL2NsaS9vdXRkYXRlZCcsXG4gIH0sXG4gIGF1ZGl0OiB7XG4gICAgZ2V0Q29tbWFuZEFyZ3M6IGdyb3VwRGVwID0+IFtcbiAgICAgIC4uLkNPTU1PTl9BVURJVF9BUkdTLFxuICAgICAgLi4ucG5wbURlcGVuZGVuY3lPcHRpb25zW2dyb3VwRGVwXSxcbiAgICBdLFxuICAgIGlnbm9yZUV4aXRDb2RlOiB0cnVlLFxuICAgIHVuaWZ5UmVzdWx0OiBwbnBtVG9BdWRpdFJlc3VsdCxcbiAgICAvLyBvcHRpb25hbCBkZXBlbmRlbmNpZXMgZG9uJ3QgaGF2ZSBhbiBleGNsdXNpdmUgb3B0aW9uIHNvIHRoZXkgbmVlZCBkdXBsaWNhdGVzIGZpbHRlcmVkIG91dFxuICAgIHBvc3RQcm9jZXNzUmVzdWx0OiAocmVzdWx0czogQXVkaXRSZXN1bHRzKSA9PiB7XG4gICAgICBjb25zdCBkZXBHcm91cHMgPSBvYmplY3RUb0tleXMocmVzdWx0cyk7XG4gICAgICBjb25zdCBwcm9kRmlsdGVyID1cbiAgICAgICAgcmVzdWx0cy5vcHRpb25hbCAmJiByZXN1bHRzLnByb2RcbiAgICAgICAgICA/IGZpbHRlckF1ZGl0UmVzdWx0KHJlc3VsdHMub3B0aW9uYWwsICdpZCcsIHJlc3VsdHMucHJvZClcbiAgICAgICAgICA6IHJlc3VsdHMub3B0aW9uYWw7XG4gICAgICBjb25zdCBkZXZGaWx0ZXIgPVxuICAgICAgICBwcm9kRmlsdGVyICYmIHJlc3VsdHMuZGV2XG4gICAgICAgICAgPyBmaWx0ZXJBdWRpdFJlc3VsdChwcm9kRmlsdGVyLCAnaWQnLCByZXN1bHRzLmRldilcbiAgICAgICAgICA6IHJlc3VsdHMub3B0aW9uYWw7XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIC4uLihkZXBHcm91cHMuaW5jbHVkZXMoJ3Byb2QnKSAmJiB7IHByb2Q6IHJlc3VsdHMucHJvZCB9KSxcbiAgICAgICAgLi4uKGRlcEdyb3Vwcy5pbmNsdWRlcygnZGV2JykgJiYgeyBkZXY6IHJlc3VsdHMuZGV2IH0pLFxuICAgICAgICAuLi4ocmVzdWx0cy5vcHRpb25hbCAmJiB7IG9wdGlvbmFsOiBkZXZGaWx0ZXIgfSksXG4gICAgICB9O1xuICAgIH0sXG4gIH0sXG4gIG91dGRhdGVkOiB7XG4gICAgY29tbWFuZEFyZ3M6IENPTU1PTl9PVVREQVRFRF9BUkdTLFxuICAgIHVuaWZ5UmVzdWx0OiBwbnBtVG9PdXRkYXRlZFJlc3VsdCxcbiAgfSxcbn07XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL3BucG0vb3V0ZGF0ZWQtcmVzdWx0LnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnMvcG5wbVwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL3BucG0vb3V0ZGF0ZWQtcmVzdWx0LnRzXCI7aW1wb3J0IHsgb2JqZWN0VG9FbnRyaWVzIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB0eXBlIHsgT3V0ZGF0ZWRSZXN1bHQgfSBmcm9tICcuLi8uLi9ydW5uZXIvb3V0ZGF0ZWQvdHlwZXMuanMnO1xuaW1wb3J0IHR5cGUgeyBQbnBtT3V0ZGF0ZWRSZXN1bHRKc29uIH0gZnJvbSAnLi90eXBlcy5qcyc7XG5pbXBvcnQgeyBmaWx0ZXJPdXRXYXJuaW5ncyB9IGZyb20gJy4vdXRpbHMuanMnO1xuXG5leHBvcnQgZnVuY3Rpb24gcG5wbVRvT3V0ZGF0ZWRSZXN1bHQob3V0cHV0OiBzdHJpbmcpOiBPdXRkYXRlZFJlc3VsdCB7XG4gIGNvbnN0IHBucG1PdXRkYXRlZCA9IEpTT04ucGFyc2UoXG4gICAgZmlsdGVyT3V0V2FybmluZ3Mob3V0cHV0KSxcbiAgKSBhcyBQbnBtT3V0ZGF0ZWRSZXN1bHRKc29uO1xuXG4gIHJldHVybiBvYmplY3RUb0VudHJpZXMocG5wbU91dGRhdGVkKS5tYXAoXG4gICAgKFtuYW1lLCB7IGN1cnJlbnQsIGxhdGVzdCwgZGVwZW5kZW5jeVR5cGU6IHR5cGUgfV0pID0+ICh7XG4gICAgICBuYW1lLFxuICAgICAgY3VycmVudCxcbiAgICAgIGxhdGVzdCxcbiAgICAgIHR5cGUsXG4gICAgfSksXG4gICk7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL3lhcm4tY2xhc3NpYy9hdWRpdC1yZXN1bHQudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy95YXJuLWNsYXNzaWNcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy95YXJuLWNsYXNzaWMvYXVkaXQtcmVzdWx0LnRzXCI7aW1wb3J0IHsgZnJvbUpzb25MaW5lcyB9IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0UmVzdWx0LCBWdWxuZXJhYmlsaXR5IH0gZnJvbSAnLi4vLi4vcnVubmVyL2F1ZGl0L3R5cGVzLmpzJztcbmltcG9ydCB7IGZpbHRlckF1ZGl0UmVzdWx0IH0gZnJvbSAnLi4vLi4vcnVubmVyL3V0aWxzLmpzJztcbmltcG9ydCB0eXBlIHtcbiAgWWFybnYxQXVkaXRBZHZpc29yeSxcbiAgWWFybnYxQXVkaXRSZXN1bHRKc29uLFxuICBZYXJudjFBdWRpdFN1bW1hcnksXG59IGZyb20gJy4vdHlwZXMuanMnO1xuXG5leHBvcnQgZnVuY3Rpb24geWFybnYxVG9BdWRpdFJlc3VsdChvdXRwdXQ6IHN0cmluZyk6IEF1ZGl0UmVzdWx0IHtcbiAgY29uc3QgeWFybnYxUmVzdWx0ID0gZnJvbUpzb25MaW5lczxZYXJudjFBdWRpdFJlc3VsdEpzb24+KG91dHB1dCk7XG4gIGNvbnN0IFt5YXJudjFBZHZpc29yeSwgeWFybnYxU3VtbWFyeV0gPSB2YWxpZGF0ZVlhcm52MVJlc3VsdCh5YXJudjFSZXN1bHQpO1xuXG4gIGNvbnN0IHZ1bG5lcmFiaWxpdGllcyA9IHlhcm52MUFkdmlzb3J5Lm1hcChcbiAgICAoeyBkYXRhOiB7IHJlc29sdXRpb24sIGFkdmlzb3J5IH0gfSk6IFZ1bG5lcmFiaWxpdHkgPT4ge1xuICAgICAgY29uc3QgeyBpZCwgcGF0aCB9ID0gcmVzb2x1dGlvbjtcbiAgICAgIGNvbnN0IGRpcmVjdERlcGVuZGVuY3kgPSBwYXRoLnNsaWNlKDAsIHBhdGguaW5kZXhPZignPicpKTtcblxuICAgICAgY29uc3Qge1xuICAgICAgICBtb2R1bGVfbmFtZTogbmFtZSxcbiAgICAgICAgdGl0bGUsXG4gICAgICAgIHVybCxcbiAgICAgICAgc2V2ZXJpdHksXG4gICAgICAgIHZ1bG5lcmFibGVfdmVyc2lvbnM6IHZlcnNpb25SYW5nZSxcbiAgICAgICAgcmVjb21tZW5kYXRpb246IGZpeEluZm9ybWF0aW9uLFxuICAgICAgfSA9IGFkdmlzb3J5O1xuXG4gICAgICByZXR1cm4ge1xuICAgICAgICBuYW1lLFxuICAgICAgICB0aXRsZSxcbiAgICAgICAgaWQsXG4gICAgICAgIHVybCxcbiAgICAgICAgc2V2ZXJpdHksXG4gICAgICAgIHZlcnNpb25SYW5nZSxcbiAgICAgICAgZGlyZWN0RGVwZW5kZW5jeTogbmFtZSA9PT0gZGlyZWN0RGVwZW5kZW5jeSA/IHRydWUgOiBkaXJlY3REZXBlbmRlbmN5LFxuICAgICAgICBmaXhJbmZvcm1hdGlvbixcbiAgICAgIH07XG4gICAgfSxcbiAgKTtcblxuICBjb25zdCBzdW1tYXJ5ID0ge1xuICAgIC4uLnlhcm52MVN1bW1hcnkuZGF0YS52dWxuZXJhYmlsaXRpZXMsXG4gICAgdG90YWw6IE9iamVjdC52YWx1ZXMoeWFybnYxU3VtbWFyeS5kYXRhLnZ1bG5lcmFiaWxpdGllcykucmVkdWNlKFxuICAgICAgKGFjYywgYW1vdW50KSA9PiBhY2MgKyBhbW91bnQsXG4gICAgICAwLFxuICAgICksXG4gIH07XG5cbiAgLy8gZHVwbGljYXRlcyBhcmUgZmlsdGVyZWQgb3V0IGJhc2VkIG9uIHRoZWlyIElEXG4gIHJldHVybiBmaWx0ZXJBdWRpdFJlc3VsdCh7IHZ1bG5lcmFiaWxpdGllcywgc3VtbWFyeSB9LCAnaWQnKTtcbn1cblxuZnVuY3Rpb24gdmFsaWRhdGVZYXJudjFSZXN1bHQoXG4gIHJlc3VsdDogWWFybnYxQXVkaXRSZXN1bHRKc29uLFxuKTogW1lhcm52MUF1ZGl0QWR2aXNvcnlbXSwgWWFybnYxQXVkaXRTdW1tYXJ5XSB7XG4gIGNvbnN0IHN1bW1hcnkgPSByZXN1bHQuYXQoLTEpO1xuICBpZiAoc3VtbWFyeT8udHlwZSAhPT0gJ2F1ZGl0U3VtbWFyeScpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgWWFybiB2MSBhdWRpdCByZXN1bHQgLSBubyBzdW1tYXJ5IGZvdW5kLicpO1xuICB9XG5cbiAgY29uc3QgdnVsbmVyYWJpbGl0aWVzID0gcmVzdWx0LmZpbHRlcihcbiAgICAoaXRlbSk6IGl0ZW0gaXMgWWFybnYxQXVkaXRBZHZpc29yeSA9PiBpdGVtLnR5cGUgPT09ICdhdWRpdEFkdmlzb3J5JyxcbiAgKTtcblxuICByZXR1cm4gW3Z1bG5lcmFiaWxpdGllcywgc3VtbWFyeV07XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL3lhcm4tY2xhc3NpYy9vdXRkYXRlZC1yZXN1bHQudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy95YXJuLWNsYXNzaWNcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy95YXJuLWNsYXNzaWMvb3V0ZGF0ZWQtcmVzdWx0LnRzXCI7aW1wb3J0IHtcbiAgZnJvbUpzb25MaW5lcyxcbiAgb2JqZWN0RnJvbUVudHJpZXMsXG4gIG9iamVjdFRvRW50cmllcyxcbiAgb2JqZWN0VG9LZXlzLFxufSBmcm9tICdAY29kZS1wdXNodXAvdXRpbHMnO1xuaW1wb3J0IHR5cGUge1xuICBPdXRkYXRlZERlcGVuZGVuY3ksXG4gIE91dGRhdGVkUmVzdWx0LFxufSBmcm9tICcuLi8uLi9ydW5uZXIvb3V0ZGF0ZWQvdHlwZXMuanMnO1xuaW1wb3J0IHtcbiAgUkVRVUlSRURfT1VUREFURURfRklFTERTLFxuICBvdXRkYXRlZHRvRmllbGRNYXBwZXIsXG59IGZyb20gJy4vY29uc3RhbnRzLmpzJztcbmltcG9ydCB7XG4gIHR5cGUgWWFybnYxRmllbGROYW1lLFxuICB0eXBlIFlhcm52MU91dGRhdGVkUmVzdWx0SnNvbixcbiAgeWFybnYxRmllbGROYW1lcyxcbn0gZnJvbSAnLi90eXBlcy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiB5YXJudjFUb091dGRhdGVkUmVzdWx0KG91dHB1dDogc3RyaW5nKTogT3V0ZGF0ZWRSZXN1bHQge1xuICBjb25zdCB5YXJudjFPdXRkYXRlZCA9IGZyb21Kc29uTGluZXM8WWFybnYxT3V0ZGF0ZWRSZXN1bHRKc29uPihvdXRwdXQpO1xuICBjb25zdCBmaWVsZHMgPSB5YXJudjFPdXRkYXRlZFsxXS5kYXRhLmhlYWQ7XG4gIGNvbnN0IGRlcGVuZGVuY2llcyA9IHlhcm52MU91dGRhdGVkWzFdLmRhdGEuYm9keTtcblxuICAvLyBubyBvdXRkYXRlZCBkZXBlbmRlbmNpZXNcbiAgaWYgKGRlcGVuZGVuY2llcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gW107XG4gIH1cblxuICAvLyBtYXAgZHluYW1pYyBmaWVsZHNcbiAgdmFsaWRhdGVPdXRkYXRlZEZpZWxkcyhmaWVsZHMpO1xuICBjb25zdCBpbmRleE1hcHBpbmcgPSBnZXRPdXRkYXRlZEZpZWxkSW5kZXhlcyhmaWVsZHMpO1xuXG4gIHJldHVybiBkZXBlbmRlbmNpZXMubWFwKFxuICAgIGRlcCA9PlxuICAgICAgb2JqZWN0RnJvbUVudHJpZXMoXG4gICAgICAgIG9iamVjdFRvS2V5cyhpbmRleE1hcHBpbmcpXG4gICAgICAgICAgLm1hcChmaWVsZCA9PiBbZmllbGQsIGRlcFtpbmRleE1hcHBpbmdbZmllbGRdXV0gYXMgY29uc3QpXG4gICAgICAgICAgLmZpbHRlcihcbiAgICAgICAgICAgIChlbnRyeSk6IGVudHJ5IGlzIFtrZXlvZiBPdXRkYXRlZERlcGVuZGVuY3ksIHN0cmluZ10gPT5cbiAgICAgICAgICAgICAgZW50cnlbMV0gIT0gbnVsbCxcbiAgICAgICAgICApLFxuICAgICAgKSBhcyBPdXRkYXRlZERlcGVuZGVuY3ksXG4gICk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB2YWxpZGF0ZU91dGRhdGVkRmllbGRzKGhlYWQ6IHN0cmluZ1tdKSB7XG4gIGNvbnN0IHJlbGV2YW50RmllbGRzID0gaGVhZC5maWx0ZXIoaXNZYXJudjFGaWVsZE5hbWUpO1xuICBpZiAoaGFzQWxsUmVxdWlyZWRGaWVsZHMocmVsZXZhbnRGaWVsZHMpKSB7XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgYFlhcm4gdjEgb3V0ZGF0ZWQ6IFRlbXBsYXRlIFske2hlYWQuam9pbihcbiAgICAgICcsICcsXG4gICAgKX1dIGRvZXMgbm90IGNvbnRhaW4gYWxsIHJlcXVpcmVkIGZpZWxkcyBbJHt5YXJudjFGaWVsZE5hbWVzLmpvaW4oJywgJyl9XWAsXG4gICk7XG59XG5cbmZ1bmN0aW9uIGlzWWFybnYxRmllbGROYW1lKHZhbHVlOiBzdHJpbmcpOiB2YWx1ZSBpcyBZYXJudjFGaWVsZE5hbWUge1xuICBjb25zdCBuYW1lczogcmVhZG9ubHkgc3RyaW5nW10gPSB5YXJudjFGaWVsZE5hbWVzO1xuICByZXR1cm4gbmFtZXMuaW5jbHVkZXModmFsdWUpO1xufVxuXG5mdW5jdGlvbiBoYXNBbGxSZXF1aXJlZEZpZWxkcyhoZWFkOiBZYXJudjFGaWVsZE5hbWVbXSkge1xuICByZXR1cm4gUkVRVUlSRURfT1VUREFURURfRklFTERTLmV2ZXJ5KGZpZWxkID0+IGhlYWQuaW5jbHVkZXMoZmllbGQpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldE91dGRhdGVkRmllbGRJbmRleGVzKGFsbDogc3RyaW5nW10pIHtcbiAgcmV0dXJuIG9iamVjdEZyb21FbnRyaWVzKFxuICAgIG9iamVjdFRvRW50cmllcyhvdXRkYXRlZHRvRmllbGRNYXBwZXIpLm1hcCgoW291dGRhdGVkRmllbGQsIHlhcm5GaWVsZF0pID0+IFtcbiAgICAgIG91dGRhdGVkRmllbGQsXG4gICAgICBhbGwuaW5kZXhPZih5YXJuRmllbGQpLFxuICAgIF0pLFxuICApO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcnVubmVyL2luZGV4LnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3J1bm5lclwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9ydW5uZXIvaW5kZXgudHNcIjtpbXBvcnQgeyB3cml0ZUZpbGUgfSBmcm9tICdub2RlOmZzL3Byb21pc2VzJztcbmltcG9ydCBwYXRoIGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgdHlwZSB7IFJ1bm5lckNvbmZpZyB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHtcbiAgZW5zdXJlRGlyZWN0b3J5RXhpc3RzLFxuICBleGVjdXRlUHJvY2VzcyxcbiAgZmlsZVBhdGhUb0NsaUFyZyxcbiAgaXNQcm9taXNlRnVsZmlsbGVkUmVzdWx0LFxuICBpc1Byb21pc2VSZWplY3RlZFJlc3VsdCxcbiAgb2JqZWN0RnJvbUVudHJpZXMsXG4gIHJlYWRKc29uRmlsZSxcbn0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB7XG4gIHR5cGUgQXVkaXRTZXZlcml0eSxcbiAgdHlwZSBEZXBlbmRlbmN5R3JvdXAsXG4gIHR5cGUgRmluYWxKU1BhY2thZ2VzUGx1Z2luQ29uZmlnLFxuICB0eXBlIFBhY2thZ2VKc29uUGF0aHMsXG4gIHR5cGUgUGFja2FnZU1hbmFnZXJJZCxcbiAgZGVwZW5kZW5jeUdyb3Vwcyxcbn0gZnJvbSAnLi4vY29uZmlnLmpzJztcbmltcG9ydCB7IGRlcGVuZGVuY3lHcm91cFRvTG9uZyB9IGZyb20gJy4uL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgeyBwYWNrYWdlTWFuYWdlcnMgfSBmcm9tICcuLi9wYWNrYWdlLW1hbmFnZXJzL3BhY2thZ2UtbWFuYWdlcnMuanMnO1xuaW1wb3J0IHsgYXVkaXRSZXN1bHRUb0F1ZGl0T3V0cHV0IH0gZnJvbSAnLi9hdWRpdC90cmFuc2Zvcm0uanMnO1xuaW1wb3J0IHR5cGUgeyBBdWRpdFJlc3VsdCB9IGZyb20gJy4vYXVkaXQvdHlwZXMuanMnO1xuaW1wb3J0IHsgUExVR0lOX0NPTkZJR19QQVRILCBSVU5ORVJfT1VUUFVUX1BBVEggfSBmcm9tICcuL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgeyBvdXRkYXRlZFJlc3VsdFRvQXVkaXRPdXRwdXQgfSBmcm9tICcuL291dGRhdGVkL3RyYW5zZm9ybS5qcyc7XG5pbXBvcnQgeyBmaW5kQWxsUGFja2FnZUpzb24sIGdldFRvdGFsRGVwZW5kZW5jaWVzIH0gZnJvbSAnLi91dGlscy5qcyc7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjcmVhdGVSdW5uZXJDb25maWcoXG4gIHNjcmlwdFBhdGg6IHN0cmluZyxcbiAgY29uZmlnOiBGaW5hbEpTUGFja2FnZXNQbHVnaW5Db25maWcsXG4pOiBQcm9taXNlPFJ1bm5lckNvbmZpZz4ge1xuICBhd2FpdCBlbnN1cmVEaXJlY3RvcnlFeGlzdHMocGF0aC5kaXJuYW1lKFBMVUdJTl9DT05GSUdfUEFUSCkpO1xuICBhd2FpdCB3cml0ZUZpbGUoUExVR0lOX0NPTkZJR19QQVRILCBKU09OLnN0cmluZ2lmeShjb25maWcpKTtcblxuICByZXR1cm4ge1xuICAgIGNvbW1hbmQ6ICdub2RlJyxcbiAgICBhcmdzOiBbZmlsZVBhdGhUb0NsaUFyZyhzY3JpcHRQYXRoKV0sXG4gICAgb3V0cHV0RmlsZTogUlVOTkVSX09VVFBVVF9QQVRILFxuICB9O1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZXhlY3V0ZVJ1bm5lcigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3Qge1xuICAgIHBhY2thZ2VNYW5hZ2VyLFxuICAgIGNoZWNrcyxcbiAgICBhdWRpdExldmVsTWFwcGluZyxcbiAgICBwYWNrYWdlSnNvblBhdGhzLFxuICAgIGRlcGVuZGVuY3lHcm91cHM6IGRlcEdyb3VwcyxcbiAgfSA9IGF3YWl0IHJlYWRKc29uRmlsZTxGaW5hbEpTUGFja2FnZXNQbHVnaW5Db25maWc+KFBMVUdJTl9DT05GSUdfUEFUSCk7XG5cbiAgY29uc3QgYXVkaXRSZXN1bHRzID0gY2hlY2tzLmluY2x1ZGVzKCdhdWRpdCcpXG4gICAgPyBhd2FpdCBwcm9jZXNzQXVkaXQocGFja2FnZU1hbmFnZXIsIGRlcEdyb3VwcywgYXVkaXRMZXZlbE1hcHBpbmcpXG4gICAgOiBbXTtcblxuICBjb25zdCBvdXRkYXRlZFJlc3VsdHMgPSBjaGVja3MuaW5jbHVkZXMoJ291dGRhdGVkJylcbiAgICA/IGF3YWl0IHByb2Nlc3NPdXRkYXRlZChwYWNrYWdlTWFuYWdlciwgZGVwR3JvdXBzLCBwYWNrYWdlSnNvblBhdGhzKVxuICAgIDogW107XG4gIGNvbnN0IGNoZWNrUmVzdWx0cyA9IFsuLi5hdWRpdFJlc3VsdHMsIC4uLm91dGRhdGVkUmVzdWx0c107XG5cbiAgYXdhaXQgZW5zdXJlRGlyZWN0b3J5RXhpc3RzKHBhdGguZGlybmFtZShSVU5ORVJfT1VUUFVUX1BBVEgpKTtcbiAgYXdhaXQgd3JpdGVGaWxlKFJVTk5FUl9PVVRQVVRfUEFUSCwgSlNPTi5zdHJpbmdpZnkoY2hlY2tSZXN1bHRzKSk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHByb2Nlc3NPdXRkYXRlZChcbiAgaWQ6IFBhY2thZ2VNYW5hZ2VySWQsXG4gIGRlcEdyb3VwczogRGVwZW5kZW5jeUdyb3VwW10sXG4gIHBhY2thZ2VKc29uUGF0aHM6IFBhY2thZ2VKc29uUGF0aHMsXG4pIHtcbiAgY29uc3QgcG0gPSBwYWNrYWdlTWFuYWdlcnNbaWRdO1xuICBjb25zdCB7IHN0ZG91dCwgc3RkZXJyIH0gPSBhd2FpdCBleGVjdXRlUHJvY2Vzcyh7XG4gICAgY29tbWFuZDogcG0uY29tbWFuZCxcbiAgICBhcmdzOiBwbS5vdXRkYXRlZC5jb21tYW5kQXJncyxcbiAgICBjd2Q6IHByb2Nlc3MuY3dkKCksXG4gICAgaWdub3JlRXhpdENvZGU6IHRydWUsIC8vIG91dGRhdGVkIHJldHVybnMgZXhpdCBjb2RlIDEgd2hlbiBvdXRkYXRlZCBkZXBlbmRlbmNpZXMgYXJlIGZvdW5kXG4gIH0pO1xuXG4gIC8vIFN1Y2Nlc3NmdWwgb3V0ZGF0ZWQgY2hlY2sgaGFzIGVtcHR5IHN0ZGVyclxuICBpZiAoc3RkZXJyKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBKUyBwYWNrYWdlcyBwbHVnaW46IG91dGRhdGVkIGVycm9yOiAke3N0ZGVycn1gKTtcbiAgfVxuXG4gIC8vIExvY2F0ZSBhbGwgcGFja2FnZS5qc29uIGZpbGVzIGluIHRoZSByZXBvc2l0b3J5IGlmIG5vdCBwcm92aWRlZFxuICBjb25zdCBmaW5hbFBhdGhzID0gQXJyYXkuaXNBcnJheShwYWNrYWdlSnNvblBhdGhzKVxuICAgID8gcGFja2FnZUpzb25QYXRoc1xuICAgIDogYXdhaXQgZmluZEFsbFBhY2thZ2VKc29uKCk7XG4gIGNvbnN0IGRlcFRvdGFscyA9IGF3YWl0IGdldFRvdGFsRGVwZW5kZW5jaWVzKGZpbmFsUGF0aHMpO1xuXG4gIGNvbnN0IG5vcm1hbGl6ZWRSZXN1bHQgPSBwbS5vdXRkYXRlZC51bmlmeVJlc3VsdChzdGRvdXQpO1xuICByZXR1cm4gZGVwR3JvdXBzLm1hcChkZXBHcm91cCA9PlxuICAgIG91dGRhdGVkUmVzdWx0VG9BdWRpdE91dHB1dChcbiAgICAgIG5vcm1hbGl6ZWRSZXN1bHQsXG4gICAgICBpZCxcbiAgICAgIGRlcEdyb3VwLFxuICAgICAgZGVwVG90YWxzW2RlcGVuZGVuY3lHcm91cFRvTG9uZ1tkZXBHcm91cF1dLFxuICAgICksXG4gICk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHByb2Nlc3NBdWRpdChcbiAgaWQ6IFBhY2thZ2VNYW5hZ2VySWQsXG4gIGRlcEdyb3VwczogRGVwZW5kZW5jeUdyb3VwW10sXG4gIGF1ZGl0TGV2ZWxNYXBwaW5nOiBBdWRpdFNldmVyaXR5LFxuKSB7XG4gIGNvbnN0IHBtID0gcGFja2FnZU1hbmFnZXJzW2lkXTtcbiAgY29uc3Qgc3VwcG9ydGVkQXVkaXREZXBHcm91cHMgPVxuICAgIHBtLmF1ZGl0LnN1cHBvcnRlZERlcEdyb3VwcyA/PyBkZXBlbmRlbmN5R3JvdXBzO1xuICBjb25zdCBjb21wYXRpYmxlQXVkaXREZXBHcm91cHMgPSBkZXBHcm91cHMuZmlsdGVyKGdyb3VwID0+XG4gICAgc3VwcG9ydGVkQXVkaXREZXBHcm91cHMuaW5jbHVkZXMoZ3JvdXApLFxuICApO1xuXG4gIGNvbnN0IGF1ZGl0UmVzdWx0cyA9IGF3YWl0IFByb21pc2UuYWxsU2V0dGxlZChcbiAgICBjb21wYXRpYmxlQXVkaXREZXBHcm91cHMubWFwKFxuICAgICAgYXN5bmMgKGRlcEdyb3VwKTogUHJvbWlzZTxbRGVwZW5kZW5jeUdyb3VwLCBBdWRpdFJlc3VsdF0+ID0+IHtcbiAgICAgICAgY29uc3QgeyBzdGRvdXQsIHN0ZGVyciB9ID0gYXdhaXQgZXhlY3V0ZVByb2Nlc3Moe1xuICAgICAgICAgIGNvbW1hbmQ6IHBtLmNvbW1hbmQsXG4gICAgICAgICAgYXJnczogcG0uYXVkaXQuZ2V0Q29tbWFuZEFyZ3MoZGVwR3JvdXApLFxuICAgICAgICAgIGN3ZDogcHJvY2Vzcy5jd2QoKSxcbiAgICAgICAgICBpZ25vcmVFeGl0Q29kZTogcG0uYXVkaXQuaWdub3JlRXhpdENvZGUsXG4gICAgICAgIH0pO1xuICAgICAgICAvLyBTdWNjZXNzZnVsIGF1ZGl0IGNoZWNrIGhhcyBlbXB0eSBzdGRlcnJcbiAgICAgICAgaWYgKHN0ZGVycikge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgSlMgcGFja2FnZXMgcGx1Z2luOiBhdWRpdCBlcnJvcjogJHtzdGRlcnJ9YCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIFtkZXBHcm91cCwgcG0uYXVkaXQudW5pZnlSZXN1bHQoc3Rkb3V0KV07XG4gICAgICB9LFxuICAgICksXG4gICk7XG5cbiAgY29uc3QgcmVqZWN0ZWQgPSBhdWRpdFJlc3VsdHMuZmlsdGVyKGlzUHJvbWlzZVJlamVjdGVkUmVzdWx0KTtcbiAgaWYgKHJlamVjdGVkLmxlbmd0aCA+IDApIHtcbiAgICByZWplY3RlZC5mb3JFYWNoKHJlc3VsdCA9PiB7XG4gICAgICBjb25zb2xlLmVycm9yKHJlc3VsdC5yZWFzb24pO1xuICAgIH0pO1xuXG4gICAgdGhyb3cgbmV3IEVycm9yKGBKUyBQYWNrYWdlcyBwbHVnaW46IFJ1bm5pbmcgJHtwbS5uYW1lfSBhdWRpdCBmYWlsZWQuYCk7XG4gIH1cblxuICBjb25zdCBmdWxmaWxsZWQgPSBvYmplY3RGcm9tRW50cmllcyhcbiAgICBhdWRpdFJlc3VsdHMuZmlsdGVyKGlzUHJvbWlzZUZ1bGZpbGxlZFJlc3VsdCkubWFwKHggPT4geC52YWx1ZSksXG4gICk7XG5cbiAgY29uc3QgdW5pcXVlUmVzdWx0cyA9IHBtLmF1ZGl0LnBvc3RQcm9jZXNzUmVzdWx0Py4oZnVsZmlsbGVkKSA/PyBmdWxmaWxsZWQ7XG5cbiAgcmV0dXJuIGNvbXBhdGlibGVBdWRpdERlcEdyb3Vwcy5tYXAoZGVwR3JvdXAgPT5cbiAgICBhdWRpdFJlc3VsdFRvQXVkaXRPdXRwdXQoXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLW5vbi1udWxsLWFzc2VydGlvblxuICAgICAgdW5pcXVlUmVzdWx0c1tkZXBHcm91cF0hLFxuICAgICAgaWQsXG4gICAgICBkZXBHcm91cCxcbiAgICAgIGF1ZGl0TGV2ZWxNYXBwaW5nLFxuICAgICksXG4gICk7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9ydW5uZXIvYXVkaXQvdHJhbnNmb3JtLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3J1bm5lci9hdWRpdFwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9ydW5uZXIvYXVkaXQvdHJhbnNmb3JtLnRzXCI7aW1wb3J0IHsgbWQgfSBmcm9tICdidWlsZC1tZCc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0T3V0cHV0LCBJc3N1ZSB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgb2JqZWN0VG9FbnRyaWVzIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB7XG4gIHR5cGUgQXVkaXRTZXZlcml0eSxcbiAgdHlwZSBEZXBlbmRlbmN5R3JvdXAsXG4gIHR5cGUgUGFja2FnZU1hbmFnZXJJZCxcbiAgcGFja2FnZUF1ZGl0TGV2ZWxzLFxufSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xuaW1wb3J0IHsgYXVkaXRTY29yZU1vZGlmaWVycyB9IGZyb20gJy4vY29uc3RhbnRzLmpzJztcbmltcG9ydCB0eXBlIHsgQXVkaXRSZXN1bHQsIEF1ZGl0U3VtbWFyeSwgVnVsbmVyYWJpbGl0eSB9IGZyb20gJy4vdHlwZXMuanMnO1xuXG5leHBvcnQgZnVuY3Rpb24gYXVkaXRSZXN1bHRUb0F1ZGl0T3V0cHV0KFxuICByZXN1bHQ6IEF1ZGl0UmVzdWx0LFxuICBpZDogUGFja2FnZU1hbmFnZXJJZCxcbiAgZGVwR3JvdXA6IERlcGVuZGVuY3lHcm91cCxcbiAgYXVkaXRMZXZlbE1hcHBpbmc6IEF1ZGl0U2V2ZXJpdHksXG4pOiBBdWRpdE91dHB1dCB7XG4gIGNvbnN0IGlzc3VlcyA9IHZ1bG5lcmFiaWxpdGllc1RvSXNzdWVzKFxuICAgIHJlc3VsdC52dWxuZXJhYmlsaXRpZXMsXG4gICAgYXVkaXRMZXZlbE1hcHBpbmcsXG4gICk7XG5cbiAgcmV0dXJuIHtcbiAgICBzbHVnOiBgJHtpZH0tYXVkaXQtJHtkZXBHcm91cH1gLFxuICAgIHNjb3JlOiBjYWxjdWxhdGVBdWRpdFNjb3JlKHJlc3VsdC5zdW1tYXJ5KSxcbiAgICB2YWx1ZTogcmVzdWx0LnN1bW1hcnkudG90YWwsXG4gICAgZGlzcGxheVZhbHVlOiBzdW1tYXJ5VG9EaXNwbGF5VmFsdWUocmVzdWx0LnN1bW1hcnkpLFxuICAgIGRldGFpbHM6IHsgaXNzdWVzIH0sXG4gIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjYWxjdWxhdGVBdWRpdFNjb3JlKHN0YXRzOiBBdWRpdFN1bW1hcnkpIHtcbiAgaWYgKHN0YXRzLnRvdGFsID09PSAwKSB7XG4gICAgcmV0dXJuIDE7XG4gIH1cblxuICByZXR1cm4gb2JqZWN0VG9FbnRyaWVzKHN0YXRzKS5yZWR1Y2U8bnVtYmVyPihcbiAgICAoc2NvcmUsIFtsZXZlbCwgdnVsbmVyYWJpbGl0aWVzXSkgPT4ge1xuICAgICAgaWYgKGxldmVsID09PSAndG90YWwnKSB7XG4gICAgICAgIHJldHVybiBzY29yZTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgcmVkdWNlZFNjb3JlID0gc2NvcmUgLSBhdWRpdFNjb3JlTW9kaWZpZXJzW2xldmVsXSAqIHZ1bG5lcmFiaWxpdGllcztcbiAgICAgIHJldHVybiBNYXRoLm1heChyZWR1Y2VkU2NvcmUsIDApO1xuICAgIH0sXG4gICAgMSxcbiAgKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHN1bW1hcnlUb0Rpc3BsYXlWYWx1ZShzdW1tYXJ5OiBBdWRpdFN1bW1hcnkpOiBzdHJpbmcge1xuICBpZiAoc3VtbWFyeS50b3RhbCA9PT0gMCkge1xuICAgIHJldHVybiAnMCB2dWxuZXJhYmlsaXRpZXMnO1xuICB9XG5cbiAgY29uc3QgdnVsbmVyYWJpbGl0eVN0YXRzID0gcGFja2FnZUF1ZGl0TGV2ZWxzXG4gICAgLm1hcChsZXZlbCA9PiAoc3VtbWFyeVtsZXZlbF0gPiAwID8gYCR7c3VtbWFyeVtsZXZlbF19ICR7bGV2ZWx9YCA6ICcnKSlcbiAgICAuZmlsdGVyKHRleHQgPT4gdGV4dCAhPT0gJycpXG4gICAgLmpvaW4oJywgJyk7XG4gIHJldHVybiBgJHtzdW1tYXJ5LnRvdGFsfSAke1xuICAgIHN1bW1hcnkudG90YWwgPT09IDEgPyAndnVsbmVyYWJpbGl0eScgOiAndnVsbmVyYWJpbGl0aWVzJ1xuICB9ICgke3Z1bG5lcmFiaWxpdHlTdGF0c30pYDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHZ1bG5lcmFiaWxpdGllc1RvSXNzdWVzKFxuICB2dWxuZXJhYmlsaXRpZXM6IFZ1bG5lcmFiaWxpdHlbXSxcbiAgYXVkaXRMZXZlbE1hcHBpbmc6IEF1ZGl0U2V2ZXJpdHksXG4pOiBJc3N1ZVtdIHtcbiAgaWYgKHZ1bG5lcmFiaWxpdGllcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gW107XG4gIH1cblxuICByZXR1cm4gdnVsbmVyYWJpbGl0aWVzLm1hcCgoZGV0YWlsKTogSXNzdWUgPT4ge1xuICAgIGNvbnN0IHZlcnNpb25SYW5nZSA9XG4gICAgICBkZXRhaWwudmVyc2lvblJhbmdlID09PSAnKidcbiAgICAgICAgPyBtZGAke21kLmJvbGQoJ2FsbCcpfSB2ZXJzaW9uc2BcbiAgICAgICAgOiBtZGB2ZXJzaW9ucyAke21kLmJvbGQoZGV0YWlsLnZlcnNpb25SYW5nZSl9YDtcbiAgICBjb25zdCBkaXJlY3REZXBlbmRlbmN5ID1cbiAgICAgIHR5cGVvZiBkZXRhaWwuZGlyZWN0RGVwZW5kZW5jeSA9PT0gJ3N0cmluZycgJiZcbiAgICAgIGRldGFpbC5kaXJlY3REZXBlbmRlbmN5ICE9PSAnJ1xuICAgICAgICA/IG1kLmNvZGUoZGV0YWlsLmRpcmVjdERlcGVuZGVuY3kpXG4gICAgICAgIDogJyc7XG4gICAgY29uc3QgZGVwSGllcmFyY2h5ID0gZGlyZWN0RGVwZW5kZW5jeVxuICAgICAgPyBtZGAke2RpcmVjdERlcGVuZGVuY3l9J3MgZGVwZW5kZW5jeSAke21kLmNvZGUoZGV0YWlsLm5hbWUpfWBcbiAgICAgIDogbWRgJHttZC5jb2RlKGRldGFpbC5uYW1lKX0gZGVwZW5kZW5jeWA7XG5cbiAgICBjb25zdCB2dWxuZXJhYmlsaXR5U3VtbWFyeSA9IG1kYGhhcyBhICR7bWQuYm9sZChcbiAgICAgIGRldGFpbC5zZXZlcml0eSxcbiAgICApfSB2dWxuZXJhYmlsaXR5IGluICR7dmVyc2lvblJhbmdlfS5gO1xuICAgIGNvbnN0IGZpeEluZm8gPSBkZXRhaWwuZml4SW5mb3JtYXRpb24gPyBgICR7ZGV0YWlsLmZpeEluZm9ybWF0aW9ufWAgOiAnJztcbiAgICBjb25zdCBhZGRpdGlvbmFsSW5mbyA9XG4gICAgICBkZXRhaWwudGl0bGUgIT0gbnVsbCAmJiBkZXRhaWwudXJsICE9IG51bGxcbiAgICAgICAgPyBtZGAgTW9yZSBpbmZvcm1hdGlvbjogJHttZC5saW5rKGRldGFpbC51cmwsIGRldGFpbC50aXRsZSl9YFxuICAgICAgICA6ICcnO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIG1lc3NhZ2U6XG4gICAgICAgIG1kYCR7ZGVwSGllcmFyY2h5fSAke3Z1bG5lcmFiaWxpdHlTdW1tYXJ5fSR7Zml4SW5mb30ke2FkZGl0aW9uYWxJbmZvfWAudG9TdHJpbmcoKSxcbiAgICAgIHNldmVyaXR5OiBhdWRpdExldmVsTWFwcGluZ1tkZXRhaWwuc2V2ZXJpdHldLFxuICAgIH07XG4gIH0pO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcnVubmVyL2NvbnN0YW50cy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9ydW5uZXJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcnVubmVyL2NvbnN0YW50cy50c1wiO2ltcG9ydCBwYXRoIGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgeyBwbHVnaW5Xb3JrRGlyIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcblxuZXhwb3J0IGNvbnN0IFdPUktESVIgPSBwbHVnaW5Xb3JrRGlyKCdqcy1wYWNrYWdlcycpO1xuZXhwb3J0IGNvbnN0IFJVTk5FUl9PVVRQVVRfUEFUSCA9IHBhdGguam9pbihXT1JLRElSLCAncnVubmVyLW91dHB1dC5qc29uJyk7XG5leHBvcnQgY29uc3QgUExVR0lOX0NPTkZJR19QQVRIID0gcGF0aC5qb2luKFxuICBwcm9jZXNzLmN3ZCgpLFxuICBXT1JLRElSLFxuICAncGx1Z2luLWNvbmZpZy5qc29uJyxcbik7XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9ydW5uZXIvb3V0ZGF0ZWQvdHJhbnNmb3JtLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3J1bm5lci9vdXRkYXRlZFwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9ydW5uZXIvb3V0ZGF0ZWQvdHJhbnNmb3JtLnRzXCI7aW1wb3J0IHsgbWQgfSBmcm9tICdidWlsZC1tZCc7XG5pbXBvcnQgeyBjbGVhbiwgZGlmZiwgbmVxIH0gZnJvbSAnc2VtdmVyJztcbmltcG9ydCB0eXBlIHsgQXVkaXRPdXRwdXQsIElzc3VlIH0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5pbXBvcnQgeyBvYmplY3RGcm9tRW50cmllcywgcGx1cmFsaXplIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB0eXBlIHsgRGVwZW5kZW5jeUdyb3VwLCBQYWNrYWdlTWFuYWdlcklkIH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcbmltcG9ydCB7IGRlcGVuZGVuY3lHcm91cFRvTG9uZyB9IGZyb20gJy4uLy4uL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgeyBSRUxFQVNFX1RZUEVTLCBvdXRkYXRlZFNldmVyaXR5IH0gZnJvbSAnLi9jb25zdGFudHMuanMnO1xuaW1wb3J0IHR5cGUgeyBPdXRkYXRlZFJlc3VsdCwgUGFja2FnZVZlcnNpb24gfSBmcm9tICcuL3R5cGVzLmpzJztcblxuZXhwb3J0IGZ1bmN0aW9uIG91dGRhdGVkUmVzdWx0VG9BdWRpdE91dHB1dChcbiAgcmVzdWx0OiBPdXRkYXRlZFJlc3VsdCxcbiAgcGFja2FnZU1hbmFnZXI6IFBhY2thZ2VNYW5hZ2VySWQsXG4gIGRlcEdyb3VwOiBEZXBlbmRlbmN5R3JvdXAsXG4gIHRvdGFsRGVwczogbnVtYmVyLFxuKTogQXVkaXRPdXRwdXQge1xuICBjb25zdCByZWxldmFudERlcGVuZGVuY2llczogT3V0ZGF0ZWRSZXN1bHQgPSByZXN1bHQuZmlsdGVyKFxuICAgIGRlcCA9PiBkZXAudHlwZSA9PT0gZGVwZW5kZW5jeUdyb3VwVG9Mb25nW2RlcEdyb3VwXSxcbiAgKTtcblxuICBjb25zdCB2YWxpZERlcGVuZGVuY2llcyA9IHJlbGV2YW50RGVwZW5kZW5jaWVzXG4gICAgLm1hcChkZXAgPT4gKHtcbiAgICAgIC4uLmRlcCxcbiAgICAgIGN1cnJlbnQ6IGNsZWFuKGRlcC5jdXJyZW50KSxcbiAgICAgIGxhdGVzdDogY2xlYW4oZGVwLmxhdGVzdCksXG4gICAgfSkpXG4gICAgLmZpbHRlcihcbiAgICAgIChkZXApOiBkZXAgaXMgT3V0ZGF0ZWRSZXN1bHRbbnVtYmVyXSA9PlxuICAgICAgICBkZXAuY3VycmVudCAhPSBudWxsICYmIGRlcC5sYXRlc3QgIT0gbnVsbCxcbiAgICApO1xuXG4gIGNvbnN0IG91dGRhdGVkRGVwZW5kZW5jaWVzID0gdmFsaWREZXBlbmRlbmNpZXMuZmlsdGVyKGRlcCA9PlxuICAgIG5lcShkZXAuY3VycmVudCwgZGVwLmxhdGVzdCksXG4gICk7XG5cbiAgY29uc3Qgb3V0ZGF0ZWRTdGF0cyA9IG91dGRhdGVkRGVwZW5kZW5jaWVzLnJlZHVjZShcbiAgICAoYWNjLCBkZXApID0+IHtcbiAgICAgIGNvbnN0IG91dGRhdGVkTGV2ZWwgPSBkaWZmKGRlcC5jdXJyZW50LCBkZXAubGF0ZXN0KTtcbiAgICAgIGlmIChvdXRkYXRlZExldmVsID09IG51bGwpIHtcbiAgICAgICAgcmV0dXJuIGFjYztcbiAgICAgIH1cbiAgICAgIHJldHVybiB7IC4uLmFjYywgW291dGRhdGVkTGV2ZWxdOiBhY2Nbb3V0ZGF0ZWRMZXZlbF0gKyAxIH07XG4gICAgfSxcbiAgICBvYmplY3RGcm9tRW50cmllcyhSRUxFQVNFX1RZUEVTLm1hcCh2ZXJzaW9uVHlwZSA9PiBbdmVyc2lvblR5cGUsIDBdKSksXG4gICk7XG5cbiAgY29uc3QgaXNzdWVzID1cbiAgICBvdXRkYXRlZERlcGVuZGVuY2llcy5sZW5ndGggPT09IDBcbiAgICAgID8gW11cbiAgICAgIDogb3V0ZGF0ZWRUb0lzc3VlcyhvdXRkYXRlZERlcGVuZGVuY2llcyk7XG5cbiAgcmV0dXJuIHtcbiAgICBzbHVnOiBgJHtwYWNrYWdlTWFuYWdlcn0tb3V0ZGF0ZWQtJHtkZXBHcm91cH1gLFxuICAgIHNjb3JlOiBjYWxjdWxhdGVPdXRkYXRlZFNjb3JlKG91dGRhdGVkU3RhdHMubWFqb3IsIHRvdGFsRGVwcyksXG4gICAgdmFsdWU6IG91dGRhdGVkRGVwZW5kZW5jaWVzLmxlbmd0aCxcbiAgICBkaXNwbGF5VmFsdWU6IG91dGRhdGVkVG9EaXNwbGF5VmFsdWUob3V0ZGF0ZWRTdGF0cyksXG4gICAgZGV0YWlsczogeyBpc3N1ZXMgfSxcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNhbGN1bGF0ZU91dGRhdGVkU2NvcmUoXG4gIG1ham9yT3V0ZGF0ZWQ6IG51bWJlcixcbiAgdG90YWxEZXBzOiBudW1iZXIsXG4pIHtcbiAgcmV0dXJuIHRvdGFsRGVwcyA+IDAgPyAodG90YWxEZXBzIC0gbWFqb3JPdXRkYXRlZCkgLyB0b3RhbERlcHMgOiAxO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gb3V0ZGF0ZWRUb0Rpc3BsYXlWYWx1ZShzdGF0czogUGFja2FnZVZlcnNpb24pIHtcbiAgY29uc3QgdG90YWwgPSBPYmplY3QudmFsdWVzKHN0YXRzKS5yZWR1Y2UoKGFjYywgdmFsdWUpID0+IGFjYyArIHZhbHVlLCAwKTtcblxuICBjb25zdCB2ZXJzaW9uQnJlYWtkb3duID0gUkVMRUFTRV9UWVBFUy5tYXAodmVyc2lvbiA9PlxuICAgIHN0YXRzW3ZlcnNpb25dID4gMCA/IGAke3N0YXRzW3ZlcnNpb25dfSAke3ZlcnNpb259YCA6ICcnLFxuICApLmZpbHRlcih0ZXh0ID0+IHRleHQgIT09ICcnKTtcblxuICBpZiAodmVyc2lvbkJyZWFrZG93bi5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gJ2FsbCBkZXBlbmRlbmNpZXMgYXJlIHVwIHRvIGRhdGUnO1xuICB9XG5cbiAgaWYgKHZlcnNpb25CcmVha2Rvd24ubGVuZ3RoID4gMSkge1xuICAgIHJldHVybiBgJHt0b3RhbH0gb3V0ZGF0ZWQgcGFja2FnZSB2ZXJzaW9ucyAoJHt2ZXJzaW9uQnJlYWtkb3duLmpvaW4oXG4gICAgICAnLCAnLFxuICAgICl9KWA7XG4gIH1cblxuICByZXR1cm4gYCR7dmVyc2lvbkJyZWFrZG93blswXX0gb3V0ZGF0ZWQgcGFja2FnZSAke3BsdXJhbGl6ZShcbiAgICAndmVyc2lvbicsXG4gICAgdG90YWwsXG4gICl9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG91dGRhdGVkVG9Jc3N1ZXMoZGVwZW5kZW5jaWVzOiBPdXRkYXRlZFJlc3VsdCk6IElzc3VlW10ge1xuICByZXR1cm4gZGVwZW5kZW5jaWVzLm1hcDxJc3N1ZT4oZGVwID0+IHtcbiAgICBjb25zdCB7IG5hbWUsIGN1cnJlbnQsIGxhdGVzdCwgdXJsIH0gPSBkZXA7XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1ub24tbnVsbC1hc3NlcnRpb25cbiAgICBjb25zdCBvdXRkYXRlZExldmVsID0gZGlmZihjdXJyZW50LCBsYXRlc3QpITtcbiAgICBjb25zdCBwYWNrYWdlUmVmZXJlbmNlID1cbiAgICAgIHVybCA9PSBudWxsID8gbWQuY29kZShuYW1lKSA6IG1kLmxpbmsodXJsLCBtZC5jb2RlKG5hbWUpKTtcblxuICAgIHJldHVybiB7XG4gICAgICBtZXNzYWdlOiBtZGBQYWNrYWdlICR7cGFja2FnZVJlZmVyZW5jZX0gcmVxdWlyZXMgYSAke21kLmJvbGQoXG4gICAgICAgIG91dGRhdGVkTGV2ZWwsXG4gICAgICApfSB1cGRhdGUgZnJvbSAke21kLmJvbGQoY3VycmVudCl9IHRvICR7bWQuYm9sZChsYXRlc3QpfS5gLnRvU3RyaW5nKCksXG4gICAgICBzZXZlcml0eTogb3V0ZGF0ZWRTZXZlcml0eVtvdXRkYXRlZExldmVsXSxcbiAgICB9O1xuICB9KTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3J1bm5lci9vdXRkYXRlZC9jb25zdGFudHMudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcnVubmVyL291dGRhdGVkXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3J1bm5lci9vdXRkYXRlZC9jb25zdGFudHMudHNcIjtpbXBvcnQgdHlwZSB7IFJlbGVhc2VUeXBlIH0gZnJvbSAnc2VtdmVyJztcbmltcG9ydCB0eXBlIHsgSXNzdWVTZXZlcml0eSB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgb2JqZWN0VG9LZXlzIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcblxuZXhwb3J0IGNvbnN0IG91dGRhdGVkU2V2ZXJpdHk6IFJlY29yZDxSZWxlYXNlVHlwZSwgSXNzdWVTZXZlcml0eT4gPSB7XG4gIG1ham9yOiAnZXJyb3InLFxuICBwcmVtYWpvcjogJ2luZm8nLFxuICBtaW5vcjogJ3dhcm5pbmcnLFxuICBwcmVtaW5vcjogJ2luZm8nLFxuICBwYXRjaDogJ2luZm8nLFxuICBwcmVwYXRjaDogJ2luZm8nLFxuICBwcmVyZWxlYXNlOiAnaW5mbycsXG59O1xuXG4vLyBSRUxFQVNFX1RZUEVTIGRpcmVjdGx5IGV4cG9ydGVkIGZyb20gc2VtdmVyIGRvbid0IHdvcmsgb3V0IG9mIHRoZSBib3hcbmV4cG9ydCBjb25zdCBSRUxFQVNFX1RZUEVTID0gb2JqZWN0VG9LZXlzKG91dGRhdGVkU2V2ZXJpdHkpO1xuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy9kZXJpdmUtcGFja2FnZS1tYW5hZ2VyLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnNcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy9kZXJpdmUtcGFja2FnZS1tYW5hZ2VyLnRzXCI7aW1wb3J0IHsgcmVhZEZpbGUgfSBmcm9tICdub2RlOmZzL3Byb21pc2VzJztcbmltcG9ydCBwYXRoIGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgeyBmaWxlRXhpc3RzIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB0eXBlIHsgUGFja2FnZU1hbmFnZXJJZCB9IGZyb20gJy4uL2NvbmZpZy5qcyc7XG5pbXBvcnQgeyBkZXJpdmVZYXJuVmVyc2lvbiB9IGZyb20gJy4vZGVyaXZlLXlhcm4uanMnO1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZGVyaXZlUGFja2FnZU1hbmFnZXJJblBhY2thZ2VKc29uKFxuICBjdXJyZW50RGlyID0gcHJvY2Vzcy5jd2QoKSxcbikge1xuICBpZiAoYXdhaXQgZmlsZUV4aXN0cyhwYXRoLmpvaW4oY3VycmVudERpciwgJ3BhY2thZ2UuanNvbicpKSkge1xuICAgIGNvbnN0IGNvbnRlbnQgPSBKU09OLnBhcnNlKFxuICAgICAgKGF3YWl0IHJlYWRGaWxlKHBhdGguam9pbigncGFja2FnZS5qc29uJykpKS50b1N0cmluZygpLFxuICAgICkgYXMgeyBwYWNrYWdlTWFuYWdlcj86IHN0cmluZyB9O1xuICAgIGNvbnN0IHsgcGFja2FnZU1hbmFnZXI6IHBhY2thZ2VNYW5hZ2VyRGF0YSA9ICcnIH0gPSBjb250ZW50O1xuXG4gICAgY29uc3QgW21hbmFnZXIgPSAnJywgdmVyc2lvbiA9ICcnXSA9IHBhY2thZ2VNYW5hZ2VyRGF0YS5zcGxpdCgnQCcpO1xuXG4gICAgaWYgKG1hbmFnZXIgPT09ICducG0nKSB7XG4gICAgICByZXR1cm4gbWFuYWdlcjtcbiAgICB9XG4gICAgaWYgKG1hbmFnZXIgPT09ICdwbnBtJykge1xuICAgICAgcmV0dXJuIG1hbmFnZXI7XG4gICAgfVxuICAgIGlmIChtYW5hZ2VyID09PSAneWFybicpIHtcbiAgICAgIGNvbnN0IG1ham9yVmVyc2lvbiA9IE51bWJlcih2ZXJzaW9uLnNwbGl0KCcuJylbMF0pO1xuICAgICAgcmV0dXJuIG1ham9yVmVyc2lvbiA+IDEgPyAneWFybi1tb2Rlcm4nIDogJ3lhcm4tY2xhc3NpYyc7XG4gICAgfVxuICB9XG4gIHJldHVybiBmYWxzZTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGRlcml2ZVBhY2thZ2VNYW5hZ2VyKFxuICBjdXJyZW50RGlyID0gcHJvY2Vzcy5jd2QoKSxcbik6IFByb21pc2U8UGFja2FnZU1hbmFnZXJJZD4ge1xuICBjb25zdCBwa2dNYW5hZ2VyRnJvbVBhY2thZ2VKc29uID1cbiAgICBhd2FpdCBkZXJpdmVQYWNrYWdlTWFuYWdlckluUGFja2FnZUpzb24oY3VycmVudERpcik7XG4gIGlmIChwa2dNYW5hZ2VyRnJvbVBhY2thZ2VKc29uKSB7XG4gICAgcmV0dXJuIHBrZ01hbmFnZXJGcm9tUGFja2FnZUpzb247XG4gIH1cblxuICAvLyBDaGVjayBmb3IgbG9jayBmaWxlc1xuICBpZiAoYXdhaXQgZmlsZUV4aXN0cyhwYXRoLmpvaW4oY3VycmVudERpciwgJ3BhY2thZ2UtbG9jay5qc29uJykpKSB7XG4gICAgcmV0dXJuICducG0nO1xuICB9IGVsc2UgaWYgKGF3YWl0IGZpbGVFeGlzdHMocGF0aC5qb2luKGN1cnJlbnREaXIsICdwbnBtLWxvY2sueWFtbCcpKSkge1xuICAgIHJldHVybiAncG5wbSc7XG4gIH0gZWxzZSBpZiAoYXdhaXQgZmlsZUV4aXN0cyhwYXRoLmpvaW4oY3VycmVudERpciwgJ3lhcm4ubG9jaycpKSkge1xuICAgIGNvbnN0IHlhcm5WZXJzaW9uID0gYXdhaXQgZGVyaXZlWWFyblZlcnNpb24oKTtcbiAgICBpZiAoeWFyblZlcnNpb24pIHtcbiAgICAgIHJldHVybiB5YXJuVmVyc2lvbjtcbiAgICB9XG4gIH1cblxuICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgJ0NvdWxkIG5vdCBkZXRlY3QgcGFja2FnZSBtYW5hZ2VyLiBQbGVhc2UgcHJvdmlkZSBpdCBpbiB0aGUganMtcGFja2FnZXMgcGx1Z2luIGNvbmZpZy4nLFxuICApO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy9kZXJpdmUteWFybi50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnMvZGVyaXZlLXlhcm4udHNcIjtpbXBvcnQgeyBleGVjdXRlUHJvY2VzcyB9IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBkZXJpdmVZYXJuVmVyc2lvbigpIHtcbiAgY29uc3QgeyBzdGRvdXQgfSA9IGF3YWl0IGV4ZWN1dGVQcm9jZXNzKHtcbiAgICBjb21tYW5kOiAneWFybicsXG4gICAgYXJnczogWyctdiddLFxuICB9KTtcblxuICBjb25zdCB5YXJuVmVyc2lvbiA9IE51bWJlci5wYXJzZUludChzdGRvdXQudG9TdHJpbmcoKS50cmltKCkuYXQoMCkgPz8gJycsIDEwKTtcbiAgaWYgKHlhcm5WZXJzaW9uID49IDIpIHtcbiAgICByZXR1cm4gJ3lhcm4tbW9kZXJuJztcbiAgfSBlbHNlIGlmICh5YXJuVmVyc2lvbiA9PT0gMSkge1xuICAgIHJldHVybiAneWFybi1jbGFzc2ljJztcbiAgfVxuICByZXR1cm4gZmFsc2U7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL2xpZ2h0aG91c2UtcGx1Z2luLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9saWdodGhvdXNlLXBsdWdpbi50c1wiO2ltcG9ydCB7IGNyZWF0ZVJlcXVpcmUgfSBmcm9tICdub2RlOm1vZHVsZSc7XG5pbXBvcnQgdHlwZSB7IFBsdWdpbkNvbmZpZyB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgTElHSFRIT1VTRV9QTFVHSU5fU0xVRyB9IGZyb20gJy4vY29uc3RhbnRzLmpzJztcbmltcG9ydCB7IG5vcm1hbGl6ZUZsYWdzIH0gZnJvbSAnLi9ub3JtYWxpemUtZmxhZ3MuanMnO1xuaW1wb3J0IHtcbiAgTElHSFRIT1VTRV9HUk9VUFMsXG4gIExJR0hUSE9VU0VfTkFWSUdBVElPTl9BVURJVFMsXG59IGZyb20gJy4vcnVubmVyL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgeyBjcmVhdGVSdW5uZXJGdW5jdGlvbiB9IGZyb20gJy4vcnVubmVyL3J1bm5lci5qcyc7XG5pbXBvcnQgdHlwZSB7IExpZ2h0aG91c2VPcHRpb25zIH0gZnJvbSAnLi90eXBlcy5qcyc7XG5pbXBvcnQgeyBmaWx0ZXJBdWRpdHNBbmRHcm91cHNCeU9ubHlPcHRpb25zIH0gZnJvbSAnLi91dGlscy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBsaWdodGhvdXNlUGx1Z2luKFxuICB1cmw6IHN0cmluZyxcbiAgZmxhZ3M/OiBMaWdodGhvdXNlT3B0aW9ucyxcbik6IFBsdWdpbkNvbmZpZyB7XG4gIGNvbnN0IHsgc2tpcEF1ZGl0cywgb25seUF1ZGl0cywgb25seUNhdGVnb3JpZXMsIC4uLnVucGFyc2VkRmxhZ3MgfSA9XG4gICAgbm9ybWFsaXplRmxhZ3MoZmxhZ3MgPz8ge30pO1xuXG4gIGNvbnN0IHsgYXVkaXRzLCBncm91cHMgfSA9IGZpbHRlckF1ZGl0c0FuZEdyb3Vwc0J5T25seU9wdGlvbnMoXG4gICAgTElHSFRIT1VTRV9OQVZJR0FUSU9OX0FVRElUUyxcbiAgICBMSUdIVEhPVVNFX0dST1VQUyxcbiAgICB7IHNraXBBdWRpdHMsIG9ubHlBdWRpdHMsIG9ubHlDYXRlZ29yaWVzIH0sXG4gICk7XG5cbiAgY29uc3QgcGFja2FnZUpzb24gPSBjcmVhdGVSZXF1aXJlKGltcG9ydC5tZXRhLnVybCkoXG4gICAgJy4uLy4uL3BhY2thZ2UuanNvbicsXG4gICkgYXMgdHlwZW9mIGltcG9ydCgnLi4vLi4vcGFja2FnZS5qc29uJyk7XG5cbiAgcmV0dXJuIHtcbiAgICBzbHVnOiBMSUdIVEhPVVNFX1BMVUdJTl9TTFVHLFxuICAgIHBhY2thZ2VOYW1lOiBwYWNrYWdlSnNvbi5uYW1lLFxuICAgIHZlcnNpb246IHBhY2thZ2VKc29uLnZlcnNpb24sXG4gICAgdGl0bGU6ICdMaWdodGhvdXNlJyxcbiAgICBpY29uOiAnbGlnaHRob3VzZScsXG4gICAgYXVkaXRzLFxuICAgIGdyb3VwcyxcbiAgICBydW5uZXI6IGNyZWF0ZVJ1bm5lckZ1bmN0aW9uKHVybCwge1xuICAgICAgc2tpcEF1ZGl0cyxcbiAgICAgIG9ubHlBdWRpdHMsXG4gICAgICBvbmx5Q2F0ZWdvcmllcyxcbiAgICAgIC4uLnVucGFyc2VkRmxhZ3MsXG4gICAgfSksXG4gIH07XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL2NvbnN0YW50cy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvY29uc3RhbnRzLnRzXCI7aW1wb3J0IHsgREVGQVVMVF9GTEFHUyB9IGZyb20gJ2Nocm9tZS1sYXVuY2hlci9kaXN0L2ZsYWdzLmpzJztcbmltcG9ydCBwYXRoIGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgeyBERUZBVUxUX1BFUlNJU1RfT1VUUFVUX0RJUiB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuXG4vLyBoZWFkbGVzcyBpcyBuZWVkZWQgdG8gcGFzcyBDSSBvbiBMaW51eCBhbmQgV2luZG93cyAobG9jYWxseSBpdCB3b3JrcyB3aXRob3V0IGhlYWRsZXNzIHRvbylcbmV4cG9ydCBjb25zdCBERUZBVUxUX0NIUk9NRV9GTEFHUyA9IFsuLi5ERUZBVUxUX0ZMQUdTLCAnLS1oZWFkbGVzcyddO1xuXG5leHBvcnQgY29uc3QgTElHSFRIT1VTRV9QTFVHSU5fU0xVRyA9ICdsaWdodGhvdXNlJztcbmV4cG9ydCBjb25zdCBMSUdIVEhPVVNFX09VVFBVVF9QQVRIID0gcGF0aC5qb2luKFxuICBERUZBVUxUX1BFUlNJU1RfT1VUUFVUX0RJUixcbiAgTElHSFRIT1VTRV9QTFVHSU5fU0xVRyxcbik7XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL25vcm1hbGl6ZS1mbGFncy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvbm9ybWFsaXplLWZsYWdzLnRzXCI7aW1wb3J0IHsgYm9sZCwgeWVsbG93IH0gZnJvbSAnYW5zaXMnO1xuaW1wb3J0IHsgdWkgfSBmcm9tICdAY29kZS1wdXNodXAvdXRpbHMnO1xuaW1wb3J0IHsgTElHSFRIT1VTRV9QTFVHSU5fU0xVRyB9IGZyb20gJy4vY29uc3RhbnRzLmpzJztcbmltcG9ydCB7IERFRkFVTFRfQ0xJX0ZMQUdTIH0gZnJvbSAnLi9ydW5uZXIvY29uc3RhbnRzLmpzJztcbmltcG9ydCB0eXBlIHsgTGlnaHRob3VzZUNsaUZsYWdzIH0gZnJvbSAnLi9ydW5uZXIvdHlwZXMuanMnO1xuaW1wb3J0IHR5cGUgeyBMaWdodGhvdXNlT3B0aW9ucyB9IGZyb20gJy4vdHlwZXMuanMnO1xuXG5jb25zdCB7IG9ubHlDYXRlZ29yaWVzLCAuLi5vcmlnaW5hbERlZmF1bHRDbGlGbGFncyB9ID0gREVGQVVMVF9DTElfRkxBR1M7XG5leHBvcnQgY29uc3QgREVGQVVMVF9MSUdIVEhPVVNFX09QVElPTlMgPSB7XG4gIC4uLm9yaWdpbmFsRGVmYXVsdENsaUZsYWdzLFxuICBvbmx5R3JvdXBzOiBvbmx5Q2F0ZWdvcmllcyxcbn0gc2F0aXNmaWVzIExpZ2h0aG91c2VPcHRpb25zO1xuXG4vLyBOT1RFOlxuLy8gVGhpcyBpcyBhbiBpbnRlcm1lZGlhdGUgdmFyaWFibGUgdG8gZ2V0IGBVbnN1cHBvcnRlZENsaUZsYWdzYC4gRm9yIHVua25vd24gcmVhc29ucyBgdHlwZXNjcmlwdEA1LjMuM2AgZG9lc24ndCB3b3JrIG90aGVyd2lzZS5cbmNvbnN0IGxpZ2h0aG91c2VVbnN1cHBvcnRlZENsaUZsYWdzID0gW1xuICAncHJlY29tcHV0ZWRMYW50ZXJuRGF0YVBhdGgnLCAvLyBQYXRoIHRvIHRoZSBmaWxlIHdoZXJlIHByZWNvbXB1dGVkIGxhbnRlcm4gZGF0YSBzaG91bGQgYmUgcmVhZCBmcm9tLlxuICAnY2hyb21lSWdub3JlRGVmYXVsdEZsYWdzJywgLy8gaWdub3JlIGRlZmF1bHQgZmxhZ3MgZnJvbSBMaWdodGhvdXNlIENMSVxuICAvLyBObyBlcnJvciByZXBvcnRpbmcgaW1wbGVtZW50ZWQgYXMgaW4gdGhlIHNvdXJjZSBTZW50cnkgd2FzIGludm9sdmVkXG4gIC8vIFNlZTogaHR0cHM6Ly9naXRodWIuY29tL0dvb2dsZUNocm9tZS9saWdodGhvdXNlL2Jsb2IvZDhjY2Y3MDY5MjIxNmI3ZmEwNDdhNGVhYTJkMTI3N2IwYjdmZTk0Ny9jbGkvYmluLmpzI0wxMjRcbiAgJ2VuYWJsZUVycm9yUmVwb3J0aW5nJywgLy8gZW5hYmxlIGVycm9yIHJlcG9ydGluZ1xuICAvLyBsaWdodGhvdXNlIENMSSBzcGVjaWZpYyBkZWJ1ZyBsb2dzXG4gICdsaXN0LWFsbC1hdWRpdHMnLCAvLyBQcmludHMgYSBsaXN0IG9mIGFsbCBhdmFpbGFibGUgYXVkaXRzIGFuZCBleGl0cy5cbiAgJ2xpc3QtbG9jYWxlcycsIC8vIFByaW50cyBhIGxpc3Qgb2YgYWxsIHN1cHBvcnRlZCBsb2NhbGVzIGFuZCBleGl0cy5cbiAgJ2xpc3QtdHJhY2UtY2F0ZWdvcmllcycsIC8vIFByaW50cyBhIGxpc3Qgb2YgYWxsIHJlcXVpcmVkIHRyYWNlIGNhdGVnb3JpZXMgYW5kIGV4aXRzLlxuXSBhcyBjb25zdDtcbnR5cGUgVW5zdXBwb3J0ZWRDbGlGbGFncyA9ICh0eXBlb2YgbGlnaHRob3VzZVVuc3VwcG9ydGVkQ2xpRmxhZ3MpW251bWJlcl07XG5cbmNvbnN0IExJR0hUSE9VU0VfVU5TVVBQT1JURURfQ0xJX0ZMQUdTID0gbmV3IFNldChsaWdodGhvdXNlVW5zdXBwb3J0ZWRDbGlGbGFncyk7XG5cbmNvbnN0IFJFRklORURfU1RSSU5HX09SX1NUUklOR19BUlJBWSA9IG5ldyBTZXQoW1xuICAnb25seUF1ZGl0cycsXG4gICdvbmx5Q2F0ZWdvcmllcycsXG4gICdza2lwQXVkaXRzJyxcbiAgJ2J1ZGdldHMnLFxuICAnY2hyb21lRmxhZ3MnLFxuXSk7XG5cbmV4cG9ydCBmdW5jdGlvbiBub3JtYWxpemVGbGFncyhmbGFncz86IExpZ2h0aG91c2VPcHRpb25zKTogTGlnaHRob3VzZUNsaUZsYWdzIHtcbiAgY29uc3QgcHJlZmlsbGVkRmxhZ3MgPSB7IC4uLkRFRkFVTFRfTElHSFRIT1VTRV9PUFRJT05TLCAuLi5mbGFncyB9O1xuXG4gIGxvZ1Vuc3VwcG9ydGVkRmxhZ3NJblVzZShwcmVmaWxsZWRGbGFncyk7XG5cbiAgcmV0dXJuIE9iamVjdC5mcm9tRW50cmllcyhcbiAgICBPYmplY3QuZW50cmllcyhwcmVmaWxsZWRGbGFncylcbiAgICAgIC5maWx0ZXIoXG4gICAgICAgIChbZmxhZ05hbWVdKSA9PlxuICAgICAgICAgICFMSUdIVEhPVVNFX1VOU1VQUE9SVEVEX0NMSV9GTEFHUy5oYXMoXG4gICAgICAgICAgICBmbGFnTmFtZSBhcyBVbnN1cHBvcnRlZENsaUZsYWdzLFxuICAgICAgICAgICksXG4gICAgICApXG4gICAgICAvLyBpbiBjb2RlLXB1c2h1cCBsaWdodGhvdXNlIGNhdGVnb3JpZXMgYXJlIG1hcHBlZCBhcyBncm91cHMsIHRoZXJlZm9yIHdlIGhhZCB0byByZW5hbWUgXCJvbmx5Q2F0ZWdvcmllc1wiIHRvIFwib25seUdyb3Vwc1wiIGZvciB0aGUgdXNlciBvZiB0aGUgcGx1Z2luIGFzIGl0IHdhcyBjb25mdXNpbmdcbiAgICAgIC5tYXAoKFtrZXksIHZdKSA9PiBba2V5ID09PSAnb25seUdyb3VwcycgPyAnb25seUNhdGVnb3JpZXMnIDoga2V5LCB2XSlcbiAgICAgIC8vIG9ubHlBdWRpdHMgYW5kIG9ubHlDYXRlZ29yaWVzIGNhbm5vdCBiZSBlbXB0eSBhcnJheXMsIG90aGVyd2lzZSBza2lwQXVkaXRzIGlzIGlnbm9yZWQgYnkgbGlnaHRob3VzZVxuICAgICAgLmZpbHRlcigoW18sIHZdKSA9PiAhKEFycmF5LmlzQXJyYXkodikgJiYgdi5sZW5ndGggPT09IDApKVxuICAgICAgLy8gdW5kZWZpbmVkIHwgc3RyaW5nIHwgc3RyaW5nW10gPT4gc3RyaW5nW10gKGVtcHR5IGZvciB1bmRlZmluZWQpXG4gICAgICAubWFwKChba2V5LCB2XSkgPT4ge1xuICAgICAgICBpZiAoIVJFRklORURfU1RSSU5HX09SX1NUUklOR19BUlJBWS5oYXMoa2V5IGFzIG5ldmVyKSkge1xuICAgICAgICAgIHJldHVybiBba2V5LCB2XTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gW2tleSwgQXJyYXkuaXNBcnJheSh2KSA/IHYgOiB2ID09IG51bGwgPyBbXSA6IFt2XV07XG4gICAgICB9KSxcbiAgKSBhcyBMaWdodGhvdXNlQ2xpRmxhZ3M7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsb2dVbnN1cHBvcnRlZEZsYWdzSW5Vc2UoXG4gIGZsYWdzOiBMaWdodGhvdXNlT3B0aW9ucyxcbiAgZGlzcGxheUNvdW50ID0gMyxcbikge1xuICBjb25zdCB1bnN1cHBvcnRlZEZsYWdzSW5Vc2UgPSBPYmplY3Qua2V5cyhmbGFncykuZmlsdGVyKGZsYWcgPT5cbiAgICBMSUdIVEhPVVNFX1VOU1VQUE9SVEVEX0NMSV9GTEFHUy5oYXMoZmxhZyBhcyBVbnN1cHBvcnRlZENsaUZsYWdzKSxcbiAgKTtcbiAgaWYgKHVuc3VwcG9ydGVkRmxhZ3NJblVzZS5sZW5ndGggPiAwKSB7XG4gICAgY29uc3QgcG9zdEZpeCA9IChjb3VudDogbnVtYmVyKSA9PlxuICAgICAgY291bnQgPiBkaXNwbGF5Q291bnQgPyBgIGFuZCAke2NvdW50IC0gZGlzcGxheUNvdW50fSBtb3JlLmAgOiAnJztcbiAgICB1aSgpLmxvZ2dlci5kZWJ1ZyhcbiAgICAgIGAke3llbGxvdygnXHUyNkEwJyl9IFBsdWdpbiAke2JvbGQoXG4gICAgICAgIExJR0hUSE9VU0VfUExVR0lOX1NMVUcsXG4gICAgICApfSB1c2VkIHVuc3VwcG9ydGVkIGZsYWdzOiAke2JvbGQoXG4gICAgICAgIHVuc3VwcG9ydGVkRmxhZ3NJblVzZS5zbGljZSgwLCBkaXNwbGF5Q291bnQpLmpvaW4oJywgJyksXG4gICAgICApfSR7cG9zdEZpeCh1bnN1cHBvcnRlZEZsYWdzSW5Vc2UubGVuZ3RoKX1gLFxuICAgICk7XG4gIH1cbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL2NvbnN0YW50cy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lclwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lci9jb25zdGFudHMudHNcIjtpbXBvcnQge1xuICB0eXBlIENsaUZsYWdzLFxuICB0eXBlIENvbmZpZyxcbiAgdHlwZSBJY3VNZXNzYWdlLFxuICB0eXBlIEF1ZGl0IGFzIExIQXVkaXQsXG4gIGRlZmF1bHRDb25maWcsXG59IGZyb20gJ2xpZ2h0aG91c2UnO1xuaW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB0eXBlIHsgQXVkaXQsIEdyb3VwIH0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5pbXBvcnQgeyBERUZBVUxUX0NIUk9NRV9GTEFHUywgTElHSFRIT1VTRV9PVVRQVVRfUEFUSCB9IGZyb20gJy4uL2NvbnN0YW50cy5qcyc7XG5cbmNvbnN0IHsgYXVkaXRzLCBjYXRlZ29yaWVzIH0gPSBkZWZhdWx0Q29uZmlnO1xuXG4vLyBpbnRlcm5hbCBpbnRlcm1lZGlhdGUgdmFyaWFibGUgdG8gZGVyaXZlIHRoZSByZWxldmFudCBhdWRpdHNcbmNvbnN0IGFsbFJhd0xpZ2h0aG91c2VBdWRpdHMgPSBhd2FpdCBQcm9taXNlLmFsbChcbiAgKGF1ZGl0cyA/PyBbXSkubWFwKGxvYWRMaWdodGhvdXNlQXVkaXQpLFxuKTtcblxuZXhwb3J0IGNvbnN0IFBMVUdJTl9TTFVHID0gJ2xpZ2h0aG91c2UnO1xuZXhwb3J0IGNvbnN0IExJR0hUSE9VU0VfTkFWSUdBVElPTl9BVURJVFM6IEF1ZGl0W10gPSBhbGxSYXdMaWdodGhvdXNlQXVkaXRzXG4gIC8vIFRoaXMgcGx1Z2luIG9ubHkgc3VwcG9ydHMgdGhlIFwibmF2aWdhdGlvblwiIG1vZGUgb2YgTGlnaHRob3VzZSBpbiB0aGUgY3VycmVudCBpbXBsZW1lbnRhdGlvblxuICAvLyBJZiB3ZSBkb24ndCBleGNsdWRlIG90aGVyIGF1ZGl0cyB3ZSB0aHJvdyBpbiB0aGUgcGx1Z2luIG91dHB1dCB2YWxpZGF0aW9uIGFzIHNvbWUgb2YgdGhlIHByb3ZpZGVkIGF1ZGl0cyBhcmUgbm90IGluY2x1ZGVkIGluIGBsaWdodGhvdXNlLXJlcG9ydC5qc29uYFxuICAuZmlsdGVyKFxuICAgIGF1ZGl0ID0+XG4gICAgICBhdWRpdC5tZXRhLnN1cHBvcnRlZE1vZGVzID09IG51bGwgfHxcbiAgICAgIChBcnJheS5pc0FycmF5KGF1ZGl0Lm1ldGEuc3VwcG9ydGVkTW9kZXMpICYmXG4gICAgICAgIGF1ZGl0Lm1ldGEuc3VwcG9ydGVkTW9kZXMuaW5jbHVkZXMoJ25hdmlnYXRpb24nKSksXG4gIClcbiAgLm1hcChhdWRpdCA9PiAoe1xuICAgIHNsdWc6IGF1ZGl0Lm1ldGEuaWQsXG4gICAgdGl0bGU6IGdldE1ldGFTdHJpbmcoYXVkaXQubWV0YS50aXRsZSksXG4gICAgZGVzY3JpcHRpb246IGdldE1ldGFTdHJpbmcoYXVkaXQubWV0YS5kZXNjcmlwdGlvbiksXG4gIH0pKTtcblxuY29uc3QgbmF2aWdhdGlvbkF1ZGl0U2x1Z3MgPSBuZXcgU2V0KFxuICBMSUdIVEhPVVNFX05BVklHQVRJT05fQVVESVRTLm1hcCgoeyBzbHVnIH0pID0+IHNsdWcpLFxuKTtcbmV4cG9ydCBjb25zdCBMSUdIVEhPVVNFX0dST1VQUzogR3JvdXBbXSA9IE9iamVjdC5lbnRyaWVzKGNhdGVnb3JpZXMgPz8ge30pLm1hcChcbiAgKFtpZCwgY2F0ZWdvcnldKSA9PiAoe1xuICAgIHNsdWc6IGlkLFxuICAgIHRpdGxlOiBnZXRNZXRhU3RyaW5nKGNhdGVnb3J5LnRpdGxlKSxcbiAgICAuLi4oY2F0ZWdvcnkuZGVzY3JpcHRpb24gJiYge1xuICAgICAgZGVzY3JpcHRpb246IGdldE1ldGFTdHJpbmcoY2F0ZWdvcnkuZGVzY3JpcHRpb24pLFxuICAgIH0pLFxuICAgIHJlZnM6IGNhdGVnb3J5LmF1ZGl0UmVmc1xuICAgICAgLmZpbHRlcigoeyBpZDogYXVkaXRTbHVnIH0pID0+IG5hdmlnYXRpb25BdWRpdFNsdWdzLmhhcyhhdWRpdFNsdWcpKVxuICAgICAgLm1hcChyZWYgPT4gKHtcbiAgICAgICAgc2x1ZzogcmVmLmlkLFxuICAgICAgICB3ZWlnaHQ6IHJlZi53ZWlnaHQsXG4gICAgICB9KSksXG4gIH0pLFxuKTtcblxuZnVuY3Rpb24gZ2V0TWV0YVN0cmluZyh2YWx1ZTogc3RyaW5nIHwgSWN1TWVzc2FnZSk6IHN0cmluZyB7XG4gIGlmICh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuIHZhbHVlO1xuICB9XG4gIHJldHVybiB2YWx1ZS5mb3JtYXR0ZWREZWZhdWx0O1xufVxuXG5hc3luYyBmdW5jdGlvbiBsb2FkTGlnaHRob3VzZUF1ZGl0KFxuICB2YWx1ZTogQ29uZmlnLkF1ZGl0SnNvbixcbik6IFByb21pc2U8dHlwZW9mIExIQXVkaXQ+IHtcbiAgLy8gdGhlIHBhc3NlZCB2YWx1ZSBkaXJlY3RseSBpbmNsdWRlcyB0aGUgaW1wbGVtZW50YXRpb24gYXMgSlMgb2JqZWN0XG4gIC8vICAgc2hhcGU6IHsgaW1wbGVtZW50YXRpb246IHR5cGVvZiBMSEF1ZGl0OyBvcHRpb25zPzoge307IH1cbiAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcgJiYgJ2ltcGxlbWVudGF0aW9uJyBpbiB2YWx1ZSkge1xuICAgIHJldHVybiB2YWx1ZS5pbXBsZW1lbnRhdGlvbjtcbiAgfVxuICAvLyB0aGUgcGFzc2VkIHZhbHVlIGlzIGEgYExILkF1ZGl0YCBjbGFzcyBpbnN0YW5jZVxuICAvLyAgIHNoYXBlOiBMSEF1ZGl0XG4gIGlmICh0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbicpIHtcbiAgICByZXR1cm4gdmFsdWU7XG4gIH1cbiAgLy8gdGhlIHBhc3NlZCB2YWx1ZSBpcyB0aGUgcGF0aCBkaXJlY3RseVxuICAvLyAgIHNoYXBlOiBzdHJpbmdcbiAgLy8gb3RoZXJ3aXNlIGl0IGlzIGEgSlMgb2JqZWN0IG1haW50YWluaW5nIGEgYHBhdGhgIHByb3BlcnR5XG4gIC8vICAgc2hhcGU6IHsgcGF0aDogc3RyaW5nLCBvcHRpb25zPzoge307IH1cbiAgY29uc3QgZmlsZSA9IHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycgPyB2YWx1ZSA6IHZhbHVlLnBhdGg7XG4gIGNvbnN0IG1vZHVsZSA9IChhd2FpdCBpbXBvcnQoYGxpZ2h0aG91c2UvY29yZS9hdWRpdHMvJHtmaWxlfS5qc2ApKSBhcyB7XG4gICAgZGVmYXVsdDogdHlwZW9mIExIQXVkaXQ7XG4gIH07XG4gIHJldHVybiBtb2R1bGUuZGVmYXVsdDtcbn1cblxuZXhwb3J0IGNvbnN0IExJR0hUSE9VU0VfUkVQT1JUX05BTUUgPSAnbGlnaHRob3VzZS1yZXBvcnQuanNvbic7XG5cbmV4cG9ydCBjb25zdCBERUZBVUxUX0NMSV9GTEFHUyA9IHtcbiAgLy8gZGVmYXVsdCB2YWx1ZXMgZXh0cmFjdGVkIGZyb21cbiAgLy8gaHR0cHM6Ly9naXRodWIuY29tL0dvb2dsZUNocm9tZS9saWdodGhvdXNlL2Jsb2IvN2Q4MDE3OGMzN2ExYjYwMGVhOGYwOTJmYzBiMDk4MDI5Nzk5YTY1OS9jbGkvY2xpLWZsYWdzLmpzI0w4MFxuICB2ZXJib3NlOiBmYWxzZSxcbiAgc2F2ZUFzc2V0czogZmFsc2UsXG4gIGNocm9tZUZsYWdzOiBERUZBVUxUX0NIUk9NRV9GTEFHUyxcbiAgcG9ydDogMCxcbiAgaG9zdG5hbWU6ICcxMjcuMC4wLjEnLFxuICB2aWV3OiBmYWxzZSxcbiAgY2hhbm5lbDogJ2NsaScsXG4gIC8vIGN1c3RvbSBvdmVyd3JpdGVzIGluIGZhdm91ciBvZiB0aGUgcGx1Z2luXG4gIC8vIGhpZGUgbG9ncyBieSBkZWZhdWx0XG4gIHF1aWV0OiB0cnVlLFxuICBvbmx5QXVkaXRzOiBbXSxcbiAgc2tpcEF1ZGl0czogW10sXG4gIG9ubHlDYXRlZ29yaWVzOiBbXSxcbiAgb3V0cHV0OiBbJ2pzb24nXSxcbiAgb3V0cHV0UGF0aDogcGF0aC5qb2luKExJR0hUSE9VU0VfT1VUUFVUX1BBVEgsIExJR0hUSE9VU0VfUkVQT1JUX05BTUUpLFxufSBzYXRpc2ZpZXMgUGFydGlhbDxDbGlGbGFncz47XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lci9ydW5uZXIudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9ydW5uZXJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9ydW5uZXIvcnVubmVyLnRzXCI7aW1wb3J0IHR5cGUgeyBSdW5uZXJSZXN1bHQgfSBmcm9tICdsaWdodGhvdXNlJztcbmltcG9ydCB7IHJ1bkxpZ2h0aG91c2UgfSBmcm9tICdsaWdodGhvdXNlL2NsaS9ydW4uanMnO1xuaW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB0eXBlIHsgQXVkaXRPdXRwdXRzLCBSdW5uZXJGdW5jdGlvbiB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgZW5zdXJlRGlyZWN0b3J5RXhpc3RzIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB7IERFRkFVTFRfQ0xJX0ZMQUdTIH0gZnJvbSAnLi9jb25zdGFudHMuanMnO1xuaW1wb3J0IHR5cGUgeyBMaWdodGhvdXNlQ2xpRmxhZ3MgfSBmcm9tICcuL3R5cGVzLmpzJztcbmltcG9ydCB7XG4gIGRldGVybWluZUFuZFNldExvZ0xldmVsLFxuICBnZXRDb25maWcsXG4gIG5vcm1hbGl6ZUF1ZGl0T3V0cHV0cyxcbiAgdG9BdWRpdE91dHB1dHMsXG59IGZyb20gJy4vdXRpbHMuanMnO1xuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlUnVubmVyRnVuY3Rpb24oXG4gIHVybFVuZGVyVGVzdDogc3RyaW5nLFxuICBmbGFnczogTGlnaHRob3VzZUNsaUZsYWdzID0gREVGQVVMVF9DTElfRkxBR1MsXG4pOiBSdW5uZXJGdW5jdGlvbiB7XG4gIHJldHVybiBhc3luYyAoKTogUHJvbWlzZTxBdWRpdE91dHB1dHM+ID0+IHtcbiAgICBjb25zdCB7XG4gICAgICBjb25maWdQYXRoLFxuICAgICAgcHJlc2V0LFxuICAgICAgb3V0cHV0UGF0aCxcbiAgICAgIC4uLnBhcnNlZEZsYWdzXG4gICAgfTogUGFydGlhbDxMaWdodGhvdXNlQ2xpRmxhZ3M+ID0gZmxhZ3M7XG5cbiAgICBjb25zdCBsb2dMZXZlbCA9IGRldGVybWluZUFuZFNldExvZ0xldmVsKHBhcnNlZEZsYWdzKTtcblxuICAgIGNvbnN0IGNvbmZpZyA9IGF3YWl0IGdldENvbmZpZyh7IGNvbmZpZ1BhdGgsIHByZXNldCB9KTtcbiAgICBpZiAob3V0cHV0UGF0aCkge1xuICAgICAgYXdhaXQgZW5zdXJlRGlyZWN0b3J5RXhpc3RzKHBhdGguZGlybmFtZShvdXRwdXRQYXRoKSk7XG4gICAgfVxuXG4gICAgY29uc3QgZW5yaWNoZWRGbGFncyA9IHtcbiAgICAgIC4uLnBhcnNlZEZsYWdzLFxuICAgICAgbG9nTGV2ZWwsXG4gICAgICBvdXRwdXRQYXRoLFxuICAgIH07XG5cbiAgICBjb25zdCBydW5uZXJSZXN1bHQ6IHVua25vd24gPSBhd2FpdCBydW5MaWdodGhvdXNlKFxuICAgICAgdXJsVW5kZXJUZXN0LFxuICAgICAgZW5yaWNoZWRGbGFncyxcbiAgICAgIGNvbmZpZyxcbiAgICApO1xuXG4gICAgaWYgKHJ1bm5lclJlc3VsdCA9PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0xpZ2h0aG91c2UgZGlkIG5vdCBwcm9kdWNlIGEgcmVzdWx0LicpO1xuICAgIH1cblxuICAgIGNvbnN0IHsgbGhyIH0gPSBydW5uZXJSZXN1bHQgYXMgUnVubmVyUmVzdWx0O1xuICAgIGNvbnN0IGF1ZGl0T3V0cHV0cyA9IHRvQXVkaXRPdXRwdXRzKE9iamVjdC52YWx1ZXMobGhyLmF1ZGl0cyksIGZsYWdzKTtcblxuICAgIHJldHVybiBub3JtYWxpemVBdWRpdE91dHB1dHMoYXVkaXRPdXRwdXRzLCBlbnJpY2hlZEZsYWdzKTtcbiAgfTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL3V0aWxzLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL3V0aWxzLnRzXCI7aW1wb3J0IHsgYm9sZCB9IGZyb20gJ2Fuc2lzJztcbmltcG9ydCB0eXBlIHsgQ29uZmlnLCBGb3JtYXR0ZWRJY3UgfSBmcm9tICdsaWdodGhvdXNlJztcbmltcG9ydCBsb2cgZnJvbSAnbGlnaHRob3VzZS1sb2dnZXInO1xuaW1wb3J0IGRlc2t0b3BDb25maWcgZnJvbSAnbGlnaHRob3VzZS9jb3JlL2NvbmZpZy9kZXNrdG9wLWNvbmZpZy5qcyc7XG5pbXBvcnQgZXhwZXJpbWVudGFsQ29uZmlnIGZyb20gJ2xpZ2h0aG91c2UvY29yZS9jb25maWcvZXhwZXJpbWVudGFsLWNvbmZpZy5qcyc7XG5pbXBvcnQgcGVyZkNvbmZpZyBmcm9tICdsaWdodGhvdXNlL2NvcmUvY29uZmlnL3BlcmYtY29uZmlnLmpzJztcbmltcG9ydCB0eXBlIERldGFpbHMgZnJvbSAnbGlnaHRob3VzZS90eXBlcy9saHIvYXVkaXQtZGV0YWlscyc7XG5pbXBvcnQgdHlwZSB7IFJlc3VsdCB9IGZyb20gJ2xpZ2h0aG91c2UvdHlwZXMvbGhyL2F1ZGl0LXJlc3VsdCc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0T3V0cHV0LCBBdWRpdE91dHB1dHMgfSBmcm9tICdAY29kZS1wdXNodXAvbW9kZWxzJztcbmltcG9ydCB7XG4gIGZvcm1hdFJlcG9ydFNjb3JlLFxuICBpbXBvcnRNb2R1bGUsXG4gIHJlYWRKc29uRmlsZSxcbiAgdWksXG59IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgdHlwZSB7IExpZ2h0aG91c2VPcHRpb25zIH0gZnJvbSAnLi4vdHlwZXMuanMnO1xuaW1wb3J0IHsgbG9nVW5zdXBwb3J0ZWREZXRhaWxzLCB0b0F1ZGl0RGV0YWlscyB9IGZyb20gJy4vZGV0YWlscy9kZXRhaWxzLmpzJztcbmltcG9ydCB0eXBlIHsgTGlnaHRob3VzZUNsaUZsYWdzIH0gZnJvbSAnLi90eXBlcy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBub3JtYWxpemVBdWRpdE91dHB1dHMoXG4gIGF1ZGl0T3V0cHV0czogQXVkaXRPdXRwdXRzLFxuICBmbGFnczogTGlnaHRob3VzZU9wdGlvbnMgPSB7IHNraXBBdWRpdHM6IFtdIH0sXG4pOiBBdWRpdE91dHB1dHMge1xuICBjb25zdCB0b1NraXAgPSBuZXcgU2V0KGZsYWdzLnNraXBBdWRpdHMgPz8gW10pO1xuICByZXR1cm4gYXVkaXRPdXRwdXRzLmZpbHRlcigoeyBzbHVnIH0pID0+ICF0b1NraXAuaGFzKHNsdWcpKTtcbn1cblxuZXhwb3J0IGNsYXNzIExpZ2h0aG91c2VBdWRpdFBhcnNpbmdFcnJvciBleHRlbmRzIEVycm9yIHtcbiAgY29uc3RydWN0b3Ioc2x1Zzogc3RyaW5nLCBlcnJvcjogRXJyb3IpIHtcbiAgICBzdXBlcihgXFxuQXVkaXQgJHtib2xkKHNsdWcpfSBmYWlsZWQgcGFyc2luZyBkZXRhaWxzOiBcXG4ke2Vycm9yLm1lc3NhZ2V9YCk7XG4gIH1cbn1cblxuZnVuY3Rpb24gZm9ybWF0QmFzZUF1ZGl0T3V0cHV0KGxockF1ZGl0OiBSZXN1bHQpOiBBdWRpdE91dHB1dCB7XG4gIGNvbnN0IHtcbiAgICBpZDogc2x1ZyxcbiAgICBzY29yZSxcbiAgICBudW1lcmljVmFsdWUsXG4gICAgZGlzcGxheVZhbHVlLFxuICAgIHNjb3JlRGlzcGxheU1vZGUsXG4gIH0gPSBsaHJBdWRpdDtcbiAgcmV0dXJuIHtcbiAgICBzbHVnLFxuICAgIHNjb3JlOiBzY29yZSA/PyAxLFxuICAgIHZhbHVlOiBudW1lcmljVmFsdWUgPz8gc2NvcmUgPz8gMCxcbiAgICBkaXNwbGF5VmFsdWU6XG4gICAgICBkaXNwbGF5VmFsdWUgPz9cbiAgICAgIChzY29yZURpc3BsYXlNb2RlID09PSAnYmluYXJ5J1xuICAgICAgICA/IHNjb3JlID09PSAxXG4gICAgICAgICAgPyAncGFzc2VkJ1xuICAgICAgICAgIDogJ2ZhaWxlZCdcbiAgICAgICAgOiBzY29yZVxuICAgICAgICAgID8gYCR7Zm9ybWF0UmVwb3J0U2NvcmUoc2NvcmUpfSVgXG4gICAgICAgICAgOiB1bmRlZmluZWQpLFxuICB9O1xufVxuXG5mdW5jdGlvbiBwcm9jZXNzQXVkaXREZXRhaWxzKFxuICBhdWRpdE91dHB1dDogQXVkaXRPdXRwdXQsXG4gIGRldGFpbHM6IEZvcm1hdHRlZEljdTxEZXRhaWxzPixcbik6IEF1ZGl0T3V0cHV0IHtcbiAgdHJ5IHtcbiAgICBjb25zdCBwYXJzZWREZXRhaWxzID0gdG9BdWRpdERldGFpbHMoZGV0YWlscyk7XG4gICAgcmV0dXJuIE9iamVjdC5rZXlzKHBhcnNlZERldGFpbHMpLmxlbmd0aCA+IDBcbiAgICAgID8geyAuLi5hdWRpdE91dHB1dCwgZGV0YWlsczogcGFyc2VkRGV0YWlscyB9XG4gICAgICA6IGF1ZGl0T3V0cHV0O1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIHRocm93IG5ldyBMaWdodGhvdXNlQXVkaXRQYXJzaW5nRXJyb3IoYXVkaXRPdXRwdXQuc2x1ZywgZXJyb3IgYXMgRXJyb3IpO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB0b0F1ZGl0T3V0cHV0cyhcbiAgbGhyQXVkaXRzOiBSZXN1bHRbXSxcbiAgeyB2ZXJib3NlID0gZmFsc2UgfTogeyB2ZXJib3NlPzogYm9vbGVhbiB9ID0ge30sXG4pOiBBdWRpdE91dHB1dHMge1xuICBpZiAodmVyYm9zZSkge1xuICAgIGxvZ1Vuc3VwcG9ydGVkRGV0YWlscyhsaHJBdWRpdHMpO1xuICB9XG4gIHJldHVybiBsaHJBdWRpdHMubWFwKGF1ZGl0ID0+IHtcbiAgICBjb25zdCBhdWRpdE91dHB1dCA9IGZvcm1hdEJhc2VBdWRpdE91dHB1dChhdWRpdCk7XG5cbiAgICByZXR1cm4gYXVkaXQuZGV0YWlscyA9PSBudWxsXG4gICAgICA/IGF1ZGl0T3V0cHV0XG4gICAgICA6IHByb2Nlc3NBdWRpdERldGFpbHMoYXVkaXRPdXRwdXQsIGF1ZGl0LmRldGFpbHMpO1xuICB9KTtcbn1cblxuZXhwb3J0IHR5cGUgTGlnaHRob3VzZUxvZ0xldmVsID1cbiAgfCAndmVyYm9zZSdcbiAgfCAnZXJyb3InXG4gIHwgJ2luZm8nXG4gIHwgJ3NpbGVudCdcbiAgfCAnd2FybidcbiAgfCB1bmRlZmluZWQ7XG5leHBvcnQgZnVuY3Rpb24gZGV0ZXJtaW5lQW5kU2V0TG9nTGV2ZWwoe1xuICB2ZXJib3NlLFxuICBxdWlldCxcbn06IHtcbiAgdmVyYm9zZT86IGJvb2xlYW47XG4gIHF1aWV0PzogYm9vbGVhbjtcbn0gPSB7fSk6IExpZ2h0aG91c2VMb2dMZXZlbCB7XG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBmdW5jdGlvbmFsL25vLWxldFxuICBsZXQgbG9nTGV2ZWw6IExpZ2h0aG91c2VMb2dMZXZlbCA9ICdpbmZvJztcbiAgLy8gc2V0IGxvZ2dpbmcgcHJlZmVyZW5jZXNcbiAgaWYgKHZlcmJvc2UpIHtcbiAgICBsb2dMZXZlbCA9ICd2ZXJib3NlJztcbiAgfSBlbHNlIGlmIChxdWlldCkge1xuICAgIGxvZ0xldmVsID0gJ3NpbGVudCc7XG4gIH1cblxuICBsb2cuc2V0TGV2ZWwobG9nTGV2ZWwpO1xuXG4gIHJldHVybiBsb2dMZXZlbDtcbn1cblxuZXhwb3J0IHR5cGUgQ29uZmlnT3B0aW9ucyA9IFBhcnRpYWw8XG4gIFBpY2s8TGlnaHRob3VzZUNsaUZsYWdzLCAnY29uZmlnUGF0aCcgfCAncHJlc2V0Jz5cbj47XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRDb25maWcoXG4gIG9wdGlvbnM6IENvbmZpZ09wdGlvbnMgPSB7fSxcbik6IFByb21pc2U8Q29uZmlnIHwgdW5kZWZpbmVkPiB7XG4gIGNvbnN0IHsgY29uZmlnUGF0aDogZmlsZXBhdGgsIHByZXNldCB9ID0gb3B0aW9ucztcblxuICBpZiAoZmlsZXBhdGggIT0gbnVsbCkge1xuICAgIGlmIChmaWxlcGF0aC5lbmRzV2l0aCgnLmpzb24nKSkge1xuICAgICAgLy8gUmVzb2x2ZSB0aGUgY29uZmlnIGZpbGUgcGF0aCByZWxhdGl2ZSB0byB3aGVyZSBjbGkgd2FzIGNhbGxlZC5cbiAgICAgIHJldHVybiByZWFkSnNvbkZpbGU8Q29uZmlnPihmaWxlcGF0aCk7XG4gICAgfSBlbHNlIGlmICgvXFwuKHRzfGpzfG1qcykkLy50ZXN0KGZpbGVwYXRoKSkge1xuICAgICAgcmV0dXJuIGltcG9ydE1vZHVsZTxDb25maWc+KHsgZmlsZXBhdGgsIGZvcm1hdDogJ2VzbScgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHVpKCkubG9nZ2VyLmluZm8oYEZvcm1hdCBvZiBmaWxlICR7ZmlsZXBhdGh9IG5vdCBzdXBwb3J0ZWRgKTtcbiAgICB9XG4gIH0gZWxzZSBpZiAocHJlc2V0ICE9IG51bGwpIHtcbiAgICBzd2l0Y2ggKHByZXNldCkge1xuICAgICAgY2FzZSAnZGVza3RvcCc6XG4gICAgICAgIHJldHVybiBkZXNrdG9wQ29uZmlnO1xuICAgICAgY2FzZSAncGVyZic6XG4gICAgICAgIHJldHVybiBwZXJmQ29uZmlnIGFzIENvbmZpZztcbiAgICAgIGNhc2UgJ2V4cGVyaW1lbnRhbCc6XG4gICAgICAgIHJldHVybiBleHBlcmltZW50YWxDb25maWcgYXMgQ29uZmlnO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgLy8gYXMgcHJlc2V0IGlzIGEgc3RyaW5nIGxpdGVyYWwgdGhlIGRlZmF1bHQgY2FzZSBoZXJlIGlzIG5vcm1hbGx5IGNhdWdodCBieSBUUyBhbmQgbm90IHBvc3NpYmxlIHRvIGhhcHBlbi4gTm93IGluIHJlYWxpdHkgaXQgY2FuIGhhcHBlbiBhbmQgcHJlc2V0IGNvdWxkIGJlIGEgc3RyaW5nIG5vdCBpbmNsdWRlZCBpbiB0aGUgbGl0ZXJhbC5cbiAgICAgICAgLy8gVGhlcmVmb3JlLCB3ZSBoYXZlIHRvIHVzZSBgYXMgc3RyaW5nYC4gT3RoZXJ3aXNlLCBpdCB3aWxsIGNvbnNpZGVyIHByZXNldCBhcyB0eXBlIG5ldmVyXG4gICAgICAgIHVpKCkubG9nZ2VyLmluZm8oYFByZXNldCBcIiR7cHJlc2V0IGFzIHN0cmluZ31cIiBpcyBub3Qgc3VwcG9ydGVkYCk7XG4gICAgfVxuICB9XG4gIHJldHVybiB1bmRlZmluZWQ7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lci9kZXRhaWxzL2RldGFpbHMudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9ydW5uZXIvZGV0YWlsc1wiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lci9kZXRhaWxzL2RldGFpbHMudHNcIjtpbXBvcnQgeyBib2xkLCB5ZWxsb3cgfSBmcm9tICdhbnNpcyc7XG5pbXBvcnQgdHlwZSB7IEZvcm1hdHRlZEljdSB9IGZyb20gJ2xpZ2h0aG91c2UnO1xuaW1wb3J0IHR5cGUgRGV0YWlscyBmcm9tICdsaWdodGhvdXNlL3R5cGVzL2xoci9hdWRpdC1kZXRhaWxzJztcbmltcG9ydCB0eXBlIHsgUmVzdWx0IH0gZnJvbSAnbGlnaHRob3VzZS90eXBlcy9saHIvYXVkaXQtcmVzdWx0JztcbmltcG9ydCB0eXBlIHsgQXVkaXREZXRhaWxzLCBUYWJsZSB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgdWkgfSBmcm9tICdAY29kZS1wdXNodXAvdXRpbHMnO1xuaW1wb3J0IHsgUExVR0lOX1NMVUcgfSBmcm9tICcuLi9jb25zdGFudHMuanMnO1xuaW1wb3J0IHsgcGFyc2VPcHBvcnR1bml0eVRvQXVkaXREZXRhaWxzVGFibGUgfSBmcm9tICcuL29wcG9ydHVuaXR5LnR5cGUuanMnO1xuaW1wb3J0IHsgcGFyc2VUYWJsZVRvQXVkaXREZXRhaWxzVGFibGUgfSBmcm9tICcuL3RhYmxlLnR5cGUuanMnO1xuXG5leHBvcnQgZnVuY3Rpb24gdG9BdWRpdERldGFpbHM8VCBleHRlbmRzIEZvcm1hdHRlZEljdTxEZXRhaWxzPj4oXG4gIGRldGFpbHM6IFQgfCB1bmRlZmluZWQsXG4pOiBBdWRpdERldGFpbHMge1xuICBpZiAoZGV0YWlscyA9PSBudWxsKSB7XG4gICAgcmV0dXJuIHt9O1xuICB9XG5cbiAgY29uc3QgeyB0eXBlIH0gPSBkZXRhaWxzO1xuXG4gIHN3aXRjaCAodHlwZSkge1xuICAgIGNhc2UgJ3RhYmxlJzpcbiAgICAgIGNvbnN0IHRhYmxlOiBUYWJsZSB8IHVuZGVmaW5lZCA9IHBhcnNlVGFibGVUb0F1ZGl0RGV0YWlsc1RhYmxlKGRldGFpbHMpO1xuICAgICAgcmV0dXJuIHRhYmxlID8geyB0YWJsZSB9IDoge307XG4gICAgY2FzZSAnb3Bwb3J0dW5pdHknOlxuICAgICAgY29uc3Qgb3Bwb3J0dW5pdHk6IFRhYmxlIHwgdW5kZWZpbmVkID1cbiAgICAgICAgcGFyc2VPcHBvcnR1bml0eVRvQXVkaXREZXRhaWxzVGFibGUoZGV0YWlscyk7XG4gICAgICByZXR1cm4gb3Bwb3J0dW5pdHkgPyB7IHRhYmxlOiBvcHBvcnR1bml0eSB9IDoge307XG4gIH1cbiAgcmV0dXJuIHt9O1xufVxuXG4vLyBAVE9ETyBpbXBsZW1lbnQgYWxsIGRldGFpbHNcbmV4cG9ydCBjb25zdCB1bnN1cHBvcnRlZERldGFpbFR5cGVzID0gbmV3IFNldChbXG4gICdkZWJ1Z2RhdGEnLFxuICAndHJlZW1hcC1kYXRhJyxcbiAgJ3NjcmVlbnNob3QnLFxuICAnZmlsbXN0cmlwJyxcbiAgJ2NyaXRpY2FscmVxdWVzdGNoYWluJyxcbl0pO1xuXG5leHBvcnQgZnVuY3Rpb24gbG9nVW5zdXBwb3J0ZWREZXRhaWxzKFxuICBsaHJBdWRpdHM6IFJlc3VsdFtdLFxuICB7IGRpc3BsYXlDb3VudCA9IDMgfTogeyBkaXNwbGF5Q291bnQ/OiBudW1iZXIgfSA9IHt9LFxuKSB7XG4gIGNvbnN0IHNsdWdzV2l0aERldGFpbFBhcnNpbmdFcnJvcnMgPSBbXG4gICAgLi4ubmV3IFNldChcbiAgICAgIGxockF1ZGl0c1xuICAgICAgICAuZmlsdGVyKCh7IGRldGFpbHMgfSkgPT5cbiAgICAgICAgICB1bnN1cHBvcnRlZERldGFpbFR5cGVzLmhhcyhkZXRhaWxzPy50eXBlIGFzIHN0cmluZyksXG4gICAgICAgIClcbiAgICAgICAgLm1hcCgoeyBkZXRhaWxzIH0pID0+IGRldGFpbHM/LnR5cGUpLFxuICAgICksXG4gIF07XG4gIGlmIChzbHVnc1dpdGhEZXRhaWxQYXJzaW5nRXJyb3JzLmxlbmd0aCA+IDApIHtcbiAgICBjb25zdCBwb3N0Rml4ID0gKGNvdW50OiBudW1iZXIpID0+XG4gICAgICBjb3VudCA+IGRpc3BsYXlDb3VudCA/IGAgYW5kICR7Y291bnQgLSBkaXNwbGF5Q291bnR9IG1vcmUuYCA6ICcnO1xuICAgIHVpKCkubG9nZ2VyLmRlYnVnKFxuICAgICAgYCR7eWVsbG93KCdcdTI2QTAnKX0gUGx1Z2luICR7Ym9sZChcbiAgICAgICAgUExVR0lOX1NMVUcsXG4gICAgICApfSBza2lwcGVkIHBhcnNpbmcgb2YgdW5zdXBwb3J0ZWQgYXVkaXQgZGV0YWlsczogJHtib2xkKFxuICAgICAgICBzbHVnc1dpdGhEZXRhaWxQYXJzaW5nRXJyb3JzLnNsaWNlKDAsIGRpc3BsYXlDb3VudCkuam9pbignLCAnKSxcbiAgICAgICl9JHtwb3N0Rml4KHNsdWdzV2l0aERldGFpbFBhcnNpbmdFcnJvcnMubGVuZ3RoKX1gLFxuICAgICk7XG4gIH1cbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL2RldGFpbHMvb3Bwb3J0dW5pdHkudHlwZS50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lci9kZXRhaWxzXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL2RldGFpbHMvb3Bwb3J0dW5pdHkudHlwZS50c1wiO2ltcG9ydCB0eXBlIERldGFpbHMgZnJvbSAnbGlnaHRob3VzZS90eXBlcy9saHIvYXVkaXQtZGV0YWlscyc7XG5pbXBvcnQge1xuICB0eXBlIFRhYmxlLFxuICB0eXBlIFRhYmxlUm93T2JqZWN0LFxuICB0YWJsZVNjaGVtYSxcbn0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5pbXBvcnQgeyBmb3JtYXRCeXRlcywgZm9ybWF0RHVyYXRpb24sIGh0bWwgfSBmcm9tICdAY29kZS1wdXNodXAvdXRpbHMnO1xuaW1wb3J0IHsgcGFyc2VUYWJsZUNvbHVtbnMsIHBhcnNlVGFibGVFbnRyeSB9IGZyb20gJy4vdGFibGUudHlwZS5qcyc7XG5pbXBvcnQgeyBMaWdodGhvdXNlQXVkaXREZXRhaWxzUGFyc2luZ0Vycm9yIH0gZnJvbSAnLi91dGlscy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBwYXJzZU9wcG9ydHVuaXR5VG9BdWRpdERldGFpbHNUYWJsZShcbiAgZGV0YWlsczogRGV0YWlscy5PcHBvcnR1bml0eSxcbik6IFRhYmxlIHwgdW5kZWZpbmVkIHtcbiAgY29uc3QgeyBoZWFkaW5nczogcmF3SGVhZGluZ3MsIGl0ZW1zIH0gPSBkZXRhaWxzO1xuXG4gIGlmIChpdGVtcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG5cbiAgdHJ5IHtcbiAgICByZXR1cm4gdGFibGVTY2hlbWEoKS5wYXJzZSh7XG4gICAgICB0aXRsZTogJ09wcG9ydHVuaXR5JyxcbiAgICAgIGNvbHVtbnM6IHBhcnNlVGFibGVDb2x1bW5zKHJhd0hlYWRpbmdzKSxcbiAgICAgIHJvd3M6IGl0ZW1zLm1hcChyb3cgPT4gcGFyc2VPcHBvcnR1bml0eUl0ZW1Ub1RhYmxlUm93KHJvdywgcmF3SGVhZGluZ3MpKSxcbiAgICB9KTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICB0aHJvdyBuZXcgTGlnaHRob3VzZUF1ZGl0RGV0YWlsc1BhcnNpbmdFcnJvcihcbiAgICAgICdvcHBvcnR1bml0eScsXG4gICAgICB7IGl0ZW1zLCBoZWFkaW5nczogcmF3SGVhZGluZ3MgfSxcbiAgICAgIChlcnJvciBhcyBFcnJvcikubWVzc2FnZS50b1N0cmluZygpLFxuICAgICk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlT3Bwb3J0dW5pdHlJdGVtVG9UYWJsZVJvdyhcbiAgb3Bwb3J0dW5pdHlJdGVtOiBEZXRhaWxzLk9wcG9ydHVuaXR5SXRlbSxcbiAgaGVhZGluZ3M6IERldGFpbHMuVGFibGVDb2x1bW5IZWFkaW5nW10sXG4pOiBUYWJsZVJvd09iamVjdCB7XG4gIGNvbnN0IGtleXMgPSBuZXcgU2V0KGhlYWRpbmdzLm1hcCgoeyBrZXkgfSkgPT4ga2V5KSk7XG4gIGNvbnN0IHZhbHVlVHlwZXNCeUtleSA9IG5ldyBNYXAoXG4gICAgaGVhZGluZ3MubWFwKCh7IGtleSwgdmFsdWVUeXBlIH0pID0+IFtrZXksIHZhbHVlVHlwZV0pLFxuICApO1xuXG4gIHJldHVybiB7XG4gICAgLi4uKE9iamVjdC5mcm9tRW50cmllcyhcbiAgICAgIE9iamVjdC5lbnRyaWVzKG9wcG9ydHVuaXR5SXRlbSlcbiAgICAgICAgLy8gZm9yd2FyZCBvbmx5IHByb3BlcnRpZXMgd2l0aCBhIGdpdmVuIHZhbHVlXG4gICAgICAgIC5maWx0ZXIoKFtrZXldKSA9PiBrZXlzLmhhcyhrZXkpKVxuICAgICAgICAubWFwKChba2V5LCB2YWx1ZV0pID0+IHtcbiAgICAgICAgICBjb25zdCB2YWx1ZVR5cGUgPSB2YWx1ZVR5cGVzQnlLZXkuZ2V0KGtleSkgYXMgRGV0YWlscy5JdGVtVmFsdWVUeXBlO1xuICAgICAgICAgIHJldHVybiBwYXJzZU9wcG9ydHVuaXR5RW50cnkoW2tleSwgdmFsdWVdLCB2YWx1ZVR5cGUpO1xuICAgICAgICB9KSxcbiAgICApIGFzIFRhYmxlUm93T2JqZWN0KSxcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlT3Bwb3J0dW5pdHlFbnRyeShcbiAgW2tleSwgdmFsdWVdOiBbXG4gICAga2V5b2YgRGV0YWlscy5PcHBvcnR1bml0eUl0ZW0sXG4gICAgRGV0YWlscy5PcHBvcnR1bml0eUl0ZW1bc3RyaW5nXSxcbiAgXSxcbiAgdmFsdWVUeXBlOiBEZXRhaWxzLkl0ZW1WYWx1ZVR5cGUsXG4pIHtcbiAgc3dpdGNoIChrZXkpIHtcbiAgICBjYXNlICd1cmwnOlxuICAgICAgcmV0dXJuIFtrZXksIGh0bWwubGluayhTdHJpbmcodmFsdWUpKV07XG4gICAgY2FzZSAnd2FzdGVkUGVyY2VudCc6XG4gICAgICByZXR1cm4gW2tleSwgYCR7TnVtYmVyKHZhbHVlKS50b0ZpeGVkKDIpfSAlYF07XG4gICAgY2FzZSAndG90YWxCeXRlcyc6XG4gICAgY2FzZSAnd2FzdGVkQnl0ZXMnOlxuICAgICAgcmV0dXJuIFtrZXksIGZvcm1hdEJ5dGVzKE51bWJlcih2YWx1ZSkpXTtcbiAgICBjYXNlICd3YXN0ZWRNcyc6XG4gICAgICByZXR1cm4gW2tleSwgZm9ybWF0RHVyYXRpb24oTnVtYmVyKHZhbHVlKSldO1xuICAgIGRlZmF1bHQ6XG4gICAgICByZXR1cm4gcGFyc2VUYWJsZUVudHJ5KFtrZXksIHZhbHVlXSwgdmFsdWVUeXBlKTtcbiAgfVxufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9ydW5uZXIvZGV0YWlscy90YWJsZS50eXBlLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL2RldGFpbHNcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9ydW5uZXIvZGV0YWlscy90YWJsZS50eXBlLnRzXCI7aW1wb3J0IHR5cGUgRGV0YWlscyBmcm9tICdsaWdodGhvdXNlL3R5cGVzL2xoci9hdWRpdC1kZXRhaWxzJztcbmltcG9ydCB7XG4gIHR5cGUgVGFibGUsXG4gIHR5cGUgVGFibGVDb2x1bW5PYmplY3QsXG4gIHR5cGUgVGFibGVSb3dPYmplY3QsXG4gIHRhYmxlU2NoZW1hLFxufSBmcm9tICdAY29kZS1wdXNodXAvbW9kZWxzJztcbmltcG9ydCB7IGZvcm1hdFRhYmxlSXRlbVByb3BlcnR5VmFsdWUgfSBmcm9tICcuL2l0ZW0tdmFsdWUuanMnO1xuaW1wb3J0IHsgTGlnaHRob3VzZUF1ZGl0RGV0YWlsc1BhcnNpbmdFcnJvciB9IGZyb20gJy4vdXRpbHMuanMnO1xuXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VUYWJsZVRvQXVkaXREZXRhaWxzVGFibGUoXG4gIGRldGFpbHM6IERldGFpbHMuVGFibGUsXG4pOiBUYWJsZSB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IHsgaGVhZGluZ3M6IHJhd0hlYWRpbmdzLCBpdGVtcyB9ID0gZGV0YWlscztcblxuICBpZiAoaXRlbXMubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuXG4gIHRyeSB7XG4gICAgcmV0dXJuIHRhYmxlU2NoZW1hKCkucGFyc2Uoe1xuICAgICAgY29sdW1uczogcGFyc2VUYWJsZUNvbHVtbnMocmF3SGVhZGluZ3MpLFxuICAgICAgcm93czogaXRlbXMubWFwKHJvdyA9PiBwYXJzZVRhYmxlUm93KHJvdywgcmF3SGVhZGluZ3MpKSxcbiAgICB9KTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICB0aHJvdyBuZXcgTGlnaHRob3VzZUF1ZGl0RGV0YWlsc1BhcnNpbmdFcnJvcihcbiAgICAgICd0YWJsZScsXG4gICAgICB7IGl0ZW1zLCBoZWFkaW5nczogcmF3SGVhZGluZ3MgfSxcbiAgICAgIChlcnJvciBhcyBFcnJvcikubWVzc2FnZS50b1N0cmluZygpLFxuICAgICk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlVGFibGVDb2x1bW5zKFxuICByYXdIZWFkaW5nczogRGV0YWlscy5UYWJsZUNvbHVtbkhlYWRpbmdbXSxcbik6IFRhYmxlQ29sdW1uT2JqZWN0W10ge1xuICByZXR1cm4gcmF3SGVhZGluZ3MubWFwKCh7IGtleSwgbGFiZWwgfSkgPT4gKHtcbiAgICBrZXk6IGtleSA/PyAnJyxcbiAgICAuLi4odHlwZW9mIGxhYmVsID09PSAnc3RyaW5nJyAmJiBsYWJlbC5sZW5ndGggPiAwID8geyBsYWJlbCB9IDoge30pLFxuICAgIGFsaWduOiAnbGVmdCcsXG4gIH0pKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlVGFibGVSb3coXG4gIHRhYmxlSXRlbTogRGV0YWlscy5UYWJsZUl0ZW0sXG4gIGhlYWRpbmdzOiBEZXRhaWxzLlRhYmxlQ29sdW1uSGVhZGluZ1tdLFxuKTogVGFibGVSb3dPYmplY3Qge1xuICBjb25zdCBrZXlzID0gbmV3IFNldChoZWFkaW5ncy5tYXAoKHsga2V5IH0pID0+IGtleSkpO1xuICBjb25zdCB2YWx1ZVR5cGVzQnlLZXkgPSBuZXcgTWFwKFxuICAgIGhlYWRpbmdzLm1hcCgoeyBrZXksIHZhbHVlVHlwZSB9KSA9PiBba2V5LCB2YWx1ZVR5cGVdKSxcbiAgKTtcblxuICByZXR1cm4gT2JqZWN0LmZyb21FbnRyaWVzKFxuICAgIE9iamVjdC5lbnRyaWVzKHRhYmxlSXRlbSlcbiAgICAgIC5maWx0ZXIoKFtrZXldKSA9PiBrZXlzLmhhcyhrZXkpKVxuICAgICAgLm1hcCgoW2tleSwgdmFsdWVdKSA9PiB7XG4gICAgICAgIGNvbnN0IHZhbHVlVHlwZSA9IHZhbHVlVHlwZXNCeUtleS5nZXQoa2V5KTtcbiAgICAgICAgcmV0dXJuIHBhcnNlVGFibGVFbnRyeShba2V5LCB2YWx1ZV0sIHZhbHVlVHlwZSk7XG4gICAgICB9KSxcbiAgKSBhcyBUYWJsZVJvd09iamVjdDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlVGFibGVFbnRyeTxUIGV4dGVuZHMgRGV0YWlscy5UYWJsZUl0ZW0+KFxuICBba2V5LCB2YWx1ZV06IFtrZXlvZiBULCBUW2tleW9mIFRdXSxcbiAgdmFsdWVUeXBlPzogRGV0YWlscy5JdGVtVmFsdWVUeXBlLFxuKTogW2tleW9mIFQsIERldGFpbHMuSXRlbVZhbHVlIHwgdW5kZWZpbmVkXSB7XG4gIGlmICh2YWx1ZSA9PSBudWxsKSB7XG4gICAgcmV0dXJuIFtrZXksIHZhbHVlXTtcbiAgfVxuXG4gIHJldHVybiBba2V5LCBmb3JtYXRUYWJsZUl0ZW1Qcm9wZXJ0eVZhbHVlKHZhbHVlLCB2YWx1ZVR5cGUpXTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL2RldGFpbHMvaXRlbS12YWx1ZS50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lci9kZXRhaWxzXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL2RldGFpbHMvaXRlbS12YWx1ZS50c1wiO2ltcG9ydCB7IGJvbGQgfSBmcm9tICdhbnNpcyc7XG5pbXBvcnQgdHlwZSB7IEljdU1lc3NhZ2UgfSBmcm9tICdsaWdodGhvdXNlJztcbmltcG9ydCB0eXBlIERldGFpbHMgZnJvbSAnbGlnaHRob3VzZS90eXBlcy9saHIvYXVkaXQtZGV0YWlscyc7XG5pbXBvcnQge1xuICBmb3JtYXRCeXRlcyxcbiAgZm9ybWF0RHVyYXRpb24sXG4gIGh0bWwsXG4gIHRydW5jYXRlVGV4dCxcbiAgdWksXG59IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5cbmV4cG9ydCB0eXBlIFByaW1pdGl2ZUl0ZW1WYWx1ZSA9IHN0cmluZyB8IG51bWJlciB8IGJvb2xlYW47XG5leHBvcnQgdHlwZSBPYmplY3RJdGVtVmFsdWUgPSBFeGNsdWRlPFxuICBEZXRhaWxzLkl0ZW1WYWx1ZSxcbiAgUHJpbWl0aXZlSXRlbVZhbHVlIHwgSWN1TWVzc2FnZVxuPjtcbmV4cG9ydCB0eXBlIFNpbXBsZUl0ZW1WYWx1ZSA9XG4gIHwgRXh0cmFjdDxcbiAgICAgIE9iamVjdEl0ZW1WYWx1ZSxcbiAgICAgIERldGFpbHMuTnVtZXJpY1ZhbHVlIHwgRGV0YWlscy5Db2RlVmFsdWUgfCBEZXRhaWxzLlVybFZhbHVlXG4gICAgPlxuICB8IFByaW1pdGl2ZUl0ZW1WYWx1ZTtcblxuZXhwb3J0IGZ1bmN0aW9uIHRyaW1TbGljZShpdGVtPzogUHJpbWl0aXZlSXRlbVZhbHVlLCBtYXhMZW5ndGggPSAwKSB7XG4gIGNvbnN0IHN0ciA9IFN0cmluZyhpdGVtKS50cmltKCk7XG4gIHJldHVybiBtYXhMZW5ndGggPiAwID8gc3RyLnNsaWNlKDAsIG1heExlbmd0aCkgOiBzdHI7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBwYXJzZU5vZGVWYWx1ZShub2RlPzogRGV0YWlscy5Ob2RlVmFsdWUpOiBzdHJpbmcge1xuICBjb25zdCB7IHNlbGVjdG9yID0gJycgfSA9IG5vZGUgPz8ge307XG4gIHJldHVybiBzZWxlY3Rvcjtcbn1cblxuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG1heC1saW5lcy1wZXItZnVuY3Rpb25cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRUYWJsZUl0ZW1Qcm9wZXJ0eVZhbHVlKFxuICBpdGVtVmFsdWU/OiBEZXRhaWxzLkl0ZW1WYWx1ZSxcbiAgaXRlbVZhbHVlRm9ybWF0PzogRGV0YWlscy5JdGVtVmFsdWVUeXBlLFxuKSB7XG4gIC8vIG51bGxcbiAgaWYgKGl0ZW1WYWx1ZSA9PSBudWxsKSB7XG4gICAgcmV0dXJuICcnO1xuICB9XG5cbiAgLy8gUHJpbWl0aXZlIFZhbHVlc1xuICBpZiAoaXRlbVZhbHVlRm9ybWF0ID09IG51bGwpIHtcbiAgICBpZiAodHlwZW9mIGl0ZW1WYWx1ZSA9PT0gJ3N0cmluZycpIHtcbiAgICAgIHJldHVybiB0cmltU2xpY2UoaXRlbVZhbHVlKTtcbiAgICB9XG5cbiAgICBpZiAodHlwZW9mIGl0ZW1WYWx1ZSA9PT0gJ251bWJlcicpIHtcbiAgICAgIHJldHVybiBOdW1iZXIoaXRlbVZhbHVlKTtcbiAgICB9XG5cbiAgICBpZiAodHlwZW9mIGl0ZW1WYWx1ZSA9PT0gJ2Jvb2xlYW4nKSB7XG4gICAgICByZXR1cm4gaXRlbVZhbHVlO1xuICAgIH1cbiAgfVxuXG4gIGNvbnN0IHBhcnNlZEl0ZW1WYWx1ZSA9IHBhcnNlVGFibGVJdGVtUHJvcGVydHlWYWx1ZShpdGVtVmFsdWUpO1xuXG4gIC8qIGVzbGludC1kaXNhYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1tYWdpYy1udW1iZXJzICovXG4gIHN3aXRjaCAoaXRlbVZhbHVlRm9ybWF0KSB7XG4gICAgY2FzZSAnYnl0ZXMnOlxuICAgICAgcmV0dXJuIGZvcm1hdEJ5dGVzKE51bWJlcihwYXJzZWRJdGVtVmFsdWUpKTtcbiAgICBjYXNlICdjb2RlJzpcbiAgICAgIHJldHVybiBodG1sLmNvZGUodHJpbVNsaWNlKHBhcnNlZEl0ZW1WYWx1ZSBhcyBzdHJpbmcpKTtcbiAgICBjYXNlICdsaW5rJzpcbiAgICAgIGNvbnN0IGxpbmsgPSBwYXJzZWRJdGVtVmFsdWUgYXMgRGV0YWlscy5MaW5rVmFsdWU7XG4gICAgICByZXR1cm4gaHRtbC5saW5rKGxpbmsudXJsLCBsaW5rLnRleHQpO1xuICAgIGNhc2UgJ3VybCc6XG4gICAgICBjb25zdCB1cmwgPSBwYXJzZWRJdGVtVmFsdWUgYXMgc3RyaW5nO1xuICAgICAgcmV0dXJuIGh0bWwubGluayh1cmwpO1xuICAgIGNhc2UgJ3RpbWVzcGFuTXMnOlxuICAgIGNhc2UgJ21zJzpcbiAgICAgIHJldHVybiBmb3JtYXREdXJhdGlvbihOdW1iZXIocGFyc2VkSXRlbVZhbHVlKSk7XG4gICAgY2FzZSAnbm9kZSc6XG4gICAgICByZXR1cm4gcGFyc2VOb2RlVmFsdWUoaXRlbVZhbHVlIGFzIERldGFpbHMuTm9kZVZhbHVlKTtcbiAgICBjYXNlICdzb3VyY2UtbG9jYXRpb24nOlxuICAgICAgcmV0dXJuIHRydW5jYXRlVGV4dChTdHJpbmcocGFyc2VkSXRlbVZhbHVlKSwgMjAwKTtcbiAgICBjYXNlICdudW1lcmljJzpcbiAgICAgIGNvbnN0IG51bSA9IE51bWJlcihwYXJzZWRJdGVtVmFsdWUpO1xuICAgICAgaWYgKG51bS50b0ZpeGVkKDMpLnRvU3RyaW5nKCkuZW5kc1dpdGgoJy4wMDAnKSkge1xuICAgICAgICByZXR1cm4gU3RyaW5nKG51bSk7XG4gICAgICB9XG4gICAgICByZXR1cm4gU3RyaW5nKG51bS50b0ZpeGVkKDMpKTtcbiAgICBjYXNlICd0ZXh0JzpcbiAgICAgIHJldHVybiB0cnVuY2F0ZVRleHQoU3RyaW5nKHBhcnNlZEl0ZW1WYWx1ZSksIDUwMCk7XG4gICAgY2FzZSAnbXVsdGknOiAvLyBAVE9ET1xuICAgICAgLy8gQFRPRE8gbG9nIHZlcmJvc2UgZmlyc3QsIHRoZW4gaW1wbGVtZW50IGRhdGEgdHlwZVxuICAgICAgdWkoKS5sb2dnZXIuaW5mbyhgRm9ybWF0IHR5cGUgJHtib2xkKCdtdWx0aScpfSBpcyBub3QgaW1wbGVtZW50ZWRgKTtcbiAgICAgIHJldHVybiAnJztcbiAgICBjYXNlICd0aHVtYm5haWwnOiAvLyBAVE9ET1xuICAgICAgLy8gQFRPRE8gbG9nIHZlcmJvc2UgZmlyc3QsIHRoZW4gaW1wbGVtZW50IGRhdGEgdHlwZVxuICAgICAgdWkoKS5sb2dnZXIuaW5mbyhgRm9ybWF0IHR5cGUgJHtib2xkKCd0aHVtYm5haWwnKX0gaXMgbm90IGltcGxlbWVudGVkYCk7XG4gICAgICByZXR1cm4gJyc7XG4gIH1cbiAgLyogZXNsaW50LWVuYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tbWFnaWMtbnVtYmVycyAqL1xuXG4gIHJldHVybiBpdGVtVmFsdWU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBwYXJzZVNpbXBsZUl0ZW1WYWx1ZShcbiAgaXRlbTogU2ltcGxlSXRlbVZhbHVlLFxuKTogUHJpbWl0aXZlSXRlbVZhbHVlIHtcbiAgaWYgKHR5cGVvZiBpdGVtID09PSAnb2JqZWN0Jykge1xuICAgIGNvbnN0IHZhbHVlID0gaXRlbS52YWx1ZTtcbiAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnb2JqZWN0Jykge1xuICAgICAgcmV0dXJuIHZhbHVlLmZvcm1hdHRlZERlZmF1bHQ7XG4gICAgfVxuICAgIHJldHVybiB2YWx1ZTtcbiAgfVxuICByZXR1cm4gaXRlbTtcbn1cblxuLy8gQFRPRE8gZXh0cmFjdCBMaW5rIHR5cGUgZnJvbSBsb2dpY1xuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlVGFibGVJdGVtUHJvcGVydHlWYWx1ZShcbiAgaXRlbVZhbHVlPzogRGV0YWlscy5JdGVtVmFsdWUsXG4pOiBQcmltaXRpdmVJdGVtVmFsdWUgfCBEZXRhaWxzLkxpbmtWYWx1ZSB7XG4gIGlmIChpdGVtVmFsdWUgPT0gbnVsbCkge1xuICAgIHJldHVybiAnJztcbiAgfVxuXG4gIC8vIFByaW1pdGl2ZSBWYWx1ZXNcbiAgaWYgKFxuICAgIHR5cGVvZiBpdGVtVmFsdWUgPT09ICdzdHJpbmcnIHx8XG4gICAgdHlwZW9mIGl0ZW1WYWx1ZSA9PT0gJ251bWJlcicgfHxcbiAgICB0eXBlb2YgaXRlbVZhbHVlID09PSAnYm9vbGVhbidcbiAgKSB7XG4gICAgcmV0dXJuIHBhcnNlU2ltcGxlSXRlbVZhbHVlKGl0ZW1WYWx1ZSk7XG4gIH1cblxuICAvLyBPYmplY3QgVmFsdWVzXG4gIGNvbnN0IG9iamVjdFZhbHVlID0gaXRlbVZhbHVlIGFzIE9iamVjdEl0ZW1WYWx1ZTtcbiAgY29uc3QgeyB0eXBlIH0gPSBvYmplY3RWYWx1ZTtcbiAgc3dpdGNoICh0eXBlKSB7XG4gICAgY2FzZSAnY29kZSc6XG4gICAgY2FzZSAndXJsJzpcbiAgICAgIHJldHVybiBTdHJpbmcocGFyc2VTaW1wbGVJdGVtVmFsdWUob2JqZWN0VmFsdWUpKTtcbiAgICBjYXNlICdub2RlJzpcbiAgICAgIHJldHVybiBwYXJzZU5vZGVWYWx1ZShvYmplY3RWYWx1ZSk7XG4gICAgY2FzZSAnbGluayc6XG4gICAgICByZXR1cm4gb2JqZWN0VmFsdWU7XG4gICAgY2FzZSAnbnVtZXJpYyc6XG4gICAgICByZXR1cm4gTnVtYmVyKHBhcnNlU2ltcGxlSXRlbVZhbHVlKG9iamVjdFZhbHVlKSk7XG4gICAgY2FzZSAnc291cmNlLWxvY2F0aW9uJzpcbiAgICAgIGNvbnN0IHsgdXJsIH0gPSBvYmplY3RWYWx1ZTtcbiAgICAgIHJldHVybiBTdHJpbmcodXJsKTtcbiAgICBjYXNlICdzdWJpdGVtcyc6XG4gICAgICAvLyBAVE9ETyBsb2cgdmVyYm9zZSBmaXJzdCwgdGhlbiBpbXBsZW1lbnQgZGF0YSB0eXBlXG4gICAgICB1aSgpLmxvZ2dlci5pbmZvKGBWYWx1ZSB0eXBlICR7Ym9sZCgnc3ViaXRlbXMnKX0gaXMgbm90IGltcGxlbWVudGVkYCk7XG4gICAgICByZXR1cm4gJyc7XG4gICAgY2FzZSAnZGVidWdkYXRhJzpcbiAgICAgIC8vIEBUT0RPIGxvZyB2ZXJib3NlIGZpcnN0LCB0aGVuIGltcGxlbWVudCBkYXRhIHR5cGVcbiAgICAgIHVpKCkubG9nZ2VyLmluZm8oYFZhbHVlIHR5cGUgJHtib2xkKCdkZWJ1Z2RhdGEnKX0gaXMgbm90IGltcGxlbWVudGVkYCwge1xuICAgICAgICBzaWxlbnQ6IHRydWUsXG4gICAgICB9KTtcbiAgICAgIHJldHVybiAnJztcbiAgfVxuICAvLyBJY3VNZXNzYWdlXG4gIHJldHVybiBwYXJzZVNpbXBsZUl0ZW1WYWx1ZShvYmplY3RWYWx1ZSBhcyBTaW1wbGVJdGVtVmFsdWUpO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9ydW5uZXIvZGV0YWlscy91dGlscy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lci9kZXRhaWxzXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL2RldGFpbHMvdXRpbHMudHNcIjtpbXBvcnQgeyBib2xkIH0gZnJvbSAnYW5zaXMnO1xuaW1wb3J0IHR5cGUgRGV0YWlscyBmcm9tICdsaWdodGhvdXNlL3R5cGVzL2xoci9hdWRpdC1kZXRhaWxzJztcblxuZXhwb3J0IGNsYXNzIExpZ2h0aG91c2VBdWRpdERldGFpbHNQYXJzaW5nRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHR5cGU6IERldGFpbHNbJ3R5cGUnXSxcbiAgICByYXdUYWJsZTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gICAgZXJyb3I6IHN0cmluZyxcbiAgKSB7XG4gICAgc3VwZXIoXG4gICAgICBgUGFyc2luZyBsaWdodGhvdXNlIHJlcG9ydCBkZXRhaWxzICR7Ym9sZChcbiAgICAgICAgdHlwZSxcbiAgICAgICl9IGZhaWxlZDogXFxuUmF3IGRhdGE6XFxuICR7SlNPTi5zdHJpbmdpZnkocmF3VGFibGUsIG51bGwsIDIpfVxcbiR7ZXJyb3J9YCxcbiAgICApO1xuICB9XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3V0aWxzLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi91dGlscy50c1wiO2ltcG9ydCB0eXBlIHsgQXVkaXQsIENhdGVnb3J5UmVmLCBHcm91cCB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgZmlsdGVySXRlbVJlZnNCeSwgdG9BcnJheSB9IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgeyBMSUdIVEhPVVNFX1BMVUdJTl9TTFVHIH0gZnJvbSAnLi9jb25zdGFudHMuanMnO1xuaW1wb3J0IHR5cGUgeyBMaWdodGhvdXNlQ2xpRmxhZ3MgfSBmcm9tICcuL3J1bm5lci90eXBlcy5qcyc7XG5cbmV4cG9ydCB0eXBlIExpZ2h0aG91c2VHcm91cFNsdWdzID1cbiAgfCAncGVyZm9ybWFuY2UnXG4gIHwgJ2FjY2Vzc2liaWxpdHknXG4gIHwgJ2Jlc3QtcHJhY3RpY2VzJ1xuICB8ICdzZW8nXG4gIHwgJ3B3YSc7XG5cbmV4cG9ydCBmdW5jdGlvbiBsaWdodGhvdXNlR3JvdXBSZWYoXG4gIGdyb3VwU2x1ZzogTGlnaHRob3VzZUdyb3VwU2x1Z3MsXG4gIHdlaWdodCA9IDEsXG4pOiBDYXRlZ29yeVJlZiB7XG4gIHJldHVybiB7XG4gICAgcGx1Z2luOiBMSUdIVEhPVVNFX1BMVUdJTl9TTFVHLFxuICAgIHNsdWc6IGdyb3VwU2x1ZyxcbiAgICB0eXBlOiAnZ3JvdXAnLFxuICAgIHdlaWdodCxcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGxpZ2h0aG91c2VBdWRpdFJlZihhdWRpdFNsdWc6IHN0cmluZywgd2VpZ2h0ID0gMSk6IENhdGVnb3J5UmVmIHtcbiAgcmV0dXJuIHtcbiAgICBwbHVnaW46IExJR0hUSE9VU0VfUExVR0lOX1NMVUcsXG4gICAgc2x1ZzogYXVkaXRTbHVnLFxuICAgIHR5cGU6ICdhdWRpdCcsXG4gICAgd2VpZ2h0LFxuICB9O1xufVxuXG5leHBvcnQgY2xhc3MgQXVkaXRzTm90SW1wbGVtZW50ZWRFcnJvciBleHRlbmRzIEVycm9yIHtcbiAgY29uc3RydWN0b3IoYXVkaXRTbHVnczogc3RyaW5nW10pIHtcbiAgICBzdXBlcihgYXVkaXRzOiBcIiR7YXVkaXRTbHVncy5qb2luKCcsICcpfVwiIG5vdCBpbXBsZW1lbnRlZGApO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB2YWxpZGF0ZUF1ZGl0cyhhdWRpdHM6IEF1ZGl0W10sIG9ubHlBdWRpdHM6IHN0cmluZ1tdKTogYm9vbGVhbiB7XG4gIGNvbnN0IG1pc3NpbmdBdWR0aXMgPSB0b0FycmF5KG9ubHlBdWRpdHMpLmZpbHRlcihcbiAgICBzbHVnID0+ICFhdWRpdHMuc29tZShhdWRpdCA9PiBhdWRpdC5zbHVnID09PSBzbHVnKSxcbiAgKTtcbiAgaWYgKG1pc3NpbmdBdWR0aXMubGVuZ3RoID4gMCkge1xuICAgIHRocm93IG5ldyBBdWRpdHNOb3RJbXBsZW1lbnRlZEVycm9yKG1pc3NpbmdBdWR0aXMpO1xuICB9XG4gIHJldHVybiB0cnVlO1xufVxuXG5leHBvcnQgY2xhc3MgQ2F0ZWdvcmllc05vdEltcGxlbWVudGVkRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIGNvbnN0cnVjdG9yKGNhdGVnb3J5U2x1Z3M6IHN0cmluZ1tdKSB7XG4gICAgc3VwZXIoYGNhdGVnb3JpZXM6IFwiJHtjYXRlZ29yeVNsdWdzLmpvaW4oJywgJyl9XCIgbm90IGltcGxlbWVudGVkYCk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlT25seUNhdGVnb3JpZXMoXG4gIGdyb3VwczogR3JvdXBbXSxcbiAgb25seUNhdGVnb3JpZXM6IHN0cmluZyB8IHN0cmluZ1tdLFxuKTogYm9vbGVhbiB7XG4gIGNvbnN0IG1pc3NpbmdDYXRlZ29yaWVzID0gdG9BcnJheShvbmx5Q2F0ZWdvcmllcykuZmlsdGVyKHNsdWcgPT5cbiAgICBncm91cHMuZXZlcnkoZ3JvdXAgPT4gZ3JvdXAuc2x1ZyAhPT0gc2x1ZyksXG4gICk7XG4gIGlmIChtaXNzaW5nQ2F0ZWdvcmllcy5sZW5ndGggPiAwKSB7XG4gICAgdGhyb3cgbmV3IENhdGVnb3JpZXNOb3RJbXBsZW1lbnRlZEVycm9yKG1pc3NpbmdDYXRlZ29yaWVzKTtcbiAgfVxuICByZXR1cm4gdHJ1ZTtcbn1cblxuZXhwb3J0IHR5cGUgRmlsdGVyT3B0aW9ucyA9IFBhcnRpYWw8XG4gIFBpY2s8TGlnaHRob3VzZUNsaUZsYWdzLCAnb25seUF1ZGl0cycgfCAnb25seUNhdGVnb3JpZXMnIHwgJ3NraXBBdWRpdHMnPlxuPjtcblxuZXhwb3J0IGZ1bmN0aW9uIGZpbHRlckF1ZGl0c0FuZEdyb3Vwc0J5T25seU9wdGlvbnMoXG4gIGF1ZGl0czogQXVkaXRbXSxcbiAgZ3JvdXBzOiBHcm91cFtdLFxuICBvcHRpb25zPzogRmlsdGVyT3B0aW9ucyxcbik6IHtcbiAgYXVkaXRzOiBBdWRpdFtdO1xuICBncm91cHM6IEdyb3VwW107XG59IHtcbiAgY29uc3Qge1xuICAgIG9ubHlBdWRpdHMgPSBbXSxcbiAgICBza2lwQXVkaXRzID0gW10sXG4gICAgb25seUNhdGVnb3JpZXMgPSBbXSxcbiAgfSA9IG9wdGlvbnMgPz8ge307XG5cbiAgLy8gY2F0ZWdvcnkgd2lucyBvdmVyIGF1ZGl0c1xuICBpZiAob25seUNhdGVnb3JpZXMubGVuZ3RoID4gMCkge1xuICAgIHZhbGlkYXRlT25seUNhdGVnb3JpZXMoZ3JvdXBzLCBvbmx5Q2F0ZWdvcmllcyk7XG5cbiAgICBjb25zdCBjYXRlZ29yeVNsdWdzID0gbmV3IFNldChvbmx5Q2F0ZWdvcmllcyk7XG4gICAgY29uc3QgZmlsdGVyZWRHcm91cHM6IEdyb3VwW10gPSBncm91cHMuZmlsdGVyKCh7IHNsdWcgfSkgPT5cbiAgICAgIGNhdGVnb3J5U2x1Z3MuaGFzKHNsdWcpLFxuICAgICk7XG4gICAgY29uc3QgYXVkaXRTbHVnc0Zyb21SZW1haW5pbmdHcm91cHMgPSBuZXcgU2V0KFxuICAgICAgZmlsdGVyZWRHcm91cHMuZmxhdE1hcCgoeyByZWZzIH0pID0+IHJlZnMubWFwKCh7IHNsdWcgfSkgPT4gc2x1ZykpLFxuICAgICk7XG4gICAgcmV0dXJuIHtcbiAgICAgIGF1ZGl0czogYXVkaXRzLmZpbHRlcigoeyBzbHVnIH0pID0+XG4gICAgICAgIGF1ZGl0U2x1Z3NGcm9tUmVtYWluaW5nR3JvdXBzLmhhcyhzbHVnKSxcbiAgICAgICksXG4gICAgICBncm91cHM6IGZpbHRlcmVkR3JvdXBzLFxuICAgIH07XG4gIH0gZWxzZSBpZiAob25seUF1ZGl0cy5sZW5ndGggPiAwIHx8IHNraXBBdWRpdHMubGVuZ3RoID4gMCkge1xuICAgIHZhbGlkYXRlQXVkaXRzKGF1ZGl0cywgb25seUF1ZGl0cyk7XG4gICAgdmFsaWRhdGVBdWRpdHMoYXVkaXRzLCBza2lwQXVkaXRzKTtcbiAgICBjb25zdCBvbmx5QXVkaXRTbHVncyA9IG5ldyBTZXQob25seUF1ZGl0cyk7XG4gICAgY29uc3Qgc2tpcEF1ZGl0U2x1Z3MgPSBuZXcgU2V0KHNraXBBdWRpdHMpO1xuICAgIGNvbnN0IGZpbHRlckF1ZGl0cyA9ICh7IHNsdWcgfTogUGljazxBdWRpdCwgJ3NsdWcnPikgPT5cbiAgICAgICEoXG4gICAgICAgIC8vIGF1ZGl0IGlzIE5PVCBpbiBnaXZlbiBvbmx5QXVkaXRTbHVnc1xuICAgICAgICAoXG4gICAgICAgICAgKG9ubHlBdWRpdHMubGVuZ3RoID4gMCAmJiAhb25seUF1ZGl0U2x1Z3MuaGFzKHNsdWcpKSB8fFxuICAgICAgICAgIC8vIGF1ZGl0IElTIGluIGdpdmVuIHNraXBBdWRpdFNsdWdzXG4gICAgICAgICAgKHNraXBBdWRpdHMubGVuZ3RoID4gMCAmJiBza2lwQXVkaXRTbHVncy5oYXMoc2x1ZykpXG4gICAgICAgIClcbiAgICAgICk7XG4gICAgcmV0dXJuIHtcbiAgICAgIGF1ZGl0czogYXVkaXRzLmZpbHRlcihmaWx0ZXJBdWRpdHMpLFxuICAgICAgZ3JvdXBzOiBmaWx0ZXJJdGVtUmVmc0J5KGdyb3VwcywgZmlsdGVyQXVkaXRzKSxcbiAgICB9O1xuICB9XG4gIC8vIHJldHVybiB1bmNoYW5nZWRcbiAgcmV0dXJuIHtcbiAgICBhdWRpdHMsXG4gICAgZ3JvdXBzLFxuICB9O1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvY29kZS1wdXNodXAucHJlc2V0LnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL2NvZGUtcHVzaHVwLnByZXNldC50c1wiO2ltcG9ydCB0eXBlIHtcbiAgQ2F0ZWdvcnlDb25maWcsXG4gIENvcmVDb25maWcsXG59IGZyb20gJy4vcGFja2FnZXMvbW9kZWxzL3NyYy9pbmRleC5qcyc7XG5pbXBvcnQgY292ZXJhZ2VQbHVnaW4sIHtcbiAgZ2V0TnhDb3ZlcmFnZVBhdGhzLFxufSBmcm9tICcuL3BhY2thZ2VzL3BsdWdpbi1jb3ZlcmFnZS9zcmMvaW5kZXguanMnO1xuaW1wb3J0IGRvY0NvdmVyYWdlUGx1Z2luLCB7IERvY0NvdmVyYWdlUGx1Z2luQ29uZmlnIH0gZnJvbSAnLi9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9pbmRleC5qcyc7XG5pbXBvcnQgeyBncm91cHMsIFBMVUdJTl9TTFVHIH0gZnJvbSAnLi9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWIvY29uc3RhbnRzLmpzJztcbmltcG9ydCB7IGZpbHRlckdyb3Vwc0J5T25seUF1ZGl0cyB9IGZyb20gJy4vcGFja2FnZXMvcGx1Z2luLWRvYy1jb3ZlcmFnZS9zcmMvbGliL3V0aWxzLmpzJztcbmltcG9ydCBlc2xpbnRQbHVnaW4sIHtcbiAgZXNsaW50Q29uZmlnRnJvbUFsbE54UHJvamVjdHMsXG4gIGVzbGludENvbmZpZ0Zyb21OeFByb2plY3QsXG59IGZyb20gJy4vcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvaW5kZXguanMnO1xuaW1wb3J0IGpzUGFja2FnZXNQbHVnaW4gZnJvbSAnLi9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2luZGV4LmpzJztcbmltcG9ydCBsaWdodGhvdXNlUGx1Z2luLCB7XG4gIGxpZ2h0aG91c2VHcm91cFJlZixcbn0gZnJvbSAnLi9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvaW5kZXguanMnO1xuXG5leHBvcnQgY29uc3QganNQYWNrYWdlc0NhdGVnb3JpZXM6IENhdGVnb3J5Q29uZmlnW10gPSBbXG4gIHtcbiAgICBzbHVnOiAnc2VjdXJpdHknLFxuICAgIHRpdGxlOiAnU2VjdXJpdHknLFxuICAgIGRlc2NyaXB0aW9uOiAnRmluZHMga25vd24gKip2dWxuZXJhYmlsaXRpZXMqKiBpbiAzcmQtcGFydHkgcGFja2FnZXMuJyxcbiAgICByZWZzOiBbXG4gICAgICB7XG4gICAgICAgIHR5cGU6ICdncm91cCcsXG4gICAgICAgIHBsdWdpbjogJ2pzLXBhY2thZ2VzJyxcbiAgICAgICAgc2x1ZzogJ25wbS1hdWRpdCcsXG4gICAgICAgIHdlaWdodDogMSxcbiAgICAgIH0sXG4gICAgXSxcbiAgfSxcbiAge1xuICAgIHNsdWc6ICd1cGRhdGVzJyxcbiAgICB0aXRsZTogJ1VwZGF0ZXMnLFxuICAgIGRlc2NyaXB0aW9uOiAnRmluZHMgKipvdXRkYXRlZCoqIDNyZC1wYXJ0eSBwYWNrYWdlcy4nLFxuICAgIHJlZnM6IFtcbiAgICAgIHtcbiAgICAgICAgdHlwZTogJ2dyb3VwJyxcbiAgICAgICAgcGx1Z2luOiAnanMtcGFja2FnZXMnLFxuICAgICAgICBzbHVnOiAnbnBtLW91dGRhdGVkJyxcbiAgICAgICAgd2VpZ2h0OiAxLFxuICAgICAgfSxcbiAgICBdLFxuICB9LFxuXTtcblxuZXhwb3J0IGNvbnN0IGxpZ2h0aG91c2VDYXRlZ29yaWVzOiBDYXRlZ29yeUNvbmZpZ1tdID0gW1xuICB7XG4gICAgc2x1ZzogJ3BlcmZvcm1hbmNlJyxcbiAgICB0aXRsZTogJ1BlcmZvcm1hbmNlJyxcbiAgICByZWZzOiBbbGlnaHRob3VzZUdyb3VwUmVmKCdwZXJmb3JtYW5jZScpXSxcbiAgfSxcbiAge1xuICAgIHNsdWc6ICdhMTF5JyxcbiAgICB0aXRsZTogJ0FjY2Vzc2liaWxpdHknLFxuICAgIHJlZnM6IFtsaWdodGhvdXNlR3JvdXBSZWYoJ2FjY2Vzc2liaWxpdHknKV0sXG4gIH0sXG4gIHtcbiAgICBzbHVnOiAnYmVzdC1wcmFjdGljZXMnLFxuICAgIHRpdGxlOiAnQmVzdCBQcmFjdGljZXMnLFxuICAgIHJlZnM6IFtsaWdodGhvdXNlR3JvdXBSZWYoJ2Jlc3QtcHJhY3RpY2VzJyldLFxuICB9LFxuICB7XG4gICAgc2x1ZzogJ3NlbycsXG4gICAgdGl0bGU6ICdTRU8nLFxuICAgIHJlZnM6IFtsaWdodGhvdXNlR3JvdXBSZWYoJ3NlbycpXSxcbiAgfSxcbl07XG5cbmV4cG9ydCBjb25zdCBlc2xpbnRDYXRlZ29yaWVzOiBDYXRlZ29yeUNvbmZpZ1tdID0gW1xuICB7XG4gICAgc2x1ZzogJ2J1Zy1wcmV2ZW50aW9uJyxcbiAgICB0aXRsZTogJ0J1ZyBwcmV2ZW50aW9uJyxcbiAgICBkZXNjcmlwdGlvbjogJ0xpbnQgcnVsZXMgdGhhdCBmaW5kICoqcG90ZW50aWFsIGJ1Z3MqKiBpbiB5b3VyIGNvZGUuJyxcbiAgICByZWZzOiBbeyB0eXBlOiAnZ3JvdXAnLCBwbHVnaW46ICdlc2xpbnQnLCBzbHVnOiAncHJvYmxlbXMnLCB3ZWlnaHQ6IDEgfV0sXG4gIH0sXG4gIHtcbiAgICBzbHVnOiAnY29kZS1zdHlsZScsXG4gICAgdGl0bGU6ICdDb2RlIHN0eWxlJyxcbiAgICBkZXNjcmlwdGlvbjpcbiAgICAgICdMaW50IHJ1bGVzIHRoYXQgcHJvbW90ZSAqKmdvb2QgcHJhY3RpY2VzKiogYW5kIGNvbnNpc3RlbmN5IGluIHlvdXIgY29kZS4nLFxuICAgIHJlZnM6IFt7IHR5cGU6ICdncm91cCcsIHBsdWdpbjogJ2VzbGludCcsIHNsdWc6ICdzdWdnZXN0aW9ucycsIHdlaWdodDogMSB9XSxcbiAgfSxcbl07XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXREb2NDb3ZlcmFnZUNhdGVnb3JpZXMoY29uZmlnOiBEb2NDb3ZlcmFnZVBsdWdpbkNvbmZpZyk6IENhdGVnb3J5Q29uZmlnW10ge1xuICByZXR1cm4gW3tcbiAgICBzbHVnOiAnZG9jLWNvdmVyYWdlLWNhdCcsXG4gICAgdGl0bGU6ICdEb2N1bWVudGF0aW9uIGNvdmVyYWdlJyxcbiAgICBkZXNjcmlwdGlvbjogJ01lYXN1cmVzIGhvdyBtdWNoIG9mIHlvdXIgY29kZSBpcyAqKmRvY3VtZW50ZWQqKi4nLFxuICAgIHJlZnM6IGZpbHRlckdyb3Vwc0J5T25seUF1ZGl0cyhncm91cHMsIGNvbmZpZykubWFwKGdyb3VwID0+ICh7XG4gICAgICB3ZWlnaHQ6IDEsXG4gICAgICB0eXBlOiAnZ3JvdXAnLFxuICAgICAgcGx1Z2luOiBQTFVHSU5fU0xVRyxcbiAgICAgIHNsdWc6IGdyb3VwLnNsdWcsXG4gICAgfSkpLFxuICB9XTtcbn1cblxuXG5cbmV4cG9ydCBjb25zdCBjb3ZlcmFnZUNhdGVnb3JpZXM6IENhdGVnb3J5Q29uZmlnW10gPSBbXG4gIHtcbiAgICBzbHVnOiAnY29kZS1jb3ZlcmFnZScsXG4gICAgdGl0bGU6ICdDb2RlIGNvdmVyYWdlJyxcbiAgICBkZXNjcmlwdGlvbjogJ01lYXN1cmVzIGhvdyBtdWNoIG9mIHlvdXIgY29kZSBpcyAqKmNvdmVyZWQgYnkgdGVzdHMqKi4nLFxuICAgIHJlZnM6IFtcbiAgICAgIHtcbiAgICAgICAgdHlwZTogJ2dyb3VwJyxcbiAgICAgICAgcGx1Z2luOiAnY292ZXJhZ2UnLFxuICAgICAgICBzbHVnOiAnY292ZXJhZ2UnLFxuICAgICAgICB3ZWlnaHQ6IDEsXG4gICAgICB9LFxuICAgIF0sXG4gIH0sXG5dO1xuXG5leHBvcnQgY29uc3QganNQYWNrYWdlc0NvcmVDb25maWcgPSBhc3luYyAoKTogUHJvbWlzZTxDb3JlQ29uZmlnPiA9PiB7XG4gIHJldHVybiB7XG4gICAgcGx1Z2luczogW2F3YWl0IGpzUGFja2FnZXNQbHVnaW4oKV0sXG4gICAgY2F0ZWdvcmllczoganNQYWNrYWdlc0NhdGVnb3JpZXMsXG4gIH07XG59O1xuXG5leHBvcnQgY29uc3QgbGlnaHRob3VzZUNvcmVDb25maWcgPSBhc3luYyAoXG4gIHVybDogc3RyaW5nLFxuKTogUHJvbWlzZTxDb3JlQ29uZmlnPiA9PiB7XG4gIHJldHVybiB7XG4gICAgcGx1Z2luczogW2F3YWl0IGxpZ2h0aG91c2VQbHVnaW4odXJsKV0sXG4gICAgY2F0ZWdvcmllczogbGlnaHRob3VzZUNhdGVnb3JpZXMsXG4gIH07XG59O1xuXG5leHBvcnQgY29uc3QgZG9jQ292ZXJhZ2VDb3JlQ29uZmlnID0gYXN5bmMgKGNvbmZpZzogRG9jQ292ZXJhZ2VQbHVnaW5Db25maWcpOiBQcm9taXNlPENvcmVDb25maWc+ID0+IHtcbiAgcmV0dXJuIHtcbiAgICBwbHVnaW5zOiBbYXdhaXQgZG9jQ292ZXJhZ2VQbHVnaW4oY29uZmlnKV0sXG4gICAgY2F0ZWdvcmllczogZ2V0RG9jQ292ZXJhZ2VDYXRlZ29yaWVzKGNvbmZpZyksXG4gIH07XG59O1xuXG5leHBvcnQgY29uc3QgZXNsaW50Q29yZUNvbmZpZ054ID0gYXN5bmMgKFxuICBwcm9qZWN0TmFtZT86IHN0cmluZyxcbik6IFByb21pc2U8Q29yZUNvbmZpZz4gPT4ge1xuICByZXR1cm4ge1xuICAgIHBsdWdpbnM6IFtcbiAgICAgIGF3YWl0IGVzbGludFBsdWdpbihcbiAgICAgICAgYXdhaXQgKHByb2plY3ROYW1lXG4gICAgICAgICAgPyBlc2xpbnRDb25maWdGcm9tTnhQcm9qZWN0KHByb2plY3ROYW1lKVxuICAgICAgICAgIDogZXNsaW50Q29uZmlnRnJvbUFsbE54UHJvamVjdHMoKSksXG4gICAgICApLFxuICAgIF0sXG4gICAgY2F0ZWdvcmllczogZXNsaW50Q2F0ZWdvcmllcyxcbiAgfTtcbn07XG5cbmV4cG9ydCBjb25zdCBjb3ZlcmFnZUNvcmVDb25maWdOeCA9IGFzeW5jIChcbiAgcHJvamVjdE5hbWU/OiBzdHJpbmcsXG4pOiBQcm9taXNlPENvcmVDb25maWc+ID0+IHtcbiAgaWYgKHByb2plY3ROYW1lKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdjb3ZlcmFnZUNvcmVDb25maWdOeCBmb3Igc2luZ2xlIHByb2plY3RzIG5vdCBpbXBsZW1lbnRlZCcpO1xuICB9XG4gIGNvbnN0IHRhcmdldE5hbWVzID0gWyd1bml0LXRlc3QnLCAnaW50ZWdyYXRpb24tdGVzdCddO1xuICBjb25zdCB0YXJnZXRBcmdzID0gW1xuICAgICctdCcsXG4gICAgJ3VuaXQtdGVzdCcsXG4gICAgJ2ludGVncmF0aW9uLXRlc3QnLFxuICAgICctLWNvdmVyYWdlLmVuYWJsZWQnLFxuICAgICctLXNraXBOeENhY2hlJyxcbiAgXTtcbiAgcmV0dXJuIHtcbiAgICBwbHVnaW5zOiBbXG4gICAgICBhd2FpdCBjb3ZlcmFnZVBsdWdpbih7XG4gICAgICAgIGNvdmVyYWdlVG9vbENvbW1hbmQ6IHtcbiAgICAgICAgICBjb21tYW5kOiAnbnB4JyxcbiAgICAgICAgICBhcmdzOiBbXG4gICAgICAgICAgICAnbngnLFxuICAgICAgICAgICAgcHJvamVjdE5hbWUgPyBgcnVuIC0tcHJvamVjdCAke3Byb2plY3ROYW1lfWAgOiAncnVuLW1hbnknLFxuICAgICAgICAgICAgLi4udGFyZ2V0QXJncyxcbiAgICAgICAgICBdLFxuICAgICAgICB9LFxuICAgICAgICByZXBvcnRzOiBhd2FpdCBnZXROeENvdmVyYWdlUGF0aHModGFyZ2V0TmFtZXMpLFxuICAgICAgfSksXG4gICAgXSxcbiAgICBjYXRlZ29yaWVzOiBjb3ZlcmFnZUNhdGVnb3JpZXMsXG4gIH07XG59O1xuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2luZGV4LnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyY1wiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvaW5kZXgudHNcIjtleHBvcnQgeyBleGlzdHMgfSBmcm9tICdAY29kZS1wdXNodXAvbW9kZWxzJztcbmV4cG9ydCB7IGNvbXBhcmVQYWlycywgbWF0Y2hBcnJheUl0ZW1zQnlLZXksIHR5cGUgRGlmZiB9IGZyb20gJy4vbGliL2RpZmYuanMnO1xuZXhwb3J0IHsgc3RyaW5naWZ5RXJyb3IgfSBmcm9tICcuL2xpYi9lcnJvcnMuanMnO1xuZXhwb3J0IHtcbiAgUHJvY2Vzc0Vycm9yLFxuICBleGVjdXRlUHJvY2VzcyxcbiAgdHlwZSBQcm9jZXNzQ29uZmlnLFxuICB0eXBlIFByb2Nlc3NPYnNlcnZlcixcbiAgdHlwZSBQcm9jZXNzUmVzdWx0LFxufSBmcm9tICcuL2xpYi9leGVjdXRlLXByb2Nlc3MuanMnO1xuZXhwb3J0IHtcbiAgY3Jhd2xGaWxlU3lzdGVtLFxuICBkaXJlY3RvcnlFeGlzdHMsXG4gIGVuc3VyZURpcmVjdG9yeUV4aXN0cyxcbiAgZmlsZUV4aXN0cyxcbiAgZmlsZVBhdGhUb0NsaUFyZyxcbiAgZmluZExpbmVOdW1iZXJJblRleHQsXG4gIGZpbmROZWFyZXN0RmlsZSxcbiAgaW1wb3J0TW9kdWxlLFxuICBsb2dNdWx0aXBsZUZpbGVSZXN1bHRzLFxuICBwbHVnaW5Xb3JrRGlyLFxuICBwcm9qZWN0VG9GaWxlbmFtZSxcbiAgcmVhZEpzb25GaWxlLFxuICByZWFkVGV4dEZpbGUsXG4gIHJlbW92ZURpcmVjdG9yeUlmRXhpc3RzLFxuICB0eXBlIENyYXdsRmlsZVN5c3RlbU9wdGlvbnMsXG4gIHR5cGUgRmlsZVJlc3VsdCxcbiAgdHlwZSBNdWx0aXBsZUZpbGVSZXN1bHRzLFxufSBmcm9tICcuL2xpYi9maWxlLXN5c3RlbS5qcyc7XG5leHBvcnQgeyBmaWx0ZXJJdGVtUmVmc0J5IH0gZnJvbSAnLi9saWIvZmlsdGVyLmpzJztcbmV4cG9ydCB7XG4gIGZvcm1hdEJ5dGVzLFxuICBmb3JtYXREdXJhdGlvbixcbiAgcGx1cmFsaXplLFxuICBwbHVyYWxpemVUb2tlbixcbiAgc2x1Z2lmeSxcbiAgdHJ1bmNhdGVEZXNjcmlwdGlvbixcbiAgdHJ1bmNhdGVJc3N1ZU1lc3NhZ2UsXG4gIHRydW5jYXRlVGV4dCxcbiAgdHJ1bmNhdGVUaXRsZSxcbn0gZnJvbSAnLi9saWIvZm9ybWF0dGluZy5qcyc7XG5leHBvcnQge1xuICBnZXRDdXJyZW50QnJhbmNoT3JUYWcsXG4gIGdldEhhc2hGcm9tVGFnLFxuICBnZXRIYXNoZXMsXG4gIGdldExhdGVzdENvbW1pdCxcbiAgZ2V0U2VtdmVyVGFncyxcbiAgdHlwZSBMb2dSZXN1bHQsXG59IGZyb20gJy4vbGliL2dpdC9naXQuY29tbWl0cy1hbmQtdGFncy5qcyc7XG5leHBvcnQge1xuICBmb3JtYXRHaXRQYXRoLFxuICBnZXRHaXRSb290LFxuICBndWFyZEFnYWluc3RMb2NhbENoYW5nZXMsXG4gIHNhZmVDaGVja291dCxcbiAgdG9HaXRQYXRoLFxufSBmcm9tICcuL2xpYi9naXQvZ2l0LmpzJztcbmV4cG9ydCB7IGdyb3VwQnlTdGF0dXMgfSBmcm9tICcuL2xpYi9ncm91cC1ieS1zdGF0dXMuanMnO1xuZXhwb3J0IHtcbiAgaXNQcm9taXNlRnVsZmlsbGVkUmVzdWx0LFxuICBpc1Byb21pc2VSZWplY3RlZFJlc3VsdCxcbiAgaGFzTm9OdWxsYWJsZVByb3BzLFxufSBmcm9tICcuL2xpYi9ndWFyZHMuanMnO1xuZXhwb3J0IHsgbG9nTXVsdGlwbGVSZXN1bHRzIH0gZnJvbSAnLi9saWIvbG9nLXJlc3VsdHMuanMnO1xuZXhwb3J0IHsgbGluaywgdWksIHR5cGUgQ2xpVWksIHR5cGUgQ29sdW1uIH0gZnJvbSAnLi9saWIvbG9nZ2luZy5qcyc7XG5leHBvcnQgeyBtZXJnZUNvbmZpZ3MgfSBmcm9tICcuL2xpYi9tZXJnZS1jb25maWdzLmpzJztcbmV4cG9ydCB7IGdldFByb2dyZXNzQmFyLCB0eXBlIFByb2dyZXNzQmFyIH0gZnJvbSAnLi9saWIvcHJvZ3Jlc3MuanMnO1xuZXhwb3J0IHtcbiAgQ09ERV9QVVNIVVBfRE9NQUlOLFxuICBDT0RFX1BVU0hVUF9VTklDT0RFX0xPR08sXG4gIEZPT1RFUl9QUkVGSVgsXG4gIFJFQURNRV9MSU5LLFxuICBURVJNSU5BTF9XSURUSCxcbn0gZnJvbSAnLi9saWIvcmVwb3J0cy9jb25zdGFudHMuanMnO1xuZXhwb3J0IHtcbiAgbGlzdEF1ZGl0c0Zyb21BbGxQbHVnaW5zLFxuICBsaXN0R3JvdXBzRnJvbUFsbFBsdWdpbnMsXG59IGZyb20gJy4vbGliL3JlcG9ydHMvZmxhdHRlbi1wbHVnaW5zLmpzJztcbmV4cG9ydCB7IGdlbmVyYXRlTWRSZXBvcnQgfSBmcm9tICcuL2xpYi9yZXBvcnRzL2dlbmVyYXRlLW1kLXJlcG9ydC5qcyc7XG5leHBvcnQge1xuICBnZW5lcmF0ZU1kUmVwb3J0c0RpZmYsXG4gIGdlbmVyYXRlTWRSZXBvcnRzRGlmZkZvck1vbm9yZXBvLFxufSBmcm9tICcuL2xpYi9yZXBvcnRzL2dlbmVyYXRlLW1kLXJlcG9ydHMtZGlmZi5qcyc7XG5leHBvcnQgeyBsb2FkUmVwb3J0IH0gZnJvbSAnLi9saWIvcmVwb3J0cy9sb2FkLXJlcG9ydC5qcyc7XG5leHBvcnQgeyBsb2dTdGRvdXRTdW1tYXJ5IH0gZnJvbSAnLi9saWIvcmVwb3J0cy9sb2ctc3Rkb3V0LXN1bW1hcnkuanMnO1xuZXhwb3J0IHsgc2NvcmVSZXBvcnQgfSBmcm9tICcuL2xpYi9yZXBvcnRzL3Njb3JpbmcuanMnO1xuZXhwb3J0IHsgc29ydFJlcG9ydCB9IGZyb20gJy4vbGliL3JlcG9ydHMvc29ydGluZy5qcyc7XG5leHBvcnQgdHlwZSB7XG4gIFNjb3JlZENhdGVnb3J5Q29uZmlnLFxuICBTY29yZWRHcm91cCxcbiAgU2NvcmVkUmVwb3J0LFxufSBmcm9tICcuL2xpYi9yZXBvcnRzL3R5cGVzLmpzJztcbmV4cG9ydCB7XG4gIGNhbGNEdXJhdGlvbixcbiAgY29tcGFyZUlzc3VlU2V2ZXJpdHksXG4gIGZvcm1hdFJlcG9ydFNjb3JlLFxufSBmcm9tICcuL2xpYi9yZXBvcnRzL3V0aWxzLmpzJztcbmV4cG9ydCB7IGlzU2VtdmVyLCBub3JtYWxpemVTZW12ZXIsIHNvcnRTZW12ZXJzIH0gZnJvbSAnLi9saWIvc2VtdmVyLmpzJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL3RleHQtZm9ybWF0cy9pbmRleC5qcyc7XG5leHBvcnQge1xuICBjYXBpdGFsaXplLFxuICBjb3VudE9jY3VycmVuY2VzLFxuICBkaXN0aW5jdCxcbiAgZmFjdG9yT2YsXG4gIGZyb21Kc29uTGluZXMsXG4gIG9iamVjdEZyb21FbnRyaWVzLFxuICBvYmplY3RUb0NsaUFyZ3MsXG4gIG9iamVjdFRvRW50cmllcyxcbiAgb2JqZWN0VG9LZXlzLFxuICB0b0FycmF5LFxuICB0b0pzb25MaW5lcyxcbiAgdG9OdW1iZXJQcmVjaXNpb24sXG4gIHRvT3JkaW5hbCxcbiAgdG9Vbml4TmV3bGluZXMsXG4gIHRvVW5peFBhdGgsXG4gIHR5cGUgQ2xpQXJnc09iamVjdCxcbn0gZnJvbSAnLi9saWIvdHJhbnNmb3JtLmpzJztcbmV4cG9ydCB0eXBlIHtcbiAgRXhjbHVkZU51bGxhYmxlUHJvcHMsXG4gIEV4dHJhY3RBcnJheSxcbiAgRXh0cmFjdEFycmF5cyxcbiAgSXRlbU9yQXJyYXksXG4gIFByZXR0aWZ5LFxuICBXaXRoUmVxdWlyZWQsXG59IGZyb20gJy4vbGliL3R5cGVzLmpzJztcbmV4cG9ydCB7IHZlcmJvc2VVdGlscyB9IGZyb20gJy4vbGliL3ZlcmJvc2UtdXRpbHMuanMnO1xuZXhwb3J0IHsgem9kRXJyb3JNZXNzYWdlQnVpbGRlciB9IGZyb20gJy4vbGliL3pvZC12YWxpZGF0aW9uLmpzJztcbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvZXhlY3V0ZS1wcm9jZXNzLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9leGVjdXRlLXByb2Nlc3MudHNcIjtpbXBvcnQge1xuICB0eXBlIENoaWxkUHJvY2VzcyxcbiAgdHlwZSBDaGlsZFByb2Nlc3NCeVN0ZGlvLFxuICB0eXBlIFNwYXduT3B0aW9uc1dpdGhTdGRpb1R1cGxlLFxuICB0eXBlIFN0ZGlvUGlwZSxcbiAgc3Bhd24sXG59IGZyb20gJ25vZGU6Y2hpbGRfcHJvY2Vzcyc7XG5pbXBvcnQgdHlwZSB7IFJlYWRhYmxlLCBXcml0YWJsZSB9IGZyb20gJ25vZGU6c3RyZWFtJztcbmltcG9ydCB7IGNhbGNEdXJhdGlvbiB9IGZyb20gJy4vcmVwb3J0cy91dGlscy5qcyc7XG5cbi8qKlxuICogUmVwcmVzZW50cyB0aGUgcHJvY2VzcyByZXN1bHQuXG4gKiBAY2F0ZWdvcnkgVHlwZXNcbiAqIEBwdWJsaWNcbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBzdGRvdXQgLSBUaGUgc3Rkb3V0IG9mIHRoZSBwcm9jZXNzLlxuICogQHByb3BlcnR5IHtzdHJpbmd9IHN0ZGVyciAtIFRoZSBzdGRlcnIgb2YgdGhlIHByb2Nlc3MuXG4gKiBAcHJvcGVydHkge251bWJlciB8IG51bGx9IGNvZGUgLSBUaGUgZXhpdCBjb2RlIG9mIHRoZSBwcm9jZXNzLlxuICovXG5leHBvcnQgdHlwZSBQcm9jZXNzUmVzdWx0ID0ge1xuICBzdGRvdXQ6IHN0cmluZztcbiAgc3RkZXJyOiBzdHJpbmc7XG4gIGNvZGU6IG51bWJlciB8IG51bGw7XG4gIGRhdGU6IHN0cmluZztcbiAgZHVyYXRpb246IG51bWJlcjtcbn07XG5cbi8qKlxuICogRXJyb3IgY2xhc3MgZm9yIHByb2Nlc3MgZXJyb3JzLlxuICogQ29udGFpbnMgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgcHJvY2VzcyByZXN1bHQuXG4gKiBAY2F0ZWdvcnkgRXJyb3JcbiAqIEBwdWJsaWNcbiAqIEBjbGFzc1xuICogQGV4dGVuZHMgRXJyb3JcbiAqIEBleGFtcGxlXG4gKiBjb25zdCByZXN1bHQgPSBhd2FpdCBleGVjdXRlUHJvY2Vzcyh7fSlcbiAqIC5jYXRjaCgoZXJyb3IpID0+IHtcbiAqICAgaWYgKGVycm9yIGluc3RhbmNlb2YgUHJvY2Vzc0Vycm9yKSB7XG4gKiAgIGNvbnNvbGUuZXJyb3IoZXJyb3IuY29kZSk7XG4gKiAgIGNvbnNvbGUuZXJyb3IoZXJyb3Iuc3RkZXJyKTtcbiAqICAgY29uc29sZS5lcnJvcihlcnJvci5zdGRvdXQpO1xuICogICB9XG4gKiB9KTtcbiAqXG4gKi9cbmV4cG9ydCBjbGFzcyBQcm9jZXNzRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIGNvZGU6IG51bWJlciB8IG51bGw7XG4gIHN0ZGVycjogc3RyaW5nO1xuICBzdGRvdXQ6IHN0cmluZztcblxuICBjb25zdHJ1Y3RvcihyZXN1bHQ6IFByb2Nlc3NSZXN1bHQpIHtcbiAgICBzdXBlcihyZXN1bHQuc3RkZXJyKTtcbiAgICB0aGlzLmNvZGUgPSByZXN1bHQuY29kZTtcbiAgICB0aGlzLnN0ZGVyciA9IHJlc3VsdC5zdGRlcnI7XG4gICAgdGhpcy5zdGRvdXQgPSByZXN1bHQuc3Rkb3V0O1xuICB9XG59XG5cbi8qKlxuICogUHJvY2VzcyBjb25maWcgb2JqZWN0LiBDb250YWlucyB0aGUgY29tbWFuZCwgYXJncyBhbmQgb2JzZXJ2ZXIuXG4gKiBAcGFyYW0gY2ZnIC0gcHJvY2VzcyBjb25maWcgb2JqZWN0IHdpdGggY29tbWFuZCwgYXJncyBhbmQgb2JzZXJ2ZXIgKG9wdGlvbmFsKVxuICogQGNhdGVnb3J5IFR5cGVzXG4gKiBAcHVibGljXG4gKiBAcHJvcGVydHkge3N0cmluZ30gY29tbWFuZCAtIFRoZSBjb21tYW5kIHRvIGV4ZWN1dGUuXG4gKiBAcHJvcGVydHkge3N0cmluZ1tdfSBhcmdzIC0gVGhlIGFyZ3VtZW50cyBmb3IgdGhlIGNvbW1hbmQuXG4gKiBAcHJvcGVydHkge1Byb2Nlc3NPYnNlcnZlcn0gb2JzZXJ2ZXIgLSBUaGUgb2JzZXJ2ZXIgZm9yIHRoZSBwcm9jZXNzLlxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogLy8gYmFzaCBjb21tYW5kXG4gKiBjb25zdCBjZmcgPSB7XG4gKiAgIGNvbW1hbmQ6ICdiYXNoJyxcbiAqICAgYXJnczogWyctYycsICdlY2hvIFwiaGVsbG8gd29ybGRcIiddXG4gKiB9O1xuICpcbiAqIC8vIG5vZGUgY29tbWFuZFxuICogY29uc3QgY2ZnID0ge1xuICogY29tbWFuZDogJ25vZGUnLFxuICogYXJnczogWyctLXZlcnNpb24nXVxuICogfTtcbiAqXG4gKiAvLyBucHggY29tbWFuZFxuICogY29uc3QgY2ZnID0ge1xuICogY29tbWFuZDogJ25weCcsXG4gKiBhcmdzOiBbJy0tdmVyc2lvbiddXG4gKlxuICovXG5leHBvcnQgdHlwZSBQcm9jZXNzQ29uZmlnID0gT21pdDxcbiAgU3Bhd25PcHRpb25zV2l0aFN0ZGlvVHVwbGU8U3RkaW9QaXBlLCBTdGRpb1BpcGUsIFN0ZGlvUGlwZT4sXG4gICdzdGRpbydcbj4gJiB7XG4gIGNvbW1hbmQ6IHN0cmluZztcbiAgYXJncz86IHN0cmluZ1tdO1xuICBvYnNlcnZlcj86IFByb2Nlc3NPYnNlcnZlcjtcbiAgaWdub3JlRXhpdENvZGU/OiBib29sZWFuO1xufTtcblxuLyoqXG4gKiBQcm9jZXNzIG9ic2VydmVyIG9iamVjdC4gQ29udGFpbnMgdGhlIG9uU3Rkb3V0LCBlcnJvciBhbmQgY29tcGxldGUgZnVuY3Rpb24uXG4gKiBAY2F0ZWdvcnkgVHlwZXNcbiAqIEBwdWJsaWNcbiAqIEBwcm9wZXJ0eSB7ZnVuY3Rpb259IG9uU3Rkb3V0IC0gVGhlIG9uU3Rkb3V0IGZ1bmN0aW9uIG9mIHRoZSBvYnNlcnZlciAob3B0aW9uYWwpLlxuICogQHByb3BlcnR5IHtmdW5jdGlvbn0gb25FcnJvciAtIFRoZSBlcnJvciBmdW5jdGlvbiBvZiB0aGUgb2JzZXJ2ZXIgKG9wdGlvbmFsKS5cbiAqIEBwcm9wZXJ0eSB7ZnVuY3Rpb259IG9uQ29tcGxldGUgLSBUaGUgY29tcGxldGUgZnVuY3Rpb24gb2YgdGhlIG9ic2VydmVyIChvcHRpb25hbCkuXG4gKlxuICogQGV4YW1wbGVcbiAqIGNvbnN0IG9ic2VydmVyID0ge1xuICogIG9uU3Rkb3V0OiAoc3Rkb3V0KSA9PiBjb25zb2xlLmluZm8oc3Rkb3V0KVxuICogIH1cbiAqL1xuZXhwb3J0IHR5cGUgUHJvY2Vzc09ic2VydmVyID0ge1xuICBvblN0ZG91dD86IChzdGRvdXQ6IHN0cmluZywgc291cmNlUHJvY2Vzcz86IENoaWxkUHJvY2VzcykgPT4gdm9pZDtcbiAgb25TdGRlcnI/OiAoc3RkZXJyOiBzdHJpbmcsIHNvdXJjZVByb2Nlc3M/OiBDaGlsZFByb2Nlc3MpID0+IHZvaWQ7XG4gIG9uRXJyb3I/OiAoZXJyb3I6IFByb2Nlc3NFcnJvcikgPT4gdm9pZDtcbiAgb25Db21wbGV0ZT86ICgpID0+IHZvaWQ7XG59O1xuXG4vKipcbiAqIEV4ZWN1dGVzIGEgcHJvY2VzcyBhbmQgcmV0dXJucyBhIHByb21pc2Ugd2l0aCB0aGUgcmVzdWx0IGFzIGBQcm9jZXNzUmVzdWx0YC5cbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqIC8vIHN5bmMgcHJvY2VzcyBleGVjdXRpb25cbiAqIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGV4ZWN1dGVQcm9jZXNzKHtcbiAqICBjb21tYW5kOiAnbm9kZScsXG4gKiAgYXJnczogWyctLXZlcnNpb24nXVxuICogfSk7XG4gKlxuICogY29uc29sZS5pbmZvKHJlc3VsdCk7XG4gKlxuICogLy8gYXN5bmMgcHJvY2VzcyBleGVjdXRpb25cbiAqIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGV4ZWN1dGVQcm9jZXNzKHtcbiAqICAgIGNvbW1hbmQ6ICdub2RlJyxcbiAqICAgIGFyZ3M6IFsnZG93bmxvYWQtZGF0YS5qcyddLFxuICogICAgb2JzZXJ2ZXI6IHtcbiAqICAgICAgb25TdGRvdXQ6IHVwZGF0ZVByb2dyZXNzLFxuICogICAgICBlcnJvcjogaGFuZGxlRXJyb3IsXG4gKiAgICAgIGNvbXBsZXRlOiBjbGVhbkxvZ3MsXG4gKiAgICB9XG4gKiB9KTtcbiAqXG4gKiBjb25zb2xlLmluZm8ocmVzdWx0KTtcbiAqXG4gKiBAcGFyYW0gY2ZnIC0gc2VlIHtAbGluayBQcm9jZXNzQ29uZmlnfVxuICovXG5leHBvcnQgZnVuY3Rpb24gZXhlY3V0ZVByb2Nlc3MoY2ZnOiBQcm9jZXNzQ29uZmlnKTogUHJvbWlzZTxQcm9jZXNzUmVzdWx0PiB7XG4gIGNvbnN0IHsgY29tbWFuZCwgYXJncywgb2JzZXJ2ZXIsIGlnbm9yZUV4aXRDb2RlID0gZmFsc2UsIC4uLm9wdGlvbnMgfSA9IGNmZztcbiAgY29uc3QgeyBvblN0ZG91dCwgb25TdGRlcnIsIG9uRXJyb3IsIG9uQ29tcGxldGUgfSA9IG9ic2VydmVyID8/IHt9O1xuICBjb25zdCBkYXRlID0gbmV3IERhdGUoKS50b0lTT1N0cmluZygpO1xuICBjb25zdCBzdGFydCA9IHBlcmZvcm1hbmNlLm5vdygpO1xuXG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgLy8gc2hlbGw6dHJ1ZSB0ZWxscyBXaW5kb3dzIHRvIHVzZSBzaGVsbCBjb21tYW5kIGZvciBzcGF3bmluZyBhIGNoaWxkIHByb2Nlc3NcbiAgICBjb25zdCBzcGF3bmVkUHJvY2VzcyA9IHNwYXduKGNvbW1hbmQsIGFyZ3MgPz8gW10sIHtcbiAgICAgIHNoZWxsOiB0cnVlLFxuICAgICAgd2luZG93c0hpZGU6IHRydWUsXG4gICAgICAuLi5vcHRpb25zLFxuICAgIH0pIGFzIENoaWxkUHJvY2Vzc0J5U3RkaW88V3JpdGFibGUsIFJlYWRhYmxlLCBSZWFkYWJsZT47XG5cbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZnVuY3Rpb25hbC9uby1sZXRcbiAgICBsZXQgc3Rkb3V0ID0gJyc7XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGZ1bmN0aW9uYWwvbm8tbGV0XG4gICAgbGV0IHN0ZGVyciA9ICcnO1xuXG4gICAgc3Bhd25lZFByb2Nlc3Muc3Rkb3V0Lm9uKCdkYXRhJywgZGF0YSA9PiB7XG4gICAgICBzdGRvdXQgKz0gU3RyaW5nKGRhdGEpO1xuICAgICAgb25TdGRvdXQ/LihTdHJpbmcoZGF0YSksIHNwYXduZWRQcm9jZXNzKTtcbiAgICB9KTtcblxuICAgIHNwYXduZWRQcm9jZXNzLnN0ZGVyci5vbignZGF0YScsIGRhdGEgPT4ge1xuICAgICAgc3RkZXJyICs9IFN0cmluZyhkYXRhKTtcbiAgICAgIG9uU3RkZXJyPy4oU3RyaW5nKGRhdGEpLCBzcGF3bmVkUHJvY2Vzcyk7XG4gICAgfSk7XG5cbiAgICBzcGF3bmVkUHJvY2Vzcy5vbignZXJyb3InLCBlcnIgPT4ge1xuICAgICAgc3RkZXJyICs9IGVyci50b1N0cmluZygpO1xuICAgIH0pO1xuXG4gICAgc3Bhd25lZFByb2Nlc3Mub24oJ2Nsb3NlJywgY29kZSA9PiB7XG4gICAgICBjb25zdCB0aW1pbmdzID0geyBkYXRlLCBkdXJhdGlvbjogY2FsY0R1cmF0aW9uKHN0YXJ0KSB9O1xuICAgICAgaWYgKGNvZGUgPT09IDAgfHwgaWdub3JlRXhpdENvZGUpIHtcbiAgICAgICAgb25Db21wbGV0ZT8uKCk7XG4gICAgICAgIHJlc29sdmUoeyBjb2RlLCBzdGRvdXQsIHN0ZGVyciwgLi4udGltaW5ncyB9KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnN0IGVycm9yTXNnID0gbmV3IFByb2Nlc3NFcnJvcih7IGNvZGUsIHN0ZG91dCwgc3RkZXJyLCAuLi50aW1pbmdzIH0pO1xuICAgICAgICBvbkVycm9yPy4oZXJyb3JNc2cpO1xuICAgICAgICByZWplY3QoZXJyb3JNc2cpO1xuICAgICAgfVxuICAgIH0pO1xuICB9KTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0cy91dGlscy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3JlcG9ydHNcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9yZXBvcnRzL3V0aWxzLnRzXCI7aW1wb3J0IGFuc2lzLCB7IHR5cGUgQW5zaXMgfSBmcm9tICdhbnNpcyc7XG5pbXBvcnQgeyB0eXBlIElubGluZVRleHQsIG1kIH0gZnJvbSAnYnVpbGQtbWQnO1xuaW1wb3J0IHR5cGUge1xuICBBdWRpdERpZmYsXG4gIEF1ZGl0UmVwb3J0LFxuICBDYXRlZ29yeVJlZixcbiAgSXNzdWVTZXZlcml0eSBhcyBDbGlJc3N1ZVNldmVyaXR5LFxuICBHcm91cCxcbiAgSXNzdWUsXG59IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgU0NPUkVfQ09MT1JfUkFOR0UgfSBmcm9tICcuL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgdHlwZSB7XG4gIFNjb3JlZFJlcG9ydCxcbiAgU29ydGFibGVBdWRpdFJlcG9ydCxcbiAgU29ydGFibGVHcm91cCxcbn0gZnJvbSAnLi90eXBlcy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRSZXBvcnRTY29yZShzY29yZTogbnVtYmVyKTogc3RyaW5nIHtcbiAgY29uc3Qgc2NhbGVkU2NvcmUgPSBzY29yZSAqIDEwMDtcbiAgY29uc3Qgcm91bmRlZFNjb3JlID0gTWF0aC5yb3VuZChzY2FsZWRTY29yZSk7XG5cbiAgcmV0dXJuIHJvdW5kZWRTY29yZSA9PT0gMTAwICYmIHNjb3JlICE9PSAxXG4gICAgPyBNYXRoLmZsb29yKHNjYWxlZFNjb3JlKS50b1N0cmluZygpXG4gICAgOiByb3VuZGVkU2NvcmUudG9TdHJpbmcoKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdFNjb3JlV2l0aENvbG9yKFxuICBzY29yZTogbnVtYmVyLFxuICBvcHRpb25zPzogeyBza2lwQm9sZD86IGJvb2xlYW4gfSxcbik6IElubGluZVRleHQge1xuICBjb25zdCBzdHlsZWROdW1iZXIgPSBvcHRpb25zPy5za2lwQm9sZFxuICAgID8gZm9ybWF0UmVwb3J0U2NvcmUoc2NvcmUpXG4gICAgOiBtZC5ib2xkKGZvcm1hdFJlcG9ydFNjb3JlKHNjb3JlKSk7XG4gIHJldHVybiBtZGAke3Njb3JlTWFya2VyKHNjb3JlKX0gJHtzdHlsZWROdW1iZXJ9YDtcbn1cblxuZXhwb3J0IHR5cGUgTWFya2VyU2hhcGUgPSAnY2lyY2xlJyB8ICdzcXVhcmUnO1xuZXhwb3J0IHR5cGUgU2NvcmVDb2xvcnMgPSAncmVkJyB8ICd5ZWxsb3cnIHwgJ2dyZWVuJztcbmV4cG9ydCBjb25zdCBNQVJLRVJTOiBSZWNvcmQ8TWFya2VyU2hhcGUsIFJlY29yZDxTY29yZUNvbG9ycywgc3RyaW5nPj4gPSB7XG4gIGNpcmNsZToge1xuICAgIHJlZDogJ1x1RDgzRFx1REQzNCcsXG4gICAgeWVsbG93OiAnXHVEODNEXHVERkUxJyxcbiAgICBncmVlbjogJ1x1RDgzRFx1REZFMicsXG4gIH0sXG4gIHNxdWFyZToge1xuICAgIHJlZDogJ1x1RDgzRFx1REZFNScsXG4gICAgeWVsbG93OiAnXHVEODNEXHVERkU4JyxcbiAgICBncmVlbjogJ1x1RDgzRFx1REZFOScsXG4gIH0sXG59O1xuXG5leHBvcnQgZnVuY3Rpb24gc2NvcmVNYXJrZXIoXG4gIHNjb3JlOiBudW1iZXIsXG4gIG1hcmtlclR5cGU6IE1hcmtlclNoYXBlID0gJ2NpcmNsZScsXG4pOiBzdHJpbmcge1xuICBpZiAoc2NvcmUgPj0gU0NPUkVfQ09MT1JfUkFOR0UuR1JFRU5fTUlOKSB7XG4gICAgcmV0dXJuIE1BUktFUlNbbWFya2VyVHlwZV0uZ3JlZW47XG4gIH1cbiAgaWYgKHNjb3JlID49IFNDT1JFX0NPTE9SX1JBTkdFLllFTExPV19NSU4pIHtcbiAgICByZXR1cm4gTUFSS0VSU1ttYXJrZXJUeXBlXS55ZWxsb3c7XG4gIH1cbiAgcmV0dXJuIE1BUktFUlNbbWFya2VyVHlwZV0ucmVkO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RGlmZk1hcmtlcihkaWZmOiBudW1iZXIpOiBzdHJpbmcge1xuICBpZiAoZGlmZiA+IDApIHtcbiAgICByZXR1cm4gJ1x1MjE5MSc7XG4gIH1cbiAgaWYgKGRpZmYgPCAwKSB7XG4gICAgcmV0dXJuICdcdTIxOTMnO1xuICB9XG4gIHJldHVybiAnJztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbG9yQnlTY29yZURpZmYodGV4dDogc3RyaW5nLCBkaWZmOiBudW1iZXIpOiBJbmxpbmVUZXh0IHtcbiAgY29uc3QgY29sb3IgPSBkaWZmID4gMCA/ICdncmVlbicgOiBkaWZmIDwgMCA/ICdyZWQnIDogJ2dyYXknO1xuICByZXR1cm4gc2hpZWxkc0JhZGdlKHRleHQsIGNvbG9yKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNoaWVsZHNCYWRnZSh0ZXh0OiBzdHJpbmcsIGNvbG9yOiBzdHJpbmcpOiBJbmxpbmVUZXh0IHtcbiAgcmV0dXJuIG1kLmltYWdlKFxuICAgIGBodHRwczovL2ltZy5zaGllbGRzLmlvL2JhZGdlLyR7ZW5jb2RlVVJJQ29tcG9uZW50KHRleHQpfS0ke2NvbG9yfWAsXG4gICAgdGV4dCxcbiAgKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdERpZmZOdW1iZXIoZGlmZjogbnVtYmVyKTogc3RyaW5nIHtcbiAgY29uc3QgbnVtYmVyID1cbiAgICBNYXRoLmFicyhkaWZmKSA9PT0gTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZID8gJ1x1MjIxRScgOiBgJHtNYXRoLmFicyhkaWZmKX1gO1xuICBjb25zdCBzaWduID0gZGlmZiA8IDAgPyAnXHUyMjEyJyA6ICcrJztcbiAgcmV0dXJuIGAke3NpZ259JHtudW1iZXJ9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNldmVyaXR5TWFya2VyKHNldmVyaXR5OiAnaW5mbycgfCAnd2FybmluZycgfCAnZXJyb3InKTogc3RyaW5nIHtcbiAgaWYgKHNldmVyaXR5ID09PSAnZXJyb3InKSB7XG4gICAgcmV0dXJuICdcdUQ4M0RcdURFQTgnO1xuICB9XG4gIGlmIChzZXZlcml0eSA9PT0gJ3dhcm5pbmcnKSB7XG4gICAgcmV0dXJuICdcdTI2QTBcdUZFMEYnO1xuICB9XG4gIHJldHVybiAnXHUyMTM5XHVGRTBGJztcbn1cblxuY29uc3QgTUlOX05PTl9aRVJPX1JFU1VMVCA9IDAuMTtcblxuZXhwb3J0IGZ1bmN0aW9uIHJvdW5kVmFsdWUodmFsdWU6IG51bWJlcik6IG51bWJlciB7XG4gIGNvbnN0IHJvdW5kZWRWYWx1ZSA9IE1hdGgucm91bmQodmFsdWUgKiAxMCkgLyAxMDsgLy8gcm91bmQgd2l0aCBtYXggMSBkZWNpbWFsXG4gIGlmIChyb3VuZGVkVmFsdWUgPT09IDAgJiYgdmFsdWUgIT09IDApIHtcbiAgICByZXR1cm4gTUlOX05PTl9aRVJPX1JFU1VMVCAqIE1hdGguc2lnbih2YWx1ZSk7XG4gIH1cbiAgcmV0dXJuIHJvdW5kZWRWYWx1ZTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdFNjb3JlQ2hhbmdlKGRpZmY6IG51bWJlcik6IElubGluZVRleHQge1xuICBjb25zdCBtYXJrZXIgPSBnZXREaWZmTWFya2VyKGRpZmYpO1xuICBjb25zdCB0ZXh0ID0gZm9ybWF0RGlmZk51bWJlcihyb3VuZFZhbHVlKGRpZmYgKiAxMDApKTtcbiAgcmV0dXJuIGNvbG9yQnlTY29yZURpZmYoYCR7bWFya2VyfSAke3RleHR9YCwgZGlmZik7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRWYWx1ZUNoYW5nZSh7XG4gIHZhbHVlcyxcbiAgc2NvcmVzLFxufTogUGljazxBdWRpdERpZmYsICd2YWx1ZXMnIHwgJ3Njb3Jlcyc+KTogSW5saW5lVGV4dCB7XG4gIGNvbnN0IG1hcmtlciA9IGdldERpZmZNYXJrZXIodmFsdWVzLmRpZmYpO1xuICBjb25zdCBwZXJjZW50YWdlID1cbiAgICB2YWx1ZXMuYmVmb3JlID09PSAwXG4gICAgICA/IHZhbHVlcy5kaWZmID4gMFxuICAgICAgICA/IE51bWJlci5QT1NJVElWRV9JTkZJTklUWVxuICAgICAgICA6IE51bWJlci5ORUdBVElWRV9JTkZJTklUWVxuICAgICAgOiByb3VuZFZhbHVlKCh2YWx1ZXMuZGlmZiAvIHZhbHVlcy5iZWZvcmUpICogMTAwKTtcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWlycmVndWxhci13aGl0ZXNwYWNlXG4gIGNvbnN0IHRleHQgPSBgJHtmb3JtYXREaWZmTnVtYmVyKHBlcmNlbnRhZ2UpfVx1MjAwOSVgO1xuICByZXR1cm4gY29sb3JCeVNjb3JlRGlmZihgJHttYXJrZXJ9ICR7dGV4dH1gLCBzY29yZXMuZGlmZik7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjYWxjRHVyYXRpb24oc3RhcnQ6IG51bWJlciwgc3RvcD86IG51bWJlcik6IG51bWJlciB7XG4gIHJldHVybiBNYXRoLnJvdW5kKChzdG9wID8/IHBlcmZvcm1hbmNlLm5vdygpKSAtIHN0YXJ0KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvdW50V2VpZ2h0ZWRSZWZzKHJlZnM6IENhdGVnb3J5UmVmW10pIHtcbiAgcmV0dXJuIHJlZnNcbiAgICAuZmlsdGVyKCh7IHdlaWdodCB9KSA9PiB3ZWlnaHQgPiAwKVxuICAgIC5yZWR1Y2UoKHN1bSwgeyB3ZWlnaHQgfSkgPT4gc3VtICsgd2VpZ2h0LCAwKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvdW50Q2F0ZWdvcnlBdWRpdHMoXG4gIHJlZnM6IENhdGVnb3J5UmVmW10sXG4gIHBsdWdpbnM6IFNjb3JlZFJlcG9ydFsncGx1Z2lucyddLFxuKTogbnVtYmVyIHtcbiAgLy8gQ3JlYXRlIGxvb2t1cCBvYmplY3QgZm9yIGdyb3VwcyB3aXRoaW4gZWFjaCBwbHVnaW5cbiAgY29uc3QgZ3JvdXBMb29rdXAgPSBwbHVnaW5zLnJlZHVjZTxSZWNvcmQ8c3RyaW5nLCBSZWNvcmQ8c3RyaW5nLCBHcm91cD4+PihcbiAgICAobG9va3VwLCBwbHVnaW4pID0+IHtcbiAgICAgIGlmIChwbHVnaW4uZ3JvdXBzID09IG51bGwgfHwgcGx1Z2luLmdyb3Vwcy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgcmV0dXJuIGxvb2t1cDtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgLi4ubG9va3VwLFxuICAgICAgICBbcGx1Z2luLnNsdWddOiBPYmplY3QuZnJvbUVudHJpZXMoXG4gICAgICAgICAgcGx1Z2luLmdyb3Vwcy5tYXAoZ3JvdXAgPT4gW2dyb3VwLnNsdWcsIGdyb3VwXSksXG4gICAgICAgICksXG4gICAgICB9O1xuICAgIH0sXG4gICAge30sXG4gICk7XG5cbiAgLy8gQ291bnQgYXVkaXRzXG4gIHJldHVybiByZWZzLnJlZHVjZSgoYWNjLCByZWYpID0+IHtcbiAgICBpZiAocmVmLnR5cGUgPT09ICdncm91cCcpIHtcbiAgICAgIGNvbnN0IGdyb3VwUmVmcyA9IGdyb3VwTG9va3VwW3JlZi5wbHVnaW5dPy5bcmVmLnNsdWddPy5yZWZzO1xuICAgICAgcmV0dXJuIGFjYyArIChncm91cFJlZnM/Lmxlbmd0aCA/PyAwKTtcbiAgICB9XG4gICAgcmV0dXJuIGFjYyArIDE7XG4gIH0sIDApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29tcGFyZUNhdGVnb3J5QXVkaXRzQW5kR3JvdXBzKFxuICBhOiBTb3J0YWJsZUF1ZGl0UmVwb3J0IHwgU29ydGFibGVHcm91cCxcbiAgYjogU29ydGFibGVBdWRpdFJlcG9ydCB8IFNvcnRhYmxlR3JvdXAsXG4pOiBudW1iZXIge1xuICBpZiAoYS5zY29yZSAhPT0gYi5zY29yZSkge1xuICAgIHJldHVybiBhLnNjb3JlIC0gYi5zY29yZTtcbiAgfVxuXG4gIGlmIChhLndlaWdodCAhPT0gYi53ZWlnaHQpIHtcbiAgICByZXR1cm4gYi53ZWlnaHQgLSBhLndlaWdodDtcbiAgfVxuXG4gIGlmICgndmFsdWUnIGluIGEgJiYgJ3ZhbHVlJyBpbiBiICYmIGEudmFsdWUgIT09IGIudmFsdWUpIHtcbiAgICByZXR1cm4gYi52YWx1ZSAtIGEudmFsdWU7XG4gIH1cblxuICByZXR1cm4gYS50aXRsZS5sb2NhbGVDb21wYXJlKGIudGl0bGUpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29tcGFyZUF1ZGl0cyhhOiBBdWRpdFJlcG9ydCwgYjogQXVkaXRSZXBvcnQpOiBudW1iZXIge1xuICBpZiAoYS5zY29yZSAhPT0gYi5zY29yZSkge1xuICAgIHJldHVybiBhLnNjb3JlIC0gYi5zY29yZTtcbiAgfVxuXG4gIGlmIChhLnZhbHVlICE9PSBiLnZhbHVlKSB7XG4gICAgcmV0dXJuIGIudmFsdWUgLSBhLnZhbHVlO1xuICB9XG5cbiAgcmV0dXJuIGEudGl0bGUubG9jYWxlQ29tcGFyZShiLnRpdGxlKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbXBhcmVJc3N1ZVNldmVyaXR5KFxuICBzZXZlcml0eTE6IENsaUlzc3VlU2V2ZXJpdHksXG4gIHNldmVyaXR5MjogQ2xpSXNzdWVTZXZlcml0eSxcbik6IG51bWJlciB7XG4gIGNvbnN0IGxldmVsczogUmVjb3JkPENsaUlzc3VlU2V2ZXJpdHksIG51bWJlcj4gPSB7XG4gICAgaW5mbzogMCxcbiAgICB3YXJuaW5nOiAxLFxuICAgIGVycm9yOiAyLFxuICB9O1xuICByZXR1cm4gbGV2ZWxzW3NldmVyaXR5MV0gLSBsZXZlbHNbc2V2ZXJpdHkyXTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHRocm93SXNOb3RQcmVzZW50RXJyb3IoXG4gIGl0ZW1OYW1lOiBzdHJpbmcsXG4gIHByZXNlbnRQbGFjZTogc3RyaW5nLFxuKTogbmV2ZXIge1xuICB0aHJvdyBuZXcgRXJyb3IoYCR7aXRlbU5hbWV9IGlzIG5vdCBwcmVzZW50IGluICR7cHJlc2VudFBsYWNlfWApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0UGx1Z2luTmFtZUZyb21TbHVnKFxuICBzbHVnOiBzdHJpbmcsXG4gIHBsdWdpbnM6IFNjb3JlZFJlcG9ydFsncGx1Z2lucyddLFxuKTogc3RyaW5nIHtcbiAgcmV0dXJuIChcbiAgICBwbHVnaW5zLmZpbmQoKHsgc2x1ZzogcGx1Z2luU2x1ZyB9KSA9PiBwbHVnaW5TbHVnID09PSBzbHVnKT8udGl0bGUgfHwgc2x1Z1xuICApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29tcGFyZUlzc3VlcyhhOiBJc3N1ZSwgYjogSXNzdWUpOiBudW1iZXIge1xuICBpZiAoYS5zZXZlcml0eSAhPT0gYi5zZXZlcml0eSkge1xuICAgIHJldHVybiAtY29tcGFyZUlzc3VlU2V2ZXJpdHkoYS5zZXZlcml0eSwgYi5zZXZlcml0eSk7XG4gIH1cbiAgaWYgKCFhLnNvdXJjZSAmJiBiLnNvdXJjZSkge1xuICAgIHJldHVybiAtMTtcbiAgfVxuICBpZiAoYS5zb3VyY2UgJiYgIWIuc291cmNlKSB7XG4gICAgcmV0dXJuIDE7XG4gIH1cbiAgaWYgKGEuc291cmNlPy5maWxlICE9PSBiLnNvdXJjZT8uZmlsZSkge1xuICAgIHJldHVybiBhLnNvdXJjZT8uZmlsZS5sb2NhbGVDb21wYXJlKGIuc291cmNlPy5maWxlIHx8ICcnKSA/PyAwO1xuICB9XG4gIHJldHVybiBjb21wYXJlU291cmNlRmlsZVBvc2l0aW9uKGEuc291cmNlPy5wb3NpdGlvbiwgYi5zb3VyY2U/LnBvc2l0aW9uKTtcbn1cblxuZnVuY3Rpb24gY29tcGFyZVNvdXJjZUZpbGVQb3NpdGlvbihcbiAgYTogTm9uTnVsbGFibGU8SXNzdWVbJ3NvdXJjZSddPlsncG9zaXRpb24nXSxcbiAgYjogTm9uTnVsbGFibGU8SXNzdWVbJ3NvdXJjZSddPlsncG9zaXRpb24nXSxcbik6IG51bWJlciB7XG4gIGlmICghYSAmJiBiKSB7XG4gICAgcmV0dXJuIC0xO1xuICB9XG4gIGlmIChhICYmICFiKSB7XG4gICAgcmV0dXJuIDE7XG4gIH1cbiAgaWYgKGE/LnN0YXJ0TGluZSAhPT0gYj8uc3RhcnRMaW5lKSB7XG4gICAgcmV0dXJuIChhPy5zdGFydExpbmUgPz8gMCkgLSAoYj8uc3RhcnRMaW5lID8/IDApO1xuICB9XG4gIHJldHVybiAwO1xufVxuXG4vLyBAVE9ETyByZXRoaW5rIGltcGxlbWVudGF0aW9uXG5leHBvcnQgZnVuY3Rpb24gYXBwbHlTY29yZUNvbG9yKFxuICB7IHNjb3JlLCB0ZXh0IH06IHsgc2NvcmU6IG51bWJlcjsgdGV4dD86IHN0cmluZyB9LFxuICBzdHlsZTogQW5zaXMgPSBhbnNpcyxcbikge1xuICBjb25zdCBmb3JtYXR0ZWRTY29yZSA9IHRleHQgPz8gZm9ybWF0UmVwb3J0U2NvcmUoc2NvcmUpO1xuXG4gIGlmIChzY29yZSA+PSBTQ09SRV9DT0xPUl9SQU5HRS5HUkVFTl9NSU4pIHtcbiAgICByZXR1cm4gdGV4dFxuICAgICAgPyBzdHlsZS5ncmVlbihmb3JtYXR0ZWRTY29yZSlcbiAgICAgIDogc3R5bGUuYm9sZChzdHlsZS5ncmVlbihmb3JtYXR0ZWRTY29yZSkpO1xuICB9XG5cbiAgaWYgKHNjb3JlID49IFNDT1JFX0NPTE9SX1JBTkdFLllFTExPV19NSU4pIHtcbiAgICByZXR1cm4gdGV4dFxuICAgICAgPyBzdHlsZS55ZWxsb3coZm9ybWF0dGVkU2NvcmUpXG4gICAgICA6IHN0eWxlLmJvbGQoc3R5bGUueWVsbG93KGZvcm1hdHRlZFNjb3JlKSk7XG4gIH1cblxuICByZXR1cm4gdGV4dFxuICAgID8gc3R5bGUucmVkKGZvcm1hdHRlZFNjb3JlKVxuICAgIDogc3R5bGUuYm9sZChzdHlsZS5yZWQoZm9ybWF0dGVkU2NvcmUpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHRhcmdldFNjb3JlSWNvbihcbiAgc2NvcmU6IG51bWJlcixcbiAgdGFyZ2V0U2NvcmU/OiBudW1iZXIsXG4gIG9wdGlvbnM6IHtcbiAgICBwYXNzSWNvbj86IHN0cmluZztcbiAgICBmYWlsSWNvbj86IHN0cmluZztcbiAgICBwcmVmaXg/OiBzdHJpbmc7XG4gICAgcG9zdGZpeD86IHN0cmluZztcbiAgfSA9IHt9LFxuKTogc3RyaW5nIHtcbiAgaWYgKHRhcmdldFNjb3JlICE9IG51bGwpIHtcbiAgICBjb25zdCB7XG4gICAgICBwYXNzSWNvbiA9ICdcdTI3MDUnLFxuICAgICAgZmFpbEljb24gPSAnXHUyNzRDJyxcbiAgICAgIHByZWZpeCA9ICcnLFxuICAgICAgcG9zdGZpeCA9ICcnLFxuICAgIH0gPSBvcHRpb25zO1xuICAgIGlmIChzY29yZSA+PSB0YXJnZXRTY29yZSkge1xuICAgICAgcmV0dXJuIGAke3ByZWZpeH0ke3Bhc3NJY29ufSR7cG9zdGZpeH1gO1xuICAgIH1cbiAgICByZXR1cm4gYCR7cHJlZml4fSR7ZmFpbEljb259JHtwb3N0Zml4fWA7XG4gIH1cbiAgcmV0dXJuICcnO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9maWxlLXN5c3RlbS50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvZmlsZS1zeXN0ZW0udHNcIjtpbXBvcnQgeyBib2xkLCBncmF5IH0gZnJvbSAnYW5zaXMnO1xuaW1wb3J0IHsgdHlwZSBPcHRpb25zLCBidW5kbGVSZXF1aXJlIH0gZnJvbSAnYnVuZGxlLXJlcXVpcmUnO1xuaW1wb3J0IHsgbWtkaXIsIHJlYWRGaWxlLCByZWFkZGlyLCBybSwgc3RhdCB9IGZyb20gJ25vZGU6ZnMvcHJvbWlzZXMnO1xuaW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB7IGZvcm1hdEJ5dGVzIH0gZnJvbSAnLi9mb3JtYXR0aW5nLmpzJztcbmltcG9ydCB7IGxvZ011bHRpcGxlUmVzdWx0cyB9IGZyb20gJy4vbG9nLXJlc3VsdHMuanMnO1xuaW1wb3J0IHsgdWkgfSBmcm9tICcuL2xvZ2dpbmcuanMnO1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVhZFRleHRGaWxlKGZpbGVQYXRoOiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz4ge1xuICBjb25zdCBidWZmZXIgPSBhd2FpdCByZWFkRmlsZShmaWxlUGF0aCk7XG4gIHJldHVybiBidWZmZXIudG9TdHJpbmcoKTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlYWRKc29uRmlsZTxUID0gdW5rbm93bj4oZmlsZVBhdGg6IHN0cmluZyk6IFByb21pc2U8VD4ge1xuICBjb25zdCB0ZXh0ID0gYXdhaXQgcmVhZFRleHRGaWxlKGZpbGVQYXRoKTtcbiAgcmV0dXJuIEpTT04ucGFyc2UodGV4dCkgYXMgVDtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGZpbGVFeGlzdHMoZmlsZVBhdGg6IHN0cmluZyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICB0cnkge1xuICAgIGNvbnN0IHN0YXRzID0gYXdhaXQgc3RhdChmaWxlUGF0aCk7XG4gICAgcmV0dXJuIHN0YXRzLmlzRmlsZSgpO1xuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGRpcmVjdG9yeUV4aXN0cyhmaWxlUGF0aDogc3RyaW5nKTogUHJvbWlzZTxib29sZWFuPiB7XG4gIHRyeSB7XG4gICAgY29uc3Qgc3RhdHMgPSBhd2FpdCBzdGF0KGZpbGVQYXRoKTtcbiAgICByZXR1cm4gc3RhdHMuaXNEaXJlY3RvcnkoKTtcbiAgfSBjYXRjaCB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBlbnN1cmVEaXJlY3RvcnlFeGlzdHMoYmFzZURpcjogc3RyaW5nKSB7XG4gIHRyeSB7XG4gICAgYXdhaXQgbWtkaXIoYmFzZURpciwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgcmV0dXJuO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIHVpKCkubG9nZ2VyLmluZm8oKGVycm9yIGFzIHsgY29kZTogc3RyaW5nOyBtZXNzYWdlOiBzdHJpbmcgfSkubWVzc2FnZSk7XG4gICAgaWYgKChlcnJvciBhcyB7IGNvZGU6IHN0cmluZyB9KS5jb2RlICE9PSAnRUVYSVNUJykge1xuICAgICAgdGhyb3cgZXJyb3I7XG4gICAgfVxuICB9XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZW1vdmVEaXJlY3RvcnlJZkV4aXN0cyhkaXI6IHN0cmluZykge1xuICBpZiAoYXdhaXQgZGlyZWN0b3J5RXhpc3RzKGRpcikpIHtcbiAgICBhd2FpdCBybShkaXIsIHsgcmVjdXJzaXZlOiB0cnVlLCBmb3JjZTogdHJ1ZSB9KTtcbiAgfVxufVxuXG5leHBvcnQgdHlwZSBGaWxlUmVzdWx0ID0gcmVhZG9ubHkgW3N0cmluZ10gfCByZWFkb25seSBbc3RyaW5nLCBudW1iZXJdO1xuZXhwb3J0IHR5cGUgTXVsdGlwbGVGaWxlUmVzdWx0cyA9IFByb21pc2VTZXR0bGVkUmVzdWx0PEZpbGVSZXN1bHQ+W107XG5cbmV4cG9ydCBmdW5jdGlvbiBsb2dNdWx0aXBsZUZpbGVSZXN1bHRzKFxuICBmaWxlUmVzdWx0czogTXVsdGlwbGVGaWxlUmVzdWx0cyxcbiAgbWVzc2FnZVByZWZpeDogc3RyaW5nLFxuKTogdm9pZCB7XG4gIGNvbnN0IHN1Y2NlZWRlZFRyYW5zZm9ybSA9IChyZXN1bHQ6IFByb21pc2VGdWxmaWxsZWRSZXN1bHQ8RmlsZVJlc3VsdD4pID0+IHtcbiAgICBjb25zdCBbZmlsZU5hbWUsIHNpemVdID0gcmVzdWx0LnZhbHVlO1xuICAgIGNvbnN0IGZvcm1hdHRlZFNpemUgPSBzaXplID8gYCAoJHtncmF5KGZvcm1hdEJ5dGVzKHNpemUpKX0pYCA6ICcnO1xuICAgIHJldHVybiBgLSAke2JvbGQoZmlsZU5hbWUpfSR7Zm9ybWF0dGVkU2l6ZX1gO1xuICB9O1xuICBjb25zdCBmYWlsZWRUcmFuc2Zvcm0gPSAocmVzdWx0OiBQcm9taXNlUmVqZWN0ZWRSZXN1bHQpID0+XG4gICAgYC0gJHtib2xkKHJlc3VsdC5yZWFzb24gYXMgc3RyaW5nKX1gO1xuXG4gIGxvZ011bHRpcGxlUmVzdWx0czxGaWxlUmVzdWx0PihcbiAgICBmaWxlUmVzdWx0cyxcbiAgICBtZXNzYWdlUHJlZml4LFxuICAgIHN1Y2NlZWRlZFRyYW5zZm9ybSxcbiAgICBmYWlsZWRUcmFuc2Zvcm0sXG4gICk7XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBpbXBvcnRNb2R1bGU8VCA9IHVua25vd24+KG9wdGlvbnM6IE9wdGlvbnMpOiBQcm9taXNlPFQ+IHtcbiAgY29uc3QgeyBtb2QgfSA9IGF3YWl0IGJ1bmRsZVJlcXVpcmU8b2JqZWN0PihvcHRpb25zKTtcblxuICBpZiAodHlwZW9mIG1vZCA9PT0gJ29iamVjdCcgJiYgJ2RlZmF1bHQnIGluIG1vZCkge1xuICAgIHJldHVybiBtb2QuZGVmYXVsdCBhcyBUO1xuICB9XG4gIHJldHVybiBtb2QgYXMgVDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBsdWdpbldvcmtEaXIoc2x1Zzogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIHBhdGguam9pbignbm9kZV9tb2R1bGVzJywgJy5jb2RlLXB1c2h1cCcsIHNsdWcpO1xufVxuXG5leHBvcnQgdHlwZSBDcmF3bEZpbGVTeXN0ZW1PcHRpb25zPFQ+ID0ge1xuICBkaXJlY3Rvcnk6IHN0cmluZztcbiAgcGF0dGVybj86IHN0cmluZyB8IFJlZ0V4cDtcbiAgZmlsZVRyYW5zZm9ybT86IChmaWxlUGF0aDogc3RyaW5nKSA9PiBQcm9taXNlPFQ+IHwgVDtcbn07XG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3Jhd2xGaWxlU3lzdGVtPFQgPSBzdHJpbmc+KFxuICBvcHRpb25zOiBDcmF3bEZpbGVTeXN0ZW1PcHRpb25zPFQ+LFxuKTogUHJvbWlzZTxUW10+IHtcbiAgY29uc3Qge1xuICAgIGRpcmVjdG9yeSxcbiAgICBwYXR0ZXJuLFxuICAgIGZpbGVUcmFuc2Zvcm0gPSAoZmlsZVBhdGg6IHN0cmluZykgPT4gZmlsZVBhdGggYXMgVCxcbiAgfSA9IG9wdGlvbnM7XG5cbiAgY29uc3QgZmlsZXMgPSBhd2FpdCByZWFkZGlyKGRpcmVjdG9yeSk7XG4gIGNvbnN0IHByb21pc2VzID0gZmlsZXMubWFwKGFzeW5jIChmaWxlKTogUHJvbWlzZTxUIHwgVFtdPiA9PiB7XG4gICAgY29uc3QgZmlsZVBhdGggPSBwYXRoLmpvaW4oZGlyZWN0b3J5LCBmaWxlKTtcbiAgICBjb25zdCBzdGF0cyA9IGF3YWl0IHN0YXQoZmlsZVBhdGgpO1xuXG4gICAgaWYgKHN0YXRzLmlzRGlyZWN0b3J5KCkpIHtcbiAgICAgIHJldHVybiBjcmF3bEZpbGVTeXN0ZW0oeyBkaXJlY3Rvcnk6IGZpbGVQYXRoLCBwYXR0ZXJuLCBmaWxlVHJhbnNmb3JtIH0pO1xuICAgIH1cbiAgICBpZiAoc3RhdHMuaXNGaWxlKCkgJiYgKCFwYXR0ZXJuIHx8IG5ldyBSZWdFeHAocGF0dGVybikudGVzdChmaWxlKSkpIHtcbiAgICAgIHJldHVybiBmaWxlVHJhbnNmb3JtKGZpbGVQYXRoKTtcbiAgICB9XG4gICAgcmV0dXJuIFtdO1xuICB9KTtcblxuICBjb25zdCByZXN1bHRzTmVzdGVkQXJyYXkgPSBhd2FpdCBQcm9taXNlLmFsbChwcm9taXNlcyk7XG4gIHJldHVybiByZXN1bHRzTmVzdGVkQXJyYXkuZmxhdCgpIGFzIFRbXTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGZpbmROZWFyZXN0RmlsZShcbiAgZmlsZU5hbWVzOiBzdHJpbmdbXSxcbiAgY3dkID0gcHJvY2Vzcy5jd2QoKSxcbik6IFByb21pc2U8c3RyaW5nIHwgdW5kZWZpbmVkPiB7XG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBmdW5jdGlvbmFsL25vLWxvb3Atc3RhdGVtZW50c1xuICBmb3IgKFxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBmdW5jdGlvbmFsL25vLWxldFxuICAgIGxldCBkaXJlY3RvcnkgPSBjd2Q7XG4gICAgZGlyZWN0b3J5ICE9PSBwYXRoLmRpcm5hbWUoZGlyZWN0b3J5KTtcbiAgICBkaXJlY3RvcnkgPSBwYXRoLmRpcm5hbWUoZGlyZWN0b3J5KVxuICApIHtcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZnVuY3Rpb25hbC9uby1sb29wLXN0YXRlbWVudHNcbiAgICBmb3IgKGNvbnN0IGZpbGUgb2YgZmlsZU5hbWVzKSB7XG4gICAgICBpZiAoYXdhaXQgZmlsZUV4aXN0cyhwYXRoLmpvaW4oZGlyZWN0b3J5LCBmaWxlKSkpIHtcbiAgICAgICAgcmV0dXJuIHBhdGguam9pbihkaXJlY3RvcnksIGZpbGUpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICByZXR1cm4gdW5kZWZpbmVkO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZmluZExpbmVOdW1iZXJJblRleHQoXG4gIGNvbnRlbnQ6IHN0cmluZyxcbiAgcGF0dGVybjogc3RyaW5nLFxuKTogbnVtYmVyIHwgbnVsbCB7XG4gIGNvbnN0IGxpbmVzID0gY29udGVudC5zcGxpdCgvXFxyP1xcbi8pOyAvLyBTcGxpdCBsaW5lcywgaGFuZGxlIGJvdGggV2luZG93cyBhbmQgVU5JWCBsaW5lIGVuZGluZ3NcblxuICBjb25zdCBsaW5lTnVtYmVyID0gbGluZXMuZmluZEluZGV4KGxpbmUgPT4gbGluZS5pbmNsdWRlcyhwYXR0ZXJuKSkgKyAxOyAvLyArMSBiZWNhdXNlIGxpbmUgbnVtYmVycyBhcmUgMS1iYXNlZFxuICByZXR1cm4gbGluZU51bWJlciA9PT0gMCA/IG51bGwgOiBsaW5lTnVtYmVyOyAvLyBJZiB0aGUgcGFja2FnZSBpc24ndCBmb3VuZCwgcmV0dXJuIG51bGxcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZpbGVQYXRoVG9DbGlBcmcoZmlsZVBhdGg6IHN0cmluZyk6IHN0cmluZyB7XG4gIC8vIG5lZWRzIHRvIGJlIGVzY2FwZWQgaWYgc3BhY2VzIGluY2x1ZGVkXG4gIHJldHVybiBgXCIke2ZpbGVQYXRofVwiYDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHByb2plY3RUb0ZpbGVuYW1lKHByb2plY3Q6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiBwcm9qZWN0LnJlcGxhY2UoL1svXFxcXFxcc10rL2csICctJykucmVwbGFjZSgvQC9nLCAnJyk7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL2Zvcm1hdHRpbmcudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYlwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL2Zvcm1hdHRpbmcudHNcIjtpbXBvcnQge1xuICBNQVhfREVTQ1JJUFRJT05fTEVOR1RILFxuICBNQVhfSVNTVUVfTUVTU0FHRV9MRU5HVEgsXG4gIE1BWF9USVRMRV9MRU5HVEgsXG59IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuXG5leHBvcnQgZnVuY3Rpb24gc2x1Z2lmeSh0ZXh0OiBzdHJpbmcpOiBzdHJpbmcge1xuICByZXR1cm4gdGV4dFxuICAgIC50cmltKClcbiAgICAudG9Mb3dlckNhc2UoKVxuICAgIC5yZXBsYWNlKC9cXHMrfFxcLy9nLCAnLScpXG4gICAgLnJlcGxhY2UoL1teYS16XFxkLV0vZywgJycpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcGx1cmFsaXplKHRleHQ6IHN0cmluZywgYW1vdW50PzogbnVtYmVyKTogc3RyaW5nIHtcbiAgaWYgKGFtb3VudCAhPSBudWxsICYmIE1hdGguYWJzKGFtb3VudCkgPT09IDEpIHtcbiAgICByZXR1cm4gdGV4dDtcbiAgfVxuXG4gIGlmICh0ZXh0LmVuZHNXaXRoKCd5JykpIHtcbiAgICByZXR1cm4gYCR7dGV4dC5zbGljZSgwLCAtMSl9aWVzYDtcbiAgfVxuICBpZiAodGV4dC5lbmRzV2l0aCgncycpKSB7XG4gICAgcmV0dXJuIGAke3RleHR9ZXNgO1xuICB9XG4gIHJldHVybiBgJHt0ZXh0fXNgO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZm9ybWF0Qnl0ZXMoYnl0ZXM6IG51bWJlciwgZGVjaW1hbHMgPSAyKSB7XG4gIGNvbnN0IHBvc2l0aXZlQnl0ZXMgPSBNYXRoLm1heChieXRlcywgMCk7XG5cbiAgLy8gZWFybHkgZXhpdFxuICBpZiAocG9zaXRpdmVCeXRlcyA9PT0gMCkge1xuICAgIHJldHVybiAnMCBCJztcbiAgfVxuXG4gIGNvbnN0IGsgPSAxMDI0O1xuICBjb25zdCBkbSA9IE1hdGgubWF4KGRlY2ltYWxzLCAwKTtcbiAgY29uc3Qgc2l6ZXMgPSBbJ0InLCAna0InLCAnTUInLCAnR0InLCAnVEInLCAnUEInLCAnRUInLCAnWkInLCAnWUInXTtcblxuICBjb25zdCBpID0gTWF0aC5mbG9vcihNYXRoLmxvZyhwb3NpdGl2ZUJ5dGVzKSAvIE1hdGgubG9nKGspKTtcblxuICByZXR1cm4gYCR7TnVtYmVyLnBhcnNlRmxvYXQoKHBvc2l0aXZlQnl0ZXMgLyBNYXRoLnBvdyhrLCBpKSkudG9GaXhlZChkbSkpfSAke1xuICAgIHNpemVzW2ldXG4gIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcGx1cmFsaXplVG9rZW4odG9rZW46IHN0cmluZywgdGltZXM6IG51bWJlcik6IHN0cmluZyB7XG4gIHJldHVybiBgJHt0aW1lc30gJHtNYXRoLmFicyh0aW1lcykgPT09IDEgPyB0b2tlbiA6IHBsdXJhbGl6ZSh0b2tlbil9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdER1cmF0aW9uKGR1cmF0aW9uOiBudW1iZXIsIGdyYW51bGFyaXR5ID0gMCk6IHN0cmluZyB7XG4gIGlmIChkdXJhdGlvbiA8IDEwMDApIHtcbiAgICByZXR1cm4gYCR7Z3JhbnVsYXJpdHkgPyBkdXJhdGlvbi50b0ZpeGVkKGdyYW51bGFyaXR5KSA6IGR1cmF0aW9ufSBtc2A7XG4gIH1cbiAgcmV0dXJuIGAkeyhkdXJhdGlvbiAvIDEwMDApLnRvRml4ZWQoMil9IHNgO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZm9ybWF0RGF0ZShkYXRlOiBEYXRlKTogc3RyaW5nIHtcbiAgY29uc3QgbG9jYWxlID0gJ2VuLVVTJzsgLy8gZml4ZWQgbG9jYWxlIHRvIGVuc3VyZSBjb25zaXN0ZW5jeSBhY3Jvc3MgbG9jYWwgZGVmYXVsdHMgZXhlY3V0aW9uXG4gIHJldHVybiBkYXRlXG4gICAgLnRvTG9jYWxlU3RyaW5nKGxvY2FsZSwge1xuICAgICAgd2Vla2RheTogJ3Nob3J0JyxcbiAgICAgIG1vbnRoOiAnc2hvcnQnLFxuICAgICAgZGF5OiAnbnVtZXJpYycsXG4gICAgICB5ZWFyOiAnbnVtZXJpYycsXG4gICAgICBob3VyOiAnbnVtZXJpYycsXG4gICAgICBtaW51dGU6ICcyLWRpZ2l0JyxcbiAgICAgIHRpbWVab25lTmFtZTogJ3Nob3J0JyxcbiAgICB9KVxuICAgIC5yZXBsYWNlKC9cXHUyMDJGL2csICcgJyk7IC8vIHNlZSBodHRwczovL2dpdGh1Yi5jb20vbm9kZWpzL25vZGUvaXNzdWVzLzQ1MTcxXG59XG5cbmV4cG9ydCBmdW5jdGlvbiB0cnVuY2F0ZVRleHQoXG4gIHRleHQ6IHN0cmluZyxcbiAgb3B0aW9uczpcbiAgICB8IG51bWJlclxuICAgIHwge1xuICAgICAgICBtYXhDaGFyczogbnVtYmVyO1xuICAgICAgICBwb3NpdGlvbj86ICdzdGFydCcgfCAnbWlkZGxlJyB8ICdlbmQnO1xuICAgICAgICBlbGxpcHNpcz86IHN0cmluZztcbiAgICAgIH0sXG4pOiBzdHJpbmcge1xuICBjb25zdCB7XG4gICAgbWF4Q2hhcnMsXG4gICAgcG9zaXRpb24gPSAnZW5kJyxcbiAgICBlbGxpcHNpcyA9ICcuLi4nLFxuICB9ID0gdHlwZW9mIG9wdGlvbnMgPT09ICdudW1iZXInID8geyBtYXhDaGFyczogb3B0aW9ucyB9IDogb3B0aW9ucztcbiAgaWYgKHRleHQubGVuZ3RoIDw9IG1heENoYXJzKSB7XG4gICAgcmV0dXJuIHRleHQ7XG4gIH1cblxuICBjb25zdCBtYXhMZW5ndGggPSBtYXhDaGFycyAtIGVsbGlwc2lzLmxlbmd0aDtcbiAgc3dpdGNoIChwb3NpdGlvbikge1xuICAgIGNhc2UgJ3N0YXJ0JzpcbiAgICAgIHJldHVybiBlbGxpcHNpcyArIHRleHQuc2xpY2UoLW1heExlbmd0aCkudHJpbSgpO1xuICAgIGNhc2UgJ21pZGRsZSc6XG4gICAgICBjb25zdCBoYWxmTWF4Q2hhcnMgPSBNYXRoLmZsb29yKG1heExlbmd0aCAvIDIpO1xuICAgICAgcmV0dXJuIChcbiAgICAgICAgdGV4dC5zbGljZSgwLCBoYWxmTWF4Q2hhcnMpLnRyaW0oKSArXG4gICAgICAgIGVsbGlwc2lzICtcbiAgICAgICAgdGV4dC5zbGljZSgtaGFsZk1heENoYXJzKS50cmltKClcbiAgICAgICk7XG4gICAgY2FzZSAnZW5kJzpcbiAgICAgIHJldHVybiB0ZXh0LnNsaWNlKDAsIG1heExlbmd0aCkudHJpbSgpICsgZWxsaXBzaXM7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHRydW5jYXRlVGl0bGUodGV4dDogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIHRydW5jYXRlVGV4dCh0ZXh0LCBNQVhfVElUTEVfTEVOR1RIKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHRydW5jYXRlRGVzY3JpcHRpb24odGV4dDogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIHRydW5jYXRlVGV4dCh0ZXh0LCBNQVhfREVTQ1JJUFRJT05fTEVOR1RIKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHRydW5jYXRlSXNzdWVNZXNzYWdlKHRleHQ6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiB0cnVuY2F0ZVRleHQodGV4dCwgTUFYX0lTU1VFX01FU1NBR0VfTEVOR1RIKTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvbG9nZ2luZy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvbG9nZ2luZy50c1wiO2ltcG9ydCBpc2FhY3NfY2xpdWkgZnJvbSAnQGlzYWFjcy9jbGl1aSc7XG5pbXBvcnQgeyBjbGl1aSB9IGZyb20gJ0Bwb3BwaW5zcy9jbGl1aSc7XG5pbXBvcnQgeyB1bmRlcmxpbmUgfSBmcm9tICdhbnNpcyc7XG5pbXBvcnQgeyBURVJNSU5BTF9XSURUSCB9IGZyb20gJy4vcmVwb3J0cy9jb25zdGFudHMuanMnO1xuXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnlcbnR5cGUgQXJndW1lbnRzVHlwZTxUPiA9IFQgZXh0ZW5kcyAoLi4uYXJnczogaW5mZXIgVSkgPT4gYW55ID8gVSA6IG5ldmVyO1xuZXhwb3J0IHR5cGUgQ2xpVWlCYXNlID0gUmV0dXJuVHlwZTx0eXBlb2YgY2xpdWk+O1xudHlwZSBVSSA9IFJldHVyblR5cGU8dHlwZW9mIGlzYWFjc19jbGl1aT47XG50eXBlIENsaUV4dGVuc2lvbiA9IHtcbiAgcm93OiAocjogQXJndW1lbnRzVHlwZTxVSVsnZGl2J10+KSA9PiB2b2lkO1xufTtcbmV4cG9ydCB0eXBlIENvbHVtbiA9IHtcbiAgdGV4dDogc3RyaW5nO1xuICB3aWR0aD86IG51bWJlcjtcbiAgYWxpZ24/OiAncmlnaHQnIHwgJ2xlZnQnIHwgJ2NlbnRlcic7XG4gIHBhZGRpbmc6IG51bWJlcltdO1xuICBib3JkZXI/OiBib29sZWFuO1xufTtcbmV4cG9ydCB0eXBlIENsaVVpID0gQ2xpVWlCYXNlICYgQ2xpRXh0ZW5zaW9uO1xuXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgaW1wb3J0L25vLW11dGFibGUtZXhwb3J0cyxmdW5jdGlvbmFsL25vLWxldFxuZXhwb3J0IGxldCBzaW5nbGV0b25VaUluc3RhbmNlOiBDbGlVaUJhc2UgfCB1bmRlZmluZWQ7XG5cbmV4cG9ydCBmdW5jdGlvbiB1aSgpOiBDbGlVaSB7XG4gIGlmIChzaW5nbGV0b25VaUluc3RhbmNlID09PSB1bmRlZmluZWQpIHtcbiAgICBzaW5nbGV0b25VaUluc3RhbmNlID0gY2xpdWkoKTtcbiAgfVxuICByZXR1cm4ge1xuICAgIC4uLnNpbmdsZXRvblVpSW5zdGFuY2UsXG4gICAgcm93OiBhcmdzID0+IHtcbiAgICAgIGxvZ0xpc3RJdGVtKGFyZ3MpO1xuICAgIH0sXG4gIH07XG59XG5cbi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBmdW5jdGlvbmFsL25vLWxldFxubGV0IHNpbmdsZXRvbmlzYWFjVWk6IFVJIHwgdW5kZWZpbmVkO1xuZXhwb3J0IGZ1bmN0aW9uIGxvZ0xpc3RJdGVtKGFyZ3M6IEFyZ3VtZW50c1R5cGU8VUlbJ2RpdiddPikge1xuICBpZiAoc2luZ2xldG9uaXNhYWNVaSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgc2luZ2xldG9uaXNhYWNVaSA9IGlzYWFjc19jbGl1aSh7IHdpZHRoOiBURVJNSU5BTF9XSURUSCB9KTtcbiAgfVxuICBzaW5nbGV0b25pc2FhY1VpLmRpdiguLi5hcmdzKTtcbiAgY29uc3QgY29udGVudCA9IHNpbmdsZXRvbmlzYWFjVWkudG9TdHJpbmcoKTtcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGZ1bmN0aW9uYWwvaW1tdXRhYmxlLWRhdGFcbiAgc2luZ2xldG9uaXNhYWNVaS5yb3dzID0gW107XG4gIHNpbmdsZXRvblVpSW5zdGFuY2U/LmxvZ2dlci5sb2coY29udGVudCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsaW5rKHRleHQ6IHN0cmluZykge1xuICByZXR1cm4gdW5kZXJsaW5lLmJsdWVCcmlnaHQodGV4dCk7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL2dpdC9naXQuY29tbWl0cy1hbmQtdGFncy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL2dpdFwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL2dpdC9naXQuY29tbWl0cy1hbmQtdGFncy50c1wiO2ltcG9ydCB7IHR5cGUgTG9nT3B0aW9ucyBhcyBTaW1wbGVHaXRMb2dPcHRpb25zLCBzaW1wbGVHaXQgfSBmcm9tICdzaW1wbGUtZ2l0JztcbmltcG9ydCB7IHR5cGUgQ29tbWl0LCBjb21taXRTY2hlbWEgfSBmcm9tICdAY29kZS1wdXNodXAvbW9kZWxzJztcbmltcG9ydCB7IGlzU2VtdmVyIH0gZnJvbSAnLi4vc2VtdmVyLmpzJztcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdldExhdGVzdENvbW1pdChcbiAgZ2l0ID0gc2ltcGxlR2l0KCksXG4pOiBQcm9taXNlPENvbW1pdCB8IG51bGw+IHtcbiAgY29uc3QgbG9nID0gYXdhaXQgZ2l0LmxvZyh7XG4gICAgbWF4Q291bnQ6IDEsXG4gICAgLy8gZ2l0IGxvZyAtMSAtLXByZXR0eT1mb3JtYXQ6XCIlSCAlcyAlYW4gJWFJXCIgLSBTZWU6IGh0dHBzOi8vZ2l0LXNjbS5jb20vZG9jcy9wcmV0dHktZm9ybWF0c1xuICAgIGZvcm1hdDogeyBoYXNoOiAnJUgnLCBtZXNzYWdlOiAnJXMnLCBhdXRob3I6ICclYW4nLCBkYXRlOiAnJWFJJyB9LFxuICB9KTtcbiAgcmV0dXJuIGNvbW1pdFNjaGVtYS5wYXJzZShsb2cubGF0ZXN0KTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdldEN1cnJlbnRCcmFuY2hPclRhZyhcbiAgZ2l0ID0gc2ltcGxlR2l0KCksXG4pOiBQcm9taXNlPHN0cmluZz4ge1xuICByZXR1cm4gKFxuICAgIChhd2FpdCBnaXQuYnJhbmNoKCkudGhlbihyID0+IHIuY3VycmVudCkpIHx8XG4gICAgLy8gSWYgbm8gY3VycmVudCBicmFuY2gsIHRyeSB0byBnZXQgdGhlIHRhZ1xuICAgIC8vIEBUT0RPIHVzZSBzaW1wbGUgZ2l0XG4gICAgKGF3YWl0IGdpdFxuICAgICAgLnJhdyhbJ2Rlc2NyaWJlJywgJy0tdGFncycsICctLWV4YWN0LW1hdGNoJ10pXG4gICAgICAudGhlbihvdXQgPT4gb3V0LnRyaW0oKSkpXG4gICk7XG59XG5cbmV4cG9ydCB0eXBlIExvZ1Jlc3VsdCA9IHsgaGFzaDogc3RyaW5nOyBtZXNzYWdlOiBzdHJpbmcgfTtcblxuZnVuY3Rpb24gdmFsaWRhdGVGaWx0ZXIoeyBmcm9tLCB0byB9OiBMb2dPcHRpb25zKSB7XG4gIGlmICh0byAmJiAhZnJvbSkge1xuICAgIC8vIHRocm93IG1vcmUgdXNlci1mcmllbmRseSBlcnJvciBpbnN0ZWFkIG9mOlxuICAgIC8vIGZhdGFsOiBhbWJpZ3VvdXMgYXJndW1lbnQgJy4uLmEnOiB1bmtub3duIHJldmlzaW9uIG9yIHBhdGggbm90IGluIHRoZSB3b3JraW5nIHRyZWUuXG4gICAgLy8gVXNlICctLScgdG8gc2VwYXJhdGUgcGF0aHMgZnJvbSByZXZpc2lvbnMsIGxpa2UgdGhpczpcbiAgICAvLyAnZ2l0IDxjb21tYW5kPiBbPHJldmlzaW9uPi4uLl0gLS0gWzxmaWxlPi4uLl0nXG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYGZpbHRlciBuZWVkcyB0aGUgXCJmcm9tXCIgb3B0aW9uIGRlZmluZWQgdG8gYWNjZXB0IHRoZSBcInRvXCIgb3B0aW9uLlxcbmAsXG4gICAgKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZmlsdGVyTG9ncyhcbiAgYWxsVGFnczogc3RyaW5nW10sXG4gIG9wdD86IFBpY2s8TG9nT3B0aW9ucywgJ2Zyb20nIHwgJ3RvJyB8ICdtYXhDb3VudCc+LFxuKSB7XG4gIGlmICghb3B0KSB7XG4gICAgcmV0dXJuIGFsbFRhZ3M7XG4gIH1cbiAgdmFsaWRhdGVGaWx0ZXIob3B0KTtcbiAgY29uc3QgeyBmcm9tLCB0bywgbWF4Q291bnQgfSA9IG9wdDtcbiAgY29uc3QgZmluSW5kZXggPSA8VD4odGFnTmFtZT86IHN0cmluZywgZmFsbGJhY2s/OiBUKSA9PiB7XG4gICAgY29uc3QgaWR4ID0gYWxsVGFncy5pbmRleE9mKHRhZ05hbWUgPz8gJycpO1xuICAgIGlmIChpZHggIT09IC0xKSB7XG4gICAgICByZXR1cm4gaWR4O1xuICAgIH1cbiAgICByZXR1cm4gZmFsbGJhY2s7XG4gIH07XG4gIGNvbnN0IGZyb21JbmRleCA9IGZpbkluZGV4KGZyb20sIDApO1xuICBjb25zdCB0b0luZGV4ID0gZmluSW5kZXgodG8sIHVuZGVmaW5lZCk7XG4gIHJldHVybiBhbGxUYWdzXG4gICAgLnNsaWNlKGZyb21JbmRleCwgdG9JbmRleCA/IHRvSW5kZXggKyAxIDogdG9JbmRleClcbiAgICAuc2xpY2UoMCwgbWF4Q291bnQgPz8gdW5kZWZpbmVkKTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdldEhhc2hGcm9tVGFnKFxuICB0YWc6IHN0cmluZyxcbiAgZ2l0ID0gc2ltcGxlR2l0KCksXG4pOiBQcm9taXNlPExvZ1Jlc3VsdD4ge1xuICBjb25zdCB0YWdEZXRhaWxzID0gYXdhaXQgZ2l0LnNob3coWyctLW5vLXBhdGNoJywgJy0tZm9ybWF0PSVIJywgdGFnXSk7XG4gIGNvbnN0IGhhc2ggPSB0YWdEZXRhaWxzLnRyaW0oKTsgLy8gUmVtb3ZlIHF1b3RlcyBhbmQgdHJpbSB3aGl0ZXNwYWNlXG4gIHJldHVybiB7XG4gICAgaGFzaDogaGFzaC5zcGxpdCgnXFxuJykuYXQoLTEpID8/ICcnLFxuICAgIG1lc3NhZ2U6IHRhZyxcbiAgfTtcbn1cblxuZXhwb3J0IHR5cGUgTG9nT3B0aW9ucyA9IHtcbiAgdGFyZ2V0QnJhbmNoPzogc3RyaW5nO1xuICBmcm9tPzogc3RyaW5nO1xuICB0bz86IHN0cmluZztcbiAgbWF4Q291bnQ/OiBudW1iZXI7XG59O1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0U2VtdmVyVGFncyhcbiAgb3B0OiBMb2dPcHRpb25zID0ge30sXG4gIGdpdCA9IHNpbXBsZUdpdCgpLFxuKTogUHJvbWlzZTxMb2dSZXN1bHRbXT4ge1xuICB2YWxpZGF0ZUZpbHRlcihvcHQpO1xuICBjb25zdCB7IHRhcmdldEJyYW5jaCwgLi4ub3B0aW9ucyB9ID0gb3B0O1xuICAvLyBtYWtlIHN1cmUgd2UgaGF2ZSBhIHRhcmdldCBicmFuY2hcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGZ1bmN0aW9uYWwvbm8tbGV0XG4gIGxldCBjdXJyZW50QnJhbmNoO1xuICBpZiAodGFyZ2V0QnJhbmNoKSB7XG4gICAgY3VycmVudEJyYW5jaCA9IGF3YWl0IGdldEN1cnJlbnRCcmFuY2hPclRhZyhnaXQpO1xuICAgIGF3YWl0IGdpdC5jaGVja291dCh0YXJnZXRCcmFuY2gpO1xuICB9XG5cbiAgLy8gRmV0Y2ggYWxsIHRhZ3MgbWVyZ2VkIGludG8gdGhlIHRhcmdldCBicmFuY2hcbiAgY29uc3QgdGFnc1JhdyA9IGF3YWl0IGdpdC50YWcoW1xuICAgICctLW1lcmdlZCcsXG4gICAgdGFyZ2V0QnJhbmNoID8/IChhd2FpdCBnZXRDdXJyZW50QnJhbmNoT3JUYWcoZ2l0KSksXG4gIF0pO1xuXG4gIGNvbnN0IGFsbFRhZ3MgPSB0YWdzUmF3XG4gICAgLnNwbGl0KC9cXG4vKVxuICAgIC5tYXAodGFnID0+IHRhZy50cmltKCkpXG4gICAgLmZpbHRlcihCb29sZWFuKVxuICAgIC5maWx0ZXIoaXNTZW12ZXIpO1xuXG4gIGNvbnN0IHJlbGV2YW50VGFncyA9IGZpbHRlckxvZ3MoYWxsVGFncywgb3B0aW9ucyk7XG5cbiAgY29uc3QgdGFnc1dpdGhIYXNoZXM6IExvZ1Jlc3VsdFtdID0gYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgcmVsZXZhbnRUYWdzLm1hcCh0YWcgPT4gZ2V0SGFzaEZyb21UYWcodGFnLCBnaXQpKSxcbiAgKTtcblxuICBpZiAoY3VycmVudEJyYW5jaCkge1xuICAgIGF3YWl0IGdpdC5jaGVja291dChjdXJyZW50QnJhbmNoKTtcbiAgfVxuXG4gIHJldHVybiB0YWdzV2l0aEhhc2hlcztcbn1cblxuLyoqXG4gKiBgZ2V0SGFzaGVzYCByZXR1cm5zIGEgbGlzdCBvZiBjb21taXQgaGFzaGVzLiBJbnRlcm5hbGx5IGl0IHVzZXMgYGdpdC5sb2coKWAgdG8gZGV0ZXJtaW5lIHRoZSBjb21taXRzIHdpdGhpbiBhIHJhbmdlLlxuICogVGhlIGFtb3VudCBjYW4gYmUgbGltaXRlZCB0byBhIG1heGltdW0gbnVtYmVyIG9mIGNvbW1pdHMgc3BlY2lmaWVkIGJ5IGBtYXhDb3VudGAuXG4gKiBXaXRoIGBmcm9tYCBhbmQgYHRvYCwgeW91IGNhbiBzcGVjaWZ5IGEgcmFuZ2Ugb2YgY29tbWl0cy5cbiAqXG4gKiAqKk5PVEU6KipcbiAqIEluIEdpdCwgc3BlY2lmeWluZyBhIHJhbmdlIHdpdGggdHdvIGRvdHMgKGBmcm9tLi50b2ApIHNlbGVjdHMgY29tbWl0cyB0aGF0IGFyZSByZWFjaGFibGUgZnJvbSBgdG9gIGJ1dCBub3QgZnJvbSBgZnJvbWAuXG4gKiBFc3NlbnRpYWxseSwgaXQgc2hvd3MgdGhlIGNvbW1pdHMgdGhhdCBhcmUgaW4gYHRvYCBidXQgbm90IGluIGBmcm9tYCwgZXhjbHVkaW5nIHRoZSBjb21taXRzIHVuaXF1ZSB0byBgZnJvbWAuXG4gKlxuICogRXhhbXBsZTpcbiAqXG4gKiBMZXQncyBjb25zaWRlciB0aGUgZm9sbG93aW5nIGNvbW1pdCBoaXN0b3J5OlxuICpcbiAqICAgQS0tLUItLS1DLS0tRC0tLUUgKG1haW4pXG4gKlxuICogVXNpbmcgYGdpdCBsb2cgQi4uRGAsIHlvdSB3b3VsZCBnZXQgdGhlIGNvbW1pdHMgQyBhbmQgRDpcbiAqXG4gKiAgIEMtLS1EXG4gKlxuICogVGhpcyBpcyBiZWNhdXNlIHRoZXNlIGNvbW1pdHMgYXJlIHJlYWNoYWJsZSBmcm9tIEQgYnV0IG5vdCBmcm9tIEIuXG4gKlxuICogQVNDSUkgUmVwcmVzZW50YXRpb246XG4gKlxuICogICBNYWluIEJyYW5jaDogICAgQS0tLUItLS1DLS0tRC0tLUVcbiAqICAgICAgICAgICAgICAgICAgICAgICBcXCAgICAgICBcXFxuICogICAgICAgICAgICAgICAgICAgICAgICBcXCAgICAgICArLS0tIENvbW1pdHMgaW5jbHVkZWQgaW4gYGdpdCBsb2cgQi4uRGBcbiAqICAgICAgICAgICAgICAgICAgICAgICAgIFxcXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgKy0tLSBFeGNsdWRlZCBieSB0aGUgYGZyb21gIHBhcmFtZXRlclxuICpcbiAqIFdpdGggYHNpbXBsZS1naXRgLCB3aGVuIHlvdSBzcGVjaWZ5IGEgYGZyb21gIGFuZCBgdG9gIHJhbmdlIGxpa2UgdGhpczpcbiAqXG4gKiAgIGdpdC5sb2coeyBmcm9tOiAnQicsIHRvOiAnRCcgfSk7XG4gKlxuICogSXQgaW50ZXJwcmV0cyBpdCBzaW1pbGFybHksIHNlbGVjdGluZyBjb21taXRzIGJldHdlZW4gQiBhbmQgRCwgaW5jbHVzaXZlIG9mIEQgYnV0IGV4Y2x1c2l2ZSBvZiBCLlxuICogRm9yIGBnaXQubG9nKHsgZnJvbTogJ0InLCB0bzogJ0QnIH0pYCBvciBgZ2l0IGxvZyBCLi5EYCwgY29tbWl0cyBDIGFuZCBEIGFyZSBzZWxlY3RlZC5cbiAqXG4gKiBAcGFyYW0gb3B0aW9ucyBPYmplY3QgY29udGFpbmluZyBgZnJvbWAsIGB0b2AsIGFuZCBvcHRpb25hbGx5IGBtYXhDb3VudGAgdG8gc3BlY2lmeSB0aGUgY29tbWl0IHJhbmdlIGFuZCBsaW1pdC5cbiAqIEBwYXJhbSBnaXQgVGhlIGBzaW1wbGUtZ2l0YCBpbnN0YW5jZSB1c2VkIHRvIGV4ZWN1dGUgR2l0IGNvbW1hbmRzLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0SGFzaGVzKFxuICBvcHRpb25zOiBTaW1wbGVHaXRMb2dPcHRpb25zICYgUGljazxMb2dPcHRpb25zLCAndGFyZ2V0QnJhbmNoJz4gPSB7fSxcbiAgZ2l0ID0gc2ltcGxlR2l0KCksXG4pOiBQcm9taXNlPExvZ1Jlc3VsdFtdPiB7XG4gIGNvbnN0IHsgdGFyZ2V0QnJhbmNoLCBmcm9tLCB0bywgbWF4Q291bnQsIC4uLm9wdCB9ID0gb3B0aW9ucztcblxuICB2YWxpZGF0ZUZpbHRlcih7IGZyb20sIHRvIH0pO1xuXG4gIC8vIEVuc3VyZSB5b3UgYXJlIG9uIHRoZSBjb3JyZWN0IGJyYW5jaFxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZnVuY3Rpb25hbC9uby1sZXRcbiAgbGV0IGN1cnJlbnRCcmFuY2g7XG4gIGlmICh0YXJnZXRCcmFuY2gpIHtcbiAgICBjdXJyZW50QnJhbmNoID0gYXdhaXQgZ2V0Q3VycmVudEJyYW5jaE9yVGFnKGdpdCk7XG4gICAgYXdhaXQgZ2l0LmNoZWNrb3V0KHRhcmdldEJyYW5jaCk7XG4gIH1cblxuICBjb25zdCBsb2dzID0gYXdhaXQgZ2l0LmxvZyh7XG4gICAgLi4ub3B0LFxuICAgIGZvcm1hdDoge1xuICAgICAgaGFzaDogJyVIJyxcbiAgICAgIG1lc3NhZ2U6ICclcycsXG4gICAgfSxcbiAgICBmcm9tLFxuICAgIHRvLFxuICAgIG1heENvdW50LFxuICB9KTtcblxuICAvLyBFbnN1cmUgeW91IGFyZSBiYWNrIHRvIHRoZSBpbml0aWFsIGJyYW5jaFxuICBpZiAodGFyZ2V0QnJhbmNoKSB7XG4gICAgYXdhaXQgZ2l0LmNoZWNrb3V0KGN1cnJlbnRCcmFuY2ggYXMgc3RyaW5nKTtcbiAgfVxuXG4gIHJldHVybiBbLi4ubG9ncy5hbGxdO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9zZW12ZXIudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYlwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3NlbXZlci50c1wiO2ltcG9ydCB7IHJjb21wYXJlLCB2YWxpZCB9IGZyb20gJ3NlbXZlcic7XG5cbmV4cG9ydCBmdW5jdGlvbiBub3JtYWxpemVTZW12ZXIoc2VtdmVyU3RyaW5nOiBzdHJpbmcpOiBzdHJpbmcge1xuICBpZiAoc2VtdmVyU3RyaW5nLnN0YXJ0c1dpdGgoJ3YnKSB8fCBzZW12ZXJTdHJpbmcuc3RhcnRzV2l0aCgnVicpKSB7XG4gICAgcmV0dXJuIHNlbXZlclN0cmluZy5zbGljZSgxKTtcbiAgfVxuXG4gIGlmIChzZW12ZXJTdHJpbmcuaW5jbHVkZXMoJ0AnKSkge1xuICAgIHJldHVybiBzZW12ZXJTdHJpbmcuc3BsaXQoJ0AnKS5hdCgtMSkgPz8gJyc7XG4gIH1cblxuICByZXR1cm4gc2VtdmVyU3RyaW5nO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gaXNTZW12ZXIoc2VtdmVyU3RyaW5nID0gJycpOiBib29sZWFuIHtcbiAgcmV0dXJuIHZhbGlkKG5vcm1hbGl6ZVNlbXZlcihzZW12ZXJTdHJpbmcpKSAhPSBudWxsO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gc29ydFNlbXZlcnMoc2VtdmVyU3RyaW5nczogc3RyaW5nW10pOiBzdHJpbmdbXSB7XG4gIHJldHVybiBzZW12ZXJTdHJpbmdzLm1hcChub3JtYWxpemVTZW12ZXIpLmZpbHRlcihpc1NlbXZlcikuc29ydChyY29tcGFyZSk7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL2dpdC9naXQudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9naXRcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9naXQvZ2l0LnRzXCI7aW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB7IHR5cGUgU3RhdHVzUmVzdWx0LCBzaW1wbGVHaXQgfSBmcm9tICdzaW1wbGUtZ2l0JztcbmltcG9ydCB7IHVpIH0gZnJvbSAnLi4vbG9nZ2luZy5qcyc7XG5pbXBvcnQgeyB0b1VuaXhQYXRoIH0gZnJvbSAnLi4vdHJhbnNmb3JtLmpzJztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEdpdFJvb3QoZ2l0ID0gc2ltcGxlR2l0KCkpOiBQcm9taXNlPHN0cmluZz4ge1xuICByZXR1cm4gZ2l0LnJldnBhcnNlKCctLXNob3ctdG9wbGV2ZWwnKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdEdpdFBhdGgoZmlsZVBhdGg6IHN0cmluZywgZ2l0Um9vdDogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgYWJzb2x1dGVQYXRoID0gcGF0aC5pc0Fic29sdXRlKGZpbGVQYXRoKVxuICAgID8gZmlsZVBhdGhcbiAgICA6IHBhdGguam9pbihwcm9jZXNzLmN3ZCgpLCBmaWxlUGF0aCk7XG4gIGNvbnN0IHJlbGF0aXZlUGF0aCA9IHBhdGgucmVsYXRpdmUoZ2l0Um9vdCwgYWJzb2x1dGVQYXRoKTtcbiAgcmV0dXJuIHRvVW5peFBhdGgocmVsYXRpdmVQYXRoKTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHRvR2l0UGF0aChcbiAgZmlsZVBhdGg6IHN0cmluZyxcbiAgZ2l0ID0gc2ltcGxlR2l0KCksXG4pOiBQcm9taXNlPHN0cmluZz4ge1xuICBjb25zdCBnaXRSb290ID0gYXdhaXQgZ2V0R2l0Um9vdChnaXQpO1xuICByZXR1cm4gZm9ybWF0R2l0UGF0aChmaWxlUGF0aCwgZ2l0Um9vdCk7XG59XG5cbmV4cG9ydCBjbGFzcyBHaXRTdGF0dXNFcnJvciBleHRlbmRzIEVycm9yIHtcbiAgc3RhdGljIGlnbm9yZWRQcm9wcyA9IG5ldyBTZXQoWydjdXJyZW50JywgJ3RyYWNraW5nJ10pO1xuXG4gIHN0YXRpYyBnZXRSZWR1Y2VkU3RhdHVzKHN0YXR1czogU3RhdHVzUmVzdWx0KSB7XG4gICAgcmV0dXJuIE9iamVjdC5mcm9tRW50cmllcyhcbiAgICAgIE9iamVjdC5lbnRyaWVzKHN0YXR1cylcbiAgICAgICAgLmZpbHRlcigoW2tleV0pID0+ICF0aGlzLmlnbm9yZWRQcm9wcy5oYXMoa2V5KSlcbiAgICAgICAgLmZpbHRlcihcbiAgICAgICAgICAoXG4gICAgICAgICAgICBlbnRyeTogW1xuICAgICAgICAgICAgICBzdHJpbmcsXG4gICAgICAgICAgICAgIG51bWJlciB8IHN0cmluZyB8IGJvb2xlYW4gfCBudWxsIHwgdW5kZWZpbmVkIHwgdW5rbm93bltdLFxuICAgICAgICAgICAgXSxcbiAgICAgICAgICApID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHZhbHVlID0gZW50cnlbMV07XG4gICAgICAgICAgICBpZiAodmFsdWUgPT0gbnVsbCkge1xuICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoQXJyYXkuaXNBcnJheSh2YWx1ZSkgJiYgdmFsdWUubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdudW1iZXInICYmIHZhbHVlID09PSAwKSB7XG4gICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiAhKHR5cGVvZiB2YWx1ZSA9PT0gJ2Jvb2xlYW4nICYmICF2YWx1ZSk7XG4gICAgICAgICAgfSxcbiAgICAgICAgKSxcbiAgICApO1xuICB9XG5cbiAgY29uc3RydWN0b3Ioc3RhdHVzOiBTdGF0dXNSZXN1bHQpIHtcbiAgICBzdXBlcihcbiAgICAgIGBXb3JraW5nIGRpcmVjdG9yeSBuZWVkcyB0byBiZSBjbGVhbiBiZWZvcmUgd2UgeW91IGNhbiBwcm9jZWVkLiBDb21taXQgeW91ciBsb2NhbCBjaGFuZ2VzIG9yIHN0YXNoIHRoZW06IFxcbiAke0pTT04uc3RyaW5naWZ5KFxuICAgICAgICBHaXRTdGF0dXNFcnJvci5nZXRSZWR1Y2VkU3RhdHVzKHN0YXR1cyksXG4gICAgICAgIG51bGwsXG4gICAgICAgIDIsXG4gICAgICApfWAsXG4gICAgKTtcbiAgfVxufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ3VhcmRBZ2FpbnN0TG9jYWxDaGFuZ2VzKFxuICBnaXQgPSBzaW1wbGVHaXQoKSxcbik6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBzdGF0dXMgPSBhd2FpdCBnaXQuc3RhdHVzKFsnLXMnXSk7XG4gIGlmIChzdGF0dXMuZmlsZXMubGVuZ3RoID4gMCkge1xuICAgIHRocm93IG5ldyBHaXRTdGF0dXNFcnJvcihzdGF0dXMpO1xuICB9XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzYWZlQ2hlY2tvdXQoXG4gIGJyYW5jaE9ySGFzaDogc3RyaW5nLFxuICBmb3JjZUNsZWFuU3RhdHVzID0gZmFsc2UsXG4gIGdpdCA9IHNpbXBsZUdpdCgpLFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIC8vIGdpdCByZXF1aXJlcyBhIGNsZWFuIGhpc3RvcnkgdG8gY2hlY2sgb3V0IGEgYnJhbmNoXG4gIGlmIChmb3JjZUNsZWFuU3RhdHVzKSB7XG4gICAgYXdhaXQgZ2l0LnJhdyhbJ3Jlc2V0JywgJy0taGFyZCddKTtcbiAgICBhd2FpdCBnaXQuY2xlYW4oWydmJywgJ2QnXSk7XG4gICAgdWkoKS5sb2dnZXIuaW5mbyhgZ2l0IHN0YXR1cyBjbGVhbmVkYCk7XG4gIH1cbiAgYXdhaXQgZ3VhcmRBZ2FpbnN0TG9jYWxDaGFuZ2VzKGdpdCk7XG4gIGF3YWl0IGdpdC5jaGVja291dChicmFuY2hPckhhc2gpO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi90cmFuc2Zvcm0udHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYlwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3RyYW5zZm9ybS50c1wiO2ltcG9ydCB7IHBsYXRmb3JtIH0gZnJvbSAnbm9kZTpvcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiB0b0FycmF5PFQ+KHZhbDogVCB8IFRbXSk6IFRbXSB7XG4gIHJldHVybiBBcnJheS5pc0FycmF5KHZhbCkgPyB2YWwgOiBbdmFsXTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG9iamVjdFRvS2V5czxUIGV4dGVuZHMgb2JqZWN0PihvYmo6IFQpIHtcbiAgcmV0dXJuIE9iamVjdC5rZXlzKG9iaikgYXMgKGtleW9mIFQpW107XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBvYmplY3RUb0VudHJpZXM8VCBleHRlbmRzIG9iamVjdD4ob2JqOiBUKSB7XG4gIHJldHVybiBPYmplY3QuZW50cmllcyhvYmopIGFzIFtrZXlvZiBULCBUW2tleW9mIFRdXVtdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gb2JqZWN0RnJvbUVudHJpZXM8SyBleHRlbmRzIFByb3BlcnR5S2V5LCBWPihlbnRyaWVzOiBbSywgVl1bXSkge1xuICByZXR1cm4gT2JqZWN0LmZyb21FbnRyaWVzKGVudHJpZXMpIGFzIFJlY29yZDxLLCBWPjtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvdW50T2NjdXJyZW5jZXM8VCBleHRlbmRzIFByb3BlcnR5S2V5PihcbiAgdmFsdWVzOiBUW10sXG4pOiBQYXJ0aWFsPFJlY29yZDxULCBudW1iZXI+PiB7XG4gIHJldHVybiB2YWx1ZXMucmVkdWNlPFBhcnRpYWw8UmVjb3JkPFQsIG51bWJlcj4+PihcbiAgICAoYWNjLCB2YWx1ZSkgPT4gKHsgLi4uYWNjLCBbdmFsdWVdOiAoYWNjW3ZhbHVlXSA/PyAwKSArIDEgfSksXG4gICAge30sXG4gICk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBkaXN0aW5jdDxUIGV4dGVuZHMgc3RyaW5nIHwgbnVtYmVyIHwgYm9vbGVhbj4oYXJyYXk6IFRbXSk6IFRbXSB7XG4gIHJldHVybiBbLi4ubmV3IFNldChhcnJheSldO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZGVlcENsb25lPFQ+KG9iajogVCk6IFQge1xuICByZXR1cm4gb2JqID09IG51bGwgfHwgdHlwZW9mIG9iaiAhPT0gJ29iamVjdCcgPyBvYmogOiBzdHJ1Y3R1cmVkQ2xvbmUob2JqKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZhY3Rvck9mPFQ+KGl0ZW1zOiBUW10sIGZpbHRlckZuOiAoaTogVCkgPT4gYm9vbGVhbik6IG51bWJlciB7XG4gIGNvbnN0IGl0ZW1Db3VudCA9IGl0ZW1zLmxlbmd0aDtcbiAgLy8gZWFybHkgZXhpdCBmb3IgZW1wdHkgcm93c1xuICBpZiAoIWl0ZW1Db3VudCkge1xuICAgIHJldHVybiAxO1xuICB9XG4gIGNvbnN0IGZpbHRlckNvdW50ID0gaXRlbXMuZmlsdGVyKGZpbHRlckZuKS5sZW5ndGg7XG4gIC8vIGlmIG5vIHJvd3MgcmVzdWx0IGZyb20gdGhlIGZpbHRlciBmbiB3ZSBmb3J3YXJkIHJldHVybiAxIGFzIGZhY3RvclxuICByZXR1cm4gZmlsdGVyQ291bnQgPT09IDAgPyAxIDogKGl0ZW1Db3VudCAtIGZpbHRlckNvdW50KSAvIGl0ZW1Db3VudDtcbn1cblxudHlwZSBBcmd1bWVudFZhbHVlID0gbnVtYmVyIHwgc3RyaW5nIHwgYm9vbGVhbiB8IHN0cmluZ1tdO1xuZXhwb3J0IHR5cGUgQ2xpQXJnc09iamVjdDxUIGV4dGVuZHMgb2JqZWN0ID0gUmVjb3JkPHN0cmluZywgQXJndW1lbnRWYWx1ZT4+ID1cbiAgVCBleHRlbmRzIG5ldmVyXG4gICAgPyBSZWNvcmQ8c3RyaW5nLCBBcmd1bWVudFZhbHVlIHwgdW5kZWZpbmVkPiB8IHsgXzogc3RyaW5nIH1cbiAgICA6IFQ7XG5cbi8qKlxuICogQ29udmVydHMgYW4gb2JqZWN0IHdpdGggZGlmZmVyZW50IHR5cGVzIG9mIHZhbHVlcyBpbnRvIGFuIGFycmF5IG9mIGNvbW1hbmQtbGluZSBhcmd1bWVudHMuXG4gKlxuICogQGV4YW1wbGVcbiAqIGNvbnN0IGFyZ3MgPSBvYmplY3RUb1Byb2Nlc3NBcmdzKHtcbiAqICAgXzogWydub2RlJywgJ2luZGV4LmpzJ10sIC8vIG5vZGUgaW5kZXguanNcbiAqICAgbmFtZTogJ0p1YW5pdGEnLCAvLyAtLW5hbWU9SnVhbml0YVxuICogICBmb3JtYXRzOiBbJ2pzb24nLCAnbWQnXSAvLyAtLWZvcm1hdD1qc29uIC0tZm9ybWF0PW1kXG4gKiB9KTtcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG9iamVjdFRvQ2xpQXJnczxcbiAgVCBleHRlbmRzIG9iamVjdCA9IFJlY29yZDxzdHJpbmcsIEFyZ3VtZW50VmFsdWU+LFxuPihwYXJhbXM/OiBDbGlBcmdzT2JqZWN0PFQ+KTogc3RyaW5nW10ge1xuICBpZiAoIXBhcmFtcykge1xuICAgIHJldHVybiBbXTtcbiAgfVxuXG4gIHJldHVybiBPYmplY3QuZW50cmllcyhwYXJhbXMpLmZsYXRNYXAoKFtrZXksIHZhbHVlXSkgPT4ge1xuICAgIC8vIHByb2Nlc3MvZmlsZS9zY3JpcHRcbiAgICBpZiAoa2V5ID09PSAnXycpIHtcbiAgICAgIHJldHVybiBBcnJheS5pc0FycmF5KHZhbHVlKSA/IHZhbHVlIDogW2Ake3ZhbHVlfWBdO1xuICAgIH1cbiAgICBjb25zdCBwcmVmaXggPSBrZXkubGVuZ3RoID09PSAxID8gJy0nIDogJy0tJztcbiAgICAvLyBcIi0qXCIgYXJndW1lbnRzIChzaG9ydGhhbmRzKVxuICAgIGlmIChBcnJheS5pc0FycmF5KHZhbHVlKSkge1xuICAgICAgcmV0dXJuIHZhbHVlLm1hcCh2ID0+IGAke3ByZWZpeH0ke2tleX09XCIke3Z9XCJgKTtcbiAgICB9XG4gICAgLy8gXCItLSpcIiBhcmd1bWVudHMgPT09PT09PT09PVxuXG4gICAgaWYgKEFycmF5LmlzQXJyYXkodmFsdWUpKSB7XG4gICAgICByZXR1cm4gdmFsdWUubWFwKHYgPT4gYCR7cHJlZml4fSR7a2V5fT1cIiR7dn1cImApO1xuICAgIH1cblxuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdvYmplY3QnKSB7XG4gICAgICByZXR1cm4gT2JqZWN0LmVudHJpZXModmFsdWUgYXMgUmVjb3JkPHN0cmluZywgQXJndW1lbnRWYWx1ZT4pLmZsYXRNYXAoXG4gICAgICAgIC8vIHRyYW5zZm9ybSBuZXN0ZWQgb2JqZWN0cyB0byB0aGUgZG90IG5vdGF0aW9uIGBrZXkuc3Via2V5YFxuICAgICAgICAoW2ssIHZdKSA9PiBvYmplY3RUb0NsaUFyZ3MoeyBbYCR7a2V5fS4ke2t9YF06IHYgfSksXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnKSB7XG4gICAgICByZXR1cm4gW2Ake3ByZWZpeH0ke2tleX09XCIke3ZhbHVlfVwiYF07XG4gICAgfVxuXG4gICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ251bWJlcicpIHtcbiAgICAgIHJldHVybiBbYCR7cHJlZml4fSR7a2V5fT0ke3ZhbHVlfWBdO1xuICAgIH1cblxuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdib29sZWFuJykge1xuICAgICAgcmV0dXJuIFtgJHtwcmVmaXh9JHt2YWx1ZSA/ICcnIDogJ25vLSd9JHtrZXl9YF07XG4gICAgfVxuXG4gICAgdGhyb3cgbmV3IEVycm9yKGBVbnN1cHBvcnRlZCB0eXBlICR7dHlwZW9mIHZhbHVlfSBmb3Iga2V5ICR7a2V5fWApO1xuICB9KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHRvVW5peFBhdGgocGF0aDogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIHBhdGgucmVwbGFjZSgvXFxcXC9nLCAnLycpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdG9Vbml4TmV3bGluZXModGV4dDogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIHBsYXRmb3JtKCkgPT09ICd3aW4zMicgPyB0ZXh0LnJlcGxhY2UoL1xcclxcbi9nLCAnXFxuJykgOiB0ZXh0O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZnJvbUpzb25MaW5lczxUID0gdW5rbm93bj4oanNvbkxpbmVzOiBzdHJpbmcpIHtcbiAgY29uc3QgdW5pZmllZE5ld0xpbmVzID0gdG9Vbml4TmV3bGluZXMoanNvbkxpbmVzKS50cmltKCk7XG4gIHJldHVybiBKU09OLnBhcnNlKGBbJHt1bmlmaWVkTmV3TGluZXMuc3BsaXQoJ1xcbicpLmpvaW4oJywnKX1dYCkgYXMgVDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHRvSnNvbkxpbmVzPFQ+KGpzb246IFRbXSkge1xuICByZXR1cm4ganNvbi5tYXAoaXRlbSA9PiBKU09OLnN0cmluZ2lmeShpdGVtKSkuam9pbignXFxuJyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjYXBpdGFsaXplPFQgZXh0ZW5kcyBzdHJpbmc+KHRleHQ6IFQpOiBDYXBpdGFsaXplPFQ+IHtcbiAgcmV0dXJuIGAke3RleHQuY2hhckF0KDApLnRvTG9jYWxlVXBwZXJDYXNlKCl9JHt0ZXh0LnNsaWNlKFxuICAgIDEsXG4gICl9YCBhcyBDYXBpdGFsaXplPFQ+O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdG9OdW1iZXJQcmVjaXNpb24oXG4gIHZhbHVlOiBudW1iZXIsXG4gIGRlY2ltYWxQbGFjZXM6IG51bWJlcixcbik6IG51bWJlciB7XG4gIHJldHVybiBOdW1iZXIoXG4gICAgYCR7TWF0aC5yb3VuZChcbiAgICAgIE51bWJlci5wYXJzZUZsb2F0KGAke3ZhbHVlfWUke2RlY2ltYWxQbGFjZXN9YCksXG4gICAgKX1lLSR7ZGVjaW1hbFBsYWNlc31gLFxuICApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdG9PcmRpbmFsKHZhbHVlOiBudW1iZXIpOiBzdHJpbmcge1xuICAvKiBlc2xpbnQtZGlzYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tbWFnaWMtbnVtYmVycyAqL1xuICBpZiAodmFsdWUgJSAxMCA9PT0gMSAmJiB2YWx1ZSAlIDEwMCAhPT0gMTEpIHtcbiAgICByZXR1cm4gYCR7dmFsdWV9c3RgO1xuICB9XG5cbiAgaWYgKHZhbHVlICUgMTAgPT09IDIgJiYgdmFsdWUgJSAxMDAgIT09IDEyKSB7XG4gICAgcmV0dXJuIGAke3ZhbHVlfW5kYDtcbiAgfVxuXG4gIGlmICh2YWx1ZSAlIDEwID09PSAzICYmIHZhbHVlICUgMTAwICE9PSAxMykge1xuICAgIHJldHVybiBgJHt2YWx1ZX1yZGA7XG4gIH1cbiAgLyogZXNsaW50LWVuYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tbWFnaWMtbnVtYmVycyAqL1xuXG4gIHJldHVybiBgJHt2YWx1ZX10aGA7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL21lcmdlLWNvbmZpZ3MudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYlwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL21lcmdlLWNvbmZpZ3MudHNcIjtpbXBvcnQgdHlwZSB7XG4gIENhdGVnb3J5Q29uZmlnLFxuICBDb3JlQ29uZmlnLFxuICBQZXJzaXN0Q29uZmlnLFxuICBQbHVnaW5Db25maWcsXG4gIFVwbG9hZENvbmZpZyxcbn0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBtZXJnZUNvbmZpZ3MoXG4gIGNvbmZpZzogQ29yZUNvbmZpZyxcbiAgLi4uY29uZmlnczogUGFydGlhbDxDb3JlQ29uZmlnPltdXG4pOiBQYXJ0aWFsPENvcmVDb25maWc+IHtcbiAgcmV0dXJuIGNvbmZpZ3MucmVkdWNlKFxuICAgIChhY2MsIG9iaikgPT4gKHtcbiAgICAgIC4uLmFjYyxcbiAgICAgIC4uLm1lcmdlQ2F0ZWdvcmllcyhhY2MuY2F0ZWdvcmllcywgb2JqLmNhdGVnb3JpZXMpLFxuICAgICAgLi4ubWVyZ2VQbHVnaW5zKGFjYy5wbHVnaW5zLCBvYmoucGx1Z2lucyksXG4gICAgICAuLi5tZXJnZVBlcnNpc3QoYWNjLnBlcnNpc3QsIG9iai5wZXJzaXN0KSxcbiAgICAgIC4uLm1lcmdlVXBsb2FkKGFjYy51cGxvYWQsIG9iai51cGxvYWQpLFxuICAgIH0pLFxuICAgIGNvbmZpZyxcbiAgKTtcbn1cblxuZnVuY3Rpb24gbWVyZ2VDYXRlZ29yaWVzKFxuICBhOiBDYXRlZ29yeUNvbmZpZ1tdIHwgdW5kZWZpbmVkLFxuICBiOiBDYXRlZ29yeUNvbmZpZ1tdIHwgdW5kZWZpbmVkLFxuKTogUGljazxDb3JlQ29uZmlnLCAnY2F0ZWdvcmllcyc+IHtcbiAgaWYgKCFhICYmICFiKSB7XG4gICAgcmV0dXJuIHt9O1xuICB9XG5cbiAgY29uc3QgbWVyZ2VkTWFwID0gbmV3IE1hcDxzdHJpbmcsIENhdGVnb3J5Q29uZmlnPigpO1xuXG4gIGNvbnN0IGFkZFRvTWFwID0gKGNhdGVnb3JpZXM6IENhdGVnb3J5Q29uZmlnW10pID0+IHtcbiAgICBjYXRlZ29yaWVzLmZvckVhY2gobmV3T2JqZWN0ID0+IHtcbiAgICAgIGlmIChtZXJnZWRNYXAuaGFzKG5ld09iamVjdC5zbHVnKSkge1xuICAgICAgICBjb25zdCBleGlzdGluZ09iamVjdDogQ2F0ZWdvcnlDb25maWcgfCB1bmRlZmluZWQgPSBtZXJnZWRNYXAuZ2V0KFxuICAgICAgICAgIG5ld09iamVjdC5zbHVnLFxuICAgICAgICApO1xuXG4gICAgICAgIG1lcmdlZE1hcC5zZXQobmV3T2JqZWN0LnNsdWcsIHtcbiAgICAgICAgICAuLi5leGlzdGluZ09iamVjdCxcbiAgICAgICAgICAuLi5uZXdPYmplY3QsXG5cbiAgICAgICAgICByZWZzOiBtZXJnZUJ5VW5pcXVlQ2F0ZWdvcnlSZWZDb21iaW5hdGlvbihcbiAgICAgICAgICAgIGV4aXN0aW5nT2JqZWN0Py5yZWZzLFxuICAgICAgICAgICAgbmV3T2JqZWN0LnJlZnMsXG4gICAgICAgICAgKSxcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBtZXJnZWRNYXAuc2V0KG5ld09iamVjdC5zbHVnLCBuZXdPYmplY3QpO1xuICAgICAgfVxuICAgIH0pO1xuICB9O1xuXG4gIGlmIChhKSB7XG4gICAgYWRkVG9NYXAoYSk7XG4gIH1cbiAgaWYgKGIpIHtcbiAgICBhZGRUb01hcChiKTtcbiAgfVxuXG4gIC8vIENvbnZlcnQgdGhlIG1hcCBiYWNrIHRvIGFuIGFycmF5XG4gIHJldHVybiB7IGNhdGVnb3JpZXM6IFsuLi5tZXJnZWRNYXAudmFsdWVzKCldIH07XG59XG5cbmZ1bmN0aW9uIG1lcmdlUGx1Z2lucyhcbiAgYTogUGx1Z2luQ29uZmlnW10gfCB1bmRlZmluZWQsXG4gIGI6IFBsdWdpbkNvbmZpZ1tdIHwgdW5kZWZpbmVkLFxuKTogUGljazxDb3JlQ29uZmlnLCAncGx1Z2lucyc+IHtcbiAgaWYgKCFhICYmICFiKSB7XG4gICAgcmV0dXJuIHsgcGx1Z2luczogW10gfTtcbiAgfVxuXG4gIGNvbnN0IG1lcmdlZE1hcCA9IG5ldyBNYXA8c3RyaW5nLCBQbHVnaW5Db25maWc+KCk7XG5cbiAgY29uc3QgYWRkVG9NYXAgPSAocGx1Z2luczogUGx1Z2luQ29uZmlnW10pID0+IHtcbiAgICBwbHVnaW5zLmZvckVhY2gobmV3T2JqZWN0ID0+IHtcbiAgICAgIG1lcmdlZE1hcC5zZXQobmV3T2JqZWN0LnNsdWcsIG5ld09iamVjdCk7XG4gICAgfSk7XG4gIH07XG5cbiAgaWYgKGEpIHtcbiAgICBhZGRUb01hcChhKTtcbiAgfVxuICBpZiAoYikge1xuICAgIGFkZFRvTWFwKGIpO1xuICB9XG5cbiAgcmV0dXJuIHsgcGx1Z2luczogWy4uLm1lcmdlZE1hcC52YWx1ZXMoKV0gfTtcbn1cblxuZnVuY3Rpb24gbWVyZ2VQZXJzaXN0KFxuICBhOiBQZXJzaXN0Q29uZmlnIHwgdW5kZWZpbmVkLFxuICBiOiBQZXJzaXN0Q29uZmlnIHwgdW5kZWZpbmVkLFxuKTogUGljazxDb3JlQ29uZmlnLCAncGVyc2lzdCc+IHtcbiAgaWYgKCFhICYmICFiKSB7XG4gICAgcmV0dXJuIHt9O1xuICB9XG5cbiAgaWYgKGEpIHtcbiAgICByZXR1cm4gYiA/IHsgcGVyc2lzdDogeyAuLi5hLCAuLi5iIH0gfSA6IHt9O1xuICB9IGVsc2Uge1xuICAgIHJldHVybiB7IHBlcnNpc3Q6IGIgfTtcbiAgfVxufVxuXG5mdW5jdGlvbiBtZXJnZUJ5VW5pcXVlQ2F0ZWdvcnlSZWZDb21iaW5hdGlvbjxcbiAgVCBleHRlbmRzIHsgc2x1Zzogc3RyaW5nOyB0eXBlOiBzdHJpbmc7IHBsdWdpbjogc3RyaW5nIH0sXG4+KGE6IFRbXSB8IHVuZGVmaW5lZCwgYjogVFtdIHwgdW5kZWZpbmVkKSB7XG4gIGNvbnN0IG1hcCA9IG5ldyBNYXA8c3RyaW5nLCBUPigpO1xuXG4gIGNvbnN0IGFkZFRvTWFwID0gKHJlZnM6IFRbXSkgPT4ge1xuICAgIHJlZnMuZm9yRWFjaChyZWYgPT4ge1xuICAgICAgY29uc3QgdW5pcXVlSWRlbnRpZmljYXRpb24gPSBgJHtyZWYudHlwZX06JHtyZWYucGx1Z2lufToke3JlZi5zbHVnfWA7XG4gICAgICBpZiAobWFwLmhhcyh1bmlxdWVJZGVudGlmaWNhdGlvbikpIHtcbiAgICAgICAgbWFwLnNldCh1bmlxdWVJZGVudGlmaWNhdGlvbiwge1xuICAgICAgICAgIC4uLm1hcC5nZXQodW5pcXVlSWRlbnRpZmljYXRpb24pLFxuICAgICAgICAgIC4uLnJlZixcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBtYXAuc2V0KHVuaXF1ZUlkZW50aWZpY2F0aW9uLCByZWYpO1xuICAgICAgfVxuICAgIH0pO1xuICB9O1xuXG4gIC8vIEFkZCBvYmplY3RzIGZyb20gYm90aCBhcnJheXMgdG8gdGhlIG1hcFxuICBpZiAoYSkge1xuICAgIGFkZFRvTWFwKGEpO1xuICB9XG4gIGlmIChiKSB7XG4gICAgYWRkVG9NYXAoYik7XG4gIH1cblxuICByZXR1cm4gWy4uLm1hcC52YWx1ZXMoKV07XG59XG5cbmZ1bmN0aW9uIG1lcmdlVXBsb2FkKFxuICBhOiBVcGxvYWRDb25maWcgfCB1bmRlZmluZWQsXG4gIGI6IFVwbG9hZENvbmZpZyB8IHVuZGVmaW5lZCxcbik6IFBpY2s8Q29yZUNvbmZpZywgJ3VwbG9hZCc+IHtcbiAgaWYgKCFhICYmICFiKSB7XG4gICAgcmV0dXJuIHt9O1xuICB9XG5cbiAgaWYgKGEpIHtcbiAgICByZXR1cm4gYiA/IHsgdXBsb2FkOiB7IC4uLmEsIC4uLmIgfSB9IDoge307XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIHsgdXBsb2FkOiBiIH07XG4gIH1cbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcHJvZ3Jlc3MudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYlwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3Byb2dyZXNzLnRzXCI7aW1wb3J0IHsgYmxhY2ssIGJvbGQsIGdyYXksIGdyZWVuIH0gZnJvbSAnYW5zaXMnO1xuaW1wb3J0IHsgdHlwZSBDdG9yT3B0aW9ucywgTXVsdGlQcm9ncmVzc0JhcnMgfSBmcm9tICdtdWx0aS1wcm9ncmVzcy1iYXJzJztcbmltcG9ydCB7IFRFUk1JTkFMX1dJRFRIIH0gZnJvbSAnLi9yZXBvcnRzL2NvbnN0YW50cy5qcyc7XG5cbnR5cGUgQmFyU3R5bGVzID0gJ2FjdGl2ZScgfCAnZG9uZScgfCAnaWRsZSc7XG50eXBlIFN0YXR1c1N0eWxlcyA9IFJlY29yZDxCYXJTdHlsZXMsIChzOiBzdHJpbmcpID0+IHN0cmluZz47XG5leHBvcnQgY29uc3QgYmFyU3R5bGVzOiBTdGF0dXNTdHlsZXMgPSB7XG4gIGFjdGl2ZTogKHM6IHN0cmluZykgPT4gZ3JlZW4ocyksXG4gIGRvbmU6IChzOiBzdHJpbmcpID0+IGdyYXkocyksXG4gIGlkbGU6IChzOiBzdHJpbmcpID0+IGdyYXkocyksXG59O1xuXG5leHBvcnQgY29uc3QgbWVzc2FnZVN0eWxlczogU3RhdHVzU3R5bGVzID0ge1xuICBhY3RpdmU6IChzOiBzdHJpbmcpID0+IGJsYWNrKHMpLFxuICBkb25lOiAoczogc3RyaW5nKSA9PiBib2xkLmdyZWVuKHMpLFxuICBpZGxlOiAoczogc3RyaW5nKSA9PiBncmF5KHMpLFxufTtcblxuZXhwb3J0IHR5cGUgUHJvZ3Jlc3NCYXIgPSB7XG4gIC8vIEBUT0RPIGZpbmQgYmV0dGVyIG5hbWluZ1xuICBpbmNyZW1lbnRJblN0ZXBzOiAobnVtU3RlcHM6IG51bWJlcikgPT4gdm9pZDtcbiAgdXBkYXRlVGl0bGU6ICh0aXRsZTogc3RyaW5nKSA9PiB2b2lkO1xuICBlbmRQcm9ncmVzczogKG1lc3NhZ2U/OiBzdHJpbmcpID0+IHZvaWQ7XG59O1xuXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZnVuY3Rpb25hbC9uby1sZXRcbmxldCBtcGI6IE11bHRpUHJvZ3Jlc3NCYXJzO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2luZ2xldG9uUHJvZ3Jlc3NCYXJzKFxuICBvcHRpb25zPzogUGFydGlhbDxDdG9yT3B0aW9ucz4sXG4pOiBNdWx0aVByb2dyZXNzQmFycyB7XG4gIGlmICghbXBiKSB7XG4gICAgbXBiID0gbmV3IE11bHRpUHJvZ3Jlc3NCYXJzKHtcbiAgICAgIHByb2dyZXNzV2lkdGg6IFRFUk1JTkFMX1dJRFRILFxuICAgICAgaW5pdE1lc3NhZ2U6ICcnLFxuICAgICAgYm9yZGVyOiB0cnVlLFxuICAgICAgLi4ub3B0aW9ucyxcbiAgICB9KTtcbiAgfVxuICByZXR1cm4gbXBiO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0UHJvZ3Jlc3NCYXIodGFza05hbWU6IHN0cmluZyk6IFByb2dyZXNzQmFyIHtcbiAgY29uc3QgdGFza3MgPSBnZXRTaW5nbGV0b25Qcm9ncmVzc0JhcnMoKTtcblxuICAvLyBJbml0aWFsaXplIHByb2dyZXNzIGJhciBpZiBub3Qgc2V0XG4gIHRhc2tzLmFkZFRhc2sodGFza05hbWUsIHtcbiAgICB0eXBlOiAncGVyY2VudGFnZScsXG4gICAgcGVyY2VudGFnZTogMCxcbiAgICBtZXNzYWdlOiAnJyxcbiAgICBiYXJUcmFuc2Zvcm1GbjogYmFyU3R5bGVzLmlkbGUsXG4gIH0pO1xuXG4gIHJldHVybiB7XG4gICAgaW5jcmVtZW50SW5TdGVwczogKG51bVBsdWdpbnM6IG51bWJlcikgPT4ge1xuICAgICAgdGFza3MuaW5jcmVtZW50VGFzayh0YXNrTmFtZSwge1xuICAgICAgICBwZXJjZW50YWdlOiAxIC8gbnVtUGx1Z2lucyxcbiAgICAgICAgYmFyVHJhbnNmb3JtRm46IGJhclN0eWxlcy5hY3RpdmUsXG4gICAgICB9KTtcbiAgICB9LFxuICAgIHVwZGF0ZVRpdGxlOiAodGl0bGU6IHN0cmluZykgPT4ge1xuICAgICAgdGFza3MudXBkYXRlVGFzayh0YXNrTmFtZSwge1xuICAgICAgICBtZXNzYWdlOiB0aXRsZSxcbiAgICAgICAgYmFyVHJhbnNmb3JtRm46IGJhclN0eWxlcy5hY3RpdmUsXG4gICAgICB9KTtcbiAgICB9LFxuICAgIGVuZFByb2dyZXNzOiAobWVzc2FnZSA9ICcnKSA9PiB7XG4gICAgICB0YXNrcy5kb25lKHRhc2tOYW1lLCB7XG4gICAgICAgIG1lc3NhZ2U6IG1lc3NhZ2VTdHlsZXMuZG9uZShtZXNzYWdlKSxcbiAgICAgICAgYmFyVHJhbnNmb3JtRm46IGJhclN0eWxlcy5kb25lLFxuICAgICAgfSk7XG4gICAgfSxcbiAgfTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0cy9nZW5lcmF0ZS1tZC1yZXBvcnQudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9yZXBvcnRzXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0cy9nZW5lcmF0ZS1tZC1yZXBvcnQudHNcIjtpbXBvcnQgeyB0eXBlIElubGluZVRleHQsIE1hcmtkb3duRG9jdW1lbnQsIG1kIH0gZnJvbSAnYnVpbGQtbWQnO1xuaW1wb3J0IHR5cGUgeyBBdWRpdFJlcG9ydCwgSXNzdWUsIFJlcG9ydCB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgZm9ybWF0RGF0ZSwgZm9ybWF0RHVyYXRpb24gfSBmcm9tICcuLi9mb3JtYXR0aW5nLmpzJztcbmltcG9ydCB7IEhJRVJBUkNIWSB9IGZyb20gJy4uL3RleHQtZm9ybWF0cy9pbmRleC5qcyc7XG5pbXBvcnQge1xuICBGT09URVJfUFJFRklYLFxuICBSRUFETUVfTElOSyxcbiAgUkVQT1JUX0hFQURMSU5FX1RFWFQsXG59IGZyb20gJy4vY29uc3RhbnRzLmpzJztcbmltcG9ydCB7XG4gIGZvcm1hdFNvdXJjZUxpbmUsXG4gIGxpbmtUb0xvY2FsU291cmNlRm9ySWRlLFxuICBtZXRhRGVzY3JpcHRpb24sXG4gIHRhYmxlU2VjdGlvbixcbn0gZnJvbSAnLi9mb3JtYXR0aW5nLmpzJztcbmltcG9ydCB7XG4gIGNhdGVnb3JpZXNEZXRhaWxzU2VjdGlvbixcbiAgY2F0ZWdvcmllc092ZXJ2aWV3U2VjdGlvbixcbn0gZnJvbSAnLi9nZW5lcmF0ZS1tZC1yZXBvcnQtY2F0ZWdveS1zZWN0aW9uLmpzJztcbmltcG9ydCB0eXBlIHsgTWRSZXBvcnRPcHRpb25zLCBTY29yZWRSZXBvcnQgfSBmcm9tICcuL3R5cGVzLmpzJztcbmltcG9ydCB7IGZvcm1hdFJlcG9ydFNjb3JlLCBzY29yZU1hcmtlciwgc2V2ZXJpdHlNYXJrZXIgfSBmcm9tICcuL3V0aWxzLmpzJztcblxuZXhwb3J0IGZ1bmN0aW9uIGF1ZGl0RGV0YWlsc0F1ZGl0VmFsdWUoe1xuICBzY29yZSxcbiAgdmFsdWUsXG4gIGRpc3BsYXlWYWx1ZSxcbn06IEF1ZGl0UmVwb3J0KTogSW5saW5lVGV4dCB7XG4gIHJldHVybiBtZGAke3Njb3JlTWFya2VyKHNjb3JlLCAnc3F1YXJlJyl9ICR7bWQuYm9sZChcbiAgICBTdHJpbmcoZGlzcGxheVZhbHVlID8/IHZhbHVlKSxcbiAgKX0gKHNjb3JlOiAke2Zvcm1hdFJlcG9ydFNjb3JlKHNjb3JlKX0pYDtcbn1cblxuZnVuY3Rpb24gaGFzQ2F0ZWdvcmllcyhcbiAgcmVwb3J0OiBTY29yZWRSZXBvcnQsXG4pOiByZXBvcnQgaXMgU2NvcmVkUmVwb3J0ICYgUmVxdWlyZWQ8UGljazxTY29yZWRSZXBvcnQsICdjYXRlZ29yaWVzJz4+IHtcbiAgcmV0dXJuICEhcmVwb3J0LmNhdGVnb3JpZXMgJiYgcmVwb3J0LmNhdGVnb3JpZXMubGVuZ3RoID4gMDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdlbmVyYXRlTWRSZXBvcnQoXG4gIHJlcG9ydDogU2NvcmVkUmVwb3J0LFxuICBvcHRpb25zPzogTWRSZXBvcnRPcHRpb25zLFxuKTogc3RyaW5nIHtcbiAgcmV0dXJuIG5ldyBNYXJrZG93bkRvY3VtZW50KClcbiAgICAuaGVhZGluZyhISUVSQVJDSFkubGV2ZWxfMSwgUkVQT1JUX0hFQURMSU5FX1RFWFQpXG4gICAgLiRjb25jYXQoXG4gICAgICAuLi4oaGFzQ2F0ZWdvcmllcyhyZXBvcnQpXG4gICAgICAgID8gW2NhdGVnb3JpZXNPdmVydmlld1NlY3Rpb24ocmVwb3J0KSwgY2F0ZWdvcmllc0RldGFpbHNTZWN0aW9uKHJlcG9ydCldXG4gICAgICAgIDogW10pLFxuICAgICAgYXVkaXRzU2VjdGlvbihyZXBvcnQsIG9wdGlvbnMpLFxuICAgICAgYWJvdXRTZWN0aW9uKHJlcG9ydCksXG4gICAgKVxuICAgIC5ydWxlKClcbiAgICAucGFyYWdyYXBoKG1kYCR7Rk9PVEVSX1BSRUZJWH0gJHttZC5saW5rKFJFQURNRV9MSU5LLCAnQ29kZSBQdXNoVXAnKX1gKVxuICAgIC50b1N0cmluZygpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYXVkaXREZXRhaWxzSXNzdWVzKFxuICBpc3N1ZXM6IElzc3VlW10gPSBbXSxcbiAgb3B0aW9ucz86IE1kUmVwb3J0T3B0aW9ucyxcbik6IE1hcmtkb3duRG9jdW1lbnQgfCBudWxsIHtcbiAgaWYgKGlzc3Vlcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICByZXR1cm4gbmV3IE1hcmtkb3duRG9jdW1lbnQoKS5oZWFkaW5nKEhJRVJBUkNIWS5sZXZlbF80LCAnSXNzdWVzJykudGFibGUoXG4gICAgW1xuICAgICAgeyBoZWFkaW5nOiAnU2V2ZXJpdHknLCBhbGlnbm1lbnQ6ICdjZW50ZXInIH0sXG4gICAgICB7IGhlYWRpbmc6ICdNZXNzYWdlJywgYWxpZ25tZW50OiAnbGVmdCcgfSxcbiAgICAgIHsgaGVhZGluZzogJ1NvdXJjZSBmaWxlJywgYWxpZ25tZW50OiAnbGVmdCcgfSxcbiAgICAgIHsgaGVhZGluZzogJ0xpbmUocyknLCBhbGlnbm1lbnQ6ICdjZW50ZXInIH0sXG4gICAgXSxcbiAgICBpc3N1ZXMubWFwKCh7IHNldmVyaXR5OiBsZXZlbCwgbWVzc2FnZSwgc291cmNlIH06IElzc3VlKSA9PiB7XG4gICAgICBjb25zdCBzZXZlcml0eSA9IG1kYCR7c2V2ZXJpdHlNYXJrZXIobGV2ZWwpfSAke21kLml0YWxpYyhsZXZlbCl9YDtcblxuICAgICAgaWYgKCFzb3VyY2UpIHtcbiAgICAgICAgcmV0dXJuIFtzZXZlcml0eSwgbWVzc2FnZV07XG4gICAgICB9XG4gICAgICBjb25zdCBmaWxlID0gbGlua1RvTG9jYWxTb3VyY2VGb3JJZGUoc291cmNlLCBvcHRpb25zKTtcbiAgICAgIGlmICghc291cmNlLnBvc2l0aW9uKSB7XG4gICAgICAgIHJldHVybiBbc2V2ZXJpdHksIG1lc3NhZ2UsIGZpbGVdO1xuICAgICAgfVxuICAgICAgY29uc3QgbGluZSA9IGZvcm1hdFNvdXJjZUxpbmUoc291cmNlLnBvc2l0aW9uKTtcbiAgICAgIHJldHVybiBbc2V2ZXJpdHksIG1lc3NhZ2UsIGZpbGUsIGxpbmVdO1xuICAgIH0pLFxuICApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYXVkaXREZXRhaWxzKFxuICBhdWRpdDogQXVkaXRSZXBvcnQsXG4gIG9wdGlvbnM/OiBNZFJlcG9ydE9wdGlvbnMsXG4pOiBNYXJrZG93bkRvY3VtZW50IHtcbiAgY29uc3QgeyB0YWJsZSwgaXNzdWVzID0gW10gfSA9IGF1ZGl0LmRldGFpbHMgPz8ge307XG4gIGNvbnN0IGRldGFpbHNWYWx1ZSA9IGF1ZGl0RGV0YWlsc0F1ZGl0VmFsdWUoYXVkaXQpO1xuXG4gIC8vIHVuZGVmaW5lZCBkZXRhaWxzIE9SIGVtcHR5IGRldGFpbHMgKHVuZGVmaW5lZCBpc3N1ZXMgT1IgZW1wdHkgaXNzdWVzIEFORCBlbXB0eSB0YWJsZSlcbiAgaWYgKGlzc3Vlcy5sZW5ndGggPT09IDAgJiYgIXRhYmxlPy5yb3dzLmxlbmd0aCkge1xuICAgIHJldHVybiBuZXcgTWFya2Rvd25Eb2N1bWVudCgpLnBhcmFncmFwaChkZXRhaWxzVmFsdWUpO1xuICB9XG5cbiAgY29uc3QgdGFibGVTZWN0aW9uQ29udGVudCA9IHRhYmxlICYmIHRhYmxlU2VjdGlvbih0YWJsZSk7XG4gIGNvbnN0IGlzc3Vlc1NlY3Rpb25Db250ZW50ID1cbiAgICBpc3N1ZXMubGVuZ3RoID4gMCAmJiBhdWRpdERldGFpbHNJc3N1ZXMoaXNzdWVzLCBvcHRpb25zKTtcblxuICByZXR1cm4gbmV3IE1hcmtkb3duRG9jdW1lbnQoKS5kZXRhaWxzKFxuICAgIGRldGFpbHNWYWx1ZSxcbiAgICBuZXcgTWFya2Rvd25Eb2N1bWVudCgpLiRjb25jYXQodGFibGVTZWN0aW9uQ29udGVudCwgaXNzdWVzU2VjdGlvbkNvbnRlbnQpLFxuICApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYXVkaXRzU2VjdGlvbihcbiAgeyBwbHVnaW5zIH06IFBpY2s8U2NvcmVkUmVwb3J0LCAncGx1Z2lucyc+LFxuICBvcHRpb25zPzogTWRSZXBvcnRPcHRpb25zLFxuKTogTWFya2Rvd25Eb2N1bWVudCB7XG4gIHJldHVybiBuZXcgTWFya2Rvd25Eb2N1bWVudCgpXG4gICAgLmhlYWRpbmcoSElFUkFSQ0hZLmxldmVsXzIsICdcdUQ4M0RcdURFRTFcdUZFMEYgQXVkaXRzJylcbiAgICAuJGZvcmVhY2goXG4gICAgICBwbHVnaW5zLmZsYXRNYXAocGx1Z2luID0+XG4gICAgICAgIHBsdWdpbi5hdWRpdHMubWFwKGF1ZGl0ID0+ICh7IC4uLmF1ZGl0LCBwbHVnaW4gfSkpLFxuICAgICAgKSxcbiAgICAgIChkb2MsIHsgcGx1Z2luLCAuLi5hdWRpdCB9KSA9PiB7XG4gICAgICAgIGNvbnN0IGF1ZGl0VGl0bGUgPSBgJHthdWRpdC50aXRsZX0gKCR7cGx1Z2luLnRpdGxlfSlgO1xuICAgICAgICBjb25zdCBkZXRhaWxzQ29udGVudCA9IGF1ZGl0RGV0YWlscyhhdWRpdCwgb3B0aW9ucyk7XG4gICAgICAgIGNvbnN0IGRlc2NyaXB0aW9uQ29udGVudCA9IG1ldGFEZXNjcmlwdGlvbihhdWRpdCk7XG5cbiAgICAgICAgcmV0dXJuIGRvY1xuICAgICAgICAgIC5oZWFkaW5nKEhJRVJBUkNIWS5sZXZlbF8zLCBhdWRpdFRpdGxlKVxuICAgICAgICAgIC4kY29uY2F0KGRldGFpbHNDb250ZW50KVxuICAgICAgICAgIC5wYXJhZ3JhcGgoZGVzY3JpcHRpb25Db250ZW50KTtcbiAgICAgIH0sXG4gICAgKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGFib3V0U2VjdGlvbihcbiAgcmVwb3J0OiBPbWl0PFNjb3JlZFJlcG9ydCwgJ3BhY2thZ2VOYW1lJz4sXG4pOiBNYXJrZG93bkRvY3VtZW50IHtcbiAgY29uc3QgeyBkYXRlLCBwbHVnaW5zIH0gPSByZXBvcnQ7XG4gIHJldHVybiBuZXcgTWFya2Rvd25Eb2N1bWVudCgpXG4gICAgLmhlYWRpbmcoSElFUkFSQ0hZLmxldmVsXzIsICdBYm91dCcpXG4gICAgLnBhcmFncmFwaChcbiAgICAgIG1kYFJlcG9ydCB3YXMgY3JlYXRlZCBieSAke21kLmxpbmsoXG4gICAgICAgIFJFQURNRV9MSU5LLFxuICAgICAgICAnQ29kZSBQdXNoVXAnLFxuICAgICAgKX0gb24gJHtmb3JtYXREYXRlKG5ldyBEYXRlKGRhdGUpKX0uYCxcbiAgICApXG4gICAgLnRhYmxlKC4uLnBsdWdpbk1ldGFUYWJsZSh7IHBsdWdpbnMgfSkpXG4gICAgLnRhYmxlKC4uLnJlcG9ydE1ldGFUYWJsZShyZXBvcnQpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBsdWdpbk1ldGFUYWJsZSh7XG4gIHBsdWdpbnMsXG59OiBQaWNrPFJlcG9ydCwgJ3BsdWdpbnMnPik6IFBhcmFtZXRlcnM8TWFya2Rvd25Eb2N1bWVudFsndGFibGUnXT4ge1xuICByZXR1cm4gW1xuICAgIFtcbiAgICAgIHsgaGVhZGluZzogJ1BsdWdpbicsIGFsaWdubWVudDogJ2xlZnQnIH0sXG4gICAgICB7IGhlYWRpbmc6ICdBdWRpdHMnLCBhbGlnbm1lbnQ6ICdjZW50ZXInIH0sXG4gICAgICB7IGhlYWRpbmc6ICdWZXJzaW9uJywgYWxpZ25tZW50OiAnY2VudGVyJyB9LFxuICAgICAgeyBoZWFkaW5nOiAnRHVyYXRpb24nLCBhbGlnbm1lbnQ6ICdyaWdodCcgfSxcbiAgICBdLFxuICAgIHBsdWdpbnMubWFwKCh7IHRpdGxlLCBhdWRpdHMsIHZlcnNpb24gPSAnJywgZHVyYXRpb24gfSkgPT4gW1xuICAgICAgdGl0bGUsXG4gICAgICBhdWRpdHMubGVuZ3RoLnRvU3RyaW5nKCksXG4gICAgICB2ZXJzaW9uICYmIG1kLmNvZGUodmVyc2lvbiksXG4gICAgICBmb3JtYXREdXJhdGlvbihkdXJhdGlvbiksXG4gICAgXSksXG4gIF07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZXBvcnRNZXRhVGFibGUoe1xuICBjb21taXQsXG4gIHZlcnNpb24sXG4gIGR1cmF0aW9uLFxuICBwbHVnaW5zLFxuICBjYXRlZ29yaWVzLFxufTogUGljazxcbiAgU2NvcmVkUmVwb3J0LFxuICAnZGF0ZScgfCAnZHVyYXRpb24nIHwgJ3ZlcnNpb24nIHwgJ2NvbW1pdCcgfCAncGx1Z2lucycgfCAnY2F0ZWdvcmllcydcbj4pOiBQYXJhbWV0ZXJzPE1hcmtkb3duRG9jdW1lbnRbJ3RhYmxlJ10+IHtcbiAgcmV0dXJuIFtcbiAgICBbXG4gICAgICB7IGhlYWRpbmc6ICdDb21taXQnLCBhbGlnbm1lbnQ6ICdsZWZ0JyB9LFxuICAgICAgeyBoZWFkaW5nOiAnVmVyc2lvbicsIGFsaWdubWVudDogJ2NlbnRlcicgfSxcbiAgICAgIHsgaGVhZGluZzogJ0R1cmF0aW9uJywgYWxpZ25tZW50OiAncmlnaHQnIH0sXG4gICAgICB7IGhlYWRpbmc6ICdQbHVnaW5zJywgYWxpZ25tZW50OiAnY2VudGVyJyB9LFxuICAgICAgeyBoZWFkaW5nOiAnQ2F0ZWdvcmllcycsIGFsaWdubWVudDogJ2NlbnRlcicgfSxcbiAgICAgIHsgaGVhZGluZzogJ0F1ZGl0cycsIGFsaWdubWVudDogJ2NlbnRlcicgfSxcbiAgICBdLFxuICAgIFtcbiAgICAgIFtcbiAgICAgICAgY29tbWl0ID8gYCR7Y29tbWl0Lm1lc3NhZ2V9ICgke2NvbW1pdC5oYXNofSlgIDogJ04vQScsXG4gICAgICAgIG1kLmNvZGUodmVyc2lvbiksXG4gICAgICAgIGZvcm1hdER1cmF0aW9uKGR1cmF0aW9uKSxcbiAgICAgICAgcGx1Z2lucy5sZW5ndGgudG9TdHJpbmcoKSxcbiAgICAgICAgKGNhdGVnb3JpZXM/Lmxlbmd0aCA/PyAwKS50b1N0cmluZygpLFxuICAgICAgICBwbHVnaW5zLnJlZHVjZSgoYWNjLCB7IGF1ZGl0cyB9KSA9PiBhY2MgKyBhdWRpdHMubGVuZ3RoLCAwKS50b1N0cmluZygpLFxuICAgICAgXSxcbiAgICBdLFxuICBdO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9yZXBvcnRzL2Zvcm1hdHRpbmcudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9yZXBvcnRzXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0cy9mb3JtYXR0aW5nLnRzXCI7aW1wb3J0IHtcbiAgdHlwZSBIZWFkaW5nTGV2ZWwsXG4gIHR5cGUgSW5saW5lVGV4dCxcbiAgTWFya2Rvd25Eb2N1bWVudCxcbiAgbWQsXG59IGZyb20gJ2J1aWxkLW1kJztcbmltcG9ydCBwYXRoIGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgdHlwZSB7XG4gIEF1ZGl0UmVwb3J0LFxuICBTb3VyY2VGaWxlTG9jYXRpb24sXG4gIFRhYmxlLFxufSBmcm9tICdAY29kZS1wdXNodXAvbW9kZWxzJztcbmltcG9ydCB7IEhJRVJBUkNIWSB9IGZyb20gJy4uL3RleHQtZm9ybWF0cy9pbmRleC5qcyc7XG5pbXBvcnQge1xuICBjb2x1bW5zVG9TdHJpbmdBcnJheSxcbiAgZ2V0Q29sdW1uQWxpZ25tZW50cyxcbiAgcm93VG9TdHJpbmdBcnJheSxcbn0gZnJvbSAnLi4vdGV4dC1mb3JtYXRzL3RhYmxlLmpzJztcbmltcG9ydCB7XG4gIGdldEVudmlyb25tZW50VHlwZSxcbiAgZ2V0R2l0SHViQmFzZVVybCxcbiAgZ2V0R2l0TGFiQmFzZVVybCxcbn0gZnJvbSAnLi9lbnZpcm9ubWVudC10eXBlLmpzJztcbmltcG9ydCB0eXBlIHsgTWRSZXBvcnRPcHRpb25zIH0gZnJvbSAnLi90eXBlcy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiB0YWJsZVNlY3Rpb24oXG4gIHRhYmxlRGF0YTogVGFibGUsXG4gIG9wdGlvbnM/OiB7XG4gICAgbGV2ZWw/OiBIZWFkaW5nTGV2ZWw7XG4gIH0sXG4pOiBNYXJrZG93bkRvY3VtZW50IHwgbnVsbCB7XG4gIGlmICh0YWJsZURhdGEucm93cy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICBjb25zdCB7IGxldmVsID0gSElFUkFSQ0hZLmxldmVsXzQgfSA9IG9wdGlvbnMgPz8ge307XG4gIGNvbnN0IGNvbHVtbnMgPSBjb2x1bW5zVG9TdHJpbmdBcnJheSh0YWJsZURhdGEpO1xuICBjb25zdCBhbGlnbm1lbnRzID0gZ2V0Q29sdW1uQWxpZ25tZW50cyh0YWJsZURhdGEpO1xuICBjb25zdCByb3dzID0gcm93VG9TdHJpbmdBcnJheSh0YWJsZURhdGEpO1xuICByZXR1cm4gbmV3IE1hcmtkb3duRG9jdW1lbnQoKS5oZWFkaW5nKGxldmVsLCB0YWJsZURhdGEudGl0bGUpLnRhYmxlKFxuICAgIGNvbHVtbnMubWFwKChoZWFkaW5nLCBpKSA9PiB7XG4gICAgICBjb25zdCBhbGlnbm1lbnQgPSBhbGlnbm1lbnRzW2ldO1xuICAgICAgaWYgKGFsaWdubWVudCkge1xuICAgICAgICByZXR1cm4geyBoZWFkaW5nLCBhbGlnbm1lbnQgfTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBoZWFkaW5nO1xuICAgIH0pLFxuICAgIHJvd3MsXG4gICk7XG59XG5cbi8vIEBUT0RPIGV4dHJhY3QgYFBpY2s8QXVkaXRSZXBvcnQsICdkb2NzVXJsJyB8ICdkZXNjcmlwdGlvbic+YCB0byBhIHJldXNhYmxlIHNjaGVtYSBhbmQgdHlwZVxuZXhwb3J0IGZ1bmN0aW9uIG1ldGFEZXNjcmlwdGlvbihcbiAgYXVkaXQ6IFBpY2s8QXVkaXRSZXBvcnQsICdkb2NzVXJsJyB8ICdkZXNjcmlwdGlvbic+LFxuKTogSW5saW5lVGV4dCB7XG4gIGNvbnN0IGRvY3NVcmwgPSBhdWRpdC5kb2NzVXJsO1xuICBjb25zdCBkZXNjcmlwdGlvbiA9IGF1ZGl0LmRlc2NyaXB0aW9uPy50cmltKCk7XG4gIGlmIChkb2NzVXJsKSB7XG4gICAgY29uc3QgZG9jc0xpbmsgPSBtZC5saW5rKGRvY3NVcmwsICdcdUQ4M0RcdURDRDYgRG9jcycpO1xuICAgIGlmICghZGVzY3JpcHRpb24pIHtcbiAgICAgIHJldHVybiBkb2NzTGluaztcbiAgICB9XG4gICAgY29uc3QgcGFyc2VkRGVzY3JpcHRpb24gPSBkZXNjcmlwdGlvbi5lbmRzV2l0aCgnYGBgJylcbiAgICAgID8gYCR7ZGVzY3JpcHRpb259XFxuXFxuYFxuICAgICAgOiBgJHtkZXNjcmlwdGlvbn0gYDtcbiAgICByZXR1cm4gbWRgJHtwYXJzZWREZXNjcmlwdGlvbn0ke2RvY3NMaW5rfWA7XG4gIH1cbiAgaWYgKGRlc2NyaXB0aW9uICYmIGRlc2NyaXB0aW9uLnRyaW0oKS5sZW5ndGggPiAwKSB7XG4gICAgcmV0dXJuIGRlc2NyaXB0aW9uO1xuICB9XG4gIHJldHVybiAnJztcbn1cblxuLyoqXG4gKiBMaW5rIHRvIGxvY2FsIHNvdXJjZSBmb3IgSURFXG4gKiBAcGFyYW0gc291cmNlXG4gKiBAcGFyYW0gcmVwb3J0TG9jYXRpb25cbiAqXG4gKiBAZXhhbXBsZVxuICogbGlua1RvTG9jYWxTb3VyY2VJbklkZSh7IGZpbGU6ICdzcmMvaW5kZXgudHMnIH0sIHsgb3V0cHV0RGlyOiAnLmNvZGUtcHVzaHVwJyB9KSAvLyBbYHNyYy9pbmRleC50c2BdKC4uL3NyYy9pbmRleC50cylcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGxpbmtUb0xvY2FsU291cmNlRm9ySWRlKFxuICBzb3VyY2U6IFNvdXJjZUZpbGVMb2NhdGlvbixcbiAgb3B0aW9ucz86IFBpY2s8TWRSZXBvcnRPcHRpb25zLCAnb3V0cHV0RGlyJz4sXG4pOiBJbmxpbmVUZXh0IHtcbiAgY29uc3QgeyBmaWxlLCBwb3NpdGlvbiB9ID0gc291cmNlO1xuICBjb25zdCB7IG91dHB1dERpciB9ID0gb3B0aW9ucyA/PyB7fTtcblxuICAvLyBOT1QgbGlua2FibGVcbiAgaWYgKCFvdXRwdXREaXIpIHtcbiAgICByZXR1cm4gbWQuY29kZShmaWxlKTtcbiAgfVxuXG4gIHJldHVybiBtZC5saW5rKGZvcm1hdEZpbGVMaW5rKGZpbGUsIHBvc2l0aW9uLCBvdXRwdXREaXIpLCBtZC5jb2RlKGZpbGUpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdFNvdXJjZUxpbmUoXG4gIHBvc2l0aW9uOiBTb3VyY2VGaWxlTG9jYXRpb25bJ3Bvc2l0aW9uJ10sXG4pOiBzdHJpbmcge1xuICBpZiAoIXBvc2l0aW9uKSB7XG4gICAgcmV0dXJuICcnO1xuICB9XG4gIGNvbnN0IHsgc3RhcnRMaW5lLCBlbmRMaW5lIH0gPSBwb3NpdGlvbjtcbiAgcmV0dXJuIGVuZExpbmUgJiYgc3RhcnRMaW5lICE9PSBlbmRMaW5lXG4gICAgPyBgJHtzdGFydExpbmV9LSR7ZW5kTGluZX1gXG4gICAgOiBgJHtzdGFydExpbmV9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdEdpdEh1YkxpbmsoXG4gIGZpbGU6IHN0cmluZyxcbiAgcG9zaXRpb246IFNvdXJjZUZpbGVMb2NhdGlvblsncG9zaXRpb24nXSxcbik6IHN0cmluZyB7XG4gIGNvbnN0IGJhc2VVcmwgPSBnZXRHaXRIdWJCYXNlVXJsKCk7XG4gIGlmICghcG9zaXRpb24pIHtcbiAgICByZXR1cm4gYCR7YmFzZVVybH0vJHtmaWxlfWA7XG4gIH1cbiAgY29uc3QgeyBzdGFydExpbmUsIGVuZExpbmUsIHN0YXJ0Q29sdW1uLCBlbmRDb2x1bW4gfSA9IHBvc2l0aW9uO1xuICBjb25zdCBzdGFydCA9IHN0YXJ0Q29sdW1uID8gYEwke3N0YXJ0TGluZX1DJHtzdGFydENvbHVtbn1gIDogYEwke3N0YXJ0TGluZX1gO1xuICBjb25zdCBlbmQgPSBlbmRMaW5lXG4gICAgPyBlbmRDb2x1bW5cbiAgICAgID8gYEwke2VuZExpbmV9QyR7ZW5kQ29sdW1ufWBcbiAgICAgIDogYEwke2VuZExpbmV9YFxuICAgIDogJyc7XG4gIGNvbnN0IGxpbmVSYW5nZSA9IGVuZCAmJiBzdGFydCAhPT0gZW5kID8gYCR7c3RhcnR9LSR7ZW5kfWAgOiBzdGFydDtcbiAgcmV0dXJuIGAke2Jhc2VVcmx9LyR7ZmlsZX0jJHtsaW5lUmFuZ2V9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdEdpdExhYkxpbmsoXG4gIGZpbGU6IHN0cmluZyxcbiAgcG9zaXRpb246IFNvdXJjZUZpbGVMb2NhdGlvblsncG9zaXRpb24nXSxcbik6IHN0cmluZyB7XG4gIGNvbnN0IGJhc2VVcmwgPSBnZXRHaXRMYWJCYXNlVXJsKCk7XG4gIGlmICghcG9zaXRpb24pIHtcbiAgICByZXR1cm4gYCR7YmFzZVVybH0vJHtmaWxlfWA7XG4gIH1cbiAgY29uc3QgeyBzdGFydExpbmUsIGVuZExpbmUgfSA9IHBvc2l0aW9uO1xuICBjb25zdCBsaW5lUmFuZ2UgPVxuICAgIGVuZExpbmUgJiYgc3RhcnRMaW5lICE9PSBlbmRMaW5lID8gYCR7c3RhcnRMaW5lfS0ke2VuZExpbmV9YCA6IHN0YXJ0TGluZTtcbiAgcmV0dXJuIGAke2Jhc2VVcmx9LyR7ZmlsZX0jTCR7bGluZVJhbmdlfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRGaWxlTGluayhcbiAgZmlsZTogc3RyaW5nLFxuICBwb3NpdGlvbjogU291cmNlRmlsZUxvY2F0aW9uWydwb3NpdGlvbiddLFxuICBvdXRwdXREaXI6IHN0cmluZyxcbik6IHN0cmluZyB7XG4gIGNvbnN0IHJlbGF0aXZlUGF0aCA9IHBhdGgucG9zaXgucmVsYXRpdmUob3V0cHV0RGlyLCBmaWxlKTtcbiAgY29uc3QgZW52ID0gZ2V0RW52aXJvbm1lbnRUeXBlKCk7XG5cbiAgc3dpdGNoIChlbnYpIHtcbiAgICBjYXNlICd2c2NvZGUnOlxuICAgICAgcmV0dXJuIHBvc2l0aW9uID8gYCR7cmVsYXRpdmVQYXRofSNMJHtwb3NpdGlvbi5zdGFydExpbmV9YCA6IHJlbGF0aXZlUGF0aDtcbiAgICBjYXNlICdnaXRodWInOlxuICAgICAgcmV0dXJuIGZvcm1hdEdpdEh1YkxpbmsoZmlsZSwgcG9zaXRpb24pO1xuICAgIGNhc2UgJ2dpdGxhYic6XG4gICAgICByZXR1cm4gZm9ybWF0R2l0TGFiTGluayhmaWxlLCBwb3NpdGlvbik7XG4gICAgZGVmYXVsdDpcbiAgICAgIHJldHVybiByZWxhdGl2ZVBhdGg7XG4gIH1cbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0cy9nZW5lcmF0ZS1tZC1yZXBvcnQtY2F0ZWdveS1zZWN0aW9uLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0c1wiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3JlcG9ydHMvZ2VuZXJhdGUtbWQtcmVwb3J0LWNhdGVnb3ktc2VjdGlvbi50c1wiO2ltcG9ydCB7IHR5cGUgSW5saW5lVGV4dCwgTWFya2Rvd25Eb2N1bWVudCwgbWQgfSBmcm9tICdidWlsZC1tZCc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0UmVwb3J0IH0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5pbXBvcnQgeyBzbHVnaWZ5IH0gZnJvbSAnLi4vZm9ybWF0dGluZy5qcyc7XG5pbXBvcnQgeyBISUVSQVJDSFkgfSBmcm9tICcuLi90ZXh0LWZvcm1hdHMvaW5kZXguanMnO1xuaW1wb3J0IHsgbWV0YURlc2NyaXB0aW9uIH0gZnJvbSAnLi9mb3JtYXR0aW5nLmpzJztcbmltcG9ydCB7IGdldFNvcnRhYmxlQXVkaXRCeVJlZiwgZ2V0U29ydGFibGVHcm91cEJ5UmVmIH0gZnJvbSAnLi9zb3J0aW5nLmpzJztcbmltcG9ydCB0eXBlIHsgU2NvcmVkR3JvdXAsIFNjb3JlZFJlcG9ydCB9IGZyb20gJy4vdHlwZXMuanMnO1xuaW1wb3J0IHtcbiAgY291bnRDYXRlZ29yeUF1ZGl0cyxcbiAgZm9ybWF0UmVwb3J0U2NvcmUsXG4gIGdldFBsdWdpbk5hbWVGcm9tU2x1ZyxcbiAgc2NvcmVNYXJrZXIsXG4gIHRhcmdldFNjb3JlSWNvbixcbn0gZnJvbSAnLi91dGlscy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBjYXRlZ29yaWVzT3ZlcnZpZXdTZWN0aW9uKFxuICByZXBvcnQ6IFJlcXVpcmVkPFBpY2s8U2NvcmVkUmVwb3J0LCAncGx1Z2lucycgfCAnY2F0ZWdvcmllcyc+Pixcbik6IE1hcmtkb3duRG9jdW1lbnQge1xuICBjb25zdCB7IGNhdGVnb3JpZXMsIHBsdWdpbnMgfSA9IHJlcG9ydDtcbiAgcmV0dXJuIG5ldyBNYXJrZG93bkRvY3VtZW50KCkudGFibGUoXG4gICAgW1xuICAgICAgeyBoZWFkaW5nOiAnXHVEODNDXHVERkY3IENhdGVnb3J5JywgYWxpZ25tZW50OiAnbGVmdCcgfSxcbiAgICAgIHsgaGVhZGluZzogJ1x1MkI1MCBTY29yZScsIGFsaWdubWVudDogJ2NlbnRlcicgfSxcbiAgICAgIHsgaGVhZGluZzogJ1x1RDgzRFx1REVFMSBBdWRpdHMnLCBhbGlnbm1lbnQ6ICdjZW50ZXInIH0sXG4gICAgXSxcbiAgICBjYXRlZ29yaWVzLm1hcCgoeyB0aXRsZSwgcmVmcywgc2NvcmUsIGlzQmluYXJ5IH0pID0+IFtcbiAgICAgIC8vIEBUT0RPIHJlZmFjdG9yIGBpc0JpbmFyeTogYm9vbGVhbmAgdG8gYHRhcmdldFNjb3JlOiBudW1iZXJgICM3MTNcbiAgICAgIC8vIFRoZSBoZWFkaW5nIFwiSURcIiBpcyBpbmZlcnJlZCBmcm9tIHRoZSBoZWFkaW5nIHRleHQgaW4gTWFya2Rvd24uXG4gICAgICBtZC5saW5rKGAjJHtzbHVnaWZ5KHRpdGxlKX1gLCB0aXRsZSksXG4gICAgICBtZGAke3Njb3JlTWFya2VyKHNjb3JlKX0gJHttZC5ib2xkKFxuICAgICAgICBmb3JtYXRSZXBvcnRTY29yZShzY29yZSksXG4gICAgICApfSR7YmluYXJ5SWNvblN1ZmZpeChzY29yZSwgaXNCaW5hcnkpfWAsXG4gICAgICBjb3VudENhdGVnb3J5QXVkaXRzKHJlZnMsIHBsdWdpbnMpLnRvU3RyaW5nKCksXG4gICAgXSksXG4gICk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjYXRlZ29yaWVzRGV0YWlsc1NlY3Rpb24oXG4gIHJlcG9ydDogUmVxdWlyZWQ8UGljazxTY29yZWRSZXBvcnQsICdwbHVnaW5zJyB8ICdjYXRlZ29yaWVzJz4+LFxuKTogTWFya2Rvd25Eb2N1bWVudCB7XG4gIGNvbnN0IHsgY2F0ZWdvcmllcywgcGx1Z2lucyB9ID0gcmVwb3J0O1xuXG4gIHJldHVybiBuZXcgTWFya2Rvd25Eb2N1bWVudCgpXG4gICAgLmhlYWRpbmcoSElFUkFSQ0hZLmxldmVsXzIsICdcdUQ4M0NcdURGRjcgQ2F0ZWdvcmllcycpXG4gICAgLiRmb3JlYWNoKGNhdGVnb3JpZXMsIChkb2MsIGNhdGVnb3J5KSA9PlxuICAgICAgZG9jXG4gICAgICAgIC5oZWFkaW5nKEhJRVJBUkNIWS5sZXZlbF8zLCBjYXRlZ29yeS50aXRsZSlcbiAgICAgICAgLnBhcmFncmFwaChtZXRhRGVzY3JpcHRpb24oY2F0ZWdvcnkpKVxuICAgICAgICAucGFyYWdyYXBoKFxuICAgICAgICAgIG1kYCR7c2NvcmVNYXJrZXIoY2F0ZWdvcnkuc2NvcmUpfSBTY29yZTogJHttZC5ib2xkKFxuICAgICAgICAgICAgZm9ybWF0UmVwb3J0U2NvcmUoY2F0ZWdvcnkuc2NvcmUpLFxuICAgICAgICAgICl9JHtiaW5hcnlJY29uU3VmZml4KGNhdGVnb3J5LnNjb3JlLCBjYXRlZ29yeS5pc0JpbmFyeSl9YCxcbiAgICAgICAgKVxuICAgICAgICAubGlzdChcbiAgICAgICAgICBjYXRlZ29yeS5yZWZzLm1hcChyZWYgPT4ge1xuICAgICAgICAgICAgLy8gQWRkIGdyb3VwIGRldGFpbHNcbiAgICAgICAgICAgIGlmIChyZWYudHlwZSA9PT0gJ2dyb3VwJykge1xuICAgICAgICAgICAgICBjb25zdCBncm91cCA9IGdldFNvcnRhYmxlR3JvdXBCeVJlZihyZWYsIHBsdWdpbnMpO1xuICAgICAgICAgICAgICBjb25zdCBncm91cEF1ZGl0cyA9IGdyb3VwLnJlZnMubWFwKGdyb3VwUmVmID0+XG4gICAgICAgICAgICAgICAgZ2V0U29ydGFibGVBdWRpdEJ5UmVmKFxuICAgICAgICAgICAgICAgICAgeyAuLi5ncm91cFJlZiwgcGx1Z2luOiBncm91cC5wbHVnaW4sIHR5cGU6ICdhdWRpdCcgfSxcbiAgICAgICAgICAgICAgICAgIHBsdWdpbnMsXG4gICAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgY29uc3QgcGx1Z2luVGl0bGUgPSBnZXRQbHVnaW5OYW1lRnJvbVNsdWcocmVmLnBsdWdpbiwgcGx1Z2lucyk7XG4gICAgICAgICAgICAgIHJldHVybiBjYXRlZ29yeUdyb3VwSXRlbShncm91cCwgZ3JvdXBBdWRpdHMsIHBsdWdpblRpdGxlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIEFkZCBhdWRpdCBkZXRhaWxzXG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgY29uc3QgYXVkaXQgPSBnZXRTb3J0YWJsZUF1ZGl0QnlSZWYocmVmLCBwbHVnaW5zKTtcbiAgICAgICAgICAgICAgY29uc3QgcGx1Z2luVGl0bGUgPSBnZXRQbHVnaW5OYW1lRnJvbVNsdWcocmVmLnBsdWdpbiwgcGx1Z2lucyk7XG4gICAgICAgICAgICAgIHJldHVybiBjYXRlZ29yeVJlZihhdWRpdCwgcGx1Z2luVGl0bGUpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pLFxuICAgICAgICApLFxuICAgICk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjYXRlZ29yeVJlZihcbiAgeyB0aXRsZSwgc2NvcmUsIHZhbHVlLCBkaXNwbGF5VmFsdWUgfTogQXVkaXRSZXBvcnQsXG4gIHBsdWdpblRpdGxlOiBzdHJpbmcsXG4pOiBJbmxpbmVUZXh0IHtcbiAgY29uc3QgYXVkaXRUaXRsZUFzTGluayA9IG1kLmxpbmsoXG4gICAgYCMke3NsdWdpZnkodGl0bGUpfS0ke3NsdWdpZnkocGx1Z2luVGl0bGUpfWAsXG4gICAgdGl0bGUsXG4gICk7XG4gIGNvbnN0IG1hcmtlciA9IHNjb3JlTWFya2VyKHNjb3JlLCAnc3F1YXJlJyk7XG4gIHJldHVybiBtZGAke21hcmtlcn0gJHthdWRpdFRpdGxlQXNMaW5rfSAoJHttZC5pdGFsaWMoXG4gICAgcGx1Z2luVGl0bGUsXG4gICl9KSAtICR7bWQuYm9sZCgoZGlzcGxheVZhbHVlIHx8IHZhbHVlKS50b1N0cmluZygpKX1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY2F0ZWdvcnlHcm91cEl0ZW0oXG4gIHsgc2NvcmUgPSAwLCB0aXRsZSB9OiBTY29yZWRHcm91cCxcbiAgZ3JvdXBBdWRpdHM6IEF1ZGl0UmVwb3J0W10sXG4gIHBsdWdpblRpdGxlOiBzdHJpbmcsXG4pOiBJbmxpbmVUZXh0IHtcbiAgY29uc3QgZ3JvdXBUaXRsZSA9IG1kYCR7c2NvcmVNYXJrZXIoc2NvcmUpfSAke3RpdGxlfSAoJHttZC5pdGFsaWMoXG4gICAgcGx1Z2luVGl0bGUsXG4gICl9KWA7XG5cbiAgY29uc3QgYXVkaXRzTGlzdCA9IG1kLmxpc3QoXG4gICAgZ3JvdXBBdWRpdHMubWFwKFxuICAgICAgKHsgdGl0bGU6IGF1ZGl0VGl0bGUsIHNjb3JlOiBhdWRpdFNjb3JlLCB2YWx1ZSwgZGlzcGxheVZhbHVlIH0pID0+IHtcbiAgICAgICAgY29uc3QgYXVkaXRUaXRsZUxpbmsgPSBtZC5saW5rKFxuICAgICAgICAgIGAjJHtzbHVnaWZ5KGF1ZGl0VGl0bGUpfS0ke3NsdWdpZnkocGx1Z2luVGl0bGUpfWAsXG4gICAgICAgICAgYXVkaXRUaXRsZSxcbiAgICAgICAgKTtcbiAgICAgICAgY29uc3QgbWFya2VyID0gc2NvcmVNYXJrZXIoYXVkaXRTY29yZSwgJ3NxdWFyZScpO1xuICAgICAgICByZXR1cm4gbWRgJHttYXJrZXJ9ICR7YXVkaXRUaXRsZUxpbmt9IC0gJHttZC5ib2xkKFxuICAgICAgICAgIFN0cmluZyhkaXNwbGF5VmFsdWUgPz8gdmFsdWUpLFxuICAgICAgICApfWA7XG4gICAgICB9LFxuICAgICksXG4gICk7XG5cbiAgcmV0dXJuIG1kYCR7Z3JvdXBUaXRsZX0ke2F1ZGl0c0xpc3R9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGJpbmFyeUljb25TdWZmaXgoXG4gIHNjb3JlOiBudW1iZXIsXG4gIGlzQmluYXJ5OiBib29sZWFuIHwgdW5kZWZpbmVkLFxuKTogc3RyaW5nIHtcbiAgLy8gQFRPRE8gcmVmYWN0b3IgYGlzQmluYXJ5OiBib29sZWFuYCB0byBgdGFyZ2V0U2NvcmU6IG51bWJlcmAgIzcxM1xuICByZXR1cm4gdGFyZ2V0U2NvcmVJY29uKHNjb3JlLCBpc0JpbmFyeSA/IDEgOiB1bmRlZmluZWQsIHsgcHJlZml4OiAnICcgfSk7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3JlcG9ydHMvZ2VuZXJhdGUtbWQtcmVwb3J0cy1kaWZmLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0c1wiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3JlcG9ydHMvZ2VuZXJhdGUtbWQtcmVwb3J0cy1kaWZmLnRzXCI7aW1wb3J0IHtcbiAgdHlwZSBIZWFkaW5nTGV2ZWwsXG4gIE1hcmtkb3duRG9jdW1lbnQsXG4gIHR5cGUgVGFibGVDb2x1bW5PYmplY3QsXG4gIHR5cGUgVGFibGVSb3csXG4gIG1kLFxufSBmcm9tICdidWlsZC1tZCc7XG5pbXBvcnQgdHlwZSB7IFJlcG9ydHNEaWZmIH0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5pbXBvcnQgeyBISUVSQVJDSFkgfSBmcm9tICcuLi90ZXh0LWZvcm1hdHMvaW5kZXguanMnO1xuaW1wb3J0IHsgdG9BcnJheSB9IGZyb20gJy4uL3RyYW5zZm9ybS5qcyc7XG5pbXBvcnQgdHlwZSB7IFdpdGhSZXF1aXJlZCB9IGZyb20gJy4uL3R5cGVzLmpzJztcbmltcG9ydCB7XG4gIGNoYW5nZXNUb0RpZmZPdXRjb21lcyxcbiAgY29tcGFyZURpZmZzQnksXG4gIGNyZWF0ZUdyb3Vwc09yQXVkaXRzRGV0YWlscyxcbiAgZm9ybWF0UG9ydGFsTGluayxcbiAgZm9ybWF0UmVwb3J0T3V0Y29tZSxcbiAgZm9ybWF0VGl0bGUsXG4gIGdldERpZmZDaGFuZ2VzLFxuICBtZXJnZURpZmZPdXRjb21lcyxcbiAgc29ydENoYW5nZXMsXG4gIHN1bW1hcml6ZURpZmZPdXRjb21lcyxcbiAgc3VtbWFyaXplVW5jaGFuZ2VkLFxufSBmcm9tICcuL2dlbmVyYXRlLW1kLXJlcG9ydHMtZGlmZi11dGlscy5qcyc7XG5pbXBvcnQgdHlwZSB7IERpZmZPdXRjb21lIH0gZnJvbSAnLi90eXBlcy5qcyc7XG5pbXBvcnQge1xuICBmb3JtYXRTY29yZUNoYW5nZSxcbiAgZm9ybWF0U2NvcmVXaXRoQ29sb3IsXG4gIGZvcm1hdFZhbHVlQ2hhbmdlLFxuICBzY29yZU1hcmtlcixcbn0gZnJvbSAnLi91dGlscy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZW5lcmF0ZU1kUmVwb3J0c0RpZmYoZGlmZjogUmVwb3J0c0RpZmYpOiBzdHJpbmcge1xuICByZXR1cm4gbmV3IE1hcmtkb3duRG9jdW1lbnQoKVxuICAgIC4kY29uY2F0KFxuICAgICAgY3JlYXRlRGlmZkhlYWRlclNlY3Rpb24oZGlmZiksXG4gICAgICBjcmVhdGVEaWZmQ2F0ZWdvcmllc1NlY3Rpb24oZGlmZiksXG4gICAgICBjcmVhdGVEaWZmRGV0YWlsc1NlY3Rpb24oZGlmZiksXG4gICAgKVxuICAgIC50b1N0cmluZygpO1xufVxuXG5leHBvcnQgdHlwZSBMYWJlbGVkRGlmZiA9IFdpdGhSZXF1aXJlZDxSZXBvcnRzRGlmZiwgJ2xhYmVsJz47XG5cbmV4cG9ydCBmdW5jdGlvbiBnZW5lcmF0ZU1kUmVwb3J0c0RpZmZGb3JNb25vcmVwbyhkaWZmczogTGFiZWxlZERpZmZbXSk6IHN0cmluZyB7XG4gIGNvbnN0IGRpZmZzV2l0aE91dGNvbWVzID0gZGlmZnNcbiAgICAubWFwKGRpZmYgPT4gKHtcbiAgICAgIC4uLmRpZmYsXG4gICAgICBvdXRjb21lOiBtZXJnZURpZmZPdXRjb21lcyhjaGFuZ2VzVG9EaWZmT3V0Y29tZXMoZ2V0RGlmZkNoYW5nZXMoZGlmZikpKSxcbiAgICB9KSlcbiAgICAuc29ydChcbiAgICAgIChhLCBiKSA9PlxuICAgICAgICBjb21wYXJlRGlmZnNCeSgnY2F0ZWdvcmllcycsIGEsIGIpIHx8XG4gICAgICAgIGNvbXBhcmVEaWZmc0J5KCdncm91cHMnLCBhLCBiKSB8fFxuICAgICAgICBjb21wYXJlRGlmZnNCeSgnYXVkaXRzJywgYSwgYikgfHxcbiAgICAgICAgYS5sYWJlbC5sb2NhbGVDb21wYXJlKGIubGFiZWwpLFxuICAgICk7XG4gIGNvbnN0IHVuY2hhbmdlZCA9IGRpZmZzV2l0aE91dGNvbWVzLmZpbHRlcihcbiAgICAoeyBvdXRjb21lIH0pID0+IG91dGNvbWUgPT09ICd1bmNoYW5nZWQnLFxuICApO1xuICBjb25zdCBjaGFuZ2VkID0gZGlmZnNXaXRoT3V0Y29tZXMuZmlsdGVyKGRpZmYgPT4gIXVuY2hhbmdlZC5pbmNsdWRlcyhkaWZmKSk7XG5cbiAgcmV0dXJuIG5ldyBNYXJrZG93bkRvY3VtZW50KClcbiAgICAuJGNvbmNhdChcbiAgICAgIGNyZWF0ZURpZmZIZWFkZXJTZWN0aW9uKGRpZmZzKSxcbiAgICAgIC4uLmNoYW5nZWQubWFwKGNyZWF0ZURpZmZQcm9qZWN0U2VjdGlvbiksXG4gICAgKVxuICAgIC4kaWYodW5jaGFuZ2VkLmxlbmd0aCA+IDAsIGRvYyA9PlxuICAgICAgZG9jXG4gICAgICAgIC5ydWxlKClcbiAgICAgICAgLnBhcmFncmFwaChzdW1tYXJpemVVbmNoYW5nZWQoJ3Byb2plY3QnLCB7IHVuY2hhbmdlZCwgY2hhbmdlZCB9KSksXG4gICAgKVxuICAgIC50b1N0cmluZygpO1xufVxuXG5mdW5jdGlvbiBjcmVhdGVEaWZmSGVhZGVyU2VjdGlvbihcbiAgZGlmZjogUmVwb3J0c0RpZmYgfCBSZXBvcnRzRGlmZltdLFxuKTogTWFya2Rvd25Eb2N1bWVudCB7XG4gIGNvbnN0IG91dGNvbWUgPSBtZXJnZURpZmZPdXRjb21lcyhcbiAgICBjaGFuZ2VzVG9EaWZmT3V0Y29tZXModG9BcnJheShkaWZmKS5mbGF0TWFwKGdldERpZmZDaGFuZ2VzKSksXG4gICk7XG4gIC8vIFRPRE86IHdoYXQgaWYgYXJyYXkgY29udGFpbnMgZGlmZmVyZW50IGNvbW1pdCBwYWlycz9cbiAgY29uc3QgY29tbWl0cyA9IEFycmF5LmlzQXJyYXkoZGlmZikgPyBkaWZmWzBdPy5jb21taXRzIDogZGlmZi5jb21taXRzO1xuICBjb25zdCBwb3J0YWxVcmwgPSBBcnJheS5pc0FycmF5KGRpZmYpID8gdW5kZWZpbmVkIDogZGlmZi5wb3J0YWxVcmw7XG5cbiAgcmV0dXJuIG5ldyBNYXJrZG93bkRvY3VtZW50KClcbiAgICAuaGVhZGluZyhISUVSQVJDSFkubGV2ZWxfMSwgJ0NvZGUgUHVzaFVwJylcbiAgICAucGFyYWdyYXBoKGZvcm1hdFJlcG9ydE91dGNvbWUob3V0Y29tZSwgY29tbWl0cykpXG4gICAgLnBhcmFncmFwaChmb3JtYXRQb3J0YWxMaW5rKHBvcnRhbFVybCkpO1xufVxuXG5mdW5jdGlvbiBjcmVhdGVEaWZmUHJvamVjdFNlY3Rpb24oXG4gIGRpZmY6IExhYmVsZWREaWZmICYgeyBvdXRjb21lOiBEaWZmT3V0Y29tZSB9LFxuKTogTWFya2Rvd25Eb2N1bWVudCB7XG4gIHJldHVybiBuZXcgTWFya2Rvd25Eb2N1bWVudCgpXG4gICAgLmhlYWRpbmcoSElFUkFSQ0hZLmxldmVsXzIsIG1kYFx1RDgzRFx1RENCQyBQcm9qZWN0ICR7bWQuY29kZShkaWZmLmxhYmVsKX1gKVxuICAgIC5wYXJhZ3JhcGgoZm9ybWF0UmVwb3J0T3V0Y29tZShkaWZmLm91dGNvbWUpKVxuICAgIC5wYXJhZ3JhcGgoZm9ybWF0UG9ydGFsTGluayhkaWZmLnBvcnRhbFVybCkpXG4gICAgLiRjb25jYXQoXG4gICAgICBjcmVhdGVEaWZmQ2F0ZWdvcmllc1NlY3Rpb24oZGlmZiwge1xuICAgICAgICBza2lwSGVhZGluZzogdHJ1ZSxcbiAgICAgICAgc2tpcFVuY2hhbmdlZDogdHJ1ZSxcbiAgICAgIH0pLFxuICAgICAgY3JlYXRlRGlmZkRldGFpbHNTZWN0aW9uKGRpZmYsIEhJRVJBUkNIWS5sZXZlbF8zKSxcbiAgICApO1xufVxuXG5mdW5jdGlvbiBjcmVhdGVEaWZmQ2F0ZWdvcmllc1NlY3Rpb24oXG4gIGRpZmY6IFJlcG9ydHNEaWZmLFxuICBvcHRpb25zPzogeyBza2lwSGVhZGluZz86IGJvb2xlYW47IHNraXBVbmNoYW5nZWQ/OiBib29sZWFuIH0sXG4pOiBNYXJrZG93bkRvY3VtZW50IHwgbnVsbCB7XG4gIGNvbnN0IHsgY2hhbmdlZCwgdW5jaGFuZ2VkLCBhZGRlZCB9ID0gZGlmZi5jYXRlZ29yaWVzO1xuICBjb25zdCB7IHNraXBIZWFkaW5nLCBza2lwVW5jaGFuZ2VkIH0gPSBvcHRpb25zID8/IHt9O1xuXG4gIGNvbnN0IGNhdGVnb3JpZXNDb3VudCA9IGNoYW5nZWQubGVuZ3RoICsgdW5jaGFuZ2VkLmxlbmd0aCArIGFkZGVkLmxlbmd0aDtcbiAgY29uc3QgaGFzQ2hhbmdlcyA9IHVuY2hhbmdlZC5sZW5ndGggPCBjYXRlZ29yaWVzQ291bnQ7XG5cbiAgaWYgKGNhdGVnb3JpZXNDb3VudCA9PT0gMCkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgY29uc3QgW2NvbHVtbnMsIHJvd3NdID0gY3JlYXRlQ2F0ZWdvcmllc1RhYmxlKGRpZmYsIHtcbiAgICBoYXNDaGFuZ2VzLFxuICAgIHNraXBVbmNoYW5nZWQsXG4gIH0pO1xuXG4gIHJldHVybiBuZXcgTWFya2Rvd25Eb2N1bWVudCgpXG4gICAgLmhlYWRpbmcoSElFUkFSQ0hZLmxldmVsXzIsICFza2lwSGVhZGluZyAmJiAnXHVEODNDXHVERkY3XHVGRTBGIENhdGVnb3JpZXMnKVxuICAgIC50YWJsZShjb2x1bW5zLCByb3dzKVxuICAgIC5wYXJhZ3JhcGgoYWRkZWQubGVuZ3RoID4gMCAmJiBtZC5pdGFsaWMoU3RyaW5nLnJhd2AoXFwqKSBOZXcgY2F0ZWdvcnkuYCkpXG4gICAgLnBhcmFncmFwaChcbiAgICAgIHNraXBVbmNoYW5nZWQgJiZcbiAgICAgICAgdW5jaGFuZ2VkLmxlbmd0aCA+IDAgJiZcbiAgICAgICAgc3VtbWFyaXplVW5jaGFuZ2VkKCdjYXRlZ29yeScsIHsgY2hhbmdlZCwgdW5jaGFuZ2VkIH0pLFxuICAgICk7XG59XG5cbmZ1bmN0aW9uIGNyZWF0ZUNhdGVnb3JpZXNUYWJsZShcbiAgZGlmZjogUmVwb3J0c0RpZmYsXG4gIG9wdGlvbnM6IHsgaGFzQ2hhbmdlczogYm9vbGVhbjsgc2tpcFVuY2hhbmdlZD86IGJvb2xlYW4gfSxcbik6IFBhcmFtZXRlcnM8TWFya2Rvd25Eb2N1bWVudFsndGFibGUnXT4ge1xuICBjb25zdCB7IGNoYW5nZWQsIHVuY2hhbmdlZCwgYWRkZWQgfSA9IGRpZmYuY2F0ZWdvcmllcztcbiAgY29uc3QgeyBoYXNDaGFuZ2VzLCBza2lwVW5jaGFuZ2VkIH0gPSBvcHRpb25zO1xuXG4gIGNvbnN0IHJvd3M6IFRhYmxlUm93W10gPSBbXG4gICAgLi4uc29ydENoYW5nZXMoY2hhbmdlZCkubWFwKGNhdGVnb3J5ID0+IFtcbiAgICAgIGZvcm1hdFRpdGxlKGNhdGVnb3J5KSxcbiAgICAgIGZvcm1hdFNjb3JlV2l0aENvbG9yKGNhdGVnb3J5LnNjb3Jlcy5iZWZvcmUsIHtcbiAgICAgICAgc2tpcEJvbGQ6IHRydWUsXG4gICAgICB9KSxcbiAgICAgIGZvcm1hdFNjb3JlV2l0aENvbG9yKGNhdGVnb3J5LnNjb3Jlcy5hZnRlciksXG4gICAgICBmb3JtYXRTY29yZUNoYW5nZShjYXRlZ29yeS5zY29yZXMuZGlmZiksXG4gICAgXSksXG4gICAgLi4uYWRkZWQubWFwKGNhdGVnb3J5ID0+IFtcbiAgICAgIGZvcm1hdFRpdGxlKGNhdGVnb3J5KSxcbiAgICAgIG1kLml0YWxpYyhTdHJpbmcucmF3YG4vYSAoXFwqKWApLFxuICAgICAgZm9ybWF0U2NvcmVXaXRoQ29sb3IoY2F0ZWdvcnkuc2NvcmUpLFxuICAgICAgbWQuaXRhbGljKFN0cmluZy5yYXdgbi9hIChcXCopYCksXG4gICAgXSksXG4gICAgLi4uKHNraXBVbmNoYW5nZWRcbiAgICAgID8gW11cbiAgICAgIDogdW5jaGFuZ2VkLm1hcChjYXRlZ29yeSA9PiBbXG4gICAgICAgICAgZm9ybWF0VGl0bGUoY2F0ZWdvcnkpLFxuICAgICAgICAgIGZvcm1hdFNjb3JlV2l0aENvbG9yKGNhdGVnb3J5LnNjb3JlLCB7IHNraXBCb2xkOiB0cnVlIH0pLFxuICAgICAgICAgIGZvcm1hdFNjb3JlV2l0aENvbG9yKGNhdGVnb3J5LnNjb3JlKSxcbiAgICAgICAgICAnXHUyMDEzJyxcbiAgICAgICAgXSkpLFxuICBdO1xuXG4gIGlmIChyb3dzLmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybiBbW10sIFtdXTtcbiAgfVxuXG4gIGNvbnN0IGNvbHVtbnM6IFRhYmxlQ29sdW1uT2JqZWN0W10gPSBbXG4gICAgeyBoZWFkaW5nOiAnXHVEODNDXHVERkY3XHVGRTBGIENhdGVnb3J5JywgYWxpZ25tZW50OiAnbGVmdCcgfSxcbiAgICB7XG4gICAgICBoZWFkaW5nOiBoYXNDaGFuZ2VzID8gJ1x1MkI1MCBQcmV2aW91cyBzY29yZScgOiAnXHUyQjUwIFNjb3JlJyxcbiAgICAgIGFsaWdubWVudDogJ2NlbnRlcicsXG4gICAgfSxcbiAgICB7IGhlYWRpbmc6ICdcdTJCNTAgQ3VycmVudCBzY29yZScsIGFsaWdubWVudDogJ2NlbnRlcicgfSxcbiAgICB7IGhlYWRpbmc6ICdcdUQ4M0RcdUREMDQgU2NvcmUgY2hhbmdlJywgYWxpZ25tZW50OiAnY2VudGVyJyB9LFxuICBdO1xuXG4gIHJldHVybiBbXG4gICAgaGFzQ2hhbmdlcyA/IGNvbHVtbnMgOiBjb2x1bW5zLnNsaWNlKDAsIDIpLFxuICAgIHJvd3MubWFwKHJvdyA9PiAoaGFzQ2hhbmdlcyA/IHJvdyA6IHJvdy5zbGljZSgwLCAyKSkpLFxuICBdO1xufVxuXG5mdW5jdGlvbiBjcmVhdGVEaWZmRGV0YWlsc1NlY3Rpb24oXG4gIGRpZmY6IFJlcG9ydHNEaWZmLFxuICBsZXZlbDogSGVhZGluZ0xldmVsID0gSElFUkFSQ0hZLmxldmVsXzIsXG4pOiBNYXJrZG93bkRvY3VtZW50IHwgbnVsbCB7XG4gIGlmIChkaWZmLmdyb3Vwcy5jaGFuZ2VkLmxlbmd0aCArIGRpZmYuYXVkaXRzLmNoYW5nZWQubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgY29uc3Qgc3VtbWFyeSA9IChbJ2dyb3VwJywgJ2F1ZGl0J10gYXMgY29uc3QpXG4gICAgLm1hcCh0b2tlbiA9PlxuICAgICAgc3VtbWFyaXplRGlmZk91dGNvbWVzKFxuICAgICAgICBjaGFuZ2VzVG9EaWZmT3V0Y29tZXMoZGlmZltgJHt0b2tlbn1zYF0uY2hhbmdlZCksXG4gICAgICAgIHRva2VuLFxuICAgICAgKSxcbiAgICApXG4gICAgLmZpbHRlcihCb29sZWFuKVxuICAgIC5qb2luKCcsICcpO1xuICBjb25zdCBkZXRhaWxzID0gbmV3IE1hcmtkb3duRG9jdW1lbnQoKS4kY29uY2F0KFxuICAgIGNyZWF0ZURpZmZHcm91cHNTZWN0aW9uKGRpZmYsIGxldmVsKSxcbiAgICBjcmVhdGVEaWZmQXVkaXRzU2VjdGlvbihkaWZmLCBsZXZlbCksXG4gICk7XG4gIHJldHVybiBuZXcgTWFya2Rvd25Eb2N1bWVudCgpLmRldGFpbHMoc3VtbWFyeSwgZGV0YWlscyk7XG59XG5cbmZ1bmN0aW9uIGNyZWF0ZURpZmZHcm91cHNTZWN0aW9uKFxuICBkaWZmOiBSZXBvcnRzRGlmZixcbiAgbGV2ZWw6IEhlYWRpbmdMZXZlbCxcbik6IE1hcmtkb3duRG9jdW1lbnQgfCBudWxsIHtcbiAgaWYgKGRpZmYuZ3JvdXBzLmNoYW5nZWQubGVuZ3RoICsgZGlmZi5ncm91cHMudW5jaGFuZ2VkLmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG4gIHJldHVybiBuZXcgTWFya2Rvd25Eb2N1bWVudCgpLmhlYWRpbmcobGV2ZWwsICdcdUQ4M0RcdUREQzNcdUZFMEYgR3JvdXBzJykuJGNvbmNhdChcbiAgICBjcmVhdGVHcm91cHNPckF1ZGl0c0RldGFpbHMoXG4gICAgICAnZ3JvdXAnLFxuICAgICAgZGlmZi5ncm91cHMsXG4gICAgICBbXG4gICAgICAgIHsgaGVhZGluZzogJ1x1RDgzRFx1REQwQyBQbHVnaW4nLCBhbGlnbm1lbnQ6ICdsZWZ0JyB9LFxuICAgICAgICB7IGhlYWRpbmc6ICdcdUQ4M0RcdUREQzNcdUZFMEYgR3JvdXAnLCBhbGlnbm1lbnQ6ICdsZWZ0JyB9LFxuICAgICAgICB7IGhlYWRpbmc6ICdcdTJCNTAgUHJldmlvdXMgc2NvcmUnLCBhbGlnbm1lbnQ6ICdjZW50ZXInIH0sXG4gICAgICAgIHsgaGVhZGluZzogJ1x1MkI1MCBDdXJyZW50IHNjb3JlJywgYWxpZ25tZW50OiAnY2VudGVyJyB9LFxuICAgICAgICB7IGhlYWRpbmc6ICdcdUQ4M0RcdUREMDQgU2NvcmUgY2hhbmdlJywgYWxpZ25tZW50OiAnY2VudGVyJyB9LFxuICAgICAgXSxcbiAgICAgIHNvcnRDaGFuZ2VzKGRpZmYuZ3JvdXBzLmNoYW5nZWQpLm1hcChncm91cCA9PiBbXG4gICAgICAgIGZvcm1hdFRpdGxlKGdyb3VwLnBsdWdpbiksXG4gICAgICAgIGZvcm1hdFRpdGxlKGdyb3VwKSxcbiAgICAgICAgZm9ybWF0U2NvcmVXaXRoQ29sb3IoZ3JvdXAuc2NvcmVzLmJlZm9yZSwgeyBza2lwQm9sZDogdHJ1ZSB9KSxcbiAgICAgICAgZm9ybWF0U2NvcmVXaXRoQ29sb3IoZ3JvdXAuc2NvcmVzLmFmdGVyKSxcbiAgICAgICAgZm9ybWF0U2NvcmVDaGFuZ2UoZ3JvdXAuc2NvcmVzLmRpZmYpLFxuICAgICAgXSksXG4gICAgKSxcbiAgKTtcbn1cblxuZnVuY3Rpb24gY3JlYXRlRGlmZkF1ZGl0c1NlY3Rpb24oXG4gIGRpZmY6IFJlcG9ydHNEaWZmLFxuICBsZXZlbDogSGVhZGluZ0xldmVsLFxuKTogTWFya2Rvd25Eb2N1bWVudCB7XG4gIHJldHVybiBuZXcgTWFya2Rvd25Eb2N1bWVudCgpLmhlYWRpbmcobGV2ZWwsICdcdUQ4M0RcdURFRTFcdUZFMEYgQXVkaXRzJykuJGNvbmNhdChcbiAgICBjcmVhdGVHcm91cHNPckF1ZGl0c0RldGFpbHMoXG4gICAgICAnYXVkaXQnLFxuICAgICAgZGlmZi5hdWRpdHMsXG4gICAgICBbXG4gICAgICAgIHsgaGVhZGluZzogJ1x1RDgzRFx1REQwQyBQbHVnaW4nLCBhbGlnbm1lbnQ6ICdsZWZ0JyB9LFxuICAgICAgICB7IGhlYWRpbmc6ICdcdUQ4M0RcdURFRTFcdUZFMEYgQXVkaXQnLCBhbGlnbm1lbnQ6ICdsZWZ0JyB9LFxuICAgICAgICB7IGhlYWRpbmc6ICdcdUQ4M0RcdURDQ0YgUHJldmlvdXMgdmFsdWUnLCBhbGlnbm1lbnQ6ICdjZW50ZXInIH0sXG4gICAgICAgIHsgaGVhZGluZzogJ1x1RDgzRFx1RENDRiBDdXJyZW50IHZhbHVlJywgYWxpZ25tZW50OiAnY2VudGVyJyB9LFxuICAgICAgICB7IGhlYWRpbmc6ICdcdUQ4M0RcdUREMDQgVmFsdWUgY2hhbmdlJywgYWxpZ25tZW50OiAnY2VudGVyJyB9LFxuICAgICAgXSxcbiAgICAgIHNvcnRDaGFuZ2VzKGRpZmYuYXVkaXRzLmNoYW5nZWQpLm1hcChhdWRpdCA9PiBbXG4gICAgICAgIGZvcm1hdFRpdGxlKGF1ZGl0LnBsdWdpbiksXG4gICAgICAgIGZvcm1hdFRpdGxlKGF1ZGl0KSxcbiAgICAgICAgYCR7c2NvcmVNYXJrZXIoYXVkaXQuc2NvcmVzLmJlZm9yZSwgJ3NxdWFyZScpfSAke1xuICAgICAgICAgIGF1ZGl0LmRpc3BsYXlWYWx1ZXMuYmVmb3JlIHx8IGF1ZGl0LnZhbHVlcy5iZWZvcmUudG9TdHJpbmcoKVxuICAgICAgICB9YCxcbiAgICAgICAgbWRgJHtzY29yZU1hcmtlcihhdWRpdC5zY29yZXMuYWZ0ZXIsICdzcXVhcmUnKX0gJHttZC5ib2xkKFxuICAgICAgICAgIGF1ZGl0LmRpc3BsYXlWYWx1ZXMuYWZ0ZXIgfHwgYXVkaXQudmFsdWVzLmFmdGVyLnRvU3RyaW5nKCksXG4gICAgICAgICl9YCxcbiAgICAgICAgZm9ybWF0VmFsdWVDaGFuZ2UoYXVkaXQpLFxuICAgICAgXSksXG4gICAgKSxcbiAgKTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0cy9nZW5lcmF0ZS1tZC1yZXBvcnRzLWRpZmYtdXRpbHMudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9yZXBvcnRzXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0cy9nZW5lcmF0ZS1tZC1yZXBvcnRzLWRpZmYtdXRpbHMudHNcIjtpbXBvcnQgeyB0eXBlIElubGluZVRleHQsIE1hcmtkb3duRG9jdW1lbnQsIG1kIH0gZnJvbSAnYnVpbGQtbWQnO1xuaW1wb3J0IHR5cGUgeyBSZXBvcnRzRGlmZiB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgcGx1cmFsaXplLCBwbHVyYWxpemVUb2tlbiB9IGZyb20gJy4uL2Zvcm1hdHRpbmcuanMnO1xuaW1wb3J0IHsgb2JqZWN0VG9FbnRyaWVzIH0gZnJvbSAnLi4vdHJhbnNmb3JtLmpzJztcbmltcG9ydCB0eXBlIHsgRGlmZk91dGNvbWUgfSBmcm9tICcuL3R5cGVzLmpzJztcblxuLy8gdG8gcHJldmVudCBleGNlZWRpbmcgTWFya2Rvd24gY29tbWVudCBjaGFyYWN0ZXIgbGltaXRcbmNvbnN0IE1BWF9ST1dTID0gMTAwO1xuXG5leHBvcnQgZnVuY3Rpb24gc3VtbWFyaXplVW5jaGFuZ2VkKFxuICB0b2tlbjogc3RyaW5nLFxuICB7IGNoYW5nZWQsIHVuY2hhbmdlZCB9OiB7IGNoYW5nZWQ6IHVua25vd25bXTsgdW5jaGFuZ2VkOiB1bmtub3duW10gfSxcbik6IHN0cmluZyB7XG4gIGNvbnN0IHBsdXJhbGl6ZWRDb3VudCA9XG4gICAgY2hhbmdlZC5sZW5ndGggPiAwXG4gICAgICA/IHBsdXJhbGl6ZVRva2VuKGBvdGhlciAke3Rva2VufWAsIHVuY2hhbmdlZC5sZW5ndGgpXG4gICAgICA6IGBBbGwgb2YgJHtwbHVyYWxpemVUb2tlbih0b2tlbiwgdW5jaGFuZ2VkLmxlbmd0aCl9YDtcbiAgY29uc3QgcGx1cmFsaXplZFZlcmIgPSB1bmNoYW5nZWQubGVuZ3RoID09PSAxID8gJ2lzJyA6ICdhcmUnO1xuICByZXR1cm4gYCR7cGx1cmFsaXplZENvdW50fSAke3BsdXJhbGl6ZWRWZXJifSB1bmNoYW5nZWQuYDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHN1bW1hcml6ZURpZmZPdXRjb21lcyhcbiAgb3V0Y29tZXM6IERpZmZPdXRjb21lW10sXG4gIHRva2VuOiBzdHJpbmcsXG4pOiBzdHJpbmcge1xuICByZXR1cm4gb2JqZWN0VG9FbnRyaWVzKGNvdW50RGlmZk91dGNvbWVzKG91dGNvbWVzKSlcbiAgICAuZmlsdGVyKFxuICAgICAgKGVudHJ5KTogZW50cnkgaXMgW0V4Y2x1ZGU8RGlmZk91dGNvbWUsICd1bmNoYW5nZWQnPiwgbnVtYmVyXSA9PlxuICAgICAgICBlbnRyeVswXSAhPT0gJ3VuY2hhbmdlZCcgJiYgZW50cnlbMV0gPiAwLFxuICAgIClcbiAgICAubWFwKChbb3V0Y29tZSwgY291bnRdKTogc3RyaW5nID0+IHtcbiAgICAgIGNvbnN0IGZvcm1hdHRlZENvdW50ID0gYDxzdHJvbmc+JHtjb3VudH08L3N0cm9uZz4gJHtwbHVyYWxpemUoXG4gICAgICAgIHRva2VuLFxuICAgICAgICBjb3VudCxcbiAgICAgICl9YDtcbiAgICAgIHN3aXRjaCAob3V0Y29tZSkge1xuICAgICAgICBjYXNlICdwb3NpdGl2ZSc6XG4gICAgICAgICAgcmV0dXJuIGBcdUQ4M0RcdURDNEQgJHtmb3JtYXR0ZWRDb3VudH0gaW1wcm92ZWRgO1xuICAgICAgICBjYXNlICduZWdhdGl2ZSc6XG4gICAgICAgICAgcmV0dXJuIGBcdUQ4M0RcdURDNEUgJHtmb3JtYXR0ZWRDb3VudH0gcmVncmVzc2VkYDtcbiAgICAgICAgY2FzZSAnbWl4ZWQnOlxuICAgICAgICAgIHJldHVybiBgJHtmb3JtYXR0ZWRDb3VudH0gY2hhbmdlZCB3aXRob3V0IGltcGFjdGluZyBzY29yZWA7XG4gICAgICB9XG4gICAgfSlcbiAgICAuam9pbignLCAnKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUdyb3Vwc09yQXVkaXRzRGV0YWlsczxUIGV4dGVuZHMgJ2dyb3VwJyB8ICdhdWRpdCc+KFxuICB0b2tlbjogVCxcbiAgeyBjaGFuZ2VkLCB1bmNoYW5nZWQgfTogUmVwb3J0c0RpZmZbYCR7VH1zYF0sXG4gIC4uLltjb2x1bW5zLCByb3dzXTogUGFyYW1ldGVyczxNYXJrZG93bkRvY3VtZW50Wyd0YWJsZSddPlxuKTogTWFya2Rvd25Eb2N1bWVudCB7XG4gIGlmIChjaGFuZ2VkLmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybiBuZXcgTWFya2Rvd25Eb2N1bWVudCgpLnBhcmFncmFwaChcbiAgICAgIHN1bW1hcml6ZVVuY2hhbmdlZCh0b2tlbiwgeyBjaGFuZ2VkLCB1bmNoYW5nZWQgfSksXG4gICAgKTtcbiAgfVxuICByZXR1cm4gbmV3IE1hcmtkb3duRG9jdW1lbnQoKVxuICAgIC50YWJsZShjb2x1bW5zLCByb3dzLnNsaWNlKDAsIE1BWF9ST1dTKSlcbiAgICAucGFyYWdyYXBoKFxuICAgICAgY2hhbmdlZC5sZW5ndGggPiBNQVhfUk9XUyAmJlxuICAgICAgICBtZC5pdGFsaWMoXG4gICAgICAgICAgYE9ubHkgdGhlICR7TUFYX1JPV1N9IG1vc3QgYWZmZWN0ZWQgJHtwbHVyYWxpemUoXG4gICAgICAgICAgICB0b2tlbixcbiAgICAgICAgICApfSBhcmUgbGlzdGVkIGFib3ZlIGZvciBicmV2aXR5LmAsXG4gICAgICAgICksXG4gICAgKVxuICAgIC5wYXJhZ3JhcGgoXG4gICAgICB1bmNoYW5nZWQubGVuZ3RoID4gMCAmJiBzdW1tYXJpemVVbmNoYW5nZWQodG9rZW4sIHsgY2hhbmdlZCwgdW5jaGFuZ2VkIH0pLFxuICAgICk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRUaXRsZSh7XG4gIHRpdGxlLFxuICBkb2NzVXJsLFxufToge1xuICB0aXRsZTogc3RyaW5nO1xuICBkb2NzVXJsPzogc3RyaW5nO1xufSk6IElubGluZVRleHQge1xuICBpZiAoZG9jc1VybCkge1xuICAgIHJldHVybiBtZC5saW5rKGRvY3NVcmwsIHRpdGxlKTtcbiAgfVxuICByZXR1cm4gdGl0bGU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRQb3J0YWxMaW5rKFxuICBwb3J0YWxVcmw6IHN0cmluZyB8IHVuZGVmaW5lZCxcbik6IElubGluZVRleHQgfCB1bmRlZmluZWQge1xuICByZXR1cm4gKFxuICAgIHBvcnRhbFVybCAmJlxuICAgIG1kLmxpbmsocG9ydGFsVXJsLCAnXHVEODNEXHVERDc1XHVGRTBGIFNlZSBmdWxsIGNvbXBhcmlzb24gaW4gQ29kZSBQdXNoVXAgcG9ydGFsIFx1RDgzRFx1REQwRCcpXG4gICk7XG59XG5cbnR5cGUgQ2hhbmdlID0ge1xuICBzY29yZXM6IHsgZGlmZjogbnVtYmVyIH07XG4gIHZhbHVlcz86IHsgZGlmZjogbnVtYmVyIH07XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gc29ydENoYW5nZXM8VCBleHRlbmRzIENoYW5nZT4oY2hhbmdlczogVFtdKTogVFtdIHtcbiAgcmV0dXJuIGNoYW5nZXMudG9Tb3J0ZWQoXG4gICAgKGEsIGIpID0+XG4gICAgICBNYXRoLmFicyhiLnNjb3Jlcy5kaWZmKSAtIE1hdGguYWJzKGEuc2NvcmVzLmRpZmYpIHx8XG4gICAgICBNYXRoLmFicyhiLnZhbHVlcz8uZGlmZiA/PyAwKSAtIE1hdGguYWJzKGEudmFsdWVzPy5kaWZmID8/IDApLFxuICApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RGlmZkNoYW5nZXMoZGlmZjogUmVwb3J0c0RpZmYpOiBDaGFuZ2VbXSB7XG4gIHJldHVybiBbXG4gICAgLi4uZGlmZi5jYXRlZ29yaWVzLmNoYW5nZWQsXG4gICAgLi4uZGlmZi5ncm91cHMuY2hhbmdlZCxcbiAgICAuLi5kaWZmLmF1ZGl0cy5jaGFuZ2VkLFxuICBdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY2hhbmdlc1RvRGlmZk91dGNvbWVzKGNoYW5nZXM6IENoYW5nZVtdKTogRGlmZk91dGNvbWVbXSB7XG4gIHJldHVybiBjaGFuZ2VzLm1hcCgoY2hhbmdlKTogRGlmZk91dGNvbWUgPT4ge1xuICAgIGlmIChjaGFuZ2Uuc2NvcmVzLmRpZmYgPiAwKSB7XG4gICAgICByZXR1cm4gJ3Bvc2l0aXZlJztcbiAgICB9XG4gICAgaWYgKGNoYW5nZS5zY29yZXMuZGlmZiA8IDApIHtcbiAgICAgIHJldHVybiAnbmVnYXRpdmUnO1xuICAgIH1cbiAgICBpZiAoY2hhbmdlLnZhbHVlcyAhPSBudWxsICYmIGNoYW5nZS52YWx1ZXMuZGlmZiAhPT0gMCkge1xuICAgICAgcmV0dXJuICdtaXhlZCc7XG4gICAgfVxuICAgIHJldHVybiAndW5jaGFuZ2VkJztcbiAgfSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBtZXJnZURpZmZPdXRjb21lcyhvdXRjb21lczogRGlmZk91dGNvbWVbXSk6IERpZmZPdXRjb21lIHtcbiAgaWYgKG91dGNvbWVzLmV2ZXJ5KG91dGNvbWUgPT4gb3V0Y29tZSA9PT0gJ3VuY2hhbmdlZCcpKSB7XG4gICAgcmV0dXJuICd1bmNoYW5nZWQnO1xuICB9XG4gIGlmIChvdXRjb21lcy5pbmNsdWRlcygncG9zaXRpdmUnKSAmJiAhb3V0Y29tZXMuaW5jbHVkZXMoJ25lZ2F0aXZlJykpIHtcbiAgICByZXR1cm4gJ3Bvc2l0aXZlJztcbiAgfVxuICBpZiAob3V0Y29tZXMuaW5jbHVkZXMoJ25lZ2F0aXZlJykgJiYgIW91dGNvbWVzLmluY2x1ZGVzKCdwb3NpdGl2ZScpKSB7XG4gICAgcmV0dXJuICduZWdhdGl2ZSc7XG4gIH1cbiAgcmV0dXJuICdtaXhlZCc7XG59XG5cbmZ1bmN0aW9uIGNvdW50RGlmZk91dGNvbWVzKFxuICBvdXRjb21lczogRGlmZk91dGNvbWVbXSxcbik6IFJlY29yZDxEaWZmT3V0Y29tZSwgbnVtYmVyPiB7XG4gIHJldHVybiB7XG4gICAgcG9zaXRpdmU6IG91dGNvbWVzLmZpbHRlcihvdXRjb21lID0+IG91dGNvbWUgPT09ICdwb3NpdGl2ZScpLmxlbmd0aCxcbiAgICBuZWdhdGl2ZTogb3V0Y29tZXMuZmlsdGVyKG91dGNvbWUgPT4gb3V0Y29tZSA9PT0gJ25lZ2F0aXZlJykubGVuZ3RoLFxuICAgIG1peGVkOiBvdXRjb21lcy5maWx0ZXIob3V0Y29tZSA9PiBvdXRjb21lID09PSAnbWl4ZWQnKS5sZW5ndGgsXG4gICAgdW5jaGFuZ2VkOiBvdXRjb21lcy5maWx0ZXIob3V0Y29tZSA9PiBvdXRjb21lID09PSAndW5jaGFuZ2VkJykubGVuZ3RoLFxuICB9O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZm9ybWF0UmVwb3J0T3V0Y29tZShcbiAgb3V0Y29tZTogRGlmZk91dGNvbWUsXG4gIGNvbW1pdHM/OiBSZXBvcnRzRGlmZlsnY29tbWl0cyddLFxuKTogSW5saW5lVGV4dCB7XG4gIGNvbnN0IG91dGNvbWVUZXh0cyA9IHtcbiAgICBwb3NpdGl2ZTogbWRgXHVEODNFXHVERDczIENvZGUgUHVzaFVwIHJlcG9ydCBoYXMgJHttZC5ib2xkKCdpbXByb3ZlZCcpfWAsXG4gICAgbmVnYXRpdmU6IG1kYFx1RDgzRFx1REUxRiBDb2RlIFB1c2hVcCByZXBvcnQgaGFzICR7bWQuYm9sZCgncmVncmVzc2VkJyl9YCxcbiAgICBtaXhlZDogbWRgXHVEODNFXHVERDI4IENvZGUgUHVzaFVwIHJlcG9ydCBoYXMgYm90aCAke21kLmJvbGQoXG4gICAgICAnaW1wcm92ZW1lbnRzIGFuZCByZWdyZXNzaW9ucycsXG4gICAgKX1gLFxuICAgIHVuY2hhbmdlZDogbWRgXHVEODNEXHVERTEwIENvZGUgUHVzaFVwIHJlcG9ydCBpcyAke21kLmJvbGQoJ3VuY2hhbmdlZCcpfWAsXG4gIH07XG5cbiAgaWYgKGNvbW1pdHMpIHtcbiAgICBjb25zdCBjb21taXRzVGV4dCA9IGBjb21wYXJlZCB0YXJnZXQgY29tbWl0ICR7Y29tbWl0cy5hZnRlci5oYXNofSB3aXRoIHNvdXJjZSBjb21taXQgJHtjb21taXRzLmJlZm9yZS5oYXNofWA7XG4gICAgcmV0dXJuIG1kYCR7b3V0Y29tZVRleHRzW291dGNvbWVdfSBcdTIwMTMgJHtjb21taXRzVGV4dH0uYDtcbiAgfVxuXG4gIHJldHVybiBtZGAke291dGNvbWVUZXh0c1tvdXRjb21lXX0uYDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbXBhcmVEaWZmc0J5PFQgZXh0ZW5kcyAnY2F0ZWdvcmllcycgfCAnZ3JvdXBzJyB8ICdhdWRpdHMnPihcbiAgdHlwZTogVCxcbiAgYTogUmVwb3J0c0RpZmYsXG4gIGI6IFJlcG9ydHNEaWZmLFxuKTogbnVtYmVyIHtcbiAgcmV0dXJuIChcbiAgICBzdW1TY29yZUNoYW5nZXMoYlt0eXBlXS5jaGFuZ2VkKSAtIHN1bVNjb3JlQ2hhbmdlcyhhW3R5cGVdLmNoYW5nZWQpIHx8XG4gICAgc3VtQ29uZmlnQ2hhbmdlcyhiW3R5cGVdKSAtIHN1bUNvbmZpZ0NoYW5nZXMoYVt0eXBlXSlcbiAgKTtcbn1cblxuZnVuY3Rpb24gc3VtU2NvcmVDaGFuZ2VzKGNoYW5nZXM6IENoYW5nZVtdKTogbnVtYmVyIHtcbiAgcmV0dXJuIGNoYW5nZXMucmVkdWNlPG51bWJlcj4oXG4gICAgKGFjYywgeyBzY29yZXMgfSkgPT4gYWNjICsgTWF0aC5hYnMoc2NvcmVzLmRpZmYpLFxuICAgIDAsXG4gICk7XG59XG5cbmZ1bmN0aW9uIHN1bUNvbmZpZ0NoYW5nZXMoe1xuICBhZGRlZCxcbiAgcmVtb3ZlZCxcbn06IHtcbiAgYWRkZWQ6IHVua25vd25bXTtcbiAgcmVtb3ZlZDogdW5rbm93bltdO1xufSk6IG51bWJlciB7XG4gIHJldHVybiBhZGRlZC5sZW5ndGggKyByZW1vdmVkLmxlbmd0aDtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0cy9sb2FkLXJlcG9ydC50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3JlcG9ydHNcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9yZXBvcnRzL2xvYWQtcmVwb3J0LnRzXCI7aW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB7XG4gIHR5cGUgRm9ybWF0LFxuICB0eXBlIFBlcnNpc3RDb25maWcsXG4gIHR5cGUgUmVwb3J0LFxuICByZXBvcnRTY2hlbWEsXG59IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHtcbiAgZW5zdXJlRGlyZWN0b3J5RXhpc3RzLFxuICByZWFkSnNvbkZpbGUsXG4gIHJlYWRUZXh0RmlsZSxcbn0gZnJvbSAnLi4vZmlsZS1zeXN0ZW0uanMnO1xuXG50eXBlIExvYWRlZFJlcG9ydEZvcm1hdDxUIGV4dGVuZHMgRm9ybWF0PiA9IFQgZXh0ZW5kcyAnanNvbicgPyBSZXBvcnQgOiBzdHJpbmc7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBsb2FkUmVwb3J0PFQgZXh0ZW5kcyBGb3JtYXQ+KFxuICBvcHRpb25zOiBSZXF1aXJlZDxPbWl0PFBlcnNpc3RDb25maWcsICdmb3JtYXQnPj4gJiB7XG4gICAgZm9ybWF0OiBUO1xuICB9LFxuKTogUHJvbWlzZTxMb2FkZWRSZXBvcnRGb3JtYXQ8VD4+IHtcbiAgY29uc3QgeyBvdXRwdXREaXIsIGZpbGVuYW1lLCBmb3JtYXQgfSA9IG9wdGlvbnM7XG4gIGF3YWl0IGVuc3VyZURpcmVjdG9yeUV4aXN0cyhvdXRwdXREaXIpO1xuICBjb25zdCBmaWxlUGF0aCA9IHBhdGguam9pbihvdXRwdXREaXIsIGAke2ZpbGVuYW1lfS4ke2Zvcm1hdH1gKTtcblxuICBpZiAoZm9ybWF0ID09PSAnanNvbicpIHtcbiAgICBjb25zdCBjb250ZW50ID0gYXdhaXQgcmVhZEpzb25GaWxlKGZpbGVQYXRoKTtcbiAgICByZXR1cm4gcmVwb3J0U2NoZW1hLnBhcnNlKGNvbnRlbnQpIGFzIExvYWRlZFJlcG9ydEZvcm1hdDxUPjtcbiAgfVxuXG4gIGNvbnN0IHRleHQgPSBhd2FpdCByZWFkVGV4dEZpbGUoZmlsZVBhdGgpO1xuICByZXR1cm4gdGV4dCBhcyBMb2FkZWRSZXBvcnRGb3JtYXQ8VD47XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3JlcG9ydHMvbG9nLXN0ZG91dC1zdW1tYXJ5LnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0c1wiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3JlcG9ydHMvbG9nLXN0ZG91dC1zdW1tYXJ5LnRzXCI7aW1wb3J0IHsgYm9sZCwgY3lhbiwgY3lhbkJyaWdodCwgZ3JlZW4sIHJlZCB9IGZyb20gJ2Fuc2lzJztcbmltcG9ydCB0eXBlIHsgQXVkaXRSZXBvcnQgfSBmcm9tICdAY29kZS1wdXNodXAvbW9kZWxzJztcbmltcG9ydCB7IHVpIH0gZnJvbSAnLi4vbG9nZ2luZy5qcyc7XG5pbXBvcnQge1xuICBDT0RFX1BVU0hVUF9ET01BSU4sXG4gIEZPT1RFUl9QUkVGSVgsXG4gIFJFUE9SVF9IRUFETElORV9URVhULFxuICBSRVBPUlRfUkFXX09WRVJWSUVXX1RBQkxFX0hFQURFUlMsXG4gIFRFUk1JTkFMX1dJRFRILFxufSBmcm9tICcuL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgdHlwZSB7IFNjb3JlZFJlcG9ydCB9IGZyb20gJy4vdHlwZXMuanMnO1xuaW1wb3J0IHtcbiAgYXBwbHlTY29yZUNvbG9yLFxuICBjb3VudENhdGVnb3J5QXVkaXRzLFxuICB0YXJnZXRTY29yZUljb24sXG59IGZyb20gJy4vdXRpbHMuanMnO1xuXG5mdW5jdGlvbiBsb2cobXNnID0gJycpOiB2b2lkIHtcbiAgdWkoKS5sb2dnZXIubG9nKG1zZyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsb2dTdGRvdXRTdW1tYXJ5KHJlcG9ydDogU2NvcmVkUmVwb3J0LCB2ZXJib3NlID0gZmFsc2UpOiB2b2lkIHtcbiAgY29uc3QgeyBwbHVnaW5zLCBjYXRlZ29yaWVzLCBwYWNrYWdlTmFtZSwgdmVyc2lvbiB9ID0gcmVwb3J0O1xuICBsb2cocmVwb3J0VG9IZWFkZXJTZWN0aW9uKHsgcGFja2FnZU5hbWUsIHZlcnNpb24gfSkpO1xuICBsb2coKTtcbiAgbG9nUGx1Z2lucyhwbHVnaW5zLCB2ZXJib3NlKTtcbiAgaWYgKGNhdGVnb3JpZXMgJiYgY2F0ZWdvcmllcy5sZW5ndGggPiAwKSB7XG4gICAgbG9nQ2F0ZWdvcmllcyh7IHBsdWdpbnMsIGNhdGVnb3JpZXMgfSk7XG4gIH1cbiAgbG9nKGAke0ZPT1RFUl9QUkVGSVh9ICR7Q09ERV9QVVNIVVBfRE9NQUlOfWApO1xuICBsb2coKTtcbn1cblxuZnVuY3Rpb24gcmVwb3J0VG9IZWFkZXJTZWN0aW9uKHtcbiAgcGFja2FnZU5hbWUsXG4gIHZlcnNpb24sXG59OiBQaWNrPFNjb3JlZFJlcG9ydCwgJ3BhY2thZ2VOYW1lJyB8ICd2ZXJzaW9uJz4pOiBzdHJpbmcge1xuICByZXR1cm4gYCR7Ym9sZChSRVBPUlRfSEVBRExJTkVfVEVYVCl9IC0gJHtwYWNrYWdlTmFtZX1AJHt2ZXJzaW9ufWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsb2dQbHVnaW5zKFxuICBwbHVnaW5zOiBTY29yZWRSZXBvcnRbJ3BsdWdpbnMnXSxcbiAgdmVyYm9zZTogYm9vbGVhbixcbik6IHZvaWQge1xuICBwbHVnaW5zLmZvckVhY2gocGx1Z2luID0+IHtcbiAgICBjb25zdCB7IHRpdGxlLCBhdWRpdHMgfSA9IHBsdWdpbjtcbiAgICBjb25zdCBmaWx0ZXJlZEF1ZGl0cyA9XG4gICAgICB2ZXJib3NlIHx8IGF1ZGl0cy5sZW5ndGggPT09IDFcbiAgICAgICAgPyBhdWRpdHNcbiAgICAgICAgOiBhdWRpdHMuZmlsdGVyKCh7IHNjb3JlIH0pID0+IHNjb3JlICE9PSAxKTtcbiAgICBjb25zdCBkaWZmID0gYXVkaXRzLmxlbmd0aCAtIGZpbHRlcmVkQXVkaXRzLmxlbmd0aDtcblxuICAgIGxvZ0F1ZGl0cyh0aXRsZSwgZmlsdGVyZWRBdWRpdHMpO1xuXG4gICAgaWYgKGRpZmYgPiAwKSB7XG4gICAgICBjb25zdCBub3RpY2UgPVxuICAgICAgICBmaWx0ZXJlZEF1ZGl0cy5sZW5ndGggPT09IDBcbiAgICAgICAgICA/IGAuLi4gQWxsICR7ZGlmZn0gYXVkaXRzIGhhdmUgcGVyZmVjdCBzY29yZXMgLi4uYFxuICAgICAgICAgIDogYC4uLiAke2RpZmZ9IGF1ZGl0cyB3aXRoIHBlcmZlY3Qgc2NvcmVzIG9taXR0ZWQgZm9yIGJyZXZpdHkgLi4uYDtcbiAgICAgIGxvZ1JvdygxLCBub3RpY2UpO1xuICAgIH1cbiAgICBsb2coKTtcbiAgfSk7XG59XG5cbmZ1bmN0aW9uIGxvZ0F1ZGl0cyhwbHVnaW5UaXRsZTogc3RyaW5nLCBhdWRpdHM6IEF1ZGl0UmVwb3J0W10pOiB2b2lkIHtcbiAgbG9nKCk7XG4gIGxvZyhib2xkLm1hZ2VudGFCcmlnaHQoYCR7cGx1Z2luVGl0bGV9IGF1ZGl0c2ApKTtcbiAgbG9nKCk7XG4gIGF1ZGl0cy5mb3JFYWNoKCh7IHNjb3JlLCB0aXRsZSwgZGlzcGxheVZhbHVlLCB2YWx1ZSB9KSA9PiB7XG4gICAgbG9nUm93KHNjb3JlLCB0aXRsZSwgZGlzcGxheVZhbHVlIHx8IGAke3ZhbHVlfWApO1xuICB9KTtcbn1cblxuZnVuY3Rpb24gbG9nUm93KHNjb3JlOiBudW1iZXIsIHRpdGxlOiBzdHJpbmcsIHZhbHVlPzogc3RyaW5nKTogdm9pZCB7XG4gIHVpKCkucm93KFtcbiAgICB7XG4gICAgICB0ZXh0OiBhcHBseVNjb3JlQ29sb3IoeyBzY29yZSwgdGV4dDogJ1x1MjVDRicgfSksXG4gICAgICB3aWR0aDogMixcbiAgICAgIHBhZGRpbmc6IFswLCAxLCAwLCAwXSxcbiAgICB9LFxuICAgIHtcbiAgICAgIHRleHQ6IHRpdGxlLFxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1tYWdpYy1udW1iZXJzXG4gICAgICBwYWRkaW5nOiBbMCwgMywgMCwgMF0sXG4gICAgfSxcbiAgICAuLi4odmFsdWVcbiAgICAgID8gW1xuICAgICAgICAgIHtcbiAgICAgICAgICAgIHRleHQ6IGN5YW5CcmlnaHQodmFsdWUpLFxuICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1tYWdpYy1udW1iZXJzXG4gICAgICAgICAgICB3aWR0aDogMjAsXG4gICAgICAgICAgICBwYWRkaW5nOiBbMCwgMCwgMCwgMF0sXG4gICAgICAgICAgfSxcbiAgICAgICAgXVxuICAgICAgOiBbXSksXG4gIF0pO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbG9nQ2F0ZWdvcmllcyh7XG4gIHBsdWdpbnMsXG4gIGNhdGVnb3JpZXMsXG59OiBSZXF1aXJlZDxQaWNrPFNjb3JlZFJlcG9ydCwgJ3BsdWdpbnMnIHwgJ2NhdGVnb3JpZXMnPj4pOiB2b2lkIHtcbiAgY29uc3QgaEFsaWduID0gKGlkeDogbnVtYmVyKSA9PiAoaWR4ID09PSAwID8gJ2xlZnQnIDogJ3JpZ2h0Jyk7XG5cbiAgY29uc3Qgcm93cyA9IGNhdGVnb3JpZXMubWFwKCh7IHRpdGxlLCBzY29yZSwgcmVmcywgaXNCaW5hcnkgfSkgPT4gW1xuICAgIHRpdGxlLFxuICAgIGAke2JpbmFyeUljb25QcmVmaXgoc2NvcmUsIGlzQmluYXJ5KX0ke2FwcGx5U2NvcmVDb2xvcih7IHNjb3JlIH0pfWAsXG4gICAgY291bnRDYXRlZ29yeUF1ZGl0cyhyZWZzLCBwbHVnaW5zKSxcbiAgXSk7XG4gIGNvbnN0IHRhYmxlID0gdWkoKS50YWJsZSgpO1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLW1hZ2ljLW51bWJlcnNcbiAgdGFibGUuY29sdW1uV2lkdGhzKFtURVJNSU5BTF9XSURUSCAtIDkgLSAxMCAtIDQsIDksIDEwXSk7XG4gIHRhYmxlLmhlYWQoXG4gICAgUkVQT1JUX1JBV19PVkVSVklFV19UQUJMRV9IRUFERVJTLm1hcCgoaGVhZGluZywgaWR4KSA9PiAoe1xuICAgICAgY29udGVudDogY3lhbihoZWFkaW5nKSxcbiAgICAgIGhBbGlnbjogaEFsaWduKGlkeCksXG4gICAgfSkpLFxuICApO1xuICByb3dzLmZvckVhY2gocm93ID0+XG4gICAgdGFibGUucm93KFxuICAgICAgcm93Lm1hcCgoY29udGVudCwgaWR4KSA9PiAoe1xuICAgICAgICBjb250ZW50OiBjb250ZW50LnRvU3RyaW5nKCksXG4gICAgICAgIGhBbGlnbjogaEFsaWduKGlkeCksXG4gICAgICB9KSksXG4gICAgKSxcbiAgKTtcblxuICBsb2coYm9sZC5tYWdlbnRhQnJpZ2h0KCdDYXRlZ29yaWVzJykpO1xuICBsb2coKTtcbiAgdGFibGUucmVuZGVyKCk7XG4gIGxvZygpO1xufVxuXG4vLyBAVE9ETyByZWZhY3RvciBgaXNCaW5hcnk6IGJvb2xlYW5gIHRvIGB0YXJnZXRTY29yZTogbnVtYmVyYCAjNzEzXG5leHBvcnQgZnVuY3Rpb24gYmluYXJ5SWNvblByZWZpeChcbiAgc2NvcmU6IG51bWJlcixcbiAgaXNCaW5hcnk6IGJvb2xlYW4gfCB1bmRlZmluZWQsXG4pOiBzdHJpbmcge1xuICByZXR1cm4gdGFyZ2V0U2NvcmVJY29uKHNjb3JlLCBpc0JpbmFyeSA/IDEgOiB1bmRlZmluZWQsIHtcbiAgICBwYXNzSWNvbjogYm9sZChncmVlbignXHUyNzEzJykpLFxuICAgIGZhaWxJY29uOiBib2xkKHJlZCgnXHUyNzE3JykpLFxuICAgIHBvc3RmaXg6ICcgJyxcbiAgfSk7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3pvZC12YWxpZGF0aW9uLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi96b2QtdmFsaWRhdGlvbi50c1wiO2ltcG9ydCB7IGJvbGQsIHJlZCB9IGZyb20gJ2Fuc2lzJztcbmltcG9ydCB0eXBlIHsgTWVzc2FnZUJ1aWxkZXIgfSBmcm9tICd6b2QtdmFsaWRhdGlvbi1lcnJvcic7XG5cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRFcnJvclBhdGgoZXJyb3JQYXRoOiAoc3RyaW5nIHwgbnVtYmVyKVtdKTogc3RyaW5nIHtcbiAgcmV0dXJuIGVycm9yUGF0aFxuICAgIC5tYXAoKGtleSwgaW5kZXgpID0+IHtcbiAgICAgIGlmICh0eXBlb2Yga2V5ID09PSAnbnVtYmVyJykge1xuICAgICAgICByZXR1cm4gYFske2tleX1dYDtcbiAgICAgIH1cbiAgICAgIHJldHVybiBpbmRleCA+IDAgPyBgLiR7a2V5fWAgOiBrZXk7XG4gICAgfSlcbiAgICAuam9pbignJyk7XG59XG5cbmV4cG9ydCBjb25zdCB6b2RFcnJvck1lc3NhZ2VCdWlsZGVyOiBNZXNzYWdlQnVpbGRlciA9IGlzc3VlcyA9PlxuICBpc3N1ZXNcbiAgICAubWFwKGlzc3VlID0+IHtcbiAgICAgIGNvbnN0IGZvcm1hdHRlZE1lc3NhZ2UgPSByZWQoYCR7Ym9sZChpc3N1ZS5jb2RlKX06ICR7aXNzdWUubWVzc2FnZX1gKTtcbiAgICAgIGNvbnN0IGZvcm1hdHRlZFBhdGggPSBmb3JtYXRFcnJvclBhdGgoaXNzdWUucGF0aCk7XG4gICAgICBpZiAoZm9ybWF0dGVkUGF0aCkge1xuICAgICAgICByZXR1cm4gYFZhbGlkYXRpb24gZXJyb3IgYXQgJHtib2xkKGZvcm1hdHRlZFBhdGgpfVxcbiR7Zm9ybWF0dGVkTWVzc2FnZX1cXG5gO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGAke2Zvcm1hdHRlZE1lc3NhZ2V9XFxuYDtcbiAgICB9KVxuICAgIC5qb2luKCdcXG4nKTtcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBcVEsT0FBTztBQUM1USxTQUFTLEtBQUFB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O0FBQUEsRUFDdkQsQ0FBQztBQUNIOyIsCiAgIm5hbWVzIjogWyJ6IiwgInBhdGgiLCAicGF0aCIsICJ1aSIsICJwYXRoIiwgImJvbGQiLCAicGF0aCIsICJ1aSIsICJ6IiwgInoiLCAiY29uZmlnIiwgImdyb3VwcyIsICJhdWRpdHMiLCAiY2FsY3VsYXRlQ292ZXJhZ2UiLCAiY29uZmlnIiwgImNhbGN1bGF0ZUNvdmVyYWdlIiwgImNvbmZpZyIsICJjcmVhdGVSZXF1aXJlIiwgInBhdGgiLCAiZmlsZVVSTFRvUGF0aCIsICJ6IiwgInoiLCAic2x1Z2lmeSIsICJ0b0FycmF5IiwgInBhdGgiLCAiZXhpc3RzIiwgInRvQXJyYXkiLCAidWkiLCAiZXhpc3RzIiwgInRvQXJyYXkiLCAidWkiLCAiRVNMaW50IiwgIndyaXRlRmlsZSIsICJwYXRoIiwgImVuc3VyZURpcmVjdG9yeUV4aXN0cyIsICJmaWxlUGF0aFRvQ2xpQXJnIiwgInBsdWdpbldvcmtEaXIiLCAicmVhZEpzb25GaWxlIiwgImRpc3RpbmN0IiwgImV4ZWN1dGVQcm9jZXNzIiwgImZpbGVQYXRoVG9DbGlBcmciLCAidG9BcnJheSIsICJ1aSIsICJXT1JLRElSIiwgInBsdWdpbldvcmtEaXIiLCAiUlVOTkVSX09VVFBVVF9QQVRIIiwgInBhdGgiLCAiUExVR0lOX0NPTkZJR19QQVRIIiwgInBhdGgiLCAiZmlsZUV4aXN0cyIsICJ0b0FycmF5IiwgImNyZWF0ZVJlcXVpcmUiLCAicGF0aCIsICJmaWxlVVJMVG9QYXRoIiwgInoiLCAieiIsICJvYmplY3RUb0tleXMiLCAicGF0aCIsICJvYmplY3RUb0tleXMiLCAicmVhZEpzb25GaWxlIiwgIm9iamVjdFRvRW50cmllcyIsICJvYmplY3RUb0VudHJpZXMiLCAib2JqZWN0VG9FbnRyaWVzIiwgIm9iamVjdFRvRW50cmllcyIsICJvYmplY3RUb0tleXMiLCAib2JqZWN0VG9LZXlzIiwgIm9iamVjdFRvRW50cmllcyIsICJmcm9tSnNvbkxpbmVzIiwgIm9iamVjdEZyb21FbnRyaWVzIiwgIm9iamVjdFRvRW50cmllcyIsICJvYmplY3RUb0tleXMiLCAid3JpdGVGaWxlIiwgInBhdGgiLCAiZW5zdXJlRGlyZWN0b3J5RXhpc3RzIiwgImV4ZWN1dGVQcm9jZXNzIiwgImZpbGVQYXRoVG9DbGlBcmciLCAib2JqZWN0RnJvbUVudHJpZXMiLCAicmVhZEpzb25GaWxlIiwgIm9iamVjdFRvRW50cmllcyIsICJwYXRoIiwgInBsdWdpbldvcmtEaXIiLCAiV09SS0RJUiIsICJwbHVnaW5Xb3JrRGlyIiwgIlJVTk5FUl9PVVRQVVRfUEFUSCIsICJwYXRoIiwgIlBMVUdJTl9DT05GSUdfUEFUSCIsICJtZCIsICJvYmplY3RGcm9tRW50cmllcyIsICJvYmplY3RUb0tleXMiLCAib2JqZWN0VG9LZXlzIiwgInBhdGgiLCAiZmlsZUV4aXN0cyIsICJleGVjdXRlUHJvY2VzcyIsICJjcmVhdGVSZXF1aXJlIiwgInBhdGgiLCAicGF0aCIsICJib2xkIiwgInVpIiwgInBhdGgiLCAicGF0aCIsICJwYXRoIiwgImVuc3VyZURpcmVjdG9yeUV4aXN0cyIsICJib2xkIiwgImltcG9ydE1vZHVsZSIsICJyZWFkSnNvbkZpbGUiLCAidWkiLCAiYm9sZCIsICJ5ZWxsb3ciLCAidWkiLCAidGFibGVTY2hlbWEiLCAiZm9ybWF0Qnl0ZXMiLCAiZm9ybWF0RHVyYXRpb24iLCAiaHRtbCIsICJib2xkIiwgInVpIiwgImJvbGQiLCAidG9BcnJheSIsICJjb25maWciLCAiY29uZmlnIiwgImV4aXN0cyIsICJtZCIsICJib2xkIiwgInJlYWRGaWxlIiwgInBhdGgiLCAicGF0aCIsICJzaW1wbGVHaXQiLCAicGxhdGZvcm0iLCAiY29uZmlnIiwgImNhdGVnb3JpZXMiLCAiYm9sZCIsICJncmF5IiwgIk1hcmtkb3duRG9jdW1lbnQiLCAibWQiLCAibWQiLCAicGF0aCIsICJNYXJrZG93bkRvY3VtZW50IiwgIm1kIiwgIk1hcmtkb3duRG9jdW1lbnQiLCAibWQiLCAiTWFya2Rvd25Eb2N1bWVudCIsICJtZCIsICJwYXRoIiwgImJvbGQiLCAiZ3JlZW4iLCAiYm9sZCIsICJyZWQiLCAieiJdCn0K diff --git a/code-pushup.config.ts b/code-pushup.config.ts index d1b674a1d..ddfcd59f6 100644 --- a/code-pushup.config.ts +++ b/code-pushup.config.ts @@ -40,5 +40,22 @@ export default mergeConfigs( 'https://github.com/code-pushup/cli?tab=readme-ov-file#code-pushup-cli/', ), await eslintCoreConfigNx(), - await docCoverageCoreConfig(), + await docCoverageCoreConfig({ + sourceGlob: [ + 'packages/**/src/**/*.ts', + '!**/*.spec.ts', + '!**/*.test.ts', + '!**/implementation/**', + '!**/internal/**', + ], + onlyAudits: [ + 'methods-coverage', + 'functions-coverage', + 'types-coverage', + 'classes-coverage', + 'interfaces-coverage', + 'enums-coverage', + 'type-aliases-coverage', + ], + }), ); diff --git a/code-pushup.preset.ts b/code-pushup.preset.ts index 979610544..1060dd1e9 100644 --- a/code-pushup.preset.ts +++ b/code-pushup.preset.ts @@ -5,8 +5,14 @@ import type { import coveragePlugin, { getNxCoveragePaths, } from './packages/plugin-coverage/src/index.js'; -import docCoveragePlugin from './packages/plugin-doc-coverage/src/index.js'; -import { docCoverageAudits } from './packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.js'; +import docCoveragePlugin, { + DocCoveragePluginConfig, +} from './packages/plugin-doc-coverage/src/index.js'; +import { + PLUGIN_SLUG, + groups, +} from './packages/plugin-doc-coverage/src/lib/constants.js'; +import { filterGroupsByOnlyAudits } from './packages/plugin-doc-coverage/src/lib/utils.js'; import eslintPlugin, { eslintConfigFromAllNxProjects, eslintConfigFromNxProject, @@ -84,19 +90,23 @@ export const eslintCategories: CategoryConfig[] = [ }, ]; -export const docCoverageCategories: CategoryConfig[] = [ - { - slug: 'doc-coverage', - title: 'Documentation coverage', - description: 'Measures how much of your code is **documented**.', - refs: docCoverageAudits.map(audit => ({ - weight: 1, - type: 'audit', - plugin: 'doc-coverage', - slug: audit.slug, - })), - }, -]; +export function getDocCoverageCategories( + config: DocCoveragePluginConfig, +): CategoryConfig[] { + return [ + { + slug: 'doc-coverage-cat', + title: 'Documentation coverage', + description: 'Measures how much of your code is **documented**.', + refs: filterGroupsByOnlyAudits(groups, config).map(group => ({ + weight: 1, + type: 'group', + plugin: PLUGIN_SLUG, + slug: group.slug, + })), + }, + ]; +} export const coverageCategories: CategoryConfig[] = [ { @@ -130,15 +140,12 @@ export const lighthouseCoreConfig = async ( }; }; -export const docCoverageCoreConfig = async (): Promise => { +export const docCoverageCoreConfig = async ( + config: DocCoveragePluginConfig, +): Promise => { return { - plugins: [ - await docCoveragePlugin({ - language: 'typescript', - sourceGlob: 'packages/**/*.ts', - }), - ], - categories: docCoverageCategories, + plugins: [await docCoveragePlugin(config)], + categories: getDocCoverageCategories(config), }; }; diff --git a/packages/plugin-doc-coverage/mocks/component-mock.spec.ts b/packages/plugin-doc-coverage/mocks/component-mock.spec.ts new file mode 100644 index 000000000..62998c2a9 --- /dev/null +++ b/packages/plugin-doc-coverage/mocks/component-mock.spec.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from 'vitest'; +import { DUMMY_FUNCTION, DUMMY_FUNCTION_2 } from './component-mock'; + +export function shouldnotBeHere() { + return 'Hello World'; +} + +describe('component-mock', () => { + it('should return Hello World', () => { + expect(DUMMY_FUNCTION()).toBe('Hello World'); + }); + + it('should return Hello World 2', () => { + expect(DUMMY_FUNCTION_2()).toBe('Hello World 2'); + }); +}); diff --git a/packages/plugin-doc-coverage/mocks/component-mock.ts b/packages/plugin-doc-coverage/mocks/component-mock.ts index acd80f441..5b2da7c27 100644 --- a/packages/plugin-doc-coverage/mocks/component-mock.ts +++ b/packages/plugin-doc-coverage/mocks/component-mock.ts @@ -10,40 +10,40 @@ export function DUMMY_FUNCTION_2() { return 'Hello World 2'; } -class DummyClass { - /** - * Dummy property that returns 'Hello World 3'. - * @returns {string} - The string 'Hello World 3'. - */ - dummyProperty = 'Hello World 3'; - - /** - * Dummy method that returns 'Hello World 4'. - * @returns {string} - The string 'Hello World 4'. - */ - dummyMethod() { - return 'Hello World 4'; - } - - constructor() { - this.dummyProperty = 'Hello World 3'; - } -} - -export default DummyClass; - -export const variableDummy = 'Hello World 5'; - -export const variableDummy2 = 'Hello World 6'; - -/** Dummy variable that returns 'Hello World 7'. */ -export const variableDummy3 = 'Hello World 7'; - -/** Dummy interface that returns 'Hello World 8'. */ -export interface DummyInterface { - dummyProperty: string; - dummyMethod(): string; -} - -/** Dummy type that returns 'Hello World 9'. */ -export type DummyType = string; +// class DummyClass { +// /** +// * Dummy property that returns 'Hello World 3'. +// * @returns {string} - The string 'Hello World 3'. +// */ +// dummyProperty = 'Hello World 3'; + +// /** +// * Dummy method that returns 'Hello World 4'. +// * @returns {string} - The string 'Hello World 4'. +// */ +// dummyMethod() { +// return 'Hello World 4'; +// } + +// constructor() { +// this.dummyProperty = 'Hello World 3'; +// } +// } + +// export default DummyClass; + +// export const variableDummy = 'Hello World 5'; + +// export const variableDummy2 = 'Hello World 6'; + +// /** Dummy variable that returns 'Hello World 7'. */ +// export const variableDummy3 = 'Hello World 7'; + +// /** Dummy interface that returns 'Hello World 8'. */ +// export interface DummyInterface { +// dummyProperty: string; +// dummyMethod(): string; +// } + +// /** Dummy type that returns 'Hello World 9'. */ +// export type DummyType = string; diff --git a/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.css b/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.css new file mode 100644 index 000000000..f3958f2b4 --- /dev/null +++ b/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.css @@ -0,0 +1,4 @@ +h1 { + color: #336699; + text-align: center; +} diff --git a/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.html b/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.html new file mode 100644 index 000000000..b6515528b --- /dev/null +++ b/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.html @@ -0,0 +1 @@ +

{{ title }}

diff --git a/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.spec.ts b/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.spec.ts new file mode 100644 index 000000000..c89f47dd8 --- /dev/null +++ b/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.spec.ts @@ -0,0 +1,3 @@ +function notRealisticFunction() { + return 'notRealisticFunction'; +} diff --git a/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.ts b/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.ts new file mode 100644 index 000000000..2fc2b165f --- /dev/null +++ b/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.ts @@ -0,0 +1,18 @@ +/** + * Basic Angular component that displays a welcome message + */ +export class AppComponent { + protected readonly title = 'My Angular App'; + + /** + * Dummy method that returns a welcome message + * @returns {string} - The welcome message + */ + getWelcomeMessage() { + return 'Welcome to My Angular App!'; + } + + sendEvent() { + return 'Event sent'; + } +} diff --git a/packages/plugin-doc-coverage/mocks/fixtures/angular/map-event.function.ts b/packages/plugin-doc-coverage/mocks/fixtures/angular/map-event.function.ts new file mode 100644 index 000000000..9cd32ce8a --- /dev/null +++ b/packages/plugin-doc-coverage/mocks/fixtures/angular/map-event.function.ts @@ -0,0 +1,8 @@ +export function mapEventToCustomEvent(event: string) { + return event; +} + +/** Commented */ +export function mapCustomEventToEvent(event: string) { + return event; +} diff --git a/packages/plugin-doc-coverage/mocks/source-files.mock.ts b/packages/plugin-doc-coverage/mocks/source-files.mock.ts new file mode 100644 index 000000000..bc0ad113c --- /dev/null +++ b/packages/plugin-doc-coverage/mocks/source-files.mock.ts @@ -0,0 +1,114 @@ +import { + ClassDeclaration, + EnumDeclaration, + FunctionDeclaration, + InterfaceDeclaration, + SourceFile, + SyntaxKind, + TypeAliasDeclaration, +} from 'ts-morph'; +import type { CoverageType } from '../src/lib/models'; + +export function sourceFileMock( + file: string, + nodes: Partial>>, +): SourceFile { + return { + getFilePath: () => file as any, + getClasses: () => + nodes.classes + ? (Object.entries(nodes.classes).map(([line, isCommented]) => + nodeMock({ + coverageType: 'classes', + line: Number(line), + file, + isCommented, + }), + ) as unknown as ClassDeclaration[]) + : [], + getFunctions: () => + nodes.functions + ? (Object.entries(nodes.functions).map(([line, isCommented]) => + nodeMock({ + coverageType: 'functions', + line: Number(line), + file, + isCommented, + }), + ) as unknown as FunctionDeclaration[]) + : [], + getEnums: () => + nodes.enums + ? (Object.entries(nodes.enums).map(([line, isCommented]) => + nodeMock({ + coverageType: 'enums', + line: Number(line), + file, + isCommented, + }), + ) as unknown as EnumDeclaration[]) + : [], + getTypeAliases: () => + nodes.types + ? (Object.entries(nodes.types).map(([line, isCommented]) => + nodeMock({ + coverageType: 'types', + line: Number(line), + file, + isCommented, + }), + ) as unknown as TypeAliasDeclaration[]) + : [], + getInterfaces: () => + nodes.interfaces + ? (Object.entries(nodes.interfaces).map(([line, isCommented]) => + nodeMock({ + coverageType: 'interfaces', + line: Number(line), + file, + isCommented, + }), + ) as unknown as InterfaceDeclaration[]) + : [], + } as SourceFile; +} + +export function nodeMock(options: { + coverageType: CoverageType; + line: number; + file: string; + isCommented: boolean; +}) { + return { + getKind: () => getKindFromCoverageType(options.coverageType), + getJsDocs: () => (options.isCommented ? ['Comment'] : []), + getName: () => 'test', + getStartLineNumber: () => options.line, + // Only for classes + getMethods: () => [], + getProperties: () => [], + }; +} + +function getKindFromCoverageType(coverageType: CoverageType): SyntaxKind { + switch (coverageType) { + case 'classes': + return SyntaxKind.ClassDeclaration; + case 'methods': + return SyntaxKind.MethodDeclaration; + case 'functions': + return SyntaxKind.FunctionDeclaration; + case 'interfaces': + return SyntaxKind.InterfaceDeclaration; + case 'enums': + return SyntaxKind.EnumDeclaration; + case 'variables': + return SyntaxKind.VariableDeclaration; + case 'properties': + return SyntaxKind.PropertyDeclaration; + case 'types': + return SyntaxKind.TypeAliasDeclaration; + default: + throw new Error(`Unsupported syntax kind: ${coverageType}`); + } +} diff --git a/packages/plugin-doc-coverage/src/lib/config.ts b/packages/plugin-doc-coverage/src/lib/config.ts index 853099796..52ccb5cb8 100644 --- a/packages/plugin-doc-coverage/src/lib/config.ts +++ b/packages/plugin-doc-coverage/src/lib/config.ts @@ -1,13 +1,10 @@ import { z } from 'zod'; -export type DocType = 'percentage-coverage'; - export const docCoveragePluginConfigSchema = z.object({ + onlyAudits: z.array(z.string()).optional(), sourceGlob: z - .string({ - description: 'Glob pattern to find source files', - }) - .default('src/**/*.{ts,tsx}'), + .array(z.string()) + .default(['src/**/*.{ts,tsx}', '!**/*.spec.ts', '!**/*.test.ts']), }); export type DocCoveragePluginConfig = z.infer< diff --git a/packages/plugin-doc-coverage/src/lib/config.unit.test.ts b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts index 4cb85ed8a..63d0007b9 100644 --- a/packages/plugin-doc-coverage/src/lib/config.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts @@ -8,7 +8,7 @@ describe('docCoveragePluginConfigSchema', () => { it('accepts a valid source glob pattern', () => { expect(() => docCoveragePluginConfigSchema.parse({ - sourceGlob: 'src/**/*.{ts,tsx}', + sourceGlob: ['src/**/*.{ts,tsx}', '!**/*.spec.ts', '!**/*.test.ts'], } satisfies DocCoveragePluginConfig), ).not.toThrow(); }); diff --git a/packages/plugin-doc-coverage/src/lib/constants.ts b/packages/plugin-doc-coverage/src/lib/constants.ts new file mode 100644 index 000000000..458409066 --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/constants.ts @@ -0,0 +1,68 @@ +import type { Audit, Group } from '@code-pushup/models'; +import type { AuditSlug } from './models'; + +export const PLUGIN_SLUG = 'doc-coverage'; + +export const AUDITS_MAP: Record = { + 'classes-coverage': { + slug: 'classes-coverage', + title: 'Classes coverage', + description: 'Coverage of classes', + }, + 'methods-coverage': { + slug: 'methods-coverage', + title: 'Methods coverage', + description: 'Coverage of methods', + }, + 'functions-coverage': { + slug: 'functions-coverage', + title: 'Functions coverage', + description: 'Coverage of functions', + }, + 'interfaces-coverage': { + slug: 'interfaces-coverage', + title: 'Interfaces coverage', + description: 'Coverage of interfaces', + }, + 'variables-coverage': { + slug: 'variables-coverage', + title: 'Variables coverage', + description: 'Coverage of variables', + }, + 'properties-coverage': { + slug: 'properties-coverage', + title: 'Properties coverage', + description: 'Coverage of properties', + }, + 'types-coverage': { + slug: 'types-coverage', + title: 'Types coverage', + description: 'Coverage of types', + }, + 'enums-coverage': { + slug: 'enums-coverage', + title: 'Enums coverage', + description: 'Coverage of enums', + }, +} as const; + +export const groups: Group[] = [ + { + slug: 'documentation-coverage', + title: 'Documentation coverage', + description: 'Documentation coverage', + refs: Object.keys(AUDITS_MAP).map(slug => { + switch (slug as AuditSlug) { + case 'classes-coverage': + case 'functions-coverage': + case 'methods-coverage': + return { slug, weight: 2 }; + case 'interfaces-coverage': + case 'properties-coverage': + case 'types-coverage': + default: + return { slug, weight: 1 }; + } + }), + }, +]; diff --git a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts index 37d54858e..371409e1b 100644 --- a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts +++ b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts @@ -1,12 +1,22 @@ -import { createRequire } from 'node:module'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; import type { PluginConfig } from '@code-pushup/models'; import { type DocCoveragePluginConfig, docCoveragePluginConfigSchema, } from './config.js'; -import { createRunnerConfig } from './runner/index.js'; +import { PLUGIN_SLUG, groups } from './constants.js'; +import { createRunnerFunction } from './runner/runner.js'; +import { + filterAuditsByPluginConfig, + filterGroupsByOnlyAudits, +} from './utils.js'; + +const PLUGIN_TITLE = 'Documentation coverage'; + +const PLUGIN_DESCRIPTION = + 'Official Code PushUp documentation coverage plugin.'; + +const PLUGIN_DOCS_URL = + 'https://www.npmjs.com/package/@code-pushup/doc-coverage-plugin/'; /** * Instantiates Code PushUp documentation coverage plugin for core config. @@ -26,40 +36,19 @@ import { createRunnerConfig } from './runner/index.js'; * * @returns Plugin configuration. */ - -export const docCoverageAudits = [ - { - slug: 'percentage-coverage', - title: 'Percentage of codebase with documentation', - description: 'Measures how many % of the codebase have documentation.', - }, -]; - export async function docCoveragePlugin( config: DocCoveragePluginConfig, ): Promise { const docCoverageConfig = docCoveragePluginConfigSchema.parse(config); - const runnerScriptPath = path.join( - fileURLToPath(path.dirname(import.meta.url)), - '..', - 'bin.js', - ); - - const packageJson = createRequire(import.meta.url)( - '../../package.json', - ) as typeof import('../../package.json'); - return { - slug: 'doc-coverage', - title: 'Documentation coverage', + slug: PLUGIN_SLUG, + title: PLUGIN_TITLE, icon: 'folder-src', - description: 'Official Code PushUp documentation coverage plugin.', - docsUrl: 'https://www.npmjs.com/package/@code-pushup/doc-coverage-plugin/', - packageName: packageJson.name, - version: packageJson.version, - audits: docCoverageAudits, - // groups: [group], - runner: await createRunnerConfig(runnerScriptPath, docCoverageConfig), + description: PLUGIN_DESCRIPTION, + docsUrl: PLUGIN_DOCS_URL, + groups: filterGroupsByOnlyAudits(groups, docCoverageConfig), + audits: filterAuditsByPluginConfig(docCoverageConfig), + runner: createRunnerFunction(docCoverageConfig), }; } diff --git a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts index 992f6de6c..d3d178a91 100644 --- a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts @@ -12,7 +12,9 @@ vi.mock('./runner/index.ts', () => ({ describe('docCoveragePlugin', () => { it('should initialise a Documentation coverage plugin', async () => { await expect( - docCoveragePlugin({ sourceGlob: 'src/**/*.ts' }), + docCoveragePlugin({ + sourceGlob: ['src/**/*.ts', '!**/*.spec.ts', '!**/*.test.ts'], + }), ).resolves.toStrictEqual( expect.objectContaining({ slug: 'doc-coverage', @@ -25,7 +27,9 @@ describe('docCoveragePlugin', () => { it('should generate percentage coverage audit', async () => { await expect( - docCoveragePlugin({ sourceGlob: 'src/**/*.ts' }), + docCoveragePlugin({ + sourceGlob: ['src/**/*.ts', '!**/*.spec.ts', '!**/*.test.ts'], + }), ).resolves.toStrictEqual( expect.objectContaining({ audits: [ @@ -43,7 +47,9 @@ describe('docCoveragePlugin', () => { it('should include package metadata', async () => { await expect( - docCoveragePlugin({ sourceGlob: 'src/**/*.ts' }), + docCoveragePlugin({ + sourceGlob: ['src/**/*.ts', '!**/*.spec.ts', '!**/*.test.ts'], + }), ).resolves.toStrictEqual( expect.objectContaining({ icon: 'folder-src', diff --git a/packages/plugin-doc-coverage/src/lib/models.ts b/packages/plugin-doc-coverage/src/lib/models.ts index dd61e1bdc..4500a13d1 100644 --- a/packages/plugin-doc-coverage/src/lib/models.ts +++ b/packages/plugin-doc-coverage/src/lib/models.ts @@ -1,30 +1,3 @@ -export type UndocumentedItem = { - file: string; - type: string; - name: string; - line: number; - class?: string; -}; +import type { CoverageType } from './runner/models'; -export type CoverageByType = { - functions: number; - variables: number; - classes: number; - methods: number; - properties: number; - interfaces: number; - types: number; -}; - -export type CoverageKey = keyof CoverageByType; - -export type DocumentationStats = { - documented: number; - total: number; -}; - -export type CoverageResult = { - undocumentedItems: UndocumentedItem[]; - currentCoverage: number; - coverageByType: CoverageByType; -}; +export type AuditSlug = `${CoverageType}-coverage`; diff --git a/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.integration.test.ts.snap b/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.integration.test.ts.snap new file mode 100644 index 000000000..33a38bd67 --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.integration.test.ts.snap @@ -0,0 +1,139 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`processDocCoverage > should succesfully get the right number of ts files 1`] = ` +{ + "classes": { + "coverage": 100, + "issues": [], + "nodesCount": 1, + }, + "enums": { + "coverage": 100, + "issues": [], + "nodesCount": 0, + }, + "functions": { + "coverage": 33.333333333333336, + "issues": [ + { + "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.spec.ts", + "line": 1, + "name": "notRealisticFunction", + "type": "functions", + }, + { + "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/map-event.function.ts", + "line": 1, + "name": "mapEventToCustomEvent", + "type": "functions", + }, + ], + "nodesCount": 3, + }, + "interfaces": { + "coverage": 100, + "issues": [], + "nodesCount": 0, + }, + "methods": { + "coverage": 50, + "issues": [ + { + "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.ts", + "line": 15, + "name": "sendEvent", + "type": "methods", + }, + ], + "nodesCount": 2, + }, + "properties": { + "coverage": 0, + "issues": [ + { + "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.ts", + "line": 5, + "name": "title", + "type": "properties", + }, + ], + "nodesCount": 1, + }, + "types": { + "coverage": 100, + "issues": [], + "nodesCount": 0, + }, + "variables": { + "coverage": 100, + "issues": [], + "nodesCount": 0, + }, +} +`; + +exports[`processDocCoverage > should succesfully get the right number of ts files and not include spec files 1`] = ` +{ + "classes": { + "coverage": 100, + "issues": [], + "nodesCount": 1, + }, + "enums": { + "coverage": 100, + "issues": [], + "nodesCount": 0, + }, + "functions": { + "coverage": 50, + "issues": [ + { + "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/map-event.function.ts", + "line": 1, + "name": "mapEventToCustomEvent", + "type": "functions", + }, + ], + "nodesCount": 2, + }, + "interfaces": { + "coverage": 100, + "issues": [], + "nodesCount": 0, + }, + "methods": { + "coverage": 50, + "issues": [ + { + "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.ts", + "line": 15, + "name": "sendEvent", + "type": "methods", + }, + ], + "nodesCount": 2, + }, + "properties": { + "coverage": 0, + "issues": [ + { + "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.ts", + "line": 5, + "name": "title", + "type": "properties", + }, + ], + "nodesCount": 1, + }, + "types": { + "coverage": 100, + "issues": [], + "nodesCount": 0, + }, + "variables": { + "coverage": 100, + "issues": [], + "nodesCount": 0, + }, +} +`; diff --git a/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.unit.test.ts.snap b/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.unit.test.ts.snap new file mode 100644 index 000000000..b72b0a151 --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.unit.test.ts.snap @@ -0,0 +1,92 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`getUnprocessedCoverageReport > should produce a full report 1`] = ` +{ + "classes": { + "coverage": 33.333333333333336, + "issues": [ + { + "file": "test.ts", + "line": 4, + "name": "test", + "type": "classes", + }, + { + "file": "test.ts", + "line": 5, + "name": "test", + "type": "classes", + }, + ], + "nodesCount": 3, + }, + "enums": { + "coverage": 33.333333333333336, + "issues": [ + { + "file": "test.ts", + "line": 8, + "name": "test", + "type": "enums", + }, + { + "file": "test.ts", + "line": 9, + "name": "test", + "type": "enums", + }, + ], + "nodesCount": 3, + }, + "functions": { + "coverage": 100, + "issues": [], + "nodesCount": 3, + }, + "interfaces": { + "coverage": 66.66666666666667, + "issues": [ + { + "file": "test.ts", + "line": 15, + "name": "test", + "type": "interfaces", + }, + ], + "nodesCount": 3, + }, + "methods": { + "coverage": 100, + "issues": [], + "nodesCount": 0, + }, + "properties": { + "coverage": 100, + "issues": [], + "nodesCount": 0, + }, + "types": { + "coverage": 50, + "issues": [ + { + "file": "test.ts", + "line": 10, + "name": "test", + "type": "types", + }, + { + "file": "test.ts", + "line": 11, + "name": "test", + "type": "types", + }, + ], + "nodesCount": 4, + }, + "variables": { + "coverage": 100, + "issues": [], + "nodesCount": 0, + }, +} +`; diff --git a/packages/plugin-doc-coverage/src/lib/runner/constants.ts b/packages/plugin-doc-coverage/src/lib/runner/constants.ts deleted file mode 100644 index 9cf24d169..000000000 --- a/packages/plugin-doc-coverage/src/lib/runner/constants.ts +++ /dev/null @@ -1,42 +0,0 @@ -import path from 'node:path'; -import { pluginWorkDir } from '@code-pushup/utils'; - -export const WORKDIR = pluginWorkDir('doc-coverage'); - -export const RUNNER_OUTPUT_PATH = path.join(WORKDIR, 'runner-output.json'); - -export const PLUGIN_CONFIG_PATH = path.join( - process.cwd(), - WORKDIR, - 'plugin-config.json', -); - -export const enum ProgrammingLanguage { - JavaScript = 'javascript', - TypeScript = 'typescript', -} - -export const DEFAULT_SOURCE_GLOB = { - [ProgrammingLanguage.JavaScript]: '"src/**/*.js"', - [ProgrammingLanguage.TypeScript]: '"src/**/*.ts"', -}; - -export const DEFAULT_OUTPUT_FOLDER_PATH = './documentation'; - -export const COMMANDS_FOR_LANGUAGES: Readonly< - Record -> = { - [ProgrammingLanguage.JavaScript]: { - command: 'npx', - args: 'typedoc $sourceGlob --entryPointStrategy expand --plugin typedoc-plugin-coverage --coverageOutputType json --skipErrorChecking --out $outputFolderPath', - }, - [ProgrammingLanguage.TypeScript]: { - command: 'npx', - args: 'typedoc $sourceGlob --entryPointStrategy expand --plugin typedoc-plugin-coverage --coverageOutputType json --skipErrorChecking --out $outputFolderPath', - }, -} as const; - -export type TypedocResult = { - percent: number; - notDocumented: string[]; -}; diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts index 94bebac27..51e607eb9 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts @@ -1,21 +1,23 @@ import { processDocCoverage } from './doc-processer'; -describe('docProcesser', () => { - it('should successfully get documentation coverage', () => { - const results = processDocCoverage( - 'packages/plugin-doc-coverage/mocks/**/*.ts', - ); - console.log(results); - expect(results).toBeDefined(); - expect(results.currentCoverage).toBe(60); - expect(results.coverageByType).toEqual({ - functions: 50, - variables: 33.33, - classes: 0, - methods: 100, - properties: 100, - interfaces: 100, - types: 100, +describe('processDocCoverage', () => { + it('should succesfully get the right number of ts files', () => { + const results = processDocCoverage({ + sourceGlob: [ + 'packages/plugin-doc-coverage/mocks/fixtures/angular/**/*.ts', + ], }); + expect(results).toMatchSnapshot(); + }); + + it('should succesfully get the right number of ts files and not include spec files', () => { + const results = processDocCoverage({ + sourceGlob: [ + 'packages/plugin-doc-coverage/mocks/fixtures/angular/**/*.ts', + '!**/*.spec.ts', + '!**/*.test.ts', + ], + }); + expect(results).toMatchSnapshot(); }); }); diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts index 46a2cbacf..fbf333bfc 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts @@ -1,289 +1,115 @@ -import { Project } from 'ts-morph'; +import { ClassDeclaration, Project, SourceFile } from 'ts-morph'; +import type { DocCoveragePluginConfig } from '../config.js'; import type { - CoverageByType, - CoverageKey, CoverageResult, - DocumentationStats, - UndocumentedItem, -} from '../models.js'; - -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable functional/immutable-data */ -/* eslint-disable @typescript-eslint/max-params */ -/* eslint-disable functional/no-let */ + CoverageType, + UnprocessedCoverageResult, +} from './models.js'; +import { + calculateCoverage, + createEmptyUnprocessedCoverageReport, + getCoverageTypeFromKind, +} from './utils.js'; /** * Processes documentation coverage for TypeScript files in the specified path * @param toInclude - The file path pattern to include for documentation analysis * @returns {CoverageResult} Object containing coverage statistics and undocumented items */ -export function processDocCoverage(toInclude: string): CoverageResult { +export function processDocCoverage( + config: DocCoveragePluginConfig, +): CoverageResult { const project = new Project(); - project.addSourceFilesAtPaths(toInclude); - - const stats: Record = { - functions: { documented: 0, total: 0 }, - variables: { documented: 0, total: 0 }, - classes: { documented: 0, total: 0 }, - methods: { documented: 0, total: 0 }, - properties: { documented: 0, total: 0 }, - interfaces: { documented: 0, total: 0 }, - types: { documented: 0, total: 0 }, - }; - - const undocumentedItems: UndocumentedItem[] = []; - - project.getSourceFiles().forEach(sourceFile => { - if (isTestFile(sourceFile.getFilePath())) { - return; - } - - processClassDeclarations(sourceFile, undocumentedItems, stats); - processDeclarations(sourceFile, undocumentedItems, stats); - }); - - return { - undocumentedItems, - currentCoverage: calculateOverallCoverage(stats), - coverageByType: calculateCoverageByType(stats), - }; -} - -/** - * Checks if a file is a test file based on its path - * @param filePath - The path of the file to check - * @returns {boolean} True if the file is a test file, false otherwise - */ -function isTestFile(filePath: string): boolean { - return filePath.includes('.spec.') || filePath.includes('.test.'); + project.addSourceFilesAtPaths(config.sourceGlob); + return getUnprocessedCoverageReport(project.getSourceFiles()); } /** - * Creates an undocumented item entry - * @param file - The file path where the item was found - * @param type - The type of the undocumented item - * @param name - The name of the undocumented item - * @param line - The line number where the item appears - * @returns {UndocumentedItem} The undocumented item entry + * Gets the unprocessed coverage report from the source files + * @param sourceFiles - The source files to process + * @returns {UnprocessedCoverageResult} The unprocessed coverage report */ -function addUndocumentedItem( - file: string, - type: CoverageKey, - name: string, - line: number, -): UndocumentedItem { - return { file, type, name, line }; -} - -/** - * Processes class declarations in a source file and updates documentation statistics - * @param sourceFile - The source file to process - * @param undocumentedItems - Array to store undocumented items found - * @param stats - Object to track documentation statistics - */ -function processClassDeclarations( - sourceFile: any, - undocumentedItems: UndocumentedItem[], - stats: Record, -): void { - sourceFile.getClasses().forEach((classDeclaration: any) => { - const className = classDeclaration.getName() || 'Anonymous Class'; - const filePath = sourceFile.getFilePath(); - stats.classes.total++; - - if (classDeclaration.getJsDocs().length === 0) { - undocumentedItems.push( - addUndocumentedItem( - filePath, - 'classes', - className, - classDeclaration.getStartLineNumber(), - ), +export function getUnprocessedCoverageReport(sourceFiles: SourceFile[]) { + const unprocessedCoverageReport = sourceFiles.reduce( + (coverageReportOfAllFiles, sourceFile) => { + // Info of the file + const filePath = sourceFile.getFilePath(); + const classes = sourceFile.getClasses(); + + // All nodes of the file + const allNodesFromFile = [ + ...sourceFile.getFunctions(), + ...classes, + ...getClassNodes(classes), + ...sourceFile.getTypeAliases(), + ...sourceFile.getEnums(), + ...sourceFile.getInterfaces(), + // ...sourceFile.getVariableStatements().flatMap(statement => statement.getDeclarations()) + ]; + + const coverageReportOfCurrentFile = allNodesFromFile.reduce( + (acc, node) => { + const nodeType = getCoverageTypeFromKind(node.getKind()); + acc[nodeType].nodesCount++; + if (node.getJsDocs().length === 0) { + acc[nodeType].issues.push({ + file: filePath, + type: nodeType, + name: node.getName() || '', + line: node.getStartLineNumber(), + }); + } + return acc; + }, + createEmptyUnprocessedCoverageReport(), ); - } else { - stats.classes.documented++; - } - // Process properties - classDeclaration.getProperties().forEach((property: any) => { - stats.properties.total++; - if (property.getJsDocs().length === 0) { - undocumentedItems.push( - addUndocumentedItem( - filePath, - 'properties', - property.getName(), - property.getStartLineNumber(), - ), - ); - } else { - stats.properties.documented++; - } - }); - - // Process methods - classDeclaration.getMethods().forEach((method: any) => { - stats.methods.total++; - if (method.getJsDocs().length === 0) { - undocumentedItems.push( - addUndocumentedItem( - filePath, - 'methods', - method.getName(), - method.getStartLineNumber(), - ), - ); - } else { - stats.methods.documented++; - } - }); - }); -} - -/** - * Processes declarations (functions, variables, interfaces, and types) in a source file - * @param sourceFile - The source file to process - * @param undocumentedItems - Array to store undocumented items found - * @param stats - Object to track documentation statistics - */ -function processDeclarations( - sourceFile: any, - undocumentedItems: UndocumentedItem[], - stats: Record, -): void { - const filePath = sourceFile.getFilePath(); - - // Process functions - processItems( - sourceFile.getFunctions(), - 'functions', - item => item.getName() || 'Anonymous Function', - filePath, - undocumentedItems, - stats, + return mergeCoverageResults( + coverageReportOfAllFiles, + coverageReportOfCurrentFile, + ); + }, + createEmptyUnprocessedCoverageReport(), ); - // Process variables - sourceFile.getVariableStatements().forEach((statement: any) => { - statement.getDeclarations().forEach((declaration: any) => { - stats.variables.total++; - if (statement.getJsDocs().length === 0) { - undocumentedItems.push( - addUndocumentedItem( - filePath, - 'variables', - declaration.getName(), - declaration.getStartLineNumber(), - ), - ); - } else { - stats.variables.documented++; - } - }); - }); - - // Process interfaces and types - processItems( - sourceFile.getInterfaces(), - 'interfaces', - item => item.getName(), - filePath, - undocumentedItems, - stats, - ); - processItems( - sourceFile.getTypeAliases(), - 'types', - item => item.getName(), - filePath, - undocumentedItems, - stats, - ); + return calculateCoverage(unprocessedCoverageReport); } /** - * Generic function to process a collection of items and update documentation statistics - * @param items - Array of items to process - * @param type - The type of items being processed - * @param getName - Function to extract the name from an item - * @param filePath - The path of the file being processed - * @param undocumentedItems - Array to store undocumented items found - * @param stats - Object to track documentation statistics + * Merges two coverage results + * @param results - The first empty coverage result + * @param current - The second coverage result + * @returns {UnprocessedCoverageResult} The merged coverage result */ -function processItems( - items: any[], - type: CoverageKey, - getName: (item: any) => string, - filePath: string, - undocumentedItems: UndocumentedItem[], - stats: Record, -): void { - items.forEach(item => { - stats[type].total++; - if (item.getJsDocs().length === 0) { - undocumentedItems.push( - addUndocumentedItem( - filePath, +export function mergeCoverageResults( + results: UnprocessedCoverageResult, + current: Partial, +) { + return { + ...Object.fromEntries( + Object.entries(results).map(([key, value]) => { + const node = value as CoverageResult[CoverageType]; + const type = key as CoverageType; + return [ type, - getName(item), - item.getStartLineNumber(), - ), - ); - } else { - stats[type].documented++; - } - }); -} - -/** - * Calculates the overall documentation coverage percentage - * @param stats - Object containing documentation statistics - * @returns {number} The overall coverage percentage (0-100) - */ -function calculateOverallCoverage( - stats: Record, -): number { - let totalDocumented = 0; - let totalItems = 0; - - Object.values(stats).forEach(({ documented, total }) => { - totalDocumented += documented; - totalItems += total; - }); - - return totalItems === 0 ? 0 : (totalDocumented / totalItems) * 100; + { + nodesCount: node.nodesCount + (current[type]?.nodesCount ?? 0), + issues: [...node.issues, ...(current[type]?.issues ?? [])], + }, + ]; + }), + ), + } as UnprocessedCoverageResult; } /** - * Calculates documentation coverage percentage for each type - * @param stats - Object containing documentation statistics - * @returns {CoverageByType} Object containing coverage percentages for each type + * Gets the nodes from a class + * @param classNodes - The class nodes to process + * @returns {Node[]} The nodes from the class */ -function calculateCoverageByType( - stats: Record, -): CoverageByType { - const calculatePercentage = (documented: number, total: number) => - total === 0 ? 0 : Number(((documented / total) * 100).toFixed(2)); - - return { - functions: calculatePercentage( - stats.functions.documented, - stats.functions.total, - ), - variables: calculatePercentage( - stats.variables.documented, - stats.variables.total, - ), - classes: calculatePercentage(stats.classes.documented, stats.classes.total), - methods: calculatePercentage(stats.methods.documented, stats.methods.total), - properties: calculatePercentage( - stats.properties.documented, - stats.properties.total, - ), - interfaces: calculatePercentage( - stats.interfaces.documented, - stats.interfaces.total, - ), - types: calculatePercentage(stats.types.documented, stats.types.total), - }; +export function getClassNodes(classNodes: ClassDeclaration[]) { + return classNodes.flatMap(classNode => [ + ...classNode.getMethods(), + ...classNode.getProperties(), + ]); } diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts new file mode 100644 index 000000000..21c829349 --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts @@ -0,0 +1,198 @@ +import type { ClassDeclaration } from 'ts-morph'; +import { nodeMock, sourceFileMock } from './../../../mocks/source-files.mock'; +import { + getClassNodes, + getUnprocessedCoverageReport, + mergeCoverageResults, +} from './doc-processer'; +import type { UnprocessedCoverageResult } from './models'; + +describe('getUnprocessedCoverageReport', () => { + it('should produce a full report', () => { + const results = getUnprocessedCoverageReport([ + sourceFileMock('test.ts', { + functions: { 1: true, 2: true, 3: true }, + classes: { 4: false, 5: false, 6: true }, + enums: { 7: true, 8: false, 9: false }, + types: { 10: false, 11: false, 12: true, 40: true }, + interfaces: { 13: true, 14: true, 15: false }, + properties: { 16: false, 17: false, 18: false }, + variables: { 22: true, 23: true, 24: true }, + }), + ]); + expect(results).toMatchSnapshot(); + }); + + it('should accept array of source files', () => { + const results = getUnprocessedCoverageReport([ + sourceFileMock('test.ts', { functions: { 1: true, 2: true, 3: false } }), + ]); + expect(results).toBeDefined(); + }); + + it('should count nodes correctly', () => { + const results = getUnprocessedCoverageReport([ + sourceFileMock('test.ts', { functions: { 1: true, 2: true, 3: false } }), + ]); + + expect(results.functions.nodesCount).toBe(3); + }); + + it('should collect uncommented nodes issues', () => { + const results = getUnprocessedCoverageReport([ + sourceFileMock('test.ts', { functions: { 1: true, 2: false, 3: false } }), + ]); + + expect(results.functions.issues.length).toBe(2); + }); + + it('should collect valid issues', () => { + const results = getUnprocessedCoverageReport([ + sourceFileMock('test.ts', { functions: { 1: false } }), + ]); + + expect(results.functions.issues).toStrictEqual([ + { + line: 1, + file: 'test.ts', + type: 'functions', + name: 'test', + }, + ]); + }); + + it('should calculate coverage correctly', () => { + const results = getUnprocessedCoverageReport([ + sourceFileMock('test.ts', { functions: { 1: true, 2: false } }), + ]); + + expect(results.functions.coverage).toBe(50); + }); +}); + +describe('mergeCoverageResults', () => { + const emptyResult: UnprocessedCoverageResult = { + enums: { nodesCount: 0, issues: [] }, + interfaces: { nodesCount: 0, issues: [] }, + types: { nodesCount: 0, issues: [] }, + functions: { nodesCount: 0, issues: [] }, + variables: { nodesCount: 0, issues: [] }, + classes: { nodesCount: 0, issues: [] }, + methods: { nodesCount: 0, issues: [] }, + properties: { nodesCount: 0, issues: [] }, + }; + + it.each([ + 'enums', + 'interfaces', + 'types', + 'functions', + 'variables', + 'classes', + 'methods', + 'properties', + ])('should merge results on top-level property: %s', type => { + const secondResult = { + [type]: { + nodesCount: 1, + issues: [{ file: 'test2.ts', line: 1, name: 'test2', type }], + }, + }; + + const results = mergeCoverageResults( + emptyResult, + secondResult as Partial, + ); + expect(results).toStrictEqual( + expect.objectContaining({ + [type]: { + nodesCount: 1, + issues: [{ file: 'test2.ts', line: 1, name: 'test2', type }], + }, + }), + ); + }); + + it('should merge empty results', () => { + const results = mergeCoverageResults(emptyResult, emptyResult); + expect(results).toStrictEqual(emptyResult); + }); + + it('should merge second level property nodesCount', () => { + const results = mergeCoverageResults( + { + ...emptyResult, + enums: { nodesCount: 1, issues: [] }, + }, + { + enums: { nodesCount: 1, issues: [] }, + }, + ); + expect(results.enums.nodesCount).toBe(2); + }); + + it('should merge second level property issues', () => { + const results = mergeCoverageResults( + { + ...emptyResult, + enums: { + nodesCount: 0, + issues: [ + { + file: 'file.enum-first.ts', + line: 6, + name: 'file.enum-first', + type: 'enums', + }, + ], + }, + }, + { + enums: { + nodesCount: 0, + issues: [ + { + file: 'file.enum-second.ts', + line: 5, + name: 'file.enum-second', + type: 'enums', + }, + ], + }, + }, + ); + expect(results.enums.issues).toStrictEqual([ + { + file: 'file.enum-first.ts', + line: 6, + name: 'file.enum-first', + type: 'enums', + }, + { + file: 'file.enum-second.ts', + line: 5, + name: 'file.enum-second', + type: 'enums', + }, + ]); + }); +}); + +describe('getClassNodes', () => { + it('should return all nodes from a class', () => { + const nodeMock1 = nodeMock({ + coverageType: 'classes', + line: 1, + file: 'test.ts', + isCommented: false, + }); + + const classNodeSpy = vi.spyOn(nodeMock1, 'getMethods'); + const propertyNodeSpy = vi.spyOn(nodeMock1, 'getProperties'); + + getClassNodes([nodeMock1] as unknown as ClassDeclaration[]); + + expect(classNodeSpy).toHaveBeenCalledTimes(1); + expect(propertyNodeSpy).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts.snap b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts.snap new file mode 100644 index 000000000..220467a98 --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts.snap @@ -0,0 +1,125 @@ +{ + "classes": { + "coverage": 0, + "issues": [ + { + "file": "test.ts", + "line": 4, + "name": "test", + "type": "classes", + }, + { + "file": "test.ts", + "line": 5, + "name": "test", + "type": "classes", + }, + { + "file": "test.ts", + "line": 6, + "name": "test", + "type": "classes", + }, + ], + "nodesCount": 3, + }, + "enums": { + "coverage": 0, + "issues": [ + { + "file": "test.ts", + "line": 7, + "name": "test", + "type": "enums", + }, + { + "file": "test.ts", + "line": 8, + "name": "test", + "type": "enums", + }, + { + "file": "test.ts", + "line": 9, + "name": "test", + "type": "enums", + }, + ], + "nodesCount": 3, + }, + "functions": { + "coverage": 66.66666666666667, + "issues": [ + { + "file": "test.ts", + "line": 3, + "name": "test", + "type": "functions", + }, + ], + "nodesCount": 3, + }, + "interfaces": { + "coverage": 0, + "issues": [ + { + "file": "test.ts", + "line": 13, + "name": "test", + "type": "interfaces", + }, + { + "file": "test.ts", + "line": 14, + "name": "test", + "type": "interfaces", + }, + { + "file": "test.ts", + "line": 15, + "name": "test", + "type": "interfaces", + }, + ], + "nodesCount": 3, + }, + "methods": { + "coverage": 100, + "issues": [], + "nodesCount": 0, + }, + "properties": { + "coverage": 100, + "issues": [], + "nodesCount": 0, + }, + "types": { + "coverage": 0, + "issues": [ + { + "file": "test.ts", + "line": 10, + "name": "test", + "type": "types", + }, + { + "file": "test.ts", + "line": 11, + "name": "test", + "type": "types", + }, + { + "file": "test.ts", + "line": 12, + "name": "test", + "type": "types", + }, + ], + "nodesCount": 3, + }, + "variables": { + "coverage": 100, + "issues": [], + "nodesCount": 0, + }, +} \ No newline at end of file diff --git a/packages/plugin-doc-coverage/src/lib/runner/index.ts b/packages/plugin-doc-coverage/src/lib/runner/index.ts deleted file mode 100644 index c56713ada..000000000 --- a/packages/plugin-doc-coverage/src/lib/runner/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { bold } from 'ansis'; -import { writeFile } from 'node:fs/promises'; -import path from 'node:path'; -import type { AuditOutput, RunnerConfig } from '@code-pushup/models'; -import { - ProcessError, - ensureDirectoryExists, - filePathToCliArg, - readJsonFile, - ui, -} from '@code-pushup/utils'; -import type { DocCoveragePluginConfig } from '../config.js'; -import type { CoverageResult } from '../models.js'; -import { PLUGIN_CONFIG_PATH, RUNNER_OUTPUT_PATH } from './constants.js'; -import { processDocCoverage } from './doc-processer.js'; - -export { PLUGIN_CONFIG_PATH, RUNNER_OUTPUT_PATH } from './constants.js'; - -export async function executeRunner(): Promise { - try { - const config = - await readJsonFile(PLUGIN_CONFIG_PATH); - console.log(config.sourceGlob, 'dadawdawd'); - const processResult = processDocCoverage(config.sourceGlob); - await _createFinalReport(processResult); - } catch (error) { - if (error instanceof ProcessError) { - ui().logger.error(bold('stdout from failed Typedoc process:')); - ui().logger.error(error.stdout); - ui().logger.error(bold('stderr from failed Typedoc process:')); - ui().logger.error(error.stderr); - } - throw new Error( - 'Doc Coverage plugin: Running Typedoc failed. Please check the error above.', - ); - } -} - -export async function createRunnerConfig( - scriptPath: string, - config: DocCoveragePluginConfig, -): Promise { - await ensureDirectoryExists(path.dirname(PLUGIN_CONFIG_PATH)); - await writeFile(PLUGIN_CONFIG_PATH, JSON.stringify(config)); - - return { - command: 'node', - args: [filePathToCliArg(scriptPath)], - outputFile: RUNNER_OUTPUT_PATH, - }; -} - -/** - * Create the final report. - * @param coverageResult - The coverage result. - */ -async function _createFinalReport( - coverageResult: CoverageResult, -): Promise { - const auditOutputs: AuditOutput[] = [ - { - slug: 'percentage-coverage', - value: coverageResult.currentCoverage, - score: coverageResult.currentCoverage / 100, - displayValue: `${coverageResult.currentCoverage} %`, - details: { - issues: coverageResult.undocumentedItems.map(item => ({ - message: `Missing documentation for a ${item.type}`, - source: { file: item.file, position: { startLine: item.line } }, - severity: 'warning', - })), - }, - }, - ]; - - await ensureDirectoryExists(path.dirname(RUNNER_OUTPUT_PATH)); - await writeFile(RUNNER_OUTPUT_PATH, JSON.stringify(auditOutputs)); -} diff --git a/packages/plugin-doc-coverage/src/lib/runner/models.ts b/packages/plugin-doc-coverage/src/lib/runner/models.ts new file mode 100644 index 000000000..81849c428 --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/runner/models.ts @@ -0,0 +1,37 @@ +import type { SyntaxKind } from 'ts-morph'; + +type SyntaxKindToStringLiteral = { + [SyntaxKind.ClassDeclaration]: 'classes'; + [SyntaxKind.MethodDeclaration]: 'methods'; + [SyntaxKind.FunctionDeclaration]: 'functions'; + [SyntaxKind.InterfaceDeclaration]: 'interfaces'; + [SyntaxKind.EnumDeclaration]: 'enums'; + [SyntaxKind.VariableDeclaration]: 'variables'; + [SyntaxKind.PropertyDeclaration]: 'properties'; + [SyntaxKind.TypeAliasDeclaration]: 'types'; +}; + +export type CoverageType = + SyntaxKindToStringLiteral[keyof SyntaxKindToStringLiteral]; + +export type UndocumentedNode = { + file: string; + type: CoverageType; + name: string; + line: number; + class?: string; +}; + +export type CoverageData = { + issues: UndocumentedNode[]; + nodesCount: number; +}; + +export type UnprocessedCoverageResult = Record; + +export type CoverageResult = Record< + CoverageType, + CoverageData & { + coverage: number; + } +>; diff --git a/packages/plugin-doc-coverage/src/lib/runner/runner.integration.test.ts b/packages/plugin-doc-coverage/src/lib/runner/runner.integration.test.ts deleted file mode 100644 index 8d8fe081a..000000000 --- a/packages/plugin-doc-coverage/src/lib/runner/runner.integration.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { writeFile } from 'node:fs/promises'; -import { describe, it } from 'vitest'; -import type { AuditOutputs, RunnerConfig } from '@code-pushup/models'; -import { readJsonFile, removeDirectoryIfExists } from '@code-pushup/utils'; -import type { DocCoveragePluginConfig } from '../config.js'; -import { - PLUGIN_CONFIG_PATH, - RUNNER_OUTPUT_PATH, - WORKDIR, -} from './constants.js'; -import { createRunnerConfig, executeRunner } from './index.js'; - -describe('createRunnerConfig', () => { - it('should create a valid runner config', async () => { - const runnerConfig = await createRunnerConfig('executeRunner.ts', { - sourceGlob: 'src/**/*.ts', - }); - expect(runnerConfig).toStrictEqual({ - command: 'node', - args: ['"executeRunner.ts"'], - outputFile: expect.stringContaining('runner-output.json'), - }); - }); - - it('should provide plugin config to runner in JSON file', async () => { - await removeDirectoryIfExists(WORKDIR); - - const pluginConfig: DocCoveragePluginConfig = { - sourceGlob: 'src/**/*.ts', - }; - - await createRunnerConfig('executeRunner.ts', pluginConfig); - - const config = - await readJsonFile(PLUGIN_CONFIG_PATH); - expect(config).toStrictEqual(pluginConfig); - }); -}); - -describe('executeRunner', () => { - it( - 'should successfully execute runner', - { - timeout: 60 * 1000, - }, - async () => { - const config: DocCoveragePluginConfig = { - sourceGlob: 'packages/plugin-doc-coverage/mocks/*.ts', - }; - - await writeFile(PLUGIN_CONFIG_PATH, JSON.stringify(config)); - await executeRunner(); - - const results = await readJsonFile(RUNNER_OUTPUT_PATH); - expect(results).toBeDefined(); - }, - ); -}); diff --git a/packages/plugin-doc-coverage/src/lib/runner/runner.ts b/packages/plugin-doc-coverage/src/lib/runner/runner.ts new file mode 100644 index 000000000..91e8282e1 --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/runner/runner.ts @@ -0,0 +1,49 @@ +import type { AuditOutputs, RunnerFunction } from '@code-pushup/models'; +import type { DocCoveragePluginConfig } from '../config'; +import { processDocCoverage } from './doc-processer'; +import type { CoverageResult, CoverageType } from './models'; + +export function createRunnerFunction( + config: DocCoveragePluginConfig, +): RunnerFunction { + return (): AuditOutputs => { + const coverageResult = processDocCoverage(config); + return trasformCoverageReportToAudits(coverageResult, config); + }; +} + +/** + * Transforms the coverage report into audit outputs. + * @param coverageResult - The coverage result containing undocumented items and coverage statistics + * @param options - Configuration options specifying which audits to include + * @returns Audit outputs with coverage scores and details about undocumented items + */ +export function trasformCoverageReportToAudits( + coverageResult: CoverageResult, + options: Pick, +): AuditOutputs { + return Object.entries(coverageResult) + .filter( + ([type]) => + !options.onlyAudits?.length || + options.onlyAudits.includes(`${type}-coverage`), + ) + .map(([type, items]) => { + const coverageType = type as CoverageType; + const coverage = items.coverage; + + return { + slug: `${coverageType}-coverage`, + value: coverage, + score: coverage / 100, + displayValue: `${coverage} %`, + details: { + issues: items.issues.map(({ file, line }) => ({ + message: 'Missing documentation', + source: { file, position: { startLine: line } }, + severity: 'warning', + })), + }, + }; + }); +} diff --git a/packages/plugin-doc-coverage/src/lib/runner/utils.ts b/packages/plugin-doc-coverage/src/lib/runner/utils.ts new file mode 100644 index 000000000..434187a00 --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/runner/utils.ts @@ -0,0 +1,61 @@ +import { SyntaxKind } from 'ts-morph'; +import type { + CoverageResult, + CoverageType, + UnprocessedCoverageResult, +} from './models'; + +export function createEmptyUnprocessedCoverageReport(): UnprocessedCoverageResult { + return { + enums: { nodesCount: 0, issues: [] }, + interfaces: { nodesCount: 0, issues: [] }, + types: { nodesCount: 0, issues: [] }, + functions: { nodesCount: 0, issues: [] }, + variables: { nodesCount: 0, issues: [] }, + classes: { nodesCount: 0, issues: [] }, + methods: { nodesCount: 0, issues: [] }, + properties: { nodesCount: 0, issues: [] }, + }; +} + +export function calculateCoverage(result: UnprocessedCoverageResult) { + return Object.fromEntries( + Object.entries(result).map(([key, value]) => { + const type = key as CoverageType; + return [ + type, + { + coverage: + value.nodesCount === 0 + ? 100 + : (1 - value.issues.length / value.nodesCount) * 100, + issues: value.issues, + nodesCount: value.nodesCount, + }, + ]; + }), + ) as CoverageResult; +} + +export function getCoverageTypeFromKind(kind: SyntaxKind): CoverageType { + switch (kind) { + case SyntaxKind.ClassDeclaration: + return 'classes'; + case SyntaxKind.MethodDeclaration: + return 'methods'; + case SyntaxKind.FunctionDeclaration: + return 'functions'; + case SyntaxKind.InterfaceDeclaration: + return 'interfaces'; + case SyntaxKind.EnumDeclaration: + return 'enums'; + case SyntaxKind.VariableDeclaration: + return 'variables'; + case SyntaxKind.PropertyDeclaration: + return 'properties'; + case SyntaxKind.TypeAliasDeclaration: + return 'types'; + default: + throw new Error(`Unsupported syntax kind: ${kind}`); + } +} diff --git a/packages/plugin-doc-coverage/src/lib/utils.ts b/packages/plugin-doc-coverage/src/lib/utils.ts new file mode 100644 index 000000000..b3e7584cb --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/utils.ts @@ -0,0 +1,46 @@ +import type { Audit, Group } from '@code-pushup/models'; +import type { DocCoveragePluginConfig } from './config'; +import { AUDITS_MAP } from './constants'; + +/** + * Get audits based on the configuration. + * If no audits are specified, return all audits. + * If audits are specified, return only the specified audits. + * @param config - The configuration object. + * @returns The audits. + */ +export function filterAuditsByPluginConfig( + config: Pick, +): Audit[] { + const { onlyAudits } = config; + + if (!onlyAudits || onlyAudits.length === 0) { + return Object.values(AUDITS_MAP); + } + + return Object.values(AUDITS_MAP).filter(audit => + onlyAudits.includes(audit.slug), + ); +} + +/** + * Filter groups by the audits that are specified in the configuration. + * The groups refs are filtered to only include the audits that are specified in the configuration. + * @param groups - The groups to filter. + * @param options - The configuration object. + * @returns The filtered groups. + */ +export function filterGroupsByOnlyAudits( + groups: Group[], + options: Pick, +): Group[] { + const audits = filterAuditsByPluginConfig(options); + return groups + .map(group => ({ + ...group, + refs: group.refs.filter(ref => + audits.some(audit => audit.slug === ref.slug), + ), + })) + .filter(group => group.refs.length > 0); +} diff --git a/packages/plugin-doc-coverage/tsconfig.test.json b/packages/plugin-doc-coverage/tsconfig.test.json index 05637ee6e..9f29d6bb0 100644 --- a/packages/plugin-doc-coverage/tsconfig.test.json +++ b/packages/plugin-doc-coverage/tsconfig.test.json @@ -8,7 +8,6 @@ "vite.config.unit.ts", "vite.config.integration.ts", "mocks/**/*.ts", - "src/**/*.test.ts", - "mocks/component-mock.ts" + "src/**/*.test.ts" ] } From 40c8daa41852741d53b5c66b6f2ed5ded0fbc5d3 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Fri, 20 Dec 2024 18:43:20 +0100 Subject: [PATCH 06/39] test(plugin-doc-coverage): finish remaining tests, add format to coverage --- .../src/lib/config.unit.test.ts | 81 +++++++++++++++--- .../src/lib/doc-coverage-plugin.ts | 6 +- .../src/lib/doc-coverage-plugin.unit.test.ts | 54 +++--------- .../doc-processer.integration.test.ts.snap | 2 +- .../doc-processer.unit.test.ts.snap | 6 +- .../src/lib/runner/utils.ts | 6 +- .../src/lib/runner/utils.unit.test.ts | 85 +++++++++++++++++++ .../src/lib/utils.unit.test.ts | 79 +++++++++++++++++ 8 files changed, 259 insertions(+), 60 deletions(-) create mode 100644 packages/plugin-doc-coverage/src/lib/runner/utils.unit.test.ts create mode 100644 packages/plugin-doc-coverage/src/lib/utils.unit.test.ts diff --git a/packages/plugin-doc-coverage/src/lib/config.unit.test.ts b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts index 63d0007b9..66f33ab52 100644 --- a/packages/plugin-doc-coverage/src/lib/config.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts @@ -5,23 +5,80 @@ import { } from './config.js'; describe('docCoveragePluginConfigSchema', () => { - it('accepts a valid source glob pattern', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - sourceGlob: ['src/**/*.{ts,tsx}', '!**/*.spec.ts', '!**/*.test.ts'], - } satisfies DocCoveragePluginConfig), - ).not.toThrow(); + describe('sourceGlob', () => { + it('accepts a valid source glob pattern', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + sourceGlob: ['src/**/*.{ts,tsx}', '!**/*.spec.ts', '!**/*.test.ts'], + } satisfies DocCoveragePluginConfig), + ).not.toThrow(); + }); + + it('uses default value for missing sourceGlob', () => { + const result = docCoveragePluginConfigSchema.parse({}); + expect(result.sourceGlob).toEqual([ + 'src/**/*.{ts,tsx}', + '!**/*.spec.ts', + '!**/*.test.ts', + ]); + }); + + it('throws for invalid sourceGlob type', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + sourceGlob: 123, + }), + ).toThrow('Expected array'); + }); }); - it('not throws for missing sourceGlob', () => { - expect(() => docCoveragePluginConfigSchema.parse({})).not.toThrow(); + describe('onlyAudits', () => { + it('accepts valid audit slugs array', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + onlyAudits: ['functions-coverage', 'classes-coverage'], + sourceGlob: ['src/**/*.ts'], + }), + ).not.toThrow(); + }); + + it('accepts empty array for onlyAudits', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + onlyAudits: [], + sourceGlob: ['src/**/*.ts'], + }), + ).not.toThrow(); + }); + + it('allows onlyAudits to be undefined', () => { + const result = docCoveragePluginConfigSchema.parse({}); + expect(result.onlyAudits).toBeUndefined(); + }); + + it('throws for invalid onlyAudits type', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + onlyAudits: 'functions-coverage', + }), + ).toThrow('Expected array'); + }); + + it('throws for array with non-string elements', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + onlyAudits: [123, true], + }), + ).toThrow('Expected string'); + }); }); - it('throws for invalid sourceGlob type', () => { + it('accepts a complete valid configuration', () => { expect(() => docCoveragePluginConfigSchema.parse({ - sourceGlob: 123, - }), - ).toThrow('Expected string'); + sourceGlob: ['src/**/*.ts'], + onlyAudits: ['functions-coverage'], + } satisfies DocCoveragePluginConfig), + ).not.toThrow(); }); }); diff --git a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts index 371409e1b..9d08784b1 100644 --- a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts +++ b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts @@ -10,12 +10,12 @@ import { filterGroupsByOnlyAudits, } from './utils.js'; -const PLUGIN_TITLE = 'Documentation coverage'; +export const PLUGIN_TITLE = 'Documentation coverage'; -const PLUGIN_DESCRIPTION = +export const PLUGIN_DESCRIPTION = 'Official Code PushUp documentation coverage plugin.'; -const PLUGIN_DOCS_URL = +export const PLUGIN_DOCS_URL = 'https://www.npmjs.com/package/@code-pushup/doc-coverage-plugin/'; /** diff --git a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts index d3d178a91..fea278277 100644 --- a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts @@ -1,6 +1,12 @@ import { describe, expect, it } from 'vitest'; import type { RunnerConfig } from '@code-pushup/models'; -import { docCoveragePlugin } from './doc-coverage-plugin.js'; +import { PLUGIN_SLUG } from './constants.js'; +import { + PLUGIN_DESCRIPTION, + PLUGIN_DOCS_URL, + PLUGIN_TITLE, + docCoveragePlugin, +} from './doc-coverage-plugin.js'; vi.mock('./runner/index.ts', () => ({ createRunnerConfig: vi.fn().mockReturnValue({ @@ -17,46 +23,14 @@ describe('docCoveragePlugin', () => { }), ).resolves.toStrictEqual( expect.objectContaining({ - slug: 'doc-coverage', - title: 'Documentation coverage', - audits: expect.any(Array), - runner: expect.any(Object), - }), - ); - }); - - it('should generate percentage coverage audit', async () => { - await expect( - docCoveragePlugin({ - sourceGlob: ['src/**/*.ts', '!**/*.spec.ts', '!**/*.test.ts'], - }), - ).resolves.toStrictEqual( - expect.objectContaining({ - audits: [ - { - slug: 'percentage-coverage', - title: 'Percentage of codebase with documentation', - description: expect.stringContaining( - 'how many % of the codebase have documentation', - ), - }, - ], - }), - ); - }); - - it('should include package metadata', async () => { - await expect( - docCoveragePlugin({ - sourceGlob: ['src/**/*.ts', '!**/*.spec.ts', '!**/*.test.ts'], - }), - ).resolves.toStrictEqual( - expect.objectContaining({ + slug: PLUGIN_SLUG, + title: PLUGIN_TITLE, icon: 'folder-src', - description: expect.stringContaining('documentation coverage plugin'), - docsUrl: expect.stringContaining('npmjs.com'), - packageName: expect.any(String), - version: expect.any(String), + description: PLUGIN_DESCRIPTION, + docsUrl: PLUGIN_DOCS_URL, + groups: expect.any(Array), + audits: expect.any(Array), + runner: expect.any(Function), }), ); }); diff --git a/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.integration.test.ts.snap b/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.integration.test.ts.snap index 33a38bd67..2507460f6 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.integration.test.ts.snap +++ b/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.integration.test.ts.snap @@ -13,7 +13,7 @@ exports[`processDocCoverage > should succesfully get the right number of ts file "nodesCount": 0, }, "functions": { - "coverage": 33.333333333333336, + "coverage": 33.33, "issues": [ { "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.spec.ts", diff --git a/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.unit.test.ts.snap b/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.unit.test.ts.snap index b72b0a151..1090891fe 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.unit.test.ts.snap +++ b/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.unit.test.ts.snap @@ -3,7 +3,7 @@ exports[`getUnprocessedCoverageReport > should produce a full report 1`] = ` { "classes": { - "coverage": 33.333333333333336, + "coverage": 33.33, "issues": [ { "file": "test.ts", @@ -21,7 +21,7 @@ exports[`getUnprocessedCoverageReport > should produce a full report 1`] = ` "nodesCount": 3, }, "enums": { - "coverage": 33.333333333333336, + "coverage": 33.33, "issues": [ { "file": "test.ts", @@ -44,7 +44,7 @@ exports[`getUnprocessedCoverageReport > should produce a full report 1`] = ` "nodesCount": 3, }, "interfaces": { - "coverage": 66.66666666666667, + "coverage": 66.67, "issues": [ { "file": "test.ts", diff --git a/packages/plugin-doc-coverage/src/lib/runner/utils.ts b/packages/plugin-doc-coverage/src/lib/runner/utils.ts index 434187a00..e9628eac6 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/utils.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/utils.ts @@ -28,7 +28,11 @@ export function calculateCoverage(result: UnprocessedCoverageResult) { coverage: value.nodesCount === 0 ? 100 - : (1 - value.issues.length / value.nodesCount) * 100, + : Number( + ((1 - value.issues.length / value.nodesCount) * 100).toFixed( + 2, + ), + ), issues: value.issues, nodesCount: value.nodesCount, }, diff --git a/packages/plugin-doc-coverage/src/lib/runner/utils.unit.test.ts b/packages/plugin-doc-coverage/src/lib/runner/utils.unit.test.ts new file mode 100644 index 000000000..403c7b3ad --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/runner/utils.unit.test.ts @@ -0,0 +1,85 @@ +import { SyntaxKind } from 'ts-morph'; +import type { UnprocessedCoverageResult } from './models'; +import { + calculateCoverage, + createEmptyUnprocessedCoverageReport, + getCoverageTypeFromKind, +} from './utils'; + +describe('createEmptyUnprocessedCoverageReport', () => { + it('should create an empty report with all categories initialized', () => { + const result = createEmptyUnprocessedCoverageReport(); + + expect(result).toStrictEqual({ + enums: { nodesCount: 0, issues: [] }, + interfaces: { nodesCount: 0, issues: [] }, + types: { nodesCount: 0, issues: [] }, + functions: { nodesCount: 0, issues: [] }, + variables: { nodesCount: 0, issues: [] }, + classes: { nodesCount: 0, issues: [] }, + methods: { nodesCount: 0, issues: [] }, + properties: { nodesCount: 0, issues: [] }, + }); + }); +}); + +describe('calculateCoverage', () => { + it('should calculate 100% coverage when there are no nodes', () => { + const input: UnprocessedCoverageResult = + createEmptyUnprocessedCoverageReport(); + const result = calculateCoverage(input); + + Object.values(result).forEach(category => { + expect(category.coverage).toBe(100); + expect(category.nodesCount).toBe(0); + expect(category.issues).toEqual([]); + }); + }); + + it('should calculate correct coverage percentage with issues', () => { + const input: UnprocessedCoverageResult = { + ...createEmptyUnprocessedCoverageReport(), + functions: { + nodesCount: 4, + issues: [ + { type: 'functions', line: 1, file: 'test.ts', name: 'fn1' }, + { type: 'functions', line: 2, file: 'test.ts', name: 'fn2' }, + ], + }, + classes: { + nodesCount: 4, + issues: [ + { type: 'classes', line: 1, file: 'test.ts', name: 'Class1' }, + { type: 'classes', line: 2, file: 'test.ts', name: 'Class2' }, + { type: 'classes', line: 3, file: 'test.ts', name: 'Class3' }, + ], + }, + }; + + const result = calculateCoverage(input); + + expect(result.functions.coverage).toBe(50); + expect(result.classes.coverage).toBe(25); + }); +}); + +describe('getCoverageTypeFromKind', () => { + it.each([ + [SyntaxKind.ClassDeclaration, 'classes'], + [SyntaxKind.MethodDeclaration, 'methods'], + [SyntaxKind.FunctionDeclaration, 'functions'], + [SyntaxKind.InterfaceDeclaration, 'interfaces'], + [SyntaxKind.EnumDeclaration, 'enums'], + [SyntaxKind.VariableDeclaration, 'variables'], + [SyntaxKind.PropertyDeclaration, 'properties'], + [SyntaxKind.TypeAliasDeclaration, 'types'], + ])('should return %s for SyntaxKind.%s', (kind, expectedType) => { + expect(getCoverageTypeFromKind(kind)).toBe(expectedType); + }); + + it('should throw error for unsupported syntax kind', () => { + expect(() => getCoverageTypeFromKind(SyntaxKind.Unknown)).toThrow( + 'Unsupported syntax kind', + ); + }); +}); diff --git a/packages/plugin-doc-coverage/src/lib/utils.unit.test.ts b/packages/plugin-doc-coverage/src/lib/utils.unit.test.ts new file mode 100644 index 000000000..ec5ec4008 --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/utils.unit.test.ts @@ -0,0 +1,79 @@ +import type { Group } from '@code-pushup/models'; +import { AUDITS_MAP } from './constants'; +import { filterAuditsByPluginConfig, filterGroupsByOnlyAudits } from './utils'; + +describe('filterAuditsByPluginConfig', () => { + it('should return all audits when onlyAudits is not provided', () => { + const result = filterAuditsByPluginConfig({}); + expect(result).toStrictEqual(Object.values(AUDITS_MAP)); + }); + + it('should return all audits when onlyAudits is empty array', () => { + const result = filterAuditsByPluginConfig({ onlyAudits: [] }); + expect(result).toStrictEqual(Object.values(AUDITS_MAP)); + }); + + it('should return only specified audits when onlyAudits is provided', () => { + const onlyAudits = ['functions-coverage', 'classes-coverage']; + const result = filterAuditsByPluginConfig({ onlyAudits }); + + expect(result).toStrictEqual( + Object.values(AUDITS_MAP).filter(audit => + onlyAudits.includes(audit.slug), + ), + ); + }); +}); + +describe('filterGroupsByOnlyAudits', () => { + const mockGroups: Group[] = [ + { + title: 'Group 1', + slug: 'group-1', + refs: [ + { slug: 'functions-coverage', weight: 1 }, + { slug: 'classes-coverage', weight: 1 }, + ], + }, + { + title: 'Group 2', + slug: 'group-2', + refs: [ + { slug: 'types-coverage', weight: 1 }, + { slug: 'interfaces-coverage', weight: 1 }, + ], + }, + ]; + + it('should return all groups when onlyAudits is not provided', () => { + const result = filterGroupsByOnlyAudits(mockGroups, {}); + expect(result).toStrictEqual(mockGroups); + }); + + it('should return all groups when onlyAudits is empty array', () => { + const result = filterGroupsByOnlyAudits(mockGroups, { onlyAudits: [] }); + expect(result).toStrictEqual(mockGroups); + }); + + it('should filter groups based on specified audits', () => { + const result = filterGroupsByOnlyAudits(mockGroups, { + onlyAudits: ['functions-coverage'], + }); + + expect(result).toStrictEqual([ + { + title: 'Group 1', + slug: 'group-1', + refs: [{ slug: 'functions-coverage', weight: 1 }], + }, + ]); + }); + + it('should remove groups with no matching refs', () => { + const result = filterGroupsByOnlyAudits(mockGroups, { + onlyAudits: ['enums-coverage'], + }); + + expect(result).toStrictEqual([]); + }); +}); From 47486c80c6262017d6bc5e902bac50d712cae9c4 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Fri, 20 Dec 2024 19:32:30 +0100 Subject: [PATCH 07/39] feat(plugin-doc-coverage): add skipAudits options to the plugin, create unit test for runner file --- code-pushup.config.ts | 41 +++-- .../plugin-doc-coverage/src/lib/config.ts | 18 ++- .../src/lib/config.unit.test.ts | 42 ++++- .../src/lib/doc-coverage-plugin.ts | 2 +- .../__snapshots__/runner.unit.test.ts.snap | 147 ++++++++++++++++++ .../src/lib/runner/models.ts | 6 + .../src/lib/runner/runner.ts | 19 ++- .../src/lib/runner/runner.unit.test.ts | 88 +++++++++++ .../src/lib/runner/utils.ts | 14 ++ packages/plugin-doc-coverage/src/lib/utils.ts | 24 +-- .../src/lib/utils.unit.test.ts | 19 ++- 11 files changed, 364 insertions(+), 56 deletions(-) create mode 100644 packages/plugin-doc-coverage/src/lib/runner/__snapshots__/runner.unit.test.ts.snap create mode 100644 packages/plugin-doc-coverage/src/lib/runner/runner.unit.test.ts diff --git a/code-pushup.config.ts b/code-pushup.config.ts index ddfcd59f6..0b2d52680 100644 --- a/code-pushup.config.ts +++ b/code-pushup.config.ts @@ -1,12 +1,6 @@ import 'dotenv/config'; import { z } from 'zod'; -import { - coverageCoreConfigNx, - docCoverageCoreConfig, - eslintCoreConfigNx, - jsPackagesCoreConfig, - lighthouseCoreConfig, -} from './code-pushup.preset.js'; +import { docCoverageCoreConfig } from './code-pushup.preset.js'; import type { CoreConfig } from './packages/models/src/index.js'; import { mergeConfigs } from './packages/utils/src/index.js'; @@ -33,13 +27,13 @@ const config: CoreConfig = { }; export default mergeConfigs( - config, - await coverageCoreConfigNx(), - await jsPackagesCoreConfig(), - await lighthouseCoreConfig( - 'https://github.com/code-pushup/cli?tab=readme-ov-file#code-pushup-cli/', - ), - await eslintCoreConfigNx(), + // config, + // await coverageCoreConfigNx(), + // await jsPackagesCoreConfig(), + // await lighthouseCoreConfig( + // 'https://github.com/code-pushup/cli?tab=readme-ov-file#code-pushup-cli/', + // ), + // await eslintCoreConfigNx(), await docCoverageCoreConfig({ sourceGlob: [ 'packages/**/src/**/*.ts', @@ -48,14 +42,15 @@ export default mergeConfigs( '!**/implementation/**', '!**/internal/**', ], - onlyAudits: [ - 'methods-coverage', - 'functions-coverage', - 'types-coverage', - 'classes-coverage', - 'interfaces-coverage', - 'enums-coverage', - 'type-aliases-coverage', - ], + skipAudits: ['methods-coverage'], + // onlyAudits: [ + // 'methods-coverage', + // 'functions-coverage', + // 'types-coverage', + // 'classes-coverage', + // 'interfaces-coverage', + // 'enums-coverage', + // 'type-aliases-coverage', + // ], }), ); diff --git a/packages/plugin-doc-coverage/src/lib/config.ts b/packages/plugin-doc-coverage/src/lib/config.ts index 52ccb5cb8..648420a1d 100644 --- a/packages/plugin-doc-coverage/src/lib/config.ts +++ b/packages/plugin-doc-coverage/src/lib/config.ts @@ -1,11 +1,17 @@ import { z } from 'zod'; -export const docCoveragePluginConfigSchema = z.object({ - onlyAudits: z.array(z.string()).optional(), - sourceGlob: z - .array(z.string()) - .default(['src/**/*.{ts,tsx}', '!**/*.spec.ts', '!**/*.test.ts']), -}); +export const docCoveragePluginConfigSchema = z + .object({ + skipAudits: z.array(z.string()).optional(), + onlyAudits: z.array(z.string()).optional(), + sourceGlob: z + .array(z.string()) + .default(['src/**/*.{ts,tsx}', '!**/*.spec.ts', '!**/*.test.ts']), + }) + .refine(data => !(data.skipAudits && data.onlyAudits), { + message: "You can't define 'skipAudits' and 'onlyAudits' simultaneously", + path: ['skipAudits', 'onlyAudits'], + }); export type DocCoveragePluginConfig = z.infer< typeof docCoveragePluginConfigSchema diff --git a/packages/plugin-doc-coverage/src/lib/config.unit.test.ts b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts index 66f33ab52..1d28d2a7d 100644 --- a/packages/plugin-doc-coverage/src/lib/config.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts @@ -5,6 +5,15 @@ import { } from './config.js'; describe('docCoveragePluginConfigSchema', () => { + it('throws when skipAudits and onlyAudits are defined', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + skipAudits: ['functions-coverage'], + onlyAudits: ['classes-coverage'], + }), + ).toThrow("You can't define 'skipAudits' and 'onlyAudits' simultaneously"); + }); + describe('sourceGlob', () => { it('accepts a valid source glob pattern', () => { expect(() => @@ -32,6 +41,15 @@ describe('docCoveragePluginConfigSchema', () => { }); }); + it('accepts a complete valid configuration', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + sourceGlob: ['src/**/*.ts'], + onlyAudits: ['functions-coverage'], + } satisfies DocCoveragePluginConfig), + ).not.toThrow(); + }); + describe('onlyAudits', () => { it('accepts valid audit slugs array', () => { expect(() => @@ -73,12 +91,22 @@ describe('docCoveragePluginConfigSchema', () => { }); }); - it('accepts a complete valid configuration', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - sourceGlob: ['src/**/*.ts'], - onlyAudits: ['functions-coverage'], - } satisfies DocCoveragePluginConfig), - ).not.toThrow(); + describe('skipAudits', () => { + it('accepts valid audit slugs array', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + skipAudits: ['functions-coverage', 'classes-coverage'], + sourceGlob: ['src/**/*.ts'], + }), + ).not.toThrow(); + }); + + it('throws for array with non-string elements', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + skipAudits: [123, true], + }), + ).toThrow('Expected string'); + }); }); }); diff --git a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts index 9d08784b1..d452839d6 100644 --- a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts +++ b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts @@ -29,7 +29,7 @@ export const PLUGIN_DOCS_URL = * plugins: [ * // ... other plugins ... * await docCoveragePlugin({ - * sourceGlob: 'src/**/*.{ts,tsx}' + * sourceGlob: 'src/**/*.{ts,tsx}', * }) * ] * } diff --git a/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/runner.unit.test.ts.snap b/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/runner.unit.test.ts.snap new file mode 100644 index 000000000..9db5313d1 --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/runner.unit.test.ts.snap @@ -0,0 +1,147 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`trasformCoverageReportToAudits > should filter audits when onlyAudits is provided 1`] = ` +[ + { + "details": { + "issues": [ + { + "message": "Missing documentation", + "severity": "warning", + "source": { + "file": "test.ts", + "position": { + "startLine": 10, + }, + }, + }, + ], + }, + "displayValue": "75 %", + "score": 0.75, + "slug": "functions-coverage", + "value": 75, + }, +] +`; + +exports[`trasformCoverageReportToAudits > should filter audits when skipAudits is provided 1`] = ` +[ + { + "details": { + "issues": [ + { + "message": "Missing documentation", + "severity": "warning", + "source": { + "file": "test.ts", + "position": { + "startLine": 10, + }, + }, + }, + ], + }, + "displayValue": "75 %", + "score": 0.75, + "slug": "functions-coverage", + "value": 75, + }, +] +`; + +exports[`trasformCoverageReportToAudits > should handle coverage result with multiple issues 1`] = ` +[ + { + "details": { + "issues": [ + { + "message": "Missing documentation", + "severity": "warning", + "source": { + "file": "test1.ts", + "position": { + "startLine": 10, + }, + }, + }, + { + "message": "Missing documentation", + "severity": "warning", + "source": { + "file": "test2.ts", + "position": { + "startLine": 20, + }, + }, + }, + ], + }, + "displayValue": "50 %", + "score": 0.5, + "slug": "functions-coverage", + "value": 50, + }, +] +`; + +exports[`trasformCoverageReportToAudits > should handle empty coverage result 1`] = `[]`; + +exports[`trasformCoverageReportToAudits > should prioritize onlyAudits over skipAudits when both are provided 1`] = ` +[ + { + "details": { + "issues": [ + { + "message": "Missing documentation", + "severity": "warning", + "source": { + "file": "test.ts", + "position": { + "startLine": 10, + }, + }, + }, + ], + }, + "displayValue": "75 %", + "score": 0.75, + "slug": "functions-coverage", + "value": 75, + }, +] +`; + +exports[`trasformCoverageReportToAudits > should transform coverage report to audit outputs with no filters 1`] = ` +[ + { + "details": { + "issues": [ + { + "message": "Missing documentation", + "severity": "warning", + "source": { + "file": "test.ts", + "position": { + "startLine": 10, + }, + }, + }, + ], + }, + "displayValue": "75 %", + "score": 0.75, + "slug": "functions-coverage", + "value": 75, + }, + { + "details": { + "issues": [], + }, + "displayValue": "100 %", + "score": 1, + "slug": "classes-coverage", + "value": 100, + }, +] +`; diff --git a/packages/plugin-doc-coverage/src/lib/runner/models.ts b/packages/plugin-doc-coverage/src/lib/runner/models.ts index 81849c428..c8119b4db 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/models.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/models.ts @@ -1,5 +1,6 @@ import type { SyntaxKind } from 'ts-morph'; +/** Maps the SyntaxKind from the library ts-morph to the coverage type. */ type SyntaxKindToStringLiteral = { [SyntaxKind.ClassDeclaration]: 'classes'; [SyntaxKind.MethodDeclaration]: 'methods'; @@ -11,9 +12,11 @@ type SyntaxKindToStringLiteral = { [SyntaxKind.TypeAliasDeclaration]: 'types'; }; +/**The coverage type is the same as the SyntaxKind from the library ts-morph but as a string. */ export type CoverageType = SyntaxKindToStringLiteral[keyof SyntaxKindToStringLiteral]; +/** The undocumented node is the node that is not documented and has the information for the report. */ export type UndocumentedNode = { file: string; type: CoverageType; @@ -22,13 +25,16 @@ export type UndocumentedNode = { class?: string; }; +/** The coverage data is the data that is used to create the coverage report. Without coverage stats yet */ export type CoverageData = { issues: UndocumentedNode[]; nodesCount: number; }; +/** The unprocessed coverage result CoverageData but for each coverage type. */ export type UnprocessedCoverageResult = Record; +/** The processed coverage result CoverageData but for each coverage type and with coverage stats. */ export type CoverageResult = Record< CoverageType, CoverageData & { diff --git a/packages/plugin-doc-coverage/src/lib/runner/runner.ts b/packages/plugin-doc-coverage/src/lib/runner/runner.ts index 91e8282e1..bb037e933 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/runner.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/runner.ts @@ -15,19 +15,24 @@ export function createRunnerFunction( /** * Transforms the coverage report into audit outputs. * @param coverageResult - The coverage result containing undocumented items and coverage statistics - * @param options - Configuration options specifying which audits to include + * @param options - Configuration options specifying which audits to include and exclude * @returns Audit outputs with coverage scores and details about undocumented items */ export function trasformCoverageReportToAudits( coverageResult: CoverageResult, - options: Pick, + options: Pick, ): AuditOutputs { return Object.entries(coverageResult) - .filter( - ([type]) => - !options.onlyAudits?.length || - options.onlyAudits.includes(`${type}-coverage`), - ) + .filter(([type]) => { + const auditSlug = `${type}-coverage`; + if (options.onlyAudits?.length) { + return options.onlyAudits.includes(auditSlug); + } + if (options.skipAudits?.length) { + return !options.skipAudits.includes(auditSlug); + } + return true; + }) .map(([type, items]) => { const coverageType = type as CoverageType; const coverage = items.coverage; diff --git a/packages/plugin-doc-coverage/src/lib/runner/runner.unit.test.ts b/packages/plugin-doc-coverage/src/lib/runner/runner.unit.test.ts new file mode 100644 index 000000000..daa1321ac --- /dev/null +++ b/packages/plugin-doc-coverage/src/lib/runner/runner.unit.test.ts @@ -0,0 +1,88 @@ +import type { CoverageResult } from './models'; +import { trasformCoverageReportToAudits } from './runner'; + +describe('trasformCoverageReportToAudits', () => { + const mockCoverageResult = { + functions: { + coverage: 75, + nodesCount: 4, + issues: [ + { + file: 'test.ts', + line: 10, + name: 'testFunction', + type: 'functions', + }, + ], + }, + classes: { + coverage: 100, + nodesCount: 2, + issues: [], + }, + } as unknown as CoverageResult; + + it('should transform coverage report to audit outputs with no filters', () => { + const result = trasformCoverageReportToAudits(mockCoverageResult, {}); + expect(result).toMatchSnapshot(); + }); + + it('should filter audits when onlyAudits is provided', () => { + const result = trasformCoverageReportToAudits(mockCoverageResult, { + onlyAudits: ['functions-coverage'], + }); + expect(result).toMatchSnapshot(); + }); + + it('should filter audits when skipAudits is provided', () => { + const result = trasformCoverageReportToAudits(mockCoverageResult, { + skipAudits: ['classes-coverage'], + }); + expect(result).toMatchSnapshot(); + }); + + it('should handle empty coverage result', () => { + const result = trasformCoverageReportToAudits( + {} as unknown as CoverageResult, + {}, + ); + expect(result).toMatchSnapshot(); + }); + + it('should handle coverage result with multiple issues', () => { + const coverageWithMultipleIssues = { + functions: { + coverage: 50, + nodesCount: 4, + issues: [ + { + file: 'test1.ts', + line: 10, + name: 'function1', + type: 'functions', + }, + { + file: 'test2.ts', + line: 20, + name: 'function2', + type: 'functions', + }, + ], + }, + } as unknown as CoverageResult; + + const result = trasformCoverageReportToAudits( + coverageWithMultipleIssues, + {}, + ); + expect(result).toMatchSnapshot(); + }); + + it('should prioritize onlyAudits over skipAudits when both are provided', () => { + const result = trasformCoverageReportToAudits(mockCoverageResult, { + onlyAudits: ['functions-coverage'], + skipAudits: ['functions-coverage'], + }); + expect(result).toMatchSnapshot(); + }); +}); diff --git a/packages/plugin-doc-coverage/src/lib/runner/utils.ts b/packages/plugin-doc-coverage/src/lib/runner/utils.ts index e9628eac6..1cdede6dd 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/utils.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/utils.ts @@ -5,6 +5,10 @@ import type { UnprocessedCoverageResult, } from './models'; +/** + * Creates an empty unprocessed coverage report. + * @returns The empty unprocessed coverage report. + */ export function createEmptyUnprocessedCoverageReport(): UnprocessedCoverageResult { return { enums: { nodesCount: 0, issues: [] }, @@ -18,6 +22,11 @@ export function createEmptyUnprocessedCoverageReport(): UnprocessedCoverageResul }; } +/** + * Calculates the coverage percentage for each coverage type. + * @param result - The unprocessed coverage result. + * @returns The processed coverage result. + */ export function calculateCoverage(result: UnprocessedCoverageResult) { return Object.fromEntries( Object.entries(result).map(([key, value]) => { @@ -41,6 +50,11 @@ export function calculateCoverage(result: UnprocessedCoverageResult) { ) as CoverageResult; } +/** + * Maps the SyntaxKind from the library ts-morph to the coverage type. + * @param kind - The SyntaxKind from the library ts-morph. + * @returns The coverage type. + */ export function getCoverageTypeFromKind(kind: SyntaxKind): CoverageType { switch (kind) { case SyntaxKind.ClassDeclaration: diff --git a/packages/plugin-doc-coverage/src/lib/utils.ts b/packages/plugin-doc-coverage/src/lib/utils.ts index b3e7584cb..45b664693 100644 --- a/packages/plugin-doc-coverage/src/lib/utils.ts +++ b/packages/plugin-doc-coverage/src/lib/utils.ts @@ -10,29 +10,35 @@ import { AUDITS_MAP } from './constants'; * @returns The audits. */ export function filterAuditsByPluginConfig( - config: Pick, + config: Pick, ): Audit[] { - const { onlyAudits } = config; + const { onlyAudits, skipAudits } = config; - if (!onlyAudits || onlyAudits.length === 0) { - return Object.values(AUDITS_MAP); + if (onlyAudits && onlyAudits.length > 0) { + return Object.values(AUDITS_MAP).filter(audit => + onlyAudits.includes(audit.slug), + ); } - return Object.values(AUDITS_MAP).filter(audit => - onlyAudits.includes(audit.slug), - ); + if (skipAudits && skipAudits.length > 0) { + return Object.values(AUDITS_MAP).filter( + audit => !skipAudits.includes(audit.slug), + ); + } + + return Object.values(AUDITS_MAP); } /** * Filter groups by the audits that are specified in the configuration. * The groups refs are filtered to only include the audits that are specified in the configuration. * @param groups - The groups to filter. - * @param options - The configuration object. + * @param options - The configuration object containing either onlyAudits or skipAudits. * @returns The filtered groups. */ export function filterGroupsByOnlyAudits( groups: Group[], - options: Pick, + options: Pick, ): Group[] { const audits = filterAuditsByPluginConfig(options); return groups diff --git a/packages/plugin-doc-coverage/src/lib/utils.unit.test.ts b/packages/plugin-doc-coverage/src/lib/utils.unit.test.ts index ec5ec4008..a7ea37d59 100644 --- a/packages/plugin-doc-coverage/src/lib/utils.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/utils.unit.test.ts @@ -3,13 +3,16 @@ import { AUDITS_MAP } from './constants'; import { filterAuditsByPluginConfig, filterGroupsByOnlyAudits } from './utils'; describe('filterAuditsByPluginConfig', () => { - it('should return all audits when onlyAudits is not provided', () => { + it('should return all audits when onlyAudits and skipAudits are not provided', () => { const result = filterAuditsByPluginConfig({}); expect(result).toStrictEqual(Object.values(AUDITS_MAP)); }); - it('should return all audits when onlyAudits is empty array', () => { - const result = filterAuditsByPluginConfig({ onlyAudits: [] }); + it('should return all audits when onlyAudits is empty array and skipAudits is also empty array', () => { + const result = filterAuditsByPluginConfig({ + onlyAudits: [], + skipAudits: [], + }); expect(result).toStrictEqual(Object.values(AUDITS_MAP)); }); @@ -23,6 +26,16 @@ describe('filterAuditsByPluginConfig', () => { ), ); }); + + it('should return only specified audits when skipAudits is provided', () => { + const skipAudits = ['functions-coverage', 'classes-coverage']; + const result = filterAuditsByPluginConfig({ skipAudits }); + expect(result).toStrictEqual( + Object.values(AUDITS_MAP).filter( + audit => !skipAudits.includes(audit.slug), + ), + ); + }); }); describe('filterGroupsByOnlyAudits', () => { From f21fbe2866730129bb727affed0a045296263b59 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Fri, 20 Dec 2024 19:45:03 +0100 Subject: [PATCH 08/39] chore(plugin-doc-coverage): fix linter problems --- ...ck.spec.ts => component-mock.unit.test.ts} | 2 +- packages/plugin-doc-coverage/package.json | 2 - packages/plugin-doc-coverage/src/bin.ts | 3 - .../plugin-doc-coverage/src/lib/constants.ts | 25 +++---- .../plugin-doc-coverage/src/lib/models.ts | 2 +- .../runner/doc-processer.integration.test.ts | 2 +- .../src/lib/runner/doc-processer.ts | 69 +++++++++++-------- .../src/lib/runner/doc-processer.unit.test.ts | 8 +-- .../src/lib/runner/runner.ts | 6 +- .../src/lib/runner/runner.unit.test.ts | 4 +- .../src/lib/runner/utils.ts | 2 +- .../src/lib/runner/utils.unit.test.ts | 4 +- packages/plugin-doc-coverage/src/lib/utils.ts | 4 +- .../src/lib/utils.unit.test.ts | 7 +- 14 files changed, 72 insertions(+), 68 deletions(-) rename packages/plugin-doc-coverage/mocks/{component-mock.spec.ts => component-mock.unit.test.ts} (98%) delete mode 100644 packages/plugin-doc-coverage/src/bin.ts diff --git a/packages/plugin-doc-coverage/mocks/component-mock.spec.ts b/packages/plugin-doc-coverage/mocks/component-mock.unit.test.ts similarity index 98% rename from packages/plugin-doc-coverage/mocks/component-mock.spec.ts rename to packages/plugin-doc-coverage/mocks/component-mock.unit.test.ts index 62998c2a9..648080fb0 100644 --- a/packages/plugin-doc-coverage/mocks/component-mock.spec.ts +++ b/packages/plugin-doc-coverage/mocks/component-mock.unit.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { DUMMY_FUNCTION, DUMMY_FUNCTION_2 } from './component-mock'; +import { DUMMY_FUNCTION, DUMMY_FUNCTION_2 } from './component-mock.js'; export function shouldnotBeHere() { return 'Hello World'; diff --git a/packages/plugin-doc-coverage/package.json b/packages/plugin-doc-coverage/package.json index ec8b984a9..c06f92a38 100644 --- a/packages/plugin-doc-coverage/package.json +++ b/packages/plugin-doc-coverage/package.json @@ -35,8 +35,6 @@ "type": "module", "dependencies": { "@code-pushup/models": "0.57.0", - "@code-pushup/utils": "0.57.0", - "ansis": "^3.3.0", "zod": "^3.22.4", "ts-morph": "^24.0.0" }, diff --git a/packages/plugin-doc-coverage/src/bin.ts b/packages/plugin-doc-coverage/src/bin.ts deleted file mode 100644 index bf6572a76..000000000 --- a/packages/plugin-doc-coverage/src/bin.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { executeRunner } from './lib/runner/index.js'; - -await executeRunner(); diff --git a/packages/plugin-doc-coverage/src/lib/constants.ts b/packages/plugin-doc-coverage/src/lib/constants.ts index 458409066..d199b2b55 100644 --- a/packages/plugin-doc-coverage/src/lib/constants.ts +++ b/packages/plugin-doc-coverage/src/lib/constants.ts @@ -1,5 +1,5 @@ import type { Audit, Group } from '@code-pushup/models'; -import type { AuditSlug } from './models'; +import type { AuditSlug } from './models.js'; export const PLUGIN_SLUG = 'doc-coverage'; @@ -51,18 +51,15 @@ export const groups: Group[] = [ slug: 'documentation-coverage', title: 'Documentation coverage', description: 'Documentation coverage', - refs: Object.keys(AUDITS_MAP).map(slug => { - switch (slug as AuditSlug) { - case 'classes-coverage': - case 'functions-coverage': - case 'methods-coverage': - return { slug, weight: 2 }; - case 'interfaces-coverage': - case 'properties-coverage': - case 'types-coverage': - default: - return { slug, weight: 1 }; - } - }), + refs: Object.keys(AUDITS_MAP).map(slug => ({ + slug, + weight: [ + 'classes-coverage', + 'functions-coverage', + 'methods-coverage', + ].includes(slug) + ? 2 + : 1, + })), }, ]; diff --git a/packages/plugin-doc-coverage/src/lib/models.ts b/packages/plugin-doc-coverage/src/lib/models.ts index 4500a13d1..a407d2a74 100644 --- a/packages/plugin-doc-coverage/src/lib/models.ts +++ b/packages/plugin-doc-coverage/src/lib/models.ts @@ -1,3 +1,3 @@ -import type { CoverageType } from './runner/models'; +import type { CoverageType } from './runner/models.js'; export type AuditSlug = `${CoverageType}-coverage`; diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts index 51e607eb9..00bdc83a7 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts @@ -1,4 +1,4 @@ -import { processDocCoverage } from './doc-processer'; +import { processDocCoverage } from './doc-processer.js'; describe('processDocCoverage', () => { it('should succesfully get the right number of ts files', () => { diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts index fbf333bfc..d510ae15f 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts @@ -29,14 +29,14 @@ export function processDocCoverage( * @param sourceFiles - The source files to process * @returns {UnprocessedCoverageResult} The unprocessed coverage report */ -export function getUnprocessedCoverageReport(sourceFiles: SourceFile[]) { +export function getUnprocessedCoverageReport( + sourceFiles: SourceFile[], +): CoverageResult { const unprocessedCoverageReport = sourceFiles.reduce( (coverageReportOfAllFiles, sourceFile) => { - // Info of the file const filePath = sourceFile.getFilePath(); const classes = sourceFile.getClasses(); - // All nodes of the file const allNodesFromFile = [ ...sourceFile.getFunctions(), ...classes, @@ -44,22 +44,33 @@ export function getUnprocessedCoverageReport(sourceFiles: SourceFile[]) { ...sourceFile.getTypeAliases(), ...sourceFile.getEnums(), ...sourceFile.getInterfaces(), - // ...sourceFile.getVariableStatements().flatMap(statement => statement.getDeclarations()) ]; const coverageReportOfCurrentFile = allNodesFromFile.reduce( (acc, node) => { const nodeType = getCoverageTypeFromKind(node.getKind()); - acc[nodeType].nodesCount++; - if (node.getJsDocs().length === 0) { - acc[nodeType].issues.push({ - file: filePath, - type: nodeType, - name: node.getName() || '', - line: node.getStartLineNumber(), - }); - } - return acc; + const currentTypeReport = acc[nodeType]; + + const updatedIssues = + node.getJsDocs().length === 0 + ? [ + ...currentTypeReport.issues, + { + file: filePath, + type: nodeType, + name: node.getName() || '', + line: node.getStartLineNumber(), + }, + ] + : currentTypeReport.issues; + + return { + ...acc, + [nodeType]: { + nodesCount: currentTypeReport.nodesCount + 1, + issues: updatedIssues, + }, + }; }, createEmptyUnprocessedCoverageReport(), ); @@ -84,22 +95,20 @@ export function getUnprocessedCoverageReport(sourceFiles: SourceFile[]) { export function mergeCoverageResults( results: UnprocessedCoverageResult, current: Partial, -) { - return { - ...Object.fromEntries( - Object.entries(results).map(([key, value]) => { - const node = value as CoverageResult[CoverageType]; - const type = key as CoverageType; - return [ - type, - { - nodesCount: node.nodesCount + (current[type]?.nodesCount ?? 0), - issues: [...node.issues, ...(current[type]?.issues ?? [])], - }, - ]; - }), - ), - } as UnprocessedCoverageResult; +): UnprocessedCoverageResult { + return Object.fromEntries( + Object.entries(results).map(([key, value]) => { + const node = value as CoverageResult[CoverageType]; + const type = key as CoverageType; + return [ + type, + { + nodesCount: node.nodesCount + (current[type]?.nodesCount ?? 0), + issues: [...node.issues, ...(current[type]?.issues ?? [])], + }, + ]; + }), + ) as UnprocessedCoverageResult; } /** diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts index 21c829349..233723f03 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts @@ -1,11 +1,11 @@ import type { ClassDeclaration } from 'ts-morph'; -import { nodeMock, sourceFileMock } from './../../../mocks/source-files.mock'; +import { nodeMock, sourceFileMock } from '../../../mocks/source-files.mock'; import { getClassNodes, getUnprocessedCoverageReport, mergeCoverageResults, -} from './doc-processer'; -import type { UnprocessedCoverageResult } from './models'; +} from './doc-processer.js'; +import type { UnprocessedCoverageResult } from './models.js'; describe('getUnprocessedCoverageReport', () => { it('should produce a full report', () => { @@ -43,7 +43,7 @@ describe('getUnprocessedCoverageReport', () => { sourceFileMock('test.ts', { functions: { 1: true, 2: false, 3: false } }), ]); - expect(results.functions.issues.length).toBe(2); + expect(results.functions.issues).toHaveLength(2); }); it('should collect valid issues', () => { diff --git a/packages/plugin-doc-coverage/src/lib/runner/runner.ts b/packages/plugin-doc-coverage/src/lib/runner/runner.ts index bb037e933..8bbf54066 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/runner.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/runner.ts @@ -1,7 +1,7 @@ import type { AuditOutputs, RunnerFunction } from '@code-pushup/models'; -import type { DocCoveragePluginConfig } from '../config'; -import { processDocCoverage } from './doc-processer'; -import type { CoverageResult, CoverageType } from './models'; +import type { DocCoveragePluginConfig } from '../config.js'; +import { processDocCoverage } from './doc-processer.js'; +import type { CoverageResult, CoverageType } from './models.js'; export function createRunnerFunction( config: DocCoveragePluginConfig, diff --git a/packages/plugin-doc-coverage/src/lib/runner/runner.unit.test.ts b/packages/plugin-doc-coverage/src/lib/runner/runner.unit.test.ts index daa1321ac..d81748ab3 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/runner.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/runner.unit.test.ts @@ -1,5 +1,5 @@ -import type { CoverageResult } from './models'; -import { trasformCoverageReportToAudits } from './runner'; +import type { CoverageResult } from './models.js'; +import { trasformCoverageReportToAudits } from './runner.js'; describe('trasformCoverageReportToAudits', () => { const mockCoverageResult = { diff --git a/packages/plugin-doc-coverage/src/lib/runner/utils.ts b/packages/plugin-doc-coverage/src/lib/runner/utils.ts index 1cdede6dd..31c312619 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/utils.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/utils.ts @@ -3,7 +3,7 @@ import type { CoverageResult, CoverageType, UnprocessedCoverageResult, -} from './models'; +} from './models.js'; /** * Creates an empty unprocessed coverage report. diff --git a/packages/plugin-doc-coverage/src/lib/runner/utils.unit.test.ts b/packages/plugin-doc-coverage/src/lib/runner/utils.unit.test.ts index 403c7b3ad..731d5280c 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/utils.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/utils.unit.test.ts @@ -1,10 +1,10 @@ import { SyntaxKind } from 'ts-morph'; -import type { UnprocessedCoverageResult } from './models'; +import type { UnprocessedCoverageResult } from './models.js'; import { calculateCoverage, createEmptyUnprocessedCoverageReport, getCoverageTypeFromKind, -} from './utils'; +} from './utils.js'; describe('createEmptyUnprocessedCoverageReport', () => { it('should create an empty report with all categories initialized', () => { diff --git a/packages/plugin-doc-coverage/src/lib/utils.ts b/packages/plugin-doc-coverage/src/lib/utils.ts index 45b664693..73e04c0af 100644 --- a/packages/plugin-doc-coverage/src/lib/utils.ts +++ b/packages/plugin-doc-coverage/src/lib/utils.ts @@ -1,6 +1,6 @@ import type { Audit, Group } from '@code-pushup/models'; -import type { DocCoveragePluginConfig } from './config'; -import { AUDITS_MAP } from './constants'; +import type { DocCoveragePluginConfig } from './config.js'; +import { AUDITS_MAP } from './constants.js'; /** * Get audits based on the configuration. diff --git a/packages/plugin-doc-coverage/src/lib/utils.unit.test.ts b/packages/plugin-doc-coverage/src/lib/utils.unit.test.ts index a7ea37d59..9252b5fd5 100644 --- a/packages/plugin-doc-coverage/src/lib/utils.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/utils.unit.test.ts @@ -1,6 +1,9 @@ import type { Group } from '@code-pushup/models'; -import { AUDITS_MAP } from './constants'; -import { filterAuditsByPluginConfig, filterGroupsByOnlyAudits } from './utils'; +import { AUDITS_MAP } from './constants.js'; +import { + filterAuditsByPluginConfig, + filterGroupsByOnlyAudits, +} from './utils.js'; describe('filterAuditsByPluginConfig', () => { it('should return all audits when onlyAudits and skipAudits are not provided', () => { From 4fb478ef46f6242a1c903a2b69edbe0db04c3c63 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Fri, 20 Dec 2024 19:46:40 +0100 Subject: [PATCH 09/39] fix: return code-pushup config to original state --- code-pushup.config.ts | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/code-pushup.config.ts b/code-pushup.config.ts index 0b2d52680..a0a33cfee 100644 --- a/code-pushup.config.ts +++ b/code-pushup.config.ts @@ -1,6 +1,12 @@ import 'dotenv/config'; import { z } from 'zod'; -import { docCoverageCoreConfig } from './code-pushup.preset.js'; +import { + coverageCoreConfigNx, + docCoverageCoreConfig, + eslintCoreConfigNx, + jsPackagesCoreConfig, + lighthouseCoreConfig, +} from './code-pushup.preset.js'; import type { CoreConfig } from './packages/models/src/index.js'; import { mergeConfigs } from './packages/utils/src/index.js'; @@ -27,13 +33,13 @@ const config: CoreConfig = { }; export default mergeConfigs( - // config, - // await coverageCoreConfigNx(), - // await jsPackagesCoreConfig(), - // await lighthouseCoreConfig( - // 'https://github.com/code-pushup/cli?tab=readme-ov-file#code-pushup-cli/', - // ), - // await eslintCoreConfigNx(), + config, + await coverageCoreConfigNx(), + await jsPackagesCoreConfig(), + await lighthouseCoreConfig( + 'https://github.com/code-pushup/cli?tab=readme-ov-file#code-pushup-cli/', + ), + await eslintCoreConfigNx(), await docCoverageCoreConfig({ sourceGlob: [ 'packages/**/src/**/*.ts', @@ -43,14 +49,5 @@ export default mergeConfigs( '!**/internal/**', ], skipAudits: ['methods-coverage'], - // onlyAudits: [ - // 'methods-coverage', - // 'functions-coverage', - // 'types-coverage', - // 'classes-coverage', - // 'interfaces-coverage', - // 'enums-coverage', - // 'type-aliases-coverage', - // ], }), ); From feefa0ad771f97448be79a35334e379907fdc21b Mon Sep 17 00:00:00 2001 From: Alejandro Date: Sat, 21 Dec 2024 12:44:27 +0100 Subject: [PATCH 10/39] feat(plugin-doc-coverage): add support for variable statement to be documented, improve test --- code-pushup.config.bundled_oq2x2csd2at.mjs | 1210 ----------------- .../mocks/component-mock.ts | 38 - .../fixtures/angular/map-event.function.ts | 2 + .../mocks/source-files.mock.ts | 72 +- .../doc-processer.integration.test.ts.snap | 38 +- .../src/lib/runner/doc-processer.ts | 32 +- .../src/lib/runner/doc-processer.unit.test.ts | 79 +- .../src/lib/runner/utils.ts | 1 + 8 files changed, 158 insertions(+), 1314 deletions(-) delete mode 100644 code-pushup.config.bundled_oq2x2csd2at.mjs diff --git a/code-pushup.config.bundled_oq2x2csd2at.mjs b/code-pushup.config.bundled_oq2x2csd2at.mjs deleted file mode 100644 index 4c033e42d..000000000 --- a/code-pushup.config.bundled_oq2x2csd2at.mjs +++ /dev/null @@ -1,1210 +0,0 @@ -// code-pushup.config.ts -// packages/utils/src/lib/logging.ts -import isaacs_cliui from '@isaacs/cliui'; -import { cliui } from '@poppinss/cliui'; -// packages/plugin-coverage/src/lib/runner/index.ts -import { bold } from 'ansis'; -// packages/plugin-coverage/src/lib/nx/coverage-paths.ts -import { bold as bold2 } from 'ansis'; -// packages/plugin-lighthouse/src/lib/normalize-flags.ts -import { bold as bold3, yellow } from 'ansis'; -// packages/plugin-lighthouse/src/lib/runner/utils.ts -import { bold as bold7 } from 'ansis'; -// packages/plugin-lighthouse/src/lib/runner/details/details.ts -import { bold as bold6, yellow as yellow2 } from 'ansis'; -// packages/plugin-lighthouse/src/lib/runner/details/item-value.ts -import { bold as bold4 } from 'ansis'; -// packages/plugin-lighthouse/src/lib/runner/details/utils.ts -import { bold as bold5 } from 'ansis'; -// packages/utils/src/lib/reports/utils.ts -import ansis from 'ansis'; -// packages/utils/src/lib/file-system.ts -import { bold as bold8, gray } from 'ansis'; -import { underline } from 'ansis'; -// packages/utils/src/lib/progress.ts -import { black, bold as bold9, gray as gray2, green } from 'ansis'; -// packages/utils/src/lib/reports/log-stdout-summary.ts -import { bold as bold11, cyan, cyanBright, green as green2, red } from 'ansis'; -// packages/utils/src/lib/zod-validation.ts -import { bold as bold12, red as red2 } from 'ansis'; -// packages/plugin-js-packages/src/lib/runner/audit/transform.ts -import { md } from 'build-md'; -// packages/plugin-js-packages/src/lib/runner/outdated/transform.ts -import { md as md2 } from 'build-md'; -import { md as md3 } from 'build-md'; -// packages/utils/src/lib/reports/generate-md-report.ts -import { MarkdownDocument as MarkdownDocument3, md as md6 } from 'build-md'; -// packages/utils/src/lib/reports/formatting.ts -import { MarkdownDocument, md as md4 } from 'build-md'; -// packages/utils/src/lib/reports/generate-md-report-categoy-section.ts -import { MarkdownDocument as MarkdownDocument2, md as md5 } from 'build-md'; -// packages/utils/src/lib/reports/generate-md-reports-diff.ts -import { MarkdownDocument as MarkdownDocument5, md as md8 } from 'build-md'; -// packages/utils/src/lib/reports/generate-md-reports-diff-utils.ts -import { MarkdownDocument as MarkdownDocument4, md as md7 } from 'build-md'; -import { bundleRequire } from 'bundle-require'; -// packages/plugin-lighthouse/src/lib/constants.ts -import { DEFAULT_FLAGS } from 'chrome-launcher/dist/flags.js'; -import 'dotenv/config'; -// packages/plugin-eslint/src/lib/setup.ts -import { ESLint } from 'eslint'; -// packages/plugin-eslint/src/lib/meta/versions/detect.ts -import { ESLint as ESLint2 } from 'eslint'; -// packages/plugin-eslint/src/lib/meta/versions/flat.ts -import { builtinRules } from 'eslint/use-at-your-own-risk'; -// packages/plugin-lighthouse/src/lib/runner/constants.ts -import { defaultConfig } from 'lighthouse'; -import log from 'lighthouse-logger'; -// packages/plugin-lighthouse/src/lib/runner/runner.ts -import { runLighthouse } from 'lighthouse/cli/run.js'; -import desktopConfig from 'lighthouse/core/config/desktop-config.js'; -import experimentalConfig from 'lighthouse/core/config/experimental-config.js'; -import perfConfig from 'lighthouse/core/config/perf-config.js'; -import { MultiProgressBars } from 'multi-progress-bars'; -// packages/utils/src/lib/execute-process.ts -import { spawn } from 'node:child_process'; -// packages/plugin-eslint/src/lib/meta/hash.ts -import { createHash } from 'node:crypto'; -import { writeFile } from 'node:fs/promises'; -// packages/plugin-eslint/src/lib/runner/index.ts -import { writeFile as writeFile2 } from 'node:fs/promises'; -// packages/plugin-js-packages/src/lib/runner/index.ts -import { writeFile as writeFile3 } from 'node:fs/promises'; -// packages/plugin-js-packages/src/lib/package-managers/derive-package-manager.ts -import { readFile } from 'node:fs/promises'; -import { - mkdir, - readFile as readFile2, - readdir, - rm, - stat, -} from 'node:fs/promises'; -// packages/plugin-coverage/src/lib/coverage-plugin.ts -import { createRequire } from 'node:module'; -// packages/plugin-eslint/src/lib/eslint-plugin.ts -import { createRequire as createRequire2 } from 'node:module'; -// packages/plugin-js-packages/src/lib/js-packages-plugin.ts -import { createRequire as createRequire3 } from 'node:module'; -// packages/plugin-lighthouse/src/lib/lighthouse-plugin.ts -import { createRequire as createRequire4 } from 'node:module'; -// packages/plugin-eslint/src/lib/runner/lint.ts -import { platform } from 'node:os'; -// packages/utils/src/lib/transform.ts -import { platform as platform2 } from 'node:os'; -import path4 from 'node:path'; -import path3 from 'node:path'; -// packages/plugin-coverage/src/lib/runner/constants.ts -import path from 'node:path'; -// packages/plugin-coverage/src/lib/runner/lcov/lcov-runner.ts -import path2 from 'node:path'; -import path5 from 'node:path'; -import path8 from 'node:path'; -import path6 from 'node:path'; -import path7 from 'node:path'; -// packages/plugin-eslint/src/lib/nx/utils.ts -import path9 from 'node:path'; -import path14 from 'node:path'; -// packages/plugin-js-packages/src/lib/runner/utils.ts -import path10 from 'node:path'; -import path12 from 'node:path'; -// packages/plugin-js-packages/src/lib/runner/constants.ts -import path11 from 'node:path'; -import path13 from 'node:path'; -import path15 from 'node:path'; -import path16 from 'node:path'; -import path17 from 'node:path'; -import path18 from 'node:path'; -// packages/utils/src/lib/git/git.ts -import path19 from 'node:path'; -import path20 from 'node:path'; -// packages/utils/src/lib/reports/load-report.ts -import path21 from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { fileURLToPath as fileURLToPath2 } from 'node:url'; -import { pathToFileURL } from 'node:url'; -import { fileURLToPath as fileURLToPath3 } from 'node:url'; -// packages/plugin-coverage/src/lib/runner/lcov/parse-lcov.ts -import parseLcovExport from 'parse-lcov'; -import { clean, diff, neq } from 'semver'; -// packages/utils/src/lib/semver.ts -import { rcompare, valid } from 'semver'; -// packages/utils/src/lib/git/git.commits-and-tags.ts -import { simpleGit } from 'simple-git'; -import { simpleGit as simpleGit2 } from 'simple-git'; -// packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts -import { Project } from 'ts-morph'; -// packages/plugin-doc-coverage/src/lib/utils.ts -import { SyntaxKind } from 'typescript'; -import { z as z5 } from 'zod'; -// packages/plugin-coverage/src/lib/config.ts -import { z } from 'zod'; -// packages/plugin-doc-coverage/src/lib/config.ts -import { z as z2 } from 'zod'; -// packages/plugin-eslint/src/lib/config.ts -import { z as z3 } from 'zod'; -// packages/plugin-js-packages/src/lib/config.ts -import { z as z4 } from 'zod'; -import { issueSeveritySchema } from '@code-pushup/models'; -import { DEFAULT_PERSIST_OUTPUT_DIR } from '@code-pushup/models'; -// packages/plugin-lighthouse/src/lib/runner/details/opportunity.type.ts -import { tableSchema as tableSchema2 } from '@code-pushup/models'; -// packages/plugin-lighthouse/src/lib/runner/details/table.type.ts -import { tableSchema } from '@code-pushup/models'; -// packages/utils/src/index.ts -import { exists as exists4 } from '@code-pushup/models'; -// packages/utils/src/lib/formatting.ts -import { - MAX_DESCRIPTION_LENGTH, - MAX_ISSUE_MESSAGE_LENGTH, - MAX_TITLE_LENGTH, -} from '@code-pushup/models'; -import { commitSchema } from '@code-pushup/models'; -import { reportSchema } from '@code-pushup/models'; -import { capitalize } from '@code-pushup/utils'; -import { - ProcessError, - ensureDirectoryExists, - executeProcess, - filePathToCliArg, - readJsonFile, - ui as ui2, -} from '@code-pushup/utils'; -import { pluginWorkDir } from '@code-pushup/utils'; -import { exists, readTextFile, toUnixNewlines, ui } from '@code-pushup/utils'; -// packages/plugin-coverage/src/lib/runner/lcov/transform.ts -import { toNumberPrecision, toOrdinal } from '@code-pushup/utils'; -import { importModule, ui as ui3 } from '@code-pushup/utils'; -import { toArray } from '@code-pushup/utils'; -// packages/plugin-eslint/src/lib/meta/groups.ts -import { objectToKeys, slugify as slugify2 } from '@code-pushup/utils'; -import { slugify } from '@code-pushup/utils'; -// packages/plugin-eslint/src/lib/meta/parse.ts -import { toArray as toArray2 } from '@code-pushup/utils'; -import { - exists as exists2, - findNearestFile, - toArray as toArray3, - ui as ui4, -} from '@code-pushup/utils'; -// packages/plugin-eslint/src/lib/meta/versions/legacy.ts -import { - distinct, - exists as exists3, - toArray as toArray4, - ui as ui5, -} from '@code-pushup/utils'; -import { fileExists } from '@code-pushup/utils'; -// packages/plugin-eslint/src/lib/meta/transform.ts -import { truncateDescription, truncateTitle } from '@code-pushup/utils'; -import { - ensureDirectoryExists as ensureDirectoryExists2, - filePathToCliArg as filePathToCliArg3, - pluginWorkDir as pluginWorkDir2, - readJsonFile as readJsonFile2, -} from '@code-pushup/utils'; -import { - distinct as distinct2, - executeProcess as executeProcess2, - filePathToCliArg as filePathToCliArg2, - toArray as toArray5, -} from '@code-pushup/utils'; -// packages/plugin-eslint/src/lib/runner/transform.ts -import { - compareIssueSeverity, - countOccurrences, - objectToEntries, - pluralizeToken, - truncateIssueMessage, - ui as ui6, -} from '@code-pushup/utils'; -import { - fileExists as fileExists2, - toArray as toArray6, -} from '@code-pushup/utils'; -// packages/plugin-js-packages/src/lib/package-managers/npm/npm.ts -import { objectToKeys as objectToKeys3 } from '@code-pushup/utils'; -import { - crawlFileSystem, - objectFromEntries, - objectToKeys as objectToKeys2, - readJsonFile as readJsonFile3, -} from '@code-pushup/utils'; -// packages/plugin-js-packages/src/lib/package-managers/npm/audit-result.ts -import { objectToEntries as objectToEntries2 } from '@code-pushup/utils'; -// packages/plugin-js-packages/src/lib/package-managers/npm/outdated-result.ts -import { objectToEntries as objectToEntries3 } from '@code-pushup/utils'; -// packages/plugin-js-packages/src/lib/package-managers/pnpm/pnpm.ts -import { objectToKeys as objectToKeys4 } from '@code-pushup/utils'; -// packages/plugin-js-packages/src/lib/package-managers/pnpm/outdated-result.ts -import { objectToEntries as objectToEntries4 } from '@code-pushup/utils'; -// packages/plugin-js-packages/src/lib/package-managers/yarn-classic/audit-result.ts -import { fromJsonLines } from '@code-pushup/utils'; -// packages/plugin-js-packages/src/lib/package-managers/yarn-classic/outdated-result.ts -import { - fromJsonLines as fromJsonLines2, - objectFromEntries as objectFromEntries2, - objectToEntries as objectToEntries5, - objectToKeys as objectToKeys5, -} from '@code-pushup/utils'; -import { - ensureDirectoryExists as ensureDirectoryExists3, - executeProcess as executeProcess3, - filePathToCliArg as filePathToCliArg4, - isPromiseFulfilledResult, - isPromiseRejectedResult, - objectFromEntries as objectFromEntries4, - readJsonFile as readJsonFile4, -} from '@code-pushup/utils'; -import { objectToEntries as objectToEntries6 } from '@code-pushup/utils'; -import { pluginWorkDir as pluginWorkDir3 } from '@code-pushup/utils'; -import { - objectFromEntries as objectFromEntries3, - pluralize, -} from '@code-pushup/utils'; -// packages/plugin-js-packages/src/lib/runner/outdated/constants.ts -import { objectToKeys as objectToKeys6 } from '@code-pushup/utils'; -import { fileExists as fileExists3 } from '@code-pushup/utils'; -// packages/plugin-js-packages/src/lib/package-managers/derive-yarn.ts -import { executeProcess as executeProcess4 } from '@code-pushup/utils'; -import { ui as ui7 } from '@code-pushup/utils'; -import { ensureDirectoryExists as ensureDirectoryExists4 } from '@code-pushup/utils'; -import { - formatReportScore, - importModule as importModule2, - readJsonFile as readJsonFile5, - ui as ui10, -} from '@code-pushup/utils'; -import { ui as ui9 } from '@code-pushup/utils'; -import { - formatBytes as formatBytes2, - formatDuration as formatDuration2, - html as html2, -} from '@code-pushup/utils'; -import { - formatBytes, - formatDuration, - html, - truncateText, - ui as ui8, -} from '@code-pushup/utils'; -// packages/plugin-lighthouse/src/lib/utils.ts -import { filterItemRefsBy, toArray as toArray7 } from '@code-pushup/utils'; - -var coverageTypeSchema = z.enum(['function', 'branch', 'line']); -var coverageResultSchema = z.union([ - z.object({ - resultsPath: z - .string({ - description: 'Path to coverage results for Nx setup.', - }) - .includes('lcov'), - pathToProject: z - .string({ - description: - 'Path from workspace root to project root. Necessary for LCOV reports which provide a relative path.', - }) - .optional(), - }), - z - .string({ - description: 'Path to coverage results for a single project setup.', - }) - .includes('lcov'), -]); -var coveragePluginConfigSchema = z.object({ - coverageToolCommand: z - .object({ - command: z - .string({ description: 'Command to run coverage tool.' }) - .min(1), - args: z - .array(z.string(), { - description: 'Arguments to be passed to the coverage tool.', - }) - .optional(), - }) - .optional(), - coverageTypes: z - .array(coverageTypeSchema, { - description: 'Coverage types measured. Defaults to all available types.', - }) - .min(1) - .default(['function', 'branch', 'line']), - reports: z - .array(coverageResultSchema, { - description: - 'Path to all code coverage report files. Only LCOV format is supported for now.', - }) - .min(1), - perfectScoreThreshold: z - .number({ - description: - 'Score will be 1 (perfect) for this coverage and above. Score range is 0 - 1.', - }) - .gt(0) - .max(1) - .optional(), -}); - -var WORKDIR = pluginWorkDir('coverage'); -var RUNNER_OUTPUT_PATH = path.join(WORKDIR, 'runner-output.json'); -var PLUGIN_CONFIG_PATH = path.join( - process.cwd(), - WORKDIR, - 'plugin-config.json', -); - -var godKnows = parseLcovExport; -var parseLcov = 'default' in godKnows ? godKnows.default : godKnows; - -var docCoveragePluginConfigSchema = z2.object({ - onlyAudits: z2.array(z2.string()).optional(), - sourceGlob: z2 - .array(z2.string()) - .default(['src/**/*.{ts,tsx}', '!**/*.spec.ts', '!**/*.test.ts']), -}); - -// packages/plugin-doc-coverage/src/lib/constants.ts -var PLUGIN_SLUG = 'doc-coverage'; -var AUDITS_MAP = { - 'classes-coverage': { - slug: 'classes-coverage', - title: 'Classes coverage', - description: 'Coverage of classes', - }, - 'methods-coverage': { - slug: 'methods-coverage', - title: 'Methods coverage', - description: 'Coverage of methods', - }, - 'functions-coverage': { - slug: 'functions-coverage', - title: 'Functions coverage', - description: 'Coverage of functions', - }, - 'interfaces-coverage': { - slug: 'interfaces-coverage', - title: 'Interfaces coverage', - description: 'Coverage of interfaces', - }, - 'variables-coverage': { - slug: 'variables-coverage', - title: 'Variables coverage', - description: 'Coverage of variables', - }, - 'properties-coverage': { - slug: 'properties-coverage', - title: 'Properties coverage', - description: 'Coverage of properties', - }, - 'types-coverage': { - slug: 'types-coverage', - title: 'Types coverage', - description: 'Coverage of types', - }, - 'enums-coverage': { - slug: 'enums-coverage', - title: 'Enums coverage', - description: 'Coverage of enums', - }, -}; -var groups = [ - { - slug: 'documentation-coverage', - title: 'Documentation coverage', - description: 'Documentation coverage', - refs: Object.keys(AUDITS_MAP).map(slug => { - switch (slug) { - case 'classes-coverage': - case 'functions-coverage': - case 'methods-coverage': - return { slug, weight: 2 }; - case 'interfaces-coverage': - case 'properties-coverage': - case 'types-coverage': - default: - return { slug, weight: 1 }; - } - }), - }, -]; - -function filterAuditsByPluginConfig(config2) { - const { onlyAudits } = config2; - if (!onlyAudits || onlyAudits.length === 0) { - return Object.values(AUDITS_MAP); - } - return Object.values(AUDITS_MAP).filter(audit => - onlyAudits.includes(audit.slug), - ); -} -function filterGroupsByOnlyAudits(groups2, options) { - const audits2 = filterAuditsByPluginConfig(options); - return groups2 - .map(group => ({ - ...group, - refs: group.refs.filter(ref => - audits2.some(audit => audit.slug === ref.slug), - ), - })) - .filter(group => group.refs.length > 0); -} -function trasformCoverageReportToAudits(coverageResult, options) { - return Object.entries(coverageResult) - .filter( - ([type]) => - !options.onlyAudits?.length || - options.onlyAudits.includes(`${type}-coverage`), - ) - .map(([type, items]) => { - const coverageType = type; - const coverage = items.coverage; - return { - slug: `${coverageType}-coverage`, - value: coverage, - score: coverage / 100, - displayValue: `${coverage} %`, - details: { - issues: items.issues.map(({ file, line }) => ({ - message: 'Missing documentation', - source: { file, position: { startLine: line } }, - severity: 'warning', - })), - }, - }; - }); -} -function getCoverageTypeFromKind(kind) { - switch (kind) { - case SyntaxKind.ClassDeclaration: - return 'classes'; - case SyntaxKind.MethodDeclaration: - return 'methods'; - case SyntaxKind.FunctionDeclaration: - return 'functions'; - case SyntaxKind.InterfaceDeclaration: - return 'interfaces'; - case SyntaxKind.EnumDeclaration: - return 'enums'; - case SyntaxKind.VariableDeclaration: - return 'variables'; - case SyntaxKind.PropertyDeclaration: - return 'properties'; - case SyntaxKind.TypeAliasDeclaration: - return 'types'; - default: - throw new Error(`Unsupported syntax kind: ${kind}`); - } -} - -// packages/plugin-doc-coverage/src/lib/runner/utils.ts -function createEmptyUnprocessedCoverageReport() { - return { - enums: { nodesCount: 0, issues: [] }, - interfaces: { nodesCount: 0, issues: [] }, - types: { nodesCount: 0, issues: [] }, - functions: { nodesCount: 0, issues: [] }, - variables: { nodesCount: 0, issues: [] }, - classes: { nodesCount: 0, issues: [] }, - methods: { nodesCount: 0, issues: [] }, - properties: { nodesCount: 0, issues: [] }, - }; -} -function calculateCoverage2(result) { - return Object.fromEntries( - Object.entries(result).map(([key, value]) => { - const type = key; - return [ - type, - { - coverage: - value.nodesCount === 0 - ? 100 - : (1 - value.issues.length / value.nodesCount) * 100, - issues: value.issues, - nodesCount: value.nodesCount, - }, - ]; - }), - ); -} - -// packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts -function processDocCoverage(config2) { - const project = new Project(); - project.addSourceFilesAtPaths(config2.sourceGlob); - return getUnprocessedCoverageReport(project.getSourceFiles()); -} -function getUnprocessedCoverageReport(sourceFiles) { - const unprocessedCoverageReport = sourceFiles.reduce( - (coverageReportOfAllFiles, sourceFile) => { - const filePath = sourceFile.getFilePath(); - const classes = sourceFile.getClasses(); - const allNodesFromFile = [ - ...sourceFile.getFunctions(), - ...classes, - ...getClassNodes(classes), - ...sourceFile.getTypeAliases(), - ...sourceFile.getEnums(), - ...sourceFile.getInterfaces(), - // ...sourceFile.getVariableStatements().flatMap(statement => statement.getDeclarations()) - ]; - const coverageReportOfCurrentFile = allNodesFromFile.reduce( - (acc, node) => { - const nodeType = getCoverageTypeFromKind(node.getKind()); - acc[nodeType].nodesCount++; - if (node.getJsDocs().length === 0) { - acc[nodeType].issues.push( - getUndocumentedNode( - filePath, - nodeType, - node.getName() || '', - node.getStartLineNumber(), - ), - ); - } - return acc; - }, - createEmptyUnprocessedCoverageReport(), - ); - return mergeCoverageResults( - coverageReportOfAllFiles, - coverageReportOfCurrentFile, - ); - }, - createEmptyUnprocessedCoverageReport(), - ); - return calculateCoverage2(unprocessedCoverageReport); -} -function mergeCoverageResults(results, current) { - return { - ...Object.fromEntries( - Object.entries(results).map(([key, value]) => { - const node = value; - const type = key; - return [ - type, - { - nodesCount: node.nodesCount + current[type].nodesCount, - issues: [...node.issues, ...current[type].issues], - }, - ]; - }), - ), - }; -} -function getClassNodes(classNodes) { - return classNodes.flatMap(classNode => [ - ...classNode.getMethods(), - ...classNode.getProperties(), - ]); -} -function getUndocumentedNode(file, type, name, line) { - return { file, type, name, line }; -} - -// packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts -var PLUGIN_TITLE = 'Documentation coverage'; -var PLUGIN_DESCRIPTION = 'Official Code PushUp documentation coverage plugin.'; -var PLUGIN_DOCS_URL = - 'https://www.npmjs.com/package/@code-pushup/doc-coverage-plugin/'; -async function docCoveragePlugin(config2) { - const docCoverageConfig = docCoveragePluginConfigSchema.parse(config2); - const groupsC = filterGroupsByOnlyAudits(groups, docCoverageConfig); - const auditsC = filterAuditsByPluginConfig(docCoverageConfig); - return { - slug: PLUGIN_SLUG, - title: PLUGIN_TITLE, - icon: 'folder-src', - description: PLUGIN_DESCRIPTION, - docsUrl: PLUGIN_DOCS_URL, - groups: filterGroupsByOnlyAudits(groups, docCoverageConfig), - audits: filterAuditsByPluginConfig(docCoverageConfig), - runner: createRunnerFunction(docCoverageConfig), - }; -} -function createRunnerFunction(config2) { - return () => { - const coverageResult = processDocCoverage(config2); - return trasformCoverageReportToAudits(coverageResult, config2); - }; -} - -// packages/plugin-doc-coverage/src/index.ts -var src_default = docCoveragePlugin; - -var patternsSchema = z3.union([z3.string(), z3.array(z3.string()).min(1)], { - description: - 'Lint target files. May contain file paths, directory paths or glob patterns', -}); -var eslintrcSchema = z3.string({ description: 'Path to ESLint config file' }); -var eslintTargetObjectSchema = z3.object({ - eslintrc: eslintrcSchema.optional(), - patterns: patternsSchema, -}); -var eslintTargetSchema = z3 - .union([patternsSchema, eslintTargetObjectSchema]) - .transform(target => - typeof target === 'string' || Array.isArray(target) - ? { patterns: target } - : target, - ); -var eslintPluginConfigSchema = z3 - .union([eslintTargetSchema, z3.array(eslintTargetSchema).min(1)]) - .transform(toArray); - -// packages/plugin-eslint/src/lib/runner/index.ts -var WORKDIR2 = pluginWorkDir2('eslint'); -var RUNNER_OUTPUT_PATH2 = path7.join(WORKDIR2, 'runner-output.json'); -var PLUGIN_CONFIG_PATH2 = path7.join( - process.cwd(), - WORKDIR2, - 'plugin-config.json', -); - -// packages/plugin-js-packages/src/lib/constants.ts -var defaultAuditLevelMapping = { - critical: 'error', - high: 'error', - moderate: 'warning', - low: 'warning', - info: 'info', -}; - -// packages/plugin-js-packages/src/lib/config.ts -var dependencyGroups = ['prod', 'dev', 'optional']; -var dependencyGroupSchema = z4.enum(dependencyGroups); -var packageCommandSchema = z4.enum(['audit', 'outdated']); -var packageManagerIdSchema = z4.enum([ - 'npm', - 'yarn-classic', - 'yarn-modern', - 'pnpm', -]); -var packageJsonPathSchema = z4 - .union([ - z4.array(z4.string()).min(1), - z4.object({ autoSearch: z4.literal(true) }), - ]) - .describe( - 'File paths to package.json. Looks only at root package.json by default', - ) - .default(['package.json']); -var packageAuditLevels = ['critical', 'high', 'moderate', 'low', 'info']; -var packageAuditLevelSchema = z4.enum(packageAuditLevels); -function fillAuditLevelMapping(mapping) { - return { - critical: mapping.critical ?? defaultAuditLevelMapping.critical, - high: mapping.high ?? defaultAuditLevelMapping.high, - moderate: mapping.moderate ?? defaultAuditLevelMapping.moderate, - low: mapping.low ?? defaultAuditLevelMapping.low, - info: mapping.info ?? defaultAuditLevelMapping.info, - }; -} -var jsPackagesPluginConfigSchema = z4.object({ - checks: z4 - .array(packageCommandSchema, { - description: - 'Package manager commands to be run. Defaults to both audit and outdated.', - }) - .min(1) - .default(['audit', 'outdated']), - packageManager: packageManagerIdSchema - .describe('Package manager to be used.') - .optional(), - dependencyGroups: z4 - .array(dependencyGroupSchema) - .min(1) - .default(['prod', 'dev']), - auditLevelMapping: z4 - .record(packageAuditLevelSchema, issueSeveritySchema, { - description: - 'Mapping of audit levels to issue severity. Custom mapping or overrides may be entered manually, otherwise has a default preset.', - }) - .default(defaultAuditLevelMapping) - .transform(fillAuditLevelMapping), - packageJsonPaths: packageJsonPathSchema, -}); - -function filterAuditResult(result, key, referenceResult) { - if (result.vulnerabilities.length === 0) { - return result; - } - const uniqueResult = result.vulnerabilities.reduce( - (acc, ref) => { - const matchReference = referenceResult ?? acc; - const isMatch = matchReference.vulnerabilities - .map(vulnerability => vulnerability[key]) - .includes(ref[key]); - if (isMatch) { - return { - vulnerabilities: acc.vulnerabilities, - summary: { - ...acc.summary, - [ref.severity]: acc.summary[ref.severity] - 1, - total: acc.summary.total - 1, - }, - }; - } - return { - vulnerabilities: [...acc.vulnerabilities, ref], - summary: acc.summary, - }; - }, - { vulnerabilities: [], summary: result.summary }, - ); - return { - vulnerabilities: uniqueResult.vulnerabilities, - summary: uniqueResult.summary, - }; -} - -// packages/plugin-js-packages/src/lib/package-managers/constants.ts -var COMMON_AUDIT_ARGS = ['audit', '--json']; -var COMMON_OUTDATED_ARGS = ['outdated', '--json']; - -function npmToAuditResult(output) { - const npmAudit = JSON.parse(output); - const vulnerabilities = objectToEntries2(npmAudit.vulnerabilities).map( - ([name, detail]) => { - const advisory = npmToAdvisory(name, npmAudit.vulnerabilities); - return { - name: name.toString(), - severity: detail.severity, - versionRange: detail.range, - directDependency: detail.isDirect ? true : (detail.effects[0] ?? ''), - fixInformation: npmToFixInformation(detail.fixAvailable), - ...(advisory != null && { - title: advisory.title, - url: advisory.url, - }), - }; - }, - ); - return { - vulnerabilities, - summary: npmAudit.metadata.vulnerabilities, - }; -} -function npmToFixInformation(fixAvailable) { - if (typeof fixAvailable === 'boolean') { - return fixAvailable ? 'Fix is available.' : ''; - } - return `Fix available: Update \`${fixAvailable.name}\` to version **${fixAvailable.version}**${fixAvailable.isSemVerMajor ? ' (breaking change).' : '.'}`; -} -function npmToAdvisory( - name, - vulnerabilities, - prevNodes = /* @__PURE__ */ new Set(), -) { - const advisory = vulnerabilities[name]?.via; - if ( - Array.isArray(advisory) && - advisory.length > 0 && - typeof advisory[0] === 'object' - ) { - return { title: advisory[0].title, url: advisory[0].url }; - } - if ( - Array.isArray(advisory) && - advisory.length > 0 && - advisory.every(value => typeof value === 'string') - ) { - let advisoryInfo = null; - let newReferences = []; - let advisoryInfoFound = false; - for (const via of advisory) { - if (!prevNodes.has(via)) { - newReferences.push(via); - } - } - while (newReferences.length > 0 && !advisoryInfoFound) { - const ref = newReferences.pop(); - prevNodes.add(ref); - const result = npmToAdvisory(ref, vulnerabilities, prevNodes); - if (result != null) { - advisoryInfo = { title: result.title, url: result.url }; - advisoryInfoFound = true; - } - } - return advisoryInfo; - } - return null; -} - -function npmToOutdatedResult(output) { - const npmOutdated = JSON.parse(output); - return objectToEntries3(npmOutdated) - .filter(entry => entry[1].current != null) - .map(([name, overview]) => ({ - name, - current: overview.current, - latest: overview.latest, - type: overview.type, - ...(overview.homepage != null && { url: overview.homepage }), - })); -} - -// packages/plugin-js-packages/src/lib/package-managers/npm/npm.ts -var npmDependencyOptions = { - prod: ['--omit=dev', '--omit=optional'], - dev: ['--include=dev', '--omit=optional'], - optional: ['--include=optional', '--omit=dev'], -}; -var npmPackageManager = { - slug: 'npm', - name: 'NPM', - command: 'npm', - icon: 'npm', - docs: { - homepage: 'https://docs.npmjs.com/', - audit: 'https://docs.npmjs.com/cli/commands/npm-audit', - outdated: 'https://docs.npmjs.com/cli/commands/npm-outdated', - }, - audit: { - getCommandArgs: groupDep => [ - ...COMMON_AUDIT_ARGS, - ...npmDependencyOptions[groupDep], - '--audit-level=none', - ], - unifyResult: npmToAuditResult, - // prod dependencies need to be filtered out manually since v10 - postProcessResult: results => { - const depGroups = objectToKeys3(results); - const devFilter = - results.dev && results.prod - ? filterAuditResult(results.dev, 'name', results.prod) - : results.dev; - const optionalFilter = - results.optional && results.prod - ? filterAuditResult(results.optional, 'name', results.prod) - : results.optional; - return { - ...(depGroups.includes('prod') && { prod: results.prod }), - ...(depGroups.includes('dev') && { dev: devFilter }), - ...(depGroups.includes('optional') && { optional: optionalFilter }), - }; - }, - }, - outdated: { - commandArgs: [...COMMON_OUTDATED_ARGS, '--long'], - unifyResult: npmToOutdatedResult, - }, -}; - -var WORKDIR3 = pluginWorkDir3('js-packages'); -var RUNNER_OUTPUT_PATH3 = path11.join(WORKDIR3, 'runner-output.json'); -var PLUGIN_CONFIG_PATH3 = path11.join( - process.cwd(), - WORKDIR3, - 'plugin-config.json', -); - -var outdatedSeverity = { - major: 'error', - premajor: 'info', - minor: 'warning', - preminor: 'info', - patch: 'info', - prepatch: 'info', - prerelease: 'info', -}; -var RELEASE_TYPES = objectToKeys6(outdatedSeverity); - -var DEFAULT_CHROME_FLAGS = [...DEFAULT_FLAGS, '--headless']; -var LIGHTHOUSE_PLUGIN_SLUG = 'lighthouse'; -var LIGHTHOUSE_OUTPUT_PATH = path15.join( - DEFAULT_PERSIST_OUTPUT_DIR, - LIGHTHOUSE_PLUGIN_SLUG, -); - -var { audits, categories } = defaultConfig; -var allRawLighthouseAudits = await Promise.all( - (audits ?? []).map(loadLighthouseAudit), -); -var LIGHTHOUSE_NAVIGATION_AUDITS = allRawLighthouseAudits - .filter( - audit => - audit.meta.supportedModes == null || - (Array.isArray(audit.meta.supportedModes) && - audit.meta.supportedModes.includes('navigation')), - ) - .map(audit => ({ - slug: audit.meta.id, - title: getMetaString(audit.meta.title), - description: getMetaString(audit.meta.description), - })); -var navigationAuditSlugs = new Set( - LIGHTHOUSE_NAVIGATION_AUDITS.map(({ slug }) => slug), -); -var LIGHTHOUSE_GROUPS = Object.entries(categories ?? {}).map( - ([id, category]) => ({ - slug: id, - title: getMetaString(category.title), - ...(category.description && { - description: getMetaString(category.description), - }), - refs: category.auditRefs - .filter(({ id: auditSlug }) => navigationAuditSlugs.has(auditSlug)) - .map(ref => ({ - slug: ref.id, - weight: ref.weight, - })), - }), -); -function getMetaString(value) { - if (typeof value === 'string') { - return value; - } - return value.formattedDefault; -} -async function loadLighthouseAudit(value) { - if (typeof value === 'object' && 'implementation' in value) { - return value.implementation; - } - if (typeof value === 'function') { - return value; - } - const file = typeof value === 'string' ? value : value.path; - const module = await import(`lighthouse/core/audits/${file}.js`); - return module.default; -} -var LIGHTHOUSE_REPORT_NAME = 'lighthouse-report.json'; -var DEFAULT_CLI_FLAGS = { - // default values extracted from - // https://github.com/GoogleChrome/lighthouse/blob/7d80178c37a1b600ea8f092fc0b098029799a659/cli/cli-flags.js#L80 - verbose: false, - saveAssets: false, - chromeFlags: DEFAULT_CHROME_FLAGS, - port: 0, - hostname: '127.0.0.1', - view: false, - channel: 'cli', - // custom overwrites in favour of the plugin - // hide logs by default - quiet: true, - onlyAudits: [], - skipAudits: [], - onlyCategories: [], - output: ['json'], - outputPath: path16.join(LIGHTHOUSE_OUTPUT_PATH, LIGHTHOUSE_REPORT_NAME), -}; - -// packages/plugin-lighthouse/src/lib/normalize-flags.ts -var { onlyCategories, ...originalDefaultCliFlags } = DEFAULT_CLI_FLAGS; -var DEFAULT_LIGHTHOUSE_OPTIONS = { - ...originalDefaultCliFlags, - onlyGroups: onlyCategories, -}; -var lighthouseUnsupportedCliFlags = [ - 'precomputedLanternDataPath', - // Path to the file where precomputed lantern data should be read from. - 'chromeIgnoreDefaultFlags', - // ignore default flags from Lighthouse CLI - // No error reporting implemented as in the source Sentry was involved - // See: https://github.com/GoogleChrome/lighthouse/blob/d8ccf70692216b7fa047a4eaa2d1277b0b7fe947/cli/bin.js#L124 - 'enableErrorReporting', - // enable error reporting - // lighthouse CLI specific debug logs - 'list-all-audits', - // Prints a list of all available audits and exits. - 'list-locales', - // Prints a list of all supported locales and exits. - 'list-trace-categories', - // Prints a list of all required trace categories and exits. -]; -var LIGHTHOUSE_UNSUPPORTED_CLI_FLAGS = new Set(lighthouseUnsupportedCliFlags); - -function lighthouseGroupRef(groupSlug, weight = 1) { - return { - plugin: LIGHTHOUSE_PLUGIN_SLUG, - slug: groupSlug, - type: 'group', - weight, - }; -} - -// code-pushup.preset.ts -var lighthouseCategories = [ - { - slug: 'performance', - title: 'Performance', - refs: [lighthouseGroupRef('performance')], - }, - { - slug: 'a11y', - title: 'Accessibility', - refs: [lighthouseGroupRef('accessibility')], - }, - { - slug: 'best-practices', - title: 'Best Practices', - refs: [lighthouseGroupRef('best-practices')], - }, - { - slug: 'seo', - title: 'SEO', - refs: [lighthouseGroupRef('seo')], - }, -]; -function getDocCoverageCategories(config2) { - return [ - { - slug: 'doc-coverage-cat', - title: 'Documentation coverage', - description: 'Measures how much of your code is **documented**.', - refs: filterGroupsByOnlyAudits(groups, config2).map(group => ({ - weight: 1, - type: 'group', - plugin: PLUGIN_SLUG, - slug: group.slug, - })), - }, - ]; -} -var docCoverageCoreConfig = async config2 => { - return { - plugins: [await src_default(config2)], - categories: getDocCoverageCategories(config2), - }; -}; - -// packages/utils/src/lib/merge-configs.ts -function mergeConfigs(config2, ...configs) { - return configs.reduce( - (acc, obj) => ({ - ...acc, - ...mergeCategories(acc.categories, obj.categories), - ...mergePlugins(acc.plugins, obj.plugins), - ...mergePersist(acc.persist, obj.persist), - ...mergeUpload(acc.upload, obj.upload), - }), - config2, - ); -} -function mergeCategories(a, b) { - if (!a && !b) { - return {}; - } - const mergedMap = /* @__PURE__ */ new Map(); - const addToMap = categories2 => { - categories2.forEach(newObject => { - if (mergedMap.has(newObject.slug)) { - const existingObject = mergedMap.get(newObject.slug); - mergedMap.set(newObject.slug, { - ...existingObject, - ...newObject, - refs: mergeByUniqueCategoryRefCombination( - existingObject?.refs, - newObject.refs, - ), - }); - } else { - mergedMap.set(newObject.slug, newObject); - } - }); - }; - if (a) { - addToMap(a); - } - if (b) { - addToMap(b); - } - return { categories: [...mergedMap.values()] }; -} -function mergePlugins(a, b) { - if (!a && !b) { - return { plugins: [] }; - } - const mergedMap = /* @__PURE__ */ new Map(); - const addToMap = plugins => { - plugins.forEach(newObject => { - mergedMap.set(newObject.slug, newObject); - }); - }; - if (a) { - addToMap(a); - } - if (b) { - addToMap(b); - } - return { plugins: [...mergedMap.values()] }; -} -function mergePersist(a, b) { - if (!a && !b) { - return {}; - } - if (a) { - return b ? { persist: { ...a, ...b } } : {}; - } else { - return { persist: b }; - } -} -function mergeByUniqueCategoryRefCombination(a, b) { - const map = /* @__PURE__ */ new Map(); - const addToMap = refs => { - refs.forEach(ref => { - const uniqueIdentification = `${ref.type}:${ref.plugin}:${ref.slug}`; - if (map.has(uniqueIdentification)) { - map.set(uniqueIdentification, { - ...map.get(uniqueIdentification), - ...ref, - }); - } else { - map.set(uniqueIdentification, ref); - } - }); - }; - if (a) { - addToMap(a); - } - if (b) { - addToMap(b); - } - return [...map.values()]; -} -function mergeUpload(a, b) { - if (!a && !b) { - return {}; - } - if (a) { - return b ? { upload: { ...a, ...b } } : {}; - } else { - return { upload: b }; - } -} - -// code-pushup.config.ts -var envSchema = z5.object({ - CP_SERVER: z5.string().url(), - CP_API_KEY: z5.string().min(1), - CP_ORGANIZATION: z5.string().min(1), - CP_PROJECT: z5.string().min(1), -}); -var { data: env } = await envSchema.safeParseAsync(process.env); -var config = { - ...(env && { - upload: { - server: env.CP_SERVER, - apiKey: env.CP_API_KEY, - organization: env.CP_ORGANIZATION, - project: env.CP_PROJECT, - }, - }), - plugins: [], -}; -var code_pushup_config_default = mergeConfigs( - config, - // await coverageCoreConfigNx(), - // await jsPackagesCoreConfig(), - // await lighthouseCoreConfig( - // 'https://github.com/code-pushup/cli?tab=readme-ov-file#code-pushup-cli/', - // ), - // await eslintCoreConfigNx(), - await docCoverageCoreConfig({ - sourceGlob: ['packages/**/*.ts', '!**/*.spec.ts', '!**/*.test.ts'], - onlyAudits: ['methods-coverage', 'functions-coverage'], - }), -); -export { code_pushup_config_default as default }; -//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiY29kZS1wdXNodXAuY29uZmlnLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1jb3ZlcmFnZS9zcmMvbGliL2NvdmVyYWdlLXBsdWdpbi50cyIsICJwYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9jb25maWcudHMiLCAicGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvcnVubmVyL2luZGV4LnRzIiwgInBhY2thZ2VzL3BsdWdpbi1jb3ZlcmFnZS9zcmMvbGliL3J1bm5lci9jb25zdGFudHMudHMiLCAicGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvcnVubmVyL2xjb3YvbGNvdi1ydW5uZXIudHMiLCAicGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvcnVubmVyL2xjb3YvcGFyc2UtbGNvdi50cyIsICJwYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXIvbGNvdi90cmFuc2Zvcm0udHMiLCAicGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvbngvY292ZXJhZ2UtcGF0aHMudHMiLCAicGFja2FnZXMvcGx1Z2luLWRvYy1jb3ZlcmFnZS9zcmMvbGliL2NvbmZpZy50cyIsICJwYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWIvY29uc3RhbnRzLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1kb2MtY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXIvZG9jLXByb2Nlc3Nlci50cyIsICJwYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWIvdXRpbHMudHMiLCAicGFja2FnZXMvcGx1Z2luLWRvYy1jb3ZlcmFnZS9zcmMvbGliL3J1bm5lci91dGlscy50cyIsICJwYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWIvZG9jLWNvdmVyYWdlLXBsdWdpbi50cyIsICJwYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9pbmRleC50cyIsICJwYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvZXNsaW50LXBsdWdpbi50cyIsICJwYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvY29uZmlnLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhL2dyb3Vwcy50cyIsICJwYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvbWV0YS9oYXNoLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhL3BhcnNlLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhL3ZlcnNpb25zL2ZsYXQudHMiLCAicGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGEvdmVyc2lvbnMvbGVnYWN5LnRzIiwgInBhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9zZXR1cC50cyIsICJwYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvbWV0YS92ZXJzaW9ucy9kZXRlY3QudHMiLCAicGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGEvdHJhbnNmb3JtLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9ydW5uZXIvaW5kZXgudHMiLCAicGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL3J1bm5lci9saW50LnRzIiwgInBhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9ydW5uZXIvdHJhbnNmb3JtLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9ueC91dGlscy50cyIsICJwYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9qcy1wYWNrYWdlcy1wbHVnaW4udHMiLCAicGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvY29uZmlnLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL2NvbnN0YW50cy50cyIsICJwYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL25wbS9ucG0udHMiLCAicGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcnVubmVyL3V0aWxzLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnMvY29uc3RhbnRzLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnMvbnBtL2F1ZGl0LXJlc3VsdC50cyIsICJwYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL25wbS9vdXRkYXRlZC1yZXN1bHQudHMiLCAicGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy9wbnBtL3BucG0udHMiLCAicGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy9wbnBtL291dGRhdGVkLXJlc3VsdC50cyIsICJwYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL3lhcm4tY2xhc3NpYy9hdWRpdC1yZXN1bHQudHMiLCAicGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy95YXJuLWNsYXNzaWMvb3V0ZGF0ZWQtcmVzdWx0LnRzIiwgInBhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3J1bm5lci9pbmRleC50cyIsICJwYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9ydW5uZXIvYXVkaXQvdHJhbnNmb3JtLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3J1bm5lci9jb25zdGFudHMudHMiLCAicGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcnVubmVyL291dGRhdGVkL3RyYW5zZm9ybS50cyIsICJwYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9ydW5uZXIvb3V0ZGF0ZWQvY29uc3RhbnRzLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnMvZGVyaXZlLXBhY2thZ2UtbWFuYWdlci50cyIsICJwYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL2Rlcml2ZS15YXJuLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvbGlnaHRob3VzZS1wbHVnaW4udHMiLCAicGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9jb25zdGFudHMudHMiLCAicGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9ub3JtYWxpemUtZmxhZ3MudHMiLCAicGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9ydW5uZXIvY29uc3RhbnRzLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL3J1bm5lci50cyIsICJwYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lci91dGlscy50cyIsICJwYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lci9kZXRhaWxzL2RldGFpbHMudHMiLCAicGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9ydW5uZXIvZGV0YWlscy9vcHBvcnR1bml0eS50eXBlLnRzIiwgInBhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL2RldGFpbHMvdGFibGUudHlwZS50cyIsICJwYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lci9kZXRhaWxzL2l0ZW0tdmFsdWUudHMiLCAicGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9ydW5uZXIvZGV0YWlscy91dGlscy50cyIsICJwYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3V0aWxzLnRzIiwgImNvZGUtcHVzaHVwLnByZXNldC50cyIsICJwYWNrYWdlcy91dGlscy9zcmMvaW5kZXgudHMiLCAicGFja2FnZXMvdXRpbHMvc3JjL2xpYi9leGVjdXRlLXByb2Nlc3MudHMiLCAicGFja2FnZXMvdXRpbHMvc3JjL2xpYi9yZXBvcnRzL3V0aWxzLnRzIiwgInBhY2thZ2VzL3V0aWxzL3NyYy9saWIvZmlsZS1zeXN0ZW0udHMiLCAicGFja2FnZXMvdXRpbHMvc3JjL2xpYi9mb3JtYXR0aW5nLnRzIiwgInBhY2thZ2VzL3V0aWxzL3NyYy9saWIvbG9nZ2luZy50cyIsICJwYWNrYWdlcy91dGlscy9zcmMvbGliL2dpdC9naXQuY29tbWl0cy1hbmQtdGFncy50cyIsICJwYWNrYWdlcy91dGlscy9zcmMvbGliL3NlbXZlci50cyIsICJwYWNrYWdlcy91dGlscy9zcmMvbGliL2dpdC9naXQudHMiLCAicGFja2FnZXMvdXRpbHMvc3JjL2xpYi90cmFuc2Zvcm0udHMiLCAicGFja2FnZXMvdXRpbHMvc3JjL2xpYi9tZXJnZS1jb25maWdzLnRzIiwgInBhY2thZ2VzL3V0aWxzL3NyYy9saWIvcHJvZ3Jlc3MudHMiLCAicGFja2FnZXMvdXRpbHMvc3JjL2xpYi9yZXBvcnRzL2dlbmVyYXRlLW1kLXJlcG9ydC50cyIsICJwYWNrYWdlcy91dGlscy9zcmMvbGliL3JlcG9ydHMvZm9ybWF0dGluZy50cyIsICJwYWNrYWdlcy91dGlscy9zcmMvbGliL3JlcG9ydHMvZ2VuZXJhdGUtbWQtcmVwb3J0LWNhdGVnb3ktc2VjdGlvbi50cyIsICJwYWNrYWdlcy91dGlscy9zcmMvbGliL3JlcG9ydHMvZ2VuZXJhdGUtbWQtcmVwb3J0cy1kaWZmLnRzIiwgInBhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0cy9nZW5lcmF0ZS1tZC1yZXBvcnRzLWRpZmYtdXRpbHMudHMiLCAicGFja2FnZXMvdXRpbHMvc3JjL2xpYi9yZXBvcnRzL2xvYWQtcmVwb3J0LnRzIiwgInBhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0cy9sb2ctc3Rkb3V0LXN1bW1hcnkudHMiLCAicGFja2FnZXMvdXRpbHMvc3JjL2xpYi96b2QtdmFsaWRhdGlvbi50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9jb2RlLXB1c2h1cC5jb25maWcudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGlcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvY29kZS1wdXNodXAuY29uZmlnLnRzXCI7aW1wb3J0ICdkb3RlbnYvY29uZmlnJztcbmltcG9ydCB7IHogfSBmcm9tICd6b2QnO1xuaW1wb3J0IHsgZG9jQ292ZXJhZ2VDb3JlQ29uZmlnIH0gZnJvbSAnLi9jb2RlLXB1c2h1cC5wcmVzZXQuanMnO1xuaW1wb3J0IHR5cGUgeyBDb3JlQ29uZmlnIH0gZnJvbSAnLi9wYWNrYWdlcy9tb2RlbHMvc3JjL2luZGV4LmpzJztcbmltcG9ydCB7IG1lcmdlQ29uZmlncyB9IGZyb20gJy4vcGFja2FnZXMvdXRpbHMvc3JjL2luZGV4LmpzJztcbi8vIGxvYWQgdXBsb2FkIGNvbmZpZ3VyYXRpb24gZnJvbSBlbnZpcm9ubWVudFxuY29uc3QgZW52U2NoZW1hID0gei5vYmplY3Qoe1xuICBDUF9TRVJWRVI6IHouc3RyaW5nKCkudXJsKCksXG4gIENQX0FQSV9LRVk6IHouc3RyaW5nKCkubWluKDEpLFxuICBDUF9PUkdBTklaQVRJT046IHouc3RyaW5nKCkubWluKDEpLFxuICBDUF9QUk9KRUNUOiB6LnN0cmluZygpLm1pbigxKSxcbn0pO1xuY29uc3QgeyBkYXRhOiBlbnYgfSA9IGF3YWl0IGVudlNjaGVtYS5zYWZlUGFyc2VBc3luYyhwcm9jZXNzLmVudik7XG5cbmNvbnN0IGNvbmZpZzogQ29yZUNvbmZpZyA9IHtcbiAgLi4uKGVudiAmJiB7XG4gICAgdXBsb2FkOiB7XG4gICAgICBzZXJ2ZXI6IGVudi5DUF9TRVJWRVIsXG4gICAgICBhcGlLZXk6IGVudi5DUF9BUElfS0VZLFxuICAgICAgb3JnYW5pemF0aW9uOiBlbnYuQ1BfT1JHQU5JWkFUSU9OLFxuICAgICAgcHJvamVjdDogZW52LkNQX1BST0pFQ1QsXG4gICAgfSxcbiAgfSksXG5cbiAgcGx1Z2luczogW10sXG59O1xuXG5leHBvcnQgZGVmYXVsdCBtZXJnZUNvbmZpZ3MoXG4gIGNvbmZpZyxcbiAgLy8gYXdhaXQgY292ZXJhZ2VDb3JlQ29uZmlnTngoKSxcbiAgLy8gYXdhaXQganNQYWNrYWdlc0NvcmVDb25maWcoKSxcbiAgLy8gYXdhaXQgbGlnaHRob3VzZUNvcmVDb25maWcoXG4gIC8vICAgJ2h0dHBzOi8vZ2l0aHViLmNvbS9jb2RlLXB1c2h1cC9jbGk/dGFiPXJlYWRtZS1vdi1maWxlI2NvZGUtcHVzaHVwLWNsaS8nLFxuICAvLyApLFxuICAvLyBhd2FpdCBlc2xpbnRDb3JlQ29uZmlnTngoKSxcbiAgYXdhaXQgZG9jQ292ZXJhZ2VDb3JlQ29uZmlnKHtcbiAgICBzb3VyY2VHbG9iOiBbJ3BhY2thZ2VzLyoqLyoudHMnLCAnISoqLyouc3BlYy50cycsICchKiovKi50ZXN0LnRzJ10sXG4gICAgb25seUF1ZGl0czogWydtZXRob2RzLWNvdmVyYWdlJywgJ2Z1bmN0aW9ucy1jb3ZlcmFnZSddXG4gIH0pLFxuKTtcbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1jb3ZlcmFnZS9zcmMvbGliL2NvdmVyYWdlLXBsdWdpbi50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYlwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9jb3ZlcmFnZS1wbHVnaW4udHNcIjtpbXBvcnQgeyBjcmVhdGVSZXF1aXJlIH0gZnJvbSAnbm9kZTptb2R1bGUnO1xuaW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB7IGZpbGVVUkxUb1BhdGggfSBmcm9tICdub2RlOnVybCc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0LCBHcm91cCwgUGx1Z2luQ29uZmlnIH0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5pbXBvcnQgeyBjYXBpdGFsaXplIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB7XG4gIHR5cGUgQ292ZXJhZ2VQbHVnaW5Db25maWcsXG4gIHR5cGUgQ292ZXJhZ2VUeXBlLFxuICBjb3ZlcmFnZVBsdWdpbkNvbmZpZ1NjaGVtYSxcbn0gZnJvbSAnLi9jb25maWcuanMnO1xuaW1wb3J0IHsgY3JlYXRlUnVubmVyQ29uZmlnIH0gZnJvbSAnLi9ydW5uZXIvaW5kZXguanMnO1xuaW1wb3J0IHsgY292ZXJhZ2VEZXNjcmlwdGlvbiwgY292ZXJhZ2VUeXBlV2VpZ2h0TWFwcGVyIH0gZnJvbSAnLi91dGlscy5qcyc7XG5cbi8qKlxuICogSW5zdGFudGlhdGVzIENvZGUgUHVzaFVwIGNvZGUgY292ZXJhZ2UgcGx1Z2luIGZvciBjb3JlIGNvbmZpZy5cbiAqXG4gKiBAZXhhbXBsZVxuICogaW1wb3J0IGNvdmVyYWdlUGx1Z2luIGZyb20gJ0Bjb2RlLXB1c2h1cC9jb3ZlcmFnZS1wbHVnaW4nXG4gKlxuICogZXhwb3J0IGRlZmF1bHQge1xuICogICAvLyAuLi4gY29yZSBjb25maWcgLi4uXG4gKiAgIHBsdWdpbnM6IFtcbiAqICAgICAvLyAuLi4gb3RoZXIgcGx1Z2lucyAuLi5cbiAqICAgICBhd2FpdCBjb3ZlcmFnZVBsdWdpbih7XG4gKiAgICAgICByZXBvcnRzOiBbeyByZXN1bHRzUGF0aDogJ2NvdmVyYWdlL2NsaS9sY292LmluZm8nLCBwYXRoVG9Qcm9qZWN0OiAncGFja2FnZXMvY2xpJyB9XVxuICogICAgIH0pXG4gKiAgIF1cbiAqIH1cbiAqXG4gKiBAcmV0dXJucyBQbHVnaW4gY29uZmlndXJhdGlvbi5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNvdmVyYWdlUGx1Z2luKFxuICBjb25maWc6IENvdmVyYWdlUGx1Z2luQ29uZmlnLFxuKTogUHJvbWlzZTxQbHVnaW5Db25maWc+IHtcbiAgY29uc3QgY292ZXJhZ2VDb25maWcgPSBjb3ZlcmFnZVBsdWdpbkNvbmZpZ1NjaGVtYS5wYXJzZShjb25maWcpO1xuXG4gIGNvbnN0IGF1ZGl0cyA9IGNvdmVyYWdlQ29uZmlnLmNvdmVyYWdlVHlwZXMubWFwKFxuICAgICh0eXBlKTogQXVkaXQgPT4gKHtcbiAgICAgIHNsdWc6IGAke3R5cGV9LWNvdmVyYWdlYCxcbiAgICAgIHRpdGxlOiBgJHtjYXBpdGFsaXplKHR5cGUpfSBjb3ZlcmFnZWAsXG4gICAgICBkZXNjcmlwdGlvbjogY292ZXJhZ2VEZXNjcmlwdGlvblt0eXBlXSxcbiAgICB9KSxcbiAgKTtcblxuICBjb25zdCBncm91cDogR3JvdXAgPSB7XG4gICAgc2x1ZzogJ2NvdmVyYWdlJyxcbiAgICB0aXRsZTogJ0NvZGUgY292ZXJhZ2UgbWV0cmljcycsXG4gICAgZGVzY3JpcHRpb246ICdHcm91cCBjb250YWluaW5nIGFsbCBkZWZpbmVkIGNvdmVyYWdlIHR5cGVzIGFzIGF1ZGl0cy4nLFxuICAgIHJlZnM6IGF1ZGl0cy5tYXAoYXVkaXQgPT4gKHtcbiAgICAgIC4uLmF1ZGl0LFxuICAgICAgd2VpZ2h0OlxuICAgICAgICBjb3ZlcmFnZVR5cGVXZWlnaHRNYXBwZXJbXG4gICAgICAgICAgYXVkaXQuc2x1Zy5zbGljZSgwLCBhdWRpdC5zbHVnLmluZGV4T2YoJy0nKSkgYXMgQ292ZXJhZ2VUeXBlXG4gICAgICAgIF0sXG4gICAgfSkpLFxuICB9O1xuXG4gIGNvbnN0IHJ1bm5lclNjcmlwdFBhdGggPSBwYXRoLmpvaW4oXG4gICAgZmlsZVVSTFRvUGF0aChwYXRoLmRpcm5hbWUoaW1wb3J0Lm1ldGEudXJsKSksXG4gICAgJy4uJyxcbiAgICAnYmluLmpzJyxcbiAgKTtcblxuICBjb25zdCBwYWNrYWdlSnNvbiA9IGNyZWF0ZVJlcXVpcmUoaW1wb3J0Lm1ldGEudXJsKShcbiAgICAnLi4vLi4vcGFja2FnZS5qc29uJyxcbiAgKSBhcyB0eXBlb2YgaW1wb3J0KCcuLi8uLi9wYWNrYWdlLmpzb24nKTtcblxuICByZXR1cm4ge1xuICAgIHNsdWc6ICdjb3ZlcmFnZScsXG4gICAgdGl0bGU6ICdDb2RlIGNvdmVyYWdlJyxcbiAgICBpY29uOiAnZm9sZGVyLWNvdmVyYWdlLW9wZW4nLFxuICAgIGRlc2NyaXB0aW9uOiAnT2ZmaWNpYWwgQ29kZSBQdXNoVXAgY29kZSBjb3ZlcmFnZSBwbHVnaW4uJyxcbiAgICBkb2NzVXJsOiAnaHR0cHM6Ly93d3cubnBtanMuY29tL3BhY2thZ2UvQGNvZGUtcHVzaHVwL2NvdmVyYWdlLXBsdWdpbi8nLFxuICAgIHBhY2thZ2VOYW1lOiBwYWNrYWdlSnNvbi5uYW1lLFxuICAgIHZlcnNpb246IHBhY2thZ2VKc29uLnZlcnNpb24sXG4gICAgYXVkaXRzLFxuICAgIGdyb3VwczogW2dyb3VwXSxcbiAgICBydW5uZXI6IGF3YWl0IGNyZWF0ZVJ1bm5lckNvbmZpZyhydW5uZXJTY3JpcHRQYXRoLCBjb3ZlcmFnZUNvbmZpZyksXG4gIH07XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9jb25maWcudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvY29uZmlnLnRzXCI7aW1wb3J0IHsgeiB9IGZyb20gJ3pvZCc7XG5cbmV4cG9ydCBjb25zdCBjb3ZlcmFnZVR5cGVTY2hlbWEgPSB6LmVudW0oWydmdW5jdGlvbicsICdicmFuY2gnLCAnbGluZSddKTtcbmV4cG9ydCB0eXBlIENvdmVyYWdlVHlwZSA9IHouaW5mZXI8dHlwZW9mIGNvdmVyYWdlVHlwZVNjaGVtYT47XG5cbmV4cG9ydCBjb25zdCBjb3ZlcmFnZVJlc3VsdFNjaGVtYSA9IHoudW5pb24oW1xuICB6Lm9iamVjdCh7XG4gICAgcmVzdWx0c1BhdGg6IHpcbiAgICAgIC5zdHJpbmcoe1xuICAgICAgICBkZXNjcmlwdGlvbjogJ1BhdGggdG8gY292ZXJhZ2UgcmVzdWx0cyBmb3IgTnggc2V0dXAuJyxcbiAgICAgIH0pXG4gICAgICAuaW5jbHVkZXMoJ2xjb3YnKSxcbiAgICBwYXRoVG9Qcm9qZWN0OiB6XG4gICAgICAuc3RyaW5nKHtcbiAgICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICAgJ1BhdGggZnJvbSB3b3Jrc3BhY2Ugcm9vdCB0byBwcm9qZWN0IHJvb3QuIE5lY2Vzc2FyeSBmb3IgTENPViByZXBvcnRzIHdoaWNoIHByb3ZpZGUgYSByZWxhdGl2ZSBwYXRoLicsXG4gICAgICB9KVxuICAgICAgLm9wdGlvbmFsKCksXG4gIH0pLFxuICB6XG4gICAgLnN0cmluZyh7XG4gICAgICBkZXNjcmlwdGlvbjogJ1BhdGggdG8gY292ZXJhZ2UgcmVzdWx0cyBmb3IgYSBzaW5nbGUgcHJvamVjdCBzZXR1cC4nLFxuICAgIH0pXG4gICAgLmluY2x1ZGVzKCdsY292JyksXG5dKTtcbmV4cG9ydCB0eXBlIENvdmVyYWdlUmVzdWx0ID0gei5pbmZlcjx0eXBlb2YgY292ZXJhZ2VSZXN1bHRTY2hlbWE+O1xuXG5leHBvcnQgY29uc3QgY292ZXJhZ2VQbHVnaW5Db25maWdTY2hlbWEgPSB6Lm9iamVjdCh7XG4gIGNvdmVyYWdlVG9vbENvbW1hbmQ6IHpcbiAgICAub2JqZWN0KHtcbiAgICAgIGNvbW1hbmQ6IHpcbiAgICAgICAgLnN0cmluZyh7IGRlc2NyaXB0aW9uOiAnQ29tbWFuZCB0byBydW4gY292ZXJhZ2UgdG9vbC4nIH0pXG4gICAgICAgIC5taW4oMSksXG4gICAgICBhcmdzOiB6XG4gICAgICAgIC5hcnJheSh6LnN0cmluZygpLCB7XG4gICAgICAgICAgZGVzY3JpcHRpb246ICdBcmd1bWVudHMgdG8gYmUgcGFzc2VkIHRvIHRoZSBjb3ZlcmFnZSB0b29sLicsXG4gICAgICAgIH0pXG4gICAgICAgIC5vcHRpb25hbCgpLFxuICAgIH0pXG4gICAgLm9wdGlvbmFsKCksXG4gIGNvdmVyYWdlVHlwZXM6IHpcbiAgICAuYXJyYXkoY292ZXJhZ2VUeXBlU2NoZW1hLCB7XG4gICAgICBkZXNjcmlwdGlvbjogJ0NvdmVyYWdlIHR5cGVzIG1lYXN1cmVkLiBEZWZhdWx0cyB0byBhbGwgYXZhaWxhYmxlIHR5cGVzLicsXG4gICAgfSlcbiAgICAubWluKDEpXG4gICAgLmRlZmF1bHQoWydmdW5jdGlvbicsICdicmFuY2gnLCAnbGluZSddKSxcbiAgcmVwb3J0czogelxuICAgIC5hcnJheShjb3ZlcmFnZVJlc3VsdFNjaGVtYSwge1xuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdQYXRoIHRvIGFsbCBjb2RlIGNvdmVyYWdlIHJlcG9ydCBmaWxlcy4gT25seSBMQ09WIGZvcm1hdCBpcyBzdXBwb3J0ZWQgZm9yIG5vdy4nLFxuICAgIH0pXG4gICAgLm1pbigxKSxcbiAgcGVyZmVjdFNjb3JlVGhyZXNob2xkOiB6XG4gICAgLm51bWJlcih7XG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ1Njb3JlIHdpbGwgYmUgMSAocGVyZmVjdCkgZm9yIHRoaXMgY292ZXJhZ2UgYW5kIGFib3ZlLiBTY29yZSByYW5nZSBpcyAwIC0gMS4nLFxuICAgIH0pXG4gICAgLmd0KDApXG4gICAgLm1heCgxKVxuICAgIC5vcHRpb25hbCgpLFxufSk7XG5leHBvcnQgdHlwZSBDb3ZlcmFnZVBsdWdpbkNvbmZpZyA9IHouaW5wdXQ8dHlwZW9mIGNvdmVyYWdlUGx1Z2luQ29uZmlnU2NoZW1hPjtcbmV4cG9ydCB0eXBlIEZpbmFsQ292ZXJhZ2VQbHVnaW5Db25maWcgPSB6LmluZmVyPFxuICB0eXBlb2YgY292ZXJhZ2VQbHVnaW5Db25maWdTY2hlbWFcbj47XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXIvaW5kZXgudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvcnVubmVyXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1jb3ZlcmFnZS9zcmMvbGliL3J1bm5lci9pbmRleC50c1wiO2ltcG9ydCB7IGJvbGQgfSBmcm9tICdhbnNpcyc7XG5pbXBvcnQgeyB3cml0ZUZpbGUgfSBmcm9tICdub2RlOmZzL3Byb21pc2VzJztcbmltcG9ydCBwYXRoIGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0T3V0cHV0cywgUnVubmVyQ29uZmlnIH0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5pbXBvcnQge1xuICBQcm9jZXNzRXJyb3IsXG4gIGVuc3VyZURpcmVjdG9yeUV4aXN0cyxcbiAgZXhlY3V0ZVByb2Nlc3MsXG4gIGZpbGVQYXRoVG9DbGlBcmcsXG4gIHJlYWRKc29uRmlsZSxcbiAgdWksXG59IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgdHlwZSB7IEZpbmFsQ292ZXJhZ2VQbHVnaW5Db25maWcgfSBmcm9tICcuLi9jb25maWcuanMnO1xuaW1wb3J0IHsgYXBwbHlNYXhTY29yZUFib3ZlVGhyZXNob2xkIH0gZnJvbSAnLi4vdXRpbHMuanMnO1xuaW1wb3J0IHsgUExVR0lOX0NPTkZJR19QQVRILCBSVU5ORVJfT1VUUFVUX1BBVEggfSBmcm9tICcuL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgeyBsY292UmVzdWx0c1RvQXVkaXRPdXRwdXRzIH0gZnJvbSAnLi9sY292L2xjb3YtcnVubmVyLmpzJztcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGV4ZWN1dGVSdW5uZXIoKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IHsgcmVwb3J0cywgY292ZXJhZ2VUb29sQ29tbWFuZCwgY292ZXJhZ2VUeXBlcyB9ID1cbiAgICBhd2FpdCByZWFkSnNvbkZpbGU8RmluYWxDb3ZlcmFnZVBsdWdpbkNvbmZpZz4oUExVR0lOX0NPTkZJR19QQVRIKTtcblxuICAvLyBSdW4gY292ZXJhZ2UgdG9vbCBpZiBwcm92aWRlZFxuICBpZiAoY292ZXJhZ2VUb29sQ29tbWFuZCAhPSBudWxsKSB7XG4gICAgY29uc3QgeyBjb21tYW5kLCBhcmdzIH0gPSBjb3ZlcmFnZVRvb2xDb21tYW5kO1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCBleGVjdXRlUHJvY2Vzcyh7IGNvbW1hbmQsIGFyZ3MgfSk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIFByb2Nlc3NFcnJvcikge1xuICAgICAgICB1aSgpLmxvZ2dlci5lcnJvcihib2xkKCdzdGRvdXQgZnJvbSBmYWlsZWQgY292ZXJhZ2UgdG9vbCBwcm9jZXNzOicpKTtcbiAgICAgICAgdWkoKS5sb2dnZXIuZXJyb3IoZXJyb3Iuc3Rkb3V0KTtcbiAgICAgICAgdWkoKS5sb2dnZXIuZXJyb3IoYm9sZCgnc3RkZXJyIGZyb20gZmFpbGVkIGNvdmVyYWdlIHRvb2wgcHJvY2VzczonKSk7XG4gICAgICAgIHVpKCkubG9nZ2VyLmVycm9yKGVycm9yLnN0ZGVycik7XG4gICAgICB9XG5cbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ0NvdmVyYWdlIHBsdWdpbjogUnVubmluZyBjb3ZlcmFnZSB0b29sIGZhaWxlZC4gTWFrZSBzdXJlIGFsbCB5b3VyIHByb3ZpZGVkIHRlc3RzIGFyZSBwYXNzaW5nLicsXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIC8vIENhbGN1bGF0ZSBjb3ZlcmFnZSBmcm9tIExDT1YgcmVzdWx0c1xuICBjb25zdCBhdWRpdE91dHB1dHMgPSBhd2FpdCBsY292UmVzdWx0c1RvQXVkaXRPdXRwdXRzKHJlcG9ydHMsIGNvdmVyYWdlVHlwZXMpO1xuXG4gIGF3YWl0IGVuc3VyZURpcmVjdG9yeUV4aXN0cyhwYXRoLmRpcm5hbWUoUlVOTkVSX09VVFBVVF9QQVRIKSk7XG4gIGF3YWl0IHdyaXRlRmlsZShSVU5ORVJfT1VUUFVUX1BBVEgsIEpTT04uc3RyaW5naWZ5KGF1ZGl0T3V0cHV0cykpO1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlUnVubmVyQ29uZmlnKFxuICBzY3JpcHRQYXRoOiBzdHJpbmcsXG4gIGNvbmZpZzogRmluYWxDb3ZlcmFnZVBsdWdpbkNvbmZpZyxcbik6IFByb21pc2U8UnVubmVyQ29uZmlnPiB7XG4gIC8vIENyZWF0ZSBKU09OIGNvbmZpZyBmb3IgZXhlY3V0ZVJ1bm5lclxuICBhd2FpdCBlbnN1cmVEaXJlY3RvcnlFeGlzdHMocGF0aC5kaXJuYW1lKFBMVUdJTl9DT05GSUdfUEFUSCkpO1xuICBhd2FpdCB3cml0ZUZpbGUoUExVR0lOX0NPTkZJR19QQVRILCBKU09OLnN0cmluZ2lmeShjb25maWcpKTtcblxuICBjb25zdCB0aHJlc2hvbGQgPSBjb25maWcucGVyZmVjdFNjb3JlVGhyZXNob2xkO1xuXG4gIHJldHVybiB7XG4gICAgY29tbWFuZDogJ25vZGUnLFxuICAgIGFyZ3M6IFtmaWxlUGF0aFRvQ2xpQXJnKHNjcmlwdFBhdGgpXSxcbiAgICBvdXRwdXRGaWxlOiBSVU5ORVJfT1VUUFVUX1BBVEgsXG4gICAgLi4uKHRocmVzaG9sZCAhPSBudWxsICYmIHtcbiAgICAgIG91dHB1dFRyYW5zZm9ybTogb3V0cHV0cyA9PlxuICAgICAgICBhcHBseU1heFNjb3JlQWJvdmVUaHJlc2hvbGQob3V0cHV0cyBhcyBBdWRpdE91dHB1dHMsIHRocmVzaG9sZCksXG4gICAgfSksXG4gIH07XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXIvY29uc3RhbnRzLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1jb3ZlcmFnZS9zcmMvbGliL3J1bm5lclwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXIvY29uc3RhbnRzLnRzXCI7aW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB7IHBsdWdpbldvcmtEaXIgfSBmcm9tICdAY29kZS1wdXNodXAvdXRpbHMnO1xuXG5leHBvcnQgY29uc3QgV09SS0RJUiA9IHBsdWdpbldvcmtEaXIoJ2NvdmVyYWdlJyk7XG5leHBvcnQgY29uc3QgUlVOTkVSX09VVFBVVF9QQVRIID0gcGF0aC5qb2luKFdPUktESVIsICdydW5uZXItb3V0cHV0Lmpzb24nKTtcbmV4cG9ydCBjb25zdCBQTFVHSU5fQ09ORklHX1BBVEggPSBwYXRoLmpvaW4oXG4gIHByb2Nlc3MuY3dkKCksXG4gIFdPUktESVIsXG4gICdwbHVnaW4tY29uZmlnLmpzb24nLFxuKTtcblxuZXhwb3J0IGNvbnN0IElOVkFMSURfRlVOQ1RJT05fTkFNRSA9ICcoZW1wdHktcmVwb3J0KSc7XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXIvbGNvdi9sY292LXJ1bm5lci50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXIvbGNvdlwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXIvbGNvdi9sY292LXJ1bm5lci50c1wiO2ltcG9ydCBwYXRoIGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgdHlwZSB7IExDT1ZSZWNvcmQgfSBmcm9tICdwYXJzZS1sY292JztcbmltcG9ydCB0eXBlIHsgQXVkaXRPdXRwdXRzIH0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5pbXBvcnQgeyBleGlzdHMsIHJlYWRUZXh0RmlsZSwgdG9Vbml4TmV3bGluZXMsIHVpIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB0eXBlIHsgQ292ZXJhZ2VSZXN1bHQsIENvdmVyYWdlVHlwZSB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XG5pbXBvcnQgeyBtZXJnZUxjb3ZSZXN1bHRzIH0gZnJvbSAnLi9tZXJnZS1sY292LmpzJztcbmltcG9ydCB7IHBhcnNlTGNvdiB9IGZyb20gJy4vcGFyc2UtbGNvdi5qcyc7XG5pbXBvcnQge1xuICBsY292Q292ZXJhZ2VUb0F1ZGl0T3V0cHV0LFxuICByZWNvcmRUb1N0YXRGdW5jdGlvbk1hcHBlcixcbn0gZnJvbSAnLi90cmFuc2Zvcm0uanMnO1xuaW1wb3J0IHR5cGUgeyBMQ09WU3RhdCwgTENPVlN0YXRzIH0gZnJvbSAnLi90eXBlcy5qcyc7XG5cbi8vIE5vdGU6IGNvbmRpdGlvbiBvciBzdGF0ZW1lbnQgY292ZXJhZ2UgaXMgbm90IHN1cHBvcnRlZCBpbiBMQ09WXG4vLyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy80ODI2MDQzNC9pcy1pdC1wb3NzaWJsZS10by1jaGVjay1jb25kaXRpb24tY292ZXJhZ2Utd2l0aC1nY292XG5cbi8qKlxuICpcbiAqIEBwYXJhbSByZXN1bHRzIFBhdGhzIHRvIExDT1YgcmVzdWx0c1xuICogQHBhcmFtIGNvdmVyYWdlVHlwZXMgdHlwZXMgb2YgY292ZXJhZ2UgdG8gYmUgY29uc2lkZXJlZFxuICogQHJldHVybnMgQXVkaXQgb3V0cHV0cyB3aXRoIGNvbXBsZXRlIGNvdmVyYWdlIGRhdGEuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBsY292UmVzdWx0c1RvQXVkaXRPdXRwdXRzKFxuICByZXN1bHRzOiBDb3ZlcmFnZVJlc3VsdFtdLFxuICBjb3ZlcmFnZVR5cGVzOiBDb3ZlcmFnZVR5cGVbXSxcbik6IFByb21pc2U8QXVkaXRPdXRwdXRzPiB7XG4gIC8vIFBhcnNlIGxjb3YgZmlsZXNcbiAgY29uc3QgbGNvdlJlc3VsdHMgPSBhd2FpdCBwYXJzZUxjb3ZGaWxlcyhyZXN1bHRzKTtcblxuICAvLyBNZXJnZSBtdWx0aXBsZSBjb3ZlcmFnZSByZXBvcnRzIGZvciB0aGUgc2FtZSBmaWxlXG4gIGNvbnN0IG1lcmdlZFJlc3VsdHMgPSBtZXJnZUxjb3ZSZXN1bHRzKGxjb3ZSZXN1bHRzKTtcblxuICAvLyBDYWxjdWxhdGUgY29kZSBjb3ZlcmFnZSBmcm9tIGFsbCBjb3ZlcmFnZSByZXN1bHRzXG4gIGNvbnN0IHRvdGFsQ292ZXJhZ2VTdGF0cyA9IGdldFRvdGFsQ292ZXJhZ2VGcm9tTGNvdlJlY29yZHMoXG4gICAgbWVyZ2VkUmVzdWx0cyxcbiAgICBjb3ZlcmFnZVR5cGVzLFxuICApO1xuXG4gIHJldHVybiBjb3ZlcmFnZVR5cGVzXG4gICAgLm1hcChjb3ZlcmFnZVR5cGUgPT4ge1xuICAgICAgY29uc3Qgc3RhdHMgPSB0b3RhbENvdmVyYWdlU3RhdHNbY292ZXJhZ2VUeXBlXTtcbiAgICAgIGlmICghc3RhdHMpIHtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG4gICAgICByZXR1cm4gbGNvdkNvdmVyYWdlVG9BdWRpdE91dHB1dChzdGF0cywgY292ZXJhZ2VUeXBlKTtcbiAgICB9KVxuICAgIC5maWx0ZXIoZXhpc3RzKTtcbn1cblxuLyoqXG4gKlxuICogQHBhcmFtIHJlc3VsdHMgUGF0aHMgdG8gTENPViByZXN1bHRzXG4gKiBAcmV0dXJucyBBcnJheSBvZiBwYXJzZWQgTENPVlJlY29yZHMuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwYXJzZUxjb3ZGaWxlcyhcbiAgcmVzdWx0czogQ292ZXJhZ2VSZXN1bHRbXSxcbik6IFByb21pc2U8TENPVlJlY29yZFtdPiB7XG4gIGNvbnN0IHBhcnNlZFJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbChcbiAgICByZXN1bHRzLm1hcChhc3luYyByZXN1bHQgPT4ge1xuICAgICAgY29uc3QgcmVzdWx0c1BhdGggPVxuICAgICAgICB0eXBlb2YgcmVzdWx0ID09PSAnc3RyaW5nJyA/IHJlc3VsdCA6IHJlc3VsdC5yZXN1bHRzUGF0aDtcbiAgICAgIGNvbnN0IGxjb3ZGaWxlQ29udGVudCA9IGF3YWl0IHJlYWRUZXh0RmlsZShyZXN1bHRzUGF0aCk7XG4gICAgICBpZiAobGNvdkZpbGVDb250ZW50LnRyaW0oKSA9PT0gJycpIHtcbiAgICAgICAgdWkoKS5sb2dnZXIud2FybmluZyhcbiAgICAgICAgICBgQ292ZXJhZ2UgcGx1Z2luOiBFbXB0eSBsY292IHJlcG9ydCBmaWxlIGRldGVjdGVkIGF0ICR7cmVzdWx0c1BhdGh9LmAsXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgICBjb25zdCBwYXJzZWRSZWNvcmRzID0gcGFyc2VMY292KHRvVW5peE5ld2xpbmVzKGxjb3ZGaWxlQ29udGVudCkpO1xuICAgICAgcmV0dXJuIHBhcnNlZFJlY29yZHMubWFwPExDT1ZSZWNvcmQ+KHJlY29yZCA9PiAoe1xuICAgICAgICAuLi5yZWNvcmQsXG4gICAgICAgIGZpbGU6XG4gICAgICAgICAgdHlwZW9mIHJlc3VsdCA9PT0gJ3N0cmluZycgfHwgcmVzdWx0LnBhdGhUb1Byb2plY3QgPT0gbnVsbFxuICAgICAgICAgICAgPyByZWNvcmQuZmlsZVxuICAgICAgICAgICAgOiBwYXRoLmpvaW4ocmVzdWx0LnBhdGhUb1Byb2plY3QsIHJlY29yZC5maWxlKSxcbiAgICAgIH0pKTtcbiAgICB9KSxcbiAgKTtcbiAgaWYgKHBhcnNlZFJlc3VsdHMubGVuZ3RoICE9PSByZXN1bHRzLmxlbmd0aCkge1xuICAgIHRocm93IG5ldyBFcnJvcignU29tZSBwcm92aWRlZCBMQ09WIHJlc3VsdHMgd2VyZSBub3QgdmFsaWQuJyk7XG4gIH1cblxuICBjb25zdCBmbGF0UmVzdWx0cyA9IHBhcnNlZFJlc3VsdHMuZmxhdCgpO1xuXG4gIGlmIChmbGF0UmVzdWx0cy5sZW5ndGggPT09IDApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0FsbCBwcm92aWRlZCByZXN1bHRzIGFyZSBlbXB0eS4nKTtcbiAgfVxuXG4gIHJldHVybiBmbGF0UmVzdWx0cztcbn1cblxuLyoqXG4gKlxuICogQHBhcmFtIHJlY29yZHMgVGhpcyBmdW5jdGlvbiBhZ2dyZWdhdGVzIGNvdmVyYWdlIHN0YXRzIGZyb20gYWxsIGNvdmVyYWdlIGZpbGVzXG4gKiBAcGFyYW0gY292ZXJhZ2VUeXBlcyBUeXBlcyBvZiBjb3ZlcmFnZSB0byBiZSBnYXRoZXJlZFxuICogQHJldHVybnMgQ29tcGxldGUgY292ZXJhZ2Ugc3RhdHMgZm9yIGFsbCBkZWZpbmVkIHR5cGVzIG9mIGNvdmVyYWdlLlxuICovXG5mdW5jdGlvbiBnZXRUb3RhbENvdmVyYWdlRnJvbUxjb3ZSZWNvcmRzKFxuICByZWNvcmRzOiBMQ09WUmVjb3JkW10sXG4gIGNvdmVyYWdlVHlwZXM6IENvdmVyYWdlVHlwZVtdLFxuKTogTENPVlN0YXRzIHtcbiAgcmV0dXJuIHJlY29yZHMucmVkdWNlPExDT1ZTdGF0cz4oXG4gICAgKGFjYywgcmVwb3J0KSA9PlxuICAgICAgT2JqZWN0LmZyb21FbnRyaWVzKFtcbiAgICAgICAgLi4uT2JqZWN0LmVudHJpZXMoYWNjKSxcbiAgICAgICAgLi4uKFxuICAgICAgICAgIE9iamVjdC5lbnRyaWVzKFxuICAgICAgICAgICAgZ2V0Q292ZXJhZ2VTdGF0c0Zyb21MY292UmVjb3JkKHJlcG9ydCwgY292ZXJhZ2VUeXBlcyksXG4gICAgICAgICAgKSBhcyBbQ292ZXJhZ2VUeXBlLCBMQ09WU3RhdF1bXVxuICAgICAgICApLm1hcCgoW3R5cGUsIHN0YXRzXSk6IFtDb3ZlcmFnZVR5cGUsIExDT1ZTdGF0XSA9PiBbXG4gICAgICAgICAgdHlwZSxcbiAgICAgICAgICB7XG4gICAgICAgICAgICB0b3RhbEZvdW5kOiAoYWNjW3R5cGVdPy50b3RhbEZvdW5kID8/IDApICsgc3RhdHMudG90YWxGb3VuZCxcbiAgICAgICAgICAgIHRvdGFsSGl0OiAoYWNjW3R5cGVdPy50b3RhbEhpdCA/PyAwKSArIHN0YXRzLnRvdGFsSGl0LFxuICAgICAgICAgICAgaXNzdWVzOiBbLi4uKGFjY1t0eXBlXT8uaXNzdWVzID8/IFtdKSwgLi4uc3RhdHMuaXNzdWVzXSxcbiAgICAgICAgICB9LFxuICAgICAgICBdKSxcbiAgICAgIF0pLFxuICAgIHt9LFxuICApO1xufVxuXG4vKipcbiAqIEBwYXJhbSByZWNvcmQgcmVjb3JkIGZpbGUgZGF0YVxuICogQHBhcmFtIGNvdmVyYWdlVHlwZXMgdHlwZXMgb2YgY292ZXJhZ2UgdG8gYmUgZ2F0aGVyZWRcbiAqIEByZXR1cm5zIFJlbGV2YW50IGNvdmVyYWdlIGRhdGEgZnJvbSBvbmUgbGNvdiByZWNvcmQgZmlsZS5cbiAqL1xuZnVuY3Rpb24gZ2V0Q292ZXJhZ2VTdGF0c0Zyb21MY292UmVjb3JkKFxuICByZWNvcmQ6IExDT1ZSZWNvcmQsXG4gIGNvdmVyYWdlVHlwZXM6IENvdmVyYWdlVHlwZVtdLFxuKTogTENPVlN0YXRzIHtcbiAgcmV0dXJuIE9iamVjdC5mcm9tRW50cmllcyhcbiAgICBjb3ZlcmFnZVR5cGVzLm1hcCgoY292ZXJhZ2VUeXBlKTogW0NvdmVyYWdlVHlwZSwgTENPVlN0YXRdID0+IFtcbiAgICAgIGNvdmVyYWdlVHlwZSxcbiAgICAgIHJlY29yZFRvU3RhdEZ1bmN0aW9uTWFwcGVyW2NvdmVyYWdlVHlwZV0ocmVjb3JkKSxcbiAgICBdKSxcbiAgKTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1jb3ZlcmFnZS9zcmMvbGliL3J1bm5lci9sY292L3BhcnNlLWxjb3YudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvcnVubmVyL2xjb3ZcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvcnVubmVyL2xjb3YvcGFyc2UtbGNvdi50c1wiO2ltcG9ydCBwYXJzZUxjb3ZFeHBvcnQgZnJvbSAncGFyc2UtbGNvdic7XG5cbnR5cGUgUGFyc2VMY292Rm4gPSB0eXBlb2YgcGFyc2VMY292RXhwb3J0O1xuXG4vLyB0aGUgcGFyc2UtbGNvdiBleHBvcnQgaXMgaW5jb25zaXN0ZW50IChzb21ldGltZXMgaXQncyAuZGVmYXVsdCwgc29tZXRpbWVzIGl0J3MgLmRlZmF1bHQuZGVmYXVsdClcbmNvbnN0IGdvZEtub3dzID0gcGFyc2VMY292RXhwb3J0IGFzIHVua25vd24gYXNcbiAgfCBQYXJzZUxjb3ZGblxuICB8IHsgZGVmYXVsdDogUGFyc2VMY292Rm4gfTtcblxuZXhwb3J0IGNvbnN0IHBhcnNlTGNvdjogUGFyc2VMY292Rm4gPVxuICAnZGVmYXVsdCcgaW4gZ29kS25vd3MgPyBnb2RLbm93cy5kZWZhdWx0IDogZ29kS25vd3M7XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXIvbGNvdi90cmFuc2Zvcm0udHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvcnVubmVyL2xjb3ZcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvcnVubmVyL2xjb3YvdHJhbnNmb3JtLnRzXCI7aW1wb3J0IHR5cGUgeyBMQ09WUmVjb3JkIH0gZnJvbSAncGFyc2UtbGNvdic7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0T3V0cHV0LCBJc3N1ZSB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgdG9OdW1iZXJQcmVjaXNpb24sIHRvT3JkaW5hbCB9IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgdHlwZSB7IENvdmVyYWdlVHlwZSB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XG5pbXBvcnQgeyBJTlZBTElEX0ZVTkNUSU9OX05BTUUgfSBmcm9tICcuLi9jb25zdGFudHMuanMnO1xuaW1wb3J0IHR5cGUgeyBMQ09WU3RhdCB9IGZyb20gJy4vdHlwZXMuanMnO1xuaW1wb3J0IHsgY2FsY3VsYXRlQ292ZXJhZ2UsIG1lcmdlQ29uc2VjdXRpdmVOdW1iZXJzIH0gZnJvbSAnLi91dGlscy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBsY292UmVwb3J0VG9GdW5jdGlvblN0YXQocmVjb3JkOiBMQ09WUmVjb3JkKTogTENPVlN0YXQge1xuICBjb25zdCB2YWxpZFJlY29yZCA9IHJlbW92ZUVtcHR5UmVwb3J0KHJlY29yZCk7XG5cbiAgcmV0dXJuIHtcbiAgICB0b3RhbEZvdW5kOiB2YWxpZFJlY29yZC5mdW5jdGlvbnMuZm91bmQsXG4gICAgdG90YWxIaXQ6IHZhbGlkUmVjb3JkLmZ1bmN0aW9ucy5oaXQsXG4gICAgaXNzdWVzOlxuICAgICAgdmFsaWRSZWNvcmQuZnVuY3Rpb25zLmhpdCA8IHZhbGlkUmVjb3JkLmZ1bmN0aW9ucy5mb3VuZFxuICAgICAgICA/IHZhbGlkUmVjb3JkLmZ1bmN0aW9ucy5kZXRhaWxzXG4gICAgICAgICAgICAuZmlsdGVyKGRldGFpbCA9PiAhZGV0YWlsLmhpdClcbiAgICAgICAgICAgIC5tYXAoXG4gICAgICAgICAgICAgIChkZXRhaWwpOiBJc3N1ZSA9PiAoe1xuICAgICAgICAgICAgICAgIG1lc3NhZ2U6IGBGdW5jdGlvbiAke2RldGFpbC5uYW1lfSBpcyBub3QgY2FsbGVkIGluIGFueSB0ZXN0IGNhc2UuYCxcbiAgICAgICAgICAgICAgICBzZXZlcml0eTogJ2Vycm9yJyxcbiAgICAgICAgICAgICAgICBzb3VyY2U6IHtcbiAgICAgICAgICAgICAgICAgIGZpbGU6IHZhbGlkUmVjb3JkLmZpbGUsXG4gICAgICAgICAgICAgICAgICBwb3NpdGlvbjogeyBzdGFydExpbmU6IGRldGFpbC5saW5lIH0sXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgfSksXG4gICAgICAgICAgICApXG4gICAgICAgIDogW10sXG4gIH07XG59XG5cbmZ1bmN0aW9uIHJlbW92ZUVtcHR5UmVwb3J0KHJlY29yZDogTENPVlJlY29yZCk6IExDT1ZSZWNvcmQge1xuICBjb25zdCB2YWxpZEZ1bmN0aW9ucyA9IHJlY29yZC5mdW5jdGlvbnMuZGV0YWlscy5maWx0ZXIoXG4gICAgZGV0YWlsID0+IGRldGFpbC5uYW1lICE9PSBJTlZBTElEX0ZVTkNUSU9OX05BTUUsXG4gICk7XG5cbiAgaWYgKHZhbGlkRnVuY3Rpb25zLmxlbmd0aCA9PT0gcmVjb3JkLmZ1bmN0aW9ucy5mb3VuZCkge1xuICAgIHJldHVybiByZWNvcmQ7XG4gIH1cblxuICByZXR1cm4ge1xuICAgIC4uLnJlY29yZCxcbiAgICBmdW5jdGlvbnM6IHtcbiAgICAgIGRldGFpbHM6IHZhbGlkRnVuY3Rpb25zLFxuICAgICAgZm91bmQ6IHZhbGlkRnVuY3Rpb25zLmxlbmd0aCxcbiAgICAgIGhpdDogdmFsaWRGdW5jdGlvbnMucmVkdWNlKFxuICAgICAgICAoYWNjLCBmbikgPT4gYWNjICsgKGZuLmhpdCAhPSBudWxsICYmIGZuLmhpdCA+IDAgPyAxIDogMCksXG4gICAgICAgIDAsXG4gICAgICApLFxuICAgIH0sXG4gIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsY292UmVwb3J0VG9MaW5lU3RhdChyZWNvcmQ6IExDT1ZSZWNvcmQpOiBMQ09WU3RhdCB7XG4gIGNvbnN0IG1pc3NpbmdDb3ZlcmFnZSA9IHJlY29yZC5saW5lcy5oaXQgPCByZWNvcmQubGluZXMuZm91bmQ7XG4gIGNvbnN0IGxpbmVzID0gbWlzc2luZ0NvdmVyYWdlXG4gICAgPyByZWNvcmQubGluZXMuZGV0YWlsc1xuICAgICAgICAuZmlsdGVyKGRldGFpbCA9PiAhZGV0YWlsLmhpdClcbiAgICAgICAgLm1hcChkZXRhaWwgPT4gZGV0YWlsLmxpbmUpXG4gICAgOiBbXTtcblxuICBjb25zdCBsaW5lUG9zaXRpb25zID0gbWVyZ2VDb25zZWN1dGl2ZU51bWJlcnMobGluZXMpO1xuXG4gIHJldHVybiB7XG4gICAgdG90YWxGb3VuZDogcmVjb3JkLmxpbmVzLmZvdW5kLFxuICAgIHRvdGFsSGl0OiByZWNvcmQubGluZXMuaGl0LFxuICAgIGlzc3VlczogbWlzc2luZ0NvdmVyYWdlXG4gICAgICA/IGxpbmVQb3NpdGlvbnMubWFwKChsaW5lUG9zaXRpb24pOiBJc3N1ZSA9PiB7XG4gICAgICAgICAgY29uc3QgbGluZVJlZmVyZW5jZSA9XG4gICAgICAgICAgICBsaW5lUG9zaXRpb24uZW5kID09IG51bGxcbiAgICAgICAgICAgICAgPyBgTGluZSAke2xpbmVQb3NpdGlvbi5zdGFydH0gaXNgXG4gICAgICAgICAgICAgIDogYExpbmVzICR7bGluZVBvc2l0aW9uLnN0YXJ0fS0ke2xpbmVQb3NpdGlvbi5lbmR9IGFyZWA7XG5cbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgbWVzc2FnZTogYCR7bGluZVJlZmVyZW5jZX0gbm90IGNvdmVyZWQgaW4gYW55IHRlc3QgY2FzZS5gLFxuICAgICAgICAgICAgc2V2ZXJpdHk6ICd3YXJuaW5nJyxcbiAgICAgICAgICAgIHNvdXJjZToge1xuICAgICAgICAgICAgICBmaWxlOiByZWNvcmQuZmlsZSxcbiAgICAgICAgICAgICAgcG9zaXRpb246IHtcbiAgICAgICAgICAgICAgICBzdGFydExpbmU6IGxpbmVQb3NpdGlvbi5zdGFydCxcbiAgICAgICAgICAgICAgICBlbmRMaW5lOiBsaW5lUG9zaXRpb24uZW5kLFxuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9O1xuICAgICAgICB9KVxuICAgICAgOiBbXSxcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGxjb3ZSZXBvcnRUb0JyYW5jaFN0YXQocmVjb3JkOiBMQ09WUmVjb3JkKTogTENPVlN0YXQge1xuICByZXR1cm4ge1xuICAgIHRvdGFsRm91bmQ6IHJlY29yZC5icmFuY2hlcy5mb3VuZCxcbiAgICB0b3RhbEhpdDogcmVjb3JkLmJyYW5jaGVzLmhpdCxcbiAgICBpc3N1ZXM6XG4gICAgICByZWNvcmQuYnJhbmNoZXMuaGl0IDwgcmVjb3JkLmJyYW5jaGVzLmZvdW5kXG4gICAgICAgID8gcmVjb3JkLmJyYW5jaGVzLmRldGFpbHNcbiAgICAgICAgICAgIC5maWx0ZXIoZGV0YWlsID0+ICFkZXRhaWwudGFrZW4pXG4gICAgICAgICAgICAubWFwKFxuICAgICAgICAgICAgICAoZGV0YWlsKTogSXNzdWUgPT4gKHtcbiAgICAgICAgICAgICAgICBtZXNzYWdlOiBgJHt0b09yZGluYWwoXG4gICAgICAgICAgICAgICAgICBkZXRhaWwuYnJhbmNoICsgMSxcbiAgICAgICAgICAgICAgICApfSBicmFuY2ggaXMgbm90IHRha2VuIGluIGFueSB0ZXN0IGNhc2UuYCxcbiAgICAgICAgICAgICAgICBzZXZlcml0eTogJ2Vycm9yJyxcbiAgICAgICAgICAgICAgICBzb3VyY2U6IHtcbiAgICAgICAgICAgICAgICAgIGZpbGU6IHJlY29yZC5maWxlLFxuICAgICAgICAgICAgICAgICAgcG9zaXRpb246IHsgc3RhcnRMaW5lOiBkZXRhaWwubGluZSB9LFxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIH0pLFxuICAgICAgICAgICAgKVxuICAgICAgICA6IFtdLFxuICB9O1xufVxuXG5leHBvcnQgY29uc3QgcmVjb3JkVG9TdGF0RnVuY3Rpb25NYXBwZXIgPSB7XG4gIGJyYW5jaDogbGNvdlJlcG9ydFRvQnJhbmNoU3RhdCxcbiAgbGluZTogbGNvdlJlcG9ydFRvTGluZVN0YXQsXG4gIGZ1bmN0aW9uOiBsY292UmVwb3J0VG9GdW5jdGlvblN0YXQsXG59O1xuXG4vKipcbiAqXG4gKiBAcGFyYW0gc3RhdCBjb2RlIGNvdmVyYWdlIHJlc3VsdCBmb3IgYSBnaXZlbiB0eXBlXG4gKiBAcGFyYW0gY292ZXJhZ2VUeXBlIGNvZGUgY292ZXJhZ2UgdHlwZVxuICogQHJldHVybnMgUmVzdWx0IG9mIGNvbXBsZXRlIGNvZGUgY2NvdmVyYWdlIGRhdGEgY292ZXJ0ZWQgdG8gQXVkaXRPdXRwdXRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGxjb3ZDb3ZlcmFnZVRvQXVkaXRPdXRwdXQoXG4gIHN0YXQ6IExDT1ZTdGF0LFxuICBjb3ZlcmFnZVR5cGU6IENvdmVyYWdlVHlwZSxcbik6IEF1ZGl0T3V0cHV0IHtcbiAgY29uc3QgY292ZXJhZ2UgPSBjYWxjdWxhdGVDb3ZlcmFnZShzdGF0LnRvdGFsSGl0LCBzdGF0LnRvdGFsRm91bmQpO1xuICBjb25zdCBNQVhfREVDSU1BTF9QTEFDRVMgPSA0O1xuICBjb25zdCBjb3ZlcmFnZVBlcmNlbnRhZ2UgPSBjb3ZlcmFnZSAqIDEwMDtcblxuICByZXR1cm4ge1xuICAgIHNsdWc6IGAke2NvdmVyYWdlVHlwZX0tY292ZXJhZ2VgLFxuICAgIHNjb3JlOiB0b051bWJlclByZWNpc2lvbihjb3ZlcmFnZSwgTUFYX0RFQ0lNQUxfUExBQ0VTKSxcbiAgICB2YWx1ZTogY292ZXJhZ2VQZXJjZW50YWdlLFxuICAgIGRpc3BsYXlWYWx1ZTogYCR7dG9OdW1iZXJQcmVjaXNpb24oY292ZXJhZ2VQZXJjZW50YWdlLCAxKX0gJWAsXG4gICAgZGV0YWlsczoge1xuICAgICAgaXNzdWVzOiBzdGF0Lmlzc3VlcyxcbiAgICB9LFxuICB9O1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvbngvY292ZXJhZ2UtcGF0aHMudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvbnhcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWNvdmVyYWdlL3NyYy9saWIvbngvY292ZXJhZ2UtcGF0aHMudHNcIjsvLy8gPHJlZmVyZW5jZSB0eXBlcz1cInZpdGVzdFwiIC8+XG5pbXBvcnQgdHlwZSB7XG4gIFByb2plY3RDb25maWd1cmF0aW9uLFxuICBQcm9qZWN0R3JhcGhQcm9qZWN0Tm9kZSxcbiAgVHJlZSxcbn0gZnJvbSAnQG54L2RldmtpdCc7XG5pbXBvcnQgdHlwZSB7IEplc3RFeGVjdXRvck9wdGlvbnMgfSBmcm9tICdAbngvamVzdC9zcmMvZXhlY3V0b3JzL2plc3Qvc2NoZW1hJztcbmltcG9ydCB0eXBlIHsgVml0ZXN0RXhlY3V0b3JPcHRpb25zIH0gZnJvbSAnQG54L3ZpdGUvZXhlY3V0b3JzJztcbmltcG9ydCB7IGJvbGQgfSBmcm9tICdhbnNpcyc7XG5pbXBvcnQgcGF0aCBmcm9tICdub2RlOnBhdGgnO1xuaW1wb3J0IHsgaW1wb3J0TW9kdWxlLCB1aSB9IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgdHlwZSB7IENvdmVyYWdlUmVzdWx0IH0gZnJvbSAnLi4vY29uZmlnLmpzJztcblxuLyoqXG4gKiBAcGFyYW0gdGFyZ2V0cyBueCB0YXJnZXRzIHRvIGJlIHVzZWQgZm9yIG1lYXN1cmluZyBjb3ZlcmFnZSwgdGVzdCBieSBkZWZhdWx0XG4gKiBAcmV0dXJucyBBbiBhcnJheSBvZiBjb3ZlcmFnZSByZXN1bHQgaW5mb3JtYXRpb24gZm9yIHRoZSBjb3ZlcmFnZSBwbHVnaW4uXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXROeENvdmVyYWdlUGF0aHMoXG4gIHRhcmdldHM6IHN0cmluZ1tdID0gWyd0ZXN0J10sXG4gIHZlcmJvc2U/OiBib29sZWFuLFxuKTogUHJvbWlzZTxDb3ZlcmFnZVJlc3VsdFtdPiB7XG4gIGlmICh2ZXJib3NlKSB7XG4gICAgdWkoKS5sb2dnZXIuaW5mbyhcbiAgICAgIGJvbGQoJ1x1RDgzRFx1RENBMSBHYXRoZXJpbmcgY292ZXJhZ2UgZnJvbSB0aGUgZm9sbG93aW5nIG54IHByb2plY3RzOicpLFxuICAgICk7XG4gIH1cblxuICBjb25zdCB7IGNyZWF0ZVByb2plY3RHcmFwaEFzeW5jIH0gPSBhd2FpdCBpbXBvcnQoJ0BueC9kZXZraXQnKTtcbiAgY29uc3QgeyBub2RlcyB9ID0gYXdhaXQgY3JlYXRlUHJvamVjdEdyYXBoQXN5bmMoeyBleGl0T25FcnJvcjogZmFsc2UgfSk7XG5cbiAgY29uc3QgY292ZXJhZ2VSZXN1bHRzID0gYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgdGFyZ2V0cy5tYXAoYXN5bmMgdGFyZ2V0ID0+IHtcbiAgICAgIGNvbnN0IHJlbGV2YW50Tm9kZXMgPSBPYmplY3QudmFsdWVzKG5vZGVzKS5maWx0ZXIoZ3JhcGggPT5cbiAgICAgICAgaGFzTnhUYXJnZXQoZ3JhcGgsIHRhcmdldCksXG4gICAgICApO1xuXG4gICAgICByZXR1cm4gYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICAgIHJlbGV2YW50Tm9kZXMubWFwPFByb21pc2U8Q292ZXJhZ2VSZXN1bHQ+Pihhc3luYyAoeyBuYW1lLCBkYXRhIH0pID0+IHtcbiAgICAgICAgICBjb25zdCBjb3ZlcmFnZVBhdGhzID0gYXdhaXQgZ2V0Q292ZXJhZ2VQYXRoc0ZvclRhcmdldChkYXRhLCB0YXJnZXQpO1xuICAgICAgICAgIGlmICh2ZXJib3NlKSB7XG4gICAgICAgICAgICB1aSgpLmxvZ2dlci5pbmZvKGAtICR7bmFtZX06ICR7dGFyZ2V0fWApO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gY292ZXJhZ2VQYXRocztcbiAgICAgICAgfSksXG4gICAgICApO1xuICAgIH0pLFxuICApO1xuXG4gIGlmICh2ZXJib3NlKSB7XG4gICAgdWkoKS5sb2dnZXIuaW5mbygnXFxuJyk7XG4gIH1cblxuICByZXR1cm4gY292ZXJhZ2VSZXN1bHRzLmZsYXQoKTtcbn1cblxuZnVuY3Rpb24gaGFzTnhUYXJnZXQoXG4gIHByb2plY3Q6IFByb2plY3RHcmFwaFByb2plY3ROb2RlLFxuICB0YXJnZXQ6IHN0cmluZyxcbik6IGJvb2xlYW4ge1xuICByZXR1cm4gcHJvamVjdC5kYXRhLnRhcmdldHMgIT0gbnVsbCAmJiB0YXJnZXQgaW4gcHJvamVjdC5kYXRhLnRhcmdldHM7XG59XG5cbmV4cG9ydCB0eXBlIFZpdGVzdENvdmVyYWdlQ29uZmlnID0ge1xuICB0ZXN0OiB7XG4gICAgY292ZXJhZ2U/OiB7XG4gICAgICByZXBvcnRlcj86IHN0cmluZ1tdO1xuICAgICAgcmVwb3J0c0RpcmVjdG9yeT86IHN0cmluZztcbiAgICB9O1xuICB9O1xufTtcblxuZXhwb3J0IHR5cGUgSmVzdENvdmVyYWdlQ29uZmlnID0ge1xuICBjb3ZlcmFnZURpcmVjdG9yeT86IHN0cmluZztcbiAgY292ZXJhZ2VSZXBvcnRlcnM/OiBzdHJpbmdbXTtcbn07XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRDb3ZlcmFnZVBhdGhzRm9yVGFyZ2V0KFxuICBwcm9qZWN0OiBQcm9qZWN0Q29uZmlndXJhdGlvbixcbiAgdGFyZ2V0OiBzdHJpbmcsXG4pOiBQcm9taXNlPENvdmVyYWdlUmVzdWx0PiB7XG4gIGNvbnN0IHRhcmdldENvbmZpZyA9IHByb2plY3QudGFyZ2V0cz8uW3RhcmdldF07XG5cbiAgaWYgKCF0YXJnZXRDb25maWcpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgTm8gY29uZmlndXJhdGlvbiBmb3VuZCBmb3IgdGFyZ2V0ICR7dGFyZ2V0fSBpbiBwcm9qZWN0ICR7cHJvamVjdC5uYW1lfWAsXG4gICAgKTtcbiAgfVxuXG4gIGlmICh0YXJnZXRDb25maWcuZXhlY3V0b3I/LmluY2x1ZGVzKCdAbngvdml0ZScpKSB7XG4gICAgcmV0dXJuIGdldENvdmVyYWdlUGF0aEZvclZpdGVzdChcbiAgICAgIHRhcmdldENvbmZpZy5vcHRpb25zIGFzIFZpdGVzdEV4ZWN1dG9yT3B0aW9ucyxcbiAgICAgIHByb2plY3QsXG4gICAgICB0YXJnZXQsXG4gICAgKTtcbiAgfVxuXG4gIGlmICh0YXJnZXRDb25maWcuZXhlY3V0b3I/LmluY2x1ZGVzKCdAbngvamVzdCcpKSB7XG4gICAgcmV0dXJuIGdldENvdmVyYWdlUGF0aEZvckplc3QoXG4gICAgICB0YXJnZXRDb25maWcub3B0aW9ucyBhcyBKZXN0RXhlY3V0b3JPcHRpb25zLFxuICAgICAgcHJvamVjdCxcbiAgICAgIHRhcmdldCxcbiAgICApO1xuICB9XG5cbiAgdGhyb3cgbmV3IEVycm9yKFxuICAgIGBVbnN1cHBvcnRlZCBleGVjdXRvciAke3RhcmdldENvbmZpZy5leGVjdXRvcn0uIE9ubHkgQG54L3ZpdGUgYW5kIEBueC9qZXN0IGFyZSBjdXJyZW50bHkgc3VwcG9ydGVkLmAsXG4gICk7XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRDb3ZlcmFnZVBhdGhGb3JWaXRlc3QoXG4gIG9wdGlvbnM6IFZpdGVzdEV4ZWN1dG9yT3B0aW9ucyxcbiAgcHJvamVjdDogUHJvamVjdENvbmZpZ3VyYXRpb24sXG4gIHRhcmdldDogc3RyaW5nLFxuKSB7XG4gIGNvbnN0IHtcbiAgICBkZWZhdWx0OiB7IG5vcm1hbGl6ZVZpdGVDb25maWdGaWxlUGF0aFdpdGhUcmVlIH0sXG4gIH0gPSBhd2FpdCBpbXBvcnQoJ0BueC92aXRlJyk7XG4gIGNvbnN0IGNvbmZpZyA9IG5vcm1hbGl6ZVZpdGVDb25maWdGaWxlUGF0aFdpdGhUcmVlKFxuICAgIC8vIEhBQ0s6IG9ubHkgdHJlZS5leGlzdHMgaXMgY2FsbGVkLCBzbyBpbmplY3RpbmcgZXhpc3RTeW5jIGZyb20gbm9kZTpmcyBpbnN0ZWFkXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9jb25zaXN0ZW50LXR5cGUtYXNzZXJ0aW9uc1xuICAgIHsgZXhpc3RzOiAoYXdhaXQgaW1wb3J0KCdub2RlOmZzJykpLmV4aXN0c1N5bmMgfSBhcyBUcmVlLFxuICAgIHByb2plY3Qucm9vdCxcbiAgICBvcHRpb25zLmNvbmZpZ0ZpbGUsXG4gICk7XG4gIGlmICghY29uZmlnKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYENvdWxkIG5vdCBmaW5kIFZpdGVzdCBjb25maWcgZmlsZSBmb3IgdGFyZ2V0ICR7dGFyZ2V0fSBpbiBwcm9qZWN0ICR7cHJvamVjdC5uYW1lfWAsXG4gICAgKTtcbiAgfVxuXG4gIGNvbnN0IHZpdGVzdENvbmZpZyA9IGF3YWl0IGltcG9ydE1vZHVsZTxWaXRlc3RDb3ZlcmFnZUNvbmZpZz4oe1xuICAgIGZpbGVwYXRoOiBjb25maWcsXG4gICAgZm9ybWF0OiAnZXNtJyxcbiAgfSk7XG5cbiAgY29uc3QgcmVwb3J0c0RpcmVjdG9yeSA9XG4gICAgb3B0aW9ucy5yZXBvcnRzRGlyZWN0b3J5ID8/IHZpdGVzdENvbmZpZy50ZXN0LmNvdmVyYWdlPy5yZXBvcnRzRGlyZWN0b3J5O1xuICBjb25zdCByZXBvcnRlciA9IHZpdGVzdENvbmZpZy50ZXN0LmNvdmVyYWdlPy5yZXBvcnRlcjtcblxuICBpZiAocmVwb3J0c0RpcmVjdG9yeSA9PSBudWxsKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYFZpdGVzdCBjb3ZlcmFnZSBjb25maWd1cmF0aW9uIGF0ICR7Y29uZmlnfSBkb2VzIG5vdCBpbmNsdWRlIGNvdmVyYWdlIHBhdGggZm9yIHRhcmdldCAke3RhcmdldH0gaW4gcHJvamVjdCAke3Byb2plY3QubmFtZX0uIEFkZCB0aGUgcGF0aCB1bmRlciBjb3ZlcmFnZSA+IHJlcG9ydHNEaXJlY3RvcnkuYCxcbiAgICApO1xuICB9XG5cbiAgaWYgKCFyZXBvcnRlcj8uaW5jbHVkZXMoJ2xjb3YnKSkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGBWaXRlc3QgY292ZXJhZ2UgY29uZmlndXJhdGlvbiBhdCAke2NvbmZpZ30gZG9lcyBub3QgaW5jbHVkZSBMQ09WIHJlcG9ydCBmb3JtYXQgZm9yIHRhcmdldCAke3RhcmdldH0gaW4gcHJvamVjdCAke3Byb2plY3QubmFtZX0uIEFkZCAnbGNvdicgZm9ybWF0IHVuZGVyIGNvdmVyYWdlID4gcmVwb3J0ZXIuYCxcbiAgICApO1xuICB9XG5cbiAgaWYgKHBhdGguaXNBYnNvbHV0ZShyZXBvcnRzRGlyZWN0b3J5KSkge1xuICAgIHJldHVybiBwYXRoLmpvaW4ocmVwb3J0c0RpcmVjdG9yeSwgJ2xjb3YuaW5mbycpO1xuICB9XG4gIHJldHVybiB7XG4gICAgcGF0aFRvUHJvamVjdDogcHJvamVjdC5yb290LFxuICAgIHJlc3VsdHNQYXRoOiBwYXRoLmpvaW4ocHJvamVjdC5yb290LCByZXBvcnRzRGlyZWN0b3J5LCAnbGNvdi5pbmZvJyksXG4gIH07XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRDb3ZlcmFnZVBhdGhGb3JKZXN0KFxuICBvcHRpb25zOiBKZXN0RXhlY3V0b3JPcHRpb25zLFxuICBwcm9qZWN0OiBQcm9qZWN0Q29uZmlndXJhdGlvbixcbiAgdGFyZ2V0OiBzdHJpbmcsXG4pIHtcbiAgY29uc3QgeyBqZXN0Q29uZmlnIH0gPSBvcHRpb25zO1xuXG4gIGNvbnN0IHRlc3RDb25maWcgPSBhd2FpdCBpbXBvcnRNb2R1bGU8SmVzdENvdmVyYWdlQ29uZmlnPih7XG4gICAgZmlsZXBhdGg6IGplc3RDb25maWcsXG4gIH0pO1xuICBjb25zdCB7IGNvdmVyYWdlRGlyZWN0b3J5LCBjb3ZlcmFnZVJlcG9ydGVycyB9ID0ge1xuICAgIC4uLnRlc3RDb25maWcsXG4gICAgLi4ub3B0aW9ucyxcbiAgfTtcblxuICBpZiAoY292ZXJhZ2VEaXJlY3RvcnkgPT0gbnVsbCkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGBKZXN0IGNvdmVyYWdlIGNvbmZpZ3VyYXRpb24gYXQgJHtqZXN0Q29uZmlnfSBkb2VzIG5vdCBpbmNsdWRlIGNvdmVyYWdlIHBhdGggZm9yIHRhcmdldCAke3RhcmdldH0gaW4gJHtwcm9qZWN0Lm5hbWV9LiBBZGQgdGhlIHBhdGggdW5kZXIgY292ZXJhZ2VEaXJlY3RvcnkuYCxcbiAgICApO1xuICB9XG5cbiAgaWYgKCFjb3ZlcmFnZVJlcG9ydGVycz8uaW5jbHVkZXMoJ2xjb3YnKSAmJiAhKCdwcmVzZXQnIGluIHRlc3RDb25maWcpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYEplc3QgY292ZXJhZ2UgY29uZmlndXJhdGlvbiBhdCAke2plc3RDb25maWd9IGRvZXMgbm90IGluY2x1ZGUgTENPViByZXBvcnQgZm9ybWF0IGZvciB0YXJnZXQgJHt0YXJnZXR9IGluICR7cHJvamVjdC5uYW1lfS4gQWRkICdsY292JyBmb3JtYXQgdW5kZXIgY292ZXJhZ2VSZXBvcnRlcnMuYCxcbiAgICApO1xuICB9XG5cbiAgaWYgKHBhdGguaXNBYnNvbHV0ZShjb3ZlcmFnZURpcmVjdG9yeSkpIHtcbiAgICByZXR1cm4gcGF0aC5qb2luKGNvdmVyYWdlRGlyZWN0b3J5LCAnbGNvdi5pbmZvJyk7XG4gIH1cbiAgcmV0dXJuIHBhdGguam9pbihwcm9qZWN0LnJvb3QsIGNvdmVyYWdlRGlyZWN0b3J5LCAnbGNvdi5pbmZvJyk7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWIvY29uZmlnLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1kb2MtY292ZXJhZ2Uvc3JjL2xpYlwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWIvY29uZmlnLnRzXCI7aW1wb3J0IHsgeiB9IGZyb20gJ3pvZCc7XG5cbmV4cG9ydCBjb25zdCBkb2NDb3ZlcmFnZVBsdWdpbkNvbmZpZ1NjaGVtYSA9IHoub2JqZWN0KHtcbiAgb25seUF1ZGl0czogei5hcnJheSh6LnN0cmluZygpKS5vcHRpb25hbCgpLFxuICBzb3VyY2VHbG9iOiB6XG4gICAgLmFycmF5KHouc3RyaW5nKCkpXG4gICAgLmRlZmF1bHQoWydzcmMvKiovKi57dHMsdHN4fScsICchKiovKi5zcGVjLnRzJywgJyEqKi8qLnRlc3QudHMnXSksXG59KTtcblxuZXhwb3J0IHR5cGUgRG9jQ292ZXJhZ2VQbHVnaW5Db25maWcgPSB6LmluZmVyPFxuICB0eXBlb2YgZG9jQ292ZXJhZ2VQbHVnaW5Db25maWdTY2hlbWFcbj47IiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWRvYy1jb3ZlcmFnZS9zcmMvbGliL2NvbnN0YW50cy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWRvYy1jb3ZlcmFnZS9zcmMvbGliL2NvbnN0YW50cy50c1wiO2ltcG9ydCB0eXBlIHsgQXVkaXQsIEdyb3VwIH0gZnJvbSBcIkBjb2RlLXB1c2h1cC9tb2RlbHNcIjtcbmltcG9ydCB0eXBlIHsgQXVkaXRTbHVnIH0gZnJvbSBcIi4vbW9kZWxzXCI7XG5cbmV4cG9ydCBjb25zdCBQTFVHSU5fU0xVRyA9ICdkb2MtY292ZXJhZ2UnO1xuXG5leHBvcnQgY29uc3QgQVVESVRTX01BUDogUmVjb3JkPEF1ZGl0U2x1ZywgQXVkaXQ+ID0ge1xuICAgICdjbGFzc2VzLWNvdmVyYWdlJzoge1xuICAgICAgICBzbHVnOiAnY2xhc3Nlcy1jb3ZlcmFnZScsXG4gICAgICAgIHRpdGxlOiAnQ2xhc3NlcyBjb3ZlcmFnZScsXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnQ292ZXJhZ2Ugb2YgY2xhc3NlcycsXG4gICAgfSxcbiAgICAnbWV0aG9kcy1jb3ZlcmFnZSc6IHtcbiAgICAgICAgc2x1ZzogJ21ldGhvZHMtY292ZXJhZ2UnLFxuICAgICAgICB0aXRsZTogJ01ldGhvZHMgY292ZXJhZ2UnLFxuICAgICAgICBkZXNjcmlwdGlvbjogJ0NvdmVyYWdlIG9mIG1ldGhvZHMnLFxuICAgIH0sXG4gICAgJ2Z1bmN0aW9ucy1jb3ZlcmFnZSc6IHtcbiAgICAgICAgc2x1ZzogJ2Z1bmN0aW9ucy1jb3ZlcmFnZScsXG4gICAgICAgIHRpdGxlOiAnRnVuY3Rpb25zIGNvdmVyYWdlJyxcbiAgICAgICAgZGVzY3JpcHRpb246ICdDb3ZlcmFnZSBvZiBmdW5jdGlvbnMnLFxuICAgIH0sXG4gICAgJ2ludGVyZmFjZXMtY292ZXJhZ2UnOiB7XG4gICAgICAgIHNsdWc6ICdpbnRlcmZhY2VzLWNvdmVyYWdlJyxcbiAgICAgICAgdGl0bGU6ICdJbnRlcmZhY2VzIGNvdmVyYWdlJyxcbiAgICAgICAgZGVzY3JpcHRpb246ICdDb3ZlcmFnZSBvZiBpbnRlcmZhY2VzJyxcbiAgICB9LFxuICAgICd2YXJpYWJsZXMtY292ZXJhZ2UnOiB7XG4gICAgICAgIHNsdWc6ICd2YXJpYWJsZXMtY292ZXJhZ2UnLFxuICAgICAgICB0aXRsZTogJ1ZhcmlhYmxlcyBjb3ZlcmFnZScsXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnQ292ZXJhZ2Ugb2YgdmFyaWFibGVzJyxcbiAgICB9LFxuICAgICdwcm9wZXJ0aWVzLWNvdmVyYWdlJzoge1xuICAgICAgICBzbHVnOiAncHJvcGVydGllcy1jb3ZlcmFnZScsXG4gICAgICAgIHRpdGxlOiAnUHJvcGVydGllcyBjb3ZlcmFnZScsXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnQ292ZXJhZ2Ugb2YgcHJvcGVydGllcycsXG4gICAgfSxcbiAgICAndHlwZXMtY292ZXJhZ2UnOiB7XG4gICAgICAgIHNsdWc6ICd0eXBlcy1jb3ZlcmFnZScsXG4gICAgICAgIHRpdGxlOiAnVHlwZXMgY292ZXJhZ2UnLFxuICAgICAgICBkZXNjcmlwdGlvbjogJ0NvdmVyYWdlIG9mIHR5cGVzJyxcbiAgICB9LFxuICAgICdlbnVtcy1jb3ZlcmFnZSc6IHtcbiAgICAgICAgc2x1ZzogJ2VudW1zLWNvdmVyYWdlJyxcbiAgICAgICAgdGl0bGU6ICdFbnVtcyBjb3ZlcmFnZScsXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnQ292ZXJhZ2Ugb2YgZW51bXMnLFxuICAgIH0sXG59IGFzIGNvbnN0O1xuXG5leHBvcnQgY29uc3QgZ3JvdXBzOiBHcm91cFtdID0gW1xuICAgIHtcbiAgICAgICAgc2x1ZzogJ2RvY3VtZW50YXRpb24tY292ZXJhZ2UnLFxuICAgICAgICB0aXRsZTogJ0RvY3VtZW50YXRpb24gY292ZXJhZ2UnLFxuICAgICAgICBkZXNjcmlwdGlvbjogJ0RvY3VtZW50YXRpb24gY292ZXJhZ2UnLFxuICAgICAgICByZWZzOiBPYmplY3Qua2V5cyhBVURJVFNfTUFQKS5tYXAoc2x1ZyA9PiB7XG4gICAgICAgICAgICBzd2l0Y2ggKHNsdWcgYXMgQXVkaXRTbHVnKSB7XG4gICAgICAgICAgICAgICAgY2FzZSAnY2xhc3Nlcy1jb3ZlcmFnZSc6XG4gICAgICAgICAgICAgICAgY2FzZSAnZnVuY3Rpb25zLWNvdmVyYWdlJzpcbiAgICAgICAgICAgICAgICBjYXNlICdtZXRob2RzLWNvdmVyYWdlJzpcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHsgc2x1Zywgd2VpZ2h0OiAyIH1cbiAgICAgICAgICAgICAgICBjYXNlICdpbnRlcmZhY2VzLWNvdmVyYWdlJzpcbiAgICAgICAgICAgICAgICBjYXNlICdwcm9wZXJ0aWVzLWNvdmVyYWdlJzpcbiAgICAgICAgICAgICAgICBjYXNlICd0eXBlcy1jb3ZlcmFnZSc6XG4gICAgICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHsgc2x1Zywgd2VpZ2h0OiAxIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSksXG4gICAgfV07IiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWRvYy1jb3ZlcmFnZS9zcmMvbGliL3J1bm5lci9kb2MtcHJvY2Vzc2VyLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1kb2MtY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWRvYy1jb3ZlcmFnZS9zcmMvbGliL3J1bm5lci9kb2MtcHJvY2Vzc2VyLnRzXCI7aW1wb3J0IHsgQ2xhc3NEZWNsYXJhdGlvbiwgUHJvamVjdCwgU291cmNlRmlsZSB9IGZyb20gJ3RzLW1vcnBoJztcbmltcG9ydCB0eXBlIHsgRG9jQ292ZXJhZ2VQbHVnaW5Db25maWcgfSBmcm9tICcuLi9jb25maWcuanMnO1xuaW1wb3J0IHR5cGUge1xuICBDb3ZlcmFnZVJlc3VsdCxcbiAgQ292ZXJhZ2VUeXBlLFxuICBVbmRvY3VtZW50ZWROb2RlLFxuICBVbnByb2Nlc3NlZENvdmVyYWdlUmVzdWx0XG59IGZyb20gJy4uL21vZGVscy5qcyc7XG5pbXBvcnQgeyBnZXRDb3ZlcmFnZVR5cGVGcm9tS2luZCB9IGZyb20gJy4uL3V0aWxzLmpzJztcbmltcG9ydCB7IGNhbGN1bGF0ZUNvdmVyYWdlLCBjcmVhdGVFbXB0eVVucHJvY2Vzc2VkQ292ZXJhZ2VSZXBvcnQgfSBmcm9tICcuL3V0aWxzLmpzJztcblxuXG4vKiBlc2xpbnQtZGlzYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55ICovXG4vKiBlc2xpbnQtZGlzYWJsZSBmdW5jdGlvbmFsL2ltbXV0YWJsZS1kYXRhICovXG4vKiBlc2xpbnQtZGlzYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbWF4LXBhcmFtcyAqL1xuLyogZXNsaW50LWRpc2FibGUgZnVuY3Rpb25hbC9uby1sZXQgKi9cblxuXG4vKipcbiAqIFByb2Nlc3NlcyBkb2N1bWVudGF0aW9uIGNvdmVyYWdlIGZvciBUeXBlU2NyaXB0IGZpbGVzIGluIHRoZSBzcGVjaWZpZWQgcGF0aFxuICogQHBhcmFtIHRvSW5jbHVkZSAtIFRoZSBmaWxlIHBhdGggcGF0dGVybiB0byBpbmNsdWRlIGZvciBkb2N1bWVudGF0aW9uIGFuYWx5c2lzXG4gKiBAcmV0dXJucyB7Q292ZXJhZ2VSZXN1bHR9IE9iamVjdCBjb250YWluaW5nIGNvdmVyYWdlIHN0YXRpc3RpY3MgYW5kIHVuZG9jdW1lbnRlZCBpdGVtc1xuICovXG5leHBvcnQgZnVuY3Rpb24gcHJvY2Vzc0RvY0NvdmVyYWdlKGNvbmZpZzogRG9jQ292ZXJhZ2VQbHVnaW5Db25maWcpOiBDb3ZlcmFnZVJlc3VsdCB7XG4gIGNvbnN0IHByb2plY3QgPSBuZXcgUHJvamVjdCgpO1xuICBwcm9qZWN0LmFkZFNvdXJjZUZpbGVzQXRQYXRocyhjb25maWcuc291cmNlR2xvYik7XG5cbiAgcmV0dXJuIGdldFVucHJvY2Vzc2VkQ292ZXJhZ2VSZXBvcnQocHJvamVjdC5nZXRTb3VyY2VGaWxlcygpKTtcblxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0VW5wcm9jZXNzZWRDb3ZlcmFnZVJlcG9ydChzb3VyY2VGaWxlczogU291cmNlRmlsZVtdKSB7XG4gIGNvbnN0IHVucHJvY2Vzc2VkQ292ZXJhZ2VSZXBvcnQgPSBzb3VyY2VGaWxlc1xuICAgIC5yZWR1Y2UoKGNvdmVyYWdlUmVwb3J0T2ZBbGxGaWxlcywgc291cmNlRmlsZSkgPT4ge1xuXG4gICAgICAvLyBJbmZvIG9mIHRoZSBmaWxlXG4gICAgICBjb25zdCBmaWxlUGF0aCA9IHNvdXJjZUZpbGUuZ2V0RmlsZVBhdGgoKTtcbiAgICAgIGNvbnN0IGNsYXNzZXMgPSBzb3VyY2VGaWxlLmdldENsYXNzZXMoKTtcblxuICAgICAgLy8gQWxsIG5vZGVzIG9mIHRoZSBmaWxlXG4gICAgICBjb25zdCBhbGxOb2Rlc0Zyb21GaWxlID0gW1xuICAgICAgICAuLi5zb3VyY2VGaWxlLmdldEZ1bmN0aW9ucygpLFxuICAgICAgICAuLi5jbGFzc2VzLFxuICAgICAgICAuLi5nZXRDbGFzc05vZGVzKGNsYXNzZXMpLFxuICAgICAgICAuLi5zb3VyY2VGaWxlLmdldFR5cGVBbGlhc2VzKCksXG4gICAgICAgIC4uLnNvdXJjZUZpbGUuZ2V0RW51bXMoKSxcbiAgICAgICAgLi4uc291cmNlRmlsZS5nZXRJbnRlcmZhY2VzKCksXG4gICAgICAgIC8vIC4uLnNvdXJjZUZpbGUuZ2V0VmFyaWFibGVTdGF0ZW1lbnRzKCkuZmxhdE1hcChzdGF0ZW1lbnQgPT4gc3RhdGVtZW50LmdldERlY2xhcmF0aW9ucygpKVxuICAgICAgXTtcblxuICAgICAgY29uc3QgY292ZXJhZ2VSZXBvcnRPZkN1cnJlbnRGaWxlID0gYWxsTm9kZXNGcm9tRmlsZS5yZWR1Y2UoKGFjYywgbm9kZSkgPT4ge1xuICAgICAgICBjb25zdCBub2RlVHlwZSA9IGdldENvdmVyYWdlVHlwZUZyb21LaW5kKG5vZGUuZ2V0S2luZCgpKTtcbiAgICAgICAgYWNjW25vZGVUeXBlXS5ub2Rlc0NvdW50Kys7XG4gICAgICAgIGlmIChub2RlLmdldEpzRG9jcygpLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgIGFjY1tub2RlVHlwZV0uaXNzdWVzLnB1c2goXG4gICAgICAgICAgICBnZXRVbmRvY3VtZW50ZWROb2RlKGZpbGVQYXRoLCBub2RlVHlwZSwgbm9kZS5nZXROYW1lKCkgfHwgJycsIG5vZGUuZ2V0U3RhcnRMaW5lTnVtYmVyKCkpXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gYWNjO1xuICAgICAgfSwgY3JlYXRlRW1wdHlVbnByb2Nlc3NlZENvdmVyYWdlUmVwb3J0KCkpO1xuXG4gICAgICByZXR1cm4gbWVyZ2VDb3ZlcmFnZVJlc3VsdHMoY292ZXJhZ2VSZXBvcnRPZkFsbEZpbGVzLCBjb3ZlcmFnZVJlcG9ydE9mQ3VycmVudEZpbGUpO1xuICAgIH0sIGNyZWF0ZUVtcHR5VW5wcm9jZXNzZWRDb3ZlcmFnZVJlcG9ydCgpKTtcblxuICByZXR1cm4gY2FsY3VsYXRlQ292ZXJhZ2UodW5wcm9jZXNzZWRDb3ZlcmFnZVJlcG9ydCk7XG5cbn1cblxuZnVuY3Rpb24gbWVyZ2VDb3ZlcmFnZVJlc3VsdHMocmVzdWx0czogVW5wcm9jZXNzZWRDb3ZlcmFnZVJlc3VsdCwgY3VycmVudDogVW5wcm9jZXNzZWRDb3ZlcmFnZVJlc3VsdCkge1xuICByZXR1cm4ge1xuICAgIC4uLk9iamVjdC5mcm9tRW50cmllcyhPYmplY3QuZW50cmllcyhyZXN1bHRzKS5tYXAoKFtrZXksIHZhbHVlXSkgPT4ge1xuICAgICAgY29uc3Qgbm9kZSA9IHZhbHVlIGFzIENvdmVyYWdlUmVzdWx0W0NvdmVyYWdlVHlwZV07XG4gICAgICBjb25zdCB0eXBlID0ga2V5IGFzIENvdmVyYWdlVHlwZTtcbiAgICAgIHJldHVybiBbdHlwZSwge1xuICAgICAgICBub2Rlc0NvdW50OiBub2RlLm5vZGVzQ291bnQgKyBjdXJyZW50W3R5cGVdLm5vZGVzQ291bnQsXG4gICAgICAgIGlzc3VlczogWy4uLm5vZGUuaXNzdWVzLCAuLi5jdXJyZW50W3R5cGVdLmlzc3Vlc10sXG4gICAgICB9XVxuICAgIH0pKVxuICB9IGFzIFVucHJvY2Vzc2VkQ292ZXJhZ2VSZXN1bHQ7XG59XG5cbmZ1bmN0aW9uIGdldENsYXNzTm9kZXMoY2xhc3NOb2RlczogQ2xhc3NEZWNsYXJhdGlvbltdKSB7XG4gIHJldHVybiBjbGFzc05vZGVzLmZsYXRNYXAoY2xhc3NOb2RlID0+IFsuLi5jbGFzc05vZGUuZ2V0TWV0aG9kcygpLCAuLi5jbGFzc05vZGUuZ2V0UHJvcGVydGllcygpXSlcbn1cblxuLyoqXG4gKiBDcmVhdGVzIGFuIHVuZG9jdW1lbnRlZCBpdGVtIGVudHJ5XG4gKiBAcGFyYW0gZmlsZSAtIFRoZSBmaWxlIHBhdGggd2hlcmUgdGhlIGl0ZW0gd2FzIGZvdW5kXG4gKiBAcGFyYW0gdHlwZSAtIFRoZSB0eXBlIG9mIHRoZSB1bmRvY3VtZW50ZWQgaXRlbVxuICogQHBhcmFtIG5hbWUgLSBUaGUgbmFtZSBvZiB0aGUgdW5kb2N1bWVudGVkIGl0ZW1cbiAqIEBwYXJhbSBsaW5lIC0gVGhlIGxpbmUgbnVtYmVyIHdoZXJlIHRoZSBpdGVtIGFwcGVhcnNcbiAqIEByZXR1cm5zIHtVbmRvY3VtZW50ZWROb2RlfSBUaGUgdW5kb2N1bWVudGVkIGl0ZW0gZW50cnlcbiAqL1xuZnVuY3Rpb24gZ2V0VW5kb2N1bWVudGVkTm9kZShmaWxlOiBzdHJpbmcsIHR5cGU6IENvdmVyYWdlVHlwZSwgbmFtZTogc3RyaW5nLCBsaW5lOiBudW1iZXIpOiBVbmRvY3VtZW50ZWROb2RlIHtcbiAgcmV0dXJuIHsgZmlsZSwgdHlwZSwgbmFtZSwgbGluZSB9O1xufVxuXG5cblxuXG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWIvdXRpbHMudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWRvYy1jb3ZlcmFnZS9zcmMvbGliXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1kb2MtY292ZXJhZ2Uvc3JjL2xpYi91dGlscy50c1wiO2ltcG9ydCB0eXBlIHsgQXVkaXQsIEF1ZGl0T3V0cHV0cywgR3JvdXAgfSBmcm9tIFwiQGNvZGUtcHVzaHVwL21vZGVsc1wiO1xuaW1wb3J0IHsgU3ludGF4S2luZCB9IGZyb20gXCJ0eXBlc2NyaXB0XCI7XG5pbXBvcnQgdHlwZSB7IERvY0NvdmVyYWdlUGx1Z2luQ29uZmlnIH0gZnJvbSBcIi4vY29uZmlnXCI7XG5pbXBvcnQgeyBBVURJVFNfTUFQIH0gZnJvbSBcIi4vY29uc3RhbnRzXCI7XG5pbXBvcnQgdHlwZSB7IENvdmVyYWdlUmVzdWx0LCBDb3ZlcmFnZVR5cGUgfSBmcm9tIFwiLi9tb2RlbHNcIjtcblxuLyoqXG4gKiBHZXQgYXVkaXRzIGJhc2VkIG9uIHRoZSBjb25maWd1cmF0aW9uLlxuICogSWYgbm8gYXVkaXRzIGFyZSBzcGVjaWZpZWQsIHJldHVybiBhbGwgYXVkaXRzLlxuICogSWYgYXVkaXRzIGFyZSBzcGVjaWZpZWQsIHJldHVybiBvbmx5IHRoZSBzcGVjaWZpZWQgYXVkaXRzLlxuICogQHBhcmFtIGNvbmZpZyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdC5cbiAqIEByZXR1cm5zIFRoZSBhdWRpdHMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmaWx0ZXJBdWRpdHNCeVBsdWdpbkNvbmZpZyhjb25maWc6IFBpY2s8RG9jQ292ZXJhZ2VQbHVnaW5Db25maWcsICdvbmx5QXVkaXRzJz4pOiBBdWRpdFtdIHtcbiAgICBjb25zdCB7IG9ubHlBdWRpdHMgfSA9IGNvbmZpZ1xuXG4gICAgaWYgKCFvbmx5QXVkaXRzIHx8IG9ubHlBdWRpdHMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgIHJldHVybiBPYmplY3QudmFsdWVzKEFVRElUU19NQVApO1xuICAgIH1cblxuICAgIHJldHVybiBPYmplY3QudmFsdWVzKEFVRElUU19NQVApLmZpbHRlcihhdWRpdCA9PiBvbmx5QXVkaXRzLmluY2x1ZGVzKGF1ZGl0LnNsdWcpKTtcblxufVxuXG4vLyByZXR1cm4gZ3JvdXBzLmZpbHRlcihncm91cCA9PiBncm91cC5yZWZzLnNvbWUocmVmID0+IGF1ZGl0cy5zb21lKGF1ZGl0ID0+IGF1ZGl0LnNsdWcgPT09IHJlZi5zbHVnKSkpO1xuXG4vKipcbiAqIEZpbHRlciBncm91cHMgYnkgdGhlIGF1ZGl0cyB0aGF0IGFyZSBzcGVjaWZpZWQgaW4gdGhlIGNvbmZpZ3VyYXRpb24uXG4gKiBUaGUgZ3JvdXBzIHJlZnMgYXJlIGZpbHRlcmVkIHRvIG9ubHkgaW5jbHVkZSB0aGUgYXVkaXRzIHRoYXQgYXJlIHNwZWNpZmllZCBpbiB0aGUgY29uZmlndXJhdGlvbi5cbiAqIEBwYXJhbSBncm91cHMgLSBUaGUgZ3JvdXBzIHRvIGZpbHRlci5cbiAqIEBwYXJhbSBvcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxuICogQHJldHVybnMgVGhlIGZpbHRlcmVkIGdyb3Vwcy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZpbHRlckdyb3Vwc0J5T25seUF1ZGl0cyhncm91cHM6IEdyb3VwW10sIG9wdGlvbnM6IFBpY2s8RG9jQ292ZXJhZ2VQbHVnaW5Db25maWcsICdvbmx5QXVkaXRzJz4pOiBHcm91cFtdIHtcbiAgICBjb25zdCBhdWRpdHMgPSBmaWx0ZXJBdWRpdHNCeVBsdWdpbkNvbmZpZyhvcHRpb25zKTtcbiAgICByZXR1cm4gZ3JvdXBzXG4gICAgICAgIC5tYXAoZ3JvdXAgPT4gKHtcbiAgICAgICAgICAgIC4uLmdyb3VwLFxuICAgICAgICAgICAgcmVmczogZ3JvdXAucmVmcy5maWx0ZXIocmVmID0+IGF1ZGl0cy5zb21lKGF1ZGl0ID0+IGF1ZGl0LnNsdWcgPT09IHJlZi5zbHVnKSlcbiAgICAgICAgfSkpXG4gICAgICAgIC5maWx0ZXIoZ3JvdXAgPT4gZ3JvdXAucmVmcy5sZW5ndGggPiAwKTs7XG59XG5cblxuLyoqXG4gKiBUcmFuc2Zvcm1zIHRoZSBjb3ZlcmFnZSByZXBvcnQgaW50byBhdWRpdCBvdXRwdXRzLlxuICogQHBhcmFtIGNvdmVyYWdlUmVzdWx0IC0gVGhlIGNvdmVyYWdlIHJlc3VsdCBjb250YWluaW5nIHVuZG9jdW1lbnRlZCBpdGVtcyBhbmQgY292ZXJhZ2Ugc3RhdGlzdGljc1xuICogQHBhcmFtIG9wdGlvbnMgLSBDb25maWd1cmF0aW9uIG9wdGlvbnMgc3BlY2lmeWluZyB3aGljaCBhdWRpdHMgdG8gaW5jbHVkZVxuICogQHJldHVybnMgQXVkaXQgb3V0cHV0cyB3aXRoIGNvdmVyYWdlIHNjb3JlcyBhbmQgZGV0YWlscyBhYm91dCB1bmRvY3VtZW50ZWQgaXRlbXNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHRyYXNmb3JtQ292ZXJhZ2VSZXBvcnRUb0F1ZGl0cyhjb3ZlcmFnZVJlc3VsdDogQ292ZXJhZ2VSZXN1bHQsIG9wdGlvbnM6IFBpY2s8RG9jQ292ZXJhZ2VQbHVnaW5Db25maWcsICdvbmx5QXVkaXRzJz4pOiBBdWRpdE91dHB1dHMge1xuXG4gICAgcmV0dXJuIE9iamVjdC5lbnRyaWVzKGNvdmVyYWdlUmVzdWx0KVxuICAgICAgICAuZmlsdGVyKChbdHlwZV0pID0+ICFvcHRpb25zLm9ubHlBdWRpdHM/Lmxlbmd0aCB8fCBvcHRpb25zLm9ubHlBdWRpdHMuaW5jbHVkZXMoYCR7dHlwZX0tY292ZXJhZ2VgKSlcbiAgICAgICAgLm1hcCgoW3R5cGUsIGl0ZW1zXSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgY292ZXJhZ2VUeXBlID0gdHlwZSBhcyBDb3ZlcmFnZVR5cGU7XG4gICAgICAgICAgICBjb25zdCBjb3ZlcmFnZSA9IGl0ZW1zLmNvdmVyYWdlO1xuXG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgIHNsdWc6IGAke2NvdmVyYWdlVHlwZX0tY292ZXJhZ2VgLFxuICAgICAgICAgICAgICAgIHZhbHVlOiBjb3ZlcmFnZSxcbiAgICAgICAgICAgICAgICBzY29yZTogY292ZXJhZ2UgLyAxMDAsXG4gICAgICAgICAgICAgICAgZGlzcGxheVZhbHVlOiBgJHtjb3ZlcmFnZX0gJWAsXG4gICAgICAgICAgICAgICAgZGV0YWlsczoge1xuICAgICAgICAgICAgICAgICAgICBpc3N1ZXM6IGl0ZW1zLmlzc3Vlcy5tYXAoKHsgZmlsZSwgbGluZSB9KSA9PiAoe1xuICAgICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogJ01pc3NpbmcgZG9jdW1lbnRhdGlvbicsXG4gICAgICAgICAgICAgICAgICAgICAgICBzb3VyY2U6IHsgZmlsZSwgcG9zaXRpb246IHsgc3RhcnRMaW5lOiBsaW5lIH0gfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHNldmVyaXR5OiAnd2FybmluZycsXG4gICAgICAgICAgICAgICAgICAgIH0pKSxcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgfTtcbiAgICAgICAgfSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRDb3ZlcmFnZVR5cGVGcm9tS2luZChraW5kOiBTeW50YXhLaW5kKTogQ292ZXJhZ2VUeXBlIHtcbiAgICBzd2l0Y2ggKGtpbmQpIHtcbiAgICAgICAgY2FzZSBTeW50YXhLaW5kLkNsYXNzRGVjbGFyYXRpb246XG4gICAgICAgICAgICByZXR1cm4gJ2NsYXNzZXMnO1xuICAgICAgICBjYXNlIFN5bnRheEtpbmQuTWV0aG9kRGVjbGFyYXRpb246XG4gICAgICAgICAgICByZXR1cm4gJ21ldGhvZHMnO1xuICAgICAgICBjYXNlIFN5bnRheEtpbmQuRnVuY3Rpb25EZWNsYXJhdGlvbjpcbiAgICAgICAgICAgIHJldHVybiAnZnVuY3Rpb25zJztcbiAgICAgICAgY2FzZSBTeW50YXhLaW5kLkludGVyZmFjZURlY2xhcmF0aW9uOlxuICAgICAgICAgICAgcmV0dXJuICdpbnRlcmZhY2VzJztcbiAgICAgICAgY2FzZSBTeW50YXhLaW5kLkVudW1EZWNsYXJhdGlvbjpcbiAgICAgICAgICAgIHJldHVybiAnZW51bXMnO1xuICAgICAgICBjYXNlIFN5bnRheEtpbmQuVmFyaWFibGVEZWNsYXJhdGlvbjpcbiAgICAgICAgICAgIHJldHVybiAndmFyaWFibGVzJztcbiAgICAgICAgY2FzZSBTeW50YXhLaW5kLlByb3BlcnR5RGVjbGFyYXRpb246XG4gICAgICAgICAgICByZXR1cm4gJ3Byb3BlcnRpZXMnO1xuICAgICAgICBjYXNlIFN5bnRheEtpbmQuVHlwZUFsaWFzRGVjbGFyYXRpb246XG4gICAgICAgICAgICByZXR1cm4gJ3R5cGVzJztcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgVW5zdXBwb3J0ZWQgc3ludGF4IGtpbmQ6ICR7a2luZH1gKTtcbiAgICB9XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWIvcnVubmVyL3V0aWxzLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1kb2MtY292ZXJhZ2Uvc3JjL2xpYi9ydW5uZXJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWRvYy1jb3ZlcmFnZS9zcmMvbGliL3J1bm5lci91dGlscy50c1wiO2ltcG9ydCB0eXBlIHsgQ292ZXJhZ2VSZXN1bHQsIENvdmVyYWdlVHlwZSwgVW5wcm9jZXNzZWRDb3ZlcmFnZVJlc3VsdCB9IGZyb20gXCIuLi9tb2RlbHNcIjtcblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUVtcHR5VW5wcm9jZXNzZWRDb3ZlcmFnZVJlcG9ydCgpOiBVbnByb2Nlc3NlZENvdmVyYWdlUmVzdWx0IHtcbiAgICByZXR1cm4ge1xuICAgICAgICBlbnVtczogeyBub2Rlc0NvdW50OiAwLCBpc3N1ZXM6IFtdIH0sXG4gICAgICAgIGludGVyZmFjZXM6IHsgbm9kZXNDb3VudDogMCwgaXNzdWVzOiBbXSB9LFxuICAgICAgICB0eXBlczogeyBub2Rlc0NvdW50OiAwLCBpc3N1ZXM6IFtdIH0sXG4gICAgICAgIGZ1bmN0aW9uczogeyBub2Rlc0NvdW50OiAwLCBpc3N1ZXM6IFtdIH0sXG4gICAgICAgIHZhcmlhYmxlczogeyBub2Rlc0NvdW50OiAwLCBpc3N1ZXM6IFtdIH0sXG4gICAgICAgIGNsYXNzZXM6IHsgbm9kZXNDb3VudDogMCwgaXNzdWVzOiBbXSB9LFxuICAgICAgICBtZXRob2RzOiB7IG5vZGVzQ291bnQ6IDAsIGlzc3VlczogW10gfSxcbiAgICAgICAgcHJvcGVydGllczogeyBub2Rlc0NvdW50OiAwLCBpc3N1ZXM6IFtdIH0sXG4gICAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gY2FsY3VsYXRlQ292ZXJhZ2UocmVzdWx0OiBVbnByb2Nlc3NlZENvdmVyYWdlUmVzdWx0KSB7XG4gICAgcmV0dXJuIE9iamVjdC5mcm9tRW50cmllcyhPYmplY3QuZW50cmllcyhyZXN1bHQpLm1hcCgoW2tleSwgdmFsdWVdKSA9PiB7XG4gICAgICAgIGNvbnN0IHR5cGUgPSBrZXkgYXMgQ292ZXJhZ2VUeXBlO1xuICAgICAgICByZXR1cm4gW3R5cGUsIHtcbiAgICAgICAgICAgIGNvdmVyYWdlOiB2YWx1ZS5ub2Rlc0NvdW50ID09PSAwID8gMTAwIDogKDEgLSB2YWx1ZS5pc3N1ZXMubGVuZ3RoIC8gdmFsdWUubm9kZXNDb3VudCkgKiAxMDAsXG4gICAgICAgICAgICBpc3N1ZXM6IHZhbHVlLmlzc3VlcyxcbiAgICAgICAgICAgIG5vZGVzQ291bnQ6IHZhbHVlLm5vZGVzQ291bnRcbiAgICAgICAgfV1cbiAgICB9KSkgYXMgQ292ZXJhZ2VSZXN1bHQ7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWIvZG9jLWNvdmVyYWdlLXBsdWdpbi50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWRvYy1jb3ZlcmFnZS9zcmMvbGliL2RvYy1jb3ZlcmFnZS1wbHVnaW4udHNcIjtpbXBvcnQgdHlwZSB7IEF1ZGl0T3V0cHV0cywgUGx1Z2luQ29uZmlnLCBSdW5uZXJGdW5jdGlvbiB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHtcbiAgdHlwZSBEb2NDb3ZlcmFnZVBsdWdpbkNvbmZpZyxcbiAgZG9jQ292ZXJhZ2VQbHVnaW5Db25maWdTY2hlbWEsXG59IGZyb20gJy4vY29uZmlnLmpzJztcbmltcG9ydCB7IGdyb3VwcywgUExVR0lOX1NMVUcgfSBmcm9tICcuL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgeyBwcm9jZXNzRG9jQ292ZXJhZ2UgfSBmcm9tICcuL3J1bm5lci9kb2MtcHJvY2Vzc2VyLmpzJztcbmltcG9ydCB7IGZpbHRlckF1ZGl0c0J5UGx1Z2luQ29uZmlnLCBmaWx0ZXJHcm91cHNCeU9ubHlBdWRpdHMsIHRyYXNmb3JtQ292ZXJhZ2VSZXBvcnRUb0F1ZGl0cyB9IGZyb20gJy4vdXRpbHMuanMnO1xuXG5jb25zdCBQTFVHSU5fVElUTEUgPSAnRG9jdW1lbnRhdGlvbiBjb3ZlcmFnZSc7XG5cbmNvbnN0IFBMVUdJTl9ERVNDUklQVElPTiA9ICdPZmZpY2lhbCBDb2RlIFB1c2hVcCBkb2N1bWVudGF0aW9uIGNvdmVyYWdlIHBsdWdpbi4nO1xuXG5jb25zdCBQTFVHSU5fRE9DU19VUkwgPSAnaHR0cHM6Ly93d3cubnBtanMuY29tL3BhY2thZ2UvQGNvZGUtcHVzaHVwL2RvYy1jb3ZlcmFnZS1wbHVnaW4vJztcblxuLyoqXG4gKiBJbnN0YW50aWF0ZXMgQ29kZSBQdXNoVXAgZG9jdW1lbnRhdGlvbiBjb3ZlcmFnZSBwbHVnaW4gZm9yIGNvcmUgY29uZmlnLlxuICpcbiAqIEBleGFtcGxlXG4gKiBpbXBvcnQgZG9jQ292ZXJhZ2VQbHVnaW4gZnJvbSAnQGNvZGUtcHVzaHVwL2RvYy1jb3ZlcmFnZS1wbHVnaW4nXG4gKlxuICogZXhwb3J0IGRlZmF1bHQge1xuICogICAvLyAuLi4gY29yZSBjb25maWcgLi4uXG4gKiAgIHBsdWdpbnM6IFtcbiAqICAgICAvLyAuLi4gb3RoZXIgcGx1Z2lucyAuLi5cbiAqICAgICBhd2FpdCBkb2NDb3ZlcmFnZVBsdWdpbih7XG4gKiAgICAgICBzb3VyY2VHbG9iOiAnc3JjJiM0NzsqKiYjNDc7Ki57dHMsdHN4fSdcbiAqICAgICB9KVxuICogICBdXG4gKiB9XG4gKlxuICogQHJldHVybnMgUGx1Z2luIGNvbmZpZ3VyYXRpb24uXG4gKi9cblxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZG9jQ292ZXJhZ2VQbHVnaW4oY29uZmlnOiBEb2NDb3ZlcmFnZVBsdWdpbkNvbmZpZyk6IFByb21pc2U8UGx1Z2luQ29uZmlnPiB7XG5cblxuICBjb25zdCBkb2NDb3ZlcmFnZUNvbmZpZyA9IGRvY0NvdmVyYWdlUGx1Z2luQ29uZmlnU2NoZW1hLnBhcnNlKGNvbmZpZyk7XG5cblxuICBjb25zdCBncm91cHNDID0gZmlsdGVyR3JvdXBzQnlPbmx5QXVkaXRzKGdyb3VwcywgZG9jQ292ZXJhZ2VDb25maWcpO1xuICBjb25zdCBhdWRpdHNDID0gZmlsdGVyQXVkaXRzQnlQbHVnaW5Db25maWcoZG9jQ292ZXJhZ2VDb25maWcpO1xuXG4gIHJldHVybiB7XG4gICAgc2x1ZzogUExVR0lOX1NMVUcsXG4gICAgdGl0bGU6IFBMVUdJTl9USVRMRSxcbiAgICBpY29uOiAnZm9sZGVyLXNyYycsXG4gICAgZGVzY3JpcHRpb246IFBMVUdJTl9ERVNDUklQVElPTixcbiAgICBkb2NzVXJsOiBQTFVHSU5fRE9DU19VUkwsXG4gICAgZ3JvdXBzOiBmaWx0ZXJHcm91cHNCeU9ubHlBdWRpdHMoZ3JvdXBzLCBkb2NDb3ZlcmFnZUNvbmZpZyksXG4gICAgYXVkaXRzOiBmaWx0ZXJBdWRpdHNCeVBsdWdpbkNvbmZpZyhkb2NDb3ZlcmFnZUNvbmZpZyksXG4gICAgcnVubmVyOiBjcmVhdGVSdW5uZXJGdW5jdGlvbihkb2NDb3ZlcmFnZUNvbmZpZyksXG4gIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVSdW5uZXJGdW5jdGlvbihjb25maWc6IERvY0NvdmVyYWdlUGx1Z2luQ29uZmlnKTogUnVubmVyRnVuY3Rpb24ge1xuICByZXR1cm4gKCk6IEF1ZGl0T3V0cHV0cyA9PiB7XG4gICAgY29uc3QgY292ZXJhZ2VSZXN1bHQgPSBwcm9jZXNzRG9jQ292ZXJhZ2UoY29uZmlnKVxuICAgIHJldHVybiB0cmFzZm9ybUNvdmVyYWdlUmVwb3J0VG9BdWRpdHMoY292ZXJhZ2VSZXN1bHQsIGNvbmZpZyk7XG4gIH07XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9pbmRleC50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyY1wiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9pbmRleC50c1wiO2ltcG9ydCB7IGRvY0NvdmVyYWdlUGx1Z2luIH0gZnJvbSAnLi9saWIvZG9jLWNvdmVyYWdlLXBsdWdpbi5qcyc7XG5cbmV4cG9ydCBkZWZhdWx0IGRvY0NvdmVyYWdlUGx1Z2luO1xuZXhwb3J0IHR5cGUgeyBEb2NDb3ZlcmFnZVBsdWdpbkNvbmZpZyB9IGZyb20gJy4vbGliL2NvbmZpZy5qcyc7XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvZXNsaW50LXBsdWdpbi50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL2VzbGludC1wbHVnaW4udHNcIjtpbXBvcnQgeyBjcmVhdGVSZXF1aXJlIH0gZnJvbSAnbm9kZTptb2R1bGUnO1xuaW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB7IGZpbGVVUkxUb1BhdGggfSBmcm9tICdub2RlOnVybCc7XG5pbXBvcnQgdHlwZSB7IFBsdWdpbkNvbmZpZyB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgdHlwZSBFU0xpbnRQbHVnaW5Db25maWcsIGVzbGludFBsdWdpbkNvbmZpZ1NjaGVtYSB9IGZyb20gJy4vY29uZmlnLmpzJztcbmltcG9ydCB7IGxpc3RBdWRpdHNBbmRHcm91cHMgfSBmcm9tICcuL21ldGEvaW5kZXguanMnO1xuaW1wb3J0IHsgY3JlYXRlUnVubmVyQ29uZmlnIH0gZnJvbSAnLi9ydW5uZXIvaW5kZXguanMnO1xuXG4vKipcbiAqIEluc3RhbnRpYXRlcyBDb2RlIFB1c2hVcCBFU0xpbnQgcGx1Z2luIGZvciB1c2UgaW4gY29yZSBjb25maWcuXG4gKlxuICogQGV4YW1wbGVcbiAqIGltcG9ydCBlc2xpbnRQbHVnaW4gZnJvbSAnQGNvZGUtcHVzaHVwL2VzbGludC1wbHVnaW4nXG4gKlxuICogZXhwb3J0IGRlZmF1bHQge1xuICogICAvLyAuLi4gY29yZSBjb25maWcgLi4uXG4gKiAgIHBsdWdpbnM6IFtcbiAqICAgICAvLyAuLi4gb3RoZXIgcGx1Z2lucyAuLi5cbiAqICAgICBhd2FpdCBlc2xpbnRQbHVnaW4oe1xuICogICAgICAgZXNsaW50cmM6ICcuZXNsaW50cmMuanNvbicsXG4gKiAgICAgICBwYXR0ZXJuczogWydzcmMnLCAndGVzdC8qLnNwZWMuanMnXVxuICogICAgIH0pXG4gKiAgIF1cbiAqIH1cbiAqXG4gKiBAcGFyYW0gY29uZmlnIENvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAqIEByZXR1cm5zIFBsdWdpbiBjb25maWd1cmF0aW9uIGFzIGEgcHJvbWlzZS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGVzbGludFBsdWdpbihcbiAgY29uZmlnOiBFU0xpbnRQbHVnaW5Db25maWcsXG4pOiBQcm9taXNlPFBsdWdpbkNvbmZpZz4ge1xuICBjb25zdCB0YXJnZXRzID0gZXNsaW50UGx1Z2luQ29uZmlnU2NoZW1hLnBhcnNlKGNvbmZpZyk7XG5cbiAgY29uc3QgeyBhdWRpdHMsIGdyb3VwcyB9ID0gYXdhaXQgbGlzdEF1ZGl0c0FuZEdyb3Vwcyh0YXJnZXRzKTtcblxuICBjb25zdCBydW5uZXJTY3JpcHRQYXRoID0gcGF0aC5qb2luKFxuICAgIGZpbGVVUkxUb1BhdGgocGF0aC5kaXJuYW1lKGltcG9ydC5tZXRhLnVybCkpLFxuICAgICcuLicsXG4gICAgJ2Jpbi5qcycsXG4gICk7XG5cbiAgY29uc3QgcGFja2FnZUpzb24gPSBjcmVhdGVSZXF1aXJlKGltcG9ydC5tZXRhLnVybCkoXG4gICAgJy4uLy4uL3BhY2thZ2UuanNvbicsXG4gICkgYXMgdHlwZW9mIGltcG9ydCgnLi4vLi4vcGFja2FnZS5qc29uJyk7XG5cbiAgcmV0dXJuIHtcbiAgICBzbHVnOiAnZXNsaW50JyxcbiAgICB0aXRsZTogJ0VTTGludCcsXG4gICAgaWNvbjogJ2VzbGludCcsXG4gICAgZGVzY3JpcHRpb246ICdPZmZpY2lhbCBDb2RlIFB1c2hVcCBFU0xpbnQgcGx1Z2luJyxcbiAgICBkb2NzVXJsOiAnaHR0cHM6Ly93d3cubnBtanMuY29tL3BhY2thZ2UvQGNvZGUtcHVzaHVwL2VzbGludC1wbHVnaW4nLFxuICAgIHBhY2thZ2VOYW1lOiBwYWNrYWdlSnNvbi5uYW1lLFxuICAgIHZlcnNpb246IHBhY2thZ2VKc29uLnZlcnNpb24sXG5cbiAgICBhdWRpdHMsXG4gICAgZ3JvdXBzLFxuXG4gICAgcnVubmVyOiBhd2FpdCBjcmVhdGVSdW5uZXJDb25maWcocnVubmVyU2NyaXB0UGF0aCwgYXVkaXRzLCB0YXJnZXRzKSxcbiAgfTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9jb25maWcudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9jb25maWcudHNcIjtpbXBvcnQgeyB6IH0gZnJvbSAnem9kJztcbmltcG9ydCB7IHRvQXJyYXkgfSBmcm9tICdAY29kZS1wdXNodXAvdXRpbHMnO1xuXG5jb25zdCBwYXR0ZXJuc1NjaGVtYSA9IHoudW5pb24oW3ouc3RyaW5nKCksIHouYXJyYXkoei5zdHJpbmcoKSkubWluKDEpXSwge1xuICBkZXNjcmlwdGlvbjpcbiAgICAnTGludCB0YXJnZXQgZmlsZXMuIE1heSBjb250YWluIGZpbGUgcGF0aHMsIGRpcmVjdG9yeSBwYXRocyBvciBnbG9iIHBhdHRlcm5zJyxcbn0pO1xuXG5jb25zdCBlc2xpbnRyY1NjaGVtYSA9IHouc3RyaW5nKHsgZGVzY3JpcHRpb246ICdQYXRoIHRvIEVTTGludCBjb25maWcgZmlsZScgfSk7XG5cbmNvbnN0IGVzbGludFRhcmdldE9iamVjdFNjaGVtYSA9IHoub2JqZWN0KHtcbiAgZXNsaW50cmM6IGVzbGludHJjU2NoZW1hLm9wdGlvbmFsKCksXG4gIHBhdHRlcm5zOiBwYXR0ZXJuc1NjaGVtYSxcbn0pO1xudHlwZSBFU0xpbnRUYXJnZXRPYmplY3QgPSB6LmluZmVyPHR5cGVvZiBlc2xpbnRUYXJnZXRPYmplY3RTY2hlbWE+O1xuXG5leHBvcnQgY29uc3QgZXNsaW50VGFyZ2V0U2NoZW1hID0gelxuICAudW5pb24oW3BhdHRlcm5zU2NoZW1hLCBlc2xpbnRUYXJnZXRPYmplY3RTY2hlbWFdKVxuICAudHJhbnNmb3JtKFxuICAgICh0YXJnZXQpOiBFU0xpbnRUYXJnZXRPYmplY3QgPT5cbiAgICAgIHR5cGVvZiB0YXJnZXQgPT09ICdzdHJpbmcnIHx8IEFycmF5LmlzQXJyYXkodGFyZ2V0KVxuICAgICAgICA/IHsgcGF0dGVybnM6IHRhcmdldCB9XG4gICAgICAgIDogdGFyZ2V0LFxuICApO1xuZXhwb3J0IHR5cGUgRVNMaW50VGFyZ2V0ID0gei5pbmZlcjx0eXBlb2YgZXNsaW50VGFyZ2V0U2NoZW1hPjtcblxuZXhwb3J0IGNvbnN0IGVzbGludFBsdWdpbkNvbmZpZ1NjaGVtYSA9IHpcbiAgLnVuaW9uKFtlc2xpbnRUYXJnZXRTY2hlbWEsIHouYXJyYXkoZXNsaW50VGFyZ2V0U2NoZW1hKS5taW4oMSldKVxuICAudHJhbnNmb3JtKHRvQXJyYXkpO1xuZXhwb3J0IHR5cGUgRVNMaW50UGx1Z2luQ29uZmlnID0gei5pbnB1dDx0eXBlb2YgZXNsaW50UGx1Z2luQ29uZmlnU2NoZW1hPjtcblxuZXhwb3J0IHR5cGUgRVNMaW50UGx1Z2luUnVubmVyQ29uZmlnID0ge1xuICB0YXJnZXRzOiBFU0xpbnRUYXJnZXRbXTtcbiAgc2x1Z3M6IHN0cmluZ1tdO1xufTtcbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhL2dyb3Vwcy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvbWV0YVwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvbWV0YS9ncm91cHMudHNcIjtpbXBvcnQgdHlwZSB7IFJ1bGUgfSBmcm9tICdlc2xpbnQnO1xuaW1wb3J0IHR5cGUgeyBHcm91cCwgR3JvdXBSZWYgfSBmcm9tICdAY29kZS1wdXNodXAvbW9kZWxzJztcbmltcG9ydCB7IG9iamVjdFRvS2V5cywgc2x1Z2lmeSB9IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgeyBydWxlVG9TbHVnIH0gZnJvbSAnLi9oYXNoLmpzJztcbmltcG9ydCB7IHR5cGUgUnVsZURhdGEsIHBhcnNlUnVsZUlkIH0gZnJvbSAnLi9wYXJzZS5qcyc7XG5cbnR5cGUgUnVsZVR5cGUgPSBOb25OdWxsYWJsZTxSdWxlLlJ1bGVNZXRhRGF0YVsndHlwZSddPjtcblxuLy8gZG9jcyBvbiBtZXRhLnR5cGU6IGh0dHBzOi8vZXNsaW50Lm9yZy9kb2NzL2xhdGVzdC9leHRlbmQvY3VzdG9tLXJ1bGVzI3J1bGUtc3RydWN0dXJlXG5jb25zdCB0eXBlR3JvdXBzOiBSZWNvcmQ8UnVsZVR5cGUsIE9taXQ8R3JvdXAsICdyZWZzJz4+ID0ge1xuICBwcm9ibGVtOiB7XG4gICAgc2x1ZzogJ3Byb2JsZW1zJyxcbiAgICB0aXRsZTogJ1Byb2JsZW1zJyxcbiAgICBkZXNjcmlwdGlvbjpcbiAgICAgICdDb2RlIHRoYXQgZWl0aGVyIHdpbGwgY2F1c2UgYW4gZXJyb3Igb3IgbWF5IGNhdXNlIGNvbmZ1c2luZyBiZWhhdmlvci4gRGV2ZWxvcGVycyBzaG91bGQgY29uc2lkZXIgdGhpcyBhIGhpZ2ggcHJpb3JpdHkgdG8gcmVzb2x2ZS4nLFxuICB9LFxuICBzdWdnZXN0aW9uOiB7XG4gICAgc2x1ZzogJ3N1Z2dlc3Rpb25zJyxcbiAgICB0aXRsZTogJ1N1Z2dlc3Rpb25zJyxcbiAgICBkZXNjcmlwdGlvbjpcbiAgICAgIFwiU29tZXRoaW5nIHRoYXQgY291bGQgYmUgZG9uZSBpbiBhIGJldHRlciB3YXkgYnV0IG5vIGVycm9ycyB3aWxsIG9jY3VyIGlmIHRoZSBjb2RlIGlzbid0IGNoYW5nZWQuXCIsXG4gIH0sXG4gIGxheW91dDoge1xuICAgIHNsdWc6ICdmb3JtYXR0aW5nJyxcbiAgICB0aXRsZTogJ0Zvcm1hdHRpbmcnLFxuICAgIGRlc2NyaXB0aW9uOlxuICAgICAgJ1ByaW1hcmlseSBhYm91dCB3aGl0ZXNwYWNlLCBzZW1pY29sb25zLCBjb21tYXMsIGFuZCBwYXJlbnRoZXNlcywgYWxsIHRoZSBwYXJ0cyBvZiB0aGUgcHJvZ3JhbSB0aGF0IGRldGVybWluZSBob3cgdGhlIGNvZGUgbG9va3MgcmF0aGVyIHRoYW4gaG93IGl0IGV4ZWN1dGVzLicsXG4gIH0sXG59O1xuXG5leHBvcnQgZnVuY3Rpb24gZ3JvdXBzRnJvbVJ1bGVUeXBlcyhydWxlczogUnVsZURhdGFbXSk6IEdyb3VwW10ge1xuICBjb25zdCBhbGxUeXBlcyA9IG9iamVjdFRvS2V5cyh0eXBlR3JvdXBzKTtcblxuICBjb25zdCBhdWRpdFNsdWdzTWFwID0gcnVsZXMucmVkdWNlPFBhcnRpYWw8UmVjb3JkPFJ1bGVUeXBlLCBzdHJpbmdbXT4+PihcbiAgICAoYWNjLCBydWxlKSA9PlxuICAgICAgcnVsZS5tZXRhLnR5cGUgPT0gbnVsbFxuICAgICAgICA/IGFjY1xuICAgICAgICA6IHtcbiAgICAgICAgICAgIC4uLmFjYyxcbiAgICAgICAgICAgIFtydWxlLm1ldGEudHlwZV06IFtcbiAgICAgICAgICAgICAgLi4uKGFjY1tydWxlLm1ldGEudHlwZV0gPz8gW10pLFxuICAgICAgICAgICAgICBydWxlVG9TbHVnKHJ1bGUpLFxuICAgICAgICAgICAgXSxcbiAgICAgICAgICB9LFxuICAgIHt9LFxuICApO1xuXG4gIHJldHVybiBhbGxUeXBlc1xuICAgIC5tYXAodHlwZSA9PiAoe1xuICAgICAgLi4udHlwZUdyb3Vwc1t0eXBlXSxcbiAgICAgIHJlZnM6XG4gICAgICAgIGF1ZGl0U2x1Z3NNYXBbdHlwZV0/Lm1hcCgoc2x1Zyk6IEdyb3VwUmVmID0+ICh7IHNsdWcsIHdlaWdodDogMSB9KSkgPz9cbiAgICAgICAgW10sXG4gICAgfSkpXG4gICAgLmZpbHRlcihncm91cCA9PiBncm91cC5yZWZzLmxlbmd0aCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBncm91cHNGcm9tUnVsZUNhdGVnb3JpZXMocnVsZXM6IFJ1bGVEYXRhW10pOiBHcm91cFtdIHtcbiAgY29uc3QgY2F0ZWdvcmllc01hcCA9IHJ1bGVzLnJlZHVjZTxSZWNvcmQ8c3RyaW5nLCBSZWNvcmQ8c3RyaW5nLCBzdHJpbmdbXT4+PihcbiAgICAoYWNjLCBydWxlKSA9PiB7XG4gICAgICAvLyBtZXRhLmRvY3MuY2F0ZWdvcnkgc3RpbGwgdXNlZCBieSBzb21lIHBvcHVsYXIgcGx1Z2lucyAoZS5nLiBpbXBvcnQsIHJlYWN0LCBmdW5jdGlvbmFsKVxuICAgICAgY29uc3QgY2F0ZWdvcnkgPSBydWxlLm1ldGEuZG9jcz8uY2F0ZWdvcnk7XG4gICAgICBpZiAoIWNhdGVnb3J5KSB7XG4gICAgICAgIHJldHVybiBhY2M7XG4gICAgICB9XG4gICAgICBjb25zdCB7IHBsdWdpbiA9ICcnIH0gPSBwYXJzZVJ1bGVJZChydWxlLmlkKTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIC4uLmFjYyxcbiAgICAgICAgW3BsdWdpbl06IHtcbiAgICAgICAgICAuLi5hY2NbcGx1Z2luXSxcbiAgICAgICAgICBbY2F0ZWdvcnldOiBbLi4uKGFjY1twbHVnaW5dPy5bY2F0ZWdvcnldID8/IFtdKSwgcnVsZVRvU2x1ZyhydWxlKV0sXG4gICAgICAgIH0sXG4gICAgICB9O1xuICAgIH0sXG4gICAge30sXG4gICk7XG5cbiAgY29uc3QgZ3JvdXBzID0gT2JqZWN0LmVudHJpZXMoY2F0ZWdvcmllc01hcCkuZmxhdE1hcCgoW3BsdWdpbiwgY2F0ZWdvcmllc10pID0+XG4gICAgT2JqZWN0LmVudHJpZXMoY2F0ZWdvcmllcykubWFwKFxuICAgICAgKFtjYXRlZ29yeSwgc2x1Z3NdKTogR3JvdXAgPT4gKHtcbiAgICAgICAgc2x1ZzogYCR7c2x1Z2lmeShwbHVnaW4pfS0ke3NsdWdpZnkoY2F0ZWdvcnkpfWAsXG4gICAgICAgIHRpdGxlOiBgJHtjYXRlZ29yeX0gKCR7cGx1Z2lufSlgLFxuICAgICAgICByZWZzOiBzbHVncy5tYXAoc2x1ZyA9PiAoeyBzbHVnLCB3ZWlnaHQ6IDEgfSkpLFxuICAgICAgfSksXG4gICAgKSxcbiAgKTtcblxuICByZXR1cm4gZ3JvdXBzLnRvU29ydGVkKChhLCBiKSA9PiBhLnNsdWcubG9jYWxlQ29tcGFyZShiLnNsdWcpKTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhL2hhc2gudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGFcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGEvaGFzaC50c1wiO2ltcG9ydCB7IGNyZWF0ZUhhc2ggfSBmcm9tICdub2RlOmNyeXB0byc7XG5pbXBvcnQgeyBzbHVnaWZ5IH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB7IHR5cGUgUnVsZURhdGEsIHJlc29sdmVSdWxlT3B0aW9ucyB9IGZyb20gJy4vcGFyc2UuanMnO1xuXG5leHBvcnQgZnVuY3Rpb24gcnVsZVRvU2x1ZyhydWxlOiBSdWxlRGF0YSk6IHN0cmluZyB7XG4gIHJldHVybiBydWxlSWRUb1NsdWcocnVsZS5pZCwgcmVzb2x2ZVJ1bGVPcHRpb25zKHJ1bGUpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJ1bGVJZFRvU2x1ZyhcbiAgcnVsZUlkOiBzdHJpbmcsXG4gIG9wdGlvbnM6IHVua25vd25bXSB8IHVuZGVmaW5lZCxcbik6IHN0cmluZyB7XG4gIGNvbnN0IHNsdWcgPSBzbHVnaWZ5KHJ1bGVJZCk7XG4gIGlmICghb3B0aW9ucz8ubGVuZ3RoKSB7XG4gICAgcmV0dXJuIHNsdWc7XG4gIH1cbiAgcmV0dXJuIGAke3NsdWd9LSR7anNvbkhhc2gob3B0aW9ucyl9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGpzb25IYXNoKGRhdGE6IHVua25vd24sIGJ5dGVzID0gOCk6IHN0cmluZyB7XG4gIHJldHVybiBjcmVhdGVIYXNoKCdzaGFrZTI1NicsIHsgb3V0cHV0TGVuZ3RoOiBieXRlcyB9KVxuICAgIC51cGRhdGUoSlNPTi5zdHJpbmdpZnkoZGF0YSkgfHwgJ251bGwnKVxuICAgIC5kaWdlc3QoJ2hleCcpO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGEvcGFyc2UudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGFcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGEvcGFyc2UudHNcIjtpbXBvcnQgdHlwZSB7IExpbnRlciwgUnVsZSB9IGZyb20gJ2VzbGludCc7XG5pbXBvcnQgeyB0b0FycmF5IH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcblxuZXhwb3J0IHR5cGUgUnVsZURhdGEgPSB7XG4gIGlkOiBzdHJpbmc7XG4gIG1ldGE6IFJ1bGUuUnVsZU1ldGFEYXRhO1xuICBvcHRpb25zOiB1bmtub3duW10gfCB1bmRlZmluZWQ7XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VSdWxlSWQocnVsZUlkOiBzdHJpbmcpOiB7IHBsdWdpbj86IHN0cmluZzsgbmFtZTogc3RyaW5nIH0ge1xuICBjb25zdCBpID0gcnVsZUlkLnN0YXJ0c1dpdGgoJ0AnKVxuICAgID8gcnVsZUlkLmxhc3RJbmRleE9mKCcvJylcbiAgICA6IHJ1bGVJZC5pbmRleE9mKCcvJyk7XG4gIGlmIChpID09PSAtMSkge1xuICAgIHJldHVybiB7IG5hbWU6IHJ1bGVJZCB9O1xuICB9XG4gIHJldHVybiB7XG4gICAgcGx1Z2luOiBydWxlSWQuc2xpY2UoMCwgaSksXG4gICAgbmFtZTogcnVsZUlkLnNsaWNlKGkgKyAxKSxcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGlzUnVsZU9mZihlbnRyeTogTGludGVyLlJ1bGVFbnRyeTx1bmtub3duW10+KTogYm9vbGVhbiB7XG4gIGNvbnN0IGxldmVsID0gQXJyYXkuaXNBcnJheShlbnRyeSkgPyBlbnRyeVswXSA6IGVudHJ5O1xuXG4gIHN3aXRjaCAobGV2ZWwpIHtcbiAgICBjYXNlIDA6XG4gICAgY2FzZSAnb2ZmJzpcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIGNhc2UgMTpcbiAgICBjYXNlIDI6XG4gICAgY2FzZSAnd2Fybic6XG4gICAgY2FzZSAnZXJyb3InOlxuICAgICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBvcHRpb25zRnJvbVJ1bGVFbnRyeShcbiAgZW50cnk6IExpbnRlci5SdWxlRW50cnk8dW5rbm93bltdPixcbik6IHVua25vd25bXSB7XG4gIHJldHVybiB0b0FycmF5KGVudHJ5KS5zbGljZSgxKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVSdWxlT3B0aW9ucyhydWxlOiBSdWxlRGF0YSk6IHVua25vd25bXSB8IHVuZGVmaW5lZCB7XG4gIGlmIChydWxlLm9wdGlvbnM/Lmxlbmd0aCkge1xuICAgIHJldHVybiBydWxlLm9wdGlvbnM7XG4gIH1cbiAgcmV0dXJuIHJ1bGUubWV0YS5kZWZhdWx0T3B0aW9ucztcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhL3ZlcnNpb25zL2ZsYXQudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGEvdmVyc2lvbnNcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGEvdmVyc2lvbnMvZmxhdC50c1wiO2ltcG9ydCB0eXBlIHsgTGludGVyLCBSdWxlIH0gZnJvbSAnZXNsaW50JztcbmltcG9ydCB7IGJ1aWx0aW5SdWxlcyB9IGZyb20gJ2VzbGludC91c2UtYXQteW91ci1vd24tcmlzayc7XG5pbXBvcnQgcGF0aCBmcm9tICdub2RlOnBhdGgnO1xuaW1wb3J0IHsgcGF0aFRvRmlsZVVSTCB9IGZyb20gJ25vZGU6dXJsJztcbmltcG9ydCB7IGV4aXN0cywgZmluZE5lYXJlc3RGaWxlLCB0b0FycmF5LCB1aSB9IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgdHlwZSB7IEVTTGludFRhcmdldCB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XG5pbXBvcnQgeyBqc29uSGFzaCB9IGZyb20gJy4uL2hhc2guanMnO1xuaW1wb3J0IHtcbiAgdHlwZSBSdWxlRGF0YSxcbiAgaXNSdWxlT2ZmLFxuICBvcHRpb25zRnJvbVJ1bGVFbnRyeSxcbiAgcGFyc2VSdWxlSWQsXG59IGZyb20gJy4uL3BhcnNlLmpzJztcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvYWRSdWxlc0ZvckZsYXRDb25maWcoe1xuICBlc2xpbnRyYyxcbn06IFBpY2s8RVNMaW50VGFyZ2V0LCAnZXNsaW50cmMnPik6IFByb21pc2U8UnVsZURhdGFbXT4ge1xuICBjb25zdCBjb25maWcgPSBlc2xpbnRyY1xuICAgID8gYXdhaXQgbG9hZENvbmZpZ0J5UGF0aChlc2xpbnRyYylcbiAgICA6IGF3YWl0IGxvYWRDb25maWdCeURlZmF1bHRMb2NhdGlvbigpO1xuICBjb25zdCBjb25maWdzID0gdG9BcnJheShjb25maWcpO1xuXG4gIGNvbnN0IHJ1bGVzID0gZmluZEVuYWJsZWRSdWxlc1dpdGhPcHRpb25zKGNvbmZpZ3MpO1xuICByZXR1cm4gcnVsZXNcbiAgICAubWFwKHJ1bGUgPT4ge1xuICAgICAgY29uc3QgbWV0YSA9IGZpbmRSdWxlTWV0YShydWxlLmlkLCBjb25maWdzKTtcbiAgICAgIGlmICghbWV0YSkge1xuICAgICAgICB1aSgpLmxvZ2dlci53YXJuaW5nKGBDYW5ub3QgZmluZCBtZXRhZGF0YSBmb3IgcnVsZSAke3J1bGUuaWR9YCk7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHsgLi4ucnVsZSwgbWV0YSB9O1xuICAgIH0pXG4gICAgLmZpbHRlcihleGlzdHMpO1xufVxuXG50eXBlIEZsYXRDb25maWcgPSBMaW50ZXIuQ29uZmlnIHwgTGludGVyLkNvbmZpZ1tdO1xuXG5hc3luYyBmdW5jdGlvbiBsb2FkQ29uZmlnQnlEZWZhdWx0TG9jYXRpb24oKTogUHJvbWlzZTxGbGF0Q29uZmlnPiB7XG4gIGNvbnN0IGZsYXRDb25maWdGaWxlTmFtZXMgPSBbXG4gICAgJ2VzbGludC5jb25maWcuanMnLFxuICAgICdlc2xpbnQuY29uZmlnLm1qcycsXG4gICAgJ2VzbGludC5jb25maWcuY2pzJyxcbiAgXTtcbiAgY29uc3QgY29uZmlnUGF0aCA9IGF3YWl0IGZpbmROZWFyZXN0RmlsZShmbGF0Q29uZmlnRmlsZU5hbWVzKTtcbiAgaWYgKGNvbmZpZ1BhdGgpIHtcbiAgICByZXR1cm4gbG9hZENvbmZpZ0J5UGF0aChjb25maWdQYXRoKTtcbiAgfVxuICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgW1xuICAgICAgYEVTTGludCBjb25maWcgZmlsZSBub3QgZm91bmQgLSBleHBlY3RlZCAke2ZsYXRDb25maWdGaWxlTmFtZXMuam9pbignLycpfSBpbiAke3Byb2Nlc3MuY3dkKCl9IG9yIHNvbWUgcGFyZW50IGRpcmVjdG9yeWAsXG4gICAgICAnSWYgeW91ciBFU0xpbnQgY29uZmlnIGlzIGluIGEgbm9uLXN0YW5kYXJkIGxvY2F0aW9uLCB1c2UgdGhlIGBlc2xpbnRyY2AgcGFyYW1ldGVyIHRvIHNwZWNpZnkgdGhlIHBhdGguJyxcbiAgICBdLmpvaW4oJ1xcbicpLFxuICApO1xufVxuXG5hc3luYyBmdW5jdGlvbiBsb2FkQ29uZmlnQnlQYXRoKGNvbmZpZ1BhdGg6IHN0cmluZyk6IFByb21pc2U8RmxhdENvbmZpZz4ge1xuICBjb25zdCBhYnNvbHV0ZVBhdGggPSBwYXRoLmlzQWJzb2x1dGUoY29uZmlnUGF0aClcbiAgICA/IGNvbmZpZ1BhdGhcbiAgICA6IHBhdGguam9pbihwcm9jZXNzLmN3ZCgpLCBjb25maWdQYXRoKTtcbiAgY29uc3QgdXJsID0gcGF0aFRvRmlsZVVSTChhYnNvbHV0ZVBhdGgpLnRvU3RyaW5nKCk7XG4gIGNvbnN0IG1vZCA9IChhd2FpdCBpbXBvcnQodXJsKSkgYXMgRmxhdENvbmZpZyB8IHsgZGVmYXVsdDogRmxhdENvbmZpZyB9O1xuICByZXR1cm4gJ2RlZmF1bHQnIGluIG1vZCA/IG1vZC5kZWZhdWx0IDogbW9kO1xufVxuXG5mdW5jdGlvbiBmaW5kRW5hYmxlZFJ1bGVzV2l0aE9wdGlvbnMoXG4gIGNvbmZpZ3M6IExpbnRlci5Db25maWdbXSxcbik6IE9taXQ8UnVsZURhdGEsICdtZXRhJz5bXSB7XG4gIGNvbnN0IGVuYWJsZWRSdWxlcyA9IGNvbmZpZ3NcbiAgICAuZmxhdE1hcCgoeyBydWxlcyB9KSA9PiBPYmplY3QuZW50cmllcyhydWxlcyA/PyB7fSkpXG4gICAgLmZpbHRlcigoWywgZW50cnldKSA9PiBlbnRyeSAhPSBudWxsICYmICFpc1J1bGVPZmYoZW50cnkpKVxuICAgIC5tYXAoKFtpZCwgZW50cnldKSA9PiAoe1xuICAgICAgaWQsXG4gICAgICBvcHRpb25zOiBlbnRyeSA/IG9wdGlvbnNGcm9tUnVsZUVudHJ5KGVudHJ5KSA6IFtdLFxuICAgIH0pKTtcbiAgY29uc3QgdW5pcXVlUnVsZXNNYXAgPSBuZXcgTWFwKFxuICAgIGVuYWJsZWRSdWxlcy5tYXAoKHsgaWQsIG9wdGlvbnMgfSkgPT4gW1xuICAgICAgYCR7aWR9Ojoke2pzb25IYXNoKG9wdGlvbnMpfWAsXG4gICAgICB7IGlkLCBvcHRpb25zIH0sXG4gICAgXSksXG4gICk7XG4gIHJldHVybiBbLi4udW5pcXVlUnVsZXNNYXAudmFsdWVzKCldO1xufVxuXG5mdW5jdGlvbiBmaW5kUnVsZU1ldGEoXG4gIHJ1bGVJZDogc3RyaW5nLFxuICBjb25maWdzOiBMaW50ZXIuQ29uZmlnW10sXG4pOiBSdWxlLlJ1bGVNZXRhRGF0YSB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IHsgcGx1Z2luLCBuYW1lIH0gPSBwYXJzZVJ1bGVJZChydWxlSWQpO1xuICBpZiAoIXBsdWdpbikge1xuICAgIHJldHVybiBmaW5kQnVpbHRpblJ1bGVNZXRhKG5hbWUpO1xuICB9XG4gIHJldHVybiBmaW5kUGx1Z2luUnVsZU1ldGEocGx1Z2luLCBuYW1lLCBjb25maWdzKTtcbn1cblxuZnVuY3Rpb24gZmluZEJ1aWx0aW5SdWxlTWV0YShuYW1lOiBzdHJpbmcpOiBSdWxlLlJ1bGVNZXRhRGF0YSB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IHJ1bGUgPSBidWlsdGluUnVsZXMuZ2V0KG5hbWUpO1xuICByZXR1cm4gcnVsZT8ubWV0YTtcbn1cblxuZnVuY3Rpb24gZmluZFBsdWdpblJ1bGVNZXRhKFxuICBwbHVnaW46IHN0cmluZyxcbiAgbmFtZTogc3RyaW5nLFxuICBjb25maWdzOiBMaW50ZXIuQ29uZmlnW10sXG4pOiBSdWxlLlJ1bGVNZXRhRGF0YSB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IGNvbmZpZyA9IGNvbmZpZ3MuZmluZCgoeyBwbHVnaW5zID0ge30gfSkgPT4gcGx1Z2luIGluIHBsdWdpbnMpO1xuICBjb25zdCBydWxlID0gY29uZmlnPy5wbHVnaW5zPy5bcGx1Z2luXT8ucnVsZXM/LltuYW1lXTtcblxuICBpZiAodHlwZW9mIHJ1bGUgPT09ICdmdW5jdGlvbicpIHtcbiAgICB1aSgpLmxvZ2dlci53YXJuaW5nKFxuICAgICAgYENhbm5vdCBwYXJzZSBtZXRhZGF0YSBmb3IgcnVsZSAke3BsdWdpbn0vJHtuYW1lfSwgcGx1Z2luIHJlZ2lzdGVycyBpdCBhcyBhIGZ1bmN0aW9uYCxcbiAgICApO1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cblxuICByZXR1cm4gcnVsZT8ubWV0YTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhL3ZlcnNpb25zL2xlZ2FjeS50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvbWV0YS92ZXJzaW9uc1wiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvbWV0YS92ZXJzaW9ucy9sZWdhY3kudHNcIjtpbXBvcnQgdHlwZSB7IEVTTGludCwgTGludGVyIH0gZnJvbSAnZXNsaW50JztcbmltcG9ydCB7IGRpc3RpbmN0LCBleGlzdHMsIHRvQXJyYXksIHVpIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB0eXBlIHsgRVNMaW50VGFyZ2V0IH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcbmltcG9ydCB7IHNldHVwRVNMaW50IH0gZnJvbSAnLi4vLi4vc2V0dXAuanMnO1xuaW1wb3J0IHsgdHlwZSBSdWxlRGF0YSwgaXNSdWxlT2ZmLCBvcHRpb25zRnJvbVJ1bGVFbnRyeSB9IGZyb20gJy4uL3BhcnNlLmpzJztcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvYWRSdWxlc0ZvckxlZ2FjeUNvbmZpZyh7XG4gIGVzbGludHJjLFxuICBwYXR0ZXJucyxcbn06IEVTTGludFRhcmdldCk6IFByb21pc2U8UnVsZURhdGFbXT4ge1xuICBjb25zdCBlc2xpbnQgPSBhd2FpdCBzZXR1cEVTTGludChlc2xpbnRyYyk7XG5cbiAgY29uc3QgY29uZmlncyA9IGF3YWl0IHRvQXJyYXkocGF0dGVybnMpLnJlZHVjZShcbiAgICBhc3luYyAoYWNjLCBwYXR0ZXJuKSA9PiBbXG4gICAgICAuLi4oYXdhaXQgYWNjKSxcbiAgICAgIChhd2FpdCBlc2xpbnQuY2FsY3VsYXRlQ29uZmlnRm9yRmlsZShwYXR0ZXJuKSkgYXMgTGludGVyLkxlZ2FjeUNvbmZpZyxcbiAgICBdLFxuICAgIFByb21pc2UucmVzb2x2ZTxMaW50ZXIuTGVnYWN5Q29uZmlnW10+KFtdKSxcbiAgKTtcblxuICBjb25zdCBydWxlc0lkcyA9IGRpc3RpbmN0KFxuICAgIGNvbmZpZ3MuZmxhdE1hcChjb25maWcgPT4gT2JqZWN0LmtleXMoY29uZmlnLnJ1bGVzID8/IHt9KSksXG4gICk7XG4gIGNvbnN0IHJ1bGVzTWV0YSA9IGVzbGludC5nZXRSdWxlc01ldGFGb3JSZXN1bHRzKFtcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L2NvbnNpc3RlbnQtdHlwZS1hc3NlcnRpb25zXG4gICAge1xuICAgICAgbWVzc2FnZXM6IHJ1bGVzSWRzLm1hcChydWxlSWQgPT4gKHsgcnVsZUlkIH0pKSxcbiAgICAgIHN1cHByZXNzZWRNZXNzYWdlczogW10gYXMgTGludGVyLlN1cHByZXNzZWRMaW50TWVzc2FnZVtdLFxuICAgIH0gYXMgRVNMaW50LkxpbnRSZXN1bHQsXG4gIF0pO1xuXG4gIHJldHVybiBjb25maWdzXG4gICAgLmZsYXRNYXAoY29uZmlnID0+IE9iamVjdC5lbnRyaWVzKGNvbmZpZy5ydWxlcyA/PyB7fSkpXG4gICAgLm1hcCgoW2lkLCBlbnRyeV0pOiBSdWxlRGF0YSB8IG51bGwgPT4ge1xuICAgICAgaWYgKGVudHJ5ID09IG51bGwgfHwgaXNSdWxlT2ZmKGVudHJ5KSkge1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHJ1bGVNZXRhID0gcnVsZXNNZXRhW2lkXTtcbiAgICAgIGlmICghcnVsZU1ldGEpIHtcbiAgICAgICAgdWkoKS5sb2dnZXIud2FybmluZyhgTWV0YWRhdGEgbm90IGZvdW5kIGZvciBFU0xpbnQgcnVsZSAke2lkfWApO1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH1cbiAgICAgIC8vIGlnbm9yaW5nIG1ldGEuZGVmYXVsdE9wdGlvbnMgdG8gbWF0Y2ggbGVnYWN5IGNvbmZpZyBoYW5kbGluZyBpbiBjYWxjdWxhdGVDb25maWdGb3JGaWxlXG4gICAgICBjb25zdCB7IGRlZmF1bHRPcHRpb25zOiBfLCAuLi5tZXRhIH0gPSBydWxlTWV0YTtcbiAgICAgIGNvbnN0IG9wdGlvbnMgPSBvcHRpb25zRnJvbVJ1bGVFbnRyeShlbnRyeSk7XG4gICAgICByZXR1cm4geyBpZCwgbWV0YSwgb3B0aW9ucyB9O1xuICAgIH0pXG4gICAgLmZpbHRlcihleGlzdHMpO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL3NldHVwLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYlwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvc2V0dXAudHNcIjtpbXBvcnQgeyBFU0xpbnQgfSBmcm9tICdlc2xpbnQnO1xuaW1wb3J0IHR5cGUgeyBFU0xpbnRUYXJnZXQgfSBmcm9tICcuL2NvbmZpZy5qcyc7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzZXR1cEVTTGludChlc2xpbnRyYzogRVNMaW50VGFyZ2V0Wydlc2xpbnRyYyddKSB7XG4gIGNvbnN0IGVzbGludENvbnN0cnVjdG9yID0gYXdhaXQgbG9hZEVTTGludCgpO1xuICByZXR1cm4gbmV3IGVzbGludENvbnN0cnVjdG9yKHtcbiAgICBvdmVycmlkZUNvbmZpZ0ZpbGU6IGVzbGludHJjLFxuICAgIGVycm9yT25Vbm1hdGNoZWRQYXR0ZXJuOiBmYWxzZSxcbiAgfSk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGxvYWRFU0xpbnQoKSB7XG4gIGNvbnN0IGVzbGludCA9IGF3YWl0IGltcG9ydCgnZXNsaW50Jyk7XG4gIC8vIGxvYWRFU0xpbnQgYWRkZWQgdG8gcHVibGljIEFQSSBpbiB2OSwgc2VsZWN0cyBFU0xpbnQgb3IgTGVnYWN5RVNMaW50IGJhc2VkIG9uIGVudmlyb25tZW50XG4gIGlmICgnbG9hZEVTTGludCcgaW4gZXNsaW50ICYmIHR5cGVvZiBlc2xpbnQubG9hZEVTTGludCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgIHJldHVybiAoYXdhaXQgZXNsaW50LmxvYWRFU0xpbnQoKSkgYXMgdHlwZW9mIEVTTGludDtcbiAgfVxuICByZXR1cm4gRVNMaW50O1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGEvdmVyc2lvbnMvZGV0ZWN0LnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhL3ZlcnNpb25zXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhL3ZlcnNpb25zL2RldGVjdC50c1wiO2ltcG9ydCB7IEVTTGludCB9IGZyb20gJ2VzbGludCc7XG5pbXBvcnQgeyBmaWxlRXhpc3RzIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB0eXBlIHsgQ29uZmlnRm9ybWF0IH0gZnJvbSAnLi9mb3JtYXRzLmpzJztcblxuLy8gcmVsZXZhbnQgRVNMaW50IGRvY3M6XG4vLyAtIGh0dHBzOi8vZXNsaW50Lm9yZy9kb2NzL2xhdGVzdC91c2UvY29uZmlndXJlL2NvbmZpZ3VyYXRpb24tZmlsZXNcbi8vIC0gaHR0cHM6Ly9lc2xpbnQub3JnL2RvY3MvbGF0ZXN0L3VzZS9jb25maWd1cmUvY29uZmlndXJhdGlvbi1maWxlcy1kZXByZWNhdGVkXG4vLyAtIGh0dHBzOi8vZXNsaW50Lm9yZy9kb2NzL3Y4LngvdXNlL2NvbmZpZ3VyZS9jb25maWd1cmF0aW9uLWZpbGVzLW5ld1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZGV0ZWN0Q29uZmlnVmVyc2lvbigpOiBQcm9taXNlPENvbmZpZ0Zvcm1hdD4ge1xuICBpZiAocHJvY2Vzcy5lbnZbJ0VTTElOVF9VU0VfRkxBVF9DT05GSUcnXSA9PT0gJ3RydWUnKSB7XG4gICAgcmV0dXJuICdmbGF0JztcbiAgfVxuICBpZiAocHJvY2Vzcy5lbnZbJ0VTTElOVF9VU0VfRkxBVF9DT05GSUcnXSA9PT0gJ2ZhbHNlJykge1xuICAgIHJldHVybiAnbGVnYWN5JztcbiAgfVxuICBpZiAoRVNMaW50LnZlcnNpb24uc3RhcnRzV2l0aCgnOC4nKSkge1xuICAgIGlmIChhd2FpdCBmaWxlRXhpc3RzKCdlc2xpbnQuY29uZmlnLmpzJykpIHtcbiAgICAgIHJldHVybiAnZmxhdCc7XG4gICAgfVxuICAgIHJldHVybiAnbGVnYWN5JztcbiAgfVxuICByZXR1cm4gJ2ZsYXQnO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL21ldGEvdHJhbnNmb3JtLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9tZXRhL3RyYW5zZm9ybS50c1wiO2ltcG9ydCB0eXBlIHsgQXVkaXQgfSBmcm9tICdAY29kZS1wdXNodXAvbW9kZWxzJztcbmltcG9ydCB7IHRydW5jYXRlRGVzY3JpcHRpb24sIHRydW5jYXRlVGl0bGUgfSBmcm9tICdAY29kZS1wdXNodXAvdXRpbHMnO1xuaW1wb3J0IHsgcnVsZVRvU2x1ZyB9IGZyb20gJy4vaGFzaC5qcyc7XG5pbXBvcnQgdHlwZSB7IFJ1bGVEYXRhIH0gZnJvbSAnLi9wYXJzZS5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBydWxlVG9BdWRpdChydWxlOiBSdWxlRGF0YSk6IEF1ZGl0IHtcbiAgY29uc3QgbmFtZSA9IHJ1bGUuaWQuc3BsaXQoJy8nKS5hdCgtMSkgPz8gcnVsZS5pZDtcbiAgY29uc3QgcGx1Z2luID1cbiAgICBuYW1lID09PSBydWxlLmlkID8gbnVsbCA6IHJ1bGUuaWQuc2xpY2UoMCwgcnVsZS5pZC5sYXN0SW5kZXhPZignLycpKTtcbiAgY29uc3QgcGx1Z2luQ29udGV4dCA9IHBsdWdpbiA/IGAsIGZyb20gXyR7cGx1Z2lufV8gcGx1Z2luYCA6ICcnO1xuXG4gIGNvbnN0IGxpbmVzOiBzdHJpbmdbXSA9IFtcbiAgICBgRVNMaW50IHJ1bGUgKioke25hbWV9Kioke3BsdWdpbkNvbnRleHR9LmAsXG4gICAgLi4uKHJ1bGUub3B0aW9ucz8ubGVuZ3RoID8gWydDdXN0b20gb3B0aW9uczonXSA6IFtdKSxcbiAgICAuLi4ocnVsZS5vcHRpb25zPy5tYXAob3B0aW9uID0+XG4gICAgICBbJ2BgYGpzb24nLCBKU09OLnN0cmluZ2lmeShvcHRpb24sIG51bGwsIDIpLCAnYGBgJ10uam9pbignXFxuJyksXG4gICAgKSA/PyBbXSksXG4gIF07XG5cbiAgcmV0dXJuIHtcbiAgICBzbHVnOiBydWxlVG9TbHVnKHJ1bGUpLFxuICAgIHRpdGxlOiB0cnVuY2F0ZVRpdGxlKHJ1bGUubWV0YS5kb2NzPy5kZXNjcmlwdGlvbiA/PyBuYW1lKSxcbiAgICBkZXNjcmlwdGlvbjogdHJ1bmNhdGVEZXNjcmlwdGlvbihsaW5lcy5qb2luKCdcXG5cXG4nKSksXG4gICAgLi4uKHJ1bGUubWV0YS5kb2NzPy51cmwgJiYge1xuICAgICAgZG9jc1VybDogcnVsZS5tZXRhLmRvY3MudXJsLFxuICAgIH0pLFxuICB9O1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL3J1bm5lci9pbmRleC50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvcnVubmVyXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9ydW5uZXIvaW5kZXgudHNcIjtpbXBvcnQgeyB3cml0ZUZpbGUgfSBmcm9tICdub2RlOmZzL3Byb21pc2VzJztcbmltcG9ydCBwYXRoIGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0LCBBdWRpdE91dHB1dCwgUnVubmVyQ29uZmlnIH0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5pbXBvcnQge1xuICBlbnN1cmVEaXJlY3RvcnlFeGlzdHMsXG4gIGZpbGVQYXRoVG9DbGlBcmcsXG4gIHBsdWdpbldvcmtEaXIsXG4gIHJlYWRKc29uRmlsZSxcbn0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB0eXBlIHsgRVNMaW50UGx1Z2luUnVubmVyQ29uZmlnLCBFU0xpbnRUYXJnZXQgfSBmcm9tICcuLi9jb25maWcuanMnO1xuaW1wb3J0IHsgbGludCB9IGZyb20gJy4vbGludC5qcyc7XG5pbXBvcnQgeyBsaW50UmVzdWx0c1RvQXVkaXRzLCBtZXJnZUxpbnRlck91dHB1dHMgfSBmcm9tICcuL3RyYW5zZm9ybS5qcyc7XG5pbXBvcnQgdHlwZSB7IExpbnRlck91dHB1dCB9IGZyb20gJy4vdHlwZXMuanMnO1xuXG5leHBvcnQgY29uc3QgV09SS0RJUiA9IHBsdWdpbldvcmtEaXIoJ2VzbGludCcpO1xuZXhwb3J0IGNvbnN0IFJVTk5FUl9PVVRQVVRfUEFUSCA9IHBhdGguam9pbihXT1JLRElSLCAncnVubmVyLW91dHB1dC5qc29uJyk7XG5leHBvcnQgY29uc3QgUExVR0lOX0NPTkZJR19QQVRIID0gcGF0aC5qb2luKFxuICBwcm9jZXNzLmN3ZCgpLFxuICBXT1JLRElSLFxuICAncGx1Z2luLWNvbmZpZy5qc29uJyxcbik7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBleGVjdXRlUnVubmVyKCk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCB7IHNsdWdzLCB0YXJnZXRzIH0gPVxuICAgIGF3YWl0IHJlYWRKc29uRmlsZTxFU0xpbnRQbHVnaW5SdW5uZXJDb25maWc+KFBMVUdJTl9DT05GSUdfUEFUSCk7XG5cbiAgY29uc3QgbGludGVyT3V0cHV0cyA9IGF3YWl0IHRhcmdldHMucmVkdWNlKFxuICAgIGFzeW5jIChhY2MsIHRhcmdldCkgPT4gWy4uLihhd2FpdCBhY2MpLCBhd2FpdCBsaW50KHRhcmdldCldLFxuICAgIFByb21pc2UucmVzb2x2ZTxMaW50ZXJPdXRwdXRbXT4oW10pLFxuICApO1xuICBjb25zdCBsaW50UmVzdWx0cyA9IG1lcmdlTGludGVyT3V0cHV0cyhsaW50ZXJPdXRwdXRzKTtcbiAgY29uc3QgZmFpbGVkQXVkaXRzID0gbGludFJlc3VsdHNUb0F1ZGl0cyhsaW50UmVzdWx0cyk7XG5cbiAgY29uc3QgYXVkaXRzID0gc2x1Z3MubWFwKFxuICAgIChzbHVnKTogQXVkaXRPdXRwdXQgPT5cbiAgICAgIGZhaWxlZEF1ZGl0cy5maW5kKGF1ZGl0ID0+IGF1ZGl0LnNsdWcgPT09IHNsdWcpID8/IHtcbiAgICAgICAgc2x1ZyxcbiAgICAgICAgc2NvcmU6IDEsXG4gICAgICAgIHZhbHVlOiAwLFxuICAgICAgICBkaXNwbGF5VmFsdWU6ICdwYXNzZWQnLFxuICAgICAgICBkZXRhaWxzOiB7IGlzc3VlczogW10gfSxcbiAgICAgIH0sXG4gICk7XG5cbiAgYXdhaXQgZW5zdXJlRGlyZWN0b3J5RXhpc3RzKHBhdGguZGlybmFtZShSVU5ORVJfT1VUUFVUX1BBVEgpKTtcbiAgYXdhaXQgd3JpdGVGaWxlKFJVTk5FUl9PVVRQVVRfUEFUSCwgSlNPTi5zdHJpbmdpZnkoYXVkaXRzKSk7XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjcmVhdGVSdW5uZXJDb25maWcoXG4gIHNjcmlwdFBhdGg6IHN0cmluZyxcbiAgYXVkaXRzOiBBdWRpdFtdLFxuICB0YXJnZXRzOiBFU0xpbnRUYXJnZXRbXSxcbik6IFByb21pc2U8UnVubmVyQ29uZmlnPiB7XG4gIGNvbnN0IGNvbmZpZzogRVNMaW50UGx1Z2luUnVubmVyQ29uZmlnID0ge1xuICAgIHRhcmdldHMsXG4gICAgc2x1Z3M6IGF1ZGl0cy5tYXAoYXVkaXQgPT4gYXVkaXQuc2x1ZyksXG4gIH07XG4gIGF3YWl0IGVuc3VyZURpcmVjdG9yeUV4aXN0cyhwYXRoLmRpcm5hbWUoUExVR0lOX0NPTkZJR19QQVRIKSk7XG4gIGF3YWl0IHdyaXRlRmlsZShQTFVHSU5fQ09ORklHX1BBVEgsIEpTT04uc3RyaW5naWZ5KGNvbmZpZykpO1xuXG4gIHJldHVybiB7XG4gICAgY29tbWFuZDogJ25vZGUnLFxuICAgIGFyZ3M6IFtmaWxlUGF0aFRvQ2xpQXJnKHNjcmlwdFBhdGgpXSxcbiAgICBvdXRwdXRGaWxlOiBSVU5ORVJfT1VUUFVUX1BBVEgsXG4gIH07XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvcnVubmVyL2xpbnQudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL3J1bm5lclwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvcnVubmVyL2xpbnQudHNcIjtpbXBvcnQgdHlwZSB7IEVTTGludCwgTGludGVyIH0gZnJvbSAnZXNsaW50JztcbmltcG9ydCB7IHBsYXRmb3JtIH0gZnJvbSAnbm9kZTpvcyc7XG5pbXBvcnQge1xuICBkaXN0aW5jdCxcbiAgZXhlY3V0ZVByb2Nlc3MsXG4gIGZpbGVQYXRoVG9DbGlBcmcsXG4gIHRvQXJyYXksXG59IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgdHlwZSB7IEVTTGludFRhcmdldCB9IGZyb20gJy4uL2NvbmZpZy5qcyc7XG5pbXBvcnQgeyBzZXR1cEVTTGludCB9IGZyb20gJy4uL3NldHVwLmpzJztcbmltcG9ydCB0eXBlIHsgTGludGVyT3V0cHV0LCBSdWxlT3B0aW9uc1BlckZpbGUgfSBmcm9tICcuL3R5cGVzLmpzJztcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxpbnQoe1xuICBlc2xpbnRyYyxcbiAgcGF0dGVybnMsXG59OiBFU0xpbnRUYXJnZXQpOiBQcm9taXNlPExpbnRlck91dHB1dD4ge1xuICBjb25zdCByZXN1bHRzID0gYXdhaXQgZXhlY3V0ZUxpbnQoeyBlc2xpbnRyYywgcGF0dGVybnMgfSk7XG4gIGNvbnN0IGVzbGludCA9IGF3YWl0IHNldHVwRVNMaW50KGVzbGludHJjKTtcbiAgY29uc3QgcnVsZU9wdGlvbnNQZXJGaWxlID0gYXdhaXQgbG9hZFJ1bGVPcHRpb25zUGVyRmlsZShlc2xpbnQsIHJlc3VsdHMpO1xuICByZXR1cm4geyByZXN1bHRzLCBydWxlT3B0aW9uc1BlckZpbGUgfTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZXhlY3V0ZUxpbnQoe1xuICBlc2xpbnRyYyxcbiAgcGF0dGVybnMsXG59OiBFU0xpbnRUYXJnZXQpOiBQcm9taXNlPEVTTGludC5MaW50UmVzdWx0W10+IHtcbiAgLy8gcnVubmluZyBhcyBDTEkgYmVjYXVzZSBFU0xpbnQjbGludEZpbGVzKCkgcnVucyBvdXQgb2YgbWVtb3J5XG4gIGNvbnN0IHsgc3Rkb3V0IH0gPSBhd2FpdCBleGVjdXRlUHJvY2Vzcyh7XG4gICAgY29tbWFuZDogJ25weCcsXG4gICAgYXJnczogW1xuICAgICAgJ2VzbGludCcsXG4gICAgICAuLi4oZXNsaW50cmMgPyBbYC0tY29uZmlnPSR7ZmlsZVBhdGhUb0NsaUFyZyhlc2xpbnRyYyl9YF0gOiBbXSksXG4gICAgICAuLi4odHlwZW9mIGVzbGludHJjID09PSAnb2JqZWN0JyA/IFsnLS1uby1lc2xpbnRyYyddIDogW10pLFxuICAgICAgJy0tbm8tZXJyb3Itb24tdW5tYXRjaGVkLXBhdHRlcm4nLFxuICAgICAgJy0tZm9ybWF0PWpzb24nLFxuICAgICAgLi4udG9BcnJheShwYXR0ZXJucykubWFwKHBhdHRlcm4gPT5cbiAgICAgICAgLy8gZ2xvYnMgbmVlZCB0byBiZSBlc2NhcGVkIG9uIFVuaXhcbiAgICAgICAgcGxhdGZvcm0oKSA9PT0gJ3dpbjMyJyA/IHBhdHRlcm4gOiBgJyR7cGF0dGVybn0nYCxcbiAgICAgICksXG4gICAgXSxcbiAgICBpZ25vcmVFeGl0Q29kZTogdHJ1ZSxcbiAgICBjd2Q6IHByb2Nlc3MuY3dkKCksXG4gIH0pO1xuXG4gIHJldHVybiBKU09OLnBhcnNlKHN0ZG91dCkgYXMgRVNMaW50LkxpbnRSZXN1bHRbXTtcbn1cblxuZnVuY3Rpb24gbG9hZFJ1bGVPcHRpb25zUGVyRmlsZShcbiAgZXNsaW50OiBFU0xpbnQsXG4gIHJlc3VsdHM6IEVTTGludC5MaW50UmVzdWx0W10sXG4pOiBQcm9taXNlPFJ1bGVPcHRpb25zUGVyRmlsZT4ge1xuICByZXR1cm4gcmVzdWx0cy5yZWR1Y2UoYXN5bmMgKGFjYywgeyBmaWxlUGF0aCwgbWVzc2FnZXMgfSkgPT4ge1xuICAgIGNvbnN0IGZpbGVzTWFwID0gYXdhaXQgYWNjO1xuICAgIGNvbnN0IGNvbmZpZyA9IChhd2FpdCBlc2xpbnQuY2FsY3VsYXRlQ29uZmlnRm9yRmlsZShcbiAgICAgIGZpbGVQYXRoLFxuICAgICkpIGFzIExpbnRlci5Db25maWc7XG4gICAgY29uc3QgcnVsZUlkcyA9IGRpc3RpbmN0KFxuICAgICAgbWVzc2FnZXNcbiAgICAgICAgLm1hcCgoeyBydWxlSWQgfSkgPT4gcnVsZUlkKVxuICAgICAgICAuZmlsdGVyKChydWxlSWQpOiBydWxlSWQgaXMgc3RyaW5nID0+IHJ1bGVJZCAhPSBudWxsKSxcbiAgICApO1xuICAgIGNvbnN0IHJ1bGVzTWFwID0gT2JqZWN0LmZyb21FbnRyaWVzKFxuICAgICAgcnVsZUlkcy5tYXAocnVsZUlkID0+IFtcbiAgICAgICAgcnVsZUlkLFxuICAgICAgICB0b0FycmF5KGNvbmZpZy5ydWxlcz8uW3J1bGVJZF0gPz8gW10pLnNsaWNlKDEpLFxuICAgICAgXSksXG4gICAgKTtcbiAgICByZXR1cm4ge1xuICAgICAgLi4uZmlsZXNNYXAsXG4gICAgICBbZmlsZVBhdGhdOiB7XG4gICAgICAgIC4uLmZpbGVzTWFwW2ZpbGVQYXRoXSxcbiAgICAgICAgLi4ucnVsZXNNYXAsXG4gICAgICB9LFxuICAgIH07XG4gIH0sIFByb21pc2UucmVzb2x2ZTxSdWxlT3B0aW9uc1BlckZpbGU+KHt9KSk7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvcnVubmVyL3RyYW5zZm9ybS50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvcnVubmVyXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9ydW5uZXIvdHJhbnNmb3JtLnRzXCI7aW1wb3J0IHR5cGUgeyBMaW50ZXIgfSBmcm9tICdlc2xpbnQnO1xuaW1wb3J0IHR5cGUgeyBBdWRpdE91dHB1dCwgSXNzdWUsIElzc3VlU2V2ZXJpdHkgfSBmcm9tICdAY29kZS1wdXNodXAvbW9kZWxzJztcbmltcG9ydCB7XG4gIGNvbXBhcmVJc3N1ZVNldmVyaXR5LFxuICBjb3VudE9jY3VycmVuY2VzLFxuICBvYmplY3RUb0VudHJpZXMsXG4gIHBsdXJhbGl6ZVRva2VuLFxuICB0cnVuY2F0ZUlzc3VlTWVzc2FnZSxcbiAgdWksXG59IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgeyBydWxlSWRUb1NsdWcgfSBmcm9tICcuLi9tZXRhL2luZGV4LmpzJztcbmltcG9ydCB0eXBlIHsgTGludGVyT3V0cHV0IH0gZnJvbSAnLi90eXBlcy5qcyc7XG5cbnR5cGUgTGludElzc3VlID0gTGludGVyLkxpbnRNZXNzYWdlICYge1xuICBmaWxlUGF0aDogc3RyaW5nO1xufTtcblxuZXhwb3J0IGZ1bmN0aW9uIG1lcmdlTGludGVyT3V0cHV0cyhvdXRwdXRzOiBMaW50ZXJPdXRwdXRbXSk6IExpbnRlck91dHB1dCB7XG4gIHJldHVybiBvdXRwdXRzLnJlZHVjZTxMaW50ZXJPdXRwdXQ+KFxuICAgIChhY2MsIHsgcmVzdWx0cywgcnVsZU9wdGlvbnNQZXJGaWxlIH0pID0+ICh7XG4gICAgICByZXN1bHRzOiBbLi4uYWNjLnJlc3VsdHMsIC4uLnJlc3VsdHNdLFxuICAgICAgcnVsZU9wdGlvbnNQZXJGaWxlOiB7IC4uLmFjYy5ydWxlT3B0aW9uc1BlckZpbGUsIC4uLnJ1bGVPcHRpb25zUGVyRmlsZSB9LFxuICAgIH0pLFxuICAgIHsgcmVzdWx0czogW10sIHJ1bGVPcHRpb25zUGVyRmlsZToge30gfSxcbiAgKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGxpbnRSZXN1bHRzVG9BdWRpdHMoe1xuICByZXN1bHRzLFxuICBydWxlT3B0aW9uc1BlckZpbGUsXG59OiBMaW50ZXJPdXRwdXQpOiBBdWRpdE91dHB1dFtdIHtcbiAgY29uc3QgaXNzdWVzUGVyQXVkaXQgPSByZXN1bHRzXG4gICAgLmZsYXRNYXAoKHsgbWVzc2FnZXMsIGZpbGVQYXRoIH0pID0+XG4gICAgICBtZXNzYWdlcy5tYXAoKG1lc3NhZ2UpOiBMaW50SXNzdWUgPT4gKHsgLi4ubWVzc2FnZSwgZmlsZVBhdGggfSkpLFxuICAgIClcbiAgICAucmVkdWNlPFJlY29yZDxzdHJpbmcsIExpbnRJc3N1ZVtdPj4oKGFjYywgaXNzdWUpID0+IHtcbiAgICAgIGNvbnN0IHsgcnVsZUlkLCBtZXNzYWdlLCBmaWxlUGF0aCB9ID0gaXNzdWU7XG4gICAgICBpZiAoIXJ1bGVJZCkge1xuICAgICAgICB1aSgpLmxvZ2dlci53YXJuaW5nKFxuICAgICAgICAgIGBFU0xpbnQgY29yZSBlcnJvciAtICR7bWVzc2FnZX0gKGZpbGU6ICR7ZmlsZVBhdGh9KWAsXG4gICAgICAgICk7XG4gICAgICAgIHJldHVybiBhY2M7XG4gICAgICB9XG4gICAgICBjb25zdCBvcHRpb25zID0gcnVsZU9wdGlvbnNQZXJGaWxlW2ZpbGVQYXRoXT8uW3J1bGVJZF0gPz8gW107XG4gICAgICBjb25zdCBhdWRpdFNsdWcgPSBydWxlSWRUb1NsdWcocnVsZUlkLCBvcHRpb25zKTtcbiAgICAgIHJldHVybiB7IC4uLmFjYywgW2F1ZGl0U2x1Z106IFsuLi4oYWNjW2F1ZGl0U2x1Z10gPz8gW10pLCBpc3N1ZV0gfTtcbiAgICB9LCB7fSk7XG5cbiAgcmV0dXJuIE9iamVjdC5lbnRyaWVzKGlzc3Vlc1BlckF1ZGl0KS5tYXAoZW50cnkgPT4gdG9BdWRpdCguLi5lbnRyeSkpO1xufVxuXG5mdW5jdGlvbiB0b0F1ZGl0KHNsdWc6IHN0cmluZywgaXNzdWVzOiBMaW50SXNzdWVbXSk6IEF1ZGl0T3V0cHV0IHtcbiAgY29uc3QgYXVkaXRJc3N1ZXMgPSBpc3N1ZXMubWFwKGNvbnZlcnRJc3N1ZSk7XG4gIGNvbnN0IHNldmVyaXR5Q291bnRzID0gY291bnRPY2N1cnJlbmNlcyhcbiAgICBhdWRpdElzc3Vlcy5tYXAoKHsgc2V2ZXJpdHkgfSkgPT4gc2V2ZXJpdHkpLFxuICApO1xuICBjb25zdCBzZXZlcml0aWVzID0gb2JqZWN0VG9FbnRyaWVzKHNldmVyaXR5Q291bnRzKTtcbiAgY29uc3Qgc3VtbWFyeVRleHQgPSBzZXZlcml0aWVzXG4gICAgLnRvU29ydGVkKChhLCBiKSA9PiAtY29tcGFyZUlzc3VlU2V2ZXJpdHkoYVswXSwgYlswXSkpXG4gICAgLm1hcCgoW3NldmVyaXR5LCBjb3VudCA9IDBdKSA9PiBwbHVyYWxpemVUb2tlbihzZXZlcml0eSwgY291bnQpKVxuICAgIC5qb2luKCcsICcpO1xuXG4gIHJldHVybiB7XG4gICAgc2x1ZyxcbiAgICBzY29yZTogTnVtYmVyKGF1ZGl0SXNzdWVzLmxlbmd0aCA9PT0gMCksXG4gICAgdmFsdWU6IGF1ZGl0SXNzdWVzLmxlbmd0aCxcbiAgICBkaXNwbGF5VmFsdWU6IHN1bW1hcnlUZXh0LFxuICAgIGRldGFpbHM6IHtcbiAgICAgIGlzc3VlczogYXVkaXRJc3N1ZXMsXG4gICAgfSxcbiAgfTtcbn1cblxuZnVuY3Rpb24gY29udmVydElzc3VlKGlzc3VlOiBMaW50SXNzdWUpOiBJc3N1ZSB7XG4gIHJldHVybiB7XG4gICAgbWVzc2FnZTogdHJ1bmNhdGVJc3N1ZU1lc3NhZ2UoaXNzdWUubWVzc2FnZSksXG4gICAgc2V2ZXJpdHk6IGNvbnZlcnRTZXZlcml0eShpc3N1ZS5zZXZlcml0eSksXG4gICAgc291cmNlOiB7XG4gICAgICBmaWxlOiBpc3N1ZS5maWxlUGF0aCxcbiAgICAgIC4uLihpc3N1ZS5saW5lID4gMCAmJiB7XG4gICAgICAgIHBvc2l0aW9uOiB7XG4gICAgICAgICAgc3RhcnRMaW5lOiBpc3N1ZS5saW5lLFxuICAgICAgICAgIC4uLihpc3N1ZS5jb2x1bW4gPiAwICYmIHsgc3RhcnRDb2x1bW46IGlzc3VlLmNvbHVtbiB9KSxcbiAgICAgICAgICAuLi4oaXNzdWUuZW5kTGluZSAmJlxuICAgICAgICAgICAgaXNzdWUuZW5kTGluZSA+IDAgJiYge1xuICAgICAgICAgICAgICBlbmRMaW5lOiBpc3N1ZS5lbmRMaW5lLFxuICAgICAgICAgICAgfSksXG4gICAgICAgICAgLi4uKGlzc3VlLmVuZENvbHVtbiAmJlxuICAgICAgICAgICAgaXNzdWUuZW5kQ29sdW1uID4gMCAmJiB7IGVuZENvbHVtbjogaXNzdWUuZW5kQ29sdW1uIH0pLFxuICAgICAgICB9LFxuICAgICAgfSksXG4gICAgfSxcbiAgfTtcbn1cblxuZnVuY3Rpb24gY29udmVydFNldmVyaXR5KHNldmVyaXR5OiBMaW50ZXIuU2V2ZXJpdHkpOiBJc3N1ZVNldmVyaXR5IHtcbiAgc3dpdGNoIChzZXZlcml0eSkge1xuICAgIGNhc2UgMjpcbiAgICAgIHJldHVybiAnZXJyb3InO1xuICAgIGNhc2UgMTpcbiAgICAgIHJldHVybiAnd2FybmluZyc7XG4gICAgY2FzZSAwOlxuICAgICAgLy8gc2hvdWxkbid0IGhhcHBlblxuICAgICAgdGhyb3cgbmV3IEVycm9yKGBVbmV4cGVjdGVkIHNldmVyaXR5ICR7c2V2ZXJpdHl9IGluIEVTTGludCByZXN1bHRzYCk7XG4gIH1cbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1lc2xpbnQvc3JjL2xpYi9ueC91dGlscy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tZXNsaW50L3NyYy9saWIvbnhcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvbGliL254L3V0aWxzLnRzXCI7aW1wb3J0IHR5cGUgeyBQcm9qZWN0Q29uZmlndXJhdGlvbiB9IGZyb20gJ0BueC9kZXZraXQnO1xuaW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB7IGZpbGVFeGlzdHMsIHRvQXJyYXkgfSBmcm9tICdAY29kZS1wdXNodXAvdXRpbHMnO1xuaW1wb3J0IHR5cGUgeyBDb25maWdGb3JtYXQgfSBmcm9tICcuLi9tZXRhL2luZGV4LmpzJztcblxuY29uc3QgRVNMSU5UX0NPTkZJR19FWFRFTlNJT05TOiBSZWNvcmQ8Q29uZmlnRm9ybWF0LCBzdHJpbmdbXT4gPSB7XG4gIC8vIGh0dHBzOi8vZXNsaW50Lm9yZy9kb2NzL2xhdGVzdC91c2UvY29uZmlndXJlL2NvbmZpZ3VyYXRpb24tZmlsZXMjY29uZmlndXJhdGlvbi1maWxlLWZvcm1hdHNcbiAgZmxhdDogWydqcycsICdtanMnLCAnY2pzJ10sXG4gIC8vIGh0dHBzOi8vZXNsaW50Lm9yZy9kb2NzL2xhdGVzdC91c2UvY29uZmlndXJlL2NvbmZpZ3VyYXRpb24tZmlsZXMtZGVwcmVjYXRlZFxuICBsZWdhY3k6IFsnanNvbicsICdqcycsICdjanMnLCAneW1sJywgJ3lhbWwnXSxcbn07XG5jb25zdCBFU0xJTlRfQ09ORklHX05BTUVTOiBSZWNvcmQ8Q29uZmlnRm9ybWF0LCBzdHJpbmdbXT4gPSB7XG4gIC8vIGh0dHBzOi8vZXNsaW50Lm9yZy9kb2NzL2xhdGVzdC91c2UvY29uZmlndXJlL2NvbmZpZ3VyYXRpb24tZmlsZXMjY29uZmlndXJhdGlvbi1maWxlLWZvcm1hdHNcbiAgZmxhdDogWydlc2xpbnQuY29uZmlnJ10sXG4gIC8vIGh0dHBzOi8vZXNsaW50Lm9yZy9kb2NzL2xhdGVzdC91c2UvY29uZmlndXJlL2NvbmZpZ3VyYXRpb24tZmlsZXMtZGVwcmVjYXRlZFxuICBsZWdhY3k6IFsnLmVzbGludHJjJ10sXG59O1xuXG5jb25zdCBDUF9FU0xJTlRfQ09ORklHX05BTUVTOiBSZWNvcmQ8Q29uZmlnRm9ybWF0LCBzdHJpbmdbXT4gPSB7XG4gIGZsYXQ6IFtcbiAgICAnY29kZS1wdXNodXAuZXNsaW50LmNvbmZpZycsXG4gICAgJ2VzbGludC5jb2RlLXB1c2h1cC5jb25maWcnLFxuICAgICdlc2xpbnQuY29uZmlnLmNvZGUtcHVzaHVwJyxcbiAgICAnZXNsaW50LnN0cmljdC5jb25maWcnLFxuICAgICdlc2xpbnQuY29uZmlnLnN0cmljdCcsXG4gIF0sXG4gIGxlZ2FjeTogWydjb2RlLXB1c2h1cC5lc2xpbnRyYycsICcuZXNsaW50cmMuY29kZS1wdXNodXAnLCAnLmVzbGludHJjLnN0cmljdCddLFxufTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGZpbmRDb2RlUHVzaHVwRXNsaW50Q29uZmlnKFxuICBwcm9qZWN0OiBQcm9qZWN0Q29uZmlndXJhdGlvbixcbiAgZm9ybWF0OiBDb25maWdGb3JtYXQsXG4pOiBQcm9taXNlPHN0cmluZyB8IHVuZGVmaW5lZD4ge1xuICByZXR1cm4gZmluZFByb2plY3RGaWxlKHByb2plY3QsIHtcbiAgICBuYW1lczogQ1BfRVNMSU5UX0NPTkZJR19OQU1FU1tmb3JtYXRdLFxuICAgIGV4dGVuc2lvbnM6IEVTTElOVF9DT05GSUdfRVhURU5TSU9OU1tmb3JtYXRdLFxuICB9KTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGZpbmRFc2xpbnRDb25maWcoXG4gIHByb2plY3Q6IFByb2plY3RDb25maWd1cmF0aW9uLFxuICBmb3JtYXQ6IENvbmZpZ0Zvcm1hdCxcbik6IFByb21pc2U8c3RyaW5nIHwgdW5kZWZpbmVkPiB7XG4gIGNvbnN0IG9wdGlvbnMgPSBwcm9qZWN0LnRhcmdldHM/LlsnbGludCddPy5vcHRpb25zIGFzXG4gICAgfCB7IGVzbGludENvbmZpZz86IHN0cmluZyB9XG4gICAgfCB1bmRlZmluZWQ7XG4gIHJldHVybiAoXG4gICAgb3B0aW9ucz8uZXNsaW50Q29uZmlnID8/XG4gICAgKGF3YWl0IGZpbmRQcm9qZWN0RmlsZShwcm9qZWN0LCB7XG4gICAgICBuYW1lczogRVNMSU5UX0NPTkZJR19OQU1FU1tmb3JtYXRdLFxuICAgICAgZXh0ZW5zaW9uczogRVNMSU5UX0NPTkZJR19FWFRFTlNJT05TW2Zvcm1hdF0sXG4gICAgfSkpXG4gICk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRMaW50RmlsZVBhdHRlcm5zKFxuICBwcm9qZWN0OiBQcm9qZWN0Q29uZmlndXJhdGlvbixcbiAgZm9ybWF0OiBDb25maWdGb3JtYXQsXG4pOiBzdHJpbmdbXSB7XG4gIGNvbnN0IG9wdGlvbnMgPSBwcm9qZWN0LnRhcmdldHM/LlsnbGludCddPy5vcHRpb25zIGFzXG4gICAgfCB7IGxpbnRGaWxlUGF0dGVybnM/OiBzdHJpbmcgfCBzdHJpbmdbXSB9XG4gICAgfCB1bmRlZmluZWQ7XG4gIC8vIGxpbnRGaWxlUGF0dGVybnMgZGVmYXVsdHMgdG8gW1wie3Byb2plY3RSb290fVwiXSAtIGh0dHBzOi8vZ2l0aHViLmNvbS9ucndsL254L3B1bGwvMjAzMTNcbiAgY29uc3QgZGVmYXVsdFBhdHRlcm5zID1cbiAgICBmb3JtYXQgPT09ICdsZWdhY3knXG4gICAgICA/IGAke3Byb2plY3Qucm9vdH0vKiovKmAgLy8gZmlsZXMgbm90IGZvbGRlciBuZWVkZWQgZm9yIGxlZ2FjeSBiZWNhdXNlIHJ1bGVzIGRldGVjdGVkIHdpdGggRVNMaW50LmNhbGN1bGF0ZUNvbmZpZ0ZvckZpbGVcbiAgICAgIDogcHJvamVjdC5yb290O1xuICBjb25zdCBwYXR0ZXJucyA9XG4gICAgb3B0aW9ucz8ubGludEZpbGVQYXR0ZXJucyA9PSBudWxsXG4gICAgICA/IFtkZWZhdWx0UGF0dGVybnNdXG4gICAgICA6IHRvQXJyYXkob3B0aW9ucy5saW50RmlsZVBhdHRlcm5zKTtcbiAgaWYgKGZvcm1hdCA9PT0gJ2xlZ2FjeScpIHtcbiAgICByZXR1cm4gW1xuICAgICAgLi4ucGF0dGVybnMsXG4gICAgICAvLyBIQUNLOiBFU0xpbnQuY2FsY3VsYXRlQ29uZmlnRm9yRmlsZSB3b24ndCBmaW5kIHJ1bGVzIGluY2x1ZGVkIG9ubHkgZm9yIHN1YnNldHMgb2YgKi50cyB3aGVuIGdsb2JzIHVzZWRcbiAgICAgIC8vIHNvIHdlIGV4cGxpY2l0bHkgcHJvdmlkZSBhZGRpdGlvbmFsIHBhdHRlcm5zIHVzZWQgYnkgQGNvZGUtcHVzaHVwL2VzbGludC1jb25maWcgdG8gZW5zdXJlIHRob3NlIHJ1bGVzIGFyZSBpbmNsdWRlZFxuICAgICAgLy8gdGhpcyB3b3JrYXJvdW5kIGlzIG9ubHkgbmVjZXNzYXJ5IGZvciBsZWdhY3kgY29uZmlncyAocnVsZXMgYXJlIGRldGVjdGVkIG1vcmUgcmVsaWFibHkgaW4gZmxhdCBjb25maWdzKVxuICAgICAgYCR7cHJvamVjdC5yb290fS8qLnNwZWMudHNgLCAvLyBqZXN0LyogYW5kIHZpdGVzdC8qIHJ1bGVzXG4gICAgICBgJHtwcm9qZWN0LnJvb3R9LyouY3kudHNgLCAvLyBjeXByZXNzLyogcnVsZXNcbiAgICAgIGAke3Byb2plY3Qucm9vdH0vKi5zdG9yaWVzLnRzYCwgLy8gc3Rvcnlib29rLyogcnVsZXNcbiAgICAgIGAke3Byb2plY3Qucm9vdH0vLnN0b3J5Ym9vay9tYWluLnRzYCwgLy8gc3Rvcnlib29rL25vLXVuaW5zdGFsbGVkLWFkZG9ucyBydWxlXG4gICAgXTtcbiAgfVxuICByZXR1cm4gcGF0dGVybnM7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGZpbmRQcm9qZWN0RmlsZShcbiAgcHJvamVjdDogUHJvamVjdENvbmZpZ3VyYXRpb24sXG4gIGZpbGU6IHtcbiAgICBuYW1lczogc3RyaW5nW107XG4gICAgZXh0ZW5zaW9uczogc3RyaW5nW107XG4gIH0sXG4pOiBQcm9taXNlPHN0cmluZyB8IHVuZGVmaW5lZD4ge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZnVuY3Rpb25hbC9uby1sb29wLXN0YXRlbWVudHNcbiAgZm9yIChjb25zdCBuYW1lIG9mIGZpbGUubmFtZXMpIHtcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZnVuY3Rpb25hbC9uby1sb29wLXN0YXRlbWVudHNcbiAgICBmb3IgKGNvbnN0IGV4dCBvZiBmaWxlLmV4dGVuc2lvbnMpIHtcbiAgICAgIGNvbnN0IGZpbGVuYW1lID0gYC4vJHtwcm9qZWN0LnJvb3R9LyR7bmFtZX0uJHtleHR9YDtcbiAgICAgIGlmIChhd2FpdCBmaWxlRXhpc3RzKHBhdGguam9pbihwcm9jZXNzLmN3ZCgpLCBmaWxlbmFtZSkpKSB7XG4gICAgICAgIHJldHVybiBmaWxlbmFtZTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgcmV0dXJuIHVuZGVmaW5lZDtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL2pzLXBhY2thZ2VzLXBsdWdpbi50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYlwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9qcy1wYWNrYWdlcy1wbHVnaW4udHNcIjtpbXBvcnQgeyBjcmVhdGVSZXF1aXJlIH0gZnJvbSAnbm9kZTptb2R1bGUnO1xuaW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB7IGZpbGVVUkxUb1BhdGggfSBmcm9tICdub2RlOnVybCc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0LCBHcm91cCwgUGx1Z2luQ29uZmlnIH0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5pbXBvcnQge1xuICB0eXBlIERlcGVuZGVuY3lHcm91cCxcbiAgdHlwZSBKU1BhY2thZ2VzUGx1Z2luQ29uZmlnLFxuICB0eXBlIFBhY2thZ2VDb21tYW5kLFxuICB0eXBlIFBhY2thZ2VNYW5hZ2VySWQsXG4gIGRlcGVuZGVuY3lHcm91cHMsXG59IGZyb20gJy4vY29uZmlnLmpzJztcbmltcG9ydCB7IGRlcGVuZGVuY3lEb2NzLCBkZXBlbmRlbmN5R3JvdXBXZWlnaHRzIH0gZnJvbSAnLi9jb25zdGFudHMuanMnO1xuaW1wb3J0IHsgcGFja2FnZU1hbmFnZXJzIH0gZnJvbSAnLi9wYWNrYWdlLW1hbmFnZXJzL3BhY2thZ2UtbWFuYWdlcnMuanMnO1xuaW1wb3J0IHsgY3JlYXRlUnVubmVyQ29uZmlnIH0gZnJvbSAnLi9ydW5uZXIvaW5kZXguanMnO1xuaW1wb3J0IHsgbm9ybWFsaXplQ29uZmlnIH0gZnJvbSAnLi91dGlscy5qcyc7XG5cbi8qKlxuICogSW5zdGFudGlhdGVzIENvZGUgUHVzaFVwIEpTIHBhY2thZ2VzIHBsdWdpbiBmb3IgY29yZSBjb25maWcuXG4gKlxuICogQGV4YW1wbGVcbiAqIGltcG9ydCBqc1BhY2thZ2VzUGx1Z2luIGZyb20gJ0Bjb2RlLXB1c2h1cC9qcy1wYWNrYWdlcy1wbHVnaW4nXG4gKlxuICogZXhwb3J0IGRlZmF1bHQge1xuICogICAvLyAuLi4gY29yZSBjb25maWcgLi4uXG4gKiAgIHBsdWdpbnM6IFtcbiAqICAgICAvLyAuLi4gb3RoZXIgcGx1Z2lucyAuLi5cbiAqICAgICBhd2FpdCBqc1BhY2thZ2VzUGx1Z2luKHsgcGFja2FnZU1hbmFnZXI6ICducG0nIH0pXG4gKiAgIF1cbiAqIH1cbiAqXG4gKiBAcmV0dXJucyBQbHVnaW4gY29uZmlndXJhdGlvbi5cbiAqL1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24ganNQYWNrYWdlc1BsdWdpbihcbiAgY29uZmlnPzogSlNQYWNrYWdlc1BsdWdpbkNvbmZpZyxcbik6IFByb21pc2U8UGx1Z2luQ29uZmlnPiB7XG4gIGNvbnN0IHsgcGFja2FnZU1hbmFnZXIsIGNoZWNrcywgZGVwR3JvdXBzLCAuLi5qc1BhY2thZ2VzUGx1Z2luQ29uZmlnUmVzdCB9ID1cbiAgICBhd2FpdCBub3JtYWxpemVDb25maWcoY29uZmlnKTtcblxuICBjb25zdCBydW5uZXJTY3JpcHRQYXRoID0gcGF0aC5qb2luKFxuICAgIGZpbGVVUkxUb1BhdGgocGF0aC5kaXJuYW1lKGltcG9ydC5tZXRhLnVybCkpLFxuICAgICcuLicsXG4gICAgJ2Jpbi5qcycsXG4gICk7XG5cbiAgY29uc3QgcGFja2FnZUpzb24gPSBjcmVhdGVSZXF1aXJlKGltcG9ydC5tZXRhLnVybCkoXG4gICAgJy4uLy4uL3BhY2thZ2UuanNvbicsXG4gICkgYXMgdHlwZW9mIGltcG9ydCgnLi4vLi4vcGFja2FnZS5qc29uJyk7XG5cbiAgcmV0dXJuIHtcbiAgICBzbHVnOiAnanMtcGFja2FnZXMnLFxuICAgIHRpdGxlOiAnSlMgUGFja2FnZXMnLFxuICAgIGljb246IHBhY2thZ2VNYW5hZ2VyLmljb24sXG4gICAgZGVzY3JpcHRpb246XG4gICAgICAnVGhpcyBwbHVnaW4gcnVucyBhdWRpdCB0byB1bmNvdmVyIHZ1bG5lcmFiaWxpdGllcyBhbmQgbGlzdHMgb3V0ZGF0ZWQgZGVwZW5kZW5jaWVzLiBJdCBzdXBwb3J0cyBucG0sIHlhcm4gY2xhc3NpYywgeWFybiBtb2Rlcm4sIGFuZCBwbnBtIHBhY2thZ2UgbWFuYWdlcnMuJyxcbiAgICBkb2NzVXJsOiBwYWNrYWdlTWFuYWdlci5kb2NzLmhvbWVwYWdlLFxuICAgIHBhY2thZ2VOYW1lOiBwYWNrYWdlSnNvbi5uYW1lLFxuICAgIHZlcnNpb246IHBhY2thZ2VKc29uLnZlcnNpb24sXG4gICAgYXVkaXRzOiBjcmVhdGVBdWRpdHMocGFja2FnZU1hbmFnZXIuc2x1ZywgY2hlY2tzLCBkZXBHcm91cHMpLFxuICAgIGdyb3VwczogY3JlYXRlR3JvdXBzKHBhY2thZ2VNYW5hZ2VyLnNsdWcsIGNoZWNrcywgZGVwR3JvdXBzKSxcbiAgICBydW5uZXI6IGF3YWl0IGNyZWF0ZVJ1bm5lckNvbmZpZyhydW5uZXJTY3JpcHRQYXRoLCB7XG4gICAgICAuLi5qc1BhY2thZ2VzUGx1Z2luQ29uZmlnUmVzdCxcbiAgICAgIGNoZWNrcyxcbiAgICAgIHBhY2thZ2VNYW5hZ2VyOiBwYWNrYWdlTWFuYWdlci5zbHVnLFxuICAgICAgZGVwZW5kZW5jeUdyb3VwczogZGVwR3JvdXBzLFxuICAgIH0pLFxuICB9O1xufVxuXG5mdW5jdGlvbiBjcmVhdGVHcm91cHMoXG4gIGlkOiBQYWNrYWdlTWFuYWdlcklkLFxuICBjaGVja3M6IFBhY2thZ2VDb21tYW5kW10sXG4gIGRlcEdyb3VwczogRGVwZW5kZW5jeUdyb3VwW10sXG4pOiBHcm91cFtdIHtcbiAgY29uc3QgcG0gPSBwYWNrYWdlTWFuYWdlcnNbaWRdO1xuICBjb25zdCBzdXBwb3J0ZWRBdWRpdERlcEdyb3VwcyA9XG4gICAgcG0uYXVkaXQuc3VwcG9ydGVkRGVwR3JvdXBzID8/IGRlcGVuZGVuY3lHcm91cHM7XG4gIGNvbnN0IGNvbXBhdGlibGVBdWRpdERlcEdyb3VwcyA9IGRlcEdyb3Vwcy5maWx0ZXIoZ3JvdXAgPT5cbiAgICBzdXBwb3J0ZWRBdWRpdERlcEdyb3Vwcy5pbmNsdWRlcyhncm91cCksXG4gICk7XG5cbiAgY29uc3QgZ3JvdXBzOiBSZWNvcmQ8UGFja2FnZUNvbW1hbmQsIEdyb3VwPiA9IHtcbiAgICBhdWRpdDoge1xuICAgICAgc2x1ZzogYCR7cG0uc2x1Z30tYXVkaXRgLFxuICAgICAgdGl0bGU6IGAke3BtLm5hbWV9IGF1ZGl0YCxcbiAgICAgIGRlc2NyaXB0aW9uOiBgR3JvdXAgY29udGFpbmluZyAke3BtLm5hbWV9IHZ1bG5lcmFiaWxpdGllcy5gLFxuICAgICAgZG9jc1VybDogcG0uZG9jcy5hdWRpdCxcbiAgICAgIHJlZnM6IGNvbXBhdGlibGVBdWRpdERlcEdyb3Vwcy5tYXAoZGVwR3JvdXAgPT4gKHtcbiAgICAgICAgc2x1ZzogYCR7cG0uc2x1Z30tYXVkaXQtJHtkZXBHcm91cH1gLFxuICAgICAgICB3ZWlnaHQ6IGRlcGVuZGVuY3lHcm91cFdlaWdodHNbZGVwR3JvdXBdLFxuICAgICAgfSkpLFxuICAgIH0sXG4gICAgb3V0ZGF0ZWQ6IHtcbiAgICAgIHNsdWc6IGAke3BtLnNsdWd9LW91dGRhdGVkYCxcbiAgICAgIHRpdGxlOiBgJHtwbS5uYW1lfSBvdXRkYXRlZCBkZXBlbmRlbmNpZXNgLFxuICAgICAgZGVzY3JpcHRpb246IGBHcm91cCBjb250YWluaW5nIG91dGRhdGVkICR7cG0ubmFtZX0gZGVwZW5kZW5jaWVzLmAsXG4gICAgICBkb2NzVXJsOiBwbS5kb2NzLm91dGRhdGVkLFxuICAgICAgcmVmczogZGVwR3JvdXBzLm1hcChkZXBHcm91cCA9PiAoe1xuICAgICAgICBzbHVnOiBgJHtwbS5zbHVnfS1vdXRkYXRlZC0ke2RlcEdyb3VwfWAsXG4gICAgICAgIHdlaWdodDogZGVwZW5kZW5jeUdyb3VwV2VpZ2h0c1tkZXBHcm91cF0sXG4gICAgICB9KSksXG4gICAgfSxcbiAgfTtcblxuICByZXR1cm4gY2hlY2tzLm1hcChjaGVjayA9PiBncm91cHNbY2hlY2tdKTtcbn1cblxuZnVuY3Rpb24gY3JlYXRlQXVkaXRzKFxuICBpZDogUGFja2FnZU1hbmFnZXJJZCxcbiAgY2hlY2tzOiBQYWNrYWdlQ29tbWFuZFtdLFxuICBkZXBHcm91cHM6IERlcGVuZGVuY3lHcm91cFtdLFxuKTogQXVkaXRbXSB7XG4gIGNvbnN0IHsgc2x1ZyB9ID0gcGFja2FnZU1hbmFnZXJzW2lkXTtcbiAgcmV0dXJuIGNoZWNrcy5mbGF0TWFwKGNoZWNrID0+IHtcbiAgICBjb25zdCBzdXBwb3J0ZWRBdWRpdERlcEdyb3VwcyA9XG4gICAgICBwYWNrYWdlTWFuYWdlcnNbaWRdLmF1ZGl0LnN1cHBvcnRlZERlcEdyb3VwcyA/PyBkZXBlbmRlbmN5R3JvdXBzO1xuXG4gICAgY29uc3QgY29tcGF0aWJsZURlcEdyb3VwcyA9XG4gICAgICBjaGVjayA9PT0gJ2F1ZGl0J1xuICAgICAgICA/IGRlcEdyb3Vwcy5maWx0ZXIoZ3JvdXAgPT4gc3VwcG9ydGVkQXVkaXREZXBHcm91cHMuaW5jbHVkZXMoZ3JvdXApKVxuICAgICAgICA6IGRlcEdyb3VwcztcblxuICAgIHJldHVybiBjb21wYXRpYmxlRGVwR3JvdXBzLm1hcChkZXBHcm91cCA9PiAoe1xuICAgICAgc2x1ZzogYCR7c2x1Z30tJHtjaGVja30tJHtkZXBHcm91cH1gLFxuICAgICAgdGl0bGU6IGdldEF1ZGl0VGl0bGUoc2x1ZywgY2hlY2ssIGRlcEdyb3VwKSxcbiAgICAgIGRlc2NyaXB0aW9uOiBnZXRBdWRpdERlc2NyaXB0aW9uKGNoZWNrLCBkZXBHcm91cCksXG4gICAgICBkb2NzVXJsOiBkZXBlbmRlbmN5RG9jc1tkZXBHcm91cF0sXG4gICAgfSkpO1xuICB9KTtcbn1cblxuZnVuY3Rpb24gZ2V0QXVkaXRUaXRsZShcbiAgaWQ6IFBhY2thZ2VNYW5hZ2VySWQsXG4gIGNoZWNrOiBQYWNrYWdlQ29tbWFuZCxcbiAgZGVwR3JvdXA6IERlcGVuZGVuY3lHcm91cCxcbikge1xuICBjb25zdCBwbSA9IHBhY2thZ2VNYW5hZ2Vyc1tpZF07XG4gIHJldHVybiBjaGVjayA9PT0gJ2F1ZGl0J1xuICAgID8gYFZ1bG5lcmFiaWxpdGllcyBmb3IgJHtwbS5uYW1lfSAke2RlcEdyb3VwfSBkZXBlbmRlbmNpZXMuYFxuICAgIDogYE91dGRhdGVkICR7cG0ubmFtZX0gJHtkZXBHcm91cH0gZGVwZW5kZW5jaWVzLmA7XG59XG5cbmZ1bmN0aW9uIGdldEF1ZGl0RGVzY3JpcHRpb24oY2hlY2s6IFBhY2thZ2VDb21tYW5kLCBkZXBHcm91cDogRGVwZW5kZW5jeUdyb3VwKSB7XG4gIHJldHVybiBjaGVjayA9PT0gJ2F1ZGl0J1xuICAgID8gYFJ1bnMgc2VjdXJpdHkgYXVkaXQgb24gJHtkZXBHcm91cH0gZGVwZW5kZW5jaWVzLmBcbiAgICA6IGBDaGVja3MgZm9yIG91dGRhdGVkICR7ZGVwR3JvdXB9IGRlcGVuZGVuY2llc2A7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9jb25maWcudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvY29uZmlnLnRzXCI7aW1wb3J0IHsgeiB9IGZyb20gJ3pvZCc7XG5pbXBvcnQgeyB0eXBlIElzc3VlU2V2ZXJpdHksIGlzc3VlU2V2ZXJpdHlTY2hlbWEgfSBmcm9tICdAY29kZS1wdXNodXAvbW9kZWxzJztcbmltcG9ydCB7IGRlZmF1bHRBdWRpdExldmVsTWFwcGluZyB9IGZyb20gJy4vY29uc3RhbnRzLmpzJztcblxuZXhwb3J0IGNvbnN0IGRlcGVuZGVuY3lHcm91cHMgPSBbJ3Byb2QnLCAnZGV2JywgJ29wdGlvbmFsJ10gYXMgY29uc3Q7XG5jb25zdCBkZXBlbmRlbmN5R3JvdXBTY2hlbWEgPSB6LmVudW0oZGVwZW5kZW5jeUdyb3Vwcyk7XG5leHBvcnQgdHlwZSBEZXBlbmRlbmN5R3JvdXAgPSAodHlwZW9mIGRlcGVuZGVuY3lHcm91cHMpW251bWJlcl07XG5cbmNvbnN0IHBhY2thZ2VDb21tYW5kU2NoZW1hID0gei5lbnVtKFsnYXVkaXQnLCAnb3V0ZGF0ZWQnXSk7XG5leHBvcnQgdHlwZSBQYWNrYWdlQ29tbWFuZCA9IHouaW5mZXI8dHlwZW9mIHBhY2thZ2VDb21tYW5kU2NoZW1hPjtcblxuY29uc3QgcGFja2FnZU1hbmFnZXJJZFNjaGVtYSA9IHouZW51bShbXG4gICducG0nLFxuICAneWFybi1jbGFzc2ljJyxcbiAgJ3lhcm4tbW9kZXJuJyxcbiAgJ3BucG0nLFxuXSk7XG5leHBvcnQgdHlwZSBQYWNrYWdlTWFuYWdlcklkID0gei5pbmZlcjx0eXBlb2YgcGFja2FnZU1hbmFnZXJJZFNjaGVtYT47XG5cbmNvbnN0IHBhY2thZ2VKc29uUGF0aFNjaGVtYSA9IHpcbiAgLnVuaW9uKFtcbiAgICB6LmFycmF5KHouc3RyaW5nKCkpLm1pbigxKSxcbiAgICB6Lm9iamVjdCh7IGF1dG9TZWFyY2g6IHoubGl0ZXJhbCh0cnVlKSB9KSxcbiAgXSlcbiAgLmRlc2NyaWJlKFxuICAgICdGaWxlIHBhdGhzIHRvIHBhY2thZ2UuanNvbi4gTG9va3Mgb25seSBhdCByb290IHBhY2thZ2UuanNvbiBieSBkZWZhdWx0JyxcbiAgKVxuICAuZGVmYXVsdChbJ3BhY2thZ2UuanNvbiddKTtcblxuZXhwb3J0IHR5cGUgUGFja2FnZUpzb25QYXRocyA9IHouaW5mZXI8dHlwZW9mIHBhY2thZ2VKc29uUGF0aFNjaGVtYT47XG5cbmV4cG9ydCBjb25zdCBwYWNrYWdlQXVkaXRMZXZlbHMgPSBbXG4gICdjcml0aWNhbCcsXG4gICdoaWdoJyxcbiAgJ21vZGVyYXRlJyxcbiAgJ2xvdycsXG4gICdpbmZvJyxcbl0gYXMgY29uc3Q7XG5jb25zdCBwYWNrYWdlQXVkaXRMZXZlbFNjaGVtYSA9IHouZW51bShwYWNrYWdlQXVkaXRMZXZlbHMpO1xuZXhwb3J0IHR5cGUgUGFja2FnZUF1ZGl0TGV2ZWwgPSB6LmluZmVyPHR5cGVvZiBwYWNrYWdlQXVkaXRMZXZlbFNjaGVtYT47XG5cbmV4cG9ydCB0eXBlIEF1ZGl0U2V2ZXJpdHkgPSBSZWNvcmQ8UGFja2FnZUF1ZGl0TGV2ZWwsIElzc3VlU2V2ZXJpdHk+O1xuXG5leHBvcnQgZnVuY3Rpb24gZmlsbEF1ZGl0TGV2ZWxNYXBwaW5nKFxuICBtYXBwaW5nOiBQYXJ0aWFsPEF1ZGl0U2V2ZXJpdHk+LFxuKTogQXVkaXRTZXZlcml0eSB7XG4gIHJldHVybiB7XG4gICAgY3JpdGljYWw6IG1hcHBpbmcuY3JpdGljYWwgPz8gZGVmYXVsdEF1ZGl0TGV2ZWxNYXBwaW5nLmNyaXRpY2FsLFxuICAgIGhpZ2g6IG1hcHBpbmcuaGlnaCA/PyBkZWZhdWx0QXVkaXRMZXZlbE1hcHBpbmcuaGlnaCxcbiAgICBtb2RlcmF0ZTogbWFwcGluZy5tb2RlcmF0ZSA/PyBkZWZhdWx0QXVkaXRMZXZlbE1hcHBpbmcubW9kZXJhdGUsXG4gICAgbG93OiBtYXBwaW5nLmxvdyA/PyBkZWZhdWx0QXVkaXRMZXZlbE1hcHBpbmcubG93LFxuICAgIGluZm86IG1hcHBpbmcuaW5mbyA/PyBkZWZhdWx0QXVkaXRMZXZlbE1hcHBpbmcuaW5mbyxcbiAgfTtcbn1cblxuZXhwb3J0IGNvbnN0IGpzUGFja2FnZXNQbHVnaW5Db25maWdTY2hlbWEgPSB6Lm9iamVjdCh7XG4gIGNoZWNrczogelxuICAgIC5hcnJheShwYWNrYWdlQ29tbWFuZFNjaGVtYSwge1xuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdQYWNrYWdlIG1hbmFnZXIgY29tbWFuZHMgdG8gYmUgcnVuLiBEZWZhdWx0cyB0byBib3RoIGF1ZGl0IGFuZCBvdXRkYXRlZC4nLFxuICAgIH0pXG4gICAgLm1pbigxKVxuICAgIC5kZWZhdWx0KFsnYXVkaXQnLCAnb3V0ZGF0ZWQnXSksXG4gIHBhY2thZ2VNYW5hZ2VyOiBwYWNrYWdlTWFuYWdlcklkU2NoZW1hXG4gICAgLmRlc2NyaWJlKCdQYWNrYWdlIG1hbmFnZXIgdG8gYmUgdXNlZC4nKVxuICAgIC5vcHRpb25hbCgpLFxuICBkZXBlbmRlbmN5R3JvdXBzOiB6XG4gICAgLmFycmF5KGRlcGVuZGVuY3lHcm91cFNjaGVtYSlcbiAgICAubWluKDEpXG4gICAgLmRlZmF1bHQoWydwcm9kJywgJ2RldiddKSxcbiAgYXVkaXRMZXZlbE1hcHBpbmc6IHpcbiAgICAucmVjb3JkKHBhY2thZ2VBdWRpdExldmVsU2NoZW1hLCBpc3N1ZVNldmVyaXR5U2NoZW1hLCB7XG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ01hcHBpbmcgb2YgYXVkaXQgbGV2ZWxzIHRvIGlzc3VlIHNldmVyaXR5LiBDdXN0b20gbWFwcGluZyBvciBvdmVycmlkZXMgbWF5IGJlIGVudGVyZWQgbWFudWFsbHksIG90aGVyd2lzZSBoYXMgYSBkZWZhdWx0IHByZXNldC4nLFxuICAgIH0pXG4gICAgLmRlZmF1bHQoZGVmYXVsdEF1ZGl0TGV2ZWxNYXBwaW5nKVxuICAgIC50cmFuc2Zvcm0oZmlsbEF1ZGl0TGV2ZWxNYXBwaW5nKSxcbiAgcGFja2FnZUpzb25QYXRoczogcGFja2FnZUpzb25QYXRoU2NoZW1hLFxufSk7XG5cbmV4cG9ydCB0eXBlIEpTUGFja2FnZXNQbHVnaW5Db25maWcgPSB6LmlucHV0PFxuICB0eXBlb2YganNQYWNrYWdlc1BsdWdpbkNvbmZpZ1NjaGVtYVxuPjtcblxuZXhwb3J0IHR5cGUgRmluYWxKU1BhY2thZ2VzUGx1Z2luQ29uZmlnID0gUmVxdWlyZWQ8XG4gIHouaW5mZXI8dHlwZW9mIGpzUGFja2FnZXNQbHVnaW5Db25maWdTY2hlbWE+XG4+O1xuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvY29uc3RhbnRzLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL2NvbnN0YW50cy50c1wiO2ltcG9ydCB0eXBlIHsgSXNzdWVTZXZlcml0eSB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHR5cGUgeyBEZXBlbmRlbmN5R3JvdXAsIFBhY2thZ2VBdWRpdExldmVsIH0gZnJvbSAnLi9jb25maWcuanMnO1xuaW1wb3J0IHR5cGUgeyBEZXBlbmRlbmN5R3JvdXBMb25nIH0gZnJvbSAnLi9ydW5uZXIvb3V0ZGF0ZWQvdHlwZXMuanMnO1xuXG5leHBvcnQgY29uc3QgZGVmYXVsdEF1ZGl0TGV2ZWxNYXBwaW5nOiBSZWNvcmQ8XG4gIFBhY2thZ2VBdWRpdExldmVsLFxuICBJc3N1ZVNldmVyaXR5XG4+ID0ge1xuICBjcml0aWNhbDogJ2Vycm9yJyxcbiAgaGlnaDogJ2Vycm9yJyxcbiAgbW9kZXJhdGU6ICd3YXJuaW5nJyxcbiAgbG93OiAnd2FybmluZycsXG4gIGluZm86ICdpbmZvJyxcbn07XG5cbmV4cG9ydCBjb25zdCBkZXBlbmRlbmN5R3JvdXBUb0xvbmc6IFJlY29yZDxcbiAgRGVwZW5kZW5jeUdyb3VwLFxuICBEZXBlbmRlbmN5R3JvdXBMb25nXG4+ID0ge1xuICBwcm9kOiAnZGVwZW5kZW5jaWVzJyxcbiAgZGV2OiAnZGV2RGVwZW5kZW5jaWVzJyxcbiAgb3B0aW9uYWw6ICdvcHRpb25hbERlcGVuZGVuY2llcycsXG59O1xuXG5leHBvcnQgY29uc3QgZGVwZW5kZW5jeUdyb3VwV2VpZ2h0czogUmVjb3JkPERlcGVuZGVuY3lHcm91cCwgbnVtYmVyPiA9IHtcbiAgLyogZXNsaW50LWRpc2FibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLW1hZ2ljLW51bWJlcnMgKi9cbiAgcHJvZDogODAsXG4gIGRldjogMTUsXG4gIG9wdGlvbmFsOiA1LFxuICAvKiBlc2xpbnQtZW5hYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1tYWdpYy1udW1iZXJzICovXG59O1xuXG5leHBvcnQgY29uc3QgZGVwZW5kZW5jeURvY3M6IFJlY29yZDxEZXBlbmRlbmN5R3JvdXAsIHN0cmluZz4gPSB7XG4gIHByb2Q6ICdodHRwczovL2NsYXNzaWMueWFybnBrZy5jb20vZG9jcy9kZXBlbmRlbmN5LXR5cGVzI3RvYy1kZXBlbmRlbmNpZXMnLFxuICBkZXY6ICdodHRwczovL2NsYXNzaWMueWFybnBrZy5jb20vZG9jcy9kZXBlbmRlbmN5LXR5cGVzI3RvYy1kZXZkZXBlbmRlbmNpZXMnLFxuICBvcHRpb25hbDpcbiAgICAnaHR0cHM6Ly9jbGFzc2ljLnlhcm5wa2cuY29tL2RvY3MvZGVwZW5kZW5jeS10eXBlcyN0b2Mtb3B0aW9uYWxkZXBlbmRlbmNpZXMnLFxufTtcbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnMvbnBtL25wbS50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL25wbVwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL25wbS9ucG0udHNcIjtpbXBvcnQgeyBvYmplY3RUb0tleXMgfSBmcm9tICdAY29kZS1wdXNodXAvdXRpbHMnO1xuaW1wb3J0IHR5cGUgeyBEZXBlbmRlbmN5R3JvdXAgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xuaW1wb3J0IHsgZmlsdGVyQXVkaXRSZXN1bHQgfSBmcm9tICcuLi8uLi9ydW5uZXIvdXRpbHMuanMnO1xuaW1wb3J0IHsgQ09NTU9OX0FVRElUX0FSR1MsIENPTU1PTl9PVVREQVRFRF9BUkdTIH0gZnJvbSAnLi4vY29uc3RhbnRzLmpzJztcbmltcG9ydCB0eXBlIHsgQXVkaXRSZXN1bHRzLCBQYWNrYWdlTWFuYWdlciB9IGZyb20gJy4uL3R5cGVzLmpzJztcbmltcG9ydCB7IG5wbVRvQXVkaXRSZXN1bHQgfSBmcm9tICcuL2F1ZGl0LXJlc3VsdC5qcyc7XG5pbXBvcnQgeyBucG1Ub091dGRhdGVkUmVzdWx0IH0gZnJvbSAnLi9vdXRkYXRlZC1yZXN1bHQuanMnO1xuXG5jb25zdCBucG1EZXBlbmRlbmN5T3B0aW9uczogUmVjb3JkPERlcGVuZGVuY3lHcm91cCwgc3RyaW5nW10+ID0ge1xuICBwcm9kOiBbJy0tb21pdD1kZXYnLCAnLS1vbWl0PW9wdGlvbmFsJ10sXG4gIGRldjogWyctLWluY2x1ZGU9ZGV2JywgJy0tb21pdD1vcHRpb25hbCddLFxuICBvcHRpb25hbDogWyctLWluY2x1ZGU9b3B0aW9uYWwnLCAnLS1vbWl0PWRldiddLFxufTtcblxuZXhwb3J0IGNvbnN0IG5wbVBhY2thZ2VNYW5hZ2VyOiBQYWNrYWdlTWFuYWdlciA9IHtcbiAgc2x1ZzogJ25wbScsXG4gIG5hbWU6ICdOUE0nLFxuICBjb21tYW5kOiAnbnBtJyxcbiAgaWNvbjogJ25wbScsXG4gIGRvY3M6IHtcbiAgICBob21lcGFnZTogJ2h0dHBzOi8vZG9jcy5ucG1qcy5jb20vJyxcbiAgICBhdWRpdDogJ2h0dHBzOi8vZG9jcy5ucG1qcy5jb20vY2xpL2NvbW1hbmRzL25wbS1hdWRpdCcsXG4gICAgb3V0ZGF0ZWQ6ICdodHRwczovL2RvY3MubnBtanMuY29tL2NsaS9jb21tYW5kcy9ucG0tb3V0ZGF0ZWQnLFxuICB9LFxuICBhdWRpdDoge1xuICAgIGdldENvbW1hbmRBcmdzOiBncm91cERlcCA9PiBbXG4gICAgICAuLi5DT01NT05fQVVESVRfQVJHUyxcbiAgICAgIC4uLm5wbURlcGVuZGVuY3lPcHRpb25zW2dyb3VwRGVwXSxcbiAgICAgICctLWF1ZGl0LWxldmVsPW5vbmUnLFxuICAgIF0sXG4gICAgdW5pZnlSZXN1bHQ6IG5wbVRvQXVkaXRSZXN1bHQsXG4gICAgLy8gcHJvZCBkZXBlbmRlbmNpZXMgbmVlZCB0byBiZSBmaWx0ZXJlZCBvdXQgbWFudWFsbHkgc2luY2UgdjEwXG4gICAgcG9zdFByb2Nlc3NSZXN1bHQ6IChyZXN1bHRzOiBBdWRpdFJlc3VsdHMpID0+IHtcbiAgICAgIGNvbnN0IGRlcEdyb3VwcyA9IG9iamVjdFRvS2V5cyhyZXN1bHRzKTtcbiAgICAgIGNvbnN0IGRldkZpbHRlciA9XG4gICAgICAgIHJlc3VsdHMuZGV2ICYmIHJlc3VsdHMucHJvZFxuICAgICAgICAgID8gZmlsdGVyQXVkaXRSZXN1bHQocmVzdWx0cy5kZXYsICduYW1lJywgcmVzdWx0cy5wcm9kKVxuICAgICAgICAgIDogcmVzdWx0cy5kZXY7XG4gICAgICBjb25zdCBvcHRpb25hbEZpbHRlciA9XG4gICAgICAgIHJlc3VsdHMub3B0aW9uYWwgJiYgcmVzdWx0cy5wcm9kXG4gICAgICAgICAgPyBmaWx0ZXJBdWRpdFJlc3VsdChyZXN1bHRzLm9wdGlvbmFsLCAnbmFtZScsIHJlc3VsdHMucHJvZClcbiAgICAgICAgICA6IHJlc3VsdHMub3B0aW9uYWw7XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIC4uLihkZXBHcm91cHMuaW5jbHVkZXMoJ3Byb2QnKSAmJiB7IHByb2Q6IHJlc3VsdHMucHJvZCB9KSxcbiAgICAgICAgLi4uKGRlcEdyb3Vwcy5pbmNsdWRlcygnZGV2JykgJiYgeyBkZXY6IGRldkZpbHRlciB9KSxcbiAgICAgICAgLi4uKGRlcEdyb3Vwcy5pbmNsdWRlcygnb3B0aW9uYWwnKSAmJiB7IG9wdGlvbmFsOiBvcHRpb25hbEZpbHRlciB9KSxcbiAgICAgIH07XG4gICAgfSxcbiAgfSxcbiAgb3V0ZGF0ZWQ6IHtcbiAgICBjb21tYW5kQXJnczogWy4uLkNPTU1PTl9PVVREQVRFRF9BUkdTLCAnLS1sb25nJ10sXG4gICAgdW5pZnlSZXN1bHQ6IG5wbVRvT3V0ZGF0ZWRSZXN1bHQsXG4gIH0sXG59O1xuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcnVubmVyL3V0aWxzLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3J1bm5lclwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9ydW5uZXIvdXRpbHMudHNcIjtpbXBvcnQgcGF0aCBmcm9tICdub2RlOnBhdGgnO1xuaW1wb3J0IHtcbiAgY3Jhd2xGaWxlU3lzdGVtLFxuICBvYmplY3RGcm9tRW50cmllcyxcbiAgb2JqZWN0VG9LZXlzLFxuICByZWFkSnNvbkZpbGUsXG59IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0UmVzdWx0LCBWdWxuZXJhYmlsaXR5IH0gZnJvbSAnLi9hdWRpdC90eXBlcy5qcyc7XG5pbXBvcnQge1xuICB0eXBlIERlcGVuZGVuY3lHcm91cExvbmcsXG4gIHR5cGUgRGVwZW5kZW5jeVRvdGFscyxcbiAgdHlwZSBQYWNrYWdlSnNvbixcbiAgZGVwZW5kZW5jeUdyb3VwTG9uZyxcbn0gZnJvbSAnLi9vdXRkYXRlZC90eXBlcy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBmaWx0ZXJBdWRpdFJlc3VsdChcbiAgcmVzdWx0OiBBdWRpdFJlc3VsdCxcbiAga2V5OiBrZXlvZiBWdWxuZXJhYmlsaXR5LFxuICByZWZlcmVuY2VSZXN1bHQ/OiBBdWRpdFJlc3VsdCxcbik6IEF1ZGl0UmVzdWx0IHtcbiAgaWYgKHJlc3VsdC52dWxuZXJhYmlsaXRpZXMubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGNvbnN0IHVuaXF1ZVJlc3VsdCA9IHJlc3VsdC52dWxuZXJhYmlsaXRpZXMucmVkdWNlPEF1ZGl0UmVzdWx0PihcbiAgICAoYWNjLCByZWYpID0+IHtcbiAgICAgIGNvbnN0IG1hdGNoUmVmZXJlbmNlID0gcmVmZXJlbmNlUmVzdWx0ID8/IGFjYztcbiAgICAgIGNvbnN0IGlzTWF0Y2ggPSBtYXRjaFJlZmVyZW5jZS52dWxuZXJhYmlsaXRpZXNcbiAgICAgICAgLm1hcCh2dWxuZXJhYmlsaXR5ID0+IHZ1bG5lcmFiaWxpdHlba2V5XSlcbiAgICAgICAgLmluY2x1ZGVzKHJlZltrZXldKTtcblxuICAgICAgaWYgKGlzTWF0Y2gpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICB2dWxuZXJhYmlsaXRpZXM6IGFjYy52dWxuZXJhYmlsaXRpZXMsXG4gICAgICAgICAgc3VtbWFyeToge1xuICAgICAgICAgICAgLi4uYWNjLnN1bW1hcnksXG4gICAgICAgICAgICBbcmVmLnNldmVyaXR5XTogYWNjLnN1bW1hcnlbcmVmLnNldmVyaXR5XSAtIDEsXG4gICAgICAgICAgICB0b3RhbDogYWNjLnN1bW1hcnkudG90YWwgLSAxLFxuICAgICAgICAgIH0sXG4gICAgICAgIH07XG4gICAgICB9XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIHZ1bG5lcmFiaWxpdGllczogWy4uLmFjYy52dWxuZXJhYmlsaXRpZXMsIHJlZl0sXG4gICAgICAgIHN1bW1hcnk6IGFjYy5zdW1tYXJ5LFxuICAgICAgfTtcbiAgICB9LFxuICAgIHsgdnVsbmVyYWJpbGl0aWVzOiBbXSwgc3VtbWFyeTogcmVzdWx0LnN1bW1hcnkgfSxcbiAgKTtcblxuICByZXR1cm4ge1xuICAgIHZ1bG5lcmFiaWxpdGllczogdW5pcXVlUmVzdWx0LnZ1bG5lcmFiaWxpdGllcyxcbiAgICBzdW1tYXJ5OiB1bmlxdWVSZXN1bHQuc3VtbWFyeSxcbiAgfTtcbn1cblxuLy8gVE9ETzogdXNlIC5naXRpZ25vcmVcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBmaW5kQWxsUGFja2FnZUpzb24oKTogUHJvbWlzZTxzdHJpbmdbXT4ge1xuICByZXR1cm4gKFxuICAgIGF3YWl0IGNyYXdsRmlsZVN5c3RlbSh7XG4gICAgICBkaXJlY3Rvcnk6ICcuJyxcbiAgICAgIHBhdHRlcm46IC8oXnxbXFxcXC9dKXBhY2thZ2VcXC5qc29uJC8sXG4gICAgfSlcbiAgKS5maWx0ZXIoXG4gICAgZmlsZVBhdGggPT5cbiAgICAgICFmaWxlUGF0aC5zdGFydHNXaXRoKGBub2RlX21vZHVsZXMke3BhdGguc2VwfWApICYmXG4gICAgICAhZmlsZVBhdGguaW5jbHVkZXMoYCR7cGF0aC5zZXB9bm9kZV9tb2R1bGVzJHtwYXRoLnNlcH1gKSAmJlxuICAgICAgIWZpbGVQYXRoLnN0YXJ0c1dpdGgoYC5ueCR7cGF0aC5zZXB9YCksXG4gICk7XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRUb3RhbERlcGVuZGVuY2llcyhcbiAgcGFja2FnZUpzb25QYXRoczogc3RyaW5nW10sXG4pOiBQcm9taXNlPERlcGVuZGVuY3lUb3RhbHM+IHtcbiAgY29uc3QgcGFyc2VkRGVwcyA9IGF3YWl0IFByb21pc2UuYWxsKFxuICAgIHBhY2thZ2VKc29uUGF0aHMubWFwKHJlYWRKc29uRmlsZTxQYWNrYWdlSnNvbj4pLFxuICApO1xuXG4gIGNvbnN0IG1lcmdlZERlcHMgPSBwYXJzZWREZXBzLnJlZHVjZTxSZWNvcmQ8RGVwZW5kZW5jeUdyb3VwTG9uZywgc3RyaW5nW10+PihcbiAgICAoYWNjLCBkZXBNYXBwZXIpID0+XG4gICAgICBvYmplY3RGcm9tRW50cmllcyhcbiAgICAgICAgZGVwZW5kZW5jeUdyb3VwTG9uZy5tYXAoZ3JvdXAgPT4ge1xuICAgICAgICAgIGNvbnN0IGRlcHMgPSBkZXBNYXBwZXJbZ3JvdXBdO1xuICAgICAgICAgIHJldHVybiBbXG4gICAgICAgICAgICBncm91cCxcbiAgICAgICAgICAgIFsuLi5hY2NbZ3JvdXBdLCAuLi4oZGVwcyA9PSBudWxsID8gW10gOiBvYmplY3RUb0tleXMoZGVwcykpXSxcbiAgICAgICAgICBdO1xuICAgICAgICB9KSxcbiAgICAgICksXG4gICAgeyBkZXBlbmRlbmNpZXM6IFtdLCBkZXZEZXBlbmRlbmNpZXM6IFtdLCBvcHRpb25hbERlcGVuZGVuY2llczogW10gfSxcbiAgKTtcbiAgcmV0dXJuIG9iamVjdEZyb21FbnRyaWVzKFxuICAgIG9iamVjdFRvS2V5cyhtZXJnZWREZXBzKS5tYXAoZGVwcyA9PiBbXG4gICAgICBkZXBzLFxuICAgICAgbmV3IFNldChtZXJnZWREZXBzW2RlcHNdKS5zaXplLFxuICAgIF0pLFxuICApO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy9jb25zdGFudHMudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vyc1wiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL2NvbnN0YW50cy50c1wiO2V4cG9ydCBjb25zdCBDT01NT05fQVVESVRfQVJHUyA9IFsnYXVkaXQnLCAnLS1qc29uJ107XG5leHBvcnQgY29uc3QgQ09NTU9OX09VVERBVEVEX0FSR1MgPSBbJ291dGRhdGVkJywgJy0tanNvbiddO1xuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy9ucG0vYXVkaXQtcmVzdWx0LnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnMvbnBtXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnMvbnBtL2F1ZGl0LXJlc3VsdC50c1wiO2ltcG9ydCB7IG9iamVjdFRvRW50cmllcyB9IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0UmVzdWx0LCBWdWxuZXJhYmlsaXR5IH0gZnJvbSAnLi4vLi4vcnVubmVyL2F1ZGl0L3R5cGVzLmpzJztcbmltcG9ydCB0eXBlIHtcbiAgTnBtQWR2aXNvcnksXG4gIE5wbUF1ZGl0UmVzdWx0SnNvbixcbiAgTnBtRml4SW5mb3JtYXRpb24sXG4gIE5wbVZ1bG5lcmFiaWxpdGllcyxcbn0gZnJvbSAnLi90eXBlcy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBucG1Ub0F1ZGl0UmVzdWx0KG91dHB1dDogc3RyaW5nKTogQXVkaXRSZXN1bHQge1xuICBjb25zdCBucG1BdWRpdCA9IEpTT04ucGFyc2Uob3V0cHV0KSBhcyBOcG1BdWRpdFJlc3VsdEpzb247XG5cbiAgY29uc3QgdnVsbmVyYWJpbGl0aWVzID0gb2JqZWN0VG9FbnRyaWVzKG5wbUF1ZGl0LnZ1bG5lcmFiaWxpdGllcykubWFwKFxuICAgIChbbmFtZSwgZGV0YWlsXSk6IFZ1bG5lcmFiaWxpdHkgPT4ge1xuICAgICAgY29uc3QgYWR2aXNvcnkgPSBucG1Ub0Fkdmlzb3J5KG5hbWUsIG5wbUF1ZGl0LnZ1bG5lcmFiaWxpdGllcyk7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBuYW1lOiBuYW1lLnRvU3RyaW5nKCksXG4gICAgICAgIHNldmVyaXR5OiBkZXRhaWwuc2V2ZXJpdHksXG4gICAgICAgIHZlcnNpb25SYW5nZTogZGV0YWlsLnJhbmdlLFxuICAgICAgICBkaXJlY3REZXBlbmRlbmN5OiBkZXRhaWwuaXNEaXJlY3QgPyB0cnVlIDogKGRldGFpbC5lZmZlY3RzWzBdID8/ICcnKSxcbiAgICAgICAgZml4SW5mb3JtYXRpb246IG5wbVRvRml4SW5mb3JtYXRpb24oZGV0YWlsLmZpeEF2YWlsYWJsZSksXG4gICAgICAgIC4uLihhZHZpc29yeSAhPSBudWxsICYmIHtcbiAgICAgICAgICB0aXRsZTogYWR2aXNvcnkudGl0bGUsXG4gICAgICAgICAgdXJsOiBhZHZpc29yeS51cmwsXG4gICAgICAgIH0pLFxuICAgICAgfTtcbiAgICB9LFxuICApO1xuXG4gIHJldHVybiB7XG4gICAgdnVsbmVyYWJpbGl0aWVzLFxuICAgIHN1bW1hcnk6IG5wbUF1ZGl0Lm1ldGFkYXRhLnZ1bG5lcmFiaWxpdGllcyxcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG5wbVRvRml4SW5mb3JtYXRpb24oXG4gIGZpeEF2YWlsYWJsZTogYm9vbGVhbiB8IE5wbUZpeEluZm9ybWF0aW9uLFxuKTogc3RyaW5nIHtcbiAgaWYgKHR5cGVvZiBmaXhBdmFpbGFibGUgPT09ICdib29sZWFuJykge1xuICAgIHJldHVybiBmaXhBdmFpbGFibGUgPyAnRml4IGlzIGF2YWlsYWJsZS4nIDogJyc7XG4gIH1cblxuICByZXR1cm4gYEZpeCBhdmFpbGFibGU6IFVwZGF0ZSBcXGAke2ZpeEF2YWlsYWJsZS5uYW1lfVxcYCB0byB2ZXJzaW9uICoqJHtcbiAgICBmaXhBdmFpbGFibGUudmVyc2lvblxuICB9Kioke2ZpeEF2YWlsYWJsZS5pc1NlbVZlck1ham9yID8gJyAoYnJlYWtpbmcgY2hhbmdlKS4nIDogJy4nfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBucG1Ub0Fkdmlzb3J5KFxuICBuYW1lOiBzdHJpbmcsXG4gIHZ1bG5lcmFiaWxpdGllczogTnBtVnVsbmVyYWJpbGl0aWVzLFxuICBwcmV2Tm9kZXMgPSBuZXcgU2V0PHN0cmluZz4oKSxcbik6IE5wbUFkdmlzb3J5IHwgbnVsbCB7XG4gIGNvbnN0IGFkdmlzb3J5ID0gdnVsbmVyYWJpbGl0aWVzW25hbWVdPy52aWE7XG5cbiAgaWYgKFxuICAgIEFycmF5LmlzQXJyYXkoYWR2aXNvcnkpICYmXG4gICAgYWR2aXNvcnkubGVuZ3RoID4gMCAmJlxuICAgIHR5cGVvZiBhZHZpc29yeVswXSA9PT0gJ29iamVjdCdcbiAgKSB7XG4gICAgcmV0dXJuIHsgdGl0bGU6IGFkdmlzb3J5WzBdLnRpdGxlLCB1cmw6IGFkdmlzb3J5WzBdLnVybCB9O1xuICB9XG5cbiAgLy8gQ3Jvc3MtcmVmZXJlbmNlcyBhbm90aGVyIHZ1bG5lcmFiaWxpdHlcbiAgaWYgKFxuICAgIEFycmF5LmlzQXJyYXkoYWR2aXNvcnkpICYmXG4gICAgYWR2aXNvcnkubGVuZ3RoID4gMCAmJlxuICAgIGFkdmlzb3J5LmV2ZXJ5KCh2YWx1ZSk6IHZhbHVlIGlzIHN0cmluZyA9PiB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnKVxuICApIHtcbiAgICAvKiBlc2xpbnQtZGlzYWJsZSBmdW5jdGlvbmFsL25vLWxldCwgZnVuY3Rpb25hbC9pbW11dGFibGUtZGF0YSwgZnVuY3Rpb25hbC9uby1sb29wLXN0YXRlbWVudHMsIHByZWZlci1jb25zdCAqL1xuICAgIGxldCBhZHZpc29yeUluZm86IE5wbUFkdmlzb3J5IHwgbnVsbCA9IG51bGw7XG4gICAgbGV0IG5ld1JlZmVyZW5jZXM6IHN0cmluZ1tdID0gW107XG4gICAgbGV0IGFkdmlzb3J5SW5mb0ZvdW5kID0gZmFsc2U7XG4gICAgLyogZXNsaW50LWVuYWJsZSBmdW5jdGlvbmFsL25vLWxldCwgcHJlZmVyLWNvbnN0ICovXG5cbiAgICBmb3IgKGNvbnN0IHZpYSBvZiBhZHZpc29yeSkge1xuICAgICAgaWYgKCFwcmV2Tm9kZXMuaGFzKHZpYSkpIHtcbiAgICAgICAgbmV3UmVmZXJlbmNlcy5wdXNoKHZpYSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgd2hpbGUgKG5ld1JlZmVyZW5jZXMubGVuZ3RoID4gMCAmJiAhYWR2aXNvcnlJbmZvRm91bmQpIHtcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tbm9uLW51bGwtYXNzZXJ0aW9uXG4gICAgICBjb25zdCByZWYgPSBuZXdSZWZlcmVuY2VzLnBvcCgpITtcbiAgICAgIHByZXZOb2Rlcy5hZGQocmVmKTtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IG5wbVRvQWR2aXNvcnkocmVmLCB2dWxuZXJhYmlsaXRpZXMsIHByZXZOb2Rlcyk7XG5cbiAgICAgIGlmIChyZXN1bHQgIT0gbnVsbCkge1xuICAgICAgICBhZHZpc29yeUluZm8gPSB7IHRpdGxlOiByZXN1bHQudGl0bGUsIHVybDogcmVzdWx0LnVybCB9O1xuICAgICAgICBhZHZpc29yeUluZm9Gb3VuZCA9IHRydWU7XG4gICAgICB9XG4gICAgfVxuICAgIC8qIGVzbGludC1lbmFibGUgZnVuY3Rpb25hbC9pbW11dGFibGUtZGF0YSwgZnVuY3Rpb25hbC9uby1sb29wLXN0YXRlbWVudHMgKi9cblxuICAgIHJldHVybiBhZHZpc29yeUluZm87XG4gIH1cblxuICByZXR1cm4gbnVsbDtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnMvbnBtL291dGRhdGVkLXJlc3VsdC50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL25wbVwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL25wbS9vdXRkYXRlZC1yZXN1bHQudHNcIjtpbXBvcnQgeyBvYmplY3RUb0VudHJpZXMgfSBmcm9tICdAY29kZS1wdXNodXAvdXRpbHMnO1xuaW1wb3J0IHR5cGUgeyBPdXRkYXRlZFJlc3VsdCB9IGZyb20gJy4uLy4uL3J1bm5lci9vdXRkYXRlZC90eXBlcy5qcyc7XG5pbXBvcnQgdHlwZSB7IE5wbU5vcm1hbGl6ZWRPdmVydmlldywgTnBtT3V0ZGF0ZWRSZXN1bHRKc29uIH0gZnJvbSAnLi90eXBlcy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBucG1Ub091dGRhdGVkUmVzdWx0KG91dHB1dDogc3RyaW5nKTogT3V0ZGF0ZWRSZXN1bHQge1xuICBjb25zdCBucG1PdXRkYXRlZCA9IEpTT04ucGFyc2Uob3V0cHV0KSBhcyBOcG1PdXRkYXRlZFJlc3VsdEpzb247XG4gIC8vIGN1cnJlbnQgbWlnaHQgYmUgbWlzc2luZyBpbiBzb21lIGNhc2VzXG4gIC8vIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzQyMjY3MTAxL25wbS1vdXRkYXRlZC1jb21tYW5kLXNob3dzLW1pc3NpbmctaW4tY3VycmVudC12ZXJzaW9uXG4gIHJldHVybiBvYmplY3RUb0VudHJpZXMobnBtT3V0ZGF0ZWQpXG4gICAgLmZpbHRlcihcbiAgICAgIChlbnRyeSk6IGVudHJ5IGlzIFtzdHJpbmcsIE5wbU5vcm1hbGl6ZWRPdmVydmlld10gPT5cbiAgICAgICAgZW50cnlbMV0uY3VycmVudCAhPSBudWxsLFxuICAgIClcbiAgICAubWFwKChbbmFtZSwgb3ZlcnZpZXddKSA9PiAoe1xuICAgICAgbmFtZSxcbiAgICAgIGN1cnJlbnQ6IG92ZXJ2aWV3LmN1cnJlbnQsXG4gICAgICBsYXRlc3Q6IG92ZXJ2aWV3LmxhdGVzdCxcbiAgICAgIHR5cGU6IG92ZXJ2aWV3LnR5cGUsXG4gICAgICAuLi4ob3ZlcnZpZXcuaG9tZXBhZ2UgIT0gbnVsbCAmJiB7IHVybDogb3ZlcnZpZXcuaG9tZXBhZ2UgfSksXG4gICAgfSkpO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy9wbnBtL3BucG0udHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy9wbnBtXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnMvcG5wbS9wbnBtLnRzXCI7aW1wb3J0IHsgb2JqZWN0VG9LZXlzIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB0eXBlIHsgRGVwZW5kZW5jeUdyb3VwIH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcbmltcG9ydCB7IGZpbHRlckF1ZGl0UmVzdWx0IH0gZnJvbSAnLi4vLi4vcnVubmVyL3V0aWxzLmpzJztcbmltcG9ydCB7IENPTU1PTl9BVURJVF9BUkdTLCBDT01NT05fT1VUREFURURfQVJHUyB9IGZyb20gJy4uL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0UmVzdWx0cywgUGFja2FnZU1hbmFnZXIgfSBmcm9tICcuLi90eXBlcy5qcyc7XG5pbXBvcnQgeyBwbnBtVG9BdWRpdFJlc3VsdCB9IGZyb20gJy4vYXVkaXQtcmVzdWx0LmpzJztcbmltcG9ydCB7IHBucG1Ub091dGRhdGVkUmVzdWx0IH0gZnJvbSAnLi9vdXRkYXRlZC1yZXN1bHQuanMnO1xuXG5jb25zdCBwbnBtRGVwZW5kZW5jeU9wdGlvbnM6IFJlY29yZDxEZXBlbmRlbmN5R3JvdXAsIHN0cmluZ1tdPiA9IHtcbiAgcHJvZDogWyctLXByb2QnLCAnLS1uby1vcHRpb25hbCddLFxuICBkZXY6IFsnLS1kZXYnLCAnLS1uby1vcHRpb25hbCddLFxuICBvcHRpb25hbDogW10sXG59O1xuXG5leHBvcnQgY29uc3QgcG5wbVBhY2thZ2VNYW5hZ2VyOiBQYWNrYWdlTWFuYWdlciA9IHtcbiAgc2x1ZzogJ3BucG0nLFxuICBuYW1lOiAncG5wbScsXG4gIGNvbW1hbmQ6ICdwbnBtJyxcbiAgaWNvbjogJ3BucG0nLFxuICBkb2NzOiB7XG4gICAgaG9tZXBhZ2U6ICdodHRwczovL3BucG0uaW8vcG5wbS1jbGknLFxuICAgIGF1ZGl0OiAnaHR0cHM6Ly9wbnBtLmlvL2NsaS9hdWRpdC8nLFxuICAgIG91dGRhdGVkOiAnaHR0cHM6Ly9wbnBtLmlvL2NsaS9vdXRkYXRlZCcsXG4gIH0sXG4gIGF1ZGl0OiB7XG4gICAgZ2V0Q29tbWFuZEFyZ3M6IGdyb3VwRGVwID0+IFtcbiAgICAgIC4uLkNPTU1PTl9BVURJVF9BUkdTLFxuICAgICAgLi4ucG5wbURlcGVuZGVuY3lPcHRpb25zW2dyb3VwRGVwXSxcbiAgICBdLFxuICAgIGlnbm9yZUV4aXRDb2RlOiB0cnVlLFxuICAgIHVuaWZ5UmVzdWx0OiBwbnBtVG9BdWRpdFJlc3VsdCxcbiAgICAvLyBvcHRpb25hbCBkZXBlbmRlbmNpZXMgZG9uJ3QgaGF2ZSBhbiBleGNsdXNpdmUgb3B0aW9uIHNvIHRoZXkgbmVlZCBkdXBsaWNhdGVzIGZpbHRlcmVkIG91dFxuICAgIHBvc3RQcm9jZXNzUmVzdWx0OiAocmVzdWx0czogQXVkaXRSZXN1bHRzKSA9PiB7XG4gICAgICBjb25zdCBkZXBHcm91cHMgPSBvYmplY3RUb0tleXMocmVzdWx0cyk7XG4gICAgICBjb25zdCBwcm9kRmlsdGVyID1cbiAgICAgICAgcmVzdWx0cy5vcHRpb25hbCAmJiByZXN1bHRzLnByb2RcbiAgICAgICAgICA/IGZpbHRlckF1ZGl0UmVzdWx0KHJlc3VsdHMub3B0aW9uYWwsICdpZCcsIHJlc3VsdHMucHJvZClcbiAgICAgICAgICA6IHJlc3VsdHMub3B0aW9uYWw7XG4gICAgICBjb25zdCBkZXZGaWx0ZXIgPVxuICAgICAgICBwcm9kRmlsdGVyICYmIHJlc3VsdHMuZGV2XG4gICAgICAgICAgPyBmaWx0ZXJBdWRpdFJlc3VsdChwcm9kRmlsdGVyLCAnaWQnLCByZXN1bHRzLmRldilcbiAgICAgICAgICA6IHJlc3VsdHMub3B0aW9uYWw7XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIC4uLihkZXBHcm91cHMuaW5jbHVkZXMoJ3Byb2QnKSAmJiB7IHByb2Q6IHJlc3VsdHMucHJvZCB9KSxcbiAgICAgICAgLi4uKGRlcEdyb3Vwcy5pbmNsdWRlcygnZGV2JykgJiYgeyBkZXY6IHJlc3VsdHMuZGV2IH0pLFxuICAgICAgICAuLi4ocmVzdWx0cy5vcHRpb25hbCAmJiB7IG9wdGlvbmFsOiBkZXZGaWx0ZXIgfSksXG4gICAgICB9O1xuICAgIH0sXG4gIH0sXG4gIG91dGRhdGVkOiB7XG4gICAgY29tbWFuZEFyZ3M6IENPTU1PTl9PVVREQVRFRF9BUkdTLFxuICAgIHVuaWZ5UmVzdWx0OiBwbnBtVG9PdXRkYXRlZFJlc3VsdCxcbiAgfSxcbn07XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL3BucG0vb3V0ZGF0ZWQtcmVzdWx0LnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnMvcG5wbVwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL3BucG0vb3V0ZGF0ZWQtcmVzdWx0LnRzXCI7aW1wb3J0IHsgb2JqZWN0VG9FbnRyaWVzIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB0eXBlIHsgT3V0ZGF0ZWRSZXN1bHQgfSBmcm9tICcuLi8uLi9ydW5uZXIvb3V0ZGF0ZWQvdHlwZXMuanMnO1xuaW1wb3J0IHR5cGUgeyBQbnBtT3V0ZGF0ZWRSZXN1bHRKc29uIH0gZnJvbSAnLi90eXBlcy5qcyc7XG5pbXBvcnQgeyBmaWx0ZXJPdXRXYXJuaW5ncyB9IGZyb20gJy4vdXRpbHMuanMnO1xuXG5leHBvcnQgZnVuY3Rpb24gcG5wbVRvT3V0ZGF0ZWRSZXN1bHQob3V0cHV0OiBzdHJpbmcpOiBPdXRkYXRlZFJlc3VsdCB7XG4gIGNvbnN0IHBucG1PdXRkYXRlZCA9IEpTT04ucGFyc2UoXG4gICAgZmlsdGVyT3V0V2FybmluZ3Mob3V0cHV0KSxcbiAgKSBhcyBQbnBtT3V0ZGF0ZWRSZXN1bHRKc29uO1xuXG4gIHJldHVybiBvYmplY3RUb0VudHJpZXMocG5wbU91dGRhdGVkKS5tYXAoXG4gICAgKFtuYW1lLCB7IGN1cnJlbnQsIGxhdGVzdCwgZGVwZW5kZW5jeVR5cGU6IHR5cGUgfV0pID0+ICh7XG4gICAgICBuYW1lLFxuICAgICAgY3VycmVudCxcbiAgICAgIGxhdGVzdCxcbiAgICAgIHR5cGUsXG4gICAgfSksXG4gICk7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL3lhcm4tY2xhc3NpYy9hdWRpdC1yZXN1bHQudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy95YXJuLWNsYXNzaWNcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy95YXJuLWNsYXNzaWMvYXVkaXQtcmVzdWx0LnRzXCI7aW1wb3J0IHsgZnJvbUpzb25MaW5lcyB9IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0UmVzdWx0LCBWdWxuZXJhYmlsaXR5IH0gZnJvbSAnLi4vLi4vcnVubmVyL2F1ZGl0L3R5cGVzLmpzJztcbmltcG9ydCB7IGZpbHRlckF1ZGl0UmVzdWx0IH0gZnJvbSAnLi4vLi4vcnVubmVyL3V0aWxzLmpzJztcbmltcG9ydCB0eXBlIHtcbiAgWWFybnYxQXVkaXRBZHZpc29yeSxcbiAgWWFybnYxQXVkaXRSZXN1bHRKc29uLFxuICBZYXJudjFBdWRpdFN1bW1hcnksXG59IGZyb20gJy4vdHlwZXMuanMnO1xuXG5leHBvcnQgZnVuY3Rpb24geWFybnYxVG9BdWRpdFJlc3VsdChvdXRwdXQ6IHN0cmluZyk6IEF1ZGl0UmVzdWx0IHtcbiAgY29uc3QgeWFybnYxUmVzdWx0ID0gZnJvbUpzb25MaW5lczxZYXJudjFBdWRpdFJlc3VsdEpzb24+KG91dHB1dCk7XG4gIGNvbnN0IFt5YXJudjFBZHZpc29yeSwgeWFybnYxU3VtbWFyeV0gPSB2YWxpZGF0ZVlhcm52MVJlc3VsdCh5YXJudjFSZXN1bHQpO1xuXG4gIGNvbnN0IHZ1bG5lcmFiaWxpdGllcyA9IHlhcm52MUFkdmlzb3J5Lm1hcChcbiAgICAoeyBkYXRhOiB7IHJlc29sdXRpb24sIGFkdmlzb3J5IH0gfSk6IFZ1bG5lcmFiaWxpdHkgPT4ge1xuICAgICAgY29uc3QgeyBpZCwgcGF0aCB9ID0gcmVzb2x1dGlvbjtcbiAgICAgIGNvbnN0IGRpcmVjdERlcGVuZGVuY3kgPSBwYXRoLnNsaWNlKDAsIHBhdGguaW5kZXhPZignPicpKTtcblxuICAgICAgY29uc3Qge1xuICAgICAgICBtb2R1bGVfbmFtZTogbmFtZSxcbiAgICAgICAgdGl0bGUsXG4gICAgICAgIHVybCxcbiAgICAgICAgc2V2ZXJpdHksXG4gICAgICAgIHZ1bG5lcmFibGVfdmVyc2lvbnM6IHZlcnNpb25SYW5nZSxcbiAgICAgICAgcmVjb21tZW5kYXRpb246IGZpeEluZm9ybWF0aW9uLFxuICAgICAgfSA9IGFkdmlzb3J5O1xuXG4gICAgICByZXR1cm4ge1xuICAgICAgICBuYW1lLFxuICAgICAgICB0aXRsZSxcbiAgICAgICAgaWQsXG4gICAgICAgIHVybCxcbiAgICAgICAgc2V2ZXJpdHksXG4gICAgICAgIHZlcnNpb25SYW5nZSxcbiAgICAgICAgZGlyZWN0RGVwZW5kZW5jeTogbmFtZSA9PT0gZGlyZWN0RGVwZW5kZW5jeSA/IHRydWUgOiBkaXJlY3REZXBlbmRlbmN5LFxuICAgICAgICBmaXhJbmZvcm1hdGlvbixcbiAgICAgIH07XG4gICAgfSxcbiAgKTtcblxuICBjb25zdCBzdW1tYXJ5ID0ge1xuICAgIC4uLnlhcm52MVN1bW1hcnkuZGF0YS52dWxuZXJhYmlsaXRpZXMsXG4gICAgdG90YWw6IE9iamVjdC52YWx1ZXMoeWFybnYxU3VtbWFyeS5kYXRhLnZ1bG5lcmFiaWxpdGllcykucmVkdWNlKFxuICAgICAgKGFjYywgYW1vdW50KSA9PiBhY2MgKyBhbW91bnQsXG4gICAgICAwLFxuICAgICksXG4gIH07XG5cbiAgLy8gZHVwbGljYXRlcyBhcmUgZmlsdGVyZWQgb3V0IGJhc2VkIG9uIHRoZWlyIElEXG4gIHJldHVybiBmaWx0ZXJBdWRpdFJlc3VsdCh7IHZ1bG5lcmFiaWxpdGllcywgc3VtbWFyeSB9LCAnaWQnKTtcbn1cblxuZnVuY3Rpb24gdmFsaWRhdGVZYXJudjFSZXN1bHQoXG4gIHJlc3VsdDogWWFybnYxQXVkaXRSZXN1bHRKc29uLFxuKTogW1lhcm52MUF1ZGl0QWR2aXNvcnlbXSwgWWFybnYxQXVkaXRTdW1tYXJ5XSB7XG4gIGNvbnN0IHN1bW1hcnkgPSByZXN1bHQuYXQoLTEpO1xuICBpZiAoc3VtbWFyeT8udHlwZSAhPT0gJ2F1ZGl0U3VtbWFyeScpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgWWFybiB2MSBhdWRpdCByZXN1bHQgLSBubyBzdW1tYXJ5IGZvdW5kLicpO1xuICB9XG5cbiAgY29uc3QgdnVsbmVyYWJpbGl0aWVzID0gcmVzdWx0LmZpbHRlcihcbiAgICAoaXRlbSk6IGl0ZW0gaXMgWWFybnYxQXVkaXRBZHZpc29yeSA9PiBpdGVtLnR5cGUgPT09ICdhdWRpdEFkdmlzb3J5JyxcbiAgKTtcblxuICByZXR1cm4gW3Z1bG5lcmFiaWxpdGllcywgc3VtbWFyeV07XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzL3lhcm4tY2xhc3NpYy9vdXRkYXRlZC1yZXN1bHQudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy95YXJuLWNsYXNzaWNcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy95YXJuLWNsYXNzaWMvb3V0ZGF0ZWQtcmVzdWx0LnRzXCI7aW1wb3J0IHtcbiAgZnJvbUpzb25MaW5lcyxcbiAgb2JqZWN0RnJvbUVudHJpZXMsXG4gIG9iamVjdFRvRW50cmllcyxcbiAgb2JqZWN0VG9LZXlzLFxufSBmcm9tICdAY29kZS1wdXNodXAvdXRpbHMnO1xuaW1wb3J0IHR5cGUge1xuICBPdXRkYXRlZERlcGVuZGVuY3ksXG4gIE91dGRhdGVkUmVzdWx0LFxufSBmcm9tICcuLi8uLi9ydW5uZXIvb3V0ZGF0ZWQvdHlwZXMuanMnO1xuaW1wb3J0IHtcbiAgUkVRVUlSRURfT1VUREFURURfRklFTERTLFxuICBvdXRkYXRlZHRvRmllbGRNYXBwZXIsXG59IGZyb20gJy4vY29uc3RhbnRzLmpzJztcbmltcG9ydCB7XG4gIHR5cGUgWWFybnYxRmllbGROYW1lLFxuICB0eXBlIFlhcm52MU91dGRhdGVkUmVzdWx0SnNvbixcbiAgeWFybnYxRmllbGROYW1lcyxcbn0gZnJvbSAnLi90eXBlcy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiB5YXJudjFUb091dGRhdGVkUmVzdWx0KG91dHB1dDogc3RyaW5nKTogT3V0ZGF0ZWRSZXN1bHQge1xuICBjb25zdCB5YXJudjFPdXRkYXRlZCA9IGZyb21Kc29uTGluZXM8WWFybnYxT3V0ZGF0ZWRSZXN1bHRKc29uPihvdXRwdXQpO1xuICBjb25zdCBmaWVsZHMgPSB5YXJudjFPdXRkYXRlZFsxXS5kYXRhLmhlYWQ7XG4gIGNvbnN0IGRlcGVuZGVuY2llcyA9IHlhcm52MU91dGRhdGVkWzFdLmRhdGEuYm9keTtcblxuICAvLyBubyBvdXRkYXRlZCBkZXBlbmRlbmNpZXNcbiAgaWYgKGRlcGVuZGVuY2llcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gW107XG4gIH1cblxuICAvLyBtYXAgZHluYW1pYyBmaWVsZHNcbiAgdmFsaWRhdGVPdXRkYXRlZEZpZWxkcyhmaWVsZHMpO1xuICBjb25zdCBpbmRleE1hcHBpbmcgPSBnZXRPdXRkYXRlZEZpZWxkSW5kZXhlcyhmaWVsZHMpO1xuXG4gIHJldHVybiBkZXBlbmRlbmNpZXMubWFwKFxuICAgIGRlcCA9PlxuICAgICAgb2JqZWN0RnJvbUVudHJpZXMoXG4gICAgICAgIG9iamVjdFRvS2V5cyhpbmRleE1hcHBpbmcpXG4gICAgICAgICAgLm1hcChmaWVsZCA9PiBbZmllbGQsIGRlcFtpbmRleE1hcHBpbmdbZmllbGRdXV0gYXMgY29uc3QpXG4gICAgICAgICAgLmZpbHRlcihcbiAgICAgICAgICAgIChlbnRyeSk6IGVudHJ5IGlzIFtrZXlvZiBPdXRkYXRlZERlcGVuZGVuY3ksIHN0cmluZ10gPT5cbiAgICAgICAgICAgICAgZW50cnlbMV0gIT0gbnVsbCxcbiAgICAgICAgICApLFxuICAgICAgKSBhcyBPdXRkYXRlZERlcGVuZGVuY3ksXG4gICk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB2YWxpZGF0ZU91dGRhdGVkRmllbGRzKGhlYWQ6IHN0cmluZ1tdKSB7XG4gIGNvbnN0IHJlbGV2YW50RmllbGRzID0gaGVhZC5maWx0ZXIoaXNZYXJudjFGaWVsZE5hbWUpO1xuICBpZiAoaGFzQWxsUmVxdWlyZWRGaWVsZHMocmVsZXZhbnRGaWVsZHMpKSB7XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgYFlhcm4gdjEgb3V0ZGF0ZWQ6IFRlbXBsYXRlIFske2hlYWQuam9pbihcbiAgICAgICcsICcsXG4gICAgKX1dIGRvZXMgbm90IGNvbnRhaW4gYWxsIHJlcXVpcmVkIGZpZWxkcyBbJHt5YXJudjFGaWVsZE5hbWVzLmpvaW4oJywgJyl9XWAsXG4gICk7XG59XG5cbmZ1bmN0aW9uIGlzWWFybnYxRmllbGROYW1lKHZhbHVlOiBzdHJpbmcpOiB2YWx1ZSBpcyBZYXJudjFGaWVsZE5hbWUge1xuICBjb25zdCBuYW1lczogcmVhZG9ubHkgc3RyaW5nW10gPSB5YXJudjFGaWVsZE5hbWVzO1xuICByZXR1cm4gbmFtZXMuaW5jbHVkZXModmFsdWUpO1xufVxuXG5mdW5jdGlvbiBoYXNBbGxSZXF1aXJlZEZpZWxkcyhoZWFkOiBZYXJudjFGaWVsZE5hbWVbXSkge1xuICByZXR1cm4gUkVRVUlSRURfT1VUREFURURfRklFTERTLmV2ZXJ5KGZpZWxkID0+IGhlYWQuaW5jbHVkZXMoZmllbGQpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldE91dGRhdGVkRmllbGRJbmRleGVzKGFsbDogc3RyaW5nW10pIHtcbiAgcmV0dXJuIG9iamVjdEZyb21FbnRyaWVzKFxuICAgIG9iamVjdFRvRW50cmllcyhvdXRkYXRlZHRvRmllbGRNYXBwZXIpLm1hcCgoW291dGRhdGVkRmllbGQsIHlhcm5GaWVsZF0pID0+IFtcbiAgICAgIG91dGRhdGVkRmllbGQsXG4gICAgICBhbGwuaW5kZXhPZih5YXJuRmllbGQpLFxuICAgIF0pLFxuICApO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcnVubmVyL2luZGV4LnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3J1bm5lclwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9ydW5uZXIvaW5kZXgudHNcIjtpbXBvcnQgeyB3cml0ZUZpbGUgfSBmcm9tICdub2RlOmZzL3Byb21pc2VzJztcbmltcG9ydCBwYXRoIGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgdHlwZSB7IFJ1bm5lckNvbmZpZyB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHtcbiAgZW5zdXJlRGlyZWN0b3J5RXhpc3RzLFxuICBleGVjdXRlUHJvY2VzcyxcbiAgZmlsZVBhdGhUb0NsaUFyZyxcbiAgaXNQcm9taXNlRnVsZmlsbGVkUmVzdWx0LFxuICBpc1Byb21pc2VSZWplY3RlZFJlc3VsdCxcbiAgb2JqZWN0RnJvbUVudHJpZXMsXG4gIHJlYWRKc29uRmlsZSxcbn0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB7XG4gIHR5cGUgQXVkaXRTZXZlcml0eSxcbiAgdHlwZSBEZXBlbmRlbmN5R3JvdXAsXG4gIHR5cGUgRmluYWxKU1BhY2thZ2VzUGx1Z2luQ29uZmlnLFxuICB0eXBlIFBhY2thZ2VKc29uUGF0aHMsXG4gIHR5cGUgUGFja2FnZU1hbmFnZXJJZCxcbiAgZGVwZW5kZW5jeUdyb3Vwcyxcbn0gZnJvbSAnLi4vY29uZmlnLmpzJztcbmltcG9ydCB7IGRlcGVuZGVuY3lHcm91cFRvTG9uZyB9IGZyb20gJy4uL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgeyBwYWNrYWdlTWFuYWdlcnMgfSBmcm9tICcuLi9wYWNrYWdlLW1hbmFnZXJzL3BhY2thZ2UtbWFuYWdlcnMuanMnO1xuaW1wb3J0IHsgYXVkaXRSZXN1bHRUb0F1ZGl0T3V0cHV0IH0gZnJvbSAnLi9hdWRpdC90cmFuc2Zvcm0uanMnO1xuaW1wb3J0IHR5cGUgeyBBdWRpdFJlc3VsdCB9IGZyb20gJy4vYXVkaXQvdHlwZXMuanMnO1xuaW1wb3J0IHsgUExVR0lOX0NPTkZJR19QQVRILCBSVU5ORVJfT1VUUFVUX1BBVEggfSBmcm9tICcuL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgeyBvdXRkYXRlZFJlc3VsdFRvQXVkaXRPdXRwdXQgfSBmcm9tICcuL291dGRhdGVkL3RyYW5zZm9ybS5qcyc7XG5pbXBvcnQgeyBmaW5kQWxsUGFja2FnZUpzb24sIGdldFRvdGFsRGVwZW5kZW5jaWVzIH0gZnJvbSAnLi91dGlscy5qcyc7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjcmVhdGVSdW5uZXJDb25maWcoXG4gIHNjcmlwdFBhdGg6IHN0cmluZyxcbiAgY29uZmlnOiBGaW5hbEpTUGFja2FnZXNQbHVnaW5Db25maWcsXG4pOiBQcm9taXNlPFJ1bm5lckNvbmZpZz4ge1xuICBhd2FpdCBlbnN1cmVEaXJlY3RvcnlFeGlzdHMocGF0aC5kaXJuYW1lKFBMVUdJTl9DT05GSUdfUEFUSCkpO1xuICBhd2FpdCB3cml0ZUZpbGUoUExVR0lOX0NPTkZJR19QQVRILCBKU09OLnN0cmluZ2lmeShjb25maWcpKTtcblxuICByZXR1cm4ge1xuICAgIGNvbW1hbmQ6ICdub2RlJyxcbiAgICBhcmdzOiBbZmlsZVBhdGhUb0NsaUFyZyhzY3JpcHRQYXRoKV0sXG4gICAgb3V0cHV0RmlsZTogUlVOTkVSX09VVFBVVF9QQVRILFxuICB9O1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZXhlY3V0ZVJ1bm5lcigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3Qge1xuICAgIHBhY2thZ2VNYW5hZ2VyLFxuICAgIGNoZWNrcyxcbiAgICBhdWRpdExldmVsTWFwcGluZyxcbiAgICBwYWNrYWdlSnNvblBhdGhzLFxuICAgIGRlcGVuZGVuY3lHcm91cHM6IGRlcEdyb3VwcyxcbiAgfSA9IGF3YWl0IHJlYWRKc29uRmlsZTxGaW5hbEpTUGFja2FnZXNQbHVnaW5Db25maWc+KFBMVUdJTl9DT05GSUdfUEFUSCk7XG5cbiAgY29uc3QgYXVkaXRSZXN1bHRzID0gY2hlY2tzLmluY2x1ZGVzKCdhdWRpdCcpXG4gICAgPyBhd2FpdCBwcm9jZXNzQXVkaXQocGFja2FnZU1hbmFnZXIsIGRlcEdyb3VwcywgYXVkaXRMZXZlbE1hcHBpbmcpXG4gICAgOiBbXTtcblxuICBjb25zdCBvdXRkYXRlZFJlc3VsdHMgPSBjaGVja3MuaW5jbHVkZXMoJ291dGRhdGVkJylcbiAgICA/IGF3YWl0IHByb2Nlc3NPdXRkYXRlZChwYWNrYWdlTWFuYWdlciwgZGVwR3JvdXBzLCBwYWNrYWdlSnNvblBhdGhzKVxuICAgIDogW107XG4gIGNvbnN0IGNoZWNrUmVzdWx0cyA9IFsuLi5hdWRpdFJlc3VsdHMsIC4uLm91dGRhdGVkUmVzdWx0c107XG5cbiAgYXdhaXQgZW5zdXJlRGlyZWN0b3J5RXhpc3RzKHBhdGguZGlybmFtZShSVU5ORVJfT1VUUFVUX1BBVEgpKTtcbiAgYXdhaXQgd3JpdGVGaWxlKFJVTk5FUl9PVVRQVVRfUEFUSCwgSlNPTi5zdHJpbmdpZnkoY2hlY2tSZXN1bHRzKSk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHByb2Nlc3NPdXRkYXRlZChcbiAgaWQ6IFBhY2thZ2VNYW5hZ2VySWQsXG4gIGRlcEdyb3VwczogRGVwZW5kZW5jeUdyb3VwW10sXG4gIHBhY2thZ2VKc29uUGF0aHM6IFBhY2thZ2VKc29uUGF0aHMsXG4pIHtcbiAgY29uc3QgcG0gPSBwYWNrYWdlTWFuYWdlcnNbaWRdO1xuICBjb25zdCB7IHN0ZG91dCwgc3RkZXJyIH0gPSBhd2FpdCBleGVjdXRlUHJvY2Vzcyh7XG4gICAgY29tbWFuZDogcG0uY29tbWFuZCxcbiAgICBhcmdzOiBwbS5vdXRkYXRlZC5jb21tYW5kQXJncyxcbiAgICBjd2Q6IHByb2Nlc3MuY3dkKCksXG4gICAgaWdub3JlRXhpdENvZGU6IHRydWUsIC8vIG91dGRhdGVkIHJldHVybnMgZXhpdCBjb2RlIDEgd2hlbiBvdXRkYXRlZCBkZXBlbmRlbmNpZXMgYXJlIGZvdW5kXG4gIH0pO1xuXG4gIC8vIFN1Y2Nlc3NmdWwgb3V0ZGF0ZWQgY2hlY2sgaGFzIGVtcHR5IHN0ZGVyclxuICBpZiAoc3RkZXJyKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBKUyBwYWNrYWdlcyBwbHVnaW46IG91dGRhdGVkIGVycm9yOiAke3N0ZGVycn1gKTtcbiAgfVxuXG4gIC8vIExvY2F0ZSBhbGwgcGFja2FnZS5qc29uIGZpbGVzIGluIHRoZSByZXBvc2l0b3J5IGlmIG5vdCBwcm92aWRlZFxuICBjb25zdCBmaW5hbFBhdGhzID0gQXJyYXkuaXNBcnJheShwYWNrYWdlSnNvblBhdGhzKVxuICAgID8gcGFja2FnZUpzb25QYXRoc1xuICAgIDogYXdhaXQgZmluZEFsbFBhY2thZ2VKc29uKCk7XG4gIGNvbnN0IGRlcFRvdGFscyA9IGF3YWl0IGdldFRvdGFsRGVwZW5kZW5jaWVzKGZpbmFsUGF0aHMpO1xuXG4gIGNvbnN0IG5vcm1hbGl6ZWRSZXN1bHQgPSBwbS5vdXRkYXRlZC51bmlmeVJlc3VsdChzdGRvdXQpO1xuICByZXR1cm4gZGVwR3JvdXBzLm1hcChkZXBHcm91cCA9PlxuICAgIG91dGRhdGVkUmVzdWx0VG9BdWRpdE91dHB1dChcbiAgICAgIG5vcm1hbGl6ZWRSZXN1bHQsXG4gICAgICBpZCxcbiAgICAgIGRlcEdyb3VwLFxuICAgICAgZGVwVG90YWxzW2RlcGVuZGVuY3lHcm91cFRvTG9uZ1tkZXBHcm91cF1dLFxuICAgICksXG4gICk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHByb2Nlc3NBdWRpdChcbiAgaWQ6IFBhY2thZ2VNYW5hZ2VySWQsXG4gIGRlcEdyb3VwczogRGVwZW5kZW5jeUdyb3VwW10sXG4gIGF1ZGl0TGV2ZWxNYXBwaW5nOiBBdWRpdFNldmVyaXR5LFxuKSB7XG4gIGNvbnN0IHBtID0gcGFja2FnZU1hbmFnZXJzW2lkXTtcbiAgY29uc3Qgc3VwcG9ydGVkQXVkaXREZXBHcm91cHMgPVxuICAgIHBtLmF1ZGl0LnN1cHBvcnRlZERlcEdyb3VwcyA/PyBkZXBlbmRlbmN5R3JvdXBzO1xuICBjb25zdCBjb21wYXRpYmxlQXVkaXREZXBHcm91cHMgPSBkZXBHcm91cHMuZmlsdGVyKGdyb3VwID0+XG4gICAgc3VwcG9ydGVkQXVkaXREZXBHcm91cHMuaW5jbHVkZXMoZ3JvdXApLFxuICApO1xuXG4gIGNvbnN0IGF1ZGl0UmVzdWx0cyA9IGF3YWl0IFByb21pc2UuYWxsU2V0dGxlZChcbiAgICBjb21wYXRpYmxlQXVkaXREZXBHcm91cHMubWFwKFxuICAgICAgYXN5bmMgKGRlcEdyb3VwKTogUHJvbWlzZTxbRGVwZW5kZW5jeUdyb3VwLCBBdWRpdFJlc3VsdF0+ID0+IHtcbiAgICAgICAgY29uc3QgeyBzdGRvdXQsIHN0ZGVyciB9ID0gYXdhaXQgZXhlY3V0ZVByb2Nlc3Moe1xuICAgICAgICAgIGNvbW1hbmQ6IHBtLmNvbW1hbmQsXG4gICAgICAgICAgYXJnczogcG0uYXVkaXQuZ2V0Q29tbWFuZEFyZ3MoZGVwR3JvdXApLFxuICAgICAgICAgIGN3ZDogcHJvY2Vzcy5jd2QoKSxcbiAgICAgICAgICBpZ25vcmVFeGl0Q29kZTogcG0uYXVkaXQuaWdub3JlRXhpdENvZGUsXG4gICAgICAgIH0pO1xuICAgICAgICAvLyBTdWNjZXNzZnVsIGF1ZGl0IGNoZWNrIGhhcyBlbXB0eSBzdGRlcnJcbiAgICAgICAgaWYgKHN0ZGVycikge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgSlMgcGFja2FnZXMgcGx1Z2luOiBhdWRpdCBlcnJvcjogJHtzdGRlcnJ9YCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIFtkZXBHcm91cCwgcG0uYXVkaXQudW5pZnlSZXN1bHQoc3Rkb3V0KV07XG4gICAgICB9LFxuICAgICksXG4gICk7XG5cbiAgY29uc3QgcmVqZWN0ZWQgPSBhdWRpdFJlc3VsdHMuZmlsdGVyKGlzUHJvbWlzZVJlamVjdGVkUmVzdWx0KTtcbiAgaWYgKHJlamVjdGVkLmxlbmd0aCA+IDApIHtcbiAgICByZWplY3RlZC5mb3JFYWNoKHJlc3VsdCA9PiB7XG4gICAgICBjb25zb2xlLmVycm9yKHJlc3VsdC5yZWFzb24pO1xuICAgIH0pO1xuXG4gICAgdGhyb3cgbmV3IEVycm9yKGBKUyBQYWNrYWdlcyBwbHVnaW46IFJ1bm5pbmcgJHtwbS5uYW1lfSBhdWRpdCBmYWlsZWQuYCk7XG4gIH1cblxuICBjb25zdCBmdWxmaWxsZWQgPSBvYmplY3RGcm9tRW50cmllcyhcbiAgICBhdWRpdFJlc3VsdHMuZmlsdGVyKGlzUHJvbWlzZUZ1bGZpbGxlZFJlc3VsdCkubWFwKHggPT4geC52YWx1ZSksXG4gICk7XG5cbiAgY29uc3QgdW5pcXVlUmVzdWx0cyA9IHBtLmF1ZGl0LnBvc3RQcm9jZXNzUmVzdWx0Py4oZnVsZmlsbGVkKSA/PyBmdWxmaWxsZWQ7XG5cbiAgcmV0dXJuIGNvbXBhdGlibGVBdWRpdERlcEdyb3Vwcy5tYXAoZGVwR3JvdXAgPT5cbiAgICBhdWRpdFJlc3VsdFRvQXVkaXRPdXRwdXQoXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLW5vbi1udWxsLWFzc2VydGlvblxuICAgICAgdW5pcXVlUmVzdWx0c1tkZXBHcm91cF0hLFxuICAgICAgaWQsXG4gICAgICBkZXBHcm91cCxcbiAgICAgIGF1ZGl0TGV2ZWxNYXBwaW5nLFxuICAgICksXG4gICk7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9ydW5uZXIvYXVkaXQvdHJhbnNmb3JtLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3J1bm5lci9hdWRpdFwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9ydW5uZXIvYXVkaXQvdHJhbnNmb3JtLnRzXCI7aW1wb3J0IHsgbWQgfSBmcm9tICdidWlsZC1tZCc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0T3V0cHV0LCBJc3N1ZSB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgb2JqZWN0VG9FbnRyaWVzIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB7XG4gIHR5cGUgQXVkaXRTZXZlcml0eSxcbiAgdHlwZSBEZXBlbmRlbmN5R3JvdXAsXG4gIHR5cGUgUGFja2FnZU1hbmFnZXJJZCxcbiAgcGFja2FnZUF1ZGl0TGV2ZWxzLFxufSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xuaW1wb3J0IHsgYXVkaXRTY29yZU1vZGlmaWVycyB9IGZyb20gJy4vY29uc3RhbnRzLmpzJztcbmltcG9ydCB0eXBlIHsgQXVkaXRSZXN1bHQsIEF1ZGl0U3VtbWFyeSwgVnVsbmVyYWJpbGl0eSB9IGZyb20gJy4vdHlwZXMuanMnO1xuXG5leHBvcnQgZnVuY3Rpb24gYXVkaXRSZXN1bHRUb0F1ZGl0T3V0cHV0KFxuICByZXN1bHQ6IEF1ZGl0UmVzdWx0LFxuICBpZDogUGFja2FnZU1hbmFnZXJJZCxcbiAgZGVwR3JvdXA6IERlcGVuZGVuY3lHcm91cCxcbiAgYXVkaXRMZXZlbE1hcHBpbmc6IEF1ZGl0U2V2ZXJpdHksXG4pOiBBdWRpdE91dHB1dCB7XG4gIGNvbnN0IGlzc3VlcyA9IHZ1bG5lcmFiaWxpdGllc1RvSXNzdWVzKFxuICAgIHJlc3VsdC52dWxuZXJhYmlsaXRpZXMsXG4gICAgYXVkaXRMZXZlbE1hcHBpbmcsXG4gICk7XG5cbiAgcmV0dXJuIHtcbiAgICBzbHVnOiBgJHtpZH0tYXVkaXQtJHtkZXBHcm91cH1gLFxuICAgIHNjb3JlOiBjYWxjdWxhdGVBdWRpdFNjb3JlKHJlc3VsdC5zdW1tYXJ5KSxcbiAgICB2YWx1ZTogcmVzdWx0LnN1bW1hcnkudG90YWwsXG4gICAgZGlzcGxheVZhbHVlOiBzdW1tYXJ5VG9EaXNwbGF5VmFsdWUocmVzdWx0LnN1bW1hcnkpLFxuICAgIGRldGFpbHM6IHsgaXNzdWVzIH0sXG4gIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjYWxjdWxhdGVBdWRpdFNjb3JlKHN0YXRzOiBBdWRpdFN1bW1hcnkpIHtcbiAgaWYgKHN0YXRzLnRvdGFsID09PSAwKSB7XG4gICAgcmV0dXJuIDE7XG4gIH1cblxuICByZXR1cm4gb2JqZWN0VG9FbnRyaWVzKHN0YXRzKS5yZWR1Y2U8bnVtYmVyPihcbiAgICAoc2NvcmUsIFtsZXZlbCwgdnVsbmVyYWJpbGl0aWVzXSkgPT4ge1xuICAgICAgaWYgKGxldmVsID09PSAndG90YWwnKSB7XG4gICAgICAgIHJldHVybiBzY29yZTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgcmVkdWNlZFNjb3JlID0gc2NvcmUgLSBhdWRpdFNjb3JlTW9kaWZpZXJzW2xldmVsXSAqIHZ1bG5lcmFiaWxpdGllcztcbiAgICAgIHJldHVybiBNYXRoLm1heChyZWR1Y2VkU2NvcmUsIDApO1xuICAgIH0sXG4gICAgMSxcbiAgKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHN1bW1hcnlUb0Rpc3BsYXlWYWx1ZShzdW1tYXJ5OiBBdWRpdFN1bW1hcnkpOiBzdHJpbmcge1xuICBpZiAoc3VtbWFyeS50b3RhbCA9PT0gMCkge1xuICAgIHJldHVybiAnMCB2dWxuZXJhYmlsaXRpZXMnO1xuICB9XG5cbiAgY29uc3QgdnVsbmVyYWJpbGl0eVN0YXRzID0gcGFja2FnZUF1ZGl0TGV2ZWxzXG4gICAgLm1hcChsZXZlbCA9PiAoc3VtbWFyeVtsZXZlbF0gPiAwID8gYCR7c3VtbWFyeVtsZXZlbF19ICR7bGV2ZWx9YCA6ICcnKSlcbiAgICAuZmlsdGVyKHRleHQgPT4gdGV4dCAhPT0gJycpXG4gICAgLmpvaW4oJywgJyk7XG4gIHJldHVybiBgJHtzdW1tYXJ5LnRvdGFsfSAke1xuICAgIHN1bW1hcnkudG90YWwgPT09IDEgPyAndnVsbmVyYWJpbGl0eScgOiAndnVsbmVyYWJpbGl0aWVzJ1xuICB9ICgke3Z1bG5lcmFiaWxpdHlTdGF0c30pYDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHZ1bG5lcmFiaWxpdGllc1RvSXNzdWVzKFxuICB2dWxuZXJhYmlsaXRpZXM6IFZ1bG5lcmFiaWxpdHlbXSxcbiAgYXVkaXRMZXZlbE1hcHBpbmc6IEF1ZGl0U2V2ZXJpdHksXG4pOiBJc3N1ZVtdIHtcbiAgaWYgKHZ1bG5lcmFiaWxpdGllcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gW107XG4gIH1cblxuICByZXR1cm4gdnVsbmVyYWJpbGl0aWVzLm1hcCgoZGV0YWlsKTogSXNzdWUgPT4ge1xuICAgIGNvbnN0IHZlcnNpb25SYW5nZSA9XG4gICAgICBkZXRhaWwudmVyc2lvblJhbmdlID09PSAnKidcbiAgICAgICAgPyBtZGAke21kLmJvbGQoJ2FsbCcpfSB2ZXJzaW9uc2BcbiAgICAgICAgOiBtZGB2ZXJzaW9ucyAke21kLmJvbGQoZGV0YWlsLnZlcnNpb25SYW5nZSl9YDtcbiAgICBjb25zdCBkaXJlY3REZXBlbmRlbmN5ID1cbiAgICAgIHR5cGVvZiBkZXRhaWwuZGlyZWN0RGVwZW5kZW5jeSA9PT0gJ3N0cmluZycgJiZcbiAgICAgIGRldGFpbC5kaXJlY3REZXBlbmRlbmN5ICE9PSAnJ1xuICAgICAgICA/IG1kLmNvZGUoZGV0YWlsLmRpcmVjdERlcGVuZGVuY3kpXG4gICAgICAgIDogJyc7XG4gICAgY29uc3QgZGVwSGllcmFyY2h5ID0gZGlyZWN0RGVwZW5kZW5jeVxuICAgICAgPyBtZGAke2RpcmVjdERlcGVuZGVuY3l9J3MgZGVwZW5kZW5jeSAke21kLmNvZGUoZGV0YWlsLm5hbWUpfWBcbiAgICAgIDogbWRgJHttZC5jb2RlKGRldGFpbC5uYW1lKX0gZGVwZW5kZW5jeWA7XG5cbiAgICBjb25zdCB2dWxuZXJhYmlsaXR5U3VtbWFyeSA9IG1kYGhhcyBhICR7bWQuYm9sZChcbiAgICAgIGRldGFpbC5zZXZlcml0eSxcbiAgICApfSB2dWxuZXJhYmlsaXR5IGluICR7dmVyc2lvblJhbmdlfS5gO1xuICAgIGNvbnN0IGZpeEluZm8gPSBkZXRhaWwuZml4SW5mb3JtYXRpb24gPyBgICR7ZGV0YWlsLmZpeEluZm9ybWF0aW9ufWAgOiAnJztcbiAgICBjb25zdCBhZGRpdGlvbmFsSW5mbyA9XG4gICAgICBkZXRhaWwudGl0bGUgIT0gbnVsbCAmJiBkZXRhaWwudXJsICE9IG51bGxcbiAgICAgICAgPyBtZGAgTW9yZSBpbmZvcm1hdGlvbjogJHttZC5saW5rKGRldGFpbC51cmwsIGRldGFpbC50aXRsZSl9YFxuICAgICAgICA6ICcnO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIG1lc3NhZ2U6XG4gICAgICAgIG1kYCR7ZGVwSGllcmFyY2h5fSAke3Z1bG5lcmFiaWxpdHlTdW1tYXJ5fSR7Zml4SW5mb30ke2FkZGl0aW9uYWxJbmZvfWAudG9TdHJpbmcoKSxcbiAgICAgIHNldmVyaXR5OiBhdWRpdExldmVsTWFwcGluZ1tkZXRhaWwuc2V2ZXJpdHldLFxuICAgIH07XG4gIH0pO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcnVubmVyL2NvbnN0YW50cy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9ydW5uZXJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcnVubmVyL2NvbnN0YW50cy50c1wiO2ltcG9ydCBwYXRoIGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgeyBwbHVnaW5Xb3JrRGlyIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcblxuZXhwb3J0IGNvbnN0IFdPUktESVIgPSBwbHVnaW5Xb3JrRGlyKCdqcy1wYWNrYWdlcycpO1xuZXhwb3J0IGNvbnN0IFJVTk5FUl9PVVRQVVRfUEFUSCA9IHBhdGguam9pbihXT1JLRElSLCAncnVubmVyLW91dHB1dC5qc29uJyk7XG5leHBvcnQgY29uc3QgUExVR0lOX0NPTkZJR19QQVRIID0gcGF0aC5qb2luKFxuICBwcm9jZXNzLmN3ZCgpLFxuICBXT1JLRElSLFxuICAncGx1Z2luLWNvbmZpZy5qc29uJyxcbik7XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9ydW5uZXIvb3V0ZGF0ZWQvdHJhbnNmb3JtLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3J1bm5lci9vdXRkYXRlZFwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9ydW5uZXIvb3V0ZGF0ZWQvdHJhbnNmb3JtLnRzXCI7aW1wb3J0IHsgbWQgfSBmcm9tICdidWlsZC1tZCc7XG5pbXBvcnQgeyBjbGVhbiwgZGlmZiwgbmVxIH0gZnJvbSAnc2VtdmVyJztcbmltcG9ydCB0eXBlIHsgQXVkaXRPdXRwdXQsIElzc3VlIH0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5pbXBvcnQgeyBvYmplY3RGcm9tRW50cmllcywgcGx1cmFsaXplIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB0eXBlIHsgRGVwZW5kZW5jeUdyb3VwLCBQYWNrYWdlTWFuYWdlcklkIH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcbmltcG9ydCB7IGRlcGVuZGVuY3lHcm91cFRvTG9uZyB9IGZyb20gJy4uLy4uL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgeyBSRUxFQVNFX1RZUEVTLCBvdXRkYXRlZFNldmVyaXR5IH0gZnJvbSAnLi9jb25zdGFudHMuanMnO1xuaW1wb3J0IHR5cGUgeyBPdXRkYXRlZFJlc3VsdCwgUGFja2FnZVZlcnNpb24gfSBmcm9tICcuL3R5cGVzLmpzJztcblxuZXhwb3J0IGZ1bmN0aW9uIG91dGRhdGVkUmVzdWx0VG9BdWRpdE91dHB1dChcbiAgcmVzdWx0OiBPdXRkYXRlZFJlc3VsdCxcbiAgcGFja2FnZU1hbmFnZXI6IFBhY2thZ2VNYW5hZ2VySWQsXG4gIGRlcEdyb3VwOiBEZXBlbmRlbmN5R3JvdXAsXG4gIHRvdGFsRGVwczogbnVtYmVyLFxuKTogQXVkaXRPdXRwdXQge1xuICBjb25zdCByZWxldmFudERlcGVuZGVuY2llczogT3V0ZGF0ZWRSZXN1bHQgPSByZXN1bHQuZmlsdGVyKFxuICAgIGRlcCA9PiBkZXAudHlwZSA9PT0gZGVwZW5kZW5jeUdyb3VwVG9Mb25nW2RlcEdyb3VwXSxcbiAgKTtcblxuICBjb25zdCB2YWxpZERlcGVuZGVuY2llcyA9IHJlbGV2YW50RGVwZW5kZW5jaWVzXG4gICAgLm1hcChkZXAgPT4gKHtcbiAgICAgIC4uLmRlcCxcbiAgICAgIGN1cnJlbnQ6IGNsZWFuKGRlcC5jdXJyZW50KSxcbiAgICAgIGxhdGVzdDogY2xlYW4oZGVwLmxhdGVzdCksXG4gICAgfSkpXG4gICAgLmZpbHRlcihcbiAgICAgIChkZXApOiBkZXAgaXMgT3V0ZGF0ZWRSZXN1bHRbbnVtYmVyXSA9PlxuICAgICAgICBkZXAuY3VycmVudCAhPSBudWxsICYmIGRlcC5sYXRlc3QgIT0gbnVsbCxcbiAgICApO1xuXG4gIGNvbnN0IG91dGRhdGVkRGVwZW5kZW5jaWVzID0gdmFsaWREZXBlbmRlbmNpZXMuZmlsdGVyKGRlcCA9PlxuICAgIG5lcShkZXAuY3VycmVudCwgZGVwLmxhdGVzdCksXG4gICk7XG5cbiAgY29uc3Qgb3V0ZGF0ZWRTdGF0cyA9IG91dGRhdGVkRGVwZW5kZW5jaWVzLnJlZHVjZShcbiAgICAoYWNjLCBkZXApID0+IHtcbiAgICAgIGNvbnN0IG91dGRhdGVkTGV2ZWwgPSBkaWZmKGRlcC5jdXJyZW50LCBkZXAubGF0ZXN0KTtcbiAgICAgIGlmIChvdXRkYXRlZExldmVsID09IG51bGwpIHtcbiAgICAgICAgcmV0dXJuIGFjYztcbiAgICAgIH1cbiAgICAgIHJldHVybiB7IC4uLmFjYywgW291dGRhdGVkTGV2ZWxdOiBhY2Nbb3V0ZGF0ZWRMZXZlbF0gKyAxIH07XG4gICAgfSxcbiAgICBvYmplY3RGcm9tRW50cmllcyhSRUxFQVNFX1RZUEVTLm1hcCh2ZXJzaW9uVHlwZSA9PiBbdmVyc2lvblR5cGUsIDBdKSksXG4gICk7XG5cbiAgY29uc3QgaXNzdWVzID1cbiAgICBvdXRkYXRlZERlcGVuZGVuY2llcy5sZW5ndGggPT09IDBcbiAgICAgID8gW11cbiAgICAgIDogb3V0ZGF0ZWRUb0lzc3VlcyhvdXRkYXRlZERlcGVuZGVuY2llcyk7XG5cbiAgcmV0dXJuIHtcbiAgICBzbHVnOiBgJHtwYWNrYWdlTWFuYWdlcn0tb3V0ZGF0ZWQtJHtkZXBHcm91cH1gLFxuICAgIHNjb3JlOiBjYWxjdWxhdGVPdXRkYXRlZFNjb3JlKG91dGRhdGVkU3RhdHMubWFqb3IsIHRvdGFsRGVwcyksXG4gICAgdmFsdWU6IG91dGRhdGVkRGVwZW5kZW5jaWVzLmxlbmd0aCxcbiAgICBkaXNwbGF5VmFsdWU6IG91dGRhdGVkVG9EaXNwbGF5VmFsdWUob3V0ZGF0ZWRTdGF0cyksXG4gICAgZGV0YWlsczogeyBpc3N1ZXMgfSxcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNhbGN1bGF0ZU91dGRhdGVkU2NvcmUoXG4gIG1ham9yT3V0ZGF0ZWQ6IG51bWJlcixcbiAgdG90YWxEZXBzOiBudW1iZXIsXG4pIHtcbiAgcmV0dXJuIHRvdGFsRGVwcyA+IDAgPyAodG90YWxEZXBzIC0gbWFqb3JPdXRkYXRlZCkgLyB0b3RhbERlcHMgOiAxO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gb3V0ZGF0ZWRUb0Rpc3BsYXlWYWx1ZShzdGF0czogUGFja2FnZVZlcnNpb24pIHtcbiAgY29uc3QgdG90YWwgPSBPYmplY3QudmFsdWVzKHN0YXRzKS5yZWR1Y2UoKGFjYywgdmFsdWUpID0+IGFjYyArIHZhbHVlLCAwKTtcblxuICBjb25zdCB2ZXJzaW9uQnJlYWtkb3duID0gUkVMRUFTRV9UWVBFUy5tYXAodmVyc2lvbiA9PlxuICAgIHN0YXRzW3ZlcnNpb25dID4gMCA/IGAke3N0YXRzW3ZlcnNpb25dfSAke3ZlcnNpb259YCA6ICcnLFxuICApLmZpbHRlcih0ZXh0ID0+IHRleHQgIT09ICcnKTtcblxuICBpZiAodmVyc2lvbkJyZWFrZG93bi5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gJ2FsbCBkZXBlbmRlbmNpZXMgYXJlIHVwIHRvIGRhdGUnO1xuICB9XG5cbiAgaWYgKHZlcnNpb25CcmVha2Rvd24ubGVuZ3RoID4gMSkge1xuICAgIHJldHVybiBgJHt0b3RhbH0gb3V0ZGF0ZWQgcGFja2FnZSB2ZXJzaW9ucyAoJHt2ZXJzaW9uQnJlYWtkb3duLmpvaW4oXG4gICAgICAnLCAnLFxuICAgICl9KWA7XG4gIH1cblxuICByZXR1cm4gYCR7dmVyc2lvbkJyZWFrZG93blswXX0gb3V0ZGF0ZWQgcGFja2FnZSAke3BsdXJhbGl6ZShcbiAgICAndmVyc2lvbicsXG4gICAgdG90YWwsXG4gICl9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG91dGRhdGVkVG9Jc3N1ZXMoZGVwZW5kZW5jaWVzOiBPdXRkYXRlZFJlc3VsdCk6IElzc3VlW10ge1xuICByZXR1cm4gZGVwZW5kZW5jaWVzLm1hcDxJc3N1ZT4oZGVwID0+IHtcbiAgICBjb25zdCB7IG5hbWUsIGN1cnJlbnQsIGxhdGVzdCwgdXJsIH0gPSBkZXA7XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1ub24tbnVsbC1hc3NlcnRpb25cbiAgICBjb25zdCBvdXRkYXRlZExldmVsID0gZGlmZihjdXJyZW50LCBsYXRlc3QpITtcbiAgICBjb25zdCBwYWNrYWdlUmVmZXJlbmNlID1cbiAgICAgIHVybCA9PSBudWxsID8gbWQuY29kZShuYW1lKSA6IG1kLmxpbmsodXJsLCBtZC5jb2RlKG5hbWUpKTtcblxuICAgIHJldHVybiB7XG4gICAgICBtZXNzYWdlOiBtZGBQYWNrYWdlICR7cGFja2FnZVJlZmVyZW5jZX0gcmVxdWlyZXMgYSAke21kLmJvbGQoXG4gICAgICAgIG91dGRhdGVkTGV2ZWwsXG4gICAgICApfSB1cGRhdGUgZnJvbSAke21kLmJvbGQoY3VycmVudCl9IHRvICR7bWQuYm9sZChsYXRlc3QpfS5gLnRvU3RyaW5nKCksXG4gICAgICBzZXZlcml0eTogb3V0ZGF0ZWRTZXZlcml0eVtvdXRkYXRlZExldmVsXSxcbiAgICB9O1xuICB9KTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3J1bm5lci9vdXRkYXRlZC9jb25zdGFudHMudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcnVubmVyL291dGRhdGVkXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3J1bm5lci9vdXRkYXRlZC9jb25zdGFudHMudHNcIjtpbXBvcnQgdHlwZSB7IFJlbGVhc2VUeXBlIH0gZnJvbSAnc2VtdmVyJztcbmltcG9ydCB0eXBlIHsgSXNzdWVTZXZlcml0eSB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgb2JqZWN0VG9LZXlzIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcblxuZXhwb3J0IGNvbnN0IG91dGRhdGVkU2V2ZXJpdHk6IFJlY29yZDxSZWxlYXNlVHlwZSwgSXNzdWVTZXZlcml0eT4gPSB7XG4gIG1ham9yOiAnZXJyb3InLFxuICBwcmVtYWpvcjogJ2luZm8nLFxuICBtaW5vcjogJ3dhcm5pbmcnLFxuICBwcmVtaW5vcjogJ2luZm8nLFxuICBwYXRjaDogJ2luZm8nLFxuICBwcmVwYXRjaDogJ2luZm8nLFxuICBwcmVyZWxlYXNlOiAnaW5mbycsXG59O1xuXG4vLyBSRUxFQVNFX1RZUEVTIGRpcmVjdGx5IGV4cG9ydGVkIGZyb20gc2VtdmVyIGRvbid0IHdvcmsgb3V0IG9mIHRoZSBib3hcbmV4cG9ydCBjb25zdCBSRUxFQVNFX1RZUEVTID0gb2JqZWN0VG9LZXlzKG91dGRhdGVkU2V2ZXJpdHkpO1xuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy9kZXJpdmUtcGFja2FnZS1tYW5hZ2VyLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnNcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy9kZXJpdmUtcGFja2FnZS1tYW5hZ2VyLnRzXCI7aW1wb3J0IHsgcmVhZEZpbGUgfSBmcm9tICdub2RlOmZzL3Byb21pc2VzJztcbmltcG9ydCBwYXRoIGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgeyBmaWxlRXhpc3RzIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB0eXBlIHsgUGFja2FnZU1hbmFnZXJJZCB9IGZyb20gJy4uL2NvbmZpZy5qcyc7XG5pbXBvcnQgeyBkZXJpdmVZYXJuVmVyc2lvbiB9IGZyb20gJy4vZGVyaXZlLXlhcm4uanMnO1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZGVyaXZlUGFja2FnZU1hbmFnZXJJblBhY2thZ2VKc29uKFxuICBjdXJyZW50RGlyID0gcHJvY2Vzcy5jd2QoKSxcbikge1xuICBpZiAoYXdhaXQgZmlsZUV4aXN0cyhwYXRoLmpvaW4oY3VycmVudERpciwgJ3BhY2thZ2UuanNvbicpKSkge1xuICAgIGNvbnN0IGNvbnRlbnQgPSBKU09OLnBhcnNlKFxuICAgICAgKGF3YWl0IHJlYWRGaWxlKHBhdGguam9pbigncGFja2FnZS5qc29uJykpKS50b1N0cmluZygpLFxuICAgICkgYXMgeyBwYWNrYWdlTWFuYWdlcj86IHN0cmluZyB9O1xuICAgIGNvbnN0IHsgcGFja2FnZU1hbmFnZXI6IHBhY2thZ2VNYW5hZ2VyRGF0YSA9ICcnIH0gPSBjb250ZW50O1xuXG4gICAgY29uc3QgW21hbmFnZXIgPSAnJywgdmVyc2lvbiA9ICcnXSA9IHBhY2thZ2VNYW5hZ2VyRGF0YS5zcGxpdCgnQCcpO1xuXG4gICAgaWYgKG1hbmFnZXIgPT09ICducG0nKSB7XG4gICAgICByZXR1cm4gbWFuYWdlcjtcbiAgICB9XG4gICAgaWYgKG1hbmFnZXIgPT09ICdwbnBtJykge1xuICAgICAgcmV0dXJuIG1hbmFnZXI7XG4gICAgfVxuICAgIGlmIChtYW5hZ2VyID09PSAneWFybicpIHtcbiAgICAgIGNvbnN0IG1ham9yVmVyc2lvbiA9IE51bWJlcih2ZXJzaW9uLnNwbGl0KCcuJylbMF0pO1xuICAgICAgcmV0dXJuIG1ham9yVmVyc2lvbiA+IDEgPyAneWFybi1tb2Rlcm4nIDogJ3lhcm4tY2xhc3NpYyc7XG4gICAgfVxuICB9XG4gIHJldHVybiBmYWxzZTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGRlcml2ZVBhY2thZ2VNYW5hZ2VyKFxuICBjdXJyZW50RGlyID0gcHJvY2Vzcy5jd2QoKSxcbik6IFByb21pc2U8UGFja2FnZU1hbmFnZXJJZD4ge1xuICBjb25zdCBwa2dNYW5hZ2VyRnJvbVBhY2thZ2VKc29uID1cbiAgICBhd2FpdCBkZXJpdmVQYWNrYWdlTWFuYWdlckluUGFja2FnZUpzb24oY3VycmVudERpcik7XG4gIGlmIChwa2dNYW5hZ2VyRnJvbVBhY2thZ2VKc29uKSB7XG4gICAgcmV0dXJuIHBrZ01hbmFnZXJGcm9tUGFja2FnZUpzb247XG4gIH1cblxuICAvLyBDaGVjayBmb3IgbG9jayBmaWxlc1xuICBpZiAoYXdhaXQgZmlsZUV4aXN0cyhwYXRoLmpvaW4oY3VycmVudERpciwgJ3BhY2thZ2UtbG9jay5qc29uJykpKSB7XG4gICAgcmV0dXJuICducG0nO1xuICB9IGVsc2UgaWYgKGF3YWl0IGZpbGVFeGlzdHMocGF0aC5qb2luKGN1cnJlbnREaXIsICdwbnBtLWxvY2sueWFtbCcpKSkge1xuICAgIHJldHVybiAncG5wbSc7XG4gIH0gZWxzZSBpZiAoYXdhaXQgZmlsZUV4aXN0cyhwYXRoLmpvaW4oY3VycmVudERpciwgJ3lhcm4ubG9jaycpKSkge1xuICAgIGNvbnN0IHlhcm5WZXJzaW9uID0gYXdhaXQgZGVyaXZlWWFyblZlcnNpb24oKTtcbiAgICBpZiAoeWFyblZlcnNpb24pIHtcbiAgICAgIHJldHVybiB5YXJuVmVyc2lvbjtcbiAgICB9XG4gIH1cblxuICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgJ0NvdWxkIG5vdCBkZXRlY3QgcGFja2FnZSBtYW5hZ2VyLiBQbGVhc2UgcHJvdmlkZSBpdCBpbiB0aGUganMtcGFja2FnZXMgcGx1Z2luIGNvbmZpZy4nLFxuICApO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWpzLXBhY2thZ2VzL3NyYy9saWIvcGFja2FnZS1tYW5hZ2Vycy9kZXJpdmUteWFybi50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2xpYi9wYWNrYWdlLW1hbmFnZXJzXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1qcy1wYWNrYWdlcy9zcmMvbGliL3BhY2thZ2UtbWFuYWdlcnMvZGVyaXZlLXlhcm4udHNcIjtpbXBvcnQgeyBleGVjdXRlUHJvY2VzcyB9IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBkZXJpdmVZYXJuVmVyc2lvbigpIHtcbiAgY29uc3QgeyBzdGRvdXQgfSA9IGF3YWl0IGV4ZWN1dGVQcm9jZXNzKHtcbiAgICBjb21tYW5kOiAneWFybicsXG4gICAgYXJnczogWyctdiddLFxuICB9KTtcblxuICBjb25zdCB5YXJuVmVyc2lvbiA9IE51bWJlci5wYXJzZUludChzdGRvdXQudG9TdHJpbmcoKS50cmltKCkuYXQoMCkgPz8gJycsIDEwKTtcbiAgaWYgKHlhcm5WZXJzaW9uID49IDIpIHtcbiAgICByZXR1cm4gJ3lhcm4tbW9kZXJuJztcbiAgfSBlbHNlIGlmICh5YXJuVmVyc2lvbiA9PT0gMSkge1xuICAgIHJldHVybiAneWFybi1jbGFzc2ljJztcbiAgfVxuICByZXR1cm4gZmFsc2U7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL2xpZ2h0aG91c2UtcGx1Z2luLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9saWdodGhvdXNlLXBsdWdpbi50c1wiO2ltcG9ydCB7IGNyZWF0ZVJlcXVpcmUgfSBmcm9tICdub2RlOm1vZHVsZSc7XG5pbXBvcnQgdHlwZSB7IFBsdWdpbkNvbmZpZyB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgTElHSFRIT1VTRV9QTFVHSU5fU0xVRyB9IGZyb20gJy4vY29uc3RhbnRzLmpzJztcbmltcG9ydCB7IG5vcm1hbGl6ZUZsYWdzIH0gZnJvbSAnLi9ub3JtYWxpemUtZmxhZ3MuanMnO1xuaW1wb3J0IHtcbiAgTElHSFRIT1VTRV9HUk9VUFMsXG4gIExJR0hUSE9VU0VfTkFWSUdBVElPTl9BVURJVFMsXG59IGZyb20gJy4vcnVubmVyL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgeyBjcmVhdGVSdW5uZXJGdW5jdGlvbiB9IGZyb20gJy4vcnVubmVyL3J1bm5lci5qcyc7XG5pbXBvcnQgdHlwZSB7IExpZ2h0aG91c2VPcHRpb25zIH0gZnJvbSAnLi90eXBlcy5qcyc7XG5pbXBvcnQgeyBmaWx0ZXJBdWRpdHNBbmRHcm91cHNCeU9ubHlPcHRpb25zIH0gZnJvbSAnLi91dGlscy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBsaWdodGhvdXNlUGx1Z2luKFxuICB1cmw6IHN0cmluZyxcbiAgZmxhZ3M/OiBMaWdodGhvdXNlT3B0aW9ucyxcbik6IFBsdWdpbkNvbmZpZyB7XG4gIGNvbnN0IHsgc2tpcEF1ZGl0cywgb25seUF1ZGl0cywgb25seUNhdGVnb3JpZXMsIC4uLnVucGFyc2VkRmxhZ3MgfSA9XG4gICAgbm9ybWFsaXplRmxhZ3MoZmxhZ3MgPz8ge30pO1xuXG4gIGNvbnN0IHsgYXVkaXRzLCBncm91cHMgfSA9IGZpbHRlckF1ZGl0c0FuZEdyb3Vwc0J5T25seU9wdGlvbnMoXG4gICAgTElHSFRIT1VTRV9OQVZJR0FUSU9OX0FVRElUUyxcbiAgICBMSUdIVEhPVVNFX0dST1VQUyxcbiAgICB7IHNraXBBdWRpdHMsIG9ubHlBdWRpdHMsIG9ubHlDYXRlZ29yaWVzIH0sXG4gICk7XG5cbiAgY29uc3QgcGFja2FnZUpzb24gPSBjcmVhdGVSZXF1aXJlKGltcG9ydC5tZXRhLnVybCkoXG4gICAgJy4uLy4uL3BhY2thZ2UuanNvbicsXG4gICkgYXMgdHlwZW9mIGltcG9ydCgnLi4vLi4vcGFja2FnZS5qc29uJyk7XG5cbiAgcmV0dXJuIHtcbiAgICBzbHVnOiBMSUdIVEhPVVNFX1BMVUdJTl9TTFVHLFxuICAgIHBhY2thZ2VOYW1lOiBwYWNrYWdlSnNvbi5uYW1lLFxuICAgIHZlcnNpb246IHBhY2thZ2VKc29uLnZlcnNpb24sXG4gICAgdGl0bGU6ICdMaWdodGhvdXNlJyxcbiAgICBpY29uOiAnbGlnaHRob3VzZScsXG4gICAgYXVkaXRzLFxuICAgIGdyb3VwcyxcbiAgICBydW5uZXI6IGNyZWF0ZVJ1bm5lckZ1bmN0aW9uKHVybCwge1xuICAgICAgc2tpcEF1ZGl0cyxcbiAgICAgIG9ubHlBdWRpdHMsXG4gICAgICBvbmx5Q2F0ZWdvcmllcyxcbiAgICAgIC4uLnVucGFyc2VkRmxhZ3MsXG4gICAgfSksXG4gIH07XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL2NvbnN0YW50cy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvY29uc3RhbnRzLnRzXCI7aW1wb3J0IHsgREVGQVVMVF9GTEFHUyB9IGZyb20gJ2Nocm9tZS1sYXVuY2hlci9kaXN0L2ZsYWdzLmpzJztcbmltcG9ydCBwYXRoIGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgeyBERUZBVUxUX1BFUlNJU1RfT1VUUFVUX0RJUiB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuXG4vLyBoZWFkbGVzcyBpcyBuZWVkZWQgdG8gcGFzcyBDSSBvbiBMaW51eCBhbmQgV2luZG93cyAobG9jYWxseSBpdCB3b3JrcyB3aXRob3V0IGhlYWRsZXNzIHRvbylcbmV4cG9ydCBjb25zdCBERUZBVUxUX0NIUk9NRV9GTEFHUyA9IFsuLi5ERUZBVUxUX0ZMQUdTLCAnLS1oZWFkbGVzcyddO1xuXG5leHBvcnQgY29uc3QgTElHSFRIT1VTRV9QTFVHSU5fU0xVRyA9ICdsaWdodGhvdXNlJztcbmV4cG9ydCBjb25zdCBMSUdIVEhPVVNFX09VVFBVVF9QQVRIID0gcGF0aC5qb2luKFxuICBERUZBVUxUX1BFUlNJU1RfT1VUUFVUX0RJUixcbiAgTElHSFRIT1VTRV9QTFVHSU5fU0xVRyxcbik7XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL25vcm1hbGl6ZS1mbGFncy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvbm9ybWFsaXplLWZsYWdzLnRzXCI7aW1wb3J0IHsgYm9sZCwgeWVsbG93IH0gZnJvbSAnYW5zaXMnO1xuaW1wb3J0IHsgdWkgfSBmcm9tICdAY29kZS1wdXNodXAvdXRpbHMnO1xuaW1wb3J0IHsgTElHSFRIT1VTRV9QTFVHSU5fU0xVRyB9IGZyb20gJy4vY29uc3RhbnRzLmpzJztcbmltcG9ydCB7IERFRkFVTFRfQ0xJX0ZMQUdTIH0gZnJvbSAnLi9ydW5uZXIvY29uc3RhbnRzLmpzJztcbmltcG9ydCB0eXBlIHsgTGlnaHRob3VzZUNsaUZsYWdzIH0gZnJvbSAnLi9ydW5uZXIvdHlwZXMuanMnO1xuaW1wb3J0IHR5cGUgeyBMaWdodGhvdXNlT3B0aW9ucyB9IGZyb20gJy4vdHlwZXMuanMnO1xuXG5jb25zdCB7IG9ubHlDYXRlZ29yaWVzLCAuLi5vcmlnaW5hbERlZmF1bHRDbGlGbGFncyB9ID0gREVGQVVMVF9DTElfRkxBR1M7XG5leHBvcnQgY29uc3QgREVGQVVMVF9MSUdIVEhPVVNFX09QVElPTlMgPSB7XG4gIC4uLm9yaWdpbmFsRGVmYXVsdENsaUZsYWdzLFxuICBvbmx5R3JvdXBzOiBvbmx5Q2F0ZWdvcmllcyxcbn0gc2F0aXNmaWVzIExpZ2h0aG91c2VPcHRpb25zO1xuXG4vLyBOT1RFOlxuLy8gVGhpcyBpcyBhbiBpbnRlcm1lZGlhdGUgdmFyaWFibGUgdG8gZ2V0IGBVbnN1cHBvcnRlZENsaUZsYWdzYC4gRm9yIHVua25vd24gcmVhc29ucyBgdHlwZXNjcmlwdEA1LjMuM2AgZG9lc24ndCB3b3JrIG90aGVyd2lzZS5cbmNvbnN0IGxpZ2h0aG91c2VVbnN1cHBvcnRlZENsaUZsYWdzID0gW1xuICAncHJlY29tcHV0ZWRMYW50ZXJuRGF0YVBhdGgnLCAvLyBQYXRoIHRvIHRoZSBmaWxlIHdoZXJlIHByZWNvbXB1dGVkIGxhbnRlcm4gZGF0YSBzaG91bGQgYmUgcmVhZCBmcm9tLlxuICAnY2hyb21lSWdub3JlRGVmYXVsdEZsYWdzJywgLy8gaWdub3JlIGRlZmF1bHQgZmxhZ3MgZnJvbSBMaWdodGhvdXNlIENMSVxuICAvLyBObyBlcnJvciByZXBvcnRpbmcgaW1wbGVtZW50ZWQgYXMgaW4gdGhlIHNvdXJjZSBTZW50cnkgd2FzIGludm9sdmVkXG4gIC8vIFNlZTogaHR0cHM6Ly9naXRodWIuY29tL0dvb2dsZUNocm9tZS9saWdodGhvdXNlL2Jsb2IvZDhjY2Y3MDY5MjIxNmI3ZmEwNDdhNGVhYTJkMTI3N2IwYjdmZTk0Ny9jbGkvYmluLmpzI0wxMjRcbiAgJ2VuYWJsZUVycm9yUmVwb3J0aW5nJywgLy8gZW5hYmxlIGVycm9yIHJlcG9ydGluZ1xuICAvLyBsaWdodGhvdXNlIENMSSBzcGVjaWZpYyBkZWJ1ZyBsb2dzXG4gICdsaXN0LWFsbC1hdWRpdHMnLCAvLyBQcmludHMgYSBsaXN0IG9mIGFsbCBhdmFpbGFibGUgYXVkaXRzIGFuZCBleGl0cy5cbiAgJ2xpc3QtbG9jYWxlcycsIC8vIFByaW50cyBhIGxpc3Qgb2YgYWxsIHN1cHBvcnRlZCBsb2NhbGVzIGFuZCBleGl0cy5cbiAgJ2xpc3QtdHJhY2UtY2F0ZWdvcmllcycsIC8vIFByaW50cyBhIGxpc3Qgb2YgYWxsIHJlcXVpcmVkIHRyYWNlIGNhdGVnb3JpZXMgYW5kIGV4aXRzLlxuXSBhcyBjb25zdDtcbnR5cGUgVW5zdXBwb3J0ZWRDbGlGbGFncyA9ICh0eXBlb2YgbGlnaHRob3VzZVVuc3VwcG9ydGVkQ2xpRmxhZ3MpW251bWJlcl07XG5cbmNvbnN0IExJR0hUSE9VU0VfVU5TVVBQT1JURURfQ0xJX0ZMQUdTID0gbmV3IFNldChsaWdodGhvdXNlVW5zdXBwb3J0ZWRDbGlGbGFncyk7XG5cbmNvbnN0IFJFRklORURfU1RSSU5HX09SX1NUUklOR19BUlJBWSA9IG5ldyBTZXQoW1xuICAnb25seUF1ZGl0cycsXG4gICdvbmx5Q2F0ZWdvcmllcycsXG4gICdza2lwQXVkaXRzJyxcbiAgJ2J1ZGdldHMnLFxuICAnY2hyb21lRmxhZ3MnLFxuXSk7XG5cbmV4cG9ydCBmdW5jdGlvbiBub3JtYWxpemVGbGFncyhmbGFncz86IExpZ2h0aG91c2VPcHRpb25zKTogTGlnaHRob3VzZUNsaUZsYWdzIHtcbiAgY29uc3QgcHJlZmlsbGVkRmxhZ3MgPSB7IC4uLkRFRkFVTFRfTElHSFRIT1VTRV9PUFRJT05TLCAuLi5mbGFncyB9O1xuXG4gIGxvZ1Vuc3VwcG9ydGVkRmxhZ3NJblVzZShwcmVmaWxsZWRGbGFncyk7XG5cbiAgcmV0dXJuIE9iamVjdC5mcm9tRW50cmllcyhcbiAgICBPYmplY3QuZW50cmllcyhwcmVmaWxsZWRGbGFncylcbiAgICAgIC5maWx0ZXIoXG4gICAgICAgIChbZmxhZ05hbWVdKSA9PlxuICAgICAgICAgICFMSUdIVEhPVVNFX1VOU1VQUE9SVEVEX0NMSV9GTEFHUy5oYXMoXG4gICAgICAgICAgICBmbGFnTmFtZSBhcyBVbnN1cHBvcnRlZENsaUZsYWdzLFxuICAgICAgICAgICksXG4gICAgICApXG4gICAgICAvLyBpbiBjb2RlLXB1c2h1cCBsaWdodGhvdXNlIGNhdGVnb3JpZXMgYXJlIG1hcHBlZCBhcyBncm91cHMsIHRoZXJlZm9yIHdlIGhhZCB0byByZW5hbWUgXCJvbmx5Q2F0ZWdvcmllc1wiIHRvIFwib25seUdyb3Vwc1wiIGZvciB0aGUgdXNlciBvZiB0aGUgcGx1Z2luIGFzIGl0IHdhcyBjb25mdXNpbmdcbiAgICAgIC5tYXAoKFtrZXksIHZdKSA9PiBba2V5ID09PSAnb25seUdyb3VwcycgPyAnb25seUNhdGVnb3JpZXMnIDoga2V5LCB2XSlcbiAgICAgIC8vIG9ubHlBdWRpdHMgYW5kIG9ubHlDYXRlZ29yaWVzIGNhbm5vdCBiZSBlbXB0eSBhcnJheXMsIG90aGVyd2lzZSBza2lwQXVkaXRzIGlzIGlnbm9yZWQgYnkgbGlnaHRob3VzZVxuICAgICAgLmZpbHRlcigoW18sIHZdKSA9PiAhKEFycmF5LmlzQXJyYXkodikgJiYgdi5sZW5ndGggPT09IDApKVxuICAgICAgLy8gdW5kZWZpbmVkIHwgc3RyaW5nIHwgc3RyaW5nW10gPT4gc3RyaW5nW10gKGVtcHR5IGZvciB1bmRlZmluZWQpXG4gICAgICAubWFwKChba2V5LCB2XSkgPT4ge1xuICAgICAgICBpZiAoIVJFRklORURfU1RSSU5HX09SX1NUUklOR19BUlJBWS5oYXMoa2V5IGFzIG5ldmVyKSkge1xuICAgICAgICAgIHJldHVybiBba2V5LCB2XTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gW2tleSwgQXJyYXkuaXNBcnJheSh2KSA/IHYgOiB2ID09IG51bGwgPyBbXSA6IFt2XV07XG4gICAgICB9KSxcbiAgKSBhcyBMaWdodGhvdXNlQ2xpRmxhZ3M7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsb2dVbnN1cHBvcnRlZEZsYWdzSW5Vc2UoXG4gIGZsYWdzOiBMaWdodGhvdXNlT3B0aW9ucyxcbiAgZGlzcGxheUNvdW50ID0gMyxcbikge1xuICBjb25zdCB1bnN1cHBvcnRlZEZsYWdzSW5Vc2UgPSBPYmplY3Qua2V5cyhmbGFncykuZmlsdGVyKGZsYWcgPT5cbiAgICBMSUdIVEhPVVNFX1VOU1VQUE9SVEVEX0NMSV9GTEFHUy5oYXMoZmxhZyBhcyBVbnN1cHBvcnRlZENsaUZsYWdzKSxcbiAgKTtcbiAgaWYgKHVuc3VwcG9ydGVkRmxhZ3NJblVzZS5sZW5ndGggPiAwKSB7XG4gICAgY29uc3QgcG9zdEZpeCA9IChjb3VudDogbnVtYmVyKSA9PlxuICAgICAgY291bnQgPiBkaXNwbGF5Q291bnQgPyBgIGFuZCAke2NvdW50IC0gZGlzcGxheUNvdW50fSBtb3JlLmAgOiAnJztcbiAgICB1aSgpLmxvZ2dlci5kZWJ1ZyhcbiAgICAgIGAke3llbGxvdygnXHUyNkEwJyl9IFBsdWdpbiAke2JvbGQoXG4gICAgICAgIExJR0hUSE9VU0VfUExVR0lOX1NMVUcsXG4gICAgICApfSB1c2VkIHVuc3VwcG9ydGVkIGZsYWdzOiAke2JvbGQoXG4gICAgICAgIHVuc3VwcG9ydGVkRmxhZ3NJblVzZS5zbGljZSgwLCBkaXNwbGF5Q291bnQpLmpvaW4oJywgJyksXG4gICAgICApfSR7cG9zdEZpeCh1bnN1cHBvcnRlZEZsYWdzSW5Vc2UubGVuZ3RoKX1gLFxuICAgICk7XG4gIH1cbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL2NvbnN0YW50cy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lclwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lci9jb25zdGFudHMudHNcIjtpbXBvcnQge1xuICB0eXBlIENsaUZsYWdzLFxuICB0eXBlIENvbmZpZyxcbiAgdHlwZSBJY3VNZXNzYWdlLFxuICB0eXBlIEF1ZGl0IGFzIExIQXVkaXQsXG4gIGRlZmF1bHRDb25maWcsXG59IGZyb20gJ2xpZ2h0aG91c2UnO1xuaW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB0eXBlIHsgQXVkaXQsIEdyb3VwIH0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5pbXBvcnQgeyBERUZBVUxUX0NIUk9NRV9GTEFHUywgTElHSFRIT1VTRV9PVVRQVVRfUEFUSCB9IGZyb20gJy4uL2NvbnN0YW50cy5qcyc7XG5cbmNvbnN0IHsgYXVkaXRzLCBjYXRlZ29yaWVzIH0gPSBkZWZhdWx0Q29uZmlnO1xuXG4vLyBpbnRlcm5hbCBpbnRlcm1lZGlhdGUgdmFyaWFibGUgdG8gZGVyaXZlIHRoZSByZWxldmFudCBhdWRpdHNcbmNvbnN0IGFsbFJhd0xpZ2h0aG91c2VBdWRpdHMgPSBhd2FpdCBQcm9taXNlLmFsbChcbiAgKGF1ZGl0cyA/PyBbXSkubWFwKGxvYWRMaWdodGhvdXNlQXVkaXQpLFxuKTtcblxuZXhwb3J0IGNvbnN0IFBMVUdJTl9TTFVHID0gJ2xpZ2h0aG91c2UnO1xuZXhwb3J0IGNvbnN0IExJR0hUSE9VU0VfTkFWSUdBVElPTl9BVURJVFM6IEF1ZGl0W10gPSBhbGxSYXdMaWdodGhvdXNlQXVkaXRzXG4gIC8vIFRoaXMgcGx1Z2luIG9ubHkgc3VwcG9ydHMgdGhlIFwibmF2aWdhdGlvblwiIG1vZGUgb2YgTGlnaHRob3VzZSBpbiB0aGUgY3VycmVudCBpbXBsZW1lbnRhdGlvblxuICAvLyBJZiB3ZSBkb24ndCBleGNsdWRlIG90aGVyIGF1ZGl0cyB3ZSB0aHJvdyBpbiB0aGUgcGx1Z2luIG91dHB1dCB2YWxpZGF0aW9uIGFzIHNvbWUgb2YgdGhlIHByb3ZpZGVkIGF1ZGl0cyBhcmUgbm90IGluY2x1ZGVkIGluIGBsaWdodGhvdXNlLXJlcG9ydC5qc29uYFxuICAuZmlsdGVyKFxuICAgIGF1ZGl0ID0+XG4gICAgICBhdWRpdC5tZXRhLnN1cHBvcnRlZE1vZGVzID09IG51bGwgfHxcbiAgICAgIChBcnJheS5pc0FycmF5KGF1ZGl0Lm1ldGEuc3VwcG9ydGVkTW9kZXMpICYmXG4gICAgICAgIGF1ZGl0Lm1ldGEuc3VwcG9ydGVkTW9kZXMuaW5jbHVkZXMoJ25hdmlnYXRpb24nKSksXG4gIClcbiAgLm1hcChhdWRpdCA9PiAoe1xuICAgIHNsdWc6IGF1ZGl0Lm1ldGEuaWQsXG4gICAgdGl0bGU6IGdldE1ldGFTdHJpbmcoYXVkaXQubWV0YS50aXRsZSksXG4gICAgZGVzY3JpcHRpb246IGdldE1ldGFTdHJpbmcoYXVkaXQubWV0YS5kZXNjcmlwdGlvbiksXG4gIH0pKTtcblxuY29uc3QgbmF2aWdhdGlvbkF1ZGl0U2x1Z3MgPSBuZXcgU2V0KFxuICBMSUdIVEhPVVNFX05BVklHQVRJT05fQVVESVRTLm1hcCgoeyBzbHVnIH0pID0+IHNsdWcpLFxuKTtcbmV4cG9ydCBjb25zdCBMSUdIVEhPVVNFX0dST1VQUzogR3JvdXBbXSA9IE9iamVjdC5lbnRyaWVzKGNhdGVnb3JpZXMgPz8ge30pLm1hcChcbiAgKFtpZCwgY2F0ZWdvcnldKSA9PiAoe1xuICAgIHNsdWc6IGlkLFxuICAgIHRpdGxlOiBnZXRNZXRhU3RyaW5nKGNhdGVnb3J5LnRpdGxlKSxcbiAgICAuLi4oY2F0ZWdvcnkuZGVzY3JpcHRpb24gJiYge1xuICAgICAgZGVzY3JpcHRpb246IGdldE1ldGFTdHJpbmcoY2F0ZWdvcnkuZGVzY3JpcHRpb24pLFxuICAgIH0pLFxuICAgIHJlZnM6IGNhdGVnb3J5LmF1ZGl0UmVmc1xuICAgICAgLmZpbHRlcigoeyBpZDogYXVkaXRTbHVnIH0pID0+IG5hdmlnYXRpb25BdWRpdFNsdWdzLmhhcyhhdWRpdFNsdWcpKVxuICAgICAgLm1hcChyZWYgPT4gKHtcbiAgICAgICAgc2x1ZzogcmVmLmlkLFxuICAgICAgICB3ZWlnaHQ6IHJlZi53ZWlnaHQsXG4gICAgICB9KSksXG4gIH0pLFxuKTtcblxuZnVuY3Rpb24gZ2V0TWV0YVN0cmluZyh2YWx1ZTogc3RyaW5nIHwgSWN1TWVzc2FnZSk6IHN0cmluZyB7XG4gIGlmICh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuIHZhbHVlO1xuICB9XG4gIHJldHVybiB2YWx1ZS5mb3JtYXR0ZWREZWZhdWx0O1xufVxuXG5hc3luYyBmdW5jdGlvbiBsb2FkTGlnaHRob3VzZUF1ZGl0KFxuICB2YWx1ZTogQ29uZmlnLkF1ZGl0SnNvbixcbik6IFByb21pc2U8dHlwZW9mIExIQXVkaXQ+IHtcbiAgLy8gdGhlIHBhc3NlZCB2YWx1ZSBkaXJlY3RseSBpbmNsdWRlcyB0aGUgaW1wbGVtZW50YXRpb24gYXMgSlMgb2JqZWN0XG4gIC8vICAgc2hhcGU6IHsgaW1wbGVtZW50YXRpb246IHR5cGVvZiBMSEF1ZGl0OyBvcHRpb25zPzoge307IH1cbiAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcgJiYgJ2ltcGxlbWVudGF0aW9uJyBpbiB2YWx1ZSkge1xuICAgIHJldHVybiB2YWx1ZS5pbXBsZW1lbnRhdGlvbjtcbiAgfVxuICAvLyB0aGUgcGFzc2VkIHZhbHVlIGlzIGEgYExILkF1ZGl0YCBjbGFzcyBpbnN0YW5jZVxuICAvLyAgIHNoYXBlOiBMSEF1ZGl0XG4gIGlmICh0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbicpIHtcbiAgICByZXR1cm4gdmFsdWU7XG4gIH1cbiAgLy8gdGhlIHBhc3NlZCB2YWx1ZSBpcyB0aGUgcGF0aCBkaXJlY3RseVxuICAvLyAgIHNoYXBlOiBzdHJpbmdcbiAgLy8gb3RoZXJ3aXNlIGl0IGlzIGEgSlMgb2JqZWN0IG1haW50YWluaW5nIGEgYHBhdGhgIHByb3BlcnR5XG4gIC8vICAgc2hhcGU6IHsgcGF0aDogc3RyaW5nLCBvcHRpb25zPzoge307IH1cbiAgY29uc3QgZmlsZSA9IHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycgPyB2YWx1ZSA6IHZhbHVlLnBhdGg7XG4gIGNvbnN0IG1vZHVsZSA9IChhd2FpdCBpbXBvcnQoYGxpZ2h0aG91c2UvY29yZS9hdWRpdHMvJHtmaWxlfS5qc2ApKSBhcyB7XG4gICAgZGVmYXVsdDogdHlwZW9mIExIQXVkaXQ7XG4gIH07XG4gIHJldHVybiBtb2R1bGUuZGVmYXVsdDtcbn1cblxuZXhwb3J0IGNvbnN0IExJR0hUSE9VU0VfUkVQT1JUX05BTUUgPSAnbGlnaHRob3VzZS1yZXBvcnQuanNvbic7XG5cbmV4cG9ydCBjb25zdCBERUZBVUxUX0NMSV9GTEFHUyA9IHtcbiAgLy8gZGVmYXVsdCB2YWx1ZXMgZXh0cmFjdGVkIGZyb21cbiAgLy8gaHR0cHM6Ly9naXRodWIuY29tL0dvb2dsZUNocm9tZS9saWdodGhvdXNlL2Jsb2IvN2Q4MDE3OGMzN2ExYjYwMGVhOGYwOTJmYzBiMDk4MDI5Nzk5YTY1OS9jbGkvY2xpLWZsYWdzLmpzI0w4MFxuICB2ZXJib3NlOiBmYWxzZSxcbiAgc2F2ZUFzc2V0czogZmFsc2UsXG4gIGNocm9tZUZsYWdzOiBERUZBVUxUX0NIUk9NRV9GTEFHUyxcbiAgcG9ydDogMCxcbiAgaG9zdG5hbWU6ICcxMjcuMC4wLjEnLFxuICB2aWV3OiBmYWxzZSxcbiAgY2hhbm5lbDogJ2NsaScsXG4gIC8vIGN1c3RvbSBvdmVyd3JpdGVzIGluIGZhdm91ciBvZiB0aGUgcGx1Z2luXG4gIC8vIGhpZGUgbG9ncyBieSBkZWZhdWx0XG4gIHF1aWV0OiB0cnVlLFxuICBvbmx5QXVkaXRzOiBbXSxcbiAgc2tpcEF1ZGl0czogW10sXG4gIG9ubHlDYXRlZ29yaWVzOiBbXSxcbiAgb3V0cHV0OiBbJ2pzb24nXSxcbiAgb3V0cHV0UGF0aDogcGF0aC5qb2luKExJR0hUSE9VU0VfT1VUUFVUX1BBVEgsIExJR0hUSE9VU0VfUkVQT1JUX05BTUUpLFxufSBzYXRpc2ZpZXMgUGFydGlhbDxDbGlGbGFncz47XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lci9ydW5uZXIudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9ydW5uZXJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9ydW5uZXIvcnVubmVyLnRzXCI7aW1wb3J0IHR5cGUgeyBSdW5uZXJSZXN1bHQgfSBmcm9tICdsaWdodGhvdXNlJztcbmltcG9ydCB7IHJ1bkxpZ2h0aG91c2UgfSBmcm9tICdsaWdodGhvdXNlL2NsaS9ydW4uanMnO1xuaW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB0eXBlIHsgQXVkaXRPdXRwdXRzLCBSdW5uZXJGdW5jdGlvbiB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgZW5zdXJlRGlyZWN0b3J5RXhpc3RzIH0gZnJvbSAnQGNvZGUtcHVzaHVwL3V0aWxzJztcbmltcG9ydCB7IERFRkFVTFRfQ0xJX0ZMQUdTIH0gZnJvbSAnLi9jb25zdGFudHMuanMnO1xuaW1wb3J0IHR5cGUgeyBMaWdodGhvdXNlQ2xpRmxhZ3MgfSBmcm9tICcuL3R5cGVzLmpzJztcbmltcG9ydCB7XG4gIGRldGVybWluZUFuZFNldExvZ0xldmVsLFxuICBnZXRDb25maWcsXG4gIG5vcm1hbGl6ZUF1ZGl0T3V0cHV0cyxcbiAgdG9BdWRpdE91dHB1dHMsXG59IGZyb20gJy4vdXRpbHMuanMnO1xuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlUnVubmVyRnVuY3Rpb24oXG4gIHVybFVuZGVyVGVzdDogc3RyaW5nLFxuICBmbGFnczogTGlnaHRob3VzZUNsaUZsYWdzID0gREVGQVVMVF9DTElfRkxBR1MsXG4pOiBSdW5uZXJGdW5jdGlvbiB7XG4gIHJldHVybiBhc3luYyAoKTogUHJvbWlzZTxBdWRpdE91dHB1dHM+ID0+IHtcbiAgICBjb25zdCB7XG4gICAgICBjb25maWdQYXRoLFxuICAgICAgcHJlc2V0LFxuICAgICAgb3V0cHV0UGF0aCxcbiAgICAgIC4uLnBhcnNlZEZsYWdzXG4gICAgfTogUGFydGlhbDxMaWdodGhvdXNlQ2xpRmxhZ3M+ID0gZmxhZ3M7XG5cbiAgICBjb25zdCBsb2dMZXZlbCA9IGRldGVybWluZUFuZFNldExvZ0xldmVsKHBhcnNlZEZsYWdzKTtcblxuICAgIGNvbnN0IGNvbmZpZyA9IGF3YWl0IGdldENvbmZpZyh7IGNvbmZpZ1BhdGgsIHByZXNldCB9KTtcbiAgICBpZiAob3V0cHV0UGF0aCkge1xuICAgICAgYXdhaXQgZW5zdXJlRGlyZWN0b3J5RXhpc3RzKHBhdGguZGlybmFtZShvdXRwdXRQYXRoKSk7XG4gICAgfVxuXG4gICAgY29uc3QgZW5yaWNoZWRGbGFncyA9IHtcbiAgICAgIC4uLnBhcnNlZEZsYWdzLFxuICAgICAgbG9nTGV2ZWwsXG4gICAgICBvdXRwdXRQYXRoLFxuICAgIH07XG5cbiAgICBjb25zdCBydW5uZXJSZXN1bHQ6IHVua25vd24gPSBhd2FpdCBydW5MaWdodGhvdXNlKFxuICAgICAgdXJsVW5kZXJUZXN0LFxuICAgICAgZW5yaWNoZWRGbGFncyxcbiAgICAgIGNvbmZpZyxcbiAgICApO1xuXG4gICAgaWYgKHJ1bm5lclJlc3VsdCA9PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0xpZ2h0aG91c2UgZGlkIG5vdCBwcm9kdWNlIGEgcmVzdWx0LicpO1xuICAgIH1cblxuICAgIGNvbnN0IHsgbGhyIH0gPSBydW5uZXJSZXN1bHQgYXMgUnVubmVyUmVzdWx0O1xuICAgIGNvbnN0IGF1ZGl0T3V0cHV0cyA9IHRvQXVkaXRPdXRwdXRzKE9iamVjdC52YWx1ZXMobGhyLmF1ZGl0cyksIGZsYWdzKTtcblxuICAgIHJldHVybiBub3JtYWxpemVBdWRpdE91dHB1dHMoYXVkaXRPdXRwdXRzLCBlbnJpY2hlZEZsYWdzKTtcbiAgfTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL3V0aWxzLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL3V0aWxzLnRzXCI7aW1wb3J0IHsgYm9sZCB9IGZyb20gJ2Fuc2lzJztcbmltcG9ydCB0eXBlIHsgQ29uZmlnLCBGb3JtYXR0ZWRJY3UgfSBmcm9tICdsaWdodGhvdXNlJztcbmltcG9ydCBsb2cgZnJvbSAnbGlnaHRob3VzZS1sb2dnZXInO1xuaW1wb3J0IGRlc2t0b3BDb25maWcgZnJvbSAnbGlnaHRob3VzZS9jb3JlL2NvbmZpZy9kZXNrdG9wLWNvbmZpZy5qcyc7XG5pbXBvcnQgZXhwZXJpbWVudGFsQ29uZmlnIGZyb20gJ2xpZ2h0aG91c2UvY29yZS9jb25maWcvZXhwZXJpbWVudGFsLWNvbmZpZy5qcyc7XG5pbXBvcnQgcGVyZkNvbmZpZyBmcm9tICdsaWdodGhvdXNlL2NvcmUvY29uZmlnL3BlcmYtY29uZmlnLmpzJztcbmltcG9ydCB0eXBlIERldGFpbHMgZnJvbSAnbGlnaHRob3VzZS90eXBlcy9saHIvYXVkaXQtZGV0YWlscyc7XG5pbXBvcnQgdHlwZSB7IFJlc3VsdCB9IGZyb20gJ2xpZ2h0aG91c2UvdHlwZXMvbGhyL2F1ZGl0LXJlc3VsdCc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0T3V0cHV0LCBBdWRpdE91dHB1dHMgfSBmcm9tICdAY29kZS1wdXNodXAvbW9kZWxzJztcbmltcG9ydCB7XG4gIGZvcm1hdFJlcG9ydFNjb3JlLFxuICBpbXBvcnRNb2R1bGUsXG4gIHJlYWRKc29uRmlsZSxcbiAgdWksXG59IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgdHlwZSB7IExpZ2h0aG91c2VPcHRpb25zIH0gZnJvbSAnLi4vdHlwZXMuanMnO1xuaW1wb3J0IHsgbG9nVW5zdXBwb3J0ZWREZXRhaWxzLCB0b0F1ZGl0RGV0YWlscyB9IGZyb20gJy4vZGV0YWlscy9kZXRhaWxzLmpzJztcbmltcG9ydCB0eXBlIHsgTGlnaHRob3VzZUNsaUZsYWdzIH0gZnJvbSAnLi90eXBlcy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBub3JtYWxpemVBdWRpdE91dHB1dHMoXG4gIGF1ZGl0T3V0cHV0czogQXVkaXRPdXRwdXRzLFxuICBmbGFnczogTGlnaHRob3VzZU9wdGlvbnMgPSB7IHNraXBBdWRpdHM6IFtdIH0sXG4pOiBBdWRpdE91dHB1dHMge1xuICBjb25zdCB0b1NraXAgPSBuZXcgU2V0KGZsYWdzLnNraXBBdWRpdHMgPz8gW10pO1xuICByZXR1cm4gYXVkaXRPdXRwdXRzLmZpbHRlcigoeyBzbHVnIH0pID0+ICF0b1NraXAuaGFzKHNsdWcpKTtcbn1cblxuZXhwb3J0IGNsYXNzIExpZ2h0aG91c2VBdWRpdFBhcnNpbmdFcnJvciBleHRlbmRzIEVycm9yIHtcbiAgY29uc3RydWN0b3Ioc2x1Zzogc3RyaW5nLCBlcnJvcjogRXJyb3IpIHtcbiAgICBzdXBlcihgXFxuQXVkaXQgJHtib2xkKHNsdWcpfSBmYWlsZWQgcGFyc2luZyBkZXRhaWxzOiBcXG4ke2Vycm9yLm1lc3NhZ2V9YCk7XG4gIH1cbn1cblxuZnVuY3Rpb24gZm9ybWF0QmFzZUF1ZGl0T3V0cHV0KGxockF1ZGl0OiBSZXN1bHQpOiBBdWRpdE91dHB1dCB7XG4gIGNvbnN0IHtcbiAgICBpZDogc2x1ZyxcbiAgICBzY29yZSxcbiAgICBudW1lcmljVmFsdWUsXG4gICAgZGlzcGxheVZhbHVlLFxuICAgIHNjb3JlRGlzcGxheU1vZGUsXG4gIH0gPSBsaHJBdWRpdDtcbiAgcmV0dXJuIHtcbiAgICBzbHVnLFxuICAgIHNjb3JlOiBzY29yZSA/PyAxLFxuICAgIHZhbHVlOiBudW1lcmljVmFsdWUgPz8gc2NvcmUgPz8gMCxcbiAgICBkaXNwbGF5VmFsdWU6XG4gICAgICBkaXNwbGF5VmFsdWUgPz9cbiAgICAgIChzY29yZURpc3BsYXlNb2RlID09PSAnYmluYXJ5J1xuICAgICAgICA/IHNjb3JlID09PSAxXG4gICAgICAgICAgPyAncGFzc2VkJ1xuICAgICAgICAgIDogJ2ZhaWxlZCdcbiAgICAgICAgOiBzY29yZVxuICAgICAgICAgID8gYCR7Zm9ybWF0UmVwb3J0U2NvcmUoc2NvcmUpfSVgXG4gICAgICAgICAgOiB1bmRlZmluZWQpLFxuICB9O1xufVxuXG5mdW5jdGlvbiBwcm9jZXNzQXVkaXREZXRhaWxzKFxuICBhdWRpdE91dHB1dDogQXVkaXRPdXRwdXQsXG4gIGRldGFpbHM6IEZvcm1hdHRlZEljdTxEZXRhaWxzPixcbik6IEF1ZGl0T3V0cHV0IHtcbiAgdHJ5IHtcbiAgICBjb25zdCBwYXJzZWREZXRhaWxzID0gdG9BdWRpdERldGFpbHMoZGV0YWlscyk7XG4gICAgcmV0dXJuIE9iamVjdC5rZXlzKHBhcnNlZERldGFpbHMpLmxlbmd0aCA+IDBcbiAgICAgID8geyAuLi5hdWRpdE91dHB1dCwgZGV0YWlsczogcGFyc2VkRGV0YWlscyB9XG4gICAgICA6IGF1ZGl0T3V0cHV0O1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIHRocm93IG5ldyBMaWdodGhvdXNlQXVkaXRQYXJzaW5nRXJyb3IoYXVkaXRPdXRwdXQuc2x1ZywgZXJyb3IgYXMgRXJyb3IpO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB0b0F1ZGl0T3V0cHV0cyhcbiAgbGhyQXVkaXRzOiBSZXN1bHRbXSxcbiAgeyB2ZXJib3NlID0gZmFsc2UgfTogeyB2ZXJib3NlPzogYm9vbGVhbiB9ID0ge30sXG4pOiBBdWRpdE91dHB1dHMge1xuICBpZiAodmVyYm9zZSkge1xuICAgIGxvZ1Vuc3VwcG9ydGVkRGV0YWlscyhsaHJBdWRpdHMpO1xuICB9XG4gIHJldHVybiBsaHJBdWRpdHMubWFwKGF1ZGl0ID0+IHtcbiAgICBjb25zdCBhdWRpdE91dHB1dCA9IGZvcm1hdEJhc2VBdWRpdE91dHB1dChhdWRpdCk7XG5cbiAgICByZXR1cm4gYXVkaXQuZGV0YWlscyA9PSBudWxsXG4gICAgICA/IGF1ZGl0T3V0cHV0XG4gICAgICA6IHByb2Nlc3NBdWRpdERldGFpbHMoYXVkaXRPdXRwdXQsIGF1ZGl0LmRldGFpbHMpO1xuICB9KTtcbn1cblxuZXhwb3J0IHR5cGUgTGlnaHRob3VzZUxvZ0xldmVsID1cbiAgfCAndmVyYm9zZSdcbiAgfCAnZXJyb3InXG4gIHwgJ2luZm8nXG4gIHwgJ3NpbGVudCdcbiAgfCAnd2FybidcbiAgfCB1bmRlZmluZWQ7XG5leHBvcnQgZnVuY3Rpb24gZGV0ZXJtaW5lQW5kU2V0TG9nTGV2ZWwoe1xuICB2ZXJib3NlLFxuICBxdWlldCxcbn06IHtcbiAgdmVyYm9zZT86IGJvb2xlYW47XG4gIHF1aWV0PzogYm9vbGVhbjtcbn0gPSB7fSk6IExpZ2h0aG91c2VMb2dMZXZlbCB7XG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBmdW5jdGlvbmFsL25vLWxldFxuICBsZXQgbG9nTGV2ZWw6IExpZ2h0aG91c2VMb2dMZXZlbCA9ICdpbmZvJztcbiAgLy8gc2V0IGxvZ2dpbmcgcHJlZmVyZW5jZXNcbiAgaWYgKHZlcmJvc2UpIHtcbiAgICBsb2dMZXZlbCA9ICd2ZXJib3NlJztcbiAgfSBlbHNlIGlmIChxdWlldCkge1xuICAgIGxvZ0xldmVsID0gJ3NpbGVudCc7XG4gIH1cblxuICBsb2cuc2V0TGV2ZWwobG9nTGV2ZWwpO1xuXG4gIHJldHVybiBsb2dMZXZlbDtcbn1cblxuZXhwb3J0IHR5cGUgQ29uZmlnT3B0aW9ucyA9IFBhcnRpYWw8XG4gIFBpY2s8TGlnaHRob3VzZUNsaUZsYWdzLCAnY29uZmlnUGF0aCcgfCAncHJlc2V0Jz5cbj47XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRDb25maWcoXG4gIG9wdGlvbnM6IENvbmZpZ09wdGlvbnMgPSB7fSxcbik6IFByb21pc2U8Q29uZmlnIHwgdW5kZWZpbmVkPiB7XG4gIGNvbnN0IHsgY29uZmlnUGF0aDogZmlsZXBhdGgsIHByZXNldCB9ID0gb3B0aW9ucztcblxuICBpZiAoZmlsZXBhdGggIT0gbnVsbCkge1xuICAgIGlmIChmaWxlcGF0aC5lbmRzV2l0aCgnLmpzb24nKSkge1xuICAgICAgLy8gUmVzb2x2ZSB0aGUgY29uZmlnIGZpbGUgcGF0aCByZWxhdGl2ZSB0byB3aGVyZSBjbGkgd2FzIGNhbGxlZC5cbiAgICAgIHJldHVybiByZWFkSnNvbkZpbGU8Q29uZmlnPihmaWxlcGF0aCk7XG4gICAgfSBlbHNlIGlmICgvXFwuKHRzfGpzfG1qcykkLy50ZXN0KGZpbGVwYXRoKSkge1xuICAgICAgcmV0dXJuIGltcG9ydE1vZHVsZTxDb25maWc+KHsgZmlsZXBhdGgsIGZvcm1hdDogJ2VzbScgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHVpKCkubG9nZ2VyLmluZm8oYEZvcm1hdCBvZiBmaWxlICR7ZmlsZXBhdGh9IG5vdCBzdXBwb3J0ZWRgKTtcbiAgICB9XG4gIH0gZWxzZSBpZiAocHJlc2V0ICE9IG51bGwpIHtcbiAgICBzd2l0Y2ggKHByZXNldCkge1xuICAgICAgY2FzZSAnZGVza3RvcCc6XG4gICAgICAgIHJldHVybiBkZXNrdG9wQ29uZmlnO1xuICAgICAgY2FzZSAncGVyZic6XG4gICAgICAgIHJldHVybiBwZXJmQ29uZmlnIGFzIENvbmZpZztcbiAgICAgIGNhc2UgJ2V4cGVyaW1lbnRhbCc6XG4gICAgICAgIHJldHVybiBleHBlcmltZW50YWxDb25maWcgYXMgQ29uZmlnO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgLy8gYXMgcHJlc2V0IGlzIGEgc3RyaW5nIGxpdGVyYWwgdGhlIGRlZmF1bHQgY2FzZSBoZXJlIGlzIG5vcm1hbGx5IGNhdWdodCBieSBUUyBhbmQgbm90IHBvc3NpYmxlIHRvIGhhcHBlbi4gTm93IGluIHJlYWxpdHkgaXQgY2FuIGhhcHBlbiBhbmQgcHJlc2V0IGNvdWxkIGJlIGEgc3RyaW5nIG5vdCBpbmNsdWRlZCBpbiB0aGUgbGl0ZXJhbC5cbiAgICAgICAgLy8gVGhlcmVmb3JlLCB3ZSBoYXZlIHRvIHVzZSBgYXMgc3RyaW5nYC4gT3RoZXJ3aXNlLCBpdCB3aWxsIGNvbnNpZGVyIHByZXNldCBhcyB0eXBlIG5ldmVyXG4gICAgICAgIHVpKCkubG9nZ2VyLmluZm8oYFByZXNldCBcIiR7cHJlc2V0IGFzIHN0cmluZ31cIiBpcyBub3Qgc3VwcG9ydGVkYCk7XG4gICAgfVxuICB9XG4gIHJldHVybiB1bmRlZmluZWQ7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lci9kZXRhaWxzL2RldGFpbHMudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9ydW5uZXIvZGV0YWlsc1wiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lci9kZXRhaWxzL2RldGFpbHMudHNcIjtpbXBvcnQgeyBib2xkLCB5ZWxsb3cgfSBmcm9tICdhbnNpcyc7XG5pbXBvcnQgdHlwZSB7IEZvcm1hdHRlZEljdSB9IGZyb20gJ2xpZ2h0aG91c2UnO1xuaW1wb3J0IHR5cGUgRGV0YWlscyBmcm9tICdsaWdodGhvdXNlL3R5cGVzL2xoci9hdWRpdC1kZXRhaWxzJztcbmltcG9ydCB0eXBlIHsgUmVzdWx0IH0gZnJvbSAnbGlnaHRob3VzZS90eXBlcy9saHIvYXVkaXQtcmVzdWx0JztcbmltcG9ydCB0eXBlIHsgQXVkaXREZXRhaWxzLCBUYWJsZSB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgdWkgfSBmcm9tICdAY29kZS1wdXNodXAvdXRpbHMnO1xuaW1wb3J0IHsgUExVR0lOX1NMVUcgfSBmcm9tICcuLi9jb25zdGFudHMuanMnO1xuaW1wb3J0IHsgcGFyc2VPcHBvcnR1bml0eVRvQXVkaXREZXRhaWxzVGFibGUgfSBmcm9tICcuL29wcG9ydHVuaXR5LnR5cGUuanMnO1xuaW1wb3J0IHsgcGFyc2VUYWJsZVRvQXVkaXREZXRhaWxzVGFibGUgfSBmcm9tICcuL3RhYmxlLnR5cGUuanMnO1xuXG5leHBvcnQgZnVuY3Rpb24gdG9BdWRpdERldGFpbHM8VCBleHRlbmRzIEZvcm1hdHRlZEljdTxEZXRhaWxzPj4oXG4gIGRldGFpbHM6IFQgfCB1bmRlZmluZWQsXG4pOiBBdWRpdERldGFpbHMge1xuICBpZiAoZGV0YWlscyA9PSBudWxsKSB7XG4gICAgcmV0dXJuIHt9O1xuICB9XG5cbiAgY29uc3QgeyB0eXBlIH0gPSBkZXRhaWxzO1xuXG4gIHN3aXRjaCAodHlwZSkge1xuICAgIGNhc2UgJ3RhYmxlJzpcbiAgICAgIGNvbnN0IHRhYmxlOiBUYWJsZSB8IHVuZGVmaW5lZCA9IHBhcnNlVGFibGVUb0F1ZGl0RGV0YWlsc1RhYmxlKGRldGFpbHMpO1xuICAgICAgcmV0dXJuIHRhYmxlID8geyB0YWJsZSB9IDoge307XG4gICAgY2FzZSAnb3Bwb3J0dW5pdHknOlxuICAgICAgY29uc3Qgb3Bwb3J0dW5pdHk6IFRhYmxlIHwgdW5kZWZpbmVkID1cbiAgICAgICAgcGFyc2VPcHBvcnR1bml0eVRvQXVkaXREZXRhaWxzVGFibGUoZGV0YWlscyk7XG4gICAgICByZXR1cm4gb3Bwb3J0dW5pdHkgPyB7IHRhYmxlOiBvcHBvcnR1bml0eSB9IDoge307XG4gIH1cbiAgcmV0dXJuIHt9O1xufVxuXG4vLyBAVE9ETyBpbXBsZW1lbnQgYWxsIGRldGFpbHNcbmV4cG9ydCBjb25zdCB1bnN1cHBvcnRlZERldGFpbFR5cGVzID0gbmV3IFNldChbXG4gICdkZWJ1Z2RhdGEnLFxuICAndHJlZW1hcC1kYXRhJyxcbiAgJ3NjcmVlbnNob3QnLFxuICAnZmlsbXN0cmlwJyxcbiAgJ2NyaXRpY2FscmVxdWVzdGNoYWluJyxcbl0pO1xuXG5leHBvcnQgZnVuY3Rpb24gbG9nVW5zdXBwb3J0ZWREZXRhaWxzKFxuICBsaHJBdWRpdHM6IFJlc3VsdFtdLFxuICB7IGRpc3BsYXlDb3VudCA9IDMgfTogeyBkaXNwbGF5Q291bnQ/OiBudW1iZXIgfSA9IHt9LFxuKSB7XG4gIGNvbnN0IHNsdWdzV2l0aERldGFpbFBhcnNpbmdFcnJvcnMgPSBbXG4gICAgLi4ubmV3IFNldChcbiAgICAgIGxockF1ZGl0c1xuICAgICAgICAuZmlsdGVyKCh7IGRldGFpbHMgfSkgPT5cbiAgICAgICAgICB1bnN1cHBvcnRlZERldGFpbFR5cGVzLmhhcyhkZXRhaWxzPy50eXBlIGFzIHN0cmluZyksXG4gICAgICAgIClcbiAgICAgICAgLm1hcCgoeyBkZXRhaWxzIH0pID0+IGRldGFpbHM/LnR5cGUpLFxuICAgICksXG4gIF07XG4gIGlmIChzbHVnc1dpdGhEZXRhaWxQYXJzaW5nRXJyb3JzLmxlbmd0aCA+IDApIHtcbiAgICBjb25zdCBwb3N0Rml4ID0gKGNvdW50OiBudW1iZXIpID0+XG4gICAgICBjb3VudCA+IGRpc3BsYXlDb3VudCA/IGAgYW5kICR7Y291bnQgLSBkaXNwbGF5Q291bnR9IG1vcmUuYCA6ICcnO1xuICAgIHVpKCkubG9nZ2VyLmRlYnVnKFxuICAgICAgYCR7eWVsbG93KCdcdTI2QTAnKX0gUGx1Z2luICR7Ym9sZChcbiAgICAgICAgUExVR0lOX1NMVUcsXG4gICAgICApfSBza2lwcGVkIHBhcnNpbmcgb2YgdW5zdXBwb3J0ZWQgYXVkaXQgZGV0YWlsczogJHtib2xkKFxuICAgICAgICBzbHVnc1dpdGhEZXRhaWxQYXJzaW5nRXJyb3JzLnNsaWNlKDAsIGRpc3BsYXlDb3VudCkuam9pbignLCAnKSxcbiAgICAgICl9JHtwb3N0Rml4KHNsdWdzV2l0aERldGFpbFBhcnNpbmdFcnJvcnMubGVuZ3RoKX1gLFxuICAgICk7XG4gIH1cbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL2RldGFpbHMvb3Bwb3J0dW5pdHkudHlwZS50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lci9kZXRhaWxzXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL2RldGFpbHMvb3Bwb3J0dW5pdHkudHlwZS50c1wiO2ltcG9ydCB0eXBlIERldGFpbHMgZnJvbSAnbGlnaHRob3VzZS90eXBlcy9saHIvYXVkaXQtZGV0YWlscyc7XG5pbXBvcnQge1xuICB0eXBlIFRhYmxlLFxuICB0eXBlIFRhYmxlUm93T2JqZWN0LFxuICB0YWJsZVNjaGVtYSxcbn0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5pbXBvcnQgeyBmb3JtYXRCeXRlcywgZm9ybWF0RHVyYXRpb24sIGh0bWwgfSBmcm9tICdAY29kZS1wdXNodXAvdXRpbHMnO1xuaW1wb3J0IHsgcGFyc2VUYWJsZUNvbHVtbnMsIHBhcnNlVGFibGVFbnRyeSB9IGZyb20gJy4vdGFibGUudHlwZS5qcyc7XG5pbXBvcnQgeyBMaWdodGhvdXNlQXVkaXREZXRhaWxzUGFyc2luZ0Vycm9yIH0gZnJvbSAnLi91dGlscy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBwYXJzZU9wcG9ydHVuaXR5VG9BdWRpdERldGFpbHNUYWJsZShcbiAgZGV0YWlsczogRGV0YWlscy5PcHBvcnR1bml0eSxcbik6IFRhYmxlIHwgdW5kZWZpbmVkIHtcbiAgY29uc3QgeyBoZWFkaW5nczogcmF3SGVhZGluZ3MsIGl0ZW1zIH0gPSBkZXRhaWxzO1xuXG4gIGlmIChpdGVtcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG5cbiAgdHJ5IHtcbiAgICByZXR1cm4gdGFibGVTY2hlbWEoKS5wYXJzZSh7XG4gICAgICB0aXRsZTogJ09wcG9ydHVuaXR5JyxcbiAgICAgIGNvbHVtbnM6IHBhcnNlVGFibGVDb2x1bW5zKHJhd0hlYWRpbmdzKSxcbiAgICAgIHJvd3M6IGl0ZW1zLm1hcChyb3cgPT4gcGFyc2VPcHBvcnR1bml0eUl0ZW1Ub1RhYmxlUm93KHJvdywgcmF3SGVhZGluZ3MpKSxcbiAgICB9KTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICB0aHJvdyBuZXcgTGlnaHRob3VzZUF1ZGl0RGV0YWlsc1BhcnNpbmdFcnJvcihcbiAgICAgICdvcHBvcnR1bml0eScsXG4gICAgICB7IGl0ZW1zLCBoZWFkaW5nczogcmF3SGVhZGluZ3MgfSxcbiAgICAgIChlcnJvciBhcyBFcnJvcikubWVzc2FnZS50b1N0cmluZygpLFxuICAgICk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlT3Bwb3J0dW5pdHlJdGVtVG9UYWJsZVJvdyhcbiAgb3Bwb3J0dW5pdHlJdGVtOiBEZXRhaWxzLk9wcG9ydHVuaXR5SXRlbSxcbiAgaGVhZGluZ3M6IERldGFpbHMuVGFibGVDb2x1bW5IZWFkaW5nW10sXG4pOiBUYWJsZVJvd09iamVjdCB7XG4gIGNvbnN0IGtleXMgPSBuZXcgU2V0KGhlYWRpbmdzLm1hcCgoeyBrZXkgfSkgPT4ga2V5KSk7XG4gIGNvbnN0IHZhbHVlVHlwZXNCeUtleSA9IG5ldyBNYXAoXG4gICAgaGVhZGluZ3MubWFwKCh7IGtleSwgdmFsdWVUeXBlIH0pID0+IFtrZXksIHZhbHVlVHlwZV0pLFxuICApO1xuXG4gIHJldHVybiB7XG4gICAgLi4uKE9iamVjdC5mcm9tRW50cmllcyhcbiAgICAgIE9iamVjdC5lbnRyaWVzKG9wcG9ydHVuaXR5SXRlbSlcbiAgICAgICAgLy8gZm9yd2FyZCBvbmx5IHByb3BlcnRpZXMgd2l0aCBhIGdpdmVuIHZhbHVlXG4gICAgICAgIC5maWx0ZXIoKFtrZXldKSA9PiBrZXlzLmhhcyhrZXkpKVxuICAgICAgICAubWFwKChba2V5LCB2YWx1ZV0pID0+IHtcbiAgICAgICAgICBjb25zdCB2YWx1ZVR5cGUgPSB2YWx1ZVR5cGVzQnlLZXkuZ2V0KGtleSkgYXMgRGV0YWlscy5JdGVtVmFsdWVUeXBlO1xuICAgICAgICAgIHJldHVybiBwYXJzZU9wcG9ydHVuaXR5RW50cnkoW2tleSwgdmFsdWVdLCB2YWx1ZVR5cGUpO1xuICAgICAgICB9KSxcbiAgICApIGFzIFRhYmxlUm93T2JqZWN0KSxcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlT3Bwb3J0dW5pdHlFbnRyeShcbiAgW2tleSwgdmFsdWVdOiBbXG4gICAga2V5b2YgRGV0YWlscy5PcHBvcnR1bml0eUl0ZW0sXG4gICAgRGV0YWlscy5PcHBvcnR1bml0eUl0ZW1bc3RyaW5nXSxcbiAgXSxcbiAgdmFsdWVUeXBlOiBEZXRhaWxzLkl0ZW1WYWx1ZVR5cGUsXG4pIHtcbiAgc3dpdGNoIChrZXkpIHtcbiAgICBjYXNlICd1cmwnOlxuICAgICAgcmV0dXJuIFtrZXksIGh0bWwubGluayhTdHJpbmcodmFsdWUpKV07XG4gICAgY2FzZSAnd2FzdGVkUGVyY2VudCc6XG4gICAgICByZXR1cm4gW2tleSwgYCR7TnVtYmVyKHZhbHVlKS50b0ZpeGVkKDIpfSAlYF07XG4gICAgY2FzZSAndG90YWxCeXRlcyc6XG4gICAgY2FzZSAnd2FzdGVkQnl0ZXMnOlxuICAgICAgcmV0dXJuIFtrZXksIGZvcm1hdEJ5dGVzKE51bWJlcih2YWx1ZSkpXTtcbiAgICBjYXNlICd3YXN0ZWRNcyc6XG4gICAgICByZXR1cm4gW2tleSwgZm9ybWF0RHVyYXRpb24oTnVtYmVyKHZhbHVlKSldO1xuICAgIGRlZmF1bHQ6XG4gICAgICByZXR1cm4gcGFyc2VUYWJsZUVudHJ5KFtrZXksIHZhbHVlXSwgdmFsdWVUeXBlKTtcbiAgfVxufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9ydW5uZXIvZGV0YWlscy90YWJsZS50eXBlLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL2RldGFpbHNcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9ydW5uZXIvZGV0YWlscy90YWJsZS50eXBlLnRzXCI7aW1wb3J0IHR5cGUgRGV0YWlscyBmcm9tICdsaWdodGhvdXNlL3R5cGVzL2xoci9hdWRpdC1kZXRhaWxzJztcbmltcG9ydCB7XG4gIHR5cGUgVGFibGUsXG4gIHR5cGUgVGFibGVDb2x1bW5PYmplY3QsXG4gIHR5cGUgVGFibGVSb3dPYmplY3QsXG4gIHRhYmxlU2NoZW1hLFxufSBmcm9tICdAY29kZS1wdXNodXAvbW9kZWxzJztcbmltcG9ydCB7IGZvcm1hdFRhYmxlSXRlbVByb3BlcnR5VmFsdWUgfSBmcm9tICcuL2l0ZW0tdmFsdWUuanMnO1xuaW1wb3J0IHsgTGlnaHRob3VzZUF1ZGl0RGV0YWlsc1BhcnNpbmdFcnJvciB9IGZyb20gJy4vdXRpbHMuanMnO1xuXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VUYWJsZVRvQXVkaXREZXRhaWxzVGFibGUoXG4gIGRldGFpbHM6IERldGFpbHMuVGFibGUsXG4pOiBUYWJsZSB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IHsgaGVhZGluZ3M6IHJhd0hlYWRpbmdzLCBpdGVtcyB9ID0gZGV0YWlscztcblxuICBpZiAoaXRlbXMubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuXG4gIHRyeSB7XG4gICAgcmV0dXJuIHRhYmxlU2NoZW1hKCkucGFyc2Uoe1xuICAgICAgY29sdW1uczogcGFyc2VUYWJsZUNvbHVtbnMocmF3SGVhZGluZ3MpLFxuICAgICAgcm93czogaXRlbXMubWFwKHJvdyA9PiBwYXJzZVRhYmxlUm93KHJvdywgcmF3SGVhZGluZ3MpKSxcbiAgICB9KTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICB0aHJvdyBuZXcgTGlnaHRob3VzZUF1ZGl0RGV0YWlsc1BhcnNpbmdFcnJvcihcbiAgICAgICd0YWJsZScsXG4gICAgICB7IGl0ZW1zLCBoZWFkaW5nczogcmF3SGVhZGluZ3MgfSxcbiAgICAgIChlcnJvciBhcyBFcnJvcikubWVzc2FnZS50b1N0cmluZygpLFxuICAgICk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlVGFibGVDb2x1bW5zKFxuICByYXdIZWFkaW5nczogRGV0YWlscy5UYWJsZUNvbHVtbkhlYWRpbmdbXSxcbik6IFRhYmxlQ29sdW1uT2JqZWN0W10ge1xuICByZXR1cm4gcmF3SGVhZGluZ3MubWFwKCh7IGtleSwgbGFiZWwgfSkgPT4gKHtcbiAgICBrZXk6IGtleSA/PyAnJyxcbiAgICAuLi4odHlwZW9mIGxhYmVsID09PSAnc3RyaW5nJyAmJiBsYWJlbC5sZW5ndGggPiAwID8geyBsYWJlbCB9IDoge30pLFxuICAgIGFsaWduOiAnbGVmdCcsXG4gIH0pKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlVGFibGVSb3coXG4gIHRhYmxlSXRlbTogRGV0YWlscy5UYWJsZUl0ZW0sXG4gIGhlYWRpbmdzOiBEZXRhaWxzLlRhYmxlQ29sdW1uSGVhZGluZ1tdLFxuKTogVGFibGVSb3dPYmplY3Qge1xuICBjb25zdCBrZXlzID0gbmV3IFNldChoZWFkaW5ncy5tYXAoKHsga2V5IH0pID0+IGtleSkpO1xuICBjb25zdCB2YWx1ZVR5cGVzQnlLZXkgPSBuZXcgTWFwKFxuICAgIGhlYWRpbmdzLm1hcCgoeyBrZXksIHZhbHVlVHlwZSB9KSA9PiBba2V5LCB2YWx1ZVR5cGVdKSxcbiAgKTtcblxuICByZXR1cm4gT2JqZWN0LmZyb21FbnRyaWVzKFxuICAgIE9iamVjdC5lbnRyaWVzKHRhYmxlSXRlbSlcbiAgICAgIC5maWx0ZXIoKFtrZXldKSA9PiBrZXlzLmhhcyhrZXkpKVxuICAgICAgLm1hcCgoW2tleSwgdmFsdWVdKSA9PiB7XG4gICAgICAgIGNvbnN0IHZhbHVlVHlwZSA9IHZhbHVlVHlwZXNCeUtleS5nZXQoa2V5KTtcbiAgICAgICAgcmV0dXJuIHBhcnNlVGFibGVFbnRyeShba2V5LCB2YWx1ZV0sIHZhbHVlVHlwZSk7XG4gICAgICB9KSxcbiAgKSBhcyBUYWJsZVJvd09iamVjdDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlVGFibGVFbnRyeTxUIGV4dGVuZHMgRGV0YWlscy5UYWJsZUl0ZW0+KFxuICBba2V5LCB2YWx1ZV06IFtrZXlvZiBULCBUW2tleW9mIFRdXSxcbiAgdmFsdWVUeXBlPzogRGV0YWlscy5JdGVtVmFsdWVUeXBlLFxuKTogW2tleW9mIFQsIERldGFpbHMuSXRlbVZhbHVlIHwgdW5kZWZpbmVkXSB7XG4gIGlmICh2YWx1ZSA9PSBudWxsKSB7XG4gICAgcmV0dXJuIFtrZXksIHZhbHVlXTtcbiAgfVxuXG4gIHJldHVybiBba2V5LCBmb3JtYXRUYWJsZUl0ZW1Qcm9wZXJ0eVZhbHVlKHZhbHVlLCB2YWx1ZVR5cGUpXTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL2RldGFpbHMvaXRlbS12YWx1ZS50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lci9kZXRhaWxzXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL2RldGFpbHMvaXRlbS12YWx1ZS50c1wiO2ltcG9ydCB7IGJvbGQgfSBmcm9tICdhbnNpcyc7XG5pbXBvcnQgdHlwZSB7IEljdU1lc3NhZ2UgfSBmcm9tICdsaWdodGhvdXNlJztcbmltcG9ydCB0eXBlIERldGFpbHMgZnJvbSAnbGlnaHRob3VzZS90eXBlcy9saHIvYXVkaXQtZGV0YWlscyc7XG5pbXBvcnQge1xuICBmb3JtYXRCeXRlcyxcbiAgZm9ybWF0RHVyYXRpb24sXG4gIGh0bWwsXG4gIHRydW5jYXRlVGV4dCxcbiAgdWksXG59IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5cbmV4cG9ydCB0eXBlIFByaW1pdGl2ZUl0ZW1WYWx1ZSA9IHN0cmluZyB8IG51bWJlciB8IGJvb2xlYW47XG5leHBvcnQgdHlwZSBPYmplY3RJdGVtVmFsdWUgPSBFeGNsdWRlPFxuICBEZXRhaWxzLkl0ZW1WYWx1ZSxcbiAgUHJpbWl0aXZlSXRlbVZhbHVlIHwgSWN1TWVzc2FnZVxuPjtcbmV4cG9ydCB0eXBlIFNpbXBsZUl0ZW1WYWx1ZSA9XG4gIHwgRXh0cmFjdDxcbiAgICAgIE9iamVjdEl0ZW1WYWx1ZSxcbiAgICAgIERldGFpbHMuTnVtZXJpY1ZhbHVlIHwgRGV0YWlscy5Db2RlVmFsdWUgfCBEZXRhaWxzLlVybFZhbHVlXG4gICAgPlxuICB8IFByaW1pdGl2ZUl0ZW1WYWx1ZTtcblxuZXhwb3J0IGZ1bmN0aW9uIHRyaW1TbGljZShpdGVtPzogUHJpbWl0aXZlSXRlbVZhbHVlLCBtYXhMZW5ndGggPSAwKSB7XG4gIGNvbnN0IHN0ciA9IFN0cmluZyhpdGVtKS50cmltKCk7XG4gIHJldHVybiBtYXhMZW5ndGggPiAwID8gc3RyLnNsaWNlKDAsIG1heExlbmd0aCkgOiBzdHI7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBwYXJzZU5vZGVWYWx1ZShub2RlPzogRGV0YWlscy5Ob2RlVmFsdWUpOiBzdHJpbmcge1xuICBjb25zdCB7IHNlbGVjdG9yID0gJycgfSA9IG5vZGUgPz8ge307XG4gIHJldHVybiBzZWxlY3Rvcjtcbn1cblxuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG1heC1saW5lcy1wZXItZnVuY3Rpb25cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRUYWJsZUl0ZW1Qcm9wZXJ0eVZhbHVlKFxuICBpdGVtVmFsdWU/OiBEZXRhaWxzLkl0ZW1WYWx1ZSxcbiAgaXRlbVZhbHVlRm9ybWF0PzogRGV0YWlscy5JdGVtVmFsdWVUeXBlLFxuKSB7XG4gIC8vIG51bGxcbiAgaWYgKGl0ZW1WYWx1ZSA9PSBudWxsKSB7XG4gICAgcmV0dXJuICcnO1xuICB9XG5cbiAgLy8gUHJpbWl0aXZlIFZhbHVlc1xuICBpZiAoaXRlbVZhbHVlRm9ybWF0ID09IG51bGwpIHtcbiAgICBpZiAodHlwZW9mIGl0ZW1WYWx1ZSA9PT0gJ3N0cmluZycpIHtcbiAgICAgIHJldHVybiB0cmltU2xpY2UoaXRlbVZhbHVlKTtcbiAgICB9XG5cbiAgICBpZiAodHlwZW9mIGl0ZW1WYWx1ZSA9PT0gJ251bWJlcicpIHtcbiAgICAgIHJldHVybiBOdW1iZXIoaXRlbVZhbHVlKTtcbiAgICB9XG5cbiAgICBpZiAodHlwZW9mIGl0ZW1WYWx1ZSA9PT0gJ2Jvb2xlYW4nKSB7XG4gICAgICByZXR1cm4gaXRlbVZhbHVlO1xuICAgIH1cbiAgfVxuXG4gIGNvbnN0IHBhcnNlZEl0ZW1WYWx1ZSA9IHBhcnNlVGFibGVJdGVtUHJvcGVydHlWYWx1ZShpdGVtVmFsdWUpO1xuXG4gIC8qIGVzbGludC1kaXNhYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1tYWdpYy1udW1iZXJzICovXG4gIHN3aXRjaCAoaXRlbVZhbHVlRm9ybWF0KSB7XG4gICAgY2FzZSAnYnl0ZXMnOlxuICAgICAgcmV0dXJuIGZvcm1hdEJ5dGVzKE51bWJlcihwYXJzZWRJdGVtVmFsdWUpKTtcbiAgICBjYXNlICdjb2RlJzpcbiAgICAgIHJldHVybiBodG1sLmNvZGUodHJpbVNsaWNlKHBhcnNlZEl0ZW1WYWx1ZSBhcyBzdHJpbmcpKTtcbiAgICBjYXNlICdsaW5rJzpcbiAgICAgIGNvbnN0IGxpbmsgPSBwYXJzZWRJdGVtVmFsdWUgYXMgRGV0YWlscy5MaW5rVmFsdWU7XG4gICAgICByZXR1cm4gaHRtbC5saW5rKGxpbmsudXJsLCBsaW5rLnRleHQpO1xuICAgIGNhc2UgJ3VybCc6XG4gICAgICBjb25zdCB1cmwgPSBwYXJzZWRJdGVtVmFsdWUgYXMgc3RyaW5nO1xuICAgICAgcmV0dXJuIGh0bWwubGluayh1cmwpO1xuICAgIGNhc2UgJ3RpbWVzcGFuTXMnOlxuICAgIGNhc2UgJ21zJzpcbiAgICAgIHJldHVybiBmb3JtYXREdXJhdGlvbihOdW1iZXIocGFyc2VkSXRlbVZhbHVlKSk7XG4gICAgY2FzZSAnbm9kZSc6XG4gICAgICByZXR1cm4gcGFyc2VOb2RlVmFsdWUoaXRlbVZhbHVlIGFzIERldGFpbHMuTm9kZVZhbHVlKTtcbiAgICBjYXNlICdzb3VyY2UtbG9jYXRpb24nOlxuICAgICAgcmV0dXJuIHRydW5jYXRlVGV4dChTdHJpbmcocGFyc2VkSXRlbVZhbHVlKSwgMjAwKTtcbiAgICBjYXNlICdudW1lcmljJzpcbiAgICAgIGNvbnN0IG51bSA9IE51bWJlcihwYXJzZWRJdGVtVmFsdWUpO1xuICAgICAgaWYgKG51bS50b0ZpeGVkKDMpLnRvU3RyaW5nKCkuZW5kc1dpdGgoJy4wMDAnKSkge1xuICAgICAgICByZXR1cm4gU3RyaW5nKG51bSk7XG4gICAgICB9XG4gICAgICByZXR1cm4gU3RyaW5nKG51bS50b0ZpeGVkKDMpKTtcbiAgICBjYXNlICd0ZXh0JzpcbiAgICAgIHJldHVybiB0cnVuY2F0ZVRleHQoU3RyaW5nKHBhcnNlZEl0ZW1WYWx1ZSksIDUwMCk7XG4gICAgY2FzZSAnbXVsdGknOiAvLyBAVE9ET1xuICAgICAgLy8gQFRPRE8gbG9nIHZlcmJvc2UgZmlyc3QsIHRoZW4gaW1wbGVtZW50IGRhdGEgdHlwZVxuICAgICAgdWkoKS5sb2dnZXIuaW5mbyhgRm9ybWF0IHR5cGUgJHtib2xkKCdtdWx0aScpfSBpcyBub3QgaW1wbGVtZW50ZWRgKTtcbiAgICAgIHJldHVybiAnJztcbiAgICBjYXNlICd0aHVtYm5haWwnOiAvLyBAVE9ET1xuICAgICAgLy8gQFRPRE8gbG9nIHZlcmJvc2UgZmlyc3QsIHRoZW4gaW1wbGVtZW50IGRhdGEgdHlwZVxuICAgICAgdWkoKS5sb2dnZXIuaW5mbyhgRm9ybWF0IHR5cGUgJHtib2xkKCd0aHVtYm5haWwnKX0gaXMgbm90IGltcGxlbWVudGVkYCk7XG4gICAgICByZXR1cm4gJyc7XG4gIH1cbiAgLyogZXNsaW50LWVuYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tbWFnaWMtbnVtYmVycyAqL1xuXG4gIHJldHVybiBpdGVtVmFsdWU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBwYXJzZVNpbXBsZUl0ZW1WYWx1ZShcbiAgaXRlbTogU2ltcGxlSXRlbVZhbHVlLFxuKTogUHJpbWl0aXZlSXRlbVZhbHVlIHtcbiAgaWYgKHR5cGVvZiBpdGVtID09PSAnb2JqZWN0Jykge1xuICAgIGNvbnN0IHZhbHVlID0gaXRlbS52YWx1ZTtcbiAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnb2JqZWN0Jykge1xuICAgICAgcmV0dXJuIHZhbHVlLmZvcm1hdHRlZERlZmF1bHQ7XG4gICAgfVxuICAgIHJldHVybiB2YWx1ZTtcbiAgfVxuICByZXR1cm4gaXRlbTtcbn1cblxuLy8gQFRPRE8gZXh0cmFjdCBMaW5rIHR5cGUgZnJvbSBsb2dpY1xuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlVGFibGVJdGVtUHJvcGVydHlWYWx1ZShcbiAgaXRlbVZhbHVlPzogRGV0YWlscy5JdGVtVmFsdWUsXG4pOiBQcmltaXRpdmVJdGVtVmFsdWUgfCBEZXRhaWxzLkxpbmtWYWx1ZSB7XG4gIGlmIChpdGVtVmFsdWUgPT0gbnVsbCkge1xuICAgIHJldHVybiAnJztcbiAgfVxuXG4gIC8vIFByaW1pdGl2ZSBWYWx1ZXNcbiAgaWYgKFxuICAgIHR5cGVvZiBpdGVtVmFsdWUgPT09ICdzdHJpbmcnIHx8XG4gICAgdHlwZW9mIGl0ZW1WYWx1ZSA9PT0gJ251bWJlcicgfHxcbiAgICB0eXBlb2YgaXRlbVZhbHVlID09PSAnYm9vbGVhbidcbiAgKSB7XG4gICAgcmV0dXJuIHBhcnNlU2ltcGxlSXRlbVZhbHVlKGl0ZW1WYWx1ZSk7XG4gIH1cblxuICAvLyBPYmplY3QgVmFsdWVzXG4gIGNvbnN0IG9iamVjdFZhbHVlID0gaXRlbVZhbHVlIGFzIE9iamVjdEl0ZW1WYWx1ZTtcbiAgY29uc3QgeyB0eXBlIH0gPSBvYmplY3RWYWx1ZTtcbiAgc3dpdGNoICh0eXBlKSB7XG4gICAgY2FzZSAnY29kZSc6XG4gICAgY2FzZSAndXJsJzpcbiAgICAgIHJldHVybiBTdHJpbmcocGFyc2VTaW1wbGVJdGVtVmFsdWUob2JqZWN0VmFsdWUpKTtcbiAgICBjYXNlICdub2RlJzpcbiAgICAgIHJldHVybiBwYXJzZU5vZGVWYWx1ZShvYmplY3RWYWx1ZSk7XG4gICAgY2FzZSAnbGluayc6XG4gICAgICByZXR1cm4gb2JqZWN0VmFsdWU7XG4gICAgY2FzZSAnbnVtZXJpYyc6XG4gICAgICByZXR1cm4gTnVtYmVyKHBhcnNlU2ltcGxlSXRlbVZhbHVlKG9iamVjdFZhbHVlKSk7XG4gICAgY2FzZSAnc291cmNlLWxvY2F0aW9uJzpcbiAgICAgIGNvbnN0IHsgdXJsIH0gPSBvYmplY3RWYWx1ZTtcbiAgICAgIHJldHVybiBTdHJpbmcodXJsKTtcbiAgICBjYXNlICdzdWJpdGVtcyc6XG4gICAgICAvLyBAVE9ETyBsb2cgdmVyYm9zZSBmaXJzdCwgdGhlbiBpbXBsZW1lbnQgZGF0YSB0eXBlXG4gICAgICB1aSgpLmxvZ2dlci5pbmZvKGBWYWx1ZSB0eXBlICR7Ym9sZCgnc3ViaXRlbXMnKX0gaXMgbm90IGltcGxlbWVudGVkYCk7XG4gICAgICByZXR1cm4gJyc7XG4gICAgY2FzZSAnZGVidWdkYXRhJzpcbiAgICAgIC8vIEBUT0RPIGxvZyB2ZXJib3NlIGZpcnN0LCB0aGVuIGltcGxlbWVudCBkYXRhIHR5cGVcbiAgICAgIHVpKCkubG9nZ2VyLmluZm8oYFZhbHVlIHR5cGUgJHtib2xkKCdkZWJ1Z2RhdGEnKX0gaXMgbm90IGltcGxlbWVudGVkYCwge1xuICAgICAgICBzaWxlbnQ6IHRydWUsXG4gICAgICB9KTtcbiAgICAgIHJldHVybiAnJztcbiAgfVxuICAvLyBJY3VNZXNzYWdlXG4gIHJldHVybiBwYXJzZVNpbXBsZUl0ZW1WYWx1ZShvYmplY3RWYWx1ZSBhcyBTaW1wbGVJdGVtVmFsdWUpO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi9ydW5uZXIvZGV0YWlscy91dGlscy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3J1bm5lci9kZXRhaWxzXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWIvcnVubmVyL2RldGFpbHMvdXRpbHMudHNcIjtpbXBvcnQgeyBib2xkIH0gZnJvbSAnYW5zaXMnO1xuaW1wb3J0IHR5cGUgRGV0YWlscyBmcm9tICdsaWdodGhvdXNlL3R5cGVzL2xoci9hdWRpdC1kZXRhaWxzJztcblxuZXhwb3J0IGNsYXNzIExpZ2h0aG91c2VBdWRpdERldGFpbHNQYXJzaW5nRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHR5cGU6IERldGFpbHNbJ3R5cGUnXSxcbiAgICByYXdUYWJsZTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gICAgZXJyb3I6IHN0cmluZyxcbiAgKSB7XG4gICAgc3VwZXIoXG4gICAgICBgUGFyc2luZyBsaWdodGhvdXNlIHJlcG9ydCBkZXRhaWxzICR7Ym9sZChcbiAgICAgICAgdHlwZSxcbiAgICAgICl9IGZhaWxlZDogXFxuUmF3IGRhdGE6XFxuICR7SlNPTi5zdHJpbmdpZnkocmF3VGFibGUsIG51bGwsIDIpfVxcbiR7ZXJyb3J9YCxcbiAgICApO1xuICB9XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvbGliL3V0aWxzLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3BsdWdpbi1saWdodGhvdXNlL3NyYy9saWJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvcGx1Z2luLWxpZ2h0aG91c2Uvc3JjL2xpYi91dGlscy50c1wiO2ltcG9ydCB0eXBlIHsgQXVkaXQsIENhdGVnb3J5UmVmLCBHcm91cCB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgZmlsdGVySXRlbVJlZnNCeSwgdG9BcnJheSB9IGZyb20gJ0Bjb2RlLXB1c2h1cC91dGlscyc7XG5pbXBvcnQgeyBMSUdIVEhPVVNFX1BMVUdJTl9TTFVHIH0gZnJvbSAnLi9jb25zdGFudHMuanMnO1xuaW1wb3J0IHR5cGUgeyBMaWdodGhvdXNlQ2xpRmxhZ3MgfSBmcm9tICcuL3J1bm5lci90eXBlcy5qcyc7XG5cbmV4cG9ydCB0eXBlIExpZ2h0aG91c2VHcm91cFNsdWdzID1cbiAgfCAncGVyZm9ybWFuY2UnXG4gIHwgJ2FjY2Vzc2liaWxpdHknXG4gIHwgJ2Jlc3QtcHJhY3RpY2VzJ1xuICB8ICdzZW8nXG4gIHwgJ3B3YSc7XG5cbmV4cG9ydCBmdW5jdGlvbiBsaWdodGhvdXNlR3JvdXBSZWYoXG4gIGdyb3VwU2x1ZzogTGlnaHRob3VzZUdyb3VwU2x1Z3MsXG4gIHdlaWdodCA9IDEsXG4pOiBDYXRlZ29yeVJlZiB7XG4gIHJldHVybiB7XG4gICAgcGx1Z2luOiBMSUdIVEhPVVNFX1BMVUdJTl9TTFVHLFxuICAgIHNsdWc6IGdyb3VwU2x1ZyxcbiAgICB0eXBlOiAnZ3JvdXAnLFxuICAgIHdlaWdodCxcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGxpZ2h0aG91c2VBdWRpdFJlZihhdWRpdFNsdWc6IHN0cmluZywgd2VpZ2h0ID0gMSk6IENhdGVnb3J5UmVmIHtcbiAgcmV0dXJuIHtcbiAgICBwbHVnaW46IExJR0hUSE9VU0VfUExVR0lOX1NMVUcsXG4gICAgc2x1ZzogYXVkaXRTbHVnLFxuICAgIHR5cGU6ICdhdWRpdCcsXG4gICAgd2VpZ2h0LFxuICB9O1xufVxuXG5leHBvcnQgY2xhc3MgQXVkaXRzTm90SW1wbGVtZW50ZWRFcnJvciBleHRlbmRzIEVycm9yIHtcbiAgY29uc3RydWN0b3IoYXVkaXRTbHVnczogc3RyaW5nW10pIHtcbiAgICBzdXBlcihgYXVkaXRzOiBcIiR7YXVkaXRTbHVncy5qb2luKCcsICcpfVwiIG5vdCBpbXBsZW1lbnRlZGApO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB2YWxpZGF0ZUF1ZGl0cyhhdWRpdHM6IEF1ZGl0W10sIG9ubHlBdWRpdHM6IHN0cmluZ1tdKTogYm9vbGVhbiB7XG4gIGNvbnN0IG1pc3NpbmdBdWR0aXMgPSB0b0FycmF5KG9ubHlBdWRpdHMpLmZpbHRlcihcbiAgICBzbHVnID0+ICFhdWRpdHMuc29tZShhdWRpdCA9PiBhdWRpdC5zbHVnID09PSBzbHVnKSxcbiAgKTtcbiAgaWYgKG1pc3NpbmdBdWR0aXMubGVuZ3RoID4gMCkge1xuICAgIHRocm93IG5ldyBBdWRpdHNOb3RJbXBsZW1lbnRlZEVycm9yKG1pc3NpbmdBdWR0aXMpO1xuICB9XG4gIHJldHVybiB0cnVlO1xufVxuXG5leHBvcnQgY2xhc3MgQ2F0ZWdvcmllc05vdEltcGxlbWVudGVkRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIGNvbnN0cnVjdG9yKGNhdGVnb3J5U2x1Z3M6IHN0cmluZ1tdKSB7XG4gICAgc3VwZXIoYGNhdGVnb3JpZXM6IFwiJHtjYXRlZ29yeVNsdWdzLmpvaW4oJywgJyl9XCIgbm90IGltcGxlbWVudGVkYCk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlT25seUNhdGVnb3JpZXMoXG4gIGdyb3VwczogR3JvdXBbXSxcbiAgb25seUNhdGVnb3JpZXM6IHN0cmluZyB8IHN0cmluZ1tdLFxuKTogYm9vbGVhbiB7XG4gIGNvbnN0IG1pc3NpbmdDYXRlZ29yaWVzID0gdG9BcnJheShvbmx5Q2F0ZWdvcmllcykuZmlsdGVyKHNsdWcgPT5cbiAgICBncm91cHMuZXZlcnkoZ3JvdXAgPT4gZ3JvdXAuc2x1ZyAhPT0gc2x1ZyksXG4gICk7XG4gIGlmIChtaXNzaW5nQ2F0ZWdvcmllcy5sZW5ndGggPiAwKSB7XG4gICAgdGhyb3cgbmV3IENhdGVnb3JpZXNOb3RJbXBsZW1lbnRlZEVycm9yKG1pc3NpbmdDYXRlZ29yaWVzKTtcbiAgfVxuICByZXR1cm4gdHJ1ZTtcbn1cblxuZXhwb3J0IHR5cGUgRmlsdGVyT3B0aW9ucyA9IFBhcnRpYWw8XG4gIFBpY2s8TGlnaHRob3VzZUNsaUZsYWdzLCAnb25seUF1ZGl0cycgfCAnb25seUNhdGVnb3JpZXMnIHwgJ3NraXBBdWRpdHMnPlxuPjtcblxuZXhwb3J0IGZ1bmN0aW9uIGZpbHRlckF1ZGl0c0FuZEdyb3Vwc0J5T25seU9wdGlvbnMoXG4gIGF1ZGl0czogQXVkaXRbXSxcbiAgZ3JvdXBzOiBHcm91cFtdLFxuICBvcHRpb25zPzogRmlsdGVyT3B0aW9ucyxcbik6IHtcbiAgYXVkaXRzOiBBdWRpdFtdO1xuICBncm91cHM6IEdyb3VwW107XG59IHtcbiAgY29uc3Qge1xuICAgIG9ubHlBdWRpdHMgPSBbXSxcbiAgICBza2lwQXVkaXRzID0gW10sXG4gICAgb25seUNhdGVnb3JpZXMgPSBbXSxcbiAgfSA9IG9wdGlvbnMgPz8ge307XG5cbiAgLy8gY2F0ZWdvcnkgd2lucyBvdmVyIGF1ZGl0c1xuICBpZiAob25seUNhdGVnb3JpZXMubGVuZ3RoID4gMCkge1xuICAgIHZhbGlkYXRlT25seUNhdGVnb3JpZXMoZ3JvdXBzLCBvbmx5Q2F0ZWdvcmllcyk7XG5cbiAgICBjb25zdCBjYXRlZ29yeVNsdWdzID0gbmV3IFNldChvbmx5Q2F0ZWdvcmllcyk7XG4gICAgY29uc3QgZmlsdGVyZWRHcm91cHM6IEdyb3VwW10gPSBncm91cHMuZmlsdGVyKCh7IHNsdWcgfSkgPT5cbiAgICAgIGNhdGVnb3J5U2x1Z3MuaGFzKHNsdWcpLFxuICAgICk7XG4gICAgY29uc3QgYXVkaXRTbHVnc0Zyb21SZW1haW5pbmdHcm91cHMgPSBuZXcgU2V0KFxuICAgICAgZmlsdGVyZWRHcm91cHMuZmxhdE1hcCgoeyByZWZzIH0pID0+IHJlZnMubWFwKCh7IHNsdWcgfSkgPT4gc2x1ZykpLFxuICAgICk7XG4gICAgcmV0dXJuIHtcbiAgICAgIGF1ZGl0czogYXVkaXRzLmZpbHRlcigoeyBzbHVnIH0pID0+XG4gICAgICAgIGF1ZGl0U2x1Z3NGcm9tUmVtYWluaW5nR3JvdXBzLmhhcyhzbHVnKSxcbiAgICAgICksXG4gICAgICBncm91cHM6IGZpbHRlcmVkR3JvdXBzLFxuICAgIH07XG4gIH0gZWxzZSBpZiAob25seUF1ZGl0cy5sZW5ndGggPiAwIHx8IHNraXBBdWRpdHMubGVuZ3RoID4gMCkge1xuICAgIHZhbGlkYXRlQXVkaXRzKGF1ZGl0cywgb25seUF1ZGl0cyk7XG4gICAgdmFsaWRhdGVBdWRpdHMoYXVkaXRzLCBza2lwQXVkaXRzKTtcbiAgICBjb25zdCBvbmx5QXVkaXRTbHVncyA9IG5ldyBTZXQob25seUF1ZGl0cyk7XG4gICAgY29uc3Qgc2tpcEF1ZGl0U2x1Z3MgPSBuZXcgU2V0KHNraXBBdWRpdHMpO1xuICAgIGNvbnN0IGZpbHRlckF1ZGl0cyA9ICh7IHNsdWcgfTogUGljazxBdWRpdCwgJ3NsdWcnPikgPT5cbiAgICAgICEoXG4gICAgICAgIC8vIGF1ZGl0IGlzIE5PVCBpbiBnaXZlbiBvbmx5QXVkaXRTbHVnc1xuICAgICAgICAoXG4gICAgICAgICAgKG9ubHlBdWRpdHMubGVuZ3RoID4gMCAmJiAhb25seUF1ZGl0U2x1Z3MuaGFzKHNsdWcpKSB8fFxuICAgICAgICAgIC8vIGF1ZGl0IElTIGluIGdpdmVuIHNraXBBdWRpdFNsdWdzXG4gICAgICAgICAgKHNraXBBdWRpdHMubGVuZ3RoID4gMCAmJiBza2lwQXVkaXRTbHVncy5oYXMoc2x1ZykpXG4gICAgICAgIClcbiAgICAgICk7XG4gICAgcmV0dXJuIHtcbiAgICAgIGF1ZGl0czogYXVkaXRzLmZpbHRlcihmaWx0ZXJBdWRpdHMpLFxuICAgICAgZ3JvdXBzOiBmaWx0ZXJJdGVtUmVmc0J5KGdyb3VwcywgZmlsdGVyQXVkaXRzKSxcbiAgICB9O1xuICB9XG4gIC8vIHJldHVybiB1bmNoYW5nZWRcbiAgcmV0dXJuIHtcbiAgICBhdWRpdHMsXG4gICAgZ3JvdXBzLFxuICB9O1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvY29kZS1wdXNodXAucHJlc2V0LnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL2NvZGUtcHVzaHVwLnByZXNldC50c1wiO2ltcG9ydCB0eXBlIHtcbiAgQ2F0ZWdvcnlDb25maWcsXG4gIENvcmVDb25maWcsXG59IGZyb20gJy4vcGFja2FnZXMvbW9kZWxzL3NyYy9pbmRleC5qcyc7XG5pbXBvcnQgY292ZXJhZ2VQbHVnaW4sIHtcbiAgZ2V0TnhDb3ZlcmFnZVBhdGhzLFxufSBmcm9tICcuL3BhY2thZ2VzL3BsdWdpbi1jb3ZlcmFnZS9zcmMvaW5kZXguanMnO1xuaW1wb3J0IGRvY0NvdmVyYWdlUGx1Z2luLCB7IERvY0NvdmVyYWdlUGx1Z2luQ29uZmlnIH0gZnJvbSAnLi9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9pbmRleC5qcyc7XG5pbXBvcnQgeyBncm91cHMsIFBMVUdJTl9TTFVHIH0gZnJvbSAnLi9wYWNrYWdlcy9wbHVnaW4tZG9jLWNvdmVyYWdlL3NyYy9saWIvY29uc3RhbnRzLmpzJztcbmltcG9ydCB7IGZpbHRlckdyb3Vwc0J5T25seUF1ZGl0cyB9IGZyb20gJy4vcGFja2FnZXMvcGx1Z2luLWRvYy1jb3ZlcmFnZS9zcmMvbGliL3V0aWxzLmpzJztcbmltcG9ydCBlc2xpbnRQbHVnaW4sIHtcbiAgZXNsaW50Q29uZmlnRnJvbUFsbE54UHJvamVjdHMsXG4gIGVzbGludENvbmZpZ0Zyb21OeFByb2plY3QsXG59IGZyb20gJy4vcGFja2FnZXMvcGx1Z2luLWVzbGludC9zcmMvaW5kZXguanMnO1xuaW1wb3J0IGpzUGFja2FnZXNQbHVnaW4gZnJvbSAnLi9wYWNrYWdlcy9wbHVnaW4tanMtcGFja2FnZXMvc3JjL2luZGV4LmpzJztcbmltcG9ydCBsaWdodGhvdXNlUGx1Z2luLCB7XG4gIGxpZ2h0aG91c2VHcm91cFJlZixcbn0gZnJvbSAnLi9wYWNrYWdlcy9wbHVnaW4tbGlnaHRob3VzZS9zcmMvaW5kZXguanMnO1xuXG5leHBvcnQgY29uc3QganNQYWNrYWdlc0NhdGVnb3JpZXM6IENhdGVnb3J5Q29uZmlnW10gPSBbXG4gIHtcbiAgICBzbHVnOiAnc2VjdXJpdHknLFxuICAgIHRpdGxlOiAnU2VjdXJpdHknLFxuICAgIGRlc2NyaXB0aW9uOiAnRmluZHMga25vd24gKip2dWxuZXJhYmlsaXRpZXMqKiBpbiAzcmQtcGFydHkgcGFja2FnZXMuJyxcbiAgICByZWZzOiBbXG4gICAgICB7XG4gICAgICAgIHR5cGU6ICdncm91cCcsXG4gICAgICAgIHBsdWdpbjogJ2pzLXBhY2thZ2VzJyxcbiAgICAgICAgc2x1ZzogJ25wbS1hdWRpdCcsXG4gICAgICAgIHdlaWdodDogMSxcbiAgICAgIH0sXG4gICAgXSxcbiAgfSxcbiAge1xuICAgIHNsdWc6ICd1cGRhdGVzJyxcbiAgICB0aXRsZTogJ1VwZGF0ZXMnLFxuICAgIGRlc2NyaXB0aW9uOiAnRmluZHMgKipvdXRkYXRlZCoqIDNyZC1wYXJ0eSBwYWNrYWdlcy4nLFxuICAgIHJlZnM6IFtcbiAgICAgIHtcbiAgICAgICAgdHlwZTogJ2dyb3VwJyxcbiAgICAgICAgcGx1Z2luOiAnanMtcGFja2FnZXMnLFxuICAgICAgICBzbHVnOiAnbnBtLW91dGRhdGVkJyxcbiAgICAgICAgd2VpZ2h0OiAxLFxuICAgICAgfSxcbiAgICBdLFxuICB9LFxuXTtcblxuZXhwb3J0IGNvbnN0IGxpZ2h0aG91c2VDYXRlZ29yaWVzOiBDYXRlZ29yeUNvbmZpZ1tdID0gW1xuICB7XG4gICAgc2x1ZzogJ3BlcmZvcm1hbmNlJyxcbiAgICB0aXRsZTogJ1BlcmZvcm1hbmNlJyxcbiAgICByZWZzOiBbbGlnaHRob3VzZUdyb3VwUmVmKCdwZXJmb3JtYW5jZScpXSxcbiAgfSxcbiAge1xuICAgIHNsdWc6ICdhMTF5JyxcbiAgICB0aXRsZTogJ0FjY2Vzc2liaWxpdHknLFxuICAgIHJlZnM6IFtsaWdodGhvdXNlR3JvdXBSZWYoJ2FjY2Vzc2liaWxpdHknKV0sXG4gIH0sXG4gIHtcbiAgICBzbHVnOiAnYmVzdC1wcmFjdGljZXMnLFxuICAgIHRpdGxlOiAnQmVzdCBQcmFjdGljZXMnLFxuICAgIHJlZnM6IFtsaWdodGhvdXNlR3JvdXBSZWYoJ2Jlc3QtcHJhY3RpY2VzJyldLFxuICB9LFxuICB7XG4gICAgc2x1ZzogJ3NlbycsXG4gICAgdGl0bGU6ICdTRU8nLFxuICAgIHJlZnM6IFtsaWdodGhvdXNlR3JvdXBSZWYoJ3NlbycpXSxcbiAgfSxcbl07XG5cbmV4cG9ydCBjb25zdCBlc2xpbnRDYXRlZ29yaWVzOiBDYXRlZ29yeUNvbmZpZ1tdID0gW1xuICB7XG4gICAgc2x1ZzogJ2J1Zy1wcmV2ZW50aW9uJyxcbiAgICB0aXRsZTogJ0J1ZyBwcmV2ZW50aW9uJyxcbiAgICBkZXNjcmlwdGlvbjogJ0xpbnQgcnVsZXMgdGhhdCBmaW5kICoqcG90ZW50aWFsIGJ1Z3MqKiBpbiB5b3VyIGNvZGUuJyxcbiAgICByZWZzOiBbeyB0eXBlOiAnZ3JvdXAnLCBwbHVnaW46ICdlc2xpbnQnLCBzbHVnOiAncHJvYmxlbXMnLCB3ZWlnaHQ6IDEgfV0sXG4gIH0sXG4gIHtcbiAgICBzbHVnOiAnY29kZS1zdHlsZScsXG4gICAgdGl0bGU6ICdDb2RlIHN0eWxlJyxcbiAgICBkZXNjcmlwdGlvbjpcbiAgICAgICdMaW50IHJ1bGVzIHRoYXQgcHJvbW90ZSAqKmdvb2QgcHJhY3RpY2VzKiogYW5kIGNvbnNpc3RlbmN5IGluIHlvdXIgY29kZS4nLFxuICAgIHJlZnM6IFt7IHR5cGU6ICdncm91cCcsIHBsdWdpbjogJ2VzbGludCcsIHNsdWc6ICdzdWdnZXN0aW9ucycsIHdlaWdodDogMSB9XSxcbiAgfSxcbl07XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXREb2NDb3ZlcmFnZUNhdGVnb3JpZXMoY29uZmlnOiBEb2NDb3ZlcmFnZVBsdWdpbkNvbmZpZyk6IENhdGVnb3J5Q29uZmlnW10ge1xuICByZXR1cm4gW3tcbiAgICBzbHVnOiAnZG9jLWNvdmVyYWdlLWNhdCcsXG4gICAgdGl0bGU6ICdEb2N1bWVudGF0aW9uIGNvdmVyYWdlJyxcbiAgICBkZXNjcmlwdGlvbjogJ01lYXN1cmVzIGhvdyBtdWNoIG9mIHlvdXIgY29kZSBpcyAqKmRvY3VtZW50ZWQqKi4nLFxuICAgIHJlZnM6IGZpbHRlckdyb3Vwc0J5T25seUF1ZGl0cyhncm91cHMsIGNvbmZpZykubWFwKGdyb3VwID0+ICh7XG4gICAgICB3ZWlnaHQ6IDEsXG4gICAgICB0eXBlOiAnZ3JvdXAnLFxuICAgICAgcGx1Z2luOiBQTFVHSU5fU0xVRyxcbiAgICAgIHNsdWc6IGdyb3VwLnNsdWcsXG4gICAgfSkpLFxuICB9XTtcbn1cblxuXG5cbmV4cG9ydCBjb25zdCBjb3ZlcmFnZUNhdGVnb3JpZXM6IENhdGVnb3J5Q29uZmlnW10gPSBbXG4gIHtcbiAgICBzbHVnOiAnY29kZS1jb3ZlcmFnZScsXG4gICAgdGl0bGU6ICdDb2RlIGNvdmVyYWdlJyxcbiAgICBkZXNjcmlwdGlvbjogJ01lYXN1cmVzIGhvdyBtdWNoIG9mIHlvdXIgY29kZSBpcyAqKmNvdmVyZWQgYnkgdGVzdHMqKi4nLFxuICAgIHJlZnM6IFtcbiAgICAgIHtcbiAgICAgICAgdHlwZTogJ2dyb3VwJyxcbiAgICAgICAgcGx1Z2luOiAnY292ZXJhZ2UnLFxuICAgICAgICBzbHVnOiAnY292ZXJhZ2UnLFxuICAgICAgICB3ZWlnaHQ6IDEsXG4gICAgICB9LFxuICAgIF0sXG4gIH0sXG5dO1xuXG5leHBvcnQgY29uc3QganNQYWNrYWdlc0NvcmVDb25maWcgPSBhc3luYyAoKTogUHJvbWlzZTxDb3JlQ29uZmlnPiA9PiB7XG4gIHJldHVybiB7XG4gICAgcGx1Z2luczogW2F3YWl0IGpzUGFja2FnZXNQbHVnaW4oKV0sXG4gICAgY2F0ZWdvcmllczoganNQYWNrYWdlc0NhdGVnb3JpZXMsXG4gIH07XG59O1xuXG5leHBvcnQgY29uc3QgbGlnaHRob3VzZUNvcmVDb25maWcgPSBhc3luYyAoXG4gIHVybDogc3RyaW5nLFxuKTogUHJvbWlzZTxDb3JlQ29uZmlnPiA9PiB7XG4gIHJldHVybiB7XG4gICAgcGx1Z2luczogW2F3YWl0IGxpZ2h0aG91c2VQbHVnaW4odXJsKV0sXG4gICAgY2F0ZWdvcmllczogbGlnaHRob3VzZUNhdGVnb3JpZXMsXG4gIH07XG59O1xuXG5leHBvcnQgY29uc3QgZG9jQ292ZXJhZ2VDb3JlQ29uZmlnID0gYXN5bmMgKGNvbmZpZzogRG9jQ292ZXJhZ2VQbHVnaW5Db25maWcpOiBQcm9taXNlPENvcmVDb25maWc+ID0+IHtcbiAgcmV0dXJuIHtcbiAgICBwbHVnaW5zOiBbYXdhaXQgZG9jQ292ZXJhZ2VQbHVnaW4oY29uZmlnKV0sXG4gICAgY2F0ZWdvcmllczogZ2V0RG9jQ292ZXJhZ2VDYXRlZ29yaWVzKGNvbmZpZyksXG4gIH07XG59O1xuXG5leHBvcnQgY29uc3QgZXNsaW50Q29yZUNvbmZpZ054ID0gYXN5bmMgKFxuICBwcm9qZWN0TmFtZT86IHN0cmluZyxcbik6IFByb21pc2U8Q29yZUNvbmZpZz4gPT4ge1xuICByZXR1cm4ge1xuICAgIHBsdWdpbnM6IFtcbiAgICAgIGF3YWl0IGVzbGludFBsdWdpbihcbiAgICAgICAgYXdhaXQgKHByb2plY3ROYW1lXG4gICAgICAgICAgPyBlc2xpbnRDb25maWdGcm9tTnhQcm9qZWN0KHByb2plY3ROYW1lKVxuICAgICAgICAgIDogZXNsaW50Q29uZmlnRnJvbUFsbE54UHJvamVjdHMoKSksXG4gICAgICApLFxuICAgIF0sXG4gICAgY2F0ZWdvcmllczogZXNsaW50Q2F0ZWdvcmllcyxcbiAgfTtcbn07XG5cbmV4cG9ydCBjb25zdCBjb3ZlcmFnZUNvcmVDb25maWdOeCA9IGFzeW5jIChcbiAgcHJvamVjdE5hbWU/OiBzdHJpbmcsXG4pOiBQcm9taXNlPENvcmVDb25maWc+ID0+IHtcbiAgaWYgKHByb2plY3ROYW1lKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdjb3ZlcmFnZUNvcmVDb25maWdOeCBmb3Igc2luZ2xlIHByb2plY3RzIG5vdCBpbXBsZW1lbnRlZCcpO1xuICB9XG4gIGNvbnN0IHRhcmdldE5hbWVzID0gWyd1bml0LXRlc3QnLCAnaW50ZWdyYXRpb24tdGVzdCddO1xuICBjb25zdCB0YXJnZXRBcmdzID0gW1xuICAgICctdCcsXG4gICAgJ3VuaXQtdGVzdCcsXG4gICAgJ2ludGVncmF0aW9uLXRlc3QnLFxuICAgICctLWNvdmVyYWdlLmVuYWJsZWQnLFxuICAgICctLXNraXBOeENhY2hlJyxcbiAgXTtcbiAgcmV0dXJuIHtcbiAgICBwbHVnaW5zOiBbXG4gICAgICBhd2FpdCBjb3ZlcmFnZVBsdWdpbih7XG4gICAgICAgIGNvdmVyYWdlVG9vbENvbW1hbmQ6IHtcbiAgICAgICAgICBjb21tYW5kOiAnbnB4JyxcbiAgICAgICAgICBhcmdzOiBbXG4gICAgICAgICAgICAnbngnLFxuICAgICAgICAgICAgcHJvamVjdE5hbWUgPyBgcnVuIC0tcHJvamVjdCAke3Byb2plY3ROYW1lfWAgOiAncnVuLW1hbnknLFxuICAgICAgICAgICAgLi4udGFyZ2V0QXJncyxcbiAgICAgICAgICBdLFxuICAgICAgICB9LFxuICAgICAgICByZXBvcnRzOiBhd2FpdCBnZXROeENvdmVyYWdlUGF0aHModGFyZ2V0TmFtZXMpLFxuICAgICAgfSksXG4gICAgXSxcbiAgICBjYXRlZ29yaWVzOiBjb3ZlcmFnZUNhdGVnb3JpZXMsXG4gIH07XG59O1xuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2luZGV4LnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyY1wiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvaW5kZXgudHNcIjtleHBvcnQgeyBleGlzdHMgfSBmcm9tICdAY29kZS1wdXNodXAvbW9kZWxzJztcbmV4cG9ydCB7IGNvbXBhcmVQYWlycywgbWF0Y2hBcnJheUl0ZW1zQnlLZXksIHR5cGUgRGlmZiB9IGZyb20gJy4vbGliL2RpZmYuanMnO1xuZXhwb3J0IHsgc3RyaW5naWZ5RXJyb3IgfSBmcm9tICcuL2xpYi9lcnJvcnMuanMnO1xuZXhwb3J0IHtcbiAgUHJvY2Vzc0Vycm9yLFxuICBleGVjdXRlUHJvY2VzcyxcbiAgdHlwZSBQcm9jZXNzQ29uZmlnLFxuICB0eXBlIFByb2Nlc3NPYnNlcnZlcixcbiAgdHlwZSBQcm9jZXNzUmVzdWx0LFxufSBmcm9tICcuL2xpYi9leGVjdXRlLXByb2Nlc3MuanMnO1xuZXhwb3J0IHtcbiAgY3Jhd2xGaWxlU3lzdGVtLFxuICBkaXJlY3RvcnlFeGlzdHMsXG4gIGVuc3VyZURpcmVjdG9yeUV4aXN0cyxcbiAgZmlsZUV4aXN0cyxcbiAgZmlsZVBhdGhUb0NsaUFyZyxcbiAgZmluZExpbmVOdW1iZXJJblRleHQsXG4gIGZpbmROZWFyZXN0RmlsZSxcbiAgaW1wb3J0TW9kdWxlLFxuICBsb2dNdWx0aXBsZUZpbGVSZXN1bHRzLFxuICBwbHVnaW5Xb3JrRGlyLFxuICBwcm9qZWN0VG9GaWxlbmFtZSxcbiAgcmVhZEpzb25GaWxlLFxuICByZWFkVGV4dEZpbGUsXG4gIHJlbW92ZURpcmVjdG9yeUlmRXhpc3RzLFxuICB0eXBlIENyYXdsRmlsZVN5c3RlbU9wdGlvbnMsXG4gIHR5cGUgRmlsZVJlc3VsdCxcbiAgdHlwZSBNdWx0aXBsZUZpbGVSZXN1bHRzLFxufSBmcm9tICcuL2xpYi9maWxlLXN5c3RlbS5qcyc7XG5leHBvcnQgeyBmaWx0ZXJJdGVtUmVmc0J5IH0gZnJvbSAnLi9saWIvZmlsdGVyLmpzJztcbmV4cG9ydCB7XG4gIGZvcm1hdEJ5dGVzLFxuICBmb3JtYXREdXJhdGlvbixcbiAgcGx1cmFsaXplLFxuICBwbHVyYWxpemVUb2tlbixcbiAgc2x1Z2lmeSxcbiAgdHJ1bmNhdGVEZXNjcmlwdGlvbixcbiAgdHJ1bmNhdGVJc3N1ZU1lc3NhZ2UsXG4gIHRydW5jYXRlVGV4dCxcbiAgdHJ1bmNhdGVUaXRsZSxcbn0gZnJvbSAnLi9saWIvZm9ybWF0dGluZy5qcyc7XG5leHBvcnQge1xuICBnZXRDdXJyZW50QnJhbmNoT3JUYWcsXG4gIGdldEhhc2hGcm9tVGFnLFxuICBnZXRIYXNoZXMsXG4gIGdldExhdGVzdENvbW1pdCxcbiAgZ2V0U2VtdmVyVGFncyxcbiAgdHlwZSBMb2dSZXN1bHQsXG59IGZyb20gJy4vbGliL2dpdC9naXQuY29tbWl0cy1hbmQtdGFncy5qcyc7XG5leHBvcnQge1xuICBmb3JtYXRHaXRQYXRoLFxuICBnZXRHaXRSb290LFxuICBndWFyZEFnYWluc3RMb2NhbENoYW5nZXMsXG4gIHNhZmVDaGVja291dCxcbiAgdG9HaXRQYXRoLFxufSBmcm9tICcuL2xpYi9naXQvZ2l0LmpzJztcbmV4cG9ydCB7IGdyb3VwQnlTdGF0dXMgfSBmcm9tICcuL2xpYi9ncm91cC1ieS1zdGF0dXMuanMnO1xuZXhwb3J0IHtcbiAgaXNQcm9taXNlRnVsZmlsbGVkUmVzdWx0LFxuICBpc1Byb21pc2VSZWplY3RlZFJlc3VsdCxcbiAgaGFzTm9OdWxsYWJsZVByb3BzLFxufSBmcm9tICcuL2xpYi9ndWFyZHMuanMnO1xuZXhwb3J0IHsgbG9nTXVsdGlwbGVSZXN1bHRzIH0gZnJvbSAnLi9saWIvbG9nLXJlc3VsdHMuanMnO1xuZXhwb3J0IHsgbGluaywgdWksIHR5cGUgQ2xpVWksIHR5cGUgQ29sdW1uIH0gZnJvbSAnLi9saWIvbG9nZ2luZy5qcyc7XG5leHBvcnQgeyBtZXJnZUNvbmZpZ3MgfSBmcm9tICcuL2xpYi9tZXJnZS1jb25maWdzLmpzJztcbmV4cG9ydCB7IGdldFByb2dyZXNzQmFyLCB0eXBlIFByb2dyZXNzQmFyIH0gZnJvbSAnLi9saWIvcHJvZ3Jlc3MuanMnO1xuZXhwb3J0IHtcbiAgQ09ERV9QVVNIVVBfRE9NQUlOLFxuICBDT0RFX1BVU0hVUF9VTklDT0RFX0xPR08sXG4gIEZPT1RFUl9QUkVGSVgsXG4gIFJFQURNRV9MSU5LLFxuICBURVJNSU5BTF9XSURUSCxcbn0gZnJvbSAnLi9saWIvcmVwb3J0cy9jb25zdGFudHMuanMnO1xuZXhwb3J0IHtcbiAgbGlzdEF1ZGl0c0Zyb21BbGxQbHVnaW5zLFxuICBsaXN0R3JvdXBzRnJvbUFsbFBsdWdpbnMsXG59IGZyb20gJy4vbGliL3JlcG9ydHMvZmxhdHRlbi1wbHVnaW5zLmpzJztcbmV4cG9ydCB7IGdlbmVyYXRlTWRSZXBvcnQgfSBmcm9tICcuL2xpYi9yZXBvcnRzL2dlbmVyYXRlLW1kLXJlcG9ydC5qcyc7XG5leHBvcnQge1xuICBnZW5lcmF0ZU1kUmVwb3J0c0RpZmYsXG4gIGdlbmVyYXRlTWRSZXBvcnRzRGlmZkZvck1vbm9yZXBvLFxufSBmcm9tICcuL2xpYi9yZXBvcnRzL2dlbmVyYXRlLW1kLXJlcG9ydHMtZGlmZi5qcyc7XG5leHBvcnQgeyBsb2FkUmVwb3J0IH0gZnJvbSAnLi9saWIvcmVwb3J0cy9sb2FkLXJlcG9ydC5qcyc7XG5leHBvcnQgeyBsb2dTdGRvdXRTdW1tYXJ5IH0gZnJvbSAnLi9saWIvcmVwb3J0cy9sb2ctc3Rkb3V0LXN1bW1hcnkuanMnO1xuZXhwb3J0IHsgc2NvcmVSZXBvcnQgfSBmcm9tICcuL2xpYi9yZXBvcnRzL3Njb3JpbmcuanMnO1xuZXhwb3J0IHsgc29ydFJlcG9ydCB9IGZyb20gJy4vbGliL3JlcG9ydHMvc29ydGluZy5qcyc7XG5leHBvcnQgdHlwZSB7XG4gIFNjb3JlZENhdGVnb3J5Q29uZmlnLFxuICBTY29yZWRHcm91cCxcbiAgU2NvcmVkUmVwb3J0LFxufSBmcm9tICcuL2xpYi9yZXBvcnRzL3R5cGVzLmpzJztcbmV4cG9ydCB7XG4gIGNhbGNEdXJhdGlvbixcbiAgY29tcGFyZUlzc3VlU2V2ZXJpdHksXG4gIGZvcm1hdFJlcG9ydFNjb3JlLFxufSBmcm9tICcuL2xpYi9yZXBvcnRzL3V0aWxzLmpzJztcbmV4cG9ydCB7IGlzU2VtdmVyLCBub3JtYWxpemVTZW12ZXIsIHNvcnRTZW12ZXJzIH0gZnJvbSAnLi9saWIvc2VtdmVyLmpzJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL3RleHQtZm9ybWF0cy9pbmRleC5qcyc7XG5leHBvcnQge1xuICBjYXBpdGFsaXplLFxuICBjb3VudE9jY3VycmVuY2VzLFxuICBkaXN0aW5jdCxcbiAgZmFjdG9yT2YsXG4gIGZyb21Kc29uTGluZXMsXG4gIG9iamVjdEZyb21FbnRyaWVzLFxuICBvYmplY3RUb0NsaUFyZ3MsXG4gIG9iamVjdFRvRW50cmllcyxcbiAgb2JqZWN0VG9LZXlzLFxuICB0b0FycmF5LFxuICB0b0pzb25MaW5lcyxcbiAgdG9OdW1iZXJQcmVjaXNpb24sXG4gIHRvT3JkaW5hbCxcbiAgdG9Vbml4TmV3bGluZXMsXG4gIHRvVW5peFBhdGgsXG4gIHR5cGUgQ2xpQXJnc09iamVjdCxcbn0gZnJvbSAnLi9saWIvdHJhbnNmb3JtLmpzJztcbmV4cG9ydCB0eXBlIHtcbiAgRXhjbHVkZU51bGxhYmxlUHJvcHMsXG4gIEV4dHJhY3RBcnJheSxcbiAgRXh0cmFjdEFycmF5cyxcbiAgSXRlbU9yQXJyYXksXG4gIFByZXR0aWZ5LFxuICBXaXRoUmVxdWlyZWQsXG59IGZyb20gJy4vbGliL3R5cGVzLmpzJztcbmV4cG9ydCB7IHZlcmJvc2VVdGlscyB9IGZyb20gJy4vbGliL3ZlcmJvc2UtdXRpbHMuanMnO1xuZXhwb3J0IHsgem9kRXJyb3JNZXNzYWdlQnVpbGRlciB9IGZyb20gJy4vbGliL3pvZC12YWxpZGF0aW9uLmpzJztcbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvZXhlY3V0ZS1wcm9jZXNzLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9leGVjdXRlLXByb2Nlc3MudHNcIjtpbXBvcnQge1xuICB0eXBlIENoaWxkUHJvY2VzcyxcbiAgdHlwZSBDaGlsZFByb2Nlc3NCeVN0ZGlvLFxuICB0eXBlIFNwYXduT3B0aW9uc1dpdGhTdGRpb1R1cGxlLFxuICB0eXBlIFN0ZGlvUGlwZSxcbiAgc3Bhd24sXG59IGZyb20gJ25vZGU6Y2hpbGRfcHJvY2Vzcyc7XG5pbXBvcnQgdHlwZSB7IFJlYWRhYmxlLCBXcml0YWJsZSB9IGZyb20gJ25vZGU6c3RyZWFtJztcbmltcG9ydCB7IGNhbGNEdXJhdGlvbiB9IGZyb20gJy4vcmVwb3J0cy91dGlscy5qcyc7XG5cbi8qKlxuICogUmVwcmVzZW50cyB0aGUgcHJvY2VzcyByZXN1bHQuXG4gKiBAY2F0ZWdvcnkgVHlwZXNcbiAqIEBwdWJsaWNcbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBzdGRvdXQgLSBUaGUgc3Rkb3V0IG9mIHRoZSBwcm9jZXNzLlxuICogQHByb3BlcnR5IHtzdHJpbmd9IHN0ZGVyciAtIFRoZSBzdGRlcnIgb2YgdGhlIHByb2Nlc3MuXG4gKiBAcHJvcGVydHkge251bWJlciB8IG51bGx9IGNvZGUgLSBUaGUgZXhpdCBjb2RlIG9mIHRoZSBwcm9jZXNzLlxuICovXG5leHBvcnQgdHlwZSBQcm9jZXNzUmVzdWx0ID0ge1xuICBzdGRvdXQ6IHN0cmluZztcbiAgc3RkZXJyOiBzdHJpbmc7XG4gIGNvZGU6IG51bWJlciB8IG51bGw7XG4gIGRhdGU6IHN0cmluZztcbiAgZHVyYXRpb246IG51bWJlcjtcbn07XG5cbi8qKlxuICogRXJyb3IgY2xhc3MgZm9yIHByb2Nlc3MgZXJyb3JzLlxuICogQ29udGFpbnMgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgcHJvY2VzcyByZXN1bHQuXG4gKiBAY2F0ZWdvcnkgRXJyb3JcbiAqIEBwdWJsaWNcbiAqIEBjbGFzc1xuICogQGV4dGVuZHMgRXJyb3JcbiAqIEBleGFtcGxlXG4gKiBjb25zdCByZXN1bHQgPSBhd2FpdCBleGVjdXRlUHJvY2Vzcyh7fSlcbiAqIC5jYXRjaCgoZXJyb3IpID0+IHtcbiAqICAgaWYgKGVycm9yIGluc3RhbmNlb2YgUHJvY2Vzc0Vycm9yKSB7XG4gKiAgIGNvbnNvbGUuZXJyb3IoZXJyb3IuY29kZSk7XG4gKiAgIGNvbnNvbGUuZXJyb3IoZXJyb3Iuc3RkZXJyKTtcbiAqICAgY29uc29sZS5lcnJvcihlcnJvci5zdGRvdXQpO1xuICogICB9XG4gKiB9KTtcbiAqXG4gKi9cbmV4cG9ydCBjbGFzcyBQcm9jZXNzRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIGNvZGU6IG51bWJlciB8IG51bGw7XG4gIHN0ZGVycjogc3RyaW5nO1xuICBzdGRvdXQ6IHN0cmluZztcblxuICBjb25zdHJ1Y3RvcihyZXN1bHQ6IFByb2Nlc3NSZXN1bHQpIHtcbiAgICBzdXBlcihyZXN1bHQuc3RkZXJyKTtcbiAgICB0aGlzLmNvZGUgPSByZXN1bHQuY29kZTtcbiAgICB0aGlzLnN0ZGVyciA9IHJlc3VsdC5zdGRlcnI7XG4gICAgdGhpcy5zdGRvdXQgPSByZXN1bHQuc3Rkb3V0O1xuICB9XG59XG5cbi8qKlxuICogUHJvY2VzcyBjb25maWcgb2JqZWN0LiBDb250YWlucyB0aGUgY29tbWFuZCwgYXJncyBhbmQgb2JzZXJ2ZXIuXG4gKiBAcGFyYW0gY2ZnIC0gcHJvY2VzcyBjb25maWcgb2JqZWN0IHdpdGggY29tbWFuZCwgYXJncyBhbmQgb2JzZXJ2ZXIgKG9wdGlvbmFsKVxuICogQGNhdGVnb3J5IFR5cGVzXG4gKiBAcHVibGljXG4gKiBAcHJvcGVydHkge3N0cmluZ30gY29tbWFuZCAtIFRoZSBjb21tYW5kIHRvIGV4ZWN1dGUuXG4gKiBAcHJvcGVydHkge3N0cmluZ1tdfSBhcmdzIC0gVGhlIGFyZ3VtZW50cyBmb3IgdGhlIGNvbW1hbmQuXG4gKiBAcHJvcGVydHkge1Byb2Nlc3NPYnNlcnZlcn0gb2JzZXJ2ZXIgLSBUaGUgb2JzZXJ2ZXIgZm9yIHRoZSBwcm9jZXNzLlxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogLy8gYmFzaCBjb21tYW5kXG4gKiBjb25zdCBjZmcgPSB7XG4gKiAgIGNvbW1hbmQ6ICdiYXNoJyxcbiAqICAgYXJnczogWyctYycsICdlY2hvIFwiaGVsbG8gd29ybGRcIiddXG4gKiB9O1xuICpcbiAqIC8vIG5vZGUgY29tbWFuZFxuICogY29uc3QgY2ZnID0ge1xuICogY29tbWFuZDogJ25vZGUnLFxuICogYXJnczogWyctLXZlcnNpb24nXVxuICogfTtcbiAqXG4gKiAvLyBucHggY29tbWFuZFxuICogY29uc3QgY2ZnID0ge1xuICogY29tbWFuZDogJ25weCcsXG4gKiBhcmdzOiBbJy0tdmVyc2lvbiddXG4gKlxuICovXG5leHBvcnQgdHlwZSBQcm9jZXNzQ29uZmlnID0gT21pdDxcbiAgU3Bhd25PcHRpb25zV2l0aFN0ZGlvVHVwbGU8U3RkaW9QaXBlLCBTdGRpb1BpcGUsIFN0ZGlvUGlwZT4sXG4gICdzdGRpbydcbj4gJiB7XG4gIGNvbW1hbmQ6IHN0cmluZztcbiAgYXJncz86IHN0cmluZ1tdO1xuICBvYnNlcnZlcj86IFByb2Nlc3NPYnNlcnZlcjtcbiAgaWdub3JlRXhpdENvZGU/OiBib29sZWFuO1xufTtcblxuLyoqXG4gKiBQcm9jZXNzIG9ic2VydmVyIG9iamVjdC4gQ29udGFpbnMgdGhlIG9uU3Rkb3V0LCBlcnJvciBhbmQgY29tcGxldGUgZnVuY3Rpb24uXG4gKiBAY2F0ZWdvcnkgVHlwZXNcbiAqIEBwdWJsaWNcbiAqIEBwcm9wZXJ0eSB7ZnVuY3Rpb259IG9uU3Rkb3V0IC0gVGhlIG9uU3Rkb3V0IGZ1bmN0aW9uIG9mIHRoZSBvYnNlcnZlciAob3B0aW9uYWwpLlxuICogQHByb3BlcnR5IHtmdW5jdGlvbn0gb25FcnJvciAtIFRoZSBlcnJvciBmdW5jdGlvbiBvZiB0aGUgb2JzZXJ2ZXIgKG9wdGlvbmFsKS5cbiAqIEBwcm9wZXJ0eSB7ZnVuY3Rpb259IG9uQ29tcGxldGUgLSBUaGUgY29tcGxldGUgZnVuY3Rpb24gb2YgdGhlIG9ic2VydmVyIChvcHRpb25hbCkuXG4gKlxuICogQGV4YW1wbGVcbiAqIGNvbnN0IG9ic2VydmVyID0ge1xuICogIG9uU3Rkb3V0OiAoc3Rkb3V0KSA9PiBjb25zb2xlLmluZm8oc3Rkb3V0KVxuICogIH1cbiAqL1xuZXhwb3J0IHR5cGUgUHJvY2Vzc09ic2VydmVyID0ge1xuICBvblN0ZG91dD86IChzdGRvdXQ6IHN0cmluZywgc291cmNlUHJvY2Vzcz86IENoaWxkUHJvY2VzcykgPT4gdm9pZDtcbiAgb25TdGRlcnI/OiAoc3RkZXJyOiBzdHJpbmcsIHNvdXJjZVByb2Nlc3M/OiBDaGlsZFByb2Nlc3MpID0+IHZvaWQ7XG4gIG9uRXJyb3I/OiAoZXJyb3I6IFByb2Nlc3NFcnJvcikgPT4gdm9pZDtcbiAgb25Db21wbGV0ZT86ICgpID0+IHZvaWQ7XG59O1xuXG4vKipcbiAqIEV4ZWN1dGVzIGEgcHJvY2VzcyBhbmQgcmV0dXJucyBhIHByb21pc2Ugd2l0aCB0aGUgcmVzdWx0IGFzIGBQcm9jZXNzUmVzdWx0YC5cbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqIC8vIHN5bmMgcHJvY2VzcyBleGVjdXRpb25cbiAqIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGV4ZWN1dGVQcm9jZXNzKHtcbiAqICBjb21tYW5kOiAnbm9kZScsXG4gKiAgYXJnczogWyctLXZlcnNpb24nXVxuICogfSk7XG4gKlxuICogY29uc29sZS5pbmZvKHJlc3VsdCk7XG4gKlxuICogLy8gYXN5bmMgcHJvY2VzcyBleGVjdXRpb25cbiAqIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGV4ZWN1dGVQcm9jZXNzKHtcbiAqICAgIGNvbW1hbmQ6ICdub2RlJyxcbiAqICAgIGFyZ3M6IFsnZG93bmxvYWQtZGF0YS5qcyddLFxuICogICAgb2JzZXJ2ZXI6IHtcbiAqICAgICAgb25TdGRvdXQ6IHVwZGF0ZVByb2dyZXNzLFxuICogICAgICBlcnJvcjogaGFuZGxlRXJyb3IsXG4gKiAgICAgIGNvbXBsZXRlOiBjbGVhbkxvZ3MsXG4gKiAgICB9XG4gKiB9KTtcbiAqXG4gKiBjb25zb2xlLmluZm8ocmVzdWx0KTtcbiAqXG4gKiBAcGFyYW0gY2ZnIC0gc2VlIHtAbGluayBQcm9jZXNzQ29uZmlnfVxuICovXG5leHBvcnQgZnVuY3Rpb24gZXhlY3V0ZVByb2Nlc3MoY2ZnOiBQcm9jZXNzQ29uZmlnKTogUHJvbWlzZTxQcm9jZXNzUmVzdWx0PiB7XG4gIGNvbnN0IHsgY29tbWFuZCwgYXJncywgb2JzZXJ2ZXIsIGlnbm9yZUV4aXRDb2RlID0gZmFsc2UsIC4uLm9wdGlvbnMgfSA9IGNmZztcbiAgY29uc3QgeyBvblN0ZG91dCwgb25TdGRlcnIsIG9uRXJyb3IsIG9uQ29tcGxldGUgfSA9IG9ic2VydmVyID8/IHt9O1xuICBjb25zdCBkYXRlID0gbmV3IERhdGUoKS50b0lTT1N0cmluZygpO1xuICBjb25zdCBzdGFydCA9IHBlcmZvcm1hbmNlLm5vdygpO1xuXG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgLy8gc2hlbGw6dHJ1ZSB0ZWxscyBXaW5kb3dzIHRvIHVzZSBzaGVsbCBjb21tYW5kIGZvciBzcGF3bmluZyBhIGNoaWxkIHByb2Nlc3NcbiAgICBjb25zdCBzcGF3bmVkUHJvY2VzcyA9IHNwYXduKGNvbW1hbmQsIGFyZ3MgPz8gW10sIHtcbiAgICAgIHNoZWxsOiB0cnVlLFxuICAgICAgd2luZG93c0hpZGU6IHRydWUsXG4gICAgICAuLi5vcHRpb25zLFxuICAgIH0pIGFzIENoaWxkUHJvY2Vzc0J5U3RkaW88V3JpdGFibGUsIFJlYWRhYmxlLCBSZWFkYWJsZT47XG5cbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZnVuY3Rpb25hbC9uby1sZXRcbiAgICBsZXQgc3Rkb3V0ID0gJyc7XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGZ1bmN0aW9uYWwvbm8tbGV0XG4gICAgbGV0IHN0ZGVyciA9ICcnO1xuXG4gICAgc3Bhd25lZFByb2Nlc3Muc3Rkb3V0Lm9uKCdkYXRhJywgZGF0YSA9PiB7XG4gICAgICBzdGRvdXQgKz0gU3RyaW5nKGRhdGEpO1xuICAgICAgb25TdGRvdXQ/LihTdHJpbmcoZGF0YSksIHNwYXduZWRQcm9jZXNzKTtcbiAgICB9KTtcblxuICAgIHNwYXduZWRQcm9jZXNzLnN0ZGVyci5vbignZGF0YScsIGRhdGEgPT4ge1xuICAgICAgc3RkZXJyICs9IFN0cmluZyhkYXRhKTtcbiAgICAgIG9uU3RkZXJyPy4oU3RyaW5nKGRhdGEpLCBzcGF3bmVkUHJvY2Vzcyk7XG4gICAgfSk7XG5cbiAgICBzcGF3bmVkUHJvY2Vzcy5vbignZXJyb3InLCBlcnIgPT4ge1xuICAgICAgc3RkZXJyICs9IGVyci50b1N0cmluZygpO1xuICAgIH0pO1xuXG4gICAgc3Bhd25lZFByb2Nlc3Mub24oJ2Nsb3NlJywgY29kZSA9PiB7XG4gICAgICBjb25zdCB0aW1pbmdzID0geyBkYXRlLCBkdXJhdGlvbjogY2FsY0R1cmF0aW9uKHN0YXJ0KSB9O1xuICAgICAgaWYgKGNvZGUgPT09IDAgfHwgaWdub3JlRXhpdENvZGUpIHtcbiAgICAgICAgb25Db21wbGV0ZT8uKCk7XG4gICAgICAgIHJlc29sdmUoeyBjb2RlLCBzdGRvdXQsIHN0ZGVyciwgLi4udGltaW5ncyB9KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnN0IGVycm9yTXNnID0gbmV3IFByb2Nlc3NFcnJvcih7IGNvZGUsIHN0ZG91dCwgc3RkZXJyLCAuLi50aW1pbmdzIH0pO1xuICAgICAgICBvbkVycm9yPy4oZXJyb3JNc2cpO1xuICAgICAgICByZWplY3QoZXJyb3JNc2cpO1xuICAgICAgfVxuICAgIH0pO1xuICB9KTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0cy91dGlscy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3JlcG9ydHNcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9yZXBvcnRzL3V0aWxzLnRzXCI7aW1wb3J0IGFuc2lzLCB7IHR5cGUgQW5zaXMgfSBmcm9tICdhbnNpcyc7XG5pbXBvcnQgeyB0eXBlIElubGluZVRleHQsIG1kIH0gZnJvbSAnYnVpbGQtbWQnO1xuaW1wb3J0IHR5cGUge1xuICBBdWRpdERpZmYsXG4gIEF1ZGl0UmVwb3J0LFxuICBDYXRlZ29yeVJlZixcbiAgSXNzdWVTZXZlcml0eSBhcyBDbGlJc3N1ZVNldmVyaXR5LFxuICBHcm91cCxcbiAgSXNzdWUsXG59IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgU0NPUkVfQ09MT1JfUkFOR0UgfSBmcm9tICcuL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgdHlwZSB7XG4gIFNjb3JlZFJlcG9ydCxcbiAgU29ydGFibGVBdWRpdFJlcG9ydCxcbiAgU29ydGFibGVHcm91cCxcbn0gZnJvbSAnLi90eXBlcy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRSZXBvcnRTY29yZShzY29yZTogbnVtYmVyKTogc3RyaW5nIHtcbiAgY29uc3Qgc2NhbGVkU2NvcmUgPSBzY29yZSAqIDEwMDtcbiAgY29uc3Qgcm91bmRlZFNjb3JlID0gTWF0aC5yb3VuZChzY2FsZWRTY29yZSk7XG5cbiAgcmV0dXJuIHJvdW5kZWRTY29yZSA9PT0gMTAwICYmIHNjb3JlICE9PSAxXG4gICAgPyBNYXRoLmZsb29yKHNjYWxlZFNjb3JlKS50b1N0cmluZygpXG4gICAgOiByb3VuZGVkU2NvcmUudG9TdHJpbmcoKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdFNjb3JlV2l0aENvbG9yKFxuICBzY29yZTogbnVtYmVyLFxuICBvcHRpb25zPzogeyBza2lwQm9sZD86IGJvb2xlYW4gfSxcbik6IElubGluZVRleHQge1xuICBjb25zdCBzdHlsZWROdW1iZXIgPSBvcHRpb25zPy5za2lwQm9sZFxuICAgID8gZm9ybWF0UmVwb3J0U2NvcmUoc2NvcmUpXG4gICAgOiBtZC5ib2xkKGZvcm1hdFJlcG9ydFNjb3JlKHNjb3JlKSk7XG4gIHJldHVybiBtZGAke3Njb3JlTWFya2VyKHNjb3JlKX0gJHtzdHlsZWROdW1iZXJ9YDtcbn1cblxuZXhwb3J0IHR5cGUgTWFya2VyU2hhcGUgPSAnY2lyY2xlJyB8ICdzcXVhcmUnO1xuZXhwb3J0IHR5cGUgU2NvcmVDb2xvcnMgPSAncmVkJyB8ICd5ZWxsb3cnIHwgJ2dyZWVuJztcbmV4cG9ydCBjb25zdCBNQVJLRVJTOiBSZWNvcmQ8TWFya2VyU2hhcGUsIFJlY29yZDxTY29yZUNvbG9ycywgc3RyaW5nPj4gPSB7XG4gIGNpcmNsZToge1xuICAgIHJlZDogJ1x1RDgzRFx1REQzNCcsXG4gICAgeWVsbG93OiAnXHVEODNEXHVERkUxJyxcbiAgICBncmVlbjogJ1x1RDgzRFx1REZFMicsXG4gIH0sXG4gIHNxdWFyZToge1xuICAgIHJlZDogJ1x1RDgzRFx1REZFNScsXG4gICAgeWVsbG93OiAnXHVEODNEXHVERkU4JyxcbiAgICBncmVlbjogJ1x1RDgzRFx1REZFOScsXG4gIH0sXG59O1xuXG5leHBvcnQgZnVuY3Rpb24gc2NvcmVNYXJrZXIoXG4gIHNjb3JlOiBudW1iZXIsXG4gIG1hcmtlclR5cGU6IE1hcmtlclNoYXBlID0gJ2NpcmNsZScsXG4pOiBzdHJpbmcge1xuICBpZiAoc2NvcmUgPj0gU0NPUkVfQ09MT1JfUkFOR0UuR1JFRU5fTUlOKSB7XG4gICAgcmV0dXJuIE1BUktFUlNbbWFya2VyVHlwZV0uZ3JlZW47XG4gIH1cbiAgaWYgKHNjb3JlID49IFNDT1JFX0NPTE9SX1JBTkdFLllFTExPV19NSU4pIHtcbiAgICByZXR1cm4gTUFSS0VSU1ttYXJrZXJUeXBlXS55ZWxsb3c7XG4gIH1cbiAgcmV0dXJuIE1BUktFUlNbbWFya2VyVHlwZV0ucmVkO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RGlmZk1hcmtlcihkaWZmOiBudW1iZXIpOiBzdHJpbmcge1xuICBpZiAoZGlmZiA+IDApIHtcbiAgICByZXR1cm4gJ1x1MjE5MSc7XG4gIH1cbiAgaWYgKGRpZmYgPCAwKSB7XG4gICAgcmV0dXJuICdcdTIxOTMnO1xuICB9XG4gIHJldHVybiAnJztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbG9yQnlTY29yZURpZmYodGV4dDogc3RyaW5nLCBkaWZmOiBudW1iZXIpOiBJbmxpbmVUZXh0IHtcbiAgY29uc3QgY29sb3IgPSBkaWZmID4gMCA/ICdncmVlbicgOiBkaWZmIDwgMCA/ICdyZWQnIDogJ2dyYXknO1xuICByZXR1cm4gc2hpZWxkc0JhZGdlKHRleHQsIGNvbG9yKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNoaWVsZHNCYWRnZSh0ZXh0OiBzdHJpbmcsIGNvbG9yOiBzdHJpbmcpOiBJbmxpbmVUZXh0IHtcbiAgcmV0dXJuIG1kLmltYWdlKFxuICAgIGBodHRwczovL2ltZy5zaGllbGRzLmlvL2JhZGdlLyR7ZW5jb2RlVVJJQ29tcG9uZW50KHRleHQpfS0ke2NvbG9yfWAsXG4gICAgdGV4dCxcbiAgKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdERpZmZOdW1iZXIoZGlmZjogbnVtYmVyKTogc3RyaW5nIHtcbiAgY29uc3QgbnVtYmVyID1cbiAgICBNYXRoLmFicyhkaWZmKSA9PT0gTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZID8gJ1x1MjIxRScgOiBgJHtNYXRoLmFicyhkaWZmKX1gO1xuICBjb25zdCBzaWduID0gZGlmZiA8IDAgPyAnXHUyMjEyJyA6ICcrJztcbiAgcmV0dXJuIGAke3NpZ259JHtudW1iZXJ9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNldmVyaXR5TWFya2VyKHNldmVyaXR5OiAnaW5mbycgfCAnd2FybmluZycgfCAnZXJyb3InKTogc3RyaW5nIHtcbiAgaWYgKHNldmVyaXR5ID09PSAnZXJyb3InKSB7XG4gICAgcmV0dXJuICdcdUQ4M0RcdURFQTgnO1xuICB9XG4gIGlmIChzZXZlcml0eSA9PT0gJ3dhcm5pbmcnKSB7XG4gICAgcmV0dXJuICdcdTI2QTBcdUZFMEYnO1xuICB9XG4gIHJldHVybiAnXHUyMTM5XHVGRTBGJztcbn1cblxuY29uc3QgTUlOX05PTl9aRVJPX1JFU1VMVCA9IDAuMTtcblxuZXhwb3J0IGZ1bmN0aW9uIHJvdW5kVmFsdWUodmFsdWU6IG51bWJlcik6IG51bWJlciB7XG4gIGNvbnN0IHJvdW5kZWRWYWx1ZSA9IE1hdGgucm91bmQodmFsdWUgKiAxMCkgLyAxMDsgLy8gcm91bmQgd2l0aCBtYXggMSBkZWNpbWFsXG4gIGlmIChyb3VuZGVkVmFsdWUgPT09IDAgJiYgdmFsdWUgIT09IDApIHtcbiAgICByZXR1cm4gTUlOX05PTl9aRVJPX1JFU1VMVCAqIE1hdGguc2lnbih2YWx1ZSk7XG4gIH1cbiAgcmV0dXJuIHJvdW5kZWRWYWx1ZTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdFNjb3JlQ2hhbmdlKGRpZmY6IG51bWJlcik6IElubGluZVRleHQge1xuICBjb25zdCBtYXJrZXIgPSBnZXREaWZmTWFya2VyKGRpZmYpO1xuICBjb25zdCB0ZXh0ID0gZm9ybWF0RGlmZk51bWJlcihyb3VuZFZhbHVlKGRpZmYgKiAxMDApKTtcbiAgcmV0dXJuIGNvbG9yQnlTY29yZURpZmYoYCR7bWFya2VyfSAke3RleHR9YCwgZGlmZik7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRWYWx1ZUNoYW5nZSh7XG4gIHZhbHVlcyxcbiAgc2NvcmVzLFxufTogUGljazxBdWRpdERpZmYsICd2YWx1ZXMnIHwgJ3Njb3Jlcyc+KTogSW5saW5lVGV4dCB7XG4gIGNvbnN0IG1hcmtlciA9IGdldERpZmZNYXJrZXIodmFsdWVzLmRpZmYpO1xuICBjb25zdCBwZXJjZW50YWdlID1cbiAgICB2YWx1ZXMuYmVmb3JlID09PSAwXG4gICAgICA/IHZhbHVlcy5kaWZmID4gMFxuICAgICAgICA/IE51bWJlci5QT1NJVElWRV9JTkZJTklUWVxuICAgICAgICA6IE51bWJlci5ORUdBVElWRV9JTkZJTklUWVxuICAgICAgOiByb3VuZFZhbHVlKCh2YWx1ZXMuZGlmZiAvIHZhbHVlcy5iZWZvcmUpICogMTAwKTtcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWlycmVndWxhci13aGl0ZXNwYWNlXG4gIGNvbnN0IHRleHQgPSBgJHtmb3JtYXREaWZmTnVtYmVyKHBlcmNlbnRhZ2UpfVx1MjAwOSVgO1xuICByZXR1cm4gY29sb3JCeVNjb3JlRGlmZihgJHttYXJrZXJ9ICR7dGV4dH1gLCBzY29yZXMuZGlmZik7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjYWxjRHVyYXRpb24oc3RhcnQ6IG51bWJlciwgc3RvcD86IG51bWJlcik6IG51bWJlciB7XG4gIHJldHVybiBNYXRoLnJvdW5kKChzdG9wID8/IHBlcmZvcm1hbmNlLm5vdygpKSAtIHN0YXJ0KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvdW50V2VpZ2h0ZWRSZWZzKHJlZnM6IENhdGVnb3J5UmVmW10pIHtcbiAgcmV0dXJuIHJlZnNcbiAgICAuZmlsdGVyKCh7IHdlaWdodCB9KSA9PiB3ZWlnaHQgPiAwKVxuICAgIC5yZWR1Y2UoKHN1bSwgeyB3ZWlnaHQgfSkgPT4gc3VtICsgd2VpZ2h0LCAwKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvdW50Q2F0ZWdvcnlBdWRpdHMoXG4gIHJlZnM6IENhdGVnb3J5UmVmW10sXG4gIHBsdWdpbnM6IFNjb3JlZFJlcG9ydFsncGx1Z2lucyddLFxuKTogbnVtYmVyIHtcbiAgLy8gQ3JlYXRlIGxvb2t1cCBvYmplY3QgZm9yIGdyb3VwcyB3aXRoaW4gZWFjaCBwbHVnaW5cbiAgY29uc3QgZ3JvdXBMb29rdXAgPSBwbHVnaW5zLnJlZHVjZTxSZWNvcmQ8c3RyaW5nLCBSZWNvcmQ8c3RyaW5nLCBHcm91cD4+PihcbiAgICAobG9va3VwLCBwbHVnaW4pID0+IHtcbiAgICAgIGlmIChwbHVnaW4uZ3JvdXBzID09IG51bGwgfHwgcGx1Z2luLmdyb3Vwcy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgcmV0dXJuIGxvb2t1cDtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgLi4ubG9va3VwLFxuICAgICAgICBbcGx1Z2luLnNsdWddOiBPYmplY3QuZnJvbUVudHJpZXMoXG4gICAgICAgICAgcGx1Z2luLmdyb3Vwcy5tYXAoZ3JvdXAgPT4gW2dyb3VwLnNsdWcsIGdyb3VwXSksXG4gICAgICAgICksXG4gICAgICB9O1xuICAgIH0sXG4gICAge30sXG4gICk7XG5cbiAgLy8gQ291bnQgYXVkaXRzXG4gIHJldHVybiByZWZzLnJlZHVjZSgoYWNjLCByZWYpID0+IHtcbiAgICBpZiAocmVmLnR5cGUgPT09ICdncm91cCcpIHtcbiAgICAgIGNvbnN0IGdyb3VwUmVmcyA9IGdyb3VwTG9va3VwW3JlZi5wbHVnaW5dPy5bcmVmLnNsdWddPy5yZWZzO1xuICAgICAgcmV0dXJuIGFjYyArIChncm91cFJlZnM/Lmxlbmd0aCA/PyAwKTtcbiAgICB9XG4gICAgcmV0dXJuIGFjYyArIDE7XG4gIH0sIDApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29tcGFyZUNhdGVnb3J5QXVkaXRzQW5kR3JvdXBzKFxuICBhOiBTb3J0YWJsZUF1ZGl0UmVwb3J0IHwgU29ydGFibGVHcm91cCxcbiAgYjogU29ydGFibGVBdWRpdFJlcG9ydCB8IFNvcnRhYmxlR3JvdXAsXG4pOiBudW1iZXIge1xuICBpZiAoYS5zY29yZSAhPT0gYi5zY29yZSkge1xuICAgIHJldHVybiBhLnNjb3JlIC0gYi5zY29yZTtcbiAgfVxuXG4gIGlmIChhLndlaWdodCAhPT0gYi53ZWlnaHQpIHtcbiAgICByZXR1cm4gYi53ZWlnaHQgLSBhLndlaWdodDtcbiAgfVxuXG4gIGlmICgndmFsdWUnIGluIGEgJiYgJ3ZhbHVlJyBpbiBiICYmIGEudmFsdWUgIT09IGIudmFsdWUpIHtcbiAgICByZXR1cm4gYi52YWx1ZSAtIGEudmFsdWU7XG4gIH1cblxuICByZXR1cm4gYS50aXRsZS5sb2NhbGVDb21wYXJlKGIudGl0bGUpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29tcGFyZUF1ZGl0cyhhOiBBdWRpdFJlcG9ydCwgYjogQXVkaXRSZXBvcnQpOiBudW1iZXIge1xuICBpZiAoYS5zY29yZSAhPT0gYi5zY29yZSkge1xuICAgIHJldHVybiBhLnNjb3JlIC0gYi5zY29yZTtcbiAgfVxuXG4gIGlmIChhLnZhbHVlICE9PSBiLnZhbHVlKSB7XG4gICAgcmV0dXJuIGIudmFsdWUgLSBhLnZhbHVlO1xuICB9XG5cbiAgcmV0dXJuIGEudGl0bGUubG9jYWxlQ29tcGFyZShiLnRpdGxlKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbXBhcmVJc3N1ZVNldmVyaXR5KFxuICBzZXZlcml0eTE6IENsaUlzc3VlU2V2ZXJpdHksXG4gIHNldmVyaXR5MjogQ2xpSXNzdWVTZXZlcml0eSxcbik6IG51bWJlciB7XG4gIGNvbnN0IGxldmVsczogUmVjb3JkPENsaUlzc3VlU2V2ZXJpdHksIG51bWJlcj4gPSB7XG4gICAgaW5mbzogMCxcbiAgICB3YXJuaW5nOiAxLFxuICAgIGVycm9yOiAyLFxuICB9O1xuICByZXR1cm4gbGV2ZWxzW3NldmVyaXR5MV0gLSBsZXZlbHNbc2V2ZXJpdHkyXTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHRocm93SXNOb3RQcmVzZW50RXJyb3IoXG4gIGl0ZW1OYW1lOiBzdHJpbmcsXG4gIHByZXNlbnRQbGFjZTogc3RyaW5nLFxuKTogbmV2ZXIge1xuICB0aHJvdyBuZXcgRXJyb3IoYCR7aXRlbU5hbWV9IGlzIG5vdCBwcmVzZW50IGluICR7cHJlc2VudFBsYWNlfWApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0UGx1Z2luTmFtZUZyb21TbHVnKFxuICBzbHVnOiBzdHJpbmcsXG4gIHBsdWdpbnM6IFNjb3JlZFJlcG9ydFsncGx1Z2lucyddLFxuKTogc3RyaW5nIHtcbiAgcmV0dXJuIChcbiAgICBwbHVnaW5zLmZpbmQoKHsgc2x1ZzogcGx1Z2luU2x1ZyB9KSA9PiBwbHVnaW5TbHVnID09PSBzbHVnKT8udGl0bGUgfHwgc2x1Z1xuICApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29tcGFyZUlzc3VlcyhhOiBJc3N1ZSwgYjogSXNzdWUpOiBudW1iZXIge1xuICBpZiAoYS5zZXZlcml0eSAhPT0gYi5zZXZlcml0eSkge1xuICAgIHJldHVybiAtY29tcGFyZUlzc3VlU2V2ZXJpdHkoYS5zZXZlcml0eSwgYi5zZXZlcml0eSk7XG4gIH1cbiAgaWYgKCFhLnNvdXJjZSAmJiBiLnNvdXJjZSkge1xuICAgIHJldHVybiAtMTtcbiAgfVxuICBpZiAoYS5zb3VyY2UgJiYgIWIuc291cmNlKSB7XG4gICAgcmV0dXJuIDE7XG4gIH1cbiAgaWYgKGEuc291cmNlPy5maWxlICE9PSBiLnNvdXJjZT8uZmlsZSkge1xuICAgIHJldHVybiBhLnNvdXJjZT8uZmlsZS5sb2NhbGVDb21wYXJlKGIuc291cmNlPy5maWxlIHx8ICcnKSA/PyAwO1xuICB9XG4gIHJldHVybiBjb21wYXJlU291cmNlRmlsZVBvc2l0aW9uKGEuc291cmNlPy5wb3NpdGlvbiwgYi5zb3VyY2U/LnBvc2l0aW9uKTtcbn1cblxuZnVuY3Rpb24gY29tcGFyZVNvdXJjZUZpbGVQb3NpdGlvbihcbiAgYTogTm9uTnVsbGFibGU8SXNzdWVbJ3NvdXJjZSddPlsncG9zaXRpb24nXSxcbiAgYjogTm9uTnVsbGFibGU8SXNzdWVbJ3NvdXJjZSddPlsncG9zaXRpb24nXSxcbik6IG51bWJlciB7XG4gIGlmICghYSAmJiBiKSB7XG4gICAgcmV0dXJuIC0xO1xuICB9XG4gIGlmIChhICYmICFiKSB7XG4gICAgcmV0dXJuIDE7XG4gIH1cbiAgaWYgKGE/LnN0YXJ0TGluZSAhPT0gYj8uc3RhcnRMaW5lKSB7XG4gICAgcmV0dXJuIChhPy5zdGFydExpbmUgPz8gMCkgLSAoYj8uc3RhcnRMaW5lID8/IDApO1xuICB9XG4gIHJldHVybiAwO1xufVxuXG4vLyBAVE9ETyByZXRoaW5rIGltcGxlbWVudGF0aW9uXG5leHBvcnQgZnVuY3Rpb24gYXBwbHlTY29yZUNvbG9yKFxuICB7IHNjb3JlLCB0ZXh0IH06IHsgc2NvcmU6IG51bWJlcjsgdGV4dD86IHN0cmluZyB9LFxuICBzdHlsZTogQW5zaXMgPSBhbnNpcyxcbikge1xuICBjb25zdCBmb3JtYXR0ZWRTY29yZSA9IHRleHQgPz8gZm9ybWF0UmVwb3J0U2NvcmUoc2NvcmUpO1xuXG4gIGlmIChzY29yZSA+PSBTQ09SRV9DT0xPUl9SQU5HRS5HUkVFTl9NSU4pIHtcbiAgICByZXR1cm4gdGV4dFxuICAgICAgPyBzdHlsZS5ncmVlbihmb3JtYXR0ZWRTY29yZSlcbiAgICAgIDogc3R5bGUuYm9sZChzdHlsZS5ncmVlbihmb3JtYXR0ZWRTY29yZSkpO1xuICB9XG5cbiAgaWYgKHNjb3JlID49IFNDT1JFX0NPTE9SX1JBTkdFLllFTExPV19NSU4pIHtcbiAgICByZXR1cm4gdGV4dFxuICAgICAgPyBzdHlsZS55ZWxsb3coZm9ybWF0dGVkU2NvcmUpXG4gICAgICA6IHN0eWxlLmJvbGQoc3R5bGUueWVsbG93KGZvcm1hdHRlZFNjb3JlKSk7XG4gIH1cblxuICByZXR1cm4gdGV4dFxuICAgID8gc3R5bGUucmVkKGZvcm1hdHRlZFNjb3JlKVxuICAgIDogc3R5bGUuYm9sZChzdHlsZS5yZWQoZm9ybWF0dGVkU2NvcmUpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHRhcmdldFNjb3JlSWNvbihcbiAgc2NvcmU6IG51bWJlcixcbiAgdGFyZ2V0U2NvcmU/OiBudW1iZXIsXG4gIG9wdGlvbnM6IHtcbiAgICBwYXNzSWNvbj86IHN0cmluZztcbiAgICBmYWlsSWNvbj86IHN0cmluZztcbiAgICBwcmVmaXg/OiBzdHJpbmc7XG4gICAgcG9zdGZpeD86IHN0cmluZztcbiAgfSA9IHt9LFxuKTogc3RyaW5nIHtcbiAgaWYgKHRhcmdldFNjb3JlICE9IG51bGwpIHtcbiAgICBjb25zdCB7XG4gICAgICBwYXNzSWNvbiA9ICdcdTI3MDUnLFxuICAgICAgZmFpbEljb24gPSAnXHUyNzRDJyxcbiAgICAgIHByZWZpeCA9ICcnLFxuICAgICAgcG9zdGZpeCA9ICcnLFxuICAgIH0gPSBvcHRpb25zO1xuICAgIGlmIChzY29yZSA+PSB0YXJnZXRTY29yZSkge1xuICAgICAgcmV0dXJuIGAke3ByZWZpeH0ke3Bhc3NJY29ufSR7cG9zdGZpeH1gO1xuICAgIH1cbiAgICByZXR1cm4gYCR7cHJlZml4fSR7ZmFpbEljb259JHtwb3N0Zml4fWA7XG4gIH1cbiAgcmV0dXJuICcnO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9maWxlLXN5c3RlbS50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvZmlsZS1zeXN0ZW0udHNcIjtpbXBvcnQgeyBib2xkLCBncmF5IH0gZnJvbSAnYW5zaXMnO1xuaW1wb3J0IHsgdHlwZSBPcHRpb25zLCBidW5kbGVSZXF1aXJlIH0gZnJvbSAnYnVuZGxlLXJlcXVpcmUnO1xuaW1wb3J0IHsgbWtkaXIsIHJlYWRGaWxlLCByZWFkZGlyLCBybSwgc3RhdCB9IGZyb20gJ25vZGU6ZnMvcHJvbWlzZXMnO1xuaW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB7IGZvcm1hdEJ5dGVzIH0gZnJvbSAnLi9mb3JtYXR0aW5nLmpzJztcbmltcG9ydCB7IGxvZ011bHRpcGxlUmVzdWx0cyB9IGZyb20gJy4vbG9nLXJlc3VsdHMuanMnO1xuaW1wb3J0IHsgdWkgfSBmcm9tICcuL2xvZ2dpbmcuanMnO1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVhZFRleHRGaWxlKGZpbGVQYXRoOiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz4ge1xuICBjb25zdCBidWZmZXIgPSBhd2FpdCByZWFkRmlsZShmaWxlUGF0aCk7XG4gIHJldHVybiBidWZmZXIudG9TdHJpbmcoKTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlYWRKc29uRmlsZTxUID0gdW5rbm93bj4oZmlsZVBhdGg6IHN0cmluZyk6IFByb21pc2U8VD4ge1xuICBjb25zdCB0ZXh0ID0gYXdhaXQgcmVhZFRleHRGaWxlKGZpbGVQYXRoKTtcbiAgcmV0dXJuIEpTT04ucGFyc2UodGV4dCkgYXMgVDtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGZpbGVFeGlzdHMoZmlsZVBhdGg6IHN0cmluZyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICB0cnkge1xuICAgIGNvbnN0IHN0YXRzID0gYXdhaXQgc3RhdChmaWxlUGF0aCk7XG4gICAgcmV0dXJuIHN0YXRzLmlzRmlsZSgpO1xuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGRpcmVjdG9yeUV4aXN0cyhmaWxlUGF0aDogc3RyaW5nKTogUHJvbWlzZTxib29sZWFuPiB7XG4gIHRyeSB7XG4gICAgY29uc3Qgc3RhdHMgPSBhd2FpdCBzdGF0KGZpbGVQYXRoKTtcbiAgICByZXR1cm4gc3RhdHMuaXNEaXJlY3RvcnkoKTtcbiAgfSBjYXRjaCB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBlbnN1cmVEaXJlY3RvcnlFeGlzdHMoYmFzZURpcjogc3RyaW5nKSB7XG4gIHRyeSB7XG4gICAgYXdhaXQgbWtkaXIoYmFzZURpciwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgcmV0dXJuO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIHVpKCkubG9nZ2VyLmluZm8oKGVycm9yIGFzIHsgY29kZTogc3RyaW5nOyBtZXNzYWdlOiBzdHJpbmcgfSkubWVzc2FnZSk7XG4gICAgaWYgKChlcnJvciBhcyB7IGNvZGU6IHN0cmluZyB9KS5jb2RlICE9PSAnRUVYSVNUJykge1xuICAgICAgdGhyb3cgZXJyb3I7XG4gICAgfVxuICB9XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZW1vdmVEaXJlY3RvcnlJZkV4aXN0cyhkaXI6IHN0cmluZykge1xuICBpZiAoYXdhaXQgZGlyZWN0b3J5RXhpc3RzKGRpcikpIHtcbiAgICBhd2FpdCBybShkaXIsIHsgcmVjdXJzaXZlOiB0cnVlLCBmb3JjZTogdHJ1ZSB9KTtcbiAgfVxufVxuXG5leHBvcnQgdHlwZSBGaWxlUmVzdWx0ID0gcmVhZG9ubHkgW3N0cmluZ10gfCByZWFkb25seSBbc3RyaW5nLCBudW1iZXJdO1xuZXhwb3J0IHR5cGUgTXVsdGlwbGVGaWxlUmVzdWx0cyA9IFByb21pc2VTZXR0bGVkUmVzdWx0PEZpbGVSZXN1bHQ+W107XG5cbmV4cG9ydCBmdW5jdGlvbiBsb2dNdWx0aXBsZUZpbGVSZXN1bHRzKFxuICBmaWxlUmVzdWx0czogTXVsdGlwbGVGaWxlUmVzdWx0cyxcbiAgbWVzc2FnZVByZWZpeDogc3RyaW5nLFxuKTogdm9pZCB7XG4gIGNvbnN0IHN1Y2NlZWRlZFRyYW5zZm9ybSA9IChyZXN1bHQ6IFByb21pc2VGdWxmaWxsZWRSZXN1bHQ8RmlsZVJlc3VsdD4pID0+IHtcbiAgICBjb25zdCBbZmlsZU5hbWUsIHNpemVdID0gcmVzdWx0LnZhbHVlO1xuICAgIGNvbnN0IGZvcm1hdHRlZFNpemUgPSBzaXplID8gYCAoJHtncmF5KGZvcm1hdEJ5dGVzKHNpemUpKX0pYCA6ICcnO1xuICAgIHJldHVybiBgLSAke2JvbGQoZmlsZU5hbWUpfSR7Zm9ybWF0dGVkU2l6ZX1gO1xuICB9O1xuICBjb25zdCBmYWlsZWRUcmFuc2Zvcm0gPSAocmVzdWx0OiBQcm9taXNlUmVqZWN0ZWRSZXN1bHQpID0+XG4gICAgYC0gJHtib2xkKHJlc3VsdC5yZWFzb24gYXMgc3RyaW5nKX1gO1xuXG4gIGxvZ011bHRpcGxlUmVzdWx0czxGaWxlUmVzdWx0PihcbiAgICBmaWxlUmVzdWx0cyxcbiAgICBtZXNzYWdlUHJlZml4LFxuICAgIHN1Y2NlZWRlZFRyYW5zZm9ybSxcbiAgICBmYWlsZWRUcmFuc2Zvcm0sXG4gICk7XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBpbXBvcnRNb2R1bGU8VCA9IHVua25vd24+KG9wdGlvbnM6IE9wdGlvbnMpOiBQcm9taXNlPFQ+IHtcbiAgY29uc3QgeyBtb2QgfSA9IGF3YWl0IGJ1bmRsZVJlcXVpcmU8b2JqZWN0PihvcHRpb25zKTtcblxuICBpZiAodHlwZW9mIG1vZCA9PT0gJ29iamVjdCcgJiYgJ2RlZmF1bHQnIGluIG1vZCkge1xuICAgIHJldHVybiBtb2QuZGVmYXVsdCBhcyBUO1xuICB9XG4gIHJldHVybiBtb2QgYXMgVDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBsdWdpbldvcmtEaXIoc2x1Zzogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIHBhdGguam9pbignbm9kZV9tb2R1bGVzJywgJy5jb2RlLXB1c2h1cCcsIHNsdWcpO1xufVxuXG5leHBvcnQgdHlwZSBDcmF3bEZpbGVTeXN0ZW1PcHRpb25zPFQ+ID0ge1xuICBkaXJlY3Rvcnk6IHN0cmluZztcbiAgcGF0dGVybj86IHN0cmluZyB8IFJlZ0V4cDtcbiAgZmlsZVRyYW5zZm9ybT86IChmaWxlUGF0aDogc3RyaW5nKSA9PiBQcm9taXNlPFQ+IHwgVDtcbn07XG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3Jhd2xGaWxlU3lzdGVtPFQgPSBzdHJpbmc+KFxuICBvcHRpb25zOiBDcmF3bEZpbGVTeXN0ZW1PcHRpb25zPFQ+LFxuKTogUHJvbWlzZTxUW10+IHtcbiAgY29uc3Qge1xuICAgIGRpcmVjdG9yeSxcbiAgICBwYXR0ZXJuLFxuICAgIGZpbGVUcmFuc2Zvcm0gPSAoZmlsZVBhdGg6IHN0cmluZykgPT4gZmlsZVBhdGggYXMgVCxcbiAgfSA9IG9wdGlvbnM7XG5cbiAgY29uc3QgZmlsZXMgPSBhd2FpdCByZWFkZGlyKGRpcmVjdG9yeSk7XG4gIGNvbnN0IHByb21pc2VzID0gZmlsZXMubWFwKGFzeW5jIChmaWxlKTogUHJvbWlzZTxUIHwgVFtdPiA9PiB7XG4gICAgY29uc3QgZmlsZVBhdGggPSBwYXRoLmpvaW4oZGlyZWN0b3J5LCBmaWxlKTtcbiAgICBjb25zdCBzdGF0cyA9IGF3YWl0IHN0YXQoZmlsZVBhdGgpO1xuXG4gICAgaWYgKHN0YXRzLmlzRGlyZWN0b3J5KCkpIHtcbiAgICAgIHJldHVybiBjcmF3bEZpbGVTeXN0ZW0oeyBkaXJlY3Rvcnk6IGZpbGVQYXRoLCBwYXR0ZXJuLCBmaWxlVHJhbnNmb3JtIH0pO1xuICAgIH1cbiAgICBpZiAoc3RhdHMuaXNGaWxlKCkgJiYgKCFwYXR0ZXJuIHx8IG5ldyBSZWdFeHAocGF0dGVybikudGVzdChmaWxlKSkpIHtcbiAgICAgIHJldHVybiBmaWxlVHJhbnNmb3JtKGZpbGVQYXRoKTtcbiAgICB9XG4gICAgcmV0dXJuIFtdO1xuICB9KTtcblxuICBjb25zdCByZXN1bHRzTmVzdGVkQXJyYXkgPSBhd2FpdCBQcm9taXNlLmFsbChwcm9taXNlcyk7XG4gIHJldHVybiByZXN1bHRzTmVzdGVkQXJyYXkuZmxhdCgpIGFzIFRbXTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGZpbmROZWFyZXN0RmlsZShcbiAgZmlsZU5hbWVzOiBzdHJpbmdbXSxcbiAgY3dkID0gcHJvY2Vzcy5jd2QoKSxcbik6IFByb21pc2U8c3RyaW5nIHwgdW5kZWZpbmVkPiB7XG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBmdW5jdGlvbmFsL25vLWxvb3Atc3RhdGVtZW50c1xuICBmb3IgKFxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBmdW5jdGlvbmFsL25vLWxldFxuICAgIGxldCBkaXJlY3RvcnkgPSBjd2Q7XG4gICAgZGlyZWN0b3J5ICE9PSBwYXRoLmRpcm5hbWUoZGlyZWN0b3J5KTtcbiAgICBkaXJlY3RvcnkgPSBwYXRoLmRpcm5hbWUoZGlyZWN0b3J5KVxuICApIHtcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZnVuY3Rpb25hbC9uby1sb29wLXN0YXRlbWVudHNcbiAgICBmb3IgKGNvbnN0IGZpbGUgb2YgZmlsZU5hbWVzKSB7XG4gICAgICBpZiAoYXdhaXQgZmlsZUV4aXN0cyhwYXRoLmpvaW4oZGlyZWN0b3J5LCBmaWxlKSkpIHtcbiAgICAgICAgcmV0dXJuIHBhdGguam9pbihkaXJlY3RvcnksIGZpbGUpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICByZXR1cm4gdW5kZWZpbmVkO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZmluZExpbmVOdW1iZXJJblRleHQoXG4gIGNvbnRlbnQ6IHN0cmluZyxcbiAgcGF0dGVybjogc3RyaW5nLFxuKTogbnVtYmVyIHwgbnVsbCB7XG4gIGNvbnN0IGxpbmVzID0gY29udGVudC5zcGxpdCgvXFxyP1xcbi8pOyAvLyBTcGxpdCBsaW5lcywgaGFuZGxlIGJvdGggV2luZG93cyBhbmQgVU5JWCBsaW5lIGVuZGluZ3NcblxuICBjb25zdCBsaW5lTnVtYmVyID0gbGluZXMuZmluZEluZGV4KGxpbmUgPT4gbGluZS5pbmNsdWRlcyhwYXR0ZXJuKSkgKyAxOyAvLyArMSBiZWNhdXNlIGxpbmUgbnVtYmVycyBhcmUgMS1iYXNlZFxuICByZXR1cm4gbGluZU51bWJlciA9PT0gMCA/IG51bGwgOiBsaW5lTnVtYmVyOyAvLyBJZiB0aGUgcGFja2FnZSBpc24ndCBmb3VuZCwgcmV0dXJuIG51bGxcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZpbGVQYXRoVG9DbGlBcmcoZmlsZVBhdGg6IHN0cmluZyk6IHN0cmluZyB7XG4gIC8vIG5lZWRzIHRvIGJlIGVzY2FwZWQgaWYgc3BhY2VzIGluY2x1ZGVkXG4gIHJldHVybiBgXCIke2ZpbGVQYXRofVwiYDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHByb2plY3RUb0ZpbGVuYW1lKHByb2plY3Q6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiBwcm9qZWN0LnJlcGxhY2UoL1svXFxcXFxcc10rL2csICctJykucmVwbGFjZSgvQC9nLCAnJyk7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL2Zvcm1hdHRpbmcudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYlwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL2Zvcm1hdHRpbmcudHNcIjtpbXBvcnQge1xuICBNQVhfREVTQ1JJUFRJT05fTEVOR1RILFxuICBNQVhfSVNTVUVfTUVTU0FHRV9MRU5HVEgsXG4gIE1BWF9USVRMRV9MRU5HVEgsXG59IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuXG5leHBvcnQgZnVuY3Rpb24gc2x1Z2lmeSh0ZXh0OiBzdHJpbmcpOiBzdHJpbmcge1xuICByZXR1cm4gdGV4dFxuICAgIC50cmltKClcbiAgICAudG9Mb3dlckNhc2UoKVxuICAgIC5yZXBsYWNlKC9cXHMrfFxcLy9nLCAnLScpXG4gICAgLnJlcGxhY2UoL1teYS16XFxkLV0vZywgJycpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcGx1cmFsaXplKHRleHQ6IHN0cmluZywgYW1vdW50PzogbnVtYmVyKTogc3RyaW5nIHtcbiAgaWYgKGFtb3VudCAhPSBudWxsICYmIE1hdGguYWJzKGFtb3VudCkgPT09IDEpIHtcbiAgICByZXR1cm4gdGV4dDtcbiAgfVxuXG4gIGlmICh0ZXh0LmVuZHNXaXRoKCd5JykpIHtcbiAgICByZXR1cm4gYCR7dGV4dC5zbGljZSgwLCAtMSl9aWVzYDtcbiAgfVxuICBpZiAodGV4dC5lbmRzV2l0aCgncycpKSB7XG4gICAgcmV0dXJuIGAke3RleHR9ZXNgO1xuICB9XG4gIHJldHVybiBgJHt0ZXh0fXNgO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZm9ybWF0Qnl0ZXMoYnl0ZXM6IG51bWJlciwgZGVjaW1hbHMgPSAyKSB7XG4gIGNvbnN0IHBvc2l0aXZlQnl0ZXMgPSBNYXRoLm1heChieXRlcywgMCk7XG5cbiAgLy8gZWFybHkgZXhpdFxuICBpZiAocG9zaXRpdmVCeXRlcyA9PT0gMCkge1xuICAgIHJldHVybiAnMCBCJztcbiAgfVxuXG4gIGNvbnN0IGsgPSAxMDI0O1xuICBjb25zdCBkbSA9IE1hdGgubWF4KGRlY2ltYWxzLCAwKTtcbiAgY29uc3Qgc2l6ZXMgPSBbJ0InLCAna0InLCAnTUInLCAnR0InLCAnVEInLCAnUEInLCAnRUInLCAnWkInLCAnWUInXTtcblxuICBjb25zdCBpID0gTWF0aC5mbG9vcihNYXRoLmxvZyhwb3NpdGl2ZUJ5dGVzKSAvIE1hdGgubG9nKGspKTtcblxuICByZXR1cm4gYCR7TnVtYmVyLnBhcnNlRmxvYXQoKHBvc2l0aXZlQnl0ZXMgLyBNYXRoLnBvdyhrLCBpKSkudG9GaXhlZChkbSkpfSAke1xuICAgIHNpemVzW2ldXG4gIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcGx1cmFsaXplVG9rZW4odG9rZW46IHN0cmluZywgdGltZXM6IG51bWJlcik6IHN0cmluZyB7XG4gIHJldHVybiBgJHt0aW1lc30gJHtNYXRoLmFicyh0aW1lcykgPT09IDEgPyB0b2tlbiA6IHBsdXJhbGl6ZSh0b2tlbil9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdER1cmF0aW9uKGR1cmF0aW9uOiBudW1iZXIsIGdyYW51bGFyaXR5ID0gMCk6IHN0cmluZyB7XG4gIGlmIChkdXJhdGlvbiA8IDEwMDApIHtcbiAgICByZXR1cm4gYCR7Z3JhbnVsYXJpdHkgPyBkdXJhdGlvbi50b0ZpeGVkKGdyYW51bGFyaXR5KSA6IGR1cmF0aW9ufSBtc2A7XG4gIH1cbiAgcmV0dXJuIGAkeyhkdXJhdGlvbiAvIDEwMDApLnRvRml4ZWQoMil9IHNgO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZm9ybWF0RGF0ZShkYXRlOiBEYXRlKTogc3RyaW5nIHtcbiAgY29uc3QgbG9jYWxlID0gJ2VuLVVTJzsgLy8gZml4ZWQgbG9jYWxlIHRvIGVuc3VyZSBjb25zaXN0ZW5jeSBhY3Jvc3MgbG9jYWwgZGVmYXVsdHMgZXhlY3V0aW9uXG4gIHJldHVybiBkYXRlXG4gICAgLnRvTG9jYWxlU3RyaW5nKGxvY2FsZSwge1xuICAgICAgd2Vla2RheTogJ3Nob3J0JyxcbiAgICAgIG1vbnRoOiAnc2hvcnQnLFxuICAgICAgZGF5OiAnbnVtZXJpYycsXG4gICAgICB5ZWFyOiAnbnVtZXJpYycsXG4gICAgICBob3VyOiAnbnVtZXJpYycsXG4gICAgICBtaW51dGU6ICcyLWRpZ2l0JyxcbiAgICAgIHRpbWVab25lTmFtZTogJ3Nob3J0JyxcbiAgICB9KVxuICAgIC5yZXBsYWNlKC9cXHUyMDJGL2csICcgJyk7IC8vIHNlZSBodHRwczovL2dpdGh1Yi5jb20vbm9kZWpzL25vZGUvaXNzdWVzLzQ1MTcxXG59XG5cbmV4cG9ydCBmdW5jdGlvbiB0cnVuY2F0ZVRleHQoXG4gIHRleHQ6IHN0cmluZyxcbiAgb3B0aW9uczpcbiAgICB8IG51bWJlclxuICAgIHwge1xuICAgICAgICBtYXhDaGFyczogbnVtYmVyO1xuICAgICAgICBwb3NpdGlvbj86ICdzdGFydCcgfCAnbWlkZGxlJyB8ICdlbmQnO1xuICAgICAgICBlbGxpcHNpcz86IHN0cmluZztcbiAgICAgIH0sXG4pOiBzdHJpbmcge1xuICBjb25zdCB7XG4gICAgbWF4Q2hhcnMsXG4gICAgcG9zaXRpb24gPSAnZW5kJyxcbiAgICBlbGxpcHNpcyA9ICcuLi4nLFxuICB9ID0gdHlwZW9mIG9wdGlvbnMgPT09ICdudW1iZXInID8geyBtYXhDaGFyczogb3B0aW9ucyB9IDogb3B0aW9ucztcbiAgaWYgKHRleHQubGVuZ3RoIDw9IG1heENoYXJzKSB7XG4gICAgcmV0dXJuIHRleHQ7XG4gIH1cblxuICBjb25zdCBtYXhMZW5ndGggPSBtYXhDaGFycyAtIGVsbGlwc2lzLmxlbmd0aDtcbiAgc3dpdGNoIChwb3NpdGlvbikge1xuICAgIGNhc2UgJ3N0YXJ0JzpcbiAgICAgIHJldHVybiBlbGxpcHNpcyArIHRleHQuc2xpY2UoLW1heExlbmd0aCkudHJpbSgpO1xuICAgIGNhc2UgJ21pZGRsZSc6XG4gICAgICBjb25zdCBoYWxmTWF4Q2hhcnMgPSBNYXRoLmZsb29yKG1heExlbmd0aCAvIDIpO1xuICAgICAgcmV0dXJuIChcbiAgICAgICAgdGV4dC5zbGljZSgwLCBoYWxmTWF4Q2hhcnMpLnRyaW0oKSArXG4gICAgICAgIGVsbGlwc2lzICtcbiAgICAgICAgdGV4dC5zbGljZSgtaGFsZk1heENoYXJzKS50cmltKClcbiAgICAgICk7XG4gICAgY2FzZSAnZW5kJzpcbiAgICAgIHJldHVybiB0ZXh0LnNsaWNlKDAsIG1heExlbmd0aCkudHJpbSgpICsgZWxsaXBzaXM7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHRydW5jYXRlVGl0bGUodGV4dDogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIHRydW5jYXRlVGV4dCh0ZXh0LCBNQVhfVElUTEVfTEVOR1RIKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHRydW5jYXRlRGVzY3JpcHRpb24odGV4dDogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIHRydW5jYXRlVGV4dCh0ZXh0LCBNQVhfREVTQ1JJUFRJT05fTEVOR1RIKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHRydW5jYXRlSXNzdWVNZXNzYWdlKHRleHQ6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiB0cnVuY2F0ZVRleHQodGV4dCwgTUFYX0lTU1VFX01FU1NBR0VfTEVOR1RIKTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvbG9nZ2luZy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvbG9nZ2luZy50c1wiO2ltcG9ydCBpc2FhY3NfY2xpdWkgZnJvbSAnQGlzYWFjcy9jbGl1aSc7XG5pbXBvcnQgeyBjbGl1aSB9IGZyb20gJ0Bwb3BwaW5zcy9jbGl1aSc7XG5pbXBvcnQgeyB1bmRlcmxpbmUgfSBmcm9tICdhbnNpcyc7XG5pbXBvcnQgeyBURVJNSU5BTF9XSURUSCB9IGZyb20gJy4vcmVwb3J0cy9jb25zdGFudHMuanMnO1xuXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnlcbnR5cGUgQXJndW1lbnRzVHlwZTxUPiA9IFQgZXh0ZW5kcyAoLi4uYXJnczogaW5mZXIgVSkgPT4gYW55ID8gVSA6IG5ldmVyO1xuZXhwb3J0IHR5cGUgQ2xpVWlCYXNlID0gUmV0dXJuVHlwZTx0eXBlb2YgY2xpdWk+O1xudHlwZSBVSSA9IFJldHVyblR5cGU8dHlwZW9mIGlzYWFjc19jbGl1aT47XG50eXBlIENsaUV4dGVuc2lvbiA9IHtcbiAgcm93OiAocjogQXJndW1lbnRzVHlwZTxVSVsnZGl2J10+KSA9PiB2b2lkO1xufTtcbmV4cG9ydCB0eXBlIENvbHVtbiA9IHtcbiAgdGV4dDogc3RyaW5nO1xuICB3aWR0aD86IG51bWJlcjtcbiAgYWxpZ24/OiAncmlnaHQnIHwgJ2xlZnQnIHwgJ2NlbnRlcic7XG4gIHBhZGRpbmc6IG51bWJlcltdO1xuICBib3JkZXI/OiBib29sZWFuO1xufTtcbmV4cG9ydCB0eXBlIENsaVVpID0gQ2xpVWlCYXNlICYgQ2xpRXh0ZW5zaW9uO1xuXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgaW1wb3J0L25vLW11dGFibGUtZXhwb3J0cyxmdW5jdGlvbmFsL25vLWxldFxuZXhwb3J0IGxldCBzaW5nbGV0b25VaUluc3RhbmNlOiBDbGlVaUJhc2UgfCB1bmRlZmluZWQ7XG5cbmV4cG9ydCBmdW5jdGlvbiB1aSgpOiBDbGlVaSB7XG4gIGlmIChzaW5nbGV0b25VaUluc3RhbmNlID09PSB1bmRlZmluZWQpIHtcbiAgICBzaW5nbGV0b25VaUluc3RhbmNlID0gY2xpdWkoKTtcbiAgfVxuICByZXR1cm4ge1xuICAgIC4uLnNpbmdsZXRvblVpSW5zdGFuY2UsXG4gICAgcm93OiBhcmdzID0+IHtcbiAgICAgIGxvZ0xpc3RJdGVtKGFyZ3MpO1xuICAgIH0sXG4gIH07XG59XG5cbi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBmdW5jdGlvbmFsL25vLWxldFxubGV0IHNpbmdsZXRvbmlzYWFjVWk6IFVJIHwgdW5kZWZpbmVkO1xuZXhwb3J0IGZ1bmN0aW9uIGxvZ0xpc3RJdGVtKGFyZ3M6IEFyZ3VtZW50c1R5cGU8VUlbJ2RpdiddPikge1xuICBpZiAoc2luZ2xldG9uaXNhYWNVaSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgc2luZ2xldG9uaXNhYWNVaSA9IGlzYWFjc19jbGl1aSh7IHdpZHRoOiBURVJNSU5BTF9XSURUSCB9KTtcbiAgfVxuICBzaW5nbGV0b25pc2FhY1VpLmRpdiguLi5hcmdzKTtcbiAgY29uc3QgY29udGVudCA9IHNpbmdsZXRvbmlzYWFjVWkudG9TdHJpbmcoKTtcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGZ1bmN0aW9uYWwvaW1tdXRhYmxlLWRhdGFcbiAgc2luZ2xldG9uaXNhYWNVaS5yb3dzID0gW107XG4gIHNpbmdsZXRvblVpSW5zdGFuY2U/LmxvZ2dlci5sb2coY29udGVudCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsaW5rKHRleHQ6IHN0cmluZykge1xuICByZXR1cm4gdW5kZXJsaW5lLmJsdWVCcmlnaHQodGV4dCk7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL2dpdC9naXQuY29tbWl0cy1hbmQtdGFncy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL2dpdFwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL2dpdC9naXQuY29tbWl0cy1hbmQtdGFncy50c1wiO2ltcG9ydCB7IHR5cGUgTG9nT3B0aW9ucyBhcyBTaW1wbGVHaXRMb2dPcHRpb25zLCBzaW1wbGVHaXQgfSBmcm9tICdzaW1wbGUtZ2l0JztcbmltcG9ydCB7IHR5cGUgQ29tbWl0LCBjb21taXRTY2hlbWEgfSBmcm9tICdAY29kZS1wdXNodXAvbW9kZWxzJztcbmltcG9ydCB7IGlzU2VtdmVyIH0gZnJvbSAnLi4vc2VtdmVyLmpzJztcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdldExhdGVzdENvbW1pdChcbiAgZ2l0ID0gc2ltcGxlR2l0KCksXG4pOiBQcm9taXNlPENvbW1pdCB8IG51bGw+IHtcbiAgY29uc3QgbG9nID0gYXdhaXQgZ2l0LmxvZyh7XG4gICAgbWF4Q291bnQ6IDEsXG4gICAgLy8gZ2l0IGxvZyAtMSAtLXByZXR0eT1mb3JtYXQ6XCIlSCAlcyAlYW4gJWFJXCIgLSBTZWU6IGh0dHBzOi8vZ2l0LXNjbS5jb20vZG9jcy9wcmV0dHktZm9ybWF0c1xuICAgIGZvcm1hdDogeyBoYXNoOiAnJUgnLCBtZXNzYWdlOiAnJXMnLCBhdXRob3I6ICclYW4nLCBkYXRlOiAnJWFJJyB9LFxuICB9KTtcbiAgcmV0dXJuIGNvbW1pdFNjaGVtYS5wYXJzZShsb2cubGF0ZXN0KTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdldEN1cnJlbnRCcmFuY2hPclRhZyhcbiAgZ2l0ID0gc2ltcGxlR2l0KCksXG4pOiBQcm9taXNlPHN0cmluZz4ge1xuICByZXR1cm4gKFxuICAgIChhd2FpdCBnaXQuYnJhbmNoKCkudGhlbihyID0+IHIuY3VycmVudCkpIHx8XG4gICAgLy8gSWYgbm8gY3VycmVudCBicmFuY2gsIHRyeSB0byBnZXQgdGhlIHRhZ1xuICAgIC8vIEBUT0RPIHVzZSBzaW1wbGUgZ2l0XG4gICAgKGF3YWl0IGdpdFxuICAgICAgLnJhdyhbJ2Rlc2NyaWJlJywgJy0tdGFncycsICctLWV4YWN0LW1hdGNoJ10pXG4gICAgICAudGhlbihvdXQgPT4gb3V0LnRyaW0oKSkpXG4gICk7XG59XG5cbmV4cG9ydCB0eXBlIExvZ1Jlc3VsdCA9IHsgaGFzaDogc3RyaW5nOyBtZXNzYWdlOiBzdHJpbmcgfTtcblxuZnVuY3Rpb24gdmFsaWRhdGVGaWx0ZXIoeyBmcm9tLCB0byB9OiBMb2dPcHRpb25zKSB7XG4gIGlmICh0byAmJiAhZnJvbSkge1xuICAgIC8vIHRocm93IG1vcmUgdXNlci1mcmllbmRseSBlcnJvciBpbnN0ZWFkIG9mOlxuICAgIC8vIGZhdGFsOiBhbWJpZ3VvdXMgYXJndW1lbnQgJy4uLmEnOiB1bmtub3duIHJldmlzaW9uIG9yIHBhdGggbm90IGluIHRoZSB3b3JraW5nIHRyZWUuXG4gICAgLy8gVXNlICctLScgdG8gc2VwYXJhdGUgcGF0aHMgZnJvbSByZXZpc2lvbnMsIGxpa2UgdGhpczpcbiAgICAvLyAnZ2l0IDxjb21tYW5kPiBbPHJldmlzaW9uPi4uLl0gLS0gWzxmaWxlPi4uLl0nXG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYGZpbHRlciBuZWVkcyB0aGUgXCJmcm9tXCIgb3B0aW9uIGRlZmluZWQgdG8gYWNjZXB0IHRoZSBcInRvXCIgb3B0aW9uLlxcbmAsXG4gICAgKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZmlsdGVyTG9ncyhcbiAgYWxsVGFnczogc3RyaW5nW10sXG4gIG9wdD86IFBpY2s8TG9nT3B0aW9ucywgJ2Zyb20nIHwgJ3RvJyB8ICdtYXhDb3VudCc+LFxuKSB7XG4gIGlmICghb3B0KSB7XG4gICAgcmV0dXJuIGFsbFRhZ3M7XG4gIH1cbiAgdmFsaWRhdGVGaWx0ZXIob3B0KTtcbiAgY29uc3QgeyBmcm9tLCB0bywgbWF4Q291bnQgfSA9IG9wdDtcbiAgY29uc3QgZmluSW5kZXggPSA8VD4odGFnTmFtZT86IHN0cmluZywgZmFsbGJhY2s/OiBUKSA9PiB7XG4gICAgY29uc3QgaWR4ID0gYWxsVGFncy5pbmRleE9mKHRhZ05hbWUgPz8gJycpO1xuICAgIGlmIChpZHggIT09IC0xKSB7XG4gICAgICByZXR1cm4gaWR4O1xuICAgIH1cbiAgICByZXR1cm4gZmFsbGJhY2s7XG4gIH07XG4gIGNvbnN0IGZyb21JbmRleCA9IGZpbkluZGV4KGZyb20sIDApO1xuICBjb25zdCB0b0luZGV4ID0gZmluSW5kZXgodG8sIHVuZGVmaW5lZCk7XG4gIHJldHVybiBhbGxUYWdzXG4gICAgLnNsaWNlKGZyb21JbmRleCwgdG9JbmRleCA/IHRvSW5kZXggKyAxIDogdG9JbmRleClcbiAgICAuc2xpY2UoMCwgbWF4Q291bnQgPz8gdW5kZWZpbmVkKTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdldEhhc2hGcm9tVGFnKFxuICB0YWc6IHN0cmluZyxcbiAgZ2l0ID0gc2ltcGxlR2l0KCksXG4pOiBQcm9taXNlPExvZ1Jlc3VsdD4ge1xuICBjb25zdCB0YWdEZXRhaWxzID0gYXdhaXQgZ2l0LnNob3coWyctLW5vLXBhdGNoJywgJy0tZm9ybWF0PSVIJywgdGFnXSk7XG4gIGNvbnN0IGhhc2ggPSB0YWdEZXRhaWxzLnRyaW0oKTsgLy8gUmVtb3ZlIHF1b3RlcyBhbmQgdHJpbSB3aGl0ZXNwYWNlXG4gIHJldHVybiB7XG4gICAgaGFzaDogaGFzaC5zcGxpdCgnXFxuJykuYXQoLTEpID8/ICcnLFxuICAgIG1lc3NhZ2U6IHRhZyxcbiAgfTtcbn1cblxuZXhwb3J0IHR5cGUgTG9nT3B0aW9ucyA9IHtcbiAgdGFyZ2V0QnJhbmNoPzogc3RyaW5nO1xuICBmcm9tPzogc3RyaW5nO1xuICB0bz86IHN0cmluZztcbiAgbWF4Q291bnQ/OiBudW1iZXI7XG59O1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0U2VtdmVyVGFncyhcbiAgb3B0OiBMb2dPcHRpb25zID0ge30sXG4gIGdpdCA9IHNpbXBsZUdpdCgpLFxuKTogUHJvbWlzZTxMb2dSZXN1bHRbXT4ge1xuICB2YWxpZGF0ZUZpbHRlcihvcHQpO1xuICBjb25zdCB7IHRhcmdldEJyYW5jaCwgLi4ub3B0aW9ucyB9ID0gb3B0O1xuICAvLyBtYWtlIHN1cmUgd2UgaGF2ZSBhIHRhcmdldCBicmFuY2hcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGZ1bmN0aW9uYWwvbm8tbGV0XG4gIGxldCBjdXJyZW50QnJhbmNoO1xuICBpZiAodGFyZ2V0QnJhbmNoKSB7XG4gICAgY3VycmVudEJyYW5jaCA9IGF3YWl0IGdldEN1cnJlbnRCcmFuY2hPclRhZyhnaXQpO1xuICAgIGF3YWl0IGdpdC5jaGVja291dCh0YXJnZXRCcmFuY2gpO1xuICB9XG5cbiAgLy8gRmV0Y2ggYWxsIHRhZ3MgbWVyZ2VkIGludG8gdGhlIHRhcmdldCBicmFuY2hcbiAgY29uc3QgdGFnc1JhdyA9IGF3YWl0IGdpdC50YWcoW1xuICAgICctLW1lcmdlZCcsXG4gICAgdGFyZ2V0QnJhbmNoID8/IChhd2FpdCBnZXRDdXJyZW50QnJhbmNoT3JUYWcoZ2l0KSksXG4gIF0pO1xuXG4gIGNvbnN0IGFsbFRhZ3MgPSB0YWdzUmF3XG4gICAgLnNwbGl0KC9cXG4vKVxuICAgIC5tYXAodGFnID0+IHRhZy50cmltKCkpXG4gICAgLmZpbHRlcihCb29sZWFuKVxuICAgIC5maWx0ZXIoaXNTZW12ZXIpO1xuXG4gIGNvbnN0IHJlbGV2YW50VGFncyA9IGZpbHRlckxvZ3MoYWxsVGFncywgb3B0aW9ucyk7XG5cbiAgY29uc3QgdGFnc1dpdGhIYXNoZXM6IExvZ1Jlc3VsdFtdID0gYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgcmVsZXZhbnRUYWdzLm1hcCh0YWcgPT4gZ2V0SGFzaEZyb21UYWcodGFnLCBnaXQpKSxcbiAgKTtcblxuICBpZiAoY3VycmVudEJyYW5jaCkge1xuICAgIGF3YWl0IGdpdC5jaGVja291dChjdXJyZW50QnJhbmNoKTtcbiAgfVxuXG4gIHJldHVybiB0YWdzV2l0aEhhc2hlcztcbn1cblxuLyoqXG4gKiBgZ2V0SGFzaGVzYCByZXR1cm5zIGEgbGlzdCBvZiBjb21taXQgaGFzaGVzLiBJbnRlcm5hbGx5IGl0IHVzZXMgYGdpdC5sb2coKWAgdG8gZGV0ZXJtaW5lIHRoZSBjb21taXRzIHdpdGhpbiBhIHJhbmdlLlxuICogVGhlIGFtb3VudCBjYW4gYmUgbGltaXRlZCB0byBhIG1heGltdW0gbnVtYmVyIG9mIGNvbW1pdHMgc3BlY2lmaWVkIGJ5IGBtYXhDb3VudGAuXG4gKiBXaXRoIGBmcm9tYCBhbmQgYHRvYCwgeW91IGNhbiBzcGVjaWZ5IGEgcmFuZ2Ugb2YgY29tbWl0cy5cbiAqXG4gKiAqKk5PVEU6KipcbiAqIEluIEdpdCwgc3BlY2lmeWluZyBhIHJhbmdlIHdpdGggdHdvIGRvdHMgKGBmcm9tLi50b2ApIHNlbGVjdHMgY29tbWl0cyB0aGF0IGFyZSByZWFjaGFibGUgZnJvbSBgdG9gIGJ1dCBub3QgZnJvbSBgZnJvbWAuXG4gKiBFc3NlbnRpYWxseSwgaXQgc2hvd3MgdGhlIGNvbW1pdHMgdGhhdCBhcmUgaW4gYHRvYCBidXQgbm90IGluIGBmcm9tYCwgZXhjbHVkaW5nIHRoZSBjb21taXRzIHVuaXF1ZSB0byBgZnJvbWAuXG4gKlxuICogRXhhbXBsZTpcbiAqXG4gKiBMZXQncyBjb25zaWRlciB0aGUgZm9sbG93aW5nIGNvbW1pdCBoaXN0b3J5OlxuICpcbiAqICAgQS0tLUItLS1DLS0tRC0tLUUgKG1haW4pXG4gKlxuICogVXNpbmcgYGdpdCBsb2cgQi4uRGAsIHlvdSB3b3VsZCBnZXQgdGhlIGNvbW1pdHMgQyBhbmQgRDpcbiAqXG4gKiAgIEMtLS1EXG4gKlxuICogVGhpcyBpcyBiZWNhdXNlIHRoZXNlIGNvbW1pdHMgYXJlIHJlYWNoYWJsZSBmcm9tIEQgYnV0IG5vdCBmcm9tIEIuXG4gKlxuICogQVNDSUkgUmVwcmVzZW50YXRpb246XG4gKlxuICogICBNYWluIEJyYW5jaDogICAgQS0tLUItLS1DLS0tRC0tLUVcbiAqICAgICAgICAgICAgICAgICAgICAgICBcXCAgICAgICBcXFxuICogICAgICAgICAgICAgICAgICAgICAgICBcXCAgICAgICArLS0tIENvbW1pdHMgaW5jbHVkZWQgaW4gYGdpdCBsb2cgQi4uRGBcbiAqICAgICAgICAgICAgICAgICAgICAgICAgIFxcXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgKy0tLSBFeGNsdWRlZCBieSB0aGUgYGZyb21gIHBhcmFtZXRlclxuICpcbiAqIFdpdGggYHNpbXBsZS1naXRgLCB3aGVuIHlvdSBzcGVjaWZ5IGEgYGZyb21gIGFuZCBgdG9gIHJhbmdlIGxpa2UgdGhpczpcbiAqXG4gKiAgIGdpdC5sb2coeyBmcm9tOiAnQicsIHRvOiAnRCcgfSk7XG4gKlxuICogSXQgaW50ZXJwcmV0cyBpdCBzaW1pbGFybHksIHNlbGVjdGluZyBjb21taXRzIGJldHdlZW4gQiBhbmQgRCwgaW5jbHVzaXZlIG9mIEQgYnV0IGV4Y2x1c2l2ZSBvZiBCLlxuICogRm9yIGBnaXQubG9nKHsgZnJvbTogJ0InLCB0bzogJ0QnIH0pYCBvciBgZ2l0IGxvZyBCLi5EYCwgY29tbWl0cyBDIGFuZCBEIGFyZSBzZWxlY3RlZC5cbiAqXG4gKiBAcGFyYW0gb3B0aW9ucyBPYmplY3QgY29udGFpbmluZyBgZnJvbWAsIGB0b2AsIGFuZCBvcHRpb25hbGx5IGBtYXhDb3VudGAgdG8gc3BlY2lmeSB0aGUgY29tbWl0IHJhbmdlIGFuZCBsaW1pdC5cbiAqIEBwYXJhbSBnaXQgVGhlIGBzaW1wbGUtZ2l0YCBpbnN0YW5jZSB1c2VkIHRvIGV4ZWN1dGUgR2l0IGNvbW1hbmRzLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0SGFzaGVzKFxuICBvcHRpb25zOiBTaW1wbGVHaXRMb2dPcHRpb25zICYgUGljazxMb2dPcHRpb25zLCAndGFyZ2V0QnJhbmNoJz4gPSB7fSxcbiAgZ2l0ID0gc2ltcGxlR2l0KCksXG4pOiBQcm9taXNlPExvZ1Jlc3VsdFtdPiB7XG4gIGNvbnN0IHsgdGFyZ2V0QnJhbmNoLCBmcm9tLCB0bywgbWF4Q291bnQsIC4uLm9wdCB9ID0gb3B0aW9ucztcblxuICB2YWxpZGF0ZUZpbHRlcih7IGZyb20sIHRvIH0pO1xuXG4gIC8vIEVuc3VyZSB5b3UgYXJlIG9uIHRoZSBjb3JyZWN0IGJyYW5jaFxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZnVuY3Rpb25hbC9uby1sZXRcbiAgbGV0IGN1cnJlbnRCcmFuY2g7XG4gIGlmICh0YXJnZXRCcmFuY2gpIHtcbiAgICBjdXJyZW50QnJhbmNoID0gYXdhaXQgZ2V0Q3VycmVudEJyYW5jaE9yVGFnKGdpdCk7XG4gICAgYXdhaXQgZ2l0LmNoZWNrb3V0KHRhcmdldEJyYW5jaCk7XG4gIH1cblxuICBjb25zdCBsb2dzID0gYXdhaXQgZ2l0LmxvZyh7XG4gICAgLi4ub3B0LFxuICAgIGZvcm1hdDoge1xuICAgICAgaGFzaDogJyVIJyxcbiAgICAgIG1lc3NhZ2U6ICclcycsXG4gICAgfSxcbiAgICBmcm9tLFxuICAgIHRvLFxuICAgIG1heENvdW50LFxuICB9KTtcblxuICAvLyBFbnN1cmUgeW91IGFyZSBiYWNrIHRvIHRoZSBpbml0aWFsIGJyYW5jaFxuICBpZiAodGFyZ2V0QnJhbmNoKSB7XG4gICAgYXdhaXQgZ2l0LmNoZWNrb3V0KGN1cnJlbnRCcmFuY2ggYXMgc3RyaW5nKTtcbiAgfVxuXG4gIHJldHVybiBbLi4ubG9ncy5hbGxdO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9zZW12ZXIudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYlwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3NlbXZlci50c1wiO2ltcG9ydCB7IHJjb21wYXJlLCB2YWxpZCB9IGZyb20gJ3NlbXZlcic7XG5cbmV4cG9ydCBmdW5jdGlvbiBub3JtYWxpemVTZW12ZXIoc2VtdmVyU3RyaW5nOiBzdHJpbmcpOiBzdHJpbmcge1xuICBpZiAoc2VtdmVyU3RyaW5nLnN0YXJ0c1dpdGgoJ3YnKSB8fCBzZW12ZXJTdHJpbmcuc3RhcnRzV2l0aCgnVicpKSB7XG4gICAgcmV0dXJuIHNlbXZlclN0cmluZy5zbGljZSgxKTtcbiAgfVxuXG4gIGlmIChzZW12ZXJTdHJpbmcuaW5jbHVkZXMoJ0AnKSkge1xuICAgIHJldHVybiBzZW12ZXJTdHJpbmcuc3BsaXQoJ0AnKS5hdCgtMSkgPz8gJyc7XG4gIH1cblxuICByZXR1cm4gc2VtdmVyU3RyaW5nO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gaXNTZW12ZXIoc2VtdmVyU3RyaW5nID0gJycpOiBib29sZWFuIHtcbiAgcmV0dXJuIHZhbGlkKG5vcm1hbGl6ZVNlbXZlcihzZW12ZXJTdHJpbmcpKSAhPSBudWxsO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gc29ydFNlbXZlcnMoc2VtdmVyU3RyaW5nczogc3RyaW5nW10pOiBzdHJpbmdbXSB7XG4gIHJldHVybiBzZW12ZXJTdHJpbmdzLm1hcChub3JtYWxpemVTZW12ZXIpLmZpbHRlcihpc1NlbXZlcikuc29ydChyY29tcGFyZSk7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL2dpdC9naXQudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9naXRcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9naXQvZ2l0LnRzXCI7aW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB7IHR5cGUgU3RhdHVzUmVzdWx0LCBzaW1wbGVHaXQgfSBmcm9tICdzaW1wbGUtZ2l0JztcbmltcG9ydCB7IHVpIH0gZnJvbSAnLi4vbG9nZ2luZy5qcyc7XG5pbXBvcnQgeyB0b1VuaXhQYXRoIH0gZnJvbSAnLi4vdHJhbnNmb3JtLmpzJztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEdpdFJvb3QoZ2l0ID0gc2ltcGxlR2l0KCkpOiBQcm9taXNlPHN0cmluZz4ge1xuICByZXR1cm4gZ2l0LnJldnBhcnNlKCctLXNob3ctdG9wbGV2ZWwnKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdEdpdFBhdGgoZmlsZVBhdGg6IHN0cmluZywgZ2l0Um9vdDogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgYWJzb2x1dGVQYXRoID0gcGF0aC5pc0Fic29sdXRlKGZpbGVQYXRoKVxuICAgID8gZmlsZVBhdGhcbiAgICA6IHBhdGguam9pbihwcm9jZXNzLmN3ZCgpLCBmaWxlUGF0aCk7XG4gIGNvbnN0IHJlbGF0aXZlUGF0aCA9IHBhdGgucmVsYXRpdmUoZ2l0Um9vdCwgYWJzb2x1dGVQYXRoKTtcbiAgcmV0dXJuIHRvVW5peFBhdGgocmVsYXRpdmVQYXRoKTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHRvR2l0UGF0aChcbiAgZmlsZVBhdGg6IHN0cmluZyxcbiAgZ2l0ID0gc2ltcGxlR2l0KCksXG4pOiBQcm9taXNlPHN0cmluZz4ge1xuICBjb25zdCBnaXRSb290ID0gYXdhaXQgZ2V0R2l0Um9vdChnaXQpO1xuICByZXR1cm4gZm9ybWF0R2l0UGF0aChmaWxlUGF0aCwgZ2l0Um9vdCk7XG59XG5cbmV4cG9ydCBjbGFzcyBHaXRTdGF0dXNFcnJvciBleHRlbmRzIEVycm9yIHtcbiAgc3RhdGljIGlnbm9yZWRQcm9wcyA9IG5ldyBTZXQoWydjdXJyZW50JywgJ3RyYWNraW5nJ10pO1xuXG4gIHN0YXRpYyBnZXRSZWR1Y2VkU3RhdHVzKHN0YXR1czogU3RhdHVzUmVzdWx0KSB7XG4gICAgcmV0dXJuIE9iamVjdC5mcm9tRW50cmllcyhcbiAgICAgIE9iamVjdC5lbnRyaWVzKHN0YXR1cylcbiAgICAgICAgLmZpbHRlcigoW2tleV0pID0+ICF0aGlzLmlnbm9yZWRQcm9wcy5oYXMoa2V5KSlcbiAgICAgICAgLmZpbHRlcihcbiAgICAgICAgICAoXG4gICAgICAgICAgICBlbnRyeTogW1xuICAgICAgICAgICAgICBzdHJpbmcsXG4gICAgICAgICAgICAgIG51bWJlciB8IHN0cmluZyB8IGJvb2xlYW4gfCBudWxsIHwgdW5kZWZpbmVkIHwgdW5rbm93bltdLFxuICAgICAgICAgICAgXSxcbiAgICAgICAgICApID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHZhbHVlID0gZW50cnlbMV07XG4gICAgICAgICAgICBpZiAodmFsdWUgPT0gbnVsbCkge1xuICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoQXJyYXkuaXNBcnJheSh2YWx1ZSkgJiYgdmFsdWUubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdudW1iZXInICYmIHZhbHVlID09PSAwKSB7XG4gICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiAhKHR5cGVvZiB2YWx1ZSA9PT0gJ2Jvb2xlYW4nICYmICF2YWx1ZSk7XG4gICAgICAgICAgfSxcbiAgICAgICAgKSxcbiAgICApO1xuICB9XG5cbiAgY29uc3RydWN0b3Ioc3RhdHVzOiBTdGF0dXNSZXN1bHQpIHtcbiAgICBzdXBlcihcbiAgICAgIGBXb3JraW5nIGRpcmVjdG9yeSBuZWVkcyB0byBiZSBjbGVhbiBiZWZvcmUgd2UgeW91IGNhbiBwcm9jZWVkLiBDb21taXQgeW91ciBsb2NhbCBjaGFuZ2VzIG9yIHN0YXNoIHRoZW06IFxcbiAke0pTT04uc3RyaW5naWZ5KFxuICAgICAgICBHaXRTdGF0dXNFcnJvci5nZXRSZWR1Y2VkU3RhdHVzKHN0YXR1cyksXG4gICAgICAgIG51bGwsXG4gICAgICAgIDIsXG4gICAgICApfWAsXG4gICAgKTtcbiAgfVxufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ3VhcmRBZ2FpbnN0TG9jYWxDaGFuZ2VzKFxuICBnaXQgPSBzaW1wbGVHaXQoKSxcbik6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBzdGF0dXMgPSBhd2FpdCBnaXQuc3RhdHVzKFsnLXMnXSk7XG4gIGlmIChzdGF0dXMuZmlsZXMubGVuZ3RoID4gMCkge1xuICAgIHRocm93IG5ldyBHaXRTdGF0dXNFcnJvcihzdGF0dXMpO1xuICB9XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzYWZlQ2hlY2tvdXQoXG4gIGJyYW5jaE9ySGFzaDogc3RyaW5nLFxuICBmb3JjZUNsZWFuU3RhdHVzID0gZmFsc2UsXG4gIGdpdCA9IHNpbXBsZUdpdCgpLFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIC8vIGdpdCByZXF1aXJlcyBhIGNsZWFuIGhpc3RvcnkgdG8gY2hlY2sgb3V0IGEgYnJhbmNoXG4gIGlmIChmb3JjZUNsZWFuU3RhdHVzKSB7XG4gICAgYXdhaXQgZ2l0LnJhdyhbJ3Jlc2V0JywgJy0taGFyZCddKTtcbiAgICBhd2FpdCBnaXQuY2xlYW4oWydmJywgJ2QnXSk7XG4gICAgdWkoKS5sb2dnZXIuaW5mbyhgZ2l0IHN0YXR1cyBjbGVhbmVkYCk7XG4gIH1cbiAgYXdhaXQgZ3VhcmRBZ2FpbnN0TG9jYWxDaGFuZ2VzKGdpdCk7XG4gIGF3YWl0IGdpdC5jaGVja291dChicmFuY2hPckhhc2gpO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi90cmFuc2Zvcm0udHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYlwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3RyYW5zZm9ybS50c1wiO2ltcG9ydCB7IHBsYXRmb3JtIH0gZnJvbSAnbm9kZTpvcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiB0b0FycmF5PFQ+KHZhbDogVCB8IFRbXSk6IFRbXSB7XG4gIHJldHVybiBBcnJheS5pc0FycmF5KHZhbCkgPyB2YWwgOiBbdmFsXTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG9iamVjdFRvS2V5czxUIGV4dGVuZHMgb2JqZWN0PihvYmo6IFQpIHtcbiAgcmV0dXJuIE9iamVjdC5rZXlzKG9iaikgYXMgKGtleW9mIFQpW107XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBvYmplY3RUb0VudHJpZXM8VCBleHRlbmRzIG9iamVjdD4ob2JqOiBUKSB7XG4gIHJldHVybiBPYmplY3QuZW50cmllcyhvYmopIGFzIFtrZXlvZiBULCBUW2tleW9mIFRdXVtdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gb2JqZWN0RnJvbUVudHJpZXM8SyBleHRlbmRzIFByb3BlcnR5S2V5LCBWPihlbnRyaWVzOiBbSywgVl1bXSkge1xuICByZXR1cm4gT2JqZWN0LmZyb21FbnRyaWVzKGVudHJpZXMpIGFzIFJlY29yZDxLLCBWPjtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvdW50T2NjdXJyZW5jZXM8VCBleHRlbmRzIFByb3BlcnR5S2V5PihcbiAgdmFsdWVzOiBUW10sXG4pOiBQYXJ0aWFsPFJlY29yZDxULCBudW1iZXI+PiB7XG4gIHJldHVybiB2YWx1ZXMucmVkdWNlPFBhcnRpYWw8UmVjb3JkPFQsIG51bWJlcj4+PihcbiAgICAoYWNjLCB2YWx1ZSkgPT4gKHsgLi4uYWNjLCBbdmFsdWVdOiAoYWNjW3ZhbHVlXSA/PyAwKSArIDEgfSksXG4gICAge30sXG4gICk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBkaXN0aW5jdDxUIGV4dGVuZHMgc3RyaW5nIHwgbnVtYmVyIHwgYm9vbGVhbj4oYXJyYXk6IFRbXSk6IFRbXSB7XG4gIHJldHVybiBbLi4ubmV3IFNldChhcnJheSldO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZGVlcENsb25lPFQ+KG9iajogVCk6IFQge1xuICByZXR1cm4gb2JqID09IG51bGwgfHwgdHlwZW9mIG9iaiAhPT0gJ29iamVjdCcgPyBvYmogOiBzdHJ1Y3R1cmVkQ2xvbmUob2JqKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZhY3Rvck9mPFQ+KGl0ZW1zOiBUW10sIGZpbHRlckZuOiAoaTogVCkgPT4gYm9vbGVhbik6IG51bWJlciB7XG4gIGNvbnN0IGl0ZW1Db3VudCA9IGl0ZW1zLmxlbmd0aDtcbiAgLy8gZWFybHkgZXhpdCBmb3IgZW1wdHkgcm93c1xuICBpZiAoIWl0ZW1Db3VudCkge1xuICAgIHJldHVybiAxO1xuICB9XG4gIGNvbnN0IGZpbHRlckNvdW50ID0gaXRlbXMuZmlsdGVyKGZpbHRlckZuKS5sZW5ndGg7XG4gIC8vIGlmIG5vIHJvd3MgcmVzdWx0IGZyb20gdGhlIGZpbHRlciBmbiB3ZSBmb3J3YXJkIHJldHVybiAxIGFzIGZhY3RvclxuICByZXR1cm4gZmlsdGVyQ291bnQgPT09IDAgPyAxIDogKGl0ZW1Db3VudCAtIGZpbHRlckNvdW50KSAvIGl0ZW1Db3VudDtcbn1cblxudHlwZSBBcmd1bWVudFZhbHVlID0gbnVtYmVyIHwgc3RyaW5nIHwgYm9vbGVhbiB8IHN0cmluZ1tdO1xuZXhwb3J0IHR5cGUgQ2xpQXJnc09iamVjdDxUIGV4dGVuZHMgb2JqZWN0ID0gUmVjb3JkPHN0cmluZywgQXJndW1lbnRWYWx1ZT4+ID1cbiAgVCBleHRlbmRzIG5ldmVyXG4gICAgPyBSZWNvcmQ8c3RyaW5nLCBBcmd1bWVudFZhbHVlIHwgdW5kZWZpbmVkPiB8IHsgXzogc3RyaW5nIH1cbiAgICA6IFQ7XG5cbi8qKlxuICogQ29udmVydHMgYW4gb2JqZWN0IHdpdGggZGlmZmVyZW50IHR5cGVzIG9mIHZhbHVlcyBpbnRvIGFuIGFycmF5IG9mIGNvbW1hbmQtbGluZSBhcmd1bWVudHMuXG4gKlxuICogQGV4YW1wbGVcbiAqIGNvbnN0IGFyZ3MgPSBvYmplY3RUb1Byb2Nlc3NBcmdzKHtcbiAqICAgXzogWydub2RlJywgJ2luZGV4LmpzJ10sIC8vIG5vZGUgaW5kZXguanNcbiAqICAgbmFtZTogJ0p1YW5pdGEnLCAvLyAtLW5hbWU9SnVhbml0YVxuICogICBmb3JtYXRzOiBbJ2pzb24nLCAnbWQnXSAvLyAtLWZvcm1hdD1qc29uIC0tZm9ybWF0PW1kXG4gKiB9KTtcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG9iamVjdFRvQ2xpQXJnczxcbiAgVCBleHRlbmRzIG9iamVjdCA9IFJlY29yZDxzdHJpbmcsIEFyZ3VtZW50VmFsdWU+LFxuPihwYXJhbXM/OiBDbGlBcmdzT2JqZWN0PFQ+KTogc3RyaW5nW10ge1xuICBpZiAoIXBhcmFtcykge1xuICAgIHJldHVybiBbXTtcbiAgfVxuXG4gIHJldHVybiBPYmplY3QuZW50cmllcyhwYXJhbXMpLmZsYXRNYXAoKFtrZXksIHZhbHVlXSkgPT4ge1xuICAgIC8vIHByb2Nlc3MvZmlsZS9zY3JpcHRcbiAgICBpZiAoa2V5ID09PSAnXycpIHtcbiAgICAgIHJldHVybiBBcnJheS5pc0FycmF5KHZhbHVlKSA/IHZhbHVlIDogW2Ake3ZhbHVlfWBdO1xuICAgIH1cbiAgICBjb25zdCBwcmVmaXggPSBrZXkubGVuZ3RoID09PSAxID8gJy0nIDogJy0tJztcbiAgICAvLyBcIi0qXCIgYXJndW1lbnRzIChzaG9ydGhhbmRzKVxuICAgIGlmIChBcnJheS5pc0FycmF5KHZhbHVlKSkge1xuICAgICAgcmV0dXJuIHZhbHVlLm1hcCh2ID0+IGAke3ByZWZpeH0ke2tleX09XCIke3Z9XCJgKTtcbiAgICB9XG4gICAgLy8gXCItLSpcIiBhcmd1bWVudHMgPT09PT09PT09PVxuXG4gICAgaWYgKEFycmF5LmlzQXJyYXkodmFsdWUpKSB7XG4gICAgICByZXR1cm4gdmFsdWUubWFwKHYgPT4gYCR7cHJlZml4fSR7a2V5fT1cIiR7dn1cImApO1xuICAgIH1cblxuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdvYmplY3QnKSB7XG4gICAgICByZXR1cm4gT2JqZWN0LmVudHJpZXModmFsdWUgYXMgUmVjb3JkPHN0cmluZywgQXJndW1lbnRWYWx1ZT4pLmZsYXRNYXAoXG4gICAgICAgIC8vIHRyYW5zZm9ybSBuZXN0ZWQgb2JqZWN0cyB0byB0aGUgZG90IG5vdGF0aW9uIGBrZXkuc3Via2V5YFxuICAgICAgICAoW2ssIHZdKSA9PiBvYmplY3RUb0NsaUFyZ3MoeyBbYCR7a2V5fS4ke2t9YF06IHYgfSksXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnKSB7XG4gICAgICByZXR1cm4gW2Ake3ByZWZpeH0ke2tleX09XCIke3ZhbHVlfVwiYF07XG4gICAgfVxuXG4gICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ251bWJlcicpIHtcbiAgICAgIHJldHVybiBbYCR7cHJlZml4fSR7a2V5fT0ke3ZhbHVlfWBdO1xuICAgIH1cblxuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdib29sZWFuJykge1xuICAgICAgcmV0dXJuIFtgJHtwcmVmaXh9JHt2YWx1ZSA/ICcnIDogJ25vLSd9JHtrZXl9YF07XG4gICAgfVxuXG4gICAgdGhyb3cgbmV3IEVycm9yKGBVbnN1cHBvcnRlZCB0eXBlICR7dHlwZW9mIHZhbHVlfSBmb3Iga2V5ICR7a2V5fWApO1xuICB9KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHRvVW5peFBhdGgocGF0aDogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIHBhdGgucmVwbGFjZSgvXFxcXC9nLCAnLycpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdG9Vbml4TmV3bGluZXModGV4dDogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIHBsYXRmb3JtKCkgPT09ICd3aW4zMicgPyB0ZXh0LnJlcGxhY2UoL1xcclxcbi9nLCAnXFxuJykgOiB0ZXh0O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZnJvbUpzb25MaW5lczxUID0gdW5rbm93bj4oanNvbkxpbmVzOiBzdHJpbmcpIHtcbiAgY29uc3QgdW5pZmllZE5ld0xpbmVzID0gdG9Vbml4TmV3bGluZXMoanNvbkxpbmVzKS50cmltKCk7XG4gIHJldHVybiBKU09OLnBhcnNlKGBbJHt1bmlmaWVkTmV3TGluZXMuc3BsaXQoJ1xcbicpLmpvaW4oJywnKX1dYCkgYXMgVDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHRvSnNvbkxpbmVzPFQ+KGpzb246IFRbXSkge1xuICByZXR1cm4ganNvbi5tYXAoaXRlbSA9PiBKU09OLnN0cmluZ2lmeShpdGVtKSkuam9pbignXFxuJyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjYXBpdGFsaXplPFQgZXh0ZW5kcyBzdHJpbmc+KHRleHQ6IFQpOiBDYXBpdGFsaXplPFQ+IHtcbiAgcmV0dXJuIGAke3RleHQuY2hhckF0KDApLnRvTG9jYWxlVXBwZXJDYXNlKCl9JHt0ZXh0LnNsaWNlKFxuICAgIDEsXG4gICl9YCBhcyBDYXBpdGFsaXplPFQ+O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdG9OdW1iZXJQcmVjaXNpb24oXG4gIHZhbHVlOiBudW1iZXIsXG4gIGRlY2ltYWxQbGFjZXM6IG51bWJlcixcbik6IG51bWJlciB7XG4gIHJldHVybiBOdW1iZXIoXG4gICAgYCR7TWF0aC5yb3VuZChcbiAgICAgIE51bWJlci5wYXJzZUZsb2F0KGAke3ZhbHVlfWUke2RlY2ltYWxQbGFjZXN9YCksXG4gICAgKX1lLSR7ZGVjaW1hbFBsYWNlc31gLFxuICApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdG9PcmRpbmFsKHZhbHVlOiBudW1iZXIpOiBzdHJpbmcge1xuICAvKiBlc2xpbnQtZGlzYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tbWFnaWMtbnVtYmVycyAqL1xuICBpZiAodmFsdWUgJSAxMCA9PT0gMSAmJiB2YWx1ZSAlIDEwMCAhPT0gMTEpIHtcbiAgICByZXR1cm4gYCR7dmFsdWV9c3RgO1xuICB9XG5cbiAgaWYgKHZhbHVlICUgMTAgPT09IDIgJiYgdmFsdWUgJSAxMDAgIT09IDEyKSB7XG4gICAgcmV0dXJuIGAke3ZhbHVlfW5kYDtcbiAgfVxuXG4gIGlmICh2YWx1ZSAlIDEwID09PSAzICYmIHZhbHVlICUgMTAwICE9PSAxMykge1xuICAgIHJldHVybiBgJHt2YWx1ZX1yZGA7XG4gIH1cbiAgLyogZXNsaW50LWVuYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tbWFnaWMtbnVtYmVycyAqL1xuXG4gIHJldHVybiBgJHt2YWx1ZX10aGA7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL21lcmdlLWNvbmZpZ3MudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYlwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL21lcmdlLWNvbmZpZ3MudHNcIjtpbXBvcnQgdHlwZSB7XG4gIENhdGVnb3J5Q29uZmlnLFxuICBDb3JlQ29uZmlnLFxuICBQZXJzaXN0Q29uZmlnLFxuICBQbHVnaW5Db25maWcsXG4gIFVwbG9hZENvbmZpZyxcbn0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBtZXJnZUNvbmZpZ3MoXG4gIGNvbmZpZzogQ29yZUNvbmZpZyxcbiAgLi4uY29uZmlnczogUGFydGlhbDxDb3JlQ29uZmlnPltdXG4pOiBQYXJ0aWFsPENvcmVDb25maWc+IHtcbiAgcmV0dXJuIGNvbmZpZ3MucmVkdWNlKFxuICAgIChhY2MsIG9iaikgPT4gKHtcbiAgICAgIC4uLmFjYyxcbiAgICAgIC4uLm1lcmdlQ2F0ZWdvcmllcyhhY2MuY2F0ZWdvcmllcywgb2JqLmNhdGVnb3JpZXMpLFxuICAgICAgLi4ubWVyZ2VQbHVnaW5zKGFjYy5wbHVnaW5zLCBvYmoucGx1Z2lucyksXG4gICAgICAuLi5tZXJnZVBlcnNpc3QoYWNjLnBlcnNpc3QsIG9iai5wZXJzaXN0KSxcbiAgICAgIC4uLm1lcmdlVXBsb2FkKGFjYy51cGxvYWQsIG9iai51cGxvYWQpLFxuICAgIH0pLFxuICAgIGNvbmZpZyxcbiAgKTtcbn1cblxuZnVuY3Rpb24gbWVyZ2VDYXRlZ29yaWVzKFxuICBhOiBDYXRlZ29yeUNvbmZpZ1tdIHwgdW5kZWZpbmVkLFxuICBiOiBDYXRlZ29yeUNvbmZpZ1tdIHwgdW5kZWZpbmVkLFxuKTogUGljazxDb3JlQ29uZmlnLCAnY2F0ZWdvcmllcyc+IHtcbiAgaWYgKCFhICYmICFiKSB7XG4gICAgcmV0dXJuIHt9O1xuICB9XG5cbiAgY29uc3QgbWVyZ2VkTWFwID0gbmV3IE1hcDxzdHJpbmcsIENhdGVnb3J5Q29uZmlnPigpO1xuXG4gIGNvbnN0IGFkZFRvTWFwID0gKGNhdGVnb3JpZXM6IENhdGVnb3J5Q29uZmlnW10pID0+IHtcbiAgICBjYXRlZ29yaWVzLmZvckVhY2gobmV3T2JqZWN0ID0+IHtcbiAgICAgIGlmIChtZXJnZWRNYXAuaGFzKG5ld09iamVjdC5zbHVnKSkge1xuICAgICAgICBjb25zdCBleGlzdGluZ09iamVjdDogQ2F0ZWdvcnlDb25maWcgfCB1bmRlZmluZWQgPSBtZXJnZWRNYXAuZ2V0KFxuICAgICAgICAgIG5ld09iamVjdC5zbHVnLFxuICAgICAgICApO1xuXG4gICAgICAgIG1lcmdlZE1hcC5zZXQobmV3T2JqZWN0LnNsdWcsIHtcbiAgICAgICAgICAuLi5leGlzdGluZ09iamVjdCxcbiAgICAgICAgICAuLi5uZXdPYmplY3QsXG5cbiAgICAgICAgICByZWZzOiBtZXJnZUJ5VW5pcXVlQ2F0ZWdvcnlSZWZDb21iaW5hdGlvbihcbiAgICAgICAgICAgIGV4aXN0aW5nT2JqZWN0Py5yZWZzLFxuICAgICAgICAgICAgbmV3T2JqZWN0LnJlZnMsXG4gICAgICAgICAgKSxcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBtZXJnZWRNYXAuc2V0KG5ld09iamVjdC5zbHVnLCBuZXdPYmplY3QpO1xuICAgICAgfVxuICAgIH0pO1xuICB9O1xuXG4gIGlmIChhKSB7XG4gICAgYWRkVG9NYXAoYSk7XG4gIH1cbiAgaWYgKGIpIHtcbiAgICBhZGRUb01hcChiKTtcbiAgfVxuXG4gIC8vIENvbnZlcnQgdGhlIG1hcCBiYWNrIHRvIGFuIGFycmF5XG4gIHJldHVybiB7IGNhdGVnb3JpZXM6IFsuLi5tZXJnZWRNYXAudmFsdWVzKCldIH07XG59XG5cbmZ1bmN0aW9uIG1lcmdlUGx1Z2lucyhcbiAgYTogUGx1Z2luQ29uZmlnW10gfCB1bmRlZmluZWQsXG4gIGI6IFBsdWdpbkNvbmZpZ1tdIHwgdW5kZWZpbmVkLFxuKTogUGljazxDb3JlQ29uZmlnLCAncGx1Z2lucyc+IHtcbiAgaWYgKCFhICYmICFiKSB7XG4gICAgcmV0dXJuIHsgcGx1Z2luczogW10gfTtcbiAgfVxuXG4gIGNvbnN0IG1lcmdlZE1hcCA9IG5ldyBNYXA8c3RyaW5nLCBQbHVnaW5Db25maWc+KCk7XG5cbiAgY29uc3QgYWRkVG9NYXAgPSAocGx1Z2luczogUGx1Z2luQ29uZmlnW10pID0+IHtcbiAgICBwbHVnaW5zLmZvckVhY2gobmV3T2JqZWN0ID0+IHtcbiAgICAgIG1lcmdlZE1hcC5zZXQobmV3T2JqZWN0LnNsdWcsIG5ld09iamVjdCk7XG4gICAgfSk7XG4gIH07XG5cbiAgaWYgKGEpIHtcbiAgICBhZGRUb01hcChhKTtcbiAgfVxuICBpZiAoYikge1xuICAgIGFkZFRvTWFwKGIpO1xuICB9XG5cbiAgcmV0dXJuIHsgcGx1Z2luczogWy4uLm1lcmdlZE1hcC52YWx1ZXMoKV0gfTtcbn1cblxuZnVuY3Rpb24gbWVyZ2VQZXJzaXN0KFxuICBhOiBQZXJzaXN0Q29uZmlnIHwgdW5kZWZpbmVkLFxuICBiOiBQZXJzaXN0Q29uZmlnIHwgdW5kZWZpbmVkLFxuKTogUGljazxDb3JlQ29uZmlnLCAncGVyc2lzdCc+IHtcbiAgaWYgKCFhICYmICFiKSB7XG4gICAgcmV0dXJuIHt9O1xuICB9XG5cbiAgaWYgKGEpIHtcbiAgICByZXR1cm4gYiA/IHsgcGVyc2lzdDogeyAuLi5hLCAuLi5iIH0gfSA6IHt9O1xuICB9IGVsc2Uge1xuICAgIHJldHVybiB7IHBlcnNpc3Q6IGIgfTtcbiAgfVxufVxuXG5mdW5jdGlvbiBtZXJnZUJ5VW5pcXVlQ2F0ZWdvcnlSZWZDb21iaW5hdGlvbjxcbiAgVCBleHRlbmRzIHsgc2x1Zzogc3RyaW5nOyB0eXBlOiBzdHJpbmc7IHBsdWdpbjogc3RyaW5nIH0sXG4+KGE6IFRbXSB8IHVuZGVmaW5lZCwgYjogVFtdIHwgdW5kZWZpbmVkKSB7XG4gIGNvbnN0IG1hcCA9IG5ldyBNYXA8c3RyaW5nLCBUPigpO1xuXG4gIGNvbnN0IGFkZFRvTWFwID0gKHJlZnM6IFRbXSkgPT4ge1xuICAgIHJlZnMuZm9yRWFjaChyZWYgPT4ge1xuICAgICAgY29uc3QgdW5pcXVlSWRlbnRpZmljYXRpb24gPSBgJHtyZWYudHlwZX06JHtyZWYucGx1Z2lufToke3JlZi5zbHVnfWA7XG4gICAgICBpZiAobWFwLmhhcyh1bmlxdWVJZGVudGlmaWNhdGlvbikpIHtcbiAgICAgICAgbWFwLnNldCh1bmlxdWVJZGVudGlmaWNhdGlvbiwge1xuICAgICAgICAgIC4uLm1hcC5nZXQodW5pcXVlSWRlbnRpZmljYXRpb24pLFxuICAgICAgICAgIC4uLnJlZixcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBtYXAuc2V0KHVuaXF1ZUlkZW50aWZpY2F0aW9uLCByZWYpO1xuICAgICAgfVxuICAgIH0pO1xuICB9O1xuXG4gIC8vIEFkZCBvYmplY3RzIGZyb20gYm90aCBhcnJheXMgdG8gdGhlIG1hcFxuICBpZiAoYSkge1xuICAgIGFkZFRvTWFwKGEpO1xuICB9XG4gIGlmIChiKSB7XG4gICAgYWRkVG9NYXAoYik7XG4gIH1cblxuICByZXR1cm4gWy4uLm1hcC52YWx1ZXMoKV07XG59XG5cbmZ1bmN0aW9uIG1lcmdlVXBsb2FkKFxuICBhOiBVcGxvYWRDb25maWcgfCB1bmRlZmluZWQsXG4gIGI6IFVwbG9hZENvbmZpZyB8IHVuZGVmaW5lZCxcbik6IFBpY2s8Q29yZUNvbmZpZywgJ3VwbG9hZCc+IHtcbiAgaWYgKCFhICYmICFiKSB7XG4gICAgcmV0dXJuIHt9O1xuICB9XG5cbiAgaWYgKGEpIHtcbiAgICByZXR1cm4gYiA/IHsgdXBsb2FkOiB7IC4uLmEsIC4uLmIgfSB9IDoge307XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIHsgdXBsb2FkOiBiIH07XG4gIH1cbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcHJvZ3Jlc3MudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYlwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3Byb2dyZXNzLnRzXCI7aW1wb3J0IHsgYmxhY2ssIGJvbGQsIGdyYXksIGdyZWVuIH0gZnJvbSAnYW5zaXMnO1xuaW1wb3J0IHsgdHlwZSBDdG9yT3B0aW9ucywgTXVsdGlQcm9ncmVzc0JhcnMgfSBmcm9tICdtdWx0aS1wcm9ncmVzcy1iYXJzJztcbmltcG9ydCB7IFRFUk1JTkFMX1dJRFRIIH0gZnJvbSAnLi9yZXBvcnRzL2NvbnN0YW50cy5qcyc7XG5cbnR5cGUgQmFyU3R5bGVzID0gJ2FjdGl2ZScgfCAnZG9uZScgfCAnaWRsZSc7XG50eXBlIFN0YXR1c1N0eWxlcyA9IFJlY29yZDxCYXJTdHlsZXMsIChzOiBzdHJpbmcpID0+IHN0cmluZz47XG5leHBvcnQgY29uc3QgYmFyU3R5bGVzOiBTdGF0dXNTdHlsZXMgPSB7XG4gIGFjdGl2ZTogKHM6IHN0cmluZykgPT4gZ3JlZW4ocyksXG4gIGRvbmU6IChzOiBzdHJpbmcpID0+IGdyYXkocyksXG4gIGlkbGU6IChzOiBzdHJpbmcpID0+IGdyYXkocyksXG59O1xuXG5leHBvcnQgY29uc3QgbWVzc2FnZVN0eWxlczogU3RhdHVzU3R5bGVzID0ge1xuICBhY3RpdmU6IChzOiBzdHJpbmcpID0+IGJsYWNrKHMpLFxuICBkb25lOiAoczogc3RyaW5nKSA9PiBib2xkLmdyZWVuKHMpLFxuICBpZGxlOiAoczogc3RyaW5nKSA9PiBncmF5KHMpLFxufTtcblxuZXhwb3J0IHR5cGUgUHJvZ3Jlc3NCYXIgPSB7XG4gIC8vIEBUT0RPIGZpbmQgYmV0dGVyIG5hbWluZ1xuICBpbmNyZW1lbnRJblN0ZXBzOiAobnVtU3RlcHM6IG51bWJlcikgPT4gdm9pZDtcbiAgdXBkYXRlVGl0bGU6ICh0aXRsZTogc3RyaW5nKSA9PiB2b2lkO1xuICBlbmRQcm9ncmVzczogKG1lc3NhZ2U/OiBzdHJpbmcpID0+IHZvaWQ7XG59O1xuXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZnVuY3Rpb25hbC9uby1sZXRcbmxldCBtcGI6IE11bHRpUHJvZ3Jlc3NCYXJzO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2luZ2xldG9uUHJvZ3Jlc3NCYXJzKFxuICBvcHRpb25zPzogUGFydGlhbDxDdG9yT3B0aW9ucz4sXG4pOiBNdWx0aVByb2dyZXNzQmFycyB7XG4gIGlmICghbXBiKSB7XG4gICAgbXBiID0gbmV3IE11bHRpUHJvZ3Jlc3NCYXJzKHtcbiAgICAgIHByb2dyZXNzV2lkdGg6IFRFUk1JTkFMX1dJRFRILFxuICAgICAgaW5pdE1lc3NhZ2U6ICcnLFxuICAgICAgYm9yZGVyOiB0cnVlLFxuICAgICAgLi4ub3B0aW9ucyxcbiAgICB9KTtcbiAgfVxuICByZXR1cm4gbXBiO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0UHJvZ3Jlc3NCYXIodGFza05hbWU6IHN0cmluZyk6IFByb2dyZXNzQmFyIHtcbiAgY29uc3QgdGFza3MgPSBnZXRTaW5nbGV0b25Qcm9ncmVzc0JhcnMoKTtcblxuICAvLyBJbml0aWFsaXplIHByb2dyZXNzIGJhciBpZiBub3Qgc2V0XG4gIHRhc2tzLmFkZFRhc2sodGFza05hbWUsIHtcbiAgICB0eXBlOiAncGVyY2VudGFnZScsXG4gICAgcGVyY2VudGFnZTogMCxcbiAgICBtZXNzYWdlOiAnJyxcbiAgICBiYXJUcmFuc2Zvcm1GbjogYmFyU3R5bGVzLmlkbGUsXG4gIH0pO1xuXG4gIHJldHVybiB7XG4gICAgaW5jcmVtZW50SW5TdGVwczogKG51bVBsdWdpbnM6IG51bWJlcikgPT4ge1xuICAgICAgdGFza3MuaW5jcmVtZW50VGFzayh0YXNrTmFtZSwge1xuICAgICAgICBwZXJjZW50YWdlOiAxIC8gbnVtUGx1Z2lucyxcbiAgICAgICAgYmFyVHJhbnNmb3JtRm46IGJhclN0eWxlcy5hY3RpdmUsXG4gICAgICB9KTtcbiAgICB9LFxuICAgIHVwZGF0ZVRpdGxlOiAodGl0bGU6IHN0cmluZykgPT4ge1xuICAgICAgdGFza3MudXBkYXRlVGFzayh0YXNrTmFtZSwge1xuICAgICAgICBtZXNzYWdlOiB0aXRsZSxcbiAgICAgICAgYmFyVHJhbnNmb3JtRm46IGJhclN0eWxlcy5hY3RpdmUsXG4gICAgICB9KTtcbiAgICB9LFxuICAgIGVuZFByb2dyZXNzOiAobWVzc2FnZSA9ICcnKSA9PiB7XG4gICAgICB0YXNrcy5kb25lKHRhc2tOYW1lLCB7XG4gICAgICAgIG1lc3NhZ2U6IG1lc3NhZ2VTdHlsZXMuZG9uZShtZXNzYWdlKSxcbiAgICAgICAgYmFyVHJhbnNmb3JtRm46IGJhclN0eWxlcy5kb25lLFxuICAgICAgfSk7XG4gICAgfSxcbiAgfTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0cy9nZW5lcmF0ZS1tZC1yZXBvcnQudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9yZXBvcnRzXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0cy9nZW5lcmF0ZS1tZC1yZXBvcnQudHNcIjtpbXBvcnQgeyB0eXBlIElubGluZVRleHQsIE1hcmtkb3duRG9jdW1lbnQsIG1kIH0gZnJvbSAnYnVpbGQtbWQnO1xuaW1wb3J0IHR5cGUgeyBBdWRpdFJlcG9ydCwgSXNzdWUsIFJlcG9ydCB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgZm9ybWF0RGF0ZSwgZm9ybWF0RHVyYXRpb24gfSBmcm9tICcuLi9mb3JtYXR0aW5nLmpzJztcbmltcG9ydCB7IEhJRVJBUkNIWSB9IGZyb20gJy4uL3RleHQtZm9ybWF0cy9pbmRleC5qcyc7XG5pbXBvcnQge1xuICBGT09URVJfUFJFRklYLFxuICBSRUFETUVfTElOSyxcbiAgUkVQT1JUX0hFQURMSU5FX1RFWFQsXG59IGZyb20gJy4vY29uc3RhbnRzLmpzJztcbmltcG9ydCB7XG4gIGZvcm1hdFNvdXJjZUxpbmUsXG4gIGxpbmtUb0xvY2FsU291cmNlRm9ySWRlLFxuICBtZXRhRGVzY3JpcHRpb24sXG4gIHRhYmxlU2VjdGlvbixcbn0gZnJvbSAnLi9mb3JtYXR0aW5nLmpzJztcbmltcG9ydCB7XG4gIGNhdGVnb3JpZXNEZXRhaWxzU2VjdGlvbixcbiAgY2F0ZWdvcmllc092ZXJ2aWV3U2VjdGlvbixcbn0gZnJvbSAnLi9nZW5lcmF0ZS1tZC1yZXBvcnQtY2F0ZWdveS1zZWN0aW9uLmpzJztcbmltcG9ydCB0eXBlIHsgTWRSZXBvcnRPcHRpb25zLCBTY29yZWRSZXBvcnQgfSBmcm9tICcuL3R5cGVzLmpzJztcbmltcG9ydCB7IGZvcm1hdFJlcG9ydFNjb3JlLCBzY29yZU1hcmtlciwgc2V2ZXJpdHlNYXJrZXIgfSBmcm9tICcuL3V0aWxzLmpzJztcblxuZXhwb3J0IGZ1bmN0aW9uIGF1ZGl0RGV0YWlsc0F1ZGl0VmFsdWUoe1xuICBzY29yZSxcbiAgdmFsdWUsXG4gIGRpc3BsYXlWYWx1ZSxcbn06IEF1ZGl0UmVwb3J0KTogSW5saW5lVGV4dCB7XG4gIHJldHVybiBtZGAke3Njb3JlTWFya2VyKHNjb3JlLCAnc3F1YXJlJyl9ICR7bWQuYm9sZChcbiAgICBTdHJpbmcoZGlzcGxheVZhbHVlID8/IHZhbHVlKSxcbiAgKX0gKHNjb3JlOiAke2Zvcm1hdFJlcG9ydFNjb3JlKHNjb3JlKX0pYDtcbn1cblxuZnVuY3Rpb24gaGFzQ2F0ZWdvcmllcyhcbiAgcmVwb3J0OiBTY29yZWRSZXBvcnQsXG4pOiByZXBvcnQgaXMgU2NvcmVkUmVwb3J0ICYgUmVxdWlyZWQ8UGljazxTY29yZWRSZXBvcnQsICdjYXRlZ29yaWVzJz4+IHtcbiAgcmV0dXJuICEhcmVwb3J0LmNhdGVnb3JpZXMgJiYgcmVwb3J0LmNhdGVnb3JpZXMubGVuZ3RoID4gMDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdlbmVyYXRlTWRSZXBvcnQoXG4gIHJlcG9ydDogU2NvcmVkUmVwb3J0LFxuICBvcHRpb25zPzogTWRSZXBvcnRPcHRpb25zLFxuKTogc3RyaW5nIHtcbiAgcmV0dXJuIG5ldyBNYXJrZG93bkRvY3VtZW50KClcbiAgICAuaGVhZGluZyhISUVSQVJDSFkubGV2ZWxfMSwgUkVQT1JUX0hFQURMSU5FX1RFWFQpXG4gICAgLiRjb25jYXQoXG4gICAgICAuLi4oaGFzQ2F0ZWdvcmllcyhyZXBvcnQpXG4gICAgICAgID8gW2NhdGVnb3JpZXNPdmVydmlld1NlY3Rpb24ocmVwb3J0KSwgY2F0ZWdvcmllc0RldGFpbHNTZWN0aW9uKHJlcG9ydCldXG4gICAgICAgIDogW10pLFxuICAgICAgYXVkaXRzU2VjdGlvbihyZXBvcnQsIG9wdGlvbnMpLFxuICAgICAgYWJvdXRTZWN0aW9uKHJlcG9ydCksXG4gICAgKVxuICAgIC5ydWxlKClcbiAgICAucGFyYWdyYXBoKG1kYCR7Rk9PVEVSX1BSRUZJWH0gJHttZC5saW5rKFJFQURNRV9MSU5LLCAnQ29kZSBQdXNoVXAnKX1gKVxuICAgIC50b1N0cmluZygpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYXVkaXREZXRhaWxzSXNzdWVzKFxuICBpc3N1ZXM6IElzc3VlW10gPSBbXSxcbiAgb3B0aW9ucz86IE1kUmVwb3J0T3B0aW9ucyxcbik6IE1hcmtkb3duRG9jdW1lbnQgfCBudWxsIHtcbiAgaWYgKGlzc3Vlcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICByZXR1cm4gbmV3IE1hcmtkb3duRG9jdW1lbnQoKS5oZWFkaW5nKEhJRVJBUkNIWS5sZXZlbF80LCAnSXNzdWVzJykudGFibGUoXG4gICAgW1xuICAgICAgeyBoZWFkaW5nOiAnU2V2ZXJpdHknLCBhbGlnbm1lbnQ6ICdjZW50ZXInIH0sXG4gICAgICB7IGhlYWRpbmc6ICdNZXNzYWdlJywgYWxpZ25tZW50OiAnbGVmdCcgfSxcbiAgICAgIHsgaGVhZGluZzogJ1NvdXJjZSBmaWxlJywgYWxpZ25tZW50OiAnbGVmdCcgfSxcbiAgICAgIHsgaGVhZGluZzogJ0xpbmUocyknLCBhbGlnbm1lbnQ6ICdjZW50ZXInIH0sXG4gICAgXSxcbiAgICBpc3N1ZXMubWFwKCh7IHNldmVyaXR5OiBsZXZlbCwgbWVzc2FnZSwgc291cmNlIH06IElzc3VlKSA9PiB7XG4gICAgICBjb25zdCBzZXZlcml0eSA9IG1kYCR7c2V2ZXJpdHlNYXJrZXIobGV2ZWwpfSAke21kLml0YWxpYyhsZXZlbCl9YDtcblxuICAgICAgaWYgKCFzb3VyY2UpIHtcbiAgICAgICAgcmV0dXJuIFtzZXZlcml0eSwgbWVzc2FnZV07XG4gICAgICB9XG4gICAgICBjb25zdCBmaWxlID0gbGlua1RvTG9jYWxTb3VyY2VGb3JJZGUoc291cmNlLCBvcHRpb25zKTtcbiAgICAgIGlmICghc291cmNlLnBvc2l0aW9uKSB7XG4gICAgICAgIHJldHVybiBbc2V2ZXJpdHksIG1lc3NhZ2UsIGZpbGVdO1xuICAgICAgfVxuICAgICAgY29uc3QgbGluZSA9IGZvcm1hdFNvdXJjZUxpbmUoc291cmNlLnBvc2l0aW9uKTtcbiAgICAgIHJldHVybiBbc2V2ZXJpdHksIG1lc3NhZ2UsIGZpbGUsIGxpbmVdO1xuICAgIH0pLFxuICApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYXVkaXREZXRhaWxzKFxuICBhdWRpdDogQXVkaXRSZXBvcnQsXG4gIG9wdGlvbnM/OiBNZFJlcG9ydE9wdGlvbnMsXG4pOiBNYXJrZG93bkRvY3VtZW50IHtcbiAgY29uc3QgeyB0YWJsZSwgaXNzdWVzID0gW10gfSA9IGF1ZGl0LmRldGFpbHMgPz8ge307XG4gIGNvbnN0IGRldGFpbHNWYWx1ZSA9IGF1ZGl0RGV0YWlsc0F1ZGl0VmFsdWUoYXVkaXQpO1xuXG4gIC8vIHVuZGVmaW5lZCBkZXRhaWxzIE9SIGVtcHR5IGRldGFpbHMgKHVuZGVmaW5lZCBpc3N1ZXMgT1IgZW1wdHkgaXNzdWVzIEFORCBlbXB0eSB0YWJsZSlcbiAgaWYgKGlzc3Vlcy5sZW5ndGggPT09IDAgJiYgIXRhYmxlPy5yb3dzLmxlbmd0aCkge1xuICAgIHJldHVybiBuZXcgTWFya2Rvd25Eb2N1bWVudCgpLnBhcmFncmFwaChkZXRhaWxzVmFsdWUpO1xuICB9XG5cbiAgY29uc3QgdGFibGVTZWN0aW9uQ29udGVudCA9IHRhYmxlICYmIHRhYmxlU2VjdGlvbih0YWJsZSk7XG4gIGNvbnN0IGlzc3Vlc1NlY3Rpb25Db250ZW50ID1cbiAgICBpc3N1ZXMubGVuZ3RoID4gMCAmJiBhdWRpdERldGFpbHNJc3N1ZXMoaXNzdWVzLCBvcHRpb25zKTtcblxuICByZXR1cm4gbmV3IE1hcmtkb3duRG9jdW1lbnQoKS5kZXRhaWxzKFxuICAgIGRldGFpbHNWYWx1ZSxcbiAgICBuZXcgTWFya2Rvd25Eb2N1bWVudCgpLiRjb25jYXQodGFibGVTZWN0aW9uQ29udGVudCwgaXNzdWVzU2VjdGlvbkNvbnRlbnQpLFxuICApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYXVkaXRzU2VjdGlvbihcbiAgeyBwbHVnaW5zIH06IFBpY2s8U2NvcmVkUmVwb3J0LCAncGx1Z2lucyc+LFxuICBvcHRpb25zPzogTWRSZXBvcnRPcHRpb25zLFxuKTogTWFya2Rvd25Eb2N1bWVudCB7XG4gIHJldHVybiBuZXcgTWFya2Rvd25Eb2N1bWVudCgpXG4gICAgLmhlYWRpbmcoSElFUkFSQ0hZLmxldmVsXzIsICdcdUQ4M0RcdURFRTFcdUZFMEYgQXVkaXRzJylcbiAgICAuJGZvcmVhY2goXG4gICAgICBwbHVnaW5zLmZsYXRNYXAocGx1Z2luID0+XG4gICAgICAgIHBsdWdpbi5hdWRpdHMubWFwKGF1ZGl0ID0+ICh7IC4uLmF1ZGl0LCBwbHVnaW4gfSkpLFxuICAgICAgKSxcbiAgICAgIChkb2MsIHsgcGx1Z2luLCAuLi5hdWRpdCB9KSA9PiB7XG4gICAgICAgIGNvbnN0IGF1ZGl0VGl0bGUgPSBgJHthdWRpdC50aXRsZX0gKCR7cGx1Z2luLnRpdGxlfSlgO1xuICAgICAgICBjb25zdCBkZXRhaWxzQ29udGVudCA9IGF1ZGl0RGV0YWlscyhhdWRpdCwgb3B0aW9ucyk7XG4gICAgICAgIGNvbnN0IGRlc2NyaXB0aW9uQ29udGVudCA9IG1ldGFEZXNjcmlwdGlvbihhdWRpdCk7XG5cbiAgICAgICAgcmV0dXJuIGRvY1xuICAgICAgICAgIC5oZWFkaW5nKEhJRVJBUkNIWS5sZXZlbF8zLCBhdWRpdFRpdGxlKVxuICAgICAgICAgIC4kY29uY2F0KGRldGFpbHNDb250ZW50KVxuICAgICAgICAgIC5wYXJhZ3JhcGgoZGVzY3JpcHRpb25Db250ZW50KTtcbiAgICAgIH0sXG4gICAgKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGFib3V0U2VjdGlvbihcbiAgcmVwb3J0OiBPbWl0PFNjb3JlZFJlcG9ydCwgJ3BhY2thZ2VOYW1lJz4sXG4pOiBNYXJrZG93bkRvY3VtZW50IHtcbiAgY29uc3QgeyBkYXRlLCBwbHVnaW5zIH0gPSByZXBvcnQ7XG4gIHJldHVybiBuZXcgTWFya2Rvd25Eb2N1bWVudCgpXG4gICAgLmhlYWRpbmcoSElFUkFSQ0hZLmxldmVsXzIsICdBYm91dCcpXG4gICAgLnBhcmFncmFwaChcbiAgICAgIG1kYFJlcG9ydCB3YXMgY3JlYXRlZCBieSAke21kLmxpbmsoXG4gICAgICAgIFJFQURNRV9MSU5LLFxuICAgICAgICAnQ29kZSBQdXNoVXAnLFxuICAgICAgKX0gb24gJHtmb3JtYXREYXRlKG5ldyBEYXRlKGRhdGUpKX0uYCxcbiAgICApXG4gICAgLnRhYmxlKC4uLnBsdWdpbk1ldGFUYWJsZSh7IHBsdWdpbnMgfSkpXG4gICAgLnRhYmxlKC4uLnJlcG9ydE1ldGFUYWJsZShyZXBvcnQpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBsdWdpbk1ldGFUYWJsZSh7XG4gIHBsdWdpbnMsXG59OiBQaWNrPFJlcG9ydCwgJ3BsdWdpbnMnPik6IFBhcmFtZXRlcnM8TWFya2Rvd25Eb2N1bWVudFsndGFibGUnXT4ge1xuICByZXR1cm4gW1xuICAgIFtcbiAgICAgIHsgaGVhZGluZzogJ1BsdWdpbicsIGFsaWdubWVudDogJ2xlZnQnIH0sXG4gICAgICB7IGhlYWRpbmc6ICdBdWRpdHMnLCBhbGlnbm1lbnQ6ICdjZW50ZXInIH0sXG4gICAgICB7IGhlYWRpbmc6ICdWZXJzaW9uJywgYWxpZ25tZW50OiAnY2VudGVyJyB9LFxuICAgICAgeyBoZWFkaW5nOiAnRHVyYXRpb24nLCBhbGlnbm1lbnQ6ICdyaWdodCcgfSxcbiAgICBdLFxuICAgIHBsdWdpbnMubWFwKCh7IHRpdGxlLCBhdWRpdHMsIHZlcnNpb24gPSAnJywgZHVyYXRpb24gfSkgPT4gW1xuICAgICAgdGl0bGUsXG4gICAgICBhdWRpdHMubGVuZ3RoLnRvU3RyaW5nKCksXG4gICAgICB2ZXJzaW9uICYmIG1kLmNvZGUodmVyc2lvbiksXG4gICAgICBmb3JtYXREdXJhdGlvbihkdXJhdGlvbiksXG4gICAgXSksXG4gIF07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZXBvcnRNZXRhVGFibGUoe1xuICBjb21taXQsXG4gIHZlcnNpb24sXG4gIGR1cmF0aW9uLFxuICBwbHVnaW5zLFxuICBjYXRlZ29yaWVzLFxufTogUGljazxcbiAgU2NvcmVkUmVwb3J0LFxuICAnZGF0ZScgfCAnZHVyYXRpb24nIHwgJ3ZlcnNpb24nIHwgJ2NvbW1pdCcgfCAncGx1Z2lucycgfCAnY2F0ZWdvcmllcydcbj4pOiBQYXJhbWV0ZXJzPE1hcmtkb3duRG9jdW1lbnRbJ3RhYmxlJ10+IHtcbiAgcmV0dXJuIFtcbiAgICBbXG4gICAgICB7IGhlYWRpbmc6ICdDb21taXQnLCBhbGlnbm1lbnQ6ICdsZWZ0JyB9LFxuICAgICAgeyBoZWFkaW5nOiAnVmVyc2lvbicsIGFsaWdubWVudDogJ2NlbnRlcicgfSxcbiAgICAgIHsgaGVhZGluZzogJ0R1cmF0aW9uJywgYWxpZ25tZW50OiAncmlnaHQnIH0sXG4gICAgICB7IGhlYWRpbmc6ICdQbHVnaW5zJywgYWxpZ25tZW50OiAnY2VudGVyJyB9LFxuICAgICAgeyBoZWFkaW5nOiAnQ2F0ZWdvcmllcycsIGFsaWdubWVudDogJ2NlbnRlcicgfSxcbiAgICAgIHsgaGVhZGluZzogJ0F1ZGl0cycsIGFsaWdubWVudDogJ2NlbnRlcicgfSxcbiAgICBdLFxuICAgIFtcbiAgICAgIFtcbiAgICAgICAgY29tbWl0ID8gYCR7Y29tbWl0Lm1lc3NhZ2V9ICgke2NvbW1pdC5oYXNofSlgIDogJ04vQScsXG4gICAgICAgIG1kLmNvZGUodmVyc2lvbiksXG4gICAgICAgIGZvcm1hdER1cmF0aW9uKGR1cmF0aW9uKSxcbiAgICAgICAgcGx1Z2lucy5sZW5ndGgudG9TdHJpbmcoKSxcbiAgICAgICAgKGNhdGVnb3JpZXM/Lmxlbmd0aCA/PyAwKS50b1N0cmluZygpLFxuICAgICAgICBwbHVnaW5zLnJlZHVjZSgoYWNjLCB7IGF1ZGl0cyB9KSA9PiBhY2MgKyBhdWRpdHMubGVuZ3RoLCAwKS50b1N0cmluZygpLFxuICAgICAgXSxcbiAgICBdLFxuICBdO1xufVxuIiwgImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9yZXBvcnRzL2Zvcm1hdHRpbmcudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9yZXBvcnRzXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0cy9mb3JtYXR0aW5nLnRzXCI7aW1wb3J0IHtcbiAgdHlwZSBIZWFkaW5nTGV2ZWwsXG4gIHR5cGUgSW5saW5lVGV4dCxcbiAgTWFya2Rvd25Eb2N1bWVudCxcbiAgbWQsXG59IGZyb20gJ2J1aWxkLW1kJztcbmltcG9ydCBwYXRoIGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgdHlwZSB7XG4gIEF1ZGl0UmVwb3J0LFxuICBTb3VyY2VGaWxlTG9jYXRpb24sXG4gIFRhYmxlLFxufSBmcm9tICdAY29kZS1wdXNodXAvbW9kZWxzJztcbmltcG9ydCB7IEhJRVJBUkNIWSB9IGZyb20gJy4uL3RleHQtZm9ybWF0cy9pbmRleC5qcyc7XG5pbXBvcnQge1xuICBjb2x1bW5zVG9TdHJpbmdBcnJheSxcbiAgZ2V0Q29sdW1uQWxpZ25tZW50cyxcbiAgcm93VG9TdHJpbmdBcnJheSxcbn0gZnJvbSAnLi4vdGV4dC1mb3JtYXRzL3RhYmxlLmpzJztcbmltcG9ydCB7XG4gIGdldEVudmlyb25tZW50VHlwZSxcbiAgZ2V0R2l0SHViQmFzZVVybCxcbiAgZ2V0R2l0TGFiQmFzZVVybCxcbn0gZnJvbSAnLi9lbnZpcm9ubWVudC10eXBlLmpzJztcbmltcG9ydCB0eXBlIHsgTWRSZXBvcnRPcHRpb25zIH0gZnJvbSAnLi90eXBlcy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiB0YWJsZVNlY3Rpb24oXG4gIHRhYmxlRGF0YTogVGFibGUsXG4gIG9wdGlvbnM/OiB7XG4gICAgbGV2ZWw/OiBIZWFkaW5nTGV2ZWw7XG4gIH0sXG4pOiBNYXJrZG93bkRvY3VtZW50IHwgbnVsbCB7XG4gIGlmICh0YWJsZURhdGEucm93cy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICBjb25zdCB7IGxldmVsID0gSElFUkFSQ0hZLmxldmVsXzQgfSA9IG9wdGlvbnMgPz8ge307XG4gIGNvbnN0IGNvbHVtbnMgPSBjb2x1bW5zVG9TdHJpbmdBcnJheSh0YWJsZURhdGEpO1xuICBjb25zdCBhbGlnbm1lbnRzID0gZ2V0Q29sdW1uQWxpZ25tZW50cyh0YWJsZURhdGEpO1xuICBjb25zdCByb3dzID0gcm93VG9TdHJpbmdBcnJheSh0YWJsZURhdGEpO1xuICByZXR1cm4gbmV3IE1hcmtkb3duRG9jdW1lbnQoKS5oZWFkaW5nKGxldmVsLCB0YWJsZURhdGEudGl0bGUpLnRhYmxlKFxuICAgIGNvbHVtbnMubWFwKChoZWFkaW5nLCBpKSA9PiB7XG4gICAgICBjb25zdCBhbGlnbm1lbnQgPSBhbGlnbm1lbnRzW2ldO1xuICAgICAgaWYgKGFsaWdubWVudCkge1xuICAgICAgICByZXR1cm4geyBoZWFkaW5nLCBhbGlnbm1lbnQgfTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBoZWFkaW5nO1xuICAgIH0pLFxuICAgIHJvd3MsXG4gICk7XG59XG5cbi8vIEBUT0RPIGV4dHJhY3QgYFBpY2s8QXVkaXRSZXBvcnQsICdkb2NzVXJsJyB8ICdkZXNjcmlwdGlvbic+YCB0byBhIHJldXNhYmxlIHNjaGVtYSBhbmQgdHlwZVxuZXhwb3J0IGZ1bmN0aW9uIG1ldGFEZXNjcmlwdGlvbihcbiAgYXVkaXQ6IFBpY2s8QXVkaXRSZXBvcnQsICdkb2NzVXJsJyB8ICdkZXNjcmlwdGlvbic+LFxuKTogSW5saW5lVGV4dCB7XG4gIGNvbnN0IGRvY3NVcmwgPSBhdWRpdC5kb2NzVXJsO1xuICBjb25zdCBkZXNjcmlwdGlvbiA9IGF1ZGl0LmRlc2NyaXB0aW9uPy50cmltKCk7XG4gIGlmIChkb2NzVXJsKSB7XG4gICAgY29uc3QgZG9jc0xpbmsgPSBtZC5saW5rKGRvY3NVcmwsICdcdUQ4M0RcdURDRDYgRG9jcycpO1xuICAgIGlmICghZGVzY3JpcHRpb24pIHtcbiAgICAgIHJldHVybiBkb2NzTGluaztcbiAgICB9XG4gICAgY29uc3QgcGFyc2VkRGVzY3JpcHRpb24gPSBkZXNjcmlwdGlvbi5lbmRzV2l0aCgnYGBgJylcbiAgICAgID8gYCR7ZGVzY3JpcHRpb259XFxuXFxuYFxuICAgICAgOiBgJHtkZXNjcmlwdGlvbn0gYDtcbiAgICByZXR1cm4gbWRgJHtwYXJzZWREZXNjcmlwdGlvbn0ke2RvY3NMaW5rfWA7XG4gIH1cbiAgaWYgKGRlc2NyaXB0aW9uICYmIGRlc2NyaXB0aW9uLnRyaW0oKS5sZW5ndGggPiAwKSB7XG4gICAgcmV0dXJuIGRlc2NyaXB0aW9uO1xuICB9XG4gIHJldHVybiAnJztcbn1cblxuLyoqXG4gKiBMaW5rIHRvIGxvY2FsIHNvdXJjZSBmb3IgSURFXG4gKiBAcGFyYW0gc291cmNlXG4gKiBAcGFyYW0gcmVwb3J0TG9jYXRpb25cbiAqXG4gKiBAZXhhbXBsZVxuICogbGlua1RvTG9jYWxTb3VyY2VJbklkZSh7IGZpbGU6ICdzcmMvaW5kZXgudHMnIH0sIHsgb3V0cHV0RGlyOiAnLmNvZGUtcHVzaHVwJyB9KSAvLyBbYHNyYy9pbmRleC50c2BdKC4uL3NyYy9pbmRleC50cylcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGxpbmtUb0xvY2FsU291cmNlRm9ySWRlKFxuICBzb3VyY2U6IFNvdXJjZUZpbGVMb2NhdGlvbixcbiAgb3B0aW9ucz86IFBpY2s8TWRSZXBvcnRPcHRpb25zLCAnb3V0cHV0RGlyJz4sXG4pOiBJbmxpbmVUZXh0IHtcbiAgY29uc3QgeyBmaWxlLCBwb3NpdGlvbiB9ID0gc291cmNlO1xuICBjb25zdCB7IG91dHB1dERpciB9ID0gb3B0aW9ucyA/PyB7fTtcblxuICAvLyBOT1QgbGlua2FibGVcbiAgaWYgKCFvdXRwdXREaXIpIHtcbiAgICByZXR1cm4gbWQuY29kZShmaWxlKTtcbiAgfVxuXG4gIHJldHVybiBtZC5saW5rKGZvcm1hdEZpbGVMaW5rKGZpbGUsIHBvc2l0aW9uLCBvdXRwdXREaXIpLCBtZC5jb2RlKGZpbGUpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdFNvdXJjZUxpbmUoXG4gIHBvc2l0aW9uOiBTb3VyY2VGaWxlTG9jYXRpb25bJ3Bvc2l0aW9uJ10sXG4pOiBzdHJpbmcge1xuICBpZiAoIXBvc2l0aW9uKSB7XG4gICAgcmV0dXJuICcnO1xuICB9XG4gIGNvbnN0IHsgc3RhcnRMaW5lLCBlbmRMaW5lIH0gPSBwb3NpdGlvbjtcbiAgcmV0dXJuIGVuZExpbmUgJiYgc3RhcnRMaW5lICE9PSBlbmRMaW5lXG4gICAgPyBgJHtzdGFydExpbmV9LSR7ZW5kTGluZX1gXG4gICAgOiBgJHtzdGFydExpbmV9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdEdpdEh1YkxpbmsoXG4gIGZpbGU6IHN0cmluZyxcbiAgcG9zaXRpb246IFNvdXJjZUZpbGVMb2NhdGlvblsncG9zaXRpb24nXSxcbik6IHN0cmluZyB7XG4gIGNvbnN0IGJhc2VVcmwgPSBnZXRHaXRIdWJCYXNlVXJsKCk7XG4gIGlmICghcG9zaXRpb24pIHtcbiAgICByZXR1cm4gYCR7YmFzZVVybH0vJHtmaWxlfWA7XG4gIH1cbiAgY29uc3QgeyBzdGFydExpbmUsIGVuZExpbmUsIHN0YXJ0Q29sdW1uLCBlbmRDb2x1bW4gfSA9IHBvc2l0aW9uO1xuICBjb25zdCBzdGFydCA9IHN0YXJ0Q29sdW1uID8gYEwke3N0YXJ0TGluZX1DJHtzdGFydENvbHVtbn1gIDogYEwke3N0YXJ0TGluZX1gO1xuICBjb25zdCBlbmQgPSBlbmRMaW5lXG4gICAgPyBlbmRDb2x1bW5cbiAgICAgID8gYEwke2VuZExpbmV9QyR7ZW5kQ29sdW1ufWBcbiAgICAgIDogYEwke2VuZExpbmV9YFxuICAgIDogJyc7XG4gIGNvbnN0IGxpbmVSYW5nZSA9IGVuZCAmJiBzdGFydCAhPT0gZW5kID8gYCR7c3RhcnR9LSR7ZW5kfWAgOiBzdGFydDtcbiAgcmV0dXJuIGAke2Jhc2VVcmx9LyR7ZmlsZX0jJHtsaW5lUmFuZ2V9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdEdpdExhYkxpbmsoXG4gIGZpbGU6IHN0cmluZyxcbiAgcG9zaXRpb246IFNvdXJjZUZpbGVMb2NhdGlvblsncG9zaXRpb24nXSxcbik6IHN0cmluZyB7XG4gIGNvbnN0IGJhc2VVcmwgPSBnZXRHaXRMYWJCYXNlVXJsKCk7XG4gIGlmICghcG9zaXRpb24pIHtcbiAgICByZXR1cm4gYCR7YmFzZVVybH0vJHtmaWxlfWA7XG4gIH1cbiAgY29uc3QgeyBzdGFydExpbmUsIGVuZExpbmUgfSA9IHBvc2l0aW9uO1xuICBjb25zdCBsaW5lUmFuZ2UgPVxuICAgIGVuZExpbmUgJiYgc3RhcnRMaW5lICE9PSBlbmRMaW5lID8gYCR7c3RhcnRMaW5lfS0ke2VuZExpbmV9YCA6IHN0YXJ0TGluZTtcbiAgcmV0dXJuIGAke2Jhc2VVcmx9LyR7ZmlsZX0jTCR7bGluZVJhbmdlfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRGaWxlTGluayhcbiAgZmlsZTogc3RyaW5nLFxuICBwb3NpdGlvbjogU291cmNlRmlsZUxvY2F0aW9uWydwb3NpdGlvbiddLFxuICBvdXRwdXREaXI6IHN0cmluZyxcbik6IHN0cmluZyB7XG4gIGNvbnN0IHJlbGF0aXZlUGF0aCA9IHBhdGgucG9zaXgucmVsYXRpdmUob3V0cHV0RGlyLCBmaWxlKTtcbiAgY29uc3QgZW52ID0gZ2V0RW52aXJvbm1lbnRUeXBlKCk7XG5cbiAgc3dpdGNoIChlbnYpIHtcbiAgICBjYXNlICd2c2NvZGUnOlxuICAgICAgcmV0dXJuIHBvc2l0aW9uID8gYCR7cmVsYXRpdmVQYXRofSNMJHtwb3NpdGlvbi5zdGFydExpbmV9YCA6IHJlbGF0aXZlUGF0aDtcbiAgICBjYXNlICdnaXRodWInOlxuICAgICAgcmV0dXJuIGZvcm1hdEdpdEh1YkxpbmsoZmlsZSwgcG9zaXRpb24pO1xuICAgIGNhc2UgJ2dpdGxhYic6XG4gICAgICByZXR1cm4gZm9ybWF0R2l0TGFiTGluayhmaWxlLCBwb3NpdGlvbik7XG4gICAgZGVmYXVsdDpcbiAgICAgIHJldHVybiByZWxhdGl2ZVBhdGg7XG4gIH1cbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0cy9nZW5lcmF0ZS1tZC1yZXBvcnQtY2F0ZWdveS1zZWN0aW9uLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0c1wiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3JlcG9ydHMvZ2VuZXJhdGUtbWQtcmVwb3J0LWNhdGVnb3ktc2VjdGlvbi50c1wiO2ltcG9ydCB7IHR5cGUgSW5saW5lVGV4dCwgTWFya2Rvd25Eb2N1bWVudCwgbWQgfSBmcm9tICdidWlsZC1tZCc7XG5pbXBvcnQgdHlwZSB7IEF1ZGl0UmVwb3J0IH0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5pbXBvcnQgeyBzbHVnaWZ5IH0gZnJvbSAnLi4vZm9ybWF0dGluZy5qcyc7XG5pbXBvcnQgeyBISUVSQVJDSFkgfSBmcm9tICcuLi90ZXh0LWZvcm1hdHMvaW5kZXguanMnO1xuaW1wb3J0IHsgbWV0YURlc2NyaXB0aW9uIH0gZnJvbSAnLi9mb3JtYXR0aW5nLmpzJztcbmltcG9ydCB7IGdldFNvcnRhYmxlQXVkaXRCeVJlZiwgZ2V0U29ydGFibGVHcm91cEJ5UmVmIH0gZnJvbSAnLi9zb3J0aW5nLmpzJztcbmltcG9ydCB0eXBlIHsgU2NvcmVkR3JvdXAsIFNjb3JlZFJlcG9ydCB9IGZyb20gJy4vdHlwZXMuanMnO1xuaW1wb3J0IHtcbiAgY291bnRDYXRlZ29yeUF1ZGl0cyxcbiAgZm9ybWF0UmVwb3J0U2NvcmUsXG4gIGdldFBsdWdpbk5hbWVGcm9tU2x1ZyxcbiAgc2NvcmVNYXJrZXIsXG4gIHRhcmdldFNjb3JlSWNvbixcbn0gZnJvbSAnLi91dGlscy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBjYXRlZ29yaWVzT3ZlcnZpZXdTZWN0aW9uKFxuICByZXBvcnQ6IFJlcXVpcmVkPFBpY2s8U2NvcmVkUmVwb3J0LCAncGx1Z2lucycgfCAnY2F0ZWdvcmllcyc+Pixcbik6IE1hcmtkb3duRG9jdW1lbnQge1xuICBjb25zdCB7IGNhdGVnb3JpZXMsIHBsdWdpbnMgfSA9IHJlcG9ydDtcbiAgcmV0dXJuIG5ldyBNYXJrZG93bkRvY3VtZW50KCkudGFibGUoXG4gICAgW1xuICAgICAgeyBoZWFkaW5nOiAnXHVEODNDXHVERkY3IENhdGVnb3J5JywgYWxpZ25tZW50OiAnbGVmdCcgfSxcbiAgICAgIHsgaGVhZGluZzogJ1x1MkI1MCBTY29yZScsIGFsaWdubWVudDogJ2NlbnRlcicgfSxcbiAgICAgIHsgaGVhZGluZzogJ1x1RDgzRFx1REVFMSBBdWRpdHMnLCBhbGlnbm1lbnQ6ICdjZW50ZXInIH0sXG4gICAgXSxcbiAgICBjYXRlZ29yaWVzLm1hcCgoeyB0aXRsZSwgcmVmcywgc2NvcmUsIGlzQmluYXJ5IH0pID0+IFtcbiAgICAgIC8vIEBUT0RPIHJlZmFjdG9yIGBpc0JpbmFyeTogYm9vbGVhbmAgdG8gYHRhcmdldFNjb3JlOiBudW1iZXJgICM3MTNcbiAgICAgIC8vIFRoZSBoZWFkaW5nIFwiSURcIiBpcyBpbmZlcnJlZCBmcm9tIHRoZSBoZWFkaW5nIHRleHQgaW4gTWFya2Rvd24uXG4gICAgICBtZC5saW5rKGAjJHtzbHVnaWZ5KHRpdGxlKX1gLCB0aXRsZSksXG4gICAgICBtZGAke3Njb3JlTWFya2VyKHNjb3JlKX0gJHttZC5ib2xkKFxuICAgICAgICBmb3JtYXRSZXBvcnRTY29yZShzY29yZSksXG4gICAgICApfSR7YmluYXJ5SWNvblN1ZmZpeChzY29yZSwgaXNCaW5hcnkpfWAsXG4gICAgICBjb3VudENhdGVnb3J5QXVkaXRzKHJlZnMsIHBsdWdpbnMpLnRvU3RyaW5nKCksXG4gICAgXSksXG4gICk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjYXRlZ29yaWVzRGV0YWlsc1NlY3Rpb24oXG4gIHJlcG9ydDogUmVxdWlyZWQ8UGljazxTY29yZWRSZXBvcnQsICdwbHVnaW5zJyB8ICdjYXRlZ29yaWVzJz4+LFxuKTogTWFya2Rvd25Eb2N1bWVudCB7XG4gIGNvbnN0IHsgY2F0ZWdvcmllcywgcGx1Z2lucyB9ID0gcmVwb3J0O1xuXG4gIHJldHVybiBuZXcgTWFya2Rvd25Eb2N1bWVudCgpXG4gICAgLmhlYWRpbmcoSElFUkFSQ0hZLmxldmVsXzIsICdcdUQ4M0NcdURGRjcgQ2F0ZWdvcmllcycpXG4gICAgLiRmb3JlYWNoKGNhdGVnb3JpZXMsIChkb2MsIGNhdGVnb3J5KSA9PlxuICAgICAgZG9jXG4gICAgICAgIC5oZWFkaW5nKEhJRVJBUkNIWS5sZXZlbF8zLCBjYXRlZ29yeS50aXRsZSlcbiAgICAgICAgLnBhcmFncmFwaChtZXRhRGVzY3JpcHRpb24oY2F0ZWdvcnkpKVxuICAgICAgICAucGFyYWdyYXBoKFxuICAgICAgICAgIG1kYCR7c2NvcmVNYXJrZXIoY2F0ZWdvcnkuc2NvcmUpfSBTY29yZTogJHttZC5ib2xkKFxuICAgICAgICAgICAgZm9ybWF0UmVwb3J0U2NvcmUoY2F0ZWdvcnkuc2NvcmUpLFxuICAgICAgICAgICl9JHtiaW5hcnlJY29uU3VmZml4KGNhdGVnb3J5LnNjb3JlLCBjYXRlZ29yeS5pc0JpbmFyeSl9YCxcbiAgICAgICAgKVxuICAgICAgICAubGlzdChcbiAgICAgICAgICBjYXRlZ29yeS5yZWZzLm1hcChyZWYgPT4ge1xuICAgICAgICAgICAgLy8gQWRkIGdyb3VwIGRldGFpbHNcbiAgICAgICAgICAgIGlmIChyZWYudHlwZSA9PT0gJ2dyb3VwJykge1xuICAgICAgICAgICAgICBjb25zdCBncm91cCA9IGdldFNvcnRhYmxlR3JvdXBCeVJlZihyZWYsIHBsdWdpbnMpO1xuICAgICAgICAgICAgICBjb25zdCBncm91cEF1ZGl0cyA9IGdyb3VwLnJlZnMubWFwKGdyb3VwUmVmID0+XG4gICAgICAgICAgICAgICAgZ2V0U29ydGFibGVBdWRpdEJ5UmVmKFxuICAgICAgICAgICAgICAgICAgeyAuLi5ncm91cFJlZiwgcGx1Z2luOiBncm91cC5wbHVnaW4sIHR5cGU6ICdhdWRpdCcgfSxcbiAgICAgICAgICAgICAgICAgIHBsdWdpbnMsXG4gICAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgY29uc3QgcGx1Z2luVGl0bGUgPSBnZXRQbHVnaW5OYW1lRnJvbVNsdWcocmVmLnBsdWdpbiwgcGx1Z2lucyk7XG4gICAgICAgICAgICAgIHJldHVybiBjYXRlZ29yeUdyb3VwSXRlbShncm91cCwgZ3JvdXBBdWRpdHMsIHBsdWdpblRpdGxlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIEFkZCBhdWRpdCBkZXRhaWxzXG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgY29uc3QgYXVkaXQgPSBnZXRTb3J0YWJsZUF1ZGl0QnlSZWYocmVmLCBwbHVnaW5zKTtcbiAgICAgICAgICAgICAgY29uc3QgcGx1Z2luVGl0bGUgPSBnZXRQbHVnaW5OYW1lRnJvbVNsdWcocmVmLnBsdWdpbiwgcGx1Z2lucyk7XG4gICAgICAgICAgICAgIHJldHVybiBjYXRlZ29yeVJlZihhdWRpdCwgcGx1Z2luVGl0bGUpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pLFxuICAgICAgICApLFxuICAgICk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjYXRlZ29yeVJlZihcbiAgeyB0aXRsZSwgc2NvcmUsIHZhbHVlLCBkaXNwbGF5VmFsdWUgfTogQXVkaXRSZXBvcnQsXG4gIHBsdWdpblRpdGxlOiBzdHJpbmcsXG4pOiBJbmxpbmVUZXh0IHtcbiAgY29uc3QgYXVkaXRUaXRsZUFzTGluayA9IG1kLmxpbmsoXG4gICAgYCMke3NsdWdpZnkodGl0bGUpfS0ke3NsdWdpZnkocGx1Z2luVGl0bGUpfWAsXG4gICAgdGl0bGUsXG4gICk7XG4gIGNvbnN0IG1hcmtlciA9IHNjb3JlTWFya2VyKHNjb3JlLCAnc3F1YXJlJyk7XG4gIHJldHVybiBtZGAke21hcmtlcn0gJHthdWRpdFRpdGxlQXNMaW5rfSAoJHttZC5pdGFsaWMoXG4gICAgcGx1Z2luVGl0bGUsXG4gICl9KSAtICR7bWQuYm9sZCgoZGlzcGxheVZhbHVlIHx8IHZhbHVlKS50b1N0cmluZygpKX1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY2F0ZWdvcnlHcm91cEl0ZW0oXG4gIHsgc2NvcmUgPSAwLCB0aXRsZSB9OiBTY29yZWRHcm91cCxcbiAgZ3JvdXBBdWRpdHM6IEF1ZGl0UmVwb3J0W10sXG4gIHBsdWdpblRpdGxlOiBzdHJpbmcsXG4pOiBJbmxpbmVUZXh0IHtcbiAgY29uc3QgZ3JvdXBUaXRsZSA9IG1kYCR7c2NvcmVNYXJrZXIoc2NvcmUpfSAke3RpdGxlfSAoJHttZC5pdGFsaWMoXG4gICAgcGx1Z2luVGl0bGUsXG4gICl9KWA7XG5cbiAgY29uc3QgYXVkaXRzTGlzdCA9IG1kLmxpc3QoXG4gICAgZ3JvdXBBdWRpdHMubWFwKFxuICAgICAgKHsgdGl0bGU6IGF1ZGl0VGl0bGUsIHNjb3JlOiBhdWRpdFNjb3JlLCB2YWx1ZSwgZGlzcGxheVZhbHVlIH0pID0+IHtcbiAgICAgICAgY29uc3QgYXVkaXRUaXRsZUxpbmsgPSBtZC5saW5rKFxuICAgICAgICAgIGAjJHtzbHVnaWZ5KGF1ZGl0VGl0bGUpfS0ke3NsdWdpZnkocGx1Z2luVGl0bGUpfWAsXG4gICAgICAgICAgYXVkaXRUaXRsZSxcbiAgICAgICAgKTtcbiAgICAgICAgY29uc3QgbWFya2VyID0gc2NvcmVNYXJrZXIoYXVkaXRTY29yZSwgJ3NxdWFyZScpO1xuICAgICAgICByZXR1cm4gbWRgJHttYXJrZXJ9ICR7YXVkaXRUaXRsZUxpbmt9IC0gJHttZC5ib2xkKFxuICAgICAgICAgIFN0cmluZyhkaXNwbGF5VmFsdWUgPz8gdmFsdWUpLFxuICAgICAgICApfWA7XG4gICAgICB9LFxuICAgICksXG4gICk7XG5cbiAgcmV0dXJuIG1kYCR7Z3JvdXBUaXRsZX0ke2F1ZGl0c0xpc3R9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGJpbmFyeUljb25TdWZmaXgoXG4gIHNjb3JlOiBudW1iZXIsXG4gIGlzQmluYXJ5OiBib29sZWFuIHwgdW5kZWZpbmVkLFxuKTogc3RyaW5nIHtcbiAgLy8gQFRPRE8gcmVmYWN0b3IgYGlzQmluYXJ5OiBib29sZWFuYCB0byBgdGFyZ2V0U2NvcmU6IG51bWJlcmAgIzcxM1xuICByZXR1cm4gdGFyZ2V0U2NvcmVJY29uKHNjb3JlLCBpc0JpbmFyeSA/IDEgOiB1bmRlZmluZWQsIHsgcHJlZml4OiAnICcgfSk7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3JlcG9ydHMvZ2VuZXJhdGUtbWQtcmVwb3J0cy1kaWZmLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0c1wiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3JlcG9ydHMvZ2VuZXJhdGUtbWQtcmVwb3J0cy1kaWZmLnRzXCI7aW1wb3J0IHtcbiAgdHlwZSBIZWFkaW5nTGV2ZWwsXG4gIE1hcmtkb3duRG9jdW1lbnQsXG4gIHR5cGUgVGFibGVDb2x1bW5PYmplY3QsXG4gIHR5cGUgVGFibGVSb3csXG4gIG1kLFxufSBmcm9tICdidWlsZC1tZCc7XG5pbXBvcnQgdHlwZSB7IFJlcG9ydHNEaWZmIH0gZnJvbSAnQGNvZGUtcHVzaHVwL21vZGVscyc7XG5pbXBvcnQgeyBISUVSQVJDSFkgfSBmcm9tICcuLi90ZXh0LWZvcm1hdHMvaW5kZXguanMnO1xuaW1wb3J0IHsgdG9BcnJheSB9IGZyb20gJy4uL3RyYW5zZm9ybS5qcyc7XG5pbXBvcnQgdHlwZSB7IFdpdGhSZXF1aXJlZCB9IGZyb20gJy4uL3R5cGVzLmpzJztcbmltcG9ydCB7XG4gIGNoYW5nZXNUb0RpZmZPdXRjb21lcyxcbiAgY29tcGFyZURpZmZzQnksXG4gIGNyZWF0ZUdyb3Vwc09yQXVkaXRzRGV0YWlscyxcbiAgZm9ybWF0UG9ydGFsTGluayxcbiAgZm9ybWF0UmVwb3J0T3V0Y29tZSxcbiAgZm9ybWF0VGl0bGUsXG4gIGdldERpZmZDaGFuZ2VzLFxuICBtZXJnZURpZmZPdXRjb21lcyxcbiAgc29ydENoYW5nZXMsXG4gIHN1bW1hcml6ZURpZmZPdXRjb21lcyxcbiAgc3VtbWFyaXplVW5jaGFuZ2VkLFxufSBmcm9tICcuL2dlbmVyYXRlLW1kLXJlcG9ydHMtZGlmZi11dGlscy5qcyc7XG5pbXBvcnQgdHlwZSB7IERpZmZPdXRjb21lIH0gZnJvbSAnLi90eXBlcy5qcyc7XG5pbXBvcnQge1xuICBmb3JtYXRTY29yZUNoYW5nZSxcbiAgZm9ybWF0U2NvcmVXaXRoQ29sb3IsXG4gIGZvcm1hdFZhbHVlQ2hhbmdlLFxuICBzY29yZU1hcmtlcixcbn0gZnJvbSAnLi91dGlscy5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZW5lcmF0ZU1kUmVwb3J0c0RpZmYoZGlmZjogUmVwb3J0c0RpZmYpOiBzdHJpbmcge1xuICByZXR1cm4gbmV3IE1hcmtkb3duRG9jdW1lbnQoKVxuICAgIC4kY29uY2F0KFxuICAgICAgY3JlYXRlRGlmZkhlYWRlclNlY3Rpb24oZGlmZiksXG4gICAgICBjcmVhdGVEaWZmQ2F0ZWdvcmllc1NlY3Rpb24oZGlmZiksXG4gICAgICBjcmVhdGVEaWZmRGV0YWlsc1NlY3Rpb24oZGlmZiksXG4gICAgKVxuICAgIC50b1N0cmluZygpO1xufVxuXG5leHBvcnQgdHlwZSBMYWJlbGVkRGlmZiA9IFdpdGhSZXF1aXJlZDxSZXBvcnRzRGlmZiwgJ2xhYmVsJz47XG5cbmV4cG9ydCBmdW5jdGlvbiBnZW5lcmF0ZU1kUmVwb3J0c0RpZmZGb3JNb25vcmVwbyhkaWZmczogTGFiZWxlZERpZmZbXSk6IHN0cmluZyB7XG4gIGNvbnN0IGRpZmZzV2l0aE91dGNvbWVzID0gZGlmZnNcbiAgICAubWFwKGRpZmYgPT4gKHtcbiAgICAgIC4uLmRpZmYsXG4gICAgICBvdXRjb21lOiBtZXJnZURpZmZPdXRjb21lcyhjaGFuZ2VzVG9EaWZmT3V0Y29tZXMoZ2V0RGlmZkNoYW5nZXMoZGlmZikpKSxcbiAgICB9KSlcbiAgICAuc29ydChcbiAgICAgIChhLCBiKSA9PlxuICAgICAgICBjb21wYXJlRGlmZnNCeSgnY2F0ZWdvcmllcycsIGEsIGIpIHx8XG4gICAgICAgIGNvbXBhcmVEaWZmc0J5KCdncm91cHMnLCBhLCBiKSB8fFxuICAgICAgICBjb21wYXJlRGlmZnNCeSgnYXVkaXRzJywgYSwgYikgfHxcbiAgICAgICAgYS5sYWJlbC5sb2NhbGVDb21wYXJlKGIubGFiZWwpLFxuICAgICk7XG4gIGNvbnN0IHVuY2hhbmdlZCA9IGRpZmZzV2l0aE91dGNvbWVzLmZpbHRlcihcbiAgICAoeyBvdXRjb21lIH0pID0+IG91dGNvbWUgPT09ICd1bmNoYW5nZWQnLFxuICApO1xuICBjb25zdCBjaGFuZ2VkID0gZGlmZnNXaXRoT3V0Y29tZXMuZmlsdGVyKGRpZmYgPT4gIXVuY2hhbmdlZC5pbmNsdWRlcyhkaWZmKSk7XG5cbiAgcmV0dXJuIG5ldyBNYXJrZG93bkRvY3VtZW50KClcbiAgICAuJGNvbmNhdChcbiAgICAgIGNyZWF0ZURpZmZIZWFkZXJTZWN0aW9uKGRpZmZzKSxcbiAgICAgIC4uLmNoYW5nZWQubWFwKGNyZWF0ZURpZmZQcm9qZWN0U2VjdGlvbiksXG4gICAgKVxuICAgIC4kaWYodW5jaGFuZ2VkLmxlbmd0aCA+IDAsIGRvYyA9PlxuICAgICAgZG9jXG4gICAgICAgIC5ydWxlKClcbiAgICAgICAgLnBhcmFncmFwaChzdW1tYXJpemVVbmNoYW5nZWQoJ3Byb2plY3QnLCB7IHVuY2hhbmdlZCwgY2hhbmdlZCB9KSksXG4gICAgKVxuICAgIC50b1N0cmluZygpO1xufVxuXG5mdW5jdGlvbiBjcmVhdGVEaWZmSGVhZGVyU2VjdGlvbihcbiAgZGlmZjogUmVwb3J0c0RpZmYgfCBSZXBvcnRzRGlmZltdLFxuKTogTWFya2Rvd25Eb2N1bWVudCB7XG4gIGNvbnN0IG91dGNvbWUgPSBtZXJnZURpZmZPdXRjb21lcyhcbiAgICBjaGFuZ2VzVG9EaWZmT3V0Y29tZXModG9BcnJheShkaWZmKS5mbGF0TWFwKGdldERpZmZDaGFuZ2VzKSksXG4gICk7XG4gIC8vIFRPRE86IHdoYXQgaWYgYXJyYXkgY29udGFpbnMgZGlmZmVyZW50IGNvbW1pdCBwYWlycz9cbiAgY29uc3QgY29tbWl0cyA9IEFycmF5LmlzQXJyYXkoZGlmZikgPyBkaWZmWzBdPy5jb21taXRzIDogZGlmZi5jb21taXRzO1xuICBjb25zdCBwb3J0YWxVcmwgPSBBcnJheS5pc0FycmF5KGRpZmYpID8gdW5kZWZpbmVkIDogZGlmZi5wb3J0YWxVcmw7XG5cbiAgcmV0dXJuIG5ldyBNYXJrZG93bkRvY3VtZW50KClcbiAgICAuaGVhZGluZyhISUVSQVJDSFkubGV2ZWxfMSwgJ0NvZGUgUHVzaFVwJylcbiAgICAucGFyYWdyYXBoKGZvcm1hdFJlcG9ydE91dGNvbWUob3V0Y29tZSwgY29tbWl0cykpXG4gICAgLnBhcmFncmFwaChmb3JtYXRQb3J0YWxMaW5rKHBvcnRhbFVybCkpO1xufVxuXG5mdW5jdGlvbiBjcmVhdGVEaWZmUHJvamVjdFNlY3Rpb24oXG4gIGRpZmY6IExhYmVsZWREaWZmICYgeyBvdXRjb21lOiBEaWZmT3V0Y29tZSB9LFxuKTogTWFya2Rvd25Eb2N1bWVudCB7XG4gIHJldHVybiBuZXcgTWFya2Rvd25Eb2N1bWVudCgpXG4gICAgLmhlYWRpbmcoSElFUkFSQ0hZLmxldmVsXzIsIG1kYFx1RDgzRFx1RENCQyBQcm9qZWN0ICR7bWQuY29kZShkaWZmLmxhYmVsKX1gKVxuICAgIC5wYXJhZ3JhcGgoZm9ybWF0UmVwb3J0T3V0Y29tZShkaWZmLm91dGNvbWUpKVxuICAgIC5wYXJhZ3JhcGgoZm9ybWF0UG9ydGFsTGluayhkaWZmLnBvcnRhbFVybCkpXG4gICAgLiRjb25jYXQoXG4gICAgICBjcmVhdGVEaWZmQ2F0ZWdvcmllc1NlY3Rpb24oZGlmZiwge1xuICAgICAgICBza2lwSGVhZGluZzogdHJ1ZSxcbiAgICAgICAgc2tpcFVuY2hhbmdlZDogdHJ1ZSxcbiAgICAgIH0pLFxuICAgICAgY3JlYXRlRGlmZkRldGFpbHNTZWN0aW9uKGRpZmYsIEhJRVJBUkNIWS5sZXZlbF8zKSxcbiAgICApO1xufVxuXG5mdW5jdGlvbiBjcmVhdGVEaWZmQ2F0ZWdvcmllc1NlY3Rpb24oXG4gIGRpZmY6IFJlcG9ydHNEaWZmLFxuICBvcHRpb25zPzogeyBza2lwSGVhZGluZz86IGJvb2xlYW47IHNraXBVbmNoYW5nZWQ/OiBib29sZWFuIH0sXG4pOiBNYXJrZG93bkRvY3VtZW50IHwgbnVsbCB7XG4gIGNvbnN0IHsgY2hhbmdlZCwgdW5jaGFuZ2VkLCBhZGRlZCB9ID0gZGlmZi5jYXRlZ29yaWVzO1xuICBjb25zdCB7IHNraXBIZWFkaW5nLCBza2lwVW5jaGFuZ2VkIH0gPSBvcHRpb25zID8/IHt9O1xuXG4gIGNvbnN0IGNhdGVnb3JpZXNDb3VudCA9IGNoYW5nZWQubGVuZ3RoICsgdW5jaGFuZ2VkLmxlbmd0aCArIGFkZGVkLmxlbmd0aDtcbiAgY29uc3QgaGFzQ2hhbmdlcyA9IHVuY2hhbmdlZC5sZW5ndGggPCBjYXRlZ29yaWVzQ291bnQ7XG5cbiAgaWYgKGNhdGVnb3JpZXNDb3VudCA9PT0gMCkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgY29uc3QgW2NvbHVtbnMsIHJvd3NdID0gY3JlYXRlQ2F0ZWdvcmllc1RhYmxlKGRpZmYsIHtcbiAgICBoYXNDaGFuZ2VzLFxuICAgIHNraXBVbmNoYW5nZWQsXG4gIH0pO1xuXG4gIHJldHVybiBuZXcgTWFya2Rvd25Eb2N1bWVudCgpXG4gICAgLmhlYWRpbmcoSElFUkFSQ0hZLmxldmVsXzIsICFza2lwSGVhZGluZyAmJiAnXHVEODNDXHVERkY3XHVGRTBGIENhdGVnb3JpZXMnKVxuICAgIC50YWJsZShjb2x1bW5zLCByb3dzKVxuICAgIC5wYXJhZ3JhcGgoYWRkZWQubGVuZ3RoID4gMCAmJiBtZC5pdGFsaWMoU3RyaW5nLnJhd2AoXFwqKSBOZXcgY2F0ZWdvcnkuYCkpXG4gICAgLnBhcmFncmFwaChcbiAgICAgIHNraXBVbmNoYW5nZWQgJiZcbiAgICAgICAgdW5jaGFuZ2VkLmxlbmd0aCA+IDAgJiZcbiAgICAgICAgc3VtbWFyaXplVW5jaGFuZ2VkKCdjYXRlZ29yeScsIHsgY2hhbmdlZCwgdW5jaGFuZ2VkIH0pLFxuICAgICk7XG59XG5cbmZ1bmN0aW9uIGNyZWF0ZUNhdGVnb3JpZXNUYWJsZShcbiAgZGlmZjogUmVwb3J0c0RpZmYsXG4gIG9wdGlvbnM6IHsgaGFzQ2hhbmdlczogYm9vbGVhbjsgc2tpcFVuY2hhbmdlZD86IGJvb2xlYW4gfSxcbik6IFBhcmFtZXRlcnM8TWFya2Rvd25Eb2N1bWVudFsndGFibGUnXT4ge1xuICBjb25zdCB7IGNoYW5nZWQsIHVuY2hhbmdlZCwgYWRkZWQgfSA9IGRpZmYuY2F0ZWdvcmllcztcbiAgY29uc3QgeyBoYXNDaGFuZ2VzLCBza2lwVW5jaGFuZ2VkIH0gPSBvcHRpb25zO1xuXG4gIGNvbnN0IHJvd3M6IFRhYmxlUm93W10gPSBbXG4gICAgLi4uc29ydENoYW5nZXMoY2hhbmdlZCkubWFwKGNhdGVnb3J5ID0+IFtcbiAgICAgIGZvcm1hdFRpdGxlKGNhdGVnb3J5KSxcbiAgICAgIGZvcm1hdFNjb3JlV2l0aENvbG9yKGNhdGVnb3J5LnNjb3Jlcy5iZWZvcmUsIHtcbiAgICAgICAgc2tpcEJvbGQ6IHRydWUsXG4gICAgICB9KSxcbiAgICAgIGZvcm1hdFNjb3JlV2l0aENvbG9yKGNhdGVnb3J5LnNjb3Jlcy5hZnRlciksXG4gICAgICBmb3JtYXRTY29yZUNoYW5nZShjYXRlZ29yeS5zY29yZXMuZGlmZiksXG4gICAgXSksXG4gICAgLi4uYWRkZWQubWFwKGNhdGVnb3J5ID0+IFtcbiAgICAgIGZvcm1hdFRpdGxlKGNhdGVnb3J5KSxcbiAgICAgIG1kLml0YWxpYyhTdHJpbmcucmF3YG4vYSAoXFwqKWApLFxuICAgICAgZm9ybWF0U2NvcmVXaXRoQ29sb3IoY2F0ZWdvcnkuc2NvcmUpLFxuICAgICAgbWQuaXRhbGljKFN0cmluZy5yYXdgbi9hIChcXCopYCksXG4gICAgXSksXG4gICAgLi4uKHNraXBVbmNoYW5nZWRcbiAgICAgID8gW11cbiAgICAgIDogdW5jaGFuZ2VkLm1hcChjYXRlZ29yeSA9PiBbXG4gICAgICAgICAgZm9ybWF0VGl0bGUoY2F0ZWdvcnkpLFxuICAgICAgICAgIGZvcm1hdFNjb3JlV2l0aENvbG9yKGNhdGVnb3J5LnNjb3JlLCB7IHNraXBCb2xkOiB0cnVlIH0pLFxuICAgICAgICAgIGZvcm1hdFNjb3JlV2l0aENvbG9yKGNhdGVnb3J5LnNjb3JlKSxcbiAgICAgICAgICAnXHUyMDEzJyxcbiAgICAgICAgXSkpLFxuICBdO1xuXG4gIGlmIChyb3dzLmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybiBbW10sIFtdXTtcbiAgfVxuXG4gIGNvbnN0IGNvbHVtbnM6IFRhYmxlQ29sdW1uT2JqZWN0W10gPSBbXG4gICAgeyBoZWFkaW5nOiAnXHVEODNDXHVERkY3XHVGRTBGIENhdGVnb3J5JywgYWxpZ25tZW50OiAnbGVmdCcgfSxcbiAgICB7XG4gICAgICBoZWFkaW5nOiBoYXNDaGFuZ2VzID8gJ1x1MkI1MCBQcmV2aW91cyBzY29yZScgOiAnXHUyQjUwIFNjb3JlJyxcbiAgICAgIGFsaWdubWVudDogJ2NlbnRlcicsXG4gICAgfSxcbiAgICB7IGhlYWRpbmc6ICdcdTJCNTAgQ3VycmVudCBzY29yZScsIGFsaWdubWVudDogJ2NlbnRlcicgfSxcbiAgICB7IGhlYWRpbmc6ICdcdUQ4M0RcdUREMDQgU2NvcmUgY2hhbmdlJywgYWxpZ25tZW50OiAnY2VudGVyJyB9LFxuICBdO1xuXG4gIHJldHVybiBbXG4gICAgaGFzQ2hhbmdlcyA/IGNvbHVtbnMgOiBjb2x1bW5zLnNsaWNlKDAsIDIpLFxuICAgIHJvd3MubWFwKHJvdyA9PiAoaGFzQ2hhbmdlcyA/IHJvdyA6IHJvdy5zbGljZSgwLCAyKSkpLFxuICBdO1xufVxuXG5mdW5jdGlvbiBjcmVhdGVEaWZmRGV0YWlsc1NlY3Rpb24oXG4gIGRpZmY6IFJlcG9ydHNEaWZmLFxuICBsZXZlbDogSGVhZGluZ0xldmVsID0gSElFUkFSQ0hZLmxldmVsXzIsXG4pOiBNYXJrZG93bkRvY3VtZW50IHwgbnVsbCB7XG4gIGlmIChkaWZmLmdyb3Vwcy5jaGFuZ2VkLmxlbmd0aCArIGRpZmYuYXVkaXRzLmNoYW5nZWQubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgY29uc3Qgc3VtbWFyeSA9IChbJ2dyb3VwJywgJ2F1ZGl0J10gYXMgY29uc3QpXG4gICAgLm1hcCh0b2tlbiA9PlxuICAgICAgc3VtbWFyaXplRGlmZk91dGNvbWVzKFxuICAgICAgICBjaGFuZ2VzVG9EaWZmT3V0Y29tZXMoZGlmZltgJHt0b2tlbn1zYF0uY2hhbmdlZCksXG4gICAgICAgIHRva2VuLFxuICAgICAgKSxcbiAgICApXG4gICAgLmZpbHRlcihCb29sZWFuKVxuICAgIC5qb2luKCcsICcpO1xuICBjb25zdCBkZXRhaWxzID0gbmV3IE1hcmtkb3duRG9jdW1lbnQoKS4kY29uY2F0KFxuICAgIGNyZWF0ZURpZmZHcm91cHNTZWN0aW9uKGRpZmYsIGxldmVsKSxcbiAgICBjcmVhdGVEaWZmQXVkaXRzU2VjdGlvbihkaWZmLCBsZXZlbCksXG4gICk7XG4gIHJldHVybiBuZXcgTWFya2Rvd25Eb2N1bWVudCgpLmRldGFpbHMoc3VtbWFyeSwgZGV0YWlscyk7XG59XG5cbmZ1bmN0aW9uIGNyZWF0ZURpZmZHcm91cHNTZWN0aW9uKFxuICBkaWZmOiBSZXBvcnRzRGlmZixcbiAgbGV2ZWw6IEhlYWRpbmdMZXZlbCxcbik6IE1hcmtkb3duRG9jdW1lbnQgfCBudWxsIHtcbiAgaWYgKGRpZmYuZ3JvdXBzLmNoYW5nZWQubGVuZ3RoICsgZGlmZi5ncm91cHMudW5jaGFuZ2VkLmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG4gIHJldHVybiBuZXcgTWFya2Rvd25Eb2N1bWVudCgpLmhlYWRpbmcobGV2ZWwsICdcdUQ4M0RcdUREQzNcdUZFMEYgR3JvdXBzJykuJGNvbmNhdChcbiAgICBjcmVhdGVHcm91cHNPckF1ZGl0c0RldGFpbHMoXG4gICAgICAnZ3JvdXAnLFxuICAgICAgZGlmZi5ncm91cHMsXG4gICAgICBbXG4gICAgICAgIHsgaGVhZGluZzogJ1x1RDgzRFx1REQwQyBQbHVnaW4nLCBhbGlnbm1lbnQ6ICdsZWZ0JyB9LFxuICAgICAgICB7IGhlYWRpbmc6ICdcdUQ4M0RcdUREQzNcdUZFMEYgR3JvdXAnLCBhbGlnbm1lbnQ6ICdsZWZ0JyB9LFxuICAgICAgICB7IGhlYWRpbmc6ICdcdTJCNTAgUHJldmlvdXMgc2NvcmUnLCBhbGlnbm1lbnQ6ICdjZW50ZXInIH0sXG4gICAgICAgIHsgaGVhZGluZzogJ1x1MkI1MCBDdXJyZW50IHNjb3JlJywgYWxpZ25tZW50OiAnY2VudGVyJyB9LFxuICAgICAgICB7IGhlYWRpbmc6ICdcdUQ4M0RcdUREMDQgU2NvcmUgY2hhbmdlJywgYWxpZ25tZW50OiAnY2VudGVyJyB9LFxuICAgICAgXSxcbiAgICAgIHNvcnRDaGFuZ2VzKGRpZmYuZ3JvdXBzLmNoYW5nZWQpLm1hcChncm91cCA9PiBbXG4gICAgICAgIGZvcm1hdFRpdGxlKGdyb3VwLnBsdWdpbiksXG4gICAgICAgIGZvcm1hdFRpdGxlKGdyb3VwKSxcbiAgICAgICAgZm9ybWF0U2NvcmVXaXRoQ29sb3IoZ3JvdXAuc2NvcmVzLmJlZm9yZSwgeyBza2lwQm9sZDogdHJ1ZSB9KSxcbiAgICAgICAgZm9ybWF0U2NvcmVXaXRoQ29sb3IoZ3JvdXAuc2NvcmVzLmFmdGVyKSxcbiAgICAgICAgZm9ybWF0U2NvcmVDaGFuZ2UoZ3JvdXAuc2NvcmVzLmRpZmYpLFxuICAgICAgXSksXG4gICAgKSxcbiAgKTtcbn1cblxuZnVuY3Rpb24gY3JlYXRlRGlmZkF1ZGl0c1NlY3Rpb24oXG4gIGRpZmY6IFJlcG9ydHNEaWZmLFxuICBsZXZlbDogSGVhZGluZ0xldmVsLFxuKTogTWFya2Rvd25Eb2N1bWVudCB7XG4gIHJldHVybiBuZXcgTWFya2Rvd25Eb2N1bWVudCgpLmhlYWRpbmcobGV2ZWwsICdcdUQ4M0RcdURFRTFcdUZFMEYgQXVkaXRzJykuJGNvbmNhdChcbiAgICBjcmVhdGVHcm91cHNPckF1ZGl0c0RldGFpbHMoXG4gICAgICAnYXVkaXQnLFxuICAgICAgZGlmZi5hdWRpdHMsXG4gICAgICBbXG4gICAgICAgIHsgaGVhZGluZzogJ1x1RDgzRFx1REQwQyBQbHVnaW4nLCBhbGlnbm1lbnQ6ICdsZWZ0JyB9LFxuICAgICAgICB7IGhlYWRpbmc6ICdcdUQ4M0RcdURFRTFcdUZFMEYgQXVkaXQnLCBhbGlnbm1lbnQ6ICdsZWZ0JyB9LFxuICAgICAgICB7IGhlYWRpbmc6ICdcdUQ4M0RcdURDQ0YgUHJldmlvdXMgdmFsdWUnLCBhbGlnbm1lbnQ6ICdjZW50ZXInIH0sXG4gICAgICAgIHsgaGVhZGluZzogJ1x1RDgzRFx1RENDRiBDdXJyZW50IHZhbHVlJywgYWxpZ25tZW50OiAnY2VudGVyJyB9LFxuICAgICAgICB7IGhlYWRpbmc6ICdcdUQ4M0RcdUREMDQgVmFsdWUgY2hhbmdlJywgYWxpZ25tZW50OiAnY2VudGVyJyB9LFxuICAgICAgXSxcbiAgICAgIHNvcnRDaGFuZ2VzKGRpZmYuYXVkaXRzLmNoYW5nZWQpLm1hcChhdWRpdCA9PiBbXG4gICAgICAgIGZvcm1hdFRpdGxlKGF1ZGl0LnBsdWdpbiksXG4gICAgICAgIGZvcm1hdFRpdGxlKGF1ZGl0KSxcbiAgICAgICAgYCR7c2NvcmVNYXJrZXIoYXVkaXQuc2NvcmVzLmJlZm9yZSwgJ3NxdWFyZScpfSAke1xuICAgICAgICAgIGF1ZGl0LmRpc3BsYXlWYWx1ZXMuYmVmb3JlIHx8IGF1ZGl0LnZhbHVlcy5iZWZvcmUudG9TdHJpbmcoKVxuICAgICAgICB9YCxcbiAgICAgICAgbWRgJHtzY29yZU1hcmtlcihhdWRpdC5zY29yZXMuYWZ0ZXIsICdzcXVhcmUnKX0gJHttZC5ib2xkKFxuICAgICAgICAgIGF1ZGl0LmRpc3BsYXlWYWx1ZXMuYWZ0ZXIgfHwgYXVkaXQudmFsdWVzLmFmdGVyLnRvU3RyaW5nKCksXG4gICAgICAgICl9YCxcbiAgICAgICAgZm9ybWF0VmFsdWVDaGFuZ2UoYXVkaXQpLFxuICAgICAgXSksXG4gICAgKSxcbiAgKTtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0cy9nZW5lcmF0ZS1tZC1yZXBvcnRzLWRpZmYtdXRpbHMudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9yZXBvcnRzXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0cy9nZW5lcmF0ZS1tZC1yZXBvcnRzLWRpZmYtdXRpbHMudHNcIjtpbXBvcnQgeyB0eXBlIElubGluZVRleHQsIE1hcmtkb3duRG9jdW1lbnQsIG1kIH0gZnJvbSAnYnVpbGQtbWQnO1xuaW1wb3J0IHR5cGUgeyBSZXBvcnRzRGlmZiB9IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHsgcGx1cmFsaXplLCBwbHVyYWxpemVUb2tlbiB9IGZyb20gJy4uL2Zvcm1hdHRpbmcuanMnO1xuaW1wb3J0IHsgb2JqZWN0VG9FbnRyaWVzIH0gZnJvbSAnLi4vdHJhbnNmb3JtLmpzJztcbmltcG9ydCB0eXBlIHsgRGlmZk91dGNvbWUgfSBmcm9tICcuL3R5cGVzLmpzJztcblxuLy8gdG8gcHJldmVudCBleGNlZWRpbmcgTWFya2Rvd24gY29tbWVudCBjaGFyYWN0ZXIgbGltaXRcbmNvbnN0IE1BWF9ST1dTID0gMTAwO1xuXG5leHBvcnQgZnVuY3Rpb24gc3VtbWFyaXplVW5jaGFuZ2VkKFxuICB0b2tlbjogc3RyaW5nLFxuICB7IGNoYW5nZWQsIHVuY2hhbmdlZCB9OiB7IGNoYW5nZWQ6IHVua25vd25bXTsgdW5jaGFuZ2VkOiB1bmtub3duW10gfSxcbik6IHN0cmluZyB7XG4gIGNvbnN0IHBsdXJhbGl6ZWRDb3VudCA9XG4gICAgY2hhbmdlZC5sZW5ndGggPiAwXG4gICAgICA/IHBsdXJhbGl6ZVRva2VuKGBvdGhlciAke3Rva2VufWAsIHVuY2hhbmdlZC5sZW5ndGgpXG4gICAgICA6IGBBbGwgb2YgJHtwbHVyYWxpemVUb2tlbih0b2tlbiwgdW5jaGFuZ2VkLmxlbmd0aCl9YDtcbiAgY29uc3QgcGx1cmFsaXplZFZlcmIgPSB1bmNoYW5nZWQubGVuZ3RoID09PSAxID8gJ2lzJyA6ICdhcmUnO1xuICByZXR1cm4gYCR7cGx1cmFsaXplZENvdW50fSAke3BsdXJhbGl6ZWRWZXJifSB1bmNoYW5nZWQuYDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHN1bW1hcml6ZURpZmZPdXRjb21lcyhcbiAgb3V0Y29tZXM6IERpZmZPdXRjb21lW10sXG4gIHRva2VuOiBzdHJpbmcsXG4pOiBzdHJpbmcge1xuICByZXR1cm4gb2JqZWN0VG9FbnRyaWVzKGNvdW50RGlmZk91dGNvbWVzKG91dGNvbWVzKSlcbiAgICAuZmlsdGVyKFxuICAgICAgKGVudHJ5KTogZW50cnkgaXMgW0V4Y2x1ZGU8RGlmZk91dGNvbWUsICd1bmNoYW5nZWQnPiwgbnVtYmVyXSA9PlxuICAgICAgICBlbnRyeVswXSAhPT0gJ3VuY2hhbmdlZCcgJiYgZW50cnlbMV0gPiAwLFxuICAgIClcbiAgICAubWFwKChbb3V0Y29tZSwgY291bnRdKTogc3RyaW5nID0+IHtcbiAgICAgIGNvbnN0IGZvcm1hdHRlZENvdW50ID0gYDxzdHJvbmc+JHtjb3VudH08L3N0cm9uZz4gJHtwbHVyYWxpemUoXG4gICAgICAgIHRva2VuLFxuICAgICAgICBjb3VudCxcbiAgICAgICl9YDtcbiAgICAgIHN3aXRjaCAob3V0Y29tZSkge1xuICAgICAgICBjYXNlICdwb3NpdGl2ZSc6XG4gICAgICAgICAgcmV0dXJuIGBcdUQ4M0RcdURDNEQgJHtmb3JtYXR0ZWRDb3VudH0gaW1wcm92ZWRgO1xuICAgICAgICBjYXNlICduZWdhdGl2ZSc6XG4gICAgICAgICAgcmV0dXJuIGBcdUQ4M0RcdURDNEUgJHtmb3JtYXR0ZWRDb3VudH0gcmVncmVzc2VkYDtcbiAgICAgICAgY2FzZSAnbWl4ZWQnOlxuICAgICAgICAgIHJldHVybiBgJHtmb3JtYXR0ZWRDb3VudH0gY2hhbmdlZCB3aXRob3V0IGltcGFjdGluZyBzY29yZWA7XG4gICAgICB9XG4gICAgfSlcbiAgICAuam9pbignLCAnKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUdyb3Vwc09yQXVkaXRzRGV0YWlsczxUIGV4dGVuZHMgJ2dyb3VwJyB8ICdhdWRpdCc+KFxuICB0b2tlbjogVCxcbiAgeyBjaGFuZ2VkLCB1bmNoYW5nZWQgfTogUmVwb3J0c0RpZmZbYCR7VH1zYF0sXG4gIC4uLltjb2x1bW5zLCByb3dzXTogUGFyYW1ldGVyczxNYXJrZG93bkRvY3VtZW50Wyd0YWJsZSddPlxuKTogTWFya2Rvd25Eb2N1bWVudCB7XG4gIGlmIChjaGFuZ2VkLmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybiBuZXcgTWFya2Rvd25Eb2N1bWVudCgpLnBhcmFncmFwaChcbiAgICAgIHN1bW1hcml6ZVVuY2hhbmdlZCh0b2tlbiwgeyBjaGFuZ2VkLCB1bmNoYW5nZWQgfSksXG4gICAgKTtcbiAgfVxuICByZXR1cm4gbmV3IE1hcmtkb3duRG9jdW1lbnQoKVxuICAgIC50YWJsZShjb2x1bW5zLCByb3dzLnNsaWNlKDAsIE1BWF9ST1dTKSlcbiAgICAucGFyYWdyYXBoKFxuICAgICAgY2hhbmdlZC5sZW5ndGggPiBNQVhfUk9XUyAmJlxuICAgICAgICBtZC5pdGFsaWMoXG4gICAgICAgICAgYE9ubHkgdGhlICR7TUFYX1JPV1N9IG1vc3QgYWZmZWN0ZWQgJHtwbHVyYWxpemUoXG4gICAgICAgICAgICB0b2tlbixcbiAgICAgICAgICApfSBhcmUgbGlzdGVkIGFib3ZlIGZvciBicmV2aXR5LmAsXG4gICAgICAgICksXG4gICAgKVxuICAgIC5wYXJhZ3JhcGgoXG4gICAgICB1bmNoYW5nZWQubGVuZ3RoID4gMCAmJiBzdW1tYXJpemVVbmNoYW5nZWQodG9rZW4sIHsgY2hhbmdlZCwgdW5jaGFuZ2VkIH0pLFxuICAgICk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRUaXRsZSh7XG4gIHRpdGxlLFxuICBkb2NzVXJsLFxufToge1xuICB0aXRsZTogc3RyaW5nO1xuICBkb2NzVXJsPzogc3RyaW5nO1xufSk6IElubGluZVRleHQge1xuICBpZiAoZG9jc1VybCkge1xuICAgIHJldHVybiBtZC5saW5rKGRvY3NVcmwsIHRpdGxlKTtcbiAgfVxuICByZXR1cm4gdGl0bGU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRQb3J0YWxMaW5rKFxuICBwb3J0YWxVcmw6IHN0cmluZyB8IHVuZGVmaW5lZCxcbik6IElubGluZVRleHQgfCB1bmRlZmluZWQge1xuICByZXR1cm4gKFxuICAgIHBvcnRhbFVybCAmJlxuICAgIG1kLmxpbmsocG9ydGFsVXJsLCAnXHVEODNEXHVERDc1XHVGRTBGIFNlZSBmdWxsIGNvbXBhcmlzb24gaW4gQ29kZSBQdXNoVXAgcG9ydGFsIFx1RDgzRFx1REQwRCcpXG4gICk7XG59XG5cbnR5cGUgQ2hhbmdlID0ge1xuICBzY29yZXM6IHsgZGlmZjogbnVtYmVyIH07XG4gIHZhbHVlcz86IHsgZGlmZjogbnVtYmVyIH07XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gc29ydENoYW5nZXM8VCBleHRlbmRzIENoYW5nZT4oY2hhbmdlczogVFtdKTogVFtdIHtcbiAgcmV0dXJuIGNoYW5nZXMudG9Tb3J0ZWQoXG4gICAgKGEsIGIpID0+XG4gICAgICBNYXRoLmFicyhiLnNjb3Jlcy5kaWZmKSAtIE1hdGguYWJzKGEuc2NvcmVzLmRpZmYpIHx8XG4gICAgICBNYXRoLmFicyhiLnZhbHVlcz8uZGlmZiA/PyAwKSAtIE1hdGguYWJzKGEudmFsdWVzPy5kaWZmID8/IDApLFxuICApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RGlmZkNoYW5nZXMoZGlmZjogUmVwb3J0c0RpZmYpOiBDaGFuZ2VbXSB7XG4gIHJldHVybiBbXG4gICAgLi4uZGlmZi5jYXRlZ29yaWVzLmNoYW5nZWQsXG4gICAgLi4uZGlmZi5ncm91cHMuY2hhbmdlZCxcbiAgICAuLi5kaWZmLmF1ZGl0cy5jaGFuZ2VkLFxuICBdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY2hhbmdlc1RvRGlmZk91dGNvbWVzKGNoYW5nZXM6IENoYW5nZVtdKTogRGlmZk91dGNvbWVbXSB7XG4gIHJldHVybiBjaGFuZ2VzLm1hcCgoY2hhbmdlKTogRGlmZk91dGNvbWUgPT4ge1xuICAgIGlmIChjaGFuZ2Uuc2NvcmVzLmRpZmYgPiAwKSB7XG4gICAgICByZXR1cm4gJ3Bvc2l0aXZlJztcbiAgICB9XG4gICAgaWYgKGNoYW5nZS5zY29yZXMuZGlmZiA8IDApIHtcbiAgICAgIHJldHVybiAnbmVnYXRpdmUnO1xuICAgIH1cbiAgICBpZiAoY2hhbmdlLnZhbHVlcyAhPSBudWxsICYmIGNoYW5nZS52YWx1ZXMuZGlmZiAhPT0gMCkge1xuICAgICAgcmV0dXJuICdtaXhlZCc7XG4gICAgfVxuICAgIHJldHVybiAndW5jaGFuZ2VkJztcbiAgfSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBtZXJnZURpZmZPdXRjb21lcyhvdXRjb21lczogRGlmZk91dGNvbWVbXSk6IERpZmZPdXRjb21lIHtcbiAgaWYgKG91dGNvbWVzLmV2ZXJ5KG91dGNvbWUgPT4gb3V0Y29tZSA9PT0gJ3VuY2hhbmdlZCcpKSB7XG4gICAgcmV0dXJuICd1bmNoYW5nZWQnO1xuICB9XG4gIGlmIChvdXRjb21lcy5pbmNsdWRlcygncG9zaXRpdmUnKSAmJiAhb3V0Y29tZXMuaW5jbHVkZXMoJ25lZ2F0aXZlJykpIHtcbiAgICByZXR1cm4gJ3Bvc2l0aXZlJztcbiAgfVxuICBpZiAob3V0Y29tZXMuaW5jbHVkZXMoJ25lZ2F0aXZlJykgJiYgIW91dGNvbWVzLmluY2x1ZGVzKCdwb3NpdGl2ZScpKSB7XG4gICAgcmV0dXJuICduZWdhdGl2ZSc7XG4gIH1cbiAgcmV0dXJuICdtaXhlZCc7XG59XG5cbmZ1bmN0aW9uIGNvdW50RGlmZk91dGNvbWVzKFxuICBvdXRjb21lczogRGlmZk91dGNvbWVbXSxcbik6IFJlY29yZDxEaWZmT3V0Y29tZSwgbnVtYmVyPiB7XG4gIHJldHVybiB7XG4gICAgcG9zaXRpdmU6IG91dGNvbWVzLmZpbHRlcihvdXRjb21lID0+IG91dGNvbWUgPT09ICdwb3NpdGl2ZScpLmxlbmd0aCxcbiAgICBuZWdhdGl2ZTogb3V0Y29tZXMuZmlsdGVyKG91dGNvbWUgPT4gb3V0Y29tZSA9PT0gJ25lZ2F0aXZlJykubGVuZ3RoLFxuICAgIG1peGVkOiBvdXRjb21lcy5maWx0ZXIob3V0Y29tZSA9PiBvdXRjb21lID09PSAnbWl4ZWQnKS5sZW5ndGgsXG4gICAgdW5jaGFuZ2VkOiBvdXRjb21lcy5maWx0ZXIob3V0Y29tZSA9PiBvdXRjb21lID09PSAndW5jaGFuZ2VkJykubGVuZ3RoLFxuICB9O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZm9ybWF0UmVwb3J0T3V0Y29tZShcbiAgb3V0Y29tZTogRGlmZk91dGNvbWUsXG4gIGNvbW1pdHM/OiBSZXBvcnRzRGlmZlsnY29tbWl0cyddLFxuKTogSW5saW5lVGV4dCB7XG4gIGNvbnN0IG91dGNvbWVUZXh0cyA9IHtcbiAgICBwb3NpdGl2ZTogbWRgXHVEODNFXHVERDczIENvZGUgUHVzaFVwIHJlcG9ydCBoYXMgJHttZC5ib2xkKCdpbXByb3ZlZCcpfWAsXG4gICAgbmVnYXRpdmU6IG1kYFx1RDgzRFx1REUxRiBDb2RlIFB1c2hVcCByZXBvcnQgaGFzICR7bWQuYm9sZCgncmVncmVzc2VkJyl9YCxcbiAgICBtaXhlZDogbWRgXHVEODNFXHVERDI4IENvZGUgUHVzaFVwIHJlcG9ydCBoYXMgYm90aCAke21kLmJvbGQoXG4gICAgICAnaW1wcm92ZW1lbnRzIGFuZCByZWdyZXNzaW9ucycsXG4gICAgKX1gLFxuICAgIHVuY2hhbmdlZDogbWRgXHVEODNEXHVERTEwIENvZGUgUHVzaFVwIHJlcG9ydCBpcyAke21kLmJvbGQoJ3VuY2hhbmdlZCcpfWAsXG4gIH07XG5cbiAgaWYgKGNvbW1pdHMpIHtcbiAgICBjb25zdCBjb21taXRzVGV4dCA9IGBjb21wYXJlZCB0YXJnZXQgY29tbWl0ICR7Y29tbWl0cy5hZnRlci5oYXNofSB3aXRoIHNvdXJjZSBjb21taXQgJHtjb21taXRzLmJlZm9yZS5oYXNofWA7XG4gICAgcmV0dXJuIG1kYCR7b3V0Y29tZVRleHRzW291dGNvbWVdfSBcdTIwMTMgJHtjb21taXRzVGV4dH0uYDtcbiAgfVxuXG4gIHJldHVybiBtZGAke291dGNvbWVUZXh0c1tvdXRjb21lXX0uYDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbXBhcmVEaWZmc0J5PFQgZXh0ZW5kcyAnY2F0ZWdvcmllcycgfCAnZ3JvdXBzJyB8ICdhdWRpdHMnPihcbiAgdHlwZTogVCxcbiAgYTogUmVwb3J0c0RpZmYsXG4gIGI6IFJlcG9ydHNEaWZmLFxuKTogbnVtYmVyIHtcbiAgcmV0dXJuIChcbiAgICBzdW1TY29yZUNoYW5nZXMoYlt0eXBlXS5jaGFuZ2VkKSAtIHN1bVNjb3JlQ2hhbmdlcyhhW3R5cGVdLmNoYW5nZWQpIHx8XG4gICAgc3VtQ29uZmlnQ2hhbmdlcyhiW3R5cGVdKSAtIHN1bUNvbmZpZ0NoYW5nZXMoYVt0eXBlXSlcbiAgKTtcbn1cblxuZnVuY3Rpb24gc3VtU2NvcmVDaGFuZ2VzKGNoYW5nZXM6IENoYW5nZVtdKTogbnVtYmVyIHtcbiAgcmV0dXJuIGNoYW5nZXMucmVkdWNlPG51bWJlcj4oXG4gICAgKGFjYywgeyBzY29yZXMgfSkgPT4gYWNjICsgTWF0aC5hYnMoc2NvcmVzLmRpZmYpLFxuICAgIDAsXG4gICk7XG59XG5cbmZ1bmN0aW9uIHN1bUNvbmZpZ0NoYW5nZXMoe1xuICBhZGRlZCxcbiAgcmVtb3ZlZCxcbn06IHtcbiAgYWRkZWQ6IHVua25vd25bXTtcbiAgcmVtb3ZlZDogdW5rbm93bltdO1xufSk6IG51bWJlciB7XG4gIHJldHVybiBhZGRlZC5sZW5ndGggKyByZW1vdmVkLmxlbmd0aDtcbn1cbiIsICJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0cy9sb2FkLXJlcG9ydC50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3JlcG9ydHNcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi9yZXBvcnRzL2xvYWQtcmVwb3J0LnRzXCI7aW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB7XG4gIHR5cGUgRm9ybWF0LFxuICB0eXBlIFBlcnNpc3RDb25maWcsXG4gIHR5cGUgUmVwb3J0LFxuICByZXBvcnRTY2hlbWEsXG59IGZyb20gJ0Bjb2RlLXB1c2h1cC9tb2RlbHMnO1xuaW1wb3J0IHtcbiAgZW5zdXJlRGlyZWN0b3J5RXhpc3RzLFxuICByZWFkSnNvbkZpbGUsXG4gIHJlYWRUZXh0RmlsZSxcbn0gZnJvbSAnLi4vZmlsZS1zeXN0ZW0uanMnO1xuXG50eXBlIExvYWRlZFJlcG9ydEZvcm1hdDxUIGV4dGVuZHMgRm9ybWF0PiA9IFQgZXh0ZW5kcyAnanNvbicgPyBSZXBvcnQgOiBzdHJpbmc7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBsb2FkUmVwb3J0PFQgZXh0ZW5kcyBGb3JtYXQ+KFxuICBvcHRpb25zOiBSZXF1aXJlZDxPbWl0PFBlcnNpc3RDb25maWcsICdmb3JtYXQnPj4gJiB7XG4gICAgZm9ybWF0OiBUO1xuICB9LFxuKTogUHJvbWlzZTxMb2FkZWRSZXBvcnRGb3JtYXQ8VD4+IHtcbiAgY29uc3QgeyBvdXRwdXREaXIsIGZpbGVuYW1lLCBmb3JtYXQgfSA9IG9wdGlvbnM7XG4gIGF3YWl0IGVuc3VyZURpcmVjdG9yeUV4aXN0cyhvdXRwdXREaXIpO1xuICBjb25zdCBmaWxlUGF0aCA9IHBhdGguam9pbihvdXRwdXREaXIsIGAke2ZpbGVuYW1lfS4ke2Zvcm1hdH1gKTtcblxuICBpZiAoZm9ybWF0ID09PSAnanNvbicpIHtcbiAgICBjb25zdCBjb250ZW50ID0gYXdhaXQgcmVhZEpzb25GaWxlKGZpbGVQYXRoKTtcbiAgICByZXR1cm4gcmVwb3J0U2NoZW1hLnBhcnNlKGNvbnRlbnQpIGFzIExvYWRlZFJlcG9ydEZvcm1hdDxUPjtcbiAgfVxuXG4gIGNvbnN0IHRleHQgPSBhd2FpdCByZWFkVGV4dEZpbGUoZmlsZVBhdGgpO1xuICByZXR1cm4gdGV4dCBhcyBMb2FkZWRSZXBvcnRGb3JtYXQ8VD47XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3JlcG9ydHMvbG9nLXN0ZG91dC1zdW1tYXJ5LnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWIvcmVwb3J0c1wiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3JlcG9ydHMvbG9nLXN0ZG91dC1zdW1tYXJ5LnRzXCI7aW1wb3J0IHsgYm9sZCwgY3lhbiwgY3lhbkJyaWdodCwgZ3JlZW4sIHJlZCB9IGZyb20gJ2Fuc2lzJztcbmltcG9ydCB0eXBlIHsgQXVkaXRSZXBvcnQgfSBmcm9tICdAY29kZS1wdXNodXAvbW9kZWxzJztcbmltcG9ydCB7IHVpIH0gZnJvbSAnLi4vbG9nZ2luZy5qcyc7XG5pbXBvcnQge1xuICBDT0RFX1BVU0hVUF9ET01BSU4sXG4gIEZPT1RFUl9QUkVGSVgsXG4gIFJFUE9SVF9IRUFETElORV9URVhULFxuICBSRVBPUlRfUkFXX09WRVJWSUVXX1RBQkxFX0hFQURFUlMsXG4gIFRFUk1JTkFMX1dJRFRILFxufSBmcm9tICcuL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgdHlwZSB7IFNjb3JlZFJlcG9ydCB9IGZyb20gJy4vdHlwZXMuanMnO1xuaW1wb3J0IHtcbiAgYXBwbHlTY29yZUNvbG9yLFxuICBjb3VudENhdGVnb3J5QXVkaXRzLFxuICB0YXJnZXRTY29yZUljb24sXG59IGZyb20gJy4vdXRpbHMuanMnO1xuXG5mdW5jdGlvbiBsb2cobXNnID0gJycpOiB2b2lkIHtcbiAgdWkoKS5sb2dnZXIubG9nKG1zZyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsb2dTdGRvdXRTdW1tYXJ5KHJlcG9ydDogU2NvcmVkUmVwb3J0LCB2ZXJib3NlID0gZmFsc2UpOiB2b2lkIHtcbiAgY29uc3QgeyBwbHVnaW5zLCBjYXRlZ29yaWVzLCBwYWNrYWdlTmFtZSwgdmVyc2lvbiB9ID0gcmVwb3J0O1xuICBsb2cocmVwb3J0VG9IZWFkZXJTZWN0aW9uKHsgcGFja2FnZU5hbWUsIHZlcnNpb24gfSkpO1xuICBsb2coKTtcbiAgbG9nUGx1Z2lucyhwbHVnaW5zLCB2ZXJib3NlKTtcbiAgaWYgKGNhdGVnb3JpZXMgJiYgY2F0ZWdvcmllcy5sZW5ndGggPiAwKSB7XG4gICAgbG9nQ2F0ZWdvcmllcyh7IHBsdWdpbnMsIGNhdGVnb3JpZXMgfSk7XG4gIH1cbiAgbG9nKGAke0ZPT1RFUl9QUkVGSVh9ICR7Q09ERV9QVVNIVVBfRE9NQUlOfWApO1xuICBsb2coKTtcbn1cblxuZnVuY3Rpb24gcmVwb3J0VG9IZWFkZXJTZWN0aW9uKHtcbiAgcGFja2FnZU5hbWUsXG4gIHZlcnNpb24sXG59OiBQaWNrPFNjb3JlZFJlcG9ydCwgJ3BhY2thZ2VOYW1lJyB8ICd2ZXJzaW9uJz4pOiBzdHJpbmcge1xuICByZXR1cm4gYCR7Ym9sZChSRVBPUlRfSEVBRExJTkVfVEVYVCl9IC0gJHtwYWNrYWdlTmFtZX1AJHt2ZXJzaW9ufWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsb2dQbHVnaW5zKFxuICBwbHVnaW5zOiBTY29yZWRSZXBvcnRbJ3BsdWdpbnMnXSxcbiAgdmVyYm9zZTogYm9vbGVhbixcbik6IHZvaWQge1xuICBwbHVnaW5zLmZvckVhY2gocGx1Z2luID0+IHtcbiAgICBjb25zdCB7IHRpdGxlLCBhdWRpdHMgfSA9IHBsdWdpbjtcbiAgICBjb25zdCBmaWx0ZXJlZEF1ZGl0cyA9XG4gICAgICB2ZXJib3NlIHx8IGF1ZGl0cy5sZW5ndGggPT09IDFcbiAgICAgICAgPyBhdWRpdHNcbiAgICAgICAgOiBhdWRpdHMuZmlsdGVyKCh7IHNjb3JlIH0pID0+IHNjb3JlICE9PSAxKTtcbiAgICBjb25zdCBkaWZmID0gYXVkaXRzLmxlbmd0aCAtIGZpbHRlcmVkQXVkaXRzLmxlbmd0aDtcblxuICAgIGxvZ0F1ZGl0cyh0aXRsZSwgZmlsdGVyZWRBdWRpdHMpO1xuXG4gICAgaWYgKGRpZmYgPiAwKSB7XG4gICAgICBjb25zdCBub3RpY2UgPVxuICAgICAgICBmaWx0ZXJlZEF1ZGl0cy5sZW5ndGggPT09IDBcbiAgICAgICAgICA/IGAuLi4gQWxsICR7ZGlmZn0gYXVkaXRzIGhhdmUgcGVyZmVjdCBzY29yZXMgLi4uYFxuICAgICAgICAgIDogYC4uLiAke2RpZmZ9IGF1ZGl0cyB3aXRoIHBlcmZlY3Qgc2NvcmVzIG9taXR0ZWQgZm9yIGJyZXZpdHkgLi4uYDtcbiAgICAgIGxvZ1JvdygxLCBub3RpY2UpO1xuICAgIH1cbiAgICBsb2coKTtcbiAgfSk7XG59XG5cbmZ1bmN0aW9uIGxvZ0F1ZGl0cyhwbHVnaW5UaXRsZTogc3RyaW5nLCBhdWRpdHM6IEF1ZGl0UmVwb3J0W10pOiB2b2lkIHtcbiAgbG9nKCk7XG4gIGxvZyhib2xkLm1hZ2VudGFCcmlnaHQoYCR7cGx1Z2luVGl0bGV9IGF1ZGl0c2ApKTtcbiAgbG9nKCk7XG4gIGF1ZGl0cy5mb3JFYWNoKCh7IHNjb3JlLCB0aXRsZSwgZGlzcGxheVZhbHVlLCB2YWx1ZSB9KSA9PiB7XG4gICAgbG9nUm93KHNjb3JlLCB0aXRsZSwgZGlzcGxheVZhbHVlIHx8IGAke3ZhbHVlfWApO1xuICB9KTtcbn1cblxuZnVuY3Rpb24gbG9nUm93KHNjb3JlOiBudW1iZXIsIHRpdGxlOiBzdHJpbmcsIHZhbHVlPzogc3RyaW5nKTogdm9pZCB7XG4gIHVpKCkucm93KFtcbiAgICB7XG4gICAgICB0ZXh0OiBhcHBseVNjb3JlQ29sb3IoeyBzY29yZSwgdGV4dDogJ1x1MjVDRicgfSksXG4gICAgICB3aWR0aDogMixcbiAgICAgIHBhZGRpbmc6IFswLCAxLCAwLCAwXSxcbiAgICB9LFxuICAgIHtcbiAgICAgIHRleHQ6IHRpdGxlLFxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1tYWdpYy1udW1iZXJzXG4gICAgICBwYWRkaW5nOiBbMCwgMywgMCwgMF0sXG4gICAgfSxcbiAgICAuLi4odmFsdWVcbiAgICAgID8gW1xuICAgICAgICAgIHtcbiAgICAgICAgICAgIHRleHQ6IGN5YW5CcmlnaHQodmFsdWUpLFxuICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1tYWdpYy1udW1iZXJzXG4gICAgICAgICAgICB3aWR0aDogMjAsXG4gICAgICAgICAgICBwYWRkaW5nOiBbMCwgMCwgMCwgMF0sXG4gICAgICAgICAgfSxcbiAgICAgICAgXVxuICAgICAgOiBbXSksXG4gIF0pO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbG9nQ2F0ZWdvcmllcyh7XG4gIHBsdWdpbnMsXG4gIGNhdGVnb3JpZXMsXG59OiBSZXF1aXJlZDxQaWNrPFNjb3JlZFJlcG9ydCwgJ3BsdWdpbnMnIHwgJ2NhdGVnb3JpZXMnPj4pOiB2b2lkIHtcbiAgY29uc3QgaEFsaWduID0gKGlkeDogbnVtYmVyKSA9PiAoaWR4ID09PSAwID8gJ2xlZnQnIDogJ3JpZ2h0Jyk7XG5cbiAgY29uc3Qgcm93cyA9IGNhdGVnb3JpZXMubWFwKCh7IHRpdGxlLCBzY29yZSwgcmVmcywgaXNCaW5hcnkgfSkgPT4gW1xuICAgIHRpdGxlLFxuICAgIGAke2JpbmFyeUljb25QcmVmaXgoc2NvcmUsIGlzQmluYXJ5KX0ke2FwcGx5U2NvcmVDb2xvcih7IHNjb3JlIH0pfWAsXG4gICAgY291bnRDYXRlZ29yeUF1ZGl0cyhyZWZzLCBwbHVnaW5zKSxcbiAgXSk7XG4gIGNvbnN0IHRhYmxlID0gdWkoKS50YWJsZSgpO1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLW1hZ2ljLW51bWJlcnNcbiAgdGFibGUuY29sdW1uV2lkdGhzKFtURVJNSU5BTF9XSURUSCAtIDkgLSAxMCAtIDQsIDksIDEwXSk7XG4gIHRhYmxlLmhlYWQoXG4gICAgUkVQT1JUX1JBV19PVkVSVklFV19UQUJMRV9IRUFERVJTLm1hcCgoaGVhZGluZywgaWR4KSA9PiAoe1xuICAgICAgY29udGVudDogY3lhbihoZWFkaW5nKSxcbiAgICAgIGhBbGlnbjogaEFsaWduKGlkeCksXG4gICAgfSkpLFxuICApO1xuICByb3dzLmZvckVhY2gocm93ID0+XG4gICAgdGFibGUucm93KFxuICAgICAgcm93Lm1hcCgoY29udGVudCwgaWR4KSA9PiAoe1xuICAgICAgICBjb250ZW50OiBjb250ZW50LnRvU3RyaW5nKCksXG4gICAgICAgIGhBbGlnbjogaEFsaWduKGlkeCksXG4gICAgICB9KSksXG4gICAgKSxcbiAgKTtcblxuICBsb2coYm9sZC5tYWdlbnRhQnJpZ2h0KCdDYXRlZ29yaWVzJykpO1xuICBsb2coKTtcbiAgdGFibGUucmVuZGVyKCk7XG4gIGxvZygpO1xufVxuXG4vLyBAVE9ETyByZWZhY3RvciBgaXNCaW5hcnk6IGJvb2xlYW5gIHRvIGB0YXJnZXRTY29yZTogbnVtYmVyYCAjNzEzXG5leHBvcnQgZnVuY3Rpb24gYmluYXJ5SWNvblByZWZpeChcbiAgc2NvcmU6IG51bWJlcixcbiAgaXNCaW5hcnk6IGJvb2xlYW4gfCB1bmRlZmluZWQsXG4pOiBzdHJpbmcge1xuICByZXR1cm4gdGFyZ2V0U2NvcmVJY29uKHNjb3JlLCBpc0JpbmFyeSA/IDEgOiB1bmRlZmluZWQsIHtcbiAgICBwYXNzSWNvbjogYm9sZChncmVlbignXHUyNzEzJykpLFxuICAgIGZhaWxJY29uOiBib2xkKHJlZCgnXHUyNzE3JykpLFxuICAgIHBvc3RmaXg6ICcgJyxcbiAgfSk7XG59XG4iLCAiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCIvaG9tZS9hbGVqYW5kcm8vZGV2L2NvZGUtcHVzaHVwLWNsaS9wYWNrYWdlcy91dGlscy9zcmMvbGliL3pvZC12YWxpZGF0aW9uLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9ob21lL2FsZWphbmRyby9kZXYvY29kZS1wdXNodXAtY2xpL3BhY2thZ2VzL3V0aWxzL3NyYy9saWJcIjtjb25zdCBfX2luamVjdGVkX2ltcG9ydF9tZXRhX3VybF9fID0gXCJmaWxlOi8vL2hvbWUvYWxlamFuZHJvL2Rldi9jb2RlLXB1c2h1cC1jbGkvcGFja2FnZXMvdXRpbHMvc3JjL2xpYi96b2QtdmFsaWRhdGlvbi50c1wiO2ltcG9ydCB7IGJvbGQsIHJlZCB9IGZyb20gJ2Fuc2lzJztcbmltcG9ydCB0eXBlIHsgTWVzc2FnZUJ1aWxkZXIgfSBmcm9tICd6b2QtdmFsaWRhdGlvbi1lcnJvcic7XG5cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRFcnJvclBhdGgoZXJyb3JQYXRoOiAoc3RyaW5nIHwgbnVtYmVyKVtdKTogc3RyaW5nIHtcbiAgcmV0dXJuIGVycm9yUGF0aFxuICAgIC5tYXAoKGtleSwgaW5kZXgpID0+IHtcbiAgICAgIGlmICh0eXBlb2Yga2V5ID09PSAnbnVtYmVyJykge1xuICAgICAgICByZXR1cm4gYFske2tleX1dYDtcbiAgICAgIH1cbiAgICAgIHJldHVybiBpbmRleCA+IDAgPyBgLiR7a2V5fWAgOiBrZXk7XG4gICAgfSlcbiAgICAuam9pbignJyk7XG59XG5cbmV4cG9ydCBjb25zdCB6b2RFcnJvck1lc3NhZ2VCdWlsZGVyOiBNZXNzYWdlQnVpbGRlciA9IGlzc3VlcyA9PlxuICBpc3N1ZXNcbiAgICAubWFwKGlzc3VlID0+IHtcbiAgICAgIGNvbnN0IGZvcm1hdHRlZE1lc3NhZ2UgPSByZWQoYCR7Ym9sZChpc3N1ZS5jb2RlKX06ICR7aXNzdWUubWVzc2FnZX1gKTtcbiAgICAgIGNvbnN0IGZvcm1hdHRlZFBhdGggPSBmb3JtYXRFcnJvclBhdGgoaXNzdWUucGF0aCk7XG4gICAgICBpZiAoZm9ybWF0dGVkUGF0aCkge1xuICAgICAgICByZXR1cm4gYFZhbGlkYXRpb24gZXJyb3IgYXQgJHtib2xkKGZvcm1hdHRlZFBhdGgpfVxcbiR7Zm9ybWF0dGVkTWVzc2FnZX1cXG5gO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGAke2Zvcm1hdHRlZE1lc3NhZ2V9XFxuYDtcbiAgICB9KVxuICAgIC5qb2luKCdcXG4nKTtcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBcVEsT0FBTztBQUM1USxTQUFTLEtBQUFB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O0FBQUEsRUFDdkQsQ0FBQztBQUNIOyIsCiAgIm5hbWVzIjogWyJ6IiwgInBhdGgiLCAicGF0aCIsICJ1aSIsICJwYXRoIiwgImJvbGQiLCAicGF0aCIsICJ1aSIsICJ6IiwgInoiLCAiY29uZmlnIiwgImdyb3VwcyIsICJhdWRpdHMiLCAiY2FsY3VsYXRlQ292ZXJhZ2UiLCAiY29uZmlnIiwgImNhbGN1bGF0ZUNvdmVyYWdlIiwgImNvbmZpZyIsICJjcmVhdGVSZXF1aXJlIiwgInBhdGgiLCAiZmlsZVVSTFRvUGF0aCIsICJ6IiwgInoiLCAic2x1Z2lmeSIsICJ0b0FycmF5IiwgInBhdGgiLCAiZXhpc3RzIiwgInRvQXJyYXkiLCAidWkiLCAiZXhpc3RzIiwgInRvQXJyYXkiLCAidWkiLCAiRVNMaW50IiwgIndyaXRlRmlsZSIsICJwYXRoIiwgImVuc3VyZURpcmVjdG9yeUV4aXN0cyIsICJmaWxlUGF0aFRvQ2xpQXJnIiwgInBsdWdpbldvcmtEaXIiLCAicmVhZEpzb25GaWxlIiwgImRpc3RpbmN0IiwgImV4ZWN1dGVQcm9jZXNzIiwgImZpbGVQYXRoVG9DbGlBcmciLCAidG9BcnJheSIsICJ1aSIsICJXT1JLRElSIiwgInBsdWdpbldvcmtEaXIiLCAiUlVOTkVSX09VVFBVVF9QQVRIIiwgInBhdGgiLCAiUExVR0lOX0NPTkZJR19QQVRIIiwgInBhdGgiLCAiZmlsZUV4aXN0cyIsICJ0b0FycmF5IiwgImNyZWF0ZVJlcXVpcmUiLCAicGF0aCIsICJmaWxlVVJMVG9QYXRoIiwgInoiLCAieiIsICJvYmplY3RUb0tleXMiLCAicGF0aCIsICJvYmplY3RUb0tleXMiLCAicmVhZEpzb25GaWxlIiwgIm9iamVjdFRvRW50cmllcyIsICJvYmplY3RUb0VudHJpZXMiLCAib2JqZWN0VG9FbnRyaWVzIiwgIm9iamVjdFRvRW50cmllcyIsICJvYmplY3RUb0tleXMiLCAib2JqZWN0VG9LZXlzIiwgIm9iamVjdFRvRW50cmllcyIsICJmcm9tSnNvbkxpbmVzIiwgIm9iamVjdEZyb21FbnRyaWVzIiwgIm9iamVjdFRvRW50cmllcyIsICJvYmplY3RUb0tleXMiLCAid3JpdGVGaWxlIiwgInBhdGgiLCAiZW5zdXJlRGlyZWN0b3J5RXhpc3RzIiwgImV4ZWN1dGVQcm9jZXNzIiwgImZpbGVQYXRoVG9DbGlBcmciLCAib2JqZWN0RnJvbUVudHJpZXMiLCAicmVhZEpzb25GaWxlIiwgIm9iamVjdFRvRW50cmllcyIsICJwYXRoIiwgInBsdWdpbldvcmtEaXIiLCAiV09SS0RJUiIsICJwbHVnaW5Xb3JrRGlyIiwgIlJVTk5FUl9PVVRQVVRfUEFUSCIsICJwYXRoIiwgIlBMVUdJTl9DT05GSUdfUEFUSCIsICJtZCIsICJvYmplY3RGcm9tRW50cmllcyIsICJvYmplY3RUb0tleXMiLCAib2JqZWN0VG9LZXlzIiwgInBhdGgiLCAiZmlsZUV4aXN0cyIsICJleGVjdXRlUHJvY2VzcyIsICJjcmVhdGVSZXF1aXJlIiwgInBhdGgiLCAicGF0aCIsICJib2xkIiwgInVpIiwgInBhdGgiLCAicGF0aCIsICJwYXRoIiwgImVuc3VyZURpcmVjdG9yeUV4aXN0cyIsICJib2xkIiwgImltcG9ydE1vZHVsZSIsICJyZWFkSnNvbkZpbGUiLCAidWkiLCAiYm9sZCIsICJ5ZWxsb3ciLCAidWkiLCAidGFibGVTY2hlbWEiLCAiZm9ybWF0Qnl0ZXMiLCAiZm9ybWF0RHVyYXRpb24iLCAiaHRtbCIsICJib2xkIiwgInVpIiwgImJvbGQiLCAidG9BcnJheSIsICJjb25maWciLCAiY29uZmlnIiwgImV4aXN0cyIsICJtZCIsICJib2xkIiwgInJlYWRGaWxlIiwgInBhdGgiLCAicGF0aCIsICJzaW1wbGVHaXQiLCAicGxhdGZvcm0iLCAiY29uZmlnIiwgImNhdGVnb3JpZXMiLCAiYm9sZCIsICJncmF5IiwgIk1hcmtkb3duRG9jdW1lbnQiLCAibWQiLCAibWQiLCAicGF0aCIsICJNYXJrZG93bkRvY3VtZW50IiwgIm1kIiwgIk1hcmtkb3duRG9jdW1lbnQiLCAibWQiLCAiTWFya2Rvd25Eb2N1bWVudCIsICJtZCIsICJwYXRoIiwgImJvbGQiLCAiZ3JlZW4iLCAiYm9sZCIsICJyZWQiLCAieiJdCn0K diff --git a/packages/plugin-doc-coverage/mocks/component-mock.ts b/packages/plugin-doc-coverage/mocks/component-mock.ts index 5b2da7c27..6f39b5118 100644 --- a/packages/plugin-doc-coverage/mocks/component-mock.ts +++ b/packages/plugin-doc-coverage/mocks/component-mock.ts @@ -9,41 +9,3 @@ export function DUMMY_FUNCTION() { export function DUMMY_FUNCTION_2() { return 'Hello World 2'; } - -// class DummyClass { -// /** -// * Dummy property that returns 'Hello World 3'. -// * @returns {string} - The string 'Hello World 3'. -// */ -// dummyProperty = 'Hello World 3'; - -// /** -// * Dummy method that returns 'Hello World 4'. -// * @returns {string} - The string 'Hello World 4'. -// */ -// dummyMethod() { -// return 'Hello World 4'; -// } - -// constructor() { -// this.dummyProperty = 'Hello World 3'; -// } -// } - -// export default DummyClass; - -// export const variableDummy = 'Hello World 5'; - -// export const variableDummy2 = 'Hello World 6'; - -// /** Dummy variable that returns 'Hello World 7'. */ -// export const variableDummy3 = 'Hello World 7'; - -// /** Dummy interface that returns 'Hello World 8'. */ -// export interface DummyInterface { -// dummyProperty: string; -// dummyMethod(): string; -// } - -// /** Dummy type that returns 'Hello World 9'. */ -// export type DummyType = string; diff --git a/packages/plugin-doc-coverage/mocks/fixtures/angular/map-event.function.ts b/packages/plugin-doc-coverage/mocks/fixtures/angular/map-event.function.ts index 9cd32ce8a..55f343e7c 100644 --- a/packages/plugin-doc-coverage/mocks/fixtures/angular/map-event.function.ts +++ b/packages/plugin-doc-coverage/mocks/fixtures/angular/map-event.function.ts @@ -1,3 +1,5 @@ +export const someVariable = 'Hello World 1'; + export function mapEventToCustomEvent(event: string) { return event; } diff --git a/packages/plugin-doc-coverage/mocks/source-files.mock.ts b/packages/plugin-doc-coverage/mocks/source-files.mock.ts index bc0ad113c..5a7ba13aa 100644 --- a/packages/plugin-doc-coverage/mocks/source-files.mock.ts +++ b/packages/plugin-doc-coverage/mocks/source-files.mock.ts @@ -6,70 +6,37 @@ import { SourceFile, SyntaxKind, TypeAliasDeclaration, + VariableStatement, } from 'ts-morph'; -import type { CoverageType } from '../src/lib/models'; +import type { CoverageType } from '../src/lib/runner/models'; export function sourceFileMock( file: string, nodes: Partial>>, ): SourceFile { + const createNodeGetter = ( + coverageType: CoverageType, + nodeData?: Record, + ) => { + if (!nodeData) return []; + return Object.entries(nodeData).map(([line, isCommented]) => + nodeMock({ coverageType, line: Number(line), file, isCommented }), + ) as unknown as T[]; + }; + return { getFilePath: () => file as any, getClasses: () => - nodes.classes - ? (Object.entries(nodes.classes).map(([line, isCommented]) => - nodeMock({ - coverageType: 'classes', - line: Number(line), - file, - isCommented, - }), - ) as unknown as ClassDeclaration[]) - : [], + createNodeGetter('classes', nodes.classes), getFunctions: () => - nodes.functions - ? (Object.entries(nodes.functions).map(([line, isCommented]) => - nodeMock({ - coverageType: 'functions', - line: Number(line), - file, - isCommented, - }), - ) as unknown as FunctionDeclaration[]) - : [], - getEnums: () => - nodes.enums - ? (Object.entries(nodes.enums).map(([line, isCommented]) => - nodeMock({ - coverageType: 'enums', - line: Number(line), - file, - isCommented, - }), - ) as unknown as EnumDeclaration[]) - : [], + createNodeGetter('functions', nodes.functions), + getEnums: () => createNodeGetter('enums', nodes.enums), getTypeAliases: () => - nodes.types - ? (Object.entries(nodes.types).map(([line, isCommented]) => - nodeMock({ - coverageType: 'types', - line: Number(line), - file, - isCommented, - }), - ) as unknown as TypeAliasDeclaration[]) - : [], + createNodeGetter('types', nodes.types), getInterfaces: () => - nodes.interfaces - ? (Object.entries(nodes.interfaces).map(([line, isCommented]) => - nodeMock({ - coverageType: 'interfaces', - line: Number(line), - file, - isCommented, - }), - ) as unknown as InterfaceDeclaration[]) - : [], + createNodeGetter('interfaces', nodes.interfaces), + getVariableStatements: () => + createNodeGetter('variables', nodes.variables), } as SourceFile; } @@ -84,6 +51,7 @@ export function nodeMock(options: { getJsDocs: () => (options.isCommented ? ['Comment'] : []), getName: () => 'test', getStartLineNumber: () => options.line, + getDeclarations: () => [], // Only for classes getMethods: () => [], getProperties: () => [], diff --git a/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.integration.test.ts.snap b/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.integration.test.ts.snap index 2507460f6..0b98c1e05 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.integration.test.ts.snap +++ b/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.integration.test.ts.snap @@ -23,7 +23,7 @@ exports[`processDocCoverage > should succesfully get the right number of ts file }, { "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/map-event.function.ts", - "line": 1, + "line": 3, "name": "mapEventToCustomEvent", "type": "functions", }, @@ -40,7 +40,7 @@ exports[`processDocCoverage > should succesfully get the right number of ts file "issues": [ { "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.ts", - "line": 15, + "line": 17, "name": "sendEvent", "type": "methods", }, @@ -52,7 +52,7 @@ exports[`processDocCoverage > should succesfully get the right number of ts file "issues": [ { "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.ts", - "line": 5, + "line": 7, "name": "title", "type": "properties", }, @@ -65,9 +65,16 @@ exports[`processDocCoverage > should succesfully get the right number of ts file "nodesCount": 0, }, "variables": { - "coverage": 100, - "issues": [], - "nodesCount": 0, + "coverage": 0, + "issues": [ + { + "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/map-event.function.ts", + "line": 1, + "name": "someVariable", + "type": "variables", + }, + ], + "nodesCount": 1, }, } `; @@ -89,7 +96,7 @@ exports[`processDocCoverage > should succesfully get the right number of ts file "issues": [ { "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/map-event.function.ts", - "line": 1, + "line": 3, "name": "mapEventToCustomEvent", "type": "functions", }, @@ -106,7 +113,7 @@ exports[`processDocCoverage > should succesfully get the right number of ts file "issues": [ { "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.ts", - "line": 15, + "line": 17, "name": "sendEvent", "type": "methods", }, @@ -118,7 +125,7 @@ exports[`processDocCoverage > should succesfully get the right number of ts file "issues": [ { "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.ts", - "line": 5, + "line": 7, "name": "title", "type": "properties", }, @@ -131,9 +138,16 @@ exports[`processDocCoverage > should succesfully get the right number of ts file "nodesCount": 0, }, "variables": { - "coverage": 100, - "issues": [], - "nodesCount": 0, + "coverage": 0, + "issues": [ + { + "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/map-event.function.ts", + "line": 1, + "name": "someVariable", + "type": "variables", + }, + ], + "nodesCount": 1, }, } `; diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts index d510ae15f..c5d82cac7 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts @@ -1,4 +1,9 @@ -import { ClassDeclaration, Project, SourceFile } from 'ts-morph'; +import { + ClassDeclaration, + Project, + SourceFile, + VariableStatement, +} from 'ts-morph'; import type { DocCoveragePluginConfig } from '../config.js'; import type { CoverageResult, @@ -11,6 +16,30 @@ import { getCoverageTypeFromKind, } from './utils.js'; +/** + * Gets the variables information from the variable statements + * @param variableStatements - The variable statements to process + * @returns {Node[]} The variables information with the right methods to get the information + */ +export function getVariablesInformation( + variableStatements: VariableStatement[], +) { + return variableStatements.flatMap(variable => { + // Get parent-level information + const parentInfo = { + getKind: () => variable.getKind(), + getJsDocs: () => variable.getJsDocs(), + getStartLineNumber: () => variable.getStartLineNumber(), + }; + + // Map each declaration to combine parent info with declaration-specific info + return variable.getDeclarations().map(declaration => ({ + ...parentInfo, + getName: () => declaration.getName(), + })); + }); +} + /** * Processes documentation coverage for TypeScript files in the specified path * @param toInclude - The file path pattern to include for documentation analysis @@ -44,6 +73,7 @@ export function getUnprocessedCoverageReport( ...sourceFile.getTypeAliases(), ...sourceFile.getEnums(), ...sourceFile.getInterfaces(), + ...getVariablesInformation(sourceFile.getVariableStatements()), ]; const coverageReportOfCurrentFile = allNodesFromFile.reduce( diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts index 233723f03..dcd9ad31b 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts @@ -1,8 +1,9 @@ -import type { ClassDeclaration } from 'ts-morph'; +import type { ClassDeclaration, VariableStatement } from 'ts-morph'; import { nodeMock, sourceFileMock } from '../../../mocks/source-files.mock'; import { getClassNodes, getUnprocessedCoverageReport, + getVariablesInformation, mergeCoverageResults, } from './doc-processer.js'; import type { UnprocessedCoverageResult } from './models.js'; @@ -196,3 +197,79 @@ describe('getClassNodes', () => { expect(propertyNodeSpy).toHaveBeenCalledTimes(1); }); }); + +describe('getVariablesInformation', () => { + it('should process variable statements correctly', () => { + const mockDeclaration = { + getName: () => 'testVariable', + }; + + const mockVariableStatement = { + getKind: () => 'const', + getJsDocs: () => ['some docs'], + getStartLineNumber: () => 42, + getDeclarations: () => [mockDeclaration], + }; + + const result = getVariablesInformation([ + mockVariableStatement as unknown as VariableStatement, + ]); + + expect(result).toHaveLength(1); + expect(result[0]).toEqual({ + getKind: expect.any(Function), + getJsDocs: expect.any(Function), + getStartLineNumber: expect.any(Function), + getName: expect.any(Function), + }); + // It must be defined + expect(result[0]!.getName()).toBe('testVariable'); + expect(result[0]!.getKind()).toBe('const'); + expect(result[0]!.getJsDocs()).toEqual(['some docs']); + expect(result[0]!.getStartLineNumber()).toBe(42); + }); + + it('should handle multiple declarations in a single variable statement', () => { + const mockDeclarations = [ + { getName: () => 'var1' }, + { getName: () => 'var2' }, + ]; + + const mockVariableStatement = { + getKind: () => 'let', + getJsDocs: () => [], + getStartLineNumber: () => 10, + getDeclarations: () => mockDeclarations, + }; + + const result = getVariablesInformation([ + mockVariableStatement as unknown as VariableStatement, + ]); + + expect(result).toHaveLength(2); + // They must be defined + expect(result[0]!.getName()).toBe('var1'); + expect(result[1]!.getName()).toBe('var2'); + expect(result[0]!.getKind()).toBe('let'); + expect(result[1]!.getKind()).toBe('let'); + }); + + it('should handle empty variable statements array', () => { + const result = getVariablesInformation([]); + expect(result).toHaveLength(0); + }); + + it('should handle variable statements without declarations', () => { + const mockVariableStatement = { + getKind: () => 'const', + getJsDocs: () => [], + getStartLineNumber: () => 1, + getDeclarations: () => [], + }; + + const result = getVariablesInformation([ + mockVariableStatement as unknown as VariableStatement, + ]); + expect(result).toHaveLength(0); + }); +}); diff --git a/packages/plugin-doc-coverage/src/lib/runner/utils.ts b/packages/plugin-doc-coverage/src/lib/runner/utils.ts index 31c312619..7b8807a2c 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/utils.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/utils.ts @@ -67,6 +67,7 @@ export function getCoverageTypeFromKind(kind: SyntaxKind): CoverageType { return 'interfaces'; case SyntaxKind.EnumDeclaration: return 'enums'; + case SyntaxKind.VariableStatement: case SyntaxKind.VariableDeclaration: return 'variables'; case SyntaxKind.PropertyDeclaration: From 1a5a6149ce07aa00961a8033cdbf7556ec5d0a66 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Sat, 21 Dec 2024 12:51:12 +0100 Subject: [PATCH 11/39] chore(plugin-doc-coverage): remove unused file and update readme file --- packages/plugin-doc-coverage/README.md | 19 +++++++++---------- .../mocks/documentation.json | 11 ----------- 2 files changed, 9 insertions(+), 21 deletions(-) delete mode 100644 packages/plugin-doc-coverage/mocks/documentation.json diff --git a/packages/plugin-doc-coverage/README.md b/packages/plugin-doc-coverage/README.md index 0b4fb33e6..59a039c9d 100644 --- a/packages/plugin-doc-coverage/README.md +++ b/packages/plugin-doc-coverage/README.md @@ -33,12 +33,14 @@ Measured documentation types are mapped to Code PushUp audits in the following w pnpm add --save-dev @code-pushup/doc-coverage-plugin ``` -3. Add Compodoc to your project. You can follow the instructions [here](https://compodoc.app/guides/installation.html). - -4. Add this plugin to the `plugins` array in your Code PushUp CLI config file (e.g. `code-pushup.config.js`). +3. Add this plugin to the `plugins` array in your Code PushUp CLI config file (e.g. `code-pushup.config.js`). Pass the target files to analyze and optionally specify which types of documentation you want to track. - All documentation types are measured by default. If you wish to focus on a subset of offered types, define them in `docTypes`. + You can skip for example tests by defining in the sourceGlob the path to the tests folder or pattern to match the tests files with the `!` symbol. + All documentation types are measured by default. + If you wish to focus on a subset of offered types, define them in `onlyAudits`. + Also you can skip some types by defining them in `skipAudits`. + You can only define or `onlyAudits` or `skipAudits`, not both. The configuration will look similarly to the following: @@ -50,16 +52,13 @@ Measured documentation types are mapped to Code PushUp audits in the following w plugins: [ // ... await docCoveragePlugin({ - coverageToolCommand: { - command: 'npx', - args: ['compodoc', '-p', 'tsconfig.doc.json', '-e', 'json'], - }, + sourceGlob: ['**/*.ts'], }), ], }; ``` -5. (Optional) Reference individual audits or the provided plugin group which you wish to include in custom categories (use `npx code-pushup print-config` to list audits and groups). +4. (Optional) Reference individual audits or the provided plugin group which you wish to include in custom categories (use `npx code-pushup print-config` to list audits and groups). 💡 Assign weights based on what influence each documentation type should have on the overall category score (assign weight 0 to only include as extra info, without influencing category score). @@ -85,7 +84,7 @@ Measured documentation types are mapped to Code PushUp audits in the following w }; ``` -6. Run the CLI with `npx code-pushup collect` and view or upload report (refer to [CLI docs](../cli/README.md)). +5. Run the CLI with `npx code-pushup collect` and view or upload report (refer to [CLI docs](../cli/README.md)). ## About documentation coverage diff --git a/packages/plugin-doc-coverage/mocks/documentation.json b/packages/plugin-doc-coverage/mocks/documentation.json deleted file mode 100644 index 7a608253c..000000000 --- a/packages/plugin-doc-coverage/mocks/documentation.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "coverage": { - "count": 85, - "files": { - "src/app/services/my.service.ts": { - "documented": 17, - "total": 20 - } - } - } -} From 8909855f8e4a142eca8b9eeb988b68ad9f11b729 Mon Sep 17 00:00:00 2001 From: Alejandro <49059458+aramirezj@users.noreply.github.com> Date: Sun, 22 Dec 2024 14:54:41 +0100 Subject: [PATCH 12/39] Update packages/plugin-doc-coverage/src/lib/constants.ts Co-authored-by: Michael Hladky <10064416+BioPhoton@users.noreply.github.com> --- packages/plugin-doc-coverage/src/lib/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-doc-coverage/src/lib/constants.ts b/packages/plugin-doc-coverage/src/lib/constants.ts index d199b2b55..d5d0d6b38 100644 --- a/packages/plugin-doc-coverage/src/lib/constants.ts +++ b/packages/plugin-doc-coverage/src/lib/constants.ts @@ -7,7 +7,7 @@ export const AUDITS_MAP: Record = { 'classes-coverage': { slug: 'classes-coverage', title: 'Classes coverage', - description: 'Coverage of classes', + description: 'Documentation coverage of classes', }, 'methods-coverage': { slug: 'methods-coverage', From b8cdf77de25368121d55a115aa1f457f9a55488e Mon Sep 17 00:00:00 2001 From: Alejandro <49059458+aramirezj@users.noreply.github.com> Date: Sun, 22 Dec 2024 14:54:51 +0100 Subject: [PATCH 13/39] Update packages/plugin-doc-coverage/package.json Co-authored-by: Michael Hladky <10064416+BioPhoton@users.noreply.github.com> --- packages/plugin-doc-coverage/package.json | 29 ++++++++++++----------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/plugin-doc-coverage/package.json b/packages/plugin-doc-coverage/package.json index c06f92a38..4d5fc0c60 100644 --- a/packages/plugin-doc-coverage/package.json +++ b/packages/plugin-doc-coverage/package.json @@ -13,21 +13,22 @@ "directory": "packages/plugin-doc-coverage" }, "keywords": [ - "CLI", - "Code PushUp", - "plugin", - "automation", - "developer tools", - "conformance", "documentation coverage", - "documentation", - "docs", - "KPI tracking", - "automated feedback", - "regression guard", - "actionable feedback", - "audit", - "score monitoring" + "documentation quality", + "docs completeness", + "automated documentation checks", + "coverage audit", + "documentation conformance", + "docs KPI tracking", + "documentation feedback", + "actionable documentation insights", + "documentation regression guard", + "documentation score monitoring", + "developer documentation tools", + "plugin for documentation coverage", + "CLI documentation coverage", + "Code PushUp documentation", + "documentation audit" ], "publishConfig": { "access": "public" From 24ab2e04861dcd1d31566d9debe3ff1461b48a30 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Sun, 22 Dec 2024 15:38:03 +0100 Subject: [PATCH 14/39] chore(plugin-doc-coverage): from Michael comments, remakes some tests, rename different stuff --- code-pushup.config.ts | 24 ++- .../mocks/component-mock.ts | 11 -- .../mocks/component-mock.unit.test.ts | 16 -- .../doc-processer.integration.test.ts.snap | 153 ------------------ .../__snapshots__/runner.unit.test.ts.snap | 147 ----------------- .../runner/doc-processer.integration.test.ts | 39 +++-- .../src/lib/runner/doc-processer.ts | 20 +-- .../src/lib/runner/doc-processer.unit.test.ts | 6 +- .../src/lib/runner/models.ts | 6 +- .../src/lib/runner/runner.ts | 11 +- .../src/lib/runner/runner.unit.test.ts | 73 ++++----- .../src/lib/runner/utils.ts | 6 +- .../src/lib/runner/utils.unit.test.ts | 15 +- 13 files changed, 95 insertions(+), 432 deletions(-) delete mode 100644 packages/plugin-doc-coverage/mocks/component-mock.ts delete mode 100644 packages/plugin-doc-coverage/mocks/component-mock.unit.test.ts delete mode 100644 packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.integration.test.ts.snap delete mode 100644 packages/plugin-doc-coverage/src/lib/runner/__snapshots__/runner.unit.test.ts.snap diff --git a/code-pushup.config.ts b/code-pushup.config.ts index a0a33cfee..6b2ea8dc4 100644 --- a/code-pushup.config.ts +++ b/code-pushup.config.ts @@ -1,12 +1,6 @@ import 'dotenv/config'; import { z } from 'zod'; -import { - coverageCoreConfigNx, - docCoverageCoreConfig, - eslintCoreConfigNx, - jsPackagesCoreConfig, - lighthouseCoreConfig, -} from './code-pushup.preset.js'; +import { docCoverageCoreConfig } from './code-pushup.preset.js'; import type { CoreConfig } from './packages/models/src/index.js'; import { mergeConfigs } from './packages/utils/src/index.js'; @@ -33,13 +27,13 @@ const config: CoreConfig = { }; export default mergeConfigs( - config, - await coverageCoreConfigNx(), - await jsPackagesCoreConfig(), - await lighthouseCoreConfig( - 'https://github.com/code-pushup/cli?tab=readme-ov-file#code-pushup-cli/', - ), - await eslintCoreConfigNx(), + // config, + // await coverageCoreConfigNx(), + // await jsPackagesCoreConfig(), + // await lighthouseCoreConfig( + // 'https://github.com/code-pushup/cli?tab=readme-ov-file#code-pushup-cli/', + // ), + // await eslintCoreConfigNx(), await docCoverageCoreConfig({ sourceGlob: [ 'packages/**/src/**/*.ts', @@ -48,6 +42,6 @@ export default mergeConfigs( '!**/implementation/**', '!**/internal/**', ], - skipAudits: ['methods-coverage'], + skipAudits: ['methodawdawdds-coverage'], }), ); diff --git a/packages/plugin-doc-coverage/mocks/component-mock.ts b/packages/plugin-doc-coverage/mocks/component-mock.ts deleted file mode 100644 index 6f39b5118..000000000 --- a/packages/plugin-doc-coverage/mocks/component-mock.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Dummy function that returns 'Hello World'. - * @returns {string} - The string 'Hello World'. - */ -export function DUMMY_FUNCTION() { - return 'Hello World'; -} - -export function DUMMY_FUNCTION_2() { - return 'Hello World 2'; -} diff --git a/packages/plugin-doc-coverage/mocks/component-mock.unit.test.ts b/packages/plugin-doc-coverage/mocks/component-mock.unit.test.ts deleted file mode 100644 index 648080fb0..000000000 --- a/packages/plugin-doc-coverage/mocks/component-mock.unit.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { describe, expect, it } from 'vitest'; -import { DUMMY_FUNCTION, DUMMY_FUNCTION_2 } from './component-mock.js'; - -export function shouldnotBeHere() { - return 'Hello World'; -} - -describe('component-mock', () => { - it('should return Hello World', () => { - expect(DUMMY_FUNCTION()).toBe('Hello World'); - }); - - it('should return Hello World 2', () => { - expect(DUMMY_FUNCTION_2()).toBe('Hello World 2'); - }); -}); diff --git a/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.integration.test.ts.snap b/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.integration.test.ts.snap deleted file mode 100644 index 0b98c1e05..000000000 --- a/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.integration.test.ts.snap +++ /dev/null @@ -1,153 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`processDocCoverage > should succesfully get the right number of ts files 1`] = ` -{ - "classes": { - "coverage": 100, - "issues": [], - "nodesCount": 1, - }, - "enums": { - "coverage": 100, - "issues": [], - "nodesCount": 0, - }, - "functions": { - "coverage": 33.33, - "issues": [ - { - "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.spec.ts", - "line": 1, - "name": "notRealisticFunction", - "type": "functions", - }, - { - "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/map-event.function.ts", - "line": 3, - "name": "mapEventToCustomEvent", - "type": "functions", - }, - ], - "nodesCount": 3, - }, - "interfaces": { - "coverage": 100, - "issues": [], - "nodesCount": 0, - }, - "methods": { - "coverage": 50, - "issues": [ - { - "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.ts", - "line": 17, - "name": "sendEvent", - "type": "methods", - }, - ], - "nodesCount": 2, - }, - "properties": { - "coverage": 0, - "issues": [ - { - "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.ts", - "line": 7, - "name": "title", - "type": "properties", - }, - ], - "nodesCount": 1, - }, - "types": { - "coverage": 100, - "issues": [], - "nodesCount": 0, - }, - "variables": { - "coverage": 0, - "issues": [ - { - "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/map-event.function.ts", - "line": 1, - "name": "someVariable", - "type": "variables", - }, - ], - "nodesCount": 1, - }, -} -`; - -exports[`processDocCoverage > should succesfully get the right number of ts files and not include spec files 1`] = ` -{ - "classes": { - "coverage": 100, - "issues": [], - "nodesCount": 1, - }, - "enums": { - "coverage": 100, - "issues": [], - "nodesCount": 0, - }, - "functions": { - "coverage": 50, - "issues": [ - { - "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/map-event.function.ts", - "line": 3, - "name": "mapEventToCustomEvent", - "type": "functions", - }, - ], - "nodesCount": 2, - }, - "interfaces": { - "coverage": 100, - "issues": [], - "nodesCount": 0, - }, - "methods": { - "coverage": 50, - "issues": [ - { - "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.ts", - "line": 17, - "name": "sendEvent", - "type": "methods", - }, - ], - "nodesCount": 2, - }, - "properties": { - "coverage": 0, - "issues": [ - { - "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/app.component.ts", - "line": 7, - "name": "title", - "type": "properties", - }, - ], - "nodesCount": 1, - }, - "types": { - "coverage": 100, - "issues": [], - "nodesCount": 0, - }, - "variables": { - "coverage": 0, - "issues": [ - { - "file": "/home/alejandro/dev/code-pushup-cli/packages/plugin-doc-coverage/mocks/fixtures/angular/map-event.function.ts", - "line": 1, - "name": "someVariable", - "type": "variables", - }, - ], - "nodesCount": 1, - }, -} -`; diff --git a/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/runner.unit.test.ts.snap b/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/runner.unit.test.ts.snap deleted file mode 100644 index 9db5313d1..000000000 --- a/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/runner.unit.test.ts.snap +++ /dev/null @@ -1,147 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`trasformCoverageReportToAudits > should filter audits when onlyAudits is provided 1`] = ` -[ - { - "details": { - "issues": [ - { - "message": "Missing documentation", - "severity": "warning", - "source": { - "file": "test.ts", - "position": { - "startLine": 10, - }, - }, - }, - ], - }, - "displayValue": "75 %", - "score": 0.75, - "slug": "functions-coverage", - "value": 75, - }, -] -`; - -exports[`trasformCoverageReportToAudits > should filter audits when skipAudits is provided 1`] = ` -[ - { - "details": { - "issues": [ - { - "message": "Missing documentation", - "severity": "warning", - "source": { - "file": "test.ts", - "position": { - "startLine": 10, - }, - }, - }, - ], - }, - "displayValue": "75 %", - "score": 0.75, - "slug": "functions-coverage", - "value": 75, - }, -] -`; - -exports[`trasformCoverageReportToAudits > should handle coverage result with multiple issues 1`] = ` -[ - { - "details": { - "issues": [ - { - "message": "Missing documentation", - "severity": "warning", - "source": { - "file": "test1.ts", - "position": { - "startLine": 10, - }, - }, - }, - { - "message": "Missing documentation", - "severity": "warning", - "source": { - "file": "test2.ts", - "position": { - "startLine": 20, - }, - }, - }, - ], - }, - "displayValue": "50 %", - "score": 0.5, - "slug": "functions-coverage", - "value": 50, - }, -] -`; - -exports[`trasformCoverageReportToAudits > should handle empty coverage result 1`] = `[]`; - -exports[`trasformCoverageReportToAudits > should prioritize onlyAudits over skipAudits when both are provided 1`] = ` -[ - { - "details": { - "issues": [ - { - "message": "Missing documentation", - "severity": "warning", - "source": { - "file": "test.ts", - "position": { - "startLine": 10, - }, - }, - }, - ], - }, - "displayValue": "75 %", - "score": 0.75, - "slug": "functions-coverage", - "value": 75, - }, -] -`; - -exports[`trasformCoverageReportToAudits > should transform coverage report to audit outputs with no filters 1`] = ` -[ - { - "details": { - "issues": [ - { - "message": "Missing documentation", - "severity": "warning", - "source": { - "file": "test.ts", - "position": { - "startLine": 10, - }, - }, - }, - ], - }, - "displayValue": "75 %", - "score": 0.75, - "slug": "functions-coverage", - "value": 75, - }, - { - "details": { - "issues": [], - }, - "displayValue": "100 %", - "score": 1, - "slug": "classes-coverage", - "value": 100, - }, -] -`; diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts index 00bdc83a7..0a93347f9 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts @@ -1,23 +1,34 @@ import { processDocCoverage } from './doc-processer.js'; describe('processDocCoverage', () => { - it('should succesfully get the right number of ts files', () => { - const results = processDocCoverage({ - sourceGlob: [ - 'packages/plugin-doc-coverage/mocks/fixtures/angular/**/*.ts', - ], - }); - expect(results).toMatchSnapshot(); + const sourcePath = + 'packages/plugin-doc-coverage/mocks/fixtures/angular/**/*.ts'; + + it('should count total nodes from TypeScript files correctly', () => { + const expectedNodeCount = 8; + + const results = processDocCoverage({ sourceGlob: [sourcePath] }); + + const totalNodeCount = Object.values(results).reduce( + (acc, node) => acc + node.nodesCount, + 0, + ); + + expect(totalNodeCount).toBe(expectedNodeCount); }); - it('should succesfully get the right number of ts files and not include spec files', () => { + it('should count total nodes from TypeScript files correctly and not include spec files when specified', () => { + const expectedNodeCount = 7; + const results = processDocCoverage({ - sourceGlob: [ - 'packages/plugin-doc-coverage/mocks/fixtures/angular/**/*.ts', - '!**/*.spec.ts', - '!**/*.test.ts', - ], + sourceGlob: [sourcePath, '!**/*.spec.ts', '!**/*.test.ts'], }); - expect(results).toMatchSnapshot(); + + const totalNodeCount = Object.values(results).reduce( + (acc, node) => acc + node.nodesCount, + 0, + ); + + expect(totalNodeCount).toBe(expectedNodeCount); }); }); diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts index c5d82cac7..d63c1f469 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts @@ -6,13 +6,13 @@ import { } from 'ts-morph'; import type { DocCoveragePluginConfig } from '../config.js'; import type { + CoverageReportShape, CoverageResult, CoverageType, - UnprocessedCoverageResult, } from './models.js'; import { calculateCoverage, - createEmptyUnprocessedCoverageReport, + createEmptyCoverageData, getCoverageTypeFromKind, } from './utils.js'; @@ -56,7 +56,7 @@ export function processDocCoverage( /** * Gets the unprocessed coverage report from the source files * @param sourceFiles - The source files to process - * @returns {UnprocessedCoverageResult} The unprocessed coverage report + * @returns {CoverageReportShape} The unprocessed coverage report */ export function getUnprocessedCoverageReport( sourceFiles: SourceFile[], @@ -102,7 +102,7 @@ export function getUnprocessedCoverageReport( }, }; }, - createEmptyUnprocessedCoverageReport(), + createEmptyCoverageData(), ); return mergeCoverageResults( @@ -110,7 +110,7 @@ export function getUnprocessedCoverageReport( coverageReportOfCurrentFile, ); }, - createEmptyUnprocessedCoverageReport(), + createEmptyCoverageData(), ); return calculateCoverage(unprocessedCoverageReport); @@ -120,12 +120,12 @@ export function getUnprocessedCoverageReport( * Merges two coverage results * @param results - The first empty coverage result * @param current - The second coverage result - * @returns {UnprocessedCoverageResult} The merged coverage result + * @returns {CoverageReportShape} The merged coverage result */ export function mergeCoverageResults( - results: UnprocessedCoverageResult, - current: Partial, -): UnprocessedCoverageResult { + results: CoverageReportShape, + current: Partial, +): CoverageReportShape { return Object.fromEntries( Object.entries(results).map(([key, value]) => { const node = value as CoverageResult[CoverageType]; @@ -138,7 +138,7 @@ export function mergeCoverageResults( }, ]; }), - ) as UnprocessedCoverageResult; + ) as CoverageReportShape; } /** diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts index dcd9ad31b..2c6fc0c17 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts @@ -6,7 +6,7 @@ import { getVariablesInformation, mergeCoverageResults, } from './doc-processer.js'; -import type { UnprocessedCoverageResult } from './models.js'; +import type { CoverageReportShape } from './models.js'; describe('getUnprocessedCoverageReport', () => { it('should produce a full report', () => { @@ -72,7 +72,7 @@ describe('getUnprocessedCoverageReport', () => { }); describe('mergeCoverageResults', () => { - const emptyResult: UnprocessedCoverageResult = { + const emptyResult: CoverageReportShape = { enums: { nodesCount: 0, issues: [] }, interfaces: { nodesCount: 0, issues: [] }, types: { nodesCount: 0, issues: [] }, @@ -102,7 +102,7 @@ describe('mergeCoverageResults', () => { const results = mergeCoverageResults( emptyResult, - secondResult as Partial, + secondResult as Partial, ); expect(results).toStrictEqual( expect.objectContaining({ diff --git a/packages/plugin-doc-coverage/src/lib/runner/models.ts b/packages/plugin-doc-coverage/src/lib/runner/models.ts index c8119b4db..547efab56 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/models.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/models.ts @@ -25,14 +25,14 @@ export type UndocumentedNode = { class?: string; }; -/** The coverage data is the data that is used to create the coverage report. Without coverage stats yet */ +/** The coverage data is the data that is used to create the coverage report. Without coverage stats. */ export type CoverageData = { issues: UndocumentedNode[]; nodesCount: number; }; -/** The unprocessed coverage result CoverageData but for each coverage type. */ -export type UnprocessedCoverageResult = Record; +/** The coverage report shape the report of every CoverageType without coverage stats. */ +export type CoverageReportShape = Record; /** The processed coverage result CoverageData but for each coverage type and with coverage stats. */ export type CoverageResult = Record< diff --git a/packages/plugin-doc-coverage/src/lib/runner/runner.ts b/packages/plugin-doc-coverage/src/lib/runner/runner.ts index 8bbf54066..1019687e8 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/runner.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/runner.ts @@ -1,7 +1,7 @@ import type { AuditOutputs, RunnerFunction } from '@code-pushup/models'; import type { DocCoveragePluginConfig } from '../config.js'; import { processDocCoverage } from './doc-processer.js'; -import type { CoverageResult, CoverageType } from './models.js'; +import type { CoverageResult } from './models.js'; export function createRunnerFunction( config: DocCoveragePluginConfig, @@ -33,17 +33,16 @@ export function trasformCoverageReportToAudits( } return true; }) - .map(([type, items]) => { - const coverageType = type as CoverageType; - const coverage = items.coverage; + .map(([type, item]) => { + const { coverage } = item; return { - slug: `${coverageType}-coverage`, + slug: `${type}-coverage`, value: coverage, score: coverage / 100, displayValue: `${coverage} %`, details: { - issues: items.issues.map(({ file, line }) => ({ + issues: item.issues.map(({ file, line }) => ({ message: 'Missing documentation', source: { file, position: { startLine: line } }, severity: 'warning', diff --git a/packages/plugin-doc-coverage/src/lib/runner/runner.unit.test.ts b/packages/plugin-doc-coverage/src/lib/runner/runner.unit.test.ts index d81748ab3..aab40d08c 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/runner.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/runner.unit.test.ts @@ -18,71 +18,58 @@ describe('trasformCoverageReportToAudits', () => { classes: { coverage: 100, nodesCount: 2, - issues: [], + issues: [ + { + file: 'test.ts', + line: 10, + name: 'testClass', + type: 'classes', + }, + ], }, } as unknown as CoverageResult; - it('should transform coverage report to audit outputs with no filters', () => { + it('should return all audits from the coverage result when no filters are provided', () => { const result = trasformCoverageReportToAudits(mockCoverageResult, {}); - expect(result).toMatchSnapshot(); + expect(result.map(item => item.slug)).toStrictEqual([ + 'functions-coverage', + 'classes-coverage', + ]); }); it('should filter audits when onlyAudits is provided', () => { const result = trasformCoverageReportToAudits(mockCoverageResult, { onlyAudits: ['functions-coverage'], }); - expect(result).toMatchSnapshot(); + expect(result).toHaveLength(1); + expect(result.map(item => item.slug)).toStrictEqual(['functions-coverage']); }); it('should filter audits when skipAudits is provided', () => { const result = trasformCoverageReportToAudits(mockCoverageResult, { - skipAudits: ['classes-coverage'], + skipAudits: ['functions-coverage'], }); - expect(result).toMatchSnapshot(); + expect(result).toHaveLength(1); + expect(result.map(item => item.slug)).toStrictEqual(['classes-coverage']); }); - it('should handle empty coverage result', () => { + it('should handle properly empty coverage result', () => { const result = trasformCoverageReportToAudits( {} as unknown as CoverageResult, {}, ); - expect(result).toMatchSnapshot(); - }); - - it('should handle coverage result with multiple issues', () => { - const coverageWithMultipleIssues = { - functions: { - coverage: 50, - nodesCount: 4, - issues: [ - { - file: 'test1.ts', - line: 10, - name: 'function1', - type: 'functions', - }, - { - file: 'test2.ts', - line: 20, - name: 'function2', - type: 'functions', - }, - ], - }, - } as unknown as CoverageResult; - - const result = trasformCoverageReportToAudits( - coverageWithMultipleIssues, - {}, - ); - expect(result).toMatchSnapshot(); + expect(result).toEqual([]); }); - it('should prioritize onlyAudits over skipAudits when both are provided', () => { - const result = trasformCoverageReportToAudits(mockCoverageResult, { - onlyAudits: ['functions-coverage'], - skipAudits: ['functions-coverage'], - }); - expect(result).toMatchSnapshot(); + it('should handle coverage result with multiple issues and add them to the details.issue of the report', () => { + const expectedIssues = 2; + const result = trasformCoverageReportToAudits(mockCoverageResult, {}); + expect(result).toHaveLength(2); + expect( + result.reduce( + (acc, item) => acc + (item.details?.issues?.length ?? 0), + 0, + ), + ).toBe(expectedIssues); }); }); diff --git a/packages/plugin-doc-coverage/src/lib/runner/utils.ts b/packages/plugin-doc-coverage/src/lib/runner/utils.ts index 7b8807a2c..cdc202991 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/utils.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/utils.ts @@ -1,15 +1,15 @@ import { SyntaxKind } from 'ts-morph'; import type { + CoverageReportShape, CoverageResult, CoverageType, - UnprocessedCoverageResult, } from './models.js'; /** * Creates an empty unprocessed coverage report. * @returns The empty unprocessed coverage report. */ -export function createEmptyUnprocessedCoverageReport(): UnprocessedCoverageResult { +export function createEmptyCoverageData(): CoverageReportShape { return { enums: { nodesCount: 0, issues: [] }, interfaces: { nodesCount: 0, issues: [] }, @@ -27,7 +27,7 @@ export function createEmptyUnprocessedCoverageReport(): UnprocessedCoverageResul * @param result - The unprocessed coverage result. * @returns The processed coverage result. */ -export function calculateCoverage(result: UnprocessedCoverageResult) { +export function calculateCoverage(result: CoverageReportShape) { return Object.fromEntries( Object.entries(result).map(([key, value]) => { const type = key as CoverageType; diff --git a/packages/plugin-doc-coverage/src/lib/runner/utils.unit.test.ts b/packages/plugin-doc-coverage/src/lib/runner/utils.unit.test.ts index 731d5280c..d45b1600c 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/utils.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/utils.unit.test.ts @@ -1,14 +1,14 @@ import { SyntaxKind } from 'ts-morph'; -import type { UnprocessedCoverageResult } from './models.js'; +import type { CoverageReportShape } from './models.js'; import { calculateCoverage, - createEmptyUnprocessedCoverageReport, + createEmptyCoverageData, getCoverageTypeFromKind, } from './utils.js'; -describe('createEmptyUnprocessedCoverageReport', () => { +describe('createEmptyCoverageData', () => { it('should create an empty report with all categories initialized', () => { - const result = createEmptyUnprocessedCoverageReport(); + const result = createEmptyCoverageData(); expect(result).toStrictEqual({ enums: { nodesCount: 0, issues: [] }, @@ -25,8 +25,7 @@ describe('createEmptyUnprocessedCoverageReport', () => { describe('calculateCoverage', () => { it('should calculate 100% coverage when there are no nodes', () => { - const input: UnprocessedCoverageResult = - createEmptyUnprocessedCoverageReport(); + const input = createEmptyCoverageData(); const result = calculateCoverage(input); Object.values(result).forEach(category => { @@ -37,8 +36,8 @@ describe('calculateCoverage', () => { }); it('should calculate correct coverage percentage with issues', () => { - const input: UnprocessedCoverageResult = { - ...createEmptyUnprocessedCoverageReport(), + const input: CoverageReportShape = { + ...createEmptyCoverageData(), functions: { nodesCount: 4, issues: [ From 6c83208016f9e7c922823e13ade088c5efc1854f Mon Sep 17 00:00:00 2001 From: Alejandro Date: Sun, 22 Dec 2024 15:46:14 +0100 Subject: [PATCH 15/39] chore(plugin-doc-coverage): remove extra line From 8c2d274074e30d151a495cc37ea8e8083acdc809 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Sun, 22 Dec 2024 15:46:55 +0100 Subject: [PATCH 16/39] chore: put back plugin config --- code-pushup.config.ts | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/code-pushup.config.ts b/code-pushup.config.ts index 6b2ea8dc4..699aee3c3 100644 --- a/code-pushup.config.ts +++ b/code-pushup.config.ts @@ -1,6 +1,12 @@ import 'dotenv/config'; import { z } from 'zod'; -import { docCoverageCoreConfig } from './code-pushup.preset.js'; +import { + coverageCoreConfigNx, + docCoverageCoreConfig, + eslintCoreConfigNx, + jsPackagesCoreConfig, + lighthouseCoreConfig, +} from './code-pushup.preset.js'; import type { CoreConfig } from './packages/models/src/index.js'; import { mergeConfigs } from './packages/utils/src/index.js'; @@ -27,13 +33,13 @@ const config: CoreConfig = { }; export default mergeConfigs( - // config, - // await coverageCoreConfigNx(), - // await jsPackagesCoreConfig(), - // await lighthouseCoreConfig( - // 'https://github.com/code-pushup/cli?tab=readme-ov-file#code-pushup-cli/', - // ), - // await eslintCoreConfigNx(), + config, + await coverageCoreConfigNx(), + await jsPackagesCoreConfig(), + await lighthouseCoreConfig( + 'https://github.com/code-pushup/cli?tab=readme-ov-file#code-pushup-cli/', + ), + await eslintCoreConfigNx(), await docCoverageCoreConfig({ sourceGlob: [ 'packages/**/src/**/*.ts', From a70c03b23bd5bcd850b069d440262d2004ddac9a Mon Sep 17 00:00:00 2001 From: Alejandro Date: Sun, 22 Dec 2024 15:49:36 +0100 Subject: [PATCH 17/39] chore: fix plugin config, remove unused snapshot --- code-pushup.config.ts | 2 +- .../runner/doc-processer.unit.test.ts.snap | 125 ------------------ 2 files changed, 1 insertion(+), 126 deletions(-) delete mode 100644 packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts.snap diff --git a/code-pushup.config.ts b/code-pushup.config.ts index 699aee3c3..a0a33cfee 100644 --- a/code-pushup.config.ts +++ b/code-pushup.config.ts @@ -48,6 +48,6 @@ export default mergeConfigs( '!**/implementation/**', '!**/internal/**', ], - skipAudits: ['methodawdawdds-coverage'], + skipAudits: ['methods-coverage'], }), ); diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts.snap b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts.snap deleted file mode 100644 index 220467a98..000000000 --- a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts.snap +++ /dev/null @@ -1,125 +0,0 @@ -{ - "classes": { - "coverage": 0, - "issues": [ - { - "file": "test.ts", - "line": 4, - "name": "test", - "type": "classes", - }, - { - "file": "test.ts", - "line": 5, - "name": "test", - "type": "classes", - }, - { - "file": "test.ts", - "line": 6, - "name": "test", - "type": "classes", - }, - ], - "nodesCount": 3, - }, - "enums": { - "coverage": 0, - "issues": [ - { - "file": "test.ts", - "line": 7, - "name": "test", - "type": "enums", - }, - { - "file": "test.ts", - "line": 8, - "name": "test", - "type": "enums", - }, - { - "file": "test.ts", - "line": 9, - "name": "test", - "type": "enums", - }, - ], - "nodesCount": 3, - }, - "functions": { - "coverage": 66.66666666666667, - "issues": [ - { - "file": "test.ts", - "line": 3, - "name": "test", - "type": "functions", - }, - ], - "nodesCount": 3, - }, - "interfaces": { - "coverage": 0, - "issues": [ - { - "file": "test.ts", - "line": 13, - "name": "test", - "type": "interfaces", - }, - { - "file": "test.ts", - "line": 14, - "name": "test", - "type": "interfaces", - }, - { - "file": "test.ts", - "line": 15, - "name": "test", - "type": "interfaces", - }, - ], - "nodesCount": 3, - }, - "methods": { - "coverage": 100, - "issues": [], - "nodesCount": 0, - }, - "properties": { - "coverage": 100, - "issues": [], - "nodesCount": 0, - }, - "types": { - "coverage": 0, - "issues": [ - { - "file": "test.ts", - "line": 10, - "name": "test", - "type": "types", - }, - { - "file": "test.ts", - "line": 11, - "name": "test", - "type": "types", - }, - { - "file": "test.ts", - "line": 12, - "name": "test", - "type": "types", - }, - ], - "nodesCount": 3, - }, - "variables": { - "coverage": 100, - "issues": [], - "nodesCount": 0, - }, -} \ No newline at end of file From 6be3803c67b505a27950fab13b0ba9a10ce038e5 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Sun, 22 Dec 2024 16:04:43 +0100 Subject: [PATCH 18/39] chore(plugin-doc-coverage): stuff of the pr and rename file --- ...mock.ts => source-files-mock.generator.ts} | 0 .../plugin-doc-coverage/src/lib/config.ts | 17 +- .../src/lib/config.unit.test.ts | 156 +++++++++--------- .../src/lib/runner/doc-processer.unit.test.ts | 5 +- 4 files changed, 96 insertions(+), 82 deletions(-) rename packages/plugin-doc-coverage/mocks/{source-files.mock.ts => source-files-mock.generator.ts} (100%) diff --git a/packages/plugin-doc-coverage/mocks/source-files.mock.ts b/packages/plugin-doc-coverage/mocks/source-files-mock.generator.ts similarity index 100% rename from packages/plugin-doc-coverage/mocks/source-files.mock.ts rename to packages/plugin-doc-coverage/mocks/source-files-mock.generator.ts diff --git a/packages/plugin-doc-coverage/src/lib/config.ts b/packages/plugin-doc-coverage/src/lib/config.ts index 648420a1d..1204ca7f7 100644 --- a/packages/plugin-doc-coverage/src/lib/config.ts +++ b/packages/plugin-doc-coverage/src/lib/config.ts @@ -2,11 +2,22 @@ import { z } from 'zod'; export const docCoveragePluginConfigSchema = z .object({ - skipAudits: z.array(z.string()).optional(), - onlyAudits: z.array(z.string()).optional(), + skipAudits: z + .array(z.string()) + .optional() + .describe( + 'List of audit slugs to exclude from evaluation. When specified, all audits except these will be evaluated.', + ), + onlyAudits: z + .array(z.string()) + .optional() + .describe( + 'List of audit slugs to evaluate. When specified, only these audits will be evaluated.', + ), sourceGlob: z .array(z.string()) - .default(['src/**/*.{ts,tsx}', '!**/*.spec.ts', '!**/*.test.ts']), + .default(['src/**/*.{ts,tsx}', '!**/*.spec.ts', '!**/*.test.ts']) + .describe('Glob pattern to match source files to evaluate.'), }) .refine(data => !(data.skipAudits && data.onlyAudits), { message: "You can't define 'skipAudits' and 'onlyAudits' simultaneously", diff --git a/packages/plugin-doc-coverage/src/lib/config.unit.test.ts b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts index 1d28d2a7d..f9f777d15 100644 --- a/packages/plugin-doc-coverage/src/lib/config.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts @@ -5,6 +5,15 @@ import { } from './config.js'; describe('docCoveragePluginConfigSchema', () => { + it('accepts a complete valid configuration', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + sourceGlob: ['src/**/*.ts'], + onlyAudits: ['functions-coverage'], + } satisfies DocCoveragePluginConfig), + ).not.toThrow(); + }); + it('throws when skipAudits and onlyAudits are defined', () => { expect(() => docCoveragePluginConfigSchema.parse({ @@ -13,100 +22,91 @@ describe('docCoveragePluginConfigSchema', () => { }), ).toThrow("You can't define 'skipAudits' and 'onlyAudits' simultaneously"); }); +}); - describe('sourceGlob', () => { - it('accepts a valid source glob pattern', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - sourceGlob: ['src/**/*.{ts,tsx}', '!**/*.spec.ts', '!**/*.test.ts'], - } satisfies DocCoveragePluginConfig), - ).not.toThrow(); - }); +describe('sourceGlob', () => { + it('accepts a valid source glob pattern', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + sourceGlob: ['src/**/*.{ts,tsx}', '!**/*.spec.ts', '!**/*.test.ts'], + } satisfies DocCoveragePluginConfig), + ).not.toThrow(); + }); - it('uses default value for missing sourceGlob', () => { - const result = docCoveragePluginConfigSchema.parse({}); - expect(result.sourceGlob).toEqual([ - 'src/**/*.{ts,tsx}', - '!**/*.spec.ts', - '!**/*.test.ts', - ]); - }); + it('uses default value for missing sourceGlob', () => { + const result = docCoveragePluginConfigSchema.parse({}); + expect(result.sourceGlob).toEqual([ + 'src/**/*.{ts,tsx}', + '!**/*.spec.ts', + '!**/*.test.ts', + ]); + }); - it('throws for invalid sourceGlob type', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - sourceGlob: 123, - }), - ).toThrow('Expected array'); - }); + it('throws for invalid sourceGlob type', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + sourceGlob: 123, + }), + ).toThrow('Expected array'); }); +}); - it('accepts a complete valid configuration', () => { +describe('onlyAudits', () => { + it('accepts valid audit slugs array', () => { expect(() => docCoveragePluginConfigSchema.parse({ + onlyAudits: ['functions-coverage', 'classes-coverage'], sourceGlob: ['src/**/*.ts'], - onlyAudits: ['functions-coverage'], - } satisfies DocCoveragePluginConfig), + }), ).not.toThrow(); }); - describe('onlyAudits', () => { - it('accepts valid audit slugs array', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - onlyAudits: ['functions-coverage', 'classes-coverage'], - sourceGlob: ['src/**/*.ts'], - }), - ).not.toThrow(); - }); - - it('accepts empty array for onlyAudits', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - onlyAudits: [], - sourceGlob: ['src/**/*.ts'], - }), - ).not.toThrow(); - }); + it('accepts empty array for onlyAudits', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + onlyAudits: [], + sourceGlob: ['src/**/*.ts'], + }), + ).not.toThrow(); + }); - it('allows onlyAudits to be undefined', () => { - const result = docCoveragePluginConfigSchema.parse({}); - expect(result.onlyAudits).toBeUndefined(); - }); + it('allows onlyAudits to be undefined', () => { + const result = docCoveragePluginConfigSchema.parse({}); + expect(result.onlyAudits).toBeUndefined(); + }); - it('throws for invalid onlyAudits type', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - onlyAudits: 'functions-coverage', - }), - ).toThrow('Expected array'); - }); + it('throws for invalid onlyAudits type', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + onlyAudits: 'functions-coverage', + }), + ).toThrow('Expected array'); + }); - it('throws for array with non-string elements', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - onlyAudits: [123, true], - }), - ).toThrow('Expected string'); - }); + it('throws for array with non-string elements', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + onlyAudits: [123, true], + }), + ).toThrow('Expected string'); }); +}); - describe('skipAudits', () => { - it('accepts valid audit slugs array', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - skipAudits: ['functions-coverage', 'classes-coverage'], - sourceGlob: ['src/**/*.ts'], - }), - ).not.toThrow(); - }); +describe('skipAudits', () => { + it('accepts valid audit slugs array', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + skipAudits: ['functions-coverage', 'classes-coverage'], + sourceGlob: ['src/**/*.ts'], + }), + ).not.toThrow(); + }); - it('throws for array with non-string elements', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - skipAudits: [123, true], - }), - ).toThrow('Expected string'); - }); + it('throws for array with non-string elements', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + skipAudits: [123, true], + }), + ).toThrow('Expected string'); }); }); diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts index 2c6fc0c17..3db7abbb7 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts @@ -1,5 +1,8 @@ import type { ClassDeclaration, VariableStatement } from 'ts-morph'; -import { nodeMock, sourceFileMock } from '../../../mocks/source-files.mock'; +import { + nodeMock, + sourceFileMock, +} from '../../../mocks/source-files-mock.generator'; import { getClassNodes, getUnprocessedCoverageReport, From 2f2972848b2ff62bdc9ba0079aa67a3a9ebd3d56 Mon Sep 17 00:00:00 2001 From: Alejandro <49059458+aramirezj@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:29:44 +0100 Subject: [PATCH 19/39] Update code-pushup.config.ts Co-authored-by: Michael Hladky <10064416+BioPhoton@users.noreply.github.com> --- code-pushup.config.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/code-pushup.config.ts b/code-pushup.config.ts index a0a33cfee..ebaaf803f 100644 --- a/code-pushup.config.ts +++ b/code-pushup.config.ts @@ -43,8 +43,7 @@ export default mergeConfigs( await docCoverageCoreConfig({ sourceGlob: [ 'packages/**/src/**/*.ts', - '!**/*.spec.ts', - '!**/*.test.ts', + '!**/*.{spec,test}.ts' '!**/implementation/**', '!**/internal/**', ], From adec386a93e67a062017007464213d0e477ad0a7 Mon Sep 17 00:00:00 2001 From: Alejandro <49059458+aramirezj@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:29:53 +0100 Subject: [PATCH 20/39] Update packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts Co-authored-by: Michael Hladky <10064416+BioPhoton@users.noreply.github.com> --- .../src/lib/doc-coverage-plugin.unit.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts index fea278277..adf079fc4 100644 --- a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts @@ -16,7 +16,7 @@ vi.mock('./runner/index.ts', () => ({ })); describe('docCoveragePlugin', () => { - it('should initialise a Documentation coverage plugin', async () => { + it('should create a valid plugin config', async () => { await expect( docCoveragePlugin({ sourceGlob: ['src/**/*.ts', '!**/*.spec.ts', '!**/*.test.ts'], From 47cff4bb8874f85651e246d6dca31fe2f5cd5b70 Mon Sep 17 00:00:00 2001 From: Alejandro <49059458+aramirezj@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:30:00 +0100 Subject: [PATCH 21/39] Update packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts Co-authored-by: Michael Hladky <10064416+BioPhoton@users.noreply.github.com> --- .../src/lib/runner/doc-processer.integration.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts index 0a93347f9..539c3af7d 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts @@ -17,7 +17,7 @@ describe('processDocCoverage', () => { expect(totalNodeCount).toBe(expectedNodeCount); }); - it('should count total nodes from TypeScript files correctly and not include spec files when specified', () => { + it('respect `sourceGlob` and only include matching files', () => { const expectedNodeCount = 7; const results = processDocCoverage({ From c644029b858f1469f5662db1f81c2f751a18847b Mon Sep 17 00:00:00 2001 From: Alejandro <49059458+aramirezj@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:30:21 +0100 Subject: [PATCH 22/39] Update packages/plugin-doc-coverage/src/lib/constants.ts Co-authored-by: Michael Hladky <10064416+BioPhoton@users.noreply.github.com> --- packages/plugin-doc-coverage/src/lib/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-doc-coverage/src/lib/constants.ts b/packages/plugin-doc-coverage/src/lib/constants.ts index d5d0d6b38..ddcc1471f 100644 --- a/packages/plugin-doc-coverage/src/lib/constants.ts +++ b/packages/plugin-doc-coverage/src/lib/constants.ts @@ -12,7 +12,7 @@ export const AUDITS_MAP: Record = { 'methods-coverage': { slug: 'methods-coverage', title: 'Methods coverage', - description: 'Coverage of methods', + description: 'Documentation coverage of methods', }, 'functions-coverage': { slug: 'functions-coverage', From 09d9eb0ff02c34a1d0116071a9f41a5b7591f6cc Mon Sep 17 00:00:00 2001 From: Alejandro <49059458+aramirezj@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:30:48 +0100 Subject: [PATCH 23/39] Update packages/plugin-doc-coverage/src/lib/constants.ts Co-authored-by: Michael Hladky <10064416+BioPhoton@users.noreply.github.com> --- packages/plugin-doc-coverage/src/lib/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-doc-coverage/src/lib/constants.ts b/packages/plugin-doc-coverage/src/lib/constants.ts index ddcc1471f..a09d463a9 100644 --- a/packages/plugin-doc-coverage/src/lib/constants.ts +++ b/packages/plugin-doc-coverage/src/lib/constants.ts @@ -22,7 +22,7 @@ export const AUDITS_MAP: Record = { 'interfaces-coverage': { slug: 'interfaces-coverage', title: 'Interfaces coverage', - description: 'Coverage of interfaces', + description: 'Documentation coverage of interfaces', }, 'variables-coverage': { slug: 'variables-coverage', From 39019550ae05d7d8e8d6088a35cff55b2ceb1ac5 Mon Sep 17 00:00:00 2001 From: Alejandro <49059458+aramirezj@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:30:55 +0100 Subject: [PATCH 24/39] Update packages/plugin-doc-coverage/src/lib/constants.ts Co-authored-by: Michael Hladky <10064416+BioPhoton@users.noreply.github.com> --- packages/plugin-doc-coverage/src/lib/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-doc-coverage/src/lib/constants.ts b/packages/plugin-doc-coverage/src/lib/constants.ts index a09d463a9..0db20d774 100644 --- a/packages/plugin-doc-coverage/src/lib/constants.ts +++ b/packages/plugin-doc-coverage/src/lib/constants.ts @@ -32,7 +32,7 @@ export const AUDITS_MAP: Record = { 'properties-coverage': { slug: 'properties-coverage', title: 'Properties coverage', - description: 'Coverage of properties', + description: 'Documentation coverage of properties', }, 'types-coverage': { slug: 'types-coverage', From 11ad4bbb73a38d1975b33a7a6947d05f508e2001 Mon Sep 17 00:00:00 2001 From: Alejandro <49059458+aramirezj@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:31:03 +0100 Subject: [PATCH 25/39] Update packages/plugin-doc-coverage/src/lib/constants.ts Co-authored-by: Michael Hladky <10064416+BioPhoton@users.noreply.github.com> --- packages/plugin-doc-coverage/src/lib/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-doc-coverage/src/lib/constants.ts b/packages/plugin-doc-coverage/src/lib/constants.ts index 0db20d774..a635afe66 100644 --- a/packages/plugin-doc-coverage/src/lib/constants.ts +++ b/packages/plugin-doc-coverage/src/lib/constants.ts @@ -37,7 +37,7 @@ export const AUDITS_MAP: Record = { 'types-coverage': { slug: 'types-coverage', title: 'Types coverage', - description: 'Coverage of types', + description: 'Documentation coverage of types', }, 'enums-coverage': { slug: 'enums-coverage', From 19d43a504dfc6150e1a11ad2b1adf755898c8780 Mon Sep 17 00:00:00 2001 From: Alejandro <49059458+aramirezj@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:31:13 +0100 Subject: [PATCH 26/39] Update packages/plugin-doc-coverage/src/lib/constants.ts Co-authored-by: Michael Hladky <10064416+BioPhoton@users.noreply.github.com> --- packages/plugin-doc-coverage/src/lib/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-doc-coverage/src/lib/constants.ts b/packages/plugin-doc-coverage/src/lib/constants.ts index a635afe66..55f41d47f 100644 --- a/packages/plugin-doc-coverage/src/lib/constants.ts +++ b/packages/plugin-doc-coverage/src/lib/constants.ts @@ -42,7 +42,7 @@ export const AUDITS_MAP: Record = { 'enums-coverage': { slug: 'enums-coverage', title: 'Enums coverage', - description: 'Coverage of enums', + description: 'Documentation coverage of enums', }, } as const; From 49e5b56e43f82c2c304a9315f8c6358a5906f765 Mon Sep 17 00:00:00 2001 From: Alejandro <49059458+aramirezj@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:31:20 +0100 Subject: [PATCH 27/39] Update packages/plugin-doc-coverage/README.md Co-authored-by: Michael Hladky <10064416+BioPhoton@users.noreply.github.com> --- packages/plugin-doc-coverage/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/plugin-doc-coverage/README.md b/packages/plugin-doc-coverage/README.md index 59a039c9d..08b4cba51 100644 --- a/packages/plugin-doc-coverage/README.md +++ b/packages/plugin-doc-coverage/README.md @@ -11,7 +11,9 @@ It analyzes your codebase and checks for documentation on different code element Measured documentation types are mapped to Code PushUp audits in the following way: -- The value is in range 0-100 and represents the documentation coverage for all passed results (_documented / total_) +- `value`: The value is the number of undocumented nodes -> 4 +- `displayValue`: `${value} undocumented ${type}` -> 4 undocumented functions +- `score`: 0.5 -> total nodes 8 undocumented 4 -> 8/4 - The score is value converted to 0-1 range - Missing documentation is mapped to issues in the audit details (undocumented classes, functions, interfaces, etc.) From 54d162118cffbe7d72cb93319f8ffd5dbb329d7e Mon Sep 17 00:00:00 2001 From: Alejandro <49059458+aramirezj@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:31:29 +0100 Subject: [PATCH 28/39] Update packages/plugin-doc-coverage/README.md Co-authored-by: Michael Hladky <10064416+BioPhoton@users.noreply.github.com> --- packages/plugin-doc-coverage/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-doc-coverage/README.md b/packages/plugin-doc-coverage/README.md index 08b4cba51..842730ab5 100644 --- a/packages/plugin-doc-coverage/README.md +++ b/packages/plugin-doc-coverage/README.md @@ -35,7 +35,7 @@ Measured documentation types are mapped to Code PushUp audits in the following w pnpm add --save-dev @code-pushup/doc-coverage-plugin ``` -3. Add this plugin to the `plugins` array in your Code PushUp CLI config file (e.g. `code-pushup.config.js`). +3. Add this plugin to the `plugins` array in your Code PushUp CLI config file (e.g. `code-pushup.config.ts`). Pass the target files to analyze and optionally specify which types of documentation you want to track. You can skip for example tests by defining in the sourceGlob the path to the tests folder or pattern to match the tests files with the `!` symbol. From b3105aefd455b4f1f151f948ba5b4d6dcb00e8a0 Mon Sep 17 00:00:00 2001 From: Alejandro <49059458+aramirezj@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:31:39 +0100 Subject: [PATCH 29/39] Update packages/plugin-doc-coverage/src/lib/constants.ts Co-authored-by: Michael Hladky <10064416+BioPhoton@users.noreply.github.com> --- packages/plugin-doc-coverage/src/lib/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-doc-coverage/src/lib/constants.ts b/packages/plugin-doc-coverage/src/lib/constants.ts index 55f41d47f..4ea1e9a14 100644 --- a/packages/plugin-doc-coverage/src/lib/constants.ts +++ b/packages/plugin-doc-coverage/src/lib/constants.ts @@ -27,7 +27,7 @@ export const AUDITS_MAP: Record = { 'variables-coverage': { slug: 'variables-coverage', title: 'Variables coverage', - description: 'Coverage of variables', + description: 'Documentation coverage of variables', }, 'properties-coverage': { slug: 'properties-coverage', From 89893a05659d19fd218e4adf4c3df5f1109dfeed Mon Sep 17 00:00:00 2001 From: Alejandro <49059458+aramirezj@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:31:47 +0100 Subject: [PATCH 30/39] Update packages/plugin-doc-coverage/src/lib/constants.ts Co-authored-by: Michael Hladky <10064416+BioPhoton@users.noreply.github.com> --- packages/plugin-doc-coverage/src/lib/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-doc-coverage/src/lib/constants.ts b/packages/plugin-doc-coverage/src/lib/constants.ts index 4ea1e9a14..cbd09b671 100644 --- a/packages/plugin-doc-coverage/src/lib/constants.ts +++ b/packages/plugin-doc-coverage/src/lib/constants.ts @@ -17,7 +17,7 @@ export const AUDITS_MAP: Record = { 'functions-coverage': { slug: 'functions-coverage', title: 'Functions coverage', - description: 'Coverage of functions', + description: 'Documentation coverage of functions', }, 'interfaces-coverage': { slug: 'interfaces-coverage', From 97d32bfc41316c8cf959756f47d4f900dac750fa Mon Sep 17 00:00:00 2001 From: Alejandro <49059458+aramirezj@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:33:50 +0100 Subject: [PATCH 31/39] Update packages/plugin-doc-coverage/src/lib/config.unit.test.ts Co-authored-by: Michael Hladky <10064416+BioPhoton@users.noreply.github.com> --- packages/plugin-doc-coverage/src/lib/config.unit.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-doc-coverage/src/lib/config.unit.test.ts b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts index f9f777d15..fe73aafce 100644 --- a/packages/plugin-doc-coverage/src/lib/config.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts @@ -5,7 +5,7 @@ import { } from './config.js'; describe('docCoveragePluginConfigSchema', () => { - it('accepts a complete valid configuration', () => { + it('accepts a valid configuration', () => { expect(() => docCoveragePluginConfigSchema.parse({ sourceGlob: ['src/**/*.ts'], From 79b6a027058446939d9ab267cdbb79a9f047693b Mon Sep 17 00:00:00 2001 From: Alejandro <49059458+aramirezj@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:34:04 +0100 Subject: [PATCH 32/39] Update packages/plugin-doc-coverage/src/lib/config.unit.test.ts Co-authored-by: Michael Hladky <10064416+BioPhoton@users.noreply.github.com> --- packages/plugin-doc-coverage/src/lib/config.unit.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-doc-coverage/src/lib/config.unit.test.ts b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts index fe73aafce..f58d7fa73 100644 --- a/packages/plugin-doc-coverage/src/lib/config.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts @@ -52,7 +52,7 @@ describe('sourceGlob', () => { }); describe('onlyAudits', () => { - it('accepts valid audit slugs array', () => { + it('accepts a valid `onlyAudits` array', () => { expect(() => docCoveragePluginConfigSchema.parse({ onlyAudits: ['functions-coverage', 'classes-coverage'], From fd3bd5eb9d691ea2b734365795e2ecb43430ed62 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Mon, 23 Dec 2024 12:03:37 +0100 Subject: [PATCH 33/39] chore(plugin-doc-coverage): add js files for integration test, remove unused dependency, new test --- .../mocks/fixtures/react/component.js | 10 + .../mocks/source-files-mock.generator.ts | 2 +- packages/plugin-doc-coverage/package.json | 5 - .../src/lib/config.unit.test.ts | 206 ++++++++++-------- .../src/lib/doc-coverage-plugin.unit.test.ts | 52 ++++- .../doc-processer.unit.test.ts.snap | 2 +- .../runner/doc-processer.integration.test.ts | 37 +++- .../src/lib/runner/doc-processer.ts | 47 ++-- .../src/lib/runner/doc-processer.unit.test.ts | 34 +-- .../src/lib/runner/models.ts | 14 +- .../src/lib/runner/runner.ts | 4 +- .../src/lib/runner/runner.unit.test.ts | 6 +- .../src/lib/runner/utils.ts | 10 +- .../src/lib/runner/utils.unit.test.ts | 4 +- 14 files changed, 266 insertions(+), 167 deletions(-) create mode 100644 packages/plugin-doc-coverage/mocks/fixtures/react/component.js diff --git a/packages/plugin-doc-coverage/mocks/fixtures/react/component.js b/packages/plugin-doc-coverage/mocks/fixtures/react/component.js new file mode 100644 index 000000000..dfa1336e3 --- /dev/null +++ b/packages/plugin-doc-coverage/mocks/fixtures/react/component.js @@ -0,0 +1,10 @@ +function MyComponent() { + return ( +
+

Hello World

+

This is a basic React component

+
+ ); +} + +export default MyComponent; diff --git a/packages/plugin-doc-coverage/mocks/source-files-mock.generator.ts b/packages/plugin-doc-coverage/mocks/source-files-mock.generator.ts index 5a7ba13aa..5d700db3c 100644 --- a/packages/plugin-doc-coverage/mocks/source-files-mock.generator.ts +++ b/packages/plugin-doc-coverage/mocks/source-files-mock.generator.ts @@ -8,7 +8,7 @@ import { TypeAliasDeclaration, VariableStatement, } from 'ts-morph'; -import type { CoverageType } from '../src/lib/runner/models'; +import type { CoverageType } from '../src/lib/runner/models.js'; export function sourceFileMock( file: string, diff --git a/packages/plugin-doc-coverage/package.json b/packages/plugin-doc-coverage/package.json index 4d5fc0c60..20f0b4f52 100644 --- a/packages/plugin-doc-coverage/package.json +++ b/packages/plugin-doc-coverage/package.json @@ -38,10 +38,5 @@ "@code-pushup/models": "0.57.0", "zod": "^3.22.4", "ts-morph": "^24.0.0" - }, - "peerDependenciesMeta": { - "@nx/devkit": { - "optional": true - } } } diff --git a/packages/plugin-doc-coverage/src/lib/config.unit.test.ts b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts index f58d7fa73..83449ecce 100644 --- a/packages/plugin-doc-coverage/src/lib/config.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts @@ -4,109 +4,135 @@ import { docCoveragePluginConfigSchema, } from './config.js'; -describe('docCoveragePluginConfigSchema', () => { - it('accepts a valid configuration', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - sourceGlob: ['src/**/*.ts'], - onlyAudits: ['functions-coverage'], - } satisfies DocCoveragePluginConfig), - ).not.toThrow(); - }); +describe('DocCoveragePlugin Configuration', () => { + describe('docCoveragePluginConfigSchema', () => { + it('accepts a valid configuration', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + sourceGlob: ['src/**/*.ts'], + onlyAudits: ['functions-coverage'], + } satisfies DocCoveragePluginConfig), + ).not.toThrow(); + }); - it('throws when skipAudits and onlyAudits are defined', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - skipAudits: ['functions-coverage'], - onlyAudits: ['classes-coverage'], - }), - ).toThrow("You can't define 'skipAudits' and 'onlyAudits' simultaneously"); + it('throws when skipAudits and onlyAudits are defined', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + skipAudits: ['functions-coverage'], + onlyAudits: ['classes-coverage'], + }), + ).toThrow( + "You can't define 'skipAudits' and 'onlyAudits' simultaneously", + ); + }); }); -}); -describe('sourceGlob', () => { - it('accepts a valid source glob pattern', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - sourceGlob: ['src/**/*.{ts,tsx}', '!**/*.spec.ts', '!**/*.test.ts'], - } satisfies DocCoveragePluginConfig), - ).not.toThrow(); - }); + describe('sourceGlob', () => { + it('accepts a valid source glob pattern', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + sourceGlob: ['src/**/*.{ts,tsx}', '!**/*.spec.ts', '!**/*.test.ts'], + } satisfies DocCoveragePluginConfig), + ).not.toThrow(); + }); - it('uses default value for missing sourceGlob', () => { - const result = docCoveragePluginConfigSchema.parse({}); - expect(result.sourceGlob).toEqual([ - 'src/**/*.{ts,tsx}', - '!**/*.spec.ts', - '!**/*.test.ts', - ]); - }); + it('uses default value for missing sourceGlob', () => { + const result = docCoveragePluginConfigSchema.parse({}); + expect(result.sourceGlob).toEqual([ + 'src/**/*.{ts,tsx}', + '!**/*.spec.ts', + '!**/*.test.ts', + ]); + }); - it('throws for invalid sourceGlob type', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - sourceGlob: 123, - }), - ).toThrow('Expected array'); + it('throws for invalid sourceGlob type', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + sourceGlob: 123, + }), + ).toThrow('Expected array'); + }); }); -}); -describe('onlyAudits', () => { - it('accepts a valid `onlyAudits` array', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - onlyAudits: ['functions-coverage', 'classes-coverage'], - sourceGlob: ['src/**/*.ts'], - }), - ).not.toThrow(); - }); + describe('onlyAudits', () => { + it('accepts a valid `onlyAudits` array', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + onlyAudits: ['functions-coverage', 'classes-coverage'], + sourceGlob: ['src/**/*.ts'], + }), + ).not.toThrow(); + }); - it('accepts empty array for onlyAudits', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - onlyAudits: [], - sourceGlob: ['src/**/*.ts'], - }), - ).not.toThrow(); - }); + it('accepts empty array for onlyAudits', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + onlyAudits: [], + sourceGlob: ['src/**/*.ts'], + }), + ).not.toThrow(); + }); - it('allows onlyAudits to be undefined', () => { - const result = docCoveragePluginConfigSchema.parse({}); - expect(result.onlyAudits).toBeUndefined(); - }); + it('allows onlyAudits to be undefined', () => { + const result = docCoveragePluginConfigSchema.parse({}); + expect(result.onlyAudits).toBeUndefined(); + }); - it('throws for invalid onlyAudits type', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - onlyAudits: 'functions-coverage', - }), - ).toThrow('Expected array'); - }); + it('throws for invalid onlyAudits type', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + onlyAudits: 'functions-coverage', + }), + ).toThrow('Expected array'); + }); - it('throws for array with non-string elements', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - onlyAudits: [123, true], - }), - ).toThrow('Expected string'); + it('throws for array with non-string elements', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + onlyAudits: [123, true], + }), + ).toThrow('Expected string'); + }); }); -}); -describe('skipAudits', () => { - it('accepts valid audit slugs array', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - skipAudits: ['functions-coverage', 'classes-coverage'], - sourceGlob: ['src/**/*.ts'], - }), - ).not.toThrow(); - }); + describe('skipAudits', () => { + it('accepts valid audit slugs array', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + skipAudits: ['functions-coverage', 'classes-coverage'], + sourceGlob: ['src/**/*.ts'], + }), + ).not.toThrow(); + }); + + it('accepts empty array for skipAudits', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + skipAudits: [], + sourceGlob: ['src/**/*.ts'], + }), + ).not.toThrow(); + }); + + it('allows skipAudits to be undefined', () => { + const result = docCoveragePluginConfigSchema.parse({}); + expect(result.skipAudits).toBeUndefined(); + }); + + it('throws for invalid skipAudits type', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + skipAudits: 'functions-coverage', + }), + ).toThrow('Expected array'); + }); - it('throws for array with non-string elements', () => { - expect(() => - docCoveragePluginConfigSchema.parse({ - skipAudits: [123, true], - }), - ).toThrow('Expected string'); + it('throws for array with non-string elements', () => { + expect(() => + docCoveragePluginConfigSchema.parse({ + skipAudits: [123, true], + }), + ).toThrow('Expected string'); + }); }); }); diff --git a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts index adf079fc4..3682d515d 100644 --- a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts @@ -1,18 +1,24 @@ -import { describe, expect, it } from 'vitest'; -import type { RunnerConfig } from '@code-pushup/models'; -import { PLUGIN_SLUG } from './constants.js'; +import { describe, expect, it, vi } from 'vitest'; +import { PLUGIN_SLUG, groups } from './constants.js'; import { PLUGIN_DESCRIPTION, PLUGIN_DOCS_URL, PLUGIN_TITLE, docCoveragePlugin, } from './doc-coverage-plugin.js'; +import { createRunnerFunction } from './runner/runner.js'; +import { + filterAuditsByPluginConfig, + filterGroupsByOnlyAudits, +} from './utils.js'; + +vi.mock('./utils.js', () => ({ + filterAuditsByPluginConfig: vi.fn().mockReturnValue(['mockAudit']), + filterGroupsByOnlyAudits: vi.fn().mockReturnValue(['mockGroup']), +})); -vi.mock('./runner/index.ts', () => ({ - createRunnerConfig: vi.fn().mockReturnValue({ - command: 'node', - outputFile: 'runner-output.json', - } satisfies RunnerConfig), +vi.mock('./runner/runner.js', () => ({ + createRunnerFunction: vi.fn().mockReturnValue(() => Promise.resolve([])), })); describe('docCoveragePlugin', () => { @@ -34,4 +40,34 @@ describe('docCoveragePlugin', () => { }), ); }); + + it('should throw for invalid plugin options', async () => { + await expect( + docCoveragePlugin({ + // @ts-expect-error testing invalid config + sourceGlob: 123, + }), + ).rejects.toThrow('Expected array, received number'); + }); + + it('should filter groups', async () => { + const config = { sourceGlob: ['src/**/*.ts'] }; + await docCoveragePlugin(config); + + expect(filterGroupsByOnlyAudits).toHaveBeenCalledWith(groups, config); + }); + + it('should filter audits', async () => { + const config = { sourceGlob: ['src/**/*.ts'] }; + await docCoveragePlugin(config); + + expect(filterAuditsByPluginConfig).toHaveBeenCalledWith(config); + }); + + it('should forward options to runner function', async () => { + const config = { sourceGlob: ['src/**/*.ts'] }; + await docCoveragePlugin(config); + + expect(createRunnerFunction).toHaveBeenCalledWith(config); + }); }); diff --git a/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.unit.test.ts.snap b/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.unit.test.ts.snap index 1090891fe..3a9c6a965 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.unit.test.ts.snap +++ b/packages/plugin-doc-coverage/src/lib/runner/__snapshots__/doc-processer.unit.test.ts.snap @@ -1,6 +1,6 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`getUnprocessedCoverageReport > should produce a full report 1`] = ` +exports[`getDocumentationReport > should produce a full report 1`] = ` { "classes": { "coverage": 33.33, diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts index 539c3af7d..2f8cb4864 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.integration.test.ts @@ -1,10 +1,9 @@ import { processDocCoverage } from './doc-processer.js'; describe('processDocCoverage', () => { - const sourcePath = - 'packages/plugin-doc-coverage/mocks/fixtures/angular/**/*.ts'; - it('should count total nodes from TypeScript files correctly', () => { + const sourcePath = + 'packages/plugin-doc-coverage/mocks/fixtures/angular/**/*.ts'; const expectedNodeCount = 8; const results = processDocCoverage({ sourceGlob: [sourcePath] }); @@ -17,7 +16,39 @@ describe('processDocCoverage', () => { expect(totalNodeCount).toBe(expectedNodeCount); }); + it('should count total nodes from Javascript files correctly', () => { + const sourcePath = + 'packages/plugin-doc-coverage/mocks/fixtures/react/**/*.js'; + const expectedNodeCount = 1; + + const results = processDocCoverage({ sourceGlob: [sourcePath] }); + + const totalNodeCount = Object.values(results).reduce( + (acc, node) => acc + node.nodesCount, + 0, + ); + + expect(totalNodeCount).toBe(expectedNodeCount); + }); + + it('should count total nodes from Javascript and TypeScript files correctly', () => { + const sourcePath = + 'packages/plugin-doc-coverage/mocks/fixtures/**/*.{js,ts}'; + const expectedNodeCount = 9; + + const results = processDocCoverage({ sourceGlob: [sourcePath] }); + + const totalNodeCount = Object.values(results).reduce( + (acc, node) => acc + node.nodesCount, + 0, + ); + + expect(totalNodeCount).toBe(expectedNodeCount); + }); + it('respect `sourceGlob` and only include matching files', () => { + const sourcePath = + 'packages/plugin-doc-coverage/mocks/fixtures/angular/**/*.ts'; const expectedNodeCount = 7; const results = processDocCoverage({ diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts index d63c1f469..c5bac7f78 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts @@ -6,9 +6,9 @@ import { } from 'ts-morph'; import type { DocCoveragePluginConfig } from '../config.js'; import type { - CoverageReportShape, - CoverageResult, CoverageType, + DocumentationCoverageReport, + DocumentationReport, } from './models.js'; import { calculateCoverage, @@ -43,24 +43,24 @@ export function getVariablesInformation( /** * Processes documentation coverage for TypeScript files in the specified path * @param toInclude - The file path pattern to include for documentation analysis - * @returns {CoverageResult} Object containing coverage statistics and undocumented items + * @returns {DocumentationCoverageReport} Object containing coverage statistics and undocumented items */ export function processDocCoverage( config: DocCoveragePluginConfig, -): CoverageResult { +): DocumentationCoverageReport { const project = new Project(); project.addSourceFilesAtPaths(config.sourceGlob); - return getUnprocessedCoverageReport(project.getSourceFiles()); + return getDocumentationReport(project.getSourceFiles()); } /** - * Gets the unprocessed coverage report from the source files + * Gets the documentation coverage report from the source files * @param sourceFiles - The source files to process - * @returns {CoverageReportShape} The unprocessed coverage report + * @returns {DocumentationCoverageReport} The documentation coverage report */ -export function getUnprocessedCoverageReport( +export function getDocumentationReport( sourceFiles: SourceFile[], -): CoverageResult { +): DocumentationCoverageReport { const unprocessedCoverageReport = sourceFiles.reduce( (coverageReportOfAllFiles, sourceFile) => { const filePath = sourceFile.getFilePath(); @@ -105,7 +105,7 @@ export function getUnprocessedCoverageReport( createEmptyCoverageData(), ); - return mergeCoverageResults( + return mergeDocumentationReports( coverageReportOfAllFiles, coverageReportOfCurrentFile, ); @@ -117,28 +117,29 @@ export function getUnprocessedCoverageReport( } /** - * Merges two coverage results - * @param results - The first empty coverage result - * @param current - The second coverage result - * @returns {CoverageReportShape} The merged coverage result + * Merges two documentation results + * @param accumulatedReport - The first empty documentation result + * @param currentFileReport - The second documentation result + * @returns {DocumentationReport} The merged documentation result */ -export function mergeCoverageResults( - results: CoverageReportShape, - current: Partial, -): CoverageReportShape { +export function mergeDocumentationReports( + accumulatedReport: DocumentationReport, + currentFileReport: Partial, +): DocumentationReport { return Object.fromEntries( - Object.entries(results).map(([key, value]) => { - const node = value as CoverageResult[CoverageType]; + Object.entries(accumulatedReport).map(([key, value]) => { + const node = value as DocumentationCoverageReport[CoverageType]; const type = key as CoverageType; return [ type, { - nodesCount: node.nodesCount + (current[type]?.nodesCount ?? 0), - issues: [...node.issues, ...(current[type]?.issues ?? [])], + nodesCount: + node.nodesCount + (currentFileReport[type]?.nodesCount ?? 0), + issues: [...node.issues, ...(currentFileReport[type]?.issues ?? [])], }, ]; }), - ) as CoverageReportShape; + ) as DocumentationReport; } /** diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts index 3db7abbb7..c8bfe7953 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts @@ -5,15 +5,15 @@ import { } from '../../../mocks/source-files-mock.generator'; import { getClassNodes, - getUnprocessedCoverageReport, + getDocumentationReport, getVariablesInformation, - mergeCoverageResults, + mergeDocumentationReports, } from './doc-processer.js'; -import type { CoverageReportShape } from './models.js'; +import type { DocumentationReport } from './models.js'; -describe('getUnprocessedCoverageReport', () => { +describe('getDocumentationReport', () => { it('should produce a full report', () => { - const results = getUnprocessedCoverageReport([ + const results = getDocumentationReport([ sourceFileMock('test.ts', { functions: { 1: true, 2: true, 3: true }, classes: { 4: false, 5: false, 6: true }, @@ -28,14 +28,14 @@ describe('getUnprocessedCoverageReport', () => { }); it('should accept array of source files', () => { - const results = getUnprocessedCoverageReport([ + const results = getDocumentationReport([ sourceFileMock('test.ts', { functions: { 1: true, 2: true, 3: false } }), ]); expect(results).toBeDefined(); }); it('should count nodes correctly', () => { - const results = getUnprocessedCoverageReport([ + const results = getDocumentationReport([ sourceFileMock('test.ts', { functions: { 1: true, 2: true, 3: false } }), ]); @@ -43,7 +43,7 @@ describe('getUnprocessedCoverageReport', () => { }); it('should collect uncommented nodes issues', () => { - const results = getUnprocessedCoverageReport([ + const results = getDocumentationReport([ sourceFileMock('test.ts', { functions: { 1: true, 2: false, 3: false } }), ]); @@ -51,7 +51,7 @@ describe('getUnprocessedCoverageReport', () => { }); it('should collect valid issues', () => { - const results = getUnprocessedCoverageReport([ + const results = getDocumentationReport([ sourceFileMock('test.ts', { functions: { 1: false } }), ]); @@ -66,7 +66,7 @@ describe('getUnprocessedCoverageReport', () => { }); it('should calculate coverage correctly', () => { - const results = getUnprocessedCoverageReport([ + const results = getDocumentationReport([ sourceFileMock('test.ts', { functions: { 1: true, 2: false } }), ]); @@ -74,8 +74,8 @@ describe('getUnprocessedCoverageReport', () => { }); }); -describe('mergeCoverageResults', () => { - const emptyResult: CoverageReportShape = { +describe('mergeDocumentationReports', () => { + const emptyResult: DocumentationReport = { enums: { nodesCount: 0, issues: [] }, interfaces: { nodesCount: 0, issues: [] }, types: { nodesCount: 0, issues: [] }, @@ -103,9 +103,9 @@ describe('mergeCoverageResults', () => { }, }; - const results = mergeCoverageResults( + const results = mergeDocumentationReports( emptyResult, - secondResult as Partial, + secondResult as Partial, ); expect(results).toStrictEqual( expect.objectContaining({ @@ -118,12 +118,12 @@ describe('mergeCoverageResults', () => { }); it('should merge empty results', () => { - const results = mergeCoverageResults(emptyResult, emptyResult); + const results = mergeDocumentationReports(emptyResult, emptyResult); expect(results).toStrictEqual(emptyResult); }); it('should merge second level property nodesCount', () => { - const results = mergeCoverageResults( + const results = mergeDocumentationReports( { ...emptyResult, enums: { nodesCount: 1, issues: [] }, @@ -136,7 +136,7 @@ describe('mergeCoverageResults', () => { }); it('should merge second level property issues', () => { - const results = mergeCoverageResults( + const results = mergeDocumentationReports( { ...emptyResult, enums: { diff --git a/packages/plugin-doc-coverage/src/lib/runner/models.ts b/packages/plugin-doc-coverage/src/lib/runner/models.ts index 547efab56..d95b7b47b 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/models.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/models.ts @@ -25,19 +25,19 @@ export type UndocumentedNode = { class?: string; }; -/** The coverage data is the data that is used to create the coverage report. Without coverage stats. */ -export type CoverageData = { +/** The documentation data has the issues and the total nodes count from a specific CoverageType. */ +export type DocumentationData = { issues: UndocumentedNode[]; nodesCount: number; }; -/** The coverage report shape the report of every CoverageType without coverage stats. */ -export type CoverageReportShape = Record; +/** The documentation report has all the documentation data for each coverage type. */ +export type DocumentationReport = Record; -/** The processed coverage result CoverageData but for each coverage type and with coverage stats. */ -export type CoverageResult = Record< +/** The processed documentation result has the documentation data for each coverage type and with coverage stats. */ +export type DocumentationCoverageReport = Record< CoverageType, - CoverageData & { + DocumentationData & { coverage: number; } >; diff --git a/packages/plugin-doc-coverage/src/lib/runner/runner.ts b/packages/plugin-doc-coverage/src/lib/runner/runner.ts index 1019687e8..b50fbdc4f 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/runner.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/runner.ts @@ -1,7 +1,7 @@ import type { AuditOutputs, RunnerFunction } from '@code-pushup/models'; import type { DocCoveragePluginConfig } from '../config.js'; import { processDocCoverage } from './doc-processer.js'; -import type { CoverageResult } from './models.js'; +import type { DocumentationCoverageReport } from './models.js'; export function createRunnerFunction( config: DocCoveragePluginConfig, @@ -19,7 +19,7 @@ export function createRunnerFunction( * @returns Audit outputs with coverage scores and details about undocumented items */ export function trasformCoverageReportToAudits( - coverageResult: CoverageResult, + coverageResult: DocumentationCoverageReport, options: Pick, ): AuditOutputs { return Object.entries(coverageResult) diff --git a/packages/plugin-doc-coverage/src/lib/runner/runner.unit.test.ts b/packages/plugin-doc-coverage/src/lib/runner/runner.unit.test.ts index aab40d08c..081df241f 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/runner.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/runner.unit.test.ts @@ -1,4 +1,4 @@ -import type { CoverageResult } from './models.js'; +import type { DocumentationCoverageReport } from './models.js'; import { trasformCoverageReportToAudits } from './runner.js'; describe('trasformCoverageReportToAudits', () => { @@ -27,7 +27,7 @@ describe('trasformCoverageReportToAudits', () => { }, ], }, - } as unknown as CoverageResult; + } as unknown as DocumentationCoverageReport; it('should return all audits from the coverage result when no filters are provided', () => { const result = trasformCoverageReportToAudits(mockCoverageResult, {}); @@ -55,7 +55,7 @@ describe('trasformCoverageReportToAudits', () => { it('should handle properly empty coverage result', () => { const result = trasformCoverageReportToAudits( - {} as unknown as CoverageResult, + {} as unknown as DocumentationCoverageReport, {}, ); expect(result).toEqual([]); diff --git a/packages/plugin-doc-coverage/src/lib/runner/utils.ts b/packages/plugin-doc-coverage/src/lib/runner/utils.ts index cdc202991..298b64806 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/utils.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/utils.ts @@ -1,15 +1,15 @@ import { SyntaxKind } from 'ts-morph'; import type { - CoverageReportShape, - CoverageResult, CoverageType, + DocumentationCoverageReport, + DocumentationReport, } from './models.js'; /** * Creates an empty unprocessed coverage report. * @returns The empty unprocessed coverage report. */ -export function createEmptyCoverageData(): CoverageReportShape { +export function createEmptyCoverageData(): DocumentationReport { return { enums: { nodesCount: 0, issues: [] }, interfaces: { nodesCount: 0, issues: [] }, @@ -27,7 +27,7 @@ export function createEmptyCoverageData(): CoverageReportShape { * @param result - The unprocessed coverage result. * @returns The processed coverage result. */ -export function calculateCoverage(result: CoverageReportShape) { +export function calculateCoverage(result: DocumentationReport) { return Object.fromEntries( Object.entries(result).map(([key, value]) => { const type = key as CoverageType; @@ -47,7 +47,7 @@ export function calculateCoverage(result: CoverageReportShape) { }, ]; }), - ) as CoverageResult; + ) as DocumentationCoverageReport; } /** diff --git a/packages/plugin-doc-coverage/src/lib/runner/utils.unit.test.ts b/packages/plugin-doc-coverage/src/lib/runner/utils.unit.test.ts index d45b1600c..fcf8e2f33 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/utils.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/utils.unit.test.ts @@ -1,5 +1,5 @@ import { SyntaxKind } from 'ts-morph'; -import type { CoverageReportShape } from './models.js'; +import type { DocumentationReport } from './models.js'; import { calculateCoverage, createEmptyCoverageData, @@ -36,7 +36,7 @@ describe('calculateCoverage', () => { }); it('should calculate correct coverage percentage with issues', () => { - const input: CoverageReportShape = { + const input: DocumentationReport = { ...createEmptyCoverageData(), functions: { nodesCount: 4, From 0ecc1931245cdcc8a91986f439ebd3df713088d0 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Mon, 23 Dec 2024 15:43:57 +0100 Subject: [PATCH 34/39] chore: run nx format --- code-pushup.config.ts | 2 +- packages/plugin-doc-coverage/README.md | 2 +- tsconfig.base.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code-pushup.config.ts b/code-pushup.config.ts index ebaaf803f..e511a1d76 100644 --- a/code-pushup.config.ts +++ b/code-pushup.config.ts @@ -43,7 +43,7 @@ export default mergeConfigs( await docCoverageCoreConfig({ sourceGlob: [ 'packages/**/src/**/*.ts', - '!**/*.{spec,test}.ts' + '!**/*.{spec,test}.ts', '!**/implementation/**', '!**/internal/**', ], diff --git a/packages/plugin-doc-coverage/README.md b/packages/plugin-doc-coverage/README.md index 842730ab5..ca54ce31f 100644 --- a/packages/plugin-doc-coverage/README.md +++ b/packages/plugin-doc-coverage/README.md @@ -11,7 +11,7 @@ It analyzes your codebase and checks for documentation on different code element Measured documentation types are mapped to Code PushUp audits in the following way: -- `value`: The value is the number of undocumented nodes -> 4 +- `value`: The value is the number of undocumented nodes -> 4 - `displayValue`: `${value} undocumented ${type}` -> 4 undocumented functions - `score`: 0.5 -> total nodes 8 undocumented 4 -> 8/4 - The score is value converted to 0-1 range diff --git a/tsconfig.base.json b/tsconfig.base.json index c5cddb98c..026003f3e 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -24,10 +24,10 @@ "@code-pushup/cli": ["packages/cli/src/index.ts"], "@code-pushup/core": ["packages/core/src/index.ts"], "@code-pushup/coverage-plugin": ["packages/plugin-coverage/src/index.ts"], - "@code-pushup/eslint-plugin": ["packages/plugin-eslint/src/index.ts"], "@code-pushup/doc-coverage-plugin": [ "packages/plugin-doc-coverage/src/index.ts" ], + "@code-pushup/eslint-plugin": ["packages/plugin-eslint/src/index.ts"], "@code-pushup/js-packages-plugin": [ "packages/plugin-js-packages/src/index.ts" ], From 7e0acbdf402856c49268e43d8540119508e570eb Mon Sep 17 00:00:00 2001 From: Alejandro Date: Mon, 23 Dec 2024 16:02:43 +0100 Subject: [PATCH 35/39] chore(plugin-doc-coverage): improve readme and audits --- packages/plugin-doc-coverage/README.md | 67 ++++++++++++++----- .../src/lib/runner/runner.ts | 6 +- 2 files changed, 55 insertions(+), 18 deletions(-) diff --git a/packages/plugin-doc-coverage/README.md b/packages/plugin-doc-coverage/README.md index ca54ce31f..78f30ace6 100644 --- a/packages/plugin-doc-coverage/README.md +++ b/packages/plugin-doc-coverage/README.md @@ -37,15 +37,6 @@ Measured documentation types are mapped to Code PushUp audits in the following w 3. Add this plugin to the `plugins` array in your Code PushUp CLI config file (e.g. `code-pushup.config.ts`). - Pass the target files to analyze and optionally specify which types of documentation you want to track. - You can skip for example tests by defining in the sourceGlob the path to the tests folder or pattern to match the tests files with the `!` symbol. - All documentation types are measured by default. - If you wish to focus on a subset of offered types, define them in `onlyAudits`. - Also you can skip some types by defining them in `skipAudits`. - You can only define or `onlyAudits` or `skipAudits`, not both. - - The configuration will look similarly to the following: - ```js import docCoveragePlugin from '@code-pushup/doc-coverage-plugin'; @@ -92,9 +83,16 @@ Measured documentation types are mapped to Code PushUp audits in the following w Documentation coverage is a metric that indicates what percentage of your code elements have proper documentation. It helps ensure your codebase is well-documented and maintainable. -The plugin provides a single audit that measures the overall percentage of documentation coverage across your codebase: +The plugin provides multiple audits, one for each documentation type (classes, functions, interfaces, etc.), and groups them together for an overall documentation coverage measurement. Each audit: + +- Measures the documentation coverage for its specific type (e.g., classes, functions) +- Provides a score based on the percentage of documented elements +- Includes details about which elements are missing documentation -- **Percentage coverage**: Measures how many percent of the codebase have documentation. +These audits are grouped together to provide a comprehensive view of your codebase's documentation status. You can use either: + +- The complete group of audits for overall documentation coverage +- Individual audits to focus on specific documentation types ## Plugin architecture @@ -102,10 +100,49 @@ The plugin provides a single audit that measures the overall percentage of docum The plugin accepts the following parameters: -- (optional) `coverageToolCommand`: If you wish to run your documentation coverage tool (compodoc) to generate the results first, you may define it here. - - `command`: Command to run coverage tool (e.g. `npx`). - - `args`: Arguments to be passed to the coverage tool (e.g. `['compodoc', '-p', 'tsconfig.doc.json', '-e', 'json']`). -- `outputPath`: Path to the documentation.json file. Defaults to `'documentation/documentation.json'`. +#### SourceGlob + +Required parameter. The `sourceGlob` option accepts an array of strings that define patterns to include or exclude files. You can use glob patterns to match files and the `!` symbol to exclude specific patterns. Example: + +```js +await docCoveragePlugin({ + sourceGlob: [ + 'src/**/*.ts', // include all TypeScript files in src + '!src/**/*.{spec,test}.ts', // exclude test files + '!src/**/testing/**/*.ts' // exclude testing utilities + ], +}), +``` + +#### OnlyAudits + +Optional parameter. The `onlyAudits` option allows you to specify which documentation types you want to measure. Only the specified audits will be included in the results. Example: + +```js +await docCoveragePlugin({ + sourceGlob: ['src/**/*.ts'], + onlyAudits: [ + 'classes-coverage', + 'functions-coverage' + ] // Only measure documentation for classes and functions +}), +``` + +#### SkipAudits + +Optional parameter. The `skipAudits` option allows you to exclude specific documentation types from measurement. All other types will be included in the results. + +```js +await docCoveragePlugin({ + sourceGlob: ['src/**/*.ts'], + skipAudits: [ + 'variables-coverage', + 'interfaces-coverage' + ] // Measure all documentation types except variables and interfaces +}), +``` + +> ⚠️ **Warning:** You cannot use both `onlyAudits` and `skipAudits` in the same configuration. Choose the one that better suits your needs. ### Audits and group diff --git a/packages/plugin-doc-coverage/src/lib/runner/runner.ts b/packages/plugin-doc-coverage/src/lib/runner/runner.ts index b50fbdc4f..8c059aac7 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/runner.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/runner.ts @@ -34,13 +34,13 @@ export function trasformCoverageReportToAudits( return true; }) .map(([type, item]) => { - const { coverage } = item; + const { coverage, issues } = item; return { slug: `${type}-coverage`, - value: coverage, + value: issues.length, score: coverage / 100, - displayValue: `${coverage} %`, + displayValue: `${issues.length} undocumented ${type}`, details: { issues: item.issues.map(({ file, line }) => ({ message: 'Missing documentation', From 24468ccc1b3862e7c318ca36bafffaff8f1e5a25 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Mon, 23 Dec 2024 19:08:00 +0100 Subject: [PATCH 36/39] chore(plugin-doc-coverage): remove unused async --- packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts index d452839d6..578c0c490 100644 --- a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts +++ b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts @@ -36,9 +36,9 @@ export const PLUGIN_DOCS_URL = * * @returns Plugin configuration. */ -export async function docCoveragePlugin( +export function docCoveragePlugin( config: DocCoveragePluginConfig, -): Promise { +): PluginConfig { const docCoverageConfig = docCoveragePluginConfigSchema.parse(config); return { From ef77df0007feaed0a0afb05bdca8bb602deda327 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Mon, 23 Dec 2024 19:53:25 +0100 Subject: [PATCH 37/39] chore(plugin-doc-coverage): fix tests, fully remove unnecessary await --- code-pushup.preset.ts | 2 +- packages/plugin-doc-coverage/README.md | 8 ++--- .../src/lib/config.unit.test.ts | 2 +- .../src/lib/doc-coverage-plugin.ts | 4 +-- .../src/lib/doc-coverage-plugin.unit.test.ts | 20 +++++------ .../src/lib/runner/doc-processer.ts | 26 +++++++------- .../src/lib/runner/doc-processer.unit.test.ts | 35 +++++++++++++++++++ 7 files changed, 67 insertions(+), 30 deletions(-) diff --git a/code-pushup.preset.ts b/code-pushup.preset.ts index 1060dd1e9..c7f907f03 100644 --- a/code-pushup.preset.ts +++ b/code-pushup.preset.ts @@ -144,7 +144,7 @@ export const docCoverageCoreConfig = async ( config: DocCoveragePluginConfig, ): Promise => { return { - plugins: [await docCoveragePlugin(config)], + plugins: [docCoveragePlugin(config)], categories: getDocCoverageCategories(config), }; }; diff --git a/packages/plugin-doc-coverage/README.md b/packages/plugin-doc-coverage/README.md index 78f30ace6..d41ba4576 100644 --- a/packages/plugin-doc-coverage/README.md +++ b/packages/plugin-doc-coverage/README.md @@ -44,7 +44,7 @@ Measured documentation types are mapped to Code PushUp audits in the following w // ... plugins: [ // ... - await docCoveragePlugin({ + docCoveragePlugin({ sourceGlob: ['**/*.ts'], }), ], @@ -105,7 +105,7 @@ The plugin accepts the following parameters: Required parameter. The `sourceGlob` option accepts an array of strings that define patterns to include or exclude files. You can use glob patterns to match files and the `!` symbol to exclude specific patterns. Example: ```js -await docCoveragePlugin({ +docCoveragePlugin({ sourceGlob: [ 'src/**/*.ts', // include all TypeScript files in src '!src/**/*.{spec,test}.ts', // exclude test files @@ -119,7 +119,7 @@ await docCoveragePlugin({ Optional parameter. The `onlyAudits` option allows you to specify which documentation types you want to measure. Only the specified audits will be included in the results. Example: ```js -await docCoveragePlugin({ +docCoveragePlugin({ sourceGlob: ['src/**/*.ts'], onlyAudits: [ 'classes-coverage', @@ -133,7 +133,7 @@ await docCoveragePlugin({ Optional parameter. The `skipAudits` option allows you to exclude specific documentation types from measurement. All other types will be included in the results. ```js -await docCoveragePlugin({ +docCoveragePlugin({ sourceGlob: ['src/**/*.ts'], skipAudits: [ 'variables-coverage', diff --git a/packages/plugin-doc-coverage/src/lib/config.unit.test.ts b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts index 83449ecce..4d4b4f1ea 100644 --- a/packages/plugin-doc-coverage/src/lib/config.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/config.unit.test.ts @@ -91,7 +91,7 @@ describe('DocCoveragePlugin Configuration', () => { docCoveragePluginConfigSchema.parse({ onlyAudits: [123, true], }), - ).toThrow('Expected string'); + ).toThrow('Expected string, received number'); }); }); diff --git a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts index 578c0c490..dc594a303 100644 --- a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts +++ b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.ts @@ -28,8 +28,8 @@ export const PLUGIN_DOCS_URL = * // ... core config ... * plugins: [ * // ... other plugins ... - * await docCoveragePlugin({ - * sourceGlob: 'src/**/*.{ts,tsx}', + * docCoveragePlugin({ + * sourceGlob: ['src/**/*.{ts,tsx}'] * }) * ] * } diff --git a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts index 3682d515d..3fda06e71 100644 --- a/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/doc-coverage-plugin.unit.test.ts @@ -22,12 +22,12 @@ vi.mock('./runner/runner.js', () => ({ })); describe('docCoveragePlugin', () => { - it('should create a valid plugin config', async () => { - await expect( + it('should create a valid plugin config', () => { + expect( docCoveragePlugin({ sourceGlob: ['src/**/*.ts', '!**/*.spec.ts', '!**/*.test.ts'], }), - ).resolves.toStrictEqual( + ).toStrictEqual( expect.objectContaining({ slug: PLUGIN_SLUG, title: PLUGIN_TITLE, @@ -41,32 +41,32 @@ describe('docCoveragePlugin', () => { ); }); - it('should throw for invalid plugin options', async () => { - await expect( + it('should throw for invalid plugin options', () => { + expect(() => docCoveragePlugin({ // @ts-expect-error testing invalid config sourceGlob: 123, }), - ).rejects.toThrow('Expected array, received number'); + ).toThrow('Expected array, received number'); }); - it('should filter groups', async () => { + it('should filter groups', () => { const config = { sourceGlob: ['src/**/*.ts'] }; - await docCoveragePlugin(config); + docCoveragePlugin(config); expect(filterGroupsByOnlyAudits).toHaveBeenCalledWith(groups, config); }); it('should filter audits', async () => { const config = { sourceGlob: ['src/**/*.ts'] }; - await docCoveragePlugin(config); + docCoveragePlugin(config); expect(filterAuditsByPluginConfig).toHaveBeenCalledWith(config); }); it('should forward options to runner function', async () => { const config = { sourceGlob: ['src/**/*.ts'] }; - await docCoveragePlugin(config); + docCoveragePlugin(config); expect(createRunnerFunction).toHaveBeenCalledWith(config); }); diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts index c5bac7f78..82be50608 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.ts @@ -53,6 +53,19 @@ export function processDocCoverage( return getDocumentationReport(project.getSourceFiles()); } +export function getAllNodesFromASourceFile(sourceFile: SourceFile) { + const classes = sourceFile.getClasses(); + return [ + ...sourceFile.getFunctions(), + ...classes, + ...getClassNodes(classes), + ...sourceFile.getTypeAliases(), + ...sourceFile.getEnums(), + ...sourceFile.getInterfaces(), + ...getVariablesInformation(sourceFile.getVariableStatements()), + ]; +} + /** * Gets the documentation coverage report from the source files * @param sourceFiles - The source files to process @@ -64,23 +77,12 @@ export function getDocumentationReport( const unprocessedCoverageReport = sourceFiles.reduce( (coverageReportOfAllFiles, sourceFile) => { const filePath = sourceFile.getFilePath(); - const classes = sourceFile.getClasses(); - - const allNodesFromFile = [ - ...sourceFile.getFunctions(), - ...classes, - ...getClassNodes(classes), - ...sourceFile.getTypeAliases(), - ...sourceFile.getEnums(), - ...sourceFile.getInterfaces(), - ...getVariablesInformation(sourceFile.getVariableStatements()), - ]; + const allNodesFromFile = getAllNodesFromASourceFile(sourceFile); const coverageReportOfCurrentFile = allNodesFromFile.reduce( (acc, node) => { const nodeType = getCoverageTypeFromKind(node.getKind()); const currentTypeReport = acc[nodeType]; - const updatedIssues = node.getJsDocs().length === 0 ? [ diff --git a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts index c8bfe7953..8cf66f43f 100644 --- a/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts +++ b/packages/plugin-doc-coverage/src/lib/runner/doc-processer.unit.test.ts @@ -4,6 +4,7 @@ import { sourceFileMock, } from '../../../mocks/source-files-mock.generator'; import { + getAllNodesFromASourceFile, getClassNodes, getDocumentationReport, getVariablesInformation, @@ -276,3 +277,37 @@ describe('getVariablesInformation', () => { expect(result).toHaveLength(0); }); }); + +describe('getAllNodesFromASourceFile', () => { + it('should combine all node types from a source file', () => { + const mockSourceFile = sourceFileMock('test.ts', { + functions: { 1: true }, + classes: { 2: false }, + types: { 3: true }, + enums: { 4: false }, + interfaces: { 5: true }, + }); + + const result = getAllNodesFromASourceFile(mockSourceFile); + + expect(result).toHaveLength(5); + }); + + it('should handle empty source file', () => { + const mockSourceFile = sourceFileMock('empty.ts', {}); + + const result = getAllNodesFromASourceFile(mockSourceFile); + + expect(result).toHaveLength(0); + }); + + it('should handle source file with only functions', () => { + const mockSourceFile = sourceFileMock('functions.ts', { + functions: { 1: true, 2: false, 3: true }, + }); + + const result = getAllNodesFromASourceFile(mockSourceFile); + + expect(result).toHaveLength(3); + }); +}); From b09aa579b01682e6ea2eb6e885d687a3de2d01e6 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Mon, 23 Dec 2024 22:26:10 +0100 Subject: [PATCH 38/39] chore: fix scope of sourceGlob --- code-pushup.config.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code-pushup.config.ts b/code-pushup.config.ts index e511a1d76..c891c7a99 100644 --- a/code-pushup.config.ts +++ b/code-pushup.config.ts @@ -43,10 +43,11 @@ export default mergeConfigs( await docCoverageCoreConfig({ sourceGlob: [ 'packages/**/src/**/*.ts', + '!packages/**/node_modules', + '!packages/**/{mocks,mocks}', '!**/*.{spec,test}.ts', '!**/implementation/**', '!**/internal/**', ], - skipAudits: ['methods-coverage'], }), ); From b9f5333a1e99b49656a5e1b3a7c48046648b7583 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Mon, 23 Dec 2024 22:26:30 +0100 Subject: [PATCH 39/39] fix: mock word --- code-pushup.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code-pushup.config.ts b/code-pushup.config.ts index c891c7a99..b48295452 100644 --- a/code-pushup.config.ts +++ b/code-pushup.config.ts @@ -44,7 +44,7 @@ export default mergeConfigs( sourceGlob: [ 'packages/**/src/**/*.ts', '!packages/**/node_modules', - '!packages/**/{mocks,mocks}', + '!packages/**/{mocks,mock}', '!**/*.{spec,test}.ts', '!**/implementation/**', '!**/internal/**',