open-next-cdk
Version:
Deploy a NextJS app using OpenNext packaging to serverless AWS using CDK
509 lines • 81.5 kB
JavaScript
"use strict";
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.NextjsDistribution = exports.CONFIG_ENV_JSON_PATH = void 0;
const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
const os = require("os");
const path = require("path");
const path_1 = require("path");
const aws_cdk_lib_1 = require("aws-cdk-lib");
const acm = require("aws-cdk-lib/aws-certificatemanager");
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 route53 = require("aws-cdk-lib/aws-route53");
const route53Targets = require("aws-cdk-lib/aws-route53-targets");
const constructs_1 = require("constructs");
const fs = require("fs-extra");
const BundleFunction_1 = require("./BundleFunction");
const constants_1 = require("./constants");
const NextjsBase_1 = require("./NextjsBase");
const utils_1 = require("./utils");
const website_redirect_1 = require("./website-redirect");
// contains server-side resolved environment vars in config bucket
exports.CONFIG_ENV_JSON_PATH = 'next-env.json';
/**
* Create a CloudFront distribution to serve a Next.js application.
*/
class NextjsDistribution extends constructs_1.Construct {
constructor(scope, id, props) {
super(scope, id);
// get dir to store temp build files in
this.tempBuildDir = props.tempBuildDir
? path.resolve(path.join(props.tempBuildDir, `nextjs-cdk-build-${this.node.id}-${this.node.addr.substring(0, 4)}`))
: fs.mkdtempSync(path.join(os.tmpdir(), 'nextjs-cdk-build-'));
// save props
this.props = { ...props, tempBuildDir: this.tempBuildDir };
// Create Custom Domain
this.validateCustomDomainSettings();
this.hostedZone = this.lookupHostedZone();
this.certificate = this.createCertificate();
// Create CloudFront
this.distribution = this.props.isPlaceholder
? this.createCloudFrontDistributionForStub()
: this.createCloudFrontDistribution();
// Connect Custom Domain to CloudFront Distribution
this.createRoute53Records();
}
/**
* The CloudFront URL of the website.
*/
get url() {
return `https://${this.distribution.distributionDomainName}`;
}
get customDomainName() {
const { customDomain } = this.props;
if (!customDomain) {
return;
}
if (typeof customDomain === 'string') {
return customDomain;
}
return customDomain.domainName;
}
/**
* If the custom domain is enabled, this is the URL of the website with the
* custom domain.
*/
get customDomainUrl() {
const customDomainName = this.customDomainName;
return customDomainName ? `https://${customDomainName}` : undefined;
}
/**
* 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;
}
/////////////////////
// CloudFront Distribution
/////////////////////
createCloudFrontDistribution() {
const { cdk: cdkProps, cachePolicies, originRequestPolicies } = this.props;
const cfDistributionProps = cdkProps?.distribution;
// build domainNames
const domainNames = this.buildDistributionDomainNames();
// S3 origin
const s3Origin = new origins.S3Origin(this.props.staticAssetsBucket);
const viewerProtocolPolicy = cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS;
// handle placeholder
if (this.props.isPlaceholder) {
return new cloudfront.Distribution(this, 'Distribution', {
defaultRootObject: 'index.html',
errorResponses: NextjsBase_1.buildErrorResponsesForRedirectToIndex('index.html'),
domainNames,
certificate: this.certificate,
defaultBehavior: {
origin: s3Origin,
viewerProtocolPolicy,
},
});
}
// cache policies
const staticCachePolicy = cachePolicies?.staticCachePolicy ?? this.createCloudFrontStaticCachePolicy();
const imageCachePolicy = cachePolicies?.imageCachePolicy ?? this.createCloudFrontImageCachePolicy();
// origin request policies
const lambdaOriginRequestPolicy = originRequestPolicies?.lambdaOriginRequestPolicy ?? this.createLambdaOriginRequestPolicy();
const fnUrlAuthType = this.props.functionUrlAuthType || lambda.FunctionUrlAuthType.NONE;
// main server function origin (lambda URL HTTP origin)
const fnUrl = this.props.serverFunction.addFunctionUrl({ authType: fnUrlAuthType });
const serverFunctionOrigin = new origins.HttpOrigin(aws_cdk_lib_1.Fn.parseDomainName(fnUrl.url));
// Image Optimization
const imageOptFnUrl = this.props.imageOptFunction.addFunctionUrl({ authType: fnUrlAuthType });
const imageOptFunctionOrigin = new origins.HttpOrigin(aws_cdk_lib_1.Fn.parseDomainName(imageOptFnUrl.url));
const imageOptORP = originRequestPolicies?.imageOptimizationOriginRequestPolicy ?? this.createImageOptimizationOriginRequestPolicy();
// lambda behavior edge function
const lambdaOriginRequestEdgeFn = this.buildLambdaOriginRequestEdgeFunction();
if (this.isFnUrlIamAuth) {
lambdaOriginRequestEdgeFn.addToRolePolicy(new aws_iam_1.PolicyStatement({
actions: ['lambda:InvokeFunctionUrl'],
resources: [this.props.serverFunction.functionArn, this.props.imageOptFunction.functionArn],
}));
}
const lambdaOriginRequestEdgeFnVersion = lambda.Version.fromVersionArn(this, 'Version', lambdaOriginRequestEdgeFn.currentVersion.functionArn);
const lambdaOriginEdgeFns = [
{
eventType: cloudfront.LambdaEdgeEventType.ORIGIN_REQUEST,
functionVersion: lambdaOriginRequestEdgeFnVersion,
includeBody: this.isFnUrlIamAuth,
},
];
// default handler for requests that don't match any other path:
// - try lambda handler first (/some-page, etc...)
// - if 403, fall back to S3
// - if 404, fall back to lambda handler
// - if 503, fall back to lambda handler
const fallbackOriginGroup = new origins.OriginGroup({
primaryOrigin: serverFunctionOrigin,
fallbackOrigin: s3Origin,
fallbackStatusCodes: [403, 404, 503],
});
const lambdaCachePolicy = cachePolicies?.lambdaCachePolicy ?? this.createCloudFrontLambdaCachePolicy();
// requests for static objects
const defaultStaticMaxAge = cachePolicies?.staticClientMaxAgeDefault?.toSeconds() || constants_1.DEFAULT_STATIC_MAX_AGE;
const staticResponseHeadersPolicy = new aws_cloudfront_1.ResponseHeadersPolicy(this, 'StaticResponseHeadersPolicy', {
// add default header for static assets
customHeadersBehavior: {
customHeaders: [
{
header: 'cache-control',
override: false,
// by default tell browser to cache static files for this long
// this is separate from the origin cache policy
value: `public,max-age=${defaultStaticMaxAge},immutable`,
},
],
},
});
const staticBehavior = {
viewerProtocolPolicy,
origin: s3Origin,
allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD_OPTIONS,
compress: true,
cachePolicy: staticCachePolicy,
responseHeadersPolicy: staticResponseHeadersPolicy,
};
// requests going to lambda (api, etc)
const lambdaBehavior = {
viewerProtocolPolicy,
origin: serverFunctionOrigin,
allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,
// cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD_OPTIONS, // this should be configurable
originRequestPolicy: lambdaOriginRequestPolicy,
compress: true,
cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED,
edgeLambdas: lambdaOriginEdgeFns,
};
const imageBehavior = {
viewerProtocolPolicy,
origin: imageOptFunctionOrigin,
allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD_OPTIONS,
compress: true,
cachePolicy: imageCachePolicy,
originRequestPolicy: imageOptORP,
edgeLambdas: this.isFnUrlIamAuth ? lambdaOriginEdgeFns : [],
};
// requests to fallback origin group (default behavior)
// used for S3 and lambda. would prefer to forward all headers to lambda but need to strip out host
// TODO: try to do this with headers whitelist or edge lambda
const fallbackOriginRequestPolicy = originRequestPolicies?.fallbackOriginRequestPolicy ?? this.createFallbackOriginRequestPolicy();
// 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');
return new cloudfront.Distribution(this, 'Distribution', {
// defaultRootObject: "index.html",
defaultRootObject: '',
// Override props.
...cfDistributionProps,
// these values can NOT be overwritten by cfDistributionProps
domainNames,
certificate: this.certificate,
defaultBehavior: {
origin: fallbackOriginGroup,
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
compress: true,
// what goes here? static or lambda?
cachePolicy: lambdaCachePolicy,
originRequestPolicy: fallbackOriginRequestPolicy,
edgeLambdas: lambdaOriginEdgeFns,
},
additionalBehaviors: {
// is index.html static or dynamic?
...(hasIndexHtml ? {} : { '/': lambdaBehavior }),
// known dynamic routes
'api/*': lambdaBehavior,
'_next/data/*': lambdaBehavior,
// dynamic images go to lambda
'_next/image*': imageBehavior,
// known static routes
// it would be nice to create routes for all the static files we know of
// but we run into the limit of CacheBehaviors per distribution
'_next/*': staticBehavior,
},
});
}
createCloudFrontStaticCachePolicy() {
return new cloudfront.CachePolicy(this, 'StaticsCache', NextjsDistribution.staticCachePolicyProps);
}
createCloudFrontImageCachePolicy() {
return new cloudfront.CachePolicy(this, 'ImageCache', NextjsDistribution.imageCachePolicyProps);
}
createLambdaOriginRequestPolicy() {
return new cloudfront.OriginRequestPolicy(this, 'LambdaOriginPolicy', NextjsDistribution.lambdaOriginRequestPolicyProps);
}
createFallbackOriginRequestPolicy() {
return new cloudfront.OriginRequestPolicy(this, 'FallbackOriginRequestPolicy', NextjsDistribution.fallbackOriginRequestPolicyProps);
}
createImageOptimizationOriginRequestPolicy() {
return new cloudfront.OriginRequestPolicy(this, 'ImageOptPolicy', NextjsDistribution.imageOptimizationOriginRequestPolicyProps);
}
createCloudFrontLambdaCachePolicy() {
return new cloudfront.CachePolicy(this, 'LambdaCache', NextjsDistribution.lambdaCachePolicyProps);
}
createCloudFrontDistributionForStub() {
return new cloudfront.Distribution(this, 'Distribution', {
defaultRootObject: 'index.html',
errorResponses: NextjsBase_1.buildErrorResponsesForRedirectToIndex('index.html'),
domainNames: this.buildDistributionDomainNames(),
certificate: this.certificate,
defaultBehavior: {
origin: new origins.S3Origin(this.props.staticAssetsBucket),
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
},
...this.props.cdk?.distribution,
});
}
buildDistributionDomainNames() {
const customDomain = typeof this.props.customDomain === 'string' ? this.props.customDomain : this.props.customDomain?.domainName;
const alternateNames = typeof this.props.customDomain === 'string' ? [] : this.props.customDomain?.alternateNames || [];
return customDomain ? [customDomain, ...alternateNames] : [];
}
/**
* Create an edge function to handle requests to the lambda server handler origin.
* It overrides the host header in the request to be the lambda URL's host.
* It's needed because we forward all headers to the origin, but the origin is itself an
* HTTP server so it needs the host header to be the address of the lambda and not
* the distribution.
*
*/
buildLambdaOriginRequestEdgeFunction() {
const app = aws_cdk_lib_1.App.of(this);
// bundle the edge function
const fileName = this.props.functionUrlAuthType === lambda.FunctionUrlAuthType.NONE
? 'LambdaOriginRequest'
: 'LambdaOriginRequestIamAuth';
const inputPath = path.join(__dirname, '..', 'assets', 'lambda@edge', fileName);
const outputPath = path.join(this.tempBuildDir, 'lambda@edge', 'LambdaOriginRequest.js');
BundleFunction_1.bundleFunction({
inputPath,
outputPath,
bundleOptions: {
bundle: true,
external: ['aws-sdk', 'url'],
minify: true,
target: 'node18',
platform: 'node',
},
});
const fn = new cloudfront.experimental.EdgeFunction(this, 'DefaultOriginRequestEdgeFn', {
runtime: aws_lambda_1.Runtime.NODEJS_18_X,
handler: 'LambdaOriginRequest.handler',
code: lambda.Code.fromAsset(path_1.dirname(outputPath)),
currentVersionOptions: {
removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY,
retryAttempts: 1,
},
stackId: `${this.props.stackPrefix ?? 'Nextjs'}-${this.props.stageName || app.stageName || 'default'}-EdgeFn-` +
this.node.addr.substring(0, 5),
});
fn.currentVersion.grantInvoke(new aws_iam_1.ServicePrincipal('edgelambda.amazonaws.com'));
fn.currentVersion.grantInvoke(new aws_iam_1.ServicePrincipal('lambda.amazonaws.com'));
return fn;
}
/////////////////////
// Custom Domain
/////////////////////
validateCustomDomainSettings() {
const { customDomain } = this.props;
if (!customDomain) {
return;
}
if (typeof customDomain === 'string') {
return;
}
if (customDomain.isExternalDomain === true) {
if (!customDomain.certificate) {
throw new Error('A valid certificate is required when "isExternalDomain" is set to "true".');
}
if (customDomain.domainAlias) {
throw new Error('Domain alias is only supported for domains hosted on Amazon Route 53. Do not set the "customDomain.domainAlias" when "isExternalDomain" is enabled.');
}
if (customDomain.hostedZone) {
throw new Error('Hosted zones can only be configured for domains hosted on Amazon Route 53. Do not set the "customDomain.hostedZone" when "isExternalDomain" is enabled.');
}
}
}
lookupHostedZone() {
const { customDomain } = this.props;
// Skip if customDomain is not configured
if (!customDomain) {
return;
}
let hostedZone;
if (typeof customDomain === 'string') {
hostedZone = route53.HostedZone.fromLookup(this, 'HostedZone', {
domainName: customDomain,
});
}
else if (typeof customDomain.hostedZone === 'string') {
hostedZone = route53.HostedZone.fromLookup(this, 'HostedZone', {
domainName: customDomain.hostedZone,
});
}
else if (customDomain.hostedZone) {
hostedZone = customDomain.hostedZone;
}
else if (typeof customDomain.domainName === 'string') {
// Skip if domain is not a Route53 domain
if (customDomain.isExternalDomain === true) {
return;
}
hostedZone = route53.HostedZone.fromLookup(this, 'HostedZone', {
domainName: customDomain.domainName,
});
}
else {
hostedZone = customDomain.hostedZone;
}
return hostedZone;
}
createCertificate() {
const { customDomain } = this.props;
if (!customDomain) {
return;
}
let acmCertificate;
// HostedZone is set for Route 53 domains
if (this.hostedZone) {
if (typeof customDomain === 'string') {
acmCertificate = new acm.Certificate(this, 'Certificate', {
domainName: customDomain,
validation: acm.CertificateValidation.fromDns(this.hostedZone),
});
}
else if (customDomain.certificate) {
acmCertificate = customDomain.certificate;
}
else {
acmCertificate = new acm.Certificate(this, 'Certificate', {
domainName: customDomain.domainName,
subjectAlternativeNames: customDomain.alternateNames,
validation: acm.CertificateValidation.fromDns(this.hostedZone),
});
}
}
// HostedZone is NOT set for non-Route 53 domains
else {
if (typeof customDomain !== 'string') {
acmCertificate = customDomain.certificate;
}
}
return acmCertificate;
}
createRoute53Records() {
const { customDomain } = this.props;
if (!customDomain || !this.hostedZone) {
return;
}
let recordName;
let domainAlias;
if (typeof customDomain === 'string') {
recordName = customDomain;
}
else {
recordName = customDomain.domainName;
domainAlias = customDomain.domainAlias;
}
// Create DNS record
const recordProps = {
recordName: utils_1.domainAddTrailingDot(recordName),
zone: this.hostedZone,
target: route53.RecordTarget.fromAlias(new route53Targets.CloudFrontTarget(this.distribution)),
};
new route53.ARecord(this, 'AliasRecord', recordProps);
new route53.AaaaRecord(this, 'AliasRecordAAAA', recordProps);
// Create Alias redirect record
if (domainAlias) {
new website_redirect_1.HttpsRedirectPatched(this, 'Redirect', {
zone: this.hostedZone,
recordNames: [domainAlias],
targetDomain: recordName,
});
}
}
}
exports.NextjsDistribution = NextjsDistribution;
_a = JSII_RTTI_SYMBOL_1;
NextjsDistribution[_a] = { fqn: "open-next-cdk.NextjsDistribution", version: "0.0.10" };
/**
* The default CloudFront cache policy properties for static pages.
*/
NextjsDistribution.staticCachePolicyProps = {
queryStringBehavior: cloudfront.CacheQueryStringBehavior.none(),
headerBehavior: cloudfront.CacheHeaderBehavior.none(),
cookieBehavior: cloudfront.CacheCookieBehavior.none(),
defaultTtl: aws_cdk_lib_1.Duration.days(30),
maxTtl: aws_cdk_lib_1.Duration.days(60),
minTtl: aws_cdk_lib_1.Duration.days(30),
enableAcceptEncodingBrotli: true,
enableAcceptEncodingGzip: true,
comment: 'Nextjs Static Default Cache Policy',
};
/**
* The default CloudFront cache policy properties for images.
*/
NextjsDistribution.imageCachePolicyProps = {
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 Default Cache Policy',
};
/**
* The default CloudFront cache policy properties for the Lambda server handler.
*/
NextjsDistribution.lambdaCachePolicyProps = {
queryStringBehavior: cloudfront.CacheQueryStringBehavior.all(),
headerBehavior: cloudfront.CacheHeaderBehavior.allowList('rsc', 'next-router-prefetch', 'next-router-state-tree'),
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 Lambda Default Cache Policy',
};
/**
* The default CloudFront lambda origin request policy.
*/
NextjsDistribution.lambdaOriginRequestPolicyProps = {
cookieBehavior: cloudfront.OriginRequestCookieBehavior.all(),
queryStringBehavior: cloudfront.OriginRequestQueryStringBehavior.all(),
headerBehavior: cloudfront.OriginRequestHeaderBehavior.all(),
comment: 'Nextjs Lambda Origin Request Policy',
};
NextjsDistribution.fallbackOriginRequestPolicyProps = {
cookieBehavior: cloudfront.OriginRequestCookieBehavior.all(),
queryStringBehavior: cloudfront.OriginRequestQueryStringBehavior.all(),
headerBehavior: cloudfront.OriginRequestHeaderBehavior.all(),
comment: 'Nextjs Fallback Origin Request Policy',
};
NextjsDistribution.imageOptimizationOriginRequestPolicyProps = {
cookieBehavior: cloudfront.OriginRequestCookieBehavior.none(),
// NOTE: if `NextjsDistributionProps.functionUrlAuthType` is set to AWS_IAM
// auth, then the assets/lambda@edge/LambdaOriginRequestIamAuth.ts file
// needs to be updated to exclude these query strings/headers (below) from
// the signature calculation. Otherwise you'll get signature mismatch error.
queryStringBehavior: cloudfront.OriginRequestQueryStringBehavior.allowList('q', 'w', 'url'),
headerBehavior: cloudfront.OriginRequestHeaderBehavior.allowList('accept'),
comment: 'Nextjs Image Optimization Origin Request Policy',
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTmV4dGpzRGlzdHJpYnV0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL05leHRqc0Rpc3RyaWJ1dGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLHlCQUF5QjtBQUN6Qiw2QkFBNkI7QUFDN0IsK0JBQStCO0FBQy9CLDZDQUErRDtBQUMvRCwwREFBMEQ7QUFDMUQseURBQXlEO0FBQ3pELCtEQUFpRjtBQUNqRiw4REFBOEQ7QUFDOUQsaURBQXdFO0FBQ3hFLGlEQUFpRDtBQUNqRCx1REFBaUQ7QUFDakQsbURBQW1EO0FBQ25ELGtFQUFrRTtBQUVsRSwyQ0FBdUM7QUFDdkMsK0JBQStCO0FBQy9CLHFEQUFrRDtBQUNsRCwyQ0FBcUQ7QUFDckQsNkNBQTJHO0FBRTNHLG1DQUErQztBQUMvQyx5REFBMEQ7QUFFMUQsa0VBQWtFO0FBQ3JELFFBQUEsb0JBQW9CLEdBQUcsZUFBZSxDQUFDO0FBcU1wRDs7R0FFRztBQUNILE1BQWEsa0JBQW1CLFNBQVEsc0JBQVM7SUE4Ri9DLFlBQVksS0FBZ0IsRUFBRSxFQUFVLEVBQUUsS0FBOEI7UUFDdEUsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztRQUVqQix1Q0FBdUM7UUFDdkMsSUFBSSxDQUFDLFlBQVksR0FBRyxLQUFLLENBQUMsWUFBWTtZQUNwQyxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FDVixJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQUUsb0JBQW9CLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUNwRztZQUNILENBQUMsQ0FBQyxFQUFFLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxFQUFFLG1CQUFtQixDQUFDLENBQUMsQ0FB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YyxFQUFFLFVBQVUsQ0FBQywyQkFBMkIsQ0FBQyxHQUFHLEVBQUU7SUFDNUQsbUJBQW1CLEVBQUUsVUFBVSxDQUFDLGdDQUFnQyxDQUFDLEdBQUcsRUFBRTtJQUN0RSxjQUFjLEVBQUUsVUFBVSxDQUFDLDJCQUEyQixDQUFDLEdBQUcsRUFBRTtJQUM1RCxPQUFPLEVBQUUscUNBQXFDO0NBQy9DLENBQUM7QUFFWSxtREFBZ0MsR0FBd0M7SUFDcEYsY0FBYyxFQUFFLFVBQVUsQ0FBQywyQkFBMkIsQ0FBQyxHQUFHLEVBQUU7SUFDNUQsbUJBQW1CLEVBQUUsVUFBVSxDQUFDLGdDQUFnQyxDQUFDLEdBQUcsRUFBRTtJQUN0RSxjQUFjLEVBQUUsVUFBVSxDQUFDLDJCQUEyQixDQUFDLEdBQUcsRUFBRTtJQUM1RCxPQUFPLEVBQUUsdUNBQXVDO0NBQ2pELENBQUM7QUFFWSw0REFBeUMsR0FBd0M7SUFDN0YsY0FBYyxFQUFFLFVBQVUsQ0FBQywyQkFBMkIsQ0FBQyxJQUFJLEVBQUU7SUFDN0QsMkVBQTJFO0lBQzNFLHVFQUF1RTtJQUN2RSwwRUFBMEU7SUFDMUUsNEVBQTRFO0lBQzVFLG1CQUFtQixFQUFFLFVBQVUsQ0FBQyxnQ0FBZ0MsQ0FBQyxTQUFTLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxLQUFLLENBQUM7SUFDM0YsY0FBYyxFQUFFLFVBQVUsQ0FBQywyQkFBMkIsQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDO0lBQzFFLE9BQU8sRUFBRSxpREFBaUQ7Q0FDM0QsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIG9zIGZyb20gJ29zJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBkaXJuYW1lIH0gZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBBcHAsIER1cmF0aW9uLCBGbiwgUmVtb3ZhbFBvbGljeSB9IGZyb20gJ2F3cy1jZGstbGliJztcbmltcG9ydCAqIGFzIGFjbSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtY2VydGlmaWNhdGVtYW5hZ2VyJztcbmltcG9ydCAqIGFzIGNsb3VkZnJvbnQgZnJvbSAnYXdzLWNkay1saWIvYXdzLWNsb3VkZnJvbnQnO1xuaW1wb3J0IHsgRGlzdHJpYnV0aW9uLCBSZXNwb25zZUhlYWRlcnNQb2xpY3kgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtY2xvdWRmcm9udCc7XG5pbXBvcnQgKiBhcyBvcmlnaW5zIGZyb20gJ2F3cy1jZGstbGliL2F3cy1jbG91ZGZyb250LW9yaWdpbnMnO1xuaW1wb3J0IHsgUG9saWN5U3RhdGVtZW50LCBTZXJ2aWNlUHJpbmNpcGFsIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWlhbSc7XG5pbXBvcnQgKiBhcyBsYW1iZGEgZnJvbSAnYXdzLWNkay1saWIvYXdzLWxhbWJkYSc7XG5pbXBvcnQgeyBSdW50aW1lIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWxhbWJkYSc7XG5pbXBvcnQgKiBhcyByb3V0ZTUzIGZyb20gJ2F3cy1jZGstbGliL2F3cy1yb3V0ZTUzJztcbmltcG9ydCAqIGFzIHJvdXRlNTNUYXJnZXRzIGZyb20gJ2F3cy1jZGstbGliL2F3cy1yb3V0ZTUzLXRhcmdldHMnO1xuaW1wb3J0ICogYXMgczMgZnJvbSAnYXdzLWNkay1saWIvYXdzLXMzJztcbmltcG9ydCB7IENvbnN0cnVjdCB9IGZyb20gJ2NvbnN0cnVjdHMnO1xuaW1wb3J0ICogYXMgZnMgZnJvbSAnZnMtZXh0cmEnO1xuaW1wb3J0IHsgYnVuZGxlRnVuY3Rpb24gfSBmcm9tICcuL0J1bmRsZUZ1bmN0aW9uJztcbmltcG9ydCB7IERFRkFVTFRfU1RBVElDX01BWF9BR0UgfSBmcm9tICcuL2NvbnN0YW50cyc7XG5pbXBvcnQgeyBCYXNlU2l0ZURvbWFpblByb3BzLCBidWlsZEVycm9yUmVzcG9uc2VzRm9yUmVkaXJlY3RUb0luZGV4LCBOZXh0anNCYXNlUHJvcHMgfSBmcm9tICcuL05leHRqc0Jhc2UnO1xuaW1wb3J0IHsgTmV4dGpzQnVpbGQgfSBmcm9tICcuL05leHRqc0J1aWxkJztcbmltcG9ydCB7IGRvbWFpbkFkZFRyYWlsaW5nRG90IH0gZnJvbSAnLi91dGlscyc7XG5pbXBvcnQgeyBIdHRwc1JlZGlyZWN0UGF0Y2hlZCB9IGZyb20gJy4vd2Vic2l0ZS1yZWRpcmVjdCc7XG5cbi8vIGNvbnRhaW5zIHNlcnZlci1zaWRlIHJlc29sdmVkIGVudmlyb25tZW50IHZhcnMgaW4gY29uZmlnIGJ1Y2tldFxuZXhwb3J0IGNvbnN0IENPTkZJR19FTlZfSlNPTl9QQVRIID0gJ25leHQtZW52Lmpzb24nO1xuXG5leHBvcnQgaW50ZXJmYWNlIE5leHRqc0RvbWFpblByb3BzIGV4dGVuZHMgQmFzZVNpdGVEb21haW5Qcm9wcyB7fVxuXG5leHBvcnQgdHlwZSBOZXh0anNEaXN0cmlidXRpb25DZGtPdmVycmlkZVByb3BzID0gY2xvdWRmcm9udC5EaXN0cmlidXRpb25Qcm9wcztcblxuZXhwb3J0IGludGVyZmFjZSBOZXh0anNEaXN0cmlidXRpb25DZGtQcm9wcyB7XG4gIC8qKlxuICAgKiBQYXNzIGluIGEgdmFsdWUgdG8gb3ZlcnJpZGUgdGhlIGRlZmF1bHQgc2V0dGluZ3MgdGhpcyBjb25zdHJ1Y3QgdXNlcyB0b1xuICAgKiBjcmVhdGUgdGhlIENsb3VkRnJvbnQgYERpc3RyaWJ1dGlvbmAgaW50ZXJuYWxseS5cbiAgICovXG4gIHJlYWRvbmx5IGRpc3RyaWJ1dGlvbj86IE5leHRqc0Rpc3RyaWJ1dGlvbkNka092ZXJyaWRlUHJvcHM7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgTmV4dGpzQ2FjaGVQb2xpY3lQcm9wcyB7XG4gIHJlYWRvbmx5IHN0YXRpY0NhY2hlUG9saWN5PzogY2xvdWRmcm9udC5JQ2FjaGVQb2xpY3k7XG4gIHJlYWRvbmx5IGxhbWJkYUNhY2hlUG9saWN5PzogY2xvdWRmcm9udC5JQ2FjaGVQb2xpY3k7XG4gIHJlYWRvbmx5IGltYWdlQ2FjaGVQb2xpY3k/OiBjbG91ZGZyb250LklDYWNoZVBvbGljeTtcblxuICAvKipcbiAgICogQ2FjaGUtY29udHJvbCBtYXgtYWdlIGRlZmF1bHQgZm9yIHN0YXRpYyBhc3NldHMgKC9fbmV4dC8qKS5cbiAgICogRGVmYXVsdDogMzAgZGF5cy5cbiAgICovXG4gIHJlYWRvbmx5IHN0YXRpY0NsaWVudE1heEFnZURlZmF1bHQ/OiBEdXJhdGlvbjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBOZXh0anNPcmlnaW5SZXF1ZXN0UG9saWN5UHJvcHMge1xuICByZWFkb25seSBsYW1iZGFPcmlnaW5SZXF1ZXN0UG9saWN5PzogY2xvdWRmcm9udC5JT3JpZ2luUmVxdWVzdFBvbGljeTtcbiAgcmVhZG9ubHkgZmFsbGJhY2tPcmlnaW5SZXF1ZXN0UG9saWN5PzogY2xvdWRmcm9udC5JT3JpZ2luUmVxdWVzdFBvbGljeTtcbiAgcmVhZG9ubHkgaW1hZ2VPcHRpbWl6YXRpb25PcmlnaW5SZXF1ZXN0UG9saWN5PzogY2xvdWRmcm9udC5JT3JpZ2luUmVxdWVzdFBvbGljeTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBOZXh0anNEaXN0cmlidXRpb25Qcm9wcyBleHRlbmRzIE5leHRqc0Jhc2VQcm9wcyB7XG4gIC8qKlxuICAgKiBCdWNrZXQgY29udGFpbmluZyBzdGF0aWMgYXNzZXRzLlxuICAgKiBNdXN0IGJlIHByb3ZpZGVkIGlmIHlvdSB3YW50IHRvIHNlcnZlIHN0YXRpYyBmaWxlcy5cbiAgICovXG4gIHJlYWRvbmx5IHN0YXRpY0Fzc2V0c0J1Y2tldDogczMuSUJ1Y2tldDtcblxuICAvKipcbiAgICogTGFtYmRhIGZ1bmN0aW9uIHRvIHJvdXRlIGFsbCBub24tc3RhdGljIHJlcXVlc3RzIHRvLlxuICAgKiBNdXN0IGJlIHByb3ZpZGVkIGlmIHlvdSB3YW50IHRvIHNlcnZlIGR5bmFtaWMgcmVxdWVzdHMuXG4gICAqL1xuICByZWFkb25seSBzZXJ2ZXJGdW5jdGlvbjogbGFtYmRhLklGdW5jdGlvbjtcblxuICAvKipcbiAgICogTGFtYmRhIGZ1bmN0aW9uIHRvIG9wdGltaXplIGltYWdlcy5cbiAgICogTXVzdCBiZSBwcm92aWRlZCBpZiB5b3Ugd2FudCB0byBzZXJ2ZSBkeW5hbWljIHJlcXVlc3RzLlxuICAgKi9cbiAgcmVhZG9ubHkgaW1hZ2VPcHRGdW5jdGlvbjogbGFtYmRhLklGdW5jdGlvbjtcblxuICAvKipcbiAgICogT3ZlcnJpZGVzIGZvciBjcmVhdGVkIENESyByZXNvdXJjZXMuXG4gICAqL1xuICByZWFkb25seSBjZGs/OiBOZXh0anNEaXN0cmlidXRpb25DZGtQcm9wcztcblxuICAvKipcbiAgICogQnVpbHQgTmV4dEpTIGFwcC5cbiAgICovXG4gIHJlYWRvbmx5IG5leHRCdWlsZDogTmV4dGpzQnVpbGQ7XG5cbiAgLyoqXG4gICAqIE92ZXJyaWRlIHRoZSBkZWZhdWx0IENsb3VkRnJvbnQgY2FjaGUgcG9saWNpZXMgY3JlYXRlZCBpbnRlcm5hbGx5LlxuICAgKi9cbiAgcmVhZG9ubHkgY2FjaGVQb2xpY2llcz86IE5leHRqc0NhY2hlUG9saWN5UHJvcHM7XG5cbiAgLyoqXG4gICAqIE92ZXJyaWRlIHRoZSBkZWZhdWx0IENsb3VkRnJvbnQgb3JpZ2luIHJlcXVlc3QgcG9saWNpZXMgY3JlYXRlZCBpbnRlcm5hbGx5LlxuICAgKi9cbiAgcmVhZG9ubHkgb3JpZ2luUmVxdWVzdFBvbGljaWVzPzogTmV4dGpzT3JpZ2luUmVxdWVzdFBvbGljeVByb3BzO1xuXG4gIC8qKlxuICAgKiBUaGUgY3VzdG9tRG9tYWluIGZvciB0aGlzIHdlYnNpdGUuIFN1cHBvcnRzIGRvbWFpbnMgdGhhdCBhcmUgaG9zdGVkXG4gICAqIGVpdGhlciBvbiBbUm91dGUgNTNdKGh0dHBzOi8vYXdzLmFtYXpvbi5jb20vcm91dGU1My8pIG9yIGV4dGVybmFsbHkuXG4gICAqXG4gICAqIE5vdGUgdGhhdCB5b3UgY2FuIGFsc28gbWlncmF0ZSBleHRlcm5hbGx5IGhvc3RlZCBkb21haW5zIHRvIFJvdXRlIDUzIGJ5XG4gICAqIFtmb2xsb3dpbmcgdGhpcyBndWlkZV0oaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL1JvdXRlNTMvbGF0ZXN0L0RldmVsb3Blckd1aWRlL01pZ3JhdGluZ0ROUy5odG1sKS5cbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogbmV3IE5leHRqc0Rpc3RyaWJ1dGlvbih0aGlzLCBcIkRpc3RcIiwge1xuICAgKiAgIGN1c3RvbURvbWFpbjogXCJkb21haW4uY29tXCIsXG4gICAqIH0pO1xuICAgKlxuICAgKiBuZXcgTmV4dGpzRGlzdHJpYnV0aW9uKHRoaXMsIFwiRGlzdFwiLCB7XG4gICAqICAgY3VzdG9tRG9tYWluOiB7XG4gICAqICAgICBkb21haW5OYW1lOiBcImRvbWFpbi5jb21cIixcbiAgICogICAgIGRvbWFpbkFsaWFzOiBcInd3dy5kb21haW4uY29tXCIsXG4gICAqICAgICBob3N0ZWRab25lOiBcImRvbWFpbi5jb21cIlxuICAgKiAgIH0sXG4gICAqIH0pO1xuICAgKi9cbiAgcmVhZG9ubHkgY3VzdG9tRG9tYWluPzogc3RyaW5nIHwgTmV4dGpzRG9tYWluUHJvcHM7XG5cbiAgLyoqXG4gICAqIEluY2x1ZGUgdGhlIG5hbWUgb2YgeW91ciBkZXBsb3ltZW50IHN0YWdlIGlmIHByZXNlbnQuXG4gICAqIFVzZWQgdG8gbmFtZSB0aGUgZWRnZSBmdW5jdGlvbnMgc3RhY2suXG4gICAqIFJlcXVpcmVkIGlmIHVzaW5nIFNTVC5cbiAgICovXG4gIHJlYWRvbmx5IHN0YWdlTmFtZT86IHN0cmluZztcblxuICAvKipcbiAgICogT3B0aW9uYWwgdmFsdWUgdG8gcHJlZml4IHRoZSBlZGdlIGZ1bmN0aW9uIHN0YWNrXG4gICAqIEl0IGRlZmF1bHRzIHRvIFwiTmV4dGpzXCJcbiAgICovXG4gIHJlYWRvbmx5IHN0YWNrUHJlZml4Pzogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBPdmVycmlkZSBsYW1iZGEgZnVuY3Rpb24gdXJsIGF1dGggdHlwZVxuICAgKiBAZGVmYXVsdCBcIk5PTkVcIlxuICAgKi9cbiAgcmVhZG9ubHkgZnVuY3Rpb25VcmxBdXRoVHlwZT86IGxhbWJkYS5GdW5jdGlvblVybEF1dGhUeXBlO1xufVxuXG4vKipcbiAqIEVmZmVjdGl2ZWx5IGEgUGFydGlhbDxOZXh0anNEaXN0cmlidXRpb25Qcm9wcz4gdG8gc2F0aXNmeSBKU0lJXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgTmV4dGpzRGlzdHJpYnV0aW9uUHJvcHNEZWZhdWx0cyBleHRlbmRzIE5leHRqc0Jhc2VQcm9wcyB7XG4gIC8qKlxuICAgKiBCdWNrZXQgY29udGFpbmluZyBzdGF0aWMgYXNzZXRzLlxuICAgKiBNdXN0IGJlIHByb3ZpZGVkIGlmIHlvdSB3YW50IHRvIHNlcnZlIHN0YXRpYyBmaWxlcy5cbiAgICovXG4gIHJlYWRvbmx5IHN0YXRpY0Fzc2V0c0J1Y2tldD86IHMzLklCdWNrZXQ7XG5cbiAgLyoqXG4gICAqIExhbWJkYSBmdW5jdGlvbiB0byByb3V0ZSBhbGwgbm9uLXN0YXRpYyByZXF1ZXN0cyB0by5cbiAgICogTXVzdCBiZSBwcm92aWRlZCBpZiB5b3Ugd2FudCB0byBzZXJ2ZSBkeW5hbWljIHJlcXVlc3RzLlxuICAgKi9cbiAgcmVhZG9ubHkgc2VydmVyRnVuY3Rpb24/OiBsYW1iZGEuSUZ1bmN0aW9uO1xuXG4gIC8qKlxuICAgKiBMYW1iZGEgZnVuY3Rpb24gdG8gb3B0aW1pemUgaW1hZ2VzLlxuICAgKiBNdXN0IGJlIHByb3ZpZGVkIGlmIHlvdSB3YW50IHRvIHNlcnZlIGR5bmFtaWMgcmVxdWVzdHMuXG4gICAqL1xuICByZWFkb25seSBpbWFnZU9wdEZ1bmN0aW9uPzogbGFtYmRhLklGdW5jdGlvbjtcblxuICAvKipcbiAgICogT3ZlcnJpZGVzIGZvciBjcmVhdGVkIENESyByZXNvdXJjZXMuXG4gICAqL1xuICByZWFkb25seSBjZGs/OiBOZXh0anNEaXN0cmlidXRpb25DZGtQcm9wcztcblxuICAvKipcbiAgICogQnVpbHQgTmV4dEpTIGFwcC5cbiAgICovXG4gIHJlYWRvbmx5IG5leHRCdWlsZD86IE5leHRqc0J1aWxkO1xuXG4gIC8qKlxuICAgKiBPdmVycmlkZSB0aGUgZGVmYXVsdCBDbG91ZEZyb250IGNhY2hlIHBvbGljaWVzIGNyZWF0ZWQgaW50ZXJuYWxseS5cbiAgICovXG4gIHJlYWRvbmx5IGNhY2