UNPKG

cdk-nextjs-standalone

Version:

Deploy a NextJS app to AWS using CDK and OpenNext.

396 lines (393 loc) 62.7 kB
"use strict"; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.NextjsDistribution = void 0; const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti"); const fs = require("node:fs"); const path = require("path"); const aws_cdk_lib_1 = require("aws-cdk-lib"); const cloudfront = require("aws-cdk-lib/aws-cloudfront"); const aws_cloudfront_1 = require("aws-cdk-lib/aws-cloudfront"); const origins = require("aws-cdk-lib/aws-cloudfront-origins"); const aws_iam_1 = require("aws-cdk-lib/aws-iam"); const lambda = require("aws-cdk-lib/aws-lambda"); const aws_lambda_1 = require("aws-cdk-lib/aws-lambda"); const constructs_1 = require("constructs"); const constants_1 = require("./constants"); /** * Create a CloudFront distribution to serve a Next.js application. */ class NextjsDistribution extends constructs_1.Construct { constructor(scope, id, props) { super(scope, id); this.commonBehaviorOptions = { viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, compress: true, }; /** * Common security headers applied by default to all origins * @see https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-response-headers-policies.html#managed-response-headers-policies-security */ this.commonSecurityHeadersBehavior = { contentTypeOptions: { override: false }, frameOptions: { frameOption: cloudfront.HeadersFrameOption.SAMEORIGIN, override: false }, referrerPolicy: { override: false, referrerPolicy: cloudfront.HeadersReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN, }, strictTransportSecurity: { accessControlMaxAge: aws_cdk_lib_1.Duration.days(365), includeSubdomains: true, override: false, preload: true, }, xssProtection: { override: false, protection: true, modeBlock: true }, }; this.edgeLambdas = []; this.props = props; // Create Behaviors this.s3Origin = new origins.S3Origin(this.props.staticAssetsBucket, this.props.overrides?.s3OriginProps); this.staticBehaviorOptions = this.createStaticBehaviorOptions(); if (this.isFnUrlIamAuth) { this.edgeLambdas.push(this.createEdgeLambda()); } this.serverBehaviorOptions = this.createServerBehaviorOptions(); this.imageBehaviorOptions = this.createImageBehaviorOptions(); // Create CloudFront Distribution this.distribution = this.getCloudFrontDistribution(); this.addStaticBehaviorsToDistribution(); this.addRootPathBehavior(); } /** * The CloudFront URL of the website. */ get url() { return `https://${this.distribution.distributionDomainName}`; } /** * The ID of the internally created CloudFront Distribution. */ get distributionId() { return this.distribution.distributionId; } /** * The domain name of the internally created CloudFront Distribution. */ get distributionDomain() { return this.distribution.distributionDomainName; } get isFnUrlIamAuth() { return this.props.functionUrlAuthType === lambda.FunctionUrlAuthType.AWS_IAM; } createStaticBehaviorOptions() { const staticBehaviorOptions = this.props.overrides?.staticBehaviorOptions; // create default response headers policy if not provided const responseHeadersPolicy = staticBehaviorOptions?.responseHeadersPolicy ?? new aws_cloudfront_1.ResponseHeadersPolicy(this, 'StaticResponseHeadersPolicy', { // add default header for static assets customHeadersBehavior: { customHeaders: [ { header: 'cache-control', override: false, // MDN Cache-Control Use Case: Caching static assets with "cache busting" // @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#caching_static_assets_with_cache_busting value: `max-age=${aws_cdk_lib_1.Duration.days(365).toSeconds()}, immutable`, }, ], }, securityHeadersBehavior: this.commonSecurityHeadersBehavior, comment: 'Nextjs Static Response Headers Policy', ...this.props.overrides?.staticResponseHeadersPolicyProps, }); return { ...this.commonBehaviorOptions, origin: this.s3Origin, allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD_OPTIONS, cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD_OPTIONS, cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED, responseHeadersPolicy, ...staticBehaviorOptions, }; } get fnUrlAuthType() { return this.props.functionUrlAuthType || lambda.FunctionUrlAuthType.NONE; } /** * Once CloudFront OAC is released, remove this to reduce latency. */ createEdgeLambda() { const signFnUrlDir = path.resolve(__dirname, '..', 'assets', 'lambdas', 'sign-fn-url'); const originRequestEdgeFn = new cloudfront.experimental.EdgeFunction(this, 'EdgeFn', { runtime: aws_lambda_1.Runtime.NODEJS_20_X, handler: 'index.handler', code: lambda.Code.fromAsset(signFnUrlDir), currentVersionOptions: { removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY, // destroy old versions retryAttempts: 1, // async retry attempts }, ...this.props.overrides?.edgeFunctionProps, }); originRequestEdgeFn.currentVersion.grantInvoke(new aws_iam_1.ServicePrincipal('edgelambda.amazonaws.com')); originRequestEdgeFn.currentVersion.grantInvoke(new aws_iam_1.ServicePrincipal('lambda.amazonaws.com')); originRequestEdgeFn.addToRolePolicy(new aws_iam_1.PolicyStatement({ actions: ['lambda:InvokeFunctionUrl'], resources: [this.props.serverFunction.functionArn, this.props.imageOptFunction.functionArn], })); const originRequestEdgeFnVersion = lambda.Version.fromVersionArn(this, 'Version', originRequestEdgeFn.currentVersion.functionArn); return { eventType: cloudfront.LambdaEdgeEventType.ORIGIN_REQUEST, functionVersion: originRequestEdgeFnVersion, includeBody: true, }; } createServerBehaviorOptions() { const fnUrl = this.props.serverFunction.addFunctionUrl({ authType: this.fnUrlAuthType, invokeMode: this.props.streaming ? aws_lambda_1.InvokeMode.RESPONSE_STREAM : aws_lambda_1.InvokeMode.BUFFERED, }); const origin = new origins.HttpOrigin(aws_cdk_lib_1.Fn.parseDomainName(fnUrl.url), this.props.overrides?.serverHttpOriginProps); const serverBehaviorOptions = this.props.overrides?.serverBehaviorOptions; // create default cache policy if not provided const cachePolicy = serverBehaviorOptions?.cachePolicy ?? new cloudfront.CachePolicy(this, 'ServerCachePolicy', { queryStringBehavior: cloudfront.CacheQueryStringBehavior.all(), headerBehavior: cloudfront.CacheHeaderBehavior.allowList('x-open-next-cache-key'), cookieBehavior: cloudfront.CacheCookieBehavior.all(), defaultTtl: aws_cdk_lib_1.Duration.seconds(0), maxTtl: aws_cdk_lib_1.Duration.days(365), minTtl: aws_cdk_lib_1.Duration.seconds(0), enableAcceptEncodingBrotli: true, enableAcceptEncodingGzip: true, comment: 'Nextjs Server Cache Policy', ...this.props.overrides?.serverCachePolicyProps, }); // create default response headers policy if not provided const responseHeadersPolicy = serverBehaviorOptions?.responseHeadersPolicy ?? new aws_cloudfront_1.ResponseHeadersPolicy(this, 'ServerResponseHeadersPolicy', { customHeadersBehavior: { customHeaders: [ { header: 'cache-control', override: false, // MDN Cache-Control Use Case: Up-to-date contents always // @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#up-to-date_contents_always value: 'no-cache', }, ], }, securityHeadersBehavior: this.commonSecurityHeadersBehavior, comment: 'Nextjs Server Response Headers Policy', ...this.props.overrides?.serverResponseHeadersPolicyProps, }); return { ...this.commonBehaviorOptions, origin, allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL, originRequestPolicy: cloudfront.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER, edgeLambdas: this.edgeLambdas.length ? this.edgeLambdas : undefined, functionAssociations: this.createCloudFrontFnAssociations(), cachePolicy, responseHeadersPolicy, ...serverBehaviorOptions, }; } useCloudFrontFunctionHostHeader() { return ` event.request.headers["x-forwarded-host"] = event.request.headers.host;`; } useCloudFrontFunctionCacheHeaderKey() { // This function is used to improve cache hit ratio by setting the cache key // based on the request headers and the path. `next/image` only needs the // accept header, and this header is not useful for the rest of the query return ` const getHeader = (key) => { const header = event.request.headers[key]; if (header) { if (header.multiValue) { return header.multiValue.map((header) => header.value).join(","); } if (header.value) { return header.value; } } return ""; } let cacheKey = ""; if (event.request.uri.startsWith("/_next/image")) { cacheKey = getHeader("accept"); } else { cacheKey = getHeader("rsc") + getHeader("next-router-prefetch") + getHeader("next-router-state-tree") + getHeader("next-url") + getHeader("x-prerender-revalidate"); } if (event.request.cookies["__prerender_bypass"]) { cacheKey += event.request.cookies["__prerender_bypass"] ? event.request.cookies["__prerender_bypass"].value : ""; } const crypto = require("crypto"); const hashedKey = crypto.createHash("md5").update(cacheKey).digest("hex"); event.request.headers["x-open-next-cache-key"] = { value: hashedKey }; `; } /** * If this doesn't run, then Next.js Server's `request.url` will be Lambda Function * URL instead of domain */ createCloudFrontFnAssociations() { let code = this.props.overrides?.viewerRequestFunctionProps?.code?.render() ?? ` async function handler(event) { // INJECT_CLOUDFRONT_FUNCTION_HOST_HEADER // INJECT_CLOUDFRONT_FUNCTION_CACHE_HEADER_KEY return event.request; } `; code = code.replace(/^\s*\/\/\s*INJECT_CLOUDFRONT_FUNCTION_HOST_HEADER.*$/im, this.useCloudFrontFunctionHostHeader()); code = code.replace(/^\s*\/\/\s*INJECT_CLOUDFRONT_FUNCTION_CACHE_HEADER_KEY.*$/im, this.useCloudFrontFunctionCacheHeaderKey()); const cloudFrontFn = new cloudfront.Function(this, 'CloudFrontFn', { runtime: cloudfront.FunctionRuntime.JS_2_0, ...this.props.overrides?.viewerRequestFunctionProps, // Override code last to get injections code: cloudfront.FunctionCode.fromInline(code), }); return [{ eventType: cloudfront.FunctionEventType.VIEWER_REQUEST, function: cloudFrontFn }]; } createImageBehaviorOptions() { const imageOptFnUrl = this.props.imageOptFunction.addFunctionUrl({ authType: this.fnUrlAuthType }); const origin = new origins.HttpOrigin(aws_cdk_lib_1.Fn.parseDomainName(imageOptFnUrl.url), this.props.overrides?.imageHttpOriginProps); const imageBehaviorOptions = this.props.overrides?.imageBehaviorOptions; // add default cache policy if not provided const cachePolicy = imageBehaviorOptions?.cachePolicy ?? new cloudfront.CachePolicy(this, 'ImageCachePolicy', { queryStringBehavior: cloudfront.CacheQueryStringBehavior.all(), headerBehavior: cloudfront.CacheHeaderBehavior.allowList('accept'), cookieBehavior: cloudfront.CacheCookieBehavior.none(), defaultTtl: aws_cdk_lib_1.Duration.days(1), maxTtl: aws_cdk_lib_1.Duration.days(365), minTtl: aws_cdk_lib_1.Duration.days(0), enableAcceptEncodingBrotli: true, enableAcceptEncodingGzip: true, comment: 'Nextjs Image Cache Policy', ...this.props.overrides?.imageCachePolicyProps, }); // add default response headers policy if not provided const responseHeadersPolicy = imageBehaviorOptions?.responseHeadersPolicy ?? new aws_cloudfront_1.ResponseHeadersPolicy(this, 'ImageResponseHeadersPolicy', { customHeadersBehavior: { customHeaders: [ { header: 'cache-control', override: false, // MDN Cache-Control Use Case: Up-to-date contents always // @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#up-to-date_contents_always value: 'no-cache', }, ], }, securityHeadersBehavior: this.commonSecurityHeadersBehavior, comment: 'Nextjs Image Response Headers Policy', ...this.props.overrides?.imageResponseHeadersPolicyProps, }); return { ...this.commonBehaviorOptions, origin, allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD_OPTIONS, cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD_OPTIONS, originRequestPolicy: cloudfront.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER, edgeLambdas: this.edgeLambdas, cachePolicy, responseHeadersPolicy, ...imageBehaviorOptions, }; } /** * Creates or uses user specified CloudFront Distribution adding behaviors * needed for Next.js. */ getCloudFrontDistribution() { let distribution; if (this.props.distribution) { distribution = this.props.distribution; } else { distribution = this.createCloudFrontDistribution(); } distribution.addBehavior(this.getPathPattern('api/*'), this.serverBehaviorOptions.origin, this.serverBehaviorOptions); distribution.addBehavior(this.getPathPattern('_next/data/*'), this.serverBehaviorOptions.origin, this.serverBehaviorOptions); distribution.addBehavior(this.getPathPattern('_next/image*'), this.imageBehaviorOptions.origin, this.imageBehaviorOptions); return distribution; } /** * Creates default CloudFront Distribution. Note, this construct will not * create a CloudFront Distribution if one is passed in by user. */ createCloudFrontDistribution() { return new cloudfront.Distribution(this, 'Distribution', { // defaultRootObject: "index.html", defaultRootObject: '', minimumProtocolVersion: cloudfront.SecurityPolicyProtocol.TLS_V1_2_2021, domainNames: this.props.nextDomain?.domainNames, certificate: this.props.nextDomain?.certificate, // these values can NOT be overwritten by cfDistributionProps defaultBehavior: this.serverBehaviorOptions, ...this.props.overrides?.distributionProps, }); } /** * this needs to be added last so that it doesn't override any other behaviors * when basePath is set, we emulate the "default behavior" (*) and / as `/base-path/*` * @private */ addRootPathBehavior() { // if we don't have a static file called index.html then we should // redirect to the lambda handler const hasIndexHtml = this.props.nextBuild.readPublicFileList().includes('index.html'); if (hasIndexHtml) return; // don't add root path behavior const { origin, ...options } = this.serverBehaviorOptions; // when basePath is set, we emulate the "default behavior" (*) for the site as `/base-path/*` if (this.props.basePath) { this.distribution.addBehavior(this.getPathPattern(''), origin, options); this.distribution.addBehavior(this.getPathPattern('*'), origin, options); } else { this.distribution.addBehavior(this.getPathPattern('/'), origin, options); } } addStaticBehaviorsToDistribution() { const publicFiles = fs.readdirSync(path.join(this.props.nextjsPath, constants_1.NEXTJS_BUILD_DIR, constants_1.NEXTJS_STATIC_DIR), { withFileTypes: true, }); if (publicFiles.length >= 25) { throw new Error('Too many public/ files in Next.js build. CloudFront limits Distributions to 25 Cache Behaviors. See documented limit here: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html#limits-web-distributions'); } for (const publicFile of publicFiles) { const pathPattern = publicFile.isDirectory() ? `${publicFile.name}/*` : publicFile.name; if (!/^[a-zA-Z0-9_\-\.\*\$/~"'@:+?&]+$/.test(pathPattern)) { throw new Error(`Invalid CloudFront Distribution Cache Behavior Path Pattern: ${pathPattern}. Please see documentation here: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-web-values-specify.html#DownloadDistValuesPathPattern`); } const finalPathPattern = this.getPathPattern(pathPattern); this.distribution.addBehavior(finalPathPattern, this.s3Origin, this.staticBehaviorOptions); } } /** * Optionally prepends base path to given path pattern. */ getPathPattern(pathPattern) { if (this.props.basePath) { // because we already have a basePath we don't use / instead we use /base-path if (pathPattern === '') return this.props.basePath; return `${this.props.basePath}/${pathPattern}`; } return pathPattern; } } exports.NextjsDistribution = NextjsDistribution; _a = JSII_RTTI_SYMBOL_1; NextjsDistribution[_a] = { fqn: "cdk-nextjs-standalone.NextjsDistribution", version: "4.2.3" }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTmV4dGpzRGlzdHJpYnV0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL05leHRqc0Rpc3RyaWJ1dGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLDhCQUE4QjtBQUM5Qiw2QkFBNkI7QUFDN0IsNkNBQTBEO0FBQzFELHlEQUF5RDtBQUN6RCwrREFNb0M7QUFDcEMsOERBQThEO0FBRTlELGlEQUF3RTtBQUN4RSxpREFBaUQ7QUFDakQsdURBQTZEO0FBRTdELDJDQUF1QztBQUN2QywyQ0FBa0U7QUFnR2xFOztHQUVHO0FBQ0gsTUFBYSxrQkFBbUIsU0FBUSxzQkFBUztJQTBDL0MsWUFBWSxLQUFnQixFQUFFLEVBQVUsRUFBRSxLQUE4QjtRQUN0RSxLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBcENYLDBCQUFxQixHQUEwRTtZQUNyRyxvQkFBb0IsRUFBRSxVQUFVLENBQUMsb0JBQW9CLENBQUMsaUJBQWlCO1lBQ3ZFLFFBQVEsRUFBRSxJQUFJO1NBQ2YsQ0FBQztRQUVGOzs7V0FHRztRQUNLLGtDQUE2QixHQUErQztZQUNsRixrQkFBa0IsRUFBRSxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUU7WUFDdkMsWUFBWSxFQUFFLEVBQUUsV0FBVyxFQUFFLFVBQVUsQ0FBQyxrQkFBa0IsQ0FBQyxVQUFVLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRTtZQUN4RixjQUFjLEVBQUU7Z0JBQ2QsUUFBUSxFQUFFLEtBQUs7Z0JBQ2YsY0FBYyxFQUFFLFVBQVUsQ0FBQyxxQkFBcUIsQ0FBQywrQkFBK0I7YUFDakY7WUFDRCx1QkFBdUIsRUFBRTtnQkFDdkIsbUJBQW1CLEVBQUUsc0JBQVEsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDO2dCQUN2QyxpQkFBaUIsRUFBRSxJQUFJO2dCQUN2QixRQUFRLEVBQUUsS0FBSztnQkFDZixPQUFPLEVBQUUsSUFBSTthQUNkO1lBQ0QsYUFBYSxFQUFFLEVBQUUsUUFBUSxF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VSxDQUFDLElBQUksQ0FBQztZQUN4RixJQUFJLENBQUMsa0NBQWtDLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUM7Z0JBQzFELE1BQU0sSUFBSSxLQUFLLENBQ2IsZ0VBQWdFLFdBQVcsd0tBQXdLLENBQ3BQLENBQUM7WUFDSixDQUFDO1lBQ0QsTUFBTSxnQkFBZ0IsR0FBRyxJQUFJLENBQUMsY0FBYyxDQUFDLFdBQVcsQ0FBQyxDQUFDO1lBQzFELElBQUksQ0FBQyxZQUFZLENBQUMsV0FBVyxDQUFDLGdCQUFnQixFQUFFLElBQUksQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLHFCQUFxQixDQUFDLENBQUM7UUFDN0YsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNLLGNBQWMsQ0FBQyxXQUFtQjtRQUN4QyxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDeEIsOEVBQThFO1lBQzlFLElBQUksV0FBVyxLQUFLLEVBQUU7Z0JBQUUsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQztZQUNuRCxPQUFPLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLElBQUksV0FBVyxFQUFFLENBQUM7UUFDakQsQ0FBQztRQUVELE9BQU8sV0FBVyxDQUFDO0lBQ3JCLENBQUM7O0FBdmNILGdEQXdjQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGZzIGZyb20gJ25vZGU6ZnMnO1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCB7IER1cmF0aW9uLCBGbiwgUmVtb3ZhbFBvbGljeSB9IGZyb20gJ2F3cy1jZGstbGliJztcbmltcG9ydCAqIGFzIGNsb3VkZnJvbnQgZnJvbSAnYXdzLWNkay1saWIvYXdzLWNsb3VkZnJvbnQnO1xuaW1wb3J0IHtcbiAgQWRkQmVoYXZpb3JPcHRpb25zLFxuICBCZWhhdmlvck9wdGlvbnMsXG4gIENhY2hlUG9saWN5UHJvcHMsXG4gIERpc3RyaWJ1dGlvbixcbiAgUmVzcG9uc2VIZWFkZXJzUG9saWN5LFxufSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtY2xvdWRmcm9udCc7XG5pbXBvcnQgKiBhcyBvcmlnaW5zIGZyb20gJ2F3cy1jZGstbGliL2F3cy1jbG91ZGZyb250LW9yaWdpbnMnO1xuaW1wb3J0IHsgSHR0cE9yaWdpblByb3BzIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWNsb3VkZnJvbnQtb3JpZ2lucyc7XG5pbXBvcnQgeyBQb2xpY3lTdGF0ZW1lbnQsIFNlcnZpY2VQcmluY2lwYWwgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtaWFtJztcbmltcG9ydCAqIGFzIGxhbWJkYSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtbGFtYmRhJztcbmltcG9ydCB7IFJ1bnRpbWUsIEludm9rZU1vZGUgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtbGFtYmRhJztcbmltcG9ydCAqIGFzIHMzIGZyb20gJ2F3cy1jZGstbGliL2F3cy1zMyc7XG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdjb25zdHJ1Y3RzJztcbmltcG9ydCB7IE5FWFRKU19CVUlMRF9ESVIsIE5FWFRKU19TVEFUSUNfRElSIH0gZnJvbSAnLi9jb25zdGFudHMnO1xuaW1wb3J0IHtcbiAgT3B0aW9uYWxDbG91ZEZyb250RnVuY3Rpb25Qcm9wcyxcbiAgT3B0aW9uYWxEaXN0cmlidXRpb25Qcm9wcyxcbiAgT3B0aW9uYWxFZGdlRnVuY3Rpb25Qcm9wcyxcbiAgT3B0aW9uYWxTM09yaWdpblByb3BzLFxufSBmcm9tICcuL2dlbmVyYXRlZC1zdHJ1Y3RzJztcbmltcG9ydCB7IE5leHRqc1Byb3BzIH0gZnJvbSAnLi9OZXh0anMnO1xuaW1wb3J0IHsgTmV4dGpzQnVpbGQgfSBmcm9tICcuL05leHRqc0J1aWxkJztcbmltcG9ydCB7IE5leHRqc0RvbWFpbiB9IGZyb20gJy4vTmV4dGpzRG9tYWluJztcblxuZXhwb3J0IGludGVyZmFjZSBWaWV3ZXJSZXF1ZXN0RnVuY3Rpb25Qcm9wcyBleHRlbmRzIE9wdGlvbmFsQ2xvdWRGcm9udEZ1bmN0aW9uUHJvcHMge1xuICAvKipcbiAgICogQ2xvdWRmcm9udCBmdW5jdGlvbiBjb2RlIHRoYXQgcnVucyBvbiBWSUVXRVJfUkVRVUVTVC5cbiAgICogVGhlIGZvbGxvd2luZyBjb21tZW50cyB3aWxsIGJlIHJlcGxhY2VkIHdpdGggY29kZSBzbmlwcGV0c1xuICAgKiBzbyB5b3UgY2FuIGN1c3RvbWl6ZSB0aGlzIGZ1bmN0aW9uLlxuICAgKlxuICAgKiBJTkpFQ1RfQ0xPVURGUk9OVF9GVU5DVElPTl9IT1NUX0hFQURFUjogQWRkIHRoZSByZXF1aXJlZCB4LWZvcndhcmRlZC1ob3N0IGhlYWRlci5cbiAgICogSU5KRUNUX0NMT1VERlJPTlRfRlVOQ1RJT05fQ0FDSEVfSEVBREVSX0tFWTogSW1wcm92ZXMgb3Blbi1uZXh0IGNhY2hlIGtleS5cbiAgICpcbiAgICogQGRlZmF1bHRcbiAgICogYXN5bmMgZnVuY3Rpb24gaGFuZGxlcihldmVudCkge1xuICAgKiAgLy8gSU5KRUNUX0NMT1VERlJPTlRfRlVOQ1RJT05fSE9TVF9IRUFERVJcbiAgICogIC8vIElOSkVDVF9DTE9VREZST05UX0ZVTkNUSU9OX0NBQ0hFX0hFQURFUl9LRVlcbiAgICogfVxuICAgKi9cbiAgcmVhZG9ubHkgY29kZT86IGNsb3VkZnJvbnQuRnVuY3Rpb25Db2RlO1xufVxuZXhwb3J0IGludGVyZmFjZSBOZXh0anNEaXN0cmlidXRpb25PdmVycmlkZXMge1xuICByZWFkb25seSB2aWV3ZXJSZXF1ZXN0RnVuY3Rpb25Qcm9wcz86IFZpZXdlclJlcXVlc3RGdW5jdGlvblByb3BzO1xuICByZWFkb25seSBkaXN0cmlidXRpb25Qcm9wcz86IE9wdGlvbmFsRGlzdHJpYnV0aW9uUHJvcHM7XG4gIHJlYWRvbmx5IGVkZ2VGdW5jdGlvblByb3BzPzogT3B0aW9uYWxFZGdlRnVuY3Rpb25Qcm9wcztcbiAgcmVhZG9ubHkgaW1hZ2VCZWhhdmlvck9wdGlvbnM/OiBBZGRCZWhhdmlvck9wdGlvbnM7XG4gIHJlYWRvbmx5IGltYWdlQ2FjaGVQb2xpY3lQcm9wcz86IENhY2hlUG9saWN5UHJvcHM7XG4gIHJlYWRvbmx5IGltYWdlUmVzcG9uc2VIZWFkZXJzUG9saWN5UHJvcHM/OiBjbG91ZGZyb250LlJlc3BvbnNlSGVhZGVyc1BvbGljeVByb3BzO1xuICByZWFkb25seSBpbWFnZUh0dHBPcmlnaW5Qcm9wcz86IEh0dHBPcmlnaW5Qcm9wcztcbiAgcmVhZG9ubHkgc2VydmVyQmVoYXZpb3JPcHRpb25zPzogQWRkQmVoYXZpb3JPcHRpb25zO1xuICByZWFkb25seSBzZXJ2ZXJDYWNoZVBvbGljeVByb3BzPzogQ2FjaGVQb2xpY3lQcm9wcztcbiAgcmVhZG9ubHkgc2VydmVyUmVzcG9uc2VIZWFkZXJzUG9saWN5UHJvcHM/OiBjbG91ZGZyb250LlJlc3BvbnNlSGVhZGVyc1BvbGljeVByb3BzO1xuICByZWFkb25seSBzZXJ2ZXJIdHRwT3JpZ2luUHJvcHM/OiBIdHRwT3JpZ2luUHJvcHM7XG4gIHJlYWRvbmx5IHN0YXRpY0JlaGF2aW9yT3B0aW9ucz86IEFkZEJlaGF2aW9yT3B0aW9ucztcbiAgcmVhZG9ubHkgc3RhdGljUmVzcG9uc2VIZWFkZXJzUG9saWN5UHJvcHM/OiBjbG91ZGZyb250LlJlc3BvbnNlSGVhZGVyc1BvbGljeVByb3BzO1xuICByZWFkb25seSBzM09yaWdpblByb3BzPzogT3B0aW9uYWxTM09yaWdpblByb3BzO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIE5leHRqc0Rpc3RyaWJ1dGlvblByb3BzIHtcbiAgLyoqXG4gICAqIEBzZWUge0BsaW5rIE5leHRqc1Byb3BzLmJhc2VQYXRofVxuICAgKi9cbiAgcmVhZG9ubHkgYmFzZVBhdGg/OiBOZXh0anNQcm9wc1snYmFzZVBhdGgnXTtcbiAgLyoqXG4gICAqIEBzZWUge0BsaW5rIE5leHRqc1Byb3BzLmRpc3RyaWJ1dGlvbn1cbiAgICovXG4gIHJlYWRvbmx5IGRpc3RyaWJ1dGlvbj86IE5leHRqc1Byb3BzWydkaXN0cmlidXRpb24nXTtcbiAgLyoqXG4gICAqIE92ZXJyaWRlIGxhbWJkYSBmdW5jdGlvbiB1cmwgYXV0aCB0eXBlXG4gICAqIEBkZWZhdWx0IFwiTk9ORVwiXG4gICAqL1xuICByZWFkb25seSBmdW5jdGlvblVybEF1dGhUeXBlPzogbGFtYmRhLkZ1bmN0aW9uVXJsQXV0aFR5cGU7XG4gIC8qKlxuICAgKiBMYW1iZGEgZnVuY3Rpb24gdG8gb3B0aW1pemUgaW1hZ2VzLlxuICAgKiBNdXN0IGJlIHByb3ZpZGVkIGlmIHlvdSB3YW50IHRvIHNlcnZlIGR5bmFtaWMgcmVxdWVzdHMuXG4gICAqL1xuICByZWFkb25seSBpbWFnZU9wdEZ1bmN0aW9uOiBsYW1iZGEuSUZ1bmN0aW9uO1xuICAvKipcbiAgICogQHNlZSB7QGxpbmsgTmV4dGpzQnVpbGR9XG4gICAqL1xuICByZWFkb25seSBuZXh0QnVpbGQ6IE5leHRqc0J1aWxkO1xuICAvKipcbiAgICogQHNlZSB7QGxpbmsgTmV4dGpzRG9tYWlufVxuICAgKi9cbiAgcmVhZG9ubHkgbmV4dERvbWFpbj86IE5leHRqc0RvbWFpbjtcbiAgLyoqXG4gICAqIEBzZWUge0BsaW5rIE5leHRqc1Byb3BzLm5leHRqc1BhdGh9XG4gICAqL1xuICByZWFkb25seSBuZXh0anNQYXRoOiBOZXh0anNQcm9wc1snbmV4dGpzUGF0aCddO1xuICAvKipcbiAgICogT3ZlcnJpZGUgcHJvcHMgZm9yIGV2ZXJ5IGNvbnN0cnVjdC5cbiAgICovXG4gIHJlYWRvbmx5IG92ZXJyaWRlcz86IE5leHRqc0Rpc3RyaWJ1dGlvbk92ZXJyaWRlcztcbiAgLyoqXG4gICAqIExhbWJkYSBmdW5jdGlvbiB0byByb3V0ZSBhbGwgbm9uLXN0YXRpYyByZXF1ZXN0cyB0by5cbiAgICogTXVzdCBiZSBwcm92aWRlZCBpZiB5b3Ugd2FudCB0byBzZXJ2ZSBkeW5hbWljIHJlcXVlc3RzLlxuICAgKi9cbiAgcmVhZG9ubHkgc2VydmVyRnVuY3Rpb246IGxhbWJkYS5JRnVuY3Rpb247XG4gIC8qKlxuICAgKiBCdWNrZXQgY29udGFpbmluZyBzdGF0aWMgYXNzZXRzLlxuICAgKiBNdXN0IGJlIHByb3ZpZGVkIGlmIHlvdSB3YW50IHRvIHNlcnZlIHN0YXRpYyBmaWxlcy5cbiAgICovXG4gIHJlYWRvbmx5IHN0YXRpY0Fzc2V0c0J1Y2tldDogczMuSUJ1Y2tldDtcbiAgLyoqXG4gICAqIEBzZWUge0BsaW5rIE5leHRqc1Byb3BzLnN0cmVhbWluZ31cbiAgICovXG4gIHJlYWRvbmx5IHN0cmVhbWluZz86IGJvb2xlYW47XG59XG5cbi8qKlxuICogQ3JlYXRlIGEgQ2xvdWRGcm9udCBkaXN0cmlidXRpb24gdG8gc2VydmUgYSBOZXh0LmpzIGFwcGxpY2F0aW9uLlxuICovXG5leHBvcnQgY2xhc3MgTmV4dGpzRGlzdHJpYnV0aW9uIGV4dGVuZHMgQ29uc3RydWN0IHtcbiAgcHJpdmF0ZSBwcm9wczogTmV4dGpzRGlzdHJpYnV0aW9uUHJvcHM7XG4gIC8qKlxuICAgKiBUaGUgaW50ZXJuYWxseSBjcmVhdGVkIENsb3VkRnJvbnQgYERpc3RyaWJ1dGlvbmAgaW5zdGFuY2UuXG4gICAqL1xuICBwdWJsaWMgZGlzdHJpYnV0aW9uOiBEaXN0cmlidXRpb247XG5cbiAgcHJpdmF0ZSBjb21tb25CZWhhdmlvck9wdGlvbnM6IFBpY2s8Y2xvdWRmcm9udC5CZWhhdmlvck9wdGlvbnMsICd2aWV3ZXJQcm90b2NvbFBvbGljeScgfCAnY29tcHJlc3MnPiA9IHtcbiAgICB2aWV3ZXJQcm90b2NvbFBvbGljeTogY2xvdWRmcm9udC5WaWV3ZXJQcm90b2NvbFBvbGljeS5SRURJUkVDVF9UT19IVFRQUyxcbiAgICBjb21wcmVzczogdHJ1ZSxcbiAgfTtcblxuICAvKipcbiAgICogQ29tbW9uIHNlY3VyaXR5IGhlYWRlcnMgYXBwbGllZCBieSBkZWZhdWx0IHRvIGFsbCBvcmlnaW5zXG4gICAqIEBzZWUgaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL0FtYXpvbkNsb3VkRnJvbnQvbGF0ZXN0L0RldmVsb3Blckd1aWRlL3VzaW5nLW1hbmFnZWQtcmVzcG9uc2UtaGVhZGVycy1wb2xpY2llcy5odG1sI21hbmFnZWQtcmVzcG9uc2UtaGVhZGVycy1wb2xpY2llcy1zZWN1cml0eVxuICAgKi9cbiAgcHJpdmF0ZSBjb21tb25TZWN1cml0eUhlYWRlcnNCZWhhdmlvcjogY2xvdWRmcm9udC5SZXNwb25zZVNlY3VyaXR5SGVhZGVyc0JlaGF2aW9yID0ge1xuICAgIGNvbnRlbnRUeXBlT3B0aW9uczogeyBvdmVycmlkZTogZmFsc2UgfSxcbiAgICBmcmFtZU9wdGlvbnM6IHsgZnJhbWVPcHRpb246IGNsb3VkZnJvbnQuSGVhZGVyc0ZyYW1lT3B0aW9uLlNBTUVPUklHSU4sIG92ZXJyaWRlOiBmYWxzZSB9LFxuICAgIHJlZmVycmVyUG9saWN5OiB7XG4gICAgICBvdmVycmlkZTogZmFsc2UsXG4gICAgICByZWZlcnJlclBvbGljeTogY2xvdWRmcm9udC5IZWFkZXJzUmVmZXJyZXJQb2xpY3kuU1RSSUNUX09SSUdJTl9XSEVOX0NST1NTX09SSUdJTixcbiAgICB9LFxuICAgIHN0cmljdFRyYW5zcG9ydFNlY3VyaXR5OiB7XG4gICAgICBhY2Nlc3NDb250cm9sTWF4QWdlOiBEdXJhdGlvbi5kYXlzKDM2NSksXG4gICAgICBpbmNsdWRlU3ViZG9tYWluczogdHJ1ZSxcbiAgICAgIG92ZXJyaWRlOiBmYWxzZSxcbiAgICAgIHByZWxvYWQ6IHRydWUsXG4gICAgfSxcbiAgICB4c3NQcm90ZWN0aW9uOiB7IG92ZXJyaWRlOiBmYWxzZSwgcHJvdGVjdGlvbjogdHJ1ZSwgbW9kZUJsb2NrOiB0cnVlIH0sXG4gIH07XG5cbiAgcHJpdmF0ZSBzM09yaWdpbjogb3JpZ2lucy5TM09yaWdpbjtcblxuICBwcml2YXRlIHN0YXRpY0JlaGF2aW9yT3B0aW9uczogY2xvdWRmcm9udC5CZWhhdmlvck9wdGlvbnM7XG5cbiAgcHJpdmF0ZSBlZGdlTGFtYmRhczogY2xvdWRmcm9udC5FZGdlTGFtYmRhW10gPSBbXTtcblxuICBwcml2YXRlIHNlcnZlckJlaGF2aW9yT3B0aW9uczogY2xvdWRmcm9udC5CZWhhdmlvck9wdGlvbnM7XG5cbiAgcHJpdmF0ZSBpbWFnZUJlaGF2aW9yT3B0aW9uczogY2xvdWRmcm9udC5CZWhhdmlvck9wdGlvbnM7XG5cbiAgY29uc3RydWN0b3Ioc2NvcGU6IENvbnN0cnVjdCwgaWQ6IHN0cmluZywgcHJvcHM6IE5leHRqc0Rpc3RyaWJ1dGlvblByb3BzKSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkKTtcblxuICAgIHRoaXMucHJvcHMgPSBwcm9wcztcblxuICAgIC8vIENyZWF0ZSBCZWhhdmlvcnNcbiAgICB0aGlzLnMzT3JpZ2luID0gbmV3IG9yaWdpbnMuUzNPcmlnaW4odGhpcy5wcm9wcy5zdGF0aWNBc3NldHNCdWNrZXQsIHRoaXMucHJvcHMub3ZlcnJpZGVzPy5zM09yaWdpblByb3BzKTtcbiAgICB0aGlzLnN0YXRpY0JlaGF2aW9yT3B0aW9ucyA9IHRoaXMuY3JlYXRlU3RhdGljQmVoYXZpb3JPcHRpb25zKCk7XG4gICAgaWYgKHRoaXMuaXNGblVybElhbUF1dGgpIHtcbiAgICAgIHRoaXMuZWRnZUxhbWJkYXMucHVzaCh0aGlzLmNyZWF0ZUVkZ2VMYW1iZGEoKSk7XG4gICAgfVxuICAgIHRoaXMuc2VydmVyQmVoYXZpb3JPcHRpb25zID0gdGhpcy5jcmVhdGVTZXJ2ZXJCZWhhdmlvck9wdGlvbnMoKTtcbiAgICB0aGlzLmltYWdlQmVoYXZpb3JPcHRpb25zID0gdGhpcy5jcmVhdGVJbWFnZUJlaGF2aW9yT3B0aW9ucygpO1xuXG4gICAgLy8gQ3JlYXRlIENsb3VkRnJvbnQgRGlzdHJpYnV0aW9uXG4gICAgdGhpcy5kaXN0cmlidXRpb24gPSB0aGlzLmdldENsb3VkRnJvbnREaXN0cmlidXRpb24oKTtcbiAgICB0aGlzLmFkZFN0YXRpY0JlaGF2aW9yc1RvRGlzdHJpYnV0aW9uKCk7XG4gICAgdGhpcy5hZGRSb290UGF0aEJlaGF2aW9yKCk7XG4gIH1cblxuICAvKipcbiAgICogVGhlIENsb3VkRnJvbnQgVVJMIG9mIHRoZSB3ZWJzaXRlLlxuICAgKi9cbiAgcHVibGljIGdldCB1cmwoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gYGh0dHBzOi8vJHt0aGlzLmRpc3RyaWJ1dGlvbi5kaXN0cmlidXRpb25Eb21haW5OYW1lfWA7XG4gIH1cblxuICAvKipcbiAgICogVGhlIElEIG9mIHRoZSBpbnRlcm5hbGx5IGNyZWF0ZWQgQ2xvdWRGcm9udCBEaXN0cmlidXRpb24uXG4gICAqL1xuICBwdWJsaWMgZ2V0IGRpc3RyaWJ1dGlvbklkKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuZGlzdHJpYnV0aW9uLmRpc3RyaWJ1dGlvbklkO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBkb21haW4gbmFtZSBvZiB0aGUgaW50ZXJuYWxseSBjcmVhdGVkIENsb3VkRnJvbnQgRGlzdHJpYnV0aW9uLlxuICAgKi9cbiAgcHVibGljIGdldCBkaXN0cmlidXRpb25Eb21haW4oKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5kaXN0cmlidXRpb24uZGlzdHJpYnV0aW9uRG9tYWluTmFtZTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0IGlzRm5VcmxJYW1BdXRoKCkge1xuICAgIHJldHVybiB0aGlzLnByb3BzLmZ1bmN0aW9uVXJsQXV0aFR5cGUgPT09IGxhbWJkYS5GdW5jdGlvblVybEF1dGhUeXBlLkFXU19JQU07XG4gIH1cblxuICBwcml2YXRlIGNyZWF0ZVN0YXRpY0JlaGF2aW9yT3B0aW9ucygpOiBCZWhhdmlvck9wdGlvbnMge1xuICAgIGNvbnN0IHN0YXRpY0JlaGF2aW9yT3B0aW9ucyA9IHRoaXMucHJvcHMub3ZlcnJpZGVzPy5zdGF0aWNCZWhhdmlvck9wdGlvbnM7XG5cbiAgICAvLyBjcmVhdGUgZGVmYXVsdCByZXNwb25zZSBoZWFkZXJzIHBvbGljeSBpZiBub3QgcHJvdmlkZWRcbiAgICBjb25zdCByZXNwb25zZUhlYWRlcnNQb2xpY3kgPVxuICAgICAgc3RhdGljQmVoYXZpb3JPcHRpb25zPy5yZXNwb25zZUhlYWRlcnNQb2xpY3kgPz9cbiAgICAgIG5ldyBSZXNwb25zZUhlYWRlcnNQb2xpY3kodGhpcywgJ1N0YXRpY1Jlc3BvbnNlSGVhZGVyc1BvbGljeScsIHtcbiAgICAgICAgLy8gYWRkIGRlZmF1bHQgaGVhZGVyIGZvciBzdGF0aWMgYXNzZXRzXG4gICAgICAgIGN1c3RvbUhlYWRlcnNCZWhhdmlvcjoge1xuICAgICAgICAgIGN1c3RvbUhlYWRlcnM6IFtcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgaGVhZGVyOiAnY2FjaGUtY29udHJvbCcsXG4gICAgICAgICAgICAgIG92ZXJyaWRlOiBmYWxzZSxcbiAgICAgICAgICAgICAgLy8gTUROIENhY2hlLUNvbnRyb2wgVXNlIENhc2U6IENhY2hpbmcgc3RhdGljIGFzc2V0cyB3aXRoIFwiY2FjaGUgYnVzdGluZ1wiXG4gICAgICAgICAgICAgIC8vIEBzZWU6IGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0hUVFAvSGVhZGVycy9DYWNoZS1Db250cm9sI2NhY2hpbmdfc3RhdGljX2Fzc2V0c193aXRoX2NhY2hlX2J1c3RpbmdcbiAgICAgICAgICAgICAgdmFsdWU6IGBtYXgtYWdlPSR7RHVyYXRpb24uZGF5cygzNjUpLnRvU2Vjb25kcygpfSwgaW1tdXRhYmxlYCxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgXSxcbiAgICAgICAgfSxcbiAgICAgICAgc2VjdXJpdHlIZWFkZXJzQmVoYXZpb3I6IHRoaXMuY29tbW9uU2VjdXJpdHlIZWFkZXJzQmVoYXZpb3IsXG4gICAgICAgIGNvbW1lbnQ6ICdOZXh0anMgU3RhdGljIFJlc3BvbnNlIEhlYWRlcnMgUG9saWN5JyxcbiAgICAgICAgLi4udGhpcy5wcm9wcy5vdmVycmlkZXM/LnN0YXRpY1Jlc3BvbnNlSGVhZGVyc1BvbGljeVByb3BzLFxuICAgICAgfSk7XG5cbiAgICByZXR1cm4ge1xuICAgICAgLi4udGhpcy5jb21tb25CZWhhdmlvck9wdGlvbnMsXG4gICAgICBvcmlnaW46IHRoaXMuczNPcmlnaW4sXG4gICAgICBhbGxvd2VkTWV0aG9kczogY2xvdWRmcm9udC5BbGxvd2VkTWV0aG9kcy5BTExPV19HRVRfSEVBRF9PUFRJT05TLFxuICAgICAgY2FjaGVkTWV0aG9kczogY2xvdWRmcm9udC5DYWNoZWRNZXRob2RzLkNBQ0hFX0dFVF9IRUFEX09QVElPTlMsXG4gICAgICBjYWNoZVBvbGljeTogY2xvdWRmcm9udC5DYWNoZVBvbGljeS5DQUNISU5HX09QVElNSVpFRCxcbiAgICAgIHJlc3BvbnNlSGVhZGVyc1BvbGljeSxcbiAgICAgIC4uLnN0YXRpY0JlaGF2aW9yT3B0aW9ucyxcbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBnZXQgZm5VcmxBdXRoVHlwZSgpOiBsYW1iZGEuRnVuY3Rpb25VcmxBdXRoVHlwZSB7XG4gICAgcmV0dXJuIHRoaXMucHJvcHMuZnVuY3Rpb25VcmxBdXRoVHlwZSB8fCBsYW1iZGEuRnVuY3Rpb25VcmxBdXRoVHlwZS5OT05FO1xuICB9XG5cbiAgLyoqXG4gICAqIE9uY2UgQ2xvdWRGcm9udCBPQUMgaXMgcmVsZWFzZWQsIHJlbW92ZSB0aGlzIHRvIHJlZHVjZSBsYXRlbmN5LlxuICAgKi9cbiAgcHJpdmF0ZSBjcmVhdGVFZGdlTGFtYmRhKCk6IGNsb3VkZnJvbnQuRWRnZUxhbWJkYSB7XG4gICAgY29uc3Qgc2lnbkZuVXJsRGlyID0gcGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgJy4uJywgJ2Fzc2V0cycsICdsYW1iZGFzJywgJ3NpZ24tZm4tdXJsJyk7XG4gICAgY29uc3Qgb3JpZ2luUmVxdWVzdEVkZ2VGbiA9IG5ldyBjbG91ZGZyb250LmV4cGVyaW1lbnRhbC5FZGdlRnVuY3Rpb24odGhpcywgJ0VkZ2VGbicsIHtcbiAgICAgIHJ1bnRpbWU6IFJ1bnRpbWUuTk9ERUpTXzIwX1gsXG4gICAgICBoYW5kbGVyOiAnaW5kZXguaGFuZGxlcicsXG4gICAgICBjb2RlOiBsYW1iZGEuQ29kZS5mcm9tQXNzZXQoc2lnbkZuVXJsRGlyKSxcbiAgICAgIGN1cnJlbnRWZXJzaW9uT3B0aW9uczoge1xuICAgICAgICByZW1vdmFsUG9saWN5OiBSZW1vdmFsUG9saWN5LkRFU1RST1ksIC8vIGRlc3Ryb3kgb2xkIHZlcnNpb25zXG4gICAgICAgIHJldHJ5QXR0ZW1wdHM6IDEsIC8vIGFzeW5jIHJldHJ5IGF0dGVtcHRzXG4gICAgICB9LFxuICAgICAgLi4udGhpcy5wcm9wcy5vdmVycmlkZXM/LmVkZ2VGdW5jdGlvblByb3BzLFxuICAgIH0pO1xuICAgIG9yaWdpblJlcXVlc3RFZGdlRm4uY3VycmVudFZlcnNpb24uZ3JhbnRJbnZva2UobmV3IFNlcnZpY2VQcmluY2lwYWwoJ2VkZ2VsYW1iZGEuYW1hem9uYXdzLmNvbScpKTtcbiAgICBvcmlnaW5SZXF1ZXN0RWRnZUZuLmN1cnJlbnRWZXJzaW9uLmdyYW50SW52b2tlKG5ldyBTZXJ2aWNlUHJpbmNpcGFsKCdsYW1iZGEuYW1hem9uYXdzLmNvbScpKTtcbiAgICBvcmlnaW5SZXF1ZXN0RWRnZUZuLmFkZFRvUm9sZVBvbGljeShcbiAgICAgIG5ldyBQb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgICBhY3Rpb25zOiBbJ2xhbWJkYTpJbnZva2VGdW5jdGlvblVybCddLFxuICAgICAgICByZXNvdXJjZXM6IFt0aGlzLnByb3BzLnNlcnZlckZ1bmN0aW9uLmZ1bmN0aW9uQXJuLCB0aGlzLnByb3BzLmltYWdlT3B0RnVuY3Rpb24uZnVuY3Rpb25Bcm5dLFxuICAgICAgfSlcbiAgICApO1xuICAgIGNvbnN0IG9yaWdpblJlcXVlc3RFZGdlRm5WZXJzaW9uID0gbGFtYmRhLlZlcnNpb24uZnJvbVZlcnNpb25Bcm4oXG4gICAgICB0aGlzLFxuICAgICAgJ1ZlcnNpb24nLFxuICAgICAgb3JpZ2luUmVxdWVzdEVkZ2VGbi5jdXJyZW50VmVyc2lvbi5mdW5jdGlvbkFyblxuICAgICk7XG4gICAgcmV0dXJuIHtcbiAgICAgIGV2ZW50VHlwZTogY2xvdWRmcm9udC5MYW1iZGFFZGdlRXZlbnRUeXBlLk9SSUdJTl9SRVFVRVNULFxuICAgICAgZnVuY3Rpb25WZXJzaW9uOiBvcmlnaW5SZXF1ZXN0RWRnZUZuVmVyc2lvbixcbiAgICAgIGluY2x1ZGVCb2R5OiB0cnVlLFxuICAgIH07XG4gIH1cblxuICBwcml2YXRlIGNyZWF0ZVNlcnZlckJlaGF2aW9yT3B0aW9ucygpOiBjbG91ZGZyb250LkJlaGF2aW9yT3B0aW9ucyB7XG4gICAgY29uc3QgZm5VcmwgPSB0aGlzLnByb3BzLnNlcnZlckZ1bmN0aW9uLmFkZEZ1bmN0aW9uVXJsKHtcbiAgICAgIGF1dGhUeXBlOiB0aGlzLmZuVXJsQXV0aFR5cGUsXG4gICAgICBpbnZva2VNb2RlOiB0aGlzLnByb3BzLnN0cmVhbWluZyA/IEludm9rZU1vZGUuUkVTUE9OU0VfU1RSRUFNIDogSW52b2tlTW9kZS5CVUZGRVJFRCxcbiAgICB9KTtcbiAgICBjb25zdCBvcmlnaW4gPSBuZXcgb3JpZ2lucy5IdHRwT3JpZ2luKEZuLnBhcnNlRG9tYWluTmFtZShmblVybC51cmwpLCB0aGlzLnByb3BzLm92ZXJyaWRlcz8uc2VydmVySHR0cE9yaWdpblByb3BzKTtcbiAgICBjb25zdCBzZXJ2ZXJCZWhhdmlvck9wdGlvbnMgPSB0aGlzLnByb3BzLm92ZXJyaWRlcz8uc2VydmVyQmVoYXZpb3JPcHRpb25zO1xuXG4gICAgLy8gY3JlYXRlIGRlZmF1bHQgY2FjaGUgcG9saWN5IGlmIG5vdCBwcm92aWRlZFxuICAgIGNvbnN0IGNhY2hlUG9saWN5ID1cbiAgICAgIHNlcnZlckJlaGF2aW9yT3B0aW9ucz8uY2FjaGVQb2xpY3kgPz9cbiAgICAgIG5ldyBjbG91ZGZyb250LkNhY2hlUG9saWN5KHRoaXMsICdTZXJ2ZXJDYWNoZVBvbGljeScsIHtcbiAgICAgICAgcXVlcnlTdHJpbmdCZWhhdmlvcjogY2xvdWRmcm9udC5DYWNoZVF1ZXJ5U3RyaW5nQmVoYXZpb3IuYWxsKCksXG4gICAgICAgIGhlYWRlckJlaGF2aW9yOiBjbG91ZGZyb250LkNhY2hlSGVhZGVyQmVoYXZpb3IuYWxsb3dMaXN0KCd4LW9wZW4tbmV4dC1jYWNoZS1rZXknKSxcbiAgICAgICAgY29va2llQmVoYXZpb3I6IGNsb3VkZnJvbnQuQ2FjaGVDb29raWVCZWhhdmlvci5hbGwoKSxcbiAgICAgICAgZGVmYXVsdFR0bDogRHVyYXRpb24uc2Vjb25kcygwKSxcbiAgICAgICAgbWF4VHRsOiBEdXJhdGlvbi5kYXlzKDM2NSksXG4gICAgICAgIG1pblR0bDogRHVyYXRpb24uc2Vjb25kcygwKSxcbiAgICAgICAgZW5hYmxlQWNjZXB0RW5jb2RpbmdCcm90bGk6IHRydWUsXG4gICAgICAgIGVuYWJsZUFjY2VwdEVuY29kaW5nR3ppcDogdHJ1ZSxcbiAgICAgICAgY29tbWVudDogJ05leHRqcyBTZXJ2ZXIgQ2FjaGUgUG9saWN5JyxcbiAgICAgICAgLi4udGhpcy5wcm9wcy5vdmVycmlkZXM/LnNlcnZlckNhY2hlUG9saWN5UHJvcHMsXG4gICAgICB9KTtcblxuICAgIC8vIGNyZWF0ZSBkZWZhdWx0IHJlc3BvbnNlIGhlYWRlcnMgcG9saWN5IGlmIG5vdCBwcm92aWRlZFxuICAgIGNvbnN0IHJlc3BvbnNlSGVhZGVyc1BvbGljeSA9XG4gICAgICBzZXJ2ZXJCZWhhdmlvck9wdGlvbnM/LnJlc3BvbnNlSGVhZGVyc1BvbGljeSA/P1xuICAgICAgbmV3IFJlc3BvbnNlSGVhZGVyc1BvbGljeSh0aGlzLCAnU2VydmVyUmVzcG9uc2VIZWFkZXJzUG9saWN5Jywge1xuICAgICAgICBjdXN0b21IZWFkZXJzQmVoYXZpb3I6IHtcbiAgICAgICAgICBjdXN0b21IZWFkZXJzOiBbXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGhlYWRlcjogJ2NhY2hlLWNvbnRyb2wnLFxuICAgICAgICAgICAgICBvdmVycmlkZTogZmFsc2UsXG4gICAgICAgICAgICAgIC8vIE1ETiBDYWNoZS1Db250cm9sIFVzZSBDYXNlOiBVcC10by1kYXRlIGNvbnRlbnRzIGFsd2F5c1xuICAgICAgICAgICAgICAvLyBAc2VlOiBodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9IVFRQL0hlYWRlcnMvQ2FjaGUtQ29udHJvbCN1cC10by1kYXRlX2NvbnRlbnRzX2Fsd2F5c1xuICAgICAgICAgICAgICB2YWx1ZTogJ25vLWNhY2hlJyxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgXSxcbiAgICAgICAgfSxcbiAgICAgICAgc2VjdXJpdHlIZWFkZXJzQmVoYXZpb3I6IHRoaXMuY29tbW9uU2VjdXJpdHlIZWFkZXJzQmVoYXZpb3IsXG4gICAgICAgIGNvbW1lbnQ6ICdOZXh0anMgU2VydmVyIFJlc3BvbnNlIEhlYWRlcnMgUG9saWN5JyxcbiAgICAgICAgLi4udGhpcy5wcm9wcy5vdmVycmlkZXM/LnNlcnZlclJlc3BvbnNlSGVhZGVyc1BvbGljeVByb3BzLFxuICAgICAgfSk7XG5cbiAgICByZXR1cm4ge1xuICAgICAgLi4udGhpcy5jb21tb25CZWhhdmlvck9wdGlvbnMsXG4gICAgICBvcmlnaW4sXG4gICAgICBhbGxvd2VkTWV0aG9kczogY2xvdWRmcm9udC5BbGxvd2VkTWV0aG9kcy5BTExPV19BTEwsXG4gICAgICBvcmlnaW5SZXF1ZXN0UG9saWN5OiBjbG91ZGZyb250Lk9yaWdpblJlcXVlc3RQb2xpY3kuQUxMX1ZJRVdFUl9FWENFU