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