@dbx-tools/appkit-lakebox 0.0.0-stage → 0.9.131

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 (55) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +104 -2
  3. package/index.ts +32 -0
  4. package/lib/index.d.ts +28 -0
  5. package/lib/index.js +26 -0
  6. package/lib/src/_package.d.ts +2 -0
  7. package/lib/src/_package.js +11 -0
  8. package/lib/src/_resource-registry.d.ts +41 -0
  9. package/lib/src/_resource-registry.js +128 -0
  10. package/lib/src/_service-snapshots.d.ts +55 -0
  11. package/lib/src/_service-snapshots.js +65 -0
  12. package/lib/src/config.d.ts +40 -0
  13. package/lib/src/config.js +86 -0
  14. package/lib/src/desktop.d.ts +6 -0
  15. package/lib/src/desktop.js +88 -0
  16. package/lib/src/metrics.d.ts +59 -0
  17. package/lib/src/metrics.js +151 -0
  18. package/lib/src/plugin.d.ts +259 -0
  19. package/lib/src/plugin.js +329 -0
  20. package/lib/src/routes.d.ts +75 -0
  21. package/lib/src/routes.js +441 -0
  22. package/lib/src/runtime.d.ts +154 -0
  23. package/lib/src/runtime.js +760 -0
  24. package/lib/src/services.d.ts +101 -0
  25. package/lib/src/services.js +188 -0
  26. package/lib/src/skills.d.ts +5 -0
  27. package/lib/src/skills.js +21 -0
  28. package/lib/src/user.d.ts +46 -0
  29. package/lib/src/user.js +76 -0
  30. package/lib/src/workspace.d.ts +43 -0
  31. package/lib/src/workspace.js +138 -0
  32. package/lib/tsconfig.tsbuildinfo +1 -0
  33. package/package.json +167 -4
  34. package/scripts/desktop-prerequisites.sh +66 -0
  35. package/scripts/desktop-session.sh +307 -0
  36. package/scripts/lakebox-admin.sh +234 -0
  37. package/scripts/lakebox-service.py +449 -0
  38. package/skills/html-page/SKILL.md +40 -0
  39. package/skills/jupyter-lab/SKILL.md +32 -0
  40. package/skills/lakebox-showcase/SKILL.md +66 -0
  41. package/skills/network-speed-test/SKILL.md +29 -0
  42. package/skills/realtime-whiteboard/SKILL.md +28 -0
  43. package/src/_package.ts +11 -0
  44. package/src/_resource-registry.ts +160 -0
  45. package/src/_service-snapshots.ts +84 -0
  46. package/src/config.ts +128 -0
  47. package/src/desktop.ts +97 -0
  48. package/src/metrics.ts +178 -0
  49. package/src/plugin.ts +446 -0
  50. package/src/routes.ts +524 -0
  51. package/src/runtime.ts +952 -0
  52. package/src/services.ts +239 -0
  53. package/src/skills.ts +23 -0
  54. package/src/user.ts +88 -0
  55. package/src/workspace.ts +185 -0
