@dbx-tools/appkit-mastra 0.3.44 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/lib/index.d.ts +71 -0
  2. package/lib/index.js +56 -0
  3. package/lib/src/agents.d.ts +347 -0
  4. package/lib/src/agents.js +554 -0
  5. package/lib/src/chart.d.ts +192 -0
  6. package/lib/src/chart.js +638 -0
  7. package/lib/src/config.d.ts +479 -0
  8. package/lib/src/config.js +190 -0
  9. package/lib/src/defaults.d.ts +68 -0
  10. package/lib/src/defaults.js +107 -0
  11. package/lib/src/filesystems.d.ts +208 -0
  12. package/lib/src/filesystems.js +958 -0
  13. package/lib/src/genie.d.ts +166 -0
  14. package/lib/src/genie.js +969 -0
  15. package/lib/src/history.d.ts +97 -0
  16. package/lib/src/history.js +264 -0
  17. package/lib/src/mcp.d.ts +66 -0
  18. package/lib/src/mcp.js +65 -0
  19. package/lib/src/memory.d.ts +111 -0
  20. package/lib/src/memory.js +275 -0
  21. package/lib/src/mlflow.d.ts +63 -0
  22. package/lib/src/mlflow.js +117 -0
  23. package/lib/src/model.d.ts +62 -0
  24. package/lib/src/model.js +168 -0
  25. package/lib/src/observability.d.ts +81 -0
  26. package/lib/src/observability.js +98 -0
  27. package/lib/src/pagination.d.ts +23 -0
  28. package/lib/src/pagination.js +31 -0
  29. package/lib/src/plugin.d.ts +352 -0
  30. package/lib/src/plugin.js +1015 -0
  31. package/lib/src/processors.d.ts +62 -0
  32. package/lib/src/processors.js +162 -0
  33. package/lib/src/rest.d.ts +36 -0
  34. package/lib/src/rest.js +46 -0
  35. package/lib/src/server.d.ts +155 -0
  36. package/lib/src/server.js +336 -0
  37. package/lib/src/serving-sanitize.d.ts +104 -0
  38. package/lib/src/serving-sanitize.js +228 -0
  39. package/lib/src/serving.d.ts +61 -0
  40. package/lib/src/serving.js +78 -0
  41. package/lib/src/statement.d.ts +51 -0
  42. package/lib/src/statement.js +83 -0
  43. package/lib/src/storage-schema.d.ts +14 -0
  44. package/lib/src/storage-schema.js +34 -0
  45. package/lib/src/summarize.d.ts +70 -0
  46. package/lib/src/summarize.js +142 -0
  47. package/lib/src/threads.d.ts +109 -0
  48. package/lib/src/threads.js +301 -0
  49. package/lib/src/validation.d.ts +19 -0
  50. package/lib/src/validation.js +17 -0
  51. package/lib/src/workspaces.d.ts +68 -0
  52. package/lib/src/workspaces.js +246 -0
  53. package/lib/src/writer.d.ts +25 -0
  54. package/lib/src/writer.js +40 -0
  55. package/lib/tsconfig.tsbuildinfo +1 -0
  56. package/package.json +17 -13
