@dbx-tools/appkit-mastra 0.6.218 → 0.6.220

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 (46) hide show
  1. package/README.md +72 -25
  2. package/index.ts +4 -7
  3. package/lib/index.d.ts +4 -7
  4. package/lib/index.js +4 -5
  5. package/lib/src/agents.d.ts +15 -6
  6. package/lib/src/agents.js +30 -12
  7. package/lib/src/chat.d.ts +7 -0
  8. package/lib/src/chat.js +19 -0
  9. package/lib/src/config.d.ts +2 -2
  10. package/lib/src/config.js +1 -1
  11. package/lib/src/genie.js +5 -4
  12. package/lib/src/memory.d.ts +2 -2
  13. package/lib/src/memory.js +2 -2
  14. package/lib/src/model.d.ts +2 -0
  15. package/lib/src/model.js +7 -1
  16. package/lib/src/plugin.d.ts +1 -1
  17. package/lib/src/plugin.js +3 -31
  18. package/lib/src/processors.d.ts +4 -28
  19. package/lib/src/processors.js +4 -61
  20. package/lib/src/server.d.ts +10 -21
  21. package/lib/src/server.js +52 -28
  22. package/lib/src/serving-sanitize.d.ts +8 -5
  23. package/lib/src/serving-sanitize.js +16 -11
  24. package/lib/src/trace-io.js +7 -3
  25. package/lib/src/writer.d.ts +7 -7
  26. package/lib/src/writer.js +12 -9
  27. package/lib/tsconfig.tsbuildinfo +1 -1
  28. package/package.json +23 -22
  29. package/src/agents.ts +92 -27
  30. package/src/chat.ts +20 -0
  31. package/src/config.ts +5 -2
  32. package/src/genie.ts +7 -11
  33. package/src/memory.ts +8 -8
  34. package/src/model.ts +7 -0
  35. package/src/plugin.ts +2 -30
  36. package/src/processors.ts +4 -66
  37. package/src/server.ts +49 -27
  38. package/src/serving-sanitize.ts +14 -10
  39. package/src/trace-io.ts +7 -2
  40. package/src/writer.ts +15 -11
  41. package/lib/src/history.d.ts +0 -93
  42. package/lib/src/history.js +0 -231
  43. package/lib/src/threads.d.ts +0 -104
  44. package/lib/src/threads.js +0 -264
  45. package/src/history.ts +0 -270
  46. package/src/threads.ts +0 -309
