cdk-nextjs 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.jsii +2467 -2714
- package/.jsii.tabl.json +1 -1
- package/API.md +1725 -1909
- package/CLAUDE.md +2 -1
- package/README.md +429 -130
- package/assets/lambdas/post-deploy/post-deploy.lambda/index.js +152 -170
- package/assets/lambdas/redeploy-stage/redeploy-stage.lambda/index.js +875 -0
- package/docs/adapter-runtime.md +185 -0
- package/docs/breaking-changes.md +687 -4
- package/docs/caching-guide.md +156 -112
- package/docs/cdk-nextjs-cfn-exec-policy.json +2 -1
- package/docs/development-guide.md +1 -1
- package/docs/harness-coverage.md +598 -0
- package/docs/init-cache-deployment.md +32 -1
- package/docs/next-build-output-guide.md +30 -20
- package/examples/README.md +5 -1
- package/lib/adapter/__fixtures__/app-playground-base-path.json +4926 -0
- package/lib/adapter/__fixtures__/app-playground.json +4917 -0
- package/lib/adapter/__fixtures__/pages-i18n.json +1728 -0
- package/lib/adapter/adapter.mjs +1548 -185
- package/lib/adapter/adapter.test.js +106 -0
- package/lib/adapter/aws-cache-store.d.ts +489 -0
- package/lib/adapter/aws-cache-store.js +1004 -0
- package/lib/adapter/aws-cache-store.test.d.ts +1 -0
- package/lib/adapter/aws-cache-store.test.js +361 -0
- package/lib/adapter/build-outputs.d.ts +80 -0
- package/lib/adapter/build-outputs.js +852 -0
- package/lib/adapter/build-outputs.test.d.ts +1 -0
- package/lib/adapter/build-outputs.test.js +861 -0
- package/lib/adapter/cache-handler.d.ts +9 -2
- package/lib/adapter/cache-handler.js +72 -96
- package/lib/adapter/cache-handler.mjs +18956 -17774
- package/lib/adapter/cache-handler.test.js +263 -31
- package/lib/adapter/cache-kinds.d.ts +32 -0
- package/lib/adapter/cache-kinds.js +35 -0
- package/lib/adapter/cache-kinds.test.d.ts +1 -0
- package/lib/adapter/cache-kinds.test.js +69 -0
- package/lib/adapter/cache-utils.d.ts +90 -0
- package/lib/adapter/cache-utils.js +199 -4
- package/lib/adapter/cache-utils.test.d.ts +1 -0
- package/lib/adapter/cache-utils.test.js +179 -0
- package/lib/adapter/function-groups.d.ts +215 -0
- package/lib/adapter/function-groups.js +1099 -0
- package/lib/adapter/function-groups.test.d.ts +1 -0
- package/lib/adapter/function-groups.test.js +743 -0
- package/lib/adapter/init-cache.d.ts +6 -0
- package/lib/adapter/init-cache.js +245 -0
- package/lib/adapter/init-cache.test.d.ts +1 -0
- package/lib/adapter/init-cache.test.js +137 -0
- package/lib/adapter/local-file-cache-handler.d.ts +23 -0
- package/lib/adapter/local-file-cache-handler.js +52 -1
- package/lib/adapter/memory-cache-handler.d.ts +26 -17
- package/lib/adapter/memory-cache-handler.js +32 -50
- package/lib/adapter/memory-cache-handler.test.js +105 -85
- package/lib/adapter/s3-cache-handler.d.ts +179 -40
- package/lib/adapter/s3-cache-handler.js +807 -277
- package/lib/adapter/s3-cache-handler.test.js +1186 -31
- package/lib/adapter/use-cache-common.d.ts +90 -0
- package/lib/adapter/use-cache-common.js +250 -0
- package/lib/adapter/use-cache-common.test.d.ts +1 -0
- package/lib/adapter/use-cache-common.test.js +646 -0
- package/lib/adapter/use-cache-default-handler.d.ts +24 -0
- package/lib/adapter/use-cache-default-handler.js +90 -0
- package/lib/adapter/use-cache-default-handler.mjs +38645 -0
- package/lib/adapter/use-cache-handlers.test.d.ts +1 -0
- package/lib/adapter/use-cache-handlers.test.js +349 -0
- package/lib/adapter/use-cache-remote-handler.d.ts +26 -0
- package/lib/adapter/use-cache-remote-handler.js +186 -0
- package/lib/{image-optimization/handler.mjs → adapter/use-cache-remote-handler.mjs} +33572 -38798
- package/lib/generated-structs/OptionalNextjsApiProps.d.ts +59 -0
- package/lib/generated-structs/OptionalNextjsApiProps.js +3 -0
- package/lib/generated-structs/OptionalNextjsCacheProps.d.ts +15 -6
- package/lib/generated-structs/OptionalNextjsCacheProps.js +1 -1
- package/lib/generated-structs/OptionalNextjsContainersProps.d.ts +25 -11
- package/lib/generated-structs/OptionalNextjsContainersProps.js +1 -1
- package/lib/generated-structs/OptionalNextjsDistributionProps.d.ts +31 -9
- package/lib/generated-structs/OptionalNextjsDistributionProps.js +1 -1
- package/lib/generated-structs/OptionalNextjsPostDeployProps.d.ts +13 -0
- package/lib/generated-structs/OptionalNextjsPostDeployProps.js +1 -1
- package/lib/generated-structs/OptionalPostDeployCustomResourceProperties.d.ts +11 -2
- package/lib/generated-structs/OptionalPostDeployCustomResourceProperties.js +1 -1
- package/lib/index.d.ts +6 -7
- package/lib/index.js +2 -4
- package/lib/lambdas/post-deploy/post-deploy.lambda.js +3 -2
- package/lib/lambdas/post-deploy/prune-cache-bucket.d.ts +7 -3
- package/lib/lambdas/post-deploy/prune-cache-bucket.js +57 -71
- package/lib/lambdas/post-deploy/prune-cache-bucket.test.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-cache-bucket.test.js +114 -0
- package/lib/lambdas/post-deploy/prune-revalidation-table.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-revalidation-table.js +23 -31
- package/lib/lambdas/post-deploy/prune-revalidation-table.test.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-revalidation-table.test.js +71 -0
- package/lib/lambdas/post-deploy/prune-s3.d.ts +19 -3
- package/lib/lambdas/post-deploy/prune-s3.js +54 -15
- package/lib/lambdas/post-deploy/prune-s3.test.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-s3.test.js +255 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage-function.d.ts +13 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage-function.js +55 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.d.ts +24 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.js +68 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.lambda.d.ts +20 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.lambda.js +24 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.test.d.ts +1 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.test.js +93 -0
- package/lib/nextjs-api.d.ts +107 -18
- package/lib/nextjs-api.js +349 -39
- package/lib/nextjs-api.test.js +582 -1
- package/lib/nextjs-build/global-containers.Dockerfile +15 -10
- package/lib/nextjs-build/nextjs-build.d.ts +339 -63
- package/lib/nextjs-build/nextjs-build.js +684 -327
- package/lib/nextjs-build/nextjs-build.test.d.ts +1 -0
- package/lib/nextjs-build/nextjs-build.test.js +412 -0
- package/lib/nextjs-build/patch-fetch.js +1 -1
- package/lib/nextjs-build/patch-fetch.test.js +166 -11
- package/lib/nextjs-build/regional-containers.Dockerfile +17 -10
- package/lib/nextjs-cache.d.ts +82 -4
- package/lib/nextjs-cache.js +186 -10
- package/lib/nextjs-cache.test.js +176 -1
- package/lib/nextjs-compute/nextjs-compute-base-props.d.ts +21 -8
- package/lib/nextjs-compute/nextjs-compute-base-props.js +41 -1
- package/lib/nextjs-compute/nextjs-compute-base-props.test.d.ts +1 -0
- package/lib/nextjs-compute/nextjs-compute-base-props.test.js +44 -0
- package/lib/nextjs-compute/nextjs-containers.d.ts +19 -0
- package/lib/nextjs-compute/nextjs-containers.js +57 -20
- package/lib/nextjs-compute/nextjs-functions.d.ts +122 -7
- package/lib/nextjs-compute/nextjs-functions.js +113 -67
- package/lib/nextjs-compute/nextjs-functions.test.d.ts +1 -0
- package/lib/nextjs-compute/nextjs-functions.test.js +99 -0
- package/lib/nextjs-distribution.d.ts +194 -23
- package/lib/nextjs-distribution.js +535 -62
- package/lib/nextjs-distribution.test.d.ts +1 -0
- package/lib/nextjs-distribution.test.js +697 -0
- package/lib/nextjs-post-deploy.d.ts +23 -3
- package/lib/nextjs-post-deploy.js +14 -10
- package/lib/nextjs-post-deploy.test.d.ts +1 -0
- package/lib/nextjs-post-deploy.test.js +34 -0
- package/lib/nextjs-static-assets.d.ts +23 -4
- package/lib/nextjs-static-assets.js +29 -6
- package/lib/nextjs-static-assets.test.js +20 -1
- package/lib/root-constructs/nextjs-base-construct.d.ts +105 -45
- package/lib/root-constructs/nextjs-base-construct.js +144 -34
- package/lib/root-constructs/nextjs-global-containers.d.ts +18 -20
- package/lib/root-constructs/nextjs-global-containers.js +20 -70
- package/lib/root-constructs/nextjs-global-functions.d.ts +26 -28
- package/lib/root-constructs/nextjs-global-functions.js +52 -69
- package/lib/root-constructs/nextjs-regional-containers.d.ts +25 -5
- package/lib/root-constructs/nextjs-regional-containers.js +17 -15
- package/lib/root-constructs/nextjs-regional-functions.d.ts +22 -18
- package/lib/root-constructs/nextjs-regional-functions.js +8 -25
- package/lib/runtime/api-gateway-path.d.ts +34 -0
- package/lib/runtime/api-gateway-path.js +71 -0
- package/lib/runtime/api-gateway-path.test.d.ts +1 -0
- package/lib/runtime/api-gateway-path.test.js +65 -0
- package/lib/runtime/case-redirect.d.ts +22 -0
- package/lib/runtime/case-redirect.js +63 -0
- package/lib/runtime/case-redirect.test.d.ts +1 -0
- package/lib/runtime/case-redirect.test.js +32 -0
- package/lib/runtime/core.d.ts +234 -0
- package/lib/runtime/core.js +1213 -0
- package/lib/runtime/core.test.d.ts +1 -0
- package/lib/runtime/core.test.js +1678 -0
- package/lib/runtime/deployment-root.d.ts +13 -0
- package/lib/runtime/deployment-root.js +29 -0
- package/lib/runtime/dispatch.d.ts +331 -0
- package/lib/runtime/dispatch.js +751 -0
- package/lib/runtime/dispatch.test.d.ts +1 -0
- package/lib/runtime/dispatch.test.js +853 -0
- package/lib/runtime/entrypoints.d.ts +57 -0
- package/lib/runtime/entrypoints.js +115 -0
- package/lib/runtime/http/abort-signal.d.ts +6 -0
- package/lib/runtime/http/abort-signal.js +19 -0
- package/lib/runtime/http/abort-signal.test.d.ts +1 -0
- package/lib/runtime/http/abort-signal.test.js +49 -0
- package/lib/runtime/http/node-server.d.ts +38 -0
- package/lib/runtime/http/node-server.js +99 -0
- package/lib/runtime/http/node-server.test.d.ts +1 -0
- package/lib/runtime/http/node-server.test.js +96 -0
- package/lib/runtime/http/node-sink.d.ts +16 -0
- package/lib/runtime/http/node-sink.js +48 -0
- package/lib/runtime/http/node-sink.test.d.ts +1 -0
- package/lib/runtime/http/node-sink.test.js +66 -0
- package/lib/runtime/http/request.d.ts +62 -0
- package/lib/runtime/http/request.js +134 -0
- package/lib/runtime/http/request.test.d.ts +1 -0
- package/lib/runtime/http/request.test.js +103 -0
- package/lib/runtime/http/response.d.ts +77 -0
- package/lib/runtime/http/response.js +229 -0
- package/lib/runtime/http/response.test.d.ts +1 -0
- package/lib/runtime/http/response.test.js +178 -0
- package/lib/runtime/http/sink.d.ts +73 -0
- package/lib/runtime/http/sink.js +188 -0
- package/lib/runtime/http/sink.test.d.ts +1 -0
- package/lib/runtime/http/sink.test.js +257 -0
- package/lib/runtime/image-utils.d.ts +78 -0
- package/lib/runtime/image-utils.js +129 -0
- package/lib/runtime/image-utils.test.d.ts +1 -0
- package/lib/runtime/image-utils.test.js +237 -0
- package/lib/runtime/image.d.ts +76 -0
- package/lib/runtime/image.js +296 -0
- package/lib/runtime/image.test.d.ts +1 -0
- package/lib/runtime/image.test.js +405 -0
- package/lib/runtime/lambda-event.d.ts +4 -0
- package/lib/runtime/lambda-event.js +113 -0
- package/lib/runtime/lambda-event.test.d.ts +1 -0
- package/lib/runtime/lambda-event.test.js +119 -0
- package/lib/runtime/lambda.mjs +41595 -0
- package/lib/runtime/load-module.d.ts +8 -0
- package/lib/runtime/load-module.js +35 -0
- package/lib/runtime/manifest.d.ts +215 -0
- package/lib/runtime/manifest.js +118 -0
- package/lib/runtime/middleware.d.ts +43 -0
- package/lib/runtime/middleware.js +113 -0
- package/lib/runtime/middleware.test.d.ts +1 -0
- package/lib/runtime/middleware.test.js +259 -0
- package/lib/runtime/next-modules.d.ts +55 -0
- package/lib/runtime/next-modules.js +124 -0
- package/lib/runtime/next-modules.test.d.ts +1 -0
- package/lib/runtime/next-modules.test.js +34 -0
- package/lib/runtime/public-files.d.ts +32 -0
- package/lib/runtime/public-files.js +117 -0
- package/lib/runtime/public-files.test.d.ts +1 -0
- package/lib/runtime/public-files.test.js +97 -0
- package/lib/runtime/server.mjs +41588 -0
- package/lib/runtime/static-files.d.ts +50 -0
- package/lib/runtime/static-files.js +266 -0
- package/lib/runtime/static-files.test.d.ts +1 -0
- package/lib/runtime/static-files.test.js +118 -0
- package/lib/runtime/util.d.ts +28 -0
- package/lib/runtime/util.js +79 -0
- package/lib/utils/base-path.d.ts +123 -11
- package/lib/utils/base-path.js +276 -39
- package/lib/utils/base-path.test.js +275 -50
- package/lib/utils/get-architecture.d.ts +7 -8
- package/lib/utils/get-architecture.js +12 -13
- package/lib/utils/zip-directory.d.ts +14 -0
- package/lib/utils/zip-directory.js +121 -0
- package/lib/utils/zip-directory.test.d.ts +1 -0
- package/lib/utils/zip-directory.test.js +99 -0
- package/package.json +22 -3
- package/scripts/capture-adapter-fixture.mjs +325 -0
- package/scripts/e2e-deploy.sh +398 -0
- package/scripts/e2e-harness/README.md +707 -0
- package/scripts/e2e-harness/app.js +370 -0
- package/scripts/e2e-harness/common.sh +249 -0
- package/scripts/e2e-harness/screen.mjs +302 -0
- package/scripts/e2e-harness/stage-proxy.mjs +145 -0
- package/scripts/e2e-harness/type-filters.mjs +47 -0
- package/scripts/e2e-logs.sh +75 -0
- package/scripts/e2e-offline.sh +91 -0
- package/scripts/e2e-sweep.sh +370 -0
- package/scripts/e2e-warm.sh +114 -0
- package/scripts/zero-config-build.mjs +418 -0
- package/docs/1k-concurrent-users-executions.png +0 -0
- package/docs/plans/adapter-runtime-release.md +0 -362
- package/docs/plans/phase-0-image-function-opt-in.md +0 -295
- package/lib/generated-structs/OptionalDockerImageFunctionProps.d.ts +0 -425
- package/lib/generated-structs/OptionalDockerImageFunctionProps.js +0 -3
- package/lib/image-optimization/handler-utils.d.ts +0 -34
- package/lib/image-optimization/handler-utils.js +0 -83
- package/lib/image-optimization/handler-utils.test.js +0 -178
- package/lib/nextjs-build/functions.Dockerfile +0 -31
- package/lib/nextjs-compute/nextjs-image-function.d.ts +0 -53
- package/lib/nextjs-compute/nextjs-image-function.js +0 -59
- package/lib/utils/experimental-flags.d.ts +0 -24
- package/lib/utils/experimental-flags.js +0 -30
- /package/lib/{image-optimization/handler-utils.test.d.ts → adapter/adapter.test.d.ts} +0 -0
|
@@ -0,0 +1,697 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const node_vm_1 = require("node:vm");
|
|
4
|
+
const aws_cdk_lib_1 = require("aws-cdk-lib");
|
|
5
|
+
const assertions_1 = require("aws-cdk-lib/assertions");
|
|
6
|
+
const aws_cloudfront_1 = require("aws-cdk-lib/aws-cloudfront");
|
|
7
|
+
const aws_lambda_1 = require("aws-cdk-lib/aws-lambda");
|
|
8
|
+
const aws_s3_1 = require("aws-cdk-lib/aws-s3");
|
|
9
|
+
const function_groups_1 = require("./adapter/function-groups");
|
|
10
|
+
const constants_1 = require("./constants");
|
|
11
|
+
const nextjs_distribution_1 = require("./nextjs-distribution");
|
|
12
|
+
/**
|
|
13
|
+
* `functionGroups` routing at the edge. `NextjsBuild` is deliberately not
|
|
14
|
+
* involved: which behaviors a group gets, and in what order, is the adapter's
|
|
15
|
+
* decision (`function-groups.test.ts`), so these pass the behaviors in directly
|
|
16
|
+
* and assert on what the distribution does with them.
|
|
17
|
+
*/
|
|
18
|
+
describe("NextjsDistribution function group behaviors", () => {
|
|
19
|
+
const setup = (groupNames, props = {}) => {
|
|
20
|
+
const stack = new aws_cdk_lib_1.Stack(new aws_cdk_lib_1.App(), "TestStack");
|
|
21
|
+
const functionGroups = groupNames.map((name) => {
|
|
22
|
+
const fn = new aws_lambda_1.Function(stack, `Fn${name}`, {
|
|
23
|
+
code: aws_lambda_1.Code.fromInline("exports.handler = () => {};"),
|
|
24
|
+
handler: "index.handler",
|
|
25
|
+
runtime: aws_lambda_1.Runtime.NODEJS_22_X,
|
|
26
|
+
});
|
|
27
|
+
return {
|
|
28
|
+
name,
|
|
29
|
+
functionUrl: new aws_lambda_1.FunctionUrl(stack, `Url${name}`, {
|
|
30
|
+
function: fn,
|
|
31
|
+
authType: aws_lambda_1.FunctionUrlAuthType.AWS_IAM,
|
|
32
|
+
}),
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
// In the order given: the build's list is already most specific first.
|
|
36
|
+
const functionGroupBehaviors = groupNames.flatMap((group) => (props.routesFor?.(group) ?? [`/${group}/**`]).flatMap((route) => (0, function_groups_1.pathPatternsFor)(route, {
|
|
37
|
+
hasDataRoutes: false,
|
|
38
|
+
trailingSlash: props.trailingSlash,
|
|
39
|
+
}).map((pattern) => ({ group, route, pattern }))));
|
|
40
|
+
const defaultFn = new aws_lambda_1.Function(stack, "FnDefault", {
|
|
41
|
+
code: aws_lambda_1.Code.fromInline("exports.handler = () => {};"),
|
|
42
|
+
handler: "index.handler",
|
|
43
|
+
runtime: aws_lambda_1.Runtime.NODEJS_22_X,
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
stack,
|
|
47
|
+
functionGroups,
|
|
48
|
+
functionGroupBehaviors,
|
|
49
|
+
distributionProps: {
|
|
50
|
+
assetsBucket: new aws_s3_1.Bucket(stack, "Assets"),
|
|
51
|
+
basePath: props.basePath,
|
|
52
|
+
functionUrl: new aws_lambda_1.FunctionUrl(stack, "UrlDefault", {
|
|
53
|
+
function: defaultFn,
|
|
54
|
+
authType: aws_lambda_1.FunctionUrlAuthType.AWS_IAM,
|
|
55
|
+
}),
|
|
56
|
+
nextjsType: constants_1.NextjsType.GLOBAL_FUNCTIONS,
|
|
57
|
+
publicDirEntries: (props.publicDirEntries ?? []).map((name) => ({
|
|
58
|
+
name,
|
|
59
|
+
isDirectory: false,
|
|
60
|
+
})),
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
const pathPatterns = (stack) => {
|
|
65
|
+
const distributions = assertions_1.Template.fromStack(stack).findResources("AWS::CloudFront::Distribution");
|
|
66
|
+
const config = Object.values(distributions)[0].Properties.DistributionConfig;
|
|
67
|
+
return config.CacheBehaviors.map((behavior) => behavior.PathPattern);
|
|
68
|
+
};
|
|
69
|
+
it("adds nothing extra when there are no groups", () => {
|
|
70
|
+
const { stack, distributionProps } = setup([]);
|
|
71
|
+
new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", distributionProps);
|
|
72
|
+
expect(pathPatterns(stack)).toEqual(["_next/static*", "_next/image*"]);
|
|
73
|
+
});
|
|
74
|
+
it("lets the OAC principal list the bucket so a missing asset is a 404", () => {
|
|
75
|
+
const { stack, distributionProps } = setup([]);
|
|
76
|
+
new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", distributionProps);
|
|
77
|
+
assertions_1.Template.fromStack(stack).hasResourceProperties("AWS::S3::BucketPolicy", {
|
|
78
|
+
PolicyDocument: {
|
|
79
|
+
Statement: assertions_1.Match.arrayWith([
|
|
80
|
+
assertions_1.Match.objectLike({
|
|
81
|
+
Action: ["s3:GetObject", "s3:ListBucket"],
|
|
82
|
+
Principal: { Service: "cloudfront.amazonaws.com" },
|
|
83
|
+
}),
|
|
84
|
+
]),
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
it("adds group behaviors in the build's order, after the image behavior", () => {
|
|
89
|
+
// CloudFront stops at the first matching behavior, so `api/*` ahead of
|
|
90
|
+
// `api/reports/*` would send every report request to the wrong function.
|
|
91
|
+
// The build sorted them when it checked them; re-sorting here could only
|
|
92
|
+
// disagree with that.
|
|
93
|
+
const { stack, functionGroups, functionGroupBehaviors, distributionProps } = setup(["reports", "api"], {
|
|
94
|
+
routesFor: (name) => name === "api" ? ["/api/**"] : ["/api/reports/**"],
|
|
95
|
+
});
|
|
96
|
+
new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
97
|
+
...distributionProps,
|
|
98
|
+
functionGroups,
|
|
99
|
+
functionGroupBehaviors,
|
|
100
|
+
});
|
|
101
|
+
expect(pathPatterns(stack)).toEqual([
|
|
102
|
+
"_next/static*",
|
|
103
|
+
"_next/image*",
|
|
104
|
+
"api/reports/*",
|
|
105
|
+
"api/*",
|
|
106
|
+
]);
|
|
107
|
+
});
|
|
108
|
+
it("keeps the two slash variants distinct under a basePath", () => {
|
|
109
|
+
// `getPathPattern` used to join through a helper that strips trailing
|
|
110
|
+
// slashes, so `pricing` and `pricing/` both became `/base/pricing`: two
|
|
111
|
+
// behaviors with the same path pattern, which CloudFront rejects at deploy
|
|
112
|
+
// with no hint that `trailingSlash` is what produced the duplicate.
|
|
113
|
+
const { stack, functionGroups, functionGroupBehaviors, distributionProps } = setup(["mkt"], {
|
|
114
|
+
routesFor: () => ["/pricing"],
|
|
115
|
+
basePath: "/base",
|
|
116
|
+
trailingSlash: true,
|
|
117
|
+
});
|
|
118
|
+
new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
119
|
+
...distributionProps,
|
|
120
|
+
functionGroups,
|
|
121
|
+
functionGroupBehaviors,
|
|
122
|
+
});
|
|
123
|
+
const patterns = pathPatterns(stack);
|
|
124
|
+
expect(patterns).toContain("/base/pricing");
|
|
125
|
+
expect(patterns).toContain("/base/pricing/");
|
|
126
|
+
});
|
|
127
|
+
it("counts the basePath behaviors against the budget", () => {
|
|
128
|
+
const { stack, distributionProps } = setup([], {
|
|
129
|
+
basePath: "/base",
|
|
130
|
+
publicDirEntries: Array.from({ length: 21 }, (_, i) => `file${i}.txt`),
|
|
131
|
+
});
|
|
132
|
+
// 3 fixed + the 2 a basePath adds to stand in for the default behavior + 21
|
|
133
|
+
// public = 26. Counting only the 3 reported 24 and let the synth through, so
|
|
134
|
+
// the limit arrived as a CloudFront deploy failure.
|
|
135
|
+
expect(() => new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
136
|
+
...distributionProps,
|
|
137
|
+
maxCacheBehaviors: 25,
|
|
138
|
+
})).toThrow(/26 CloudFront cache behaviors.*5 used by cdk-nextjs itself/s);
|
|
139
|
+
});
|
|
140
|
+
it("defaults the budget to CloudFront's quota of 75", () => {
|
|
141
|
+
// The hard-coded 25 predated the quota's increase to 75.
|
|
142
|
+
const { stack, distributionProps } = setup([], {
|
|
143
|
+
publicDirEntries: Array.from({ length: 72 }, (_, i) => `file${i}.txt`),
|
|
144
|
+
});
|
|
145
|
+
expect(() => new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", distributionProps)).not.toThrow();
|
|
146
|
+
const { stack: over, distributionProps: overProps } = setup([], {
|
|
147
|
+
publicDirEntries: Array.from({ length: 73 }, (_, i) => `file${i}.txt`),
|
|
148
|
+
});
|
|
149
|
+
expect(() => new nextjs_distribution_1.NextjsDistribution(over, "Distribution", overProps)).toThrow(/76 CloudFront cache behaviors, over the limit of 75.*set `maxCacheBehaviors`/s);
|
|
150
|
+
});
|
|
151
|
+
it("honors a raised maxCacheBehaviors", () => {
|
|
152
|
+
const { stack, distributionProps } = setup([], {
|
|
153
|
+
publicDirEntries: Array.from({ length: 97 }, (_, i) => `file${i}.txt`),
|
|
154
|
+
});
|
|
155
|
+
expect(() => new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
156
|
+
...distributionProps,
|
|
157
|
+
maxCacheBehaviors: 100,
|
|
158
|
+
})).not.toThrow();
|
|
159
|
+
});
|
|
160
|
+
it("does not count basePath behaviors for a basePath of /", () => {
|
|
161
|
+
// `basePath: "/"` normalizes to no basePath and adds no behaviors, so
|
|
162
|
+
// counting 2 for it could reject an app that is under the limit.
|
|
163
|
+
const { stack, distributionProps } = setup([], {
|
|
164
|
+
basePath: "/",
|
|
165
|
+
publicDirEntries: Array.from({ length: 22 }, (_, i) => `file${i}.txt`),
|
|
166
|
+
});
|
|
167
|
+
expect(() => new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
168
|
+
...distributionProps,
|
|
169
|
+
maxCacheBehaviors: 25,
|
|
170
|
+
})).not.toThrow();
|
|
171
|
+
});
|
|
172
|
+
it("rejects a behavior for a group it was not given", () => {
|
|
173
|
+
const { stack, functionGroupBehaviors, distributionProps } = setup(["api"]);
|
|
174
|
+
expect(() => new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
175
|
+
...distributionProps,
|
|
176
|
+
functionGroupBehaviors,
|
|
177
|
+
})).toThrow(/routes to function group "api", which is not in `functionGroups`/);
|
|
178
|
+
});
|
|
179
|
+
it("points each group's behaviors at that group's own origin", () => {
|
|
180
|
+
const { stack, functionGroups, functionGroupBehaviors, distributionProps } = setup(["api"], {
|
|
181
|
+
routesFor: () => ["/api/**", "/health"],
|
|
182
|
+
});
|
|
183
|
+
new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
184
|
+
...distributionProps,
|
|
185
|
+
functionGroups,
|
|
186
|
+
functionGroupBehaviors,
|
|
187
|
+
});
|
|
188
|
+
const distributions = assertions_1.Template.fromStack(stack).findResources("AWS::CloudFront::Distribution");
|
|
189
|
+
const config = Object.values(distributions)[0].Properties.DistributionConfig;
|
|
190
|
+
const behaviors = config.CacheBehaviors;
|
|
191
|
+
const groupOrigins = new Set(behaviors
|
|
192
|
+
.filter((behavior) => ["api/*", "health"].includes(behavior.PathPattern))
|
|
193
|
+
.map((behavior) => behavior.TargetOriginId));
|
|
194
|
+
// One origin, shared by both of the group's patterns, and not the default's.
|
|
195
|
+
expect(groupOrigins.size).toBe(1);
|
|
196
|
+
expect(groupOrigins).not.toContain(config.DefaultCacheBehavior.TargetOriginId);
|
|
197
|
+
});
|
|
198
|
+
it("prefixes group patterns with basePath", () => {
|
|
199
|
+
const { stack, functionGroups, functionGroupBehaviors, distributionProps } = setup(["api"], {
|
|
200
|
+
routesFor: () => ["/api/**"],
|
|
201
|
+
basePath: "/base",
|
|
202
|
+
});
|
|
203
|
+
new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
204
|
+
...distributionProps,
|
|
205
|
+
functionGroups,
|
|
206
|
+
functionGroupBehaviors,
|
|
207
|
+
});
|
|
208
|
+
expect(pathPatterns(stack)).toContain("/base/api/*");
|
|
209
|
+
});
|
|
210
|
+
it("names every claim on the behavior budget when it is exceeded", () => {
|
|
211
|
+
const { stack, functionGroups, functionGroupBehaviors, distributionProps } = setup(["api"], {
|
|
212
|
+
routesFor: () => ["/api/**"],
|
|
213
|
+
publicDirEntries: Array.from({ length: 22 }, (_, i) => `file${i}.txt`),
|
|
214
|
+
});
|
|
215
|
+
expect(() => new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
216
|
+
...distributionProps,
|
|
217
|
+
functionGroups,
|
|
218
|
+
functionGroupBehaviors,
|
|
219
|
+
maxCacheBehaviors: 25,
|
|
220
|
+
})).toThrow(/26 CloudFront cache behaviors.*1 for `functionGroups` patterns/s);
|
|
221
|
+
});
|
|
222
|
+
it("rejects a group pattern that duplicates a public/ directory's", () => {
|
|
223
|
+
// `public/docs/` and `/docs/**` both deploy as `docs/*`: the synth
|
|
224
|
+
// succeeded and CloudFront rejected the deploy.
|
|
225
|
+
const { stack, functionGroups, functionGroupBehaviors, distributionProps } = setup(["docs"]);
|
|
226
|
+
expect(() => new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
227
|
+
...distributionProps,
|
|
228
|
+
functionGroups,
|
|
229
|
+
functionGroupBehaviors,
|
|
230
|
+
publicDirEntries: [{ name: "docs", isDirectory: true }],
|
|
231
|
+
})).toThrow(/pattern "\/docs\/\*\*" \(group "docs"\) deploys as the path pattern "docs\/\*", which a top-level public\/ entry already uses/);
|
|
232
|
+
});
|
|
233
|
+
it("rejects a group pattern a public/ directory's behavior would shadow", () => {
|
|
234
|
+
// `docs/*` is added first, so `docs/guide/*` never matches.
|
|
235
|
+
const { stack, functionGroups, functionGroupBehaviors, distributionProps } = setup(["guide"], {
|
|
236
|
+
routesFor: () => ["/docs/guide/**"],
|
|
237
|
+
});
|
|
238
|
+
expect(() => new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
239
|
+
...distributionProps,
|
|
240
|
+
functionGroups,
|
|
241
|
+
functionGroupBehaviors,
|
|
242
|
+
publicDirEntries: [{ name: "docs", isDirectory: true }],
|
|
243
|
+
})).toThrow(/public\/ entry behind "docs\/\*" is matched first/);
|
|
244
|
+
});
|
|
245
|
+
it("rejects an exact group pattern equal to a public/ file's, basePath included", () => {
|
|
246
|
+
const { stack, functionGroups, functionGroupBehaviors, distributionProps } = setup(["mkt"], {
|
|
247
|
+
routesFor: () => ["/robots.txt"],
|
|
248
|
+
basePath: "/base",
|
|
249
|
+
});
|
|
250
|
+
expect(() => new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
251
|
+
...distributionProps,
|
|
252
|
+
functionGroups,
|
|
253
|
+
functionGroupBehaviors,
|
|
254
|
+
publicDirEntries: [{ name: "robots.txt", isDirectory: false }],
|
|
255
|
+
})).toThrow(/deploys as the path pattern "\/base\/robots\.txt"/);
|
|
256
|
+
});
|
|
257
|
+
it("allows a group pattern beside a public/ entry it does not overlap", () => {
|
|
258
|
+
// `/docs` exact is not under `docs/*`, and `docs/*` does not match `/docs`.
|
|
259
|
+
const { stack, functionGroups, functionGroupBehaviors, distributionProps } = setup(["docs"], {
|
|
260
|
+
routesFor: () => ["/docs"],
|
|
261
|
+
});
|
|
262
|
+
new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
263
|
+
...distributionProps,
|
|
264
|
+
functionGroups,
|
|
265
|
+
functionGroupBehaviors,
|
|
266
|
+
publicDirEntries: [{ name: "docs", isDirectory: true }],
|
|
267
|
+
});
|
|
268
|
+
expect(pathPatterns(stack)).toEqual(expect.arrayContaining(["docs/*", "docs"]));
|
|
269
|
+
});
|
|
270
|
+
it("serves bundles under a path-style assetPrefix, rewriting the prefix away", () => {
|
|
271
|
+
// Next.js emits `<assetPrefix>/_next/static/...` for every bundle while the
|
|
272
|
+
// objects keep their unprefixed S3 keys, so without a behavior of its own the
|
|
273
|
+
// request falls through to the compute origin and 404s — the deployment
|
|
274
|
+
// package carries no `.next/static`. Measured against
|
|
275
|
+
// `test/e2e/app-dir/asset-prefix`, where 2 of 7 cases failed on exactly that.
|
|
276
|
+
const { stack, distributionProps } = setup([]);
|
|
277
|
+
new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
278
|
+
...distributionProps,
|
|
279
|
+
assetPrefix: "/custom-asset-prefix",
|
|
280
|
+
});
|
|
281
|
+
// Ahead of the bare `_next/static*` in nothing but list position; the two
|
|
282
|
+
// patterns cannot both match one request, so order does not matter here.
|
|
283
|
+
expect(pathPatterns(stack)).toEqual([
|
|
284
|
+
"_next/static*",
|
|
285
|
+
"/custom-asset-prefix/_next/static*",
|
|
286
|
+
"_next/image*",
|
|
287
|
+
]);
|
|
288
|
+
const fns = assertions_1.Template.fromStack(stack).findResources("AWS::CloudFront::Function");
|
|
289
|
+
const code = Object.values(fns)
|
|
290
|
+
.map((fn) => fn.Properties.FunctionCode)
|
|
291
|
+
.find((it) => it.includes("request.uri.slice"));
|
|
292
|
+
// `"/custom-asset-prefix".length`, so `/custom-asset-prefix/_next/static/x`
|
|
293
|
+
// reaches S3 as `_next/static/x`.
|
|
294
|
+
expect(code).toContain('"" + request.uri.slice(20)');
|
|
295
|
+
});
|
|
296
|
+
it("refuses an assetPrefix when an override already claims VIEWER_REQUEST", () => {
|
|
297
|
+
// CloudFront permits one function per event type per behavior, so the
|
|
298
|
+
// rewrite and the override cannot coexist: the stack synthed cleanly and was
|
|
299
|
+
// rejected at deploy, naming neither the override nor `assetPrefix`.
|
|
300
|
+
const { stack, distributionProps } = setup([]);
|
|
301
|
+
expect(() => new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
302
|
+
...distributionProps,
|
|
303
|
+
assetPrefix: "/cdn",
|
|
304
|
+
overrides: {
|
|
305
|
+
staticBehaviorOptions: {
|
|
306
|
+
functionAssociations: [
|
|
307
|
+
{
|
|
308
|
+
eventType: aws_cloudfront_1.FunctionEventType.VIEWER_REQUEST,
|
|
309
|
+
function: new aws_cloudfront_1.Function(stack, "UserFn", {
|
|
310
|
+
code: aws_cloudfront_1.FunctionCode.fromInline("function handler(event) { return event.request; }"),
|
|
311
|
+
}),
|
|
312
|
+
},
|
|
313
|
+
],
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
})).toThrow(/already associates a CloudFront function with viewer-request/);
|
|
317
|
+
});
|
|
318
|
+
const userFn = (stack, eventType) => ({
|
|
319
|
+
eventType,
|
|
320
|
+
function: new aws_cloudfront_1.Function(stack, `UserFn${eventType}`, {
|
|
321
|
+
code: aws_cloudfront_1.FunctionCode.fromInline("function handler(event) { return event.request || event.response; }"),
|
|
322
|
+
}),
|
|
323
|
+
});
|
|
324
|
+
it.each(["dynamicBehaviorOptions", "imageBehaviorOptions"])("refuses a %s override that would replace the x-forwarded-host function", (overrideName) => {
|
|
325
|
+
// The runtime trusts `x-forwarded-host` on Function URL events because
|
|
326
|
+
// this function overwrites it; replaced, a viewer's own header reaches it.
|
|
327
|
+
const { stack, distributionProps } = setup([]);
|
|
328
|
+
expect(() => new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
329
|
+
...distributionProps,
|
|
330
|
+
overrides: {
|
|
331
|
+
[overrideName]: {
|
|
332
|
+
functionAssociations: [
|
|
333
|
+
userFn(stack, aws_cloudfront_1.FunctionEventType.VIEWER_REQUEST),
|
|
334
|
+
],
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
})).toThrow(new RegExp(`overrides\\.${overrideName}\\.functionAssociations`));
|
|
338
|
+
});
|
|
339
|
+
it("keeps the x-forwarded-host function next to an override's other associations", () => {
|
|
340
|
+
const { stack, distributionProps } = setup([]);
|
|
341
|
+
new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
342
|
+
...distributionProps,
|
|
343
|
+
overrides: {
|
|
344
|
+
dynamicBehaviorOptions: {
|
|
345
|
+
functionAssociations: [
|
|
346
|
+
userFn(stack, aws_cloudfront_1.FunctionEventType.VIEWER_RESPONSE),
|
|
347
|
+
],
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
});
|
|
351
|
+
const config = Object.values(assertions_1.Template.fromStack(stack).findResources("AWS::CloudFront::Distribution"))[0].Properties.DistributionConfig;
|
|
352
|
+
const eventTypes = config.DefaultCacheBehavior.FunctionAssociations.map((association) => association.EventType);
|
|
353
|
+
expect(eventTypes.sort()).toEqual(["viewer-request", "viewer-response"]);
|
|
354
|
+
});
|
|
355
|
+
it("rewrites an assetPrefix back onto the basePath S3 keys", () => {
|
|
356
|
+
const { stack, distributionProps } = setup([], { basePath: "/base" });
|
|
357
|
+
new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
358
|
+
...distributionProps,
|
|
359
|
+
assetPrefix: "/cdn",
|
|
360
|
+
});
|
|
361
|
+
expect(pathPatterns(stack)).toContain("/cdn/_next/static*");
|
|
362
|
+
const fns = assertions_1.Template.fromStack(stack).findResources("AWS::CloudFront::Function");
|
|
363
|
+
const code = Object.values(fns)
|
|
364
|
+
.map((fn) => fn.Properties.FunctionCode)
|
|
365
|
+
.find((it) => it.includes("request.uri.slice"));
|
|
366
|
+
// The objects are at `base/_next/static/...`: `assetPrefix` replaces the
|
|
367
|
+
// `basePath` prefix in the URL, so the rewrite has to put it back.
|
|
368
|
+
expect(code).toContain('"/base" + request.uri.slice(4)');
|
|
369
|
+
});
|
|
370
|
+
it("serves bundles under the path an absolute assetPrefix carries", () => {
|
|
371
|
+
// `next build` compiles a `/custom-asset-prefix/_next/:path+ → /_next/:path+`
|
|
372
|
+
// rewrite of its own for an absolute prefix with a path, so `next start` serves
|
|
373
|
+
// every bundle under that path as well. A CDN fronting this distribution there
|
|
374
|
+
// gets the same request, and without a behavior it reaches the compute origin
|
|
375
|
+
// and 404s. Measured against `test/e2e/app-dir/asset-prefix-absolute`, whose
|
|
376
|
+
// one case failed on exactly that.
|
|
377
|
+
for (const assetPrefix of [
|
|
378
|
+
"https://example.vercel.sh/custom-asset-prefix",
|
|
379
|
+
"//example.vercel.sh/custom-asset-prefix/",
|
|
380
|
+
]) {
|
|
381
|
+
const { stack, distributionProps } = setup([]);
|
|
382
|
+
new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
383
|
+
...distributionProps,
|
|
384
|
+
assetPrefix,
|
|
385
|
+
});
|
|
386
|
+
expect(pathPatterns(stack)).toContain("/custom-asset-prefix/_next/static*");
|
|
387
|
+
const code = Object.values(assertions_1.Template.fromStack(stack).findResources("AWS::CloudFront::Function"))
|
|
388
|
+
.map((fn) => fn.Properties.FunctionCode)
|
|
389
|
+
.find((it) => it.includes("request.uri.slice"));
|
|
390
|
+
expect(code).toContain('"" + request.uri.slice(20)');
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
it("adds no behavior for an assetPrefix that needs none", () => {
|
|
394
|
+
// An absolute prefix with no path of its own names an origin this distribution
|
|
395
|
+
// does not serve, and a prefix equal to the basePath one is what Next.js
|
|
396
|
+
// defaults to when `basePath` is set — `_next/static*` already resolves under
|
|
397
|
+
// it, and a duplicate pattern would make CloudFront reject the distribution.
|
|
398
|
+
for (const [assetPrefix, basePath] of [
|
|
399
|
+
["https://cdn.example.com", undefined],
|
|
400
|
+
["https://cdn.example.com/", undefined],
|
|
401
|
+
["//cdn.example.com", undefined],
|
|
402
|
+
["/base", "/base"],
|
|
403
|
+
["https://cdn.example.com/base", "/base"],
|
|
404
|
+
["", undefined],
|
|
405
|
+
]) {
|
|
406
|
+
const { stack, distributionProps } = setup([], { basePath });
|
|
407
|
+
new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
408
|
+
...distributionProps,
|
|
409
|
+
assetPrefix,
|
|
410
|
+
});
|
|
411
|
+
const patterns = pathPatterns(stack);
|
|
412
|
+
expect(patterns.filter((p) => p.includes("_next/static"))).toHaveLength(1);
|
|
413
|
+
// The x-forwarded-host function is always there on function compute; the
|
|
414
|
+
// rewrite one should not be.
|
|
415
|
+
const codes = Object.values(assertions_1.Template.fromStack(stack).findResources("AWS::CloudFront::Function")).map((fn) => fn.Properties.FunctionCode);
|
|
416
|
+
expect(codes.some((it) => it.includes("request.uri.slice"))).toBe(false);
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
it("counts the assetPrefix behavior against the budget", () => {
|
|
420
|
+
const { stack, distributionProps } = setup([], {
|
|
421
|
+
publicDirEntries: Array.from({ length: 22 }, (_, i) => `file${i}.txt`),
|
|
422
|
+
});
|
|
423
|
+
// 3 fixed + 22 public = 25, exactly the limit; the assetPrefix one is the
|
|
424
|
+
// 26th, and a limit error that did not count it would come as a deploy
|
|
425
|
+
// failure instead.
|
|
426
|
+
expect(() => new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
427
|
+
...distributionProps,
|
|
428
|
+
assetPrefix: "/cdn",
|
|
429
|
+
maxCacheBehaviors: 25,
|
|
430
|
+
})).toThrow(/26 CloudFront cache behaviors.*4 used by cdk-nextjs itself/s);
|
|
431
|
+
});
|
|
432
|
+
it("does not cache a dynamic response that sends no Cache-Control", () => {
|
|
433
|
+
// CDK's `CachePolicy` default is a day, which cached every route handler
|
|
434
|
+
// without a `Cache-Control` - `/api/echo` in app-playground - for 24 hours.
|
|
435
|
+
const { stack, distributionProps } = setup([]);
|
|
436
|
+
new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", distributionProps);
|
|
437
|
+
assertions_1.Template.fromStack(stack).hasResourceProperties("AWS::CloudFront::CachePolicy", {
|
|
438
|
+
CachePolicyConfig: assertions_1.Match.objectLike({
|
|
439
|
+
Comment: assertions_1.Match.stringLikeRegexp("Dynamic"),
|
|
440
|
+
DefaultTTL: 0,
|
|
441
|
+
MinTTL: 0,
|
|
442
|
+
}),
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
it("wildcards a public/ name CloudFront cannot spell", () => {
|
|
446
|
+
// `public/hello world.jpg` is a valid Next.js asset, but a space cannot appear
|
|
447
|
+
// in a path pattern, so cdk-nextjs threw at synth and the app could not be
|
|
448
|
+
// deployed at all. CloudFront matches the *decoded* path, and `?` matches one
|
|
449
|
+
// byte of it, so one `?` per UTF-8 byte is the narrowest pattern that
|
|
450
|
+
// matches. Not one per percent-encoded character: `hello???world.jpg`
|
|
451
|
+
// deploys and then never matches, which is a 404 rather than a synth error.
|
|
452
|
+
const { stack, distributionProps } = setup([], {
|
|
453
|
+
publicDirEntries: ["hello world.jpg", "äöüščří.png", "favicon.ico"],
|
|
454
|
+
});
|
|
455
|
+
new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", distributionProps);
|
|
456
|
+
expect(pathPatterns(stack)).toEqual(expect.arrayContaining([
|
|
457
|
+
"hello?world.jpg",
|
|
458
|
+
// 7 characters, 2 UTF-8 bytes each.
|
|
459
|
+
`${"?".repeat(14)}.png`,
|
|
460
|
+
"favicon.ico",
|
|
461
|
+
]));
|
|
462
|
+
});
|
|
463
|
+
it("wildcards a public/ directory name the same way", () => {
|
|
464
|
+
const { stack, distributionProps } = setup([]);
|
|
465
|
+
new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
466
|
+
...distributionProps,
|
|
467
|
+
publicDirEntries: [{ name: "my images", isDirectory: true }],
|
|
468
|
+
});
|
|
469
|
+
expect(pathPatterns(stack)).toContain("my?images/*");
|
|
470
|
+
});
|
|
471
|
+
it("rejects a public/ name too long to express as a path pattern", () => {
|
|
472
|
+
const { stack, distributionProps } = setup([], {
|
|
473
|
+
publicDirEntries: [`${"ä".repeat(130)}.png`],
|
|
474
|
+
});
|
|
475
|
+
expect(() => new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", distributionProps)).toThrow(/needs a 264-character CloudFront path pattern/);
|
|
476
|
+
});
|
|
477
|
+
it("skips a public/ entry whose pattern would be wildcards alone", () => {
|
|
478
|
+
// `фото/` is `????????/*`, which matches any 8-byte first segment. Public
|
|
479
|
+
// behaviors are added ahead of the compute ones, so `/products/42` would go
|
|
480
|
+
// to S3 and 403. The entry gets no behavior and a warning instead.
|
|
481
|
+
const { stack, distributionProps } = setup([]);
|
|
482
|
+
new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
483
|
+
...distributionProps,
|
|
484
|
+
publicDirEntries: [
|
|
485
|
+
{ name: "фото", isDirectory: true },
|
|
486
|
+
{ name: "图", isDirectory: false },
|
|
487
|
+
{ name: "фото-1", isDirectory: true },
|
|
488
|
+
],
|
|
489
|
+
});
|
|
490
|
+
const patterns = pathPatterns(stack);
|
|
491
|
+
expect(patterns).not.toContain(`${"?".repeat(8)}/*`);
|
|
492
|
+
expect(patterns).not.toContain("???");
|
|
493
|
+
// One literal character keeps a pattern narrow enough to keep.
|
|
494
|
+
expect(patterns).toContain(`${"?".repeat(8)}-1/*`);
|
|
495
|
+
assertions_1.Annotations.fromStack(stack).hasWarning("*", assertions_1.Match.stringLikeRegexp('"фото", "图"'));
|
|
496
|
+
});
|
|
497
|
+
it("does not count a skipped public/ entry against the budget", () => {
|
|
498
|
+
const { stack, distributionProps } = setup([], {
|
|
499
|
+
publicDirEntries: [
|
|
500
|
+
...Array.from({ length: 22 }, (_, i) => `file${i}.txt`),
|
|
501
|
+
"图",
|
|
502
|
+
],
|
|
503
|
+
});
|
|
504
|
+
// 3 fixed + 22 = 25, exactly the limit; the skipped entry adds nothing.
|
|
505
|
+
expect(() => new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
506
|
+
...distributionProps,
|
|
507
|
+
maxCacheBehaviors: 25,
|
|
508
|
+
})).not.toThrow();
|
|
509
|
+
});
|
|
510
|
+
it("checks the length of the final pattern, basePath and /* included", () => {
|
|
511
|
+
// 83 three-byte characters + ".pdf" is a 253-character pattern on its own,
|
|
512
|
+
// under the limit; `/docs/` in front takes it to 259, which CloudFront
|
|
513
|
+
// rejects at deploy.
|
|
514
|
+
const name = `${"图".repeat(83)}.pdf`;
|
|
515
|
+
const { stack, distributionProps } = setup([], {
|
|
516
|
+
publicDirEntries: [name],
|
|
517
|
+
});
|
|
518
|
+
expect(() => new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
519
|
+
...distributionProps,
|
|
520
|
+
basePath: "/docs",
|
|
521
|
+
})).toThrow(/needs a 259-character CloudFront path pattern/);
|
|
522
|
+
const { stack: dirStack, distributionProps: dirProps } = setup([]);
|
|
523
|
+
expect(() => new nextjs_distribution_1.NextjsDistribution(dirStack, "Distribution", {
|
|
524
|
+
...dirProps,
|
|
525
|
+
// 254 characters, under the limit until the `/*`.
|
|
526
|
+
publicDirEntries: [
|
|
527
|
+
{ name: `ab${"图".repeat(84)}`, isDirectory: true },
|
|
528
|
+
],
|
|
529
|
+
})).toThrow(/needs a 256-character/);
|
|
530
|
+
});
|
|
531
|
+
it("drops the cache key when an override disables dynamic caching", () => {
|
|
532
|
+
// CloudFront rejects a policy that caches nothing but keys on headers. CDK
|
|
533
|
+
// used to raise `maxTtl: 0` to the day-long default, so the override
|
|
534
|
+
// deployed; with a default of 0 it is honored, and must stay deployable.
|
|
535
|
+
const { stack, distributionProps } = setup([]);
|
|
536
|
+
new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
537
|
+
...distributionProps,
|
|
538
|
+
overrides: {
|
|
539
|
+
dynamicCachePolicyProps: { maxTtl: aws_cdk_lib_1.Duration.seconds(0) },
|
|
540
|
+
},
|
|
541
|
+
});
|
|
542
|
+
assertions_1.Template.fromStack(stack).hasResourceProperties("AWS::CloudFront::CachePolicy", {
|
|
543
|
+
CachePolicyConfig: assertions_1.Match.objectLike({
|
|
544
|
+
Comment: assertions_1.Match.stringLikeRegexp("Dynamic"),
|
|
545
|
+
DefaultTTL: 0,
|
|
546
|
+
MaxTTL: 0,
|
|
547
|
+
MinTTL: 0,
|
|
548
|
+
ParametersInCacheKeyAndForwardedToOrigin: {
|
|
549
|
+
CookiesConfig: { CookieBehavior: "none" },
|
|
550
|
+
EnableAcceptEncodingBrotli: false,
|
|
551
|
+
EnableAcceptEncodingGzip: false,
|
|
552
|
+
HeadersConfig: { HeaderBehavior: "none" },
|
|
553
|
+
QueryStringsConfig: { QueryStringBehavior: "none" },
|
|
554
|
+
},
|
|
555
|
+
}),
|
|
556
|
+
});
|
|
557
|
+
});
|
|
558
|
+
it("keeps the cache key when a zero maxTtl is raised by another TTL", () => {
|
|
559
|
+
// CDK raises `maxTtl` to `defaultTtl`, so this policy caches for an hour.
|
|
560
|
+
// Dropping the key for it would serve one viewer's page to every viewer.
|
|
561
|
+
const { stack, distributionProps } = setup([]);
|
|
562
|
+
new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
563
|
+
...distributionProps,
|
|
564
|
+
overrides: {
|
|
565
|
+
dynamicCachePolicyProps: {
|
|
566
|
+
maxTtl: aws_cdk_lib_1.Duration.seconds(0),
|
|
567
|
+
defaultTtl: aws_cdk_lib_1.Duration.hours(1),
|
|
568
|
+
},
|
|
569
|
+
},
|
|
570
|
+
});
|
|
571
|
+
assertions_1.Template.fromStack(stack).hasResourceProperties("AWS::CloudFront::CachePolicy", {
|
|
572
|
+
CachePolicyConfig: assertions_1.Match.objectLike({
|
|
573
|
+
Comment: assertions_1.Match.stringLikeRegexp("Dynamic"),
|
|
574
|
+
MaxTTL: 3600,
|
|
575
|
+
ParametersInCacheKeyAndForwardedToOrigin: assertions_1.Match.objectLike({
|
|
576
|
+
CookiesConfig: { CookieBehavior: "all" },
|
|
577
|
+
QueryStringsConfig: { QueryStringBehavior: "all" },
|
|
578
|
+
}),
|
|
579
|
+
}),
|
|
580
|
+
});
|
|
581
|
+
});
|
|
582
|
+
it("keys the dynamic cache only on headers the origin reads", () => {
|
|
583
|
+
// `x-matched-path` is stripped by the runtime and `x-next-cache-tags` is
|
|
584
|
+
// never sent by viewers: keying on either spends a header slot and lets a
|
|
585
|
+
// client fragment the cache for nothing.
|
|
586
|
+
const { stack, distributionProps } = setup([]);
|
|
587
|
+
new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", distributionProps);
|
|
588
|
+
const policies = assertions_1.Template.fromStack(stack).findResources("AWS::CloudFront::CachePolicy", {
|
|
589
|
+
Properties: {
|
|
590
|
+
CachePolicyConfig: { Comment: assertions_1.Match.stringLikeRegexp("Dynamic") },
|
|
591
|
+
},
|
|
592
|
+
});
|
|
593
|
+
const [policy] = Object.values(policies);
|
|
594
|
+
const headers = policy.Properties.CachePolicyConfig
|
|
595
|
+
.ParametersInCacheKeyAndForwardedToOrigin.HeadersConfig.Headers;
|
|
596
|
+
expect(headers).not.toContain("x-matched-path");
|
|
597
|
+
expect(headers).not.toContain("x-next-cache-tags");
|
|
598
|
+
expect(headers).toContain("rsc");
|
|
599
|
+
});
|
|
600
|
+
it("rejects splitting on a deployment type that cannot route it", () => {
|
|
601
|
+
const { stack, functionGroups, functionGroupBehaviors, distributionProps } = setup(["api"]);
|
|
602
|
+
expect(() => new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
603
|
+
...distributionProps,
|
|
604
|
+
nextjsType: constants_1.NextjsType.GLOBAL_CONTAINERS,
|
|
605
|
+
loadBalancer: undefined,
|
|
606
|
+
functionGroups,
|
|
607
|
+
functionGroupBehaviors,
|
|
608
|
+
})).toThrow(/only supported by NextjsGlobalFunctions/);
|
|
609
|
+
});
|
|
610
|
+
});
|
|
611
|
+
/**
|
|
612
|
+
* The viewer-request function, run as CloudFront would run it.
|
|
613
|
+
*
|
|
614
|
+
* It is authored as a string inside a TypeScript template literal, so every
|
|
615
|
+
* backslash in it is escaped twice - and a regex that collapses slashes is nothing
|
|
616
|
+
* but backslashes. Asserting on behavior rather than on the text is what catches an
|
|
617
|
+
* escaping mistake, which a snapshot would happily record.
|
|
618
|
+
*/
|
|
619
|
+
describe("the viewer-request CloudFront Function", () => {
|
|
620
|
+
const handler = (() => {
|
|
621
|
+
const stack = new aws_cdk_lib_1.Stack(new aws_cdk_lib_1.App(), "FnStack");
|
|
622
|
+
const fn = new aws_lambda_1.Function(stack, "Fn", {
|
|
623
|
+
code: aws_lambda_1.Code.fromInline("exports.handler = () => {};"),
|
|
624
|
+
handler: "index.handler",
|
|
625
|
+
runtime: aws_lambda_1.Runtime.NODEJS_22_X,
|
|
626
|
+
});
|
|
627
|
+
new nextjs_distribution_1.NextjsDistribution(stack, "Distribution", {
|
|
628
|
+
assetsBucket: new aws_s3_1.Bucket(stack, "Assets"),
|
|
629
|
+
functionUrl: new aws_lambda_1.FunctionUrl(stack, "Url", {
|
|
630
|
+
function: fn,
|
|
631
|
+
authType: aws_lambda_1.FunctionUrlAuthType.AWS_IAM,
|
|
632
|
+
}),
|
|
633
|
+
nextjsType: constants_1.NextjsType.GLOBAL_FUNCTIONS,
|
|
634
|
+
publicDirEntries: [],
|
|
635
|
+
});
|
|
636
|
+
const functions = assertions_1.Template.fromStack(stack).findResources("AWS::CloudFront::Function");
|
|
637
|
+
const code = Object.values(functions)[0].Properties.FunctionCode;
|
|
638
|
+
return (0, node_vm_1.runInNewContext)(`${code}\nhandler`);
|
|
639
|
+
})();
|
|
640
|
+
const send = (uri, querystring = {}) => handler({
|
|
641
|
+
request: { uri, querystring, headers: { host: { value: "a.test" } } },
|
|
642
|
+
});
|
|
643
|
+
const redirect = (result) => ({
|
|
644
|
+
statusCode: result.statusCode,
|
|
645
|
+
location: result.headers.location?.value,
|
|
646
|
+
});
|
|
647
|
+
it.each([
|
|
648
|
+
["//", "/"],
|
|
649
|
+
["///", "/"],
|
|
650
|
+
["/foo//bar", "/foo/bar"],
|
|
651
|
+
["/basepath//to-sv", "/basepath/to-sv"],
|
|
652
|
+
["/a\\b", "/a/b"],
|
|
653
|
+
])("redirects %s to %s with a 308", (uri, location) => {
|
|
654
|
+
expect(redirect(send(uri))).toEqual({ statusCode: 308, location });
|
|
655
|
+
});
|
|
656
|
+
it("keeps the query on the redirect, including repeated keys", () => {
|
|
657
|
+
// CloudFront's shape for a repeated key: `multiValue` holds every value,
|
|
658
|
+
// the first included, and `value` repeats that first one.
|
|
659
|
+
const result = send("/x//y", {
|
|
660
|
+
json: { value: "true" },
|
|
661
|
+
a: { value: "1", multiValue: [{ value: "1" }, { value: "2" }] },
|
|
662
|
+
flag: { value: "" },
|
|
663
|
+
});
|
|
664
|
+
expect(redirect(result).location).toBe("/x/y?json=true&a=1&a=2&flag");
|
|
665
|
+
});
|
|
666
|
+
it("passes percent-encoded query values through without re-encoding", () => {
|
|
667
|
+
// CloudFront hands the function the values still encoded, as sent.
|
|
668
|
+
const result = send("/x//y", {
|
|
669
|
+
a: { value: "%20b%26c" },
|
|
670
|
+
p: { value: "1+2" },
|
|
671
|
+
});
|
|
672
|
+
expect(redirect(result).location).toBe("/x/y?a=%20b%26c&p=1+2");
|
|
673
|
+
});
|
|
674
|
+
it("passes an ordinary path through with x-forwarded-host set", () => {
|
|
675
|
+
const result = send("/a/b");
|
|
676
|
+
expect(result.uri).toBe("/a/b");
|
|
677
|
+
// CloudFront rewrites `host` to the origin domain, so the app would otherwise
|
|
678
|
+
// build every absolute URL against the Function URL's hostname.
|
|
679
|
+
expect(result.headers["x-forwarded-host"].value).toBe("a.test");
|
|
680
|
+
});
|
|
681
|
+
it("overwrites a client-supplied x-forwarded-host", () => {
|
|
682
|
+
// The runtime trusts this header behind the Function URL, so a viewer's
|
|
683
|
+
// own value must not survive to build redirects or origin checks.
|
|
684
|
+
const result = handler({
|
|
685
|
+
request: {
|
|
686
|
+
uri: "/a",
|
|
687
|
+
querystring: {},
|
|
688
|
+
headers: {
|
|
689
|
+
host: { value: "a.test" },
|
|
690
|
+
"x-forwarded-host": { value: "evil.test" },
|
|
691
|
+
},
|
|
692
|
+
},
|
|
693
|
+
});
|
|
694
|
+
expect(result.headers["x-forwarded-host"].value).toBe("a.test");
|
|
695
|
+
});
|
|
696
|
+
});
|
|
697
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmV4dGpzLWRpc3RyaWJ1dGlvbi50ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL25leHRqcy1kaXN0cmlidXRpb24udGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLHFDQUEwQztBQUMxQyw2Q0FBbUQ7QUFDbkQsdURBQXNFO0FBQ3RFLCtEQUlvQztBQUNwQyx1REFNZ0M7QUFDaEMsK0NBQTRDO0FBQzVDLCtEQUE0RDtBQUM1RCwyQ0FBeUM7QUFDekMsK0RBRytCO0FBRS9COzs7OztHQUtHO0FBQ0gsUUFBUSxDQUFDLDZDQUE2QyxFQUFFLEdBQUcsRUFBRTtJQUMzRCxNQUFNLEtBQUssR0FBRyxDQUNaLFVBQW9CLEVBQ3BCLFFBS0ksRUFBRSxFQUNOLEVBQUU7UUFDRixNQUFNLEtBQUssR0FBRyxJQUFJLG1CQUFLLENBQUMsSUFBSSxpQkFBRyxFQUFFLEVBQUUsV0FBVyxDQUFDLENBQUM7UUFDaEQsTUFBTSxjQUFjLEdBQXNDLFVBQVUsQ0FBQyxHQUFHLENBQ3RFLENBQUMsSUFBSSxFQUFFLEVBQUU7WUFDUCxNQUFNLEVBQUUsR0FBRyxJQUFJLHFCQUFjLENBQUMsS0FBSyxFQUFFLEtBQUssSUFBSSxFQUFFLEVBQUU7Z0JBQ2hELElBQUksRUFBRSxpQkFBSSxDQUFDLFVBQVUsQ0FBQyw2QkFBNkIsQ0FBQztnQkFDcEQsT0FBTyxFQUFFLGVBQWU7Z0JBQ3hCLE9BQU8sRUFBRSxvQkFBTyxDQUFDLFdBQVc7YUFDN0IsQ0FBQyxDQUFDO1lBQ0gsT0FBTztnQkFDTCxJQUFJO2dCQUNKLFdBQVcsRUFBRSxJQUFJLHdCQUFXLENBQUMsS0FBSyxFQUFFLE1BQU0sSUFBSSxFQUFFLEVBQUU7b0JBQ2hELFFBQVEsRUFBRSxFQUFFO29CQUNaLFFBQVEsRUFBRSxnQ0FBbUIsQ0FBQyxPQUFPO2lCQUN0QyxDQUFDO2FBQ0gsQ0FBQztRQUNKLENBQUMsQ0FDRixDQUFDO1FBQ0YsdUVBQXVFO1FBQ3ZFLE1BQU0sc0JBQXNCLEdB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QUFRLENBQUMsQ0FBQztJQUNsRSxDQUFDLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgcnVuSW5OZXdDb250ZXh0IH0gZnJvbSBcIm5vZGU6dm1cIjtcbmltcG9ydCB7IEFwcCwgRHVyYXRpb24sIFN0YWNrIH0gZnJvbSBcImF3cy1jZGstbGliXCI7XG5pbXBvcnQgeyBBbm5vdGF0aW9ucywgTWF0Y2gsIFRlbXBsYXRlIH0gZnJvbSBcImF3cy1jZGstbGliL2Fzc2VydGlvbnNcIjtcbmltcG9ydCB7XG4gIEZ1bmN0aW9uIGFzIENsb3VkRnJvbnRGdW5jdGlvbixcbiAgRnVuY3Rpb25Db2RlLFxuICBGdW5jdGlvbkV2ZW50VHlwZSxcbn0gZnJvbSBcImF3cy1jZGstbGliL2F3cy1jbG91ZGZyb250XCI7XG5pbXBvcnQge1xuICBDb2RlLFxuICBGdW5jdGlvbiBhcyBMYW1iZGFGdW5jdGlvbixcbiAgRnVuY3Rpb25VcmwsXG4gIEZ1bmN0aW9uVXJsQXV0aFR5cGUsXG4gIFJ1bnRpbWUsXG59IGZyb20gXCJhd3MtY2RrLWxpYi9hd3MtbGFtYmRhXCI7XG5pbXBvcnQgeyBCdWNrZXQgfSBmcm9tIFwiYXdzLWNkay1saWIvYXdzLXMzXCI7XG5pbXBvcnQgeyBwYXRoUGF0dGVybnNGb3IgfSBmcm9tIFwiLi9hZGFwdGVyL2Z1bmN0aW9uLWdyb3Vwc1wiO1xuaW1wb3J0IHsgTmV4dGpzVHlwZSB9IGZyb20gXCIuL2NvbnN0YW50c1wiO1xuaW1wb3J0IHtcbiAgTmV4dGpzRGlzdHJpYnV0aW9uLFxuICBOZXh0anNEaXN0cmlidXRpb25GdW5jdGlvbkdyb3VwLFxufSBmcm9tIFwiLi9uZXh0anMtZGlzdHJpYnV0aW9uXCI7XG5cbi8qKlxuICogYGZ1bmN0aW9uR3JvdXBzYCByb3V0aW5nIGF0IHRoZSBlZGdlLiBgTmV4dGpzQnVpbGRgIGlzIGRlbGliZXJhdGVseSBub3RcbiAqIGludm9sdmVkOiB3aGljaCBiZWhhdmlvcnMgYSBncm91cCBnZXRzLCBhbmQgaW4gd2hhdCBvcmRlciwgaXMgdGhlIGFkYXB0ZXInc1xuICogZGVjaXNpb24gKGBmdW5jdGlvbi1ncm91cHMudGVzdC50c2ApLCBzbyB0aGVzZSBwYXNzIHRoZSBiZWhhdmlvcnMgaW4gZGlyZWN0bHlcbiAqIGFuZCBhc3NlcnQgb24gd2hhdCB0aGUgZGlzdHJpYnV0aW9uIGRvZXMgd2l0aCB0aGVtLlxuICovXG5kZXNjcmliZShcIk5leHRqc0Rpc3RyaWJ1dGlvbiBmdW5jdGlvbiBncm91cCBiZWhhdmlvcnNcIiwgKCkgPT4ge1xuICBjb25zdCBzZXR1cCA9IChcbiAgICBncm91cE5hbWVzOiBzdHJpbmdbXSxcbiAgICBwcm9wczoge1xuICAgICAgcm91dGVzRm9yPzogKG5hbWU6IHN0cmluZykgPT4gc3RyaW5nW107XG4gICAgICBwdWJsaWNEaXJFbnRyaWVzPzogc3RyaW5nW107XG4gICAgICBiYXNlUGF0aD86IHN0cmluZztcbiAgICAgIHRyYWlsaW5nU2xhc2g/OiBib29sZWFuO1xuICAgIH0gPSB7fSxcbiAgKSA9PiB7XG4gICAgY29uc3Qgc3RhY2sgPSBuZXcgU3RhY2sobmV3IEFwcCgpLCBcIlRlc3RTdGFja1wiKTtcbiAgICBjb25zdCBmdW5jdGlvbkdyb3VwczogTmV4dGpzRGlzdHJpYnV0aW9uRnVuY3Rpb25Hcm91cFtdID0gZ3JvdXBOYW1lcy5tYXAoXG4gICAgICAobmFtZSkgPT4ge1xuICAgICAgICBjb25zdCBmbiA9IG5ldyBMYW1iZGFGdW5jdGlvbihzdGFjaywgYEZuJHtuYW1lfWAsIHtcbiAgICAgICAgICBjb2RlOiBDb2RlLmZyb21JbmxpbmUoXCJleHBvcnRzLmhhbmRsZXIgPSAoKSA9PiB7fTtcIiksXG4gICAgICAgICAgaGFuZGxlcjogXCJpbmRleC5oYW5kbGVyXCIsXG4gICAgICAgICAgcnVudGltZTogUnVudGltZS5OT0RFSlNfMjJfWCxcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgbmFtZSxcbiAgICAgICAgICBmdW5jdGlvblVybDogbmV3IEZ1bmN0aW9uVXJsKHN0YWNrLCBgVXJsJHtuYW1lfWAsIHtcbiAgICAgICAgICAgIGZ1bmN0aW9uOiBmbixcbiAgICAgICAgICAgIGF1dGhUeXBlOiBGdW5jdGlvblVybEF1dGhUeXBlLkFXU19JQU0sXG4gICAgICAgICAgfSksXG4gICAgICAgIH07XG4gICAgICB9LFxuICAgICk7XG4gICAgLy8gSW4gdGhlIG9yZGVyIGdpdmVuOiB0aGUgYnVpbGQncyBsaXN0IGlzIGFscmVhZHkgbW9zdCBzcGVjaWZpYyBmaXJzdC5cbiAgICBjb25zdCBmdW5jdGlvbkdyb3VwQmVoYXZpb3JzID0gZ3JvdXBOYW1lcy5mbGF0TWFwKChncm91cCkgPT5cbiAgICAgIChwcm9wcy5yb3V0ZXNGb3I/Lihncm91cCkgPz8gW2AvJHtncm91cH0vKipgXSkuZmxhdE1hcCgocm91dGUpID0+XG4gICAgICAgIHBhdGhQYXR0ZXJuc0Zvcihyb3V0ZSwge1xuICAgICAgICAgIGhhc0RhdGFSb3V0ZXM6IGZhbHNlLFxuICAgICAgICAgIHRyYWlsaW5nU2xhc2g6IHByb3BzLnRyYWlsaW5nU2xhc2gsXG4gICAgICAgIH0pLm1hcCgocGF0dGVybikgPT4gKHsgZ3JvdXAsIHJvdXRlLCBwYXR0ZXJuIH0pKSxcbiAgICAgICksXG4gICAgKTtcbiAgICBjb25zdCBkZWZhdWx0Rm4gPSBuZXcgTGFtYmRhRnVuY3Rpb24oc3RhY2ssIFwiRm5EZWZhdWx0XCIsIHtcbiAgICAgIGNvZGU6IENvZGUuZnJvbUlubGluZShcImV4cG9ydHMuaGFuZGxlciA9ICgpID0+IHt9O1wiKSxcbiAgICAgIGhhbmRsZXI6IFwiaW5kZXguaGFuZGxlclwiLFxuICAgICAgcnVudGltZTogUnVudGltZS5OT0RFSlNfMjJfWCxcbiAgICB9KTtcbiAgICByZXR1cm4ge1xuICAgICAgc3RhY2ssXG4gICAgICBmdW5jdGlvbkdyb3VwcyxcbiAgICAgIGZ1bmN0aW9uR3JvdXBCZWhhdmlvcnMsXG4gICAgICBkaXN0cmlidXRpb25Qcm9wczoge1xuICAgICAgICBhc3NldHNCdWNrZXQ6IG5ldyBCdWNrZXQoc3RhY2ssIFwiQXNzZXRzXCIpLFxuICAgICAgICBiYXNlUGF0aDogcHJvcHMuYmFzZVBhdGgsXG4gICAgICAgIGZ1bmN0aW9uVXJsOiBuZXcgRnVuY3Rpb25Vcmwoc3RhY2ssIFwiVXJsRGVmYXVsdFwiLCB7XG4gICAgICAgICAgZnVuY3Rpb246IGRlZmF1bHRGbixcbiAgICAgICAgICBhdXRoVHlwZTogRnVuY3Rpb25VcmxBdXRoVHlwZS5BV1NfSUFNLFxuICAgICAgICB9KSxcbiAgICAgICAgbmV4dGpzVHlwZTogTmV4dGpzVHlwZS5HTE9CQUxfRlVOQ1RJT05TLFxuICAgICAgICBwdWJsaWNEaXJFbnRyaWVzOiAocHJvcHMucHVibGljRGlyRW50cmllcyA/PyBbXSkubWFwKChuYW1lKSA9PiAoe1xuICAgICAgICAgIG5hbWUsXG4gICAgICAgICAgaXNEaXJlY3Rvcnk6IGZhbHNlLFxuICAgICAgICB9KSksXG4gICAgICB9LFxuICAgIH07XG4gIH07XG5cbiAgY29uc3QgcGF0aFBhdHRlcm5zID0gKHN0YWNrOiBTdGFjaykgPT4ge1xuICAgIGNvbnN0IGRpc3RyaWJ1dGlvbnMgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spLmZpbmRSZXNvdXJjZXMoXG4gICAgICBcIkFXUzo6Q2xvdWRGcm9udDo6RGlzdHJpYnV0aW9uXCIsXG4gICAgKTtcbiAgICBjb25zdCBjb25maWcgPVxuICAgICAgT2JqZWN0LnZhbHVlcyhkaXN0cmlidXRpb25zKVswXS5Qcm9wZXJ0aWVzLkRpc3RyaWJ1dGlvbkNvbmZpZztcbiAgICByZXR1cm4gKGNvbmZpZy5DYWNoZUJlaGF2aW9ycyBhcyBBcnJheTx7IFBhdGhQYXR0ZXJuOiBzdHJpbmcgfT4pLm1hcChcbiAgICAgIChiZWhhdmlvcikgPT4gYmVoYXZpb3IuUGF0aFBhdHRlcm4sXG4gICAgKTtcbiAgfTtcblxuICBpdChcImFkZHMgbm90aGluZyBleHRyYSB3aGVuIHRoZXJlIGFyZSBubyBncm91cHNcIiwgKCkgPT4ge1xuICAgIGNvbnN0IHsgc3RhY2ssIGRpc3RyaWJ1dGlvblByb3BzIH0gPSBzZXR1cChbXSk7XG4gICAgbmV3IE5leHRqc0Rpc3RyaWJ1dGlvbihzdGFjaywgXCJEaXN0cmlidXRpb25cIiwgZGlzdHJpYnV0aW9uUHJvcHMpO1xuICAgIGV4cGVjdChwYXRoUGF0dGVybnMoc3RhY2spKS50b0VxdWFsKFtcIl9uZXh0L3N0YXRpYypcIiwgXCJfbmV4dC9pbWFnZSpcIl0pO1xuICB9KTtcblxuICBpdChcImxldHMgdGhlIE9BQyBwcmluY2lwYWwgbGlzdCB0aGUgYnVja2V0IHNvIGEgbWlzc2luZyBhc3NldCBpcyBhIDQwNFwiLCAoKSA9PiB7XG4gICAgY29uc3QgeyBzdGFjaywgZGlzdHJpYnV0aW9uUHJvcHMgfSA9IHNldHVwKFtdKTtcbiAgICBuZXcgTmV4dGpzRGlzdHJpYnV0aW9uKHN0YWNrLCBcIkRpc3RyaWJ1dGlvblwiLCBkaXN0cmlidXRpb25Qcm9wcyk7XG4gICAgVGVtcGxhdGUuZnJvbVN0YWNrKHN0YWNrKS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoXCJBV1M6OlMzOjpCdWNrZXRQb2xpY3lcIiwge1xuICAgICAgUG9saWN5RG9jdW1lbnQ6IHtcbiAgICAgICAgU3RhdGVtZW50OiBNYXRjaC5hcnJheVdpdGgoW1xuICAgICAgICAgIE1hdGNoLm9iamVjdExpa2Uoe1xuICAgICAgICAgICAgQWN0aW9uOiBbXCJzMzpHZXRPYmplY3RcIiwgXCJzMzpMaXN0QnVja2V0XCJdLFxuICAgICAgICAgICAgUHJpbmNpcGFsOiB7IFNlcnZpY2U6IFwiY2xvdWRmcm9udC5hbWF6b25hd3MuY29tXCIgfSxcbiAgICAgICAgICB9KSxcbiAgICAgICAgXSksXG4gICAgICB9LFxuICAgIH0pO1xuICB9KTtcblxuICBpdChcImFkZHMgZ3JvdXAgYmVoYXZpb3JzIGluIHRoZSBidWlsZCdzIG9yZGVyLCBhZnRlciB0aGUgaW1hZ2UgYmVoYXZpb3JcIiwgKCkgPT4ge1xuICAgIC8vIENsb3VkRnJvbnQgc3RvcHMgYXQgdGhlIGZpcnN0IG1hdGNoaW5nIGJlaGF2aW9yLCBzbyBgYXBpLypgIGFoZWFkIG9mXG4gICAgLy8gYGFwaS9yZXBvcnRzLypgIHdvdWxkIHNlbmQgZXZlcnkgcmVwb3J0IHJlcXVlc3QgdG8gdGhlIHdyb25nIGZ1bmN0aW9uLlxuICAgIC8vIFRoZSBidWlsZCBzb3J0ZWQgdGhlbSB3aGVuIGl0IGNoZWNrZWQgdGhlbTsgcmUtc29ydGluZyBoZXJlIGNvdWxkIG9ubHlcbiAgICAvLyBkaXNhZ3JlZSB3aXRoIHRoYXQuXG4gICAgY29uc3QgeyBzdGFjaywgZnVuY3Rpb25Hcm91cHMsIGZ1bmN0aW9uR3JvdXBCZWhhdmlvcnMsIGRpc3RyaWJ1dGlvblByb3BzIH0gPVxuICAgICAgc2V0dXAoW1wicmVwb3J0c1wiLCBcImFwaVwiXSwge1xuICAgICAgICByb3V0ZXNGb3I6IChuYW1lKSA9PlxuICAgICAgICAgIG5hbWUgPT09IFwiYXBpXCIgPyBbXCIvYXBpLyoqXCJdIDogW1wiL2FwaS9yZXBvcnRzLyoqXCJdLFxuICAgICAgfSk7XG4gICAgbmV3IE5leHRqc0Rpc3RyaWJ1dGlvbihzdGFjaywgXCJEaXN0cmlidXRpb25cIiwge1xuICAgICAgLi4uZGlzdHJpYnV0aW9uUHJvcHMsXG4gICAgICBmdW5jdGlvbkdyb3VwcyxcbiAgICAgIGZ1bmN0aW9uR3JvdXBCZWhhdmlvcnMsXG4gICAgfSk7XG4gICAgZXhwZWN0KHBhdGhQYXR0ZXJucyhzdGFjaykpLnRvRXF1YWwoW1xuICAgICAgXCJfbmV4dC9zdGF0aWMqXCIsXG4gICAgICBcIl9uZXh0L2ltYWdlKlwiLFxuICAgICAgXCJhcGkvcmVwb3J0cy8qXCIsXG4gICAgICBcImFwaS8qXCIsXG4gICAgXSk7XG4gIH0pO1xuXG4gIGl0KFwia2VlcHMgdGhlIHR3byBzbGFzaCB2YXJpYW50cyBkaXN0aW5jdCB1bmRlciBhIGJhc2VQYXRoXCIsICgpID0+IHtcbiAgICAvLyBgZ2V0UGF0aFBhdHRlcm5gIHVzZWQgdG8gam9pbiB0aHJvdWdoIGEgaGVscGVyIHRoYXQgc3RyaXBzIHRyYWlsaW5nXG4gICAgLy8gc2xhc2hlcywgc28gYHByaWNpbmdgIGFuZCBgcHJpY2luZy9gIGJvdGggYmVjYW1lIGAvYmFzZS9wcmljaW5nYDogdHdvXG4gICAgLy8gYmVoYXZpb3JzIHdpdGggdGhlIHNhbWUgcGF0aCBwYXR0ZXJuLCB3aGljaCBDbG91ZEZyb250IHJlamVjdHMgYXQgZGVwbG95XG4gICAgLy8gd2l0aCBubyBoaW50IHRoYXQgYHRyYWlsaW5nU2xhc2hgIGlzIHdoYXQgcHJvZHVjZWQgdGhlIGR1cGxpY2F0ZS5cbiAgICBjb25zdCB7IHN0YWNrLCBmdW5jdGlvbkdyb3VwcywgZnVuY3Rpb25Hcm91cEJlaGF2aW9ycywgZGlzdHJpYnV0aW9uUHJvcHMgfSA9XG4gICAgICBzZXR1cChbXCJta3RcIl0sIHtcbiAgICAgICAgcm91dGVzRm9yOiAoKSA9PiBbXCIvcHJpY2luZ1wiXSxcbiAgICAgICAgYmFzZVBhdGg6IFwiL2Jhc2VcIixcbiAgICAgICAgdHJhaWxpbmdTbGFzaDogdHJ1ZSxcbiAgICAgIH0pO1xuICAgIG5ldyBOZXh0anNEaXN0cmlidXRpb24oc3RhY2ssIFwiRGlzdHJpYnV0aW9uXCIsIHtcbiAgICAgIC4uLmRpc3RyaWJ1dGlvblByb3BzLFxuICAgICAgZnVuY3Rpb25Hcm91cHMsXG4gICAgICBmdW5jdGlvbkdyb3VwQmVoYXZpb3JzLFxuICAgIH0pO1xuICAgIGNvbnN0IHBhdHRlcm5zID0gcGF0aFBhdHRlcm5zKHN0YWNrKTtcbiAgICBleHBlY3QocGF0dGVybnMpLnRvQ29udGFpbihcIi9iYXNlL3ByaWNpbmdcIik7XG4gICAgZXhwZWN0KHBhdHRlcm5zKS50b0NvbnRhaW4oXCIvYmFzZS9wcmljaW5nL1wiKTtcbiAgfSk7XG5cbiAgaXQoXCJjb3VudHMgdGhlIGJhc2VQYXRoIGJlaGF2aW9ycyBhZ2FpbnN0IHRoZSBidWRnZXRcIiwgKCkgPT4ge1xuICAgIGNvbnN0IHsgc3RhY2ssIGRpc3RyaWJ1dGlvblByb3BzIH0gPSBzZXR1cChbXSwge1xuICAgICAgYmFzZVBhdGg6IFwiL2Jhc2VcIixcbiAgICAgIHB1YmxpY0RpckVudHJpZXM6IEFycmF5LmZyb20oeyBsZW5ndGg6IDIxIH0sIChfLCBpKSA9PiBgZmlsZSR7aX0udHh0YCksXG4gICAgfSk7XG4gICAgLy8gMyBmaXhlZCArIHRoZSAyIGEgYmFzZVBhdGggYWRkcyB0byBzdGFuZCBpbiBmb3IgdGhlIGRlZmF1bHQgYmVoYXZpb3IgKyAyMVxuICAgIC8vIHB1YmxpYyA9IDI2LiBDb3VudGluZyBvbmx5IHRoZSAzIHJlcG9ydGVkIDI0IGFuZCBsZXQgdGhlIHN5bnRoIHRocm91Z2gsIHNvXG4gICAgLy8gdGhlIGxpbWl0IGFycml2ZWQgYXMgYSBDbG91ZEZyb250IGRlcGxveSBmYWlsdXJlLlxuICAgIGV4cGVjdChcbiAgICAgICgpID0+XG4gICAgICAgIG5ldyBOZXh0anNEaXN0cmlidXRpb24oc3RhY2ssIFwiRGlzdHJpYnV0aW9uXCIsIHtcbiAgICAgICAgICAuLi5kaXN0cmlidXRpb25Qcm9wcyxcbiAgICAgICAgICBtYXhDYWNoZUJlaGF2aW9yczogMjUsXG4gICAgICAgIH0pLFxuICAgICkudG9UaHJvdygvMjYgQ2xvdWRGcm9udCBjYWNoZSBiZWhhdmlvcnMuKjUgdXNlZCBieSBjZGstbmV4dGpzIGl0c2VsZi9zKTtcbiAgfSk7XG5cbiAgaXQoXCJkZWZhdWx0cyB0aGUgYnVkZ2V0IHRvIENsb3VkRnJvbnQncyBxdW90YSBvZiA3NVwiLCAoKSA9PiB7XG4gICAgLy8gVGhlIGhhcmQtY29kZWQgMjUgcHJlZGF0ZWQgdGhlIHF1b3RhJ3MgaW5jcmVhc2UgdG8gNzUuXG4gICAgY29uc3QgeyBzdGFjaywgZGlzdHJpYnV0aW9uUHJvcHMgfSA9IHNldHVwKFtdLCB7XG4gICAgICBwdWJsaWNEaXJFbnRyaWVzOiBBcnJheS5mcm9tKHsgbGVuZ3RoOiA3MiB9LCAoXywgaSkgPT4gYGZpbGUke2l9LnR4dGApLFxuICAgIH0pO1xuICAgIGV4cGVjdChcbiAgICAgICgpID0+IG5ldyBOZXh0anNEaXN0cmlidXRpb24oc3RhY2ssIFwiRGlzdHJpYnV0aW9uXCIsIGRpc3RyaWJ1dGlvblByb3BzKSxcbiAgICApLm5vdC50b1Rocm93KCk7XG4gICAgY29uc3QgeyBzdGFjazogb3ZlciwgZGlzdHJpYnV0aW9uUHJvcHM6IG92ZXJQcm9wcyB9ID0gc2V0dXAoW10sIHtcbiAgICAgIHB1YmxpY0RpckVudHJpZXM6IEFycmF5LmZyb20oeyBsZW5ndGg6IDczIH0sIChfLCBpKSA9PiBgZmlsZSR7aX0udHh0YCksXG4gICAgfSk7XG4gICAgZXhwZWN0KFxuICAgICAgKCkgPT4gbmV3IE5leHRqc0Rpc3RyaWJ1dGlvbihvdmVyLCBcIkRpc3RyaWJ1dGlvblwiLCBvdmVyUHJvcHMpLFxuICAgICkudG9UaHJvdyhcbiAgICAgIC83NiBDbG91ZEZyb250IGNhY2hlIGJlaGF2aW9ycywgb3ZlciB0aGUgbGltaXQgb2YgNzUuKnNldCBgbWF4Q2FjaGVCZWhhdmlvcnNgL3MsXG4gICAgKTtcbiAgfSk7XG5cbiAgaXQoXCJob25vcnMgYSByYWlzZWQgbWF4Q2FjaGVCZWhhdmlvcnNcIiwgKCkgPT4ge1xuICAgIGNvbnN0IHsgc3RhY2ssIGRpc3RyaWJ1dGlvblByb3BzIH0gPSBzZXR1cChbXSwge1xuICAgICAgcHVibGljRGlyRW50cmllczogQXJyYXkuZnJvbSh7IGxlbmd0aDogOTcgfSwgKF8sIGkpID0+IGBmaWxlJHtpfS50eHRgKSxcbiAgICB9KTtcbiAgICBleHBlY3QoXG4gICAgICAoKSA9PlxuICAgICAgICBuZXcgTmV4dGpzRGlzdHJpYnV0aW9uKHN0YWNrLCBcIkRpc3RyaWJ1dGlvblwiLCB7XG4gICAgICAgICAgLi4uZGlzdHJpYnV0aW9uUHJvcHMsXG4gICAgICAgICAgbWF4Q2FjaGVCZWhhdmlvcnM6IDEwMCxcbiAgICAgICAgfSksXG4gICAgKS5ub3QudG9UaHJvdygpO1xuICB9KTtcblxuICBpdChcImRvZXMgbm90IGNvdW50IGJhc2VQYXRoIGJlaGF2aW9ycyBmb3IgYSBiYXNlUGF0aCBvZiAvXCIsICgpID0+IHtcbiAgICAvLyBgYmFzZVBhdGg6IFwiL1wiYCBub3JtYWxpemVzIHRvIG5vIGJhc2VQYXRoIGFuZCBhZGRzIG5vIGJlaGF2aW9ycywgc29cbiAgICAvLyBjb3VudGluZyAyIGZvciBpdCBjb3VsZCByZWplY3QgYW4gYXBwIHRoYXQgaXMgdW5kZXIgdGhlIGxpbWl0LlxuICAgIGNvbnN0IHsgc3RhY2ssIGRpc3RyaWJ1dGlvblByb3BzIH0gPSBzZXR1cChbXSwge1xuICAgICAgYmFzZVBhdGg6IFwiL1wiLFxuICAgICAgcHVibGljRGlyRW50cmllczogQXJyYXkuZnJvbSh7IGxlbmd0aDogMjIgfSwgKF8sIGkpID0+IGBmaWxlJHtpfS50eHRgKSxcbiAgICB9KTtcbiAgICBleHBlY3QoXG4gICAgICAoKSA9PlxuICAgICAgICBuZXcgTmV4dGpzRGlzdHJpYnV0aW9uKHN0YWNrLCBcIkRpc3RyaWJ1dGlvblwiLCB7XG4gICAgICAgICAgLi4uZGlzdHJpYnV0aW9uUHJvcHMsXG4gICAgICAgICAgbWF4Q2FjaGVCZWhhdmlvcnM6IDI1LFxuICAgICAgICB9KSxcbiAgICApLm5vdC50b1Rocm93KCk7XG4gIH0pO1xuXG4gIGl0KFwicmVqZWN0cyBhIGJlaGF2aW9yIGZvciBhIGdyb3VwIGl0IHdhcyBub3QgZ2l2ZW5cIiwgKCkgPT4ge1xuICAgIGNvbnN0IHsgc3RhY2ssIGZ1bmN0aW9uR3JvdXBCZWhhdmlvcnMsIGRpc3RyaWJ1dGlvblByb3BzIH0gPSBzZXR1cChbXCJhcGlcIl0pO1xuICAgIGV4cGVjdChcbiAgICAgICgpID0+XG4gICAgICAgIG5ldyBOZXh0anNEaXN0cmlidXRpb24oc3RhY2ssIFwiRGlzdHJpYnV0aW9uXCIsIHtcbiAgICAgICAgICAuLi5kaXN0cmlidXRpb25Qcm9wcyxcbiAgICAgICAgICBmdW5jdGlvbkdyb3VwQmVoYXZpb3JzLFxuICAgICAgICB9KSxcbiAgICApLnRvVGhyb3coXG4gICAgICAvcm91dGVzIHRvIGZ1bmN0aW9uIGdyb3VwIFwiYXBpXCIsIHdoaWNoIGlzIG5vdCBpbiBgZnVuY3Rpb25Hcm91cHNgLyxcbiAgICApO1xuICB9KTtcblxuICBpdChcInBvaW50cyBlYWNoIGdyb3VwJ3MgYmVoYXZpb3JzIGF0IHRoYXQgZ3JvdXAncyBvd24gb3JpZ2luXCIsICgpID0+IHtcbiAgICBjb25zdCB7IHN0YWNrLCBmdW5jdGlvbkdyb3VwcywgZnVuY3Rpb25Hcm91cEJlaGF2aW9ycywgZGlzdHJpYnV0aW9uUHJvcHMgfSA9XG4gICAgICBzZXR1cChbXCJhcGlcIl0sIHtcbiAgICAgICAgcm91dGVzRm9yOiAoKSA9PiBbXCIvYXBpLyoqXCIsIFwiL2hlYWx0aFwiXSxcbiAgICAgIH0pO1xuICAgIG5ldyBOZXh0anNEaXN0cmlidXRpb24oc3RhY2ssIFwiRGlzdHJpYnV0aW9uXCIsIHtcbiAgICAgIC4uLmRpc3RyaWJ1dGlvblByb3BzLFxuICAgICAgZnVuY3Rpb25Hcm91cHMsXG4gICAgICBmdW5jdGlvbkdyb3VwQmVoYXZpb3JzLFxuICAgIH0pO1xuICAgIGNvbnN0IGRpc3RyaWJ1dGlvbnMgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spLmZpbmRSZXNvdXJjZXMoXG4gICAgICBcIkFXUzo6Q2xvdWRGcm9udDo6RGlzdHJpYnV0aW9uXCIsXG4gICAgKTtcbiAgICBjb25zdCBjb25maWcgPVxuICAgICAgT2JqZWN0LnZhbHVlcyhkaXN0cmlidXRpb25zKVswXS5Qcm9wZXJ0aWVzLkRpc3RyaWJ1dGlvbkNvbmZpZztcbiAgICBjb25zdCBiZWhhdmlvcnMgPSBjb25maWcuQ2FjaGVCZWhhdmlvcnMgYXMgQXJyYXk8e1xuICAgICAgUGF0aFBhdHRlcm46IHN0cmluZztcbiAgICAgIFRhcmdldE9yaWdpbklkOiBzdHJpbmc7XG4gICAgfT47XG4gICAgY29uc3QgZ3JvdXBPcmlnaW5zID0gbmV3IFNldChcbiAgICAgIGJlaGF2aW9yc1xuICAgICAgICAuZmlsdGVyKChiZWhhdmlvcikgPT5cbiAgICAgICAgICBbXCJhcGkvKlwiLCBcImhlYWx0aFwiXS5pbmNsdWRlcyhiZWhhdmlvci5QYXRoUGF0dGVybiksXG4gICAgICAgIClcbiAgICAgICAgLm1hcCgoYmVoYXZpb3IpID0+IGJlaGF2aW9yLlRhcmdldE9yaWdpbklkKSxcbiAgICApO1xuICAgIC8vIE9uZSBvcmlnaW4sIHNoYXJlZCBieSBib3RoIG9mIHRoZSBncm91cCdzIHBhdHRlcm5zLCBhbmQgbm90IHRoZSBkZWZhdWx0J3MuXG4gICAgZXhwZWN0KGdyb3VwT3JpZ2lucy5zaXplKS50b0JlKDEpO1xuICAgIGV4cGVjdChncm91cE9yaWdpbnMpLm5vdC50b0NvbnRhaW4oXG4gICAgICBjb25maWcuRGVmYXVsdENhY2hlQmVoYXZpb3IuVGFyZ2V0T3JpZ2luSWQsXG4gICAgKTtcbiAgfSk7XG5cbiAgaXQoXCJwcmVmaXhlcyBncm91cCBwYXR0ZXJucyB3aXRoIGJhc2VQYXRoXCIsICgpID0+IHtcbiAgICBjb25zdCB7IHN0YWNrLCBmdW5jdGlvbkdyb3VwcywgZnVuY3Rpb25Hcm91cEJlaGF2aW9ycywgZGlzdHJpYnV0aW9uUHJvcHMgfSA9XG4gICAgICBzZXR1cChbXCJhcGlcIl0sIHtcbiAgICAgICAgcm91dGVzRm9yOiAoKSA9PiBbXCIvYXBpLyoqXCJdLFxuICAgICAgICBiYXNlUGF0aDogXCIvYmFzZVwiLFxuICAgICAgfSk7XG4gICAgbmV3IE5leHRqc0Rpc3RyaWJ1dGlvbihzdGFjaywgXCJEaXN0cmlidXRpb25cIiwge1xuICAgICAgLi4uZGlzdHJpYnV0aW9uUHJvcHMsXG4gICAgICBmdW5jdGlvbkdyb3VwcyxcbiAgICAgIGZ1bmN0aW9uR3JvdXBCZWhhdmlvcnMsXG4gICAgfSk7XG4gICAgZXhwZWN0KHBhdGhQYXR0ZXJucyhzdGFjaykpLnRvQ29udGFpbihcIi9iYXNlL2FwaS8qXCIpO1xuICB9KTtcblxuICBpdChcIm5hbWVzIGV2ZXJ5IGNsYWltIG9uIHRoZSBiZWhhdmlvciBidWRnZXQgd2hlbiBpdCBpcyBleGNlZWRlZFwiLCAoKSA9PiB7XG4gICAgY29uc3QgeyBzdGFjaywgZnVuY3Rpb25Hcm91cHMsIGZ1bmN0aW9uR3JvdXBCZWhhdmlvcnMsIGRpc3RyaWJ1dGlvblByb3BzIH0gPVxuICAgICAgc2V0dXAoW1wiYXBpXCJdLCB7XG4gICAgICAgIHJvdXRlc0ZvcjogKCkgPT4gW1wiL2FwaS8qKlwiXSxcbiAgICAgICAgcHVibGljRGlyRW50cmllczogQXJyYXkuZnJvbSh7IGxlbmd0aDogMjIgfSwgKF8sIGkpID0+IGBmaWxlJHtpfS50eHRgKSxcbiAgICAgIH0pO1xuICAgIGV4cGVjdChcbiAgICAgICgpID0+XG4gICAgICAgIG5ldyBOZXh0anNEaXN0cmlidXRpb24oc3RhY2ssIFwiRGlzdHJpYnV0aW9uXCIsIHtcbiAgICAgICAgICAuLi5kaXN0cmlidXRpb25Qcm9wcyxcbiAgICAgICAgICBmdW5jdGlvbkdyb3VwcyxcbiAgICAgICAgICBmdW5jdGlvbkdyb3VwQmVoYXZpb3JzLFxuICAgICAgICAgIG1heENhY2hlQmVoYXZpb3JzOiAyNSxcbiAgICAgICAgfSksXG4gICAgKS50b1Rocm93KFxuICAgICAgLzI2IENsb3VkRnJvbnQgY2FjaGUgYmVoYXZpb3JzLioxIGZvciBgZnVuY3Rpb25Hcm91cHNgIHBhdHRlcm5zL3MsXG4gICAgKTtcbiAgfSk7XG5cbiAgaXQoXCJyZWplY3RzIGEgZ3JvdXAgcGF0dGVybiB0aGF0IGR1cGxpY2F0ZXMgYSBwdWJsaWMvIGRpcmVjdG9yeSdzXCIsICgpID0+IHtcbiAgICAvLyBgcHVibGljL2RvY3MvYCBhbmQgYC9kb2NzLyoqYCBib3RoIGRlcGxveSBhcyBgZG9jcy8qYDogdGhlIHN5bnRoXG4gICAgLy8gc3VjY2VlZGVkIGFuZCBDbG91ZEZyb250IHJlamVjdGVkIHRoZSBkZXBsb3kuXG4gICAgY29uc3QgeyBzdGFjaywgZnVuY3Rpb25Hcm91cHMsIGZ1bmN0aW9uR3JvdXBCZWhhdmlvcnMsIGRpc3RyaWJ1dGlvblByb3BzIH0gPVxuICAgICAgc2V0dXAoW1wiZG9jc1wiXSk7XG4gICAgZXhwZWN0KFxuICAgICAgKCkgPT5cbiAgICAgICAgbmV3IE5leHRqc0Rpc3RyaWJ1dGlvbihzdGFjaywgXCJEaXN0cmlidXRpb25cIiwge1xuICAgICAgICAgIC4uLmRpc3RyaWJ1dGlvblByb3BzLFxuICAgICAgICAgIGZ1bmN0aW9uR3JvdXBzLFxuICAgICAgICAgIGZ1bmN0aW9uR3JvdXBCZWhhdmlvcnMsXG4gICAgICAgICAgcHVibGljRGlyRW50cmllczogW3sgbmFtZTogXCJkb2NzXCIsIGlzRGlyZWN0b3J5OiB0cnVlIH1dLFxuICAgICAgICB9KSxcbiAgICApLnRvVGhyb3coXG4gICAgICAvcGF0dGVybiBcIlxcL2RvY3NcXC9cXCpcXCpcIiBcXChncm91cCBcImRvY3NcIlxcKSBkZXBsb3lzIGFzIHRoZSBwYXRoIHBhdHRlcm4gXCJkb2NzXFwvXFwqXCIsIHdoaWNoIGEgdG9wLWxldmVsIHB1YmxpY1xcLyBlbnRyeSBhbHJlYWR5IHVzZXMvLFxuICAgICk7XG4gIH0pO1xuXG4gIGl0KFwicmVqZWN0cyBhIGdyb3VwIHBhdHRlcm4gYSBwdWJsaWMvIGRpcmVjdG9yeSdzIGJlaGF2aW9yIHdvdWxkIHNoYWRvd1wiLCAoKSA9PiB7XG4gICAgLy8gYGRvY3MvKmAgaXMgYWRkZWQgZmlyc3QsIHNvIGBkb2NzL2d1aWRlLypgIG5ldmVyIG1hdGNoZXMuXG4gICAgY29uc3QgeyBzdGFjaywgZnVuY3Rpb25Hcm91cHMsIGZ1bmN0aW9uR3JvdXBCZWhhdmlvcnMsIGRpc3RyaWJ1dGlvblByb3BzIH0gPVxuICAgICAgc2V0dXAoW1wiZ3VpZGVcIl0sIHtcbiAgICAgICAgcm91dGVzRm9yOiAoKSA9PiBbXCIvZG9jcy9ndWlkZS8qKlwiXSxcbiAgICAgIH0pO1xuICAgIGV4cGVjdChcbiAgICAgICgpID0+XG4gICAgICAgIG5ldyBOZXh0anNEaXN0cmlidXRpb24oc3RhY2ssIFwiRGlzdHJpYnV0aW9uXCIsIHtcbiAgICAgICAgICAuLi5kaXN0cmlidXRpb25Qcm9wcyxcbiAgICAgICAgICBmdW5jdGlvbkdyb3VwcyxcbiAgICAgICAgICBmdW5jdGlvbkdyb3VwQmVoYXZpb3JzLFxuICAgICAgICAgIHB1YmxpY0RpckVudHJpZXM6IFt7IG5hbWU6IFwiZG9jc1wiLCBpc0RpcmVjdG9yeTogdHJ1ZSB9XSxcbiAgICAgICAgfSksXG4gICAgKS50b1Rocm93KC9wdWJsaWNcXC8gZW50cnkgYmVoaW5kIFwiZG9jc1xcL1xcKlwiIGlzIG1hdGNoZWQgZmlyc3QvKTtcbiAgfSk7XG5cbiAgaXQoXCJyZWplY3RzIGFuIGV4YWN0IGdyb3VwIHBhdHRlcm4gZXF1YWwgdG8gYSBwdWJsaWMvIGZpbGUncywgYmFzZVBhdGggaW5jbHVkZWRcIiwgKCkgPT4ge1xuICAgIGNvbnN0IHsgc3RhY2ssIGZ1bmN0aW9uR3JvdXBzLCBmdW5jdGlvbkdyb3VwQmVoYXZpb3JzLCBkaXN0cmlidXRpb25Qcm9wcyB9ID1cbiAgICAgIHNldHVwKFtcIm1rdFwiXSwge1xuICAgICAgICByb3V0ZXNGb3I6ICgpID0+IFtcIi9yb2JvdHMudHh0XCJdLFxuICAgICAgICBiYXNlUGF0aDogXCIvYmFzZVwiLFxuICAgICAgfSk7XG4gICAgZXhwZWN0KFxuICAgICAgKCkgPT5cbiAgICAgICAgbmV3IE5leHRqc0Rpc3RyaWJ1dGlvbihzdGFjaywgXCJEaXN0cmlidXRpb25cIiwge1xuICAgICAgICAgIC4uLmRpc3RyaWJ1dGlvblByb3BzLFxuICAgICAgICAgIGZ1bmN0aW9uR3JvdXBzLFxuICAgICAgICAgIGZ1bmN0aW9uR3JvdXBCZWhhdmlvcnMsXG4gICAgICAgICAgcHVibGljRGlyRW50cmllczogW3sgbmFtZTogXCJyb2JvdHMudHh0XCIsIGlzRGlyZWN0b3J5OiBmYWxzZSB9XSxcbiAgICAgICAgfSksXG4gICAgKS50b1Rocm93KC9kZXBsb3lzIGFzIHRoZSBwYXRoIHBhdHRlcm4gXCJcXC9iYXNlXFwvcm9ib3RzXFwudHh0XCIvKTtcbiAgfSk7XG5cbiAgaXQoXCJhbGxvd3MgYSBncm91cCBwYXR0ZXJuIGJlc2lkZSBhIHB1YmxpYy8gZW50cnkgaXQgZG9lcyBub3Qgb3ZlcmxhcFwiLCAoKSA9PiB7XG4gICAgLy8gYC9kb2NzYCBleGFjdCBpcyBub3QgdW5kZXIgYGRvY3MvKmAsIGFuZCBgZG9jcy8qYCBkb2VzIG5vdCBtYXRjaCBgL2RvY3NgLlxuICAgIGNvbnN0IHsgc3RhY2ssIGZ1bmN0aW9uR3JvdXBzLCBmdW5jdGlvbkdyb3VwQmVoYXZpb3JzLCBkaXN0cmlidXRpb25Qcm9wcyB9ID1cbiAgICAgIHNldHVwKFtcImRvY3NcIl0sIHtcbiAgICAgICAgcm91dGVzRm9yOiAoKSA9PiBbXCIvZG9jc1wiXSxcbiAgICAgIH0pO1xuICAgIG5ldyBOZXh0anNEaXN0cmlidXRpb24oc3RhY2ssIFwiRGlzdHJpYnV0aW9uXCIsIHtcbiAgICAgIC4uLmRpc3RyaWJ1dGlvblByb3BzLFxuICAgICAgZnVuY3Rpb25Hcm91cHMsXG4gICAgICBmdW5jdGlvbkdyb3VwQmVoYXZpb3JzLFxuICAgICAgcHVibGljRGlyRW50cmllczogW3sgbmFtZTogXCJkb2NzXCIsIGlzRGlyZWN0b3J5OiB0cnVlIH1dLFxuICAgIH0pO1xuICAgIGV4cGVjdChwYXRoUGF0dGVybnMoc3RhY2spKS50b0VxdWFsKFxuICAgICAgZXhwZWN0LmFycmF5Q29udGFpbmluZyhbXCJkb2NzLypcIiwgXCJkb2NzXCJdKSxcbiAgICApO1xuICB9KTtcblxuICBpdChcInNlcnZlcyBidW5kbGVzIHVuZGVyIGEgcGF0aC1zdHlsZSBhc3NldFByZWZpeCwgcmV3cml0aW5nIHRoZSBwcmVmaXggYXdheVwiLCAoKSA9PiB7XG4gICAgLy8gTmV4dC5qcyBlbWl0cyBgPGFzc2V0UHJlZml4Pi9fbmV4dC9zdGF0aWMvLi4uYCBmb3IgZXZlcnkgYnVuZGxlIHdoaWxlIHRoZVxuICAgIC8vIG9iamVjdHMga2VlcCB0aGVpciB1bnByZWZpeGVkIFMzIGtleXMsIHNvIHdpdGhvdXQgYSBiZWhhdmlvciBvZiBpdHMgb3duIHRoZVxuICAgIC8vIHJlcXVlc3QgZmFsbHMgdGhyb3VnaCB0byB0aGUgY29tcHV0ZSBvcmlnaW4gYW5kIDQwNHMg4oCUIHRoZSBkZXBsb3ltZW50XG4gICAgLy8gcGFja2FnZSBjYXJyaWVzIG5vIGAubmV4dC9zdGF0aWNgLiBNZWFzdXJlZCBhZ2FpbnN0XG4gICAgLy8gYHRlc3QvZTJlL2FwcC1kaXIvYXNzZXQtcHJlZml4YCwgd2hlcmUgMiBvZiA3IGNhc2VzIGZhaWxlZCBvbiBleGFjdGx5IHRoYXQuXG4gICAgY29uc3QgeyBzdGFjaywgZGlzdHJpYnV0aW9uUHJvcHMgfSA9IHNldHVwKFtdKTtcbiAgICBuZXcgTmV4dGpzRGlzdHJpYnV0aW9uKHN0YWNrLCBcIkRpc3RyaWJ1dGlvblwiLCB7XG4gICAgICAuLi5kaXN0cmlidXRpb25Qcm9wcyxcbiAgICAgIGFzc2V0UHJlZml4OiBcIi9jdXN0b20tYXNzZXQtcHJlZml4XCIsXG4gICAgfSk7XG4gICAgLy8gQWhlYWQgb2YgdGhlIGJhcmUgYF9uZXh0L3N0YXRpYypgIGluIG5vdGhpbmcgYnV0IGxpc3QgcG9zaXRpb247IHRoZSB0d29cbiAgICAvLyBwYXR0ZXJucyBjYW5ub3QgYm90aCBtYXRjaCBvbmUgcmVxdWVzdCwgc28gb3JkZXIgZG9lcyBub3QgbWF0dGVyIGhlcmUuXG4gICAgZXhwZWN0KHBhdGhQYXR0ZXJucyhzdGFjaykpLnRvRXF1YWwoW1xuICAgICAgXCJfbmV4dC9zdGF0aWMqXCIsXG4gICAgICBcIi9jdXN0b20tYXNzZXQtcHJlZml4L19uZXh0L3N0YXRpYypcIixcbiAgICAgIFwiX25leHQvaW1hZ2UqXCIsXG4gICAgXSk7XG4gICAgY29uc3QgZm5zID0gVGVtcGxhdGUuZnJvbVN0YWNrKHN0YWNrKS5maW5kUmVzb3VyY2VzKFxuICAgICAgXCJBV1M6OkNsb3VkRnJvbnQ6OkZ1bmN0aW9uXCIsXG4gICAgKTtcbiAgICBjb25zdCBjb2RlID0gT2JqZWN0LnZhbHVlcyhmbnMpXG4gICAgICAubWFwKChmbikgPT4gZm4uUHJvcGVydGllcy5GdW5jdGlvbkNvZGUgYXMgc3RyaW5nKVxuICAgICAgLmZpbmQoKGl0KSA9PiBpdC5pbmNsdWRlcyhcInJlcXVlc3QudXJpLnNsaWNlXCIpKTtcbiAgICAvLyBgXCIvY3VzdG9tLWFzc2V0LXByZWZpeFwiLmxlbmd0aGAsIHNvIGAvY3VzdG9tLWFzc2V0LXByZWZpeC9fbmV4dC9zdGF0aWMveGBcbiAgICAvLyByZWFjaGVzIFMzIGFzIGBfbmV4dC9zdGF0aWMveGAuXG4gICAgZXhwZWN0KGNvZGUpLnRvQ29udGFpbignXCJcIiArIHJlcXVlc3QudXJpLnNsaWNlKDIwKScpO1xuICB9KTtcblxuICBpdChcInJlZnVzZXMgYW4gYXNzZXRQcmVmaXggd2hlbiBhbiBvdmVycmlkZSBhbHJlYWR5IGNsYWltcyBWSUVXRVJfUkVRVUVTVFwiLCAoKSA9PiB7XG4gICAgLy8gQ2xvdWRGcm9udCBwZXJtaXRzIG9uZSBmdW5jdGlvbiBwZXIgZXZlbnQgdHlwZSBwZXIgYmVoYXZpb3IsIHNvIHRoZVxuICAgIC8vIHJld3JpdGUgYW5kIHRoZSBvdmVycmlkZSBjYW5ub3QgY29leGlzdDogdGhlIHN0YWNrIHN5bnRoZWQgY2xlYW5seSBhbmQgd2FzXG4gICAgLy8gcmVqZWN0ZWQgYXQgZGVwbG95LCBuYW1pbmcgbmVpdGhlciB0aGUgb3ZlcnJpZGUgbm9yIGBhc3NldFByZWZpeGAuXG4gICAgY29uc3QgeyBzdGFjaywgZGlzdHJpYnV0aW9uUHJvcHMgfSA9IHNldHVwKFtdKTtcbiAgICBleHBlY3QoXG4gICAgICAoKSA9PlxuICAgICAgICBuZXcgTmV4dGpzRGlzdHJpYnV0aW9uKHN0YWNrLCBcIkRpc3RyaWJ1dGlvblwiLCB7XG4gICAgICAgICAgLi4uZGlzdHJpYnV0aW9uUHJvcHMsXG4gICAgICAgICAgYXNzZXRQcmVmaXg6IFwiL2NkblwiLFxuICAgICAgICAgIG92ZXJyaWRlczoge1xuICAgICAgICAgICAgc3RhdGljQmVoYXZpb3JPcHRpb25zOiB7XG4gICAgICAgICAgICAgIGZ1bmN0aW9uQXNzb2NpYXRpb25zOiBbXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgZXZlbnRUeXBlOiBGdW5jdGlvbkV2ZW50VHlwZS5WSUVXRVJfUkVRVUVTVCxcbiAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uOiBuZXcgQ2xvdWRGcm9udEZ1bmN0aW9uKHN0YWNrLCBcIlVzZXJGblwiLCB7XG4gICAgICAgICAgICAgICAgICAgIGNvZGU6IEZ1bmN0aW9uQ29kZS5mcm9tSW5saW5lKFxuICAgICAgICAgICAgICAgICAgICAgIFwiZnVuY3Rpb24gaGFuZGxlcihldmVudCkgeyByZXR1cm4gZXZlbnQucmVxdWVzdDsgfVwiLFxuICAgICAgICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICAgICAgfSksXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgXSxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfSxcbiAgICAgICAgfSksXG4gICAgKS50b1Rocm93KC9hbHJlYWR5IGFzc29jaWF0ZXMgYSBDbG91ZEZyb250IGZ1bmN0aW9uIHdpdGggdmlld2VyLXJlcXVlc3QvKTtcbiAgfSk7XG5cbiAgY29uc3QgdXNlckZuID0gKHN0YWNrOiBTdGFjaywgZXZlbnRUeXBlOiBGdW5jdGlvbkV2ZW50VHlwZSkgPT4gKHtcbiAgICBldmVudFR5cGUsXG4gICAgZnVuY3Rpb246IG5ldyBDbG91ZEZyb250RnVuY3Rpb24oc3RhY2ssIGBVc2VyRm4ke2V2ZW50VHlwZX1gLCB7XG4gICAgICBjb2RlOiBGdW5jdGlvbkNvZGUuZnJvbUlubGluZShcbiAgICAgICAgXCJmdW5jdGlvbiBoYW5kbGVyKGV2ZW50KSB7IHJldHVybiBldmVudC5yZXF1ZXN0IHx8IGV2ZW50LnJlc3BvbnNlOyB9XCIsXG4gICAgICApLFxuICAgIH0pLFxuICB9KTtcblxuICBpdC5lYWNoKFtcImR5bmFtaWNCZWhhdmlvck9wdGlvbnNcIiwgXCJpbWFnZUJlaGF2aW9yT3B0aW9uc1wiXSBhcyBjb25zdCkoXG4gICAgXCJyZWZ1c2VzIGEgJXMgb3ZlcnJpZGUgdGhhdCB3b3VsZCByZXBsYWNlIHRoZSB4LWZvcndhcmRlZC1ob3N0IGZ1bmN0aW9uXCIsXG4gICAgKG92ZXJyaWRlTmFtZSkgPT4ge1xuICAgICAgLy8gVGhlIHJ1bnRpbWUgdHJ1c3RzIGB4LWZvcndhcmRlZC1ob3N0YCBvbiBGdW5jdGlvbiBVUkwgZXZlbnRzIGJlY2F1c2VcbiAgICAgIC8vIHRoaXMgZnVuY3Rpb24gb3ZlcndyaXRlcyBpdDsgcmVwbGFjZWQsIGEgdmlld2VyJ3Mgb3duIGhlYWRlciByZWFjaGVzIGl0LlxuICAgICAgY29uc3QgeyBzdGFjaywgZGlzdHJpYnV0aW9uUHJvcHMgfSA9IHNldHVwKFtdKTtcbiAgICAgIGV4cGVjdChcbiAgICAgICAgKCkgPT5cbiAgICAgICAgICBuZXcgTmV4dGpzRGlzdHJpYnV0aW9uKHN0YWNrLCBcIkRpc3RyaWJ1dGlvblwiLCB7XG4gICAgICAgICAgICAuLi5kaXN0cmlidXRpb25Qcm9wcyxcbiAgICAgICAgICAgIG92ZXJyaWRlczoge1xuICAgICAgICAgICAgICBbb3ZlcnJpZGVOYW1lXToge1xuICAgICAgICAgICAgICAgIGZ1bmN0aW9uQXNzb2NpYXRpb25zOiBbXG4gICAgICAgICAgICAgICAgICB1c2VyRm4oc3RhY2ssIEZ1bmN0aW9uRXZlbnRUeXBlLlZJRVdFUl9SRVFVRVNUKSxcbiAgICAgICAgICAgICAgICBdLFxuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9KSxcbiAgICAgICkudG9UaHJvdyhcbiAgICAgICAgbmV3IFJlZ0V4cChgb3ZlcnJpZGVzXFxcXC4ke292ZXJyaWRlTmFtZX1cXFxcLmZ1bmN0aW9uQXNzb2NpYXRpb25zYCksXG4gICAgICApO1xuICAgIH0sXG4gICk7XG5cbiAgaXQoXCJrZWVwcyB0aGUgeC1mb3J3YXJkZWQtaG9zdCBmdW5jdGlvbiBuZXh0IHRvIGFuIG92ZXJyaWRlJ3Mgb3RoZXIgYXNzb2NpYXRpb25zXCIsICgpID0+IHtcbiAgICBjb25zdCB7IHN0YWNrLCBkaXN0cmlidXRpb25Qcm9wcyB9ID0gc2V0dXAoW10pO1xuICAgIG5ldyBOZXh0anNEaXN0cmlidXRpb24oc3RhY2ssIFwiRGlzdHJpYnV0aW9uXCIsIHtcbiAgICAgIC4uLmRpc3RyaWJ1dGlvblByb3BzLFxuICAgICAgb3ZlcnJpZGVzOiB7XG4gICAgICAgIGR5bmFtaWNCZWhhdmlvck9wdGlvbnM6IHtcbiAgICAgICAgICBmdW5jdGlvbkFzc29jaWF0aW9uczogW1xuICAgICAgICAgICAgdXNlckZuKHN0YWNrLCBGdW5jdGlvbkV2ZW50VHlwZS5WSUVXRVJfUkVTUE9OU0UpLFxuICAgICAgICAgIF0sXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgIH0pO1xuICAgIGNvbnN0IGNvbmZpZyA9IE9iamVjdC52YWx1ZXMoXG4gICAgICBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spLmZpbmRSZXNvdXJjZXMoXCJBV1M6OkNsb3VkRnJvbnQ6OkRpc3RyaWJ1dGlvblwiKSxcbiAgICApWzBdLlByb3BlcnRpZXMuRGlzdHJpYnV0aW9uQ29uZmlnO1xuICAgIGNvbnN0IGV2ZW50VHlwZXMgPSAoXG4gICAgICBjb25maWcuRGVmYXVsdENhY2hlQmVoYXZpb3IuRnVuY3Rpb25Bc3NvY2lhdGlvbnMgYXMgQXJyYXk8e1xuICAgICAgICBFdmVudFR5cGU6IHN0cmluZztcbiAgICAgIH0+XG4gICAgKS5tYXAoKGFzc29jaWF0aW9uKSA9PiBhc3NvY2lhdGlvbi5FdmVudFR5cGUpO1xuICAgIGV4cGVjdChldmVudFR5cGVzLnNvcnQoKSkudG9FcXVhbChbXCJ2aWV3ZXItcmVxdWVzdFwiLCBcInZpZXdlci1yZXNwb25zZVwiXSk7XG4gIH0pO1xuXG4gIGl0KFwicmV3cml0ZXMgYW4gYXNzZXRQcmVmaXggYmFjayBvbnRvIHRoZSBiYXNlUGF0aCBTMyBrZXlzXCIsICgpID0+IHtcbiAgICBjb25zdCB7IHN0YWNrLCBkaXN0cmlidXRpb25Qcm9wcyB9ID0gc2V0dXAoW10sIHsgYmFzZVBhdGg6IFwiL2Jhc2VcIiB9KTtcbiAgICBuZXcgTmV4dGpzRGlzdHJpYnV0aW9uKHN0YWNrLCBcIkRpc3RyaWJ1dGlvblwiLCB7XG4gICAgICAuLi5kaXN0cmlidXRpb25Qcm9wcyxcbiAgICAgIGFzc2V0UHJlZml4OiBcIi9jZG5cIixcbiAgICB9KTtcbiAgICBleHBlY3QocGF0aFBhdHRlcm5zKHN0YWNrKSkudG9Db250YWluKFwiL2Nkbi9fbmV4dC9zdGF0aWMqXCIpO1xuICAgIGNvbnN0IGZucyA9IFRlbXBsYXRlLmZyb21TdGFjayhzdGFjaykuZmluZFJlc291cmNlcyhcbiAgICAgIFwiQVdTOjpDbG91ZEZyb250OjpGdW5jdGlvblwiLFxuICAgICk7XG4gICAgY29uc3QgY29kZSA9IE9iamVjdC52YWx1ZXMoZm5zKVxuICAgICAgLm1hcCgoZm4pID0+IGZuLlByb3BlcnRpZXMuRnVuY3Rpb25Db2RlIGFzIHN0cmluZylcbiAgICAgIC5maW5kKChpdCkgPT4gaXQuaW5jbHVkZXMoXCJyZXF1ZXN0LnVyaS5zbGljZVwiKSk7XG4gICAgLy8gVGhlIG9iamVjdHMgYXJlIGF0IGBiYXNlL19uZXh0L3N0YXRpYy8uLi5gOiBgYXNzZXRQcmVmaXhgIHJlcGxhY2VzIHRoZVxuICAgIC8vIGBiYXNlUGF0aGAgcHJlZml4IGluIHRoZSBVUkwsIHNvIHRoZSByZXdyaXRlIGhhcyB0byBwdXQgaXQgYmFjay5cbiAgICBleHBlY3QoY29kZSkudG9Db250YWluKCdcIi9iYXNlXCIgKyByZXF1ZXN0LnVyaS5zbGljZSg0KScpO1xuICB9KTtcblxuICBpdChcInNlcnZlcyBidW5kbGVzIHVuZGVyIHRoZSBwYXRoIGFuIGFic29sdXRlIGFzc2V0UHJlZml4IGNhcnJpZXNcIiwgKCkgPT4ge1xuICAgIC8vIGBuZXh0IGJ1aWxkYCBjb21waWxlcyBhIGAvY3VzdG9tLWFzc2V0LXByZWZpeC9fbmV4dC86cGF0aCsg4oaSIC9fbmV4dC86cGF0aCtgXG4gICAgLy8gcmV3cml0ZSBvZiBpdHMgb3duIGZvciBhbiBhYnNvbHV0ZSBwcmVmaXggd2l0aCBhIHBhdGgsIHNvIGBuZXh0IHN0YXJ0YCBzZXJ2ZXNcbiAgICAvLyBldmVyeSBidW5kbGUgdW5kZXIgdGhhdCBwYXRoIGFzIHdlbGwuIEEgQ0ROIGZyb250aW5nIHRoaXMgZGlzdHJpYnV0aW9uIHRoZXJlXG4gICAgLy8gZ2V0cyB0aGUgc2FtZSByZXF1ZXN0LCBhbmQgd2l0aG91dCBhIGJlaGF2aW9yIGl0IHJlYWNoZXMgdGhlIGNvbXB1dGUgb3JpZ2luXG4gICAgLy8gYW5kIDQwNHMuIE1lYXN1cmVkIGFnYWluc3QgYHRlc3QvZTJlL2FwcC1kaXIvYXNzZXQtcHJlZml4LWFic29sdXRlYCwgd2hvc2VcbiAgICAvLyBvbmUgY2FzZSBmYWlsZWQgb24gZXhhY3RseSB0aGF0LlxuICAgIGZvciAoY29uc3QgYXNzZXRQcmVmaXggb2YgW1xuICAgICAgXCJodHRwczovL2V4YW1wbGUudmVyY2VsLnNoL2N1c3RvbS1hc3NldC1wcmVmaXhcIixcbiAgICAgIFwiLy9leGFtcGxlLnZlcmNlbC5zaC9jdXN0b20tYXNzZXQtcHJlZml4L1wiLFxuICAgIF0pIHtcbiAgICAgIGNvbnN0IHsgc3RhY2ssIGRpc3RyaWJ1dGlvblByb3BzIH0gPSBzZXR1cChbXSk7XG4gICAgICBuZXcgTmV4dGpzRGlzdHJpYnV0aW9uKHN0YWNrLCBcIkRpc3RyaWJ1dGlvblwiLCB7XG4gICAgICAgIC4uLmRpc3RyaWJ1dGlvblByb3BzLFxuICAgICAgICBhc3NldFByZWZpeCxcbiAgICAgIH0pO1xuICAgICAgZXhwZWN0KHBhdGhQYXR0ZXJucyhzdGFjaykpLnRvQ29udGFpbihcbiAgICAgICAgXCIvY3VzdG9tLWFzc2V0LXByZWZpeC9fbmV4dC9zdGF0aWMqXCIsXG4gICAgICApO1xuICAgICAgY29uc3QgY29kZSA9IE9iamVjdC52YWx1ZXMoXG4gICAgICAgIFRlbXBsYXRlLmZyb21TdGFjayhzdGFjaykuZmluZFJlc291cmNlcyhcIkFXUzo6Q2xvdWRGcm9udDo6RnVuY3Rpb25cIiksXG4gICAgICApXG4gICAgICAgIC5tYXAoKGZuKSA9PiBmbi5Qcm9wZXJ0aWVzLkZ1bmN0aW9uQ29kZSBhcyBzdHJpbmcpXG4gICAgICAgIC5maW5kKChpdCkgPT4gaXQuaW5jbHVkZXMoXCJyZXF1ZXN0LnVyaS5zbGljZVwiKSk7XG4gICAgICBleHBlY3QoY29kZSkudG9Db250YWluKCdcIlwiICsgcmVxdWVzdC51cmkuc2xpY2UoMjApJyk7XG4gICAgfVxuICB9KTtcblxuICBpdChcImFkZHMgbm8gYmVoYXZpb3IgZm9yIGFuIGFzc2V0UHJlZml4IHRoYXQgbmVlZHMgbm9uZVwiLCAoKSA9PiB7XG4gICAgLy8gQW4gYWJzb2x1dGUgcHJlZml4IHdpdGggbm8gcGF0aCBvZiBpdHMgb3duIG5hbWVzIGFuIG9yaWdpbiB0aGlzIGRpc3RyaWJ1dGlvblxuICAgIC8vIGRvZXMgbm90IHNlcnZlLCBhbmQgYSBwcmVmaXggZXF1YWwgdG8gdGhlIGJhc2VQYXRoIG9uZSBpcyB3aGF0IE5leHQuanNcbiAgICAvLyBkZWZhdWx0cyB0byB3aGVuIGBiYXNlUGF0aGAgaXMgc2V0IOKAlCBgX25leHQvc3RhdGljKmAgYWxyZWFkeSByZXNvbHZlcyB1bmRlclxuICAgIC8vIGl0LCBhbmQgYSBkdXBsaWNhdGUgcGF0dGVybiB3b3VsZCBtYWtlIENsb3VkRnJvbnQgcmVqZWN0IHRoZSBkaXN0cmlidXRpb24uXG4gICAgZm9yIChjb25zdCBbYXNzZXRQcmVmaXgsIGJhc2VQYXRoXSBvZiBbXG4gICAgICBbXCJodHRwczovL2Nkbi5leGFtcGxlLmNvbVwiLCB1bmRlZmluZWRdLFxuICAgICAgW1wiaHR0cHM6Ly9jZG4uZXhhbXBsZS5jb20vXCIsIHVuZGVmaW5lZF0sXG4gICAgICBbXCIvL2Nkbi5leGFtcGxlLmNvbVwiLCB1bmRlZmluZWRdLFxuICAgICAgW1wiL2Jhc2VcIiwgXCIvYmFzZVwiXSxcbiAgICAgIFtcImh0dHBzOi8vY2RuLmV4YW1wbGUuY29tL2Jhc2VcIiwgXCIvYmFzZVwiXSxcbiAgICAgIFtcIlwiLCB1bmRlZmluZWRdLFxuICAgIF0gYXMgY29uc3QpIHtcbiAgICAgIGNvbnN0IHsgc3RhY2ssIGRpc3RyaWJ1dGlvblByb3BzIH0gPSBzZXR1cChbXSwgeyBiYXNlUGF0aCB9KTtcbiAgICAgIG5ldyBOZXh0anNEaXN0cmlidXRpb24oc3RhY2ssIFwiRGlzdHJpYnV0aW9uXCIsIHtcbiAgICAgICAgLi4uZGlzdHJpYnV0aW9uUHJvcHMsXG4gICAgICAgIGFzc2V0UHJlZml4LFxuICAgICAgfSk7XG4gICAgICBjb25zdCBwYXR0ZXJucyA9IHBhdGhQYXR0ZXJucyhzdGFjayk7XG4gICAgICBleHBlY3QocGF0dGVybnMuZmlsdGVyKChwKSA9PiBwLmluY2x1ZGVzKFwiX25leHQvc3RhdGljXCIpKSkudG9IYXZlTGVuZ3RoKFxuICAgICAgICAxLFxuICAgICAgKTtcbiAgICAgIC8vIFRoZSB4LWZvcndhcmRlZC1ob3N0IGZ1bmN0aW9uIGlzIGFsd2F5cyB0aGVyZSBvbiBmdW5jdGlvbiBjb21wdXRlOyB0aGVcbiAgICAgIC8vIHJld3JpdGUgb25lIHNob3VsZCBub3QgYmUuXG4gICAgICBjb25zdCBjb2RlcyA9IE9iamVjdC52YWx1ZXMoXG4gICAgICAgIFRlbXBsYXRlLmZyb21TdGFjayhzdGFjaykuZmluZFJlc291cmNlcyhcIkFXUzo6Q2xvdWRGcm9udDo6RnVuY3Rpb25cIiksXG4gICAgICApLm1hcCgoZm4pID0+IGZuLlByb3BlcnRpZXMuRnVuY3Rpb25Db2RlIGFzIHN0cmluZyk7XG4gICAgICBleHBlY3QoY29kZXMuc29tZSgoaXQpID0+IGl0LmluY2x1ZGVzKFwicmVxdWVzdC51cmkuc2xpY2VcIikpKS50b0JlKGZhbHNlKTtcbiAgICB9XG4gIH0pO1xuXG4gIGl0KFwiY291bnRzIHRoZSBhc3NldFByZWZpeCBiZWhhdmlvciBhZ2FpbnN0IHRoZSBidWRnZXRcIiwgKCkgPT4ge1xuICAgIGNvbnN0IHsgc3RhY2ssIGRpc3RyaWJ1dGlvblByb3BzIH0gPSBzZXR1cChbXSwge1xuICAgICAgcHVibGljRGlyRW50cmllczogQXJyYXkuZnJvbSh7IGxlbmd0aDogMjIgfSwgKF8sIGkpID0+IGBmaWxlJHtpfS50eHRgKSxcbiAgICB9KTtcbiAgICAvLyAzIGZpeGVkICsgMjIgcHVibGljID0gMjUsIGV4YWN0bHkgdGhlIGxpbWl0OyB0aGUgYXNzZXRQcmVmaXggb25lIGlzIHRoZVxuICAgIC8vIDI2dGgsIGFuZCBhIGxpbWl0IGVycm9yIHRoYXQgZGlkIG5vdCBjb3VudCBpdCB3b3VsZCBjb21lIGFzIGEgZGVwbG95XG4gICAgLy8gZmFpbHVyZSBpbnN0ZWFkLlxuICAgIGV4cGVjdChcbiAgICAgICgpID0+XG4gICAgICAgIG5ldyBOZXh0anNEaXN0cmlidXRpb24oc3RhY2ssIFwiRGlzdHJpYnV0aW9uXCIsIHtcbiAgICAgICAgICAuLi5kaXN0cmlidXRpb25Qcm9wcyxcbiAgICAgICAgICBhc3NldFByZWZpeDogXCIvY2RuXCIsXG4gICAgICAgICAgbWF4Q2FjaGVCZWhhdmlvcnM6IDI1LFxuICAgICAgICB9KSxcbiAgICApLnRvVGhyb3coLzI2IENsb3VkRnJvbnQgY2FjaGUgYmVoYXZpb3JzLio0IHVzZWQgYnkgY2RrLW5leHRqcyBpdHNlbGYvcyk7XG4gIH0pO1xuXG4gIGl0KFwiZG9lcyBub3QgY2FjaGUgYSBkeW5hbWljIHJlc3BvbnNlIHRoYXQgc2VuZHMgbm8gQ2FjaGUtQ29udHJvbFwiLCAoKSA9PiB7XG4gICAgLy8gQ0RLJ3MgYENhY2hlUG9saWN5YCBkZWZhdWx0IGlzIGEgZGF5LCB3aGljaCBjYWNoZWQgZXZlcnkgcm91dGUgaGFuZGxlclxuICAgIC8vIHdpdGhvdXQgYSBgQ2FjaGUtQ29udHJvbGAgLSBgL2FwaS9lY2hvYCBpbiBhcHAtcGxheWdyb3VuZCAtIGZvciAyNCBob3Vycy5cbiAgICBjb25zdCB7IHN0YWNrLCBkaXN0cmlidXRpb25Qcm9wcyB9ID0gc2V0dXAoW10pO1xuICAgIG5ldyBOZXh0anNEaXN0cmlidXRpb24oc3RhY2ssIFwiRGlzdHJpYnV0aW9uXCIsIGRpc3RyaWJ1dGlvblByb3BzKTtcbiAgICBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spLmhhc1Jlc291cmNlUHJvcGVydGllcyhcbiAgICAgIFwiQVdTOjpDbG91ZEZyb250OjpDYWNoZVBvbGljeVwiLFxuICAgICAge1xuICAgICAgICBDYWNoZVBvbGljeUNvbmZpZzogTWF0Y2gub2JqZWN0TGlrZSh7XG4gICAgICAgICAgQ29tbWVudDogTWF0Y2guc3RyaW5nTGlrZVJlZ2V4cChcIkR5bmFtaWNcIiksXG4gICAgICAgICAgRGVmYXVsdFRUTDogMCxcbiAgICAgICAgICBNaW5UVEw6IDAsXG4gICAgICAgIH0pLFxuICAgICAgfSxcbiAgICApO1xuICB9KTtcblxuICBpdChcIndpbGRjYXJkcyBhIHB1YmxpYy8gbmFtZSBDbG91ZEZyb250IGNhbm5vdCBzcGVsbFwiLCAoKSA9PiB7XG4gICAgLy8gYHB1YmxpYy9oZWxsbyB3b3JsZC5qcGdgIGlzIGEgdmFsaWQgTmV4dC5qcyBhc3NldCwgYnV0IGEgc3BhY2UgY2Fubm90IGFwcGVhclxuICAgIC8vIGluIGEgcGF0aCBwYXR0ZXJuLCBzbyBjZGstbmV4dGpzIHRocmV3IGF0IHN5bnRoIGFuZCB0aGUgYXBwIGNvdWxkIG5vdCBiZVxuICAgIC8vIGRlcGxveWVkIGF0IGFsbC4gQ2xvdWRGcm9udCBtYXRjaGVzIHRoZSAqZGVjb2RlZCogcGF0aCwgYW5kIGA/YCBtYXRjaGVzIG9uZVxuICAgIC8vIGJ5dGUgb2YgaXQsIHNvIG9uZSBgP2AgcGVyIFVURi04IGJ5dGUgaXMgdGhlIG5hcnJvd2VzdCBwYXR0ZXJuIHRoYXRcbiAgICAvLyBtYXRjaGVzLiBOb3Qgb25lIHBlciBwZXJjZW50LWVuY29kZWQgY2hhcmFjdGVyOiBgaGVsbG8/Pz93b3JsZC5qcGdgXG4gICAgLy8gZGVwbG95cyBhbmQgdGhlbiBuZXZlciBtYXRjaGVzLCB3aGljaCBpcyBhIDQwNCByYXRoZXIgdGhhbiBhIHN5bnRoIGVycm9yLlxuICAgIGNvbnN0IHsgc3RhY2ssIGRpc3RyaWJ1dGlvblByb3BzIH0gPSBzZXR1cChbXSwge1xuICAgICAgcHVibGljRGlyRW50cmllczogW1wiaGVsbG8gd29ybGQuanBnXCIsIFwiw6TDtsO8xaHEjcWZw60ucG5nXCIsIFwiZmF2aWNvbi5pY29cIl0sXG4gICAgfSk7XG4gICAgbmV3IE5leHRqc0Rpc3RyaWJ1dGlvbihzdGFjaywgXCJEaXN0cmlidXRpb25cIiwgZGlzdHJpYnV0aW9uUHJvcHMpO1xuICAgIGV4cGVjdChwYXRoUGF0dGVybnMoc3RhY2spKS50b0VxdWFsKFxuICAgICAgZXhwZWN0LmFycmF5Q29udGFpbmluZyhbXG4gICAgICAgIFwiaGVsbG8/d29ybGQuanBnXCIsXG4gICAgICAgIC8vIDcgY2hhcmFjdGVycywgMiBVVEYtOCBieXRlcyBlYWNoLlxuICAgICAgICBgJHtcIj9cIi5yZXBlYXQoMTQpfS5wbmdgLFxuICAgICAgICBcImZhdmljb24uaWNvXCIsXG4gICAgICBdKSxcbiAgICApO1xuICB9KTtcblxuICBpdChcIndpbGRjYXJkcyBhIHB1YmxpYy8gZGlyZWN0b3J5IG5hbWUgdGhlIHNhbWUgd2F5XCIsICgpID0+IHtcbiAgICBjb25zdCB7IHN0YWNrLCBkaXN0cmlidXRpb25Qcm9wcyB9ID0gc2V0dXAoW10pO1xuICAgIG5ldyBOZXh0anNEaXN0cmlidXRpb24oc3RhY2ssIFwiRGlzdHJpYnV0aW9uXCIsIHtcbiAgICAgIC4uLmRpc3RyaWJ1dGlvblByb3BzLFxuICAgICAgcHVibGljRGlyRW50cmllczogW3sgbmFtZTogXCJteSBpbWFnZXNcIiwgaXNEaXJlY3Rvcnk6IHRydWUgfV0sXG4gICAgfSk7XG4gICAgZXhwZWN0KHBhdGhQYXR0ZXJucyhzdGFjaykpLnRvQ29udGFpbihcIm15P2ltYWdlcy8qXCIpO1xuICB9KTtcblxuICBpdChcInJlamVjdHMgYSBwdWJsaWMvIG5hbWUgdG9vIGxvbmcgdG8gZXhwcmVzcyBhcyBhIHBhdGggcGF0dGVyblwiLCAoKSA9PiB7XG4gICAgY29uc3QgeyBzdGFjaywgZGlzdHJpYnV0aW9uUHJvcHMgfSA9IHNldHVwKFtdLCB7XG4gICAgICBwdWJsaWNEaXJFbnRyaWVzOiBbYCR7XCLDpFwiLnJlcGVhdCgxMzApfS5wbmdgXSxcbiAgICB9KTtcbiAgICBleHBlY3QoXG4gICAgICAoKSA9PiBuZXcgTmV4dGpzRGlzdHJpYnV0aW9uKHN0YWNrLCBcIkRpc3RyaWJ1dGlvblwiLCBkaXN0cmlidXRpb25Qcm9wcyksXG4gICAgKS50b1Rocm93KC9uZWVkcyBhIDI2NC1jaGFyYWN0ZXIgQ2xvdWRGcm9udCBwYXRoIHBhdHRlcm4vKTtcbiAgfSk7XG5cbiAgaXQoXCJza2lwcyBhIHB1YmxpYy8gZW50cnkgd2hvc2UgcGF0dGVybiB3b3VsZCBiZSB3aWxkY2FyZHMgYWxvbmVcIiwgKCkgPT4ge1xuICAgIC8vIGDRhNC+0YLQvi9gIGlzIGA/Pz8/Pz8/Py8qYCwgd2hpY2ggbWF0Y2hlcyBhbnkgOC1ieXRlIGZpcnN0IHNlZ21lbnQuIFB1YmxpY1xuICAgIC8vIGJlaGF2aW9ycyBhcmUgYWRkZWQgYWhlYWQgb2YgdGhlIGNvbXB1dGUgb25lcywgc28gYC9wcm9kdWN0cy80MmAgd291bGQgZ29cbiAgICAvLyB0byBTMyBhbmQgNDAzLiBUaGUgZW50cnkgZ2V0cyBubyBiZWhhdmlvciBhbmQgYSB3YXJuaW5nIGluc3RlYWQuXG4gICAgY29uc3QgeyBzdGFjaywgZGlzdHJpYnV0aW9uUHJvcHMgfSA9IHNldHVwKFtdKTtcbiAgICBuZXcgTmV4dGpzRGlzdHJpYnV0aW9uKHN0YWNrLCBcIkRpc3RyaWJ1dGlvblwiLCB7XG4gICAgICAuLi5kaXN0cmlidXRpb25Qcm9wcyxcbiAgICAgIHB1YmxpY0RpckVudHJpZXM6IFtcbiAgICAgICAgeyBuYW1lOiBcItGE0L7RgtC+XCIsIGlzRGlyZWN0b3J5OiB0cnVlIH0sXG4gICAgICAgIHsgbmFtZTogXCLlm75cIiwgaXNEaXJlY3Rvcnk6IGZhbHNlIH0sXG4gICAgICAgIHsgbmFtZTogXCLRhNC+0YLQvi0xXCIsIGlzRGlyZWN0b3J5OiB0cnVlIH0sXG4gICAgICBdLFxuICAgIH0pO1xuICAgIGNvbnN0IHBhdHRlcm5zID0gcGF0aFBhdHRlcm5zKHN0YWNrKTtcbiAgICBleHBlY3QocGF0dGVybnMpLm5vdC50b0NvbnRhaW4oYCR7XCI/XCIucmVwZWF0KDgpfS8qYCk7XG4gICAgZXhwZWN0KHBhdHRlcm5zKS5ub3QudG9Db250YWluKFwiPz8/XCIpO1xuICAgIC8vIE9uZSBsaXRlcmFsIGNoYXJhY3RlciBrZWVwcyBhIHBhdHRlcm4gbmFycm93IGVub3VnaCB0byBrZWVwLlxuICAgIGV4cGVjdChwYXR0ZXJucykudG9Db250YWluKGAke1wiP1wiLnJlcGVhdCg4KX0tMS8qYCk7XG4gICAgQW5ub3RhdGlvbnMuZnJvbVN0YWNrKHN0YWNrKS5oYXNXYXJuaW5nKFxuICAgICAgXCIqXCIsXG4gICAgICBNYXRjaC5zdHJpbmdMaWtlUmVnZXhwKCdcItGE0L7RgtC+XCIsIFwi5Zu+XCInKSxcbiAgICApO1xuICB9KTtcblxuICBpdChcImRvZXMgbm90IGNvdW50IGEgc2tpcHBlZCBwdWJsaWMvIGVudHJ5IGFnYWluc3QgdGhlIGJ1ZGdldFwiLCAoKSA9PiB7XG4gICAgY29uc3QgeyBzdGFjaywgZGlzdHJpYnV0aW9uUHJvcHMgfSA9IHNldHVwKFtdLCB7XG4gICAgICBwdWJsaWNEaXJFbnRyaWVzOiBbXG4gICAgICAgIC4uLkFycmF5LmZyb20oeyBsZW5ndGg6IDIyIH0sIChfLCBpKSA9PiBgZmlsZSR7aX0udHh0YCksXG4gICAgICAgIFwi5Zu+XCIsXG4gICAgICBdLFxuICAgIH0pO1xuICAgIC8vIDMgZml4ZWQgKyAyMiA9IDI1LCBleGFjdGx5IHRoZSBsaW1pdDsgdGhlIHNraXBwZWQgZW50cnkgYWRkcyBub3RoaW5nLlxuICAgIGV4cGVjdChcbiAgICAgICgpID0+XG4gICAgICAgIG5ldyBOZXh0anNEaXN0cmlidXRpb24oc3RhY2ssIFwiRGlzdHJpYnV0aW9uXCIsIHtcbiAgICAgICAgICAuLi5kaXN0cmlidXRpb25Qcm9wcyxcbiAgICAgICAgICBtYXhDYWNoZUJlaGF2aW9yczogMjUsXG4gICAgICAgIH0pLFxuICAgICkubm90LnRvVGhyb3coKTtcbiAgfSk7XG5cbiAgaXQoXCJjaGVja3MgdGhlIGxlbmd0aCBvZiB0aGUgZmluYWwgcGF0dGVybiwgYmFzZVBhdGggYW5kIC8qIGluY2x1ZGVkXCIsICgpID0+IHtcbiAgICAvLyA4MyB0aHJlZS1ieXRlIGNoYXJhY3RlcnMgKyBcIi5wZGZcIiBpcyBhIDI1My1jaGFyYWN0ZXIgcGF0dGVybiBvbiBpdHMgb3duLFxuICAgIC8vIHVuZGVyIHRoZSBsaW1pdDsgYC9kb2NzL2AgaW4gZnJvbnQgdGFrZXMgaXQgdG8gMjU5LCB3aGljaCBDbG91ZEZyb250XG4gICAgLy8gcmVqZWN0cyBhdCBkZXBsb3kuXG4gICAgY29uc3QgbmFtZSA9IGAke1wi5Zu+XCIucmVwZWF0KDgzKX0ucGRmYDtcbiAgICBjb25zdCB7IHN0YWNrLCBkaXN0cmlidXRpb25Qcm9wcyB9ID0gc2V0dXAoW10sIHtcbiAgICAgIHB1YmxpY0RpckVudHJpZXM6IFtuYW1lXSxcbiAgICB9KTtcbiAgICBleHBlY3QoXG4gICAgICAoKSA9PlxuICAgICAgICBuZXcgTmV4dGpzRGlzdHJpYnV0aW9uKHN0YWNrLCBcIkRpc3RyaWJ1dGlvblwiLCB7XG4gICAgICAgICAgLi4uZGlzdHJpYnV0aW9uUHJvcHMsXG4gICAgICAgICAgYmFzZVBhdGg6IFwiL2RvY3NcIixcbiAgICAgICAgfSksXG4gICAgKS50b1Rocm93KC9uZWVkcyBhIDI1OS1jaGFyYWN0ZXIgQ2xvdWRGcm9udCBwYXRoIHBhdHRlcm4vKTtcblxuICAgIGNvbnN0IHsgc3RhY2s6IGRpclN0YWNrLCBkaXN0cmlidXRpb25Qcm9wczogZGlyUHJvcHMgfSA9IHNldHVwKFtdKTtcbiAgICBleHBlY3QoXG4gICAgICAoKSA9PlxuICAgICAgICBuZXcgTmV4dGpzRGlzdHJpYnV0aW9uKGRpclN0YWNrLCBcIkRpc3RyaWJ1dGlvblwiLCB7XG4gICAgICAgICAgLi4uZGlyUHJvcHMsXG4gICAgICAgICAgLy8gMjU0IGNoYXJhY3RlcnMsIHVuZGVyIHRoZSBsaW1pdCB1bnRpbCB0aGUgYC8qYC5cbiAgICAgICAgICBwdWJsaWNEaXJFbnRyaWVzOiBbXG4gICAgICAgICAgICB7IG5hbWU6IGBhYiR7XCLlm75cIi5yZXBlYXQoODQpfWAsIGlzRGlyZWN0b3J5OiB0cnVlIH0sXG4gICAgICAgICAgXSxcbiAgICAgICAgfSksXG4gICAgKS50b1Rocm93KC9uZWVkcyBhIDI1Ni1jaGFyYWN0ZXIvKTtcbiAgfSk7XG5cbiAgaXQoXCJkcm9wcyB0aGUgY2FjaGUga2V5IHdoZW4gYW4gb3ZlcnJpZGUgZGlzYWJsZXMgZHluYW1pYyBjYWNoaW5nXCIsICgpID0+IHtcbiAgICAvLyBDbG91ZEZyb250IHJlamVjdHMgYSBwb2xpY3kgdGhhdCBjYWNoZXMgbm90aGluZyBidXQga2V5cyBvbiBoZWFkZXJzLiBDREtcbiAgICAvLyB1c2VkIHRvIHJhaXNlIGBtYXhUdGw6IDBgIHRvIHRoZSBkYXktbG9uZyBkZWZhdWx0LCBzbyB0aGUgb3ZlcnJpZGVcbiAgICAvLyBkZXBsb3llZDsgd2l0aCBhIGRlZmF1bHQgb2YgMCBpdCBpcyBob25vcmVkLCBhbmQgbXVzdCBzdGF5IGRlcGxveWFibGUuXG4gICAgY29uc3QgeyBzdGFjaywgZGlzdHJpYnV0aW9uUHJvcHMgfSA9IHNldHVwKFtdKTtcbiAgICBuZXcgTmV4dGpzRGlzdHJpYnV0aW9uKHN0YWNrLCBcIkRpc3RyaWJ1dGlvblwiLCB7XG4gICAgICAuLi5kaXN0cmlidXRpb25Qcm9wcyxcbiAgICAgIG92ZXJyaWRlczoge1xuICAgICAgICBkeW5hbWljQ2FjaGVQb2xpY3lQcm9wczogeyBtYXhUdGw6IER1cmF0aW9uLnNlY29uZHMoMCkgfSxcbiAgICAgIH0sXG4gICAgfSk7XG4gICAgVGVtcGxhdGUuZnJvbVN0YWNrKHN0YWNrKS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoXG4gICAgICBcIkFXUzo6Q2xvdWRGcm9udDo6Q2FjaGVQb2xpY3lcIixcbiAgICAgIHtcbiAgICAgICAgQ2FjaGVQb2xpY3lDb25maWc6IE1hdGNoLm9iamVjdExpa2Uoe1xuICAgICAgICAgIENvbW1lbnQ6IE1hdGNoLnN0cmluZ0xpa2VSZWdleHAoXCJEeW5hbWljXCIpLFxuICAgICAgICAgIERlZmF1bHRUVEw6IDAsXG4gICAgICAgICAgTWF4VFRMOiAwLFxuICAgICAgICAgIE1pblRUTDogMCxcbiAgICAgICAgICBQYXJhbWV0ZXJzSW5DYWNoZUtleUFuZEZvcndhcmRlZFRvT3JpZ2luOiB7XG4gICAgICAgICAgICBDb29raWVzQ29uZmlnOiB7IENvb2tpZUJlaGF2aW9yOiBcIm5vbmVcIiB9LFxuICAgICAgICAgICAgRW5hYmxlQWNjZXB0RW5jb2RpbmdCcm90bGk6IGZhbHNlLFxuICAgICAgICAgICAgRW5hYmxlQWNjZXB0RW5jb2RpbmdHemlwOiBmYWxzZSxcbiAgICAgICAgICAgIEhlYWRlcnNDb25maWc6IHsgSGVhZGVyQmVoYXZpb3I6IFwibm9uZVwiIH0sXG4gICAgICAgICAgICBRdWVyeVN0cmluZ3NDb25maWc6IHsgUXVlcnlTdHJpbmdCZWhhdmlvcjogXCJub25lXCIgfSxcbiAgICAgICAgICB9LFxuICAgICAgICB9KSxcbiAgICAgIH0sXG4gICAgKTtcbiAgfSk7XG5cbiAgaXQoXCJrZWVwcyB0aGUgY2FjaGUga2V5IHdoZW4gYSB6ZXJvIG1heFR0bCBpcyByYWlzZWQgYnkgYW5vdGhlciBUVExcIiwgKCkgPT4ge1xuICAgIC8vIENESyByYWlzZXMgYG1heFR0bGAgdG8gYGRlZmF1bHRUdGxgLCBzbyB0aGlzIHBvbGljeSBjYWNoZXMgZm9yIGFuIGhvdXIuXG4gICAgLy8gRHJvcHBpbmcgdGhlIGtleSBmb3IgaXQgd291bGQgc2VydmUgb25lIHZpZXdlcidzIHBhZ2UgdG8gZXZlcnkgdmlld2VyLlxuICAgIGNvbnN0IHsgc3RhY2ssIGRpc3RyaWJ1dGlvblByb3BzIH0gPSBzZXR1cChbXSk7XG4gICAgbmV3IE5leHRqc0Rpc3RyaWJ1dGlvbihzdGFjaywgXCJEaXN0cmlidXRpb25cIiwge1xuICAgICAgLi4uZGlzdHJpYnV0aW9uUHJvcHMsXG4gICAgICBvdmVycmlkZXM6IHtcbiAgICAgICAgZHluYW1pY0NhY2hlUG9saWN5UHJvcHM6IHtcbiAgICAgICAgICBtYXhUdGw6IER1cmF0aW9uLnNlY29uZHMoMCksXG4gICAgICAgICAgZGVmYXVsdFR0bDogRHVyYXRpb24uaG91cnMoMSksXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgIH0pO1xuICAgIFRlbXBsYXRlLmZyb21TdGFjayhzdGFjaykuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKFxuICAgICAgXCJBV1M6OkNsb3VkRnJvbnQ6OkNhY2hlUG9saWN5XCIsXG4gICAgICB7XG4gICAgICAgIENhY2hlUG9saWN5Q29uZmlnOiBNYXRjaC5vYmplY3RMaWtlKHtcbiAgICAgICAgICBDb21tZW50OiBNYXRjaC5zdHJpbmdMaWtlUmVnZXhwKFwiRHluYW1pY1wiKSxcbiAgICAgICAgICBNYXhUVEw6IDM2MDAsXG4gICAgICAgICAgUGFyYW1ldGVyc0luQ2FjaGVLZXlBbmRGb3J3YXJkZWRUb09yaWdpbjogTWF0Y2gub2JqZWN0TGlrZSh7XG4gICAgICAgICAgICBDb29raWVzQ29uZmlnOiB7IENvb2tpZUJlaGF2aW9yOiBcImFsbFwiIH0sXG4gICAgICAgICAgICBRdWVyeVN0cmluZ3NDb25maWc6IHsgUXVlcnlTdHJpbmdCZWhhdmlvcjogXCJhbGxcIiB9LFxuICAgICAgICAgIH0pLFxuICAgICAgICB9KSxcbiAgICAgIH0sXG4gICAgKTtcbiAgfSk7XG5cbiAgaXQoXCJrZXlzIHRoZSBkeW5hbWljIGNhY2hlIG9ubHkgb24gaGVhZGVycyB0aGUgb3JpZ2luIHJlYWRzXCIsICgpID0+IHtcbiAgICAvLyBgeC1tYXRjaGVkLXBhdGhgIGlzIHN0cmlwcGVkIGJ5IHRoZSBydW50aW1lIGFuZCBgeC1uZXh0LWNhY2hlLXRhZ3NgIGlzXG4gICAgLy8gbmV2ZXIgc2VudCBieSB2aWV3ZXJzOiBrZXlpbmcgb24gZWl0aGVyIHNwZW5kcyBhIGhlYWRlciBzbG90IGFuZCBsZXRzIGFcbiAgICAvLyBjbGllbnQgZnJhZ21lbnQgdGhlIGNhY2hlIGZvciBub3RoaW5nLlxuICAgIGNvbnN0IHsgc3RhY2ssIGRpc3RyaWJ1dGlvblByb3BzIH0gPSBzZXR1cChbXSk7XG4gICAgbmV3IE5leHRqc0Rpc3RyaWJ1dGlvbihzdGFjaywgXCJEaXN0cmlidXRpb25cIiwgZGlzdHJpYnV0aW9uUHJvcHMpO1xuICAgIGNvbnN0IHBvbGljaWVzID0gVGVtcGxhdGUuZnJvbVN0YWNrKHN0YWNrKS5maW5kUmVzb3VyY2VzKFxuICAgICAgXCJBV1M6OkNsb3VkRnJvbnQ6OkNhY2hlUG9saWN5XCIsXG4gICAgICB7XG4gICAgICAgIFByb3BlcnRpZXM6IHtcbiAgICAgICAgICBDYWNoZVBvbGljeUNvbmZpZzogeyBDb21tZW50OiBNYXRjaC5zdHJpbmdMaWtlUmVnZXhwKFwiRHluYW1pY1wiKSB9LFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICApO1xuICAgIGNvbnN0IFtwb2xpY3ldID0gT2JqZWN0LnZhbHVlcyhwb2xpY2llcyk7XG4gICAgY29uc3QgaGVhZGVyczogc3RyaW5nW10gPVxuICAgICAgcG9saWN5LlByb3BlcnRpZXMuQ2FjaGVQb2xpY3lDb25maWdcbiAgICAgICAgLlBhcmFtZXRlcnNJbkNhY2hlS2V5QW5kRm9yd2FyZGVkVG9PcmlnaW4uSGVhZGVyc0NvbmZpZy5IZWFkZXJzO1xuICAgIGV4cGVjdChoZWFkZXJzKS5ub3QudG9Db250YWluKFwieC1tYXRjaGVkLXBhdGhcIik7XG4gICAgZXhwZWN0KGhlYWRlcnMpLm5vdC50b0NvbnRhaW4oXCJ4LW5leHQtY2FjaGUtdGFnc1wiKTtcbiAgICBleHBlY3QoaGVhZGVycykudG9Db250YWluKFwicnNjXCIpO1xuICB9KTtcblxuICBpdChcInJlamVjdHMgc3BsaXR0aW5nIG9uIGEgZGVwbG95bWVudCB0eXBlIHRoYXQgY2Fubm90IHJvdXRlIGl0XCIsICgpID0+IHtcbiAgICBjb25zdCB7IHN0YWNrLCBmdW5jdGlvbkdyb3VwcywgZnVuY3Rpb25Hcm91cEJlaGF2aW9ycywgZGlzdHJpYnV0aW9uUHJvcHMgfSA9XG4gICAgICBzZXR1cChbXCJhcGlcIl0pO1xuICAgIGV4cGVjdChcbiAgICAgICgpID0+XG4gICAgICAgIG5ldyBOZXh0anNEaXN0cmlidXRpb24oc3RhY2ssIFwiRGlzdHJpYnV0aW9uXCIsIHtcbiAgICAgICAgICAuLi5kaXN0cmlidXRpb25Qcm9wcyxcbiAgICAgICAgICBuZXh0anNUeXBlOiBOZXh0anNUeXBlLkdMT0JBTF9DT05UQUlORVJTLFxuICAgICAgICAgIGxvYWRCYWxhbmNlcjogdW5kZWZpbmVkLFxuICAgICAgICAgIGZ1bmN0aW9uR3JvdXBzLFxuICAgICAgICAgIGZ1bmN0aW9uR3JvdXBCZWhhdmlvcnMsXG4gICAgICAgIH0pLFxuICAgICkudG9UaHJvdygvb25seSBzdXBwb3J0ZWQgYnkgTmV4dGpzR2xvYmFsRnVuY3Rpb25zLyk7XG4gIH0pO1xufSk7XG5cbi8qKlxuICogVGhlIHZpZXdlci1yZXF1ZXN0IGZ1bmN0aW9uLCBydW4gYXMgQ2xvdWRGcm9udCB3b3VsZCBydW4gaXQuXG4gKlxuICogSXQgaXMgYXV0aG9yZWQgYXMgYSBzdHJpbmcgaW5zaWRlIGEgVHlwZVNjcmlwdCB0ZW1wbGF0ZSBsaXRlcmFsLCBzbyBldmVyeVxuICogYmFja3NsYXNoIGluIGl0IGlzIGVzY2FwZWQgdHdpY2UgLSBhbmQgYSByZWdleCB0aGF0IGNvbGxhcHNlcyBzbGFzaGVzIGlzIG5vdGhpbmdcbiAqIGJ1dCBiYWNrc2xhc2hlcy4gQXNzZXJ0aW5nIG9uIGJlaGF2aW9yIHJhdGhlciB0aGFuIG9uIHRoZSB0ZXh0IGlzIHdoYXQgY2F0Y2hlcyBhblxuICogZXNjYXBpbmcgbWlzdGFrZSwgd2hpY2ggYSBzbmFwc2hvdCB3b3VsZCBoYXBwaWx5IHJlY29yZC5cbiAqL1xuZGVzY3JpYmUoXCJ0aGUgdmlld2VyLXJlcXVlc3QgQ2xvdWRGcm9udCBGdW5jdGlvblwiLCAoKSA9PiB7XG4gIGludGVyZmFjZSBRdWVyeVZhbHVlIHtcbiAgICB2YWx1ZTogc3RyaW5nO1xuICAgIG11bHRpVmFsdWU/OiBBcnJheTx7IHZhbHVlOiBzdHJpbmcgfT47XG4gIH1cbiAgaW50ZXJmYWNlIEZ1bmN0aW9uUmVxdWVzdCB7XG4gICAgdXJpOiBzdHJpbmc7XG4gICAgcXVlcnlzdHJpbmc6IFJlY29yZDxzdHJpbmcsIFF1ZXJ5VmFsdWU+O1xuICAgIGhlYWRlcnM6IFJlY29yZDxzdHJpbmcsIHsgdmFsdWU6IHN0cmluZyB9PjtcbiAgfVxuICB0eXBlIEZ1bmN0aW9uUmVzdWx0ID1cbiAgICB8IEZ1bmN0aW9uUmVxdWVzdFxuICAgIHwgeyBzdGF0dXNDb2RlOiBudW1iZXI7IGhlYWRlcnM6IFJlY29yZDxzdHJpbmcsIHsgdmFsdWU6IHN0cmluZyB9PiB9O1xuXG4gIGNvbnN0IGhhbmRsZXIgPSAoKCkgPT4ge1xuICAgIGNvbnN0IHN0YWNrID0gbmV3IFN0YWNrKG5ldyBBcHAoKSwgXCJGblN0YWNrXCIpO1xuICAgIGNvbnN0IGZuID0gbmV3IExhbWJkYUZ1bmN0aW9uKHN0YWNrLCBcIkZuXCIsIHtcbiAgICAgIGNvZGU6IENvZGUuZnJvbUlubGluZShcImV4cG9ydHMuaGFuZGxlciA9ICgpID0+IHt9O1wiKSxcbiAgICAgIGhhbmRsZXI6IFwiaW5kZXguaGFuZGxlclwiLFxuICAgICAgcnVudGltZTogUnVudGltZS5OT0RFSlNfMjJfWCxcbiAgICB9KTtcbiAgICBuZXcgTmV4dGpzRGlzdHJpYnV0aW9uKHN0YWNrLCBcIkRpc3RyaWJ1dGlvblwiLCB7XG4gICAgICBhc3NldHNCdWNrZXQ6IG5ldyBCdWNrZXQoc3RhY2ssIFwiQXNzZXRzXCIpLFxuICAgICAgZnVuY3Rpb25Vcmw6IG5ldyBGdW5jdGlvblVybChzdGFjaywgXCJVcmxcIiwge1xuICAgICAgICBmdW5jdGlvbjogZm4sXG4gICAgICAgIGF1dGhUeXBlOiBGdW5jdGlvblVybEF1dGhUeXBlLkFXU19JQU0sXG4gICAgICB9KSxcbiAgICAgIG5leHRqc1R5cGU6IE5leHRqc1R5cGUuR0xPQkFMX0ZVTkNUSU9OUyxcbiAgICAgIHB1YmxpY0RpckVudHJpZXM6IFtdLFxuICAgIH0pO1xuICAgIGNvbnN0IGZ1bmN0aW9ucyA9IFRlbXBsYXRlLmZyb21TdGFjayhzdGFjaykuZmluZFJlc291cmNlcyhcbiAgICAgIFwiQVdTOjpDbG91ZEZyb250OjpGdW5jdGlvblwiLFxuICAgICk7XG4gICAgY29uc3QgY29kZSA9IE9iamVjdC52YWx1ZXMoZnVuY3Rpb25zKVswXS5Qcm9wZXJ0aWVzLkZ1bmN0aW9uQ29kZSBhcyBzdHJpbmc7XG4gICAgcmV0dXJuIHJ1bkluTmV3Q29udGV4dChgJHtjb2RlfVxcbmhhbmRsZXJgKSBhcyAoZXZlbnQ6IHtcbiAgICAgIHJlcXVlc3Q6IEZ1bmN0aW9uUmVxdWVzdDtcbiAgICB9KSA9PiBGdW5jdGlvblJlc3VsdDtcbiAgfSkoKTtcblxuICBjb25zdCBzZW5kID0gKFxuICAgIHVyaTogc3RyaW5nLFxuICAgIHF1ZXJ5c3RyaW5nOiBSZWNvcmQ8c3RyaW5nLCBRdWVyeVZhbHVlPiA9IHt9LFxuICApOiBGdW5jdGlvblJlc3VsdCA9PlxuICAgIGhhbmRsZXIoe1xuICAgICAgcmVxdWVzdDogeyB1cmksIHF1ZXJ5c3RyaW5nLCBoZWFkZXJzOiB7IGhvc3Q6IHsgdmFsdWU6IFwiYS50ZXN0XCIgfSB9IH0sXG4gICAgfSk7XG5cbiAgY29uc3QgcmVkaXJlY3QgPSAocmVzdWx0OiBGdW5jdGlvblJlc3VsdCkgPT4gKHtcbiAgICBzdGF0dXNDb2RlOiAocmVzdWx0IGFzIHsgc3RhdHVzQ29kZTogbnVtYmVyIH0pLnN0YXR1c0NvZGUsXG4gICAgbG9jYXRpb246IHJlc3VsdC5oZWFkZXJzLmxvY2F0aW9uPy52YWx1ZSxcbiAgfSk7XG5cbiAgaXQuZWFjaChbXG4gICAgW1wiLy9cIiwgXCIvXCJdLFxuICAgIFtcIi8vL1wiLCBcIi9cIl0sXG4gICAgW1wiL2Zvby8vYmFyXCIsIFwiL2Zvby9iYXJcIl0sXG4gICAgW1wiL2Jhc2VwYXRoLy90by1zdlwiLCBcIi9iYXNlcGF0aC90by1zdlwiXSxcbiAgICBbXCIvYVxcXFxiXCIsIFwiL2EvYlwiXSxcbiAgXSkoXCJyZWRpcmVjdHMgJXMgdG8gJXMgd2l0aCBhIDMwOFwiLCAodXJpLCBsb2NhdGlvbikgPT4ge1xuICAgIGV4cGVjdChyZWRpcmVjdChzZW5kKHVyaSkpKS50b0VxdWFsKHsgc3RhdHVzQ29kZTogMzA4LCBsb2NhdGlvbiB9KTtcbiAgfSk7XG5cbiAgaXQoXCJrZWVwcyB0aGUgcXVlcnkgb24gdGhlIHJlZGlyZWN0LCBpbmNsdWRpbmcgcmVwZWF0ZWQga2V5c1wiLCAoKSA9PiB7XG4gICAgLy8gQ2xvdWRGcm9udCdzIHNoYXBlIGZvciBhIHJlcGVhdGVkIGtleTogYG11bHRpVmFsdWVgIGhvbGRzIGV2ZXJ5IHZhbHVlLFxuICAgIC8vIHRoZSBmaXJzdCBpbmNsdWRlZCwgYW5kIGB2YWx1ZWAgcmVwZWF0cyB0aGF0IGZpcnN0IG9uZS5cbiAgICBjb25zdCByZXN1bHQgPSBzZW5kKFwiL3gvL3lcIiwge1xuICAgICAganNvbjogeyB2YWx1ZTogXCJ0cnVlXCIgfSxcbiAgICAgIGE6IHsgdmFsdWU6IFwiMVwiLCBtdWx0aVZhbHVlOiBbeyB2YWx1ZTogXCIxXCIgfSwgeyB2YWx1ZTogXCIyXCIgfV0gfSxcbiAgICAgIGZsYWc6IHsgdmFsdWU6IFwiXCIgfSxcbiAgICB9KTtcbiAgICBleHBlY3QocmVkaXJlY3QocmVzdWx0KS5sb2NhdGlvbikudG9CZShcIi94L3k/anNvbj10cnVlJmE9MSZhPTImZmxhZ1wiKTtcbiAgfSk7XG5cbiAgaXQoXCJwYXNzZXMgcGVyY2VudC1lbmNvZGVkIHF1ZXJ5IHZhbHVlcyB0aHJvdWdoIHdpdGhvdXQgcmUtZW5jb2RpbmdcIiwgKCkgPT4ge1xuICAgIC8vIENsb3VkRnJvbnQgaGFuZHMgdGhlIGZ1bmN0aW9uIHRoZSB2YWx1ZXMgc3RpbGwgZW5jb2RlZCwgYXMgc2VudC5cbiAgICBjb25zdCByZXN1bHQgPSBzZW5kKFwiL3gvL3lcIiwge1xuICAgICAgYTogeyB2YWx1ZTogXCIlMjBiJTI2Y1wiIH0sXG4gICAgICBwOiB7IHZhbHVlOiBcIjErMlwiIH0sXG4gICAgfSk7XG4gICAgZXhwZWN0KHJlZGlyZWN0KHJlc3VsdCkubG9jYXRpb24pLnRvQmUoXCIveC95P2E9JTIwYiUyNmMmcD0xKzJcIik7XG4gIH0pO1xuXG4gIGl0KFwicGFzc2VzIGFuIG9yZGluYXJ5IHBhdGggdGhyb3VnaCB3aXRoIHgtZm9yd2FyZGVkLWhvc3Qgc2V0XCIsICgpID0+IHtcbiAgICBjb25zdCByZXN1bHQgPSBzZW5kKFwiL2EvYlwiKSBhcyBGdW5jdGlvblJlcXVlc3Q7XG4gICAgZXhwZWN0KHJlc3VsdC51cmkpLnRvQmUoXCIvYS9iXCIpO1xuICAgIC8vIENsb3VkRnJvbnQgcmV3cml0ZXMgYGhvc3RgIHRvIHRoZSBvcmlnaW4gZG9tYWluLCBzbyB0aGUgYXBwIHdvdWxkIG90aGVyd2lzZVxuICAgIC8vIGJ1aWxkIGV2ZXJ5IGFic29sdXRlIFVSTCBhZ2FpbnN0IHRoZSBGdW5jdGlvbiBVUkwncyBob3N0bmFtZS5cbiAgICBleHBlY3QocmVzdWx0LmhlYWRlcnNbXCJ4LWZvcndhcmRlZC1ob3N0XCJdLnZhbHVlKS50b0JlKFwiYS50ZXN0XCIpO1xuICB9KTtcblxuICBpdChcIm92ZXJ3cml0ZXMgYSBjbGllbnQtc3VwcGxpZWQgeC1mb3J3YXJkZWQtaG9zdFwiLCAoKSA9PiB7XG4gICAgLy8gVGhlIHJ1bnRpbWUgdHJ1c3RzIHRoaXMgaGVhZGVyIGJlaGluZCB0aGUgRnVuY3Rpb24gVVJMLCBzbyBhIHZpZXdlcidzXG4gICAgLy8gb3duIHZhbHVlIG11c3Qgbm90IHN1cnZpdmUgdG8gYnVpbGQgcmVkaXJlY3RzIG9yIG9yaWdpbiBjaGVja3MuXG4gICAgY29uc3QgcmVzdWx0ID0gaGFuZGxlcih7XG4gICAgICByZXF1ZXN0OiB7XG4gICAgICAgIHVyaTogXCIvYVwiLFxuICAgICAgICBxdWVyeXN0cmluZzoge30sXG4gICAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgICBob3N0OiB7IHZhbHVlOiBcImEudGVzdFwiIH0sXG4gICAgICAgICAgXCJ4LWZvcndhcmRlZC1ob3N0XCI6IHsgdmFsdWU6IFwiZXZpbC50ZXN0XCIgfSxcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgfSkgYXMgRnVuY3Rpb25SZXF1ZXN0O1xuICAgIGV4cGVjdChyZXN1bHQuaGVhZGVyc1tcIngtZm9yd2FyZGVkLWhvc3RcIl0udmFsdWUpLnRvQmUoXCJhLnRlc3RcIik7XG4gIH0pO1xufSk7XG4iXX0=
|