cdk-nextjs 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.jsii +2467 -2714
- package/.jsii.tabl.json +1 -1
- package/API.md +1725 -1909
- package/CLAUDE.md +2 -1
- package/README.md +429 -130
- package/assets/lambdas/post-deploy/post-deploy.lambda/index.js +152 -170
- package/assets/lambdas/redeploy-stage/redeploy-stage.lambda/index.js +875 -0
- package/docs/adapter-runtime.md +185 -0
- package/docs/breaking-changes.md +687 -4
- package/docs/caching-guide.md +156 -112
- package/docs/cdk-nextjs-cfn-exec-policy.json +2 -1
- package/docs/development-guide.md +1 -1
- package/docs/harness-coverage.md +598 -0
- package/docs/init-cache-deployment.md +32 -1
- package/docs/next-build-output-guide.md +30 -20
- package/examples/README.md +5 -1
- package/lib/adapter/__fixtures__/app-playground-base-path.json +4926 -0
- package/lib/adapter/__fixtures__/app-playground.json +4917 -0
- package/lib/adapter/__fixtures__/pages-i18n.json +1728 -0
- package/lib/adapter/adapter.mjs +1548 -185
- package/lib/adapter/adapter.test.js +106 -0
- package/lib/adapter/aws-cache-store.d.ts +489 -0
- package/lib/adapter/aws-cache-store.js +1004 -0
- package/lib/adapter/aws-cache-store.test.d.ts +1 -0
- package/lib/adapter/aws-cache-store.test.js +361 -0
- package/lib/adapter/build-outputs.d.ts +80 -0
- package/lib/adapter/build-outputs.js +852 -0
- package/lib/adapter/build-outputs.test.d.ts +1 -0
- package/lib/adapter/build-outputs.test.js +861 -0
- package/lib/adapter/cache-handler.d.ts +9 -2
- package/lib/adapter/cache-handler.js +72 -96
- package/lib/adapter/cache-handler.mjs +18956 -17774
- package/lib/adapter/cache-handler.test.js +263 -31
- package/lib/adapter/cache-kinds.d.ts +32 -0
- package/lib/adapter/cache-kinds.js +35 -0
- package/lib/adapter/cache-kinds.test.d.ts +1 -0
- package/lib/adapter/cache-kinds.test.js +69 -0
- package/lib/adapter/cache-utils.d.ts +90 -0
- package/lib/adapter/cache-utils.js +199 -4
- package/lib/adapter/cache-utils.test.d.ts +1 -0
- package/lib/adapter/cache-utils.test.js +179 -0
- package/lib/adapter/function-groups.d.ts +215 -0
- package/lib/adapter/function-groups.js +1099 -0
- package/lib/adapter/function-groups.test.d.ts +1 -0
- package/lib/adapter/function-groups.test.js +743 -0
- package/lib/adapter/init-cache.d.ts +6 -0
- package/lib/adapter/init-cache.js +245 -0
- package/lib/adapter/init-cache.test.d.ts +1 -0
- package/lib/adapter/init-cache.test.js +137 -0
- package/lib/adapter/local-file-cache-handler.d.ts +23 -0
- package/lib/adapter/local-file-cache-handler.js +52 -1
- package/lib/adapter/memory-cache-handler.d.ts +26 -17
- package/lib/adapter/memory-cache-handler.js +32 -50
- package/lib/adapter/memory-cache-handler.test.js +105 -85
- package/lib/adapter/s3-cache-handler.d.ts +179 -40
- package/lib/adapter/s3-cache-handler.js +807 -277
- package/lib/adapter/s3-cache-handler.test.js +1186 -31
- package/lib/adapter/use-cache-common.d.ts +90 -0
- package/lib/adapter/use-cache-common.js +250 -0
- package/lib/adapter/use-cache-common.test.d.ts +1 -0
- package/lib/adapter/use-cache-common.test.js +646 -0
- package/lib/adapter/use-cache-default-handler.d.ts +24 -0
- package/lib/adapter/use-cache-default-handler.js +90 -0
- package/lib/adapter/use-cache-default-handler.mjs +38645 -0
- package/lib/adapter/use-cache-handlers.test.d.ts +1 -0
- package/lib/adapter/use-cache-handlers.test.js +349 -0
- package/lib/adapter/use-cache-remote-handler.d.ts +26 -0
- package/lib/adapter/use-cache-remote-handler.js +186 -0
- package/lib/{image-optimization/handler.mjs → adapter/use-cache-remote-handler.mjs} +33572 -38798
- package/lib/generated-structs/OptionalNextjsApiProps.d.ts +59 -0
- package/lib/generated-structs/OptionalNextjsApiProps.js +3 -0
- package/lib/generated-structs/OptionalNextjsCacheProps.d.ts +15 -6
- package/lib/generated-structs/OptionalNextjsCacheProps.js +1 -1
- package/lib/generated-structs/OptionalNextjsContainersProps.d.ts +25 -11
- package/lib/generated-structs/OptionalNextjsContainersProps.js +1 -1
- package/lib/generated-structs/OptionalNextjsDistributionProps.d.ts +31 -9
- package/lib/generated-structs/OptionalNextjsDistributionProps.js +1 -1
- package/lib/generated-structs/OptionalNextjsPostDeployProps.d.ts +13 -0
- package/lib/generated-structs/OptionalNextjsPostDeployProps.js +1 -1
- package/lib/generated-structs/OptionalPostDeployCustomResourceProperties.d.ts +11 -2
- package/lib/generated-structs/OptionalPostDeployCustomResourceProperties.js +1 -1
- package/lib/index.d.ts +6 -7
- package/lib/index.js +2 -4
- package/lib/lambdas/post-deploy/post-deploy.lambda.js +3 -2
- package/lib/lambdas/post-deploy/prune-cache-bucket.d.ts +7 -3
- package/lib/lambdas/post-deploy/prune-cache-bucket.js +57 -71
- package/lib/lambdas/post-deploy/prune-cache-bucket.test.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-cache-bucket.test.js +114 -0
- package/lib/lambdas/post-deploy/prune-revalidation-table.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-revalidation-table.js +23 -31
- package/lib/lambdas/post-deploy/prune-revalidation-table.test.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-revalidation-table.test.js +71 -0
- package/lib/lambdas/post-deploy/prune-s3.d.ts +19 -3
- package/lib/lambdas/post-deploy/prune-s3.js +54 -15
- package/lib/lambdas/post-deploy/prune-s3.test.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-s3.test.js +255 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage-function.d.ts +13 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage-function.js +55 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.d.ts +24 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.js +68 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.lambda.d.ts +20 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.lambda.js +24 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.test.d.ts +1 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.test.js +93 -0
- package/lib/nextjs-api.d.ts +107 -18
- package/lib/nextjs-api.js +349 -39
- package/lib/nextjs-api.test.js +582 -1
- package/lib/nextjs-build/global-containers.Dockerfile +15 -10
- package/lib/nextjs-build/nextjs-build.d.ts +339 -63
- package/lib/nextjs-build/nextjs-build.js +684 -327
- package/lib/nextjs-build/nextjs-build.test.d.ts +1 -0
- package/lib/nextjs-build/nextjs-build.test.js +412 -0
- package/lib/nextjs-build/patch-fetch.js +1 -1
- package/lib/nextjs-build/patch-fetch.test.js +166 -11
- package/lib/nextjs-build/regional-containers.Dockerfile +17 -10
- package/lib/nextjs-cache.d.ts +82 -4
- package/lib/nextjs-cache.js +186 -10
- package/lib/nextjs-cache.test.js +176 -1
- package/lib/nextjs-compute/nextjs-compute-base-props.d.ts +21 -8
- package/lib/nextjs-compute/nextjs-compute-base-props.js +41 -1
- package/lib/nextjs-compute/nextjs-compute-base-props.test.d.ts +1 -0
- package/lib/nextjs-compute/nextjs-compute-base-props.test.js +44 -0
- package/lib/nextjs-compute/nextjs-containers.d.ts +19 -0
- package/lib/nextjs-compute/nextjs-containers.js +57 -20
- package/lib/nextjs-compute/nextjs-functions.d.ts +122 -7
- package/lib/nextjs-compute/nextjs-functions.js +113 -67
- package/lib/nextjs-compute/nextjs-functions.test.d.ts +1 -0
- package/lib/nextjs-compute/nextjs-functions.test.js +99 -0
- package/lib/nextjs-distribution.d.ts +194 -23
- package/lib/nextjs-distribution.js +535 -62
- package/lib/nextjs-distribution.test.d.ts +1 -0
- package/lib/nextjs-distribution.test.js +697 -0
- package/lib/nextjs-post-deploy.d.ts +23 -3
- package/lib/nextjs-post-deploy.js +14 -10
- package/lib/nextjs-post-deploy.test.d.ts +1 -0
- package/lib/nextjs-post-deploy.test.js +34 -0
- package/lib/nextjs-static-assets.d.ts +23 -4
- package/lib/nextjs-static-assets.js +29 -6
- package/lib/nextjs-static-assets.test.js +20 -1
- package/lib/root-constructs/nextjs-base-construct.d.ts +105 -45
- package/lib/root-constructs/nextjs-base-construct.js +144 -34
- package/lib/root-constructs/nextjs-global-containers.d.ts +18 -20
- package/lib/root-constructs/nextjs-global-containers.js +20 -70
- package/lib/root-constructs/nextjs-global-functions.d.ts +26 -28
- package/lib/root-constructs/nextjs-global-functions.js +52 -69
- package/lib/root-constructs/nextjs-regional-containers.d.ts +25 -5
- package/lib/root-constructs/nextjs-regional-containers.js +17 -15
- package/lib/root-constructs/nextjs-regional-functions.d.ts +22 -18
- package/lib/root-constructs/nextjs-regional-functions.js +8 -25
- package/lib/runtime/api-gateway-path.d.ts +34 -0
- package/lib/runtime/api-gateway-path.js +71 -0
- package/lib/runtime/api-gateway-path.test.d.ts +1 -0
- package/lib/runtime/api-gateway-path.test.js +65 -0
- package/lib/runtime/case-redirect.d.ts +22 -0
- package/lib/runtime/case-redirect.js +63 -0
- package/lib/runtime/case-redirect.test.d.ts +1 -0
- package/lib/runtime/case-redirect.test.js +32 -0
- package/lib/runtime/core.d.ts +234 -0
- package/lib/runtime/core.js +1213 -0
- package/lib/runtime/core.test.d.ts +1 -0
- package/lib/runtime/core.test.js +1678 -0
- package/lib/runtime/deployment-root.d.ts +13 -0
- package/lib/runtime/deployment-root.js +29 -0
- package/lib/runtime/dispatch.d.ts +331 -0
- package/lib/runtime/dispatch.js +751 -0
- package/lib/runtime/dispatch.test.d.ts +1 -0
- package/lib/runtime/dispatch.test.js +853 -0
- package/lib/runtime/entrypoints.d.ts +57 -0
- package/lib/runtime/entrypoints.js +115 -0
- package/lib/runtime/http/abort-signal.d.ts +6 -0
- package/lib/runtime/http/abort-signal.js +19 -0
- package/lib/runtime/http/abort-signal.test.d.ts +1 -0
- package/lib/runtime/http/abort-signal.test.js +49 -0
- package/lib/runtime/http/node-server.d.ts +38 -0
- package/lib/runtime/http/node-server.js +99 -0
- package/lib/runtime/http/node-server.test.d.ts +1 -0
- package/lib/runtime/http/node-server.test.js +96 -0
- package/lib/runtime/http/node-sink.d.ts +16 -0
- package/lib/runtime/http/node-sink.js +48 -0
- package/lib/runtime/http/node-sink.test.d.ts +1 -0
- package/lib/runtime/http/node-sink.test.js +66 -0
- package/lib/runtime/http/request.d.ts +62 -0
- package/lib/runtime/http/request.js +134 -0
- package/lib/runtime/http/request.test.d.ts +1 -0
- package/lib/runtime/http/request.test.js +103 -0
- package/lib/runtime/http/response.d.ts +77 -0
- package/lib/runtime/http/response.js +229 -0
- package/lib/runtime/http/response.test.d.ts +1 -0
- package/lib/runtime/http/response.test.js +178 -0
- package/lib/runtime/http/sink.d.ts +73 -0
- package/lib/runtime/http/sink.js +188 -0
- package/lib/runtime/http/sink.test.d.ts +1 -0
- package/lib/runtime/http/sink.test.js +257 -0
- package/lib/runtime/image-utils.d.ts +78 -0
- package/lib/runtime/image-utils.js +129 -0
- package/lib/runtime/image-utils.test.d.ts +1 -0
- package/lib/runtime/image-utils.test.js +237 -0
- package/lib/runtime/image.d.ts +76 -0
- package/lib/runtime/image.js +296 -0
- package/lib/runtime/image.test.d.ts +1 -0
- package/lib/runtime/image.test.js +405 -0
- package/lib/runtime/lambda-event.d.ts +4 -0
- package/lib/runtime/lambda-event.js +113 -0
- package/lib/runtime/lambda-event.test.d.ts +1 -0
- package/lib/runtime/lambda-event.test.js +119 -0
- package/lib/runtime/lambda.mjs +41595 -0
- package/lib/runtime/load-module.d.ts +8 -0
- package/lib/runtime/load-module.js +35 -0
- package/lib/runtime/manifest.d.ts +215 -0
- package/lib/runtime/manifest.js +118 -0
- package/lib/runtime/middleware.d.ts +43 -0
- package/lib/runtime/middleware.js +113 -0
- package/lib/runtime/middleware.test.d.ts +1 -0
- package/lib/runtime/middleware.test.js +259 -0
- package/lib/runtime/next-modules.d.ts +55 -0
- package/lib/runtime/next-modules.js +124 -0
- package/lib/runtime/next-modules.test.d.ts +1 -0
- package/lib/runtime/next-modules.test.js +34 -0
- package/lib/runtime/public-files.d.ts +32 -0
- package/lib/runtime/public-files.js +117 -0
- package/lib/runtime/public-files.test.d.ts +1 -0
- package/lib/runtime/public-files.test.js +97 -0
- package/lib/runtime/server.mjs +41588 -0
- package/lib/runtime/static-files.d.ts +50 -0
- package/lib/runtime/static-files.js +266 -0
- package/lib/runtime/static-files.test.d.ts +1 -0
- package/lib/runtime/static-files.test.js +118 -0
- package/lib/runtime/util.d.ts +28 -0
- package/lib/runtime/util.js +79 -0
- package/lib/utils/base-path.d.ts +123 -11
- package/lib/utils/base-path.js +276 -39
- package/lib/utils/base-path.test.js +275 -50
- package/lib/utils/get-architecture.d.ts +7 -8
- package/lib/utils/get-architecture.js +12 -13
- package/lib/utils/zip-directory.d.ts +14 -0
- package/lib/utils/zip-directory.js +121 -0
- package/lib/utils/zip-directory.test.d.ts +1 -0
- package/lib/utils/zip-directory.test.js +99 -0
- package/package.json +22 -3
- package/scripts/capture-adapter-fixture.mjs +325 -0
- package/scripts/e2e-deploy.sh +398 -0
- package/scripts/e2e-harness/README.md +707 -0
- package/scripts/e2e-harness/app.js +370 -0
- package/scripts/e2e-harness/common.sh +249 -0
- package/scripts/e2e-harness/screen.mjs +302 -0
- package/scripts/e2e-harness/stage-proxy.mjs +145 -0
- package/scripts/e2e-harness/type-filters.mjs +47 -0
- package/scripts/e2e-logs.sh +75 -0
- package/scripts/e2e-offline.sh +91 -0
- package/scripts/e2e-sweep.sh +370 -0
- package/scripts/e2e-warm.sh +114 -0
- package/scripts/zero-config-build.mjs +418 -0
- package/docs/1k-concurrent-users-executions.png +0 -0
- package/docs/plans/adapter-runtime-release.md +0 -362
- package/docs/plans/phase-0-image-function-opt-in.md +0 -295
- package/lib/generated-structs/OptionalDockerImageFunctionProps.d.ts +0 -425
- package/lib/generated-structs/OptionalDockerImageFunctionProps.js +0 -3
- package/lib/image-optimization/handler-utils.d.ts +0 -34
- package/lib/image-optimization/handler-utils.js +0 -83
- package/lib/image-optimization/handler-utils.test.js +0 -178
- package/lib/nextjs-build/functions.Dockerfile +0 -31
- package/lib/nextjs-compute/nextjs-image-function.d.ts +0 -53
- package/lib/nextjs-compute/nextjs-image-function.js +0 -59
- package/lib/utils/experimental-flags.d.ts +0 -24
- package/lib/utils/experimental-flags.js +0 -30
- /package/lib/{image-optimization/handler-utils.test.d.ts → adapter/adapter.test.d.ts} +0 -0
|
@@ -9,200 +9,525 @@ exports.S3CacheHandler = void 0;
|
|
|
9
9
|
*/
|
|
10
10
|
/* eslint-disable import/no-extraneous-dependencies */
|
|
11
11
|
const node_crypto_1 = require("node:crypto");
|
|
12
|
+
const node_fs_1 = require("node:fs");
|
|
13
|
+
const node_module_1 = require("node:module");
|
|
12
14
|
const node_path_1 = require("node:path");
|
|
13
15
|
const client_cloudfront_1 = require("@aws-sdk/client-cloudfront");
|
|
14
16
|
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
15
17
|
const client_s3_1 = require("@aws-sdk/client-s3");
|
|
16
18
|
const client_ssm_1 = require("@aws-sdk/client-ssm");
|
|
17
19
|
const debug_1 = __importDefault(require("debug"));
|
|
20
|
+
const aws_cache_store_1 = require("./aws-cache-store");
|
|
18
21
|
const cache_utils_1 = require("./cache-utils");
|
|
22
|
+
const use_cache_common_1 = require("./use-cache-common");
|
|
23
|
+
const manifest_1 = require("../runtime/manifest");
|
|
24
|
+
const base_path_1 = require("../utils/base-path");
|
|
25
|
+
/**
|
|
26
|
+
* The tags a stored entry has to be tag-revalidated against.
|
|
27
|
+
*
|
|
28
|
+
* A runtime `set` records `tags` alongside the entry, but a build-time prerender
|
|
29
|
+
* does not: those entries are written as files by the adapter's
|
|
30
|
+
* `onBuildComplete` rather than through `set`, and carry their tags only in the
|
|
31
|
+
* render's `x-next-cache-tags` header — the same place Next.js's own
|
|
32
|
+
* `IncrementalCache.get` reads them from when it decides staleness. Without this
|
|
33
|
+
* fallback no prerendered page is reachable by `revalidateTag`/`revalidatePath`
|
|
34
|
+
* until some later runtime re-render happens to replace the seeded entry, so an
|
|
35
|
+
* app whose pages are all static never revalidates at all. Measured against
|
|
36
|
+
* next.js's `test/e2e/app-dir/resume-data-cache`.
|
|
37
|
+
*/
|
|
38
|
+
function entryTags(stored) {
|
|
39
|
+
return stored.tags?.length ? stored.tags : (0, cache_utils_1.headerTags)(stored.value);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The `lastModified` that tells Next.js "this entry is expired, re-render it
|
|
43
|
+
* before answering".
|
|
44
|
+
*
|
|
45
|
+
* `IncrementalCache.get` turns it into `isStale: -1`, which `app-page-runtime`
|
|
46
|
+
* reads as an on-demand revalidation and answers with a blocking render of
|
|
47
|
+
* *this* route, then stores it. Returning `null` instead is not the same thing:
|
|
48
|
+
* a miss on a PPR route whose `prerender-manifest.json` entry carries a
|
|
49
|
+
* `fallback` (`compute: "resuming"`) is answered from the route's fallback
|
|
50
|
+
* shell, which is served `cache-control: private, no-store` with no
|
|
51
|
+
* `x-nextjs-cache` header, and - unless `partialPrefetching` is on - is never
|
|
52
|
+
* upgraded into a concrete entry. So the first `revalidateTag` to reach a
|
|
53
|
+
* seeded prerender left that page uncacheable, and dynamically resumed per
|
|
54
|
+
* request, for the rest of the deployment's life. The isr e2e sees it as an
|
|
55
|
+
* absent `x-nextjs-cache`.
|
|
56
|
+
*/
|
|
57
|
+
const EXPIRED_LAST_MODIFIED = -1;
|
|
58
|
+
/**
|
|
59
|
+
* Whether a `get` is for the `fetch` cache rather than for a route's response,
|
|
60
|
+
* as a narrowing predicate so that `ctx.tags` and `ctx.softTags` — which only
|
|
61
|
+
* {@link GetIncrementalFetchCacheContext} has — are reachable without a cast.
|
|
62
|
+
*
|
|
63
|
+
* `IncrementalCacheKind.FETCH`, compared as its own string value so this file
|
|
64
|
+
* keeps importing no Next.js internals (the enum is `const`, so it has no
|
|
65
|
+
* runtime representation to import anyway).
|
|
66
|
+
*/
|
|
67
|
+
function isFetchCacheGet(ctx) {
|
|
68
|
+
return ctx.kind === "FETCH";
|
|
69
|
+
}
|
|
70
|
+
/** `NEXT_CACHE_IMPLICIT_TAG_ID`, inlined so this file imports no Next.js internals. */
|
|
71
|
+
const NEXT_CACHE_IMPLICIT_TAG_ID = "_N_T_";
|
|
72
|
+
/**
|
|
73
|
+
* The `type` argument of `revalidatePath`, which Next.js appends to the implicit
|
|
74
|
+
* tag as a path segment. @see implicitTagPaths
|
|
75
|
+
*/
|
|
76
|
+
const REVALIDATE_PATH_TYPES = ["layout", "page"];
|
|
77
|
+
/**
|
|
78
|
+
* The request paths a `revalidatePath` tag could name, or empty for an app tag.
|
|
79
|
+
*
|
|
80
|
+
* `revalidatePath("/blog")` reaches the cache handler as the implicit tag
|
|
81
|
+
* `_N_T_/blog` - the path is right there in the tag, which is what makes the CDN
|
|
82
|
+
* copy of a build-time prerender reachable at all. A `revalidateTag("posts")`
|
|
83
|
+
* carries no path and depends on the mapping rows instead.
|
|
84
|
+
*
|
|
85
|
+
* Two paths come back when the tag ends in a `revalidatePath` *type*, because
|
|
86
|
+
* `revalidatePath(path, type)` appends it to the tag
|
|
87
|
+
* (`next/dist/server/web/spec-extension/revalidate.js`):
|
|
88
|
+
* `revalidatePath("/blog", "layout")` is `_N_T_/blog/layout`, and
|
|
89
|
+
* `revalidatePath("/", "layout")` is `_N_T_/layout`. Reading either of those as a
|
|
90
|
+
* request path invalidates a URI that does not exist and never touches the one
|
|
91
|
+
* that does. The suffix cannot be told apart from a route that genuinely ends in
|
|
92
|
+
* `/layout`, so both readings are emitted: an invalidation path matching nothing
|
|
93
|
+
* costs a path, while missing the real one leaves the edge stale for the whole
|
|
94
|
+
* `s-maxage`.
|
|
95
|
+
*/
|
|
96
|
+
function implicitTagPaths(tag) {
|
|
97
|
+
if (!tag.startsWith(`${NEXT_CACHE_IMPLICIT_TAG_ID}/`)) {
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
const path = tag.slice(NEXT_CACHE_IMPLICIT_TAG_ID.length);
|
|
101
|
+
const candidates = [path];
|
|
102
|
+
for (const type of REVALIDATE_PATH_TYPES) {
|
|
103
|
+
if (path.endsWith(`/${type}`)) {
|
|
104
|
+
// `_N_T_/layout` is the root, not the empty path.
|
|
105
|
+
candidates.push(path.slice(0, -(type.length + 1)) || "/");
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// A dynamic route template ("/blog/[slug]") matches no cached URI. Harmless to
|
|
109
|
+
// send, but it costs an invalidation path, and those are metered.
|
|
110
|
+
return Array.from(new Set(candidates)).filter((p) => !p.includes("["));
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Invalidation paths covering every URI CloudFront could be holding the response
|
|
114
|
+
* for `route` under, once `basePath` is prefixed.
|
|
115
|
+
*
|
|
116
|
+
* An invalidation path matches only the query string it spells out, and a page's
|
|
117
|
+
* RSC payload is cached under `?_rsc=<hash>`; dropping the HTML while leaving the
|
|
118
|
+
* payload behind leaves the router navigating to the pre-revalidation page. A
|
|
119
|
+
* `trailingSlash` app caches the redirect target rather than the route, so the
|
|
120
|
+
* slash variant has to go too.
|
|
121
|
+
*
|
|
122
|
+
* One trailing wildcard covers all four (`/blog`, `/blog?…`, `/blog/`,
|
|
123
|
+
* `/blog/?…`) for a single wildcard path, where spelling them out cost two -
|
|
124
|
+
* and wildcards are the quota that runs out. It also matches `/blogroll` and
|
|
125
|
+
* everything under `/blog/`, which is a lower hit rate on those, never a stale
|
|
126
|
+
* page. The app's root is the exception: `/*` there is the whole app.
|
|
127
|
+
*
|
|
128
|
+
* `basePath` is prefixed here because neither source of a route carries it.
|
|
129
|
+
* Cache keys are routes — Next.js strips `basePath` before routing, so the cache
|
|
130
|
+
* handler never sees one — and `revalidatePath("/blog")` names the route as
|
|
131
|
+
* well. CloudFront only ever saw `/base/blog`, so invalidating `/blog` clears
|
|
132
|
+
* nothing and the edge keeps serving the pre-revalidation page until `s-maxage`
|
|
133
|
+
* expires. The app's root under a `basePath` is `/base`, not `/base/`.
|
|
134
|
+
*/
|
|
135
|
+
function cdnInvalidationPaths(route, basePath) {
|
|
136
|
+
const trimmed = route.replace(/\/+$/, "");
|
|
137
|
+
if (trimmed === "") {
|
|
138
|
+
return basePath
|
|
139
|
+
? [basePath, `${basePath}?*`, `${basePath}/`, `${basePath}/?*`]
|
|
140
|
+
: ["/", "/?*"];
|
|
141
|
+
}
|
|
142
|
+
return [`${basePath}${encodeInvalidationRoute(trimmed)}*`];
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* The app's `basePath`, from the `required-server-files.json` every deployment
|
|
146
|
+
* root stages next to `serverDistDir`: Next.js hands the cache handler no
|
|
147
|
+
* other copy.
|
|
148
|
+
*/
|
|
149
|
+
function appBasePath(serverDistDir) {
|
|
150
|
+
if (!serverDistDir) {
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
const file = (0, node_path_1.join)(serverDistDir, "..", "required-server-files.json");
|
|
155
|
+
return JSON.parse((0, node_fs_1.readFileSync)(file, "utf8")).config?.basePath;
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
console.warn("Could not read basePath for CDN invalidation:", error);
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* `route` as the URI a browser requests it under, which is what CloudFront
|
|
164
|
+
* caches it by.
|
|
165
|
+
*
|
|
166
|
+
* Next.js builds cache keys from the *decoded* pathname (`decodePathParams` in
|
|
167
|
+
* `route-module.js`), so `/blog/héllo` or `/blog/hello world` reach here raw,
|
|
168
|
+
* while CloudFront holds `/blog/h%C3%A9llo`, and an invalidation path matches
|
|
169
|
+
* the encoded form only - AWS requires non-ASCII and unsafe characters to be
|
|
170
|
+
* percent-encoded, and nothing else. Each segment is encoded the way
|
|
171
|
+
* `encodeURI` does, which leaves the characters a browser sends raw in a path
|
|
172
|
+
* (`@`, `:`, `,`, `;`, ...) alone. A `%` that already starts an escape is kept:
|
|
173
|
+
* `decodePathParams` re-escapes path delimiters (`%2F`, `%3F`, `%23`, `%5C`),
|
|
174
|
+
* and a `revalidatePath` tag may name the encoded path itself. `?` and `#` can
|
|
175
|
+
* only be in a decoded segment as data, so they are encoded too.
|
|
176
|
+
*/
|
|
177
|
+
function encodeInvalidationRoute(route) {
|
|
178
|
+
return route
|
|
179
|
+
.split("/")
|
|
180
|
+
.map((segment) => segment
|
|
181
|
+
.split(/(%[0-9A-Fa-f]{2})/)
|
|
182
|
+
.map((part, i) => i % 2 === 1
|
|
183
|
+
? part
|
|
184
|
+
: encodeURI(part).replace(/[?#]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`))
|
|
185
|
+
.join(""))
|
|
186
|
+
.join("/");
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Whether a cache key is a fetch-cache entry rather than a page route.
|
|
190
|
+
*
|
|
191
|
+
* Those keys are the hash Next.js derives from the request — a single segment of
|
|
192
|
+
* lowercase hex, long enough that no route pathname takes the same shape. They
|
|
193
|
+
* are mapped to tags exactly like pages are, but name no URI CloudFront could be
|
|
194
|
+
* holding, so an invalidation path built from one is always wasted.
|
|
195
|
+
*/
|
|
196
|
+
function isFetchCacheKey(key) {
|
|
197
|
+
return /^[0-9a-f]{32,}$/.test(key);
|
|
198
|
+
}
|
|
199
|
+
/** Whether CloudFront counts this path against the wildcard quota. */
|
|
200
|
+
function isWildcardPath(path) {
|
|
201
|
+
return path.includes("*");
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* The most wildcard paths one `revalidateTag` sends before collapsing to a single
|
|
205
|
+
* app-wide wildcard, and the most paths of any kind.
|
|
206
|
+
*
|
|
207
|
+
* CloudFront's wildcard quota is per *distribution*, not per request: the
|
|
208
|
+
* invalidations it has in progress share it, so splitting an oversized set into
|
|
209
|
+
* back-to-back requests is what gets the later ones rejected
|
|
210
|
+
* (`TooManyInvalidationsInProgress`, or a throttle under the newer rate-based
|
|
211
|
+
* quotas) - and a rejected request is a page that stays stale at the edge. So
|
|
212
|
+
* everything goes out as one request, and a set that would not fit in one is
|
|
213
|
+
* answered with the whole app (`/*`, see `wholeAppInvalidationPaths`) instead:
|
|
214
|
+
* a worse hit rate, and a strictly correct answer.
|
|
215
|
+
* @see https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html#limits-invalidations
|
|
216
|
+
*/
|
|
217
|
+
const MAX_WILDCARD_PATHS_PER_INVALIDATION = 15;
|
|
218
|
+
const MAX_PATHS_PER_INVALIDATION = 3000;
|
|
219
|
+
/**
|
|
220
|
+
* Retries of an invalidation CloudFront rejected for its in-progress quota, and
|
|
221
|
+
* the delay before the first; each later one waits twice as long.
|
|
222
|
+
*
|
|
223
|
+
* Sizing a request against the quota cannot see the invalidations other
|
|
224
|
+
* instances already have in flight, so two `revalidateTag`s close together can
|
|
225
|
+
* each fit on their own and still be rejected together. Dropping the rejected
|
|
226
|
+
* one left its pages stale at the edge for the whole `s-maxage`. The retry asks
|
|
227
|
+
* for the whole app instead, which is one wildcard - the least of the quota any
|
|
228
|
+
* request can take - at the cost of a colder edge.
|
|
229
|
+
*/
|
|
230
|
+
const INVALIDATION_RETRIES = 2;
|
|
231
|
+
const INVALIDATION_RETRY_DELAY_MS = 1000;
|
|
232
|
+
/** The errors that mean "the quota is full right now", not "this is wrong". */
|
|
233
|
+
function isInvalidationQuotaError(error) {
|
|
234
|
+
const name = error?.name;
|
|
235
|
+
return name === "TooManyInvalidationsInProgress" || name === "Throttling";
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* The most cache keys to remember as served stale while awaiting their
|
|
239
|
+
* regeneration. A background render that fails never calls `set`, so without a
|
|
240
|
+
* bound its key would stay behind for the life of the instance.
|
|
241
|
+
* @see S3CacheHandler.softRevalidatedKeys
|
|
242
|
+
*/
|
|
243
|
+
const MAX_SOFT_REVALIDATED_KEYS = 1000;
|
|
244
|
+
/**
|
|
245
|
+
* How many 1 MB Query pages of one tag's mapping rows to walk. A ceiling rather
|
|
246
|
+
* than a real limit: it bounds a runaway tag instead of paginating a whole table
|
|
247
|
+
* on a request path. A tag cut off here invalidates the whole app, because the
|
|
248
|
+
* rows it never read are pages whose edge copy would otherwise stay stale.
|
|
249
|
+
*/
|
|
250
|
+
const MAX_TAG_QUERY_PAGES = 20;
|
|
251
|
+
/**
|
|
252
|
+
* Next.js's in-process tag manifest, which `IncrementalCache.get` consults to
|
|
253
|
+
* decide whether an entry is stale - or `undefined` when it cannot be found
|
|
254
|
+
* unambiguously.
|
|
255
|
+
*
|
|
256
|
+
* `revalidateTag(tag, profile)` asks for stale-while-revalidate: the entry is
|
|
257
|
+
* served once more while a background render replaces it. A cache handler has
|
|
258
|
+
* no field to say "stale" in - `lastModified: -1` means expired, which blocks -
|
|
259
|
+
* so this sets the tag's `stale` timestamp where Next.js already looks for it,
|
|
260
|
+
* which is exactly what its own `FileSystemCache.revalidateTag` does. The module
|
|
261
|
+
* is read from the loaded-module cache rather than `require`d: the handler is
|
|
262
|
+
* bundled apart from the app, and resolving `next` from here could reach a
|
|
263
|
+
* different copy than the server loaded, whose manifest nothing reads. A stale
|
|
264
|
+
* mark written there would serve the entry as fresh, so without exactly one
|
|
265
|
+
* loaded copy the caller falls back to expiring the entry outright.
|
|
266
|
+
*/
|
|
267
|
+
const TAGS_MANIFEST_MODULE = "/next/dist/server/lib/incremental-cache/tags-manifest.external.js";
|
|
268
|
+
function nextTagsManifest() {
|
|
269
|
+
const cache = (0, node_module_1.createRequire)((0, node_path_1.join)(process.cwd(), "index.js")).cache;
|
|
270
|
+
const loaded = Object.keys(cache).filter((path) => path.replace(/\\/g, "/").endsWith(TAGS_MANIFEST_MODULE));
|
|
271
|
+
if (loaded.length !== 1) {
|
|
272
|
+
return undefined;
|
|
273
|
+
}
|
|
274
|
+
const manifest = cache[loaded[0]]?.exports?.tagsManifest;
|
|
275
|
+
return manifest instanceof Map ? manifest : undefined;
|
|
276
|
+
}
|
|
19
277
|
class S3CacheHandler {
|
|
20
278
|
s3Client;
|
|
21
279
|
dynamoClient;
|
|
22
280
|
cloudFrontClient;
|
|
23
281
|
ssmClient;
|
|
24
|
-
|
|
25
|
-
|
|
282
|
+
bucket;
|
|
283
|
+
tags;
|
|
284
|
+
config;
|
|
26
285
|
cloudFrontConfig;
|
|
27
286
|
debug = (0, debug_1.default)("cdk-nextjs:cache-handler:s3");
|
|
28
287
|
// Cached for the lifetime of this instance (i.e. the compute instance) once
|
|
29
288
|
// resolved, since a deployment's distribution ID never changes at runtime.
|
|
30
289
|
cachedDistributionId = null;
|
|
290
|
+
/**
|
|
291
|
+
* Cache keys this instance served stale because of a soft `revalidateTag`,
|
|
292
|
+
* whose regenerated entry `set` has yet to write.
|
|
293
|
+
*
|
|
294
|
+
* `revalidateTag(tag, profile)` invalidates CloudFront at once, but the next
|
|
295
|
+
* request is answered from the stale entry - stale-while-revalidate - and that
|
|
296
|
+
* response carries the entry's original `s-maxage`, so the edge caches the
|
|
297
|
+
* stale body again. The background render that replaces it runs in this same
|
|
298
|
+
* process, so `set` checks this set and invalidates the key's paths a second
|
|
299
|
+
* time once the fresh entry is in S3. Without that, the edge kept serving the
|
|
300
|
+
* pre-revalidation page for the whole `s-maxage`.
|
|
301
|
+
*
|
|
302
|
+
* Expiring soft-revalidated entries outright instead would also keep the edge
|
|
303
|
+
* right, but it turns every `revalidateTag(tag, "max")` into a blocking render
|
|
304
|
+
* at the origin, which is the thing a profile asks not to happen.
|
|
305
|
+
*/
|
|
306
|
+
softRevalidatedKeys = new Set();
|
|
307
|
+
/** @see buildTagManifest */
|
|
308
|
+
buildTags;
|
|
31
309
|
constructor(options) {
|
|
32
|
-
|
|
33
|
-
// Initialize S3 configuration from environment variables and options
|
|
34
|
-
this.s3Config = {
|
|
35
|
-
bucketName: options.s3Config?.bucketName ||
|
|
36
|
-
process.env.CDK_NEXTJS_CACHE_BUCKET_NAME ||
|
|
37
|
-
"",
|
|
38
|
-
region: options.s3Config?.region || process.env.AWS_REGION || "us-east-1",
|
|
39
|
-
buildId: options.s3Config?.buildId || buildId,
|
|
40
|
-
};
|
|
41
|
-
// Initialize DynamoDB configuration from environment variables and options
|
|
42
|
-
this.dynamoConfig = {
|
|
43
|
-
tableName: options.dynamoConfig?.tableName ||
|
|
44
|
-
process.env.CDK_NEXTJS_REVALIDATION_TABLE_NAME ||
|
|
45
|
-
"",
|
|
46
|
-
region: options.dynamoConfig?.region || process.env.AWS_REGION || "us-east-1",
|
|
47
|
-
buildId: options.dynamoConfig?.buildId || buildId,
|
|
48
|
-
};
|
|
49
|
-
// Initialize CloudFront configuration from environment variables and options.
|
|
310
|
+
this.config = (0, aws_cache_store_1.resolveAwsCacheConfig)();
|
|
50
311
|
// Only set for CloudFront-fronted deployments (NextjsGlobalFunctions/Containers).
|
|
51
312
|
// When unset, on-demand revalidation skips CDN invalidation and relies on the
|
|
52
313
|
// distribution's cache policy TTL (driven by the origin's Cache-Control header)
|
|
53
314
|
// to eventually pick up fresh content.
|
|
54
315
|
this.cloudFrontConfig = {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
region: options.cloudFrontConfig?.region ||
|
|
59
|
-
process.env.AWS_REGION ||
|
|
60
|
-
"us-east-1",
|
|
316
|
+
distributionId: process.env.CDK_NEXTJS_DISTRIBUTION_ID || "",
|
|
317
|
+
distributionIdParameterName: process.env.CDK_NEXTJS_DISTRIBUTION_ID_PARAM_NAME || "",
|
|
318
|
+
basePath: (0, base_path_1.basePathPrefix)(options.basePath),
|
|
61
319
|
};
|
|
320
|
+
// Only invalidation needs it, so only a handler behind a distribution reads
|
|
321
|
+
// it, and a missing file there is worth a warning.
|
|
322
|
+
if (this.invalidatesCdn && options.basePath === undefined) {
|
|
323
|
+
this.cloudFrontConfig.basePath = (0, base_path_1.basePathPrefix)(appBasePath(options.context.serverDistDir));
|
|
324
|
+
}
|
|
62
325
|
// Initialize AWS clients
|
|
63
|
-
|
|
64
|
-
this.
|
|
65
|
-
|
|
66
|
-
});
|
|
67
|
-
this.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
326
|
+
const { region } = this.config;
|
|
327
|
+
this.s3Client = new client_s3_1.S3Client({ region });
|
|
328
|
+
this.dynamoClient = new client_dynamodb_1.DynamoDBClient({ region });
|
|
329
|
+
this.cloudFrontClient = new client_cloudfront_1.CloudFrontClient({ region });
|
|
330
|
+
this.ssmClient = new client_ssm_1.SSMClient({ region });
|
|
331
|
+
this.bucket = new aws_cache_store_1.CacheBucket(this.s3Client, this.config.bucketName);
|
|
332
|
+
// The process's one copy of the tag markers, shared with the `'use cache'`
|
|
333
|
+
// handlers: one `revalidateTag` writes each tag's rows once, and one log
|
|
334
|
+
// `Query` per interval serves every handler.
|
|
335
|
+
this.tags = (0, use_cache_common_1.sharedTagManifest)();
|
|
336
|
+
// `res.revalidate()` regenerates a page without going through
|
|
337
|
+
// `revalidateTag`, so the runtime asks for the CDN copies itself, through
|
|
338
|
+
// this hook (`invalidateRevalidatedPage` in `src/runtime/core.ts`). A global
|
|
339
|
+
// because the runtime and this handler are separate bundles in one process.
|
|
340
|
+
// Every instance registers the same thing, so the latest one winning is fine.
|
|
341
|
+
if (this.invalidatesCdn) {
|
|
342
|
+
globalThis[manifest_1.REVALIDATED_PAGE_HOOK] = (routes) => {
|
|
343
|
+
const { basePath } = this.cloudFrontConfig;
|
|
344
|
+
return this.invalidateCloudFrontPaths(routes.flatMap((route) => cdnInvalidationPaths(route, basePath)));
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
if (!this.config.bucketName) {
|
|
72
348
|
console.warn("CDK_NEXTJS_CACHE_BUCKET_NAME environment variable not set, S3 cache disabled");
|
|
73
349
|
}
|
|
74
|
-
if (!this.
|
|
350
|
+
if (!this.config.tableName) {
|
|
75
351
|
console.warn("CDK_NEXTJS_REVALIDATION_TABLE_NAME environment variable not set, revalidation tracking disabled");
|
|
76
352
|
}
|
|
77
|
-
if (!
|
|
353
|
+
if (!process.env.CDK_NEXTJS_BUILD_ID) {
|
|
78
354
|
console.warn("CDK_NEXTJS_BUILD_ID environment variable not set, cache isolation may not work correctly");
|
|
79
355
|
}
|
|
80
|
-
// Log the options for debugging (optional usage to avoid unused parameter warning)
|
|
81
|
-
if (options.context.dev) {
|
|
82
|
-
this.debug("S3DynamoCacheHandler initialized in development mode");
|
|
83
|
-
}
|
|
84
356
|
}
|
|
85
357
|
async get(cacheKey, ctx) {
|
|
86
358
|
try {
|
|
87
|
-
|
|
88
|
-
if (ctx.kind) {
|
|
89
|
-
this.debug(`S3 cache get operation for ${cacheKey} with kind: ${ctx.kind}`);
|
|
90
|
-
}
|
|
91
|
-
if (!this.s3Config.bucketName) {
|
|
359
|
+
if (!this.config.bucketName) {
|
|
92
360
|
return null;
|
|
93
361
|
}
|
|
94
362
|
const s3Key = this.buildS3Key(cacheKey);
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
});
|
|
99
|
-
const response = await this.s3Client.send(command);
|
|
100
|
-
if (!response.Body) {
|
|
363
|
+
const response = await this.bucket.get(s3Key);
|
|
364
|
+
if (!response) {
|
|
365
|
+
this.debug(`S3 CACHE MISS: ${cacheKey}`);
|
|
101
366
|
return null;
|
|
102
367
|
}
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
await this.deleteS3Entry(s3Key);
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
console.log(`Invalid content type: ${contentType}`);
|
|
130
|
-
// This shouldn't happen since we always store as JSON now
|
|
131
|
-
return null;
|
|
368
|
+
// Every object is JSON (`putJson`, and the seeded `.json` files); one
|
|
369
|
+
// that does not parse is a miss, through the `catch` below.
|
|
370
|
+
const parsedValue = (0, cache_utils_1.parseCacheValue)(response);
|
|
371
|
+
if (await this.isRevalidated(parsedValue, ctx, cacheKey)) {
|
|
372
|
+
this.debug(`S3 CACHE INVALIDATED BY TAG: ${cacheKey}`);
|
|
373
|
+
// A revalidated `fetch` entry has to read as a miss so the request
|
|
374
|
+
// refetches instead of reusing the body - the same thing Next.js's
|
|
375
|
+
// own `FileSystemCache` does with `revalidatedTags`. Only a
|
|
376
|
+
// *response* entry gets handed back expired, because for those a
|
|
377
|
+
// miss is worse than stale: see `EXPIRED_LAST_MODIFIED`.
|
|
378
|
+
//
|
|
379
|
+
// Neither is deleted. The refetch or re-render this provokes
|
|
380
|
+
// overwrites the object through `set`, with a `lastModified` past the
|
|
381
|
+
// tag's marker; deleting it here as well raced that write, and a
|
|
382
|
+
// concurrent request's fresh entry could be the one removed. Keeping
|
|
383
|
+
// a response entry also means a render that fails is answered from
|
|
384
|
+
// the last good copy rather than from a shell.
|
|
385
|
+
return isFetchCacheGet(ctx)
|
|
386
|
+
? null
|
|
387
|
+
: { lastModified: EXPIRED_LAST_MODIFIED, value: parsedValue.value };
|
|
132
388
|
}
|
|
133
389
|
this.debug(`S3 CACHE HIT: ${cacheKey} (${s3Key})`);
|
|
134
|
-
|
|
390
|
+
// Without the stored tags.
|
|
391
|
+
return {
|
|
392
|
+
lastModified: parsedValue.lastModified,
|
|
393
|
+
value: parsedValue.value,
|
|
394
|
+
};
|
|
135
395
|
}
|
|
136
396
|
catch (error) {
|
|
137
|
-
|
|
138
|
-
this.debug(`S3 CACHE MISS: ${cacheKey}`);
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
// Log actual errors (not cache misses)
|
|
397
|
+
// Log actual errors (a cache miss is not one: see `CacheBucket.get`)
|
|
142
398
|
console.error(`Error retrieving cache from S3:`, error);
|
|
143
399
|
return null;
|
|
144
400
|
}
|
|
145
401
|
}
|
|
402
|
+
/**
|
|
403
|
+
* Whether a tag revalidation has expired `entry`, for a `get` with `ctx`.
|
|
404
|
+
*
|
|
405
|
+
* Public because the in-memory layer in front of this handler has to ask too:
|
|
406
|
+
* a memory hit never reaches {@link get}, and without this check an instance
|
|
407
|
+
* other than the one that ran `revalidateTag` kept answering from memory for
|
|
408
|
+
* the whole memory TTL - long enough for CloudFront to cache the stale page
|
|
409
|
+
* again after the invalidation.
|
|
410
|
+
*
|
|
411
|
+
* Which tags to check depends on the kind, and Next.js's own
|
|
412
|
+
* `FileSystemCache.get` splits the same two ways: a response entry is checked
|
|
413
|
+
* against the tags *it* was stored with, but a `fetch` entry is checked
|
|
414
|
+
* against `[...ctx.tags, ...ctx.softTags]` — the tags of the request asking
|
|
415
|
+
* for it. That distinction is load-bearing, because a `fetch` entry is only
|
|
416
|
+
* ever stored with its *explicit* `cache: { tags }` and never with the
|
|
417
|
+
* implicit `_N_T_/<path>` chain, which arrives only as `ctx.softTags`.
|
|
418
|
+
* Reading the stored tags for a `fetch` too meant an untagged `force-cache`
|
|
419
|
+
* fetch had `[]`, skipped the check entirely, and no `revalidatePath` could
|
|
420
|
+
* ever evict it: a `force-dynamic` page whose data comes from such a fetch
|
|
421
|
+
* re-rendered on every request and still served the same body forever.
|
|
422
|
+
* Measured against next.js's `test/e2e/app-dir/revalidate-path-with-rewrites`.
|
|
423
|
+
*
|
|
424
|
+
* `cacheKey` is the key the entry was read under. A response entry found
|
|
425
|
+
* stale is remembered by it, for `set` to invalidate CloudFront again once
|
|
426
|
+
* its regeneration lands: see {@link softRevalidatedKeys}.
|
|
427
|
+
*/
|
|
428
|
+
async isRevalidated(entry, ctx, cacheKey) {
|
|
429
|
+
if (!this.config.tableName) {
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
const checkTags = isFetchCacheGet(ctx)
|
|
433
|
+
? [...(ctx.tags ?? []), ...(ctx.softTags ?? [])]
|
|
434
|
+
: entryTags(entry);
|
|
435
|
+
if (checkTags.length === 0) {
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
const state = await this.checkIfRevalidated(entry.lastModified ?? 0, checkTags);
|
|
439
|
+
if (state === "stale" && !isFetchCacheGet(ctx)) {
|
|
440
|
+
this.noteSoftRevalidated(cacheKey);
|
|
441
|
+
}
|
|
442
|
+
return state === "expired";
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Remember that `cacheKey` was served stale, when there is a distribution
|
|
446
|
+
* whose copy of it will need invalidating again. See
|
|
447
|
+
* {@link softRevalidatedKeys}.
|
|
448
|
+
*/
|
|
449
|
+
noteSoftRevalidated(cacheKey) {
|
|
450
|
+
if (!this.invalidatesCdn) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
this.softRevalidatedKeys.delete(cacheKey);
|
|
454
|
+
if (this.softRevalidatedKeys.size >= MAX_SOFT_REVALIDATED_KEYS) {
|
|
455
|
+
// Insertion order: the first key is the one waiting longest.
|
|
456
|
+
const oldest = this.softRevalidatedKeys.values().next().value;
|
|
457
|
+
if (oldest !== undefined) {
|
|
458
|
+
this.softRevalidatedKeys.delete(oldest);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
this.softRevalidatedKeys.add(cacheKey);
|
|
462
|
+
}
|
|
146
463
|
async set(cacheKey, data, ctx) {
|
|
147
464
|
try {
|
|
148
465
|
if (!data) {
|
|
149
466
|
// Delete from S3 and DynamoDB
|
|
150
467
|
this.debug(`S3 CACHE DELETE: ${cacheKey}`);
|
|
151
|
-
if (!this.
|
|
468
|
+
if (!this.config.bucketName) {
|
|
152
469
|
return;
|
|
153
470
|
}
|
|
471
|
+
// The entry is gone, so there is no regeneration left to wait for.
|
|
472
|
+
this.softRevalidatedKeys.delete(cacheKey);
|
|
154
473
|
// Build S3 key without needing to know the kind
|
|
155
474
|
const s3Key = this.buildS3Key(cacheKey);
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
this.debug(`Failed to delete S3 key ${s3Key}:`, error);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
// TODO: Also delete tag associations from DynamoDB if needed
|
|
475
|
+
// Read the entry's tags before the object is gone: they are the only
|
|
476
|
+
// way to name its mapping rows, whose sort key is `tag#s3Key` and so
|
|
477
|
+
// cannot be queried from the key side. See `storedEntryTags`.
|
|
478
|
+
const tags = await this.storedEntryTags(s3Key, ctx);
|
|
479
|
+
await this.s3Client.send(new client_s3_1.DeleteObjectCommand({
|
|
480
|
+
Bucket: this.config.bucketName,
|
|
481
|
+
Key: s3Key,
|
|
482
|
+
}));
|
|
483
|
+
this.debug(`S3 CACHE DELETED: ${s3Key}`);
|
|
484
|
+
await this.deleteDynamoDBTagMappings(s3Key, tags);
|
|
170
485
|
return;
|
|
171
486
|
}
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
487
|
+
// `entryTags`, not `getTags(ctx)` alone: for a page or route response
|
|
488
|
+
// `ctx` carries only `{ cacheControl, isRoutePPREnabled, isFallback }` —
|
|
489
|
+
// `ResponseCache.set` builds it that way and `IncrementalCache.set`
|
|
490
|
+
// forwards it unchanged — so the tags live in the render's
|
|
491
|
+
// `x-next-cache-tags` header and nowhere else. Reading only `ctx` meant no
|
|
492
|
+
// runtime-rendered page ever got a mapping row, and a page that is not in
|
|
493
|
+
// the build's tag manifest names none either: a later
|
|
494
|
+
// `revalidateTag` found nothing to invalidate and CloudFront kept serving
|
|
495
|
+
// the stale HTML and RSC payload for the whole `s-maxage`. Same source the
|
|
496
|
+
// read path above already falls back to.
|
|
497
|
+
const tags = entryTags({ tags: (0, cache_utils_1.getTags)(ctx), value: data });
|
|
498
|
+
this.debug(`S3 CACHE SET: Key: ${cacheKey}, tags: ${tags.length ? tags : "none"}`);
|
|
499
|
+
if (!this.config.bucketName) {
|
|
181
500
|
return;
|
|
182
501
|
}
|
|
183
502
|
const s3Key = this.buildS3Key(cacheKey);
|
|
184
503
|
// Create CacheHandlerValue structure for S3 storage with tags
|
|
185
504
|
const cacheHandlerValue = {
|
|
186
|
-
|
|
505
|
+
// On the marker clock, since that is what it is compared with.
|
|
506
|
+
lastModified: (0, aws_cache_store_1.markerClock)(),
|
|
187
507
|
value: data,
|
|
188
508
|
};
|
|
189
509
|
// Store tags with the cache entry for revalidation checking
|
|
190
510
|
const cacheEntryWithTags = {
|
|
191
511
|
...cacheHandlerValue,
|
|
192
|
-
tags
|
|
512
|
+
tags,
|
|
193
513
|
};
|
|
194
514
|
// Serialize with custom handling for Map and Buffer objects
|
|
195
515
|
const body = (0, cache_utils_1.serializeCacheValue)(cacheEntryWithTags);
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
Key: s3Key,
|
|
199
|
-
Body: body,
|
|
200
|
-
ContentType: "application/json; charset=utf-8", // Always JSON since we store CacheHandlerValue
|
|
201
|
-
});
|
|
202
|
-
await this.s3Client.send(command);
|
|
516
|
+
// Always JSON since we store CacheHandlerValue
|
|
517
|
+
await this.bucket.putJson(s3Key, body);
|
|
203
518
|
this.debug(`S3 CACHE STORED: ${cacheKey} (${data.kind})`);
|
|
519
|
+
// The regeneration of an entry served stale: CloudFront may have cached
|
|
520
|
+
// that stale response after `revalidateTag`'s invalidation, so the key's
|
|
521
|
+
// paths go once more now that S3 holds the fresh one.
|
|
522
|
+
if (this.softRevalidatedKeys.delete(cacheKey)) {
|
|
523
|
+
const route = this.s3KeyToInvalidationPath(s3Key);
|
|
524
|
+
if (route !== undefined) {
|
|
525
|
+
this.debug(`SOFT REVALIDATION REGENERATED: ${cacheKey}`);
|
|
526
|
+
await this.invalidateCloudFrontPaths(cdnInvalidationPaths(route, this.cloudFrontConfig.basePath));
|
|
527
|
+
}
|
|
528
|
+
}
|
|
204
529
|
// Store tag-to-cache-key mappings in DynamoDB for revalidation
|
|
205
|
-
if (tags
|
|
530
|
+
if (tags.length > 0 && this.mapsTagsToPaths) {
|
|
206
531
|
this.debug(`STORING TAGS: ${cacheKey} -> [${tags.join(", ")}]`);
|
|
207
532
|
await this.storeDynamoDBTagMappings(s3Key, tags);
|
|
208
533
|
}
|
|
@@ -211,140 +536,253 @@ class S3CacheHandler {
|
|
|
211
536
|
console.error("Error storing cache to S3:", error);
|
|
212
537
|
}
|
|
213
538
|
}
|
|
214
|
-
async revalidateTag(tag) {
|
|
539
|
+
async revalidateTag(tag, durations) {
|
|
215
540
|
const tags = Array.isArray(tag) ? tag : [tag];
|
|
216
541
|
this.debug(`REVALIDATING TAGS: [${tags.join(", ")}]`);
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
542
|
+
if (!this.config.tableName) {
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
// Record the revalidation against each tag itself, not only against the
|
|
546
|
+
// cache keys already mapped to it: `checkIfRevalidated` reads these marker
|
|
547
|
+
// rows, and a build-time prerender has no mapping rows at all.
|
|
548
|
+
const recorded = await this.tags.update(tags, durations);
|
|
549
|
+
if (!this.mapsTagsToPaths) {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
const { basePath } = this.cloudFrontConfig;
|
|
553
|
+
if (!recorded) {
|
|
554
|
+
// Other instances may never learn of this revalidation, so no path list
|
|
555
|
+
// is enough: drop the whole app from CloudFront rather than serve stale
|
|
556
|
+
// pages until their TTL.
|
|
557
|
+
await this.invalidateCloudFrontPaths((0, base_path_1.wholeAppInvalidationPaths)(basePath));
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
// Settled rather than `all`: one tag's throttled Query must not drop the
|
|
561
|
+
// CloudFront paths every other tag resolved.
|
|
562
|
+
const results = await Promise.allSettled(tags.map((t) => this.tagRoutes(t)));
|
|
563
|
+
const routes = [];
|
|
564
|
+
let wholeApp = false;
|
|
565
|
+
for (const result of results) {
|
|
566
|
+
if (result.status === "rejected") {
|
|
567
|
+
console.error("Error reading tag mappings:", result.reason);
|
|
568
|
+
// The tag's entries are revalidated at the origin while nothing names
|
|
569
|
+
// their CloudFront paths. The same answer as a tag cut short by
|
|
570
|
+
// `MAX_TAG_QUERY_PAGES`: the whole app, never a stale page.
|
|
571
|
+
wholeApp = true;
|
|
572
|
+
continue;
|
|
220
573
|
}
|
|
221
|
-
|
|
222
|
-
|
|
574
|
+
routes.push(...result.value.routes);
|
|
575
|
+
wholeApp ||= result.value.truncated;
|
|
223
576
|
}
|
|
224
|
-
|
|
225
|
-
|
|
577
|
+
// Invalidate the CDN edge cache so CloudFront-fronted deployments don't
|
|
578
|
+
// keep serving stale responses until the cache policy's TTL naturally
|
|
579
|
+
// expires. One request for every tag at once: see
|
|
580
|
+
// `MAX_WILDCARD_PATHS_PER_INVALIDATION` for why never several.
|
|
581
|
+
await this.invalidateCloudFrontPaths(wholeApp
|
|
582
|
+
? (0, base_path_1.wholeAppInvalidationPaths)(basePath)
|
|
583
|
+
: routes.flatMap((route) => cdnInvalidationPaths(route, basePath)));
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* The routes CloudFront could be holding a response for `tag` under -
|
|
587
|
+
* `truncated` when they could not all be named: more mapping rows than
|
|
588
|
+
* {@link MAX_TAG_QUERY_PAGES}, or no build tag manifest to read.
|
|
589
|
+
*/
|
|
590
|
+
async tagRoutes(tag) {
|
|
591
|
+
const [{ items, truncated }, buildTags] = await Promise.all([
|
|
592
|
+
this.queryTagMappings(tag),
|
|
593
|
+
this.buildTagManifest(),
|
|
594
|
+
]);
|
|
595
|
+
// Extract S3 keys from sort keys (format: "tag#s3Key"). Split at the tag's
|
|
596
|
+
// own length rather than at the first "#": a tag is app-defined and may
|
|
597
|
+
// contain one, and `revalidateTag("user#42")` then yielded
|
|
598
|
+
// "42#<buildId>/account.json" — whose invalidation path
|
|
599
|
+
// ("/42#<buildId>/account") names nothing CloudFront cached, so the edge
|
|
600
|
+
// kept serving the stale page.
|
|
601
|
+
//
|
|
602
|
+
// The same `#` makes `begins_with(sk, "user#")` match rows that are not
|
|
603
|
+
// `user`'s at all: the marker row of tag `user#42` (sk `user#42`) and every
|
|
604
|
+
// mapping row of it (`user#42#<buildId>/…`). What is left after the prefix
|
|
605
|
+
// is an S3 key only if it starts with the build's own prefix, which is how
|
|
606
|
+
// `buildS3Key` writes every one.
|
|
607
|
+
const prefixLength = tag.length + 1;
|
|
608
|
+
const keyPrefix = this.config.buildId ? `${this.config.buildId}/` : "";
|
|
609
|
+
const s3Keys = items
|
|
610
|
+
.map((item) => item.sk?.S?.slice(prefixLength))
|
|
611
|
+
.filter((s3Key) => Boolean(s3Key && s3Key.startsWith(keyPrefix)));
|
|
612
|
+
// The build-time prerenders carrying the tag, which no `set` wrote a row
|
|
613
|
+
// for.
|
|
614
|
+
for (const cacheKey of buildTags?.get(tag) ?? []) {
|
|
615
|
+
s3Keys.push(this.buildS3Key(cacheKey));
|
|
226
616
|
}
|
|
617
|
+
this.debug(`TAG ${tag}: Found ${s3Keys.length} cache entries to invalidate`);
|
|
618
|
+
// The mapping rows themselves are deliberately left alone. Stamping
|
|
619
|
+
// `revalidatedAt` on each `tag#cacheKey` row is what this used to do, and
|
|
620
|
+
// nothing has read that attribute since `checkIfRevalidated` began reading
|
|
621
|
+
// the bare-tag marker row written above — so the writes were dead, and they
|
|
622
|
+
// were dead *in front of* the invalidation. One throttled `UpdateItem`
|
|
623
|
+
// rejected the batch, the CloudFront invalidation never ran, and the only
|
|
624
|
+
// trace was a `console.error` while the edge kept serving the stale page.
|
|
625
|
+
//
|
|
626
|
+
// Deliberately not deleting the tag's S3 objects either. The marker row
|
|
627
|
+
// above is what invalidates them: `get` compares it against each entry's
|
|
628
|
+
// own `lastModified` and hands the entry back expired, which is the signal
|
|
629
|
+
// that makes Next.js re-render *this* route and store the result (see
|
|
630
|
+
// `EXPIRED_LAST_MODIFIED`). Deleting the object instead turned the next
|
|
631
|
+
// request into a hard miss, and a hard miss on a PPR route is answered from
|
|
632
|
+
// the route's fallback shell — uncacheable, and never upgraded back into a
|
|
633
|
+
// concrete entry, so one `revalidateTag` left the page resuming
|
|
634
|
+
// dynamically for good.
|
|
635
|
+
const routes = s3Keys
|
|
636
|
+
.map((s3Key) => this.s3KeyToInvalidationPath(s3Key))
|
|
637
|
+
.filter((route) => route !== undefined);
|
|
638
|
+
// A `revalidatePath` names its path in the tag itself, which is the only
|
|
639
|
+
// way to reach a build-time prerender's CDN copy when it has no mapping
|
|
640
|
+
// row.
|
|
641
|
+
routes.push(...implicitTagPaths(tag));
|
|
642
|
+
return { routes, truncated: truncated || !buildTags };
|
|
227
643
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
644
|
+
/**
|
|
645
|
+
* The build-time prerenders' cache keys by tag ({@link INIT_CACHE_TAG_MANIFEST}),
|
|
646
|
+
* read from the cache bucket once per instance, or `undefined` when that
|
|
647
|
+
* failed, to be read again next time. A prerender is written by the adapter,
|
|
648
|
+
* never through `set`, so this is the only record of which tags name its
|
|
649
|
+
* CloudFront paths, and it is served with a year-long `s-maxage`.
|
|
650
|
+
*/
|
|
651
|
+
buildTagManifest() {
|
|
652
|
+
this.buildTags ??= this.bucket
|
|
653
|
+
.get(`${this.config.buildId}/${cache_utils_1.INIT_CACHE_TAG_MANIFEST}`)
|
|
654
|
+
// None is normal: only tagged prerenders write one.
|
|
655
|
+
.then((object) => new Map(Object.entries(JSON.parse(object ?? "{}"))));
|
|
656
|
+
return this.buildTags.catch((error) => {
|
|
657
|
+
this.buildTags = undefined;
|
|
658
|
+
console.warn("Could not read the build's tag manifest:", error);
|
|
659
|
+
return undefined;
|
|
237
660
|
});
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
if (this.s3Config.bucketName) {
|
|
272
|
-
const deletePromises = cacheKeys.map(async (s3Key) => {
|
|
273
|
-
if (s3Key) {
|
|
274
|
-
const deleteCommand = new client_s3_1.DeleteObjectCommand({
|
|
275
|
-
Bucket: this.s3Config.bucketName,
|
|
276
|
-
Key: s3Key,
|
|
277
|
-
});
|
|
278
|
-
try {
|
|
279
|
-
await this.s3Client.send(deleteCommand);
|
|
280
|
-
}
|
|
281
|
-
catch (error) {
|
|
282
|
-
// Log but don't fail - the entry might not exist in S3
|
|
283
|
-
console.warn(`Failed to delete S3 cache entry ${s3Key}:`, error);
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
});
|
|
287
|
-
await Promise.all(deletePromises.filter(Boolean));
|
|
288
|
-
}
|
|
289
|
-
// Invalidate the CDN edge cache so CloudFront-fronted deployments don't
|
|
290
|
-
// keep serving stale responses until the cache policy's TTL naturally expires.
|
|
291
|
-
if (this.cloudFrontConfig.distributionIdParameterName) {
|
|
292
|
-
const invalidationPaths = cacheKeys
|
|
293
|
-
.filter((s3Key) => Boolean(s3Key))
|
|
294
|
-
.map((s3Key) => this.s3KeyToInvalidationPath(s3Key));
|
|
295
|
-
await this.invalidateCloudFrontPaths(invalidationPaths);
|
|
296
|
-
}
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Every mapping row for `tag`, following `LastEvaluatedKey`, and whether
|
|
664
|
+
* {@link MAX_TAG_QUERY_PAGES} cut the walk short.
|
|
665
|
+
*
|
|
666
|
+
* DynamoDB caps a Query at 1 MB of items regardless of how many match, and
|
|
667
|
+
* there is one row per tagged entry a runtime `set` wrote, so a large site
|
|
668
|
+
* passes 1 MB on a common tag. Stopping at the
|
|
669
|
+
* first page invalidates only that page's entries while still reporting
|
|
670
|
+
* success, which is indistinguishable from revalidation having worked.
|
|
671
|
+
*/
|
|
672
|
+
async queryTagMappings(tag) {
|
|
673
|
+
const items = [];
|
|
674
|
+
let exclusiveStartKey;
|
|
675
|
+
let pages = 0;
|
|
676
|
+
do {
|
|
677
|
+
const response = await this.dynamoClient.send(new client_dynamodb_1.QueryCommand({
|
|
678
|
+
TableName: this.config.tableName,
|
|
679
|
+
KeyConditionExpression: "pk = :pk AND begins_with(sk, :skPrefix)",
|
|
680
|
+
ExpressionAttributeValues: {
|
|
681
|
+
":pk": { S: this.config.buildId },
|
|
682
|
+
":skPrefix": { S: `${tag}#` },
|
|
683
|
+
},
|
|
684
|
+
ExclusiveStartKey: exclusiveStartKey,
|
|
685
|
+
}));
|
|
686
|
+
items.push(...(response.Items ?? []));
|
|
687
|
+
exclusiveStartKey = response.LastEvaluatedKey;
|
|
688
|
+
pages++;
|
|
689
|
+
} while (exclusiveStartKey && pages < MAX_TAG_QUERY_PAGES);
|
|
690
|
+
const truncated = exclusiveStartKey !== undefined;
|
|
691
|
+
if (truncated) {
|
|
692
|
+
console.warn(`Stopped paginating tag "${tag}" after ${pages} pages ` +
|
|
693
|
+
`(${items.length} entries); invalidating the whole app instead.`);
|
|
297
694
|
}
|
|
695
|
+
return { items, truncated };
|
|
298
696
|
}
|
|
299
697
|
/**
|
|
300
|
-
* Reverses `buildS3Key` to recover the
|
|
301
|
-
*
|
|
302
|
-
*
|
|
698
|
+
* Reverses `buildS3Key` to recover the route the response was cached for, for
|
|
699
|
+
* {@link cdnInvalidationPaths} to turn into the URIs CloudFront holds it under, or
|
|
700
|
+
* `undefined` for a key that names no cached URI.
|
|
701
|
+
*
|
|
702
|
+
* Two kinds of mapping row do not: a dynamic route template (`blog/[slug].json`,
|
|
703
|
+
* which the adapter's seeded entries include), and a fetch-cache entry, whose
|
|
704
|
+
* key is an opaque hash rather than a route. Both would invalidate a URI that
|
|
705
|
+
* cannot exist, and each one costs one of the fifteen wildcard paths an
|
|
706
|
+
* invalidation may carry — so they are dropped here rather than crowding out
|
|
707
|
+
* the real ones.
|
|
303
708
|
*/
|
|
304
709
|
s3KeyToInvalidationPath(s3Key) {
|
|
305
|
-
const prefix = `${this.
|
|
710
|
+
const prefix = `${this.config.buildId}/`;
|
|
306
711
|
const withoutPrefix = s3Key.startsWith(prefix)
|
|
307
712
|
? s3Key.slice(prefix.length)
|
|
308
713
|
: s3Key;
|
|
309
714
|
const withoutSuffix = withoutPrefix.endsWith(".json")
|
|
310
715
|
? withoutPrefix.slice(0, -".json".length)
|
|
311
716
|
: withoutPrefix;
|
|
717
|
+
if (withoutSuffix.includes("[") || isFetchCacheKey(withoutSuffix)) {
|
|
718
|
+
return undefined;
|
|
719
|
+
}
|
|
312
720
|
return withoutSuffix === "index" ? "/" : `/${withoutSuffix}`;
|
|
313
721
|
}
|
|
722
|
+
/**
|
|
723
|
+
* Invalidate `paths` in a single request, or the whole app
|
|
724
|
+
* ({@link wholeAppInvalidationPaths}) when they would not fit in one. See
|
|
725
|
+
* {@link MAX_WILDCARD_PATHS_PER_INVALIDATION}.
|
|
726
|
+
*/
|
|
314
727
|
async invalidateCloudFrontPaths(paths) {
|
|
315
|
-
|
|
316
|
-
if (
|
|
728
|
+
let batch = Array.from(new Set(paths));
|
|
729
|
+
if (batch.length === 0) {
|
|
317
730
|
return;
|
|
318
731
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
732
|
+
const distributionId = await this.getDistributionId().catch((error) => {
|
|
733
|
+
console.warn("Could not resolve the distribution to invalidate:", error);
|
|
734
|
+
return null;
|
|
735
|
+
});
|
|
736
|
+
if (!distributionId) {
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
const wildcards = batch.filter(isWildcardPath).length;
|
|
740
|
+
if (wildcards > MAX_WILDCARD_PATHS_PER_INVALIDATION ||
|
|
741
|
+
batch.length > MAX_PATHS_PER_INVALIDATION) {
|
|
742
|
+
const wholeApp = (0, base_path_1.wholeAppInvalidationPaths)(this.cloudFrontConfig.basePath);
|
|
743
|
+
this.debug(`CLOUDFRONT INVALIDATION: ${batch.length} paths (${wildcards} ` +
|
|
744
|
+
`wildcards) do not fit one request, collapsing to ` +
|
|
745
|
+
`[${wholeApp.join(", ")}]`);
|
|
746
|
+
batch = wholeApp;
|
|
747
|
+
}
|
|
748
|
+
for (let attempt = 0;; attempt++) {
|
|
749
|
+
this.debug(`CLOUDFRONT INVALIDATION: [${batch.join(", ")}] on distribution ${distributionId}`);
|
|
750
|
+
try {
|
|
751
|
+
await this.cloudFrontClient.send(new client_cloudfront_1.CreateInvalidationCommand({
|
|
752
|
+
DistributionId: distributionId,
|
|
753
|
+
InvalidationBatch: {
|
|
754
|
+
CallerReference: (0, node_crypto_1.randomUUID)(),
|
|
755
|
+
Paths: {
|
|
756
|
+
Quantity: batch.length,
|
|
757
|
+
Items: batch,
|
|
758
|
+
},
|
|
759
|
+
},
|
|
760
|
+
}));
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
catch (error) {
|
|
764
|
+
if (isInvalidationQuotaError(error) && attempt < INVALIDATION_RETRIES) {
|
|
765
|
+
// See `INVALIDATION_RETRIES`.
|
|
766
|
+
batch = (0, base_path_1.wholeAppInvalidationPaths)(this.cloudFrontConfig.basePath);
|
|
767
|
+
await new Promise((resolve) => setTimeout(resolve, INVALIDATION_RETRY_DELAY_MS * 2 ** attempt));
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
// Log but don't fail - the S3/DynamoDB invalidation already succeeded,
|
|
771
|
+
// and the CloudFront cache policy TTL provides an eventual fallback.
|
|
772
|
+
console.warn(`Failed to create CloudFront invalidation for [${batch.join(", ")}]:`, error);
|
|
322
773
|
return;
|
|
323
774
|
}
|
|
324
|
-
this.debug(`CLOUDFRONT INVALIDATION: [${uniquePaths.join(", ")}] on distribution ${distributionId}`);
|
|
325
|
-
await this.cloudFrontClient.send(new client_cloudfront_1.CreateInvalidationCommand({
|
|
326
|
-
DistributionId: distributionId,
|
|
327
|
-
InvalidationBatch: {
|
|
328
|
-
CallerReference: (0, node_crypto_1.randomUUID)(),
|
|
329
|
-
Paths: {
|
|
330
|
-
Quantity: uniquePaths.length,
|
|
331
|
-
Items: uniquePaths,
|
|
332
|
-
},
|
|
333
|
-
},
|
|
334
|
-
}));
|
|
335
|
-
}
|
|
336
|
-
catch (error) {
|
|
337
|
-
// Log but don't fail - the S3/DynamoDB invalidation already succeeded,
|
|
338
|
-
// and the CloudFront cache policy TTL provides an eventual fallback.
|
|
339
|
-
console.warn("Failed to create CloudFront invalidation:", error);
|
|
340
775
|
}
|
|
341
776
|
}
|
|
342
777
|
/**
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
* for why
|
|
778
|
+
* The distribution's physical ID, from the environment or else SSM Parameter
|
|
779
|
+
* Store, cached for the lifetime of this instance. See
|
|
780
|
+
* `CloudFrontInvalidationConfig` for why Functions need the indirection.
|
|
346
781
|
*/
|
|
347
782
|
async getDistributionId() {
|
|
783
|
+
if (this.cloudFrontConfig.distributionId) {
|
|
784
|
+
return this.cloudFrontConfig.distributionId;
|
|
785
|
+
}
|
|
348
786
|
if (this.cachedDistributionId) {
|
|
349
787
|
return this.cachedDistributionId;
|
|
350
788
|
}
|
|
@@ -361,31 +799,44 @@ class S3CacheHandler {
|
|
|
361
799
|
// This handler doesn't maintain request-level cache state
|
|
362
800
|
// The actual cache clearing is handled by the memory cache handler
|
|
363
801
|
}
|
|
802
|
+
/**
|
|
803
|
+
* Whether this deployment keeps `tag#s3Key` mapping rows at all.
|
|
804
|
+
*
|
|
805
|
+
* Their one reader is `revalidateTag`'s CloudFront invalidation, which needs
|
|
806
|
+
* the entries' paths - tag revalidation itself runs on the bare-tag marker
|
|
807
|
+
* rows. A deployment with no distribution (the Regional constructs) would pay
|
|
808
|
+
* a DynamoDB write per tag on every `set`, a Query per `revalidateTag`, and an
|
|
809
|
+
* extra S3 read before every delete, for rows nothing ever reads.
|
|
810
|
+
*/
|
|
811
|
+
get mapsTagsToPaths() {
|
|
812
|
+
return Boolean(this.config.tableName) && this.invalidatesCdn;
|
|
813
|
+
}
|
|
814
|
+
/** Whether there is a distribution to invalidate. */
|
|
815
|
+
get invalidatesCdn() {
|
|
816
|
+
return Boolean(this.cloudFrontConfig.distributionId ||
|
|
817
|
+
this.cloudFrontConfig.distributionIdParameterName);
|
|
818
|
+
}
|
|
819
|
+
/** `{buildId}/{cacheKey}.json`; see {@link buildS3Key}. */
|
|
364
820
|
buildS3Key(cacheKey) {
|
|
365
|
-
|
|
366
|
-
// Handle edge cases:
|
|
367
|
-
// - Root path "/" should become "index" or similar
|
|
368
|
-
// - Remove leading slashes to prevent empty folders
|
|
369
|
-
let cleanCacheKey = cacheKey;
|
|
370
|
-
if (cacheKey === "/" || cacheKey === "") {
|
|
371
|
-
cleanCacheKey = "index";
|
|
372
|
-
}
|
|
373
|
-
else if (cacheKey.startsWith("/")) {
|
|
374
|
-
cleanCacheKey = cacheKey.slice(1);
|
|
375
|
-
}
|
|
376
|
-
return (0, node_path_1.join)(this.s3Config.buildId, `${cleanCacheKey}.json`);
|
|
821
|
+
return (0, aws_cache_store_1.buildS3Key)(this.config.buildId, cacheKey);
|
|
377
822
|
}
|
|
378
823
|
async storeDynamoDBTagMappings(s3Key, tags) {
|
|
379
824
|
try {
|
|
380
825
|
const updatePromises = tags.map(async (tag) => {
|
|
381
826
|
const tagCacheKey = `${tag}#${s3Key}`;
|
|
382
827
|
const updateCommand = new client_dynamodb_1.UpdateItemCommand({
|
|
383
|
-
TableName: this.
|
|
828
|
+
TableName: this.config.tableName,
|
|
384
829
|
Key: {
|
|
385
|
-
pk: { S: this.
|
|
830
|
+
pk: { S: this.config.buildId },
|
|
386
831
|
sk: { S: tagCacheKey },
|
|
387
832
|
},
|
|
388
|
-
|
|
833
|
+
// Deliberately no `revalidatedAt`: a mapping row records only that an
|
|
834
|
+
// entry carries the tag. Stamping it at write time made every tagged
|
|
835
|
+
// entry look revalidated one millisecond after it was stored — the
|
|
836
|
+
// row's timestamp is taken after the entry's `lastModified` — so
|
|
837
|
+
// `checkIfRevalidated` deleted healthy entries and the page
|
|
838
|
+
// re-rendered on every request.
|
|
839
|
+
UpdateExpression: "SET createdAt = if_not_exists(createdAt, :now)",
|
|
389
840
|
ExpressionAttributeValues: {
|
|
390
841
|
":now": { N: Date.now().toString() },
|
|
391
842
|
},
|
|
@@ -399,54 +850,133 @@ class S3CacheHandler {
|
|
|
399
850
|
// Don't throw - cache storage should continue even if tag mapping fails
|
|
400
851
|
}
|
|
401
852
|
}
|
|
402
|
-
|
|
853
|
+
/**
|
|
854
|
+
* Drop the `tag#s3Key` mapping rows for an entry that no longer exists.
|
|
855
|
+
*
|
|
856
|
+
* The counterpart of {@link storeDynamoDBTagMappings}, and the reason its
|
|
857
|
+
* caller has to know the entry's tags: a mapping row's sort key starts with
|
|
858
|
+
* the tag, so the rows belonging to one cache key cannot be queried for - only
|
|
859
|
+
* a full scan of the build's partition would find them.
|
|
860
|
+
*
|
|
861
|
+
* Left behind, a row still resolves to a cache key on the next
|
|
862
|
+
* `revalidateTag`, which spends one of CloudFront's fifteen wildcard paths
|
|
863
|
+
* invalidating a URI whose entry was deleted - crowding out the paths that do
|
|
864
|
+
* need it, and past {@link MAX_WILDCARD_PATHS_PER_INVALIDATION} collapsing the
|
|
865
|
+
* whole app into one wildcard. The rows also count against the 1 MB a tag's Query returns, so
|
|
866
|
+
* enough of them push live entries onto a page
|
|
867
|
+
* {@link MAX_TAG_QUERY_PAGES} may not reach.
|
|
868
|
+
*
|
|
869
|
+
* Only the mapping rows: the bare-`tag` marker row `revalidateTag`
|
|
870
|
+
* writes belongs to the tag rather than to any entry, and `checkIfRevalidated`
|
|
871
|
+
* reads it for every *other* entry carrying that tag.
|
|
872
|
+
*
|
|
873
|
+
* Deliberately no CloudFront invalidation for the deleted path. The edge copy
|
|
874
|
+
* does outlive the object, but this runs on every revalidated `fetch` entry as
|
|
875
|
+
* well, where the key names no URI, and an invalidation request per deleted
|
|
876
|
+
* entry is a cost the TTL already bounds.
|
|
877
|
+
*/
|
|
878
|
+
async deleteDynamoDBTagMappings(s3Key, tags) {
|
|
879
|
+
if (!this.mapsTagsToPaths || tags.length === 0) {
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
403
882
|
try {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
883
|
+
await Promise.all(
|
|
884
|
+
// A page's tags can repeat - `ctx.tags` and the render's header are both
|
|
885
|
+
// read - and one delete per tag is enough.
|
|
886
|
+
Array.from(new Set(tags)).map(async (tag) => {
|
|
887
|
+
await this.dynamoClient.send(new client_dynamodb_1.DeleteItemCommand({
|
|
888
|
+
TableName: this.config.tableName,
|
|
889
|
+
Key: {
|
|
890
|
+
pk: { S: this.config.buildId },
|
|
891
|
+
sk: { S: `${tag}#${s3Key}` },
|
|
412
892
|
},
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
const queryResponse = await this.dynamoClient.send(queryCommand);
|
|
417
|
-
if (queryResponse.Items && queryResponse.Items.length > 0) {
|
|
418
|
-
const revalidatedAt = queryResponse.Items[0].revalidatedAt?.N;
|
|
419
|
-
if (revalidatedAt && parseInt(revalidatedAt) > cacheLastModified) {
|
|
420
|
-
this.debug(`Tag ${tag} was revalidated at ${revalidatedAt}, cache created at ${cacheLastModified}`);
|
|
421
|
-
return true; // Cache is invalidated
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
return false; // Cache is still valid
|
|
893
|
+
}));
|
|
894
|
+
}));
|
|
895
|
+
this.debug(`DELETED TAG MAPPINGS: ${s3Key} -> [${tags.join(", ")}]`);
|
|
426
896
|
}
|
|
427
897
|
catch (error) {
|
|
428
|
-
|
|
429
|
-
//
|
|
430
|
-
|
|
898
|
+
// Log but don't fail: the entry itself is already gone, and a stale
|
|
899
|
+
// mapping row costs an invalidation path rather than a wrong response.
|
|
900
|
+
console.error("Error deleting DynamoDB tag mappings:", error);
|
|
431
901
|
}
|
|
432
902
|
}
|
|
433
|
-
|
|
903
|
+
/**
|
|
904
|
+
* The tags an entry about to be deleted was stored with.
|
|
905
|
+
*
|
|
906
|
+
* `ctx` carries them for a `fetch` delete, but a response delete arrives as
|
|
907
|
+
* `{ cacheControl, isRoutePPREnabled, isFallback }` - `ResponseCache.set`
|
|
908
|
+
* builds it that way - so the entry itself is the only source, and it has to
|
|
909
|
+
* be read before it is removed. That is one extra GET on a path that runs when
|
|
910
|
+
* a cached route starts answering `notFound()`: `IncrementalCache.set`
|
|
911
|
+
* forwards `ResponseCache`'s null value through to here.
|
|
912
|
+
*
|
|
913
|
+
* A page is also the case worth paying it for, since it carries the whole
|
|
914
|
+
* implicit `_N_T_/…` chain and therefore the most rows.
|
|
915
|
+
*/
|
|
916
|
+
async storedEntryTags(s3Key, ctx) {
|
|
917
|
+
const ctxTags = (0, cache_utils_1.getTags)(ctx);
|
|
918
|
+
if (ctxTags?.length) {
|
|
919
|
+
return ctxTags;
|
|
920
|
+
}
|
|
921
|
+
if (!this.mapsTagsToPaths) {
|
|
922
|
+
return [];
|
|
923
|
+
}
|
|
434
924
|
try {
|
|
435
|
-
|
|
436
|
-
|
|
925
|
+
const response = await this.bucket.get(s3Key);
|
|
926
|
+
if (!response) {
|
|
927
|
+
return [];
|
|
437
928
|
}
|
|
438
|
-
|
|
439
|
-
Bucket: this.s3Config.bucketName,
|
|
440
|
-
Key: s3Key,
|
|
441
|
-
});
|
|
442
|
-
await this.s3Client.send(deleteCommand);
|
|
443
|
-
this.debug(`Deleted stale S3 cache entry: ${s3Key}`);
|
|
929
|
+
return entryTags((0, cache_utils_1.parseCacheValue)(response));
|
|
444
930
|
}
|
|
445
931
|
catch (error) {
|
|
446
|
-
|
|
447
|
-
|
|
932
|
+
console.warn(`Failed to read tags of ${s3Key} before deleting:`, error);
|
|
933
|
+
return [];
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Whether any of `tags` was revalidated after this entry was stored, and how.
|
|
938
|
+
*
|
|
939
|
+
* Reads the per-tag marker rows `revalidateTag` writes, as this instance
|
|
940
|
+
* tracks them: see `TrackedTagMarkers` for when they are read.
|
|
941
|
+
* Scanning the tag's mapping rows instead would answer the wrong question:
|
|
942
|
+
* those rows exist per cache *key*, so which one a `Limit: 1` query returned
|
|
943
|
+
* depended on sort order, and an entry could be judged against another
|
|
944
|
+
* entry's timestamp.
|
|
945
|
+
*
|
|
946
|
+
* A tag revalidated with a profile only makes the entry *stale*: that is
|
|
947
|
+
* reported to Next.js through its tag manifest (see {@link nextTagsManifest})
|
|
948
|
+
* and answered `"stale"` here, so the entry is served while a background
|
|
949
|
+
* render replaces it. When the manifest cannot be reached a stale entry is
|
|
950
|
+
* reported expired instead - a blocking render, never a stale page presented
|
|
951
|
+
* as fresh.
|
|
952
|
+
*/
|
|
953
|
+
async checkIfRevalidated(cacheLastModified, tags) {
|
|
954
|
+
await this.tags.refresh();
|
|
955
|
+
await this.tags.ensure(tags);
|
|
956
|
+
const state = this.tags.state(tags, cacheLastModified);
|
|
957
|
+
if (state !== "stale") {
|
|
958
|
+
if (state === "expired") {
|
|
959
|
+
this.debug(`Tags [${tags}] expired entry created at ${cacheLastModified}`);
|
|
960
|
+
}
|
|
961
|
+
return state;
|
|
962
|
+
}
|
|
963
|
+
const manifest = nextTagsManifest();
|
|
964
|
+
if (!manifest) {
|
|
965
|
+
this.debug(`Tags [${tags}] are stale and Next.js's tag manifest is ` +
|
|
966
|
+
`unreachable, expiring the entry instead`);
|
|
967
|
+
return "expired";
|
|
968
|
+
}
|
|
969
|
+
for (const tag of tags) {
|
|
970
|
+
const staleAt = this.tags.get(tag)?.staleAt;
|
|
971
|
+
const existing = manifest.get(tag);
|
|
972
|
+
if (staleAt !== undefined &&
|
|
973
|
+
staleAt > cacheLastModified &&
|
|
974
|
+
(existing?.stale ?? 0) < staleAt) {
|
|
975
|
+
manifest.set(tag, { ...existing, stale: staleAt });
|
|
976
|
+
}
|
|
448
977
|
}
|
|
978
|
+
return "stale";
|
|
449
979
|
}
|
|
450
980
|
}
|
|
451
981
|
exports.S3CacheHandler = S3CacheHandler;
|
|
452
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiczMtY2FjaGUtaGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hZGFwdGVyL3MzLWNhY2hlLWhhbmRsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUE7O0VBRUU7QUFDRixzREFBc0Q7QUFDdEQsNkNBQXlDO0FBQ3pDLHlDQUFpQztBQUNqQyxrRUFHb0M7QUFDcEMsOERBSWtDO0FBQ2xDLGtEQU00QjtBQUM1QixvREFBcUU7QUFDckUsa0RBQTZCO0FBYTdCLCtDQUE4RTtBQXFDOUUsTUFBYSxjQUFjO0lBQ2pCLFFBQVEsQ0FBVztJQUNuQixZQUFZLENBQWlCO0lBQzdCLGdCQUFnQixDQUFtQjtJQUNuQyxTQUFTLENBQVk7SUFDckIsUUFBUSxDQUFnQjtJQUN4QixZQUFZLENBQTZCO0lBQ3pDLGdCQUFnQixDQUErQjtJQUMvQyxLQUFLLEdBQUcsSUFBQSxlQUFRLEVBQUMsNkJBQTZCLENBQUMsQ0FBQztJQUV4RCw0RUFBNEU7SUFDNUUsMkVBQTJFO0lBQ25FLG9CQUFvQixHQUFrQixJQUFJLENBQUM7SUFFbkQsWUFBWSxPQUE4QjtRQUN4QyxNQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLG1CQUFtQixJQUFJLEVBQUUsQ0FBQztRQUV0RCxxRUFBcUU7UUFDckUsSUFBSSxDQUFDLFFBQVEsR0FBRztZQUNkLFVBQVUsRUFDUixPQUFPLENBQUMsUUFBUSxFQUFFLFVBQVU7Z0JBQzVCLE9BQU8sQ0FBQyxHQUFHLENBQUMsNEJBQTRCO2dCQUN4QyxFQUFFO1lBQ0osTUFBTSxFQUFFLE9BQU8sQ0FBQyxRQUFRLEVBQUUsTUFBTSxJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxJQUFJLFdBQVc7WUFDekUsT0FBTyxFQUFFLE9BQU8sQ0FBQyxRQUFRLEVBQUUsT0FBTyxJQUFJLE9BQU87U0FDOUMsQ0FBQztRQUVGLDJFQUEyRTtRQUMzRSxJQUFJLENBQUMsWUFBWSxHQUFHO1lBQ2xCLFNB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LENBQUM7WUFDeEMsSUFBSSxDQUFDLEtBQUssQ0FBQyxpQ0FBaUMsS0FBSyxFQUFFLENBQUMsQ0FBQztRQUN2RCxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLHVEQUF1RDtZQUN2RCxPQUFPLENBQUMsSUFBSSxDQUFDLHlDQUF5QyxLQUFLLEdBQUcsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUN6RSxDQUFDO0lBQ0gsQ0FBQztDQUNGO0FBM2hCRCx3Q0EyaEJDIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAgUzMgYW5kIER5bmFtb0RCIGNhY2hlIGhhbmRsZXIgZm9yIE5leHQuanMgaW5jcmVtZW50YWwgY2FjaGVcbiovXG4vKiBlc2xpbnQtZGlzYWJsZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXMgKi9cbmltcG9ydCB7IHJhbmRvbVVVSUQgfSBmcm9tIFwibm9kZTpjcnlwdG9cIjtcbmltcG9ydCB7IGpvaW4gfSBmcm9tIFwibm9kZTpwYXRoXCI7XG5pbXBvcnQge1xuICBDbG91ZEZyb250Q2xpZW50LFxuICBDcmVhdGVJbnZhbGlkYXRpb25Db21tYW5kLFxufSBmcm9tIFwiQGF3cy1zZGsvY2xpZW50LWNsb3VkZnJvbnRcIjtcbmltcG9ydCB7XG4gIER5bmFtb0RCQ2xpZW50LFxuICBRdWVyeUNvbW1hbmQsXG4gIFVwZGF0ZUl0ZW1Db21tYW5kLFxufSBmcm9tIFwiQGF3cy1zZGsvY2xpZW50LWR5bmFtb2RiXCI7XG5pbXBvcnQge1xuICBTM0NsaWVudCxcbiAgR2V0T2JqZWN0Q29tbWFuZCxcbiAgUHV0T2JqZWN0Q29tbWFuZCxcbiAgRGVsZXRlT2JqZWN0Q29tbWFuZCxcbiAgTm9TdWNoS2V5LFxufSBmcm9tIFwiQGF3cy1zZGsvY2xpZW50LXMzXCI7XG5pbXBvcnQgeyBTU01DbGllbnQsIEdldFBhcmFtZXRlckNvbW1hbmQgfSBmcm9tIFwiQGF3cy1zZGsvY2xpZW50LXNzbVwiO1xuaW1wb3J0IGdldERlYnVnIGZyb20gXCJkZWJ1Z1wiO1xuaW1wb3J0IHtcbiAgQ2FjaGVIYW5kbGVyLFxuICBDYWNoZUhhbmRsZXJWYWx1ZSxcbiAgQ2FjaGVIYW5kbGVyQ29udGV4dCxcbn0gZnJvbSBcIm5leHQvZGlzdC9zZXJ2ZXIvbGliL2luY3JlbWVudGFsLWNhY2hlXCI7XG5pbXBvcnQge1xuICBJbmNyZW1lbnRhbENhY2hlVmFsdWUsXG4gIEdldEluY3JlbWVudGFsRmV0Y2hDYWNoZUNvbnRleHQsXG4gIEdldEluY3JlbWVudGFsUmVzcG9uc2VDYWNoZUNvbnRleHQsXG4gIFNldEluY3JlbWVudGFsRmV0Y2hDYWNoZUNvbnRleHQsXG4gIFNldEluY3JlbWVudGFsUmVzcG9uc2VDYWNoZUNvbnRleHQsXG59IGZyb20gXCJuZXh0L2Rpc3Qvc2VydmVyL3Jlc3BvbnNlLWNhY2hlXCI7XG5pbXBvcnQgeyBzZXJpYWxpemVDYWNoZVZhbHVlLCBwYXJzZUNhY2hlVmFsdWUsIGdldFRhZ3MgfSBmcm9tIFwiLi9jYWNoZS11dGlsc1wiO1xuXG5pbnRlcmZhY2UgUzNDYWNoZUNvbmZpZyB7XG4gIGJ1Y2tldE5hbWU6IHN0cmluZztcbiAgcmVnaW9uOiBzdHJpbmc7XG4gIGJ1aWxkSWQ6IHN0cmluZztcbn1cblxuaW50ZXJmYWNlIER5bmFtb0RCUmV2YWxpZGF0aW9uQ29uZmlnIHtcbiAgdGFibGVOYW1lOiBzdHJpbmc7XG4gIHJlZ2lvbjogc3RyaW5nO1xuICBidWlsZElkOiBzdHJpbmc7XG59XG5cbmludGVyZmFjZSBDbG91ZEZyb250SW52YWxpZGF0aW9uQ29uZmlnIHtcbiAgLyoqXG4gICAqIE5hbWUgKG5vdCB2YWx1ZSkgb2YgdGhlIFNTTSBQYXJhbWV0ZXIgaG9sZGluZyB0aGUgZGlzdHJpYnV0aW9uIElELlxuICAgKlxuICAgKiBUaGUgZGlzdHJpYnV0aW9uIElEIGl0c2VsZiBjYW4ndCBiZSBwYXNzZWQgYXMgYSBwbGFpbiBlbnYgdmFyOiBDREtcbiAgICogY29uc3RydWN0cyB0aGUgTGFtYmRhL0ZhcmdhdGUgdGFzayBiZWZvcmUgdGhlIGRpc3RyaWJ1dGlvbiBleGlzdHMgKHRoZVxuICAgKiBkaXN0cmlidXRpb24ncyBvcmlnaW4gcmVmZXJlbmNlcyB0aGUgY29tcHV0ZSdzIGZ1bmN0aW9uIFVSTC9BTEIpLCBzb1xuICAgKiBlbWJlZGRpbmcgdGhlIGRpc3RyaWJ1dGlvbidzIHBoeXNpY2FsIElEIGRpcmVjdGx5IGluIHRoZSBjb21wdXRlJ3NcbiAgICogZW52aXJvbm1lbnQgb3IgSUFNIHBvbGljeSB3b3VsZCBjcmVhdGUgYSBjaXJjdWxhciBDbG91ZEZvcm1hdGlvblxuICAgKiBkZXBlbmRlbmN5LiBUaGUgcGFyYW1ldGVyICpuYW1lKiBpcyBzdGF0aWMgYW5kIGtub3duIGF0IHN5bnRoIHRpbWUsIHNvXG4gICAqIGl0IGNhbiBiZSBzYWZlbHkgZW1iZWRkZWQ7IG9ubHkgaXRzICp2YWx1ZSogZGVwZW5kcyBvbiB0aGUgZGlzdHJpYnV0aW9uLlxuICAgKi9cbiAgZGlzdHJpYnV0aW9uSWRQYXJhbWV0ZXJOYW1lOiBzdHJpbmc7XG4gIHJlZ2lvbjogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFMzQ2FjaGVIYW5kbGVyT3B0aW9ucyB7XG4gIGNvbnRleHQ6IENhY2hlSGFuZGxlckNvbnRleHQ7XG4gIHMzQ29uZmlnPzogUGFydGlhbDxTM0NhY2hlQ29uZmlnPjtcbiAgZHluYW1vQ29uZmlnPzogUGFydGlhbDxEeW5hbW9EQlJldmFsaWRhdGlvbkNvbmZpZz47XG4gIGNsb3VkRnJvbnRDb25maWc/OiBQYXJ0aWFsPENsb3VkRnJvbnRJbnZhbGlkYXRpb25Db25maWc+O1xufVxuXG5leHBvcnQgY2xhc3MgUzNDYWNoZUhhbmRsZXIgaW1wbGVtZW50cyBDYWNoZUhhbmRsZXIge1xuICBwcml2YXRlIHMzQ2xpZW50OiBTM0NsaWVudDtcbiAgcHJpdmF0ZSBkeW5hbW9DbGllbnQ6IER5bmFtb0RCQ2xpZW50O1xuICBwcml2YXRlIGNsb3VkRnJvbnRDbGllbnQ6IENsb3VkRnJvbnRDbGllbnQ7XG4gIHByaXZhdGUgc3NtQ2xpZW50OiBTU01DbGllbnQ7XG4gIHByaXZhdGUgczNDb25maWc6IFMzQ2FjaGVDb25maWc7XG4gIHByaXZhdGUgZHluYW1vQ29uZmlnOiBEeW5hbW9EQlJldmFsaWRhdGlvbkNvbmZpZztcbiAgcHJpdmF0ZSBjbG91ZEZyb250Q29uZmlnOiBDbG91ZEZyb250SW52YWxpZGF0aW9uQ29uZmlnO1xuICBwcml2YXRlIGRlYnVnID0gZ2V0RGVidWcoXCJjZGstbmV4dGpzOmNhY2hlLWhhbmRsZXI6czNcIik7XG5cbiAgLy8gQ2FjaGVkIGZvciB0aGUgbGlmZXRpbWUgb2YgdGhpcyBpbnN0YW5jZSAoaS5lLiB0aGUgY29tcHV0ZSBpbnN0YW5jZSkgb25jZVxuICAvLyByZXNvbHZlZCwgc2luY2UgYSBkZXBsb3ltZW50J3MgZGlzdHJpYnV0aW9uIElEIG5ldmVyIGNoYW5nZXMgYXQgcnVudGltZS5cbiAgcHJpdmF0ZSBjYWNoZWREaXN0cmlidXRpb25JZDogc3RyaW5nIHwgbnVsbCA9IG51bGw7XG5cbiAgY29uc3RydWN0b3Iob3B0aW9uczogUzNDYWNoZUhhbmRsZXJPcHRpb25zKSB7XG4gICAgY29uc3QgYnVpbGRJZCA9IHByb2Nlc3MuZW52LkNES19ORVhUSlNfQlVJTERfSUQgfHwgXCJcIjtcblxuICAgIC8vIEluaXRpYWxpemUgUzMgY29uZmlndXJhdGlvbiBmcm9tIGVudmlyb25tZW50IHZhcmlhYmxlcyBhbmQgb3B0aW9uc1xuICAgIHRoaXMuczNDb25maWcgPSB7XG4gICAgICBidWNrZXROYW1lOlxuICAgICAgICBvcHRpb25zLnMzQ29uZmlnPy5idWNrZXROYW1lIHx8XG4gICAgICAgIHByb2Nlc3MuZW52LkNES19ORVhUSlNfQ0FDSEVfQlVDS0VUX05BTUUgfHxcbiAgICAgICAgXCJcIixcbiAgICAgIHJlZ2lvbjogb3B0aW9ucy5zM0NvbmZpZz8ucmVnaW9uIHx8IHByb2Nlc3MuZW52LkFXU19SRUdJT04gfHwgXCJ1cy1lYXN0LTFcIixcbiAgICAgIGJ1aWxkSWQ6IG9wdGlvbnMuczNDb25maWc/LmJ1aWxkSWQgfHwgYnVpbGRJZCxcbiAgICB9O1xuXG4gICAgLy8gSW5pdGlhbGl6ZSBEeW5hbW9EQiBjb25maWd1cmF0aW9uIGZyb20gZW52aXJvbm1lbnQgdmFyaWFibGVzIGFuZCBvcHRpb25zXG4gICAgdGhpcy5keW5hbW9Db25maWcgPSB7XG4gICAgICB0YWJsZU5hbWU6XG4gICAgICAgIG9wdGlvbnMuZHluYW1vQ29uZmlnPy50YWJsZU5hbWUgfHxcbiAgICAgICAgcHJvY2Vzcy5lbnYuQ0RLX05FWFRKU19SRVZBTElEQVRJT05fVEFCTEVfTkFNRSB8fFxuICAgICAgICBcIlwiLFxuICAgICAgcmVnaW9uOlxuICAgICAgICBvcHRpb25zLmR5bmFtb0NvbmZpZz8ucmVnaW9uIHx8IHByb2Nlc3MuZW52LkFXU19SRUdJT04gfHwgXCJ1cy1lYXN0LTFcIixcbiAgICAgIGJ1aWxkSWQ6IG9wdGlvbnMuZHluYW1vQ29uZmlnPy5idWlsZElkIHx8IGJ1aWxkSWQsXG4gICAgfTtcblxuICAgIC8vIEluaXRpYWxpemUgQ2xvdWRGcm9udCBjb25maWd1cmF0aW9uIGZyb20gZW52aXJvbm1lbnQgdmFyaWFibGVzIGFuZCBvcHRpb25zLlxuICAgIC8vIE9ubHkgc2V0IGZvciBDbG91ZEZyb250LWZyb250ZWQgZGVwbG95bWVudHMgKE5leHRqc0dsb2JhbEZ1bmN0aW9ucy9Db250YWluZXJzKS5cbiAgICAvLyBXaGVuIHVuc2V0LCBvbi1kZW1hbmQgcmV2YWxpZGF0aW9uIHNraXBzIENETiBpbnZhbGlkYXRpb24gYW5kIHJlbGllcyBvbiB0aGVcbiAgICAvLyBkaXN0cmlidXRpb24ncyBjYWNoZSBwb2xpY3kgVFRMIChkcml2ZW4gYnkgdGhlIG9yaWdpbidzIENhY2hlLUNvbnRyb2wgaGVhZGVyKVxuICAgIC8vIHRvIGV2ZW50dWFsbHkgcGljayB1cCBmcmVzaCBjb250ZW50LlxuICAgIHRoaXMuY2xvdWRGcm9udENvbmZpZyA9IHtcbiAgICAgIGRpc3RyaWJ1dGlvbklkUGFyYW1ldGVyTmFtZTpcbiAgICAgICAgb3B0aW9ucy5jbG91ZEZyb250Q29uZmlnPy5kaXN0cmlidXRpb25JZFBhcmFtZXRlck5hbWUgfHxcbiAgICAgICAgcHJvY2Vzcy5lbnYuQ0RLX05FWFRKU19ESVNUUklCVVRJT05fSURfUEFSQU1fTkFNRSB8fFxuICAgICAgICBcIlwiLFxuICAgICAgcmVnaW9uOlxuICAgICAgICBvcHRpb25zLmNsb3VkRnJvbnRDb25maWc/LnJlZ2lvbiB8fFxuICAgICAgICBwcm9jZXNzLmVudi5BV1NfUkVHSU9OIHx8XG4gICAgICAgIFwidXMtZWFzdC0xXCIsXG4gICAgfTtcblxuICAgIC8vIEluaXRpYWxpemUgQVdTIGNsaWVudHNcbiAgICB0aGlzLnMzQ2xpZW50ID0gbmV3IFMzQ2xpZW50KHsgcmVnaW9uOiB0aGlzLnMzQ29uZmlnLnJlZ2lvbiB9KTtcbiAgICB0aGlzLmR5bmFtb0NsaWVudCA9IG5ldyBEeW5hbW9EQkNsaWVudCh7XG4gICAgICByZWdpb246IHRoaXMuZHluYW1vQ29uZmlnLnJlZ2lvbixcbiAgICB9KTtcbiAgICB0aGlzLmNsb3VkRnJvbnRDbGllbnQgPSBuZXcgQ2xvdWRGcm9udENsaWVudCh7XG4gICAgICByZWdpb246IHRoaXMuY2xvdWRGcm9udENvbmZpZy5yZWdpb24sXG4gICAgfSk7XG4gICAgdGhpcy5zc21DbGllbnQgPSBuZXcgU1NNQ2xpZW50KHsgcmVnaW9uOiB0aGlzLmNsb3VkRnJvbnRDb25maWcucmVnaW9uIH0pO1xuXG4gICAgaWYgKCF0aGlzLnMzQ29uZmlnLmJ1Y2tldE5hbWUpIHtcbiAgICAgIGNvbnNvbGUud2FybihcbiAgICAgICAgXCJDREtfTkVYVEpTX0NBQ0hFX0JVQ0tFVF9OQU1FIGVudmlyb25tZW50IHZhcmlhYmxlIG5vdCBzZXQsIFMzIGNhY2hlIGRpc2FibGVkXCIsXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmICghdGhpcy5keW5hbW9Db25maWcudGFibGVOYW1lKSB7XG4gICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgIFwiQ0RLX05FWFRKU19SRVZBTElEQVRJT05fVEFCTEVfTkFNRSBlbnZpcm9ubWVudCB2YXJpYWJsZSBub3Qgc2V0LCByZXZhbGlkYXRpb24gdHJhY2tpbmcgZGlzYWJsZWRcIixcbiAgICAgICk7XG4gICAgfVxuXG4gICAgaWYgKCFidWlsZElkKSB7XG4gICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgIFwiQ0RLX05FWFRKU19CVUlMRF9JRCBlbnZpcm9ubWVudCB2YXJpYWJsZSBub3Qgc2V0LCBjYWNoZSBpc29sYXRpb24gbWF5IG5vdCB3b3JrIGNvcnJlY3RseVwiLFxuICAgICAgKTtcbiAgICB9XG5cbiAgICAvLyBMb2cgdGhlIG9wdGlvbnMgZm9yIGRlYnVnZ2luZyAob3B0aW9uYWwgdXNhZ2UgdG8gYXZvaWQgdW51c2VkIHBhcmFtZXRlciB3YXJuaW5nKVxuICAgIGlmIChvcHRpb25zLmNvbnRleHQuZGV2KSB7XG4gICAgICB0aGlzLmRlYnVnKFwiUzNEeW5hbW9DYWNoZUhhbmRsZXIgaW5pdGlhbGl6ZWQgaW4gZGV2ZWxvcG1lbnQgbW9kZVwiKTtcbiAgICB9XG4gIH1cblxuICBhc3luYyBnZXQoXG4gICAgY2FjaGVLZXk6IHN0cmluZyxcbiAgICBjdHg6IEdldEluY3JlbWVudGFsRmV0Y2hDYWNoZUNvbnRleHQgfCBHZXRJbmNyZW1lbnRhbFJlc3BvbnNlQ2FjaGVDb250ZXh0LFxuICApOiBQcm9taXNlPENhY2hlSGFuZGxlclZhbHVlIHwgbnVsbD4ge1xuICAgIHRyeSB7XG4gICAgICAvLyBMb2cgY29udGV4dCBmb3IgZGVidWdnaW5nIChvcHRpb25hbCB1c2FnZSB0byBhdm9pZCB1bnVzZWQgcGFyYW1ldGVyIHdhcm5pbmcpXG4gICAgICBpZiAoY3R4LmtpbmQpIHtcbiAgICAgICAgdGhpcy5kZWJ1ZyhcbiAgICAgICAgICBgUzMgY2FjaGUgZ2V0IG9wZXJhdGlvbiBmb3IgJHtjYWNoZUtleX0gd2l0aCBraW5kOiAke2N0eC5raW5kfWAsXG4gICAgICAgICk7XG4gICAgICB9XG5cbiAgICAgIGlmICghdGhpcy5zM0NvbmZpZy5idWNrZXROYW1lKSB7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBzM0tleSA9IHRoaXMuYnVpbGRTM0tleShjYWNoZUtleSk7XG5cbiAgICAgIGNvbnN0IGNvbW1hbmQgPSBuZXcgR2V0T2JqZWN0Q29tbWFuZCh7XG4gICAgICAgIEJ1Y2tldDogdGhpcy5zM0NvbmZpZy5idWNrZXROYW1lLFxuICAgICAgICBLZXk6IHMzS2V5LFxuICAgICAgfSk7XG5cbiAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgdGhpcy5zM0NsaWVudC5zZW5kKGNvbW1hbmQpO1xuXG4gICAgICBpZiAoIXJlc3BvbnNlLkJvZHkpIHtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG5cbiAgICAgIC8vIEhhbmRsZSBkaWZmZXJlbnQgY29udGVudCB0eXBlcyBhcHByb3ByaWF0ZWx5IGZvciBkb2N1bWVudGVkIGZvcm1hdHNcbiAgICAgIGxldCBjYWNoZVZhbHVlOiBDYWNoZUhhbmRsZXJWYWx1ZTtcbiAgICAgIGNvbnN0IGNvbnRlbnRUeXBlID0gcmVzcG9uc2UuQ29udGVudFR5cGUgfHwgXCJhcHBsaWNhdGlvbi9qc29uXCI7XG5cbiAgICAgIC8vIEhhbmRsZSB0ZXh0LWJhc2VkIGRhdGEgKEpTT04sIEhUTUwsIHBsYWluIHRleHQpIC0gYWxsIGRvY3VtZW50ZWQgZm9ybWF0cyBhcmUgdGV4dC1iYXNlZFxuICAgICAgY29uc3QgYm9keVN0cmluZyA9IGF3YWl0IHJlc3BvbnNlLkJvZHkudHJhbnNmb3JtVG9TdHJpbmcoXCJ1dGYtOFwiKTtcblxuICAgICAgaWYgKGNvbnRlbnRUeXBlLmluY2x1ZGVzKFwiYXBwbGljYXRpb24vanNvblwiKSkge1xuICAgICAgICAvLyBQYXJzZSB0aGUgc3RvcmVkIENhY2hlSGFuZGxlclZhbHVlIGRpcmVjdGx5XG4gICAgICAgIGNvbnN0IHBhcnNlZFZhbHVlID0gcGFyc2VDYWNoZVZhbHVlKGJvZHlTdHJpbmcpO1xuXG4gICAgICAgIC8vIEV4dHJhY3QgdGhlIGFjdHVhbCBDYWNoZUhhbmRsZXJWYWx1ZSAod2l0aG91dCB0YWdzKSBmb3IgcmV0dXJuXG4gICAgICAgIGNhY2hlVmFsdWUgPSB7XG4gICAgICAgICAgbGFzdE1vZGlmaWVkOiBwYXJzZWRWYWx1ZS5sYXN0TW9kaWZpZWQsXG4gICAgICAgICAgdmFsdWU6IHBhcnNlZFZhbHVlLnZhbHVlLFxuICAgICAgICB9O1xuXG4gICAgICAgIC8vIENoZWNrIGlmIGNhY2hlIGhhcyBiZWVuIGludmFsaWRhdGVkIGJ5IHRhZyByZXZhbGlkYXRpb25cbiAgICAgICAgY29uc3Qgc3RvcmVkVGFncyA9IHBhcnNlZFZhbHVlLnRhZ3MgfHwgW107XG4gICAgICAgIGlmIChzdG9yZWRUYWdzLmxlbmd0aCA+IDAgJiYgdGhpcy5keW5hbW9Db25maWcudGFibGVOYW1lKSB7XG4gICAgICAgICAgY29uc3QgaXNJbnZhbGlkYXRlZCA9IGF3YWl0IHRoaXMuY2hlY2tJZlJldmFsaWRhdGVkKFxuICAgICAgICAgICAgY2FjaGVWYWx1ZS5sYXN0TW9kaWZpZWQsXG4gICAgICAgICAgICBzdG9yZWRUYWdzLFxuICAgICAgICAgICk7XG4gICAgICAgICAgaWYgKGlzSW52YWxpZGF0ZWQpIHtcbiAgICAgICAgICAgIHRoaXMuZGVidWcoYFMzIENBQ0hFIElOVkFMSURBVEVEIEJZIFRBRzogJHtjYWNoZUtleX1gKTtcbiAgICAgICAgICAgIC8vIERlbGV0ZSB0aGUgc3RhbGUgUzMgZW50cnlcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuZGVsZXRlUzNFbnRyeShzM0tleSk7XG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGBJbnZhbGlkIGNvbnRlbnQgdHlwZTogJHtjb250ZW50VHlwZX1gKTtcbiAgICAgICAgLy8gVGhpcyBzaG91bGRuJ3QgaGFwcGVuIHNpbmNlIHdlIGFsd2F5cyBzdG9yZSBhcyBKU09OIG5vd1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH1cblxuICAgICAgdGhpcy5kZWJ1ZyhgUzMgQ0FDSEUgSElUOiAke2NhY2hlS2V5fSAoJHtzM0tleX0pYCk7XG5cbiAgICAgIHJldHVybiBjYWNoZVZhbHVlO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBOb1N1Y2hLZXkpIHtcbiAgICAgICAgdGhpcy5kZWJ1ZyhgUzMgQ0FDSEUgTUlTUzogJHtjYWNoZUtleX1gKTtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG5cbiAgICAgIC8vIExvZyBhY3R1YWwgZXJyb3JzIChub3QgY2FjaGUgbWlzc2VzKVxuICAgICAgY29uc29sZS5lcnJvcihgRXJyb3IgcmV0cmlldmluZyBjYWNoZSBmcm9tIFMzOmAsIGVycm9yKTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIHNldChcbiAgICBjYWNoZUtleTogc3RyaW5nLFxuICAgIGRhdGE6IEluY3JlbWVudGFsQ2FjaGVWYWx1ZSB8IG51bGwsXG4gICAgY3R4OiBTZXRJbmNyZW1lbnRhbEZldGNoQ2FjaGVDb250ZXh0IHwgU2V0SW5jcmVtZW50YWxSZXNwb25zZUNhY2hlQ29udGV4dCxcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdHJ5IHtcbiAgICAgIGlmICghZGF0YSkge1xuICAgICAgICAvLyBEZWxldGUgZnJvbSBTMyBhbmQgRHluYW1vREJcbiAgICAgICAgdGhpcy5kZWJ1ZyhgUzMgQ0FDSEUgREVMRVRFOiAke2NhY2hlS2V5fWApO1xuXG4gICAgICAgIGlmICghdGhpcy5zM0NvbmZpZy5idWNrZXROYW1lKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gQnVpbGQgUzMga2V5IHdpdGhvdXQgbmVlZGluZyB0byBrbm93IHRoZSBraW5kXG4gICAgICAgIGNvbnN0IHMzS2V5ID0gdGhpcy5idWlsZFMzS2V5KGNhY2hlS2V5KTtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IGRlbGV0ZUNvbW1hbmQgPSBuZXcgRGVsZXRlT2JqZWN0Q29tbWFuZCh7XG4gICAgICAgICAgICBCdWNrZXQ6IHRoaXMuczNDb25maWcuYnVja2V0TmFtZSxcbiAgICAgICAgICAgIEtleTogczNLZXksXG4gICAgICAgICAgfSk7XG4gICAgICAgICAgYXdhaXQgdGhpcy5zM0NsaWVudC5zZW5kKGRlbGV0ZUNvbW1hbmQpO1xuICAgICAgICAgIHRoaXMuZGVidWcoYFMzIENBQ0hFIERFTEVURUQ6ICR7czNLZXl9YCk7XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgaWYgKGVycm9yIGluc3RhbmNlb2YgTm9TdWNoS2V5KSB7XG4gICAgICAgICAgICB0aGlzLmRlYnVnKGBGYWlsZWQgdG8gZGVsZXRlIFMzIGtleSAke3MzS2V5fTpgLCBlcnJvcik7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gVE9ETzogQWxzbyBkZWxldGUgdGFnIGFzc29jaWF0aW9ucyBmcm9tIER5bmFtb0RCIGlmIG5lZWRlZFxuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIC8vIERlYnVnIGxvZ2dpbmcgdG8gdW5kZXJzdGFuZCB3aGF0IGRhdGEgaXMgYmVpbmcgY2FjaGVkXG4gICAgICBjb25zdCB0YWdzID0gZ2V0VGFncyhjdHgpO1xuICAgICAgdGhpcy5kZWJ1ZyhgUzMgQ0FDSEUgU0VUOiBLZXk6ICR7Y2FjaGVLZXl9LCB0YWdzOiAke3RhZ3MgfHwgXCJub25lXCJ9YCk7XG5cbiAgICAgIC8vIE5vdGU6IGN0eC50YWdzIGFyZSBhdmFpbGFibGUgYnV0IHJldmFsaWRhdGlvbiBpcyBoYW5kbGVkIHNlcGFyYXRlbHkgaW4gcmV2YWxpZGF0ZVRhZyBtZXRob2RcbiAgICAgIC8vIExvZyB0YWdzIGZvciBkZWJ1Z2dpbmcgaWYgcHJlc2VudCAob25seSBpbiBTZXRJbmNyZW1lbnRhbEZldGNoQ2FjaGVDb250ZXh0KVxuICAgICAgaWYgKHRhZ3MgJiYgdGFncy5sZW5ndGggPiAwKSB7XG4gICAgICAgIHRoaXMuZGVidWcoYFMzIGNhY2hlIGVudHJ5IGZvciAke2NhY2hlS2V5fSBoYXMgdGFnczpgLCB0YWdzKTtcbiAgICAgIH1cblxuICAgICAgaWYgKCF0aGlzLnMzQ29uZmlnLmJ1Y2tldE5hbWUpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBzM0tleSA9IHRoaXMuYnVpbGRTM0tleShjYWNoZUtleSk7XG5cbiAgICAgIC8vIENyZWF0ZSBDYWNoZUhhbmRsZXJWYWx1ZSBzdHJ1Y3R1cmUgZm9yIFMzIHN0b3JhZ2Ugd2l0aCB0YWdzXG4gICAgICBjb25zdCBjYWNoZUhhbmRsZXJWYWx1ZTogQ2FjaGVIYW5kbGVyVmFsdWUgPSB7XG4gICAgICAgIGxhc3RNb2RpZmllZDogRGF0ZS5ub3coKSxcbiAgICAgICAgdmFsdWU6IGRhdGEsXG4gICAgICB9O1xuXG4gICAgICAvLyBTdG9yZSB0YWdzIHdpdGggdGhlIGNhY2hlIGVudHJ5IGZvciByZXZhbGlkYXRpb24gY2hlY2tpbmdcbiAgICAgIGNvbnN0IGNhY2hlRW50cnlXaXRoVGFncyA9IHtcbiAgICAgICAgLi4uY2FjaGVIYW5kbGVyVmFsdWUsXG4gICAgICAgIHRhZ3M6IHRhZ3MgfHwgW10sXG4gICAgICB9O1xuXG4gICAgICAvLyBTZXJpYWxpemUgd2l0aCBjdXN0b20gaGFuZGxpbmcgZm9yIE1hcCBhbmQgQnVmZmVyIG9iamVjdHNcbiAgICAgIGNvbnN0IGJvZHkgPSBzZXJpYWxpemVDYWNoZVZhbHVlKGNhY2hlRW50cnlXaXRoVGFncyk7XG5cbiAgICAgIGNvbnN0IGNvbW1hbmQgPSBuZXcgUHV0T2JqZWN0Q29tbWFuZCh7XG4gICAgICAgIEJ1Y2tldDogdGhpcy5zM0NvbmZpZy5idWNrZXROYW1lLFxuICAgICAgICBLZXk6IHMzS2V5LFxuICAgICAgICBCb2R5OiBib2R5LFxuICAgICAgICBDb250ZW50VHlwZTogXCJhcHBsaWNhdGlvbi9qc29uOyBjaGFyc2V0PXV0Zi04XCIsIC8vIEFsd2F5cyBKU09OIHNpbmNlIHdlIHN0b3JlIENhY2hlSGFuZGxlclZhbHVlXG4gICAgICB9KTtcblxuICAgICAgYXdhaXQgdGhpcy5zM0NsaWVudC5zZW5kKGNvbW1hbmQpO1xuXG4gICAgICB0aGlzLmRlYnVnKGBTMyBDQUNIRSBTVE9SRUQ6ICR7Y2FjaGVLZXl9ICgke2RhdGEua2luZH0pYCk7XG5cbiAgICAgIC8vIFN0b3JlIHRhZy10by1jYWNoZS1rZXkgbWFwcGluZ3MgaW4gRHluYW1vREIgZm9yIHJldmFsaWRhdGlvblxuICAgICAgaWYgKHRhZ3MgJiYgdGFncy5sZW5ndGggPiAwICYmIHRoaXMuZHluYW1vQ29uZmlnLnRhYmxlTmFtZSkge1xuICAgICAgICB0aGlzLmRlYnVnKGBTVE9SSU5HIFRBR1M6ICR7Y2FjaGVLZXl9IC0+IFske3RhZ3Muam9pbihcIiwgXCIpfV1gKTtcbiAgICAgICAgYXdhaXQgdGhpcy5zdG9yZUR5bmFtb0RCVGFnTWFwcGluZ3MoczNLZXksIHRhZ3MpO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zb2xlLmVycm9yKFwiRXJyb3Igc3RvcmluZyBjYWNoZSB0byBTMzpcIiwgZXJyb3IpO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIHJldmFsaWRhdGVUYWcodGFnOiBzdHJpbmcgfCBzdHJpbmdbXSk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHRhZ3MgPSBBcnJheS5pc0FycmF5KHRhZykgPyB0YWcgOiBbdGFnXTtcbiAgICB0aGlzLmRlYnVnKGBSRVZBTElEQVRJTkcgVEFHUzogWyR7dGFncy5qb2luKFwiLCBcIil9XWApO1xuXG4gICAgdHJ5IHtcbiAgICAgIGlmICghdGhpcy5keW5hbW9Db25maWcudGFibGVOYW1lKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgLy8gUHJvY2VzcyBhbGwgdGFncyBpbiBwYXJhbGxlbCBmb3IgYmV0dGVyIHBlcmZvcm1hbmNlXG4gICAgICBhd2FpdCBQcm9taXNlLmFsbCh0YWdzLm1hcCgodCkgPT4gdGhpcy5yZXZhbGlkYXRlU2luZ2xlVGFnKHQpKSk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoXCJFcnJvciB1cGRhdGluZyByZXZhbGlkYXRpb24gbWV0YWRhdGE6XCIsIGVycm9yKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHJldmFsaWRhdGVTaW5nbGVUYWcodGFnOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAvLyBRdWVyeSBhbGwgcGF0aHMgYXNzb2NpYXRlZCB3aXRoIHRoaXMgdGFnXG4gICAgY29uc3QgcXVlcnlDb21tYW5kID0gbmV3IFF1ZXJ5Q29tbWFuZCh7XG4gICAgICBUYWJsZU5hbWU6IHRoaXMuZHluYW1vQ29uZmlnLnRhYmxlTmFtZSxcbiAgICAgIEtleUNvbmRpdGlvbkV4cHJlc3Npb246IFwicGsgPSA6cGsgQU5EIGJlZ2luc193aXRoKHNrLCA6c2tQcmVmaXgpXCIsXG4gICAgICBFeHByZXNzaW9uQXR0cmlidXRlVmFsdWVzOiB7XG4gICAgICAgIFwiOnBrXCI6IHsgUzogdGhpcy5keW5hbW9Db25maWcuYnVpbGRJZCB9LFxuICAgICAgICBcIjpza1ByZWZpeFwiOiB7IFM6IGAke3RhZ30jYCB9LFxuICAgICAgfSxcbiAgICB9KTtcblxuICAgIGNvbnN0IHF1ZXJ5UmVzcG9uc2UgPSBhd2FpdCB0aGlzLmR5bmFtb0NsaWVudC5zZW5kKHF1ZXJ5Q29tbWFuZCk7XG5cbiAgICBpZiAocXVlcnlSZXNwb25zZS5JdGVtcykge1xuICAgICAgLy8gRXh0cmFjdCBTMyBrZXlzIGZyb20gc29ydCBrZXlzIChmb3JtYXQ6IFwidGFnI3MzS2V5XCIpXG4gICAgICBjb25zdCBjYWNoZUtleXMgPSBxdWVyeVJlc3BvbnNlLkl0ZW1zLm1hcCgoaXRlbSkgPT4ge1xuICAgICAgICBjb25zdCBzayA9IGl0ZW0uc2s/LlM7XG4gICAgICAgIGlmIChzaykge1xuICAgICAgICAgIGNvbnN0IGhhc2hJbmRleCA9IHNrLmluZGV4T2YoXCIjXCIpO1xuICAgICAgICAgIHJldHVybiBoYXNoSW5kZXggIT09IC0xID8gc2suc3Vic3RyaW5nKGhhc2hJbmRleCArIDEpIDogbnVsbDtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH0pLmZpbHRlcihCb29sZWFuKTtcblxuICAgICAgdGhpcy5kZWJ1ZyhcbiAgICAgICAgYFRBRyAke3RhZ306IEZvdW5kICR7Y2FjaGVLZXlzLmxlbmd0aH0gY2FjaGUgZW50cmllcyB0byBpbnZhbGlkYXRlYCxcbiAgICAgICk7XG5cbiAgICAgIC8vIFVwZGF0ZSByZXZhbGlkYXRpb24gdGltZXN0YW1wIGZvciBhbGwgY2FjaGUga2V5cyB3aXRoIHRoaXMgdGFnXG4gICAgICBjb25zdCB1cGRhdGVQcm9taXNlcyA9IHF1ZXJ5UmVzcG9uc2UuSXRlbXMubWFwKGFzeW5jIChpdGVtKSA9PiB7XG4gICAgICAgIGNvbnN0IHNrID0gaXRlbS5zaz8uUztcbiAgICAgICAgaWYgKHNrKSB7XG4gICAgICAgICAgY29uc3QgdXBkYXRlQ29tbWFuZCA9IG5ldyBVcGRhdGVJdGVtQ29tbWFuZCh7XG4gICAgICAgICAgICBUYWJsZU5hbWU6IHRoaXMuZHluYW1vQ29uZmlnLnRhYmxlTmFtZSxcbiAgICAgICAgICAgIEtleToge1xuICAgICAgICAgICAgICBwazogeyBTOiB0aGlzLmR5bmFtb0NvbmZpZy5idWlsZElkIH0sXG4gICAgICAgICAgICAgIHNrOiB7IFM6IHNrIH0sXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgVXBkYXRlRXhwcmVzc2lvbjogXCJTRVQgcmV2YWxpZGF0ZWRBdCA9IDp0aW1lc3RhbXBcIixcbiAgICAgICAgICAgIEV4cHJlc3Npb25BdHRyaWJ1dGVWYWx1ZXM6IHtcbiAgICAgICAgICAgICAgXCI6dGltZXN0YW1wXCI6IHsgTjogRGF0ZS5ub3coKS50b1N0cmluZygpIH0sXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgcmV0dXJuIHRoaXMuZHluYW1vQ2xpZW50LnNlbmQodXBkYXRlQ29tbWFuZCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpO1xuICAgICAgfSk7XG5cbiAgICAgIGF3YWl0IFByb21pc2UuYWxsKHVwZGF0ZVByb21pc2VzLmZpbHRlcihCb29sZWFuKSk7XG5cbiAgICAgIC8vIERlbGV0ZSB0aGUgY29ycmVzcG9uZGluZyBTMyBjYWNoZSBlbnRyaWVzIHRvIGludmFsaWRhdGUgdGhlbVxuICAgICAgaWYgKHRoaXMuczNDb25maWcuYnVja2V0TmFtZSkge1xuICAgICAgICBjb25zdCBkZWxldGVQcm9taXNlcyA9IGNhY2hlS2V5cy5tYXAoYXN5bmMgKHMzS2V5KSA9PiB7XG4gICAgICAgICAgaWYgKHMzS2V5KSB7XG4gICAgICAgICAgICBjb25zdCBkZWxldGVDb21tYW5kID0gbmV3IERlbGV0ZU9iamVjdENvbW1hbmQoe1xuICAgICAgICAgICAgICBCdWNrZXQ6IHRoaXMuczNDb25maWcuYnVja2V0TmFtZSxcbiAgICAgICAgICAgICAgS2V5OiBzM0tleSxcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICBhd2FpdCB0aGlzLnMzQ2xpZW50LnNlbmQoZGVsZXRlQ29tbWFuZCk7XG4gICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgICAvLyBMb2cgYnV0IGRvbid0IGZhaWwgLSB0aGUgZW50cnkgbWlnaHQgbm90IGV4aXN0IGluIFMzXG4gICAgICAgICAgICAgIGNvbnNvbGUud2FybihgRmFpbGVkIHRvIGRlbGV0ZSBTMyBjYWNoZSBlbnRyeSAke3MzS2V5fTpgLCBlcnJvcik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICBhd2FpdCBQcm9taXNlLmFsbChkZWxldGVQcm9taXNlcy5maWx0ZXIoQm9vbGVhbikpO1xuICAgICAgfVxuXG4gICAgICAvLyBJbnZhbGlkYXRlIHRoZSBDRE4gZWRnZSBjYWNoZSBzbyBDbG91ZEZyb250LWZyb250ZWQgZGVwbG95bWVudHMgZG9uJ3RcbiAgICAgIC8vIGtlZXAgc2VydmluZyBzdGFsZSByZXNwb25zZXMgdW50aWwgdGhlIGNhY2hlIHBvbGljeSdzIFRUTCBuYXR1cmFsbHkgZXhwaXJlcy5cbiAgICAgIGlmICh0aGlzLmNsb3VkRnJvbnRDb25maWcuZGlzdHJpYnV0aW9uSWRQYXJhbWV0ZXJOYW1lKSB7XG4gICAgICAgIGNvbnN0IGludmFsaWRhdGlvblBhdGhzID0gY2FjaGVLZXlzXG4gICAgICAgICAgLmZpbHRlcigoczNLZXkpOiBzM0tleSBpcyBzdHJpbmcgPT4gQm9vbGVhbihzM0tleSkpXG4gICAgICAgICAgLm1hcCgoczNLZXkpID0+IHRoaXMuczNLZXlUb0ludmFsaWRhdGlvblBhdGgoczNLZXkpKTtcbiAgICAgICAgYXdhaXQgdGhpcy5pbnZhbGlkYXRlQ2xvdWRGcm9udFBhdGhzKGludmFsaWRhdGlvblBhdGhzKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmV2ZXJzZXMgYGJ1aWxkUzNLZXlgIHRvIHJlY292ZXIgdGhlIHJlcXVlc3QgcGF0aCBDbG91ZEZyb250IGNhY2hlZCB0aGVcbiAgICogcmVzcG9uc2UgdW5kZXIuIEZldGNoLWNhY2hlIGVudHJpZXMgKG9wYXF1ZSBoYXNoIGtleXMsIG5vdCBwYWdlIHJvdXRlcylcbiAgICogdHJhbnNsYXRlIHRvIGEgcGF0aCB0aGF0IHdvbid0IG1hdGNoIGFueXRoaW5nIGNhY2hlZCwgd2hpY2ggaXMgaGFybWxlc3MuXG4gICAqL1xuICBwcml2YXRlIHMzS2V5VG9JbnZhbGlkYXRpb25QYXRoKHMzS2V5OiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIGNvbnN0IHByZWZpeCA9IGAke3RoaXMuczNDb25maWcuYnVpbGRJZH0vYDtcbiAgICBjb25zdCB3aXRob3V0UHJlZml4ID0gczNLZXkuc3RhcnRzV2l0aChwcmVmaXgpXG4gICAgICA/IHMzS2V5LnNsaWNlKHByZWZpeC5sZW5ndGgpXG4gICAgICA6IHMzS2V5O1xuICAgIGNvbnN0IHdpdGhvdXRTdWZmaXggPSB3aXRob3V0UHJlZml4LmVuZHNXaXRoKFwiLmpzb25cIilcbiAgICAgID8gd2l0aG91dFByZWZpeC5zbGljZSgwLCAtXCIuanNvblwiLmxlbmd0aClcbiAgICAgIDogd2l0aG91dFByZWZpeDtcblxuICAgIHJldHVybiB3aXRob3V0U3VmZml4ID09PSBcImluZGV4XCIgPyBcIi9cIiA6IGAvJHt3aXRob3V0U3VmZml4fWA7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGludmFsaWRhdGVDbG91ZEZyb250UGF0aHMocGF0aHM6IHN0cmluZ1tdKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgdW5pcXVlUGF0aHMgPSBBcnJheS5mcm9tKG5ldyBTZXQocGF0aHMpKTtcbiAgICBpZiAodW5pcXVlUGF0aHMubGVuZ3RoID09PSAwKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGRpc3RyaWJ1dGlvbklkID0gYXdhaXQgdGhpcy5nZXREaXN0cmlidXRpb25JZCgpO1xuICAgICAgaWYgKCFkaXN0cmlidXRpb25JZCkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIHRoaXMuZGVidWcoXG4gICAgICAgIGBDTE9VREZST05UIElOVkFMSURBVElPTjogWyR7dW5pcXVlUGF0aHMuam9pbihcIiwgXCIpfV0gb24gZGlzdHJpYnV0aW9uICR7ZGlzdHJpYnV0aW9uSWR9YCxcbiAgICAgICk7XG5cbiAgICAgIGF3YWl0IHRoaXMuY2xvdWRGcm9udENsaWVudC5zZW5kKFxuICAgICAgICBuZXcgQ3JlYXRlSW52YWxpZGF0aW9uQ29tbWFuZCh7XG4gICAgICAgICAgRGlzdHJpYnV0aW9uSWQ6IGRpc3RyaWJ1dGlvbklkLFxuICAgICAgICAgIEludmFsaWRhdGlvbkJhdGNoOiB7XG4gICAgICAgICAgICBDYWxsZXJSZWZlcmVuY2U6IHJhbmRvbVVVSUQoKSxcbiAgICAgICAgICAgIFBhdGhzOiB7XG4gICAgICAgICAgICAgIFF1YW50aXR5OiB1bmlxdWVQYXRocy5sZW5ndGgsXG4gICAgICAgICAgICAgIEl0ZW1zOiB1bmlxdWVQYXRocyxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfSxcbiAgICAgICAgfSksXG4gICAgICApO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAvLyBMb2cgYnV0IGRvbid0IGZhaWwgLSB0aGUgUzMvRHluYW1vREIgaW52YWxpZGF0aW9uIGFscmVhZHkgc3VjY2VlZGVkLFxuICAgICAgLy8gYW5kIHRoZSBDbG91ZEZyb250IGNhY2hlIHBvbGljeSBUVEwgcHJvdmlkZXMgYW4gZXZlbnR1YWwgZmFsbGJhY2suXG4gICAgICBjb25zb2xlLndhcm4oXCJGYWlsZWQgdG8gY3JlYXRlIENsb3VkRnJvbnQgaW52YWxpZGF0aW9uOlwiLCBlcnJvcik7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJlc29sdmVzIHRoZSBkaXN0cmlidXRpb24ncyBwaHlzaWNhbCBJRCB2aWEgU1NNIFBhcmFtZXRlciBTdG9yZSwgY2FjaGluZ1xuICAgKiBpdCBmb3IgdGhlIGxpZmV0aW1lIG9mIHRoaXMgaW5zdGFuY2UuIFNlZSBgQ2xvdWRGcm9udEludmFsaWRhdGlvbkNvbmZpZ2BcbiAgICogZm9yIHdoeSB0aGlzIGluZGlyZWN0aW9uIChyYXRoZXIgdGhhbiBhIHBsYWluIGVudiB2YXIpIGlzIG5lY2Vzc2FyeS5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZ2V0RGlzdHJpYnV0aW9uSWQoKTogUHJvbWlzZTxzdHJpbmcgfCBudWxsPiB7XG4gICAgaWYgKHRoaXMuY2FjaGVkRGlzdHJpYnV0aW9uSWQpIHtcbiAgICAgIHJldHVybiB0aGlzLmNhY2hlZERpc3RyaWJ1dGlvbklkO1xuICAgIH1cblxuICAgIGlmICghdGhpcy5jbG91ZEZyb250Q29uZmlnLmRpc3RyaWJ1dGlvbklkUGFyYW1ldGVyTmFtZSkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCB0aGlzLnNzbUNsaWVudC5zZW5kKFxuICAgICAgbmV3IEdldFBhcmFtZXRlckNvbW1hbmQoe1xuICAgICAgICBOYW1lOiB0aGlzLmNsb3VkRnJvbnRDb25maWcuZGlzdHJpYnV0aW9uSWRQYXJhbWV0ZXJOYW1lLFxuICAgICAgfSksXG4gICAgKTtcblxuICAgIHRoaXMuY2FjaGVkRGlzdHJpYnV0aW9uSWQgPSByZXNwb25zZS5QYXJhbWV0ZXI/LlZhbHVlIHx8IG51bGw7XG4gICAgcmV0dXJuIHRoaXMuY2FjaGVkRGlzdHJpYnV0aW9uSWQ7XG4gIH1cblxuICBhc3luYyByZXNldFJlcXVlc3RDYWNoZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAvLyBUaGlzIGhhbmRsZXIgZG9lc24ndCBtYWludGFpbiByZXF1ZXN0LWxldmVsIGNhY2hlIHN0YXRlXG4gICAgLy8gVGhlIGFjdHVhbCBjYWNoZSBjbGVhcmluZyBpcyBoYW5kbGVkIGJ5IHRoZSBtZW1vcnkgY2FjaGUgaGFuZGxlclxuICB9XG5cbiAgcHJpdmF0ZSBidWlsZFMzS2V5KGNhY2hlS2V5OiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIC8vIFVzZSBCVUlMRF9JRCBwcmVmaXhpbmc6IHtidWlsZElkfS97Y2FjaGVLZXl9Lmpzb25cblxuICAgIC8vIEhhbmRsZSBlZGdlIGNhc2VzOlxuICAgIC8vIC0gUm9vdCBwYXRoIFwiL1wiIHNob3VsZCBiZWNvbWUgXCJpbmRleFwiIG9yIHNpbWlsYXJcbiAgICAvLyAtIFJlbW92ZSBsZWFkaW5nIHNsYXNoZXMgdG8gcHJldmVudCBlbXB0eSBmb2xkZXJzXG4gICAgbGV0IGNsZWFuQ2FjaGVLZXkgPSBjYWNoZUtleTtcblxuICAgIGlmIChjYWNoZUtleSA9PT0gXCIvXCIgfHwgY2FjaGVLZXkgPT09IFwiXCIpIHtcbiAgICAgIGNsZWFuQ2FjaGVLZXkgPSBcImluZGV4XCI7XG4gICAgfSBlbHNlIGlmIChjYWNoZUtleS5zdGFydHNXaXRoKFwiL1wiKSkge1xuICAgICAgY2xlYW5DYWNoZUtleSA9IGNhY2hlS2V5LnNsaWNlKDEpO1xuICAgIH1cblxuICAgIHJldHVybiBqb2luKHRoaXMuczNDb25maWcuYnVpbGRJZCwgYCR7Y2xlYW5DYWNoZUtleX0uanNvbmApO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBzdG9yZUR5bmFtb0RCVGFnTWFwcGluZ3MoXG4gICAgczNLZXk6IHN0cmluZyxcbiAgICB0YWdzOiBzdHJpbmdbXSxcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHVwZGF0ZVByb21pc2VzID0gdGFncy5tYXAoYXN5bmMgKHRhZykgPT4ge1xuICAgICAgICBjb25zdCB0YWdDYWNoZUtleSA9IGAke3RhZ30jJHtzM0tleX1gO1xuICAgICAgICBjb25zdCB1cGRhdGVDb21tYW5kID0gbmV3IFVwZGF0ZUl0ZW1Db21tYW5kKHtcbiAgICAgICAgICBUYWJsZU5hbWU6IHRoaXMuZHluYW1vQ29uZmlnLnRhYmxlTmFtZSxcbiAgICAgICAgICBLZXk6IHtcbiAgICAgICAgICAgIHBrOiB7IFM6IHRoaXMuZHluYW1vQ29uZmlnLmJ1aWxkSWQgfSxcbiAgICAgICAgICAgIHNrOiB7IFM6IHRhZ0NhY2hlS2V5IH0sXG4gICAgICAgICAgfSxcbiAgICAgICAgICBVcGRhdGVFeHByZXNzaW9uOlxuICAgICAgICAgICAgXCJTRVQgY3JlYXRlZEF0ID0gaWZfbm90X2V4aXN0cyhjcmVhdGVkQXQsIDpub3cpLCByZXZhbGlkYXRlZEF0ID0gOm5vd1wiLFxuICAgICAgICAgIEV4cHJlc3Npb25BdHRyaWJ1dGVWYWx1ZXM6IHtcbiAgICAgICAgICAgIFwiOm5vd1wiOiB7IE46IERhdGUubm93KCkudG9TdHJpbmcoKSB9LFxuICAgICAgICAgIH0sXG4gICAgICAgIH0pO1xuXG4gICAgICAgIGF3YWl0IHRoaXMuZHluYW1vQ2xpZW50LnNlbmQodXBkYXRlQ29tbWFuZCk7XG4gICAgICB9KTtcblxuICAgICAgYXdhaXQgUHJvbWlzZS5hbGwodXBkYXRlUHJvbWlzZXMpO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zb2xlLmVycm9yKFwiRXJyb3Igc3RvcmluZyBEeW5hbW9EQiB0YWcgbWFwcGluZ3M6XCIsIGVycm9yKTtcbiAgICAgIC8vIERvbid0IHRocm93IC0gY2FjaGUgc3RvcmFnZSBzaG91bGQgY29udGludWUgZXZlbiBpZiB0YWcgbWFwcGluZyBmYWlsc1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgY2hlY2tJZlJldmFsaWRhdGVkKFxuICAgIGNhY2hlTGFzdE1vZGlmaWVkOiBudW1iZXIsXG4gICAgdGFnczogc3RyaW5nW10sXG4gICk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIHRyeSB7XG4gICAgICAvLyBDaGVjayBlYWNoIHRhZyB0byBzZWUgaWYgaXQgaGFzIGJlZW4gcmV2YWxpZGF0ZWQgYWZ0ZXIgdGhlIGNhY2hlIHdhcyBjcmVhdGVkXG4gICAgICBmb3IgKGNvbnN0IHRhZyBvZiB0YWdzKSB7XG4gICAgICAgIGNvbnN0IHF1ZXJ5Q29tbWFuZCA9IG5ldyBRdWVyeUNvbW1hbmQoe1xuICAgICAgICAgIFRhYmxlTmFtZTogdGhpcy5keW5hbW9Db25maWcudGFibGVOYW1lLFxuICAgICAgICAgIEtleUNvbmRpdGlvbkV4cHJlc3Npb246IFwicGsgPSA6cGsgQU5EIGJlZ2luc193aXRoKHNrLCA6c2tQcmVmaXgpXCIsXG4gICAgICAgICAgRXhwcmVzc2lvbkF0dHJpYnV0ZVZhbHVlczoge1xuICAgICAgICAgICAgXCI6cGtcIjogeyBTOiB0aGlzLmR5bmFtb0NvbmZpZy5idWlsZElkIH0sXG4gICAgICAgICAgICBcIjpza1ByZWZpeFwiOiB7IFM6IGAke3RhZ30jYCB9LFxuICAgICAgICAgIH0sXG4gICAgICAgICAgUHJvamVjdGlvbkV4cHJlc3Npb246IFwicmV2YWxpZGF0ZWRBdFwiLFxuICAgICAgICAgIExpbWl0OiAxLCAvLyBXZSBvbmx5IG5lZWQgdG8gY2hlY2sgaWYgYW55IGVudHJ5IGV4aXN0cyB3aXRoIHRoaXMgdGFnXG4gICAgICAgIH0pO1xuXG4gICAgICAgIGNvbnN0IHF1ZXJ5UmVzcG9uc2UgPSBhd2FpdCB0aGlzLmR5bmFtb0NsaWVudC5zZW5kKHF1ZXJ5Q29tbWFuZCk7XG5cbiAgICAgICAgaWYgKHF1ZXJ5UmVzcG9uc2UuSXRlbXMgJiYgcXVlcnlSZXNwb25zZS5JdGVtcy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgY29uc3QgcmV2YWxpZGF0ZWRBdCA9IHF1ZXJ5UmVzcG9uc2UuSXRlbXNbMF0ucmV2YWxpZGF0ZWRBdD8uTjtcbiAgICAgICAgICBpZiAocmV2YWxpZGF0ZWRBdCAmJiBwYXJzZUludChyZXZhbGlkYXRlZEF0KSA+IGNhY2hlTGFzdE1vZGlmaWVkKSB7XG4gICAgICAgICAgICB0aGlzLmRlYnVnKFxuICAgICAgICAgICAgICBgVGFnICR7dGFnfSB3YXMgcmV2YWxpZGF0ZWQgYXQgJHtyZXZhbGlkYXRlZEF0fSwgY2FjaGUgY3JlYXRlZCBhdCAke2NhY2hlTGFzdE1vZGlmaWVkfWAsXG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgcmV0dXJuIHRydWU7IC8vIENhY2hlIGlzIGludmFsaWRhdGVkXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBmYWxzZTsgLy8gQ2FjaGUgaXMgc3RpbGwgdmFsaWRcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgY29uc29sZS5lcnJvcihcIkVycm9yIGNoZWNraW5nIGNhY2hlIHJldmFsaWRhdGlvbjpcIiwgZXJyb3IpO1xuICAgICAgLy8gT24gZXJyb3IsIGFzc3VtZSBjYWNoZSBpcyB2YWxpZCB0byBhdm9pZCB1bm5lY2Vzc2FyeSBjYWNoZSBtaXNzZXNcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGRlbGV0ZVMzRW50cnkoczNLZXk6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRyeSB7XG4gICAgICBpZiAoIXRoaXMuczNDb25maWcuYnVja2V0TmFtZSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGRlbGV0ZUNvbW1hbmQgPSBuZXcgRGVsZXRlT2JqZWN0Q29tbWFuZCh7XG4gICAgICAgIEJ1Y2tldDogdGhpcy5zM0NvbmZpZy5idWNrZXROYW1lLFxuICAgICAgICBLZXk6IHMzS2V5LFxuICAgICAgfSk7XG5cbiAgICAgIGF3YWl0IHRoaXMuczNDbGllbnQuc2VuZChkZWxldGVDb21tYW5kKTtcbiAgICAgIHRoaXMuZGVidWcoYERlbGV0ZWQgc3RhbGUgUzMgY2FjaGUgZW50cnk6ICR7czNLZXl9YCk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIC8vIExvZyBidXQgZG9uJ3QgZmFpbCAtIHRoZSBlbnRyeSBtaWdodCBub3QgZXhpc3QgaW4gUzNcbiAgICAgIGNvbnNvbGUud2FybihgRmFpbGVkIHRvIGRlbGV0ZSBzdGFsZSBTMyBjYWNoZSBlbnRyeSAke3MzS2V5fTpgLCBlcnJvcik7XG4gICAgfVxuICB9XG59XG4iXX0=
|
|
982
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiczMtY2FjaGUtaGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hZGFwdGVyL3MzLWNhY2hlLWhhbmRsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUE7O0VBRUU7QUFDRixzREFBc0Q7QUFDdEQsNkNBQXlDO0FBQ3pDLHFDQUF1QztBQUN2Qyw2Q0FBNEM7QUFDNUMseUNBQWlDO0FBQ2pDLGtFQUdvQztBQUNwQyw4REFNa0M7QUFDbEMsa0RBQW1FO0FBQ25FLG9EQUFxRTtBQUNyRSxrREFBNkI7QUFhN0IsdURBUzJCO0FBQzNCLCtDQU11QjtBQUN2Qix5REFBdUQ7QUFDdkQsa0RBQTREO0FBQzVELGtEQUErRTtBQUUvRTs7Ozs7Ozs7Ozs7O0dBWUc7QUFDSCxTQUFTLFNBQVMsQ0FBQyxNQUE0QztJQUM3RCxPQUFPLE1BQU0sQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFBLHdCQUFVLEVBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO0FBQ3RFLENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7O0dBZUc7QUFDSCxNQUFNLHFCQUFxQixHQUFHLENBQUMsQ0FBQyxDQUFDO0FBRWpDOzs7Ozs7OztHQVFHO0FBQ0gsU0FBUyxlQUFlLENBQ3RCLEdBQXlFO0lBRXpFLE9BQU8sR0FBRyxDQUFDLElBQUksS0FBSyxPQUFPLENBQUM7QUFDOUIsQ0FBQztBQUVELHVGQUF1RjtBQUN2RixNQUFNLDBCQUEwQixHQUFHLE9BQU8sQ0FBQztBQUUzQzs7O0dBR0c7QUFDSCxNQUFNLHFCQUFxQixHQUFHLENBQUMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxDQUFDO0FBRWpEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FrQkc7QUFDSCxTQUFTLGdCQUFnQixDQUFDLEdBQVc7SUFDbkMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsR0FBRywwQkFBMEIsR0FB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T0FBTyxDQUFDO1lBQzVDLE1BQU0sUUFBUSxHQUFHLFFBQVEsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDbkMsSUFDRSxPQUFPLEtBQUssU0FBUztnQkFDckIsT0FBTyxHQUFHLGlCQUFpQjtnQkFDM0IsQ0FBQyxRQUFRLEVBQUUsS0FBSyxJQUFJLENBQUMsQ0FBQyxHQUFHLE9BQU8sRUFDaEMsQ0FBQztnQkFDRCxRQUFRLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxFQUFFLEdBQUcsUUFBUSxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDO1lBQ3JELENBQUM7UUFDSCxDQUFDO1FBQ0QsT0FBTyxPQUFPLENBQUM7SUFDakIsQ0FBQztDQUNGO0FBNzFCRCx3Q0E2MUJDIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAgUzMgYW5kIER5bmFtb0RCIGNhY2hlIGhhbmRsZXIgZm9yIE5leHQuanMgaW5jcmVtZW50YWwgY2FjaGVcbiovXG4vKiBlc2xpbnQtZGlzYWJsZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXMgKi9cbmltcG9ydCB7IHJhbmRvbVVVSUQgfSBmcm9tIFwibm9kZTpjcnlwdG9cIjtcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gXCJub2RlOmZzXCI7XG5pbXBvcnQgeyBjcmVhdGVSZXF1aXJlIH0gZnJvbSBcIm5vZGU6bW9kdWxlXCI7XG5pbXBvcnQgeyBqb2luIH0gZnJvbSBcIm5vZGU6cGF0aFwiO1xuaW1wb3J0IHtcbiAgQ2xvdWRGcm9udENsaWVudCxcbiAgQ3JlYXRlSW52YWxpZGF0aW9uQ29tbWFuZCxcbn0gZnJvbSBcIkBhd3Mtc2RrL2NsaWVudC1jbG91ZGZyb250XCI7XG5pbXBvcnQge1xuICBBdHRyaWJ1dGVWYWx1ZSxcbiAgRGVsZXRlSXRlbUNvbW1hbmQsXG4gIER5bmFtb0RCQ2xpZW50LFxuICBRdWVyeUNvbW1hbmQsXG4gIFVwZGF0ZUl0ZW1Db21tYW5kLFxufSBmcm9tIFwiQGF3cy1zZGsvY2xpZW50LWR5bmFtb2RiXCI7XG5pbXBvcnQgeyBTM0NsaWVudCwgRGVsZXRlT2JqZWN0Q29tbWFuZCB9IGZyb20gXCJAYXdzLXNkay9jbGllbnQtczNcIjtcbmltcG9ydCB7IFNTTUNsaWVudCwgR2V0UGFyYW1ldGVyQ29tbWFuZCB9IGZyb20gXCJAYXdzLXNkay9jbGllbnQtc3NtXCI7XG5pbXBvcnQgZ2V0RGVidWcgZnJvbSBcImRlYnVnXCI7XG5pbXBvcnQge1xuICBDYWNoZUhhbmRsZXIsXG4gIENhY2hlSGFuZGxlclZhbHVlLFxuICBDYWNoZUhhbmRsZXJDb250ZXh0LFxufSBmcm9tIFwibmV4dC9kaXN0L3NlcnZlci9saWIvaW5jcmVtZW50YWwtY2FjaGVcIjtcbmltcG9ydCB7XG4gIEluY3JlbWVudGFsQ2FjaGVWYWx1ZSxcbiAgR2V0SW5jcmVtZW50YWxGZXRjaENhY2hlQ29udGV4dCxcbiAgR2V0SW5jcmVtZW50YWxSZXNwb25zZUNhY2hlQ29udGV4dCxcbiAgU2V0SW5jcmVtZW50YWxGZXRjaENhY2hlQ29udGV4dCxcbiAgU2V0SW5jcmVtZW50YWxSZXNwb25zZUNhY2hlQ29udGV4dCxcbn0gZnJvbSBcIm5leHQvZGlzdC9zZXJ2ZXIvcmVzcG9uc2UtY2FjaGVcIjtcbmltcG9ydCB7XG4gIEF3c0NhY2hlQ29uZmlnLFxuICBidWlsZFMzS2V5LFxuICBDYWNoZUJ1Y2tldCxcbiAgbWFya2VyQ2xvY2ssXG4gIHJlc29sdmVBd3NDYWNoZUNvbmZpZyxcbiAgUmV2YWxpZGF0ZUR1cmF0aW9ucyxcbiAgUmV2YWxpZGF0aW9uU3RhdGUsXG4gIFRyYWNrZWRUYWdNYXJrZXJzLFxufSBmcm9tIFwiLi9hd3MtY2FjaGUtc3RvcmVcIjtcbmltcG9ydCB7XG4gIHNlcmlhbGl6ZUNhY2hlVmFsdWUsXG4gIHBhcnNlQ2FjaGVWYWx1ZSxcbiAgZ2V0VGFncyxcbiAgaGVhZGVyVGFncyxcbiAgSU5JVF9DQUNIRV9UQUdfTUFOSUZFU1QsXG59IGZyb20gXCIuL2NhY2hlLXV0aWxzXCI7XG5pbXBvcnQgeyBzaGFyZWRUYWdNYW5pZmVzdCB9IGZyb20gXCIuL3VzZS1jYWNoZS1jb21tb25cIjtcbmltcG9ydCB7IFJFVkFMSURBVEVEX1BBR0VfSE9PSyB9IGZyb20gXCIuLi9ydW50aW1lL21hbmlmZXN0XCI7XG5pbXBvcnQgeyBiYXNlUGF0aFByZWZpeCwgd2hvbGVBcHBJbnZhbGlkYXRpb25QYXRocyB9IGZyb20gXCIuLi91dGlscy9iYXNlLXBhdGhcIjtcblxuLyoqXG4gKiBUaGUgdGFncyBhIHN0b3JlZCBlbnRyeSBoYXMgdG8gYmUgdGFnLXJldmFsaWRhdGVkIGFnYWluc3QuXG4gKlxuICogQSBydW50aW1lIGBzZXRgIHJlY29yZHMgYHRhZ3NgIGFsb25nc2lkZSB0aGUgZW50cnksIGJ1dCBhIGJ1aWxkLXRpbWUgcHJlcmVuZGVyXG4gKiBkb2VzIG5vdDogdGhvc2UgZW50cmllcyBhcmUgd3JpdHRlbiBhcyBmaWxlcyBieSB0aGUgYWRhcHRlcidzXG4gKiBgb25CdWlsZENvbXBsZXRlYCByYXRoZXIgdGhhbiB0aHJvdWdoIGBzZXRgLCBhbmQgY2FycnkgdGhlaXIgdGFncyBvbmx5IGluIHRoZVxuICogcmVuZGVyJ3MgYHgtbmV4dC1jYWNoZS10YWdzYCBoZWFkZXIg4oCUIHRoZSBzYW1lIHBsYWNlIE5leHQuanMncyBvd25cbiAqIGBJbmNyZW1lbnRhbENhY2hlLmdldGAgcmVhZHMgdGhlbSBmcm9tIHdoZW4gaXQgZGVjaWRlcyBzdGFsZW5lc3MuIFdpdGhvdXQgdGhpc1xuICogZmFsbGJhY2sgbm8gcHJlcmVuZGVyZWQgcGFnZSBpcyByZWFjaGFibGUgYnkgYHJldmFsaWRhdGVUYWdgL2ByZXZhbGlkYXRlUGF0aGBcbiAqIHVudGlsIHNvbWUgbGF0ZXIgcnVudGltZSByZS1yZW5kZXIgaGFwcGVucyB0byByZXBsYWNlIHRoZSBzZWVkZWQgZW50cnksIHNvIGFuXG4gKiBhcHAgd2hvc2UgcGFnZXMgYXJlIGFsbCBzdGF0aWMgbmV2ZXIgcmV2YWxpZGF0ZXMgYXQgYWxsLiBNZWFzdXJlZCBhZ2FpbnN0XG4gKiBuZXh0LmpzJ3MgYHRlc3QvZTJlL2FwcC1kaXIvcmVzdW1lLWRhdGEtY2FjaGVgLlxuICovXG5mdW5jdGlvbiBlbnRyeVRhZ3Moc3RvcmVkOiB7IHRhZ3M/OiBzdHJpbmdbXTsgdmFsdWU/OiB1bmtub3duIH0pOiBzdHJpbmdbXSB7XG4gIHJldHVybiBzdG9yZWQudGFncz8ubGVuZ3RoID8gc3RvcmVkLnRhZ3MgOiBoZWFkZXJUYWdzKHN0b3JlZC52YWx1ZSk7XG59XG5cbi8qKlxuICogVGhlIGBsYXN0TW9kaWZpZWRgIHRoYXQgdGVsbHMgTmV4dC5qcyBcInRoaXMgZW50cnkgaXMgZXhwaXJlZCwgcmUtcmVuZGVyIGl0XG4gKiBiZWZvcmUgYW5zd2VyaW5nXCIuXG4gKlxuICogYEluY3JlbWVudGFsQ2FjaGUuZ2V0YCB0dXJucyBpdCBpbnRvIGBpc1N0YWxlOiAtMWAsIHdoaWNoIGBhcHAtcGFnZS1ydW50aW1lYFxuICogcmVhZHMgYXMgYW4gb24tZGVtYW5kIHJldmFsaWRhdGlvbiBhbmQgYW5zd2VycyB3aXRoIGEgYmxvY2tpbmcgcmVuZGVyIG9mXG4gKiAqdGhpcyogcm91dGUsIHRoZW4gc3RvcmVzIGl0LiBSZXR1cm5pbmcgYG51bGxgIGluc3RlYWQgaXMgbm90IHRoZSBzYW1lIHRoaW5nOlxuICogYSBtaXNzIG9uIGEgUFBSIHJvdXRlIHdob3NlIGBwcmVyZW5kZXItbWFuaWZlc3QuanNvbmAgZW50cnkgY2FycmllcyBhXG4gKiBgZmFsbGJhY2tgIChgY29tcHV0ZTogXCJyZXN1bWluZ1wiYCkgaXMgYW5zd2VyZWQgZnJvbSB0aGUgcm91dGUncyBmYWxsYmFja1xuICogc2hlbGwsIHdoaWNoIGlzIHNlcnZlZCBgY2FjaGUtY29udHJvbDogcHJpdmF0ZSwgbm8tc3RvcmVgIHdpdGggbm9cbiAqIGB4LW5leHRqcy1jYWNoZWAgaGVhZGVyLCBhbmQgLSB1bmxlc3MgYHBhcnRpYWxQcmVmZXRjaGluZ2AgaXMgb24gLSBpcyBuZXZlclxuICogdXBncmFkZWQgaW50byBhIGNvbmNyZXRlIGVudHJ5LiBTbyB0aGUgZmlyc3QgYHJldmFsaWRhdGVUYWdgIHRvIHJlYWNoIGFcbiAqIHNlZWRlZCBwcmVyZW5kZXIgbGVmdCB0aGF0IHBhZ2UgdW5jYWNoZWFibGUsIGFuZCBkeW5hbWljYWxseSByZXN1bWVkIHBlclxuICogcmVxdWVzdCwgZm9yIHRoZSByZXN0IG9mIHRoZSBkZXBsb3ltZW50J3MgbGlmZS4gVGhlIGlzciBlMmUgc2VlcyBpdCBhcyBhblxuICogYWJzZW50IGB4LW5leHRqcy1jYWNoZWAuXG4gKi9cbmNvbnN0IEVYUElSRURfTEFTVF9NT0RJRklFRCA9IC0xO1xuXG4vKipcbiAqIFdoZXRoZXIgYSBgZ2V0YCBpcyBmb3IgdGhlIGBmZXRjaGAgY2FjaGUgcmF0aGVyIHRoYW4gZm9yIGEgcm91dGUncyByZXNwb25zZSxcbiAqIGFzIGEgbmFycm93aW5nIHByZWRpY2F0ZSBzbyB0aGF0IGBjdHgudGFnc2AgYW5kIGBjdHguc29mdFRhZ3NgIOKAlCB3aGljaCBvbmx5XG4gKiB7QGxpbmsgR2V0SW5jcmVtZW50YWxGZXRjaENhY2hlQ29udGV4dH0gaGFzIOKAlCBhcmUgcmVhY2hhYmxlIHdpdGhvdXQgYSBjYXN0LlxuICpcbiAqIGBJbmNyZW1lbnRhbENhY2hlS2luZC5GRVRDSGAsIGNvbXBhcmVkIGFzIGl0cyBvd24gc3RyaW5nIHZhbHVlIHNvIHRoaXMgZmlsZVxuICoga2VlcHMgaW1wb3J0aW5nIG5vIE5leHQuanMgaW50ZXJuYWxzICh0aGUgZW51bSBpcyBgY29uc3RgLCBzbyBpdCBoYXMgbm9cbiAqIHJ1bnRpbWUgcmVwcmVzZW50YXRpb24gdG8gaW1wb3J0IGFueXdheSkuXG4gKi9cbmZ1bmN0aW9uIGlzRmV0Y2hDYWNoZUdldChcbiAgY3R4OiBHZXRJbmNyZW1lbnRhbEZldGNoQ2FjaGVDb250ZXh0IHwgR2V0SW5jcmVtZW50YWxSZXNwb25zZUNhY2hlQ29udGV4dCxcbik6IGN0eCBpcyBHZXRJbmNyZW1lbnRhbEZldGNoQ2FjaGVDb250ZXh0IHtcbiAgcmV0dXJuIGN0eC5raW5kID09PSBcIkZFVENIXCI7XG59XG5cbi8qKiBgTkVYVF9DQUNIRV9JTVBMSUNJVF9UQUdfSURgLCBpbmxpbmVkIHNvIHRoaXMgZmlsZSBpbXBvcnRzIG5vIE5leHQuanMgaW50ZXJuYWxzLiAqL1xuY29uc3QgTkVYVF9DQUNIRV9JTVBMSUNJVF9UQUdfSUQgPSBcIl9OX1RfXCI7XG5cbi8qKlxuICogVGhlIGB0eXBlYCBhcmd1bWVudCBvZiBgcmV2YWxpZGF0ZVBhdGhgLCB3aGljaCBOZXh0LmpzIGFwcGVuZHMgdG8gdGhlIGltcGxpY2l0XG4gKiB0YWcgYXMgYSBwYXRoIHNlZ21lbnQuIEBzZWUgaW1wbGljaXRUYWdQYXRoc1xuICovXG5jb25zdCBSRVZBTElEQVRFX1BBVEhfVFlQRVMgPSBbXCJsYXlvdXRcIiwgXCJwYWdlXCJdO1xuXG4vKipcbiAqIFRoZSByZXF1ZXN0IHBhdGhzIGEgYHJldmFsaWRhdGVQYXRoYCB0YWcgY291bGQgbmFtZSwgb3IgZW1wdHkgZm9yIGFuIGFwcCB0YWcuXG4gKlxuICogYHJldmFsaWRhdGVQYXRoKFwiL2Jsb2dcIilgIHJlYWNoZXMgdGhlIGNhY2hlIGhhbmRsZXIgYXMgdGhlIGltcGxpY2l0IHRhZ1xuICogYF9OX1RfL2Jsb2dgIC0gdGhlIHBhdGggaXMgcmlnaHQgdGhlcmUgaW4gdGhlIHRhZywgd2hpY2ggaXMgd2hhdCBtYWtlcyB0aGUgQ0ROXG4gKiBjb3B5IG9mIGEgYnVpbGQtdGltZSBwcmVyZW5kZXIgcmVhY2hhYmxlIGF0IGFsbC4gQSBgcmV2YWxpZGF0ZVRhZyhcInBvc3RzXCIpYFxuICogY2FycmllcyBubyBwYXRoIGFuZCBkZXBlbmRzIG9uIHRoZSBtYXBwaW5nIHJvd3MgaW5zdGVhZC5cbiAqXG4gKiBUd28gcGF0aHMgY29tZSBiYWNrIHdoZW4gdGhlIHRhZyBlbmRzIGluIGEgYHJldmFsaWRhdGVQYXRoYCAqdHlwZSosIGJlY2F1c2VcbiAqIGByZXZhbGlkYXRlUGF0aChwYXRoLCB0eXBlKWAgYXBwZW5kcyBpdCB0byB0aGUgdGFnXG4gKiAoYG5leHQvZGlzdC9zZXJ2ZXIvd2ViL3NwZWMtZXh0ZW5zaW9uL3JldmFsaWRhdGUuanNgKTpcbiAqIGByZXZhbGlkYXRlUGF0aChcIi9ibG9nXCIsIFwibGF5b3V0XCIpYCBpcyBgX05fVF8vYmxvZy9sYXlvdXRgLCBhbmRcbiAqIGByZXZhbGlkYXRlUGF0aChcIi9cIiwgXCJsYXlvdXRcIilgIGlzIGBfTl9UXy9sYXlvdXRgLiBSZWFkaW5nIGVpdGhlciBvZiB0aG9zZSBhcyBhXG4gKiByZXF1ZXN0IHBhdGggaW52YWxpZGF0ZXMgYSBVUkkgdGhhdCBkb2VzIG5vdCBleGlzdCBhbmQgbmV2ZXIgdG91Y2hlcyB0aGUgb25lXG4gKiB0aGF0IGRvZXMuIFRoZSBzdWZmaXggY2Fubm90IGJlIHRvbGQgYXBhcnQgZnJvbSBhIHJvdXRlIHRoYXQgZ2VudWluZWx5IGVuZHMgaW5cbiAqIGAvbGF5b3V0YCwgc28gYm90aCByZWFkaW5ncyBhcmUgZW1pdHRlZDogYW4gaW52YWxpZGF0aW9uIHBhdGggbWF0Y2hpbmcgbm90aGluZ1xuICogY29zdHMgYSBwYXRoLCB3aGlsZSBtaXNzaW5nIHRoZSByZWFsIG9uZSBsZWF2ZXMgdGhlIGVkZ2Ugc3RhbGUgZm9yIHRoZSB3aG9sZVxuICogYHMtbWF4YWdlYC5cbiAqL1xuZnVuY3Rpb24gaW1wbGljaXRUYWdQYXRocyh0YWc6IHN0cmluZyk6IHN0cmluZ1tdIHtcbiAgaWYgKCF0YWcuc3RhcnRzV2l0aChgJHtORVhUX0NBQ0hFX0lNUExJQ0lUX1RBR19JRH0vYCkpIHtcbiAgICByZXR1cm4gW107XG4gIH1cbiAgY29uc3QgcGF0aCA9IHRhZy5zbGljZShORVhUX0NBQ0hFX0lNUExJQ0lUX1RBR19JRC5sZW5ndGgpO1xuICBjb25zdCBjYW5kaWRhdGVzID0gW3BhdGhdO1xuICBmb3IgKGNvbnN0IHR5cGUgb2YgUkVWQUxJREFURV9QQVRIX1RZUEVTKSB7XG4gICAgaWYgKHBhdGguZW5kc1dpdGgoYC8ke3R5cGV9YCkpIHtcbiAgICAgIC8vIGBfTl9UXy9sYXlvdXRgIGlzIHRoZSByb290LCBub3QgdGhlIGVtcHR5IHBhdGguXG4gICAgICBjYW5kaWRhdGVzLnB1c2gocGF0aC5zbGljZSgwLCAtKHR5cGUubGVuZ3RoICsgMSkpIHx8IFwiL1wiKTtcbiAgICB9XG4gIH1cbiAgLy8gQSBkeW5hbWljIHJvdXRlIHRlbXBsYXRlIChcIi9ibG9nL1tzbHVnXVwiKSBtYXRjaGVzIG5vIGNhY2hlZCBVUkkuIEhhcm1sZXNzIHRvXG4gIC8vIHNlbmQsIGJ1dCBpdCBjb3N0cyBhbiBpbnZhbGlkYXRpb24gcGF0aCwgYW5kIHRob3NlIGFyZSBtZXRlcmVkLlxuICByZXR1cm4gQXJyYXkuZnJvbShuZXcgU2V0KGNhbmRpZGF0ZXMpKS5maWx0ZXIoKHApID0+ICFwLmluY2x1ZGVzKFwiW1wiKSk7XG59XG5cbi8qKlxuICogSW52YWxpZGF0aW9uIHBhdGhzIGNvdmVyaW5nIGV2ZXJ5IFVSSSBDbG91ZEZyb250IGNvdWxkIGJlIGhvbGRpbmcgdGhlIHJlc3BvbnNlXG4gKiBmb3IgYHJvdXRlYCB1bmRlciwgb25jZSBgYmFzZVBhdGhgIGlzIHByZWZpeGVkLlxuICpcbiAqIEFuIGludmFsaWRhdGlvbiBwYXRoIG1hdGNoZXMgb25seSB0aGUgcXVlcnkgc3RyaW5nIGl0IHNwZWxscyBvdXQsIGFuZCBhIHBhZ2Unc1xuICogUlNDIHBheWxvYWQgaXMgY2FjaGVkIHVuZGVyIGA/X3JzYz08aGFzaD5gOyBkcm9wcGluZyB0aGUgSFRNTCB3aGlsZSBsZWF2aW5nIHRoZVxuICogcGF5bG9hZCBiZWhpbmQgbGVhdmVzIHRoZSByb3V0ZXIgbmF2aWdhdGluZyB0byB0aGUgcHJlLXJldmFsaWRhdGlvbiBwYWdlLiBBXG4gKiBgdHJhaWxpbmdTbGFzaGAgYXBwIGNhY2hlcyB0aGUgcmVkaXJlY3QgdGFyZ2V0IHJhdGhlciB0aGFuIHRoZSByb3V0ZSwgc28gdGhlXG4gKiBzbGFzaCB2YXJpYW50IGhhcyB0byBnbyB0b28uXG4gKlxuICogT25lIHRyYWlsaW5nIHdpbGRjYXJkIGNvdmVycyBhbGwgZm91ciAoYC9ibG9nYCwgYC9ibG9nP+KApmAsIGAvYmxvZy9gLFxuICogYC9ibG9nLz/igKZgKSBmb3IgYSBzaW5nbGUgd2lsZGNhcmQgcGF0aCwgd2hlcmUgc3BlbGxpbmcgdGhlbSBvdXQgY29zdCB0d28gLVxuICogYW5kIHdpbGRjYXJkcyBhcmUgdGhlIHF1b3RhIHRoYXQgcnVucyBvdXQuIEl0IGFsc28gbWF0Y2hlcyBgL2Jsb2dyb2xsYCBhbmRcbiAqIGV2ZXJ5dGhpbmcgdW5kZXIgYC9ibG9nL2AsIHdoaWNoIGlzIGEgbG93ZXIgaGl0IHJhdGUgb24gdGhvc2UsIG5ldmVyIGEgc3RhbGVcbiAqIHBhZ2UuIFRoZSBhcHAncyByb290IGlzIHRoZSBleGNlcHRpb246IGAvKmAgdGhlcmUgaXMgdGhlIHdob2xlIGFwcC5cbiAqXG4gKiBgYmFzZVBhdGhgIGlzIHByZWZpeGVkIGhlcmUgYmVjYXVzZSBuZWl0aGVyIHNvdXJjZSBvZiBhIHJvdXRlIGNhcnJpZXMgaXQuXG4gKiBDYWNoZSBrZXlzIGFyZSByb3V0ZXMg4oCUIE5leHQuanMgc3RyaXBzIGBiYXNlUGF0aGAgYmVmb3JlIHJvdXRpbmcsIHNvIHRoZSBjYWNoZVxuICogaGFuZGxlciBuZXZlciBzZWVzIG9uZSDigJQgYW5kIGByZXZhbGlkYXRlUGF0aChcIi9ibG9nXCIpYCBuYW1lcyB0aGUgcm91dGUgYXNcbiAqIHdlbGwuIENsb3VkRnJvbnQgb25seSBldmVyIHNhdyBgL2Jhc2UvYmxvZ2AsIHNvIGludmFsaWRhdGluZyBgL2Jsb2dgIGNsZWFyc1xuICogbm90aGluZyBhbmQgdGhlIGVkZ2Uga2VlcHMgc2VydmluZyB0aGUgcHJlLXJldmFsaWRhdGlvbiBwYWdlIHVudGlsIGBzLW1heGFnZWBcbiAqIGV4cGlyZXMuIFRoZSBhcHAncyByb290IHVuZGVyIGEgYGJhc2VQYXRoYCBpcyBgL2Jhc2VgLCBub3QgYC9iYXNlL2AuXG4gKi9cbmZ1bmN0aW9uIGNkbkludmFsaWRhdGlvblBhdGhzKHJvdXRlOiBzdHJpbmcsIGJhc2VQYXRoOiBzdHJpbmcpOiBzdHJpbmdbXSB7XG4gIGNvbnN0IHRyaW1tZWQgPSByb3V0ZS5yZXBsYWNlKC9cXC8rJC8sIFwiXCIpO1xuICBpZiAodHJpbW1lZCA9PT0gXCJcIikge1xuICAgIHJldHVybiBiYXNlUGF0aFxuICAgICAgPyBbYmFzZVBhdGgsIGAke2Jhc2VQYXRofT8qYCwgYCR7YmFzZVBhdGh9L2AsIGAke2Jhc2VQYXRofS8/KmBdXG4gICAgICA6IFtcIi9cIiwgXCIvPypcIl07XG4gIH1cbiAgcmV0dXJuIFtgJHtiYXNlUGF0aH0ke2VuY29kZUludmFsaWRhdGlvblJvdXRlKHRyaW1tZWQpfSpgXTtcbn1cblxuLyoqXG4gKiBUaGUgYXBwJ3MgYGJhc2VQYXRoYCwgZnJvbSB0aGUgYHJlcXVpcmVkLXNlcnZlci1maWxlcy5qc29uYCBldmVyeSBkZXBsb3ltZW50XG4gKiByb290IHN0YWdlcyBuZXh0IHRvIGBzZXJ2ZXJEaXN0RGlyYDogTmV4dC5qcyBoYW5kcyB0aGUgY2FjaGUgaGFuZGxlciBub1xuICogb3RoZXIgY29weS5cbiAqL1xuZnVuY3Rpb24gYXBwQmFzZVBhdGgoc2VydmVyRGlzdERpcjogc3RyaW5nIHwgdW5kZWZpbmVkKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgaWYgKCFzZXJ2ZXJEaXN0RGlyKSB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuICB0cnkge1xuICAgIGNvbnN0IGZpbGUgPSBqb2luKHNlcnZlckRpc3REaXIsIFwiLi5cIiwgXCJyZXF1aXJlZC1zZXJ2ZXItZmlsZXMuanNvblwiKTtcbiAgICByZXR1cm4gSlNPTi5wYXJzZShyZWFkRmlsZVN5bmMoZmlsZSwgXCJ1dGY4XCIpKS5jb25maWc/LmJhc2VQYXRoO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnNvbGUud2FybihcIkNvdWxkIG5vdCByZWFkIGJhc2VQYXRoIGZvciBDRE4gaW52YWxpZGF0aW9uOlwiLCBlcnJvcik7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxufVxuXG4vKipcbiAqIGByb3V0ZWAgYXMgdGhlIFVSSSBhIGJyb3dzZXIgcmVxdWVzdHMgaXQgdW5kZXIsIHdoaWNoIGlzIHdoYXQgQ2xvdWRGcm9udFxuICogY2FjaGVzIGl0IGJ5LlxuICpcbiAqIE5leHQuanMgYnVpbGRzIGNhY2hlIGtleXMgZnJvbSB0aGUgKmRlY29kZWQqIHBhdGhuYW1lIChgZGVjb2RlUGF0aFBhcmFtc2AgaW5cbiAqIGByb3V0ZS1tb2R1bGUuanNgKSwgc28gYC9ibG9nL2jDqWxsb2Agb3IgYC9ibG9nL2hlbGxvIHdvcmxkYCByZWFjaCBoZXJlIHJhdyxcbiAqIHdoaWxlIENsb3VkRnJvbnQgaG9sZHMgYC9ibG9nL2glQzMlQTlsbG9gLCBhbmQgYW4gaW52YWxpZGF0aW9uIHBhdGggbWF0Y2hlc1xuICogdGhlIGVuY29kZWQgZm9ybSBvbmx5IC0gQVdTIHJlcXVpcmVzIG5vbi1BU0NJSSBhbmQgdW5zYWZlIGNoYXJhY3RlcnMgdG8gYmVcbiAqIHBlcmNlbnQtZW5jb2RlZCwgYW5kIG5vdGhpbmcgZWxzZS4gRWFjaCBzZWdtZW50IGlzIGVuY29kZWQgdGhlIHdheVxuICogYGVuY29kZVVSSWAgZG9lcywgd2hpY2ggbGVhdmVzIHRoZSBjaGFyYWN0ZXJzIGEgYnJvd3NlciBzZW5kcyByYXcgaW4gYSBwYXRoXG4gKiAoYEBgLCBgOmAsIGAsYCwgYDtgLCAuLi4pIGFsb25lLiBBIGAlYCB0aGF0IGFscmVhZHkgc3RhcnRzIGFuIGVzY2FwZSBpcyBrZXB0OlxuICogYGRlY29kZVBhdGhQYXJhbXNgIHJlLWVzY2FwZXMgcGF0aCBkZWxpbWl0ZXJzIChgJTJGYCwgYCUzRmAsIGAlMjNgLCBgJTVDYCksXG4gKiBhbmQgYSBgcmV2YWxpZGF0ZVBhdGhgIHRhZyBtYXkgbmFtZSB0aGUgZW5jb2RlZCBwYXRoIGl0c2VsZi4gYD9gIGFuZCBgI2AgY2FuXG4gKiBvbmx5IGJlIGluIGEgZGVjb2RlZCBzZWdtZW50IGFzIGRhdGEsIHNvIHRoZXkgYXJlIGVuY29kZWQgdG9vLlxuICovXG5mdW5jdGlvbiBlbmNvZGVJbnZhbGlkYXRpb25Sb3V0ZShyb3V0ZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIHJvdXRlXG4gICAgLnNwbGl0KFwiL1wiKVxuICAgIC5tYXAoKHNlZ21lbnQpID0+XG4gICAgICBzZWdtZW50XG4gICAgICAgIC5zcGxpdCgvKCVbMC05QS1GYS1mXXsyfSkvKVxuICAgICAgICAubWFwKChwYXJ0LCBpKSA9PlxuICAgICAgICAgIGkgJSAyID09PSAxXG4gICAgICAgICAgICA/IHBhcnRcbiAgICAgICAgICAgIDogZW5jb2RlVVJJKHBhcnQpLnJlcGxhY2UoXG4gICAgICAgICAgICAgICAgL1s/I10vZyxcbiAgICAgICAgICAgICAgICAoYykgPT4gYCUke2MuY2hhckNvZGVBdCgwKS50b1N0cmluZygxNikudG9VcHBlckNhc2UoKX1gLFxuICAgICAgICAgICAgICApLFxuICAgICAgICApXG4gICAgICAgIC5qb2luKFwiXCIpLFxuICAgIClcbiAgICAuam9pbihcIi9cIik7XG59XG5cbi8qKlxuICogV2hldGhlciBhIGNhY2hlIGtleSBpcyBhIGZldGNoLWNhY2hlIGVudHJ5IHJhdGhlciB0aGFuIGEgcGFnZSByb3V0ZS5cbiAqXG4gKiBUaG9zZSBrZXlzIGFyZSB0aGUgaGFzaCBOZXh0LmpzIGRlcml2ZXMgZnJvbSB0aGUgcmVxdWVzdCDigJQgYSBzaW5nbGUgc2VnbWVudCBvZlxuICogbG93ZXJjYXNlIGhleCwgbG9uZyBlbm91Z2ggdGhhdCBubyByb3V0ZSBwYXRobmFtZSB0YWtlcyB0aGUgc2FtZSBzaGFwZS4gVGhleVxuICogYXJlIG1hcHBlZCB0byB0YWdzIGV4YWN0bHkgbGlrZSBwYWdlcyBhcmUsIGJ1dCBuYW1lIG5vIFVSSSBDbG91ZEZyb250IGNvdWxkIGJlXG4gKiBob2xkaW5nLCBzbyBhbiBpbnZhbGlkYXRpb24gcGF0aCBidWlsdCBmcm9tIG9uZSBpcyBhbHdheXMgd2FzdGVkLlxuICovXG5mdW5jdGlvbiBpc0ZldGNoQ2FjaGVLZXkoa2V5OiBzdHJpbmcpOiBib29sZWFuIHtcbiAgcmV0dXJuIC9eWzAtOWEtZl17MzIsfSQvLnRlc3Qoa2V5KTtcbn1cblxuLyoqIFdoZXRoZXIgQ2xvdWRGcm9udCBjb3VudHMgdGhpcyBwYXRoIGFnYWluc3QgdGhlIHdpbGRjYXJkIHF1b3RhLiAqL1xuZnVuY3Rpb24gaXNXaWxkY2FyZFBhdGgocGF0aDogc3RyaW5nKTogYm9vbGVhbiB7XG4gIHJldHVybiBwYXRoLmluY2x1ZGVzKFwiKlwiKTtcbn1cblxuLyoqXG4gKiBUaGUgbW9zdCB3aWxkY2FyZCBwYXRocyBvbmUgYHJldmFsaWRhdGVUYWdgIHNlbmRzIGJlZm9yZSBjb2xsYXBzaW5nIHRvIGEgc2luZ2xlXG4gKiBhcHAtd2lkZSB3aWxkY2FyZCwgYW5kIHRoZSBtb3N0IHBhdGhzIG9mIGFueSBraW5kLlxuICpcbiAqIENsb3VkRnJvbnQncyB3aWxkY2FyZCBxdW90YSBpcyBwZXIgKmRpc3RyaWJ1dGlvbiosIG5vdCBwZXIgcmVxdWVzdDogdGhlXG4gKiBpbnZhbGlkYXRpb25zIGl0IGhhcyBpbiBwcm9ncmVzcyBzaGFyZSBpdCwgc28gc3BsaXR0aW5nIGFuIG92ZXJzaXplZCBzZXQgaW50b1xuICogYmFjay10by1iYWNrIHJlcXVlc3RzIGlzIHdoYXQgZ2V0cyB0aGUgbGF0ZXIgb25lcyByZWplY3RlZFxuICogKGBUb29NYW55SW52YWxpZGF0aW9uc0luUHJvZ3Jlc3NgLCBvciBhIHRocm90dGxlIHVuZGVyIHRoZSBuZXdlciByYXRlLWJhc2VkXG4gKiBxdW90YXMpIC0gYW5kIGEgcmVqZWN0ZWQgcmVxdWVzdCBpcyBhIHBhZ2UgdGhhdCBzdGF5cyBzdGFsZSBhdCB0aGUgZWRnZS4gU29cbiAqIGV2ZXJ5dGhpbmcgZ29lcyBvdXQgYXMgb25lIHJlcXVlc3QsIGFuZCBhIHNldCB0aGF0IHdvdWxkIG5vdCBmaXQgaW4gb25lIGlzXG4gKiBhbnN3ZXJlZCB3aXRoIHRoZSB3aG9sZSBhcHAgKGAvKmAsIHNlZSBgd2hvbGVBcHBJbnZhbGlkYXRpb25QYXRoc2ApIGluc3RlYWQ6XG4gKiBhIHdvcnNlIGhpdCByYXRlLCBhbmQgYSBzdHJpY3RseSBjb3JyZWN0IGFuc3dlci5cbiAqIEBzZWUgaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL0FtYXpvbkNsb3VkRnJvbnQvbGF0ZXN0L0RldmVsb3Blckd1aWRlL2Nsb3VkZnJvbnQtbGltaXRzLmh0bWwjbGltaXRzLWludmFsaWRhdGlvbnNcbiAqL1xuY29uc3QgTUFYX1dJTERDQVJEX1BBVEhTX1BFUl9JTlZBTElEQVRJT04gPSAxNTtcblxuY29uc3QgTUFYX1BBVEhTX1BFUl9JTlZBTElEQVRJT04gPSAzMDAwO1xuXG4vKipcbiAqIFJldHJpZXMgb2YgYW4gaW52YWxpZGF0aW9uIENsb3VkRnJvbnQgcmVqZWN0ZWQgZm9yIGl0cyBpbi1wcm9ncmVzcyBxdW90YSwgYW5kXG4gKiB0aGUgZGVsYXkgYmVmb3JlIHRoZSBmaXJzdDsgZWFjaCBsYXRlciBvbmUgd2FpdHMgdHdpY2UgYXMgbG9uZy5cbiAqXG4gKiBTaXppbmcgYSByZXF1ZXN0IGFnYWluc3QgdGhlIHF1b3RhIGNhbm5vdCBzZWUgdGhlIGludmFsaWRhdGlvbnMgb3RoZXJcbiAqIGluc3RhbmNlcyBhbHJlYWR5IGhhdmUgaW4gZmxpZ2h0LCBzbyB0d28gYHJldmFsaWRhdGVUYWdgcyBjbG9zZSB0b2dldGhlciBjYW5cbiAqIGVhY2ggZml0IG9uIHRoZWlyIG93biBhbmQgc3RpbGwgYmUgcmVqZWN0ZWQgdG9nZXRoZXIuIERyb3BwaW5nIHRoZSByZWplY3RlZFxuICogb25lIGxlZnQgaXRzIHBhZ2VzIHN0YWxlIGF0IHRoZSBlZGdlIGZvciB0aGUgd2hvbGUgYHMtbWF4YWdlYC4gVGhlIHJldHJ5IGFza3NcbiAqIGZvciB0aGUgd2hvbGUgYXBwIGluc3RlYWQsIHdoaWNoIGlzIG9uZSB3aWxkY2FyZCAtIHRoZSBsZWFzdCBvZiB0aGUgcXVvdGEgYW55XG4gKiByZXF1ZXN0IGNhbiB0YWtlIC0gYXQgdGhlIGNvc3Qgb2YgYSBjb2xkZXIgZWRnZS5cbiAqL1xuY29uc3QgSU5WQUxJREFUSU9OX1JFVFJJRVMgPSAyO1xuY29uc3QgSU5WQUxJREFUSU9OX1JFVFJZX0RFTEFZX01TID0gMTAwMDtcblxuLyoqIFRoZSBlcnJvcnMgdGhhdCBtZWFuIFwidGhlIHF1b3RhIGlzIGZ1bGwgcmlnaHQgbm93XCIsIG5vdCBcInRoaXMgaXMgd3JvbmdcIi4gKi9cbmZ1bmN0aW9uIGlzSW52YWxpZGF0aW9uUXVvdGFFcnJvcihlcnJvcjogdW5rbm93bik6IGJvb2xlYW4ge1xuICBjb25zdCBuYW1lID0gKGVycm9yIGFzIHsgbmFtZT86IHVua25vd24gfSB8IG51bGwpPy5uYW1lO1xuICByZXR1cm4gbmFtZSA9PT0gXCJUb29NYW55SW52YWxpZGF0aW9uc0luUHJvZ3Jlc3NcIiB8fCBuYW1lID09PSBcIlRocm90dGxpbmdcIjtcbn1cblxuLyoqXG4gKiBUaGUgbW9zdCBjYWNoZSBrZXlzIHRvIHJlbWVtYmVyIGFzIHNlcnZlZCBzdGFsZSB3aGlsZSBhd2FpdGluZyB0aGVpclxuICogcmVnZW5lcmF0aW9uLiBBIGJhY2tncm91bmQgcmVuZGVyIHRoYXQgZmFpbHMgbmV2ZXIgY2FsbHMgYHNldGAsIHNvIHdpdGhvdXQgYVxuICogYm91bmQgaXRzIGtleSB3b3VsZCBzdGF5IGJlaGluZCBmb3IgdGhlIGxpZmUgb2YgdGhlIGluc3RhbmNlLlxuICogQHNlZSBTM0NhY2hlSGFuZGxlci5zb2Z0UmV2YWxpZGF0ZWRLZXlzXG4gKi9cbmNvbnN0IE1BWF9TT0ZUX1JFVkFMSURBVEVEX0tFWVMgPSAxMDAwO1xuXG4vKipcbiAqIEhvdyBtYW55IDEgTUIgUXVlcnkgcGFnZXMgb2Ygb25lIHRhZydzIG1hcHBpbmcgcm93cyB0byB3YWxrLiBBIGNlaWxpbmcgcmF0aGVyXG4gKiB0aGFuIGEgcmVhbCBsaW1pdDogaXQgYm91bmRzIGEgcnVuYXdheSB0YWcgaW5zdGVhZCBvZiBwYWdpbmF0aW5nIGEgd2hvbGUgdGFibGVcbiAqIG9uIGEgcmVxdWVzdCBwYXRoLiBBIHRhZyBjdXQgb2ZmIGhlcmUgaW52YWxpZGF0ZXMgdGhlIHdob2xlIGFwcCwgYmVjYXVzZSB0aGVcbiAqIHJvd3MgaXQgbmV2ZXIgcmVhZCBhcmUgcGFnZXMgd2hvc2UgZWRnZSBjb3B5IHdvdWxkIG90aGVyd2lzZSBzdGF5IHN0YWxlLlxuICovXG5jb25zdCBNQVhfVEFHX1FVRVJZX1BBR0VTID0gMjA7XG5cbi8qKlxuICogTmV4dC5qcydzIGluLXByb2Nlc3MgdGFnIG1hbmlmZXN0LCB3aGljaCBgSW5jcmVtZW50YWxDYWNoZS5nZXRgIGNvbnN1bHRzIHRvXG4gKiBkZWNpZGUgd2hldGhlciBhbiBlbnRyeSBpcyBzdGFsZSAtIG9yIGB1bmRlZmluZWRgIHdoZW4gaXQgY2Fubm90IGJlIGZvdW5kXG4gKiB1bmFtYmlndW91c2x5LlxuICpcbiAqIGByZXZhbGlkYXRlVGFnKHRhZywgcHJvZmlsZSlgIGFza3MgZm9yIHN0YWxlLXdoaWxlLXJldmFsaWRhdGU6IHRoZSBlbnRyeSBpc1xuICogc2VydmVkIG9uY2UgbW9yZSB3aGlsZSBhIGJhY2tncm91bmQgcmVuZGVyIHJlcGxhY2VzIGl0LiBBIGNhY2hlIGhhbmRsZXIgaGFzXG4gKiBubyBmaWVsZCB0byBzYXkgXCJzdGFsZVwiIGluIC0gYGxhc3RNb2RpZmllZDogLTFgIG1lYW5zIGV4cGlyZWQsIHdoaWNoIGJsb2NrcyAtXG4gKiBzbyB0aGlzIHNldHMgdGhlIHRhZydzIGBzdGFsZWAgdGltZXN0YW1wIHdoZXJlIE5leHQuanMgYWxyZWFkeSBsb29rcyBmb3IgaXQsXG4gKiB3aGljaCBpcyBleGFjdGx5IHdoYXQgaXRzIG93biBgRmlsZVN5c3RlbUNhY2hlLnJldmFsaWRhdGVUYWdgIGRvZXMuIFRoZSBtb2R1bGVcbiAqIGlzIHJlYWQgZnJvbSB0aGUgbG9hZGVkLW1vZHVsZSBjYWNoZSByYXRoZXIgdGhhbiBgcmVxdWlyZWBkOiB0aGUgaGFuZGxlciBpc1xuICogYnVuZGxlZCBhcGFydCBmcm9tIHRoZSBhcHAsIGFuZCByZXNvbHZpbmcgYG5leHRgIGZyb20gaGVyZSBjb3VsZCByZWFjaCBhXG4gKiBkaWZmZXJlbnQgY29weSB0aGFuIHRoZSBzZXJ2ZXIgbG9hZGVkLCB3aG9zZSBtYW5pZmVzdCBub3RoaW5nIHJlYWRzLiBBIHN0YWxlXG4gKiBtYXJrIHdyaXR0ZW4gdGhlcmUgd291bGQgc2VydmUgdGhlIGVudHJ5IGFzIGZyZXNoLCBzbyB3aXRob3V0IGV4YWN0bHkgb25lXG4gKiBsb2FkZWQgY29weSB0aGUgY2FsbGVyIGZhbGxzIGJhY2sgdG8gZXhwaXJpbmcgdGhlIGVudHJ5IG91dHJpZ2h0LlxuICovXG5jb25zdCBUQUdTX01BTklGRVNUX01PRFVMRSA9XG4gIFwiL25leHQvZGlzdC9zZXJ2ZXIvbGliL2luY3JlbWVudGFsLWNhY2hlL3RhZ3MtbWFuaWZlc3QuZXh0ZXJuYWwuanNcIjtcbmZ1bmN0aW9uIG5leHRUYWdzTWFuaWZlc3QoKTpcbiAgTWFwPHN0cmluZywgeyBzdGFsZT86IG51bWJlcjsgZXhwaXJlZD86IG51bWJlciB9PiB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IGNhY2hlID0gY3JlYXRlUmVxdWlyZShqb2luKHByb2Nlc3MuY3dkKCksIFwiaW5kZXguanNcIikpLmNhY2hlO1xuICBjb25zdCBsb2FkZWQgPSBPYmplY3Qua2V5cyhjYWNoZSkuZmlsdGVyKChwYXRoKSA9PlxuICAgIHBhdGgucmVwbGFjZSgvXFxcXC9nLCBcIi9cIikuZW5kc1dpdGgoVEFHU19NQU5JRkVTVF9NT0RVTEUpLFxuICApO1xuICBpZiAobG9hZGVkLmxlbmd0aCAhPT0gMSkge1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbiAgY29uc3QgbWFuaWZlc3QgPSBjYWNoZVtsb2FkZWRbMF1dPy5leHBvcnRzPy50YWdzTWFuaWZlc3Q7XG4gIHJldHVybiBtYW5pZmVzdCBpbnN0YW5jZW9mIE1hcCA/IG1hbmlmZXN0IDogdW5kZWZpbmVkO1xufVxuXG5pbnRlcmZhY2UgQ2xvdWRGcm9udEludmFsaWRhdGlvbkNvbmZpZyB7XG4gIC8qKlxuICAgKiBUaGUgZGlzdHJpYnV0aW9uIElELCB3aGVuIHRoZSBjb21wdXRlJ3MgZW52aXJvbm1lbnQgY2FuIGNhcnJ5IGl0XG4gICAqIChgTmV4dGpzR2xvYmFsQ29udGFpbmVyc2ApLlxuICAgKi9cbiAgZGlzdHJpYnV0aW9uSWQ6IHN0cmluZztcbiAgLyoqXG4gICAqIE90aGVyd2lzZSwgdGhlIG5hbWUgKG5vdCB2YWx1ZSkgb2YgdGhlIFNTTSBQYXJhbWV0ZXIgaG9sZGluZyBpdC5cbiAgICpcbiAgICogT24gYE5leHRqc0dsb2JhbEZ1bmN0aW9uc2AgdGhlIGRpc3RyaWJ1dGlvbidzIG9yaWdpbiBpcyB0aGUgZnVuY3Rpb24nc1xuICAgKiBVUkwsIHNvIHRoZSBmdW5jdGlvbidzIGVudmlyb25tZW50IG5hbWluZyB0aGUgZGlzdHJpYnV0aW9uIGJhY2sgd291bGQgYmVcbiAgICogYSBjaXJjdWxhciBDbG91ZEZvcm1hdGlvbiBkZXBlbmRlbmN5LiBUaGUgcGFyYW1ldGVyICpuYW1lKiBpcyBzdGF0aWMgYW5kXG4gICAqIGtub3duIGF0IHN5bnRoIHRpbWU7IG9ubHkgaXRzICp2YWx1ZSogZGVwZW5kcyBvbiB0aGUgZGlzdHJpYnV0aW9uLlxuICAgKi9cbiAgZGlzdHJpYnV0aW9uSWRQYXJhbWV0ZXJOYW1lOiBzdHJpbmc7XG4gIC8qKlxuICAgKiBUaGUgYXBwJ3MgYGJhc2VQYXRoYCwgYXMgdGhlIFVSSSBwcmVmaXggQ2xvdWRGcm9udCBjYWNoZWQgdGhlIHJlc3BvbnNlc1xuICAgKiB1bmRlci5cbiAgICogQHNlZSBjZG5JbnZhbGlkYXRpb25QYXRoc1xuICAgKi9cbiAgYmFzZVBhdGg6IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBTM0NhY2hlSGFuZGxlck9wdGlvbnMge1xuICBjb250ZXh0OiBDYWNoZUhhbmRsZXJDb250ZXh0O1xuICAvKipcbiAgICogVGhlIGFwcCdzIGBiYXNlUGF0aGAsIGZvciB0ZXN0czogcmVhZCBmcm9tIGByZXF1aXJlZC1zZXJ2ZXItZmlsZXMuanNvbmBcbiAgICogb3RoZXJ3aXNlLlxuICAgKi9cbiAgYmFzZVBhdGg/OiBzdHJpbmc7XG59XG5cbmV4cG9ydCBjbGFzcyBTM0NhY2hlSGFuZGxlciBpbXBsZW1lbnRzIENhY2hlSGFuZGxlciB7XG4gIHByaXZhdGUgczNDbGllbnQ6IFMzQ2xpZW50O1xuICBwcml2YXRlIGR5bmFtb0NsaWVudDogRHluYW1vREJDbGllbnQ7XG4gIHByaXZhdGUgY2xvdWRGcm9udENsaWVudDogQ2xvdWRGcm9udENsaWVudDtcbiAgcHJpdmF0ZSBzc21DbGllbnQ6IFNTTUNsaWVudDtcbiAgcHJpdmF0ZSBidWNrZXQ6IENhY2hlQnVja2V0O1xuICBwcml2YXRlIHRhZ3M6IFRyYWNrZWRUYWdNYXJrZXJzO1xuICBwcml2YXRlIGNvbmZpZzogQXdzQ2FjaGVDb25maWc7XG4gIHByaXZhdGUgY2xvdWRGcm9udENvbmZpZzogQ2xvdWRGcm9udEludmFsaWRhdGlvbkNvbmZpZztcbiAgcHJpdmF0ZSBkZWJ1ZyA9IGdldERlYnVnKFwiY2RrLW5leHRqczpjYWNoZS1oYW5kbGVyOnMzXCIpO1xuXG4gIC8vIENhY2hlZCBmb3IgdGhlIGxpZmV0aW1lIG9mIHRoaXMgaW5zdGFuY2UgKGkuZS4gdGhlIGNvbXB1dGUgaW5zdGFuY2UpIG9uY2VcbiAgLy8gcmVzb2x2ZWQsIHNpbmNlIGEgZGVwbG95bWVudCdzIGRpc3RyaWJ1dGlvbiBJRCBuZXZlciBjaGFuZ2VzIGF0IHJ1bnRpbWUuXG4gIHByaXZhdGUgY2FjaGVkRGlzdHJpYnV0aW9uSWQ6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuXG4gIC8qKlxuICAgKiBDYWNoZSBrZXlzIHRoaXMgaW5zdGFuY2Ugc2VydmVkIHN0YWxlIGJlY2F1c2Ugb2YgYSBzb2Z0IGByZXZhbGlkYXRlVGFnYCxcbiAgICogd2hvc2UgcmVnZW5lcmF0ZWQgZW50cnkgYHNldGAgaGFzIHlldCB0byB3cml0ZS5cbiAgICpcbiAgICogYHJldmFsaWRhdGVUYWcodGFnLCBwcm9maWxlKWAgaW52YWxpZGF0ZXMgQ2xvdWRGcm9udCBhdCBvbmNlLCBidXQgdGhlIG5leHRcbiAgICogcmVxdWVzdCBpcyBhbnN3ZXJlZCBmcm9tIHRoZSBzdGFsZSBlbnRyeSAtIHN0YWxlLXdoaWxlLXJldmFsaWRhdGUgLSBhbmQgdGhhdFxuICAgKiByZXNwb25zZSBjYXJyaWVzIHRoZSBlbnRyeSdzIG9yaWdpbmFsIGBzLW1heGFnZWAsIHNvIHRoZSBlZGdlIGNhY2hlcyB0aGVcbiAgICogc3RhbGUgYm9keSBhZ2Fpbi4gVGhlIGJhY2tncm91bmQgcmVuZGVyIHRoYXQgcmVwbGFjZXMgaXQgcnVucyBpbiB0aGlzIHNhbWVcbiAgICogcHJvY2Vzcywgc28gYHNldGAgY2hlY2tzIHRoaXMgc2V0IGFuZCBpbnZhbGlkYXRlcyB0aGUga2V5J3MgcGF0aHMgYSBzZWNvbmRcbiAgICogdGltZSBvbmNlIHRoZSBmcmVzaCBlbnRyeSBpcyBpbiBTMy4gV2l0aG91dCB0aGF0LCB0aGUgZWRnZSBrZXB0IHNlcnZpbmcgdGhlXG4gICAqIHByZS1yZXZhbGlkYXRpb24gcGFnZSBmb3IgdGhlIHdob2xlIGBzLW1heGFnZWAuXG4gICAqXG4gICAqIEV4cGlyaW5nIHNvZnQtcmV2YWxpZGF0ZWQgZW50cmllcyBvdXRyaWdodCBpbnN0ZWFkIHdvdWxkIGFsc28ga2VlcCB0aGUgZWRnZVxuICAgKiByaWdodCwgYnV0IGl0IHR1cm5zIGV2ZXJ5IGByZXZhbGlkYXRlVGFnKHRhZywgXCJtYXhcIilgIGludG8gYSBibG9ja2luZyByZW5kZXJcbiAgICogYXQgdGhlIG9yaWdpbiwgd2hpY2ggaXMgdGhlIHRoaW5nIGEgcHJvZmlsZSBhc2tzIG5vdCB0byBoYXBwZW4uXG4gICAqL1xuICBwcml2YXRlIHNvZnRSZXZhbGlkYXRlZEtleXMgPSBuZXcgU2V0PHN0cmluZz4oKTtcblxuICAvKiogQHNlZSBidWlsZFRhZ01hbmlmZXN0ICovXG4gIHByaXZhdGUgYnVpbGRUYWdzOiBQcm9taXNlPE1hcDxzdHJpbmcsIHN0cmluZ1tdPj4gfCB1bmRlZmluZWQ7XG5cbiAgY29uc3RydWN0b3Iob3B0aW9uczogUzNDYWNoZUhhbmRsZXJPcHRpb25zKSB7XG4gICAgdGhpcy5jb25maWcgPSByZXNvbHZlQXdzQ2FjaGVDb25maWcoKTtcblxuICAgIC8vIE9ubHkgc2V0IGZvciBDbG91ZEZyb250LWZyb250ZWQgZGVwbG95bWVudHMgKE5leHRqc0dsb2JhbEZ1bmN0aW9ucy9Db250YWluZXJzKS5cbiAgICAvLyBXaGVuIHVuc2V0LCBvbi1kZW1hbmQgcmV2YWxpZGF0aW9uIHNraXBzIENETiBpbnZhbGlkYXRpb24gYW5kIHJlbGllcyBvbiB0aGVcbiAgICAvLyBkaXN0cmlidXRpb24ncyBjYWNoZSBwb2xpY3kgVFRMIChkcml2ZW4gYnkgdGhlIG9yaWdpbidzIENhY2hlLUNvbnRyb2wgaGVhZGVyKVxuICAgIC8vIHRvIGV2ZW50dWFsbHkgcGljayB1cCBmcmVzaCBjb250ZW50LlxuICAgIHRoaXMuY2xvdWRGcm9udENvbmZpZyA9IHtcbiAgICAgIGRpc3RyaWJ1dGlvbklkOiBwcm9jZXNzLmVudi5DREtfTkVYVEpTX0RJU1RSSUJVVElPTl9JRCB8fCBcIlwiLFxuICAgICAgZGlzdHJpYnV0aW9uSWRQYXJhbWV0ZXJOYW1lOlxuICAgICAgICBwcm9jZXNzLmVudi5DREtfTkVYVEpTX0RJU1RSSUJVVElPTl9JRF9QQVJBTV9OQU1FIHx8IFwiXCIsXG4gICAgICBiYXNlUGF0aDogYmFzZVBhdGhQcmVmaXgob3B0aW9ucy5iYXNlUGF0aCksXG4gICAgfTtcbiAgICAvLyBPbmx5IGludmFsaWRhdGlvbiBuZWVkcyBpdCwgc28gb25seSBhIGhhbmRsZXIgYmVoaW5kIGEgZGlzdHJpYnV0aW9uIHJlYWRzXG4gICAgLy8gaXQsIGFuZCBhIG1pc3NpbmcgZmlsZSB0aGVyZSBpcyB3b3J0aCBhIHdhcm5pbmcuXG4gICAgaWYgKHRoaXMuaW52YWxpZGF0ZXNDZG4gJiYgb3B0aW9ucy5iYXNlUGF0aCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aGlzLmNsb3VkRnJvbnRDb25maWcuYmFzZVBhdGggPSBiYXNlUGF0aFByZWZpeChcbiAgICAgICAgYXBwQmFzZVBhdGgob3B0aW9ucy5jb250ZXh0LnNlcnZlckRpc3REaXIpLFxuICAgICAgKTtcbiAgICB9XG5cbiAgICAvLyBJbml0aWFsaXplIEFXUyBjbGllbnRzXG4gICAgY29uc3QgeyByZWdpb24gfSA9IHRoaXMuY29uZmlnO1xuICAgIHRoaXMuczNDbGllbnQgPSBuZXcgUzNDbGllbnQoeyByZWdpb24gfSk7XG4gICAgdGhpcy5keW5hbW9DbGllbnQgPSBuZXcgRHluYW1vREJDbGllbnQoeyByZWdpb24gfSk7XG4gICAgdGhpcy5jbG91ZEZyb250Q2xpZW50ID0gbmV3IENsb3VkRnJvbnRDbGllbnQoeyByZWdpb24gfSk7XG4gICAgdGhpcy5zc21DbGllbnQgPSBuZXcgU1NNQ2xpZW50KHsgcmVnaW9uIH0pO1xuICAgIHRoaXMuYnVja2V0ID0gbmV3IENhY2hlQnVja2V0KHRoaXMuczNDbGllbnQsIHRoaXMuY29uZmlnLmJ1Y2tldE5hbWUpO1xuICAgIC8vIFRoZSBwcm9jZXNzJ3Mgb25lIGNvcHkgb2YgdGhlIHRhZyBtYXJrZXJzLCBzaGFyZWQgd2l0aCB0aGUgYCd1c2UgY2FjaGUnYFxuICAgIC8vIGhhbmRsZXJzOiBvbmUgYHJldmFsaWRhdGVUYWdgIHdyaXRlcyBlYWNoIHRhZydzIHJvd3Mgb25jZSwgYW5kIG9uZSBsb2dcbiAgICAvLyBgUXVlcnlgIHBlciBpbnRlcnZhbCBzZXJ2ZXMgZXZlcnkgaGFuZGxlci5cbiAgICB0aGlzLnRhZ3MgPSBzaGFyZWRUYWdNYW5pZmVzdCgpO1xuXG4gICAgLy8gYHJlcy5yZXZhbGlkYXRlKClgIHJlZ2VuZXJhdGVzIGEgcGFnZSB3aXRob3V0IGdvaW5nIHRocm91Z2hcbiAgICAvLyBgcmV2YWxpZGF0ZVRhZ2AsIHNvIHRoZSBydW50aW1lIGFza3MgZm9yIHRoZSBDRE4gY29waWVzIGl0c2VsZiwgdGhyb3VnaFxuICAgIC8vIHRoaXMgaG9vayAoYGludmFsaWRhdGVSZXZhbGlkYXRlZFBhZ2VgIGluIGBzcmMvcnVudGltZS9jb3JlLnRzYCkuIEEgZ2xvYmFsXG4gICAgLy8gYmVjYXVzZSB0aGUgcnVudGltZSBhbmQgdGhpcyBoYW5kbGVyIGFyZSBzZXBhcmF0ZSBidW5kbGVzIGluIG9uZSBwcm9jZXNzLlxuICAgIC8vIEV2ZXJ5IGluc3RhbmNlIHJlZ2lzdGVycyB0aGUgc2FtZSB0aGluZywgc28gdGhlIGxhdGVzdCBvbmUgd2lubmluZyBpcyBmaW5lLlxuICAgIGlmICh0aGlzLmludmFsaWRhdGVzQ2RuKSB7XG4gICAgICAoZ2xvYmFsVGhpcyBhcyBSZWNvcmQ8c3ltYm9sLCB1bmtub3duPilbUkVWQUxJREFURURfUEFHRV9IT09LXSA9IChcbiAgICAgICAgcm91dGVzOiByZWFkb25seSBzdHJpbmdbXSxcbiAgICAgICk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgICAgICBjb25zdCB7IGJhc2VQYXRoIH0gPSB0aGlzLmNsb3VkRnJvbnRDb25maWc7XG4gICAgICAgIHJldHVybiB0aGlzLmludmFsaWRhdGVDbG91ZEZyb250UGF0aHMoXG4gICAgICAgICAgcm91dGVzLmZsYXRNYXAoKHJvdXRlKSA9PiBjZG5JbnZhbGlkYXRpb25QYXRocyhyb3V0ZSwgYmFzZVBhdGgpKSxcbiAgICAgICAgKTtcbiAgICAgIH07XG4gICAgfVxuXG4gICAgaWYgKCF0aGlzLmNvbmZpZy5idWNrZXROYW1lKSB7XG4gICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgIFwiQ0RLX05FWFRKU19DQUNIRV9CVUNLRVRfTkFNRSBlbnZpcm9ubWVudCB2YXJpYWJsZSBub3Qgc2V0LCBTMyBjYWNoZSBkaXNhYmxlZFwiLFxuICAgICAgKTtcbiAgICB9XG5cbiAgICBpZiAoIXRoaXMuY29uZmlnLnRhYmxlTmFtZSkge1xuICAgICAgY29uc29sZS53YXJuKFxuICAgICAgICBcIkNES19ORVhUSlNfUkVWQUxJREFUSU9OX1RBQkxFX05BTUUgZW52aXJvbm1lbnQgdmFyaWFibGUgbm90IHNldCwgcmV2YWxpZGF0aW9uIHRyYWNraW5nIGRpc2FibGVkXCIsXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmICghcHJvY2Vzcy5lbnYuQ0RLX05FWFRKU19CVUlMRF9JRCkge1xuICAgICAgY29uc29sZS53YXJuKFxuICAgICAgICBcIkNES19ORVhUSlNfQlVJTERfSUQgZW52aXJvbm1lbnQgdmFyaWFibGUgbm90IHNldCwgY2FjaGUgaXNvbGF0aW9uIG1heSBub3Qgd29yayBjb3JyZWN0bHlcIixcbiAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgZ2V0KFxuICAgIGNhY2hlS2V5OiBzdHJpbmcsXG4gICAgY3R4OiBHZXRJbmNyZW1lbnRhbEZldGNoQ2FjaGVDb250ZXh0IHwgR2V0SW5jcmVtZW50YWxSZXNwb25zZUNhY2hlQ29udGV4dCxcbiAgKTogUHJvbWlzZTxDYWNoZUhhbmRsZXJWYWx1ZSB8IG51bGw+IHtcbiAgICB0cnkge1xuICAgICAgaWYgKCF0aGlzLmNvbmZpZy5idWNrZXROYW1lKSB7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBzM0tleSA9IHRoaXMuYnVpbGRTM0tleShjYWNoZUtleSk7XG5cbiAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgdGhpcy5idWNrZXQuZ2V0KHMzS2V5KTtcblxuICAgICAgaWYgKCFyZXNwb25zZSkge1xuICAgICAgICB0aGlzLmRlYnVnKGBTMyBDQUNIRSBNSVNTOiAke2NhY2hlS2V5fWApO1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH1cblxuICAgICAgLy8gRXZlcnkgb2JqZWN0IGlzIEpTT04gKGBwdXRKc29uYCwgYW5kIHRoZSBzZWVkZWQgYC5qc29uYCBmaWxlcyk7IG9uZVxuICAgICAgLy8gdGhhdCBkb2VzIG5vdCBwYXJzZSBpcyBhIG1pc3MsIHRocm91Z2ggdGhlIGBjYXRjaGAgYmVsb3cuXG4gICAgICBjb25zdCBwYXJzZWRWYWx1ZSA9IHBhcnNlQ2FjaGVWYWx1ZShyZXNwb25zZSk7XG5cbiAgICAgIGlmIChhd2FpdCB0aGlzLmlzUmV2YWxpZGF0ZWQocGFyc2VkVmFsdWUsIGN0eCwgY2FjaGVLZXkpKSB7XG4gICAgICAgIHRoaXMuZGVidWcoYFMzIENBQ0hFIElOVkFMSURBVEVEIEJZIFRBRzogJHtjYWNoZUtleX1gKTtcbiAgICAgICAgLy8gQSByZXZhbGlkYXRlZCBgZmV0Y2hgIGVudHJ5IGhhcyB0byByZWFkIGFzIGEgbWlzcyBzbyB0aGUgcmVxdWVzdFxuICAgICAgICAvLyByZWZldGNoZXMgaW5zdGVhZCBvZiByZXVzaW5nIHRoZSBib2R5IC0gdGhlIHNhbWUgdGhpbmcgTmV4dC5qcydzXG4gICAgICAgIC8vIG93biBgRmlsZVN5c3RlbUNhY2hlYCBkb2VzIHdpdGggYHJldmFsaWRhdGVkVGFnc2AuIE9ubHkgYVxuICAgICAgICAvLyAqcmVzcG9uc2UqIGVudHJ5IGdldHMgaGFuZGVkIGJhY2sgZXhwaXJlZCwgYmVjYXVzZSBmb3IgdGhvc2UgYVxuICAgICAgICAvLyBtaXNzIGlzIHdvcnNlIHRoYW4gc3RhbGU6IHNlZSBgRVhQSVJFRF9MQVNUX01PRElGSUVEYC5cbiAgICAgICAgLy9cbiAgICAgICAgLy8gTmVpdGhlciBpcyBkZWxldGVkLiBUaGUgcmVmZXRjaCBvciByZS1yZW5kZXIgdGhpcyBwcm92b2tlc1xuICAgICAgICAvLyBvdmVyd3JpdGVzIHRoZSBvYmplY3QgdGhyb3VnaCBgc2V0YCwgd2l0aCBhIGBsYXN0TW9kaWZpZWRgIHBhc3QgdGhlXG4gICAgICAgIC8vIHRhZydzIG1hcmtlcjsgZGVsZXRpbmcgaXQgaGVyZSBhcyB3ZWxsIHJhY2VkIHRoYXQgd3JpdGUsIGFuZCBhXG4gICAgICAgIC8vIGNvbmN1cnJlbnQgcmVxdWVzdCdzIGZyZXNoIGVudHJ5IGNvdWxkIGJlIHRoZSBvbmUgcmVtb3ZlZC4gS2VlcGluZ1xuICAgICAgICAvLyBhIHJlc3BvbnNlIGVudHJ5IGFsc28gbWVhbnMgYSByZW5kZXIgdGhhdCBmYWlscyBpcyBhbnN3ZXJlZCBmcm9tXG4gICAgICAgIC8vIHRoZSBsYXN0IGdvb2QgY29weSByYXRoZXIgdGhhbiBmcm9tIGEgc2hlbGwuXG4gICAgICAgIHJldHVybiBpc0ZldGNoQ2FjaGVHZXQoY3R4KVxuICAgICAgICAgID8gbnVsbFxuICAgICAgICAgIDogeyBsYXN0TW9kaWZpZWQ6IEVYUElSRURfTEFTVF9NT0RJRklFRCwgdmFsdWU6IHBhcnNlZFZhbHVlLnZhbHVlIH07XG4gICAgICB9XG5cbiAgICAgIHRoaXMuZGVidWcoYFMzIENBQ0hFIEhJVDogJHtjYWNoZUtleX0gKCR7czNLZXl9KWApO1xuXG4gICAgICAvLyBXaXRob3V0IHRoZSBzdG9yZWQgdGFncy5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIGxhc3RNb2RpZmllZDogcGFyc2VkVmFsdWUubGFzdE1vZGlmaWVkLFxuICAgICAgICB2YWx1ZTogcGFyc2VkVmFsdWUudmFsdWUsXG4gICAgICB9O1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAvLyBMb2cgYWN0dWFsIGVycm9ycyAoYSBjYWNoZSBtaXNzIGlzIG5vdCBvbmU6IHNlZSBgQ2FjaGVCdWNrZXQuZ2V0YClcbiAgICAgIGNvbnNvbGUuZXJyb3IoYEVycm9yIHJldHJpZXZpbmcgY2FjaGUgZnJvbSBTMzpgLCBlcnJvcik7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogV2hldGhlciBhIHRhZyByZXZhbGlkYXRpb24gaGFzIGV4cGlyZWQgYGVudHJ5YCwgZm9yIGEgYGdldGAgd2l0aCBgY3R4YC5cbiAgICpcbiAgICogUHVibGljIGJlY2F1c2UgdGhlIGluLW1lbW9yeSBsYXllciBpbiBmcm9udCBvZiB0aGlzIGhhbmRsZXIgaGFzIHRvIGFzayB0b286XG4gICAqIGEgbWVtb3J5IGhpdCBuZXZlciByZWFjaGVzIHtAbGluayBnZXR9LCBhbmQgd2l0aG91dCB0aGlzIGNoZWNrIGFuIGluc3RhbmNlXG4gICAqIG90aGVyIHRoYW4gdGhlIG9uZSB0aGF0IHJhbiBgcmV2YWxpZGF0ZVRhZ2Aga2VwdCBhbnN3ZXJpbmcgZnJvbSBtZW1vcnkgZm9yXG4gICAqIHRoZSB3aG9sZSBtZW1vcnkgVFRMIC0gbG9uZyBlbm91Z2ggZm9yIENsb3VkRnJvbnQgdG8gY2FjaGUgdGhlIHN0YWxlIHBhZ2VcbiAgICogYWdhaW4gYWZ0ZXIgdGhlIGludmFsaWRhdGlvbi5cbiAgICpcbiAgICogV2hpY2ggdGFncyB0byBjaGVjayBkZXBlbmRzIG9uIHRoZSBraW5kLCBhbmQgTmV4dC5qcydzIG93blxuICAgKiBgRmlsZVN5c3RlbUNhY2hlLmdldGAgc3BsaXRzIHRoZSBzYW1lIHR3byB3YXlzOiBhIHJlc3BvbnNlIGVudHJ5IGlzIGNoZWNrZWRcbiAgICogYWdhaW5zdCB0aGUgdGFncyAqaXQqIHdhcyBzdG9yZWQgd2l0aCwgYnV0IGEgYGZldGNoYCBlbnRyeSBpcyBjaGVja2VkXG4gICAqIGFnYWluc3QgYFsuLi5jdHgudGFncywgLi4uY3R4LnNvZnRUYWdzXWAg4oCUIHRoZSB0YWdzIG9mIHRoZSByZXF1ZXN0IGFza2luZ1xuICAgKiBmb3IgaXQuIFRoYXQgZGlzdGluY3Rpb24gaXMgbG9hZC1iZWFyaW5nLCBiZWNhdXNlIGEgYGZldGNoYCBlbnRyeSBpcyBvbmx5XG4gICAqIGV2ZXIgc3RvcmVkIHdpdGggaXRzICpleHBsaWNpdCogYGNhY2hlOiB7IHRhZ3MgfWAgYW5kIG5ldmVyIHdpdGggdGhlXG4gICAqIGltcGxpY2l0IGBfTl9UXy88cGF0aD5gIGNoYWluLCB3aGljaCBhcnJpdmVzIG9ubHkgYXMgYGN0eC5zb2Z0VGFnc2AuXG4gICAqIFJlYWRpbmcgdGhlIHN0b3JlZCB0YWdzIGZvciBhIGBmZXRjaGAgdG9vIG1lYW50IGFuIHVudGFnZ2VkIGBmb3JjZS1jYWNoZWBcbiAgICogZmV0Y2ggaGFkIGBbXWAsIHNraXBwZWQgdGhlIGNoZWNrIGVudGlyZWx5LCBhbmQgbm8gYHJldmFsaWRhdGVQYXRoYCBjb3VsZFxuICAgKiBldmVyIGV2aWN0IGl0OiBhIGBmb3JjZS1keW5hbWljYCBwYWdlIHdob3NlIGRhdGEgY29tZXMgZnJvbSBzdWNoIGEgZmV0Y2hcbiAgICogcmUtcmVuZGVyZWQgb24gZXZlcnkgcmVxdWVzdCBhbmQgc3RpbGwgc2VydmVkIHRoZSBzYW1lIGJvZHkgZm9yZXZlci5cbiAgICogTWVhc3VyZWQgYWdhaW5zdCBuZXh0LmpzJ3MgYHRlc3QvZTJlL2FwcC1kaXIvcmV2YWxpZGF0ZS1wYXRoLXdpdGgtcmV3cml0ZXNgLlxuICAgKlxuICAgKiBgY2FjaGVLZXlgIGlzIHRoZSBrZXkgdGhlIGVudHJ5IHdhcyByZWFkIHVuZGVyLiBBIHJlc3BvbnNlIGVudHJ5IGZvdW5kXG4gICAqIHN0YWxlIGlzIHJlbWVtYmVyZWQgYnkgaXQsIGZvciBgc2V0YCB0byBpbnZhbGlkYXRlIENsb3VkRnJvbnQgYWdhaW4gb25jZVxuICAgKiBpdHMgcmVnZW5lcmF0aW9uIGxhbmRzOiBzZWUge0BsaW5rIHNvZnRSZXZhbGlkYXRlZEtleXN9LlxuICAgKi9cbiAgYXN5bmMgaXNSZXZhbGlkYXRlZChcbiAgICBlbnRyeTogeyBsYXN0TW9kaWZpZWQ/OiBudW1iZXI7IHRhZ3M/OiBzdHJpbmdbXTsgdmFsdWU/OiB1bmtub3duIH0sXG4gICAgY3R4OiBHZXRJbmNyZW1lbnRhbEZldGNoQ2FjaGVDb250ZXh0IHwgR2V0SW5jcmVtZW50YWxSZXNwb25zZUNhY2hlQ29udGV4dCxcbiAgICBjYWNoZUtleTogc3RyaW5nLFxuICApOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBpZiAoIXRoaXMuY29uZmlnLnRhYmxlTmFtZSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBjb25zdCBjaGVja1RhZ3MgPSBpc0ZldGNoQ2FjaGVHZXQoY3R4KVxuICAgICAgPyBbLi4uKGN0eC50YWdzID8/IFtdKSwgLi4uKGN0eC5zb2Z0VGFncyA/PyBbXSldXG4gICAgICA6IGVudHJ5VGFncyhlbnRyeSk7XG4gICAgaWYgKGNoZWNrVGFncy5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgY29uc3Qgc3RhdGUgPSBhd2FpdCB0aGlzLmNoZWNrSWZSZXZhbGlkYXRlZChcbiAgICAgIGVudHJ5Lmxhc3RNb2RpZmllZCA/PyAwLFxuICAgICAgY2hlY2tUYWdzLFxuICAgICk7XG4gICAgaWYgKHN0YXRlID09PSBcInN0YWxlXCIgJiYgIWlzRmV0Y2hDYWNoZUdldChjdHgpKSB7XG4gICAgICB0aGlzLm5vdGVTb2Z0UmV2YWxpZGF0ZWQoY2FjaGVLZXkpO1xuICAgIH1cbiAgICByZXR1cm4gc3RhdGUgPT09IFwiZXhwaXJlZFwiO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbWVtYmVyIHRoYXQgYGNhY2hlS2V5YCB3YXMgc2VydmVkIHN0YWxlLCB3aGVuIHRoZXJlIGlzIGEgZGlzdHJpYnV0aW9uXG4gICAqIHdob3NlIGNvcHkgb2YgaXQgd2lsbCBuZWVkIGludmFsaWRhdGluZyBhZ2Fpbi4gU2VlXG4gICAqIHtAbGluayBzb2Z0UmV2YWxpZGF0ZWRLZXlzfS5cbiAgICovXG4gIHByaXZhdGUgbm90ZVNvZnRSZXZhbGlkYXRlZChjYWNoZUtleTogc3RyaW5nKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmludmFsaWRhdGVzQ2RuKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuc29mdFJldmFsaWRhdGVkS2V5cy5kZWxldGUoY2FjaGVLZXkpO1xuICAgIGlmICh0aGlzLnNvZnRSZXZhbGlkYXRlZEtleXMuc2l6ZSA+PSBNQVhfU09GVF9SRVZBTElEQVRFRF9LRVlTKSB7XG4gICAgICAvLyBJbnNlcnRpb24gb3JkZXI6IHRoZSBmaXJzdCBrZXkgaXMgdGhlIG9uZSB3YWl0aW5nIGxvbmdlc3QuXG4gICAgICBjb25zdCBvbGRlc3QgPSB0aGlzLnNvZnRSZXZhbGlkYXRlZEtleXMudmFsdWVzKCkubmV4dCgpLnZhbHVlO1xuICAgICAgaWYgKG9sZGVzdCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHRoaXMuc29mdFJldmFsaWRhdGVkS2V5cy5kZWxldGUob2xkZXN0KTtcbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5zb2Z0UmV2YWxpZGF0ZWRLZXlzLmFkZChjYWNoZUtleSk7XG4gIH1cblxuICBhc3luYyBzZXQoXG4gICAgY2FjaGVLZXk6IHN0cmluZyxcbiAgICBkYXRhOiBJbmNyZW1lbnRhbENhY2hlVmFsdWUgfCBudWxsLFxuICAgIGN0eDogU2V0SW5jcmVtZW50YWxGZXRjaENhY2hlQ29udGV4dCB8IFNldEluY3JlbWVudGFsUmVzcG9uc2VDYWNoZUNvbnRleHQsXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRyeSB7XG4gICAgICBpZiAoIWRhdGEpIHtcbiAgICAgICAgLy8gRGVsZXRlIGZyb20gUzMgYW5kIER5bmFtb0RCXG4gICAgICAgIHRoaXMuZGVidWcoYFMzIENBQ0hFIERFTEVURTogJHtjYWNoZUtleX1gKTtcblxuICAgICAgICBpZiAoIXRoaXMuY29uZmlnLmJ1Y2tldE5hbWUpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBUaGUgZW50cnkgaXMgZ29uZSwgc28gdGhlcmUgaXMgbm8gcmVnZW5lcmF0aW9uIGxlZnQgdG8gd2FpdCBmb3IuXG4gICAgICAgIHRoaXMuc29mdFJldmFsaWRhdGVkS2V5cy5kZWxldGUoY2FjaGVLZXkpO1xuXG4gICAgICAgIC8vIEJ1aWxkIFMzIGtleSB3aXRob3V0IG5lZWRpbmcgdG8ga25vdyB0aGUga2luZFxuICAgICAgICBjb25zdCBzM0tleSA9IHRoaXMuYnVpbGRTM0tleShjYWNoZUtleSk7XG5cbiAgICAgICAgLy8gUmVhZCB0aGUgZW50cnkncyB0YWdzIGJlZm9yZSB0aGUgb2JqZWN0IGlzIGdvbmU6IHRoZXkgYXJlIHRoZSBvbmx5XG4gICAgICAgIC8vIHdheSB0byBuYW1lIGl0cyBtYXBwaW5nIHJvd3MsIHdob3NlIHNvcnQga2V5IGlzIGB0YWcjczNLZXlgIGFuZCBzb1xuICAgICAgICAvLyBjYW5ub3QgYmUgcXVlcmllZCBmcm9tIHRoZSBrZXkgc2lkZS4gU2VlIGBzdG9yZWRFbnRyeVRhZ3NgLlxuICAgICAgICBjb25zdCB0YWdzID0gYXdhaXQgdGhpcy5zdG9yZWRFbnRyeVRhZ3MoczNLZXksIGN0eCk7XG5cbiAgICAgICAgYXdhaXQgdGhpcy5zM0NsaWVudC5zZW5kKFxuICAgICAgICAgIG5ldyBEZWxldGVPYmplY3RDb21tYW5kKHtcbiAgICAgICAgICAgIEJ1Y2tldDogdGhpcy5jb25maWcuYnVja2V0TmFtZSxcbiAgICAgICAgICAgIEtleTogczNLZXksXG4gICAgICAgICAgfSksXG4gICAgICAgICk7XG4gICAgICAgIHRoaXMuZGVidWcoYFMzIENBQ0hFIERFTEVURUQ6ICR7czNLZXl9YCk7XG5cbiAgICAgICAgYXdhaXQgdGhpcy5kZWxldGVEeW5hbW9EQlRhZ01hcHBpbmdzKHMzS2V5LCB0YWdzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICAvLyBgZW50cnlUYWdzYCwgbm90IGBnZXRUYWdzKGN0eClgIGFsb25lOiBmb3IgYSBwYWdlIG9yIHJvdXRlIHJlc3BvbnNlXG4gICAgICAvLyBgY3R4YCBjYXJyaWVzIG9ubHkgYHsgY2FjaGVDb250cm9sLCBpc1JvdXRlUFBSRW5hYmxlZCwgaXNGYWxsYmFjayB9YCDigJRcbiAgICAgIC8vIGBSZXNwb25zZUNhY2hlLnNldGAgYnVpbGRzIGl0IHRoYXQgd2F5IGFuZCBgSW5jcmVtZW50YWxDYWNoZS5zZXRgXG4gICAgICAvLyBmb3J3YXJkcyBpdCB1bmNoYW5nZWQg4oCUIHNvIHRoZSB0YWdzIGxpdmUgaW4gdGhlIHJlbmRlcidzXG4gICAgICAvLyBgeC1uZXh0LWNhY2hlLXRhZ3NgIGhlYWRlciBhbmQgbm93aGVyZSBlbHNlLiBSZWFkaW5nIG9ubHkgYGN0eGAgbWVhbnQgbm9cbiAgICAgIC8vIHJ1bnRpbWUtcmVuZGVyZWQgcGFnZSBldmVyIGdvdCBhIG1hcHBpbmcgcm93LCBhbmQgYSBwYWdlIHRoYXQgaXMgbm90IGluXG4gICAgICAvLyB0aGUgYnVpbGQncyB0YWcgbWFuaWZlc3QgbmFtZXMgbm9uZSBlaXRoZXI6IGEgbGF0ZXJcbiAgICAgIC8vIGByZXZhbGlkYXRlVGFnYCBmb3VuZCBub3RoaW5nIHRvIGludmFsaWRhdGUgYW5kIENsb3VkRnJvbnQga2VwdCBzZXJ2aW5nXG4gICAgICAvLyB0aGUgc3RhbGUgSFRNTCBhbmQgUlNDIHBheWxvYWQgZm9yIHRoZSB3aG9sZSBgcy1tYXhhZ2VgLiBTYW1lIHNvdXJjZSB0aGVcbiAgICAgIC8vIHJlYWQgcGF0aCBhYm92ZSBhbHJlYWR5IGZhbGxzIGJhY2sgdG8uXG4gICAgICBjb25zdCB0YWdzID0gZW50cnlUYWdzKHsgdGFnczogZ2V0VGFncyhjdHgpLCB2YWx1ZTogZGF0YSB9KTtcbiAgICAgIHRoaXMuZGVidWcoXG4gICAgICAgIGBTMyBDQUNIRSBTRVQ6IEtleTogJHtjYWNoZUtleX0sIHRhZ3M6ICR7dGFncy5sZW5ndGggPyB0YWdzIDogXCJub25lXCJ9YCxcbiAgICAgICk7XG5cbiAgICAgIGlmICghdGhpcy5jb25maWcuYnVja2V0TmFtZSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHMzS2V5ID0gdGhpcy5idWlsZFMzS2V5KGNhY2hlS2V5KTtcblxuICAgICAgLy8gQ3JlYXRlIENhY2hlSGFuZGxlclZhbHVlIHN0cnVjdHVyZSBmb3IgUzMgc3RvcmFnZSB3aXRoIHRhZ3NcbiAgICAgIGNvbnN0IGNhY2hlSGFuZGxlclZhbHVlOiBDYWNoZUhhbmRsZXJWYWx1ZSA9IHtcbiAgICAgICAgLy8gT24gdGhlIG1hcmtlciBjbG9jaywgc2luY2UgdGhhdCBpcyB3aGF0IGl0IGlzIGNvbXBhcmVkIHdpdGguXG4gICAgICAgIGxhc3RNb2RpZmllZDogbWFya2VyQ2xvY2soKSxcbiAgICAgICAgdmFsdWU6IGRhdGEsXG4gICAgICB9O1xuXG4gICAgICAvLyBTdG9yZSB0YWdzIHdpdGggdGhlIGNhY2hlIGVudHJ5IGZvciByZXZhbGlkYXRpb24gY2hlY2tpbmdcbiAgICAgIGNvbnN0IGNhY2hlRW50cnlXaXRoVGFncyA9IHtcbiAgICAgICAgLi4uY2FjaGVIYW5kbGVyVmFsdWUsXG4gICAgICAgIHRhZ3MsXG4gICAgICB9O1xuXG4gICAgICAvLyBTZXJpYWxpemUgd2l0aCBjdXN0b20gaGFuZGxpbmcgZm9yIE1hcCBhbmQgQnVmZmVyIG9iamVjdHNcbiAgICAgIGNvbnN0IGJvZHkgPSBzZXJpYWxpemVDYWNoZVZhbHVlKGNhY2hlRW50cnlXaXRoVGFncyk7XG5cbiAgICAgIC8vIEFsd2F5cyBKU09OIHNpbmNlIHdlIHN0b3JlIENhY2hlSGFuZGxlclZhbHVlXG4gICAgICBhd2FpdCB0aGlzLmJ1Y2tldC5wdXRKc29uKHMzS2V5LCBib2R5KTtcblxuICAgICAgdGhpcy5kZWJ1ZyhgUzMgQ0FDSEUgU1RPUkVEOiAke2NhY2hlS2V5fSAoJHtkYXRhLmtpbmR9KWApO1xuXG4gICAgICAvLyBUaGUgcmVnZW5lcmF0aW9uIG9mIGFuIGVudHJ5IHNlcnZlZCBzdGFsZTogQ2xvdWRGcm9udCBtYXkgaGF2ZSBjYWNoZWRcbiAgICAgIC8vIHRoYXQgc3RhbGUgcmVzcG9uc2UgYWZ0ZXIgYHJldmFsaWRhdGVUYWdgJ3MgaW52YWxpZGF0aW9uLCBzbyB0aGUga2V5J3NcbiAgICAgIC8vIHBhdGhzIGdvIG9uY2UgbW9yZSBub3cgdGhhdCBTMyBob2xkcyB0aGUgZnJlc2ggb25lLlxuICAgICAgaWYgKHRoaXMuc29mdFJldmFsaWRhdGVkS2V5cy5kZWxldGUoY2FjaGVLZXkpKSB7XG4gICAgICAgIGNvbnN0IHJvdXRlID0gdGhpcy5zM0tleVRvSW52YWxpZGF0aW9uUGF0aChzM0tleSk7XG4gICAgICAgIGlmIChyb3V0ZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgdGhpcy5kZWJ1ZyhgU09GVCBSRVZBTElEQVRJT04gUkVHRU5FUkFURUQ6ICR7Y2FjaGVLZXl9YCk7XG4gICAgICAgICAgYXdhaXQgdGhpcy5pbnZhbGlkYXRlQ2xvdWRGcm9udFBhdGhzKFxuICAgICAgICAgICAgY2RuSW52YWxpZGF0aW9uUGF0aHMocm91dGUsIHRoaXMuY2xvdWRGcm9udENvbmZpZy5iYXNlUGF0aCksXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBTdG9yZSB0YWctdG8tY2FjaGUta2V5IG1hcHBpbmdzIGluIER5bmFtb0RCIGZvciByZXZhbGlkYXRpb25cbiAgICAgIGlmICh0YWdzLmxlbmd0aCA+IDAgJiYgdGhpcy5tYXBzVGFnc1RvUGF0aHMpIHtcbiAgICAgICAgdGhpcy5kZWJ1ZyhgU1RPUklORyBUQUdTOiAke2NhY2hlS2V5fSAtPiBbJHt0YWdzLmpvaW4oXCIsIFwiKX1dYCk7XG4gICAgICAgIGF3YWl0IHRoaXMuc3RvcmVEeW5hbW9EQlRhZ01hcHBpbmdzKHMzS2V5LCB0YWdzKTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgY29uc29sZS5lcnJvcihcIkVycm9yIHN0b3JpbmcgY2FjaGUgdG8gUzM6XCIsIGVycm9yKTtcbiAgICB9XG4gIH1cblxuICBhc3luYyByZXZhbGlkYXRlVGFnKFxuICAgIHRhZzogc3RyaW5nIHwgc3RyaW5nW10sXG4gICAgZHVyYXRpb25zPzogUmV2YWxpZGF0ZUR1cmF0aW9ucyxcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgdGFncyA9IEFycmF5LmlzQXJyYXkodGFnKSA/IHRhZyA6IFt0YWddO1xuICAgIHRoaXMuZGVidWcoYFJFVkFMSURBVElORyBUQUdTOiBbJHt0YWdzLmpvaW4oXCIsIFwiKX1dYCk7XG5cbiAgICBpZiAoIXRoaXMuY29uZmlnLnRhYmxlTmFtZSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIFJlY29yZCB0aGUgcmV2YWxpZGF0aW9uIGFnYWluc3QgZWFjaCB0YWcgaXRzZWxmLCBub3Qgb25seSBhZ2FpbnN0IHRoZVxuICAgIC8vIGNhY2hlIGtleXMgYWxyZWFkeSBtYXBwZWQgdG8gaXQ6IGBjaGVja0lmUmV2YWxpZGF0ZWRgIHJlYWRzIHRoZXNlIG1hcmtlclxuICAgIC8vIHJvd3MsIGFuZCBhIGJ1aWxkLXRpbWUgcHJlcmVuZGVyIGhhcyBubyBtYXBwaW5nIHJvd3MgYXQgYWxsLlxuICAgIGNvbnN0IHJlY29yZGVkID0gYXdhaXQgdGhpcy50YWdzLnVwZGF0ZSh0YWdzLCBkdXJhdGlvbnMpO1xuXG4gICAgaWYgKCF0aGlzLm1hcHNUYWdzVG9QYXRocykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IHsgYmFzZVBhdGggfSA9IHRoaXMuY2xvdWRGcm9udENvbmZpZztcbiAgICBpZiAoIXJlY29yZGVkKSB7XG4gICAgICAvLyBPdGhlciBpbnN0YW5jZXMgbWF5IG5ldmVyIGxlYXJuIG9mIHRoaXMgcmV2YWxpZGF0aW9uLCBzbyBubyBwYXRoIGxpc3RcbiAgICAgIC8vIGlzIGVub3VnaDogZHJvcCB0aGUgd2hvbGUgYXBwIGZyb20gQ2xvdWRGcm9udCByYXRoZXIgdGhhbiBzZXJ2ZSBzdGFsZVxuICAgICAgLy8gcGFnZXMgdW50aWwgdGhlaXIgVFRMLlxuICAgICAgYXdhaXQgdGhpcy5pbnZhbGlkYXRlQ2xvdWRGcm9udFBhdGhzKHdob2xlQXBwSW52YWxpZGF0aW9uUGF0aHMoYmFzZVBhdGgpKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBTZXR0bGVkIHJhdGhlciB0aGFuIGBhbGxgOiBvbmUgdGFnJ3MgdGhyb3R0bGVkIFF1ZXJ5IG11c3Qgbm90IGRyb3AgdGhlXG4gICAgLy8gQ2xvdWRGcm9udCBwYXRocyBldmVyeSBvdGhlciB0YWcgcmVzb2x2ZWQuXG4gICAgY29uc3QgcmVzdWx0cyA9IGF3YWl0IFByb21pc2UuYWxsU2V0dGxlZChcbiAgICAgIHRhZ3MubWFwKCh0KSA9PiB0aGlzLnRhZ1JvdXRlcyh0KSksXG4gICAgKTtcbiAgICBjb25zdCByb3V0ZXM6IHN0cmluZ1tdID0gW107XG4gICAgbGV0IHdob2xlQXBwID0gZmFsc2U7XG4gICAgZm9yIChjb25zdCByZXN1bHQgb2YgcmVzdWx0cykge1xuICAgICAgaWYgKHJlc3VsdC5zdGF0dXMgPT09IFwicmVqZWN0ZWRcIikge1xuICAgICAgICBjb25zb2xlLmVycm9yKFwiRXJyb3IgcmVhZGluZyB0YWcgbWFwcGluZ3M6XCIsIHJlc3VsdC5yZWFzb24pO1xuICAgICAgICAvLyBUaGUgdGFnJ3MgZW50cmllcyBhcmUgcmV2YWxpZGF0ZWQgYXQgdGhlIG9yaWdpbiB3aGlsZSBub3RoaW5nIG5hbWVzXG4gICAgICAgIC8vIHRoZWlyIENsb3VkRnJvbnQgcGF0aHMuIFRoZSBzYW1lIGFuc3dlciBhcyBhIHRhZyBjdXQgc2hvcnQgYnlcbiAgICAgICAgLy8gYE1BWF9UQUdfUVVFUllfUEFHRVNgOiB0aGUgd2hvbGUgYXBwLCBuZXZlciBhIHN0YWxlIHBhZ2UuXG4gICAgICAgIHdob2xlQXBwID0gdHJ1ZTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICByb3V0ZXMucHVzaCguLi5yZXN1bHQudmFsdWUucm91dGVzKTtcbiAgICAgIHdob2xlQXBwIHx8PSByZXN1bHQudmFsdWUudHJ1bmNhdGVkO1xuICAgIH1cblxuICAgIC8vIEludmFsaWRhdGUgdGhlIENETiBlZGdlIGNhY2hlIHNvIENsb3VkRnJvbnQtZnJvbnRlZCBkZXBsb3ltZW50cyBkb24ndFxuICAgIC8vIGtlZXAgc2VydmluZyBzdGFsZSByZXNwb25zZXMgdW50aWwgdGhlIGNhY2hlIHBvbGljeSdzIFRUTCBuYXR1cmFsbHlcbiAgICAvLyBleHBpcmVzLiBPbmUgcmVxdWVzdCBmb3IgZXZlcnkgdGFnIGF0IG9uY2U6IHNlZVxuICAgIC8vIGBNQVhfV0lMRENBUkRfUEFUSFNfUEVSX0lOVkFMSURBVElPTmAgZm9yIHdoeSBuZXZlciBzZXZlcmFsLlxuICAgIGF3YWl0IHRoaXMuaW52YWxpZGF0ZUNsb3VkRnJvbnRQYXRocyhcbiAgICAgIHdob2xlQXBwXG4gICAgICAgID8gd2hvbGVBcHBJbnZhbGlkYXRpb25QYXRocyhiYXNlUGF0aClcbiAgICAgICAgOiByb3V0ZXMuZmxhdE1hcCgocm91dGUpID0+IGNkbkludmFsaWRhdGlvblBhdGhzKHJvdXRlLCBiYXNlUGF0aCkpLFxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogVGhlIHJvdXRlcyBDbG91ZEZyb250IGNvdWxkIGJlIGhvbGRpbmcgYSByZXNwb25zZSBmb3IgYHRhZ2AgdW5kZXIgLVxuICAgKiBgdHJ1bmNhdGVkYCB3aGVuIHRoZXkgY291bGQgbm90IGFsbCBiZSBuYW1lZDogbW9yZSBtYXBwaW5nIHJvd3MgdGhhblxuICAgKiB7QGxpbmsgTUFYX1RBR19RVUVSWV9QQUdFU30sIG9yIG5vIGJ1aWxkIHRhZyBtYW5pZmVzdCB0byByZWFkLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyB0YWdSb3V0ZXMoXG4gICAgdGFnOiBzdHJpbmcsXG4gICk6IFByb21pc2U8eyByb3V0ZXM6IHN0cmluZ1tdOyB0cnVuY2F0ZWQ6IGJvb2xlYW4gfT4ge1xuICAgIGNvbnN0IFt7IGl0ZW1zLCB0cnVuY2F0ZWQgfSwgYnVpbGRUYWdzXSA9IGF3YWl0IFByb21pc2UuYWxsKFtcbiAgICAgIHRoaXMucXVlcnlUYWdNYXBwaW5ncyh0YWcpLFxuICAgICAgdGhpcy5idWlsZFRhZ01hbmlmZXN0KCksXG4gICAgXSk7XG4gICAgLy8gRXh0cmFjdCBTMyBrZXlzIGZyb20gc29ydCBrZXlzIChmb3JtYXQ6IFwidGFnI3MzS2V5XCIpLiBTcGxpdCBhdCB0aGUgdGFnJ3NcbiAgICAvLyBvd24gbGVuZ3RoIHJhdGhlciB0aGFuIGF0IHRoZSBmaXJzdCBcIiNcIjogYSB0YWcgaXMgYXBwLWRlZmluZWQgYW5kIG1heVxuICAgIC8vIGNvbnRhaW4gb25lLCBhbmQgYHJldmFsaWRhdGVUYWcoXCJ1c2VyIzQyXCIpYCB0aGVuIHlpZWxkZWRcbiAgICAvLyBcIjQyIzxidWlsZElkPi9hY2NvdW50Lmpzb25cIiDigJQgd2hvc2UgaW52YWxpZGF0aW9uIHBhdGhcbiAgICAvLyAoXCIvNDIjPGJ1aWxkSWQ+L2FjY291bnRcIikgbmFtZXMgbm90aGluZyBDbG91ZEZyb250IGNhY2hlZCwgc28gdGhlIGVkZ2VcbiAgICAvLyBrZXB0IHNlcnZpbmcgdGhlIHN0YWxlIHBhZ2UuXG4gICAgLy9cbiAgICAvLyBUaGUgc2FtZSBgI2AgbWFrZXMgYGJlZ2luc193aXRoKHNrLCBcInVzZXIjXCIpYCBtYXRjaCByb3dzIHRoYXQgYXJlIG5vdFxuICAgIC8vIGB1c2VyYCdzIGF0IGFsbDogdGhlIG1hcmtlciByb3cgb2YgdGFnIGB1c2VyIzQyYCAoc2sgYHVzZXIjNDJgKSBhbmQgZXZlcnlcbiAgICAvLyBtYXBwaW5nIHJvdyBvZiBpdCAoYHVzZXIjNDIjPGJ1aWxkSWQ+L+KApmApLiBXaGF0IGlzIGxlZnQgYWZ0ZXIgdGhlIHByZWZpeFxuICAgIC8vIGlzIGFuIFMzIGtleSBvbmx5IGlmIGl0IHN0YXJ0cyB3aXRoIHRoZSBidWlsZCdzIG93biBwcmVmaXgsIHdoaWNoIGlzIGhvd1xuICAgIC8vIGBidWlsZFMzS2V5YCB3cml0ZXMgZXZlcnkgb25lLlxuICAgIGNvbnN0IHByZWZpeExlbmd0aCA9IHRhZy5sZW5ndGggKyAxO1xuICAgIGNvbnN0IGtleVByZWZpeCA9IHRoaXMuY29uZmlnLmJ1aWxkSWQgPyBgJHt0aGlzLmNvbmZpZy5idWlsZElkfS9gIDogXCJcIjtcbiAgICBjb25zdCBzM0tleXMgPSBpdGVtc1xuICAgICAgLm1hcCgoaXRlbSkgPT4gaXRlbS5zaz8uUz8uc2xpY2UocHJlZml4TGVuZ3RoKSlcbiAgICAgIC5maWx0ZXIoKHMzS2V5KTogczNLZXkgaXMgc3RyaW5nID0+XG4gICAgICAgIEJvb2xlYW4oczNLZXkgJiYgczNLZXkuc3RhcnRzV2l0aChrZXlQcmVmaXgpKSxcbiAgICAgICk7XG4gICAgLy8gVGhlIGJ1aWxkLXRpbWUgcHJlcmVuZGVycyBjYXJyeWluZyB0aGUgdGFnLCB3aGljaCBubyBgc2V0YCB3cm90ZSBhIHJvd1xuICAgIC8vIGZvci5cbiAgICBmb3IgKGNvbnN0IGNhY2hlS2V5IG9mIGJ1aWxkVGFncz8uZ2V0KHRhZykgPz8gW10pIHtcbiAgICAgIHMzS2V5cy5wdXNoKHRoaXMuYnVpbGRTM0tleShjYWNoZUtleSkpO1xuICAgIH1cblxuICAgIHRoaXMuZGVidWcoXG4gICAgICBgVEFHICR7dGFnfTogRm91bmQgJHtzM0tleXMubGVuZ3RofSBjYWNoZSBlbnRyaWVzIHRvIGludmFsaWRhdGVgLFxuICAgICk7XG5cbiAgICAvLyBUaGUgbWFwcGluZyByb3dzIHRoZW1zZWx2ZXMgYXJlIGRlbGliZXJhdGVseSBsZWZ0IGFsb25lLiBTdGFtcGluZ1xuICAgIC8vIGByZXZhbGlkYXRlZEF0YCBvbiBlYWNoIGB0YWcjY2FjaGVLZXlgIHJvdyBpcyB3aGF0IHRoaXMgdXNlZCB0byBkbywgYW5kXG4gICAgLy8gbm90aGluZyBoYXMgcmVhZCB0aGF0IGF0dHJpYnV0ZSBzaW5jZSBgY2hlY2tJZlJldmFsaWRhdGVkYCBiZWdhbiByZWFkaW5nXG4gICAgLy8gdGhlIGJhcmUtdGFnIG1hcmtlciByb3cgd3JpdHRlbiBhYm92ZSDigJQgc28gdGhlIHdyaXRlcyB3ZXJlIGRlYWQsIGFuZCB0aGV5XG4gICAgLy8gd2VyZSBkZWFkICppbiBmcm9udCBvZiogdGhlIGludmFsaWRhdGlvbi4gT25lIHRocm90dGxlZCBgVXBkYXRlSXRlbWBcbiAgICAvLyByZWplY3RlZCB0aGUgYmF0Y2gsIHRoZSBDbG91ZEZyb250IGludmFsaWRhdGlvbiBuZXZlciByYW4sIGFuZCB0aGUgb25seVxuICAgIC8vIHRyYWNlIHdhcyBhIGBjb25zb2xlLmVycm9yYCB3aGlsZSB0aGUgZWRnZSBrZXB0IHNlcnZpbmcgdGhlIHN0YWxlIHBhZ2UuXG4gICAgLy9cbiAgICAvLyBEZWxpYmVyYXRlbHkgbm90IGRlbGV0aW5nIHRoZSB0YWcncyBTMyBvYmplY3RzIGVpdGhlci4gVGhlIG1hcmtlciByb3dcbiAgICAvLyBhYm92ZSBpcyB3aGF0IGludmFsaWRhdGVzIHRoZW06IGBnZXRgIGNvbXBhcmVzIGl0IGFnYWluc3QgZWFjaCBlbnRyeSdzXG4gICAgLy8gb3duIGBsYXN0TW9kaWZpZWRgIGFuZCBoYW5kcyB0aGUgZW50cnkgYmFjayBleHBpcmVkLCB3aGljaCBpcyB0aGUgc2lnbmFsXG4gICAgLy8gdGhhdCBtYWtlcyBOZXh0LmpzIHJlLXJlbmRlciAqdGhpcyogcm91dGUgYW5kIHN0b3JlIHRoZSByZXN1bHQgKHNlZVxuICAgIC8vIGBFWFBJUkVEX0xBU1RfTU9ESUZJRURgKS4gRGVsZXRpbmcgdGhlIG9iamVjdCBpbnN0ZWFkIHR1cm5lZCB0aGUgbmV4dFxuICAgIC8vIHJlcXVlc3QgaW50byBhIGhhcmQgbWlzcywgYW5kIGEgaGFyZCBtaXNzIG9uIGEgUFBSIHJvdXRlIGlzIGFuc3dlcmVkIGZyb21cbiAgICAvLyB0aGUgcm91dGUncyBmYWxsYmFjayBzaGVsbCDigJQgdW5jYWNoZWFibGUsIGFuZCBuZXZlciB1cGdyYWRlZCBiYWNrIGludG8gYVxuICAgIC8vIGNvbmNyZXRlIGVudHJ5LCBzbyBvbmUgYHJldmFsaWRhdGVUYWdgIGxlZnQgdGhlIHBhZ2UgcmVzdW1pbmdcbiAgICAvLyBkeW5hbWljYWxseSBmb3IgZ29vZC5cbiAgICBjb25zdCByb3V0ZXMgPSBzM0tleXNcbiAgICAgIC5tYXAoKHMzS2V5KSA9PiB0aGlzLnMzS2V5VG9JbnZhbGlkYXRpb25QYXRoKHMzS2V5KSlcbiAgICAgIC5maWx0ZXIoKHJvdXRlKTogcm91dGUgaXMgc3RyaW5nID0+IHJvdXRlICE9PSB1bmRlZmluZWQpO1xuICAgIC8vIEEgYHJldmFsaWRhdGVQYXRoYCBuYW1lcyBpdHMgcGF0aCBpbiB0aGUgdGFnIGl0c2VsZiwgd2hpY2ggaXMgdGhlIG9ubHlcbiAgICAvLyB3YXkgdG8gcmVhY2ggYSBidWlsZC10aW1lIHByZXJlbmRlcidzIENETiBjb3B5IHdoZW4gaXQgaGFzIG5vIG1hcHBpbmdcbiAgICAvLyByb3cuXG4gICAgcm91dGVzLnB1c2goLi4uaW1wbGljaXRUYWdQYXRocyh0YWcpKTtcbiAgICByZXR1cm4geyByb3V0ZXMsIHRydW5jYXRlZDogdHJ1bmNhdGVkIHx8ICFidWlsZFRhZ3MgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgYnVpbGQtdGltZSBwcmVyZW5kZXJzJyBjYWNoZSBrZXlzIGJ5IHRhZyAoe0BsaW5rIElOSVRfQ0FDSEVfVEFHX01BTklGRVNUfSksXG4gICAqIHJlYWQgZnJvbSB0aGUgY2FjaGUgYnVja2V0IG9uY2UgcGVyIGluc3RhbmNlLCBvciBgdW5kZWZpbmVkYCB3aGVuIHRoYXRcbiAgICogZmFpbGVkLCB0byBiZSByZWFkIGFnYWluIG5leHQgdGltZS4gQSBwcmVyZW5kZXIgaXMgd3JpdHRlbiBieSB0aGUgYWRhcHRlcixcbiAgICogbmV2ZXIgdGhyb3VnaCBgc2V0YCwgc28gdGhpcyBpcyB0aGUgb25seSByZWNvcmQgb2Ygd2hpY2ggdGFncyBuYW1lIGl0c1xuICAgKiBDbG91ZEZyb250IHBhdGhzLCBhbmQgaXQgaXMgc2VydmVkIHdpdGggYSB5ZWFyLWxvbmcgYHMtbWF4YWdlYC5cbiAgICovXG4gIHByaXZhdGUgYnVpbGRUYWdNYW5pZmVzdCgpOiBQcm9taXNlPE1hcDxzdHJpbmcsIHN0cmluZ1tdPiB8IHVuZGVmaW5lZD4ge1xuICAgIHRoaXMuYnVpbGRUYWdzID8/PSB0aGlzLmJ1Y2tldFxuICAgICAgLmdldChgJHt0aGlzLmNvbmZpZy5idWlsZElkfS8ke0lOSVRfQ0FDSEVfVEFHX01BTklGRVNUfWApXG4gICAgICAvLyBOb25lIGlzIG5vcm1hbDogb25seSB0YWdnZWQgcHJlcmVuZGVycyB3cml0ZSBvbmUuXG4gICAgICAudGhlbigob2JqZWN0KSA9PiBuZXcgTWFwKE9iamVjdC5lbnRyaWVzKEpTT04ucGFyc2Uob2JqZWN0ID8/IFwie31cIikpKSk7XG4gICAgcmV0dXJuIHRoaXMuYnVpbGRUYWdzLmNhdGNoKChlcnJvcikgPT4ge1xuICAgICAgdGhpcy5idWlsZFRhZ3MgPSB1bmRlZmluZWQ7XG4gICAgICBjb25zb2xlLndhcm4oXCJDb3VsZCBub3QgcmVhZCB0aGUgYnVpbGQncyB0YWcgbWFuaWZlc3Q6XCIsIGVycm9yKTtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogRXZlcnkgbWFwcGluZyByb3cgZm9yIGB0YWdgLCBmb2xsb3dpbmcgYExhc3RFdmFsdWF0ZWRLZXlgLCBhbmQgd2hldGhlclxuICAgKiB7QGxpbmsgTUFYX1RBR19RVUVSWV9QQUdFU30gY3V0IHRoZSB3YWxrIHNob3J0LlxuICAgKlxuICAgKiBEeW5hbW9EQiBjYXBzIGEgUXVlcnkgYXQgMSBNQiBvZiBpdGVtcyByZWdhcmRsZXNzIG9mIGhvdyBtYW55IG1hdGNoLCBhbmRcbiAgICogdGhlcmUgaXMgb25lIHJvdyBwZXIgdGFnZ2VkIGVudHJ5IGEgcnVudGltZSBgc2V0YCB3cm90ZSwgc28gYSBsYXJnZSBzaXRlXG4gICAqIHBhc3NlcyAxIE1CIG9uIGEgY29tbW9uIHRhZy4gU3RvcHBpbmcgYXQgdGhlXG4gICAqIGZpcnN0IHBhZ2UgaW52YWxpZGF0ZXMgb25seSB0aGF0IHBhZ2UncyBlbnRyaWVzIHdoaWxlIHN0aWxsIHJlcG9ydGluZ1xuICAgKiBzdWNjZXNzLCB3aGljaCBpcyBpbmRpc3Rpbmd1aXNoYWJsZSBmcm9tIHJldmFsaWRhdGlvbiBoYXZpbmcgd29ya2VkLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBxdWVyeVRhZ01hcHBpbmdzKFxuICAgIHRhZzogc3RyaW5nLFxuICApOiBQcm9taXNlPHsgaXRlbXM6IFJlY29yZDxzdHJpbmcsIEF0dHJpYnV0ZVZhbHVlPltdOyB0cnVuY2F0ZWQ6IGJvb2xlYW4gfT4ge1xuICAgIGNvbnN0IGl0ZW1zOiBSZWNvcmQ8c3RyaW5nLCBBdHRyaWJ1dGVWYWx1ZT5bXSA9IFtdO1xuICAgIGxldCBleGNsdXNpdmVTdGFydEtleTogUmVjb3JkPHN0cmluZywgQXR0cmlidXRlVmFsdWU+IHwgdW5kZWZpbmVkO1xuICAgIGxldCBwYWdlcyA9IDA7XG5cbiAgICBkbyB7XG4gICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IHRoaXMuZHluYW1vQ2xpZW50LnNlbmQoXG4gICAgICAgIG5ldyBRdWVyeUNvbW1hbmQoe1xuICAgICAgICAgIFRhYmxlTmFtZTogdGhpcy5jb25maWcudGFibGVOYW1lLFxuICAgICAgICAgIEtleUNvbmRpdGlvbkV4cHJlc3Npb246IFwicGsgPSA6cGsgQU5EIGJlZ2luc193aXRoKHNrLCA6c2tQcmVmaXgpXCIsXG4gICAgICAgICAgRXhwcmVzc2lvbkF0dHJpYnV0ZVZhbHVlczoge1xuICAgICAgICAgICAgXCI6cGtcIjogeyBTOiB0aGlzLmNvbmZpZy5idWlsZElkIH0sXG4gICAgICAgICAgICBcIjpza1ByZWZpeFwiOiB7IFM6IGAke3RhZ30jYCB9LFxuICAgICAgICAgIH0sXG4gICAgICAgICAgRXhjbHVzaXZlU3RhcnRLZXk6IGV4Y2x1c2l2ZVN0YXJ0S2V5LFxuICAgICAgICB9KSxcbiAgICAgICk7XG4gICAgICBpdGVtcy5wdXNoKC4uLihyZXNwb25zZS5JdGVtcyA/PyBbXSkpO1xuICAgICAgZXhjbHVzaXZlU3RhcnRLZXkgPSByZXNwb25zZS5MYXN0RXZhbHVhdGVkS2V5O1xuICAgICAgcGFnZXMrKztcbiAgICB9IHdoaWxlIChleGNsdXNpdmVTdGFydEtleSAmJiBwYWdlcyA8IE1BWF9UQUdfUVVFUllfUEFHRVMpO1xuXG4gICAgY29uc3QgdHJ1bmNhdGVkID0gZXhjbHVzaXZlU3RhcnRLZXkgIT09IHVuZGVmaW5lZDtcbiAgICBpZiAodHJ1bmNhdGVkKSB7XG4gICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgIGBTdG9wcGVkIHBhZ2luYXRpbmcgdGFnIFwiJHt0YWd9XCIgYWZ0ZXIgJHtwYWdlc30gcGFnZXMgYCArXG4gICAgICAgICAgYCgke2l0ZW1zLmxlbmd0aH0gZW50cmllcyk7IGludmFsaWRhdGluZyB0aGUgd2hvbGUgYXBwIGluc3RlYWQuYCxcbiAgICAgICk7XG4gICAgfVxuICAgIHJldHVybiB7IGl0ZW1zLCB0cnVuY2F0ZWQgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXZlcnNlcyBgYnVpbGRTM0tleWAgdG8gcmVjb3ZlciB0aGUgcm91dGUgdGhlIHJlc3BvbnNlIHdhcyBjYWNoZWQgZm9yLCBmb3JcbiAgICoge0BsaW5rIGNkbkludmFsaWRhdGlvblBhdGhzfSB0byB0dXJuIGludG8gdGhlIFVSSXMgQ2xvdWRGcm9udCBob2xkcyBpdCB1bmRlciwgb3JcbiAgICogYHVuZGVmaW5lZGAgZm9yIGEga2V5IHRoYXQgbmFtZXMgbm8gY2FjaGVkIFVSSS5cbiAgICpcbiAgICogVHdvIGtpbmRzIG9mIG1hcHBpbmcgcm93IGRvIG5vdDogYSBkeW5hbWljIHJvdXRlIHRlbXBsYXRlIChgYmxvZy9bc2x1Z10uanNvbmAsXG4gICAqIHdoaWNoIHRoZSBhZGFwdGVyJ3Mgc2VlZGVkIGVudHJpZXMgaW5jbHVkZSksIGFuZCBhIGZldGNoLWNhY2hlIGVudHJ5LCB3aG9zZVxuICAgKiBrZXkgaXMgYW4gb3BhcXVlIGhhc2ggcmF0aGVyIHRoYW4gYSByb3V0ZS4gQm90aCB3b3VsZCBpbnZhbGlkYXRlIGEgVVJJIHRoYXRcbiAgICogY2Fubm90IGV4aXN0LCBhbmQgZWFjaCBvbmUgY29zdHMgb25lIG9mIHRoZSBmaWZ0ZWVuIHdpbGRjYXJkIHBhdGhzIGFuXG4gICAqIGludmFsaWRhdGlvbiBtYXkgY2Fycnkg4oCUIHNvIHRoZXkgYXJlIGRyb3BwZWQgaGVyZSByYXRoZXIgdGhhbiBjcm93ZGluZyBvdXRcbiAgICogdGhlIHJlYWwgb25lcy5cbiAgICovXG4gIHByaXZhdGUgczNLZXlUb0ludmFsaWRhdGlvblBhdGgoczNLZXk6IHN0cmluZyk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gICAgY29uc3QgcHJlZml4ID0gYCR7dGhpcy5jb25maWcuYnVpbGRJZH0vYDtcbiAgICBjb25zdCB3aXRob3V0UHJlZml4ID0gczNLZXkuc3RhcnRzV2l0aChwcmVmaXgpXG4gICAgICA/IHMzS2V5LnNsaWNlKHByZWZpeC5sZW5ndGgpXG4gICAgICA6IHMzS2V5O1xuICAgIGNvbnN0IHdpdGhvdXRTdWZmaXggPSB3aXRob3V0UHJlZml4LmVuZHNXaXRoKFwiLmpzb25cIilcbiAgICAgID8gd2l0aG91dFByZWZpeC5zbGljZSgwLCAtXCIuanNvblwiLmxlbmd0aClcbiAgICAgIDogd2l0aG91dFByZWZpeDtcblxuICAgIGlmICh3aXRob3V0U3VmZml4LmluY2x1ZGVzKFwiW1wiKSB8fCBpc0ZldGNoQ2FjaGVLZXkod2l0aG91dFN1ZmZpeCkpIHtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuICAgIHJldHVybiB3aXRob3V0U3VmZml4ID09PSBcImluZGV4XCIgPyBcIi9cIiA6IGAvJHt3aXRob3V0U3VmZml4fWA7XG4gIH1cblxuICAvKipcbiAgICogSW52YWxpZGF0ZSBgcGF0aHNgIGluIGEgc2luZ2xlIHJlcXVlc3QsIG9yIHRoZSB3aG9sZSBhcHBcbiAgICogKHtAbGluayB3aG9sZUFwcEludmFsaWRhdGlvblBhdGhzfSkgd2hlbiB0aGV5IHdvdWxkIG5vdCBmaXQgaW4gb25lLiBTZWVcbiAgICoge0BsaW5rIE1BWF9XSUxEQ0FSRF9QQVRIU19QRVJfSU5WQUxJREFUSU9OfS5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgaW52YWxpZGF0ZUNsb3VkRnJvbnRQYXRocyhwYXRoczogc3RyaW5nW10pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBsZXQgYmF0Y2ggPSBBcnJheS5mcm9tKG5ldyBTZXQocGF0aHMpKTtcbiAgICBpZiAoYmF0Y2gubGVuZ3RoID09PSAwKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgZGlzdHJpYnV0aW9uSWQgPSBhd2FpdCB0aGlzLmdldERpc3RyaWJ1dGlvbklkKCkuY2F0Y2goKGVycm9yKSA9PiB7XG4gICAgICBjb25zb2xlLndhcm4oXCJDb3VsZCBub3QgcmVzb2x2ZSB0aGUgZGlzdHJpYnV0aW9uIHRvIGludmFsaWRhdGU6XCIsIGVycm9yKTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH0pO1xuICAgIGlmICghZGlzdHJpYnV0aW9uSWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCB3aWxkY2FyZHMgPSBiYXRjaC5maWx0ZXIoaXNXaWxkY2FyZFBhdGgpLmxlbmd0aDtcbiAgICBpZiAoXG4gICAgICB3aWxkY2FyZHMgPiBNQVhfV0lMRENBUkRfUEFUSFNfUEVSX0lOVkFMSURBVElPTiB8fFxuICAgICAgYmF0Y2gubGVuZ3RoID4gTUFYX1BBVEhTX1BFUl9JTlZBTElEQVRJT05cbiAgICApIHtcbiAgICAgIGNvbnN0IHdob2xlQXBwID0gd2hvbGVBcHBJbnZhbGlkYXRpb25QYXRocyhcbiAgICAgICAgdGhpcy5jbG91ZEZyb250Q29uZmlnLmJhc2VQYXRoLFxuICAgICAgKTtcbiAgICAgIHRoaXMuZGVidWcoXG4gICAgICAgIGBDTE9VREZST05UIElOVkFMSURBVElPTjogJHtiYXRjaC5sZW5ndGh9IHBhdGhzICgke3dpbGRjYXJkc30gYCArXG4gICAgICAgICAgYHdpbGRjYXJkcykgZG8gbm90IGZpdCBvbmUgcmVxdWVzdCwgY29sbGFwc2luZyB0byBgICtcbiAgICAgICAgICBgWyR7d2hvbGVBcHAuam9pbihcIiwgXCIpfV1gLFxuICAgICAgKTtcbiAgICAgIGJhdGNoID0gd2hvbGVBcHA7XG4gICAgfVxuXG4gICAgZm9yIChsZXQgYXR0ZW1wdCA9IDA7IDsgYXR0ZW1wdCsrKSB7XG4gICAgICB0aGlzLmRlYnVnKFxuICAgICAgICBgQ0xPVURGUk9OVCBJTlZBTElEQVRJT046IFske2JhdGNoLmpvaW4oXCIsIFwiKX1dIG9uIGRpc3RyaWJ1dGlvbiAke2Rpc3RyaWJ1dGlvbklkfWAsXG4gICAgICApO1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgdGhpcy5jbG91ZEZyb250Q2xpZW50LnNlbmQoXG4gICAgICAgICAgbmV3IENyZWF0ZUludmFsaWRhdGlvbkNvbW1hbmQoe1xuICAgICAgICAgICAgRGlzdHJpYnV0aW9uSWQ6IGRpc3RyaWJ1dGlvbklkLFxuICAgICAgICAgICAgSW52YWxpZGF0aW9uQmF0Y2g6IHtcbiAgICAgICAgICAgICAgQ2FsbGVyUmVmZXJlbmNlOiByYW5kb21VVUlEKCksXG4gICAgICAgICAgICAgIFBhdGhzOiB7XG4gICAgICAgICAgICAgICAgUXVhbnRpdHk6IGJhdGNoLmxlbmd0aCxcbiAgICAgICAgICAgICAgICBJdGVtczogYmF0Y2gsXG4gICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH0pLFxuICAgICAgICApO1xuICAgICAgICByZXR1cm47XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBpZiAoaXNJbnZhbGlkYXRpb25RdW90YUVycm9yKGVycm9yKSAmJiBhdHRlbXB0IDwgSU5WQUxJREFUSU9OX1JFVFJJRVMpIHtcbiAgICAgICAgICAvLyBTZWUgYElOVkFMSURBVElPTl9SRVRSSUVTYC5cbiAgICAgICAgICBiYXRjaCA9IHdob2xlQXBwSW52YWxpZGF0aW9uUGF0aHModGhpcy5jbG91ZEZyb250Q29uZmlnLmJhc2VQYXRoKTtcbiAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT5cbiAgICAgICAgICAgIHNldFRpbWVvdXQocmVzb2x2ZSwgSU5WQUxJREFUSU9OX1JFVFJZX0RFTEFZX01TICogMiAqKiBhdHRlbXB0KSxcbiAgICAgICAgICApO1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIC8vIExvZyBidXQgZG9uJ3QgZmFpbCAtIHRoZSBTMy9EeW5hbW9EQiBpbnZhbGlkYXRpb24gYWxyZWFkeSBzdWNjZWVkZWQsXG4gICAgICAgIC8vIGFuZCB0aGUgQ2xvdWRGcm9udCBjYWNoZSBwb2xpY3kgVFRMIHByb3ZpZGVzIGFuIGV2ZW50dWFsIGZhbGxiYWNrLlxuICAgICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgICAgYEZhaWxlZCB0byBjcmVhdGUgQ2xvdWRGcm9udCBpbnZhbGlkYXRpb24gZm9yIFske2JhdGNoLmpvaW4oXCIsIFwiKX1dOmAsXG4gICAgICAgICAgZXJyb3IsXG4gICAgICAgICk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVGhlIGRpc3RyaWJ1dGlvbidzIHBoeXNpY2FsIElELCBmcm9tIHRoZSBlbnZpcm9ubWVudCBvciBlbHNlIFNTTSBQYXJhbWV0ZXJcbiAgICogU3RvcmUsIGNhY2hlZCBmb3IgdGhlIGxpZmV0aW1lIG9mIHRoaXMgaW5zdGFuY2UuIFNlZVxuICAgKiBgQ2xvdWRGcm9udEludmFsaWRhdGlvbkNvbmZpZ2AgZm9yIHdoeSBGdW5jdGlvbnMgbmVlZCB0aGUgaW5kaXJlY3Rpb24uXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGdldERpc3RyaWJ1dGlvbklkKCk6IFByb21pc2U8c3RyaW5nIHwgbnVsbD4ge1xuICAgIGlmICh0aGlzLmNsb3VkRnJvbnRDb25maWcuZGlzdHJpYnV0aW9uSWQpIHtcbiAgICAgIHJldHVybiB0aGlzLmNsb3VkRnJvbnRDb25maWcuZGlzdHJpYnV0aW9uSWQ7XG4gICAgfVxuICAgIGlmICh0aGlzLmNhY2hlZERpc3RyaWJ1dGlvbklkKSB7XG4gICAgICByZXR1cm4gdGhpcy5jYWNoZWREaXN0cmlidXRpb25JZDtcbiAgICB9XG5cbiAgICBpZiAoIXRoaXMuY2xvdWRGcm9udENvbmZpZy5kaXN0cmlidXRpb25JZFBhcmFtZXRlck5hbWUpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgdGhpcy5zc21DbGllbnQuc2VuZChcbiAgICAgIG5ldyBHZXRQYXJhbWV0ZXJDb21tYW5kKHtcbiAgICAgICAgTmFtZTogdGhpcy5jbG91ZEZyb250Q29uZmlnLmRpc3RyaWJ1dGlvbklkUGFyYW1ldGVyTmFtZSxcbiAgICAgIH0pLFxuICAgICk7XG5cbiAgICB0aGlzLmNhY2hlZERpc3RyaWJ1dGlvbklkID0gcmVzcG9uc2UuUGFyYW1ldGVyPy5WYWx1ZSB8fCBudWxsO1xuICAgIHJldHVybiB0aGlzLmNhY2hlZERpc3RyaWJ1dGlvbklkO1xuICB9XG5cbiAgYXN5bmMgcmVzZXRSZXF1ZXN0Q2FjaGUoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8gVGhpcyBoYW5kbGVyIGRvZXNuJ3QgbWFpbnRhaW4gcmVxdWVzdC1sZXZlbCBjYWNoZSBzdGF0ZVxuICAgIC8vIFRoZSBhY3R1YWwgY2FjaGUgY2xlYXJpbmcgaXMgaGFuZGxlZCBieSB0aGUgbWVtb3J5IGNhY2hlIGhhbmRsZXJcbiAgfVxuXG4gIC8qKlxuICAgKiBXaGV0aGVyIHRoaXMgZGVwbG95bWVudCBrZWVwcyBgdGFnI3MzS2V5YCBtYXBwaW5nIHJvd3MgYXQgYWxsLlxuICAgKlxuICAgKiBUaGVpciBvbmUgcmVhZGVyIGlzIGByZXZhbGlkYXRlVGFnYCdzIENsb3VkRnJvbnQgaW52YWxpZGF0aW9uLCB3aGljaCBuZWVkc1xuICAgKiB0aGUgZW50cmllcycgcGF0aHMgLSB0YWcgcmV2YWxpZGF0aW9uIGl0c2VsZiBydW5zIG9uIHRoZSBiYXJlLXRhZyBtYXJrZXJcbiAgICogcm93cy4gQSBkZXBsb3ltZW50IHdpdGggbm8gZGlzdHJpYnV0aW9uICh0aGUgUmVnaW9uYWwgY29uc3RydWN0cykgd291bGQgcGF5XG4gICAqIGEgRHluYW1vREIgd3JpdGUgcGVyIHRhZyBvbiBldmVyeSBgc2V0YCwgYSBRdWVyeSBwZXIgYHJldmFsaWRhdGVUYWdgLCBhbmQgYW5cbiAgICogZXh0cmEgUzMgcmVhZCBiZWZvcmUgZXZlcnkgZGVsZXRlLCBmb3Igcm93cyBub3RoaW5nIGV2ZXIgcmVhZHMuXG4gICAqL1xuICBwcml2YXRlIGdldCBtYXBzVGFnc1RvUGF0aHMoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIEJvb2xlYW4odGhpcy5jb25maWcudGFibGVOYW1lKSAmJiB0aGlzLmludmFsaWRhdGVzQ2RuO1xuICB9XG5cbiAgLyoqIFdoZXRoZXIgdGhlcmUgaXMgYSBkaXN0cmlidXRpb24gdG8gaW52YWxpZGF0ZS4gKi9cbiAgcHJpdmF0ZSBnZXQgaW52YWxpZGF0ZXNDZG4oKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIEJvb2xlYW4oXG4gICAgICB0aGlzLmNsb3VkRnJvbnRDb25maWcuZGlzdHJpYnV0aW9uSWQgfHxcbiAgICAgIHRoaXMuY2xvdWRGcm9udENvbmZpZy5kaXN0cmlidXRpb25JZFBhcmFtZXRlck5hbWUsXG4gICAgKTtcbiAgfVxuXG4gIC8qKiBge2J1aWxkSWR9L3tjYWNoZUtleX0uanNvbmA7IHNlZSB7QGxpbmsgYnVpbGRTM0tleX0uICovXG4gIHByaXZhdGUgYnVpbGRTM0tleShjYWNoZUtleTogc3RyaW5nKTogc3RyaW5nIHtcbiAgICByZXR1cm4gYnVpbGRTM0tleSh0aGlzLmNvbmZpZy5idWlsZElkLCBjYWNoZUtleSk7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHN0b3JlRHluYW1vREJUYWdNYXBwaW5ncyhcbiAgICBzM0tleTogc3RyaW5nLFxuICAgIHRhZ3M6IHN0cmluZ1tdLFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgdXBkYXRlUHJvbWlzZXMgPSB0YWdzLm1hcChhc3luYyAodGFnKSA9PiB7XG4gICAgICAgIGNvbnN0IHRhZ0NhY2hlS2V5ID0gYCR7dGFnfSMke3MzS2V5fWA7XG4gICAgICAgIGNvbnN0IHVwZGF0ZUNvbW1hbmQgPSBuZXcgVXBkYXRlSXRlbUNvbW1hbmQoe1xuICAgICAgICAgIFRhYmxlTmFtZTogdGhpcy5jb25maWcudGFibGVOYW1lLFxuICAgICAgICAgIEtleToge1xuICAgICAgICAgICAgcGs6IHsgUzogdGhpcy5jb25maWcuYnVpbGRJZCB9LFxuICAgICAgICAgICAgc2s6IHsgUzogdGFnQ2FjaGVLZXkgfSxcbiAgICAgICAgICB9LFxuICAgICAgICAgIC8vIERlbGliZXJhdGVseSBubyBgcmV2YWxpZGF0ZWRBdGA6IGEgbWFwcGluZyByb3cgcmVjb3JkcyBvbmx5IHRoYXQgYW5cbiAgICAgICAgICAvLyBlbnRyeSBjYXJyaWVzIHRoZSB0YWcuIFN0YW1waW5nIGl0IGF0IHdyaXRlIHRpbWUgbWFkZSBldmVyeSB0YWdnZWRcbiAgICAgICAgICAvLyBlbnRyeSBsb29rIHJldmFsaWRhdGVkIG9uZSBtaWxsaXNlY29uZCBhZnRlciBpdCB3YXMgc3RvcmVkIOKAlCB0aGVcbiAgICAgICAgICAvLyByb3cncyB0aW1lc3RhbXAgaXMgdGFrZW4gYWZ0ZXIgdGhlIGVudHJ5J3MgYGxhc3RNb2RpZmllZGAg4oCUIHNvXG4gICAgICAgICAgLy8gYGNoZWNrSWZSZXZhbGlkYXRlZGAgZGVsZXRlZCBoZWFsdGh5IGVudHJpZXMgYW5kIHRoZSBwYWdlXG4gICAgICAgICAgLy8gcmUtcmVuZGVyZWQgb24gZXZlcnkgcmVxdWVzdC5cbiAgICAgICAgICBVcGRhdGVFeHByZXNzaW9uOiBcIlNFVCBjcmVhdGVkQXQgPSBpZl9ub3RfZXhpc3RzKGNyZWF0ZWRBdCwgOm5vdylcIixcbiAgICAgICAgICBFeHByZXNzaW9uQXR0cmlidXRlVmFsdWVzOiB7XG4gICAgICAgICAgICBcIjpub3dcIjogeyBOOiBEYXRlLm5vdygpLnRvU3RyaW5nKCkgfSxcbiAgICAgICAgICB9LFxuICAgICAgICB9KTtcblxuICAgICAgICBhd2FpdCB0aGlzLmR5bmFtb0NsaWVudC5zZW5kKHVwZGF0ZUNvbW1hbmQpO1xuICAgICAgfSk7XG5cbiAgICAgIGF3YWl0IFByb21pc2UuYWxsKHVwZGF0ZVByb21pc2VzKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgY29uc29sZS5lcnJvcihcIkVycm9yIHN0b3JpbmcgRHluYW1vREIgdGFnIG1hcHBpbmdzOlwiLCBlcnJvcik7XG4gICAgICAvLyBEb24ndCB0aHJvdyAtIGNhY2hlIHN0b3JhZ2Ugc2hvdWxkIGNvbnRpbnVlIGV2ZW4gaWYgdGFnIG1hcHBpbmcgZmFpbHNcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRHJvcCB0aGUgYHRhZyNzM0tleWAgbWFwcGluZyByb3dzIGZvciBhbiBlbnRyeSB0aGF0IG5vIGxvbmdlciBleGlzdHMuXG4gICAqXG4gICAqIFRoZSBjb3VudGVycGFydCBvZiB7QGxpbmsgc3RvcmVEeW5hbW9EQlRhZ01hcHBpbmdzfSwgYW5kIHRoZSByZWFzb24gaXRzXG4gICAqIGNhbGxlciBoYXMgdG8ga25vdyB0aGUgZW50cnkncyB0YWdzOiBhIG1hcHBpbmcgcm93J3Mgc29ydCBrZXkgc3RhcnRzIHdpdGhcbiAgICogdGhlIHRhZywgc28gdGhlIHJvd3MgYmVsb25naW5nIHRvIG9uZSBjYWNoZSBrZXkgY2Fubm90IGJlIHF1ZXJpZWQgZm9yIC0gb25seVxuICAgKiBhIGZ1bGwgc2NhbiBvZiB0aGUgYnVpbGQncyBwYXJ0aXRpb24gd291bGQgZmluZCB0aGVtLlxuICAgKlxuICAgKiBMZWZ0IGJlaGluZCwgYSByb3cgc3RpbGwgcmVzb2x2ZXMgdG8gYSBjYWNoZSBrZXkgb24gdGhlIG5leHRcbiAgICogYHJldmFsaWRhdGVUYWdgLCB3aGljaCBzcGVuZHMgb25lIG9mIENsb3VkRnJvbnQncyBmaWZ0ZWVuIHdpbGRjYXJkIHBhdGhzXG4gICAqIGludmFsaWRhdGluZyBhIFVSSSB3aG9zZSBlbnRyeSB3YXMgZGVsZXRlZCAtIGNyb3dkaW5nIG91dCB0aGUgcGF0aHMgdGhhdCBkb1xuICAgKiBuZWVkIGl0LCBhbmQgcGFzdCB7QGxpbmsgTUFYX1dJTERDQVJEX1BBVEhTX1BFUl9JTlZBTElEQVRJT059IGNvbGxhcHNpbmcgdGhlXG4gICAqIHdob2xlIGFwcCBpbnRvIG9uZSB3aWxkY2FyZC4gVGhlIHJvd3MgYWxzbyBjb3VudCBhZ2FpbnN0IHRoZSAxIE1CIGEgdGFnJ3MgUXVlcnkgcmV0dXJucywgc29cbiAgICogZW5vdWdoIG9mIHRoZW0gcHVzaCBsaXZlIGVudHJpZXMgb250byBhIHBhZ2VcbiAgICoge0BsaW5rIE1BWF9UQUdfUVVFUllfUEFHRVN9IG1heSBub3QgcmVhY2guXG4gICAqXG4gICAqIE9ubHkgdGhlIG1hcHBpbmcgcm93czogdGhlIGJhcmUtYHRhZ2AgbWFya2VyIHJvdyBgcmV2YWxpZGF0ZVRhZ2BcbiAgICogd3JpdGVzIGJlbG9uZ3MgdG8gdGhlIHRhZyByYXRoZXIgdGhhbiB0byBhbnkgZW50cnksIGFuZCBgY2hlY2tJZlJldmFsaWRhdGVkYFxuICAgKiByZWFkcyBpdCBmb3IgZXZlcnkgKm90aGVyKiBlbnRyeSBjYXJyeWluZyB0aGF0IHRhZy5cbiAgICpcbiAgICogRGVsaWJlcmF0ZWx5IG5vIENsb3VkRnJvbnQgaW52YWxpZGF0aW9uIGZvciB0aGUgZGVsZXRlZCBwYXRoLiBUaGUgZWRnZSBjb3B5XG4gICAqIGRvZXMgb3V0bGl2ZSB0aGUgb2JqZWN0LCBidXQgdGhpcyBydW5zIG9uIGV2ZXJ5IHJldmFsaWRhdGVkIGBmZXRjaGAgZW50cnkgYXNcbiAgICogd2VsbCwgd2hlcmUgdGhlIGtleSBuYW1lcyBubyBVUkksIGFuZCBhbiBpbnZhbGlkYXRpb24gcmVxdWVzdCBwZXIgZGVsZXRlZFxuICAgKiBlbnRyeSBpcyBhIGNvc3QgdGhlIFRUTCBhbHJlYWR5IGJvdW5kcy5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZGVsZXRlRHluYW1vREJUYWdNYXBwaW5ncyhcbiAgICBzM0tleTogc3RyaW5nLFxuICAgIHRhZ3M6IHN0cmluZ1tdLFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAoIXRoaXMubWFwc1RhZ3NUb1BhdGhzIHx8IHRhZ3MubGVuZ3RoID09PSAwKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgICAgLy8gQSBwYWdlJ3MgdGFncyBjYW4gcmVwZWF0IC0gYGN0eC50YWdzYCBhbmQgdGhlIHJlbmRlcidzIGhlYWRlciBhcmUgYm90aFxuICAgICAgICAvLyByZWFkIC0gYW5kIG9uZSBkZWxldGUgcGVyIHRhZyBpcyBlbm91Z2guXG4gICAgICAgIEFycmF5LmZyb20obmV3IFNldCh0YWdzKSkubWFwKGFzeW5jICh0YWcpID0+IHtcbiAgICAgICAgICBhd2FpdCB0aGlzLmR5bmFtb0NsaWVudC5zZW5kKFxuICAgICAgICAgICAgbmV3IERlbGV0ZUl0ZW1Db21tYW5kKHtcbiAgICAgICAgICAgICAgVGFibGVOYW1lOiB0aGlzLmNvbmZpZy50YWJsZU5hbWUsXG4gICAgICAgICAgICAgIEtleToge1xuICAgICAgICAgICAgICAgIHBrOiB7IFM6IHRoaXMuY29uZmlnLmJ1aWxkSWQgfSxcbiAgICAgICAgICAgICAgICBzazogeyBTOiBgJHt0YWd9IyR7czNLZXl9YCB9LFxuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgfSksXG4gICAgICAgICAgKTtcbiAgICAgICAgfSksXG4gICAgICApO1xuICAgICAgdGhpcy5kZWJ1ZyhgREVMRVRFRCBUQUcgTUFQUElOR1M6ICR7czNLZXl9IC0+IFske3RhZ3Muam9pbihcIiwgXCIpfV1gKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgLy8gTG9nIGJ1dCBkb24ndCBmYWlsOiB0aGUgZW50cnkgaXRzZWxmIGlzIGFscmVhZHkgZ29uZSwgYW5kIGEgc3RhbGVcbiAgICAgIC8vIG1hcHBpbmcgcm93IGNvc3RzIGFuIGludmFsaWRhdGlvbiBwYXRoIHJhdGhlciB0aGFuIGEgd3JvbmcgcmVzcG9uc2UuXG4gICAgICBjb25zb2xlLmVycm9yKFwiRXJyb3IgZGVsZXRpbmcgRHluYW1vREIgdGFnIG1hcHBpbmdzOlwiLCBlcnJvcik7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFRoZSB0YWdzIGFuIGVudHJ5IGFib3V0IHRvIGJlIGRlbGV0ZWQgd2FzIHN0b3JlZCB3aXRoLlxuICAgKlxuICAgKiBgY3R4YCBjYXJyaWVzIHRoZW0gZm9yIGEgYGZldGNoYCBkZWxldGUsIGJ1dCBhIHJlc3BvbnNlIGRlbGV0ZSBhcnJpdmVzIGFzXG4gICAqIGB7IGNhY2hlQ29udHJvbCwgaXNSb3V0ZVBQUkVuYWJsZWQsIGlzRmFsbGJhY2sgfWAgLSBgUmVzcG9uc2VDYWNoZS5zZXRgXG4gICAqIGJ1aWxkcyBpdCB0aGF0IHdheSAtIHNvIHRoZSBlbnRyeSBpdHNlbGYgaXMgdGhlIG9ubHkgc291cmNlLCBhbmQgaXQgaGFzIHRvXG4gICAqIGJlIHJlYWQgYmVmb3JlIGl0IGlzIHJlbW92ZWQuIFRoYXQgaXMgb25lIGV4dHJhIEdFVCBvbiBhIHBhdGggdGhhdCBydW5zIHdoZW5cbiAgICogYSBjYWNoZWQgcm91dGUgc3RhcnRzIGFuc3dlcmluZyBgbm90Rm91bmQoKWA6IGBJbmNyZW1lbnRhbENhY2hlLnNldGBcbiAgICogZm9yd2FyZHMgYFJlc3BvbnNlQ2FjaGVgJ3MgbnVsbCB2YWx1ZSB0aHJvdWdoIHRvIGhlcmUuXG4gICAqXG4gICAqIEEgcGFnZSBpcyBhbHNvIHRoZSBjYXNlIHdvcnRoIHBheWluZyBpdCBmb3IsIHNpbmNlIGl0IGNhcnJpZXMgdGhlIHdob2xlXG4gICAqIGltcGxpY2l0IGBfTl9UXy/igKZgIGNoYWluIGFuZCB0aGVyZWZvcmUgdGhlIG1vc3Qgcm93cy5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgc3RvcmVkRW50cnlUYWdzKFxuICAgIHMzS2V5OiBzdHJpbmcsXG4gICAgY3R4OiBTZXRJbmNyZW1lbnRhbEZldGNoQ2FjaGVDb250ZXh0IHwgU2V0SW5jcmVtZW50YWxSZXNwb25zZUNhY2hlQ29udGV4dCxcbiAgKTogUHJvbWlzZTxzdHJpbmdbXT4ge1xuICAgIGNvbnN0IGN0eFRhZ3MgPSBnZXRUYWdzKGN0eCk7XG4gICAgaWYgKGN0eFRhZ3M/Lmxlbmd0aCkge1xuICAgICAgcmV0dXJuIGN0eFRhZ3M7XG4gICAgfVxuICAgIGlmICghdGhpcy5tYXBzVGFnc1RvUGF0aHMpIHtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgdGhpcy5idWNrZXQuZ2V0KHMzS2V5KTtcbiAgICAgIGlmICghcmVzcG9uc2UpIHtcbiAgICAgICAgcmV0dXJuIFtdO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGVudHJ5VGFncyhwYXJzZUNhY2hlVmFsdWUocmVzcG9uc2UpKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgY29uc29sZS53YXJuKGBGYWlsZWQgdG8gcmVhZCB0YWdzIG9mICR7czNLZXl9IGJlZm9yZSBkZWxldGluZzpgLCBlcnJvcik7XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgYW55IG9mIGB0YWdzYCB3YXMgcmV2YWxpZGF0ZWQgYWZ0ZXIgdGhpcyBlbnRyeSB3YXMgc3RvcmVkLCBhbmQgaG93LlxuICAgKlxuICAgKiBSZWFkcyB0aGUgcGVyLXRhZyBtYXJrZXIgcm93cyBgcmV2YWxpZGF0ZVRhZ2Agd3JpdGVzLCBhcyB0aGlzIGluc3RhbmNlXG4gICAqIHRyYWNrcyB0aGVtOiBzZWUgYFRyYWNrZWRUYWdNYXJrZXJzYCBmb3Igd2hlbiB0aGV5IGFyZSByZWFkLlxuICAgKiBTY2FubmluZyB0aGUgdGFnJ3MgbWFwcGluZyByb3dzIGluc3RlYWQgd291bGQgYW5zd2VyIHRoZSB3cm9uZyBxdWVzdGlvbjpcbiAgICogdGhvc2Ugcm93cyBleGlzdCBwZXIgY2FjaGUgKmtleSosIHNvIHdoaWNoIG9uZSBhIGBMaW1pdDogMWAgcXVlcnkgcmV0dXJuZWRcbiAgICogZGVwZW5kZWQgb24gc29ydCBvcmRlciwgYW5kIGFuIGVudHJ5IGNvdWxkIGJlIGp1ZGdlZCBhZ2FpbnN0IGFub3RoZXJcbiAgICogZW50cnkncyB0aW1lc3RhbXAuXG4gICAqXG4gICAqIEEgdGFnIHJldmFsaWRhdGVkIHdpdGggYSBwcm9maWxlIG9ubHkgbWFrZXMgdGhlIGVudHJ5ICpzdGFsZSo6IHRoYXQgaXNcbiAgICogcmVwb3J0ZWQgdG8gTmV4dC5qcyB0aHJvdWdoIGl0cyB0YWcgbWFuaWZlc3QgKHNlZSB7QGxpbmsgbmV4dFRhZ3NNYW5pZmVzdH0pXG4gICAqIGFuZCBhbnN3ZXJlZCBgXCJzdGFsZVwiYCBoZXJlLCBzbyB0aGUgZW50cnkgaXMgc2VydmVkIHdoaWxlIGEgYmFja2dyb3VuZFxuICAgKiByZW5kZXIgcmVwbGFjZXMgaXQuIFdoZW4gdGhlIG1hbmlmZXN0IGNhbm5vdCBiZSByZWFjaGVkIGEgc3RhbGUgZW50cnkgaXNcbiAgICogcmVwb3J0ZWQgZXhwaXJlZCBpbnN0ZWFkIC0gYSBibG9ja2luZyByZW5kZXIsIG5ldmVyIGEgc3RhbGUgcGFnZSBwcmVzZW50ZWRcbiAgICogYXMgZnJlc2guXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGNoZWNrSWZSZXZhbGlkYXRlZChcbiAgICBjYWNoZUxhc3RNb2RpZmllZDogbnVtYmVyLFxuICAgIHRhZ3M6IHN0cmluZ1tdLFxuICApOiBQcm9taXNlPFJldmFsaWRhdGlvblN0YXRlPiB7XG4gICAgYXdhaXQgdGhpcy50YWdzLnJlZnJlc2goKTtcbiAgICBhd2FpdCB0aGlzLnRhZ3MuZW5zdXJlKHRhZ3MpO1xuICAgIGNvbnN0IHN0YXRlID0gdGhpcy50YWdzLnN0YXRlKHRhZ3MsIGNhY2hlTGFzdE1vZGlmaWVkKTtcbiAgICBpZiAoc3RhdGUgIT09IFwic3RhbGVcIikge1xuICAgICAgaWYgKHN0YXRlID09PSBcImV4cGlyZWRcIikge1xuICAgICAgICB0aGlzLmRlYnVnKFxuICAgICAgICAgIGBUYWdzIFske3RhZ3N9XSBleHBpcmVkIGVudHJ5IGNyZWF0ZWQgYXQgJHtjYWNoZUxhc3RNb2RpZmllZH1gLFxuICAgICAgICApO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHN0YXRlO1xuICAgIH1cblxuICAgIGNvbnN0IG1hbmlmZXN0ID0gbmV4dFRhZ3NNYW5pZmVzdCgpO1xuICAgIGlmICghbWFuaWZlc3QpIHtcbiAgICAgIHRoaXMuZGVidWcoXG4gICAgICAgIGBUYWdzIFske3RhZ3N9XSBhcmUgc3RhbGUgYW5kIE5leHQuanMncyB0YWcgbWFuaWZlc3QgaXMgYCArXG4gICAgICAgICAgYHVucmVhY2hhYmxlLCBleHBpcmluZyB0aGUgZW50cnkgaW5zdGVhZGAsXG4gICAgICApO1xuICAgICAgcmV0dXJuIFwiZXhwaXJlZFwiO1xuICAgIH1cbiAgICBmb3IgKGNvbnN0IHRhZyBvZiB0YWdzKSB7XG4gICAgICBjb25zdCBzdGFsZUF0ID0gdGhpcy50YWdzLmdldCh0YWcpPy5zdGFsZUF0O1xuICAgICAgY29uc3QgZXhpc3RpbmcgPSBtYW5pZmVzdC5nZXQodGFnKTtcbiAgICAgIGlmIChcbiAgICAgICAgc3RhbGVBdCAhPT0gdW5kZWZpbmVkICYmXG4gICAgICAgIHN0YWxlQXQgPiBjYWNoZUxhc3RNb2RpZmllZCAmJlxuICAgICAgICAoZXhpc3Rpbmc/LnN0YWxlID8/IDApIDwgc3RhbGVBdFxuICAgICAgKSB7XG4gICAgICAgIG1hbmlmZXN0LnNldCh0YWcsIHsgLi4uZXhpc3RpbmcsIHN0YWxlOiBzdGFsZUF0IH0pO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gXCJzdGFsZVwiO1xuICB9XG59XG4iXX0=
|