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.
- package/.jsii +2404 -2723
- package/.jsii.tabl.json +1 -1
- package/API.md +1681 -1930
- package/CLAUDE.md +2 -1
- package/README.md +429 -130
- package/assets/lambdas/post-deploy/post-deploy.lambda/index.js +148 -169
- package/assets/lambdas/redeploy-stage/redeploy-stage.lambda/index.js +875 -0
- package/docs/adapter-runtime.md +185 -0
- package/docs/breaking-changes.md +667 -4
- package/docs/caching-guide.md +156 -112
- package/docs/cdk-nextjs-cfn-exec-policy.json +2 -1
- package/docs/development-guide.md +1 -1
- package/docs/harness-coverage.md +598 -0
- package/docs/init-cache-deployment.md +32 -1
- package/docs/next-build-output-guide.md +30 -20
- package/examples/README.md +5 -1
- package/lib/adapter/__fixtures__/app-playground-base-path.json +4926 -0
- package/lib/adapter/__fixtures__/app-playground.json +4917 -0
- package/lib/adapter/__fixtures__/pages-i18n.json +1728 -0
- package/lib/adapter/adapter.mjs +1548 -185
- package/lib/adapter/adapter.test.js +106 -0
- package/lib/adapter/aws-cache-store.d.ts +489 -0
- package/lib/adapter/aws-cache-store.js +1004 -0
- package/lib/adapter/aws-cache-store.test.d.ts +1 -0
- package/lib/adapter/aws-cache-store.test.js +361 -0
- package/lib/adapter/build-outputs.d.ts +80 -0
- package/lib/adapter/build-outputs.js +852 -0
- package/lib/adapter/build-outputs.test.d.ts +1 -0
- package/lib/adapter/build-outputs.test.js +861 -0
- package/lib/adapter/cache-handler.d.ts +9 -2
- package/lib/adapter/cache-handler.js +72 -96
- package/lib/adapter/cache-handler.mjs +18956 -17774
- package/lib/adapter/cache-handler.test.js +263 -31
- package/lib/adapter/cache-kinds.d.ts +32 -0
- package/lib/adapter/cache-kinds.js +35 -0
- package/lib/adapter/cache-kinds.test.d.ts +1 -0
- package/lib/adapter/cache-kinds.test.js +69 -0
- package/lib/adapter/cache-utils.d.ts +90 -0
- package/lib/adapter/cache-utils.js +199 -4
- package/lib/adapter/cache-utils.test.d.ts +1 -0
- package/lib/adapter/cache-utils.test.js +179 -0
- package/lib/adapter/function-groups.d.ts +215 -0
- package/lib/adapter/function-groups.js +1099 -0
- package/lib/adapter/function-groups.test.d.ts +1 -0
- package/lib/adapter/function-groups.test.js +743 -0
- package/lib/adapter/init-cache.d.ts +6 -0
- package/lib/adapter/init-cache.js +245 -0
- package/lib/adapter/init-cache.test.d.ts +1 -0
- package/lib/adapter/init-cache.test.js +137 -0
- package/lib/adapter/local-file-cache-handler.d.ts +23 -0
- package/lib/adapter/local-file-cache-handler.js +52 -1
- package/lib/adapter/memory-cache-handler.d.ts +26 -17
- package/lib/adapter/memory-cache-handler.js +32 -50
- package/lib/adapter/memory-cache-handler.test.js +105 -85
- package/lib/adapter/s3-cache-handler.d.ts +179 -40
- package/lib/adapter/s3-cache-handler.js +807 -277
- package/lib/adapter/s3-cache-handler.test.js +1186 -31
- package/lib/adapter/use-cache-common.d.ts +90 -0
- package/lib/adapter/use-cache-common.js +250 -0
- package/lib/adapter/use-cache-common.test.d.ts +1 -0
- package/lib/adapter/use-cache-common.test.js +646 -0
- package/lib/adapter/use-cache-default-handler.d.ts +24 -0
- package/lib/adapter/use-cache-default-handler.js +90 -0
- package/lib/adapter/use-cache-default-handler.mjs +38645 -0
- package/lib/adapter/use-cache-handlers.test.d.ts +1 -0
- package/lib/adapter/use-cache-handlers.test.js +349 -0
- package/lib/adapter/use-cache-remote-handler.d.ts +26 -0
- package/lib/adapter/use-cache-remote-handler.js +186 -0
- package/lib/{image-optimization/handler.mjs → adapter/use-cache-remote-handler.mjs} +33572 -38798
- package/lib/generated-structs/OptionalNextjsApiProps.d.ts +59 -0
- package/lib/generated-structs/OptionalNextjsApiProps.js +3 -0
- package/lib/generated-structs/OptionalNextjsCacheProps.d.ts +15 -6
- package/lib/generated-structs/OptionalNextjsCacheProps.js +1 -1
- package/lib/generated-structs/OptionalNextjsContainersProps.d.ts +25 -11
- package/lib/generated-structs/OptionalNextjsContainersProps.js +1 -1
- package/lib/generated-structs/OptionalNextjsDistributionProps.d.ts +31 -9
- package/lib/generated-structs/OptionalNextjsDistributionProps.js +1 -1
- package/lib/generated-structs/OptionalNextjsPostDeployProps.d.ts +8 -0
- package/lib/generated-structs/OptionalNextjsPostDeployProps.js +1 -1
- package/lib/generated-structs/OptionalPostDeployCustomResourceProperties.d.ts +6 -4
- package/lib/generated-structs/OptionalPostDeployCustomResourceProperties.js +1 -1
- package/lib/index.d.ts +6 -7
- package/lib/index.js +2 -4
- package/lib/lambdas/post-deploy/prune-cache-bucket.d.ts +7 -3
- package/lib/lambdas/post-deploy/prune-cache-bucket.js +57 -71
- package/lib/lambdas/post-deploy/prune-cache-bucket.test.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-cache-bucket.test.js +114 -0
- package/lib/lambdas/post-deploy/prune-revalidation-table.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-revalidation-table.js +23 -31
- package/lib/lambdas/post-deploy/prune-revalidation-table.test.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-revalidation-table.test.js +71 -0
- package/lib/lambdas/post-deploy/prune-s3.d.ts +14 -5
- package/lib/lambdas/post-deploy/prune-s3.js +41 -12
- package/lib/lambdas/post-deploy/prune-s3.test.js +97 -18
- package/lib/lambdas/redeploy-stage/redeploy-stage-function.d.ts +13 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage-function.js +55 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.d.ts +24 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.js +68 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.lambda.d.ts +20 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.lambda.js +24 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.test.d.ts +1 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.test.js +93 -0
- package/lib/nextjs-api.d.ts +102 -18
- package/lib/nextjs-api.js +338 -38
- package/lib/nextjs-api.test.js +537 -1
- package/lib/nextjs-build/global-containers.Dockerfile +15 -10
- package/lib/nextjs-build/nextjs-build.d.ts +339 -63
- package/lib/nextjs-build/nextjs-build.js +684 -327
- package/lib/nextjs-build/nextjs-build.test.d.ts +1 -0
- package/lib/nextjs-build/nextjs-build.test.js +412 -0
- package/lib/nextjs-build/patch-fetch.js +1 -1
- package/lib/nextjs-build/patch-fetch.test.js +166 -11
- package/lib/nextjs-build/regional-containers.Dockerfile +17 -10
- package/lib/nextjs-cache.d.ts +82 -4
- package/lib/nextjs-cache.js +186 -10
- package/lib/nextjs-cache.test.js +176 -1
- package/lib/nextjs-compute/nextjs-compute-base-props.d.ts +21 -8
- package/lib/nextjs-compute/nextjs-compute-base-props.js +41 -1
- package/lib/nextjs-compute/nextjs-compute-base-props.test.d.ts +1 -0
- package/lib/nextjs-compute/nextjs-compute-base-props.test.js +44 -0
- package/lib/nextjs-compute/nextjs-containers.d.ts +19 -0
- package/lib/nextjs-compute/nextjs-containers.js +57 -20
- package/lib/nextjs-compute/nextjs-functions.d.ts +122 -7
- package/lib/nextjs-compute/nextjs-functions.js +113 -67
- package/lib/nextjs-compute/nextjs-functions.test.d.ts +1 -0
- package/lib/nextjs-compute/nextjs-functions.test.js +99 -0
- package/lib/nextjs-distribution.d.ts +194 -23
- package/lib/nextjs-distribution.js +535 -62
- package/lib/nextjs-distribution.test.d.ts +1 -0
- package/lib/nextjs-distribution.test.js +697 -0
- package/lib/nextjs-post-deploy.d.ts +13 -5
- package/lib/nextjs-post-deploy.js +13 -10
- package/lib/nextjs-post-deploy.test.d.ts +1 -0
- package/lib/nextjs-post-deploy.test.js +34 -0
- package/lib/nextjs-static-assets.d.ts +17 -4
- package/lib/nextjs-static-assets.js +22 -5
- package/lib/root-constructs/nextjs-base-construct.d.ts +102 -58
- package/lib/root-constructs/nextjs-base-construct.js +139 -47
- package/lib/root-constructs/nextjs-global-containers.d.ts +18 -20
- package/lib/root-constructs/nextjs-global-containers.js +20 -71
- package/lib/root-constructs/nextjs-global-functions.d.ts +26 -28
- package/lib/root-constructs/nextjs-global-functions.js +52 -70
- package/lib/root-constructs/nextjs-regional-containers.d.ts +18 -5
- package/lib/root-constructs/nextjs-regional-containers.js +8 -15
- package/lib/root-constructs/nextjs-regional-functions.d.ts +22 -18
- package/lib/root-constructs/nextjs-regional-functions.js +8 -26
- package/lib/runtime/api-gateway-path.d.ts +34 -0
- package/lib/runtime/api-gateway-path.js +71 -0
- package/lib/runtime/api-gateway-path.test.d.ts +1 -0
- package/lib/runtime/api-gateway-path.test.js +65 -0
- package/lib/runtime/case-redirect.d.ts +22 -0
- package/lib/runtime/case-redirect.js +63 -0
- package/lib/runtime/case-redirect.test.d.ts +1 -0
- package/lib/runtime/case-redirect.test.js +32 -0
- package/lib/runtime/core.d.ts +234 -0
- package/lib/runtime/core.js +1213 -0
- package/lib/runtime/core.test.d.ts +1 -0
- package/lib/runtime/core.test.js +1678 -0
- package/lib/runtime/deployment-root.d.ts +13 -0
- package/lib/runtime/deployment-root.js +29 -0
- package/lib/runtime/dispatch.d.ts +331 -0
- package/lib/runtime/dispatch.js +751 -0
- package/lib/runtime/dispatch.test.d.ts +1 -0
- package/lib/runtime/dispatch.test.js +853 -0
- package/lib/runtime/entrypoints.d.ts +57 -0
- package/lib/runtime/entrypoints.js +115 -0
- package/lib/runtime/http/abort-signal.d.ts +6 -0
- package/lib/runtime/http/abort-signal.js +19 -0
- package/lib/runtime/http/abort-signal.test.d.ts +1 -0
- package/lib/runtime/http/abort-signal.test.js +49 -0
- package/lib/runtime/http/node-server.d.ts +38 -0
- package/lib/runtime/http/node-server.js +99 -0
- package/lib/runtime/http/node-server.test.d.ts +1 -0
- package/lib/runtime/http/node-server.test.js +96 -0
- package/lib/runtime/http/node-sink.d.ts +16 -0
- package/lib/runtime/http/node-sink.js +48 -0
- package/lib/runtime/http/node-sink.test.d.ts +1 -0
- package/lib/runtime/http/node-sink.test.js +66 -0
- package/lib/runtime/http/request.d.ts +62 -0
- package/lib/runtime/http/request.js +134 -0
- package/lib/runtime/http/request.test.d.ts +1 -0
- package/lib/runtime/http/request.test.js +103 -0
- package/lib/runtime/http/response.d.ts +77 -0
- package/lib/runtime/http/response.js +229 -0
- package/lib/runtime/http/response.test.d.ts +1 -0
- package/lib/runtime/http/response.test.js +178 -0
- package/lib/runtime/http/sink.d.ts +73 -0
- package/lib/runtime/http/sink.js +188 -0
- package/lib/runtime/http/sink.test.d.ts +1 -0
- package/lib/runtime/http/sink.test.js +257 -0
- package/lib/runtime/image-utils.d.ts +78 -0
- package/lib/runtime/image-utils.js +129 -0
- package/lib/runtime/image-utils.test.d.ts +1 -0
- package/lib/runtime/image-utils.test.js +237 -0
- package/lib/runtime/image.d.ts +76 -0
- package/lib/runtime/image.js +296 -0
- package/lib/runtime/image.test.d.ts +1 -0
- package/lib/runtime/image.test.js +405 -0
- package/lib/runtime/lambda-event.d.ts +4 -0
- package/lib/runtime/lambda-event.js +113 -0
- package/lib/runtime/lambda-event.test.d.ts +1 -0
- package/lib/runtime/lambda-event.test.js +119 -0
- package/lib/runtime/lambda.mjs +41595 -0
- package/lib/runtime/load-module.d.ts +8 -0
- package/lib/runtime/load-module.js +35 -0
- package/lib/runtime/manifest.d.ts +215 -0
- package/lib/runtime/manifest.js +118 -0
- package/lib/runtime/middleware.d.ts +43 -0
- package/lib/runtime/middleware.js +113 -0
- package/lib/runtime/middleware.test.d.ts +1 -0
- package/lib/runtime/middleware.test.js +259 -0
- package/lib/runtime/next-modules.d.ts +55 -0
- package/lib/runtime/next-modules.js +124 -0
- package/lib/runtime/next-modules.test.d.ts +1 -0
- package/lib/runtime/next-modules.test.js +34 -0
- package/lib/runtime/public-files.d.ts +32 -0
- package/lib/runtime/public-files.js +117 -0
- package/lib/runtime/public-files.test.d.ts +1 -0
- package/lib/runtime/public-files.test.js +97 -0
- package/lib/runtime/server.mjs +41588 -0
- package/lib/runtime/static-files.d.ts +50 -0
- package/lib/runtime/static-files.js +266 -0
- package/lib/runtime/static-files.test.d.ts +1 -0
- package/lib/runtime/static-files.test.js +118 -0
- package/lib/runtime/util.d.ts +28 -0
- package/lib/runtime/util.js +79 -0
- package/lib/utils/base-path.d.ts +106 -13
- package/lib/utils/base-path.js +254 -41
- package/lib/utils/base-path.test.js +219 -51
- package/lib/utils/get-architecture.d.ts +7 -8
- package/lib/utils/get-architecture.js +12 -13
- package/lib/utils/zip-directory.d.ts +14 -0
- package/lib/utils/zip-directory.js +121 -0
- package/lib/utils/zip-directory.test.d.ts +1 -0
- package/lib/utils/zip-directory.test.js +99 -0
- package/package.json +22 -3
- package/scripts/capture-adapter-fixture.mjs +325 -0
- package/scripts/e2e-deploy.sh +398 -0
- package/scripts/e2e-harness/README.md +707 -0
- package/scripts/e2e-harness/app.js +370 -0
- package/scripts/e2e-harness/common.sh +249 -0
- package/scripts/e2e-harness/screen.mjs +302 -0
- package/scripts/e2e-harness/stage-proxy.mjs +145 -0
- package/scripts/e2e-harness/type-filters.mjs +47 -0
- package/scripts/e2e-logs.sh +75 -0
- package/scripts/e2e-offline.sh +91 -0
- package/scripts/e2e-sweep.sh +370 -0
- package/scripts/e2e-warm.sh +114 -0
- package/scripts/zero-config-build.mjs +418 -0
- package/docs/1k-concurrent-users-executions.png +0 -0
- package/docs/plans/adapter-runtime-release.md +0 -362
- package/docs/plans/phase-0-image-function-opt-in.md +0 -295
- package/lib/generated-structs/OptionalDockerImageFunctionProps.d.ts +0 -425
- package/lib/generated-structs/OptionalDockerImageFunctionProps.js +0 -3
- package/lib/image-optimization/handler-utils.d.ts +0 -34
- package/lib/image-optimization/handler-utils.js +0 -83
- package/lib/image-optimization/handler-utils.test.js +0 -178
- package/lib/nextjs-build/functions.Dockerfile +0 -31
- package/lib/nextjs-compute/nextjs-image-function.d.ts +0 -53
- package/lib/nextjs-compute/nextjs-image-function.js +0 -59
- package/lib/utils/experimental-flags.d.ts +0 -24
- package/lib/utils/experimental-flags.js +0 -30
- /package/lib/{image-optimization/handler-utils.test.d.ts → adapter/adapter.test.d.ts} +0 -0
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
/* eslint-disable import/no-extraneous-dependencies */
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_os_1 = require("node:os");
|
|
6
|
+
const node_path_1 = require("node:path");
|
|
7
|
+
// What `require.cache` holds in a running server. Jest's `createRequire` hands
|
|
8
|
+
// out a cache of its own that nothing is ever loaded into, so the handler's
|
|
9
|
+
// lookup of Next.js's tag manifest reads this stand-in instead.
|
|
10
|
+
const mockModuleCache = {};
|
|
11
|
+
jest.mock("node:module", () => ({
|
|
12
|
+
...jest.requireActual("node:module"),
|
|
13
|
+
createRequire: () => ({ cache: mockModuleCache }),
|
|
14
|
+
}));
|
|
4
15
|
// Mock AWS SDK
|
|
5
16
|
jest.mock("@aws-sdk/client-s3");
|
|
6
17
|
jest.mock("@aws-sdk/client-dynamodb");
|
|
@@ -11,6 +22,8 @@ const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
|
11
22
|
const client_s3_1 = require("@aws-sdk/client-s3");
|
|
12
23
|
const client_ssm_1 = require("@aws-sdk/client-ssm");
|
|
13
24
|
const s3_cache_handler_1 = require("./s3-cache-handler");
|
|
25
|
+
const use_cache_common_1 = require("./use-cache-common");
|
|
26
|
+
const manifest_1 = require("../runtime/manifest");
|
|
14
27
|
const mockS3Send = jest.fn();
|
|
15
28
|
const mockDynamoSend = jest.fn();
|
|
16
29
|
const mockCloudFrontSend = jest.fn();
|
|
@@ -30,13 +43,60 @@ client_ssm_1.SSMClient.mockImplementation(() => ({
|
|
|
30
43
|
describe("S3DynamoCacheHandler", () => {
|
|
31
44
|
let handler;
|
|
32
45
|
let mockContext;
|
|
46
|
+
let distDir;
|
|
47
|
+
/** Stage `required-server-files.json` for an app with `basePath: "/base"`. */
|
|
48
|
+
const useBasePath = () => {
|
|
49
|
+
distDir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "s3-cache-handler-"));
|
|
50
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(distDir, "required-server-files.json"), JSON.stringify({ config: { basePath: "/base" } }));
|
|
51
|
+
mockContext.serverDistDir = (0, node_path_1.join)(distDir, "server");
|
|
52
|
+
};
|
|
33
53
|
// Helper to create set context with tags
|
|
34
54
|
const createSetContext = (tags) => ({
|
|
35
55
|
fetchCache: true,
|
|
36
56
|
tags,
|
|
37
57
|
});
|
|
58
|
+
/**
|
|
59
|
+
* The input a mocked command was constructed with. The SDK's command classes
|
|
60
|
+
* are automocked, so an instance carries nothing of its own.
|
|
61
|
+
*/
|
|
62
|
+
const commandInput = (command, type) => {
|
|
63
|
+
const mock = type;
|
|
64
|
+
return mock.mock.calls[mock.mock.instances.indexOf(command)][0];
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Answer DynamoDB by command type rather than by call order: `revalidateTag`
|
|
68
|
+
* writes its tag marker before querying the tag's mapping rows, and which of
|
|
69
|
+
* those comes first is an implementation detail. `get` is the marker row every
|
|
70
|
+
* requested tag has, as a `GetItem` would have returned it.
|
|
71
|
+
*/
|
|
72
|
+
const dynamoResponses = (responses) => {
|
|
73
|
+
mockDynamoSend.mockImplementation((command) => {
|
|
74
|
+
if (command instanceof client_dynamodb_1.QueryCommand) {
|
|
75
|
+
return Promise.resolve(responses.query ?? {});
|
|
76
|
+
}
|
|
77
|
+
if (command instanceof client_dynamodb_1.BatchGetItemCommand) {
|
|
78
|
+
const { RequestItems } = commandInput(command, client_dynamodb_1.BatchGetItemCommand);
|
|
79
|
+
const [[table, { Keys }]] = Object.entries(RequestItems);
|
|
80
|
+
const item = responses.get?.Item;
|
|
81
|
+
return Promise.resolve({
|
|
82
|
+
Responses: {
|
|
83
|
+
[table]: item ? Keys.map(({ sk }) => ({ ...item, sk })) : [],
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return Promise.resolve({});
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Drop the process's tag manifest, so the next handler starts with one of
|
|
92
|
+
* its own, as a new instance does.
|
|
93
|
+
*/
|
|
94
|
+
const resetTagManifest = () => {
|
|
95
|
+
delete globalThis[Symbol.for("cdk-nextjs.use-cache.tag-manifest")];
|
|
96
|
+
};
|
|
38
97
|
beforeEach(() => {
|
|
39
98
|
mockContext = { dev: false };
|
|
99
|
+
resetTagManifest();
|
|
40
100
|
// Set up environment variables
|
|
41
101
|
process.env.CDK_NEXTJS_CACHE_BUCKET_NAME = "test-bucket";
|
|
42
102
|
process.env.CDK_NEXTJS_REVALIDATION_TABLE_NAME = "test-table";
|
|
@@ -49,6 +109,8 @@ describe("S3DynamoCacheHandler", () => {
|
|
|
49
109
|
});
|
|
50
110
|
// Reset mocks
|
|
51
111
|
mockS3Send.mockReset();
|
|
112
|
+
// An empty bucket: no object, such as no build tag manifest.
|
|
113
|
+
mockS3Send.mockResolvedValue({});
|
|
52
114
|
mockDynamoSend.mockReset();
|
|
53
115
|
mockCloudFrontSend.mockReset();
|
|
54
116
|
mockSsmSend.mockReset();
|
|
@@ -60,6 +122,11 @@ describe("S3DynamoCacheHandler", () => {
|
|
|
60
122
|
delete process.env.CDK_NEXTJS_BUILD_ID;
|
|
61
123
|
delete process.env.AWS_REGION;
|
|
62
124
|
delete process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME;
|
|
125
|
+
delete process.env.CDK_NEXTJS_DISTRIBUTION_ID;
|
|
126
|
+
if (distDir) {
|
|
127
|
+
(0, node_fs_1.rmSync)(distDir, { recursive: true, force: true });
|
|
128
|
+
distDir = undefined;
|
|
129
|
+
}
|
|
63
130
|
// Restore console.warn
|
|
64
131
|
jest.restoreAllMocks();
|
|
65
132
|
});
|
|
@@ -114,6 +181,346 @@ describe("S3DynamoCacheHandler", () => {
|
|
|
114
181
|
});
|
|
115
182
|
expect(result).toBeNull();
|
|
116
183
|
});
|
|
184
|
+
it("invalidates a seeded prerender only when its tag marker is newer", async () => {
|
|
185
|
+
// A build-time prerender is written as a file by the adapter's
|
|
186
|
+
// `onBuildComplete`, never through `set`, so it has no `tags` array and no
|
|
187
|
+
// DynamoDB mapping rows - its tags live in the render's
|
|
188
|
+
// `x-next-cache-tags` header. Two bugs met here: those header tags were
|
|
189
|
+
// ignored, so `revalidateTag` could not reach any static page; and the
|
|
190
|
+
// mapping rows `set` writes were stamped `revalidatedAt = now`, which is
|
|
191
|
+
// *after* the entry's own `lastModified`, so a tagged entry looked
|
|
192
|
+
// revalidated the moment it was stored and every request re-rendered.
|
|
193
|
+
// Measured against next.js's `test/e2e/app-dir/resume-data-cache`.
|
|
194
|
+
const lastModified = Date.now();
|
|
195
|
+
const seeded = {
|
|
196
|
+
lastModified,
|
|
197
|
+
value: {
|
|
198
|
+
kind: "APP_PAGE" /* CachedRouteKind.APP_PAGE */,
|
|
199
|
+
html: "<html>seeded</html>",
|
|
200
|
+
headers: { "x-next-cache-tags": "_N_T_/,_N_T_/page,test" },
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
const s3Body = () => ({
|
|
204
|
+
Body: {
|
|
205
|
+
transformToString: jest
|
|
206
|
+
.fn()
|
|
207
|
+
.mockResolvedValue(JSON.stringify(seeded)),
|
|
208
|
+
},
|
|
209
|
+
ContentType: "application/json",
|
|
210
|
+
});
|
|
211
|
+
mockS3Send.mockImplementation((command) => Promise.resolve(command instanceof client_s3_1.GetObjectCommand ? s3Body() : {}));
|
|
212
|
+
const getCtx = {
|
|
213
|
+
kind: "APP_PAGE" /* IncrementalCacheKind.APP_PAGE */,
|
|
214
|
+
isFallback: false,
|
|
215
|
+
};
|
|
216
|
+
// Each step below changes the markers DynamoDB holds; a new instance
|
|
217
|
+
// reads them afresh.
|
|
218
|
+
const freshHandler = () => {
|
|
219
|
+
resetTagManifest();
|
|
220
|
+
return new s3_cache_handler_1.S3CacheHandler({ context: mockContext });
|
|
221
|
+
};
|
|
222
|
+
// No marker at all: the tag was never revalidated.
|
|
223
|
+
dynamoResponses({});
|
|
224
|
+
expect(await freshHandler().get("index", getCtx)).toMatchObject({
|
|
225
|
+
lastModified,
|
|
226
|
+
});
|
|
227
|
+
// A marker older than the entry means the entry already reflects it.
|
|
228
|
+
dynamoResponses({
|
|
229
|
+
get: { Item: { revalidatedAt: { N: String(lastModified - 1000) } } },
|
|
230
|
+
});
|
|
231
|
+
expect(await freshHandler().get("index", getCtx)).toMatchObject({
|
|
232
|
+
lastModified,
|
|
233
|
+
});
|
|
234
|
+
expect(mockS3Send).not.toHaveBeenCalledWith(expect.any(client_s3_1.DeleteObjectCommand));
|
|
235
|
+
// A newer marker: the entry comes back marked expired, and stays in S3.
|
|
236
|
+
dynamoResponses({
|
|
237
|
+
get: { Item: { revalidatedAt: { N: String(lastModified + 1000) } } },
|
|
238
|
+
});
|
|
239
|
+
expect(await freshHandler().get("index", getCtx)).toMatchObject({
|
|
240
|
+
lastModified: -1,
|
|
241
|
+
value: seeded.value,
|
|
242
|
+
});
|
|
243
|
+
expect(mockS3Send).not.toHaveBeenCalledWith(expect.any(client_s3_1.DeleteObjectCommand));
|
|
244
|
+
});
|
|
245
|
+
it("reports a revalidated response entry as expired rather than as a miss", async () => {
|
|
246
|
+
// `lastModified: -1` is how a cache handler says "expired, re-render
|
|
247
|
+
// before answering": `IncrementalCache.get` turns it into `isStale: -1`,
|
|
248
|
+
// which `app-page-runtime` reads as an on-demand revalidation and answers
|
|
249
|
+
// with a blocking render of this route, then stores it. Returning `null`
|
|
250
|
+
// is not equivalent. A miss on a PPR route whose `prerender-manifest.json`
|
|
251
|
+
// entry has a `fallback` (`compute: "resuming"`) is answered from the
|
|
252
|
+
// route's fallback shell - `cache-control: private, no-store`, no
|
|
253
|
+
// `x-nextjs-cache` - and without `partialPrefetching` nothing ever
|
|
254
|
+
// upgrades that back into a concrete entry. So the first `revalidateTag`
|
|
255
|
+
// to reach a page left it uncacheable and dynamically resumed per request
|
|
256
|
+
// for the rest of the deployment's life, which is what the isr e2e's
|
|
257
|
+
// missing `x-nextjs-cache` header was.
|
|
258
|
+
const lastModified = Date.now();
|
|
259
|
+
const stored = {
|
|
260
|
+
lastModified,
|
|
261
|
+
tags: ["collection"],
|
|
262
|
+
value: {
|
|
263
|
+
kind: "APP_PAGE" /* CachedRouteKind.APP_PAGE */,
|
|
264
|
+
html: "<html>isr</html>",
|
|
265
|
+
headers: { "x-next-cache-tags": "collection" },
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
mockS3Send.mockImplementation((command) => Promise.resolve(command instanceof client_s3_1.GetObjectCommand
|
|
269
|
+
? {
|
|
270
|
+
Body: {
|
|
271
|
+
transformToString: jest
|
|
272
|
+
.fn()
|
|
273
|
+
.mockResolvedValue(JSON.stringify(stored)),
|
|
274
|
+
},
|
|
275
|
+
ContentType: "application/json",
|
|
276
|
+
}
|
|
277
|
+
: {}));
|
|
278
|
+
dynamoResponses({
|
|
279
|
+
get: { Item: { revalidatedAt: { N: String(lastModified + 1000) } } },
|
|
280
|
+
});
|
|
281
|
+
const result = await handler.get("isr/1", {
|
|
282
|
+
kind: "APP_PAGE" /* IncrementalCacheKind.APP_PAGE */,
|
|
283
|
+
isFallback: false,
|
|
284
|
+
});
|
|
285
|
+
expect(result).toEqual({ lastModified: -1, value: stored.value });
|
|
286
|
+
// The body is still there for the render that fails, and `set` overwrites
|
|
287
|
+
// it with a `lastModified` past the marker.
|
|
288
|
+
expect(mockS3Send).not.toHaveBeenCalledWith(expect.any(client_s3_1.DeleteObjectCommand));
|
|
289
|
+
});
|
|
290
|
+
it("reports a revalidated fetch entry as a miss so the request refetches", async () => {
|
|
291
|
+
// The opposite of a response entry: reusing a revalidated `fetch` body is
|
|
292
|
+
// serving stale data, so this reads as a miss, like Next.js's own
|
|
293
|
+
// `FileSystemCache` does for its `revalidatedTags`.
|
|
294
|
+
const lastModified = Date.now();
|
|
295
|
+
const stored = {
|
|
296
|
+
lastModified,
|
|
297
|
+
tags: ["collection"],
|
|
298
|
+
value: {
|
|
299
|
+
kind: "FETCH" /* CachedRouteKind.FETCH */,
|
|
300
|
+
data: { headers: {}, body: "stale", status: 200, url: "/api" },
|
|
301
|
+
revalidate: 10,
|
|
302
|
+
},
|
|
303
|
+
};
|
|
304
|
+
mockS3Send.mockImplementation((command) => Promise.resolve(command instanceof client_s3_1.GetObjectCommand
|
|
305
|
+
? {
|
|
306
|
+
Body: {
|
|
307
|
+
transformToString: jest
|
|
308
|
+
.fn()
|
|
309
|
+
.mockResolvedValue(JSON.stringify(stored)),
|
|
310
|
+
},
|
|
311
|
+
ContentType: "application/json",
|
|
312
|
+
}
|
|
313
|
+
: {}));
|
|
314
|
+
dynamoResponses({
|
|
315
|
+
get: { Item: { revalidatedAt: { N: String(lastModified + 1000) } } },
|
|
316
|
+
});
|
|
317
|
+
const result = await handler.get("fetch-key", {
|
|
318
|
+
kind: "FETCH" /* IncrementalCacheKind.FETCH */,
|
|
319
|
+
revalidate: 10,
|
|
320
|
+
fetchUrl: "https://example.test/api",
|
|
321
|
+
fetchIdx: 1,
|
|
322
|
+
tags: ["collection"],
|
|
323
|
+
softTags: [],
|
|
324
|
+
});
|
|
325
|
+
expect(result).toBeNull();
|
|
326
|
+
// Not deleted: the refetch this provokes overwrites it through `set`.
|
|
327
|
+
// Deleting it here raced that write, so a concurrent request's fresh
|
|
328
|
+
// entry could be the one removed.
|
|
329
|
+
expect(mockS3Send).not.toHaveBeenCalledWith(expect.any(client_s3_1.DeleteObjectCommand));
|
|
330
|
+
expect(client_dynamodb_1.DeleteItemCommand).not.toHaveBeenCalled();
|
|
331
|
+
});
|
|
332
|
+
it("checks a fetch entry against the request's implicit tags, not its own", async () => {
|
|
333
|
+
// An untagged `cache: "force-cache"` fetch is stored with no tags at all -
|
|
334
|
+
// the implicit `_N_T_/<path>` chain reaches the handler only as
|
|
335
|
+
// `ctx.softTags`, which is the source Next.js's own `FileSystemCache` reads
|
|
336
|
+
// for a `FETCH` get. Checking the stored tags instead left this entry with
|
|
337
|
+
// nothing to check and no `revalidatePath` could ever evict it.
|
|
338
|
+
const lastModified = Date.now();
|
|
339
|
+
const stored = {
|
|
340
|
+
lastModified,
|
|
341
|
+
tags: [],
|
|
342
|
+
value: {
|
|
343
|
+
kind: "FETCH" /* CachedRouteKind.FETCH */,
|
|
344
|
+
data: { headers: {}, body: "stale", status: 200, url: "/api" },
|
|
345
|
+
revalidate: 10,
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
mockS3Send.mockImplementation((command) => Promise.resolve(command instanceof client_s3_1.GetObjectCommand
|
|
349
|
+
? {
|
|
350
|
+
Body: {
|
|
351
|
+
transformToString: jest
|
|
352
|
+
.fn()
|
|
353
|
+
.mockResolvedValue(JSON.stringify(stored)),
|
|
354
|
+
},
|
|
355
|
+
ContentType: "application/json",
|
|
356
|
+
}
|
|
357
|
+
: {}));
|
|
358
|
+
dynamoResponses({
|
|
359
|
+
get: { Item: { revalidatedAt: { N: String(lastModified + 1000) } } },
|
|
360
|
+
});
|
|
361
|
+
const result = await handler.get("fetch-key", {
|
|
362
|
+
kind: "FETCH" /* IncrementalCacheKind.FETCH */,
|
|
363
|
+
revalidate: 10,
|
|
364
|
+
fetchUrl: "https://next-data-api-endpoint.test/api/random",
|
|
365
|
+
fetchIdx: 1,
|
|
366
|
+
tags: [],
|
|
367
|
+
softTags: ["_N_T_/layout", "_N_T_/dynamic"],
|
|
368
|
+
});
|
|
369
|
+
expect(result).toBeNull();
|
|
370
|
+
const [{ RequestItems }] = client_dynamodb_1.BatchGetItemCommand
|
|
371
|
+
.mock.calls[0];
|
|
372
|
+
expect(RequestItems["test-table"].Keys.map((key) => key.sk.S)).toEqual(["_N_T_/layout", "_N_T_/dynamic"]);
|
|
373
|
+
});
|
|
374
|
+
describe("a tag revalidated with a profile", () => {
|
|
375
|
+
// `revalidateTag("posts", "max")` is stale-while-revalidate: the entry is
|
|
376
|
+
// served once more while a background render replaces it, and expires
|
|
377
|
+
// outright only after the profile's `expire`. Treating it as `updateTag`
|
|
378
|
+
// made every such call a blocking re-render.
|
|
379
|
+
const lastModified = Date.now() - 1000;
|
|
380
|
+
const stored = {
|
|
381
|
+
lastModified,
|
|
382
|
+
value: {
|
|
383
|
+
kind: "APP_PAGE" /* CachedRouteKind.APP_PAGE */,
|
|
384
|
+
html: "<html>posts</html>",
|
|
385
|
+
headers: { "x-next-cache-tags": "posts" },
|
|
386
|
+
},
|
|
387
|
+
};
|
|
388
|
+
const getCtx = {
|
|
389
|
+
kind: "APP_PAGE" /* IncrementalCacheKind.APP_PAGE */,
|
|
390
|
+
isFallback: false,
|
|
391
|
+
};
|
|
392
|
+
const manifestPath = (0, node_path_1.join)("/deployment/node_modules/next/dist/server/lib/incremental-cache", "tags-manifest.external.js");
|
|
393
|
+
// The module the server loads, resolved for real, so a Next.js upgrade
|
|
394
|
+
// that moves or reshapes it fails here rather than silently turning
|
|
395
|
+
// stale-while-revalidate into blocking renders.
|
|
396
|
+
const realManifestPath = require.resolve("next/dist/server/lib/incremental-cache/tags-manifest.external.js");
|
|
397
|
+
beforeEach(() => {
|
|
398
|
+
mockS3Send.mockImplementation((command) => Promise.resolve(command instanceof client_s3_1.GetObjectCommand
|
|
399
|
+
? {
|
|
400
|
+
Body: {
|
|
401
|
+
transformToString: jest
|
|
402
|
+
.fn()
|
|
403
|
+
.mockResolvedValue(JSON.stringify(stored)),
|
|
404
|
+
},
|
|
405
|
+
ContentType: "application/json",
|
|
406
|
+
}
|
|
407
|
+
: {}));
|
|
408
|
+
});
|
|
409
|
+
afterEach(() => {
|
|
410
|
+
delete mockModuleCache[manifestPath];
|
|
411
|
+
delete mockModuleCache[realManifestPath];
|
|
412
|
+
});
|
|
413
|
+
it("serves the entry and marks the tag stale in Next.js's manifest", async () => {
|
|
414
|
+
const next = jest.requireActual(realManifestPath);
|
|
415
|
+
mockModuleCache[realManifestPath] = { exports: next };
|
|
416
|
+
const staleAt = lastModified + 500;
|
|
417
|
+
dynamoResponses({
|
|
418
|
+
get: {
|
|
419
|
+
Item: {
|
|
420
|
+
staleAt: { N: String(staleAt) },
|
|
421
|
+
expiredAt: { N: String(Date.now() + 60_000) },
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
});
|
|
425
|
+
expect(await handler.get("posts", getCtx)).toEqual({
|
|
426
|
+
lastModified,
|
|
427
|
+
value: stored.value,
|
|
428
|
+
});
|
|
429
|
+
// Where `IncrementalCache.get` reads it, to answer `isStale: true`.
|
|
430
|
+
expect(next.tagsManifest.get("posts")).toEqual({ stale: staleAt });
|
|
431
|
+
expect(next.areTagsStale(["posts"], lastModified)).toBe(true);
|
|
432
|
+
});
|
|
433
|
+
it("expires the entry when Next.js's manifest cannot be found", async () => {
|
|
434
|
+
// A stale mark nothing reads would serve the entry as fresh; a blocking
|
|
435
|
+
// render is the safe side of that.
|
|
436
|
+
dynamoResponses({
|
|
437
|
+
get: { Item: { staleAt: { N: String(lastModified + 500) } } },
|
|
438
|
+
});
|
|
439
|
+
expect(await handler.get("posts", getCtx)).toMatchObject({
|
|
440
|
+
lastModified: -1,
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
it("expires the entry once the profile's expire has passed", async () => {
|
|
444
|
+
dynamoResponses({
|
|
445
|
+
get: {
|
|
446
|
+
Item: {
|
|
447
|
+
staleAt: { N: String(lastModified + 100) },
|
|
448
|
+
expiredAt: { N: String(lastModified + 500) },
|
|
449
|
+
},
|
|
450
|
+
},
|
|
451
|
+
});
|
|
452
|
+
expect(await handler.get("posts", getCtx)).toMatchObject({
|
|
453
|
+
lastModified: -1,
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
describe("behind a distribution", () => {
|
|
457
|
+
// `revalidateTag`'s invalidation goes out before the stale entry is
|
|
458
|
+
// served, and the stale response carries the entry's original
|
|
459
|
+
// `s-maxage`: CloudFront caches it again. Only the regeneration's `set`
|
|
460
|
+
// knows when the fresh entry exists to be fetched instead.
|
|
461
|
+
let cdnHandler;
|
|
462
|
+
const regenerated = stored.value;
|
|
463
|
+
const invalidatedPaths = () => client_cloudfront_1.CreateInvalidationCommand.mock.calls.map(([input]) => input.InvalidationBatch.Paths.Items);
|
|
464
|
+
beforeEach(() => {
|
|
465
|
+
process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME = "test-param-name";
|
|
466
|
+
cdnHandler = new s3_cache_handler_1.S3CacheHandler({ context: mockContext });
|
|
467
|
+
mockModuleCache[manifestPath] = {
|
|
468
|
+
exports: { tagsManifest: new Map() },
|
|
469
|
+
};
|
|
470
|
+
dynamoResponses({
|
|
471
|
+
get: { Item: { staleAt: { N: String(lastModified + 500) } } },
|
|
472
|
+
});
|
|
473
|
+
mockSsmSend.mockResolvedValue({
|
|
474
|
+
Parameter: { Value: "test-distribution-id" },
|
|
475
|
+
});
|
|
476
|
+
mockCloudFrontSend.mockResolvedValue({});
|
|
477
|
+
});
|
|
478
|
+
it("invalidates the page again once its regeneration is stored", async () => {
|
|
479
|
+
await cdnHandler.get("posts", getCtx);
|
|
480
|
+
expect(invalidatedPaths()).toEqual([]);
|
|
481
|
+
await cdnHandler.set("posts", regenerated, {
|
|
482
|
+
cacheControl: { revalidate: false, expire: undefined },
|
|
483
|
+
isRoutePPREnabled: false,
|
|
484
|
+
isFallback: false,
|
|
485
|
+
});
|
|
486
|
+
expect(invalidatedPaths()).toEqual([["/posts*"]]);
|
|
487
|
+
// Once: a later write of the same page is an ordinary one.
|
|
488
|
+
await cdnHandler.set("posts", regenerated, {
|
|
489
|
+
cacheControl: { revalidate: false, expire: undefined },
|
|
490
|
+
isRoutePPREnabled: false,
|
|
491
|
+
isFallback: false,
|
|
492
|
+
});
|
|
493
|
+
expect(invalidatedPaths()).toHaveLength(1);
|
|
494
|
+
});
|
|
495
|
+
it("remembers a stale memory hit by the key it was read under", async () => {
|
|
496
|
+
await cdnHandler.isRevalidated(stored, getCtx, "posts");
|
|
497
|
+
await cdnHandler.set("posts", regenerated, {
|
|
498
|
+
isRoutePPREnabled: false,
|
|
499
|
+
isFallback: false,
|
|
500
|
+
});
|
|
501
|
+
expect(invalidatedPaths()).toEqual([["/posts*"]]);
|
|
502
|
+
});
|
|
503
|
+
it("does not invalidate after an ordinary write", async () => {
|
|
504
|
+
await cdnHandler.set("posts", regenerated, {
|
|
505
|
+
isRoutePPREnabled: false,
|
|
506
|
+
isFallback: false,
|
|
507
|
+
});
|
|
508
|
+
expect(invalidatedPaths()).toEqual([]);
|
|
509
|
+
});
|
|
510
|
+
it("forgets a stale page that is deleted instead of regenerated", async () => {
|
|
511
|
+
await cdnHandler.get("posts", getCtx);
|
|
512
|
+
await cdnHandler.set("posts", null, {
|
|
513
|
+
isRoutePPREnabled: false,
|
|
514
|
+
isFallback: false,
|
|
515
|
+
});
|
|
516
|
+
await cdnHandler.set("posts", regenerated, {
|
|
517
|
+
isRoutePPREnabled: false,
|
|
518
|
+
isFallback: false,
|
|
519
|
+
});
|
|
520
|
+
expect(invalidatedPaths()).toEqual([]);
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
});
|
|
117
524
|
it("should handle S3 errors and return null", async () => {
|
|
118
525
|
mockS3Send.mockRejectedValueOnce(new Error("S3 Error"));
|
|
119
526
|
const result = await handler.get("error-key", {
|
|
@@ -123,7 +530,109 @@ describe("S3DynamoCacheHandler", () => {
|
|
|
123
530
|
expect(result).toBeNull();
|
|
124
531
|
});
|
|
125
532
|
});
|
|
533
|
+
describe("tag marker cache", () => {
|
|
534
|
+
// Every cache hit checks its tags' markers, and all of a deployment's
|
|
535
|
+
// markers share one partition key: read per request, they capped a route's
|
|
536
|
+
// cache hits at ~1,900 req/s. Each instance holds them for a second.
|
|
537
|
+
const lastModified = Date.now() - 60_000;
|
|
538
|
+
const stored = {
|
|
539
|
+
lastModified,
|
|
540
|
+
value: {
|
|
541
|
+
kind: "APP_PAGE" /* CachedRouteKind.APP_PAGE */,
|
|
542
|
+
html: "<html>cached</html>",
|
|
543
|
+
headers: { "x-next-cache-tags": "_N_T_/page,posts" },
|
|
544
|
+
},
|
|
545
|
+
};
|
|
546
|
+
const getCtx = {
|
|
547
|
+
kind: "APP_PAGE" /* IncrementalCacheKind.APP_PAGE */,
|
|
548
|
+
isFallback: false,
|
|
549
|
+
};
|
|
550
|
+
const markerReads = () => mockDynamoSend.mock.calls.filter(([command]) => command instanceof client_dynamodb_1.BatchGetItemCommand).length;
|
|
551
|
+
// What `get` answers for a revalidated page: expired, not a miss.
|
|
552
|
+
const expired = { lastModified: -1 };
|
|
553
|
+
const revalidatedAfterStore = {
|
|
554
|
+
get: { Item: { revalidatedAt: { N: String(lastModified + 1000) } } },
|
|
555
|
+
};
|
|
556
|
+
beforeEach(() => {
|
|
557
|
+
mockS3Send.mockImplementation((command) => Promise.resolve(command instanceof client_s3_1.GetObjectCommand
|
|
558
|
+
? {
|
|
559
|
+
Body: {
|
|
560
|
+
transformToString: jest
|
|
561
|
+
.fn()
|
|
562
|
+
.mockResolvedValue(JSON.stringify(stored)),
|
|
563
|
+
},
|
|
564
|
+
ContentType: "application/json",
|
|
565
|
+
}
|
|
566
|
+
: {}));
|
|
567
|
+
dynamoResponses({});
|
|
568
|
+
});
|
|
569
|
+
it("answers checks within the TTL from the markers it already read", async () => {
|
|
570
|
+
await handler.get("page", getCtx);
|
|
571
|
+
await handler.get("page", getCtx);
|
|
572
|
+
expect(markerReads()).toBe(1);
|
|
573
|
+
});
|
|
574
|
+
it("shares one read between concurrent checks", async () => {
|
|
575
|
+
await Promise.all([
|
|
576
|
+
handler.get("page", getCtx),
|
|
577
|
+
handler.get("page", getCtx),
|
|
578
|
+
handler.get("page", getCtx),
|
|
579
|
+
]);
|
|
580
|
+
expect(markerReads()).toBe(1);
|
|
581
|
+
});
|
|
582
|
+
it("sees its own revalidateTag at once, and logs it for the others", async () => {
|
|
583
|
+
expect(await handler.get("page", getCtx)).toMatchObject({ lastModified });
|
|
584
|
+
dynamoResponses(revalidatedAfterStore);
|
|
585
|
+
await handler.revalidateTag("posts");
|
|
586
|
+
expect(await handler.get("page", getCtx)).toMatchObject(expired);
|
|
587
|
+
const puts = mockDynamoSend.mock.calls.filter(([command]) => command instanceof client_dynamodb_1.PutItemCommand);
|
|
588
|
+
expect(puts).toHaveLength(1);
|
|
589
|
+
expect(commandInput(puts[0][0], client_dynamodb_1.PutItemCommand).Item).toMatchObject({
|
|
590
|
+
pk: { S: "test-build-id#log" },
|
|
591
|
+
sk: { S: expect.stringMatching(/^\d{15}#posts$/) },
|
|
592
|
+
revalidatedAt: { N: expect.any(String) },
|
|
593
|
+
ttl: { N: expect.any(String) },
|
|
594
|
+
});
|
|
595
|
+
});
|
|
596
|
+
it("writes each tag once when the 'use cache' handlers get the same call", async () => {
|
|
597
|
+
// Next.js calls `revalidateTag` here and `updateTags` on every
|
|
598
|
+
// `cacheHandlers` entry, and all of them share one manifest.
|
|
599
|
+
await Promise.all([
|
|
600
|
+
handler.revalidateTag(["posts"]),
|
|
601
|
+
(0, use_cache_common_1.sharedTagManifest)().update(["posts"], undefined),
|
|
602
|
+
]);
|
|
603
|
+
const writes = mockDynamoSend.mock.calls.filter(([command]) => command instanceof client_dynamodb_1.UpdateItemCommand ||
|
|
604
|
+
command instanceof client_dynamodb_1.PutItemCommand);
|
|
605
|
+
expect(writes).toHaveLength(2);
|
|
606
|
+
});
|
|
607
|
+
});
|
|
126
608
|
describe("set", () => {
|
|
609
|
+
// Mapping rows exist only to name CloudFront paths, so only a deployment
|
|
610
|
+
// with a distribution keeps them.
|
|
611
|
+
beforeEach(() => {
|
|
612
|
+
process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME = "test-param-name";
|
|
613
|
+
handler = new s3_cache_handler_1.S3CacheHandler({ context: mockContext });
|
|
614
|
+
});
|
|
615
|
+
it("keeps no mapping rows without a distribution to invalidate", async () => {
|
|
616
|
+
// The Regional constructs have no CloudFront. A row per tag per `set`,
|
|
617
|
+
// and the S3 read before a delete to name them, bought nothing there.
|
|
618
|
+
delete process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME;
|
|
619
|
+
const regional = new s3_cache_handler_1.S3CacheHandler({ context: mockContext });
|
|
620
|
+
mockS3Send.mockResolvedValue({});
|
|
621
|
+
mockDynamoSend.mockResolvedValue({});
|
|
622
|
+
await regional.set("/isr/1", {
|
|
623
|
+
kind: "APP_PAGE" /* CachedRouteKind.APP_PAGE */,
|
|
624
|
+
html: "<p/>",
|
|
625
|
+
rscData: undefined,
|
|
626
|
+
headers: { "x-next-cache-tags": "tag" },
|
|
627
|
+
postponed: undefined,
|
|
628
|
+
segmentData: undefined,
|
|
629
|
+
status: undefined,
|
|
630
|
+
}, createSetContext([]));
|
|
631
|
+
await regional.set("/isr/1", null, { isFallback: false });
|
|
632
|
+
expect(mockDynamoSend).not.toHaveBeenCalled();
|
|
633
|
+
expect(mockS3Send).not.toHaveBeenCalledWith(expect.any(client_s3_1.GetObjectCommand));
|
|
634
|
+
expect(mockS3Send).toHaveBeenCalledWith(expect.any(client_s3_1.DeleteObjectCommand));
|
|
635
|
+
});
|
|
127
636
|
it("should store cache value in S3", async () => {
|
|
128
637
|
const testData = {
|
|
129
638
|
kind: "APP_PAGE" /* CachedRouteKind.APP_PAGE */,
|
|
@@ -157,6 +666,124 @@ describe("S3DynamoCacheHandler", () => {
|
|
|
157
666
|
await handlerWithoutBucket.set("no-bucket-key", testData, createSetContext([]));
|
|
158
667
|
expect(mockS3Send).not.toHaveBeenCalled();
|
|
159
668
|
});
|
|
669
|
+
it("maps the tags a page carries only in its x-next-cache-tags header", async () => {
|
|
670
|
+
// `ResponseCache.set` builds a page's `ctx` without tags, so the render's
|
|
671
|
+
// header is the only place they exist. No mapping row meant `revalidateTag`
|
|
672
|
+
// found nothing to invalidate and CloudFront kept the stale HTML.
|
|
673
|
+
const testData = {
|
|
674
|
+
kind: "APP_PAGE" /* CachedRouteKind.APP_PAGE */,
|
|
675
|
+
html: "<html>tagged</html>",
|
|
676
|
+
rscData: undefined,
|
|
677
|
+
headers: { "x-next-cache-tags": "header-tag,other-tag" },
|
|
678
|
+
postponed: undefined,
|
|
679
|
+
segmentData: undefined,
|
|
680
|
+
status: undefined,
|
|
681
|
+
};
|
|
682
|
+
mockS3Send.mockResolvedValue({});
|
|
683
|
+
mockDynamoSend.mockResolvedValue({});
|
|
684
|
+
await handler.set("/isr/1", testData, createSetContext([]));
|
|
685
|
+
const mappingKeys = client_dynamodb_1.UpdateItemCommand.mock.calls
|
|
686
|
+
.map(([input]) => input.Key.sk.S)
|
|
687
|
+
.sort();
|
|
688
|
+
expect(mappingKeys).toEqual([
|
|
689
|
+
"header-tag#test-build-id/isr/1.json",
|
|
690
|
+
"other-tag#test-build-id/isr/1.json",
|
|
691
|
+
]);
|
|
692
|
+
});
|
|
693
|
+
it("prefers the context's tags over the header when both are present", async () => {
|
|
694
|
+
const testData = {
|
|
695
|
+
kind: "APP_PAGE" /* CachedRouteKind.APP_PAGE */,
|
|
696
|
+
html: "<html>tagged</html>",
|
|
697
|
+
rscData: undefined,
|
|
698
|
+
headers: { "x-next-cache-tags": "header-tag" },
|
|
699
|
+
postponed: undefined,
|
|
700
|
+
segmentData: undefined,
|
|
701
|
+
status: undefined,
|
|
702
|
+
};
|
|
703
|
+
mockS3Send.mockResolvedValue({});
|
|
704
|
+
mockDynamoSend.mockResolvedValue({});
|
|
705
|
+
await handler.set("/isr/1", testData, createSetContext(["ctx-tag"]));
|
|
706
|
+
const mappingKeys = client_dynamodb_1.UpdateItemCommand.mock.calls
|
|
707
|
+
.map(([input]) => input.Key.sk.S)
|
|
708
|
+
.sort();
|
|
709
|
+
expect(mappingKeys).toEqual(["ctx-tag#test-build-id/isr/1.json"]);
|
|
710
|
+
});
|
|
711
|
+
it("removes a deleted page's mapping rows, reading its tags from S3", async () => {
|
|
712
|
+
// A response delete - a cached route that starts answering `notFound()` -
|
|
713
|
+
// arrives as `set(key, null, { cacheControl, ... })` with no tags at all,
|
|
714
|
+
// so the entry itself is the only place its mapping rows can be named
|
|
715
|
+
// from. Left behind, they resolve on the next `revalidateTag` to a cache
|
|
716
|
+
// key whose object is gone and spend a CloudFront wildcard path on it.
|
|
717
|
+
const stored = {
|
|
718
|
+
lastModified: Date.now(),
|
|
719
|
+
tags: ["collection", "_N_T_/isr/1"],
|
|
720
|
+
value: {
|
|
721
|
+
kind: "APP_PAGE" /* CachedRouteKind.APP_PAGE */,
|
|
722
|
+
html: "<html>gone</html>",
|
|
723
|
+
},
|
|
724
|
+
};
|
|
725
|
+
mockS3Send.mockImplementation((command) => Promise.resolve(command instanceof client_s3_1.GetObjectCommand
|
|
726
|
+
? {
|
|
727
|
+
Body: {
|
|
728
|
+
transformToString: jest
|
|
729
|
+
.fn()
|
|
730
|
+
.mockResolvedValue(JSON.stringify(stored)),
|
|
731
|
+
},
|
|
732
|
+
ContentType: "application/json",
|
|
733
|
+
}
|
|
734
|
+
: {}));
|
|
735
|
+
mockDynamoSend.mockResolvedValue({});
|
|
736
|
+
await handler.set("/isr/1", null, {
|
|
737
|
+
cacheControl: { revalidate: 60, expire: undefined },
|
|
738
|
+
isRoutePPREnabled: false,
|
|
739
|
+
isFallback: false,
|
|
740
|
+
});
|
|
741
|
+
expect(mockS3Send).toHaveBeenCalledWith(expect.any(client_s3_1.DeleteObjectCommand));
|
|
742
|
+
const deletedKeys = client_dynamodb_1.DeleteItemCommand.mock.calls
|
|
743
|
+
.map(([input]) => input.Key.sk.S)
|
|
744
|
+
.sort();
|
|
745
|
+
// Mapping rows only. The bare-tag marker row belongs to the tag, and
|
|
746
|
+
// `checkIfRevalidated` reads it for every other entry carrying it.
|
|
747
|
+
expect(deletedKeys).toEqual([
|
|
748
|
+
"_N_T_/isr/1#test-build-id/isr/1.json",
|
|
749
|
+
"collection#test-build-id/isr/1.json",
|
|
750
|
+
]);
|
|
751
|
+
});
|
|
752
|
+
it("deletes a fetch entry's mapping rows from the context's tags alone", async () => {
|
|
753
|
+
// `ctx` carries them here, so the pre-delete read is skipped: only the
|
|
754
|
+
// DeleteObject reaches S3.
|
|
755
|
+
mockS3Send.mockResolvedValue({});
|
|
756
|
+
mockDynamoSend.mockResolvedValue({});
|
|
757
|
+
await handler.set("fetch-key", null, createSetContext(["ctx-tag"]));
|
|
758
|
+
expect(mockS3Send).not.toHaveBeenCalledWith(expect.any(client_s3_1.GetObjectCommand));
|
|
759
|
+
const deletedKeys = client_dynamodb_1.DeleteItemCommand.mock.calls.map(([input]) => input.Key.sk.S);
|
|
760
|
+
expect(deletedKeys).toEqual(["ctx-tag#test-build-id/fetch-key.json"]);
|
|
761
|
+
});
|
|
762
|
+
it("deletes no mapping rows for an untagged entry", async () => {
|
|
763
|
+
mockS3Send.mockImplementation((command) => Promise.resolve(command instanceof client_s3_1.GetObjectCommand
|
|
764
|
+
? {
|
|
765
|
+
Body: {
|
|
766
|
+
transformToString: jest.fn().mockResolvedValue(JSON.stringify({
|
|
767
|
+
lastModified: Date.now(),
|
|
768
|
+
value: { kind: "APP_PAGE" /* CachedRouteKind.APP_PAGE */, html: "<p/>" },
|
|
769
|
+
})),
|
|
770
|
+
},
|
|
771
|
+
ContentType: "application/json",
|
|
772
|
+
}
|
|
773
|
+
: {}));
|
|
774
|
+
mockDynamoSend.mockResolvedValue({});
|
|
775
|
+
await handler.set("/untagged", null, { isFallback: false });
|
|
776
|
+
expect(mockS3Send).toHaveBeenCalledWith(expect.any(client_s3_1.DeleteObjectCommand));
|
|
777
|
+
expect(client_dynamodb_1.DeleteItemCommand).not.toHaveBeenCalled();
|
|
778
|
+
});
|
|
779
|
+
it("still deletes the object when its tags cannot be read", async () => {
|
|
780
|
+
mockS3Send.mockImplementation((command) => command instanceof client_s3_1.GetObjectCommand
|
|
781
|
+
? Promise.reject(new Error("S3 Get Error"))
|
|
782
|
+
: Promise.resolve({}));
|
|
783
|
+
mockDynamoSend.mockResolvedValue({});
|
|
784
|
+
await expect(handler.set("/isr/1", null, { isFallback: false })).resolves.not.toThrow();
|
|
785
|
+
expect(mockS3Send).toHaveBeenCalledWith(expect.any(client_s3_1.DeleteObjectCommand));
|
|
786
|
+
});
|
|
160
787
|
it("should handle S3 errors gracefully", async () => {
|
|
161
788
|
const testData = {
|
|
162
789
|
kind: "APP_PAGE" /* CachedRouteKind.APP_PAGE */,
|
|
@@ -173,20 +800,32 @@ describe("S3DynamoCacheHandler", () => {
|
|
|
173
800
|
});
|
|
174
801
|
});
|
|
175
802
|
describe("revalidateTag", () => {
|
|
176
|
-
it("should
|
|
803
|
+
it("should record the revalidation without deleting the tag's S3 entries", async () => {
|
|
177
804
|
const mockQueryResponse = {
|
|
178
805
|
Items: [
|
|
179
806
|
{ sk: { S: "test-tag#cache-key-1" } },
|
|
180
807
|
{ sk: { S: "test-tag#cache-key-2" } },
|
|
181
808
|
],
|
|
182
809
|
};
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
mockS3Send.mockResolvedValue({}); // For delete commands
|
|
810
|
+
dynamoResponses({ query: mockQueryResponse });
|
|
811
|
+
mockS3Send.mockResolvedValue({});
|
|
186
812
|
await handler.revalidateTag("test-tag");
|
|
187
|
-
|
|
813
|
+
// No distribution, so no mapping rows to read: the marker is all of it.
|
|
814
|
+
expect(mockDynamoSend).not.toHaveBeenCalledWith(expect.any(client_dynamodb_1.QueryCommand));
|
|
188
815
|
expect(mockDynamoSend).toHaveBeenCalledWith(expect.any(client_dynamodb_1.UpdateItemCommand));
|
|
189
|
-
|
|
816
|
+
// The marker row is the invalidation; `get` reads it and hands the entry
|
|
817
|
+
// back expired so Next.js re-renders the route and replaces the object.
|
|
818
|
+
// Deleting the objects here made the next request a hard miss, which on a
|
|
819
|
+
// PPR route is answered from the uncacheable fallback shell for good.
|
|
820
|
+
expect(mockS3Send).not.toHaveBeenCalledWith(expect.any(client_s3_1.DeleteObjectCommand));
|
|
821
|
+
// The tag marker `checkIfRevalidated` reads: keyed by the bare tag, so
|
|
822
|
+
// entries with no mapping row of their own still see the revalidation.
|
|
823
|
+
const markerWrite = client_dynamodb_1.UpdateItemCommand.mock.calls
|
|
824
|
+
.map(([input]) => input)
|
|
825
|
+
.find((input) => input.Key.sk.S === "test-tag");
|
|
826
|
+
expect(markerWrite).toMatchObject({
|
|
827
|
+
UpdateExpression: "SET revalidatedAt = :revalidatedAt",
|
|
828
|
+
});
|
|
190
829
|
});
|
|
191
830
|
it("should not revalidate when DynamoDB table is not configured", async () => {
|
|
192
831
|
delete process.env.CDK_NEXTJS_REVALIDATION_TABLE_NAME;
|
|
@@ -212,8 +851,7 @@ describe("S3DynamoCacheHandler", () => {
|
|
|
212
851
|
{ sk: { S: "test-tag#test-build-id/isr/2.json" } },
|
|
213
852
|
],
|
|
214
853
|
};
|
|
215
|
-
|
|
216
|
-
mockDynamoSend.mockResolvedValue({}); // For update commands
|
|
854
|
+
dynamoResponses({ query: mockQueryResponse });
|
|
217
855
|
mockS3Send.mockResolvedValue({}); // For delete commands
|
|
218
856
|
mockSsmSend.mockResolvedValue({
|
|
219
857
|
Parameter: { Value: "test-distribution-id" },
|
|
@@ -224,14 +862,146 @@ describe("S3DynamoCacheHandler", () => {
|
|
|
224
862
|
expect(mockCloudFrontSend).toHaveBeenCalledWith(expect.any(client_cloudfront_1.CreateInvalidationCommand));
|
|
225
863
|
const [invalidationInput] = client_cloudfront_1.CreateInvalidationCommand.mock.calls[0];
|
|
226
864
|
expect(invalidationInput.DistributionId).toBe("test-distribution-id");
|
|
227
|
-
|
|
865
|
+
// One trailing wildcard per page: an invalidation path matches only the
|
|
866
|
+
// query string it spells out, and a page's RSC payload is cached under
|
|
867
|
+
// `?_rsc=<hash>` - `/isr/1*` covers it, and the slash variant, for one
|
|
868
|
+
// of the fifteen wildcards instead of two.
|
|
869
|
+
expect(invalidationInput.InvalidationBatch.Paths.Items).toEqual([
|
|
870
|
+
"/isr/1*",
|
|
871
|
+
"/isr/2*",
|
|
872
|
+
]);
|
|
873
|
+
});
|
|
874
|
+
it("invalidates a distribution whose ID is in the environment without SSM", async () => {
|
|
875
|
+
// NextjsGlobalContainers: the task can name the distribution directly.
|
|
876
|
+
process.env.CDK_NEXTJS_DISTRIBUTION_ID = "direct-distribution-id";
|
|
877
|
+
const handlerWithDistribution = new s3_cache_handler_1.S3CacheHandler({
|
|
878
|
+
context: mockContext,
|
|
879
|
+
});
|
|
880
|
+
dynamoResponses({
|
|
881
|
+
query: { Items: [{ sk: { S: "test-tag#test-build-id/isr/1.json" } }] },
|
|
882
|
+
});
|
|
883
|
+
mockS3Send.mockResolvedValue({});
|
|
884
|
+
mockCloudFrontSend.mockResolvedValue({});
|
|
885
|
+
await handlerWithDistribution.revalidateTag("test-tag");
|
|
886
|
+
expect(mockSsmSend).not.toHaveBeenCalled();
|
|
887
|
+
const [invalidationInput] = client_cloudfront_1.CreateInvalidationCommand.mock.calls[0];
|
|
888
|
+
expect(invalidationInput.DistributionId).toBe("direct-distribution-id");
|
|
889
|
+
});
|
|
890
|
+
it("splits a mapping row at the tag's length, so a tag containing # still resolves", async () => {
|
|
891
|
+
// A tag is app-defined: `revalidateTag("user#42")` is legal. Splitting at
|
|
892
|
+
// the first "#" left "42#test-build-id/account.json" as the S3 key, whose
|
|
893
|
+
// invalidation path names nothing CloudFront cached, so the page stayed
|
|
894
|
+
// stale while the row was stamped revalidated and never retried.
|
|
895
|
+
process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME = "test-param-name";
|
|
896
|
+
const handlerWithDistribution = new s3_cache_handler_1.S3CacheHandler({
|
|
897
|
+
context: mockContext,
|
|
898
|
+
});
|
|
899
|
+
dynamoResponses({
|
|
900
|
+
query: { Items: [{ sk: { S: "user#42#test-build-id/account.json" } }] },
|
|
901
|
+
});
|
|
902
|
+
mockSsmSend.mockResolvedValue({
|
|
903
|
+
Parameter: { Value: "test-distribution-id" },
|
|
904
|
+
});
|
|
905
|
+
mockCloudFrontSend.mockResolvedValue({});
|
|
906
|
+
await handlerWithDistribution.revalidateTag("user#42");
|
|
907
|
+
const [invalidationInput] = client_cloudfront_1.CreateInvalidationCommand.mock.calls[0];
|
|
908
|
+
expect(invalidationInput.InvalidationBatch.Paths.Items).toEqual([
|
|
909
|
+
"/account*",
|
|
910
|
+
]);
|
|
911
|
+
});
|
|
912
|
+
it("invalidates the path a revalidatePath tag names even with no mapping rows", async () => {
|
|
913
|
+
// A build-time prerender has no mapping rows, so the query comes back
|
|
914
|
+
// empty and there is nothing to derive a CloudFront path from - except the
|
|
915
|
+
// tag, which for `revalidatePath` is `_N_T_<path>`. Without this the CDN
|
|
916
|
+
// answers with the pre-revalidation page until `s-maxage=31536000`
|
|
917
|
+
// expires. Measured against next.js's `test/e2e/app-dir/trailingslash`.
|
|
918
|
+
process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME = "test-param-name";
|
|
919
|
+
const handlerWithDistribution = new s3_cache_handler_1.S3CacheHandler({
|
|
920
|
+
context: mockContext,
|
|
921
|
+
});
|
|
922
|
+
dynamoResponses({ query: { Items: [] } });
|
|
923
|
+
mockSsmSend.mockResolvedValue({
|
|
924
|
+
Parameter: { Value: "test-distribution-id" },
|
|
925
|
+
});
|
|
926
|
+
mockCloudFrontSend.mockResolvedValue({});
|
|
927
|
+
await handlerWithDistribution.revalidateTag("_N_T_/en/legacy/");
|
|
928
|
+
const [invalidationInput] = client_cloudfront_1.CreateInvalidationCommand.mock.calls[0];
|
|
929
|
+
// Both slash variants, which the trailing wildcard covers: a
|
|
930
|
+
// `trailingSlash` app's cached URI is the redirect target, not the route.
|
|
931
|
+
expect(invalidationInput.InvalidationBatch.Paths.Items).toEqual([
|
|
932
|
+
"/en/legacy*",
|
|
933
|
+
]);
|
|
934
|
+
});
|
|
935
|
+
it("invalidates the URI under the app's basePath, not the bare route", async () => {
|
|
936
|
+
// CloudFront cached `/base/isr/1`; the cache key and the `revalidatePath`
|
|
937
|
+
// tag both name `/isr/1`, because Next.js strips `basePath` before routing
|
|
938
|
+
// and never shows it to the cache handler. Invalidating the bare route
|
|
939
|
+
// matches nothing at the edge, so the stale page survives until
|
|
940
|
+
// `s-maxage` expires.
|
|
941
|
+
process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME = "test-param-name";
|
|
942
|
+
useBasePath();
|
|
943
|
+
const handlerWithBasePath = new s3_cache_handler_1.S3CacheHandler({
|
|
944
|
+
context: mockContext,
|
|
945
|
+
});
|
|
946
|
+
dynamoResponses({
|
|
947
|
+
query: {
|
|
948
|
+
Items: [
|
|
949
|
+
{ sk: { S: "test-tag#test-build-id/isr/1.json" } },
|
|
950
|
+
{ sk: { S: "test-tag#test-build-id/index.json" } },
|
|
951
|
+
],
|
|
952
|
+
},
|
|
953
|
+
});
|
|
954
|
+
mockS3Send.mockResolvedValue({});
|
|
955
|
+
mockSsmSend.mockResolvedValue({
|
|
956
|
+
Parameter: { Value: "test-distribution-id" },
|
|
957
|
+
});
|
|
958
|
+
mockCloudFrontSend.mockResolvedValue({});
|
|
959
|
+
await handlerWithBasePath.revalidateTag("test-tag");
|
|
960
|
+
const [invalidationInput] = client_cloudfront_1.CreateInvalidationCommand.mock.calls[0];
|
|
961
|
+
expect(invalidationInput.InvalidationBatch.Paths.Items).toEqual(expect.arrayContaining([
|
|
962
|
+
"/base/isr/1*",
|
|
963
|
+
// The app's root under a `basePath` is `/base`, not `/base/`, and
|
|
964
|
+
// spelled out: `/base*` would be the whole app.
|
|
965
|
+
"/base",
|
|
966
|
+
"/base?*",
|
|
967
|
+
"/base/",
|
|
968
|
+
"/base/?*",
|
|
969
|
+
]));
|
|
970
|
+
expect(invalidationInput.InvalidationBatch.Paths.Items).not.toContain("/isr/1");
|
|
971
|
+
});
|
|
972
|
+
it("adds the basePath to a revalidatePath tag's path too", async () => {
|
|
973
|
+
process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME = "test-param-name";
|
|
974
|
+
useBasePath();
|
|
975
|
+
const handlerWithBasePath = new s3_cache_handler_1.S3CacheHandler({
|
|
976
|
+
context: mockContext,
|
|
977
|
+
});
|
|
978
|
+
dynamoResponses({ query: { Items: [] } });
|
|
979
|
+
mockSsmSend.mockResolvedValue({
|
|
980
|
+
Parameter: { Value: "test-distribution-id" },
|
|
981
|
+
});
|
|
982
|
+
mockCloudFrontSend.mockResolvedValue({});
|
|
983
|
+
await handlerWithBasePath.revalidateTag("_N_T_/blog");
|
|
984
|
+
const [invalidationInput] = client_cloudfront_1.CreateInvalidationCommand.mock.calls[0];
|
|
985
|
+
expect(invalidationInput.InvalidationBatch.Paths.Items).toEqual([
|
|
986
|
+
"/base/blog*",
|
|
987
|
+
]);
|
|
988
|
+
});
|
|
989
|
+
it("does not invalidate a CloudFront path for an app tag", async () => {
|
|
990
|
+
process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME = "test-param-name";
|
|
991
|
+
const handlerWithDistribution = new s3_cache_handler_1.S3CacheHandler({
|
|
992
|
+
context: mockContext,
|
|
993
|
+
});
|
|
994
|
+
dynamoResponses({ query: { Items: [] } });
|
|
995
|
+
await handlerWithDistribution.revalidateTag("posts");
|
|
996
|
+
// Nothing to invalidate: an app tag names no path, and no entry is mapped
|
|
997
|
+
// to it.
|
|
998
|
+
expect(mockCloudFrontSend).not.toHaveBeenCalled();
|
|
228
999
|
});
|
|
229
1000
|
it("should not call SSM or CloudFront when no distribution parameter is configured", async () => {
|
|
230
1001
|
const mockQueryResponse = {
|
|
231
1002
|
Items: [{ sk: { S: "test-tag#test-build-id/isr/1.json" } }],
|
|
232
1003
|
};
|
|
233
|
-
|
|
234
|
-
mockDynamoSend.mockResolvedValue({});
|
|
1004
|
+
dynamoResponses({ query: mockQueryResponse });
|
|
235
1005
|
mockS3Send.mockResolvedValue({});
|
|
236
1006
|
await handler.revalidateTag("test-tag");
|
|
237
1007
|
expect(mockSsmSend).not.toHaveBeenCalled();
|
|
@@ -245,35 +1015,420 @@ describe("S3DynamoCacheHandler", () => {
|
|
|
245
1015
|
const mockQueryResponse = {
|
|
246
1016
|
Items: [{ sk: { S: "test-tag#test-build-id/isr/1.json" } }],
|
|
247
1017
|
};
|
|
248
|
-
|
|
249
|
-
mockDynamoSend.mockResolvedValue({});
|
|
1018
|
+
dynamoResponses({ query: mockQueryResponse });
|
|
250
1019
|
mockS3Send.mockResolvedValue({});
|
|
251
1020
|
mockSsmSend.mockRejectedValueOnce(new Error("SSM Error"));
|
|
252
1021
|
await expect(handlerWithDistribution.revalidateTag("test-tag")).resolves.not.toThrow();
|
|
253
1022
|
});
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
1023
|
+
it("invalidates the page itself for a revalidatePath(path, 'page') tag", async () => {
|
|
1024
|
+
// `revalidatePath(path, type)` appends the type to the implicit tag, so the
|
|
1025
|
+
// tag reads `_N_T_/pricing/page` - a path no URL has. Taking it literally
|
|
1026
|
+
// invalidated nothing that existed, which is the whole of the bug: the
|
|
1027
|
+
// second argument silently turned CDN invalidation off.
|
|
1028
|
+
process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME = "test-param-name";
|
|
1029
|
+
const handlerWithDistribution = new s3_cache_handler_1.S3CacheHandler({
|
|
1030
|
+
context: mockContext,
|
|
1031
|
+
});
|
|
1032
|
+
dynamoResponses({ query: { Items: [] } });
|
|
1033
|
+
mockSsmSend.mockResolvedValue({
|
|
1034
|
+
Parameter: { Value: "test-distribution-id" },
|
|
1035
|
+
});
|
|
1036
|
+
mockCloudFrontSend.mockResolvedValue({});
|
|
1037
|
+
await handlerWithDistribution.revalidateTag("_N_T_/pricing/page");
|
|
1038
|
+
const [invalidationInput] = client_cloudfront_1.CreateInvalidationCommand.mock.calls[0];
|
|
1039
|
+
expect(invalidationInput.InvalidationBatch.Paths.Items).toEqual(expect.arrayContaining(["/pricing*"]));
|
|
258
1040
|
});
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
1041
|
+
it("skips mapping rows that name no cached URI", async () => {
|
|
1042
|
+
// A dynamic template and a fetch-cache entry are both rows a real tag
|
|
1043
|
+
// carries, and neither is a URL: `/blog/[slug]` matches nothing at the edge
|
|
1044
|
+
// (CloudFront has no such URI), and a fetch key is a hash of the request.
|
|
1045
|
+
process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME = "test-param-name";
|
|
1046
|
+
const handlerWithDistribution = new s3_cache_handler_1.S3CacheHandler({
|
|
263
1047
|
context: mockContext,
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
1048
|
+
});
|
|
1049
|
+
dynamoResponses({
|
|
1050
|
+
query: {
|
|
1051
|
+
Items: [
|
|
1052
|
+
{ sk: { S: "test-tag#test-build-id/blog/[slug].json" } },
|
|
1053
|
+
{
|
|
1054
|
+
sk: {
|
|
1055
|
+
S: "test-tag#test-build-id/0123456789abcdef0123456789abcdef.json",
|
|
1056
|
+
},
|
|
1057
|
+
},
|
|
1058
|
+
{ sk: { S: "test-tag#test-build-id/blog/hello.json" } },
|
|
1059
|
+
],
|
|
268
1060
|
},
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
1061
|
+
});
|
|
1062
|
+
mockS3Send.mockResolvedValue({});
|
|
1063
|
+
mockSsmSend.mockResolvedValue({
|
|
1064
|
+
Parameter: { Value: "test-distribution-id" },
|
|
1065
|
+
});
|
|
1066
|
+
mockCloudFrontSend.mockResolvedValue({});
|
|
1067
|
+
await handlerWithDistribution.revalidateTag("test-tag");
|
|
1068
|
+
const [invalidationInput] = client_cloudfront_1.CreateInvalidationCommand.mock.calls[0];
|
|
1069
|
+
const items = invalidationInput.InvalidationBatch.Paths.Items;
|
|
1070
|
+
expect(items).toEqual(["/blog/hello*"]);
|
|
1071
|
+
expect(items.some((path) => path.includes("["))).toBe(false);
|
|
1072
|
+
expect(items.some((path) => path.includes("0123456789abcdef"))).toBe(false);
|
|
1073
|
+
});
|
|
1074
|
+
it("walks every Query page of a tag's mapping rows", async () => {
|
|
1075
|
+
// One `Query` answers with at most 1 MB. A tag on a large app runs past
|
|
1076
|
+
// that, and taking only the first page deleted some of the tag's entries
|
|
1077
|
+
// and invalidated only some of its paths - a partial revalidation that
|
|
1078
|
+
// looks like a cache bug.
|
|
1079
|
+
process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME = "test-param-name";
|
|
1080
|
+
const handlerWithDistribution = new s3_cache_handler_1.S3CacheHandler({
|
|
1081
|
+
context: mockContext,
|
|
1082
|
+
});
|
|
1083
|
+
let queries = 0;
|
|
1084
|
+
mockDynamoSend.mockImplementation((command) => {
|
|
1085
|
+
if (command instanceof client_dynamodb_1.QueryCommand) {
|
|
1086
|
+
queries++;
|
|
1087
|
+
return Promise.resolve(queries === 1
|
|
1088
|
+
? {
|
|
1089
|
+
Items: [{ sk: { S: "test-tag#test-build-id/isr/1.json" } }],
|
|
1090
|
+
LastEvaluatedKey: { pk: { S: "test-build-id" } },
|
|
1091
|
+
}
|
|
1092
|
+
: { Items: [{ sk: { S: "test-tag#test-build-id/isr/2.json" } }] });
|
|
1093
|
+
}
|
|
1094
|
+
return Promise.resolve({});
|
|
1095
|
+
});
|
|
1096
|
+
mockS3Send.mockResolvedValue({});
|
|
1097
|
+
mockSsmSend.mockResolvedValue({
|
|
1098
|
+
Parameter: { Value: "test-distribution-id" },
|
|
1099
|
+
});
|
|
1100
|
+
mockCloudFrontSend.mockResolvedValue({});
|
|
1101
|
+
await handlerWithDistribution.revalidateTag("test-tag");
|
|
1102
|
+
expect(queries).toBe(2);
|
|
1103
|
+
const [invalidationInput] = client_cloudfront_1.CreateInvalidationCommand.mock.calls[0];
|
|
1104
|
+
expect(invalidationInput.InvalidationBatch.Paths.Items).toEqual([
|
|
1105
|
+
"/isr/1*",
|
|
1106
|
+
"/isr/2*",
|
|
1107
|
+
]);
|
|
1108
|
+
});
|
|
1109
|
+
it("collapses to one app-wide wildcard rather than sending many invalidations", async () => {
|
|
1110
|
+
// CloudFront caps a request at 15 wildcard paths and allows 15
|
|
1111
|
+
// invalidations in progress. A tag covering hundreds of pages therefore
|
|
1112
|
+
// cannot be spelled out: sending a request per 15 paths throttles, and a
|
|
1113
|
+
// throttled request is a page that stays stale.
|
|
1114
|
+
process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME = "test-param-name";
|
|
1115
|
+
const handlerWithDistribution = new s3_cache_handler_1.S3CacheHandler({
|
|
1116
|
+
context: mockContext,
|
|
1117
|
+
});
|
|
1118
|
+
dynamoResponses({
|
|
1119
|
+
query: {
|
|
1120
|
+
Items: Array.from({ length: 200 }, (_, i) => ({
|
|
1121
|
+
sk: { S: `test-tag#test-build-id/isr/${i}.json` },
|
|
1122
|
+
})),
|
|
273
1123
|
},
|
|
274
1124
|
});
|
|
275
|
-
|
|
1125
|
+
mockS3Send.mockResolvedValue({});
|
|
1126
|
+
mockSsmSend.mockResolvedValue({
|
|
1127
|
+
Parameter: { Value: "test-distribution-id" },
|
|
1128
|
+
});
|
|
1129
|
+
mockCloudFrontSend.mockResolvedValue({});
|
|
1130
|
+
await handlerWithDistribution.revalidateTag("test-tag");
|
|
1131
|
+
expect(mockCloudFrontSend).toHaveBeenCalledTimes(1);
|
|
1132
|
+
const [invalidationInput] = client_cloudfront_1.CreateInvalidationCommand.mock.calls[0];
|
|
1133
|
+
expect(invalidationInput.InvalidationBatch.Paths.Items).toEqual(["/*"]);
|
|
1134
|
+
});
|
|
1135
|
+
/** A handler with a distribution, and DynamoDB answering `query` for it. */
|
|
1136
|
+
const withDistribution = (query) => {
|
|
1137
|
+
process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME = "test-param-name";
|
|
1138
|
+
dynamoResponses({ query });
|
|
1139
|
+
mockSsmSend.mockResolvedValue({
|
|
1140
|
+
Parameter: { Value: "test-distribution-id" },
|
|
1141
|
+
});
|
|
1142
|
+
mockCloudFrontSend.mockResolvedValue({});
|
|
1143
|
+
return new s3_cache_handler_1.S3CacheHandler({ context: mockContext });
|
|
1144
|
+
};
|
|
1145
|
+
const invalidations = () => client_cloudfront_1.CreateInvalidationCommand.mock.calls.map(([input]) => input.InvalidationBatch.Paths.Items);
|
|
1146
|
+
const pages = (tag, count) => ({
|
|
1147
|
+
Items: Array.from({ length: count }, (_, i) => ({
|
|
1148
|
+
sk: { S: `${tag}#test-build-id/isr/${i}.json` },
|
|
1149
|
+
})),
|
|
1150
|
+
});
|
|
1151
|
+
it("sends a tag's paths as one request while they fit", async () => {
|
|
1152
|
+
// CloudFront's wildcard quota is shared by every invalidation in
|
|
1153
|
+
// progress on the distribution. Splitting ten pages' twenty wildcards
|
|
1154
|
+
// into two back-to-back requests had the second rejected, and its pages
|
|
1155
|
+
// stayed stale at the edge.
|
|
1156
|
+
await withDistribution(pages("test-tag", 10)).revalidateTag("test-tag");
|
|
1157
|
+
expect(invalidations()).toHaveLength(1);
|
|
1158
|
+
expect(invalidations()[0]).toHaveLength(10);
|
|
1159
|
+
});
|
|
1160
|
+
it("collapses to the whole app past fifteen wildcards", async () => {
|
|
1161
|
+
await withDistribution(pages("test-tag", 16)).revalidateTag("test-tag");
|
|
1162
|
+
expect(invalidations()).toEqual([["/*"]]);
|
|
1163
|
+
});
|
|
1164
|
+
it("collapses to the whole app under a basePath, its root included", async () => {
|
|
1165
|
+
// Not `/base*`, which would also invalidate a sibling app at `/base2`.
|
|
1166
|
+
useBasePath();
|
|
1167
|
+
await withDistribution(pages("test-tag", 16)).revalidateTag("test-tag");
|
|
1168
|
+
expect(invalidations()).toEqual([["/base", "/base?*", "/base/*"]]);
|
|
1169
|
+
});
|
|
1170
|
+
it("invalidates the whole app under a basePath when a tag's rows cannot be read", async () => {
|
|
1171
|
+
useBasePath();
|
|
1172
|
+
jest.spyOn(console, "error").mockImplementation();
|
|
1173
|
+
const cdnHandler = withDistribution({ Items: [] });
|
|
1174
|
+
mockDynamoSend.mockImplementation((command) => command instanceof client_dynamodb_1.QueryCommand
|
|
1175
|
+
? Promise.reject(new Error("ProvisionedThroughputExceeded"))
|
|
1176
|
+
: Promise.resolve({}));
|
|
1177
|
+
await cdnHandler.revalidateTag("posts");
|
|
1178
|
+
expect(invalidations()).toEqual([["/base", "/base?*", "/base/*"]]);
|
|
1179
|
+
});
|
|
1180
|
+
it("percent-encodes the routes it invalidates", async () => {
|
|
1181
|
+
// Cache keys are decoded pathnames, while CloudFront caches - and
|
|
1182
|
+
// invalidation paths match - the encoded URI the browser sent.
|
|
1183
|
+
await withDistribution({
|
|
1184
|
+
Items: [
|
|
1185
|
+
{ sk: { S: "test-tag#test-build-id/blog/héllo.json" } },
|
|
1186
|
+
{ sk: { S: "test-tag#test-build-id/blog/hello world.json" } },
|
|
1187
|
+
// Path delimiters Next.js re-escaped in the key stay as they are.
|
|
1188
|
+
{ sk: { S: "test-tag#test-build-id/files/a%2Fb.json" } },
|
|
1189
|
+
{ sk: { S: "test-tag#test-build-id/@user/50%.json" } },
|
|
1190
|
+
],
|
|
1191
|
+
}).revalidateTag(["test-tag", "_N_T_/tags/c#?"]);
|
|
1192
|
+
expect(invalidations()).toEqual([
|
|
1193
|
+
[
|
|
1194
|
+
"/blog/h%C3%A9llo*",
|
|
1195
|
+
"/blog/hello%20world*",
|
|
1196
|
+
"/files/a%2Fb*",
|
|
1197
|
+
"/@user/50%25*",
|
|
1198
|
+
"/tags/c%23%3F*",
|
|
1199
|
+
],
|
|
1200
|
+
]);
|
|
1201
|
+
});
|
|
1202
|
+
it("invalidates every tag of one call in a single request", async () => {
|
|
1203
|
+
// `revalidateTag` hands the handler every tag a request revalidated at
|
|
1204
|
+
// once; a request per tag competes for the same quota.
|
|
1205
|
+
await withDistribution({ Items: [] }).revalidateTag([
|
|
1206
|
+
"_N_T_/a",
|
|
1207
|
+
"_N_T_/b",
|
|
1208
|
+
]);
|
|
1209
|
+
expect(invalidations()).toEqual([["/a*", "/b*"]]);
|
|
1210
|
+
});
|
|
1211
|
+
it("invalidates the whole app when a tag has more rows than it walks", async () => {
|
|
1212
|
+
// The rows past the last page walked are pages too; invalidating only the
|
|
1213
|
+
// ones read left the rest stale at the edge for up to a year.
|
|
1214
|
+
process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME = "test-param-name";
|
|
1215
|
+
mockDynamoSend.mockImplementation((command) => Promise.resolve(command instanceof client_dynamodb_1.QueryCommand
|
|
1216
|
+
? {
|
|
1217
|
+
Items: [{ sk: { S: "test-tag#test-build-id/isr/1.json" } }],
|
|
1218
|
+
LastEvaluatedKey: { pk: { S: "test-build-id" } },
|
|
1219
|
+
}
|
|
1220
|
+
: {}));
|
|
1221
|
+
mockSsmSend.mockResolvedValue({
|
|
1222
|
+
Parameter: { Value: "test-distribution-id" },
|
|
1223
|
+
});
|
|
1224
|
+
mockCloudFrontSend.mockResolvedValue({});
|
|
1225
|
+
await new s3_cache_handler_1.S3CacheHandler({ context: mockContext }).revalidateTag("test-tag");
|
|
1226
|
+
expect(invalidations()).toEqual([["/*"]]);
|
|
1227
|
+
});
|
|
1228
|
+
it("ignores rows that another tag sharing the prefix owns", async () => {
|
|
1229
|
+
// `begins_with(sk, "user#")` also matches the tag `user#42`: its bare
|
|
1230
|
+
// marker row, and its mapping rows. Read as `user`'s, they became
|
|
1231
|
+
// invalidation paths like `/42` that name nothing.
|
|
1232
|
+
await withDistribution({
|
|
1233
|
+
Items: [
|
|
1234
|
+
{ sk: { S: "user#42" } },
|
|
1235
|
+
{ sk: { S: "user#42#test-build-id/account.json" } },
|
|
1236
|
+
{ sk: { S: "user#test-build-id/profile.json" } },
|
|
1237
|
+
],
|
|
1238
|
+
}).revalidateTag("user");
|
|
1239
|
+
expect(invalidations()).toEqual([["/profile*"]]);
|
|
1240
|
+
});
|
|
1241
|
+
it("adds the build-time prerenders the build's tag manifest names, reading it once", async () => {
|
|
1242
|
+
// A prerender is written by the adapter, never through `set`, so it has
|
|
1243
|
+
// no mapping row: the manifest is the only record of its tags.
|
|
1244
|
+
const cdnHandler = withDistribution({
|
|
1245
|
+
Items: [{ sk: { S: "posts#test-build-id/blog/b.json" } }],
|
|
1246
|
+
});
|
|
1247
|
+
const manifestGets = () => client_s3_1.GetObjectCommand.mock.calls.filter(([input]) => input.Key === "test-build-id/_cdk-nextjs-tag-manifest.json");
|
|
1248
|
+
mockS3Send.mockImplementation((command) => Promise.resolve(command instanceof client_s3_1.GetObjectCommand
|
|
1249
|
+
? {
|
|
1250
|
+
Body: {
|
|
1251
|
+
transformToString: async () => JSON.stringify({ posts: ["blog/a", "index"] }),
|
|
1252
|
+
},
|
|
1253
|
+
}
|
|
1254
|
+
: {}));
|
|
1255
|
+
await cdnHandler.revalidateTag("posts");
|
|
1256
|
+
await cdnHandler.revalidateTag("constructor");
|
|
1257
|
+
expect(invalidations()).toEqual([["/blog/b*", "/blog/a*", "/", "/?*"]]);
|
|
1258
|
+
expect(manifestGets()).toHaveLength(1);
|
|
1259
|
+
});
|
|
1260
|
+
it("invalidates the whole app when the build's tag manifest cannot be read, and reads it again next time", async () => {
|
|
1261
|
+
const cdnHandler = withDistribution({ Items: [] });
|
|
1262
|
+
mockS3Send.mockRejectedValueOnce(new Error("AccessDenied"));
|
|
1263
|
+
await cdnHandler.revalidateTag("posts");
|
|
1264
|
+
await cdnHandler.revalidateTag("posts");
|
|
1265
|
+
expect(invalidations()).toEqual([["/*"]]);
|
|
1266
|
+
expect(mockS3Send).toHaveBeenCalledTimes(2);
|
|
1267
|
+
});
|
|
1268
|
+
it("invalidates the whole app when a tag's marker row cannot be written", async () => {
|
|
1269
|
+
// Other instances would never see the revalidation, so no path list is
|
|
1270
|
+
// enough.
|
|
1271
|
+
process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME = "test-param-name";
|
|
1272
|
+
jest.spyOn(console, "error").mockImplementation();
|
|
1273
|
+
mockDynamoSend.mockImplementation((command) => command instanceof client_dynamodb_1.QueryCommand
|
|
1274
|
+
? Promise.resolve({ Items: [] })
|
|
1275
|
+
: Promise.reject(new Error("ProvisionedThroughputExceeded")));
|
|
1276
|
+
mockSsmSend.mockResolvedValue({
|
|
1277
|
+
Parameter: { Value: "test-distribution-id" },
|
|
1278
|
+
});
|
|
1279
|
+
mockCloudFrontSend.mockResolvedValue({});
|
|
1280
|
+
await new s3_cache_handler_1.S3CacheHandler({ context: mockContext }).revalidateTag("posts");
|
|
1281
|
+
expect(invalidations()).toEqual([["/*"]]);
|
|
1282
|
+
});
|
|
1283
|
+
it("invalidates the whole app when a tag's mapping rows cannot be read", async () => {
|
|
1284
|
+
// The marker row is written before the Query, so a failed Query leaves
|
|
1285
|
+
// the tag revalidated at the origin with no paths named for the edge.
|
|
1286
|
+
process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME = "test-param-name";
|
|
1287
|
+
jest.spyOn(console, "error").mockImplementation();
|
|
1288
|
+
mockDynamoSend.mockImplementation((command) => command instanceof client_dynamodb_1.QueryCommand
|
|
1289
|
+
? Promise.reject(new Error("ProvisionedThroughputExceeded"))
|
|
1290
|
+
: Promise.resolve({}));
|
|
1291
|
+
mockSsmSend.mockResolvedValue({
|
|
1292
|
+
Parameter: { Value: "test-distribution-id" },
|
|
1293
|
+
});
|
|
1294
|
+
mockCloudFrontSend.mockResolvedValue({});
|
|
1295
|
+
await new s3_cache_handler_1.S3CacheHandler({ context: mockContext }).revalidateTag([
|
|
1296
|
+
"_N_T_/a",
|
|
1297
|
+
"posts",
|
|
1298
|
+
]);
|
|
1299
|
+
expect(invalidations()).toEqual([["/*"]]);
|
|
1300
|
+
});
|
|
1301
|
+
describe("when CloudFront's invalidation quota is full", () => {
|
|
1302
|
+
beforeEach(() => {
|
|
1303
|
+
jest.useFakeTimers();
|
|
1304
|
+
});
|
|
1305
|
+
afterEach(() => {
|
|
1306
|
+
jest.useRealTimers();
|
|
1307
|
+
});
|
|
1308
|
+
const quotaError = () => Object.assign(new Error("Your request contains too many invalidations"), {
|
|
1309
|
+
name: "TooManyInvalidationsInProgress",
|
|
1310
|
+
});
|
|
1311
|
+
it("retries as one app-wide wildcard after a backoff", async () => {
|
|
1312
|
+
const cdnHandler = withDistribution(pages("test-tag", 2));
|
|
1313
|
+
mockCloudFrontSend
|
|
1314
|
+
.mockRejectedValueOnce(quotaError())
|
|
1315
|
+
.mockResolvedValueOnce({});
|
|
1316
|
+
const done = cdnHandler.revalidateTag("test-tag");
|
|
1317
|
+
await jest.advanceTimersByTimeAsync(1000);
|
|
1318
|
+
await done;
|
|
1319
|
+
expect(invalidations()).toEqual([["/isr/0*", "/isr/1*"], ["/*"]]);
|
|
1320
|
+
});
|
|
1321
|
+
it("gives up after the last retry", async () => {
|
|
1322
|
+
const cdnHandler = withDistribution(pages("test-tag", 1));
|
|
1323
|
+
mockCloudFrontSend.mockRejectedValue(quotaError());
|
|
1324
|
+
const done = cdnHandler.revalidateTag("test-tag");
|
|
1325
|
+
await jest.advanceTimersByTimeAsync(3000);
|
|
1326
|
+
await expect(done).resolves.toBeUndefined();
|
|
1327
|
+
expect(invalidations()).toEqual([["/isr/0*"], ["/*"], ["/*"]]);
|
|
1328
|
+
});
|
|
1329
|
+
it("does not retry any other error", async () => {
|
|
1330
|
+
const cdnHandler = withDistribution(pages("test-tag", 1));
|
|
1331
|
+
mockCloudFrontSend.mockRejectedValue(new Error("AccessDenied"));
|
|
1332
|
+
await cdnHandler.revalidateTag("test-tag");
|
|
1333
|
+
expect(invalidations()).toEqual([["/isr/0*"]]);
|
|
1334
|
+
});
|
|
1335
|
+
it("retries the app's root under a basePath too", async () => {
|
|
1336
|
+
// `/base/*` matches neither `/base` nor `/base?_rsc=…`: retrying
|
|
1337
|
+
// `revalidatePath("/")` as that left the home page stale at the edge.
|
|
1338
|
+
useBasePath();
|
|
1339
|
+
const cdnHandler = withDistribution({ Items: [] });
|
|
1340
|
+
mockCloudFrontSend
|
|
1341
|
+
.mockRejectedValueOnce(quotaError())
|
|
1342
|
+
.mockResolvedValueOnce({});
|
|
1343
|
+
const done = cdnHandler.revalidateTag("_N_T_/");
|
|
1344
|
+
await jest.advanceTimersByTimeAsync(1000);
|
|
1345
|
+
await done;
|
|
1346
|
+
expect(invalidations()[1]).toEqual(["/base", "/base?*", "/base/*"]);
|
|
1347
|
+
});
|
|
1348
|
+
});
|
|
1349
|
+
it("records a profile's revalidation as stale now, expiring later", async () => {
|
|
1350
|
+
const before = Date.now();
|
|
1351
|
+
await handler.revalidateTag("posts", { expire: 60 });
|
|
1352
|
+
const [input] = client_dynamodb_1.UpdateItemCommand.mock.calls[0];
|
|
1353
|
+
expect(input.Key.sk.S).toBe("posts");
|
|
1354
|
+
expect(input.UpdateExpression).toBe("SET staleAt = :staleAt, expiredAt = :expiredAt");
|
|
1355
|
+
const staleAt = Number(input.ExpressionAttributeValues[":staleAt"].N);
|
|
1356
|
+
expect(staleAt).toBeGreaterThanOrEqual(before);
|
|
1357
|
+
expect(Number(input.ExpressionAttributeValues[":expiredAt"].N)).toBe(staleAt + 60_000);
|
|
1358
|
+
});
|
|
1359
|
+
});
|
|
1360
|
+
/**
|
|
1361
|
+
* The cache handler's half of `res.revalidate()`'s CDN invalidation: the hook
|
|
1362
|
+
* it registers for `invalidateRevalidatedPage` in `runtime/core.ts`. The
|
|
1363
|
+
* runtime's half is in `core.test.ts`.
|
|
1364
|
+
*/
|
|
1365
|
+
describe("the revalidated-page hook", () => {
|
|
1366
|
+
const HOOK = manifest_1.REVALIDATED_PAGE_HOOK;
|
|
1367
|
+
const globals = globalThis;
|
|
1368
|
+
beforeEach(() => {
|
|
1369
|
+
delete globals[HOOK];
|
|
1370
|
+
mockSsmSend.mockResolvedValue({ Parameter: { Value: "EDISTRIBUTION" } });
|
|
1371
|
+
mockCloudFrontSend.mockResolvedValue({});
|
|
1372
|
+
client_cloudfront_1.CreateInvalidationCommand.mockClear();
|
|
1373
|
+
});
|
|
1374
|
+
function invalidatedPaths() {
|
|
1375
|
+
const [input] = client_cloudfront_1.CreateInvalidationCommand.mock
|
|
1376
|
+
.calls[0];
|
|
1377
|
+
return input.InvalidationBatch.Paths.Items;
|
|
1378
|
+
}
|
|
1379
|
+
it("is registered only behind a distribution", () => {
|
|
1380
|
+
new s3_cache_handler_1.S3CacheHandler({ context: mockContext });
|
|
1381
|
+
expect(globals[HOOK]).toBeUndefined();
|
|
1382
|
+
process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME = "param";
|
|
1383
|
+
new s3_cache_handler_1.S3CacheHandler({ context: mockContext });
|
|
1384
|
+
expect(typeof globals[HOOK]).toBe("function");
|
|
1385
|
+
});
|
|
1386
|
+
it("invalidates the page and its data route under basePath", async () => {
|
|
1387
|
+
process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME = "param";
|
|
1388
|
+
new s3_cache_handler_1.S3CacheHandler({ context: mockContext, basePath: "base" });
|
|
1389
|
+
await globals[HOOK]([
|
|
1390
|
+
"/blog/hello",
|
|
1391
|
+
"/_next/data/test-build-id/blog/hello.json",
|
|
1392
|
+
]);
|
|
1393
|
+
expect(invalidatedPaths()).toEqual([
|
|
1394
|
+
"/base/blog/hello*",
|
|
1395
|
+
"/base/_next/data/test-build-id/blog/hello.json*",
|
|
1396
|
+
]);
|
|
1397
|
+
});
|
|
1398
|
+
it("spells out the root rather than invalidating the whole app", async () => {
|
|
1399
|
+
process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME = "param";
|
|
1400
|
+
new s3_cache_handler_1.S3CacheHandler({ context: mockContext });
|
|
1401
|
+
await globals[HOOK]([
|
|
1402
|
+
"/",
|
|
1403
|
+
"/_next/data/test-build-id/index.json",
|
|
1404
|
+
]);
|
|
1405
|
+
expect(invalidatedPaths()).toEqual([
|
|
1406
|
+
"/",
|
|
1407
|
+
"/?*",
|
|
1408
|
+
"/_next/data/test-build-id/index.json*",
|
|
1409
|
+
]);
|
|
1410
|
+
});
|
|
1411
|
+
it("sends every path once, in one invalidation", async () => {
|
|
1412
|
+
process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME = "param";
|
|
1413
|
+
new s3_cache_handler_1.S3CacheHandler({ context: mockContext });
|
|
1414
|
+
await globals[HOOK]([
|
|
1415
|
+
"/blog",
|
|
1416
|
+
"/_next/data/test-build-id/en/blog.json",
|
|
1417
|
+
"/en/blog",
|
|
1418
|
+
"/blog",
|
|
1419
|
+
]);
|
|
1420
|
+
expect(client_cloudfront_1.CreateInvalidationCommand).toHaveBeenCalledTimes(1);
|
|
1421
|
+
expect(invalidatedPaths()).toEqual([
|
|
1422
|
+
"/blog*",
|
|
1423
|
+
"/_next/data/test-build-id/en/blog.json*",
|
|
1424
|
+
"/en/blog*",
|
|
1425
|
+
]);
|
|
1426
|
+
});
|
|
1427
|
+
});
|
|
1428
|
+
describe("resetRequestCache", () => {
|
|
1429
|
+
it("should complete without errors", async () => {
|
|
1430
|
+
await expect(handler.resetRequestCache()).resolves.not.toThrow();
|
|
276
1431
|
});
|
|
277
1432
|
});
|
|
278
1433
|
});
|
|
279
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiczMtY2FjaGUtaGFuZGxlci50ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FkYXB0ZXIvczMtY2FjaGUtaGFuZGxlci50ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsc0RBQXNEO0FBQ3RELGVBQWU7QUFDZixJQUFJLENBQUMsSUFBSSxDQUFDLG9CQUFvQixDQUFDLENBQUM7QUFDaEMsSUFBSSxDQUFDLElBQUksQ0FBQywwQkFBMEIsQ0FBQyxDQUFDO0FBQ3RDLElBQUksQ0FBQyxJQUFJLENBQUMsNEJBQTRCLENBQUMsQ0FBQztBQUN4QyxJQUFJLENBQUMsSUFBSSxDQUFDLHFCQUFxQixDQUFDLENBQUM7QUFFakMsa0VBR29DO0FBQ3BDLDhEQUlrQztBQUNsQyxrREFLNEI7QUFDNUIsb0RBQWdEO0FBT2hELHlEQUFvRDtBQUVwRCxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsRUFBRSxFQUFFLENBQUM7QUFDN0IsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLEVBQUUsRUFBRSxDQUFDO0FBQ2pDLE1BQU0sa0JBQWtCLEdBQUcsSUFBSSxDQUFDLEVBQUUsRUFBRSxDQUFDO0FBQ3JDLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQztBQUU3QixvQkFBc0IsQ0FBQyxrQkFBa0IsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDO0lBQ2hELElBQUksRUFBRSxVQUFVO0NBQ2pCLENBQUMsQ0FBQyxDQUFDO0FBRUgsZ0NBQTRCLENBQUMsa0JBQWtCLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQztJQUN0RCxJQUFJLEVBQUUsY0FBYztDQUNyQixDQUFDLENBQUMsQ0FBQztBQUVILG9DQUE4QixDQUFDLGtCQUFrQixDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDeEQsSUFBSSxFQUFFLGtCQUFrQjtDQUN6QixDQUFDLENBQUMsQ0FBQztBQUVILHNCQUF1QixDQUFDLGtCQUFrQixDQUFDLEdBQUcs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QUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDbkUsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDLENBQUMsQ0FBQztJQUVILFFBQVEsQ0FBQyxzQkFBc0IsRUFBRSxHQUFHLEVBQUU7UUFDcEMsRUFBRSxDQUFDLDRDQUE0QyxFQUFFLEdBQUcsRUFBRTtZQUNwRCxNQUFNLGFBQWEsR0FBRyxJQUFJLGlDQUFjLENBQUM7Z0JBQ3ZDLE9BQU8sRUFBRSxXQUFXO2dCQUNwQixRQUFRLEVBQUU7b0JBQ1IsVUFBVSxFQUFFLGVBQWU7b0JBQzNCLE1BQU0sRUFBRSxXQUFXO29CQUNuQixPQUFPLEVBQUUsY0FBYztpQkFDeEI7Z0JBQ0QsWUFBWSxFQUFFO29CQUNaLFNBQVMsRUFBRSxjQUFjO29CQUN6QixNQUFNLEVBQUUsV0FBVztvQkFDbkIsT0FBTyxFQUFFLGNBQWM7aUJBQ3hCO2FBQ0YsQ0FBQyxDQUFDO1lBRUgsTUFBTSxDQUFDLGFBQWEsQ0FBQyxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ3RDLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qIGVzbGludC1kaXNhYmxlIGltcG9ydC9uby1leHRyYW5lb3VzLWRlcGVuZGVuY2llcyAqL1xuLy8gTW9jayBBV1MgU0RLXG5qZXN0Lm1vY2soXCJAYXdzLXNkay9jbGllbnQtczNcIik7XG5qZXN0Lm1vY2soXCJAYXdzLXNkay9jbGllbnQtZHluYW1vZGJcIik7XG5qZXN0Lm1vY2soXCJAYXdzLXNkay9jbGllbnQtY2xvdWRmcm9udFwiKTtcbmplc3QubW9jayhcIkBhd3Mtc2RrL2NsaWVudC1zc21cIik7XG5cbmltcG9ydCB7XG4gIENsb3VkRnJvbnRDbGllbnQsXG4gIENyZWF0ZUludmFsaWRhdGlvbkNvbW1hbmQsXG59IGZyb20gXCJAYXdzLXNkay9jbGllbnQtY2xvdWRmcm9udFwiO1xuaW1wb3J0IHtcbiAgRHluYW1vREJDbGllbnQsXG4gIFF1ZXJ5Q29tbWFuZCxcbiAgVXBkYXRlSXRlbUNvbW1hbmQsXG59IGZyb20gXCJAYXdzLXNkay9jbGllbnQtZHluYW1vZGJcIjtcbmltcG9ydCB7XG4gIFMzQ2xpZW50LFxuICBHZXRPYmplY3RDb21tYW5kLFxuICBQdXRPYmplY3RDb21tYW5kLFxuICBEZWxldGVPYmplY3RDb21tYW5kLFxufSBmcm9tIFwiQGF3cy1zZGsvY2xpZW50LXMzXCI7XG5pbXBvcnQgeyBTU01DbGllbnQgfSBmcm9tIFwiQGF3cy1zZGsvY2xpZW50LXNzbVwiO1xuaW1wb3J0IHsgQ2FjaGVIYW5kbGVyQ29udGV4dCB9IGZyb20gXCJuZXh0L2Rpc3Qvc2VydmVyL2xpYi9pbmNyZW1lbnRhbC1jYWNoZVwiO1xuaW1wb3J0IHtcbiAgSW5jcmVtZW50YWxDYWNoZVZhbHVlLFxuICBDYWNoZWRSb3V0ZUtpbmQsXG4gIEluY3JlbWVudGFsQ2FjaGVLaW5kLFxufSBmcm9tIFwibmV4dC9kaXN0L3NlcnZlci9yZXNwb25zZS1jYWNoZVwiO1xuaW1wb3J0IHsgUzNDYWNoZUhhbmRsZXIgfSBmcm9tIFwiLi9zMy1jYWNoZS1oYW5kbGVyXCI7XG5cbmNvbnN0IG1vY2tTM1NlbmQgPSBqZXN0LmZuKCk7XG5jb25zdCBtb2NrRHluYW1vU2VuZCA9IGplc3QuZm4oKTtcbmNvbnN0IG1vY2tDbG91ZEZyb250U2VuZCA9IGplc3QuZm4oKTtcbmNvbnN0IG1vY2tTc21TZW5kID0gamVzdC5mbigpO1xuXG4oUzNDbGllbnQgYXMgamVzdC5Nb2NrKS5tb2NrSW1wbGVtZW50YXRpb24oKCkgPT4gKHtcbiAgc2VuZDogbW9ja1MzU2VuZCxcbn0pKTtcblxuKER5bmFtb0RCQ2xpZW50IGFzIGplc3QuTW9jaykubW9ja0ltcGxlbWVudGF0aW9uKCgpID0+ICh7XG4gIHNlbmQ6IG1vY2tEeW5hbW9TZW5kLFxufSkpO1xuXG4oQ2xvdWRGcm9udENsaWVudCBhcyBqZXN0Lk1vY2spLm1vY2tJbXBsZW1lbnRhdGlvbigoKSA9PiAoe1xuICBzZW5kOiBtb2NrQ2xvdWRGcm9udFNlbmQsXG59KSk7XG5cbihTU01DbGllbnQgYXMgamVzdC5Nb2NrKS5tb2NrSW1wbGVtZW50YXRpb24oKCkgPT4gKHtcbiAgc2VuZDogbW9ja1NzbVNlbmQsXG59KSk7XG5cbmRlc2NyaWJlKFwiUzNEeW5hbW9DYWNoZUhhbmRsZXJcIiwgKCkgPT4ge1xuICBsZXQgaGFuZGxlcjogUzNDYWNoZUhhbmRsZXI7XG4gIGxldCBtb2NrQ29udGV4dDogQ2FjaGVIYW5kbGVyQ29udGV4dDtcblxuICAvLyBIZWxwZXIgdG8gY3JlYXRlIHNldCBjb250ZXh0IHdpdGggdGFnc1xuICBjb25zdCBjcmVhdGVTZXRDb250ZXh0ID0gKHRhZ3M6IHN0cmluZ1tdKSA9PiAoe1xuICAgIGZldGNoQ2FjaGU6IHRydWUgYXMgY29uc3QsXG4gICAgdGFncyxcbiAgfSk7XG5cbiAgYmVmb3JlRWFjaCgoKSA9PiB7XG4gICAgbW9ja0NvbnRleHQgPSB7IGRldjogZmFsc2UgfSBhcyBDYWNoZUhhbmRsZXJDb250ZXh0O1xuXG4gICAgLy8gU2V0IHVwIGVudmlyb25tZW50IHZhcmlhYmxlc1xuICAgIHByb2Nlc3MuZW52LkNES19ORVhUSlNfQ0FDSEVfQlVDS0VUX05BTUUgPSBcInRlc3QtYnVja2V0XCI7XG4gICAgcHJvY2Vzcy5lbnYuQ0RLX05FWFRKU19SRVZBTElEQVRJT05fVEFCTEVfTkFNRSA9IFwidGVzdC10YWJsZVwiO1xuICAgIHByb2Nlc3MuZW52LkNES19ORVhUSlNfQlVJTERfSUQgPSBcInRlc3QtYnVpbGQtaWRcIjtcbiAgICBwcm9jZXNzLmVudi5BV1NfUkVHSU9OID0gXCJ1cy1lYXN0LTFcIjtcblxuICAgIC8vIFN1cHByZXNzIGV4cGVjdGVkIHdhcm5pbmdzIGdsb2JhbGx5ICh0ZXN0cyBkZWxpYmVyYXRlbHkgdHJpZ2dlciB3YXJuaW5ncylcbiAgICBqZXN0LnNweU9uKGNvbnNvbGUsIFwid2FyblwiKS5tb2NrSW1wbGVtZW50YXRpb24oKTtcblxuICAgIGhhbmRsZXIgPSBuZXcgUzNDYWNoZUhhbmRsZXIoe1xuICAgICAgY29udGV4dDogbW9ja0NvbnRleHQsXG4gICAgfSk7XG5cbiAgICAvLyBSZXNldCBtb2Nrc1xuICAgIG1vY2tTM1NlbmQubW9ja1Jlc2V0KCk7XG4gICAgbW9ja0R5bmFtb1NlbmQubW9ja1Jlc2V0KCk7XG4gICAgbW9ja0Nsb3VkRnJvbnRTZW5kLm1vY2tSZXNldCgpO1xuICAgIG1vY2tTc21TZW5kLm1vY2tSZXNldCgpO1xuICB9KTtcblxuICBhZnRlckVhY2goKCkgPT4ge1xuICAgIC8vIENsZWFuIHVwIGVudmlyb25tZW50IHZhcmlhYmxlc1xuICAgIGRlbGV0ZSBwcm9jZXNzLmVudi5DREtfTkVYVEpTX0NBQ0hFX0JVQ0tFVF9OQU1FO1xuICAgIGRlbGV0ZSBwcm9jZXNzLmVudi5DREtfTkVYVEpTX1JFVkFMSURBVElPTl9UQUJMRV9OQU1FO1xuICAgIGRlbGV0ZSBwcm9jZXNzLmVudi5DREtfTkVYVEpTX0JVSUxEX0lEO1xuICAgIGRlbGV0ZSBwcm9jZXNzLmVudi5BV1NfUkVHSU9OO1xuICAgIGRlbGV0ZSBwcm9jZXNzLmVudi5DREtfTkVYVEpTX0RJU1RSSUJVVElPTl9JRF9QQVJBTV9OQU1FO1xuXG4gICAgLy8gUmVzdG9yZSBjb25zb2xlLndhcm5cbiAgICBqZXN0LnJlc3RvcmVBbGxNb2NrcygpO1xuICB9KTtcblxuICBkZXNjcmliZShcImdldFwiLCAoKSA9PiB7XG4gICAgaXQoXCJzaG91bGQgcmV0dXJuIG51bGwgd2hlbiBTMyBidWNrZXQgaXMgbm90IGNvbmZpZ3VyZWRcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgZGVsZXRlIHByb2Nlc3MuZW52LkNES19ORVhUSlNfQ0FDSEVfQlVDS0VUX05BTUU7XG5cbiAgICAgIGNvbnN0IGhhbmRsZXJXaXRob3V0QnVja2V0ID0gbmV3IFMzQ2FjaGVIYW5kbGVyKHtcbiAgICAgICAgY29udGV4dDogbW9ja0NvbnRleHQsXG4gICAgICB9KTtcblxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgaGFuZGxlcldpdGhvdXRCdWNrZXQuZ2V0KFwidGVzdC1rZXlcIiwge1xuICAgICAgICBraW5kOiBJbmNyZW1lbnRhbENhY2hlS2luZC5BUFBfUEFHRSxcbiAgICAgICAgaXNGYWxsYmFjazogZmFsc2UsXG4gICAgICB9KTtcbiAgICAgIGV4cGVjdChyZXN1bHQpLnRvQmVOdWxsKCk7XG4gICAgfSk7XG5cbiAgICBpdChcInNob3VsZCByZXRyaWV2ZSBhbmQgcGFyc2UgY2FjaGUgdmFsdWUgZnJvbSBTM1wiLCBhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCB0ZXN0RGF0YTogSW5jcmVtZW50YWxDYWNoZVZhbHVlID0ge1xuICAgICAgICBraW5kOiBDYWNoZWRSb3V0ZUtpbmQuQVBQX1BBR0UsXG4gICAgICAgIGh0bWw6IFwiPGh0bWw+dGVzdDwvaHRtbD5cIixcbiAgICAgICAgcnNjRGF0YTogdW5kZWZpbmVkLFxuICAgICAgICBoZWFkZXJzOiB1bmRlZmluZWQsXG4gICAgICAgIHBvc3Rwb25lZDogdW5kZWZpbmVkLFxuICAgICAgICBzZWdtZW50RGF0YTogdW5kZWZpbmVkLFxuICAgICAgICBzdGF0dXM6IHVuZGVmaW5lZCxcbiAgICAgIH07XG5cbiAgICAgIGNvbnN0IGNhY2hlVmFsdWUgPSB7XG4gICAgICAgIGxhc3RNb2RpZmllZDogRGF0ZS5ub3coKSxcbiAgICAgICAgdmFsdWU6IHRlc3REYXRhLFxuICAgICAgfTtcblxuICAgICAgbW9ja1MzU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZU9uY2Uoe1xuICAgICAgICBCb2R5OiB7XG4gICAgICAgICAgdHJhbnNmb3JtVG9TdHJpbmc6IGplc3RcbiAgICAgICAgICAgIC5mbigpXG4gICAgICAgICAgICAubW9ja1Jlc29sdmVkVmFsdWUoSlNPTi5zdHJpbmdpZnkoY2FjaGVWYWx1ZSkpLFxuICAgICAgICB9LFxuICAgICAgICBDb250ZW50VHlwZTogXCJhcHBsaWNhdGlvbi9qc29uXCIsXG4gICAgICB9KTtcblxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgaGFuZGxlci5nZXQoXCJ0ZXN0LWtleVwiLCB7XG4gICAgICAgIGtpbmQ6IEluY3JlbWVudGFsQ2FjaGVLaW5kLkFQUF9QQUdFLFxuICAgICAgICBpc0ZhbGxiYWNrOiBmYWxzZSxcbiAgICAgIH0pO1xuXG4gICAgICBleHBlY3QocmVzdWx0KS50b0VxdWFsKGNhY2hlVmFsdWUpO1xuICAgICAgZXhwZWN0KG1vY2tTM1NlbmQpLnRvSGF2ZUJlZW5DYWxsZWRXaXRoKGV4cGVjdC5hbnkoR2V0T2JqZWN0Q29tbWFuZCkpO1xuICAgIH0pO1xuXG4gICAgaXQoXCJzaG91bGQgcmV0dXJuIG51bGwgd2hlbiBTMyBvYmplY3Qgbm90IGZvdW5kXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIG1vY2tTM1NlbmQubW9ja1Jlc29sdmVkVmFsdWVPbmNlKHtcbiAgICAgICAgQm9keTogbnVsbCxcbiAgICAgIH0pO1xuXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBoYW5kbGVyLmdldChcIm1pc3Npbmcta2V5XCIsIHtcbiAgICAgICAga2luZDogSW5jcmVtZW50YWxDYWNoZUtpbmQuQVBQX1BBR0UsXG4gICAgICAgIGlzRmFsbGJhY2s6IGZhbHNlLFxuICAgICAgfSk7XG4gICAgICBleHBlY3QocmVzdWx0KS50b0JlTnVsbCgpO1xuICAgIH0pO1xuXG4gICAgaXQoXCJzaG91bGQgaGFuZGxlIFMzIGVycm9ycyBhbmQgcmV0dXJuIG51bGxcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgbW9ja1MzU2VuZC5tb2NrUmVqZWN0ZWRWYWx1ZU9uY2UobmV3IEVycm9yKFwiUzMgRXJyb3JcIikpO1xuXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBoYW5kbGVyLmdldChcImVycm9yLWtleVwiLCB7XG4gICAgICAgIGtpbmQ6IEluY3JlbWVudGFsQ2FjaGVLaW5kLkFQUF9QQUdFLFxuICAgICAgICBpc0ZhbGxiYWNrOiBmYWxzZSxcbiAgICAgIH0pO1xuICAgICAgZXhwZWN0KHJlc3VsdCkudG9CZU51bGwoKTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoXCJzZXRcIiwgKCkgPT4ge1xuICAgIGl0KFwic2hvdWxkIHN0b3JlIGNhY2hlIHZhbHVlIGluIFMzXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIGNvbnN0IHRlc3REYXRhOiBJbmNyZW1lbnRhbENhY2hlVmFsdWUgPSB7XG4gICAgICAgIGtpbmQ6IENhY2hlZFJvdXRlS2luZC5BUFBfUEFHRSxcbiAgICAgICAgaHRtbDogXCI8aHRtbD5zZXQgdGVzdDwvaHRtbD5cIixcbiAgICAgICAgcnNjRGF0YTogdW5kZWZpbmVkLFxuICAgICAgICBoZWFkZXJzOiB1bmRlZmluZWQsXG4gICAgICAgIHBvc3Rwb25lZDogdW5kZWZpbmVkLFxuICAgICAgICBzZWdtZW50RGF0YTogdW5kZWZpbmVkLFxuICAgICAgICBzdGF0dXM6IHVuZGVmaW5lZCxcbiAgICAgIH07XG5cbiAgICAgIG1vY2tTM1NlbmQubW9ja1Jlc29sdmVkVmFsdWVPbmNlKHt9KTtcbiAgICAgIG1vY2tEeW5hbW9TZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHt9KTtcblxuICAgICAgYXdhaXQgaGFuZGxlci5zZXQoXCJzZXQta2V5XCIsIHRlc3REYXRhLCBjcmVhdGVTZXRDb250ZXh0KFtcInRhZzFcIl0pKTtcblxuICAgICAgZXhwZWN0KG1vY2tTM1NlbmQpLnRvSGF2ZUJlZW5DYWxsZWRXaXRoKGV4cGVjdC5hbnkoUHV0T2JqZWN0Q29tbWFuZCkpO1xuICAgICAgZXhwZWN0KG1vY2tEeW5hbW9TZW5kKS50b0hhdmVCZWVuQ2FsbGVkV2l0aChcbiAgICAgICAgZXhwZWN0LmFueShVcGRhdGVJdGVtQ29tbWFuZCksXG4gICAgICApO1xuICAgIH0pO1xuXG4gICAgaXQoXCJzaG91bGQgbm90IHN0b3JlIHdoZW4gUzMgYnVja2V0IGlzIG5vdCBjb25maWd1cmVkXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIGRlbGV0ZSBwcm9jZXNzLmVudi5DREtfTkVYVEpTX0NBQ0hFX0JVQ0tFVF9OQU1FO1xuXG4gICAgICBjb25zdCBoYW5kbGVyV2l0aG91dEJ1Y2tldCA9IG5ldyBTM0NhY2hlSGFuZGxlcih7XG4gICAgICAgIGNvbnRleHQ6IG1vY2tDb250ZXh0LFxuICAgICAgfSk7XG5cbiAgICAgIGNvbnN0IHRlc3REYXRhOiBJbmNyZW1lbnRhbENhY2hlVmFsdWUgPSB7XG4gICAgICAgIGtpbmQ6IENhY2hlZFJvdXRlS2luZC5BUFBfUEFHRSxcbiAgICAgICAgaHRtbDogXCI8aHRtbD5ubyBidWNrZXQ8L2h0bWw+XCIsXG4gICAgICAgIHJzY0RhdGE6IHVuZGVmaW5lZCxcbiAgICAgICAgaGVhZGVyczogdW5kZWZpbmVkLFxuICAgICAgICBwb3N0cG9uZWQ6IHVuZGVmaW5lZCxcbiAgICAgICAgc2VnbWVudERhdGE6IHVuZGVmaW5lZCxcbiAgICAgICAgc3RhdHVzOiB1bmRlZmluZWQsXG4gICAgICB9O1xuXG4gICAgICBhd2FpdCBoYW5kbGVyV2l0aG91dEJ1Y2tldC5zZXQoXG4gICAgICAgIFwibm8tYnVja2V0LWtleVwiLFxuICAgICAgICB0ZXN0RGF0YSxcbiAgICAgICAgY3JlYXRlU2V0Q29udGV4dChbXSksXG4gICAgICApO1xuXG4gICAgICBleHBlY3QobW9ja1MzU2VuZCkubm90LnRvSGF2ZUJlZW5DYWxsZWQoKTtcbiAgICB9KTtcblxuICAgIGl0KFwic2hvdWxkIGhhbmRsZSBTMyBlcnJvcnMgZ3JhY2VmdWxseVwiLCBhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCB0ZXN0RGF0YTogSW5jcmVtZW50YWxDYWNoZVZhbHVlID0ge1xuICAgICAgICBraW5kOiBDYWNoZWRSb3V0ZUtpbmQuQVBQX1BBR0UsXG4gICAgICAgIGh0bWw6IFwiPGh0bWw+ZXJyb3IgdGVzdDwvaHRtbD5cIixcbiAgICAgICAgcnNjRGF0YTogdW5kZWZpbmVkLFxuICAgICAgICBoZWFkZXJzOiB1bmRlZmluZWQsXG4gICAgICAgIHBvc3Rwb25lZDogdW5kZWZpbmVkLFxuICAgICAgICBzZWdtZW50RGF0YTogdW5kZWZpbmVkLFxuICAgICAgICBzdGF0dXM6IHVuZGVmaW5lZCxcbiAgICAgIH07XG5cbiAgICAgIG1vY2tTM1NlbmQubW9ja1JlamVjdGVkVmFsdWVPbmNlKG5ldyBFcnJvcihcIlMzIFB1dCBFcnJvclwiKSk7XG5cbiAgICAgIC8vIFNob3VsZCBub3QgdGhyb3dcbiAgICAgIGF3YWl0IGV4cGVjdChcbiAgICAgICAgaGFuZGxlci5zZXQoXCJlcnJvci1rZXlcIiwgdGVzdERhdGEsIGNyZWF0ZVNldENvbnRleHQoW10pKSxcbiAgICAgICkucmVzb2x2ZXMubm90LnRvVGhyb3coKTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoXCJyZXZhbGlkYXRlVGFnXCIsICgpID0+IHtcbiAgICBpdChcInNob3VsZCBxdWVyeSBEeW5hbW9EQiBhbmQgZGVsZXRlIFMzIGVudHJpZXNcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgY29uc3QgbW9ja1F1ZXJ5UmVzcG9uc2UgPSB7XG4gICAgICAgIEl0ZW1zOiBbXG4gICAgICAgICAgeyBzazogeyBTOiBcInRlc3QtdGFnI2NhY2hlLWtleS0xXCIgfSB9LFxuICAgICAgICAgIHsgc2s6IHsgUzogXCJ0ZXN0LXRhZyNjYWNoZS1rZXktMlwiIH0gfSxcbiAgICAgICAgXSxcbiAgICAgIH07XG5cbiAgICAgIG1vY2tEeW5hbW9TZW5kLm1vY2tSZXNvbHZlZFZhbHVlT25jZShtb2NrUXVlcnlSZXNwb25zZSk7XG4gICAgICBtb2NrRHluYW1vU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7fSk7IC8vIEZvciB1cGRhdGUgY29tbWFuZHNcbiAgICAgIG1vY2tTM1NlbmQubW9ja1Jlc29sdmVkVmFsdWUoe30pOyAvLyBGb3IgZGVsZXRlIGNvbW1hbmRzXG5cbiAgICAgIGF3YWl0IGhhbmRsZXIucmV2YWxpZGF0ZVRhZyhcInRlc3QtdGFnXCIpO1xuXG4gICAgICBleHBlY3QobW9ja0R5bmFtb1NlbmQpLnRvSGF2ZUJlZW5DYWxsZWRXaXRoKGV4cGVjdC5hbnkoUXVlcnlDb21tYW5kKSk7XG4gICAgICBleHBlY3QobW9ja0R5bmFtb1NlbmQpLnRvSGF2ZUJlZW5DYWxsZWRXaXRoKFxuICAgICAgICBleHBlY3QuYW55KFVwZGF0ZUl0ZW1Db21tYW5kKSxcbiAgICAgICk7XG4gICAgICBleHBlY3QobW9ja1MzU2VuZCkudG9IYXZlQmVlbkNhbGxlZFdpdGgoZXhwZWN0LmFueShEZWxldGVPYmplY3RDb21tYW5kKSk7XG4gICAgfSk7XG5cbiAgICBpdChcInNob3VsZCBub3QgcmV2YWxpZGF0ZSB3aGVuIER5bmFtb0RCIHRhYmxlIGlzIG5vdCBjb25maWd1cmVkXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIGRlbGV0ZSBwcm9jZXNzLmVudi5DREtfTkVYVEpTX1JFVkFMSURBVElPTl9UQUJMRV9OQU1FO1xuXG4gICAgICBjb25zdCBoYW5kbGVyV2l0aG91dFRhYmxlID0gbmV3IFMzQ2FjaGVIYW5kbGVyKHtcbiAgICAgICAgY29udGV4dDogbW9ja0NvbnRleHQsXG4gICAgICB9KTtcblxuICAgICAgYXdhaXQgaGFuZGxlcldpdGhvdXRUYWJsZS5yZXZhbGlkYXRlVGFnKFwidGVzdC10YWdcIik7XG5cbiAgICAgIGV4cGVjdChtb2NrRHluYW1vU2VuZCkubm90LnRvSGF2ZUJlZW5DYWxsZWQoKTtcbiAgICB9KTtcblxuICAgIGl0KFwic2hvdWxkIGhhbmRsZSBEeW5hbW9EQiBlcnJvcnMgZ3JhY2VmdWxseVwiLCBhc3luYyAoKSA9PiB7XG4gICAgICBtb2NrRHluYW1vU2VuZC5tb2NrUmVqZWN0ZWRWYWx1ZU9uY2UobmV3IEVycm9yKFwiRHluYW1vREIgRXJyb3JcIikpO1xuXG4gICAgICAvLyBTaG91bGQgbm90IHRocm93XG4gICAgICBhd2FpdCBleHBlY3QoaGFuZGxlci5yZXZhbGlkYXRlVGFnKFwiZXJyb3ItdGFnXCIpKS5yZXNvbHZlcy5ub3QudG9UaHJvdygpO1xuICAgIH0pO1xuXG4gICAgaXQoXCJzaG91bGQgY3JlYXRlIGEgQ2xvdWRGcm9udCBpbnZhbGlkYXRpb24gZm9yIGFmZmVjdGVkIHBhdGhzIHdoZW4gYSBkaXN0cmlidXRpb24gcGFyYW1ldGVyIGlzIGNvbmZpZ3VyZWRcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgcHJvY2Vzcy5lbnYuQ0RLX05FWFRKU19ESVNUUklCVVRJT05fSURfUEFSQU1fTkFNRSA9IFwidGVzdC1wYXJhbS1uYW1lXCI7XG4gICAgICBjb25zdCBoYW5kbGVyV2l0aERpc3RyaWJ1dGlvbiA9IG5ldyBTM0NhY2hlSGFuZGxlcih7XG4gICAgICAgIGNvbnRleHQ6IG1vY2tDb250ZXh0LFxuICAgICAgfSk7XG5cbiAgICAgIGNvbnN0IG1vY2tRdWVyeVJlc3BvbnNlID0ge1xuICAgICAgICBJdGVtczogW1xuICAgICAgICAgIHsgc2s6IHsgUzogXCJ0ZXN0LXRhZyN0ZXN0LWJ1aWxkLWlkL2lzci8xLmpzb25cIiB9IH0sXG4gICAgICAgICAgeyBzazogeyBTOiBcInRlc3QtdGFnI3Rlc3QtYnVpbGQtaWQvaXNyLzIuanNvblwiIH0gfSxcbiAgICAgICAgXSxcbiAgICAgIH07XG5cbiAgICAgIG1vY2tEeW5hbW9TZW5kLm1vY2tSZXNvbHZlZFZhbHVlT25jZShtb2NrUXVlcnlSZXNwb25zZSk7XG4gICAgICBtb2NrRHluYW1vU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7fSk7IC8vIEZvciB1cGRhdGUgY29tbWFuZHNcbiAgICAgIG1vY2tTM1NlbmQubW9ja1Jlc29sdmVkVmFsdWUoe30pOyAvLyBGb3IgZGVsZXRlIGNvbW1hbmRzXG4gICAgICBtb2NrU3NtU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7XG4gICAgICAgIFBhcmFtZXRlcjogeyBWYWx1ZTogXCJ0ZXN0LWRpc3RyaWJ1dGlvbi1pZFwiIH0sXG4gICAgICB9KTtcbiAgICAgIG1vY2tDbG91ZEZyb250U2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7fSk7XG5cbiAgICAgIGF3YWl0IGhhbmRsZXJXaXRoRGlzdHJpYnV0aW9uLnJldmFsaWRhdGVUYWcoXCJ0ZXN0LXRhZ1wiKTtcblxuICAgICAgZXhwZWN0KG1vY2tTc21TZW5kKS50b0hhdmVCZWVuQ2FsbGVkV2l0aChleHBlY3QuYW55KE9iamVjdCkpO1xuICAgICAgZXhwZWN0KG1vY2tDbG91ZEZyb250U2VuZCkudG9IYXZlQmVlbkNhbGxlZFdpdGgoXG4gICAgICAgIGV4cGVjdC5hbnkoQ3JlYXRlSW52YWxpZGF0aW9uQ29tbWFuZCksXG4gICAgICApO1xuICAgICAgY29uc3QgW2ludmFsaWRhdGlvbklucHV0XSA9IChcbiAgICAgICAgQ3JlYXRlSW52YWxpZGF0aW9uQ29tbWFuZCBhcyB1bmtub3duIGFzIGplc3QuTW9ja1xuICAgICAgKS5tb2NrLmNhbGxzWzBdO1xuICAgICAgZXhwZWN0KGludmFsaWRhdGlvbklucHV0LkRpc3RyaWJ1dGlvbklkKS50b0JlKFwidGVzdC1kaXN0cmlidXRpb24taWRcIik7XG4gICAgICBleHBlY3QoaW52YWxpZGF0aW9uSW5wdXQuSW52YWxpZGF0aW9uQmF0Y2guUGF0aHMuSXRlbXMpLnRvRXF1YWwoXG4gICAgICAgIGV4cGVjdC5hcnJheUNvbnRhaW5pbmcoW1wiL2lzci8xXCIsIFwiL2lzci8yXCJdKSxcbiAgICAgICk7XG4gICAgfSk7XG5cbiAgICBpdChcInNob3VsZCBub3QgY2FsbCBTU00gb3IgQ2xvdWRGcm9udCB3aGVuIG5vIGRpc3RyaWJ1dGlvbiBwYXJhbWV0ZXIgaXMgY29uZmlndXJlZFwiLCBhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBtb2NrUXVlcnlSZXNwb25zZSA9IHtcbiAgICAgICAgSXRlbXM6IFt7IHNrOiB7IFM6IFwidGVzdC10YWcjdGVzdC1idWlsZC1pZC9pc3IvMS5qc29uXCIgfSB9XSxcbiAgICAgIH07XG5cbiAgICAgIG1vY2tEeW5hbW9TZW5kLm1vY2tSZXNvbHZlZFZhbHVlT25jZShtb2NrUXVlcnlSZXNwb25zZSk7XG4gICAgICBtb2NrRHluYW1vU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7fSk7XG4gICAgICBtb2NrUzNTZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHt9KTtcblxuICAgICAgYXdhaXQgaGFuZGxlci5yZXZhbGlkYXRlVGFnKFwidGVzdC10YWdcIik7XG5cbiAgICAgIGV4cGVjdChtb2NrU3NtU2VuZCkubm90LnRvSGF2ZUJlZW5DYWxsZWQoKTtcbiAgICAgIGV4cGVjdChtb2NrQ2xvdWRGcm9udFNlbmQpLm5vdC50b0hhdmVCZWVuQ2FsbGVkKCk7XG4gICAgfSk7XG5cbiAgICBpdChcInNob3VsZCBoYW5kbGUgU1NNL0Nsb3VkRnJvbnQgaW52YWxpZGF0aW9uIGVycm9ycyBncmFjZWZ1bGx5XCIsIGFzeW5jICgpID0+IHtcbiAgICAgIHByb2Nlc3MuZW52LkNES19ORVhUSlNfRElTVFJJQlVUSU9OX0lEX1BBUkFNX05BTUUgPSBcInRlc3QtcGFyYW0tbmFtZVwiO1xuICAgICAgY29uc3QgaGFuZGxlcldpdGhEaXN0cmlidXRpb24gPSBuZXcgUzNDYWNoZUhhbmRsZXIoe1xuICAgICAgICBjb250ZXh0OiBtb2NrQ29udGV4dCxcbiAgICAgIH0pO1xuXG4gICAgICBjb25zdCBtb2NrUXVlcnlSZXNwb25zZSA9IHtcbiAgICAgICAgSXRlbXM6IFt7IHNrOiB7IFM6IFwidGVzdC10YWcjdGVzdC1idWlsZC1pZC9pc3IvMS5qc29uXCIgfSB9XSxcbiAgICAgIH07XG5cbiAgICAgIG1vY2tEeW5hbW9TZW5kLm1vY2tSZXNvbHZlZFZhbHVlT25jZShtb2NrUXVlcnlSZXNwb25zZSk7XG4gICAgICBtb2NrRHluYW1vU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7fSk7XG4gICAgICBtb2NrUzNTZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHt9KTtcbiAgICAgIG1vY2tTc21TZW5kLm1vY2tSZWplY3RlZFZhbHVlT25jZShuZXcgRXJyb3IoXCJTU00gRXJyb3JcIikpO1xuXG4gICAgICBhd2FpdCBleHBlY3QoXG4gICAgICAgIGhhbmRsZXJXaXRoRGlzdHJpYnV0aW9uLnJldmFsaWRhdGVUYWcoXCJ0ZXN0LXRhZ1wiKSxcbiAgICAgICkucmVzb2x2ZXMubm90LnRvVGhyb3coKTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoXCJyZXNldFJlcXVlc3RDYWNoZVwiLCAoKSA9PiB7XG4gICAgaXQoXCJzaG91bGQgY29tcGxldGUgd2l0aG91dCBlcnJvcnNcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgYXdhaXQgZXhwZWN0KGhhbmRsZXIucmVzZXRSZXF1ZXN0Q2FjaGUoKSkucmVzb2x2ZXMubm90LnRvVGhyb3coKTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoXCJjdXN0b20gY29uZmlndXJhdGlvblwiLCAoKSA9PiB7XG4gICAgaXQoXCJzaG91bGQgYWNjZXB0IGN1c3RvbSBjb25maWd1cmF0aW9uIG9wdGlvbnNcIiwgKCkgPT4ge1xuICAgICAgY29uc3QgY3VzdG9tSGFuZGxlciA9IG5ldyBTM0NhY2hlSGFuZGxlcih7XG4gICAgICAgIGNvbnRleHQ6IG1vY2tDb250ZXh0LFxuICAgICAgICBzM0NvbmZpZzoge1xuICAgICAgICAgIGJ1Y2tldE5hbWU6IFwiY3VzdG9tLWJ1Y2tldFwiLFxuICAgICAgICAgIHJlZ2lvbjogXCJldS13ZXN0LTFcIixcbiAgICAgICAgICBidWlsZElkOiBcImN1c3RvbS1idWlsZFwiLFxuICAgICAgICB9LFxuICAgICAgICBkeW5hbW9Db25maWc6IHtcbiAgICAgICAgICB0YWJsZU5hbWU6IFwiY3VzdG9tLXRhYmxlXCIsXG4gICAgICAgICAgcmVnaW9uOiBcImV1LXdlc3QtMVwiLFxuICAgICAgICAgIGJ1aWxkSWQ6IFwiY3VzdG9tLWJ1aWxkXCIsXG4gICAgICAgIH0sXG4gICAgICB9KTtcblxuICAgICAgZXhwZWN0KGN1c3RvbUhhbmRsZXIpLnRvQmVEZWZpbmVkKCk7XG4gICAgfSk7XG4gIH0pO1xufSk7XG4iXX0=
|
|
1434
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiczMtY2FjaGUtaGFuZGxlci50ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FkYXB0ZXIvczMtY2FjaGUtaGFuZGxlci50ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsc0RBQXNEO0FBQ3RELHFDQUE2RDtBQUM3RCxxQ0FBaUM7QUFDakMseUNBQWlDO0FBQ2pDLCtFQUErRTtBQUMvRSw0RUFBNEU7QUFDNUUsZ0VBQWdFO0FBQ2hFLE1BQU0sZUFBZSxHQUE0QixFQUFFLENBQUM7QUFDcEQsSUFBSSxDQUFDLElBQUksQ0FBQyxhQUFhLEVBQUUsR0FBRyxFQUFFLENBQUMsQ0FBQztJQUM5QixHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsYUFBYSxDQUFDO0lBQ3BDLGFBQWEsRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDLEVBQUUsS0FBSyxFQUFFLGVBQWUsRUFBRSxDQUFDO0NBQ2xELENBQUMsQ0FBQyxDQUFDO0FBQ0osZUFBZTtBQUNmLElBQUksQ0FBQyxJQUFJLENBQUMsb0JBQW9CLENBQUMsQ0FBQztBQUNoQyxJQUFJLENBQUMsSUFBSSxDQUFDLDBCQUEwQixDQUFDLENBQUM7QUFDdEMsSUFBSSxDQUFDLElBQUksQ0FBQyw0QkFBNEIsQ0FBQyxDQUFDO0FBQ3hDLElBQUksQ0FBQyxJQUFJLENBQUMscUJBQXFCLENBQUMsQ0FBQztBQUVqQyxrRUFHb0M7QUFDcEMsOERBT2tDO0FBQ2xDLGtEQUs0QjtBQUM1QixvREFBZ0Q7QUFPaEQseURBQW9EO0FBQ3BELHlEQUF1RDtBQUN2RCxrREFBNEQ7QUFFNUQsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLEVBQUUsRUFBRSxDQUFDO0FBQzdCLE1BQU0sY0FBYyxHQUFHLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQztBQUNqQyxNQUFNLGtCQUFrQixHQUFHLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQztBQUNyQyxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsRUFBRSxF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LENBQUMsQ0FBQyxDQUFDO1lBQzNELE1BQU0sQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDLENBQUMsT0FBTyxDQUFDO2dCQUNqQyxRQUFRO2dCQUNSLHlDQUF5QztnQkFDekMsV0FBVzthQUNaLENBQUMsQ0FBQztRQUNMLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQyxDQUFDLENBQUM7SUFFSCxRQUFRLENBQUMsbUJBQW1CLEVBQUUsR0FBRyxFQUFFO1FBQ2pDLEVBQUUsQ0FBQyxnQ0FBZ0MsRUFBRSxLQUFLLElBQUksRUFBRTtZQUM5QyxNQUFNLE1BQU0sQ0FBQyxPQUFPLENBQUMsaUJBQWlCLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDbkUsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyogZXNsaW50LWRpc2FibGUgaW1wb3J0L25vLWV4dHJhbmVvdXMtZGVwZW5kZW5jaWVzICovXG5pbXBvcnQgeyBta2R0ZW1wU3luYywgcm1TeW5jLCB3cml0ZUZpbGVTeW5jIH0gZnJvbSBcIm5vZGU6ZnNcIjtcbmltcG9ydCB7IHRtcGRpciB9IGZyb20gXCJub2RlOm9zXCI7XG5pbXBvcnQgeyBqb2luIH0gZnJvbSBcIm5vZGU6cGF0aFwiO1xuLy8gV2hhdCBgcmVxdWlyZS5jYWNoZWAgaG9sZHMgaW4gYSBydW5uaW5nIHNlcnZlci4gSmVzdCdzIGBjcmVhdGVSZXF1aXJlYCBoYW5kc1xuLy8gb3V0IGEgY2FjaGUgb2YgaXRzIG93biB0aGF0IG5vdGhpbmcgaXMgZXZlciBsb2FkZWQgaW50bywgc28gdGhlIGhhbmRsZXInc1xuLy8gbG9va3VwIG9mIE5leHQuanMncyB0YWcgbWFuaWZlc3QgcmVhZHMgdGhpcyBzdGFuZC1pbiBpbnN0ZWFkLlxuY29uc3QgbW9ja01vZHVsZUNhY2hlOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHt9O1xuamVzdC5tb2NrKFwibm9kZTptb2R1bGVcIiwgKCkgPT4gKHtcbiAgLi4uamVzdC5yZXF1aXJlQWN0dWFsKFwibm9kZTptb2R1bGVcIiksXG4gIGNyZWF0ZVJlcXVpcmU6ICgpID0+ICh7IGNhY2hlOiBtb2NrTW9kdWxlQ2FjaGUgfSksXG59KSk7XG4vLyBNb2NrIEFXUyBTREtcbmplc3QubW9jayhcIkBhd3Mtc2RrL2NsaWVudC1zM1wiKTtcbmplc3QubW9jayhcIkBhd3Mtc2RrL2NsaWVudC1keW5hbW9kYlwiKTtcbmplc3QubW9jayhcIkBhd3Mtc2RrL2NsaWVudC1jbG91ZGZyb250XCIpO1xuamVzdC5tb2NrKFwiQGF3cy1zZGsvY2xpZW50LXNzbVwiKTtcblxuaW1wb3J0IHtcbiAgQ2xvdWRGcm9udENsaWVudCxcbiAgQ3JlYXRlSW52YWxpZGF0aW9uQ29tbWFuZCxcbn0gZnJvbSBcIkBhd3Mtc2RrL2NsaWVudC1jbG91ZGZyb250XCI7XG5pbXBvcnQge1xuICBCYXRjaEdldEl0ZW1Db21tYW5kLFxuICBEZWxldGVJdGVtQ29tbWFuZCxcbiAgRHluYW1vREJDbGllbnQsXG4gIFB1dEl0ZW1Db21tYW5kLFxuICBRdWVyeUNvbW1hbmQsXG4gIFVwZGF0ZUl0ZW1Db21tYW5kLFxufSBmcm9tIFwiQGF3cy1zZGsvY2xpZW50LWR5bmFtb2RiXCI7XG5pbXBvcnQge1xuICBTM0NsaWVudCxcbiAgR2V0T2JqZWN0Q29tbWFuZCxcbiAgUHV0T2JqZWN0Q29tbWFuZCxcbiAgRGVsZXRlT2JqZWN0Q29tbWFuZCxcbn0gZnJvbSBcIkBhd3Mtc2RrL2NsaWVudC1zM1wiO1xuaW1wb3J0IHsgU1NNQ2xpZW50IH0gZnJvbSBcIkBhd3Mtc2RrL2NsaWVudC1zc21cIjtcbmltcG9ydCB7IENhY2hlSGFuZGxlckNvbnRleHQgfSBmcm9tIFwibmV4dC9kaXN0L3NlcnZlci9saWIvaW5jcmVtZW50YWwtY2FjaGVcIjtcbmltcG9ydCB7XG4gIEluY3JlbWVudGFsQ2FjaGVWYWx1ZSxcbiAgQ2FjaGVkUm91dGVLaW5kLFxuICBJbmNyZW1lbnRhbENhY2hlS2luZCxcbn0gZnJvbSBcIm5leHQvZGlzdC9zZXJ2ZXIvcmVzcG9uc2UtY2FjaGVcIjtcbmltcG9ydCB7IFMzQ2FjaGVIYW5kbGVyIH0gZnJvbSBcIi4vczMtY2FjaGUtaGFuZGxlclwiO1xuaW1wb3J0IHsgc2hhcmVkVGFnTWFuaWZlc3QgfSBmcm9tIFwiLi91c2UtY2FjaGUtY29tbW9uXCI7XG5pbXBvcnQgeyBSRVZBTElEQVRFRF9QQUdFX0hPT0sgfSBmcm9tIFwiLi4vcnVudGltZS9tYW5pZmVzdFwiO1xuXG5jb25zdCBtb2NrUzNTZW5kID0gamVzdC5mbigpO1xuY29uc3QgbW9ja0R5bmFtb1NlbmQgPSBqZXN0LmZuKCk7XG5jb25zdCBtb2NrQ2xvdWRGcm9udFNlbmQgPSBqZXN0LmZuKCk7XG5jb25zdCBtb2NrU3NtU2VuZCA9IGplc3QuZm4oKTtcblxuKFMzQ2xpZW50IGFzIGplc3QuTW9jaykubW9ja0ltcGxlbWVudGF0aW9uKCgpID0+ICh7XG4gIHNlbmQ6IG1vY2tTM1NlbmQsXG59KSk7XG5cbihEeW5hbW9EQkNsaWVudCBhcyBqZXN0Lk1vY2spLm1vY2tJbXBsZW1lbnRhdGlvbigoKSA9PiAoe1xuICBzZW5kOiBtb2NrRHluYW1vU2VuZCxcbn0pKTtcblxuKENsb3VkRnJvbnRDbGllbnQgYXMgamVzdC5Nb2NrKS5tb2NrSW1wbGVtZW50YXRpb24oKCkgPT4gKHtcbiAgc2VuZDogbW9ja0Nsb3VkRnJvbnRTZW5kLFxufSkpO1xuXG4oU1NNQ2xpZW50IGFzIGplc3QuTW9jaykubW9ja0ltcGxlbWVudGF0aW9uKCgpID0+ICh7XG4gIHNlbmQ6IG1vY2tTc21TZW5kLFxufSkpO1xuXG5kZXNjcmliZShcIlMzRHluYW1vQ2FjaGVIYW5kbGVyXCIsICgpID0+IHtcbiAgbGV0IGhhbmRsZXI6IFMzQ2FjaGVIYW5kbGVyO1xuICBsZXQgbW9ja0NvbnRleHQ6IENhY2hlSGFuZGxlckNvbnRleHQ7XG4gIGxldCBkaXN0RGlyOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG5cbiAgLyoqIFN0YWdlIGByZXF1aXJlZC1zZXJ2ZXItZmlsZXMuanNvbmAgZm9yIGFuIGFwcCB3aXRoIGBiYXNlUGF0aDogXCIvYmFzZVwiYC4gKi9cbiAgY29uc3QgdXNlQmFzZVBhdGggPSAoKSA9PiB7XG4gICAgZGlzdERpciA9IG1rZHRlbXBTeW5jKGpvaW4odG1wZGlyKCksIFwiczMtY2FjaGUtaGFuZGxlci1cIikpO1xuICAgIHdyaXRlRmlsZVN5bmMoXG4gICAgICBqb2luKGRpc3REaXIsIFwicmVxdWlyZWQtc2VydmVyLWZpbGVzLmpzb25cIiksXG4gICAgICBKU09OLnN0cmluZ2lmeSh7IGNvbmZpZzogeyBiYXNlUGF0aDogXCIvYmFzZVwiIH0gfSksXG4gICAgKTtcbiAgICBtb2NrQ29udGV4dC5zZXJ2ZXJEaXN0RGlyID0gam9pbihkaXN0RGlyLCBcInNlcnZlclwiKTtcbiAgfTtcblxuICAvLyBIZWxwZXIgdG8gY3JlYXRlIHNldCBjb250ZXh0IHdpdGggdGFnc1xuICBjb25zdCBjcmVhdGVTZXRDb250ZXh0ID0gKHRhZ3M6IHN0cmluZ1tdKSA9PiAoe1xuICAgIGZldGNoQ2FjaGU6IHRydWUgYXMgY29uc3QsXG4gICAgdGFncyxcbiAgfSk7XG5cbiAgLyoqXG4gICAqIFRoZSBpbnB1dCBhIG1vY2tlZCBjb21tYW5kIHdhcyBjb25zdHJ1Y3RlZCB3aXRoLiBUaGUgU0RLJ3MgY29tbWFuZCBjbGFzc2VzXG4gICAqIGFyZSBhdXRvbW9ja2VkLCBzbyBhbiBpbnN0YW5jZSBjYXJyaWVzIG5vdGhpbmcgb2YgaXRzIG93bi5cbiAgICovXG4gIGNvbnN0IGNvbW1hbmRJbnB1dCA9IChjb21tYW5kOiB1bmtub3duLCB0eXBlOiB1bmtub3duKSA9PiB7XG4gICAgY29uc3QgbW9jayA9IHR5cGUgYXMgamVzdC5Nb2NrO1xuICAgIHJldHVybiBtb2NrLm1vY2suY2FsbHNbbW9jay5tb2NrLmluc3RhbmNlcy5pbmRleE9mKGNvbW1hbmQpXVswXTtcbiAgfTtcblxuICAvKipcbiAgICogQW5zd2VyIER5bmFtb0RCIGJ5IGNvbW1hbmQgdHlwZSByYXRoZXIgdGhhbiBieSBjYWxsIG9yZGVyOiBgcmV2YWxpZGF0ZVRhZ2BcbiAgICogd3JpdGVzIGl0cyB0YWcgbWFya2VyIGJlZm9yZSBxdWVyeWluZyB0aGUgdGFnJ3MgbWFwcGluZyByb3dzLCBhbmQgd2hpY2ggb2ZcbiAgICogdGhvc2UgY29tZXMgZmlyc3QgaXMgYW4gaW1wbGVtZW50YXRpb24gZGV0YWlsLiBgZ2V0YCBpcyB0aGUgbWFya2VyIHJvdyBldmVyeVxuICAgKiByZXF1ZXN0ZWQgdGFnIGhhcywgYXMgYSBgR2V0SXRlbWAgd291bGQgaGF2ZSByZXR1cm5lZCBpdC5cbiAgICovXG4gIGNvbnN0IGR5bmFtb1Jlc3BvbnNlcyA9IChyZXNwb25zZXM6IHtcbiAgICBxdWVyeT86IHVua25vd247XG4gICAgZ2V0PzogeyBJdGVtPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfTtcbiAgfSkgPT4ge1xuICAgIG1vY2tEeW5hbW9TZW5kLm1vY2tJbXBsZW1lbnRhdGlvbigoY29tbWFuZDogdW5rbm93bikgPT4ge1xuICAgICAgaWYgKGNvbW1hbmQgaW5zdGFuY2VvZiBRdWVyeUNvbW1hbmQpIHtcbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShyZXNwb25zZXMucXVlcnkgPz8ge30pO1xuICAgICAgfVxuICAgICAgaWYgKGNvbW1hbmQgaW5zdGFuY2VvZiBCYXRjaEdldEl0ZW1Db21tYW5kKSB7XG4gICAgICAgIGNvbnN0IHsgUmVxdWVzdEl0ZW1zIH0gPSBjb21tYW5kSW5wdXQoY29tbWFuZCwgQmF0Y2hHZXRJdGVtQ29tbWFuZCk7XG4gICAgICAgIGNvbnN0IFtbdGFibGUsIHsgS2V5cyB9XV0gPSBPYmplY3QuZW50cmllcyhSZXF1ZXN0SXRlbXMpIGFzIFtcbiAgICAgICAgICBzdHJpbmcsXG4gICAgICAgICAgeyBLZXlzOiB7IHNrOiB1bmtub3duIH1bXSB9LFxuICAgICAgICBdW107XG4gICAgICAgIGNvbnN0IGl0ZW0gPSByZXNwb25zZXMuZ2V0Py5JdGVtO1xuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHtcbiAgICAgICAgICBSZXNwb25zZXM6IHtcbiAgICAgICAgICAgIFt0YWJsZV06IGl0ZW0gPyBLZXlzLm1hcCgoeyBzayB9KSA9PiAoeyAuLi5pdGVtLCBzayB9KSkgOiBbXSxcbiAgICAgICAgICB9LFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoe30pO1xuICAgIH0pO1xuICB9O1xuXG4gIC8qKlxuICAgKiBEcm9wIHRoZSBwcm9jZXNzJ3MgdGFnIG1hbmlmZXN0LCBzbyB0aGUgbmV4dCBoYW5kbGVyIHN0YXJ0cyB3aXRoIG9uZSBvZlxuICAgKiBpdHMgb3duLCBhcyBhIG5ldyBpbnN0YW5jZSBkb2VzLlxuICAgKi9cbiAgY29uc3QgcmVzZXRUYWdNYW5pZmVzdCA9ICgpID0+IHtcbiAgICBkZWxldGUgKGdsb2JhbFRoaXMgYXMgUmVjb3JkPHN5bWJvbCwgdW5rbm93bj4pW1xuICAgICAgU3ltYm9sLmZvcihcImNkay1uZXh0anMudXNlLWNhY2hlLnRhZy1tYW5pZmVzdFwiKVxuICAgIF07XG4gIH07XG5cbiAgYmVmb3JlRWFjaCgoKSA9PiB7XG4gICAgbW9ja0NvbnRleHQgPSB7IGRldjogZmFsc2UgfSBhcyBDYWNoZUhhbmRsZXJDb250ZXh0O1xuICAgIHJlc2V0VGFnTWFuaWZlc3QoKTtcblxuICAgIC8vIFNldCB1cCBlbnZpcm9ubWVudCB2YXJpYWJsZXNcbiAgICBwcm9jZXNzLmVudi5DREtfTkVYVEpTX0NBQ0hFX0JVQ0tFVF9OQU1FID0gXCJ0ZXN0LWJ1Y2tldFwiO1xuICAgIHByb2Nlc3MuZW52LkNES19ORVhUSlNfUkVWQUxJREFUSU9OX1RBQkxFX05BTUUgPSBcInRlc3QtdGFibGVcIjtcbiAgICBwcm9jZXNzLmVudi5DREtfTkVYVEpTX0JVSUxEX0lEID0gXCJ0ZXN0LWJ1aWxkLWlkXCI7XG4gICAgcHJvY2Vzcy5lbnYuQVdTX1JFR0lPTiA9IFwidXMtZWFzdC0xXCI7XG5cbiAgICAvLyBTdXBwcmVzcyBleHBlY3RlZCB3YXJuaW5ncyBnbG9iYWxseSAodGVzdHMgZGVsaWJlcmF0ZWx5IHRyaWdnZXIgd2FybmluZ3MpXG4gICAgamVzdC5zcHlPbihjb25zb2xlLCBcIndhcm5cIikubW9ja0ltcGxlbWVudGF0aW9uKCk7XG5cbiAgICBoYW5kbGVyID0gbmV3IFMzQ2FjaGVIYW5kbGVyKHtcbiAgICAgIGNvbnRleHQ6IG1vY2tDb250ZXh0LFxuICAgIH0pO1xuXG4gICAgLy8gUmVzZXQgbW9ja3NcbiAgICBtb2NrUzNTZW5kLm1vY2tSZXNldCgpO1xuICAgIC8vIEFuIGVtcHR5IGJ1Y2tldDogbm8gb2JqZWN0LCBzdWNoIGFzIG5vIGJ1aWxkIHRhZyBtYW5pZmVzdC5cbiAgICBtb2NrUzNTZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHt9KTtcbiAgICBtb2NrRHluYW1vU2VuZC5tb2NrUmVzZXQoKTtcbiAgICBtb2NrQ2xvdWRGcm9udFNlbmQubW9ja1Jlc2V0KCk7XG4gICAgbW9ja1NzbVNlbmQubW9ja1Jlc2V0KCk7XG4gIH0pO1xuXG4gIGFmdGVyRWFjaCgoKSA9PiB7XG4gICAgLy8gQ2xlYW4gdXAgZW52aXJvbm1lbnQgdmFyaWFibGVzXG4gICAgZGVsZXRlIHByb2Nlc3MuZW52LkNES19ORVhUSlNfQ0FDSEVfQlVDS0VUX05BTUU7XG4gICAgZGVsZXRlIHByb2Nlc3MuZW52LkNES19ORVhUSlNfUkVWQUxJREFUSU9OX1RBQkxFX05BTUU7XG4gICAgZGVsZXRlIHByb2Nlc3MuZW52LkNES19ORVhUSlNfQlVJTERfSUQ7XG4gICAgZGVsZXRlIHByb2Nlc3MuZW52LkFXU19SRUdJT047XG4gICAgZGVsZXRlIHByb2Nlc3MuZW52LkNES19ORVhUSlNfRElTVFJJQlVUSU9OX0lEX1BBUkFNX05BTUU7XG4gICAgZGVsZXRlIHByb2Nlc3MuZW52LkNES19ORVhUSlNfRElTVFJJQlVUSU9OX0lEO1xuICAgIGlmIChkaXN0RGlyKSB7XG4gICAgICBybVN5bmMoZGlzdERpciwgeyByZWN1cnNpdmU6IHRydWUsIGZvcmNlOiB0cnVlIH0pO1xuICAgICAgZGlzdERpciA9IHVuZGVmaW5lZDtcbiAgICB9XG5cbiAgICAvLyBSZXN0b3JlIGNvbnNvbGUud2FyblxuICAgIGplc3QucmVzdG9yZUFsbE1vY2tzKCk7XG4gIH0pO1xuXG4gIGRlc2NyaWJlKFwiZ2V0XCIsICgpID0+IHtcbiAgICBpdChcInNob3VsZCByZXR1cm4gbnVsbCB3aGVuIFMzIGJ1Y2tldCBpcyBub3QgY29uZmlndXJlZFwiLCBhc3luYyAoKSA9PiB7XG4gICAgICBkZWxldGUgcHJvY2Vzcy5lbnYuQ0RLX05FWFRKU19DQUNIRV9CVUNLRVRfTkFNRTtcblxuICAgICAgY29uc3QgaGFuZGxlcldpdGhvdXRCdWNrZXQgPSBuZXcgUzNDYWNoZUhhbmRsZXIoe1xuICAgICAgICBjb250ZXh0OiBtb2NrQ29udGV4dCxcbiAgICAgIH0pO1xuXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBoYW5kbGVyV2l0aG91dEJ1Y2tldC5nZXQoXCJ0ZXN0LWtleVwiLCB7XG4gICAgICAgIGtpbmQ6IEluY3JlbWVudGFsQ2FjaGVLaW5kLkFQUF9QQUdFLFxuICAgICAgICBpc0ZhbGxiYWNrOiBmYWxzZSxcbiAgICAgIH0pO1xuICAgICAgZXhwZWN0KHJlc3VsdCkudG9CZU51bGwoKTtcbiAgICB9KTtcblxuICAgIGl0KFwic2hvdWxkIHJldHJpZXZlIGFuZCBwYXJzZSBjYWNoZSB2YWx1ZSBmcm9tIFMzXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIGNvbnN0IHRlc3REYXRhOiBJbmNyZW1lbnRhbENhY2hlVmFsdWUgPSB7XG4gICAgICAgIGtpbmQ6IENhY2hlZFJvdXRlS2luZC5BUFBfUEFHRSxcbiAgICAgICAgaHRtbDogXCI8aHRtbD50ZXN0PC9odG1sPlwiLFxuICAgICAgICByc2NEYXRhOiB1bmRlZmluZWQsXG4gICAgICAgIGhlYWRlcnM6IHVuZGVmaW5lZCxcbiAgICAgICAgcG9zdHBvbmVkOiB1bmRlZmluZWQsXG4gICAgICAgIHNlZ21lbnREYXRhOiB1bmRlZmluZWQsXG4gICAgICAgIHN0YXR1czogdW5kZWZpbmVkLFxuICAgICAgfTtcblxuICAgICAgY29uc3QgY2FjaGVWYWx1ZSA9IHtcbiAgICAgICAgbGFzdE1vZGlmaWVkOiBEYXRlLm5vdygpLFxuICAgICAgICB2YWx1ZTogdGVzdERhdGEsXG4gICAgICB9O1xuXG4gICAgICBtb2NrUzNTZW5kLm1vY2tSZXNvbHZlZFZhbHVlT25jZSh7XG4gICAgICAgIEJvZHk6IHtcbiAgICAgICAgICB0cmFuc2Zvcm1Ub1N0cmluZzogamVzdFxuICAgICAgICAgICAgLmZuKClcbiAgICAgICAgICAgIC5tb2NrUmVzb2x2ZWRWYWx1ZShKU09OLnN0cmluZ2lmeShjYWNoZVZhbHVlKSksXG4gICAgICAgIH0sXG4gICAgICAgIENvbnRlbnRUeXBlOiBcImFwcGxpY2F0aW9uL2pzb25cIixcbiAgICAgIH0pO1xuXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBoYW5kbGVyLmdldChcInRlc3Qta2V5XCIsIHtcbiAgICAgICAga2luZDogSW5jcmVtZW50YWxDYWNoZUtpbmQuQVBQX1BBR0UsXG4gICAgICAgIGlzRmFsbGJhY2s6IGZhbHNlLFxuICAgICAgfSk7XG5cbiAgICAgIGV4cGVjdChyZXN1bHQpLnRvRXF1YWwoY2FjaGVWYWx1ZSk7XG4gICAgICBleHBlY3QobW9ja1MzU2VuZCkudG9IYXZlQmVlbkNhbGxlZFdpdGgoZXhwZWN0LmFueShHZXRPYmplY3RDb21tYW5kKSk7XG4gICAgfSk7XG5cbiAgICBpdChcInNob3VsZCByZXR1cm4gbnVsbCB3aGVuIFMzIG9iamVjdCBub3QgZm91bmRcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgbW9ja1MzU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZU9uY2Uoe1xuICAgICAgICBCb2R5OiBudWxsLFxuICAgICAgfSk7XG5cbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGhhbmRsZXIuZ2V0KFwibWlzc2luZy1rZXlcIiwge1xuICAgICAgICBraW5kOiBJbmNyZW1lbnRhbENhY2hlS2luZC5BUFBfUEFHRSxcbiAgICAgICAgaXNGYWxsYmFjazogZmFsc2UsXG4gICAgICB9KTtcbiAgICAgIGV4cGVjdChyZXN1bHQpLnRvQmVOdWxsKCk7XG4gICAgfSk7XG5cbiAgICBpdChcImludmFsaWRhdGVzIGEgc2VlZGVkIHByZXJlbmRlciBvbmx5IHdoZW4gaXRzIHRhZyBtYXJrZXIgaXMgbmV3ZXJcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgLy8gQSBidWlsZC10aW1lIHByZXJlbmRlciBpcyB3cml0dGVuIGFzIGEgZmlsZSBieSB0aGUgYWRhcHRlcidzXG4gICAgICAvLyBgb25CdWlsZENvbXBsZXRlYCwgbmV2ZXIgdGhyb3VnaCBgc2V0YCwgc28gaXQgaGFzIG5vIGB0YWdzYCBhcnJheSBhbmQgbm9cbiAgICAgIC8vIER5bmFtb0RCIG1hcHBpbmcgcm93cyAtIGl0cyB0YWdzIGxpdmUgaW4gdGhlIHJlbmRlcidzXG4gICAgICAvLyBgeC1uZXh0LWNhY2hlLXRhZ3NgIGhlYWRlci4gVHdvIGJ1Z3MgbWV0IGhlcmU6IHRob3NlIGhlYWRlciB0YWdzIHdlcmVcbiAgICAgIC8vIGlnbm9yZWQsIHNvIGByZXZhbGlkYXRlVGFnYCBjb3VsZCBub3QgcmVhY2ggYW55IHN0YXRpYyBwYWdlOyBhbmQgdGhlXG4gICAgICAvLyBtYXBwaW5nIHJvd3MgYHNldGAgd3JpdGVzIHdlcmUgc3RhbXBlZCBgcmV2YWxpZGF0ZWRBdCA9IG5vd2AsIHdoaWNoIGlzXG4gICAgICAvLyAqYWZ0ZXIqIHRoZSBlbnRyeSdzIG93biBgbGFzdE1vZGlmaWVkYCwgc28gYSB0YWdnZWQgZW50cnkgbG9va2VkXG4gICAgICAvLyByZXZhbGlkYXRlZCB0aGUgbW9tZW50IGl0IHdhcyBzdG9yZWQgYW5kIGV2ZXJ5IHJlcXVlc3QgcmUtcmVuZGVyZWQuXG4gICAgICAvLyBNZWFzdXJlZCBhZ2FpbnN0IG5leHQuanMncyBgdGVzdC9lMmUvYXBwLWRpci9yZXN1bWUtZGF0YS1jYWNoZWAuXG4gICAgICBjb25zdCBsYXN0TW9kaWZpZWQgPSBEYXRlLm5vdygpO1xuICAgICAgY29uc3Qgc2VlZGVkID0ge1xuICAgICAgICBsYXN0TW9kaWZpZWQsXG4gICAgICAgIHZhbHVlOiB7XG4gICAgICAgICAga2luZDogQ2FjaGVkUm91dGVLaW5kLkFQUF9QQUdFLFxuICAgICAgICAgIGh0bWw6IFwiPGh0bWw+c2VlZGVkPC9odG1sPlwiLFxuICAgICAgICAgIGhlYWRlcnM6IHsgXCJ4LW5leHQtY2FjaGUtdGFnc1wiOiBcIl9OX1RfLyxfTl9UXy9wYWdlLHRlc3RcIiB9LFxuICAgICAgICB9LFxuICAgICAgfTtcbiAgICAgIGNvbnN0IHMzQm9keSA9ICgpID0+ICh7XG4gICAgICAgIEJvZHk6IHtcbiAgICAgICAgICB0cmFuc2Zvcm1Ub1N0cmluZzogamVzdFxuICAgICAgICAgICAgLmZuKClcbiAgICAgICAgICAgIC5tb2NrUmVzb2x2ZWRWYWx1ZShKU09OLnN0cmluZ2lmeShzZWVkZWQpKSxcbiAgICAgICAgfSxcbiAgICAgICAgQ29udGVudFR5cGU6IFwiYXBwbGljYXRpb24vanNvblwiLFxuICAgICAgfSk7XG4gICAgICBtb2NrUzNTZW5kLm1vY2tJbXBsZW1lbnRhdGlvbigoY29tbWFuZDogdW5rbm93bikgPT5cbiAgICAgICAgUHJvbWlzZS5yZXNvbHZlKGNvbW1hbmQgaW5zdGFuY2VvZiBHZXRPYmplY3RDb21tYW5kID8gczNCb2R5KCkgOiB7fSksXG4gICAgICApO1xuICAgICAgY29uc3QgZ2V0Q3R4ID0ge1xuICAgICAgICBraW5kOiBJbmNyZW1lbnRhbENhY2hlS2luZC5BUFBfUEFHRSxcbiAgICAgICAgaXNGYWxsYmFjazogZmFsc2UsXG4gICAgICB9IGFzIGNvbnN0O1xuICAgICAgLy8gRWFjaCBzdGVwIGJlbG93IGNoYW5nZXMgdGhlIG1hcmtlcnMgRHluYW1vREIgaG9sZHM7IGEgbmV3IGluc3RhbmNlXG4gICAgICAvLyByZWFkcyB0aGVtIGFmcmVzaC5cbiAgICAgIGNvbnN0IGZyZXNoSGFuZGxlciA9ICgpID0+IHtcbiAgICAgICAgcmVzZXRUYWdNYW5pZmVzdCgpO1xuICAgICAgICByZXR1cm4gbmV3IFMzQ2FjaGVIYW5kbGVyKHsgY29udGV4dDogbW9ja0NvbnRleHQgfSk7XG4gICAgICB9O1xuXG4gICAgICAvLyBObyBtYXJrZXIgYXQgYWxsOiB0aGUgdGFnIHdhcyBuZXZlciByZXZhbGlkYXRlZC5cbiAgICAgIGR5bmFtb1Jlc3BvbnNlcyh7fSk7XG4gICAgICBleHBlY3QoYXdhaXQgZnJlc2hIYW5kbGVyKCkuZ2V0KFwiaW5kZXhcIiwgZ2V0Q3R4KSkudG9NYXRjaE9iamVjdCh7XG4gICAgICAgIGxhc3RNb2RpZmllZCxcbiAgICAgIH0pO1xuXG4gICAgICAvLyBBIG1hcmtlciBvbGRlciB0aGFuIHRoZSBlbnRyeSBtZWFucyB0aGUgZW50cnkgYWxyZWFkeSByZWZsZWN0cyBpdC5cbiAgICAgIGR5bmFtb1Jlc3BvbnNlcyh7XG4gICAgICAgIGdldDogeyBJdGVtOiB7IHJldmFsaWRhdGVkQXQ6IHsgTjogU3RyaW5nKGxhc3RNb2RpZmllZCAtIDEwMDApIH0gfSB9LFxuICAgICAgfSk7XG4gICAgICBleHBlY3QoYXdhaXQgZnJlc2hIYW5kbGVyKCkuZ2V0KFwiaW5kZXhcIiwgZ2V0Q3R4KSkudG9NYXRjaE9iamVjdCh7XG4gICAgICAgIGxhc3RNb2RpZmllZCxcbiAgICAgIH0pO1xuICAgICAgZXhwZWN0KG1vY2tTM1NlbmQpLm5vdC50b0hhdmVCZWVuQ2FsbGVkV2l0aChcbiAgICAgICAgZXhwZWN0LmFueShEZWxldGVPYmplY3RDb21tYW5kKSxcbiAgICAgICk7XG5cbiAgICAgIC8vIEEgbmV3ZXIgbWFya2VyOiB0aGUgZW50cnkgY29tZXMgYmFjayBtYXJrZWQgZXhwaXJlZCwgYW5kIHN0YXlzIGluIFMzLlxuICAgICAgZHluYW1vUmVzcG9uc2VzKHtcbiAgICAgICAgZ2V0OiB7IEl0ZW06IHsgcmV2YWxpZGF0ZWRBdDogeyBOOiBTdHJpbmcobGFzdE1vZGlmaWVkICsgMTAwMCkgfSB9IH0sXG4gICAgICB9KTtcbiAgICAgIGV4cGVjdChhd2FpdCBmcmVzaEhhbmRsZXIoKS5nZXQoXCJpbmRleFwiLCBnZXRDdHgpKS50b01hdGNoT2JqZWN0KHtcbiAgICAgICAgbGFzdE1vZGlmaWVkOiAtMSxcbiAgICAgICAgdmFsdWU6IHNlZWRlZC52YWx1ZSxcbiAgICAgIH0pO1xuICAgICAgZXhwZWN0KG1vY2tTM1NlbmQpLm5vdC50b0hhdmVCZWVuQ2FsbGVkV2l0aChcbiAgICAgICAgZXhwZWN0LmFueShEZWxldGVPYmplY3RDb21tYW5kKSxcbiAgICAgICk7XG4gICAgfSk7XG5cbiAgICBpdChcInJlcG9ydHMgYSByZXZhbGlkYXRlZCByZXNwb25zZSBlbnRyeSBhcyBleHBpcmVkIHJhdGhlciB0aGFuIGFzIGEgbWlzc1wiLCBhc3luYyAoKSA9PiB7XG4gICAgICAvLyBgbGFzdE1vZGlmaWVkOiAtMWAgaXMgaG93IGEgY2FjaGUgaGFuZGxlciBzYXlzIFwiZXhwaXJlZCwgcmUtcmVuZGVyXG4gICAgICAvLyBiZWZvcmUgYW5zd2VyaW5nXCI6IGBJbmNyZW1lbnRhbENhY2hlLmdldGAgdHVybnMgaXQgaW50byBgaXNTdGFsZTogLTFgLFxuICAgICAgLy8gd2hpY2ggYGFwcC1wYWdlLXJ1bnRpbWVgIHJlYWRzIGFzIGFuIG9uLWRlbWFuZCByZXZhbGlkYXRpb24gYW5kIGFuc3dlcnNcbiAgICAgIC8vIHdpdGggYSBibG9ja2luZyByZW5kZXIgb2YgdGhpcyByb3V0ZSwgdGhlbiBzdG9yZXMgaXQuIFJldHVybmluZyBgbnVsbGBcbiAgICAgIC8vIGlzIG5vdCBlcXVpdmFsZW50LiBBIG1pc3Mgb24gYSBQUFIgcm91dGUgd2hvc2UgYHByZXJlbmRlci1tYW5pZmVzdC5qc29uYFxuICAgICAgLy8gZW50cnkgaGFzIGEgYGZhbGxiYWNrYCAoYGNvbXB1dGU6IFwicmVzdW1pbmdcImApIGlzIGFuc3dlcmVkIGZyb20gdGhlXG4gICAgICAvLyByb3V0ZSdzIGZhbGxiYWNrIHNoZWxsIC0gYGNhY2hlLWNvbnRyb2w6IHByaXZhdGUsIG5vLXN0b3JlYCwgbm9cbiAgICAgIC8vIGB4LW5leHRqcy1jYWNoZWAgLSBhbmQgd2l0aG91dCBgcGFydGlhbFByZWZldGNoaW5nYCBub3RoaW5nIGV2ZXJcbiAgICAgIC8vIHVwZ3JhZGVzIHRoYXQgYmFjayBpbnRvIGEgY29uY3JldGUgZW50cnkuIFNvIHRoZSBmaXJzdCBgcmV2YWxpZGF0ZVRhZ2BcbiAgICAgIC8vIHRvIHJlYWNoIGEgcGFnZSBsZWZ0IGl0IHVuY2FjaGVhYmxlIGFuZCBkeW5hbWljYWxseSByZXN1bWVkIHBlciByZXF1ZXN0XG4gICAgICAvLyBmb3IgdGhlIHJlc3Qgb2YgdGhlIGRlcGxveW1lbnQncyBsaWZlLCB3aGljaCBpcyB3aGF0IHRoZSBpc3IgZTJlJ3NcbiAgICAgIC8vIG1pc3NpbmcgYHgtbmV4dGpzLWNhY2hlYCBoZWFkZXIgd2FzLlxuICAgICAgY29uc3QgbGFzdE1vZGlmaWVkID0gRGF0ZS5ub3coKTtcbiAgICAgIGNvbnN0IHN0b3JlZCA9IHtcbiAgICAgICAgbGFzdE1vZGlmaWVkLFxuICAgICAgICB0YWdzOiBbXCJjb2xsZWN0aW9uXCJdLFxuICAgICAgICB2YWx1ZToge1xuICAgICAgICAgIGtpbmQ6IENhY2hlZFJvdXRlS2luZC5BUFBfUEFHRSxcbiAgICAgICAgICBodG1sOiBcIjxodG1sPmlzcjwvaHRtbD5cIixcbiAgICAgICAgICBoZWFkZXJzOiB7IFwieC1uZXh0LWNhY2hlLXRhZ3NcIjogXCJjb2xsZWN0aW9uXCIgfSxcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgICBtb2NrUzNTZW5kLm1vY2tJbXBsZW1lbnRhdGlvbigoY29tbWFuZDogdW5rbm93bikgPT5cbiAgICAgICAgUHJvbWlzZS5yZXNvbHZlKFxuICAgICAgICAgIGNvbW1hbmQgaW5zdGFuY2VvZiBHZXRPYmplY3RDb21tYW5kXG4gICAgICAgICAgICA/IHtcbiAgICAgICAgICAgICAgICBCb2R5OiB7XG4gICAgICAgICAgICAgICAgICB0cmFuc2Zvcm1Ub1N0cmluZzogamVzdFxuICAgICAgICAgICAgICAgICAgICAuZm4oKVxuICAgICAgICAgICAgICAgICAgICAubW9ja1Jlc29sdmVkVmFsdWUoSlNPTi5zdHJpbmdpZnkoc3RvcmVkKSksXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBDb250ZW50VHlwZTogXCJhcHBsaWNhdGlvbi9qc29uXCIsXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIDoge30sXG4gICAgICAgICksXG4gICAgICApO1xuICAgICAgZHluYW1vUmVzcG9uc2VzKHtcbiAgICAgICAgZ2V0OiB7IEl0ZW06IHsgcmV2YWxpZGF0ZWRBdDogeyBOOiBTdHJpbmcobGFzdE1vZGlmaWVkICsgMTAwMCkgfSB9IH0sXG4gICAgICB9KTtcblxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgaGFuZGxlci5nZXQoXCJpc3IvMVwiLCB7XG4gICAgICAgIGtpbmQ6IEluY3JlbWVudGFsQ2FjaGVLaW5kLkFQUF9QQUdFLFxuICAgICAgICBpc0ZhbGxiYWNrOiBmYWxzZSxcbiAgICAgIH0pO1xuXG4gICAgICBleHBlY3QocmVzdWx0KS50b0VxdWFsKHsgbGFzdE1vZGlmaWVkOiAtMSwgdmFsdWU6IHN0b3JlZC52YWx1ZSB9KTtcbiAgICAgIC8vIFRoZSBib2R5IGlzIHN0aWxsIHRoZXJlIGZvciB0aGUgcmVuZGVyIHRoYXQgZmFpbHMsIGFuZCBgc2V0YCBvdmVyd3JpdGVzXG4gICAgICAvLyBpdCB3aXRoIGEgYGxhc3RNb2RpZmllZGAgcGFzdCB0aGUgbWFya2VyLlxuICAgICAgZXhwZWN0KG1vY2tTM1NlbmQpLm5vdC50b0hhdmVCZWVuQ2FsbGVkV2l0aChcbiAgICAgICAgZXhwZWN0LmFueShEZWxldGVPYmplY3RDb21tYW5kKSxcbiAgICAgICk7XG4gICAgfSk7XG5cbiAgICBpdChcInJlcG9ydHMgYSByZXZhbGlkYXRlZCBmZXRjaCBlbnRyeSBhcyBhIG1pc3Mgc28gdGhlIHJlcXVlc3QgcmVmZXRjaGVzXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIC8vIFRoZSBvcHBvc2l0ZSBvZiBhIHJlc3BvbnNlIGVudHJ5OiByZXVzaW5nIGEgcmV2YWxpZGF0ZWQgYGZldGNoYCBib2R5IGlzXG4gICAgICAvLyBzZXJ2aW5nIHN0YWxlIGRhdGEsIHNvIHRoaXMgcmVhZHMgYXMgYSBtaXNzLCBsaWtlIE5leHQuanMncyBvd25cbiAgICAgIC8vIGBGaWxlU3lzdGVtQ2FjaGVgIGRvZXMgZm9yIGl0cyBgcmV2YWxpZGF0ZWRUYWdzYC5cbiAgICAgIGNvbnN0IGxhc3RNb2RpZmllZCA9IERhdGUubm93KCk7XG4gICAgICBjb25zdCBzdG9yZWQgPSB7XG4gICAgICAgIGxhc3RNb2RpZmllZCxcbiAgICAgICAgdGFnczogW1wiY29sbGVjdGlvblwiXSxcbiAgICAgICAgdmFsdWU6IHtcbiAgICAgICAgICBraW5kOiBDYWNoZWRSb3V0ZUtpbmQuRkVUQ0gsXG4gICAgICAgICAgZGF0YTogeyBoZWFkZXJzOiB7fSwgYm9keTogXCJzdGFsZVwiLCBzdGF0dXM6IDIwMCwgdXJsOiBcIi9hcGlcIiB9LFxuICAgICAgICAgIHJldmFsaWRhdGU6IDEwLFxuICAgICAgICB9LFxuICAgICAgfTtcbiAgICAgIG1vY2tTM1NlbmQubW9ja0ltcGxlbWVudGF0aW9uKChjb21tYW5kOiB1bmtub3duKSA9PlxuICAgICAgICBQcm9taXNlLnJlc29sdmUoXG4gICAgICAgICAgY29tbWFuZCBpbnN0YW5jZW9mIEdldE9iamVjdENvbW1hbmRcbiAgICAgICAgICAgID8ge1xuICAgICAgICAgICAgICAgIEJvZHk6IHtcbiAgICAgICAgICAgICAgICAgIHRyYW5zZm9ybVRvU3RyaW5nOiBqZXN0XG4gICAgICAgICAgICAgICAgICAgIC5mbigpXG4gICAgICAgICAgICAgICAgICAgIC5tb2NrUmVzb2x2ZWRWYWx1ZShKU09OLnN0cmluZ2lmeShzdG9yZWQpKSxcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIENvbnRlbnRUeXBlOiBcImFwcGxpY2F0aW9uL2pzb25cIixcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgOiB7fSxcbiAgICAgICAgKSxcbiAgICAgICk7XG4gICAgICBkeW5hbW9SZXNwb25zZXMoe1xuICAgICAgICBnZXQ6IHsgSXRlbTogeyByZXZhbGlkYXRlZEF0OiB7IE46IFN0cmluZyhsYXN0TW9kaWZpZWQgKyAxMDAwKSB9IH0gfSxcbiAgICAgIH0pO1xuXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBoYW5kbGVyLmdldChcImZldGNoLWtleVwiLCB7XG4gICAgICAgIGtpbmQ6IEluY3JlbWVudGFsQ2FjaGVLaW5kLkZFVENILFxuICAgICAgICByZXZhbGlkYXRlOiAxMCxcbiAgICAgICAgZmV0Y2hVcmw6IFwiaHR0cHM6Ly9leGFtcGxlLnRlc3QvYXBpXCIsXG4gICAgICAgIGZldGNoSWR4OiAxLFxuICAgICAgICB0YWdzOiBbXCJjb2xsZWN0aW9uXCJdLFxuICAgICAgICBzb2Z0VGFnczogW10sXG4gICAgICB9KTtcblxuICAgICAgZXhwZWN0KHJlc3VsdCkudG9CZU51bGwoKTtcbiAgICAgIC8vIE5vdCBkZWxldGVkOiB0aGUgcmVmZXRjaCB0aGlzIHByb3Zva2VzIG92ZXJ3cml0ZXMgaXQgdGhyb3VnaCBgc2V0YC5cbiAgICAgIC8vIERlbGV0aW5nIGl0IGhlcmUgcmFjZWQgdGhhdCB3cml0ZSwgc28gYSBjb25jdXJyZW50IHJlcXVlc3QncyBmcmVzaFxuICAgICAgLy8gZW50cnkgY291bGQgYmUgdGhlIG9uZSByZW1vdmVkLlxuICAgICAgZXhwZWN0KG1vY2tTM1NlbmQpLm5vdC50b0hhdmVCZWVuQ2FsbGVkV2l0aChcbiAgICAgICAgZXhwZWN0LmFueShEZWxldGVPYmplY3RDb21tYW5kKSxcbiAgICAgICk7XG4gICAgICBleHBlY3QoRGVsZXRlSXRlbUNvbW1hbmQgYXMgdW5rbm93biBhcyBqZXN0Lk1vY2spLm5vdC50b0hhdmVCZWVuQ2FsbGVkKCk7XG4gICAgfSk7XG5cbiAgICBpdChcImNoZWNrcyBhIGZldGNoIGVudHJ5IGFnYWluc3QgdGhlIHJlcXVlc3QncyBpbXBsaWNpdCB0YWdzLCBub3QgaXRzIG93blwiLCBhc3luYyAoKSA9PiB7XG4gICAgICAvLyBBbiB1bnRhZ2dlZCBgY2FjaGU6IFwiZm9yY2UtY2FjaGVcImAgZmV0Y2ggaXMgc3RvcmVkIHdpdGggbm8gdGFncyBhdCBhbGwgLVxuICAgICAgLy8gdGhlIGltcGxpY2l0IGBfTl9UXy88cGF0aD5gIGNoYWluIHJlYWNoZXMgdGhlIGhhbmRsZXIgb25seSBhc1xuICAgICAgLy8gYGN0eC5zb2Z0VGFnc2AsIHdoaWNoIGlzIHRoZSBzb3VyY2UgTmV4dC5qcydzIG93biBgRmlsZVN5c3RlbUNhY2hlYCByZWFkc1xuICAgICAgLy8gZm9yIGEgYEZFVENIYCBnZXQuIENoZWNraW5nIHRoZSBzdG9yZWQgdGFncyBpbnN0ZWFkIGxlZnQgdGhpcyBlbnRyeSB3aXRoXG4gICAgICAvLyBub3RoaW5nIHRvIGNoZWNrIGFuZCBubyBgcmV2YWxpZGF0ZVBhdGhgIGNvdWxkIGV2ZXIgZXZpY3QgaXQuXG4gICAgICBjb25zdCBsYXN0TW9kaWZpZWQgPSBEYXRlLm5vdygpO1xuICAgICAgY29uc3Qgc3RvcmVkID0ge1xuICAgICAgICBsYXN0TW9kaWZpZWQsXG4gICAgICAgIHRhZ3M6IFtdLFxuICAgICAgICB2YWx1ZToge1xuICAgICAgICAgIGtpbmQ6IENhY2hlZFJvdXRlS2luZC5GRVRDSCxcbiAgICAgICAgICBkYXRhOiB7IGhlYWRlcnM6IHt9LCBib2R5OiBcInN0YWxlXCIsIHN0YXR1czogMjAwLCB1cmw6IFwiL2FwaVwiIH0sXG4gICAgICAgICAgcmV2YWxpZGF0ZTogMTAsXG4gICAgICAgIH0sXG4gICAgICB9O1xuICAgICAgbW9ja1MzU2VuZC5tb2NrSW1wbGVtZW50YXRpb24oKGNvbW1hbmQ6IHVua25vd24pID0+XG4gICAgICAgIFByb21pc2UucmVzb2x2ZShcbiAgICAgICAgICBjb21tYW5kIGluc3RhbmNlb2YgR2V0T2JqZWN0Q29tbWFuZFxuICAgICAgICAgICAgPyB7XG4gICAgICAgICAgICAgICAgQm9keToge1xuICAgICAgICAgICAgICAgICAgdHJhbnNmb3JtVG9TdHJpbmc6IGplc3RcbiAgICAgICAgICAgICAgICAgICAgLmZuKClcbiAgICAgICAgICAgICAgICAgICAgLm1vY2tSZXNvbHZlZFZhbHVlKEpTT04uc3RyaW5naWZ5KHN0b3JlZCkpLFxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgQ29udGVudFR5cGU6IFwiYXBwbGljYXRpb24vanNvblwiLFxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICA6IHt9LFxuICAgICAgICApLFxuICAgICAgKTtcbiAgICAgIGR5bmFtb1Jlc3BvbnNlcyh7XG4gICAgICAgIGdldDogeyBJdGVtOiB7IHJldmFsaWRhdGVkQXQ6IHsgTjogU3RyaW5nKGxhc3RNb2RpZmllZCArIDEwMDApIH0gfSB9LFxuICAgICAgfSk7XG5cbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGhhbmRsZXIuZ2V0KFwiZmV0Y2gta2V5XCIsIHtcbiAgICAgICAga2luZDogSW5jcmVtZW50YWxDYWNoZUtpbmQuRkVUQ0gsXG4gICAgICAgIHJldmFsaWRhdGU6IDEwLFxuICAgICAgICBmZXRjaFVybDogXCJodHRwczovL25leHQtZGF0YS1hcGktZW5kcG9pbnQudGVzdC9hcGkvcmFuZG9tXCIsXG4gICAgICAgIGZldGNoSWR4OiAxLFxuICAgICAgICB0YWdzOiBbXSxcbiAgICAgICAgc29mdFRhZ3M6IFtcIl9OX1RfL2xheW91dFwiLCBcIl9OX1RfL2R5bmFtaWNcIl0sXG4gICAgICB9KTtcblxuICAgICAgZXhwZWN0KHJlc3VsdCkudG9CZU51bGwoKTtcbiAgICAgIGNvbnN0IFt7IFJlcXVlc3RJdGVtcyB9XSA9IChCYXRjaEdldEl0ZW1Db21tYW5kIGFzIHVua25vd24gYXMgamVzdC5Nb2NrKVxuICAgICAgICAubW9jay5jYWxsc1swXTtcbiAgICAgIGV4cGVjdChcbiAgICAgICAgUmVxdWVzdEl0ZW1zW1widGVzdC10YWJsZVwiXS5LZXlzLm1hcChcbiAgICAgICAgICAoa2V5OiB7IHNrOiB7IFM6IHN0cmluZyB9IH0pID0+IGtleS5zay5TLFxuICAgICAgICApLFxuICAgICAgKS50b0VxdWFsKFtcIl9OX1RfL2xheW91dFwiLCBcIl9OX1RfL2R5bmFtaWNcIl0pO1xuICAgIH0pO1xuXG4gICAgZGVzY3JpYmUoXCJhIHRhZyByZXZhbGlkYXRlZCB3aXRoIGEgcHJvZmlsZVwiLCAoKSA9PiB7XG4gICAgICAvLyBgcmV2YWxpZGF0ZVRhZyhcInBvc3RzXCIsIFwibWF4XCIpYCBpcyBzdGFsZS13aGlsZS1yZXZhbGlkYXRlOiB0aGUgZW50cnkgaXNcbiAgICAgIC8vIHNlcnZlZCBvbmNlIG1vcmUgd2hpbGUgYSBiYWNrZ3JvdW5kIHJlbmRlciByZXBsYWNlcyBpdCwgYW5kIGV4cGlyZXNcbiAgICAgIC8vIG91dHJpZ2h0IG9ubHkgYWZ0ZXIgdGhlIHByb2ZpbGUncyBgZXhwaXJlYC4gVHJlYXRpbmcgaXQgYXMgYHVwZGF0ZVRhZ2BcbiAgICAgIC8vIG1hZGUgZXZlcnkgc3VjaCBjYWxsIGEgYmxvY2tpbmcgcmUtcmVuZGVyLlxuICAgICAgY29uc3QgbGFzdE1vZGlmaWVkID0gRGF0ZS5ub3coKSAtIDEwMDA7XG4gICAgICBjb25zdCBzdG9yZWQgPSB7XG4gICAgICAgIGxhc3RNb2RpZmllZCxcbiAgICAgICAgdmFsdWU6IHtcbiAgICAgICAgICBraW5kOiBDYWNoZWRSb3V0ZUtpbmQuQVBQX1BBR0UsXG4gICAgICAgICAgaHRtbDogXCI8aHRtbD5wb3N0czwvaHRtbD5cIixcbiAgICAgICAgICBoZWFkZXJzOiB7IFwieC1uZXh0LWNhY2hlLXRhZ3NcIjogXCJwb3N0c1wiIH0sXG4gICAgICAgIH0sXG4gICAgICB9O1xuICAgICAgY29uc3QgZ2V0Q3R4ID0ge1xuICAgICAgICBraW5kOiBJbmNyZW1lbnRhbENhY2hlS2luZC5BUFBfUEFHRSxcbiAgICAgICAgaXNGYWxsYmFjazogZmFsc2UsXG4gICAgICB9IGFzIGNvbnN0O1xuICAgICAgY29uc3QgbWFuaWZlc3RQYXRoID0gam9pbihcbiAgICAgICAgXCIvZGVwbG95bWVudC9ub2RlX21vZHVsZXMvbmV4dC9kaXN0L3NlcnZlci9saWIvaW5jcmVtZW50YWwtY2FjaGVcIixcbiAgICAgICAgXCJ0YWdzLW1hbmlmZXN0LmV4dGVybmFsLmpzXCIsXG4gICAgICApO1xuICAgICAgLy8gVGhlIG1vZHVsZSB0aGUgc2VydmVyIGxvYWRzLCByZXNvbHZlZCBmb3IgcmVhbCwgc28gYSBOZXh0LmpzIHVwZ3JhZGVcbiAgICAgIC8vIHRoYXQgbW92ZXMgb3IgcmVzaGFwZXMgaXQgZmFpbHMgaGVyZSByYXRoZXIgdGhhbiBzaWxlbnRseSB0dXJuaW5nXG4gICAgICAvLyBzdGFsZS13aGlsZS1yZXZhbGlkYXRlIGludG8gYmxvY2tpbmcgcmVuZGVycy5cbiAgICAgIGNvbnN0IHJlYWxNYW5pZmVzdFBhdGggPVxuICAgICAgICByZXF1aXJlLnJlc29sdmUoXCJuZXh0L2Rpc3Qvc2VydmVyL2xpYi9pbmNyZW1lbnRhbC1jYWNoZS90YWdzLW1hbmlmZXN0LmV4dGVybmFsLmpzXCIpO1xuXG4gICAgICBiZWZvcmVFYWNoKCgpID0+IHtcbiAgICAgICAgbW9ja1MzU2VuZC5tb2NrSW1wbGVtZW50YXRpb24oKGNvbW1hbmQ6IHVua25vd24pID0+XG4gICAgICAgICAgUHJvbWlzZS5yZXNvbHZlKFxuICAgICAgICAgICAgY29tbWFuZCBpbnN0YW5jZW9mIEdldE9iamVjdENvbW1hbmRcbiAgICAgICAgICAgICAgPyB7XG4gICAgICAgICAgICAgICAgICBCb2R5OiB7XG4gICAgICAgICAgICAgICAgICAgIHRyYW5zZm9ybVRvU3RyaW5nOiBqZXN0XG4gICAgICAgICAgICAgICAgICAgICAgLmZuKClcbiAgICAgICAgICAgICAgICAgICAgICAubW9ja1Jlc29sdmVkVmFsdWUoSlNPTi5zdHJpbmdpZnkoc3RvcmVkKSksXG4gICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgQ29udGVudFR5cGU6IFwiYXBwbGljYXRpb24vanNvblwiLFxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgOiB7fSxcbiAgICAgICAgICApLFxuICAgICAgICApO1xuICAgICAgfSk7XG5cbiAgICAgIGFmdGVyRWFjaCgoKSA9PiB7XG4gICAgICAgIGRlbGV0ZSBtb2NrTW9kdWxlQ2FjaGVbbWFuaWZlc3RQYXRoXTtcbiAgICAgICAgZGVsZXRlIG1vY2tNb2R1bGVDYWNoZVtyZWFsTWFuaWZlc3RQYXRoXTtcbiAgICAgIH0pO1xuXG4gICAgICBpdChcInNlcnZlcyB0aGUgZW50cnkgYW5kIG1hcmtzIHRoZSB0YWcgc3RhbGUgaW4gTmV4dC5qcydzIG1hbmlmZXN0XCIsIGFzeW5jICgpID0+IHtcbiAgICAgICAgY29uc3QgbmV4dCA9IGplc3QucmVxdWlyZUFjdHVhbChyZWFsTWFuaWZlc3RQYXRoKTtcbiAgICAgICAgbW9ja01vZHVsZUNhY2hlW3JlYWxNYW5pZmVzdFBhdGhdID0geyBleHBvcnRzOiBuZXh0IH07XG4gICAgICAgIGNvbnN0IHN0YWxlQXQgPSBsYXN0TW9kaWZpZWQgKyA1MDA7XG4gICAgICAgIGR5bmFtb1Jlc3BvbnNlcyh7XG4gICAgICAgICAgZ2V0OiB7XG4gICAgICAgICAgICBJdGVtOiB7XG4gICAgICAgICAgICAgIHN0YWxlQXQ6IHsgTjogU3RyaW5nKHN0YWxlQXQpIH0sXG4gICAgICAgICAgICAgIGV4cGlyZWRBdDogeyBOOiBTdHJpbmcoRGF0ZS5ub3coKSArIDYwXzAwMCkgfSxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfSxcbiAgICAgICAgfSk7XG5cbiAgICAgICAgZXhwZWN0KGF3YWl0IGhhbmRsZXIuZ2V0KFwicG9zdHNcIiwgZ2V0Q3R4KSkudG9FcXVhbCh7XG4gICAgICAgICAgbGFzdE1vZGlmaWVkLFxuICAgICAgICAgIHZhbHVlOiBzdG9yZWQudmFsdWUsXG4gICAgICAgIH0pO1xuICAgICAgICAvLyBXaGVyZSBgSW5jcmVtZW50YWxDYWNoZS5nZXRgIHJlYWRzIGl0LCB0byBhbnN3ZXIgYGlzU3RhbGU6IHRydWVgLlxuICAgICAgICBleHBlY3QobmV4dC50YWdzTWFuaWZlc3QuZ2V0KFwicG9zdHNcIikpLnRvRXF1YWwoeyBzdGFsZTogc3RhbGVBdCB9KTtcbiAgICAgICAgZXhwZWN0KG5leHQuYXJlVGFnc1N0YWxlKFtcInBvc3RzXCJdLCBsYXN0TW9kaWZpZWQpKS50b0JlKHRydWUpO1xuICAgICAgfSk7XG5cbiAgICAgIGl0KFwiZXhwaXJlcyB0aGUgZW50cnkgd2hlbiBOZXh0LmpzJ3MgbWFuaWZlc3QgY2Fubm90IGJlIGZvdW5kXCIsIGFzeW5jICgpID0+IHtcbiAgICAgICAgLy8gQSBzdGFsZSBtYXJrIG5vdGhpbmcgcmVhZHMgd291bGQgc2VydmUgdGhlIGVudHJ5IGFzIGZyZXNoOyBhIGJsb2NraW5nXG4gICAgICAgIC8vIHJlbmRlciBpcyB0aGUgc2FmZSBzaWRlIG9mIHRoYXQuXG4gICAgICAgIGR5bmFtb1Jlc3BvbnNlcyh7XG4gICAgICAgICAgZ2V0OiB7IEl0ZW06IHsgc3RhbGVBdDogeyBOOiBTdHJpbmcobGFzdE1vZGlmaWVkICsgNTAwKSB9IH0gfSxcbiAgICAgICAgfSk7XG5cbiAgICAgICAgZXhwZWN0KGF3YWl0IGhhbmRsZXIuZ2V0KFwicG9zdHNcIiwgZ2V0Q3R4KSkudG9NYXRjaE9iamVjdCh7XG4gICAgICAgICAgbGFzdE1vZGlmaWVkOiAtMSxcbiAgICAgICAgfSk7XG4gICAgICB9KTtcblxuICAgICAgaXQoXCJleHBpcmVzIHRoZSBlbnRyeSBvbmNlIHRoZSBwcm9maWxlJ3MgZXhwaXJlIGhhcyBwYXNzZWRcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgICBkeW5hbW9SZXNwb25zZXMoe1xuICAgICAgICAgIGdldDoge1xuICAgICAgICAgICAgSXRlbToge1xuICAgICAgICAgICAgICBzdGFsZUF0OiB7IE46IFN0cmluZyhsYXN0TW9kaWZpZWQgKyAxMDApIH0sXG4gICAgICAgICAgICAgIGV4cGlyZWRBdDogeyBOOiBTdHJpbmcobGFzdE1vZGlmaWVkICsgNTAwKSB9LFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9LFxuICAgICAgICB9KTtcblxuICAgICAgICBleHBlY3QoYXdhaXQgaGFuZGxlci5nZXQoXCJwb3N0c1wiLCBnZXRDdHgpKS50b01hdGNoT2JqZWN0KHtcbiAgICAgICAgICBsYXN0TW9kaWZpZWQ6IC0xLFxuICAgICAgICB9KTtcbiAgICAgIH0pO1xuXG4gICAgICBkZXNjcmliZShcImJlaGluZCBhIGRpc3RyaWJ1dGlvblwiLCAoKSA9PiB7XG4gICAgICAgIC8vIGByZXZhbGlkYXRlVGFnYCdzIGludmFsaWRhdGlvbiBnb2VzIG91dCBiZWZvcmUgdGhlIHN0YWxlIGVudHJ5IGlzXG4gICAgICAgIC8vIHNlcnZlZCwgYW5kIHRoZSBzdGFsZSByZXNwb25zZSBjYXJyaWVzIHRoZSBlbnRyeSdzIG9yaWdpbmFsXG4gICAgICAgIC8vIGBzLW1heGFnZWA6IENsb3VkRnJvbnQgY2FjaGVzIGl0IGFnYWluLiBPbmx5IHRoZSByZWdlbmVyYXRpb24ncyBgc2V0YFxuICAgICAgICAvLyBrbm93cyB3aGVuIHRoZSBmcmVzaCBlbnRyeSBleGlzdHMgdG8gYmUgZmV0Y2hlZCBpbnN0ZWFkLlxuICAgICAgICBsZXQgY2RuSGFuZGxlcjogUzNDYWNoZUhhbmRsZXI7XG4gICAgICAgIGNvbnN0IHJlZ2VuZXJhdGVkID0gc3RvcmVkLnZhbHVlIGFzIHVua25vd24gYXMgSW5jcmVtZW50YWxDYWNoZVZhbHVlO1xuICAgICAgICBjb25zdCBpbnZhbGlkYXRlZFBhdGhzID0gKCk6IHN0cmluZ1tdW10gPT5cbiAgICAgICAgICAoQ3JlYXRlSW52YWxpZGF0aW9uQ29tbWFuZCBhcyB1bmtub3duIGFzIGplc3QuTW9jaykubW9jay5jYWxscy5tYXAoXG4gICAgICAgICAgICAoW2lucHV0XSkgPT4gaW5wdXQuSW52YWxpZGF0aW9uQmF0Y2guUGF0aHMuSXRlbXMsXG4gICAgICAgICAgKTtcblxuICAgICAgICBiZWZvcmVFYWNoKCgpID0+IHtcbiAgICAgICAgICBwcm9jZXNzLmVudi5DREtfTkVYVEpTX0RJU1RSSUJVVElPTl9JRF9QQVJBTV9OQU1FID0gXCJ0ZXN0LXBhcmFtLW5hbWVcIjtcbiAgICAgICAgICBjZG5IYW5kbGVyID0gbmV3IFMzQ2FjaGVIYW5kbGVyKHsgY29udGV4dDogbW9ja0NvbnRleHQgfSk7XG4gICAgICAgICAgbW9ja01vZHVsZUNhY2hlW21hbmlmZXN0UGF0aF0gPSB7XG4gICAgICAgICAgICBleHBvcnRzOiB7IHRhZ3NNYW5pZmVzdDogbmV3IE1hcCgpIH0sXG4gICAgICAgICAgfTtcbiAgICAgICAgICBkeW5hbW9SZXNwb25zZXMoe1xuICAgICAgICAgICAgZ2V0OiB7IEl0ZW06IHsgc3RhbGVBdDogeyBOOiBTdHJpbmcobGFzdE1vZGlmaWVkICsgNTAwKSB9IH0gfSxcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBtb2NrU3NtU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7XG4gICAgICAgICAgICBQYXJhbWV0ZXI6IHsgVmFsdWU6IFwidGVzdC1kaXN0cmlidXRpb24taWRcIiB9LFxuICAgICAgICAgIH0pO1xuICAgICAgICAgIG1vY2tDbG91ZEZyb250U2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7fSk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGl0KFwiaW52YWxpZGF0ZXMgdGhlIHBhZ2UgYWdhaW4gb25jZSBpdHMgcmVnZW5lcmF0aW9uIGlzIHN0b3JlZFwiLCBhc3luYyAoKSA9PiB7XG4gICAgICAgICAgYXdhaXQgY2RuSGFuZGxlci5nZXQoXCJwb3N0c1wiLCBnZXRDdHgpO1xuICAgICAgICAgIGV4cGVjdChpbnZhbGlkYXRlZFBhdGhzKCkpLnRvRXF1YWwoW10pO1xuXG4gICAgICAgICAgYXdhaXQgY2RuSGFuZGxlci5zZXQoXCJwb3N0c1wiLCByZWdlbmVyYXRlZCwge1xuICAgICAgICAgICAgY2FjaGVDb250cm9sOiB7IHJldmFsaWRhdGU6IGZhbHNlLCBleHBpcmU6IHVuZGVmaW5lZCB9LFxuICAgICAgICAgICAgaXNSb3V0ZVBQUkVuYWJsZWQ6IGZhbHNlLFxuICAgICAgICAgICAgaXNGYWxsYmFjazogZmFsc2UsXG4gICAgICAgICAgfSBhcyBhbnkpO1xuXG4gICAgICAgICAgZXhwZWN0KGludmFsaWRhdGVkUGF0aHMoKSkudG9FcXVhbChbW1wiL3Bvc3RzKlwiXV0pO1xuXG4gICAgICAgICAgLy8gT25jZTogYSBsYXRlciB3cml0ZSBvZiB0aGUgc2FtZSBwYWdlIGlzIGFuIG9yZGluYXJ5IG9uZS5cbiAgICAgICAgICBhd2FpdCBjZG5IYW5kbGVyLnNldChcInBvc3RzXCIsIHJlZ2VuZXJhdGVkLCB7XG4gICAgICAgICAgICBjYWNoZUNvbnRyb2w6IHsgcmV2YWxpZGF0ZTogZmFsc2UsIGV4cGlyZTogdW5kZWZpbmVkIH0sXG4gICAgICAgICAgICBpc1JvdXRlUFBSRW5hYmxlZDogZmFsc2UsXG4gICAgICAgICAgICBpc0ZhbGxiYWNrOiBmYWxzZSxcbiAgICAgICAgICB9IGFzIGFueSk7XG4gICAgICAgICAgZXhwZWN0KGludmFsaWRhdGVkUGF0aHMoKSkudG9IYXZlTGVuZ3RoKDEpO1xuICAgICAgICB9KTtcblxuICAgICAgICBpdChcInJlbWVtYmVycyBhIHN0YWxlIG1lbW9yeSBoaXQgYnkgdGhlIGtleSBpdCB3YXMgcmVhZCB1bmRlclwiLCBhc3luYyAoKSA9PiB7XG4gICAgICAgICAgYXdhaXQgY2RuSGFuZGxlci5pc1JldmFsaWRhdGVkKHN0b3JlZCwgZ2V0Q3R4LCBcInBvc3RzXCIpO1xuICAgICAgICAgIGF3YWl0IGNkbkhhbmRsZXIuc2V0KFwicG9zdHNcIiwgcmVnZW5lcmF0ZWQsIHtcbiAgICAgICAgICAgIGlzUm91dGVQUFJFbmFibGVkOiBmYWxzZSxcbiAgICAgICAgICAgIGlzRmFsbGJhY2s6IGZhbHNlLFxuICAgICAgICAgIH0gYXMgYW55KTtcblxuICAgICAgICAgIGV4cGVjdChpbnZhbGlkYXRlZFBhdGhzKCkpLnRvRXF1YWwoW1tcIi9wb3N0cypcIl1dKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgaXQoXCJkb2VzIG5vdCBpbnZhbGlkYXRlIGFmdGVyIGFuIG9yZGluYXJ5IHdyaXRlXCIsIGFzeW5jICgpID0+IHtcbiAgICAgICAgICBhd2FpdCBjZG5IYW5kbGVyLnNldChcInBvc3RzXCIsIHJlZ2VuZXJhdGVkLCB7XG4gICAgICAgICAgICBpc1JvdXRlUFBSRW5hYmxlZDogZmFsc2UsXG4gICAgICAgICAgICBpc0ZhbGxiYWNrOiBmYWxzZSxcbiAgICAgICAgICB9IGFzIGFueSk7XG5cbiAgICAgICAgICBleHBlY3QoaW52YWxpZGF0ZWRQYXRocygpKS50b0VxdWFsKFtdKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgaXQoXCJmb3JnZXRzIGEgc3RhbGUgcGFnZSB0aGF0IGlzIGRlbGV0ZWQgaW5zdGVhZCBvZiByZWdlbmVyYXRlZFwiLCBhc3luYyAoKSA9PiB7XG4gICAgICAgICAgYXdhaXQgY2RuSGFuZGxlci5nZXQoXCJwb3N0c1wiLCBnZXRDdHgpO1xuICAgICAgICAgIGF3YWl0IGNkbkhhbmRsZXIuc2V0KFwicG9zdHNcIiwgbnVsbCwge1xuICAgICAgICAgICAgaXNSb3V0ZVBQUkVuYWJsZWQ6IGZhbHNlLFxuICAgICAgICAgICAgaXNGYWxsYmFjazogZmFsc2UsXG4gICAgICAgICAgfSBhcyBhbnkpO1xuICAgICAgICAgIGF3YWl0IGNkbkhhbmRsZXIuc2V0KFwicG9zdHNcIiwgcmVnZW5lcmF0ZWQsIHtcbiAgICAgICAgICAgIGlzUm91dGVQUFJFbmFibGVkOiBmYWxzZSxcbiAgICAgICAgICAgIGlzRmFsbGJhY2s6IGZhbHNlLFxuICAgICAgICAgIH0gYXMgYW55KTtcblxuICAgICAgICAgIGV4cGVjdChpbnZhbGlkYXRlZFBhdGhzKCkpLnRvRXF1YWwoW10pO1xuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgaXQoXCJzaG91bGQgaGFuZGxlIFMzIGVycm9ycyBhbmQgcmV0dXJuIG51bGxcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgbW9ja1MzU2VuZC5tb2NrUmVqZWN0ZWRWYWx1ZU9uY2UobmV3IEVycm9yKFwiUzMgRXJyb3JcIikpO1xuXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBoYW5kbGVyLmdldChcImVycm9yLWtleVwiLCB7XG4gICAgICAgIGtpbmQ6IEluY3JlbWVudGFsQ2FjaGVLaW5kLkFQUF9QQUdFLFxuICAgICAgICBpc0ZhbGxiYWNrOiBmYWxzZSxcbiAgICAgIH0pO1xuICAgICAgZXhwZWN0KHJlc3VsdCkudG9CZU51bGwoKTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoXCJ0YWcgbWFya2VyIGNhY2hlXCIsICgpID0+IHtcbiAgICAvLyBFdmVyeSBjYWNoZSBoaXQgY2hlY2tzIGl0cyB0YWdzJyBtYXJrZXJzLCBhbmQgYWxsIG9mIGEgZGVwbG95bWVudCdzXG4gICAgLy8gbWFya2VycyBzaGFyZSBvbmUgcGFydGl0aW9uIGtleTogcmVhZCBwZXIgcmVxdWVzdCwgdGhleSBjYXBwZWQgYSByb3V0ZSdzXG4gICAgLy8gY2FjaGUgaGl0cyBhdCB+MSw5MDAgcmVxL3MuIEVhY2ggaW5zdGFuY2UgaG9sZHMgdGhlbSBmb3IgYSBzZWNvbmQuXG4gICAgY29uc3QgbGFzdE1vZGlmaWVkID0gRGF0ZS5ub3coKSAtIDYwXzAwMDtcbiAgICBjb25zdCBzdG9yZWQgPSB7XG4gICAgICBsYXN0TW9kaWZpZWQsXG4gICAgICB2YWx1ZToge1xuICAgICAgICBraW5kOiBDYWNoZWRSb3V0ZUtpbmQuQVBQX1BBR0UsXG4gICAgICAgIGh0bWw6IFwiPGh0bWw+Y2FjaGVkPC9odG1sPlwiLFxuICAgICAgICBoZWFkZXJzOiB7IFwieC1uZXh0LWNhY2hlLXRhZ3NcIjogXCJfTl9UXy9wYWdlLHBvc3RzXCIgfSxcbiAgICAgIH0sXG4gICAgfTtcbiAgICBjb25zdCBnZXRDdHggPSB7XG4gICAgICBraW5kOiBJbmNyZW1lbnRhbENhY2hlS2luZC5BUFBfUEFHRSxcbiAgICAgIGlzRmFsbGJhY2s6IGZhbHNlLFxuICAgIH0gYXMgY29uc3Q7XG4gICAgY29uc3QgbWFya2VyUmVhZHMgPSAoKSA9PlxuICAgICAgbW9ja0R5bmFtb1NlbmQubW9jay5jYWxscy5maWx0ZXIoXG4gICAgICAgIChbY29tbWFuZF0pID0+IGNvbW1hbmQgaW5zdGFuY2VvZiBCYXRjaEdldEl0ZW1Db21tYW5kLFxuICAgICAgKS5sZW5ndGg7XG4gICAgLy8gV2hhdCBgZ2V0YCBhbnN3ZXJzIGZvciBhIHJldmFsaWRhdGVkIHBhZ2U6IGV4cGlyZWQsIG5vdCBhIG1pc3MuXG4gICAgY29uc3QgZXhwaXJlZCA9IHsgbGFzdE1vZGlmaWVkOiAtMSB9O1xuICAgIGNvbnN0IHJldmFsaWRhdGVkQWZ0ZXJTdG9yZSA9IHtcbiAgICAgIGdldDogeyBJdGVtOiB7IHJldmFsaWRhdGVkQXQ6IHsgTjogU3RyaW5nKGxhc3RNb2RpZmllZCArIDEwMDApIH0gfSB9LFxuICAgIH07XG5cbiAgICBiZWZvcmVFYWNoKCgpID0+IHtcbiAgICAgIG1vY2tTM1NlbmQubW9ja0ltcGxlbWVudGF0aW9uKChjb21tYW5kOiB1bmtub3duKSA9PlxuICAgICAgICBQcm9taXNlLnJlc29sdmUoXG4gICAgICAgICAgY29tbWFuZCBpbnN0YW5jZW9mIEdldE9iamVjdENvbW1hbmRcbiAgICAgICAgICAgID8ge1xuICAgICAgICAgICAgICAgIEJvZHk6IHtcbiAgICAgICAgICAgICAgICAgIHRyYW5zZm9ybVRvU3RyaW5nOiBqZXN0XG4gICAgICAgICAgICAgICAgICAgIC5mbigpXG4gICAgICAgICAgICAgICAgICAgIC5tb2NrUmVzb2x2ZWRWYWx1ZShKU09OLnN0cmluZ2lmeShzdG9yZWQpKSxcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIENvbnRlbnRUeXBlOiBcImFwcGxpY2F0aW9uL2pzb25cIixcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgOiB7fSxcbiAgICAgICAgKSxcbiAgICAgICk7XG4gICAgICBkeW5hbW9SZXNwb25zZXMoe30pO1xuICAgIH0pO1xuXG4gICAgaXQoXCJhbnN3ZXJzIGNoZWNrcyB3aXRoaW4gdGhlIFRUTCBmcm9tIHRoZSBtYXJrZXJzIGl0IGFscmVhZHkgcmVhZFwiLCBhc3luYyAoKSA9PiB7XG4gICAgICBhd2FpdCBoYW5kbGVyLmdldChcInBhZ2VcIiwgZ2V0Q3R4KTtcbiAgICAgIGF3YWl0IGhhbmRsZXIuZ2V0KFwicGFnZVwiLCBnZXRDdHgpO1xuXG4gICAgICBleHBlY3QobWFya2VyUmVhZHMoKSkudG9CZSgxKTtcbiAgICB9KTtcblxuICAgIGl0KFwic2hhcmVzIG9uZSByZWFkIGJldHdlZW4gY29uY3VycmVudCBjaGVja3NcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgYXdhaXQgUHJvbWlzZS5hbGwoW1xuICAgICAgICBoYW5kbGVyLmdldChcInBhZ2VcIiwgZ2V0Q3R4KSxcbiAgICAgICAgaGFuZGxlci5nZXQoXCJwYWdlXCIsIGdldEN0eCksXG4gICAgICAgIGhhbmRsZXIuZ2V0KFwicGFnZVwiLCBnZXRDdHgpLFxuICAgICAgXSk7XG5cbiAgICAgIGV4cGVjdChtYXJrZXJSZWFkcygpKS50b0JlKDEpO1xuICAgIH0pO1xuXG4gICAgaXQoXCJzZWVzIGl0cyBvd24gcmV2YWxpZGF0ZVRhZyBhdCBvbmNlLCBhbmQgbG9ncyBpdCBmb3IgdGhlIG90aGVyc1wiLCBhc3luYyAoKSA9PiB7XG4gICAgICBleHBlY3QoYXdhaXQgaGFuZGxlci5nZXQoXCJwYWdlXCIsIGdldEN0eCkpLnRvTWF0Y2hPYmplY3QoeyBsYXN0TW9kaWZpZWQgfSk7XG5cbiAgICAgIGR5bmFtb1Jlc3BvbnNlcyhyZXZhbGlkYXRlZEFmdGVyU3RvcmUpO1xuICAgICAgYXdhaXQgaGFuZGxlci5yZXZhbGlkYXRlVGFnKFwicG9zdHNcIik7XG5cbiAgICAgIGV4cGVjdChhd2FpdCBoYW5kbGVyLmdldChcInBhZ2VcIiwgZ2V0Q3R4KSkudG9NYXRjaE9iamVjdChleHBpcmVkKTtcbiAgICAgIGNvbnN0IHB1dHMgPSBtb2NrRHluYW1vU2VuZC5tb2NrLmNhbGxzLmZpbHRlcihcbiAgICAgICAgKFtjb21tYW5kXSkgPT4gY29tbWFuZCBpbnN0YW5jZW9mIFB1dEl0ZW1Db21tYW5kLFxuICAgICAgKTtcbiAgICAgIGV4cGVjdChwdXRzKS50b0hhdmVMZW5ndGgoMSk7XG4gICAgICBleHBlY3QoY29tbWFuZElucHV0KHB1dHNbMF1bMF0sIFB1dEl0ZW1Db21tYW5kKS5JdGVtKS50b01hdGNoT2JqZWN0KHtcbiAgICAgICAgcGs6IHsgUzogXCJ0ZXN0LWJ1aWxkLWlkI2xvZ1wiIH0sXG4gICAgICAgIHNrOiB7IFM6IGV4cGVjdC5zdHJpbmdNYXRjaGluZygvXlxcZHsxNX0jcG9zdHMkLykgfSxcbiAgICAgICAgcmV2YWxpZGF0ZWRBdDogeyBOOiBleHBlY3QuYW55KFN0cmluZykgfSxcbiAgICAgICAgdHRsOiB7IE46IGV4cGVjdC5hbnkoU3RyaW5nKSB9LFxuICAgICAgfSk7XG4gICAgfSk7XG5cbiAgICBpdChcIndyaXRlcyBlYWNoIHRhZyBvbmNlIHdoZW4gdGhlICd1c2UgY2FjaGUnIGhhbmRsZXJzIGdldCB0aGUgc2FtZSBjYWxsXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIC8vIE5leHQuanMgY2FsbHMgYHJldmFsaWRhdGVUYWdgIGhlcmUgYW5kIGB1cGRhdGVUYWdzYCBvbiBldmVyeVxuICAgICAgLy8gYGNhY2hlSGFuZGxlcnNgIGVudHJ5LCBhbmQgYWxsIG9mIHRoZW0gc2hhcmUgb25lIG1hbmlmZXN0LlxuICAgICAgYXdhaXQgUHJvbWlzZS5hbGwoW1xuICAgICAgICBoYW5kbGVyLnJldmFsaWRhdGVUYWcoW1wicG9zdHNcIl0pLFxuICAgICAgICBzaGFyZWRUYWdNYW5pZmVzdCgpLnVwZGF0ZShbXCJwb3N0c1wiXSwgdW5kZWZpbmVkKSxcbiAgICAgIF0pO1xuXG4gICAgICBjb25zdCB3cml0ZXMgPSBtb2NrRHluYW1vU2VuZC5tb2NrLmNhbGxzLmZpbHRlcihcbiAgICAgICAgKFtjb21tYW5kXSkgPT5cbiAgICAgICAgICBjb21tYW5kIGluc3RhbmNlb2YgVXBkYXRlSXRlbUNvbW1hbmQgfHxcbiAgICAgICAgICBjb21tYW5kIGluc3RhbmNlb2YgUHV0SXRlbUNvbW1hbmQsXG4gICAgICApO1xuICAgICAgZXhwZWN0KHdyaXRlcykudG9IYXZlTGVuZ3RoKDIpO1xuICAgIH0pO1xuICB9KTtcblxuICBkZXNjcmliZShcInNldFwiLCAoKSA9PiB7XG4gICAgLy8gTWFwcGluZyByb3dzIGV4aXN0IG9ubHkgdG8gbmFtZSBDbG91ZEZyb250IHBhdGhzLCBzbyBvbmx5IGEgZGVwbG95bWVudFxuICAgIC8vIHdpdGggYSBkaXN0cmlidXRpb24ga2VlcHMgdGhlbS5cbiAgICBiZWZvcmVFYWNoKCgpID0+IHtcbiAgICAgIHByb2Nlc3MuZW52LkNES19ORVhUSlNfRElTVFJJQlVUSU9OX0lEX1BBUkFNX05BTUUgPSBcInRlc3QtcGFyYW0tbmFtZVwiO1xuICAgICAgaGFuZGxlciA9IG5ldyBTM0NhY2hlSGFuZGxlcih7IGNvbnRleHQ6IG1vY2tDb250ZXh0IH0pO1xuICAgIH0pO1xuXG4gICAgaXQoXCJrZWVwcyBubyBtYXBwaW5nIHJvd3Mgd2l0aG91dCBhIGRpc3RyaWJ1dGlvbiB0byBpbnZhbGlkYXRlXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIC8vIFRoZSBSZWdpb25hbCBjb25zdHJ1Y3RzIGhhdmUgbm8gQ2xvdWRGcm9udC4gQSByb3cgcGVyIHRhZyBwZXIgYHNldGAsXG4gICAgICAvLyBhbmQgdGhlIFMzIHJlYWQgYmVmb3JlIGEgZGVsZXRlIHRvIG5hbWUgdGhlbSwgYm91Z2h0IG5vdGhpbmcgdGhlcmUuXG4gICAgICBkZWxldGUgcHJvY2Vzcy5lbnYuQ0RLX05FWFRKU19ESVNUUklCVVRJT05fSURfUEFSQU1fTkFNRTtcbiAgICAgIGNvbnN0IHJlZ2lvbmFsID0gbmV3IFMzQ2FjaGVIYW5kbGVyKHsgY29udGV4dDogbW9ja0NvbnRleHQgfSk7XG4gICAgICBtb2NrUzNTZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHt9KTtcbiAgICAgIG1vY2tEeW5hbW9TZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHt9KTtcblxuICAgICAgYXdhaXQgcmVnaW9uYWwuc2V0KFxuICAgICAgICBcIi9pc3IvMVwiLFxuICAgICAgICB7XG4gICAgICAgICAga2luZDogQ2FjaGVkUm91dGVLaW5kLkFQUF9QQUdFLFxuICAgICAgICAgIGh0bWw6IFwiPHAvPlwiLFxuICAgICAgICAgIHJzY0RhdGE6IHVuZGVmaW5lZCxcbiAgICAgICAgICBoZWFkZXJzOiB7IFwieC1uZXh0LWNhY2hlLXRhZ3NcIjogXCJ0YWdcIiB9LFxuICAgICAgICAgIHBvc3Rwb25lZDogdW5kZWZpbmVkLFxuICAgICAgICAgIHNlZ21lbnREYXRhOiB1bmRlZmluZWQsXG4gICAgICAgICAgc3RhdHVzOiB1bmRlZmluZWQsXG4gICAgICAgIH0sXG4gICAgICAgIGNyZWF0ZVNldENvbnRleHQoW10pLFxuICAgICAgKTtcbiAgICAgIGF3YWl0IHJlZ2lvbmFsLnNldChcIi9pc3IvMVwiLCBudWxsLCB7IGlzRmFsbGJhY2s6IGZhbHNlIH0pO1xuXG4gICAgICBleHBlY3QobW9ja0R5bmFtb1NlbmQpLm5vdC50b0hhdmVCZWVuQ2FsbGVkKCk7XG4gICAgICBleHBlY3QobW9ja1MzU2VuZCkubm90LnRvSGF2ZUJlZW5DYWxsZWRXaXRoKGV4cGVjdC5hbnkoR2V0T2JqZWN0Q29tbWFuZCkpO1xuICAgICAgZXhwZWN0KG1vY2tTM1NlbmQpLnRvSGF2ZUJlZW5DYWxsZWRXaXRoKGV4cGVjdC5hbnkoRGVsZXRlT2JqZWN0Q29tbWFuZCkpO1xuICAgIH0pO1xuXG4gICAgaXQoXCJzaG91bGQgc3RvcmUgY2FjaGUgdmFsdWUgaW4gUzNcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgY29uc3QgdGVzdERhdGE6IEluY3JlbWVudGFsQ2FjaGVWYWx1ZSA9IHtcbiAgICAgICAga2luZDogQ2FjaGVkUm91dGVLaW5kLkFQUF9QQUdFLFxuICAgICAgICBodG1sOiBcIjxodG1sPnNldCB0ZXN0PC9odG1sPlwiLFxuICAgICAgICByc2NEYXRhOiB1bmRlZmluZWQsXG4gICAgICAgIGhlYWRlcnM6IHVuZGVmaW5lZCxcbiAgICAgICAgcG9zdHBvbmVkOiB1bmRlZmluZWQsXG4gICAgICAgIHNlZ21lbnREYXRhOiB1bmRlZmluZWQsXG4gICAgICAgIHN0YXR1czogdW5kZWZpbmVkLFxuICAgICAgfTtcblxuICAgICAgbW9ja1MzU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZU9uY2Uoe30pO1xuICAgICAgbW9ja0R5bmFtb1NlbmQubW9ja1Jlc29sdmVkVmFsdWUoe30pO1xuXG4gICAgICBhd2FpdCBoYW5kbGVyLnNldChcInNldC1rZXlcIiwgdGVzdERhdGEsIGNyZWF0ZVNldENvbnRleHQoW1widGFnMVwiXSkpO1xuXG4gICAgICBleHBlY3QobW9ja1MzU2VuZCkudG9IYXZlQmVlbkNhbGxlZFdpdGgoZXhwZWN0LmFueShQdXRPYmplY3RDb21tYW5kKSk7XG4gICAgICBleHBlY3QobW9ja0R5bmFtb1NlbmQpLnRvSGF2ZUJlZW5DYWxsZWRXaXRoKFxuICAgICAgICBleHBlY3QuYW55KFVwZGF0ZUl0ZW1Db21tYW5kKSxcbiAgICAgICk7XG4gICAgfSk7XG5cbiAgICBpdChcInNob3VsZCBub3Qgc3RvcmUgd2hlbiBTMyBidWNrZXQgaXMgbm90IGNvbmZpZ3VyZWRcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgZGVsZXRlIHByb2Nlc3MuZW52LkNES19ORVhUSlNfQ0FDSEVfQlVDS0VUX05BTUU7XG5cbiAgICAgIGNvbnN0IGhhbmRsZXJXaXRob3V0QnVja2V0ID0gbmV3IFMzQ2FjaGVIYW5kbGVyKHtcbiAgICAgICAgY29udGV4dDogbW9ja0NvbnRleHQsXG4gICAgICB9KTtcblxuICAgICAgY29uc3QgdGVzdERhdGE6IEluY3JlbWVudGFsQ2FjaGVWYWx1ZSA9IHtcbiAgICAgICAga2luZDogQ2FjaGVkUm91dGVLaW5kLkFQUF9QQUdFLFxuICAgICAgICBodG1sOiBcIjxodG1sPm5vIGJ1Y2tldDwvaHRtbD5cIixcbiAgICAgICAgcnNjRGF0YTogdW5kZWZpbmVkLFxuICAgICAgICBoZWFkZXJzOiB1bmRlZmluZWQsXG4gICAgICAgIHBvc3Rwb25lZDogdW5kZWZpbmVkLFxuICAgICAgICBzZWdtZW50RGF0YTogdW5kZWZpbmVkLFxuICAgICAgICBzdGF0dXM6IHVuZGVmaW5lZCxcbiAgICAgIH07XG5cbiAgICAgIGF3YWl0IGhhbmRsZXJXaXRob3V0QnVja2V0LnNldChcbiAgICAgICAgXCJuby1idWNrZXQta2V5XCIsXG4gICAgICAgIHRlc3REYXRhLFxuICAgICAgICBjcmVhdGVTZXRDb250ZXh0KFtdKSxcbiAgICAgICk7XG5cbiAgICAgIGV4cGVjdChtb2NrUzNTZW5kKS5ub3QudG9IYXZlQmVlbkNhbGxlZCgpO1xuICAgIH0pO1xuXG4gICAgaXQoXCJtYXBzIHRoZSB0YWdzIGEgcGFnZSBjYXJyaWVzIG9ubHkgaW4gaXRzIHgtbmV4dC1jYWNoZS10YWdzIGhlYWRlclwiLCBhc3luYyAoKSA9PiB7XG4gICAgICAvLyBgUmVzcG9uc2VDYWNoZS5zZXRgIGJ1aWxkcyBhIHBhZ2UncyBgY3R4YCB3aXRob3V0IHRhZ3MsIHNvIHRoZSByZW5kZXInc1xuICAgICAgLy8gaGVhZGVyIGlzIHRoZSBvbmx5IHBsYWNlIHRoZXkgZXhpc3QuIE5vIG1hcHBpbmcgcm93IG1lYW50IGByZXZhbGlkYXRlVGFnYFxuICAgICAgLy8gZm91bmQgbm90aGluZyB0byBpbnZhbGlkYXRlIGFuZCBDbG91ZEZyb250IGtlcHQgdGhlIHN0YWxlIEhUTUwuXG4gICAgICBjb25zdCB0ZXN0RGF0YTogSW5jcmVtZW50YWxDYWNoZVZhbHVlID0ge1xuICAgICAgICBraW5kOiBDYWNoZWRSb3V0ZUtpbmQuQVBQX1BBR0UsXG4gICAgICAgIGh0bWw6IFwiPGh0bWw+dGFnZ2VkPC9odG1sPlwiLFxuICAgICAgICByc2NEYXRhOiB1bmRlZmluZWQsXG4gICAgICAgIGhlYWRlcnM6IHsgXCJ4LW5leHQtY2FjaGUtdGFnc1wiOiBcImhlYWRlci10YWcsb3RoZXItdGFnXCIgfSxcbiAgICAgICAgcG9zdHBvbmVkOiB1bmRlZmluZWQsXG4gICAgICAgIHNlZ21lbnREYXRhOiB1bmRlZmluZWQsXG4gICAgICAgIHN0YXR1czogdW5kZWZpbmVkLFxuICAgICAgfTtcblxuICAgICAgbW9ja1MzU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7fSk7XG4gICAgICBtb2NrRHluYW1vU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7fSk7XG5cbiAgICAgIGF3YWl0IGhhbmRsZXIuc2V0KFwiL2lzci8xXCIsIHRlc3REYXRhLCBjcmVhdGVTZXRDb250ZXh0KFtdKSk7XG5cbiAgICAgIGNvbnN0IG1hcHBpbmdLZXlzID0gKFVwZGF0ZUl0ZW1Db21tYW5kIGFzIHVua25vd24gYXMgamVzdC5Nb2NrKS5tb2NrLmNhbGxzXG4gICAgICAgIC5tYXAoKFtpbnB1dF0pID0+IGlucHV0LktleS5zay5TKVxuICAgICAgICAuc29ydCgpO1xuICAgICAgZXhwZWN0KG1hcHBpbmdLZXlzKS50b0VxdWFsKFtcbiAgICAgICAgXCJoZWFkZXItdGFnI3Rlc3QtYnVpbGQtaWQvaXNyLzEuanNvblwiLFxuICAgICAgICBcIm90aGVyLXRhZyN0ZXN0LWJ1aWxkLWlkL2lzci8xLmpzb25cIixcbiAgICAgIF0pO1xuICAgIH0pO1xuXG4gICAgaXQoXCJwcmVmZXJzIHRoZSBjb250ZXh0J3MgdGFncyBvdmVyIHRoZSBoZWFkZXIgd2hlbiBib3RoIGFyZSBwcmVzZW50XCIsIGFzeW5jICgpID0+IHtcbiAgICAgIGNvbnN0IHRlc3REYXRhOiBJbmNyZW1lbnRhbENhY2hlVmFsdWUgPSB7XG4gICAgICAgIGtpbmQ6IENhY2hlZFJvdXRlS2luZC5BUFBfUEFHRSxcbiAgICAgICAgaHRtbDogXCI8aHRtbD50YWdnZWQ8L2h0bWw+XCIsXG4gICAgICAgIHJzY0RhdGE6IHVuZGVmaW5lZCxcbiAgICAgICAgaGVhZGVyczogeyBcIngtbmV4dC1jYWNoZS10YWdzXCI6IFwiaGVhZGVyLXRhZ1wiIH0sXG4gICAgICAgIHBvc3Rwb25lZDogdW5kZWZpbmVkLFxuICAgICAgICBzZWdtZW50RGF0YTogdW5kZWZpbmVkLFxuICAgICAgICBzdGF0dXM6IHVuZGVmaW5lZCxcbiAgICAgIH07XG5cbiAgICAgIG1vY2tTM1NlbmQubW9ja1Jlc29sdmVkVmFsdWUoe30pO1xuICAgICAgbW9ja0R5bmFtb1NlbmQubW9ja1Jlc29sdmVkVmFsdWUoe30pO1xuXG4gICAgICBhd2FpdCBoYW5kbGVyLnNldChcIi9pc3IvMVwiLCB0ZXN0RGF0YSwgY3JlYXRlU2V0Q29udGV4dChbXCJjdHgtdGFnXCJdKSk7XG5cbiAgICAgIGNvbnN0IG1hcHBpbmdLZXlzID0gKFVwZGF0ZUl0ZW1Db21tYW5kIGFzIHVua25vd24gYXMgamVzdC5Nb2NrKS5tb2NrLmNhbGxzXG4gICAgICAgIC5tYXAoKFtpbnB1dF0pID0+IGlucHV0LktleS5zay5TKVxuICAgICAgICAuc29ydCgpO1xuICAgICAgZXhwZWN0KG1hcHBpbmdLZXlzKS50b0VxdWFsKFtcImN0eC10YWcjdGVzdC1idWlsZC1pZC9pc3IvMS5qc29uXCJdKTtcbiAgICB9KTtcblxuICAgIGl0KFwicmVtb3ZlcyBhIGRlbGV0ZWQgcGFnZSdzIG1hcHBpbmcgcm93cywgcmVhZGluZyBpdHMgdGFncyBmcm9tIFMzXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIC8vIEEgcmVzcG9uc2UgZGVsZXRlIC0gYSBjYWNoZWQgcm91dGUgdGhhdCBzdGFydHMgYW5zd2VyaW5nIGBub3RGb3VuZCgpYCAtXG4gICAgICAvLyBhcnJpdmVzIGFzIGBzZXQoa2V5LCBudWxsLCB7IGNhY2hlQ29udHJvbCwgLi4uIH0pYCB3aXRoIG5vIHRhZ3MgYXQgYWxsLFxuICAgICAgLy8gc28gdGhlIGVudHJ5IGl0c2VsZiBpcyB0aGUgb25seSBwbGFjZSBpdHMgbWFwcGluZyByb3dzIGNhbiBiZSBuYW1lZFxuICAgICAgLy8gZnJvbS4gTGVmdCBiZWhpbmQsIHRoZXkgcmVzb2x2ZSBvbiB0aGUgbmV4dCBgcmV2YWxpZGF0ZVRhZ2AgdG8gYSBjYWNoZVxuICAgICAgLy8ga2V5IHdob3NlIG9iamVjdCBpcyBnb25lIGFuZCBzcGVuZCBhIENsb3VkRnJvbnQgd2lsZGNhcmQgcGF0aCBvbiBpdC5cbiAgICAgIGNvbnN0IHN0b3JlZCA9IHtcbiAgICAgICAgbGFzdE1vZGlmaWVkOiBEYXRlLm5vdygpLFxuICAgICAgICB0YWdzOiBbXCJjb2xsZWN0aW9uXCIsIFwiX05fVF8vaXNyLzFcIl0sXG4gICAgICAgIHZhbHVlOiB7XG4gICAgICAgICAga2luZDogQ2FjaGVkUm91dGVLaW5kLkFQUF9QQUdFLFxuICAgICAgICAgIGh0bWw6IFwiPGh0bWw+Z29uZTwvaHRtbD5cIixcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgICBtb2NrUzNTZW5kLm1vY2tJbXBsZW1lbnRhdGlvbigoY29tbWFuZDogdW5rbm93bikgPT5cbiAgICAgICAgUHJvbWlzZS5yZXNvbHZlKFxuICAgICAgICAgIGNvbW1hbmQgaW5zdGFuY2VvZiBHZXRPYmplY3RDb21tYW5kXG4gICAgICAgICAgICA/IHtcbiAgICAgICAgICAgICAgICBCb2R5OiB7XG4gICAgICAgICAgICAgICAgICB0cmFuc2Zvcm1Ub1N0cmluZzogamVzdFxuICAgICAgICAgICAgICAgICAgICAuZm4oKVxuICAgICAgICAgICAgICAgICAgICAubW9ja1Jlc29sdmVkVmFsdWUoSlNPTi5zdHJpbmdpZnkoc3RvcmVkKSksXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBDb250ZW50VHlwZTogXCJhcHBsaWNhdGlvbi9qc29uXCIsXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIDoge30sXG4gICAgICAgICksXG4gICAgICApO1xuICAgICAgbW9ja0R5bmFtb1NlbmQubW9ja1Jlc29sdmVkVmFsdWUoe30pO1xuXG4gICAgICBhd2FpdCBoYW5kbGVyLnNldChcIi9pc3IvMVwiLCBudWxsLCB7XG4gICAgICAgIGNhY2hlQ29udHJvbDogeyByZXZhbGlkYXRlOiA2MCwgZXhwaXJlOiB1bmRlZmluZWQgfSxcbiAgICAgICAgaXNSb3V0ZVBQUkVuYWJsZWQ6IGZhbHNlLFxuICAgICAgICBpc0ZhbGxiYWNrOiBmYWxzZSxcbiAgICAgIH0pO1xuXG4gICAgICBleHBlY3QobW9ja1MzU2VuZCkudG9IYXZlQmVlbkNhbGxlZFdpdGgoZXhwZWN0LmFueShEZWxldGVPYmplY3RDb21tYW5kKSk7XG4gICAgICBjb25zdCBkZWxldGVkS2V5cyA9IChEZWxldGVJdGVtQ29tbWFuZCBhcyB1bmtub3duIGFzIGplc3QuTW9jaykubW9jay5jYWxsc1xuICAgICAgICAubWFwKChbaW5wdXRdKSA9PiBpbnB1dC5LZXkuc2suUylcbiAgICAgICAgLnNvcnQoKTtcbiAgICAgIC8vIE1hcHBpbmcgcm93cyBvbmx5LiBUaGUgYmFyZS10YWcgbWFya2VyIHJvdyBiZWxvbmdzIHRvIHRoZSB0YWcsIGFuZFxuICAgICAgLy8gYGNoZWNrSWZSZXZhbGlkYXRlZGAgcmVhZHMgaXQgZm9yIGV2ZXJ5IG90aGVyIGVudHJ5IGNhcnJ5aW5nIGl0LlxuICAgICAgZXhwZWN0KGRlbGV0ZWRLZXlzKS50b0VxdWFsKFtcbiAgICAgICAgXCJfTl9UXy9pc3IvMSN0ZXN0LWJ1aWxkLWlkL2lzci8xLmpzb25cIixcbiAgICAgICAgXCJjb2xsZWN0aW9uI3Rlc3QtYnVpbGQtaWQvaXNyLzEuanNvblwiLFxuICAgICAgXSk7XG4gICAgfSk7XG5cbiAgICBpdChcImRlbGV0ZXMgYSBmZXRjaCBlbnRyeSdzIG1hcHBpbmcgcm93cyBmcm9tIHRoZSBjb250ZXh0J3MgdGFncyBhbG9uZVwiLCBhc3luYyAoKSA9PiB7XG4gICAgICAvLyBgY3R4YCBjYXJyaWVzIHRoZW0gaGVyZSwgc28gdGhlIHByZS1kZWxldGUgcmVhZCBpcyBza2lwcGVkOiBvbmx5IHRoZVxuICAgICAgLy8gRGVsZXRlT2JqZWN0IHJlYWNoZXMgUzMuXG4gICAgICBtb2NrUzNTZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHt9KTtcbiAgICAgIG1vY2tEeW5hbW9TZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHt9KTtcblxuICAgICAgYXdhaXQgaGFuZGxlci5zZXQoXCJmZXRjaC1rZXlcIiwgbnVsbCwgY3JlYXRlU2V0Q29udGV4dChbXCJjdHgtdGFnXCJdKSk7XG5cbiAgICAgIGV4cGVjdChtb2NrUzNTZW5kKS5ub3QudG9IYXZlQmVlbkNhbGxlZFdpdGgoZXhwZWN0LmFueShHZXRPYmplY3RDb21tYW5kKSk7XG4gICAgICBjb25zdCBkZWxldGVkS2V5cyA9IChcbiAgICAgICAgRGVsZXRlSXRlbUNvbW1hbmQgYXMgdW5rbm93biBhcyBqZXN0Lk1vY2tcbiAgICAgICkubW9jay5jYWxscy5tYXAoKFtpbnB1dF0pID0+IGlucHV0LktleS5zay5TKTtcbiAgICAgIGV4cGVjdChkZWxldGVkS2V5cykudG9FcXVhbChbXCJjdHgtdGFnI3Rlc3QtYnVpbGQtaWQvZmV0Y2gta2V5Lmpzb25cIl0pO1xuICAgIH0pO1xuXG4gICAgaXQoXCJkZWxldGVzIG5vIG1hcHBpbmcgcm93cyBmb3IgYW4gdW50YWdnZWQgZW50cnlcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgbW9ja1MzU2VuZC5tb2NrSW1wbGVtZW50YXRpb24oKGNvbW1hbmQ6IHVua25vd24pID0+XG4gICAgICAgIFByb21pc2UucmVzb2x2ZShcbiAgICAgICAgICBjb21tYW5kIGluc3RhbmNlb2YgR2V0T2JqZWN0Q29tbWFuZFxuICAgICAgICAgICAgPyB7XG4gICAgICAgICAgICAgICAgQm9keToge1xuICAgICAgICAgICAgICAgICAgdHJhbnNmb3JtVG9TdHJpbmc6IGplc3QuZm4oKS5tb2NrUmVzb2x2ZWRWYWx1ZShcbiAgICAgICAgICAgICAgICAgICAgSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICAgICAgICAgICAgICAgIGxhc3RNb2RpZmllZDogRGF0ZS5ub3coKSxcbiAgICAgICAgICAgICAgICAgICAgICB2YWx1ZTogeyBraW5kOiBDYWNoZWRSb3V0ZUtpbmQuQVBQX1BBR0UsIGh0bWw6IFwiPHAvPlwiIH0sXG4gICAgICAgICAgICAgICAgICAgIH0pLFxuICAgICAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIENvbnRlbnRUeXBlOiBcImFwcGxpY2F0aW9uL2pzb25cIixcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgOiB7fSxcbiAgICAgICAgKSxcbiAgICAgICk7XG4gICAgICBtb2NrRHluYW1vU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7fSk7XG5cbiAgICAgIGF3YWl0IGhhbmRsZXIuc2V0KFwiL3VudGFnZ2VkXCIsIG51bGwsIHsgaXNGYWxsYmFjazogZmFsc2UgfSk7XG5cbiAgICAgIGV4cGVjdChtb2NrUzNTZW5kKS50b0hhdmVCZWVuQ2FsbGVkV2l0aChleHBlY3QuYW55KERlbGV0ZU9iamVjdENvbW1hbmQpKTtcbiAgICAgIGV4cGVjdChEZWxldGVJdGVtQ29tbWFuZCBhcyB1bmtub3duIGFzIGplc3QuTW9jaykubm90LnRvSGF2ZUJlZW5DYWxsZWQoKTtcbiAgICB9KTtcblxuICAgIGl0KFwic3RpbGwgZGVsZXRlcyB0aGUgb2JqZWN0IHdoZW4gaXRzIHRhZ3MgY2Fubm90IGJlIHJlYWRcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgbW9ja1MzU2VuZC5tb2NrSW1wbGVtZW50YXRpb24oKGNvbW1hbmQ6IHVua25vd24pID0+XG4gICAgICAgIGNvbW1hbmQgaW5zdGFuY2VvZiBHZXRPYmplY3RDb21tYW5kXG4gICAgICAgICAgPyBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJTMyBHZXQgRXJyb3JcIikpXG4gICAgICAgICAgOiBQcm9taXNlLnJlc29sdmUoe30pLFxuICAgICAgKTtcbiAgICAgIG1vY2tEeW5hbW9TZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHt9KTtcblxuICAgICAgYXdhaXQgZXhwZWN0KFxuICAgICAgICBoYW5kbGVyLnNldChcIi9pc3IvMVwiLCBudWxsLCB7IGlzRmFsbGJhY2s6IGZhbHNlIH0pLFxuICAgICAgKS5yZXNvbHZlcy5ub3QudG9UaHJvdygpO1xuXG4gICAgICBleHBlY3QobW9ja1MzU2VuZCkudG9IYXZlQmVlbkNhbGxlZFdpdGgoZXhwZWN0LmFueShEZWxldGVPYmplY3RDb21tYW5kKSk7XG4gICAgfSk7XG5cbiAgICBpdChcInNob3VsZCBoYW5kbGUgUzMgZXJyb3JzIGdyYWNlZnVsbHlcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgY29uc3QgdGVzdERhdGE6IEluY3JlbWVudGFsQ2FjaGVWYWx1ZSA9IHtcbiAgICAgICAga2luZDogQ2FjaGVkUm91dGVLaW5kLkFQUF9QQUdFLFxuICAgICAgICBodG1sOiBcIjxodG1sPmVycm9yIHRlc3Q8L2h0bWw+XCIsXG4gICAgICAgIHJzY0RhdGE6IHVuZGVmaW5lZCxcbiAgICAgICAgaGVhZGVyczogdW5kZWZpbmVkLFxuICAgICAgICBwb3N0cG9uZWQ6IHVuZGVmaW5lZCxcbiAgICAgICAgc2VnbWVudERhdGE6IHVuZGVmaW5lZCxcbiAgICAgICAgc3RhdHVzOiB1bmRlZmluZWQsXG4gICAgICB9O1xuXG4gICAgICBtb2NrUzNTZW5kLm1vY2tSZWplY3RlZFZhbHVlT25jZShuZXcgRXJyb3IoXCJTMyBQdXQgRXJyb3JcIikpO1xuXG4gICAgICAvLyBTaG91bGQgbm90IHRocm93XG4gICAgICBhd2FpdCBleHBlY3QoXG4gICAgICAgIGhhbmRsZXIuc2V0KFwiZXJyb3Ita2V5XCIsIHRlc3REYXRhLCBjcmVhdGVTZXRDb250ZXh0KFtdKSksXG4gICAgICApLnJlc29sdmVzLm5vdC50b1Rocm93KCk7XG4gICAgfSk7XG4gIH0pO1xuXG4gIGRlc2NyaWJlKFwicmV2YWxpZGF0ZVRhZ1wiLCAoKSA9PiB7XG4gICAgaXQoXCJzaG91bGQgcmVjb3JkIHRoZSByZXZhbGlkYXRpb24gd2l0aG91dCBkZWxldGluZyB0aGUgdGFnJ3MgUzMgZW50cmllc1wiLCBhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBtb2NrUXVlcnlSZXNwb25zZSA9IHtcbiAgICAgICAgSXRlbXM6IFtcbiAgICAgICAgICB7IHNrOiB7IFM6IFwidGVzdC10YWcjY2FjaGUta2V5LTFcIiB9IH0sXG4gICAgICAgICAgeyBzazogeyBTOiBcInRlc3QtdGFnI2NhY2hlLWtleS0yXCIgfSB9LFxuICAgICAgICBdLFxuICAgICAgfTtcblxuICAgICAgZHluYW1vUmVzcG9uc2VzKHsgcXVlcnk6IG1vY2tRdWVyeVJlc3BvbnNlIH0pO1xuICAgICAgbW9ja1MzU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7fSk7XG5cbiAgICAgIGF3YWl0IGhhbmRsZXIucmV2YWxpZGF0ZVRhZyhcInRlc3QtdGFnXCIpO1xuXG4gICAgICAvLyBObyBkaXN0cmlidXRpb24sIHNvIG5vIG1hcHBpbmcgcm93cyB0byByZWFkOiB0aGUgbWFya2VyIGlzIGFsbCBvZiBpdC5cbiAgICAgIGV4cGVjdChtb2NrRHluYW1vU2VuZCkubm90LnRvSGF2ZUJlZW5DYWxsZWRXaXRoKGV4cGVjdC5hbnkoUXVlcnlDb21tYW5kKSk7XG4gICAgICBleHBlY3QobW9ja0R5bmFtb1NlbmQpLnRvSGF2ZUJlZW5DYWxsZWRXaXRoKFxuICAgICAgICBleHBlY3QuYW55KFVwZGF0ZUl0ZW1Db21tYW5kKSxcbiAgICAgICk7XG4gICAgICAvLyBUaGUgbWFya2VyIHJvdyBpcyB0aGUgaW52YWxpZGF0aW9uOyBgZ2V0YCByZWFkcyBpdCBhbmQgaGFuZHMgdGhlIGVudHJ5XG4gICAgICAvLyBiYWNrIGV4cGlyZWQgc28gTmV4dC5qcyByZS1yZW5kZXJzIHRoZSByb3V0ZSBhbmQgcmVwbGFjZXMgdGhlIG9iamVjdC5cbiAgICAgIC8vIERlbGV0aW5nIHRoZSBvYmplY3RzIGhlcmUgbWFkZSB0aGUgbmV4dCByZXF1ZXN0IGEgaGFyZCBtaXNzLCB3aGljaCBvbiBhXG4gICAgICAvLyBQUFIgcm91dGUgaXMgYW5zd2VyZWQgZnJvbSB0aGUgdW5jYWNoZWFibGUgZmFsbGJhY2sgc2hlbGwgZm9yIGdvb2QuXG4gICAgICBleHBlY3QobW9ja1MzU2VuZCkubm90LnRvSGF2ZUJlZW5DYWxsZWRXaXRoKFxuICAgICAgICBleHBlY3QuYW55KERlbGV0ZU9iamVjdENvbW1hbmQpLFxuICAgICAgKTtcblxuICAgICAgLy8gVGhlIHRhZyBtYXJrZXIgYGNoZWNrSWZSZXZhbGlkYXRlZGAgcmVhZHM6IGtleWVkIGJ5IHRoZSBiYXJlIHRhZywgc29cbiAgICAgIC8vIGVudHJpZXMgd2l0aCBubyBtYXBwaW5nIHJvdyBvZiB0aGVpciBvd24gc3RpbGwgc2VlIHRoZSByZXZhbGlkYXRpb24uXG4gICAgICBjb25zdCBtYXJrZXJXcml0ZSA9IChVcGRhdGVJdGVtQ29tbWFuZCBhcyB1bmtub3duIGFzIGplc3QuTW9jaykubW9jay5jYWxsc1xuICAgICAgICAubWFwKChbaW5wdXRdKSA9PiBpbnB1dClcbiAgICAgICAgLmZpbmQoKGlucHV0KSA9PiBpbnB1dC5LZXkuc2suUyA9PT0gXCJ0ZXN0LXRhZ1wiKTtcbiAgICAgIGV4cGVjdChtYXJrZXJXcml0ZSkudG9NYXRjaE9iamVjdCh7XG4gICAgICAgIFVwZGF0ZUV4cHJlc3Npb246IFwiU0VUIHJldmFsaWRhdGVkQXQgPSA6cmV2YWxpZGF0ZWRBdFwiLFxuICAgICAgfSk7XG4gICAgfSk7XG5cbiAgICBpdChcInNob3VsZCBub3QgcmV2YWxpZGF0ZSB3aGVuIER5bmFtb0RCIHRhYmxlIGlzIG5vdCBjb25maWd1cmVkXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIGRlbGV0ZSBwcm9jZXNzLmVudi5DREtfTkVYVEpTX1JFVkFMSURBVElPTl9UQUJMRV9OQU1FO1xuXG4gICAgICBjb25zdCBoYW5kbGVyV2l0aG91dFRhYmxlID0gbmV3IFMzQ2FjaGVIYW5kbGVyKHtcbiAgICAgICAgY29udGV4dDogbW9ja0NvbnRleHQsXG4gICAgICB9KTtcblxuICAgICAgYXdhaXQgaGFuZGxlcldpdGhvdXRUYWJsZS5yZXZhbGlkYXRlVGFnKFwidGVzdC10YWdcIik7XG5cbiAgICAgIGV4cGVjdChtb2NrRHluYW1vU2VuZCkubm90LnRvSGF2ZUJlZW5DYWxsZWQoKTtcbiAgICB9KTtcblxuICAgIGl0KFwic2hvdWxkIGhhbmRsZSBEeW5hbW9EQiBlcnJvcnMgZ3JhY2VmdWxseVwiLCBhc3luYyAoKSA9PiB7XG4gICAgICBtb2NrRHluYW1vU2VuZC5tb2NrUmVqZWN0ZWRWYWx1ZU9uY2UobmV3IEVycm9yKFwiRHluYW1vREIgRXJyb3JcIikpO1xuXG4gICAgICAvLyBTaG91bGQgbm90IHRocm93XG4gICAgICBhd2FpdCBleHBlY3QoaGFuZGxlci5yZXZhbGlkYXRlVGFnKFwiZXJyb3ItdGFnXCIpKS5yZXNvbHZlcy5ub3QudG9UaHJvdygpO1xuICAgIH0pO1xuXG4gICAgaXQoXCJzaG91bGQgY3JlYXRlIGEgQ2xvdWRGcm9udCBpbnZhbGlkYXRpb24gZm9yIGFmZmVjdGVkIHBhdGhzIHdoZW4gYSBkaXN0cmlidXRpb24gcGFyYW1ldGVyIGlzIGNvbmZpZ3VyZWRcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgcHJvY2Vzcy5lbnYuQ0RLX05FWFRKU19ESVNUUklCVVRJT05fSURfUEFSQU1fTkFNRSA9IFwidGVzdC1wYXJhbS1uYW1lXCI7XG4gICAgICBjb25zdCBoYW5kbGVyV2l0aERpc3RyaWJ1dGlvbiA9IG5ldyBTM0NhY2hlSGFuZGxlcih7XG4gICAgICAgIGNvbnRleHQ6IG1vY2tDb250ZXh0LFxuICAgICAgfSk7XG5cbiAgICAgIGNvbnN0IG1vY2tRdWVyeVJlc3BvbnNlID0ge1xuICAgICAgICBJdGVtczogW1xuICAgICAgICAgIHsgc2s6IHsgUzogXCJ0ZXN0LXRhZyN0ZXN0LWJ1aWxkLWlkL2lzci8xLmpzb25cIiB9IH0sXG4gICAgICAgICAgeyBzazogeyBTOiBcInRlc3QtdGFnI3Rlc3QtYnVpbGQtaWQvaXNyLzIuanNvblwiIH0gfSxcbiAgICAgICAgXSxcbiAgICAgIH07XG5cbiAgICAgIGR5bmFtb1Jlc3BvbnNlcyh7IHF1ZXJ5OiBtb2NrUXVlcnlSZXNwb25zZSB9KTtcbiAgICAgIG1vY2tTM1NlbmQubW9ja1Jlc29sdmVkVmFsdWUoe30pOyAvLyBGb3IgZGVsZXRlIGNvbW1hbmRzXG4gICAgICBtb2NrU3NtU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7XG4gICAgICAgIFBhcmFtZXRlcjogeyBWYWx1ZTogXCJ0ZXN0LWRpc3RyaWJ1dGlvbi1pZFwiIH0sXG4gICAgICB9KTtcbiAgICAgIG1vY2tDbG91ZEZyb250U2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7fSk7XG5cbiAgICAgIGF3YWl0IGhhbmRsZXJXaXRoRGlzdHJpYnV0aW9uLnJldmFsaWRhdGVUYWcoXCJ0ZXN0LXRhZ1wiKTtcblxuICAgICAgZXhwZWN0KG1vY2tTc21TZW5kKS50b0hhdmVCZWVuQ2FsbGVkV2l0aChleHBlY3QuYW55KE9iamVjdCkpO1xuICAgICAgZXhwZWN0KG1vY2tDbG91ZEZyb250U2VuZCkudG9IYXZlQmVlbkNhbGxlZFdpdGgoXG4gICAgICAgIGV4cGVjdC5hbnkoQ3JlYXRlSW52YWxpZGF0aW9uQ29tbWFuZCksXG4gICAgICApO1xuICAgICAgY29uc3QgW2ludmFsaWRhdGlvbklucHV0XSA9IChcbiAgICAgICAgQ3JlYXRlSW52YWxpZGF0aW9uQ29tbWFuZCBhcyB1bmtub3duIGFzIGplc3QuTW9ja1xuICAgICAgKS5tb2NrLmNhbGxzWzBdO1xuICAgICAgZXhwZWN0KGludmFsaWRhdGlvbklucHV0LkRpc3RyaWJ1dGlvbklkKS50b0JlKFwidGVzdC1kaXN0cmlidXRpb24taWRcIik7XG4gICAgICAvLyBPbmUgdHJhaWxpbmcgd2lsZGNhcmQgcGVyIHBhZ2U6IGFuIGludmFsaWRhdGlvbiBwYXRoIG1hdGNoZXMgb25seSB0aGVcbiAgICAgIC8vIHF1ZXJ5IHN0cmluZyBpdCBzcGVsbHMgb3V0LCBhbmQgYSBwYWdlJ3MgUlNDIHBheWxvYWQgaXMgY2FjaGVkIHVuZGVyXG4gICAgICAvLyBgP19yc2M9PGhhc2g+YCAtIGAvaXNyLzEqYCBjb3ZlcnMgaXQsIGFuZCB0aGUgc2xhc2ggdmFyaWFudCwgZm9yIG9uZVxuICAgICAgLy8gb2YgdGhlIGZpZnRlZW4gd2lsZGNhcmRzIGluc3RlYWQgb2YgdHdvLlxuICAgICAgZXhwZWN0KGludmFsaWRhdGlvbklucHV0LkludmFsaWRhdGlvbkJhdGNoLlBhdGhzLkl0ZW1zKS50b0VxdWFsKFtcbiAgICAgICAgXCIvaXNyLzEqXCIsXG4gICAgICAgIFwiL2lzci8yKlwiLFxuICAgICAgXSk7XG4gICAgfSk7XG5cbiAgICBpdChcImludmFsaWRhdGVzIGEgZGlzdHJpYnV0aW9uIHdob3NlIElEIGlzIGluIHRoZSBlbnZpcm9ubWVudCB3aXRob3V0IFNTTVwiLCBhc3luYyAoKSA9PiB7XG4gICAgICAvLyBOZXh0anNHbG9iYWxDb250YWluZXJzOiB0aGUgdGFzayBjYW4gbmFtZSB0aGUgZGlzdHJpYnV0aW9uIGRpcmVjdGx5LlxuICAgICAgcHJvY2Vzcy5lbnYuQ0RLX05FWFRKU19ESVNUUklCVVRJT05fSUQgPSBcImRpcmVjdC1kaXN0cmlidXRpb24taWRcIjtcbiAgICAgIGNvbnN0IGhhbmRsZXJXaXRoRGlzdHJpYnV0aW9uID0gbmV3IFMzQ2FjaGVIYW5kbGVyKHtcbiAgICAgICAgY29udGV4dDogbW9ja0NvbnRleHQsXG4gICAgICB9KTtcbiAgICAgIGR5bmFtb1Jlc3BvbnNlcyh7XG4gICAgICAgIHF1ZXJ5OiB7IEl0ZW1zOiBbeyBzazogeyBTOiBcInRlc3QtdGFnI3Rlc3QtYnVpbGQtaWQvaXNyLzEuanNvblwiIH0gfV0gfSxcbiAgICAgIH0pO1xuICAgICAgbW9ja1MzU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7fSk7XG4gICAgICBtb2NrQ2xvdWRGcm9udFNlbmQubW9ja1Jlc29sdmVkVmFsdWUoe30pO1xuXG4gICAgICBhd2FpdCBoYW5kbGVyV2l0aERpc3RyaWJ1dGlvbi5yZXZhbGlkYXRlVGFnKFwidGVzdC10YWdcIik7XG5cbiAgICAgIGV4cGVjdChtb2NrU3NtU2VuZCkubm90LnRvSGF2ZUJlZW5DYWxsZWQoKTtcbiAgICAgIGNvbnN0IFtpbnZhbGlkYXRpb25JbnB1dF0gPSAoXG4gICAgICAgIENyZWF0ZUludmFsaWRhdGlvbkNvbW1hbmQgYXMgdW5rbm93biBhcyBqZXN0Lk1vY2tcbiAgICAgICkubW9jay5jYWxsc1swXTtcbiAgICAgIGV4cGVjdChpbnZhbGlkYXRpb25JbnB1dC5EaXN0cmlidXRpb25JZCkudG9CZShcImRpcmVjdC1kaXN0cmlidXRpb24taWRcIik7XG4gICAgfSk7XG5cbiAgICBpdChcInNwbGl0cyBhIG1hcHBpbmcgcm93IGF0IHRoZSB0YWcncyBsZW5ndGgsIHNvIGEgdGFnIGNvbnRhaW5pbmcgIyBzdGlsbCByZXNvbHZlc1wiLCBhc3luYyAoKSA9PiB7XG4gICAgICAvLyBBIHRhZyBpcyBhcHAtZGVmaW5lZDogYHJldmFsaWRhdGVUYWcoXCJ1c2VyIzQyXCIpYCBpcyBsZWdhbC4gU3BsaXR0aW5nIGF0XG4gICAgICAvLyB0aGUgZmlyc3QgXCIjXCIgbGVmdCBcIjQyI3Rlc3QtYnVpbGQtaWQvYWNjb3VudC5qc29uXCIgYXMgdGhlIFMzIGtleSwgd2hvc2VcbiAgICAgIC8vIGludmFsaWRhdGlvbiBwYXRoIG5hbWVzIG5vdGhpbmcgQ2xvdWRGcm9udCBjYWNoZWQsIHNvIHRoZSBwYWdlIHN0YXllZFxuICAgICAgLy8gc3RhbGUgd2hpbGUgdGhlIHJvdyB3YXMgc3RhbXBlZCByZXZhbGlkYXRlZCBhbmQgbmV2ZXIgcmV0cmllZC5cbiAgICAgIHByb2Nlc3MuZW52LkNES19ORVhUSlNfRElTVFJJQlVUSU9OX0lEX1BBUkFNX05BTUUgPSBcInRlc3QtcGFyYW0tbmFtZVwiO1xuICAgICAgY29uc3QgaGFuZGxlcldpdGhEaXN0cmlidXRpb24gPSBuZXcgUzNDYWNoZUhhbmRsZXIoe1xuICAgICAgICBjb250ZXh0OiBtb2NrQ29udGV4dCxcbiAgICAgIH0pO1xuXG4gICAgICBkeW5hbW9SZXNwb25zZXMoe1xuICAgICAgICBxdWVyeTogeyBJdGVtczogW3sgc2s6IHsgUzogXCJ1c2VyIzQyI3Rlc3QtYnVpbGQtaWQvYWNjb3VudC5qc29uXCIgfSB9XSB9LFxuICAgICAgfSk7XG4gICAgICBtb2NrU3NtU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7XG4gICAgICAgIFBhcmFtZXRlcjogeyBWYWx1ZTogXCJ0ZXN0LWRpc3RyaWJ1dGlvbi1pZFwiIH0sXG4gICAgICB9KTtcbiAgICAgIG1vY2tDbG91ZEZyb250U2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7fSk7XG5cbiAgICAgIGF3YWl0IGhhbmRsZXJXaXRoRGlzdHJpYnV0aW9uLnJldmFsaWRhdGVUYWcoXCJ1c2VyIzQyXCIpO1xuXG4gICAgICBjb25zdCBbaW52YWxpZGF0aW9uSW5wdXRdID0gKFxuICAgICAgICBDcmVhdGVJbnZhbGlkYXRpb25Db21tYW5kIGFzIHVua25vd24gYXMgamVzdC5Nb2NrXG4gICAgICApLm1vY2suY2FsbHNbMF07XG4gICAgICBleHBlY3QoaW52YWxpZGF0aW9uSW5wdXQuSW52YWxpZGF0aW9uQmF0Y2guUGF0aHMuSXRlbXMpLnRvRXF1YWwoW1xuICAgICAgICBcIi9hY2NvdW50KlwiLFxuICAgICAgXSk7XG4gICAgfSk7XG5cbiAgICBpdChcImludmFsaWRhdGVzIHRoZSBwYXRoIGEgcmV2YWxpZGF0ZVBhdGggdGFnIG5hbWVzIGV2ZW4gd2l0aCBubyBtYXBwaW5nIHJvd3NcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgLy8gQSBidWlsZC10aW1lIHByZXJlbmRlciBoYXMgbm8gbWFwcGluZyByb3dzLCBzbyB0aGUgcXVlcnkgY29tZXMgYmFja1xuICAgICAgLy8gZW1wdHkgYW5kIHRoZXJlIGlzIG5vdGhpbmcgdG8gZGVyaXZlIGEgQ2xvdWRGcm9udCBwYXRoIGZyb20gLSBleGNlcHQgdGhlXG4gICAgICAvLyB0YWcsIHdoaWNoIGZvciBgcmV2YWxpZGF0ZVBhdGhgIGlzIGBfTl9UXzxwYXRoPmAuIFdpdGhvdXQgdGhpcyB0aGUgQ0ROXG4gICAgICAvLyBhbnN3ZXJzIHdpdGggdGhlIHByZS1yZXZhbGlkYXRpb24gcGFnZSB1bnRpbCBgcy1tYXhhZ2U9MzE1MzYwMDBgXG4gICAgICAvLyBleHBpcmVzLiBNZWFzdXJlZCBhZ2FpbnN0IG5leHQuanMncyBgdGVzdC9lMmUvYXBwLWRpci90cmFpbGluZ3NsYXNoYC5cbiAgICAgIHByb2Nlc3MuZW52LkNES19ORVhUSlNfRElTVFJJQlVUSU9OX0lEX1BBUkFNX05BTUUgPSBcInRlc3QtcGFyYW0tbmFtZVwiO1xuICAgICAgY29uc3QgaGFuZGxlcldpdGhEaXN0cmlidXRpb24gPSBuZXcgUzNDYWNoZUhhbmRsZXIoe1xuICAgICAgICBjb250ZXh0OiBtb2NrQ29udGV4dCxcbiAgICAgIH0pO1xuXG4gICAgICBkeW5hbW9SZXNwb25zZXMoeyBxdWVyeTogeyBJdGVtczogW10gfSB9KTtcbiAgICAgIG1vY2tTc21TZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHtcbiAgICAgICAgUGFyYW1ldGVyOiB7IFZhbHVlOiBcInRlc3QtZGlzdHJpYnV0aW9uLWlkXCIgfSxcbiAgICAgIH0pO1xuICAgICAgbW9ja0Nsb3VkRnJvbnRTZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHt9KTtcblxuICAgICAgYXdhaXQgaGFuZGxlcldpdGhEaXN0cmlidXRpb24ucmV2YWxpZGF0ZVRhZyhcIl9OX1RfL2VuL2xlZ2FjeS9cIik7XG5cbiAgICAgIGNvbnN0IFtpbnZhbGlkYXRpb25JbnB1dF0gPSAoXG4gICAgICAgIENyZWF0ZUludmFsaWRhdGlvbkNvbW1hbmQgYXMgdW5rbm93biBhcyBqZXN0Lk1vY2tcbiAgICAgICkubW9jay5jYWxsc1swXTtcbiAgICAgIC8vIEJvdGggc2xhc2ggdmFyaWFudHMsIHdoaWNoIHRoZSB0cmFpbGluZyB3aWxkY2FyZCBjb3ZlcnM6IGFcbiAgICAgIC8vIGB0cmFpbGluZ1NsYXNoYCBhcHAncyBjYWNoZWQgVVJJIGlzIHRoZSByZWRpcmVjdCB0YXJnZXQsIG5vdCB0aGUgcm91dGUuXG4gICAgICBleHBlY3QoaW52YWxpZGF0aW9uSW5wdXQuSW52YWxpZGF0aW9uQmF0Y2guUGF0aHMuSXRlbXMpLnRvRXF1YWwoW1xuICAgICAgICBcIi9lbi9sZWdhY3kqXCIsXG4gICAgICBdKTtcbiAgICB9KTtcblxuICAgIGl0KFwiaW52YWxpZGF0ZXMgdGhlIFVSSSB1bmRlciB0aGUgYXBwJ3MgYmFzZVBhdGgsIG5vdCB0aGUgYmFyZSByb3V0ZVwiLCBhc3luYyAoKSA9PiB7XG4gICAgICAvLyBDbG91ZEZyb250IGNhY2hlZCBgL2Jhc2UvaXNyLzFgOyB0aGUgY2FjaGUga2V5IGFuZCB0aGUgYHJldmFsaWRhdGVQYXRoYFxuICAgICAgLy8gdGFnIGJvdGggbmFtZSBgL2lzci8xYCwgYmVjYXVzZSBOZXh0LmpzIHN0cmlwcyBgYmFzZVBhdGhgIGJlZm9yZSByb3V0aW5nXG4gICAgICAvLyBhbmQgbmV2ZXIgc2hvd3MgaXQgdG8gdGhlIGNhY2hlIGhhbmRsZXIuIEludmFsaWRhdGluZyB0aGUgYmFyZSByb3V0ZVxuICAgICAgLy8gbWF0Y2hlcyBub3RoaW5nIGF0IHRoZSBlZGdlLCBzbyB0aGUgc3RhbGUgcGFnZSBzdXJ2aXZlcyB1bnRpbFxuICAgICAgLy8gYHMtbWF4YWdlYCBleHBpcmVzLlxuICAgICAgcHJvY2Vzcy5lbnYuQ0RLX05FWFRKU19ESVNUUklCVVRJT05fSURfUEFSQU1fTkFNRSA9IFwidGVzdC1wYXJhbS1uYW1lXCI7XG4gICAgICB1c2VCYXNlUGF0aCgpO1xuICAgICAgY29uc3QgaGFuZGxlcldpdGhCYXNlUGF0aCA9IG5ldyBTM0NhY2hlSGFuZGxlcih7XG4gICAgICAgIGNvbnRleHQ6IG1vY2tDb250ZXh0LFxuICAgICAgfSk7XG5cbiAgICAgIGR5bmFtb1Jlc3BvbnNlcyh7XG4gICAgICAgIHF1ZXJ5OiB7XG4gICAgICAgICAgSXRlbXM6IFtcbiAgICAgICAgICAgIHsgc2s6IHsgUzogXCJ0ZXN0LXRhZyN0ZXN0LWJ1aWxkLWlkL2lzci8xLmpzb25cIiB9IH0sXG4gICAgICAgICAgICB7IHNrOiB7IFM6IFwidGVzdC10YWcjdGVzdC1idWlsZC1pZC9pbmRleC5qc29uXCIgfSB9LFxuICAgICAgICAgIF0sXG4gICAgICAgIH0sXG4gICAgICB9KTtcbiAgICAgIG1vY2tTM1NlbmQubW9ja1Jlc29sdmVkVmFsdWUoe30pO1xuICAgICAgbW9ja1NzbVNlbmQubW9ja1Jlc29sdmVkVmFsdWUoe1xuICAgICAgICBQYXJhbWV0ZXI6IHsgVmFsdWU6IFwidGVzdC1kaXN0cmlidXRpb24taWRcIiB9LFxuICAgICAgfSk7XG4gICAgICBtb2NrQ2xvdWRGcm9udFNlbmQubW9ja1Jlc29sdmVkVmFsdWUoe30pO1xuXG4gICAgICBhd2FpdCBoYW5kbGVyV2l0aEJhc2VQYXRoLnJldmFsaWRhdGVUYWcoXCJ0ZXN0LXRhZ1wiKTtcblxuICAgICAgY29uc3QgW2ludmFsaWRhdGlvbklucHV0XSA9IChcbiAgICAgICAgQ3JlYXRlSW52YWxpZGF0aW9uQ29tbWFuZCBhcyB1bmtub3duIGFzIGplc3QuTW9ja1xuICAgICAgKS5tb2NrLmNhbGxzWzBdO1xuICAgICAgZXhwZWN0KGludmFsaWRhdGlvbklucHV0LkludmFsaWRhdGlvbkJhdGNoLlBhdGhzLkl0ZW1zKS50b0VxdWFsKFxuICAgICAgICBleHBlY3QuYXJyYXlDb250YWluaW5nKFtcbiAgICAgICAgICBcIi9iYXNlL2lzci8xKlwiLFxuICAgICAgICAgIC8vIFRoZSBhcHAncyByb290IHVuZGVyIGEgYGJhc2VQYXRoYCBpcyBgL2Jhc2VgLCBub3QgYC9iYXNlL2AsIGFuZFxuICAgICAgICAgIC8vIHNwZWxsZWQgb3V0OiBgL2Jhc2UqYCB3b3VsZCBiZSB0aGUgd2hvbGUgYXBwLlxuICAgICAgICAgIFwiL2Jhc2VcIixcbiAgICAgICAgICBcIi9iYXNlPypcIixcbiAgICAgICAgICBcIi9iYXNlL1wiLFxuICAgICAgICAgIFwiL2Jhc2UvPypcIixcbiAgICAgICAgXSksXG4gICAgICApO1xuICAgICAgZXhwZWN0KGludmFsaWRhdGlvbklucHV0LkludmFsaWRhdGlvbkJhdGNoLlBhdGhzLkl0ZW1zKS5ub3QudG9Db250YWluKFxuICAgICAgICBcIi9pc3IvMVwiLFxuICAgICAgKTtcbiAgICB9KTtcblxuICAgIGl0KFwiYWRkcyB0aGUgYmFzZVBhdGggdG8gYSByZXZhbGlkYXRlUGF0aCB0YWcncyBwYXRoIHRvb1wiLCBhc3luYyAoKSA9PiB7XG4gICAgICBwcm9jZXNzLmVudi5DREtfTkVYVEpTX0RJU1RSSUJVVElPTl9JRF9QQVJBTV9OQU1FID0gXCJ0ZXN0LXBhcmFtLW5hbWVcIjtcbiAgICAgIHVzZUJhc2VQYXRoKCk7XG4gICAgICBjb25zdCBoYW5kbGVyV2l0aEJhc2VQYXRoID0gbmV3IFMzQ2FjaGVIYW5kbGVyKHtcbiAgICAgICAgY29udGV4dDogbW9ja0NvbnRleHQsXG4gICAgICB9KTtcblxuICAgICAgZHluYW1vUmVzcG9uc2VzKHsgcXVlcnk6IHsgSXRlbXM6IFtdIH0gfSk7XG4gICAgICBtb2NrU3NtU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7XG4gICAgICAgIFBhcmFtZXRlcjogeyBWYWx1ZTogXCJ0ZXN0LWRpc3RyaWJ1dGlvbi1pZFwiIH0sXG4gICAgICB9KTtcbiAgICAgIG1vY2tDbG91ZEZyb250U2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7fSk7XG5cbiAgICAgIGF3YWl0IGhhbmRsZXJXaXRoQmFzZVBhdGgucmV2YWxpZGF0ZVRhZyhcIl9OX1RfL2Jsb2dcIik7XG5cbiAgICAgIGNvbnN0IFtpbnZhbGlkYXRpb25JbnB1dF0gPSAoXG4gICAgICAgIENyZWF0ZUludmFsaWRhdGlvbkNvbW1hbmQgYXMgdW5rbm93biBhcyBqZXN0Lk1vY2tcbiAgICAgICkubW9jay5jYWxsc1swXTtcbiAgICAgIGV4cGVjdChpbnZhbGlkYXRpb25JbnB1dC5JbnZhbGlkYXRpb25CYXRjaC5QYXRocy5JdGVtcykudG9FcXVhbChbXG4gICAgICAgIFwiL2Jhc2UvYmxvZypcIixcbiAgICAgIF0pO1xuICAgIH0pO1xuXG4gICAgaXQoXCJkb2VzIG5vdCBpbnZhbGlkYXRlIGEgQ2xvdWRGcm9udCBwYXRoIGZvciBhbiBhcHAgdGFnXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIHByb2Nlc3MuZW52LkNES19ORVhUSlNfRElTVFJJQlVUSU9OX0lEX1BBUkFNX05BTUUgPSBcInRlc3QtcGFyYW0tbmFtZVwiO1xuICAgICAgY29uc3QgaGFuZGxlcldpdGhEaXN0cmlidXRpb24gPSBuZXcgUzNDYWNoZUhhbmRsZXIoe1xuICAgICAgICBjb250ZXh0OiBtb2NrQ29udGV4dCxcbiAgICAgIH0pO1xuXG4gICAgICBkeW5hbW9SZXNwb25zZXMoeyBxdWVyeTogeyBJdGVtczogW10gfSB9KTtcblxuICAgICAgYXdhaXQgaGFuZGxlcldpdGhEaXN0cmlidXRpb24ucmV2YWxpZGF0ZVRhZyhcInBvc3RzXCIpO1xuXG4gICAgICAvLyBOb3RoaW5nIHRvIGludmFsaWRhdGU6IGFuIGFwcCB0YWcgbmFtZXMgbm8gcGF0aCwgYW5kIG5vIGVudHJ5IGlzIG1hcHBlZFxuICAgICAgLy8gdG8gaXQuXG4gICAgICBleHBlY3QobW9ja0Nsb3VkRnJvbnRTZW5kKS5ub3QudG9IYXZlQmVlbkNhbGxlZCgpO1xuICAgIH0pO1xuXG4gICAgaXQoXCJzaG91bGQgbm90IGNhbGwgU1NNIG9yIENsb3VkRnJvbnQgd2hlbiBubyBkaXN0cmlidXRpb24gcGFyYW1ldGVyIGlzIGNvbmZpZ3VyZWRcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgY29uc3QgbW9ja1F1ZXJ5UmVzcG9uc2UgPSB7XG4gICAgICAgIEl0ZW1zOiBbeyBzazogeyBTOiBcInRlc3QtdGFnI3Rlc3QtYnVpbGQtaWQvaXNyLzEuanNvblwiIH0gfV0sXG4gICAgICB9O1xuXG4gICAgICBkeW5hbW9SZXNwb25zZXMoeyBxdWVyeTogbW9ja1F1ZXJ5UmVzcG9uc2UgfSk7XG4gICAgICBtb2NrUzNTZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHt9KTtcblxuICAgICAgYXdhaXQgaGFuZGxlci5yZXZhbGlkYXRlVGFnKFwidGVzdC10YWdcIik7XG5cbiAgICAgIGV4cGVjdChtb2NrU3NtU2VuZCkubm90LnRvSGF2ZUJlZW5DYWxsZWQoKTtcbiAgICAgIGV4cGVjdChtb2NrQ2xvdWRGcm9udFNlbmQpLm5vdC50b0hhdmVCZWVuQ2FsbGVkKCk7XG4gICAgfSk7XG5cbiAgICBpdChcInNob3VsZCBoYW5kbGUgU1NNL0Nsb3VkRnJvbnQgaW52YWxpZGF0aW9uIGVycm9ycyBncmFjZWZ1bGx5XCIsIGFzeW5jICgpID0+IHtcbiAgICAgIHByb2Nlc3MuZW52LkNES19ORVhUSlNfRElTVFJJQlVUSU9OX0lEX1BBUkFNX05BTUUgPSBcInRlc3QtcGFyYW0tbmFtZVwiO1xuICAgICAgY29uc3QgaGFuZGxlcldpdGhEaXN0cmlidXRpb24gPSBuZXcgUzNDYWNoZUhhbmRsZXIoe1xuICAgICAgICBjb250ZXh0OiBtb2NrQ29udGV4dCxcbiAgICAgIH0pO1xuXG4gICAgICBjb25zdCBtb2NrUXVlcnlSZXNwb25zZSA9IHtcbiAgICAgICAgSXRlbXM6IFt7IHNrOiB7IFM6IFwidGVzdC10YWcjdGVzdC1idWlsZC1pZC9pc3IvMS5qc29uXCIgfSB9XSxcbiAgICAgIH07XG5cbiAgICAgIGR5bmFtb1Jlc3BvbnNlcyh7IHF1ZXJ5OiBtb2NrUXVlcnlSZXNwb25zZSB9KTtcbiAgICAgIG1vY2tTM1NlbmQubW9ja1Jlc29sdmVkVmFsdWUoe30pO1xuICAgICAgbW9ja1NzbVNlbmQubW9ja1JlamVjdGVkVmFsdWVPbmNlKG5ldyBFcnJvcihcIlNTTSBFcnJvclwiKSk7XG5cbiAgICAgIGF3YWl0IGV4cGVjdChcbiAgICAgICAgaGFuZGxlcldpdGhEaXN0cmlidXRpb24ucmV2YWxpZGF0ZVRhZyhcInRlc3QtdGFnXCIpLFxuICAgICAgKS5yZXNvbHZlcy5ub3QudG9UaHJvdygpO1xuICAgIH0pO1xuXG4gICAgaXQoXCJpbnZhbGlkYXRlcyB0aGUgcGFnZSBpdHNlbGYgZm9yIGEgcmV2YWxpZGF0ZVBhdGgocGF0aCwgJ3BhZ2UnKSB0YWdcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgLy8gYHJldmFsaWRhdGVQYXRoKHBhdGgsIHR5cGUpYCBhcHBlbmRzIHRoZSB0eXBlIHRvIHRoZSBpbXBsaWNpdCB0YWcsIHNvIHRoZVxuICAgICAgLy8gdGFnIHJlYWRzIGBfTl9UXy9wcmljaW5nL3BhZ2VgIC0gYSBwYXRoIG5vIFVSTCBoYXMuIFRha2luZyBpdCBsaXRlcmFsbHlcbiAgICAgIC8vIGludmFsaWRhdGVkIG5vdGhpbmcgdGhhdCBleGlzdGVkLCB3aGljaCBpcyB0aGUgd2hvbGUgb2YgdGhlIGJ1ZzogdGhlXG4gICAgICAvLyBzZWNvbmQgYXJndW1lbnQgc2lsZW50bHkgdHVybmVkIENETiBpbnZhbGlkYXRpb24gb2ZmLlxuICAgICAgcHJvY2Vzcy5lbnYuQ0RLX05FWFRKU19ESVNUUklCVVRJT05fSURfUEFSQU1fTkFNRSA9IFwidGVzdC1wYXJhbS1uYW1lXCI7XG4gICAgICBjb25zdCBoYW5kbGVyV2l0aERpc3RyaWJ1dGlvbiA9IG5ldyBTM0NhY2hlSGFuZGxlcih7XG4gICAgICAgIGNvbnRleHQ6IG1vY2tDb250ZXh0LFxuICAgICAgfSk7XG5cbiAgICAgIGR5bmFtb1Jlc3BvbnNlcyh7IHF1ZXJ5OiB7IEl0ZW1zOiBbXSB9IH0pO1xuICAgICAgbW9ja1NzbVNlbmQubW9ja1Jlc29sdmVkVmFsdWUoe1xuICAgICAgICBQYXJhbWV0ZXI6IHsgVmFsdWU6IFwidGVzdC1kaXN0cmlidXRpb24taWRcIiB9LFxuICAgICAgfSk7XG4gICAgICBtb2NrQ2xvdWRGcm9udFNlbmQubW9ja1Jlc29sdmVkVmFsdWUoe30pO1xuXG4gICAgICBhd2FpdCBoYW5kbGVyV2l0aERpc3RyaWJ1dGlvbi5yZXZhbGlkYXRlVGFnKFwiX05fVF8vcHJpY2luZy9wYWdlXCIpO1xuXG4gICAgICBjb25zdCBbaW52YWxpZGF0aW9uSW5wdXRdID0gKFxuICAgICAgICBDcmVhdGVJbnZhbGlkYXRpb25Db21tYW5kIGFzIHVua25vd24gYXMgamVzdC5Nb2NrXG4gICAgICApLm1vY2suY2FsbHNbMF07XG4gICAgICBleHBlY3QoaW52YWxpZGF0aW9uSW5wdXQuSW52YWxpZGF0aW9uQmF0Y2guUGF0aHMuSXRlbXMpLnRvRXF1YWwoXG4gICAgICAgIGV4cGVjdC5hcnJheUNvbnRhaW5pbmcoW1wiL3ByaWNpbmcqXCJdKSxcbiAgICAgICk7XG4gICAgfSk7XG5cbiAgICBpdChcInNraXBzIG1hcHBpbmcgcm93cyB0aGF0IG5hbWUgbm8gY2FjaGVkIFVSSVwiLCBhc3luYyAoKSA9PiB7XG4gICAgICAvLyBBIGR5bmFtaWMgdGVtcGxhdGUgYW5kIGEgZmV0Y2gtY2FjaGUgZW50cnkgYXJlIGJvdGggcm93cyBhIHJlYWwgdGFnXG4gICAgICAvLyBjYXJyaWVzLCBhbmQgbmVpdGhlciBpcyBhIFVSTDogYC9ibG9nL1tzbHVnXWAgbWF0Y2hlcyBub3RoaW5nIGF0IHRoZSBlZGdlXG4gICAgICAvLyAoQ2xvdWRGcm9udCBoYXMgbm8gc3VjaCBVUkkpLCBhbmQgYSBmZXRjaCBrZXkgaXMgYSBoYXNoIG9mIHRoZSByZXF1ZXN0LlxuICAgICAgcHJvY2Vzcy5lbnYuQ0RLX05FWFRKU19ESVNUUklCVVRJT05fSURfUEFSQU1fTkFNRSA9IFwidGVzdC1wYXJhbS1uYW1lXCI7XG4gICAgICBjb25zdCBoYW5kbGVyV2l0aERpc3RyaWJ1dGlvbiA9IG5ldyBTM0NhY2hlSGFuZGxlcih7XG4gICAgICAgIGNvbnRleHQ6IG1vY2tDb250ZXh0LFxuICAgICAgfSk7XG5cbiAgICAgIGR5bmFtb1Jlc3BvbnNlcyh7XG4gICAgICAgIHF1ZXJ5OiB7XG4gICAgICAgICAgSXRlbXM6IFtcbiAgICAgICAgICAgIHsgc2s6IHsgUzogXCJ0ZXN0LXRhZyN0ZXN0LWJ1aWxkLWlkL2Jsb2cvW3NsdWddLmpzb25cIiB9IH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIHNrOiB7XG4gICAgICAgICAgICAgICAgUzogXCJ0ZXN0LXRhZyN0ZXN0LWJ1aWxkLWlkLzAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmLmpzb25cIixcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7IHNrOiB7IFM6IFwidGVzdC10YWcjdGVzdC1idWlsZC1pZC9ibG9nL2hlbGxvLmpzb25cIiB9IH0sXG4gICAgICAgICAgXSxcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuICAgICAgbW9ja1MzU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7fSk7XG4gICAgICBtb2NrU3NtU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7XG4gICAgICAgIFBhcmFtZXRlcjogeyBWYWx1ZTogXCJ0ZXN0LWRpc3RyaWJ1dGlvbi1pZFwiIH0sXG4gICAgICB9KTtcbiAgICAgIG1vY2tDbG91ZEZyb250U2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7fSk7XG5cbiAgICAgIGF3YWl0IGhhbmRsZXJXaXRoRGlzdHJpYnV0aW9uLnJldmFsaWRhdGVUYWcoXCJ0ZXN0LXRhZ1wiKTtcblxuICAgICAgY29uc3QgW2ludmFsaWRhdGlvbklucHV0XSA9IChcbiAgICAgICAgQ3JlYXRlSW52YWxpZGF0aW9uQ29tbWFuZCBhcyB1bmtub3duIGFzIGplc3QuTW9ja1xuICAgICAgKS5tb2NrLmNhbGxzWzBdO1xuICAgICAgY29uc3QgaXRlbXM6IHN0cmluZ1tdID0gaW52YWxpZGF0aW9uSW5wdXQuSW52YWxpZGF0aW9uQmF0Y2guUGF0aHMuSXRlbXM7XG4gICAgICBleHBlY3QoaXRlbXMpLnRvRXF1YWwoW1wiL2Jsb2cvaGVsbG8qXCJdKTtcbiAgICAgIGV4cGVjdChpdGVtcy5zb21lKChwYXRoKSA9PiBwYXRoLmluY2x1ZGVzKFwiW1wiKSkpLnRvQmUoZmFsc2UpO1xuICAgICAgZXhwZWN0KGl0ZW1zLnNvbWUoKHBhdGgpID0+IHBhdGguaW5jbHVkZXMoXCIwMTIzNDU2Nzg5YWJjZGVmXCIpKSkudG9CZShcbiAgICAgICAgZmFsc2UsXG4gICAgICApO1xuICAgIH0pO1xuXG4gICAgaXQoXCJ3YWxrcyBldmVyeSBRdWVyeSBwYWdlIG9mIGEgdGFnJ3MgbWFwcGluZyByb3dzXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIC8vIE9uZSBgUXVlcnlgIGFuc3dlcnMgd2l0aCBhdCBtb3N0IDEgTUIuIEEgdGFnIG9uIGEgbGFyZ2UgYXBwIHJ1bnMgcGFzdFxuICAgICAgLy8gdGhhdCwgYW5kIHRha2luZyBvbmx5IHRoZSBmaXJzdCBwYWdlIGRlbGV0ZWQgc29tZSBvZiB0aGUgdGFnJ3MgZW50cmllc1xuICAgICAgLy8gYW5kIGludmFsaWRhdGVkIG9ubHkgc29tZSBvZiBpdHMgcGF0aHMgLSBhIHBhcnRpYWwgcmV2YWxpZGF0aW9uIHRoYXRcbiAgICAgIC8vIGxvb2tzIGxpa2UgYSBjYWNoZSBidWcuXG4gICAgICBwcm9jZXNzLmVudi5DREtfTkVYVEpTX0RJU1RSSUJVVElPTl9JRF9QQVJBTV9OQU1FID0gXCJ0ZXN0LXBhcmFtLW5hbWVcIjtcbiAgICAgIGNvbnN0IGhhbmRsZXJXaXRoRGlzdHJpYnV0aW9uID0gbmV3IFMzQ2FjaGVIYW5kbGVyKHtcbiAgICAgICAgY29udGV4dDogbW9ja0NvbnRleHQsXG4gICAgICB9KTtcblxuICAgICAgbGV0IHF1ZXJpZXMgPSAwO1xuICAgICAgbW9ja0R5bmFtb1NlbmQubW9ja0ltcGxlbWVudGF0aW9uKChjb21tYW5kOiB1bmtub3duKSA9PiB7XG4gICAgICAgIGlmIChjb21tYW5kIGluc3RhbmNlb2YgUXVlcnlDb21tYW5kKSB7XG4gICAgICAgICAgcXVlcmllcysrO1xuICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoXG4gICAgICAgICAgICBxdWVyaWVzID09PSAxXG4gICAgICAgICAgICAgID8ge1xuICAgICAgICAgICAgICAgICAgSXRlbXM6IFt7IHNrOiB7IFM6IFwidGVzdC10YWcjdGVzdC1idWlsZC1pZC9pc3IvMS5qc29uXCIgfSB9XSxcbiAgICAgICAgICAgICAgICAgIExhc3RFdmFsdWF0ZWRLZXk6IHsgcGs6IHsgUzogXCJ0ZXN0LWJ1aWxkLWlkXCIgfSB9LFxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgOiB7IEl0ZW1zOiBbeyBzazogeyBTOiBcInRlc3QtdGFnI3Rlc3QtYnVpbGQtaWQvaXNyLzIuanNvblwiIH0gfV0gfSxcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoe30pO1xuICAgICAgfSk7XG4gICAgICBtb2NrUzNTZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHt9KTtcbiAgICAgIG1vY2tTc21TZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHtcbiAgICAgICAgUGFyYW1ldGVyOiB7IFZhbHVlOiBcInRlc3QtZGlzdHJpYnV0aW9uLWlkXCIgfSxcbiAgICAgIH0pO1xuICAgICAgbW9ja0Nsb3VkRnJvbnRTZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHt9KTtcblxuICAgICAgYXdhaXQgaGFuZGxlcldpdGhEaXN0cmlidXRpb24ucmV2YWxpZGF0ZVRhZyhcInRlc3QtdGFnXCIpO1xuXG4gICAgICBleHBlY3QocXVlcmllcykudG9CZSgyKTtcbiAgICAgIGNvbnN0IFtpbnZhbGlkYXRpb25JbnB1dF0gPSAoXG4gICAgICAgIENyZWF0ZUludmFsaWRhdGlvbkNvbW1hbmQgYXMgdW5rbm93biBhcyBqZXN0Lk1vY2tcbiAgICAgICkubW9jay5jYWxsc1swXTtcbiAgICAgIGV4cGVjdChpbnZhbGlkYXRpb25JbnB1dC5JbnZhbGlkYXRpb25CYXRjaC5QYXRocy5JdGVtcykudG9FcXVhbChbXG4gICAgICAgIFwiL2lzci8xKlwiLFxuICAgICAgICBcIi9pc3IvMipcIixcbiAgICAgIF0pO1xuICAgIH0pO1xuXG4gICAgaXQoXCJjb2xsYXBzZXMgdG8gb25lIGFwcC13aWRlIHdpbGRjYXJkIHJhdGhlciB0aGFuIHNlbmRpbmcgbWFueSBpbnZhbGlkYXRpb25zXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIC8vIENsb3VkRnJvbnQgY2FwcyBhIHJlcXVlc3QgYXQgMTUgd2lsZGNhcmQgcGF0aHMgYW5kIGFsbG93cyAxNVxuICAgICAgLy8gaW52YWxpZGF0aW9ucyBpbiBwcm9ncmVzcy4gQSB0YWcgY292ZXJpbmcgaHVuZHJlZHMgb2YgcGFnZXMgdGhlcmVmb3JlXG4gICAgICAvLyBjYW5ub3QgYmUgc3BlbGxlZCBvdXQ6IHNlbmRpbmcgYSByZXF1ZXN0IHBlciAxNSBwYXRocyB0aHJvdHRsZXMsIGFuZCBhXG4gICAgICAvLyB0aHJvdHRsZWQgcmVxdWVzdCBpcyBhIHBhZ2UgdGhhdCBzdGF5cyBzdGFsZS5cbiAgICAgIHByb2Nlc3MuZW52LkNES19ORVhUSlNfRElTVFJJQlVUSU9OX0lEX1BBUkFNX05BTUUgPSBcInRlc3QtcGFyYW0tbmFtZVwiO1xuICAgICAgY29uc3QgaGFuZGxlcldpdGhEaXN0cmlidXRpb24gPSBuZXcgUzNDYWNoZUhhbmRsZXIoe1xuICAgICAgICBjb250ZXh0OiBtb2NrQ29udGV4dCxcbiAgICAgIH0pO1xuXG4gICAgICBkeW5hbW9SZXNwb25zZXMoe1xuICAgICAgICBxdWVyeToge1xuICAgICAgICAgIEl0ZW1zOiBBcnJheS5mcm9tKHsgbGVuZ3RoOiAyMDAgfSwgKF8sIGkpID0+ICh7XG4gICAgICAgICAgICBzazogeyBTOiBgdGVzdC10YWcjdGVzdC1idWlsZC1pZC9pc3IvJHtpfS5qc29uYCB9LFxuICAgICAgICAgIH0pKSxcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuICAgICAgbW9ja1MzU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7fSk7XG4gICAgICBtb2NrU3NtU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7XG4gICAgICAgIFBhcmFtZXRlcjogeyBWYWx1ZTogXCJ0ZXN0LWRpc3RyaWJ1dGlvbi1pZFwiIH0sXG4gICAgICB9KTtcbiAgICAgIG1vY2tDbG91ZEZyb250U2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7fSk7XG5cbiAgICAgIGF3YWl0IGhhbmRsZXJXaXRoRGlzdHJpYnV0aW9uLnJldmFsaWRhdGVUYWcoXCJ0ZXN0LXRhZ1wiKTtcblxuICAgICAgZXhwZWN0KG1vY2tDbG91ZEZyb250U2VuZCkudG9IYXZlQmVlbkNhbGxlZFRpbWVzKDEpO1xuICAgICAgY29uc3QgW2ludmFsaWRhdGlvbklucHV0XSA9IChcbiAgICAgICAgQ3JlYXRlSW52YWxpZGF0aW9uQ29tbWFuZCBhcyB1bmtub3duIGFzIGplc3QuTW9ja1xuICAgICAgKS5tb2NrLmNhbGxzWzBdO1xuICAgICAgZXhwZWN0KGludmFsaWRhdGlvbklucHV0LkludmFsaWRhdGlvbkJhdGNoLlBhdGhzLkl0ZW1zKS50b0VxdWFsKFtcIi8qXCJdKTtcbiAgICB9KTtcblxuICAgIC8qKiBBIGhhbmRsZXIgd2l0aCBhIGRpc3RyaWJ1dGlvbiwgYW5kIER5bmFtb0RCIGFuc3dlcmluZyBgcXVlcnlgIGZvciBpdC4gKi9cbiAgICBjb25zdCB3aXRoRGlzdHJpYnV0aW9uID0gKHF1ZXJ5OiB1bmtub3duKSA9PiB7XG4gICAgICBwcm9jZXNzLmVudi5DREtfTkVYVEpTX0RJU1RSSUJVVElPTl9JRF9QQVJBTV9OQU1FID0gXCJ0ZXN0LXBhcmFtLW5hbWVcIjtcbiAgICAgIGR5bmFtb1Jlc3BvbnNlcyh7IHF1ZXJ5IH0pO1xuICAgICAgbW9ja1NzbVNlbmQubW9ja1Jlc29sdmVkVmFsdWUoe1xuICAgICAgICBQYXJhbWV0ZXI6IHsgVmFsdWU6IFwidGVzdC1kaXN0cmlidXRpb24taWRcIiB9LFxuICAgICAgfSk7XG4gICAgICBtb2NrQ2xvdWRGcm9udFNlbmQubW9ja1Jlc29sdmVkVmFsdWUoe30pO1xuICAgICAgcmV0dXJuIG5ldyBTM0NhY2hlSGFuZGxlcih7IGNvbnRleHQ6IG1vY2tDb250ZXh0IH0pO1xuICAgIH07XG4gICAgY29uc3QgaW52YWxpZGF0aW9ucyA9ICgpOiBzdHJpbmdbXVtdID0+XG4gICAgICAoQ3JlYXRlSW52YWxpZGF0aW9uQ29tbWFuZCBhcyB1bmtub3duIGFzIGplc3QuTW9jaykubW9jay5jYWxscy5tYXAoXG4gICAgICAgIChbaW5wdXRdKSA9PiBpbnB1dC5JbnZhbGlkYXRpb25CYXRjaC5QYXRocy5JdGVtcyxcbiAgICAgICk7XG4gICAgY29uc3QgcGFnZXMgPSAodGFnOiBzdHJpbmcsIGNvdW50OiBudW1iZXIpID0+ICh7XG4gICAgICBJdGVtczogQXJyYXkuZnJvbSh7IGxlbmd0aDogY291bnQgfSwgKF8sIGkpID0+ICh7XG4gICAgICAgIHNrOiB7IFM6IGAke3RhZ30jdGVzdC1idWlsZC1pZC9pc3IvJHtpfS5qc29uYCB9LFxuICAgICAgfSkpLFxuICAgIH0pO1xuXG4gICAgaXQoXCJzZW5kcyBhIHRhZydzIHBhdGhzIGFzIG9uZSByZXF1ZXN0IHdoaWxlIHRoZXkgZml0XCIsIGFzeW5jICgpID0+IHtcbiAgICAgIC8vIENsb3VkRnJvbnQncyB3aWxkY2FyZCBxdW90YSBpcyBzaGFyZWQgYnkgZXZlcnkgaW52YWxpZGF0aW9uIGluXG4gICAgICAvLyBwcm9ncmVzcyBvbiB0aGUgZGlzdHJpYnV0aW9uLiBTcGxpdHRpbmcgdGVuIHBhZ2VzJyB0d2VudHkgd2lsZGNhcmRzXG4gICAgICAvLyBpbnRvIHR3byBiYWNrLXRvLWJhY2sgcmVxdWVzdHMgaGFkIHRoZSBzZWNvbmQgcmVqZWN0ZWQsIGFuZCBpdHMgcGFnZXNcbiAgICAgIC8vIHN0YXllZCBzdGFsZSBhdCB0aGUgZWRnZS5cbiAgICAgIGF3YWl0IHdpdGhEaXN0cmlidXRpb24ocGFnZXMoXCJ0ZXN0LXRhZ1wiLCAxMCkpLnJldmFsaWRhdGVUYWcoXCJ0ZXN0LXRhZ1wiKTtcblxuICAgICAgZXhwZWN0KGludmFsaWRhdGlvbnMoKSkudG9IYXZlTGVuZ3RoKDEpO1xuICAgICAgZXhwZWN0KGludmFsaWRhdGlvbnMoKVswXSkudG9IYXZlTGVuZ3RoKDEwKTtcbiAgICB9KTtcblxuICAgIGl0KFwiY29sbGFwc2VzIHRvIHRoZSB3aG9sZSBhcHAgcGFzdCBmaWZ0ZWVuIHdpbGRjYXJkc1wiLCBhc3luYyAoKSA9PiB7XG4gICAgICBhd2FpdCB3aXRoRGlzdHJpYnV0aW9uKHBhZ2VzKFwidGVzdC10YWdcIiwgMTYpKS5yZXZhbGlkYXRlVGFnKFwidGVzdC10YWdcIik7XG5cbiAgICAgIGV4cGVjdChpbnZhbGlkYXRpb25zKCkpLnRvRXF1YWwoW1tcIi8qXCJdXSk7XG4gICAgfSk7XG5cbiAgICBpdChcImNvbGxhcHNlcyB0byB0aGUgd2hvbGUgYXBwIHVuZGVyIGEgYmFzZVBhdGgsIGl0cyByb290IGluY2x1ZGVkXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIC8vIE5vdCBgL2Jhc2UqYCwgd2hpY2ggd291bGQgYWxzbyBpbnZhbGlkYXRlIGEgc2libGluZyBhcHAgYXQgYC9iYXNlMmAuXG4gICAgICB1c2VCYXNlUGF0aCgpO1xuICAgICAgYXdhaXQgd2l0aERpc3RyaWJ1dGlvbihwYWdlcyhcInRlc3QtdGFnXCIsIDE2KSkucmV2YWxpZGF0ZVRhZyhcInRlc3QtdGFnXCIpO1xuXG4gICAgICBleHBlY3QoaW52YWxpZGF0aW9ucygpKS50b0VxdWFsKFtbXCIvYmFzZVwiLCBcIi9iYXNlPypcIiwgXCIvYmFzZS8qXCJdXSk7XG4gICAgfSk7XG5cbiAgICBpdChcImludmFsaWRhdGVzIHRoZSB3aG9sZSBhcHAgdW5kZXIgYSBiYXNlUGF0aCB3aGVuIGEgdGFnJ3Mgcm93cyBjYW5ub3QgYmUgcmVhZFwiLCBhc3luYyAoKSA9PiB7XG4gICAgICB1c2VCYXNlUGF0aCgpO1xuICAgICAgamVzdC5zcHlPbihjb25zb2xlLCBcImVycm9yXCIpLm1vY2tJbXBsZW1lbnRhdGlvbigpO1xuICAgICAgY29uc3QgY2RuSGFuZGxlciA9IHdpdGhEaXN0cmlidXRpb24oeyBJdGVtczogW10gfSk7XG4gICAgICBtb2NrRHluYW1vU2VuZC5tb2NrSW1wbGVtZW50YXRpb24oKGNvbW1hbmQ6IHVua25vd24pID0+XG4gICAgICAgIGNvbW1hbmQgaW5zdGFuY2VvZiBRdWVyeUNvbW1hbmRcbiAgICAgICAgICA/IFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcIlByb3Zpc2lvbmVkVGhyb3VnaHB1dEV4Y2VlZGVkXCIpKVxuICAgICAgICAgIDogUHJvbWlzZS5yZXNvbHZlKHt9KSxcbiAgICAgICk7XG5cbiAgICAgIGF3YWl0IGNkbkhhbmRsZXIucmV2YWxpZGF0ZVRhZyhcInBvc3RzXCIpO1xuXG4gICAgICBleHBlY3QoaW52YWxpZGF0aW9ucygpKS50b0VxdWFsKFtbXCIvYmFzZVwiLCBcIi9iYXNlPypcIiwgXCIvYmFzZS8qXCJdXSk7XG4gICAgfSk7XG5cbiAgICBpdChcInBlcmNlbnQtZW5jb2RlcyB0aGUgcm91dGVzIGl0IGludmFsaWRhdGVzXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIC8vIENhY2hlIGtleXMgYXJlIGRlY29kZWQgcGF0aG5hbWVzLCB3aGlsZSBDbG91ZEZyb250IGNhY2hlcyAtIGFuZFxuICAgICAgLy8gaW52YWxpZGF0aW9uIHBhdGhzIG1hdGNoIC0gdGhlIGVuY29kZWQgVVJJIHRoZSBicm93c2VyIHNlbnQuXG4gICAgICBhd2FpdCB3aXRoRGlzdHJpYnV0aW9uKHtcbiAgICAgICAgSXRlbXM6IFtcbiAgICAgICAgICB7IHNrOiB7IFM6IFwidGVzdC10YWcjdGVzdC1idWlsZC1pZC9ibG9nL2jDqWxsby5qc29uXCIgfSB9LFxuICAgICAgICAgIHsgc2s6IHsgUzogXCJ0ZXN0LXRhZyN0ZXN0LWJ1aWxkLWlkL2Jsb2cvaGVsbG8gd29ybGQuanNvblwiIH0gfSxcbiAgICAgICAgICAvLyBQYXRoIGRlbGltaXRlcnMgTmV4dC5qcyByZS1lc2NhcGVkIGluIHRoZSBrZXkgc3RheSBhcyB0aGV5IGFyZS5cbiAgICAgICAgICB7IHNrOiB7IFM6IFwidGVzdC10YWcjdGVzdC1idWlsZC1pZC9maWxlcy9hJTJGYi5qc29uXCIgfSB9LFxuICAgICAgICAgIHsgc2s6IHsgUzogXCJ0ZXN0LXRhZyN0ZXN0LWJ1aWxkLWlkL0B1c2VyLzUwJS5qc29uXCIgfSB9LFxuICAgICAgICBdLFxuICAgICAgfSkucmV2YWxpZGF0ZVRhZyhbXCJ0ZXN0LXRhZ1wiLCBcIl9OX1RfL3RhZ3MvYyM/XCJdKTtcblxuICAgICAgZXhwZWN0KGludmFsaWRhdGlvbnMoKSkudG9FcXVhbChbXG4gICAgICAgIFtcbiAgICAgICAgICBcIi9ibG9nL2glQzMlQTlsbG8qXCIsXG4gICAgICAgICAgXCIvYmxvZy9oZWxsbyUyMHdvcmxkKlwiLFxuICAgICAgICAgIFwiL2ZpbGVzL2ElMkZiKlwiLFxuICAgICAgICAgIFwiL0B1c2VyLzUwJTI1KlwiLFxuICAgICAgICAgIFwiL3RhZ3MvYyUyMyUzRipcIixcbiAgICAgICAgXSxcbiAgICAgIF0pO1xuICAgIH0pO1xuXG4gICAgaXQoXCJpbnZhbGlkYXRlcyBldmVyeSB0YWcgb2Ygb25lIGNhbGwgaW4gYSBzaW5nbGUgcmVxdWVzdFwiLCBhc3luYyAoKSA9PiB7XG4gICAgICAvLyBgcmV2YWxpZGF0ZVRhZ2AgaGFuZHMgdGhlIGhhbmRsZXIgZXZlcnkgdGFnIGEgcmVxdWVzdCByZXZhbGlkYXRlZCBhdFxuICAgICAgLy8gb25jZTsgYSByZXF1ZXN0IHBlciB0YWcgY29tcGV0ZXMgZm9yIHRoZSBzYW1lIHF1b3RhLlxuICAgICAgYXdhaXQgd2l0aERpc3RyaWJ1dGlvbih7IEl0ZW1zOiBbXSB9KS5yZXZhbGlkYXRlVGFnKFtcbiAgICAgICAgXCJfTl9UXy9hXCIsXG4gICAgICAgIFwiX05fVF8vYlwiLFxuICAgICAgXSk7XG5cbiAgICAgIGV4cGVjdChpbnZhbGlkYXRpb25zKCkpLnRvRXF1YWwoW1tcIi9hKlwiLCBcIi9iKlwiXV0pO1xuICAgIH0pO1xuXG4gICAgaXQoXCJpbnZhbGlkYXRlcyB0aGUgd2hvbGUgYXBwIHdoZW4gYSB0YWcgaGFzIG1vcmUgcm93cyB0aGFuIGl0IHdhbGtzXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIC8vIFRoZSByb3dzIHBhc3QgdGhlIGxhc3QgcGFnZSB3YWxrZWQgYXJlIHBhZ2VzIHRvbzsgaW52YWxpZGF0aW5nIG9ubHkgdGhlXG4gICAgICAvLyBvbmVzIHJlYWQgbGVmdCB0aGUgcmVzdCBzdGFsZSBhdCB0aGUgZWRnZSBmb3IgdXAgdG8gYSB5ZWFyLlxuICAgICAgcHJvY2Vzcy5lbnYuQ0RLX05FWFRKU19ESVNUUklCVVRJT05fSURfUEFSQU1fTkFNRSA9IFwidGVzdC1wYXJhbS1uYW1lXCI7XG4gICAgICBtb2NrRHluYW1vU2VuZC5tb2NrSW1wbGVtZW50YXRpb24oKGNvbW1hbmQ6IHVua25vd24pID0+XG4gICAgICAgIFByb21pc2UucmVzb2x2ZShcbiAgICAgICAgICBjb21tYW5kIGluc3RhbmNlb2YgUXVlcnlDb21tYW5kXG4gICAgICAgICAgICA/IHtcbiAgICAgICAgICAgICAgICBJdGVtczogW3sgc2s6IHsgUzogXCJ0ZXN0LXRhZyN0ZXN0LWJ1aWxkLWlkL2lzci8xLmpzb25cIiB9IH1dLFxuICAgICAgICAgICAgICAgIExhc3RFdmFsdWF0ZWRLZXk6IHsgcGs6IHsgUzogXCJ0ZXN0LWJ1aWxkLWlkXCIgfSB9LFxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICA6IHt9LFxuICAgICAgICApLFxuICAgICAgKTtcbiAgICAgIG1vY2tTc21TZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHtcbiAgICAgICAgUGFyYW1ldGVyOiB7IFZhbHVlOiBcInRlc3QtZGlzdHJpYnV0aW9uLWlkXCIgfSxcbiAgICAgIH0pO1xuICAgICAgbW9ja0Nsb3VkRnJvbnRTZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHt9KTtcblxuICAgICAgYXdhaXQgbmV3IFMzQ2FjaGVIYW5kbGVyKHsgY29udGV4dDogbW9ja0NvbnRleHQgfSkucmV2YWxpZGF0ZVRhZyhcbiAgICAgICAgXCJ0ZXN0LXRhZ1wiLFxuICAgICAgKTtcblxuICAgICAgZXhwZWN0KGludmFsaWRhdGlvbnMoKSkudG9FcXVhbChbW1wiLypcIl1dKTtcbiAgICB9KTtcblxuICAgIGl0KFwiaWdub3JlcyByb3dzIHRoYXQgYW5vdGhlciB0YWcgc2hhcmluZyB0aGUgcHJlZml4IG93bnNcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgLy8gYGJlZ2luc193aXRoKHNrLCBcInVzZXIjXCIpYCBhbHNvIG1hdGNoZXMgdGhlIHRhZyBgdXNlciM0MmA6IGl0cyBiYXJlXG4gICAgICAvLyBtYXJrZXIgcm93LCBhbmQgaXRzIG1hcHBpbmcgcm93cy4gUmVhZCBhcyBgdXNlcmAncywgdGhleSBiZWNhbWVcbiAgICAgIC8vIGludmFsaWRhdGlvbiBwYXRocyBsaWtlIGAvNDJgIHRoYXQgbmFtZSBub3RoaW5nLlxuICAgICAgYXdhaXQgd2l0aERpc3RyaWJ1dGlvbih7XG4gICAgICAgIEl0ZW1zOiBbXG4gICAgICAgICAgeyBzazogeyBTOiBcInVzZXIjNDJcIiB9IH0sXG4gICAgICAgICAgeyBzazogeyBTOiBcInVzZXIjNDIjdGVzdC1idWlsZC1pZC9hY2NvdW50Lmpzb25cIiB9IH0sXG4gICAgICAgICAgeyBzazogeyBTOiBcInVzZXIjdGVzdC1idWlsZC1pZC9wcm9maWxlLmpzb25cIiB9IH0sXG4gICAgICAgIF0sXG4gICAgICB9KS5yZXZhbGlkYXRlVGFnKFwidXNlclwiKTtcblxuICAgICAgZXhwZWN0KGludmFsaWRhdGlvbnMoKSkudG9FcXVhbChbW1wiL3Byb2ZpbGUqXCJdXSk7XG4gICAgfSk7XG5cbiAgICBpdChcImFkZHMgdGhlIGJ1aWxkLXRpbWUgcHJlcmVuZGVycyB0aGUgYnVpbGQncyB0YWcgbWFuaWZlc3QgbmFtZXMsIHJlYWRpbmcgaXQgb25jZVwiLCBhc3luYyAoKSA9PiB7XG4gICAgICAvLyBBIHByZXJlbmRlciBpcyB3cml0dGVuIGJ5IHRoZSBhZGFwdGVyLCBuZXZlciB0aHJvdWdoIGBzZXRgLCBzbyBpdCBoYXNcbiAgICAgIC8vIG5vIG1hcHBpbmcgcm93OiB0aGUgbWFuaWZlc3QgaXMgdGhlIG9ubHkgcmVjb3JkIG9mIGl0cyB0YWdzLlxuICAgICAgY29uc3QgY2RuSGFuZGxlciA9IHdpdGhEaXN0cmlidXRpb24oe1xuICAgICAgICBJdGVtczogW3sgc2s6IHsgUzogXCJwb3N0cyN0ZXN0LWJ1aWxkLWlkL2Jsb2cvYi5qc29uXCIgfSB9XSxcbiAgICAgIH0pO1xuICAgICAgY29uc3QgbWFuaWZlc3RHZXRzID0gKCkgPT5cbiAgICAgICAgKEdldE9iamVjdENvbW1hbmQgYXMgdW5rbm93biBhcyBqZXN0Lk1vY2spLm1vY2suY2FsbHMuZmlsdGVyKFxuICAgICAgICAgIChbaW5wdXRdKSA9PlxuICAgICAgICAgICAgaW5wdXQuS2V5ID09PSBcInRlc3QtYnVpbGQtaWQvX2Nkay1uZXh0anMtdGFnLW1hbmlmZXN0Lmpzb25cIixcbiAgICAgICAgKTtcbiAgICAgIG1vY2tTM1NlbmQubW9ja0ltcGxlbWVudGF0aW9uKChjb21tYW5kOiB1bmtub3duKSA9PlxuICAgICAgICBQcm9taXNlLnJlc29sdmUoXG4gICAgICAgICAgY29tbWFuZCBpbnN0YW5jZW9mIEdldE9iamVjdENvbW1hbmRcbiAgICAgICAgICAgID8ge1xuICAgICAgICAgICAgICAgIEJvZHk6IHtcbiAgICAgICAgICAgICAgICAgIHRyYW5zZm9ybVRvU3RyaW5nOiBhc3luYyAoKSA9PlxuICAgICAgICAgICAgICAgICAgICBKU09OLnN0cmluZ2lmeSh7IHBvc3RzOiBbXCJibG9nL2FcIiwgXCJpbmRleFwiXSB9KSxcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICA6IHt9LFxuICAgICAgICApLFxuICAgICAgKTtcblxuICAgICAgYXdhaXQgY2RuSGFuZGxlci5yZXZhbGlkYXRlVGFnKFwicG9zdHNcIik7XG4gICAgICBhd2FpdCBjZG5IYW5kbGVyLnJldmFsaWRhdGVUYWcoXCJjb25zdHJ1Y3RvclwiKTtcblxuICAgICAgZXhwZWN0KGludmFsaWRhdGlvbnMoKSkudG9FcXVhbChbW1wiL2Jsb2cvYipcIiwgXCIvYmxvZy9hKlwiLCBcIi9cIiwgXCIvPypcIl1dKTtcbiAgICAgIGV4cGVjdChtYW5pZmVzdEdldHMoKSkudG9IYXZlTGVuZ3RoKDEpO1xuICAgIH0pO1xuXG4gICAgaXQoXCJpbnZhbGlkYXRlcyB0aGUgd2hvbGUgYXBwIHdoZW4gdGhlIGJ1aWxkJ3MgdGFnIG1hbmlmZXN0IGNhbm5vdCBiZSByZWFkLCBhbmQgcmVhZHMgaXQgYWdhaW4gbmV4dCB0aW1lXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIGNvbnN0IGNkbkhhbmRsZXIgPSB3aXRoRGlzdHJpYnV0aW9uKHsgSXRlbXM6IFtdIH0pO1xuICAgICAgbW9ja1MzU2VuZC5tb2NrUmVqZWN0ZWRWYWx1ZU9uY2UobmV3IEVycm9yKFwiQWNjZXNzRGVuaWVkXCIpKTtcblxuICAgICAgYXdhaXQgY2RuSGFuZGxlci5yZXZhbGlkYXRlVGFnKFwicG9zdHNcIik7XG4gICAgICBhd2FpdCBjZG5IYW5kbGVyLnJldmFsaWRhdGVUYWcoXCJwb3N0c1wiKTtcblxuICAgICAgZXhwZWN0KGludmFsaWRhdGlvbnMoKSkudG9FcXVhbChbW1wiLypcIl1dKTtcbiAgICAgIGV4cGVjdChtb2NrUzNTZW5kKS50b0hhdmVCZWVuQ2FsbGVkVGltZXMoMik7XG4gICAgfSk7XG5cbiAgICBpdChcImludmFsaWRhdGVzIHRoZSB3aG9sZSBhcHAgd2hlbiBhIHRhZydzIG1hcmtlciByb3cgY2Fubm90IGJlIHdyaXR0ZW5cIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgLy8gT3RoZXIgaW5zdGFuY2VzIHdvdWxkIG5ldmVyIHNlZSB0aGUgcmV2YWxpZGF0aW9uLCBzbyBubyBwYXRoIGxpc3QgaXNcbiAgICAgIC8vIGVub3VnaC5cbiAgICAgIHByb2Nlc3MuZW52LkNES19ORVhUSlNfRElTVFJJQlVUSU9OX0lEX1BBUkFNX05BTUUgPSBcInRlc3QtcGFyYW0tbmFtZVwiO1xuICAgICAgamVzdC5zcHlPbihjb25zb2xlLCBcImVycm9yXCIpLm1vY2tJbXBsZW1lbnRhdGlvbigpO1xuICAgICAgbW9ja0R5bmFtb1NlbmQubW9ja0ltcGxlbWVudGF0aW9uKChjb21tYW5kOiB1bmtub3duKSA9PlxuICAgICAgICBjb21tYW5kIGluc3RhbmNlb2YgUXVlcnlDb21tYW5kXG4gICAgICAgICAgPyBQcm9taXNlLnJlc29sdmUoeyBJdGVtczogW10gfSlcbiAgICAgICAgICA6IFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcIlByb3Zpc2lvbmVkVGhyb3VnaHB1dEV4Y2VlZGVkXCIpKSxcbiAgICAgICk7XG4gICAgICBtb2NrU3NtU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7XG4gICAgICAgIFBhcmFtZXRlcjogeyBWYWx1ZTogXCJ0ZXN0LWRpc3RyaWJ1dGlvbi1pZFwiIH0sXG4gICAgICB9KTtcbiAgICAgIG1vY2tDbG91ZEZyb250U2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7fSk7XG5cbiAgICAgIGF3YWl0IG5ldyBTM0NhY2hlSGFuZGxlcih7IGNvbnRleHQ6IG1vY2tDb250ZXh0IH0pLnJldmFsaWRhdGVUYWcoXCJwb3N0c1wiKTtcblxuICAgICAgZXhwZWN0KGludmFsaWRhdGlvbnMoKSkudG9FcXVhbChbW1wiLypcIl1dKTtcbiAgICB9KTtcblxuICAgIGl0KFwiaW52YWxpZGF0ZXMgdGhlIHdob2xlIGFwcCB3aGVuIGEgdGFnJ3MgbWFwcGluZyByb3dzIGNhbm5vdCBiZSByZWFkXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIC8vIFRoZSBtYXJrZXIgcm93IGlzIHdyaXR0ZW4gYmVmb3JlIHRoZSBRdWVyeSwgc28gYSBmYWlsZWQgUXVlcnkgbGVhdmVzXG4gICAgICAvLyB0aGUgdGFnIHJldmFsaWRhdGVkIGF0IHRoZSBvcmlnaW4gd2l0aCBubyBwYXRocyBuYW1lZCBmb3IgdGhlIGVkZ2UuXG4gICAgICBwcm9jZXNzLmVudi5DREtfTkVYVEpTX0RJU1RSSUJVVElPTl9JRF9QQVJBTV9OQU1FID0gXCJ0ZXN0LXBhcmFtLW5hbWVcIjtcbiAgICAgIGplc3Quc3B5T24oY29uc29sZSwgXCJlcnJvclwiKS5tb2NrSW1wbGVtZW50YXRpb24oKTtcbiAgICAgIG1vY2tEeW5hbW9TZW5kLm1vY2tJbXBsZW1lbnRhdGlvbigoY29tbWFuZDogdW5rbm93bikgPT5cbiAgICAgICAgY29tbWFuZCBpbnN0YW5jZW9mIFF1ZXJ5Q29tbWFuZFxuICAgICAgICAgID8gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiUHJvdmlzaW9uZWRUaHJvdWdocHV0RXhjZWVkZWRcIikpXG4gICAgICAgICAgOiBQcm9taXNlLnJlc29sdmUoe30pLFxuICAgICAgKTtcbiAgICAgIG1vY2tTc21TZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHtcbiAgICAgICAgUGFyYW1ldGVyOiB7IFZhbHVlOiBcInRlc3QtZGlzdHJpYnV0aW9uLWlkXCIgfSxcbiAgICAgIH0pO1xuICAgICAgbW9ja0Nsb3VkRnJvbnRTZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHt9KTtcblxuICAgICAgYXdhaXQgbmV3IFMzQ2FjaGVIYW5kbGVyKHsgY29udGV4dDogbW9ja0NvbnRleHQgfSkucmV2YWxpZGF0ZVRhZyhbXG4gICAgICAgIFwiX05fVF8vYVwiLFxuICAgICAgICBcInBvc3RzXCIsXG4gICAgICBdKTtcblxuICAgICAgZXhwZWN0KGludmFsaWRhdGlvbnMoKSkudG9FcXVhbChbW1wiLypcIl1dKTtcbiAgICB9KTtcblxuICAgIGRlc2NyaWJlKFwid2hlbiBDbG91ZEZyb250J3MgaW52YWxpZGF0aW9uIHF1b3RhIGlzIGZ1bGxcIiwgKCkgPT4ge1xuICAgICAgYmVmb3JlRWFjaCgoKSA9PiB7XG4gICAgICAgIGplc3QudXNlRmFrZVRpbWVycygpO1xuICAgICAgfSk7XG4gICAgICBhZnRlckVhY2goKCkgPT4ge1xuICAgICAgICBqZXN0LnVzZVJlYWxUaW1lcnMoKTtcbiAgICAgIH0pO1xuXG4gICAgICBjb25zdCBxdW90YUVycm9yID0gKCkgPT5cbiAgICAgICAgT2JqZWN0LmFzc2lnbihcbiAgICAgICAgICBuZXcgRXJyb3IoXCJZb3VyIHJlcXVlc3QgY29udGFpbnMgdG9vIG1hbnkgaW52YWxpZGF0aW9uc1wiKSxcbiAgICAgICAgICB7XG4gICAgICAgICAgICBuYW1lOiBcIlRvb01hbnlJbnZhbGlkYXRpb25zSW5Qcm9ncmVzc1wiLFxuICAgICAgICAgIH0sXG4gICAgICAgICk7XG5cbiAgICAgIGl0KFwicmV0cmllcyBhcyBvbmUgYXBwLXdpZGUgd2lsZGNhcmQgYWZ0ZXIgYSBiYWNrb2ZmXCIsIGFzeW5jICgpID0+IHtcbiAgICAgICAgY29uc3QgY2RuSGFuZGxlciA9IHdpdGhEaXN0cmlidXRpb24ocGFnZXMoXCJ0ZXN0LXRhZ1wiLCAyKSk7XG4gICAgICAgIG1vY2tDbG91ZEZyb250U2VuZFxuICAgICAgICAgIC5tb2NrUmVqZWN0ZWRWYWx1ZU9uY2UocXVvdGFFcnJvcigpKVxuICAgICAgICAgIC5tb2NrUmVzb2x2ZWRWYWx1ZU9uY2Uoe30pO1xuXG4gICAgICAgIGNvbnN0IGRvbmUgPSBjZG5IYW5kbGVyLnJldmFsaWRhdGVUYWcoXCJ0ZXN0LXRhZ1wiKTtcbiAgICAgICAgYXdhaXQgamVzdC5hZHZhbmNlVGltZXJzQnlUaW1lQXN5bmMoMTAwMCk7XG4gICAgICAgIGF3YWl0IGRvbmU7XG5cbiAgICAgICAgZXhwZWN0KGludmFsaWRhdGlvbnMoKSkudG9FcXVhbChbW1wiL2lzci8wKlwiLCBcIi9pc3IvMSpcIl0sIFtcIi8qXCJdXSk7XG4gICAgICB9KTtcblxuICAgICAgaXQoXCJnaXZlcyB1cCBhZnRlciB0aGUgbGFzdCByZXRyeVwiLCBhc3luYyAoKSA9PiB7XG4gICAgICAgIGNvbnN0IGNkbkhhbmRsZXIgPSB3aXRoRGlzdHJpYnV0aW9uKHBhZ2VzKFwidGVzdC10YWdcIiwgMSkpO1xuICAgICAgICBtb2NrQ2xvdWRGcm9udFNlbmQubW9ja1JlamVjdGVkVmFsdWUocXVvdGFFcnJvcigpKTtcblxuICAgICAgICBjb25zdCBkb25lID0gY2RuSGFuZGxlci5yZXZhbGlkYXRlVGFnKFwidGVzdC10YWdcIik7XG4gICAgICAgIGF3YWl0IGplc3QuYWR2YW5jZVRpbWVyc0J5VGltZUFzeW5jKDMwMDApO1xuICAgICAgICBhd2FpdCBleHBlY3QoZG9uZSkucmVzb2x2ZXMudG9CZVVuZGVmaW5lZCgpO1xuXG4gICAgICAgIGV4cGVjdChpbnZhbGlkYXRpb25zKCkpLnRvRXF1YWwoW1tcIi9pc3IvMCpcIl0sIFtcIi8qXCJdLCBbXCIvKlwiXV0pO1xuICAgICAgfSk7XG5cbiAgICAgIGl0KFwiZG9lcyBub3QgcmV0cnkgYW55IG90aGVyIGVycm9yXCIsIGFzeW5jICgpID0+IHtcbiAgICAgICAgY29uc3QgY2RuSGFuZGxlciA9IHdpdGhEaXN0cmlidXRpb24ocGFnZXMoXCJ0ZXN0LXRhZ1wiLCAxKSk7XG4gICAgICAgIG1vY2tDbG91ZEZyb250U2VuZC5tb2NrUmVqZWN0ZWRWYWx1ZShuZXcgRXJyb3IoXCJBY2Nlc3NEZW5pZWRcIikpO1xuXG4gICAgICAgIGF3YWl0IGNkbkhhbmRsZXIucmV2YWxpZGF0ZVRhZyhcInRlc3QtdGFnXCIpO1xuXG4gICAgICAgIGV4cGVjdChpbnZhbGlkYXRpb25zKCkpLnRvRXF1YWwoW1tcIi9pc3IvMCpcIl1dKTtcbiAgICAgIH0pO1xuXG4gICAgICBpdChcInJldHJpZXMgdGhlIGFwcCdzIHJvb3QgdW5kZXIgYSBiYXNlUGF0aCB0b29cIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgICAvLyBgL2Jhc2UvKmAgbWF0Y2hlcyBuZWl0aGVyIGAvYmFzZWAgbm9yIGAvYmFzZT9fcnNjPeKApmA6IHJldHJ5aW5nXG4gICAgICAgIC8vIGByZXZhbGlkYXRlUGF0aChcIi9cIilgIGFzIHRoYXQgbGVmdCB0aGUgaG9tZSBwYWdlIHN0YWxlIGF0IHRoZSBlZGdlLlxuICAgICAgICB1c2VCYXNlUGF0aCgpO1xuICAgICAgICBjb25zdCBjZG5IYW5kbGVyID0gd2l0aERpc3RyaWJ1dGlvbih7IEl0ZW1zOiBbXSB9KTtcbiAgICAgICAgbW9ja0Nsb3VkRnJvbnRTZW5kXG4gICAgICAgICAgLm1vY2tSZWplY3RlZFZhbHVlT25jZShxdW90YUVycm9yKCkpXG4gICAgICAgICAgLm1vY2tSZXNvbHZlZFZhbHVlT25jZSh7fSk7XG5cbiAgICAgICAgY29uc3QgZG9uZSA9IGNkbkhhbmRsZXIucmV2YWxpZGF0ZVRhZyhcIl9OX1RfL1wiKTtcbiAgICAgICAgYXdhaXQgamVzdC5hZHZhbmNlVGltZXJzQnlUaW1lQXN5bmMoMTAwMCk7XG4gICAgICAgIGF3YWl0IGRvbmU7XG5cbiAgICAgICAgZXhwZWN0KGludmFsaWRhdGlvbnMoKVsxXSkudG9FcXVhbChbXCIvYmFzZVwiLCBcIi9iYXNlPypcIiwgXCIvYmFzZS8qXCJdKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgaXQoXCJyZWNvcmRzIGEgcHJvZmlsZSdzIHJldmFsaWRhdGlvbiBhcyBzdGFsZSBub3csIGV4cGlyaW5nIGxhdGVyXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIGNvbnN0IGJlZm9yZSA9IERhdGUubm93KCk7XG4gICAgICBhd2FpdCBoYW5kbGVyLnJldmFsaWRhdGVUYWcoXCJwb3N0c1wiLCB7IGV4cGlyZTogNjAgfSk7XG5cbiAgICAgIGNvbnN0IFtpbnB1dF0gPSAoVXBkYXRlSXRlbUNvbW1hbmQgYXMgdW5rbm93biBhcyBqZXN0Lk1vY2spLm1vY2suY2FsbHNbMF07XG4gICAgICBleHBlY3QoaW5wdXQuS2V5LnNrLlMpLnRvQmUoXCJwb3N0c1wiKTtcbiAgICAgIGV4cGVjdChpbnB1dC5VcGRhdGVFeHByZXNzaW9uKS50b0JlKFxuICAgICAgICBcIlNFVCBzdGFsZUF0ID0gOnN0YWxlQXQsIGV4cGlyZWRBdCA9IDpleHBpcmVkQXRcIixcbiAgICAgICk7XG4gICAgICBjb25zdCBzdGFsZUF0ID0gTnVtYmVyKGlucHV0LkV4cHJlc3Npb25BdHRyaWJ1dGVWYWx1ZXNbXCI6c3RhbGVBdFwiXS5OKTtcbiAgICAgIGV4cGVjdChzdGFsZUF0KS50b0JlR3JlYXRlclRoYW5PckVxdWFsKGJlZm9yZSk7XG4gICAgICBleHBlY3QoTnVtYmVyKGlucHV0LkV4cHJlc3Npb25BdHRyaWJ1dGVWYWx1ZXNbXCI6ZXhwaXJlZEF0XCJdLk4pKS50b0JlKFxuICAgICAgICBzdGFsZUF0ICsgNjBfMDAwLFxuICAgICAgKTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgLyoqXG4gICAqIFRoZSBjYWNoZSBoYW5kbGVyJ3MgaGFsZiBvZiBgcmVzLnJldmFsaWRhdGUoKWAncyBDRE4gaW52YWxpZGF0aW9uOiB0aGUgaG9va1xuICAgKiBpdCByZWdpc3RlcnMgZm9yIGBpbnZhbGlkYXRlUmV2YWxpZGF0ZWRQYWdlYCBpbiBgcnVudGltZS9jb3JlLnRzYC4gVGhlXG4gICAqIHJ1bnRpbWUncyBoYWxmIGlzIGluIGBjb3JlLnRlc3QudHNgLlxuICAgKi9cbiAgZGVzY3JpYmUoXCJ0aGUgcmV2YWxpZGF0ZWQtcGFnZSBob29rXCIsICgpID0+IHtcbiAgICBjb25zdCBIT09LID0gUkVWQUxJREFURURfUEFHRV9IT09LO1xuICAgIGNvbnN0IGdsb2JhbHMgPSBnbG9iYWxUaGlzIGFzIFJlY29yZDxzeW1ib2wsIHVua25vd24+O1xuICAgIHR5cGUgSG9vayA9IChyb3V0ZXM6IHJlYWRvbmx5IHN0cmluZ1tdKSA9PiBQcm9taXNlPHZvaWQ+O1xuXG4gICAgYmVmb3JlRWFjaCgoKSA9PiB7XG4gICAgICBkZWxldGUgZ2xvYmFsc1tIT09LXTtcbiAgICAgIG1vY2tTc21TZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHsgUGFyYW1ldGVyOiB7IFZhbHVlOiBcIkVESVNUUklCVVRJT05cIiB9IH0pO1xuICAgICAgbW9ja0Nsb3VkRnJvbnRTZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHt9KTtcbiAgICAgIChDcmVhdGVJbnZhbGlkYXRpb25Db21tYW5kIGFzIHVua25vd24gYXMgamVzdC5Nb2NrKS5tb2NrQ2xlYXIoKTtcbiAgICB9KTtcblxuICAgIGZ1bmN0aW9uIGludmFsaWRhdGVkUGF0aHMoKTogc3RyaW5nW10ge1xuICAgICAgY29uc3QgW2lucHV0XSA9IChDcmVhdGVJbnZhbGlkYXRpb25Db21tYW5kIGFzIHVua25vd24gYXMgamVzdC5Nb2NrKS5tb2NrXG4gICAgICAgIC5jYWxsc1swXTtcbiAgICAgIHJldHVybiBpbnB1dC5JbnZhbGlkYXRpb25CYXRjaC5QYXRocy5JdGVtcztcbiAgICB9XG5cbiAgICBpdChcImlzIHJlZ2lzdGVyZWQgb25seSBiZWhpbmQgYSBkaXN0cmlidXRpb25cIiwgKCkgPT4ge1xuICAgICAgbmV3IFMzQ2FjaGVIYW5kbGVyKHsgY29udGV4dDogbW9ja0NvbnRleHQgfSk7XG4gICAgICBleHBlY3QoZ2xvYmFsc1tIT09LXSkudG9CZVVuZGVmaW5lZCgpO1xuXG4gICAgICBwcm9jZXNzLmVudi5DREtfTkVYVEpTX0RJU1RSSUJVVElPTl9JRF9QQVJBTV9OQU1FID0gXCJwYXJhbVwiO1xuICAgICAgbmV3IFMzQ2FjaGVIYW5kbGVyKHsgY29udGV4dDogbW9ja0NvbnRleHQgfSk7XG4gICAgICBleHBlY3QodHlwZW9mIGdsb2JhbHNbSE9PS10pLnRvQmUoXCJmdW5jdGlvblwiKTtcbiAgICB9KTtcblxuICAgIGl0KFwiaW52YWxpZGF0ZXMgdGhlIHBhZ2UgYW5kIGl0cyBkYXRhIHJvdXRlIHVuZGVyIGJhc2VQYXRoXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIHByb2Nlc3MuZW52LkNES19ORVhUSlNfRElTVFJJQlVUSU9OX0lEX1BBUkFNX05BTUUgPSBcInBhcmFtXCI7XG4gICAgICBuZXcgUzNDYWNoZUhhbmRsZXIoeyBjb250ZXh0OiBtb2NrQ29udGV4dCwgYmFzZVBhdGg6IFwiYmFzZVwiIH0pO1xuICAgICAgYXdhaXQgKGdsb2JhbHNbSE9PS10gYXMgSG9vaykoW1xuICAgICAgICBcIi9ibG9nL2hlbGxvXCIsXG4gICAgICAgIFwiL19uZXh0L2RhdGEvdGVzdC1idWlsZC1pZC9ibG9nL2hlbGxvLmpzb25cIixcbiAgICAgIF0pO1xuICAgICAgZXhwZWN0KGludmFsaWRhdGVkUGF0aHMoKSkudG9FcXVhbChbXG4gICAgICAgIFwiL2Jhc2UvYmxvZy9oZWxsbypcIixcbiAgICAgICAgXCIvYmFzZS9fbmV4dC9kYXRhL3Rlc3QtYnVpbGQtaWQvYmxvZy9oZWxsby5qc29uKlwiLFxuICAgICAgXSk7XG4gICAgfSk7XG5cbiAgICBpdChcInNwZWxscyBvdXQgdGhlIHJvb3QgcmF0aGVyIHRoYW4gaW52YWxpZGF0aW5nIHRoZSB3aG9sZSBhcHBcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgcHJvY2Vzcy5lbnYuQ0RLX05FWFRKU19ESVNUUklCVVRJT05fSURfUEFSQU1fTkFNRSA9IFwicGFyYW1cIjtcbiAgICAgIG5ldyBTM0NhY2hlSGFuZGxlcih7IGNvbnRleHQ6IG1vY2tDb250ZXh0IH0pO1xuICAgICAgYXdhaXQgKGdsb2JhbHNbSE9PS10gYXMgSG9vaykoW1xuICAgICAgICBcIi9cIixcbiAgICAgICAgXCIvX25leHQvZGF0YS90ZXN0LWJ1aWxkLWlkL2luZGV4Lmpzb25cIixcbiAgICAgIF0pO1xuICAgICAgZXhwZWN0KGludmFsaWRhdGVkUGF0aHMoKSkudG9FcXVhbChbXG4gICAgICAgIFwiL1wiLFxuICAgICAgICBcIi8/KlwiLFxuICAgICAgICBcIi9fbmV4dC9kYXRhL3Rlc3QtYnVpbGQtaWQvaW5kZXguanNvbipcIixcbiAgICAgIF0pO1xuICAgIH0pO1xuXG4gICAgaXQoXCJzZW5kcyBldmVyeSBwYXRoIG9uY2UsIGluIG9uZSBpbnZhbGlkYXRpb25cIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgcHJvY2Vzcy5lbnYuQ0RLX05FWFRKU19ESVNUUklCVVRJT05fSURfUEFSQU1fTkFNRSA9IFwicGFyYW1cIjtcbiAgICAgIG5ldyBTM0NhY2hlSGFuZGxlcih7IGNvbnRleHQ6IG1vY2tDb250ZXh0IH0pO1xuICAgICAgYXdhaXQgKGdsb2JhbHNbSE9PS10gYXMgSG9vaykoW1xuICAgICAgICBcIi9ibG9nXCIsXG4gICAgICAgIFwiL19uZXh0L2RhdGEvdGVzdC1idWlsZC1pZC9lbi9ibG9nLmpzb25cIixcbiAgICAgICAgXCIvZW4vYmxvZ1wiLFxuICAgICAgICBcIi9ibG9nXCIsXG4gICAgICBdKTtcbiAgICAgIGV4cGVjdChDcmVhdGVJbnZhbGlkYXRpb25Db21tYW5kKS50b0hhdmVCZWVuQ2FsbGVkVGltZXMoMSk7XG4gICAgICBleHBlY3QoaW52YWxpZGF0ZWRQYXRocygpKS50b0VxdWFsKFtcbiAgICAgICAgXCIvYmxvZypcIixcbiAgICAgICAgXCIvX25leHQvZGF0YS90ZXN0LWJ1aWxkLWlkL2VuL2Jsb2cuanNvbipcIixcbiAgICAgICAgXCIvZW4vYmxvZypcIixcbiAgICAgIF0pO1xuICAgIH0pO1xuICB9KTtcblxuICBkZXNjcmliZShcInJlc2V0UmVxdWVzdENhY2hlXCIsICgpID0+IHtcbiAgICBpdChcInNob3VsZCBjb21wbGV0ZSB3aXRob3V0IGVycm9yc1wiLCBhc3luYyAoKSA9PiB7XG4gICAgICBhd2FpdCBleHBlY3QoaGFuZGxlci5yZXNldFJlcXVlc3RDYWNoZSgpKS5yZXNvbHZlcy5ub3QudG9UaHJvdygpO1xuICAgIH0pO1xuICB9KTtcbn0pO1xuIl19
|