cdk-nextjs 0.6.2 → 0.7.1
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 +1551 -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 +857 -0
- package/lib/adapter/build-outputs.test.d.ts +1 -0
- package/lib/adapter/build-outputs.test.js +863 -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 +1216 -0
- package/lib/runtime/core.test.d.ts +1 -0
- package/lib/runtime/core.test.js +1686 -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 +41600 -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 +71 -0
- package/lib/runtime/next-modules.js +144 -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 +41593 -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,1216 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NextjsRuntime = void 0;
|
|
4
|
+
exports.withoutInternalHeaders = withoutInternalHeaders;
|
|
5
|
+
exports.revalidatedPageRoutes = revalidatedPageRoutes;
|
|
6
|
+
exports.loadRuntime = loadRuntime;
|
|
7
|
+
exports.splitBody = splitBody;
|
|
8
|
+
/* eslint-disable import/no-extraneous-dependencies */
|
|
9
|
+
/**
|
|
10
|
+
* The runtime core: one `handle(request, sink)` that both shells wrap.
|
|
11
|
+
*
|
|
12
|
+
* The Lambda shell (`lambda.mts`) and the container shell (`server.mts`) do
|
|
13
|
+
* nothing but turn their input into a {@link RuntimeRequest} and their output into
|
|
14
|
+
* a {@link ResponseSink}. Everything else — synthesizing `req`/`res`, dispatch,
|
|
15
|
+
* middleware, entrypoint invocation, static files, image optimization, gzip,
|
|
16
|
+
* `waitUntil` — happens here, so the container e2e suite exercises the same code
|
|
17
|
+
* Lambda runs. That is deliberate: the container path used to get real
|
|
18
|
+
* `node:http` objects from Lambda Web Adapter, which meant container tests proved
|
|
19
|
+
* nothing about Lambda.
|
|
20
|
+
*/
|
|
21
|
+
const promises_1 = require("node:fs/promises");
|
|
22
|
+
const node_path_1 = require("node:path");
|
|
23
|
+
const node_stream_1 = require("node:stream");
|
|
24
|
+
const case_redirect_1 = require("./case-redirect");
|
|
25
|
+
const dispatch_1 = require("./dispatch");
|
|
26
|
+
const entrypoints_1 = require("./entrypoints");
|
|
27
|
+
const request_1 = require("./http/request");
|
|
28
|
+
const response_1 = require("./http/response");
|
|
29
|
+
const sink_1 = require("./http/sink");
|
|
30
|
+
const image_1 = require("./image");
|
|
31
|
+
const manifest_1 = require("./manifest");
|
|
32
|
+
const middleware_1 = require("./middleware");
|
|
33
|
+
const next_modules_1 = require("./next-modules");
|
|
34
|
+
const public_files_1 = require("./public-files");
|
|
35
|
+
const static_files_1 = require("./static-files");
|
|
36
|
+
const util_1 = require("./util");
|
|
37
|
+
class NextjsRuntime {
|
|
38
|
+
options;
|
|
39
|
+
entrypoints;
|
|
40
|
+
dispatcher;
|
|
41
|
+
middleware;
|
|
42
|
+
images;
|
|
43
|
+
/** The 500 ladder; see `statusTargets`. */
|
|
44
|
+
errorTargetFor;
|
|
45
|
+
/** The assets bucket, when `public/` files are served from it. */
|
|
46
|
+
publicBucket;
|
|
47
|
+
constructor(options) {
|
|
48
|
+
this.options = options;
|
|
49
|
+
const { manifest, deploymentRoot } = options;
|
|
50
|
+
this.entrypoints = new entrypoints_1.EntrypointRegistry(deploymentRoot, manifest);
|
|
51
|
+
// Listed once, at cold start; see `resolvePublicFiles`.
|
|
52
|
+
const publicFiles = (0, public_files_1.resolvePublicFiles)(deploymentRoot, manifest);
|
|
53
|
+
this.dispatcher = new dispatch_1.Dispatcher({
|
|
54
|
+
manifest,
|
|
55
|
+
publicFiles: publicFiles.files,
|
|
56
|
+
});
|
|
57
|
+
// On disk otherwise, with `.next/static`.
|
|
58
|
+
this.publicBucket = publicFiles.inS3 ? options.bucket : undefined;
|
|
59
|
+
this.errorTargetFor = (0, dispatch_1.statusTargets)(manifest, 500);
|
|
60
|
+
this.images = new image_1.RuntimeImageOptimizer({
|
|
61
|
+
deploymentRoot,
|
|
62
|
+
manifest,
|
|
63
|
+
bucket: this.publicBucket ?? "",
|
|
64
|
+
bucketKeyPrefix: options.bucketKeyPrefix ?? "",
|
|
65
|
+
fetchInternal: (href, req, maximumBody) => this.fetchInternal(href, req, maximumBody),
|
|
66
|
+
});
|
|
67
|
+
// The runner is shared — it memoizes the loaded middleware module — while the
|
|
68
|
+
// `invokeMiddleware` callback it produces is per request.
|
|
69
|
+
this.middleware = manifest.middleware
|
|
70
|
+
? new middleware_1.MiddlewareRunner({
|
|
71
|
+
middleware: manifest.middleware,
|
|
72
|
+
root: deploymentRoot,
|
|
73
|
+
})
|
|
74
|
+
: undefined;
|
|
75
|
+
}
|
|
76
|
+
get manifest() {
|
|
77
|
+
return this.options.manifest;
|
|
78
|
+
}
|
|
79
|
+
async handle(request, sink) {
|
|
80
|
+
const { manifest } = this.options;
|
|
81
|
+
const pending = [];
|
|
82
|
+
const waitUntil = (promise) => {
|
|
83
|
+
pending.push(promise);
|
|
84
|
+
};
|
|
85
|
+
const body = splitBody(request.body, this.middleware !== undefined);
|
|
86
|
+
const req = new request_1.ShimIncomingMessage({
|
|
87
|
+
method: request.method,
|
|
88
|
+
url: request.url,
|
|
89
|
+
headers: withoutInternalHeaders(request.headers, request.url, manifest.config.basePath),
|
|
90
|
+
body: body.forRequest,
|
|
91
|
+
remoteAddress: request.remoteAddress,
|
|
92
|
+
encrypted: request.encrypted,
|
|
93
|
+
trustForwardedHost: request.trustForwardedHost,
|
|
94
|
+
});
|
|
95
|
+
const res = new response_1.ShimServerResponse();
|
|
96
|
+
// Next.js reads `res.req` in a few error paths.
|
|
97
|
+
res.req = req;
|
|
98
|
+
request.signal?.addEventListener("abort", () => res.destroy(), {
|
|
99
|
+
once: true,
|
|
100
|
+
});
|
|
101
|
+
// The rejection handler is attached here rather than at the `await` below.
|
|
102
|
+
// `route()` awaits real I/O, so a stream that breaks while it runs would
|
|
103
|
+
// leave this promise rejected with no handler attached for a full event-loop
|
|
104
|
+
// turn — and Node 24 defaults to `--unhandled-rejections=throw`, which on
|
|
105
|
+
// the container shell takes the whole task down and aborts every other
|
|
106
|
+
// in-flight request on it. A mid-stream EPIPE from one client disconnect is
|
|
107
|
+
// not grounds for that.
|
|
108
|
+
//
|
|
109
|
+
// The error is logged, not rethrown: the stream broke after (or while) the
|
|
110
|
+
// head went out — a client disconnect, or `sendError` destroying a
|
|
111
|
+
// half-written response — and neither is an invocation failure. Rethrowing
|
|
112
|
+
// would make Lambda retry a request the client already abandoned.
|
|
113
|
+
const finished = (0, sink_1.pipeToSink)(req, res, sink, {
|
|
114
|
+
compress: manifest.config.compress,
|
|
115
|
+
}).catch((error) => {
|
|
116
|
+
console.error("The response stream did not complete:", error);
|
|
117
|
+
});
|
|
118
|
+
// For the error page's locale; unset when the throw is `absoluteUrl`'s.
|
|
119
|
+
let url;
|
|
120
|
+
try {
|
|
121
|
+
// Before anything parses the target, including `absoluteUrl` - `new URL`
|
|
122
|
+
// reads a leading `//` as protocol-relative and would take the first path
|
|
123
|
+
// segment for the host.
|
|
124
|
+
const collapsed = collapseRepeatedSlashes(request.url);
|
|
125
|
+
if (collapsed !== undefined) {
|
|
126
|
+
sendRedirect(res, collapsed, 308);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
// Inside the try: `absoluteUrl` builds a `URL` out of the request line
|
|
130
|
+
// and the forwarded headers, and a throw here has to reach the error
|
|
131
|
+
// ladder like any other. Outside it, on the Lambda shells — whose
|
|
132
|
+
// handlers wrap nothing — the same throw is an invocation error and a
|
|
133
|
+
// 502.
|
|
134
|
+
url = absoluteUrl(request);
|
|
135
|
+
await this.route(req, res, url, body, waitUntil, request);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
await this.sendError(req, res, waitUntil, error, url);
|
|
140
|
+
}
|
|
141
|
+
await finished;
|
|
142
|
+
// Lambda freezes the execution environment the moment the handler resolves —
|
|
143
|
+
// there is no post-response keepalive — so background work registered with
|
|
144
|
+
// `waitUntil` (notably ISR revalidation) has to be awaited here. After the
|
|
145
|
+
// response stream has closed, so client latency is unaffected; billed
|
|
146
|
+
// duration extends, which is the correct trade against a revalidation that
|
|
147
|
+
// never completes.
|
|
148
|
+
//
|
|
149
|
+
// Drained until nothing new arrives, not awaited once: `waitUntil` keeps
|
|
150
|
+
// being called after the response — Next's `AfterContext` registers its
|
|
151
|
+
// callback queue on the first `after()`, and an `after(promise)` made from
|
|
152
|
+
// inside a running callback, or a background regeneration that itself calls
|
|
153
|
+
// `after()`, registers more. A single `allSettled` snapshots the array and
|
|
154
|
+
// lets Lambda freeze on that late work.
|
|
155
|
+
let settledCount = 0;
|
|
156
|
+
while (settledCount < pending.length) {
|
|
157
|
+
const batch = pending.slice(settledCount);
|
|
158
|
+
settledCount = pending.length;
|
|
159
|
+
const settled = await Promise.allSettled(batch);
|
|
160
|
+
for (const result of settled) {
|
|
161
|
+
if (result.status === "rejected") {
|
|
162
|
+
console.error("A waitUntil() promise rejected:", result.reason);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async route(req, res, url, body, waitUntil, request) {
|
|
168
|
+
const result = await this.dispatcher.dispatch({
|
|
169
|
+
method: req.method ?? "GET",
|
|
170
|
+
url,
|
|
171
|
+
headers: new Headers(toWebHeaders(req.headers)),
|
|
172
|
+
body: body.forDispatch,
|
|
173
|
+
invokeMiddleware: this.middleware?.invokerFor({
|
|
174
|
+
waitUntil,
|
|
175
|
+
signal: request.signal,
|
|
176
|
+
}),
|
|
177
|
+
});
|
|
178
|
+
body.releaseUnread(result.kind === "middleware-responded");
|
|
179
|
+
applyHeaders(res, result.responseHeaders);
|
|
180
|
+
if (result.status !== undefined) {
|
|
181
|
+
res.statusCode = result.status;
|
|
182
|
+
}
|
|
183
|
+
switch (result.kind) {
|
|
184
|
+
case "entrypoint": {
|
|
185
|
+
let handler;
|
|
186
|
+
try {
|
|
187
|
+
handler = await this.entrypoints.load(result.entrypoint);
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
if (!(error instanceof entrypoints_1.RouteInOtherGroupError))
|
|
191
|
+
throw error;
|
|
192
|
+
// A URL the edge sent here only because of its case goes to its
|
|
193
|
+
// canonical spelling, which the edge routes to the owning group.
|
|
194
|
+
const canonical = (0, case_redirect_1.caseCanonicalPath)(url.pathname, result.resolvedPathname, this.options.manifest);
|
|
195
|
+
if (canonical) {
|
|
196
|
+
sendRedirect(res, `${canonical}${url.search}`, 308);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if (request.onRouteInOtherGroup) {
|
|
200
|
+
// Explained by the caller of `handleInternally`, which knows why it
|
|
201
|
+
// happened.
|
|
202
|
+
request.onRouteInOtherGroup(error);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
// Logged, because it is also what a misrouted group looks like; see
|
|
206
|
+
// `RouteInOtherGroupError` for why it is a 404.
|
|
207
|
+
console.warn(error.message);
|
|
208
|
+
}
|
|
209
|
+
await this.sendUnmatched(req, res, waitUntil, { pathname: url.pathname, requestHeaders: result.requestHeaders }, url);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
// The invocation target, not the requested URL: `resolveRoutes` has
|
|
213
|
+
// applied rewrites, stripped i18n prefixes, and appended the `nxtP`
|
|
214
|
+
// route params as query values. That is exactly the contract Next.js
|
|
215
|
+
// documents for a proxy in front of a function — `RouteModule.prepare`
|
|
216
|
+
// recovers `params` from these query values — so that contract, not
|
|
217
|
+
// `requestMeta.params`, is how params get passed. The one exception is
|
|
218
|
+
// a capture the contract would corrupt; see `outOfBandRouteParams`.
|
|
219
|
+
const outOfBand = (0, dispatch_1.outOfBandRouteParams)(result.invocationTarget.query, result.resolvedPathname);
|
|
220
|
+
const invocationQuery = outOfBand?.query ?? result.invocationTarget.query;
|
|
221
|
+
const { pathname } = result.invocationTarget;
|
|
222
|
+
const search = (0, util_1.toSearch)(invocationQuery);
|
|
223
|
+
req.url = search ? `${pathname}?${search}` : pathname;
|
|
224
|
+
// Middleware may have rewritten request headers via
|
|
225
|
+
// `NextResponse.next({ request: { headers } })`.
|
|
226
|
+
req.headers = (0, request_1.toIncomingHttpHeaders)(result.requestHeaders);
|
|
227
|
+
await handler(req, (0, response_1.asServerResponse)(res), {
|
|
228
|
+
waitUntil,
|
|
229
|
+
requestMeta: {
|
|
230
|
+
// The resolved query, stated rather than left to be re-derived.
|
|
231
|
+
//
|
|
232
|
+
// `RouteModule.prepare` runs `handleRewrites` against `req.url`
|
|
233
|
+
// unconditionally, and `req.url` is the *already rewritten* target.
|
|
234
|
+
// A `beforeFiles` rewrite whose condition still holds after it has
|
|
235
|
+
// been applied therefore gets applied twice: in
|
|
236
|
+
// `test/e2e/link-with-api-rewrite` the rule
|
|
237
|
+
// `/:path(.*)` + `has: query json=true` -> `/api/json?from=/:path`
|
|
238
|
+
// matched `/api/json?json=true&from=/some/route/for` a second time
|
|
239
|
+
// and overwrote `from` with `/api/json`. `next start` escapes it
|
|
240
|
+
// because its router leaves `req.url` as the URL the client sent, so
|
|
241
|
+
// the one pass `prepare` makes is the only one.
|
|
242
|
+
//
|
|
243
|
+
// `prepare` prefers this meta over its own `parsedUrl.query`
|
|
244
|
+
// ("when deployed proxies will add query values from resolving the
|
|
245
|
+
// routes to pass to function"), which is the documented division of
|
|
246
|
+
// labour for a proxy in front of a function - and it is the same
|
|
247
|
+
// division the `req.url` above relies on. Setting it does not stop
|
|
248
|
+
// the second rewrite pass, it just stops that pass from being what
|
|
249
|
+
// the route sees.
|
|
250
|
+
query: { ...invocationQuery },
|
|
251
|
+
// Set only for a capture the query contract cannot carry, and then
|
|
252
|
+
// in place of it rather than alongside.
|
|
253
|
+
...(outOfBand ? { params: outOfBand.params } : {}),
|
|
254
|
+
// Without this, `RouteModule.prepare` falls back to
|
|
255
|
+
// `http://localhost${req.url}` and every absolute URL a route
|
|
256
|
+
// handler builds is wrong. `relativeProjectDir` is deliberately
|
|
257
|
+
// *not* passed: the runtime `chdir`s to the project dir, so the
|
|
258
|
+
// value Next.js inlined at build time ("") is already correct, and
|
|
259
|
+
// `app-page-runtime.js` ignores the requestMeta override anyway.
|
|
260
|
+
initURL: url.href,
|
|
261
|
+
// Feeds `routerServerContext.hostname` (`route-module.js`
|
|
262
|
+
// `getRouterServerContext`). Includes the port, because it is
|
|
263
|
+
// concatenated into absolute URLs.
|
|
264
|
+
hostname: url.host,
|
|
265
|
+
// Both routers call this for a `notFound: true` / `notFound()` that
|
|
266
|
+
// the entrypoint itself cannot render, and fall back to
|
|
267
|
+
// `res.end('This page could not be found')` — no status, no app 404
|
|
268
|
+
// page — when it is absent. The `req`/`res` they pass are the ones
|
|
269
|
+
// handed in just above, so the captured pair is used instead of
|
|
270
|
+
// re-deriving them.
|
|
271
|
+
render404: async () => {
|
|
272
|
+
await this.sendNotFound(req, res, waitUntil, this.dispatcher.notFoundFor(url));
|
|
273
|
+
},
|
|
274
|
+
// `res.revalidate()` from a Pages API route. Without it, Next.js
|
|
275
|
+
// falls back to `fetch('https://' + req.headers.host + urlPath)` —
|
|
276
|
+
// and only when `experimental.trustHostHeader` is set, which it is
|
|
277
|
+
// not, so `res.revalidate()` threw
|
|
278
|
+
//
|
|
279
|
+
// Failed to revalidate /: Invariant: missing internal
|
|
280
|
+
// router-server-methods this is an internal bug
|
|
281
|
+
//
|
|
282
|
+
// and `test/e2e/revalidate-reason` saw `stale` where `next start`
|
|
283
|
+
// reports `on-demand`: the route still re-rendered, just as an
|
|
284
|
+
// ordinary stale regeneration rather than the on-demand one it
|
|
285
|
+
// asked for.
|
|
286
|
+
//
|
|
287
|
+
// Answering it in process rather than by setting
|
|
288
|
+
// `trustHostHeader`: the fetch path would leave the function, cross
|
|
289
|
+
// CloudFront and come back — a second billed invocation, a
|
|
290
|
+
// dependency on `x-prerender-revalidate` surviving the edge, and a
|
|
291
|
+
// reason to trust a client-supplied `Host`. Next.js's own
|
|
292
|
+
// non-serverless answer is the same shape as this one
|
|
293
|
+
// (`NextServer#revalidate` runs its request handler against a
|
|
294
|
+
// mocked `req`/`res`).
|
|
295
|
+
revalidate: (config) => this.revalidate(config, request),
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
// A Pages API route owns the end of its response: `stream.pipe(res)`,
|
|
299
|
+
// an `externalResolver` proxy, or a callback that calls `res.json()`
|
|
300
|
+
// later all return from the handler before they are done writing, and
|
|
301
|
+
// ending here sent an empty 200 and made their writes throw
|
|
302
|
+
// `ERR_STREAM_WRITE_AFTER_END`. `next start`'s `apiResolver` leaves it
|
|
303
|
+
// open too — one that never ends stalls, there as here. `handle` awaits
|
|
304
|
+
// the response itself, so nothing is cut short by returning.
|
|
305
|
+
if (!res.writableEnded && result.entrypoint.type !== "page-api") {
|
|
306
|
+
res.end();
|
|
307
|
+
}
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
case "static-file": {
|
|
311
|
+
const statusPage = result.source === "server"
|
|
312
|
+
? this.statusPageStatus(result.pathname)
|
|
313
|
+
: undefined;
|
|
314
|
+
// `next start` serves a `public/` or `_next/static` file to GET and
|
|
315
|
+
// HEAD only, and `send` would serve it to any method. The same goes for
|
|
316
|
+
// an auto-exported Pages Router page (`base-server.js`
|
|
317
|
+
// `renderToResponseWithComponentsImpl`: a string `Component` answers
|
|
318
|
+
// anything else with a 405), except the 404 and 500 pages.
|
|
319
|
+
if (req.method !== "GET" &&
|
|
320
|
+
req.method !== "HEAD" &&
|
|
321
|
+
(result.source !== "server" ||
|
|
322
|
+
(statusPage === undefined && result.filePath.endsWith(".html")))) {
|
|
323
|
+
sendMethodNotAllowed(res);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
// "ensure correct status is set when visiting a status page directly":
|
|
327
|
+
// `next start` answers `/404` with a 404 and `/500` with a 500, not the
|
|
328
|
+
// 200 their prerendered HTML would otherwise go out with.
|
|
329
|
+
if (statusPage !== undefined) {
|
|
330
|
+
res.statusCode = statusPage;
|
|
331
|
+
}
|
|
332
|
+
const etag = this.options.manifest.config.generateEtags;
|
|
333
|
+
const publicDir = `${(0, public_files_1.publicDirKey)(this.options.manifest)}/`;
|
|
334
|
+
const served = (await (0, static_files_1.serveStaticFile)(req, res, this.options.deploymentRoot, result.filePath, { etag })) ||
|
|
335
|
+
// Listed but not staged: a Lambda root, whose `public/` is in S3.
|
|
336
|
+
(result.source === "public" &&
|
|
337
|
+
!!this.publicBucket &&
|
|
338
|
+
(await (0, static_files_1.serveS3PublicFile)(req, res, {
|
|
339
|
+
bucket: this.publicBucket,
|
|
340
|
+
keyPrefix: this.options.bucketKeyPrefix ?? "",
|
|
341
|
+
file: result.filePath.slice(publicDir.length),
|
|
342
|
+
etag,
|
|
343
|
+
})));
|
|
344
|
+
if (!served) {
|
|
345
|
+
// The routing rule that matched a build asset already set its
|
|
346
|
+
// year-long `immutable` Cache-Control, which `sendUnmatched`
|
|
347
|
+
// replaces: under it the 404 would be cached at the edge and in
|
|
348
|
+
// browsers for that long, and a chunk that shows up on the next
|
|
349
|
+
// deploy would stay missing.
|
|
350
|
+
await this.sendUnmatched(req, res, waitUntil, result, url);
|
|
351
|
+
}
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
case "image-optimization":
|
|
355
|
+
if (!this.images.isEnabled()) {
|
|
356
|
+
await this.sendUnmatched(req, res, waitUntil, { pathname: url.pathname, requestHeaders: result.requestHeaders }, url);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
req.headers = (0, request_1.toIncomingHttpHeaders)(result.requestHeaders);
|
|
360
|
+
await this.images.handle(req, res, result.url, waitUntil);
|
|
361
|
+
return;
|
|
362
|
+
case "redirect":
|
|
363
|
+
sendRedirect(res, result.location, result.status);
|
|
364
|
+
return;
|
|
365
|
+
case "external-rewrite":
|
|
366
|
+
await proxyExternal(req, res, result.url, result.requestHeaders);
|
|
367
|
+
return;
|
|
368
|
+
case "middleware-responded":
|
|
369
|
+
await sendWebResponse(res, result.response);
|
|
370
|
+
return;
|
|
371
|
+
case "response":
|
|
372
|
+
res.end();
|
|
373
|
+
return;
|
|
374
|
+
case "not-found":
|
|
375
|
+
await this.sendUnmatched(req, res, waitUntil, result, url);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* 404 or 500 when `pathname` is the prerendered `/404` or `/500` page — under
|
|
381
|
+
* `basePath` and, in an i18n app, any locale (`/fr/404`) — and `undefined`
|
|
382
|
+
* otherwise. `next start` matches the page, not the URL, so the locale does
|
|
383
|
+
* not matter.
|
|
384
|
+
*/
|
|
385
|
+
statusPageStatus(pathname) {
|
|
386
|
+
const { basePath, i18n } = this.options.manifest.config;
|
|
387
|
+
let page = (0, util_1.withoutPathPrefix)(pathname, basePath);
|
|
388
|
+
const locales = i18n?.locales;
|
|
389
|
+
for (const locale of locales ?? []) {
|
|
390
|
+
const stripped = (0, util_1.withoutPathPrefix)(page, `/${locale}`);
|
|
391
|
+
if (stripped !== page) {
|
|
392
|
+
page = stripped;
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
if (page === "/404")
|
|
397
|
+
return 404;
|
|
398
|
+
if (page === "/500")
|
|
399
|
+
return 500;
|
|
400
|
+
return undefined;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* `next start`'s answer to a path nothing serves (`router-server.js`, the
|
|
404
|
+
* "404 case"), for both ways of getting here: dispatch matched nothing, or it
|
|
405
|
+
* matched a build asset that is not in the package.
|
|
406
|
+
*
|
|
407
|
+
* No-store, always. A prerendered `/_not-found` would otherwise send its
|
|
408
|
+
* cache entry's year-long `s-maxage`, and the CDN would keep the 404 past the
|
|
409
|
+
* deploy that adds the route. Set rather than forced: Next's page handlers
|
|
410
|
+
* only write a `Cache-Control` when none is set (`sendRenderResult`,
|
|
411
|
+
* `pages-handler.js`), which is exactly how `next start`'s own no-store here
|
|
412
|
+
* survives the render. `render404` is not this path: a `notFound()` from an
|
|
413
|
+
* ISR page is cacheable for that page's revalidate period.
|
|
414
|
+
*
|
|
415
|
+
* A missing `_next/static` file, and a GET or HEAD whose `Sec-Fetch-Dest`
|
|
416
|
+
* says it cannot display HTML (an `<img>`, a script, a font), get a
|
|
417
|
+
* plain-text `Not Found` instead of the rendered page — nothing would show
|
|
418
|
+
* it, and rendering it is the expensive part.
|
|
419
|
+
*
|
|
420
|
+
* Everything else renders the app's 404 as the path that was asked for, not
|
|
421
|
+
* as `/_not-found`: the App Router serializes the canonical URL into the RSC
|
|
422
|
+
* payload, so a client hydrated off a `/_not-found` payload reports the wrong
|
|
423
|
+
* `usePathname()` and pushes the wrong history entry. It renders with the
|
|
424
|
+
* request headers middleware set, too — a CSP nonce, say.
|
|
425
|
+
*/
|
|
426
|
+
async sendUnmatched(req, res, waitUntil, result, url) {
|
|
427
|
+
res.setHeader("Cache-Control", NO_STORE);
|
|
428
|
+
const { basePath, assetPrefix } = this.options.manifest.config;
|
|
429
|
+
const pathname = (0, util_1.withoutPathPrefix)((0, util_1.withoutPathPrefix)(result.pathname, basePath), assetPrefix);
|
|
430
|
+
if (pathname.startsWith("/_next/static/") ||
|
|
431
|
+
((req.method === "GET" || req.method === "HEAD") &&
|
|
432
|
+
NON_HTML_SEC_FETCH_DESTS.has((0, util_1.firstValue)(req.headers["sec-fetch-dest"]) ?? ""))) {
|
|
433
|
+
res.statusCode = 404;
|
|
434
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
435
|
+
res.end("Not Found");
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
req.headers = (0, request_1.toIncomingHttpHeaders)(result.requestHeaders);
|
|
439
|
+
await this.sendNotFound(req, res, waitUntil, this.dispatcher.notFoundFor(url), `${result.pathname}${url.search}`);
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* One on-demand revalidation, run against this runtime rather than over the
|
|
443
|
+
* network. Wired in as `requestMeta.revalidate`; see the comment there.
|
|
444
|
+
*
|
|
445
|
+
* The response is thrown away — only its status matters — but it goes through
|
|
446
|
+
* the full {@link handle}, so the entrypoint sees a request with
|
|
447
|
+
* `x-prerender-revalidate` on it and writes the fresh entry through the cache
|
|
448
|
+
* handler exactly as a request from outside would. `handle` awaits its own
|
|
449
|
+
* `waitUntil` work, so the new entry is committed before `res.revalidate()`
|
|
450
|
+
* resolves, which is what callers that revalidate and then redirect depend on.
|
|
451
|
+
*
|
|
452
|
+
* The accept/throw rule is `NextServer#revalidate`'s, verbatim: a revalidation
|
|
453
|
+
* is successful if it was cached or answered 200, and `notFound: true`
|
|
454
|
+
* legitimately answers 404 for an `unstable_onlyGenerated` caller. Anything
|
|
455
|
+
* else throws, and `res.revalidate()` turns it into `Failed to revalidate
|
|
456
|
+
* <path>: <message>`.
|
|
457
|
+
*/
|
|
458
|
+
async revalidate(config, origin) {
|
|
459
|
+
const { head, otherGroup } = await this.handleInternally({
|
|
460
|
+
method: "GET",
|
|
461
|
+
url: config.urlPath,
|
|
462
|
+
// The caller's authority, not the revalidated path's: it is what
|
|
463
|
+
// `absoluteUrl` builds the request URL from, and a revalidation is for
|
|
464
|
+
// the origin being served. The forwarded pair with it — behind
|
|
465
|
+
// CloudFront `host` is the Function URL, and a render that built its
|
|
466
|
+
// absolute URLs from that would cache links to the raw function.
|
|
467
|
+
headers: {
|
|
468
|
+
...config.headers,
|
|
469
|
+
host: origin.headers.host,
|
|
470
|
+
...pickDefined(origin.headers, [
|
|
471
|
+
"x-forwarded-host",
|
|
472
|
+
"x-forwarded-proto",
|
|
473
|
+
]),
|
|
474
|
+
},
|
|
475
|
+
encrypted: origin.encrypted,
|
|
476
|
+
trustForwardedHost: origin.trustForwardedHost,
|
|
477
|
+
}, {});
|
|
478
|
+
const status = head?.statusCode ?? 500;
|
|
479
|
+
if (otherGroup) {
|
|
480
|
+
const self = process.env[manifest_1.FUNCTION_GROUP_ENV_VAR];
|
|
481
|
+
// A page packaged into another `functionGroups` group: it is rendered in
|
|
482
|
+
// process, and this function does not have the page's code. Checked
|
|
483
|
+
// first because it renders as a 404, which `unstable_onlyGenerated`
|
|
484
|
+
// would otherwise accept.
|
|
485
|
+
throw new Error(`Invalid response ${status}: "${config.urlPath}" belongs to ` +
|
|
486
|
+
`\`functionGroups\` group "${otherGroup.owner}", and ` +
|
|
487
|
+
`res.revalidate() can only revalidate pages in the group it runs ` +
|
|
488
|
+
`in ("${self}"). Call it from an API route in group ` +
|
|
489
|
+
`"${otherGroup.owner}", or use revalidatePath()/revalidateTag(), ` +
|
|
490
|
+
`which work from any group.`);
|
|
491
|
+
}
|
|
492
|
+
if (head?.headers["x-nextjs-cache"] !== "REVALIDATED" &&
|
|
493
|
+
status !== 200 &&
|
|
494
|
+
!(status === 404 && config.opts.unstable_onlyGenerated)) {
|
|
495
|
+
throw new Error(`Invalid response ${status}`);
|
|
496
|
+
}
|
|
497
|
+
await invalidateRevalidatedPage(config.urlPath, this.options.manifest);
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* An image optimization source that is not a file: `<Image src="/api/avatar">`
|
|
501
|
+
* served by a route handler. Wired in as the optimizer's `fetchInternal`, and
|
|
502
|
+
* run in process for the reasons {@link revalidate} is — `next start` fetches
|
|
503
|
+
* the source from itself over loopback, which here would be a second billed
|
|
504
|
+
* invocation through CloudFront.
|
|
505
|
+
*
|
|
506
|
+
* Only the authority is forwarded, as `next start`'s `fetchInternalImage`
|
|
507
|
+
* forwards none of the viewer's headers: the source is cached and served to
|
|
508
|
+
* everyone, so it must not depend on one viewer's cookies.
|
|
509
|
+
*/
|
|
510
|
+
async fetchInternal(href, req, maximumBody) {
|
|
511
|
+
const request = {
|
|
512
|
+
method: "GET",
|
|
513
|
+
url: href,
|
|
514
|
+
headers: pickDefined(req.headers, [
|
|
515
|
+
"host",
|
|
516
|
+
"x-forwarded-host",
|
|
517
|
+
"x-forwarded-proto",
|
|
518
|
+
]),
|
|
519
|
+
encrypted: req.socket?.encrypted,
|
|
520
|
+
trustForwardedHost: req.trustForwardedHost,
|
|
521
|
+
};
|
|
522
|
+
const { head, body, otherGroup, tooLarge } = await this.handleInternally(request, { maximumBody });
|
|
523
|
+
if (tooLarge) {
|
|
524
|
+
return { statusCode: 0, headers: {}, body: Buffer.alloc(0), tooLarge };
|
|
525
|
+
}
|
|
526
|
+
if (otherGroup) {
|
|
527
|
+
// The edge sends every `/_next/image` request to the default group, and
|
|
528
|
+
// the source is rendered in process, so a route packaged into another
|
|
529
|
+
// group is out of reach: the optimizer answers (and logs) a 502.
|
|
530
|
+
return {
|
|
531
|
+
statusCode: 0,
|
|
532
|
+
headers: {},
|
|
533
|
+
body: Buffer.alloc(0),
|
|
534
|
+
otherGroup: otherGroup.owner,
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
return {
|
|
538
|
+
statusCode: head?.statusCode ?? 0,
|
|
539
|
+
headers: head?.headers ?? {},
|
|
540
|
+
body,
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* One request through {@link handle}, answered to this runtime instead of a
|
|
545
|
+
* client: the head, the body when it is wanted, and the error when the route
|
|
546
|
+
* is packaged into another `functionGroups` group. `handle` awaits the
|
|
547
|
+
* request's `waitUntil` work too, so whatever the render wrote is committed by
|
|
548
|
+
* the time this resolves.
|
|
549
|
+
*
|
|
550
|
+
* The body is kept only when `maximumBody` is set. One past it fails the
|
|
551
|
+
* response stream, which stops the route writing, and comes back as
|
|
552
|
+
* `tooLarge` rather than held in memory.
|
|
553
|
+
*/
|
|
554
|
+
async handleInternally(request, options) {
|
|
555
|
+
let head;
|
|
556
|
+
const chunks = [];
|
|
557
|
+
let size = 0;
|
|
558
|
+
let tooLarge = false;
|
|
559
|
+
let otherGroup;
|
|
560
|
+
const onRouteInOtherGroup = (error) => {
|
|
561
|
+
otherGroup = error;
|
|
562
|
+
};
|
|
563
|
+
await this.handle({ ...request, onRouteInOtherGroup }, {
|
|
564
|
+
begin(responseHead) {
|
|
565
|
+
head = responseHead;
|
|
566
|
+
return new node_stream_1.Writable({
|
|
567
|
+
write(chunk, _encoding, callback) {
|
|
568
|
+
if (options.maximumBody === undefined)
|
|
569
|
+
return callback();
|
|
570
|
+
size += chunk.byteLength;
|
|
571
|
+
if (size > options.maximumBody) {
|
|
572
|
+
tooLarge = true;
|
|
573
|
+
return callback(new Error("Over images.maximumResponseBody"));
|
|
574
|
+
}
|
|
575
|
+
chunks.push(Buffer.from(chunk));
|
|
576
|
+
callback();
|
|
577
|
+
},
|
|
578
|
+
});
|
|
579
|
+
},
|
|
580
|
+
});
|
|
581
|
+
return {
|
|
582
|
+
head,
|
|
583
|
+
body: tooLarge ? Buffer.alloc(0) : Buffer.concat(chunks),
|
|
584
|
+
otherGroup,
|
|
585
|
+
tooLarge,
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* App Router builds an invocable `/_not-found`, Pages Router a prerendered
|
|
590
|
+
* `404.html` (per locale under i18n) or else `/_error`; see `statusTargets`.
|
|
591
|
+
*/
|
|
592
|
+
async sendNotFound(req, res, waitUntil, target,
|
|
593
|
+
/**
|
|
594
|
+
* What to render the 404 as. Omitted by `render404`, which is called
|
|
595
|
+
* mid-render with `req.url` already pointing at the route that gave up —
|
|
596
|
+
* the path Next.js would render that 404 for.
|
|
597
|
+
*/
|
|
598
|
+
requestedUrl) {
|
|
599
|
+
res.statusCode = 404;
|
|
600
|
+
if (target.kind === "entrypoint") {
|
|
601
|
+
const handler = await this.entrypoints.load(target.entrypoint);
|
|
602
|
+
req.url = requestedUrl ?? req.url;
|
|
603
|
+
await handler(req, (0, response_1.asServerResponse)(res), { waitUntil });
|
|
604
|
+
if (!res.writableEnded) {
|
|
605
|
+
res.end();
|
|
606
|
+
}
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
if (target.kind === "static-file" &&
|
|
610
|
+
(await this.sendHtmlFile(res, target))) {
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
614
|
+
res.end("This page could not be found.");
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Turn a thrown error into a response, rendering the app's error page when it
|
|
618
|
+
* has one.
|
|
619
|
+
*
|
|
620
|
+
* Next.js's own page handlers catch, report and then *rethrow* ("rethrow so
|
|
621
|
+
* that we can handle serving error page", `pages-handler.ts`), which makes the
|
|
622
|
+
* error page the host's job — the same division of labor as `render404`. Without
|
|
623
|
+
* this ladder every throw answered a bare `text/plain` 500 and a custom
|
|
624
|
+
* `pages/_error` was dead code (`test/e2e/async-modules`, whose `/make-error`
|
|
625
|
+
* throws in `getServerSideProps`).
|
|
626
|
+
*
|
|
627
|
+
* Once the head is out there is nothing to say — destroying the stream is what
|
|
628
|
+
* tells the client the response is truncated rather than complete.
|
|
629
|
+
*/
|
|
630
|
+
async sendError(req, res, waitUntil, error,
|
|
631
|
+
/** The request, as received: which locale's prerendered 500 to send. */
|
|
632
|
+
url) {
|
|
633
|
+
console.error("Unhandled error while handling the request:", error);
|
|
634
|
+
if (res.headersSent) {
|
|
635
|
+
res.destroy(asError(error));
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
res.statusCode = 500;
|
|
639
|
+
// A failed render may have set headers describing a body that never
|
|
640
|
+
// arrived. Every rung below sends a different body, so they go first: a
|
|
641
|
+
// stale `Content-Length` on a 500.html of another size hangs the client or
|
|
642
|
+
// desyncs a keep-alive connection.
|
|
643
|
+
res.removeHeader("Content-Length");
|
|
644
|
+
res.removeHeader("ETag");
|
|
645
|
+
// Every rung, the error-page entrypoint included: otherwise the throwing
|
|
646
|
+
// render's `s-maxage` (or a `headers()` rule's) rides along and CloudFront
|
|
647
|
+
// caches the 500. `next start` sets it before rendering the error page too.
|
|
648
|
+
res.setHeader("Cache-Control", NO_STORE);
|
|
649
|
+
const target = this.errorTargetFor(url);
|
|
650
|
+
if (target.kind === "entrypoint") {
|
|
651
|
+
try {
|
|
652
|
+
const handler = await this.entrypoints.load(target.entrypoint);
|
|
653
|
+
// `req.url` is left alone: the error page renders for the URL that was
|
|
654
|
+
// asked for, and `_error`'s `getInitialProps` reads the status off `res`,
|
|
655
|
+
// which is why the 500 above is set first.
|
|
656
|
+
await handler(req, (0, response_1.asServerResponse)(res), { waitUntil });
|
|
657
|
+
if (!res.writableEnded) {
|
|
658
|
+
res.end();
|
|
659
|
+
}
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
catch (errorPageError) {
|
|
663
|
+
console.error("The error page itself failed to render:", errorPageError);
|
|
664
|
+
if (res.headersSent) {
|
|
665
|
+
res.destroy(asError(errorPageError));
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
if (target.kind === "static-file" &&
|
|
671
|
+
(await this.sendHtmlFile(res, target))) {
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
// Again: an error page that threw may have described its own body too.
|
|
675
|
+
res.removeHeader("Content-Length");
|
|
676
|
+
res.removeHeader("ETag");
|
|
677
|
+
res.statusCode = 500;
|
|
678
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
679
|
+
res.setHeader("Cache-Control", NO_STORE);
|
|
680
|
+
res.end("Internal Server Error");
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* A prerendered 404 or 500 page, or `false` when it cannot be read and the
|
|
684
|
+
* caller's plain-text fallback should answer. Read rather than `serveStatic`,
|
|
685
|
+
* which keeps the status already set but would answer a `Range` request with
|
|
686
|
+
* a 206 of the error page, and add `ETag`, `Last-Modified` and, where none is
|
|
687
|
+
* set yet, a public `Cache-Control` to it.
|
|
688
|
+
*/
|
|
689
|
+
async sendHtmlFile(res, target) {
|
|
690
|
+
let html;
|
|
691
|
+
try {
|
|
692
|
+
html = await (0, promises_1.readFile)((0, node_path_1.join)(this.options.deploymentRoot, target.filePath));
|
|
693
|
+
}
|
|
694
|
+
catch {
|
|
695
|
+
return false;
|
|
696
|
+
}
|
|
697
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
698
|
+
res.end(html);
|
|
699
|
+
return true;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
exports.NextjsRuntime = NextjsRuntime;
|
|
703
|
+
/**
|
|
704
|
+
* What next sends with an error it rendered itself. Without it a `Cache-Control`
|
|
705
|
+
* left behind by the render that threw can get a 500 cached at the edge.
|
|
706
|
+
*/
|
|
707
|
+
const NO_STORE = "private, no-cache, no-store, max-age=0, must-revalidate";
|
|
708
|
+
/**
|
|
709
|
+
* `Sec-Fetch-Dest` values that can never display an HTML response, from
|
|
710
|
+
* `next/dist/server/lib/is-non-html-sec-fetch-dest.js`. Restated rather than
|
|
711
|
+
* required, because that module is recent and the app's `next` may predate it:
|
|
712
|
+
* a failed require here would turn every 404 into a 500. Excludes `document`,
|
|
713
|
+
* `iframe` and the like, and `empty` — `fetch()`, which is how RSC requests go.
|
|
714
|
+
*/
|
|
715
|
+
const NON_HTML_SEC_FETCH_DESTS = new Set([
|
|
716
|
+
"audio",
|
|
717
|
+
"audioworklet",
|
|
718
|
+
"font",
|
|
719
|
+
"image",
|
|
720
|
+
"json",
|
|
721
|
+
"manifest",
|
|
722
|
+
"paintworklet",
|
|
723
|
+
"report",
|
|
724
|
+
"script",
|
|
725
|
+
"serviceworker",
|
|
726
|
+
"sharedworker",
|
|
727
|
+
"style",
|
|
728
|
+
"track",
|
|
729
|
+
"video",
|
|
730
|
+
"webidentity",
|
|
731
|
+
"worker",
|
|
732
|
+
"xslt",
|
|
733
|
+
]);
|
|
734
|
+
/**
|
|
735
|
+
* `INTERNAL_HEADERS` from `next/dist/server/lib/server-ipc/utils.js` (Next
|
|
736
|
+
* 16.3), which `next start` deletes from every request before routing it
|
|
737
|
+
* (`filterInternalHeaders` in `router-server.js`). They are signals between
|
|
738
|
+
* Next's own router and render layers; honoring one a client sent lets it steer
|
|
739
|
+
* those layers — `next-resume` makes a PPR page resume from a postponed state
|
|
740
|
+
* the client supplies. Restated rather than required, like
|
|
741
|
+
* {@link NON_HTML_SEC_FETCH_DESTS}.
|
|
742
|
+
*/
|
|
743
|
+
const INTERNAL_HEADERS = new Set([
|
|
744
|
+
"x-middleware-rewrite",
|
|
745
|
+
"x-middleware-redirect",
|
|
746
|
+
"x-middleware-set-cookie",
|
|
747
|
+
"x-middleware-skip",
|
|
748
|
+
"x-middleware-override-headers",
|
|
749
|
+
"x-middleware-next",
|
|
750
|
+
"x-now-route-matches",
|
|
751
|
+
"x-matched-path",
|
|
752
|
+
"x-nextjs-data",
|
|
753
|
+
"x-next-resume-state-length",
|
|
754
|
+
"next-resume",
|
|
755
|
+
]);
|
|
756
|
+
/**
|
|
757
|
+
* The request headers with {@link INTERNAL_HEADERS} removed, and `x-nextjs-data`
|
|
758
|
+
* put back for a `_next/data` request, which is what `next start`'s
|
|
759
|
+
* `resolveRoutes` does (`setIsNextDataRequest`) right after stripping it: the
|
|
760
|
+
* header is internal, but Next's handlers and middleware still read it — it is
|
|
761
|
+
* how a data request gets `x-nextjs-matched-path` — so a real data request has
|
|
762
|
+
* to keep it.
|
|
763
|
+
*/
|
|
764
|
+
function withoutInternalHeaders(headers, target, basePath) {
|
|
765
|
+
const filtered = {};
|
|
766
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
767
|
+
if (!INTERNAL_HEADERS.has(name)) {
|
|
768
|
+
filtered[name] = value;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
const pathname = (0, util_1.withoutPathPrefix)(target.split("?")[0], basePath);
|
|
772
|
+
if (pathname.startsWith("/_next/data/") && pathname.endsWith(".json")) {
|
|
773
|
+
filtered["x-nextjs-data"] = "1";
|
|
774
|
+
}
|
|
775
|
+
return filtered;
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Invalidate the CDN copies of a page `res.revalidate()` just regenerated: its
|
|
779
|
+
* HTML and RSC payload, and its Pages Router `_next/data` JSON. Without this the
|
|
780
|
+
* fresh entry sat behind CloudFront's copy of the old one for the page's whole
|
|
781
|
+
* `s-maxage` — a year, for an on-demand-only ISR page. `revalidatePath` and
|
|
782
|
+
* `revalidateTag` go through the cache handler's `revalidateTag`, which already
|
|
783
|
+
* invalidates; `res.revalidate()` does not touch it.
|
|
784
|
+
*
|
|
785
|
+
* Every route is without `basePath`, which the hook prefixes. A no-op outside
|
|
786
|
+
* the Global constructs, where no hook is registered.
|
|
787
|
+
*/
|
|
788
|
+
async function invalidateRevalidatedPage(urlPath, manifest) {
|
|
789
|
+
const hook = globalThis[manifest_1.REVALIDATED_PAGE_HOOK];
|
|
790
|
+
if (typeof hook !== "function") {
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
const route = (0, util_1.withoutPathPrefix)(urlPath.split("?")[0], manifest.config.basePath);
|
|
794
|
+
try {
|
|
795
|
+
// One call, deduplicated: the default locale's two pairs share a data
|
|
796
|
+
// route, and each invalidation path counts against CloudFront's quota.
|
|
797
|
+
await hook([...new Set(revalidatedPageRoutes(route, manifest).flat())]);
|
|
798
|
+
}
|
|
799
|
+
catch (error) {
|
|
800
|
+
// The regeneration itself succeeded; the edge catches up at `s-maxage`.
|
|
801
|
+
console.warn(`Could not invalidate the CDN copy of ${urlPath}:`, error);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* The `[route, dataRoute]` pairs a revalidated page is cached under at the edge.
|
|
806
|
+
*
|
|
807
|
+
* In an i18n app the Next client always puts the locale in a data href, the
|
|
808
|
+
* default locale included (`/_next/data/<id>/en/blog.json`, and `…/en.json` for
|
|
809
|
+
* the root), so the unprefixed data route names nothing CloudFront holds. The
|
|
810
|
+
* page is one locale's — `res.revalidate("/blog")` regenerates the default
|
|
811
|
+
* locale's, `"/fr/blog"` French — and the default locale's HTML is reachable,
|
|
812
|
+
* and cached, both with and without its prefix, so its two pairs share a data
|
|
813
|
+
* route, which the caller sends once.
|
|
814
|
+
*/
|
|
815
|
+
function revalidatedPageRoutes(route, manifest) {
|
|
816
|
+
const dataRoute = (page) => `/_next/data/${manifest.buildId}${page === "/" ? "/index" : page.replace(/\/+$/, "")}.json`;
|
|
817
|
+
const i18n = manifest.config.i18n;
|
|
818
|
+
if (!i18n?.defaultLocale) {
|
|
819
|
+
return [[route, dataRoute(route)]];
|
|
820
|
+
}
|
|
821
|
+
const { defaultLocale } = i18n;
|
|
822
|
+
const segment = route.split("/")[1] ?? "";
|
|
823
|
+
const locale = (i18n.locales ?? []).includes(segment)
|
|
824
|
+
? segment
|
|
825
|
+
: defaultLocale;
|
|
826
|
+
const page = locale === segment ? (0, util_1.withoutPathPrefix)(route, `/${segment}`) : route;
|
|
827
|
+
const localized = page === "/" ? `/${locale}` : `/${locale}${page}`;
|
|
828
|
+
// The root's data route is the locale itself, not `/<locale>/index`.
|
|
829
|
+
const localizedData = `/_next/data/${manifest.buildId}${localized.replace(/\/+$/, "")}.json`;
|
|
830
|
+
return locale === defaultLocale
|
|
831
|
+
? [
|
|
832
|
+
[page, localizedData],
|
|
833
|
+
[localized, localizedData],
|
|
834
|
+
]
|
|
835
|
+
: [[localized, localizedData]];
|
|
836
|
+
}
|
|
837
|
+
function asError(error) {
|
|
838
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* Read the manifest and pin the layout it describes.
|
|
842
|
+
*
|
|
843
|
+
* `process.cwd()` is load-bearing and there is exactly one chance to get it
|
|
844
|
+
* right: Next.js inlines `relative(buildCwd, projectDir)` into every entrypoint
|
|
845
|
+
* and resolves it against the *runtime* cwd to find
|
|
846
|
+
* `required-server-files.json`, the prerender manifest, and the app's chunks.
|
|
847
|
+
* `assertBuildCwd` in `build-outputs.ts` keeps that inlined value `""`, so the
|
|
848
|
+
* whole invariant reduces to "cwd is the staged project dir" — asserted rather
|
|
849
|
+
* than assumed, because getting it wrong produces a
|
|
850
|
+
* `Cannot find module …/required-server-files.json` from inside compiled Next.js
|
|
851
|
+
* code with no indication that the layout is the problem.
|
|
852
|
+
*/
|
|
853
|
+
async function loadRuntime(deploymentRoot) {
|
|
854
|
+
const manifestPath = (0, manifest_1.deployedManifestPath)(deploymentRoot);
|
|
855
|
+
let manifest;
|
|
856
|
+
try {
|
|
857
|
+
manifest = JSON.parse(await (0, promises_1.readFile)(manifestPath, "utf-8"));
|
|
858
|
+
}
|
|
859
|
+
catch (error) {
|
|
860
|
+
throw new Error(`Could not read the cdk-nextjs adapter manifest at "${manifestPath}". ` +
|
|
861
|
+
`The deployment package is incomplete: synth copies ` +
|
|
862
|
+
`${manifest_1.MANIFEST_FILE_NAME} in next to the bundled runtime.`, { cause: error });
|
|
863
|
+
}
|
|
864
|
+
const projectDir = (0, node_path_1.join)(deploymentRoot, manifest.relativeProjectDir);
|
|
865
|
+
const probe = (0, node_path_1.join)(projectDir, manifest.config.distDir, "required-server-files.json");
|
|
866
|
+
try {
|
|
867
|
+
await (0, promises_1.readFile)(probe);
|
|
868
|
+
}
|
|
869
|
+
catch (error) {
|
|
870
|
+
throw new Error(`The deployment root "${deploymentRoot}" does not contain the staged ` +
|
|
871
|
+
`Next.js project: expected "${probe}" to exist. Every built entrypoint ` +
|
|
872
|
+
`resolves its own paths against process.cwd(), so this layout is part ` +
|
|
873
|
+
`of the contract, not a convenience.`, { cause: error });
|
|
874
|
+
}
|
|
875
|
+
// `next start` forces this, and externalized packages (React for Pages Router
|
|
876
|
+
// SSR, anything in `serverExternalPackages`) pick their dev or prod build from
|
|
877
|
+
// it at require time. The Containers Dockerfiles set it and the Functions
|
|
878
|
+
// constructs do too; this covers a hand-wired function. Before any `require`.
|
|
879
|
+
// (`next`'s typings declare NODE_ENV read-only.)
|
|
880
|
+
process.env.NODE_ENV ??= "production";
|
|
881
|
+
process.chdir(projectDir);
|
|
882
|
+
// Before anything can serve a request: the runtime's own `next` imports resolve
|
|
883
|
+
// out of the staged app, not out of the shell's directory. See `next-modules.ts`.
|
|
884
|
+
(0, next_modules_1.useNextFrom)(projectDir);
|
|
885
|
+
// Before any entrypoint can read them at module scope.
|
|
886
|
+
(0, next_modules_1.loadEnvFiles)(projectDir);
|
|
887
|
+
// Then, before any entrypoint (or middleware) can be loaded: Next's own
|
|
888
|
+
// node-environment bootstrap. See `setupNodeEnvironment`.
|
|
889
|
+
(0, next_modules_1.setupNodeEnvironment)();
|
|
890
|
+
// And last, once the environment is ready: `register()`, before any entrypoint
|
|
891
|
+
// or middleware module is evaluated. See `registerInstrumentation`.
|
|
892
|
+
await (0, next_modules_1.registerInstrumentation)(projectDir, manifest.config.distDir);
|
|
893
|
+
return new NextjsRuntime({
|
|
894
|
+
deploymentRoot,
|
|
895
|
+
manifest,
|
|
896
|
+
bucket: process.env.CDK_NEXTJS_STATIC_ASSETS_BUCKET_NAME,
|
|
897
|
+
bucketKeyPrefix: process.env.CDK_NEXTJS_STATIC_ASSETS_KEY_PREFIX,
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* A syntactically valid `host[:port]`: a registered name or a bracketed IPv6
|
|
902
|
+
* literal, optionally with a port. Anything else cannot be the authority of a
|
|
903
|
+
* URL.
|
|
904
|
+
*/
|
|
905
|
+
const HOST_AUTHORITY = /^(?:\[[0-9A-Fa-f:.]+\]|[0-9A-Za-z._-]+)(?::\d{1,5})?$/;
|
|
906
|
+
/**
|
|
907
|
+
* The first value that is actually usable as an authority, or `undefined`.
|
|
908
|
+
*
|
|
909
|
+
* `x-forwarded-host` can arrive as a list when more than one proxy appends to
|
|
910
|
+
* it. Validating is what keeps a malformed value from making the `URL`
|
|
911
|
+
* constructor throw — which on the Lambda shells is an invocation error and a
|
|
912
|
+
* 502, not a response.
|
|
913
|
+
*/
|
|
914
|
+
function validAuthority(value) {
|
|
915
|
+
const candidate = (0, util_1.firstValue)(value)?.split(",")[0].trim();
|
|
916
|
+
return candidate && HOST_AUTHORITY.test(candidate) ? candidate : undefined;
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* `resolveRoutes` needs an absolute URL, and it becomes the origin every
|
|
920
|
+
* absolute URL the app builds starts with — `req.nextUrl.origin` in middleware,
|
|
921
|
+
* `initURL` for a route handler — so its host has to be one the client cannot
|
|
922
|
+
* pick.
|
|
923
|
+
*
|
|
924
|
+
* That is `Host` on every deployment but one: CloudFront forwards the viewer's
|
|
925
|
+
* `Host` to a container origin (`ALL_VIEWER`), an ALB passes it through, and API
|
|
926
|
+
* Gateway sets its own domain. The exception is a Lambda Function URL, where
|
|
927
|
+
* CloudFront has to send the URL's own domain as `Host` and puts the viewer's in
|
|
928
|
+
* `x-forwarded-host`; see {@link RuntimeRequest.trustForwardedHost}. Anywhere
|
|
929
|
+
* else the header is the client's, and honoring it let a request with
|
|
930
|
+
* `X-Forwarded-Host: evil.example` point a password-reset link at evil.example —
|
|
931
|
+
* and, on Global Containers, have CloudFront cache that page, since the header
|
|
932
|
+
* is not in its cache key.
|
|
933
|
+
*/
|
|
934
|
+
function absoluteUrl(request) {
|
|
935
|
+
const host = (request.trustForwardedHost
|
|
936
|
+
? validAuthority(request.headers["x-forwarded-host"])
|
|
937
|
+
: undefined) ??
|
|
938
|
+
validAuthority(request.headers.host) ??
|
|
939
|
+
"localhost";
|
|
940
|
+
// Behind CloudFront on Global Containers the container shell has already put
|
|
941
|
+
// the viewer's protocol here; see `trustCloudFrontProto` in http/node-server.ts.
|
|
942
|
+
const forwardedProto = (0, util_1.firstValue)(request.headers["x-forwarded-proto"])
|
|
943
|
+
?.split(",")[0]
|
|
944
|
+
.trim()
|
|
945
|
+
.toLowerCase();
|
|
946
|
+
// Client-supplied where no proxy overwrites it, but harmless: the host is
|
|
947
|
+
// already fixed, and anything but these two would not parse.
|
|
948
|
+
const proto = forwardedProto === "http" || forwardedProto === "https"
|
|
949
|
+
? forwardedProto
|
|
950
|
+
: request.encrypted === false
|
|
951
|
+
? "http"
|
|
952
|
+
: "https";
|
|
953
|
+
return new URL(request.url, `${proto}://${host}`);
|
|
954
|
+
}
|
|
955
|
+
function pickDefined(headers, names) {
|
|
956
|
+
const picked = {};
|
|
957
|
+
for (const name of names) {
|
|
958
|
+
if (headers[name] !== undefined) {
|
|
959
|
+
picked[name] = headers[name];
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
return picked;
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* `normalizeRepeatedSlashes` (`next/dist/shared/lib/utils.js`), applied where
|
|
966
|
+
* Next.js applies it: before routing, as a 308 to the collapsed path.
|
|
967
|
+
* `base-server.ts` does `if (urlNoQuery?.match(/(\\|\/\/)/))
|
|
968
|
+
* res.redirect(normalizeRepeatedSlashes(req.url), 308)`, so `/a//b` and `/a\b`
|
|
969
|
+
* both answer a redirect to `/a/b` rather than a 404 - and `@next/routing`'s
|
|
970
|
+
* `resolveRoutes` does not do it for us, which is how `//` reached this runtime as
|
|
971
|
+
* a 500 and `/api//json` as a 404.
|
|
972
|
+
*
|
|
973
|
+
* Encoded backslashes are left alone, as Next.js leaves them: `%5C` is a literal
|
|
974
|
+
* character in a path segment, not a separator.
|
|
975
|
+
*
|
|
976
|
+
* Returns `undefined` when there is nothing to collapse, which is the common case.
|
|
977
|
+
*/
|
|
978
|
+
function collapseRepeatedSlashes(target) {
|
|
979
|
+
const parts = target.split("?");
|
|
980
|
+
const pathname = parts[0];
|
|
981
|
+
if (!/\\|\/\//.test(pathname)) {
|
|
982
|
+
return undefined;
|
|
983
|
+
}
|
|
984
|
+
const query = parts.length > 1 ? `?${parts.slice(1).join("?")}` : "";
|
|
985
|
+
return pathname.replace(/\\/g, "/").replace(/\/\/+/g, "/") + query;
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* `resolveRoutes` hands `requestBody` to middleware, which consumes it, while the
|
|
989
|
+
* entrypoint needs the same bytes — the same problem `next-server` solves with
|
|
990
|
+
* `getCloneableBody()`. Only split when there is middleware to feed: teeing costs
|
|
991
|
+
* a buffer copy of every upload on a path that would otherwise be a straight pipe.
|
|
992
|
+
*
|
|
993
|
+
* A `tee()` buffers, for the branch that is behind, every chunk the other one
|
|
994
|
+
* has read — so a branch that is never read holds the whole body. Middleware
|
|
995
|
+
* that doesn't read the body is the common case (and its matcher may not even
|
|
996
|
+
* have run it), after which the entrypoint streaming an upload would pile all
|
|
997
|
+
* of it up in the middleware branch: the whole upload in memory, on a
|
|
998
|
+
* container that serves other requests. So once dispatch returns, the branch
|
|
999
|
+
* with no reader left is cancelled — the middleware one, or the entrypoint one
|
|
1000
|
+
* when middleware answered and `req` will never be read. Cancelling one
|
|
1001
|
+
* branch leaves the source and the other branch running.
|
|
1002
|
+
*/
|
|
1003
|
+
function splitBody(body, hasMiddleware) {
|
|
1004
|
+
const releaseNothing = () => { };
|
|
1005
|
+
if (body === undefined) {
|
|
1006
|
+
return {
|
|
1007
|
+
forDispatch: new Blob([]).stream(),
|
|
1008
|
+
releaseUnread: releaseNothing,
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
if (!hasMiddleware) {
|
|
1012
|
+
return {
|
|
1013
|
+
forRequest: body,
|
|
1014
|
+
forDispatch: new Blob([]).stream(),
|
|
1015
|
+
releaseUnread: releaseNothing,
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
if (Buffer.isBuffer(body)) {
|
|
1019
|
+
// Already fully in memory, so "teeing" is just reading it twice.
|
|
1020
|
+
return {
|
|
1021
|
+
forRequest: body,
|
|
1022
|
+
forDispatch: new Blob([body]).stream(),
|
|
1023
|
+
releaseUnread: releaseNothing,
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
const [forMiddleware, forEntrypoint] = node_stream_1.Readable.toWeb(body).tee();
|
|
1027
|
+
const forRequest = node_stream_1.Readable.fromWeb(forEntrypoint);
|
|
1028
|
+
return {
|
|
1029
|
+
forRequest,
|
|
1030
|
+
forDispatch: forMiddleware,
|
|
1031
|
+
releaseUnread(middlewareResponded) {
|
|
1032
|
+
if (middlewareResponded) {
|
|
1033
|
+
// `fromWeb` holds the branch's reader, so it is destroyed rather than
|
|
1034
|
+
// cancelled; its `_destroy` cancels the reader.
|
|
1035
|
+
forRequest.destroy();
|
|
1036
|
+
}
|
|
1037
|
+
else if (!forMiddleware.locked) {
|
|
1038
|
+
// Locked means middleware took a reader: it is reading, or has read,
|
|
1039
|
+
// the body itself, and a locked stream cannot be cancelled anyway.
|
|
1040
|
+
forMiddleware.cancel().catch(() => { });
|
|
1041
|
+
}
|
|
1042
|
+
},
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
/** `IncomingHttpHeaders` → the flat entries a `Headers` constructor accepts. */
|
|
1046
|
+
function toWebHeaders(headers) {
|
|
1047
|
+
const entries = [];
|
|
1048
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
1049
|
+
if (value === undefined) {
|
|
1050
|
+
continue;
|
|
1051
|
+
}
|
|
1052
|
+
for (const single of Array.isArray(value) ? value : [value]) {
|
|
1053
|
+
entries.push([name, single]);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
return entries;
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* `headers()` rules, the immutable `cache-control` for build assets, and anything
|
|
1060
|
+
* middleware set with `NextResponse.next({ headers })`. Applied before the
|
|
1061
|
+
* entrypoint runs so that a route can still override its own.
|
|
1062
|
+
*/
|
|
1063
|
+
function applyHeaders(res, headers) {
|
|
1064
|
+
for (const [name, value] of headers.entries()) {
|
|
1065
|
+
// `entries()` yields `set-cookie` once per cookie, so appending the whole
|
|
1066
|
+
// `getSetCookie()` array here would emit N² of them — two cookies set by
|
|
1067
|
+
// middleware arrived as ["a=1","b=2","a=1","b=2"]. Handled once, outside.
|
|
1068
|
+
if (name === "set-cookie") {
|
|
1069
|
+
continue;
|
|
1070
|
+
}
|
|
1071
|
+
res.setHeader(name, value);
|
|
1072
|
+
}
|
|
1073
|
+
for (const cookie of headers.getSetCookie()) {
|
|
1074
|
+
res.appendHeader("set-cookie", cookie);
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Mirrors Next.js's own redirect response (`base-server.ts`): the destination is
|
|
1079
|
+
* also the body, and a 308 carries a `Refresh` header because some clients and
|
|
1080
|
+
* intermediaries still do not implement 308.
|
|
1081
|
+
*/
|
|
1082
|
+
function sendRedirect(res, location, status) {
|
|
1083
|
+
res.statusCode = status;
|
|
1084
|
+
res.setHeader("Location", location);
|
|
1085
|
+
if (status === 308) {
|
|
1086
|
+
res.setHeader("Refresh", `0;url=${location}`);
|
|
1087
|
+
}
|
|
1088
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
1089
|
+
res.end(location);
|
|
1090
|
+
}
|
|
1091
|
+
/**
|
|
1092
|
+
* `next start`'s answer to a POST, PUT or DELETE for a static file: 405 with
|
|
1093
|
+
* `Allow: GET, HEAD` (`router-server.js`). It renders that through `/_error`;
|
|
1094
|
+
* this is the plain-text equivalent, since the status and `Allow` are what a
|
|
1095
|
+
* client acts on.
|
|
1096
|
+
*/
|
|
1097
|
+
function sendMethodNotAllowed(res) {
|
|
1098
|
+
res.statusCode = 405;
|
|
1099
|
+
res.setHeader("Allow", "GET, HEAD");
|
|
1100
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
1101
|
+
res.end("Method Not Allowed");
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Request headers {@link proxyExternal} does not forward. The upstream's own
|
|
1105
|
+
* `Host` must win; the rest describe our connection, not the proxied one, and
|
|
1106
|
+
* undici throws on most of them, which would answer the request with a 500.
|
|
1107
|
+
* Next.js's list for the requests it makes with undici (`ipcForbiddenHeaders`,
|
|
1108
|
+
* `next/dist/server/lib/server-ipc/utils.js`), plus `host` and `upgrade`, minus
|
|
1109
|
+
* `content-encoding`: Next strips that over a body it has already decoded, while
|
|
1110
|
+
* this one is forwarded as received, still encoded.
|
|
1111
|
+
*/
|
|
1112
|
+
const PROXY_DROPPED_HEADERS = [
|
|
1113
|
+
"host",
|
|
1114
|
+
"upgrade",
|
|
1115
|
+
"accept-encoding",
|
|
1116
|
+
"keepalive",
|
|
1117
|
+
"keep-alive",
|
|
1118
|
+
"transfer-encoding",
|
|
1119
|
+
"connection",
|
|
1120
|
+
"expect",
|
|
1121
|
+
];
|
|
1122
|
+
/**
|
|
1123
|
+
* The SigV4 signature Origin Access Control put on a request to the Function
|
|
1124
|
+
* URL, also dropped by {@link proxyExternal} when the request was IAM-verified:
|
|
1125
|
+
* forwarded, it would let the third-party origin replay the request against
|
|
1126
|
+
* the Function URL. Anywhere else these are the client's own, and are
|
|
1127
|
+
* forwarded as `next start` forwards them.
|
|
1128
|
+
*/
|
|
1129
|
+
const SIGV4_HEADERS = [
|
|
1130
|
+
"authorization",
|
|
1131
|
+
"x-amz-date",
|
|
1132
|
+
"x-amz-content-sha256",
|
|
1133
|
+
"x-amz-security-token",
|
|
1134
|
+
];
|
|
1135
|
+
/** A `next.config` rewrite whose destination is another origin. */
|
|
1136
|
+
async function proxyExternal(req, res, url, requestHeaders) {
|
|
1137
|
+
const method = req.method ?? "GET";
|
|
1138
|
+
const hasBody = method !== "GET" && method !== "HEAD";
|
|
1139
|
+
const headers = new Headers(requestHeaders);
|
|
1140
|
+
for (const name of PROXY_DROPPED_HEADERS) {
|
|
1141
|
+
headers.delete(name);
|
|
1142
|
+
}
|
|
1143
|
+
if (req.trustForwardedHost) {
|
|
1144
|
+
for (const name of SIGV4_HEADERS) {
|
|
1145
|
+
headers.delete(name);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
const upstream = await fetch(url, {
|
|
1149
|
+
method,
|
|
1150
|
+
headers,
|
|
1151
|
+
body: hasBody ? node_stream_1.Readable.toWeb(req) : undefined,
|
|
1152
|
+
redirect: "manual",
|
|
1153
|
+
...(hasBody ? { duplex: "half" } : {}),
|
|
1154
|
+
});
|
|
1155
|
+
await sendWebResponse(res, upstream);
|
|
1156
|
+
}
|
|
1157
|
+
/**
|
|
1158
|
+
* Headers describing the framing of a body that is no longer the body being
|
|
1159
|
+
* sent, dropped from every `Response` {@link sendWebResponse} forwards.
|
|
1160
|
+
*
|
|
1161
|
+
* A proxied origin's `Response` came back from `fetch`, and so, often, did
|
|
1162
|
+
* middleware's own (`return fetch(upstream)` in `proxy.ts`, since middleware
|
|
1163
|
+
* runs in process and its `fetch` is undici). undici decodes the body — gzip,
|
|
1164
|
+
* deflate, br, zstd — and leaves `content-encoding` and `content-length`
|
|
1165
|
+
* describing the encoded bytes it already threw away. Forwarding them emits
|
|
1166
|
+
* plaintext labelled `gzip` (the browser fails the whole response with
|
|
1167
|
+
* `ERR_CONTENT_DECODING_FAILED`) under a `Content-Length` that is too short,
|
|
1168
|
+
* and `shouldGzip` then declines to compress it because `content-encoding` is
|
|
1169
|
+
* already set, so nothing downstream repairs it. `transfer-encoding` is
|
|
1170
|
+
* hop-by-hop: it described the upstream connection, not this one.
|
|
1171
|
+
*
|
|
1172
|
+
* Stock Next.js drops the same three from every middleware response
|
|
1173
|
+
* (`FORBIDDEN_HEADERS` in `next/dist/server/web/sandbox/sandbox.js`), so
|
|
1174
|
+
* middleware cannot label a body it encoded itself there either.
|
|
1175
|
+
*/
|
|
1176
|
+
const STALE_FRAMING_HEADERS = new Set([
|
|
1177
|
+
"content-encoding",
|
|
1178
|
+
"content-length",
|
|
1179
|
+
"transfer-encoding",
|
|
1180
|
+
]);
|
|
1181
|
+
/** Stream a `Response` — middleware's own, or a proxied origin's — into `res`. */
|
|
1182
|
+
async function sendWebResponse(res, response) {
|
|
1183
|
+
res.statusCode = response.status;
|
|
1184
|
+
if (response.statusText) {
|
|
1185
|
+
res.statusMessage = response.statusText;
|
|
1186
|
+
}
|
|
1187
|
+
const headers = new Headers(response.headers);
|
|
1188
|
+
for (const name of STALE_FRAMING_HEADERS) {
|
|
1189
|
+
headers.delete(name);
|
|
1190
|
+
}
|
|
1191
|
+
applyHeaders(res, headers);
|
|
1192
|
+
if (!response.body) {
|
|
1193
|
+
res.end();
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
for await (const chunk of response.body) {
|
|
1197
|
+
// Leaving the loop cancels the body, which is what stops a proxied origin
|
|
1198
|
+
// from being read to the end for a client that has already gone.
|
|
1199
|
+
if (res.destroyed) {
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
// Paced by the client: without waiting for `drain`, a large proxied body
|
|
1203
|
+
// headed for a slow client is buffered whole in memory.
|
|
1204
|
+
const accepted = res.write(chunk);
|
|
1205
|
+
// Set by the sink when it compresses. Without it the chunk sits in zlib's
|
|
1206
|
+
// buffer, and a streamed body — server-sent events through a rewrite, a
|
|
1207
|
+
// middleware `ReadableStream` — reaches the client all at once, at the end.
|
|
1208
|
+
// Next.js flushes after every chunk for the same reason (`pipe-readable`).
|
|
1209
|
+
res.flush?.();
|
|
1210
|
+
if (!accepted && !(await (0, util_1.drained)(res))) {
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
res.end();
|
|
1215
|
+
}
|
|
1216
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29yZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ydW50aW1lL2NvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBMDlCQSx3REFnQkM7QUFtREQsc0RBK0JDO0FBbUJELGtDQTBEQztBQWlJRCw4QkEyQ0M7QUFyekNELHNEQUFzRDtBQUN0RDs7Ozs7Ozs7Ozs7R0FXRztBQUNILCtDQUE0QztBQUU1Qyx5Q0FBaUM7QUFDakMsNkNBQWlEO0FBQ2pELG1EQUFvRDtBQUNwRCx5Q0FLb0I7QUFDcEIsK0NBSXVCO0FBQ3ZCLDRDQUE0RTtBQUM1RSw4Q0FJeUI7QUFDekIsc0NBQXVEO0FBQ3ZELG1DQUF1RTtBQUN2RSx5Q0FNb0I7QUFDcEIsNkNBQWdEO0FBQ2hELGlEQUt3QjtBQUN4QixpREFBa0U7QUFDbEUsaURBQW9FO0FBQ3BFLGlDQUEwRTtBQStEMUUsTUFBYSxhQUFhO0lBVVk7SUFUbkIsV0FBVyxDQUFxQjtJQUNoQyxVQUFVLENBQWE7SUFDdkIsVUFBVSxDQUFvQjtJQUM5QixNQUFNLENBQXdCO0lBQy9DLDJDQUEyQztJQUMxQixjQUFjLENBQXlDO0lBQ3hFLGtFQUFrRTtJQUNqRCxZQUFZLENBQVU7SUFFdkMsWUFBb0MsT0FBNkI7UUFBN0IsWUFBTyxHQUFQLE9BQU8sQ0FBc0I7UUFDL0QsTUFBTSxFQUFFLFFBQVEsRUFBRSxjQUFjLEVBQUUsR0FBRyxPQUFPLENBQUM7UUFDN0MsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLGdDQUFrQixDQUFDLGNBQWMsRUFBRSxRQUFRLENBQUMsQ0FBQztRQUNwRSx3REFBd0Q7UUFDeEQsTUFBTSxXQUFXLEdBQUcsSUFBQSxpQ0FBa0IsRUFBQyxjQUFjLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDakUsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLHFCQUFVLENBQUM7WUFDL0IsUUFBUTtZQUNSLFdBQVcsRUFBRSxXQUFXLENBQUMsS0FBSztTQUMvQixDQUFDLENBQUM7UUFDSCww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QVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNuQixHQUFHLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDVixPQUFPO0lBQ1QsQ0FBQztJQUNELElBQUksS0FBSyxFQUFFLE1BQU0sS0FBSyxJQUFJLFFBQVEsQ0FBQyxJQUE0QyxFQUFFLENBQUM7UUFDaEYsMEVBQTBFO1FBQzFFLGlFQUFpRTtRQUNqRSxJQUFJLEdBQUcsQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUNsQixPQUFPO1FBQ1QsQ0FBQztRQUNELHlFQUF5RTtRQUN6RSx3REFBd0Q7UUFDeEQsTUFBTSxRQUFRLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNsQywwRUFBMEU7UUFDMUUsd0VBQXdFO1FBQ3hFLDRFQUE0RTtRQUM1RSwyRUFBMkU7UUFDM0UsR0FBRyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUM7UUFDZCxJQUFJLENBQUMsUUFBUSxJQUFJLENBQUMsQ0FBQyxNQUFNLElBQUEsY0FBTyxFQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUN2QyxPQUFPO1FBQ1QsQ0FBQztJQUNILENBQUM7SUFDRCxHQUFHLENBQUMsR0FBRyxFQUFFLENBQUM7QUFDWixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyogZXNsaW50LWRpc2FibGUgaW1wb3J0L25vLWV4dHJhbmVvdXMtZGVwZW5kZW5jaWVzICovXG4vKipcbiAqIFRoZSBydW50aW1lIGNvcmU6IG9uZSBgaGFuZGxlKHJlcXVlc3QsIHNpbmspYCB0aGF0IGJvdGggc2hlbGxzIHdyYXAuXG4gKlxuICogVGhlIExhbWJkYSBzaGVsbCAoYGxhbWJkYS5tdHNgKSBhbmQgdGhlIGNvbnRhaW5lciBzaGVsbCAoYHNlcnZlci5tdHNgKSBkb1xuICogbm90aGluZyBidXQgdHVybiB0aGVpciBpbnB1dCBpbnRvIGEge0BsaW5rIFJ1bnRpbWVSZXF1ZXN0fSBhbmQgdGhlaXIgb3V0cHV0IGludG9cbiAqIGEge0BsaW5rIFJlc3BvbnNlU2lua30uIEV2ZXJ5dGhpbmcgZWxzZSDigJQgc3ludGhlc2l6aW5nIGByZXFgL2ByZXNgLCBkaXNwYXRjaCxcbiAqIG1pZGRsZXdhcmUsIGVudHJ5cG9pbnQgaW52b2NhdGlvbiwgc3RhdGljIGZpbGVzLCBpbWFnZSBvcHRpbWl6YXRpb24sIGd6aXAsXG4gKiBgd2FpdFVudGlsYCDigJQgaGFwcGVucyBoZXJlLCBzbyB0aGUgY29udGFpbmVyIGUyZSBzdWl0ZSBleGVyY2lzZXMgdGhlIHNhbWUgY29kZVxuICogTGFtYmRhIHJ1bnMuIFRoYXQgaXMgZGVsaWJlcmF0ZTogdGhlIGNvbnRhaW5lciBwYXRoIHVzZWQgdG8gZ2V0IHJlYWxcbiAqIGBub2RlOmh0dHBgIG9iamVjdHMgZnJvbSBMYW1iZGEgV2ViIEFkYXB0ZXIsIHdoaWNoIG1lYW50IGNvbnRhaW5lciB0ZXN0cyBwcm92ZWRcbiAqIG5vdGhpbmcgYWJvdXQgTGFtYmRhLlxuICovXG5pbXBvcnQgeyByZWFkRmlsZSB9IGZyb20gXCJub2RlOmZzL3Byb21pc2VzXCI7XG5pbXBvcnQgdHlwZSB7IEluY29taW5nSHR0cEhlYWRlcnMgfSBmcm9tIFwibm9kZTpodHRwXCI7XG5pbXBvcnQgeyBqb2luIH0gZnJvbSBcIm5vZGU6cGF0aFwiO1xuaW1wb3J0IHsgUmVhZGFibGUsIFdyaXRhYmxlIH0gZnJvbSBcIm5vZGU6c3RyZWFtXCI7XG5pbXBvcnQgeyBjYXNlQ2Fub25pY2FsUGF0aCB9IGZyb20gXCIuL2Nhc2UtcmVkaXJlY3RcIjtcbmltcG9ydCB7XG4gIERpc3BhdGNoZXIsXG4gIG91dE9mQmFuZFJvdXRlUGFyYW1zLFxuICBTdGF0dXNUYXJnZXQsXG4gIHN0YXR1c1RhcmdldHMsXG59IGZyb20gXCIuL2Rpc3BhdGNoXCI7XG5pbXBvcnQge1xuICBFbnRyeXBvaW50SGFuZGxlcixcbiAgRW50cnlwb2ludFJlZ2lzdHJ5LFxuICBSb3V0ZUluT3RoZXJHcm91cEVycm9yLFxufSBmcm9tIFwiLi9lbnRyeXBvaW50c1wiO1xuaW1wb3J0IHsgU2hpbUluY29taW5nTWVzc2FnZSwgdG9JbmNvbWluZ0h0dHBIZWFkZXJzIH0gZnJvbSBcIi4vaHR0cC9yZXF1ZXN0XCI7XG5pbXBvcnQge1xuICBhc1NlcnZlclJlc3BvbnNlLFxuICBSZXNwb25zZUhlYWQsXG4gIFNoaW1TZXJ2ZXJSZXNwb25zZSxcbn0gZnJvbSBcIi4vaHR0cC9yZXNwb25zZVwiO1xuaW1wb3J0IHsgcGlwZVRvU2luaywgUmVzcG9uc2VTaW5rIH0gZnJvbSBcIi4vaHR0cC9zaW5rXCI7XG5pbXBvcnQgeyBJbnRlcm5hbEltYWdlUmVzcG9uc2UsIFJ1bnRpbWVJbWFnZU9wdGltaXplciB9IGZyb20gXCIuL2ltYWdlXCI7XG5pbXBvcnQge1xuICBBZGFwdGVyTWFuaWZlc3QsXG4gIGRlcGxveWVkTWFuaWZlc3RQYXRoLFxuICBGVU5DVElPTl9HUk9VUF9FTlZfVkFSLFxuICBNQU5JRkVTVF9GSUxFX05BTUUsXG4gIFJFVkFMSURBVEVEX1BBR0VfSE9PSyxcbn0gZnJvbSBcIi4vbWFuaWZlc3RcIjtcbmltcG9ydCB7IE1pZGRsZXdhcmVSdW5uZXIgfSBmcm9tIFwiLi9taWRkbGV3YXJlXCI7XG5pbXBvcnQge1xuICBsb2FkRW52RmlsZXMsXG4gIHJlZ2lzdGVySW5zdHJ1bWVudGF0aW9uLFxuICBzZXR1cE5vZGVFbnZpcm9ubWVudCxcbiAgdXNlTmV4dEZyb20sXG59IGZyb20gXCIuL25leHQtbW9kdWxlc1wiO1xuaW1wb3J0IHsgcHVibGljRGlyS2V5LCByZXNvbHZlUHVibGljRmlsZXMgfSBmcm9tIFwiLi9wdWJsaWMtZmlsZXNcIjtcbmltcG9ydCB7IHNlcnZlUzNQdWJsaWNGaWxlLCBzZXJ2ZVN0YXRpY0ZpbGUgfSBmcm9tIFwiLi9zdGF0aWMtZmlsZXNcIjtcbmltcG9ydCB7IGRyYWluZWQsIGZpcnN0VmFsdWUsIHRvU2VhcmNoLCB3aXRob3V0UGF0aFByZWZpeCB9IGZyb20gXCIuL3V0aWxcIjtcblxuLyoqIE9uZSByZXF1ZXN0LCBub3JtYWxpemVkIGJ5IGEgc2hlbGwuICovXG5leHBvcnQgaW50ZXJmYWNlIFJ1bnRpbWVSZXF1ZXN0IHtcbiAgcmVhZG9ubHkgbWV0aG9kOiBzdHJpbmc7XG4gIC8qKiBPcmlnaW4tZm9ybSB0YXJnZXQgZXhhY3RseSBhcyByZWNlaXZlZDogcGF0aCArIHF1ZXJ5LCBuZXZlciBhYnNvbHV0ZS4gKi9cbiAgcmVhZG9ubHkgdXJsOiBzdHJpbmc7XG4gIHJlYWRvbmx5IGhlYWRlcnM6IEluY29taW5nSHR0cEhlYWRlcnM7XG4gIHJlYWRvbmx5IGJvZHk/OiBSZWFkYWJsZSB8IEJ1ZmZlcjtcbiAgcmVhZG9ubHkgcmVtb3RlQWRkcmVzcz86IHN0cmluZztcbiAgLyoqIGBmYWxzZWAgYmVoaW5kIGEgVExTLXRlcm1pbmF0aW5nIEFMQi4gT25seSBhZmZlY3RzIHRoZSBzeW50aGVzaXplZCBzb2NrZXQuICovXG4gIHJlYWRvbmx5IGVuY3J5cHRlZD86IGJvb2xlYW47XG4gIC8qKlxuICAgKiBBYm9ydGVkIHdoZW4gdGhlIGNsaWVudCBkaXNjb25uZWN0cy4gV2lyaW5nIGl0IHRvIGByZXMuZGVzdHJveSgpYCBpcyB3aGF0XG4gICAqIG1ha2VzIGByZXF1ZXN0LnNpZ25hbC5vbmFib3J0YCBmaXJlIGluc2lkZSByb3V0ZSBoYW5kbGVycy5cbiAgICovXG4gIHJlYWRvbmx5IHNpZ25hbD86IEFib3J0U2lnbmFsO1xuICAvKipcbiAgICogV2hldGhlciBgeC1mb3J3YXJkZWQtaG9zdGAgbmFtZXMgdGhlIGhvc3QgdGhlIGNsaWVudCBhc2tlZCBmb3IuIE9ubHkgdGhlXG4gICAqIHNoZWxsIGNhbiBrbm93OiBpdCBpcyBgdHJ1ZWAgZm9yIGEgRnVuY3Rpb24gVVJMIGV2ZW50IHdob3NlIFNpZ1Y0IExhbWJkYVxuICAgKiB2ZXJpZmllZC4gVGhhdCBpcyBhbnkgSUFNLWF1dGhvcml6ZWQgaW52b2tlciDigJQgaW4gcHJhY3RpY2UgQ2xvdWRGcm9udCdzXG4gICAqIE9yaWdpbiBBY2Nlc3MgQ29udHJvbCwgd2hvc2Ugdmlld2VyLXJlcXVlc3QgZnVuY3Rpb24gb3ZlcndyaXRlcyB0aGUgaGVhZGVyXG4gICAqIHdpdGggdGhlIHZpZXdlcidzIGBIb3N0YCAoQ2xvdWRGcm9udCBoYXMgdG8gcmVwbGFjZSBgSG9zdGAgaXRzZWxmIHdpdGggdGhlXG4gICAqIEZ1bmN0aW9uIFVSTCdzIGRvbWFpbiksIHBsdXMgcHJpbmNpcGFscyBncmFudGVkIGBsYW1iZGE6SW52b2tlRnVuY3Rpb25VcmxgLFxuICAgKiB3aG8gY291bGQgcmVwbGFjZSB0aGUgZnVuY3Rpb24ncyBjb2RlIGFueXdheS4gSXQgaXMgYWxzbyB3aGF0XG4gICAqIGBwcm94eUV4dGVybmFsYCBkcm9wcyBPQUMncyBzaWduYXR1cmUgb24uIEV2ZXJ5d2hlcmUgZWxzZSB0aGUgaGVhZGVyIGlzXG4gICAqIHdoYXRldmVyIHRoZSBjbGllbnQgc2VudCwgYW5kIGl0IGlzXG4gICAqIGlnbm9yZWQsIGFzIGBuZXh0IHN0YXJ0YCBpZ25vcmVzIGl0IHdpdGhvdXQgYGV4cGVyaW1lbnRhbC50cnVzdEhvc3RIZWFkZXJgLlxuICAgKi9cbiAgcmVhZG9ubHkgdHJ1c3RGb3J3YXJkZWRIb3N0PzogYm9vbGVhbjtcbiAgLyoqXG4gICAqIFNldCBieSB7QGxpbmsgTmV4dGpzUnVudGltZS5oYW5kbGVJbnRlcm5hbGx5fSBmb3IgdGhlIGluLXByb2Nlc3MgcmVxdWVzdHMgaXRcbiAgICogbWFrZXM6IGNhbGxlZCB3aXRoIHRoZSBgUm91dGVJbk90aGVyR3JvdXBFcnJvcmAgdGhlIHJvdXRlIHRocmV3LCBpbnN0ZWFkIG9mXG4gICAqIGxvZ2dpbmcgdGhlIG1pc3JvdXRlZC1yZXF1ZXN0IHdhcm5pbmcsIHdoaWNoIGRlc2NyaWJlcyBhIENsb3VkRnJvbnQgb3IgQVBJXG4gICAqIEdhdGV3YXkgcHJvYmxlbSB0aGlzIGlzIG5vdC5cbiAgICovXG4gIHJlYWRvbmx5IG9uUm91dGVJbk90aGVyR3JvdXA/OiAoZXJyb3I6IFJvdXRlSW5PdGhlckdyb3VwRXJyb3IpID0+IHZvaWQ7XG59XG5cbi8qKlxuICogYFJldmFsaWRhdGVGbmAncyBhcmd1bWVudCAoYG5leHQvZGlzdC9zZXJ2ZXIvbGliL3JvdXRlci11dGlscy9yb3V0ZXItc2VydmVyLWNvbnRleHRgKSxcbiAqIHJlc3RhdGVkIGJlY2F1c2UgdGhhdCBtb2R1bGUgaXMgbm90IHBhcnQgb2YgYG5leHRgJ3MgcHVibGlzaGVkIHR5cGUgc3VyZmFjZS5cbiAqL1xuaW50ZXJmYWNlIFJldmFsaWRhdGVDb25maWcge1xuICByZWFkb25seSB1cmxQYXRoOiBzdHJpbmc7XG4gIHJlYWRvbmx5IGhlYWRlcnM6IHsgW2tleTogc3RyaW5nXTogc3RyaW5nIHwgc3RyaW5nW10gfTtcbiAgcmVhZG9ubHkgb3B0czogeyB1bnN0YWJsZV9vbmx5R2VuZXJhdGVkPzogYm9vbGVhbiB9O1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIE5leHRqc1J1bnRpbWVPcHRpb25zIHtcbiAgLyoqXG4gICAqIEFic29sdXRlIHBhdGggdG8gdGhlIGRlcGxveW1lbnQgcm9vdCDigJQgdGhlIHN0YWdlZCB0cmVlIGV2ZXJ5IG1hbmlmZXN0IGtleVxuICAgKiByZXNvbHZlcyBhZ2FpbnN0LiBgTEFNQkRBX1RBU0tfUk9PVGAgZm9yIEZ1bmN0aW9ucywgdGhlIGltYWdlIGBXT1JLRElSYCBmb3JcbiAgICogQ29udGFpbmVyczsgYm90aCBzaGVsbHMgZGVyaXZlIGl0IGZyb20gdGhlaXIgb3duIGxvY2F0aW9uIGluc3RlYWQuXG4gICAqL1xuICByZWFkb25seSBkZXBsb3ltZW50Um9vdDogc3RyaW5nO1xuICByZWFkb25seSBtYW5pZmVzdDogQWRhcHRlck1hbmlmZXN0O1xuICAvKiogYENES19ORVhUSlNfU1RBVElDX0FTU0VUU19CVUNLRVRfTkFNRWAuICovXG4gIHJlYWRvbmx5IGJ1Y2tldD86IHN0cmluZztcbiAgLyoqIGBDREtfTkVYVEpTX1NUQVRJQ19BU1NFVFNfS0VZX1BSRUZJWGAuICovXG4gIHJlYWRvbmx5IGJ1Y2tldEtleVByZWZpeD86IHN0cmluZztcbn1cblxuZXhwb3J0IGNsYXNzIE5leHRqc1J1bnRpbWUge1xuICBwcml2YXRlIHJlYWRvbmx5IGVudHJ5cG9pbnRzOiBFbnRyeXBvaW50UmVnaXN0cnk7XG4gIHByaXZhdGUgcmVhZG9ubHkgZGlzcGF0Y2hlcjogRGlzcGF0Y2hlcjtcbiAgcHJpdmF0ZSByZWFkb25seSBtaWRkbGV3YXJlPzogTWlkZGxld2FyZVJ1bm5lcjtcbiAgcHJpdmF0ZSByZWFkb25seSBpbWFnZXM6IFJ1bnRpbWVJbWFnZU9wdGltaXplcjtcbiAgLyoqIFRoZSA1MDAgbGFkZGVyOyBzZWUgYHN0YXR1c1RhcmdldHNgLiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IGVycm9yVGFyZ2V0Rm9yOiAodXJsOiBVUkwgfCB1bmRlZmluZWQpID0+IFN0YXR1c1RhcmdldDtcbiAgLyoqIFRoZSBhc3NldHMgYnVja2V0LCB3aGVuIGBwdWJsaWMvYCBmaWxlcyBhcmUgc2VydmVkIGZyb20gaXQuICovXG4gIHByaXZhdGUgcmVhZG9ubHkgcHVibGljQnVja2V0Pzogc3RyaW5nO1xuXG4gIHB1YmxpYyBjb25zdHJ1Y3Rvcihwcml2YXRlIHJlYWRvbmx5IG9wdGlvbnM6IE5leHRqc1J1bnRpbWVPcHRpb25zKSB7XG4gICAgY29uc3QgeyBtYW5pZmVzdCwgZGVwbG95bWVudFJvb3QgfSA9IG9wdGlvbnM7XG4gICAgdGhpcy5lbnRyeXBvaW50cyA9IG5ldyBFbnRyeXBvaW50UmVnaXN0cnkoZGVwbG95bWVudFJvb3QsIG1hbmlmZXN0KTtcbiAgICAvLyBMaXN0ZWQgb25jZSwgYXQgY29sZCBzdGFydDsgc2VlIGByZXNvbHZlUHVibGljRmlsZXNgLlxuICAgIGNvbnN0IHB1YmxpY0ZpbGVzID0gcmVzb2x2ZVB1YmxpY0ZpbGVzKGRlcGxveW1lbnRSb290LCBtYW5pZmVzdCk7XG4gICAgdGhpcy5kaXNwYXRjaGVyID0gbmV3IERpc3BhdGNoZXIoe1xuICAgICAgbWFuaWZlc3QsXG4gICAgICBwdWJsaWNGaWxlczogcHVibGljRmlsZXMuZmlsZXMsXG4gICAgfSk7XG4gICAgLy8gT24gZGlzayBvdGhlcndpc2UsIHdpdGggYC5uZXh0L3N0YXRpY2AuXG4gICAgdGhpcy5wdWJsaWNCdWNrZXQgPSBwdWJsaWNGaWxlcy5pblMzID8gb3B0aW9ucy5idWNrZXQgOiB1bmRlZmluZWQ7XG4gICAgdGhpcy5lcnJvclRhcmdldEZvciA9IHN0YXR1c1RhcmdldHMobWFuaWZlc3QsIDUwMCk7XG4gICAgdGhpcy5pbWFnZXMgPSBuZXcgUnVudGltZUltYWdlT3B0aW1pemVyKHtcbiAgICAgIGRlcGxveW1lbnRSb290LFxuICAgICAgbWFuaWZlc3QsXG4gICAgICBidWNrZXQ6IHRoaXMucHVibGljQnVja2V0ID8/IFwiXCIsXG4gICAgICBidWNrZXRLZXlQcmVmaXg6IG9wdGlvbnMuYnVja2V0S2V5UHJlZml4ID8/IFwiXCIsXG4gICAgICBmZXRjaEludGVybmFsOiAoaHJlZiwgcmVxLCBtYXhpbXVtQm9keSkgPT5cbiAgICAgICAgdGhpcy5mZXRjaEludGVybmFsKGhyZWYsIHJlcSwgbWF4aW11bUJvZHkpLFxuICAgIH0pO1xuICAgIC8vIFRoZSBydW5uZXIgaXMgc2hhcmVkIOKAlCBpdCBtZW1vaXplcyB0aGUgbG9hZGVkIG1pZGRsZXdhcmUgbW9kdWxlIOKAlCB3aGlsZSB0aGVcbiAgICAvLyBgaW52b2tlTWlkZGxld2FyZWAgY2FsbGJhY2sgaXQgcHJvZHVjZXMgaXMgcGVyIHJlcXVlc3QuXG4gICAgdGhpcy5taWRkbGV3YXJlID0gbWFuaWZlc3QubWlkZGxld2FyZVxuICAgICAgPyBuZXcgTWlkZGxld2FyZVJ1bm5lcih7XG4gICAgICAgICAgbWlkZGxld2FyZTogbWFuaWZlc3QubWlkZGxld2FyZSxcbiAgICAgICAgICByb290OiBkZXBsb3ltZW50Um9vdCxcbiAgICAgICAgfSlcbiAgICAgIDogdW5kZWZpbmVkO1xuICB9XG5cbiAgcHVibGljIGdldCBtYW5pZmVzdCgpOiBBZGFwdGVyTWFuaWZlc3Qge1xuICAgIHJldHVybiB0aGlzLm9wdGlvbnMubWFuaWZlc3Q7XG4gIH1cblxuICBwdWJsaWMgYXN5bmMgaGFuZGxlKFxuICAgIHJlcXVlc3Q6IFJ1bnRpbWVSZXF1ZXN0LFxuICAgIHNpbms6IFJlc3BvbnNlU2luayxcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgeyBtYW5pZmVzdCB9ID0gdGhpcy5vcHRpb25zO1xuICAgIGNvbnN0IHBlbmRpbmc6IEFycmF5PFByb21pc2U8dW5rbm93bj4+ID0gW107XG4gICAgY29uc3Qgd2FpdFVudGlsID0gKHByb21pc2U6IFByb21pc2U8dW5rbm93bj4pID0+IHtcbiAgICAgIHBlbmRpbmcucHVzaChwcm9taXNlKTtcbiAgICB9O1xuXG4gICAgY29uc3QgYm9keSA9IHNwbGl0Qm9keShyZXF1ZXN0LmJvZHksIHRoaXMubWlkZGxld2FyZSAhPT0gdW5kZWZpbmVkKTtcbiAgICBjb25zdCByZXEgPSBuZXcgU2hpbUluY29taW5nTWVzc2FnZSh7XG4gICAgICBtZXRob2Q6IHJlcXVlc3QubWV0aG9kLFxuICAgICAgdXJsOiByZXF1ZXN0LnVybCxcbiAgICAgIGhlYWRlcnM6IHdpdGhvdXRJbnRlcm5hbEhlYWRlcnMoXG4gICAgICAgIHJlcXVlc3QuaGVhZGVycyxcbiAgICAgICAgcmVxdWVzdC51cmwsXG4gICAgICAgIG1hbmlmZXN0LmNvbmZpZy5iYXNlUGF0aCxcbiAgICAgICksXG4gICAgICBib2R5OiBib2R5LmZvclJlcXVlc3QsXG4gICAgICByZW1vdGVBZGRyZXNzOiByZXF1ZXN0LnJlbW90ZUFkZHJlc3MsXG4gICAgICBlbmNyeXB0ZWQ6IHJlcXVlc3QuZW5jcnlwdGVkLFxuICAgICAgdHJ1c3RGb3J3YXJkZWRIb3N0OiByZXF1ZXN0LnRydXN0Rm9yd2FyZGVkSG9zdCxcbiAgICB9KTtcbiAgICBjb25zdCByZXMgPSBuZXcgU2hpbVNlcnZlclJlc3BvbnNlKCk7XG4gICAgLy8gTmV4dC5qcyByZWFkcyBgcmVzLnJlcWAgaW4gYSBmZXcgZXJyb3IgcGF0aHMuXG4gICAgcmVzLnJlcSA9IHJlcTtcbiAgICByZXF1ZXN0LnNpZ25hbD8uYWRkRXZlbnRMaXN0ZW5lcihcImFib3J0XCIsICgpID0+IHJlcy5kZXN0cm95KCksIHtcbiAgICAgIG9uY2U6IHRydWUsXG4gICAgfSk7XG5cbiAgICAvLyBUaGUgcmVqZWN0aW9uIGhhbmRsZXIgaXMgYXR0YWNoZWQgaGVyZSByYXRoZXIgdGhhbiBhdCB0aGUgYGF3YWl0YCBiZWxvdy5cbiAgICAvLyBgcm91dGUoKWAgYXdhaXRzIHJlYWwgSS9PLCBzbyBhIHN0cmVhbSB0aGF0IGJyZWFrcyB3aGlsZSBpdCBydW5zIHdvdWxkXG4gICAgLy8gbGVhdmUgdGhpcyBwcm9taXNlIHJlamVjdGVkIHdpdGggbm8gaGFuZGxlciBhdHRhY2hlZCBmb3IgYSBmdWxsIGV2ZW50LWxvb3BcbiAgICAvLyB0dXJuIOKAlCBhbmQgTm9kZSAyNCBkZWZhdWx0cyB0byBgLS11bmhhbmRsZWQtcmVqZWN0aW9ucz10aHJvd2AsIHdoaWNoIG9uXG4gICAgLy8gdGhlIGNvbnRhaW5lciBzaGVsbCB0YWtlcyB0aGUgd2hvbGUgdGFzayBkb3duIGFuZCBhYm9ydHMgZXZlcnkgb3RoZXJcbiAgICAvLyBpbi1mbGlnaHQgcmVxdWVzdCBvbiBpdC4gQSBtaWQtc3RyZWFtIEVQSVBFIGZyb20gb25lIGNsaWVudCBkaXNjb25uZWN0IGlzXG4gICAgLy8gbm90IGdyb3VuZHMgZm9yIHRoYXQuXG4gICAgLy9cbiAgICAvLyBUaGUgZXJyb3IgaXMgbG9nZ2VkLCBub3QgcmV0aHJvd246IHRoZSBzdHJlYW0gYnJva2UgYWZ0ZXIgKG9yIHdoaWxlKSB0aGVcbiAgICAvLyBoZWFkIHdlbnQgb3V0IOKAlCBhIGNsaWVudCBkaXNjb25uZWN0LCBvciBgc2VuZEVycm9yYCBkZXN0cm95aW5nIGFcbiAgICAvLyBoYWxmLXdyaXR0ZW4gcmVzcG9uc2Ug4oCUIGFuZCBuZWl0aGVyIGlzIGFuIGludm9jYXRpb24gZmFpbHVyZS4gUmV0aHJvd2luZ1xuICAgIC8vIHdvdWxkIG1ha2UgTGFtYmRhIHJldHJ5IGEgcmVxdWVzdCB0aGUgY2xpZW50IGFscmVhZHkgYWJhbmRvbmVkLlxuICAgIGNvbnN0IGZpbmlzaGVkID0gcGlwZVRvU2luayhyZXEsIHJlcywgc2luaywge1xuICAgICAgY29tcHJlc3M6IG1hbmlmZXN0LmNvbmZpZy5jb21wcmVzcyxcbiAgICB9KS5jYXRjaCgoZXJyb3I6IHVua25vd24pID0+IHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoXCJUaGUgcmVzcG9uc2Ugc3RyZWFtIGRpZCBub3QgY29tcGxldGU6XCIsIGVycm9yKTtcbiAgICB9KTtcblxuICAgIC8vIEZvciB0aGUgZXJyb3IgcGFnZSdzIGxvY2FsZTsgdW5zZXQgd2hlbiB0aGUgdGhyb3cgaXMgYGFic29sdXRlVXJsYCdzLlxuICAgIGxldCB1cmw6IFVSTCB8IHVuZGVmaW5lZDtcbiAgICB0cnkge1xuICAgICAgLy8gQmVmb3JlIGFueXRoaW5nIHBhcnNlcyB0aGUgdGFyZ2V0LCBpbmNsdWRpbmcgYGFic29sdXRlVXJsYCAtIGBuZXcgVVJMYFxuICAgICAgLy8gcmVhZHMgYSBsZWFkaW5nIGAvL2AgYXMgcHJvdG9jb2wtcmVsYXRpdmUgYW5kIHdvdWxkIHRha2UgdGhlIGZpcnN0IHBhdGhcbiAgICAgIC8vIHNlZ21lbnQgZm9yIHRoZSBob3N0LlxuICAgICAgY29uc3QgY29sbGFwc2VkID0gY29sbGFwc2VSZXBlYXRlZFNsYXNoZXMocmVxdWVzdC51cmwpO1xuICAgICAgaWYgKGNvbGxhcHNlZCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHNlbmRSZWRpcmVjdChyZXMsIGNvbGxhcHNlZCwgMzA4KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIEluc2lkZSB0aGUgdHJ5OiBgYWJzb2x1dGVVcmxgIGJ1aWxkcyBhIGBVUkxgIG91dCBvZiB0aGUgcmVxdWVzdCBsaW5lXG4gICAgICAgIC8vIGFuZCB0aGUgZm9yd2FyZGVkIGhlYWRlcnMsIGFuZCBhIHRocm93IGhlcmUgaGFzIHRvIHJlYWNoIHRoZSBlcnJvclxuICAgICAgICAvLyBsYWRkZXIgbGlrZSBhbnkgb3RoZXIuIE91dHNpZGUgaXQsIG9uIHRoZSBMYW1iZGEgc2hlbGxzIOKAlCB3aG9zZVxuICAgICAgICAvLyBoYW5kbGVycyB3cmFwIG5vdGhpbmcg4oCUIHRoZSBzYW1lIHRocm93IGlzIGFuIGludm9jYXRpb24gZXJyb3IgYW5kIGFcbiAgICAgICAgLy8gNTAyLlxuICAgICAgICB1cmwgPSBhYnNvbHV0ZVVybChyZXF1ZXN0KTtcbiAgICAgICAgYXdhaXQgdGhpcy5yb3V0ZShyZXEsIHJlcywgdXJsLCBib2R5LCB3YWl0VW50aWwsIHJlcXVlc3QpO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBhd2FpdCB0aGlzLnNlbmRFcnJvcihyZXEsIHJlcywgd2FpdFVudGlsLCBlcnJvciwgdXJsKTtcbiAgICB9XG5cbiAgICBhd2FpdCBmaW5pc2hlZDtcblxuICAgIC8vIExhbWJkYSBmcmVlemVzIHRoZSBleGVjdXRpb24gZW52aXJvbm1lbnQgdGhlIG1vbWVudCB0aGUgaGFuZGxlciByZXNvbHZlcyDigJRcbiAgICAvLyB0aGVyZSBpcyBubyBwb3N0LXJlc3BvbnNlIGtlZXBhbGl2ZSDigJQgc28gYmFja2dyb3VuZCB3b3JrIHJlZ2lzdGVyZWQgd2l0aFxuICAgIC8vIGB3YWl0VW50aWxgIChub3RhYmx5IElTUiByZXZhbGlkYXRpb24pIGhhcyB0byBiZSBhd2FpdGVkIGhlcmUuIEFmdGVyIHRoZVxuICAgIC8vIHJlc3BvbnNlIHN0cmVhbSBoYXMgY2xvc2VkLCBzbyBjbGllbnQgbGF0ZW5jeSBpcyB1bmFmZmVjdGVkOyBiaWxsZWRcbiAgICAvLyBkdXJhdGlvbiBleHRlbmRzLCB3aGljaCBpcyB0aGUgY29ycmVjdCB0cmFkZSBhZ2FpbnN0IGEgcmV2YWxpZGF0aW9uIHRoYXRcbiAgICAvLyBuZXZlciBjb21wbGV0ZXMuXG4gICAgLy9cbiAgICAvLyBEcmFpbmVkIHVudGlsIG5vdGhpbmcgbmV3IGFycml2ZXMsIG5vdCBhd2FpdGVkIG9uY2U6IGB3YWl0VW50aWxgIGtlZXBzXG4gICAgLy8gYmVpbmcgY2FsbGVkIGFmdGVyIHRoZSByZXNwb25zZSDigJQgTmV4dCdzIGBBZnRlckNvbnRleHRgIHJlZ2lzdGVycyBpdHNcbiAgICAvLyBjYWxsYmFjayBxdWV1ZSBvbiB0aGUgZmlyc3QgYGFmdGVyKClgLCBhbmQgYW4gYGFmdGVyKHByb21pc2UpYCBtYWRlIGZyb21cbiAgICAvLyBpbnNpZGUgYSBydW5uaW5nIGNhbGxiYWNrLCBvciBhIGJhY2tncm91bmQgcmVnZW5lcmF0aW9uIHRoYXQgaXRzZWxmIGNhbGxzXG4gICAgLy8gYGFmdGVyKClgLCByZWdpc3RlcnMgbW9yZS4gQSBzaW5nbGUgYGFsbFNldHRsZWRgIHNuYXBzaG90cyB0aGUgYXJyYXkgYW5kXG4gICAgLy8gbGV0cyBMYW1iZGEgZnJlZXplIG9uIHRoYXQgbGF0ZSB3b3JrLlxuICAgIGxldCBzZXR0bGVkQ291bnQgPSAwO1xuICAgIHdoaWxlIChzZXR0bGVkQ291bnQgPCBwZW5kaW5nLmxlbmd0aCkge1xuICAgICAgY29uc3QgYmF0Y2ggPSBwZW5kaW5nLnNsaWNlKHNldHRsZWRDb3VudCk7XG4gICAgICBzZXR0bGVkQ291bnQgPSBwZW5kaW5nLmxlbmd0aDtcbiAgICAgIGNvbnN0IHNldHRsZWQgPSBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoYmF0Y2gpO1xuICAgICAgZm9yIChjb25zdCByZXN1bHQgb2Ygc2V0dGxlZCkge1xuICAgICAgICBpZiAocmVzdWx0LnN0YXR1cyA9PT0gXCJyZWplY3RlZFwiKSB7XG4gICAgICAgICAgY29uc29sZS5lcnJvcihcIkEgd2FpdFVudGlsKCkgcHJvbWlzZSByZWplY3RlZDpcIiwgcmVzdWx0LnJlYXNvbik7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHJvdXRlKFxuICAgIHJlcTogU2hpbUluY29taW5nTWVzc2FnZSxcbiAgICByZXM6IFNoaW1TZXJ2ZXJSZXNwb25zZSxcbiAgICB1cmw6IFVSTCxcbiAgICBib2R5OiBTcGxpdEJvZHksXG4gICAgd2FpdFVudGlsOiAocHJvbWlzZTogUHJvbWlzZTx1bmtub3duPikgPT4gdm9pZCxcbiAgICByZXF1ZXN0OiBSdW50aW1lUmVxdWVzdCxcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5kaXNwYXRjaGVyLmRpc3BhdGNoKHtcbiAgICAgIG1ldGhvZDogcmVxLm1ldGhvZCA/PyBcIkdFVFwiLFxuICAgICAgdXJsLFxuICAgICAgaGVhZGVyczogbmV3IEhlYWRlcnModG9XZWJIZWFkZXJzKHJlcS5oZWFkZXJzKSksXG4gICAgICBib2R5OiBib2R5LmZvckRpc3BhdGNoLFxuICAgICAgaW52b2tlTWlkZGxld2FyZTogdGhpcy5taWRkbGV3YXJlPy5pbnZva2VyRm9yKHtcbiAgICAgICAgd2FpdFVudGlsLFxuICAgICAgICBzaWduYWw6IHJlcXVlc3Quc2lnbmFsLFxuICAgICAgfSksXG4gICAgfSk7XG4gICAgYm9keS5yZWxlYXNlVW5yZWFkKHJlc3VsdC5raW5kID09PSBcIm1pZGRsZXdhcmUtcmVzcG9uZGVkXCIpO1xuXG4gICAgYXBwbHlIZWFkZXJzKHJlcywgcmVzdWx0LnJlc3BvbnNlSGVhZGVycyk7XG4gICAgaWYgKHJlc3VsdC5zdGF0dXMgIT09IHVuZGVmaW5lZCkge1xuICAgICAgcmVzLnN0YXR1c0NvZGUgPSByZXN1bHQuc3RhdHVzO1xuICAgIH1cblxuICAgIHN3aXRjaCAocmVzdWx0LmtpbmQpIHtcbiAgICAgIGNhc2UgXCJlbnRyeXBvaW50XCI6IHtcbiAgICAgICAgbGV0IGhhbmRsZXI6IEVudHJ5cG9pbnRIYW5kbGVyO1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGhhbmRsZXIgPSBhd2FpdCB0aGlzLmVudHJ5cG9pbnRzLmxvYWQocmVzdWx0LmVudHJ5cG9pbnQpO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIGlmICghKGVycm9yIGluc3RhbmNlb2YgUm91dGVJbk90aGVyR3JvdXBFcnJvcikpIHRocm93IGVycm9yO1xuICAgICAgICAgIC8vIEEgVVJMIHRoZSBlZGdlIHNlbnQgaGVyZSBvbmx5IGJlY2F1c2Ugb2YgaXRzIGNhc2UgZ29lcyB0byBpdHNcbiAgICAgICAgICAvLyBjYW5vbmljYWwgc3BlbGxpbmcsIHdoaWNoIHRoZSBlZGdlIHJvdXRlcyB0byB0aGUgb3duaW5nIGdyb3VwLlxuICAgICAgICAgIGNvbnN0IGNhbm9uaWNhbCA9IGNhc2VDYW5vbmljYWxQYXRoKFxuICAgICAgICAgICAgdXJsLnBhdGhuYW1lLFxuICAgICAgICAgICAgcmVzdWx0LnJlc29sdmVkUGF0aG5hbWUsXG4gICAgICAgICAgICB0aGlzLm9wdGlvbnMubWFuaWZlc3QsXG4gICAgICAgICAgKTtcbiAgICAgICAgICBpZiAoY2Fub25pY2FsKSB7XG4gICAgICAgICAgICBzZW5kUmVkaXJlY3QocmVzLCBgJHtjYW5vbmljYWx9JHt1cmwuc2VhcmNofWAsIDMwOCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChyZXF1ZXN0Lm9uUm91dGVJbk90aGVyR3JvdXApIHtcbiAgICAgICAgICAgIC8vIEV4cGxhaW5lZCBieSB0aGUgY2FsbGVyIG9mIGBoYW5kbGVJbnRlcm5hbGx5YCwgd2hpY2gga25vd3Mgd2h5IGl0XG4gICAgICAgICAgICAvLyBoYXBwZW5lZC5cbiAgICAgICAgICAgIHJlcXVlc3Qub25Sb3V0ZUluT3RoZXJHcm91cChlcnJvcik7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIExvZ2dlZCwgYmVjYXVzZSBpdCBpcyBhbHNvIHdoYXQgYSBtaXNyb3V0ZWQgZ3JvdXAgbG9va3MgbGlrZTsgc2VlXG4gICAgICAgICAgICAvLyBgUm91dGVJbk90aGVyR3JvdXBFcnJvcmAgZm9yIHdoeSBpdCBpcyBhIDQwNC5cbiAgICAgICAgICAgIGNvbnNvbGUud2FybihlcnJvci5tZXNzYWdlKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgYXdhaXQgdGhpcy5zZW5kVW5tYXRjaGVkKFxuICAgICAgICAgICAgcmVxLFxuICAgICAgICAgICAgcmVzLFxuICAgICAgICAgICAgd2FpdFVudGlsLFxuICAgICAgICAgICAgeyBwYXRobmFtZTogdXJsLnBhdGhuYW1lLCByZXF1ZXN0SGVhZGVyczogcmVzdWx0LnJlcXVlc3RIZWFkZXJzIH0sXG4gICAgICAgICAgICB1cmwsXG4gICAgICAgICAgKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgLy8gVGhlIGludm9jYXRpb24gdGFyZ2V0LCBub3QgdGhlIHJlcXVlc3RlZCBVUkw6IGByZXNvbHZlUm91dGVzYCBoYXNcbiAgICAgICAgLy8gYXBwbGllZCByZXdyaXRlcywgc3RyaXBwZWQgaTE4biBwcmVmaXhlcywgYW5kIGFwcGVuZGVkIHRoZSBgbnh0UGBcbiAgICAgICAgLy8gcm91dGUgcGFyYW1zIGFzIHF1ZXJ5IHZhbHVlcy4gVGhhdCBpcyBleGFjdGx5IHRoZSBjb250cmFjdCBOZXh0LmpzXG4gICAgICAgIC8vIGRvY3VtZW50cyBmb3IgYSBwcm94eSBpbiBmcm9udCBvZiBhIGZ1bmN0aW9uIOKAlCBgUm91dGVNb2R1bGUucHJlcGFyZWBcbiAgICAgICAgLy8gcmVjb3ZlcnMgYHBhcmFtc2AgZnJvbSB0aGVzZSBxdWVyeSB2YWx1ZXMg4oCUIHNvIHRoYXQgY29udHJhY3QsIG5vdFxuICAgICAgICAvLyBgcmVxdWVzdE1ldGEucGFyYW1zYCwgaXMgaG93IHBhcmFtcyBnZXQgcGFzc2VkLiBUaGUgb25lIGV4Y2VwdGlvbiBpc1xuICAgICAgICAvLyBhIGNhcHR1cmUgdGhlIGNvbnRyYWN0IHdvdWxkIGNvcnJ1cHQ7IHNlZSBgb3V0T2ZCYW5kUm91dGVQYXJhbXNgLlxuICAgICAgICBjb25zdCBvdXRPZkJhbmQgPSBvdXRPZkJhbmRSb3V0ZVBhcmFtcyhcbiAgICAgICAgICByZXN1bHQuaW52b2NhdGlvblRhcmdldC5xdWVyeSxcbiAgICAgICAgICByZXN1bHQucmVzb2x2ZWRQYXRobmFtZSxcbiAgICAgICAgKTtcbiAgICAgICAgY29uc3QgaW52b2NhdGlvblF1ZXJ5ID1cbiAgICAgICAgICBvdXRPZkJhbmQ/LnF1ZXJ5ID8/IHJlc3VsdC5pbnZvY2F0aW9uVGFyZ2V0LnF1ZXJ5O1xuICAgICAgICBjb25zdCB7IHBhdGhuYW1lIH0gPSByZXN1bHQuaW52b2NhdGlvblRhcmdldDtcbiAgICAgICAgY29uc3Qgc2VhcmNoID0gdG9TZWFyY2goaW52b2NhdGlvblF1ZXJ5KTtcbiAgICAgICAgcmVxLnVybCA9IHNlYXJjaCA/IGAke3BhdGhuYW1lfT8ke3NlYXJjaH1gIDogcGF0aG5hbWU7XG4gICAgICAgIC8vIE1pZGRsZXdhcmUgbWF5IGhhdmUgcmV3cml0dGVuIHJlcXVlc3QgaGVhZGVycyB2aWFcbiAgICAgICAgLy8gYE5leHRSZXNwb25zZS5uZXh0KHsgcmVxdWVzdDogeyBoZWFkZXJzIH0gfSlgLlxuICAgICAgICByZXEuaGVhZGVycyA9IHRvSW5jb21pbmdIdHRwSGVhZGVycyhyZXN1bHQucmVxdWVzdEhlYWRlcnMpO1xuICAgICAgICBhd2FpdCBoYW5kbGVyKHJlcSwgYXNTZXJ2ZXJSZXNwb25zZShyZXMpLCB7XG4gICAgICAgICAgd2FpdFVudGlsLFxuICAgICAgICAgIHJlcXVlc3RNZXRhOiB7XG4gICAgICAgICAgICAvLyBUaGUgcmVzb2x2ZWQgcXVlcnksIHN0YXRlZCByYXRoZXIgdGhhbiBsZWZ0IHRvIGJlIHJlLWRlcml2ZWQuXG4gICAgICAgICAgICAvL1xuICAgICAgICAgICAgLy8gYFJvdXRlTW9kdWxlLnByZXBhcmVgIHJ1bnMgYGhhbmRsZVJld3JpdGVzYCBhZ2FpbnN0IGByZXEudXJsYFxuICAgICAgICAgICAgLy8gdW5jb25kaXRpb25hbGx5LCBhbmQgYHJlcS51cmxgIGlzIHRoZSAqYWxyZWFkeSByZXdyaXR0ZW4qIHRhcmdldC5cbiAgICAgICAgICAgIC8vIEEgYGJlZm9yZUZpbGVzYCByZXdyaXRlIHdob3NlIGNvbmRpdGlvbiBzdGlsbCBob2xkcyBhZnRlciBpdCBoYXNcbiAgICAgICAgICAgIC8vIGJlZW4gYXBwbGllZCB0aGVyZWZvcmUgZ2V0cyBhcHBsaWVkIHR3aWNlOiBpblxuICAgICAgICAgICAgLy8gYHRlc3QvZTJlL2xpbmstd2l0aC1hcGktcmV3cml0ZWAgdGhlIHJ1bGVcbiAgICAgICAgICAgIC8vIGAvOnBhdGgoLiopYCArIGBoYXM6IHF1ZXJ5IGpzb249dHJ1ZWAgLT4gYC9hcGkvanNvbj9mcm9tPS86cGF0aGBcbiAgICAgICAgICAgIC8vIG1hdGNoZWQgYC9hcGkvanNvbj9qc29uPXRydWUmZnJvbT0vc29tZS9yb3V0ZS9mb3JgIGEgc2Vjb25kIHRpbWVcbiAgICAgICAgICAgIC8vIGFuZCBvdmVyd3JvdGUgYGZyb21gIHdpdGggYC9hcGkvanNvbmAuIGBuZXh0IHN0YXJ0YCBlc2NhcGVzIGl0XG4gICAgICAgICAgICAvLyBiZWNhdXNlIGl0cyByb3V0ZXIgbGVhdmVzIGByZXEudXJsYCBhcyB0aGUgVVJMIHRoZSBjbGllbnQgc2VudCwgc29cbiAgICAgICAgICAgIC8vIHRoZSBvbmUgcGFzcyBgcHJlcGFyZWAgbWFrZXMgaXMgdGhlIG9ubHkgb25lLlxuICAgICAgICAgICAgLy9cbiAgICAgICAgICAgIC8vIGBwcmVwYXJlYCBwcmVmZXJzIHRoaXMgbWV0YSBvdmVyIGl0cyBvd24gYHBhcnNlZFVybC5xdWVyeWBcbiAgICAgICAgICAgIC8vIChcIndoZW4gZGVwbG95ZWQgcHJveGllcyB3aWxsIGFkZCBxdWVyeSB2YWx1ZXMgZnJvbSByZXNvbHZpbmcgdGhlXG4gICAgICAgICAgICAvLyByb3V0ZXMgdG8gcGFzcyB0byBmdW5jdGlvblwiKSwgd2hpY2ggaXMgdGhlIGRvY3VtZW50ZWQgZGl2aXNpb24gb2ZcbiAgICAgICAgICAgIC8vIGxhYm91ciBmb3IgYSBwcm94eSBpbiBmcm9udCBvZiBhIGZ1bmN0aW9uIC0gYW5kIGl0IGlzIHRoZSBzYW1lXG4gICAgICAgICAgICAvLyBkaXZpc2lvbiB0aGUgYHJlcS51cmxgIGFib3ZlIHJlbGllcyBvbi4gU2V0dGluZyBpdCBkb2VzIG5vdCBzdG9wXG4gICAgICAgICAgICAvLyB0aGUgc2Vjb25kIHJld3JpdGUgcGFzcywgaXQganVzdCBzdG9wcyB0aGF0IHBhc3MgZnJvbSBiZWluZyB3aGF0XG4gICAgICAgICAgICAvLyB0aGUgcm91dGUgc2Vlcy5cbiAgICAgICAgICAgIHF1ZXJ5OiB7IC4uLmludm9jYXRpb25RdWVyeSB9LFxuICAgICAgICAgICAgLy8gU2V0IG9ubHkgZm9yIGEgY2FwdHVyZSB0aGUgcXVlcnkgY29udHJhY3QgY2Fubm90IGNhcnJ5LCBhbmQgdGhlblxuICAgICAgICAgICAgLy8gaW4gcGxhY2Ugb2YgaXQgcmF0aGVyIHRoYW4gYWxvbmdzaWRlLlxuICAgICAgICAgICAgLi4uKG91dE9mQmFuZCA/IHsgcGFyYW1zOiBvdXRPZkJhbmQucGFyYW1zIH0gOiB7fSksXG4gICAgICAgICAgICAvLyBXaXRob3V0IHRoaXMsIGBSb3V0ZU1vZHVsZS5wcmVwYXJlYCBmYWxscyBiYWNrIHRvXG4gICAgICAgICAgICAvLyBgaHR0cDovL2xvY2FsaG9zdCR7cmVxLnVybH1gIGFuZCBldmVyeSBhYnNvbHV0ZSBVUkwgYSByb3V0ZVxuICAgICAgICAgICAgLy8gaGFuZGxlciBidWlsZHMgaXMgd3JvbmcuIGByZWxhdGl2ZVByb2plY3REaXJgIGlzIGRlbGliZXJhdGVseVxuICAgICAgICAgICAgLy8gKm5vdCogcGFzc2VkOiB0aGUgcnVudGltZSBgY2hkaXJgcyB0byB0aGUgcHJvamVjdCBkaXIsIHNvIHRoZVxuICAgICAgICAgICAgLy8gdmFsdWUgTmV4dC5qcyBpbmxpbmVkIGF0IGJ1aWxkIHRpbWUgKFwiXCIpIGlzIGFscmVhZHkgY29ycmVjdCwgYW5kXG4gICAgICAgICAgICAvLyBgYXBwLXBhZ2UtcnVudGltZS5qc2AgaWdub3JlcyB0aGUgcmVxdWVzdE1ldGEgb3ZlcnJpZGUgYW55d2F5LlxuICAgICAgICAgICAgaW5pdFVSTDogdXJsLmhyZWYsXG4gICAgICAgICAgICAvLyBGZWVkcyBgcm91dGVyU2VydmVyQ29udGV4dC5ob3N0bmFtZWAgKGByb3V0ZS1tb2R1bGUuanNgXG4gICAgICAgICAgICAvLyBgZ2V0Um91dGVyU2VydmVyQ29udGV4dGApLiBJbmNsdWRlcyB0aGUgcG9ydCwgYmVjYXVzZSBpdCBpc1xuICAgICAgICAgICAgLy8gY29uY2F0ZW5hdGVkIGludG8gYWJzb2x1dGUgVVJMcy5cbiAgICAgICAgICAgIGhvc3RuYW1lOiB1cmwuaG9zdCxcbiAgICAgICAgICAgIC8vIEJvdGggcm91dGVycyBjYWxsIHRoaXMgZm9yIGEgYG5vdEZvdW5kOiB0cnVlYCAvIGBub3RGb3VuZCgpYCB0aGF0XG4gICAgICAgICAgICAvLyB0aGUgZW50cnlwb2ludCBpdHNlbGYgY2Fubm90IHJlbmRlciwgYW5kIGZhbGwgYmFjayB0b1xuICAgICAgICAgICAgLy8gYHJlcy5lbmQoJ1RoaXMgcGFnZSBjb3VsZCBub3QgYmUgZm91bmQnKWAg4oCUIG5vIHN0YXR1cywgbm8gYXBwIDQwNFxuICAgICAgICAgICAgLy8gcGFnZSDigJQgd2hlbiBpdCBpcyBhYnNlbnQuIFRoZSBgcmVxYC9gcmVzYCB0aGV5IHBhc3MgYXJlIHRoZSBvbmVzXG4gICAgICAgICAgICAvLyBoYW5kZWQgaW4ganVzdCBhYm92ZSwgc28gdGhlIGNhcHR1cmVkIHBhaXIgaXMgdXNlZCBpbnN0ZWFkIG9mXG4gICAgICAgICAgICAvLyByZS1kZXJpdmluZyB0aGVtLlxuICAgICAgICAgICAgcmVuZGVyNDA0OiBhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICAgIGF3YWl0IHRoaXMuc2VuZE5vdEZvdW5kKFxuICAgICAgICAgICAgICAgIHJlcSxcbiAgICAgICAgICAgICAgICByZXMsXG4gICAgICAgICAgICAgICAgd2FpdFVudGlsLFxuICAgICAgICAgICAgICAgIHRoaXMuZGlzcGF0Y2hlci5ub3RGb3VuZEZvcih1cmwpLFxuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIC8vIGByZXMucmV2YWxpZGF0ZSgpYCBmcm9tIGEgUGFnZXMgQVBJIHJvdXRlLiBXaXRob3V0IGl0LCBOZXh0LmpzXG4gICAgICAgICAgICAvLyBmYWxscyBiYWNrIHRvIGBmZXRjaCgnaHR0cHM6Ly8nICsgcmVxLmhlYWRlcnMuaG9zdCArIHVybFBhdGgpYCDigJRcbiAgICAgICAgICAgIC8vIGFuZCBvbmx5IHdoZW4gYGV4cGVyaW1lbnRhbC50cnVzdEhvc3RIZWFkZXJgIGlzIHNldCwgd2hpY2ggaXQgaXNcbiAgICAgICAgICAgIC8vIG5vdCwgc28gYHJlcy5yZXZhbGlkYXRlKClgIHRocmV3XG4gICAgICAgICAgICAvL1xuICAgICAgICAgICAgLy8gICBGYWlsZWQgdG8gcmV2YWxpZGF0ZSAvOiBJbnZhcmlhbnQ6IG1pc3NpbmcgaW50ZXJuYWxcbiAgICAgICAgICAgIC8vICAgcm91dGVyLXNlcnZlci1tZXRob2RzIHRoaXMgaXMgYW4gaW50ZXJuYWwgYnVnXG4gICAgICAgICAgICAvL1xuICAgICAgICAgICAgLy8gYW5kIGB0ZXN0L2UyZS9yZXZhbGlkYXRlLXJlYXNvbmAgc2F3IGBzdGFsZWAgd2hlcmUgYG5leHQgc3RhcnRgXG4gICAgICAgICAgICAvLyByZXBvcnRzIGBvbi1kZW1hbmRgOiB0aGUgcm91dGUgc3RpbGwgcmUtcmVuZGVyZWQsIGp1c3QgYXMgYW5cbiAgICAgICAgICAgIC8vIG9yZGluYXJ5IHN0YWxlIHJlZ2VuZXJhdGlvbiByYXRoZXIgdGhhbiB0aGUgb24tZGVtYW5kIG9uZSBpdFxuICAgICAgICAgICAgLy8gYXNrZWQgZm9yLlxuICAgICAgICAgICAgLy9cbiAgICAgICAgICAgIC8vIEFuc3dlcmluZyBpdCBpbiBwcm9jZXNzIHJhdGhlciB0aGFuIGJ5IHNldHRpbmdcbiAgICAgICAgICAgIC8vIGB0cnVzdEhvc3RIZWFkZXJgOiB0aGUgZmV0Y2ggcGF0aCB3b3VsZCBsZWF2ZSB0aGUgZnVuY3Rpb24sIGNyb3NzXG4gICAgICAgICAgICAvLyBDbG91ZEZyb250IGFuZCBjb21lIGJhY2sg4oCUIGEgc2Vjb25kIGJpbGxlZCBpbnZvY2F0aW9uLCBhXG4gICAgICAgICAgICAvLyBkZXBlbmRlbmN5IG9uIGB4LXByZXJlbmRlci1yZXZhbGlkYXRlYCBzdXJ2aXZpbmcgdGhlIGVkZ2UsIGFuZCBhXG4gICAgICAgICAgICAvLyByZWFzb24gdG8gdHJ1c3QgYSBjbGllbnQtc3VwcGxpZWQgYEhvc3RgLiBOZXh0LmpzJ3Mgb3duXG4gICAgICAgICAgICAvLyBub24tc2VydmVybGVzcyBhbnN3ZXIgaXMgdGhlIHNhbWUgc2hhcGUgYXMgdGhpcyBvbmVcbiAgICAgICAgICAgIC8vIChgTmV4dFNlcnZlciNyZXZhbGlkYXRlYCBydW5zIGl0cyByZXF1ZXN0IGhhbmRsZXIgYWdhaW5zdCBhXG4gICAgICAgICAgICAvLyBtb2NrZWQgYHJlcWAvYHJlc2ApLlxuICAgICAgICAgICAgcmV2YWxpZGF0ZTogKGNvbmZpZzogUmV2YWxpZGF0ZUNvbmZpZykgPT5cbiAgICAgICAgICAgICAgdGhpcy5yZXZhbGlkYXRlKGNvbmZpZywgcmVxdWVzdCksXG4gICAgICAgICAgfSxcbiAgICAgICAgfSk7XG4gICAgICAgIC8vIEEgUGFnZXMgQVBJIHJvdXRlIG93bnMgdGhlIGVuZCBvZiBpdHMgcmVzcG9uc2U6IGBzdHJlYW0ucGlwZShyZXMpYCxcbiAgICAgICAgLy8gYW4gYGV4dGVybmFsUmVzb2x2ZXJgIHByb3h5LCBvciBhIGNhbGxiYWNrIHRoYXQgY2FsbHMgYHJlcy5qc29uKClgXG4gICAgICAgIC8vIGxhdGVyIGFsbCByZXR1cm4gZnJvbSB0aGUgaGFuZGxlciBiZWZvcmUgdGhleSBhcmUgZG9uZSB3cml0aW5nLCBhbmRcbiAgICAgICAgLy8gZW5kaW5nIGhlcmUgc2VudCBhbiBlbXB0eSAyMDAgYW5kIG1hZGUgdGhlaXIgd3JpdGVzIHRocm93XG4gICAgICAgIC8vIGBFUlJfU1RSRUFNX1dSSVRFX0FGVEVSX0VORGAuIGBuZXh0IHN0YXJ0YCdzIGBhcGlSZXNvbHZlcmAgbGVhdmVzIGl0XG4gICAgICAgIC8vIG9wZW4gdG9vIOKAlCBvbmUgdGhhdCBuZXZlciBlbmRzIHN0YWxscywgdGhlcmUgYXMgaGVyZS4gYGhhbmRsZWAgYXdhaXRzXG4gICAgICAgIC8vIHRoZSByZXNwb25zZSBpdHNlbGYsIHNvIG5vdGhpbmcgaXMgY3V0IHNob3J0IGJ5IHJldHVybmluZy5cbiAgICAgICAgaWYgKCFyZXMud3JpdGFibGVFbmRlZCAmJiByZXN1bHQuZW50cnlwb2ludC50eXBlICE9PSBcInBhZ2UtYXBpXCIpIHtcbiAgICAgICAgICByZXMuZW5kKCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBjYXNlIFwic3RhdGljLWZpbGVcIjoge1xuICAgICAgICBjb25zdCBzdGF0dXNQYWdlID1cbiAgICAgICAgICByZXN1bHQuc291cmNlID09PSBcInNlcnZlclwiXG4gICAgICAgICAgICA/IHRoaXMuc3RhdHVzUGFnZVN0YXR1cyhyZXN1bHQucGF0aG5hbWUpXG4gICAgICAgICAgICA6IHVuZGVmaW5lZDtcbiAgICAgICAgLy8gYG5leHQgc3RhcnRgIHNlcnZlcyBhIGBwdWJsaWMvYCBvciBgX25leHQvc3RhdGljYCBmaWxlIHRvIEdFVCBhbmRcbiAgICAgICAgLy8gSEVBRCBvbmx5LCBhbmQgYHNlbmRgIHdvdWxkIHNlcnZlIGl0IHRvIGFueSBtZXRob2QuIFRoZSBzYW1lIGdvZXMgZm9yXG4gICAgICAgIC8vIGFuIGF1dG8tZXhwb3J0ZWQgUGFnZXMgUm91dGVyIHBhZ2UgKGBiYXNlLXNlcnZlci5qc2BcbiAgICAgICAgLy8gYHJlbmRlclRvUmVzcG9uc2VXaXRoQ29tcG9uZW50c0ltcGxgOiBhIHN0cmluZyBgQ29tcG9uZW50YCBhbnN3ZXJzXG4gICAgICAgIC8vIGFueXRoaW5nIGVsc2Ugd2l0aCBhIDQwNSksIGV4Y2VwdCB0aGUgNDA0IGFuZCA1MDAgcGFnZXMuXG4gICAgICAgIGlmIChcbiAgICAgICAgICByZXEubWV0aG9kICE9PSBcIkdFVFwiICYmXG4gICAgICAgICAgcmVxLm1ldGhvZCAhPT0gXCJIRUFEXCIgJiZcbiAgICAgICAgICAocmVzdWx0LnNvdXJjZSAhPT0gXCJzZXJ2ZXJcIiB8fFxuICAgICAgICAgICAgKHN0YXR1c1BhZ2UgPT09IHVuZGVmaW5lZCAmJiByZXN1bHQuZmlsZVBhdGguZW5kc1dpdGgoXCIuaHRtbFwiKSkpXG4gICAgICAgICkge1xuICAgICAgICAgIHNlbmRNZXRob2ROb3RBbGxvd2VkKHJlcyk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIC8vIFwiZW5zdXJlIGNvcnJlY3Qgc3RhdHVzIGlzIHNldCB3aGVuIHZpc2l0aW5nIGEgc3RhdHVzIHBhZ2UgZGlyZWN0bHlcIjpcbiAgICAgICAgLy8gYG5leHQgc3RhcnRgIGFuc3dlcnMgYC80MDRgIHdpdGggYSA0MDQgYW5kIGAvNTAwYCB3aXRoIGEgNTAwLCBub3QgdGhlXG4gICAgICAgIC8vIDIwMCB0aGVpciBwcmVyZW5kZXJlZCBIVE1MIHdvdWxkIG90aGVyd2lzZSBnbyBvdXQgd2l0aC5cbiAgICAgICAgaWYgKHN0YXR1c1BhZ2UgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIHJlcy5zdGF0dXNDb2RlID0gc3RhdHVzUGFnZTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBldGFnID0gdGhpcy5vcHRpb25zLm1hbmlmZXN0LmNvbmZpZy5nZW5lcmF0ZUV0YWdzO1xuICAgICAgICBjb25zdCBwdWJsaWNEaXIgPSBgJHtwdWJsaWNEaXJLZXkodGhpcy5vcHRpb25zLm1hbmlmZXN0KX0vYDtcbiAgICAgICAgY29uc3Qgc2VydmVkID1cbiAgICAgICAgICAoYXdhaXQgc2VydmVTdGF0aWNGaWxlKFxuICAgICAgICAgICAgcmVxLFxuICAgICAgICAgICAgcmVzLFxuICAgICAgICAgICAgdGhpcy5vcHRpb25zLmRlcGxveW1lbnRSb290LFxuICAgICAgICAgICAgcmVzdWx0LmZpbGVQYXRoLFxuICAgICAgICAgICAgeyBldGFnIH0sXG4gICAgICAgICAgKSkgfHxcbiAgICAgICAgICAvLyBMaXN0ZWQgYnV0IG5vdCBzdGFnZWQ6IGEgTGFtYmRhIHJvb3QsIHdob3NlIGBwdWJsaWMvYCBpcyBpbiBTMy5cbiAgICAgICAgICAocmVzdWx0LnNvdXJjZSA9PT0gXCJwdWJsaWNcIiAmJlxuICAgICAgICAgICAgISF0aGlzLnB1YmxpY0J1Y2tldCAmJlxuICAgICAgICAgICAgKGF3YWl0IHNlcnZlUzNQdWJsaWNGaWxlKHJlcSwgcmVzLCB7XG4gICAgICAgICAgICAgIGJ1Y2tldDogdGhpcy5wdWJsaWNCdWNrZXQsXG4gICAgICAgICAgICAgIGtleVByZWZpeDogdGhpcy5vcHRpb25zLmJ1Y2tldEtleVByZWZpeCA/PyBcIlwiLFxuICAgICAgICAgICAgICBmaWxlOiByZXN1bHQuZmlsZVBhdGguc2xpY2UocHVibGljRGlyLmxlbmd0aCksXG4gICAgICAgICAgICAgIGV0YWcsXG4gICAgICAgICAgICB9KSkpO1xuICAgICAgICBpZiAoIXNlcnZlZCkge1xuICAgICAgICAgIC8vIFRoZSByb3V0aW5nIHJ1bGUgdGhhdCBtYXRjaGVkIGEgYnVpbGQgYXNzZXQgYWxyZWFkeSBzZXQgaXRzXG4gICAgICAgICAgLy8geWVhci1sb25nIGBpbW11dGFibGVgIENhY2hlLUNvbnRyb2wsIHdoaWNoIGBzZW5kVW5tYXRjaGVkYFxuICAgICAgICAgIC8vIHJlcGxhY2VzOiB1bmRlciBpdCB0aGUgNDA0IHdvdWxkIGJlIGNhY2hlZCBhdCB0aGUgZWRnZSBhbmQgaW5cbiAgICAgICAgICAvLyBicm93c2VycyBmb3IgdGhhdCBsb25nLCBhbmQgYSBjaHVuayB0aGF0IHNob3dzIHVwIG9uIHRoZSBuZXh0XG4gICAgICAgICAgLy8gZGVwbG95IHdvdWxkIHN0YXkgbWlzc2luZy5cbiAgICAgICAgICBhd2FpdCB0aGlzLnNlbmRVbm1hdGNoZWQocmVxLCByZXMsIHdhaXRVbnRpbCwgcmVzdWx0LCB1cmwpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgY2FzZSBcImltYWdlLW9wdGltaXphdGlvblwiOlxuICAgICAgICBpZiAoIXRoaXMuaW1hZ2VzLmlzRW5hYmxlZCgpKSB7XG4gICAgICAgICAgYXdhaXQgdGhpcy5zZW5kVW5tYXRjaGVkKFxuICAgICAgICAgICAgcmVxLFxuICAgICAgICAgICAgcmVzLFxuICAgICAgICAgICAgd2FpdFVudGlsLFxuICAgICAgICAgICAgeyBwYXRobmFtZTogdXJsLnBhdGhuYW1lLCByZXF1ZXN0SGVhZGVyczogcmVzdWx0LnJlcXVlc3RIZWFkZXJzIH0sXG4gICAgICAgICAgICB1cmwsXG4gICAgICAgICAgKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgcmVxLmhlYWRlcnMgPSB0b0luY29taW5nSHR0cEhlYWRlcnMocmVzdWx0LnJlcXVlc3RIZWFkZXJzKTtcbiAgICAgICAgYXdhaXQgdGhpcy5pbWFnZXMuaGFuZGxlKHJlcSwgcmVzLCByZXN1bHQudXJsLCB3YWl0VW50aWwpO1xuICAgICAgICByZXR1cm47XG5cbiAgICAgIGNhc2UgXCJyZWRpcmVjdFwiOlxuICAgICAgICBzZW5kUmVkaXJlY3QocmVzLCByZXN1bHQubG9jYXRpb24sIHJlc3VsdC5zdGF0dXMpO1xuICAgICAgICByZXR1cm47XG5cbiAgICAgIGNhc2UgXCJleHRlcm5hbC1yZXdyaXRlXCI6XG4gICAgICAgIGF3YWl0IHByb3h5RXh0ZXJuYWwocmVxLCByZXMsIHJlc3VsdC51cmwsIHJlc3VsdC5yZXF1ZXN0SGVhZGVycyk7XG4gICAgICAgIHJldHVybjtcblxuICAgICAgY2FzZSBcIm1pZGRsZXdhcmUtcmVzcG9uZGVkXCI6XG4gICAgICAgIGF3YWl0IHNlbmRXZWJSZXNwb25zZShyZXMsIHJlc3VsdC5yZXNwb25zZSk7XG4gICAgICAgIHJldHVybjtcblxuICAgICAgY2FzZSBcInJlc3BvbnNlXCI6XG4gICAgICAgIHJlcy5lbmQoKTtcbiAgICAgICAgcmV0dXJuO1xuXG4gICAgICBjYXNlIFwibm90LWZvdW5kXCI6XG4gICAgICAgIGF3YWl0IHRoaXMuc2VuZFVubWF0Y2hlZChyZXEsIHJlcywgd2FpdFVudGlsLCByZXN1bHQsIHVybCk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogNDA0IG9yIDUwMCB3aGVuIGBwYXRobmFtZWAgaXMgdGhlIHByZXJlbmRlcmVkIGAvNDA0YCBvciBgLzUwMGAgcGFnZSDigJQgdW5kZXJcbiAgICogYGJhc2VQYXRoYCBhbmQsIGluIGFuIGkxOG4gYXBwLCBhbnkgbG9jYWxlIChgL2ZyLzQwNGApIOKAlCBhbmQgYHVuZGVmaW5lZGBcbiAgICogb3RoZXJ3aXNlLiBgbmV4dCBzdGFydGAgbWF0Y2hlcyB0aGUgcGFnZSwgbm90IHRoZSBVUkwsIHNvIHRoZSBsb2NhbGUgZG9lc1xuICAgKiBub3QgbWF0dGVyLlxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0dXNQYWdlU3RhdHVzKHBhdGhuYW1lOiBzdHJpbmcpOiBudW1iZXIgfCB1bmRlZmluZWQge1xuICAgIGNvbnN0IHsgYmFzZVBhdGgsIGkxOG4gfSA9IHRoaXMub3B0aW9ucy5tYW5pZmVzdC5jb25maWc7XG4gICAgbGV0IHBhZ2UgPSB3aXRob3V0UGF0aFByZWZpeChwYXRobmFtZSwgYmFzZVBhdGgpO1xuICAgIGNvbnN0IGxvY2FsZXMgPSAoaTE4biBhcyB7IGxvY2FsZXM/OiByZWFkb25seSBzdHJpbmdbXSB9IHwgbnVsbCk/LmxvY2FsZXM7XG4gICAgZm9yIChjb25zdCBsb2NhbGUgb2YgbG9jYWxlcyA/PyBbXSkge1xuICAgICAgY29uc3Qgc3RyaXBwZWQgPSB3aXRob3V0UGF0aFByZWZpeChwYWdlLCBgLyR7bG9jYWxlfWApO1xuICAgICAgaWYgKHN0cmlwcGVkICE9PSBwYWdlKSB7XG4gICAgICAgIHBhZ2UgPSBzdHJpcHBlZDtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuICAgIGlmIChwYWdlID09PSBcIi80MDRcIikgcmV0dXJuIDQwNDtcbiAgICBpZiAocGFnZSA9PT0gXCIvNTAwXCIpIHJldHVybiA1MDA7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBgbmV4dCBzdGFydGAncyBhbnN3ZXIgdG8gYSBwYXRoIG5vdGhpbmcgc2VydmVzIChgcm91dGVyLXNlcnZlci5qc2AsIHRoZVxuICAgKiBcIjQwNCBjYXNlXCIpLCBmb3IgYm90aCB3YXlzIG9mIGdldHRpbmcgaGVyZTogZGlzcGF0Y2ggbWF0Y2hlZCBub3RoaW5nLCBvciBpdFxuICAgKiBtYXRjaGVkIGEgYnVpbGQgYXNzZXQgdGhhdCBpcyBub3QgaW4gdGhlIHBhY2thZ2UuXG4gICAqXG4gICAqIE5vLXN0b3JlLCBhbHdheXMuIEEgcHJlcmVuZGVyZWQgYC9fbm90LWZvdW5kYCB3b3VsZCBvdGhlcndpc2Ugc2VuZCBpdHNcbiAgICogY2FjaGUgZW50cnkncyB5ZWFyLWxvbmcgYHMtbWF4YWdlYCwgYW5kIHRoZSBDRE4gd291bGQga2VlcCB0aGUgNDA0IHBhc3QgdGhlXG4gICAqIGRlcGxveSB0aGF0IGFkZHMgdGhlIHJvdXRlLiBTZXQgcmF0aGVyIHRoYW4gZm9yY2VkOiBOZXh0J3MgcGFnZSBoYW5kbGVyc1xuICAgKiBvbmx5IHdyaXRlIGEgYENhY2hlLUNvbnRyb2xgIHdoZW4gbm9uZSBpcyBzZXQgKGBzZW5kUmVuZGVyUmVzdWx0YCxcbiAgICogYHBhZ2VzLWhhbmRsZXIuanNgKSwgd2hpY2ggaXMgZXhhY3RseSBob3cgYG5leHQgc3RhcnRgJ3Mgb3duIG5vLXN0b3JlIGhlcmVcbiAgICogc3Vydml2ZXMgdGhlIHJlbmRlci4gYHJlbmRlcjQwNGAgaXMgbm90IHRoaXMgcGF0aDogYSBgbm90Rm91bmQoKWAgZnJvbSBhblxuICAgKiBJU1IgcGFnZSBpcyBjYWNoZWFibGUgZm9yIHRoYXQgcGFnZSdzIHJldmFsaWRhdGUgcGVyaW9kLlxuICAgKlxuICAgKiBBIG1pc3NpbmcgYF9uZXh0L3N0YXRpY2AgZmlsZSwgYW5kIGEgR0VUIG9yIEhFQUQgd2hvc2UgYFNlYy1GZXRjaC1EZXN0YFxuICAgKiBzYXlzIGl0IGNhbm5vdCBkaXNwbGF5IEhUTUwgKGFuIGA8aW1nPmAsIGEgc2NyaXB0LCBhIGZvbnQpLCBnZXQgYVxuICAgKiBwbGFpbi10ZXh0IGBOb3QgRm91bmRgIGluc3RlYWQgb2YgdGhlIHJlbmRlcmVkIHBhZ2Ug4oCUIG5vdGhpbmcgd291bGQgc2hvd1xuICAgKiBpdCwgYW5kIHJlbmRlcmluZyBpdCBpcyB0aGUgZXhwZW5zaXZlIHBhcnQuXG4gICAqXG4gICAqIEV2ZXJ5dGhpbmcgZWxzZSByZW5kZXJzIHRoZSBhcHAncyA0MDQgYXMgdGhlIHBhdGggdGhhdCB3YXMgYXNrZWQgZm9yLCBub3RcbiAgICogYXMgYC9fbm90LWZvdW5kYDogdGhlIEFwcCBSb3V0ZXIgc2VyaWFsaXplcyB0aGUgY2Fub25pY2FsIFVSTCBpbnRvIHRoZSBSU0NcbiAgICogcGF5bG9hZCwgc28gYSBjbGllbnQgaHlkcmF0ZWQgb2ZmIGEgYC9fbm90LWZvdW5kYCBwYXlsb2FkIHJlcG9ydHMgdGhlIHdyb25nXG4gICAqIGB1c2VQYXRobmFtZSgpYCBhbmQgcHVzaGVzIHRoZSB3cm9uZyBoaXN0b3J5IGVudHJ5LiBJdCByZW5kZXJzIHdpdGggdGhlXG4gICAqIHJlcXVlc3QgaGVhZGVycyBtaWRkbGV3YXJlIHNldCwgdG9vIOKAlCBhIENTUCBub25jZSwgc2F5LlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBzZW5kVW5tYXRjaGVkKFxuICAgIHJlcTogU2hpbUluY29taW5nTWVzc2FnZSxcbiAgICByZXM6IFNoaW1TZXJ2ZXJSZXNwb25zZSxcbiAgICB3YWl0VW50aWw6IChwcm9taXNlOiBQcm9taXNlPHVua25vd24+KSA9PiB2b2lkLFxuICAgIHJlc3VsdDogeyByZWFkb25seSBwYXRobmFtZTogc3RyaW5nOyByZWFkb25seSByZXF1ZXN0SGVhZGVyczogSGVhZGVycyB9LFxuICAgIHVybDogVVJMLFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXMuc2V0SGVhZGVyKFwiQ2FjaGUtQ29udHJvbFwiLCBOT19TVE9SRSk7XG4gICAgY29uc3QgeyBiYXNlUGF0aCwgYXNzZXRQcmVmaXggfSA9IHRoaXMub3B0aW9ucy5tYW5pZmVzdC5jb25maWc7XG4gICAgY29uc3QgcGF0aG5hbWUgPSB3aXRob3V0UGF0aFByZWZpeChcbiAgICAgIHdpdGhvdXRQYXRoUHJlZml4KHJlc3VsdC5wYXRobmFtZSwgYmFzZVBhdGgpLFxuICAgICAgYXNzZXRQcmVmaXgsXG4gICAgKTtcbiAgICBpZiAoXG4gICAgICBwYXRobmFtZS5zdGFydHNXaXRoKFwiL19uZXh0L3N0YXRpYy9cIikgfHxcbiAgICAgICgocmVxLm1ldGhvZCA9PT0gXCJHRVRcIiB8fCByZXEubWV0aG9kID09PSBcIkhFQURcIikgJiZcbiAgICAgICAgTk9OX0hUTUxfU0VDX0ZFVENIX0RFU1RTLmhhcyhcbiAgICAgICAgICBmaXJzdFZhbHVlKHJlcS5oZWFkZXJzW1wic2VjLWZldGNoLWRlc3RcIl0pID8/IFwiXCIsXG4gICAgICAgICkpXG4gICAgKSB7XG4gICAgICByZXMuc3RhdHVzQ29kZSA9IDQwNDtcbiAgICAgIHJlcy5zZXRIZWFkZXIoXCJDb250ZW50LVR5cGVcIiwgXCJ0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04XCIpO1xuICAgICAgcmVzLmVuZChcIk5vdCBGb3VuZFwiKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgcmVxLmhlYWRlcnMgPSB0b0luY29taW5nSHR0cEhlYWRlcnMocmVzdWx0LnJlcXVlc3RIZWFkZXJzKTtcbiAgICBhd2FpdCB0aGlzLnNlbmROb3RGb3VuZChcbiAgICAgIHJlcSxcbiAgICAgIHJlcyxcbiAgICAgIHdhaXRVbnRpbCxcbiAgICAgIHRoaXMuZGlzcGF0Y2hlci5ub3RGb3VuZEZvcih1cmwpLFxuICAgICAgYCR7cmVzdWx0LnBhdGhuYW1lfSR7dXJsLnNlYXJjaH1gLFxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogT25lIG9uLWRlbWFuZCByZXZhbGlkYXRpb24sIHJ1biBhZ2FpbnN0IHRoaXMgcnVudGltZSByYXRoZXIgdGhhbiBvdmVyIHRoZVxuICAgKiBuZXR3b3JrLiBXaXJlZCBpbiBhcyBgcmVxdWVzdE1ldGEucmV2YWxpZGF0ZWA7IHNlZSB0aGUgY29tbWVudCB0aGVyZS5cbiAgICpcbiAgICogVGhlIHJlc3BvbnNlIGlzIHRocm93biBhd2F5IOKAlCBvbmx5IGl0cyBzdGF0dXMgbWF0dGVycyDigJQgYnV0IGl0IGdvZXMgdGhyb3VnaFxuICAgKiB0aGUgZnVsbCB7QGxpbmsgaGFuZGxlfSwgc28gdGhlIGVudHJ5cG9pbnQgc2VlcyBhIHJlcXVlc3Qgd2l0aFxuICAgKiBgeC1wcmVyZW5kZXItcmV2YWxpZGF0ZWAgb24gaXQgYW5kIHdyaXRlcyB0aGUgZnJlc2ggZW50cnkgdGhyb3VnaCB0aGUgY2FjaGVcbiAgICogaGFuZGxlciBleGFjdGx5IGFzIGEgcmVxdWVzdCBmcm9tIG91dHNpZGUgd291bGQuIGBoYW5kbGVgIGF3YWl0cyBpdHMgb3duXG4gICAqIGB3YWl0VW50aWxgIHdvcmssIHNvIHRoZSBuZXcgZW50cnkgaXMgY29tbWl0dGVkIGJlZm9yZSBgcmVzLnJldmFsaWRhdGUoKWBcbiAgICogcmVzb2x2ZXMsIHdoaWNoIGlzIHdoYXQgY2FsbGVycyB0aGF0IHJldmFsaWRhdGUgYW5kIHRoZW4gcmVkaXJlY3QgZGVwZW5kIG9uLlxuICAgKlxuICAgKiBUaGUgYWNjZXB0L3Rocm93IHJ1bGUgaXMgYE5leHRTZXJ2ZXIjcmV2YWxpZGF0ZWAncywgdmVyYmF0aW06IGEgcmV2YWxpZGF0aW9uXG4gICAqIGlzIHN1Y2Nlc3NmdWwgaWYgaXQgd2FzIGNhY2hlZCBvciBhbnN3ZXJlZCAyMDAsIGFuZCBgbm90Rm91bmQ6IHRydWVgXG4gICAqIGxlZ2l0aW1hdGVseSBhbnN3ZXJzIDQwNCBmb3IgYW4gYHVuc3RhYmxlX29ubHlHZW5lcmF0ZWRgIGNhbGxlci4gQW55dGhpbmdcbiAgICogZWxzZSB0aHJvd3MsIGFuZCBgcmVzLnJldmFsaWRhdGUoKWAgdHVybnMgaXQgaW50byBgRmFpbGVkIHRvIHJldmFsaWRhdGVcbiAgICogPHBhdGg+OiA8bWVzc2FnZT5gLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyByZXZhbGlkYXRlKFxuICAgIGNvbmZpZzogUmV2YWxpZGF0ZUNvbmZpZyxcbiAgICBvcmlnaW46IFJ1bnRpbWVSZXF1ZXN0LFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB7IGhlYWQsIG90aGVyR3JvdXAgfSA9IGF3YWl0IHRoaXMuaGFuZGxlSW50ZXJuYWxseShcbiAgICAgIHtcbiAgICAgICAgbWV0aG9kOiBcIkdFVFwiLFxuICAgICAgICB1cmw6IGNvbmZpZy51cmxQYXRoLFxuICAgICAgICAvLyBUaGUgY2FsbGVyJ3MgYXV0aG9yaXR5LCBub3QgdGhlIHJldmFsaWRhdGVkIHBhdGgnczogaXQgaXMgd2hhdFxuICAgICAgICAvLyBgYWJzb2x1dGVVcmxgIGJ1aWxkcyB0aGUgcmVxdWVzdCBVUkwgZnJvbSwgYW5kIGEgcmV2YWxpZGF0aW9uIGlzIGZvclxuICAgICAgICAvLyB0aGUgb3JpZ2luIGJlaW5nIHNlcnZlZC4gVGhlIGZvcndhcmRlZCBwYWlyIHdpdGggaXQg4oCUIGJlaGluZFxuICAgICAgICAvLyBDbG91ZEZyb250IGBob3N0YCBpcyB0aGUgRnVuY3Rpb24gVVJMLCBhbmQgYSByZW5kZXIgdGhhdCBidWlsdCBpdHNcbiAgICAgICAgLy8gYWJzb2x1dGUgVVJMcyBmcm9tIHRoYXQgd291bGQgY2FjaGUgbGlua3MgdG8gdGhlIHJhdyBmdW5jdGlvbi5cbiAgICAgICAgaGVhZGVyczoge1xuICAgICAgICAgIC4uLmNvbmZpZy5oZWFkZXJzLFxuICAgICAgICAgIGhvc3Q6IG9yaWdpbi5oZWFkZXJzLmhvc3QsXG4gICAgICAgICAgLi4ucGlja0RlZmluZWQob3JpZ2luLmhlYWRlcnMsIFtcbiAgICAgICAgICAgIFwieC1mb3J3YXJkZWQtaG9zdFwiLFxuICAgICAgICAgICAgXCJ4LWZvcndhcmRlZC1wcm90b1wiLFxuICAgICAgICAgIF0pLFxuICAgICAgICB9LFxuICAgICAgICBlbmNyeXB0ZWQ6IG9yaWdpbi5lbmNyeXB0ZWQsXG4gICAgICAgIHRydXN0Rm9yd2FyZGVkSG9zdDogb3JpZ2luLnRydXN0Rm9yd2FyZGVkSG9zdCxcbiAgICAgIH0sXG4gICAgICB7fSxcbiAgICApO1xuXG4gICAgY29uc3Qgc3RhdHVzID0gaGVhZD8uc3RhdHVzQ29kZSA/PyA1MDA7XG4gICAgaWYgKG90aGVyR3JvdXApIHtcbiAgICAgIGNvbnN0IHNlbGYgPSBwcm9jZXNzLmVudltGVU5DVElPTl9HUk9VUF9FTlZfVkFSXTtcbiAgICAgIC8vIEEgcGFnZSBwYWNrYWdlZCBpbnRvIGFub3RoZXIgYGZ1bmN0aW9uR3JvdXBzYCBncm91cDogaXQgaXMgcmVuZGVyZWQgaW5cbiAgICAgIC8vIHByb2Nlc3MsIGFuZCB0aGlzIGZ1bmN0aW9uIGRvZXMgbm90IGhhdmUgdGhlIHBhZ2UncyBjb2RlLiBDaGVja2VkXG4gICAgICAvLyBmaXJzdCBiZWNhdXNlIGl0IHJlbmRlcnMgYXMgYSA0MDQsIHdoaWNoIGB1bnN0YWJsZV9vbmx5R2VuZXJhdGVkYFxuICAgICAgLy8gd291bGQgb3RoZXJ3aXNlIGFjY2VwdC5cbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYEludmFsaWQgcmVzcG9uc2UgJHtzdGF0dXN9OiBcIiR7Y29uZmlnLnVybFBhdGh9XCIgYmVsb25ncyB0byBgICtcbiAgICAgICAgICBgXFxgZnVuY3Rpb25Hcm91cHNcXGAgZ3JvdXAgXCIke290aGVyR3JvdXAub3duZXJ9XCIsIGFuZCBgICtcbiAgICAgICAgICBgcmVzLnJldmFsaWRhdGUoKSBjYW4gb25seSByZXZhbGlkYXRlIHBhZ2VzIGluIHRoZSBncm91cCBpdCBydW5zIGAgK1xuICAgICAgICAgIGBpbiAoXCIke3NlbGZ9XCIpLiBDYWxsIGl0IGZyb20gYW4gQVBJIHJvdXRlIGluIGdyb3VwIGAgK1xuICAgICAgICAgIGBcIiR7b3RoZXJHcm91cC5vd25lcn1cIiwgb3IgdXNlIHJldmFsaWRhdGVQYXRoKCkvcmV2YWxpZGF0ZVRhZygpLCBgICtcbiAgICAgICAgICBgd2hpY2ggd29yayBmcm9tIGFueSBncm91cC5gLFxuICAgICAgKTtcbiAgICB9XG4gICAgaWYgKFxuICAgICAgaGVhZD8uaGVhZGVyc1tcIngtbmV4dGpzLWNhY2hlXCJdICE9PSBcIlJFVkFMSURBVEVEXCIgJiZcbiAgICAgIHN0YXR1cyAhPT0gMjAwICYmXG4gICAgICAhKHN0YXR1cyA9PT0gNDA0ICYmIGNvbmZpZy5vcHRzLnVuc3RhYmxlX29ubHlHZW5lcmF0ZWQpXG4gICAgKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgcmVzcG9uc2UgJHtzdGF0dXN9YCk7XG4gICAgfVxuXG4gICAgYXdhaXQgaW52YWxpZGF0ZVJldmFsaWRhdGVkUGFnZShjb25maWcudXJsUGF0aCwgdGhpcy5vcHRpb25zLm1hbmlmZXN0KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBbiBpbWFnZSBvcHRpbWl6YXRpb24gc291cmNlIHRoYXQgaXMgbm90IGEgZmlsZTogYDxJbWFnZSBzcmM9XCIvYXBpL2F2YXRhclwiPmBcbiAgICogc2VydmVkIGJ5IGEgcm91dGUgaGFuZGxlci4gV2lyZWQgaW4gYXMgdGhlIG9wdGltaXplcidzIGBmZXRjaEludGVybmFsYCwgYW5kXG4gICAqIHJ1biBpbiBwcm9jZXNzIGZvciB0aGUgcmVhc29ucyB7QGxpbmsgcmV2YWxpZGF0ZX0gaXMg4oCUIGBuZXh0IHN0YXJ0YCBmZXRjaGVzXG4gICAqIHRoZSBzb3VyY2UgZnJvbSBpdHNlbGYgb3ZlciBsb29wYmFjaywgd2hpY2ggaGVyZSB3b3VsZCBiZSBhIHNlY29uZCBiaWxsZWRcbiAgICogaW52b2NhdGlvbiB0aHJvdWdoIENsb3VkRnJvbnQuXG4gICAqXG4gICAqIE9ubHkgdGhlIGF1dGhvcml0eSBpcyBmb3J3YXJkZWQsIGFzIGBuZXh0IHN0YXJ0YCdzIGBmZXRjaEludGVybmFsSW1hZ2VgXG4gICAqIGZvcndhcmRzIG5vbmUgb2YgdGhlIHZpZXdlcidzIGhlYWRlcnM6IHRoZSBzb3VyY2UgaXMgY2FjaGVkIGFuZCBzZXJ2ZWQgdG9cbiAgICogZXZlcnlvbmUsIHNvIGl0IG11c3Qgbm90IGRlcGVuZCBvbiBvbmUgdmlld2VyJ3MgY29va2llcy5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZmV0Y2hJbnRlcm5hbChcbiAgICBocmVmOiBzdHJpbmcsXG4gICAgcmVxOiBTaGltSW5jb21pbmdNZXNzYWdlLFxuICAgIG1heGltdW1Cb2R5OiBudW1iZXIsXG4gICk6IFByb21pc2U8SW50ZXJuYWxJbWFnZVJlc3BvbnNlPiB7XG4gICAgY29uc3QgcmVxdWVzdDogUnVudGltZVJlcXVlc3QgPSB7XG4gICAgICBtZXRob2Q6IFwiR0VUXCIsXG4gICAgICB1cmw6IGhyZWYsXG4gICAgICBoZWFkZXJzOiBwaWNrRGVmaW5lZChyZXEuaGVhZGVycywgW1xuICAgICAgICBcImhvc3RcIixcbiAgICAgICAgXCJ4LWZvcndhcmRlZC1ob3N0XCIsXG4gICAgICAgIFwieC1mb3J3YXJkZWQtcHJvdG9cIixcbiAgICAgIF0pLFxuICAgICAgZW5jcnlwdGVkOiAocmVxLnNvY2tldCBhcyB7IGVuY3J5cHRlZD86IGJvb2xlYW4gfSB8IHVuZGVmaW5lZCk/LmVuY3J5cHRlZCxcbiAgICAgIHRydXN0Rm9yd2FyZGVkSG9zdDogcmVxLnRydXN0Rm9yd2FyZGVkSG9zdCxcbiAgICB9O1xuICAgIGNvbnN0IHsgaGVhZCwgYm9keSwgb3RoZXJHcm91cCwgdG9vTGFyZ2UgfSA9IGF3YWl0IHRoaXMuaGFuZGxlSW50ZXJuYWxseShcbiAgICAgIHJlcXVlc3QsXG4gICAgICB7IG1heGltdW1Cb2R5IH0sXG4gICAgKTtcbiAgICBpZiAodG9vTGFyZ2UpIHtcbiAgICAgIHJldHVybiB7IHN0YXR1c0NvZGU6IDAsIGhlYWRlcnM6IHt9LCBib2R5OiBCdWZmZXIuYWxsb2MoMCksIHRvb0xhcmdlIH07XG4gICAgfVxuICAgIGlmIChvdGhlckdyb3VwKSB7XG4gICAgICAvLyBUaGUgZWRnZSBzZW5kcyBldmVyeSBgL19uZXh0L2ltYWdlYCByZXF1ZXN0IHRvIHRoZSBkZWZhdWx0IGdyb3VwLCBhbmRcbiAgICAgIC8vIHRoZSBzb3VyY2UgaXMgcmVuZGVyZWQgaW4gcHJvY2Vzcywgc28gYSByb3V0ZSBwYWNrYWdlZCBpbnRvIGFub3RoZXJcbiAgICAgIC8vIGdyb3VwIGlzIG91dCBvZiByZWFjaDogdGhlIG9wdGltaXplciBhbnN3ZXJzIChhbmQgbG9ncykgYSA1MDIuXG4gICAgICByZXR1cm4ge1xuICAgICAgICBzdGF0dXNDb2RlOiAwLFxuICAgICAgICBoZWFkZXJzOiB7fSxcbiAgICAgICAgYm9keTogQnVmZmVyLmFsbG9jKDApLFxuICAgICAgICBvdGhlckdyb3VwOiBvdGhlckdyb3VwLm93bmVyLFxuICAgICAgfTtcbiAgICB9XG4gICAgcmV0dXJuIHtcbiAgICAgIHN0YXR1c0NvZGU6IGhlYWQ/LnN0YXR1c0NvZGUgPz8gMCxcbiAgICAgIGhlYWRlcnM6IGhlYWQ/LmhlYWRlcnMgPz8ge30sXG4gICAgICBib2R5LFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogT25lIHJlcXVlc3QgdGhyb3VnaCB7QGxpbmsgaGFuZGxlfSwgYW5zd2VyZWQgdG8gdGhpcyBydW50aW1lIGluc3RlYWQgb2YgYVxuICAgKiBjbGllbnQ6IHRoZSBoZWFkLCB0aGUgYm9keSB3aGVuIGl0IGlzIHdhbnRlZCwgYW5kIHRoZSBlcnJvciB3aGVuIHRoZSByb3V0ZVxuICAgKiBpcyBwYWNrYWdlZCBpbnRvIGFub3RoZXIgYGZ1bmN0aW9uR3JvdXBzYCBncm91cC4gYGhhbmRsZWAgYXdhaXRzIHRoZVxuICAgKiByZXF1ZXN0J3MgYHdhaXRVbnRpbGAgd29yayB0b28sIHNvIHdoYXRldmVyIHRoZSByZW5kZXIgd3JvdGUgaXMgY29tbWl0dGVkIGJ5XG4gICAqIHRoZSB0aW1lIHRoaXMgcmVzb2x2ZXMuXG4gICAqXG4gICAqIFRoZSBib2R5IGlzIGtlcHQgb25seSB3aGVuIGBtYXhpbXVtQm9keWAgaXMgc2V0LiBPbmUgcGFzdCBpdCBmYWlscyB0aGVcbiAgICogcmVzcG9uc2Ugc3RyZWFtLCB3aGljaCBzdG9wcyB0aGUgcm91dGUgd3JpdGluZywgYW5kIGNvbWVzIGJhY2sgYXNcbiAgICogYHRvb0xhcmdlYCByYXRoZXIgdGhhbiBoZWxkIGluIG1lbW9yeS5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgaGFuZGxlSW50ZXJuYWxseShcbiAgICByZXF1ZXN0OiBSdW50aW1lUmVxdWVzdCxcbiAgICBvcHRpb25zOiB7IHJlYWRvbmx5IG1heGltdW1Cb2R5PzogbnVtYmVyIH0sXG4gICk6IFByb21pc2U8e1xuICAgIGhlYWQ/OiBSZXNwb25zZUhlYWQ7XG4gICAgYm9keTogQnVmZmVyO1xuICAgIG90aGVyR3JvdXA/OiBSb3V0ZUluT3RoZXJHcm91cEVycm9yO1xuICAgIHRvb0xhcmdlOiBib29sZWFuO1xuICB9PiB7XG4gICAgbGV0IGhlYWQ6IFJlc3BvbnNlSGVhZCB8IHVuZGVmaW5lZDtcbiAgICBjb25zdCBjaHVua3M6IEJ1ZmZlcltdID0gW107XG4gICAgbGV0IHNpemUgPSAwO1xuICAgIGxldCB0b29MYXJnZSA9IGZhbHNlO1xuICAgIGxldCBvdGhlckdyb3VwOiBSb3V0ZUluT3RoZXJHcm91cEVycm9yIHwgdW5kZWZpbmVkO1xuICAgIGNvbnN0IG9uUm91dGVJbk90aGVyR3JvdXAgPSAoZXJyb3I6IFJvdXRlSW5PdGhlckdyb3VwRXJyb3IpID0+IHtcbiAgICAgIG90aGVyR3JvdXAgPSBlcnJvcjtcbiAgICB9O1xuICAgIGF3YWl0IHRoaXMuaGFuZGxlKFxuICAgICAgeyAuLi5yZXF1ZXN0LCBvblJvdXRlSW5PdGhlckdyb3VwIH0sXG4gICAgICB7XG4gICAgICAgIGJlZ2luKHJlc3BvbnNlSGVhZCkge1xuICAgICAgICAgIGhlYWQgPSByZXNwb25zZUhlYWQ7XG4gICAgICAgICAgcmV0dXJuIG5ldyBXcml0YWJsZSh7XG4gICAgICAgICAgICB3cml0ZShjaHVuazogQnVmZmVyLCBfZW5jb2RpbmcsIGNhbGxiYWNrKSB7XG4gICAgICAgICAgICAgIGlmIChvcHRpb25zLm1heGltdW1Cb2R5ID09PSB1bmRlZmluZWQpIHJldHVybiBjYWxsYmFjaygpO1xuICAgICAgICAgICAgICBzaXplICs9IGNodW5rLmJ5dGVMZW5ndGg7XG4gICAgICAgICAgICAgIGlmIChzaXplID4gb3B0aW9ucy5tYXhpbXVtQm9keSkge1xuICAgICAgICAgICAgICAgIHRvb0xhcmdlID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICByZXR1cm4gY2FsbGJhY2sobmV3IEVycm9yKFwiT3ZlciBpbWFnZXMubWF4aW11bVJlc3BvbnNlQm9keVwiKSk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgY2h1bmtzLnB1c2goQnVmZmVyLmZyb20oY2h1bmspKTtcbiAgICAgICAgICAgICAgY2FsbGJhY2soKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgICk7XG4gICAgcmV0dXJuIHtcbiAgICAgIGhlYWQsXG4gICAgICBib2R5OiB0b29MYXJnZSA/IEJ1ZmZlci5hbGxvYygwKSA6IEJ1ZmZlci5jb25jYXQoY2h1bmtzKSxcbiAgICAgIG90aGVyR3JvdXAsXG4gICAgICB0b29MYXJnZSxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEFwcCBSb3V0ZXIgYnVpbGRzIGFuIGludm9jYWJsZSBgL19ub3QtZm91bmRgLCBQYWdlcyBSb3V0ZXIgYSBwcmVyZW5kZXJlZFxuICAgKiBgNDA0Lmh0bWxgIChwZXIgbG9jYWxlIHVuZGVyIGkxOG4pIG9yIGVsc2UgYC9fZXJyb3JgOyBzZWUgYHN0YXR1c1RhcmdldHNgLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBzZW5kTm90Rm91bmQoXG4gICAgcmVxOiBTaGltSW5jb21pbmdNZXNzYWdlLFxuICAgIHJlczogU2hpbVNlcnZlclJlc3BvbnNlLFxuICAgIHdhaXRVbnRpbDogKHByb21pc2U6IFByb21pc2U8dW5rbm93bj4pID0+IHZvaWQsXG4gICAgdGFyZ2V0OiBTdGF0dXNUYXJnZXQsXG4gICAgLyoqXG4gICAgICogV2hhdCB0byByZW5kZXIgdGhlIDQwNCBhcy4gT21pdHRlZCBieSBgcmVuZGVyNDA0YCwgd2hpY2ggaXMgY2FsbGVkXG4gICAgICogbWlkLXJlbmRlciB3aXRoIGByZXEudXJsYCBhbHJlYWR5IHBvaW50aW5nIGF0IHRoZSByb3V0ZSB0aGF0IGdhdmUgdXAg4oCUXG4gICAgICogdGhlIHBhdGggTmV4dC5qcyB3b3VsZCByZW5kZXIgdGhhdCA0MDQgZm9yLlxuICAgICAqL1xuICAgIHJlcXVlc3RlZFVybD86IHN0cmluZyxcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgcmVzLnN0YXR1c0NvZGUgPSA0MDQ7XG5cbiAgICBpZiAodGFyZ2V0LmtpbmQgPT09IFwiZW50cnlwb2ludFwiKSB7XG4gICAgICBjb25zdCBoYW5kbGVyID0gYXdhaXQgdGhpcy5lbnRyeXBvaW50cy5sb2FkKHRhcmdldC5lbnRyeXBvaW50KTtcbiAgICAgIHJlcS51cmwgPSByZXF1ZXN0ZWRVcmwgPz8gcmVxLnVybDtcbiAgICAgIGF3YWl0IGhhbmRsZXIocmVxLCBhc1NlcnZlclJlc3BvbnNlKHJlcyksIHsgd2FpdFVudGlsIH0pO1xuICAgICAgaWYgKCFyZXMud3JpdGFibGVFbmRlZCkge1xuICAgICAgICByZXMuZW5kKCk7XG4gICAgICB9XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKFxuICAgICAgdGFyZ2V0LmtpbmQgPT09IFwic3RhdGljLWZpbGVcIiAmJlxuICAgICAgKGF3YWl0IHRoaXMuc2VuZEh0bWxGaWxlKHJlcywgdGFyZ2V0KSlcbiAgICApIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICByZXMuc2V0SGVhZGVyKFwiQ29udGVudC1UeXBlXCIsIFwidGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOFwiKTtcbiAgICByZXMuZW5kKFwiVGhpcyBwYWdlIGNvdWxkIG5vdCBiZSBmb3VuZC5cIik7XG4gIH1cblxuICAvKipcbiAgICogVHVybiBhIHRocm93biBlcnJvciBpbnRvIGEgcmVzcG9uc2UsIHJlbmRlcmluZyB0aGUgYXBwJ3MgZXJyb3IgcGFnZSB3aGVuIGl0XG4gICAqIGhhcyBvbmUuXG4gICAqXG4gICAqIE5leHQuanMncyBvd24gcGFnZSBoYW5kbGVycyBjYXRjaCwgcmVwb3J0IGFuZCB0aGVuICpyZXRocm93KiAoXCJyZXRocm93IHNvXG4gICAqIHRoYXQgd2UgY2FuIGhhbmRsZSBzZXJ2aW5nIGVycm9yIHBhZ2VcIiwgYHBhZ2VzLWhhbmRsZXIudHNgKSwgd2hpY2ggbWFrZXMgdGhlXG4gICAqIGVycm9yIHBhZ2UgdGhlIGhvc3QncyBqb2Ig4oCUIHRoZSBzYW1lIGRpdmlzaW9uIG9mIGxhYm9yIGFzIGByZW5kZXI0MDRgLiBXaXRob3V0XG4gICAqIHRoaXMgbGFkZGVyIGV2ZXJ5IHRocm93IGFuc3dlcmVkIGEgYmFyZSBgdGV4dC9wbGFpbmAgNTAwIGFuZCBhIGN1c3RvbVxuICAgKiBgcGFnZXMvX2Vycm9yYCB3YXMgZGVhZCBjb2RlIChgdGVzdC9lMmUvYXN5bmMtbW9kdWxlc2AsIHdob3NlIGAvbWFrZS1lcnJvcmBcbiAgICogdGhyb3dzIGluIGBnZXRTZXJ2ZXJTaWRlUHJvcHNgKS5cbiAgICpcbiAgICogT25jZSB0aGUgaGVhZCBpcyBvdXQgdGhlcmUgaXMgbm90aGluZyB0byBzYXkg4oCUIGRlc3Ryb3lpbmcgdGhlIHN0cmVhbSBpcyB3aGF0XG4gICAqIHRlbGxzIHRoZSBjbGllbnQgdGhlIHJlc3BvbnNlIGlzIHRydW5jYXRlZCByYXRoZXIgdGhhbiBjb21wbGV0ZS5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgc2VuZEVycm9yKFxuICAgIHJlcTogU2hpbUluY29taW5nTWVzc2FnZSxcbiAgICByZXM6IFNoaW1TZXJ2ZXJSZXNwb25zZSxcbiAgICB3YWl0VW50aWw6IChwcm9taXNlOiBQcm9taXNlPHVua25vd24+KSA9PiB2b2lkLFxuICAgIGVycm9yOiB1bmtub3duLFxuICAgIC8qKiBUaGUgcmVxdWVzdCwgYXMgcmVjZWl2ZWQ6IHdoaWNoIGxvY2FsZSdzIHByZXJlbmRlcmVkIDUwMCB0byBzZW5kLiAqL1xuICAgIHVybDogVVJMIHwgdW5kZWZpbmVkLFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zb2xlLmVycm9yKFwiVW5oYW5kbGVkIGVycm9yIHdoaWxlIGhhbmRsaW5nIHRoZSByZXF1ZXN0OlwiLCBlcnJvcik7XG4gICAgaWYgKHJlcy5oZWFkZXJzU2VudCkge1xuICAgICAgcmVzLmRlc3Ryb3koYXNFcnJvcihlcnJvcikpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICByZXMuc3RhdHVzQ29kZSA9IDUwMDtcbiAgICAvLyBBIGZhaWxlZCByZW5kZXIgbWF5IGhhdmUgc2V0IGhlYWRlcnMgZGVzY3JpYmluZyBhIGJvZHkgdGhhdCBuZXZlclxuICAgIC8vIGFycml2ZWQuIEV2ZXJ5IHJ1bmcgYmVsb3cgc2VuZHMgYSBkaWZmZXJlbnQgYm9keSwgc28gdGhleSBnbyBmaXJzdDogYVxuICAgIC8vIHN0YWxlIGBDb250ZW50LUxlbmd0aGAgb24gYSA1MDAuaHRtbCBvZiBhbm90aGVyIHNpemUgaGFuZ3MgdGhlIGNsaWVudCBvclxuICAgIC8vIGRlc3luY3MgYSBrZWVwLWFsaXZlIGNvbm5lY3Rpb24uXG4gICAgcmVzLnJlbW92ZUhlYWRlcihcIkNvbnRlbnQtTGVuZ3RoXCIpO1xuICAgIHJlcy5yZW1vdmVIZWFkZXIoXCJFVGFnXCIpO1xuICAgIC8vIEV2ZXJ5IHJ1bmcsIHRoZSBlcnJvci1wYWdlIGVudHJ5cG9pbnQgaW5jbHVkZWQ6IG90aGVyd2lzZSB0aGUgdGhyb3dpbmdcbiAgICAvLyByZW5kZXIncyBgcy1tYXhhZ2VgIChvciBhIGBoZWFkZXJzKClgIHJ1bGUncykgcmlkZXMgYWxvbmcgYW5kIENsb3VkRnJvbnRcbiAgICAvLyBjYWNoZXMgdGhlIDUwMC4gYG5leHQgc3RhcnRgIHNldHMgaXQgYmVmb3JlIHJlbmRlcmluZyB0aGUgZXJyb3IgcGFnZSB0b28uXG4gICAgcmVzLnNldEhlYWRlcihcIkNhY2hlLUNvbnRyb2xcIiwgTk9fU1RPUkUpO1xuICAgIGNvbnN0IHRhcmdldCA9IHRoaXMuZXJyb3JUYXJnZXRGb3IodXJsKTtcblxuICAgIGlmICh0YXJnZXQua2luZCA9PT0gXCJlbnRyeXBvaW50XCIpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IGhhbmRsZXIgPSBhd2FpdCB0aGlzLmVudHJ5cG9pbnRzLmxvYWQodGFyZ2V0LmVudHJ5cG9pbnQpO1xuICAgICAgICAvLyBgcmVxLnVybGAgaXMgbGVmdCBhbG9uZTogdGhlIGVycm9yIHBhZ2UgcmVuZGVycyBmb3IgdGhlIFVSTCB0aGF0IHdhc1xuICAgICAgICAvLyBhc2tlZCBmb3IsIGFuZCBgX2Vycm9yYCdzIGBnZXRJbml0aWFsUHJvcHNgIHJlYWRzIHRoZSBzdGF0dXMgb2ZmIGByZXNgLFxuICAgICAgICAvLyB3aGljaCBpcyB3aHkgdGhlIDUwMCBhYm92ZSBpcyBzZXQgZmlyc3QuXG4gICAgICAgIGF3YWl0IGhhbmRsZXIocmVxLCBhc1NlcnZlclJlc3BvbnNlKHJlcyksIHsgd2FpdFVudGlsIH0pO1xuICAgICAgICBpZiAoIXJlcy53cml0YWJsZUVuZGVkKSB7XG4gICAgICAgICAgcmVzLmVuZCgpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybjtcbiAgICAgIH0gY2F0Y2ggKGVycm9yUGFnZUVycm9yKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoXG4gICAgICAgICAgXCJUaGUgZXJyb3IgcGFnZSBpdHNlbGYgZmFpbGVkIHRvIHJlbmRlcjpcIixcbiAgICAgICAgICBlcnJvclBhZ2VFcnJvcixcbiAgICAgICAgKTtcbiAgICAgICAgaWYgKHJlcy5oZWFkZXJzU2VudCkge1xuICAgICAgICAgIHJlcy5kZXN0cm95KGFzRXJyb3IoZXJyb3JQYWdlRXJyb3IpKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoXG4gICAgICB0YXJnZXQua2luZCA9PT0gXCJzdGF0aWMtZmlsZVwiICYmXG4gICAgICAoYXdhaXQgdGhpcy5zZW5kSHRtbEZpbGUocmVzLCB0YXJnZXQpKVxuICAgICkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIEFnYWluOiBhbiBlcnJvciBwYWdlIHRoYXQgdGhyZXcgbWF5IGhhdmUgZGVzY3JpYmVkIGl0cyBvd24gYm9keSB0b28uXG4gICAgcmVzLnJlbW92ZUhlYWRlcihcIkNvbnRlbnQtTGVuZ3RoXCIpO1xuICAgIHJlcy5yZW1vdmVIZWFkZXIoXCJFVGFnXCIpO1xuICAgIHJlcy5zdGF0dXNDb2RlID0gNTAwO1xuICAgIHJlcy5zZXRIZWFkZXIoXCJDb250ZW50LVR5cGVcIiwgXCJ0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04XCIpO1xuICAgIHJlcy5zZXRIZWFkZXIoXCJDYWNoZS1Db250cm9sXCIsIE5PX1NUT1JFKTtcbiAgICByZXMuZW5kKFwiSW50ZXJuYWwgU2VydmVyIEVycm9yXCIpO1xuICB9XG5cbiAgLyoqXG4gICAqIEEgcHJlcmVuZGVyZWQgNDA0IG9yIDUwMCBwYWdlLCBvciBgZmFsc2VgIHdoZW4gaXQgY2Fubm90IGJlIHJlYWQgYW5kIHRoZVxuICAgKiBjYWxsZXIncyBwbGFpbi10ZXh0IGZhbGxiYWNrIHNob3VsZCBhbnN3ZXIuIFJlYWQgcmF0aGVyIHRoYW4gYHNlcnZlU3RhdGljYCxcbiAgICogd2hpY2gga2VlcHMgdGhlIHN0YXR1cyBhbHJlYWR5IHNldCBidXQgd291bGQgYW5zd2VyIGEgYFJhbmdlYCByZXF1ZXN0IHdpdGhcbiAgICogYSAyMDYgb2YgdGhlIGVycm9yIHBhZ2UsIGFuZCBhZGQgYEVUYWdgLCBgTGFzdC1Nb2RpZmllZGAgYW5kLCB3aGVyZSBub25lIGlzXG4gICAqIHNldCB5ZXQsIGEgcHVibGljIGBDYWNoZS1Db250cm9sYCB0byBpdC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgc2VuZEh0bWxGaWxlKFxuICAgIHJlczogU2hpbVNlcnZlclJlc3BvbnNlLFxuICAgIHRhcmdldDogeyByZWFkb25seSBmaWxlUGF0aDogc3RyaW5nIH0sXG4gICk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIGxldCBodG1sOiBCdWZmZXI7XG4gICAgdHJ5IHtcbiAgICAgIGh0bWwgPSBhd2FpdCByZWFkRmlsZShqb2luKHRoaXMub3B0aW9ucy5kZXBsb3ltZW50Um9vdCwgdGFyZ2V0LmZpbGVQYXRoKSk7XG4gICAgfSBjYXRjaCB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIHJlcy5zZXRIZWFkZXIoXCJDb250ZW50LVR5cGVcIiwgXCJ0ZXh0L2h0bWw7IGNoYXJzZXQ9dXRmLThcIik7XG4gICAgcmVzLmVuZChodG1sKTtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxufVxuXG4vKipcbiAqIFdoYXQgbmV4dCBzZW5kcyB3aXRoIGFuIGVycm9yIGl0IHJlbmRlcmVkIGl0c2VsZi4gV2l0aG91dCBpdCBhIGBDYWNoZS1Db250cm9sYFxuICogbGVmdCBiZWhpbmQgYnkgdGhlIHJlbmRlciB0aGF0IHRocmV3IGNhbiBnZXQgYSA1MDAgY2FjaGVkIGF0IHRoZSBlZGdlLlxuICovXG5jb25zdCBOT19TVE9SRSA9IFwicHJpdmF0ZSwgbm8tY2FjaGUsIG5vLXN0b3JlLCBtYXgtYWdlPTAsIG11c3QtcmV2YWxpZGF0ZVwiO1xuXG4vKipcbiAqIGBTZWMtRmV0Y2gtRGVzdGAgdmFsdWVzIHRoYXQgY2FuIG5ldmVyIGRpc3BsYXkgYW4gSFRNTCByZXNwb25zZSwgZnJvbVxuICogYG5leHQvZGlzdC9zZXJ2ZXIvbGliL2lzLW5vbi1odG1sLXNlYy1mZXRjaC1kZXN0LmpzYC4gUmVzdGF0ZWQgcmF0aGVyIHRoYW5cbiAqIHJlcXVpcmVkLCBiZWNhdXNlIHRoYXQgbW9kdWxlIGlzIHJlY2VudCBhbmQgdGhlIGFwcCdzIGBuZXh0YCBtYXkgcHJlZGF0ZSBpdDpcbiAqIGEgZmFpbGVkIHJlcXVpcmUgaGVyZSB3b3VsZCB0dXJuIGV2ZXJ5IDQwNCBpbnRvIGEgNTAwLiBFeGNsdWRlcyBgZG9jdW1lbnRgLFxuICogYGlmcmFtZWAgYW5kIHRoZSBsaWtlLCBhbmQgYGVtcHR5YCDigJQgYGZldGNoKClgLCB3aGljaCBpcyBob3cgUlNDIHJlcXVlc3RzIGdvLlxuICovXG5jb25zdCBOT05fSFRNTF9TRUNfRkVUQ0hfREVTVFM6IFJlYWRvbmx5U2V0PHN0cmluZz4gPSBuZXcgU2V0KFtcbiAgXCJhdWRpb1wiLFxuICBcImF1ZGlvd29ya2xldFwiLFxuICBcImZvbnRcIixcbiAgXCJpbWFnZVwiLFxuICBcImpzb25cIixcbiAgXCJtYW5pZmVzdFwiLFxuICBcInBhaW50d29ya2xldFwiLFxuICBcInJlcG9ydFwiLFxuICBcInNjcmlwdFwiLFxuICBcInNlcnZpY2V3b3JrZXJcIixcbiAgXCJzaGFyZWR3b3JrZXJcIixcbiAgXCJzdHlsZVwiLFxuICBcInRyYWNrXCIsXG4gIFwidmlkZW9cIixcbiAgXCJ3ZWJpZGVudGl0eVwiLFxuICBcIndvcmtlclwiLFxuICBcInhzbHRcIixcbl0pO1xuXG4vKipcbiAqIGBJTlRFUk5BTF9IRUFERVJTYCBmcm9tIGBuZXh0L2Rpc3Qvc2VydmVyL2xpYi9zZXJ2ZXItaXBjL3V0aWxzLmpzYCAoTmV4dFxuICogMTYuMyksIHdoaWNoIGBuZXh0IHN0YXJ0YCBkZWxldGVzIGZyb20gZXZlcnkgcmVxdWVzdCBiZWZvcmUgcm91dGluZyBpdFxuICogKGBmaWx0ZXJJbnRlcm5hbEhlYWRlcnNgIGluIGByb3V0ZXItc2VydmVyLmpzYCkuIFRoZXkgYXJlIHNpZ25hbHMgYmV0d2VlblxuICogTmV4dCdzIG93biByb3V0ZXIgYW5kIHJlbmRlciBsYXllcnM7IGhvbm9yaW5nIG9uZSBhIGNsaWVudCBzZW50IGxldHMgaXQgc3RlZXJcbiAqIHRob3NlIGxheWVycyDigJQgYG5leHQtcmVzdW1lYCBtYWtlcyBhIFBQUiBwYWdlIHJlc3VtZSBmcm9tIGEgcG9zdHBvbmVkIHN0YXRlXG4gKiB0aGUgY2xpZW50IHN1cHBsaWVzLiBSZXN0YXRlZCByYXRoZXIgdGhhbiByZXF1aXJlZCwgbGlrZVxuICoge0BsaW5rIE5PTl9IVE1MX1NFQ19GRVRDSF9ERVNUU30uXG4gKi9cbmNvbnN0IElOVEVSTkFMX0hFQURFUlM6IFJlYWRvbmx5U2V0PHN0cmluZz4gPSBuZXcgU2V0KFtcbiAgXCJ4LW1pZGRsZXdhcmUtcmV3cml0ZVwiLFxuICBcIngtbWlkZGxld2FyZS1yZWRpcmVjdFwiLFxuICBcIngtbWlkZGxld2FyZS1zZXQtY29va2llXCIsXG4gIFwieC1taWRkbGV3YXJlLXNraXBcIixcbiAgXCJ4LW1pZGRsZXdhcmUtb3ZlcnJpZGUtaGVhZGVyc1wiLFxuICBcIngtbWlkZGxld2FyZS1uZXh0XCIsXG4gIFwieC1ub3ctcm91dGUtbWF0Y2hlc1wiLFxuICBcIngtbWF0Y2hlZC1wYXRoXCIsXG4gIFwieC1uZXh0anMtZGF0YVwiLFxuICBcIngtbmV4dC1yZXN1bWUtc3RhdGUtbGVuZ3RoXCIsXG4gIFwibmV4dC1yZXN1bWVcIixcbl0pO1xuXG4vKipcbiAqIFRoZSByZXF1ZXN0IGhlYWRlcnMgd2l0aCB7QGxpbmsgSU5URVJOQUxfSEVBREVSU30gcmVtb3ZlZCwgYW5kIGB4LW5leHRqcy1kYXRhYFxuICogcHV0IGJhY2sgZm9yIGEgYF9uZXh0L2RhdGFgIHJlcXVlc3QsIHdoaWNoIGlzIHdoYXQgYG5leHQgc3RhcnRgJ3NcbiAqIGByZXNvbHZlUm91dGVzYCBkb2VzIChgc2V0SXNOZXh0RGF0YVJlcXVlc3RgKSByaWdodCBhZnRlciBzdHJpcHBpbmcgaXQ6IHRoZVxuICogaGVhZGVyIGlzIGludGVybmFsLCBidXQgTmV4dCdzIGhhbmRsZXJzIGFuZCBtaWRkbGV3YXJlIHN0aWxsIHJlYWQgaXQg4oCUIGl0IGlzXG4gKiBob3cgYSBkYXRhIHJlcXVlc3QgZ2V0cyBgeC1uZXh0anMtbWF0Y2hlZC1wYXRoYCDigJQgc28gYSByZWFsIGRhdGEgcmVxdWVzdCBoYXNcbiAqIHRvIGtlZXAgaXQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB3aXRob3V0SW50ZXJuYWxIZWFkZXJzKFxuICBoZWFkZXJzOiBJbmNvbWluZ0h0dHBIZWFkZXJzLFxuICB0YXJnZXQ6IHN0cmluZyxcbiAgYmFzZVBhdGg6IHN0cmluZyxcbik6IEluY29taW5nSHR0cEhlYWRlcnMge1xuICBjb25zdCBmaWx0ZXJlZDogSW5jb21pbmdIdHRwSGVhZGVycyA9IHt9O1xuICBmb3IgKGNvbnN0IFtuYW1lLCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoaGVhZGVycykpIHtcbiAgICBpZiAoIUlOVEVSTkFMX0hFQURFUlMuaGFzKG5hbWUpKSB7XG4gICAgICBmaWx0ZXJlZFtuYW1lXSA9IHZhbHVlO1xuICAgIH1cbiAgfVxuICBjb25zdCBwYXRobmFtZSA9IHdpdGhvdXRQYXRoUHJlZml4KHRhcmdldC5zcGxpdChcIj9cIilbMF0sIGJhc2VQYXRoKTtcbiAgaWYgKHBhdGhuYW1lLnN0YXJ0c1dpdGgoXCIvX25leHQvZGF0YS9cIikgJiYgcGF0aG5hbWUuZW5kc1dpdGgoXCIuanNvblwiKSkge1xuICAgIGZpbHRlcmVkW1wieC1uZXh0anMtZGF0YVwiXSA9IFwiMVwiO1xuICB9XG4gIHJldHVybiBmaWx0ZXJlZDtcbn1cblxuLyoqIEV2ZXJ5IHJvdXRlIHRvIGludmFsaWRhdGUsIGVhY2ggb25jZSwgaW4gb25lIENsb3VkRnJvbnQgaW52YWxpZGF0aW9uLiAqL1xudHlwZSBSZXZhbGlkYXRlZFBhZ2VIb29rID0gKHJvdXRlczogcmVhZG9ubHkgc3RyaW5nW10pID0+IFByb21pc2U8dm9pZD47XG5cbi8qKlxuICogSW52YWxpZGF0ZSB0aGUgQ0ROIGNvcGllcyBvZiBhIHBhZ2UgYHJlcy5yZXZhbGlkYXRlKClgIGp1c3QgcmVnZW5lcmF0ZWQ6IGl0c1xuICogSFRNTCBhbmQgUlNDIHBheWxvYWQsIGFuZCBpdHMgUGFnZXMgUm91dGVyIGBfbmV4dC9kYXRhYCBKU09OLiBXaXRob3V0IHRoaXMgdGhlXG4gKiBmcmVzaCBlbnRyeSBzYXQgYmVoaW5kIENsb3VkRnJvbnQncyBjb3B5IG9mIHRoZSBvbGQgb25lIGZvciB0aGUgcGFnZSdzIHdob2xlXG4gKiBgcy1tYXhhZ2VgIOKAlCBhIHllYXIsIGZvciBhbiBvbi1kZW1hbmQtb25seSBJU1IgcGFnZS4gYHJldmFsaWRhdGVQYXRoYCBhbmRcbiAqIGByZXZhbGlkYXRlVGFnYCBnbyB0aHJvdWdoIHRoZSBjYWNoZSBoYW5kbGVyJ3MgYHJldmFsaWRhdGVUYWdgLCB3aGljaCBhbHJlYWR5XG4gKiBpbnZhbGlkYXRlczsgYHJlcy5yZXZhbGlkYXRlKClgIGRvZXMgbm90IHRvdWNoIGl0LlxuICpcbiAqIEV2ZXJ5IHJvdXRlIGlzIHdpdGhvdXQgYGJhc2VQYXRoYCwgd2hpY2ggdGhlIGhvb2sgcHJlZml4ZXMuIEEgbm8tb3Agb3V0c2lkZVxuICogdGhlIEdsb2JhbCBjb25zdHJ1Y3RzLCB3aGVyZSBubyBob29rIGlzIHJlZ2lzdGVyZWQuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGludmFsaWRhdGVSZXZhbGlkYXRlZFBhZ2UoXG4gIHVybFBhdGg6IHN0cmluZyxcbiAgbWFuaWZlc3Q6IEFkYXB0ZXJNYW5pZmVzdCxcbik6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBob29rID0gKGdsb2JhbFRoaXMgYXMgUmVjb3JkPHN5bWJvbCwgdW5rbm93bj4pW1xuICAgIFJFVkFMSURBVEVEX1BBR0VfSE9PS1xuICBdIGFzIFJldmFsaWRhdGVkUGFnZUhvb2sgfCB1bmRlZmluZWQ7XG4gIGlmICh0eXBlb2YgaG9vayAhPT0gXCJmdW5jdGlvblwiKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIGNvbnN0IHJvdXRlID0gd2l0aG91dFBhdGhQcmVmaXgoXG4gICAgdXJsUGF0aC5zcGxpdChcIj9cIilbMF0sXG4gICAgbWFuaWZlc3QuY29uZmlnLmJhc2VQYXRoLFxuICApO1xuICB0cnkge1xuICAgIC8vIE9uZSBjYWxsLCBkZWR1cGxpY2F0ZWQ6IHRoZSBkZWZhdWx0IGxvY2FsZSdzIHR3byBwYWlycyBzaGFyZSBhIGRhdGFcbiAgICAvLyByb3V0ZSwgYW5kIGVhY2ggaW52YWxpZGF0aW9uIHBhdGggY291bnRzIGFnYWluc3QgQ2xvdWRGcm9udCdzIHF1b3RhLlxuICAgIGF3YWl0IGhvb2soWy4uLm5ldyBTZXQocmV2YWxpZGF0ZWRQYWdlUm91dGVzKHJvdXRlLCBtYW5pZmVzdCkuZmxhdCgpKV0pO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIC8vIFRoZSByZWdlbmVyYXRpb24gaXRzZWxmIHN1Y2NlZWRlZDsgdGhlIGVkZ2UgY2F0Y2hlcyB1cCBhdCBgcy1tYXhhZ2VgLlxuICAgIGNvbnNvbGUud2FybihgQ291bGQgbm90IGludmFsaWRhdGUgdGhlIENETiBjb3B5IG9mICR7dXJsUGF0aH06YCwgZXJyb3IpO1xuICB9XG59XG5cbi8qKlxuICogVGhlIGBbcm91dGUsIGRhdGFSb3V0ZV1gIHBhaXJzIGEgcmV2YWxpZGF0ZWQgcGFnZSBpcyBjYWNoZWQgdW5kZXIgYXQgdGhlIGVkZ2UuXG4gKlxuICogSW4gYW4gaTE4biBhcHAgdGhlIE5leHQgY2xpZW50IGFsd2F5cyBwdXRzIHRoZSBsb2NhbGUgaW4gYSBkYXRhIGhyZWYsIHRoZVxuICogZGVmYXVsdCBsb2NhbGUgaW5jbHVkZWQgKGAvX25leHQvZGF0YS88aWQ+L2VuL2Jsb2cuanNvbmAsIGFuZCBg4oCmL2VuLmpzb25gIGZvclxuICogdGhlIHJvb3QpLCBzbyB0aGUgdW5wcmVmaXhlZCBkYXRhIHJvdXRlIG5hbWVzIG5vdGhpbmcgQ2xvdWRGcm9udCBob2xkcy4gVGhlXG4gKiBwYWdlIGlzIG9uZSBsb2NhbGUncyDigJQgYHJlcy5yZXZhbGlkYXRlKFwiL2Jsb2dcIilgIHJlZ2VuZXJhdGVzIHRoZSBkZWZhdWx0XG4gKiBsb2NhbGUncywgYFwiL2ZyL2Jsb2dcImAgRnJlbmNoIOKAlCBhbmQgdGhlIGRlZmF1bHQgbG9jYWxlJ3MgSFRNTCBpcyByZWFjaGFibGUsXG4gKiBhbmQgY2FjaGVkLCBib3RoIHdpdGggYW5kIHdpdGhvdXQgaXRzIHByZWZpeCwgc28gaXRzIHR3byBwYWlycyBzaGFyZSBhIGRhdGFcbiAqIHJvdXRlLCB3aGljaCB0aGUgY2FsbGVyIHNlbmRzIG9uY2UuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXZhbGlkYXRlZFBhZ2VSb3V0ZXMoXG4gIHJvdXRlOiBzdHJpbmcsXG4gIG1hbmlmZXN0OiBQaWNrPEFkYXB0ZXJNYW5pZmVzdCwgXCJidWlsZElkXCIgfCBcImNvbmZpZ1wiPixcbik6IEFycmF5PFtzdHJpbmcsIHN0cmluZ10+IHtcbiAgY29uc3QgZGF0YVJvdXRlID0gKHBhZ2U6IHN0cmluZykgPT5cbiAgICBgL19uZXh0L2RhdGEvJHttYW5pZmVzdC5idWlsZElkfSR7XG4gICAgICBwYWdlID09PSBcIi9cIiA/IFwiL2luZGV4XCIgOiBwYWdlLnJlcGxhY2UoL1xcLyskLywgXCJcIilcbiAgICB9Lmpzb25gO1xuICBjb25zdCBpMThuID0gbWFuaWZlc3QuY29uZmlnLmkxOG4gYXMge1xuICAgIHJlYWRvbmx5IGxvY2FsZXM/OiByZWFkb25seSBzdHJpbmdbXTtcbiAgICByZWFkb25seSBkZWZhdWx0TG9jYWxlPzogc3RyaW5nO1xuICB9IHwgbnVsbDtcbiAgaWYgKCFpMThuPy5kZWZhdWx0TG9jYWxlKSB7XG4gICAgcmV0dXJuIFtbcm91dGUsIGRhdGFSb3V0ZShyb3V0ZSldXTtcbiAgfVxuICBjb25zdCB7IGRlZmF1bHRMb2NhbGUgfSA9IGkxOG47XG4gIGNvbnN0IHNlZ21lbnQgPSByb3V0ZS5zcGxpdChcIi9cIilbMV0gPz8gXCJcIjtcbiAgY29uc3QgbG9jYWxlID0gKGkxOG4ubG9jYWxlcyA/PyBbXSkuaW5jbHVkZXMoc2VnbWVudClcbiAgICA/IHNlZ21lbnRcbiAgICA6IGRlZmF1bHRMb2NhbGU7XG4gIGNvbnN0IHBhZ2UgPVxuICAgIGxvY2FsZSA9PT0gc2VnbWVudCA/IHdpdGhvdXRQYXRoUHJlZml4KHJvdXRlLCBgLyR7c2VnbWVudH1gKSA6IHJvdXRlO1xuICBjb25zdCBsb2NhbGl6ZWQgPSBwYWdlID09PSBcIi9cIiA/IGAvJHtsb2NhbGV9YCA6IGAvJHtsb2NhbGV9JHtwYWdlfWA7XG4gIC8vIFRoZSByb290J3MgZGF0YSByb3V0ZSBpcyB0aGUgbG9jYWxlIGl0c2VsZiwgbm90IGAvPGxvY2FsZT4vaW5kZXhgLlxuICBjb25zdCBsb2NhbGl6ZWREYXRhID0gYC9fbmV4dC9kYXRhLyR7bWFuaWZlc3QuYnVpbGRJZH0ke2xvY2FsaXplZC5yZXBsYWNlKC9cXC8rJC8sIFwiXCIpfS5qc29uYDtcbiAgcmV0dXJuIGxvY2FsZSA9PT0gZGVmYXVsdExvY2FsZVxuICAgID8gW1xuICAgICAgICBbcGFnZSwgbG9jYWxpemVkRGF0YV0sXG4gICAgICAgIFtsb2NhbGl6ZWQsIGxvY2FsaXplZERhdGFdLFxuICAgICAgXVxuICAgIDogW1tsb2NhbGl6ZWQsIGxvY2FsaXplZERhdGFdXTtcbn1cblxuZnVuY3Rpb24gYXNFcnJvcihlcnJvcjogdW5rbm93bik6IEVycm9yIHtcbiAgcmV0dXJuIGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvciA6IG5ldyBFcnJvcihTdHJpbmcoZXJyb3IpKTtcbn1cblxuLyoqXG4gKiBSZWFkIHRoZSBtYW5pZmVzdCBhbmQgcGluIHRoZSBsYXlvdXQgaXQgZGVzY3JpYmVzLlxuICpcbiAqIGBwcm9jZXNzLmN3ZCgpYCBpcyBsb2FkLWJlYXJpbmcgYW5kIHRoZXJlIGlzIGV4YWN0bHkgb25lIGNoYW5jZSB0byBnZXQgaXRcbiAqIHJpZ2h0OiBOZXh0LmpzIGlubGluZXMgYHJlbGF0aXZlKGJ1aWxkQ3dkLCBwcm9qZWN0RGlyKWAgaW50byBldmVyeSBlbnRyeXBvaW50XG4gKiBhbmQgcmVzb2x2ZXMgaXQgYWdhaW5zdCB0aGUgKnJ1bnRpbWUqIGN3ZCB0byBmaW5kXG4gKiBgcmVxdWlyZWQtc2VydmVyLWZpbGVzLmpzb25gLCB0aGUgcHJlcmVuZGVyIG1hbmlmZXN0LCBhbmQgdGhlIGFwcCdzIGNodW5rcy5cbiAqIGBhc3NlcnRCdWlsZEN3ZGAgaW4gYGJ1aWxkLW91dHB1dHMudHNgIGtlZXBzIHRoYXQgaW5saW5lZCB2YWx1ZSBgXCJcImAsIHNvIHRoZVxuICogd2hvbGUgaW52YXJpYW50IHJlZHVjZXMgdG8gXCJjd2QgaXMgdGhlIHN0YWdlZCBwcm9qZWN0IGRpclwiIOKAlCBhc3NlcnRlZCByYXRoZXJcbiAqIHRoYW4gYXNzdW1lZCwgYmVjYXVzZSBnZXR0aW5nIGl0IHdyb25nIHByb2R1Y2VzIGFcbiAqIGBDYW5ub3QgZmluZCBtb2R1bGUg4oCmL3JlcXVpcmVkLXNlcnZlci1maWxlcy5qc29uYCBmcm9tIGluc2lkZSBjb21waWxlZCBOZXh0LmpzXG4gKiBjb2RlIHdpdGggbm8gaW5kaWNhdGlvbiB0aGF0IHRoZSBsYXlvdXQgaXMgdGhlIHByb2JsZW0uXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBsb2FkUnVudGltZShcbiAgZGVwbG95bWVudFJvb3Q6IHN0cmluZyxcbik6IFByb21pc2U8TmV4dGpzUnVudGltZT4ge1xuICBjb25zdCBtYW5pZmVzdFBhdGggPSBkZXBsb3llZE1hbmlmZXN0UGF0aChkZXBsb3ltZW50Um9vdCk7XG4gIGxldCBtYW5pZmVzdDogQWRhcHRlck1hbmlmZXN0O1xuICB0cnkge1xuICAgIG1hbmlmZXN0ID0gSlNPTi5wYXJzZShhd2FpdCByZWFkRmlsZShtYW5pZmVzdFBhdGgsIFwidXRmLThcIikpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGBDb3VsZCBub3QgcmVhZCB0aGUgY2RrLW5leHRqcyBhZGFwdGVyIG1hbmlmZXN0IGF0IFwiJHttYW5pZmVzdFBhdGh9XCIuIGAgK1xuICAgICAgICBgVGhlIGRlcGxveW1lbnQgcGFja2FnZSBpcyBpbmNvbXBsZXRlOiBzeW50aCBjb3BpZXMgYCArXG4gICAgICAgIGAke01BTklGRVNUX0ZJTEVfTkFNRX0gaW4gbmV4dCB0byB0aGUgYnVuZGxlZCBydW50aW1lLmAsXG4gICAgICB7IGNhdXNlOiBlcnJvciB9LFxuICAgICk7XG4gIH1cblxuICBjb25zdCBwcm9qZWN0RGlyID0gam9pbihkZXBsb3ltZW50Um9vdCwgbWFuaWZlc3QucmVsYXRpdmVQcm9qZWN0RGlyKTtcbiAgY29uc3QgcHJvYmUgPSBqb2luKFxuICAgIHByb2plY3REaXIsXG4gICAgbWFuaWZlc3QuY29uZmlnLmRpc3REaXIsXG4gICAgXCJyZXF1aXJlZC1zZXJ2ZXItZmlsZXMuanNvblwiLFxuICApO1xuICB0cnkge1xuICAgIGF3YWl0IHJlYWRGaWxlKHByb2JlKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgVGhlIGRlcGxveW1lbnQgcm9vdCBcIiR7ZGVwbG95bWVudFJvb3R9XCIgZG9lcyBub3QgY29udGFpbiB0aGUgc3RhZ2VkIGAgK1xuICAgICAgICBgTmV4dC5qcyBwcm9qZWN0OiBleHBlY3RlZCBcIiR7cHJvYmV9XCIgdG8gZXhpc3QuIEV2ZXJ5IGJ1aWx0IGVudHJ5cG9pbnQgYCArXG4gICAgICAgIGByZXNvbHZlcyBpdHMgb3duIHBhdGhzIGFnYWluc3QgcHJvY2Vzcy5jd2QoKSwgc28gdGhpcyBsYXlvdXQgaXMgcGFydCBgICtcbiAgICAgICAgYG9mIHRoZSBjb250cmFjdCwgbm90IGEgY29udmVuaWVuY2UuYCxcbiAgICAgIHsgY2F1c2U6IGVycm9yIH0sXG4gICAgKTtcbiAgfVxuICAvLyBgbmV4dCBzdGFydGAgZm9yY2VzIHRoaXMsIGFuZCBleHRlcm5hbGl6ZWQgcGFja2FnZXMgKFJlYWN0IGZvciBQYWdlcyBSb3V0ZXJcbiAgLy8gU1NSLCBhbnl0aGluZyBpbiBgc2VydmVyRXh0ZXJuYWxQYWNrYWdlc2ApIHBpY2sgdGhlaXIgZGV2IG9yIHByb2QgYnVpbGQgZnJvbVxuICAvLyBpdCBhdCByZXF1aXJlIHRpbWUuIFRoZSBDb250YWluZXJzIERvY2tlcmZpbGVzIHNldCBpdCBhbmQgdGhlIEZ1bmN0aW9uc1xuICAvLyBjb25zdHJ1Y3RzIGRvIHRvbzsgdGhpcyBjb3ZlcnMgYSBoYW5kLXdpcmVkIGZ1bmN0aW9uLiBCZWZvcmUgYW55IGByZXF1aXJlYC5cbiAgLy8gKGBuZXh0YCdzIHR5cGluZ3MgZGVjbGFyZSBOT0RFX0VOViByZWFkLW9ubHkuKVxuICAocHJvY2Vzcy5lbnYgYXMgUmVjb3JkPHN0cmluZywgc3RyaW5nIHwgdW5kZWZpbmVkPikuTk9ERV9FTlYgPz89IFwicHJvZHVjdGlvblwiO1xuICBwcm9jZXNzLmNoZGlyKHByb2plY3REaXIpO1xuICAvLyBCZWZvcmUgYW55dGhpbmcgY2FuIHNlcnZlIGEgcmVxdWVzdDogdGhlIHJ1bnRpbWUncyBvd24gYG5leHRgIGltcG9ydHMgcmVzb2x2ZVxuICAvLyBvdXQgb2YgdGhlIHN0YWdlZCBhcHAsIG5vdCBvdXQgb2YgdGhlIHNoZWxsJ3MgZGlyZWN0b3J5LiBTZWUgYG5leHQtbW9kdWxlcy50c2AuXG4gIHVzZU5leHRGcm9tKHByb2plY3REaXIpO1xuICAvLyBCZWZvcmUgYW55IGVudHJ5cG9pbnQgY2FuIHJlYWQgdGhlbSBhdCBtb2R1bGUgc2NvcGUuXG4gIGxvYWRFbnZGaWxlcyhwcm9qZWN0RGlyKTtcbiAgLy8gVGhlbiwgYmVmb3JlIGFueSBlbnRyeXBvaW50IChvciBtaWRkbGV3YXJlKSBjYW4gYmUgbG9hZGVkOiBOZXh0J3Mgb3duXG4gIC8vIG5vZGUtZW52aXJvbm1lbnQgYm9vdHN0cmFwLiBTZWUgYHNldHVwTm9kZUVudmlyb25tZW50YC5cbiAgc2V0dXBOb2RlRW52aXJvbm1lbnQoKTtcbiAgLy8gQW5kIGxhc3QsIG9uY2UgdGhlIGVudmlyb25tZW50IGlzIHJlYWR5OiBgcmVnaXN0ZXIoKWAsIGJlZm9yZSBhbnkgZW50cnlwb2ludFxuICAvLyBvciBtaWRkbGV3YXJlIG1vZHVsZSBpcyBldmFsdWF0ZWQuIFNlZSBgcmVnaXN0ZXJJbnN0cnVtZW50YXRpb25gLlxuICBhd2FpdCByZWdpc3Rlckluc3RydW1lbnRhdGlvbihwcm9qZWN0RGlyLCBtYW5pZmVzdC5jb25maWcuZGlzdERpcik7XG5cbiAgcmV0dXJuIG5ldyBOZXh0anNSdW50aW1lKHtcbiAgICBkZXBsb3ltZW50Um9vdCxcbiAgICBtYW5pZmVzdCxcbiAgICBidWNrZXQ6IHByb2Nlc3MuZW52LkNES19ORVhUSlNfU1RBVElDX0FTU0VUU19CVUNLRVRfTkFNRSxcbiAgICBidWNrZXRLZXlQcmVmaXg6IHByb2Nlc3MuZW52LkNES19ORVhUSlNfU1RBVElDX0FTU0VUU19LRVlfUFJFRklYLFxuICB9KTtcbn1cblxuLyoqXG4gKiBBIHN5bnRhY3RpY2FsbHkgdmFsaWQgYGhvc3RbOnBvcnRdYDogYSByZWdpc3RlcmVkIG5hbWUgb3IgYSBicmFja2V0ZWQgSVB2NlxuICogbGl0ZXJhbCwgb3B0aW9uYWxseSB3aXRoIGEgcG9ydC4gQW55dGhpbmcgZWxzZSBjYW5ub3QgYmUgdGhlIGF1dGhvcml0eSBvZiBhXG4gKiBVUkwuXG4gKi9cbmNvbnN0IEhPU1RfQVVUSE9SSVRZID0gL14oPzpcXFtbMC05QS1GYS1mOi5dK1xcXXxbMC05QS1aYS16Ll8tXSspKD86OlxcZHsxLDV9KT8kLztcblxuLyoqXG4gKiBUaGUgZmlyc3QgdmFsdWUgdGhhdCBpcyBhY3R1YWxseSB1c2FibGUgYXMgYW4gYXV0aG9yaXR5LCBvciBgdW5kZWZpbmVkYC5cbiAqXG4gKiBgeC1mb3J3YXJkZWQtaG9zdGAgY2FuIGFycml2ZSBhcyBhIGxpc3Qgd2hlbiBtb3JlIHRoYW4gb25lIHByb3h5IGFwcGVuZHMgdG9cbiAqIGl0LiBWYWxpZGF0aW5nIGlzIHdoYXQga2VlcHMgYSBtYWxmb3JtZWQgdmFsdWUgZnJvbSBtYWtpbmcgdGhlIGBVUkxgXG4gKiBjb25zdHJ1Y3RvciB0aHJvdyDigJQgd2hpY2ggb24gdGhlIExhbWJkYSBzaGVsbHMgaXMgYW4gaW52b2NhdGlvbiBlcnJvciBhbmQgYVxuICogNTAyLCBub3QgYSByZXNwb25zZS5cbiAqL1xuZnVuY3Rpb24gdmFsaWRBdXRob3JpdHkoXG4gIHZhbHVlOiBzdHJpbmcgfCBzdHJpbmdbXSB8IHVuZGVmaW5lZCxcbik6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IGNhbmRpZGF0ZSA9IGZpcnN0VmFsdWUodmFsdWUpPy5zcGxpdChcIixcIilbMF0udHJpbSgpO1xuICByZXR1cm4gY2FuZGlkYXRlICYmIEhPU1RfQVVUSE9SSVRZLnRlc3QoY2FuZGlkYXRlKSA/IGNhbmRpZGF0ZSA6IHVuZGVmaW5lZDtcbn1cblxuLyoqXG4gKiBgcmVzb2x2ZVJvdXRlc2AgbmVlZHMgYW4gYWJzb2x1dGUgVVJMLCBhbmQgaXQgYmVjb21lcyB0aGUgb3JpZ2luIGV2ZXJ5XG4gKiBhYnNvbHV0ZSBVUkwgdGhlIGFwcCBidWlsZHMgc3RhcnRzIHdpdGgg4oCUIGByZXEubmV4dFVybC5vcmlnaW5gIGluIG1pZGRsZXdhcmUsXG4gKiBgaW5pdFVSTGAgZm9yIGEgcm91dGUgaGFuZGxlciDigJQgc28gaXRzIGhvc3QgaGFzIHRvIGJlIG9uZSB0aGUgY2xpZW50IGNhbm5vdFxuICogcGljay5cbiAqXG4gKiBUaGF0IGlzIGBIb3N0YCBvbiBldmVyeSBkZXBsb3ltZW50IGJ1dCBvbmU6IENsb3VkRnJvbnQgZm9yd2FyZHMgdGhlIHZpZXdlcidzXG4gKiBgSG9zdGAgdG8gYSBjb250YWluZXIgb3JpZ2luIChgQUxMX1ZJRVdFUmApLCBhbiBBTEIgcGFzc2VzIGl0IHRocm91Z2gsIGFuZCBBUElcbiAqIEdhdGV3YXkgc2V0cyBpdHMgb3duIGRvbWFpbi4gVGhlIGV4Y2VwdGlvbiBpcyBhIExhbWJkYSBGdW5jdGlvbiBVUkwsIHdoZXJlXG4gKiBDbG91ZEZyb250IGhhcyB0byBzZW5kIHRoZSBVUkwncyBvd24gZG9tYWluIGFzIGBIb3N0YCBhbmQgcHV0cyB0aGUgdmlld2VyJ3MgaW5cbiAqIGB4LWZvcndhcmRlZC1ob3N0YDsgc2VlIHtAbGluayBSdW50aW1lUmVxdWVzdC50cnVzdEZvcndhcmRlZEhvc3R9LiBBbnl3aGVyZVxuICogZWxzZSB0aGUgaGVhZGVyIGlzIHRoZSBjbGllbnQncywgYW5kIGhvbm9yaW5nIGl0IGxldCBhIHJlcXVlc3Qgd2l0aFxuICogYFgtRm9yd2FyZGVkLUhvc3Q6IGV2aWwuZXhhbXBsZWAgcG9pbnQgYSBwYXNzd29yZC1yZXNldCBsaW5rIGF0IGV2aWwuZXhhbXBsZSDigJRcbiAqIGFuZCwgb24gR2xvYmFsIENvbnRhaW5lcnMsIGhhdmUgQ2xvdWRGcm9udCBjYWNoZSB0aGF0IHBhZ2UsIHNpbmNlIHRoZSBoZWFkZXJcbiAqIGlzIG5vdCBpbiBpdHMgY2FjaGUga2V5LlxuICovXG5mdW5jdGlvbiBhYnNvbHV0ZVVybChyZXF1ZXN0OiBSdW50aW1lUmVxdWVzdCk6IFVSTCB7XG4gIGNvbnN0IGhvc3QgPVxuICAgIChyZXF1ZXN0LnRydXN0Rm9yd2FyZGVkSG9zdFxuICAgICAgPyB2YWxpZEF1dGhvcml0eShyZXF1ZXN0LmhlYWRlcnNbXCJ4LWZvcndhcmRlZC1ob3N0XCJdKVxuICAgICAgOiB1bmRlZmluZWQpID8/XG4gICAgdmFsaWRBdXRob3JpdHkocmVxdWVzdC5oZWFkZXJzLmhvc3QpID8/XG4gICAgXCJsb2NhbGhvc3RcIjtcbiAgLy8gQmVoaW5kIENsb3VkRnJvbnQgb24gR2xvYmFsIENvbnRhaW5lcnMgdGhlIGNvbnRhaW5lciBzaGVsbCBoYXMgYWxyZWFkeSBwdXRcbiAgLy8gdGhlIHZpZXdlcidzIHByb3RvY29sIGhlcmU7IHNlZSBgdHJ1c3RDbG91ZEZyb250UHJvdG9gIGluIGh0dHAvbm9kZS1zZXJ2ZXIudHMuXG4gIGNvbnN0IGZvcndhcmRlZFByb3RvID0gZmlyc3RWYWx1ZShyZXF1ZXN0LmhlYWRlcnNbXCJ4LWZvcndhcmRlZC1wcm90b1wiXSlcbiAgICA/LnNwbGl0KFwiLFwiKVswXVxuICAgIC50cmltKClcbiAgICAudG9Mb3dlckNhc2UoKTtcbiAgLy8gQ2xpZW50LXN1cHBsaWVkIHdoZXJlIG5vIHByb3h5IG92ZXJ3cml0ZXMgaXQsIGJ1dCBoYXJtbGVzczogdGhlIGhvc3QgaXNcbiAgLy8gYWxyZWFkeSBmaXhlZCwgYW5kIGFueXRoaW5nIGJ1dCB0aGVzZSB0d28gd291bGQgbm90IHBhcnNlLlxuICBjb25zdCBwcm90byA9XG4gICAgZm9yd2FyZGVkUHJvdG8gPT09IFwiaHR0cFwiIHx8IGZvcndhcmRlZFByb3RvID09PSBcImh0dHBzXCJcbiAgICAgID8gZm9yd2FyZGVkUHJvdG9cbiAgICAgIDogcmVxdWVzdC5lbmNyeXB0ZWQgPT09IGZhbHNlXG4gICAgICAgID8gXCJodHRwXCJcbiAgICAgICAgOiBcImh0dHBzXCI7XG4gIHJldHVybiBuZXcgVVJMKHJlcXVlc3QudXJsLCBgJHtwcm90b306Ly8ke2hvc3R9YCk7XG59XG5cbmZ1bmN0aW9uIHBpY2tEZWZpbmVkKFxuICBoZWFkZXJzOiBJbmNvbWluZ0h0dHBIZWFkZXJzLFxuICBuYW1lczogcmVhZG9ubHkgc3RyaW5nW10sXG4pOiBJbmNvbWluZ0h0dHBIZWFkZXJzIHtcbiAgY29uc3QgcGlja2VkOiBJbmNvbWluZ0h0dHBIZWFkZXJzID0ge307XG4gIGZvciAoY29uc3QgbmFtZSBvZiBuYW1lcykge1xuICAgIGlmIChoZWFkZXJzW25hbWVdICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIHBpY2tlZFtuYW1lXSA9IGhlYWRlcnNbbmFtZV07XG4gICAgfVxuICB9XG4gIHJldHVybiBwaWNrZWQ7XG59XG5cbi8qKlxuICogYG5vcm1hbGl6ZVJlcGVhdGVkU2xhc2hlc2AgKGBuZXh0L2Rpc3Qvc2hhcmVkL2xpYi91dGlscy5qc2ApLCBhcHBsaWVkIHdoZXJlXG4gKiBOZXh0LmpzIGFwcGxpZXMgaXQ6IGJlZm9yZSByb3V0aW5nLCBhcyBhIDMwOCB0byB0aGUgY29sbGFwc2VkIHBhdGguXG4gKiBgYmFzZS1zZXJ2ZXIudHNgIGRvZXMgYGlmICh1cmxOb1F1ZXJ5Py5tYXRjaCgvKFxcXFx8XFwvXFwvKS8pKVxuICogcmVzLnJlZGlyZWN0KG5vcm1hbGl6ZVJlcGVhdGVkU2xhc2hlcyhyZXEudXJsKSwgMzA4KWAsIHNvIGAvYS8vYmAgYW5kIGAvYVxcYmBcbiAqIGJvdGggYW5zd2VyIGEgcmVkaXJlY3QgdG8gYC9hL2JgIHJhdGhlciB0aGFuIGEgNDA0IC0gYW5kIGBAbmV4dC9yb3V0aW5nYCdzXG4gKiBgcmVzb2x2ZVJvdXRlc2AgZG9lcyBub3QgZG8gaXQgZm9yIHVzLCB3aGljaCBpcyBob3cgYC8vYCByZWFjaGVkIHRoaXMgcnVudGltZSBhc1xuICogYSA1MDAgYW5kIGAvYXBpLy9qc29uYCBhcyBhIDQwNC5cbiAqXG4gKiBFbmNvZGVkIGJhY2tzbGFzaGVzIGFyZSBsZWZ0IGFsb25lLCBhcyBOZXh0LmpzIGxlYXZlcyB0aGVtOiBgJTVDYCBpcyBhIGxpdGVyYWxcbiAqIGNoYXJhY3RlciBpbiBhIHBhdGggc2VnbWVudCwgbm90IGEgc2VwYXJhdG9yLlxuICpcbiAqIFJldHVybnMgYHVuZGVmaW5lZGAgd2hlbiB0aGVyZSBpcyBub3RoaW5nIHRvIGNvbGxhcHNlLCB3aGljaCBpcyB0aGUgY29tbW9uIGNhc2UuXG4gKi9cbmZ1bmN0aW9uIGNvbGxhcHNlUmVwZWF0ZWRTbGFzaGVzKHRhcmdldDogc3RyaW5nKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgY29uc3QgcGFydHMgPSB0YXJnZXQuc3BsaXQoXCI/XCIpO1xuICBjb25zdCBwYXRobmFtZSA9IHBhcnRzWzBdO1xuICBpZiAoIS9cXFxcfFxcL1xcLy8udGVzdChwYXRobmFtZSkpIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG4gIGNvbnN0IHF1ZXJ5ID0gcGFydHMubGVuZ3RoID4gMSA/IGA/JHtwYXJ0cy5zbGljZSgxKS5qb2luKFwiP1wiKX1gIDogXCJcIjtcbiAgcmV0dXJuIHBhdGhuYW1lLnJlcGxhY2UoL1xcXFwvZywgXCIvXCIpLnJlcGxhY2UoL1xcL1xcLysvZywgXCIvXCIpICsgcXVlcnk7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU3BsaXRCb2R5IHtcbiAgLyoqIFdoYXQgYHJlcWAgcmVhZHM6IHRoZSBlbnRyeXBvaW50J3MgY29weS4gKi9cbiAgcmVhZG9ubHkgZm9yUmVxdWVzdD86IFJlYWRhYmxlIHwgQnVmZmVyO1xuICAvKiogV2hhdCBgcmVzb2x2ZVJvdXRlc2AgaGFuZHMgbWlkZGxld2FyZS4gKi9cbiAgcmVhZG9ubHkgZm9yRGlzcGF0Y2g6IFJlYWRhYmxlU3RyZWFtO1xuICAvKipcbiAgICogQ2FsbGVkIG9uY2UgZGlzcGF0Y2ggaGFzIHJldHVybmVkLCB3aXRoIHdoZXRoZXIgbWlkZGxld2FyZSBhbnN3ZXJlZCB0aGVcbiAgICogcmVxdWVzdCBpdHNlbGYuIExldHMgZ28gb2Ygd2hpY2hldmVyIGNvcHkgbm90aGluZyB3aWxsIHJlYWQgYW55IG1vcmUuXG4gICAqL1xuICByZWxlYXNlVW5yZWFkKG1pZGRsZXdhcmVSZXNwb25kZWQ6IGJvb2xlYW4pOiB2b2lkO1xufVxuXG4vKipcbiAqIGByZXNvbHZlUm91dGVzYCBoYW5kcyBgcmVxdWVzdEJvZHlgIHRvIG1pZGRsZXdhcmUsIHdoaWNoIGNvbnN1bWVzIGl0LCB3aGlsZSB0aGVcbiAqIGVudHJ5cG9pbnQgbmVlZHMgdGhlIHNhbWUgYnl0ZXMg4oCUIHRoZSBzYW1lIHByb2JsZW0gYG5leHQtc2VydmVyYCBzb2x2ZXMgd2l0aFxuICogYGdldENsb25lYWJsZUJvZHkoKWAuIE9ubHkgc3BsaXQgd2hlbiB0aGVyZSBpcyBtaWRkbGV3YXJlIHRvIGZlZWQ6IHRlZWluZyBjb3N0c1xuICogYSBidWZmZXIgY29weSBvZiBldmVyeSB1cGxvYWQgb24gYSBwYXRoIHRoYXQgd291bGQgb3RoZXJ3aXNlIGJlIGEgc3RyYWlnaHQgcGlwZS5cbiAqXG4gKiBBIGB0ZWUoKWAgYnVmZmVycywgZm9yIHRoZSBicmFuY2ggdGhhdCBpcyBiZWhpbmQsIGV2ZXJ5IGNodW5rIHRoZSBvdGhlciBvbmVcbiAqIGhhcyByZWFkIOKAlCBzbyBhIGJyYW5jaCB0aGF0IGlzIG5ldmVyIHJlYWQgaG9sZHMgdGhlIHdob2xlIGJvZHkuIE1pZGRsZXdhcmVcbiAqIHRoYXQgZG9lc24ndCByZWFkIHRoZSBib2R5IGlzIHRoZSBjb21tb24gY2FzZSAoYW5kIGl0cyBtYXRjaGVyIG1heSBub3QgZXZlblxuICogaGF2ZSBydW4gaXQpLCBhZnRlciB3aGljaCB0aGUgZW50cnlwb2ludCBzdHJlYW1pbmcgYW4gdXBsb2FkIHdvdWxkIHBpbGUgYWxsXG4gKiBvZiBpdCB1cCBpbiB0aGUgbWlkZGxld2FyZSBicmFuY2g6IHRoZSB3aG9sZSB1cGxvYWQgaW4gbWVtb3J5LCBvbiBhXG4gKiBjb250YWluZXIgdGhhdCBzZXJ2ZXMgb3RoZXIgcmVxdWVzdHMuIFNvIG9uY2UgZGlzcGF0Y2ggcmV0dXJucywgdGhlIGJyYW5jaFxuICogd2l0aCBubyByZWFkZXIgbGVmdCBpcyBjYW5jZWxsZWQg4oCUIHRoZSBtaWRkbGV3YXJlIG9uZSwgb3IgdGhlIGVudHJ5cG9pbnQgb25lXG4gKiB3aGVuIG1pZGRsZXdhcmUgYW5zd2VyZWQgYW5kIGByZXFgIHdpbGwgbmV2ZXIgYmUgcmVhZC4gQ2FuY2VsbGluZyBvbmVcbiAqIGJyYW5jaCBsZWF2ZXMgdGhlIHNvdXJjZSBhbmQgdGhlIG90aGVyIGJyYW5jaCBydW5uaW5nLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc3BsaXRCb2R5KFxuICBib2R5OiBSZWFkYWJsZSB8IEJ1ZmZlciB8IHVuZGVmaW5lZCxcbiAgaGFzTWlkZGxld2FyZTogYm9vbGVhbixcbik6IFNwbGl0Qm9keSB7XG4gIGNvbnN0IHJlbGVhc2VOb3RoaW5nID0gKCk6IHZvaWQgPT4ge307XG4gIGlmIChib2R5ID09PSB1bmRlZmluZWQpIHtcbiAgICByZXR1cm4ge1xuICAgICAgZm9yRGlzcGF0Y2g6IG5ldyBCbG9iKFtdKS5zdHJlYW0oKSxcbiAgICAgIHJlbGVhc2VVbnJlYWQ6IHJlbGVhc2VOb3RoaW5nLFxuICAgIH07XG4gIH1cbiAgaWYgKCFoYXNNaWRkbGV3YXJlKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIGZvclJlcXVlc3Q6IGJvZHksXG4gICAgICBmb3JEaXNwYXRjaDogbmV3IEJsb2IoW10pLnN0cmVhbSgpLFxuICAgICAgcmVsZWFzZVVucmVhZDogcmVsZWFzZU5vdGhpbmcsXG4gICAgfTtcbiAgfVxuICBpZiAoQnVmZmVyLmlzQnVmZmVyKGJvZHkpKSB7XG4gICAgLy8gQWxyZWFkeSBmdWxseSBpbiBtZW1vcnksIHNvIFwidGVlaW5nXCIgaXMganVzdCByZWFkaW5nIGl0IHR3aWNlLlxuICAgIHJldHVybiB7XG4gICAgICBmb3JSZXF1ZXN0OiBib2R5LFxuICAgICAgZm9yRGlzcGF0Y2g6IG5ldyBCbG9iKFtib2R5XSkuc3RyZWFtKCksXG4gICAgICByZWxlYXNlVW5yZWFkOiByZWxlYXNlTm90aGluZyxcbiAgICB9O1xuICB9XG4gIGNvbnN0IFtmb3JNaWRkbGV3YXJlLCBmb3JFbnRyeXBvaW50XSA9IFJlYWRhYmxlLnRvV2ViKGJvZHkpLnRlZSgpO1xuICBjb25zdCBmb3JSZXF1ZXN0ID0gUmVhZGFibGUuZnJvbVdlYihmb3JFbnRyeXBvaW50IGFzIG5ldmVyKTtcbiAgcmV0dXJuIHtcbiAgICBmb3JSZXF1ZXN0LFxuICAgIGZvckRpc3BhdGNoOiBmb3JNaWRkbGV3YXJlIGFzIFJlYWRhYmxlU3RyZWFtLFxuICAgIHJlbGVhc2VVbnJlYWQobWlkZGxld2FyZVJlc3BvbmRlZCkge1xuICAgICAgaWYgKG1pZGRsZXdhcmVSZXNwb25kZWQpIHtcbiAgICAgICAgLy8gYGZyb21XZWJgIGhvbGRzIHRoZSBicmFuY2gncyByZWFkZXIsIHNvIGl0IGlzIGRlc3Ryb3llZCByYXRoZXIgdGhhblxuICAgICAgICAvLyBjYW5jZWxsZWQ7IGl0cyBgX2Rlc3Ryb3lgIGNhbmNlbHMgdGhlIHJlYWRlci5cbiAgICAgICAgZm9yUmVxdWVzdC5kZXN0cm95KCk7XG4gICAgICB9IGVsc2UgaWYgKCFmb3JNaWRkbGV3YXJlLmxvY2tlZCkge1xuICAgICAgICAvLyBMb2NrZWQgbWVhbnMgbWlkZGxld2FyZSB0b29rIGEgcmVhZGVyOiBpdCBpcyByZWFkaW5nLCBvciBoYXMgcmVhZCxcbiAgICAgICAgLy8gdGhlIGJvZHkgaXRzZWxmLCBhbmQgYSBsb2NrZWQgc3RyZWFtIGNhbm5vdCBiZSBjYW5jZWxsZWQgYW55d2F5LlxuICAgICAgICBmb3JNaWRkbGV3YXJlLmNhbmNlbCgpLmNhdGNoKCgpID0+IHt9KTtcbiAgICAgIH1cbiAgICB9LFxuICB9O1xufVxuXG4vKiogYEluY29taW5nSHR0cEhlYWRlcnNgIOKGkiB0aGUgZmxhdCBlbnRyaWVzIGEgYEhlYWRlcnNgIGNvbnN0cnVjdG9yIGFjY2VwdHMuICovXG5mdW5jdGlvbiB0b1dlYkhlYWRlcnMoaGVhZGVyczogSW5jb21pbmdIdHRwSGVhZGVycyk6IEFycmF5PFtzdHJpbmcsIHN0cmluZ10+IHtcbiAgY29uc3QgZW50cmllczogQXJyYXk8W3N0cmluZywgc3RyaW5nXT4gPSBbXTtcbiAgZm9yIChjb25zdCBbbmFtZSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKGhlYWRlcnMpKSB7XG4gICAgaWYgKHZhbHVlID09PSB1bmRlZmluZWQpIHtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICBmb3IgKGNvbnN0IHNpbmdsZSBvZiBBcnJheS5pc0FycmF5KHZhbHVlKSA/IHZhbHVlIDogW3ZhbHVlXSkge1xuICAgICAgZW50cmllcy5wdXNoKFtuYW1lLCBzaW5nbGVdKTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIGVudHJpZXM7XG59XG5cbi8qKlxuICogYGhlYWRlcnMoKWAgcnVsZXMsIHRoZSBpbW11dGFibGUgYGNhY2hlLWNvbnRyb2xgIGZvciBidWlsZCBhc3NldHMsIGFuZCBhbnl0aGluZ1xuICogbWlkZGxld2FyZSBzZXQgd2l0aCBgTmV4dFJlc3BvbnNlLm5leHQoeyBoZWFkZXJzIH0pYC4gQXBwbGllZCBiZWZvcmUgdGhlXG4gKiBlbnRyeXBvaW50IHJ1bnMgc28gdGhhdCBhIHJvdXRlIGNhbiBzdGlsbCBvdmVycmlkZSBpdHMgb3duLlxuICovXG5mdW5jdGlvbiBhcHBseUhlYWRlcnMocmVzOiBTaGltU2VydmVyUmVzcG9uc2UsIGhlYWRlcnM6IEhlYWRlcnMpOiB2b2lkIHtcbiAgZm9yIChjb25zdCBbbmFtZSwgdmFsdWVdIG9mIGhlYWRlcnMuZW50cmllcygpKSB7XG4gICAgLy8gYGVudHJpZXMoKWAgeWllbGRzIGBzZXQtY29va2llYCBvbmNlIHBlciBjb29raWUsIHNvIGFwcGVuZGluZyB0aGUgd2hvbGVcbiAgICAvLyBgZ2V0U2V0Q29va2llKClgIGFycmF5IGhlcmUgd291bGQgZW1pdCBOwrIgb2YgdGhlbSDigJQgdHdvIGNvb2tpZXMgc2V0IGJ5XG4gICAgLy8gbWlkZGxld2FyZSBhcnJpdmVkIGFzIFtcImE9MVwiLFwiYj0yXCIsXCJhPTFcIixcImI9MlwiXS4gSGFuZGxlZCBvbmNlLCBvdXRzaWRlLlxuICAgIGlmIChuYW1lID09PSBcInNldC1jb29raWVcIikge1xuICAgICAgY29udGludWU7XG4gICAgfVxuICAgIHJlcy5zZXRIZWFkZXIobmFtZSwgdmFsdWUpO1xuICB9XG4gIGZvciAoY29uc3QgY29va2llIG9mIGhlYWRlcnMuZ2V0U2V0Q29va2llKCkpIHtcbiAgICByZXMuYXBwZW5kSGVhZGVyKFwic2V0LWNvb2tpZVwiLCBjb29raWUpO1xuICB9XG59XG5cbi8qKlxuICogTWlycm9ycyBOZXh0LmpzJ3Mgb3duIHJlZGlyZWN0IHJlc3BvbnNlIChgYmFzZS1zZXJ2ZXIudHNgKTogdGhlIGRlc3RpbmF0aW9uIGlzXG4gKiBhbHNvIHRoZSBib2R5LCBhbmQgYSAzMDggY2FycmllcyBhIGBSZWZyZXNoYCBoZWFkZXIgYmVjYXVzZSBzb21lIGNsaWVudHMgYW5kXG4gKiBpbnRlcm1lZGlhcmllcyBzdGlsbCBkbyBub3QgaW1wbGVtZW50IDMwOC5cbiAqL1xuZnVuY3Rpb24gc2VuZFJlZGlyZWN0KFxuICByZXM6IFNoaW1TZXJ2ZXJSZXNwb25zZSxcbiAgbG9jYXRpb246IHN0cmluZyxcbiAgc3RhdHVzOiBudW1iZXIsXG4pOiB2b2lkIHtcbiAgcmVzLnN0YXR1c0NvZGUgPSBzdGF0dXM7XG4gIHJlcy5zZXRIZWFkZXIoXCJMb2NhdGlvblwiLCBsb2NhdGlvbik7XG4gIGlmIChzdGF0dXMgPT09IDMwOCkge1xuICAgIHJlcy5zZXRIZWFkZXIoXCJSZWZyZXNoXCIsIGAwO3VybD0ke2xvY2F0aW9ufWApO1xuICB9XG4gIHJlcy5zZXRIZWFkZXIoXCJDb250ZW50LVR5cGVcIiwgXCJ0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04XCIpO1xuICByZXMuZW5kKGxvY2F0aW9uKTtcbn1cblxuLyoqXG4gKiBgbmV4dCBzdGFydGAncyBhbnN3ZXIgdG8gYSBQT1NULCBQVVQgb3IgREVMRVRFIGZvciBhIHN0YXRpYyBmaWxlOiA0MDUgd2l0aFxuICogYEFsbG93OiBHRVQsIEhFQURgIChgcm91dGVyLXNlcnZlci5qc2ApLiBJdCByZW5kZXJzIHRoYXQgdGhyb3VnaCBgL19lcnJvcmA7XG4gKiB0aGlzIGlzIHRoZSBwbGFpbi10ZXh0IGVxdWl2YWxlbnQsIHNpbmNlIHRoZSBzdGF0dXMgYW5kIGBBbGxvd2AgYXJlIHdoYXQgYVxuICogY2xpZW50IGFjdHMgb24uXG4gKi9cbmZ1bmN0aW9uIHNlbmRNZXRob2ROb3RBbGxvd2VkKHJlczogU2hpbVNlcnZlclJlc3BvbnNlKTogdm9pZCB7XG4gIHJlcy5zdGF0dXNDb2RlID0gNDA1O1xuICByZXMuc2V0SGVhZGVyKFwiQWxsb3dcIiwgXCJHRVQsIEhFQURcIik7XG4gIHJlcy5zZXRIZWFkZXIoXCJDb250ZW50LVR5cGVcIiwgXCJ0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04XCIpO1xuICByZXMuZW5kKFwiTWV0aG9kIE5vdCBBbGxvd2VkXCIpO1xufVxuXG4vKipcbiAqIFJlcXVlc3QgaGVhZGVycyB7QGxpbmsgcHJveHlFeHRlcm5hbH0gZG9lcyBub3QgZm9yd2FyZC4gVGhlIHVwc3RyZWFtJ3Mgb3duXG4gKiBgSG9zdGAgbXVzdCB3aW47IHRoZSByZXN0IGRlc2NyaWJlIG91ciBjb25uZWN0aW9uLCBub3QgdGhlIHByb3hpZWQgb25lLCBhbmRcbiAqIHVuZGljaSB0aHJvd3Mgb24gbW9zdCBvZiB0aGVtLCB3aGljaCB3b3VsZCBhbnN3ZXIgdGhlIHJlcXVlc3Qgd2l0aCBhIDUwMC5cbiAqIE5leHQuanMncyBsaXN0IGZvciB0aGUgcmVxdWVzdHMgaXQgbWFrZXMgd2l0aCB1bmRpY2kgKGBpcGNGb3JiaWRkZW5IZWFkZXJzYCxcbiAqIGBuZXh0L2Rpc3Qvc2VydmVyL2xpYi9zZXJ2ZXItaXBjL3V0aWxzLmpzYCksIHBsdXMgYGhvc3RgIGFuZCBgdXBncmFkZWAsIG1pbnVzXG4gKiBgY29udGVudC1lbmNvZGluZ2A6IE5leHQgc3RyaXBzIHRoYXQgb3ZlciBhIGJvZHkgaXQgaGFzIGFscmVhZHkgZGVjb2RlZCwgd2hpbGVcbiAqIHRoaXMgb25lIGlzIGZvcndhcmRlZCBhcyByZWNlaXZlZCwgc3RpbGwgZW5jb2RlZC5cbiAqL1xuY29uc3QgUFJPWFlfRFJPUFBFRF9IRUFERVJTOiByZWFkb25seSBzdHJpbmdbXSA9IFtcbiAgXCJob3N0XCIsXG4gIFwidXBncmFkZVwiLFxuICBcImFjY2VwdC1lbmNvZGluZ1wiLFxuICBcImtlZXBhbGl2ZVwiLFxuICBcImtlZXAtYWxpdmVcIixcbiAgXCJ0cmFuc2Zlci1lbmNvZGluZ1wiLFxuICBcImNvbm5lY3Rpb25cIixcbiAgXCJleHBlY3RcIixcbl07XG5cbi8qKlxuICogVGhlIFNpZ1Y0IHNpZ25hdHVyZSBPcmlnaW4gQWNjZXNzIENvbnRyb2wgcHV0IG9uIGEgcmVxdWVzdCB0byB0aGUgRnVuY3Rpb25cbiAqIFVSTCwgYWxzbyBkcm9wcGVkIGJ5IHtAbGluayBwcm94eUV4dGVybmFsfSB3aGVuIHRoZSByZXF1ZXN0IHdhcyBJQU0tdmVyaWZpZWQ6XG4gKiBmb3J3YXJkZWQsIGl0IHdvdWxkIGxldCB0aGUgdGhpcmQtcGFydHkgb3JpZ2luIHJlcGxheSB0aGUgcmVxdWVzdCBhZ2FpbnN0XG4gKiB0aGUgRnVuY3Rpb24gVVJMLiBBbnl3aGVyZSBlbHNlIHRoZXNlIGFyZSB0aGUgY2xpZW50J3Mgb3duLCBhbmQgYXJlXG4gKiBmb3J3YXJkZWQgYXMgYG5leHQgc3RhcnRgIGZvcndhcmRzIHRoZW0uXG4gKi9cbmNvbnN0IFNJR1Y0X0hFQURFUlM6IHJlYWRvbmx5IHN0cmluZ1tdID0gW1xuICBcImF1dGhvcml6YXRpb25cIixcbiAgXCJ4LWFtei1kYXRlXCIsXG4gIFwieC1hbXotY29udGVudC1zaGEyNTZcIixcbiAgXCJ4LWFtei1zZWN1cml0eS10b2tlblwiLFxuXTtcblxuLyoqIEEgYG5leHQuY29uZmlnYCByZXdyaXRlIHdob3NlIGRlc3RpbmF0aW9uIGlzIGFub3RoZXIgb3JpZ2luLiAqL1xuYXN5bmMgZnVuY3Rpb24gcHJveHlFeHRlcm5hbChcbiAgcmVxOiBTaGltSW5jb21pbmdNZXNzYWdlLFxuICByZXM6IFNoaW1TZXJ2ZXJSZXNwb25zZSxcbiAgdXJsOiBVUkwsXG4gIHJlcXVlc3RIZWFkZXJzOiBIZWFkZXJzLFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IG1ldGhvZCA9IHJlcS5tZXRob2QgPz8gXCJHRVRcIjtcbiAgY29uc3QgaGFzQm9keSA9IG1ldGhvZCAhPT0gXCJHRVRcIiAmJiBtZXRob2QgIT09IFwiSEVBRFwiO1xuICBjb25zdCBoZWFkZXJzID0gbmV3IEhlYWRlcnMocmVxdWVzdEhlYWRlcnMpO1xuICBmb3IgKGNvbnN0IG5hbWUgb2YgUFJPWFlfRFJPUFBFRF9IRUFERVJTKSB7XG4gICAgaGVhZGVycy5kZWxldGUobmFtZSk7XG4gIH1cbiAgaWYgKHJlcS50cnVzdEZvcndhcmRlZEhvc3QpIHtcbiAgICBmb3IgKGNvbnN0IG5hbWUgb2YgU0lHVjRfSEVBREVSUykge1xuICAgICAgaGVhZGVycy5kZWxldGUobmFtZSk7XG4gICAgfVxuICB9XG4gIGNvbnN0IHVwc3RyZWFtID0gYXdhaXQgZmV0Y2godXJsLCB7XG4gICAgbWV0aG9kLFxuICAgIGhlYWRlcnMsXG4gICAgYm9keTogaGFzQm9keSA/IChSZWFkYWJsZS50b1dlYihyZXEpIGFzIFJlYWRhYmxlU3RyZWFtKSA6IHVuZGVmaW5lZCxcbiAgICByZWRpcmVjdDogXCJtYW51YWxcIixcbiAgICAuLi4oaGFzQm9keSA/IHsgZHVwbGV4OiBcImhhbGZcIiB9IDoge30pLFxuICB9IGFzIFJlcXVlc3RJbml0KTtcbiAgYXdhaXQgc2VuZFdlYlJlc3BvbnNlKHJlcywgdXBzdHJlYW0pO1xufVxuXG4vKipcbiAqIEhlYWRlcnMgZGVzY3JpYmluZyB0aGUgZnJhbWluZyBvZiBhIGJvZHkgdGhhdCBpcyBubyBsb25nZXIgdGhlIGJvZHkgYmVpbmdcbiAqIHNlbnQsIGRyb3BwZWQgZnJvbSBldmVyeSBgUmVzcG9uc2VgIHtAbGluayBzZW5kV2ViUmVzcG9uc2V9IGZvcndhcmRzLlxuICpcbiAqIEEgcHJveGllZCBvcmlnaW4ncyBgUmVzcG9uc2VgIGNhbWUgYmFjayBmcm9tIGBmZXRjaGAsIGFuZCBzbywgb2Z0ZW4sIGRpZFxuICogbWlkZGxld2FyZSdzIG93biAoYHJldHVybiBmZXRjaCh1cHN0cmVhbSlgIGluIGBwcm94eS50c2AsIHNpbmNlIG1pZGRsZXdhcmVcbiAqIHJ1bnMgaW4gcHJvY2VzcyBhbmQgaXRzIGBmZXRjaGAgaXMgdW5kaWNpKS4gdW5kaWNpIGRlY29kZXMgdGhlIGJvZHkg4oCUIGd6aXAsXG4gKiBkZWZsYXRlLCBiciwgenN0ZCDigJQgYW5kIGxlYXZlcyBgY29udGVudC1lbmNvZGluZ2AgYW5kIGBjb250ZW50LWxlbmd0aGBcbiAqIGRlc2NyaWJpbmcgdGhlIGVuY29kZWQgYnl0ZXMgaXQgYWxyZWFkeSB0aHJldyBhd2F5LiBGb3J3YXJkaW5nIHRoZW0gZW1pdHNcbiAqIHBsYWludGV4dCBsYWJlbGxlZCBgZ3ppcGAgKHRoZSBicm93c2VyIGZhaWxzIHRoZSB3aG9sZSByZXNwb25zZSB3aXRoXG4gKiBgRVJSX0NPTlRFTlRfREVDT0RJTkdfRkFJTEVEYCkgdW5kZXIgYSBgQ29udGVudC1MZW5ndGhgIHRoYXQgaXMgdG9vIHNob3J0LFxuICogYW5kIGBzaG91bGRHemlwYCB0aGVuIGRlY2xpbmVzIHRvIGNvbXByZXNzIGl0IGJlY2F1c2UgYGNvbnRlbnQtZW5jb2RpbmdgIGlzXG4gKiBhbHJlYWR5IHNldCwgc28gbm90aGluZyBkb3duc3RyZWFtIHJlcGFpcnMgaXQuIGB0cmFuc2Zlci1lbmNvZGluZ2AgaXNcbiAqIGhvcC1ieS1ob3A6IGl0IGRlc2NyaWJlZCB0aGUgdXBzdHJlYW0gY29ubmVjdGlvbiwgbm90IHRoaXMgb25lLlxuICpcbiAqIFN0b2NrIE5leHQuanMgZHJvcHMgdGhlIHNhbWUgdGhyZWUgZnJvbSBldmVyeSBtaWRkbGV3YXJlIHJlc3BvbnNlXG4gKiAoYEZPUkJJRERFTl9IRUFERVJTYCBpbiBgbmV4dC9kaXN0L3NlcnZlci93ZWIvc2FuZGJveC9zYW5kYm94LmpzYCksIHNvXG4gKiBtaWRkbGV3YXJlIGNhbm5vdCBsYWJlbCBhIGJvZHkgaXQgZW5jb2RlZCBpdHNlbGYgdGhlcmUgZWl0aGVyLlxuICovXG5jb25zdCBTVEFMRV9GUkFNSU5HX0hFQURFUlM6IFJlYWRvbmx5U2V0PHN0cmluZz4gPSBuZXcgU2V0KFtcbiAgXCJjb250ZW50LWVuY29kaW5nXCIsXG4gIFwiY29udGVudC1sZW5ndGhcIixcbiAgXCJ0cmFuc2Zlci1lbmNvZGluZ1wiLFxuXSk7XG5cbi8qKiBTdHJlYW0gYSBgUmVzcG9uc2VgIOKAlCBtaWRkbGV3YXJlJ3Mgb3duLCBvciBhIHByb3hpZWQgb3JpZ2luJ3Mg4oCUIGludG8gYHJlc2AuICovXG5hc3luYyBmdW5jdGlvbiBzZW5kV2ViUmVzcG9uc2UoXG4gIHJlczogU2hpbVNlcnZlclJlc3BvbnNlLFxuICByZXNwb25zZTogUmVzcG9uc2UsXG4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgcmVzLnN0YXR1c0NvZGUgPSByZXNwb25zZS5zdGF0dXM7XG4gIGlmIChyZXNwb25zZS5zdGF0dXNUZXh0KSB7XG4gICAgcmVzLnN0YXR1c01lc3NhZ2UgPSByZXNwb25zZS5zdGF0dXNUZXh0O1xuICB9XG4gIGNvbnN0IGhlYWRlcnMgPSBuZXcgSGVhZGVycyhyZXNwb25zZS5oZWFkZXJzKTtcbiAgZm9yIChjb25zdCBuYW1lIG9mIFNUQUxFX0ZSQU1JTkdfSEVBREVSUykge1xuICAgIGhlYWRlcnMuZGVsZXRlKG5hbWUpO1xuICB9XG4gIGFwcGx5SGVhZGVycyhyZXMsIGhlYWRlcnMpO1xuICBpZiAoIXJlc3BvbnNlLmJvZHkpIHtcbiAgICByZXMuZW5kKCk7XG4gICAgcmV0dXJuO1xuICB9XG4gIGZvciBhd2FpdCAoY29uc3QgY2h1bmsgb2YgcmVzcG9uc2UuYm9keSBhcyB1bmtub3duIGFzIEFzeW5jSXRlcmFibGU8VWludDhBcnJheT4pIHtcbiAgICAvLyBMZWF2aW5nIHRoZSBsb29wIGNhbmNlbHMgdGhlIGJvZHksIHdoaWNoIGlzIHdoYXQgc3RvcHMgYSBwcm94aWVkIG9yaWdpblxuICAgIC8vIGZyb20gYmVpbmcgcmVhZCB0byB0aGUgZW5kIGZvciBhIGNsaWVudCB0aGF0IGhhcyBhbHJlYWR5IGdvbmUuXG4gICAgaWYgKHJlcy5kZXN0cm95ZWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgLy8gUGFjZWQgYnkgdGhlIGNsaWVudDogd2l0aG91dCB3YWl0aW5nIGZvciBgZHJhaW5gLCBhIGxhcmdlIHByb3hpZWQgYm9keVxuICAgIC8vIGhlYWRlZCBmb3IgYSBzbG93IGNsaWVudCBpcyBidWZmZXJlZCB3aG9sZSBpbiBtZW1vcnkuXG4gICAgY29uc3QgYWNjZXB0ZWQgPSByZXMud3JpdGUoY2h1bmspO1xuICAgIC8vIFNldCBieSB0aGUgc2luayB3aGVuIGl0IGNvbXByZXNzZXMuIFdpdGhvdXQgaXQgdGhlIGNodW5rIHNpdHMgaW4gemxpYidzXG4gICAgLy8gYnVmZmVyLCBhbmQgYSBzdHJlYW1lZCBib2R5IOKAlCBzZXJ2ZXItc2VudCBldmVudHMgdGhyb3VnaCBhIHJld3JpdGUsIGFcbiAgICAvLyBtaWRkbGV3YXJlIGBSZWFkYWJsZVN0cmVhbWAg4oCUIHJlYWNoZXMgdGhlIGNsaWVudCBhbGwgYXQgb25jZSwgYXQgdGhlIGVuZC5cbiAgICAvLyBOZXh0LmpzIGZsdXNoZXMgYWZ0ZXIgZXZlcnkgY2h1bmsgZm9yIHRoZSBzYW1lIHJlYXNvbiAoYHBpcGUtcmVhZGFibGVgKS5cbiAgICByZXMuZmx1c2g/LigpO1xuICAgIGlmICghYWNjZXB0ZWQgJiYgIShhd2FpdCBkcmFpbmVkKHJlcykpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICB9XG4gIHJlcy5lbmQoKTtcbn1cbiJdfQ==
|