UNPKG

projen

Version:

CDK for software projects

283 lines (275 loc) • 39.3 kB
"use strict"; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.AwsCdkTypeScriptApp = void 0; const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti"); const fs = require("fs"); const path = require("path"); const component_1 = require("../component"); const dependencies_1 = require("../dependencies"); const javascript_1 = require("../javascript"); const typescript_1 = require("../typescript"); const util_1 = require("../util"); const auto_discover_1 = require("./auto-discover"); const awscdk_deps_js_1 = require("./awscdk-deps-js"); const cdk_config_1 = require("./cdk-config"); const cdk_tasks_1 = require("./cdk-tasks"); const integ_runner_1 = require("./integ-runner"); /** * AWS CDK app in TypeScript * * @pjid awscdk-app-ts */ class AwsCdkTypeScriptApp extends typescript_1.TypeScriptAppProject { /** * The CDK version this app is using. */ get cdkVersion() { return this.cdkDeps.cdkVersion; } constructor(options) { // CDK default compiler options const cdkDefaultCompilerOptions = { target: "ES2022", module: "NodeNext", moduleResolution: javascript_1.TypeScriptModuleResolution.NODE_NEXT, lib: ["es2022"], declaration: true, strict: true, noImplicitAny: true, strictNullChecks: true, noImplicitThis: true, alwaysStrict: true, noUnusedLocals: false, noUnusedParameters: false, noImplicitReturns: true, noFallthroughCasesInSwitch: false, inlineSourceMap: true, inlineSources: true, experimentalDecorators: true, strictPropertyInitialization: false, typeRoots: ["./node_modules/@types"], }; let finalCompilerOptions = cdkDefaultCompilerOptions; if (options.tsconfig?.compilerOptions) { // Deep merge user's `compilerOptions` onto CDK-specific defaults. finalCompilerOptions = (0, util_1.deepMerge)([cdkDefaultCompilerOptions, options.tsconfig.compilerOptions], { destructive: true }); } // CDK default exclude const cdkDefaultExclude = ["node_modules", "cdk.out"]; let finalExclude = cdkDefaultExclude; if (options.tsconfig?.exclude) { // Merge and deduplicate user's `exclude` with CDK-specific defaults. finalExclude = [ ...new Set([...cdkDefaultExclude, ...options.tsconfig.exclude]), ]; } /** * The final `tsconfig` object passed to the superclass. * It incorporates AWS CDK defaults (derived from `cdkDefaultCompilerOptions` and `cdkDefaultExclude` above) * and any user-provided overrides. The aim is to align with the standard CDK `tsconfig.json`: * @see https://github.com/aws/aws-cdk-cli/blob/main/packages/aws-cdk/lib/init-templates/app/typescript/tsconfig.json */ const tsconfigToSuper = { ...options.tsconfig, // Pass through any other top-level tsconfig options from user compilerOptions: finalCompilerOptions, exclude: finalExclude, }; super({ ...options, sampleCode: false, bundlerOptions: { ...options.bundlerOptions, // we invoke the "bundle" task as part of the build step in cdk.json so // we don't want it to be added to the pre-compile phase. runBundleTask: javascript_1.RunBundleTask.MANUAL, }, tsconfig: tsconfigToSuper, }); this.cdkDeps = new awscdk_deps_js_1.AwsCdkDepsJs(this, { dependencyType: dependencies_1.DependencyType.RUNTIME, ...options, }); this.appEntrypoint = options.appEntrypoint ?? "main.ts"; // CLI this.addDevDeps(`aws-cdk@${this.cdkDeps.cdkCliVersion}`); // no compile step because we do all of it in typescript directly this.compileTask.reset(); this.cdkTasks = new cdk_tasks_1.CdkTasks(this); // add synth to the build this.postCompileTask.spawn(this.cdkTasks.synthSilent); const tsConfigFile = this.tsconfig?.fileName; if (!tsConfigFile) { throw new Error("Expecting tsconfig.json"); } this.cdkConfig = new cdk_config_1.CdkConfig(this, { buildCommand: this.runTaskCommand(this.bundler.bundleTask), watchIncludes: [`${this.srcdir}/**/*.ts`, `${this.testdir}/**/*.ts`], watchExcludes: [ "README.md", "cdk*.json", "**/*.d.ts", "**/*.js", "tsconfig.json", "package*.json", "yarn.lock", "node_modules", ], ...options, app: this.getCdkApp(options), }); this.gitignore.exclude(".parcel-cache/"); this.npmignore?.exclude(`${this.cdkConfig.cdkout}/`); this.npmignore?.exclude(".cdk.staging/"); if (this.tsconfig) { this.tsconfig.exclude.push(this.cdkConfig.cdkout); } this.addDevDeps("ts-node"); if (options.sampleCode ?? true) { new SampleCode(this, this.cdkDeps.cdkMajorVersion); } new auto_discover_1.AutoDiscover(this, { srcdir: this.srcdir, testdir: this.testdir, lambdaOptions: options.lambdaOptions, tsconfigPath: this.tsconfigDev.fileName, cdkDeps: this.cdkDeps, lambdaAutoDiscover: options.lambdaAutoDiscover ?? true, edgeLambdaAutoDiscover: options.edgeLambdaAutoDiscover ?? true, singletonLambdaAutoDiscover: options.singletonLambdaAutoDiscover ?? true, lambdaExtensionAutoDiscover: options.lambdaExtensionAutoDiscover ?? true, integrationTestAutoDiscover: options.integrationTestAutoDiscover ?? true, }); if (options.experimentalIntegRunner) { new integ_runner_1.IntegRunner(this); } } /** * Adds an AWS CDK module dependencies * @param modules The list of modules to depend on */ addCdkDependency(...modules) { return this.cdkDeps.addV1Dependencies(...modules); } getCdkApp(options) { if (options.app && options.appEntrypoint) { throw new Error("Only one of 'app' or 'appEntrypoint' can be specified"); } // prefer an explicitly provided app command if (options.app) { return options.app; } const appEntrypoint = path.posix.join(this.srcdir, this.appEntrypoint); const tsNodeConfig = this.tsconfig?.fileName ? ` -P ${this.tsconfig?.fileName}` : ""; const tsNodeApp = `ts-node${tsNodeConfig} --prefer-ts-exts ${appEntrypoint}`; switch (this.package.packageManager) { case javascript_1.NodePackageManager.BUN: const bunTsConfig = this.tsconfig?.fileName ? ` --tsconfig-override=${this.tsconfig?.fileName}` : ""; const bunEntrypoint = ensureRelativePathPrefix(appEntrypoint); // https://bun.sh/docs/cli/run // bun can run ts files directly return `bun run${bunTsConfig} ${bunEntrypoint}`; case javascript_1.NodePackageManager.PNPM: case javascript_1.NodePackageManager.YARN_CLASSIC: case javascript_1.NodePackageManager.YARN: case javascript_1.NodePackageManager.YARN_BERRY: case javascript_1.NodePackageManager.YARN2: // use npx with also for yarn & pnpm due to reported issues // @see https://github.com/projen/projen/issues/4180 return `npx ${tsNodeApp}`; default: return `npx ${tsNodeApp}`; } } } exports.AwsCdkTypeScriptApp = AwsCdkTypeScriptApp; _a = JSII_RTTI_SYMBOL_1; AwsCdkTypeScriptApp[_a] = { fqn: "projen.awscdk.AwsCdkTypeScriptApp", version: "0.99.34" }; /** * Ensures a path is properly prefixed with './' if it's a relative path * @param {string} filePath - The path to normalize * @returns {string} - The normalized path */ function ensureRelativePathPrefix(filePath) { // If it's already an absolute path, return as is if (path.isAbsolute(filePath)) { return filePath; } // If it already starts with ./ or ../, return as is if (filePath.startsWith("./") || filePath.startsWith("../")) { return filePath; } // Otherwise, add ./ prefix return `./${filePath}`; } class SampleCode extends component_1.Component { constructor(project, cdkMajorVersion) { super(project); this.cdkMajorVersion = cdkMajorVersion; this.appProject = project; } synthesize() { const outdir = this.project.outdir; const srcdir = path.join(outdir, this.appProject.srcdir); if (fs.existsSync(srcdir) && fs.readdirSync(srcdir).filter((x) => x.endsWith(".ts"))) { return; } const srcImports = new Array(); if (this.cdkMajorVersion < 2) { srcImports.push("import { App, Construct, Stack, StackProps } from '@aws-cdk/core';"); } else { srcImports.push("import { App, Stack, StackProps } from 'aws-cdk-lib';"); srcImports.push("import { Construct } from 'constructs';"); } const srcCode = `${srcImports.join("\n")} export class MyStack extends Stack { constructor(scope: Construct, id: string, props: StackProps = {}) { super(scope, id, props); // define resources here... } } // for development, use account/region from cdk cli const devEnv = { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION, }; const app = new App(); new MyStack(app, '${this.project.name}-dev', { env: devEnv }); // new MyStack(app, '${this.project.name}-prod', { env: prodEnv }); app.synth();`; fs.mkdirSync(srcdir, { recursive: true }); fs.writeFileSync(path.join(srcdir, this.appProject.appEntrypoint), srcCode); const testdir = path.join(outdir, this.appProject.testdir); if (fs.existsSync(testdir) && fs.readdirSync(testdir).filter((x) => x.endsWith(".ts"))) { return; } const testImports = new Array(); if (this.cdkMajorVersion < 2) { testImports.push("import { App } from '@aws-cdk/core';"); testImports.push("import { Template } from '@aws-cdk/assertions';"); } else { testImports.push("import { App } from 'aws-cdk-lib';"); testImports.push("import { Template } from 'aws-cdk-lib/assertions';"); } const appEntrypointName = path.basename(this.appProject.appEntrypoint, ".ts"); const testCode = `${testImports.join("\n")} import { MyStack } from '../${this.appProject.srcdir}/${appEntrypointName}'; test('Snapshot', () => { const app = new App(); const stack = new MyStack(app, 'test'); const template = Template.fromStack(stack); expect(template.toJSON()).toMatchSnapshot(); });`; fs.mkdirSync(testdir, { recursive: true }); fs.writeFileSync(path.join(testdir, `${appEntrypointName}.test.ts`), testCode); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXdzY2RrLWFwcC10cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hd3NjZGsvYXdzY2RrLWFwcC10cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLHlCQUF5QjtBQUN6Qiw2QkFBNkI7QUFDN0IsNENBQXlDO0FBQ3pDLGtEQUFpRDtBQUNqRCw4Q0FLdUI7QUFDdkIsOENBQStFO0FBQy9FLGtDQUFvQztBQUNwQyxtREFBK0M7QUFFL0MscURBQWdEO0FBQ2hELDZDQUFpRTtBQUNqRSwyQ0FBdUM7QUFDdkMsaURBQTZDO0FBZ0Y3Qzs7OztHQUlHO0FBQ0gsTUFBYSxtQkFBb0IsU0FBUSxpQ0FBb0I7SUFDM0Q7O09BRUc7SUFDSCxJQUFXLFVBQVU7UUFDbkIsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQztJQUNqQyxDQUFDO0lBbUJELFlBQVksT0FBbUM7UUFDN0MsK0JBQStCO1FBQy9CLE1BQU0seUJBQXlCLEdBQzdCO1lBQ0UsTUFBTSxFQUFFLFFBQVE7WUFDaEIsTUFBTSxFQUFFLFVBQVU7WUFDbEIsZ0JBQWdCLEVBQUUsdUNBQTBCLENBQUMsU0FBUztZQUN0RCxHQUFHLEVBQUUsQ0FBQyxRQUFRLENBQUM7WUFDZixXQUFXLEVBQUUsSUFBSTtZQUNqQixNQUFNLEVBQUUsSUFBSTtZQUNaLGFBQWEsRUFBRSxJQUFJO1lBQ25CLGdCQUFnQixFQUFFLElBQUk7WUFDdEIsY0FBYyxFQUFFLElBQUk7WUFDcEIsWUFBWSxFQUFFLElBQUk7WUFDbEIsY0FBYyxFQUFFLEtBQUs7WUFDckIsa0JBQWtCLEVBQUUsS0FBSztZQUN6QixpQkFBaUIsRUFBRSxJQUFJO1lBQ3ZCLDBCQUEwQixFQUFFLEtBQUs7WUFDakMsZUFBZSxFQUFFLElBQUk7WUFDckIsYUFBYSxFQUFFLElBQUk7WUFDbkIsc0JBQXNCLEVBQUUsSUFBSTtZQUM1Qiw0QkFBNEIsRUFBRSxLQUFLO1lBQ25DLFNBQVMsRUFBRSxDQUFDLHVCQUF1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QU8sRUFBRSxHQUFHLGlCQUFpQixVQUFVLENBQUMsRUFDbEQsUUFBUSxDQUNULENBQUM7SUFDSixDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBmcyBmcm9tIFwiZnNcIjtcbmltcG9ydCAqIGFzIHBhdGggZnJvbSBcInBhdGhcIjtcbmltcG9ydCB7IENvbXBvbmVudCB9IGZyb20gXCIuLi9jb21wb25lbnRcIjtcbmltcG9ydCB7IERlcGVuZGVuY3lUeXBlIH0gZnJvbSBcIi4uL2RlcGVuZGVuY2llc1wiO1xuaW1wb3J0IHtcbiAgTm9kZVBhY2thZ2VNYW5hZ2VyLFxuICBSdW5CdW5kbGVUYXNrLFxuICBUeXBlU2NyaXB0TW9kdWxlUmVzb2x1dGlvbixcbiAgVHlwZXNjcmlwdENvbmZpZ09wdGlvbnMsXG59IGZyb20gXCIuLi9qYXZhc2NyaXB0XCI7XG5pbXBvcnQgeyBUeXBlU2NyaXB0QXBwUHJvamVjdCwgVHlwZVNjcmlwdFByb2plY3RPcHRpb25zIH0gZnJvbSBcIi4uL3R5cGVzY3JpcHRcIjtcbmltcG9ydCB7IGRlZXBNZXJnZSB9IGZyb20gXCIuLi91dGlsXCI7XG5pbXBvcnQgeyBBdXRvRGlzY292ZXIgfSBmcm9tIFwiLi9hdXRvLWRpc2NvdmVyXCI7XG5pbXBvcnQgeyBBd3NDZGtEZXBzLCBBd3NDZGtEZXBzQ29tbW9uT3B0aW9ucyB9IGZyb20gXCIuL2F3c2Nkay1kZXBzXCI7XG5pbXBvcnQgeyBBd3NDZGtEZXBzSnMgfSBmcm9tIFwiLi9hd3NjZGstZGVwcy1qc1wiO1xuaW1wb3J0IHsgQ2RrQ29uZmlnLCBDZGtDb25maWdDb21tb25PcHRpb25zIH0gZnJvbSBcIi4vY2RrLWNvbmZpZ1wiO1xuaW1wb3J0IHsgQ2RrVGFza3MgfSBmcm9tIFwiLi9jZGstdGFza3NcIjtcbmltcG9ydCB7IEludGVnUnVubmVyIH0gZnJvbSBcIi4vaW50ZWctcnVubmVyXCI7XG5pbXBvcnQgeyBMYW1iZGFGdW5jdGlvbkNvbW1vbk9wdGlvbnMgfSBmcm9tIFwiLi9sYW1iZGEtZnVuY3Rpb25cIjtcblxuZXhwb3J0IGludGVyZmFjZSBBd3NDZGtUeXBlU2NyaXB0QXBwT3B0aW9uc1xuICBleHRlbmRzXG4gICAgVHlwZVNjcmlwdFByb2plY3RPcHRpb25zLFxuICAgIENka0NvbmZpZ0NvbW1vbk9wdGlvbnMsXG4gICAgQXdzQ2RrRGVwc0NvbW1vbk9wdGlvbnMge1xuICAvKipcbiAgICogVGhlIENESyBhcHAncyBlbnRyeXBvaW50IChyZWxhdGl2ZSB0byB0aGUgc291cmNlIGRpcmVjdG9yeSwgd2hpY2ggaXNcbiAgICogXCJzcmNcIiBieSBkZWZhdWx0KS5cbiAgICpcbiAgICogQGRlZmF1bHQgXCJtYWluLnRzXCJcbiAgICovXG4gIHJlYWRvbmx5IGFwcEVudHJ5cG9pbnQ/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBUaGUgY29tbWFuZCBsaW5lIHRvIGV4ZWN1dGUgaW4gb3JkZXIgdG8gc3ludGhlc2l6ZSB0aGUgQ0RLIGFwcGxpY2F0aW9uXG4gICAqIChsYW5ndWFnZSBzcGVjaWZpYykuXG4gICAqL1xuICByZWFkb25seSBhcHA/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBBdXRvbWF0aWNhbGx5IGFkZHMgYW4gYGF3c2Nkay5MYW1iZGFGdW5jdGlvbmAgZm9yIGVhY2ggYC5sYW1iZGEudHNgIGhhbmRsZXJcbiAgICogaW4geW91ciBzb3VyY2UgdHJlZS4gSWYgdGhpcyBpcyBkaXNhYmxlZCwgeW91IGNhbiBtYW51YWxseSBhZGQgYW5cbiAgICogYGF3c2Nkay5BdXRvRGlzY292ZXJgIGNvbXBvbmVudCB0byB5b3VyIHByb2plY3QuXG4gICAqXG4gICAqIEBkZWZhdWx0IHRydWVcbiAgICovXG4gIHJlYWRvbmx5IGxhbWJkYUF1dG9EaXNjb3Zlcj86IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIEF1dG9tYXRpY2FsbHkgYWRkcyBhbiBgY2xvdWRmcm9udC5leHBlcmltZW50YWwuRWRnZUZ1bmN0aW9uYCBmb3IgZWFjaFxuICAgKiBgLmVkZ2UtbGFtYmRhLnRzYCBoYW5kbGVyIGluIHlvdXIgc291cmNlIHRyZWUuIElmIHRoaXMgaXMgZGlzYWJsZWQsIHlvdSBjYW5cbiAgICogbWFudWFsbHkgYWRkIGFuIGBhd3NjZGsuQXV0b0Rpc2NvdmVyYCBjb21wb25lbnQgdG8geW91ciBwcm9qZWN0LlxuICAgKlxuICAgKiBAZGVmYXVsdCB0cnVlXG4gICAqL1xuICByZWFkb25seSBlZGdlTGFtYmRhQXV0b0Rpc2NvdmVyPzogYm9vbGVhbjtcblxuICAvKipcbiAgICogQXV0b21hdGljYWxseSBhZGRzIGFuIGBhd3NjZGsuU2luZ2xldG9uRnVuY3Rpb25gIGZvciBlYWNoXG4gICAqIGAuc2luZ2xldG9uLWxhbWJkYS50c2AgaGFuZGxlciBpbiB5b3VyIHNvdXJjZSB0cmVlLiBJZiB0aGlzIGlzIGRpc2FibGVkLCB5b3UgY2FuXG4gICAqIG1hbnVhbGx5IGFkZCBhbiBgYXdzY2RrLkF1dG9EaXNjb3ZlcmAgY29tcG9uZW50IHRvIHlvdXIgcHJvamVjdC5cbiAgICpcbiAgICogQGRlZmF1bHQgdHJ1ZVxuICAgKi9cbiAgcmVhZG9ubHkgc2luZ2xldG9uTGFtYmRhQXV0b0Rpc2NvdmVyPzogYm9vbGVhbjtcblxuICAvKipcbiAgICogQXV0b21hdGljYWxseSBhZGRzIGFuIGBhd3NjZGsuTGFtYmRhRXh0ZW5zaW9uYCBmb3IgZWFjaCBgLmxhbWJkYS1leHRlbnNpb24udHNgXG4gICAqIGVudHJ5cG9pbnQgaW4geW91ciBzb3VyY2UgdHJlZS4gSWYgdGhpcyBpcyBkaXNhYmxlZCwgeW91IGNhbiBtYW51YWxseSBhZGQgYW5cbiAgICogYGF3c2Nkay5BdXRvRGlzY292ZXJgIGNvbXBvbmVudCB0byB5b3VyIHByb2plY3RcbiAgICpcbiAgICogQGRlZmF1bHQgdHJ1ZVxuICAgKi9cbiAgcmVhZG9ubHkgbGFtYmRhRXh0ZW5zaW9uQXV0b0Rpc2NvdmVyPzogYm9vbGVhbjtcblxuICAvKipcbiAgICogQXV0b21hdGljYWxseSBkaXNjb3ZlcnMgYW5kIGNyZWF0ZXMgaW50ZWdyYXRpb24gdGVzdHMgZm9yIGVhY2ggYC5pbnRlZy50c2BcbiAgICogZmlsZSBpbiB1bmRlciB5b3VyIHRlc3QgZGlyZWN0b3J5LlxuICAgKlxuICAgKiBAZGVmYXVsdCB0cnVlXG4gICAqL1xuICByZWFkb25seSBpbnRlZ3JhdGlvblRlc3RBdXRvRGlzY292ZXI/OiBib29sZWFuO1xuXG4gIC8qKlxuICAgKiBFbmFibGUgZXhwZXJpbWVudGFsIHN1cHBvcnQgZm9yIHRoZSBBV1MgQ0RLIGludGVnLXJ1bm5lci5cbiAgICpcbiAgICogQGRlZmF1bHQgZmFsc2VcbiAgICogQGV4cGVyaW1lbnRhbFxuICAgKi9cbiAgcmVhZG9ubHkgZXhwZXJpbWVudGFsSW50ZWdSdW5uZXI/OiBib29sZWFuO1xuXG4gIC8qKlxuICAgKiBDb21tb24gb3B0aW9ucyBmb3IgYWxsIEFXUyBMYW1iZGEgZnVuY3Rpb25zLlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIGRlZmF1bHQgb3B0aW9uc1xuICAgKi9cbiAgcmVhZG9ubHkgbGFtYmRhT3B0aW9ucz86IExhbWJkYUZ1bmN0aW9uQ29tbW9uT3B0aW9ucztcbn1cblxuLyoqXG4gKiBBV1MgQ0RLIGFwcCBpbiBUeXBlU2NyaXB0XG4gKlxuICogQHBqaWQgYXdzY2RrLWFwcC10c1xuICovXG5leHBvcnQgY2xhc3MgQXdzQ2RrVHlwZVNjcmlwdEFwcCBleHRlbmRzIFR5cGVTY3JpcHRBcHBQcm9qZWN0IHtcbiAgLyoqXG4gICAqIFRoZSBDREsgdmVyc2lvbiB0aGlzIGFwcCBpcyB1c2luZy5cbiAgICovXG4gIHB1YmxpYyBnZXQgY2RrVmVyc2lvbigpIHtcbiAgICByZXR1cm4gdGhpcy5jZGtEZXBzLmNka1ZlcnNpb247XG4gIH1cblxuICAvKipcbiAgICogVGhlIENESyBhcHAgZW50cnlwb2ludFxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGFwcEVudHJ5cG9pbnQ6IHN0cmluZztcblxuICAvKipcbiAgICogQ29tbW9uIENESyB0YXNrcy5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBjZGtUYXNrczogQ2RrVGFza3M7XG5cbiAgLyoqXG4gICAqIGNkay5qc29uIGNvbmZpZ3VyYXRpb24uXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgY2RrQ29uZmlnOiBDZGtDb25maWc7XG5cbiAgcHVibGljIHJlYWRvbmx5IGNka0RlcHM6IEF3c0Nka0RlcHM7XG5cbiAgY29uc3RydWN0b3Iob3B0aW9uczogQXdzQ2RrVHlwZVNjcmlwdEFwcE9wdGlvbnMpIHtcbiAgICAvLyBDREsgZGVmYXVsdCBjb21waWxlciBvcHRpb25zXG4gICAgY29uc3QgY2RrRGVmYXVsdENvbXBpbGVyT3B0aW9uczogVHlwZXNjcmlwdENvbmZpZ09wdGlvbnNbXCJjb21waWxlck9wdGlvbnNcIl0gPVxuICAgICAge1xuICAgICAgICB0YXJnZXQ6IFwiRVMyMDIyXCIsXG4gICAgICAgIG1vZHVsZTogXCJOb2RlTmV4dFwiLFxuICAgICAgICBtb2R1bGVSZXNvbHV0aW9uOiBUeXBlU2NyaXB0TW9kdWxlUmVzb2x1dGlvbi5OT0RFX05FWFQsXG4gICAgICAgIGxpYjogW1wiZXMyMDIyXCJdLFxuICAgICAgICBkZWNsYXJhdGlvbjogdHJ1ZSxcbiAgICAgICAgc3RyaWN0OiB0cnVlLFxuICAgICAgICBub0ltcGxpY2l0QW55OiB0cnVlLFxuICAgICAgICBzdHJpY3ROdWxsQ2hlY2tzOiB0cnVlLFxuICAgICAgICBub0ltcGxpY2l0VGhpczogdHJ1ZSxcbiAgICAgICAgYWx3YXlzU3RyaWN0OiB0cnVlLFxuICAgICAgICBub1VudXNlZExvY2FsczogZmFsc2UsXG4gICAgICAgIG5vVW51c2VkUGFyYW1ldGVyczogZmFsc2UsXG4gICAgICAgIG5vSW1wbGljaXRSZXR1cm5zOiB0cnVlLFxuICAgICAgICBub0ZhbGx0aHJvdWdoQ2FzZXNJblN3aXRjaDogZmFsc2UsXG4gICAgICAgIGlubGluZVNvdXJjZU1hcDogdHJ1ZSxcbiAgICAgICAgaW5saW5lU291cmNlczogdHJ1ZSxcbiAgICAgICAgZXhwZXJpbWVudGFsRGVjb3JhdG9yczogdHJ1ZSxcbiAgICAgICAgc3RyaWN0UHJvcGVydHlJbml0aWFsaXphdGlvbjogZmFsc2UsXG4gICAgICAgIHR5cGVSb290czogW1wiLi9ub2RlX21vZHVsZXMvQHR5cGVzXCJdLFxuICAgICAgfTtcblxuICAgIGxldCBmaW5hbENvbXBpbGVyT3B0aW9ucyA9IGNka0RlZmF1bHRDb21waWxlck9wdGlvbnM7XG4gICAgaWYgKG9wdGlvbnMudHNjb25maWc/LmNvbXBpbGVyT3B0aW9ucykge1xuICAgICAgLy8gRGVlcCBtZXJnZSB1c2VyJ3MgYGNvbXBpbGVyT3B0aW9uc2Agb250byBDREstc3BlY2lmaWMgZGVmYXVsdHMuXG4gICAgICBmaW5hbENvbXBpbGVyT3B0aW9ucyA9IGRlZXBNZXJnZShcbiAgICAgICAgW2Nka0RlZmF1bHRDb21waWxlck9wdGlvbnMsIG9wdGlvbnMudHNjb25maWcuY29tcGlsZXJPcHRpb25zXSxcbiAgICAgICAgeyBkZXN0cnVjdGl2ZTogdHJ1ZSB9LFxuICAgICAgKTtcbiAgICB9XG5cbiAgICAvLyBDREsgZGVmYXVsdCBleGNsdWRlXG4gICAgY29uc3QgY2RrRGVmYXVsdEV4Y2x1ZGUgPSBbXCJub2RlX21vZHVsZXNcIiwgXCJjZGsub3V0XCJdO1xuICAgIGxldCBmaW5hbEV4Y2x1ZGUgPSBjZGtEZWZhdWx0RXhjbHVkZTtcbiAgICBpZiAob3B0aW9ucy50c2NvbmZpZz8uZXhjbHVkZSkge1xuICAgICAgLy8gTWVyZ2UgYW5kIGRlZHVwbGljYXRlIHVzZXIncyBgZXhjbHVkZWAgd2l0aCBDREstc3BlY2lmaWMgZGVmYXVsdHMuXG4gICAgICBmaW5hbEV4Y2x1ZGUgPSBbXG4gICAgICAgIC4uLm5ldyBTZXQoWy4uLmNka0RlZmF1bHRFeGNsdWRlLCAuLi5vcHRpb25zLnRzY29uZmlnLmV4Y2x1ZGVdKSxcbiAgICAgIF07XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVGhlIGZpbmFsIGB0c2NvbmZpZ2Agb2JqZWN0IHBhc3NlZCB0byB0aGUgc3VwZXJjbGFzcy5cbiAgICAgKiBJdCBpbmNvcnBvcmF0ZXMgQVdTIENESyBkZWZhdWx0cyAoZGVyaXZlZCBmcm9tIGBjZGtEZWZhdWx0Q29tcGlsZXJPcHRpb25zYCBhbmQgYGNka0RlZmF1bHRFeGNsdWRlYCBhYm92ZSlcbiAgICAgKiBhbmQgYW55IHVzZXItcHJvdmlkZWQgb3ZlcnJpZGVzLiBUaGUgYWltIGlzIHRvIGFsaWduIHdpdGggdGhlIHN0YW5kYXJkIENESyBgdHNjb25maWcuanNvbmA6XG4gICAgICogQHNlZSBodHRwczovL2dpdGh1Yi5jb20vYXdzL2F3cy1jZGstY2xpL2Jsb2IvbWFpbi9wYWNrYWdlcy9hd3MtY2RrL2xpYi9pbml0LXRlbXBsYXRlcy9hcHAvdHlwZXNjcmlwdC90c2NvbmZpZy5qc29uXG4gICAgICovXG4gICAgY29uc3QgdHNjb25maWdUb1N1cGVyOiBUeXBlc2NyaXB0Q29uZmlnT3B0aW9ucyA9IHtcbiAgICAgIC4uLm9wdGlvbnMudHNjb25maWcsIC8vIFBhc3MgdGhyb3VnaCBhbnkgb3RoZXIgdG9wLWxldmVsIHRzY29uZmlnIG9wdGlvbnMgZnJvbSB1c2VyXG4gICAgICBjb21waWxlck9wdGlvbnM6IGZpbmFsQ29tcGlsZXJPcHRpb25zLFxuICAgICAgZXhjbHVkZTogZmluYWxFeGNsdWRlLFxuICAgIH07XG5cbiAgICBzdXBlcih7XG4gICAgICAuLi5vcHRpb25zLFxuICAgICAgc2FtcGxlQ29kZTogZmFsc2UsXG4gICAgICBidW5kbGVyT3B0aW9uczoge1xuICAgICAgICAuLi5vcHRpb25zLmJ1bmRsZXJPcHRpb25zLFxuICAgICAgICAvLyB3ZSBpbnZva2UgdGhlIFwiYnVuZGxlXCIgdGFzayBhcyBwYXJ0IG9mIHRoZSBidWlsZCBzdGVwIGluIGNkay5qc29uIHNvXG4gICAgICAgIC8vIHdlIGRvbid0IHdhbnQgaXQgdG8gYmUgYWRkZWQgdG8gdGhlIHByZS1jb21waWxlIHBoYXNlLlxuICAgICAgICBydW5CdW5kbGVUYXNrOiBSdW5CdW5kbGVUYXNrLk1BTlVBTCxcbiAgICAgIH0sXG4gICAgICB0c2NvbmZpZzogdHNjb25maWdUb1N1cGVyLFxuICAgIH0pO1xuICAgIHRoaXMuY2RrRGVwcyA9IG5ldyBBd3NDZGtEZXBzSnModGhpcywge1xuICAgICAgZGVwZW5kZW5jeVR5cGU6IERlcGVuZGVuY3lUeXBlLlJVTlRJTUUsXG4gICAgICAuLi5vcHRpb25zLFxuICAgIH0pO1xuICAgIHRoaXMuYXBwRW50cnlwb2ludCA9IG9wdGlvbnMuYXBwRW50cnlwb2ludCA/PyBcIm1haW4udHNcIjtcblxuICAgIC8vIENMSVxuICAgIHRoaXMuYWRkRGV2RGVwcyhgYXdzLWNka0Ake3RoaXMuY2RrRGVwcy5jZGtDbGlWZXJzaW9ufWApO1xuXG4gICAgLy8gbm8gY29tcGlsZSBzdGVwIGJlY2F1c2Ugd2UgZG8gYWxsIG9mIGl0IGluIHR5cGVzY3JpcHQgZGlyZWN0bHlcbiAgICB0aGlzLmNvbXBpbGVUYXNrLnJlc2V0KCk7XG5cbiAgICB0aGlzLmNka1Rhc2tzID0gbmV3IENka1Rhc2tzKHRoaXMpO1xuXG4gICAgLy8gYWRkIHN5bnRoIHRvIHRoZSBidWlsZFxuICAgIHRoaXMucG9zdENvbXBpbGVUYXNrLnNwYXduKHRoaXMuY2RrVGFza3Muc3ludGhTaWxlbnQpO1xuXG4gICAgY29uc3QgdHNDb25maWdGaWxlID0gdGhpcy50c2NvbmZpZz8uZmlsZU5hbWU7XG4gICAgaWYgKCF0c0NvbmZpZ0ZpbGUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIkV4cGVjdGluZyB0c2NvbmZpZy5qc29uXCIpO1xuICAgIH1cblxuICAgIHRoaXMuY2RrQ29uZmlnID0gbmV3IENka0NvbmZpZyh0aGlzLCB7XG4gICAgICBidWlsZENvbW1hbmQ6IHRoaXMucnVuVGFza0NvbW1hbmQodGhpcy5idW5kbGVyLmJ1bmRsZVRhc2spLFxuICAgICAgd2F0Y2hJbmNsdWRlczogW2Ake3RoaXMuc3JjZGlyfS8qKi8qLnRzYCwgYCR7dGhpcy50ZXN0ZGlyfS8qKi8qLnRzYF0sXG4gICAgICB3YXRjaEV4Y2x1ZGVzOiBbXG4gICAgICAgIFwiUkVBRE1FLm1kXCIsXG4gICAgICAgIFwiY2RrKi5qc29uXCIsXG4gICAgICAgIFwiKiovKi5kLnRzXCIsXG4gICAgICAgIFwiKiovKi5qc1wiLFxuICAgICAgICBcInRzY29uZmlnLmpzb25cIixcbiAgICAgICAgXCJwYWNrYWdlKi5qc29uXCIsXG4gICAgICAgIFwieWFybi5sb2NrXCIsXG4gICAgICAgIFwibm9kZV9tb2R1bGVzXCIsXG4gICAgICBdLFxuICAgICAgLi4ub3B0aW9ucyxcbiAgICAgIGFwcDogdGhpcy5nZXRDZGtBcHAob3B0aW9ucyksXG4gICAgfSk7XG5cbiAgICB0aGlzLmdpdGlnbm9yZS5leGNsdWRlKFwiLnBhcmNlbC1jYWNoZS9cIik7XG5cbiAgICB0aGlzLm5wbWlnbm9yZT8uZXhjbHVkZShgJHt0aGlzLmNka0NvbmZpZy5jZGtvdXR9L2ApO1xuICAgIHRoaXMubnBtaWdub3JlPy5leGNsdWRlKFwiLmNkay5zdGFnaW5nL1wiKTtcblxuICAgIGlmICh0aGlzLnRzY29uZmlnKSB7XG4gICAgICB0aGlzLnRzY29uZmlnLmV4Y2x1ZGUucHVzaCh0aGlzLmNka0NvbmZpZy5jZGtvdXQpO1xuICAgIH1cblxuICAgIHRoaXMuYWRkRGV2RGVwcyhcInRzLW5vZGVcIik7XG4gICAgaWYgKG9wdGlvbnMuc2FtcGxlQ29kZSA/PyB0cnVlKSB7XG4gICAgICBuZXcgU2FtcGxlQ29kZSh0aGlzLCB0aGlzLmNka0RlcHMuY2RrTWFqb3JWZXJzaW9uKTtcbiAgICB9XG5cbiAgICBuZXcgQXV0b0Rpc2NvdmVyKHRoaXMsIHtcbiAgICAgIHNyY2RpcjogdGhpcy5zcmNkaXIsXG4gICAgICB0ZXN0ZGlyOiB0aGlzLnRlc3RkaXIsXG4gICAgICBsYW1iZGFPcHRpb25zOiBvcHRpb25zLmxhbWJkYU9wdGlvbnMsXG4gICAgICB0c2NvbmZpZ1BhdGg6IHRoaXMudHNjb25maWdEZXYuZmlsZU5hbWUsXG4gICAgICBjZGtEZXBzOiB0aGlzLmNka0RlcHMsXG4gICAgICBsYW1iZGFBdXRvRGlzY292ZXI6IG9wdGlvbnMubGFtYmRhQXV0b0Rpc2NvdmVyID8/IHRydWUsXG4gICAgICBlZGdlTGFtYmRhQXV0b0Rpc2NvdmVyOiBvcHRpb25zLmVkZ2VMYW1iZGFBdXRvRGlzY292ZXIgPz8gdHJ1ZSxcbiAgICAgIHNpbmdsZXRvbkxhbWJkYUF1dG9EaXNjb3Zlcjogb3B0aW9ucy5zaW5nbGV0b25MYW1iZGFBdXRvRGlzY292ZXIgPz8gdHJ1ZSxcbiAgICAgIGxhbWJkYUV4dGVuc2lvbkF1dG9EaXNjb3Zlcjogb3B0aW9ucy5sYW1iZGFFeHRlbnNpb25BdXRvRGlzY292ZXIgPz8gdHJ1ZSxcbiAgICAgIGludGVncmF0aW9uVGVzdEF1dG9EaXNjb3Zlcjogb3B0aW9ucy5pbnRlZ3JhdGlvblRlc3RBdXRvRGlzY292ZXIgPz8gdHJ1ZSxcbiAgICB9KTtcblxuICAgIGlmIChvcHRpb25zLmV4cGVyaW1lbnRhbEludGVnUnVubmVyKSB7XG4gICAgICBuZXcgSW50ZWdSdW5uZXIodGhpcyk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEFkZHMgYW4gQVdTIENESyBtb2R1bGUgZGVwZW5kZW5jaWVzXG4gICAqIEBwYXJhbSBtb2R1bGVzIFRoZSBsaXN0IG9mIG1vZHVsZXMgdG8gZGVwZW5kIG9uXG4gICAqL1xuICBwdWJsaWMgYWRkQ2RrRGVwZW5kZW5jeSguLi5tb2R1bGVzOiBzdHJpbmdbXSkge1xuICAgIHJldHVybiB0aGlzLmNka0RlcHMuYWRkVjFEZXBlbmRlbmNpZXMoLi4ubW9kdWxlcyk7XG4gIH1cblxuICBwcml2YXRlIGdldENka0FwcChvcHRpb25zOiBBd3NDZGtUeXBlU2NyaXB0QXBwT3B0aW9ucyk6IHN0cmluZyB7XG4gICAgaWYgKG9wdGlvbnMuYXBwICYmIG9wdGlvbnMuYXBwRW50cnlwb2ludCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiT25seSBvbmUgb2YgJ2FwcCcgb3IgJ2FwcEVudHJ5cG9pbnQnIGNhbiBiZSBzcGVjaWZpZWRcIik7XG4gICAgfVxuXG4gICAgLy8gcHJlZmVyIGFuIGV4cGxpY2l0bHkgcHJvdmlkZWQgYXBwIGNvbW1hbmRcbiAgICBpZiAob3B0aW9ucy5hcHApIHtcbiAgICAgIHJldHVybiBvcHRpb25zLmFwcDtcbiAgICB9XG5cbiAgICBjb25zdCBhcHBFbnRyeXBvaW50ID0gcGF0aC5wb3NpeC5qb2luKHRoaXMuc3JjZGlyLCB0aGlzLmFwcEVudHJ5cG9pbnQpO1xuXG4gICAgY29uc3QgdHNOb2RlQ29uZmlnID0gdGhpcy50c2NvbmZpZz8uZmlsZU5hbWVcbiAgICAgID8gYCAtUCAke3RoaXMudHNjb25maWc/LmZpbGVOYW1lfWBcbiAgICAgIDogXCJcIjtcbiAgICBjb25zdCB0c05vZGVBcHAgPSBgdHMtbm9kZSR7dHNOb2RlQ29uZmlnfSAtLXByZWZlci10cy1leHRzICR7YXBwRW50cnlwb2ludH1gO1xuXG4gICAgc3dpdGNoICh0aGlzLnBhY2thZ2UucGFja2FnZU1hbmFnZXIpIHtcbiAgICAgIGNhc2UgTm9kZVBhY2thZ2VNYW5hZ2VyLkJVTjpcbiAgICAgICAgY29uc3QgYnVuVHNDb25maWcgPSB0aGlzLnRzY29uZmlnPy5maWxlTmFtZVxuICAgICAgICAgID8gYCAtLXRzY29uZmlnLW92ZXJyaWRlPSR7dGhpcy50c2NvbmZpZz8uZmlsZU5hbWV9YFxuICAgICAgICAgIDogXCJcIjtcbiAgICAgICAgY29uc3QgYnVuRW50cnlwb2ludCA9IGVuc3VyZVJlbGF0aXZlUGF0aFByZWZpeChhcHBFbnRyeXBvaW50KTtcblxuICAgICAgICAvLyBodHRwczovL2J1bi5zaC9kb2NzL2NsaS9ydW5cbiAgICAgICAgLy8gYnVuIGNhbiBydW4gdHMgZmlsZXMgZGlyZWN0bHlcbiAgICAgICAgcmV0dXJuIGBidW4gcnVuJHtidW5Uc0NvbmZpZ30gJHtidW5FbnRyeXBvaW50fWA7XG4gICAgICBjYXNlIE5vZGVQYWNrYWdlTWFuYWdlci5QTlBNOlxuICAgICAgY2FzZSBOb2RlUGFja2FnZU1hbmFnZXIuWUFSTl9DTEFTU0lDOlxuICAgICAgY2FzZSBOb2RlUGFja2FnZU1hbmFnZXIuWUFSTjpcbiAgICAgIGNhc2UgTm9kZVBhY2thZ2VNYW5hZ2VyLllBUk5fQkVSUlk6XG4gICAgICBjYXNlIE5vZGVQYWNrYWdlTWFuYWdlci5ZQVJOMjpcbiAgICAgICAgLy8gdXNlIG5weCB3aXRoIGFsc28gZm9yIHlhcm4gJiBwbnBtIGR1ZSB0byByZXBvcnRlZCBpc3N1ZXNcbiAgICAgICAgLy8gQHNlZSBodHRwczovL2dpdGh1Yi5jb20vcHJvamVuL3Byb2plbi9pc3N1ZXMvNDE4MFxuICAgICAgICByZXR1cm4gYG5weCAke3RzTm9kZUFwcH1gO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuIGBucHggJHt0c05vZGVBcHB9YDtcbiAgICB9XG4gIH1cbn1cblxuLyoqXG4gKiBFbnN1cmVzIGEgcGF0aCBpcyBwcm9wZXJseSBwcmVmaXhlZCB3aXRoICcuLycgaWYgaXQncyBhIHJlbGF0aXZlIHBhdGhcbiAqIEBwYXJhbSB7c3RyaW5nfSBmaWxlUGF0aCAtIFRoZSBwYXRoIHRvIG5vcm1hbGl6ZVxuICogQHJldHVybnMge3N0cmluZ30gLSBUaGUgbm9ybWFsaXplZCBwYXRoXG4gKi9cbmZ1bmN0aW9uIGVuc3VyZVJlbGF0aXZlUGF0aFByZWZpeChmaWxlUGF0aDogc3RyaW5nKSB7XG4gIC8vIElmIGl0J3MgYWxyZWFkeSBhbiBhYnNvbHV0ZSBwYXRoLCByZXR1cm4gYXMgaXNcbiAgaWYgKHBhdGguaXNBYnNvbHV0ZShmaWxlUGF0aCkpIHtcbiAgICByZXR1cm4gZmlsZVBhdGg7XG4gIH1cblxuICAvLyBJZiBpdCBhbHJlYWR5IHN0YXJ0cyB3aXRoIC4vIG9yIC4uLywgcmV0dXJuIGFzIGlzXG4gIGlmIChmaWxlUGF0aC5zdGFydHNXaXRoKFwiLi9cIikgfHwgZmlsZVBhdGguc3RhcnRzV2l0aChcIi4uL1wiKSkge1xuICAgIHJldHVybiBmaWxlUGF0aDtcbiAgfVxuXG4gIC8vIE90aGVyd2lzZSwgYWRkIC4vIHByZWZpeFxuICByZXR1cm4gYC4vJHtmaWxlUGF0aH1gO1xufVxuXG5jbGFzcyBTYW1wbGVDb2RlIGV4dGVuZHMgQ29tcG9uZW50IHtcbiAgcHJpdmF0ZSByZWFkb25seSBhcHBQcm9qZWN0OiBBd3NDZGtUeXBlU2NyaXB0QXBwO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByb2plY3Q6IEF3c0Nka1R5cGVTY3JpcHRBcHAsXG4gICAgcHJpdmF0ZSByZWFkb25seSBjZGtNYWpvclZlcnNpb246IG51bWJlcixcbiAgKSB7XG4gICAgc3VwZXIocHJvamVjdCk7XG4gICAgdGhpcy5hcHBQcm9qZWN0ID0gcHJvamVjdDtcbiAgfVxuXG4gIHB1YmxpYyBzeW50aGVzaXplKCkge1xuICAgIGNvbnN0IG91dGRpciA9IHRoaXMucHJvamVjdC5vdXRkaXI7XG4gICAgY29uc3Qgc3JjZGlyID0gcGF0aC5qb2luKG91dGRpciwgdGhpcy5hcHBQcm9qZWN0LnNyY2Rpcik7XG4gICAgaWYgKFxuICAgICAgZnMuZXhpc3RzU3luYyhzcmNkaXIpICYmXG4gICAgICBmcy5yZWFkZGlyU3luYyhzcmNkaXIpLmZpbHRlcigoeCkgPT4geC5lbmRzV2l0aChcIi50c1wiKSlcbiAgICApIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBzcmNJbXBvcnRzID0gbmV3IEFycmF5PHN0cmluZz4oKTtcbiAgICBpZiAodGhpcy5jZGtNYWpvclZlcnNpb24gPCAyKSB7XG4gICAgICBzcmNJbXBvcnRzLnB1c2goXG4gICAgICAgIFwiaW1wb3J0IHsgQXBwLCBDb25zdHJ1Y3QsIFN0YWNrLCBTdGFja1Byb3BzIH0gZnJvbSAnQGF3cy1jZGsvY29yZSc7XCIsXG4gICAgICApO1xuICAgIH0gZWxzZSB7XG4gICAgICBzcmNJbXBvcnRzLnB1c2goXCJpbXBvcnQgeyBBcHAsIFN0YWNrLCBTdGFja1Byb3BzIH0gZnJvbSAnYXdzLWNkay1saWInO1wiKTtcbiAgICAgIHNyY0ltcG9ydHMucHVzaChcImltcG9ydCB7IENvbnN0cnVjdCB9IGZyb20gJ2NvbnN0cnVjdHMnO1wiKTtcbiAgICB9XG5cbiAgICBjb25zdCBzcmNDb2RlID0gYCR7c3JjSW1wb3J0cy5qb2luKFwiXFxuXCIpfVxuXG5leHBvcnQgY2xhc3MgTXlTdGFjayBleHRlbmRzIFN0YWNrIHtcbiAgY29uc3RydWN0b3Ioc2NvcGU6IENvbnN0cnVjdCwgaWQ6IHN0cmluZywgcHJvcHM6IFN0YWNrUHJvcHMgPSB7fSkge1xuICAgIHN1cGVyKHNjb3BlLCBpZCwgcHJvcHMpO1xuXG4gICAgLy8gZGVmaW5lIHJlc291cmNlcyBoZXJlLi4uXG4gIH1cbn1cblxuLy8gZm9yIGRldmVsb3BtZW50LCB1c2UgYWNjb3VudC9yZWdpb24gZnJvbSBjZGsgY2xpXG5jb25zdCBkZXZFbnYgPSB7XG4gIGFjY291bnQ6IHByb2Nlc3MuZW52LkNES19ERUZBVUxUX0FDQ09VTlQsXG4gIHJlZ2lvbjogcHJvY2Vzcy5lbnYuQ0RLX0RFRkFVTFRfUkVHSU9OLFxufTtcblxuY29uc3QgYXBwID0gbmV3IEFwcCgpO1xuXG5uZXcgTXlTdGFjayhhcHAsICcke3RoaXMucHJvamVjdC5uYW1lfS1kZXYnLCB7IGVudjogZGV2RW52IH0pO1xuLy8gbmV3IE15U3RhY2soYXBwLCAnJHt0aGlzLnByb2plY3QubmFtZX0tcHJvZCcsIHsgZW52OiBwcm9kRW52IH0pO1xuXG5hcHAuc3ludGgoKTtgO1xuXG4gICAgZnMubWtkaXJTeW5jKHNyY2RpciwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgZnMud3JpdGVGaWxlU3luYyhwYXRoLmpvaW4oc3JjZGlyLCB0aGlzLmFwcFByb2plY3QuYXBwRW50cnlwb2ludCksIHNyY0NvZGUpO1xuXG4gICAgY29uc3QgdGVzdGRpciA9IHBhdGguam9pbihvdXRkaXIsIHRoaXMuYXBwUHJvamVjdC50ZXN0ZGlyKTtcbiAgICBpZiAoXG4gICAgICBmcy5leGlzdHNTeW5jKHRlc3RkaXIpICYmXG4gICAgICBmcy5yZWFkZGlyU3luYyh0ZXN0ZGlyKS5maWx0ZXIoKHgpID0+IHguZW5kc1dpdGgoXCIudHNcIikpXG4gICAgKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgdGVzdEltcG9ydHMgPSBuZXcgQXJyYXk8c3RyaW5nPigpO1xuICAgIGlmICh0aGlzLmNka01ham9yVmVyc2lvbiA8IDIpIHtcbiAgICAgIHRlc3RJbXBvcnRzLnB1c2goXCJpbXBvcnQgeyBBcHAgfSBmcm9tICdAYXdzLWNkay9jb3JlJztcIik7XG4gICAgICB0ZXN0SW1wb3J0cy5wdXNoKFwiaW1wb3J0IHsgVGVtcGxhdGUgfSBmcm9tICdAYXdzLWNkay9hc3NlcnRpb25zJztcIik7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRlc3RJbXBvcnRzLnB1c2goXCJpbXBvcnQgeyBBcHAgfSBmcm9tICdhd3MtY2RrLWxpYic7XCIpO1xuICAgICAgdGVzdEltcG9ydHMucHVzaChcImltcG9ydCB7IFRlbXBsYXRlIH0gZnJvbSAnYXdzLWNkay1saWIvYXNzZXJ0aW9ucyc7XCIpO1xuICAgIH1cblxuICAgIGNvbnN0IGFwcEVudHJ5cG9pbnROYW1lID0gcGF0aC5iYXNlbmFtZShcbiAgICAgIHRoaXMuYXBwUHJvamVjdC5hcHBFbnRyeXBvaW50LFxuICAgICAgXCIudHNcIixcbiAgICApO1xuICAgIGNvbnN0IHRlc3RDb2RlID0gYCR7dGVzdEltcG9ydHMuam9pbihcIlxcblwiKX1cbmltcG9ydCB7IE15U3RhY2sgfSBmcm9tICcuLi8ke3RoaXMuYXBwUHJvamVjdC5zcmNkaXJ9LyR7YXBwRW50cnlwb2ludE5hbWV9JztcblxudGVzdCgnU25hcHNob3QnLCAoKSA9PiB7XG4gIGNvbnN0IGFwcCA9IG5ldyBBcHAoKTtcbiAgY29uc3Qgc3RhY2sgPSBuZXcgTXlTdGFjayhhcHAsICd0ZXN0Jyk7XG5cbiAgY29uc3QgdGVtcGxhdGUgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spO1xuICBleHBlY3QodGVtcGxhdGUudG9KU09OKCkpLnRvTWF0Y2hTbmFwc2hvdCgpO1xufSk7YDtcblxuICAgIGZzLm1rZGlyU3luYyh0ZXN0ZGlyLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICBmcy53cml0ZUZpbGVTeW5jKFxuICAgICAgcGF0aC5qb2luKHRlc3RkaXIsIGAke2FwcEVudHJ5cG9pbnROYW1lfS50ZXN0LnRzYCksXG4gICAgICB0ZXN0Q29kZSxcbiAgICApO1xuICB9XG59XG4iXX0=