UNPKG

@scloud/cdk-patterns

Version:

Serverless CDK patterns for common infrastructure needs

388 lines 59 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.redirectWww = redirectWww; exports.webApp = webApp; exports.webAppRoutes = webAppRoutes; exports.cloudFront = cloudFront; exports.redirect = redirect; const route53 = __importStar(require("aws-cdk-lib/aws-route53")); const cloudfront = __importStar(require("aws-cdk-lib/aws-cloudfront")); const origins = __importStar(require("aws-cdk-lib/aws-cloudfront-origins")); const route53patterns = __importStar(require("aws-cdk-lib/aws-route53-patterns")); const aws_cdk_lib_1 = require("aws-cdk-lib"); const aws_certificatemanager_1 = require("aws-cdk-lib/aws-certificatemanager"); const aws_route53_targets_1 = require("aws-cdk-lib/aws-route53-targets"); const aws_cloudfront_origins_1 = require("aws-cdk-lib/aws-cloudfront-origins"); const aws_cloudfront_1 = require("aws-cdk-lib/aws-cloudfront"); const aws_apigateway_1 = require("aws-cdk-lib/aws-apigateway"); const ZipFunction_1 = require("../ZipFunction"); const ghaUserDeprecated_1 = require("./ghaUserDeprecated"); const bucket_1 = require("./bucket"); // Disabled for now as routing "*.*" to s3 may handle most of what we need to junk: // export const junkPaths: string[] = ['/wp-includes/*', '/wp-admin*', '*.xml', '*.php', '*.aspx', '*.env', '/.git*', '/.remote*', '/.production*', '/.local*']; /** * @deprecated Use RedirectWww instead */ function redirectWww(construct, name, zone, certificate, domain) { const domainName = domain || `${zone.zoneName}`; new route53patterns.HttpsRedirect(construct, `${name}WwwRedirect`, { targetDomain: domainName, recordNames: [`www.${domainName}`], zone, certificate: certificate || new aws_certificatemanager_1.DnsValidatedCertificate(construct, `${name}WwwCertificate`, { domainName: `www.${domainName}`, hostedZone: zone, // this is required for Cloudfront certificates: // https://docs.aws.amazon.com/cdk/api/v1/docs/aws-cloudfront-readme.html region: 'us-east-1', }), }); } /** * @deprecated Use WebApp instead * * Builds a dynamic web application, backed by a Lambda function. * @param stack The CDK stack. The name of the stack will be included in the API Gateway description to aid readability/identification in the AWS console. * @param name The name for the web app. This will infulence naming for Cloudfront, API Gateway, Lambda and the static bucket. * @param zone The DNS zone for this web app. * @param environment Any environment variables your lambda will need to handle requests. * @param domain Optional: by default the zone apex will be mapped to the Cloudfront distribution (e.g. 'example.com') but yo ucan specify a subdomain here (e.g. 'subdomain.example.com'). * @param lambdaProps Optional: if you need to modify the properties of the Lambda function, you can use this parameter. * @param headers Optional: any headers you want passed through Cloudfront in addition to the defaults of User-Agent and Referer * @param defaultIndex Default: true. Maps a viewer request for '/' to a request for /index.html. * @param wwwRedirect Default: true. Redirects www requests to the bare domain name, e.g. www.example.com->example.com, www.sub.example.com->sub.example.com. * @returns */ function webApp(stack, name, zone, environment, domain, lambdaProps, headers, defaultIndex = true, wwwRedirect = true, autoDeleteObjects = true) { const domainName = domain || `${zone.zoneName}`; // Static content const bucket = (0, bucket_1.privateBucket)(stack, `${name}Static`, { autoDeleteObjects }); (0, ghaUserDeprecated_1.addGhaBucket)(stack, name, bucket); // Permissions to access the bucket from Cloudfront const originAccessIdentity = new cloudfront.OriginAccessIdentity(stack, `${name}OAI`, { comment: 'Access to static bucket', }); bucket.grantRead(originAccessIdentity); // Web app handler - default values can be overridden using lambdaProps const lambda = new ZipFunction_1.ZipFunction(stack, name, { functionProps: { environment, memorySize: 3008, timeout: aws_cdk_lib_1.Duration.seconds(10), ...lambdaProps, }, }); const api = new aws_apigateway_1.LambdaRestApi(stack, `${name}ApiGateway`, { handler: lambda, proxy: true, description: `${stack.stackName} ${name}`, binaryMediaTypes: ['multipart/form-data'], }); const staticBehavior = { origin: new aws_cloudfront_origins_1.S3Origin(bucket), allowedMethods: aws_cloudfront_1.AllowedMethods.ALLOW_GET_HEAD_OPTIONS, viewerProtocolPolicy: aws_cloudfront_1.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, compress: true, }; const certificate = new aws_certificatemanager_1.DnsValidatedCertificate(stack, `${name}Certificate`, { domainName, subjectAlternativeNames: [`www.${domainName}`], hostedZone: zone, region: 'us-east-1', }); const distribution = new aws_cloudfront_1.Distribution(stack, `${name}Distribution`, { domainNames: [domainName], comment: domainName, defaultRootObject: defaultIndex ? 'index.html' : undefined, defaultBehavior: { origin: new aws_cloudfront_origins_1.RestApiOrigin(api), // , { // customHeaders: { host: '' }, // }), allowedMethods: aws_cloudfront_1.AllowedMethods.ALLOW_ALL, viewerProtocolPolicy: aws_cloudfront_1.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, compress: true, cachePolicy: aws_cloudfront_1.CachePolicy.CACHING_DISABLED, // https://stackoverflow.com/questions/71367982/cloudfront-gives-403-when-origin-request-policy-include-all-headers-querystri // OriginRequestHeaderBehavior.all() gives an error so just cookie, user-agent, referer originRequestPolicy: new aws_cloudfront_1.OriginRequestPolicy(stack, `${name}OriginRequestPolicy`, { headerBehavior: aws_cloudfront_1.OriginRequestHeaderBehavior.allowList(...['user-agent', 'User-Agent', 'Referer', 'referer'].concat(headers || [])), cookieBehavior: aws_cloudfront_1.OriginRequestCookieBehavior.all(), queryStringBehavior: aws_cloudfront_1.OriginRequestQueryStringBehavior.all(), }), // originRequestPolicy: OriginRequestPolicy.USER_AGENT_REFERER_HEADERS, // edgeLambdas: [{ // functionVersion: headerFilter.currentVersion, // eventType: cloudfront.LambdaEdgeEventType.ORIGIN_REQUEST, // }], }, additionalBehaviors: { '*.*': staticBehavior, // All requests for something with a file extension (actually, any path that contains a period. The aim is to route *.css, *.js, *.jpeg, etc) // index.html direct from s3 for latency on / route? // '/': staticBehaviour' }, certificate, }); (0, ghaUserDeprecated_1.addGhaDistribution)(stack, name, distribution); // Disabled for now as routing "*.*" to s3 may handle most of what we need to junk: // // Handle junk requests by routing to the static bucket // // so they don't invoke Lambda // const junkOptions = { // allowedMethods: AllowedMethods.ALLOW_ALL, // viewerProtocolPolicy: ViewerProtocolPolicy.ALLOW_ALL, // }; // junkPaths.forEach((path) => distribution.addBehavior(path, new S3Origin(bucket), junkOptions)); new route53.ARecord(stack, `${name}ARecord`, { recordName: domainName, target: route53.RecordTarget.fromAlias(new aws_route53_targets_1.CloudFrontTarget(distribution)), zone, }); if (wwwRedirect) redirectWww(stack, name, zone, certificate); return { lambda, api, bucket, distribution, }; } /** * @deprecated Use WebRoutes instead * * Builds a dynamic web application, backed by Lambda functions that serve specific routes. * By default a single Lambda is generated that responds to the / route. * Alternatively you can pass a mapping of routes to functions * (or map to undedfined, which means functions will be generated for you) * @param stack The CDK stack. The name of the stack will be included in the API Gateway description to aid readability/identification in the AWS console. * @param name The name for the web app. This will infulence naming for Cloudfront, API Gateway, Lambda and the static bucket. * @param zone The DNS zone for this web app. * @param routes The set of routes you would like to be handled by Lambda functions. Functions can be undefined (meaning theu will be generated for you). You can optionally request specific headers (deafult: User-Agent and Referer) to be passed through Cloudfront * @param domain Optional: by default the zone apex will be mapped to the Cloudfront distribution (e.g. 'example.com') but yo ucan specify a subdomain here (e.g. 'subdomain.example.com'). * @param defaultIndex Default: true. Maps a viewer request for '/' to a request for /index.html. * @param wwwRedirect Default: true. Redirects www requests to the bare domain name, e.g. www.example.com->example.com, www.sub.example.com->sub.example.com. * @returns */ function webAppRoutes(stack, name, zone, routes = { '/': undefined }, domain = undefined, cognitoPool = undefined, defaultIndex = true, wwwRedirect = true, autoDeleteObjects = true) { const domainName = domain || `${zone.zoneName}`; // We consider the objects in the static bucket to be expendable because // they're static content we generate (rather than user data). const bucket = (0, bucket_1.privateBucket)(stack, `${name}Static`, { autoDeleteObjects }); (0, ghaUserDeprecated_1.addGhaBucket)(stack, name, bucket); // Permissions to access the bucket from Cloudfront const originAccessIdentity = new cloudfront.OriginAccessIdentity(stack, `${name}OAI`, { comment: 'Access to static bucket', }); bucket.grantRead(originAccessIdentity); // Cloudfromt distribution - handle static requests // TODO add a secret so only Cludfront can access APIg const distribution = new aws_cloudfront_1.Distribution(stack, `${name}Distribution`, { domainNames: [domainName], comment: domainName, defaultRootObject: defaultIndex ? 'index.html' : undefined, defaultBehavior: { // Request bin: default is to deflect all requests that aren't known to the API - mostly scripts probing for Wordpress installations origin: new aws_cloudfront_origins_1.S3Origin(bucket, { originAccessIdentity }), allowedMethods: aws_cloudfront_1.AllowedMethods.ALLOW_GET_HEAD_OPTIONS, // Minimal methods - do we need Options? viewerProtocolPolicy: aws_cloudfront_1.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, cachePolicy: aws_cloudfront_1.CachePolicy.CACHING_OPTIMIZED, }, certificate: new aws_certificatemanager_1.DnsValidatedCertificate(stack, `${name}Certificate`, { domainName, hostedZone: zone, region: 'us-east-1', }), }); new route53.ARecord(stack, `${name}ARecord`, { recordName: domainName, target: route53.RecordTarget.fromAlias(new aws_route53_targets_1.CloudFrontTarget(distribution)), zone, }); // Handle API paths const lambdas = {}; // Allowed headers: // https://stackoverflow.com/questions/71367982/cloudfront-gives-403-when-origin-request-policy-include-all-headers-querystri // OriginRequestHeaderBehavior.all() gives an error so just cookie, user-agent, referer // const originRequestPolicy = new OriginRequestPolicy(stack, `${name}OriginRequestPolicy`, { // headerBehavior: OriginRequestHeaderBehavior.allowList(...allowedHeaders, 'user-agent', 'User-Agent', 'Referer', 'referer'), // cookieBehavior: OriginRequestCookieBehavior.all(), // queryStringBehavior: OriginRequestQueryStringBehavior.all(), // }); // At some point we cah probably move to: // OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER // It's in the docs, but not showing up in code: https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_cloudfront.OriginRequestPolicy.html#static-all_viewer_except_host_header // (seems like it's not available yet? This seems like an old issue though: https://github.com/aws/aws-cdk/issues/24552) const originRequestPolicy = cloudfront.OriginRequestPolicy.fromOriginRequestPolicyId(stack, `${name}AllViewerExceptHostHeader`, 'b689b0a8-53d0-40ab-baf2-68738e2966ac'); // const cachePolicy = new CachePolicy(stack, 'cache', { // ...CachePolicy.CACHING_DISABLED, // cookieBehavior: cloudfront.CacheCookieBehavior.all(), // headerBehavior: cloudfront.CacheHeaderBehavior.allowList(...headers, ...allowedHeaders, 'Authorization'), // queryStringBehavior: cloudfront.CacheQueryStringBehavior.all(), // }); const originMap = {}; Object.keys(routes).forEach((pathPattern) => { // Use the provided function, or generate a default one: const lambda = routes[pathPattern] || new ZipFunction_1.ZipFunction(stack, name, { functionProps: { memorySize: 3008 } }); let origin = originMap[lambda.functionName]; if (!origin) { let lambdaRestApiProps = { handler: lambda, proxy: true, description: `${stack.stackName} ${name}-${pathPattern}`, }; if (cognitoPool) { const authorizer = new aws_apigateway_1.CognitoUserPoolsAuthorizer(stack, 'auth', { cognitoUserPools: [cognitoPool], }); lambdaRestApiProps = { ...lambdaRestApiProps, defaultMethodOptions: { authorizationType: aws_apigateway_1.AuthorizationType.COGNITO, authorizer, }, }; } const api = new aws_apigateway_1.LambdaRestApi(stack, `${name}${pathPattern}`, lambdaRestApiProps); origin = new aws_cloudfront_origins_1.RestApiOrigin(api); originMap[lambda.functionName] = origin; } distribution.addBehavior(pathPattern, origin, { allowedMethods: aws_cloudfront_1.AllowedMethods.ALLOW_ALL, viewerProtocolPolicy: aws_cloudfront_1.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, compress: true, cachePolicy: aws_cloudfront_1.CachePolicy.CACHING_DISABLED, // https://stackoverflow.com/questions/71367982/cloudfront-gives-403-when-origin-request-policy-include-all-headers-querystri // OriginRequestHeaderBehavior.all() gives an error so just cookie, user-agent, referer originRequestPolicy, }); lambdas[pathPattern] = lambda; }); (0, ghaUserDeprecated_1.addGhaDistribution)(stack, name, distribution); // Redirect www -> zone root if (wwwRedirect) { new route53patterns.HttpsRedirect(stack, `${name}WwwRedirect`, { recordNames: [`www.${domainName}`], targetDomain: domainName, zone, certificate: new aws_certificatemanager_1.DnsValidatedCertificate(stack, `${name}CertificateWww`, { domainName: `www.${domainName}`, hostedZone: zone, region: 'us-east-1', }), }); } return { lambdas, bucket, distribution }; } /** * @deprecated Use WebFrontend instead * * A Cloudfront distribution backed by an s3 bucket. * NB us-east-1 is required for Cloudfront certificates: * https://docs.aws.amazon.com/cdk/api/v1/docs/aws-cloudfront-readme.html * @param construct Parent CDK construct (typically 'this') * @param zone DNS zone to use for the distribution - default the zone name will be used as the DNS name. * @param name The domain name for the distribution (and name for associated resources) * @param defaultBehavior By default an s3 bucket will be created, but this parameter can override that default behavior (sic.) * @param wwwRedirect whether a www. subdomain should be created to redirect to the main domain * @returns The distribution and (if created) static bucket */ function cloudFront(stack, name, zone, defaultBehavior, domain = undefined, defaultIndex = true, wwwRedirect = true, autoDeleteObjects = true) { const domainName = domain || zone.zoneName; let behavior; let bucket; if (defaultBehavior) { behavior = defaultBehavior; } else { // Default: Cloudfont -> bucket on domain name // We consider the objects in the static bucket to be expendable because // they're static content we generate (rather than user data). bucket = (0, bucket_1.privateBucket)(stack, `${name}Static`, { autoDeleteObjects }); (0, ghaUserDeprecated_1.addGhaBucket)(stack, name, bucket); // Permissions to access the bucket from Cloudfront const originAccessIdentity = new cloudfront.OriginAccessIdentity(stack, `${name}OAI`, { comment: 'Access to static bucket', }); bucket.grantRead(originAccessIdentity); behavior = { origin: new origins.S3Origin(bucket), allowedMethods: aws_cloudfront_1.AllowedMethods.ALLOW_GET_HEAD_OPTIONS, viewerProtocolPolicy: aws_cloudfront_1.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, compress: true, }; } const distribution = new cloudfront.Distribution(stack, `${name}Distribution`, { domainNames: [domainName], comment: domainName, defaultRootObject: defaultIndex ? 'index.html' : undefined, defaultBehavior: behavior, certificate: new aws_certificatemanager_1.DnsValidatedCertificate(stack, `${name}Certificate`, { domainName, hostedZone: zone, region: 'us-east-1', }), }); (0, ghaUserDeprecated_1.addGhaDistribution)(stack, name, distribution); new route53.ARecord(stack, `${name}ARecord`, { zone, recordName: domainName, target: route53.RecordTarget.fromAlias(new aws_route53_targets_1.CloudFrontTarget(distribution)), }); // Redirect www -> zone root if (wwwRedirect) { new route53patterns.HttpsRedirect(stack, `${name}WwwRedirect`, { recordNames: [`www.${domainName}`], targetDomain: domainName, zone, certificate: new aws_certificatemanager_1.DnsValidatedCertificate(stack, `${name}CertificateWww`, { domainName: `www.${domainName}`, hostedZone: zone, region: 'us-east-1', }), }); } return { bucket, distribution, // lambda: edgeFunction?.lambda, }; } function redirect(construct, name, zone, targetDomain) { new route53patterns.HttpsRedirect(construct, `${name}Redirect`, { targetDomain, recordNames: [zone.zoneName, `www.${zone.zoneName}`], zone, certificate: new aws_certificatemanager_1.DnsValidatedCertificate(construct, `${name}Certificate`, { domainName: zone.zoneName, subjectAlternativeNames: [`www.${zone.zoneName}`], hostedZone: zone, // this is required for Cloudfront certificates: // https://docs.aws.amazon.com/cdk/api/v1/docs/aws-cloudfront-readme.html region: 'us-east-1', }), }); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xvdWRmcm9udC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kZXByZWNhdGVkL2Nsb3VkZnJvbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFrQ0Esa0NBcUJDO0FBaUJELHdCQTBHQztBQWtCRCxvQ0F1SUM7QUFlRCxnQ0ErRUM7QUFFRCw0QkFtQkM7QUE5YkQsaUVBQW1EO0FBRW5ELHVFQUF5RDtBQUN6RCw0RUFBOEQ7QUFDOUQsa0ZBQW9FO0FBQ3BFLDZDQUVxQjtBQUNyQiwrRUFBMkY7QUFDM0YseUVBQW1FO0FBRW5FLCtFQUE2RTtBQUM3RSwrREFPb0M7QUFDcEMsK0RBRW9DO0FBR3BDLGdEQUE2QztBQUM3QywyREFBdUU7QUFDdkUscUNBQXlDO0FBRXpDLG1GQUFtRjtBQUNuRixnS0FBZ0s7QUFDaEs7O0dBRUc7QUFDSCxTQUFnQixXQUFXLENBQ3pCLFNBQW9CLEVBQ3BCLElBQVksRUFDWixJQUF5QixFQUN6QixXQUEwQixFQUMxQixNQUFlO0lBRWYsTUFBTSxVQUFVLEdBQUcsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBRWhELElBQUksZUFBZSxDQUFDLGFBQWEsQ0FBQyxTQUFTLEVBQUUsR0FBRyxJQUFJLGFBQWEsRUFBRTtRQUNqRSxZQUFZLEVBQUUsVUFBVTtRQUN4QixXQUFXLEVBQUUsQ0FBQyxPQUFPLFVBQVUsRUFBRSxDQUFDO1FBQ2xDLElBQUk7UUFDSixXQUFXLEVBQUUsV0FBVyxJQUFJLElBQUksZ0RBQXVCLENBQUMsU0FBUyxFQUFFLEdBQUcsSUFBSSxnQkFBZ0IsRUFBRTtZQUMxRixVQUFVLEVBQUUsT0FBTyxVQUFVLEVBQUU7WUFDL0IsVUFBVSxFQUFFLElBQUk7WUFDaEIsZ0RBQWdEO1lBQ2hELHlFQUF5RTtZQUN6RSxNQUFNLEVBQUUsV0FBVztTQUNwQixDQUFDO0tBQ0gsQ0FBQyxDQUFDO0FBQ0ws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UUFDWixXQUFXLEVBQUUsQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLE9BQU8sSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQ3BELElBQUk7UUFDSixXQUFXLEVBQUUsSUFBSSxnREFBdUIsQ0FBQyxTQUFTLEVBQUUsR0FBRyxJQUFJLGFBQWEsRUFBRTtZQUN4RSxVQUFVLEVBQUUsSUFBSSxDQUFDLFFBQVE7WUFDekIsdUJBQXVCLEVBQUUsQ0FBQyxPQUFPLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUNqRCxVQUFVLEVBQUUsSUFBSTtZQUNoQixnREFBZ0Q7WUFDaEQseUVBQXlFO1lBQ3pFLE1BQU0sRUFBRSxXQUFXO1NBQ3BCLENBQUM7S0FDSCxDQUFDLENBQUM7QUFDTCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgcm91dGU1MyBmcm9tICdhd3MtY2RrLWxpYi9hd3Mtcm91dGU1Myc7XG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdjb25zdHJ1Y3RzJztcbmltcG9ydCAqIGFzIGNsb3VkZnJvbnQgZnJvbSAnYXdzLWNkay1saWIvYXdzLWNsb3VkZnJvbnQnO1xuaW1wb3J0ICogYXMgb3JpZ2lucyBmcm9tICdhd3MtY2RrLWxpYi9hd3MtY2xvdWRmcm9udC1vcmlnaW5zJztcbmltcG9ydCAqIGFzIHJvdXRlNTNwYXR0ZXJucyBmcm9tICdhd3MtY2RrLWxpYi9hd3Mtcm91dGU1My1wYXR0ZXJucyc7XG5pbXBvcnQge1xuICBEdXJhdGlvbiwgU3RhY2ssXG59IGZyb20gJ2F3cy1jZGstbGliJztcbmltcG9ydCB7IERuc1ZhbGlkYXRlZENlcnRpZmljYXRlLCBJQ2VydGlmaWNhdGUgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtY2VydGlmaWNhdGVtYW5hZ2VyJztcbmltcG9ydCB7IENsb3VkRnJvbnRUYXJnZXQgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3Mtcm91dGU1My10YXJnZXRzJztcbmltcG9ydCB7IEJ1Y2tldCB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1zMyc7XG5pbXBvcnQgeyBSZXN0QXBpT3JpZ2luLCBTM09yaWdpbiB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1jbG91ZGZyb250LW9yaWdpbnMnO1xuaW1wb3J0IHtcbiAgQWxsb3dlZE1ldGhvZHMsIEJlaGF2aW9yT3B0aW9ucywgQ2FjaGVQb2xpY3ksIERpc3RyaWJ1dGlvbixcbiAgT3JpZ2luUmVxdWVzdENvb2tpZUJlaGF2aW9yLFxuICBPcmlnaW5SZXF1ZXN0SGVhZGVyQmVoYXZpb3IsXG4gIE9yaWdpblJlcXVlc3RQb2xpY3ksXG4gIE9yaWdpblJlcXVlc3RRdWVyeVN0cmluZ0JlaGF2aW9yLFxuICBWaWV3ZXJQcm90b2NvbFBvbGljeSxcbn0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWNsb3VkZnJvbnQnO1xuaW1wb3J0IHtcbiAgQXV0aG9yaXphdGlvblR5cGUsIENvZ25pdG9Vc2VyUG9vbHNBdXRob3JpemVyLCBMYW1iZGFSZXN0QXBpLCBMYW1iZGFSZXN0QXBpUHJvcHMsXG59IGZyb20gJ2F3cy1jZGstbGliL2F3cy1hcGlnYXRld2F5JztcbmltcG9ydCB7IEZ1bmN0aW9uLCBGdW5jdGlvblByb3BzIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWxhbWJkYSc7XG5pbXBvcnQgeyBVc2VyUG9vbCB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1jb2duaXRvJztcbmltcG9ydCB7IFppcEZ1bmN0aW9uIH0gZnJvbSAnLi4vWmlwRnVuY3Rpb24nO1xuaW1wb3J0IHsgYWRkR2hhQnVja2V0LCBhZGRHaGFEaXN0cmlidXRpb24gfSBmcm9tICcuL2doYVVzZXJEZXByZWNhdGVkJztcbmltcG9ydCB7IHByaXZhdGVCdWNrZXQgfSBmcm9tICcuL2J1Y2tldCc7XG5cbi8vIERpc2FibGVkIGZvciBub3cgYXMgcm91dGluZyBcIiouKlwiIHRvIHMzIG1heSBoYW5kbGUgbW9zdCBvZiB3aGF0IHdlIG5lZWQgdG8ganVuazpcbi8vIGV4cG9ydCBjb25zdCBqdW5rUGF0aHM6IHN0cmluZ1tdID0gWycvd3AtaW5jbHVkZXMvKicsICcvd3AtYWRtaW4qJywgJyoueG1sJywgJyoucGhwJywgJyouYXNweCcsICcqLmVudicsICcvLmdpdConLCAnLy5yZW1vdGUqJywgJy8ucHJvZHVjdGlvbionLCAnLy5sb2NhbConXTtcbi8qKlxuICogQGRlcHJlY2F0ZWQgVXNlIFJlZGlyZWN0V3d3IGluc3RlYWRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlZGlyZWN0V3d3KFxuICBjb25zdHJ1Y3Q6IENvbnN0cnVjdCxcbiAgbmFtZTogc3RyaW5nLFxuICB6b25lOiByb3V0ZTUzLklIb3N0ZWRab25lLFxuICBjZXJ0aWZpY2F0ZT86IElDZXJ0aWZpY2F0ZSxcbiAgZG9tYWluPzogc3RyaW5nLFxuKSB7XG4gIGNvbnN0IGRvbWFpbk5hbWUgPSBkb21haW4gfHwgYCR7em9uZS56b25lTmFtZX1gO1xuXG4gIG5ldyByb3V0ZTUzcGF0dGVybnMuSHR0cHNSZWRpcmVjdChjb25zdHJ1Y3QsIGAke25hbWV9V3d3UmVkaXJlY3RgLCB7XG4gICAgdGFyZ2V0RG9tYWluOiBkb21haW5OYW1lLFxuICAgIHJlY29yZE5hbWVzOiBbYHd3dy4ke2RvbWFpbk5hbWV9YF0sXG4gICAgem9uZSxcbiAgICBjZXJ0aWZpY2F0ZTogY2VydGlmaWNhdGUgfHwgbmV3IERuc1ZhbGlkYXRlZENlcnRpZmljYXRlKGNvbnN0cnVjdCwgYCR7bmFtZX1Xd3dDZXJ0aWZpY2F0ZWAsIHtcbiAgICAgIGRvbWFpbk5hbWU6IGB3d3cuJHtkb21haW5OYW1lfWAsXG4gICAgICBob3N0ZWRab25lOiB6b25lLFxuICAgICAgLy8gdGhpcyBpcyByZXF1aXJlZCBmb3IgQ2xvdWRmcm9udCBjZXJ0aWZpY2F0ZXM6XG4gICAgICAvLyBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vY2RrL2FwaS92MS9kb2NzL2F3cy1jbG91ZGZyb250LXJlYWRtZS5odG1sXG4gICAgICByZWdpb246ICd1cy1lYXN0LTEnLFxuICAgIH0pLFxuICB9KTtcbn1cblxuLyoqXG4gKiBAZGVwcmVjYXRlZCBVc2UgV2ViQXBwIGluc3RlYWRcbiAqXG4gKiBCdWlsZHMgYSBkeW5hbWljIHdlYiBhcHBsaWNhdGlvbiwgYmFja2VkIGJ5IGEgTGFtYmRhIGZ1bmN0aW9uLlxuICogQHBhcmFtIHN0YWNrIFRoZSBDREsgc3RhY2suIFRoZSBuYW1lIG9mIHRoZSBzdGFjayB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBBUEkgR2F0ZXdheSBkZXNjcmlwdGlvbiB0byBhaWQgcmVhZGFiaWxpdHkvaWRlbnRpZmljYXRpb24gaW4gdGhlIEFXUyBjb25zb2xlLlxuICogQHBhcmFtIG5hbWUgVGhlIG5hbWUgZm9yIHRoZSB3ZWIgYXBwLiBUaGlzIHdpbGwgaW5mdWxlbmNlIG5hbWluZyBmb3IgQ2xvdWRmcm9udCwgQVBJIEdhdGV3YXksIExhbWJkYSBhbmQgdGhlIHN0YXRpYyBidWNrZXQuXG4gKiBAcGFyYW0gem9uZSBUaGUgRE5TIHpvbmUgZm9yIHRoaXMgd2ViIGFwcC5cbiAqIEBwYXJhbSBlbnZpcm9ubWVudCBBbnkgZW52aXJvbm1lbnQgdmFyaWFibGVzIHlvdXIgbGFtYmRhIHdpbGwgbmVlZCB0byBoYW5kbGUgcmVxdWVzdHMuXG4gKiBAcGFyYW0gZG9tYWluIE9wdGlvbmFsOiBieSBkZWZhdWx0IHRoZSB6b25lIGFwZXggd2lsbCBiZSBtYXBwZWQgdG8gdGhlIENsb3VkZnJvbnQgZGlzdHJpYnV0aW9uIChlLmcuICdleGFtcGxlLmNvbScpIGJ1dCB5byB1Y2FuIHNwZWNpZnkgYSBzdWJkb21haW4gaGVyZSAoZS5nLiAnc3ViZG9tYWluLmV4YW1wbGUuY29tJykuXG4gKiBAcGFyYW0gbGFtYmRhUHJvcHMgT3B0aW9uYWw6IGlmIHlvdSBuZWVkIHRvIG1vZGlmeSB0aGUgcHJvcGVydGllcyBvZiB0aGUgTGFtYmRhIGZ1bmN0aW9uLCB5b3UgY2FuIHVzZSB0aGlzIHBhcmFtZXRlci5cbiAqIEBwYXJhbSBoZWFkZXJzIE9wdGlvbmFsOiBhbnkgaGVhZGVycyB5b3Ugd2FudCBwYXNzZWQgdGhyb3VnaCBDbG91ZGZyb250IGluIGFkZGl0aW9uIHRvIHRoZSBkZWZhdWx0cyBvZiBVc2VyLUFnZW50IGFuZCBSZWZlcmVyXG4gKiBAcGFyYW0gZGVmYXVsdEluZGV4IERlZmF1bHQ6IHRydWUuIE1hcHMgYSB2aWV3ZXIgcmVxdWVzdCBmb3IgJy8nIHRvIGEgcmVxdWVzdCBmb3IgL2luZGV4Lmh0bWwuXG4gKiBAcGFyYW0gd3d3UmVkaXJlY3QgRGVmYXVsdDogdHJ1ZS4gUmVkaXJlY3RzIHd3dyByZXF1ZXN0cyB0byB0aGUgYmFyZSBkb21haW4gbmFtZSwgZS5nLiB3d3cuZXhhbXBsZS5jb20tPmV4YW1wbGUuY29tLCB3d3cuc3ViLmV4YW1wbGUuY29tLT5zdWIuZXhhbXBsZS5jb20uXG4gKiBAcmV0dXJuc1xuICovXG5leHBvcnQgZnVuY3Rpb24gd2ViQXBwKFxuICBzdGFjazogU3RhY2ssXG4gIG5hbWU6IHN0cmluZyxcbiAgem9uZTogcm91dGU1My5JSG9zdGVkWm9uZSxcbiAgZW52aXJvbm1lbnQ/OiB7IFtrZXk6IHN0cmluZ106IHN0cmluZzsgfSxcbiAgZG9tYWluPzogc3RyaW5nLFxuICBsYW1iZGFQcm9wcz86IFBhcnRpYWw8RnVuY3Rpb25Qcm9wcz4sXG4gIGhlYWRlcnM/OiBzdHJpbmdbXSxcbiAgZGVmYXVsdEluZGV4OiBib29sZWFuID0gdHJ1ZSxcbiAgd3d3UmVkaXJlY3Q6IGJvb2xlYW4gPSB0cnVlLFxuICBhdXRvRGVsZXRlT2JqZWN0czogYm9vbGVhbiA9IHRydWUsXG4pOiB7IGxhbWJkYTogRnVuY3Rpb24sIGFwaTogTGFtYmRhUmVzdEFwaSwgYnVja2V0OiBCdWNrZXQsIGRpc3RyaWJ1dGlvbjogRGlzdHJpYnV0aW9uOyB9IHtcbiAgY29uc3QgZG9tYWluTmFtZSA9IGRvbWFpbiB8fCBgJHt6b25lLnpvbmVOYW1lfWA7XG5cbiAgLy8gU3RhdGljIGNvbnRlbnRcbiAgY29uc3QgYnVja2V0ID0gcHJpdmF0ZUJ1Y2tldChzdGFjaywgYCR7bmFtZX1TdGF0aWNgLCB7IGF1dG9EZWxldGVPYmplY3RzIH0pO1xuICBhZGRHaGFCdWNrZXQoc3RhY2ssIG5hbWUsIGJ1Y2tldCk7XG5cbiAgLy8gUGVybWlzc2lvbnMgdG8gYWNjZXNzIHRoZSBidWNrZXQgZnJvbSBDbG91ZGZyb250XG4gIGNvbnN0IG9yaWdpbkFjY2Vzc0lkZW50aXR5ID0gbmV3IGNsb3VkZnJvbnQuT3JpZ2luQWNjZXNzSWRlbnRpdHkoc3RhY2ssIGAke25hbWV9T0FJYCwge1xuICAgIGNvbW1lbnQ6ICdBY2Nlc3MgdG8gc3RhdGljIGJ1Y2tldCcsXG4gIH0pO1xuICBidWNrZXQuZ3JhbnRSZWFkKG9yaWdpbkFjY2Vzc0lkZW50aXR5KTtcblxuICAvLyBXZWIgYXBwIGhhbmRsZXIgLSBkZWZhdWx0IHZhbHVlcyBjYW4gYmUgb3ZlcnJpZGRlbiB1c2luZyBsYW1iZGFQcm9wc1xuICBjb25zdCBsYW1iZGEgPSBuZXcgWmlwRnVuY3Rpb24oc3RhY2ssIG5hbWUsIHtcbiAgICBmdW5jdGlvblByb3BzOiB7XG4gICAgICBlbnZpcm9ubWVudCwgbWVtb3J5U2l6ZTogMzAwOCwgdGltZW91dDogRHVyYXRpb24uc2Vjb25kcygxMCksIC4uLmxhbWJkYVByb3BzLFxuICAgIH0sXG4gIH0pO1xuXG4gIGNvbnN0IGFwaSA9IG5ldyBMYW1iZGFSZXN0QXBpKHN0YWNrLCBgJHtuYW1lfUFwaUdhdGV3YXlgLCB7XG4gICAgaGFuZGxlcjogbGFtYmRhLFxuICAgIHByb3h5OiB0cnVlLFxuICAgIGRlc2NyaXB0aW9uOiBgJHtzdGFjay5zdGFja05hbWV9ICR7bmFtZX1gLFxuICAgIGJpbmFyeU1lZGlhVHlwZXM6IFsnbXVsdGlwYXJ0L2Zvcm0tZGF0YSddLFxuICB9KTtcblxuICBjb25zdCBzdGF0aWNCZWhhdmlvciA9IHtcbiAgICBvcmlnaW46IG5ldyBTM09yaWdpbihidWNrZXQpLFxuICAgIGFsbG93ZWRNZXRob2RzOiBBbGxvd2VkTWV0aG9kcy5BTExPV19HRVRfSEVBRF9PUFRJT05TLFxuICAgIHZpZXdlclByb3RvY29sUG9saWN5OiBWaWV3ZXJQcm90b2NvbFBvbGljeS5SRURJUkVDVF9UT19IVFRQUyxcbiAgICBjb21wcmVzczogdHJ1ZSxcbiAgfTtcblxuICBjb25zdCBjZXJ0aWZpY2F0ZSA9IG5ldyBEbnNWYWxpZGF0ZWRDZXJ0aWZpY2F0ZShzdGFjaywgYCR7bmFtZX1DZXJ0aWZpY2F0ZWAsIHtcbiAgICBkb21haW5OYW1lLFxuICAgIHN1YmplY3RBbHRlcm5hdGl2ZU5hbWVzOiBbYHd3dy4ke2RvbWFpbk5hbWV9YF0sXG4gICAgaG9zdGVkWm9uZTogem9uZSxcbiAgICByZWdpb246ICd1cy1lYXN0LTEnLFxuICB9KTtcblxuICBjb25zdCBkaXN0cmlidXRpb24gPSBuZXcgRGlzdHJpYnV0aW9uKHN0YWNrLCBgJHtuYW1lfURpc3RyaWJ1dGlvbmAsIHtcbiAgICBkb21haW5OYW1lczogW2RvbWFpbk5hbWVdLFxuICAgIGNvbW1lbnQ6IGRvbWFpbk5hbWUsXG4gICAgZGVmYXVsdFJvb3RPYmplY3Q6IGRlZmF1bHRJbmRleCA/ICdpbmRleC5odG1sJyA6IHVuZGVmaW5lZCxcbiAgICBkZWZhdWx0QmVoYXZpb3I6IHtcbiAgICAgIG9yaWdpbjogbmV3IFJlc3RBcGlPcmlnaW4oYXBpKSxcbiAgICAgIC8vICAgLCB7XG4gICAgICAvLyAgIGN1c3RvbUhlYWRlcnM6IHsgaG9zdDogJycgfSxcbiAgICAgIC8vIH0pLFxuICAgICAgYWxsb3dlZE1ldGhvZHM6IEFsbG93ZWRNZXRob2RzLkFMTE9XX0FMTCxcbiAgICAgIHZpZXdlclByb3RvY29sUG9saWN5OiBWaWV3ZXJQcm90b2NvbFBvbGljeS5SRURJUkVDVF9UT19IVFRQUyxcbiAgICAgIGNvbXByZXNzOiB0cnVlLFxuICAgICAgY2FjaGVQb2xpY3k6IENhY2hlUG9saWN5LkNBQ0hJTkdfRElTQUJMRUQsXG4gICAgICAvLyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy83MTM2Nzk4Mi9jbG91ZGZyb250LWdpdmVzLTQwMy13aGVuLW9yaWdpbi1yZXF1ZXN0LXBvbGljeS1pbmNsdWRlLWFsbC1oZWFkZXJzLXF1ZXJ5c3RyaVxuICAgICAgLy8gT3JpZ2luUmVxdWVzdEhlYWRlckJlaGF2aW9yLmFsbCgpIGdpdmVzIGFuIGVycm9yIHNvIGp1c3QgY29va2llLCB1c2VyLWFnZW50LCByZWZlcmVyXG4gICAgICBvcmlnaW5SZXF1ZXN0UG9saWN5OiBuZXcgT3JpZ2luUmVxdWVzdFBvbGljeShzdGFjaywgYCR7bmFtZX1PcmlnaW5SZXF1ZXN0UG9saWN5YCwge1xuICAgICAgICBoZWFkZXJCZWhhdmlvcjogT3JpZ2luUmVxdWVzdEhlYWRlckJlaGF2aW9yLmFsbG93TGlzdCguLi5bJ3VzZXItYWdlbnQnLCAnVXNlci1BZ2VudCcsICdSZWZlcmVyJywgJ3JlZmVyZXInXS5jb25jYXQoaGVhZGVycyB8fCBbXSkpLFxuICAgICAgICBjb29raWVCZWhhdmlvcjogT3JpZ2luUmVxdWVzdENvb2tpZUJlaGF2aW9yLmFsbCgpLFxuICAgICAgICBxdWVyeVN0cmluZ0JlaGF2aW9yOiBPcmlnaW5SZXF1ZXN0UXVlcnlTdHJpbmdCZWhhdmlvci5hbGwoKSxcbiAgICAgIH0pLFxuICAgICAgLy8gb3JpZ2luUmVxdWVzdFBvbGljeTogT3JpZ2luUmVxdWVzdFBvbGljeS5VU0VSX0FHRU5UX1JFRkVSRVJfSEVBREVSUyxcbiAgICAgIC8vIGVkZ2VMYW1iZGFzOiBbe1xuICAgICAgLy8gICBmdW5jdGlvblZlcnNpb246IGhlYWRlckZpbHRlci5jdXJyZW50VmVyc2lvbixcbiAgICAgIC8vICAgZXZlbnRUeXBlOiBjbG91ZGZyb250LkxhbWJkYUVkZ2VFdmVudFR5cGUuT1JJR0lOX1JFUVVFU1QsXG4gICAgICAvLyB9XSxcbiAgICB9LFxuICAgIGFkZGl0aW9uYWxCZWhhdmlvcnM6IHtcbiAgICAgICcqLionOiBzdGF0aWNCZWhhdmlvciwgLy8gQWxsIHJlcXVlc3RzIGZvciBzb21ldGhpbmcgd2l0aCBhIGZpbGUgZXh0ZW5zaW9uIChhY3R1YWxseSwgYW55IHBhdGggdGhhdCBjb250YWlucyBhIHBlcmlvZC4gVGhlIGFpbSBpcyB0byByb3V0ZSAqLmNzcywgKi5qcywgKi5qcGVnLCBldGMpXG4gICAgICAvLyBpbmRleC5odG1sIGRpcmVjdCBmcm9tIHMzIGZvciBsYXRlbmN5IG9uIC8gcm91dGU/IC8vICcvJzogc3RhdGljQmVoYXZpb3VyJ1xuICAgIH0sXG4gICAgY2VydGlmaWNhdGUsXG4gIH0pO1xuICBhZGRHaGFEaXN0cmlidXRpb24oc3RhY2ssIG5hbWUsIGRpc3RyaWJ1dGlvbik7XG5cbiAgLy8gRGlzYWJsZWQgZm9yIG5vdyBhcyByb3V0aW5nIFwiKi4qXCIgdG8gczMgbWF5IGhhbmRsZSBtb3N0IG9mIHdoYXQgd2UgbmVlZCB0byBqdW5rOlxuICAvLyAvLyBIYW5kbGUganVuayByZXF1ZXN0cyBieSByb3V0aW5nIHRvIHRoZSBzdGF0aWMgYnVja2V0XG4gIC8vIC8vIHNvIHRoZXkgZG9uJ3QgaW52b2tlIExhbWJkYVxuICAvLyBjb25zdCBqdW5rT3B0aW9ucyA9IHtcbiAgLy8gICBhbGxvd2VkTWV0aG9kczogQWxsb3dlZE1ldGhvZHMuQUxMT1dfQUxMLFxuICAvLyAgIHZpZXdlclByb3RvY29sUG9saWN5OiBWaWV3ZXJQcm90b2NvbFBvbGljeS5BTExPV19BTEwsXG4gIC8vIH07XG4gIC8vIGp1bmtQYXRocy5mb3JFYWNoKChwYXRoKSA9PiBkaXN0cmlidXRpb24uYWRkQmVoYXZpb3IocGF0aCwgbmV3IFMzT3JpZ2luKGJ1Y2tldCksIGp1bmtPcHRpb25zKSk7XG5cbiAgbmV3IHJvdXRlNTMuQVJlY29yZChzdGFjaywgYCR7bmFtZX1BUmVjb3JkYCwge1xuICAgIHJlY29yZE5hbWU6IGRvbWFpbk5hbWUsXG4gICAgdGFyZ2V0OiByb3V0ZTUzLlJlY29yZFRhcmdldC5mcm9tQWxpYXMobmV3IENsb3VkRnJvbnRUYXJnZXQoZGlzdHJpYnV0aW9uKSksXG4gICAgem9uZSxcbiAgfSk7XG5cbiAgaWYgKHd3d1JlZGlyZWN0KSByZWRpcmVjdFd3dyhzdGFjaywgbmFtZSwgem9uZSwgY2VydGlmaWNhdGUpO1xuXG4gIHJldHVybiB7XG4gICAgbGFtYmRhLCBhcGksIGJ1Y2tldCwgZGlzdHJpYnV0aW9uLFxuICB9O1xufVxuXG4vKipcbiAqIEBkZXByZWNhdGVkIFVzZSBXZWJSb3V0ZXMgaW5zdGVhZFxuICpcbiAqIEJ1aWxkcyBhIGR5bmFtaWMgd2ViIGFwcGxpY2F0aW9uLCBiYWNrZWQgYnkgTGFtYmRhIGZ1bmN0aW9ucyB0aGF0IHNlcnZlIHNwZWNpZmljIHJvdXRlcy5cbiAqIEJ5IGRlZmF1bHQgYSBzaW5nbGUgTGFtYmRhIGlzIGdlbmVyYXRlZCB0aGF0IHJlc3BvbmRzIHRvIHRoZSAvIHJvdXRlLlxuICogQWx0ZXJuYXRpdmVseSB5b3UgY2FuIHBhc3MgYSBtYXBwaW5nIG9mIHJvdXRlcyB0byBmdW5jdGlvbnNcbiAqIChvciBtYXAgdG8gdW5kZWRmaW5lZCwgd2hpY2ggbWVhbnMgZnVuY3Rpb25zIHdpbGwgYmUgZ2VuZXJhdGVkIGZvciB5b3UpXG4gKiBAcGFyYW0gc3RhY2sgVGhlIENESyBzdGFjay4gVGhlIG5hbWUgb2YgdGhlIHN0YWNrIHdpbGwgYmUgaW5jbHVkZWQgaW4gdGhlIEFQSSBHYXRld2F5IGRlc2NyaXB0aW9uIHRvIGFpZCByZWFkYWJpbGl0eS9pZGVudGlmaWNhdGlvbiBpbiB0aGUgQVdTIGNvbnNvbGUuXG4gKiBAcGFyYW0gbmFtZSBUaGUgbmFtZSBmb3IgdGhlIHdlYiBhcHAuIFRoaXMgd2lsbCBpbmZ1bGVuY2UgbmFtaW5nIGZvciBDbG91ZGZyb250LCBBUEkgR2F0ZXdheSwgTGFtYmRhIGFuZCB0aGUgc3RhdGljIGJ1Y2tldC5cbiAqIEBwYXJhbSB6b25lIFRoZSBETlMgem9uZSBmb3IgdGhpcyB3ZWIgYXBwLlxuICogQHBhcmFtIHJvdXRlcyBUaGUgc2V0IG9mIHJvdXRlcyB5b3Ugd291bGQgbGlrZSB0byBiZSBoYW5kbGVkIGJ5IExhbWJkYSBmdW5jdGlvbnMuIEZ1bmN0aW9ucyBjYW4gYmUgdW5kZWZpbmVkIChtZWFuaW5nIHRoZXUgd2lsbCBiZSBnZW5lcmF0ZWQgZm9yIHlvdSkuIFlvdSBjYW4gb3B0aW9uYWxseSByZXF1ZXN0IHNwZWNpZmljIGhlYWRlcnMgKGRlYWZ1bHQ6IFVzZXItQWdlbnQgYW5kIFJlZmVyZXIpIHRvIGJlIHBhc3NlZCB0aHJvdWdoIENsb3VkZnJvbnRcbiAqIEBwYXJhbSBkb21haW4gT3B0aW9uYWw6IGJ5IGRlZmF1bHQgdGhlIHpvbmUgYXBleCB3aWxsIGJlIG1hcHBlZCB0byB0aGUgQ2xvdWRmcm9udCBkaXN0cmlidXRpb24gKGUuZy4gJ2V4YW1wbGUuY29tJykgYnV0IHlvIHVjYW4gc3BlY2lmeSBhIHN1YmRvbWFpbiBoZXJlIChlLmcuICdzdWJkb21haW4uZXhhbXBsZS5jb20nKS5cbiAqIEBwYXJhbSBkZWZhdWx0SW5kZXggRGVmYXVsdDogdHJ1ZS4gTWFwcyBhIHZpZXdlciByZXF1ZXN0IGZvciAnLycgdG8gYSByZXF1ZXN0IGZvciAvaW5kZXguaHRtbC5cbiAqIEBwYXJhbSB3d3dSZWRpcmVjdCBEZWZhdWx0OiB0cnVlLiBSZWRpcmVjdHMgd3d3IHJlcXVlc3RzIHRvIHRoZSBiYXJlIGRvbWFpbiBuYW1lLCBlLmcuIHd3dy5leGFtcGxlLmNvbS0+ZXhhbXBsZS5jb20sIHd3dy5zdWIuZXhhbXBsZS5jb20tPnN1Yi5leGFtcGxlLmNvbS5cbiAqIEByZXR1cm5zXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB3ZWJBcHBSb3V0ZXMoXG4gIHN0YWNrOiBTdGFjayxcbiAgbmFtZTogc3RyaW5nLFxuICB6b25lOiByb3V0ZTUzLklIb3N0ZWRab25lLFxuICByb3V0ZXM6IHsgW3BhdGhQYXR0ZXJuOiBzdHJpbmddOiBGdW5jdGlvbiB8IHVuZGVmaW5lZDsgfSA9IHsgJy8nOiB1bmRlZmluZWQgfSxcbiAgZG9tYWluOiBzdHJpbmcgfCB1bmRlZmluZWQgPSB1bmRlZmluZWQsXG4gIGNvZ25pdG9Qb29sOiBVc2VyUG9vbCB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZCxcbiAgZGVmYXVsdEluZGV4OiBib29sZWFuID0gdHJ1ZSxcbiAgd3d3UmVkaXJlY3Q6IGJvb2xlYW4gPSB0cnVlLFxuICBhdXRvRGVsZXRlT2JqZWN0czogYm9vbGVhbiA9IHRydWUsXG4pOiB7IGxhbWJkYXM6IHsgW3BhdGg6IHN0cmluZ106IEZ1bmN0aW9uOyB9LCBidWNrZXQ6IEJ1Y2tldCwgZGlzdHJpYnV0aW9uOiBEaXN0cmlidXRpb247IH0ge1xuICBjb25zdCBkb21haW5OYW1lID0gZG9tYWluIHx8IGAke3pvbmUuem9uZU5hbWV9YDtcblxuICAvLyBXZSBjb25zaWRlciB0aGUgb2JqZWN0cyBpbiB0aGUgc3RhdGljIGJ1Y2tldCB0byBiZSBleHBlbmRhYmxlIGJlY2F1c2VcbiAgLy8gdGhleSdyZSBzdGF0aWMgY29udGVudCB3ZSBnZW5lcmF0ZSAocmF0aGVyIHRoYW4gdXNlciBkYXRhKS5cbiAgY29uc3QgYnVja2V0ID0gcHJpdmF0ZUJ1Y2tldChzdGFjaywgYCR7bmFtZX1TdGF0aWNgLCB7IGF1dG9EZWxldGVPYmplY3RzIH0pO1xuICBhZGRHaGFCdWNrZXQoc3RhY2ssIG5hbWUsIGJ1Y2tldCk7XG5cbiAgLy8gUGVybWlzc2lvbnMgdG8gYWNjZXNzIHRoZSBidWNrZXQgZnJvbSBDbG91ZGZyb250XG4gIGNvbnN0IG9yaWdpbkFjY2Vzc0lkZW50aXR5ID0gbmV3IGNsb3VkZnJvbnQuT3JpZ2luQWNjZXNzSWRlbnRpdHkoc3RhY2ssIGAke25hbWV9T0FJYCwge1xuICAgIGNvbW1lbnQ6ICdBY2Nlc3MgdG8gc3RhdGljIGJ1Y2tldCcsXG4gIH0pO1xuICBidWNrZXQuZ3JhbnRSZWFkKG9yaWdpbkFjY2Vzc0lkZW50aXR5KTtcblxuICAvLyBDbG91ZGZyb210IGRpc3RyaWJ1dGlvbiAtIGhhbmRsZSBzdGF0aWMgcmVxdWVzdHNcbiAgLy8gVE9ETyBhZGQgYSBzZWNyZXQgc28gb25seSBDbHVkZnJvbnQgY2FuIGFjY2VzcyBBUElnXG4gIGNvbnN0IGRpc3RyaWJ1dGlvbiA9IG5ldyBEaXN0cmlidXRpb24oc3RhY2ssIGAke25hbWV9RGlzdHJpYnV0aW9uYCwge1xuICAgIGRvbWFpbk5hbWVzOiBbZG9tYWluTmFtZV0sXG4gICAgY29tbWVudDogZG9tYWluTmFtZSxcbiAgICBkZWZhdWx0Um9vdE9iamVjdDogZGVmYXVsdEluZGV4ID8gJ2luZGV4Lmh0bWwnIDogdW5kZWZpbmVkLFxuICAgIGRlZmF1bHRCZWhhdmlvcjoge1xuICAgICAgLy8gUmVxdWVzdCBiaW46IGRlZmF1bHQgaXMgdG8gZGVmbGVjdCBhbGwgcmVxdWVzdHMgdGhhdCBhcmVuJ3Qga25vd24gdG8gdGhlIEFQSSAtIG1vc3RseSBzY3JpcHRzIHByb2JpbmcgZm9yIFdvcmRwcmVzcyBpbnN0YWxsYXRpb25zXG4gICAgICBvcmlnaW46IG5ldyBTM09yaWdpbihidWNrZXQsIHsgb3JpZ2luQWNjZXNzSWRlbnRpdHkgfSksXG4gICAgICBhbGxvd2VkTWV0aG9kczogQWxsb3dlZE1ldGhvZHMuQUxMT1dfR0VUX0hFQURfT1BUSU9OUywgLy8gTWluaW1hbCBtZXRob2RzIC0gZG8gd2UgbmVlZCBPcHRpb25zP1xuICAgICAgdmlld2VyUHJvdG9jb2xQb2xpY3k6IFZpZXdlclByb3RvY29sUG9saWN5LlJFRElSRUNUX1RPX0hUVFBTLFxuICAgICAgY2FjaGVQb2xpY3k6IENhY2hlUG9saWN5LkNBQ0hJTkdfT1BUSU1JWkVELFxuICAgIH0sXG4gICAgY2VydGlmaWNhdGU6IG5ldyBEbnNWYWxpZGF0ZWRDZXJ0aWZpY2F0ZShzdGFjaywgYCR7bmFtZX1DZXJ0aWZpY2F0ZWAsIHtcbiAgICAgIGRvbWFpbk5hbWUsXG4gICAgICBob3N0ZWRab25lOiB6b25lLFxuICAgICAgcmVnaW9uOiAndXMtZWFzdC0xJyxcbiAgICB9KSxcbiAgfSk7XG4gIG5ldyByb3V0ZTUzLkFSZWNvcmQoc3RhY2ssIGAke25hbWV9QVJlY29yZGAsIHtcbiAgICByZWNvcmROYW1lOiBkb21haW5OYW1lLFxuICAgIHRhcmdldDogcm91dGU1My5SZWNvcmRUYXJnZXQuZnJvbUFsaWFzKG5ldyBDbG91ZEZyb250VGFyZ2V0KGRpc3RyaWJ1dGlvbikpLFxuICAgIHpvbmUsXG4gIH0pO1xuXG4gIC8vIEhhbmRsZSBBUEkgcGF0aHNcbiAgY29uc3QgbGFtYmRhczogeyBbcGF0aDogc3RyaW5nXTogRnVuY3Rpb247IH0gPSB7fTtcbiAgLy8gQWxsb3dlZCBoZWFkZXJzOlxuICAvLyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy83MTM2Nzk4Mi9jbG91ZGZyb250LWdpdmVzLTQwMy13aGVuLW9yaWdpbi1yZXF1ZXN0LXBvbGljeS1pbmNsdWRlLWFsbC1oZWFkZXJzLXF1ZXJ5c3RyaVxuICAvLyBPcmlnaW5SZXF1ZXN0SGVhZGVyQmVoYXZpb3IuYWxsKCkgZ2l2ZXMgYW4gZXJyb3Igc28ganVzdCBjb29raWUsIHVzZXItYWdlbnQsIHJlZmVyZXJcbiAgLy8gY29uc3Qgb3JpZ2luUmVxdWVzdFBvbGljeSA9IG5ldyBPcmlnaW5SZXF1ZXN0UG9saWN5KHN0YWNrLCBgJHtuYW1lfU9yaWdpblJlcXVlc3RQb2xpY3lgLCB7XG4gIC8vICAgaGVhZGVyQmVoYXZpb3I6IE9yaWdpblJlcXVlc3RIZWFkZXJCZWhhdmlvci5hbGxvd0xpc3QoLi4uYWxsb3dlZEhlYWRlcnMsICd1c2VyLWFnZW50JywgJ1VzZXItQWdlbnQnLCAnUmVmZXJlcicsICdyZWZlcmVyJyksXG4gIC8vICAgY29va2llQmVoYXZpb3I6IE9yaWdpblJlcXVlc3RDb29raWVCZWhhdmlvci5hbGwoKSxcbiAgLy8gICBxdWVyeVN0cmluZ0JlaGF2aW9yOiBPcmlnaW5SZXF1ZXN0UXVlcnlTdHJpbmdCZWhhdmlvci5hbGwoKSxcbiAgLy8gfSk7XG5cbiAgLy8gQXQgc29tZSBwb2ludCB3ZSBjYWggcHJvYmFibHkgbW92ZSB0bzpcbiAgLy8gT3JpZ2luUmVxdWVzdFBvbGljeS5BTExfVklFV0VSX0VYQ0VQVF9IT1NUX0hFQURFUlxuICAvLyBJdCdzIGluIHRoZSBkb2NzLCBidXQgbm90IHNob3dpbmcgdXAgaW4gY29kZTogaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL2Nkay9hcGkvdjIvZG9jcy9hd3MtY2RrLWxpYi5hd3NfY2xvdWRmcm9udC5PcmlnaW5SZXF1ZXN0UG9saWN5Lmh0bWwjc3RhdGljLWFsbF92aWV3ZXJfZXhjZXB0X2hvc3RfaGVhZGVyXG4gIC8vIChzZWVtcyBsaWtlIGl0J3Mgbm90IGF2YWlsYWJsZSB5ZXQ/IFRoaXMgc2VlbXMgbGlrZSBhbiBvbGQgaXNzdWUgdGhvdWdoOiBodHRwczovL2dpdGh1Yi5jb20vYX