@@ -0,0 +1,441 @@
1
+ import { createServer } from "node:net";
2
+ import { PassThrough, Readable } from "node:stream";
3
+ import { ACCESS_TOKEN_HEADER, USER_EMAIL_HEADER, USER_ID_HEADER } from "@dbx-tools/appkit";
4
+ import { errorUtils, log } from "@dbx-tools/shared-core";
5
+ import ProxyModule from "http-proxy-3";
6
+ const MAX_PENDING_BYTES = 1024 * 1024;
7
+ const FORWARDING_READY_TIMEOUT_MS = 15_000;
8
+ const logger = log.logger("appkit-lakebox:routes");
9
+ /**
10
+ * Proxies `<prefix>/<user-hash>/<service>` to that user's service port.
11
+ *
12
+ * The user hash is the only routing key. A request is forwarded only when its
13
+ * authenticated identity hashes to the same value, and the port is derived from
14
+ * the hash and service name, so there is no stored route table. Requests
15
+ * without an identity are rejected with 404, including WebSocket upgrades.
16
+ */
17
+ export class LakeboxRouteRegistry {
18
+ prefix;
19
+ resolver;
20
+ forwardingReadyTimeoutMs;
21
+ bridges = new Map();
22
+ upgrades = new Set();
23
+ pending = new WeakMap();
24
+ proxy = ProxyModule.createProxyServer({ ws: true, xfwd: true });
25
+ shuttingDown = false;
26
+ /** Creates a registry beneath one AppKit route prefix. */
27
+ constructor(prefix, resolver, forwardingReadyTimeoutMs = FORWARDING_READY_TIMEOUT_MS) {
28
+ this.prefix = prefix;
29
+ this.resolver = resolver;
30
+ this.forwardingReadyTimeoutMs = forwardingReadyTimeoutMs;
31
+ this.proxy.on("error", (error, request, response) => {
32
+ if (this.shuttingDown && isConnectionClosureError(error)) {
33
+ response.destroy();
34
+ return;
35
+ }
36
+ const pending = this.pending.get(request);
37
+ if (pending && "writeHead" in response && !response.headersSent) {
38
+ this.resolver.unavailable(pending.user, pending.service);
39
+ sendLoadingResponse(request.headers.accept, response);
40
+ return;
41
+ }
42
+ logger.warn("Lakebox route proxy failed", {
43
+ method: request.method,
44
+ path: request.url,
45
+ error,
46
+ });
47
+ if ("writeHead" in response) {
48
+ if (response.headersSent) {
49
+ response.destroy();
50
+ }
51
+ else {
52
+ response.writeHead(502);
53
+ response.end("Lakebox route unavailable");
54
+ }
55
+ }
56
+ else if (!response.destroyed) {
57
+ response.destroy();
58
+ }
59
+ });
60
+ this.proxy.on("proxyRes", (proxyResponse, request, response) => {
61
+ const pending = this.pending.get(request);
62
+ if (pending && [502, 503, 504].includes(proxyResponse.statusCode ?? 0)) {
63
+ proxyResponse.resume();
64
+ this.resolver.unavailable(pending.user, pending.service);
65
+ sendLoadingResponse(request.headers.accept, response);
66
+ return;
67
+ }
68
+ if (pending?.head) {
69
+ void injectHtmlHead(proxyResponse, response, pending.head);
70
+ return;
71
+ }
72
+ pipeProxyResponse(proxyResponse, response);
73
+ });
74
+ }
75
+ /** Returns the route path segment match for a request path. */
76
+ match(path) {
77
+ const root = this.prefix.replace(/\/+$/, "");
78
+ if (!path.startsWith(`${root}/`))
79
+ return undefined;
80
+ const [userHash, service] = path.slice(root.length + 1).split("/");
81
+ if (!userHash || !service)
82
+ return undefined;
83
+ return { userHash, service, mount: `${root}/${userHash}/${service}` };
84
+ }
85
+ /** Express middleware that authorizes, rewrites, and proxies matching requests. */
86
+ middleware() {
87
+ return async (request, response, next) => {
88
+ const match = this.match(request.path);
89
+ if (!match)
90
+ return next();
91
+ const user = this.authorize(request.headers, match);
92
+ if (!user) {
93
+ response.statusCode = 404;
94
+ response.end("Not Found");
95
+ return;
96
+ }
97
+ if (request.path === match.mount) {
98
+ response.statusCode = 308;
99
+ response.setHeader("location", routeRedirectUrl(request.url, match.mount));
100
+ response.end();
101
+ return;
102
+ }
103
+ let resolution;
104
+ try {
105
+ resolution = await this.resolver.resolve(user, match.service);
106
+ }
107
+ catch (error) {
108
+ return next(error);
109
+ }
110
+ if (resolution.kind === "missing") {
111
+ response.statusCode = 404;
112
+ response.end("Not Found");
113
+ return;
114
+ }
115
+ if (resolution.kind === "loading") {
116
+ sendLoadingResponse(request.header("accept"), response);
117
+ return;
118
+ }
119
+ if (resolution.kind === "failed") {
120
+ response.statusCode = 502;
121
+ response.setHeader("content-type", "text/plain; charset=utf-8");
122
+ response.end(`Lakebox service failed: ${resolution.message}`);
123
+ return;
124
+ }
125
+ const bridge = await this.bridge(resolution.session, resolution.route.port);
126
+ const head = request.method === "GET" && request.path === `${match.mount}/`
127
+ ? resolution.route.htmlHead
128
+ : undefined;
129
+ if (head)
130
+ request.headers["accept-encoding"] = "identity";
131
+ this.pending.set(request, { user, service: match.service, ...(head ? { head } : {}) });
132
+ const replayBody = replayParsedJsonBody(request);
133
+ if (resolution.route.stripPrefix)
134
+ request.url = outboundPath(request.url, match.mount);
135
+ stripIdentityHeaders(request);
136
+ this.proxy.web(request, response, {
137
+ target: bridge.target,
138
+ changeOrigin: true,
139
+ selfHandleResponse: true,
140
+ ...(replayBody ? { buffer: replayBody } : {}),
141
+ });
142
+ };
143
+ }
144
+ /** Number of open forwarded HTTP and WebSocket connections. */
145
+ activeConnections() {
146
+ let count = this.upgrades.size;
147
+ for (const bridge of this.bridges.values())
148
+ count += bridge.sockets.size;
149
+ return count;
150
+ }
151
+ /** Returns whether an upgrade path belongs to the Lakebox route prefix. */
152
+ canHandleUpgrade(path) {
153
+ return Boolean(this.match(path));
154
+ }
155
+ /** Proxies an authorized WebSocket upgrade. Returns false when it was not handled. */
156
+ async handleUpgrade(request, socket, head) {
157
+ const url = new URL(request.url ?? "/", "http://localhost");
158
+ const match = this.match(url.pathname);
159
+ if (!match)
160
+ return false;
161
+ const user = this.authorize(request.headers, match);
162
+ if (!user)
163
+ return false;
164
+ const resolution = await this.resolver.resolve(user, match.service);
165
+ if (resolution.kind !== "ready")
166
+ return false;
167
+ if (resolution.route.stripPrefix)
168
+ request.url = outboundPath(request.url, match.mount);
169
+ this.upgrades.add(socket);
170
+ socket.once("close", () => this.upgrades.delete(socket));
171
+ proxyWebSocket(request, socket, head, resolution.session, resolution.route.port, this.forwardingReadyTimeoutMs);
172
+ return true;
173
+ }
174
+ /** Closes every forwarding bridge, for example after the Lakebox stops. */
175
+ async closeBridges() {
176
+ this.upgrades.forEach((socket) => socket.destroy());
177
+ this.upgrades.clear();
178
+ const bridges = [...this.bridges.values()];
179
+ this.bridges.clear();
180
+ await Promise.all(bridges.map(closeBridge));
181
+ }
182
+ /** Suppresses expected proxy errors once shutdown begins. */
183
+ beginShutdown() {
184
+ this.shuttingDown = true;
185
+ }
186
+ /** Closes bridges and the shared proxy. */
187
+ async close() {
188
+ this.beginShutdown();
189
+ await this.closeBridges();
190
+ this.proxy.close();
191
+ }
192
+ authorize(headers, match) {
193
+ const user = this.resolver.user(headers);
194
+ return user?.hash === match.userHash ? user : undefined;
195
+ }
196
+ async bridge(session, port) {
197
+ const existing = this.bridges.get(port);
198
+ if (existing?.session === session)
199
+ return existing;
200
+ if (existing) {
201
+ this.bridges.delete(port);
202
+ void closeBridge(existing);
203
+ }
204
+ const sockets = new Set();
205
+ const server = createServer((socket) => {
206
+ sockets.add(socket);
207
+ socket.once("close", () => sockets.delete(socket));
208
+ bridgeSocket(socket, session.forwardOut("127.0.0.1", port), this.forwardingReadyTimeoutMs);
209
+ });
210
+ const target = await listenTarget(server);
211
+ const bridge = { session, server, target, sockets };
212
+ const raced = this.bridges.get(port);
213
+ if (raced?.session === session) {
214
+ void closeBridge(bridge);
215
+ return raced;
216
+ }
217
+ this.bridges.set(port, bridge);
218
+ return bridge;
219
+ }
220
+ }
221
+ async function closeBridge(bridge) {
222
+ bridge.sockets.forEach((socket) => socket.destroy());
223
+ await closeServer(bridge.server);
224
+ }
225
+ function stripIdentityHeaders(request) {
226
+ for (const name of [ACCESS_TOKEN_HEADER, "authorization", "cookie"])
227
+ delete request.headers[name];
228
+ }
229
+ function sendLoadingResponse(accept, response) {
230
+ response.statusCode = 202;
231
+ response.setHeader("cache-control", "no-store");
232
+ response.setHeader("retry-after", "2");
233
+ const acceptsHtml = (Array.isArray(accept) ? accept.join(",") : (accept ?? ""))
234
+ .toLowerCase()
235
+ .includes("text/html");
236
+ if (!acceptsHtml) {
237
+ response.setHeader("content-type", "application/json; charset=utf-8");
238
+ response.end(JSON.stringify({ status: "loading", message: "Starting Lakebox service" }));
239
+ return;
240
+ }
241
+ response.setHeader("content-type", "text/html; charset=utf-8");
242
+ response.end(`<!doctype html>
243
+ <html lang="en">
244
+ <head>
245
+ <meta charset="utf-8">
246
+ <meta name="viewport" content="width=device-width,initial-scale=1">
247
+ <meta http-equiv="refresh" content="2">
248
+ <title>Starting Lakebox service</title>
249
+ <style>
250
+ html{color-scheme:light dark;font-family:system-ui,sans-serif}body{min-height:100vh;margin:0;display:grid;place-items:center;background:Canvas;color:CanvasText}.loading{display:flex;align-items:center;gap:.75rem}.spinner{width:1.25rem;height:1.25rem;border:.2rem solid color-mix(in srgb,CanvasText 20%,transparent);border-top-color:CanvasText;border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}
251
+ </style>
252
+ </head>
253
+ <body><div class="loading" role="status"><span class="spinner" aria-hidden="true"></span><span>Starting Lakebox service</span></div></body>
254
+ </html>`);
255
+ }
256
+ function isConnectionClosureError(error) {
257
+ const code = error && typeof error === "object" && "code" in error
258
+ ? String(error.code)
259
+ : undefined;
260
+ return (code === "ECONNRESET" ||
261
+ code === "EPIPE" ||
262
+ code === "ERR_STREAM_PREMATURE_CLOSE" ||
263
+ code === "ERR_STREAM_DESTROYED");
264
+ }
265
+ function replayParsedJsonBody(request) {
266
+ if (!request.readableEnded || request.body === undefined)
267
+ return undefined;
268
+ if (!request.header("content-type")?.toLowerCase().includes("json"))
269
+ return undefined;
270
+ const body = Buffer.from(JSON.stringify(request.body));
271
+ request.headers["content-length"] = String(body.byteLength);
272
+ delete request.headers["transfer-encoding"];
273
+ return Readable.from(body);
274
+ }
275
+ async function injectHtmlHead(proxyResponse, response, head) {
276
+ try {
277
+ const chunks = [];
278
+ for await (const chunk of proxyResponse) {
279
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
280
+ }
281
+ const source = Buffer.concat(chunks).toString("utf8");
282
+ const body = source.includes("<head>")
283
+ ? source.replace("<head>", `<head>${head}`)
284
+ : source.includes("</head>")
285
+ ? source.replace("</head>", `${head}</head>`)
286
+ : source;
287
+ for (const [name, value] of Object.entries(proxyResponse.headers)) {
288
+ if (value === undefined || name === "content-length" || name === "transfer-encoding") {
289
+ continue;
290
+ }
291
+ response.setHeader(name, value);
292
+ }
293
+ response.statusCode = proxyResponse.statusCode ?? 200;
294
+ if (proxyResponse.statusMessage)
295
+ response.statusMessage = proxyResponse.statusMessage;
296
+ response.setHeader("content-length", Buffer.byteLength(body));
297
+ response.end(body);
298
+ }
299
+ catch (error) {
300
+ logger.warn("Lakebox route HTML injection failed", {
301
+ error: errorUtils.toError(error),
302
+ });
303
+ if (!response.headersSent)
304
+ response.writeHead(502);
305
+ response.end("Lakebox route unavailable");
306
+ }
307
+ }
308
+ function pipeProxyResponse(proxyResponse, response) {
309
+ for (const [name, value] of Object.entries(proxyResponse.headers)) {
310
+ if (value !== undefined)
311
+ response.setHeader(name, value);
312
+ }
313
+ response.statusCode = proxyResponse.statusCode ?? 200;
314
+ if (proxyResponse.statusMessage)
315
+ response.statusMessage = proxyResponse.statusMessage;
316
+ proxyResponse.pipe(response);
317
+ }
318
+ async function closeServer(server) {
319
+ await new Promise((resolve) => server.close(() => resolve()));
320
+ }
321
+ function routeRedirectUrl(url, routePath) {
322
+ const parsed = new URL(url, "http://localhost");
323
+ parsed.pathname = `${routePath}/`;
324
+ return `${parsed.pathname}${parsed.search}`;
325
+ }
326
+ /**
327
+ * Keep the AppKit request URL on the inbound IncomingMessage so AppKit's
328
+ * request logging and tracing see the public route. Prefix
329
+ * stripping is applied only on the outbound proxy request.
330
+ */
331
+ function outboundPath(requestUrl, stripPrefix) {
332
+ const url = requestUrl && requestUrl.length > 0 ? requestUrl : "/";
333
+ const queryIndex = url.indexOf("?");
334
+ const pathname = queryIndex >= 0 ? url.slice(0, queryIndex) : url;
335
+ const query = queryIndex >= 0 ? url.slice(queryIndex) : "";
336
+ if (!stripPrefix)
337
+ return url.startsWith("/") ? url : `/${url}`;
338
+ return `${pathname.slice(stripPrefix.length) || "/"}${query}`;
339
+ }
340
+ async function listenTarget(server) {
341
+ await new Promise((resolve, reject) => {
342
+ server.once("error", reject);
343
+ server.listen(0, "127.0.0.1", resolve);
344
+ });
345
+ const address = server.address();
346
+ if (!address || typeof address === "string") {
347
+ throw new Error("Failed to bind Lakebox route bridge");
348
+ }
349
+ return { host: "127.0.0.1", port: address.port, protocol: "http:" };
350
+ }
351
+ function bridgeSocket(socket, channelPromise, readyTimeoutMs, initialData) {
352
+ const pending = new PassThrough({ highWaterMark: MAX_PENDING_BYTES });
353
+ let channel;
354
+ let timedOut = false;
355
+ const readyTimeout = setTimeout(() => {
356
+ timedOut = true;
357
+ pending.destroy();
358
+ channel?.destroy();
359
+ if (!socket.destroyed) {
360
+ socket.end("HTTP/1.1 504 Gateway Timeout\r\nConnection: close\r\nContent-Length: 0\r\n\r\n");
361
+ }
362
+ }, readyTimeoutMs);
363
+ readyTimeout.unref?.();
364
+ const clearReadyTimeout = () => clearTimeout(readyTimeout);
365
+ if (initialData)
366
+ pending.write(initialData);
367
+ socket.once("error", () => {
368
+ clearReadyTimeout();
369
+ pending.destroy();
370
+ channel?.destroy();
371
+ });
372
+ socket.pipe(pending);
373
+ socket.once("close", () => {
374
+ clearReadyTimeout();
375
+ pending.destroy();
376
+ channel?.destroy();
377
+ });
378
+ void channelPromise.then((openedChannel) => {
379
+ if (timedOut || socket.destroyed) {
380
+ pending.destroy();
381
+ openedChannel.destroy();
382
+ return;
383
+ }
384
+ channel = openedChannel;
385
+ clearReadyTimeout();
386
+ let channelEnded = false;
387
+ channel.once("end", () => {
388
+ channelEnded = true;
389
+ });
390
+ channel.once("close", () => {
391
+ if (!channelEnded && !socket.destroyed)
392
+ socket.destroy();
393
+ });
394
+ channel.once("error", () => socket.destroy());
395
+ channel.pipe(socket);
396
+ pending.pipe(channel);
397
+ }, (error) => {
398
+ clearReadyTimeout();
399
+ if (!timedOut && !socket.destroyed) {
400
+ logger.warn("Lakebox route forwarding channel failed", {
401
+ error: errorUtils.toError(error),
402
+ });
403
+ socket.end("HTTP/1.1 502 Bad Gateway\r\nConnection: close\r\nContent-Length: 0\r\n\r\n");
404
+ }
405
+ pending.destroy();
406
+ if (timedOut || socket.destroyed)
407
+ socket.destroy();
408
+ });
409
+ }
410
+ function proxyWebSocket(request, socket, head, session, port, readyTimeoutMs) {
411
+ stripWebsocketIdentityHeaders(request);
412
+ request.headers.origin = `http://127.0.0.1:${port}`;
413
+ request.headers.host = `127.0.0.1:${port}`;
414
+ bridgeSocket(socket, session.forwardOut("127.0.0.1", port), readyTimeoutMs, serializeUpgradeRequest(request, head));
415
+ }
416
+ function stripWebsocketIdentityHeaders(request) {
417
+ for (const name of [
418
+ "authorization",
419
+ "cookie",
420
+ ACCESS_TOKEN_HEADER,
421
+ USER_EMAIL_HEADER,
422
+ USER_ID_HEADER,
423
+ ]) {
424
+ delete request.headers[name];
425
+ }
426
+ }
427
+ function serializeUpgradeRequest(request, head) {
428
+ const lines = [
429
+ `${request.method ?? "GET"} ${request.url ?? "/"} HTTP/${request.httpVersion || "1.1"}`,
430
+ ];
431
+ for (const [name, value] of Object.entries(request.headers)) {
432
+ if (value === undefined)
433
+ continue;
434
+ for (const item of Array.isArray(value) ? value : [value]) {
435
+ lines.push(`${name}: ${item}`);
436
+ }
437
+ }
438
+ lines.push("", "");
439
+ return Buffer.concat([Buffer.from(lines.join("\r\n")), head]);
440
+ }
441
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3JvdXRlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsWUFBWSxFQUE0QixNQUFNLFVBQVUsQ0FBQztBQUNsRSxPQUFPLEVBQWUsV0FBVyxFQUFFLFFBQVEsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUNqRSxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsaUJBQWlCLEVBQUUsY0FBYyxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFFM0YsT0FBTyxFQUFFLFVBQVUsRUFBRSxHQUFHLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUd6RCxPQUFPLFdBQVcsTUFBTSxjQUFjLENBQUM7QUFHdkMsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLEdBQUcsSUFBSSxDQUFDO0FBQ3RDLE1BQU0sMkJBQTJCLEdBQUcsTUFBTSxDQUFDO0FBQzNDLE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsdUJBQXVCLENBQUMsQ0FBQztBQXlDbkQ7Ozs7Ozs7R0FPRztBQUNILE1BQU0sT0FBTyxvQkFBb0I7SUFTWjtJQUNBO0lBQ0E7SUFWRixPQUFPLEdBQUcsSUFBSSxHQUFHLEVBQWtCLENBQUM7SUFDcEMsUUFBUSxHQUFHLElBQUksR0FBRyxFQUFVLENBQUM7SUFDN0IsT0FBTyxHQUFHLElBQUksT0FBTyxFQUFvQyxDQUFDO0lBQzFELEtBQUssR0FBRyxXQUFXLENBQUMsaUJBQWlCLENBQUMsRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ3pFLFlBQVksR0FBRyxLQUFLLENBQUM7SUFFN0IsMERBQTBEO0lBQzFELFlBQ21CLE1BQWMsRUFDZCxRQUE4QixFQUM5QiwyQkFBMkIsMkJBQTJCO1FBRnRELFdBQU0sR0FBTixNQUFNLENBQVE7UUFDZCxhQUFRLEdBQVIsUUFBUSxDQUFzQjtRQUM5Qiw2QkFBd0IsR0FBeEIsd0JB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SUFDRixLQUFLLE1BQU0sQ0FBQyxJQUFJLEVBQUUsS0FBSyxDQUFDLElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztRQUM1RCxJQUFJLEtBQUssS0FBSyxTQUFTO1lBQUUsU0FBUztRQUNsQyxLQUFLLE1BQU0sSUFBSSxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQzFELEtBQUssQ0FBQyxJQUFJLENBQUMsR0FBRyxJQUFJLEtBQUssSUFBSSxFQUFFLENBQUMsQ0FBQztRQUNqQyxDQUFDO0lBQ0gsQ0FBQztJQUNELEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ25CLE9BQU8sTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7QUFDaEUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgSW5jb21pbmdIdHRwSGVhZGVycywgSW5jb21pbmdNZXNzYWdlLCBTZXJ2ZXJSZXNwb25zZSB9IGZyb20gXCJub2RlOmh0dHBcIjtcbmltcG9ydCB7IGNyZWF0ZVNlcnZlciwgdHlwZSBTZXJ2ZXIsIHR5cGUgU29ja2V0IH0gZnJvbSBcIm5vZGU6bmV0XCI7XG5pbXBvcnQgeyB0eXBlIER1cGxleCwgUGFzc1Rocm91Z2gsIFJlYWRhYmxlIH0gZnJvbSBcIm5vZGU6c3RyZWFtXCI7XG5pbXBvcnQgeyBBQ0NFU1NfVE9LRU5fSEVBREVSLCBVU0VSX0VNQUlMX0hFQURFUiwgVVNFUl9JRF9IRUFERVIgfSBmcm9tIFwiQGRieC10b29scy9hcHBraXRcIjtcbmltcG9ydCB0eXBlIHsgU3NoU2Vzc2lvbiB9IGZyb20gXCJAZGJ4LXRvb2xzL2xha2Vib3gvc2Vzc2lvblwiO1xuaW1wb3J0IHsgZXJyb3JVdGlscywgbG9nIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB0eXBlIHsgTGFrZWJveFVzZXIgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtbGFrZWJveFwiO1xuaW1wb3J0IHR5cGUgeyBOZXh0RnVuY3Rpb24sIFJlcXVlc3QsIFJlc3BvbnNlIH0gZnJvbSBcImV4cHJlc3NcIjtcbmltcG9ydCBQcm94eU1vZHVsZSBmcm9tIFwiaHR0cC1wcm94eS0zXCI7XG5pbXBvcnQgdHlwZSB7IExha2Vib3hTZXJ2aWNlUm91dGUgfSBmcm9tIFwiLi9zZXJ2aWNlcy50c1wiO1xuXG5jb25zdCBNQVhfUEVORElOR19CWVRFUyA9IDEwMjQgKiAxMDI0O1xuY29uc3QgRk9SV0FSRElOR19SRUFEWV9USU1FT1VUX01TID0gMTVfMDAwO1xuY29uc3QgbG9nZ2VyID0gbG9nLmxvZ2dlcihcImFwcGtpdC1sYWtlYm94OnJvdXRlc1wiKTtcblxuLyoqIFJlc3VsdCBvZiByZXNvbHZpbmcgb25lIHVzZXIncyBzZXJ2aWNlIGZvciBhIHJvdXRlZCByZXF1ZXN0LiAqL1xuZXhwb3J0IHR5cGUgTGFrZWJveFJvdXRlUmVzb2x1dGlvbiA9XG4gIHwgeyBraW5kOiBcInJlYWR5XCI7IHNlc3Npb246IFNzaFNlc3Npb247IHJvdXRlOiBMYWtlYm94U2VydmljZVJvdXRlIH1cbiAgfCB7IGtpbmQ6IFwibG9hZGluZ1wiIH1cbiAgfCB7IGtpbmQ6IFwiZmFpbGVkXCI7IG1lc3NhZ2U6IHN0cmluZyB9XG4gIHwgeyBraW5kOiBcIm1pc3NpbmdcIiB9O1xuXG4vKiogUnVudGltZSBjYWxsYmFja3MgdGhhdCBhdXRob3JpemUgYW5kIHJlc29sdmUgZGV0ZXJtaW5pc3RpYyByb3V0ZXMuICovXG5leHBvcnQgaW50ZXJmYWNlIExha2Vib3hSb3V0ZVJlc29sdmVyIHtcbiAgLyoqIFJldHVybnMgdGhlIGF1dGhlbnRpY2F0ZWQgTGFrZWJveCB1c2VyIGZvciByZXF1ZXN0IGhlYWRlcnMsIG9yIGB1bmRlZmluZWRgLiAqL1xuICB1c2VyKGhlYWRlcnM6IEluY29taW5nSHR0cEhlYWRlcnMpOiBMYWtlYm94VXNlciB8IHVuZGVmaW5lZDtcbiAgLyoqXG4gICAqIFJlc29sdmVzIGEgcnVubmluZyBzZXJ2aWNlLiBSZXR1cm5zIGBsb2FkaW5nYCBhZnRlciBzdGFydGluZyB0aGUgTGFrZWJveFxuICAgKiBvciBzZXJ2aWNlIGluIHRoZSBiYWNrZ3JvdW5kLCBhbmQgYG1pc3NpbmdgIGZvciBhbiB1bmtub3duIHNlcnZpY2UuXG4gICAqL1xuICByZXNvbHZlKHVzZXI6IExha2Vib3hVc2VyLCBzZXJ2aWNlOiBzdHJpbmcpOiBQcm9taXNlPExha2Vib3hSb3V0ZVJlc29sdXRpb24+O1xuICAvKiogUmVwb3J0cyB0aGF0IGEgcmVhZHkgc2VydmljZSByZWZ1c2VkIGEgZm9yd2FyZGVkIGNvbm5lY3Rpb24uICovXG4gIHVuYXZhaWxhYmxlKHVzZXI6IExha2Vib3hVc2VyLCBzZXJ2aWNlOiBzdHJpbmcpOiB2b2lkO1xufVxuXG5pbnRlcmZhY2UgUm91dGVNYXRjaCB7XG4gIHVzZXJIYXNoOiBzdHJpbmc7XG4gIHNlcnZpY2U6IHN0cmluZztcbiAgbW91bnQ6IHN0cmluZztcbn1cblxuaW50ZXJmYWNlIEJyaWRnZSB7XG4gIHNlc3Npb246IFNzaFNlc3Npb247XG4gIHNlcnZlcjogU2VydmVyO1xuICB0YXJnZXQ6IHsgaG9zdDogc3RyaW5nOyBwb3J0OiBudW1iZXI7IHByb3RvY29sOiBzdHJpbmcgfTtcbiAgc29ja2V0czogU2V0PFNvY2tldD47XG59XG5cbmludGVyZmFjZSBQZW5kaW5nUmVzcG9uc2Uge1xuICB1c2VyOiBMYWtlYm94VXNlcjtcbiAgc2VydmljZTogc3RyaW5nO1xuICBoZWFkPzogc3RyaW5nO1xufVxuXG4vKipcbiAqIFByb3hpZXMgYDxwcmVmaXg+Lzx1c2VyLWhhc2g+LzxzZXJ2aWNlPmAgdG8gdGhhdCB1c2VyJ3Mgc2VydmljZSBwb3J0LlxuICpcbiAqIFRoZSB1c2VyIGhhc2ggaXMgdGhlIG9ubHkgcm91dGluZyBrZXkuIEEgcmVxdWVzdCBpcyBmb3J3YXJkZWQgb25seSB3aGVuIGl0c1xuICogYXV0aGVudGljYXRlZCBpZGVudGl0eSBoYXNoZXMgdG8gdGhlIHNhbWUgdmFsdWUsIGFuZCB0aGUgcG9ydCBpcyBkZXJpdmVkIGZyb21cbiAqIHRoZSBoYXNoIGFuZCBzZXJ2aWNlIG5hbWUsIHNvIHRoZXJlIGlzIG5vIHN0b3JlZCByb3V0ZSB0YWJsZS4gUmVxdWVzdHNcbiAqIHdpdGhvdXQgYW4gaWRlbnRpdHkgYXJlIHJlamVjdGVkIHdpdGggNDA0LCBpbmNsdWRpbmcgV2ViU29ja2V0IHVwZ3JhZGVzLlxuICovXG5leHBvcnQgY2xhc3MgTGFrZWJveFJvdXRlUmVnaXN0cnkge1xuICBwcml2YXRlIHJlYWRvbmx5IGJyaWRnZXMgPSBuZXcgTWFwPG51bWJlciwgQnJpZGdlPigpO1xuICBwcml2YXRlIHJlYWRvbmx5IHVwZ3JhZGVzID0gbmV3IFNldDxTb2NrZXQ+KCk7XG4gIHByaXZhdGUgcmVhZG9ubHkgcGVuZGluZyA9IG5ldyBXZWFrTWFwPEluY29taW5nTWVzc2FnZSwgUGVuZGluZ1Jlc3BvbnNlPigpO1xuICBwcml2YXRlIHJlYWRvbmx5IHByb3h5ID0gUHJveHlNb2R1bGUuY3JlYXRlUHJveHlTZXJ2ZXIoeyB3czogdHJ1ZSwgeGZ3ZDogdHJ1ZSB9KTtcbiAgcHJpdmF0ZSBzaHV0dGluZ0Rvd24gPSBmYWxzZTtcblxuICAvKiogQ3JlYXRlcyBhIHJlZ2lzdHJ5IGJlbmVhdGggb25lIEFwcEtpdCByb3V0ZSBwcmVmaXguICovXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgcHJlZml4OiBzdHJpbmcsXG4gICAgcHJpdmF0ZSByZWFkb25seSByZXNvbHZlcjogTGFrZWJveFJvdXRlUmVzb2x2ZXIsXG4gICAgcHJpdmF0ZSByZWFkb25seSBmb3J3YXJkaW5nUmVhZHlUaW1lb3V0TXMgPSBGT1JXQVJESU5HX1JFQURZX1RJTUVPVVRfTVMsXG4gICkge1xuICAgIHRoaXMucHJveHkub24oXCJlcnJvclwiLCAoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlKSA9PiB7XG4gICAgICBpZiAodGhpcy5zaHV0dGluZ0Rvd24gJiYgaXNDb25uZWN0aW9uQ2xvc3VyZUVycm9yKGVycm9yKSkge1xuICAgICAgICByZXNwb25zZS5kZXN0cm95KCk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHBlbmRpbmcgPSB0aGlzLnBlbmRpbmcuZ2V0KHJlcXVlc3QpO1xuICAgICAgaWYgKHBlbmRpbmcgJiYgXCJ3cml0ZUhlYWRcIiBpbiByZXNwb25zZSAmJiAhcmVzcG9uc2UuaGVhZGVyc1NlbnQpIHtcbiAgICAgICAgdGhpcy5yZXNvbHZlci51bmF2YWlsYWJsZShwZW5kaW5nLnVzZXIsIHBlbmRpbmcuc2VydmljZSk7XG4gICAgICAgIHNlbmRMb2FkaW5nUmVzcG9uc2UocmVxdWVzdC5oZWFkZXJzLmFjY2VwdCwgcmVzcG9uc2UpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBsb2dnZXIud2FybihcIkxha2Vib3ggcm91dGUgcHJveHkgZmFpbGVkXCIsIHtcbiAgICAgICAgbWV0aG9kOiByZXF1ZXN0Lm1ldGhvZCxcbiAgICAgICAgcGF0aDogcmVxdWVzdC51cmwsXG4gICAgICAgIGVycm9yLFxuICAgICAgfSk7XG4gICAgICBpZiAoXCJ3cml0ZUhlYWRcIiBpbiByZXNwb25zZSkge1xuICAgICAgICBpZiAocmVzcG9uc2UuaGVhZGVyc1NlbnQpIHtcbiAgICAgICAgICByZXNwb25zZS5kZXN0cm95KCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmVzcG9uc2Uud3JpdGVIZWFkKDUwMik7XG4gICAgICAgICAgcmVzcG9uc2UuZW5kKFwiTGFrZWJveCByb3V0ZSB1bmF2YWlsYWJsZVwiKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmICghcmVzcG9uc2UuZGVzdHJveWVkKSB7XG4gICAgICAgIHJlc3BvbnNlLmRlc3Ryb3koKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICB0aGlzLnByb3h5Lm9uKFwicHJveHlSZXNcIiwgKHByb3h5UmVzcG9uc2UsIHJlcXVlc3QsIHJlc3BvbnNlKSA9PiB7XG4gICAgICBjb25zdCBwZW5kaW5nID0gdGhpcy5wZW5kaW5nLmdldChyZXF1ZXN0KTtcbiAgICAgIGlmIChwZW5kaW5nICYmIFs1MDIsIDUwMywgNTA0XS5pbmNsdWRlcyhwcm94eVJlc3BvbnNlLnN0YXR1c0NvZGUgPz8gMCkpIHtcbiAgICAgICAgcHJveHlSZXNwb25zZS5yZXN1bWUoKTtcbiAgICAgICAgdGhpcy5yZXNvbHZlci51bmF2YWlsYWJsZShwZW5kaW5nLnVzZXIsIHBlbmRpbmcuc2VydmljZSk7XG4gICAgICAgIHNlbmRMb2FkaW5nUmVzcG9uc2UocmVxdWVzdC5oZWFkZXJzLmFjY2VwdCwgcmVzcG9uc2UpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBpZiAocGVuZGluZz8uaGVhZCkge1xuICAgICAgICB2b2lkIGluamVjdEh0bWxIZWFkKHByb3h5UmVzcG9uc2UsIHJlc3BvbnNlLCBwZW5kaW5nLmhlYWQpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBwaXBlUHJveHlSZXNwb25zZShwcm94eVJlc3BvbnNlLCByZXNwb25zZSk7XG4gICAgfSk7XG4gIH1cblxuICAvKiogUmV0dXJucyB0aGUgcm91dGUgcGF0aCBzZWdtZW50IG1hdGNoIGZvciBhIHJlcXVlc3QgcGF0aC4gKi9cbiAgbWF0Y2gocGF0aDogc3RyaW5nKTogUm91dGVNYXRjaCB8IHVuZGVmaW5lZCB7XG4gICAgY29uc3Qgcm9vdCA9IHRoaXMucHJlZml4LnJlcGxhY2UoL1xcLyskLywgXCJcIik7XG4gICAgaWYgKCFwYXRoLnN0YXJ0c1dpdGgoYCR7cm9vdH0vYCkpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgY29uc3QgW3VzZXJIYXNoLCBzZXJ2aWNlXSA9IHBhdGguc2xpY2Uocm9vdC5sZW5ndGggKyAxKS5zcGxpdChcIi9cIik7XG4gICAgaWYgKCF1c2VySGFzaCB8fCAhc2VydmljZSkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICByZXR1cm4geyB1c2VySGFzaCwgc2VydmljZSwgbW91bnQ6IGAke3Jvb3R9LyR7dXNlckhhc2h9LyR7c2VydmljZX1gIH07XG4gIH1cblxuICAvKiogRXhwcmVzcyBtaWRkbGV3YXJlIHRoYXQgYXV0aG9yaXplcywgcmV3cml0ZXMsIGFuZCBwcm94aWVzIG1hdGNoaW5nIHJlcXVlc3RzLiAqL1xuICBtaWRkbGV3YXJlKCkge1xuICAgIHJldHVybiBhc3luYyAocmVxdWVzdDogUmVxdWVzdCwgcmVzcG9uc2U6IFJlc3BvbnNlLCBuZXh0OiBOZXh0RnVuY3Rpb24pID0+IHtcbiAgICAgIGNvbnN0IG1hdGNoID0gdGhpcy5tYXRjaChyZXF1ZXN0LnBhdGgpO1xuICAgICAgaWYgKCFtYXRjaCkgcmV0dXJuIG5leHQoKTtcbiAgICAgIGNvbnN0IHVzZXIgPSB0aGlzLmF1dGhvcml6ZShyZXF1ZXN0LmhlYWRlcnMsIG1hdGNoKTtcbiAgICAgIGlmICghdXNlcikge1xuICAgICAgICByZXNwb25zZS5zdGF0dXNDb2RlID0gNDA0O1xuICAgICAgICByZXNwb25zZS5lbmQoXCJOb3QgRm91bmRcIik7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGlmIChyZXF1ZXN0LnBhdGggPT09IG1hdGNoLm1vdW50KSB7XG4gICAgICAgIHJlc3BvbnNlLnN0YXR1c0NvZGUgPSAzMDg7XG4gICAgICAgIHJlc3BvbnNlLnNldEhlYWRlcihcImxvY2F0aW9uXCIsIHJvdXRlUmVkaXJlY3RVcmwocmVxdWVzdC51cmwsIG1hdGNoLm1vdW50KSk7XG4gICAgICAgIHJlc3BvbnNlLmVuZCgpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBsZXQgcmVzb2x1dGlvbjogTGFrZWJveFJvdXRlUmVzb2x1dGlvbjtcbiAgICAgIHRyeSB7XG4gICAgICAgIHJlc29sdXRpb24gPSBhd2FpdCB0aGlzLnJlc29sdmVyLnJlc29sdmUodXNlciwgbWF0Y2guc2VydmljZSk7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICByZXR1cm4gbmV4dChlcnJvcik7XG4gICAgICB9XG4gICAgICBpZiAocmVzb2x1dGlvbi5raW5kID09PSBcIm1pc3NpbmdcIikge1xuICAgICAgICByZXNwb25zZS5zdGF0dXNDb2RlID0gNDA0O1xuICAgICAgICByZXNwb25zZS5lbmQoXCJOb3QgRm91bmRcIik7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGlmIChyZXNvbHV0aW9uLmtpbmQgPT09IFwibG9hZGluZ1wiKSB7XG4gICAgICAgIHNlbmRMb2FkaW5nUmVzcG9uc2UocmVxdWVzdC5oZWFkZXIoXCJhY2NlcHRcIiksIHJlc3BvbnNlKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgaWYgKHJlc29sdXRpb24ua2luZCA9PT0gXCJmYWlsZWRcIikge1xuICAgICAgICByZXNwb25zZS5zdGF0dXNDb2RlID0gNTAyO1xuICAgICAgICByZXNwb25zZS5zZXRIZWFkZXIoXCJjb250ZW50LXR5cGVcIiwgXCJ0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04XCIpO1xuICAgICAgICByZXNwb25zZS5lbmQoYExha2Vib3ggc2VydmljZSBmYWlsZWQ6ICR7cmVzb2x1dGlvbi5tZXNzYWdlfWApO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBjb25zdCBicmlkZ2UgPSBhd2FpdCB0aGlzLmJyaWRnZShyZXNvbHV0aW9uLnNlc3Npb24sIHJlc29sdXRpb24ucm91dGUucG9ydCk7XG4gICAgICBjb25zdCBoZWFkID1cbiAgICAgICAgcmVxdWVzdC5tZXRob2QgPT09IFwiR0VUXCIgJiYgcmVxdWVzdC5wYXRoID09PSBgJHttYXRjaC5tb3VudH0vYFxuICAgICAgICAgID8gcmVzb2x1dGlvbi5yb3V0ZS5odG1sSGVhZFxuICAgICAgICAgIDogdW5kZWZpbmVkO1xuICAgICAgaWYgKGhlYWQpIHJlcXVlc3QuaGVhZGVyc1tcImFjY2VwdC1lbmNvZGluZ1wiXSA9IFwiaWRlbnRpdHlcIjtcbiAgICAgIHRoaXMucGVuZGluZy5zZXQocmVxdWVzdCwgeyB1c2VyLCBzZXJ2aWNlOiBtYXRjaC5zZXJ2aWNlLCAuLi4oaGVhZCA/IHsgaGVhZCB9IDoge30pIH0pO1xuICAgICAgY29uc3QgcmVwbGF5Qm9keSA9IHJlcGxheVBhcnNlZEpzb25Cb2R5KHJlcXVlc3QpO1xuICAgICAgaWYgKHJlc29sdXRpb24ucm91dGUuc3RyaXBQcmVmaXgpIHJlcXVlc3QudXJsID0gb3V0Ym91bmRQYXRoKHJlcXVlc3QudXJsLCBtYXRjaC5tb3VudCk7XG4gICAgICBzdHJpcElkZW50aXR5SGVhZGVycyhyZXF1ZXN0KTtcbiAgICAgIHRoaXMucHJveHkud2ViKHJlcXVlc3QsIHJlc3BvbnNlLCB7XG4gICAgICAgIHRhcmdldDogYnJpZGdlLnRhcmdldCxcbiAgICAgICAgY2hhbmdlT3JpZ2luOiB0cnVlLFxuICAgICAgICBzZWxmSGFuZGxlUmVzcG9uc2U6IHRydWUsXG4gICAgICAgIC4uLihyZXBsYXlCb2R5ID8geyBidWZmZXI6IHJlcGxheUJvZHkgfSA6IHt9KSxcbiAgICAgIH0pO1xuICAgIH07XG4gIH1cblxuICAvKiogTnVtYmVyIG9mIG9wZW4gZm9yd2FyZGVkIEhUVFAgYW5kIFdlYlNvY2tldCBjb25uZWN0aW9ucy4gKi9cbiAgYWN0aXZlQ29ubmVjdGlvbnMoKTogbnVtYmVyIHtcbiAgICBsZXQgY291bnQgPSB0aGlzLnVwZ3JhZGVzLnNpemU7XG4gICAgZm9yIChjb25zdCBicmlkZ2Ugb2YgdGhpcy5icmlkZ2VzLnZhbHVlcygpKSBjb3VudCArPSBicmlkZ2Uuc29ja2V0cy5zaXplO1xuICAgIHJldHVybiBjb3VudDtcbiAgfVxuXG4gIC8qKiBSZXR1cm5zIHdoZXRoZXIgYW4gdXBncmFkZSBwYXRoIGJlbG9uZ3MgdG8gdGhlIExha2Vib3ggcm91dGUgcHJlZml4LiAqL1xuICBjYW5IYW5kbGVVcGdyYWRlKHBhdGg6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBCb29sZWFuKHRoaXMubWF0Y2gocGF0aCkpO1xuICB9XG5cbiAgLyoqIFByb3hpZXMgYW4gYXV0aG9yaXplZCBXZWJTb2NrZXQgdXBncmFkZS4gUmV0dXJucyBmYWxzZSB3aGVuIGl0IHdhcyBub3QgaGFuZGxlZC4gKi9cbiAgYXN5bmMgaGFuZGxlVXBncmFkZShyZXF1ZXN0OiBJbmNvbWluZ01lc3NhZ2UsIHNvY2tldDogU29ja2V0LCBoZWFkOiBCdWZmZXIpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBjb25zdCB1cmwgPSBuZXcgVVJMKHJlcXVlc3QudXJsID8/IFwiL1wiLCBcImh0dHA6Ly9sb2NhbGhvc3RcIik7XG4gICAgY29uc3QgbWF0Y2ggPSB0aGlzLm1hdGNoKHVybC5wYXRobmFtZSk7XG4gICAgaWYgKCFtYXRjaCkgcmV0dXJuIGZhbHNlO1xuICAgIGNvbnN0IHVzZXIgPSB0aGlzLmF1dGhvcml6ZShyZXF1ZXN0LmhlYWRlcnMsIG1hdGNoKTtcbiAgICBpZiAoIXVzZXIpIHJldHVybiBmYWxzZTtcbiAgICBjb25zdCByZXNvbHV0aW9uID0gYXdhaXQgdGhpcy5yZXNvbHZlci5yZXNvbHZlKHVzZXIsIG1hdGNoLnNlcnZpY2UpO1xuICAgIGlmIChyZXNvbHV0aW9uLmtpbmQgIT09IFwicmVhZHlcIikgcmV0dXJuIGZhbHNlO1xuICAgIGlmIChyZXNvbHV0aW9uLnJvdXRlLnN0cmlwUHJlZml4KSByZXF1ZXN0LnVybCA9IG91dGJvdW5kUGF0aChyZXF1ZXN0LnVybCwgbWF0Y2gubW91bnQpO1xuICAgIHRoaXMudXBncmFkZXMuYWRkKHNvY2tldCk7XG4gICAgc29ja2V0Lm9uY2UoXCJjbG9zZVwiLCAoKSA9PiB0aGlzLnVwZ3JhZGVzLmRlbGV0ZShzb2NrZXQpKTtcbiAgICBwcm94eVdlYlNvY2tldChcbiAgICAgIHJlcXVlc3QsXG4gICAgICBzb2NrZXQsXG4gICAgICBoZWFkLFxuICAgICAgcmVzb2x1dGlvbi5zZXNzaW9uLFxuICAgICAgcmVzb2x1dGlvbi5yb3V0ZS5wb3J0LFxuICAgICAgdGhpcy5mb3J3YXJkaW5nUmVhZHlUaW1lb3V0TXMsXG4gICAgKTtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIC8qKiBDbG9zZXMgZXZlcnkgZm9yd2FyZGluZyBicmlkZ2UsIGZvciBleGFtcGxlIGFmdGVyIHRoZSBMYWtlYm94IHN0b3BzLiAqL1xuICBhc3luYyBjbG9zZUJyaWRnZXMoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy51cGdyYWRlcy5mb3JFYWNoKChzb2NrZXQpID0+IHNvY2tldC5kZXN0cm95KCkpO1xuICAgIHRoaXMudXBncmFkZXMuY2xlYXIoKTtcbiAgICBjb25zdCBicmlkZ2VzID0gWy4uLnRoaXMuYnJpZGdlcy52YWx1ZXMoKV07XG4gICAgdGhpcy5icmlkZ2VzLmNsZWFyKCk7XG4gICAgYXdhaXQgUHJvbWlzZS5hbGwoYnJpZGdlcy5tYXAoY2xvc2VCcmlkZ2UpKTtcbiAgfVxuXG4gIC8qKiBTdXBwcmVzc2VzIGV4cGVjdGVkIHByb3h5IGVycm9ycyBvbmNlIHNodXRkb3duIGJlZ2lucy4gKi9cbiAgYmVnaW5TaHV0ZG93bigpOiB2b2lkIHtcbiAgICB0aGlzLnNodXR0aW5nRG93biA9IHRydWU7XG4gIH1cblxuICAvKiogQ2xvc2VzIGJyaWRnZXMgYW5kIHRoZSBzaGFyZWQgcHJveHkuICovXG4gIGFzeW5jIGNsb3NlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuYmVnaW5TaHV0ZG93bigpO1xuICAgIGF3YWl0IHRoaXMuY2xvc2VCcmlkZ2VzKCk7XG4gICAgdGhpcy5wcm94eS5jbG9zZSgpO1xuICB9XG5cbiAgcHJpdmF0ZSBhdXRob3JpemUoaGVhZGVyczogSW5jb21pbmdIdHRwSGVhZGVycywgbWF0Y2g6IFJvdXRlTWF0Y2gpOiBMYWtlYm94VXNlciB8IHVuZGVmaW5lZCB7XG4gICAgY29uc3QgdXNlciA9IHRoaXMucmVzb2x2ZXIudXNlcihoZWFkZXJzKTtcbiAgICByZXR1cm4gdXNlcj8uaGFzaCA9PT0gbWF0Y2gudXNlckhhc2ggPyB1c2VyIDogdW5kZWZpbmVkO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBicmlkZ2Uoc2Vzc2lvbjogU3NoU2Vzc2lvbiwgcG9ydDogbnVtYmVyKTogUHJvbWlzZTxCcmlkZ2U+IHtcbiAgICBjb25zdCBleGlzdGluZyA9IHRoaXMuYnJpZGdlcy5nZXQocG9ydCk7XG4gICAgaWYgKGV4aXN0aW5nPy5zZXNzaW9uID09PSBzZXNzaW9uKSByZXR1cm4gZXhpc3Rpbmc7XG4gICAgaWYgKGV4aXN0aW5nKSB7XG4gICAgICB0aGlzLmJyaWRnZXMuZGVsZXRlKHBvcnQpO1xuICAgICAgdm9pZCBjbG9zZUJyaWRnZShleGlzdGluZyk7XG4gICAgfVxuICAgIGNvbnN0IHNvY2tldHMgPSBuZXcgU2V0PFNvY2tldD4oKTtcbiAgICBjb25zdCBzZXJ2ZXIgPSBjcmVhdGVTZXJ2ZXIoKHNvY2tldCkgPT4ge1xuICAgICAgc29ja2V0cy5hZGQoc29ja2V0KTtcbiAgICAgIHNvY2tldC5vbmNlKFwiY2xvc2VcIiwgKCkgPT4gc29ja2V0cy5kZWxldGUoc29ja2V0KSk7XG4gICAgICBicmlkZ2VTb2NrZXQoc29ja2V0LCBzZXNzaW9uLmZvcndhcmRPdXQoXCIxMjcuMC4wLjFcIiwgcG9ydCksIHRoaXMuZm9yd2FyZGluZ1JlYWR5VGltZW91dE1zKTtcbiAgICB9KTtcbiAgICBjb25zdCB0YXJnZXQgPSBhd2FpdCBsaXN0ZW5UYXJnZXQoc2VydmVyKTtcbiAgICBjb25zdCBicmlkZ2UgPSB7IHNlc3Npb24sIHNlcnZlciwgdGFyZ2V0LCBzb2NrZXRzIH07XG4gICAgY29uc3QgcmFjZWQgPSB0aGlzLmJyaWRnZXMuZ2V0KHBvcnQpO1xuICAgIGlmIChyYWNlZD8uc2Vzc2lvbiA9PT0gc2Vzc2lvbikge1xuICAgICAgdm9pZCBjbG9zZUJyaWRnZShicmlkZ2UpO1xuICAgICAgcmV0dXJuIHJhY2VkO1xuICAgIH1cbiAgICB0aGlzLmJyaWRnZXMuc2V0KHBvcnQsIGJyaWRnZSk7XG4gICAgcmV0dXJuIGJyaWRnZTtcbiAgfVxufVxuXG5hc3luYyBmdW5jdGlvbiBjbG9zZUJyaWRnZShicmlkZ2U6IEJyaWRnZSk6IFByb21pc2U8dm9pZD4ge1xuICBicmlkZ2Uuc29ja2V0cy5mb3JFYWNoKChzb2NrZXQpID0+IHNvY2tldC5kZXN0cm95KCkpO1xuICBhd2FpdCBjbG9zZVNlcnZlcihicmlkZ2Uuc2VydmVyKTtcbn1cblxuZnVuY3Rpb24gc3RyaXBJZGVudGl0eUhlYWRlcnMocmVxdWVzdDogSW5jb21pbmdNZXNzYWdlKTogdm9pZCB7XG4gIGZvciAoY29uc3QgbmFtZSBvZiBbQUNDRVNTX1RPS0VOX0hFQURFUiwgXCJhdXRob3JpemF0aW9uXCIsIFwiY29va2llXCJdKSBkZWxldGUgcmVxdWVzdC5oZWFkZXJzW25hbWVdO1xufVxuXG5mdW5jdGlvbiBzZW5kTG9hZGluZ1Jlc3BvbnNlKFxuICBhY2NlcHQ6IHN0cmluZyB8IHN0cmluZ1tdIHwgdW5kZWZpbmVkLFxuICByZXNwb25zZTogUGljazxTZXJ2ZXJSZXNwb25zZSwgXCJzdGF0dXNDb2RlXCIgfCBcInNldEhlYWRlclwiIHwgXCJlbmRcIj4sXG4pOiB2b2lkIHtcbiAgcmVzcG9uc2Uuc3RhdHVzQ29kZSA9IDIwMjtcbiAgcmVzcG9uc2Uuc2V0SGVhZGVyKFwiY2FjaGUtY29udHJvbFwiLCBcIm5vLXN0b3JlXCIpO1xuICByZXNwb25zZS5zZXRIZWFkZXIoXCJyZXRyeS1hZnRlclwiLCBcIjJcIik7XG4gIGNvbnN0IGFjY2VwdHNIdG1sID0gKEFycmF5LmlzQXJyYXkoYWNjZXB0KSA/IGFjY2VwdC5qb2luKFwiLFwiKSA6IChhY2NlcHQgPz8gXCJcIikpXG4gICAgLnRvTG93ZXJDYXNlKClcbiAgICAuaW5jbHVkZXMoXCJ0ZXh0L2h0bWxcIik7XG4gIGlmICghYWNjZXB0c0h0bWwpIHtcbiAgICByZXNwb25zZS5zZXRIZWFkZXIoXCJjb250ZW50LXR5cGVcIiwgXCJhcHBsaWNhdGlvbi9qc29uOyBjaGFyc2V0PXV0Zi04XCIpO1xuICAgIHJlc3BvbnNlLmVuZChKU09OLnN0cmluZ2lmeSh7IHN0YXR1czogXCJsb2FkaW5nXCIsIG1lc3NhZ2U6IFwiU3RhcnRpbmcgTGFrZWJveCBzZXJ2aWNlXCIgfSkpO1xuICAgIHJldHVybjtcbiAgfVxuICByZXNwb25zZS5zZXRIZWFkZXIoXCJjb250ZW50LXR5cGVcIiwgXCJ0ZXh0L2h0bWw7IGNoYXJzZXQ9dXRmLThcIik7XG4gIHJlc3BvbnNlLmVuZChgPCFkb2N0eXBlIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbjxoZWFkPlxuPG1ldGEgY2hhcnNldD1cInV0Zi04XCI+XG48bWV0YSBuYW1lPVwidmlld3BvcnRcIiBjb250ZW50PVwid2lkdGg9ZGV2aWNlLXdpZHRoLGluaXRpYWwtc2NhbGU9MVwiPlxuPG1ldGEgaHR0cC1lcXVpdj1cInJlZnJlc2hcIiBjb250ZW50PVwiMlwiPlxuPHRpdGxlPlN0YXJ0aW5nIExha2Vib3ggc2VydmljZTwvdGl0bGU+XG48c3R5bGU+XG5odG1se2NvbG9yLXNjaGVtZTpsaWdodCBkYXJrO2ZvbnQtZmFtaWx5OnN5c3RlbS11aSxzYW5zLXNlcmlmfWJvZHl7bWluLWhlaWdodDoxMDB2aDttYXJnaW46MDtkaXNwbGF5OmdyaWQ7cGxhY2UtaXRlbXM6Y2VudGVyO2JhY2tncm91bmQ6Q2FudmFzO2NvbG9yOkNhbnZhc1RleHR9LmxvYWRpbmd7ZGlzcGxheTpmbGV4O2FsaWduLWl0ZW1zOmNlbnRlcjtnYXA6Ljc1cmVtfS5zcGlubmVye3dpZHRoOjEuMjVyZW07aGVpZ2h0OjEuMjVyZW07Ym9yZGVyOi4ycmVtIHNvbGlkIGNvbG9yLW1peChpbiBzcmdiLENhbnZhc1RleHQgMjAlLHRyYW5zcGFyZW50KTtib3JkZXItdG9wLWNvbG9yOkNhbnZhc1RleHQ7Ym9yZGVyLXJhZGl1czo1MCU7YW5pbWF0aW9uOnNwaW4gLjhzIGxpbmVhciBpbmZpbml0ZX1Aa2V5ZnJhbWVzIHNwaW57dG97dHJhbnNmb3JtOnJvdGF0ZSgzNjBkZWcpfX1cbjwvc3R5bGU+XG48L2hlYWQ+XG48Ym9keT48ZGl2IGNsYXNzPVwibG9hZGluZ1wiIHJvbGU9XCJzdGF0dXNcIj48c3BhbiBjbGFzcz1cInNwaW5uZXJcIiBhcmlhLWhpZGRlbj1cInRydWVcIj48L3NwYW4+PHNwYW4+U3RhcnRpbmcgTGFrZWJveCBzZXJ2aWNlPC9zcGFuPjwvZGl2PjwvYm9keT5cbjwvaHRtbD5gKTtcbn1cblxuZnVuY3Rpb24gaXNDb25uZWN0aW9uQ2xvc3VyZUVycm9yKGVycm9yOiB1bmtub3duKTogYm9vbGVhbiB7XG4gIGNvbnN0IGNvZGUgPVxuICAgIGVycm9yICYmIHR5cGVvZiBlcnJvciA9PT0gXCJvYmplY3RcIiAmJiBcImNvZGVcIiBpbiBlcnJvclxuICAgICAgPyBTdHJpbmcoKGVycm9yIGFzIHsgY29kZT86IHVua25vd24gfSkuY29kZSlcbiAgICAgIDogdW5kZWZpbmVkO1xuICByZXR1cm4gKFxuICAgIGNvZGUgPT09IFwiRUNPTk5SRVNFVFwiIHx8XG4gICAgY29kZSA9PT0gXCJFUElQRVwiIHx8XG4gICAgY29kZSA9PT0gXCJFUlJfU1RSRUFNX1BSRU1BVFVSRV9DTE9TRVwiIHx8XG4gICAgY29kZSA9PT0gXCJFUlJfU1RSRUFNX0RFU1RST1lFRFwiXG4gICk7XG59XG5cbmZ1bmN0aW9uIHJlcGxheVBhcnNlZEpzb25Cb2R5KHJlcXVlc3Q6IFJlcXVlc3QpOiBSZWFkYWJsZSB8IHVuZGVmaW5lZCB7XG4gIGlmICghcmVxdWVzdC5yZWFkYWJsZUVuZGVkIHx8IHJlcXVlc3QuYm9keSA9PT0gdW5kZWZpbmVkKSByZXR1cm4gdW5kZWZpbmVkO1xuICBpZiAoIXJlcXVlc3QuaGVhZGVyKFwiY29udGVudC10eXBlXCIpPy50b0xvd2VyQ2FzZSgpLmluY2x1ZGVzKFwianNvblwiKSkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgY29uc3QgYm9keSA9IEJ1ZmZlci5mcm9tKEpTT04uc3RyaW5naWZ5KHJlcXVlc3QuYm9keSkpO1xuICByZXF1ZXN0LmhlYWRlcnNbXCJjb250ZW50LWxlbmd0aFwiXSA9IFN0cmluZyhib2R5LmJ5dGVMZW5ndGgpO1xuICBkZWxldGUgcmVxdWVzdC5oZWFkZXJzW1widHJhbnNmZXItZW5jb2RpbmdcIl07XG4gIHJldHVybiBSZWFkYWJsZS5mcm9tKGJvZHkpO1xufVxuXG5hc3luYyBmdW5jdGlvbiBpbmplY3RIdG1sSGVhZChcbiAgcHJveHlSZXNwb25zZTogSW5jb21pbmdNZXNzYWdlLFxuICByZXNwb25zZTogU2VydmVyUmVzcG9uc2UsXG4gIGhlYWQ6IHN0cmluZyxcbik6IFByb21pc2U8dm9pZD4ge1xuICB0cnkge1xuICAgIGNvbnN0IGNodW5rczogQnVmZmVyW10gPSBbXTtcbiAgICBmb3IgYXdhaXQgKGNvbnN0IGNodW5rIG9mIHByb3h5UmVzcG9uc2UpIHtcbiAgICAgIGNodW5rcy5wdXNoKEJ1ZmZlci5pc0J1ZmZlcihjaHVuaykgPyBjaHVuayA6IEJ1ZmZlci5mcm9tKGNodW5rKSk7XG4gICAgfVxuICAgIGNvbnN0IHNvdXJjZSA9IEJ1ZmZlci5jb25jYXQoY2h1bmtzKS50b1N0cmluZyhcInV0ZjhcIik7XG4gICAgY29uc3QgYm9keSA9IHNvdXJjZS5pbmNsdWRlcyhcIjxoZWFkPlwiKVxuICAgICAgPyBzb3VyY2UucmVwbGFjZShcIjxoZWFkPlwiLCBgPGhlYWQ+JHtoZWFkfWApXG4gICAgICA6IHNvdXJjZS5pbmNsdWRlcyhcIjwvaGVhZD5cIilcbiAgICAgICAgPyBzb3VyY2UucmVwbGFjZShcIjwvaGVhZD5cIiwgYCR7aGVhZH08L2hlYWQ+YClcbiAgICAgICAgOiBzb3VyY2U7XG4gICAgZm9yIChjb25zdCBbbmFtZSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKHByb3h5UmVzcG9uc2UuaGVhZGVycykpIHtcbiAgICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkIHx8IG5hbWUgPT09IFwiY29udGVudC1sZW5ndGhcIiB8fCBuYW1lID09PSBcInRyYW5zZmVyLWVuY29kaW5nXCIpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICByZXNwb25zZS5zZXRIZWFkZXIobmFtZSwgdmFsdWUpO1xuICAgIH1cbiAgICByZXNwb25zZS5zdGF0dXNDb2RlID0gcHJveHlSZXNwb25zZS5zdGF0dXNDb2RlID8/IDIwMDtcbiAgICBpZiAocHJveHlSZXNwb25zZS5zdGF0dXNNZXNzYWdlKSByZXNwb25zZS5zdGF0dXNNZXNzYWdlID0gcHJveHlSZXNwb25zZS5zdGF0dXNNZXNzYWdlO1xuICAgIHJlc3BvbnNlLnNldEhlYWRlcihcImNvbnRlbnQtbGVuZ3RoXCIsIEJ1ZmZlci5ieXRlTGVuZ3RoKGJvZHkpKTtcbiAgICByZXNwb25zZS5lbmQoYm9keSk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgbG9nZ2VyLndhcm4oXCJMYWtlYm94IHJvdXRlIEhUTUwgaW5qZWN0aW9uIGZhaWxlZFwiLCB7XG4gICAgICBlcnJvcjogZXJyb3JVdGlscy50b0Vycm9yKGVycm9yKSxcbiAgICB9KTtcbiAgICBpZiAoIXJlc3BvbnNlLmhlYWRlcnNTZW50KSByZXNwb25zZS53cml0ZUhlYWQoNTAyKTtcbiAgICByZXNwb25zZS5lbmQoXCJMYWtlYm94IHJvdXRlIHVuYXZhaWxhYmxlXCIpO1xuICB9XG59XG5cbmZ1bmN0aW9uIHBpcGVQcm94eVJlc3BvbnNlKHByb3h5UmVzcG9uc2U6IEluY29taW5nTWVzc2FnZSwgcmVzcG9uc2U6IFNlcnZlclJlc3BvbnNlKTogdm9pZCB7XG4gIGZvciAoY29uc3QgW25hbWUsIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhwcm94eVJlc3BvbnNlLmhlYWRlcnMpKSB7XG4gICAgaWYgKHZhbHVlICE9PSB1bmRlZmluZWQpIHJlc3BvbnNlLnNldEhlYWRlcihuYW1lLCB2YWx1ZSk7XG4gIH1cbiAgcmVzcG9uc2Uuc3RhdHVzQ29kZSA9IHByb3h5UmVzcG9uc2Uuc3RhdHVzQ29kZSA/PyAyMDA7XG4gIGlmIChwcm94eVJlc3BvbnNlLnN0YXR1c01lc3NhZ2UpIHJlc3BvbnNlLnN0YXR1c01lc3NhZ2UgPSBwcm94eVJlc3BvbnNlLnN0YXR1c01lc3NhZ2U7XG4gIHByb3h5UmVzcG9uc2UucGlwZShyZXNwb25zZSk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGNsb3NlU2VydmVyKHNlcnZlcjogU2VydmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlKSA9PiBzZXJ2ZXIuY2xvc2UoKCkgPT4gcmVzb2x2ZSgpKSk7XG59XG5cbmZ1bmN0aW9uIHJvdXRlUmVkaXJlY3RVcmwodXJsOiBzdHJpbmcsIHJvdXRlUGF0aDogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgcGFyc2VkID0gbmV3IFVSTCh1cmwsIFwiaHR0cDovL2xvY2FsaG9zdFwiKTtcbiAgcGFyc2VkLnBhdGhuYW1lID0gYCR7cm91dGVQYXRofS9gO1xuICByZXR1cm4gYCR7cGFyc2VkLnBhdGhuYW1lfSR7cGFyc2VkLnNlYXJjaH1gO1xufVxuXG4vKipcbiAqIEtlZXAgdGhlIEFwcEtpdCByZXF1ZXN0IFVSTCBvbiB0aGUgaW5ib3VuZCBJbmNvbWluZ01lc3NhZ2Ugc28gQXBwS2l0J3NcbiAqIHJlcXVlc3QgbG9nZ2luZyBhbmQgdHJhY2luZyBzZWUgdGhlIHB1YmxpYyByb3V0ZS4gUHJlZml4XG4gKiBzdHJpcHBpbmcgaXMgYXBwbGllZCBvbmx5IG9uIHRoZSBvdXRib3VuZCBwcm94eSByZXF1ZXN0LlxuICovXG5mdW5jdGlvbiBvdXRib3VuZFBhdGgocmVxdWVzdFVybDogc3RyaW5nIHwgdW5kZWZpbmVkLCBzdHJpcFByZWZpeD86IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IHVybCA9IHJlcXVlc3RVcmwgJiYgcmVxdWVzdFVybC5sZW5ndGggPiAwID8gcmVxdWVzdFVybCA6IFwiL1wiO1xuICBjb25zdCBxdWVyeUluZGV4ID0gdXJsLmluZGV4T2YoXCI/XCIpO1xuICBjb25zdCBwYXRobmFtZSA9IHF1ZXJ5SW5kZXggPj0gMCA/IHVybC5zbGljZSgwLCBxdWVyeUluZGV4KSA6IHVybDtcbiAgY29uc3QgcXVlcnkgPSBxdWVyeUluZGV4ID49IDAgPyB1cmwuc2xpY2UocXVlcnlJbmRleCkgOiBcIlwiO1xuICBpZiAoIXN0cmlwUHJlZml4KSByZXR1cm4gdXJsLnN0YXJ0c1dpdGgoXCIvXCIpID8gdXJsIDogYC8ke3VybH1gO1xuICByZXR1cm4gYCR7cGF0aG5hbWUuc2xpY2Uoc3RyaXBQcmVmaXgubGVuZ3RoKSB8fCBcIi9cIn0ke3F1ZXJ5fWA7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGxpc3RlblRhcmdldChcbiAgc2VydmVyOiBTZXJ2ZXIsXG4pOiBQcm9taXNlPHsgaG9zdDogc3RyaW5nOyBwb3J0OiBudW1iZXI7IHByb3RvY29sOiBzdHJpbmcgfT4ge1xuICBhd2FpdCBuZXcgUHJvbWlzZTx2b2lkPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgc2VydmVyLm9uY2UoXCJlcnJvclwiLCByZWplY3QpO1xuICAgIHNlcnZlci5saXN0ZW4oMCwgXCIxMjcuMC4wLjFcIiwgcmVzb2x2ZSk7XG4gIH0pO1xuICBjb25zdCBhZGRyZXNzID0gc2VydmVyLmFkZHJlc3MoKTtcbiAgaWYgKCFhZGRyZXNzIHx8IHR5cGVvZiBhZGRyZXNzID09PSBcInN0cmluZ1wiKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFwiRmFpbGVkIHRvIGJpbmQgTGFrZWJveCByb3V0ZSBicmlkZ2VcIik7XG4gIH1cbiAgcmV0dXJuIHsgaG9zdDogXCIxMjcuMC4wLjFcIiwgcG9ydDogYWRkcmVzcy5wb3J0LCBwcm90b2NvbDogXCJodHRwOlwiIH07XG59XG5cbmZ1bmN0aW9uIGJyaWRnZVNvY2tldChcbiAgc29ja2V0OiBTb2NrZXQsXG4gIGNoYW5uZWxQcm9taXNlOiBQcm9taXNlPER1cGxleD4sXG4gIHJlYWR5VGltZW91dE1zOiBudW1iZXIsXG4gIGluaXRpYWxEYXRhPzogQnVmZmVyLFxuKTogdm9pZCB7XG4gIGNvbnN0IHBlbmRpbmcgPSBuZXcgUGFzc1Rocm91Z2goeyBoaWdoV2F0ZXJNYXJrOiBNQVhfUEVORElOR19CWVRFUyB9KTtcbiAgbGV0IGNoYW5uZWw6IER1cGxleCB8IHVuZGVmaW5lZDtcbiAgbGV0IHRpbWVkT3V0ID0gZmFsc2U7XG4gIGNvbnN0IHJlYWR5VGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgIHRpbWVkT3V0ID0gdHJ1ZTtcbiAgICBwZW5kaW5nLmRlc3Ryb3koKTtcbiAgICBjaGFubmVsPy5kZXN0cm95KCk7XG4gICAgaWYgKCFzb2NrZXQuZGVzdHJveWVkKSB7XG4gICAgICBzb2NrZXQuZW5kKFwiSFRUUC8xLjEgNTA0IEdhdGV3YXkgVGltZW91dFxcclxcbkNvbm5lY3Rpb246IGNsb3NlXFxyXFxuQ29udGVudC1MZW5ndGg6IDBcXHJcXG5cXHJcXG5cIik7XG4gICAgfVxuICB9LCByZWFkeVRpbWVvdXRNcyk7XG4gIHJlYWR5VGltZW91dC51bnJlZj8uKCk7XG4gIGNvbnN0IGNsZWFyUmVhZHlUaW1lb3V0ID0gKCkgPT4gY2xlYXJUaW1lb3V0KHJlYWR5VGltZW91dCk7XG4gIGlmIChpbml0aWFsRGF0YSkgcGVuZGluZy53cml0ZShpbml0aWFsRGF0YSk7XG4gIHNvY2tldC5vbmNlKFwiZXJyb3JcIiwgKCkgPT4ge1xuICAgIGNsZWFyUmVhZHlUaW1lb3V0KCk7XG4gICAgcGVuZGluZy5kZXN0cm95KCk7XG4gICAgY2hhbm5lbD8uZGVzdHJveSgpO1xuICB9KTtcbiAgc29ja2V0LnBpcGUocGVuZGluZyk7XG4gIHNvY2tldC5vbmNlKFwiY2xvc2VcIiwgKCkgPT4ge1xuICAgIGNsZWFyUmVhZHlUaW1lb3V0KCk7XG4gICAgcGVuZGluZy5kZXN0cm95KCk7XG4gICAgY2hhbm5lbD8uZGVzdHJveSgpO1xuICB9KTtcblxuICB2b2lkIGNoYW5uZWxQcm9taXNlLnRoZW4oXG4gICAgKG9wZW5lZENoYW5uZWwpID0+IHtcbiAgICAgIGlmICh0aW1lZE91dCB8fCBzb2NrZXQuZGVzdHJveWVkKSB7XG4gICAgICAgIHBlbmRpbmcuZGVzdHJveSgpO1xuICAgICAgICBvcGVuZWRDaGFubmVsLmRlc3Ryb3koKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgY2hhbm5lbCA9IG9wZW5lZENoYW5uZWw7XG4gICAgICBjbGVhclJlYWR5VGltZW91dCgpO1xuICAgICAgbGV0IGNoYW5uZWxFbmRlZCA9IGZhbHNlO1xuICAgICAgY2hhbm5lbC5vbmNlKFwiZW5kXCIsICgpID0+IHtcbiAgICAgICAgY2hhbm5lbEVuZGVkID0gdHJ1ZTtcbiAgICAgIH0pO1xuICAgICAgY2hhbm5lbC5vbmNlKFwiY2xvc2VcIiwgKCkgPT4ge1xuICAgICAgICBpZiAoIWNoYW5uZWxFbmRlZCAmJiAhc29ja2V0LmRlc3Ryb3llZCkgc29ja2V0LmRlc3Ryb3koKTtcbiAgICAgIH0pO1xuICAgICAgY2hhbm5lbC5vbmNlKFwiZXJyb3JcIiwgKCkgPT4gc29ja2V0LmRlc3Ryb3koKSk7XG4gICAgICBjaGFubmVsLnBpcGUoc29ja2V0KTtcbiAgICAgIHBlbmRpbmcucGlwZShjaGFubmVsKTtcbiAgICB9LFxuICAgIChlcnJvcikgPT4ge1xuICAgICAgY2xlYXJSZWFkeVRpbWVvdXQoKTtcbiAgICAgIGlmICghdGltZWRPdXQgJiYgIXNvY2tldC5kZXN0cm95ZWQpIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oXCJMYWtlYm94IHJvdXRlIGZvcndhcmRpbmcgY2hhbm5lbCBmYWlsZWRcIiwge1xuICAgICAgICAgIGVycm9yOiBlcnJvclV0aWxzLnRvRXJyb3IoZXJyb3IpLFxuICAgICAgICB9KTtcbiAgICAgICAgc29ja2V0LmVuZChcIkhUVFAvMS4xIDUwMiBCYWQgR2F0ZXdheVxcclxcbkNvbm5lY3Rpb246IGNsb3NlXFxyXFxuQ29udGVudC1MZW5ndGg6IDBcXHJcXG5cXHJcXG5cIik7XG4gICAgICB9XG4gICAgICBwZW5kaW5nLmRlc3Ryb3koKTtcbiAgICAgIGlmICh0aW1lZE91dCB8fCBzb2NrZXQuZGVzdHJveWVkKSBzb2NrZXQuZGVzdHJveSgpO1xuICAgIH0sXG4gICk7XG59XG5cbmZ1bmN0aW9uIHByb3h5V2ViU29ja2V0KFxuICByZXF1ZXN0OiBJbmNvbWluZ01lc3NhZ2UsXG4gIHNvY2tldDogU29ja2V0LFxuICBoZWFkOiBCdWZmZXIsXG4gIHNlc3Npb246IFNzaFNlc3Npb24sXG4gIHBvcnQ6IG51bWJlcixcbiAgcmVhZHlUaW1lb3V0TXM6IG51bWJlcixcbik6IHZvaWQge1xuICBzdHJpcFdlYnNvY2tldElkZW50aXR5SGVhZGVycyhyZXF1ZXN0KTtcbiAgcmVxdWVzdC5oZWFkZXJzLm9yaWdpbiA9IGBodHRwOi8vMTI3LjAuMC4xOiR7cG9ydH1gO1xuICByZXF1ZXN0LmhlYWRlcnMuaG9zdCA9IGAxMjcuMC4wLjE6JHtwb3J0fWA7XG4gIGJyaWRnZVNvY2tldChcbiAgICBzb2NrZXQsXG4gICAgc2Vzc2lvbi5mb3J3YXJkT3V0KFwiMTI3LjAuMC4xXCIsIHBvcnQpLFxuICAgIHJlYWR5VGltZW91dE1zLFxuICAgIHNlcmlhbGl6ZVVwZ3JhZGVSZXF1ZXN0KHJlcXVlc3QsIGhlYWQpLFxuICApO1xufVxuXG5mdW5jdGlvbiBzdHJpcFdlYnNvY2tldElkZW50aXR5SGVhZGVycyhyZXF1ZXN0OiBJbmNvbWluZ01lc3NhZ2UpOiB2b2lkIHtcbiAgZm9yIChjb25zdCBuYW1lIG9mIFtcbiAgICBcImF1dGhvcml6YXRpb25cIixcbiAgICBcImNvb2tpZVwiLFxuICAgIEFDQ0VTU19UT0tFTl9IRUFERVIsXG4gICAgVVNFUl9FTUFJTF9IRUFERVIsXG4gICAgVVNFUl9JRF9IRUFERVIsXG4gIF0pIHtcbiAgICBkZWxldGUgcmVxdWVzdC5oZWFkZXJzW25hbWVdO1xuICB9XG59XG5cbmZ1bmN0aW9uIHNlcmlhbGl6ZVVwZ3JhZGVSZXF1ZXN0KHJlcXVlc3Q6IEluY29taW5nTWVzc2FnZSwgaGVhZDogQnVmZmVyKTogQnVmZmVyIHtcbiAgY29uc3QgbGluZXMgPSBbXG4gICAgYCR7cmVxdWVzdC5tZXRob2QgPz8gXCJHRVRcIn0gJHtyZXF1ZXN0LnVybCA/PyBcIi9cIn0gSFRUUC8ke3JlcXVlc3QuaHR0cFZlcnNpb24gfHwgXCIxLjFcIn1gLFxuICBdO1xuICBmb3IgKGNvbnN0IFtuYW1lLCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMocmVxdWVzdC5oZWFkZXJzKSkge1xuICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkKSBjb250aW51ZTtcbiAgICBmb3IgKGNvbnN0IGl0ZW0gb2YgQXJyYXkuaXNBcnJheSh2YWx1ZSkgPyB2YWx1ZSA6IFt2YWx1ZV0pIHtcbiAgICAgIGxpbmVzLnB1c2goYCR7bmFtZX06ICR7aXRlbX1gKTtcbiAgICB9XG4gIH1cbiAgbGluZXMucHVzaChcIlwiLCBcIlwiKTtcbiAgcmV0dXJuIEJ1ZmZlci5jb25jYXQoW0J1ZmZlci5mcm9tKGxpbmVzLmpvaW4oXCJcXHJcXG5cIikpLCBoZWFkXSk7XG59XG4iXX0=