@dbx-tools/appkit-mastra 0.9.58 → 0.9.61

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.
@@ -13,16 +13,40 @@
13
13
  *
14
14
  * There is no MLflow JS SDK, so this posts to the assessments REST
15
15
  * endpoint directly using the OBO-scoped workspace client (the feedback
16
- * is thus attributed to the signed-in user). Trace export is
17
- * asynchronous, so the just-finished trace may not exist in MLflow yet
18
- * when the user reacts; the log call retries briefly on "not found"
19
- * before giving up softly.
16
+ * is thus attributed to the signed-in user). UC-backed Apps traces need
17
+ * the experiment's table-prefix URI (`trace:/{catalog}.{schema}.{prefix}/{tr-hex}`)
18
+ * rather than the experiment-store id the UI does not store. That prefix is
19
+ * read from the experiment's `mlflow.experiment.databricksTraceDestinationPath`
20
+ * tag (or the spans-table tag); `MLFLOW_UC_TRACE_PREFIX` remains an optional
21
+ * override. Trace export is asynchronous, so the just-finished trace may not
22
+ * exist in MLflow yet when the user reacts; the log call retries briefly on
23
+ * "not found" before giving up softly.
20
24
  *
21
25
  * @module
22
26
  */
23
27
  import { appkit } from "@dbx-tools/appkit";
24
28
  /** Workspace client carried on an AppKit execution context. */
25
29
  type WorkspaceClient = appkit.WorkspaceClientLike;
30
+ /**
31
+ * UC table prefix the assessments API needs, read from experiment tags.
32
+ *
33
+ * Prefers the three-part destination path Databricks writes when the
34
+ * experiment is linked to a table-prefix location. Falls back to stripping
35
+ * `_otel_spans` from the spans-table tag. Two-part schema-linked destinations
36
+ * are ignored (they are not a table prefix).
37
+ */
38
+ export declare function ucTracePrefixFromExperimentTags(rawTags: unknown): string | undefined;
39
+ /** Drop the in-process UC prefix cache (tests). */
40
+ export declare function resetUcTracePrefixCache(): void;
41
+ /**
42
+ * Trace id the assessments REST API accepts for this deployment.
43
+ *
44
+ * Databricks Apps persist traces in Unity Catalog. The experiment UI lists
45
+ * those rows, but `POST /api/3.0/mlflow/traces/{id}/assessments` looks up the
46
+ * experiment store unless `{id}` is the UC URI `trace:/{catalog}.{schema}.{prefix}/{tr-hex}`.
47
+ * A bare `tr-<hex>` 404s and the assessment never appears.
48
+ */
49
+ export declare function mlflowAssessmentTraceId(traceId: string, prefix?: string): string;
26
50
  /**
27
51
  * Whether MLflow feedback logging is available for this deployment.
28
52
  *
@@ -41,6 +65,18 @@ export declare function mlflowEnabled(): boolean;
41
65
  * the two never disagree.
42
66
  */
43
67
  export declare function resolveFeedbackEnabled(explicit: boolean | undefined): boolean;
68
+ /**
69
+ * Reject an explicitly enabled feedback surface when no MLflow experiment is
70
+ * configured. Auto mode remains non-fatal and simply disables feedback.
71
+ */
72
+ export declare function validateFeedbackConfig(explicit: boolean | undefined): void;
73
+ /**
74
+ * Return the configured experiment URL only when the active Databricks
75
+ * principal has effective `CAN_MANAGE` permission. Permission lookup is
76
+ * intentionally best-effort so Apps environments that do not expose the
77
+ * required APIs simply omit the debug affordance.
78
+ */
79
+ export declare function mlflowExperimentManagerUrl(client: WorkspaceClient): Promise<string | undefined>;
44
80
  /** Parameters for {@link logFeedback}. */
45
81
  export interface LogFeedbackParams {
46
82
  /** MLflow trace id the assessment attaches to (`tr-<hex>`). */
package/lib/src/mlflow.js CHANGED
@@ -13,23 +13,184 @@
13
13
  *
14
14
  * There is no MLflow JS SDK, so this posts to the assessments REST
15
15
  * endpoint directly using the OBO-scoped workspace client (the feedback
16
- * is thus attributed to the signed-in user). Trace export is
17
- * asynchronous, so the just-finished trace may not exist in MLflow yet
18
- * when the user reacts; the log call retries briefly on "not found"
19
- * before giving up softly.
16
+ * is thus attributed to the signed-in user). UC-backed Apps traces need
17
+ * the experiment's table-prefix URI (`trace:/{catalog}.{schema}.{prefix}/{tr-hex}`)
18
+ * rather than the experiment-store id the UI does not store. That prefix is
19
+ * read from the experiment's `mlflow.experiment.databricksTraceDestinationPath`
20
+ * tag (or the spans-table tag); `MLFLOW_UC_TRACE_PREFIX` remains an optional
21
+ * override. Trace export is asynchronous, so the just-finished trace may not
22
+ * exist in MLflow yet when the user reacts; the log call retries briefly on
23
+ * "not found" before giving up softly.
20
24
  *
21
25
  * @module
22
26
  */
23
- import { asyncUtils, errorUtils, log } from "@dbx-tools/shared-core";
27
+ import { ConfigurationError } from "@databricks/appkit";
28
+ import { asyncUtils, errorUtils, log, object } from "@dbx-tools/shared-core";
24
29
  import { feedback } from "@dbx-tools/shared-mastra";
30
+ import { z } from "zod";
25
31
  import { databricksFetch, readResponseJson, readResponseText } from "./rest.js";
26
32
  const logger = log.logger("mastra/mlflow");
