cdk-nextjs 0.6.2 → 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.
Files changed (263) hide show
  1. package/.jsii +2404 -2723
  2. package/.jsii.tabl.json +1 -1
  3. package/API.md +1681 -1930
  4. package/CLAUDE.md +2 -1
  5. package/README.md +429 -130
  6. package/assets/lambdas/post-deploy/post-deploy.lambda/index.js +148 -169
  7. package/assets/lambdas/redeploy-stage/redeploy-stage.lambda/index.js +875 -0
  8. package/docs/adapter-runtime.md +185 -0
  9. package/docs/breaking-changes.md +667 -4
  10. package/docs/caching-guide.md +156 -112
  11. package/docs/cdk-nextjs-cfn-exec-policy.json +2 -1
  12. package/docs/development-guide.md +1 -1
  13. package/docs/harness-coverage.md +598 -0
  14. package/docs/init-cache-deployment.md +32 -1
  15. package/docs/next-build-output-guide.md +30 -20
  16. package/examples/README.md +5 -1
  17. package/lib/adapter/__fixtures__/app-playground-base-path.json +4926 -0
  18. package/lib/adapter/__fixtures__/app-playground.json +4917 -0
  19. package/lib/adapter/__fixtures__/pages-i18n.json +1728 -0
  20. package/lib/adapter/adapter.mjs +1548 -185
  21. package/lib/adapter/adapter.test.js +106 -0
  22. package/lib/adapter/aws-cache-store.d.ts +489 -0
  23. package/lib/adapter/aws-cache-store.js +1004 -0
  24. package/lib/adapter/aws-cache-store.test.d.ts +1 -0
  25. package/lib/adapter/aws-cache-store.test.js +361 -0
  26. package/lib/adapter/build-outputs.d.ts +80 -0
  27. package/lib/adapter/build-outputs.js +852 -0
  28. package/lib/adapter/build-outputs.test.d.ts +1 -0
  29. package/lib/adapter/build-outputs.test.js +861 -0
  30. package/lib/adapter/cache-handler.d.ts +9 -2
  31. package/lib/adapter/cache-handler.js +72 -96
  32. package/lib/adapter/cache-handler.mjs +18956 -17774
  33. package/lib/adapter/cache-handler.test.js +263 -31
  34. package/lib/adapter/cache-kinds.d.ts +32 -0
  35. package/lib/adapter/cache-kinds.js +35 -0
  36. package/lib/adapter/cache-kinds.test.d.ts +1 -0
  37. package/lib/adapter/cache-kinds.test.js +69 -0
  38. package/lib/adapter/cache-utils.d.ts +90 -0
  39. package/lib/adapter/cache-utils.js +199 -4
  40. package/lib/adapter/cache-utils.test.d.ts +1 -0
  41. package/lib/adapter/cache-utils.test.js +179 -0
  42. package/lib/adapter/function-groups.d.ts +215 -0
  43. package/lib/adapter/function-groups.js +1099 -0
  44. package/lib/adapter/function-groups.test.d.ts +1 -0
  45. package/lib/adapter/function-groups.test.js +743 -0
  46. package/lib/adapter/init-cache.d.ts +6 -0
  47. package/lib/adapter/init-cache.js +245 -0
  48. package/lib/adapter/init-cache.test.d.ts +1 -0
  49. package/lib/adapter/init-cache.test.js +137 -0
  50. package/lib/adapter/local-file-cache-handler.d.ts +23 -0
  51. package/lib/adapter/local-file-cache-handler.js +52 -1
  52. package/lib/adapter/memory-cache-handler.d.ts +26 -17
  53. package/lib/adapter/memory-cache-handler.js +32 -50
  54. package/lib/adapter/memory-cache-handler.test.js +105 -85
  55. package/lib/adapter/s3-cache-handler.d.ts +179 -40
  56. package/lib/adapter/s3-cache-handler.js +807 -277
  57. package/lib/adapter/s3-cache-handler.test.js +1186 -31
  58. package/lib/adapter/use-cache-common.d.ts +90 -0
  59. package/lib/adapter/use-cache-common.js +250 -0
  60. package/lib/adapter/use-cache-common.test.d.ts +1 -0
  61. package/lib/adapter/use-cache-common.test.js +646 -0
  62. package/lib/adapter/use-cache-default-handler.d.ts +24 -0
  63. package/lib/adapter/use-cache-default-handler.js +90 -0
  64. package/lib/adapter/use-cache-default-handler.mjs +38645 -0
  65. package/lib/adapter/use-cache-handlers.test.d.ts +1 -0
  66. package/lib/adapter/use-cache-handlers.test.js +349 -0
  67. package/lib/adapter/use-cache-remote-handler.d.ts +26 -0
  68. package/lib/adapter/use-cache-remote-handler.js +186 -0
  69. package/lib/{image-optimization/handler.mjs → adapter/use-cache-remote-handler.mjs} +33572 -38798
  70. package/lib/generated-structs/OptionalNextjsApiProps.d.ts +59 -0
  71. package/lib/generated-structs/OptionalNextjsApiProps.js +3 -0
  72. package/lib/generated-structs/OptionalNextjsCacheProps.d.ts +15 -6
  73. package/lib/generated-structs/OptionalNextjsCacheProps.js +1 -1
  74. package/lib/generated-structs/OptionalNextjsContainersProps.d.ts +25 -11
  75. package/lib/generated-structs/OptionalNextjsContainersProps.js +1 -1
  76. package/lib/generated-structs/OptionalNextjsDistributionProps.d.ts +31 -9
  77. package/lib/generated-structs/OptionalNextjsDistributionProps.js +1 -1
  78. package/lib/generated-structs/OptionalNextjsPostDeployProps.d.ts +8 -0
  79. package/lib/generated-structs/OptionalNextjsPostDeployProps.js +1 -1
  80. package/lib/generated-structs/OptionalPostDeployCustomResourceProperties.d.ts +6 -4
  81. package/lib/generated-structs/OptionalPostDeployCustomResourceProperties.js +1 -1
  82. package/lib/index.d.ts +6 -7
  83. package/lib/index.js +2 -4
  84. package/lib/lambdas/post-deploy/prune-cache-bucket.d.ts +7 -3
  85. package/lib/lambdas/post-deploy/prune-cache-bucket.js +57 -71
  86. package/lib/lambdas/post-deploy/prune-cache-bucket.test.d.ts +1 -0
  87. package/lib/lambdas/post-deploy/prune-cache-bucket.test.js +114 -0
  88. package/lib/lambdas/post-deploy/prune-revalidation-table.d.ts +1 -0
  89. package/lib/lambdas/post-deploy/prune-revalidation-table.js +23 -31
  90. package/lib/lambdas/post-deploy/prune-revalidation-table.test.d.ts +1 -0
  91. package/lib/lambdas/post-deploy/prune-revalidation-table.test.js +71 -0
  92. package/lib/lambdas/post-deploy/prune-s3.d.ts +14 -5
  93. package/lib/lambdas/post-deploy/prune-s3.js +41 -12
  94. package/lib/lambdas/post-deploy/prune-s3.test.js +97 -18
  95. package/lib/lambdas/redeploy-stage/redeploy-stage-function.d.ts +13 -0
  96. package/lib/lambdas/redeploy-stage/redeploy-stage-function.js +55 -0
  97. package/lib/lambdas/redeploy-stage/redeploy-stage.d.ts +24 -0
  98. package/lib/lambdas/redeploy-stage/redeploy-stage.js +68 -0
  99. package/lib/lambdas/redeploy-stage/redeploy-stage.lambda.d.ts +20 -0
  100. package/lib/lambdas/redeploy-stage/redeploy-stage.lambda.js +24 -0
  101. package/lib/lambdas/redeploy-stage/redeploy-stage.test.d.ts +1 -0
  102. package/lib/lambdas/redeploy-stage/redeploy-stage.test.js +93 -0
  103. package/lib/nextjs-api.d.ts +102 -18
  104. package/lib/nextjs-api.js +338 -38
  105. package/lib/nextjs-api.test.js +537 -1
  106. package/lib/nextjs-build/global-containers.Dockerfile +15 -10
  107. package/lib/nextjs-build/nextjs-build.d.ts +339 -63
  108. package/lib/nextjs-build/nextjs-build.js +684 -327
  109. package/lib/nextjs-build/nextjs-build.test.d.ts +1 -0
  110. package/lib/nextjs-build/nextjs-build.test.js +412 -0
  111. package/lib/nextjs-build/patch-fetch.js +1 -1
  112. package/lib/nextjs-build/patch-fetch.test.js +166 -11
  113. package/lib/nextjs-build/regional-containers.Dockerfile +17 -10
  114. package/lib/nextjs-cache.d.ts +82 -4
  115. package/lib/nextjs-cache.js +186 -10
  116. package/lib/nextjs-cache.test.js +176 -1
  117. package/lib/nextjs-compute/nextjs-compute-base-props.d.ts +21 -8
  118. package/lib/nextjs-compute/nextjs-compute-base-props.js +41 -1
  119. package/lib/nextjs-compute/nextjs-compute-base-props.test.d.ts +1 -0
  120. package/lib/nextjs-compute/nextjs-compute-base-props.test.js +44 -0
  121. package/lib/nextjs-compute/nextjs-containers.d.ts +19 -0
  122. package/lib/nextjs-compute/nextjs-containers.js +57 -20
  123. package/lib/nextjs-compute/nextjs-functions.d.ts +122 -7
  124. package/lib/nextjs-compute/nextjs-functions.js +113 -67
  125. package/lib/nextjs-compute/nextjs-functions.test.d.ts +1 -0
  126. package/lib/nextjs-compute/nextjs-functions.test.js +99 -0
  127. package/lib/nextjs-distribution.d.ts +194 -23
  128. package/lib/nextjs-distribution.js +535 -62
  129. package/lib/nextjs-distribution.test.d.ts +1 -0
  130. package/lib/nextjs-distribution.test.js +697 -0
  131. package/lib/nextjs-post-deploy.d.ts +13 -5
  132. package/lib/nextjs-post-deploy.js +13 -10
  133. package/lib/nextjs-post-deploy.test.d.ts +1 -0
  134. package/lib/nextjs-post-deploy.test.js +34 -0
  135. package/lib/nextjs-static-assets.d.ts +17 -4
  136. package/lib/nextjs-static-assets.js +22 -5
  137. package/lib/root-constructs/nextjs-base-construct.d.ts +102 -58
  138. package/lib/root-constructs/nextjs-base-construct.js +139 -47
  139. package/lib/root-constructs/nextjs-global-containers.d.ts +18 -20
  140. package/lib/root-constructs/nextjs-global-containers.js +20 -71
  141. package/lib/root-constructs/nextjs-global-functions.d.ts +26 -28
  142. package/lib/root-constructs/nextjs-global-functions.js +52 -70
  143. package/lib/root-constructs/nextjs-regional-containers.d.ts +18 -5
  144. package/lib/root-constructs/nextjs-regional-containers.js +8 -15
  145. package/lib/root-constructs/nextjs-regional-functions.d.ts +22 -18
  146. package/lib/root-constructs/nextjs-regional-functions.js +8 -26
  147. package/lib/runtime/api-gateway-path.d.ts +34 -0
  148. package/lib/runtime/api-gateway-path.js +71 -0
  149. package/lib/runtime/api-gateway-path.test.d.ts +1 -0
  150. package/lib/runtime/api-gateway-path.test.js +65 -0
  151. package/lib/runtime/case-redirect.d.ts +22 -0
  152. package/lib/runtime/case-redirect.js +63 -0
  153. package/lib/runtime/case-redirect.test.d.ts +1 -0
  154. package/lib/runtime/case-redirect.test.js +32 -0
  155. package/lib/runtime/core.d.ts +234 -0
  156. package/lib/runtime/core.js +1213 -0
  157. package/lib/runtime/core.test.d.ts +1 -0
  158. package/lib/runtime/core.test.js +1678 -0
  159. package/lib/runtime/deployment-root.d.ts +13 -0
  160. package/lib/runtime/deployment-root.js +29 -0
  161. package/lib/runtime/dispatch.d.ts +331 -0
  162. package/lib/runtime/dispatch.js +751 -0
  163. package/lib/runtime/dispatch.test.d.ts +1 -0
  164. package/lib/runtime/dispatch.test.js +853 -0
  165. package/lib/runtime/entrypoints.d.ts +57 -0
  166. package/lib/runtime/entrypoints.js +115 -0
  167. package/lib/runtime/http/abort-signal.d.ts +6 -0
  168. package/lib/runtime/http/abort-signal.js +19 -0
  169. package/lib/runtime/http/abort-signal.test.d.ts +1 -0
  170. package/lib/runtime/http/abort-signal.test.js +49 -0
  171. package/lib/runtime/http/node-server.d.ts +38 -0
  172. package/lib/runtime/http/node-server.js +99 -0
  173. package/lib/runtime/http/node-server.test.d.ts +1 -0
  174. package/lib/runtime/http/node-server.test.js +96 -0
  175. package/lib/runtime/http/node-sink.d.ts +16 -0
  176. package/lib/runtime/http/node-sink.js +48 -0
  177. package/lib/runtime/http/node-sink.test.d.ts +1 -0
  178. package/lib/runtime/http/node-sink.test.js +66 -0
  179. package/lib/runtime/http/request.d.ts +62 -0
  180. package/lib/runtime/http/request.js +134 -0
  181. package/lib/runtime/http/request.test.d.ts +1 -0
  182. package/lib/runtime/http/request.test.js +103 -0
  183. package/lib/runtime/http/response.d.ts +77 -0
  184. package/lib/runtime/http/response.js +229 -0
  185. package/lib/runtime/http/response.test.d.ts +1 -0
  186. package/lib/runtime/http/response.test.js +178 -0
  187. package/lib/runtime/http/sink.d.ts +73 -0
  188. package/lib/runtime/http/sink.js +188 -0
  189. package/lib/runtime/http/sink.test.d.ts +1 -0
  190. package/lib/runtime/http/sink.test.js +257 -0
  191. package/lib/runtime/image-utils.d.ts +78 -0
  192. package/lib/runtime/image-utils.js +129 -0
  193. package/lib/runtime/image-utils.test.d.ts +1 -0
  194. package/lib/runtime/image-utils.test.js +237 -0
  195. package/lib/runtime/image.d.ts +76 -0
  196. package/lib/runtime/image.js +296 -0
  197. package/lib/runtime/image.test.d.ts +1 -0
  198. package/lib/runtime/image.test.js +405 -0
  199. package/lib/runtime/lambda-event.d.ts +4 -0
  200. package/lib/runtime/lambda-event.js +113 -0
  201. package/lib/runtime/lambda-event.test.d.ts +1 -0
  202. package/lib/runtime/lambda-event.test.js +119 -0
  203. package/lib/runtime/lambda.mjs +41595 -0
  204. package/lib/runtime/load-module.d.ts +8 -0
  205. package/lib/runtime/load-module.js +35 -0
  206. package/lib/runtime/manifest.d.ts +215 -0
  207. package/lib/runtime/manifest.js +118 -0
  208. package/lib/runtime/middleware.d.ts +43 -0
  209. package/lib/runtime/middleware.js +113 -0
  210. package/lib/runtime/middleware.test.d.ts +1 -0
  211. package/lib/runtime/middleware.test.js +259 -0
  212. package/lib/runtime/next-modules.d.ts +55 -0
  213. package/lib/runtime/next-modules.js +124 -0
  214. package/lib/runtime/next-modules.test.d.ts +1 -0
  215. package/lib/runtime/next-modules.test.js +34 -0
  216. package/lib/runtime/public-files.d.ts +32 -0
  217. package/lib/runtime/public-files.js +117 -0
  218. package/lib/runtime/public-files.test.d.ts +1 -0
  219. package/lib/runtime/public-files.test.js +97 -0
  220. package/lib/runtime/server.mjs +41588 -0
  221. package/lib/runtime/static-files.d.ts +50 -0
  222. package/lib/runtime/static-files.js +266 -0
  223. package/lib/runtime/static-files.test.d.ts +1 -0
  224. package/lib/runtime/static-files.test.js +118 -0
  225. package/lib/runtime/util.d.ts +28 -0
  226. package/lib/runtime/util.js +79 -0
  227. package/lib/utils/base-path.d.ts +106 -13
  228. package/lib/utils/base-path.js +254 -41
  229. package/lib/utils/base-path.test.js +219 -51
  230. package/lib/utils/get-architecture.d.ts +7 -8
  231. package/lib/utils/get-architecture.js +12 -13
  232. package/lib/utils/zip-directory.d.ts +14 -0
  233. package/lib/utils/zip-directory.js +121 -0
  234. package/lib/utils/zip-directory.test.d.ts +1 -0
  235. package/lib/utils/zip-directory.test.js +99 -0
  236. package/package.json +22 -3
  237. package/scripts/capture-adapter-fixture.mjs +325 -0
  238. package/scripts/e2e-deploy.sh +398 -0
  239. package/scripts/e2e-harness/README.md +707 -0
  240. package/scripts/e2e-harness/app.js +370 -0
  241. package/scripts/e2e-harness/common.sh +249 -0
  242. package/scripts/e2e-harness/screen.mjs +302 -0
  243. package/scripts/e2e-harness/stage-proxy.mjs +145 -0
  244. package/scripts/e2e-harness/type-filters.mjs +47 -0
  245. package/scripts/e2e-logs.sh +75 -0
  246. package/scripts/e2e-offline.sh +91 -0
  247. package/scripts/e2e-sweep.sh +370 -0
  248. package/scripts/e2e-warm.sh +114 -0
  249. package/scripts/zero-config-build.mjs +418 -0
  250. package/docs/1k-concurrent-users-executions.png +0 -0
  251. package/docs/plans/adapter-runtime-release.md +0 -362
  252. package/docs/plans/phase-0-image-function-opt-in.md +0 -295
  253. package/lib/generated-structs/OptionalDockerImageFunctionProps.d.ts +0 -425
  254. package/lib/generated-structs/OptionalDockerImageFunctionProps.js +0 -3
  255. package/lib/image-optimization/handler-utils.d.ts +0 -34
  256. package/lib/image-optimization/handler-utils.js +0 -83
  257. package/lib/image-optimization/handler-utils.test.js +0 -178
  258. package/lib/nextjs-build/functions.Dockerfile +0 -31
  259. package/lib/nextjs-compute/nextjs-image-function.d.ts +0 -53
  260. package/lib/nextjs-compute/nextjs-image-function.js +0 -59
  261. package/lib/utils/experimental-flags.d.ts +0 -24
  262. package/lib/utils/experimental-flags.js +0 -30
  263. /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=