UNPKG

@aws-solutions-constructs/core

Version:
266 lines 39.7 kB
"use strict"; /** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * with the License. A copy of the License is located at * * http://www.apache.org/licenses/LICENSE-2.0 * * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * and limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.fakeEcrRepoArn = void 0; exports.CreateScrapBucket = CreateScrapBucket; exports.generateIntegStackName = generateIntegStackName; exports.getTestVpc = getTestVpc; exports.getFakeCertificate = getFakeCertificate; exports.CreateTestStateMachine = CreateTestStateMachine; exports.CreateTestStateMachineDefinitionBody = CreateTestStateMachineDefinitionBody; exports.suppressCustomHandlerCfnNagWarnings = suppressCustomHandlerCfnNagWarnings; exports.CreateTestCache = CreateTestCache; exports.expectKmsKeyAttachedToCorrectResource = expectKmsKeyAttachedToCorrectResource; exports.expectNonexistence = expectNonexistence; exports.CreateTestApi = CreateTestApi; exports.CreateApiAuthorizer = CreateApiAuthorizer; exports.CreateShortUniqueTestName = CreateShortUniqueTestName; exports.SuppressCfnNagLambdaWarnings = SuppressCfnNagLambdaWarnings; const aws_s3_1 = require("aws-cdk-lib/aws-s3"); const aws_cdk_lib_1 = require("aws-cdk-lib"); const vpc_helper_1 = require("../lib/vpc-helper"); const vpc_defaults_1 = require("../lib/vpc-defaults"); const utils_1 = require("../lib/utils"); const defaults = require("../index"); const elasticache_helper_1 = require("../lib/elasticache-helper"); const path = require("path"); const cache = require("aws-cdk-lib/aws-elasticache"); const ec2 = require("aws-cdk-lib/aws-ec2"); const acm = require("aws-cdk-lib/aws-certificatemanager"); const elasticache_defaults_1 = require("../lib/elasticache-defaults"); const assertions_1 = require("aws-cdk-lib/assertions"); const lambda = require("aws-cdk-lib/aws-lambda"); const sfn = require("aws-cdk-lib/aws-stepfunctions"); const sftasks = require("aws-cdk-lib/aws-stepfunctions-tasks"); const api = require("aws-cdk-lib/aws-apigateway"); exports.fakeEcrRepoArn = 'arn:aws:ecr:us-east-1:123456789012:repository/fake-repo'; // Creates a bucket used for testing - minimal properties, destroyed after test function CreateScrapBucket(scope, id, props) { if (props?.serverAccessLogsBucket) { throw new Error("Don't try to send a log bucket to CreateScrapBucket"); } // Basic props for scrap and log buckets const defaultProps = { versioned: true, removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY, autoDeleteObjects: true, encryption: aws_s3_1.BucketEncryption.S3_MANAGED, enforceSSL: true }; // Create basic log bucket const logBucket = new aws_s3_1.Bucket(scope, `${id}Log`, defaultProps); // Combine basic props with special props from test client let synthesizedProps; if (props) { synthesizedProps = (0, utils_1.overrideProps)(defaultProps, props); } else { synthesizedProps = defaultProps; } // Finally - set up logging for the scrap bucket const finalProps = (0, utils_1.overrideProps)(synthesizedProps, { serverAccessLogsBucket: logBucket }); const scriptBucket = new aws_s3_1.Bucket(scope, id, finalProps); (0, utils_1.addCfnSuppressRules)(logBucket, [ { id: "W35", reason: "This is a log bucket", } ]); return scriptBucket; } /** * @summary Creates a stack name for Integration tests * @param {string} filename - the filename of the integ test * @returns {string} - a string with current filename after removing anything before the prefix '.' and suffix '.js' * e.g. 'integ.apigateway-dynamodb-CRUD.js' will return 'apigateway-dynamodb-CRUD' */ function generateIntegStackName(filename) { const file = path.basename(filename, path.extname(filename)); const stackname = file.slice(file.lastIndexOf('.') + 1).replace(/_/g, '-'); return stackname; } // Helper Functions function getTestVpc(stack, publicFacing = true, userVpcProps) { return (0, vpc_helper_1.buildVpc)(stack, { defaultVpcProps: publicFacing ? (0, vpc_defaults_1.DefaultPublicPrivateVpcProps)() : (0, vpc_defaults_1.DefaultIsolatedVpcProps)(), constructVpcProps: { enableDnsHostnames: true, enableDnsSupport: true, ipAddresses: ec2.IpAddresses.cidr('172.168.0.0/16'), }, userVpcProps }); } function getFakeCertificate(scope, id) { return acm.Certificate.fromCertificateArn(scope, id, "arn:aws:acm:us-east-1:123456789012:certificate/11112222-3333-1234-1234-123456789012"); } function CreateTestStateMachine(scope, id) { return new sfn.StateMachine(scope, id, { definitionBody: defaults.CreateTestStateMachineDefinitionBody(scope, id) }); } function CreateTestStateMachineDefinitionBody(scope, id) { const smStep = new lambda.Function(scope, `lambda${id}`, { code: new lambda.InlineCode("exports.handler = async (event) => console.log(event)"), runtime: lambda.Runtime.NODEJS_20_X, handler: "index.handler", }); const task = new sftasks.LambdaInvoke(scope, `task${id}`, { lambdaFunction: smStep, }); SuppressCfnNagLambdaWarnings(aws_cdk_lib_1.Stack.of(scope)); return sfn.DefinitionBody.fromChainable(task); } function suppressCustomHandlerCfnNagWarnings(stack, handlerId) { stack.node.children.forEach(child => { if (child.node.id === handlerId) { const handlerFunction = child.node.findChild('Handler'); (0, utils_1.addCfnSuppressRules)(handlerFunction, [{ id: "W58", reason: "CDK generated custom resource" }]); (0, utils_1.addCfnSuppressRules)(handlerFunction, [{ id: "W89", reason: "CDK generated custom resource" }]); (0, utils_1.addCfnSuppressRules)(handlerFunction, [{ id: "W92", reason: "CDK generated custom resource" }]); } }); } function CreateTestCache(scope, id, vpc, port) { const cachePort = port ?? (0, elasticache_defaults_1.GetDefaultCachePort)(); // Create the subnet group from all the isolated subnets in the VPC const subnetGroup = (0, elasticache_helper_1.createCacheSubnetGroup)(scope, vpc, id); const emptySG = new ec2.SecurityGroup(scope, `${id}-cachesg`, { vpc, allowAllOutbound: true, }); (0, utils_1.addCfnSuppressRules)(emptySG, [{ id: "W40", reason: "Test Resource" }]); (0, utils_1.addCfnSuppressRules)(emptySG, [{ id: "W5", reason: "Test Resource" }]); (0, utils_1.addCfnSuppressRules)(emptySG, [{ id: "W36", reason: "Test Resource" }]); const cacheProps = { clusterName: `${id}-cdk-cluster`, cacheNodeType: "cache.t3.medium", engine: "memcached", numCacheNodes: 2, port: cachePort, azMode: "cross-az", vpcSecurityGroupIds: [emptySG.securityGroupId], cacheSubnetGroupName: subnetGroup.cacheSubnetGroupName, }; const newCache = new cache.CfnCacheCluster(scope, `${id}-cluster`, cacheProps); newCache.addDependency(subnetGroup); return newCache; } /** * Asserts that a KMS key with a specific description exists on a resource * * @param stack The CloudFormation Stack that contains the to validate. * @param parentResourceType The type of CloudFormation Resource that should have the key set on it, e.g., `AWS::SNS::Topic`, etc... * @param description The value of the Description property on the KMS Key */ function expectKmsKeyAttachedToCorrectResource(stack, parentResourceType, keyDescription) { const template = assertions_1.Template.fromStack(stack); const resource = template.findResources('AWS::KMS::Key', { Properties: { Description: assertions_1.Match.exact(keyDescription) } }); const [logicalId] = Object.keys(resource); assertions_1.Template.fromStack(stack).hasResourceProperties(parentResourceType, { KmsMasterKeyId: { "Fn::GetAtt": [ logicalId, "Arn" ] } }); } function expectNonexistence(stack, type, props) { const shouldFindNothing = assertions_1.Template.fromStack(stack).findResources(type, props); expect(Object.keys(shouldFindNothing).length).toEqual(0); } // private helper class to suppress the standard cfn nag warnings for lambda functions used in integ tests class CfnNagLambdaAspect { visit(node) { const resource = node; if (resource.cfnResourceType === 'AWS::Lambda::Function') { (0, utils_1.addCfnSuppressRules)(resource, [ { id: 'W58', reason: 'This Lambda Function is created for integration testing purposes only and is not part of an actual construct' }, { id: 'W89', reason: 'This Lambda Function is created for integration testing purposes only and is not part of an actual construct' }, { id: 'W92', reason: 'This Lambda Function is created for integration testing purposes only and is not part of an actual construct' } ]); } } } function CreateTestApi(stack, id) { const lambdaFunction = new lambda.Function(stack, `${id}Function`, { code: lambda.Code.fromAsset(`${__dirname}/lambda`), runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME, handler: ".handler", }); (0, utils_1.addCfnSuppressRules)(lambdaFunction, [{ id: "W58", reason: "Test Resource" }]); (0, utils_1.addCfnSuppressRules)(lambdaFunction, [{ id: "W89", reason: "Test Resource" }]); (0, utils_1.addCfnSuppressRules)(lambdaFunction, [{ id: "W92", reason: "Test Resource" }]); const restApi = new api.LambdaRestApi(stack, `${id}Api`, { handler: lambdaFunction, defaultMethodOptions: { authorizationType: api.AuthorizationType.CUSTOM, authorizer: CreateApiAuthorizer(stack, `${id}-authorizer`) } }); const newDeployment = restApi.latestDeployment; if (newDeployment) { (0, utils_1.addCfnSuppressRules)(newDeployment, [ { id: "W68", reason: "Test Resource" }, ]); } const newMethod = restApi.methods[0]; (0, utils_1.addCfnSuppressRules)(newMethod, [{ id: "W59", reason: "Test Resource" }]); const newMethodTwo = restApi.methods[1]; (0, utils_1.addCfnSuppressRules)(newMethodTwo, [{ id: "W59", reason: "Test Resource" }]); const newStage = restApi.deploymentStage; (0, utils_1.addCfnSuppressRules)(newStage, [{ id: "W64", reason: "Test Resource" }]); (0, utils_1.addCfnSuppressRules)(newStage, [{ id: "W69", reason: "Test Resource" }]); return restApi; } function CreateApiAuthorizer(stack, id) { const authFn = new lambda.Function(stack, `${id}AuthFunction`, { code: lambda.Code.fromAsset(`${__dirname}/lambda`), runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME, handler: ".handler", }); (0, utils_1.addCfnSuppressRules)(authFn, [{ id: "W58", reason: "Test Resource" }]); (0, utils_1.addCfnSuppressRules)(authFn, [{ id: "W89", reason: "Test Resource" }]); (0, utils_1.addCfnSuppressRules)(authFn, [{ id: "W92", reason: "Test Resource" }]); const authorizer = new api.RequestAuthorizer(stack, id, { handler: authFn, identitySources: [api.IdentitySource.header('Authorization')] }); return authorizer; } // Create a short, unique to this stack name // technically this is not 100% OK, as it only uses a portion of the // stack guid - but it's for tests only so if the last segment of 2 stack guids collide someday // (VERY unlikely), just running again should take care of it. function CreateShortUniqueTestName(stub) { const stackGuid = aws_cdk_lib_1.Fn.select(2, aws_cdk_lib_1.Fn.split('/', `${aws_cdk_lib_1.Aws.STACK_ID}`)); const guidPortion = aws_cdk_lib_1.Fn.select(4, aws_cdk_lib_1.Fn.split('-', stackGuid)); return aws_cdk_lib_1.Fn.join("-", [stub, guidPortion]); } /** * Used to suppress cfn nag W58, W89, and W92 rules on lambda integration test resources. * * @param stack - The stack to suppress cfn nag lambda rules on */ function SuppressCfnNagLambdaWarnings(stack) { aws_cdk_lib_1.Aspects.of(stack).add(new CfnNagLambdaAspect()); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdC1oZWxwZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJ0ZXN0LWhlbHBlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7Ozs7Ozs7O0dBV0c7OztBQTBCSCw4Q0EyQ0M7QUFRRCx3REFJQztBQUdELGdDQVlDO0FBRUQsZ0RBTUM7QUFFRCx3REFJQztBQUVELG9GQWlCQztBQUVELGtGQVNDO0FBRUQsMENBK0JDO0FBU0Qsc0ZBaUJDO0FBRUQsZ0RBR0M7QUFnQkQsc0NBbUNDO0FBRUQsa0RBZ0JDO0FBTUQsOERBSUM7QUFPRCxvRUFFQztBQWhTRCwrQ0FBMkU7QUFDM0UsNkNBQTJGO0FBQzNGLGtEQUE2QztBQUM3QyxzREFBNEY7QUFDNUYsd0NBQWtFO0FBQ2xFLHFDQUFxQztBQUNyQyxrRUFBbUU7QUFDbkUsNkJBQTZCO0FBQzdCLHFEQUFxRDtBQUNyRCwyQ0FBMkM7QUFDM0MsMERBQTBEO0FBRTFELHNFQUFrRTtBQUNsRSx1REFBeUQ7QUFDekQsaURBQWlEO0FBQ2pELHFEQUFxRDtBQUNyRCwrREFBK0Q7QUFDL0Qsa0RBQWtEO0FBRXJDLFFBQUEsY0FBYyxHQUFHLHlEQUF5RCxDQUFDO0FBRXhGLCtFQUErRTtBQUMvRSxTQUFnQixpQkFBaUIsQ0FBQyxLQUFnQixFQUFFLEVBQVUsRUFBRSxLQUF5QjtJQUV2RixJQUFJLEtBQUssRUFBRSxzQkFBc0IsRUFBRSxDQUFDO1FBQ2xDLE1BQU0sSUFBSSxLQUFLLENBQUMscURBQXFELENBQUMsQ0FBQztJQUN6RSxDQUFDO0lBRUQsd0NBQXdDO0lBQ3hDLE1BQU0sWUFBWSxHQUFnQjtRQUNoQyxTQUFTLEVBQUUsSUFBSTtRQUNmLGFBQWEsRUFBRSwyQkFBYSxDQUFDLE9BQU87UUFDcEMsaUJBQWlCLEVBQUUsSUFBSTtRQUN2QixVQUFVLEVBQUUseUJBQWdCLENBQUMsVUFBVTtRQUN2QyxVQUFVLEVBQUUsSUFBSTtLQUNqQixDQUFDO0lBRUYsMEJBQTBCO0lBQzFCLE1BQU0sU0FBUyxHQUFHLElBQUksZUFBTSxDQUFDLEtBQUssRUFBRSxHQUFHLEVBQUUsS0FBSyxFQUFFLFlBQVksQ0FBQyxDQUFDO0lBRTlELDBEQUEwRDtJQUMxRCxJQUFJLGdCQUE2QixDQUFDO0lBQ2xDLElBQUksS0FBSyxFQUFFLENBQUM7UUFDVixnQkFBZ0IsR0FBRyxJQUFBLHFCQUFhLEVBQUMsWUFBWSxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQ3hELENBQUM7U0FBTSxDQUFDO1FBQ04sZ0JBQWdCLEdBQUcsWUFBWSxDQUFDO0lBQ2xDLENBQUM7SUFFRCxnREFBZ0Q7SUFDaEQsTUFBTSxVQUFVLEdBQUcsSUFBQSxxQkFBYSxFQUFDLGdCQUFnQixFQUFFLEVBQUUsc0JBQXNCLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQztJQUUxRixNQUFNLFlBQVksR0FBRyxJQUFJLGVBQU0sQ0FDN0IsS0FBSyxFQUNMLEVBQUUsRUFDRixVQUFVLENBQ1gsQ0FBQztJQUVGLElBQUEsMkJBQW1CLEVBQUMsU0FBUyxFQUFFO1FBQzdCO1lBQ0UsRUFBRSxFQUFFLEtBQUs7WUFDVCxNQUFNLEVBQUUsc0JBQXNCO1NBQy9CO0tBQ0YsQ0FBQyxDQUFDO0lBRUgsT0FBTyxZQUFZLENBQUM7QUFDdEIsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsU0FBZ0Isc0JBQXNCLENBQUMsUUFBZ0I7SUFDckQsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDO0lBQzdELE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQzNFLE9BQU8sU0FBUyxDQUFDO0FBQ25CLENBQUM7QUFFRCxtQkFBbUI7QUFDbkIsU0FBZ0IsVUFBVSxDQUFDLEtBQVksRUFBRSxlQUF3QixJQUFJLEVBQUUsWUFBMkI7SUFDaEcsT0FBTyxJQUFBLHFCQUFRLEVBQUMsS0FBSyxFQUFFO1FBQ3JCLGVBQWUsRUFBRSxZQUFZLENBQUMsQ0FBQztZQUM3QixJQUFBLDJDQUE0QixHQUFFLENBQUMsQ0FBQztZQUNoQyxJQUFBLHNDQUF1QixHQUFFO1FBQzNCLGlCQUFpQixFQUFFO1lBQ2pCLGtCQUFrQixFQUFFLElBQUk7WUFDeEIsZ0JBQWdCLEVBQUUsSUFBSTtZQUN0QixXQUFXLEVBQUUsR0FBRyxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUM7U0FDcEQ7UUFDRCxZQUFZO0tBQ2IsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVELFNBQWdCLGtCQUFrQixDQUFDLEtBQWdCLEVBQUUsRUFBVTtJQUM3RCxPQUFPLEdBQUcsQ0FBQyxXQUFXLENBQUMsa0JBQWtCLENBQ3ZDLEtBQUssRUFDTCxFQUFFLEVBQ0YscUZBQXFGLENBQ3RGLENBQUM7QUFDSixDQUFDO0FBRUQsU0FBZ0Isc0JBQXNCLENBQUMsS0FBZ0IsRUFBRSxFQUFVO0lBQ2pFLE9BQU8sSUFBSSxHQUFHLENBQUMsWUFBWSxDQUFDLEtBQUssRUFBRSxFQUFFLEVBQUU7UUFDckMsY0FBYyxFQUFFLFFBQVEsQ0FBQyxvQ0FBb0MsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDO0tBQ3pFLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRCxTQUFnQixvQ0FBb0MsQ0FBQyxLQUFnQixFQUFFLEVBQVU7SUFFL0UsTUFBTSxNQUFNLEdBQUcsSUFBSSxNQUFNLENBQUMsUUFBUSxDQUFDLEtBQUssRUFBRSxTQUFTLEVBQUUsRUFBRSxFQUFFO1FBQ3ZELElBQUksRUFBRSxJQUFJLE1BQU0sQ0FBQyxVQUFVLENBQ3pCLHVEQUF1RCxDQUN4RDtRQUNELE9BQU8sRUFBRSxNQUFNLENBQUMsT0FBTyxDQUFDLFdBQVc7UUFDbkMsT0FBTyxFQUFFLGVBQWU7S0FDekIsQ0FBQyxDQUFDO0lBRUgsTUFBTSxJQUFJLEdBQUcsSUFBSSxPQUFPLENBQUMsWUFBWSxDQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsRUFBRSxFQUFFO1FBQ3hELGNBQWMsRUFBRSxNQUFNO0tBQ3ZCLENBQUMsQ0FBQztJQUVILDRCQUE0QixDQUFDLG1CQUFLLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7SUFFOUMsT0FBTyxHQUFHLENBQUMsY0FBYyxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUNoRCxDQUFDO0FBRUQsU0FBZ0IsbUNBQW1DLENBQUMsS0FBWSxFQUFFLFNBQWlCO0lBQ2pGLEtBQUssQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsRUFBRTtRQUNsQyxJQUFJLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQ2hDLE1BQU0sZUFBZSxHQUFHLEtBQUssQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLFNBQVMsQ0FBZ0IsQ0FBQztZQUN2RSxJQUFBLDJCQUFtQixFQUFDLGVBQWUsRUFBRSxDQUFDLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsK0JBQStCLEVBQUUsQ0FBQyxDQUFDLENBQUM7WUFDL0YsSUFBQSwyQkFBbUIsRUFBQyxlQUFlLEVBQUUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLCtCQUErQixFQUFFLENBQUMsQ0FBQyxDQUFDO1lBQy9GLElBQUEsMkJBQW1CLEVBQUMsZUFBZSxFQUFFLENBQUMsRUFBRSxFQUFFLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSwrQkFBK0IsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUNqRyxDQUFDO0lBQ0gsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQsU0FBZ0IsZUFBZSxDQUFDLEtBQWdCLEVBQUUsRUFBVSxFQUFFLEdBQWEsRUFBRSxJQUFhO0lBQ3hGLE1BQU0sU0FBUyxHQUFHLElBQUksSUFBSSxJQUFBLDBDQUFtQixHQUFFLENBQUM7SUFFaEQsbUVBQW1FO0lBQ25FLE1BQU0sV0FBVyxHQUFHLElBQUEsMkNBQXNCLEVBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUMzRCxNQUFNLE9BQU8sR0FBRyxJQUFJLEdBQUcsQ0FBQyxhQUFhLENBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRSxVQUFVLEVBQUU7UUFDNUQsR0FBRztRQUNILGdCQUFnQixFQUFFLElBQUk7S0FDdkIsQ0FBQyxDQUFDO0lBQ0gsSUFBQSwyQkFBbUIsRUFBQyxPQUFPLEVBQUUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLGVBQWUsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUN2RSxJQUFBLDJCQUFtQixFQUFDLE9BQU8sRUFBRSxDQUFDLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsZUFBZSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ3RFLElBQUEsMkJBQW1CLEVBQUMsT0FBTyxFQUFFLENBQUMsRUFBRSxFQUFFLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxlQUFlLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFFdkUsTUFBTSxVQUFVLEdBQUc7UUFDakIsV0FBVyxFQUFFLEdBQUcsRUFBRSxjQUFjO1FBQ2hDLGFBQWEsRUFBRSxpQkFBaUI7UUFDaEMsTUFBTSxFQUFFLFdBQVc7UUFDbkIsYUFBYSxFQUFFLENBQUM7UUFDaEIsSUFBSSxFQUFFLFNBQVM7UUFDZixNQUFNLEVBQUUsVUFBVTtRQUNsQixtQkFBbUIsRUFBRSxDQUFDLE9BQU8sQ0FBQyxlQUFlLENBQUM7UUFDOUMsb0JBQW9CLEVBQUUsV0FBVyxDQUFDLG9CQUFvQjtLQUN2RCxDQUFDO0lBRUYsTUFBTSxRQUFRLEdBQUcsSUFBSSxLQUFLLENBQUMsZUFBZSxDQUN4QyxLQUFLLEVBQ0wsR0FBRyxFQUFFLFVBQVUsRUFDZixVQUFVLENBQ1gsQ0FBQztJQUNGLFFBQVEsQ0FBQyxhQUFhLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDcEMsT0FBTyxRQUFRLENBQUM7QUFDbEIsQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNILFNBQWdCLHFDQUFxQyxDQUFDLEtBQVksRUFBRSxrQkFBMEIsRUFBRSxjQUFzQjtJQUNwSCxNQUFNLFFBQVEsR0FBRyxxQkFBUSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMzQyxNQUFNLFFBQVEsR0FBRyxRQUFRLENBQUMsYUFBYSxDQUFDLGVBQWUsRUFBRTtRQUN2RCxVQUFVLEVBQUU7WUFDVixXQUFXLEVBQUUsa0JBQUssQ0FBQyxLQUFLLENBQUMsY0FBYyxDQUFDO1NBQ3pDO0tBQ0YsQ0FBQyxDQUFDO0lBRUgsTUFBTSxDQUFDLFNBQVMsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDMUMscUJBQVEsQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMscUJBQXFCLENBQUMsa0JBQWtCLEVBQUU7UUFDbEUsY0FBYyxFQUFFO1lBQ2QsWUFBWSxFQUFFO2dCQUNaLFNBQVM7Z0JBQ1QsS0FBSzthQUNOO1NBQ0Y7S0FDRixDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQsU0FBZ0Isa0JBQWtCLENBQUMsS0FBWSxFQUFFLElBQVksRUFBRSxLQUFhO0lBQzFFLE1BQU0saUJBQWlCLEdBQUcscUJBQVEsQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsYUFBYSxDQUFDLElBQUksRUFBRSxLQUFLLENBQUMsQ0FBQztJQUMvRSxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUMzRCxDQUFDO0FBRUQsMEdBQTBHO0FBQzFHLE1BQU0sa0JBQWtCO0lBQ2YsS0FBSyxDQUFDLElBQWdCO1FBQzNCLE1BQU0sUUFBUSxHQUFHLElBQW1CLENBQUM7UUFDckMsSUFBSSxRQUFRLENBQUMsZUFBZSxLQUFLLHVCQUF1QixFQUFFLENBQUM7WUFDekQsSUFBQSwyQkFBbUIsRUFBQyxRQUFRLEVBQUU7Z0JBQzVCLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsOEdBQThHLEVBQUU7Z0JBQ3JJLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsOEdBQThHLEVBQUU7Z0JBQ3JJLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsOEdBQThHLEVBQUU7YUFDdEksQ0FBQyxDQUFDO1FBQ0wsQ0FBQztJQUNILENBQUM7Q0FDRjtBQUVELFNBQWdCLGFBQWEsQ0FBQyxLQUFZLEVBQUUsRUFBVTtJQUNwRCxNQUFNLGNBQWMsR0FBRyxJQUFJLE1BQU0sQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRSxVQUFVLEVBQUU7UUFDakUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsU0FBUyxTQUFTLENBQUM7UUFDbEQsT0FBTyxFQUFFLFFBQVEsQ0FBQyxxQ0FBcUM7UUFDdkQsT0FBTyxFQUFFLFVBQVU7S0FDcEIsQ0FBQyxDQUFDO0lBQ0gsSUFBQSwyQkFBbUIsRUFBQyxjQUFjLEVBQUUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLGVBQWUsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUM5RSxJQUFBLDJCQUFtQixFQUFDLGNBQWMsRUFBRSxDQUFDLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsZUFBZSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzlFLElBQUEsMkJBQW1CLEVBQUMsY0FBYyxFQUFFLENBQUMsRUFBRSxFQUFFLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxlQUFlLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFFOUUsTUFBTSxPQUFPLEdBQUcsSUFBSSxHQUFHLENBQUMsYUFBYSxDQUFDLEtBQUssRUFBRSxHQUFHLEVBQUUsS0FBSyxFQUFFO1FBQ3ZELE9BQU8sRUFBRSxjQUFjO1FBQ3ZCLG9CQUFvQixFQUFFO1lBQ3BCLGlCQUFpQixFQUFFLEdBQUcsQ0FBQyxpQkFBaUIsQ0FBQyxNQUFNO1lBQy9DLFVBQVUsRUFBRSxtQkFBbUIsQ0FBQyxLQUFLLEVBQUUsR0FBRyxFQUFFLGFBQWEsQ0FBQztTQUMzRDtLQUNGLENBQUMsQ0FBQztJQUVILE1BQU0sYUFBYSxHQUFHLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQztJQUMvQyxJQUFJLGFBQWEsRUFBRSxDQUFDO1FBQ2xCLElBQUEsMkJBQW1CLEVBQUMsYUFBYSxFQUFFO1lBQ2pDLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsZUFBZSxFQUFFO1NBQ3ZDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxNQUFNLFNBQVMsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3JDLElBQUEsMkJBQW1CLEVBQUMsU0FBUyxFQUFFLENBQUMsRUFBRSxFQUFFLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxlQUFlLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDekUsTUFBTSxZQUFZLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN4QyxJQUFBLDJCQUFtQixFQUFDLFlBQVksRUFBRSxDQUFDLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsZUFBZSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBRTVFLE1BQU0sUUFBUSxHQUFHLE9BQU8sQ0FBQyxlQUFlLENBQUM7SUFDekMsSUFBQSwyQkFBbUIsRUFBQyxRQUFRLEVBQUUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLGVBQWUsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUN4RSxJQUFBLDJCQUFtQixFQUFDLFFBQVEsRUFBRSxDQUFDLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsZUFBZSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBRXhFLE9BQU8sT0FBTyxDQUFDO0FBQ2pCLENBQUM7QUFFRCxTQUFnQixtQkFBbUIsQ0FBQyxLQUFZLEVBQUUsRUFBVTtJQUMxRCxNQUFNLE1BQU0sR0FBRyxJQUFJLE1BQU0sQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRSxjQUFjLEVBQUU7UUFDN0QsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsU0FBUyxTQUFTLENBQUM7UUFDbEQsT0FBTyxFQUFFLFFBQVEsQ0FBQyxxQ0FBcUM7UUFDdkQsT0FBTyxFQUFFLFVBQVU7S0FDcEIsQ0FBQyxDQUFDO0lBQ0gsSUFBQSwyQkFBbUIsRUFBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLGVBQWUsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUN0RSxJQUFBLDJCQUFtQixFQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsZUFBZSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ3RFLElBQUEsMkJBQW1CLEVBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxlQUFlLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFFdEUsTUFBTSxVQUFVLEdBQUcsSUFBSSxHQUFHLENBQUMsaUJBQWlCLENBQUMsS0FBSyxFQUFFLEVBQUUsRUFBRTtRQUN0RCxPQUFPLEVBQUUsTUFBTTtRQUNmLGVBQWUsRUFBRSxDQUFDLEdBQUcsQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLGVBQWUsQ0FBQyxDQUFDO0tBQzlELENBQUMsQ0FBQztJQUVILE9BQU8sVUFBVSxDQUFDO0FBQ3BCLENBQUM7QUFFRCw0Q0FBNEM7QUFDNUMsb0VBQW9FO0FBQ3BFLCtGQUErRjtBQUMvRiw4REFBOEQ7QUFDOUQsU0FBZ0IseUJBQXlCLENBQUMsSUFBWTtJQUNwRCxNQUFNLFNBQVMsR0FBRyxnQkFBRSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsZ0JBQUUsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLEdBQUcsaUJBQUcsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDakUsTUFBTSxXQUFXLEdBQUcsZ0JBQUUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLGdCQUFFLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRSxTQUFTLENBQUMsQ0FBQyxDQUFDO0lBQzNELE9BQU8sZ0JBQUUsQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxFQUFFLFdBQVcsQ0FBQyxDQUFDLENBQUM7QUFDM0MsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxTQUFnQiw0QkFBNEIsQ0FBQyxLQUFZO0lBQ3ZELHFCQUFPLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLGtCQUFrQixFQUFFLENBQUMsQ0FBQztBQUNsRCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiAgQ29weXJpZ2h0IEFtYXpvbi5jb20sIEluYy4gb3IgaXRzIGFmZmlsaWF0ZXMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIikuIFlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiAqICB3aXRoIHRoZSBMaWNlbnNlLiBBIGNvcHkgb2YgdGhlIExpY2Vuc2UgaXMgbG9jYXRlZCBhdFxuICpcbiAqICAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogIG9yIGluIHRoZSAnbGljZW5zZScgZmlsZSBhY2NvbXBhbnlpbmcgdGhpcyBmaWxlLiBUaGlzIGZpbGUgaXMgZGlzdHJpYnV0ZWQgb24gYW4gJ0FTIElTJyBCQVNJUywgV0lUSE9VVCBXQVJSQU5USUVTXG4gKiAgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZXhwcmVzcyBvciBpbXBsaWVkLiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnNcbiAqICBhbmQgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKi9cblxuLy8gSW1wb3J0c1xuaW1wb3J0IHsgQ29uc3RydWN0LCBJQ29uc3RydWN0IH0gZnJvbSAnY29uc3RydWN0cyc7XG5pbXBvcnQgeyBCdWNrZXQsIEJ1Y2tldFByb3BzLCBCdWNrZXRFbmNyeXB0aW9uIH0gZnJvbSBcImF3cy1jZGstbGliL2F3cy1zM1wiO1xuaW1wb3J0IHsgQ2ZuUmVzb3VyY2UsIFJlbW92YWxQb2xpY3ksIFN0YWNrLCBBc3BlY3RzLCBJQXNwZWN0LCBBd3MsIEZuIH0gZnJvbSBcImF3cy1jZGstbGliXCI7XG5pbXBvcnQgeyBidWlsZFZwYyB9IGZyb20gJy4uL2xpYi92cGMtaGVscGVyJztcbmltcG9ydCB7IERlZmF1bHRQdWJsaWNQcml2YXRlVnBjUHJvcHMsIERlZmF1bHRJc29sYXRlZFZwY1Byb3BzIH0gZnJvbSAnLi4vbGliL3ZwYy1kZWZhdWx0cyc7XG5pbXBvcnQgeyBvdmVycmlkZVByb3BzLCBhZGRDZm5TdXBwcmVzc1J1bGVzIH0gZnJvbSBcIi4uL2xpYi91dGlsc1wiO1xuaW1wb3J0ICogYXMgZGVmYXVsdHMgZnJvbSAnLi4vaW5kZXgnO1xuaW1wb3J0IHsgY3JlYXRlQ2FjaGVTdWJuZXRHcm91cCB9IGZyb20gXCIuLi9saWIvZWxhc3RpY2FjaGUtaGVscGVyXCI7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0ICogYXMgY2FjaGUgZnJvbSAnYXdzLWNkay1saWIvYXdzLWVsYXN0aWNhY2hlJztcbmltcG9ydCAqIGFzIGVjMiBmcm9tICdhd3MtY2RrLWxpYi9hd3MtZWMyJztcbmltcG9ydCAqIGFzIGFjbSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtY2VydGlmaWNhdGVtYW5hZ2VyJztcbmltcG9ydCB7IENmbkZ1bmN0aW9uIH0gZnJvbSBcImF3cy1jZGstbGliL2F3cy1sYW1iZGFcIjtcbmltcG9ydCB7IEdldERlZmF1bHRDYWNoZVBvcnQgfSBmcm9tIFwiLi4vbGliL2VsYXN0aWNhY2hlLWRlZmF1bHRzXCI7XG5pbXBvcnQgeyBNYXRjaCwgVGVtcGxhdGUgfSBmcm9tICdhd3MtY2RrLWxpYi9hc3NlcnRpb25zJztcbmltcG9ydCAqIGFzIGxhbWJkYSBmcm9tIFwiYXdzLWNkay1saWIvYXdzLWxhbWJkYVwiO1xuaW1wb3J0ICogYXMgc2ZuIGZyb20gXCJhd3MtY2RrLWxpYi9hd3Mtc3RlcGZ1bmN0aW9uc1wiO1xuaW1wb3J0ICogYXMgc2Z0YXNrcyBmcm9tIFwiYXdzLWNkay1saWIvYXdzLXN0ZXBmdW5jdGlvbnMtdGFza3NcIjtcbmltcG9ydCAqIGFzIGFwaSBmcm9tIFwiYXdzLWNkay1saWIvYXdzLWFwaWdhdGV3YXlcIjtcblxuZXhwb3J0IGNvbnN0IGZha2VFY3JSZXBvQXJuID0gJ2Fybjphd3M6ZWNyOnVzLWVhc3QtMToxMjM0NTY3ODkwMTI6cmVwb3NpdG9yeS9mYWtlLXJlcG8nO1xuXG4vLyBDcmVhdGVzIGEgYnVja2V0IHVzZWQgZm9yIHRlc3RpbmcgLSBtaW5pbWFsIHByb3BlcnRpZXMsIGRlc3Ryb3llZCBhZnRlciB0ZXN0XG5leHBvcnQgZnVuY3Rpb24gQ3JlYXRlU2NyYXBCdWNrZXQoc2NvcGU6IENvbnN0cnVjdCwgaWQ6IHN0cmluZywgcHJvcHM/OiBCdWNrZXRQcm9wcyB8IGFueSkge1xuXG4gIGlmIChwcm9wcz8uc2VydmVyQWNjZXNzTG9nc0J1Y2tldCkge1xuICAgIHRocm93IG5ldyBFcnJvcihcIkRvbid0IHRyeSB0byBzZW5kIGEgbG9nIGJ1Y2tldCB0byBDcmVhdGVTY3JhcEJ1Y2tldFwiKTtcbiAgfVxuXG4gIC8vIEJhc2ljIHByb3BzIGZvciBzY3JhcCBhbmQgbG9nIGJ1Y2tldHNcbiAgY29uc3QgZGVmYXVsdFByb3BzOiBCdWNrZXRQcm9wcyA9IHtcbiAgICB2ZXJzaW9uZWQ6IHRydWUsXG4gICAgcmVtb3ZhbFBvbGljeTogUmVtb3ZhbFBvbGljeS5ERVNUUk9ZLFxuICAgIGF1dG9EZWxldGVPYmplY3RzOiB0cnVlLFxuICAgIGVuY3J5cHRpb246IEJ1Y2tldEVuY3J5cHRpb24uUzNfTUFOQUdFRCxcbiAgICBlbmZvcmNlU1NMOiB0cnVlXG4gIH07XG5cbiAgLy8gQ3JlYXRlIGJhc2ljIGxvZyBidWNrZXRcbiAgY29uc3QgbG9nQnVja2V0ID0gbmV3IEJ1Y2tldChzY29wZSwgYCR7aWR9TG9nYCwgZGVmYXVsdFByb3BzKTtcblxuICAvLyBDb21iaW5lIGJhc2ljIHByb3BzIHdpdGggc3BlY2lhbCBwcm9wcyBmcm9tIHRlc3QgY2xpZW50XG4gIGxldCBzeW50aGVzaXplZFByb3BzOiBCdWNrZXRQcm9wcztcbiAgaWYgKHByb3BzKSB7XG4gICAgc3ludGhlc2l6ZWRQcm9wcyA9IG92ZXJyaWRlUHJvcHMoZGVmYXVsdFByb3BzLCBwcm9wcyk7XG4gIH0gZWxzZSB7XG4gICAgc3ludGhlc2l6ZWRQcm9wcyA9IGRlZmF1bHRQcm9wcztcbiAgfVxuXG4gIC8vIEZpbmFsbHkgLSBzZXQgdXAgbG9nZ2luZyBmb3IgdGhlIHNjcmFwIGJ1Y2tldFxuICBjb25zdCBmaW5hbFByb3BzID0gb3ZlcnJpZGVQcm9wcyhzeW50aGVzaXplZFByb3BzLCB7IHNlcnZlckFjY2Vzc0xvZ3NCdWNrZXQ6IGxvZ0J1Y2tldCB9KTtcblxuICBjb25zdCBzY3JpcHRCdWNrZXQgPSBuZXcgQnVja2V0KFxuICAgIHNjb3BlLFxuICAgIGlkLFxuICAgIGZpbmFsUHJvcHNcbiAgKTtcblxuICBhZGRDZm5TdXBwcmVzc1J1bGVzKGxvZ0J1Y2tldCwgW1xuICAgIHtcbiAgICAgIGlkOiBcIlczNVwiLFxuICAgICAgcmVhc29uOiBcIlRoaXMgaXMgYSBsb2cgYnVja2V0XCIsXG4gICAgfVxuICBdKTtcblxuICByZXR1cm4gc2NyaXB0QnVja2V0O1xufVxuXG4vKipcbiAqIEBzdW1tYXJ5IENyZWF0ZXMgYSBzdGFjayBuYW1lIGZvciBJbnRlZ3JhdGlvbiB0ZXN0c1xuICogQHBhcmFtIHtzdHJpbmd9IGZpbGVuYW1lIC0gdGhlIGZpbGVuYW1lIG9mIHRoZSBpbnRlZyB0ZXN0XG4gKiBAcmV0dXJucyB7c3RyaW5nfSAtIGEgc3RyaW5nIHdpdGggY3VycmVudCBmaWxlbmFtZSBhZnRlciByZW1vdmluZyBhbnl0aGluZyBiZWZvcmUgdGhlIHByZWZpeCAnLicgYW5kIHN1ZmZpeCAnLmpzJ1xuICogZS5nLiAnaW50ZWcuYXBpZ2F0ZXdheS1keW5hbW9kYi1DUlVELmpzJyB3aWxsIHJldHVybiAnYXBpZ2F0ZXdheS1keW5hbW9kYi1DUlVEJ1xuICovXG5leHBvcnQgZnVuY3Rpb24gZ2VuZXJhdGVJbnRlZ1N0YWNrTmFtZShmaWxlbmFtZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgZmlsZSA9IHBhdGguYmFzZW5hbWUoZmlsZW5hbWUsIHBhdGguZXh0bmFtZShmaWxlbmFtZSkpO1xuICBjb25zdCBzdGFja25hbWUgPSBmaWxlLnNsaWNlKGZpbGUubGFzdEluZGV4T2YoJy4nKSArIDEpLnJlcGxhY2UoL18vZywgJy0nKTtcbiAgcmV0dXJuIHN0YWNrbmFtZTtcbn1cblxuLy8gSGVscGVyIEZ1bmN0aW9uc1xuZXhwb3J0IGZ1bmN0aW9uIGdldFRlc3RWcGMoc3RhY2s6IFN0YWNrLCBwdWJsaWNGYWNpbmc6IGJvb2xlYW4gPSB0cnVlLCB1c2VyVnBjUHJvcHM/OiBlYzIuVnBjUHJvcHMpIHtcbiAgcmV0dXJuIGJ1aWxkVnBjKHN0YWNrLCB7XG4gICAgZGVmYXVsdFZwY1Byb3BzOiBwdWJsaWNGYWNpbmcgP1xuICAgICAgRGVmYXVsdFB1YmxpY1ByaXZhdGVWcGNQcm9wcygpIDpcbiAgICAgIERlZmF1bHRJc29sYXRlZFZwY1Byb3BzKCksXG4gICAgY29uc3RydWN0VnBjUHJvcHM6IHtcbiAgICAgIGVuYWJsZURuc0hvc3RuYW1lczogdHJ1ZSxcbiAgICAgIGVuYWJsZURuc1N1cHBvcnQ6IHRydWUsXG4gICAgICBpcEFkZHJlc3NlczogZWMyLklwQWRkcmVzc2VzLmNpZHIoJzE3Mi4xNjguMC4wLzE2JyksXG4gICAgfSxcbiAgICB1c2VyVnBjUHJvcHNcbiAgfSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGYWtlQ2VydGlmaWNhdGUoc2NvcGU6IENvbnN0cnVjdCwgaWQ6IHN0cmluZyk6IGFjbS5JQ2VydGlmaWNhdGUge1xuICByZXR1cm4gYWNtLkNlcnRpZmljYXRlLmZyb21DZXJ0aWZpY2F0ZUFybihcbiAgICBzY29wZSxcbiAgICBpZCxcbiAgICBcImFybjphd3M6YWNtOnVzLWVhc3QtMToxMjM0NTY3ODkwMTI6Y2VydGlmaWNhdGUvMTExMTIyMjItMzMzMy0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyXCJcbiAgKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIENyZWF0ZVRlc3RTdGF0ZU1hY2hpbmUoc2NvcGU6IENvbnN0cnVjdCwgaWQ6IHN0cmluZyk6IHNmbi5TdGF0ZU1hY2hpbmUge1xuICByZXR1cm4gbmV3IHNmbi5TdGF0ZU1hY2hpbmUoc2NvcGUsIGlkLCB7XG4gICAgZGVmaW5pdGlvbkJvZHk6IGRlZmF1bHRzLkNyZWF0ZVRlc3RTdGF0ZU1hY2hpbmVEZWZpbml0aW9uQm9keShzY29wZSwgaWQpXG4gIH0pO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gQ3JlYXRlVGVzdFN0YXRlTWFjaGluZURlZmluaXRpb25Cb2R5KHNjb3BlOiBDb25zdHJ1Y3QsIGlkOiBzdHJpbmcpOiBzZm4uRGVmaW5pdGlvbkJvZHkge1xuXG4gIGNvbnN0IHNtU3RlcCA9IG5ldyBsYW1iZGEuRnVuY3Rpb24oc2NvcGUsIGBsYW1iZGEke2lkfWAsIHtcbiAgICBjb2RlOiBuZXcgbGFtYmRhLklubGluZUNvZGUoXG4gICAgICBcImV4cG9ydHMuaGFuZGxlciA9IGFzeW5jIChldmVudCkgPT4gY29uc29sZS5sb2coZXZlbnQpXCJcbiAgICApLFxuICAgIHJ1bnRpbWU6IGxhbWJkYS5SdW50aW1lLk5PREVKU18yMF9YLFxuICAgIGhhbmRsZXI6IFwiaW5kZXguaGFuZGxlclwiLFxuICB9KTtcblxuICBjb25zdCB0YXNrID0gbmV3IHNmdGFza3MuTGFtYmRhSW52b2tlKHNjb3BlLCBgdGFzayR7aWR9YCwge1xuICAgIGxhbWJkYUZ1bmN0aW9uOiBzbVN0ZXAsXG4gIH0pO1xuXG4gIFN1cHByZXNzQ2ZuTmFnTGFtYmRhV2FybmluZ3MoU3RhY2sub2Yoc2NvcGUpKTtcblxuICByZXR1cm4gc2ZuLkRlZmluaXRpb25Cb2R5LmZyb21DaGFpbmFibGUodGFzayk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzdXBwcmVzc0N1c3RvbUhhbmRsZXJDZm5OYWdXYXJuaW5ncyhzdGFjazogU3RhY2ssIGhhbmRsZXJJZDogc3RyaW5nKSB7XG4gIHN0YWNrLm5vZGUuY2hpbGRyZW4uZm9yRWFjaChjaGlsZCA9PiB7XG4gICAgaWYgKGNoaWxkLm5vZGUuaWQgPT09IGhhbmRsZXJJZCkge1xuICAgICAgY29uc3QgaGFuZGxlckZ1bmN0aW9uID0gY2hpbGQubm9kZS5maW5kQ2hpbGQoJ0hhbmRsZXInKSBhcyBDZm5GdW5jdGlvbjtcbiAgICAgIGFkZENmblN1cHByZXNzUnVsZXMoaGFuZGxlckZ1bmN0aW9uLCBbeyBpZDogXCJXNThcIiwgcmVhc29uOiBcIkNESyBnZW5lcmF0ZWQgY3VzdG9tIHJlc291cmNlXCIgfV0pO1xuICAgICAgYWRkQ2ZuU3VwcHJlc3NSdWxlcyhoYW5kbGVyRnVuY3Rpb24sIFt7IGlkOiBcIlc4OVwiLCByZWFzb246IFwiQ0RLIGdlbmVyYXRlZCBjdXN0b20gcmVzb3VyY2VcIiB9XSk7XG4gICAgICBhZGRDZm5TdXBwcmVzc1J1bGVzKGhhbmRsZXJGdW5jdGlvbiwgW3sgaWQ6IFwiVzkyXCIsIHJlYXNvbjogXCJDREsgZ2VuZXJhdGVkIGN1c3RvbSByZXNvdXJjZVwiIH1dKTtcbiAgICB9XG4gIH0pO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gQ3JlYXRlVGVzdENhY2hlKHNjb3BlOiBDb25zdHJ1Y3QsIGlkOiBzdHJpbmcsIHZwYzogZWMyLklWcGMsIHBvcnQ/OiBudW1iZXIpIHtcbiAgY29uc3QgY2FjaGVQb3J0ID0gcG9ydCA/PyBHZXREZWZhdWx0Q2FjaGVQb3J0KCk7XG5cbiAgLy8gQ3JlYXRlIHRoZSBzdWJuZXQgZ3JvdXAgZnJvbSBhbGwgdGhlIGlzb2xhdGVkIHN1Ym5ldHMgaW4gdGhlIFZQQ1xuICBjb25zdCBzdWJuZXRHcm91cCA9IGNyZWF0ZUNhY2hlU3VibmV0R3JvdXAoc2NvcGUsIHZwYywgaWQpO1xuICBjb25zdCBlbXB0eVNHID0gbmV3IGVjMi5TZWN1cml0eUdyb3VwKHNjb3BlLCBgJHtpZH0tY2FjaGVzZ2AsIHtcbiAgICB2cGMsXG4gICAgYWxsb3dBbGxPdXRib3VuZDogdHJ1ZSxcbiAgfSk7XG4gIGFkZENmblN1cHByZXNzUnVsZXMoZW1wdHlTRywgW3sgaWQ6IFwiVzQwXCIsIHJlYXNvbjogXCJUZXN0IFJlc291cmNlXCIgfV0pO1xuICBhZGRDZm5TdXBwcmVzc1J1bGVzKGVtcHR5U0csIFt7IGlkOiBcIlc1XCIsIHJlYXNvbjogXCJUZXN0IFJlc291cmNlXCIgfV0pO1xuICBhZGRDZm5TdXBwcmVzc1J1bGVzKGVtcHR5U0csIFt7IGlkOiBcIlczNlwiLCByZWFzb246IFwiVGVzdCBSZXNvdXJjZVwiIH1dKTtcblxuICBjb25zdCBjYWNoZVByb3BzID0ge1xuICAgIGNsdXN0ZXJOYW1lOiBgJHtpZH0tY2RrLWNsdXN0ZXJgLFxuICAgIGNhY2hlTm9kZVR5cGU6IFwiY2FjaGUudDMubWVkaXVtXCIsXG4gICAgZW5naW5lOiBcIm1lbWNhY2hlZFwiLFxuICAgIG51bUNhY2hlTm9kZXM6IDIsXG4gICAgcG9ydDogY2FjaGVQb3J0LFxuICAgIGF6TW9kZTogXCJjcm9zcy1helwiLFxuICAgIHZwY1NlY3VyaXR5R3JvdXBJZHM6IFtlbXB0eVNHLnNlY3VyaXR5R3JvdXBJZF0sXG4gICAgY2FjaGVTdWJuZXRHcm91cE5hbWU6IHN1Ym5ldEdyb3VwLmNhY2hlU3VibmV0R3JvdXBOYW1lLFxuICB9O1xuXG4gIGNvbnN0IG5ld0NhY2hlID0gbmV3IGNhY2hlLkNmbkNhY2hlQ2x1c3RlcihcbiAgICBzY29wZSxcbiAgICBgJHtpZH0tY2x1c3RlcmAsXG4gICAgY2FjaGVQcm9wc1xuICApO1xuICBuZXdDYWNoZS5hZGREZXBlbmRlbmN5KHN1Ym5ldEdyb3VwKTtcbiAgcmV0dXJuIG5ld0NhY2hlO1xufVxuXG4vKipcbiAqIEFzc2VydHMgdGhhdCBhIEtNUyBrZXkgd2l0aCBhIHNwZWNpZmljIGRlc2NyaXB0aW9uIGV4aXN0cyBvbiBhIHJlc291cmNlXG4gKlxuICogQHBhcmFtIHN0YWNrIFRoZSBDbG91ZEZvcm1hdGlvbiBTdGFjayB0aGF0IGNvbnRhaW5zIHRoZSB0byB2YWxpZGF0ZS5cbiAqIEBwYXJhbSBwYXJlbnRSZXNvdXJjZVR5cGUgVGhlIHR5cGUgb2YgQ2xvdWRGb3JtYXRpb24gUmVzb3VyY2UgdGhhdCBzaG91bGQgaGF2ZSB0aGUga2V5IHNldCBvbiBpdCwgZS5nLiwgYEFXUzo6U05TOjpUb3BpY2AsIGV0Yy4uLlxuICogQHBhcmFtIGRlc2NyaXB0aW9uIFRoZSB2YWx1ZSBvZiB0aGUgRGVzY3JpcHRpb24gcHJvcGVydHkgb24gdGhlIEtNUyBLZXlcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGV4cGVjdEttc0tleUF0dGFjaGVkVG9Db3JyZWN0UmVzb3VyY2Uoc3RhY2s6IFN0YWNrLCBwYXJlbnRSZXNvdXJjZVR5cGU6IHN0cmluZywga2V5RGVzY3JpcHRpb246IHN0cmluZykge1xuICBjb25zdCB0ZW1wbGF0ZSA9IFRlbXBsYXRlLmZyb21TdGFjayhzdGFjayk7XG4gIGNvbnN0IHJlc291cmNlID0gdGVtcGxhdGUuZmluZFJlc291cmNlcygnQVdTOjpLTVM6OktleScsIHtcbiAgICBQcm9wZXJ0aWVzOiB7XG4gICAgICBEZXNjcmlwdGlvbjogTWF0Y2guZXhhY3Qoa2V5RGVzY3JpcHRpb24pXG4gICAgfVxuICB9KTtcblxuICBjb25zdCBbbG9naWNhbElkXSA9IE9iamVjdC5rZXlzKHJlc291cmNlKTtcbiAgVGVtcGxhdGUuZnJvbVN0YWNrKHN0YWNrKS5oYXNSZXNvdXJjZVByb3BlcnRpZXMocGFyZW50UmVzb3VyY2VUeXBlLCB7XG4gICAgS21zTWFzdGVyS2V5SWQ6IHtcbiAgICAgIFwiRm46OkdldEF0dFwiOiBbXG4gICAgICAgIGxvZ2ljYWxJZCxcbiAgICAgICAgXCJBcm5cIlxuICAgICAgXVxuICAgIH1cbiAgfSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBleHBlY3ROb25leGlzdGVuY2Uoc3RhY2s6IFN0YWNrLCB0eXBlOiBzdHJpbmcsIHByb3BzOiBvYmplY3QpIHtcbiAgY29uc3Qgc2hvdWxkRmluZE5vdGhpbmcgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spLmZpbmRSZXNvdXJjZXModHlwZSwgcHJvcHMpO1xuICBleHBlY3QoT2JqZWN0LmtleXMoc2hvdWxkRmluZE5vdGhpbmcpLmxlbmd0aCkudG9FcXVhbCgwKTtcbn1cblxuLy8gcHJpdmF0ZSBoZWxwZXIgY2xhc3MgdG8gc3VwcHJlc3MgdGhlIHN0YW5kYXJkIGNmbiBuYWcgd2FybmluZ3MgZm9yIGxhbWJkYSBmdW5jdGlvbnMgdXNlZCBpbiBpbnRlZyB0ZXN0c1xuY2xhc3MgQ2ZuTmFnTGFtYmRhQXNwZWN0IGltcGxlbWVudHMgSUFzcGVjdCB7XG4gIHB1YmxpYyB2aXNpdChub2RlOiBJQ29uc3RydWN0KTogdm9pZCB7XG4gICAgY29uc3QgcmVzb3VyY2UgPSBub2RlIGFzIENmblJlc291cmNlO1xuICAgIGlmIChyZXNvdXJjZS5jZm5SZXNvdXJjZVR5cGUgPT09ICdBV1M6OkxhbWJkYTo6RnVuY3Rpb24nKSB7XG4gICAgICBhZGRDZm5TdXBwcmVzc1J1bGVzKHJlc291cmNlLCBbXG4gICAgICAgIHsgaWQ6ICdXNTgnLCByZWFzb246ICdUaGlzIExhbWJkYSBGdW5jdGlvbiBpcyBjcmVhdGVkIGZvciBpbnRlZ3JhdGlvbiB0ZXN0aW5nIHB1cnBvc2VzIG9ubHkgYW5kIGlzIG5vdCBwYXJ0IG9mIGFuIGFjdHVhbCBjb25zdHJ1Y3QnIH0sXG4gICAgICAgIHsgaWQ6ICdXODknLCByZWFzb246ICdUaGlzIExhbWJkYSBGdW5jdGlvbiBpcyBjcmVhdGVkIGZvciBpbnRlZ3JhdGlvbiB0ZXN0aW5nIHB1cnBvc2VzIG9ubHkgYW5kIGlzIG5vdCBwYXJ0IG9mIGFuIGFjdHVhbCBjb25zdHJ1Y3QnIH0sXG4gICAgICAgIHsgaWQ6ICdXOTInLCByZWFzb246ICdUaGlzIExhbWJkYSBGdW5jdGlvbiBpcyBjcmVhdGVkIGZvciBpbnRlZ3JhdGlvbiB0ZXN0aW5nIHB1cnBvc2VzIG9ubHkgYW5kIGlzIG5vdCBwYXJ0IG9mIGFuIGFjdHVhbCBjb25zdHJ1Y3QnIH1cbiAgICAgIF0pO1xuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gQ3JlYXRlVGVzdEFwaShzdGFjazogU3RhY2ssIGlkOiBzdHJpbmcpOiBhcGkuTGFtYmRhUmVzdEFwaSB7XG4gIGNvbnN0IGxhbWJkYUZ1bmN0aW9uID0gbmV3IGxhbWJkYS5GdW5jdGlvbihzdGFjaywgYCR7aWR9RnVuY3Rpb25gLCB7XG4gICAgY29kZTogbGFtYmRhLkNvZGUuZnJvbUFzc2V0KGAke19fZGlybmFtZX0vbGFtYmRhYCksXG4gICAgcnVudGltZTogZGVmYXVsdHMuQ09NTUVSQ0lBTF9SRUdJT05fTEFNQkRBX05PREVfUlVOVElNRSxcbiAgICBoYW5kbGVyOiBcIi5oYW5kbGVyXCIsXG4gIH0pO1xuICBhZGRDZm5TdXBwcmVzc1J1bGVzKGxhbWJkYUZ1bmN0aW9uLCBbeyBpZDogXCJXNThcIiwgcmVhc29uOiBcIlRlc3QgUmVzb3VyY2VcIiB9XSk7XG4gIGFkZENmblN1cHByZXNzUnVsZXMobGFtYmRhRnVuY3Rpb24sIFt7IGlkOiBcIlc4OVwiLCByZWFzb246IFwiVGVzdCBSZXNvdXJjZVwiIH1dKTtcbiAgYWRkQ2ZuU3VwcHJlc3NSdWxlcyhsYW1iZGFGdW5jdGlvbiwgW3sgaWQ6IFwiVzkyXCIsIHJlYXNvbjogXCJUZXN0IFJlc291cmNlXCIgfV0pO1xuXG4gIGNvbnN0IHJlc3RBcGkgPSBuZXcgYXBpLkxhbWJkYVJlc3RBcGkoc3RhY2ssIGAke2lkfUFwaWAsIHtcbiAgICBoYW5kbGVyOiBsYW1iZGFGdW5jdGlvbixcbiAgICBkZWZhdWx0TWV0aG9kT3B0aW9uczoge1xuICAgICAgYXV0aG9yaXphdGlvblR5cGU6IGFwaS5BdXRob3JpemF0aW9uVHlwZS5DVVNUT00sXG4gICAgICBhdXRob3JpemVyOiBDcmVhdGVBcGlBdXRob3JpemVyKHN0YWNrLCBgJHtpZH0tYXV0aG9yaXplcmApXG4gICAgfVxuICB9KTtcblxuICBjb25zdCBuZXdEZXBsb3ltZW50ID0gcmVzdEFwaS5sYXRlc3REZXBsb3ltZW50O1xuICBpZiAobmV3RGVwbG95bWVudCkge1xuICAgIGFkZENmblN1cHByZXNzUnVsZXMobmV3RGVwbG95bWVudCwgW1xuICAgICAgeyBpZDogXCJXNjhcIiwgcmVhc29uOiBcIlRlc3QgUmVzb3VyY2VcIiB9LFxuICAgIF0pO1xuICB9XG5cbiAgY29uc3QgbmV3TWV0aG9kID0gcmVzdEFwaS5tZXRob2RzWzBdO1xuICBhZGRDZm5TdXBwcmVzc1J1bGVzKG5ld01ldGhvZCwgW3sgaWQ6IFwiVzU5XCIsIHJlYXNvbjogXCJUZXN0IFJlc291cmNlXCIgfV0pO1xuICBjb25zdCBuZXdNZXRob2RUd28gPSByZXN0QXBpLm1ldGhvZHNbMV07XG4gIGFkZENmblN1cHByZXNzUnVsZXMobmV3TWV0aG9kVHdvLCBbeyBpZDogXCJXNTlcIiwgcmVhc29uOiBcIlRlc3QgUmVzb3VyY2VcIiB9XSk7XG5cbiAgY29uc3QgbmV3U3RhZ2UgPSByZXN0QXBpLmRlcGxveW1lbnRTdGFnZTtcbiAgYWRkQ2ZuU3VwcHJlc3NSdWxlcyhuZXdTdGFnZSwgW3sgaWQ6IFwiVzY0XCIsIHJlYXNvbjogXCJUZXN0IFJlc291cmNlXCIgfV0pO1xuICBhZGRDZm5TdXBwcmVzc1J1bGVzKG5ld1N0YWdlLCBbeyBpZDogXCJXNjlcIiwgcmVhc29uOiBcIlRlc3QgUmVzb3VyY2VcIiB9XSk7XG5cbiAgcmV0dXJuIHJlc3RBcGk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBDcmVhdGVBcGlBdXRob3JpemVyKHN0YWNrOiBTdGFjaywgaWQ6IHN0cmluZyk6IGFwaS5JQXV0aG9yaXplciB7XG4gIGNvbnN0IGF1dGhGbiA9IG5ldyBsYW1iZGEuRnVuY3Rpb24oc3RhY2ssIGAke2lkfUF1dGhGdW5jdGlvbmAsIHtcbiAgICBjb2RlOiBsYW1iZGEuQ29kZS5mcm9tQXNzZXQoYCR7X19kaXJuYW1lfS9sYW1iZGFgKSxcbiAgICBydW50aW1lOiBkZWZhdWx0cy5DT01NRVJDSUFMX1JFR0lPTl9MQU1CREFfTk9ERV9SVU5USU1FLFxuICAgIGhhbmRsZXI6IFwiLmhhbmRsZXJcIixcbiAgfSk7XG4gIGFkZENmblN1cHByZXNzUnVsZXMoYXV0aEZuLCBbeyBpZDogXCJXNThcIiwgcmVhc29uOiBcIlRlc3QgUmVzb3VyY2VcIiB9XSk7XG4gIGFkZENmblN1cHByZXNzUnVsZXMoYXV0aEZuLCBbeyBpZDogXCJXODlcIiwgcmVhc29uOiBcIlRlc3QgUmVzb3VyY2VcIiB9XSk7XG4gIGFkZENmblN1cHByZXNzUnVsZXMoYXV0aEZuLCBbeyBpZDogXCJXOTJcIiwgcmVhc29uOiBcIlRlc3QgUmVzb3VyY2VcIiB9XSk7XG5cbiAgY29uc3QgYXV0aG9yaXplciA9IG5ldyBhcGkuUmVxdWVzdEF1dGhvcml6ZXIoc3RhY2ssIGlkLCB7XG4gICAgaGFuZGxlcjogYXV0aEZuLFxuICAgIGlkZW50aXR5U291cmNlczogW2FwaS5JZGVudGl0eVNvdXJjZS5oZWFkZXIoJ0F1dGhvcml6YXRpb24nKV1cbiAgfSk7XG5cbiAgcmV0dXJuIGF1dGhvcml6ZXI7XG59XG5cbi8vIENyZWF0ZSBhIHNob3J0LCB1bmlxdWUgdG8gdGhpcyBzdGFjayBuYW1lXG4vLyB0ZWNobmljYWxseSB0aGlzIGlzIG5vdCAxMDAlIE9LLCBhcyBpdCBvbmx5IHVzZXMgYSBwb3J0aW9uIG9mIHRoZVxuLy8gc3RhY2sgZ3VpZCAtIGJ1dCBpdCdzIGZvciB0ZXN0cyBvbmx5IHNvIGlmIHRoZSBsYXN0IHNlZ21lbnQgb2YgMiBzdGFjayBndWlkcyBjb2xsaWRlIHNvbWVkYXlcbi8vIChWRVJZIHVubGlrZWx5KSwganVzdCBydW5uaW5nIGFnYWluIHNob3VsZCB0YWtlIGNhcmUgb2YgaXQuXG5leHBvcnQgZnVuY3Rpb24gQ3JlYXRlU2hvcnRVbmlxdWVUZXN0TmFtZShzdHViOiBzdHJpbmcpIHtcbiAgY29uc3Qgc3RhY2tHdWlkID0gRm4uc2VsZWN0KDIsIEZuLnNwbGl0KCcvJywgYCR7QXdzLlNUQUNLX0lEfWApKTtcbiAgY29uc3QgZ3VpZFBvcnRpb24gPSBGbi5zZWxlY3QoNCwgRm4uc3BsaXQoJy0nLCBzdGFja0d1aWQpKTtcbiAgcmV0dXJuIEZuLmpvaW4oXCItXCIsIFtzdHViLCBndWlkUG9ydGlvbl0pO1xufVxuXG4vKipcbiAqIFVzZWQgdG8gc3VwcHJlc3MgY2ZuIG5hZyBXNTgsIFc4OSwgYW5kIFc5MiBydWxlcyBvbiBsYW1iZGEgaW50ZWdyYXRpb24gdGVzdCByZXNvdXJjZXMuXG4gKlxuICogQHBhcmFtIHN0YWNrIC0gVGhlIHN0YWNrIHRvIHN1cHByZXNzIGNmbiBuYWcgbGFtYmRhIHJ1bGVzIG9uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBTdXBwcmVzc0Nmbk5hZ0xhbWJkYVdhcm5pbmdzKHN0YWNrOiBTdGFjaykge1xuICBBc3BlY3RzLm9mKHN0YWNrKS5hZGQobmV3IENmbk5hZ0xhbWJkYUFzcGVjdCgpKTtcbn1cbiJdfQ==