33
+ /** Experiment tag Databricks stores as `catalog.schema.table_prefix`. */
34
+ const TRACE_DESTINATION_PATH_TAG = "mlflow.experiment.databricksTraceDestinationPath";
35
+ /** Experiment tag naming the OTel spans table (`catalog.schema.{prefix}_otel_spans`). */
36
+ const TRACE_SPAN_STORAGE_TABLE_TAG = "mlflow.experiment.databricksTraceSpanStorageTable";
37
+ /**
38
+ * Cached UC table prefix resolved from the experiment. `loaded` distinguishes
39
+ * "not fetched yet" from "fetched, experiment has no UC location". Network
40
+ * failures are not cached so a later thumbs click can retry.
41
+ */
42
+ let loadedUcPrefix;
43
+ /** Strip the `trace:/` URI wrapper and trailing slashes from a UC prefix. */
44
+ function normalizeUcPrefix(raw) {
45
+ const value = raw?.trim().replace(/^trace:\//, "").replace(/\/+$/, "");
46
+ return value || undefined;
47
+ }
48
+ /**
49
+ * Derive `catalog.schema.table_prefix` from a spans table name.
50
+ * Databricks names the table `{prefix}_otel_spans`.
51
+ */
52
+ function prefixFromSpanTable(table) {
53
+ const name = table?.trim();
54
+ if (!name)
55
+ return undefined;
56
+ if (name.endsWith("_otel_spans"))
57
+ return name.slice(0, -"_otel_spans".length);
58
+ if (name.endsWith("_spans"))
59
+ return name.slice(0, -"_spans".length);
60
+ return undefined;
61
+ }
62
+ function experimentTagMap(raw) {
63
+ const tags = new Map();
64
+ if (Array.isArray(raw)) {
65
+ for (const tag of raw) {
66
+ if (!object.isRecord(tag) || typeof tag.key !== "string" || typeof tag.value !== "string") {
67
+ continue;
68
+ }
69
+ tags.set(tag.key, tag.value);
70
+ }
71
+ return tags;
72
+ }
73
+ if (!object.isRecord(raw))
74
+ return tags;
75
+ for (const [key, value] of Object.entries(raw)) {
76
+ if (typeof value === "string")
77
+ tags.set(key, value);
78
+ }
79
+ return tags;
80
+ }
81
+ /**
82
+ * UC table prefix the assessments API needs, read from experiment tags.
83
+ *
84
+ * Prefers the three-part destination path Databricks writes when the
85
+ * experiment is linked to a table-prefix location. Falls back to stripping
86
+ * `_otel_spans` from the spans-table tag. Two-part schema-linked destinations
87
+ * are ignored (they are not a table prefix).
88
+ */
89
+ export function ucTracePrefixFromExperimentTags(rawTags) {
90
+ const tags = experimentTagMap(rawTags);
91
+ const destination = normalizeUcPrefix(tags.get(TRACE_DESTINATION_PATH_TAG));
92
+ if (destination && destination.split(".").length >= 3)
93
+ return destination;
94
+ return prefixFromSpanTable(tags.get(TRACE_SPAN_STORAGE_TABLE_TAG));
95
+ }
96
+ /** Drop the in-process UC prefix cache (tests). */
97
+ export function resetUcTracePrefixCache() {
98
+ loadedUcPrefix = undefined;
99
+ }
100
+ /**
101
+ * Trace id the assessments REST API accepts for this deployment.
102
+ *
103
+ * Databricks Apps persist traces in Unity Catalog. The experiment UI lists
104
+ * those rows, but `POST /api/3.0/mlflow/traces/{id}/assessments` looks up the
105
+ * experiment store unless `{id}` is the UC URI `trace:/{catalog}.{schema}.{prefix}/{tr-hex}`.
106
+ * A bare `tr-<hex>` 404s and the assessment never appears.
107
+ */
108
+ export function mlflowAssessmentTraceId(traceId, prefix) {
109
+ const resolved = normalizeUcPrefix(prefix);
110
+ return resolved ? `trace:/${resolved}/${traceId}` : traceId;
111
+ }
112
+ async function resolveUcTracePrefix(client) {
113
+ const fromEnv = normalizeUcPrefix(process.env.MLFLOW_UC_TRACE_PREFIX);
114
+ if (fromEnv)
115
+ return fromEnv;
116
+ if (loadedUcPrefix)
117
+ return loadedUcPrefix.prefix;
118
+ const experiment = await fetchExperiment(client);
119
+ if (!experiment)
120
+ return undefined;
121
+ const prefix = ucTracePrefixFromExperimentTags(experiment.tags);
122
+ loadedUcPrefix = { prefix };
123
+ return prefix;
124
+ }
27
125
  /** Assessments REST path for a trace. `3.0` is the current MLflow API version. */
28
126
  const assessmentsPath = (traceId) => `/api/3.0/mlflow/traces/${encodeURIComponent(traceId)}/assessments`;
29
127
  /** Number of times to retry a "trace not found" response before giving up. */
30
128
  const NOT_FOUND_RETRIES = 3;
31
129
  /** Base backoff between "trace not found" retries, in ms (grows linearly). */
32
130
  const NOT_FOUND_BACKOFF_MS = 1200;
131
+ const MlflowExperimentResponseSchema = z
132
+ .object({
133
+ experiment: z
134
+ .object({
135
+ experiment_id: z.string().min(1).describe("Workspace MLflow experiment identifier."),
136
+ tags: z.unknown().optional().describe("Experiment tags, including the UC trace location."),
137
+ })
138
+ .passthrough()
139
+ .describe("Configured MLflow experiment."),
140
+ })
141
+ .passthrough()
142
+ .describe("MLflow get-experiment response.");
143
+ const CurrentUserResponseSchema = z
144
+ .object({
145
+ userName: z.string().optional().describe("Current workspace user name."),
146
+ applicationId: z.string().optional().describe("Current service principal application id."),
147
+ displayName: z.string().optional().describe("Current principal display name."),
148
+ groups: z
149
+ .array(z
150
+ .object({
151
+ value: z.string().optional().describe("Workspace group identifier."),
152
+ display: z.string().optional().describe("Workspace group display name."),
153
+ })
154
+ .passthrough())
155
+ .optional()
156
+ .describe("Groups assigned to the current principal."),
157
+ })
158
+ .passthrough()
159
+ .describe("Databricks Current User API response.");
160
+ const ExperimentPermissionsResponseSchema = z
161
+ .object({
162
+ access_control_list: z
163
+ .array(z
164
+ .object({
165
+ user_name: z.string().optional().describe("User principal name."),
166
+ group_name: z.string().optional().describe("Group principal name."),
167
+ service_principal_name: z
168
+ .string()
169
+ .optional()
170
+ .describe("Service principal application id."),
171
+ all_permissions: z
172
+ .array(z
173
+ .object({
174
+ permission_level: z.string().describe("Effective experiment permission level."),
175
+ })
176
+ .passthrough())
177
+ .optional()
178
+ .describe("Direct and inherited permissions for this principal."),
179
+ })
180
+ .passthrough())
181
+ .optional()
182
+ .describe("Experiment access control entries."),
183
+ })
184
+ .passthrough()
185
+ .describe("Databricks experiment permissions response.");
186
+ function configuredExperiment() {
187
+ const id = process.env.MLFLOW_EXPERIMENT_ID?.trim();
188
+ const name = process.env.MLFLOW_EXPERIMENT_NAME?.trim();
189
+ return {
190
+ ...(id ? { id } : {}),
191
+ ...(name ? { name } : {}),
192
+ };
193
+ }
33
194
  /**
34
195
  * Whether MLflow feedback logging is available for this deployment.
35
196
  *
@@ -55,6 +216,83 @@ export function mlflowEnabled() {
55
216
  export function resolveFeedbackEnabled(explicit) {
56
217
  return explicit ?? mlflowEnabled();
57
218
  }
219
+ /**
220
+ * Reject an explicitly enabled feedback surface when no MLflow experiment is
221
+ * configured. Auto mode remains non-fatal and simply disables feedback.
222
+ */
223
+ export function validateFeedbackConfig(explicit) {
224
+ if (explicit !== true)
225
+ return;
226
+ const experiment = configuredExperiment();
227
+ if (experiment.id || experiment.name)
228
+ return;
229
+ throw new ConfigurationError("mastra: feedback is enabled but no MLflow experiment is configured. " +
230
+ "Set MLFLOW_EXPERIMENT_ID or MLFLOW_EXPERIMENT_NAME, use feedback: false, " +
231
+ "or omit feedback to use automatic detection.");
232
+ }
233
+ /**
234
+ * Return the configured experiment URL only when the active Databricks
235
+ * principal has effective `CAN_MANAGE` permission. Permission lookup is
236
+ * intentionally best-effort so Apps environments that do not expose the
237
+ * required APIs simply omit the debug affordance.
238
+ */
239
+ export async function mlflowExperimentManagerUrl(client) {
240
+ try {
241
+ return await resolveMlflowExperimentManagerUrl(client);
242
+ }
243
+ catch {
244
+ return undefined;
245
+ }
246
+ }
247
+ async function resolveMlflowExperimentManagerUrl(client) {
248
+ const experimentId = await resolveExperimentId(client);
249
+ if (!experimentId)
250
+ return undefined;
251
+ const [currentResponse, permissionsResponse] = await Promise.all([
252
+ databricksFetch(client, "/api/2.0/preview/scim/v2/Me", { method: "GET" }),
253
+ databricksFetch(client, `/api/2.0/permissions/experiments/${encodeURIComponent(experimentId)}`, { method: "GET" }),
254
+ ]);
255
+ if (!currentResponse.ok || !permissionsResponse.ok)
256
+ return undefined;
257
+ const current = CurrentUserResponseSchema.safeParse(await readResponseJson(currentResponse));
258
+ const permissions = ExperimentPermissionsResponseSchema.safeParse(await readResponseJson(permissionsResponse));
259
+ if (!current.success || !permissions.success)
260
+ return undefined;
261
+ const userNames = new Set([current.data.userName, current.data.applicationId, current.data.displayName].filter((value) => Boolean(value)));
262
+ const groupNames = new Set((current.data.groups ?? []).flatMap(({ value, display }) => [value, display].filter((entry) => Boolean(entry))));
263
+ const canManage = (permissions.data.access_control_list ?? []).some((entry) => {
264
+ const matches = (entry.user_name !== undefined && userNames.has(entry.user_name)) ||
265
+ (entry.service_principal_name !== undefined && userNames.has(entry.service_principal_name)) ||
266
+ (entry.group_name !== undefined && groupNames.has(entry.group_name));
267
+ return (matches &&
268
+ (entry.all_permissions ?? []).some((permission) => permission.permission_level === "CAN_MANAGE"));
269
+ });
270
+ if (!canManage)
271
+ return undefined;
272
+ const host = (await client.config.getHost()).toString();
273
+ return new URL(`/ml/experiments/${encodeURIComponent(experimentId)}`, host).toString();
274
+ }
275
+ async function fetchExperiment(client) {
276
+ const experiment = configuredExperiment();
277
+ const path = experiment.id
278
+ ? `/api/2.0/mlflow/experiments/get?experiment_id=${encodeURIComponent(experiment.id)}`
279
+ : experiment.name
280
+ ? `/api/2.0/mlflow/experiments/get-by-name?experiment_name=${encodeURIComponent(experiment.name)}`
281
+ : undefined;
282
+ if (!path)
283
+ return undefined;
284
+ const response = await databricksFetch(client, path, { method: "GET" });
285
+ if (!response.ok)
286
+ return undefined;
287
+ const parsed = MlflowExperimentResponseSchema.safeParse(await readResponseJson(response));
288
+ return parsed.success ? parsed.data.experiment : undefined;
289
+ }
290
+ async function resolveExperimentId(client) {
291
+ const configured = configuredExperiment();
292
+ if (configured.id)
293
+ return configured.id;
294
+ return (await fetchExperiment(client))?.experiment_id;
295
+ }
58
296
  /**
59
297
  * Log a HUMAN feedback assessment to a trace. Returns the created
60
298
  * assessment id on success, or `undefined` when the trace can't be
@@ -69,8 +307,9 @@ export async function logFeedback(client, params) {
69
307
  const name = params.name?.trim() ||
70
308
  (hasValue ? feedback.DEFAULT_FEEDBACK_NAME : feedback.DEFAULT_COMMENT_NAME);
71
309
  const value = hasValue ? params.value : params.comment;
310
+ const assessmentTraceId = mlflowAssessmentTraceId(params.traceId, await resolveUcTracePrefix(client));
72
311
  const assessment = {
73
- trace_id: params.traceId,
312
+ trace_id: assessmentTraceId,
74
313
  assessment_name: name,
75
314
  source: {
76
315
  source_type: "HUMAN",
@@ -83,7 +322,7 @@ export async function logFeedback(client, params) {
83
322
  for (let attempt = 0; attempt <= NOT_FOUND_RETRIES; attempt++) {
84
323
  let res;
85
324
  try {
86
- res = await databricksFetch(client, assessmentsPath(params.traceId), {
325
+ res = await databricksFetch(client, assessmentsPath(assessmentTraceId), {
87
326
  method: "POST",
88
327
  body,
89
328
  });
@@ -91,6 +330,7 @@ export async function logFeedback(client, params) {
91
330
  catch (err) {
92
331
  logger.warn("feedback request failed", {
93
332
  traceId: params.traceId,
333
+ assessmentTraceId,
94
334
  error: errorUtils.errorMessage(err),
95
335
  });
96
336
  return undefined;
@@ -109,6 +349,7 @@ export async function logFeedback(client, params) {
109
349
  }
110
350
  logger.warn("feedback not recorded", {
111
351
  traceId: params.traceId,
352
+ assessmentTraceId,
112
353
  status: res.status,
113
354
  body: await readResponseText(res),
114
355
  });
@@ -116,4 +357,4 @@ export async function logFeedback(client, params) {
116
357
  }
117
358
  return undefined;
118
359
  }
119
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWxmbG93LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL21sZmxvdy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBcUJHO0FBR0gsT0FBTyxFQUFFLFVBQVUsRUFBRSxVQUFVLEVBQUUsR0FBRyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDckUsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3BELE9BQU8sRUFBRSxlQUFlLEVBQUUsZ0JBQWdCLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFFaEYsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxlQUFlLENBQUMsQ0FBQztBQUszQyxrRkFBa0Y7QUFDbEYsTUFBTSxlQUFlLEdBQUcsQ0FBQyxPQUFlLEVBQVUsRUFBRSxDQUNsRCwwQkFBMEIsa0JBQWtCLENBQUMsT0FBTyxDQUFDLGNBQWMsQ0FBQztBQUV0RSw4RUFBOEU7QUFDOUUsTUFBTSxpQkFBaUIsR0FBRyxDQUFDLENBQUM7QUFDNUIsOEVBQThFO0FBQzlFLE1BQU0sb0JBQW9CLEdBQUcsSUFBSSxDQUFDO0FBRWxDOzs7Ozs7OztHQVFHO0FBQ0gsTUFBTSxVQUFVLGFBQWE7SUFDM0IsTUFBTSxXQUFXLEdBQUcsT0FBTyxDQUN6QixPQUFPLENBQUMsR0FBRyxDQUFDLDJCQUEyQixFQUFFLElBQUksRUFBRTtRQUMvQyxPQUFPLENBQUMsR0FBRyxDQUFDLGtDQUFrQyxFQUFFLElBQUksRUFBRSxDQUN2RCxDQUFDO0lBQ0YsTUFBTSxhQUFhLEdBQUcsT0FBTyxDQUMzQixPQUFPLENBQUMsR0FBRyxDQUFDLG9CQUFvQixFQUFFLElBQUksRUFBRSxJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsc0JBQXNCLEVBQUUsSUFBSSxFQUFFLENBQ3ZGLENBQUM7SUFDRixPQUFPLFdBQVcsSUFBSSxhQUFhLENBQUM7QUFDdEMsQ0FBQztBQUVE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QUN2QixNQUFNLEVBQUUsR0FBRyxDQUFDLE1BQU07WUFDbEIsSUFBSSxFQUFFLE1BQU0sZ0JBQWdCLENBQUMsR0FBRyxDQUFDO1NBQ2xDLENBQUMsQ0FBQztRQUNILE9BQU8sU0FBUyxDQUFDO0lBQ25CLENBQUM7SUFDRCxPQUFPLFNBQVMsQ0FBQztBQUNuQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBNTGZsb3cgdXNlci1mZWVkYmFjayBsb2dnaW5nOiBkZXRlY3Qgd2hldGhlciBNTGZsb3cgdHJhY2luZyBpcyB3aXJlZFxuICogZm9yIHRoaXMgZGVwbG95bWVudCwgYW5kIGxvZyBhIHRodW1icyAvIGNvbW1lbnQgYXMgYSB0cmFjZVxuICogKmFzc2Vzc21lbnQqIHZpYSB0aGUgRGF0YWJyaWNrcyBNTGZsb3cgUkVTVCBBUEkuXG4gKlxuICogRmVlZGJhY2sgYXR0YWNoZXMgdG8gYSB0cmFjZSwgYW5kIHRoZSBwbHVnaW4ncyBzcGFucyByZWFjaCBNTGZsb3dcbiAqIHRocm91Z2ggdGhlIHNhbWUgT1RlbCBwaXBlbGluZSBhcyBldmVyeSBvdGhlciBBcHBLaXQgc3BhbiAoc2VlXG4gKiBgb2JzZXJ2YWJpbGl0eS50c2ApLiBPbiBEYXRhYnJpY2tzIEFwcHMgdGhhdCBwaXBlbGluZSBpcyB0aGUgVUNcbiAqIHNpZGVjYXIgaW5qZWN0ZWQgYnkgYHRlbGVtZXRyeV9leHBvcnRfZGVzdGluYXRpb25zYCwgbm90IGEgZGlyZWN0XG4gKiB3b3Jrc3BhY2UgT1RMUCBVUkwuIE1MZmxvdyBkZXJpdmVzIGl0cyB0cmFjZSBpZCBmcm9tIHRoZSBPcGVuVGVsZW1ldHJ5XG4gKiB0cmFjZSBpZCAoYHRyLTxoZXgob3RlbFRyYWNlSWQpPmApLCBzbyB0aGUgc2VydmVyIHN0YW1wcyB0aGUgYWN0aXZlXG4gKiB0cmFjZSBpZCBvbiBlYWNoIHR1cm4ncyByZXNwb25zZSBhbmQgdGhlIGNsaWVudCBzZW5kcyBpdCBiYWNrIGhlcmUuXG4gKlxuICogVGhlcmUgaXMgbm8gTUxmbG93IEpTIFNESywgc28gdGhpcyBwb3N0cyB0byB0aGUgYXNzZXNzbWVudHMgUkVTVFxuICogZW5kcG9pbnQgZGlyZWN0bHkgdXNpbmcgdGhlIE9CTy1zY29wZWQgd29ya3NwYWNlIGNsaWVudCAodGhlIGZlZWRiYWNrXG4gKiBpcyB0aHVzIGF0dHJpYnV0ZWQgdG8gdGhlIHNpZ25lZC1pbiB1c2VyKS4gVHJhY2UgZXhwb3J0IGlzXG4gKiBhc3luY2hyb25vdXMsIHNvIHRoZSBqdXN0LWZpbmlzaGVkIHRyYWNlIG1heSBub3QgZXhpc3QgaW4gTUxmbG93IHlldFxuICogd2hlbiB0aGUgdXNlciByZWFjdHM7IHRoZSBsb2cgY2FsbCByZXRyaWVzIGJyaWVmbHkgb24gXCJub3QgZm91bmRcIlxuICogYmVmb3JlIGdpdmluZyB1cCBzb2Z0bHkuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IGFwcGtpdCB9IGZyb20gXCJAZGJ4LXRvb2xzL2FwcGtpdFwiO1xuaW1wb3J0IHsgYXN5bmNVdGlscywgZXJyb3JVdGlscywgbG9nIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB7IGZlZWRiYWNrIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLW1hc3RyYVwiO1xuaW1wb3J0IHsgZGF0YWJyaWNrc0ZldGNoLCByZWFkUmVzcG9uc2VKc29uLCByZWFkUmVzcG9uc2VUZXh0IH0gZnJvbSBcIi4vcmVzdC50c1wiO1xuXG5jb25zdCBsb2dnZXIgPSBsb2cubG9nZ2VyKFwibWFzdHJhL21sZmxvd1wiKTtcblxuLyoqIFdvcmtzcGFjZSBjbGllbnQgY2FycmllZCBvbiBhbiBBcHBLaXQgZXhlY3V0aW9uIGNvbnRleHQuICovXG50eXBlIFdvcmtzcGFjZUNsaWVudCA9IGFwcGtpdC5Xb3Jrc3BhY2VDbGllbnRMaWtlO1xuXG4vKiogQXNzZXNzbWVudHMgUkVTVCBwYXRoIGZvciBhIHRyYWNlLiBgMy4wYCBpcyB0aGUgY3VycmVudCBNTGZsb3cgQVBJIHZlcnNpb24uICovXG5jb25zdCBhc3Nlc3NtZW50c1BhdGggPSAodHJhY2VJZDogc3RyaW5nKTogc3RyaW5nID0+XG4gIGAvYXBpLzMuMC9tbGZsb3cvdHJhY2VzLyR7ZW5jb2RlVVJJQ29tcG9uZW50KHRyYWNlSWQpfS9hc3Nlc3NtZW50c2A7XG5cbi8qKiBOdW1iZXIgb2YgdGltZXMgdG8gcmV0cnkgYSBcInRyYWNlIG5vdCBmb3VuZFwiIHJlc3BvbnNlIGJlZm9yZSBnaXZpbmcgdXAuICovXG5jb25zdCBOT1RfRk9VTkRfUkVUUklFUyA9IDM7XG4vKiogQmFzZSBiYWNrb2ZmIGJldHdlZW4gXCJ0cmFjZSBub3QgZm91bmRcIiByZXRyaWVzLCBpbiBtcyAoZ3Jvd3MgbGluZWFybHkpLiAqL1xuY29uc3QgTk9UX0ZPVU5EX0JBQ0tPRkZfTVMgPSAxMjAwO1xuXG4vKipcbiAqIFdoZXRoZXIgTUxmbG93IGZlZWRiYWNrIGxvZ2dpbmcgaXMgYXZhaWxhYmxlIGZvciB0aGlzIGRlcGxveW1lbnQuXG4gKlxuICogRW5hYmxlZCB3aGVuIGFuIE9UTFAgZXhwb3J0ZXIgZW5kcG9pbnQgaXMgY29uZmlndXJlZCAodHJhY2VzIGFyZVxuICogYWN0dWFsbHkgc2hpcHBlZCBzb21ld2hlcmUpIEFORCBhbiBNTGZsb3cgZXhwZXJpbWVudCBpcyBuYW1lZCAtIHRoZVxuICogdHdvIHNpZ25hbHMgdGhhdCB0aGUgT1RMUCBiYWNrZW5kIGlzIE1MZmxvdyBhbmQgdHJhY2VzIHdpbGxcbiAqIG1hdGVyaWFsaXplIHRoZXJlLiBCb3RoIGFyZSBzdGFuZGFyZCBlbnYgdmFycywgc28gbm8gcGx1Z2luIGNvbmZpZyBpc1xuICogcmVxdWlyZWQ7IGEgZGVwbG95bWVudCBvcHRzIGluIHNpbXBseSBieSB3aXJpbmcgTUxmbG93IHRyYWNpbmcuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBtbGZsb3dFbmFibGVkKCk6IGJvb2xlYW4ge1xuICBjb25zdCBoYXNFeHBvcnRlciA9IEJvb2xlYW4oXG4gICAgcHJvY2Vzcy5lbnYuT1RFTF9FWFBPUlRFUl9PVExQX0VORFBPSU5UPy50cmltKCkgfHxcbiAgICBwcm9jZXNzLmVudi5PVEVMX0VYUE9SVEVSX09UTFBfVFJBQ0VTX0VORFBPSU5UPy50cmltKCksXG4gICk7XG4gIGNvbnN0IGhhc0V4cGVyaW1lbnQgPSBCb29sZWFuKFxuICAgIHByb2Nlc3MuZW52Lk1MRkxPV19FWFBFUklNRU5UX0lEPy50cmltKCkgfHwgcHJvY2Vzcy5lbnYuTUxGTE9XX0VYUEVSSU1FTlRfTkFNRT8udHJpbSgpLFxuICApO1xuICByZXR1cm4gaGFzRXhwb3J0ZXIgJiYgaGFzRXhwZXJpbWVudDtcbn1cblxuLyoqXG4gKiBSZXNvbHZlIHdoZXRoZXIgdXNlciBmZWVkYmFjayBpcyBlbmFibGVkIGZyb20gdGhlIHBsdWdpbidzIG9wdGlvbmFsXG4gKiBgY29uZmlnLmZlZWRiYWNrYCBvdmVycmlkZTogdGhlIGV4cGxpY2l0IGJvb2xlYW4gd2lucywgb3RoZXJ3aXNlIGZhbGxcbiAqIGJhY2sgdG8gYXV0by1kZXRlY3RpbmcgTUxmbG93IHRyYWNpbmcgKHtAbGluayBtbGZsb3dFbmFibGVkfSkuIFNoYXJlZFxuICogYnkgdGhlIHBsdWdpbidzIGNsaWVudC1jb25maWcgZ2F0ZSBhbmQgdGhlIHNlcnZlcidzIHRyYWNlLWlkIGhlYWRlciBzb1xuICogdGhlIHR3byBuZXZlciBkaXNhZ3JlZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVGZWVkYmFja0VuYWJsZWQoZXhwbGljaXQ6IGJvb2xlYW4gfCB1bmRlZmluZWQpOiBib29sZWFuIHtcbiAgcmV0dXJuIGV4cGxpY2l0ID8/IG1sZmxvd0VuYWJsZWQoKTtcbn1cblxuLyoqIFBhcmFtZXRlcnMgZm9yIHtAbGluayBsb2dGZWVkYmFja30uICovXG5leHBvcnQgaW50ZXJmYWNlIExvZ0ZlZWRiYWNrUGFyYW1zIHtcbiAgLyoqIE1MZmxvdyB0cmFjZSBpZCB0aGUgYXNzZXNzbWVudCBhdHRhY2hlcyB0byAoYHRyLTxoZXg+YCkuICovXG4gIHRyYWNlSWQ6IHN0cmluZztcbiAgLyoqIEFzc2Vzc21lbnQgbmFtZTsgZGVmYXVsdHMgcGVyIHdoZXRoZXIgYSB2YWx1ZSBvciBhIGNvbW1lbnQgaXMgc2VudC4gKi9cbiAgbmFtZT86IHN0cmluZztcbiAgLyoqIFRodW1icyAvIHJhdGluZyAvIGxhYmVsIHZhbHVlLiBPbWl0IGZvciBhIGNvbW1lbnQtb25seSBzdWJtaXNzaW9uLiAqL1xuICB2YWx1ZT86IGJvb2xlYW4gfCBudW1iZXIgfCBzdHJpbmc7XG4gIC8qKiBGcmVlZm9ybSBjb21tZW50OiB0aGUgcmF0aW9uYWxlIGFsb25nc2lkZSBhIHZhbHVlLCBvciB0aGUgdmFsdWUgaXRzZWxmIHdoZW4gbm9uZS4gKi9cbiAgY29tbWVudD86IHN0cmluZztcbiAgLyoqIElkZW50aXR5IHRoZSBmZWVkYmFjayBpcyBhdHRyaWJ1dGVkIHRvICh1c2VyIGVtYWlsIC8gaWQpLiAqL1xuICBzb3VyY2VJZD86IHN0cmluZztcbn1cblxuLyoqXG4gKiBMb2cgYSBIVU1BTiBmZWVkYmFjayBhc3Nlc3NtZW50IHRvIGEgdHJhY2UuIFJldHVybnMgdGhlIGNyZWF0ZWRcbiAqIGFzc2Vzc21lbnQgaWQgb24gc3VjY2Vzcywgb3IgYHVuZGVmaW5lZGAgd2hlbiB0aGUgdHJhY2UgY2FuJ3QgYmVcbiAqIGZvdW5kIChldmVuIGFmdGVyIHJldHJ5aW5nIGZvciBleHBvcnQgbGFnKSBvciB0aGUgcmVxdWVzdCBvdGhlcndpc2VcbiAqIGZhaWxzIC0gY2FsbGVycyBzdXJmYWNlIHRoYXQgYXMgYSBzb2Z0IFwibm90IHJlY29yZGVkXCIgcmF0aGVyIHRoYW4gYW5cbiAqIGVycm9yLCBrZWVwaW5nIHRoZSBjaGF0IHVzYWJsZS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvZ0ZlZWRiYWNrKFxuICBjbGllbnQ6IFdvcmtzcGFjZUNsaWVudCxcbiAgcGFyYW1zOiBMb2dGZWVkYmFja1BhcmFtcyxcbik6IFByb21pc2U8c3RyaW5nIHwgdW5kZWZpbmVkPiB7XG4gIC8vIEEgY29tbWVudCB3aXRoIG5vIHRodW1icyB2YWx1ZSBpcyBsb2dnZWQgYXMgdGV4dCBmZWVkYmFjazsgYSB2YWx1ZVxuICAvLyAod2l0aCBhbiBvcHRpb25hbCBjb21tZW50IGFzIHRoZSByYXRpb25hbGUpIGlzIHRoZSB0aHVtYnMgcGF0aC5cbiAgY29uc3QgaGFzVmFsdWUgPSBwYXJhbXMudmFsdWUgIT09IHVuZGVmaW5lZDtcbiAgY29uc3QgbmFtZSA9XG4gICAgcGFyYW1zLm5hbWU/LnRyaW0oKSB8fFxuICAgIChoYXNWYWx1ZSA/IGZlZWRiYWNrLkRFRkFVTFRfRkVFREJBQ0tfTkFNRSA6IGZlZWRiYWNrLkRFRkFVTFRfQ09NTUVOVF9OQU1FKTtcbiAgY29uc3QgdmFsdWUgPSBoYXNWYWx1ZSA/IHBhcmFtcy52YWx1ZSA6IHBhcmFtcy5jb21tZW50O1xuICBjb25zdCBhc3Nlc3NtZW50OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHtcbiAgICB0cmFjZV9pZDogcGFyYW1zLnRyYWNlSWQsXG4gICAgYXNzZXNzbWVudF9uYW1lOiBuYW1lLFxuICAgIHNvdXJjZToge1xuICAgICAgc291cmNlX3R5cGU6IFwiSFVNQU5cIixcbiAgICAgIHNvdXJjZV9pZDogcGFyYW1zLnNvdXJjZUlkPy50cmltKCkgfHwgXCJ1c2VyXCIsXG4gICAgfSxcbiAgICBmZWVkYmFjazogeyB2YWx1ZSB9LFxuICAgIC4uLihoYXNWYWx1ZSAmJiBwYXJhbXMuY29tbWVudD8udHJpbSgpID8geyByYXRpb25hbGU6IHBhcmFtcy5jb21tZW50IH0gOiB7fSksXG4gIH07XG4gIGNvbnN0IGJvZHkgPSB7IGFzc2Vzc21lbnQgfTtcblxuICBmb3IgKGxldCBhdHRlbXB0ID0gMDsgYXR0ZW1wdCA8PSBOT1RfRk9VTkRfUkVUUklFUzsgYXR0ZW1wdCsrKSB7XG4gICAgbGV0IHJlczogUmVzcG9uc2U7XG4gICAgdHJ5IHtcbiAgICAgIHJlcyA9IGF3YWl0IGRhdGFicmlja3NGZXRjaChjbGllbnQsIGFzc2Vzc21lbnRzUGF0aChwYXJhbXMudHJhY2VJZCksIHtcbiAgICAgICAgbWV0aG9kOiBcIlBPU1RcIixcbiAgICAgICAgYm9keSxcbiAgICAgIH0pO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgbG9nZ2VyLndhcm4oXCJmZWVkYmFjayByZXF1ZXN0IGZhaWxlZFwiLCB7XG4gICAgICAgIHRyYWNlSWQ6IHBhcmFtcy50cmFjZUlkLFxuICAgICAgICBlcnJvcjogZXJyb3JVdGlscy5lcnJvck1lc3NhZ2UoZXJyKSxcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG4gICAgaWYgKHJlcy5vaykge1xuICAgICAgY29uc3QgcGFyc2VkID0gYXdhaXQgcmVhZFJlc3BvbnNlSnNvbihyZXMpO1xuICAgICAgY29uc3QgYXNzZXNzbWVudElkID1cbiAgICAgICAgKHBhcnNlZCBhcyB7IGFzc2Vzc21lbnQ/OiB7IGFzc2Vzc21lbnRfaWQ/OiB1bmtub3duIH0gfSk/LmFzc2Vzc21lbnQ/LmFzc2Vzc21lbnRfaWQgPz9cbiAgICAgICAgKHBhcnNlZCBhcyB7IGFzc2Vzc21lbnRfaWQ/OiB1bmtub3duIH0pPy5hc3Nlc3NtZW50X2lkO1xuICAgICAgcmV0dXJuIHR5cGVvZiBhc3Nlc3NtZW50SWQgPT09IFwic3RyaW5nXCIgPyBhc3Nlc3NtZW50SWQgOiBcIlwiO1xuICAgIH1cbiAgICAvLyBUcmFjZSBleHBvcnQgaXMgYXN5bmM7IGEgZnJlc2ggdHJhY2UgbWF5IG5vdCBleGlzdCB5ZXQuIFJldHJ5IGFcbiAgICAvLyBmZXcgdGltZXMgd2l0aCBhIHNob3J0IGJhY2tvZmYgYmVmb3JlIGdpdmluZyB1cCBzb2Z0bHkuXG4gICAgaWYgKHJlcy5zdGF0dXMgPT09IDQwNCAmJiBhdHRlbXB0IDwgTk9UX0ZPVU5EX1JFVFJJRVMpIHtcbiAgICAgIGF3YWl0IGFzeW5jVXRpbHMuc2xlZXAoTk9UX0ZPVU5EX0JBQ0tPRkZfTVMgKiAoYXR0ZW1wdCArIDEpKTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICBsb2dnZXIud2FybihcImZlZWRiYWNrIG5vdCByZWNvcmRlZFwiLCB7XG4gICAgICB0cmFjZUlkOiBwYXJhbXMudHJhY2VJZCxcbiAgICAgIHN0YXR1czogcmVzLnN0YXR1cyxcbiAgICAgIGJvZHk6IGF3YWl0IHJlYWRSZXNwb25zZVRleHQocmVzKSxcbiAgICB9KTtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG4gIHJldHVybiB1bmRlZmluZWQ7XG59XG4iXX0=
360
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWxmbG93LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL21sZmxvdy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXlCRztBQUVILE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBRXhELE9BQU8sRUFBRSxVQUFVLEVBQUUsVUFBVSxFQUFFLEdBQUcsRUFBRSxNQUFNLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUM3RSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDcEQsT0FBTyxFQUFFLENBQUMsRUFBRSxNQUFNLEtBQUssQ0FBQztBQUN4QixPQUFPLEVBQUUsZUFBZSxFQUFFLGdCQUFnQixFQUFFLGdCQUFnQixFQUFFLE1BQU0sV0FBVyxDQUFDO0FBRWhGLE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsZUFBZSxDQUFDLENBQUM7QUFLM0MseUVBQXlFO0FBQ3pFLE1BQU0sMEJBQTBCLEdBQUcsa0RBQWtELENBQUM7QUFDdEYseUZBQXlGO0FBQ3pGLE1BQU0sNEJBQTRCLEdBQUcsbURBQW1ELENBQUM7QUFFekY7Ozs7R0FJRztBQUNILElBQUksY0FBMEQsQ0FBQztBQUUvRCw2RUFBNkU7QUFDN0UsU0FBUyxpQkFBaUIsQ0FBQyxHQUF1QjtJQUNoRCxNQUFNLEtBQUssR0FBRyxHQUFHLEVBQUUsSUFBSSxFQUFFLENBQUMsT0FBTyxDQUFDLFdBQVcsRUFBRSxFQUFFLENBQUMsQ0FBQyxPQUFPLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ3ZFLE9BQU8sS0FBSyxJQUFJLFNBQVMsQ0FBQztBQUM1QixDQUFDO0FBRUQ7OztHQUdHO0FBQ0gsU0FBUyxtQkFBbUIsQ0FBQyxLQUF5QjtJQUNwRCxNQUFNLElBQUksR0FBRyxLQUFLLEVBQUUsSUFBSSxFQUFFLENBQUM7SUFDM0IsSUFBSSxDQUFDLElBQUk7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QUNqQixLQUFLLEVBQUUsVUFBVSxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUM7YUFDcEMsQ0FBQyxDQUFDO1lBQ0gsT0FBTyxTQUFTLENBQUM7UUFDbkIsQ0FBQztRQUNELElBQUksR0FBRyxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQ1gsTUFBTSxNQUFNLEdBQUcsTUFBTSxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUMzQyxNQUFNLFlBQVksR0FDZixNQUF1RCxFQUFFLFVBQVUsRUFBRSxhQUFhO2dCQUNsRixNQUFzQyxFQUFFLGFBQWEsQ0FBQztZQUN6RCxPQUFPLE9BQU8sWUFBWSxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDOUQsQ0FBQztRQUNELGtFQUFrRTtRQUNsRSwwREFBMEQ7UUFDMUQsSUFBSSxHQUFHLENBQUMsTUFBTSxLQUFLLEdBQUcsSUFBSSxPQUFPLEdBQUcsaUJBQWlCLEVBQUUsQ0FBQztZQUN0RCxNQUFNLFVBQVUsQ0FBQyxLQUFLLENBQUMsb0JBQW9CLEdBQUcsQ0FBQyxPQUFPLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUM3RCxTQUFTO1FBQ1gsQ0FBQztRQUNELE1BQU0sQ0FBQyxJQUFJLENBQUMsdUJBQXVCLEVBQUU7WUFDbkMsT0FBTyxFQUFFLE1BQU0sQ0FBQyxPQUFPO1lBQ3ZCLGlCQUFpQjtZQUNqQixNQUFNLEVBQUUsR0FBRyxDQUFDLE1BQU07WUFDbEIsSUFBSSxFQUFFLE1BQU0sZ0JBQWdCLENBQUMsR0FBRyxDQUFDO1NBQ2xDLENBQUMsQ0FBQztRQUNILE9BQU8sU0FBUyxDQUFDO0lBQ25CLENBQUM7SUFDRCxPQUFPLFNBQVMsQ0FBQztBQUNuQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBNTGZsb3cgdXNlci1mZWVkYmFjayBsb2dnaW5nOiBkZXRlY3Qgd2hldGhlciBNTGZsb3cgdHJhY2luZyBpcyB3aXJlZFxuICogZm9yIHRoaXMgZGVwbG95bWVudCwgYW5kIGxvZyBhIHRodW1icyAvIGNvbW1lbnQgYXMgYSB0cmFjZVxuICogKmFzc2Vzc21lbnQqIHZpYSB0aGUgRGF0YWJyaWNrcyBNTGZsb3cgUkVTVCBBUEkuXG4gKlxuICogRmVlZGJhY2sgYXR0YWNoZXMgdG8gYSB0cmFjZSwgYW5kIHRoZSBwbHVnaW4ncyBzcGFucyByZWFjaCBNTGZsb3dcbiAqIHRocm91Z2ggdGhlIHNhbWUgT1RlbCBwaXBlbGluZSBhcyBldmVyeSBvdGhlciBBcHBLaXQgc3BhbiAoc2VlXG4gKiBgb2JzZXJ2YWJpbGl0eS50c2ApLiBPbiBEYXRhYnJpY2tzIEFwcHMgdGhhdCBwaXBlbGluZSBpcyB0aGUgVUNcbiAqIHNpZGVjYXIgaW5qZWN0ZWQgYnkgYHRlbGVtZXRyeV9leHBvcnRfZGVzdGluYXRpb25zYCwgbm90IGEgZGlyZWN0XG4gKiB3b3Jrc3BhY2UgT1RMUCBVUkwuIE1MZmxvdyBkZXJpdmVzIGl0cyB0cmFjZSBpZCBmcm9tIHRoZSBPcGVuVGVsZW1ldHJ5XG4gKiB0cmFjZSBpZCAoYHRyLTxoZXgob3RlbFRyYWNlSWQpPmApLCBzbyB0aGUgc2VydmVyIHN0YW1wcyB0aGUgYWN0aXZlXG4gKiB0cmFjZSBpZCBvbiBlYWNoIHR1cm4ncyByZXNwb25zZSBhbmQgdGhlIGNsaWVudCBzZW5kcyBpdCBiYWNrIGhlcmUuXG4gKlxuICogVGhlcmUgaXMgbm8gTUxmbG93IEpTIFNESywgc28gdGhpcyBwb3N0cyB0byB0aGUgYXNzZXNzbWVudHMgUkVTVFxuICogZW5kcG9pbnQgZGlyZWN0bHkgdXNpbmcgdGhlIE9CTy1zY29wZWQgd29ya3NwYWNlIGNsaWVudCAodGhlIGZlZWRiYWNrXG4gKiBpcyB0aHVzIGF0dHJpYnV0ZWQgdG8gdGhlIHNpZ25lZC1pbiB1c2VyKS4gVUMtYmFja2VkIEFwcHMgdHJhY2VzIG5lZWRcbiAqIHRoZSBleHBlcmltZW50J3MgdGFibGUtcHJlZml4IFVSSSAoYHRyYWNlOi97Y2F0YWxvZ30ue3NjaGVtYX0ue3ByZWZpeH0ve3RyLWhleH1gKVxuICogcmF0aGVyIHRoYW4gdGhlIGV4cGVyaW1lbnQtc3RvcmUgaWQgdGhlIFVJIGRvZXMgbm90IHN0b3JlLiBUaGF0IHByZWZpeCBpc1xuICogcmVhZCBmcm9tIHRoZSBleHBlcmltZW50J3MgYG1sZmxvdy5leHBlcmltZW50LmRhdGFicmlja3NUcmFjZURlc3RpbmF0aW9uUGF0aGBcbiAqIHRhZyAob3IgdGhlIHNwYW5zLXRhYmxlIHRhZyk7IGBNTEZMT1dfVUNfVFJBQ0VfUFJFRklYYCByZW1haW5zIGFuIG9wdGlvbmFsXG4gKiBvdmVycmlkZS4gVHJhY2UgZXhwb3J0IGlzIGFzeW5jaHJvbm91cywgc28gdGhlIGp1c3QtZmluaXNoZWQgdHJhY2UgbWF5IG5vdFxuICogZXhpc3QgaW4gTUxmbG93IHlldCB3aGVuIHRoZSB1c2VyIHJlYWN0czsgdGhlIGxvZyBjYWxsIHJldHJpZXMgYnJpZWZseSBvblxuICogXCJub3QgZm91bmRcIiBiZWZvcmUgZ2l2aW5nIHVwIHNvZnRseS5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHsgQ29uZmlndXJhdGlvbkVycm9yIH0gZnJvbSBcIkBkYXRhYnJpY2tzL2FwcGtpdFwiO1xuaW1wb3J0IHsgYXBwa2l0IH0gZnJvbSBcIkBkYngtdG9vbHMvYXBwa2l0XCI7XG5pbXBvcnQgeyBhc3luY1V0aWxzLCBlcnJvclV0aWxzLCBsb2csIG9iamVjdCB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5pbXBvcnQgeyBmZWVkYmFjayB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1tYXN0cmFcIjtcbmltcG9ydCB7IHogfSBmcm9tIFwiem9kXCI7XG5pbXBvcnQgeyBkYXRhYnJpY2tzRmV0Y2gsIHJlYWRSZXNwb25zZUpzb24sIHJlYWRSZXNwb25zZVRleHQgfSBmcm9tIFwiLi9yZXN0LnRzXCI7XG5cbmNvbnN0IGxvZ2dlciA9IGxvZy5sb2dnZXIoXCJtYXN0cmEvbWxmbG93XCIpO1xuXG4vKiogV29ya3NwYWNlIGNsaWVudCBjYXJyaWVkIG9uIGFuIEFwcEtpdCBleGVjdXRpb24gY29udGV4dC4gKi9cbnR5cGUgV29ya3NwYWNlQ2xpZW50ID0gYXBwa2l0LldvcmtzcGFjZUNsaWVudExpa2U7XG5cbi8qKiBFeHBlcmltZW50IHRhZyBEYXRhYnJpY2tzIHN0b3JlcyBhcyBgY2F0YWxvZy5zY2hlbWEudGFibGVfcHJlZml4YC4gKi9cbmNvbnN0IFRSQUNFX0RFU1RJTkFUSU9OX1BBVEhfVEFHID0gXCJtbGZsb3cuZXhwZXJpbWVudC5kYXRhYnJpY2tzVHJhY2VEZXN0aW5hdGlvblBhdGhcIjtcbi8qKiBFeHBlcmltZW50IHRhZyBuYW1pbmcgdGhlIE9UZWwgc3BhbnMgdGFibGUgKGBjYXRhbG9nLnNjaGVtYS57cHJlZml4fV9vdGVsX3NwYW5zYCkuICovXG5jb25zdCBUUkFDRV9TUEFOX1NUT1JBR0VfVEFCTEVfVEFHID0gXCJtbGZsb3cuZXhwZXJpbWVudC5kYXRhYnJpY2tzVHJhY2VTcGFuU3RvcmFnZVRhYmxlXCI7XG5cbi8qKlxuICogQ2FjaGVkIFVDIHRhYmxlIHByZWZpeCByZXNvbHZlZCBmcm9tIHRoZSBleHBlcmltZW50LiBgbG9hZGVkYCBkaXN0aW5ndWlzaGVzXG4gKiBcIm5vdCBmZXRjaGVkIHlldFwiIGZyb20gXCJmZXRjaGVkLCBleHBlcmltZW50IGhhcyBubyBVQyBsb2NhdGlvblwiLiBOZXR3b3JrXG4gKiBmYWlsdXJlcyBhcmUgbm90IGNhY2hlZCBzbyBhIGxhdGVyIHRodW1icyBjbGljayBjYW4gcmV0cnkuXG4gKi9cbmxldCBsb2FkZWRVY1ByZWZpeDogeyBwcmVmaXg6IHN0cmluZyB8IHVuZGVmaW5lZCB9IHwgdW5kZWZpbmVkO1xuXG4vKiogU3RyaXAgdGhlIGB0cmFjZTovYCBVUkkgd3JhcHBlciBhbmQgdHJhaWxpbmcgc2xhc2hlcyBmcm9tIGEgVUMgcHJlZml4LiAqL1xuZnVuY3Rpb24gbm9ybWFsaXplVWNQcmVmaXgocmF3OiBzdHJpbmcgfCB1bmRlZmluZWQpOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICBjb25zdCB2YWx1ZSA9IHJhdz8udHJpbSgpLnJlcGxhY2UoL150cmFjZTpcXC8vLCBcIlwiKS5yZXBsYWNlKC9cXC8rJC8sIFwiXCIpO1xuICByZXR1cm4gdmFsdWUgfHwgdW5kZWZpbmVkO1xufVxuXG4vKipcbiAqIERlcml2ZSBgY2F0YWxvZy5zY2hlbWEudGFibGVfcHJlZml4YCBmcm9tIGEgc3BhbnMgdGFibGUgbmFtZS5cbiAqIERhdGFicmlja3MgbmFtZXMgdGhlIHRhYmxlIGB7cHJlZml4fV9vdGVsX3NwYW5zYC5cbiAqL1xuZnVuY3Rpb24gcHJlZml4RnJvbVNwYW5UYWJsZSh0YWJsZTogc3RyaW5nIHwgdW5kZWZpbmVkKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgY29uc3QgbmFtZSA9IHRhYmxlPy50cmltKCk7XG4gIGlmICghbmFtZSkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgaWYgKG5hbWUuZW5kc1dpdGgoXCJfb3RlbF9zcGFuc1wiKSkgcmV0dXJuIG5hbWUuc2xpY2UoMCwgLVwiX290ZWxfc3BhbnNcIi5sZW5ndGgpO1xuICBpZiAobmFtZS5lbmRzV2l0aChcIl9zcGFuc1wiKSkgcmV0dXJuIG5hbWUuc2xpY2UoMCwgLVwiX3NwYW5zXCIubGVuZ3RoKTtcbiAgcmV0dXJuIHVuZGVmaW5lZDtcbn1cblxuZnVuY3Rpb24gZXhwZXJpbWVudFRhZ01hcChyYXc6IHVua25vd24pOiBNYXA8c3RyaW5nLCBzdHJpbmc+IHtcbiAgY29uc3QgdGFncyA9IG5ldyBNYXA8c3RyaW5nLCBzdHJpbmc+KCk7XG4gIGlmIChBcnJheS5pc0FycmF5KHJhdykpIHtcbiAgICBmb3IgKGNvbnN0IHRhZyBvZiByYXcpIHtcbiAgICAgIGlmICghb2JqZWN0LmlzUmVjb3JkKHRhZykgfHwgdHlwZW9mIHRhZy5rZXkgIT09IFwic3RyaW5nXCIgfHwgdHlwZW9mIHRhZy52YWx1ZSAhPT0gXCJzdHJpbmdcIikge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIHRhZ3Muc2V0KHRhZy5rZXksIHRhZy52YWx1ZSk7XG4gICAgfVxuICAgIHJldHVybiB0YWdzO1xuICB9XG4gIGlmICghb2JqZWN0LmlzUmVjb3JkKHJhdykpIHJldHVybiB0YWdzO1xuICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhyYXcpKSB7XG4gICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gXCJzdHJpbmdcIikgdGFncy5zZXQoa2V5LCB2YWx1ZSk7XG4gIH1cbiAgcmV0dXJuIHRhZ3M7XG59XG5cbi8qKlxuICogVUMgdGFibGUgcHJlZml4IHRoZSBhc3Nlc3NtZW50cyBBUEkgbmVlZHMsIHJlYWQgZnJvbSBleHBlcmltZW50IHRhZ3MuXG4gKlxuICogUHJlZmVycyB0aGUgdGhyZWUtcGFydCBkZXN0aW5hdGlvbiBwYXRoIERhdGFicmlja3Mgd3JpdGVzIHdoZW4gdGhlXG4gKiBleHBlcmltZW50IGlzIGxpbmtlZCB0byBhIHRhYmxlLXByZWZpeCBsb2NhdGlvbi4gRmFsbHMgYmFjayB0byBzdHJpcHBpbmdcbiAqIGBfb3RlbF9zcGFuc2AgZnJvbSB0aGUgc3BhbnMtdGFibGUgdGFnLiBUd28tcGFydCBzY2hlbWEtbGlua2VkIGRlc3RpbmF0aW9uc1xuICogYXJlIGlnbm9yZWQgKHRoZXkgYXJlIG5vdCBhIHRhYmxlIHByZWZpeCkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB1Y1RyYWNlUHJlZml4RnJvbUV4cGVyaW1lbnRUYWdzKHJhd1RhZ3M6IHVua25vd24pOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICBjb25zdCB0YWdzID0gZXhwZXJpbWVudFRhZ01hcChyYXdUYWdzKTtcbiAgY29uc3QgZGVzdGluYXRpb24gPSBub3JtYWxpemVVY1ByZWZpeCh0YWdzLmdldChUUkFDRV9ERVNUSU5BVElPTl9QQVRIX1RBRykpO1xuICBpZiAoZGVzdGluYXRpb24gJiYgZGVzdGluYXRpb24uc3BsaXQoXCIuXCIpLmxlbmd0aCA+PSAzKSByZXR1cm4gZGVzdGluYXRpb247XG4gIHJldHVybiBwcmVmaXhGcm9tU3BhblRhYmxlKHRhZ3MuZ2V0KFRSQUNFX1NQQU5fU1RPUkFHRV9UQUJMRV9UQUcpKTtcbn1cblxuLyoqIERyb3AgdGhlIGluLXByb2Nlc3MgVUMgcHJlZml4IGNhY2hlICh0ZXN0cykuICovXG5leHBvcnQgZnVuY3Rpb24gcmVzZXRVY1RyYWNlUHJlZml4Q2FjaGUoKTogdm9pZCB7XG4gIGxvYWRlZFVjUHJlZml4ID0gdW5kZWZpbmVkO1xufVxuXG4vKipcbiAqIFRyYWNlIGlkIHRoZSBhc3Nlc3NtZW50cyBSRVNUIEFQSSBhY2NlcHRzIGZvciB0aGlzIGRlcGxveW1lbnQuXG4gKlxuICogRGF0YWJyaWNrcyBBcHBzIHBlcnNpc3QgdHJhY2VzIGluIFVuaXR5IENhdGFsb2cuIFRoZSBleHBlcmltZW50IFVJIGxpc3RzXG4gKiB0aG9zZSByb3dzLCBidXQgYFBPU1QgL2FwaS8zLjAvbWxmbG93L3RyYWNlcy97aWR9L2Fzc2Vzc21lbnRzYCBsb29rcyB1cCB0aGVcbiAqIGV4cGVyaW1lbnQgc3RvcmUgdW5sZXNzIGB7aWR9YCBpcyB0aGUgVUMgVVJJIGB0cmFjZTove2NhdGFsb2d9LntzY2hlbWF9LntwcmVmaXh9L3t0ci1oZXh9YC5cbiAqIEEgYmFyZSBgdHItPGhleD5gIDQwNHMgYW5kIHRoZSBhc3Nlc3NtZW50IG5ldmVyIGFwcGVhcnMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBtbGZsb3dBc3Nlc3NtZW50VHJhY2VJZCh0cmFjZUlkOiBzdHJpbmcsIHByZWZpeD86IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IHJlc29sdmVkID0gbm9ybWFsaXplVWNQcmVmaXgocHJlZml4KTtcbiAgcmV0dXJuIHJlc29sdmVkID8gYHRyYWNlOi8ke3Jlc29sdmVkfS8ke3RyYWNlSWR9YCA6IHRyYWNlSWQ7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHJlc29sdmVVY1RyYWNlUHJlZml4KGNsaWVudDogV29ya3NwYWNlQ2xpZW50KTogUHJvbWlzZTxzdHJpbmcgfCB1bmRlZmluZWQ+IHtcbiAgY29uc3QgZnJvbUVudiA9IG5vcm1hbGl6ZVVjUHJlZml4KHByb2Nlc3MuZW52Lk1MRkxPV19VQ19UUkFDRV9QUkVGSVgpO1xuICBpZiAoZnJvbUVudikgcmV0dXJuIGZyb21FbnY7XG4gIGlmIChsb2FkZWRVY1ByZWZpeCkgcmV0dXJuIGxvYWRlZFVjUHJlZml4LnByZWZpeDtcbiAgY29uc3QgZXhwZXJpbWVudCA9IGF3YWl0IGZldGNoRXhwZXJpbWVudChjbGllbnQpO1xuICBpZiAoIWV4cGVyaW1lbnQpIHJldHVybiB1bmRlZmluZWQ7XG4gIGNvbnN0IHByZWZpeCA9IHVjVHJhY2VQcmVmaXhGcm9tRXhwZXJpbWVudFRhZ3MoZXhwZXJpbWVudC50YWdzKTtcbiAgbG9hZGVkVWNQcmVmaXggPSB7IHByZWZpeCB9O1xuICByZXR1cm4gcHJlZml4O1xufVxuXG4vKiogQXNzZXNzbWVudHMgUkVTVCBwYXRoIGZvciBhIHRyYWNlLiBgMy4wYCBpcyB0aGUgY3VycmVudCBNTGZsb3cgQVBJIHZlcnNpb24uICovXG5jb25zdCBhc3Nlc3NtZW50c1BhdGggPSAodHJhY2VJZDogc3RyaW5nKTogc3RyaW5nID0+XG4gIGAvYXBpLzMuMC9tbGZsb3cvdHJhY2VzLyR7ZW5jb2RlVVJJQ29tcG9uZW50KHRyYWNlSWQpfS9hc3Nlc3NtZW50c2A7XG5cbi8qKiBOdW1iZXIgb2YgdGltZXMgdG8gcmV0cnkgYSBcInRyYWNlIG5vdCBmb3VuZFwiIHJlc3BvbnNlIGJlZm9yZSBnaXZpbmcgdXAuICovXG5jb25zdCBOT1RfRk9VTkRfUkVUUklFUyA9IDM7XG4vKiogQmFzZSBiYWNrb2ZmIGJldHdlZW4gXCJ0cmFjZSBub3QgZm91bmRcIiByZXRyaWVzLCBpbiBtcyAoZ3Jvd3MgbGluZWFybHkpLiAqL1xuY29uc3QgTk9UX0ZPVU5EX0JBQ0tPRkZfTVMgPSAxMjAwO1xuXG5jb25zdCBNbGZsb3dFeHBlcmltZW50UmVzcG9uc2VTY2hlbWEgPSB6XG4gIC5vYmplY3Qoe1xuICAgIGV4cGVyaW1lbnQ6IHpcbiAgICAgIC5vYmplY3Qoe1xuICAgICAgICBleHBlcmltZW50X2lkOiB6LnN0cmluZygpLm1pbigxKS5kZXNjcmliZShcIldvcmtzcGFjZSBNTGZsb3cgZXhwZXJpbWVudCBpZGVudGlmaWVyLlwiKSxcbiAgICAgICAgdGFnczogei51bmtub3duKCkub3B0aW9uYWwoKS5kZXNjcmliZShcIkV4cGVyaW1lbnQgdGFncywgaW5jbHVkaW5nIHRoZSBVQyB0cmFjZSBsb2NhdGlvbi5cIiksXG4gICAgICB9KVxuICAgICAgLnBhc3N0aHJvdWdoKClcbiAgICAgIC5kZXNjcmliZShcIkNvbmZpZ3VyZWQgTUxmbG93IGV4cGVyaW1lbnQuXCIpLFxuICB9KVxuICAucGFzc3Rocm91Z2goKVxuICAuZGVzY3JpYmUoXCJNTGZsb3cgZ2V0LWV4cGVyaW1lbnQgcmVzcG9uc2UuXCIpO1xuXG5jb25zdCBDdXJyZW50VXNlclJlc3BvbnNlU2NoZW1hID0gelxuICAub2JqZWN0KHtcbiAgICB1c2VyTmFtZTogei5zdHJpbmcoKS5vcHRpb25hbCgpLmRlc2NyaWJlKFwiQ3VycmVudCB3b3Jrc3BhY2UgdXNlciBuYW1lLlwiKSxcbiAgICBhcHBsaWNhdGlvbklkOiB6LnN0cmluZygpLm9wdGlvbmFsKCkuZGVzY3JpYmUoXCJDdXJyZW50IHNlcnZpY2UgcHJpbmNpcGFsIGFwcGxpY2F0aW9uIGlkLlwiKSxcbiAgICBkaXNwbGF5TmFtZTogei5zdHJpbmcoKS5vcHRpb25hbCgpLmRlc2NyaWJlKFwiQ3VycmVudCBwcmluY2lwYWwgZGlzcGxheSBuYW1lLlwiKSxcbiAgICBncm91cHM6IHpcbiAgICAgIC5hcnJheShcbiAgICAgICAgelxuICAgICAgICAgIC5vYmplY3Qoe1xuICAgICAgICAgICAgdmFsdWU6IHouc3RyaW5nKCkub3B0aW9uYWwoKS5kZXNjcmliZShcIldvcmtzcGFjZSBncm91cCBpZGVudGlmaWVyLlwiKSxcbiAgICAgICAgICAgIGRpc3BsYXk6IHouc3RyaW5nKCkub3B0aW9uYWwoKS5kZXNjcmliZShcIldvcmtzcGFjZSBncm91cCBkaXNwbGF5IG5hbWUuXCIpLFxuICAgICAgICAgIH0pXG4gICAgICAgICAgLnBhc3N0aHJvdWdoKCksXG4gICAgICApXG4gICAgICAub3B0aW9uYWwoKVxuICAgICAgLmRlc2NyaWJlKFwiR3JvdXBzIGFzc2lnbmVkIHRvIHRoZSBjdXJyZW50IHByaW5jaXBhbC5cIiksXG4gIH0pXG4gIC5wYXNzdGhyb3VnaCgpXG4gIC5kZXNjcmliZShcIkRhdGFicmlja3MgQ3VycmVudCBVc2VyIEFQSSByZXNwb25zZS5cIik7XG5cbmNvbnN0IEV4cGVyaW1lbnRQZXJtaXNzaW9uc1Jlc3BvbnNlU2NoZW1hID0gelxuICAub2JqZWN0KHtcbiAgICBhY2Nlc3NfY29udHJvbF9saXN0OiB6XG4gICAgICAuYXJyYXkoXG4gICAgICAgIHpcbiAgICAgICAgICAub2JqZWN0KHtcbiAgICAgICAgICAgIHVzZXJfbmFtZTogei5zdHJpbmcoKS5vcHRpb25hbCgpLmRlc2NyaWJlKFwiVXNlciBwcmluY2lwYWwgbmFtZS5cIiksXG4gICAgICAgICAgICBncm91cF9uYW1lOiB6LnN0cmluZygpLm9wdGlvbmFsKCkuZGVzY3JpYmUoXCJHcm91cCBwcmluY2lwYWwgbmFtZS5cIiksXG4gICAgICAgICAgICBzZXJ2aWNlX3ByaW5jaXBhbF9uYW1lOiB6XG4gICAgICAgICAgICAgIC5zdHJpbmcoKVxuICAgICAgICAgICAgICAub3B0aW9uYWwoKVxuICAgICAgICAgICAgICAuZGVzY3JpYmUoXCJTZXJ2aWNlIHByaW5jaXBhbCBhcHBsaWNhdGlvbiBpZC5cIiksXG4gICAgICAgICAgICBhbGxfcGVybWlzc2lvbnM6IHpcbiAgICAgICAgICAgICAgLmFycmF5KFxuICAgICAgICAgICAgICAgIHpcbiAgICAgICAgICAgICAgICAgIC5vYmplY3Qoe1xuICAgICAgICAgICAgICAgICAgICBwZXJtaXNzaW9uX2xldmVsOiB6LnN0cmluZygpLmRlc2NyaWJlKFwiRWZmZWN0aXZlIGV4cGVyaW1lbnQgcGVybWlzc2lvbiBsZXZlbC5cIiksXG4gICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgLnBhc3N0aHJvdWdoKCksXG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgLm9wdGlvbmFsKClcbiAgICAgICAgICAgICAgLmRlc2NyaWJlKFwiRGlyZWN0IGFuZCBpbmhlcml0ZWQgcGVybWlzc2lvbnMgZm9yIHRoaXMgcHJpbmNpcGFsLlwiKSxcbiAgICAgICAgICB9KVxuICAgICAgICAgIC5wYXNzdGhyb3VnaCgpLFxuICAgICAgKVxuICAgICAgLm9wdGlvbmFsKClcbiAgICAgIC5kZXNjcmliZShcIkV4cGVyaW1lbnQgYWNjZXNzIGNvbnRyb2wgZW50cmllcy5cIiksXG4gIH0pXG4gIC5wYXNzdGhyb3VnaCgpXG4gIC5kZXNjcmliZShcIkRhdGFicmlja3MgZXhwZXJpbWVudCBwZXJtaXNzaW9ucyByZXNwb25zZS5cIik7XG5cbmZ1bmN0aW9uIGNvbmZpZ3VyZWRFeHBlcmltZW50KCk6IHsgaWQ/OiBzdHJpbmc7IG5hbWU/OiBzdHJpbmcgfSB7XG4gIGNvbnN0IGlkID0gcHJvY2Vzcy5lbnYuTUxGTE9XX0VYUEVSSU1FTlRfSUQ/LnRyaW0oKTtcbiAgY29uc3QgbmFtZSA9IHByb2Nlc3MuZW52Lk1MRkxPV19FWFBFUklNRU5UX05BTUU/LnRyaW0oKTtcbiAgcmV0dXJuIHtcbiAgICAuLi4oaWQgPyB7IGlkIH0gOiB7fSksXG4gICAgLi4uKG5hbWUgPyB7IG5hbWUgfSA6IHt9KSxcbiAgfTtcbn1cblxuLyoqXG4gKiBXaGV0aGVyIE1MZmxvdyBmZWVkYmFjayBsb2dnaW5nIGlzIGF2YWlsYWJsZSBmb3IgdGhpcyBkZXBsb3ltZW50LlxuICpcbiAqIEVuYWJsZWQgd2hlbiBhbiBPVExQIGV4cG9ydGVyIGVuZHBvaW50IGlzIGNvbmZpZ3VyZWQgKHRyYWNlcyBhcmVcbiAqIGFjdHVhbGx5IHNoaXBwZWQgc29tZXdoZXJlKSBBTkQgYW4gTUxmbG93IGV4cGVyaW1lbnQgaXMgbmFtZWQgLSB0aGVcbiAqIHR3byBzaWduYWxzIHRoYXQgdGhlIE9UTFAgYmFja2VuZCBpcyBNTGZsb3cgYW5kIHRyYWNlcyB3aWxsXG4gKiBtYXRlcmlhbGl6ZSB0aGVyZS4gQm90aCBhcmUgc3RhbmRhcmQgZW52IHZhcnMsIHNvIG5vIHBsdWdpbiBjb25maWcgaXNcbiAqIHJlcXVpcmVkOyBhIGRlcGxveW1lbnQgb3B0cyBpbiBzaW1wbHkgYnkgd2lyaW5nIE1MZmxvdyB0cmFjaW5nLlxuICovXG5leHBvcnQgZnVuY3Rpb24gbWxmbG93RW5hYmxlZCgpOiBib29sZWFuIHtcbiAgY29uc3QgaGFzRXhwb3J0ZXIgPSBCb29sZWFuKFxuICAgIHByb2Nlc3MuZW52Lk9URUxfRVhQT1JURVJfT1RMUF9FTkRQT0lOVD8udHJpbSgpIHx8XG4gICAgcHJvY2Vzcy5lbnYuT1RFTF9FWFBPUlRFUl9PVExQX1RSQUNFU19FTkRQT0lOVD8udHJpbSgpLFxuICApO1xuICBjb25zdCBoYXNFeHBlcmltZW50ID0gQm9vbGVhbihcbiAgICBwcm9jZXNzLmVudi5NTEZMT1dfRVhQRVJJTUVOVF9JRD8udHJpbSgpIHx8IHByb2Nlc3MuZW52Lk1MRkxPV19FWFBFUklNRU5UX05BTUU/LnRyaW0oKSxcbiAgKTtcbiAgcmV0dXJuIGhhc0V4cG9ydGVyICYmIGhhc0V4cGVyaW1lbnQ7XG59XG5cbi8qKlxuICogUmVzb2x2ZSB3aGV0aGVyIHVzZXIgZmVlZGJhY2sgaXMgZW5hYmxlZCBmcm9tIHRoZSBwbHVnaW4ncyBvcHRpb25hbFxuICogYGNvbmZpZy5mZWVkYmFja2Agb3ZlcnJpZGU6IHRoZSBleHBsaWNpdCBib29sZWFuIHdpbnMsIG90aGVyd2lzZSBmYWxsXG4gKiBiYWNrIHRvIGF1dG8tZGV0ZWN0aW5nIE1MZmxvdyB0cmFjaW5nICh7QGxpbmsgbWxmbG93RW5hYmxlZH0pLiBTaGFyZWRcbiAqIGJ5IHRoZSBwbHVnaW4ncyBjbGllbnQtY29uZmlnIGdhdGUgYW5kIHRoZSBzZXJ2ZXIncyB0cmFjZS1pZCBoZWFkZXIgc29cbiAqIHRoZSB0d28gbmV2ZXIgZGlzYWdyZWUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNvbHZlRmVlZGJhY2tFbmFibGVkKGV4cGxpY2l0OiBib29sZWFuIHwgdW5kZWZpbmVkKTogYm9vbGVhbiB7XG4gIHJldHVybiBleHBsaWNpdCA/PyBtbGZsb3dFbmFibGVkKCk7XG59XG5cbi8qKlxuICogUmVqZWN0IGFuIGV4cGxpY2l0bHkgZW5hYmxlZCBmZWVkYmFjayBzdXJmYWNlIHdoZW4gbm8gTUxmbG93IGV4cGVyaW1lbnQgaXNcbiAqIGNvbmZpZ3VyZWQuIEF1dG8gbW9kZSByZW1haW5zIG5vbi1mYXRhbCBhbmQgc2ltcGx5IGRpc2FibGVzIGZlZWRiYWNrLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdmFsaWRhdGVGZWVkYmFja0NvbmZpZyhleHBsaWNpdDogYm9vbGVhbiB8IHVuZGVmaW5lZCk6IHZvaWQge1xuICBpZiAoZXhwbGljaXQgIT09IHRydWUpIHJldHVybjtcbiAgY29uc3QgZXhwZXJpbWVudCA9IGNvbmZpZ3VyZWRFeHBlcmltZW50KCk7XG4gIGlmIChleHBlcmltZW50LmlkIHx8IGV4cGVyaW1lbnQubmFtZSkgcmV0dXJuO1xuICB0aHJvdyBuZXcgQ29uZmlndXJhdGlvbkVycm9yKFxuICAgIFwibWFzdHJhOiBmZWVkYmFjayBpcyBlbmFibGVkIGJ1dCBubyBNTGZsb3cgZXhwZXJpbWVudCBpcyBjb25maWd1cmVkLiBcIiArXG4gICAgICBcIlNldCBNTEZMT1dfRVhQRVJJTUVOVF9JRCBvciBNTEZMT1dfRVhQRVJJTUVOVF9OQU1FLCB1c2UgZmVlZGJhY2s6IGZhbHNlLCBcIiArXG4gICAgICBcIm9yIG9taXQgZmVlZGJhY2sgdG8gdXNlIGF1dG9tYXRpYyBkZXRlY3Rpb24uXCIsXG4gICk7XG59XG5cbi8qKlxuICogUmV0dXJuIHRoZSBjb25maWd1cmVkIGV4cGVyaW1lbnQgVVJMIG9ubHkgd2hlbiB0aGUgYWN0aXZlIERhdGFicmlja3NcbiAqIHByaW5jaXBhbCBoYXMgZWZmZWN0aXZlIGBDQU5fTUFOQUdFYCBwZXJtaXNzaW9uLiBQZXJtaXNzaW9uIGxvb2t1cCBpc1xuICogaW50ZW50aW9uYWxseSBiZXN0LWVmZm9ydCBzbyBBcHBzIGVudmlyb25tZW50cyB0aGF0IGRvIG5vdCBleHBvc2UgdGhlXG4gKiByZXF1aXJlZCBBUElzIHNpbXBseSBvbWl0IHRoZSBkZWJ1ZyBhZmZvcmRhbmNlLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbWxmbG93RXhwZXJpbWVudE1hbmFnZXJVcmwoXG4gIGNsaWVudDogV29ya3NwYWNlQ2xpZW50LFxuKTogUHJvbWlzZTxzdHJpbmcgfCB1bmRlZmluZWQ+IHtcbiAgdHJ5IHtcbiAgICByZXR1cm4gYXdhaXQgcmVzb2x2ZU1sZmxvd0V4cGVyaW1lbnRNYW5hZ2VyVXJsKGNsaWVudCk7XG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gcmVzb2x2ZU1sZmxvd0V4cGVyaW1lbnRNYW5hZ2VyVXJsKFxuICBjbGllbnQ6IFdvcmtzcGFjZUNsaWVudCxcbik6IFByb21pc2U8c3RyaW5nIHwgdW5kZWZpbmVkPiB7XG4gIGNvbnN0IGV4cGVyaW1lbnRJZCA9IGF3YWl0IHJlc29sdmVFeHBlcmltZW50SWQoY2xpZW50KTtcbiAgaWYgKCFleHBlcmltZW50SWQpIHJldHVybiB1bmRlZmluZWQ7XG5cbiAgY29uc3QgW2N1cnJlbnRSZXNwb25zZSwgcGVybWlzc2lvbnNSZXNwb25zZV0gPSBhd2FpdCBQcm9taXNlLmFsbChbXG4gICAgZGF0YWJyaWNrc0ZldGNoKGNsaWVudCwgXCIvYXBpLzIuMC9wcmV2aWV3L3NjaW0vdjIvTWVcIiwgeyBtZXRob2Q6IFwiR0VUXCIgfSksXG4gICAgZGF0YWJyaWNrc0ZldGNoKFxuICAgICAgY2xpZW50LFxuICAgICAgYC9hcGkvMi4wL3Blcm1pc3Npb25zL2V4cGVyaW1lbnRzLyR7ZW5jb2RlVVJJQ29tcG9uZW50KGV4cGVyaW1lbnRJZCl9YCxcbiAgICAgIHsgbWV0aG9kOiBcIkdFVFwiIH0sXG4gICAgKSxcbiAgXSk7XG4gIGlmICghY3VycmVudFJlc3BvbnNlLm9rIHx8ICFwZXJtaXNzaW9uc1Jlc3BvbnNlLm9rKSByZXR1cm4gdW5kZWZpbmVkO1xuXG4gIGNvbnN0IGN1cnJlbnQgPSBDdXJyZW50VXNlclJlc3BvbnNlU2NoZW1hLnNhZmVQYXJzZShhd2FpdCByZWFkUmVzcG9uc2VKc29uKGN1cnJlbnRSZXNwb25zZSkpO1xuICBjb25zdCBwZXJtaXNzaW9ucyA9IEV4cGVyaW1lbnRQZXJtaXNzaW9uc1Jlc3BvbnNlU2NoZW1hLnNhZmVQYXJzZShcbiAgICBhd2FpdCByZWFkUmVzcG9uc2VKc29uKHBlcm1pc3Npb25zUmVzcG9uc2UpLFxuICApO1xuICBpZiAoIWN1cnJlbnQuc3VjY2VzcyB8fCAhcGVybWlzc2lvbnMuc3VjY2VzcykgcmV0dXJuIHVuZGVmaW5lZDtcblxuICBjb25zdCB1c2VyTmFtZXMgPSBuZXcgU2V0KFxuICAgIFtjdXJyZW50LmRhdGEudXNlck5hbWUsIGN1cnJlbnQuZGF0YS5hcHBsaWNhdGlvbklkLCBjdXJyZW50LmRhdGEuZGlzcGxheU5hbWVdLmZpbHRlcihcbiAgICAgICh2YWx1ZSk6IHZhbHVlIGlzIHN0cmluZyA9PiBCb29sZWFuKHZhbHVlKSxcbiAgICApLFxuICApO1xuICBjb25zdCBncm91cE5hbWVzID0gbmV3IFNldChcbiAgICAoY3VycmVudC5kYXRhLmdyb3VwcyA/PyBbXSkuZmxhdE1hcCgoeyB2YWx1ZSwgZGlzcGxheSB9KSA9PlxuICAgICAgW3ZhbHVlLCBkaXNwbGF5XS5maWx0ZXIoKGVudHJ5KTogZW50cnkgaXMgc3RyaW5nID0+IEJvb2xlYW4oZW50cnkpKSxcbiAgICApLFxuICApO1xuICBjb25zdCBjYW5NYW5hZ2UgPSAocGVybWlzc2lvbnMuZGF0YS5hY2Nlc3NfY29udHJvbF9saXN0ID8/IFtdKS5zb21lKChlbnRyeSkgPT4ge1xuICAgIGNvbnN0IG1hdGNoZXMgPVxuICAgICAgKGVudHJ5LnVzZXJfbmFtZSAhPT0gdW5kZWZpbmVkICYmIHVzZXJOYW1lcy5oYXMoZW50cnkudXNlcl9uYW1lKSkgfHxcbiAgICAgIChlbnRyeS5zZXJ2aWNlX3ByaW5jaXBhbF9uYW1lICE9PSB1bmRlZmluZWQgJiYgdXNlck5hbWVzLmhhcyhlbnRyeS5zZXJ2aWNlX3ByaW5jaXBhbF9uYW1lKSkgfHxcbiAgICAgIChlbnRyeS5ncm91cF9uYW1lICE9PSB1bmRlZmluZWQgJiYgZ3JvdXBOYW1lcy5oYXMoZW50cnkuZ3JvdXBfbmFtZSkpO1xuICAgIHJldHVybiAoXG4gICAgICBtYXRjaGVzICYmXG4gICAgICAoZW50cnkuYWxsX3Blcm1pc3Npb25zID8/IFtdKS5zb21lKFxuICAgICAgICAocGVybWlzc2lvbikgPT4gcGVybWlzc2lvbi5wZXJtaXNzaW9uX2xldmVsID09PSBcIkNBTl9NQU5BR0VcIixcbiAgICAgIClcbiAgICApO1xuICB9KTtcbiAgaWYgKCFjYW5NYW5hZ2UpIHJldHVybiB1bmRlZmluZWQ7XG5cbiAgY29uc3QgaG9zdCA9IChhd2FpdCBjbGllbnQuY29uZmlnLmdldEhvc3QoKSkudG9TdHJpbmcoKTtcbiAgcmV0dXJuIG5ldyBVUkwoYC9tbC9leHBlcmltZW50cy8ke2VuY29kZVVSSUNvbXBvbmVudChleHBlcmltZW50SWQpfWAsIGhvc3QpLnRvU3RyaW5nKCk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGZldGNoRXhwZXJpbWVudChcbiAgY2xpZW50OiBXb3Jrc3BhY2VDbGllbnQsXG4pOiBQcm9taXNlPHsgZXhwZXJpbWVudF9pZDogc3RyaW5nOyB0YWdzPzogdW5rbm93biB9IHwgdW5kZWZpbmVkPiB7XG4gIGNvbnN0IGV4cGVyaW1lbnQgPSBjb25maWd1cmVkRXhwZXJpbWVudCgpO1xuICBjb25zdCBwYXRoID0gZXhwZXJpbWVudC5pZFxuICAgID8gYC9hcGkvMi4wL21sZmxvdy9leHBlcmltZW50cy9nZXQ/ZXhwZXJpbWVudF9pZD0ke2VuY29kZVVSSUNvbXBvbmVudChleHBlcmltZW50LmlkKX1gXG4gICAgOiBleHBlcmltZW50Lm5hbWVcbiAgICAgID8gYC9hcGkvMi4wL21sZmxvdy9leHBlcmltZW50cy9nZXQtYnktbmFtZT9leHBlcmltZW50X25hbWU9JHtlbmNvZGVVUklDb21wb25lbnQoXG4gICAgICAgICAgZXhwZXJpbWVudC5uYW1lLFxuICAgICAgICApfWBcbiAgICAgIDogdW5kZWZpbmVkO1xuICBpZiAoIXBhdGgpIHJldHVybiB1bmRlZmluZWQ7XG4gIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZGF0YWJyaWNrc0ZldGNoKGNsaWVudCwgcGF0aCwgeyBtZXRob2Q6IFwiR0VUXCIgfSk7XG4gIGlmICghcmVzcG9uc2Uub2spIHJldHVybiB1bmRlZmluZWQ7XG4gIGNvbnN0IHBhcnNlZCA9IE1sZmxvd0V4cGVyaW1lbnRSZXNwb25zZVNjaGVtYS5zYWZlUGFyc2UoYXdhaXQgcmVhZFJlc3BvbnNlSnNvbihyZXNwb25zZSkpO1xuICByZXR1cm4gcGFyc2VkLnN1Y2Nlc3MgPyBwYXJzZWQuZGF0YS5leHBlcmltZW50IDogdW5kZWZpbmVkO1xufVxuXG5hc3luYyBmdW5jdGlvbiByZXNvbHZlRXhwZXJpbWVudElkKGNsaWVudDogV29ya3NwYWNlQ2xpZW50KTogUHJvbWlzZTxzdHJpbmcgfCB1bmRlZmluZWQ+IHtcbiAgY29uc3QgY29uZmlndXJlZCA9IGNvbmZpZ3VyZWRFeHBlcmltZW50KCk7XG4gIGlmIChjb25maWd1cmVkLmlkKSByZXR1cm4gY29uZmlndXJlZC5pZDtcbiAgcmV0dXJuIChhd2FpdCBmZXRjaEV4cGVyaW1lbnQoY2xpZW50KSk/LmV4cGVyaW1lbnRfaWQ7XG59XG5cbi8qKiBQYXJhbWV0ZXJzIGZvciB7QGxpbmsgbG9nRmVlZGJhY2t9LiAqL1xuZXhwb3J0IGludGVyZmFjZSBMb2dGZWVkYmFja1BhcmFtcyB7XG4gIC8qKiBNTGZsb3cgdHJhY2UgaWQgdGhlIGFzc2Vzc21lbnQgYXR0YWNoZXMgdG8gKGB0ci08aGV4PmApLiAqL1xuICB0cmFjZUlkOiBzdHJpbmc7XG4gIC8qKiBBc3Nlc3NtZW50IG5hbWU7IGRlZmF1bHRzIHBlciB3aGV0aGVyIGEgdmFsdWUgb3IgYSBjb21tZW50IGlzIHNlbnQuICovXG4gIG5hbWU/OiBzdHJpbmc7XG4gIC8qKiBUaHVtYnMgLyByYXRpbmcgLyBsYWJlbCB2YWx1ZS4gT21pdCBmb3IgYSBjb21tZW50LW9ubHkgc3VibWlzc2lvbi4gKi9cbiAgdmFsdWU/OiBib29sZWFuIHwgbnVtYmVyIHwgc3RyaW5nO1xuICAvKiogRnJlZWZvcm0gY29tbWVudDogdGhlIHJhdGlvbmFsZSBhbG9uZ3NpZGUgYSB2YWx1ZSwgb3IgdGhlIHZhbHVlIGl0c2VsZiB3aGVuIG5vbmUuICovXG4gIGNvbW1lbnQ/OiBzdHJpbmc7XG4gIC8qKiBJZGVudGl0eSB0aGUgZmVlZGJhY2sgaXMgYXR0cmlidXRlZCB0byAodXNlciBlbWFpbCAvIGlkKS4gKi9cbiAgc291cmNlSWQ/OiBzdHJpbmc7XG59XG5cbi8qKlxuICogTG9nIGEgSFVNQU4gZmVlZGJhY2sgYXNzZXNzbWVudCB0byBhIHRyYWNlLiBSZXR1cm5zIHRoZSBjcmVhdGVkXG4gKiBhc3Nlc3NtZW50IGlkIG9uIHN1Y2Nlc3MsIG9yIGB1bmRlZmluZWRgIHdoZW4gdGhlIHRyYWNlIGNhbid0IGJlXG4gKiBmb3VuZCAoZXZlbiBhZnRlciByZXRyeWluZyBmb3IgZXhwb3J0IGxhZykgb3IgdGhlIHJlcXVlc3Qgb3RoZXJ3aXNlXG4gKiBmYWlscyAtIGNhbGxlcnMgc3VyZmFjZSB0aGF0IGFzIGEgc29mdCBcIm5vdCByZWNvcmRlZFwiIHJhdGhlciB0aGFuIGFuXG4gKiBlcnJvciwga2VlcGluZyB0aGUgY2hhdCB1c2FibGUuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBsb2dGZWVkYmFjayhcbiAgY2xpZW50OiBXb3Jrc3BhY2VDbGllbnQsXG4gIHBhcmFtczogTG9nRmVlZGJhY2tQYXJhbXMsXG4pOiBQcm9taXNlPHN0cmluZyB8IHVuZGVmaW5lZD4ge1xuICAvLyBBIGNvbW1lbnQgd2l0aCBubyB0aHVtYnMgdmFsdWUgaXMgbG9nZ2VkIGFzIHRleHQgZmVlZGJhY2s7IGEgdmFsdWVcbiAgLy8gKHdpdGggYW4gb3B0aW9uYWwgY29tbWVudCBhcyB0aGUgcmF0aW9uYWxlKSBpcyB0aGUgdGh1bWJzIHBhdGguXG4gIGNvbnN0IGhhc1ZhbHVlID0gcGFyYW1zLnZhbHVlICE9PSB1bmRlZmluZWQ7XG4gIGNvbnN0IG5hbWUgPVxuICAgIHBhcmFtcy5uYW1lPy50cmltKCkgfHxcbiAgICAoaGFzVmFsdWUgPyBmZWVkYmFjay5ERUZBVUxUX0ZFRURCQUNLX05BTUUgOiBmZWVkYmFjay5ERUZBVUxUX0NPTU1FTlRfTkFNRSk7XG4gIGNvbnN0IHZhbHVlID0gaGFzVmFsdWUgPyBwYXJhbXMudmFsdWUgOiBwYXJhbXMuY29tbWVudDtcbiAgY29uc3QgYXNzZXNzbWVudFRyYWNlSWQgPSBtbGZsb3dBc3Nlc3NtZW50VHJhY2VJZChcbiAgICBwYXJhbXMudHJhY2VJZCxcbiAgICBhd2FpdCByZXNvbHZlVWNUcmFjZVByZWZpeChjbGllbnQpLFxuICApO1xuICBjb25zdCBhc3Nlc3NtZW50OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHtcbiAgICB0cmFjZV9pZDogYXNzZXNzbWVudFRyYWNlSWQsXG4gICAgYXNzZXNzbWVudF9uYW1lOiBuYW1lLFxuICAgIHNvdXJjZToge1xuICAgICAgc291cmNlX3R5cGU6IFwiSFVNQU5cIixcbiAgICAgIHNvdXJjZV9pZDogcGFyYW1zLnNvdXJjZUlkPy50cmltKCkgfHwgXCJ1c2VyXCIsXG4gICAgfSxcbiAgICBmZWVkYmFjazogeyB2YWx1ZSB9LFxuICAgIC4uLihoYXNWYWx1ZSAmJiBwYXJhbXMuY29tbWVudD8udHJpbSgpID8geyByYXRpb25hbGU6IHBhcmFtcy5jb21tZW50IH0gOiB7fSksXG4gIH07XG4gIGNvbnN0IGJvZHkgPSB7IGFzc2Vzc21lbnQgfTtcblxuICBmb3IgKGxldCBhdHRlbXB0ID0gMDsgYXR0ZW1wdCA8PSBOT1RfRk9VTkRfUkVUUklFUzsgYXR0ZW1wdCsrKSB7XG4gICAgbGV0IHJlczogUmVzcG9uc2U7XG4gICAgdHJ5IHtcbiAgICAgIHJlcyA9IGF3YWl0IGRhdGFicmlja3NGZXRjaChjbGllbnQsIGFzc2Vzc21lbnRzUGF0aChhc3Nlc3NtZW50VHJhY2VJZCksIHtcbiAgICAgICAgbWV0aG9kOiBcIlBPU1RcIixcbiAgICAgICAgYm9keSxcbiAgICAgIH0pO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgbG9nZ2VyLndhcm4oXCJmZWVkYmFjayByZXF1ZXN0IGZhaWxlZFwiLCB7XG4gICAgICAgIHRyYWNlSWQ6IHBhcmFtcy50cmFjZUlkLFxuICAgICAgICBhc3Nlc3NtZW50VHJhY2VJZCxcbiAgICAgICAgZXJyb3I6IGVycm9yVXRpbHMuZXJyb3JNZXNzYWdlKGVyciksXG4gICAgICB9KTtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuICAgIGlmIChyZXMub2spIHtcbiAgICAgIGNvbnN0IHBhcnNlZCA9IGF3YWl0IHJlYWRSZXNwb25zZUpzb24ocmVzKTtcbiAgICAgIGNvbnN0IGFzc2Vzc21lbnRJZCA9XG4gICAgICAgIChwYXJzZWQgYXMgeyBhc3Nlc3NtZW50PzogeyBhc3Nlc3NtZW50X2lkPzogdW5rbm93biB9IH0pPy5hc3Nlc3NtZW50Py5hc3Nlc3NtZW50X2lkID8/XG4gICAgICAgIChwYXJzZWQgYXMgeyBhc3Nlc3NtZW50X2lkPzogdW5rbm93biB9KT8uYXNzZXNzbWVudF9pZDtcbiAgICAgIHJldHVybiB0eXBlb2YgYXNzZXNzbWVudElkID09PSBcInN0cmluZ1wiID8gYXNzZXNzbWVudElkIDogXCJcIjtcbiAgICB9XG4gICAgLy8gVHJhY2UgZXhwb3J0IGlzIGFzeW5jOyBhIGZyZXNoIHRyYWNlIG1heSBub3QgZXhpc3QgeWV0LiBSZXRyeSBhXG4gICAgLy8gZmV3IHRpbWVzIHdpdGggYSBzaG9ydCBiYWNrb2ZmIGJlZm9yZSBnaXZpbmcgdXAgc29mdGx5LlxuICAgIGlmIChyZXMuc3RhdHVzID09PSA0MDQgJiYgYXR0ZW1wdCA8IE5PVF9GT1VORF9SRVRSSUVTKSB7XG4gICAgICBhd2FpdCBhc3luY1V0aWxzLnNsZWVwKE5PVF9GT1VORF9CQUNLT0ZGX01TICogKGF0dGVtcHQgKyAxKSk7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG4gICAgbG9nZ2VyLndhcm4oXCJmZWVkYmFjayBub3QgcmVjb3JkZWRcIiwge1xuICAgICAgdHJhY2VJZDogcGFyYW1zLnRyYWNlSWQsXG4gICAgICBhc3Nlc3NtZW50VHJhY2VJZCxcbiAgICAgIHN0YXR1czogcmVzLnN0YXR1cyxcbiAgICAgIGJvZHk6IGF3YWl0IHJlYWRSZXNwb25zZVRleHQocmVzKSxcbiAgICB9KTtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG4gIHJldHVybiB1bmRlZmluZWQ7XG59XG4iXX0=
@@ -260,6 +260,8 @@ export declare class MastraPlugin extends Plugin<MastraPluginConfig> {
260
260
  private registerSuggestionRoutes;
261
261
  /** Register MLflow feedback submission when configured. */
262
262
  private registerFeedbackRoutes;
263
+ /** Register the best-effort per-viewer MLflow experiment debug link. */
264
+ private registerMlflowExperimentRoute;
263
265
  /** Register the gated Mastra catch-all after every explicit route. */
264
266
  private registerAgentRoutes;
265
267
  /**
@@ -307,6 +309,11 @@ export declare class MastraPlugin extends Plugin<MastraPluginConfig> {
307
309
  * (see {@link logFeedback} in `./mlflow.js`).
308
310
  */
309
311
  private logFeedback;
312
+ /**
313
+ * Resolve an experiment link under the active request identity. The helper
314
+ * returns no URL unless the viewer has effective `CAN_MANAGE` permission.
315
+ */
316
+ private fetchMlflowExperimentUrl;
310
317
  /**
311
318
  * Implementation backing the `data` embed resolver
312
319
  * (`GET /embed/data/:id`). Runs inside the AppKit user-context proxy so