@@ -0,0 +1,1015 @@
1
+ /**
2
+ * AppKit plugin that builds one or more Mastra `Agent` instances and
3
+ * mounts the `@mastra/express` server. Clients drive the conversation
4
+ * over the standard Mastra agent stream (`@mastra/client-js`'s
5
+ * `getAgent(id).stream()`), so there's no bespoke chat transport to
6
+ * keep in sync.
7
+ *
8
+ * - Agents: registered through `config.agents` at plugin creation
9
+ * ({@link MastraAgentDefinition}). Each entry's `tools` field accepts
10
+ * either a plain record or a `(plugins) => tools` callback that gets
11
+ * a typed sibling-plugin index ({@link MastraPlugins}). Omit
12
+ * `config.agents` to get a single built-in `default` analyst.
13
+ * - Model: each agent call resolves a `MastraModelConfig` via
14
+ * {@link buildModel} from `./model.js`. Per-agent `model` overrides
15
+ * (`AgentConfig["model"]` or a `modelId` string) flow through
16
+ * {@link buildAgents}.
17
+ * - Memory / storage: per-agent, built by {@link createMemoryBuilder}
18
+ * from `./memory.js`. Both auto-default to `true` when the
19
+ * `lakebase` plugin is registered (unless the caller passed
20
+ * `false` or a custom config). Storage namespaces per agent via
21
+ * {@link agentStorageSchemaName} per agent; the vector store is a single
22
+ * shared singleton across every agent.
23
+ * - Server: the Express subapp wiring lives in `./server.js`.
24
+ * - HTTP: AppKit mounts this plugin under `/api/mastra`. Alongside the
25
+ * Mastra agent routes, the plugin registers `/route/history`
26
+ * (load + clear a thread's messages), `/route/threads` (list the
27
+ * caller's conversations + delete one), `/models`, `/default-model`,
28
+ * `/suggestions`, `/route/feedback` (log a thumbs / comment to MLflow
29
+ * when feedback is enabled), and the generic `/embed/:type/:id`
30
+ * resolver for inline chart / data markers. Each is registered through
31
+ * AppKit's `route()` so it lands in the plugin's endpoint map, and
32
+ * every outbound call one makes runs through `execute()` (see
33
+ * `./defaults.js`). The stock `@mastra/express` surface is gated
34
+ * by `config.apiAccess` (default `"scoped"`): only agent inference,
35
+ * read-only agent metadata, the `/route/*` routes, and (when enabled)
36
+ * MCP are dispatched to Mastra; admin / mutating / bulk-export routes
37
+ * are refused with `403`. See {@link isMastraRequestAllowed}.
38
+ * - MCP: opt in with `config.mcp` to expose the agents (and optionally
39
+ * tools) as a Mastra `MCPServer`. It is registered on the `Mastra`
40
+ * instance via `mcpServers`, so `@mastra/express` serves the stock
41
+ * MCP transport routes (`/mcp/<serverId>/...`) under the mount. See
42
+ * `./mcp.js`.
43
+ *
44
+ * @module
45
+ */
46
+ import { ExecutionError, genie, getExecutionContext, lakebase, Plugin, toPlugin, } from "@databricks/appkit";
47
+ import { plugin } from "@dbx-tools/appkit";
48
+ import { serving as nodeServing } from "@dbx-tools/model";
49
+ import { async, error, log, string } from "@dbx-tools/shared-core";
50
+ import { feedback, routes, } from "@dbx-tools/shared-mastra";
51
+ import { display } from "@dbx-tools/shared-model";
52
+ import { Mastra } from "@mastra/core/mastra";
53
+ import express from "express";
54
+ import { buildAgents, FALLBACK_AGENT_ID } from "./agents.js";
55
+ import { fetchChart } from "./chart.js";
56
+ import { MASTRA_CONFIG_SCHEMA, resolveUserKey } from "./config.js";
57
+ import { chartFetchDefaults, feedbackWriteDefaults, genieSuggestionDefaults, modelCatalogueDefaults, statementDataDefaults, } from "./defaults.js";
58
+ import { collectSpaceSuggestions, resolveGenieSpaces } from "./genie.js";
59
+ import { historyRoute } from "./history.js";
60
+ import { buildMcpServer } from "./mcp.js";
61
+ import { createMemoryBuilder, createServicePrincipalPool, needsLakebase } from "./memory.js";
62
+ import { logFeedback, resolveFeedbackEnabled } from "./mlflow.js";
63
+ import { buildObservability } from "./observability.js";
64
+ import { attachRoutePatchMiddleware, createRequestContext, isMastraRequestAllowed, MastraServer, } from "./server.js";
65
+ import { resolveServingConfig } from "./serving.js";
66
+ import { fetchStatementData, STATEMENT_ROW_CAP } from "./statement.js";
67
+ import { threadsRoute } from "./threads.js";
68
+ import { invalidFields } from "./validation.js";
69
+ const GENIE_MANIFEST = plugin.data(genie).plugin.manifest;
70
+ const LAKEBASE_MANIFEST = plugin.data(lakebase).plugin.manifest;
71
+ /**
72
+ * Budget for draining the memory service-principal pool on shutdown. Well
73
+ * inside AppKit's 15s graceful-shutdown window, so a stuck connection can
74
+ * never be what holds the process open.
75
+ */
76
+ const POOL_DRAIN_TIMEOUT_MS = 5_000;
77
+ /** Ceiling on the `?timeoutMs=` long-poll budget a client may request. */
78
+ const MAX_EMBED_POLL_TIMEOUT_MS = 5 * 60_000;
79
+ /** Stable client message when the workspace's model catalogue can't be read. */
80
+ const MODEL_CATALOGUE_FAILED_MESSAGE = "Could not read the workspace's model catalogue";
81
+ /** Stable client message for a feedback body that fails schema validation. */
82
+ const INVALID_FEEDBACK_MESSAGE = "Invalid feedback request";
83
+ /** Cache namespace for statement result sets, matching the chart cache's form. */
84
+ const STATEMENT_CACHE_NAMESPACE = "mastra:statement";
85
+ /**
86
+ * AppKit plugin (registered name: `mastra`) that hosts Mastra agents
87
+ * with optional Lakebase-backed memory and AI SDK chat routes under
88
+ * the plugin mount (typically `/api/mastra`).
89
+ *
90
+ * @example Register the plugin
91
+ * ```ts
92
+ * import { createApp, lakebase } from "@databricks/appkit";
93
+ * import { createAgent, mastra } from "@dbx-tools/appkit-mastra";
94
+ *
95
+ * // `lakebase` first: registering it auto-enables Mastra storage + memory.
96
+ * const app = await createApp({
97
+ * plugins: [
98
+ * lakebase(),
99
+ * mastra({
100
+ * genieSpaces: { default: process.env.DATABRICKS_GENIE_SPACE_ID! },
101
+ * agents: createAgent({
102
+ * name: "analyst",
103
+ * instructions: "You answer questions about revenue and returns.",
104
+ * tools(plugins) {
105
+ * return { ...plugins.genie?.toolkit() };
106
+ * },
107
+ * }),
108
+ * }),
109
+ * ],
110
+ * });
111
+ * ```
112
+ *
113
+ * @example Read the agents back off the AppKit instance
114
+ * ```ts
115
+ * const agentIds = app.mastra.list();
116
+ * const endpoints = await app.mastra.listModels();
117
+ * ```
118
+ */
119
+ export class MastraPlugin extends Plugin {
120
+ // Annotated rather than left to `satisfies`: the config schema's type comes
121
+ // from `@types/json-schema`, which this package does not depend on, so an
122
+ // inferred manifest type cannot be named in the emitted declaration. The
123
+ // `PluginManifest<"mastra">` parameter still carries the literal name into
124
+ // `toPlugin()`'s factory type.
125
+ static manifest = {
126
+ name: "mastra",
127
+ displayName: "Mastra",
128
+ description: "Builds a Mastra Agent with user-scoped workspace auth (asUser) " +
129
+ "and optional Postgres-backed Mastra Memory via the `lakebase` plugin.",
130
+ stability: "beta",
131
+ resources: {
132
+ required: [],
133
+ optional: [
134
+ // Reuse the Genie space-id binding declared by AppKit's `genie`
135
+ // manifest so the resource-binding shape is identical to AppKit's and
136
+ // an existing `app.yaml` / `genie({ spaces })` wiring keeps working.
137
+ // The built-in Genie tools talk to Genie directly through
138
+ // `@dbx-tools/genie`, so only the binding is shared, not the plugin's
139
+ // tools.
140
+ ...GENIE_MANIFEST.resources.required,
141
+ ...LAKEBASE_MANIFEST.resources.required,
142
+ ],
143
+ },
144
+ config: { schema: MASTRA_CONFIG_SCHEMA },
145
+ };
146
+ /**
147
+ * Tighten resource requirements based on which features are enabled.
148
+ * AppKit calls this at registration time (config-aware) so disabled
149
+ * features don't surface their resource asks to the host app.
150
+ */
151
+ static getResourceRequirements(config) {
152
+ const resources = [];
153
+ const enabledManifests = [];
154
+ if (needsLakebase(config)) {
155
+ enabledManifests.push(LAKEBASE_MANIFEST);
156
+ }
157
+ for (const m of enabledManifests) {
158
+ for (const resource of m.resources.required) {
159
+ resources.push({ ...resource, required: true });
160
+ }
161
+ }
162
+ return resources;
163
+ }
164
+ logger = log.logger(this);
165
+ built = null;
166
+ mastra = null;
167
+ mastraApp = null;
168
+ mastraServer = null;
169
+ /**
170
+ * The optional MCP server exposing this plugin's agents / tools, or
171
+ * `null` when `config.mcp` is disabled (the default). Built in
172
+ * {@link buildAgentAndServer} and registered on the Mastra instance.
173
+ */
174
+ mcp = null;
175
+ /**
176
+ * Dedicated service-principal Lakebase pool backing Mastra memory /
177
+ * storage. Built once in {@link buildAgentAndServer} (outside any
178
+ * `asUser` scope, so it never inherits a request's OBO identity) and
179
+ * drained in {@link shutdown}. `null` until setup runs or when
180
+ * Lakebase isn't needed.
181
+ */
182
+ servicePrincipalPool = null;
183
+ async setup() {
184
+ // Wait until sibling plugins (e.g. `lakebase`) finish `setup()` so
185
+ // the lakebase pool is valid when storage/memory are enabled.
186
+ this.context?.onLifecycle("setup:complete", async () => {
187
+ this.applyLakebaseAutoDefaults();
188
+ await this.buildAgentAndServer();
189
+ });
190
+ }
191
+ /**
192
+ * When the `lakebase` plugin is registered, auto-enable `storage`
193
+ * and `memory` unless the caller opted out explicitly (`false` or a
194
+ * custom config object). Run after `setup:complete` so the lookup
195
+ * is reliable: any plugin that registers itself synchronously is
196
+ * already in the registry by the time this fires.
197
+ */
198
+ applyLakebaseAutoDefaults() {
199
+ const hasLakebase = plugin.instance(this.context, lakebase) !== undefined;
200
+ if (!hasLakebase)
201
+ return;
202
+ if (this.config.storage === undefined)
203
+ this.config.storage = true;
204
+ if (this.config.memory === undefined)
205
+ this.config.memory = true;
206
+ }
207
+ /**
208
+ * Drain the memory service-principal pool. Idempotent: the handle is
209
+ * cleared before the drain starts, so a second call is a no-op and a
210
+ * later `setup()` rebuilds the pool. Bounded by
211
+ * {@link POOL_DRAIN_TIMEOUT_MS} to stay well inside the 15s graceful
212
+ * shutdown budget.
213
+ */
214
+ async shutdown() {
215
+ const pool = this.servicePrincipalPool;
216
+ if (!pool)
217
+ return;
218
+ this.servicePrincipalPool = null;
219
+ this.logger.info("closing memory SP pool");
220
+ // The budget timer is cancelled on the way out so a drain that finished
221
+ // early cannot hold the event loop open for the rest of the window.
222
+ const budget = new AbortController();
223
+ try {
224
+ await Promise.race([pool.end(), async.sleep(POOL_DRAIN_TIMEOUT_MS, budget.signal)]);
225
+ }
226
+ catch (err) {
227
+ this.logger.error("error closing memory SP pool", {
228
+ error: error.errorMessage(err),
229
+ });
230
+ }
231
+ finally {
232
+ budget.abort();
233
+ }
234
+ }
235
+ /**
236
+ * Abort in-flight work. AppKit's graceful shutdown calls this hook
237
+ * synchronously and never awaits {@link shutdown}, so the pool drain is
238
+ * started here too; it cannot be awaited from a `void` hook, and
239
+ * {@link shutdown} is idempotent so the duplicate call is free.
240
+ */
241
+ abortActiveOperations() {
242
+ super.abortActiveOperations();
243
+ void this.shutdown();
244
+ }
245
+ exports() {
246
+ return {
247
+ /**
248
+ * Ids of every registered agent in registration order. Matches
249
+ * AppKit `agents.list()` so callers can iterate the registry the
250
+ * same way under both plugins.
251
+ */
252
+ list: () => Object.keys(this.built?.agents ?? {}),
253
+ /**
254
+ * Look up a registered agent by id. Returns `null` (not
255
+ * undefined) when unknown so call sites can early-return without
256
+ * a separate `in` check.
257
+ */
258
+ get: (id) => this.built?.agents[id] ?? null,
259
+ /**
260
+ * The agent the client converses with when it doesn't name one.
261
+ * Resolves to `config.defaultAgent`, the first registered id, or
262
+ * the built-in `default` fallback.
263
+ */
264
+ getDefault: () => (this.built && this.built.agents[this.built.defaultAgentId]) ?? null,
265
+ /** Underlying Mastra instance for advanced use (custom routes etc.). */
266
+ getMastra: () => this.mastra,
267
+ /**
268
+ * Build the `RequestContext` an agent turn driven from OUTSIDE this
269
+ * plugin's routes needs (a Teams activity on another plugin's endpoint, a
270
+ * scheduled job).
271
+ *
272
+ * Mastra's user-scoped tools - `ask_genie` most visibly - read the AppKit
273
+ * user off the request context, which only the HTTP middleware stamps.
274
+ * Calling `agent.generate` with a raw prompt therefore answers "the data
275
+ * source is unreachable" where the chat routes answer with real data.
276
+ * Passing this as `requestContext` closes that gap, so an out-of-band turn
277
+ * has exactly the capabilities a chat turn has.
278
+ *
279
+ * Must be called INSIDE an `asUser(req)` scope to inherit the caller's
280
+ * OBO identity; outside one it resolves to the service principal.
281
+ */
282
+ createRequestContext: (options = {}) => createRequestContext(options),
283
+ /**
284
+ * MCP endpoint info when `config.mcp` is enabled, else `null`.
285
+ * Streamable HTTP is `http`; the SSE pair is the legacy transport.
286
+ *
287
+ * Each path is given twice: mount-relative (`httpPath`, `ssePath`,
288
+ * `messagePath`) for anything that already knows where the plugin is
289
+ * mounted, and absolute (`http`, `sse`, `messages`) for MCP clients,
290
+ * which take a single URL and cannot compose one. The absolute form is
291
+ * built from {@link basePath}, so it honors a `config.name` override but
292
+ * still assumes AppKit's default `/api/<name>` mount.
293
+ */
294
+ getMcp: () => this.mcp
295
+ ? {
296
+ serverId: this.mcp.serverId,
297
+ httpPath: this.mcp.httpPath,
298
+ ssePath: this.mcp.ssePath,
299
+ messagePath: this.mcp.messagePath,
300
+ http: `${this.basePath}${this.mcp.httpPath}`,
301
+ sse: `${this.basePath}${this.mcp.ssePath}`,
302
+ messages: `${this.basePath}${this.mcp.messagePath}`,
303
+ }
304
+ : null,
305
+ /** Express subapp Mastra is mounted on; mostly for tests. */
306
+ getMastraServer: () => this.mastraServer,
307
+ /**
308
+ * Fetch the workspace's Model Serving endpoints (cached). Same
309
+ * payload the `GET /models` route returns; surfaced here so
310
+ * other plugins / scripts can introspect the catalogue without
311
+ * an HTTP round-trip. AppKit wraps this with `asUser(req)` for
312
+ * OBO scoping automatically. Throws when the listing fails, since
313
+ * there is no status code to hand back on this surface.
314
+ */
315
+ listModels: () => this.listModels(),
316
+ /**
317
+ * Force-evict cached endpoint listings via AppKit's
318
+ * `CacheManager`. Useful in tests or right after an admin
319
+ * deploys a new endpoint and doesn't want to wait for the TTL.
320
+ * Returns the underlying `CacheManager.delete`/`clear` promise.
321
+ */
322
+ clearModelsCache: (host) => nodeServing.clearServingEndpointsCache(host),
323
+ };
324
+ }
325
+ /**
326
+ * Absolute mount this plugin's routes answer on. AppKit mounts every plugin
327
+ * at `/api/<plugin.name>` and `this.name` honors a `config.name` override,
328
+ * so the per-route segments (`routes.MASTRA_ROUTES`) hang off this.
329
+ */
330
+ get basePath() {
331
+ return `/api/${this.name}`;
332
+ }
333
+ clientConfig() {
334
+ // Publishing `basePath` is enough for the client to stay correct under a
335
+ // custom mount id: the per-route segments are fixed
336
+ // (`routes.MASTRA_ROUTES`) and the client (`MastraPluginClient`) derives
337
+ // every endpoint from `basePath`.
338
+ // Return widens to `Record<string, unknown>` to satisfy the
339
+ // base-class signature; consumers read it through the typed
340
+ // `MastraClientConfig` shape via `usePluginClientConfig<...>(...)`.
341
+ const config = {
342
+ basePath: this.basePath,
343
+ defaultAgent: this.built?.defaultAgentId ?? FALLBACK_AGENT_ID,
344
+ agents: Object.keys(this.built?.agents ?? {}),
345
+ feedbackEnabled: this.feedbackEnabled(),
346
+ };
347
+ return config;
348
+ }
349
+ /**
350
+ * Whether user feedback can be logged to MLflow. Delegates to
351
+ * {@link resolveFeedbackEnabled} so the client-config flag and the
352
+ * feedback route share the same gate as the server's trace-id header.
353
+ */
354
+ feedbackEnabled() {
355
+ return resolveFeedbackEnabled(this.config.feedback);
356
+ }
357
+ injectRoutes(router) {
358
+ // Expose the MCP transport at the clean `/mcp` (plus the legacy
359
+ // `/sse` + `/messages`) under the plugin mount. `@mastra/express`
360
+ // mounts MCP under `/mcp/<serverId>/<transport>`, and the serverId
361
+ // defaults to the plugin name, so the raw route reads
362
+ // `/api/mastra/mcp/mastra/mcp` (doubled segment). This runs before
363
+ // the catch-all and rewrites the alias to the underlying route, so
364
+ // the public path is just `/api/<plugin>/mcp`; the query string
365
+ // (e.g. the SSE `sessionId`) is preserved.
366
+ //
367
+ // Middleware rather than `this.route`: it rewrites the URL for routes
368
+ // `@mastra/express` owns further down the chain instead of answering the
369
+ // request, which `RouteConfig` has no shape for.
370
+ router.use((req, _res, next) => {
371
+ const target = this.mcpRouteAlias(req.path);
372
+ if (target) {
373
+ const q = req.url.indexOf("?");
374
+ req.url = q >= 0 ? target + req.url.slice(q) : target;
375
+ }
376
+ next();
377
+ });
378
+ // `GET /models` exposes the cached endpoint list so clients can
379
+ // populate model pickers, validate `?model=` choices, etc. Must
380
+ // be registered before the catch-all that forwards everything to
381
+ // the Mastra subapp.
382
+ this.route(router, {
383
+ name: "models",
384
+ method: "get",
385
+ path: routes.MASTRA_ROUTES.models,
386
+ handler: async (req, res) => {
387
+ const result = await this.asUser(req).listModelsResult();
388
+ if (!result.ok) {
389
+ this.sendFailure(res, result, "models", MODEL_CATALOGUE_FAILED_MESSAGE);
390
+ return;
391
+ }
392
+ res.json({ endpoints: result.data });
393
+ },
394
+ });
395
+ // `GET /default-model` (and `/default-model/:agentId`) reports the static
396
+ // serving-endpoint an agent falls back to when the client pins no model,
397
+ // so the picker can label its default option with the model's humanized
398
+ // name. Agent-scoped via the optional `/:agentId` suffix (URL symmetry
399
+ // with the history / threads / suggestions routes), defaulting to the
400
+ // default agent. `model` / `displayName` are null when the agent has no
401
+ // static default (a dynamic, call-time model); an unknown agent id is a
402
+ // 404, matching the history / threads routes. Reads only in-memory build
403
+ // state, so it needs no OBO scoping. Registered before the catch-all,
404
+ // same as `/models`.
405
+ const handleDefaultModel = async (req, res) => {
406
+ const requested = string.firstNonEmpty(req.params.agentId);
407
+ if (requested !== null && !this.built?.agents[requested]) {
408
+ res.status(404).json({ error: this.unknownAgentMessage(requested) });
409
+ return;
410
+ }
411
+ const agentId = requested ?? this.built?.defaultAgentId ?? FALLBACK_AGENT_ID;
412
+ const raw = this.built?.defaultModels[agentId];
413
+ // `"<dynamic>"` (a call-time function) has no fixed id to advertise.
414
+ const model = raw && raw !== "<dynamic>" ? raw : null;
415
+ // Return the humanized label too, so the picker shows a friendly name on
416
+ // load without waiting on the `/models` catalogue (no raw-id flash).
417
+ res.json({
418
+ agentId,
419
+ model,
420
+ displayName: model ? display.toModelDisplayName(model) : null,
421
+ });
422
+ };
423
+ this.route(router, {
424
+ name: "defaultModel",
425
+ method: "get",
426
+ path: routes.MASTRA_ROUTES.defaultModel,
427
+ handler: handleDefaultModel,
428
+ });
429
+ this.route(router, {
430
+ name: "defaultModelByAgent",
431
+ method: "get",
432
+ path: `${routes.MASTRA_ROUTES.defaultModel}/:agentId`,
433
+ handler: handleDefaultModel,
434
+ });
435
+ // `GET /embed/:type/:id` is the single resolver for every embed
436
+ // marker the agent emits in prose (`[chart:<id>]`,
437
+ // `[data:<id>]`, ...). `:type` selects a resolver from the
438
+ // registry below; `:id` is that resolver's lookup key. The
439
+ // grammar (see `marker.ts`) is type-agnostic on purpose - new
440
+ // embed kinds are added by registering a resolver here, with no
441
+ // client or grammar change.
442
+ //
443
+ // Status codes:
444
+ // - 200 with the resolver's JSON body when the id resolves.
445
+ // - 404 when `:type` isn't registered (unsupported embed
446
+ // type) OR a registered resolver can't find `:id` (unknown
447
+ // / expired - e.g. a chart past its 1h TTL or a fabricated
448
+ // id the model never minted).
449
+ // - 400 when `:id` is empty.
450
+ //
451
+ // Per-type query knobs and behavior:
452
+ // - `chart`: long-polls the chart cache until the entry
453
+ // settles (`result` / `error`) or the budget elapses (then
454
+ // returns the still-processing entry to poll again).
455
+ // `?timeoutMs=<n>` (default 60s, capped 5min) tunes it.
456
+ // - `data`: one OBO-scoped Statement Execution fetch.
457
+ // `?limit=<n>` caps rows (clamped to STATEMENT_ROW_CAP).
458
+ //
459
+ // Built once (this handler is registered once) and keyed by the
460
+ // raw `:type` token. Each resolver gets the request (for query
461
+ // parsing + OBO scoping) and an `AbortSignal` bridged off the
462
+ // connection `close` event so a long-poll unblocks the instant
463
+ // the client disconnects. `undefined` from a resolver maps to a
464
+ // clean 404; thrown errors bubble through `next(err)`.
465
+ const embedResolvers = {
466
+ chart: (req, id, signal) => {
467
+ const timeoutMs = parseTimeoutMs(req.query.timeoutMs);
468
+ return this.asUser(req).fetchChartEntry(id, {
469
+ ...(timeoutMs !== undefined ? { timeoutMs } : {}),
470
+ signal,
471
+ });
472
+ },
473
+ data: (req, id, signal) => {
474
+ const limit = parseStatementLimit(req.query.limit);
475
+ return this.asUser(req).fetchStatement(id, {
476
+ ...(limit !== undefined ? { limit } : {}),
477
+ signal,
478
+ });
479
+ },
480
+ };
481
+ this.route(router, {
482
+ name: "embed",
483
+ method: "get",
484
+ path: `${routes.MASTRA_ROUTES.embed}/:type/:id`,
485
+ handler: async (req, res) => {
486
+ const type = string.firstNonEmpty(req.params.type) ?? "";
487
+ const id = string.firstNonEmpty(req.params.id);
488
+ const resolve = embedResolvers[type];
489
+ if (!resolve) {
490
+ res.status(404).json({ error: `unsupported embed type: ${type}` });
491
+ return;
492
+ }
493
+ if (!id) {
494
+ res.status(400).json({ error: "id is required" });
495
+ return;
496
+ }
497
+ // Express's `req` predates `AbortSignal`; bridge the `close`
498
+ // event onto an `AbortController` so a closed connection
499
+ // unblocks any long-poll immediately and frees the request
500
+ // thread. The listener is GC'd with the request on normal
501
+ // completion.
502
+ const controller = new AbortController();
503
+ req.on("close", () => controller.abort());
504
+ const result = await resolve(req, id, controller.signal);
505
+ if (controller.signal.aborted)
506
+ return;
507
+ if (!result.ok) {
508
+ this.sendFailure(res, result, `embed:${type}`, `Could not resolve the ${type} embed`);
509
+ return;
510
+ }
511
+ if (result.data === undefined) {
512
+ res.status(404).json({ error: `${type} not found` });
513
+ return;
514
+ }
515
+ res.json(result.data);
516
+ },
517
+ });
518
+ // `GET /suggestions` (and `/suggestions/:agentId`) returns the
519
+ // curated starter questions for the agent's Genie space(s) - the
520
+ // author-configured `sample_questions`, surfaced as one-tap
521
+ // prompts on the chat empty state. Returns `{ questions: [] }`
522
+ // when no Genie space is wired so the client renders a bare
523
+ // empty state (no built-in example prompts). The `:agentId`
524
+ // segment is accepted for URL symmetry with the chat / history
525
+ // routes; Genie spaces are resolved per-plugin, not per-agent,
526
+ // so it doesn't change the result. OBO-scoped like the other
527
+ // data routes so the space lookup runs as the calling user.
528
+ const handleSuggestions = async (req, res) => {
529
+ const controller = new AbortController();
530
+ req.on("close", () => controller.abort());
531
+ const result = await this.asUser(req).fetchSuggestions(controller.signal);
532
+ if (controller.signal.aborted)
533
+ return;
534
+ if (!result.ok) {
535
+ // Suggestions are a non-critical enhancement; a lookup failure should
536
+ // leave the chat usable with a bare empty state rather than surfacing
537
+ // the upstream status. Log and degrade.
538
+ this.logger.warn("suggestions:error", {
539
+ status: result.status,
540
+ error: result.message,
541
+ });
542
+ res.json({ questions: [] });
543
+ return;
544
+ }
545
+ res.json({ questions: result.data });
546
+ };
547
+ this.route(router, {
548
+ name: "suggestions",
549
+ method: "get",
550
+ path: routes.MASTRA_ROUTES.suggestions,
551
+ handler: handleSuggestions,
552
+ });
553
+ this.route(router, {
554
+ name: "suggestionsByAgent",
555
+ method: "get",
556
+ path: `${routes.MASTRA_ROUTES.suggestions}/:agentId`,
557
+ handler: handleSuggestions,
558
+ });
559
+ // `POST /route/feedback` logs a thumbs / comment assessment against
560
+ // a turn's MLflow trace (the `traceId` the client captured from the
561
+ // stream response's trace-id header). Registered on the AppKit
562
+ // router (like `/models`) rather than the Mastra subapp so it runs
563
+ // under the same OBO scope - the feedback is attributed to the
564
+ // signed-in user. Returns 404 when feedback is disabled so the
565
+ // client treats the capability as absent; 400 on a malformed body.
566
+ // A recorded assessment yields `{ ok: true }`; a soft failure (most
567
+ // often the trace hasn't finished exporting to MLflow yet) yields
568
+ // `{ ok: false }` without a 5xx so the UI can prompt a retry.
569
+ this.route(router, {
570
+ name: "feedback",
571
+ method: "post",
572
+ path: routes.MASTRA_ROUTES.feedback,
573
+ handler: async (req, res) => {
574
+ if (!this.feedbackEnabled()) {
575
+ res.status(404).json({ ok: false });
576
+ return;
577
+ }
578
+ const parsed = feedback.MastraFeedbackRequestSchema.safeParse(req.body);
579
+ if (!parsed.success) {
580
+ // A Zod issue string quotes the received body back, so only the
581
+ // offending field names travel; the full issue list is logged.
582
+ this.logger.warn("feedback:invalid", { error: parsed.error.message });
583
+ res.status(400).json({
584
+ ok: false,
585
+ error: INVALID_FEEDBACK_MESSAGE,
586
+ fields: invalidFields(parsed.error),
587
+ });
588
+ return;
589
+ }
590
+ const result = await this.asUser(req).logFeedback(parsed.data);
591
+ if (!result.ok) {
592
+ this.sendFailure(res, result, "feedback", "Feedback could not be recorded");
593
+ return;
594
+ }
595
+ const assessmentId = result.data;
596
+ res.json({
597
+ ok: assessmentId !== undefined,
598
+ ...(assessmentId ? { assessmentId } : {}),
599
+ });
600
+ },
601
+ });
602
+ // Middleware rather than `this.route`: this is the catch-all that hands
603
+ // every remaining method / path pair to the Mastra sub-app, so it has no
604
+ // single method or path to register under.
605
+ router.use((req, res, next) => {
606
+ if (!this.mastraApp)
607
+ return res.status(503).end();
608
+ // Gate the stock Mastra surface before dispatch. In the default
609
+ // "scoped" mode only agent inference, read-only agent metadata, this
610
+ // plugin's own `/route/*` routes, and (when enabled) MCP reach Mastra;
611
+ // admin / mutating / bulk-export routes are refused here. `req.path`
612
+ // is mount-relative under the plugin mount. See `server.ts`.
613
+ if (!isMastraRequestAllowed(req.method, req.path, {
614
+ access: this.config.apiAccess ?? "scoped",
615
+ // Reflect the *resolved* MCP state, not raw `config.mcp`: MCP is
616
+ // on by default (`config.mcp` undefined), so gate on whether the
617
+ // server was actually built and mounted.
618
+ mcpEnabled: this.mcp !== null,
619
+ })) {
620
+ res.status(403).json({ error: "Endpoint not exposed to the client (apiAccess=scoped)" });
621
+ return;
622
+ }
623
+ // Dispatch through a real method, NOT the `mastraApp` property. The
624
+ // AppKit `asUser(req)` proxy wraps function-valued props with
625
+ // `value.bind(target)`. `mastraApp` is an express app whose `.bind` is
626
+ // the HTTP BIND route registrar (express defines a method per HTTP verb,
627
+ // and BIND is one), not `Function.prototype.bind` - so binding it through
628
+ // the proxy registers a bogus route and crashes `pathToRegexp`
629
+ // ("path must be a string ..."). This only manifests in production where
630
+ // an OBO token makes `userScopedSelf` return the proxy. `dispatchMastra`
631
+ // is a plain method (its `.bind` is the normal one) and invokes
632
+ // `this.mastraApp` off the real target, keeping the OBO scope active.
633
+ return this.asUser(req).dispatchMastra(req, res, next);
634
+ });
635
+ }
636
+ /**
637
+ * Map a failed {@link ExecutionResult} onto the response: the status the
638
+ * execution pipeline resolved, paired with `clientMessage` rather than the
639
+ * result's own text, which can carry upstream detail. The full message is
640
+ * logged under `<operation>:error`.
641
+ */
642
+ sendFailure(res, result, operation, clientMessage) {
643
+ this.logger.warn(`${operation}:error`, {
644
+ status: result.status,
645
+ error: result.message,
646
+ });
647
+ if (res.headersSent)
648
+ return;
649
+ res.status(result.status).json({ error: clientMessage });
650
+ }
651
+ /** 404 body for a request naming an agent that isn't registered. */
652
+ unknownAgentMessage(agentId) {
653
+ const registered = Object.keys(this.built?.agents ?? {});
654
+ return `Unknown agent "${agentId}". Registered agents: ${registered.join(", ") || "none"}`;
655
+ }
656
+ /**
657
+ * Invoke the Mastra express sub-app. Exists as a method (instead of reading
658
+ * `this.mastraApp` through the `asUser(req)` proxy at the call site) so the
659
+ * proxy binds this plain method - whose `.bind` is `Function.prototype.bind`
660
+ * - rather than the express app, whose `.bind` is the HTTP BIND route
661
+ * registrar (see the note in `injectRoutes`). Runs inside the user scope so
662
+ * `getExecutionContext()` returns the OBO client for the agent/model
663
+ * resolvers.
664
+ */
665
+ dispatchMastra(req, res, next) {
666
+ this.mastraApp(req, res, next);
667
+ }
668
+ /**
669
+ * Map a clean, mount-relative MCP alias path to the underlying
670
+ * `@mastra/express` route. Returns `null` when MCP is off or the path
671
+ * isn't an alias. Collapses the stock `/mcp/<serverId>/<transport>`
672
+ * layout (serverId defaults to the plugin name) down to `/mcp`,
673
+ * `/sse`, and `/messages`.
674
+ */
675
+ mcpRouteAlias(path) {
676
+ if (!this.mcp)
677
+ return null;
678
+ const id = this.mcp.serverId;
679
+ if (path === "/mcp")
680
+ return `/mcp/${id}/mcp`;
681
+ if (path === "/sse")
682
+ return `/mcp/${id}/sse`;
683
+ if (path === "/messages")
684
+ return `/mcp/${id}/messages`;
685
+ return null;
686
+ }
687
+ /**
688
+ * Implementation backing the `/suggestions` route. Runs inside the
689
+ * AppKit user-context proxy so `getExecutionContext()` returns the
690
+ * OBO-scoped client. Resolves the plugin's Genie spaces and merges
691
+ * their curated `sample_questions` (see {@link collectSpaceSuggestions}).
692
+ * Returns `[]` when no Genie space is configured so the client
693
+ * shows a bare empty state instead of built-in example prompts.
694
+ */
695
+ async fetchSuggestions(signal) {
696
+ const spaces = resolveGenieSpaces(this.config, this.context);
697
+ if (Object.keys(spaces).length === 0)
698
+ return { ok: true, data: [] };
699
+ const client = getExecutionContext().client;
700
+ return this.execute((executeSignal) => {
701
+ const combined = async.combineAbortSignals(signal, executeSignal);
702
+ return collectSpaceSuggestions({
703
+ spaces,
704
+ client,
705
+ ...(combined ? { signal: combined } : {}),
706
+ });
707
+ }, genieSuggestionDefaults);
708
+ }
709
+ /**
710
+ * Implementation backing the `/route/feedback` route. Runs inside the
711
+ * AppKit user-context proxy so `getExecutionContext()` returns the
712
+ * OBO-scoped client and the assessment is attributed to the signed-in
713
+ * user (their email / id as the assessment source). Returns the
714
+ * created assessment id on success, or `undefined` on a soft failure
715
+ * (see {@link logFeedback} in `./mlflow.js`).
716
+ */
717
+ async logFeedback(feedback) {
718
+ const ctx = getExecutionContext();
719
+ const sourceId = "userEmail" in ctx && ctx.userEmail
720
+ ? ctx.userEmail
721
+ : "userId" in ctx
722
+ ? ctx.userId
723
+ : ctx.serviceUserId;
724
+ return this.execute(() => logFeedback(ctx.client, {
725
+ ...feedback,
726
+ ...(sourceId ? { sourceId } : {}),
727
+ }), feedbackWriteDefaults);
728
+ }
729
+ /**
730
+ * Implementation backing the `data` embed resolver
731
+ * (`GET /embed/data/:id`). Runs inside the AppKit user-context proxy so
732
+ * `getExecutionContext()` returns the OBO-scoped workspace
733
+ * client, then reuses the same `fetchStatementData` pipeline
734
+ * the `get_statement` tool runs so the LLM and the UI see the
735
+ * exact same shape for the same statement.
736
+ *
737
+ * Resolves to `undefined` data for upstream 404s so the route can map them
738
+ * to a clean HTTP 404; any other failure comes back as a non-`ok` result
739
+ * carrying the status to answer with.
740
+ */
741
+ async fetchStatement(statementId, options = {}) {
742
+ const client = getExecutionContext().client;
743
+ const limit = Math.min(options.limit ?? STATEMENT_ROW_CAP, STATEMENT_ROW_CAP);
744
+ return this.execute(async (executeSignal) => {
745
+ const combined = async.combineAbortSignals(options.signal, executeSignal);
746
+ try {
747
+ const data = await fetchStatementData(client, statementId, {
748
+ limit,
749
+ ...(combined ? { signal: combined } : {}),
750
+ });
751
+ return {
752
+ columns: data.columns,
753
+ rows: data.rows,
754
+ rowCount: data.rowCount,
755
+ truncated: data.rows.length < data.rowCount,
756
+ };
757
+ }
758
+ catch (err) {
759
+ // The Databricks SDK throws on 404; surface as `undefined`
760
+ // so the route maps to a clean HTTP 404 instead of a 500.
761
+ if (error.errorContext(err).notAccessible)
762
+ return undefined;
763
+ throw err;
764
+ }
765
+ }, {
766
+ default: {
767
+ ...statementDataDefaults.default,
768
+ // Namespaced so the key can't collide with another cache sharing the
769
+ // manager, and keyed on `limit` because the slice is part of the
770
+ // payload.
771
+ cache: {
772
+ ...statementDataDefaults.default.cache,
773
+ cacheKey: [STATEMENT_CACHE_NAMESPACE, statementId, limit],
774
+ },
775
+ },
776
+ });
777
+ }
778
+ /**
779
+ * Implementation backing the `chart` embed resolver
780
+ * (`GET /embed/chart/:id`). Runs inside the AppKit user-context proxy so the
781
+ * lookup is namespaced to the calling identity: a chart minted for another
782
+ * user is indistinguishable from an unknown id, and the route answers 404.
783
+ */
784
+ async fetchChartEntry(chartId, options) {
785
+ const userKey = resolveUserKey();
786
+ return this.execute((executeSignal) => fetchChart(chartId, {
787
+ userKey,
788
+ ...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
789
+ signal: async.combineAbortSignals(options.signal, executeSignal) ?? options.signal,
790
+ }), chartFetchDefaults);
791
+ }
792
+ /**
793
+ * Implementation backing the `/models` route. Runs inside the AppKit
794
+ * user-context proxy so `getExecutionContext()` returns the OBO-scoped
795
+ * client and the catalogue reflects what the caller can invoke.
796
+ */
797
+ async listModelsResult() {
798
+ return this.execute(async () => {
799
+ const client = getExecutionContext().client;
800
+ const host = (await client.config.getHost()).toString();
801
+ const serving = resolveServingConfig(this.config);
802
+ return nodeServing.listServingEndpoints(client, host, { ttlMs: serving.ttlMs });
803
+ }, modelCatalogueDefaults);
804
+ }
805
+ /**
806
+ * Implementation backing the `listModels` export. The programmatic surface
807
+ * carries no status code, so a failed execution becomes a throw with a
808
+ * stable message; the routes branch on the {@link ExecutionResult} instead.
809
+ */
810
+ async listModels() {
811
+ const result = await this.listModelsResult();
812
+ if (result.ok)
813
+ return result.data;
814
+ this.logger.warn("listModels:error", {
815
+ status: result.status,
816
+ error: result.message,
817
+ });
818
+ throw new ExecutionError(MODEL_CATALOGUE_FAILED_MESSAGE);
819
+ }
820
+ async buildAgentAndServer() {
821
+ // Per-agent memory factory. When any storage / memory setting needs
822
+ // Postgres, stand up a dedicated service-principal pool first so
823
+ // memory acts as the app SP (owner of the `mastra_*` schemas),
824
+ // never the per-request OBO identity the chat turn runs under.
825
+ // `getPgConfig()` is read here, outside any `asUser` scope, so it
826
+ // returns the SP connection target + token refresh plus any
827
+ // `lakebase({ pool })` overrides; `require` turns a missing
828
+ // sibling into a clear wiring error. The builder caches the shared
829
+ // `PgVector` singleton so registering N agents stays cheap. See
830
+ // `./memory.js`.
831
+ if (needsLakebase(this.config)) {
832
+ const spPgConfig = plugin
833
+ .require(this.context, lakebase, this.config)
834
+ .exports()
835
+ .getPgConfig();
836
+ this.servicePrincipalPool = await createServicePrincipalPool(spPgConfig);
837
+ }
838
+ const memoryBuilder = this.servicePrincipalPool
839
+ ? createMemoryBuilder(this.config, this.servicePrincipalPool)
840
+ : undefined;
841
+ // Build every agent declared in `config.agents` (or the built-in
842
+ // fallback when none are declared). Each agent's `model` resolves
843
+ // workspace URL + bearer at call time so concurrent requests get
844
+ // distinct user identities; the `asUser(req)` scope around
845
+ // `handleChat` is what lets `getExecutionContext()` return the
846
+ // right user inside the resolver.
847
+ this.built = await buildAgents({
848
+ config: this.config,
849
+ context: this.context,
850
+ memoryBuilder,
851
+ log: this.logger,
852
+ });
853
+ // `mastra.server.apiRoutes` is only honored by Mastra's standalone
854
+ // dev server. Since we're hosting Mastra inside our own Express
855
+ // subapp via `@mastra/express`, custom routes must be passed to
856
+ // the `MastraServer` constructor directly.
857
+ //
858
+ // `storage` here is *Mastra-instance-level* and persists workflow
859
+ // snapshots (where suspended `requireApproval` tool calls live).
860
+ // It's separate from each agent's `Memory.storage`, which only
861
+ // covers thread / message history. Without it,
862
+ // `agent.resumeStream()` errors with "could not find a suspended
863
+ // run" and the approval UI hangs after the user clicks Approve.
864
+ const instanceStorage = memoryBuilder?.instanceStorage();
865
+ // Wire Mastra's tracer into AppKit's global OTel pipeline via
866
+ // `@mastra/otel-bridge`. Mastra spans become native OTel spans on
867
+ // whatever tracer provider `TelemetryManager` registered during
868
+ // `createApp`, so the OTLP endpoint / headers / sampling are
869
+ // env-driven and shared with every other AppKit plugin.
870
+ const observability = await buildObservability({
871
+ serviceName: this.name,
872
+ enabled: this.config.observability,
873
+ });
874
+ // Optional MCP exposure: build a Mastra MCP server from the
875
+ // registered agents (and, opt-in, the ambient tools) and register
876
+ // it on the Mastra instance. `@mastra/express` serves the stock MCP
877
+ // transport routes (`/mcp/<serverId>/...`) off `mcpServers`, so the
878
+ // catch-all dispatch below already routes MCP requests under OBO -
879
+ // no bespoke route needed. See `./mcp.js`.
880
+ this.mcp = buildMcpServer({
881
+ config: this.config,
882
+ pluginName: this.name,
883
+ displayName: MastraPlugin.manifest.displayName,
884
+ agents: this.built.agents,
885
+ ambientTools: this.built.ambientTools,
886
+ });
887
+ this.mastra = new Mastra({
888
+ agents: this.built.agents,
889
+ ...(instanceStorage ? { storage: instanceStorage } : {}),
890
+ ...(observability ? { observability } : {}),
891
+ ...(this.mcp ? { mcpServers: { [this.mcp.serverId]: this.mcp.server } } : {}),
892
+ });
893
+ this.mastraApp = express();
894
+ attachRoutePatchMiddleware(this.mastraApp);
895
+ this.mastraServer = new MastraServer(this.config, {
896
+ app: this.mastraApp,
897
+ mastra: this.mastra,
898
+ prefix: "",
899
+ customApiRoutes: [
900
+ // `historyRoute` registers both GET (load) and DELETE
901
+ // (clear) on the same path, so it returns an array we
902
+ // splice in.
903
+ ...historyRoute({
904
+ path: routes.MASTRA_ROUTES.history,
905
+ agent: this.built.defaultAgentId,
906
+ }),
907
+ // Assert the `:agentId` template type: the per-package build's
908
+ // NodeNext resolution widens the imported `routes.MASTRA_ROUTES.history`
909
+ // to `string` (the source/bundler typecheck keeps it a literal),
910
+ // which would otherwise drop this out of the dynamic-agent
911
+ // overload and demand a fixed `agent`.
912
+ ...historyRoute({
913
+ path: `${routes.MASTRA_ROUTES.history}/:agentId`,
914
+ }),
915
+ // `threadsRoute` registers GET (list the caller's conversation
916
+ // threads) and DELETE (remove the targeted thread) on the same
917
+ // path; both the default-agent and dynamic-agent mounts are
918
+ // spliced in, mirroring the history routes above.
919
+ ...threadsRoute({
920
+ path: routes.MASTRA_ROUTES.threads,
921
+ agent: this.built.defaultAgentId,
922
+ }),
923
+ ...threadsRoute({
924
+ path: `${routes.MASTRA_ROUTES.threads}/:agentId`,
925
+ }),
926
+ ],
927
+ });
928
+ await this.mastraServer.init();
929
+ this.logger.info("ready", {
930
+ agents: Object.keys(this.built.agents),
931
+ defaultAgent: this.built.defaultAgentId,
932
+ apiAccess: this.config.apiAccess ?? "scoped",
933
+ mcp: this.mcp ? `${this.basePath}${this.mcp.httpPath}` : "off",
934
+ lakebase: memoryBuilder !== undefined,
935
+ feedback: this.feedbackEnabled(),
936
+ observability: observability !== undefined ? "mlflow" : "off",
937
+ });
938
+ }
939
+ }
940
+ /**
941
+ * Parse the optional `?timeoutMs=<n>` query parameter from a
942
+ * `GET /embed/chart/:id` request. Accepts a positive integer up
943
+ * to {@link MAX_EMBED_POLL_TIMEOUT_MS} (clamped) and rejects everything else
944
+ * as `undefined` so {@link fetchChart} falls back to its default.
945
+ * Express produces `string | string[] | undefined`; we normalize
946
+ * to the first scalar before parsing.
947
+ */
948
+ function parseTimeoutMs(raw) {
949
+ const v = Array.isArray(raw) ? raw[0] : raw;
950
+ if (typeof v !== "string")
951
+ return undefined;
952
+ const n = Number(v);
953
+ if (!Number.isFinite(n) || n <= 0)
954
+ return undefined;
955
+ return Math.min(Math.floor(n), MAX_EMBED_POLL_TIMEOUT_MS);
956
+ }
957
+ /**
958
+ * Parse the optional `?limit=<n>` query parameter from a
959
+ * `GET /embed/data/:id` request. Accepts a non-negative
960
+ * integer and lets the route clamp to `STATEMENT_ROW_CAP`;
961
+ * rejects anything else as `undefined` so the route falls back
962
+ * to the server-side cap.
963
+ */
964
+ function parseStatementLimit(raw) {
965
+ const v = Array.isArray(raw) ? raw[0] : raw;
966
+ if (typeof v !== "string")
967
+ return undefined;
968
+ const n = Number(v);
969
+ if (!Number.isFinite(n) || n < 0)
970
+ return undefined;
971
+ return Math.floor(n);
972
+ }
973
+ /**
974
+ * Register the Mastra plugin on an AppKit app. Mounts the agents, the
975
+ * `/route/*` chat surface, and (unless disabled) the MCP transport under
976
+ * `/api/mastra`.
977
+ *
978
+ * @example Minimal app
979
+ * ```ts
980
+ * import { createApp } from "@databricks/appkit";
981
+ * import { mastra } from "@dbx-tools/appkit-mastra";
982
+ *
983
+ * // No `agents`: a single built-in `default` analyst is registered, so the
984
+ * // chat endpoint works out of the box.
985
+ * const app = await createApp({ plugins: [mastra()] });
986
+ * ```
987
+ *
988
+ * @example Two agents, a pinned model, and Genie
989
+ * ```ts
990
+ * import { createApp, lakebase } from "@databricks/appkit";
991
+ * import { createAgent, mastra } from "@dbx-tools/appkit-mastra";
992
+ *
993
+ * const app = await createApp({
994
+ * plugins: [
995
+ * lakebase(),
996
+ * mastra({
997
+ * defaultModel: "databricks-claude-sonnet-4-6",
998
+ * defaultAgent: "analyst",
999
+ * genieSpaces: { sales: { spaceId: "01ef...", hint: "orders, returns" } },
1000
+ * agents: {
1001
+ * analyst: createAgent({
1002
+ * instructions: "You answer questions about sales.",
1003
+ * tools(plugins) {
1004
+ * return { ...plugins.genie?.toolkit() };
1005
+ * },
1006
+ * }),
1007
+ * helper: createAgent({ instructions: "You explain the analyst's answers." }),
1008
+ * },
1009
+ * }),
1010
+ * ],
1011
+ * });
1012
+ * ```
1013
+ */
1014
+ export const mastra = toPlugin(MastraPlugin);
1015
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2luLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3BsdWdpbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0E0Q0c7QUFFSCxPQUFPLEVBQ0wsY0FBYyxFQUNkLEtBQUssRUFDTCxtQkFBbUIsRUFDbkIsUUFBUSxFQUNSLE1BQU0sRUFDTixRQUFRLEdBS1QsTUFBTSxvQkFBb0IsQ0FBQztBQUM1QixPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDM0MsT0FBTyxFQUFFLE9BQU8sSUFBSSxXQUFXLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUMxRCxPQUFPLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsTUFBTSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDbkUsT0FBTyxFQUNMLFFBQVEsRUFDUixNQUFNLEdBS1AsTUFBTSwwQkFBMEIsQ0FBQztBQUNsQyxPQUFPLEVBQUUsT0FBTyxFQUErQixNQUFNLHlCQUF5QixDQUFDO0FBRS9FLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUU3QyxPQUFPLE9BQU8sTUFBTSxTQUFTLENBQUM7QUFHOUIsT0FBTyxFQUFFLFdBQVcsRUFBRSxpQkFBaUIsRUFBb0IsTUFBTSxVQUFVLENBQUM7QUFDNUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUNyQyxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsY0FBYyxFQUEyQixNQUFNLFVBQVUsQ0FBQztBQUN6RixPQUFPLEVBQ0wsa0JBQWtCLEVBQ2xCLHFCQUFxQixFQUNyQix1QkFBdUIsRUFDdkIsc0JBQXNCLEVBQ3RCLHFCQUFxQixHQUN0QixNQUFNLFlBQVksQ0FBQztBQUNwQixPQUFPLEVBQUUsdUJBQXVCLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDdEUs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LElBQUksT0FBTyxDQUFDLEtBQUssUUFBUTtRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQzVDLE1BQU0sQ0FBQyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNwQixJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQztRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQ25ELE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUN2QixDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F3Q0c7QUFDSCxNQUFNLENBQUMsTUFBTSxNQUFNLEdBQUcsUUFBUSxDQUFDLFlBQVksQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBBcHBLaXQgcGx1Z2luIHRoYXQgYnVpbGRzIG9uZSBvciBtb3JlIE1hc3RyYSBgQWdlbnRgIGluc3RhbmNlcyBhbmRcbiAqIG1vdW50cyB0aGUgYEBtYXN0cmEvZXhwcmVzc2Agc2VydmVyLiBDbGllbnRzIGRyaXZlIHRoZSBjb252ZXJzYXRpb25cbiAqIG92ZXIgdGhlIHN0YW5kYXJkIE1hc3RyYSBhZ2VudCBzdHJlYW0gKGBAbWFzdHJhL2NsaWVudC1qc2Anc1xuICogYGdldEFnZW50KGlkKS5zdHJlYW0oKWApLCBzbyB0aGVyZSdzIG5vIGJlc3Bva2UgY2hhdCB0cmFuc3BvcnQgdG9cbiAqIGtlZXAgaW4gc3luYy5cbiAqXG4gKiAtIEFnZW50czogcmVnaXN0ZXJlZCB0aHJvdWdoIGBjb25maWcuYWdlbnRzYCBhdCBwbHVnaW4gY3JlYXRpb25cbiAqICAgKHtAbGluayBNYXN0cmFBZ2VudERlZmluaXRpb259KS4gRWFjaCBlbnRyeSdzIGB0b29sc2AgZmllbGQgYWNjZXB0c1xuICogICBlaXRoZXIgYSBwbGFpbiByZWNvcmQgb3IgYSBgKHBsdWdpbnMpID0+IHRvb2xzYCBjYWxsYmFjayB0aGF0IGdldHNcbiAqICAgYSB0eXBlZCBzaWJsaW5nLXBsdWdpbiBpbmRleCAoe0BsaW5rIE1hc3RyYVBsdWdpbnN9KS4gT21pdFxuICogICBgY29uZmlnLmFnZW50c2AgdG8gZ2V0IGEgc2luZ2xlIGJ1aWx0LWluIGBkZWZhdWx0YCBhbmFseXN0LlxuICogLSBNb2RlbDogZWFjaCBhZ2VudCBjYWxsIHJlc29sdmVzIGEgYE1hc3RyYU1vZGVsQ29uZmlnYCB2aWFcbiAqICAge0BsaW5rIGJ1aWxkTW9kZWx9IGZyb20gYC4vbW9kZWwuanNgLiBQZXItYWdlbnQgYG1vZGVsYCBvdmVycmlkZXNcbiAqICAgKGBBZ2VudENvbmZpZ1tcIm1vZGVsXCJdYCBvciBhIGBtb2RlbElkYCBzdHJpbmcpIGZsb3cgdGhyb3VnaFxuICogICB7QGxpbmsgYnVpbGRBZ2VudHN9LlxuICogLSBNZW1vcnkgLyBzdG9yYWdlOiBwZXItYWdlbnQsIGJ1aWx0IGJ5IHtAbGluayBjcmVhdGVNZW1vcnlCdWlsZGVyfVxuICogICBmcm9tIGAuL21lbW9yeS5qc2AuIEJvdGggYXV0by1kZWZhdWx0IHRvIGB0cnVlYCB3aGVuIHRoZVxuICogICBgbGFrZWJhc2VgIHBsdWdpbiBpcyByZWdpc3RlcmVkICh1bmxlc3MgdGhlIGNhbGxlciBwYXNzZWRcbiAqICAgYGZhbHNlYCBvciBhIGN1c3RvbSBjb25maWcpLiBTdG9yYWdlIG5hbWVzcGFjZXMgcGVyIGFnZW50IHZpYVxuICogICB7QGxpbmsgYWdlbnRTdG9yYWdlU2NoZW1hTmFtZX0gcGVyIGFnZW50OyB0aGUgdmVjdG9yIHN0b3JlIGlzIGEgc2luZ2xlXG4gKiAgIHNoYXJlZCBzaW5nbGV0b24gYWNyb3NzIGV2ZXJ5IGFnZW50LlxuICogLSBTZXJ2ZXI6IHRoZSBFeHByZXNzIHN1YmFwcCB3aXJpbmcgbGl2ZXMgaW4gYC4vc2VydmVyLmpzYC5cbiAqIC0gSFRUUDogQXBwS2l0IG1vdW50cyB0aGlzIHBsdWdpbiB1bmRlciBgL2FwaS9tYXN0cmFgLiBBbG9uZ3NpZGUgdGhlXG4gKiAgIE1hc3RyYSBhZ2VudCByb3V0ZXMsIHRoZSBwbHVnaW4gcmVnaXN0ZXJzIGAvcm91dGUvaGlzdG9yeWBcbiAqICAgKGxvYWQgKyBjbGVhciBhIHRocmVhZCdzIG1lc3NhZ2VzKSwgYC9yb3V0ZS90aHJlYWRzYCAobGlzdCB0aGVcbiAqICAgY2FsbGVyJ3MgY29udmVyc2F0aW9ucyArIGRlbGV0ZSBvbmUpLCBgL21vZGVsc2AsIGAvZGVmYXVsdC1tb2RlbGAsXG4gKiAgIGAvc3VnZ2VzdGlvbnNgLCBgL3JvdXRlL2ZlZWRiYWNrYCAobG9nIGEgdGh1bWJzIC8gY29tbWVudCB0byBNTGZsb3dcbiAqICAgd2hlbiBmZWVkYmFjayBpcyBlbmFibGVkKSwgYW5kIHRoZSBnZW5lcmljIGAvZW1iZWQvOnR5cGUvOmlkYFxuICogICByZXNvbHZlciBmb3IgaW5saW5lIGNoYXJ0IC8gZGF0YSBtYXJrZXJzLiBFYWNoIGlzIHJlZ2lzdGVyZWQgdGhyb3VnaFxuICogICBBcHBLaXQncyBgcm91dGUoKWAgc28gaXQgbGFuZHMgaW4gdGhlIHBsdWdpbidzIGVuZHBvaW50IG1hcCwgYW5kXG4gKiAgIGV2ZXJ5IG91dGJvdW5kIGNhbGwgb25lIG1ha2VzIHJ1bnMgdGhyb3VnaCBgZXhlY3V0ZSgpYCAoc2VlXG4gKiAgIGAuL2RlZmF1bHRzLmpzYCkuIFRoZSBzdG9jayBgQG1hc3RyYS9leHByZXNzYCBzdXJmYWNlIGlzIGdhdGVkXG4gKiAgIGJ5IGBjb25maWcuYXBpQWNjZXNzYCAoZGVmYXVsdCBgXCJzY29wZWRcImApOiBvbmx5IGFnZW50IGluZmVyZW5jZSxcbiAqICAgcmVhZC1vbmx5IGFnZW50IG1ldGFkYXRhLCB0aGUgYC9yb3V0ZS8qYCByb3V0ZXMsIGFuZCAod2hlbiBlbmFibGVkKVxuICogICBNQ1AgYXJlIGRpc3BhdGNoZWQgdG8gTWFzdHJhOyBhZG1pbiAvIG11dGF0aW5nIC8gYnVsay1leHBvcnQgcm91dGVzXG4gKiAgIGFyZSByZWZ1c2VkIHdpdGggYDQwM2AuIFNlZSB7QGxpbmsgaXNNYXN0cmFSZXF1ZXN0QWxsb3dlZH0uXG4gKiAtIE1DUDogb3B0IGluIHdpdGggYGNvbmZpZy5tY3BgIHRvIGV4cG9zZSB0aGUgYWdlbnRzIChhbmQgb3B0aW9uYWxseVxuICogICB0b29scykgYXMgYSBNYXN0cmEgYE1DUFNlcnZlcmAuIEl0IGlzIHJlZ2lzdGVyZWQgb24gdGhlIGBNYXN0cmFgXG4gKiAgIGluc3RhbmNlIHZpYSBgbWNwU2VydmVyc2AsIHNvIGBAbWFzdHJhL2V4cHJlc3NgIHNlcnZlcyB0aGUgc3RvY2tcbiAqICAgTUNQIHRyYW5zcG9ydCByb3V0ZXMgKGAvbWNwLzxzZXJ2ZXJJZD4vLi4uYCkgdW5kZXIgdGhlIG1vdW50LiBTZWVcbiAqICAgYC4vbWNwLmpzYC5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHtcbiAgRXhlY3V0aW9uRXJyb3IsXG4gIGdlbmllLFxuICBnZXRFeGVjdXRpb25Db250ZXh0LFxuICBsYWtlYmFzZSxcbiAgUGx1Z2luLFxuICB0b1BsdWdpbixcbiAgdHlwZSBFeGVjdXRpb25SZXN1bHQsXG4gIHR5cGUgSUFwcFJvdXRlcixcbiAgdHlwZSBQbHVnaW5NYW5pZmVzdCxcbiAgdHlwZSBSZXNvdXJjZVJlcXVpcmVtZW50LFxufSBmcm9tIFwiQGRhdGFicmlja3MvYXBwa2l0XCI7XG5pbXBvcnQgeyBwbHVnaW4gfSBmcm9tIFwiQGRieC10b29scy9hcHBraXRcIjtcbmltcG9ydCB7IHNlcnZpbmcgYXMgbm9kZVNlcnZpbmcgfSBmcm9tIFwiQGRieC10b29scy9tb2RlbFwiO1xuaW1wb3J0IHsgYXN5bmMsIGVycm9yLCBsb2csIHN0cmluZyB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5pbXBvcnQge1xuICBmZWVkYmFjayxcbiAgcm91dGVzLFxuICB0eXBlIENoYXJ0LFxuICB0eXBlIE1hc3RyYUNsaWVudENvbmZpZyxcbiAgdHlwZSBNYXN0cmFGZWVkYmFja1JlcXVlc3QsXG4gIHR5cGUgU3RhdGVtZW50RGF0YSxcbn0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLW1hc3RyYVwiO1xuaW1wb3J0IHsgZGlzcGxheSwgdHlwZSBTZXJ2aW5nRW5kcG9pbnRTdW1tYXJ5IH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLW1vZGVsXCI7XG5pbXBvcnQgdHlwZSB7IEFnZW50IH0gZnJvbSBcIkBtYXN0cmEvY29yZS9hZ2VudFwiO1xuaW1wb3J0IHsgTWFzdHJhIH0gZnJvbSBcIkBtYXN0cmEvY29yZS9tYXN0cmFcIjtcbmltcG9ydCB0eXBlIHsgUmVxdWVzdENvbnRleHQgfSBmcm9tIFwiQG1hc3RyYS9jb3JlL3JlcXVlc3QtY29udGV4dFwiO1xuaW1wb3J0IGV4cHJlc3MgZnJvbSBcImV4cHJlc3NcIjtcbmltcG9ydCB0eXBlIHsgUG9vbCB9IGZyb20gXCJwZ1wiO1xuXG5pbXBvcnQgeyBidWlsZEFnZW50cywgRkFMTEJBQ0tfQUdFTlRfSUQsIHR5cGUgQnVpbHRBZ2VudHMgfSBmcm9tIFwiLi9hZ2VudHNcIjtcbmltcG9ydCB7IGZldGNoQ2hhcnQgfSBmcm9tIFwiLi9jaGFydFwiO1xuaW1wb3J0IHsgTUFTVFJBX0NPTkZJR19TQ0hFTUEsIHJlc29sdmVVc2VyS2V5LCB0eXBlIE1hc3RyYVBsdWdpbkNvbmZpZyB9IGZyb20gXCIuL2NvbmZpZ1wiO1xuaW1wb3J0IHtcbiAgY2hhcnRGZXRjaERlZmF1bHRzLFxuICBmZWVkYmFja1dyaXRlRGVmYXVsdHMsXG4gIGdlbmllU3VnZ2VzdGlvbkRlZmF1bHRzLFxuICBtb2RlbENhdGFsb2d1ZURlZmF1bHRzLFxuICBzdGF0ZW1lbnREYXRhRGVmYXVsdHMsXG59IGZyb20gXCIuL2RlZmF1bHRzXCI7XG5pbXBvcnQgeyBjb2xsZWN0U3BhY2VTdWdnZXN0aW9ucywgcmVzb2x2ZUdlbmllU3BhY2VzIH0gZnJvbSBcIi4vZ2VuaWVcIjtcbmltcG9ydCB7IGhpc3RvcnlSb3V0ZSB9IGZyb20gXCIuL2hpc3RvcnlcIjtcbmltcG9ydCB7IGJ1aWxkTWNwU2VydmVyLCB0eXBlIFJlc29sdmVkTWNwIH0gZnJvbSBcIi4vbWNwXCI7XG5pbXBvcnQgeyBjcmVhdGVNZW1vcnlCdWlsZGVyLCBjcmVhdGVTZXJ2aWNlUHJpbmNpcGFsUG9vbCwgbmVlZHNMYWtlYmFzZSB9IGZyb20gXCIuL21lbW9yeVwiO1xuaW1wb3J0IHsgbG9nRmVlZGJhY2ssIHJlc29sdmVGZWVkYmFja0VuYWJsZWQgfSBmcm9tIFwiLi9tbGZsb3dcIjtcbmltcG9ydCB7IGJ1aWxkT2JzZXJ2YWJpbGl0eSB9IGZyb20gXCIuL29ic2VydmFiaWxpdHlcIjtcbmltcG9ydCB7XG4gIGF0dGFjaFJvdXRlUGF0Y2hNaWRkbGV3YXJlLFxuICBjcmVhdGVSZXF1ZXN0Q29udGV4dCxcbiAgaXNNYXN0cmFSZXF1ZXN0QWxsb3dlZCxcbiAgTWFzdHJhU2VydmVyLFxufSBmcm9tIFwiLi9zZXJ2ZXJcIjtcbmltcG9ydCB7IHJlc29sdmVTZXJ2aW5nQ29uZmlnIH0gZnJvbSBcIi4vc2VydmluZ1wiO1xuaW1wb3J0IHsgZmV0Y2hTdGF0ZW1lbnREYXRhLCBTVEFURU1FTlRfUk9XX0NBUCB9IGZyb20gXCIuL3N0YXRlbWVudFwiO1xuaW1wb3J0IHsgdGhyZWFkc1JvdXRlIH0gZnJvbSBcIi4vdGhyZWFkc1wiO1xuaW1wb3J0IHsgaW52YWxpZEZpZWxkcyB9IGZyb20gXCIuL3ZhbGlkYXRpb25cIjtcblxuY29uc3QgR0VOSUVfTUFOSUZFU1QgPSBwbHVnaW4uZGF0YShnZW5pZSkucGx1Z2luLm1hbmlmZXN0O1xuY29uc3QgTEFLRUJBU0VfTUFOSUZFU1QgPSBwbHVnaW4uZGF0YShsYWtlYmFzZSkucGx1Z2luLm1hbmlmZXN0O1xuXG4vKipcbiAqIEJ1ZGdldCBmb3IgZHJhaW5pbmcgdGhlIG1lbW9yeSBzZXJ2aWNlLXByaW5jaXBhbCBwb29sIG9uIHNodXRkb3duLiBXZWxsXG4gKiBpbnNpZGUgQXBwS2l0J3MgMTVzIGdyYWNlZnVsLXNodXRkb3duIHdpbmRvdywgc28gYSBzdHVjayBjb25uZWN0aW9uIGNhblxuICogbmV2ZXIgYmUgd2hhdCBob2xkcyB0aGUgcHJvY2VzcyBvcGVuLlxuICovXG5jb25zdCBQT09MX0RSQUlOX1RJTUVPVVRfTVMgPSA1XzAwMDtcblxuLyoqIENlaWxpbmcgb24gdGhlIGA/dGltZW91dE1zPWAgbG9uZy1wb2xsIGJ1ZGdldCBhIGNsaWVudCBtYXkgcmVxdWVzdC4gKi9cbmNvbnN0IE1BWF9FTUJFRF9QT0xMX1RJTUVPVVRfTVMgPSA1ICogNjBfMDAwO1xuXG4vKiogU3RhYmxlIGNsaWVudCBtZXNzYWdlIHdoZW4gdGhlIHdvcmtzcGFjZSdzIG1vZGVsIGNhdGFsb2d1ZSBjYW4ndCBiZSByZWFkLiAqL1xuY29uc3QgTU9ERUxfQ0FUQUxPR1VFX0ZBSUxFRF9NRVNTQUdFID0gXCJDb3VsZCBub3QgcmVhZCB0aGUgd29ya3NwYWNlJ3MgbW9kZWwgY2F0YWxvZ3VlXCI7XG5cbi8qKiBTdGFibGUgY2xpZW50IG1lc3NhZ2UgZm9yIGEgZmVlZGJhY2sgYm9keSB0aGF0IGZhaWxzIHNjaGVtYSB2YWxpZGF0aW9uLiAqL1xuY29uc3QgSU5WQUxJRF9GRUVEQkFDS19NRVNTQUdFID0gXCJJbnZhbGlkIGZlZWRiYWNrIHJlcXVlc3RcIjtcblxuLyoqIENhY2hlIG5hbWVzcGFjZSBmb3Igc3RhdGVtZW50IHJlc3VsdCBzZXRzLCBtYXRjaGluZyB0aGUgY2hhcnQgY2FjaGUncyBmb3JtLiAqL1xuY29uc3QgU1RBVEVNRU5UX0NBQ0hFX05BTUVTUEFDRSA9IFwibWFzdHJhOnN0YXRlbWVudFwiO1xuXG4vKipcbiAqIEFwcEtpdCBwbHVnaW4gKHJlZ2lzdGVyZWQgbmFtZTogYG1hc3RyYWApIHRoYXQgaG9zdHMgTWFzdHJhIGFnZW50c1xuICogd2l0aCBvcHRpb25hbCBMYWtlYmFzZS1iYWNrZWQgbWVtb3J5IGFuZCBBSSBTREsgY2hhdCByb3V0ZXMgdW5kZXJcbiAqIHRoZSBwbHVnaW4gbW91bnQgKHR5cGljYWxseSBgL2FwaS9tYXN0cmFgKS5cbiAqXG4gKiBAZXhhbXBsZSBSZWdpc3RlciB0aGUgcGx1Z2luXG4gKiBgYGB0c1xuICogaW1wb3J0IHsgY3JlYXRlQXBwLCBsYWtlYmFzZSB9IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXRcIjtcbiAqIGltcG9ydCB7IGNyZWF0ZUFnZW50LCBtYXN0cmEgfSBmcm9tIFwiQGRieC10b29scy9hcHBraXQtbWFzdHJhXCI7XG4gKlxuICogLy8gYGxha2ViYXNlYCBmaXJzdDogcmVnaXN0ZXJpbmcgaXQgYXV0by1lbmFibGVzIE1hc3RyYSBzdG9yYWdlICsgbWVtb3J5LlxuICogY29uc3QgYXBwID0gYXdhaXQgY3JlYXRlQXBwKHtcbiAqICAgcGx1Z2luczogW1xuICogICAgIGxha2ViYXNlKCksXG4gKiAgICAgbWFzdHJhKHtcbiAqICAgICAgIGdlbmllU3BhY2VzOiB7IGRlZmF1bHQ6IHByb2Nlc3MuZW52LkRBVEFCUklDS1NfR0VOSUVfU1BBQ0VfSUQhIH0sXG4gKiAgICAgICBhZ2VudHM6IGNyZWF0ZUFnZW50KHtcbiAqICAgICAgICAgbmFtZTogXCJhbmFseXN0XCIsXG4gKiAgICAgICAgIGluc3RydWN0aW9uczogXCJZb3UgYW5zd2VyIHF1ZXN0aW9ucyBhYm91dCByZXZlbnVlIGFuZCByZXR1cm5zLlwiLFxuICogICAgICAgICB0b29scyhwbHVnaW5zKSB7XG4gKiAgICAgICAgICAgcmV0dXJuIHsgLi4ucGx1Z2lucy5nZW5pZT8udG9vbGtpdCgpIH07XG4gKiAgICAgICAgIH0sXG4gKiAgICAgICB9KSxcbiAqICAgICB9KSxcbiAqICAgXSxcbiAqIH0pO1xuICogYGBgXG4gKlxuICogQGV4YW1wbGUgUmVhZCB0aGUgYWdlbnRzIGJhY2sgb2ZmIHRoZSBBcHBLaXQgaW5zdGFuY2VcbiAqIGBgYHRzXG4gKiBjb25zdCBhZ2VudElkcyA9IGFwcC5tYXN0cmEubGlzdCgpO1xuICogY29uc3QgZW5kcG9pbnRzID0gYXdhaXQgYXBwLm1hc3RyYS5saXN0TW9kZWxzKCk7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGNsYXNzIE1hc3RyYVBsdWdpbiBleHRlbmRzIFBsdWdpbjxNYXN0cmFQbHVnaW5Db25maWc+IHtcbiAgLy8gQW5ub3RhdGVkIHJhdGhlciB0aGFuIGxlZnQgdG8gYHNhdGlzZmllc2A6IHRoZSBjb25maWcgc2NoZW1hJ3MgdHlwZSBjb21lc1xuICAvLyBmcm9tIGBAdHlwZXMvanNvbi1zY2hlbWFgLCB3aGljaCB0aGlzIHBhY2thZ2UgZG9lcyBub3QgZGVwZW5kIG9uLCBzbyBhblxuICAvLyBpbmZlcnJlZCBtYW5pZmVzdCB0eXBlIGNhbm5vdCBiZSBuYW1lZCBpbiB0aGUgZW1pdHRlZCBkZWNsYXJhdGlvbi4gVGhlXG4gIC8vIGBQbHVnaW5NYW5pZmVzdDxcIm1hc3RyYVwiPmAgcGFyYW1ldGVyIHN0aWxsIGNhcnJpZXMgdGhlIGxpdGVyYWwgbmFtZSBpbnRvXG4gIC8vIGB0b1BsdWdpbigpYCdzIGZhY3RvcnkgdHlwZS5cbiAgc3RhdGljIG1hbmlmZXN0OiBQbHVnaW5NYW5pZmVzdDxcIm1hc3RyYVwiPiA9IHtcbiAgICBuYW1lOiBcIm1hc3RyYVwiLFxuICAgIGRpc3BsYXlOYW1lOiBcIk1hc3RyYVwiLFxuICAgIGRlc2NyaXB0aW9uOlxuICAgICAgXCJCdWlsZHMgYSBNYXN0cmEgQWdlbnQgd2l0aCB1c2VyLXNjb3BlZCB3b3Jrc3BhY2UgYXV0aCAoYXNVc2VyKSBcIiArXG4gICAgICBcImFuZCBvcHRpb25hbCBQb3N0Z3Jlcy1iYWNrZWQgTWFzdHJhIE1lbW9yeSB2aWEgdGhlIGBsYWtlYmFzZWAgcGx1Z2luLlwiLFxuICAgIHN0YWJpbGl0eTogXCJiZXRhXCIsXG4gICAgcmVzb3VyY2VzOiB7XG4gICAgICByZXF1aXJlZDogW10sXG4gICAgICBvcHRpb25hbDogW1xuICAgICAgICAvLyBSZXVzZSB0aGUgR2VuaWUgc3BhY2UtaWQgYmluZGluZyBkZWNsYXJlZCBieSBBcHBLaXQncyBgZ2VuaWVgXG4gICAgICAgIC8vIG1hbmlmZXN0IHNvIHRoZSByZXNvdXJjZS1iaW5kaW5nIHNoYXBlIGlzIGlkZW50aWNhbCB0byBBcHBLaXQncyBhbmRcbiAgICAgICAgLy8gYW4gZXhpc3RpbmcgYGFwcC55YW1sYCAvIGBnZW5pZSh7IHNwYWNlcyB9KWAgd2lyaW5nIGtlZXBzIHdvcmtpbmcuXG4gICAgICAgIC8vIFRoZSBidWlsdC1pbiBHZW5pZSB0b29scyB0YWxrIHRvIEdlbmllIGRpcmVjdGx5IHRocm91Z2hcbiAgICAgICAgLy8gYEBkYngtdG9vbHMvZ2VuaWVgLCBzbyBvbmx5IHRoZSBiaW5kaW5nIGlzIHNoYXJlZCwgbm90IHRoZSBwbHVnaW4nc1xuICAgICAgICAvLyB0b29scy5cbiAgICAgICAgLi4uR0VOSUVfTUFOSUZFU1QucmVzb3VyY2VzLnJlcXVpcmVkLFxuICAgICAgICAuLi5MQUtFQkFTRV9NQU5JRkVTVC5yZXNvdXJjZXMucmVxdWlyZWQsXG4gICAgICBdLFxuICAgIH0sXG4gICAgY29uZmlnOiB7IHNjaGVtYTogTUFTVFJBX0NPTkZJR19TQ0hFTUEgfSxcbiAgfTtcblxuICAvKipcbiAgICogVGlnaHRlbiByZXNvdXJjZSByZXF1aXJlbWVudHMgYmFzZWQgb24gd2hpY2ggZmVhdHVyZXMgYXJlIGVuYWJsZWQuXG4gICAqIEFwcEtpdCBjYWxscyB0aGlzIGF0IHJlZ2lzdHJhdGlvbiB0aW1lIChjb25maWctYXdhcmUpIHNvIGRpc2FibGVkXG4gICAqIGZlYXR1cmVzIGRvbid0IHN1cmZhY2UgdGhlaXIgcmVzb3VyY2UgYXNrcyB0byB0aGUgaG9zdCBhcHAuXG4gICAqL1xuICBzdGF0aWMgZ2V0UmVzb3VyY2VSZXF1aXJlbWVudHMoY29uZmlnOiBNYXN0cmFQbHVnaW5Db25maWcpOiBSZXNvdXJjZVJlcXVpcmVtZW50W10ge1xuICAgIGNvbnN0IHJlc291cmNlczogUmVzb3VyY2VSZXF1aXJlbWVudFtdID0gW107XG4gICAgY29uc3QgZW5hYmxlZE1hbmlmZXN0czogUGx1Z2luTWFuaWZlc3Q8c3RyaW5nPltdID0gW107XG5cbiAgICBpZiAobmVlZHNMYWtlYmFzZShjb25maWcpKSB7XG4gICAgICBlbmFibGVkTWFuaWZlc3RzLnB1c2goTEFLRUJBU0VfTUFOSUZFU1QpO1xuICAgIH1cbiAgICBmb3IgKGNvbnN0IG0gb2YgZW5hYmxlZE1hbmlmZXN0cykge1xuICAgICAgZm9yIChjb25zdCByZXNvdXJjZSBvZiBtLnJlc291cmNlcy5yZXF1aXJlZCkge1xuICAgICAgICByZXNvdXJjZXMucHVzaCh7IC4uLnJlc291cmNlLCByZXF1aXJlZDogdHJ1ZSB9KTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHJlc291cmNlcztcbiAgfVxuXG4gIHByaXZhdGUgbG9nZ2VyID0gbG9nLmxvZ2dlcih0aGlzKTtcbiAgcHJpdmF0ZSBidWlsdDogQnVpbHRBZ2VudHMgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBtYXN0cmE6IE1hc3RyYSB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIG1hc3RyYUFwcDogZXhwcmVzcy5FeHByZXNzIHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgbWFzdHJhU2VydmVyOiBNYXN0cmFTZXJ2ZXIgfCBudWxsID0gbnVsbDtcbiAgLyoqXG4gICAqIFRoZSBvcHRpb25hbCBNQ1Agc2VydmVyIGV4cG9zaW5nIHRoaXMgcGx1Z2luJ3MgYWdlbnRzIC8gdG9vbHMsIG9yXG4gICAqIGBudWxsYCB3aGVuIGBjb25maWcubWNwYCBpcyBkaXNhYmxlZCAodGhlIGRlZmF1bHQpLiBCdWlsdCBpblxuICAgKiB7QGxpbmsgYnVpbGRBZ2VudEFuZFNlcnZlcn0gYW5kIHJlZ2lzdGVyZWQgb24gdGhlIE1hc3RyYSBpbnN0YW5jZS5cbiAgICovXG4gIHByaXZhdGUgbWNwOiBSZXNvbHZlZE1jcCB8IG51bGwgPSBudWxsO1xuICAvKipcbiAgICogRGVkaWNhdGVkIHNlcnZpY2UtcHJpbmNpcGFsIExha2ViYXNlIHBvb2wgYmFja2luZyBNYXN0cmEgbWVtb3J5IC9cbiAgICogc3RvcmFnZS4gQnVpbHQgb25jZSBpbiB7QGxpbmsgYnVpbGRBZ2VudEFuZFNlcnZlcn0gKG91dHNpZGUgYW55XG4gICAqIGBhc1VzZXJgIHNjb3BlLCBzbyBpdCBuZXZlciBpbmhlcml0cyBhIHJlcXVlc3QncyBPQk8gaWRlbnRpdHkpIGFuZFxuICAgKiBkcmFpbmVkIGluIHtAbGluayBzaHV0ZG93bn0uIGBudWxsYCB1bnRpbCBzZXR1cCBydW5zIG9yIHdoZW5cbiAgICogTGFrZWJhc2UgaXNuJ3QgbmVlZGVkLlxuICAgKi9cbiAgcHJpdmF0ZSBzZXJ2aWNlUHJpbmNpcGFsUG9vbDogUG9vbCB8IG51bGwgPSBudWxsO1xuXG4gIG92ZXJyaWRlIGFzeW5jIHNldHVwKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIC8vIFdhaXQgdW50aWwgc2libGluZyBwbHVnaW5zIChlLmcuIGBsYWtlYmFzZWApIGZpbmlzaCBgc2V0dXAoKWAgc29cbiAgICAvLyB0aGUgbGFrZWJhc2UgcG9vbCBpcyB2YWxpZCB3aGVuIHN0b3JhZ2UvbWVtb3J5IGFyZSBlbmFibGVkLlxuICAgIHRoaXMuY29udGV4dD8ub25MaWZlY3ljbGUoXCJzZXR1cDpjb21wbGV0ZVwiLCBhc3luYyAoKSA9PiB7XG4gICAgICB0aGlzLmFwcGx5TGFrZWJhc2VBdXRvRGVmYXVsdHMoKTtcbiAgICAgIGF3YWl0IHRoaXMuYnVpbGRBZ2VudEFuZFNlcnZlcigpO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFdoZW4gdGhlIGBsYWtlYmFzZWAgcGx1Z2luIGlzIHJlZ2lzdGVyZWQsIGF1dG8tZW5hYmxlIGBzdG9yYWdlYFxuICAgKiBhbmQgYG1lbW9yeWAgdW5sZXNzIHRoZSBjYWxsZXIgb3B0ZWQgb3V0IGV4cGxpY2l0bHkgKGBmYWxzZWAgb3IgYVxuICAgKiBjdXN0b20gY29uZmlnIG9iamVjdCkuIFJ1biBhZnRlciBgc2V0dXA6Y29tcGxldGVgIHNvIHRoZSBsb29rdXBcbiAgICogaXMgcmVsaWFibGU6IGFueSBwbHVnaW4gdGhhdCByZWdpc3RlcnMgaXRzZWxmIHN5bmNocm9ub3VzbHkgaXNcbiAgICogYWxyZWFkeSBpbiB0aGUgcmVnaXN0cnkgYnkgdGhlIHRpbWUgdGhpcyBmaXJlcy5cbiAgICovXG4gIHByaXZhdGUgYXBwbHlMYWtlYmFzZUF1dG9EZWZhdWx0cygpOiB2b2lkIHtcbiAgICBjb25zdCBoYXNMYWtlYmFzZSA9IHBsdWdpbi5pbnN0YW5jZSh0aGlzLmNvbnRleHQsIGxha2ViYXNlKSAhPT0gdW5kZWZpbmVkO1xuICAgIGlmICghaGFzTGFrZWJhc2UpIHJldHVybjtcbiAgICBpZiAodGhpcy5jb25maWcuc3RvcmFnZSA9PT0gdW5kZWZpbmVkKSB0aGlzLmNvbmZpZy5zdG9yYWdlID0gdHJ1ZTtcbiAgICBpZiAodGhpcy5jb25maWcubWVtb3J5ID09PSB1bmRlZmluZWQpIHRoaXMuY29uZmlnLm1lbW9yeSA9IHRydWU7XG4gIH1cblxuICAvKipcbiAgICogRHJhaW4gdGhlIG1lbW9yeSBzZXJ2aWNlLXByaW5jaXBhbCBwb29sLiBJZGVtcG90ZW50OiB0aGUgaGFuZGxlIGlzXG4gICAqIGNsZWFyZWQgYmVmb3JlIHRoZSBkcmFpbiBzdGFydHMsIHNvIGEgc2Vjb25kIGNhbGwgaXMgYSBuby1vcCBhbmQgYVxuICAgKiBsYXRlciBgc2V0dXAoKWAgcmVidWlsZHMgdGhlIHBvb2wuIEJvdW5kZWQgYnlcbiAgICoge0BsaW5rIFBPT0xfRFJBSU5fVElNRU9VVF9NU30gdG8gc3RheSB3ZWxsIGluc2lkZSB0aGUgMTVzIGdyYWNlZnVsXG4gICAqIHNodXRkb3duIGJ1ZGdldC5cbiAgICovXG4gIGFzeW5jIHNodXRkb3duKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHBvb2wgPSB0aGlzLnNlcnZpY2VQcmluY2lwYWxQb29sO1xuICAgIGlmICghcG9vbCkgcmV0dXJuO1xuICAgIHRoaXMuc2VydmljZVByaW5jaXBhbFBvb2wgPSBudWxsO1xuICAgIHRoaXMubG9nZ2VyLmluZm8oXCJjbG9zaW5nIG1lbW9yeSBTUCBwb29sXCIpO1xuICAgIC8vIFRoZSBidWRnZXQgdGltZXIgaXMgY2FuY2VsbGVkIG9uIHRoZSB3YXkgb3V0IHNvIGEgZHJhaW4gdGhhdCBmaW5pc2hlZFxuICAgIC8vIGVhcmx5IGNhbm5vdCBob2xkIHRoZSBldmVudCBsb29wIG9wZW4gZm9yIHRoZSByZXN0IG9mIHRoZSB3aW5kb3cuXG4gICAgY29uc3QgYnVkZ2V0ID0gbmV3IEFib3J0Q29udHJvbGxlcigpO1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCBQcm9taXNlLnJhY2UoW3Bvb2wuZW5kKCksIGFzeW5jLnNsZWVwKFBPT0xfRFJBSU5fVElNRU9VVF9NUywgYnVkZ2V0LnNpZ25hbCldKTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIHRoaXMubG9nZ2VyLmVycm9yKFwiZXJyb3IgY2xvc2luZyBtZW1vcnkgU1AgcG9vbFwiLCB7XG4gICAgICAgIGVycm9yOiBlcnJvci5lcnJvck1lc3NhZ2UoZXJyKSxcbiAgICAgIH0pO1xuICAgIH0gZmluYWxseSB7XG4gICAgICBidWRnZXQuYWJvcnQoKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQWJvcnQgaW4tZmxpZ2h0IHdvcmsuIEFwcEtpdCdzIGdyYWNlZnVsIHNodXRkb3duIGNhbGxzIHRoaXMgaG9va1xuICAgKiBzeW5jaHJvbm91c2x5IGFuZCBuZXZlciBhd2FpdHMge0BsaW5rIHNodXRkb3dufSwgc28gdGhlIHBvb2wgZHJhaW4gaXNcbiAgICogc3RhcnRlZCBoZXJlIHRvbzsgaXQgY2Fubm90IGJlIGF3YWl0ZWQgZnJvbSBhIGB2b2lkYCBob29rLCBhbmRcbiAgICoge0BsaW5rIHNodXRkb3dufSBpcyBpZGVtcG90ZW50IHNvIHRoZSBkdXBsaWNhdGUgY2FsbCBpcyBmcmVlLlxuICAgKi9cbiAgb3ZlcnJpZGUgYWJvcnRBY3RpdmVPcGVyYXRpb25zKCk6IHZvaWQge1xuICAgIHN1cGVyLmFib3J0QWN0aXZlT3BlcmF0aW9ucygpO1xuICAgIHZvaWQgdGhpcy5zaHV0ZG93bigpO1xuICB9XG5cbiAgb3ZlcnJpZGUgZXhwb3J0cygpIHtcbiAgICByZXR1cm4ge1xuICAgICAgLyoqXG4gICAgICAgKiBJZHMgb2YgZXZlcnkgcmVnaXN0ZXJlZCBhZ2VudCBpbiByZWdpc3RyYXRpb24gb3JkZXIuIE1hdGNoZXNcbiAgICAgICAqIEFwcEtpdCBgYWdlbnRzLmxpc3QoKWAgc28gY2FsbGVycyBjYW4gaXRlcmF0ZSB0aGUgcmVnaXN0cnkgdGhlXG4gICAgICAgKiBzYW1lIHdheSB1bmRlciBib3RoIHBsdWdpbnMuXG4gICAgICAgKi9cbiAgICAgIGxpc3Q6ICgpOiBzdHJpbmdbXSA9PiBPYmplY3Qua2V5cyh0aGlzLmJ1aWx0Py5hZ2VudHMgPz8ge30pLFxuICAgICAgLyoqXG4gICAgICAgKiBMb29rIHVwIGEgcmVnaXN0ZXJlZCBhZ2VudCBieSBpZC4gUmV0dXJucyBgbnVsbGAgKG5vdFxuICAgICAgICogdW5kZWZpbmVkKSB3aGVuIHVua25vd24gc28gY2FsbCBzaXRlcyBjYW4gZWFybHktcmV0dXJuIHdpdGhvdXRcbiAgICAgICAqIGEgc2VwYXJhdGUgYGluYCBjaGVjay5cbiAgICAgICAqL1xuICAgICAgZ2V0OiAoaWQ6IHN0cmluZyk6IEFnZW50IHwgbnVsbCA9PiB0aGlzLmJ1aWx0Py5hZ2VudHNbaWRdID8/IG51bGwsXG4gICAgICAvKipcbiAgICAgICAqIFRoZSBhZ2VudCB0aGUgY2xpZW50IGNvbnZlcnNlcyB3aXRoIHdoZW4gaXQgZG9lc24ndCBuYW1lIG9uZS5cbiAgICAgICAqIFJlc29sdmVzIHRvIGBjb25maWcuZGVmYXVsdEFnZW50YCwgdGhlIGZpcnN0IHJlZ2lzdGVyZWQgaWQsIG9yXG4gICAgICAgKiB0aGUgYnVpbHQtaW4gYGRlZmF1bHRgIGZhbGxiYWNrLlxuICAgICAgICovXG4gICAgICBnZXREZWZhdWx0OiAoKTogQWdlbnQgfCBudWxsID0+XG4gICAgICAgICh0aGlzLmJ1aWx0ICYmIHRoaXMuYnVpbHQuYWdlbnRzW3RoaXMuYnVpbHQuZGVmYXVsdEFnZW50SWRdKSA/PyBudWxsLFxuICAgICAgLyoqIFVuZGVybHlpbmcgTWFzdHJhIGluc3RhbmNlIGZvciBhZHZhbmNlZCB1c2UgKGN1c3RvbSByb3V0ZXMgZXRjLikuICovXG4gICAgICBnZXRNYXN0cmE6ICgpID0+IHRoaXMubWFzdHJhLFxuICAgICAgLyoqXG4gICAgICAgKiBCdWlsZCB0aGUgYFJlcXVlc3RDb250ZXh0YCBhbiBhZ2VudCB0dXJuIGRyaXZlbiBmcm9tIE9VVFNJREUgdGhpc1xuICAgICAgICogcGx1Z2luJ3Mgcm91dGVzIG5lZWRzIChhIFRlYW1zIGFjdGl2aXR5IG9uIGFub3RoZXIgcGx1Z2luJ3MgZW5kcG9pbnQsIGFcbiAgICAgICAqIHNjaGVkdWxlZCBqb2IpLlxuICAgICAgICpcbiAgICAgICAqIE1hc3RyYSdzIHVzZXItc2NvcGVkIHRvb2xzIC0gYGFza19nZW5pZWAgbW9zdCB2aXNpYmx5IC0gcmVhZCB0aGUgQXBwS2l0XG4gICAgICAgKiB1c2VyIG9mZiB0aGUgcmVxdWVzdCBjb250ZXh0LCB3aGljaCBvbmx5IHRoZSBIVFRQIG1pZGRsZXdhcmUgc3RhbXBzLlxuICAgICAgICogQ2FsbGluZyBgYWdlbnQuZ2VuZXJhdGVgIHdpdGggYSByYXcgcHJvbXB0IHRoZXJlZm9yZSBhbnN3ZXJzIFwidGhlIGRhdGFcbiAgICAgICAqIHNvdXJjZSBpcyB1bnJlYWNoYWJsZVwiIHdoZXJlIHRoZSBjaGF0IHJvdXRlcyBhbnN3ZXIgd2l0aCByZWFsIGRhdGEuXG4gICAgICAgKiBQYXNzaW5nIHRoaXMgYXMgYHJlcXVlc3RDb250ZXh0YCBjbG9zZXMgdGhhdCBnYXAsIHNvIGFuIG91dC1vZi1iYW5kIHR1cm5cbiAgICAgICAqIGhhcyBleGFjdGx5IHRoZSBjYXBhYmlsaXRpZXMgYSBjaGF0IHR1cm4gaGFzLlxuICAgICAgICpcbiAgICAgICAqIE11c3QgYmUgY2FsbGVkIElOU0lERSBhbiBgYXNVc2VyKHJlcSlgIHNjb3BlIHRvIGluaGVyaXQgdGhlIGNhbGxlcidzXG4gICAgICAgKiBPQk8gaWRlbnRpdHk7IG91dHNpZGUgb25lIGl0IHJlc29sdmVzIHRvIHRoZSBzZXJ2aWNlIHByaW5jaXBhbC5cbiAgICAgICAqL1xuICAgICAgY3JlYXRlUmVxdWVzdENvbnRleHQ6IChcbiAgICAgICAgb3B0aW9uczogeyB0aHJlYWRJZD86IHN0cmluZzsgcmVzb3VyY2VJZD86IHN0cmluZzsgcmVxdWVzdElkPzogc3RyaW5nIH0gPSB7fSxcbiAgICAgICk6IFByb21pc2U8UmVxdWVzdENvbnRleHQ+ID0+IGNyZWF0ZVJlcXVlc3RDb250ZXh0KG9wdGlvbnMpLFxuICAgICAgLyoqXG4gICAgICAgKiBNQ1AgZW5kcG9pbnQgaW5mbyB3aGVuIGBjb25maWcubWNwYCBpcyBlbmFibGVkLCBlbHNlIGBudWxsYC5cbiAgICAgICAqIFN0cmVhbWFibGUgSFRUUCBpcyBgaHR0cGA7IHRoZSBTU0UgcGFpciBpcyB0aGUgbGVnYWN5IHRyYW5zcG9ydC5cbiAgICAgICAqXG4gICAgICAgKiBFYWNoIHBhdGggaXMgZ2l2ZW4gdHdpY2U6IG1vdW50LXJlbGF0aXZlIChgaHR0cFBhdGhgLCBgc3NlUGF0aGAsXG4gICAgICAgKiBgbWVzc2FnZVBhdGhgKSBmb3IgYW55dGhpbmcgdGhhdCBhbHJlYWR5IGtub3dzIHdoZXJlIHRoZSBwbHVnaW4gaXNcbiAgICAgICAqIG1vdW50ZWQsIGFuZCBhYnNvbHV0ZSAoYGh0dHBgLCBgc3NlYCwgYG1lc3NhZ2VzYCkgZm9yIE1DUCBjbGllbnRzLFxuICAgICAgICogd2hpY2ggdGFrZSBhIHNpbmdsZSBVUkwgYW5kIGNhbm5vdCBjb21wb3NlIG9uZS4gVGhlIGFic29sdXRlIGZvcm0gaXNcbiAgICAgICAqIGJ1aWx0IGZyb20ge0BsaW5rIGJhc2VQYXRofSwgc28gaXQgaG9ub3JzIGEgYGNvbmZpZy5uYW1lYCBvdmVycmlkZSBidXRcbiAgICAgICAqIHN0aWxsIGFzc3VtZXMgQXBwS2l0J3MgZGVmYXVsdCBgL2FwaS88bmFtZT5gIG1vdW50LlxuICAgICAgICovXG4gICAgICBnZXRNY3A6ICgpOiB7XG4gICAgICAgIHNlcnZlcklkOiBzdHJpbmc7XG4gICAgICAgIGh0dHBQYXRoOiBzdHJpbmc7XG4gICAgICAgIHNzZVBhdGg6IHN0cmluZztcbiAgICAgICAgbWVzc2FnZVBhdGg6IHN0cmluZztcbiAgICAgICAgaHR0cDogc3RyaW5nO1xuICAgICAgICBzc2U6IHN0cmluZztcbiAgICAgICAgbWVzc2FnZXM6IHN0cmluZztcbiAgICAgIH0gfCBudWxsID0+XG4gICAgICAgIHRoaXMubWNwXG4gICAgICAgICAgPyB7XG4gICAgICAgICAgICAgIHNlcnZlcklkOiB0aGlzLm1jcC5zZXJ2ZXJJZCxcbiAgICAgICAgICAgICAgaHR0cFBhdGg6IHRoaXMubWNwLmh0dHBQYXRoLFxuICAgICAgICAgICAgICBzc2VQYXRoOiB0aGlzLm1jcC5zc2VQYXRoLFxuICAgICAgICAgICAgICBtZXNzYWdlUGF0aDogdGhpcy5tY3AubWVzc2FnZVBhdGgsXG4gICAgICAgICAgICAgIGh0dHA6IGAke3RoaXMuYmFzZVBhdGh9JHt0aGlzLm1jcC5odHRwUGF0aH1gLFxuICAgICAgICAgICAgICBzc2U6IGAke3RoaXMuYmFzZVBhdGh9JHt0aGlzLm1jcC5zc2VQYXRofWAsXG4gICAgICAgICAgICAgIG1lc3NhZ2VzOiBgJHt0aGlzLmJhc2VQYXRofSR7dGhpcy5tY3AubWVzc2FnZVBhdGh9YCxcbiAgICAgICAgICAgIH1cbiAgICAgICAgICA6IG51bGwsXG4gICAgICAvKiogRXhwcmVzcyBzdWJhcHAgTWFzdHJhIGlzIG1vdW50ZWQgb247IG1vc3RseSBmb3IgdGVzdHMuICovXG4gICAgICBnZXRNYXN0cmFTZXJ2ZXI6ICgpID0+IHRoaXMubWFzdHJhU2VydmVyLFxuICAgICAgLyoqXG4gICAgICAgKiBGZXRjaCB0aGUgd29ya3NwYWNlJ3MgTW9kZWwgU2VydmluZyBlbmRwb2ludHMgKGNhY2hlZCkuIFNhbWVcbiAgICAgICAqIHBheWxvYWQgdGhlIGBHRVQgL21vZGVsc2Agcm91dGUgcmV0dXJuczsgc3VyZmFjZWQgaGVyZSBzb1xuICAgICAgICogb3RoZXIgcGx1Z2lucyAvIHNjcmlwdHMgY2FuIGludHJvc3BlY3QgdGhlIGNhdGFsb2d1ZSB3aXRob3V0XG4gICAgICAgKiBhbiBIVFRQIHJvdW5kLXRyaXAuIEFwcEtpdCB3cmFwcyB0aGlzIHdpdGggYGFzVXNlcihyZXEpYCBmb3JcbiAgICAgICAqIE9CTyBzY29waW5nIGF1dG9tYXRpY2FsbHkuIFRocm93cyB3aGVuIHRoZSBsaXN0aW5nIGZhaWxzLCBzaW5jZVxuICAgICAgICogdGhlcmUgaXMgbm8gc3RhdHVzIGNvZGUgdG8gaGFuZCBiYWNrIG9uIHRoaXMgc3VyZmFjZS5cbiAgICAgICAqL1xuICAgICAgbGlzdE1vZGVsczogKCk6IFByb21pc2U8U2VydmluZ0VuZHBvaW50U3VtbWFyeVtdPiA9PiB0aGlzLmxpc3RNb2RlbHMoKSxcbiAgICAgIC8qKlxuICAgICAgICogRm9yY2UtZXZpY3QgY2FjaGVkIGVuZHBvaW50IGxpc3RpbmdzIHZpYSBBcHBLaXQnc1xuICAgICAgICogYENhY2hlTWFuYWdlcmAuIFVzZWZ1bCBpbiB0ZXN0cyBvciByaWdodCBhZnRlciBhbiBhZG1pblxuICAgICAgICogZGVwbG95cyBhIG5ldyBlbmRwb2ludCBhbmQgZG9lc24ndCB3YW50IHRvIHdhaXQgZm9yIHRoZSBUVEwuXG4gICAgICAgKiBSZXR1cm5zIHRoZSB1bmRlcmx5aW5nIGBDYWNoZU1hbmFnZXIuZGVsZXRlYC9gY2xlYXJgIHByb21pc2UuXG4gICAgICAgKi9cbiAgICAgIGNsZWFyTW9kZWxzQ2FjaGU6IChob3N0Pzogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiA9PlxuICAgICAgICBub2RlU2VydmluZy5jbGVhclNlcnZpbmdFbmRwb2ludHNDYWNoZShob3N0KSxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEFic29sdXRlIG1vdW50IHRoaXMgcGx1Z2luJ3Mgcm91dGVzIGFuc3dlciBvbi4gQXBwS2l0IG1vdW50cyBldmVyeSBwbHVnaW5cbiAgICogYXQgYC9hcGkvPHBsdWdpbi5uYW1lPmAgYW5kIGB0aGlzLm5hbWVgIGhvbm9ycyBhIGBjb25maWcubmFtZWAgb3ZlcnJpZGUsXG4gICAqIHNvIHRoZSBwZXItcm91dGUgc2VnbWVudHMgKGByb3V0ZXMuTUFTVFJBX1JPVVRFU2ApIGhhbmcgb2ZmIHRoaXMuXG4gICAqL1xuICBwcml2YXRlIGdldCBiYXNlUGF0aCgpOiBzdHJpbmcge1xuICAgIHJldHVybiBgL2FwaS8ke3RoaXMubmFtZX1gO1xuICB9XG5cbiAgb3ZlcnJpZGUgY2xpZW50Q29uZmlnKCk6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgICAvLyBQdWJsaXNoaW5nIGBiYXNlUGF0aGAgaXMgZW5vdWdoIGZvciB0aGUgY2xpZW50IHRvIHN0YXkgY29ycmVjdCB1bmRlciBhXG4gICAgLy8gY3VzdG9tIG1vdW50IGlkOiB0aGUgcGVyLXJvdXRlIHNlZ21lbnRzIGFyZSBmaXhlZFxuICAgIC8vIChgcm91dGVzLk1BU1RSQV9ST1VURVNgKSBhbmQgdGhlIGNsaWVudCAoYE1hc3RyYVBsdWdpbkNsaWVudGApIGRlcml2ZXNcbiAgICAvLyBldmVyeSBlbmRwb2ludCBmcm9tIGBiYXNlUGF0aGAuXG4gICAgLy8gUmV0dXJuIHdpZGVucyB0byBgUmVjb3JkPHN0cmluZywgdW5rbm93bj5gIHRvIHNhdGlzZnkgdGhlXG4gICAgLy8gYmFzZS1jbGFzcyBzaWduYXR1cmU7IGNvbnN1bWVycyByZWFkIGl0IHRocm91Z2ggdGhlIHR5cGVkXG4gICAgLy8gYE1hc3RyYUNsaWVudENvbmZpZ2Agc2hhcGUgdmlhIGB1c2VQbHVnaW5DbGllbnRDb25maWc8Li4uPiguLi4pYC5cbiAgICBjb25zdCBjb25maWc6IE1hc3RyYUNsaWVudENvbmZpZyA9IHtcbiAgICAgIGJhc2VQYXRoOiB0aGlzLmJhc2VQYXRoLFxuICAgICAgZGVmYXVsdEFnZW50OiB0aGlzLmJ1aWx0Py5kZWZhdWx0QWdlbnRJZCA/PyBGQUxMQkFDS19BR0VOVF9JRCxcbiAgICAgIGFnZW50czogT2JqZWN0LmtleXModGhpcy5idWlsdD8uYWdlbnRzID8/IHt9KSxcbiAgICAgIGZlZWRiYWNrRW5hYmxlZDogdGhpcy5mZWVkYmFja0VuYWJsZWQoKSxcbiAgICB9O1xuICAgIHJldHVybiBjb25maWcgYXMgdW5rbm93biBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgfVxuXG4gIC8qKlxuICAgKiBXaGV0aGVyIHVzZXIgZmVlZGJhY2sgY2FuIGJlIGxvZ2dlZCB0byBNTGZsb3cuIERlbGVnYXRlcyB0b1xuICAgKiB7QGxpbmsgcmVzb2x2ZUZlZWRiYWNrRW5hYmxlZH0gc28gdGhlIGNsaWVudC1jb25maWcgZmxhZyBhbmQgdGhlXG4gICAqIGZlZWRiYWNrIHJvdXRlIHNoYXJlIHRoZSBzYW1lIGdhdGUgYXMgdGhlIHNlcnZlcidzIHRyYWNlLWlkIGhlYWRlci5cbiAgICovXG4gIHByaXZhdGUgZmVlZGJhY2tFbmFibGVkKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiByZXNvbHZlRmVlZGJhY2tFbmFibGVkKHRoaXMuY29uZmlnLmZlZWRiYWNrKTtcbiAgfVxuXG4gIG92ZXJyaWRlIGluamVjdFJvdXRlcyhyb3V0ZXI6IElBcHBSb3V0ZXIpOiB2b2lkIHtcbiAgICAvLyBFeHBvc2UgdGhlIE1DUCB0cmFuc3BvcnQgYXQgdGhlIGNsZWFuIGAvbWNwYCAocGx1cyB0aGUgbGVnYWN5XG4gICAgLy8gYC9zc2VgICsgYC9tZXNzYWdlc2ApIHVuZGVyIHRoZSBwbHVnaW4gbW91bnQuIGBAbWFzdHJhL2V4cHJlc3NgXG4gICAgLy8gbW91bnRzIE1DUCB1bmRlciBgL21jcC88c2VydmVySWQ+Lzx0cmFuc3BvcnQ+YCwgYW5kIHRoZSBzZXJ2ZXJJZFxuICAgIC8vIGRlZmF1bHRzIHRvIHRoZSBwbHVnaW4gbmFtZSwgc28gdGhlIHJhdyByb3V0ZSByZWFkc1xuICAgIC8vIGAvYXBpL21hc3RyYS9tY3AvbWFzdHJhL21jcGAgKGRvdWJsZWQgc2VnbWVudCkuIFRoaXMgcnVucyBiZWZvcmVcbiAgICAvLyB0aGUgY2F0Y2gtYWxsIGFuZCByZXdyaXRlcyB0aGUgYWxpYXMgdG8gdGhlIHVuZGVybHlpbmcgcm91dGUsIHNvXG4gICAgLy8gdGhlIHB1YmxpYyBwYXRoIGlzIGp1c3QgYC9hcGkvPHBsdWdpbj4vbWNwYDsgdGhlIHF1ZXJ5IHN0cmluZ1xuICAgIC8vIChlLmcuIHRoZSBTU0UgYHNlc3Npb25JZGApIGlzIHByZXNlcnZlZC5cbiAgICAvL1xuICAgIC8vIE1pZGRsZXdhcmUgcmF0aGVyIHRoYW4gYHRoaXMucm91dGVgOiBpdCByZXdyaXRlcyB0aGUgVVJMIGZvciByb3V0ZXNcbiAgICAvLyBgQG1hc3RyYS9leHByZXNzYCBvd25zIGZ1cnRoZXIgZG93biB0aGUgY2hhaW4gaW5zdGVhZCBvZiBhbnN3ZXJpbmcgdGhlXG4gICAgLy8gcmVxdWVzdCwgd2hpY2ggYFJvdXRlQ29uZmlnYCBoYXMgbm8gc2hhcGUgZm9yLlxuICAgIHJvdXRlci51c2UoKHJlcSwgX3JlcywgbmV4dCkgPT4ge1xuICAgICAgY29uc3QgdGFyZ2V0ID0gdGhpcy5tY3BSb3V0ZUFsaWFzKHJlcS5wYXRoKTtcbiAgICAgIGlmICh0YXJnZXQpIHtcbiAgICAgICAgY29uc3QgcSA9IHJlcS51cmwuaW5kZXhPZihcIj9cIik7XG4gICAgICAgIHJlcS51cmwgPSBxID49IDAgPyB0YXJnZXQgKyByZXEudXJsLnNsaWNlKHEpIDogdGFyZ2V0O1xuICAgICAgfVxuICAgICAgbmV4dCgpO1xuICAgIH0pO1xuXG4gICAgLy8gYEdFVCAvbW9kZWxzYCBleHBvc2VzIHRoZSBjYWNoZWQgZW5kcG9pbnQgbGlzdCBzbyBjbGllbnRzIGNhblxuICAgIC8vIHBvcHVsYXRlIG1vZGVsIHBpY2tlcnMsIHZhbGlkYXRlIGA/bW9kZWw9YCBjaG9pY2VzLCBldGMuIE11c3RcbiAgICAvLyBiZSByZWdpc3RlcmVkIGJlZm9yZSB0aGUgY2F0Y2gtYWxsIHRoYXQgZm9yd2FyZHMgZXZlcnl0aGluZyB0b1xuICAgIC8vIHRoZSBNYXN0cmEgc3ViYXBwLlxuICAgIHRoaXMucm91dGUocm91dGVyLCB7XG4gICAgICBuYW1lOiBcIm1vZGVsc1wiLFxuICAgICAgbWV0aG9kOiBcImdldFwiLFxuICAgICAgcGF0aDogcm91dGVzLk1BU1RSQV9ST1VURVMubW9kZWxzLFxuICAgICAgaGFuZGxlcjogYXN5bmMgKHJlcSwgcmVzKSA9PiB7XG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMuYXNVc2VyKHJlcSkubGlzdE1vZGVsc1Jlc3VsdCgpO1xuICAgICAgICBpZiAoIXJlc3VsdC5vaykge1xuICAgICAgICAgIHRoaXMuc2VuZEZhaWx1cmUocmVzLCByZXN1bHQsIFwibW9kZWxzXCIsIE1PREVMX0NBVEFMT0dVRV9GQUlMRURfTUVTU0FHRSk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIHJlcy5qc29uKHsgZW5kcG9pbnRzOiByZXN1bHQuZGF0YSB9KTtcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICAvLyBgR0VUIC9kZWZhdWx0LW1vZGVsYCAoYW5kIGAvZGVmYXVsdC1tb2RlbC86YWdlbnRJZGApIHJlcG9ydHMgdGhlIHN0YXRpY1xuICAgIC8vIHNlcnZpbmctZW5kcG9pbnQgYW4gYWdlbnQgZmFsbHMgYmFjayB0byB3aGVuIHRoZSBjbGllbnQgcGlucyBubyBtb2RlbCxcbiAgICAvLyBzbyB0aGUgcGlja2VyIGNhbiBsYWJlbCBpdHMgZGVmYXVsdCBvcHRpb24gd2l0aCB0aGUgbW9kZWwncyBodW1hbml6ZWRcbiAgICAvLyBuYW1lLiBBZ2VudC1zY29wZWQgdmlhIHRoZSBvcHRpb25hbCBgLzphZ2VudElkYCBzdWZmaXggKFVSTCBzeW1tZXRyeVxuICAgIC8vIHdpdGggdGhlIGhpc3RvcnkgLyB0aHJlYWRzIC8gc3VnZ2VzdGlvbnMgcm91dGVzKSwgZGVmYXVsdGluZyB0byB0aGVcbiAgICAvLyBkZWZhdWx0IGFnZW50LiBgbW9kZWxgIC8gYGRpc3BsYXlOYW1lYCBhcmUgbnVsbCB3aGVuIHRoZSBhZ2VudCBoYXMgbm9cbiAgICAvLyBzdGF0aWMgZGVmYXVsdCAoYSBkeW5hbWljLCBjYWxsLXRpbWUgbW9kZWwpOyBhbiB1bmtub3duIGFnZW50IGlkIGlzIGFcbiAgICAvLyA0MDQsIG1hdGNoaW5nIHRoZSBoaXN0b3J5IC8gdGhyZWFkcyByb3V0ZXMuIFJlYWRzIG9ubHkgaW4tbWVtb3J5IGJ1aWxkXG4gICAgLy8gc3RhdGUsIHNvIGl0IG5lZWRzIG5vIE9CTyBzY29waW5nLiBSZWdpc3RlcmVkIGJlZm9yZSB0aGUgY2F0Y2gtYWxsLFxuICAgIC8vIHNhbWUgYXMgYC9tb2RlbHNgLlxuICAgIGNvbnN0IGhhbmRsZURlZmF1bHRNb2RlbCA9IGFzeW5jIChcbiAgICAgIHJlcTogZXhwcmVzcy5SZXF1ZXN0LFxuICAgICAgcmVzOiBleHByZXNzLlJlc3BvbnNlLFxuICAgICk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgICAgY29uc3QgcmVxdWVzdGVkID0gc3RyaW5nLmZpcnN0Tm9uRW1wdHkocmVxLnBhcmFtcy5hZ2VudElkKTtcbiAgICAgIGlmIChyZXF1ZXN0ZWQgIT09IG51bGwgJiYgIXRoaXMuYnVpbHQ/LmFnZW50c1tyZXF1ZXN0ZWRdKSB7XG4gICAgICAgIHJlcy5zdGF0dXMoNDA0KS5qc29uKHsgZXJyb3I6IHRoaXMudW5rbm93bkFnZW50TWVzc2FnZShyZXF1ZXN0ZWQpIH0pO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBjb25zdCBhZ2VudElkID0gcmVxdWVzdGVkID8/IHRoaXMuYnVpbHQ/LmRlZmF1bHRBZ2VudElkID8/IEZBTExCQUNLX0FHRU5UX0lEO1xuICAgICAgY29uc3QgcmF3ID0gdGhpcy5idWlsdD8uZGVmYXVsdE1vZGVsc1thZ2VudElkXTtcbiAgICAgIC8vIGBcIjxkeW5hbWljPlwiYCAoYSBjYWxsLXRpbWUgZnVuY3Rpb24pIGhhcyBubyBmaXhlZCBpZCB0byBhZHZlcnRpc2UuXG4gICAgICBjb25zdCBtb2RlbCA9IHJhdyAmJiByYXcgIT09IFwiPGR5bmFtaWM+XCIgPyByYXcgOiBudWxsO1xuICAgICAgLy8gUmV0dXJuIHRoZSBodW1hbml6ZWQgbGFiZWwgdG9vLCBzbyB0aGUgcGlja2VyIHNob3dzIGEgZnJpZW5kbHkgbmFtZSBvblxuICAgICAgLy8gbG9hZCB3aXRob3V0IHdhaXRpbmcgb24gdGhlIGAvbW9kZWxzYCBjYXRhbG9ndWUgKG5vIHJhdy1pZCBmbGFzaCkuXG4gICAgICByZXMuanNvbih7XG4gICAgICAgIGFnZW50SWQsXG4gICAgICAgIG1vZGVsLFxuICAgICAgICBkaXNwbGF5TmFtZTogbW9kZWwgPyBkaXNwbGF5LnRvTW9kZWxEaXNwbGF5TmFtZShtb2RlbCkgOiBudWxsLFxuICAgICAgfSk7XG4gICAgfTtcbiAgICB0aGlzLnJvdXRlKHJvdXRlciwge1xuICAgICAgbmFtZTogXCJkZWZhdWx0TW9kZWxcIixcbiAgICAgIG1ldGhvZDogXCJnZXRcIixcbiAgICAgIHBhdGg6IHJvdXRlcy5NQVNUUkFfUk9VVEVTLmRlZmF1bHRNb2RlbCxcbiAgICAgIGhhbmRsZXI6IGhhbmRsZURlZmF1bHRNb2RlbCxcbiAgICB9KTtcbiAgICB0aGlzLnJvdXRlKHJvdXRlciwge1xuICAgICAgbmFtZTogXCJkZWZhdWx0TW9kZWxCeUFnZW50XCIsXG4gICAgICBtZXRob2Q6IFwiZ2V0XCIsXG4gICAgICBwYXRoOiBgJHtyb3V0ZXMuTUFTVFJBX1JPVVRFUy5kZWZhdWx0TW9kZWx9LzphZ2VudElkYCxcbiAgICAgIGhhbmRsZXI6IGhhbmRsZURlZmF1bHRNb2RlbCxcbiAgICB9KTtcblxuICAgIC8vIGBHRVQgL2VtYmVkLzp0eXBlLzppZGAgaXMgdGhlIHNpbmdsZSByZXNvbHZlciBmb3IgZXZlcnkgZW1iZWRcbiAgICAvLyBtYXJrZXIgdGhlIGFnZW50IGVtaXRzIGluIHByb3NlIChgW2NoYXJ0OjxpZD5dYCxcbiAgICAvLyBgW2RhdGE6PGlkPl1gLCAuLi4pLiBgOnR5cGVgIHNlbGVjdHMgYSByZXNvbHZlciBmcm9tIHRoZVxuICAgIC8vIHJlZ2lzdHJ5IGJlbG93OyBgOmlkYCBpcyB0aGF0IHJlc29sdmVyJ3MgbG9va3VwIGtleS4gVGhlXG4gICAgLy8gZ3JhbW1hciAoc2VlIGBtYXJrZXIudHNgKSBpcyB0eXBlLWFnbm9zdGljIG9uIHB1cnBvc2UgLSBuZXdcbiAgICAvLyBlbWJlZCBraW5kcyBhcmUgYWRkZWQgYnkgcmVnaXN0ZXJpbmcgYSByZXNvbHZlciBoZXJlLCB3aXRoIG5vXG4gICAgLy8gY2xpZW50IG9yIGdyYW1tYXIgY2hhbmdlLlxuICAgIC8vXG4gICAgLy8gU3RhdHVzIGNvZGVzOlxuICAgIC8vICAgLSAyMDAgd2l0aCB0aGUgcmVzb2x2ZXIncyBKU09OIGJvZHkgd2hlbiB0aGUgaWQgcmVzb2x2ZXMuXG4gICAgLy8gICAtIDQwNCB3aGVuIGA6dHlwZWAgaXNuJ3QgcmVnaXN0ZXJlZCAodW5zdXBwb3J0ZWQgZW1iZWRcbiAgICAvLyAgICAgdHlwZSkgT1IgYSByZWdpc3RlcmVkIHJlc29sdmVyIGNhbid0IGZpbmQgYDppZGAgKHVua25vd25cbiAgICAvLyAgICAgLyBleHBpcmVkIC0gZS5nLiBhIGNoYXJ0IHBhc3QgaXRzIDFoIFRUTCBvciBhIGZhYnJpY2F0ZWRcbiAgICAvLyAgICAgaWQgdGhlIG1vZGVsIG5ldmVyIG1pbnRlZCkuXG4gICAgLy8gICAtIDQwMCB3aGVuIGA6aWRgIGlzIGVtcHR5LlxuICAgIC8vXG4gICAgLy8gUGVyLXR5cGUgcXVlcnkga25vYnMgYW5kIGJlaGF2aW9yOlxuICAgIC8vICAgLSBgY2hhcnRgOiBsb25nLXBvbGxzIHRoZSBjaGFydCBjYWNoZSB1bnRpbCB0aGUgZW50cnlcbiAgICAvLyAgICAgc2V0dGxlcyAoYHJlc3VsdGAgLyBgZXJyb3JgKSBvciB0aGUgYnVkZ2V0IGVsYXBzZXMgKHRoZW5cbiAgICAvLyAgICAgcmV0dXJucyB0aGUgc3RpbGwtcHJvY2Vzc2luZyBlbnRyeSB0byBwb2xsIGFnYWluKS5cbiAgICAvLyAgICAgYD90aW1lb3V0TXM9PG4+YCAoZGVmYXVsdCA2MHMsIGNhcHBlZCA1bWluKSB0dW5lcyBpdC5cbiAgICAvLyAgIC0gYGRhdGFgOiBvbmUgT0JPLXNjb3BlZCBTdGF0ZW1lbnQgRXhlY3V0aW9uIGZldGNoLlxuICAgIC8vICAgICBgP2xpbWl0PTxuPmAgY2FwcyByb3dzIChjbGFtcGVkIHRvIFNUQVRFTUVOVF9ST1dfQ0FQKS5cbiAgICAvL1xuICAgIC8vIEJ1aWx0IG9uY2UgKHRoaXMgaGFuZGxlciBpcyByZWdpc3RlcmVkIG9uY2UpIGFuZCBrZXllZCBieSB0aGVcbiAgICAvLyByYXcgYDp0eXBlYCB0b2tlbi4gRWFjaCByZXNvbHZlciBnZXRzIHRoZSByZXF1ZXN0IChmb3IgcXVlcnlcbiAgICAvLyBwYXJzaW5nICsgT0JPIHNjb3BpbmcpIGFuZCBhbiBgQWJvcnRTaWduYWxgIGJyaWRnZWQgb2ZmIHRoZVxuICAgIC8vIGNvbm5lY3Rpb24gYGNsb3NlYCBldmVudCBzbyBhIGxvbmctcG9sbCB1bmJsb2NrcyB0aGUgaW5zdGFudFxuICAgIC8vIHRoZSBjbGllbnQgZGlzY29ubmVjdHMuIGB1bmRlZmluZWRgIGZyb20gYSByZXNvbHZlciBtYXBzIHRvIGFcbiAgICAvLyBjbGVhbiA0MDQ7IHRocm93biBlcnJvcnMgYnViYmxlIHRocm91Z2ggYG5leHQoZXJyKWAuXG4gICAgY29uc3QgZW1iZWRSZXNvbHZlcnM6IFJlY29yZDxzdHJpbmcsIEVtYmVkUmVzb2x2ZXI+ID0ge1xuICAgICAgY2hhcnQ6IChyZXEsIGlkLCBzaWduYWwpID0+IHtcbiAgICAgICAgY29uc3QgdGltZW91dE1zID0gcGFyc2VUaW1lb3V0TXMocmVxLnF1ZXJ5LnRpbWVvdXRNcyk7XG4gICAgICAgIHJldHVybiB0aGlzLmFzVXNlcihyZXEpLmZldGNoQ2hhcnRFbnRyeShpZCwge1xuICAgICAgICAgIC4uLih0aW1lb3V0TXMgIT09IHVuZGVmaW5lZCA/IHsgdGltZW91dE1zIH0gOiB7fSksXG4gICAgICAgICAgc2lnbmFsLFxuICAgICAgICB9KTtcbiAgICAgIH0sXG4gICAgICBkYXRhOiAocmVxLCBpZCwgc2lnbmFsKSA9PiB7XG4gICAgICAgIGNvbnN0IGxpbWl0ID0gcGFyc2VTdGF0ZW1lbnRMaW1pdChyZXEucXVlcnkubGltaXQpO1xuICAgICAgICByZXR1cm4gdGhpcy5hc1VzZXIocmVxKS5mZXRjaFN0YXRlbWVudChpZCwge1xuICAgICAgICAgIC4uLihsaW1pdCAhPT0gdW5kZWZpbmVkID8geyBsaW1pdCB9IDoge30pLFxuICAgICAgICAgIHNpZ25hbCxcbiAgICAgICAgfSk7XG4gICAgICB9LFxuICAgIH07XG5cbiAgICB0aGlzLnJvdXRlKHJvdXRlciwge1xuICAgICAgbmFtZTogXCJlbWJlZFwiLFxuICAgICAgbWV0aG9kOiBcImdldFwiLFxuICAgICAgcGF0aDogYCR7cm91dGVzLk1BU1RSQV9ST1VURVMuZW1iZWR9Lzp0eXBlLzppZGAsXG4gICAgICBoYW5kbGVyOiBhc3luYyAocmVxLCByZXMpID0+IHtcbiAgICAgICAgY29uc3QgdHlwZSA9IHN0cmluZy5maXJzdE5vbkVtcHR5KHJlcS5wYXJhbXMudHlwZSkgPz8gXCJcIjtcbiAgICAgICAgY29uc3QgaWQgPSBzdHJpbmcuZmlyc3ROb25FbXB0eShyZXEucGFyYW1zLmlkKTtcbiAgICAgICAgY29uc3QgcmVzb2x2ZSA9IGVtYmVkUmVzb2x2ZXJzW3R5cGVdO1xuICAgICAgICBpZiAoIXJlc29sdmUpIHtcbiAgICAgICAgICByZXMuc3RhdHVzKDQwNCkuanNvbih7IGVycm9yOiBgdW5zdXBwb3J0ZWQgZW1iZWQgdHlwZTogJHt0eXBlfWAgfSk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGlmICghaWQpIHtcbiAgICAgICAgICByZXMuc3RhdHVzKDQwMCkuanNvbih7IGVycm9yOiBcImlkIGlzIHJlcXVpcmVkXCIgfSk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIC8vIEV4cHJlc3MncyBgcmVxYCBwcmVkYXRlcyBgQWJvcnRTaWduYWxgOyBicmlkZ2UgdGhlIGBjbG9zZWBcbiAgICAgICAgLy8gZXZlbnQgb250byBhbiBgQWJvcnRDb250cm9sbGVyYCBzbyBhIGNsb3NlZCBjb25uZWN0aW9uXG4gICAgICAgIC8vIHVuYmxvY2tzIGFueSBsb25nLXBvbGwgaW1tZWRpYXRlbHkgYW5kIGZyZWVzIHRoZSByZXF1ZXN0XG4gICAgICAgIC8vIHRocmVhZC4gVGhlIGxpc3RlbmVyIGlzIEdDJ2Qgd2l0aCB0aGUgcmVxdWVzdCBvbiBub3JtYWxcbiAgICAgICAgLy8gY29tcGxldGlvbi5cbiAgICAgICAgY29uc3QgY29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTtcbiAgICAgICAgcmVxLm9uKFwiY2xvc2VcIiwgKCkgPT4gY29udHJvbGxlci5hYm9ydCgpKTtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcmVzb2x2ZShyZXEsIGlkLCBjb250cm9sbGVyLnNpZ25hbCk7XG4gICAgICAgIGlmIChjb250cm9sbGVyLnNpZ25hbC5hYm9ydGVkKSByZXR1cm47XG4gICAgICAgIGlmICghcmVzdWx0Lm9rKSB7XG4gICAgICAgICAgdGhpcy5zZW5kRmFpbHVyZShyZXMsIHJlc3VsdCwgYGVtYmVkOiR7dHlwZX1gLCBgQ291bGQgbm90IHJlc29sdmUgdGhlICR7dHlwZX0gZW1iZWRgKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHJlc3VsdC5kYXRhID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICByZXMuc3RhdHVzKDQwNCkuanNvbih7IGVycm9yOiBgJHt0eXBlfSBub3QgZm91bmRgIH0pO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICByZXMuanNvbihyZXN1bHQuZGF0YSk7XG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgLy8gYEdFVCAvc3VnZ2VzdGlvbnNgIChhbmQgYC9zdWdnZXN0aW9ucy86YWdlbnRJZGApIHJldHVybnMgdGhlXG4gICAgLy8gY3VyYXRlZCBzdGFydGVyIHF1ZXN0aW9ucyBmb3IgdGhlIGFnZW50J3MgR2VuaWUgc3BhY2UocykgLSB0aGVcbiAgICAvLyBhdXRob3ItY29uZmlndXJlZCBgc2FtcGxlX3F1ZXN0aW9uc2AsIHN1cmZhY2VkIGFzIG9uZS10YXBcbiAgICAvLyBwcm9tcHRzIG9uIHRoZSBjaGF0IGVtcHR5IHN0YXRlLiBSZXR1cm5zIGB7IHF1ZXN0aW9uczogW10gfWBcbiAgICAvLyB3aGVuIG5vIEdlbmllIHNwYWNlIGlzIHdpcmVkIHNvIHRoZSBjbGllbnQgcmVuZGVycyBhIGJhcmVcbiAgICAvLyBlbXB0eSBzdGF0ZSAobm8gYnVpbHQtaW4gZXhhbXBsZSBwcm9tcHRzKS4gVGhlIGA6YWdlbnRJZGBcbiAgICAvLyBzZWdtZW50IGlzIGFjY2VwdGVkIGZvciBVUkwgc3ltbWV0cnkgd2l0aCB0aGUgY2hhdCAvIGhpc3RvcnlcbiAgICAvLyByb3V0ZXM7IEdlbmllIHNwYWNlcyBhcmUgcmVzb2x2ZWQgcGVyLXBsdWdpbiwgbm90IHBlci1hZ2VudCxcbiAgICAvLyBzbyBpdCBkb2Vzbid0IGNoYW5nZSB0aGUgcmVzdWx0LiBPQk8tc2NvcGVkIGxpa2UgdGhlIG90aGVyXG4gICAgLy8gZGF0YSByb3V0ZXMgc28gdGhlIHNwYWNlIGxvb2t1cCBydW5zIGFzIHRoZSBjYWxsaW5nIHVzZXIuXG4gICAgY29uc3QgaGFuZGxlU3VnZ2VzdGlvbnMgPSBhc3luYyAoXG4gICAgICByZXE6IGV4cHJlc3MuUmVxdWVzdCxcbiAgICAgIHJlczogZXhwcmVzcy5SZXNwb25zZSxcbiAgICApOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICAgIGNvbnN0IGNvbnRyb2xsZXIgPSBuZXcgQWJvcnRDb250cm9sbGVyKCk7XG4gICAgICByZXEub24oXCJjbG9zZVwiLCAoKSA9PiBjb250cm9sbGVyLmFib3J0KCkpO1xuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5hc1VzZXIocmVxKS5mZXRjaFN1Z2dlc3Rpb25zKGNvbnRyb2xsZXIuc2lnbmFsKTtcbiAgICAgIGlmIChjb250cm9sbGVyLnNpZ25hbC5hYm9ydGVkKSByZXR1cm47XG4gICAgICBpZiAoIXJlc3VsdC5vaykge1xuICAgICAgICAvLyBTdWdnZXN0aW9ucyBhcmUgYSBub24tY3JpdGljYWwgZW5oYW5jZW1lbnQ7IGEgbG9va3VwIGZhaWx1cmUgc2hvdWxkXG4gICAgICAgIC8vIGxlYXZlIHRoZSBjaGF0IHVzYWJsZSB3aXRoIGEgYmFyZSBlbXB0eSBzdGF0ZSByYXRoZXIgdGhhbiBzdXJmYWNpbmdcbiAgICAgICAgLy8gdGhlIHVwc3RyZWFtIHN0YXR1cy4gTG9nIGFuZCBkZWdyYWRlLlxuICAgICAgICB0aGlzLmxvZ2dlci53YXJuKFwic3VnZ2VzdGlvbnM6ZXJyb3JcIiwge1xuICAgICAgICAgIHN0YXR1czogcmVzdWx0LnN0YXR1cyxcbiAgICAgICAgICBlcnJvcjogcmVzdWx0Lm1lc3NhZ2UsXG4gICAgICAgIH0pO1xuICAgICAgICByZXMuanNvbih7IHF1ZXN0aW9uczogW10gfSk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHJlcy5qc29uKHsgcXVlc3Rpb25zOiByZXN1bHQuZGF0YSB9KTtcbiAgICB9O1xuICAgIHRoaXMucm91dGUocm91dGVyLCB7XG4gICAgICBuYW1lOiBcInN1Z2dlc3Rpb25zXCIsXG4gICAgICBtZXRob2Q6IFwiZ2V0XCIsXG4gICAgICBwYXRoOiByb3V0ZXMuTUFTVFJBX1JPVVRFUy5zdWdnZXN0aW9ucyxcbiAgICAgIGhhbmRsZXI6IGhhbmRsZVN1Z2dlc3Rpb25zLFxuICAgIH0pO1xuICAgIHRoaXMucm91dGUocm91dGVyLCB7XG4gICAgICBuYW1lOiBcInN1Z2dlc3Rpb25zQnlBZ2VudFwiLFxuICAgICAgbWV0aG9kOiBcImdldFwiLFxuICAgICAgcGF0aDogYCR7cm91dGVzLk1BU1RSQV9ST1VURVMuc3VnZ2VzdGlvbnN9LzphZ2VudElkYCxcbiAgICAgIGhhbmRsZXI6IGhhbmRsZVN1Z2dlc3Rpb25zLFxuICAgIH0pO1xuXG4gICAgLy8gYFBPU1QgL3JvdXRlL2ZlZWRiYWNrYCBsb2dzIGEgdGh1bWJzIC8gY29tbWVudCBhc3Nlc3NtZW50IGFnYWluc3RcbiAgICAvLyBhIHR1cm4ncyBNTGZsb3cgdHJhY2UgKHRoZSBgdHJhY2VJZGAgdGhlIGNsaWVudCBjYXB0dXJlZCBmcm9tIHRoZVxuICAgIC8vIHN0cmVhbSByZXNwb25zZSdzIHRyYWNlLWlkIGhlYWRlcikuIFJlZ2lzdGVyZWQgb24gdGhlIEFwcEtpdFxuICAgIC8vIHJvdXRlciAobGlrZSBgL21vZGVsc2ApIHJhdGhlciB0aGFuIHRoZSBNYXN0cmEgc3ViYXBwIHNvIGl0IHJ1bnNcbiAgICAvLyB1bmRlciB0aGUgc2FtZSBPQk8gc2NvcGUgLSB0aGUgZmVlZGJhY2sgaXMgYXR0cmlidXRlZCB0byB0aGVcbiAgICAvLyBzaWduZWQtaW4gdXNlci4gUmV0dXJucyA0MDQgd2hlbiBmZWVkYmFjayBpcyBkaXNhYmxlZCBzbyB0aGVcbiAgICAvLyBjbGllbnQgdHJlYXRzIHRoZSBjYXBhYmlsaXR5IGFzIGFic2VudDsgNDAwIG9uIGEgbWFsZm9ybWVkIGJvZHkuXG4gICAgLy8gQSByZWNvcmRlZCBhc3Nlc3NtZW50IHlpZWxkcyBgeyBvazogdHJ1ZSB9YDsgYSBzb2Z0IGZhaWx1cmUgKG1vc3RcbiAgICAvLyBvZnRlbiB0aGUgdHJhY2UgaGFzbid0IGZpbmlzaGVkIGV4cG9ydGluZyB0byBNTGZsb3cgeWV0KSB5aWVsZHNcbiAgICAvLyBgeyBvazogZmFsc2UgfWAgd2l0aG91dCBhIDV4eCBzbyB0aGUgVUkgY2FuIHByb21wdCBhIHJldHJ5LlxuICAgIHRoaXMucm91dGUocm91dGVyLCB7XG4gICAgICBuYW1lOiBcImZlZWRiYWNrXCIsXG4gICAgICBtZXRob2Q6IFwicG9zdFwiLFxuICAgICAgcGF0aDogcm91dGVzLk1BU1RSQV9ST1VURVMuZmVlZGJhY2ssXG4gICAgICBoYW5kbGVyOiBhc3luYyAocmVxLCByZXMpID0+IHtcbiAgICAgICAgaWYgKCF0aGlzLmZlZWRiYWNrRW5hYmxlZCgpKSB7XG4gICAgICAgICAgcmVzLnN0YXR1cyg0MDQpLmpzb24oeyBvazogZmFsc2UgfSk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHBhcnNlZCA9IGZlZWRiYWNrLk1hc3RyYUZlZWRiYWNrUmVxdWVzdFNjaGVtYS5zYWZlUGFyc2UocmVxLmJvZHkpO1xuICAgICAgICBpZiAoIXBhcnNlZC5zdWNjZXNzKSB7XG4gICAgICAgICAgLy8gQSBab2QgaXNzdWUgc3RyaW5nIHF1b3RlcyB0aGUgcmVjZWl2ZWQgYm9keSBiYWNrLCBzbyBvbmx5IHRoZVxuICAgICAgICAgIC8vIG9mZmVuZGluZyBmaWVsZCBuYW1lcyB0cmF2ZWw7IHRoZSBmdWxsIGlzc3VlIGxpc3QgaXMgbG9nZ2VkLlxuICAgICAgICAgIHRoaXMubG9nZ2VyLndhcm4oXCJmZWVkYmFjazppbnZhbGlkXCIsIHsgZXJyb3I6IHBhcnNlZC5lcnJvci5tZXNzYWdlIH0pO1xuICAgICAgICAgIHJlcy5zdGF0dXMoNDAwKS5qc29uKHtcbiAgICAgICAgICAgIG9rOiBmYWxzZSxcbiAgICAgICAgICAgIGVycm9yOiBJTlZBTElEX0ZFRURCQUNLX01FU1NBR0UsXG4gICAgICAgICAgICBmaWVsZHM6IGludmFsaWRGaWVsZHMocGFyc2VkLmVycm9yKSxcbiAgICAgICAgICB9KTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5hc1VzZXIocmVxKS5sb2dGZWVkYmFjayhwYXJzZWQuZGF0YSk7XG4gICAgICAgIGlmICghcmVzdWx0Lm9rKSB7XG4gICAgICAgICAgdGhpcy5zZW5kRmFpbHVyZShyZXMsIHJlc3VsdCwgXCJmZWVkYmFja1wiLCBcIkZlZWRiYWNrIGNvdWxkIG5vdCBiZSByZWNvcmRlZFwiKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgYXNzZXNzbWVudElkID0gcmVzdWx0LmRhdGE7XG4gICAgICAgIHJlcy5qc29uKHtcbiAgICAgICAgICBvazogYXNzZXNzbWVudElkICE9PSB1bmRlZmluZWQsXG4gICAgICAgICAgLi4uKGFzc2Vzc21lbnRJZCA/IHsgYXNzZXNzbWVudElkIH0gOiB7fSksXG4gICAgICAgIH0pO1xuICAgICAgfSxcbiAgICB9KTtcblxuICAgIC8vIE1pZGRsZXdhcmUgcmF0aGVyIHRoYW4gYHRoaXMucm91dGVgOiB0aGlzIGlzIHRoZSBjYXRjaC1hbGwgdGhhdCBoYW5kc1xuICAgIC8vIGV2ZXJ5IHJlbWFpbmluZyBtZXRob2QgLyBwYXRoIHBhaXIgdG8gdGhlIE1hc3RyYSBzdWItYXBwLCBzbyBpdCBoYXMgbm9cbiAgICAvLyBzaW5nbGUgbWV0aG9kIG9yIHBhdGggdG8gcmVnaXN0ZXIgdW5kZXIuXG4gICAgcm91dGVyLnVzZSgocmVxLCByZXMsIG5leHQpID0+IHtcbiAgICAgIGlmICghdGhpcy5tYXN0cmFBcHApIHJldHVybiByZXMuc3RhdHVzKDUwMykuZW5kKCk7XG4gICAgICAvLyBHYXRlIHRoZSBzdG9jayBNYXN0cmEgc3VyZmFjZSBiZWZvcmUgZGlzcGF0Y2guIEluIHRoZSBkZWZhdWx0XG4gICAgICAvLyBcInNjb3BlZFwiIG1vZGUgb25seSBhZ2VudCBpbmZlcmVuY2UsIHJlYWQtb25seSBhZ2VudCBtZXRhZGF0YSwgdGhpc1xuICAgICAgLy8gcGx1Z2luJ3Mgb3duIGAvcm91dGUvKmAgcm91dGVzLCBhbmQgKHdoZW4gZW5hYmxlZCkgTUNQIHJlYWNoIE1hc3RyYTtcbiAgICAgIC8vIGFkbWluIC8gbXV0YXRpbmcgLyBidWxrLWV4cG9ydCByb3V0ZXMgYXJlIHJlZnVzZWQgaGVyZS4gYHJlcS5wYXRoYFxuICAgICAgLy8gaXMgbW91bnQtcmVsYXRpdmUgdW5kZXIgdGhlIHBsdWdpbiBtb3VudC4gU2VlIGBzZXJ2ZXIudHNgLlxuICAgICAgaWYgKFxuICAgICAgICAhaXNNYXN0cmFSZXF1ZXN0QWxsb3dlZChyZXEubWV0aG9kLCByZXEucGF0aCwge1xuICAgICAgICAgIGFjY2VzczogdGhpcy5jb25maWcuYXBpQWNjZXNzID8/IFwic2NvcGVkXCIsXG4gICAgICAgICAgLy8gUmVmbGVjdCB0aGUgKnJlc29sdmVkKiBNQ1Agc3RhdGUsIG5vdCByYXcgYGNvbmZpZy5tY3BgOiBNQ1AgaXNcbiAgICAgICAgICAvLyBvbiBieSBkZWZhdWx0IChgY29uZmlnLm1jcGAgdW5kZWZpbmVkKSwgc28gZ2F0ZSBvbiB3aGV0aGVyIHRoZVxuICAgICAgICAgIC8vIHNlcnZlciB3YXMgYWN0dWFsbHkgYnVpbHQgYW5kIG1vdW50ZWQuXG4gICAgICAgICAgbWNwRW5hYmxlZDogdGhpcy5tY3AgIT09IG51bGwsXG4gICAgICAgIH0pXG4gICAgICApIHtcbiAgICAgICAgcmVzLnN0YXR1cyg0MDMpLmpzb24oeyBlcnJvcjogXCJFbmRwb2ludCBub3QgZXhwb3NlZCB0byB0aGUgY2xpZW50IChhcGlBY2Nlc3M9c2NvcGVkKVwiIH0pO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICAvLyBEaXNwYXRjaCB0aHJvdWdoIGEgcmVhbCBtZXRob2QsIE5PVCB0aGUgYG1hc3RyYUFwcGAgcHJvcGVydHkuIFRoZVxuICAgICAgLy8gQXBwS2l0IGBhc1VzZXIocmVxKWAgcHJveHkgd3JhcHMgZnVuY3Rpb24tdmFsdWVkIHByb3BzIHdpdGhcbiAgICAgIC8vIGB2YWx1ZS5iaW5kKHRhcmdldClgLiBgbWFzdHJhQXBwYCBpcyBhbiBleHByZXNzIGFwcCB3aG9zZSBgLmJpbmRgIGlzXG4gICAgICAvLyB0aGUgSFRUUCBCSU5EIHJvdXRlIHJlZ2lzdHJhciAoZXhwcmVzcyBkZWZpbmVzIGEgbWV0aG9kIHBlciBIVFRQIHZlcmIsXG4gICAgICAvLyBhbmQgQklORCBpcyBvbmUpLCBub3QgYEZ1bmN0aW9uLnByb3RvdHlwZS5iaW5kYCAtIHNvIGJpbmRpbmcgaXQgdGhyb3VnaFxuICAgICAgLy8gdGhlIHByb3h5IHJlZ2lzdGVycyBhIGJvZ3VzIHJvdXRlIGFuZCBjcmFzaGVzIGBwYXRoVG9SZWdleHBgXG4gICAgICAvLyAoXCJwYXRoIG11c3QgYmUgYSBzdHJpbmcgLi4uXCIpLiBUaGlzIG9ubHkgbWFuaWZlc3RzIGluIHByb2R1Y3Rpb24gd2hlcmVcbiAgICAgIC8vIGFuIE9CTyB0b2tlbiBtYWtlcyBgdXNlclNjb3BlZFNlbGZgIHJldHVybiB0aGUgcHJveHkuIGBkaXNwYXRjaE1hc3RyYWBcbiAgICAgIC8vIGlzIGEgcGxhaW4gbWV0aG9kIChpdHMgYC5iaW5kYCBpcyB0aGUgbm9ybWFsIG9uZSkgYW5kIGludm9rZXNcbiAgICAgIC8vIGB0aGlzLm1hc3RyYUFwcGAgb2ZmIHRoZSByZWFsIHRhcmdldCwga2VlcGluZyB0aGUgT0JPIHNjb3BlIGFjdGl2ZS5cbiAgICAgIHJldHVybiB0aGlzLmFzVXNlcihyZXEpLmRpc3BhdGNoTWFzdHJhKHJlcSwgcmVzLCBuZXh0KTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBNYXAgYSBmYWlsZWQge0BsaW5rIEV4ZWN1dGlvblJlc3VsdH0gb250byB0aGUgcmVzcG9uc2U6IHRoZSBzdGF0dXMgdGhlXG4gICAqIGV4ZWN1dGlvbiBwaXBlbGluZSByZXNvbHZlZCwgcGFpcmVkIHdpdGggYGNsaWVudE1lc3NhZ2VgIHJhdGhlciB0aGFuIHRoZVxuICAgKiByZXN1bHQncyBvd24gdGV4dCwgd2hpY2ggY2FuIGNhcnJ5IHVwc3RyZWFtIGRldGFpbC4gVGhlIGZ1bGwgbWVzc2FnZSBpc1xuICAgKiBsb2dnZWQgdW5kZXIgYDxvcGVyYXRpb24+OmVycm9yYC5cbiAgICovXG4gIHByaXZhdGUgc2VuZEZhaWx1cmUoXG4gICAgcmVzOiBleHByZXNzLlJlc3BvbnNlLFxuICAgIHJlc3VsdDogeyBzdGF0dXM6IG51bWJlcjsgbWVzc2FnZTogc3RyaW5nIH0sXG4gICAgb3BlcmF0aW9uOiBzdHJpbmcsXG4gICAgY2xpZW50TWVzc2FnZTogc3RyaW5nLFxuICApOiB2b2lkIHtcbiAgICB0aGlzLmxvZ2dlci53YXJuKGAke29wZXJhdGlvbn06ZXJyb3JgLCB7XG4gICAgICBzdGF0dXM6IHJlc3VsdC5zdGF0dXMsXG4gICAgICBlcnJvcjogcmVzdWx0Lm1lc3NhZ2UsXG4gICAgfSk7XG4gICAgaWYgKHJlcy5oZWFkZXJzU2VudCkgcmV0dXJuO1xuICAgIHJlcy5zdGF0dXMocmVzdWx0LnN0YXR1cykuanNvbih7IGVycm9yOiBjbGllbnRNZXNzYWdlIH0pO1xuICB9XG5cbiAgLyoqIDQwNCBib2R5IGZvciBhIHJlcXVlc3QgbmFtaW5nIGFuIGFnZW50IHRoYXQgaXNuJ3QgcmVnaXN0ZXJlZC4gKi9cbiAgcHJpdmF0ZSB1bmtub3duQWdlbnRNZXNzYWdlKGFnZW50SWQ6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgY29uc3QgcmVnaXN0ZXJlZCA9IE9iamVjdC5rZXlzKHRoaXMuYnVpbHQ/LmFnZW50cyA/PyB7fSk7XG4gICAgcmV0dXJuIGBVbmtub3duIGFnZW50IFwiJHthZ2VudElkfVwiLiBSZWdpc3RlcmVkIGFnZW50czogJHtyZWdpc3RlcmVkLmpvaW4oXCIsIFwiKSB8fCBcIm5vbmVcIn1gO1xuICB9XG5cbiAgLyoqXG4gICAqIEludm9rZSB0aGUgTWFzdHJhIGV4cHJlc3Mgc3ViLWFwcC4gRXhpc3RzIGFzIGEgbWV0aG9kIChpbnN0ZWFkIG9mIHJlYWRpbmdcbiAgICogYHRoaXMubWFzdHJhQXBwYCB0aHJvdWdoIHRoZSBgYXNVc2VyKHJlcSlgIHByb3h5IGF0IHRoZSBjYWxsIHNpdGUpIHNvIHRoZVxuICAgKiBwcm94eSBiaW5kcyB0aGlzIHBsYWluIG1ldGhvZCAtIHdob3NlIGAuYmluZGAgaXMgYEZ1bmN0aW9uLnByb3RvdHlwZS5iaW5kYFxuICAgKiAtIHJhdGhlciB0aGFuIHRoZSBleHByZXNzIGFwcCwgd2hvc2UgYC5iaW5kYCBpcyB0aGUgSFRUUCBCSU5EIHJvdXRlXG4gICAqIHJlZ2lzdHJhciAoc2VlIHRoZSBub3RlIGluIGBpbmplY3RSb3V0ZXNgKS4gUnVucyBpbnNpZGUgdGhlIHVzZXIgc2NvcGUgc29cbiAgICogYGdldEV4ZWN1dGlvbkNvbnRleHQoKWAgcmV0dXJucyB0aGUgT0JPIGNsaWVudCBmb3IgdGhlIGFnZW50L21vZGVsXG4gICAqIHJlc29sdmVycy5cbiAgICovXG4gIHByaXZhdGUgZGlzcGF0Y2hNYXN0cmEoXG4gICAgcmVxOiBleHByZXNzLlJlcXVlc3QsXG4gICAgcmVzOiBleHByZXNzLlJlc3BvbnNlLFxuICAgIG5leHQ6IGV4cHJlc3MuTmV4dEZ1bmN0aW9uLFxuICApOiB2b2lkIHtcbiAgICB0aGlzLm1hc3RyYUFwcCEocmVxLCByZXMsIG5leHQpO1xuICB9XG5cbiAgLyoqXG4gICAqIE1hcCBhIGNsZWFuLCBtb3VudC1yZWxhdGl2ZSBNQ1AgYWxpYXMgcGF0aCB0byB0aGUgdW5kZXJseWluZ1xuICAgKiBgQG1hc3RyYS9leHByZXNzYCByb3V0ZS4gUmV0dXJucyBgbnVsbGAgd2hlbiBNQ1AgaXMgb2ZmIG9yIHRoZSBwYXRoXG4gICAqIGlzbid0IGFuIGFsaWFzLiBDb2xsYXBzZXMgdGhlIHN0b2NrIGAvbWNwLzxzZXJ2ZXJJZD4vPHRyYW5zcG9ydD5gXG4gICAqIGxheW91dCAoc2VydmVySWQgZGVmYXVsdHMgdG8gdGhlIHBsdWdpbiBuYW1lKSBkb3duIHRvIGAvbWNwYCxcbiAgICogYC9zc2VgLCBhbmQgYC9tZXNzYWdlc2AuXG4gICAqL1xuICBwcml2YXRlIG1jcFJvdXRlQWxpYXMocGF0aDogc3RyaW5nKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgaWYgKCF0aGlzLm1jcCkgcmV0dXJuIG51bGw7XG4gICAgY29uc3QgaWQgPSB0aGlzLm1jcC5zZXJ2ZXJJZDtcbiAgICBpZiAocGF0aCA9PT0gXCIvbWNwXCIpIHJldHVybiBgL21jcC8ke2lkfS9tY3BgO1xuICAgIGlmIChwYXRoID09PSBcIi9zc2VcIikgcmV0dXJuIGAvbWNwLyR7aWR9L3NzZWA7XG4gICAgaWYgKHBhdGggPT09IFwiL21lc3NhZ2VzXCIpIHJldHVybiBgL21jcC8ke2lkfS9tZXNzYWdlc2A7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICAvKipcbiAgICogSW1wbGVtZW50YXRpb24gYmFja2luZyB0aGUgYC9zdWdnZXN0aW9uc2Agcm91dGUuIFJ1bnMgaW5zaWRlIHRoZVxuICAgKiBBcHBLaXQgdXNlci1jb250ZXh0IHByb3h5IHNvIGBnZXRFeGVjdXRpb25Db250ZXh0KClgIHJldHVybnMgdGhlXG4gICAqIE9CTy1zY29wZWQgY2xpZW50LiBSZXNvbHZlcyB0aGUgcGx1Z2luJ3MgR2VuaWUgc3BhY2VzIGFuZCBtZXJnZXNcbiAgICogdGhlaXIgY3VyYXRlZCBgc2FtcGxlX3F1ZXN0aW9uc2AgKHNlZSB7QGxpbmsgY29sbGVjdFNwYWNlU3VnZ2VzdGlvbnN9KS5cbiAgICogUmV0dXJucyBgW11gIHdoZW4gbm8gR2VuaWUgc3BhY2UgaXMgY29uZmlndXJlZCBzbyB0aGUgY2xpZW50XG4gICAqIHNob3dzIGEgYmFyZSBlbXB0eSBzdGF0ZSBpbnN0ZWFkIG9mIGJ1aWx0LWluIGV4YW1wbGUgcHJvbXB0cy5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZmV0Y2hTdWdnZXN0aW9ucyhzaWduYWw/OiBBYm9ydFNpZ25hbCk6IFByb21pc2U8RXhlY3V0aW9uUmVzdWx0PHN0cmluZ1tdPj4ge1xuICAgIGNvbnN0IHNwYWNlcyA9IHJlc29sdmVHZW5pZVNwYWNlcyh0aGlzLmNvbmZpZywgdGhpcy5jb250ZXh0KTtcbiAgICBpZiAoT2JqZWN0LmtleXMoc3BhY2VzKS5sZW5ndGggPT09IDApIHJldHVybiB7IG9rOiB0cnVlLCBkYXRhOiBbXSB9O1xuICAgIGNvbnN0IGNsaWVudCA9IGdldEV4ZWN1dGlvbkNvbnRleHQoKS5jbGllbnQ7XG4gICAgcmV0dXJuIHRoaXMuZXhlY3V0ZSgoZXhlY3V0ZVNpZ25hbCkgPT4ge1xuICAgICAgY29uc3QgY29tYmluZWQgPSBhc3luYy5jb21iaW5lQWJvcnRTaWduYWxzKHNpZ25hbCwgZXhlY3V0ZVNpZ25hbCk7XG4gICAgICByZXR1cm4gY29sbGVjdFNwYWNlU3VnZ2VzdGlvbnMoe1xuICAgICAgICBzcGFjZXMsXG4gICAgICAgIGNsaWVudCxcbiAgICAgICAgLi4uKGNvbWJpbmVkID8geyBzaWduYWw6IGNvbWJpbmVkIH0gOiB7fSksXG4gICAgICB9KTtcbiAgICB9LCBnZW5pZVN1Z2dlc3Rpb25EZWZhdWx0cyk7XG4gIH1cblxuICAvKipcbiAgICogSW1wbGVtZW50YXRpb24gYmFja2luZyB0aGUgYC9yb3V0ZS9mZWVkYmFja2Agcm91dGUuIFJ1bnMgaW5zaWRlIHRoZVxuICAgKiBBcHBLaXQgdXNlci1jb250ZXh0IHByb3h5IHNvIGBnZXRFeGVjdXRpb25Db250ZXh0KClgIHJldHVybnMgdGhlXG4gICAqIE9CTy1zY29wZWQgY2xpZW50IGFuZCB0aGUgYXNzZXNzbWVudCBpcyBhdHRyaWJ1dGVkIHRvIHRoZSBzaWduZWQtaW5cbiAgICogdXNlciAodGhlaXIgZW1haWwgLyBpZCBhcyB0aGUgYXNzZXNzbWVudCBzb3VyY2UpLiBSZXR1cm5zIHRoZVxuICAgKiBjcmVhdGVkIGFzc2Vzc21lbnQgaWQgb24gc3VjY2Vzcywgb3IgYHVuZGVmaW5lZGAgb24gYSBzb2Z0IGZhaWx1cmVcbiAgICogKHNlZSB7QGxpbmsgbG9nRmVlZGJhY2t9IGluIGAuL21sZmxvdy5qc2ApLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBsb2dGZWVkYmFjayhcbiAgICBmZWVkYmFjazogTWFzdHJhRmVlZGJhY2tSZXF1ZXN0LFxuICApOiBQcm9taXNlPEV4ZWN1dGlvblJlc3VsdDxzdHJpbmcgfCB1bmRlZmluZWQ+PiB7XG4gICAgY29uc3QgY3R4ID0gZ2V0RXhlY3V0aW9uQ29udGV4dCgpO1xuICAgIGNvbnN0IHNvdXJjZUlkID1cbiAgICAgIFwidXNlckVtYWlsXCIgaW4gY3R4ICYmIGN0eC51c2VyRW1haWxcbiAgICAgICAgPyBjdHgudXNlckVtYWlsXG4gICAgICAgIDogXCJ1c2VySWRcIiBpbiBjdHhcbiAgICAgICAgICA/IGN0eC51c2VySWRcbiAgICAgICAgICA6IGN0eC5zZXJ2aWNlVXNlcklkO1xuICAgIHJldHVybiB0aGlzLmV4ZWN1dGUoXG4gICAgICAoKSA9PlxuICAgICAgICBsb2dGZWVkYmFjayhjdHguY2xpZW50LCB7XG4gICAgICAgICAgLi4uZmVlZGJhY2ssXG4gICAgICAgICAgLi4uKHNvdXJjZUlkID8geyBzb3VyY2VJZCB9IDoge30pLFxuICAgICAgICB9KSxcbiAgICAgIGZlZWRiYWNrV3JpdGVEZWZhdWx0cyxcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEltcGxlbWVudGF0aW9uIGJhY2tpbmcgdGhlIGBkYXRhYCBlbWJlZCByZXNvbHZlclxuICAgKiAoYEdFVCAvZW1iZWQvZGF0YS86aWRgKS4gUnVucyBpbnNpZGUgdGhlIEFwcEtpdCB1c2VyLWNvbnRleHQgcHJveHkgc29cbiAgICogYGdldEV4ZWN1dGlvbkNvbnRleHQoKWAgcmV0dXJucyB0aGUgT0JPLXNjb3BlZCB3b3Jrc3BhY2VcbiAgICogY2xpZW50LCB0aGVuIHJldXNlcyB0aGUgc2FtZSBgZmV0Y2hTdGF0ZW1lbnREYXRhYCBwaXBlbGluZVxuICAgKiB0aGUgYGdldF9zdGF0ZW1lbnRgIHRvb2wgcnVucyBzbyB0aGUgTExNIGFuZCB0aGUgVUkgc2VlIHRoZVxuICAgKiBleGFjdCBzYW1lIHNoYXBlIGZvciB0aGUgc2FtZSBzdGF0ZW1lbnQuXG4gICAqXG4gICAqIFJlc29sdmVzIHRvIGB1bmRlZmluZWRgIGRhdGEgZm9yIHVwc3RyZWFtIDQwNHMgc28gdGhlIHJvdXRlIGNhbiBtYXAgdGhlbVxuICAgKiB0byBhIGNsZWFuIEhUVFAgNDA0OyBhbnkgb3RoZXIgZmFpbHVyZSBjb21lcyBiYWNrIGFzIGEgbm9uLWBva2AgcmVzdWx0XG4gICAqIGNhcnJ5aW5nIHRoZSBzdGF0dXMgdG8gYW5zd2VyIHdpdGguXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGZldGNoU3RhdGVtZW50KFxuICAgIHN0YXRlbWVudElkOiBzdHJpbmcsXG4gICAgb3B0aW9uczogeyBsaW1pdD86IG51bWJlcjsgc2lnbmFsPzogQWJvcnRTaWduYWwgfSA9IHt9LFxuICApOiBQcm9taXNlPEV4ZWN1dGlvblJlc3VsdDxTdGF0ZW1lbnREYXRhIHwgdW5kZWZpbmVkPj4ge1xuICAgIGNvbnN0IGNsaWVudCA9IGdldEV4ZWN1dGlvbkNvbnRleHQoKS5jbGllbnQ7XG4gICAgY29uc3QgbGltaXQgPSBNYXRoLm1pbihvcHRpb25zLmxpbWl0ID8/IFNUQVRFTUVOVF9ST1dfQ0FQLCBTVEFURU1FTlRfUk9XX0NBUCk7XG4gICAgcmV0dXJuIHRoaXMuZXhlY3V0ZShcbiAgICAgIGFzeW5jIChleGVjdXRlU2lnbmFsKSA9PiB7XG4gICAgICAgIGNvbnN0IGNvbWJpbmVkID0gYXN5bmMuY29tYmluZUFib3J0U2lnbmFscyhvcHRpb25zLnNpZ25hbCwgZXhlY3V0ZVNpZ25hbCk7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgZGF0YSA9IGF3YWl0IGZldGNoU3RhdGVtZW50RGF0YShjbGllbnQsIHN0YXRlbWVudElkLCB7XG4gICAgICAgICAgICBsaW1pdCxcbiAgICAgICAgICAgIC4uLihjb21iaW5lZCA/IHsgc2lnbmFsOiBjb21iaW5lZCB9IDoge30pLFxuICAgICAgICAgIH0pO1xuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBjb2x1bW5zOiBkYXRhLmNvbHVtbnMsXG4gICAgICAgICAgICByb3dzOiBkYXRhLnJvd3MsXG4gICAgICAgICAgICByb3dDb3VudDogZGF0YS5yb3dDb3VudCxcbiAgICAgICAgICAgIHRydW5jYXRlZDogZGF0YS5yb3dzLmxlbmd0aCA8IGRhdGEucm93Q291bnQsXG4gICAgICAgICAgfTtcbiAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgLy8gVGhlIERhdGFicmlja3MgU0RLIHRocm93cyBvbiA0MDQ7IHN1cmZhY2UgYXMgYHVuZGVmaW5lZGBcbiAgICAgICAgICAvLyBzbyB0aGUgcm91dGUgbWFwcyB0byBhIGNsZWFuIEhUVFAgNDA0IGluc3RlYWQgb2YgYSA1MDAuXG4gICAgICAgICAgaWYgKGVycm9yLmVycm9yQ29udGV4dChlcnIpLm5vdEFjY2Vzc2libGUpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgICAgICAgdGhyb3cgZXJyO1xuICAgICAgICB9XG4gICAgICB9LFxuICAgICAge1xuICAgICAgICBkZWZhdWx0OiB7XG4gICAgICAgICAgLi4uc3RhdGVtZW50RGF0YURlZmF1bHRzLmRlZmF1bHQsXG4gICAgICAgICAgLy8gTmFtZXNwYWNlZCBzbyB0aGUga2V5IGNhbid0IGNvbGxpZGUgd2l0aCBhbm90aGVyIGNhY2hlIHNoYXJpbmcgdGhlXG4gICAgICAgICAgLy8gbWFuYWdlciwgYW5kIGtleWVkIG9uIGBsaW1pdGAgYmVjYXVzZSB0aGUgc2xpY2UgaXMgcGFydCBvZiB0aGVcbiAgICAgICAgICAvLyBwYXlsb2FkLlxuICAgICAgICAgIGNhY2hlOiB7XG4gICAgICAgICAgICAuLi5zdGF0ZW1lbnREYXRhRGVmYXVsdHMuZGVmYXVsdC5jYWNoZSxcbiAgICAgICAgICAgIGNhY2hlS2V5OiBbU1RBVEVNRU5UX0NBQ0hFX05BTUVTUEFDRSwgc3RhdGVtZW50SWQsIGxpbWl0XSxcbiAgICAgICAgICB9LFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEltcGxlbWVudGF0aW9uIGJhY2tpbmcgdGhlIGBjaGFydGAgZW1iZWQgcmVzb2x2ZXJcbiAgICogKGBHRVQgL2VtYmVkL2NoYXJ0LzppZGApLiBSdW5zIGluc2lkZSB0aGUgQXBwS2l0IHVzZXItY29udGV4dCBwcm94eSBzbyB0aGVcbiAgICogbG9va3VwIGlzIG5hbWVzcGFjZWQgdG8gdGhlIGNhbGxpbmcgaWRlbnRpdHk6IGEgY2hhcnQgbWludGVkIGZvciBhbm90aGVyXG4gICAqIHVzZXIgaXMgaW5kaXN0aW5ndWlzaGFibGUgZnJvbSBhbiB1bmtub3duIGlkLCBhbmQgdGhlIHJvdXRlIGFuc3dlcnMgNDA0LlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBmZXRjaENoYXJ0RW50cnkoXG4gICAgY2hhcnRJZDogc3RyaW5nLFxuICAgIG9wdGlvbnM6IHsgdGltZW91dE1zPzogbnVtYmVyOyBzaWduYWw6IEFib3J0U2lnbmFsIH0sXG4gICk6IFByb21pc2U8RXhlY3V0aW9uUmVzdWx0PENoYXJ0IHwgdW5kZWZpbmVkPj4ge1xuICAgIGNvbnN0IHVzZXJLZXkgPSByZXNvbHZlVXNlcktleSgpO1xuICAgIHJldHVybiB0aGlzLmV4ZWN1dGUoXG4gICAgICAoZXhlY3V0ZVNpZ25hbCkgPT5cbiAgICAgICAgZmV0Y2hDaGFydChjaGFydElkLCB7XG4gICAgICAgICAgdXNlcktleSxcbiAgICAgICAgICAuLi4ob3B0aW9ucy50aW1lb3V0TXMgIT09IHVuZGVmaW5lZCA/IHsgdGltZW91dE1zOiBvcHRpb25zLnRpbWVvdXRNcyB9IDoge30pLFxuICAgICAgICAgIHNpZ25hbDogYXN5bmMuY29tYmluZUFib3J0U2lnbmFscyhvcHRpb25zLnNpZ25hbCwgZXhlY3V0ZVNpZ25hbCkgPz8gb3B0aW9ucy5zaWduYWwsXG4gICAgICAgIH0pLFxuICAgICAgY2hhcnRGZXRjaERlZmF1bHRzLFxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogSW1wbGVtZW50YXRpb24gYmFja2luZyB0aGUgYC9tb2RlbHNgIHJvdXRlLiBSdW5zIGluc2lkZSB0aGUgQXBwS2l0XG4gICAqIHVzZXItY29udGV4dCBwcm94eSBzbyBgZ2V0RXhlY3V0aW9uQ29udGV4dCgpYCByZXR1cm5zIHRoZSBPQk8tc2NvcGVkXG4gICAqIGNsaWVudCBhbmQgdGhlIGNhdGFsb2d1ZSByZWZsZWN0cyB3aGF0IHRoZSBjYWxsZXIgY2FuIGludm9rZS5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgbGlzdE1vZGVsc1Jlc3VsdCgpOiBQcm9taXNlPEV4ZWN1dGlvblJlc3VsdDxTZXJ2aW5nRW5kcG9pbnRTdW1tYXJ5W10+PiB7XG4gICAgcmV0dXJuIHRoaXMuZXhlY3V0ZShhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBjbGllbnQgPSBnZXRFeGVjdXRpb25Db250ZXh0KCkuY2xpZW50O1xuICAgICAgY29uc3QgaG9zdCA9IChhd2FpdCBjbGllbnQuY29uZmlnLmdldEhvc3QoKSkudG9TdHJpbmcoKTtcbiAgICAgIGNvbnN0IHNlcnZpbmcgPSByZXNvbHZlU2VydmluZ0NvbmZpZyh0aGlzLmNvbmZpZyk7XG4gICAgICByZXR1cm4gbm9kZVNlcnZpbmcubGlzdFNlcnZpbmdFbmRwb2ludHMoY2xpZW50LCBob3N0LCB7IHR0bE1zOiBzZXJ2aW5nLnR0bE1zIH0pO1xuICAgIH0sIG1vZGVsQ2F0YWxvZ3VlRGVmYXVsdHMpO1xuICB9XG5cbiAgLyoqXG4gICAqIEltcGxlbWVudGF0aW9uIGJhY2tpbmcgdGhlIGBsaXN0TW9kZWxzYCBleHBvcnQuIFRoZSBwcm9ncmFtbWF0aWMgc3VyZmFjZVxuICAgKiBjYXJyaWVzIG5vIHN0YXR1cyBjb2RlLCBzbyBhIGZhaWxlZCBleGVjdXRpb24gYmVjb21lcyBhIHRocm93IHdpdGggYVxuICAgKiBzdGFibGUgbWVzc2FnZTsgdGhlIHJvdXRlcyBicmFuY2ggb24gdGhlIHtAbGluayBFeGVjdXRpb25SZXN1bHR9IGluc3RlYWQuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGxpc3RNb2RlbHMoKTogUHJvbWlzZTxTZXJ2aW5nRW5kcG9pbnRTdW1tYXJ5W10+IHtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB0aGlzLmxpc3RNb2RlbHNSZXN1bHQoKTtcbiAgICBpZiAocmVzdWx0Lm9rKSByZXR1cm4gcmVzdWx0LmRhdGE7XG4gICAgdGhpcy5sb2dnZXIud2FybihcImxpc3RNb2RlbHM6ZXJyb3JcIiwge1xuICAgICAgc3RhdHVzOiByZXN1bHQuc3RhdHVzLFxuICAgICAgZXJyb3I6IHJlc3VsdC5tZXNzYWdlLFxuICAgIH0pO1xuICAgIHRocm93IG5ldyBFeGVjdXRpb25FcnJvcihNT0RFTF9DQVRBTE9HVUVfRkFJTEVEX01FU1NBR0UpO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBidWlsZEFnZW50QW5kU2VydmVyKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIC8vIFBlci1hZ2VudCBtZW1vcnkgZmFjdG9yeS4gV2hlbiBhbnkgc3RvcmFnZSAvIG1lbW9yeSBzZXR0aW5nIG5lZWRzXG4gICAgLy8gUG9zdGdyZXMsIHN0YW5kIHVwIGEgZGVkaWNhdGVkIHNlcnZpY2UtcHJpbmNpcGFsIHBvb2wgZmlyc3Qgc29cbiAgICAvLyBtZW1vcnkgYWN0cyBhcyB0aGUgYXBwIFNQIChvd25lciBvZiB0aGUgYG1hc3RyYV8qYCBzY2hlbWFzKSxcbiAgICAvLyBuZXZlciB0aGUgcGVyLXJlcXVlc3QgT0JPIGlkZW50aXR5IHRoZSBjaGF0IHR1cm4gcnVucyB1bmRlci5cbiAgICAvLyBgZ2V0UGdDb25maWcoKWAgaXMgcmVhZCBoZXJlLCBvdXRzaWRlIGFueSBgYXNVc2VyYCBzY29wZSwgc28gaXRcbiAgICAvLyByZXR1cm5zIHRoZSBTUCBjb25uZWN0aW9uIHRhcmdldCArIHRva2VuIHJlZnJlc2ggcGx1cyBhbnlcbiAgICAvLyBgbGFrZWJhc2UoeyBwb29sIH0pYCBvdmVycmlkZXM7IGByZXF1aXJlYCB0dXJucyBhIG1pc3NpbmdcbiAgICAvLyBzaWJsaW5nIGludG8gYSBjbGVhciB3aXJpbmcgZXJyb3IuIFRoZSBidWlsZGVyIGNhY2hlcyB0aGUgc2hhcmVkXG4gICAgLy8gYFBnVmVjdG9yYCBzaW5nbGV0b24gc28gcmVnaXN0ZXJpbmcgTiBhZ2VudHMgc3RheXMgY2hlYXAuIFNlZVxuICAgIC8vIGAuL21lbW9yeS5qc2AuXG4gICAgaWYgKG5lZWRzTGFrZWJhc2UodGhpcy5jb25maWcpKSB7XG4gICAgICBjb25zdCBzcFBnQ29uZmlnID0gcGx1Z2luXG4gICAgICAgIC5yZXF1aXJlKHRoaXMuY29udGV4dCwgbGFrZWJhc2UsIHRoaXMuY29uZmlnKVxuICAgICAgICAuZXhwb3J0cygpXG4gICAgICAgIC5nZXRQZ0NvbmZpZygpO1xuICAgICAgdGhpcy5zZXJ2aWNlUHJpbmNpcGFsUG9vbCA9IGF3YWl0IGNyZWF0ZVNlcnZpY2VQcmluY2lwYWxQb29sKHNwUGdDb25maWcpO1xuICAgIH1cbiAgICBjb25zdCBtZW1vcnlCdWlsZGVyID0gdGhpcy5zZXJ2aWNlUHJpbmNpcGFsUG9vbFxuICAgICAgPyBjcmVhdGVNZW1vcnlCdWlsZGVyKHRoaXMuY29uZmlnLCB0aGlzLnNlcnZpY2VQcmluY2lwYWxQb29sKVxuICAgICAgOiB1bmRlZmluZWQ7XG5cbiAgICAvLyBCdWlsZCBldmVyeSBhZ2VudCBkZWNsYXJlZCBpbiBgY29uZmlnLmFnZW50c2AgKG9yIHRoZSBidWlsdC1pblxuICAgIC8vIGZhbGxiYWNrIHdoZW4gbm9uZSBhcmUgZGVjbGFyZWQpLiBFYWNoIGFnZW50J3MgYG1vZGVsYCByZXNvbHZlc1xuICAgIC8vIHdvcmtzcGFjZSBVUkwgKyBiZWFyZXIgYXQgY2FsbCB0aW1lIHNvIGNvbmN1cnJlbnQgcmVxdWVzdHMgZ2V0XG4gICAgLy8gZGlzdGluY3QgdXNlciBpZGVudGl0aWVzOyB0aGUgYGFzVXNlcihyZXEpYCBzY29wZSBhcm91bmRcbiAgICAvLyBgaGFuZGxlQ2hhdGAgaXMgd2hhdCBsZXRzIGBnZXRFeGVjdXRpb25Db250ZXh0KClgIHJldHVybiB0aGVcbiAgICAvLyByaWdodCB1c2VyIGluc2lkZSB0aGUgcmVzb2x2ZXIuXG4gICAgdGhpcy5idWlsdCA9IGF3YWl0IGJ1aWxkQWdlbnRzKHtcbiAgICAgIGNvbmZpZzogdGhpcy5jb25maWcsXG4gICAgICBjb250ZXh0OiB0aGlzLmNvbnRleHQsXG4gICAgICBtZW1vcnlCdWlsZGVyLFxuICAgICAgbG9nOiB0aGlzLmxvZ2dlcixcbiAgICB9KTtcblxuICAgIC8vIGBtYXN0cmEuc2VydmVyLmFwaVJvdXRlc2AgaXMgb25seSBob25vcmVkIGJ5IE1hc3RyYSdzIHN0YW5kYWxvbmVcbiAgICAvLyBkZXYgc2VydmVyLiBTaW5jZSB3ZSdyZSBob3N0aW5nIE1hc3RyYSBpbnNpZGUgb3VyIG93biBFeHByZXNzXG4gICAgLy8gc3ViYXBwIHZpYSBgQG1hc3RyYS9leHByZXNzYCwgY3VzdG9tIHJvdXRlcyBtdXN0IGJlIHBhc3NlZCB0b1xuICAgIC8vIHRoZSBgTWFzdHJhU2VydmVyYCBjb25zdHJ1Y3RvciBkaXJlY3RseS5cbiAgICAvL1xuICAgIC8vIGBzdG9yYWdlYCBoZXJlIGlzICpNYXN0cmEtaW5zdGFuY2UtbGV2ZWwqIGFuZCBwZXJzaXN0cyB3b3JrZmxvd1xuICAgIC8vIHNuYXBzaG90cyAod2hlcmUgc3VzcGVuZGVkIGByZXF1aXJlQXBwcm92YWxgIHRvb2wgY2FsbHMgbGl2ZSkuXG4gICAgLy8gSXQncyBzZXBhcmF0ZSBmcm9tIGVhY2ggYWdlbnQncyBgTWVtb3J5LnN0b3JhZ2VgLCB3aGljaCBvbmx5XG4gICAgLy8gY292ZXJzIHRocmVhZCAvIG1lc3NhZ2UgaGlzdG9yeS4gV2l0aG91dCBpdCxcbiAgICAvLyBgYWdlbnQucmVzdW1lU3RyZWFtKClgIGVycm9ycyB3aXRoIFwiY291bGQgbm90IGZpbmQgYSBzdXNwZW5kZWRcbiAgICAvLyBydW5cIiBhbmQgdGhlIGFwcHJvdmFsIFVJIGhhbmdzIGFmdGVyIHRoZSB1c2VyIGNsaWNrcyBBcHByb3ZlLlxuICAgIGNvbnN0IGluc3RhbmNlU3RvcmFnZSA9IG1lbW9yeUJ1aWxkZXI/Lmluc3RhbmNlU3RvcmFnZSgpO1xuICAgIC8vIFdpcmUgTWFzdHJhJ3MgdHJhY2VyIGludG8gQXBwS2l0J3MgZ2xvYmFsIE9UZWwgcGlwZWxpbmUgdmlhXG4gICAgLy8gYEBtYXN0cmEvb3RlbC1icmlkZ2VgLiBNYXN0cmEgc3BhbnMgYmVjb21lIG5hdGl2ZSBPVGVsIHNwYW5zIG9uXG4gICAgLy8gd2hhdGV2ZXIgdHJhY2VyIHByb3ZpZGVyIGBUZWxlbWV0cnlNYW5hZ2VyYCByZWdpc3RlcmVkIGR1cmluZ1xuICAgIC8vIGBjcmVhdGVBcHBgLCBzbyB0aGUgT1RMUCBlbmRwb2ludCAvIGhlYWRlcnMgLyBzYW1wbGluZyBhcmVcbiAgICAvLyBlbnYtZHJpdmVuIGFuZCBzaGFyZWQgd2l0aCBldmVyeSBvdGhlciBBcHBLaXQgcGx1Z2luLlxuICAgIGNvbnN0IG9ic2VydmFiaWxpdHkgPSBhd2FpdCBidWlsZE9ic2VydmFiaWxpdHkoe1xuICAgICAgc2VydmljZU5hbWU6IHRoaXMubmFtZSxcbiAgICAgIGVuYWJsZWQ6IHRoaXMuY29uZmlnLm9ic2VydmFiaWxpdHksXG4gICAgfSk7XG4gICAgLy8gT3B0aW9uYWwgTUNQIGV4cG9zdXJlOiBidWlsZCBhIE1hc3RyYSBNQ1Agc2VydmVyIGZyb20gdGhlXG4gICAgLy8gcmVnaXN0ZXJlZCBhZ2VudHMgKGFuZCwgb3B0LWluLCB0aGUgYW1iaWVudCB0b29scykgYW5kIHJlZ2lzdGVyXG4gICAgLy8gaXQgb24gdGhlIE1hc3RyYSBpbnN0YW5jZS4gYEBtYXN0cmEvZXhwcmVzc2Agc2VydmVzIHRoZSBzdG9jayBNQ1BcbiAgICAvLyB0cmFuc3BvcnQgcm91dGVzIChgL21jcC88c2VydmVySWQ+Ly4uLmApIG9mZiBgbWNwU2VydmVyc2AsIHNvIHRoZVxuICAgIC8vIGNhdGNoLWFsbCBkaXNwYXRjaCBiZWxvdyBhbHJlYWR5IHJvdXRlcyBNQ1AgcmVxdWVzdHMgdW5kZXIgT0JPIC1cbiAgICAvLyBubyBiZXNwb2tlIHJvdXRlIG5lZWRlZC4gU2VlIGAuL21jcC5qc2AuXG4gICAgdGhpcy5tY3AgPSBidWlsZE1jcFNlcnZlcih7XG4gICAgICBjb25maWc6IHRoaXMuY29uZmlnLFxuICAgICAgcGx1Z2luTmFtZTogdGhpcy5uYW1lLFxuICAgICAgZGlzcGxheU5hbWU6IE1hc3RyYVBsdWdpbi5tYW5pZmVzdC5kaXNwbGF5TmFtZSxcbiAgICAgIGFnZW50czogdGhpcy5idWlsdC5hZ2VudHMsXG4gICAgICBhbWJpZW50VG9vbHM6IHRoaXMuYnVpbHQuYW1iaWVudFRvb2xzLFxuICAgIH0pO1xuICAgIHRoaXMubWFzdHJhID0gbmV3IE1hc3RyYSh7XG4gICAgICBhZ2VudHM6IHRoaXMuYnVpbHQuYWdlbnRzLFxuICAgICAgLi4uKGluc3RhbmNlU3RvcmFnZSA/IHsgc3RvcmFnZTogaW5zdGFuY2VTdG9yYWdlIH0gOiB7fSksXG4gICAgICAuLi4ob2JzZXJ2YWJpbGl0eSA/IHsgb2JzZXJ2YWJpbGl0eSB9IDoge30pLFxuICAgICAgLi4uKHRoaXMubWNwID8geyBtY3BTZXJ2ZXJzOiB7IFt0aGlzLm1jcC5zZXJ2ZXJJZF06IHRoaXMubWNwLnNlcnZlciB9IH0gOiB7fSksXG4gICAgfSk7XG4gICAgdGhpcy5tYXN0cmFBcHAgPSBleHByZXNzKCk7XG4gICAgYXR0YWNoUm91dGVQYXRjaE1pZGRsZXdhcmUodGhpcy5tYXN0cmFBcHApO1xuICAgIHRoaXMubWFzdHJhU2VydmVyID0gbmV3IE1hc3RyYVNlcnZlcih0aGlzLmNvbmZpZywge1xuICAgICAgYXBwOiB0aGlzLm1hc3RyYUFwcCxcbiAgICAgIG1hc3RyYTogdGhpcy5tYXN0cmEsXG4gICAgICBwcmVmaXg6IFwiXCIsXG4gICAgICBjdXN0b21BcGlSb3V0ZXM6IFtcbiAgICAgICAgLy8gYGhpc3RvcnlSb3V0ZWAgcmVnaXN0ZXJzIGJvdGggR0VUIChsb2FkKSBhbmQgREVMRVRFXG4gICAgICAgIC8vIChjbGVhcikgb24gdGhlIHNhbWUgcGF0aCwgc28gaXQgcmV0dXJucyBhbiBhcnJheSB3ZVxuICAgICAgICAvLyBzcGxpY2UgaW4uXG4gICAgICAgIC4uLmhpc3RvcnlSb3V0ZSh7XG4gICAgICAgICAgcGF0aDogcm91dGVzLk1BU1RSQV9ST1VURVMuaGlzdG9yeSxcbiAgICAgICAgICBhZ2VudDogdGhpcy5idWlsdC5kZWZhdWx0QWdlbnRJZCxcbiAgICAgICAgfSksXG4gICAgICAgIC8vIEFzc2VydCB0aGUgYDphZ2VudElkYCB0ZW1wbGF0ZSB0eXBlOiB0aGUgcGVyLXBhY2thZ2UgYnVpbGQnc1xuICAgICAgICAvLyBOb2RlTmV4dCByZXNvbHV0aW9uIHdpZGVucyB0aGUgaW1wb3J0ZWQgYHJvdXRlcy5NQVNUUkFfUk9VVEVTLmhpc3RvcnlgXG4gICAgICAgIC8vIHRvIGBzdHJpbmdgICh0aGUgc291cmNlL2J1bmRsZXIgdHlwZWNoZWNrIGtlZXBzIGl0IGEgbGl0ZXJhbCksXG4gICAgICAgIC8vIHdoaWNoIHdvdWxkIG90aGVyd2lzZSBkcm9wIHRoaXMgb3V0IG9mIHRoZSBkeW5hbWljLWFnZW50XG4gICAgICAgIC8vIG92ZXJsb2FkIGFuZCBkZW1hbmQgYSBmaXhlZCBgYWdlbnRgLlxuICAgICAgICAuLi5oaXN0b3J5Um91dGUoe1xuICAgICAgICAgIHBhdGg6IGAke3JvdXRlcy5NQVNUUkFfUk9VVEVTLmhpc3Rvcnl9LzphZ2VudElkYCBhcyBgJHtzdHJpbmd9OmFnZW50SWRgLFxuICAgICAgICB9KSxcbiAgICAgICAgLy8gYHRocmVhZHNSb3V0ZWAgcmVnaXN0ZXJzIEdFVCAobGlzdCB0aGUgY2FsbGVyJ3MgY29udmVyc2F0aW9uXG4gICAgICAgIC8vIHRocmVhZHMpIGFuZCBERUxFVEUgKHJlbW92ZSB0aGUgdGFyZ2V0ZWQgdGhyZWFkKSBvbiB0aGUgc2FtZVxuICAgICAgICAvLyBwYXRoOyBib3RoIHRoZSBkZWZhdWx0LWFnZW50IGFuZCBkeW5hbWljLWFnZW50IG1vdW50cyBhcmVcbiAgICAgICAgLy8gc3BsaWNlZCBpbiwgbWlycm9yaW5nIHRoZSBoaXN0b3J5IHJvdXRlcyBhYm92ZS5cbiAgICAgICAgLi4udGhyZWFkc1JvdXRlKHtcbiAgICAgICAgICBwYXRoOiByb3V0ZXMuTUFTVFJBX1JPVVRFUy50aHJlYWRzLFxuICAgICAgICAgIGFnZW50OiB0aGlzLmJ1aWx0LmRlZmF1bHRBZ2VudElkLFxuICAgICAgICB9KSxcbiAgICAgICAgLi4udGhyZWFkc1JvdXRlKHtcbiAgICAgICAgICBwYXRoOiBgJHtyb3V0ZXMuTUFTVFJBX1JPVVRFUy50aHJlYWRzfS86YWdlbnRJZGAgYXMgYCR7c3RyaW5nfTphZ2VudElkYCxcbiAgICAgICAgfSksXG4gICAgICBdLFxuICAgIH0pO1xuICAgIGF3YWl0IHRoaXMubWFzdHJhU2VydmVyLmluaXQoKTtcbiAgICB0aGlzLmxvZ2dlci5pbmZvKFwicmVhZHlcIiwge1xuICAgICAgYWdlbnRzOiBPYmplY3Qua2V5cyh0aGlzLmJ1aWx0LmFnZW50cyksXG4gICAgICBkZWZhdWx0QWdlbnQ6IHRoaXMuYnVpbHQuZGVmYXVsdEFnZW50SWQsXG4gICAgICBhcGlBY2Nlc3M6IHRoaXMuY29uZmlnLmFwaUFjY2VzcyA/PyBcInNjb3BlZFwiLFxuICAgICAgbWNwOiB0aGlzLm1jcCA/IGAke3RoaXMuYmFzZVBhdGh9JHt0aGlzLm1jcC5odHRwUGF0aH1gIDogXCJvZmZcIixcbiAgICAgIGxha2ViYXNlOiBtZW1vcnlCdWlsZGVyICE9PSB1bmRlZmluZWQsXG4gICAgICBmZWVkYmFjazogdGhpcy5mZWVkYmFja0VuYWJsZWQoKSxcbiAgICAgIG9ic2VydmFiaWxpdHk6IG9ic2VydmFiaWxpdHkgIT09IHVuZGVmaW5lZCA/IFwibWxmbG93XCIgOiBcIm9mZlwiLFxuICAgIH0pO1xuICB9XG59XG5cbi8qKlxuICogUmVzb2x2ZXIgZm9yIG9uZSBlbWJlZCBgPHR5cGU+YCBiZWhpbmQgdGhlIGdlbmVyaWNcbiAqIGBHRVQgL2VtYmVkLzp0eXBlLzppZGAgcm91dGUuIFJldHVybnMgdGhlIEpTT04gYm9keSB0byBzZW5kIG9uXG4gKiBzdWNjZXNzLCBvciBgdW5kZWZpbmVkYCB0byBzaWduYWwgYSA0MDQgKHVua25vd24gLyBleHBpcmVkIGlkKS5cbiAqIGBzaWduYWxgIGFib3J0cyB3aGVuIHRoZSBjbGllbnQgZGlzY29ubmVjdHMgc28gbG9uZy1wb2xsaW5nXG4gKiByZXNvbHZlcnMgKGUuZy4gYGNoYXJ0YCkgdW5ibG9jayBpbW1lZGlhdGVseS5cbiAqL1xudHlwZSBFbWJlZFJlc29sdmVyID0gKFxuICByZXE6IGV4cHJlc3MuUmVxdWVzdCxcbiAgaWQ6IHN0cmluZyxcbiAgc2lnbmFsOiBBYm9ydFNpZ25hbCxcbikgPT4gUHJvbWlzZTxFeGVjdXRpb25SZXN1bHQ8dW5rbm93bj4+O1xuXG4vKipcbiAqIFBhcnNlIHRoZSBvcHRpb25hbCBgP3RpbWVvdXRNcz08bj5gIHF1ZXJ5IHBhcmFtZXRlciBmcm9tIGFcbiAqIGBHRVQgL2VtYmVkL2NoYXJ0LzppZGAgcmVxdWVzdC4gQWNjZXB0cyBhIHBvc2l0aXZlIGludGVnZXIgdXBcbiAqIHRvIHtAbGluayBNQVhfRU1CRURfUE9MTF9USU1FT1VUX01TfSAoY2xhbXBlZCkgYW5kIHJlamVjdHMgZXZlcnl0aGluZyBlbHNlXG4gKiBhcyBgdW5kZWZpbmVkYCBzbyB7QGxpbmsgZmV0Y2hDaGFydH0gZmFsbHMgYmFjayB0byBpdHMgZGVmYXVsdC5cbiAqIEV4cHJlc3MgcHJvZHVjZXMgYHN0cmluZyB8IHN0cmluZ1tdIHwgdW5kZWZpbmVkYDsgd2Ugbm9ybWFsaXplXG4gKiB0byB0aGUgZmlyc3Qgc2NhbGFyIGJlZm9yZSBwYXJzaW5nLlxuICovXG5mdW5jdGlvbiBwYXJzZVRpbWVvdXRNcyhyYXc6IHVua25vd24pOiBudW1iZXIgfCB1bmRlZmluZWQge1xuICBjb25zdCB2ID0gQXJyYXkuaXNBcnJheShyYXcpID8gcmF3WzBdIDogcmF3O1xuICBpZiAodHlwZW9mIHYgIT09IFwic3RyaW5nXCIpIHJldHVybiB1bmRlZmluZWQ7XG4gIGNvbnN0IG4gPSBOdW1iZXIodik7XG4gIGlmICghTnVtYmVyLmlzRmluaXRlKG4pIHx8IG4gPD0gMCkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgcmV0dXJuIE1hdGgubWluKE1hdGguZmxvb3IobiksIE1BWF9FTUJFRF9QT0xMX1RJTUVPVVRfTVMpO1xufVxuXG4vKipcbiAqIFBhcnNlIHRoZSBvcHRpb25hbCBgP2xpbWl0PTxuPmAgcXVlcnkgcGFyYW1ldGVyIGZyb20gYVxuICogYEdFVCAvZW1iZWQvZGF0YS86aWRgIHJlcXVlc3QuIEFjY2VwdHMgYSBub24tbmVnYXRpdmVcbiAqIGludGVnZXIgYW5kIGxldHMgdGhlIHJvdXRlIGNsYW1wIHRvIGBTVEFURU1FTlRfUk9XX0NBUGA7XG4gKiByZWplY3RzIGFueXRoaW5nIGVsc2UgYXMgYHVuZGVmaW5lZGAgc28gdGhlIHJvdXRlIGZhbGxzIGJhY2tcbiAqIHRvIHRoZSBzZXJ2ZXItc2lkZSBjYXAuXG4gKi9cbmZ1bmN0aW9uIHBhcnNlU3RhdGVtZW50TGltaXQocmF3OiB1bmtub3duKTogbnVtYmVyIHwgdW5kZWZpbmVkIHtcbiAgY29uc3QgdiA9IEFycmF5LmlzQXJyYXkocmF3KSA/IHJhd1swXSA6IHJhdztcbiAgaWYgKHR5cGVvZiB2ICE9PSBcInN0cmluZ1wiKSByZXR1cm4gdW5kZWZpbmVkO1xuICBjb25zdCBuID0gTnVtYmVyKHYpO1xuICBpZiAoIU51bWJlci5pc0Zpbml0ZShuKSB8fCBuIDwgMCkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgcmV0dXJuIE1hdGguZmxvb3Iobik7XG59XG5cbi8qKlxuICogUmVnaXN0ZXIgdGhlIE1hc3RyYSBwbHVnaW4gb24gYW4gQXBwS2l0IGFwcC4gTW91bnRzIHRoZSBhZ2VudHMsIHRoZVxuICogYC9yb3V0ZS8qYCBjaGF0IHN1cmZhY2UsIGFuZCAodW5sZXNzIGRpc2FibGVkKSB0aGUgTUNQIHRyYW5zcG9ydCB1bmRlclxuICogYC9hcGkvbWFzdHJhYC5cbiAqXG4gKiBAZXhhbXBsZSBNaW5pbWFsIGFwcFxuICogYGBgdHNcbiAqIGltcG9ydCB7IGNyZWF0ZUFwcCB9IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXRcIjtcbiAqIGltcG9ydCB7IG1hc3RyYSB9IGZyb20gXCJAZGJ4LXRvb2xzL2FwcGtpdC1tYXN0cmFcIjtcbiAqXG4gKiAvLyBObyBgYWdlbnRzYDogYSBzaW5nbGUgYnVpbHQtaW4gYGRlZmF1bHRgIGFuYWx5c3QgaXMgcmVnaXN0ZXJlZCwgc28gdGhlXG4gKiAvLyBjaGF0IGVuZHBvaW50IHdvcmtzIG91dCBvZiB0aGUgYm94LlxuICogY29uc3QgYXBwID0gYXdhaXQgY3JlYXRlQXBwKHsgcGx1Z2luczogW21hc3RyYSgpXSB9KTtcbiAqIGBgYFxuICpcbiAqIEBleGFtcGxlIFR3byBhZ2VudHMsIGEgcGlubmVkIG1vZGVsLCBhbmQgR2VuaWVcbiAqIGBgYHRzXG4gKiBpbXBvcnQgeyBjcmVhdGVBcHAsIGxha2ViYXNlIH0gZnJvbSBcIkBkYXRhYnJpY2tzL2FwcGtpdFwiO1xuICogaW1wb3J0IHsgY3JlYXRlQWdlbnQsIG1hc3RyYSB9IGZyb20gXCJAZGJ4LXRvb2xzL2FwcGtpdC1tYXN0cmFcIjtcbiAqXG4gKiBjb25zdCBhcHAgPSBhd2FpdCBjcmVhdGVBcHAoe1xuICogICBwbHVnaW5zOiBbXG4gKiAgICAgbGFrZWJhc2UoKSxcbiAqICAgICBtYXN0cmEoe1xuICogICAgICAgZGVmYXVsdE1vZGVsOiBcImRhdGFicmlja3MtY2xhdWRlLXNvbm5ldC00LTZcIixcbiAqICAgICAgIGRlZmF1bHRBZ2VudDogXCJhbmFseXN0XCIsXG4gKiAgICAgICBnZW5pZVNwYWNlczogeyBzYWxlczogeyBzcGFjZUlkOiBcIjAxZWYuLi5cIiwgaGludDogXCJvcmRlcnMsIHJldHVybnNcIiB9IH0sXG4gKiAgICAgICBhZ2VudHM6IHtcbiAqICAgICAgICAgYW5hbHlzdDogY3JlYXRlQWdlbnQoe1xuICogICAgICAgICAgIGluc3RydWN0aW9uczogXCJZb3UgYW5zd2VyIHF1ZXN0aW9ucyBhYm91dCBzYWxlcy5cIixcbiAqICAgICAgICAgICB0b29scyhwbHVnaW5zKSB7XG4gKiAgICAgICAgICAgICByZXR1cm4geyAuLi5wbHVnaW5zLmdlbmllPy50b29sa2l0KCkgfTtcbiAqICAgICAgICAgICB9LFxuICogICAgICAgICB9KSxcbiAqICAgICAgICAgaGVscGVyOiBjcmVhdGVBZ2VudCh7IGluc3RydWN0aW9uczogXCJZb3UgZXhwbGFpbiB0aGUgYW5hbHlzdCdzIGFuc3dlcnMuXCIgfSksXG4gKiAgICAgICB9LFxuICogICAgIH0pLFxuICogICBdLFxuICogfSk7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGNvbnN0IG1hc3RyYSA9IHRvUGx1Z2luKE1hc3RyYVBsdWdpbik7XG4iXX0=