@dbx-tools/appkit-mastra 0.9.140 → 0.9.143

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.
@@ -0,0 +1,322 @@
1
+ /**
2
+ * ChatFast post-turn follow-up suggestion generation.
3
+ *
4
+ * @module
5
+ */
6
+ import { asyncUtils, errorUtils, log, stringUtils } from "@dbx-tools/shared-core";
7
+ import { FOLLOW_UP_SUGGESTIONS_PART_TYPE, FollowUpSuggestionsPartDataSchema, } from "@dbx-tools/shared-mastra/wire";
8
+ import { model } from "@dbx-tools/shared-model";
9
+ import { Agent } from "@mastra/core/agent";
10
+ import { createTool } from "@mastra/core/tools";
11
+ import { z } from "zod";
12
+ import { buildModel } from "./model.js";
13
+ import { TYPOGRAPHY_RULE } from "./style.js";
14
+ const logger = log.logger("mastra/follow-up-suggestions");
15
+ const FOLLOW_UP_MODEL_CLASS = model.ModelClass.ChatFast;
16
+ const DEFAULT_QUESTION_LIMIT = 3;
17
+ const MAX_QUESTION_LIMIT = 10;
18
+ const DEFAULT_TIMEOUT_MS = 10_000;
19
+ const MAX_TURN_CONTEXT_CHARS = 16_000;
20
+ const GENERIC_QUESTIONS = new Set([
21
+ "anything else",
22
+ "can you tell me more",
23
+ "tell me more",
24
+ "what else",
25
+ "what would you like to know",
26
+ ]);
27
+ const ASSISTANT_DIRECTED_QUESTIONS = [
28
+ /^(?:would|do) you (?:like|want|prefer)\b/,
29
+ /^(?:should|shall|can|may) (?:i|we)\b/,
30
+ /^are you interested\b/,
31
+ /^should (?:the |this )?(?:chart|graph|table|visualization|response|output|results?|data)\b/,
32
+ ];
33
+ /** Trusted request-context key carrying the current user message. */
34
+ export const FOLLOW_UP_USER_TEXT_KEY = "mastra__follow_up_user_text";
35
+ /** Trusted request-context key carrying native Genie follow-up questions. */
36
+ export const FOLLOW_UP_NATIVE_QUESTIONS_KEY = "mastra__follow_up_native_questions";
37
+ /** Trusted request-context key coordinating one post-turn stream with its processor. */
38
+ export const FOLLOW_UP_RUN_ID_KEY = "mastra__follow_up_run_id";
39
+ const FollowUpOutputSchema = z
40
+ .object({
41
+ questions: z
42
+ .array(z.string().trim().min(1))
43
+ .max(MAX_QUESTION_LIMIT)
44
+ .describe("Specific next questions grounded in the completed turn."),
45
+ })
46
+ .strict();
47
+ const FollowUpToolInputSchema = z
48
+ .object({
49
+ userText: z.string().trim().min(1).describe("User message that started the completed turn."),
50
+ answer: z.string().trim().min(1).describe("Assistant answer from the completed turn."),
51
+ turnContext: z
52
+ .string()
53
+ .trim()
54
+ .max(MAX_TURN_CONTEXT_CHARS)
55
+ .optional()
56
+ .describe("Relevant tool inputs and outputs from the completed turn."),
57
+ nativeQuestions: z
58
+ .array(z.string().trim().min(1))
59
+ .optional()
60
+ .describe("Existing follow-up questions to avoid repeating."),
61
+ })
62
+ .strict();
63
+ const FOLLOW_UP_INSTRUCTIONS = [
64
+ "Generate concise questions that a user can send to dig further into a completed assistant turn.",
65
+ "Write every item in the user's voice as a message for the assistant to answer when clicked.",
66
+ "Never address the user or ask what they would like, want, prefer, or how the response should be customized.",
67
+ "Every question must be answerable from the same tools and data domain as the completed turn.",
68
+ "Ground questions in the answer and turn tool data, including displayed chart or table values.",
69
+ "Prefer concrete analyses, comparisons, rankings, calculations, filters, or explanations of the result.",
70
+ "For a chart or table, ask about the values and patterns before suggesting presentation changes.",
71
+ "Good examples include 'Which category has the highest value?', 'How large is the gap between the highest and lowest categories?', and 'What pattern stands out in these values?'.",
72
+ "Do not repeat the user's question, the answer, or an existing suggestion.",
73
+ "Do not ask generic filler such as 'Anything else?' or request unsupported data.",
74
+ "Return zero questions when no useful grounded follow-up exists.",
75
+ TYPOGRAPHY_RULE,
76
+ ].join(" ");
77
+ const suggestionAgents = new WeakMap();
78
+ const generatedRunParts = new Map();
79
+ const GENERATED_RUN_PART_TTL_MS = 5 * 60_000;
80
+ /** Resolve disabled, shorthand, and configured suggestion policy forms. */
81
+ export function resolveFollowUpSuggestionsConfig(value) {
82
+ if (!value)
83
+ return undefined;
84
+ const configured = typeof value === "object" ? value : { mode: value };
85
+ return {
86
+ mode: configured.mode === true ? "auto" : configured.mode,
87
+ limit: Math.min(MAX_QUESTION_LIMIT, Math.max(1, configured.limit ?? DEFAULT_QUESTION_LIMIT)),
88
+ timeoutMs: Math.min(30_000, Math.max(1_000, configured.timeoutMs ?? DEFAULT_TIMEOUT_MS)),
89
+ exposeTool: configured.exposeTool ?? false,
90
+ };
91
+ }
92
+ function followUpModel(config) {
93
+ return ({ requestContext }) => buildModel(config, requestContext, { modelClass: FOLLOW_UP_MODEL_CLASS });
94
+ }
95
+ function suggestionAgent(config) {
96
+ let agent = suggestionAgents.get(config);
97
+ if (!agent) {
98
+ agent = new Agent({
99
+ id: "follow-up-suggestions",
100
+ name: "Follow-up suggestions",
101
+ description: "Generates grounded next questions using the fast chat model.",
102
+ instructions: FOLLOW_UP_INSTRUCTIONS,
103
+ model: followUpModel(config),
104
+ });
105
+ suggestionAgents.set(config, agent);
106
+ }
107
+ return agent;
108
+ }
109
+ function normalizedQuestion(value) {
110
+ return value
111
+ .trim()
112
+ .toLowerCase()
113
+ .replace(/[^\p{L}\p{N}\s]/gu, " ")
114
+ .replace(/\s+/g, " ");
115
+ }
116
+ function acceptedQuestions(questions, options, maxQuestions) {
117
+ const excluded = new Set([
118
+ normalizedQuestion(options.userText),
119
+ ...(options.nativeQuestions ?? []).map(normalizedQuestion),
120
+ ]);
121
+ const accepted = [];
122
+ for (const question of questions) {
123
+ const trimmed = question.trim();
124
+ const normalized = normalizedQuestion(trimmed);
125
+ if (!normalized ||
126
+ normalized.length < 8 ||
127
+ excluded.has(normalized) ||
128
+ GENERIC_QUESTIONS.has(normalized) ||
129
+ ASSISTANT_DIRECTED_QUESTIONS.some((pattern) => pattern.test(normalized))) {
130
+ continue;
131
+ }
132
+ excluded.add(normalized);
133
+ accepted.push(trimmed);
134
+ if (accepted.length >= maxQuestions)
135
+ break;
136
+ }
137
+ return accepted;
138
+ }
139
+ function serializedTurnContext(steps) {
140
+ const activity = steps.flatMap((step) => [
141
+ ...step.toolCalls.map((call) => ({
142
+ tool: call.payload.toolName,
143
+ input: call.payload.args,
144
+ })),
145
+ ...step.toolResults.map((result) => ({
146
+ tool: result.payload.toolName,
147
+ output: result.payload.result,
148
+ ...(result.payload.isError ? { isError: true } : {}),
149
+ })),
150
+ ]);
151
+ if (activity.length === 0)
152
+ return undefined;
153
+ try {
154
+ const serialized = JSON.stringify(activity.slice(-12));
155
+ return serialized.length > MAX_TURN_CONTEXT_CHARS
156
+ ? serialized.slice(0, MAX_TURN_CONTEXT_CHARS)
157
+ : serialized;
158
+ }
159
+ catch {
160
+ return undefined;
161
+ }
162
+ }
163
+ /** Generate grounded follow-ups through ChatFast, failing open to an empty list. */
164
+ export async function suggestFollowUps(config, options) {
165
+ const settings = resolveFollowUpSuggestionsConfig(config.followUpSuggestions);
166
+ if (!settings || settings.mode === "genie")
167
+ return [];
168
+ const timeout = AbortSignal.timeout(settings.timeoutMs);
169
+ const abortSignal = asyncUtils.combineAbortSignals(options.abortSignal, timeout);
170
+ const prompt = stringUtils.toDescription({
171
+ "User message": options.userText,
172
+ "Assistant answer": options.answer,
173
+ ...(options.turnContext ? { "Turn tool data": options.turnContext } : {}),
174
+ ...(options.nativeQuestions?.length
175
+ ? { "Existing suggestions": JSON.stringify(options.nativeQuestions) }
176
+ : {}),
177
+ "Maximum questions": String(settings.limit),
178
+ });
179
+ try {
180
+ const result = await suggestionAgent(config).generate(prompt, {
181
+ structuredOutput: { schema: FollowUpOutputSchema },
182
+ ...(options.requestContext ? { requestContext: options.requestContext } : {}),
183
+ abortSignal,
184
+ });
185
+ const parsed = FollowUpOutputSchema.parse(result.object);
186
+ return acceptedQuestions(parsed.questions, options, settings.limit);
187
+ }
188
+ catch (error) {
189
+ if (!options.abortSignal?.aborted) {
190
+ logger.warn("generation-failed", { error: errorUtils.errorMessage(error) });
191
+ }
192
+ return [];
193
+ }
194
+ }
195
+ function lastAssistantResponse(messageList) {
196
+ const messages = messageList.get.response.db();
197
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
198
+ const message = messages[index];
199
+ if (message?.role === "assistant")
200
+ return message;
201
+ }
202
+ return undefined;
203
+ }
204
+ function persistedFollowUpData(messageList) {
205
+ const message = lastAssistantResponse(messageList);
206
+ if (!message)
207
+ return undefined;
208
+ for (const part of message.content.parts) {
209
+ if (part.type !== FOLLOW_UP_SUGGESTIONS_PART_TYPE || !("data" in part))
210
+ continue;
211
+ const parsed = FollowUpSuggestionsPartDataSchema.safeParse(part.data);
212
+ if (parsed.success)
213
+ return parsed.data;
214
+ }
215
+ return undefined;
216
+ }
217
+ function persistFollowUpData(messageList, data) {
218
+ const message = lastAssistantResponse(messageList);
219
+ if (!message)
220
+ return;
221
+ messageList.add({
222
+ id: message.id,
223
+ role: "assistant",
224
+ parts: [{ type: FOLLOW_UP_SUGGESTIONS_PART_TYPE, data }],
225
+ }, "response", { merge: true });
226
+ }
227
+ function rememberGeneratedRunPart(runId, data) {
228
+ if (!runId)
229
+ return;
230
+ generatedRunParts.set(runId, data);
231
+ const timer = setTimeout(() => generatedRunParts.delete(runId), GENERATED_RUN_PART_TTL_MS);
232
+ timer.unref();
233
+ }
234
+ /** Build Mastra's final output processor for configured generated follow-ups. */
235
+ export function buildFollowUpSuggestionsProcessor(config, suggester = suggestFollowUps) {
236
+ const settings = resolveFollowUpSuggestionsConfig(config.followUpSuggestions);
237
+ if (!settings || settings.mode === "genie")
238
+ return undefined;
239
+ return {
240
+ id: "follow-up-suggestions",
241
+ async processOutputResult({ result, messageList, requestContext, abortSignal, }) {
242
+ if (!requestContext || result.finishReason === "abort" || result.finishReason === "error") {
243
+ return messageList;
244
+ }
245
+ const runIdValue = requestContext.getRaw(FOLLOW_UP_RUN_ID_KEY);
246
+ const runId = typeof runIdValue === "string" ? runIdValue : undefined;
247
+ const persisted = persistedFollowUpData(messageList);
248
+ if (persisted) {
249
+ rememberGeneratedRunPart(runId, persisted);
250
+ return messageList;
251
+ }
252
+ const answer = result.text.trim();
253
+ const userText = requestContext.getRaw(FOLLOW_UP_USER_TEXT_KEY);
254
+ if (!answer || typeof userText !== "string")
255
+ return messageList;
256
+ const nativeValue = requestContext.getRaw(FOLLOW_UP_NATIVE_QUESTIONS_KEY);
257
+ const nativeQuestions = settings.mode === "auto" && Array.isArray(nativeValue)
258
+ ? nativeValue.filter((value) => typeof value === "string")
259
+ : [];
260
+ const turnContext = serializedTurnContext(result.steps);
261
+ const suggestionOptions = {
262
+ userText,
263
+ answer,
264
+ ...(turnContext ? { turnContext } : {}),
265
+ ...(nativeQuestions.length > 0 ? { nativeQuestions } : {}),
266
+ requestContext,
267
+ ...(abortSignal ? { abortSignal } : {}),
268
+ };
269
+ const suggested = await suggester(config, suggestionOptions);
270
+ const questions = acceptedQuestions(suggested, suggestionOptions, settings.limit);
271
+ if (questions.length === 0)
272
+ return messageList;
273
+ const data = FollowUpSuggestionsPartDataSchema.parse({
274
+ questions,
275
+ source: "model",
276
+ });
277
+ persistFollowUpData(messageList, data);
278
+ rememberGeneratedRunPart(runId, data);
279
+ return messageList;
280
+ },
281
+ };
282
+ }
283
+ /** Inject the processor's persisted data part before Mastra's finish chunk. */
284
+ export function buildFollowUpSuggestionsTransform(runId) {
285
+ return () => new TransformStream({
286
+ transform(chunk, controller) {
287
+ if (chunk.type === "finish") {
288
+ const data = generatedRunParts.get(runId);
289
+ if (data) {
290
+ const part = {
291
+ type: FOLLOW_UP_SUGGESTIONS_PART_TYPE,
292
+ data,
293
+ };
294
+ controller.enqueue(part);
295
+ }
296
+ }
297
+ controller.enqueue(chunk);
298
+ },
299
+ });
300
+ }
301
+ /** Build the optional explicitly callable follow-up suggestion tool. */
302
+ export function buildSuggestFollowUpsTool(config) {
303
+ return createTool({
304
+ id: "suggest_follow_ups",
305
+ description: "Generate a short list of grounded follow-up questions for a completed user message and answer.",
306
+ inputSchema: FollowUpToolInputSchema,
307
+ outputSchema: FollowUpOutputSchema,
308
+ execute: async (input, context) => {
309
+ const values = FollowUpToolInputSchema.parse(input);
310
+ const questions = await suggestFollowUps(config, {
311
+ userText: values.userText,
312
+ answer: values.answer,
313
+ ...(values.turnContext ? { turnContext: values.turnContext } : {}),
314
+ ...(values.nativeQuestions ? { nativeQuestions: values.nativeQuestions } : {}),
315
+ ...(context?.requestContext ? { requestContext: context.requestContext } : {}),
316
+ ...(context?.abortSignal ? { abortSignal: context.abortSignal } : {}),
317
+ });
318
+ return { questions };
319
+ },
320
+ });
321
+ }
322
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9sbG93LXVwLXN1Z2dlc3Rpb25zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2ZvbGxvdy11cC1zdWdnZXN0aW9ucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7OztHQUlHO0FBRUgsT0FBTyxFQUFFLFVBQVUsRUFBRSxVQUFVLEVBQUUsR0FBRyxFQUFFLFdBQVcsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ2xGLE9BQU8sRUFDTCwrQkFBK0IsRUFFL0IsaUNBQWlDLEdBQ2xDLE1BQU0sK0JBQStCLENBQUM7QUFDdkMsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ2hELE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQU0zQyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDaEQsT0FBTyxFQUFFLENBQUMsRUFBRSxNQUFNLEtBQUssQ0FBQztBQUd4QixPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFFN0MsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO0FBQzFELE1BQU0scUJBQXFCLEdBQUcsS0FBSyxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUM7QUFDeEQsTUFBTSxzQkFBc0IsR0FBRyxDQUFDLENBQUM7QUFDakMsTUFBTSxrQkFBa0IsR0FBRyxFQUFFLENBQUM7QUFDOUIsTUFBTSxrQkFBa0IsR0FBRyxNQUFNLENBQUM7QUFDbEMsTUFBTSxzQkFBc0IsR0FBRyxNQUFNLENBQUM7QUFDdEMsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLEdBQUcsQ0FBQztJQUNoQyxlQUFlO0lBQ2Ysc0JBQXNCO0lBQ3RCLGNBQWM7SUFDZCxXQUFXO0lBQ1gsNkJBQTZCO0NBQzlCLENBQUMsQ0FBQztBQUNILE1BQU0sNEJBQTRC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QUFHLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsRUFBRSxXQUFXLEVBQUUsTUFBTSxDQUFDLFdBQVcsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7Z0JBQ2xFLEdBQUcsQ0FBQyxNQUFNLENBQUMsZUFBZSxDQUFDLENBQUMsQ0FBQyxFQUFFLGVBQWUsRUFBRSxNQUFNLENBQUMsZUFBZSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztnQkFDOUUsR0FBRyxDQUFDLE9BQU8sRUFBRSxjQUFjLENBQUMsQ0FBQyxDQUFDLEVBQUUsY0FBYyxFQUFFLE9BQU8sQ0FBQyxjQUFjLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO2dCQUM5RSxHQUFHLENBQUMsT0FBTyxFQUFFLFdBQVcsQ0FBQyxDQUFDLENBQUMsRUFBRSxXQUFXLEVBQUUsT0FBTyxDQUFDLFdBQVcsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7YUFDdEUsQ0FBQyxDQUFDO1lBQ0gsT0FBTyxFQUFFLFNBQVMsRUFBRSxDQUFDO1FBQ3ZCLENBQUM7S0FDRixDQUFDLENBQUM7QUFDTCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDaGF0RmFzdCBwb3N0LXR1cm4gZm9sbG93LXVwIHN1Z2dlc3Rpb24gZ2VuZXJhdGlvbi5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHsgYXN5bmNVdGlscywgZXJyb3JVdGlscywgbG9nLCBzdHJpbmdVdGlscyB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5pbXBvcnQge1xuICBGT0xMT1dfVVBfU1VHR0VTVElPTlNfUEFSVF9UWVBFLFxuICB0eXBlIEZvbGxvd1VwU3VnZ2VzdGlvbnNQYXJ0RGF0YSxcbiAgRm9sbG93VXBTdWdnZXN0aW9uc1BhcnREYXRhU2NoZW1hLFxufSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtbWFzdHJhL3dpcmVcIjtcbmltcG9ydCB7IG1vZGVsIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLW1vZGVsXCI7XG5pbXBvcnQgeyBBZ2VudCB9IGZyb20gXCJAbWFzdHJhL2NvcmUvYWdlbnRcIjtcbmltcG9ydCB0eXBlIHsgTWVzc2FnZUxpc3QgfSBmcm9tIFwiQG1hc3RyYS9jb3JlL2FnZW50L21lc3NhZ2UtbGlzdFwiO1xuaW1wb3J0IHR5cGUgeyBNYXN0cmFNb2RlbENvbmZpZyB9IGZyb20gXCJAbWFzdHJhL2NvcmUvbGxtXCI7XG5pbXBvcnQgdHlwZSB7IE91dHB1dFByb2Nlc3NvciB9IGZyb20gXCJAbWFzdHJhL2NvcmUvcHJvY2Vzc29yc1wiO1xuaW1wb3J0IHR5cGUgeyBSZXF1ZXN0Q29udGV4dCB9IGZyb20gXCJAbWFzdHJhL2NvcmUvcmVxdWVzdC1jb250ZXh0XCI7XG5pbXBvcnQgdHlwZSB7IERhdGFDaHVua1R5cGUsIExMTVN0ZXBSZXN1bHQsIE1hc3RyYVN0cmVhbVRyYW5zZm9ybSB9IGZyb20gXCJAbWFzdHJhL2NvcmUvc3RyZWFtXCI7XG5pbXBvcnQgeyBjcmVhdGVUb29sIH0gZnJvbSBcIkBtYXN0cmEvY29yZS90b29sc1wiO1xuaW1wb3J0IHsgeiB9IGZyb20gXCJ6b2RcIjtcblxuaW1wb3J0IHR5cGUgeyBGb2xsb3dVcFN1Z2dlc3Rpb25zQ29uZmlnLCBNYXN0cmFQbHVnaW5Db25maWcgfSBmcm9tIFwiLi9jb25maWcudHNcIjtcbmltcG9ydCB7IGJ1aWxkTW9kZWwgfSBmcm9tIFwiLi9tb2RlbC50c1wiO1xuaW1wb3J0IHsgVFlQT0dSQVBIWV9SVUxFIH0gZnJvbSBcIi4vc3R5bGUudHNcIjtcblxuY29uc3QgbG9nZ2VyID0gbG9nLmxvZ2dlcihcIm1hc3RyYS9mb2xsb3ctdXAtc3VnZ2VzdGlvbnNcIik7XG5jb25zdCBGT0xMT1dfVVBfTU9ERUxfQ0xBU1MgPSBtb2RlbC5Nb2RlbENsYXNzLkNoYXRGYXN0O1xuY29uc3QgREVGQVVMVF9RVUVTVElPTl9MSU1JVCA9IDM7XG5jb25zdCBNQVhfUVVFU1RJT05fTElNSVQgPSAxMDtcbmNvbnN0IERFRkFVTFRfVElNRU9VVF9NUyA9IDEwXzAwMDtcbmNvbnN0IE1BWF9UVVJOX0NPTlRFWFRfQ0hBUlMgPSAxNl8wMDA7XG5jb25zdCBHRU5FUklDX1FVRVNUSU9OUyA9IG5ldyBTZXQoW1xuICBcImFueXRoaW5nIGVsc2VcIixcbiAgXCJjYW4geW91IHRlbGwgbWUgbW9yZVwiLFxuICBcInRlbGwgbWUgbW9yZVwiLFxuICBcIndoYXQgZWxzZVwiLFxuICBcIndoYXQgd291bGQgeW91IGxpa2UgdG8ga25vd1wiLFxuXSk7XG5jb25zdCBBU1NJU1RBTlRfRElSRUNURURfUVVFU1RJT05TID0gW1xuICAvXig/OndvdWxkfGRvKSB5b3UgKD86bGlrZXx3YW50fHByZWZlcilcXGIvLFxuICAvXig/OnNob3VsZHxzaGFsbHxjYW58bWF5KSAoPzppfHdlKVxcYi8sXG4gIC9eYXJlIHlvdSBpbnRlcmVzdGVkXFxiLyxcbiAgL15zaG91bGQgKD86dGhlIHx0aGlzICk/KD86Y2hhcnR8Z3JhcGh8dGFibGV8dmlzdWFsaXphdGlvbnxyZXNwb25zZXxvdXRwdXR8cmVzdWx0cz98ZGF0YSlcXGIvLFxuXSBhcyBjb25zdDtcblxuLyoqIFRydXN0ZWQgcmVxdWVzdC1jb250ZXh0IGtleSBjYXJyeWluZyB0aGUgY3VycmVudCB1c2VyIG1lc3NhZ2UuICovXG5leHBvcnQgY29uc3QgRk9MTE9XX1VQX1VTRVJfVEVYVF9LRVkgPSBcIm1hc3RyYV9fZm9sbG93X3VwX3VzZXJfdGV4dFwiO1xuLyoqIFRydXN0ZWQgcmVxdWVzdC1jb250ZXh0IGtleSBjYXJyeWluZyBuYXRpdmUgR2VuaWUgZm9sbG93LXVwIHF1ZXN0aW9ucy4gKi9cbmV4cG9ydCBjb25zdCBGT0xMT1dfVVBfTkFUSVZFX1FVRVNUSU9OU19LRVkgPSBcIm1hc3RyYV9fZm9sbG93X3VwX25hdGl2ZV9xdWVzdGlvbnNcIjtcbi8qKiBUcnVzdGVkIHJlcXVlc3QtY29udGV4dCBrZXkgY29vcmRpbmF0aW5nIG9uZSBwb3N0LXR1cm4gc3RyZWFtIHdpdGggaXRzIHByb2Nlc3Nvci4gKi9cbmV4cG9ydCBjb25zdCBGT0xMT1dfVVBfUlVOX0lEX0tFWSA9IFwibWFzdHJhX19mb2xsb3dfdXBfcnVuX2lkXCI7XG5cbi8qKiBGdWxseSByZXNvbHZlZCBmb2xsb3ctdXAgc3VnZ2VzdGlvbiBzZXR0aW5ncy4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUmVzb2x2ZWRGb2xsb3dVcFN1Z2dlc3Rpb25zQ29uZmlnIHtcbiAgbW9kZTogXCJhdXRvXCIgfCBcImdlbmVyYXRlZFwiIHwgXCJnZW5pZVwiO1xuICBsaW1pdDogbnVtYmVyO1xuICB0aW1lb3V0TXM6IG51bWJlcjtcbiAgZXhwb3NlVG9vbDogYm9vbGVhbjtcbn1cblxuLyoqIElucHV0cyBmb3Igb25lIGZvbGxvdy11cCBnZW5lcmF0aW9uIHBhc3MuICovXG5leHBvcnQgaW50ZXJmYWNlIFN1Z2dlc3RGb2xsb3dVcHNPcHRpb25zIHtcbiAgdXNlclRleHQ6IHN0cmluZztcbiAgYW5zd2VyOiBzdHJpbmc7XG4gIC8qKiBCb3VuZGVkIHNlcmlhbGl6ZWQgdG9vbCBpbnB1dHMgYW5kIG91dHB1dHMgdGhhdCBncm91bmQgdGhlIG5leHQgcXVlc3Rpb25zLiAqL1xuICB0dXJuQ29udGV4dD86IHN0cmluZztcbiAgbmF0aXZlUXVlc3Rpb25zPzogcmVhZG9ubHkgc3RyaW5nW107XG4gIHJlcXVlc3RDb250ZXh0PzogUmVxdWVzdENvbnRleHQ7XG4gIGFib3J0U2lnbmFsPzogQWJvcnRTaWduYWw7XG59XG5cbi8qKiBSZXBsYWNlYWJsZSBzdWdnZXN0aW9uIGJvdW5kYXJ5IHVzZWQgYnkgdGhlIGZpbmlzaCBjYWxsYmFjayBhbmQgdGVzdHMuICovXG5leHBvcnQgdHlwZSBGb2xsb3dVcFN1Z2dlc3RlciA9IChcbiAgY29uZmlnOiBNYXN0cmFQbHVnaW5Db25maWcsXG4gIG9wdGlvbnM6IFN1Z2dlc3RGb2xsb3dVcHNPcHRpb25zLFxuKSA9PiBQcm9taXNlPHN0cmluZ1tdPjtcblxuY29uc3QgRm9sbG93VXBPdXRwdXRTY2hlbWEgPSB6XG4gIC5vYmplY3Qoe1xuICAgIHF1ZXN0aW9uczogelxuICAgICAgLmFycmF5KHouc3RyaW5nKCkudHJpbSgpLm1pbigxKSlcbiAgICAgIC5tYXgoTUFYX1FVRVNUSU9OX0xJTUlUKVxuICAgICAgLmRlc2NyaWJlKFwiU3BlY2lmaWMgbmV4dCBxdWVzdGlvbnMgZ3JvdW5kZWQgaW4gdGhlIGNvbXBsZXRlZCB0dXJuLlwiKSxcbiAgfSlcbiAgLnN0cmljdCgpO1xuXG5jb25zdCBGb2xsb3dVcFRvb2xJbnB1dFNjaGVtYSA9IHpcbiAgLm9iamVjdCh7XG4gICAgdXNlclRleHQ6IHouc3RyaW5nKCkudHJpbSgpLm1pbigxKS5kZXNjcmliZShcIlVzZXIgbWVzc2FnZSB0aGF0IHN0YXJ0ZWQgdGhlIGNvbXBsZXRlZCB0dXJuLlwiKSxcbiAgICBhbnN3ZXI6IHouc3RyaW5nKCkudHJpbSgpLm1pbigxKS5kZXNjcmliZShcIkFzc2lzdGFudCBhbnN3ZXIgZnJvbSB0aGUgY29tcGxldGVkIHR1cm4uXCIpLFxuICAgIHR1cm5Db250ZXh0OiB6XG4gICAgICAuc3RyaW5nKClcbiAgICAgIC50cmltKClcbiAgICAgIC5tYXgoTUFYX1RVUk5fQ09OVEVYVF9DSEFSUylcbiAgICAgIC5vcHRpb25hbCgpXG4gICAgICAuZGVzY3JpYmUoXCJSZWxldmFudCB0b29sIGlucHV0cyBhbmQgb3V0cHV0cyBmcm9tIHRoZSBjb21wbGV0ZWQgdHVybi5cIiksXG4gICAgbmF0aXZlUXVlc3Rpb25zOiB6XG4gICAgICAuYXJyYXkoei5zdHJpbmcoKS50cmltKCkubWluKDEpKVxuICAgICAgLm9wdGlvbmFsKClcbiAgICAgIC5kZXNjcmliZShcIkV4aXN0aW5nIGZvbGxvdy11cCBxdWVzdGlvbnMgdG8gYXZvaWQgcmVwZWF0aW5nLlwiKSxcbiAgfSlcbiAgLnN0cmljdCgpO1xuXG5jb25zdCBGT0xMT1dfVVBfSU5TVFJVQ1RJT05TID0gW1xuICBcIkdlbmVyYXRlIGNvbmNpc2UgcXVlc3Rpb25zIHRoYXQgYSB1c2VyIGNhbiBzZW5kIHRvIGRpZyBmdXJ0aGVyIGludG8gYSBjb21wbGV0ZWQgYXNzaXN0YW50IHR1cm4uXCIsXG4gIFwiV3JpdGUgZXZlcnkgaXRlbSBpbiB0aGUgdXNlcidzIHZvaWNlIGFzIGEgbWVzc2FnZSBmb3IgdGhlIGFzc2lzdGFudCB0byBhbnN3ZXIgd2hlbiBjbGlja2VkLlwiLFxuICBcIk5ldmVyIGFkZHJlc3MgdGhlIHVzZXIgb3IgYXNrIHdoYXQgdGhleSB3b3VsZCBsaWtlLCB3YW50LCBwcmVmZXIsIG9yIGhvdyB0aGUgcmVzcG9uc2Ugc2hvdWxkIGJlIGN1c3RvbWl6ZWQuXCIsXG4gIFwiRXZlcnkgcXVlc3Rpb24gbXVzdCBiZSBhbnN3ZXJhYmxlIGZyb20gdGhlIHNhbWUgdG9vbHMgYW5kIGRhdGEgZG9tYWluIGFzIHRoZSBjb21wbGV0ZWQgdHVybi5cIixcbiAgXCJHcm91bmQgcXVlc3Rpb25zIGluIHRoZSBhbnN3ZXIgYW5kIHR1cm4gdG9vbCBkYXRhLCBpbmNsdWRpbmcgZGlzcGxheWVkIGNoYXJ0IG9yIHRhYmxlIHZhbHVlcy5cIixcbiAgXCJQcmVmZXIgY29uY3JldGUgYW5hbHlzZXMsIGNvbXBhcmlzb25zLCByYW5raW5ncywgY2FsY3VsYXRpb25zLCBmaWx0ZXJzLCBvciBleHBsYW5hdGlvbnMgb2YgdGhlIHJlc3VsdC5cIixcbiAgXCJGb3IgYSBjaGFydCBvciB0YWJsZSwgYXNrIGFib3V0IHRoZSB2YWx1ZXMgYW5kIHBhdHRlcm5zIGJlZm9yZSBzdWdnZXN0aW5nIHByZXNlbnRhdGlvbiBjaGFuZ2VzLlwiLFxuICBcIkdvb2QgZXhhbXBsZXMgaW5jbHVkZSAnV2hpY2ggY2F0ZWdvcnkgaGFzIHRoZSBoaWdoZXN0IHZhbHVlPycsICdIb3cgbGFyZ2UgaXMgdGhlIGdhcCBiZXR3ZWVuIHRoZSBoaWdoZXN0IGFuZCBsb3dlc3QgY2F0ZWdvcmllcz8nLCBhbmQgJ1doYXQgcGF0dGVybiBzdGFuZHMgb3V0IGluIHRoZXNlIHZhbHVlcz8nLlwiLFxuICBcIkRvIG5vdCByZXBlYXQgdGhlIHVzZXIncyBxdWVzdGlvbiwgdGhlIGFuc3dlciwgb3IgYW4gZXhpc3Rpbmcgc3VnZ2VzdGlvbi5cIixcbiAgXCJEbyBub3QgYXNrIGdlbmVyaWMgZmlsbGVyIHN1Y2ggYXMgJ0FueXRoaW5nIGVsc2U/JyBvciByZXF1ZXN0IHVuc3VwcG9ydGVkIGRhdGEuXCIsXG4gIFwiUmV0dXJuIHplcm8gcXVlc3Rpb25zIHdoZW4gbm8gdXNlZnVsIGdyb3VuZGVkIGZvbGxvdy11cCBleGlzdHMuXCIsXG4gIFRZUE9HUkFQSFlfUlVMRSxcbl0uam9pbihcIiBcIik7XG5cbmNvbnN0IHN1Z2dlc3Rpb25BZ2VudHMgPSBuZXcgV2Vha01hcDxNYXN0cmFQbHVnaW5Db25maWcsIEFnZW50PigpO1xuY29uc3QgZ2VuZXJhdGVkUnVuUGFydHMgPSBuZXcgTWFwPHN0cmluZywgRm9sbG93VXBTdWdnZXN0aW9uc1BhcnREYXRhPigpO1xuY29uc3QgR0VORVJBVEVEX1JVTl9QQVJUX1RUTF9NUyA9IDUgKiA2MF8wMDA7XG5cbi8qKiBSZXNvbHZlIGRpc2FibGVkLCBzaG9ydGhhbmQsIGFuZCBjb25maWd1cmVkIHN1Z2dlc3Rpb24gcG9saWN5IGZvcm1zLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVGb2xsb3dVcFN1Z2dlc3Rpb25zQ29uZmlnKFxuICB2YWx1ZTogTWFzdHJhUGx1Z2luQ29uZmlnW1wiZm9sbG93VXBTdWdnZXN0aW9uc1wiXSxcbik6IFJlc29sdmVkRm9sbG93VXBTdWdnZXN0aW9uc0NvbmZpZyB8IHVuZGVmaW5lZCB7XG4gIGlmICghdmFsdWUpIHJldHVybiB1bmRlZmluZWQ7XG4gIGNvbnN0IGNvbmZpZ3VyZWQ6IEZvbGxvd1VwU3VnZ2VzdGlvbnNDb25maWcgPSB0eXBlb2YgdmFsdWUgPT09IFwib2JqZWN0XCIgPyB2YWx1ZSA6IHsgbW9kZTogdmFsdWUgfTtcbiAgcmV0dXJuIHtcbiAgICBtb2RlOiBjb25maWd1cmVkLm1vZGUgPT09IHRydWUgPyBcImF1dG9cIiA6IGNvbmZpZ3VyZWQubW9kZSxcbiAgICBsaW1pdDogTWF0aC5taW4oTUFYX1FVRVNUSU9OX0xJTUlULCBNYXRoLm1heCgxLCBjb25maWd1cmVkLmxpbWl0ID8/IERFRkFVTFRfUVVFU1RJT05fTElNSVQpKSxcbiAgICB0aW1lb3V0TXM6IE1hdGgubWluKDMwXzAwMCwgTWF0aC5tYXgoMV8wMDAsIGNvbmZpZ3VyZWQudGltZW91dE1zID8/IERFRkFVTFRfVElNRU9VVF9NUykpLFxuICAgIGV4cG9zZVRvb2w6IGNvbmZpZ3VyZWQuZXhwb3NlVG9vbCA/PyBmYWxzZSxcbiAgfTtcbn1cblxuZnVuY3Rpb24gZm9sbG93VXBNb2RlbChcbiAgY29uZmlnOiBNYXN0cmFQbHVnaW5Db25maWcsXG4pOiAoYXJnczogeyByZXF1ZXN0Q29udGV4dDogUmVxdWVzdENvbnRleHQgfSkgPT4gUHJvbWlzZTxNYXN0cmFNb2RlbENvbmZpZz4ge1xuICByZXR1cm4gKHsgcmVxdWVzdENvbnRleHQgfSkgPT5cbiAgICBidWlsZE1vZGVsKGNvbmZpZywgcmVxdWVzdENvbnRleHQsIHsgbW9kZWxDbGFzczogRk9MTE9XX1VQX01PREVMX0NMQVNTIH0pO1xufVxuXG5mdW5jdGlvbiBzdWdnZXN0aW9uQWdlbnQoY29uZmlnOiBNYXN0cmFQbHVnaW5Db25maWcpOiBBZ2VudCB7XG4gIGxldCBhZ2VudCA9IHN1Z2dlc3Rpb25BZ2VudHMuZ2V0KGNvbmZpZyk7XG4gIGlmICghYWdlbnQpIHtcbiAgICBhZ2VudCA9IG5ldyBBZ2VudCh7XG4gICAgICBpZDogXCJmb2xsb3ctdXAtc3VnZ2VzdGlvbnNcIixcbiAgICAgIG5hbWU6IFwiRm9sbG93LXVwIHN1Z2dlc3Rpb25zXCIsXG4gICAgICBkZXNjcmlwdGlvbjogXCJHZW5lcmF0ZXMgZ3JvdW5kZWQgbmV4dCBxdWVzdGlvbnMgdXNpbmcgdGhlIGZhc3QgY2hhdCBtb2RlbC5cIixcbiAgICAgIGluc3RydWN0aW9uczogRk9MTE9XX1VQX0lOU1RSVUNUSU9OUyxcbiAgICAgIG1vZGVsOiBmb2xsb3dVcE1vZGVsKGNvbmZpZyksXG4gICAgfSk7XG4gICAgc3VnZ2VzdGlvbkFnZW50cy5zZXQoY29uZmlnLCBhZ2VudCk7XG4gIH1cbiAgcmV0dXJuIGFnZW50O1xufVxuXG5mdW5jdGlvbiBub3JtYWxpemVkUXVlc3Rpb24odmFsdWU6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiB2YWx1ZVxuICAgIC50cmltKClcbiAgICAudG9Mb3dlckNhc2UoKVxuICAgIC5yZXBsYWNlKC9bXlxccHtMfVxccHtOfVxcc10vZ3UsIFwiIFwiKVxuICAgIC5yZXBsYWNlKC9cXHMrL2csIFwiIFwiKTtcbn1cblxuZnVuY3Rpb24gYWNjZXB0ZWRRdWVzdGlvbnMoXG4gIHF1ZXN0aW9uczogcmVhZG9ubHkgc3RyaW5nW10sXG4gIG9wdGlvbnM6IFN1Z2dlc3RGb2xsb3dVcHNPcHRpb25zLFxuICBtYXhRdWVzdGlvbnM6IG51bWJlcixcbik6IHN0cmluZ1tdIHtcbiAgY29uc3QgZXhjbHVkZWQgPSBuZXcgU2V0KFtcbiAgICBub3JtYWxpemVkUXVlc3Rpb24ob3B0aW9ucy51c2VyVGV4dCksXG4gICAgLi4uKG9wdGlvbnMubmF0aXZlUXVlc3Rpb25zID8/IFtdKS5tYXAobm9ybWFsaXplZFF1ZXN0aW9uKSxcbiAgXSk7XG4gIGNvbnN0IGFjY2VwdGVkOiBzdHJpbmdbXSA9IFtdO1xuICBmb3IgKGNvbnN0IHF1ZXN0aW9uIG9mIHF1ZXN0aW9ucykge1xuICAgIGNvbnN0IHRyaW1tZWQgPSBxdWVzdGlvbi50cmltKCk7XG4gICAgY29uc3Qgbm9ybWFsaXplZCA9IG5vcm1hbGl6ZWRRdWVzdGlvbih0cmltbWVkKTtcbiAgICBpZiAoXG4gICAgICAhbm9ybWFsaXplZCB8fFxuICAgICAgbm9ybWFsaXplZC5sZW5ndGggPCA4IHx8XG4gICAgICBleGNsdWRlZC5oYXMobm9ybWFsaXplZCkgfHxcbiAgICAgIEdFTkVSSUNfUVVFU1RJT05TLmhhcyhub3JtYWxpemVkKSB8fFxuICAgICAgQVNTSVNUQU5UX0RJUkVDVEVEX1FVRVNUSU9OUy5zb21lKChwYXR0ZXJuKSA9PiBwYXR0ZXJuLnRlc3Qobm9ybWFsaXplZCkpXG4gICAgKSB7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG4gICAgZXhjbHVkZWQuYWRkKG5vcm1hbGl6ZWQpO1xuICAgIGFjY2VwdGVkLnB1c2godHJpbW1lZCk7XG4gICAgaWYgKGFjY2VwdGVkLmxlbmd0aCA+PSBtYXhRdWVzdGlvbnMpIGJyZWFrO1xuICB9XG4gIHJldHVybiBhY2NlcHRlZDtcbn1cblxuZnVuY3Rpb24gc2VyaWFsaXplZFR1cm5Db250ZXh0KHN0ZXBzOiByZWFkb25seSBMTE1TdGVwUmVzdWx0W10pOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICBjb25zdCBhY3Rpdml0eSA9IHN0ZXBzLmZsYXRNYXAoKHN0ZXApID0+IFtcbiAgICAuLi5zdGVwLnRvb2xDYWxscy5tYXAoKGNhbGwpID0+ICh7XG4gICAgICB0b29sOiBjYWxsLnBheWxvYWQudG9vbE5hbWUsXG4gICAgICBpbnB1dDogY2FsbC5wYXlsb2FkLmFyZ3MsXG4gICAgfSkpLFxuICAgIC4uLnN0ZXAudG9vbFJlc3VsdHMubWFwKChyZXN1bHQpID0+ICh7XG4gICAgICB0b29sOiByZXN1bHQucGF5bG9hZC50b29sTmFtZSxcbiAgICAgIG91dHB1dDogcmVzdWx0LnBheWxvYWQucmVzdWx0LFxuICAgICAgLi4uKHJlc3VsdC5wYXlsb2FkLmlzRXJyb3IgPyB7IGlzRXJyb3I6IHRydWUgfSA6IHt9KSxcbiAgICB9KSksXG4gIF0pO1xuICBpZiAoYWN0aXZpdHkubGVuZ3RoID09PSAwKSByZXR1cm4gdW5kZWZpbmVkO1xuICB0cnkge1xuICAgIGNvbnN0IHNlcmlhbGl6ZWQgPSBKU09OLnN0cmluZ2lmeShhY3Rpdml0eS5zbGljZSgtMTIpKTtcbiAgICByZXR1cm4gc2VyaWFsaXplZC5sZW5ndGggPiBNQVhfVFVSTl9DT05URVhUX0NIQVJTXG4gICAgICA/IHNlcmlhbGl6ZWQuc2xpY2UoMCwgTUFYX1RVUk5fQ09OVEVYVF9DSEFSUylcbiAgICAgIDogc2VyaWFsaXplZDtcbiAgfSBjYXRjaCB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxufVxuXG4vKiogR2VuZXJhdGUgZ3JvdW5kZWQgZm9sbG93LXVwcyB0aHJvdWdoIENoYXRGYXN0LCBmYWlsaW5nIG9wZW4gdG8gYW4gZW1wdHkgbGlzdC4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzdWdnZXN0Rm9sbG93VXBzKFxuICBjb25maWc6IE1hc3RyYVBsdWdpbkNvbmZpZyxcbiAgb3B0aW9uczogU3VnZ2VzdEZvbGxvd1Vwc09wdGlvbnMsXG4pOiBQcm9taXNlPHN0cmluZ1tdPiB7XG4gIGNvbnN0IHNldHRpbmdzID0gcmVzb2x2ZUZvbGxvd1VwU3VnZ2VzdGlvbnNDb25maWcoY29uZmlnLmZvbGxvd1VwU3VnZ2VzdGlvbnMpO1xuICBpZiAoIXNldHRpbmdzIHx8IHNldHRpbmdzLm1vZGUgPT09IFwiZ2VuaWVcIikgcmV0dXJuIFtdO1xuICBjb25zdCB0aW1lb3V0ID0gQWJvcnRTaWduYWwudGltZW91dChzZXR0aW5ncy50aW1lb3V0TXMpO1xuICBjb25zdCBhYm9ydFNpZ25hbCA9IGFzeW5jVXRpbHMuY29tYmluZUFib3J0U2lnbmFscyhvcHRpb25zLmFib3J0U2lnbmFsLCB0aW1lb3V0KTtcbiAgY29uc3QgcHJvbXB0ID0gc3RyaW5nVXRpbHMudG9EZXNjcmlwdGlvbih7XG4gICAgXCJVc2VyIG1lc3NhZ2VcIjogb3B0aW9ucy51c2VyVGV4dCxcbiAgICBcIkFzc2lzdGFudCBhbnN3ZXJcIjogb3B0aW9ucy5hbnN3ZXIsXG4gICAgLi4uKG9wdGlvbnMudHVybkNvbnRleHQgPyB7IFwiVHVybiB0b29sIGRhdGFcIjogb3B0aW9ucy50dXJuQ29udGV4dCB9IDoge30pLFxuICAgIC4uLihvcHRpb25zLm5hdGl2ZVF1ZXN0aW9ucz8ubGVuZ3RoXG4gICAgICA/IHsgXCJFeGlzdGluZyBzdWdnZXN0aW9uc1wiOiBKU09OLnN0cmluZ2lmeShvcHRpb25zLm5hdGl2ZVF1ZXN0aW9ucykgfVxuICAgICAgOiB7fSksXG4gICAgXCJNYXhpbXVtIHF1ZXN0aW9uc1wiOiBTdHJpbmcoc2V0dGluZ3MubGltaXQpLFxuICB9KTtcbiAgdHJ5IHtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBzdWdnZXN0aW9uQWdlbnQoY29uZmlnKS5nZW5lcmF0ZShwcm9tcHQsIHtcbiAgICAgIHN0cnVjdHVyZWRPdXRwdXQ6IHsgc2NoZW1hOiBGb2xsb3dVcE91dHB1dFNjaGVtYSB9LFxuICAgICAgLi4uKG9wdGlvbnMucmVxdWVzdENvbnRleHQgPyB7IHJlcXVlc3RDb250ZXh0OiBvcHRpb25zLnJlcXVlc3RDb250ZXh0IH0gOiB7fSksXG4gICAgICBhYm9ydFNpZ25hbCxcbiAgICB9KTtcbiAgICBjb25zdCBwYXJzZWQgPSBGb2xsb3dVcE91dHB1dFNjaGVtYS5wYXJzZShyZXN1bHQub2JqZWN0KTtcbiAgICByZXR1cm4gYWNjZXB0ZWRRdWVzdGlvbnMocGFyc2VkLnF1ZXN0aW9ucywgb3B0aW9ucywgc2V0dGluZ3MubGltaXQpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGlmICghb3B0aW9ucy5hYm9ydFNpZ25hbD8uYWJvcnRlZCkge1xuICAgICAgbG9nZ2VyLndhcm4oXCJnZW5lcmF0aW9uLWZhaWxlZFwiLCB7IGVycm9yOiBlcnJvclV0aWxzLmVycm9yTWVzc2FnZShlcnJvcikgfSk7XG4gICAgfVxuICAgIHJldHVybiBbXTtcbiAgfVxufVxuXG5mdW5jdGlvbiBsYXN0QXNzaXN0YW50UmVzcG9uc2UobWVzc2FnZUxpc3Q6IE1lc3NhZ2VMaXN0KSB7XG4gIGNvbnN0IG1lc3NhZ2VzID0gbWVzc2FnZUxpc3QuZ2V0LnJlc3BvbnNlLmRiKCk7XG4gIGZvciAobGV0IGluZGV4ID0gbWVzc2FnZXMubGVuZ3RoIC0gMTsgaW5kZXggPj0gMDsgaW5kZXggLT0gMSkge1xuICAgIGNvbnN0IG1lc3NhZ2UgPSBtZXNzYWdlc1tpbmRleF07XG4gICAgaWYgKG1lc3NhZ2U/LnJvbGUgPT09IFwiYXNzaXN0YW50XCIpIHJldHVybiBtZXNzYWdlO1xuICB9XG4gIHJldHVybiB1bmRlZmluZWQ7XG59XG5cbmZ1bmN0aW9uIHBlcnNpc3RlZEZvbGxvd1VwRGF0YShtZXNzYWdlTGlzdDogTWVzc2FnZUxpc3QpOiBGb2xsb3dVcFN1Z2dlc3Rpb25zUGFydERhdGEgfCB1bmRlZmluZWQge1xuICBjb25zdCBtZXNzYWdlID0gbGFzdEFzc2lzdGFudFJlc3BvbnNlKG1lc3NhZ2VMaXN0KTtcbiAgaWYgKCFtZXNzYWdlKSByZXR1cm4gdW5kZWZpbmVkO1xuICBmb3IgKGNvbnN0IHBhcnQgb2YgbWVzc2FnZS5jb250ZW50LnBhcnRzKSB7XG4gICAgaWYgKHBhcnQudHlwZSAhPT0gRk9MTE9XX1VQX1NVR0dFU1RJT05TX1BBUlRfVFlQRSB8fCAhKFwiZGF0YVwiIGluIHBhcnQpKSBjb250aW51ZTtcbiAgICBjb25zdCBwYXJzZWQgPSBGb2xsb3dVcFN1Z2dlc3Rpb25zUGFydERhdGFTY2hlbWEuc2FmZVBhcnNlKHBhcnQuZGF0YSk7XG4gICAgaWYgKHBhcnNlZC5zdWNjZXNzKSByZXR1cm4gcGFyc2VkLmRhdGE7XG4gIH1cbiAgcmV0dXJuIHVuZGVmaW5lZDtcbn1cblxuZnVuY3Rpb24gcGVyc2lzdEZvbGxvd1VwRGF0YShtZXNzYWdlTGlzdDogTWVzc2FnZUxpc3QsIGRhdGE6IEZvbGxvd1VwU3VnZ2VzdGlvbnNQYXJ0RGF0YSk6IHZvaWQge1xuICBjb25zdCBtZXNzYWdlID0gbGFzdEFzc2lzdGFudFJlc3BvbnNlKG1lc3NhZ2VMaXN0KTtcbiAgaWYgKCFtZXNzYWdlKSByZXR1cm47XG4gIG1lc3NhZ2VMaXN0LmFkZChcbiAgICB7XG4gICAgICBpZDogbWVzc2FnZS5pZCxcbiAgICAgIHJvbGU6IFwiYXNzaXN0YW50XCIsXG4gICAgICBwYXJ0czogW3sgdHlwZTogRk9MTE9XX1VQX1NVR0dFU1RJT05TX1BBUlRfVFlQRSwgZGF0YSB9XSxcbiAgICB9LFxuICAgIFwicmVzcG9uc2VcIixcbiAgICB7IG1lcmdlOiB0cnVlIH0sXG4gICk7XG59XG5cbmZ1bmN0aW9uIHJlbWVtYmVyR2VuZXJhdGVkUnVuUGFydChydW5JZDogc3RyaW5nIHwgdW5kZWZpbmVkLCBkYXRhOiBGb2xsb3dVcFN1Z2dlc3Rpb25zUGFydERhdGEpIHtcbiAgaWYgKCFydW5JZCkgcmV0dXJuO1xuICBnZW5lcmF0ZWRSdW5QYXJ0cy5zZXQocnVuSWQsIGRhdGEpO1xuICBjb25zdCB0aW1lciA9IHNldFRpbWVvdXQoKCkgPT4gZ2VuZXJhdGVkUnVuUGFydHMuZGVsZXRlKHJ1bklkKSwgR0VORVJBVEVEX1JVTl9QQVJUX1RUTF9NUyk7XG4gIHRpbWVyLnVucmVmKCk7XG59XG5cbi8qKiBCdWlsZCBNYXN0cmEncyBmaW5hbCBvdXRwdXQgcHJvY2Vzc29yIGZvciBjb25maWd1cmVkIGdlbmVyYXRlZCBmb2xsb3ctdXBzLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJ1aWxkRm9sbG93VXBTdWdnZXN0aW9uc1Byb2Nlc3NvcihcbiAgY29uZmlnOiBNYXN0cmFQbHVnaW5Db25maWcsXG4gIHN1Z2dlc3RlcjogRm9sbG93VXBTdWdnZXN0ZXIgPSBzdWdnZXN0Rm9sbG93VXBzLFxuKTogT3V0cHV0UHJvY2Vzc29yIHwgdW5kZWZpbmVkIHtcbiAgY29uc3Qgc2V0dGluZ3MgPSByZXNvbHZlRm9sbG93VXBTdWdnZXN0aW9uc0NvbmZpZyhjb25maWcuZm9sbG93VXBTdWdnZXN0aW9ucyk7XG4gIGlmICghc2V0dGluZ3MgfHwgc2V0dGluZ3MubW9kZSA9PT0gXCJnZW5pZVwiKSByZXR1cm4gdW5kZWZpbmVkO1xuICByZXR1cm4ge1xuICAgIGlkOiBcImZvbGxvdy11cC1zdWdnZXN0aW9uc1wiLFxuICAgIGFzeW5jIHByb2Nlc3NPdXRwdXRSZXN1bHQoe1xuICAgICAgcmVzdWx0LFxuICAgICAgbWVzc2FnZUxpc3QsXG4gICAgICByZXF1ZXN0Q29udGV4dCxcbiAgICAgIGFib3J0U2lnbmFsLFxuICAgIH0pOiBQcm9taXNlPE1lc3NhZ2VMaXN0PiB7XG4gICAgICBpZiAoIXJlcXVlc3RDb250ZXh0IHx8IHJlc3VsdC5maW5pc2hSZWFzb24gPT09IFwiYWJvcnRcIiB8fCByZXN1bHQuZmluaXNoUmVhc29uID09PSBcImVycm9yXCIpIHtcbiAgICAgICAgcmV0dXJuIG1lc3NhZ2VMaXN0O1xuICAgICAgfVxuICAgICAgY29uc3QgcnVuSWRWYWx1ZSA9IHJlcXVlc3RDb250ZXh0LmdldFJhdyhGT0xMT1dfVVBfUlVOX0lEX0tFWSk7XG4gICAgICBjb25zdCBydW5JZCA9IHR5cGVvZiBydW5JZFZhbHVlID09PSBcInN0cmluZ1wiID8gcnVuSWRWYWx1ZSA6IHVuZGVmaW5lZDtcbiAgICAgIGNvbnN0IHBlcnNpc3RlZCA9IHBlcnNpc3RlZEZvbGxvd1VwRGF0YShtZXNzYWdlTGlzdCk7XG4gICAgICBpZiAocGVyc2lzdGVkKSB7XG4gICAgICAgIHJlbWVtYmVyR2VuZXJhdGVkUnVuUGFydChydW5JZCwgcGVyc2lzdGVkKTtcbiAgICAgICAgcmV0dXJuIG1lc3NhZ2VMaXN0O1xuICAgICAgfVxuICAgICAgY29uc3QgYW5zd2VyID0gcmVzdWx0LnRleHQudHJpbSgpO1xuICAgICAgY29uc3QgdXNlclRleHQgPSByZXF1ZXN0Q29udGV4dC5nZXRSYXcoRk9MTE9XX1VQX1VTRVJfVEVYVF9LRVkpO1xuICAgICAgaWYgKCFhbnN3ZXIgfHwgdHlwZW9mIHVzZXJUZXh0ICE9PSBcInN0cmluZ1wiKSByZXR1cm4gbWVzc2FnZUxpc3Q7XG4gICAgICBjb25zdCBuYXRpdmVWYWx1ZSA9IHJlcXVlc3RDb250ZXh0LmdldFJhdyhGT0xMT1dfVVBfTkFUSVZFX1FVRVNUSU9OU19LRVkpO1xuICAgICAgY29uc3QgbmF0aXZlUXVlc3Rpb25zID1cbiAgICAgICAgc2V0dGluZ3MubW9kZSA9PT0gXCJhdXRvXCIgJiYgQXJyYXkuaXNBcnJheShuYXRpdmVWYWx1ZSlcbiAgICAgICAgICA/IG5hdGl2ZVZhbHVlLmZpbHRlcigodmFsdWUpOiB2YWx1ZSBpcyBzdHJpbmcgPT4gdHlwZW9mIHZhbHVlID09PSBcInN0cmluZ1wiKVxuICAgICAgICAgIDogW107XG4gICAgICBjb25zdCB0dXJuQ29udGV4dCA9IHNlcmlhbGl6ZWRUdXJuQ29udGV4dChyZXN1bHQuc3RlcHMpO1xuICAgICAgY29uc3Qgc3VnZ2VzdGlvbk9wdGlvbnM6IFN1Z2dlc3RGb2xsb3dVcHNPcHRpb25zID0ge1xuICAgICAgICB1c2VyVGV4dCxcbiAgICAgICAgYW5zd2VyLFxuICAgICAgICAuLi4odHVybkNvbnRleHQgPyB7IHR1cm5Db250ZXh0IH0gOiB7fSksXG4gICAgICAgIC4uLihuYXRpdmVRdWVzdGlvbnMubGVuZ3RoID4gMCA/IHsgbmF0aXZlUXVlc3Rpb25zIH0gOiB7fSksXG4gICAgICAgIHJlcXVlc3RDb250ZXh0LFxuICAgICAgICAuLi4oYWJvcnRTaWduYWwgPyB7IGFib3J0U2lnbmFsIH0gOiB7fSksXG4gICAgICB9O1xuICAgICAgY29uc3Qgc3VnZ2VzdGVkID0gYXdhaXQgc3VnZ2VzdGVyKGNvbmZpZywgc3VnZ2VzdGlvbk9wdGlvbnMpO1xuICAgICAgY29uc3QgcXVlc3Rpb25zID0gYWNjZXB0ZWRRdWVzdGlvbnMoc3VnZ2VzdGVkLCBzdWdnZXN0aW9uT3B0aW9ucywgc2V0dGluZ3MubGltaXQpO1xuICAgICAgaWYgKHF1ZXN0aW9ucy5sZW5ndGggPT09IDApIHJldHVybiBtZXNzYWdlTGlzdDtcbiAgICAgIGNvbnN0IGRhdGEgPSBGb2xsb3dVcFN1Z2dlc3Rpb25zUGFydERhdGFTY2hlbWEucGFyc2Uoe1xuICAgICAgICBxdWVzdGlvbnMsXG4gICAgICAgIHNvdXJjZTogXCJtb2RlbFwiLFxuICAgICAgfSk7XG4gICAgICBwZXJzaXN0Rm9sbG93VXBEYXRhKG1lc3NhZ2VMaXN0LCBkYXRhKTtcbiAgICAgIHJlbWVtYmVyR2VuZXJhdGVkUnVuUGFydChydW5JZCwgZGF0YSk7XG4gICAgICByZXR1cm4gbWVzc2FnZUxpc3Q7XG4gICAgfSxcbiAgfTtcbn1cblxuLyoqIEluamVjdCB0aGUgcHJvY2Vzc29yJ3MgcGVyc2lzdGVkIGRhdGEgcGFydCBiZWZvcmUgTWFzdHJhJ3MgZmluaXNoIGNodW5rLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJ1aWxkRm9sbG93VXBTdWdnZXN0aW9uc1RyYW5zZm9ybShydW5JZDogc3RyaW5nKTogTWFzdHJhU3RyZWFtVHJhbnNmb3JtIHtcbiAgcmV0dXJuICgpID0+XG4gICAgbmV3IFRyYW5zZm9ybVN0cmVhbSh7XG4gICAgICB0cmFuc2Zvcm0oY2h1bmssIGNvbnRyb2xsZXIpIHtcbiAgICAgICAgaWYgKGNodW5rLnR5cGUgPT09IFwiZmluaXNoXCIpIHtcbiAgICAgICAgICBjb25zdCBkYXRhID0gZ2VuZXJhdGVkUnVuUGFydHMuZ2V0KHJ1bklkKTtcbiAgICAgICAgICBpZiAoZGF0YSkge1xuICAgICAgICAgICAgY29uc3QgcGFydDogRGF0YUNodW5rVHlwZSA9IHtcbiAgICAgICAgICAgICAgdHlwZTogRk9MTE9XX1VQX1NVR0dFU1RJT05TX1BBUlRfVFlQRSxcbiAgICAgICAgICAgICAgZGF0YSxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBjb250cm9sbGVyLmVucXVldWUocGFydCBhcyBuZXZlcik7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGNvbnRyb2xsZXIuZW5xdWV1ZShjaHVuayk7XG4gICAgICB9LFxuICAgIH0pO1xufVxuXG4vKiogQnVpbGQgdGhlIG9wdGlvbmFsIGV4cGxpY2l0bHkgY2FsbGFibGUgZm9sbG93LXVwIHN1Z2dlc3Rpb24gdG9vbC4gKi9cbmV4cG9ydCBmdW5jdGlvbiBidWlsZFN1Z2dlc3RGb2xsb3dVcHNUb29sKGNvbmZpZzogTWFzdHJhUGx1Z2luQ29uZmlnKSB7XG4gIHJldHVybiBjcmVhdGVUb29sKHtcbiAgICBpZDogXCJzdWdnZXN0X2ZvbGxvd191cHNcIixcbiAgICBkZXNjcmlwdGlvbjpcbiAgICAgIFwiR2VuZXJhdGUgYSBzaG9ydCBsaXN0IG9mIGdyb3VuZGVkIGZvbGxvdy11cCBxdWVzdGlvbnMgZm9yIGEgY29tcGxldGVkIHVzZXIgbWVzc2FnZSBhbmQgYW5zd2VyLlwiLFxuICAgIGlucHV0U2NoZW1hOiBGb2xsb3dVcFRvb2xJbnB1dFNjaGVtYSxcbiAgICBvdXRwdXRTY2hlbWE6IEZvbGxvd1VwT3V0cHV0U2NoZW1hLFxuICAgIGV4ZWN1dGU6IGFzeW5jIChpbnB1dCwgY29udGV4dCkgPT4ge1xuICAgICAgY29uc3QgdmFsdWVzID0gRm9sbG93VXBUb29sSW5wdXRTY2hlbWEucGFyc2UoaW5wdXQpO1xuICAgICAgY29uc3QgcXVlc3Rpb25zID0gYXdhaXQgc3VnZ2VzdEZvbGxvd1Vwcyhjb25maWcsIHtcbiAgICAgICAgdXNlclRleHQ6IHZhbHVlcy51c2VyVGV4dCxcbiAgICAgICAgYW5zd2VyOiB2YWx1ZXMuYW5zd2VyLFxuICAgICAgICAuLi4odmFsdWVzLnR1cm5Db250ZXh0ID8geyB0dXJuQ29udGV4dDogdmFsdWVzLnR1cm5Db250ZXh0IH0gOiB7fSksXG4gICAgICAgIC4uLih2YWx1ZXMubmF0aXZlUXVlc3Rpb25zID8geyBuYXRpdmVRdWVzdGlvbnM6IHZhbHVlcy5uYXRpdmVRdWVzdGlvbnMgfSA6IHt9KSxcbiAgICAgICAgLi4uKGNvbnRleHQ/LnJlcXVlc3RDb250ZXh0ID8geyByZXF1ZXN0Q29udGV4dDogY29udGV4dC5yZXF1ZXN0Q29udGV4dCB9IDoge30pLFxuICAgICAgICAuLi4oY29udGV4dD8uYWJvcnRTaWduYWwgPyB7IGFib3J0U2lnbmFsOiBjb250ZXh0LmFib3J0U2lnbmFsIH0gOiB7fSksXG4gICAgICB9KTtcbiAgICAgIHJldHVybiB7IHF1ZXN0aW9ucyB9O1xuICAgIH0sXG4gIH0pO1xufVxuIl19