UNPKG

@scloud/cdk-patterns

Version:

Serverless CDK patterns for common infrastructure needs

259 lines 39.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.githubActions = githubActions; const node_fs_1 = require("node:fs"); const aws_iam_1 = require("aws-cdk-lib/aws-iam"); const aws_cdk_lib_1 = require("aws-cdk-lib"); const constructs_1 = require("constructs"); const lodash_1 = __importDefault(require("lodash")); /** * To use this construct, call the githubActions() function to get a singleton instance. * * You'l want to call one of these two methods: * - ghaOidcRole: If you'd like to use keyless access to AWS resources from GitHub Actions. * NB you'll need an OIDC provider set up in the accout. * You can create one by calling ghaOidcProvider() or by creating one manually. * - ghaUser If you'd like to use an IAM user with an access key to access AWS resources from GitHub Actions. * The access key and secret access key will be output so you can add them GitHub Actions Secrets. * * A Construct that helps integrate GitHub Actions for deploying to AWS */ class GithubActions extends constructs_1.Construct { constructor(scope, id) { super(scope, id || 'GithubActions'); this.ghaInfo = { resources: { repositories: [], buckets: [], lambdas: [], services: [], distributions: [], tables: [], }, secrets: [], variables: [], }; this.stackName = aws_cdk_lib_1.Stack.of(scope).stackName; this.account = aws_cdk_lib_1.Stack.of(scope).account; this.scope = scope; } addGhaSecret(name, value) { const cfnOutput = new aws_cdk_lib_1.CfnOutput(this.scope, name, { value }); this.ghaInfo.secrets.push(cfnOutput.node.id); } addGhaVariable(name, type, value) { const variableName = `${lodash_1.default.lowerFirst(name)}${lodash_1.default.capitalize(type)}`; const cfnOutput = new aws_cdk_lib_1.CfnOutput(this.scope, variableName, { value }); this.ghaInfo.variables.push(cfnOutput.node.id); } addGhaLambda(name, lambda) { this.ghaInfo.resources.lambdas.push(lambda); this.addGhaVariable(name, 'lambda', lambda.functionName); } addGhaBucket(name, bucket) { this.ghaInfo.resources.buckets.push(bucket); this.addGhaVariable(name, 'bucket', bucket.bucketName); } addGhaDistribution(name, distribution) { this.ghaInfo.resources.distributions.push(distribution); this.addGhaVariable(name, 'distributionId', distribution.distributionId); } addGhaRepository(name, repository) { this.ghaInfo.resources.repositories.push(repository); this.addGhaVariable(name, 'ecr', repository.repositoryName); } addGhaTable(name, table, writeAccess = false) { if (!name || !table) return; this.ghaInfo.resources.tables.push({ table, writeAccess }); this.addGhaVariable(name, 'table', table.tableName); } ghaPolicy() { if (!this.policy) { const managedPolicyName = `gha-${this.stackName}-policy`; this.policy = new aws_iam_1.ManagedPolicy(this.scope, managedPolicyName, { managedPolicyName, }); // ECR repositories - push/pull images const repositoryArns = this.ghaInfo.resources.repositories .filter((repository) => repository) .map((repository) => repository.repositoryArn); if (repositoryArns.length > 0) { this.addToPolicy('ecrLogin', ['*'], ['ecr:GetAuthorizationToken']); this.addToPolicy('ecrRepositories', repositoryArns, [ 'ecr:GetDownloadUrlForLayer', 'ecr:BatchGetImage', 'ecr:BatchDeleteImage', 'ecr:CompleteLayerUpload', 'ecr:UploadLayerPart', 'ecr:InitiateLayerUpload', 'ecr:BatchCheckLayerAvailability', 'ecr:PutImage', 'ecr:ListImages', ]); } // Buckets - upload/sync const bucketArns = this.ghaInfo.resources.buckets .filter((bucket) => bucket) .map((bucket) => bucket.bucketArn); this.addToPolicy('buckets', bucketArns, [ 's3:ListBucket', ]); const bucketObjectsArns = bucketArns.map((arn) => `${arn}/*`); this.addToPolicy('bucketObjects', bucketObjectsArns, [ 's3:GetObject', 's3:PutObject', 's3:DeleteObject', ]); // Lambdas - update update with a new zip/container build const lambdaArns = this.ghaInfo.resources.lambdas .filter((lambda) => lambda) .map((lambda) => lambda.functionArn); this.addToPolicy('lambdas', lambdaArns, [ 'lambda:UpdateFunctionCode', // 'lambda:PublishVersion', ]); // Fargate services - update with a new container build const serviceArns = this.ghaInfo.resources.services .filter((service) => service) .map((service) => service.serviceArn); this.addToPolicy('fargateServices', serviceArns, [ 'ecs:UpdateService', ]); // Cloudfront distribution - cache invalidation const distributionArns = this.ghaInfo.resources.distributions .filter((distribution) => distribution !== undefined) // Not sure where to 'properly' get a distribution ARN from? .map((distribution) => `arn:aws:cloudfront::${this.account}:distribution/${distribution.distributionId}`); this.addToPolicy('distributions', distributionArns, [ 'cloudfront:CreateInvalidation', ]); // DynamoDB tables - read const dynamoTablesReadResources = []; for (const item of this.ghaInfo.resources.tables) { dynamoTablesReadResources.push(item.table.tableArn); dynamoTablesReadResources.push(`${item.table.tableArn}/index/*`); } this.addToPolicy('dynamoTablesRead', dynamoTablesReadResources, [ "dynamodb:GetItem", "dynamodb:BatchGetItem", "dynamodb:Query", "dynamodb:Scan", ]); // DynamoDB tables - write const dynamoTablesWriteResources = []; for (const item of this.ghaInfo.resources.tables) { dynamoTablesWriteResources.push(item.table.tableArn); dynamoTablesWriteResources.push(`${item.table.tableArn}/index/*`); } this.addToPolicy('dynamoTablesWrite', dynamoTablesWriteResources, [ "dynamodb:PutItem", "dynamodb:UpdateItem", "dynamodb:DeleteItem", "dynamodb:BatchWriteItem", ]); } return this.policy; } addToPolicy(name, resources, actions) { if (resources.length > 0) { this.policy.addStatements(new aws_iam_1.PolicyStatement({ actions, resources, sid: name, })); } } /** * Create an account-wide OIDC connection fo Guthub Actions. * * NB only one OIDC provider for GitHub can be created per AWS account (because the provider URL must be unique). * * To provide access to resources, you can create multiple roles that trust the provider so you'll probably want to call ghaOidcRole() instead. * See: https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services * @param repo What to grant access to. This is a minimum of a GitHub owner (user or org), optionally a repository name, and you can also specify a filter to limit access to e.g. a branch. */ ghaOidcProvider() { return new aws_iam_1.OpenIdConnectProvider(this.scope, 'oidc-provider', { url: 'https://token.actions.githubusercontent.com', clientIds: ['sts.amazonaws.com'], }); } /** * Add permissions to the GitHub OIDC role that allow workflows to access the AWS resources in this stack that need to be updated at build time. * See: https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services * @param repo The repository to grant access to (owner and name). You can also specify a filter to limit access e.g. to a branch. */ ghaOidcRole(repo, openIdConnectProvider) { const provider = openIdConnectProvider || aws_iam_1.OpenIdConnectProvider.fromOpenIdConnectProviderArn(this.scope, `oidc-provider-${this.account}`, `arn:aws:iam::${this.account}:oidc-provider/token.actions.githubusercontent.com`); // Grant only requests coming from the specific owner/repository/filter to assume this role. const role = new aws_iam_1.Role(this.scope, `gha-oidc-role-${this.stackName}`, { assumedBy: new aws_iam_1.WebIdentityPrincipal(provider.openIdConnectProviderArn, { StringLike: { 'token.actions.githubusercontent.com:sub': [`repo:${repo.owner}/${repo.repo}:${repo.filter || '*'}`], }, }), managedPolicies: [ this.ghaPolicy(), ], roleName: `gha-oidc-${this.stackName}`, description: `Role for GitHub Actions to assume when deploying to ${this.stackName}`, }); this.addGhaVariable('ghaOidc', 'Role', role.roleArn); this.saveGhaValues(); return role; } /** * @deprecated: use githubActions().ghaOidcRole() instead. * A user for Gihud Actions CI/CD. */ ghaUser(username) { // A user with the policy attached const user = new aws_iam_1.User(this.scope, 'ghaUser', { userName: username || `gha-${this.stackName}` }); const policy = this.ghaPolicy(); user.addManagedPolicy(policy); // Credentials let accessKey; if (!process.env.REKEY) { accessKey = new aws_iam_1.CfnAccessKey(this.scope, 'ghaUserAccessKey', { userName: user.userName, }); // Access key details for GHA secrets this.addGhaSecret('awsAccessKeyId', accessKey.ref); this.addGhaSecret('awsSecretAccessKey', accessKey.attrSecretAccessKey); } this.saveGhaValues(); return { user, accessKey }; } saveGhaValues() { if ((0, node_fs_1.existsSync)('cdk.out')) { // Write out the list of secret and variable names: (0, node_fs_1.writeFileSync)(`cdk.out/${this.stackName}.ghaSecrets.json`, JSON.stringify(this.ghaInfo.secrets)); (0, node_fs_1.writeFileSync)(`cdk.out/${this.stackName}.ghaVariables.json`, JSON.stringify(this.ghaInfo.variables)); } // Flush ghaInfo so we're free to build another stack if needed: this.ghaInfo.resources.buckets = []; this.ghaInfo.resources.distributions = []; this.ghaInfo.resources.lambdas = []; this.ghaInfo.resources.repositories = []; this.ghaInfo.resources.services = []; this.ghaInfo.secrets = []; this.ghaInfo.variables = []; } } /** * Returns a singleton instance of the GithubActions construct by default. * For most use cases, only one OIDC role is needed in GitHub Actions. * If you need different roles with different permissions, you can create multiple instances of this construct by passing a different id. * @param id Optional: by default the id will be 'GithubActions', which gives you a singleton instance. */ function githubActions(scope, id) { // Find the existing instance in the stack, if present: const stack = aws_cdk_lib_1.Stack.of(scope); const existing = stack.node.tryFindChild(id || 'GithubActions'); return existing || new GithubActions(scope, id); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiR2l0aHViQWN0aW9ucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9HaXRodWJBY3Rpb25zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBaVVBLHNDQUtDO0FBclVELHFDQUFvRDtBQUNwRCxpREFFNkI7QUFDN0IsNkNBQStDO0FBTS9DLDJDQUF1QztBQUN2QyxvREFBdUI7QUFHdkI7Ozs7Ozs7Ozs7O0dBV0c7QUFDSCxNQUFNLGFBQWMsU0FBUSxzQkFBUztJQXVCbkMsWUFDRSxLQUFnQixFQUNoQixFQUFXO1FBRVgsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLElBQUksZUFBZSxDQUFDLENBQUM7UUFqQnRDLFlBQU8sR0FBRztZQUNSLFNBQVMsRUFBRTtnQkFDVCxZQUFZLEVBQWlCLEVBQUU7Z0JBQy9CLE9BQU8sRUFBYSxFQUFFO2dCQUN0QixPQUFPLEVBQWUsRUFBRTtnQkFDeEIsUUFBUSxFQUFxQixFQUFFO2dCQUMvQixhQUFhLEVBQW1CLEVBQUU7Z0JBQ2xDLE1BQU0sRUFBK0MsRUFBRTthQUN4RDtZQUNELE9BQU8sRUFBWSxFQUFFO1lBQ3JCLFNBQVMsRUFBWSxFQUFFO1NBQ3hCLENBQUM7UUFPQSxJQUFJLENBQUMsU0FBUyxHQUFHLG1CQUFLLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLFNBQVMsQ0FBQztRQUMzQyxJQUFJLENBQUMsT0FBTyxHQUFHLG1CQUFLLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUN2QyxJQUFJLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQztJQUNyQixDQUFDO0lBRUQsWUFBWSxDQUNWLElBQVksRUFDWixLQUFhO1FBRWIsTUFBTSxTQUFTLEdBQUcsSUFBSSx1QkFBUyxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsSUFBSSxFQUFFLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztRQUM3RCxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJ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QUMsV0FBVyxJQUFJLENBQUMsU0FBUyxvQkFBb0IsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQztRQUN2RyxDQUFDO1FBRUQsZ0VBQWdFO1FBQ2hFLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7UUFDcEMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsYUFBYSxHQUFHLEVBQUUsQ0FBQztRQUMxQyxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO1FBQ3BDLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLFlBQVksR0FBRyxFQUFFLENBQUM7UUFDekMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsUUFBUSxHQUFHLEVBQUUsQ0FBQztRQUNyQyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7UUFDMUIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEdBQUcsRUFBRSxDQUFDO0lBQzlCLENBQUM7Q0FDRjtBQUVEOzs7OztHQUtHO0FBQ0gsU0FBZ0IsYUFBYSxDQUFDLEtBQWdCLEVBQUUsRUFBVztJQUN6RCx1REFBdUQ7SUFDdkQsTUFBTSxLQUFLLEdBQUcsbUJBQUssQ0FBQyxFQUFFLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDOUIsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsRUFBRSxJQUFJLGVBQWUsQ0FBQyxDQUFDO0lBQ2hFLE9BQU8sUUFBeUIsSUFBSSxJQUFJLGFBQWEsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7QUFDbkUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIlxuaW1wb3J0IHsgZXhpc3RzU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ25vZGU6ZnMnO1xuaW1wb3J0IHtcbiAgQ2ZuQWNjZXNzS2V5LCBNYW5hZ2VkUG9saWN5LCBPcGVuSWRDb25uZWN0UHJvdmlkZXIsIFBvbGljeVN0YXRlbWVudCwgUm9sZSwgVXNlciwgV2ViSWRlbnRpdHlQcmluY2lwYWwsXG59IGZyb20gJ2F3cy1jZGstbGliL2F3cy1pYW0nO1xuaW1wb3J0IHsgQ2ZuT3V0cHV0LCBTdGFjayB9IGZyb20gJ2F3cy1jZGstbGliJztcbmltcG9ydCB7IElSZXBvc2l0b3J5IH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWVjcic7XG5pbXBvcnQgeyBJRnVuY3Rpb24gfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtbGFtYmRhJztcbmltcG9ydCB7IElGYXJnYXRlU2VydmljZSB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1lY3MnO1xuaW1wb3J0IHsgSUJ1Y2tldCB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1zMyc7XG5pbXBvcnQgeyBJRGlzdHJpYnV0aW9uIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWNsb3VkZnJvbnQnO1xuaW1wb3J0IHsgQ29uc3RydWN0IH0gZnJvbSAnY29uc3RydWN0cyc7XG5pbXBvcnQgXyBmcm9tICdsb2Rhc2gnO1xuaW1wb3J0IHsgSVRhYmxlIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWR5bmFtb2RiJztcblxuLyoqXG4gKiBUbyB1c2UgdGhpcyBjb25zdHJ1Y3QsIGNhbGwgdGhlIGdpdGh1YkFjdGlvbnMoKSBmdW5jdGlvbiB0byBnZXQgYSBzaW5nbGV0b24gaW5zdGFuY2UuXG4gKlxuICogWW91J2wgd2FudCB0byBjYWxsIG9uZSBvZiB0aGVzZSB0d28gbWV0aG9kczpcbiAqIC0gZ2hhT2lkY1JvbGU6IElmIHlvdSdkIGxpa2UgdG8gdXNlIGtleWxlc3MgYWNjZXNzIHRvIEFXUyByZXNvdXJjZXMgZnJvbSBHaXRIdWIgQWN0aW9ucy5cbiAqIE5CIHlvdSdsbCBuZWVkIGFuIE9JREMgcHJvdmlkZXIgc2V0IHVwIGluIHRoZSBhY2NvdXQuXG4gKiBZb3UgY2FuIGNyZWF0ZSBvbmUgYnkgY2FsbGluZyBnaGFPaWRjUHJvdmlkZXIoKSBvciBieSBjcmVhdGluZyBvbmUgbWFudWFsbHkuXG4gKiAtIGdoYVVzZXIgSWYgeW91J2QgbGlrZSB0byB1c2UgYW4gSUFNIHVzZXIgd2l0aCBhbiBhY2Nlc3Mga2V5IHRvIGFjY2VzcyBBV1MgcmVzb3VyY2VzIGZyb20gR2l0SHViIEFjdGlvbnMuXG4gKiBUaGUgYWNjZXNzIGtleSBhbmQgc2VjcmV0IGFjY2VzcyBrZXkgd2lsbCBiZSBvdXRwdXQgc28geW91IGNhbiBhZGQgdGhlbSBHaXRIdWIgQWN0aW9ucyBTZWNyZXRzLlxuICpcbiAqIEEgQ29uc3RydWN0IHRoYXQgaGVscHMgaW50ZWdyYXRlIEdpdEh1YiBBY3Rpb25zIGZvciBkZXBsb3lpbmcgdG8gQVdTXG4gKi9cbmNsYXNzIEdpdGh1YkFjdGlvbnMgZXh0ZW5kcyBDb25zdHJ1Y3Qge1xuICAvLyBVc2luZyAndGhpcy5zY29wZScgZm9yIHRoZSBwYXJlbnQgYmVjYXVzZSB1c2luZyAndGhpcycgY3JlYXRlcyBsb25nZXIgbmFtZXMuXG4gIHNjb3BlOiBDb25zdHJ1Y3Q7XG5cbiAgc3RhY2tOYW1lOiBzdHJpbmc7XG5cbiAgYWNjb3VudDogc3RyaW5nO1xuXG4gIHBvbGljeTogTWFuYWdlZFBvbGljeTtcblxuICBnaGFJbmZvID0ge1xuICAgIHJlc291cmNlczoge1xuICAgICAgcmVwb3NpdG9yaWVzOiA8SVJlcG9zaXRvcnlbXT5bXSxcbiAgICAgIGJ1Y2tldHM6IDxJQnVja2V0W10+W10sXG4gICAgICBsYW1iZGFzOiA8SUZ1bmN0aW9uW10+W10sXG4gICAgICBzZXJ2aWNlczogPElGYXJnYXRlU2VydmljZVtdPltdLFxuICAgICAgZGlzdHJpYnV0aW9uczogPElEaXN0cmlidXRpb25bXT5bXSxcbiAgICAgIHRhYmxlczogPHsgdGFibGU6IElUYWJsZSwgd3JpdGVBY2Nlc3M/OiBib29sZWFuOyB9W10+W10sXG4gICAgfSxcbiAgICBzZWNyZXRzOiA8c3RyaW5nW10+W10sXG4gICAgdmFyaWFibGVzOiA8c3RyaW5nW10+W10sXG4gIH07XG5cbiAgY29uc3RydWN0b3IoXG4gICAgc2NvcGU6IENvbnN0cnVjdCxcbiAgICBpZD86IHN0cmluZ1xuICApIHtcbiAgICBzdXBlcihzY29wZSwgaWQgfHwgJ0dpdGh1YkFjdGlvbnMnKTtcbiAgICB0aGlzLnN0YWNrTmFtZSA9IFN0YWNrLm9mKHNjb3BlKS5zdGFja05hbWU7XG4gICAgdGhpcy5hY2NvdW50ID0gU3RhY2sub2Yoc2NvcGUpLmFjY291bnQ7XG4gICAgdGhpcy5zY29wZSA9IHNjb3BlO1xuICB9XG5cbiAgYWRkR2hhU2VjcmV0KFxuICAgIG5hbWU6IHN0cmluZyxcbiAgICB2YWx1ZTogc3RyaW5nLFxuICApIHtcbiAgICBjb25zdCBjZm5PdXRwdXQgPSBuZXcgQ2ZuT3V0cHV0KHRoaXMuc2NvcGUsIG5hbWUsIHsgdmFsdWUgfSk7XG4gICAgdGhpcy5naGFJbmZvLnNlY3JldHMucHVzaChjZm5PdXRwdXQubm9kZS5pZCk7XG4gIH1cblxuICBhZGRHaGFWYXJpYWJsZShcbiAgICBuYW1lOiBzdHJpbmcsXG4gICAgdHlwZTogc3RyaW5nLFxuICAgIHZhbHVlOiBzdHJpbmcsXG4gICkge1xuICAgIGNvbnN0IHZhcmlhYmxlTmFtZSA9IGAke18ubG93ZXJGaXJzdChuYW1lKX0ke18uY2FwaXRhbGl6ZSh0eXBlKX1gO1xuICAgIGNvbnN0IGNmbk91dHB1dCA9IG5ldyBDZm5PdXRwdXQodGhpcy5zY29wZSwgdmFyaWFibGVOYW1lLCB7IHZhbHVlIH0pO1xuICAgIHRoaXMuZ2hhSW5mby52YXJpYWJsZXMucHVzaChjZm5PdXRwdXQubm9kZS5pZCk7XG4gIH1cblxuICBhZGRHaGFMYW1iZGEoXG4gICAgbmFtZTogc3RyaW5nLFxuICAgIGxhbWJkYTogSUZ1bmN0aW9uLFxuICApIHtcbiAgICB0aGlzLmdoYUluZm8ucmVzb3VyY2VzLmxhbWJkYXMucHVzaChsYW1iZGEpO1xuICAgIHRoaXMuYWRkR2hhVmFyaWFibGUobmFtZSwgJ2xhbWJkYScsIGxhbWJkYS5mdW5jdGlvbk5hbWUpO1xuICB9XG5cbiAgYWRkR2hhQnVja2V0KFxuICAgIG5hbWU6IHN0cmluZyxcbiAgICBidWNrZXQ6IElCdWNrZXQsXG4gICkge1xuICAgIHRoaXMuZ2hhSW5mby5yZXNvdXJjZXMuYnVja2V0cy5wdXNoKGJ1Y2tldCk7XG4gICAgdGhpcy5hZGRHaGFWYXJpYWJsZShuYW1lLCAnYnVja2V0JywgYnVja2V0LmJ1Y2tldE5hbWUpO1xuICB9XG5cbiAgYWRkR2hhRGlzdHJpYnV0aW9uKFxuICAgIG5hbWU6IHN0cmluZyxcbiAgICBkaXN0cmlidXRpb246IElEaXN0cmlidXRpb24sXG4gICkge1xuICAgIHRoaXMuZ2hhSW5mby5yZXNvdXJjZXMuZGlzdHJpYnV0aW9ucy5wdXNoKGRpc3RyaWJ1dGlvbik7XG4gICAgdGhpcy5hZGRHaGFWYXJpYWJsZShuYW1lLCAnZGlzdHJpYnV0aW9uSWQnLCBkaXN0cmlidXRpb24uZGlzdHJpYnV0aW9uSWQpO1xuICB9XG5cbiAgYWRkR2hhUmVwb3NpdG9yeShcbiAgICBuYW1lOiBzdHJpbmcsXG4gICAgcmVwb3NpdG9yeTogSVJlcG9zaXRvcnksXG4gICkge1xuICAgIHRoaXMuZ2hhSW5mby5yZXNvdXJjZXMucmVwb3NpdG9yaWVzLnB1c2gocmVwb3NpdG9yeSk7XG4gICAgdGhpcy5hZGRHaGFWYXJpYWJsZShuYW1lLCAnZWNyJywgcmVwb3NpdG9yeS5yZXBvc2l0b3J5TmFtZSk7XG4gIH1cblxuICBhZGRHaGFUYWJsZShcbiAgICBuYW1lOiBzdHJpbmcsXG4gICAgdGFibGU6IElUYWJsZSxcbiAgICB3cml0ZUFjY2VzczogYm9vbGVhbiA9IGZhbHNlLFxuICApIHtcbiAgICBpZiAoIW5hbWUgfHwgIXRhYmxlKSByZXR1cm47XG4gICAgdGhpcy5naGFJbmZvLnJlc291cmNlcy50YWJsZXMucHVzaCh7IHRhYmxlLCB3cml0ZUFjY2VzcyB9KTtcbiAgICB0aGlzLmFkZEdoYVZhcmlhYmxlKG5hbWUsICd0YWJsZScsIHRhYmxlLnRhYmxlTmFtZSk7XG4gIH1cblxuICBnaGFQb2xpY3koKSB7XG4gICAgaWYgKCF0aGlzLnBvbGljeSkge1xuICAgICAgY29uc3QgbWFuYWdlZFBvbGljeU5hbWUgPSBgZ2hhLSR7dGhpcy5zdGFja05hbWV9LXBvbGljeWA7XG4gICAgICB0aGlzLnBvbGljeSA9IG5ldyBNYW5hZ2VkUG9saWN5KHRoaXMuc2NvcGUsIG1hbmFnZWRQb2xpY3lOYW1lLCB7XG4gICAgICAgIG1hbmFnZWRQb2xpY3lOYW1lLFxuICAgICAgfSk7XG5cbiAgICAgIC8vIEVDUiByZXBvc2l0b3JpZXMgLSBwdXNoL3B1bGwgaW1hZ2VzXG4gICAgICBjb25zdCByZXBvc2l0b3J5QXJucyA9IHRoaXMuZ2hhSW5mby5yZXNvdXJjZXMucmVwb3NpdG9yaWVzXG4gICAgICAgIC5maWx0ZXIoKHJlcG9zaXRvcnkpID0+IHJlcG9zaXRvcnkpXG4gICAgICAgIC5tYXAoKHJlcG9zaXRvcnkpID0+IHJlcG9zaXRvcnkucmVwb3NpdG9yeUFybik7XG4gICAgICBpZiAocmVwb3NpdG9yeUFybnMubGVuZ3RoID4gMCkge1xuICAgICAgICB0aGlzLmFkZFRvUG9saWN5KCdlY3JMb2dpbicsIFsnKiddLCBbJ2VjcjpHZXRBdXRob3JpemF0aW9uVG9rZW4nXSk7XG4gICAgICAgIHRoaXMuYWRkVG9Qb2xpY3koJ2VjclJlcG9zaXRvcmllcycsIHJlcG9zaXRvcnlBcm5zLCBbXG4gICAgICAgICAgJ2VjcjpHZXREb3dubG9hZFVybEZvckxheWVyJyxcbiAgICAgICAgICAnZWNyOkJhdGNoR2V0SW1hZ2UnLFxuICAgICAgICAgICdlY3I6QmF0Y2hEZWxldGVJbWFnZScsXG4gICAgICAgICAgJ2VjcjpDb21wbGV0ZUxheWVyVXBsb2FkJyxcbiAgICAgICAgICAnZWNyOlVwbG9hZExheWVyUGFydCcsXG4gICAgICAgICAgJ2VjcjpJbml0aWF0ZUxheWVyVXBsb2FkJyxcbiAgICAgICAgICAnZWNyOkJhdGNoQ2hlY2tMYXllckF2YWlsYWJpbGl0eScsXG4gICAgICAgICAgJ2VjcjpQdXRJbWFnZScsXG4gICAgICAgICAgJ2VjcjpMaXN0SW1hZ2VzJyxcbiAgICAgICAgXSk7XG4gICAgICB9XG4gICAgICAvLyBCdWNrZXRzIC0gdXBsb2FkL3N5bmNcbiAgICAgIGNvbnN0IGJ1Y2tldEFybnMgPSB0aGlzLmdoYUluZm8ucmVzb3VyY2VzLmJ1Y2tldHNcbiAgICAgICAgLmZpbHRlcigoYnVja2V0KSA9PiBidWNrZXQpXG4gICAgICAgIC5tYXAoKGJ1Y2tldCkgPT4gYnVja2V0LmJ1Y2tldEFybik7XG4gICAgICB0aGlzLmFkZFRvUG9saWN5KCdidWNrZXRzJywgYnVja2V0QXJucywgW1xuICAgICAgICAnczM6TGlzdEJ1Y2tldCcsXG4gICAgICBdKTtcbiAgICAgIGNvbnN0IGJ1Y2tldE9iamVjdHNBcm5zID0gYnVja2V0QXJucy5tYXAoKGFybikgPT4gYCR7YXJufS8qYCk7XG4gICAgICB0aGlzLmFkZFRvUG9saWN5KCdidWNrZXRPYmplY3RzJywgYnVja2V0T2JqZWN0c0FybnMsIFtcbiAgICAgICAgJ3MzOkdldE9iamVjdCcsXG4gICAgICAgICdzMzpQdXRPYmplY3QnLFxuICAgICAgICAnczM6RGVsZXRlT2JqZWN0JyxcbiAgICAgIF0pO1xuXG4gICAgICAvLyBMYW1iZGFzIC0gdXBkYXRlIHVwZGF0ZSB3aXRoIGEgbmV3IHppcC9jb250YWluZXIgYnVpbGRcbiAgICAgIGNvbnN0IGxhbWJkYUFybnMgPSB0aGlzLmdoYUluZm8ucmVzb3VyY2VzLmxhbWJkYXNcbiAgICAgICAgLmZpbHRlcigobGFtYmRhKSA9PiBsYW1iZGEpXG4gICAgICAgIC5tYXAoKGxhbWJkYSkgPT4gbGFtYmRhLmZ1bmN0aW9uQXJuKTtcbiAgICAgIHRoaXMuYWRkVG9Qb2xpY3koJ2xhbWJkYXMnLCBsYW1iZGFBcm5zLCBbXG4gICAgICAgICdsYW1iZGE6VXBkYXRlRnVuY3Rpb25Db2RlJyxcbiAgICAgICAgLy8gJ2xhbWJkYTpQdWJsaXNoVmVyc2lvbicsXG4gICAgICBdKTtcblxuICAgICAgLy8gRmFyZ2F0ZSBzZXJ2aWNlcyAtIHVwZGF0ZSB3aXRoIGEgbmV3IGNvbnRhaW5lciBidWlsZFxuICAgICAgY29uc3Qgc2VydmljZUFybnMgPSB0aGlzLmdoYUluZm8ucmVzb3VyY2VzLnNlcnZpY2VzXG4gICAgICAgIC5maWx0ZXIoKHNlcnZpY2UpID0+IHNlcnZpY2UpXG4gICAgICAgIC5tYXAoKHNlcnZpY2UpID0+IHNlcnZpY2Uuc2VydmljZUFybik7XG4gICAgICB0aGlzLmFkZFRvUG9saWN5KCdmYXJnYXRlU2VydmljZXMnLCBzZXJ2aWNlQXJucywgW1xuICAgICAgICAnZWNzOlVwZGF0ZVNlcnZpY2UnLFxuICAgICAgXSk7XG5cbiAgICAgIC8vIENsb3VkZnJvbnQgZGlzdHJpYnV0aW9uIC0gY2FjaGUgaW52YWxpZGF0aW9uXG4gICAgICBjb25zdCBkaXN0cmlidXRpb25Bcm5zID0gdGhpcy5naGFJbmZvLnJlc291cmNlcy5kaXN0cmlidXRpb25zXG4gICAgICAgIC5maWx0ZXIoKGRpc3RyaWJ1dGlvbikgPT4gZGlzdHJpYnV0aW9uICE9PSB1bmRlZmluZWQpXG4gICAgICAgIC8vIE5vdCBzdXJlIHdoZXJlIHRvICdwcm9wZXJseScgZ2V0IGEgZGlzdHJpYnV0aW9uIEFSTiBmcm9tP1xuICAgICAgICAubWFwKChkaXN0cmlidXRpb24pID0+IGBhcm46YXdzOmNsb3VkZnJvbnQ6OiR7dGhpcy5hY2NvdW50fTpkaXN0cmlidXRpb24vJHtkaXN0cmlidXRpb24uZGlzdHJpYnV0aW9uSWR9YCk7XG4gICAgICB0aGlzLmFkZFRvUG9saWN5KCdkaXN0cmlidXRpb25zJywgZGlzdHJpYnV0aW9uQXJucywgW1xuICAgICAgICAnY2xvdWRmcm9udDpDcmVhdGVJbnZhbGlkYXRpb24nLFxuICAgICAgXSk7XG5cbiAgICAgIC8vIER5bmFtb0RCIHRhYmxlcyAtIHJlYWRcbiAgICAgIGNvbnN0IGR5bmFtb1RhYmxlc1JlYWRSZXNvdXJjZXM6IHN0cmluZ1tdID0gW107XG4gICAgICBmb3IgKGNvbnN0IGl0ZW0gb2YgdGhpcy5naGFJbmZvLnJlc291cmNlcy50YWJsZXMpIHtcbiAgICAgICAgZHluYW1vVGFibGVzUmVhZFJlc291cmNlcy5wdXNoKGl0ZW0udGFibGUudGFibGVBcm4pO1xuICAgICAgICBkeW5hbW9UYWJsZXNSZWFkUmVzb3VyY2VzLnB1c2goYCR7aXRlbS50YWJsZS50YWJsZUFybn0vaW5kZXgvKmApO1xuICAgICAgfVxuICAgICAgdGhpcy5hZGRUb1BvbGljeSgnZHluYW1vVGFibGVzUmVhZCcsIGR5bmFtb1RhYmxlc1JlYWRSZXNvdXJjZXMsIFtcbiAgICAgICAgXCJkeW5hbW9kYjpHZXRJdGVtXCIsXG4gICAgICAgIFwiZHluYW1vZGI6QmF0Y2hHZXRJdGVtXCIsXG4gICAgICAgIFwiZHluYW1vZGI6UXVlcnlcIixcbiAgICAgICAgXCJkeW5hbW9kYjpTY2FuXCIsXG4gICAgICBdKTtcblxuICAgICAgLy8gRHluYW1vREIgdGFibGVzIC0gd3JpdGVcbiAgICAgIGNvbnN0IGR5bmFtb1RhYmxlc1dyaXRlUmVzb3VyY2VzOiBzdHJpbmdbXSA9IFtdO1xuICAgICAgZm9yIChjb25zdCBpdGVtIG9mIHRoaXMuZ2hhSW5mby5yZXNvdXJjZXMudGFibGVzKSB7XG4gICAgICAgIGR5bmFtb1RhYmxlc1dyaXRlUmVzb3VyY2VzLnB1c2goaXRlbS50YWJsZS50YWJsZUFybik7XG4gICAgICAgIGR5bmFtb1RhYmxlc1dyaXRlUmVzb3VyY2VzLnB1c2goYCR7aXRlbS50YWJsZS50YWJsZUFybn0vaW5kZXgvKmApO1xuICAgICAgfVxuICAgICAgdGhpcy5hZGRUb1BvbGljeSgnZHluYW1vVGFibGVzV3JpdGUnLCBkeW5hbW9UYWJsZXNXcml0ZVJlc291cmNlcywgW1xuICAgICAgICBcImR5bmFtb2RiOlB1dEl0ZW1cIixcbiAgICAgICAgXCJkeW5hbW9kYjpVcGRhdGVJdGVtXCIsXG4gICAgICAgIFwiZHluYW1vZGI6RGVsZXRlSXRlbVwiLFxuICAgICAgICBcImR5bmFtb2RiOkJhdGNoV3JpdGVJdGVtXCIsXG4gICAgICBdKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5wb2xpY3k7XG4gIH1cblxuICBhZGRUb1BvbGljeShuYW1lOiBzdHJpbmcsIHJlc291cmNlczogc3RyaW5nW10sIGFjdGlvbnM6IHN0cmluZ1tdKSB7XG4gICAgaWYgKHJlc291cmNlcy5sZW5ndGggPiAwKSB7XG4gICAgICB0aGlzLnBvbGljeS5hZGRTdGF0ZW1lbnRzKG5ldyBQb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgICBhY3Rpb25zLFxuICAgICAgICByZXNvdXJjZXMsXG4gICAgICAgIHNpZDogbmFtZSxcbiAgICAgIH0pKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlIGFuIGFjY291bnQtd2lkZSBPSURDIGNvbm5lY3Rpb24gZm8gR3V0aHViIEFjdGlvbnMuXG4gICAqXG4gICAqIE5CIG9ubHkgb25lIE9JREMgcHJvdmlkZXIgZm9yIEdpdEh1YiBjYW4gYmUgY3JlYXRlZCBwZXIgQVdTIGFjY291bnQgKGJlY2F1c2UgdGhlIHByb3ZpZGVyIFVSTCBtdXN0IGJlIHVuaXF1ZSkuXG4gICAqXG4gICAqIFRvIHByb3ZpZGUgYWNjZXNzIHRvIHJlc291cmNlcywgeW91IGNhbiBjcmVhdGUgbXVsdGlwbGUgcm9sZXMgdGhhdCB0cnVzdCB0aGUgcHJvdmlkZXIgc28geW91J2xsIHByb2JhYmx5IHdhbnQgdG8gY2FsbCBnaGFPaWRjUm9sZSgpIGluc3RlYWQuXG4gICAqIFNlZTogaHR0cHM6Ly9kb2NzLmdpdGh1Yi5jb20vZW4vYWN0aW9ucy9kZXBsb3ltZW50L3NlY3VyaXR5LWhhcmRlbmluZy15b3VyLWRlcGxveW1lbnRzL2NvbmZpZ3VyaW5nLW9wZW5pZC1jb25uZWN0LWluLWFtYXpvbi13ZWItc2VydmljZXNcbiAgICogQHBhcmFtIHJlcG8gV2hhdCB0byBncmFudCBhY2Nlc3MgdG8uIFRoaXMgaXMgYSBtaW5pbXVtIG9mIGEgR2l0SHViIG93bmVyICh1c2VyIG9yIG9yZyksIG9wdGlvbmFsbHkgYSByZXBvc2l0b3J5IG5hbWUsIGFuZCB5b3UgY2FuIGFsc28gc3BlY2lmeSBhIGZpbHRlciB0byBsaW1pdCBhY2Nlc3MgdG8gZS5nLiBhIGJyYW5jaC5cbiAgICovXG4gIGdoYU9pZGNQcm92aWRlcigpOiBPcGVuSWRDb25uZWN0UHJvdmlkZXIge1xuICAgIHJldHVybiBuZXcgT3BlbklkQ29ubmVjdFByb3ZpZGVyKHRoaXMuc2NvcGUsICdvaWRjLXByb3ZpZGVyJywge1xuICAgICAgdXJsOiAnaHR0cHM6Ly90b2tlbi5hY3Rpb25zLmdpdGh1YnVzZXJjb250ZW50LmNvbScsXG4gICAgICBjbGllbnRJZHM6IFsnc3RzLmFtYXpvbmF3cy5jb20nXSxcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGQgcGVybWlzc2lvbnMgdG8gdGhlIEdpdEh1YiBPSURDIHJvbGUgdGhhdCBhbGxvdyB3b3JrZmxvd3MgdG8gYWNjZXNzIHRoZSBBV1MgcmVzb3VyY2VzIGluIHRoaXMgc3RhY2sgdGhhdCBuZWVkIHRvIGJlIHVwZGF0ZWQgYXQgYnVpbGQgdGltZS5cbiAgICogU2VlOiBodHRwczovL2RvY3MuZ2l0aHViLmNvbS9lbi9hY3Rpb25zL2RlcGxveW1lbnQvc2VjdXJpdHktaGFyZGVuaW5nLXlvdXItZGVwbG95bWVudHMvY29uZmlndXJpbmctb3BlbmlkLWNvbm5lY3QtaW4tYW1hem9uLXdlYi1zZXJ2aWNlc1xuICAgKiBAcGFyYW0gcmVwbyBUaGUgcmVwb3NpdG9yeSB0byBncmFudCBhY2Nlc3MgdG8gKG93bmVyIGFuZCBuYW1lKS4gWW91IGNhbiBhbHNvIHNwZWNpZnkgYSBmaWx0ZXIgdG8gbGltaXQgYWNjZXNzIGUuZy4gdG8gYSBicmFuY2guXG4gICAqL1xuICBnaGFPaWRjUm9sZShyZXBvOiB7IG93bmVyOiBzdHJpbmcsIHJlcG8/OiBzdHJpbmc7IGZpbHRlcj86IHN0cmluZzsgfSwgb3BlbklkQ29ubmVjdFByb3ZpZGVyPzogT3BlbklkQ29ubmVjdFByb3ZpZGVyKTogUm9sZSB7XG4gICAgY29uc3QgcHJvdmlkZXIgPSBvcGVuSWRDb25uZWN0UHJvdmlkZXIgfHwgT3BlbklkQ29ubmVjdFByb3ZpZGVyLmZyb21PcGVuSWRDb25uZWN0UHJvdmlkZXJBcm4odGhpcy5zY29wZSwgYG9pZGMtcHJvdmlkZXItJHt0aGlzLmFjY291bnR9YCwgYGFybjphd3M6aWFtOjoke3RoaXMuYWNjb3VudH06b2lkYy1wcm92aWRlci90b2tlbi5hY3Rpb25zLmdpdGh1YnVzZXJjb250ZW50LmNvbWApO1xuXG4gICAgLy8gR3JhbnQgb25seSByZXF1ZXN0cyBjb21pbmcgZnJvbSB0aGUgc3BlY2lmaWMgb3duZXIvcmVwb3NpdG9yeS9maWx0ZXIgdG8gYXNzdW1lIHRoaXMgcm9sZS5cbiAgICBjb25zdCByb2xlID0gbmV3IFJvbGUodGhpcy5zY29wZSwgYGdoYS1vaWRjLXJvbGUtJHt0aGlzLnN0YWNrTmFtZX1gLCB7XG4gICAgICBhc3N1bWVkQnk6IG5ldyBXZWJJZGVudGl0eVByaW5jaXBhbChcbiAgICAgICAgcHJvdmlkZXIub3BlbklkQ29ubmVjdFByb3ZpZGVyQXJuLFxuICAgICAgICB7XG4gICAgICAgICAgU3RyaW5nTGlrZToge1xuICAgICAgICAgICAgJ3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tOnN1Yic6IFtgcmVwbzoke3JlcG8ub3duZXJ9LyR7cmVwby5yZXBvfToke3JlcG8uZmlsdGVyIHx8ICcqJ31gXSxcbiAgICAgICAgICB9LFxuICAgICAgICB9LFxuICAgICAgKSxcbiAgICAgIG1hbmFnZWRQb2xpY2llczogW1xuICAgICAgICB0aGlzLmdoYVBvbGljeSgpLFxuICAgICAgXSxcbiAgICAgIHJvbGVOYW1lOiBgZ2hhLW9pZGMtJHt0aGlzLnN0YWNrTmFtZX1gLFxuICAgICAgZGVzY3JpcHRpb246IGBSb2xlIGZvciBHaXRIdWIgQWN0aW9ucyB0byBhc3N1bWUgd2hlbiBkZXBsb3lpbmcgdG8gJHt0aGlzLnN0YWNrTmFtZX1gLFxuICAgIH0pO1xuICAgIHRoaXMuYWRkR2hhVmFyaWFibGUoJ2doYU9pZGMnLCAnUm9sZScsIHJvbGUucm9sZUFybik7XG5cbiAgICB0aGlzLnNhdmVHaGFWYWx1ZXMoKTtcbiAgICByZXR1cm4gcm9sZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVwcmVjYXRlZDogdXNlIGdpdGh1YkFjdGlvbnMoKS5naGFPaWRjUm9sZSgpIGluc3RlYWQuXG4gICAqIEEgdXNlciBmb3IgR2lodWQgQWN0aW9ucyBDSS9DRC5cbiAgICovXG4gIGdoYVVzZXIodXNlcm5hbWU/OiBzdHJpbmcpOiB7IHVzZXI6IFVzZXIsIGFjY2Vzc0tleTogQ2ZuQWNjZXNzS2V5IHwgdW5kZWZpbmVkOyB9IHtcbiAgICAvLyBBIHVzZXIgd2l0aCB0aGUgcG9saWN5IGF0dGFjaGVkXG4gICAgY29uc3QgdXNlciA9IG5ldyBVc2VyKHRoaXMuc2NvcGUsICdnaGFVc2VyJywgeyB1c2VyTmFtZTogdXNlcm5hbWUgfHwgYGdoYS0ke3RoaXMuc3RhY2tOYW1lfWAgfSk7XG4gICAgY29uc3QgcG9saWN5ID0gdGhpcy5naGFQb2xpY3koKTtcbiAgICB1c2VyLmFkZE1hbmFnZWRQb2xpY3kocG9saWN5KTtcblxuICAgIC8vIENyZWRlbnRpYWxzXG4gICAgbGV0IGFjY2Vzc0tleTogQ2ZuQWNjZXNzS2V5IHwgdW5kZWZpbmVkO1xuICAgIGlmICghcHJvY2Vzcy5lbnYuUkVLRVkpIHtcbiAgICAgIGFjY2Vzc0tleSA9IG5ldyBDZm5BY2Nlc3NLZXkodGhpcy5zY29wZSwgJ2doYVVzZXJBY2Nlc3NLZXknLCB7XG4gICAgICAgIHVzZXJOYW1lOiB1c2VyLnVzZXJOYW1lLFxuICAgICAgfSk7XG5cbiAgICAgIC8vIEFjY2VzcyBrZXkgZGV0YWlscyBmb3IgR0hBIHNlY3JldHNcbiAgICAgIHRoaXMuYWRkR2hhU2VjcmV0KCdhd3NBY2Nlc3NLZXlJZCcsIGFjY2Vzc0tleS5yZWYpO1xuICAgICAgdGhpcy5hZGRHaGFTZWNyZXQoJ2F3c1NlY3JldEFjY2Vzc0tleScsIGFjY2Vzc0tleS5hdHRyU2VjcmV0QWNjZXNzS2V5KTtcbiAgICB9XG5cbiAgICB0aGlzLnNhdmVHaGFWYWx1ZXMoKTtcbiAgICByZXR1cm4geyB1c2VyLCBhY2Nlc3NLZXkgfTtcbiAgfVxuXG4gIHNhdmVHaGFWYWx1ZXMoKSB7XG4gICAgaWYgKGV4aXN0c1N5bmMoJ2Nkay5vdXQnKSkge1xuICAgICAgLy8gV3JpdGUgb3V0IHRoZSBsaXN0IG9mIHNlY3JldCBhbmQgdmFyaWFibGUgbmFtZXM6XG4gICAgICB3cml0ZUZpbGVTeW5jKGBjZGsub3V0LyR7dGhpcy5zdGFja05hbWV9LmdoYVNlY3JldHMuanNvbmAsIEpTT04uc3RyaW5naWZ5KHRoaXMuZ2hhSW5mby5zZWNyZXRzKSk7XG4gICAgICB3cml0ZUZpbGVTeW5jKGBjZGsub3V0LyR7dGhpcy5zdGFja05hbWV9LmdoYVZhcmlhYmxlcy5qc29uYCwgSlNPTi5zdHJpbmdpZnkodGhpcy5naGFJbmZvLnZhcmlhYmxlcykpO1xuICAgIH1cblxuICAgIC8vIEZsdXNoIGdoYUluZm8gc28gd2UncmUgZnJlZSB0byBidWlsZCBhbm90aGVyIHN0YWNrIGlmIG5lZWRlZDpcbiAgICB0aGlzLmdoYUluZm8ucmVzb3VyY2VzLmJ1Y2tldHMgPSBbXTtcbiAgICB0aGlzLmdoYUluZm8ucmVzb3VyY2VzLmRpc3RyaWJ1dGlvbnMgPSBbXTtcbiAgICB0aGlzLmdoYUluZm8ucmVzb3VyY2VzLmxhbWJkYXMgPSBbXTtcbiAgICB0aGlzLmdoYUluZm8ucmVzb3VyY2VzLnJlcG9zaXRvcmllcyA9IFtdO1xuICAgIHRoaXMuZ2hhSW5mby5yZXNvdXJjZXMuc2VydmljZXMgPSBbXTtcbiAgICB0aGlzLmdoYUluZm8uc2VjcmV0cyA9IFtdO1xuICAgIHRoaXMuZ2hhSW5mby52YXJpYWJsZXMgPSBbXTtcbiAgfVxufVxuXG4vKipcbiAqIFJldHVybnMgYSBzaW5nbGV0b24gaW5zdGFuY2Ugb2YgdGhlIEdpdGh1YkFjdGlvbnMgY29uc3RydWN0IGJ5IGRlZmF1bHQuXG4gKiBGb3IgbW9zdCB1c2UgY2FzZXMsIG9ubHkgb25lIE9JREMgcm9sZSBpcyBuZWVkZWQgaW4gR2l0SHViIEFjdGlvbnMuXG4gKiBJZiB5b3UgbmVlZCBkaWZmZXJlbnQgcm9sZXMgd2l0aCBkaWZmZXJlbnQgcGVybWlzc2lvbnMsIHlvdSBjYW4gY3JlYXRlIG11bHRpcGxlIGluc3RhbmNlcyBvZiB0aGlzIGNvbnN0cnVjdCBieSBwYXNzaW5nIGEgZGlmZmVyZW50IGlkLlxuICogQHBhcmFtIGlkIE9wdGlvbmFsOiBieSBkZWZhdWx0IHRoZSBpZCB3aWxsIGJlICdHaXRodWJBY3Rpb25zJywgd2hpY2ggZ2l2ZXMgeW91IGEgc2luZ2xldG9uIGluc3RhbmNlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2l0aHViQWN0aW9ucyhzY29wZTogQ29uc3RydWN0LCBpZD86IHN0cmluZyk6IEdpdGh1YkFjdGlvbnMge1xuICAvLyBGaW5kIHRoZSBleGlzdGluZyBpbnN0YW5jZSBpbiB0aGUgc3RhY2ssIGlmIHByZXNlbnQ6XG4gIGNvbnN0IHN0YWNrID0gU3RhY2sub2Yoc2NvcGUpO1xuICBjb25zdCBleGlzdGluZyA9IHN0YWNrLm5vZGUudHJ5RmluZENoaWxkKGlkIHx8ICdHaXRodWJBY3Rpb25zJyk7XG4gIHJldHVybiBleGlzdGluZyBhcyBHaXRodWJBY3Rpb25zIHx8IG5ldyBHaXRodWJBY3Rpb25zKHNjb3BlLCBpZCk7XG59XG4iXX0=