@@ -1,104 +0,0 @@
1
- /**
2
- * Conversation-thread listing exposed as a Mastra custom API route.
3
- *
4
- * Backed entirely by native Mastra: looks up the active agent by id,
5
- * asks its `Memory` instance to `listThreads` filtered to the caller's
6
- * resource, and shapes each into the JSON-safe {@link MastraThread}
7
- * wire type. A sibling `DELETE` removes a single named thread.
8
- *
9
- * A sibling `DELETE` removes a single named thread; a `PATCH` renames
10
- * one ({@link renameThread}).
11
- *
12
- * Like {@link historyRoute} this registers through Mastra's
13
- * `registerApiRoute` so it shares the `MastraServer` auth-middleware
14
- * pipeline (in `./server.ts`), which has already stamped the resource
15
- * id (`MASTRA_RESOURCE_ID_KEY`) and the targeted thread id
16
- * (`MASTRA_THREAD_ID_KEY`, resolved from the thread-selection header /
17
- * cookie) on `RequestContext` by the time a handler runs. Resource
18
- * scoping lives here so a caller can only ever see, rename, or delete
19
- * its own threads; no cookie or user lookups happen in this module.
20
- *
21
- * @module
22
- */
23
- import { type MastraThread, type MastraThreadsResponse } from "@dbx-tools/shared-mastra";
24
- import type { Agent } from "@mastra/core/agent";
25
- import { type AgentRouteOptions } from "./_agent-route-context.ts";
26
- /** Inputs accepted by {@link listThreads}. */
27
- export interface ListThreadsOptions {
28
- agent: Agent;
29
- resourceId: string;
30
- page?: number;
31
- perPage?: number;
32
- }
33
- /**
34
- * Fetch a page of the resource's conversation threads, newest
35
- * (`updatedAt` DESC) first.
36
- *
37
- * Uses the agent's resolved `Memory` (`getMemory()`) so the per-agent
38
- * storage namespace (`mastra_<agentId>` schema) applies automatically.
39
- * When the agent has no memory configured the response is a successful
40
- * empty page so callers don't have to special-case stateless agents.
41
- */
42
- export declare function listThreads(opts: ListThreadsOptions): Promise<MastraThreadsResponse>;
43
- /** Inputs accepted by {@link deleteThread}. */
44
- export interface DeleteThreadOptions {
45
- agent: Agent;
46
- threadId: string;
47
- resourceId: string;
48
- }
49
- /**
50
- * Delete a single named thread (and every message on it).
51
- *
52
- * Ownership is enforced: the thread is only removed when it belongs to
53
- * the calling resource, so a client can't delete another user's
54
- * conversation by guessing its id. A thread that doesn't exist (or
55
- * isn't owned by the caller) is a successful no-op (`deleted: false`)
56
- * so the UI can fire-and-forget.
57
- */
58
- export declare function deleteThread(opts: DeleteThreadOptions): Promise<{
59
- deleted: boolean;
60
- }>;
61
- /** Inputs accepted by {@link renameThread}. */
62
- export interface RenameThreadOptions {
63
- agent: Agent;
64
- threadId: string;
65
- resourceId: string;
66
- title: string;
67
- }
68
- /**
69
- * Rename a single thread, returning the updated wire thread.
70
- *
71
- * Ownership is enforced the same way {@link deleteThread} enforces it:
72
- * the title is only changed when the thread belongs to the calling
73
- * resource, so a client can't rename another user's conversation by
74
- * guessing its id. Existing thread `metadata` is preserved untouched
75
- * (Mastra's `updateThread` replaces the row, so it must be passed back
76
- * in). Returns `null` when the thread doesn't exist, isn't owned by the
77
- * caller, or the agent has no memory configured, letting the route map
78
- * that to a 404.
79
- */
80
- export declare function renameThread(opts: RenameThreadOptions): Promise<MastraThread | null>;
81
- /** Options accepted by {@link threadsRoute}. */
82
- export type ThreadsRouteOptions = AgentRouteOptions;
83
- /**
84
- * Register the `<path>` Mastra custom API route. Handles three methods
85
- * on the same mount:
86
- *
87
- * - `GET`: a page of the resource's conversation threads
88
- * ({@link listThreads}).
89
- * - `DELETE`: remove the thread named by the thread-selection header
90
- * / `?threadId=` query ({@link deleteThread}). The id is read from
91
- * `RequestContext` (the auth middleware resolves it the same way it
92
- * does for streaming and history), so the client deletes any of its
93
- * threads by stamping the target id - no separate path param.
94
- * - `PATCH`: rename the thread named by the thread-selection header /
95
- * `?threadId=` query to the `{ title }` in the JSON body
96
- * ({@link renameThread}). Targets a thread the same way `DELETE`
97
- * does; 404s when the thread isn't owned by the caller.
98
- *
99
- * Follows the `@mastra/ai-sdk` agent-binding convention: pass `agent`
100
- * for a fixed-agent mount, or include `:agentId` in the path for
101
- * dynamic routing. The plugin registers both `/route/threads` (default
102
- * agent) and `/route/threads/:agentId`.
103
- */
104
- export declare function threadsRoute(options: ThreadsRouteOptions): import("@mastra/core/server").ApiRoute[];
@@ -1,264 +0,0 @@
1
- /**
2
- * Conversation-thread listing exposed as a Mastra custom API route.
3
- *
4
- * Backed entirely by native Mastra: looks up the active agent by id,
5
- * asks its `Memory` instance to `listThreads` filtered to the caller's
6
- * resource, and shapes each into the JSON-safe {@link MastraThread}
7
- * wire type. A sibling `DELETE` removes a single named thread.
8
- *
9
- * A sibling `DELETE` removes a single named thread; a `PATCH` renames
10
- * one ({@link renameThread}).
11
- *
12
- * Like {@link historyRoute} this registers through Mastra's
13
- * `registerApiRoute` so it shares the `MastraServer` auth-middleware
14
- * pipeline (in `./server.ts`), which has already stamped the resource
15
- * id (`MASTRA_RESOURCE_ID_KEY`) and the targeted thread id
16
- * (`MASTRA_THREAD_ID_KEY`, resolved from the thread-selection header /
17
- * cookie) on `RequestContext` by the time a handler runs. Resource
18
- * scoping lives here so a caller can only ever see, rename, or delete
19
- * its own threads; no cookie or user lookups happen in this module.
20
- *
21
- * @module
22
- */
23
- import { log } from "@dbx-tools/shared-core";
24
- import { wire, } from "@dbx-tools/shared-mastra";
25
- import { registerApiRoute } from "@mastra/core/server";
26
- import { resolveAgentRequestContext, resolveAgentRouteOptions, } from "./_agent-route-context.js";
27
- import { clampPerPage, parseIntParam } from "./pagination.js";
28
- import { invalidFields } from "./validation.js";
29
- const logger = log.logger("mastra/threads");
30
- /** Default threads page size. */
31
- const DEFAULT_PER_PAGE = 30;
32
- /** Hard cap so a misbehaving client can't fetch every thread at once. */
33
- const MAX_PER_PAGE = 200;
34
- /** Stable client message for a rename body that fails schema validation. */
35
- const INVALID_RENAME_MESSAGE = "Invalid thread rename request";
36
- /**
37
- * Fetch a page of the resource's conversation threads, newest
38
- * (`updatedAt` DESC) first.
39
- *
40
- * Uses the agent's resolved `Memory` (`getMemory()`) so the per-agent
41
- * storage namespace (`mastra_<agentId>` schema) applies automatically.
42
- * When the agent has no memory configured the response is a successful
43
- * empty page so callers don't have to special-case stateless agents.
44
- */
45
- export async function listThreads(opts) {
46
- const perPage = clampPerPage(opts.perPage, {
47
- fallback: DEFAULT_PER_PAGE,
48
- max: MAX_PER_PAGE,
49
- });
50
- const page = Math.max(0, Math.trunc(opts.page ?? 0));
51
- const memory = await opts.agent.getMemory();
52
- if (!memory) {
53
- logger.debug("list:no-memory", { agentId: opts.agent.id });
54
- return { threads: [], page, perPage, total: 0, hasMore: false };
55
- }
56
- const startedAt = Date.now();
57
- const result = await memory.listThreads({
58
- filter: { resourceId: opts.resourceId },
59
- page,
60
- perPage,
61
- orderBy: { field: "updatedAt", direction: "DESC" },
62
- });
63
- const threads = result.threads.map(toWireThread);
64
- logger.debug("list:done", {
65
- agentId: opts.agent.id,
66
- resourceId: opts.resourceId,
67
- page,
68
- perPage,
69
- returned: threads.length,
70
- total: result.total,
71
- hasMore: result.hasMore,
72
- elapsedMs: Date.now() - startedAt,
73
- });
74
- return {
75
- threads,
76
- page,
77
- perPage,
78
- total: result.total,
79
- hasMore: result.hasMore,
80
- };
81
- }
82
- /**
83
- * Delete a single named thread (and every message on it).
84
- *
85
- * Ownership is enforced: the thread is only removed when it belongs to
86
- * the calling resource, so a client can't delete another user's
87
- * conversation by guessing its id. A thread that doesn't exist (or
88
- * isn't owned by the caller) is a successful no-op (`deleted: false`)
89
- * so the UI can fire-and-forget.
90
- */
91
- export async function deleteThread(opts) {
92
- const memory = await opts.agent.getMemory();
93
- if (!memory) {
94
- logger.debug("delete:no-memory", { agentId: opts.agent.id });
95
- return { deleted: false };
96
- }
97
- // Confirm the thread exists and is owned by the caller before
98
- // deleting; never let a guessed id touch another resource's data.
99
- const existing = await memory.getThreadById({ threadId: opts.threadId });
100
- if (!existing || existing.resourceId !== opts.resourceId) {
101
- logger.debug("delete:not-owned", {
102
- agentId: opts.agent.id,
103
- threadId: opts.threadId,
104
- found: existing !== null,
105
- });
106
- return { deleted: false };
107
- }
108
- const startedAt = Date.now();
109
- await memory.deleteThread(opts.threadId);
110
- logger.info("delete:done", {
111
- agentId: opts.agent.id,
112
- threadId: opts.threadId,
113
- elapsedMs: Date.now() - startedAt,
114
- });
115
- return { deleted: true };
116
- }
117
- /**
118
- * Rename a single thread, returning the updated wire thread.
119
- *
120
- * Ownership is enforced the same way {@link deleteThread} enforces it:
121
- * the title is only changed when the thread belongs to the calling
122
- * resource, so a client can't rename another user's conversation by
123
- * guessing its id. Existing thread `metadata` is preserved untouched
124
- * (Mastra's `updateThread` replaces the row, so it must be passed back
125
- * in). Returns `null` when the thread doesn't exist, isn't owned by the
126
- * caller, or the agent has no memory configured, letting the route map
127
- * that to a 404.
128
- */
129
- export async function renameThread(opts) {
130
- const memory = await opts.agent.getMemory();
131
- if (!memory) {
132
- logger.debug("rename:no-memory", { agentId: opts.agent.id });
133
- return null;
134
- }
135
- const existing = await memory.getThreadById({ threadId: opts.threadId });
136
- if (!existing || existing.resourceId !== opts.resourceId) {
137
- logger.debug("rename:not-owned", {
138
- agentId: opts.agent.id,
139
- threadId: opts.threadId,
140
- found: existing !== null,
141
- });
142
- return null;
143
- }
144
- const startedAt = Date.now();
145
- const updated = await memory.updateThread({
146
- id: opts.threadId,
147
- title: opts.title,
148
- metadata: existing.metadata ?? {},
149
- });
150
- logger.info("rename:done", {
151
- agentId: opts.agent.id,
152
- threadId: opts.threadId,
153
- elapsedMs: Date.now() - startedAt,
154
- });
155
- return toWireThread(updated);
156
- }
157
- /**
158
- * Register the `<path>` Mastra custom API route. Handles three methods
159
- * on the same mount:
160
- *
161
- * - `GET`: a page of the resource's conversation threads
162
- * ({@link listThreads}).
163
- * - `DELETE`: remove the thread named by the thread-selection header
164
- * / `?threadId=` query ({@link deleteThread}). The id is read from
165
- * `RequestContext` (the auth middleware resolves it the same way it
166
- * does for streaming and history), so the client deletes any of its
167
- * threads by stamping the target id - no separate path param.
168
- * - `PATCH`: rename the thread named by the thread-selection header /
169
- * `?threadId=` query to the `{ title }` in the JSON body
170
- * ({@link renameThread}). Targets a thread the same way `DELETE`
171
- * does; 404s when the thread isn't owned by the caller.
172
- *
173
- * Follows the `@mastra/ai-sdk` agent-binding convention: pass `agent`
174
- * for a fixed-agent mount, or include `:agentId` in the path for
175
- * dynamic routing. The plugin registers both `/route/threads` (default
176
- * agent) and `/route/threads/:agentId`.
177
- */
178
- export function threadsRoute(options) {
179
- const { path, fixedAgent } = resolveAgentRouteOptions(options, "threadsRoute.path");
180
- return [
181
- registerApiRoute(path, {
182
- method: "GET",
183
- handler: async (c) => {
184
- const ctx = resolveAgentRequestContext(c, { fixedAgent });
185
- if ("error" in ctx)
186
- return ctx.error;
187
- const payload = await listThreads({
188
- agent: ctx.agent,
189
- resourceId: ctx.resourceId,
190
- page: parseIntParam(c.req.query("page")),
191
- perPage: parseIntParam(c.req.query("perPage")),
192
- });
193
- return c.json(payload);
194
- },
195
- }),
196
- registerApiRoute(path, {
197
- method: "DELETE",
198
- handler: async (c) => {
199
- const ctx = resolveAgentRequestContext(c, { fixedAgent, threadId: "required" });
200
- if ("error" in ctx)
201
- return ctx.error;
202
- const { deleted } = await deleteThread({
203
- agent: ctx.agent,
204
- threadId: ctx.threadId,
205
- resourceId: ctx.resourceId,
206
- });
207
- const payload = {
208
- ok: true,
209
- agentId: ctx.agentId,
210
- threadId: ctx.threadId,
211
- deleted,
212
- };
213
- return c.json(payload);
214
- },
215
- }),
216
- registerApiRoute(path, {
217
- method: "PATCH",
218
- handler: async (c) => {
219
- const ctx = resolveAgentRequestContext(c, { fixedAgent, threadId: "required" });
220
- if ("error" in ctx)
221
- return ctx.error;
222
- const body = wire.MastraUpdateThreadRequestSchema.safeParse(await c.req.json());
223
- if (!body.success) {
224
- logger.warn("rename:invalid", { error: body.error.message });
225
- return c.json({ error: INVALID_RENAME_MESSAGE, fields: invalidFields(body.error) }, 400);
226
- }
227
- const thread = await renameThread({
228
- agent: ctx.agent,
229
- threadId: ctx.threadId,
230
- resourceId: ctx.resourceId,
231
- title: body.data.title,
232
- });
233
- if (!thread) {
234
- return c.json({ error: `Unknown thread "${ctx.threadId}"` }, 404);
235
- }
236
- const payload = {
237
- ok: true,
238
- agentId: ctx.agentId,
239
- thread,
240
- };
241
- return c.json(payload);
242
- },
243
- }),
244
- ];
245
- }
246
- /** Coerce a Mastra `StorageThreadType` into the JSON-safe wire shape. */
247
- function toWireThread(thread) {
248
- return {
249
- id: thread.id,
250
- ...(thread.title ? { title: thread.title } : {}),
251
- resourceId: thread.resourceId,
252
- createdAt: toIso(thread.createdAt),
253
- updatedAt: toIso(thread.updatedAt),
254
- ...(thread.metadata ? { metadata: thread.metadata } : {}),
255
- };
256
- }
257
- /** Render a storage timestamp as an ISO-8601 string for the wire. */
258
- function toIso(value) {
259
- if (value instanceof Date)
260
- return value.toISOString();
261
- const parsed = new Date(value);
262
- return Number.isNaN(parsed.getTime()) ? new Date(0).toISOString() : parsed.toISOString();
263
- }
264
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGhyZWFkcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90aHJlYWRzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FxQkc7QUFFSCxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDN0MsT0FBTyxFQUNMLElBQUksR0FLTCxNQUFNLDBCQUEwQixDQUFDO0FBSWxDLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBRXZELE9BQU8sRUFDTCwwQkFBMEIsRUFDMUIsd0JBQXdCLEdBRXpCLE1BQU0sMkJBQTJCLENBQUM7QUFDbkMsT0FBTyxFQUFFLFlBQVksRUFBRSxhQUFhLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUM5RCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFaEQsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0FBRTVDLGlDQUFpQztBQUNqQyxNQUFNLGdCQUFnQixHQUFHLEVBQUUsQ0FBQztBQUM1Qix5RUFBeUU7QUFDekUsTUFBTSxZQUFZLEdBQUcsR0FBRyxDQUFDO0FBQ3pCLDRFQUE0RTtBQUM1RSxNQUFNLHNCQUFzQixHQUFHLCtCQUErQixDQUFDO0FBVS9EOzs7Ozs7OztHQVFHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxXQUFXLENBQUMsSUFBd0I7SUFDeEQsTUFBTSxPQUFPLEdBQUcsWUFBWSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUU7UUFDekMsUUFBUSxFQUFFLGdCQUFnQjtRQUMxQixHQUFHLEVBQUUsWUFBWTtLQUNsQixDQUFDLENBQUM7SUFDSCxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJ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QU8sQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUN6QixDQUFDO1NBQ0YsQ0FBQztLQUNILENBQUM7QUFDSixDQUFDO0FBRUQseUVBQXlFO0FBQ3pFLFNBQVMsWUFBWSxDQUFDLE1BQXlCO0lBQzdDLE9BQU87UUFDTCxFQUFFLEVBQUUsTUFBTSxDQUFDLEVBQUU7UUFDYixHQUFHLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxLQUFLLEVBQUUsTUFBTSxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDaEQsVUFBVSxFQUFFLE1BQU0sQ0FBQyxVQUFVO1FBQzdCLFNBQVMsRUFBRSxLQUFLLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQztRQUNsQyxTQUFTLEVBQUUsS0FBSyxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUM7UUFDbEMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLEVBQUUsUUFBUSxFQUFFLE1BQU0sQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO0tBQzFELENBQUM7QUFDSixDQUFDO0FBRUQscUVBQXFFO0FBQ3JFLFNBQVMsS0FBSyxDQUFDLEtBQTZCO0lBQzFDLElBQUksS0FBSyxZQUFZLElBQUk7UUFBRSxPQUFPLEtBQUssQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUN0RCxNQUFNLE1BQU0sR0FBRyxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMvQixPQUFPLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLFdBQVcsRUFBRSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsV0FBVyxFQUFFLENBQUM7QUFDM0YsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQ29udmVyc2F0aW9uLXRocmVhZCBsaXN0aW5nIGV4cG9zZWQgYXMgYSBNYXN0cmEgY3VzdG9tIEFQSSByb3V0ZS5cbiAqXG4gKiBCYWNrZWQgZW50aXJlbHkgYnkgbmF0aXZlIE1hc3RyYTogbG9va3MgdXAgdGhlIGFjdGl2ZSBhZ2VudCBieSBpZCxcbiAqIGFza3MgaXRzIGBNZW1vcnlgIGluc3RhbmNlIHRvIGBsaXN0VGhyZWFkc2AgZmlsdGVyZWQgdG8gdGhlIGNhbGxlcidzXG4gKiByZXNvdXJjZSwgYW5kIHNoYXBlcyBlYWNoIGludG8gdGhlIEpTT04tc2FmZSB7QGxpbmsgTWFzdHJhVGhyZWFkfVxuICogd2lyZSB0eXBlLiBBIHNpYmxpbmcgYERFTEVURWAgcmVtb3ZlcyBhIHNpbmdsZSBuYW1lZCB0aHJlYWQuXG4gKlxuICogQSBzaWJsaW5nIGBERUxFVEVgIHJlbW92ZXMgYSBzaW5nbGUgbmFtZWQgdGhyZWFkOyBhIGBQQVRDSGAgcmVuYW1lc1xuICogb25lICh7QGxpbmsgcmVuYW1lVGhyZWFkfSkuXG4gKlxuICogTGlrZSB7QGxpbmsgaGlzdG9yeVJvdXRlfSB0aGlzIHJlZ2lzdGVycyB0aHJvdWdoIE1hc3RyYSdzXG4gKiBgcmVnaXN0ZXJBcGlSb3V0ZWAgc28gaXQgc2hhcmVzIHRoZSBgTWFzdHJhU2VydmVyYCBhdXRoLW1pZGRsZXdhcmVcbiAqIHBpcGVsaW5lIChpbiBgLi9zZXJ2ZXIudHNgKSwgd2hpY2ggaGFzIGFscmVhZHkgc3RhbXBlZCB0aGUgcmVzb3VyY2VcbiAqIGlkIChgTUFTVFJBX1JFU09VUkNFX0lEX0tFWWApIGFuZCB0aGUgdGFyZ2V0ZWQgdGhyZWFkIGlkXG4gKiAoYE1BU1RSQV9USFJFQURfSURfS0VZYCwgcmVzb2x2ZWQgZnJvbSB0aGUgdGhyZWFkLXNlbGVjdGlvbiBoZWFkZXIgL1xuICogY29va2llKSBvbiBgUmVxdWVzdENvbnRleHRgIGJ5IHRoZSB0aW1lIGEgaGFuZGxlciBydW5zLiBSZXNvdXJjZVxuICogc2NvcGluZyBsaXZlcyBoZXJlIHNvIGEgY2FsbGVyIGNhbiBvbmx5IGV2ZXIgc2VlLCByZW5hbWUsIG9yIGRlbGV0ZVxuICogaXRzIG93biB0aHJlYWRzOyBubyBjb29raWUgb3IgdXNlciBsb29rdXBzIGhhcHBlbiBpbiB0aGlzIG1vZHVsZS5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHsgbG9nIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB7XG4gIHdpcmUsXG4gIHR5cGUgTWFzdHJhRGVsZXRlVGhyZWFkUmVzcG9uc2UsXG4gIHR5cGUgTWFzdHJhVGhyZWFkLFxuICB0eXBlIE1hc3RyYVRocmVhZHNSZXNwb25zZSxcbiAgdHlwZSBNYXN0cmFVcGRhdGVUaHJlYWRSZXNwb25zZSxcbn0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLW1hc3RyYVwiO1xuaW1wb3J0IHR5cGUgeyBBZ2VudCB9IGZyb20gXCJAbWFzdHJhL2NvcmUvYWdlbnRcIjtcbmltcG9ydCB0eXBlIHsgU3RvcmFnZVRocmVhZFR5cGUgfSBmcm9tIFwiQG1hc3RyYS9jb3JlL21lbW9yeVwiO1xuaW1wb3J0IHR5cGUgeyBDb250ZXh0V2l0aE1hc3RyYSB9IGZyb20gXCJAbWFzdHJhL2NvcmUvc2VydmVyXCI7XG5pbXBvcnQgeyByZWdpc3RlckFwaVJvdXRlIH0gZnJvbSBcIkBtYXN0cmEvY29yZS9zZXJ2ZXJcIjtcblxuaW1wb3J0IHtcbiAgcmVzb2x2ZUFnZW50UmVxdWVzdENvbnRleHQsXG4gIHJlc29sdmVBZ2VudFJvdXRlT3B0aW9ucyxcbiAgdHlwZSBBZ2VudFJvdXRlT3B0aW9ucyxcbn0gZnJvbSBcIi4vX2FnZW50LXJvdXRlLWNvbnRleHQudHNcIjtcbmltcG9ydCB7IGNsYW1wUGVyUGFnZSwgcGFyc2VJbnRQYXJhbSB9IGZyb20gXCIuL3BhZ2luYXRpb24udHNcIjtcbmltcG9ydCB7IGludmFsaWRGaWVsZHMgfSBmcm9tIFwiLi92YWxpZGF0aW9uLnRzXCI7XG5cbmNvbnN0IGxvZ2dlciA9IGxvZy5sb2dnZXIoXCJtYXN0cmEvdGhyZWFkc1wiKTtcblxuLyoqIERlZmF1bHQgdGhyZWFkcyBwYWdlIHNpemUuICovXG5jb25zdCBERUZBVUxUX1BFUl9QQUdFID0gMzA7XG4vKiogSGFyZCBjYXAgc28gYSBtaXNiZWhhdmluZyBjbGllbnQgY2FuJ3QgZmV0Y2ggZXZlcnkgdGhyZWFkIGF0IG9uY2UuICovXG5jb25zdCBNQVhfUEVSX1BBR0UgPSAyMDA7XG4vKiogU3RhYmxlIGNsaWVudCBtZXNzYWdlIGZvciBhIHJlbmFtZSBib2R5IHRoYXQgZmFpbHMgc2NoZW1hIHZhbGlkYXRpb24uICovXG5jb25zdCBJTlZBTElEX1JFTkFNRV9NRVNTQUdFID0gXCJJbnZhbGlkIHRocmVhZCByZW5hbWUgcmVxdWVzdFwiO1xuXG4vKiogSW5wdXRzIGFjY2VwdGVkIGJ5IHtAbGluayBsaXN0VGhyZWFkc30uICovXG5leHBvcnQgaW50ZXJmYWNlIExpc3RUaHJlYWRzT3B0aW9ucyB7XG4gIGFnZW50OiBBZ2VudDtcbiAgcmVzb3VyY2VJZDogc3RyaW5nO1xuICBwYWdlPzogbnVtYmVyO1xuICBwZXJQYWdlPzogbnVtYmVyO1xufVxuXG4vKipcbiAqIEZldGNoIGEgcGFnZSBvZiB0aGUgcmVzb3VyY2UncyBjb252ZXJzYXRpb24gdGhyZWFkcywgbmV3ZXN0XG4gKiAoYHVwZGF0ZWRBdGAgREVTQykgZmlyc3QuXG4gKlxuICogVXNlcyB0aGUgYWdlbnQncyByZXNvbHZlZCBgTWVtb3J5YCAoYGdldE1lbW9yeSgpYCkgc28gdGhlIHBlci1hZ2VudFxuICogc3RvcmFnZSBuYW1lc3BhY2UgKGBtYXN0cmFfPGFnZW50SWQ+YCBzY2hlbWEpIGFwcGxpZXMgYXV0b21hdGljYWxseS5cbiAqIFdoZW4gdGhlIGFnZW50IGhhcyBubyBtZW1vcnkgY29uZmlndXJlZCB0aGUgcmVzcG9uc2UgaXMgYSBzdWNjZXNzZnVsXG4gKiBlbXB0eSBwYWdlIHNvIGNhbGxlcnMgZG9uJ3QgaGF2ZSB0byBzcGVjaWFsLWNhc2Ugc3RhdGVsZXNzIGFnZW50cy5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxpc3RUaHJlYWRzKG9wdHM6IExpc3RUaHJlYWRzT3B0aW9ucyk6IFByb21pc2U8TWFzdHJhVGhyZWFkc1Jlc3BvbnNlPiB7XG4gIGNvbnN0IHBlclBhZ2UgPSBjbGFtcFBlclBhZ2Uob3B0cy5wZXJQYWdlLCB7XG4gICAgZmFsbGJhY2s6IERFRkFVTFRfUEVSX1BBR0UsXG4gICAgbWF4OiBNQVhfUEVSX1BBR0UsXG4gIH0pO1xuICBjb25zdCBwYWdlID0gTWF0aC5tYXgoMCwgTWF0aC50cnVuYyhvcHRzLnBhZ2UgPz8gMCkpO1xuICBjb25zdCBtZW1vcnkgPSBhd2FpdCBvcHRzLmFnZW50LmdldE1lbW9yeSgpO1xuICBpZiAoIW1lbW9yeSkge1xuICAgIGxvZ2dlci5kZWJ1ZyhcImxpc3Q6bm8tbWVtb3J5XCIsIHsgYWdlbnRJZDogb3B0cy5hZ2VudC5pZCB9KTtcbiAgICByZXR1cm4geyB0aHJlYWRzOiBbXSwgcGFnZSwgcGVyUGFnZSwgdG90YWw6IDAsIGhhc01vcmU6IGZhbHNlIH07XG4gIH1cbiAgY29uc3Qgc3RhcnRlZEF0ID0gRGF0ZS5ub3coKTtcbiAgY29uc3QgcmVzdWx0ID0gYXdhaXQgbWVtb3J5Lmxpc3RUaHJlYWRzKHtcbiAgICBmaWx0ZXI6IHsgcmVzb3VyY2VJZDogb3B0cy5yZXNvdXJjZUlkIH0sXG4gICAgcGFnZSxcbiAgICBwZXJQYWdlLFxuICAgIG9yZGVyQnk6IHsgZmllbGQ6IFwidXBkYXRlZEF0XCIsIGRpcmVjdGlvbjogXCJERVNDXCIgfSxcbiAgfSk7XG4gIGNvbnN0IHRocmVhZHMgPSByZXN1bHQudGhyZWFkcy5tYXAodG9XaXJlVGhyZWFkKTtcbiAgbG9nZ2VyLmRlYnVnKFwibGlzdDpkb25lXCIsIHtcbiAgICBhZ2VudElkOiBvcHRzLmFnZW50LmlkLFxuICAgIHJlc291cmNlSWQ6IG9wdHMucmVzb3VyY2VJZCxcbiAgICBwYWdlLFxuICAgIHBlclBhZ2UsXG4gICAgcmV0dXJuZWQ6IHRocmVhZHMubGVuZ3RoLFxuICAgIHRvdGFsOiByZXN1bHQudG90YWwsXG4gICAgaGFzTW9yZTogcmVzdWx0Lmhhc01vcmUsXG4gICAgZWxhcHNlZE1zOiBEYXRlLm5vdygpIC0gc3RhcnRlZEF0LFxuICB9KTtcbiAgcmV0dXJuIHtcbiAgICB0aHJlYWRzLFxuICAgIHBhZ2UsXG4gICAgcGVyUGFnZSxcbiAgICB0b3RhbDogcmVzdWx0LnRvdGFsLFxuICAgIGhhc01vcmU6IHJlc3VsdC5oYXNNb3JlLFxuICB9O1xufVxuXG4vKiogSW5wdXRzIGFjY2VwdGVkIGJ5IHtAbGluayBkZWxldGVUaHJlYWR9LiAqL1xuZXhwb3J0IGludGVyZmFjZSBEZWxldGVUaHJlYWRPcHRpb25zIHtcbiAgYWdlbnQ6IEFnZW50O1xuICB0aHJlYWRJZDogc3RyaW5nO1xuICByZXNvdXJjZUlkOiBzdHJpbmc7XG59XG5cbi8qKlxuICogRGVsZXRlIGEgc2luZ2xlIG5hbWVkIHRocmVhZCAoYW5kIGV2ZXJ5IG1lc3NhZ2Ugb24gaXQpLlxuICpcbiAqIE93bmVyc2hpcCBpcyBlbmZvcmNlZDogdGhlIHRocmVhZCBpcyBvbmx5IHJlbW92ZWQgd2hlbiBpdCBiZWxvbmdzIHRvXG4gKiB0aGUgY2FsbGluZyByZXNvdXJjZSwgc28gYSBjbGllbnQgY2FuJ3QgZGVsZXRlIGFub3RoZXIgdXNlcidzXG4gKiBjb252ZXJzYXRpb24gYnkgZ3Vlc3NpbmcgaXRzIGlkLiBBIHRocmVhZCB0aGF0IGRvZXNuJ3QgZXhpc3QgKG9yXG4gKiBpc24ndCBvd25lZCBieSB0aGUgY2FsbGVyKSBpcyBhIHN1Y2Nlc3NmdWwgbm8tb3AgKGBkZWxldGVkOiBmYWxzZWApXG4gKiBzbyB0aGUgVUkgY2FuIGZpcmUtYW5kLWZvcmdldC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGRlbGV0ZVRocmVhZChvcHRzOiBEZWxldGVUaHJlYWRPcHRpb25zKTogUHJvbWlzZTx7IGRlbGV0ZWQ6IGJvb2xlYW4gfT4ge1xuICBjb25zdCBtZW1vcnkgPSBhd2FpdCBvcHRzLmFnZW50LmdldE1lbW9yeSgpO1xuICBpZiAoIW1lbW9yeSkge1xuICAgIGxvZ2dlci5kZWJ1ZyhcImRlbGV0ZTpuby1tZW1vcnlcIiwgeyBhZ2VudElkOiBvcHRzLmFnZW50LmlkIH0pO1xuICAgIHJldHVybiB7IGRlbGV0ZWQ6IGZhbHNlIH07XG4gIH1cbiAgLy8gQ29uZmlybSB0aGUgdGhyZWFkIGV4aXN0cyBhbmQgaXMgb3duZWQgYnkgdGhlIGNhbGxlciBiZWZvcmVcbiAgLy8gZGVsZXRpbmc7IG5ldmVyIGxldCBhIGd1ZXNzZWQgaWQgdG91Y2ggYW5vdGhlciByZXNvdXJjZSdzIGRhdGEuXG4gIGNvbnN0IGV4aXN0aW5nID0gYXdhaXQgbWVtb3J5LmdldFRocmVhZEJ5SWQoeyB0aHJlYWRJZDogb3B0cy50aHJlYWRJZCB9KTtcbiAgaWYgKCFleGlzdGluZyB8fCBleGlzdGluZy5yZXNvdXJjZUlkICE9PSBvcHRzLnJlc291cmNlSWQpIHtcbiAgICBsb2dnZXIuZGVidWcoXCJkZWxldGU6bm90LW93bmVkXCIsIHtcbiAgICAgIGFnZW50SWQ6IG9wdHMuYWdlbnQuaWQsXG4gICAgICB0aHJlYWRJZDogb3B0cy50aHJlYWRJZCxcbiAgICAgIGZvdW5kOiBleGlzdGluZyAhPT0gbnVsbCxcbiAgICB9KTtcbiAgICByZXR1cm4geyBkZWxldGVkOiBmYWxzZSB9O1xuICB9XG4gIGNvbnN0IHN0YXJ0ZWRBdCA9IERhdGUubm93KCk7XG4gIGF3YWl0IG1lbW9yeS5kZWxldGVUaHJlYWQob3B0cy50aHJlYWRJZCk7XG4gIGxvZ2dlci5pbmZvKFwiZGVsZXRlOmRvbmVcIiwge1xuICAgIGFnZW50SWQ6IG9wdHMuYWdlbnQuaWQsXG4gICAgdGhyZWFkSWQ6IG9wdHMudGhyZWFkSWQsXG4gICAgZWxhcHNlZE1zOiBEYXRlLm5vdygpIC0gc3RhcnRlZEF0LFxuICB9KTtcbiAgcmV0dXJuIHsgZGVsZXRlZDogdHJ1ZSB9O1xufVxuXG4vKiogSW5wdXRzIGFjY2VwdGVkIGJ5IHtAbGluayByZW5hbWVUaHJlYWR9LiAqL1xuZXhwb3J0IGludGVyZmFjZSBSZW5hbWVUaHJlYWRPcHRpb25zIHtcbiAgYWdlbnQ6IEFnZW50O1xuICB0aHJlYWRJZDogc3RyaW5nO1xuICByZXNvdXJjZUlkOiBzdHJpbmc7XG4gIHRpdGxlOiBzdHJpbmc7XG59XG5cbi8qKlxuICogUmVuYW1lIGEgc2luZ2xlIHRocmVhZCwgcmV0dXJuaW5nIHRoZSB1cGRhdGVkIHdpcmUgdGhyZWFkLlxuICpcbiAqIE93bmVyc2hpcCBpcyBlbmZvcmNlZCB0aGUgc2FtZSB3YXkge0BsaW5rIGRlbGV0ZVRocmVhZH0gZW5mb3JjZXMgaXQ6XG4gKiB0aGUgdGl0bGUgaXMgb25seSBjaGFuZ2VkIHdoZW4gdGhlIHRocmVhZCBiZWxvbmdzIHRvIHRoZSBjYWxsaW5nXG4gKiByZXNvdXJjZSwgc28gYSBjbGllbnQgY2FuJ3QgcmVuYW1lIGFub3RoZXIgdXNlcidzIGNvbnZlcnNhdGlvbiBieVxuICogZ3Vlc3NpbmcgaXRzIGlkLiBFeGlzdGluZyB0aHJlYWQgYG1ldGFkYXRhYCBpcyBwcmVzZXJ2ZWQgdW50b3VjaGVkXG4gKiAoTWFzdHJhJ3MgYHVwZGF0ZVRocmVhZGAgcmVwbGFjZXMgdGhlIHJvdywgc28gaXQgbXVzdCBiZSBwYXNzZWQgYmFja1xuICogaW4pLiBSZXR1cm5zIGBudWxsYCB3aGVuIHRoZSB0aHJlYWQgZG9lc24ndCBleGlzdCwgaXNuJ3Qgb3duZWQgYnkgdGhlXG4gKiBjYWxsZXIsIG9yIHRoZSBhZ2VudCBoYXMgbm8gbWVtb3J5IGNvbmZpZ3VyZWQsIGxldHRpbmcgdGhlIHJvdXRlIG1hcFxuICogdGhhdCB0byBhIDQwNC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlbmFtZVRocmVhZChvcHRzOiBSZW5hbWVUaHJlYWRPcHRpb25zKTogUHJvbWlzZTxNYXN0cmFUaHJlYWQgfCBudWxsPiB7XG4gIGNvbnN0IG1lbW9yeSA9IGF3YWl0IG9wdHMuYWdlbnQuZ2V0TWVtb3J5KCk7XG4gIGlmICghbWVtb3J5KSB7XG4gICAgbG9nZ2VyLmRlYnVnKFwicmVuYW1lOm5vLW1lbW9yeVwiLCB7IGFnZW50SWQ6IG9wdHMuYWdlbnQuaWQgfSk7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgY29uc3QgZXhpc3RpbmcgPSBhd2FpdCBtZW1vcnkuZ2V0VGhyZWFkQnlJZCh7IHRocmVhZElkOiBvcHRzLnRocmVhZElkIH0pO1xuICBpZiAoIWV4aXN0aW5nIHx8IGV4aXN0aW5nLnJlc291cmNlSWQgIT09IG9wdHMucmVzb3VyY2VJZCkge1xuICAgIGxvZ2dlci5kZWJ1ZyhcInJlbmFtZTpub3Qtb3duZWRcIiwge1xuICAgICAgYWdlbnRJZDogb3B0cy5hZ2VudC5pZCxcbiAgICAgIHRocmVhZElkOiBvcHRzLnRocmVhZElkLFxuICAgICAgZm91bmQ6IGV4aXN0aW5nICE9PSBudWxsLFxuICAgIH0pO1xuICAgIHJldHVybiBudWxsO1xuICB9XG4gIGNvbnN0IHN0YXJ0ZWRBdCA9IERhdGUubm93KCk7XG4gIGNvbnN0IHVwZGF0ZWQgPSBhd2FpdCBtZW1vcnkudXBkYXRlVGhyZWFkKHtcbiAgICBpZDogb3B0cy50aHJlYWRJZCxcbiAgICB0aXRsZTogb3B0cy50aXRsZSxcbiAgICBtZXRhZGF0YTogZXhpc3RpbmcubWV0YWRhdGEgPz8ge30sXG4gIH0pO1xuICBsb2dnZXIuaW5mbyhcInJlbmFtZTpkb25lXCIsIHtcbiAgICBhZ2VudElkOiBvcHRzLmFnZW50LmlkLFxuICAgIHRocmVhZElkOiBvcHRzLnRocmVhZElkLFxuICAgIGVsYXBzZWRNczogRGF0ZS5ub3coKSAtIHN0YXJ0ZWRBdCxcbiAgfSk7XG4gIHJldHVybiB0b1dpcmVUaHJlYWQodXBkYXRlZCk7XG59XG5cbi8qKiBPcHRpb25zIGFjY2VwdGVkIGJ5IHtAbGluayB0aHJlYWRzUm91dGV9LiAqL1xuZXhwb3J0IHR5cGUgVGhyZWFkc1JvdXRlT3B0aW9ucyA9IEFnZW50Um91dGVPcHRpb25zO1xuXG4vKipcbiAqIFJlZ2lzdGVyIHRoZSBgPHBhdGg+YCBNYXN0cmEgY3VzdG9tIEFQSSByb3V0ZS4gSGFuZGxlcyB0aHJlZSBtZXRob2RzXG4gKiBvbiB0aGUgc2FtZSBtb3VudDpcbiAqXG4gKiAgIC0gYEdFVGA6IGEgcGFnZSBvZiB0aGUgcmVzb3VyY2UncyBjb252ZXJzYXRpb24gdGhyZWFkc1xuICogICAgICh7QGxpbmsgbGlzdFRocmVhZHN9KS5cbiAqICAgLSBgREVMRVRFYDogcmVtb3ZlIHRoZSB0aHJlYWQgbmFtZWQgYnkgdGhlIHRocmVhZC1zZWxlY3Rpb24gaGVhZGVyXG4gKiAgICAgLyBgP3RocmVhZElkPWAgcXVlcnkgKHtAbGluayBkZWxldGVUaHJlYWR9KS4gVGhlIGlkIGlzIHJlYWQgZnJvbVxuICogICAgIGBSZXF1ZXN0Q29udGV4dGAgKHRoZSBhdXRoIG1pZGRsZXdhcmUgcmVzb2x2ZXMgaXQgdGhlIHNhbWUgd2F5IGl0XG4gKiAgICAgZG9lcyBmb3Igc3RyZWFtaW5nIGFuZCBoaXN0b3J5KSwgc28gdGhlIGNsaWVudCBkZWxldGVzIGFueSBvZiBpdHNcbiAqICAgICB0aHJlYWRzIGJ5IHN0YW1waW5nIHRoZSB0YXJnZXQgaWQgLSBubyBzZXBhcmF0ZSBwYXRoIHBhcmFtLlxuICogICAtIGBQQVRDSGA6IHJlbmFtZSB0aGUgdGhyZWFkIG5hbWVkIGJ5IHRoZSB0aHJlYWQtc2VsZWN0aW9uIGhlYWRlciAvXG4gKiAgICAgYD90aHJlYWRJZD1gIHF1ZXJ5IHRvIHRoZSBgeyB0aXRsZSB9YCBpbiB0aGUgSlNPTiBib2R5XG4gKiAgICAgKHtAbGluayByZW5hbWVUaHJlYWR9KS4gVGFyZ2V0cyBhIHRocmVhZCB0aGUgc2FtZSB3YXkgYERFTEVURWBcbiAqICAgICBkb2VzOyA0MDRzIHdoZW4gdGhlIHRocmVhZCBpc24ndCBvd25lZCBieSB0aGUgY2FsbGVyLlxuICpcbiAqIEZvbGxvd3MgdGhlIGBAbWFzdHJhL2FpLXNka2AgYWdlbnQtYmluZGluZyBjb252ZW50aW9uOiBwYXNzIGBhZ2VudGBcbiAqIGZvciBhIGZpeGVkLWFnZW50IG1vdW50LCBvciBpbmNsdWRlIGA6YWdlbnRJZGAgaW4gdGhlIHBhdGggZm9yXG4gKiBkeW5hbWljIHJvdXRpbmcuIFRoZSBwbHVnaW4gcmVnaXN0ZXJzIGJvdGggYC9yb3V0ZS90aHJlYWRzYCAoZGVmYXVsdFxuICogYWdlbnQpIGFuZCBgL3JvdXRlL3RocmVhZHMvOmFnZW50SWRgLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdGhyZWFkc1JvdXRlKG9wdGlvbnM6IFRocmVhZHNSb3V0ZU9wdGlvbnMpIHtcbiAgY29uc3QgeyBwYXRoLCBmaXhlZEFnZW50IH0gPSByZXNvbHZlQWdlbnRSb3V0ZU9wdGlvbnMob3B0aW9ucywgXCJ0aHJlYWRzUm91dGUucGF0aFwiKTtcblxuICByZXR1cm4gW1xuICAgIHJlZ2lzdGVyQXBpUm91dGUocGF0aCwge1xuICAgICAgbWV0aG9kOiBcIkdFVFwiLFxuICAgICAgaGFuZGxlcjogYXN5bmMgKGM6IENvbnRleHRXaXRoTWFzdHJhKSA9PiB7XG4gICAgICAgIGNvbnN0IGN0eCA9IHJlc29sdmVBZ2VudFJlcXVlc3RDb250ZXh0KGMsIHsgZml4ZWRBZ2VudCB9KTtcbiAgICAgICAgaWYgKFwiZXJyb3JcIiBpbiBjdHgpIHJldHVybiBjdHguZXJyb3I7XG4gICAgICAgIGNvbnN0IHBheWxvYWQgPSBhd2FpdCBsaXN0VGhyZWFkcyh7XG4gICAgICAgICAgYWdlbnQ6IGN0eC5hZ2VudCxcbiAgICAgICAgICByZXNvdXJjZUlkOiBjdHgucmVzb3VyY2VJZCxcbiAgICAgICAgICBwYWdlOiBwYXJzZUludFBhcmFtKGMucmVxLnF1ZXJ5KFwicGFnZVwiKSksXG4gICAgICAgICAgcGVyUGFnZTogcGFyc2VJbnRQYXJhbShjLnJlcS5xdWVyeShcInBlclBhZ2VcIikpLFxuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIGMuanNvbihwYXlsb2FkKTtcbiAgICAgIH0sXG4gICAgfSksXG4gICAgcmVnaXN0ZXJBcGlSb3V0ZShwYXRoLCB7XG4gICAgICBtZXRob2Q6IFwiREVMRVRFXCIsXG4gICAgICBoYW5kbGVyOiBhc3luYyAoYzogQ29udGV4dFdpdGhNYXN0cmEpID0+IHtcbiAgICAgICAgY29uc3QgY3R4ID0gcmVzb2x2ZUFnZW50UmVxdWVzdENvbnRleHQoYywgeyBmaXhlZEFnZW50LCB0aHJlYWRJZDogXCJyZXF1aXJlZFwiIH0pO1xuICAgICAgICBpZiAoXCJlcnJvclwiIGluIGN0eCkgcmV0dXJuIGN0eC5lcnJvcjtcbiAgICAgICAgY29uc3QgeyBkZWxldGVkIH0gPSBhd2FpdCBkZWxldGVUaHJlYWQoe1xuICAgICAgICAgIGFnZW50OiBjdHguYWdlbnQsXG4gICAgICAgICAgdGhyZWFkSWQ6IGN0eC50aHJlYWRJZCxcbiAgICAgICAgICByZXNvdXJjZUlkOiBjdHgucmVzb3VyY2VJZCxcbiAgICAgICAgfSk7XG4gICAgICAgIGNvbnN0IHBheWxvYWQ6IE1hc3RyYURlbGV0ZVRocmVhZFJlc3BvbnNlID0ge1xuICAgICAgICAgIG9rOiB0cnVlLFxuICAgICAgICAgIGFnZW50SWQ6IGN0eC5hZ2VudElkLFxuICAgICAgICAgIHRocmVhZElkOiBjdHgudGhyZWFkSWQsXG4gICAgICAgICAgZGVsZXRlZCxcbiAgICAgICAgfTtcbiAgICAgICAgcmV0dXJuIGMuanNvbihwYXlsb2FkKTtcbiAgICAgIH0sXG4gICAgfSksXG4gICAgcmVnaXN0ZXJBcGlSb3V0ZShwYXRoLCB7XG4gICAgICBtZXRob2Q6IFwiUEFUQ0hcIixcbiAgICAgIGhhbmRsZXI6IGFzeW5jIChjOiBDb250ZXh0V2l0aE1hc3RyYSkgPT4ge1xuICAgICAgICBjb25zdCBjdHggPSByZXNvbHZlQWdlbnRSZXF1ZXN0Q29udGV4dChjLCB7IGZpeGVkQWdlbnQsIHRocmVhZElkOiBcInJlcXVpcmVkXCIgfSk7XG4gICAgICAgIGlmIChcImVycm9yXCIgaW4gY3R4KSByZXR1cm4gY3R4LmVycm9yO1xuICAgICAgICBjb25zdCBib2R5ID0gd2lyZS5NYXN0cmFVcGRhdGVUaHJlYWRSZXF1ZXN0U2NoZW1hLnNhZmVQYXJzZShhd2FpdCBjLnJlcS5qc29uKCkpO1xuICAgICAgICBpZiAoIWJvZHkuc3VjY2Vzcykge1xuICAgICAgICAgIGxvZ2dlci53YXJuKFwicmVuYW1lOmludmFsaWRcIiwgeyBlcnJvcjogYm9keS5lcnJvci5tZXNzYWdlIH0pO1xuICAgICAgICAgIHJldHVybiBjLmpzb24oeyBlcnJvcjogSU5WQUxJRF9SRU5BTUVfTUVTU0FHRSwgZmllbGRzOiBpbnZhbGlkRmllbGRzKGJvZHkuZXJyb3IpIH0sIDQwMCk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgdGhyZWFkID0gYXdhaXQgcmVuYW1lVGhyZWFkKHtcbiAgICAgICAgICBhZ2VudDogY3R4LmFnZW50LFxuICAgICAgICAgIHRocmVhZElkOiBjdHgudGhyZWFkSWQsXG4gICAgICAgICAgcmVzb3VyY2VJZDogY3R4LnJlc291cmNlSWQsXG4gICAgICAgICAgdGl0bGU6IGJvZHkuZGF0YS50aXRsZSxcbiAgICAgICAgfSk7XG4gICAgICAgIGlmICghdGhyZWFkKSB7XG4gICAgICAgICAgcmV0dXJuIGMuanNvbih7IGVycm9yOiBgVW5rbm93biB0aHJlYWQgXCIke2N0eC50aHJlYWRJZH1cImAgfSwgNDA0KTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBwYXlsb2FkOiBNYXN0cmFVcGRhdGVUaHJlYWRSZXNwb25zZSA9IHtcbiAgICAgICAgICBvazogdHJ1ZSxcbiAgICAgICAgICBhZ2VudElkOiBjdHguYWdlbnRJZCxcbiAgICAgICAgICB0aHJlYWQsXG4gICAgICAgIH07XG4gICAgICAgIHJldHVybiBjLmpzb24ocGF5bG9hZCk7XG4gICAgICB9LFxuICAgIH0pLFxuICBdO1xufVxuXG4vKiogQ29lcmNlIGEgTWFzdHJhIGBTdG9yYWdlVGhyZWFkVHlwZWAgaW50byB0aGUgSlNPTi1zYWZlIHdpcmUgc2hhcGUuICovXG5mdW5jdGlvbiB0b1dpcmVUaHJlYWQodGhyZWFkOiBTdG9yYWdlVGhyZWFkVHlwZSk6IE1hc3RyYVRocmVhZCB7XG4gIHJldHVybiB7XG4gICAgaWQ6IHRocmVhZC5pZCxcbiAgICAuLi4odGhyZWFkLnRpdGxlID8geyB0aXRsZTogdGhyZWFkLnRpdGxlIH0gOiB7fSksXG4gICAgcmVzb3VyY2VJZDogdGhyZWFkLnJlc291cmNlSWQsXG4gICAgY3JlYXRlZEF0OiB0b0lzbyh0aHJlYWQuY3JlYXRlZEF0KSxcbiAgICB1cGRhdGVkQXQ6IHRvSXNvKHRocmVhZC51cGRhdGVkQXQpLFxuICAgIC4uLih0aHJlYWQubWV0YWRhdGEgPyB7IG1ldGFkYXRhOiB0aHJlYWQubWV0YWRhdGEgfSA6IHt9KSxcbiAgfTtcbn1cblxuLyoqIFJlbmRlciBhIHN0b3JhZ2UgdGltZXN0YW1wIGFzIGFuIElTTy04NjAxIHN0cmluZyBmb3IgdGhlIHdpcmUuICovXG5mdW5jdGlvbiB0b0lzbyh2YWx1ZTogRGF0ZSB8IHN0cmluZyB8IG51bWJlcik6IHN0cmluZyB7XG4gIGlmICh2YWx1ZSBpbnN0YW5jZW9mIERhdGUpIHJldHVybiB2YWx1ZS50b0lTT1N0cmluZygpO1xuICBjb25zdCBwYXJzZWQgPSBuZXcgRGF0ZSh2YWx1ZSk7XG4gIHJldHVybiBOdW1iZXIuaXNOYU4ocGFyc2VkLmdldFRpbWUoKSkgPyBuZXcgRGF0ZSgwKS50b0lTT1N0cmluZygpIDogcGFyc2VkLnRvSVNPU3RyaW5nKCk7XG59XG4iXX0=