UNPKG

open-next-cdk

Version:

Deploy a NextJS app using OpenNext packaging to serverless AWS using CDK

509 lines 81.5 kB
"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