@blokjs/trigger-mcp 0.6.12

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,114 @@
1
+ /**
2
+ * McpTrigger — Model Context Protocol trigger. Exposes Blok workflows as
3
+ * MCP **tools** (and **resources**) to external clients (Cursor, Claude
4
+ * Code, …) over two transports multiplexed on the shared Hono port:
5
+ *
6
+ * - **SSE (legacy 2024-11-05)** — `GET <path>/sse` opens the stream and
7
+ * announces `POST <path>/messages?sessionId=…` for JSON-RPC. This is the
8
+ * 2-endpoint shape existing IDE configs expect (drop-in parity).
9
+ * - **Streamable-HTTP** — a single `<path>` endpoint (the current official
10
+ * remote transport), served statelessly via the SDK's web-standard
11
+ * transport directly off `c.req.raw`.
12
+ *
13
+ * **Authoring surface** — a workflow opts in with `trigger.mcp`:
14
+ *
15
+ * ```ts
16
+ * export default workflow({
17
+ * name: "search_code",
18
+ * version: "1.0.0",
19
+ * input: z.object({ query: z.string(), limit: z.number().optional() }),
20
+ * trigger: { mcp: { path: "/mcp", serverName: "tetrix-platform",
21
+ * tool: { description: "Full-text search the indexed code" } } },
22
+ * steps: [ { id: "search", use: "@tetrix/meili-search", inputs: { query: $.req.body.query } } ],
23
+ * });
24
+ * ```
25
+ *
26
+ * Every workflow sharing the same `path` + `serverName` is aggregated into one
27
+ * MCP server. Each tool's `inputSchema` is generated from the workflow's `input`
28
+ * Zod schema (via zod-to-json-schema). On `tools/call` the trigger runs the
29
+ * mapped workflow through the runner — so every call is a Blok Studio run with
30
+ * tracing / retries / idempotency for free.
31
+ *
32
+ * **Hono integration:** identical to `SSETrigger`/`WebhookTrigger` — accepts the
33
+ * shared `Hono` app + an optional `HttpTriggerLike` exposing `addPreCatchAllHook`
34
+ * so routes register AFTER the workflow registry is populated but BEFORE the
35
+ * legacy `/:workflow{.+}` catch-all.
36
+ *
37
+ * **Identity:** an `x-user-context` header (or `?user_context=`) carrying base64
38
+ * `{userId,email}` is parsed per connection and passed to the workflow ctx for
39
+ * credential injection. It is NOT access control — there is no scoping here
40
+ * (that's the app's call). Tokens are never logged (the global sanitizer middleware
41
+ * handles redaction).
42
+ */
43
+ import { DefaultLogger, type GlobalOptions as RunnerGlobalOptions, TriggerBase } from "@blokjs/runner";
44
+ import type { Hono } from "hono";
45
+ /** Parsed `x-user-context` identity (credential injection only — never scoping). */
46
+ export interface McpUserContext {
47
+ userId: string;
48
+ email: string;
49
+ }
50
+ type McpTransportKind = "sse" | "streamable-http";
51
+ interface McpToolMeta {
52
+ name?: string;
53
+ description?: string;
54
+ }
55
+ interface McpResourceMeta {
56
+ uri: string;
57
+ name?: string;
58
+ description?: string;
59
+ mimeType?: string;
60
+ }
61
+ /** Loosely-read `trigger.mcp` config (validated at workflow load by the helper). */
62
+ interface McpTriggerConfig {
63
+ path: string;
64
+ serverName?: string;
65
+ serverVersion?: string;
66
+ transports?: McpTransportKind[];
67
+ tool?: McpToolMeta;
68
+ resource?: McpResourceMeta;
69
+ middleware?: string[];
70
+ }
71
+ interface HttpTriggerLike {
72
+ addPreCatchAllHook(cb: () => void | Promise<void>): void;
73
+ }
74
+ /** Parse a base64-encoded `{userId,email}` identity string. Returns null on any failure. */
75
+ export declare function parseUserContext(value: string | undefined | null): McpUserContext | null;
76
+ export default class McpTrigger extends TriggerBase {
77
+ protected nodeMap: RunnerGlobalOptions;
78
+ protected readonly logger: DefaultLogger;
79
+ protected readonly tracer: import("@opentelemetry/api").Tracer;
80
+ private readonly meter;
81
+ private readonly counterToolCalls;
82
+ private readonly counterSessions;
83
+ private readonly app;
84
+ private readonly httpTrigger;
85
+ private wired;
86
+ /** Live SSE sessions: sessionId → { transport, server, userContext }. */
87
+ private sseSessions;
88
+ constructor(app: Hono<any, any, any>, httpTrigger?: HttpTriggerLike);
89
+ /** Inject the runner's GlobalOptions (nodes + workflows). Called before `listen()`. */
90
+ setNodeMap(nodeMap: RunnerGlobalOptions): void;
91
+ listen(): Promise<number>;
92
+ stop(): Promise<void>;
93
+ getStats(): {
94
+ sessions: number;
95
+ };
96
+ private registerRoutesFromRegistry;
97
+ private getServerGroups;
98
+ private buildServer;
99
+ private dispatchTool;
100
+ private dispatchResource;
101
+ /**
102
+ * Run a workflow through the runner and return `ctx.response.data`. Mirrors
103
+ * the WebhookTrigger request→response dispatch (init → context → middleware →
104
+ * run). MCP tool calls within a session are serial, matching the shared
105
+ * `this.configuration` lifecycle the other triggers use.
106
+ */
107
+ private runWorkflow;
108
+ private registerGroupRoutes;
109
+ private registerSseRoutes;
110
+ private registerStreamableHttpRoute;
111
+ }
112
+ export declare function _setActiveMcpTrigger(trigger: McpTrigger | null): void;
113
+ export declare function _getActiveMcpTrigger(): McpTrigger | null;
114
+ export type { McpTriggerConfig };
@@ -0,0 +1,423 @@
1
+ /**
2
+ * McpTrigger — Model Context Protocol trigger. Exposes Blok workflows as
3
+ * MCP **tools** (and **resources**) to external clients (Cursor, Claude
4
+ * Code, …) over two transports multiplexed on the shared Hono port:
5
+ *
6
+ * - **SSE (legacy 2024-11-05)** — `GET <path>/sse` opens the stream and
7
+ * announces `POST <path>/messages?sessionId=…` for JSON-RPC. This is the
8
+ * 2-endpoint shape existing IDE configs expect (drop-in parity).
9
+ * - **Streamable-HTTP** — a single `<path>` endpoint (the current official
10
+ * remote transport), served statelessly via the SDK's web-standard
11
+ * transport directly off `c.req.raw`.
12
+ *
13
+ * **Authoring surface** — a workflow opts in with `trigger.mcp`:
14
+ *
15
+ * ```ts
16
+ * export default workflow({
17
+ * name: "search_code",
18
+ * version: "1.0.0",
19
+ * input: z.object({ query: z.string(), limit: z.number().optional() }),
20
+ * trigger: { mcp: { path: "/mcp", serverName: "tetrix-platform",
21
+ * tool: { description: "Full-text search the indexed code" } } },
22
+ * steps: [ { id: "search", use: "@tetrix/meili-search", inputs: { query: $.req.body.query } } ],
23
+ * });
24
+ * ```
25
+ *
26
+ * Every workflow sharing the same `path` + `serverName` is aggregated into one
27
+ * MCP server. Each tool's `inputSchema` is generated from the workflow's `input`
28
+ * Zod schema (via zod-to-json-schema). On `tools/call` the trigger runs the
29
+ * mapped workflow through the runner — so every call is a Blok Studio run with
30
+ * tracing / retries / idempotency for free.
31
+ *
32
+ * **Hono integration:** identical to `SSETrigger`/`WebhookTrigger` — accepts the
33
+ * shared `Hono` app + an optional `HttpTriggerLike` exposing `addPreCatchAllHook`
34
+ * so routes register AFTER the workflow registry is populated but BEFORE the
35
+ * legacy `/:workflow{.+}` catch-all.
36
+ *
37
+ * **Identity:** an `x-user-context` header (or `?user_context=`) carrying base64
38
+ * `{userId,email}` is parsed per connection and passed to the workflow ctx for
39
+ * credential injection. It is NOT access control — there is no scoping here
40
+ * (that's the app's call). Tokens are never logged (the global sanitizer middleware
41
+ * handles redaction).
42
+ */
43
+ import { DefaultLogger, TriggerBase, WorkflowRegistry, } from "@blokjs/runner";
44
+ import { RESPONSE_ALREADY_SENT } from "@hono/node-server/utils/response";
45
+ import { Server as McpSdkServer } from "@modelcontextprotocol/sdk/server/index.js";
46
+ import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
47
+ import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
48
+ import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
49
+ import { SpanStatusCode, metrics, trace } from "@opentelemetry/api";
50
+ import { v4 as uuid } from "uuid";
51
+ import { zodToJsonSchema } from "zod-to-json-schema";
52
+ const DEFAULT_SERVER_NAME = "blok-mcp";
53
+ const DEFAULT_SERVER_VERSION = "1.0.0";
54
+ const DEFAULT_TRANSPORTS = ["sse", "streamable-http"];
55
+ // -----------------------------------------------------------------------------
56
+ // Helpers
57
+ // -----------------------------------------------------------------------------
58
+ /** Parse a base64-encoded `{userId,email}` identity string. Returns null on any failure. */
59
+ export function parseUserContext(value) {
60
+ if (!value || typeof value !== "string")
61
+ return null;
62
+ try {
63
+ const decoded = Buffer.from(value, "base64").toString("utf-8");
64
+ if (!decoded || decoded.trim() === "null")
65
+ return null;
66
+ const ctx = JSON.parse(decoded);
67
+ if (!ctx || typeof ctx.userId !== "string")
68
+ return null;
69
+ return { userId: ctx.userId, email: typeof ctx.email === "string" ? ctx.email : "unknown" };
70
+ }
71
+ catch {
72
+ return null;
73
+ }
74
+ }
75
+ /** Convert a Zod schema to a JSON-Schema object suitable for an MCP tool `inputSchema`. */
76
+ // biome-ignore lint/suspicious/noExplicitAny: zod schema is opaque here
77
+ function toInputJsonSchema(inputZod) {
78
+ const empty = { type: "object", properties: {}, additionalProperties: true };
79
+ if (!inputZod || typeof inputZod !== "object" || typeof inputZod.safeParse !== "function") {
80
+ return empty;
81
+ }
82
+ try {
83
+ const json = zodToJsonSchema(inputZod, { target: "jsonSchema7", $refStrategy: "none" });
84
+ // biome-ignore lint/performance/noDelete: strip JSON-Schema meta the MCP client doesn't need
85
+ delete json.$schema;
86
+ if (json.type !== "object") {
87
+ return { ...empty, _wrapped: json };
88
+ }
89
+ return json;
90
+ }
91
+ catch {
92
+ return empty;
93
+ }
94
+ }
95
+ // -----------------------------------------------------------------------------
96
+ // Trigger
97
+ // -----------------------------------------------------------------------------
98
+ export default class McpTrigger extends TriggerBase {
99
+ nodeMap = {};
100
+ logger = new DefaultLogger();
101
+ tracer = trace.getTracer(process.env.PROJECT_NAME || "trigger-mcp-workflow", process.env.PROJECT_VERSION || "0.0.1");
102
+ meter = metrics.getMeter("blok");
103
+ counterToolCalls = this.meter.createCounter("blok_mcp_tool_calls_total", {
104
+ description: "MCP tools/call dispatches.",
105
+ unit: "1",
106
+ });
107
+ counterSessions = this.meter.createCounter("blok_mcp_sse_sessions_total", {
108
+ description: "MCP SSE sessions opened.",
109
+ unit: "1",
110
+ });
111
+ // biome-ignore lint/suspicious/noExplicitAny: Hono generic propagation (matches SSE/WS triggers)
112
+ app;
113
+ httpTrigger;
114
+ wired = false;
115
+ /** Live SSE sessions: sessionId → { transport, server, userContext }. */
116
+ sseSessions = new Map();
117
+ // biome-ignore lint/suspicious/noExplicitAny: matches `app` field generic
118
+ constructor(app, httpTrigger) {
119
+ super();
120
+ this.app = app;
121
+ this.httpTrigger = httpTrigger ?? null;
122
+ _setActiveMcpTrigger(this);
123
+ }
124
+ /** Inject the runner's GlobalOptions (nodes + workflows). Called before `listen()`. */
125
+ setNodeMap(nodeMap) {
126
+ this.nodeMap = nodeMap;
127
+ }
128
+ async listen() {
129
+ const startTime = this.startCounter();
130
+ if (this.wired) {
131
+ this.logger.log("[blok][mcp] listen() called twice; ignoring");
132
+ return this.endCounter(startTime);
133
+ }
134
+ this.wired = true;
135
+ if (this.httpTrigger) {
136
+ this.httpTrigger.addPreCatchAllHook(() => this.registerRoutesFromRegistry());
137
+ }
138
+ else {
139
+ this.registerRoutesFromRegistry();
140
+ }
141
+ return this.endCounter(startTime);
142
+ }
143
+ async stop() {
144
+ for (const { transport } of this.sseSessions.values()) {
145
+ try {
146
+ await transport.close();
147
+ }
148
+ catch {
149
+ /* ignore */
150
+ }
151
+ }
152
+ this.sseSessions.clear();
153
+ this.wired = false;
154
+ if (_getActiveMcpTrigger() === this)
155
+ _setActiveMcpTrigger(null);
156
+ this.destroyMonitoring();
157
+ this.logger.log("[blok][mcp] stopped");
158
+ }
159
+ getStats() {
160
+ return { sessions: this.sseSessions.size };
161
+ }
162
+ // ---------------------------------------------------------------------------
163
+ // Registry scan + grouping
164
+ // ---------------------------------------------------------------------------
165
+ registerRoutesFromRegistry() {
166
+ const groups = this.getServerGroups();
167
+ if (groups.length === 0) {
168
+ this.logger.log("[blok][mcp] no workflows with trigger.mcp found");
169
+ return;
170
+ }
171
+ for (const group of groups) {
172
+ this.registerGroupRoutes(group);
173
+ }
174
+ }
175
+ getServerGroups() {
176
+ const registry = WorkflowRegistry.getInstance();
177
+ const byPath = new Map();
178
+ for (const entry of registry.list()) {
179
+ // Workflows registered as builders expose config on `_config`; plain
180
+ // objects (tests / JSON) expose it at the top level.
181
+ const wf = entry.workflow?._config ?? entry.workflow;
182
+ const cfg = wf?.trigger?.mcp;
183
+ if (!cfg || typeof cfg.path !== "string")
184
+ continue;
185
+ const path = cfg.path;
186
+ let group = byPath.get(path);
187
+ if (!group) {
188
+ group = {
189
+ path,
190
+ serverName: cfg.serverName || DEFAULT_SERVER_NAME,
191
+ serverVersion: cfg.serverVersion || DEFAULT_SERVER_VERSION,
192
+ transports: Array.isArray(cfg.transports) && cfg.transports.length > 0 ? cfg.transports : DEFAULT_TRANSPORTS,
193
+ tools: [],
194
+ resources: [],
195
+ };
196
+ byPath.set(path, group);
197
+ }
198
+ if (cfg.resource && typeof cfg.resource.uri === "string") {
199
+ group.resources.push({
200
+ workflowName: entry.name,
201
+ uri: cfg.resource.uri,
202
+ name: cfg.resource.name || entry.name,
203
+ description: cfg.resource.description,
204
+ mimeType: cfg.resource.mimeType || "application/json",
205
+ });
206
+ }
207
+ else {
208
+ const inputZod = wf?.input;
209
+ group.tools.push({
210
+ workflowName: entry.name,
211
+ toolName: cfg.tool?.name || entry.name,
212
+ description: cfg.tool?.description || `Run the "${entry.name}" workflow.`,
213
+ inputZod,
214
+ });
215
+ }
216
+ }
217
+ return [...byPath.values()];
218
+ }
219
+ // ---------------------------------------------------------------------------
220
+ // MCP server factory (one per request/session, bound to a user context)
221
+ // ---------------------------------------------------------------------------
222
+ buildServer(group, getUserContext) {
223
+ const server = new McpSdkServer({ name: group.serverName, version: group.serverVersion }, { capabilities: { tools: {}, resources: {} } });
224
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
225
+ tools: group.tools.map((t) => ({
226
+ name: t.toolName,
227
+ description: t.description,
228
+ inputSchema: toInputJsonSchema(t.inputZod),
229
+ })),
230
+ }));
231
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
232
+ const toolName = req.params.name;
233
+ const tool = group.tools.find((t) => t.toolName === toolName);
234
+ if (!tool) {
235
+ return { content: [{ type: "text", text: `Unknown tool: ${toolName}` }], isError: true };
236
+ }
237
+ const args = (req.params.arguments ?? {});
238
+ return this.dispatchTool(tool, args, getUserContext());
239
+ });
240
+ if (group.resources.length > 0) {
241
+ server.setRequestHandler(ListResourcesRequestSchema, async () => ({
242
+ resources: group.resources.map((r) => ({
243
+ uri: r.uri,
244
+ name: r.name,
245
+ description: r.description,
246
+ mimeType: r.mimeType,
247
+ })),
248
+ }));
249
+ server.setRequestHandler(ReadResourceRequestSchema, async (req) => {
250
+ const uri = req.params.uri;
251
+ const resource = group.resources.find((r) => r.uri === uri);
252
+ if (!resource)
253
+ throw new Error(`Unknown resource: ${uri}`);
254
+ const result = await this.dispatchResource(resource, getUserContext());
255
+ const text = typeof result === "string" ? result : JSON.stringify(result, null, 2);
256
+ return { contents: [{ uri: resource.uri, mimeType: resource.mimeType, text }] };
257
+ });
258
+ }
259
+ return server;
260
+ }
261
+ // ---------------------------------------------------------------------------
262
+ // Workflow dispatch (tools/call + resources/read run through the runner)
263
+ // ---------------------------------------------------------------------------
264
+ async dispatchTool(tool, args, userContext) {
265
+ this.counterToolCalls.add(1, { tool: tool.toolName });
266
+ try {
267
+ const data = await this.runWorkflow(tool.workflowName, args, userContext, `mcp.tool:${tool.toolName}`);
268
+ const text = typeof data === "string" ? data : JSON.stringify(data, null, 2);
269
+ return { content: [{ type: "text", text }] };
270
+ }
271
+ catch (err) {
272
+ const msg = err instanceof Error ? err.message : String(err);
273
+ this.logger.error(`[blok][mcp] tool "${tool.toolName}" failed: ${msg}`);
274
+ // Tool failure → MCP tool error result, NOT a transport crash.
275
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
276
+ }
277
+ }
278
+ async dispatchResource(resource, userContext) {
279
+ return this.runWorkflow(resource.workflowName, {}, userContext, `mcp.resource:${resource.uri}`);
280
+ }
281
+ /**
282
+ * Run a workflow through the runner and return `ctx.response.data`. Mirrors
283
+ * the WebhookTrigger request→response dispatch (init → context → middleware →
284
+ * run). MCP tool calls within a session are serial, matching the shared
285
+ * `this.configuration` lifecycle the other triggers use.
286
+ */
287
+ async runWorkflow(workflowName, args, userContext, spanLabel) {
288
+ const requestId = uuid();
289
+ return this.tracer.startActiveSpan(`mcp:${workflowName}`, async (span) => {
290
+ try {
291
+ const registry = WorkflowRegistry.getInstance();
292
+ const entry = registry.get(workflowName);
293
+ if (!entry)
294
+ throw new Error(`workflow "${workflowName}" not found in registry`);
295
+ await this.configuration.init(workflowName, this.nodeMap, entry.workflow);
296
+ const headers = {};
297
+ if (userContext) {
298
+ // Carry identity to the workflow exactly as HTTP would (base64 header).
299
+ headers["x-user-context"] = Buffer.from(JSON.stringify(userContext), "utf-8").toString("base64");
300
+ }
301
+ const ctx = this.createContext(undefined, workflowName, requestId);
302
+ ctx.request = {
303
+ body: args,
304
+ headers,
305
+ params: {},
306
+ query: {},
307
+ };
308
+ ctx._mcp = { userContext };
309
+ await this.applyMiddlewareChain(ctx, this.nodeMap);
310
+ await this.run(ctx);
311
+ span.setAttribute("workflow_name", workflowName);
312
+ span.setAttribute("mcp_label", spanLabel);
313
+ span.setStatus({ code: SpanStatusCode.OK });
314
+ return ctx.response?.data;
315
+ }
316
+ catch (err) {
317
+ const msg = err instanceof Error ? err.message : String(err);
318
+ span.recordException(err);
319
+ span.setStatus({ code: SpanStatusCode.ERROR, message: msg });
320
+ throw err;
321
+ }
322
+ finally {
323
+ span.end();
324
+ }
325
+ });
326
+ }
327
+ // ---------------------------------------------------------------------------
328
+ // Route registration (SSE + Streamable-HTTP)
329
+ // ---------------------------------------------------------------------------
330
+ registerGroupRoutes(group) {
331
+ this.logger.log(`[blok][mcp] server "${group.serverName}" at ${group.path} — ${group.tools.length} tool(s), ${group.resources.length} resource(s), transports=[${group.transports.join(",")}]`);
332
+ if (group.transports.includes("sse")) {
333
+ this.registerSseRoutes(group);
334
+ }
335
+ if (group.transports.includes("streamable-http")) {
336
+ this.registerStreamableHttpRoute(group);
337
+ }
338
+ }
339
+ registerSseRoutes(group) {
340
+ const ssePath = `${group.path}/sse`;
341
+ const messagesPath = `${group.path}/messages`;
342
+ this.logger.log(`[blok][mcp] GET ${ssePath} POST ${messagesPath} (sse)`);
343
+ // GET <path>/sse — open the SSE stream; SDK announces the messages endpoint.
344
+ this.app.get(ssePath, async (c) => {
345
+ const env = c.env;
346
+ if (!env?.outgoing) {
347
+ return c.text("MCP SSE transport requires the Node server (@hono/node-server).", 500);
348
+ }
349
+ const rawUserCtx = c.req.header("x-user-context") || (new URL(c.req.url).searchParams.get("user_context") ?? undefined);
350
+ const userContext = parseUserContext(rawUserCtx);
351
+ const transport = new SSEServerTransport(messagesPath, env.outgoing);
352
+ const sessionId = transport.sessionId;
353
+ const server = this.buildServer(group, () => this.sseSessions.get(sessionId)?.userContext ?? null);
354
+ this.sseSessions.set(sessionId, { transport, server, userContext });
355
+ this.counterSessions.add(1, { server: group.serverName });
356
+ transport.onclose = () => {
357
+ this.sseSessions.delete(sessionId);
358
+ };
359
+ try {
360
+ await server.connect(transport);
361
+ }
362
+ catch (err) {
363
+ this.sseSessions.delete(sessionId);
364
+ const msg = err instanceof Error ? err.message : String(err);
365
+ this.logger.error(`[blok][mcp] sse connect failed: ${msg}`);
366
+ }
367
+ return RESPONSE_ALREADY_SENT;
368
+ });
369
+ // POST <path>/messages?sessionId=… — JSON-RPC messages for an open stream.
370
+ this.app.post(messagesPath, async (c) => {
371
+ const env = c.env;
372
+ const sessionId = new URL(c.req.url).searchParams.get("sessionId") ?? undefined;
373
+ if (!sessionId)
374
+ return c.text("Missing sessionId query parameter", 400);
375
+ const session = this.sseSessions.get(sessionId);
376
+ if (!session)
377
+ return c.text("Session not found", 404);
378
+ const body = await c.req.json().catch(() => undefined);
379
+ try {
380
+ await session.transport.handlePostMessage(env.incoming, env.outgoing, body);
381
+ }
382
+ catch (err) {
383
+ const msg = err instanceof Error ? err.message : String(err);
384
+ this.logger.error(`[blok][mcp] handlePostMessage failed: ${msg}`);
385
+ if (!env.outgoing.headersSent)
386
+ return c.text("Error handling MCP message", 500);
387
+ }
388
+ return RESPONSE_ALREADY_SENT;
389
+ });
390
+ }
391
+ registerStreamableHttpRoute(group) {
392
+ this.logger.log(`[blok][mcp] ALL ${group.path} (streamable-http)`);
393
+ // Stateless Streamable-HTTP: a fresh server + transport per request, served
394
+ // directly off the Fetch `Request` (works on any Hono runtime).
395
+ this.app.all(group.path, async (c) => {
396
+ const rawUserCtx = c.req.header("x-user-context") || (new URL(c.req.url).searchParams.get("user_context") ?? undefined);
397
+ const userContext = parseUserContext(rawUserCtx);
398
+ const server = this.buildServer(group, () => userContext);
399
+ const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: undefined });
400
+ try {
401
+ await server.connect(transport);
402
+ const res = await transport.handleRequest(c.req.raw);
403
+ return res;
404
+ }
405
+ catch (err) {
406
+ const msg = err instanceof Error ? err.message : String(err);
407
+ this.logger.error(`[blok][mcp] streamable-http request failed: ${msg}`);
408
+ return c.json({ jsonrpc: "2.0", error: { code: -32603, message: msg }, id: null }, 500);
409
+ }
410
+ });
411
+ }
412
+ }
413
+ // -----------------------------------------------------------------------------
414
+ // Singleton accessor (parity with SSE/Webhook triggers)
415
+ // -----------------------------------------------------------------------------
416
+ let activeTrigger = null;
417
+ export function _setActiveMcpTrigger(trigger) {
418
+ activeTrigger = trigger;
419
+ }
420
+ export function _getActiveMcpTrigger() {
421
+ return activeTrigger;
422
+ }
423
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWNwVHJpZ2dlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9NY3BUcmlnZ2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXlDRztBQUdILE9BQU8sRUFDTixhQUFhLEVBRWIsV0FBVyxFQUNYLGdCQUFnQixHQUNoQixNQUFNLGdCQUFnQixDQUFDO0FBRXhCLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBQ3pFLE9BQU8sRUFBRSxNQUFNLElBQUksWUFBWSxFQUFFLE1BQU0sMkNBQTJDLENBQUM7QUFDbkYsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0seUNBQXlDLENBQUM7QUFDN0UsT0FBTyxFQUFFLHdDQUF3QyxFQUFFLE1BQU0sK0RBQStELENBQUM7QUFDekgsT0FBTyxFQUNOLHFCQUFxQixFQUNyQiwwQkFBMEIsRUFDMUIsc0JBQXNCLEVBQ3RCLHlCQUF5QixHQUN6QixNQUFNLG9DQUFvQyxDQUFDO0FBQzVDLE9BQU8sRUFBYSxjQUFjLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBRS9FLE9BQU8sRUFBRSxFQUFFLElBQUksSUFBSSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ2xDLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQW9FckQsTUFBTSxtQkFBbUIsR0FBRyxVQUFVLENBQUM7QUFDdkMsTUFBTSxzQkFBc0IsR0FBRyxPQUFPLENBQUM7QUFDdkMsTUFBTSxrQkFBa0IsR0FBdUIsQ0FBQyxLQUFLLEVBQUUsaUJBQWlCLENBQUMsQ0FBQztBQUUxRSxnRkFBZ0Y7QUFDaEYsVUFBVTtBQUNWLGdGQUFnRjtBQUVoRiw0RkFBNEY7QUFDNUYsTUFBTSxVQUFVLGdCQUFnQixDQUFDLEtBQWdDO0lBQ2hFLElBQUksQ0FBQyxLQUFLLElBQUksT0FBTyxLQUFLLEtB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TUFBTSxVQUFVLG9CQUFvQjtJQUNuQyxPQUFPLGFBQWEsQ0FBQztBQUN0QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBNY3BUcmlnZ2VyIOKAlCBNb2RlbCBDb250ZXh0IFByb3RvY29sIHRyaWdnZXIuIEV4cG9zZXMgQmxvayB3b3JrZmxvd3MgYXNcbiAqIE1DUCAqKnRvb2xzKiogKGFuZCAqKnJlc291cmNlcyoqKSB0byBleHRlcm5hbCBjbGllbnRzIChDdXJzb3IsIENsYXVkZVxuICogQ29kZSwg4oCmKSBvdmVyIHR3byB0cmFuc3BvcnRzIG11bHRpcGxleGVkIG9uIHRoZSBzaGFyZWQgSG9ubyBwb3J0OlxuICpcbiAqICAgLSAqKlNTRSAobGVnYWN5IDIwMjQtMTEtMDUpKiog4oCUIGBHRVQgPHBhdGg+L3NzZWAgb3BlbnMgdGhlIHN0cmVhbSBhbmRcbiAqICAgICBhbm5vdW5jZXMgYFBPU1QgPHBhdGg+L21lc3NhZ2VzP3Nlc3Npb25JZD3igKZgIGZvciBKU09OLVJQQy4gVGhpcyBpcyB0aGVcbiAqICAgICAyLWVuZHBvaW50IHNoYXBlIGV4aXN0aW5nIElERSBjb25maWdzIGV4cGVjdCAoZHJvcC1pbiBwYXJpdHkpLlxuICogICAtICoqU3RyZWFtYWJsZS1IVFRQKiog4oCUIGEgc2luZ2xlIGA8cGF0aD5gIGVuZHBvaW50ICh0aGUgY3VycmVudCBvZmZpY2lhbFxuICogICAgIHJlbW90ZSB0cmFuc3BvcnQpLCBzZXJ2ZWQgc3RhdGVsZXNzbHkgdmlhIHRoZSBTREsncyB3ZWItc3RhbmRhcmRcbiAqICAgICB0cmFuc3BvcnQgZGlyZWN0bHkgb2ZmIGBjLnJlcS5yYXdgLlxuICpcbiAqICoqQXV0aG9yaW5nIHN1cmZhY2UqKiDigJQgYSB3b3JrZmxvdyBvcHRzIGluIHdpdGggYHRyaWdnZXIubWNwYDpcbiAqXG4gKiBgYGB0c1xuICogZXhwb3J0IGRlZmF1bHQgd29ya2Zsb3coe1xuICogICBuYW1lOiBcInNlYXJjaF9jb2RlXCIsXG4gKiAgIHZlcnNpb246IFwiMS4wLjBcIixcbiAqICAgaW5wdXQ6IHoub2JqZWN0KHsgcXVlcnk6IHouc3RyaW5nKCksIGxpbWl0OiB6Lm51bWJlcigpLm9wdGlvbmFsKCkgfSksXG4gKiAgIHRyaWdnZXI6IHsgbWNwOiB7IHBhdGg6IFwiL21jcFwiLCBzZXJ2ZXJOYW1lOiBcInRldHJpeC1wbGF0Zm9ybVwiLFxuICogICAgICAgICAgICAgICAgICAgICB0b29sOiB7IGRlc2NyaXB0aW9uOiBcIkZ1bGwtdGV4dCBzZWFyY2ggdGhlIGluZGV4ZWQgY29kZVwiIH0gfSB9LFxuICogICBzdGVwczogWyB7IGlkOiBcInNlYXJjaFwiLCB1c2U6IFwiQHRldHJpeC9tZWlsaS1zZWFyY2hcIiwgaW5wdXRzOiB7IHF1ZXJ5OiAkLnJlcS5ib2R5LnF1ZXJ5IH0gfSBdLFxuICogfSk7XG4gKiBgYGBcbiAqXG4gKiBFdmVyeSB3b3JrZmxvdyBzaGFyaW5nIHRoZSBzYW1lIGBwYXRoYCArIGBzZXJ2ZXJOYW1lYCBpcyBhZ2dyZWdhdGVkIGludG8gb25lXG4gKiBNQ1Agc2VydmVyLiBFYWNoIHRvb2wncyBgaW5wdXRTY2hlbWFgIGlzIGdlbmVyYXRlZCBmcm9tIHRoZSB3b3JrZmxvdydzIGBpbnB1dGBcbiAqIFpvZCBzY2hlbWEgKHZpYSB6b2QtdG8tanNvbi1zY2hlbWEpLiBPbiBgdG9vbHMvY2FsbGAgdGhlIHRyaWdnZXIgcnVucyB0aGVcbiAqIG1hcHBlZCB3b3JrZmxvdyB0aHJvdWdoIHRoZSBydW5uZXIg4oCUIHNvIGV2ZXJ5IGNhbGwgaXMgYSBCbG9rIFN0dWRpbyBydW4gd2l0aFxuICogdHJhY2luZyAvIHJldHJpZXMgLyBpZGVtcG90ZW5jeSBmb3IgZnJlZS5cbiAqXG4gKiAqKkhvbm8gaW50ZWdyYXRpb246KiogaWRlbnRpY2FsIHRvIGBTU0VUcmlnZ2VyYC9gV2ViaG9va1RyaWdnZXJgIOKAlCBhY2NlcHRzIHRoZVxuICogc2hhcmVkIGBIb25vYCBhcHAgKyBhbiBvcHRpb25hbCBgSHR0cFRyaWdnZXJMaWtlYCBleHBvc2luZyBgYWRkUHJlQ2F0Y2hBbGxIb29rYFxuICogc28gcm91dGVzIHJlZ2lzdGVyIEFGVEVSIHRoZSB3b3JrZmxvdyByZWdpc3RyeSBpcyBwb3B1bGF0ZWQgYnV0IEJFRk9SRSB0aGVcbiAqIGxlZ2FjeSBgLzp3b3JrZmxvd3suK31gIGNhdGNoLWFsbC5cbiAqXG4gKiAqKklkZW50aXR5OioqIGFuIGB4LXVzZXItY29udGV4dGAgaGVhZGVyIChvciBgP3VzZXJfY29udGV4dD1gKSBjYXJyeWluZyBiYXNlNjRcbiAqIGB7dXNlcklkLGVtYWlsfWAgaXMgcGFyc2VkIHBlciBjb25uZWN0aW9uIGFuZCBwYXNzZWQgdG8gdGhlIHdvcmtmbG93IGN0eCBmb3JcbiAqIGNyZWRlbnRpYWwgaW5qZWN0aW9uLiBJdCBpcyBOT1QgYWNjZXNzIGNvbnRyb2wg4oCUIHRoZXJlIGlzIG5vIHNjb3BpbmcgaGVyZVxuICogKHRoYXQncyB0aGUgYXBwJ3MgY2FsbCkuIFRva2VucyBhcmUgbmV2ZXIgbG9nZ2VkICh0aGUgZ2xvYmFsIHNhbml0aXplciBtaWRkbGV3YXJlXG4gKiBoYW5kbGVzIHJlZGFjdGlvbikuXG4gKi9cblxuaW1wb3J0IHR5cGUgeyBJbmNvbWluZ01lc3NhZ2UsIFNlcnZlclJlc3BvbnNlIH0gZnJvbSBcIm5vZGU6aHR0cFwiO1xuaW1wb3J0IHtcblx0RGVmYXVsdExvZ2dlcixcblx0dHlwZSBHbG9iYWxPcHRpb25zIGFzIFJ1bm5lckdsb2JhbE9wdGlvbnMsXG5cdFRyaWdnZXJCYXNlLFxuXHRXb3JrZmxvd1JlZ2lzdHJ5LFxufSBmcm9tIFwiQGJsb2tqcy9ydW5uZXJcIjtcbmltcG9ydCB0eXBlIHsgQ29udGV4dCwgUmVxdWVzdENvbnRleHQgfSBmcm9tIFwiQGJsb2tqcy9zaGFyZWRcIjtcbmltcG9ydCB7IFJFU1BPTlNFX0FMUkVBRFlfU0VOVCB9IGZyb20gXCJAaG9uby9ub2RlLXNlcnZlci91dGlscy9yZXNwb25zZVwiO1xuaW1wb3J0IHsgU2VydmVyIGFzIE1jcFNka1NlcnZlciB9IGZyb20gXCJAbW9kZWxjb250ZXh0cHJvdG9jb2wvc2RrL3NlcnZlci9pbmRleC5qc1wiO1xuaW1wb3J0IHsgU1NFU2VydmVyVHJhbnNwb3J0IH0gZnJvbSBcIkBtb2RlbGNvbnRleHRwcm90b2NvbC9zZGsvc2VydmVyL3NzZS5qc1wiO1xuaW1wb3J0IHsgV2ViU3RhbmRhcmRTdHJlYW1hYmxlSFRUUFNlcnZlclRyYW5zcG9ydCB9IGZyb20gXCJAbW9kZWxjb250ZXh0cHJvdG9jb2wvc2RrL3NlcnZlci93ZWJTdGFuZGFyZFN0cmVhbWFibGVIdHRwLmpzXCI7XG5pbXBvcnQge1xuXHRDYWxsVG9vbFJlcXVlc3RTY2hlbWEsXG5cdExpc3RSZXNvdXJjZXNSZXF1ZXN0U2NoZW1hLFxuXHRMaXN0VG9vbHNSZXF1ZXN0U2NoZW1hLFxuXHRSZWFkUmVzb3VyY2VSZXF1ZXN0U2NoZW1hLFxufSBmcm9tIFwiQG1vZGVsY29udGV4dHByb3RvY29sL3Nkay90eXBlcy5qc1wiO1xuaW1wb3J0IHsgdHlwZSBTcGFuLCBTcGFuU3RhdHVzQ29kZSwgbWV0cmljcywgdHJhY2UgfSBmcm9tIFwiQG9wZW50ZWxlbWV0cnkvYXBpXCI7XG5pbXBvcnQgdHlwZSB7IEhvbm8sIENvbnRleHQgYXMgSG9ub0NvbnRleHQgfSBmcm9tIFwiaG9ub1wiO1xuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gXCJ1dWlkXCI7XG5pbXBvcnQgeyB6b2RUb0pzb25TY2hlbWEgfSBmcm9tIFwiem9kLXRvLWpzb24tc2NoZW1hXCI7XG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4vLyBUeXBlc1xuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLyoqIFBhcnNlZCBgeC11c2VyLWNvbnRleHRgIGlkZW50aXR5IChjcmVkZW50aWFsIGluamVjdGlvbiBvbmx5IOKAlCBuZXZlciBzY29waW5nKS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgTWNwVXNlckNvbnRleHQge1xuXHR1c2VySWQ6IHN0cmluZztcblx0ZW1haWw6IHN0cmluZztcbn1cblxudHlwZSBNY3BUcmFuc3BvcnRLaW5kID0gXCJzc2VcIiB8IFwic3RyZWFtYWJsZS1odHRwXCI7XG5cbmludGVyZmFjZSBNY3BUb29sTWV0YSB7XG5cdG5hbWU/OiBzdHJpbmc7XG5cdGRlc2NyaXB0aW9uPzogc3RyaW5nO1xufVxuaW50ZXJmYWNlIE1jcFJlc291cmNlTWV0YSB7XG5cdHVyaTogc3RyaW5nO1xuXHRuYW1lPzogc3RyaW5nO1xuXHRkZXNjcmlwdGlvbj86IHN0cmluZztcblx0bWltZVR5cGU/OiBzdHJpbmc7XG59XG5cbi8qKiBMb29zZWx5LXJlYWQgYHRyaWdnZXIubWNwYCBjb25maWcgKHZhbGlkYXRlZCBhdCB3b3JrZmxvdyBsb2FkIGJ5IHRoZSBoZWxwZXIpLiAqL1xuaW50ZXJmYWNlIE1jcFRyaWdnZXJDb25maWcge1xuXHRwYXRoOiBzdHJpbmc7XG5cdHNlcnZlck5hbWU/OiBzdHJpbmc7XG5cdHNlcnZlclZlcnNpb24/OiBzdHJpbmc7XG5cdHRyYW5zcG9ydHM/OiBNY3BUcmFuc3BvcnRLaW5kW107XG5cdHRvb2w/OiBNY3BUb29sTWV0YTtcblx0cmVzb3VyY2U/OiBNY3BSZXNvdXJjZU1ldGE7XG5cdG1pZGRsZXdhcmU/OiBzdHJpbmdbXTtcbn1cblxuaW50ZXJmYWNlIEh0dHBUcmlnZ2VyTGlrZSB7XG5cdGFkZFByZUNhdGNoQWxsSG9vayhjYjogKCkgPT4gdm9pZCB8IFByb21pc2U8dm9pZD4pOiB2b2lkO1xufVxuXG4vKiogQSB3b3JrZmxvdyBleHBvc2VkIGFzIGFuIE1DUCB0b29sLiAqL1xuaW50ZXJmYWNlIFRvb2xFbnRyeSB7XG5cdHdvcmtmbG93TmFtZTogc3RyaW5nO1xuXHR0b29sTmFtZTogc3RyaW5nO1xuXHRkZXNjcmlwdGlvbjogc3RyaW5nO1xuXHQvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vRXhwbGljaXRBbnk6IHdvcmtmbG93IGBpbnB1dGAgaXMgYW4gb3BhcXVlIFpvZFR5cGVcblx0aW5wdXRab2Q6IGFueSB8IHVuZGVmaW5lZDtcbn1cblxuLyoqIEEgd29ya2Zsb3cgZXhwb3NlZCBhcyBhbiBNQ1AgcmVzb3VyY2UuICovXG5pbnRlcmZhY2UgUmVzb3VyY2VFbnRyeSB7XG5cdHdvcmtmbG93TmFtZTogc3RyaW5nO1xuXHR1cmk6IHN0cmluZztcblx0bmFtZTogc3RyaW5nO1xuXHRkZXNjcmlwdGlvbj86IHN0cmluZztcblx0bWltZVR5cGU6IHN0cmluZztcbn1cblxuLyoqIEFsbCB3b3JrZmxvd3MgdGhhdCBzaGFyZSBhIChwYXRoLCBzZXJ2ZXJOYW1lKSBmb3JtIG9uZSBNQ1Agc2VydmVyLiAqL1xuaW50ZXJmYWNlIFNlcnZlckdyb3VwIHtcblx0cGF0aDogc3RyaW5nO1xuXHRzZXJ2ZXJOYW1lOiBzdHJpbmc7XG5cdHNlcnZlclZlcnNpb246IHN0cmluZztcblx0dHJhbnNwb3J0czogTWNwVHJhbnNwb3J0S2luZFtdO1xuXHR0b29sczogVG9vbEVudHJ5W107XG5cdHJlc291cmNlczogUmVzb3VyY2VFbnRyeVtdO1xufVxuXG5jb25zdCBERUZBVUxUX1NFUlZFUl9OQU1FID0gXCJibG9rLW1jcFwiO1xuY29uc3QgREVGQVVMVF9TRVJWRVJfVkVSU0lPTiA9IFwiMS4wLjBcIjtcbmNvbnN0IERFRkFVTFRfVFJBTlNQT1JUUzogTWNwVHJhbnNwb3J0S2luZFtdID0gW1wic3NlXCIsIFwic3RyZWFtYWJsZS1odHRwXCJdO1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuLy8gSGVscGVyc1xuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLyoqIFBhcnNlIGEgYmFzZTY0LWVuY29kZWQgYHt1c2VySWQsZW1haWx9YCBpZGVudGl0eSBzdHJpbmcuIFJldHVybnMgbnVsbCBvbiBhbnkgZmFpbHVyZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBwYXJzZVVzZXJDb250ZXh0KHZhbHVlOiBzdHJpbmcgfCB1bmRlZmluZWQgfCBudWxsKTogTWNwVXNlckNvbnRleHQgfCBudWxsIHtcblx0aWYgKCF2YWx1ZSB8fCB0eXBlb2YgdmFsdWUgIT09IFwic3RyaW5nXCIpIHJldHVybiBudWxsO1xuXHR0cnkge1xuXHRcdGNvbnN0IGRlY29kZWQgPSBCdWZmZXIuZnJvbSh2YWx1ZSwgXCJiYXNlNjRcIikudG9TdHJpbmcoXCJ1dGYtOFwiKTtcblx0XHRpZiAoIWRlY29kZWQgfHwgZGVjb2RlZC50cmltKCkgPT09IFwibnVsbFwiKSByZXR1cm4gbnVsbDtcblx0XHRjb25zdCBjdHggPSBKU09OLnBhcnNlKGRlY29kZWQpIGFzIHsgdXNlcklkPzogc3RyaW5nOyBlbWFpbD86IHN0cmluZyB9O1xuXHRcdGlmICghY3R4IHx8IHR5cGVvZiBjdHgudXNlcklkICE9PSBcInN0cmluZ1wiKSByZXR1cm4gbnVsbDtcblx0XHRyZXR1cm4geyB1c2VySWQ6IGN0eC51c2VySWQsIGVtYWlsOiB0eXBlb2YgY3R4LmVtYWlsID09PSBcInN0cmluZ1wiID8gY3R4LmVtYWlsIDogXCJ1bmtub3duXCIgfTtcblx0fSBjYXRjaCB7XG5cdFx0cmV0dXJuIG51bGw7XG5cdH1cbn1cblxuLyoqIENvbnZlcnQgYSBab2Qgc2NoZW1hIHRvIGEgSlNPTi1TY2hlbWEgb2JqZWN0IHN1aXRhYmxlIGZvciBhbiBNQ1AgdG9vbCBgaW5wdXRTY2hlbWFgLiAqL1xuLy8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0V4cGxpY2l0QW55OiB6b2Qgc2NoZW1hIGlzIG9wYXF1ZSBoZXJlXG5mdW5jdGlvbiB0b0lucHV0SnNvblNjaGVtYShpbnB1dFpvZDogYW55IHwgdW5kZWZpbmVkKTogeyB0eXBlOiBcIm9iamVjdFwiOyBbazogc3RyaW5nXTogdW5rbm93biB9IHtcblx0Y29uc3QgZW1wdHkgPSB7IHR5cGU6IFwib2JqZWN0XCIgYXMgY29uc3QsIHByb3BlcnRpZXM6IHt9LCBhZGRpdGlvbmFsUHJvcGVydGllczogdHJ1ZSB9O1xuXHRpZiAoIWlucHV0Wm9kIHx8IHR5cGVvZiBpbnB1dFpvZCAhPT0gXCJvYmplY3RcIiB8fCB0eXBlb2YgaW5wdXRab2Quc2FmZVBhcnNlICE9PSBcImZ1bmN0aW9uXCIpIHtcblx0XHRyZXR1cm4gZW1wdHk7XG5cdH1cblx0dHJ5IHtcblx0XHRjb25zdCBqc29uID0gem9kVG9Kc29uU2NoZW1hKGlucHV0Wm9kLCB7IHRhcmdldDogXCJqc29uU2NoZW1hN1wiLCAkcmVmU3RyYXRlZ3k6IFwibm9uZVwiIH0pIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuXHRcdC8vIGJpb21lLWlnbm9yZSBsaW50L3BlcmZvcm1hbmNlL25vRGVsZXRlOiBzdHJpcCBKU09OLVNjaGVtYSBtZXRhIHRoZSBNQ1AgY2xpZW50IGRvZXNuJ3QgbmVlZFxuXHRcdGRlbGV0ZSBqc29uLiRzY2hlbWE7XG5cdFx0aWYgKGpzb24udHlwZSAhPT0gXCJvYmplY3RcIikge1xuXHRcdFx0cmV0dXJuIHsgLi4uZW1wdHksIF93cmFwcGVkOiBqc29uIH0gYXMgeyB0eXBlOiBcIm9iamVjdFwiOyBbazogc3RyaW5nXTogdW5rbm93biB9O1xuXHRcdH1cblx0XHRyZXR1cm4ganNvbiBhcyB7IHR5cGU6IFwib2JqZWN0XCI7IFtrOiBzdHJpbmddOiB1bmtub3duIH07XG5cdH0gY2F0Y2gge1xuXHRcdHJldHVybiBlbXB0eTtcblx0fVxufVxuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuLy8gVHJpZ2dlclxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgTWNwVHJpZ2dlciBleHRlbmRzIFRyaWdnZXJCYXNlIHtcblx0cHJvdGVjdGVkIG5vZGVNYXA6IFJ1bm5lckdsb2JhbE9wdGlvbnMgPSB7fSBhcyBSdW5uZXJHbG9iYWxPcHRpb25zO1xuXHRwcm90ZWN0ZWQgcmVhZG9ubHkgbG9nZ2VyID0gbmV3IERlZmF1bHRMb2dnZXIoKTtcblx0cHJvdGVjdGVkIHJlYWRvbmx5IHRyYWNlciA9IHRyYWNlLmdldFRyYWNlcihcblx0XHRwcm9jZXNzLmVudi5QUk9KRUNUX05BTUUgfHwgXCJ0cmlnZ2VyLW1jcC13b3JrZmxvd1wiLFxuXHRcdHByb2Nlc3MuZW52LlBST0pFQ1RfVkVSU0lPTiB8fCBcIjAuMC4xXCIsXG5cdCk7XG5cdHByaXZhdGUgcmVhZG9ubHkgbWV0ZXIgPSBtZXRyaWNzLmdldE1ldGVyKFwiYmxva1wiKTtcblx0cHJpdmF0ZSByZWFkb25seSBjb3VudGVyVG9vbENhbGxzID0gdGhpcy5tZXRlci5jcmVhdGVDb3VudGVyKFwiYmxva19tY3BfdG9vbF9jYWxsc190b3RhbFwiLCB7XG5cdFx0ZGVzY3JpcHRpb246IFwiTUNQIHRvb2xzL2NhbGwgZGlzcGF0Y2hlcy5cIixcblx0XHR1bml0OiBcIjFcIixcblx0fSk7XG5cdHByaXZhdGUgcmVhZG9ubHkgY291bnRlclNlc3Npb25zID0gdGhpcy5tZXRlci5jcmVhdGVDb3VudGVyKFwiYmxva19tY3Bfc3NlX3Nlc3Npb25zX3RvdGFsXCIsIHtcblx0XHRkZXNjcmlwdGlvbjogXCJNQ1AgU1NFIHNlc3Npb25zIG9wZW5lZC5cIixcblx0XHR1bml0OiBcIjFcIixcblx0fSk7XG5cblx0Ly8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0V4cGxpY2l0QW55OiBIb25vIGdlbmVyaWMgcHJvcGFnYXRpb24gKG1hdGNoZXMgU1NFL1dTIHRyaWdnZXJzKVxuXHRwcml2YXRlIHJlYWRvbmx5IGFwcDogSG9ubzxhbnksIGFueSwgYW55Pjtcblx0cHJpdmF0ZSByZWFkb25seSBodHRwVHJpZ2dlcjogSHR0cFRyaWdnZXJMaWtlIHwgbnVsbDtcblx0cHJpdmF0ZSB3aXJlZCA9IGZhbHNlO1xuXG5cdC8qKiBMaXZlIFNTRSBzZXNzaW9uczogc2Vzc2lvbklkIOKGkiB7IHRyYW5zcG9ydCwgc2VydmVyLCB1c2VyQ29udGV4dCB9LiAqL1xuXHRwcml2YXRlIHNzZVNlc3Npb25zID0gbmV3IE1hcDxcblx0XHRzdHJpbmcsXG5cdFx0eyB0cmFuc3BvcnQ6IFNTRVNlcnZlclRyYW5zcG9ydDsgc2VydmVyOiBNY3BTZGtTZXJ2ZXI7IHVzZXJDb250ZXh0OiBNY3BVc2VyQ29udGV4dCB8IG51bGwgfVxuXHQ+KCk7XG5cblx0Ly8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0V4cGxpY2l0QW55OiBtYXRjaGVzIGBhcHBgIGZpZWxkIGdlbmVyaWNcblx0Y29uc3RydWN0b3IoYXBwOiBIb25vPGFueSwgYW55LCBhbnk+LCBodHRwVHJpZ2dlcj86IEh0dHBUcmlnZ2VyTGlrZSkge1xuXHRcdHN1cGVyKCk7XG5cdFx0dGhpcy5hcHAgPSBhcHA7XG5cdFx0dGhpcy5odHRwVHJpZ2dlciA9IGh0dHBUcmlnZ2VyID8/IG51bGw7XG5cdFx0X3NldEFjdGl2ZU1jcFRyaWdnZXIodGhpcyk7XG5cdH1cblxuXHQvKiogSW5qZWN0IHRoZSBydW5uZXIncyBHbG9iYWxPcHRpb25zIChub2RlcyArIHdvcmtmbG93cykuIENhbGxlZCBiZWZvcmUgYGxpc3RlbigpYC4gKi9cblx0c2V0Tm9kZU1hcChub2RlTWFwOiBSdW5uZXJHbG9iYWxPcHRpb25zKTogdm9pZCB7XG5cdFx0dGhpcy5ub2RlTWFwID0gbm9kZU1hcDtcblx0fVxuXG5cdGFzeW5jIGxpc3RlbigpOiBQcm9taXNlPG51bWJlcj4ge1xuXHRcdGNvbnN0IHN0YXJ0VGltZSA9IHRoaXMuc3RhcnRDb3VudGVyKCk7XG5cdFx0aWYgKHRoaXMud2lyZWQpIHtcblx0XHRcdHRoaXMubG9nZ2VyLmxvZyhcIltibG9rXVttY3BdIGxpc3RlbigpIGNhbGxlZCB0d2ljZTsgaWdub3JpbmdcIik7XG5cdFx0XHRyZXR1cm4gdGhpcy5lbmRDb3VudGVyKHN0YXJ0VGltZSk7XG5cdFx0fVxuXHRcdHRoaXMud2lyZWQgPSB0cnVlO1xuXG5cdFx0aWYgKHRoaXMuaHR0cFRyaWdnZXIpIHtcblx0XHRcdHRoaXMuaHR0cFRyaWdnZXIuYWRkUHJlQ2F0Y2hBbGxIb29rKCgpID0+IHRoaXMucmVnaXN0ZXJSb3V0ZXNGcm9tUmVnaXN0cnkoKSk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMucmVnaXN0ZXJSb3V0ZXNGcm9tUmVnaXN0cnkoKTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXMuZW5kQ291bnRlcihzdGFydFRpbWUpO1xuXHR9XG5cblx0YXN5bmMgc3RvcCgpOiBQcm9taXNlPHZvaWQ+IHtcblx0XHRmb3IgKGNvbnN0IHsgdHJhbnNwb3J0IH0gb2YgdGhpcy5zc2VTZXNzaW9ucy52YWx1ZXMoKSkge1xuXHRcdFx0dHJ5IHtcblx0XHRcdFx0YXdhaXQgdHJhbnNwb3J0LmNsb3NlKCk7XG5cdFx0XHR9IGNhdGNoIHtcblx0XHRcdFx0LyogaWdub3JlICovXG5cdFx0XHR9XG5cdFx0fVxuXHRcdHRoaXMuc3NlU2Vzc2lvbnMuY2xlYXIoKTtcblx0XHR0aGlzLndpcmVkID0gZmFsc2U7XG5cdFx0aWYgKF9nZXRBY3RpdmVNY3BUcmlnZ2VyKCkgPT09IHRoaXMpIF9zZXRBY3RpdmVNY3BUcmlnZ2VyKG51bGwpO1xuXHRcdHRoaXMuZGVzdHJveU1vbml0b3JpbmcoKTtcblx0XHR0aGlzLmxvZ2dlci5sb2coXCJbYmxva11bbWNwXSBzdG9wcGVkXCIpO1xuXHR9XG5cblx0Z2V0U3RhdHMoKTogeyBzZXNzaW9uczogbnVtYmVyIH0ge1xuXHRcdHJldHVybiB7IHNlc3Npb25zOiB0aGlzLnNzZVNlc3Npb25zLnNpemUgfTtcblx0fVxuXG5cdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXHQvLyBSZWdpc3RyeSBzY2FuICsgZ3JvdXBpbmdcblx0Ly8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cblx0cHJpdmF0ZSByZWdpc3RlclJvdXRlc0Zyb21SZWdpc3RyeSgpOiB2b2lkIHtcblx0XHRjb25zdCBncm91cHMgPSB0aGlzLmdldFNlcnZlckdyb3VwcygpO1xuXHRcdGlmIChncm91cHMubGVuZ3RoID09PSAwKSB7XG5cdFx0XHR0aGlzLmxvZ2dlci5sb2coXCJbYmxva11bbWNwXSBubyB3b3JrZmxvd3Mgd2l0aCB0cmlnZ2VyLm1jcCBmb3VuZFwiKTtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0Zm9yIChjb25zdCBncm91cCBvZiBncm91cHMpIHtcblx0XHRcdHRoaXMucmVnaXN0ZXJHcm91cFJvdXRlcyhncm91cCk7XG5cdFx0fVxuXHR9XG5cblx0cHJpdmF0ZSBnZXRTZXJ2ZXJHcm91cHMoKTogU2VydmVyR3JvdXBbXSB7XG5cdFx0Y29uc3QgcmVnaXN0cnkgPSBXb3JrZmxvd1JlZ2lzdHJ5LmdldEluc3RhbmNlKCk7XG5cdFx0Y29uc3QgYnlQYXRoID0gbmV3IE1hcDxzdHJpbmcsIFNlcnZlckdyb3VwPigpO1xuXG5cdFx0Zm9yIChjb25zdCBlbnRyeSBvZiByZWdpc3RyeS5saXN0KCkpIHtcblx0XHRcdC8vIFdvcmtmbG93cyByZWdpc3RlcmVkIGFzIGJ1aWxkZXJzIGV4cG9zZSBjb25maWcgb24gYF9jb25maWdgOyBwbGFpblxuXHRcdFx0Ly8gb2JqZWN0cyAodGVzdHMgLyBKU09OKSBleHBvc2UgaXQgYXQgdGhlIHRvcCBsZXZlbC5cblx0XHRcdGNvbnN0IHdmID0gKGVudHJ5LndvcmtmbG93IGFzIHsgX2NvbmZpZz86IHVua25vd24gfSk/Ll9jb25maWcgPz8gZW50cnkud29ya2Zsb3c7XG5cdFx0XHRjb25zdCBjZmcgPSAod2YgYXMgeyB0cmlnZ2VyPzogeyBtY3A/OiBNY3BUcmlnZ2VyQ29uZmlnIH0gfSk/LnRyaWdnZXI/Lm1jcDtcblx0XHRcdGlmICghY2ZnIHx8IHR5cGVvZiBjZmcucGF0aCAhPT0gXCJzdHJpbmdcIikgY29udGludWU7XG5cblx0XHRcdGNvbnN0IHBhdGggPSBjZmcucGF0aDtcblx0XHRcdGxldCBncm91cCA9IGJ5UGF0aC5nZXQocGF0aCk7XG5cdFx0XHRpZiAoIWdyb3VwKSB7XG5cdFx0XHRcdGdyb3VwID0ge1xuXHRcdFx0XHRcdHBhdGgsXG5cdFx0XHRcdFx0c2VydmVyTmFtZTogY2ZnLnNlcnZlck5hbWUgfHwgREVGQVVMVF9TRVJWRVJfTkFNRSxcblx0XHRcdFx0XHRzZXJ2ZXJWZXJzaW9uOiBjZmcuc2VydmVyVmVyc2lvbiB8fCBERUZBVUxUX1NFUlZFUl9WRVJTSU9OLFxuXHRcdFx0XHRcdHRyYW5zcG9ydHM6IEFycmF5LmlzQXJyYXkoY2ZnLnRyYW5zcG9ydHMpICYmIGNmZy50cmFuc3BvcnRzLmxlbmd0aCA+IDAgPyBjZmcudHJhbnNwb3J0cyA6IERFRkFVTFRfVFJBTlNQT1JUUyxcblx0XHRcdFx0XHR0b29sczogW10sXG5cdFx0XHRcdFx0cmVzb3VyY2VzOiBbXSxcblx0XHRcdFx0fTtcblx0XHRcdFx0YnlQYXRoLnNldChwYXRoLCBncm91cCk7XG5cdFx0XHR9XG5cblx0XHRcdGlmIChjZmcucmVzb3VyY2UgJiYgdHlwZW9mIGNmZy5yZXNvdXJjZS51cmkgPT09IFwic3RyaW5nXCIpIHtcblx0XHRcdFx0Z3JvdXAucmVzb3VyY2VzLnB1c2goe1xuXHRcdFx0XHRcdHdvcmtmbG93TmFtZTogZW50cnkubmFtZSxcblx0XHRcdFx0XHR1cmk6IGNmZy5yZXNvdXJjZS51cmksXG5cdFx0XHRcdFx0bmFtZTogY2ZnLnJlc291cmNlLm5hbWUgfHwgZW50cnkubmFtZSxcblx0XHRcdFx0XHRkZXNjcmlwdGlvbjogY2ZnLnJlc291cmNlLmRlc2NyaXB0aW9uLFxuXHRcdFx0XHRcdG1pbWVUeXBlOiBjZmcucmVzb3VyY2UubWltZVR5cGUgfHwgXCJhcHBsaWNhdGlvbi9qc29uXCIsXG5cdFx0XHRcdH0pO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0Y29uc3QgaW5wdXRab2QgPSAod2YgYXMgeyBpbnB1dD86IHVua25vd24gfSk/LmlucHV0O1xuXHRcdFx0XHRncm91cC50b29scy5wdXNoKHtcblx0XHRcdFx0XHR3b3JrZmxvd05hbWU6IGVudHJ5Lm5hbWUsXG5cdFx0XHRcdFx0dG9vbE5hbWU6IGNmZy50b29sPy5uYW1lIHx8IGVudHJ5Lm5hbWUsXG5cdFx0XHRcdFx0ZGVzY3JpcHRpb246IGNmZy50b29sPy5kZXNjcmlwdGlvbiB8fCBgUnVuIHRoZSBcIiR7ZW50cnkubmFtZX1cIiB3b3JrZmxvdy5gLFxuXHRcdFx0XHRcdGlucHV0Wm9kLFxuXHRcdFx0XHR9KTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRyZXR1cm4gWy4uLmJ5UGF0aC52YWx1ZXMoKV07XG5cdH1cblxuXHQvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblx0Ly8gTUNQIHNlcnZlciBmYWN0b3J5IChvbmUgcGVyIHJlcXVlc3Qvc2Vzc2lvbiwgYm91bmQgdG8gYSB1c2VyIGNvbnRleHQpXG5cdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG5cdHByaXZhdGUgYnVpbGRTZXJ2ZXIoZ3JvdXA6IFNlcnZlckdyb3VwLCBnZXRVc2VyQ29udGV4dDogKCkgPT4gTWNwVXNlckNvbnRleHQgfCBudWxsKTogTWNwU2RrU2VydmVyIHtcblx0XHRjb25zdCBzZXJ2ZXIgPSBuZXcgTWNwU2RrU2VydmVyKFxuXHRcdFx0eyBuYW1lOiBncm91cC5zZXJ2ZXJOYW1lLCB2ZXJzaW9uOiBncm91cC5zZXJ2ZXJWZXJzaW9uIH0sXG5cdFx0XHR7IGNhcGFiaWxpdGllczogeyB0b29sczoge30sIHJlc291cmNlczoge30gfSB9LFxuXHRcdCk7XG5cblx0XHRzZXJ2ZXIuc2V0UmVxdWVzdEhhbmRsZXIoTGlzdFRvb2xzUmVxdWVzdFNjaGVtYSwgYXN5bmMgKCkgPT4gKHtcblx0XHRcdHRvb2xzOiBncm91cC50b29scy5tYXAoKHQpID0+ICh7XG5cdFx0XHRcdG5hbWU6IHQudG9vbE5hbWUsXG5cdFx0XHRcdGRlc2NyaXB0aW9uOiB0LmRlc2NyaXB0aW9uLFxuXHRcdFx0XHRpbnB1dFNjaGVtYTogdG9JbnB1dEpzb25TY2hlbWEodC5pbnB1dFpvZCksXG5cdFx0XHR9KSksXG5cdFx0fSkpO1xuXG5cdFx0c2VydmVyLnNldFJlcXVlc3RIYW5kbGVyKENhbGxUb29sUmVxdWVzdFNjaGVtYSwgYXN5bmMgKHJlcSkgPT4ge1xuXHRcdFx0Y29uc3QgdG9vbE5hbWUgPSByZXEucGFyYW1zLm5hbWU7XG5cdFx0XHRjb25zdCB0b29sID0gZ3JvdXAudG9vbHMuZmluZCgodCkgPT4gdC50b29sTmFtZSA9PT0gdG9vbE5hbWUpO1xuXHRcdFx0aWYgKCF0b29sKSB7XG5cdFx0XHRcdHJldHVybiB7IGNvbnRlbnQ6IFt7IHR5cGU6IFwidGV4dFwiLCB0ZXh0OiBgVW5rbm93biB0b29sOiAke3Rvb2xOYW1lfWAgfV0sIGlzRXJyb3I6IHRydWUgfTtcblx0XHRcdH1cblx0XHRcdGNvbnN0IGFyZ3MgPSAocmVxLnBhcmFtcy5hcmd1bWVudHMgPz8ge30pIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuXHRcdFx0cmV0dXJuIHRoaXMuZGlzcGF0Y2hUb29sKHRvb2wsIGFyZ3MsIGdldFVzZXJDb250ZXh0KCkpO1xuXHRcdH0pO1xuXG5cdFx0aWYgKGdyb3VwLnJlc291cmNlcy5sZW5ndGggPiAwKSB7XG5cdFx0XHRzZXJ2ZXIuc2V0UmVxdWVzdEhhbmRsZXIoTGlzdFJlc291cmNlc1JlcXVlc3RTY2hlbWEsIGFzeW5jICgpID0+ICh7XG5cdFx0XHRcdHJlc291cmNlczogZ3JvdXAucmVzb3VyY2VzLm1hcCgocikgPT4gKHtcblx0XHRcdFx0XHR1cmk6IHIudXJpLFxuXHRcdFx0XHRcdG5hbWU6IHIubmFtZSxcblx0XHRcdFx0XHRkZXNjcmlwdGlvbjogci5kZXNjcmlwdGlvbixcblx0XHRcdFx0XHRtaW1lVHlwZTogci5taW1lVHlwZSxcblx0XHRcdFx0fSkpLFxuXHRcdFx0fSkpO1xuXG5cdFx0XHRzZXJ2ZXIuc2V0UmVxdWVzdEhhbmRsZXIoUmVhZFJlc291cmNlUmVxdWVzdFNjaGVtYSwgYXN5bmMgKHJlcSkgPT4ge1xuXHRcdFx0XHRjb25zdCB1cmkgPSByZXEucGFyYW1zLnVyaTtcblx0XHRcdFx0Y29uc3QgcmVzb3VyY2UgPSBncm91cC5yZXNvdXJjZXMuZmluZCgocikgPT4gci51cmkgPT09IHVyaSk7XG5cdFx0XHRcdGlmICghcmVzb3VyY2UpIHRocm93IG5ldyBFcnJvcihgVW5rbm93biByZXNvdXJjZTogJHt1cml9YCk7XG5cdFx0XHRcdGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMuZGlzcGF0Y2hSZXNvdXJjZShyZXNvdXJjZSwgZ2V0VXNlckNvbnRleHQoKSk7XG5cdFx0XHRcdGNvbnN0IHRleHQgPSB0eXBlb2YgcmVzdWx0ID09PSBcInN0cmluZ1wiID8gcmVzdWx0IDogSlNPTi5zdHJpbmdpZnkocmVzdWx0LCBudWxsLCAyKTtcblx0XHRcdFx0cmV0dXJuIHsgY29udGVudHM6IFt7IHVyaTogcmVzb3VyY2UudXJpLCBtaW1lVHlwZTogcmVzb3VyY2UubWltZVR5cGUsIHRleHQgfV0gfTtcblx0XHRcdH0pO1xuXHRcdH1cblxuXHRcdHJldHVybiBzZXJ2ZXI7XG5cdH1cblxuXHQvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblx0Ly8gV29ya2Zsb3cgZGlzcGF0Y2ggKHRvb2xzL2NhbGwgKyByZXNvdXJjZXMvcmVhZCBydW4gdGhyb3VnaCB0aGUgcnVubmVyKVxuXHQvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuXHRwcml2YXRlIGFzeW5jIGRpc3BhdGNoVG9vbChcblx0XHR0b29sOiBUb29sRW50cnksXG5cdFx0YXJnczogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG5cdFx0dXNlckNvbnRleHQ6IE1jcFVzZXJDb250ZXh0IHwgbnVsbCxcblx0KTogUHJvbWlzZTx7IGNvbnRlbnQ6IEFycmF5PHsgdHlwZTogXCJ0ZXh0XCI7IHRleHQ6IHN0cmluZyB9PjsgaXNFcnJvcj86IGJvb2xlYW4gfT4ge1xuXHRcdHRoaXMuY291bnRlclRvb2xDYWxscy5hZGQoMSwgeyB0b29sOiB0b29sLnRvb2xOYW1lIH0pO1xuXHRcdHRyeSB7XG5cdFx0XHRjb25zdCBkYXRhID0gYXdhaXQgdGhpcy5ydW5Xb3JrZmxvdyh0b29sLndvcmtmbG93TmFtZSwgYXJncywgdXNlckNvbnRleHQsIGBtY3AudG9vbDoke3Rvb2wudG9vbE5hbWV9YCk7XG5cdFx0XHRjb25zdCB0ZXh0ID0gdHlwZW9mIGRhdGEgPT09IFwic3RyaW5nXCIgPyBkYXRhIDogSlNPTi5zdHJpbmdpZnkoZGF0YSwgbnVsbCwgMik7XG5cdFx0XHRyZXR1cm4geyBjb250ZW50OiBbeyB0eXBlOiBcInRleHRcIiwgdGV4dCB9XSB9O1xuXHRcdH0gY2F0Y2ggKGVycikge1xuXHRcdFx0Y29uc3QgbXNnID0gZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6IFN0cmluZyhlcnIpO1xuXHRcdFx0dGhpcy5sb2dnZXIuZXJyb3IoYFtibG9rXVttY3BdIHRvb2wgXCIke3Rvb2wudG9vbE5hbWV9XCIgZmFpbGVkOiAke21zZ31gKTtcblx0XHRcdC8vIFRvb2wgZmFpbHVyZSDihpIgTUNQIHRvb2wgZXJyb3IgcmVzdWx0LCBOT1QgYSB0cmFuc3BvcnQgY3Jhc2guXG5cdFx0XHRyZXR1cm4geyBjb250ZW50OiBbeyB0eXBlOiBcInRleHRcIiwgdGV4dDogYEVycm9yOiAke21zZ31gIH1dLCBpc0Vycm9yOiB0cnVlIH07XG5cdFx0fVxuXHR9XG5cblx0cHJpdmF0ZSBhc3luYyBkaXNwYXRjaFJlc291cmNlKHJlc291cmNlOiBSZXNvdXJjZUVudHJ5LCB1c2VyQ29udGV4dDogTWNwVXNlckNvbnRleHQgfCBudWxsKTogUHJvbWlzZTx1bmtub3duPiB7XG5cdFx0cmV0dXJuIHRoaXMucnVuV29ya2Zsb3cocmVzb3VyY2Uud29ya2Zsb3dOYW1lLCB7fSwgdXNlckNvbnRleHQsIGBtY3AucmVzb3VyY2U6JHtyZXNvdXJjZS51cml9YCk7XG5cdH1cblxuXHQvKipcblx0ICogUnVuIGEgd29ya2Zsb3cgdGhyb3VnaCB0aGUgcnVubmVyIGFuZCByZXR1cm4gYGN0eC5yZXNwb25zZS5kYXRhYC4gTWlycm9yc1xuXHQgKiB0aGUgV2ViaG9va1RyaWdnZXIgcmVxdWVzdOKGknJlc3BvbnNlIGRpc3BhdGNoIChpbml0IOKGkiBjb250ZXh0IOKGkiBtaWRkbGV3YXJlIOKGklxuXHQgKiBydW4pLiBNQ1AgdG9vbCBjYWxscyB3aXRoaW4gYSBzZXNzaW9uIGFyZSBzZXJpYWwsIG1hdGNoaW5nIHRoZSBzaGFyZWRcblx0ICogYHRoaXMuY29uZmlndXJhdGlvbmAgbGlmZWN5Y2xlIHRoZSBvdGhlciB0cmlnZ2VycyB1c2UuXG5cdCAqL1xuXHRwcml2YXRlIGFzeW5jIHJ1bldvcmtmbG93KFxuXHRcdHdvcmtmbG93TmFtZTogc3RyaW5nLFxuXHRcdGFyZ3M6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LFxuXHRcdHVzZXJDb250ZXh0OiBNY3BVc2VyQ29udGV4dCB8IG51bGwsXG5cdFx0c3BhbkxhYmVsOiBzdHJpbmcsXG5cdCk6IFByb21pc2U8dW5rbm93bj4ge1xuXHRcdGNvbnN0IHJlcXVlc3RJZCA9IHV1aWQoKTtcblx0XHRyZXR1cm4gdGhpcy50cmFjZXIuc3RhcnRBY3RpdmVTcGFuKGBtY3A6JHt3b3JrZmxvd05hbWV9YCwgYXN5bmMgKHNwYW46IFNwYW4pID0+IHtcblx0XHRcdHRyeSB7XG5cdFx0XHRcdGNvbnN0IHJlZ2lzdHJ5ID0gV29ya2Zsb3dSZWdpc3RyeS5nZXRJbnN0YW5jZSgpO1xuXHRcdFx0XHRjb25zdCBlbnRyeSA9IHJlZ2lzdHJ5LmdldCh3b3JrZmxvd05hbWUpO1xuXHRcdFx0XHRpZiAoIWVudHJ5KSB0aHJvdyBuZXcgRXJyb3IoYHdvcmtmbG93IFwiJHt3b3JrZmxvd05hbWV9XCIgbm90IGZvdW5kIGluIHJlZ2lzdHJ5YCk7XG5cdFx0XHRcdGF3YWl0IHRoaXMuY29uZmlndXJhdGlvbi5pbml0KHdvcmtmbG93TmFtZSwgdGhpcy5ub2RlTWFwLCBlbnRyeS53b3JrZmxvdyk7XG5cblx0XHRcdFx0Y29uc3QgaGVhZGVyczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHt9O1xuXHRcdFx0XHRpZiAodXNlckNvbnRleHQpIHtcblx0XHRcdFx0XHQvLyBDYXJyeSBpZGVudGl0eSB0byB0aGUgd29ya2Zsb3cgZXhhY3RseSBhcyBIVFRQIHdvdWxkIChiYXNlNjQgaGVhZGVyKS5cblx0XHRcdFx0XHRoZWFkZXJzW1wieC11c2VyLWNvbnRleHRcIl0gPSBCdWZmZXIuZnJvbShKU09OLnN0cmluZ2lmeSh1c2VyQ29udGV4dCksIFwidXRmLThcIikudG9TdHJpbmcoXCJiYXNlNjRcIik7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRjb25zdCBjdHg6IENvbnRleHQgPSB0aGlzLmNyZWF0ZUNvbnRleHQodW5kZWZpbmVkLCB3b3JrZmxvd05hbWUsIHJlcXVlc3RJZCk7XG5cdFx0XHRcdGN0eC5yZXF1ZXN0ID0ge1xuXHRcdFx0XHRcdGJvZHk6IGFyZ3MsXG5cdFx0XHRcdFx0aGVhZGVycyxcblx0XHRcdFx0XHRwYXJhbXM6IHt9LFxuXHRcdFx0XHRcdHF1ZXJ5OiB7fSxcblx0XHRcdFx0fSBhcyB1bmtub3duIGFzIFJlcXVlc3RDb250ZXh0O1xuXHRcdFx0XHQoY3R4IGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KS5fbWNwID0geyB1c2VyQ29udGV4dCB9O1xuXG5cdFx0XHRcdGF3YWl0IHRoaXMuYXBwbHlNaWRkbGV3YXJlQ2hhaW4oY3R4LCB0aGlzLm5vZGVNYXApO1xuXHRcdFx0XHRhd2FpdCB0aGlzLnJ1bihjdHgpO1xuXG5cdFx0XHRcdHNwYW4uc2V0QXR0cmlidXRlKFwid29ya2Zsb3dfbmFtZVwiLCB3b3JrZmxvd05hbWUpO1xuXHRcdFx0XHRzcGFuLnNldEF0dHJpYnV0ZShcIm1jcF9sYWJlbFwiLCBzcGFuTGFiZWwpO1xuXHRcdFx0XHRzcGFuLnNldFN0YXR1cyh7IGNvZGU6IFNwYW5TdGF0dXNDb2RlLk9LIH0pO1xuXHRcdFx0XHRyZXR1cm4gY3R4LnJlc3BvbnNlPy5kYXRhO1xuXHRcdFx0fSBjYXRjaCAoZXJyKSB7XG5cdFx0XHRcdGNvbnN0IG1zZyA9IGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiBTdHJpbmcoZXJyKTtcblx0XHRcdFx0c3Bhbi5yZWNvcmRFeGNlcHRpb24oZXJyIGFzIEVycm9yKTtcblx0XHRcdFx0c3Bhbi5zZXRTdGF0dXMoeyBjb2RlOiBTcGFuU3RhdHVzQ29kZS5FUlJPUiwgbWVzc2FnZTogbXNnIH0pO1xuXHRcdFx0XHR0aHJvdyBlcnI7XG5cdFx0XHR9IGZpbmFsbHkge1xuXHRcdFx0XHRzcGFuLmVuZCgpO1xuXHRcdFx0fVxuXHRcdH0pO1xuXHR9XG5cblx0Ly8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cdC8vIFJvdXRlIHJlZ2lzdHJhdGlvbiAoU1NFICsgU3RyZWFtYWJsZS1IVFRQKVxuXHQvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuXHRwcml2YXRlIHJlZ2lzdGVyR3JvdXBSb3V0ZXMoZ3JvdXA6IFNlcnZlckdyb3VwKTogdm9pZCB7XG5cdFx0dGhpcy5sb2dnZXIubG9nKFxuXHRcdFx0YFtibG9rXVttY3BdIHNlcnZlciBcIiR7Z3JvdXAuc2VydmVyTmFtZX1cIiBhdCAke2dyb3VwLnBhdGh9IOKAlCAke2dyb3VwLnRvb2xzLmxlbmd0aH0gdG9vbChzKSwgJHtncm91cC5yZXNvdXJjZXMubGVuZ3RofSByZXNvdXJjZShzKSwgdHJhbnNwb3J0cz1bJHtncm91cC50cmFuc3BvcnRzLmpvaW4oXCIsXCIpfV1gLFxuXHRcdCk7XG5cblx0XHRpZiAoZ3JvdXAudHJhbnNwb3J0cy5pbmNsdWRlcyhcInNzZVwiKSkge1xuXHRcdFx0dGhpcy5yZWdpc3RlclNzZVJvdXRlcyhncm91cCk7XG5cdFx0fVxuXHRcdGlmIChncm91cC50cmFuc3BvcnRzLmluY2x1ZGVzKFwic3RyZWFtYWJsZS1odHRwXCIpKSB7XG5cdFx0XHR0aGlzLnJlZ2lzdGVyU3RyZWFtYWJsZUh0dHBSb3V0ZShncm91cCk7XG5cdFx0fVxuXHR9XG5cblx0cHJpdmF0ZSByZWdpc3RlclNzZVJvdXRlcyhncm91cDogU2VydmVyR3JvdXApOiB2b2lkIHtcblx0XHRjb25zdCBzc2VQYXRoID0gYCR7Z3JvdXAucGF0aH0vc3NlYDtcblx0XHRjb25zdCBtZXNzYWdlc1BhdGggPSBgJHtncm91cC5wYXRofS9tZXNzYWdlc2A7XG5cdFx0dGhpcy5sb2dnZXIubG9nKGBbYmxva11bbWNwXSAgIEdFVCAgJHtzc2VQYXRofSAgIFBPU1QgJHttZXNzYWdlc1BhdGh9ICAoc3NlKWApO1xuXG5cdFx0Ly8gR0VUIDxwYXRoPi9zc2Ug4oCUIG9wZW4gdGhlIFNTRSBzdHJlYW07IFNESyBhbm5vdW5jZXMgdGhlIG1lc3NhZ2VzIGVuZHBvaW50LlxuXHRcdHRoaXMuYXBwLmdldChzc2VQYXRoLCBhc3luYyAoYzogSG9ub0NvbnRleHQpID0+IHtcblx0XHRcdGNvbnN0IGVudiA9IGMuZW52IGFzIHVua25vd24gYXMgeyBpbmNvbWluZzogSW5jb21pbmdNZXNzYWdlOyBvdXRnb2luZzogU2VydmVyUmVzcG9uc2UgfTtcblx0XHRcdGlmICghZW52Py5vdXRnb2luZykge1xuXHRcdFx0XHRyZXR1cm4gYy50ZXh0KFwiTUNQIFNTRSB0cmFuc3BvcnQgcmVxdWlyZXMgdGhlIE5vZGUgc2VydmVyIChAaG9uby9ub2RlLXNlcnZlcikuXCIsIDUwMCk7XG5cdFx0XHR9XG5cdFx0XHRjb25zdCByYXdVc2VyQ3R4ID1cblx0XHRcdFx0Yy5yZXEuaGVhZGVyKFwieC11c2VyLWNvbnRleHRcIikgfHwgKG5ldyBVUkwoYy5yZXEudXJsKS5zZWFyY2hQYXJhbXMuZ2V0KFwidXNlcl9jb250ZXh0XCIpID8/IHVuZGVmaW5lZCk7XG5cdFx0XHRjb25zdCB1c2VyQ29udGV4dCA9IHBhcnNlVXNlckNvbnRleHQocmF3VXNlckN0eCk7XG5cblx0XHRcdGNvbnN0IHRyYW5zcG9ydCA9IG5ldyBTU0VTZXJ2ZXJUcmFuc3BvcnQobWVzc2FnZXNQYXRoLCBlbnYub3V0Z29pbmcpO1xuXHRcdFx0Y29uc3Qgc2Vzc2lvbklkID0gdHJhbnNwb3J0LnNlc3Npb25JZDtcblx0XHRcdGNvbnN0IHNlcnZlciA9IHRoaXMuYnVpbGRTZXJ2ZXIoZ3JvdXAsICgpID0+IHRoaXMuc3NlU2Vzc2lvbnMuZ2V0KHNlc3Npb25JZCk/LnVzZXJDb250ZXh0ID8/IG51bGwpO1xuXHRcdFx0dGhpcy5zc2VTZXNzaW9ucy5zZXQoc2Vzc2lvbklkLCB7IHRyYW5zcG9ydCwgc2VydmVyLCB1c2VyQ29udGV4dCB9KTtcblx0XHRcdHRoaXMuY291bnRlclNlc3Npb25zLmFkZCgxLCB7IHNlcnZlcjogZ3JvdXAuc2VydmVyTmFtZSB9KTtcblxuXHRcdFx0dHJhbnNwb3J0Lm9uY2xvc2UgPSAoKSA9PiB7XG5cdFx0XHRcdHRoaXMuc3NlU2Vzc2lvbnMuZGVsZXRlKHNlc3Npb25JZCk7XG5cdFx0XHR9O1xuXG5cdFx0XHR0cnkge1xuXHRcdFx0XHRhd2FpdCBzZXJ2ZXIuY29ubmVjdCh0cmFuc3BvcnQpO1xuXHRcdFx0fSBjYXRjaCAoZXJyKSB7XG5cdFx0XHRcdHRoaXMuc3NlU2Vzc2lvbnMuZGVsZXRlKHNlc3Npb25JZCk7XG5cdFx0XHRcdGNvbnN0IG1zZyA9IGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiBTdHJpbmcoZXJyKTtcblx0XHRcdFx0dGhpcy5sb2dnZXIuZXJyb3IoYFtibG9rXVttY3BdIHNzZSBjb25uZWN0IGZhaWxlZDogJHttc2d9YCk7XG5cdFx0XHR9XG5cdFx0XHRyZXR1cm4gUkVTUE9OU0VfQUxSRUFEWV9TRU5UO1xuXHRcdH0pO1xuXG5cdFx0Ly8gUE9TVCA8cGF0aD4vbWVzc2FnZXM/c2Vzc2lvbklkPeKApiDigJQgSlNPTi1SUEMgbWVzc2FnZXMgZm9yIGFuIG9wZW4gc3RyZWFtLlxuXHRcdHRoaXMuYXBwLnBvc3QobWVzc2FnZXNQYXRoLCBhc3luYyAoYzogSG9ub0NvbnRleHQpID0+IHtcblx0XHRcdGNvbnN0IGVudiA9IGMuZW52IGFzIHVua25vd24gYXMgeyBpbmNvbWluZzogSW5jb21pbmdNZXNzYWdlOyBvdXRnb2luZzogU2VydmVyUmVzcG9uc2UgfTtcblx0XHRcdGNvbnN0IHNlc3Npb25JZCA9IG5ldyBVUkwoYy5yZXEudXJsKS5zZWFyY2hQYXJhbXMuZ2V0KFwic2Vzc2lvbklkXCIpID8/IHVuZGVmaW5lZDtcblx0XHRcdGlmICghc2Vzc2lvbklkKSByZXR1cm4gYy50ZXh0KFwiTWlzc2luZyBzZXNzaW9uSWQgcXVlcnkgcGFyYW1ldGVyXCIsIDQwMCk7XG5cdFx0XHRjb25zdCBzZXNzaW9uID0gdGhpcy5zc2VTZXNzaW9ucy5nZXQoc2Vzc2lvbklkKTtcblx0XHRcdGlmICghc2Vzc2lvbikgcmV0dXJuIGMudGV4dChcIlNlc3Npb24gbm90IGZvdW5kXCIsIDQwNCk7XG5cdFx0XHRjb25zdCBib2R5ID0gYXdhaXQgYy5yZXEuanNvbigpLmNhdGNoKCgpID0+IHVuZGVmaW5lZCk7XG5cdFx0XHR0cnkge1xuXHRcdFx0XHRhd2FpdCBzZXNzaW9uLnRyYW5zcG9ydC5oYW5kbGVQb3N0TWVzc2FnZShlbnYuaW5jb21pbmcsIGVudi5vdXRnb2luZywgYm9keSk7XG5cdFx0XHR9IGNhdGNoIChlcnIpIHtcblx0XHRcdFx0Y29uc3QgbXNnID0gZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6IFN0cmluZyhlcnIpO1xuXHRcdFx0XHR0aGlzLmxvZ2dlci5lcnJvcihgW2Jsb2tdW21jcF0gaGFuZGxlUG9zdE1lc3NhZ2UgZmFpbGVkOiAke21zZ31gKTtcblx0XHRcdFx0aWYgKCFlbnYub3V0Z29pbmcuaGVhZGVyc1NlbnQpIHJldHVybiBjLnRleHQoXCJFcnJvciBoYW5kbGluZyBNQ1AgbWVzc2FnZVwiLCA1MDApO1xuXHRcdFx0fVxuXHRcdFx0cmV0dXJuIFJFU1BPTlNFX0FMUkVBRFlfU0VOVDtcblx0XHR9KTtcblx0fVxuXG5cdHByaXZhdGUgcmVnaXN0ZXJTdHJlYW1hYmxlSHR0cFJvdXRlKGdyb3VwOiBTZXJ2ZXJHcm91cCk6IHZvaWQge1xuXHRcdHRoaXMubG9nZ2VyLmxvZyhgW2Jsb2tdW21jcF0gICBBTEwgICR7Z3JvdXAucGF0aH0gIChzdHJlYW1hYmxlLWh0dHApYCk7XG5cblx0XHQvLyBTdGF0ZWxlc3MgU3RyZWFtYWJsZS1IVFRQOiBhIGZyZXNoIHNlcnZlciArIHRyYW5zcG9ydCBwZXIgcmVxdWVzdCwgc2VydmVkXG5cdFx0Ly8gZGlyZWN0bHkgb2ZmIHRoZSBGZXRjaCBgUmVxdWVzdGAgKHdvcmtzIG9uIGFueSBIb25vIHJ1bnRpbWUpLlxuXHRcdHRoaXMuYXBwLmFsbChncm91cC5wYXRoLCBhc3luYyAoYzogSG9ub0NvbnRleHQpID0+IHtcblx0XHRcdGNvbnN0IHJhd1VzZXJDdHggPVxuXHRcdFx0XHRjLnJlcS5oZWFkZXIoXCJ4LXVzZXItY29udGV4dFwiKSB8fCAobmV3IFVSTChjLnJlcS51cmwpLnNlYXJjaFBhcmFtcy5nZXQoXCJ1c2VyX2NvbnRleHRcIikgPz8gdW5kZWZpbmVkKTtcblx0XHRcdGNvbnN0IHVzZXJDb250ZXh0ID0gcGFyc2VVc2VyQ29udGV4dChyYXdVc2VyQ3R4KTtcblxuXHRcdFx0Y29uc3Qgc2VydmVyID0gdGhpcy5idWlsZFNlcnZlcihncm91cCwgKCkgPT4gdXNlckNvbnRleHQpO1xuXHRcdFx0Y29uc3QgdHJhbnNwb3J0ID0gbmV3IFdlYlN0YW5kYXJkU3RyZWFtYWJsZUhUVFBTZXJ2ZXJUcmFuc3BvcnQoeyBzZXNzaW9uSWRHZW5lcmF0b3I6IHVuZGVmaW5lZCB9KTtcblx0XHRcdHRyeSB7XG5cdFx0XHRcdGF3YWl0IHNlcnZlci5jb25uZWN0KHRyYW5zcG9ydCk7XG5cdFx0XHRcdGNvbnN0IHJlcyA9IGF3YWl0IHRyYW5zcG9ydC5oYW5kbGVSZXF1ZXN0KGMucmVxLnJhdyk7XG5cdFx0XHRcdHJldHVybiByZXM7XG5cdFx0XHR9IGNhdGNoIChlcnIpIHtcblx0XHRcdFx0Y29uc3QgbXNnID0gZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6IFN0cmluZyhlcnIpO1xuXHRcdFx0XHR0aGlzLmxvZ2dlci5lcnJvcihgW2Jsb2tdW21jcF0gc3RyZWFtYWJsZS1odHRwIHJlcXVlc3QgZmFpbGVkOiAke21zZ31gKTtcblx0XHRcdFx0cmV0dXJuIGMuanNvbih7IGpzb25ycGM6IFwiMi4wXCIsIGVycm9yOiB7IGNvZGU6IC0zMjYwMywgbWVzc2FnZTogbXNnIH0sIGlkOiBudWxsIH0sIDUwMCk7XG5cdFx0XHR9XG5cdFx0fSk7XG5cdH1cbn1cblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIFNpbmdsZXRvbiBhY2Nlc3NvciAocGFyaXR5IHdpdGggU1NFL1dlYmhvb2sgdHJpZ2dlcnMpXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG5sZXQgYWN0aXZlVHJpZ2dlcjogTWNwVHJpZ2dlciB8IG51bGwgPSBudWxsO1xuZXhwb3J0IGZ1bmN0aW9uIF9zZXRBY3RpdmVNY3BUcmlnZ2VyKHRyaWdnZXI6IE1jcFRyaWdnZXIgfCBudWxsKTogdm9pZCB7XG5cdGFjdGl2ZVRyaWdnZXIgPSB0cmlnZ2VyO1xufVxuZXhwb3J0IGZ1bmN0aW9uIF9nZXRBY3RpdmVNY3BUcmlnZ2VyKCk6IE1jcFRyaWdnZXIgfCBudWxsIHtcblx0cmV0dXJuIGFjdGl2ZVRyaWdnZXI7XG59XG5cbmV4cG9ydCB0eXBlIHsgTWNwVHJpZ2dlckNvbmZpZyB9O1xuIl19