UNPKG

cdk-nextjs-standalone

Version:

Deploy a NextJS app to AWS using CDK and OpenNext.

406 lines (403 loc) 66.8 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; let responseHeadersPolicy = staticBehaviorOptions?.responseHeadersPolicy; if (!responseHeadersPolicy && !this.props.supressDefaults?.staticResponseHeadersPolicy) { // create default response headers policy if not provided 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: `no-cache, no-store, must-revalidate, max-age=0`, }, ], }, 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; let cachePolicy = serverBehaviorOptions?.cachePolicy; if (!cachePolicy && !this.props.supressDefaults?.serverCachePolicy) { // create default cache policy if not provided 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, }); } let responseHeadersPolicy = serverBehaviorOptions?.responseHeadersPolicy; // create default response headers policy if not provided if (!responseHeadersPolicy && !this.props.supressDefaults?.serverResponseHeadersPolicy) { 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; let cachePolicy = imageBehaviorOptions?.cachePolicy; if (!cachePolicy && !this.props.supressDefaults?.imageCachePolicy) { // add default cache policy if not provided 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, }); } let responseHeadersPolicy = imageBehaviorOptions?.responseHeadersPolicy; if (!responseHeadersPolicy && !this.props.supressDefaults?.imageResponseHeadersPolicy) { // add default response headers policy if not provided 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.3.0" }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTmV4dGpzRGlzdHJpYnV0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL05leHRqc0Rpc3RyaWJ1dGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLDhCQUE4QjtBQUM5Qiw2QkFBNkI7QUFDN0IsNkNBQTBEO0FBQzFELHlEQUF5RDtBQUN6RCwrREFNb0M7QUFDcEMsOERBQThEO0FBRTlELGlEQUF3RTtBQUN4RSxpREFBaUQ7QUFDakQsdURBQTZEO0FBRTdELDJDQUF1QztBQUN2QywyQ0FBa0U7QUF3SWxFOztHQUVHO0FBQ0gsTUFBYSxrQkFBbUIsU0FBUSxzQkFBUztJQTBDL0MsWUFBWSxLQUFnQixFQUFFLEVBQVUsRUFBRSxLQUE4QjtRQUN0RSxLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBcENYLDBCQUFxQixHQUEwRTtZQUNyRyxvQkFBb0IsRUFBRSxVQUFVLENBQUMsb0JBQW9CLENBQUMsaUJBQWlCO1lBQ3ZFLFFBQVEsRUFBRSxJQUFJO1NBQ2YsQ0FBQztRQUVGOzs7V0FHRztRQUNLLGtDQUE2QixHQUErQztZQUNsRixrQkFBa0IsRUFBRSxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUU7WUFDdkMsWUFBWSxFQUFFLEVBQUUsV0FBVyxFQUFFLFVBQVUsQ0FBQyxrQkFBa0IsQ0FBQyxVQUFVLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRTtZQUN4RixjQUFjLEVBQUU7Z0JBQ2QsUUFBUSxFQUFFLEtBQUs7Z0JBQ2YsY0FBYyxFQUFFLFVBQVUsQ0FBQyxxQkFBcUIsQ0FBQywrQkFBK0I7YUFDakY7WUFDRCx1QkFBdUIsRUFBRTtnQkFDdkIsbUJBQW1CLEVBQUUsc0JBQVEsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDO2dCQUN2QyxpQkFBaUIsRUFBRSxJQUFJO2dCQUN2QixRQUFRLEVBQUUsS0FBSztnQkFDZixPQUFPLEVBQUUsSUFBSTthQUNkO1lBQ0QsYUFBYSxFQUFFLEVBQUUsUUFBUSxF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SyxFQUFFO2dCQUFFLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUM7WUFDbkQsT0FBTyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsUUFBUSxJQUFJLFdBQVcsRUFBRSxDQUFDO1FBQ2pELENBQUM7UUFFRCxPQUFPLFdBQVcsQ0FBQztJQUNyQixDQUFDOztBQWpkSCxnREFrZEMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBmcyBmcm9tICdub2RlOmZzJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBEdXJhdGlvbiwgRm4sIFJlbW92YWxQb2xpY3kgfSBmcm9tICdhd3MtY2RrLWxpYic7XG5pbXBvcnQgKiBhcyBjbG91ZGZyb250IGZyb20gJ2F3cy1jZGstbGliL2F3cy1jbG91ZGZyb250JztcbmltcG9ydCB7XG4gIEFkZEJlaGF2aW9yT3B0aW9ucyxcbiAgQmVoYXZpb3JPcHRpb25zLFxuICBDYWNoZVBvbGljeVByb3BzLFxuICBEaXN0cmlidXRpb24sXG4gIFJlc3BvbnNlSGVhZGVyc1BvbGljeSxcbn0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWNsb3VkZnJvbnQnO1xuaW1wb3J0ICogYXMgb3JpZ2lucyBmcm9tICdhd3MtY2RrLWxpYi9hd3MtY2xvdWRmcm9udC1vcmlnaW5zJztcbmltcG9ydCB7IEh0dHBPcmlnaW5Qcm9wcyB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1jbG91ZGZyb250LW9yaWdpbnMnO1xuaW1wb3J0IHsgUG9saWN5U3RhdGVtZW50LCBTZXJ2aWNlUHJpbmNpcGFsIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWlhbSc7XG5pbXBvcnQgKiBhcyBsYW1iZGEgZnJvbSAnYXdzLWNkay1saWIvYXdzLWxhbWJkYSc7XG5pbXBvcnQgeyBSdW50aW1lLCBJbnZva2VNb2RlIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWxhbWJkYSc7XG5pbXBvcnQgKiBhcyBzMyBmcm9tICdhd3MtY2RrLWxpYi9hd3MtczMnO1xuaW1wb3J0IHsgQ29uc3RydWN0IH0gZnJvbSAnY29uc3RydWN0cyc7XG5pbXBvcnQgeyBORVhUSlNfQlVJTERfRElSLCBORVhUSlNfU1RBVElDX0RJUiB9IGZyb20gJy4vY29uc3RhbnRzJztcbmltcG9ydCB7XG4gIE9wdGlvbmFsQ2xvdWRGcm9udEZ1bmN0aW9uUHJvcHMsXG4gIE9wdGlvbmFsRGlzdHJpYnV0aW9uUHJvcHMsXG4gIE9wdGlvbmFsRWRnZUZ1bmN0aW9uUHJvcHMsXG4gIE9wdGlvbmFsUzNPcmlnaW5Qcm9wcyxcbn0gZnJvbSAnLi9nZW5lcmF0ZWQtc3RydWN0cyc7XG5pbXBvcnQgeyBOZXh0anNQcm9wcyB9IGZyb20gJy4vTmV4dGpzJztcbmltcG9ydCB7IE5leHRqc0J1aWxkIH0gZnJvbSAnLi9OZXh0anNCdWlsZCc7XG5pbXBvcnQgeyBOZXh0anNEb21haW4gfSBmcm9tICcuL05leHRqc0RvbWFpbic7XG5cbmV4cG9ydCBpbnRlcmZhY2UgVmlld2VyUmVxdWVzdEZ1bmN0aW9uUHJvcHMgZXh0ZW5kcyBPcHRpb25hbENsb3VkRnJvbnRGdW5jdGlvblByb3BzIHtcbiAgLyoqXG4gICAqIENsb3VkZnJvbnQgZnVuY3Rpb24gY29kZSB0aGF0IHJ1bnMgb24gVklFV0VSX1JFUVVFU1QuXG4gICAqIFRoZSBmb2xsb3dpbmcgY29tbWVudHMgd2lsbCBiZSByZXBsYWNlZCB3aXRoIGNvZGUgc25pcHBldHNcbiAgICogc28geW91IGNhbiBjdXN0b21pemUgdGhpcyBmdW5jdGlvbi5cbiAgICpcbiAgICogSU5KRUNUX0NMT1VERlJPTlRfRlVOQ1RJT05fSE9TVF9IRUFERVI6IEFkZCB0aGUgcmVxdWlyZWQgeC1mb3J3YXJkZWQtaG9zdCBoZWFkZXIuXG4gICAqIElOSkVDVF9DTE9VREZST05UX0ZVTkNUSU9OX0NBQ0hFX0hFQURFUl9LRVk6IEltcHJvdmVzIG9wZW4tbmV4dCBjYWNoZSBrZXkuXG4gICAqXG4gICAqIEBkZWZhdWx0XG4gICAqIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQpIHtcbiAgICogIC8vIElOSkVDVF9DTE9VREZST05UX0ZVTkNUSU9OX0hPU1RfSEVBREVSXG4gICAqICAvLyBJTkpFQ1RfQ0xPVURGUk9OVF9GVU5DVElPTl9DQUNIRV9IRUFERVJfS0VZXG4gICAqIH1cbiAgICovXG4gIHJlYWRvbmx5IGNvZGU/OiBjbG91ZGZyb250LkZ1bmN0aW9uQ29kZTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBOZXh0anNEaXN0cmlidXRpb25EZWZhdWx0cyB7XG4gIC8qKlxuICAgKiBQcmV2ZW50IHRoZSBjcmVhdGlvbiBvZiBhIGRlZmF1bHQgcmVzcG9uc2UgaGVhZGVycyBwb2xpY3kgZm9yIHN0YXRpYyByZXF1ZXN0cy5cbiAgICogSGFzIG5vIGVmZmVjdCBpZiBhIGBzdGF0aWNCZWhhdmlvck9wdGlvbnMucmVzcG9uc2VIZWFkZXJzUG9saWN5YCBpcyBwcm92aWRlZCBpbiB7QGxpbmsgTmV4dGpzRGlzdHJpYnV0aW9uUHJvcHMub3ZlcnJpZGVzfVxuICAgKiBAZGVmYXVsdCBmYWxzZVxuICAgKi9cbiAgcmVhZG9ubHkgc3RhdGljUmVzcG9uc2VIZWFkZXJzUG9saWN5PzogYm9vbGVhbjtcbiAgLyoqXG4gICAqIFByZXZlbnQgdGhlIGNyZWF0aW9uIG9mIGEgZGVmYXVsdCBjYWNoZSBwb2xpY3kgZm9yIHNlcnZlciByZXF1ZXN0cy5cbiAgICogSGFzIG5vIGVmZmVjdCBpZiBhIGBzZXJ2ZXJCZWhhdmlvck9wdGlvbnMuY2FjaGVQb2xpY3lgIGlzIHByb3ZpZGVkIGluIHtAbGluayBOZXh0anNEaXN0cmlidXRpb25Qcm9wcy5vdmVycmlkZXN9XG4gICAqIEBkZWZhdWx0IGZhbHNlXG4gICAqL1xuICByZWFkb25seSBzZXJ2ZXJDYWNoZVBvbGljeT86IGJvb2xlYW47XG4gIC8qKlxuICAgKiBQcmV2ZW50IHRoZSBjcmVhdGlvbiBvZiBhIGRlZmF1bHQgcmVzcG9uc2UgaGVhZGVycyBwb2xpY3kgZm9yIHNlcnZlciByZXF1ZXN0cy5cbiAgICogSGFzIG5vIGVmZmVjdCBpZiBhIGBzZXJ2ZXJCZWhhdmlvck9wdGlvbnMucmVzcG9uc2VIZWFkZXJzUG9saWN5YCBpcyBwcm92aWRlZCBpbiB7QGxpbmsgTmV4dGpzRGlzdHJpYnV0aW9uUHJvcHMub3ZlcnJpZGVzfVxuICAgKiBAZGVmYXVsdCBmYWxzZVxuICAgKi9cbiAgcmVhZG9ubHkgc2VydmVyUmVzcG9uc2VIZWFkZXJzUG9saWN5PzogYm9vbGVhbjtcbiAgLyoqXG4gICAqIFByZXZlbnQgdGhlIGNyZWF0aW9uIG9mIGEgZGVmYXVsdCBjYWNoZSBwb2xpY3kgZm9yIGltYWdlIHJlcXVlc3RzLlxuICAgKiBIYXMgbm8gZWZmZWN0IGlmIGEgYGltYWdlQmVoYXZpb3JPcHRpb25zLmNhY2hlUG9saWN5YCBpcyBwcm92aWRlZCBpbiB7QGxpbmsgTmV4dGpzRGlzdHJpYnV0aW9uUHJvcHMub3ZlcnJpZGVzfVxuICAgKiBAZGVmYXVsdCBmYWxzZVxuICAgKi9cbiAgcmVhZG9ubHkgaW1hZ2VDYWNoZVBvbGljeT86IGJvb2xlYW47XG4gIC8qKlxuICAgKiBQcmV2ZW50IHRoZSBjcmVhdGlvbiBvZiBhIGRlZmF1bHQgcmVzcG9uc2UgaGVhZGVycyBwb2xpY3kgZm9yIGltYWdlIHJlcXVlc3RzLlxuICAgKiBIYXMgbm8gZWZmZWN0IGlmIGEgYGltYWdlQmVoYXZpb3JPcHRpb25zLnJlc3BvbnNlSGVhZGVyc1BvbGljeWAgaXMgcHJvdmlkZWQgaW4ge0BsaW5rIE5leHRqc0Rpc3RyaWJ1dGlvblByb3BzLm92ZXJyaWRlc31cbiAgICogQGRlZmF1bHQgZmFsc2VcbiAgICovXG4gIHJlYWRvbmx5IGltYWdlUmVzcG9uc2VIZWFkZXJzUG9saWN5PzogYm9vbGVhbjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBOZXh0anNEaXN0cmlidXRpb25PdmVycmlkZXMge1xuICByZWFkb25seSB2aWV3ZXJSZXF1ZXN0RnVuY3Rpb25Qcm9wcz86IFZpZXdlclJlcXVlc3RGdW5jdGlvblByb3BzO1xuICByZWFkb25seSBkaXN0cmlidXRpb25Qcm9wcz86IE9wdGlvbmFsRGlzdHJpYnV0aW9uUHJvcHM7XG4gIHJlYWRvbmx5IGVkZ2VGdW5jdGlvblByb3BzPzogT3B0aW9uYWxFZGdlRnVuY3Rpb25Qcm9wcztcbiAgcmVhZG9ubHkgaW1hZ2VCZWhhdmlvck9wdGlvbnM/OiBBZGRCZWhhdmlvck9wdGlvbnM7XG4gIHJlYWRvbmx5IGltYWdlQ2FjaGVQb2xpY3lQcm9wcz86IENhY2hlUG9saWN5UHJvcHM7XG4gIHJlYWRvbmx5IGltYWdlUmVzcG9uc2VIZWFkZXJzUG9saWN5UHJvcHM/OiBjbG91ZGZyb250LlJlc3BvbnNlSGVhZGVyc1BvbGljeVByb3BzO1xuICByZWFkb25seSBpbWFnZUh0dHBPcmlnaW5Qcm9wcz86IEh0dHBPcmlnaW5Qcm9wcztcbiAgcmVhZG9ubHkgc2VydmVyQmVoYXZpb3JPcHRpb25zPzogQWRkQmVoYXZpb3JPcHRpb25zO1xuICByZWFkb25seSBzZXJ2ZXJDYWNoZVBvbGljeVByb3BzPzogQ2FjaGVQb2xpY3lQcm9wcztcbiAgcmVhZG9ubHkgc2VydmVyUmVzcG9uc2VIZWFkZXJzUG9saWN5UHJvcHM/OiBjbG91ZGZyb250LlJlc3BvbnNlSGVhZGVyc1BvbGljeVByb3BzO1xuICByZWFkb25seSBzZXJ2ZXJIdHRwT3JpZ2luUHJvcHM/OiBIdHRwT3JpZ2luUHJvcHM7XG4gIHJlYWRvbmx5IHN0YXRpY0JlaGF2aW9yT3B0aW9ucz86IEFkZEJlaGF2aW9yT3B0aW9ucztcbiAgcmVhZG9ubHkgc3RhdGljUmVzcG9uc2VIZWFkZXJzUG9saWN5UHJvcHM/OiBjbG91ZGZyb250LlJlc3BvbnNlSGVhZGVyc1BvbGljeVByb3BzO1xuICByZWFkb25seSBzM09yaWdpblByb3BzPzogT3B0aW9uYWxTM09yaWdpblByb3BzO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIE5leHRqc0Rpc3RyaWJ1dGlvblByb3BzIHtcbiAgLyoqXG4gICAqIEBzZWUge0BsaW5rIE5leHRqc1Byb3BzLmJhc2VQYXRofVxuICAgKi9cbiAgcmVhZG9ubHkgYmFzZVBhdGg/OiBOZXh0anNQcm9wc1snYmFzZVBhdGgnXTtcbiAgLyoqXG4gICAqIEBzZWUge0BsaW5rIE5leHRqc1Byb3BzLmRpc3RyaWJ1dGlvbn1cbiAgICovXG4gIHJlYWRvbmx5IGRpc3RyaWJ1dGlvbj86IE5leHRqc1Byb3BzWydkaXN0cmlidXRpb24nXTtcbiAgLyoqXG4gICAqIE92ZXJyaWRlIGxhbWJkYSBmdW5jdGlvbiB1cmwgYXV0aCB0eXBlXG4gICAqIEBkZWZhdWx0IFwiTk9ORVwiXG4gICAqL1xuICByZWFkb25seSBmdW5jdGlvblVybEF1dGhUeXBlPzogbGFtYmRhLkZ1bmN0aW9uVXJsQXV0aFR5cGU7XG4gIC8qKlxuICAgKiBMYW1iZGEgZnVuY3Rpb24gdG8gb3B0aW1pemUgaW1hZ2VzLlxuICAgKiBNdXN0IGJlIHByb3ZpZGVkIGlmIHlvdSB3YW50IHRvIHNlcnZlIGR5bmFtaWMgcmVxdWVzdHMuXG4gICAqL1xuICByZWFkb25seSBpbWFnZU9wdEZ1bmN0aW9uOiBsYW1iZGEuSUZ1bmN0aW9uO1xuICAvKipcbiAgICogQHNlZSB7QGxpbmsgTmV4dGpzQnVpbGR9XG4gICAqL1xuICByZWFkb25seSBuZXh0QnVpbGQ6IE5leHRqc0J1aWxkO1xuICAvKipcbiAgICogQHNlZSB7QGxpbmsgTmV4dGpzRG9tYWlufVxuICAgKi9cbiAgcmVhZG9ubHkgbmV4dERvbWFpbj86IE5leHRqc0RvbWFpbjtcbiAgLyoqXG4gICAqIEBzZWUge0BsaW5rIE5leHRqc1Byb3BzLm5leHRqc1BhdGh9XG4gICAqL1xuICByZWFkb25seSBuZXh0anNQYXRoOiBOZXh0anNQcm9wc1snbmV4dGpzUGF0aCddO1xuICAvKipcbiAgICogT3ZlcnJpZGUgcHJvcHMgZm9yIGV2ZXJ5IGNvbnN0cnVjdC5cbiAgICovXG4gIHJlYWRvbmx5IG92ZXJyaWRlcz86IE5leHRqc0Rpc3RyaWJ1dGlvbk92ZXJyaWRlcztcbiAgLyoqXG4gICAqIExhbWJkYSBmdW5jdGlvbiB0byByb3V0ZSBhbGwgbm9uLXN0YXRpYyByZXF1ZXN0cyB0by5cbiAgICogTXVzdCBiZSBwcm92aWRlZCBpZiB5b3Ugd2FudCB0byBzZXJ2ZSBkeW5hbWljIHJlcXVlc3RzLlxuICAgKi9cbiAgcmVhZG9ubHkgc2VydmVyRnVuY3Rpb246IGxhbWJkYS5JRnVuY3Rpb247XG4gIC8qKlxuICAgKiBCdWNrZXQgY29udGFpbmluZyBzdGF0aWMgYXNzZXRzLlxuICAgKiBNdXN0IGJlIHByb3ZpZGVkIGlmIHlvdSB3YW50IHRvIHNlcnZlIHN0YXRpYyBmaWxlcy5cbiAgICovXG4gIHJlYWRvbmx5IHN0YXRpY0Fzc2V0c0J1Y2tldDogczMuSUJ1Y2tldDtcbiAgLyoqXG4gICAqIEBzZWUge0BsaW5rIE5leHRqc1Byb3BzLnN0cmVhbWluZ31cbiAgICovXG4gIHJlYWRvbmx5IHN0cmVhbWluZz86IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIFN1cHJlc3MgdGhlIGNyZWF0aW9uIG9mIGRlZmF1bHQgcG9saWNpZXMgaWZcbiAgICogbm9uZSBhcmUgcHJvdmlkZWQgYnkgeW91XG4gICAqL1xuICByZWFkb25seSBzdXByZXNzRGVmYXVsdHM/OiBOZXh0anNEaXN0cmlidXRpb25EZWZhdWx0cztcbn1cblxuLyoqXG4gKiBDcmVhdGUgYSBDbG91ZEZyb250IGRpc3RyaWJ1dGlvbiB0byBzZXJ2ZSBhIE5leHQuanMgYXBwbGljYXRpb24uXG4gKi9cbmV4cG9ydCBjbGFzcyBOZXh0anNEaXN0cmlidXRpb24gZXh0ZW5kcyBDb25zdHJ1Y3Qge1xuICBwcml2YXRlIHByb3BzOiBOZXh0anNEaXN0cmlidXRpb25Qcm9wcztcbiAgLyoqXG4gICAqIFRoZSBpbnRlcm5hbGx5IGNyZWF0ZWQgQ2xvdWRGcm9udCBgRGlzdHJpYnV0aW9uYCBpbnN0YW5jZS5cbiAgICovXG4gIHB1YmxpYyBkaXN0cmlidXRpb246IERpc3RyaWJ1dGlvbjtcblxuICBwcml2YXRlIGNvbW1vbkJlaGF2aW9yT3B0aW9uczogUGljazxjbG91ZGZyb250LkJlaGF2aW9yT3B0aW9ucywgJ3ZpZXdlclByb3RvY29sUG9saWN5JyB8ICdjb21wcmVzcyc+ID0ge1xuICAgIHZpZXdlclByb3RvY29sUG9saWN5OiBjbG91ZGZyb250LlZpZXdlclByb3RvY29sUG9saWN5LlJFRElSRUNUX1RPX0hUVFBTLFxuICAgIGNvbXByZXNzOiB0cnVlLFxuICB9O1xuXG4gIC8qKlxuICAgKiBDb21tb24gc2VjdXJpdHkgaGVhZGVycyBhcHBsaWVkIGJ5IGRlZmF1bHQgdG8gYWxsIG9yaWdpbnNcbiAgICogQHNlZSBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vQW1hem9uQ2xvdWRGcm9udC9sYXRlc3QvRGV2ZWxvcGVyR3VpZGUvdXNpbmctbWFuYWdlZC1yZXNwb25zZS1oZWFkZXJzLXBvbGljaWVzLmh0bWwjbWFuYWdlZC1yZXNwb25zZS1oZWFkZXJzLXBvbGljaWVzLXNlY3VyaXR5XG4gICAqL1xuICBwcml2YXRlIGNvbW1vblNlY3VyaXR5SGVhZGVyc0JlaGF2aW9yOiBjbG91ZGZyb250LlJlc3BvbnNlU2VjdXJpdHlIZWFkZXJzQmVoYXZpb3IgPSB7XG4gICAgY29udGVudFR5cGVPcHRpb25zOiB7IG92ZXJyaWRlOiBmYWxzZSB9LFxuICAgIGZyYW1lT3B0aW9uczogeyBmcmFtZU9wdGlvbjogY2xvdWRmcm9udC5IZWFkZXJzRnJhbWVPcHRpb24uU0FNRU9SSUdJTiwgb3ZlcnJpZGU6IGZhbHNlIH0sXG4gICAgcmVmZXJyZXJQb2xpY3k6IHtcbiAgICAgIG92ZXJyaWRlOiBmYWxzZSxcbiAgICAgIHJlZmVycmVyUG9saWN5OiBjbG91ZGZyb250LkhlYWRlcnNSZWZlcnJlclBvbGljeS5TVFJJQ1RfT1JJR0lOX1dIRU5fQ1JPU1NfT1JJR0lOLFxuICAgIH0sXG4gICAgc3RyaWN0VHJhbnNwb3J0U2VjdXJpdHk6IHtcbiAgICAgIGFjY2Vzc0NvbnRyb2xNYXhBZ2U6IER1cmF0aW9uLmRheXMoMzY1KSxcbiAgICAgIGluY2x1ZGVTdWJkb21haW5zOiB0cnVlLFxuICAgICAgb3ZlcnJpZGU6IGZhbHNlLFxuICAgICAgcHJlbG9hZDogdHJ1ZSxcbiAgICB9LFxuICAgIHhzc1Byb3RlY3Rpb246IHsgb3ZlcnJpZGU6IGZhbHNlLCBwcm90ZWN0aW9uOiB0cnVlLCBtb2RlQmxvY2s6IHRydWUgfSxcbiAgfTtcblxuICBwcml2YXRlIHMzT3JpZ2luOiBvcmlnaW5zLlMzT3JpZ2luO1xuXG4gIHByaXZhdGUgc3RhdGljQmVoYXZpb3JPcHRpb25zOiBjbG91ZGZyb250LkJlaGF2aW9yT3B0aW9ucztcblxuICBwcml2YXRlIGVkZ2VMYW1iZGFzOiBjbG91ZGZyb250LkVkZ2VMYW1iZGFbXSA9IFtdO1xuXG4gIHByaXZhdGUgc2VydmVyQmVoYXZpb3JPcHRpb25zOiBjbG91ZGZyb250LkJlaGF2aW9yT3B0aW9ucztcblxuICBwcml2YXRlIGltYWdlQmVoYXZpb3JPcHRpb25zOiBjbG91ZGZyb250LkJlaGF2aW9yT3B0aW9ucztcblxuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wczogTmV4dGpzRGlzdHJpYnV0aW9uUHJvcHMpIHtcbiAgICBzdXBlcihzY29wZSwgaWQpO1xuXG4gICAgdGhpcy5wcm9wcyA9IHByb3BzO1xuXG4gICAgLy8gQ3JlYXRlIEJlaGF2aW9yc1xuICAgIHRoaXMuczNPcmlnaW4gPSBuZXcgb3JpZ2lucy5TM09yaWdpbih0aGlzLnByb3BzLnN0YXRpY0Fzc2V0c0J1Y2tldCwgdGhpcy5wcm9wcy5vdmVycmlkZXM/LnMzT3JpZ2luUHJvcHMpO1xuICAgIHRoaXMuc3RhdGljQmVoYXZpb3JPcHRpb25zID0gdGhpcy5jcmVhdGVTdGF0aWNCZWhhdmlvck9wdGlvbnMoKTtcbiAgICBpZiAodGhpcy5pc0ZuVXJsSWFtQXV0aCkge1xuICAgICAgdGhpcy5lZGdlTGFtYmRhcy5wdXNoKHRoaXMuY3JlYXRlRWRnZUxhbWJkYSgpKTtcbiAgICB9XG4gICAgdGhpcy5zZXJ2ZXJCZWhhdmlvck9wdGlvbnMgPSB0aGlzLmNyZWF0ZVNlcnZlckJlaGF2aW9yT3B0aW9ucygpO1xuICAgIHRoaXMuaW1hZ2VCZWhhdmlvck9wdGlvbnMgPSB0aGlzLmNyZWF0ZUltYWdlQmVoYXZpb3JPcHRpb25zKCk7XG5cbiAgICAvLyBDcmVhdGUgQ2xvdWRGcm9udCBEaXN0cmlidXRpb25cbiAgICB0aGlzLmRpc3RyaWJ1dGlvbiA9IHRoaXMuZ2V0Q2xvdWRGcm9udERpc3RyaWJ1dGlvbigpO1xuICAgIHRoaXMuYWRkU3RhdGljQmVoYXZpb3JzVG9EaXN0cmlidXRpb24oKTtcbiAgICB0aGlzLmFkZFJvb3RQYXRoQmVoYXZpb3IoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgQ2xvdWRGcm9udCBVUkwgb2YgdGhlIHdlYnNpdGUuXG4gICAqL1xuICBwdWJsaWMgZ2V0IHVybCgpOiBzdHJpbmcge1xuICAgIHJldHVybiBgaHR0cHM6Ly8ke3RoaXMuZGlzdHJpYnV0aW9uLmRpc3RyaWJ1dGlvbkRvbWFpbk5hbWV9YDtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgSUQgb2YgdGhlIGludGVybmFsbHkgY3JlYXRlZCBDbG91ZEZyb250IERpc3RyaWJ1dGlvbi5cbiAgICovXG4gIHB1YmxpYyBnZXQgZGlzdHJpYnV0aW9uSWQoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5kaXN0cmlidXRpb24uZGlzdHJpYnV0aW9uSWQ7XG4gIH1cblxuICAvKipcbiAgICogVGhlIGRvbWFpbiBuYW1lIG9mIHRoZSBpbnRlcm5hbGx5IGNyZWF0ZWQgQ2xvdWRGcm9udCBEaXN0cmlidXRpb24uXG4gICAqL1xuICBwdWJsaWMgZ2V0IGRpc3RyaWJ1dGlvbkRvbWFpbigpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLmRpc3RyaWJ1dGlvbi5kaXN0cmlidXRpb25Eb21haW5OYW1lO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXQgaXNGblVybElhbUF1dGgoKSB7XG4gICAgcmV0dXJuIHRoaXMucHJvcHMuZnVuY3Rpb25VcmxBdXRoVHlwZSA9PT0gbGFtYmRhLkZ1bmN0aW9uVXJsQXV0aFR5cGUuQVdTX0lBTTtcbiAgfVxuXG4gIHByaXZhdGUgY3JlYXRlU3RhdGljQmVoYXZpb3JPcHRpb25zKCk6IEJlaGF2aW9yT3B0aW9ucyB7XG4gICAgY29uc3Qgc3RhdGljQmVoYXZpb3JPcHRpb25zID0gdGhpcy5wcm9wcy5vdmVycmlkZXM/LnN0YXRpY0JlaGF2aW9yT3B0aW9ucztcblxuICAgIGxldCByZXNwb25zZUhlYWRlcnNQb2xpY3kgPSBzdGF0aWNCZWhhdmlvck9wdGlvbnM/LnJlc3BvbnNlSGVhZGVyc1BvbGljeTtcblxuICAgIGlmICghcmVzcG9uc2VIZWFkZXJzUG9saWN5ICYmICF0aGlzLnByb3BzLnN1cHJlc3NEZWZhdWx0cz8uc3RhdGljUmVzcG9uc2VIZWFkZXJzUG9saWN5KSB7XG4gICAgICAvLyBjcmVhdGUgZGVmYXVsdCByZXNwb25zZSBoZWFkZXJzIHBvbGljeSBpZiBub3QgcHJvdmlkZWRcbiAgICAgIHJlc3BvbnNlSGVhZGVyc1BvbGljeSA9IG5ldyBSZXNwb25zZUhlYWRlcnNQb2xpY3kodGhpcywgJ1N0YXRpY1Jlc3BvbnNlSGVhZGVyc1BvbGljeScsIHtcbiAgICAgICAgLy8gYWRkIGRlZmF1bHQgaGVhZGVyIGZvciBzdGF0aWMgYXNzZXRzXG4gICAgICAgIGN1c3RvbUhlYWRlcnNCZWhhdmlvcjoge1xuICAgICAgICAgIGN1c3RvbUhlYWRlcnM6IFtcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgaGVhZGVyOiAnY2FjaGUtY29udHJvbCcsXG4gICAgICAgICAgICAgIG92ZXJyaWRlOiBmYWxzZSxcbiAgICAgICAgICAgICAgLy8gTUROIENhY2hlLUNvbnRyb2wgVXNlIENhc2U6IENhY2hpbmcgc3RhdGljIGFzc2V0cyB3aXRoIFwiY2FjaGUgYnVzdGluZ1wiXG4gICAgICAgICAgICAgIC8vIEBzZWU6IGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0hUVFAvSGVhZGVycy9DYWNoZS1Db250cm9sI2NhY2hpbmdfc3RhdGljX2Fzc2V0c193aXRoX2NhY2hlX2J1c3RpbmdcbiAgICAgICAgICAgICAgdmFsdWU6IGBuby1jYWNoZSwgbm8tc3RvcmUsIG11c3QtcmV2YWxpZGF0ZSwgbWF4LWFnZT0wYCxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgXSxcbiAgICAgICAgfSxcbiAgICAgICAgc2VjdXJpdHlIZWFkZXJzQmVoYXZpb3I6IHRoaXMuY29tbW9uU2VjdXJpdHlIZWFkZXJzQmVoYXZpb3IsXG4gICAgICAgIGNvbW1lbnQ6ICdOZXh0anMgU3RhdGljIFJlc3BvbnNlIEhlYWRlcnMgUG9saWN5JyxcbiAgICAgICAgLi4udGhpcy5wcm9wcy5vdmVycmlkZXM/LnN0YXRpY1Jlc3BvbnNlSGVhZGVyc1BvbGljeVByb3BzLFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIC4uLnRoaXMuY29tbW9uQmVoYXZpb3JPcHRpb25zLFxuICAgICAgb3JpZ2luOiB0aGlzLnMzT3JpZ2luLFxuICAgICAgYWxsb3dlZE1ldGhvZHM6IGNsb3VkZnJvbnQuQWxsb3dlZE1ldGhvZHMuQUxMT1dfR0VUX0hFQURfT1BUSU9OUyxcbiAgICAgIGNhY2hlZE1ldGhvZHM6IGNsb3VkZnJvbnQuQ2FjaGVkTWV0aG9kcy5DQUNIRV9HRVRfSEVBRF9PUFRJT05TLFxuICAgICAgY2FjaGVQb2xpY3k6IGNsb3VkZnJvbnQuQ2FjaGVQb2xpY3kuQ0FDSElOR19PUFRJTUlaRUQsXG4gICAgICByZXNwb25zZUhlYWRlcnNQb2xpY3ksXG4gICAgICAuLi5zdGF0aWNCZWhhdmlvck9wdGlvbnMsXG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0IGZuVXJsQXV0aFR5cGUoKTogbGFtYmRhLkZ1bmN0aW9uVXJsQXV0aFR5cGUge1xuICAgIHJldHVybiB0aGlzLnByb3BzLmZ1bmN0aW9uVXJsQXV0aFR5cGUgfHwgbGFtYmRhLkZ1bmN0aW9uVXJsQXV0aFR5cGUuTk9ORTtcbiAgfVxuXG4gIC8qKlxuICAgKiBPbmNlIENsb3VkRnJvbnQgT0FDIGlzIHJlbGVhc2VkLCByZW1vdmUgdGhpcyB0byByZWR1Y2UgbGF0ZW5jeS5cbiAgICovXG4gIHByaXZhdGUgY3JlYXRlRWRnZUxhbWJkYSgpOiBjbG91ZGZyb250LkVkZ2VMYW1iZGEge1xuICAgIGNvbnN0IHNpZ25GblVybERpciA9IHBhdGgucmVzb2x2ZShfX2Rpcm5hbWUsICcuLicsICdhc3NldHMnLCAnbGFtYmRhcycsICdzaWduLWZuLXVybCcpO1xuICAgIGNvbnN0IG9yaWdpblJlcXVlc3RFZGdlRm4gPSBuZXcgY2xvdWRmcm9udC5leHBlcmltZW50YWwuRWRnZUZ1bmN0aW9uKHRoaXMsICdFZGdlRm4nLCB7XG4gICAgICBydW50aW1lOiBSdW50aW1lLk5PREVKU18yMF9YLFxuICAgICAgaGFuZGxlcjogJ2luZGV4LmhhbmRsZXInLFxuICAgICAgY29kZTogbGFtYmRhLkNvZGUuZnJvbUFzc2V0KHNpZ25GblVybERpciksXG4gICAgICBjdXJyZW50VmVyc2lvbk9wdGlvbnM6IHtcbiAgICAgICAgcmVtb3ZhbFBvbGljeTogUmVtb3ZhbFBvbGljeS5ERVNUUk9ZLCAvLyBkZXN0cm95IG9sZCB2ZXJzaW9uc1xuICAgICAgICByZXRyeUF0dGVtcHRzOiAxLCAvLyBhc3luYyByZXRyeSBhdHRlbXB0c1xuICAgICAgfSxcbiAgICAgIC4uLnRoaXMucHJvcHMub3ZlcnJpZGVzPy5lZGdlRnVuY3Rpb25Qcm9wcyxcbiAgICB9KTtcbiAgICBvcmlnaW5SZXF1ZXN0RWRnZUZuLmN1cnJlbnRWZXJzaW9uLmdyYW50SW52b2tlKG5ldyBTZXJ2aWNlUHJpbmNpcGFsKCdlZGdlbGFtYmRhLmFtYXpvbmF3cy5jb20nKSk7XG4gICAgb3JpZ2luUmVxdWVzdEVkZ2VGbi5jdXJyZW50VmVyc2lvbi5ncmFudEludm9rZShuZXcgU2VydmljZVByaW5jaXBhbCgnbGFtYmRhLmFtYXpvbmF3cy5jb20nKSk7XG4gICAgb3JpZ2luUmVxdWVzdEVkZ2VGbi5hZGRUb1JvbGVQb2xpY3koXG4gICAgICBuZXcgUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgYWN0aW9uczogWydsYW1iZGE6SW52b2tlRnVuY3Rpb25VcmwnXSxcbiAgICAgICAgcmVzb3VyY2VzOiBbdGhpcy5wcm9wcy5zZXJ2ZXJGdW5jdGlvbi5mdW5jdGlvbkFybiwgdGhpcy5