cdk-nextjs-standalone
Version:
Deploy a NextJS app to AWS using CDK and OpenNext.
396 lines (393 loc) • 62.7 kB
JavaScript
"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