cdk-nextjs 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.jsii +2404 -2723
- package/.jsii.tabl.json +1 -1
- package/API.md +1681 -1930
- package/CLAUDE.md +2 -1
- package/README.md +429 -130
- package/assets/lambdas/post-deploy/post-deploy.lambda/index.js +148 -169
- package/assets/lambdas/redeploy-stage/redeploy-stage.lambda/index.js +875 -0
- package/docs/adapter-runtime.md +185 -0
- package/docs/breaking-changes.md +667 -4
- package/docs/caching-guide.md +156 -112
- package/docs/cdk-nextjs-cfn-exec-policy.json +2 -1
- package/docs/development-guide.md +1 -1
- package/docs/harness-coverage.md +598 -0
- package/docs/init-cache-deployment.md +32 -1
- package/docs/next-build-output-guide.md +30 -20
- package/examples/README.md +5 -1
- package/lib/adapter/__fixtures__/app-playground-base-path.json +4926 -0
- package/lib/adapter/__fixtures__/app-playground.json +4917 -0
- package/lib/adapter/__fixtures__/pages-i18n.json +1728 -0
- package/lib/adapter/adapter.mjs +1548 -185
- package/lib/adapter/adapter.test.js +106 -0
- package/lib/adapter/aws-cache-store.d.ts +489 -0
- package/lib/adapter/aws-cache-store.js +1004 -0
- package/lib/adapter/aws-cache-store.test.d.ts +1 -0
- package/lib/adapter/aws-cache-store.test.js +361 -0
- package/lib/adapter/build-outputs.d.ts +80 -0
- package/lib/adapter/build-outputs.js +852 -0
- package/lib/adapter/build-outputs.test.d.ts +1 -0
- package/lib/adapter/build-outputs.test.js +861 -0
- package/lib/adapter/cache-handler.d.ts +9 -2
- package/lib/adapter/cache-handler.js +72 -96
- package/lib/adapter/cache-handler.mjs +18956 -17774
- package/lib/adapter/cache-handler.test.js +263 -31
- package/lib/adapter/cache-kinds.d.ts +32 -0
- package/lib/adapter/cache-kinds.js +35 -0
- package/lib/adapter/cache-kinds.test.d.ts +1 -0
- package/lib/adapter/cache-kinds.test.js +69 -0
- package/lib/adapter/cache-utils.d.ts +90 -0
- package/lib/adapter/cache-utils.js +199 -4
- package/lib/adapter/cache-utils.test.d.ts +1 -0
- package/lib/adapter/cache-utils.test.js +179 -0
- package/lib/adapter/function-groups.d.ts +215 -0
- package/lib/adapter/function-groups.js +1099 -0
- package/lib/adapter/function-groups.test.d.ts +1 -0
- package/lib/adapter/function-groups.test.js +743 -0
- package/lib/adapter/init-cache.d.ts +6 -0
- package/lib/adapter/init-cache.js +245 -0
- package/lib/adapter/init-cache.test.d.ts +1 -0
- package/lib/adapter/init-cache.test.js +137 -0
- package/lib/adapter/local-file-cache-handler.d.ts +23 -0
- package/lib/adapter/local-file-cache-handler.js +52 -1
- package/lib/adapter/memory-cache-handler.d.ts +26 -17
- package/lib/adapter/memory-cache-handler.js +32 -50
- package/lib/adapter/memory-cache-handler.test.js +105 -85
- package/lib/adapter/s3-cache-handler.d.ts +179 -40
- package/lib/adapter/s3-cache-handler.js +807 -277
- package/lib/adapter/s3-cache-handler.test.js +1186 -31
- package/lib/adapter/use-cache-common.d.ts +90 -0
- package/lib/adapter/use-cache-common.js +250 -0
- package/lib/adapter/use-cache-common.test.d.ts +1 -0
- package/lib/adapter/use-cache-common.test.js +646 -0
- package/lib/adapter/use-cache-default-handler.d.ts +24 -0
- package/lib/adapter/use-cache-default-handler.js +90 -0
- package/lib/adapter/use-cache-default-handler.mjs +38645 -0
- package/lib/adapter/use-cache-handlers.test.d.ts +1 -0
- package/lib/adapter/use-cache-handlers.test.js +349 -0
- package/lib/adapter/use-cache-remote-handler.d.ts +26 -0
- package/lib/adapter/use-cache-remote-handler.js +186 -0
- package/lib/{image-optimization/handler.mjs → adapter/use-cache-remote-handler.mjs} +33572 -38798
- package/lib/generated-structs/OptionalNextjsApiProps.d.ts +59 -0
- package/lib/generated-structs/OptionalNextjsApiProps.js +3 -0
- package/lib/generated-structs/OptionalNextjsCacheProps.d.ts +15 -6
- package/lib/generated-structs/OptionalNextjsCacheProps.js +1 -1
- package/lib/generated-structs/OptionalNextjsContainersProps.d.ts +25 -11
- package/lib/generated-structs/OptionalNextjsContainersProps.js +1 -1
- package/lib/generated-structs/OptionalNextjsDistributionProps.d.ts +31 -9
- package/lib/generated-structs/OptionalNextjsDistributionProps.js +1 -1
- package/lib/generated-structs/OptionalNextjsPostDeployProps.d.ts +8 -0
- package/lib/generated-structs/OptionalNextjsPostDeployProps.js +1 -1
- package/lib/generated-structs/OptionalPostDeployCustomResourceProperties.d.ts +6 -4
- package/lib/generated-structs/OptionalPostDeployCustomResourceProperties.js +1 -1
- package/lib/index.d.ts +6 -7
- package/lib/index.js +2 -4
- package/lib/lambdas/post-deploy/prune-cache-bucket.d.ts +7 -3
- package/lib/lambdas/post-deploy/prune-cache-bucket.js +57 -71
- package/lib/lambdas/post-deploy/prune-cache-bucket.test.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-cache-bucket.test.js +114 -0
- package/lib/lambdas/post-deploy/prune-revalidation-table.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-revalidation-table.js +23 -31
- package/lib/lambdas/post-deploy/prune-revalidation-table.test.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-revalidation-table.test.js +71 -0
- package/lib/lambdas/post-deploy/prune-s3.d.ts +14 -5
- package/lib/lambdas/post-deploy/prune-s3.js +41 -12
- package/lib/lambdas/post-deploy/prune-s3.test.js +97 -18
- package/lib/lambdas/redeploy-stage/redeploy-stage-function.d.ts +13 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage-function.js +55 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.d.ts +24 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.js +68 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.lambda.d.ts +20 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.lambda.js +24 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.test.d.ts +1 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.test.js +93 -0
- package/lib/nextjs-api.d.ts +102 -18
- package/lib/nextjs-api.js +338 -38
- package/lib/nextjs-api.test.js +537 -1
- package/lib/nextjs-build/global-containers.Dockerfile +15 -10
- package/lib/nextjs-build/nextjs-build.d.ts +339 -63
- package/lib/nextjs-build/nextjs-build.js +684 -327
- package/lib/nextjs-build/nextjs-build.test.d.ts +1 -0
- package/lib/nextjs-build/nextjs-build.test.js +412 -0
- package/lib/nextjs-build/patch-fetch.js +1 -1
- package/lib/nextjs-build/patch-fetch.test.js +166 -11
- package/lib/nextjs-build/regional-containers.Dockerfile +17 -10
- package/lib/nextjs-cache.d.ts +82 -4
- package/lib/nextjs-cache.js +186 -10
- package/lib/nextjs-cache.test.js +176 -1
- package/lib/nextjs-compute/nextjs-compute-base-props.d.ts +21 -8
- package/lib/nextjs-compute/nextjs-compute-base-props.js +41 -1
- package/lib/nextjs-compute/nextjs-compute-base-props.test.d.ts +1 -0
- package/lib/nextjs-compute/nextjs-compute-base-props.test.js +44 -0
- package/lib/nextjs-compute/nextjs-containers.d.ts +19 -0
- package/lib/nextjs-compute/nextjs-containers.js +57 -20
- package/lib/nextjs-compute/nextjs-functions.d.ts +122 -7
- package/lib/nextjs-compute/nextjs-functions.js +113 -67
- package/lib/nextjs-compute/nextjs-functions.test.d.ts +1 -0
- package/lib/nextjs-compute/nextjs-functions.test.js +99 -0
- package/lib/nextjs-distribution.d.ts +194 -23
- package/lib/nextjs-distribution.js +535 -62
- package/lib/nextjs-distribution.test.d.ts +1 -0
- package/lib/nextjs-distribution.test.js +697 -0
- package/lib/nextjs-post-deploy.d.ts +13 -5
- package/lib/nextjs-post-deploy.js +13 -10
- package/lib/nextjs-post-deploy.test.d.ts +1 -0
- package/lib/nextjs-post-deploy.test.js +34 -0
- package/lib/nextjs-static-assets.d.ts +17 -4
- package/lib/nextjs-static-assets.js +22 -5
- package/lib/root-constructs/nextjs-base-construct.d.ts +102 -58
- package/lib/root-constructs/nextjs-base-construct.js +139 -47
- package/lib/root-constructs/nextjs-global-containers.d.ts +18 -20
- package/lib/root-constructs/nextjs-global-containers.js +20 -71
- package/lib/root-constructs/nextjs-global-functions.d.ts +26 -28
- package/lib/root-constructs/nextjs-global-functions.js +52 -70
- package/lib/root-constructs/nextjs-regional-containers.d.ts +18 -5
- package/lib/root-constructs/nextjs-regional-containers.js +8 -15
- package/lib/root-constructs/nextjs-regional-functions.d.ts +22 -18
- package/lib/root-constructs/nextjs-regional-functions.js +8 -26
- package/lib/runtime/api-gateway-path.d.ts +34 -0
- package/lib/runtime/api-gateway-path.js +71 -0
- package/lib/runtime/api-gateway-path.test.d.ts +1 -0
- package/lib/runtime/api-gateway-path.test.js +65 -0
- package/lib/runtime/case-redirect.d.ts +22 -0
- package/lib/runtime/case-redirect.js +63 -0
- package/lib/runtime/case-redirect.test.d.ts +1 -0
- package/lib/runtime/case-redirect.test.js +32 -0
- package/lib/runtime/core.d.ts +234 -0
- package/lib/runtime/core.js +1213 -0
- package/lib/runtime/core.test.d.ts +1 -0
- package/lib/runtime/core.test.js +1678 -0
- package/lib/runtime/deployment-root.d.ts +13 -0
- package/lib/runtime/deployment-root.js +29 -0
- package/lib/runtime/dispatch.d.ts +331 -0
- package/lib/runtime/dispatch.js +751 -0
- package/lib/runtime/dispatch.test.d.ts +1 -0
- package/lib/runtime/dispatch.test.js +853 -0
- package/lib/runtime/entrypoints.d.ts +57 -0
- package/lib/runtime/entrypoints.js +115 -0
- package/lib/runtime/http/abort-signal.d.ts +6 -0
- package/lib/runtime/http/abort-signal.js +19 -0
- package/lib/runtime/http/abort-signal.test.d.ts +1 -0
- package/lib/runtime/http/abort-signal.test.js +49 -0
- package/lib/runtime/http/node-server.d.ts +38 -0
- package/lib/runtime/http/node-server.js +99 -0
- package/lib/runtime/http/node-server.test.d.ts +1 -0
- package/lib/runtime/http/node-server.test.js +96 -0
- package/lib/runtime/http/node-sink.d.ts +16 -0
- package/lib/runtime/http/node-sink.js +48 -0
- package/lib/runtime/http/node-sink.test.d.ts +1 -0
- package/lib/runtime/http/node-sink.test.js +66 -0
- package/lib/runtime/http/request.d.ts +62 -0
- package/lib/runtime/http/request.js +134 -0
- package/lib/runtime/http/request.test.d.ts +1 -0
- package/lib/runtime/http/request.test.js +103 -0
- package/lib/runtime/http/response.d.ts +77 -0
- package/lib/runtime/http/response.js +229 -0
- package/lib/runtime/http/response.test.d.ts +1 -0
- package/lib/runtime/http/response.test.js +178 -0
- package/lib/runtime/http/sink.d.ts +73 -0
- package/lib/runtime/http/sink.js +188 -0
- package/lib/runtime/http/sink.test.d.ts +1 -0
- package/lib/runtime/http/sink.test.js +257 -0
- package/lib/runtime/image-utils.d.ts +78 -0
- package/lib/runtime/image-utils.js +129 -0
- package/lib/runtime/image-utils.test.d.ts +1 -0
- package/lib/runtime/image-utils.test.js +237 -0
- package/lib/runtime/image.d.ts +76 -0
- package/lib/runtime/image.js +296 -0
- package/lib/runtime/image.test.d.ts +1 -0
- package/lib/runtime/image.test.js +405 -0
- package/lib/runtime/lambda-event.d.ts +4 -0
- package/lib/runtime/lambda-event.js +113 -0
- package/lib/runtime/lambda-event.test.d.ts +1 -0
- package/lib/runtime/lambda-event.test.js +119 -0
- package/lib/runtime/lambda.mjs +41595 -0
- package/lib/runtime/load-module.d.ts +8 -0
- package/lib/runtime/load-module.js +35 -0
- package/lib/runtime/manifest.d.ts +215 -0
- package/lib/runtime/manifest.js +118 -0
- package/lib/runtime/middleware.d.ts +43 -0
- package/lib/runtime/middleware.js +113 -0
- package/lib/runtime/middleware.test.d.ts +1 -0
- package/lib/runtime/middleware.test.js +259 -0
- package/lib/runtime/next-modules.d.ts +55 -0
- package/lib/runtime/next-modules.js +124 -0
- package/lib/runtime/next-modules.test.d.ts +1 -0
- package/lib/runtime/next-modules.test.js +34 -0
- package/lib/runtime/public-files.d.ts +32 -0
- package/lib/runtime/public-files.js +117 -0
- package/lib/runtime/public-files.test.d.ts +1 -0
- package/lib/runtime/public-files.test.js +97 -0
- package/lib/runtime/server.mjs +41588 -0
- package/lib/runtime/static-files.d.ts +50 -0
- package/lib/runtime/static-files.js +266 -0
- package/lib/runtime/static-files.test.d.ts +1 -0
- package/lib/runtime/static-files.test.js +118 -0
- package/lib/runtime/util.d.ts +28 -0
- package/lib/runtime/util.js +79 -0
- package/lib/utils/base-path.d.ts +106 -13
- package/lib/utils/base-path.js +254 -41
- package/lib/utils/base-path.test.js +219 -51
- package/lib/utils/get-architecture.d.ts +7 -8
- package/lib/utils/get-architecture.js +12 -13
- package/lib/utils/zip-directory.d.ts +14 -0
- package/lib/utils/zip-directory.js +121 -0
- package/lib/utils/zip-directory.test.d.ts +1 -0
- package/lib/utils/zip-directory.test.js +99 -0
- package/package.json +22 -3
- package/scripts/capture-adapter-fixture.mjs +325 -0
- package/scripts/e2e-deploy.sh +398 -0
- package/scripts/e2e-harness/README.md +707 -0
- package/scripts/e2e-harness/app.js +370 -0
- package/scripts/e2e-harness/common.sh +249 -0
- package/scripts/e2e-harness/screen.mjs +302 -0
- package/scripts/e2e-harness/stage-proxy.mjs +145 -0
- package/scripts/e2e-harness/type-filters.mjs +47 -0
- package/scripts/e2e-logs.sh +75 -0
- package/scripts/e2e-offline.sh +91 -0
- package/scripts/e2e-sweep.sh +370 -0
- package/scripts/e2e-warm.sh +114 -0
- package/scripts/zero-config-build.mjs +418 -0
- package/docs/1k-concurrent-users-executions.png +0 -0
- package/docs/plans/adapter-runtime-release.md +0 -362
- package/docs/plans/phase-0-image-function-opt-in.md +0 -295
- package/lib/generated-structs/OptionalDockerImageFunctionProps.d.ts +0 -425
- package/lib/generated-structs/OptionalDockerImageFunctionProps.js +0 -3
- package/lib/image-optimization/handler-utils.d.ts +0 -34
- package/lib/image-optimization/handler-utils.js +0 -83
- package/lib/image-optimization/handler-utils.test.js +0 -178
- package/lib/nextjs-build/functions.Dockerfile +0 -31
- package/lib/nextjs-compute/nextjs-image-function.d.ts +0 -53
- package/lib/nextjs-compute/nextjs-image-function.js +0 -59
- package/lib/utils/experimental-flags.d.ts +0 -24
- package/lib/utils/experimental-flags.js +0 -30
- /package/lib/{image-optimization/handler-utils.test.d.ts → adapter/adapter.test.d.ts} +0 -0
|
@@ -0,0 +1,1004 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CacheBucket = exports.TrackedTagMarkers = exports.DEFAULT_MAX_TRACKED_TAGS = exports.MAX_REVALIDATION_LOG_GAP_MS = exports.DEFAULT_TAG_RESYNC_MS = exports.REVALIDATION_LOG_LOOKBACK_MS = exports.RevalidationLog = exports.REVALIDATION_LOG_MAX_PAGES = exports.REVALIDATION_LOG_TTL_MS = exports.TagMarkerTable = exports.DEFAULT_TAG_REFRESH_MS = exports.BATCH_GET_MAX_KEYS = exports.USE_CACHE_KEY_PREFIX = void 0;
|
|
4
|
+
exports.resolveAwsCacheConfig = resolveAwsCacheConfig;
|
|
5
|
+
exports.buildS3Key = buildS3Key;
|
|
6
|
+
exports.useCacheS3Key = useCacheS3Key;
|
|
7
|
+
exports.markerState = markerState;
|
|
8
|
+
exports.markerClock = markerClock;
|
|
9
|
+
exports.markerFor = markerFor;
|
|
10
|
+
exports.applyMarker = applyMarker;
|
|
11
|
+
exports.markerUpdate = markerUpdate;
|
|
12
|
+
exports.mergeMarkers = mergeMarkers;
|
|
13
|
+
/*
|
|
14
|
+
The S3 / DynamoDB plumbing shared by every cdk-nextjs cache handler: the
|
|
15
|
+
incremental `cacheHandler` (`S3CacheHandler`) and the `cacheHandlers` for
|
|
16
|
+
`'use cache'` / `'use cache: remote'`.
|
|
17
|
+
|
|
18
|
+
What lives here is what has to agree between them - where the configuration
|
|
19
|
+
comes from, how a cache key becomes an S3 key, and what a tag's marker row in
|
|
20
|
+
the revalidation table looks like and means. A `revalidateTag` reaches both
|
|
21
|
+
kinds of handler, and they must read each other's markers the same way.
|
|
22
|
+
*/
|
|
23
|
+
/* eslint-disable import/no-extraneous-dependencies */
|
|
24
|
+
const node_path_1 = require("node:path");
|
|
25
|
+
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
26
|
+
const client_s3_1 = require("@aws-sdk/client-s3");
|
|
27
|
+
/** The configuration from the environment. */
|
|
28
|
+
function resolveAwsCacheConfig() {
|
|
29
|
+
return {
|
|
30
|
+
bucketName: process.env.CDK_NEXTJS_CACHE_BUCKET_NAME || "",
|
|
31
|
+
tableName: process.env.CDK_NEXTJS_REVALIDATION_TABLE_NAME || "",
|
|
32
|
+
region: process.env.AWS_REGION || "us-east-1",
|
|
33
|
+
buildId: process.env.CDK_NEXTJS_BUILD_ID || "",
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* `{buildId}/{cacheKey}.json`, the incremental cache's key convention.
|
|
38
|
+
*
|
|
39
|
+
* The root path becomes `index`, and a leading slash is dropped so there is no
|
|
40
|
+
* empty folder under the build prefix. Everything this returns ends in `.json`,
|
|
41
|
+
* which is what keeps it disjoint from {@link useCacheS3Key}.
|
|
42
|
+
*/
|
|
43
|
+
function buildS3Key(buildId, cacheKey) {
|
|
44
|
+
let cleanCacheKey = cacheKey;
|
|
45
|
+
if (cacheKey === "/" || cacheKey === "") {
|
|
46
|
+
cleanCacheKey = "index";
|
|
47
|
+
}
|
|
48
|
+
else if (cacheKey.startsWith("/")) {
|
|
49
|
+
cleanCacheKey = cacheKey.slice(1);
|
|
50
|
+
}
|
|
51
|
+
return (0, node_path_1.join)(buildId, `${cleanCacheKey}.json`);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The folder under the build prefix that `'use cache'` entries are stored in.
|
|
55
|
+
* @see useCacheS3Key
|
|
56
|
+
*/
|
|
57
|
+
exports.USE_CACHE_KEY_PREFIX = "_use-cache";
|
|
58
|
+
/**
|
|
59
|
+
* `{buildId}/_use-cache/{hash}.entry`, where a `cacheHandlers` entry is stored.
|
|
60
|
+
*
|
|
61
|
+
* Hashed because a `'use cache'` key is the serialized arguments of the call and
|
|
62
|
+
* has no length limit, where an S3 key stops at 1024 bytes. The `.entry` suffix
|
|
63
|
+
* rather than `.json` is what makes a collision with the incremental cache
|
|
64
|
+
* impossible rather than unlikely: every key {@link buildS3Key} writes ends in
|
|
65
|
+
* `.json`, including one for a route that happens to be named `/_use-cache/…`.
|
|
66
|
+
* Still under the build prefix, so post-deploy pruning drops them with the build.
|
|
67
|
+
*/
|
|
68
|
+
function useCacheS3Key(buildId, keyHash) {
|
|
69
|
+
return (0, node_path_1.join)(buildId, exports.USE_CACHE_KEY_PREFIX, `${keyHash}.entry`);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* What `marker` means for an entry created at `createdAt`, at `now`.
|
|
73
|
+
*
|
|
74
|
+
* `expiredAt` in the future is a profile's `expire` that has not come yet,
|
|
75
|
+
* compared the way Next.js's `areTagsExpired` does.
|
|
76
|
+
*/
|
|
77
|
+
function markerState(marker, createdAt, now) {
|
|
78
|
+
const { revalidatedAt, expiredAt, staleAt } = marker;
|
|
79
|
+
if ((revalidatedAt !== undefined && revalidatedAt > createdAt) ||
|
|
80
|
+
(expiredAt !== undefined && expiredAt <= now && expiredAt > createdAt)) {
|
|
81
|
+
return "expired";
|
|
82
|
+
}
|
|
83
|
+
if (staleAt !== undefined && staleAt > createdAt) {
|
|
84
|
+
return "stale";
|
|
85
|
+
}
|
|
86
|
+
return "fresh";
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The clock every tag marker is stamped with, and every entry timestamp it is
|
|
90
|
+
* compared against: the one Next.js stamps `CacheEntry.timestamp` with
|
|
91
|
+
* (`performance.timeOrigin + performance.now()`, in `use-cache-wrapper.js` and
|
|
92
|
+
* the default handler), rather than `Date.now()`. Both handlers write the same
|
|
93
|
+
* marker rows, so both have to use it: two clocks on one row let the last writer
|
|
94
|
+
* decide which clock the others compare against, and they drift apart for as
|
|
95
|
+
* long as a Lambda sandbox or Fargate task lives.
|
|
96
|
+
*/
|
|
97
|
+
function markerClock() {
|
|
98
|
+
return performance.timeOrigin + performance.now();
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The marker fields a tag revalidated at `now` sets.
|
|
102
|
+
*
|
|
103
|
+
* Without `durations` - `updateTag`, or `revalidateTag` with no profile - the
|
|
104
|
+
* tag's entries expire immediately (`revalidatedAt`). With them the entries go
|
|
105
|
+
* stale now and expire after the profile's `expire`, the same pair of
|
|
106
|
+
* timestamps Next.js's `FileSystemCache.revalidateTag` records. A profile with
|
|
107
|
+
* no `expire` sets only `staleAt`, and whatever `expiredAt` the tag had stays.
|
|
108
|
+
*/
|
|
109
|
+
function markerFor(now, durations) {
|
|
110
|
+
if (!durations) {
|
|
111
|
+
return { revalidatedAt: now };
|
|
112
|
+
}
|
|
113
|
+
if (durations.expire === undefined) {
|
|
114
|
+
return { staleAt: now };
|
|
115
|
+
}
|
|
116
|
+
return { staleAt: now, expiredAt: now + durations.expire * 1000 };
|
|
117
|
+
}
|
|
118
|
+
const MARKER_FIELDS = ["revalidatedAt", "staleAt", "expiredAt"];
|
|
119
|
+
/**
|
|
120
|
+
* `existing` with the fields `update` sets, as a {@link markerUpdate} leaves
|
|
121
|
+
* the row: a field `update` does not set keeps its value.
|
|
122
|
+
*/
|
|
123
|
+
function applyMarker(existing, update) {
|
|
124
|
+
const marker = { ...existing };
|
|
125
|
+
for (const field of MARKER_FIELDS) {
|
|
126
|
+
if (update[field] !== undefined) {
|
|
127
|
+
marker[field] = update[field];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return marker;
|
|
131
|
+
}
|
|
132
|
+
/** The marker-row update that sets {@link markerFor}'s fields. */
|
|
133
|
+
function markerUpdate(now, durations) {
|
|
134
|
+
const marker = markerFor(now, durations);
|
|
135
|
+
const fields = MARKER_FIELDS.filter((field) => marker[field] !== undefined);
|
|
136
|
+
return {
|
|
137
|
+
UpdateExpression: `SET ${fields.map((field) => `${field} = :${field}`).join(", ")}`,
|
|
138
|
+
ExpressionAttributeValues: Object.fromEntries(fields.map((field) => [`:${field}`, { N: String(marker[field]) }])),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/** A DynamoDB number attribute as a number, or `undefined` when absent. */
|
|
142
|
+
function numberAttribute(value) {
|
|
143
|
+
return value?.N === undefined ? undefined : Number(value.N);
|
|
144
|
+
}
|
|
145
|
+
/** The marker fields of a marker or log row. */
|
|
146
|
+
function markerOf(item) {
|
|
147
|
+
return {
|
|
148
|
+
revalidatedAt: numberAttribute(item.revalidatedAt),
|
|
149
|
+
staleAt: numberAttribute(item.staleAt),
|
|
150
|
+
expiredAt: numberAttribute(item.expiredAt),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/** DynamoDB's cap on keys in one `BatchGetItem`. */
|
|
154
|
+
exports.BATCH_GET_MAX_KEYS = 100;
|
|
155
|
+
/**
|
|
156
|
+
* How often, at most, an instance catches up on revalidations other instances
|
|
157
|
+
* ran, unless `CDK_NEXTJS_TAG_REFRESH_MS` says otherwise: the staleness window
|
|
158
|
+
* for a `revalidateTag` run elsewhere. Every cache hit checks its tags'
|
|
159
|
+
* markers, and every marker of a deployment shares one partition key, so
|
|
160
|
+
* reading them per request capped a route's cache hits at the partition's read
|
|
161
|
+
* throughput (~1,900 req/s on NextjsRegionalFunctions). Once a second, from the
|
|
162
|
+
* revalidation log, the reads scale with instances instead of requests or tags.
|
|
163
|
+
*/
|
|
164
|
+
exports.DEFAULT_TAG_REFRESH_MS = 1000;
|
|
165
|
+
/**
|
|
166
|
+
* The bare-tag marker rows of the revalidation table (`pk = buildId`,
|
|
167
|
+
* `sk = tag`), which record when a tag was last revalidated and how.
|
|
168
|
+
*
|
|
169
|
+
* The table also holds the incremental cache's `tag#s3Key` mapping rows, which
|
|
170
|
+
* are its own business and stay in `S3CacheHandler`: only the markers mean the
|
|
171
|
+
* same thing to every handler. What an instance keeps of them between reads is
|
|
172
|
+
* {@link TrackedTagMarkers}.
|
|
173
|
+
*/
|
|
174
|
+
class TagMarkerTable {
|
|
175
|
+
client;
|
|
176
|
+
tableName;
|
|
177
|
+
buildId;
|
|
178
|
+
constructor(client, tableName, buildId) {
|
|
179
|
+
this.client = client;
|
|
180
|
+
this.tableName = tableName;
|
|
181
|
+
this.buildId = buildId;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Record `tag`'s revalidation at `now`. See {@link markerUpdate}.
|
|
185
|
+
*
|
|
186
|
+
* Returns the row as this write left it, so the instance that wrote it can
|
|
187
|
+
* track it rather than read it back: reads are eventually consistent, and a
|
|
188
|
+
* read-back racing replication could return the marker from before the write.
|
|
189
|
+
*/
|
|
190
|
+
async write(tag, now, durations) {
|
|
191
|
+
const response = await this.client.send(new client_dynamodb_1.UpdateItemCommand({
|
|
192
|
+
TableName: this.tableName,
|
|
193
|
+
Key: {
|
|
194
|
+
pk: { S: this.buildId },
|
|
195
|
+
sk: { S: tag },
|
|
196
|
+
},
|
|
197
|
+
...markerUpdate(now, durations),
|
|
198
|
+
ReturnValues: "ALL_NEW",
|
|
199
|
+
}));
|
|
200
|
+
return response?.Attributes ? markerOf(response.Attributes) : undefined;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* The marker rows for `tags` that exist, by tag, in one `BatchGetItem` per
|
|
204
|
+
* {@link BATCH_GET_MAX_KEYS} tags rather than a `GetItem` each: a page
|
|
205
|
+
* carries its whole implicit `_N_T_/…` chain plus the app's own tags, a
|
|
206
|
+
* page with a few fetches checks each of them too, and every one is on the
|
|
207
|
+
* request path. A tag with no row is absent from the result.
|
|
208
|
+
*/
|
|
209
|
+
async read(tags) {
|
|
210
|
+
const { tableName, buildId } = this;
|
|
211
|
+
const unique = Array.from(new Set(tags));
|
|
212
|
+
const markers = new Map();
|
|
213
|
+
for (let i = 0; i < unique.length; i += exports.BATCH_GET_MAX_KEYS) {
|
|
214
|
+
let keys = unique
|
|
215
|
+
.slice(i, i + exports.BATCH_GET_MAX_KEYS)
|
|
216
|
+
.map((tag) => ({ pk: { S: buildId }, sk: { S: tag } }));
|
|
217
|
+
// `UnprocessedKeys` is DynamoDB declining part of the batch under load;
|
|
218
|
+
// a marker left unread is a revalidation missed, so it is asked again.
|
|
219
|
+
for (let attempt = 0; keys?.length && attempt < 3; attempt++) {
|
|
220
|
+
const response = await this.client.send(new client_dynamodb_1.BatchGetItemCommand({
|
|
221
|
+
RequestItems: {
|
|
222
|
+
[tableName]: {
|
|
223
|
+
Keys: keys,
|
|
224
|
+
ProjectionExpression: "sk, revalidatedAt, staleAt, expiredAt",
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
}));
|
|
228
|
+
for (const item of response.Responses?.[tableName] ?? []) {
|
|
229
|
+
const tag = item.sk?.S;
|
|
230
|
+
if (tag !== undefined) {
|
|
231
|
+
markers.set(tag, markerOf(item));
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
keys = response.UnprocessedKeys?.[tableName]?.Keys;
|
|
235
|
+
}
|
|
236
|
+
if (keys?.length) {
|
|
237
|
+
throw new Error(`DynamoDB left ${keys.length} tag markers unread after retrying`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return markers;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
exports.TagMarkerTable = TagMarkerTable;
|
|
244
|
+
/**
|
|
245
|
+
* How long a revalidation log row is kept (`ttl`, epoch seconds). DynamoDB never
|
|
246
|
+
* deletes an item before its TTL, only some time after, so a reader that last
|
|
247
|
+
* queried less than this long ago (less a margin for writers' clocks) is sure
|
|
248
|
+
* to find every row it has not seen yet.
|
|
249
|
+
*/
|
|
250
|
+
exports.REVALIDATION_LOG_TTL_MS = 15 * 60 * 1000;
|
|
251
|
+
/** The most `Query` pages one {@link RevalidationLog.query} reads. */
|
|
252
|
+
exports.REVALIDATION_LOG_MAX_PAGES = 5;
|
|
253
|
+
/** How many sort keys {@link RevalidationLog.put} tries before giving up. */
|
|
254
|
+
const LOG_PUT_ATTEMPTS = 5;
|
|
255
|
+
/** Digits a log row's timestamp is zero-padded to, so sort keys sort by time. */
|
|
256
|
+
const LOG_SK_DIGITS = 15;
|
|
257
|
+
/**
|
|
258
|
+
* The revalidation log: one row per tag revalidation (`pk = <buildId>#log`,
|
|
259
|
+
* `sk = <Date.now(), zero-padded>#<tag>`), which expires after
|
|
260
|
+
* {@link REVALIDATION_LOG_TTL_MS}.
|
|
261
|
+
*
|
|
262
|
+
* The marker rows stay the source of truth. The log exists so an instance
|
|
263
|
+
* can ask "what was revalidated since I last looked?" in one `Query`, instead
|
|
264
|
+
* of re-reading every tag it tracks to find out that almost none changed. Every
|
|
265
|
+
* marker row of a deployment shares one partition, and those re-reads from a
|
|
266
|
+
* handful of busy instances were enough to throttle it.
|
|
267
|
+
*
|
|
268
|
+
* The sort key's timestamp is the writer's wall clock (`Date.now()`), not the
|
|
269
|
+
* performance clock ({@link markerClock}) the marker values are stamped with by both
|
|
270
|
+
* handlers: it only orders rows for readers' cursors, which are on their own
|
|
271
|
+
* wall clocks, and is never compared with an entry's timestamp.
|
|
272
|
+
*/
|
|
273
|
+
class RevalidationLog {
|
|
274
|
+
client;
|
|
275
|
+
tableName;
|
|
276
|
+
pk;
|
|
277
|
+
constructor(client, tableName, buildId) {
|
|
278
|
+
this.client = client;
|
|
279
|
+
this.tableName = tableName;
|
|
280
|
+
this.pk = `${buildId}#log`;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Record that `tag` was revalidated at `at` (`Date.now()`), setting `marker`.
|
|
284
|
+
*
|
|
285
|
+
* Two revalidations of `tag` in the same millisecond would share a sort key,
|
|
286
|
+
* and the second would overwrite the first. So the row is only written where
|
|
287
|
+
* its key is free, and on a collision it moves to the next millisecond, up
|
|
288
|
+
* to {@link LOG_PUT_ATTEMPTS} times.
|
|
289
|
+
*/
|
|
290
|
+
async put(tag, at, marker) {
|
|
291
|
+
for (let attempt = 1;; attempt++) {
|
|
292
|
+
const item = {
|
|
293
|
+
pk: { S: this.pk },
|
|
294
|
+
sk: { S: `${logSkPrefix(at)}#${tag}` },
|
|
295
|
+
ttl: { N: String(Math.ceil((at + exports.REVALIDATION_LOG_TTL_MS) / 1000)) },
|
|
296
|
+
};
|
|
297
|
+
for (const field of MARKER_FIELDS) {
|
|
298
|
+
const value = marker[field];
|
|
299
|
+
if (value !== undefined) {
|
|
300
|
+
item[field] = { N: String(value) };
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
try {
|
|
304
|
+
await this.client.send(new client_dynamodb_1.PutItemCommand({
|
|
305
|
+
TableName: this.tableName,
|
|
306
|
+
Item: item,
|
|
307
|
+
ConditionExpression: "attribute_not_exists(sk)",
|
|
308
|
+
}));
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
if (error?.name !==
|
|
313
|
+
"ConditionalCheckFailedException" ||
|
|
314
|
+
attempt >= LOG_PUT_ATTEMPTS) {
|
|
315
|
+
throw error;
|
|
316
|
+
}
|
|
317
|
+
at = Math.floor(at) + 1;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* The rows written at or after `since` (`Date.now()`), oldest first.
|
|
323
|
+
* `truncated` when there were more than {@link REVALIDATION_LOG_MAX_PAGES}
|
|
324
|
+
* pages of them, and `rows` is only the start.
|
|
325
|
+
*
|
|
326
|
+
* Eventually consistent: a row written a moment ago may not show yet, which
|
|
327
|
+
* is what the reader's lookback is for.
|
|
328
|
+
*/
|
|
329
|
+
async query(since) {
|
|
330
|
+
const rows = [];
|
|
331
|
+
let startKey;
|
|
332
|
+
for (let page = 0; page < exports.REVALIDATION_LOG_MAX_PAGES; page++) {
|
|
333
|
+
const response = await this.client.send(new client_dynamodb_1.QueryCommand({
|
|
334
|
+
TableName: this.tableName,
|
|
335
|
+
KeyConditionExpression: "pk = :pk AND sk >= :since",
|
|
336
|
+
ExpressionAttributeValues: {
|
|
337
|
+
":pk": { S: this.pk },
|
|
338
|
+
":since": { S: logSkPrefix(Math.max(0, since)) },
|
|
339
|
+
},
|
|
340
|
+
ProjectionExpression: "sk, revalidatedAt, staleAt, expiredAt",
|
|
341
|
+
ExclusiveStartKey: startKey,
|
|
342
|
+
}));
|
|
343
|
+
for (const item of response.Items ?? []) {
|
|
344
|
+
const sk = item.sk?.S;
|
|
345
|
+
// `<digits>#<tag>`: the tag is everything after the fixed-width
|
|
346
|
+
// timestamp, `#`s included.
|
|
347
|
+
if (sk === undefined || sk[LOG_SK_DIGITS] !== "#") {
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
rows.push({
|
|
351
|
+
sk,
|
|
352
|
+
at: Number(sk.slice(0, LOG_SK_DIGITS)),
|
|
353
|
+
tag: sk.slice(LOG_SK_DIGITS + 1),
|
|
354
|
+
marker: markerOf(item),
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
startKey = response.LastEvaluatedKey;
|
|
358
|
+
if (!startKey) {
|
|
359
|
+
return { rows, truncated: false };
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return { rows, truncated: true };
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
exports.RevalidationLog = RevalidationLog;
|
|
366
|
+
function logSkPrefix(at) {
|
|
367
|
+
return String(Math.floor(at)).padStart(LOG_SK_DIGITS, "0");
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* How far behind the last query's start the next one starts reading the log:
|
|
371
|
+
* the room for the `Query` being eventually consistent and for writers' clocks
|
|
372
|
+
* running behind the reader's. Rows inside it are read again, and dropped as
|
|
373
|
+
* already applied.
|
|
374
|
+
*/
|
|
375
|
+
exports.REVALIDATION_LOG_LOOKBACK_MS = 5000;
|
|
376
|
+
/**
|
|
377
|
+
* The longest {@link TrackedTagMarkers} trusts the log alone for a tag before
|
|
378
|
+
* reading its marker row again. The log is the fast path, but the marker rows
|
|
379
|
+
* are the source of truth: a log row whose write failed would otherwise be
|
|
380
|
+
* missed for as long as the tag stays tracked.
|
|
381
|
+
*
|
|
382
|
+
* Rolling, not all at once: each refresh re-reads at most
|
|
383
|
+
* {@link BATCH_GET_MAX_KEYS} of the tags due, oldest first. Re-reading them all
|
|
384
|
+
* together cost ~500 RCU at 1000 tags - a fleet started by one load test did it
|
|
385
|
+
* in the same minute, 12,800 RCU, and throttled - and would be ~5,000 RCU at
|
|
386
|
+
* {@link DEFAULT_MAX_TRACKED_TAGS}, past what the one partition serves in a
|
|
387
|
+
* second. Spread out, 10,000 tracked tags cost ~8 RCU a second per instance.
|
|
388
|
+
*
|
|
389
|
+
* Each tag is due at a random 75-100% of this after it was last read, so tags
|
|
390
|
+
* first read together - an instance warming up - do not all come due together
|
|
391
|
+
* again every interval: the load test that measured the rolling re-read saw
|
|
392
|
+
* its warm-up's reads return as a 30-36 RCU/s bump ten minutes later.
|
|
393
|
+
*/
|
|
394
|
+
exports.DEFAULT_TAG_RESYNC_MS = 10 * 60 * 1000;
|
|
395
|
+
/** The fraction of {@link DEFAULT_TAG_RESYNC_MS} a tag may come due early by. */
|
|
396
|
+
const TAG_RESYNC_JITTER = 0.25;
|
|
397
|
+
/**
|
|
398
|
+
* How long since the last successful log query the log can still be trusted to
|
|
399
|
+
* hold every row not read yet: its TTL less a margin for writers' clocks. Past
|
|
400
|
+
* it - a Lambda frozen between invocations, a run of failed queries - the
|
|
401
|
+
* tracked markers are forgotten, to be read again as they are needed, and the
|
|
402
|
+
* log is followed from there.
|
|
403
|
+
*/
|
|
404
|
+
exports.MAX_REVALIDATION_LOG_GAP_MS = exports.REVALIDATION_LOG_TTL_MS - 5 * 60 * 1000;
|
|
405
|
+
/**
|
|
406
|
+
* The most tags one instance tracks. Past it the least recently used is
|
|
407
|
+
* forgotten, which costs a read the next time it is needed: an untracked tag
|
|
408
|
+
* is read before it is trusted.
|
|
409
|
+
*
|
|
410
|
+
* A tracked tag costs memory (~2 MB at this bound) and its share of the rolling
|
|
411
|
+
* re-read, not a read every refresh, so the bound is set by how many tags an
|
|
412
|
+
* instance really uses. Every path is a tag of its own (`_N_T_/<path>`), and at
|
|
413
|
+
* 1000 an app of 1000 tagged pages kept evicting what it was about to need:
|
|
414
|
+
* half its requests re-read a marker.
|
|
415
|
+
*/
|
|
416
|
+
exports.DEFAULT_MAX_TRACKED_TAGS = 10_000;
|
|
417
|
+
/**
|
|
418
|
+
* One instance's copy of the marker rows of the tags it has needed, kept
|
|
419
|
+
* current through the {@link RevalidationLog}: the process's one tag manifest
|
|
420
|
+
* (`sharedTagManifest`), for the `'use cache'` handlers and the incremental
|
|
421
|
+
* cache alike.
|
|
422
|
+
*
|
|
423
|
+
* Next.js's built-in `'use cache'` handler keeps tags in a module-level map
|
|
424
|
+
* (`tags-manifest.external.js`) that only its own `updateTags` writes, and its
|
|
425
|
+
* `refreshTags` is a no-op, so a `revalidateTag` on one instance never reaches
|
|
426
|
+
* the others: they keep serving the revalidated entry, including inside pages
|
|
427
|
+
* that are being re-rendered *because* of that revalidation. The
|
|
428
|
+
* `cacheHandlers` interface's answer is `refreshTags`, "called before starting
|
|
429
|
+
* a new request … to refresh the local tags manifest", and this is that
|
|
430
|
+
* manifest, backed by the same marker rows the incremental cache's
|
|
431
|
+
* `revalidateTag` writes (`pk = buildId`, `sk = tag`).
|
|
432
|
+
*
|
|
433
|
+
* The cost is bounded per instance, not per request:
|
|
434
|
+
* - {@link ensure} reads a tag's marker the first time it is needed and not
|
|
435
|
+
* again until it is evicted, so a tag costs one read per instance, not one
|
|
436
|
+
* per request.
|
|
437
|
+
* - {@link refresh}, at most once per `refreshIntervalMs`, sends one `Query`
|
|
438
|
+
* for the log rows written since the last one, and applies those of tags
|
|
439
|
+
* this instance tracks. Concurrent callers share it. Alongside, it re-reads
|
|
440
|
+
* the markers of up to {@link BATCH_GET_MAX_KEYS} tags not read for
|
|
441
|
+
* {@link DEFAULT_TAG_RESYNC_MS}, in one `BatchGetItem`.
|
|
442
|
+
* A revalidation on another instance is therefore seen within
|
|
443
|
+
* `refreshIntervalMs` (plus the query itself). The instance that ran it applies
|
|
444
|
+
* it itself, at once, with {@link set}.
|
|
445
|
+
*/
|
|
446
|
+
class TrackedTagMarkers {
|
|
447
|
+
markers;
|
|
448
|
+
log;
|
|
449
|
+
refreshIntervalMs;
|
|
450
|
+
maxTrackedTags;
|
|
451
|
+
resyncIntervalMs;
|
|
452
|
+
clock;
|
|
453
|
+
random;
|
|
454
|
+
debug;
|
|
455
|
+
/** By tag, least recently used first. */
|
|
456
|
+
tags = new Map();
|
|
457
|
+
/**
|
|
458
|
+
* When each tracked tag's marker is due to be read again: a random 75-100%
|
|
459
|
+
* of `resyncIntervalMs` after it was last known from the table - read,
|
|
460
|
+
* written here, or new. In the order they became known, least recent first.
|
|
461
|
+
*/
|
|
462
|
+
dueAt = new Map();
|
|
463
|
+
reading = new Map();
|
|
464
|
+
/**
|
|
465
|
+
* Log rows for tags whose first read is in flight, merged by tag: the read
|
|
466
|
+
* may have been answered before the revalidation, so they are applied to its
|
|
467
|
+
* result rather than dropped.
|
|
468
|
+
*/
|
|
469
|
+
pendingRows = new Map();
|
|
470
|
+
/** Tracked tags whose read failed, for the next refresh to read again. */
|
|
471
|
+
unread = new Set();
|
|
472
|
+
lastRefresh = -Infinity;
|
|
473
|
+
refreshing;
|
|
474
|
+
/**
|
|
475
|
+
* Where the next log query starts (`clock()`): the last successful one's
|
|
476
|
+
* start, less {@link REVALIDATION_LOG_LOOKBACK_MS}.
|
|
477
|
+
*/
|
|
478
|
+
cursor;
|
|
479
|
+
/** When the last successful log query started, or the log was first read. */
|
|
480
|
+
lastLogRead;
|
|
481
|
+
/** Log rows at or after the cursor already applied, by sort key. */
|
|
482
|
+
applied = new Map();
|
|
483
|
+
/** {@link update}s of the current turn, by their arguments. */
|
|
484
|
+
updating = new Map();
|
|
485
|
+
constructor(options) {
|
|
486
|
+
this.markers = options.markers;
|
|
487
|
+
this.log = options.log;
|
|
488
|
+
this.refreshIntervalMs =
|
|
489
|
+
options.refreshIntervalMs ?? exports.DEFAULT_TAG_REFRESH_MS;
|
|
490
|
+
this.maxTrackedTags = options.maxTrackedTags ?? exports.DEFAULT_MAX_TRACKED_TAGS;
|
|
491
|
+
this.resyncIntervalMs = options.resyncIntervalMs ?? exports.DEFAULT_TAG_RESYNC_MS;
|
|
492
|
+
// Looked up on each call rather than bound here, so a test's `Date.now`
|
|
493
|
+
// spy applies.
|
|
494
|
+
this.clock = options.clock ?? (() => Date.now());
|
|
495
|
+
this.random = options.random ?? Math.random;
|
|
496
|
+
this.debug = options.debug ?? (() => { });
|
|
497
|
+
// Nothing is tracked yet, so nothing before this can be missed: every tag
|
|
498
|
+
// is read from its marker the first time it is needed.
|
|
499
|
+
const at = this.clock();
|
|
500
|
+
this.cursor = at - exports.REVALIDATION_LOG_LOOKBACK_MS;
|
|
501
|
+
this.lastLogRead = at;
|
|
502
|
+
}
|
|
503
|
+
/** `tag`'s marker as this instance knows it, if it tracks `tag`. */
|
|
504
|
+
get(tag) {
|
|
505
|
+
return this.tags.get(tag);
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Track `tag` with `marker`: what this instance just wrote, which it knows
|
|
509
|
+
* without reading it back.
|
|
510
|
+
*/
|
|
511
|
+
set(tag, marker) {
|
|
512
|
+
this.remember(tag, marker);
|
|
513
|
+
this.known(tag, this.clock());
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Note a use of `tags`: those of an entry this instance just stored.
|
|
517
|
+
*
|
|
518
|
+
* With the table, a tag not tracked yet is left untracked, to be read by
|
|
519
|
+
* {@link ensure} the first time an entry needs it. Nothing revalidated before
|
|
520
|
+
* the new entry was created applies to *it*, but tracking is per tag, not per
|
|
521
|
+
* entry: tracking the tag with an empty marker would also vouch for every
|
|
522
|
+
* older entry that carries it - one another instance stored in S3, or one
|
|
523
|
+
* this instance kept in memory across a {@link forget} - and a revalidation
|
|
524
|
+
* from before the log's cursor would go unseen until the rolling re-read.
|
|
525
|
+
*
|
|
526
|
+
* Without the table every revalidation is this process's own, applied with
|
|
527
|
+
* {@link set}, so a tag is tracked at once.
|
|
528
|
+
*/
|
|
529
|
+
track(tags) {
|
|
530
|
+
if (!this.markers) {
|
|
531
|
+
this.trackUnread(tags);
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
for (const tag of tags) {
|
|
535
|
+
this.touch(tag);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Track `tags` as they are known now, without reading them, with an empty
|
|
540
|
+
* marker for any not tracked yet.
|
|
541
|
+
*/
|
|
542
|
+
trackUnread(tags) {
|
|
543
|
+
const at = this.clock();
|
|
544
|
+
for (const tag of tags) {
|
|
545
|
+
if (!this.tags.has(tag)) {
|
|
546
|
+
this.known(tag, at);
|
|
547
|
+
}
|
|
548
|
+
this.remember(tag, this.tags.get(tag) ?? {});
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Read the markers of whichever of `tags` this instance does not track yet.
|
|
553
|
+
* Needed before judging an entry this instance did not create, since its
|
|
554
|
+
* tags may have been revalidated at any time before.
|
|
555
|
+
*/
|
|
556
|
+
async ensure(tags) {
|
|
557
|
+
if (!this.markers) {
|
|
558
|
+
this.trackUnread(tags);
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
const waits = [];
|
|
562
|
+
const unread = [];
|
|
563
|
+
for (const tag of new Set(tags)) {
|
|
564
|
+
const inFlight = this.reading.get(tag);
|
|
565
|
+
if (inFlight) {
|
|
566
|
+
waits.push(inFlight);
|
|
567
|
+
}
|
|
568
|
+
else if (this.tags.has(tag)) {
|
|
569
|
+
this.touch(tag);
|
|
570
|
+
}
|
|
571
|
+
else {
|
|
572
|
+
unread.push(tag);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
if (unread.length > 0) {
|
|
576
|
+
// `readInto` never rejects: a failed read leaves the tags tracked.
|
|
577
|
+
const read = this.readInto(unread).then(() => {
|
|
578
|
+
for (const tag of unread) {
|
|
579
|
+
this.reading.delete(tag);
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
for (const tag of unread) {
|
|
583
|
+
this.reading.set(tag, read);
|
|
584
|
+
}
|
|
585
|
+
waits.push(read);
|
|
586
|
+
}
|
|
587
|
+
await Promise.all(waits);
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Catch up on revalidations other instances ran, at most once per
|
|
591
|
+
* `refreshIntervalMs`. See {@link TrackedTagMarkers} for the cost.
|
|
592
|
+
*/
|
|
593
|
+
async refresh() {
|
|
594
|
+
const log = this.log;
|
|
595
|
+
if (!log) {
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
if (this.refreshing) {
|
|
599
|
+
return this.refreshing;
|
|
600
|
+
}
|
|
601
|
+
const at = this.clock();
|
|
602
|
+
if (at - this.lastRefresh < this.refreshIntervalMs) {
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
this.lastRefresh = at;
|
|
606
|
+
// Whatever gets tracked before the next refresh is read from its marker,
|
|
607
|
+
// so there is nothing to catch up on yet.
|
|
608
|
+
if (this.tags.size === 0) {
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
this.refreshing = this.sync(at, log).finally(() => {
|
|
612
|
+
this.refreshing = undefined;
|
|
613
|
+
});
|
|
614
|
+
return this.refreshing;
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Record a revalidation of `tags`: here at once, and in the revalidation
|
|
618
|
+
* table for every other instance - the marker row plus a log row per tag.
|
|
619
|
+
*
|
|
620
|
+
* Next.js calls `revalidateTag` on the incremental cache and `updateTags` on
|
|
621
|
+
* every distinct `cacheHandlers` entry, all in one synchronous loop
|
|
622
|
+
* (`revalidation-utils`), and all of them share this manifest. So an
|
|
623
|
+
* identical call in the same turn joins the first instead of writing the
|
|
624
|
+
* rows again. One in a later turn is a revalidation of its own, even while
|
|
625
|
+
* the first one's writes are in flight, and gets its own, later, marker.
|
|
626
|
+
* Never rejects: resolves `false` when a row failed to write (the error is
|
|
627
|
+
* logged), so other instances may not see it.
|
|
628
|
+
*/
|
|
629
|
+
async update(tags, durations) {
|
|
630
|
+
const key = JSON.stringify([tags, durations ?? null]);
|
|
631
|
+
const inFlight = this.updating.get(key);
|
|
632
|
+
if (inFlight) {
|
|
633
|
+
return inFlight;
|
|
634
|
+
}
|
|
635
|
+
const update = this.write(tags, durations);
|
|
636
|
+
this.updating.set(key, update);
|
|
637
|
+
// Forgotten as soon as the current turn's calls have been made.
|
|
638
|
+
void Promise.resolve().then(() => this.updating.delete(key));
|
|
639
|
+
return update;
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* What the tracked markers say about an entry carrying `tags`, created at
|
|
643
|
+
* `createdAt`: the most severe answer of any of them.
|
|
644
|
+
*/
|
|
645
|
+
state(tags, createdAt) {
|
|
646
|
+
// `markerClock()`, not `Date.now()`, here and wherever a marker is stamped:
|
|
647
|
+
// `createdAt` is on Next.js's clock, and a marker on the wall clock would be
|
|
648
|
+
// compared across the two, so an entry regenerated just after a
|
|
649
|
+
// revalidation could still read as older than it wherever they drift.
|
|
650
|
+
const at = markerClock();
|
|
651
|
+
let state = "fresh";
|
|
652
|
+
for (const tag of tags) {
|
|
653
|
+
const marker = this.get(tag);
|
|
654
|
+
if (!marker) {
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
const tagState = markerState(marker, createdAt, at);
|
|
658
|
+
if (tagState === "expired") {
|
|
659
|
+
return "expired";
|
|
660
|
+
}
|
|
661
|
+
if (tagState === "stale") {
|
|
662
|
+
state = "stale";
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return state;
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* `getExpiration`'s answer: the latest time any of `tags` expired, `0` if
|
|
669
|
+
* none has. Only expirations already past count - a profile's future
|
|
670
|
+
* `expire` is not an expiration yet - and a stale mark does not, matching
|
|
671
|
+
* Next.js's default handler, which reads only `expired` for implicit tags.
|
|
672
|
+
*/
|
|
673
|
+
expiration(tags) {
|
|
674
|
+
const at = markerClock();
|
|
675
|
+
let latest = 0;
|
|
676
|
+
for (const tag of tags) {
|
|
677
|
+
const marker = this.get(tag);
|
|
678
|
+
if (!marker) {
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
if (marker.revalidatedAt !== undefined) {
|
|
682
|
+
latest = Math.max(latest, marker.revalidatedAt);
|
|
683
|
+
}
|
|
684
|
+
if (marker.expiredAt !== undefined && marker.expiredAt <= at) {
|
|
685
|
+
latest = Math.max(latest, marker.expiredAt);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return latest;
|
|
689
|
+
}
|
|
690
|
+
async write(tags, durations) {
|
|
691
|
+
const at = markerClock();
|
|
692
|
+
const marker = markerFor(at, durations);
|
|
693
|
+
for (const tag of tags) {
|
|
694
|
+
this.set(tag, applyMarker(this.get(tag), marker));
|
|
695
|
+
}
|
|
696
|
+
const { markers, log } = this;
|
|
697
|
+
if (!markers || !log) {
|
|
698
|
+
return true;
|
|
699
|
+
}
|
|
700
|
+
const writes = [];
|
|
701
|
+
for (const tag of new Set(tags)) {
|
|
702
|
+
writes.push([
|
|
703
|
+
"Error writing tag revalidation marker:",
|
|
704
|
+
// The row as it is now, which carries any earlier `revalidatedAt`
|
|
705
|
+
// another instance wrote: tracking only what this call set would
|
|
706
|
+
// hide that one until the rolling re-read, and an entry older than it
|
|
707
|
+
// would read as merely stale.
|
|
708
|
+
markers.write(tag, at, durations).then((row) => {
|
|
709
|
+
if (row) {
|
|
710
|
+
this.set(tag, mergeMarkers(this.get(tag), row));
|
|
711
|
+
}
|
|
712
|
+
}),
|
|
713
|
+
]);
|
|
714
|
+
writes.push([
|
|
715
|
+
"Error writing tag revalidation log row:",
|
|
716
|
+
this.putLogRow(log, tag, marker),
|
|
717
|
+
]);
|
|
718
|
+
}
|
|
719
|
+
const results = await Promise.allSettled(writes.map(([, write]) => write));
|
|
720
|
+
results.forEach((result, i) => {
|
|
721
|
+
if (result.status === "rejected") {
|
|
722
|
+
console.error(writes[i][0], result.reason);
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
return results.every((result) => result.status === "fulfilled");
|
|
726
|
+
}
|
|
727
|
+
/**
|
|
728
|
+
* Put `tag`'s log row, stamped on the wall clock (the marker values are on
|
|
729
|
+
* Next.js's: see {@link RevalidationLog}).
|
|
730
|
+
*
|
|
731
|
+
* Readers only look {@link REVALIDATION_LOG_LOOKBACK_MS} behind their last
|
|
732
|
+
* query, so a row that shows up much later than its timestamp - a slow
|
|
733
|
+
* connection, retries under throttling - is behind every cursor and never
|
|
734
|
+
* read. A slow put is therefore made again under a fresh timestamp, which
|
|
735
|
+
* readers will see; one slow twice is reported as a failure.
|
|
736
|
+
*/
|
|
737
|
+
async putLogRow(log, tag, marker) {
|
|
738
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
739
|
+
const at = this.clock();
|
|
740
|
+
await log.put(tag, at, marker);
|
|
741
|
+
if (this.clock() - at <= MAX_LOG_PUT_MS) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
throw new Error(`The revalidation log row for ${tag} took longer than readers look back`);
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* One refresh started at `at`: the log rows since the cursor, and the next
|
|
749
|
+
* slice of the rolling re-read.
|
|
750
|
+
*
|
|
751
|
+
* A failed query changes nothing but the log: the tags stay tracked as they
|
|
752
|
+
* were, and the next refresh asks again from the same cursor.
|
|
753
|
+
*/
|
|
754
|
+
async sync(at, log) {
|
|
755
|
+
if (at - this.lastLogRead > exports.MAX_REVALIDATION_LOG_GAP_MS) {
|
|
756
|
+
this.forget(at, "the revalidation log may not cover the gap since it was last read");
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
const [result] = await Promise.all([
|
|
760
|
+
log.query(this.cursor).catch((error) => {
|
|
761
|
+
console.error("Error reading tag revalidation log:", error);
|
|
762
|
+
return undefined;
|
|
763
|
+
}),
|
|
764
|
+
this.readInto(this.due(at)),
|
|
765
|
+
]);
|
|
766
|
+
if (!result) {
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
if (result.truncated) {
|
|
770
|
+
this.forget(at, "the revalidation log has more rows than one query reads");
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
let applied = 0;
|
|
774
|
+
for (const row of result.rows) {
|
|
775
|
+
if (this.applied.has(row.sk)) {
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
const marker = this.tags.get(row.tag);
|
|
779
|
+
if (marker) {
|
|
780
|
+
// `set` on a key already there keeps its place in the LRU order: being
|
|
781
|
+
// revalidated elsewhere is not a use.
|
|
782
|
+
this.tags.set(row.tag, mergeMarkers(marker, row.marker));
|
|
783
|
+
}
|
|
784
|
+
else if (this.reading.has(row.tag)) {
|
|
785
|
+
this.pendingRows.set(row.tag, mergeMarkers(this.pendingRows.get(row.tag), row.marker));
|
|
786
|
+
}
|
|
787
|
+
else {
|
|
788
|
+
// A tag this instance does not track is read from its marker when it
|
|
789
|
+
// is next needed - but not yet marked applied, because that read may
|
|
790
|
+
// miss this very revalidation: the writer puts the log row and the
|
|
791
|
+
// marker at the same time, and the read is an eventually consistent
|
|
792
|
+
// `BatchGetItem` that can land milliseconds after the row showed here.
|
|
793
|
+
// Marked applied, the row was skipped by every query the lookback
|
|
794
|
+
// returned it to, and the pre-revalidation marker stood until the
|
|
795
|
+
// rolling re-read. Left unmarked, the next refresh applies it to
|
|
796
|
+
// whatever that read found; it costs no read, only the row being
|
|
797
|
+
// looked at again while it is inside the lookback.
|
|
798
|
+
continue;
|
|
799
|
+
}
|
|
800
|
+
this.applied.set(row.sk, row.at);
|
|
801
|
+
applied++;
|
|
802
|
+
}
|
|
803
|
+
this.lastLogRead = at;
|
|
804
|
+
this.advance(at - exports.REVALIDATION_LOG_LOOKBACK_MS);
|
|
805
|
+
this.debug(`read ${result.rows.length} revalidation log rows (${applied} applied)`);
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* The tags to read on this refresh, at most one `BatchGetItem`'s worth:
|
|
809
|
+
* those whose read failed, then those whose re-read has come due, in the
|
|
810
|
+
* order they were last read.
|
|
811
|
+
*/
|
|
812
|
+
due(at) {
|
|
813
|
+
const due = [];
|
|
814
|
+
for (const tag of this.unread) {
|
|
815
|
+
if (due.length >= exports.BATCH_GET_MAX_KEYS)
|
|
816
|
+
return due;
|
|
817
|
+
due.push(tag);
|
|
818
|
+
}
|
|
819
|
+
// Known in time order, and each due at most `jitter` earlier than the
|
|
820
|
+
// tags after it: past a tag due more than that from now, none is due.
|
|
821
|
+
const jitter = this.resyncIntervalMs * TAG_RESYNC_JITTER;
|
|
822
|
+
for (const [tag, dueAt] of this.dueAt) {
|
|
823
|
+
if (due.length >= exports.BATCH_GET_MAX_KEYS || dueAt - jitter > at) {
|
|
824
|
+
break;
|
|
825
|
+
}
|
|
826
|
+
if (dueAt <= at && !this.unread.has(tag)) {
|
|
827
|
+
due.push(tag);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
return due;
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Forget every tracked marker, and follow the log from `at`: what is needed
|
|
834
|
+
* next is read from its marker as it is needed, spread over the requests
|
|
835
|
+
* that need it, rather than every tracked tag re-read at once.
|
|
836
|
+
*/
|
|
837
|
+
forget(at, why) {
|
|
838
|
+
this.debug(`forgetting ${this.tags.size} tracked tags: ${why}`);
|
|
839
|
+
this.tags.clear();
|
|
840
|
+
this.dueAt.clear();
|
|
841
|
+
this.unread.clear();
|
|
842
|
+
this.applied.clear();
|
|
843
|
+
this.cursor = at - exports.REVALIDATION_LOG_LOOKBACK_MS;
|
|
844
|
+
this.lastLogRead = at;
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* Move the cursor forward to `cursor`, forgetting applied rows before it:
|
|
848
|
+
* no query returns them again.
|
|
849
|
+
*/
|
|
850
|
+
advance(cursor) {
|
|
851
|
+
if (cursor <= this.cursor) {
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
this.cursor = cursor;
|
|
855
|
+
for (const [sk, at] of this.applied) {
|
|
856
|
+
if (at < cursor) {
|
|
857
|
+
this.applied.delete(sk);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Read `tags` from the table, and report whether that worked. A read that
|
|
863
|
+
* fails leaves them tracked as they were, and the next refresh reads them
|
|
864
|
+
* again: the same "assume the entry is valid" the incremental cache has
|
|
865
|
+
* always answered with, rather than a read on every request while DynamoDB
|
|
866
|
+
* is unavailable.
|
|
867
|
+
*/
|
|
868
|
+
async readInto(tags) {
|
|
869
|
+
if (tags.length === 0) {
|
|
870
|
+
return true;
|
|
871
|
+
}
|
|
872
|
+
const at = this.clock();
|
|
873
|
+
try {
|
|
874
|
+
const read = await this.markers.read(tags);
|
|
875
|
+
for (const tag of tags) {
|
|
876
|
+
this.unread.delete(tag);
|
|
877
|
+
this.remember(tag, mergeMarkers(mergeMarkers(this.tags.get(tag), read.get(tag)), this.takePendingRows(tag)));
|
|
878
|
+
this.known(tag, at);
|
|
879
|
+
}
|
|
880
|
+
this.debug(`read ${tags.length} tag markers (${read.size} set)`);
|
|
881
|
+
return true;
|
|
882
|
+
}
|
|
883
|
+
catch (error) {
|
|
884
|
+
console.error("Error reading tag markers:", error);
|
|
885
|
+
this.trackUnread(tags);
|
|
886
|
+
for (const tag of tags) {
|
|
887
|
+
this.unread.add(tag);
|
|
888
|
+
const pending = this.takePendingRows(tag);
|
|
889
|
+
if (pending) {
|
|
890
|
+
this.remember(tag, mergeMarkers(this.tags.get(tag), pending));
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
return false;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
takePendingRows(tag) {
|
|
897
|
+
const pending = this.pendingRows.get(tag);
|
|
898
|
+
this.pendingRows.delete(tag);
|
|
899
|
+
return pending;
|
|
900
|
+
}
|
|
901
|
+
/** Record that `tag`'s marker was known from the table at `at`. */
|
|
902
|
+
known(tag, at) {
|
|
903
|
+
this.dueAt.delete(tag);
|
|
904
|
+
this.dueAt.set(tag, at + this.resyncIntervalMs * (1 - TAG_RESYNC_JITTER * this.random()));
|
|
905
|
+
}
|
|
906
|
+
remember(tag, marker) {
|
|
907
|
+
this.tags.delete(tag);
|
|
908
|
+
this.tags.set(tag, marker);
|
|
909
|
+
for (const oldest of this.tags.keys()) {
|
|
910
|
+
if (this.tags.size <= this.maxTrackedTags) {
|
|
911
|
+
break;
|
|
912
|
+
}
|
|
913
|
+
this.tags.delete(oldest);
|
|
914
|
+
this.dueAt.delete(oldest);
|
|
915
|
+
this.unread.delete(oldest);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
touch(tag) {
|
|
919
|
+
const marker = this.tags.get(tag);
|
|
920
|
+
if (marker) {
|
|
921
|
+
this.remember(tag, marker);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
exports.TrackedTagMarkers = TrackedTagMarkers;
|
|
926
|
+
/**
|
|
927
|
+
* The longest a log row's put may take and still be seen by every reader: half
|
|
928
|
+
* the lookback, leaving the rest for writers' clocks and eventually consistent
|
|
929
|
+
* queries.
|
|
930
|
+
*/
|
|
931
|
+
const MAX_LOG_PUT_MS = exports.REVALIDATION_LOG_LOOKBACK_MS / 2;
|
|
932
|
+
/**
|
|
933
|
+
* A marker as read from the table or the log, keeping whatever this instance
|
|
934
|
+
* already knows that the read does not show yet: reads are eventually
|
|
935
|
+
* consistent, and a read straight after this instance's own revalidation could
|
|
936
|
+
* otherwise take it back.
|
|
937
|
+
*/
|
|
938
|
+
function mergeMarkers(local, remote) {
|
|
939
|
+
if (!local) {
|
|
940
|
+
return remote ?? {};
|
|
941
|
+
}
|
|
942
|
+
if (!remote) {
|
|
943
|
+
return local;
|
|
944
|
+
}
|
|
945
|
+
// `expiredAt` is written with `staleAt`, so it belongs to whichever is
|
|
946
|
+
// newer - unless that revalidation's profile had no `expire` and left it.
|
|
947
|
+
const [older, newer] = (local.staleAt ?? -1) > (remote.staleAt ?? -1)
|
|
948
|
+
? [remote, local]
|
|
949
|
+
: [local, remote];
|
|
950
|
+
return {
|
|
951
|
+
...applyMarker(older, newer),
|
|
952
|
+
revalidatedAt: maxDefined(local.revalidatedAt, remote.revalidatedAt),
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
function maxDefined(a, b) {
|
|
956
|
+
if (a === undefined)
|
|
957
|
+
return b;
|
|
958
|
+
if (b === undefined)
|
|
959
|
+
return a;
|
|
960
|
+
return Math.max(a, b);
|
|
961
|
+
}
|
|
962
|
+
/**
|
|
963
|
+
* The cache bucket, as the handlers use it: whole objects read and written as
|
|
964
|
+
* UTF-8 text under the keys above.
|
|
965
|
+
*/
|
|
966
|
+
class CacheBucket {
|
|
967
|
+
client;
|
|
968
|
+
bucketName;
|
|
969
|
+
constructor(client, bucketName) {
|
|
970
|
+
this.client = client;
|
|
971
|
+
this.bucketName = bucketName;
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* The body of the object at `key`, or `undefined` when there is none (`NoSuchKey`, or an
|
|
975
|
+
* empty response body). Any other error is thrown: the handlers disagree on
|
|
976
|
+
* whether one is worth logging.
|
|
977
|
+
*/
|
|
978
|
+
async get(key) {
|
|
979
|
+
try {
|
|
980
|
+
const response = await this.client.send(new client_s3_1.GetObjectCommand({ Bucket: this.bucketName, Key: key }));
|
|
981
|
+
if (!response.Body) {
|
|
982
|
+
return undefined;
|
|
983
|
+
}
|
|
984
|
+
return await response.Body.transformToString("utf-8");
|
|
985
|
+
}
|
|
986
|
+
catch (error) {
|
|
987
|
+
if (error instanceof client_s3_1.NoSuchKey) {
|
|
988
|
+
return undefined;
|
|
989
|
+
}
|
|
990
|
+
throw error;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
/** Write `body` as a JSON object at `key`. */
|
|
994
|
+
async putJson(key, body) {
|
|
995
|
+
await this.client.send(new client_s3_1.PutObjectCommand({
|
|
996
|
+
Bucket: this.bucketName,
|
|
997
|
+
Key: key,
|
|
998
|
+
Body: body,
|
|
999
|
+
ContentType: "application/json; charset=utf-8",
|
|
1000
|
+
}));
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
exports.CacheBucket = CacheBucket;
|
|
1004
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXdzLWNhY2hlLXN0b3JlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FkYXB0ZXIvYXdzLWNhY2hlLXN0b3JlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQThDQSxzREFPQztBQVNELGdDQVFDO0FBa0JELHNDQUVDO0FBcUNELGtDQWdCQztBQVdELGtDQUVDO0FBV0QsOEJBV0M7QUFRRCxrQ0FXQztBQUdELG9DQWVDO0FBdzVCRCxvQ0FvQkM7QUFub0NEOzs7Ozs7Ozs7RUFTRTtBQUNGLHNEQUFzRDtBQUN0RCx5Q0FBaUM7QUFDakMsOERBU2tDO0FBQ2xDLGtEQUs0QjtBQWtCNUIsOENBQThDO0FBQzlDLFNBQWdCLHFCQUFxQjtJQUNuQyxPQUFPO1FBQ0wsVUFBVSxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsNEJBQTRCLElBQUksRUFBRTtRQUMxRCxTQUFTLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxrQ0FBa0MsSUFBSSxFQUFFO1FBQy9ELE1BQU0sRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLFVBQVUsSUFBSSxXQUFXO1FBQzdDLE9BQU8sRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLG1CQUFtQixJQUFJLEVBQUU7S0FDL0MsQ0FBQztBQUNKLENBQUM7QUFFRDs7Ozs7O0dBTUc7QUFDSCxTQUFnQixVQUFVLENBQUMsT0FBZSxFQUFFLFFBQWdCO0lBQzFELElBQUksYUFBYSxHQUFHLFFBQVEsQ0FBQztJQUM3QixJQUFJLFFBQVEsS0FBSyxHQUFHLElBQUksUUFBUSxLQUFLLEVBQUUsRUFBRSxDQUFDO1FBQ3hDLGFBQWEsR0FBRyxPQUFPLENBQUM7SUFDMUIsQ0FBQztTQUFNLElBQUksUUFBUSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ3BDLGFBQWEsR0FBRyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3BDLENBQUM7SUFDRCxPQUFPLElBQUEsZ0JBQUksRUFBQyxP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RUFBRSxJQUFJLENBQUMsVUFBVSxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUM1RCxDQUFDO1lBQ0YsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztnQkFDbkIsT0FBTyxTQUFTLENBQUM7WUFDbkIsQ0FBQztZQUNELE9BQU8sTUFBTSxRQUFRLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ3hELENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsSUFBSSxLQUFLLFlBQVkscUJBQVMsRUFBRSxDQUFDO2dCQUMvQixPQUFPLFNBQVMsQ0FBQztZQUNuQixDQUFDO1lBQ0QsTUFBTSxLQUFLLENBQUM7UUFDZCxDQUFDO0lBQ0gsQ0FBQztJQUVELDhDQUE4QztJQUM5QyxLQUFLLENBQUMsT0FBTyxDQUFDLEdBQVcsRUFBRSxJQUFZO1FBQ3JDLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQ3BCLElBQUksNEJBQWdCLENBQUM7WUFDbkIsTUFBTSxFQUFFLElBQUksQ0FBQyxVQUFVO1lBQ3ZCLEdBQUcsRUFBRSxHQUFHO1lBQ1IsSUFBSSxFQUFFLElBQUk7WUFDVixXQUFXLEVBQUUsaUNBQWlDO1NBQy9DLENBQUMsQ0FDSCxDQUFDO0lBQ0osQ0FBQztDQUNGO0FBdkNELGtDQXVDQyIsInNvdXJjZXNDb250ZW50IjpbIi8qXG4gIFRoZSBTMyAvIER5bmFtb0RCIHBsdW1iaW5nIHNoYXJlZCBieSBldmVyeSBjZGstbmV4dGpzIGNhY2hlIGhhbmRsZXI6IHRoZVxuICBpbmNyZW1lbnRhbCBgY2FjaGVIYW5kbGVyYCAoYFMzQ2FjaGVIYW5kbGVyYCkgYW5kIHRoZSBgY2FjaGVIYW5kbGVyc2AgZm9yXG4gIGAndXNlIGNhY2hlJ2AgLyBgJ3VzZSBjYWNoZTogcmVtb3RlJ2AuXG5cbiAgV2hhdCBsaXZlcyBoZXJlIGlzIHdoYXQgaGFzIHRvIGFncmVlIGJldHdlZW4gdGhlbSAtIHdoZXJlIHRoZSBjb25maWd1cmF0aW9uXG4gIGNvbWVzIGZyb20sIGhvdyBhIGNhY2hlIGtleSBiZWNvbWVzIGFuIFMzIGtleSwgYW5kIHdoYXQgYSB0YWcncyBtYXJrZXIgcm93IGluXG4gIHRoZSByZXZhbGlkYXRpb24gdGFibGUgbG9va3MgbGlrZSBhbmQgbWVhbnMuIEEgYHJldmFsaWRhdGVUYWdgIHJlYWNoZXMgYm90aFxuICBraW5kcyBvZiBoYW5kbGVyLCBhbmQgdGhleSBtdXN0IHJlYWQgZWFjaCBvdGhlcidzIG1hcmtlcnMgdGhlIHNhbWUgd2F5LlxuKi9cbi8qIGVzbGludC1kaXNhYmxlIGltcG9ydC9uby1leHRyYW5lb3VzLWRlcGVuZGVuY2llcyAqL1xuaW1wb3J0IHsgam9pbiB9IGZyb20gXCJub2RlOnBhdGhcIjtcbmltcG9ydCB7XG4gIEF0dHJpYnV0ZVZhbHVlLFxuICBCYXRjaEdldEl0ZW1Db21tYW5kLFxuICBCYXRjaEdldEl0ZW1Db21tYW5kT3V0cHV0LFxuICBEeW5hbW9EQkNsaWVudCxcbiAgUHV0SXRlbUNvbW1hbmQsXG4gIFF1ZXJ5Q29tbWFuZCxcbiAgUXVlcnlDb21tYW5kT3V0cHV0LFxuICBVcGRhdGVJdGVtQ29tbWFuZCxcbn0gZnJvbSBcIkBhd3Mtc2RrL2NsaWVudC1keW5hbW9kYlwiO1xuaW1wb3J0IHtcbiAgR2V0T2JqZWN0Q29tbWFuZCxcbiAgTm9TdWNoS2V5LFxuICBQdXRPYmplY3RDb21tYW5kLFxuICBTM0NsaWVudCxcbn0gZnJvbSBcIkBhd3Mtc2RrL2NsaWVudC1zM1wiO1xuXG4vKipcbiAqIFdoZXJlIHRoZSBjYWNoZSBsaXZlcywgYXMgdGhlIGNvbnN0cnVjdHMgcGFzcyBpdCB0byB0aGUgY29tcHV0ZSB0aHJvdWdoIHRoZVxuICogZW52aXJvbm1lbnQgKGBOZXh0anNGdW5jdGlvbnNgLCBgTmV4dGpzQ29udGFpbmVyc2ApLiBFbXB0eSBzdHJpbmdzIGZvciB3aGF0IGlzXG4gKiBub3QgY29uZmlndXJlZCwgd2hpY2ggZWFjaCBoYW5kbGVyIHJlYWRzIGFzIFwidGhhdCBsYXllciBpcyBkaXNhYmxlZFwiLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIEF3c0NhY2hlQ29uZmlnIHtcbiAgLyoqIGBDREtfTkVYVEpTX0NBQ0hFX0JVQ0tFVF9OQU1FYC4gKi9cbiAgYnVja2V0TmFtZTogc3RyaW5nO1xuICAvKiogYENES19ORVhUSlNfUkVWQUxJREFUSU9OX1RBQkxFX05BTUVgLiAqL1xuICB0YWJsZU5hbWU6IHN0cmluZztcbiAgLyoqIGBBV1NfUkVHSU9OYCwgYHVzLWVhc3QtMWAgd2hlbiB1bnNldC4gKi9cbiAgcmVnaW9uOiBzdHJpbmc7XG4gIC8qKiBgQ0RLX05FWFRKU19CVUlMRF9JRGA6IGV2ZXJ5IFMzIGtleSBhbmQgZXZlcnkgbWFya2VyIHJvdyBpcyBzY29wZWQgdG8gaXQuICovXG4gIGJ1aWxkSWQ6IHN0cmluZztcbn1cblxuLyoqIFRoZSBjb25maWd1cmF0aW9uIGZyb20gdGhlIGVudmlyb25tZW50LiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVBd3NDYWNoZUNvbmZpZygpOiBBd3NDYWNoZUNvbmZpZyB7XG4gIHJldHVybiB7XG4gICAgYnVja2V0TmFtZTogcHJvY2Vzcy5lbnYuQ0RLX05FWFRKU19DQUNIRV9CVUNLRVRfTkFNRSB8fCBcIlwiLFxuICAgIHRhYmxlTmFtZTogcHJvY2Vzcy5lbnYuQ0RLX05FWFRKU19SRVZBTElEQVRJT05fVEFCTEVfTkFNRSB8fCBcIlwiLFxuICAgIHJlZ2lvbjogcHJvY2Vzcy5lbnYuQVdTX1JFR0lPTiB8fCBcInVzLWVhc3QtMVwiLFxuICAgIGJ1aWxkSWQ6IHByb2Nlc3MuZW52LkNES19ORVhUSlNfQlVJTERfSUQgfHwgXCJcIixcbiAgfTtcbn1cblxuLyoqXG4gKiBge2J1aWxkSWR9L3tjYWNoZUtleX0uanNvbmAsIHRoZSBpbmNyZW1lbnRhbCBjYWNoZSdzIGtleSBjb252ZW50aW9uLlxuICpcbiAqIFRoZSByb290IHBhdGggYmVjb21lcyBgaW5kZXhgLCBhbmQgYSBsZWFkaW5nIHNsYXNoIGlzIGRyb3BwZWQgc28gdGhlcmUgaXMgbm9cbiAqIGVtcHR5IGZvbGRlciB1bmRlciB0aGUgYnVpbGQgcHJlZml4LiBFdmVyeXRoaW5nIHRoaXMgcmV0dXJucyBlbmRzIGluIGAuanNvbmAsXG4gKiB3aGljaCBpcyB3aGF0IGtlZXBzIGl0IGRpc2pvaW50IGZyb20ge0BsaW5rIHVzZUNhY2hlUzNLZXl9LlxuICovXG5leHBvcnQgZnVuY3Rpb24gYnVpbGRTM0tleShidWlsZElkOiBzdHJpbmcsIGNhY2hlS2V5OiBzdHJpbmcpOiBzdHJpbmcge1xuICBsZXQgY2xlYW5DYWNoZUtleSA9IGNhY2hlS2V5O1xuICBpZiAoY2FjaGVLZXkgPT09IFwiL1wiIHx8IGNhY2hlS2V5ID09PSBcIlwiKSB7XG4gICAgY2xlYW5DYWNoZUtleSA9IFwiaW5kZXhcIjtcbiAgfSBlbHNlIGlmIChjYWNoZUtleS5zdGFydHNXaXRoKFwiL1wiKSkge1xuICAgIGNsZWFuQ2FjaGVLZXkgPSBjYWNoZUtleS5zbGljZSgxKTtcbiAgfVxuICByZXR1cm4gam9pbihidWlsZElkLCBgJHtjbGVhbkNhY2hlS2V5fS5qc29uYCk7XG59XG5cbi8qKlxuICogVGhlIGZvbGRlciB1bmRlciB0aGUgYnVpbGQgcHJlZml4IHRoYXQgYCd1c2UgY2FjaGUnYCBlbnRyaWVzIGFyZSBzdG9yZWQgaW4uXG4gKiBAc2VlIHVzZUNhY2hlUzNLZXlcbiAqL1xuZXhwb3J0IGNvbnN0IFVTRV9DQUNIRV9LRVlfUFJFRklYID0gXCJfdXNlLWNhY2hlXCI7XG5cbi8qKlxuICogYHtidWlsZElkfS9fdXNlLWNhY2hlL3toYXNofS5lbnRyeWAsIHdoZXJlIGEgYGNhY2hlSGFuZGxlcnNgIGVudHJ5IGlzIHN0b3JlZC5cbiAqXG4gKiBIYXNoZWQgYmVjYXVzZSBhIGAndXNlIGNhY2hlJ2Aga2V5IGlzIHRoZSBzZXJpYWxpemVkIGFyZ3VtZW50cyBvZiB0aGUgY2FsbCBhbmRcbiAqIGhhcyBubyBsZW5ndGggbGltaXQsIHdoZXJlIGFuIFMzIGtleSBzdG9wcyBhdCAxMDI0IGJ5dGVzLiBUaGUgYC5lbnRyeWAgc3VmZml4XG4gKiByYXRoZXIgdGhhbiBgLmpzb25gIGlzIHdoYXQgbWFrZXMgYSBjb2xsaXNpb24gd2l0aCB0aGUgaW5jcmVtZW50YWwgY2FjaGVcbiAqIGltcG9zc2libGUgcmF0aGVyIHRoYW4gdW5saWtlbHk6IGV2ZXJ5IGtleSB7QGxpbmsgYnVpbGRTM0tleX0gd3JpdGVzIGVuZHMgaW5cbiAqIGAuanNvbmAsIGluY2x1ZGluZyBvbmUgZm9yIGEgcm91dGUgdGhhdCBoYXBwZW5zIHRvIGJlIG5hbWVkIGAvX3VzZS1jYWNoZS/igKZgLlxuICogU3RpbGwgdW5kZXIgdGhlIGJ1aWxkIHByZWZpeCwgc28gcG9zdC1kZXBsb3kgcHJ1bmluZyBkcm9wcyB0aGVtIHdpdGggdGhlIGJ1aWxkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdXNlQ2FjaGVTM0tleShidWlsZElkOiBzdHJpbmcsIGtleUhhc2g6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiBqb2luKGJ1aWxkSWQsIFVTRV9DQUNIRV9LRVlfUFJFRklYLCBgJHtrZXlIYXNofS5lbnRyeWApO1xufVxuXG4vKipcbiAqIFRoZSBgZHVyYXRpb25zYCBOZXh0LmpzIDE2IHBhc3NlcyBhIHRhZyByZXZhbGlkYXRpb24gZm9yIGEgcHJvZmlsZTpcbiAqIGByZXZhbGlkYXRlVGFnKFwicG9zdHNcIiwgXCJtYXhcIilgIGFycml2ZXMgYXMgYHsgZXhwaXJlOiA8dGhlIHByb2ZpbGUncyBleHBpcmU+IH1gLlxuICogQWJzZW50IGZvciBgdXBkYXRlVGFnYCBhbmQgYSBiYXJlIGByZXZhbGlkYXRlVGFnYCwgd2hpY2ggZXhwaXJlIGltbWVkaWF0ZWx5LlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFJldmFsaWRhdGVEdXJhdGlvbnMge1xuICBleHBpcmU/OiBudW1iZXI7XG59XG5cbi8qKlxuICogQSB0YWcncyBtYXJrZXIgcm93LCBhcyB0aW1lc3RhbXBzIGluIG1pbGxpc2Vjb25kcy4gV3JpdHRlbiBieVxuICoge0BsaW5rIG1hcmtlclVwZGF0ZX07IGVhY2ggZmllbGQgaXMgYWJzZW50IHVudGlsIGEgcmV2YWxpZGF0aW9uIG9mIHRoYXQgc2hhcGVcbiAqIGhhcyBoYXBwZW5lZC5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBUYWdNYXJrZXIge1xuICAvKiogRXhwaXJlZCBvdXRyaWdodDogYHVwZGF0ZVRhZ2AsIG9yIGByZXZhbGlkYXRlVGFnYCB3aXRoIG5vIHByb2ZpbGUuICovXG4gIHJldmFsaWRhdGVkQXQ/OiBudW1iZXI7XG4gIC8qKiBTdGFsZSBmcm9tIGhlcmU6IGByZXZhbGlkYXRlVGFnKHRhZywgcHJvZmlsZSlgLiAqL1xuICBzdGFsZUF0PzogbnVtYmVyO1xuICAvKiogV2hlbiB0aGUgcHJvZmlsZSdzIGBleHBpcmVgIHJ1bnMgb3V0LCB3aGljaCBtYXkgYmUgaW4gdGhlIGZ1dHVyZS4gKi9cbiAgZXhwaXJlZEF0PzogbnVtYmVyO1xufVxuXG4vKipcbiAqIFdoYXQgdGhlIHRhZyBtYXJrZXJzIHNheSBhYm91dCBhbiBlbnRyeTogdW50b3VjaGVkLCBzZXJ2ZWQgc3RhbGUgd2hpbGUgYVxuICogYmFja2dyb3VuZCByZW5kZXIgcmVwbGFjZXMgaXQsIG9yIGV4cGlyZWQgYW5kIHJlLXJlbmRlcmVkIGJlZm9yZSBhbnN3ZXJpbmcuXG4gKi9cbmV4cG9ydCB0eXBlIFJldmFsaWRhdGlvblN0YXRlID0gXCJmcmVzaFwiIHwgXCJzdGFsZVwiIHwgXCJleHBpcmVkXCI7XG5cbi8qKlxuICogV2hhdCBgbWFya2VyYCBtZWFucyBmb3IgYW4gZW50cnkgY3JlYXRlZCBhdCBgY3JlYXRlZEF0YCwgYXQgYG5vd2AuXG4gKlxuICogYGV4cGlyZWRBdGAgaW4gdGhlIGZ1dHVyZSBpcyBhIHByb2ZpbGUncyBgZXhwaXJlYCB0aGF0IGhhcyBub3QgY29tZSB5ZXQsXG4gKiBjb21wYXJlZCB0aGUgd2F5IE5leHQuanMncyBgYXJlVGFnc0V4cGlyZWRgIGRvZXMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBtYXJrZXJTdGF0ZShcbiAgbWFya2VyOiBUYWdNYXJrZXIsXG4gIGNyZWF0ZWRBdDogbnVtYmVyLFxuICBub3c6IG51bWJlcixcbik6IFJldmFsaWRhdGlvblN0YXRlIHtcbiAgY29uc3QgeyByZXZhbGlkYXRlZEF0LCBleHBpcmVkQXQsIHN0YWxlQXQgfSA9IG1hcmtlcjtcbiAgaWYgKFxuICAgIChyZXZhbGlkYXRlZEF0ICE9PSB1bmRlZmluZWQgJiYgcmV2YWxpZGF0ZWRBdCA+IGNyZWF0ZWRBdCkgfHxcbiAgICAoZXhwaXJlZEF0ICE9PSB1bmRlZmluZWQgJiYgZXhwaXJlZEF0IDw9IG5vdyAmJiBleHBpcmVkQXQgPiBjcmVhdGVkQXQpXG4gICkge1xuICAgIHJldHVybiBcImV4cGlyZWRcIjtcbiAgfVxuICBpZiAoc3RhbGVBdCAhPT0gdW5kZWZpbmVkICYmIHN0YWxlQXQgPiBjcmVhdGVkQXQpIHtcbiAgICByZXR1cm4gXCJzdGFsZVwiO1xuICB9XG4gIHJldHVybiBcImZyZXNoXCI7XG59XG5cbi8qKlxuICogVGhlIGNsb2NrIGV2ZXJ5IHRhZyBtYXJrZXIgaXMgc3RhbXBlZCB3aXRoLCBhbmQgZXZlcnkgZW50cnkgdGltZXN0YW1wIGl0IGlzXG4gKiBjb21wYXJlZCBhZ2FpbnN0OiB0aGUgb25lIE5leHQuanMgc3RhbXBzIGBDYWNoZUVudHJ5LnRpbWVzdGFtcGAgd2l0aFxuICogKGBwZXJmb3JtYW5jZS50aW1lT3JpZ2luICsgcGVyZm9ybWFuY2Uubm93KClgLCBpbiBgdXNlLWNhY2hlLXdyYXBwZXIuanNgIGFuZFxuICogdGhlIGRlZmF1bHQgaGFuZGxlciksIHJhdGhlciB0aGFuIGBEYXRlLm5vdygpYC4gQm90aCBoYW5kbGVycyB3cml0ZSB0aGUgc2FtZVxuICogbWFya2VyIHJvd3MsIHNvIGJvdGggaGF2ZSB0byB1c2UgaXQ6IHR3byBjbG9ja3Mgb24gb25lIHJvdyBsZXQgdGhlIGxhc3Qgd3JpdGVyXG4gKiBkZWNpZGUgd2hpY2ggY2xvY2sgdGhlIG90aGVycyBjb21wYXJlIGFnYWluc3QsIGFuZCB0aGV5IGRyaWZ0IGFwYXJ0IGZvciBhc1xuICogbG9uZyBhcyBhIExhbWJkYSBzYW5kYm94IG9yIEZhcmdhdGUgdGFzayBsaXZlcy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG1hcmtlckNsb2NrKCk6IG51bWJlciB7XG4gIHJldHVybiBwZXJmb3JtYW5jZS50aW1lT3JpZ2luICsgcGVyZm9ybWFuY2Uubm93KCk7XG59XG5cbi8qKlxuICogVGhlIG1hcmtlciBmaWVsZHMgYSB0YWcgcmV2YWxpZGF0ZWQgYXQgYG5vd2Agc2V0cy5cbiAqXG4gKiBXaXRob3V0IGBkdXJhdGlvbnNgIC0gYHVwZGF0ZVRhZ2AsIG9yIGByZXZhbGlkYXRlVGFnYCB3aXRoIG5vIHByb2ZpbGUgLSB0aGVcbiAqIHRhZydzIGVudHJpZXMgZXhwaXJlIGltbWVkaWF0ZWx5IChgcmV2YWxpZGF0ZWRBdGApLiBXaXRoIHRoZW0gdGhlIGVudHJpZXMgZ29cbiAqIHN0YWxlIG5vdyBhbmQgZXhwaXJlIGFmdGVyIHRoZSBwcm9maWxlJ3MgYGV4cGlyZWAsIHRoZSBzYW1lIHBhaXIgb2ZcbiAqIHRpbWVzdGFtcHMgTmV4dC5qcydzIGBGaWxlU3lzdGVtQ2FjaGUucmV2YWxpZGF0ZVRhZ2AgcmVjb3Jkcy4gQSBwcm9maWxlIHdpdGhcbiAqIG5vIGBleHBpcmVgIHNldHMgb25seSBgc3RhbGVBdGAsIGFuZCB3aGF0ZXZlciBgZXhwaXJlZEF0YCB0aGUgdGFnIGhhZCBzdGF5cy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG1hcmtlckZvcihcbiAgbm93OiBudW1iZXIsXG4gIGR1cmF0aW9uczogUmV2YWxpZGF0ZUR1cmF0aW9ucyB8IHVuZGVmaW5lZCxcbik6IFRhZ01hcmtlciB7XG4gIGlmICghZHVyYXRpb25zKSB7XG4gICAgcmV0dXJuIHsgcmV2YWxpZGF0ZWRBdDogbm93IH07XG4gIH1cbiAgaWYgKGR1cmF0aW9ucy5leHBpcmUgPT09IHVuZGVmaW5lZCkge1xuICAgIHJldHVybiB7IHN0YWxlQXQ6IG5vdyB9O1xuICB9XG4gIHJldHVybiB7IHN0YWxlQXQ6IG5vdywgZXhwaXJlZEF0OiBub3cgKyBkdXJhdGlvbnMuZXhwaXJlICogMTAwMCB9O1xufVxuXG5jb25zdCBNQVJLRVJfRklFTERTID0gW1wicmV2YWxpZGF0ZWRBdFwiLCBcInN0YWxlQXRcIiwgXCJleHBpcmVkQXRcIl0gYXMgY29uc3Q7XG5cbi8qKlxuICogYGV4aXN0aW5nYCB3aXRoIHRoZSBmaWVsZHMgYHVwZGF0ZWAgc2V0cywgYXMgYSB7QGxpbmsgbWFya2VyVXBkYXRlfSBsZWF2ZXNcbiAqIHRoZSByb3c6IGEgZmllbGQgYHVwZGF0ZWAgZG9lcyBub3Qgc2V0IGtlZXBzIGl0cyB2YWx1ZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGFwcGx5TWFya2VyKFxuICBleGlzdGluZzogVGFnTWFya2VyIHwgdW5kZWZpbmVkLFxuICB1cGRhdGU6IFRhZ01hcmtlcixcbik6IFRhZ01hcmtlciB7XG4gIGNvbnN0IG1hcmtlciA9IHsgLi4uZXhpc3RpbmcgfTtcbiAgZm9yIChjb25zdCBmaWVsZCBvZiBNQVJLRVJfRklFTERTKSB7XG4gICAgaWYgKHVwZGF0ZVtmaWVsZF0gIT09IHVuZGVmaW5lZCkge1xuICAgICAgbWFya2VyW2ZpZWxkXSA9IHVwZGF0ZVtmaWVsZF07XG4gICAgfVxuICB9XG4gIHJldHVybiBtYXJrZXI7XG59XG5cbi8qKiBUaGUgbWFya2VyLXJvdyB1cGRhdGUgdGhhdCBzZXRzIHtAbGluayBtYXJrZXJGb3J9J3MgZmllbGRzLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG1hcmtlclVwZGF0ZShcbiAgbm93OiBudW1iZXIsXG4gIGR1cmF0aW9uczogUmV2YWxpZGF0ZUR1cmF0aW9ucyB8IHVuZGVmaW5lZCxcbik6IFBpY2s8XG4gIENvbnN0cnVjdG9yUGFyYW1ldGVyczx0eXBlb2YgVXBkYXRlSXRlbUNvbW1hbmQ+WzBdLFxuICBcIlVwZGF0ZUV4cHJlc3Npb25cIiB8IFwiRXhwcmVzc2lvbkF0dHJpYnV0ZVZhbHVlc1wiXG4+IHtcbiAgY29uc3QgbWFya2VyID0gbWFya2VyRm9yKG5vdywgZHVyYXRpb25zKTtcbiAgY29uc3QgZmllbGRzID0gTUFSS0VSX0ZJRUxEUy5maWx0ZXIoKGZpZWxkKSA9PiBtYXJrZXJbZmllbGRdICE9PSB1bmRlZmluZWQpO1xuICByZXR1cm4ge1xuICAgIFVwZGF0ZUV4cHJlc3Npb246IGBTRVQgJHtmaWVsZHMubWFwKChmaWVsZCkgPT4gYCR7ZmllbGR9ID0gOiR7ZmllbGR9YCkuam9pbihcIiwgXCIpfWAsXG4gICAgRXhwcmVzc2lvbkF0dHJpYnV0ZVZhbHVlczogT2JqZWN0LmZyb21FbnRyaWVzKFxuICAgICAgZmllbGRzLm1hcCgoZmllbGQpID0+IFtgOiR7ZmllbGR9YCwgeyBOOiBTdHJpbmcobWFya2VyW2ZpZWxkXSkgfV0pLFxuICAgICksXG4gIH07XG59XG5cbi8qKiBBIER5bmFtb0RCIG51bWJlciBhdHRyaWJ1dGUgYXMgYSBudW1iZXIsIG9yIGB1bmRlZmluZWRgIHdoZW4gYWJzZW50LiAqL1xuZnVuY3Rpb24gbnVtYmVyQXR0cmlidXRlKFxuICB2YWx1ZTogQXR0cmlidXRlVmFsdWUgfCB1bmRlZmluZWQsXG4pOiBudW1iZXIgfCB1bmRlZmluZWQge1xuICByZXR1cm4gdmFsdWU/Lk4gPT09IHVuZGVmaW5lZCA/IHVuZGVmaW5lZCA6IE51bWJlcih2YWx1ZS5OKTtcbn1cblxuLyoqIFRoZSBtYXJrZXIgZmllbGRzIG9mIGEgbWFya2VyIG9yIGxvZyByb3cuICovXG5mdW5jdGlvbiBtYXJrZXJPZihpdGVtOiBSZWNvcmQ8c3RyaW5nLCBBdHRyaWJ1dGVWYWx1ZT4pOiBUYWdNYXJrZXIge1xuICByZXR1cm4ge1xuICAgIHJldmFsaWRhdGVkQXQ6IG51bWJlckF0dHJpYnV0ZShpdGVtLnJldmFsaWRhdGVkQXQpLFxuICAgIHN0YWxlQXQ6IG51bWJlckF0dHJpYnV0ZShpdGVtLnN0YWxlQXQpLFxuICAgIGV4cGlyZWRBdDogbnVtYmVyQXR0cmlidXRlKGl0ZW0uZXhwaXJlZEF0KSxcbiAgfTtcbn1cblxuLyoqIER5bmFtb0RCJ3MgY2FwIG9uIGtleXMgaW4gb25lIGBCYXRjaEdldEl0ZW1gLiAqL1xuZXhwb3J0IGNvbnN0IEJBVENIX0dFVF9NQVhfS0VZUyA9IDEwMDtcblxuLyoqXG4gKiBIb3cgb2Z0ZW4sIGF0IG1vc3QsIGFuIGluc3RhbmNlIGNhdGNoZXMgdXAgb24gcmV2YWxpZGF0aW9ucyBvdGhlciBpbnN0YW5jZXNcbiAqIHJhbiwgdW5sZXNzIGBDREtfTkVYVEpTX1RBR19SRUZSRVNIX01TYCBzYXlzIG90aGVyd2lzZTogdGhlIHN0YWxlbmVzcyB3aW5kb3dcbiAqIGZvciBhIGByZXZhbGlkYXRlVGFnYCBydW4gZWxzZXdoZXJlLiBFdmVyeSBjYWNoZSBoaXQgY2hlY2tzIGl0cyB0YWdzJ1xuICogbWFya2VycywgYW5kIGV2ZXJ5IG1hcmtlciBvZiBhIGRlcGxveW1lbnQgc2hhcmVzIG9uZSBwYXJ0aXRpb24ga2V5LCBzb1xuICogcmVhZGluZyB0aGVtIHBlciByZXF1ZXN0IGNhcHBlZCBhIHJvdXRlJ3MgY2FjaGUgaGl0cyBhdCB0aGUgcGFydGl0aW9uJ3MgcmVhZFxuICogdGhyb3VnaHB1dCAofjEsOTAwIHJlcS9zIG9uIE5leHRqc1JlZ2lvbmFsRnVuY3Rpb25zKS4gT25jZSBhIHNlY29uZCwgZnJvbSB0aGVcbiAqIHJldmFsaWRhdGlvbiBsb2csIHRoZSByZWFkcyBzY2FsZSB3aXRoIGluc3RhbmNlcyBpbnN0ZWFkIG9mIHJlcXVlc3RzIG9yIHRhZ3MuXG4gKi9cbmV4cG9ydCBjb25zdCBERUZBVUxUX1RBR19SRUZSRVNIX01TID0gMTAwMDtcblxuLyoqXG4gKiBUaGUgYmFyZS10YWcgbWFya2VyIHJvd3Mgb2YgdGhlIHJldmFsaWRhdGlvbiB0YWJsZSAoYHBrID0gYnVpbGRJZGAsXG4gKiBgc2sgPSB0YWdgKSwgd2hpY2ggcmVjb3JkIHdoZW4gYSB0YWcgd2FzIGxhc3QgcmV2YWxpZGF0ZWQgYW5kIGhvdy5cbiAqXG4gKiBUaGUgdGFibGUgYWxzbyBob2xkcyB0aGUgaW5jcmVtZW50YWwgY2FjaGUncyBgdGFnI3MzS2V5YCBtYXBwaW5nIHJvd3MsIHdoaWNoXG4gKiBhcmUgaXRzIG93biBidXNpbmVzcyBhbmQgc3RheSBpbiBgUzNDYWNoZUhhbmRsZXJgOiBvbmx5IHRoZSBtYXJrZXJzIG1lYW4gdGhlXG4gKiBzYW1lIHRoaW5nIHRvIGV2ZXJ5IGhhbmRsZXIuIFdoYXQgYW4gaW5zdGFuY2Uga2VlcHMgb2YgdGhlbSBiZXR3ZWVuIHJlYWRzIGlzXG4gKiB7QGxpbmsgVHJhY2tlZFRhZ01hcmtlcnN9LlxuICovXG5leHBvcnQgY2xhc3MgVGFnTWFya2VyVGFibGUge1xuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHJlYWRvbmx5IGNsaWVudDogRHluYW1vREJDbGllbnQsXG4gICAgcHJpdmF0ZSByZWFkb25seSB0YWJsZU5hbWU6IHN0cmluZyxcbiAgICBwcml2YXRlIHJlYWRvbmx5IGJ1aWxkSWQ6IHN0cmluZyxcbiAgKSB7fVxuXG4gIC8qKlxuICAgKiBSZWNvcmQgYHRhZ2AncyByZXZhbGlkYXRpb24gYXQgYG5vd2AuIFNlZSB7QGxpbmsgbWFya2VyVXBkYXRlfS5cbiAgICpcbiAgICogUmV0dXJucyB0aGUgcm93IGFzIHRoaXMgd3JpdGUgbGVmdCBpdCwgc28gdGhlIGluc3RhbmNlIHRoYXQgd3JvdGUgaXQgY2FuXG4gICAqIHRyYWNrIGl0IHJhdGhlciB0aGFuIHJlYWQgaXQgYmFjazogcmVhZHMgYXJlIGV2ZW50dWFsbHkgY29uc2lzdGVudCwgYW5kIGFcbiAgICogcmVhZC1iYWNrIHJhY2luZyByZXBsaWNhdGlvbiBjb3VsZCByZXR1cm4gdGhlIG1hcmtlciBmcm9tIGJlZm9yZSB0aGUgd3JpdGUuXG4gICAqL1xuICBhc3luYyB3cml0ZShcbiAgICB0YWc6IHN0cmluZyxcbiAgICBub3c6IG51bWJlcixcbiAgICBkdXJhdGlvbnM6IFJldmFsaWRhdGVEdXJhdGlvbnMgfCB1bmRlZmluZWQsXG4gICk6IFByb21pc2U8VGFnTWFya2VyIHwgdW5kZWZpbmVkPiB7XG4gICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCB0aGlzLmNsaWVudC5zZW5kKFxuICAgICAgbmV3IFVwZGF0ZUl0ZW1Db21tYW5kKHtcbiAgICAgICAgVGFibGVOYW1lOiB0aGlzLnRhYmxlTmFtZSxcbiAgICAgICAgS2V5OiB7XG4gICAgICAgICAgcGs6IHsgUzogdGhpcy5idWlsZElkIH0sXG4gICAgICAgICAgc2s6IHsgUzogdGFnIH0sXG4gICAgICAgIH0sXG4gICAgICAgIC4uLm1hcmtlclVwZGF0ZShub3csIGR1cmF0aW9ucyksXG4gICAgICAgIFJldHVyblZhbHVlczogXCJBTExfTkVXXCIsXG4gICAgICB9KSxcbiAgICApO1xuICAgIHJldHVybiByZXNwb25zZT8uQXR0cmlidXRlcyA/IG1hcmtlck9mKHJlc3BvbnNlLkF0dHJpYnV0ZXMpIDogdW5kZWZpbmVkO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBtYXJrZXIgcm93cyBmb3IgYHRhZ3NgIHRoYXQgZXhpc3QsIGJ5IHRhZywgaW4gb25lIGBCYXRjaEdldEl0ZW1gIHBlclxuICAgKiB7QGxpbmsgQkFUQ0hfR0VUX01BWF9LRVlTfSB0YWdzIHJhdGhlciB0aGFuIGEgYEdldEl0ZW1gIGVhY2g6IGEgcGFnZVxuICAgKiBjYXJyaWVzIGl0cyB3aG9sZSBpbXBsaWNpdCBgX05fVF8v4oCmYCBjaGFpbiBwbHVzIHRoZSBhcHAncyBvd24gdGFncywgYVxuICAgKiBwYWdlIHdpdGggYSBmZXcgZmV0Y2hlcyBjaGVja3MgZWFjaCBvZiB0aGVtIHRvbywgYW5kIGV2ZXJ5IG9uZSBpcyBvbiB0aGVcbiAgICogcmVxdWVzdCBwYXRoLiBBIHRhZyB3aXRoIG5vIHJvdyBpcyBhYnNlbnQgZnJvbSB0aGUgcmVzdWx0LlxuICAgKi9cbiAgYXN5bmMgcmVhZCh0YWdzOiBzdHJpbmdbXSk6IFByb21pc2U8TWFwPHN0cmluZywgVGFnTWFya2VyPj4ge1xuICAgIGNvbnN0IHsgdGFibGVOYW1lLCBidWlsZElkIH0gPSB0aGlzO1xuICAgIGNvbnN0IHVuaXF1ZSA9IEFycmF5LmZyb20obmV3IFNldCh0YWdzKSk7XG4gICAgY29uc3QgbWFya2VycyA9IG5ldyBNYXA8c3RyaW5nLCBUYWdNYXJrZXI+KCk7XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHVuaXF1ZS5sZW5ndGg7IGkgKz0gQkFUQ0hfR0VUX01BWF9LRVlTKSB7XG4gICAgICBsZXQga2V5czogUmVjb3JkPHN0cmluZywgQXR0cmlidXRlVmFsdWU+W10gfCB1bmRlZmluZWQgPSB1bmlxdWVcbiAgICAgICAgLnNsaWNlKGksIGkgKyBCQVRDSF9HRVRfTUFYX0tFWVMpXG4gICAgICAgIC5tYXAoKHRhZykgPT4gKHsgcGs6IHsgUzogYnVpbGRJZCB9LCBzazogeyBTOiB0YWcgfSB9KSk7XG4gICAgICAvLyBgVW5wcm9jZXNzZWRLZXlzYCBpcyBEeW5hbW9EQiBkZWNsaW5pbmcgcGFydCBvZiB0aGUgYmF0Y2ggdW5kZXIgbG9hZDtcbiAgICAgIC8vIGEgbWFya2VyIGxlZnQgdW5yZWFkIGlzIGEgcmV2YWxpZGF0aW9uIG1pc3NlZCwgc28gaXQgaXMgYXNrZWQgYWdhaW4uXG4gICAgICBmb3IgKGxldCBhdHRlbXB0ID0gMDsga2V5cz8ubGVuZ3RoICYmIGF0dGVtcHQgPCAzOyBhdHRlbXB0KyspIHtcbiAgICAgICAgY29uc3QgcmVzcG9uc2U6IEJhdGNoR2V0SXRlbUNvbW1hbmRPdXRwdXQgPSBhd2FpdCB0aGlzLmNsaWVudC5zZW5kKFxuICAgICAgICAgIG5ldyBCYXRjaEdldEl0ZW1Db21tYW5kKHtcbiAgICAgICAgICAgIFJlcXVlc3RJdGVtczoge1xuICAgICAgICAgICAgICBbdGFibGVOYW1lXToge1xuICAgICAgICAgICAgICAgIEtleXM6IGtleXMsXG4gICAgICAgICAgICAgICAgUHJvamVjdGlvbkV4cHJlc3Npb246IFwic2ssIHJldmFsaWRhdGVkQXQsIHN0YWxlQXQsIGV4cGlyZWRBdFwiLFxuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9KSxcbiAgICAgICAgKTtcbiAgICAgICAgZm9yIChjb25zdCBpdGVtIG9mIHJlc3BvbnNlLlJlc3BvbnNlcz8uW3RhYmxlTmFtZV0gPz8gW10pIHtcbiAgICAgICAgICBjb25zdCB0YWcgPSBpdGVtLnNrPy5TO1xuICAgICAgICAgIGlmICh0YWcgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgbWFya2Vycy5zZXQodGFnLCBtYXJrZXJPZihpdGVtKSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGtleXMgPSByZXNwb25zZS5VbnByb2Nlc3NlZEtleXM/Llt0YWJsZU5hbWVdPy5LZXlzO1xuICAgICAgfVxuICAgICAgaWYgKGtleXM/Lmxlbmd0aCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgYER5bmFtb0RCIGxlZnQgJHtrZXlzLmxlbmd0aH0gdGFnIG1hcmtlcnMgdW5yZWFkIGFmdGVyIHJldHJ5aW5nYCxcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG1hcmtlcnM7XG4gIH1cbn1cblxuLyoqXG4gKiBIb3cgbG9uZyBhIHJldmFsaWRhdGlvbiBsb2cgcm93IGlzIGtlcHQgKGB0dGxgLCBlcG9jaCBzZWNvbmRzKS4gRHluYW1vREIgbmV2ZXJcbiAqIGRlbGV0ZXMgYW4gaXRlbSBiZWZvcmUgaXRzIFRUTCwgb25seSBzb21lIHRpbWUgYWZ0ZXIsIHNvIGEgcmVhZGVyIHRoYXQgbGFzdFxuICogcXVlcmllZCBsZXNzIHRoYW4gdGhpcyBsb25nIGFnbyAobGVzcyBhIG1hcmdpbiBmb3Igd3JpdGVycycgY2xvY2tzKSBpcyBzdXJlXG4gKiB0byBmaW5kIGV2ZXJ5IHJvdyBpdCBoYXMgbm90IHNlZW4geWV0LlxuICovXG5leHBvcnQgY29uc3QgUkVWQUxJREFUSU9OX0xPR19UVExfTVMgPSAxNSAqIDYwICogMTAwMDtcblxuLyoqIFRoZSBtb3N0IGBRdWVyeWAgcGFnZXMgb25lIHtAbGluayBSZXZhbGlkYXRpb25Mb2cucXVlcnl9IHJlYWRzLiAqL1xuZXhwb3J0IGNvbnN0IFJFVkFMSURBVElPTl9MT0dfTUFYX1BBR0VTID0gNTtcblxuLyoqIEhvdyBtYW55IHNvcnQga2V5cyB7QGxpbmsgUmV2YWxpZGF0aW9uTG9nLnB1dH0gdHJpZXMgYmVmb3JlIGdpdmluZyB1cC4gKi9cbmNvbnN0IExPR19QVVRfQVRURU1QVFMgPSA1O1xuXG4vKiogRGlnaXRzIGEgbG9nIHJvdydzIHRpbWVzdGFtcCBpcyB6ZXJvLXBhZGRlZCB0bywgc28gc29ydCBrZXlzIHNvcnQgYnkgdGltZS4gKi9cbmNvbnN0IExPR19TS19ESUdJVFMgPSAxNTtcblxuLyoqIEEgcmV2YWxpZGF0aW9uIHJlYWQgYmFjayBmcm9tIHRoZSBsb2cuICovXG5leHBvcnQgaW50ZXJmYWNlIFJldmFsaWRhdGlvbkxvZ1JvdyB7XG4gIC8qKiBUaGUgcm93J3Mgc29ydCBrZXk6IHVuaXF1ZSBwZXIgcmV2YWxpZGF0aW9uIG9mIGEgdGFnLiAqL1xuICBzazogc3RyaW5nO1xuICAvKiogV2hlbiBpdCB3YXMgd3JpdHRlbiwgb24gdGhlIHdyaXRlcidzIGBEYXRlLm5vdygpYC4gKi9cbiAgYXQ6IG51bWJlcjtcbiAgdGFnOiBzdHJpbmc7XG4gIC8qKiBUaGUgbWFya2VyIGZpZWxkcyB0aGUgcmV2YWxpZGF0aW9uIHNldCwgYXMge0BsaW5rIG1hcmtlckZvcn0gcmV0dXJucyB0aGVtLiAqL1xuICBtYXJrZXI6IFRhZ01hcmtlcjtcbn1cblxuLyoqXG4gKiBUaGUgcmV2YWxpZGF0aW9uIGxvZzogb25lIHJvdyBwZXIgdGFnIHJldmFsaWRhdGlvbiAoYHBrID0gPGJ1aWxkSWQ+I2xvZ2AsXG4gKiBgc2sgPSA8RGF0ZS5ub3coKSwgemVyby1wYWRkZWQ+Izx0YWc+YCksIHdoaWNoIGV4cGlyZXMgYWZ0ZXJcbiAqIHtAbGluayBSRVZBTElEQVRJT05fTE9HX1RUTF9NU30uXG4gKlxuICogVGhlIG1hcmtlciByb3dzIHN0YXkgdGhlIHNvdXJjZSBvZiB0cnV0aC4gVGhlIGxvZyBleGlzdHMgc28gYW4gaW5zdGFuY2VcbiAqIGNhbiBhc2sgXCJ3aGF0IHdhcyByZXZhbGlkYXRlZCBzaW5jZSBJIGxhc3QgbG9va2VkP1wiIGluIG9uZSBgUXVlcnlgLCBpbnN0ZWFkXG4gKiBvZiByZS1yZWFkaW5nIGV2ZXJ5IHRhZyBpdCB0cmFja3MgdG8gZmluZCBvdXQgdGhhdCBhbG1vc3Qgbm9uZSBjaGFuZ2VkLiBFdmVyeVxuICogbWFya2VyIHJvdyBvZiBhIGRlcGxveW1lbnQgc2hhcmVzIG9uZSBwYXJ0aXRpb24sIGFuZCB0aG9zZSByZS1yZWFkcyBmcm9tIGFcbiAqIGhhbmRmdWwgb2YgYnVzeSBpbnN0YW5jZXMgd2VyZSBlbm91Z2ggdG8gdGhyb3R0bGUgaXQuXG4gKlxuICogVGhlIHNvcnQga2V5J3MgdGltZXN0YW1wIGlzIHRoZSB3cml0ZXIncyB3YWxsIGNsb2NrIChgRGF0ZS5ub3coKWApLCBub3QgdGhlXG4gKiBwZXJmb3JtYW5jZSBjbG9jayAoe0BsaW5rIG1hcmtlckNsb2NrfSkgdGhlIG1hcmtlciB2YWx1ZXMgYXJlIHN0YW1wZWQgd2l0aCBieSBib3RoXG4gKiBoYW5kbGVyczogaXQgb25seSBvcmRlcnMgcm93cyBmb3IgcmVhZGVycycgY3Vyc29ycywgd2hpY2ggYXJlIG9uIHRoZWlyIG93blxuICogd2FsbCBjbG9ja3MsIGFuZCBpcyBuZXZlciBjb21wYXJlZCB3aXRoIGFuIGVudHJ5J3MgdGltZXN0YW1wLlxuICovXG5leHBvcnQgY2xhc3MgUmV2YWxpZGF0aW9uTG9nIHtcbiAgcHJpdmF0ZSByZWFkb25seSBwazogc3RyaW5nO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgY2xpZW50OiBEeW5hbW9EQkNsaWVudCxcbiAgICBwcml2YXRlIHJlYWRvbmx5IHRhYmxlTmFtZTogc3RyaW5nLFxuICAgIGJ1aWxkSWQ6IHN0cmluZyxcbiAgKSB7XG4gICAgdGhpcy5wayA9IGAke2J1aWxkSWR9I2xvZ2A7XG4gIH1cblxuICAvKipcbiAgICogUmVjb3JkIHRoYXQgYHRhZ2Agd2FzIHJldmFsaWRhdGVkIGF0IGBhdGAgKGBEYXRlLm5vdygpYCksIHNldHRpbmcgYG1hcmtlcmAuXG4gICAqXG4gICAqIFR3byByZXZhbGlkYXRpb25zIG9mIGB0YWdgIGluIHRoZSBzYW1lIG1pbGxpc2Vjb25kIHdvdWxkIHNoYXJlIGEgc29ydCBrZXksXG4gICAqIGFuZCB0aGUgc2Vjb25kIHdvdWxkIG92ZXJ3cml0ZSB0aGUgZmlyc3QuIFNvIHRoZSByb3cgaXMgb25seSB3cml0dGVuIHdoZXJlXG4gICAqIGl0cyBrZXkgaXMgZnJlZSwgYW5kIG9uIGEgY29sbGlzaW9uIGl0IG1vdmVzIHRvIHRoZSBuZXh0IG1pbGxpc2Vjb25kLCB1cFxuICAgKiB0byB7QGxpbmsgTE9HX1BVVF9BVFRFTVBUU30gdGltZXMuXG4gICAqL1xuICBhc3luYyBwdXQodGFnOiBzdHJpbmcsIGF0OiBudW1iZXIsIG1hcmtlcjogVGFnTWFya2VyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgZm9yIChsZXQgYXR0ZW1wdCA9IDE7IDsgYXR0ZW1wdCsrKSB7XG4gICAgICBjb25zdCBpdGVtOiBSZWNvcmQ8c3RyaW5nLCBBdHRyaWJ1dGVWYWx1ZT4gPSB7XG4gICAgICAgIHBrOiB7IFM6IHRoaXMucGsgfSxcbiAgICAgICAgc2s6IHsgUzogYCR7bG9nU2tQcmVmaXgoYXQpfSMke3RhZ31gIH0sXG4gICAgICAgIHR0bDogeyBOOiBTdHJpbmcoTWF0aC5jZWlsKChhdCArIFJFVkFMSURBVElPTl9MT0dfVFRMX01TKSAvIDEwMDApKSB9LFxuICAgICAgfTtcbiAgICAgIGZvciAoY29uc3QgZmllbGQgb2YgTUFSS0VSX0ZJRUxEUykge1xuICAgICAgICBjb25zdCB2YWx1ZSA9IG1hcmtlcltmaWVsZF07XG4gICAgICAgIGlmICh2YWx1ZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgaXRlbVtmaWVsZF0gPSB7IE46IFN0cmluZyh2YWx1ZSkgfTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgdGhpcy5jbGllbnQuc2VuZChcbiAgICAgICAgICBuZXcgUHV0SXRlbUNvbW1hbmQoe1xuICAgICAgICAgICAgVGFibGVOYW1lOiB0aGlzLnRhYmxlTmFtZSxcbiAgICAgICAgICAgIEl0ZW06IGl0ZW0sXG4gICAgICAgICAgICBDb25kaXRpb25FeHByZXNzaW9uOiBcImF0dHJpYnV0ZV9ub3RfZXhpc3RzKHNrKVwiLFxuICAgICAgICAgIH0pLFxuICAgICAgICApO1xuICAgICAgICByZXR1cm47XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBpZiAoXG4gICAgICAgICAgKGVycm9yIGFzIEVycm9yIHwgdW5kZWZpbmVkKT8ubmFtZSAhPT1cbiAgICAgICAgICAgIFwiQ29uZGl0aW9uYWxDaGVja0ZhaWxlZEV4Y2VwdGlvblwiIHx8XG4gICAgICAgICAgYXR0ZW1wdCA+PSBMT0dfUFVUX0FUVEVNUFRTXG4gICAgICAgICkge1xuICAgICAgICAgIHRocm93IGVycm9yO1xuICAgICAgICB9XG4gICAgICAgIGF0ID0gTWF0aC5mbG9vcihhdCkgKyAxO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgcm93cyB3cml0dGVuIGF0IG9yIGFmdGVyIGBzaW5jZWAgKGBEYXRlLm5vdygpYCksIG9sZGVzdCBmaXJzdC5cbiAgICogYHRydW5jYXRlZGAgd2hlbiB0aGVyZSB3ZXJlIG1vcmUgdGhhbiB7QGxpbmsgUkVWQUxJREFUSU9OX0xPR19NQVhfUEFHRVN9XG4gICAqIHBhZ2VzIG9mIHRoZW0sIGFuZCBgcm93c2AgaXMgb25seSB0aGUgc3RhcnQuXG4gICAqXG4gICAqIEV2ZW50dWFsbHkgY29uc2lzdGVudDogYSByb3cgd3JpdHRlbiBhIG1vbWVudCBhZ28gbWF5IG5vdCBzaG93IHlldCwgd2hpY2hcbiAgICogaXMgd2hhdCB0aGUgcmVhZGVyJ3MgbG9va2JhY2sgaXMgZm9yLlxuICAgKi9cbiAgYXN5bmMgcXVlcnkoXG4gICAgc2luY2U6IG51bWJlcixcbiAgKTogUHJvbWlzZTx7IHJvd3M6IFJldmFsaWRhdGlvbkxvZ1Jvd1tdOyB0cnVuY2F0ZWQ6IGJvb2xlYW4gfT4ge1xuICAgIGNvbnN0IHJvd3M6IFJldmFsaWRhdGlvbkxvZ1Jvd1tdID0gW107XG4gICAgbGV0IHN0YXJ0S2V5OiBSZWNvcmQ8c3RyaW5nLCBBdHRyaWJ1dGVWYWx1ZT4gfCB1bmRlZmluZWQ7XG4gICAgZm9yIChsZXQgcGFnZSA9IDA7IHBhZ2UgPCBSRVZBTElEQVRJT05fTE9HX01BWF9QQUdFUzsgcGFnZSsrKSB7XG4gICAgICBjb25zdCByZXNwb25zZTogUXVlcnlDb21tYW5kT3V0cHV0ID0gYXdhaXQgdGhpcy5jbGllbnQuc2VuZChcbiAgICAgICAgbmV3IFF1ZXJ5Q29tbWFuZCh7XG4gICAgICAgICAgVGFibGVOYW1lOiB0aGlzLnRhYmxlTmFtZSxcbiAgICAgICAgICBLZXlDb25kaXRpb25FeHByZXNzaW9uOiBcInBrID0gOnBrIEFORCBzayA+PSA6c2luY2VcIixcbiAgICAgICAgICBFeHByZXNzaW9uQXR0cmlidXRlVmFsdWVzOiB7XG4gICAgICAgICAgICBcIjpwa1wiOiB7IFM6IHRoaXMucGsgfSxcbiAgICAgICAgICAgIFwiOnNpbmNlXCI6IHsgUzogbG9nU2tQcmVmaXgoTWF0aC5tYXgoMCwgc2luY2UpKSB9LFxuICAgICAgICAgIH0sXG4gICAgICAgICAgUHJvamVjdGlvbkV4cHJlc3Npb246IFwic2ssIHJldmFsaWRhdGVkQXQsIHN0YWxlQXQsIGV4cGlyZWRBdFwiLFxuICAgICAgICAgIEV4Y2x1c2l2ZVN0YXJ0S2V5OiBzdGFydEtleSxcbiAgICAgICAgfSksXG4gICAgICApO1xuICAgICAgZm9yIChjb25zdCBpdGVtIG9mIHJlc3BvbnNlLkl0ZW1zID8/IFtdKSB7XG4gICAgICAgIGNvbnN0IHNrID0gaXRlbS5zaz8uUztcbiAgICAgICAgLy8gYDxkaWdpdHM+Izx0YWc+YDogdGhlIHRhZyBpcyBldmVyeXRoaW5nIGFmdGVyIHRoZSBmaXhlZC13aWR0aFxuICAgICAgICAvLyB0aW1lc3RhbXAsIGAjYHMgaW5jbHVkZWQuXG4gICAgICAgIGlmIChzayA9PT0gdW5kZWZpbmVkIHx8IHNrW0xPR19TS19ESUdJVFNdICE9PSBcIiNcIikge1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIHJvd3MucHVzaCh7XG4gICAgICAgICAgc2ssXG4gICAgICAgICAgYXQ6IE51bWJlcihzay5zbGljZSgwLCBMT0dfU0tfRElHSVRTKSksXG4gICAgICAgICAgdGFnOiBzay5zbGljZShMT0dfU0tfRElHSVRTICsgMSksXG4gICAgICAgICAgbWFya2VyOiBtYXJrZXJPZihpdGVtKSxcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICBzdGFydEtleSA9IHJlc3BvbnNlLkxhc3RFdmFsdWF0ZWRLZXk7XG4gICAgICBpZiAoIXN0YXJ0S2V5KSB7XG4gICAgICAgIHJldHVybiB7IHJvd3MsIHRydW5jYXRlZDogZmFsc2UgfTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHsgcm93cywgdHJ1bmNhdGVkOiB0cnVlIH07XG4gIH1cbn1cblxuZnVuY3Rpb24gbG9nU2tQcmVmaXgoYXQ6IG51bWJlcik6IHN0cmluZyB7XG4gIHJldHVybiBTdHJpbmcoTWF0aC5mbG9vcihhdCkpLnBhZFN0YXJ0KExPR19TS19ESUdJVFMsIFwiMFwiKTtcbn1cblxuLyoqXG4gKiBIb3cgZmFyIGJlaGluZCB0aGUgbGFzdCBxdWVyeSdzIHN0YXJ0IHRoZSBuZXh0IG9uZSBzdGFydHMgcmVhZGluZyB0aGUgbG9nOlxuICogdGhlIHJvb20gZm9yIHRoZSBgUXVlcnlgIGJlaW5nIGV2ZW50dWFsbHkgY29uc2lzdGVudCBhbmQgZm9yIHdyaXRlcnMnIGNsb2Nrc1xuICogcnVubmluZyBiZWhpbmQgdGhlIHJlYWRlcidzLiBSb3dzIGluc2lkZSBpdCBhcmUgcmVhZCBhZ2FpbiwgYW5kIGRyb3BwZWQgYXNcbiAqIGFscmVhZHkgYXBwbGllZC5cbiAqL1xuZXhwb3J0IGNvbnN0IFJFVkFMSURBVElPTl9MT0dfTE9PS0JBQ0tfTVMgPSA1MDAwO1xuXG4vKipcbiAqIFRoZSBsb25nZXN0IHtAbGluayBUcmFja2VkVGFnTWFya2Vyc30gdHJ1c3RzIHRoZSBsb2cgYWxvbmUgZm9yIGEgdGFnIGJlZm9yZVxuICogcmVhZGluZyBpdHMgbWFya2VyIHJvdyBhZ2Fpbi4gVGhlIGxvZyBpcyB0aGUgZmFzdCBwYXRoLCBidXQgdGhlIG1hcmtlciByb3dzXG4gKiBhcmUgdGhlIHNvdXJjZSBvZiB0cnV0aDogYSBsb2cgcm93IHdob3NlIHdyaXRlIGZhaWxlZCB3b3VsZCBvdGhlcndpc2UgYmVcbiAqIG1pc3NlZCBmb3IgYXMgbG9uZyBhcyB0aGUgdGFnIHN0YXlzIHRyYWNrZWQuXG4gKlxuICogUm9sbGluZywgbm90IGFsbCBhdCBvbmNlOiBlYWNoIHJlZnJlc2ggcmUtcmVhZHMgYXQgbW9zdFxuICoge0BsaW5rIEJBVENIX0dFVF9NQVhfS0VZU30gb2YgdGhlIHRhZ3MgZHVlLCBvbGRlc3QgZmlyc3QuIFJlLXJlYWRpbmcgdGhlbSBhbGxcbiAqIHRvZ2V0aGVyIGNvc3QgfjUwMCBSQ1UgYXQgMTAwMCB0YWdzIC0gYSBmbGVldCBzdGFydGVkIGJ5IG9uZSBsb2FkIHRlc3QgZGlkIGl0XG4gKiBpbiB0aGUgc2FtZSBtaW51dGUsIDEyLDgwMCBSQ1UsIGFuZCB0aHJvdHRsZWQgLSBhbmQgd291bGQgYmUgfjUsMDAwIFJDVSBhdFxuICoge0BsaW5rIERFRkFVTFRfTUFYX1RSQUNLRURfVEFHU30sIHBhc3Qgd2hhdCB0aGUgb25lIHBhcnRpdGlvbiBzZXJ2ZXMgaW4gYVxuICogc2Vjb25kLiBTcHJlYWQgb3V0LCAxMCwwMDAgdHJhY2tlZCB0YWdzIGNvc3QgfjggUkNVIGEgc2Vjb25kIHBlciBpbnN0YW5jZS5cbiAqXG4gKiBFYWNoIHRhZyBpcyBkdWUgYXQgYSByYW5kb20gNzUtMTAwJSBvZiB0aGlzIGFmdGVyIGl0IHdhcyBsYXN0IHJlYWQsIHNvIHRhZ3NcbiAqIGZpcnN0IHJlYWQgdG9nZXRoZXIgLSBhbiBpbnN0YW5jZSB3YXJtaW5nIHVwIC0gZG8gbm90IGFsbCBjb21lIGR1ZSB0b2dldGhlclxuICogYWdhaW4gZXZlcnkgaW50ZXJ2YWw6IHRoZSBsb2FkIHRlc3QgdGhhdCBtZWFzdXJlZCB0aGUgcm9sbGluZyByZS1yZWFkIHNhd1xuICogaXRzIHdhcm0tdXAncyByZWFkcyByZXR1cm4gYXMgYSAzMC0zNiBSQ1UvcyBidW1wIHRlbiBtaW51dGVzIGxhdGVyLlxuICovXG5leHBvcnQgY29uc3QgREVGQVVMVF9UQUdfUkVTWU5DX01TID0gMTAgKiA2MCAqIDEwMDA7XG5cbi8qKiBUaGUgZnJhY3Rpb24gb2Yge0BsaW5rIERFRkFVTFRfVEFHX1JFU1lOQ19NU30gYSB0YWcgbWF5IGNvbWUgZHVlIGVhcmx5IGJ5LiAqL1xuY29uc3QgVEFHX1JFU1lOQ19KSVRURVIgPSAwLjI1O1xuXG4vKipcbiAqIEhvdyBsb25nIHNpbmNlIHRoZSBsYXN0IHN1Y2Nlc3NmdWwgbG9nIHF1ZXJ5IHRoZSBsb2cgY2FuIHN0aWxsIGJlIHRydXN0ZWQgdG9cbiAqIGhvbGQgZXZlcnkgcm93IG5vdCByZWFkIHlldDogaXRzIFRUTCBsZXNzIGEgbWFyZ2luIGZvciB3cml0ZXJzJyBjbG9ja3MuIFBhc3RcbiAqIGl0IC0gYSBMYW1iZGEgZnJvemVuIGJldHdlZW4gaW52b2NhdGlvbnMsIGEgcnVuIG9mIGZhaWxlZCBxdWVyaWVzIC0gdGhlXG4gKiB0cmFja2VkIG1hcmtlcnMgYXJlIGZvcmdvdHRlbiwgdG8gYmUgcmVhZCBhZ2FpbiBhcyB0aGV5IGFyZSBuZWVkZWQsIGFuZCB0aGVcbiAqIGxvZyBpcyBmb2xsb3dlZCBmcm9tIHRoZXJlLlxuICovXG5leHBvcnQgY29uc3QgTUFYX1JFVkFMSURBVElPTl9MT0dfR0FQX01TID1cbiAgUkVWQUxJREFUSU9OX0xPR19UVExfTVMgLSA1ICogNjAgKiAxMDAwO1xuXG4vKipcbiAqIFRoZSBtb3N0IHRhZ3Mgb25lIGluc3RhbmNlIHRyYWNrcy4gUGFzdCBpdCB0aGUgbGVhc3QgcmVjZW50bHkgdXNlZCBpc1xuICogZm9yZ290dGVuLCB3aGljaCBjb3N0cyBhIHJlYWQgdGhlIG5leHQgdGltZSBpdCBpcyBuZWVkZWQ6IGFuIHVudHJhY2tlZCB0YWdcbiAqIGlzIHJlYWQgYmVmb3JlIGl0IGlzIHRydXN0ZWQuXG4gKlxuICogQSB0cmFja2VkIHRhZyBjb3N0cyBtZW1vcnkgKH4yIE1CIGF0IHRoaXMgYm91bmQpIGFuZCBpdHMgc2hhcmUgb2YgdGhlIHJvbGxpbmdcbiAqIHJlLXJlYWQsIG5vdCBhIHJlYWQgZXZlcnkgcmVmcmVzaCwgc28gdGhlIGJvdW5kIGlzIHNldCBieSBob3cgbWFueSB0YWdzIGFuXG4gKiBpbnN0YW5jZSByZWFsbHkgdXNlcy4gRXZlcnkgcGF0aCBpcyBhIHRhZyBvZiBpdHMgb3duIChgX05fVF8vPHBhdGg+YCksIGFuZCBhdFxuICogMTAwMCBhbiBhcHAgb2YgMTAwMCB0YWdnZWQgcGFnZXMga2VwdCBldmljdGluZyB3aGF0IGl0IHdhcyBhYm91dCB0byBuZWVkOlxuICogaGFsZiBpdHMgcmVxdWVzdHMgcmUtcmVhZCBhIG1hcmtlci5cbiAqL1xuZXhwb3J0IGNvbnN0IERFRkFVTFRfTUFYX1RSQUNLRURfVEFHUyA9IDEwXzAwMDtcblxuLyoqXG4gKiBUaGUgcmV2YWxpZGF0aW9uIHRhYmxlJ3MgbWFya2VyIHJvd3MgYW5kIGl0cyBsb2csIHdoaWNoIGFyZSBvbmx5IGV2ZXIgdXNlZFxuICogdG9nZXRoZXIsIG9yIG5laXRoZXIgZm9yIHRhZ3MgbG9jYWwgdG8gdGhpcyBwcm9jZXNzLlxuICovXG5leHBvcnQgdHlwZSBUYWdUYWJsZSA9XG4gIHwgeyBtYXJrZXJzOiBUYWdNYXJrZXJUYWJsZTsgbG9nOiBSZXZhbGlkYXRpb25Mb2cgfVxuICB8IHsgbWFya2Vycz86IHVuZGVmaW5lZDsgbG9nPzogdW5kZWZpbmVkIH07XG5cbi8qKiBIb3cge0BsaW5rIFRyYWNrZWRUYWdNYXJrZXJzfSBpcyBidWlsdC4gKi9cbmV4cG9ydCB0eXBlIFRyYWNrZWRUYWdNYXJrZXJzT3B0aW9ucyA9IFRhZ1RhYmxlICYge1xuICAvKipcbiAgICogSG93IG9mdGVuLCBhdCBtb3N0LCB7QGxpbmsgVHJhY2tlZFRhZ01hcmtlcnMucmVmcmVzaH0gcmVhZHMgYW55dGhpbmcuXG4gICAqIEBkZWZhdWx0IERFRkFVTFRfVEFHX1JFRlJFU0hfTVNcbiAgICovXG4gIHJlZnJlc2hJbnRlcnZhbE1zPzogbnVtYmVyO1xuICBtYXhUcmFja2VkVGFncz86IG51bWJlcjtcbiAgLyoqIEBzZWUgREVGQVVMVF9UQUdfUkVTWU5DX01TICovXG4gIHJlc3luY0ludGVydmFsTXM/OiBudW1iZXI7XG4gIC8qKiBUaGUgd2FsbCBjbG9jayB0aGUgbG9nJ3MgY3Vyc29yIGFuZCB0aGUgaW50ZXJ2YWxzIGFyZSBrZXB0IG9uLiAqL1xuICBjbG9jaz86ICgpID0+IG51bWJlcjtcbiAgLyoqIGBNYXRoLnJhbmRvbWAsIGZvciB3aGVuIGVhY2ggdGFnJ3MgcmUtcmVhZCBjb21lcyBkdWUuICovXG4gIHJhbmRvbT86ICgpID0+IG51bWJlcjtcbiAgZGVidWc/OiAobWVzc2FnZTogc3RyaW5nKSA9PiB2b2lkO1xufTtcblxuLyoqXG4gKiBPbmUgaW5zdGFuY2UncyBjb3B5IG9mIHRoZSBtYXJrZXIgcm93cyBvZiB0aGUgdGFncyBpdCBoYXMgbmVlZGVkLCBrZXB0XG4gKiBjdXJyZW50IHRocm91Z2ggdGhlIHtAbGluayBSZXZhbGlkYXRpb25Mb2d9OiB0aGUgcHJvY2VzcydzIG9uZSB0YWcgbWFuaWZlc3RcbiAqIChgc2hhcmVkVGFnTWFuaWZlc3RgKSwgZm9yIHRoZSBgJ3VzZSBjYWNoZSdgIGhhbmRsZXJzIGFuZCB0aGUgaW5jcmVtZW50YWxcbiAqIGNhY2hlIGFsaWtlLlxuICpcbiAqIE5leHQuanMncyBidWlsdC1pbiBgJ3VzZSBjYWNoZSdgIGhhbmRsZXIga2VlcHMgdGFncyBpbiBhIG1vZHVsZS1sZXZlbCBtYXBcbiAqIChgdGFncy1tYW5pZmVzdC5leHRlcm5hbC5qc2ApIHRoYXQgb25seSBpdHMgb3duIGB1cGRhdGVUYWdzYCB3cml0ZXMsIGFuZCBpdHNcbiAqIGByZWZyZXNoVGFnc2AgaXMgYSBuby1vcCwgc28gYSBgcmV2YWxpZGF0ZVRhZ2Agb24gb25lIGluc3RhbmNlIG5ldmVyIHJlYWNoZXNcbiAqIHRoZSBvdGhlcnM6IHRoZXkga2VlcCBzZXJ2aW5nIHRoZSByZXZhbGlkYXRlZCBlbnRyeSwgaW5jbHVkaW5nIGluc2lkZSBwYWdlc1xuICogdGhhdCBhcmUgYmVpbmcgcmUtcmVuZGVyZWQgKmJlY2F1c2UqIG9mIHRoYXQgcmV2YWxpZGF0aW9uLiBUaGVcbiAqIGBjYWNoZUhhbmRsZXJzYCBpbnRlcmZhY2UncyBhbnN3ZXIgaXMgYHJlZnJlc2hUYWdzYCwgXCJjYWxsZWQgYmVmb3JlIHN0YXJ0aW5nXG4gKiBhIG5ldyByZXF1ZXN0IOKApiB0byByZWZyZXNoIHRoZSBsb2NhbCB0YWdzIG1hbmlmZXN0XCIsIGFuZCB0aGlzIGlzIHRoYXRcbiAqIG1hbmlmZXN0LCBiYWNrZWQgYnkgdGhlIHNhbWUgbWFya2VyIHJvd3MgdGhlIGluY3JlbWVudGFsIGNhY2hlJ3NcbiAqIGByZXZhbGlkYXRlVGFnYCB3cml0ZXMgKGBwayA9IGJ1aWxkSWRgLCBgc2sgPSB0YWdgKS5cbiAqXG4gKiBUaGUgY29zdCBpcyBib3VuZGVkIHBlciBpbnN0YW5jZSwgbm90IHBlciByZXF1ZXN0OlxuICogLSB7QGxpbmsgZW5zdXJlfSByZWFkcyBhIHRhZydzIG1hcmtlciB0aGUgZmlyc3QgdGltZSBpdCBpcyBuZWVkZWQgYW5kIG5vdFxuICogICBhZ2FpbiB1bnRpbCBpdCBpcyBldmljdGVkLCBzbyBhIHRhZyBjb3N0cyBvbmUgcmVhZCBwZXIgaW5zdGFuY2UsIG5vdCBvbmVcbiAqICAgcGVyIHJlcXVlc3QuXG4gKiAtIHtAbGluayByZWZyZXNofSwgYXQgbW9zdCBvbmNlIHBlciBgcmVmcmVzaEludGVydmFsTXNgLCBzZW5kcyBvbmUgYFF1ZXJ5YFxuICogICBmb3IgdGhlIGxvZyByb3dzIHdyaXR0ZW4gc2luY2UgdGhlIGxhc3Qgb25lLCBhbmQgYXBwbGllcyB0aG9zZSBvZiB0YWdzXG4gKiAgIHRoaXMgaW5zdGFuY2UgdHJhY2tzLiBDb25jdXJyZW50IGNhbGxlcnMgc2hhcmUgaXQuIEFsb25nc2lkZSwgaXQgcmUtcmVhZHNcbiAqICAgdGhlIG1hcmtlcnMgb2YgdXAgdG8ge0BsaW5rIEJBVENIX0dFVF9NQVhfS0VZU30gdGFncyBub3QgcmVhZCBmb3JcbiAqICAge0BsaW5rIERFRkFVTFRfVEFHX1JFU1lOQ19NU30sIGluIG9uZSBgQmF0Y2hHZXRJdGVtYC5cbiAqIEEgcmV2YWxpZGF0aW9uIG9uIGFub3RoZXIgaW5zdGFuY2UgaXMgdGhlcmVmb3JlIHNlZW4gd2l0aGluXG4gKiBgcmVmcmVzaEludGVydmFsTXNgIChwbHVzIHRoZSBxdWVyeSBpdHNlbGYpLiBUaGUgaW5zdGFuY2UgdGhhdCByYW4gaXQgYXBwbGllc1xuICogaXQgaXRzZWxmLCBhdCBvbmNlLCB3aXRoIHtAbGluayBzZXR9LlxuICovXG5leHBvcnQgY2xhc3MgVHJhY2tlZFRhZ01hcmtlcnMge1xuICBwcml2YXRlIHJlYWRvbmx5IG1hcmtlcnM6IFRhZ01hcmtlclRhYmxlIHwgdW5kZWZpbmVkO1xuICBwcml2YXRlIHJlYWRvbmx5IGxvZzogUmV2YWxpZGF0aW9uTG9nIHwgdW5kZWZpbmVkO1xuICBwcml2YXRlIHJlYWRvbmx5IHJlZnJlc2hJbnRlcnZhbE1zOiBudW1iZXI7XG4gIHByaXZhdGUgcmVhZG9ubHkgbWF4VHJhY2tlZFRhZ3M6IG51bWJlcjtcbiAgcHJpdmF0ZSByZWFkb25seSByZXN5bmNJbnRlcnZhbE1zOiBudW1iZXI7XG4gIHByaXZhdGUgcmVhZG9ubHkgY2xvY2s6ICgpID0+IG51bWJlcjtcbiAgcHJpdmF0ZSByZWFkb25seSByYW5kb206ICgpID0+IG51bWJlcjtcbiAgcHJpdmF0ZSByZWFkb25seSBkZWJ1ZzogKG1lc3NhZ2U6IHN0cmluZykgPT4gdm9pZDtcbiAgLyoqIEJ5IHRhZywgbGVhc3QgcmVjZW50bHkgdXNlZCBmaXJzdC4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSB0YWdzID0gbmV3IE1hcDxzdHJpbmcsIFRhZ01hcmtlcj4oKTtcbiAgLyoqXG4gICAqIFdoZW4gZWFjaCB0cmFja2VkIHRhZydzIG1hcmtlciBpcyBkdWUgdG8gYmUgcmVhZCBhZ2FpbjogYSByYW5kb20gNzUtMTAwJVxuICAgKiBvZiBgcmVzeW5jSW50ZXJ2YWxNc2AgYWZ0ZXIgaXQgd2FzIGxhc3Qga25vd24gZnJvbSB0aGUgdGFibGUgLSByZWFkLFxuICAgKiB3cml0dGVuIGhlcmUsIG9yIG5ldy4gSW4gdGhlIG9yZGVyIHRoZXkgYmVjYW1lIGtub3duLCBsZWFzdCByZWNlbnQgZmlyc3QuXG4gICAqL1xuICBwcml2YXRlIHJlYWRvbmx5IGR1ZUF0ID0gbmV3IE1hcDxzdHJpbmcsIG51bWJlcj4oKTtcbiAgcHJpdmF0ZSByZWFkb25seSByZWFkaW5nID0gbmV3IE1hcDxzdHJpbmcsIFByb21pc2U8dm9pZD4+KCk7XG4gIC8qKlxuICAgKiBMb2cgcm93cyBmb3IgdGFncyB3aG9zZSBmaXJzdCByZWFkIGlzIGluIGZsaWdodCwgbWVyZ2VkIGJ5IHRhZzogdGhlIHJlYWRcbiAgICogbWF5IGhhdmUgYmVlbiBhbnN3ZXJlZCBiZWZvcmUgdGhlIHJldmFsaWRhdGlvbiwgc28gdGhleSBhcmUgYXBwbGllZCB0byBpdHNcbiAgICogcmVzdWx0IHJhdGhlciB0aGFuIGRyb3BwZWQuXG4gICAqL1xuICBwcml2YXRlIHJlYWRvbmx5IHBlbmRpbmdSb3dzID0gbmV3IE1hcDxzdHJpbmcsIFRhZ01hcmtlcj4oKTtcbiAgLyoqIFRyYWNrZWQgdGFncyB3aG9zZSByZWFkIGZhaWxlZCwgZm9yIHRoZSBuZXh0IHJlZnJlc2ggdG8gcmVhZCBhZ2Fpbi4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSB1bnJlYWQgPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgcHJpdmF0ZSBsYXN0UmVmcmVzaCA9IC1JbmZpbml0eTtcbiAgcHJpdmF0ZSByZWZyZXNoaW5nOiBQcm9taXNlPHZvaWQ+IHwgdW5kZWZpbmVkO1xuICAvKipcbiAgICogV2hlcmUgdGhlIG5leHQgbG9nIHF1ZXJ5IHN0YXJ0cyAoYGNsb2NrKClgKTogdGhlIGxhc3Qgc3VjY2Vzc2Z1bCBvbmUnc1xuICAgKiBzdGFydCwgbGVzcyB7QGxpbmsgUkVWQUxJREFUSU9OX0xPR19MT09LQkFDS19NU30uXG4gICAqL1xuICBwcml2YXRlIGN1cnNvcjogbnVtYmVyO1xuICAvKiogV2hlbiB0aGUgbGFzdCBzdWNjZXNzZnVsIGxvZyBxdWVyeSBzdGFydGVkLCBvciB0aGUgbG9nIHdhcyBmaXJzdCByZWFkLiAqL1xuICBwcml2YXRlIGxhc3RMb2dSZWFkOiBudW1iZXI7XG4gIC8qKiBMb2cgcm93cyBhdCBvciBhZnRlciB0aGUgY3Vyc29yIGFscmVhZHkgYXBwbGllZCwgYnkgc29ydCBrZXkuICovXG4gIHByaXZhdGUgcmVhZG9ubHkgYXBwbGllZCA9IG5ldyBNYXA8c3RyaW5nLCBudW1iZXI+KCk7XG4gIC8qKiB7QGxpbmsgdXBkYXRlfXMgb2YgdGhlIGN1cnJlbnQgdHVybiwgYnkgdGhlaXIgYXJndW1lbnRzLiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IHVwZGF0aW5nID0gbmV3IE1hcDxzdHJpbmcsIFByb21pc2U8Ym9vbGVhbj4+KCk7XG5cbiAgY29uc3RydWN0b3Iob3B0aW9uczogVHJhY2tlZFRhZ01hcmtlcnNPcHRpb25zKSB7XG4gICAgdGhpcy5tYXJrZXJzID0gb3B0aW9ucy5tYXJrZXJzO1xuICAgIHRoaXMubG9nID0gb3B0aW9ucy5sb2c7XG4gICAgdGhpcy5yZWZyZXNoSW50ZXJ2YWxNcyA9XG4gICAgICBvcHRpb25zLnJlZnJlc2hJbnRlcnZhbE1zID8/IERFRkFVTFRfVEFHX1JFRlJFU0hfTVM7XG4gICAgdGhpcy5tYXhUcmFja2VkVGFncyA9IG9wdGlvbnMubWF4VHJhY2tlZFRhZ3MgPz8gREVGQVVMVF9NQVhfVFJBQ0tFRF9UQUdTO1xuICAgIHRoaXMucmVzeW5jSW50ZXJ2YWxNcyA9IG9wdGlvbnMucmVzeW5jSW50ZXJ2YWxNcyA/PyBERUZBVUxUX1RBR19SRVNZTkNfTVM7XG4gICAgLy8gTG9va2VkIHVwIG9uIGVhY2ggY2FsbCByYXRoZXIgdGhhbiBib3VuZCBoZXJlLCBzbyBhIHRlc3QncyBgRGF0ZS5ub3dgXG4gICAgLy8gc3B5IGFwcGxpZXMuXG4gICAgdGhpcy5jbG9jayA9IG9wdGlvbnMuY2xvY2sgPz8gKCgpID0+IERhdGUubm93KCkpO1xuICAgIHRoaXMucmFuZG9tID0gb3B0aW9ucy5yYW5kb20gPz8gTWF0aC5yYW5kb207XG4gICAgdGhpcy5kZWJ1ZyA9IG9wdGlvbnMuZGVidWcgPz8gKCgpID0+IHt9KTtcbiAgICAvLyBOb3RoaW5nIGlzIHRyYWNrZWQgeWV0LCBzbyBub3RoaW5nIGJlZm9yZSB0aGlzIGNhbiBiZSBtaXNzZWQ6IGV2ZXJ5IHRhZ1xuICAgIC8vIGlzIHJlYWQgZnJvbSBpdHMgbWFya2VyIHRoZSBmaXJzdCB0aW1lIGl0IGlzIG5lZWRlZC5cbiAgICBjb25zdCBhdCA9IHRoaXMuY2xvY2soKTtcbiAgICB0aGlzLmN1cnNvciA9IGF0IC0gUkVWQUxJREFUSU9OX0xPR19MT09LQkFDS19NUztcbiAgICB0aGlzLmxhc3RMb2dSZWFkID0gYXQ7XG4gIH1cblxuICAvKiogYHRhZ2AncyBtYXJrZXIgYXMgdGhpcyBpbnN0YW5jZSBrbm93cyBpdCwgaWYgaXQgdHJhY2tzIGB0YWdgLiAqL1xuICBnZXQodGFnOiBzdHJpbmcpOiBUYWdNYXJrZXIgfCB1bmRlZmluZWQge1xuICAgIHJldHVybiB0aGlzLnRhZ3MuZ2V0KHRhZyk7XG4gIH1cblxuICAvKipcbiAgICogVHJhY2sgYHRhZ2Agd2l0aCBgbWFya2VyYDogd2hhdCB0aGlzIGluc3RhbmNlIGp1c3Qgd3JvdGUsIHdoaWNoIGl0IGtub3dzXG4gICAqIHdpdGhvdXQgcmVhZGluZyBpdCBiYWNrLlxuICAgKi9cbiAgc2V0KHRhZzogc3RyaW5nLCBtYXJrZXI6IFRhZ01hcmtlcik6IHZvaWQge1xuICAgIHRoaXMucmVtZW1iZXIodGFnLCBtYXJrZXIpO1xuICAgIHRoaXMua25vd24odGFnLCB0aGlzLmNsb2NrKCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIE5vdGUgYSB1c2Ugb2YgYHRhZ3NgOiB0aG9zZSBvZiBhbiBlbnRyeSB0aGlzIGluc3RhbmNlIGp1c3Qgc3RvcmVkLlxuICAgKlxuICAgKiBXaXRoIHRoZSB0YWJsZSwgYSB0YWcgbm90IHRyYWNrZWQgeWV0IGlzIGxlZnQgdW50cmFja2VkLCB0byBiZSByZWFkIGJ5XG4gICAqIHtAbGluayBlbnN1cmV9IHRoZSBmaXJzdCB0aW1lIGFuIGVudHJ5IG5lZWRzIGl0LiBOb3RoaW5nIHJldmFsaWRhdGVkIGJlZm9yZVxuICAgKiB0aGUgbmV3IGVudHJ5IHdhcyBjcmVhdGVkIGFwcGxpZXMgdG8gKml0KiwgYnV0IHRyYWNraW5nIGlzIHBlciB0YWcsIG5vdCBwZXJcbiAgICogZW50cnk6IHRyYWNraW5nIHRoZSB0YWcgd2l0aCBhbiBlbXB0eSBtYXJrZXIgd291bGQgYWxzbyB2b3VjaCBmb3IgZXZlcnlcbiAgICogb2xkZXIgZW50cnkgdGhhdCBjYXJyaWVzIGl0IC0gb25lIGFub3RoZXIgaW5zdGFuY2Ugc3RvcmVkIGluIFMzLCBvciBvbmVcbiAgICogdGhpcyBpbnN0YW5jZSBrZXB0IGluIG1lbW9yeSBhY3Jvc3MgYSB7QGxpbmsgZm9yZ2V0fSAtIGFuZCBhIHJldmFsaWRhdGlvblxuICAgKiBmcm9tIGJlZm9yZSB0aGUgbG9nJ3MgY3Vyc29yIHdvdWxkIGdvIHVuc2VlbiB1bnRpbCB0aGUgcm9sbGluZyByZS1yZWFkLlxuICAgKlxuICAgKiBXaXRob3V0IHRoZSB0YWJsZSBldmVyeSByZXZhbGlkYXRpb24gaXMgdGhpcyBwcm9jZXNzJ3Mgb3duLCBhcHBsaWVkIHdpdGhcbiAgICoge0BsaW5rIHNldH0sIHNvIGEgdGFnIGlzIHRyYWNrZWQgYXQgb25jZS5cbiAgICovXG4gIHRyYWNrKHRhZ3M6IHJlYWRvbmx5IHN0cmluZ1tdKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLm1hcmtlcnMpIHtcbiAgICAgIHRoaXMudHJhY2tVbnJlYWQodGFncyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGZvciAoY29uc3QgdGFnIG9mIHRhZ3MpIHtcbiAgICAgIHRoaXMudG91Y2godGFnKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVHJhY2sgYHRhZ3NgIGFzIHRoZXkgYXJlIGtub3duIG5vdywgd2l0aG91dCByZWFkaW5nIHRoZW0sIHdpdGggYW4gZW1wdHlcbiAgICogbWFya2VyIGZvciBhbnkgbm90IHRyYWNrZWQgeWV0LlxuICAgKi9cbiAgcHJpdmF0ZSB0cmFja1VucmVhZCh0YWdzOiByZWFkb25seSBzdHJpbmdbXSk6IHZvaWQge1xuICAgIGNvbnN0IGF0ID0gdGhpcy5jbG9jaygpO1xuICAgIGZvciAoY29uc3QgdGFnIG9mIHRhZ3MpIHtcbiAgICAgIGlmICghdGhpcy50YWdzLmhhcyh0YWcpKSB7XG4gICAgICAgIHRoaXMua25vd24odGFnLCBhdCk7XG4gICAgICB9XG4gICAgICB0aGlzLnJlbWVtYmVyKHRhZywgdGhpcy50YWdzLmdldCh0YWcpID8/IHt9KTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVhZCB0aGUgbWFya2VycyBvZiB3aGljaGV2ZXIgb2YgYHRhZ3NgIHRoaXMgaW5zdGFuY2UgZG9lcyBub3QgdHJhY2sgeWV0LlxuICAgKiBOZWVkZWQgYmVmb3JlIGp1ZGdpbmcgYW4gZW50cnkgdGhpcyBpbnN0YW5jZSBkaWQgbm90IGNyZWF0ZSwgc2luY2UgaXRzXG4gICAqIHRhZ3MgbWF5IGhhdmUgYmVlbiByZXZhbGlkYXRlZCBhdCBhbnkgdGltZSBiZWZvcmUuXG4gICAqL1xuICBhc3luYyBlbnN1cmUodGFnczogcmVhZG9ubHkgc3RyaW5nW10pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAoIXRoaXMubWFya2Vycykge1xuICAgICAgdGhpcy50cmFja1VucmVhZCh0YWdzKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3Qgd2FpdHM6IFByb21pc2U8dm9pZD5bXSA9IFtdO1xuICAgIGNvbnN0IHVucmVhZDogc3RyaW5nW10gPSBbXTtcbiAgICBmb3IgKGNvbnN0IHRhZyBvZiBuZXcgU2V0KHRhZ3MpKSB7XG4gICAgICBjb25zdCBpbkZsaWdodCA9IHRoaXMucmVhZGluZy5nZXQodGFnKTtcbiAgICAgIGlmIChpbkZsaWdodCkge1xuICAgICAgICB3YWl0cy5wdXNoKGluRmxpZ2h0KTtcbiAgICAgIH0gZWxzZSBpZiAodGhpcy50YWdzLmhhcyh0YWcpKSB7XG4gICAgICAgIHRoaXMudG91Y2godGFnKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHVucmVhZC5wdXNoKHRhZyk7XG4gICAgICB9XG4gICAgfVxuICAgIGlmICh1bnJlYWQubGVuZ3RoID4gMCkge1xuICAgICAgLy8gYHJlYWRJbnRvYCBuZXZlciByZWplY3RzOiBhIGZhaWxlZCByZWFkIGxlYXZlcyB0aGUgdGFncyB0cmFja2VkLlxuICAgICAgY29uc3QgcmVhZCA9IHRoaXMucmVhZEludG8odW5yZWFkKS50aGVuKCgpID0+IHtcbiAgICAgICAgZm9yIChjb25zdCB0YWcgb2YgdW5yZWFkKSB7XG4gICAgICAgICAgdGhpcy5yZWFkaW5nLmRlbGV0ZSh0YWcpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIGZvciAoY29uc3QgdGFnIG9mIHVucmVhZCkge1xuICAgICAgICB0aGlzLnJlYWRpbmcuc2V0KHRhZywgcmVhZCk7XG4gICAgICB9XG4gICAgICB3YWl0cy5wdXNoKHJlYWQpO1xuICAgIH1cbiAgICBhd2FpdCBQcm9taXNlLmFsbCh3YWl0cyk7XG4gIH1cblxuICAvKipcbiAgICogQ2F0Y2ggdXAgb24gcmV2YWxpZGF0aW9ucyBvdGhlciBpbnN0YW5jZXMgcmFuLCBhdCBtb3N0IG9uY2UgcGVyXG4gICAqIGByZWZyZXNoSW50ZXJ2YWxNc2AuIFNlZSB7QGxpbmsgVHJhY2tlZFRhZ01hcmtlcnN9IGZvciB0aGUgY29zdC5cbiAgICovXG4gIGFzeW5jIHJlZnJlc2goKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgbG9nID0gdGhpcy5sb2c7XG4gICAgaWYgKCFsb2cpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKHRoaXMucmVmcmVzaGluZykge1xuICAgICAgcmV0dXJuIHRoaXMucmVmcmVzaGluZztcbiAgICB9XG4gICAgY29uc3QgYXQgPSB0aGlzLmNsb2NrKCk7XG4gICAgaWYgKGF0IC0gdGhpcy5sYXN0UmVmcmVzaCA8IHRoaXMucmVmcmVzaEludGVydmFsTXMpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5sYXN0UmVmcmVzaCA9IGF0O1xuICAgIC8vIFdoYXRldmVyIGdldHMgdHJhY2tlZCBiZWZvcmUgdGhlIG5leHQgcmVmcmVzaCBpcyByZWFkIGZyb20gaXRzIG1hcmtlcixcbiAgICAvLyBzbyB0aGVyZSBpcyBub3RoaW5nIHRvIGNhdGNoIHVwIG9uIHlldC5cbiAgICBpZiAodGhpcy50YWdzLnNpemUgPT09IDApIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5yZWZyZXNoaW5nID0gdGhpcy5zeW5jKGF0LCBsb2cpLmZpbmFsbHkoKCkgPT4ge1xuICAgICAgdGhpcy5yZWZyZXNoaW5nID0gdW5kZWZpbmVkO1xuICAgIH0pO1xuICAgIHJldHVybiB0aGlzLnJlZnJlc2hpbmc7XG4gIH1cblxuICAvKipcbiAgICogUmVjb3JkIGEgcmV2YWxpZGF0aW9uIG9mIGB0YWdzYDogaGVyZSBhdCBvbmNlLCBhbmQgaW4gdGhlIHJldmFsaWRhdGlvblxuICAgKiB0YWJsZSBmb3IgZXZlcnkgb3RoZXIgaW5zdGFuY2UgLSB0aGUgbWFya2VyIHJvdyBwbHVzIGEgbG9nIHJvdyBwZXIgdGFnLlxuICAgKlxuICAgKiBOZXh0LmpzIGNhbGxzIGByZXZhbGlkYXRlVGFnYCBvbiB0aGUgaW5jcmVtZW50YWwgY2FjaGUgYW5kIGB1cGRhdGVUYWdzYCBvblxuICAgKiBldmVyeSBkaXN0aW5jdCBgY2FjaGVIYW5kbGVyc2AgZW50cnksIGFsbCBpbiBvbmUgc3luY2hyb25vdXMgbG9vcFxuICAgKiAoYHJldmFsaWRhdGlvbi11dGlsc2ApLCBhbmQgYWxsIG9mIHRoZW0gc2hhcmUgdGhpcyBtYW5pZmVzdC4gU28gYW5cbiAgICogaWRlbnRpY2FsIGNhbGwgaW4gdGhlIHNhbWUgdHVybiBqb2lucyB0aGUgZmlyc3QgaW5zdGVhZCBvZiB3cml0aW5nIHRoZVxuICAgKiByb3dzIGFnYWluLiBPbmUgaW4gYSBsYXRlciB0dXJuIGlzIGEgcmV2YWxpZGF0aW9uIG9mIGl0cyBvd24sIGV2ZW4gd2hpbGVcbiAgICogdGhlIGZpcnN0IG9uZSdzIHdyaXRlcyBhcmUgaW4gZmxpZ2h0LCBhbmQgZ2V0cyBpdHMgb3duLCBsYXRlciwgbWFya2VyLlxuICAgKiBOZXZlciByZWplY3RzOiByZXNvbHZlcyBgZmFsc2VgIHdoZW4gYSByb3cgZmFpbGVkIHRvIHdyaXRlICh0aGUgZXJyb3IgaXNcbiAgICogbG9nZ2VkKSwgc28gb3RoZXIgaW5zdGFuY2VzIG1heSBub3Qgc2VlIGl0LlxuICAgKi9cbiAgYXN5bmMgdXBkYXRlKFxuICAgIHRhZ3M6IHJlYWRvbmx5IHN0cmluZ1tdLFxuICAgIGR1cmF0aW9uczogUmV2YWxpZGF0ZUR1cmF0aW9ucyB8IHVuZGVmaW5lZCxcbiAgKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgY29uc3Qga2V5ID0gSlNPTi5zdHJpbmdpZnkoW3RhZ3MsIGR1cmF0aW9ucyA/PyBudWxsXSk7XG4gICAgY29uc3QgaW5GbGlnaHQgPSB0aGlzLnVwZGF0aW5nLmdldChrZXkpO1xuICAgIGlmIChpbkZsaWdodCkge1xuICAgICAgcmV0dXJuIGluRmxpZ2h0O1xuICAgIH1cbiAgICBjb25zdCB1cGRhdGUgPSB0aGlzLndyaXRlKHRhZ3MsIGR1cmF0aW9ucyk7XG4gICAgdGhpcy51cGRhdGluZy5zZXQoa2V5LCB1cGRhdGUpO1xuICAgIC8vIEZvcmdvdHRlbiBhcyBzb29uIGFzIHRoZSBjdXJyZW50IHR1cm4ncyBjYWxscyBoYXZlIGJlZW4gbWFkZS5cbiAgICB2b2lkIFByb21pc2UucmVzb2x2ZSgpLnRoZW4oKCkgPT4gdGhpcy51cGRhdGluZy5kZWxldGUoa2V5KSk7XG4gICAgcmV0dXJuIHVwZGF0ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBXaGF0IHRoZSB0cmFja2VkIG1hcmtlcnMgc2F5IGFib3V0IGFuIGVudHJ5IGNhcnJ5aW5nIGB0YWdzYCwgY3JlYXRlZCBhdFxuICAgKiBgY3JlYXRlZEF0YDogdGhlIG1vc3Qgc2V2ZXJlIGFuc3dlciBvZiBhbnkgb2YgdGhlbS5cbiAgICovXG4gIHN0YXRlKHRhZ3M6IHJlYWRvbmx5IHN0cmluZ1tdLCBjcmVhdGVkQXQ6IG51bWJlcik6IFJldmFsaWRhdGlvblN0YXRlIHtcbiAgICAvLyBgbWFya2VyQ2xvY2soKWAsIG5vdCBgRGF0ZS5ub3coKWAsIGhlcmUgYW5kIHdoZXJldmVyIGEgbWFya2VyIGlzIHN0YW1wZWQ6XG4gICAgLy8gYGNyZWF0ZWRBdGAgaXMgb24gTmV4dC5qcydzIGNsb2NrLCBhbmQgYSBtYXJrZXIgb24gdGhlIHdhbGwgY2xvY2sgd291bGQgYmVcbiAgICAvLyBjb21wYXJlZCBhY3Jvc3MgdGhlIHR3bywgc28gYW4gZW50cnkgcmVnZW5lcmF0ZWQganVzdCBhZnRlciBhXG4gICAgLy8gcmV2YWxpZGF0aW9uIGNvdWxkIHN0aWxsIHJlYWQgYXMgb2xkZXIgdGhhbiBpdCB3aGVyZXZlciB0aGV5IGRyaWZ0LlxuICAgIGNvbnN0IGF0ID0gbWFya2VyQ2xvY2soKTtcbiAgICBsZXQgc3RhdGU6IFJldmFsaWRhdGlvblN0YXRlID0gXCJmcmVzaFwiO1xuICAgIGZvciAoY29uc3QgdGFnIG9mIHRhZ3MpIHtcbiAgICAgIGNvbnN0IG1hcmtlciA9IHRoaXMuZ2V0KHRhZyk7XG4gICAgICBpZiAoIW1hcmtlcikge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHRhZ1N0YXRlID0gbWFya2VyU3RhdGUobWFya2VyLCBjcmVhdGVkQXQsIGF0KTtcbiAgICAgIGlmICh0YWdTdGF0ZSA9PT0gXCJleHBpcmVkXCIpIHtcbiAgICAgICAgcmV0dXJuIFwiZXhwaXJlZFwiO1xuICAgICAgfVxuICAgICAgaWYgKHRhZ1N0YXRlID09PSBcInN0YWxlXCIpIHtcbiAgICAgICAgc3RhdGUgPSBcInN0YWxlXCI7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBzdGF0ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBgZ2V0RXhwaXJhdGlvbmAncyBhbnN3ZXI6IHRoZSBsYXRlc3QgdGltZSBhbnkgb2YgYHRhZ3NgIGV4cGlyZWQsIGAwYCBpZlxuICAgKiBub25lIGhhcy4gT25seSBleHBpcmF0aW9ucyBhbHJlYWR5IHBhc3QgY291bnQgLSBhIHByb2ZpbGUncyBmdXR1cmVcbiAgICogYGV4cGlyZWAgaXMgbm90IGFuIGV4cGlyYXRpb24geWV0IC0gYW5kIGEgc3RhbGUgbWFyayBkb2VzIG5vdCwgbWF0Y2hpbmdcbiAgICogTmV4dC5qcydzIGRlZmF1bHQgaGFuZGxlciwgd2hpY2ggcmVhZHMgb25seSBgZXhwaXJlZGAgZm9yIGltcGxpY2l0IHRhZ3MuXG4gICAqL1xuICBleHBpcmF0aW9uKHRhZ3M6IHJlYWRvbmx5IHN0cmluZ1tdKTogbnVtYmVyIHtcbiAgICBjb25zdCBhdCA9IG1hcmtlckNsb2NrKCk7XG4gICAgbGV0IGxhdGVzdCA9IDA7XG4gICAgZm9yIChjb25zdCB0YWcgb2YgdGFncykge1xuICAgICAgY29uc3QgbWFya2VyID0gdGhpcy5nZXQodGFnKTtcbiAgICAgIGlmICghbWFya2VyKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgaWYgKG1hcmtlci5yZXZhbGlkYXRlZEF0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgbGF0ZXN0ID0gTWF0aC5tYXgobGF0ZXN0LCBtYXJrZXIucmV2YWxpZGF0ZWRBdCk7XG4gICAgICB9XG4gICAgICBpZiAobWFya2VyLmV4cGlyZWRBdCAhPT0gdW5kZWZpbmVkICYmIG1hcmtlci5leHBpcmVkQXQgPD0gYXQpIHtcbiAgICAgICAgbGF0ZXN0ID0gTWF0aC5tYXgobGF0ZXN0LCBtYXJrZXIuZXhwaXJlZEF0KTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGxhdGVzdDtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgd3JpdGUoXG4gICAgdGFnczogcmVhZG9ubHkgc3RyaW5nW10sXG4gICAgZHVyYXRpb25zOiBSZXZhbGlkYXRlRHVyYXRpb25zIHwgdW5kZWZpbmVkLFxuICApOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBjb25zdCBhdCA9IG1hcmtlckNsb2NrKCk7XG4gICAgY29uc3QgbWFya2VyID0gbWFya2VyRm9yKGF0LCBkdXJhdGlvbnMpO1xuICAgIGZvciAoY29uc3QgdGFnIG9mIHRhZ3MpIHtcbiAgICAgIHRoaXMuc2V0KHRhZywgYXBwbHlNYXJrZXIodGhpcy5nZXQodGFnKSwgbWFya2VyKSk7XG4gICAgfVxuICAgIGNvbnN0IHsgbWFya2VycywgbG9nIH0gPSB0aGlzO1xuICAgIGlmICghbWFya2VycyB8fCAhbG9nKSB7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgY29uc3Qgd3JpdGVzOiBbc3RyaW5nLCBQcm9taXNlPHVua25vd24+XVtdID0gW107XG4gICAgZm9yIChjb25zdCB0YWcgb2YgbmV3IFNldCh0YWdzKSkge1xuICAgICAgd3JpdGVzLnB1c2goW1xuICAgICAgICBcIkVycm9yIHdyaXRpbmcgdGFnIHJldmFsaWRhdGlvbiBtYXJrZXI6XCIsXG4gICAgICAgIC8vIFRoZSByb3cgYXMgaXQgaXMgbm93LCB3aGljaCBjYXJyaWVzIGFueSBlYXJsaWVyIGByZXZhbGlkYXRlZEF0YFxuICAgICAgICAvLyBhbm90aGVyIGluc3RhbmNlIHdyb3RlOiB0cmFja2luZyBvbmx5IHdoYXQgdGhpcyBjYWxsIHNldCB3b3VsZFxuICAgICAgICAvLyBoaWRlIHRoYXQgb25lIHVudGlsIHRoZSByb2xsaW5nIHJlLXJlYWQsIGFuZCBhbiBlbnRyeSBvbGRlciB0aGFuIGl0XG4gICAgICAgIC8vIHdvdWxkIHJlYWQgYXMgbWVyZWx5IHN0YWxlLlxuICAgICAgICBtYXJrZXJzLndyaXRlKHRhZywgYXQsIGR1cmF0aW9ucykudGhlbigocm93KSA9PiB7XG4gICAgICAgICAgaWYgKHJvdykge1xuICAgICAgICAgICAgdGhpcy5zZXQodGFnLCBtZXJnZU1hcmtlcnModGhpcy5nZXQodGFnKSwgcm93KSk7XG4gICAgICAgICAgfVxuICAgICAgICB9KSxcbiAgICAgIF0pO1xuICAgICAgd3JpdGVzLnB1c2goW1xuICAgICAgICBcIkVycm9yIHdyaXRpbmcgdGFnIHJldmFsaWRhdGlvbiBsb2cgcm93OlwiLFxuICAgICAgICB0aGlzLnB1dExvZ1Jvdyhsb2csIHRhZywgbWFya2VyKSxcbiAgICAgIF0pO1xuICAgIH1cbiAgICBjb25zdCByZXN1bHRzID0gYXdhaXQgUHJvbWlzZS5hbGxTZXR0bGVkKHdyaXRlcy5tYXAoKFssIHdyaXRlXSkgPT4gd3JpdGUpKTtcbiAgICByZXN1bHRzLmZvckVhY2goKHJlc3VsdCwgaSkgPT4ge1xuICAgICAgaWYgKHJlc3VsdC5zdGF0dXMgPT09IFwicmVqZWN0ZWRcIikge1xuICAgICAgICBjb25zb2xlLmVycm9yKHdyaXRlc1tpXVswXSwgcmVzdWx0LnJlYXNvbik7XG4gICAgICB9XG4gICAgfSk7XG4gICAgcmV0dXJuIHJlc3VsdHMuZXZlcnkoKHJlc3VsdCkgPT4gcmVzdWx0LnN0YXR1cyA9PT0gXCJmdWxmaWxsZWRcIik7XG4gIH1cblxuICAvKipcbiAgICogUHV0IGB0YWdgJ3MgbG9nIHJvdywgc3RhbXBlZCBvbiB0aGUgd2FsbCBjbG9jayAodGhlIG1hcmtlciB2YWx1ZXMgYXJlIG9uXG4gICAqIE5leHQuanMnczogc2VlIHtAbGluayBSZXZhbGlkYXRpb25Mb2d9KS5cbiAgICpcbiAgICogUmVhZGVycyBvbmx5IGxvb2sge0BsaW5rIFJFVkFMSURBVElPTl9MT0dfTE9PS0JBQ0tfTVN9IGJlaGluZCB0aGVpciBsYXN0XG4gICAqIHF1ZXJ5LCBzbyBhIHJvdyB0aGF0IHNob3dzIHVwIG11Y2ggbGF0ZXIgdGhhbiBpdHMgdGltZXN0YW1wIC0gYSBzbG93XG4gICAqIGNvbm5lY3Rpb24sIHJldHJpZXMgdW5kZXIgdGhyb3R0bGluZyAtIGlzIGJlaGluZCBldmVyeSBjdXJzb3IgYW5kIG5ldmVyXG4gICAqIHJlYWQuIEEgc2xvdyBwdXQgaXMgdGhlcmVmb3JlIG1hZGUgYWdhaW4gdW5kZXIgYSBmcmVzaCB0aW1lc3RhbXAsIHdoaWNoXG4gICAqIHJlYWRlcnMgd2lsbCBzZWU7IG9uZSBzbG93IHR3aWNlIGlzIHJlcG9ydGVkIGFzIGEgZmFpbHVyZS5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgcHV0TG9nUm93KFxuICAgIGxvZzogUmV2YWxpZGF0aW9uTG9nLFxuICAgIHRhZzogc3RyaW5nLFxuICAgIG1hcmtlcjogVGFnTWFya2VyLFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBmb3IgKGxldCBhdHRlbXB0ID0gMDsgYXR0ZW1wdCA8IDI7IGF0dGVtcHQrKykge1xuICAgICAgY29uc3QgYXQgPSB0aGlzLmNsb2NrKCk7XG4gICAgICBhd2FpdCBsb2cucHV0KHRhZywgYXQsIG1hcmtlcik7XG4gICAgICBpZiAodGhpcy5jbG9jaygpIC0gYXQgPD0gTUFYX0xPR19QVVRfTVMpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgIH1cbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgVGhlIHJldmFsaWRhdGlvbiBsb2cgcm93IGZvciAke3RhZ30gdG9vayBsb25nZXIgdGhhbiByZWFkZXJzIGxvb2sgYmFja2AsXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBPbmUgcmVmcmVzaCBzdGFydGVkIGF0IGBhdGA6IHRoZSBsb2cgcm93cyBzaW5jZSB0aGUgY3Vyc29yLCBhbmQgdGhlIG5leHRcbiAgICogc2xpY2Ugb2YgdGhlIHJvbGxpbmcgcmUtcmVhZC5cbiAgICpcbiAgICogQSBmYWlsZWQgcXVlcnkgY2hhbmdlcyBub3RoaW5nIGJ1dCB0aGUgbG9nOiB0aGUgdGFncyBzdGF5IHRyYWNrZWQgYXMgdGhleVxuICAgKiB3ZXJlLCBhbmQgdGhlIG5leHQgcmVmcmVzaCBhc2tzIGFnYWluIGZyb20gdGhlIHNhbWUgY3Vyc29yLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBzeW5jKGF0OiBudW1iZXIsIGxvZzogUmV2YWxpZGF0aW9uTG9nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKGF0IC0gdGhpcy5sYXN0TG9nUmVhZCA+IE1BWF9SRVZBTElEQVRJT05fTE9HX0dBUF9NUykge1xuICAgICAgdGhpcy5mb3JnZXQoXG4gICAgICAgIGF0LFxuICAgICAgICBcInRoZSByZXZhbGlkYXRpb24gbG9nIG1heSBub3QgY292ZXIgdGhlIGdhcCBzaW5jZSBpdCB3YXMgbGFzdCByZWFkXCIsXG4gICAgICApO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBbcmVzdWx0XSA9IGF3YWl0IFByb21pc2UuYWxsKFtcbiAgICAgIGxvZy5xdWVyeSh0aGlzLmN1cnNvcikuY2F0Y2goKGVycm9yKSA9PiB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoXCJFcnJvciByZWFkaW5nIHRhZyByZXZhbGlkYXRpb24gbG9nOlwiLCBlcnJvcik7XG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgICB9KSxcbiAgICAgIHRoaXMucmVhZEludG8odGhpcy5kdWUoYXQpKSxcbiAgICBdKTtcbiAgICBpZiAoIXJlc3VsdCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAocmVzdWx0LnRydW5jYXRlZCkge1xuICAgICAgdGhpcy5mb3JnZXQoXG4gICAgICAgIGF0LFxuICAgICAgICBcInRoZSByZXZhbGlkYXRpb24gbG9nIGhhcyBtb3JlIHJvd3MgdGhhbiBvbmUgcXVlcnkgcmVhZHNcIixcbiAgICAgICk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGxldCBhcHBsaWVkID0gMDtcbiAgICBmb3IgKGNvbnN0IHJvdyBvZiByZXN1bHQucm93cykge1xuICAgICAgaWYgKHRoaXMuYXBwbGllZC5oYXMocm93LnNrKSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IG1hcmtlciA9IHRoaXMudGFncy5nZXQocm93LnRhZyk7XG4gICAgICBpZiAobWFya2VyKSB7XG4gICAgICAgIC8vIGBzZXRgIG9uIGEga2V5IGFscmVhZHkgdGhlcmUga2VlcHMgaXRzIHBsYWNlIGluIHRoZSBMUlUgb3JkZXI6IGJlaW5nXG4gICAgICAgIC8vIHJldmFsaWRhdGVkIGVsc2V3aGVyZSBpcyBub3QgYSB1c2UuXG4gICAgICAgIHRoaXMudGFncy5zZXQocm93LnRhZywgbWVyZ2VNYXJrZXJzKG1hcmtlciwgcm93Lm1hcmtlcikpO1xuICAgICAgfSBlbHNlIGlmICh0aGlzLnJlYWRpbmcuaGFzKHJvdy50YWcpKSB7XG4gICAgICAgIHRoaXMucGVuZGluZ1Jvd3Muc2V0KFxuICAgICAgICAgIHJvdy50YWcsXG4gICAgICAgICAgbWVyZ2VNYXJrZXJzKHRoaXMucGVuZGluZ1Jvd3MuZ2V0KHJvdy50YWcpLCByb3cubWFya2VyKSxcbiAgICAgICAgKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIEEgdGFnIHRoaXMgaW5zdGFuY2UgZG9lcyBub3QgdHJhY2sgaXMgcmVhZCBmcm9tIGl0cyBtYXJrZXIgd2hlbiBpdFxuICAgICAgICAvLyBpcyBuZXh0IG5lZWRlZCAtIGJ1dCBub3QgeWV0IG1hcmtlZCBhcHBsaWVkLCBiZWNhdXNlIHRoYXQgcmVhZCBtYXlcbiAgICAgICAgLy8gbWlzcyB0aGlzIHZlcnkgcmV2YWxpZGF0aW9uOiB0aGUgd3JpdGVyIHB1dHMgdGhlIGxvZyByb3cgYW5kIHRoZVxuICAgICAgICAvLyBtYXJrZXIgYXQgdGhlIHNhbWUgdGltZSwgYW5kIHRoZSByZWFkIGlzIGFuIGV2ZW50dWFsbHkgY29uc2lzdGVudFxuICAgICAgICAvLyBgQmF0Y2hHZXRJdGVtYCB0aGF0IGNhbiBsYW5kIG1pbGxpc2Vjb25kcyBhZnRlciB0aGUgcm93IHNob3dlZCBoZXJlLlxuICAgICAgICAvLyBNYXJrZWQgYXBwbGllZCwgdGhlIHJvdyB3YXMgc2tpcHBlZCBieSBldmVyeSBxdWVyeSB0aGUgbG9va2JhY2tcbiAgICAgICAgLy8gcmV0dXJuZWQgaXQgdG8sIGFuZCB0aGUgcHJlLXJldmFsaWRhdGlvbiBtYXJrZXIgc3Rvb2QgdW50aWwgdGhlXG4gICAgICAgIC8vIHJvbGxpbmcgcmUtcmVhZC4gTGVmdCB1bm1hcmtlZCwgdGhlIG5leHQgcmVmcmVzaCBhcHBsaWVzIGl0IHRvXG4gICAgICAgIC8vIHdoYXRldmVyIHRoYXQgcmVhZCBmb3VuZDsgaXQgY29zdHMgbm8gcmVhZCwgb25seSB0aGUgcm93IGJlaW5nXG4gICAgICAgIC8vIGxvb2tlZCBhdCBhZ2FpbiB3aGlsZSBpdCBpcyBpbnNpZGUgdGhlIGxvb2tiYWNrLlxuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIHRoaXMuYXBwbGllZC5zZXQocm93LnNrLCByb3cuYXQpO1xuICAgICAgYXBwbGllZCsrO1xuICAgIH1cbiAgICB0aGlzLmxhc3RMb2dSZWFkID0gYXQ7XG4gICAgdGhpcy5hZHZhbmNlKGF0IC0gUkVWQUxJREFUSU9OX0xPR19MT09LQkFDS19NUyk7XG4gICAgdGhpcy5kZWJ1ZyhcbiAgICAgIGByZWFkICR7cmVzdWx0LnJvd3MubGVuZ3RofSByZXZhbGlkYXRpb24gbG9nIHJvd3MgKCR7YXBwbGllZH0gYXBwbGllZClgLFxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogVGhlIHRhZ3MgdG8gcmVhZCBvbiB0aGlzIHJlZnJlc2gsIGF0IG1vc3Qgb25lIGBCYXRjaEdldEl0ZW1gJ3Mgd29ydGg6XG4gICAqIHRob3NlIHdob3NlIHJlYWQgZmFpbGVkLCB0aGVuIHRob3NlIHdob3NlIHJlLXJlYWQgaGFzIGNvbWUgZHVlLCBpbiB0aGVcbiAgICogb3JkZXIgdGhleSB3ZXJlIGxhc3QgcmVhZC5cbiAgICovXG4gIHByaXZhdGUgZHVlKGF0OiBudW1iZXIpOiBzdHJpbmdbXSB7XG4gICAgY29uc3QgZHVlOiBzdHJpbmdbXSA9IFtdO1xuICAgIGZvciAoY29uc3QgdGFnIG9mIHRoaXMudW5yZWFkKSB7XG4gICAgICBpZiAoZHVlLmxlbmd0aCA+PSBCQVRDSF9HRVRfTUFYX0tFWVMpIHJldHVybiBkdWU7XG4gICAgICBkdWUucHVzaCh0YWcpO1xuICAgIH1cbiAgICAvLyBLbm93biBpbiB0aW1lIG9yZGVyLCBhbmQgZWFjaCBkdWUgYXQgbW9zdCBgaml0dGVyYCBlYXJsaWVyIHRoYW4gdGhlXG4gICAgLy8gdGFncyBhZnRlciBpdDogcGFzdCBhIHRhZyBkdWUgbW9yZSB0aGFuIHRoYXQgZnJvbSBub3csIG5vbmUgaXMgZHVlLlxuICAgIGNvbnN0IGppdHRlciA9IHRoaXMucmVzeW5jSW50ZXJ2YWxNcyAqIFRBR19SRVNZTkNfSklUVEVSO1xuICAgIGZvciAoY29uc3QgW3RhZywgZHVlQXRdIG9mIHRoaXMuZHVlQXQpIHtcbiAgICAgIGlmIChkdWUubGVuZ3RoID49IEJBVENIX0dFVF9NQVhfS0VZUyB8fCBkdWVBdCAtIGppdHRlciA+IGF0KSB7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgaWYgKGR1ZUF0IDw9IGF0ICYmICF0aGlzLnVucmVhZC5oYXModGFnKSkge1xuICAgICAgICBkdWUucHVzaCh0YWcpO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZHVlO1xuICB9XG5cbiAgLyoqXG4gICAqIEZvcmdldCBldmVyeSB0cmFja2VkIG1hcmtlciwgYW5kIGZvbGxvdyB0aGUgbG9nIGZyb20gYGF0YDogd2hhdCBpcyBuZWVkZWRcbiAgICogbmV4dCBpcyByZWFkIGZyb20gaXRzIG1hcmtlciBhcyBpdCBpcyBuZWVkZWQsIHNwcmVhZCBvdmVyIHRoZSByZXF1ZXN0c1xuICAgKiB0aGF0IG5lZWQgaXQsIHJhdGhlciB0aGFuIGV2ZXJ5IHRyYWNrZWQgdGFnIHJlLXJlYWQgYXQgb25jZS5cbiAgICovXG4gIHByaXZhdGUgZm9yZ2V0KGF0OiBudW1iZXIsIHdoeTogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5kZWJ1ZyhgZm9yZ2V0dGluZyAke3RoaXMudGFncy5zaXplfSB0cmFja2VkIHRhZ3M6ICR7d2h5fWApO1xuICAgIHRoaXMudGFncy5jbGVhcigpO1xuICAgIHRoaXMuZHVlQXQuY2xlYXIoKTtcbiAgICB0aGlzLnVucmVhZC5jbGVhcigpO1xuICAgIHRoaXMuYXBwbGllZC5jbGVhcigpO1xuICAgIHRoaXMuY3Vyc29yID0gYXQgLSBSRVZBTElEQVRJT05fTE9HX0xPT0tCQUNLX01TO1xuICAgIHRoaXMubGFzdExvZ1JlYWQgPSBhdDtcbiAgfVxuXG4gIC8qKlxuICAgKiBNb3ZlIHRoZSBjdXJzb3IgZm9yd2FyZCB0byBgY3Vyc29yYCwgZm9yZ2V0dGluZyBhcHBsaWVkIHJvd3MgYmVmb3JlIGl0OlxuICAgKiBubyBxdWVyeSByZXR1cm5zIHRoZW0gYWdhaW4uXG4gICAqL1xuICBwcml2YXRlIGFkdmFuY2UoY3Vyc29yOiBudW1iZXIpOiB2b2lkIHtcbiAgICBpZiAoY3Vyc29yIDw9IHRoaXMuY3Vyc29yKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuY3Vyc29yID0gY3Vyc29yO1xuICAgIGZvciAoY29uc3QgW3NrLCBhdF0gb2YgdGhpcy5hcHBsaWVkKSB7XG4gICAgICBpZiAoYXQgPCBjdXJzb3IpIHtcbiAgICAgICAgdGhpcy5hcHBsaWVkLmRlbGV0ZShzayk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJlYWQgYHRhZ3NgIGZyb20gdGhlIHRhYmxlLCBhbmQgcmVwb3J0IHdoZXRoZXIgdGhhdCB3b3JrZWQuIEEgcmVhZCB0aGF0XG4gICAqIGZhaWxzIGxlYXZlcyB0aGVtIHRyYWNrZWQgYXMgdGhleSB3ZXJlLCBhbmQgdGhlIG5leHQgcmVmcmVzaCByZWFkcyB0aGVtXG4gICAqIGFnYWluOiB0aGUgc2FtZSBcImFzc3VtZSB0aGUgZW50cnkgaXMgdmFsaWRcIiB0aGUgaW5jcmVtZW50YWwgY2FjaGUgaGFzXG4gICAqIGFsd2F5cyBhbnN3ZXJlZCB3aXRoLCByYXRoZXIgdGhhbiBhIHJlYWQgb24gZXZlcnkgcmVxdWVzdCB3aGlsZSBEeW5hbW9EQlxuICAgKiBpcyB1bmF2YWlsYWJsZS5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgcmVhZEludG8odGFnczogc3RyaW5nW10pOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBpZiAodGFncy5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgICBjb25zdCBhdCA9IHRoaXMuY2xvY2soKTtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVhZCA9IGF3YWl0IHRoaXMubWFya2VycyEucmVhZCh0YWdzKTtcbiAgICAgIGZvciAoY29uc3QgdGFnIG9mIHRhZ3MpIHtcbiAgICAgICAgdGhpcy51bnJlYWQuZGVsZXRlKHRhZyk7XG4gICAgICAgIHRoaXMucmVtZW1iZXIoXG4gICAgICAgICAgdGFnLFxuICAgICAgICAgIG1lcmdlTWFya2VycyhcbiAgICAgICAgICAgIG1lcmdlTWFya2Vycyh0aGlzLnRhZ3MuZ2V0KHRhZyksIHJlYWQuZ2V0KHRhZykpLFxuICAgICAgICAgICAgdGhpcy50YWtlUGVuZGluZ1Jvd3ModGFnKSxcbiAgICAgICAgICApLFxuICAgICAgICApO1xuICAgICAgICB0aGlzLmtub3duKHRhZywgYXQpO1xuICAgICAgfVxuICAgICAgdGhpcy5kZWJ1ZyhgcmVhZCAke3RhZ3MubGVuZ3RofSB0YWcgbWFya2VycyAoJHtyZWFkLnNpemV9IHNldClgKTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zb2xlLmVycm9yKFwiRXJyb3IgcmVhZGluZyB0YWcgbWFya2VyczpcIiwgZXJyb3IpO1xuICAgICAgdGhpcy50cmFja1VucmVhZCh0YWdzKTtcbiAgICAgIGZvciAoY29uc3QgdGFnIG9mIHRhZ3MpIHtcbiAgICAgICAgdGhpcy51bnJlYWQuYWRkKHRhZyk7XG4gICAgICAgIGNvbnN0IHBlbmRpbmcgPSB0aGlzLnRha2VQZW5kaW5nUm93cyh0YWcpO1xuICAgICAgICBpZiAocGVuZGluZykge1xuICAgICAgICAgIHRoaXMucmVtZW1iZXIodGFnLCBtZXJnZU1hcmtlcnModGhpcy50YWdzLmdldCh0YWcpLCBwZW5kaW5nKSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHRha2VQZW5kaW5nUm93cyh0YWc6IHN0cmluZyk6IFRhZ01hcmtlciB8IHVuZGVmaW5lZCB7XG4gICAgY29uc3QgcGVuZGluZyA9IHRoaXMucGVuZGluZ1Jvd3MuZ2V0KHRhZyk7XG4gICAgdGhpcy5wZW5kaW5nUm93cy5kZWxldGUodGFnKTtcbiAgICByZXR1cm4gcGVuZGluZztcbiAgfVxuXG4gIC8qKiBSZWNvcmQgdGhhdCBgdGFnYCdzIG1hcmtlciB3YXMga25vd24gZnJvbSB0aGUgdGFibGUgYXQgYGF0YC4gKi9cbiAgcHJpdmF0ZSBrbm93bih0YWc6IHN0cmluZywgYXQ6IG51bWJlcik6IHZvaWQge1xuICAgIHRoaXMuZHVlQXQuZGVsZXRlKHRhZyk7XG4gICAgdGhpcy5kdWVBdC5zZXQoXG4gICAgICB0YWcsXG4gICAgICBhdCArIHRoaXMucmVzeW5jSW50ZXJ2YWxNcyAqICgxIC0gVEFHX1JFU1lOQ19KSVRURVIgKiB0aGlzLnJhbmRvbSgpKSxcbiAgICApO1xuICB9XG5cbiAgcHJpdmF0ZSByZW1lbWJlcih0YWc6IHN0cmluZywgbWFya2VyOiBUYWdNYXJrZXIpOiB2b2lkIHtcbiAgICB0aGlzLnRhZ3MuZGVsZXRlKHRhZyk7XG4gICAgdGhpcy50YWdzLnNldCh0YWcsIG1hcmtlcik7XG4gICAgZm9yIChjb25zdCBvbGRlc3Qgb2YgdGhpcy50YWdzLmtleXMoKSkge1xuICAgICAgaWYgKHRoaXMudGFncy5zaXplIDw9IHRoaXMubWF4VHJhY2tlZFRhZ3MpIHtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICB0aGlzLnRhZ3MuZGVsZXRlKG9sZGVzdCk7XG4gICAgICB0aGlzLmR1ZUF0LmRlbGV0ZShvbGRlc3QpO1xuICAgICAgdGhpcy51bnJlYWQuZGVsZXRlKG9sZGVzdCk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSB0b3VjaCh0YWc6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IG1hcmtlciA9IHRoaXMudGFncy5nZXQodGFnKTtcbiAgICBpZiAobWFya2VyKSB7XG4gICAgICB0aGlzLnJlbWVtYmVyKHRhZywgbWFya2VyKTtcbiAgICB9XG4gIH1cbn1cblxuLyoqXG4gKiBUaGUgbG9uZ2VzdCBhIGxvZyByb3cncyBwdXQgbWF5IHRha2UgYW5kIHN0aWxsIGJlIHNlZW4gYnkgZXZlcnkgcmVhZGVyOiBoYWxmXG4gKiB0aGUgbG9va2JhY2ssIGxlYXZpbmcgdGhlIHJlc3QgZm9yIHdyaXRlcnMnIGNsb2NrcyBhbmQgZXZlbnR1YWxseSBjb25zaXN0ZW50XG4gKiBxdWVyaWVzLlxuICovXG5jb25zdCBNQVhfTE9HX1BVVF9NUyA9IFJFVkFMSURBVElPTl9MT0dfTE9PS0JBQ0tfTVMgLyAyO1xuXG4vKipcbiAqIEEgbWFya2VyIGFzIHJlYWQgZnJvbSB0aGUgdGFibGUgb3IgdGhlIGxvZywga2VlcGluZyB3aGF0ZXZlciB0aGlzIGluc3RhbmNlXG4gKiBhbHJlYWR5IGtub3dzIHRoYXQgdGhlIHJlYWQgZG9lcyBub3Qgc2hvdyB5ZXQ6IHJlYWRzIGFyZSBldmVudHVhbGx5XG4gKiBjb25zaXN0ZW50LCBhbmQgYSByZWFkIHN0cmFpZ2h0IGFmdGVyIHRoaXMgaW5zdGFuY2UncyBvd24gcmV2YWxpZGF0aW9uIGNvdWxkXG4gKiBvdGhlcndpc2UgdGFrZSBpdCBiYWNrLlxuICovXG5leHBvcnQgZnVuY3Rpb24gbWVyZ2VNYXJrZXJzKFxuICBsb2NhbDogVGFnTWFya2VyIHwgdW5kZWZpbmVkLFxuICByZW1vdGU6IFRhZ01hcmtlciB8IHVuZGVmaW5lZCxcbik6IFRhZ01hcmtlciB7XG4gIGlmICghbG9jYWwpIHtcbiAgICByZXR1cm4gcmVtb3RlID8/IHt9O1xuICB9XG4gIGlmICghcmVtb3RlKSB7XG4gICAgcmV0dXJuIGxvY2FsO1xuICB9XG4gIC8vIGBleHBpcmVkQXRgIGlzIHdyaXR0ZW4gd2l0aCBgc3RhbGVBdGAsIHNvIGl0IGJlbG9uZ3MgdG8gd2hpY2hldmVyIGlzXG4gIC8vIG5ld2VyIC0gdW5sZXNzIHRoYXQgcmV2YWxpZGF0aW9uJ3MgcHJvZmlsZSBoYWQgbm8gYGV4cGlyZWAgYW5kIGxlZnQgaXQuXG4gIGNvbnN0IFtvbGRlciwgbmV3ZXJdID1cbiAgICAobG9jYWwuc3RhbGVBdCA/PyAtMSkgPiAocmVtb3RlLnN0YWxlQXQgPz8gLTEpXG4gICAgICA/IFtyZW1vdGUsIGxvY2FsXVxuICAgICAgOiBbbG9jYWwsIHJlbW90ZV07XG4gIHJldHVybiB7XG4gICAgLi4uYXBwbHlNYXJrZXIob2xkZXIsIG5ld2VyKSxcbiAgICByZXZhbGlkYXRlZEF0OiBtYXhEZWZpbmVkKGxvY2FsLnJldmFsaWRhdGVkQXQsIHJlbW90ZS5yZXZhbGlkYXRlZEF0KSxcbiAgfTtcbn1cblxuZnVuY3Rpb24gbWF4RGVmaW5lZChhOiBudW1iZXIgfCB1bmRlZmluZWQsIGI6IG51bWJlciB8IHVuZGVmaW5lZCkge1xuICBpZiAoYSA9PT0gdW5kZWZpbmVkKSByZXR1cm4gYjtcbiAgaWYgKGIgPT09IHVuZGVmaW5lZCkgcmV0dXJuIGE7XG4gIHJldHVybiBNYXRoLm1heChhLCBiKTtcbn1cblxuLyoqXG4gKiBUaGUgY2FjaGUgYnVja2V0LCBhcyB0aGUgaGFuZGxlcnMgdXNlIGl0OiB3aG9sZSBvYmplY3RzIHJlYWQgYW5kIHdyaXR0ZW4gYXNcbiAqIFVURi04IHRleHQgdW5kZXIgdGhlIGtleXMgYWJvdmUuXG4gKi9cbmV4cG9ydCBjbGFzcyBDYWNoZUJ1Y2tldCB7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgY2xpZW50OiBTM0NsaWVudCxcbiAgICBwcml2YXRlIHJlYWRvbmx5IGJ1Y2tldE5hbWU6IHN0cmluZyxcbiAgKSB7fVxuXG4gIC8qKlxuICAgKiBUaGUgYm9keSBvZiB0aGUgb2JqZWN0IGF0IGBrZXlgLCBvciBgdW5kZWZpbmVkYCB3aGVuIHRoZXJlIGlzIG5vbmUgKGBOb1N1Y2hLZXlgLCBvciBhblxuICAgKiBlbXB0eSByZXNwb25zZSBib2R5KS4gQW55IG90aGVyIGVycm9yIGlzIHRocm93bjogdGhlIGhhbmRsZXJzIGRpc2FncmVlIG9uXG4gICAqIHdoZXRoZXIgb25lIGlzIHdvcnRoIGxvZ2dpbmcuXG4gICAqL1xuICBhc3luYyBnZXQoa2V5OiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZyB8IHVuZGVmaW5lZD4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IHRoaXMuY2xpZW50LnNlbmQoXG4gICAgICAgIG5ldyBHZXRPYmplY3RDb21tYW5kKHsgQnVja2V0OiB0aGlzLmJ1Y2tldE5hbWUsIEtleToga2V5IH0pLFxuICAgICAgKTtcbiAgICAgIGlmICghcmVzcG9uc2UuQm9keSkge1xuICAgICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGF3YWl0IHJlc3BvbnNlLkJvZHkudHJhbnNmb3JtVG9TdHJpbmcoXCJ1dGYtOFwiKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgaWYgKGVycm9yIGluc3RhbmNlb2YgTm9TdWNoS2V5KSB7XG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgICB9XG4gICAgICB0aHJvdyBlcnJvcjtcbiAgICB9XG4gIH1cblxuICAvKiogV3JpdGUgYGJvZHlgIGFzIGEgSlNPTiBvYmplY3QgYXQgYGtleWAuICovXG4gIGFzeW5jIHB1dEpzb24oa2V5OiBzdHJpbmcsIGJvZHk6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHRoaXMuY2xpZW50LnNlbmQoXG4gICAgICBuZXcgUHV0T2JqZWN0Q29tbWFuZCh7XG4gICAgICAgIEJ1Y2tldDogdGhpcy5idWNrZXROYW1lLFxuICAgICAgICBLZXk6IGtleSxcbiAgICAgICAgQm9keTogYm9keSxcbiAgICAgICAgQ29udGVudFR5cGU6IFwiYXBwbGljYXRpb24vanNvbjsgY2hhcnNldD11dGYtOFwiLFxuICAgICAgfSksXG4gICAgKTtcbiAgfVxufVxuIl19
|