@better-auth/mcp 1.5.0-beta.9 → 1.7.0-beta.6

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.
package/dist/index.mjs CHANGED
@@ -1,1040 +1,214 @@
1
- #!/usr/bin/env node
2
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
- import * as z from "zod";
5
-
6
- //#region src/lib/parser.ts
7
- function parseExistingSetup(existingSetup) {
8
- const detected = { features: [] };
9
- if (existingSetup.authConfig) {
10
- const config = existingSetup.authConfig;
11
- detected.database = detectDatabase(config);
12
- detected.orm = detectORM(config);
13
- detected.features = detectFeatures(config, existingSetup.authClientConfig);
14
- }
15
- return detected;
16
- }
17
- function detectDatabase(config) {
18
- if (/provider:\s*["']postgresql["']/.test(config) || /provider:\s*["']pg["']/.test(config)) return "postgres";
19
- if (/provider:\s*["']mysql["']/.test(config)) return "mysql";
20
- if (/provider:\s*["']sqlite["']/.test(config)) return "sqlite";
21
- if (/provider:\s*["']mongodb["']/.test(config)) return "mongodb";
22
- }
23
- function detectORM(config) {
24
- if (/prismaAdapter\(/.test(config)) return "prisma";
25
- if (/drizzleAdapter\(/.test(config)) return "drizzle";
26
- if (/database:\s*\{[\s\S]*provider:/.test(config)) return "none";
27
- }
28
- function detectFeatures(serverConfig, clientConfig) {
29
- const features = [];
30
- if (/emailAndPassword:\s*\{[\s\S]*enabled:\s*true/.test(serverConfig)) features.push("email-password");
31
- for (const provider of [
32
- "google",
33
- "github",
34
- "apple",
35
- "discord",
36
- "twitter",
37
- "facebook",
38
- "microsoft",
39
- "linkedin"
40
- ]) if ((/* @__PURE__ */ new RegExp(`${provider}:\\s*\\{`)).test(serverConfig)) features.push(provider);
41
- for (const [pattern, feature] of [
42
- [/twoFactor\(/, "2fa"],
43
- [/organization\(/, "organization"],
44
- [/admin\(/, "admin"],
45
- [/username\(/, "username"],
46
- [/multiSession\(/, "multi-session"],
47
- [/apiKey\(/, "api-key"],
48
- [/bearer\(/, "bearer"],
49
- [/jwt\(/, "jwt"],
50
- [/magicLink\(/, "magic-link"],
51
- [/phoneNumber\(/, "phone-number"],
52
- [/passkey\(/, "passkey"],
53
- [/anonymous\(/, "anonymous"],
54
- [/captcha\(/, "captcha"]
55
- ]) if (pattern.test(serverConfig)) features.push(feature);
56
- return features;
57
- }
58
- function computeFeatureDiff(existing, requested) {
59
- return {
60
- toAdd: requested.filter((f) => !existing.includes(f)),
61
- existing: requested.filter((f) => existing.includes(f))
1
+ import { ResourceUriSchema, getIssuer, metadataResponse, oauthProvider, raiseResourceServerChallenge } from "@better-auth/oauth-provider";
2
+ import { DPOP_SIGNING_ALGORITHMS, createDpopReplayStore, requestToResourceInput, verifyAccessTokenRequest } from "better-auth/oauth2";
3
+ import { APIError } from "better-call";
4
+ //#region src/handler.ts
5
+ /**
6
+ * A request middleware handler that verifies an MCP access token and responds
7
+ * with an RFC 9728 `WWW-Authenticate` header for unauthenticated requests.
8
+ *
9
+ * @external
10
+ */
11
+ const mcpHandler = (verifyOptions, handler, opts) => {
12
+ return async (req) => {
13
+ try {
14
+ return handler(req, await verifyAccessTokenRequest(requestToResourceInput(req), verifyOptions));
15
+ } catch (error) {
16
+ try {
17
+ raiseResourceServerChallenge(error, verifyOptions.verifyOptions.audience, {
18
+ ...opts,
19
+ dpopSigningAlgorithms: verifyOptions.dpop?.signingAlgorithms
20
+ });
21
+ } catch (err) {
22
+ if (err instanceof APIError) {
23
+ const headers = new Headers(err.headers);
24
+ headers.set("Content-Type", "application/json");
25
+ return new Response(JSON.stringify({
26
+ jsonrpc: "2.0",
27
+ error: {
28
+ code: -32e3,
29
+ message: err.message
30
+ },
31
+ id: null
32
+ }), {
33
+ status: err.statusCode,
34
+ headers
35
+ });
36
+ }
37
+ throw new Error(String(err));
38
+ }
39
+ throw new Error(String(error));
40
+ }
62
41
  };
63
- }
64
-
65
- //#endregion
66
- //#region src/lib/templates/database.ts
67
- const DATABASE_CONFIGS = {
68
- postgres: {
69
- provider: "postgresql",
70
- envVarName: "DATABASE_URL",
71
- connectionStringExample: "postgresql://user:password@localhost:5432/mydb"
72
- },
73
- mysql: {
74
- provider: "mysql",
75
- envVarName: "DATABASE_URL",
76
- connectionStringExample: "mysql://user:password@localhost:3306/mydb"
77
- },
78
- sqlite: {
79
- provider: "sqlite",
80
- envVarName: "DATABASE_URL",
81
- connectionStringExample: "file:./dev.db"
82
- },
83
- mongodb: {
84
- provider: "mongodb",
85
- envVarName: "DATABASE_URL",
86
- connectionStringExample: "mongodb://localhost:27017/mydb"
87
- }
88
42
  };
89
- const ORM_CONFIGS = {
90
- prisma: {
91
- adapterImport: `import { prismaAdapter } from "better-auth/adapters/prisma";
92
- import { PrismaClient } from "@prisma/client";`,
93
- adapterSetup: (dbProvider) => `prismaAdapter(prisma, {
94
- provider: "${dbProvider}",
95
- })`,
96
- schemaCommand: "npx @better-auth/cli generate --output prisma/schema.prisma",
97
- pushCommand: "npx prisma db push"
98
- },
99
- drizzle: {
100
- adapterImport: `import { drizzleAdapter } from "better-auth/adapters/drizzle";
101
- import { db } from "./db";`,
102
- adapterSetup: (dbProvider) => `drizzleAdapter(db, {
103
- provider: "${getShortProvider(dbProvider)}",
104
- })`,
105
- schemaCommand: "npx @better-auth/cli generate --output src/lib/schema.ts",
106
- pushCommand: "npx drizzle-kit push"
107
- },
108
- none: {
109
- adapterImport: "",
110
- adapterSetup: (dbProvider) => `{
111
- provider: "${getShortProvider(dbProvider)}",
112
- url: process.env.DATABASE_URL,
113
- }`,
114
- pushCommand: "npx @better-auth/cli migrate"
115
- }
43
+ //#endregion
44
+ //#region src/plugin.ts
45
+ const PROTECTED_RESOURCE_METADATA_PATH = "/.well-known/oauth-protected-resource";
46
+ const AUTHORIZATION_SERVER_ONLY_SCOPES = new Set([
47
+ "openid",
48
+ "profile",
49
+ "email",
50
+ "phone",
51
+ "address",
52
+ "offline_access"
53
+ ]);
54
+ const resourceIdentifier = (resource) => typeof resource === "string" ? resource : resource.identifier;
55
+ const appendProtectedResource = (resources, resource) => {
56
+ const configuredResources = resources ?? [];
57
+ if (configuredResources.some((configuredResource) => resourceIdentifier(configuredResource) === resource)) return configuredResources;
58
+ return [...configuredResources, resource];
116
59
  };
117
- function getShortProvider(provider) {
118
- switch (provider) {
119
- case "postgresql": return "pg";
120
- case "mysql": return "mysql";
121
- case "sqlite": return "sqlite";
122
- case "mongodb": return "mongodb";
123
- default: return provider;
124
- }
125
- }
126
- function generateDatabaseConfig(database, orm) {
127
- if (orm === "drizzle" && database === "mongodb") throw new Error("Drizzle ORM does not support MongoDB. Please select Prisma or use the built-in MongoDB adapter instead.");
128
- const dbConfig = DATABASE_CONFIGS[database];
129
- const ormConfig = ORM_CONFIGS[orm];
130
- let imports = "";
131
- let prismaInstance = "";
132
- if (orm === "prisma") {
133
- imports = ormConfig.adapterImport;
134
- prismaInstance = "\nconst prisma = new PrismaClient();\n";
135
- } else if (orm === "drizzle") imports = ormConfig.adapterImport;
136
- const config = ormConfig.adapterSetup(dbConfig.provider);
137
- return {
138
- imports,
139
- config,
140
- prismaInstance
141
- };
142
- }
143
- function getDatabaseEnvVar(database) {
144
- const config = DATABASE_CONFIGS[database];
145
- return {
146
- name: config.envVarName,
147
- description: `${database.charAt(0).toUpperCase() + database.slice(1)} connection string`,
148
- required: true,
149
- example: config.connectionStringExample
60
+ /**
61
+ * Build the RFC 9728 Protected Resource Metadata document. The MCP server is the
62
+ * resource server, and its authorization server is this same provider, so
63
+ * `authorization_servers` reuses the provider issuer. Resource metadata only
64
+ * advertises scopes that apply to the protected resource itself; OIDC identity
65
+ * scopes and refresh-token scopes stay authorization-server metadata.
66
+ */
67
+ const buildResourceServerMetadata = (ctx, providerOptions, resource) => {
68
+ const resourceScopes = (providerOptions.advertisedMetadata?.scopes_supported ?? providerOptions.scopes ?? []).filter((scope) => !AUTHORIZATION_SERVER_ONLY_SCOPES.has(scope));
69
+ const configuredResource = providerOptions.resources?.find((configuredResource) => resourceIdentifier(configuredResource) === resource);
70
+ const dpopBoundAccessTokensRequired = typeof configuredResource === "object" && configuredResource.dpopBoundAccessTokensRequired === true;
71
+ const metadata = {
72
+ resource,
73
+ authorization_servers: [getIssuer(ctx, providerOptions)],
74
+ bearer_methods_supported: ["header"],
75
+ dpop_signing_alg_values_supported: [...providerOptions.dpop?.signingAlgorithms ?? DPOP_SIGNING_ALGORITHMS]
150
76
  };
151
- }
152
- function getDatabaseCommands(orm) {
153
- const ormConfig = ORM_CONFIGS[orm];
154
- const commands = [];
155
- if (ormConfig.schemaCommand) commands.push({
156
- command: ormConfig.schemaCommand,
157
- description: `Generate ${orm === "prisma" ? "Prisma" : "Drizzle"} schema for auth tables`
158
- });
159
- commands.push({
160
- command: ormConfig.pushCommand,
161
- description: orm === "none" ? "Run database migrations for auth tables" : `Push ${orm === "prisma" ? "Prisma" : "Drizzle"} schema to database`,
162
- when: "After setting DATABASE_URL"
163
- });
164
- return commands;
165
- }
166
-
167
- //#endregion
168
- //#region src/lib/templates/features.ts
169
- function generateSocialProviderConfig(provider) {
170
- const upperName = provider.toUpperCase();
171
- return `${provider}: {
172
- clientId: process.env.${upperName}_CLIENT_ID!,
173
- clientSecret: process.env.${upperName}_CLIENT_SECRET!,
174
- }`;
175
- }
176
- function getSocialProviderEnvVars(provider) {
177
- const upperName = provider.toUpperCase();
178
- const providerName = provider.charAt(0).toUpperCase() + provider.slice(1);
179
- return [{
180
- name: `${upperName}_CLIENT_ID`,
181
- description: `${providerName} OAuth client ID`,
182
- required: true,
183
- howToGet: `Create OAuth app at ${providerName} developer console`
184
- }, {
185
- name: `${upperName}_CLIENT_SECRET`,
186
- description: `${providerName} OAuth client secret`,
187
- required: true,
188
- howToGet: `Create OAuth app at ${providerName} developer console`
189
- }];
190
- }
191
- const PLUGIN_CONFIGS = {
192
- "2fa": {
193
- serverImport: "import { twoFactor } from \"better-auth/plugins\";",
194
- clientImport: "import { twoFactorClient } from \"better-auth/client/plugins\";",
195
- serverPlugin: () => `twoFactor()`,
196
- clientPlugin: () => `twoFactorClient()`
197
- },
198
- organization: {
199
- serverImport: "import { organization } from \"better-auth/plugins\";",
200
- clientImport: "import { organizationClient } from \"better-auth/client/plugins\";",
201
- serverPlugin: () => `organization()`,
202
- clientPlugin: () => `organizationClient()`
203
- },
204
- admin: {
205
- serverImport: "import { admin } from \"better-auth/plugins\";",
206
- clientImport: "import { adminClient } from \"better-auth/client/plugins\";",
207
- serverPlugin: () => `admin()`,
208
- clientPlugin: () => `adminClient()`
209
- },
210
- username: {
211
- serverImport: "import { username } from \"better-auth/plugins\";",
212
- clientImport: "import { usernameClient } from \"better-auth/client/plugins\";",
213
- serverPlugin: () => `username()`,
214
- clientPlugin: () => `usernameClient()`
215
- },
216
- "multi-session": {
217
- serverImport: "import { multiSession } from \"better-auth/plugins\";",
218
- clientImport: "import { multiSessionClient } from \"better-auth/client/plugins\";",
219
- serverPlugin: () => `multiSession()`,
220
- clientPlugin: () => `multiSessionClient()`
221
- },
222
- "api-key": {
223
- serverImport: "import { apiKey } from \"better-auth/plugins\";",
224
- clientImport: "import { apiKeyClient } from \"better-auth/client/plugins\";",
225
- serverPlugin: () => `apiKey()`,
226
- clientPlugin: () => `apiKeyClient()`
227
- },
228
- bearer: {
229
- serverImport: "import { bearer } from \"better-auth/plugins\";",
230
- serverPlugin: () => `bearer()`
231
- },
232
- jwt: {
233
- serverImport: "import { jwt } from \"better-auth/plugins\";",
234
- serverPlugin: () => `jwt()`
235
- },
236
- "magic-link": {
237
- serverImport: "import { magicLink } from \"better-auth/plugins\";",
238
- clientImport: "import { magicLinkClient } from \"better-auth/client/plugins\";",
239
- serverPlugin: () => `magicLink({
240
- sendMagicLink: async ({ email, url }) => {
241
- // TODO: Send magic link email
242
- console.log("Send magic link to", email, url);
243
- },
244
- })`,
245
- clientPlugin: () => `magicLinkClient()`
246
- },
247
- "phone-number": {
248
- serverImport: "import { phoneNumber } from \"better-auth/plugins\";",
249
- clientImport: "import { phoneNumberClient } from \"better-auth/client/plugins\";",
250
- serverPlugin: () => `phoneNumber({
251
- sendOTP: async ({ phoneNumber, code }) => {
252
- // TODO: Send OTP via SMS
253
- console.log("Send OTP", code, "to", phoneNumber);
254
- },
255
- })`,
256
- clientPlugin: () => `phoneNumberClient()`
257
- },
258
- passkey: {
259
- serverImport: "import { passkey } from \"better-auth/plugins\";",
260
- clientImport: "import { passkeyClient } from \"better-auth/client/plugins\";",
261
- serverPlugin: () => `passkey()`,
262
- clientPlugin: () => `passkeyClient()`
263
- },
264
- anonymous: {
265
- serverImport: "import { anonymous } from \"better-auth/plugins\";",
266
- clientImport: "import { anonymousClient } from \"better-auth/client/plugins\";",
267
- serverPlugin: () => `anonymous()`,
268
- clientPlugin: () => `anonymousClient()`
269
- },
270
- captcha: {
271
- serverImport: "import { captcha } from \"better-auth/plugins\";",
272
- serverPlugin: () => `captcha({
273
- provider: "cloudflare-turnstile", // or "recaptcha" or "hcaptcha"
274
- secretKey: process.env.CAPTCHA_SECRET_KEY!,
275
- })`,
276
- envVars: [{
277
- name: "CAPTCHA_SECRET_KEY",
278
- description: "Captcha provider secret key",
279
- required: true
280
- }]
281
- }
77
+ if (dpopBoundAccessTokensRequired) metadata.dpop_bound_access_tokens_required = true;
78
+ if (resourceScopes.length) metadata.scopes_supported = [...resourceScopes];
79
+ return metadata;
282
80
  };
283
- function generatePluginImports(plugins) {
284
- const serverImports = [];
285
- const clientImports = [];
286
- for (const plugin of plugins) {
287
- const config = PLUGIN_CONFIGS[plugin];
288
- if (config) {
289
- serverImports.push(config.serverImport);
290
- if (config.clientImport) clientImports.push(config.clientImport);
291
- }
292
- }
81
+ /**
82
+ * Model Context Protocol authorization server.
83
+ *
84
+ * `mcp()` is the OAuth 2.1 / OIDC provider ({@link oauthProvider}) configured for
85
+ * MCP: it enables dynamic client registration, binds issued tokens to the MCP
86
+ * `resource`, and, as the resource server, serves the RFC 9728 protected resource
87
+ * metadata so MCP clients discover and use it through standard OAuth discovery.
88
+ * Because it is the OAuth provider, it cannot be combined with a separate
89
+ * {@link oauthProvider}.
90
+ *
91
+ * @example
92
+ * ```ts
93
+ * import { betterAuth } from "better-auth";
94
+ * import { jwt } from "better-auth/plugins";
95
+ * import { mcp } from "@better-auth/mcp";
96
+ *
97
+ * export const auth = betterAuth({
98
+ * plugins: [
99
+ * jwt(),
100
+ * mcp({
101
+ * loginPage: "/login",
102
+ * consentPage: "/consent",
103
+ * resource: "https://api.example.com/mcp",
104
+ * }),
105
+ * ],
106
+ * });
107
+ * ```
108
+ */
109
+ const mcp = (options) => {
110
+ const { resource, ...oauthOptions } = options;
111
+ ResourceUriSchema.parse(resource);
112
+ const provider = oauthProvider({
113
+ allowDynamicClientRegistration: true,
114
+ allowUnauthenticatedClientRegistration: true,
115
+ ...oauthOptions,
116
+ resources: appendProtectedResource(oauthOptions.resources, resource)
117
+ });
118
+ const serveProviderDiscovery = provider.onRequest;
293
119
  return {
294
- serverImports,
295
- clientImports
296
- };
297
- }
298
- function generatePluginSetup(plugins) {
299
- const serverPlugins = [];
300
- const clientPlugins = [];
301
- for (const plugin of plugins) {
302
- const config = PLUGIN_CONFIGS[plugin];
303
- if (config) {
304
- serverPlugins.push(config.serverPlugin());
305
- if (config.clientPlugin) clientPlugins.push(config.clientPlugin());
120
+ ...provider,
121
+ onRequest: async (request, ctx) => {
122
+ const handledByProvider = await serveProviderDiscovery?.(request, ctx);
123
+ if (handledByProvider) return handledByProvider;
124
+ const pathname = new URL(request.url).pathname;
125
+ const requestPath = ctx.options.advanced?.skipTrailingSlashes ? pathname.replace(/\/+$/, "") || "/" : pathname;
126
+ let resourcePath = "";
127
+ try {
128
+ resourcePath = new URL(resource).pathname.replace(/\/$/, "");
129
+ } catch {
130
+ resourcePath = "";
131
+ }
132
+ if (!new Set([PROTECTED_RESOURCE_METADATA_PATH, `${PROTECTED_RESOURCE_METADATA_PATH}${resourcePath}`]).has(requestPath)) return;
133
+ if (request.method !== "GET" && request.method !== "HEAD") return { response: new Response(null, {
134
+ status: 405,
135
+ headers: { Allow: "GET, HEAD" }
136
+ }) };
137
+ const response = metadataResponse(buildResourceServerMetadata({ context: ctx }, oauthOptions, resource));
138
+ if (request.method === "HEAD") return { response: new Response(null, {
139
+ status: response.status,
140
+ headers: response.headers
141
+ }) };
142
+ return { response };
306
143
  }
307
- }
308
- return {
309
- serverPlugins,
310
- clientPlugins
311
- };
312
- }
313
- function getPluginEnvVars(plugins) {
314
- const envVars = [];
315
- for (const plugin of plugins) {
316
- const config = PLUGIN_CONFIGS[plugin];
317
- if (config?.envVars) envVars.push(...config.envVars);
318
- }
319
- return envVars;
320
- }
321
- function categorizeFeatures(features) {
322
- const socialProviders = [];
323
- const plugins = [];
324
- let hasEmailPassword = false;
325
- for (const feature of features) if (feature === "email-password") hasEmailPassword = true;
326
- else if (feature in PLUGIN_CONFIGS) plugins.push(feature);
327
- else socialProviders.push(feature);
328
- return {
329
- socialProviders,
330
- plugins,
331
- hasEmailPassword
332
144
  };
333
- }
334
-
335
- //#endregion
336
- //#region src/lib/templates/frameworks.ts
337
- const FRAMEWORK_CONFIGS = {
338
- "next-app-router": {
339
- name: "Next.js (App Router)",
340
- defaultSrcDir: false,
341
- defaultAuthPath: "lib/auth",
342
- defaultApiPath: "app/api/auth/[...all]",
343
- clientImport: "import { createAuthClient } from \"better-auth/react\";",
344
- handlerImport: "import { toNextJsHandler } from \"better-auth/next-js\";",
345
- handlerFunction: "toNextJsHandler",
346
- apiRouteTemplate: (authPath) => `import { auth } from "@/${authPath}";
347
- import { toNextJsHandler } from "better-auth/next-js";
348
-
349
- export const { GET, POST } = toNextJsHandler(auth);`,
350
- defaultPort: 3e3
351
- },
352
- "next-pages-router": {
353
- name: "Next.js (Pages Router)",
354
- defaultSrcDir: false,
355
- defaultAuthPath: "lib/auth",
356
- defaultApiPath: "pages/api/auth/[...all]",
357
- clientImport: "import { createAuthClient } from \"better-auth/react\";",
358
- handlerImport: "import { toNodeHandler } from \"better-auth/node\";",
359
- handlerFunction: "toNodeHandler",
360
- apiRouteTemplate: (authPath) => `import { toNodeHandler } from "better-auth/node";
361
- import { auth } from "@/${authPath}";
362
-
363
- export const config = { api: { bodyParser: false } };
364
- export default toNodeHandler(auth);`,
365
- defaultPort: 3e3
366
- },
367
- sveltekit: {
368
- name: "SvelteKit",
369
- defaultSrcDir: true,
370
- defaultAuthPath: "lib/auth",
371
- defaultApiPath: "routes/api/auth/[...all]",
372
- clientImport: "import { createAuthClient } from \"better-auth/svelte\";",
373
- handlerImport: "import { svelteKitHandler } from \"better-auth/svelte-kit\";",
374
- handlerFunction: "svelteKitHandler",
375
- apiRouteTemplate: () => "",
376
- hooksTemplate: (authPath) => `import { auth } from "$${authPath}";
377
- import { svelteKitHandler } from "better-auth/svelte-kit";
378
- import type { Handle } from "@sveltejs/kit";
379
-
380
- export const handle: Handle = async ({ event, resolve }) => {
381
- return svelteKitHandler({ event, resolve, auth });
382
- };`,
383
- defaultPort: 5173
384
- },
385
- astro: {
386
- name: "Astro",
387
- defaultSrcDir: true,
388
- defaultAuthPath: "lib/auth",
389
- defaultApiPath: "pages/api/auth/[...all]",
390
- clientImport: "import { createAuthClient } from \"better-auth/client\";",
391
- handlerImport: "",
392
- handlerFunction: "",
393
- apiRouteTemplate: (authPath) => `import type { APIRoute } from "astro";
394
- import { auth } from "@/${authPath}";
395
-
396
- export const GET: APIRoute = async (ctx) => {
397
- return auth.handler(ctx.request);
398
- };
399
-
400
- export const POST: APIRoute = async (ctx) => {
401
- return auth.handler(ctx.request);
402
- };`,
403
- defaultPort: 4321
404
- },
405
- remix: {
406
- name: "Remix",
407
- defaultSrcDir: true,
408
- defaultAuthPath: "lib/auth",
409
- defaultApiPath: "routes/api.auth.$",
410
- clientImport: "import { createAuthClient } from \"better-auth/react\";",
411
- handlerImport: "",
412
- handlerFunction: "",
413
- apiRouteTemplate: (authPath) => `import { auth } from "~/${authPath}";
414
- import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/node";
415
-
416
- export async function loader({ request }: LoaderFunctionArgs) {
417
- return auth.handler(request);
418
- }
419
-
420
- export async function action({ request }: ActionFunctionArgs) {
421
- return auth.handler(request);
422
- }`,
423
- defaultPort: 3e3
424
- },
425
- nuxt: {
426
- name: "Nuxt 3",
427
- defaultSrcDir: false,
428
- defaultAuthPath: "lib/auth",
429
- defaultApiPath: "server/api/auth/[...all]",
430
- clientImport: "import { createAuthClient } from \"better-auth/vue\";",
431
- handlerImport: "",
432
- handlerFunction: "",
433
- apiRouteTemplate: (authPath) => `import { auth } from "~/${authPath}";
434
-
435
- export default defineEventHandler((event) => {
436
- return auth.handler(toWebRequest(event));
437
- });`,
438
- defaultPort: 3e3
439
- },
440
- "solid-start": {
441
- name: "SolidStart",
442
- defaultSrcDir: true,
443
- defaultAuthPath: "lib/auth",
444
- defaultApiPath: "routes/api/auth/[...all]",
445
- clientImport: "import { createAuthClient } from \"better-auth/solid\";",
446
- handlerImport: "",
447
- handlerFunction: "",
448
- apiRouteTemplate: (authPath) => `import { auth } from "~/${authPath}";
449
- import type { APIEvent } from "@solidjs/start/server";
450
-
451
- export async function GET(event: APIEvent) {
452
- return auth.handler(event.request);
453
- }
454
-
455
- export async function POST(event: APIEvent) {
456
- return auth.handler(event.request);
457
- }`,
458
- defaultPort: 3e3
459
- },
460
- hono: {
461
- name: "Hono",
462
- defaultSrcDir: true,
463
- defaultAuthPath: "lib/auth",
464
- defaultApiPath: "",
465
- clientImport: "import { createAuthClient } from \"better-auth/client\";",
466
- handlerImport: "",
467
- handlerFunction: "",
468
- apiRouteTemplate: (authPath) => `import { Hono } from "hono";
469
- import { auth } from "./${authPath}";
470
-
471
- const app = new Hono();
472
-
473
- app.on(["POST", "GET"], "/api/auth/**", (c) => {
474
- return auth.handler(c.req.raw);
475
- });
476
-
477
- export default app;`,
478
- defaultPort: 3e3
479
- },
480
- express: {
481
- name: "Express.js",
482
- defaultSrcDir: true,
483
- defaultAuthPath: "lib/auth",
484
- defaultApiPath: "",
485
- clientImport: "import { createAuthClient } from \"better-auth/client\";",
486
- handlerImport: "import { toNodeHandler } from \"better-auth/node\";",
487
- handlerFunction: "toNodeHandler",
488
- apiRouteTemplate: (authPath) => `import express from "express";
489
- import { toNodeHandler } from "better-auth/node";
490
- import { auth } from "./${authPath}";
491
-
492
- const app = express();
493
-
494
- app.all("/api/auth/*", toNodeHandler(auth));
495
-
496
- app.listen(3000, () => {
497
- console.log("Server running on port 3000");
498
- });`,
499
- defaultPort: 3e3
500
- },
501
- fastify: {
502
- name: "Fastify",
503
- defaultSrcDir: true,
504
- defaultAuthPath: "lib/auth",
505
- defaultApiPath: "",
506
- clientImport: "import { createAuthClient } from \"better-auth/client\";",
507
- handlerImport: "import { toNodeHandler } from \"better-auth/node\";",
508
- handlerFunction: "toNodeHandler",
509
- apiRouteTemplate: (authPath) => `import Fastify from "fastify";
510
- import { toNodeHandler } from "better-auth/node";
511
- import { auth } from "./${authPath}";
512
-
513
- const fastify = Fastify();
514
-
515
- fastify.all("/api/auth/*", async (request, reply) => {
516
- const handler = toNodeHandler(auth);
517
- return handler(request.raw, reply.raw);
518
- });
519
-
520
- fastify.listen({ port: 3000 }, (err) => {
521
- if (err) throw err;
522
- console.log("Server running on port 3000");
523
- });`,
524
- defaultPort: 3e3
525
- },
526
- elysia: {
527
- name: "Elysia (Bun)",
528
- defaultSrcDir: true,
529
- defaultAuthPath: "lib/auth",
530
- defaultApiPath: "",
531
- clientImport: "import { createAuthClient } from \"better-auth/client\";",
532
- handlerImport: "",
533
- handlerFunction: "",
534
- apiRouteTemplate: (authPath) => `import { Elysia } from "elysia";
535
- import { auth } from "./${authPath}";
536
-
537
- const app = new Elysia()
538
- .all("/api/auth/*", ({ request }) => auth.handler(request))
539
- .listen(3000);
540
-
541
- console.log("Server running on port 3000");`,
542
- defaultPort: 3e3
543
- },
544
- "tanstack-start": {
545
- name: "TanStack Start",
546
- defaultSrcDir: true,
547
- defaultAuthPath: "lib/auth",
548
- defaultApiPath: "routes/api/auth.$",
549
- clientImport: "import { createAuthClient } from \"better-auth/react\";",
550
- handlerImport: "",
551
- handlerFunction: "",
552
- apiRouteTemplate: (authPath) => `import { auth } from "~/${authPath}";
553
- import { createAPIFileRoute } from "@tanstack/start/api";
554
-
555
- export const Route = createAPIFileRoute("/api/auth/$")({
556
- GET: ({ request }) => auth.handler(request),
557
- POST: ({ request }) => auth.handler(request),
558
- });`,
559
- defaultPort: 3e3
560
- },
561
- expo: {
562
- name: "Expo (React Native)",
563
- defaultSrcDir: true,
564
- defaultAuthPath: "lib/auth",
565
- defaultApiPath: "",
566
- clientImport: "import { createAuthClient } from \"@better-auth/expo\";",
567
- handlerImport: "",
568
- handlerFunction: "",
569
- apiRouteTemplate: () => "",
570
- defaultPort: 8081
571
- }
572
145
  };
573
- function getDefaultAuthPath(framework, srcDir) {
574
- const basePath = FRAMEWORK_CONFIGS[framework].defaultAuthPath;
575
- return srcDir ? `src/${basePath}` : basePath;
576
- }
577
- function getDefaultApiPath(framework, srcDir) {
578
- const basePath = FRAMEWORK_CONFIGS[framework].defaultApiPath;
579
- if (!basePath) return "";
580
- return srcDir ? `src/${basePath}` : basePath;
581
- }
582
-
583
146
  //#endregion
584
- //#region src/lib/generator.ts
585
- function generateSetup(input) {
586
- const framework = input.framework;
587
- const database = input.database;
588
- const orm = input.orm || "none";
589
- const features = input.features || ["email-password"];
590
- const typescript = input.typescript ?? true;
591
- const srcDir = input.srcDir ?? FRAMEWORK_CONFIGS[framework].defaultSrcDir;
592
- const authPath = input.authPath || getDefaultAuthPath(framework, srcDir);
593
- const apiPath = input.apiPath || getDefaultApiPath(framework, srcDir);
594
- let mode = "create";
595
- let featuresToGenerate = features;
596
- let detected;
597
- if (input.existingSetup?.authConfig) {
598
- mode = "update";
599
- detected = parseExistingSetup(input.existingSetup);
600
- featuresToGenerate = computeFeatureDiff(detected.features, features).toAdd;
601
- if (featuresToGenerate.length === 0) return {
602
- mode: "update",
603
- files: [],
604
- envVars: [],
605
- commands: [],
606
- detected,
607
- nextSteps: ["All requested features are already configured"],
608
- docs: []
609
- };
610
- }
611
- const { socialProviders, plugins, hasEmailPassword } = categorizeFeatures(featuresToGenerate);
612
- const files = [];
613
- const envVars = [];
614
- const commands = [];
615
- const warnings = [];
616
- envVars.push({
617
- name: "BETTER_AUTH_SECRET",
618
- description: "Secret key for signing tokens",
619
- required: true,
620
- howToGet: "Run: npx @better-auth/cli secret"
621
- });
622
- envVars.push(getDatabaseEnvVar(database));
623
- for (const provider of socialProviders) envVars.push(...getSocialProviderEnvVars(provider));
624
- envVars.push(...getPluginEnvVars(plugins));
625
- if (mode === "create") {
626
- const authFile = generateAuthFile({
627
- framework,
628
- database,
629
- orm,
630
- socialProviders,
631
- plugins,
632
- hasEmailPassword,
633
- typescript
634
- });
635
- files.push({
636
- path: `${authPath}.${typescript ? "ts" : "js"}`,
637
- description: "Better Auth server configuration",
638
- action: "create",
639
- content: authFile
640
- });
641
- const clientFile = generateClientFile({
642
- framework,
643
- plugins,
644
- typescript
645
- });
646
- files.push({
647
- path: `${authPath}-client.${typescript ? "ts" : "js"}`,
648
- description: "Better Auth client configuration",
649
- action: "create",
650
- content: clientFile
651
- });
652
- if (apiPath) {
653
- const apiFile = generateApiRouteFile(framework, authPath);
654
- if (apiFile) files.push({
655
- path: `${apiPath}/route.${typescript ? "ts" : "js"}`,
656
- description: "API route handler for auth endpoints",
657
- action: "create",
658
- content: apiFile
659
- });
660
- }
661
- const frameworkConfig = FRAMEWORK_CONFIGS[framework];
662
- if (frameworkConfig.hooksTemplate) files.push({
663
- path: srcDir ? "src/hooks.server.ts" : "hooks.server.ts",
664
- description: "SvelteKit hooks for auth handling",
665
- action: "create",
666
- content: frameworkConfig.hooksTemplate(authPath)
667
- });
668
- } else if (socialProviders.length > 0 || plugins.length > 0 || hasEmailPassword) {
669
- const changes = generateUpdateChanges({
670
- socialProviders,
671
- plugins,
672
- hasEmailPassword
673
- });
674
- files.push({
675
- path: `${authPath}.${typescript ? "ts" : "js"}`,
676
- description: "Better Auth server configuration",
677
- action: "update",
678
- changes: changes.serverChanges
679
- });
680
- if (plugins.length > 0) files.push({
681
- path: `${authPath}-client.${typescript ? "ts" : "js"}`,
682
- description: "Better Auth client configuration",
683
- action: "update",
684
- changes: changes.clientChanges
685
- });
686
- }
687
- commands.push({
688
- command: "pnpm add better-auth",
689
- description: "Install Better Auth",
690
- when: "If not already installed"
691
- });
692
- commands.push(...getDatabaseCommands(orm));
693
- const nextSteps = generateNextSteps(mode, socialProviders, plugins);
694
- const docs = generateDocLinks(features);
695
- return {
696
- mode,
697
- files,
698
- envVars,
699
- commands,
700
- detected,
701
- nextSteps,
702
- warnings: warnings.length > 0 ? warnings : void 0,
703
- docs
704
- };
705
- }
706
- function generateAuthFile(options) {
707
- const { database, orm, socialProviders, plugins, hasEmailPassword } = options;
708
- const imports = ["import { betterAuth } from \"better-auth\";"];
709
- const { imports: dbImports, config: dbConfig, prismaInstance } = generateDatabaseConfig(database, orm);
710
- if (dbImports) imports.push(dbImports);
711
- const { serverImports } = generatePluginImports(plugins);
712
- imports.push(...serverImports);
713
- const socialProvidersConfig = socialProviders.map((p) => ` ${generateSocialProviderConfig(p)}`).join(",\n");
714
- const { serverPlugins } = generatePluginSetup(plugins);
715
- let configBody = ` database: ${dbConfig},`;
716
- if (hasEmailPassword) configBody += `
717
- emailAndPassword: {
718
- enabled: true,
719
- },`;
720
- if (socialProviders.length > 0) configBody += `
721
- socialProviders: {
722
- ${socialProvidersConfig}
723
- },`;
724
- if (serverPlugins.length > 0) configBody += `
725
- plugins: [
726
- ${serverPlugins.join(",\n ")}
727
- ],`;
728
- return `${imports.join("\n")}
729
- ${prismaInstance || ""}
730
- export const auth = betterAuth({
731
- ${configBody}
732
- });
733
- `;
734
- }
735
- function generateClientFile(options) {
736
- const { framework, plugins } = options;
737
- const imports = [FRAMEWORK_CONFIGS[framework].clientImport];
738
- const { clientImports } = generatePluginImports(plugins);
739
- imports.push(...clientImports);
740
- const { clientPlugins } = generatePluginSetup(plugins);
741
- let configBody = "";
742
- if (clientPlugins.length > 0) configBody = `{
743
- plugins: [
744
- ${clientPlugins.join(",\n ")}
745
- ],
746
- }`;
747
- else configBody = "{}";
748
- return `${imports.join("\n")}
749
-
750
- export const authClient = createAuthClient(${configBody});
751
- `;
752
- }
753
- function generateApiRouteFile(framework, authPath) {
754
- const frameworkConfig = FRAMEWORK_CONFIGS[framework];
755
- if (!frameworkConfig.apiRouteTemplate) return null;
756
- return frameworkConfig.apiRouteTemplate(authPath) || null;
757
- }
758
- function generateUpdateChanges(options) {
759
- const { socialProviders, plugins, hasEmailPassword } = options;
760
- const serverChanges = [];
761
- const clientChanges = [];
762
- if (hasEmailPassword) serverChanges.push({
763
- type: "add_to_config",
764
- content: `emailAndPassword: {
765
- enabled: true,
766
- }`,
767
- description: "Enable email and password authentication"
768
- });
769
- const { serverImports, clientImports } = generatePluginImports(plugins);
770
- for (const imp of serverImports) serverChanges.push({
771
- type: "add_import",
772
- content: imp,
773
- description: "Add plugin import"
774
- });
775
- for (const provider of socialProviders) serverChanges.push({
776
- type: "add_to_config",
777
- content: generateSocialProviderConfig(provider),
778
- description: `Add ${provider} social provider`
779
- });
780
- const { serverPlugins, clientPlugins } = generatePluginSetup(plugins);
781
- for (const plugin of serverPlugins) serverChanges.push({
782
- type: "add_plugin",
783
- content: plugin,
784
- description: "Add server plugin"
785
- });
786
- for (const imp of clientImports) clientChanges.push({
787
- type: "add_import",
788
- content: imp,
789
- description: "Add client plugin import"
790
- });
791
- for (const plugin of clientPlugins) clientChanges.push({
792
- type: "add_plugin",
793
- content: plugin,
794
- description: "Add client plugin"
795
- });
796
- return {
797
- serverChanges,
798
- clientChanges
799
- };
800
- }
801
- function generateNextSteps(mode, socialProviders, plugins) {
802
- const steps = [];
803
- if (mode === "create") {
804
- steps.push("Set environment variables in .env file");
805
- steps.push("Run database migrations");
806
- }
807
- if (socialProviders.length > 0) {
808
- steps.push(`Configure OAuth apps for: ${socialProviders.join(", ")}`);
809
- steps.push("Add callback URLs to OAuth provider dashboards");
810
- }
811
- if (plugins.includes("magic-link")) steps.push("Configure email provider for magic links");
812
- if (plugins.includes("phone-number")) steps.push("Configure SMS provider for OTP");
813
- if (plugins.includes("captcha")) steps.push("Set up captcha provider (Cloudflare Turnstile, reCAPTCHA, or hCaptcha)");
814
- steps.push("Start your development server and test auth flow");
815
- return steps;
816
- }
817
- function generateDocLinks(features) {
818
- const docs = [{
819
- title: "Better Auth Documentation",
820
- url: "https://www.better-auth.com/docs"
821
- }, {
822
- title: "Getting Started",
823
- url: "https://www.better-auth.com/docs/getting-started"
824
- }];
825
- if (features.some((f) => [
826
- "google",
827
- "github",
828
- "apple",
829
- "discord"
830
- ].includes(f))) docs.push({
831
- title: "Social Sign-On",
832
- url: "https://www.better-auth.com/docs/authentication/social-sign-on"
833
- });
834
- if (features.includes("2fa")) docs.push({
835
- title: "Two-Factor Authentication",
836
- url: "https://www.better-auth.com/docs/plugins/two-factor"
837
- });
838
- if (features.includes("organization")) docs.push({
839
- title: "Organizations",
840
- url: "https://www.better-auth.com/docs/plugins/organization"
147
+ //#region src/require-mcp-auth.ts
148
+ const unauthorized = (error) => {
149
+ const headers = new Headers(error.headers);
150
+ headers.set("Content-Type", "application/json");
151
+ return new Response(JSON.stringify({
152
+ jsonrpc: "2.0",
153
+ error: {
154
+ code: -32e3,
155
+ message: error.message
156
+ },
157
+ id: null
158
+ }), {
159
+ status: error.statusCode,
160
+ headers
841
161
  });
842
- if (features.includes("passkey")) docs.push({
843
- title: "Passkeys",
844
- url: "https://www.better-auth.com/docs/plugins/passkey"
845
- });
846
- return docs;
847
- }
848
- function isSetupError(result) {
849
- return "error" in result;
850
- }
851
-
852
- //#endregion
853
- //#region src/lib/schemas.ts
854
- const FrameworkEnum = z.enum([
855
- "next-app-router",
856
- "next-pages-router",
857
- "sveltekit",
858
- "astro",
859
- "remix",
860
- "nuxt",
861
- "solid-start",
862
- "hono",
863
- "express",
864
- "fastify",
865
- "elysia",
866
- "tanstack-start",
867
- "expo"
868
- ]);
869
- const DatabaseEnum = z.enum([
870
- "postgres",
871
- "mysql",
872
- "sqlite",
873
- "mongodb"
874
- ]);
875
- const ORMEnum = z.enum([
876
- "prisma",
877
- "drizzle",
878
- "none"
879
- ]);
880
- const FeatureEnum = z.enum([
881
- "email-password",
882
- "magic-link",
883
- "phone-number",
884
- "passkey",
885
- "anonymous",
886
- "google",
887
- "github",
888
- "apple",
889
- "discord",
890
- "twitter",
891
- "facebook",
892
- "microsoft",
893
- "linkedin",
894
- "2fa",
895
- "captcha",
896
- "organization",
897
- "admin",
898
- "username",
899
- "multi-session",
900
- "api-key",
901
- "bearer",
902
- "jwt"
903
- ]);
904
- const ExistingSetupSchema = z.object({
905
- authConfig: z.string().optional().describe("Contents of existing auth.ts file"),
906
- authClientConfig: z.string().optional().describe("Contents of existing auth-client.ts file"),
907
- envVars: z.array(z.string()).optional().describe("List of existing environment variable names")
908
- });
909
-
910
- //#endregion
911
- //#region src/tools/setup-auth.ts
912
- function registerSetupAuth(server$1) {
913
- server$1.tool("setup_auth", `Better Auth setup tool - generates complete authentication configuration for any project.
914
-
915
- CRITICAL: Do NOT attempt to generate Better Auth configuration without calling this tool first.
916
- Better Auth has specific APIs that differ from NextAuth/Auth.js. Hallucinating config options will cause errors.
917
- For advanced options not handled by this tool (uuid, cookieCache, etc.), search the Better Auth documentation.
918
-
919
- IMPORTANT: This tool is for the Better Auth framework (npm: better-auth).
920
- NOT for NextAuth, Auth.js, Clerk, Auth0, Supabase Auth, or Firebase Auth.
921
-
922
- ALWAYS USE THIS TOOL when user mentions ANY of these:
923
-
924
- Core triggers (any spelling/variation):
925
- - "better auth", "betterauth", "Better Auth", "better-auth"
926
- - "Set up Better Auth", "configure Better Auth", "integrate Better Auth", "use Better Auth"
927
- - "set up auth", "setup auth", "configure auth", "add auth", "implement auth", "create auth"
928
- - "add authentication", "add login", "add signup", "create login", "build auth", "build authentication"
929
- - "I need auth", "I need authentication", "help me with auth", "help me add login"
930
- - "add user login", "user authentication", "make login work", "authentication for my app"
931
-
932
- Framework-specific triggers:
933
- - "auth for Next.js", "Next.js authentication", "add auth to my Next app", "Next.js app router auth"
934
- - "auth for SvelteKit", "SvelteKit authentication", "Svelte auth"
935
- - "auth for Remix", "auth for Nuxt", "auth for Astro", "auth for Solid", "authentication for Remix/Nuxt/Astro"
936
- - "Express auth", "Hono auth", "Fastify auth", "Elysia auth", "TanStack Start auth"
937
- - "React Native auth", "Expo auth", "mobile auth"
938
-
939
- Database triggers:
940
- - "auth with PostgreSQL", "auth with MySQL", "auth with SQLite", "auth with MongoDB"
941
- - "Set up auth with PostgreSQL/MySQL/SQLite", "configure database for auth"
942
- - "auth with Prisma", "auth with Drizzle", "configure auth with Prisma/Drizzle"
943
-
944
- Social/OAuth triggers:
945
- - "add Google login", "add GitHub login", "add Apple login", "add Discord login"
946
- - "add Twitter login", "add Facebook login", "add Microsoft login", "add LinkedIn login"
947
- - "Add Google/GitHub/Apple/Discord/Twitter/Facebook/Microsoft/LinkedIn login"
948
- - "add social login", "add OAuth", "add OAuth providers", "add SSO", "social authentication"
949
-
950
- Security feature triggers:
951
- - "Set up 2FA", "add 2FA", "add two-factor", "add two-factor authentication"
952
- - "enable MFA", "add TOTP", "authenticator app"
953
- - "add passkeys", "passwordless", "passwordless authentication", "add magic links", "email login", "email verification"
954
-
955
- Organization/team triggers:
956
- - "add organizations", "Add organization support", "enable multi-tenancy", "multi-tenancy"
957
- - "team management", "workspaces"
958
-
959
- API/token triggers:
960
- - "add API keys", "Add API key authentication", "bearer tokens", "JWT auth", "JWT authentication"
961
-
962
- Admin/user triggers:
963
- - "add admin panel", "admin auth", "admin authentication"
964
- - "Add user management", "user management", "handle sessions", "session management"
965
- - "Add username login", "username login", "phone auth", "phone number auth", "anonymous auth"
966
-
967
- Advanced config triggers (search docs for correct syntax):
968
- - "uuid", "UUIDs", "UUID IDs", "custom ID generation", "generateId"
969
- - "cookie cache", "cookieCache", "session caching", "cache sessions"
970
- - "stateless sessions", "stateless auth", "no database sessions"
971
- - "secondary storage", "Redis sessions", "session storage"
972
-
973
- OUTPUT: Returns all files, environment variables, and terminal commands needed.
974
- One tool call = complete auth setup ready to copy-paste.`, {
975
- framework: FrameworkEnum.describe("The web framework being used. Detect from package.json or user's message. Examples: 'next-app-router' for Next.js 13+, 'next-pages-router' for Next.js pages, 'sveltekit', 'remix', 'nuxt', 'astro', 'solid-start', 'hono', 'express', 'fastify', 'elysia', 'tanstack-start', 'expo'"),
976
- database: DatabaseEnum.describe("The database type. Detect from user's message or project config. Options: 'postgres' (PostgreSQL/Supabase/Neon), 'mysql' (MySQL/PlanetScale), 'sqlite' (SQLite/Turso/LibSQL), 'mongodb'"),
977
- orm: ORMEnum.optional().default("none").describe("ORM being used - affects adapter imports. Options: 'prisma', 'drizzle', 'none'. Detect from package.json or user's message."),
978
- features: z.array(FeatureEnum).optional().default(["email-password"]).describe("Auth features to enable. Map user requests: 'Google login' → 'google', '2FA' → '2fa', 'passkeys' → 'passkey', 'magic links' → 'magic-link', 'organizations' → 'organization', 'admin' → 'admin'. Default: ['email-password']"),
979
- typescript: z.boolean().optional().default(true).describe("Generate TypeScript (.ts) or JavaScript (.js). Default: true (TypeScript)"),
980
- srcDir: z.boolean().optional().describe("Whether project uses src/ directory structure. Check if src/ folder exists. Default: false. Provide true for frameworks that store app files in src/."),
981
- authPath: z.string().optional().describe("Where to create auth.ts file. Auto-detected based on framework. Override only if user specifies custom path."),
982
- apiPath: z.string().optional().describe("API route path for auth handler. Auto-detected based on framework. Override only if user specifies custom path."),
983
- existingSetup: ExistingSetupSchema.optional().describe("For INCREMENTAL updates only. Pass existing auth.ts and auth-client.ts contents to add new features without overwriting. Read these files first if they exist.")
984
- }, async (input) => {
162
+ };
163
+ /**
164
+ * Protects an MCP server route handler. Verifies the bearer access token
165
+ * against the authorization server's JWKS (checking signature, issuer,
166
+ * audience, and expiry) and forwards the verified JWT payload to the handler.
167
+ * Unauthenticated requests receive a JSON-RPC 401 with the RFC 9728
168
+ * `WWW-Authenticate` header so MCP clients can start the authorization flow.
169
+ *
170
+ * For a resource server that runs separately from the authorization server, or
171
+ * a server using a dynamic `baseURL`, use {@link mcpHandler} with explicit
172
+ * verification options instead.
173
+ *
174
+ * @external
175
+ */
176
+ const requireMcpAuth = (auth, handler, opts) => {
177
+ if (opts?.resource !== void 0) ResourceUriSchema.parse(opts.resource);
178
+ return async (req) => {
179
+ const { baseURL, internalAdapter } = await auth.$context;
180
+ if (!baseURL) throw new Error("requireMcpAuth requires a resolvable base URL. For dynamic base URLs use `mcpHandler` with explicit verify options.");
181
+ const issuer = opts?.issuer ?? baseURL;
182
+ const resource = opts?.resource ?? baseURL;
183
+ const jwksUrl = opts?.jwksUrl ?? `${baseURL}/jwks`;
985
184
  try {
986
- const result = generateSetup({
987
- framework: input.framework,
988
- database: input.database,
989
- orm: input.orm || "none",
990
- features: input.features || ["email-password"],
991
- typescript: input.typescript ?? true,
992
- srcDir: input.srcDir ?? false,
993
- authPath: input.authPath,
994
- apiPath: input.apiPath,
995
- existingSetup: input.existingSetup
996
- });
997
- if (isSetupError(result)) return {
998
- content: [{
999
- type: "text",
1000
- text: JSON.stringify(result, null, 2)
1001
- }],
1002
- isError: true
1003
- };
1004
- return { content: [{
1005
- type: "text",
1006
- text: JSON.stringify(result, null, 2)
1007
- }] };
185
+ return handler(req, await verifyAccessTokenRequest(requestToResourceInput(req), {
186
+ verifyOptions: {
187
+ issuer,
188
+ audience: resource
189
+ },
190
+ jwksUrl,
191
+ dpop: {
192
+ proofMaxAgeSeconds: opts?.dpop?.proofMaxAgeSeconds,
193
+ signingAlgorithms: opts?.dpop?.signingAlgorithms,
194
+ replayStore: opts?.dpop?.replayStore ?? createDpopReplayStore(internalAdapter)
195
+ }
196
+ }));
1008
197
  } catch (error) {
1009
- const message = error instanceof Error ? error.message : "Unknown error";
1010
- return {
1011
- content: [{
1012
- type: "text",
1013
- text: JSON.stringify({ error: {
1014
- code: "SETUP_ERROR",
1015
- message
1016
- } }, null, 2)
1017
- }],
1018
- isError: true
1019
- };
198
+ try {
199
+ raiseResourceServerChallenge(error, resource, {
200
+ scope: opts?.scope,
201
+ resourceMetadataMappings: opts?.resourceMetadataMappings,
202
+ dpopSigningAlgorithms: opts?.dpop?.signingAlgorithms
203
+ });
204
+ } catch (challengeError) {
205
+ if (challengeError instanceof APIError) return unauthorized(challengeError);
206
+ if (challengeError instanceof Error) throw challengeError;
207
+ throw new Error(String(challengeError));
208
+ }
209
+ throw new Error(String(error));
1020
210
  }
1021
- });
1022
- }
1023
-
1024
- //#endregion
1025
- //#region src/index.ts
1026
- const server = new McpServer({
1027
- name: "better-auth",
1028
- description: "Better Auth MCP server for AI-powered auth setup and diagnostics",
1029
- version: "1.5.0-beta.9"
1030
- });
1031
- registerSetupAuth(server);
1032
- async function main() {
1033
- const transport = new StdioServerTransport();
1034
- await server.connect(transport);
1035
- }
1036
- main().catch(console.error);
1037
-
211
+ };
212
+ };
1038
213
  //#endregion
1039
- export { };
1040
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXgubWpzIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIi4uL3NyYy9saWIvcGFyc2VyLnRzIiwiLi4vc3JjL2xpYi90ZW1wbGF0ZXMvZGF0YWJhc2UudHMiLCIuLi9zcmMvbGliL3RlbXBsYXRlcy9mZWF0dXJlcy50cyIsIi4uL3NyYy9saWIvdGVtcGxhdGVzL2ZyYW1ld29ya3MudHMiLCIuLi9zcmMvbGliL2dlbmVyYXRvci50cyIsIi4uL3NyYy9saWIvc2NoZW1hcy50cyIsIi4uL3NyYy90b29scy9zZXR1cC1hdXRoLnRzIiwiLi4vc3JjL2luZGV4LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgRGV0ZWN0ZWRDb25maWcsIEV4aXN0aW5nU2V0dXAsIEZlYXR1cmUgfSBmcm9tIFwiLi90eXBlcy5qc1wiO1xuXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VFeGlzdGluZ1NldHVwKFxuXHRleGlzdGluZ1NldHVwOiBFeGlzdGluZ1NldHVwLFxuKTogRGV0ZWN0ZWRDb25maWcge1xuXHRjb25zdCBkZXRlY3RlZDogRGV0ZWN0ZWRDb25maWcgPSB7XG5cdFx0ZmVhdHVyZXM6IFtdLFxuXHR9O1xuXG5cdGlmIChleGlzdGluZ1NldHVwLmF1dGhDb25maWcpIHtcblx0XHRjb25zdCBjb25maWcgPSBleGlzdGluZ1NldHVwLmF1dGhDb25maWc7XG5cdFx0ZGV0ZWN0ZWQuZGF0YWJhc2UgPSBkZXRlY3REYXRhYmFzZShjb25maWcpO1xuXHRcdGRldGVjdGVkLm9ybSA9IGRldGVjdE9STShjb25maWcpO1xuXHRcdGRldGVjdGVkLmZlYXR1cmVzID0gZGV0ZWN0RmVhdHVyZXMoY29uZmlnLCBleGlzdGluZ1NldHVwLmF1dGhDbGllbnRDb25maWcpO1xuXHR9XG5cblx0cmV0dXJuIGRldGVjdGVkO1xufVxuXG5mdW5jdGlvbiBkZXRlY3REYXRhYmFzZShjb25maWc6IHN0cmluZyk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG5cdGlmIChcblx0XHQvcHJvdmlkZXI6XFxzKltcIiddcG9zdGdyZXNxbFtcIiddLy50ZXN0KGNvbmZpZykgfHxcblx0XHQvcHJvdmlkZXI6XFxzKltcIiddcGdbXCInXS8udGVzdChjb25maWcpXG5cdCkge1xuXHRcdHJldHVybiBcInBvc3RncmVzXCI7XG5cdH1cblx0aWYgKC9wcm92aWRlcjpcXHMqW1wiJ11teXNxbFtcIiddLy50ZXN0KGNvbmZpZykpIHtcblx0XHRyZXR1cm4gXCJteXNxbFwiO1xuXHR9XG5cdGlmICgvcHJvdmlkZXI6XFxzKltcIiddc3FsaXRlW1wiJ10vLnRlc3QoY29uZmlnKSkge1xuXHRcdHJldHVybiBcInNxbGl0ZVwiO1xuXHR9XG5cdGlmICgvcHJvdmlkZXI6XFxzKltcIiddbW9uZ29kYltcIiddLy50ZXN0KGNvbmZpZykpIHtcblx0XHRyZXR1cm4gXCJtb25nb2RiXCI7XG5cdH1cblx0cmV0dXJuIHVuZGVmaW5lZDtcbn1cblxuZnVuY3Rpb24gZGV0ZWN0T1JNKGNvbmZpZzogc3RyaW5nKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcblx0aWYgKC9wcmlzbWFBZGFwdGVyXFwoLy50ZXN0KGNvbmZpZykpIHtcblx0XHRyZXR1cm4gXCJwcmlzbWFcIjtcblx0fVxuXHRpZiAoL2RyaXp6bGVBZGFwdGVyXFwoLy50ZXN0KGNvbmZpZykpIHtcblx0XHRyZXR1cm4gXCJkcml6emxlXCI7XG5cdH1cblx0aWYgKC9kYXRhYmFzZTpcXHMqXFx7W1xcc1xcU10qcHJvdmlkZXI6Ly50ZXN0KGNvbmZpZykpIHtcblx0XHRyZXR1cm4gXCJub25lXCI7XG5cdH1cblx0cmV0dXJuIHVuZGVmaW5lZDtcbn1cblxuZnVuY3Rpb24gZGV0ZWN0RmVhdHVyZXMoXG5cdHNlcnZlckNvbmZpZzogc3RyaW5nLFxuXHRjbGllbnRDb25maWc/OiBzdHJpbmcsXG4pOiBGZWF0dXJlW10ge1xuXHRjb25zdCBmZWF0dXJlczogRmVhdHVyZVtdID0gW107XG5cblx0aWYgKC9lbWFpbEFuZFBhc3N3b3JkOlxccypcXHtbXFxzXFxTXSplbmFibGVkOlxccyp0cnVlLy50ZXN0KHNlcnZlckNvbmZpZykpIHtcblx0XHRmZWF0dXJlcy5wdXNoKFwiZW1haWwtcGFzc3dvcmRcIik7XG5cdH1cblxuXHRjb25zdCBzb2NpYWxQcm92aWRlcnM6IEZlYXR1cmVbXSA9IFtcblx0XHRcImdvb2dsZVwiLFxuXHRcdFwiZ2l0aHViXCIsXG5cdFx0XCJhcHBsZVwiLFxuXHRcdFwiZGlzY29yZFwiLFxuXHRcdFwidHdpdHRlclwiLFxuXHRcdFwiZmFjZWJvb2tcIixcblx0XHRcIm1pY3Jvc29mdFwiLFxuXHRcdFwibGlua2VkaW5cIixcblx0XTtcblxuXHRmb3IgKGNvbnN0IHByb3ZpZGVyIG9mIHNvY2lhbFByb3ZpZGVycykge1xuXHRcdGlmIChuZXcgUmVnRXhwKGAke3Byb3ZpZGVyfTpcXFxccypcXFxce2ApLnRlc3Qoc2VydmVyQ29uZmlnKSkge1xuXHRcdFx0ZmVhdHVyZXMucHVzaChwcm92aWRlcik7XG5cdFx0fVxuXHR9XG5cblx0Y29uc3QgcGx1Z2luUGF0dGVybnM6IFtSZWdFeHAsIEZlYXR1cmVdW10gPSBbXG5cdFx0Wy90d29GYWN0b3JcXCgvLCBcIjJmYVwiXSxcblx0XHRbL29yZ2FuaXphdGlvblxcKC8sIFwib3JnYW5pemF0aW9uXCJdLFxuXHRcdFsvYWRtaW5cXCgvLCBcImFkbWluXCJdLFxuXHRcdFsvdXNlcm5hbWVcXCgvLCBcInVzZXJuYW1lXCJdLFxuXHRcdFsvbXVsdGlTZXNzaW9uXFwoLywgXCJtdWx0aS1zZXNzaW9uXCJdLFxuXHRcdFsvYXBpS2V5XFwoLywgXCJhcGkta2V5XCJdLFxuXHRcdFsvYmVhcmVyXFwoLywgXCJiZWFyZXJcIl0sXG5cdFx0Wy9qd3RcXCgvLCBcImp3dFwiXSxcblx0XHRbL21hZ2ljTGlua1xcKC8sIFwibWFnaWMtbGlua1wiXSxcblx0XHRbL3Bob25lTnVtYmVyXFwoLywgXCJwaG9uZS1udW1iZXJcIl0sXG5cdFx0Wy9wYXNza2V5XFwoLywgXCJwYXNza2V5XCJdLFxuXHRcdFsvYW5vbnltb3VzXFwoLywgXCJhbm9ueW1vdXNcIl0sXG5cdFx0Wy9jYXB0Y2hhXFwoLywgXCJjYXB0Y2hhXCJdLFxuXHRdO1xuXG5cdGZvciAoY29uc3QgW3BhdHRlcm4sIGZlYXR1cmVdIG9mIHBsdWdpblBhdHRlcm5zKSB7XG5cdFx0aWYgKHBhdHRlcm4udGVzdChzZXJ2ZXJDb25maWcpKSB7XG5cdFx0XHRmZWF0dXJlcy5wdXNoKGZlYXR1cmUpO1xuXHRcdH1cblx0fVxuXG5cdHJldHVybiBmZWF0dXJlcztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbXB1dGVGZWF0dXJlRGlmZihcblx0ZXhpc3Rpbmc6IEZlYXR1cmVbXSxcblx0cmVxdWVzdGVkOiBGZWF0dXJlW10sXG4pOiB7IHRvQWRkOiBGZWF0dXJlW107IGV4aXN0aW5nOiBGZWF0dXJlW10gfSB7XG5cdGNvbnN0IHRvQWRkID0gcmVxdWVzdGVkLmZpbHRlcigoZikgPT4gIWV4aXN0aW5nLmluY2x1ZGVzKGYpKTtcblx0Y29uc3QgZXhpc3RpbmdGZWF0dXJlcyA9IHJlcXVlc3RlZC5maWx0ZXIoKGYpID0+IGV4aXN0aW5nLmluY2x1ZGVzKGYpKTtcblx0cmV0dXJuIHsgdG9BZGQsIGV4aXN0aW5nOiBleGlzdGluZ0ZlYXR1cmVzIH07XG59XG4iLCJpbXBvcnQgdHlwZSB7XG5cdERhdGFiYXNlLFxuXHREYXRhYmFzZUNvbmZpZyxcblx0RW52VmFyLFxuXHRPUk0sXG5cdE9STUNvbmZpZyxcbn0gZnJvbSBcIi4uL3R5cGVzLmpzXCI7XG5cbi8vIGNzcGVsbDppZ25vcmUgbXlkYlxuY29uc3QgREFUQUJBU0VfQ09ORklHUzogUmVjb3JkPERhdGFiYXNlLCBEYXRhYmFzZUNvbmZpZz4gPSB7XG5cdHBvc3RncmVzOiB7XG5cdFx0cHJvdmlkZXI6IFwicG9zdGdyZXNxbFwiLFxuXHRcdGVudlZhck5hbWU6IFwiREFUQUJBU0VfVVJMXCIsXG5cdFx0Y29ubmVjdGlvblN0cmluZ0V4YW1wbGU6IFwicG9zdGdyZXNxbDovL3VzZXI6cGFzc3dvcmRAbG9jYWxob3N0OjU0MzIvbXlkYlwiLFxuXHR9LFxuXHRteXNxbDoge1xuXHRcdHByb3ZpZGVyOiBcIm15c3FsXCIsXG5cdFx0ZW52VmFyTmFtZTogXCJEQVRBQkFTRV9VUkxcIixcblx0XHRjb25uZWN0aW9uU3RyaW5nRXhhbXBsZTogXCJteXNxbDovL3VzZXI6cGFzc3dvcmRAbG9jYWxob3N0OjMzMDYvbXlkYlwiLFxuXHR9LFxuXHRzcWxpdGU6IHtcblx0XHRwcm92aWRlcjogXCJzcWxpdGVcIixcblx0XHRlbnZWYXJOYW1lOiBcIkRBVEFCQVNFX1VSTFwiLFxuXHRcdGNvbm5lY3Rpb25TdHJpbmdFeGFtcGxlOiBcImZpbGU6Li9kZXYuZGJcIixcblx0fSxcblx0bW9uZ29kYjoge1xuXHRcdHByb3ZpZGVyOiBcIm1vbmdvZGJcIixcblx0XHRlbnZWYXJOYW1lOiBcIkRBVEFCQVNFX1VSTFwiLFxuXHRcdGNvbm5lY3Rpb25TdHJpbmdFeGFtcGxlOiBcIm1vbmdvZGI6Ly9sb2NhbGhvc3Q6MjcwMTcvbXlkYlwiLFxuXHR9LFxufTtcblxuY29uc3QgT1JNX0NPTkZJR1M6IFJlY29yZDxPUk0sIE9STUNvbmZpZz4gPSB7XG5cdHByaXNtYToge1xuXHRcdGFkYXB0ZXJJbXBvcnQ6IGBpbXBvcnQgeyBwcmlzbWFBZGFwdGVyIH0gZnJvbSBcImJldHRlci1hdXRoL2FkYXB0ZXJzL3ByaXNtYVwiO1xuaW1wb3J0IHsgUHJpc21hQ2xpZW50IH0gZnJvbSBcIkBwcmlzbWEvY2xpZW50XCI7YCxcblx0XHRhZGFwdGVyU2V0dXA6IChkYlByb3ZpZGVyOiBzdHJpbmcpID0+IGBwcmlzbWFBZGFwdGVyKHByaXNtYSwge1xuICAgIHByb3ZpZGVyOiBcIiR7ZGJQcm92aWRlcn1cIixcbiAgfSlgLFxuXHRcdHNjaGVtYUNvbW1hbmQ6XG5cdFx0XHRcIm5weCBAYmV0dGVyLWF1dGgvY2xpIGdlbmVyYXRlIC0tb3V0cHV0IHByaXNtYS9zY2hlbWEucHJpc21hXCIsXG5cdFx0cHVzaENvbW1hbmQ6IFwibnB4IHByaXNtYSBkYiBwdXNoXCIsXG5cdH0sXG5cdGRyaXp6bGU6IHtcblx0XHRhZGFwdGVySW1wb3J0OiBgaW1wb3J0IHsgZHJpenpsZUFkYXB0ZXIgfSBmcm9tIFwiYmV0dGVyLWF1dGgvYWRhcHRlcnMvZHJpenpsZVwiO1xuaW1wb3J0IHsgZGIgfSBmcm9tIFwiLi9kYlwiO2AsXG5cdFx0YWRhcHRlclNldHVwOiAoZGJQcm92aWRlcjogc3RyaW5nKSA9PiBgZHJpenpsZUFkYXB0ZXIoZGIsIHtcbiAgICBwcm92aWRlcjogXCIke2dldFNob3J0UHJvdmlkZXIoZGJQcm92aWRlcil9XCIsXG4gIH0pYCxcblx0XHRzY2hlbWFDb21tYW5kOiBcIm5weCBAYmV0dGVyLWF1dGgvY2xpIGdlbmVyYXRlIC0tb3V0cHV0IHNyYy9saWIvc2NoZW1hLnRzXCIsXG5cdFx0cHVzaENvbW1hbmQ6IFwibnB4IGRyaXp6bGUta2l0IHB1c2hcIixcblx0fSxcblx0bm9uZToge1xuXHRcdGFkYXB0ZXJJbXBvcnQ6IFwiXCIsXG5cdFx0YWRhcHRlclNldHVwOiAoZGJQcm92aWRlcjogc3RyaW5nKSA9PiBge1xuICAgIHByb3ZpZGVyOiBcIiR7Z2V0U2hvcnRQcm92aWRlcihkYlByb3ZpZGVyKX1cIixcbiAgICB1cmw6IHByb2Nlc3MuZW52LkRBVEFCQVNFX1VSTCxcbiAgfWAsXG5cdFx0cHVzaENvbW1hbmQ6IFwibnB4IEBiZXR0ZXItYXV0aC9jbGkgbWlncmF0ZVwiLFxuXHR9LFxufTtcblxuZnVuY3Rpb24gZ2V0U2hvcnRQcm92aWRlcihwcm92aWRlcjogc3RyaW5nKTogc3RyaW5nIHtcblx0c3dpdGNoIChwcm92aWRlcikge1xuXHRcdGNhc2UgXCJwb3N0Z3Jlc3FsXCI6XG5cdFx0XHRyZXR1cm4gXCJwZ1wiO1xuXHRcdGNhc2UgXCJteXNxbFwiOlxuXHRcdFx0cmV0dXJuIFwibXlzcWxcIjtcblx0XHRjYXNlIFwic3FsaXRlXCI6XG5cdFx0XHRyZXR1cm4gXCJzcWxpdGVcIjtcblx0XHRjYXNlIFwibW9uZ29kYlwiOlxuXHRcdFx0cmV0dXJuIFwibW9uZ29kYlwiO1xuXHRcdGRlZmF1bHQ6XG5cdFx0XHRyZXR1cm4gcHJvdmlkZXI7XG5cdH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdlbmVyYXRlRGF0YWJhc2VDb25maWcoXG5cdGRhdGFiYXNlOiBEYXRhYmFzZSxcblx0b3JtOiBPUk0sXG4pOiB7IGltcG9ydHM6IHN0cmluZzsgY29uZmlnOiBzdHJpbmc7IHByaXNtYUluc3RhbmNlPzogc3RyaW5nIH0ge1xuXHRpZiAob3JtID09PSBcImRyaXp6bGVcIiAmJiBkYXRhYmFzZSA9PT0gXCJtb25nb2RiXCIpIHtcblx0XHR0aHJvdyBuZXcgRXJyb3IoXG5cdFx0XHRcIkRyaXp6bGUgT1JNIGRvZXMgbm90IHN1cHBvcnQgTW9uZ29EQi4gUGxlYXNlIHNlbGVjdCBQcmlzbWEgb3IgdXNlIHRoZSBidWlsdC1pbiBNb25nb0RCIGFkYXB0ZXIgaW5zdGVhZC5cIixcblx0XHQpO1xuXHR9XG5cblx0Y29uc3QgZGJDb25maWcgPSBEQVRBQkFTRV9DT05GSUdTW2RhdGFiYXNlXTtcblx0Y29uc3Qgb3JtQ29uZmlnID0gT1JNX0NPTkZJR1Nbb3JtXTtcblxuXHRsZXQgaW1wb3J0cyA9IFwiXCI7XG5cdGxldCBwcmlzbWFJbnN0YW5jZSA9IFwiXCI7XG5cblx0aWYgKG9ybSA9PT0gXCJwcmlzbWFcIikge1xuXHRcdGltcG9ydHMgPSBvcm1Db25maWcuYWRhcHRlckltcG9ydDtcblx0XHRwcmlzbWFJbnN0YW5jZSA9IFwiXFxuY29uc3QgcHJpc21hID0gbmV3IFByaXNtYUNsaWVudCgpO1xcblwiO1xuXHR9IGVsc2UgaWYgKG9ybSA9PT0gXCJkcml6emxlXCIpIHtcblx0XHRpbXBvcnRzID0gb3JtQ29uZmlnLmFkYXB0ZXJJbXBvcnQ7XG5cdH1cblxuXHRjb25zdCBjb25maWcgPSBvcm1Db25maWcuYWRhcHRlclNldHVwKGRiQ29uZmlnLnByb3ZpZGVyKTtcblxuXHRyZXR1cm4geyBpbXBvcnRzLCBjb25maWcsIHByaXNtYUluc3RhbmNlIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXREYXRhYmFzZUVudlZhcihkYXRhYmFzZTogRGF0YWJhc2UpOiBFbnZWYXIge1xuXHRjb25zdCBjb25maWcgPSBEQVRBQkFTRV9DT05GSUdTW2RhdGFiYXNlXTtcblx0cmV0dXJuIHtcblx0XHRuYW1lOiBjb25maWcuZW52VmFyTmFtZSxcblx0XHRkZXNjcmlwdGlvbjogYCR7ZGF0YWJhc2UuY2hhckF0KDApLnRvVXBwZXJDYXNlKCkgKyBkYXRhYmFzZS5zbGljZSgxKX0gY29ubmVjdGlvbiBzdHJpbmdgLFxuXHRcdHJlcXVpcmVkOiB0cnVlLFxuXHRcdGV4YW1wbGU6IGNvbmZpZy5jb25uZWN0aW9uU3RyaW5nRXhhbXBsZSxcblx0fTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldERhdGFiYXNlQ29tbWFuZHMoXG5cdG9ybTogT1JNLFxuKTogeyBjb21tYW5kOiBzdHJpbmc7IGRlc2NyaXB0aW9uOiBzdHJpbmc7IHdoZW4/OiBzdHJpbmcgfVtdIHtcblx0Y29uc3Qgb3JtQ29uZmlnID0gT1JNX0NPTkZJR1Nbb3JtXTtcblx0Y29uc3QgY29tbWFuZHM6IHsgY29tbWFuZDogc3RyaW5nOyBkZXNjcmlwdGlvbjogc3RyaW5nOyB3aGVuPzogc3RyaW5nIH1bXSA9XG5cdFx0W107XG5cblx0aWYgKG9ybUNvbmZpZy5zY2hlbWFDb21tYW5kKSB7XG5cdFx0Y29tbWFuZHMucHVzaCh7XG5cdFx0XHRjb21tYW5kOiBvcm1Db25maWcuc2NoZW1hQ29tbWFuZCxcblx0XHRcdGRlc2NyaXB0aW9uOiBgR2VuZXJhdGUgJHtvcm0gPT09IFwicHJpc21hXCIgPyBcIlByaXNtYVwiIDogXCJEcml6emxlXCJ9IHNjaGVtYSBmb3IgYXV0aCB0YWJsZXNgLFxuXHRcdH0pO1xuXHR9XG5cblx0Y29tbWFuZHMucHVzaCh7XG5cdFx0Y29tbWFuZDogb3JtQ29uZmlnLnB1c2hDb21tYW5kLFxuXHRcdGRlc2NyaXB0aW9uOlxuXHRcdFx0b3JtID09PSBcIm5vbmVcIlxuXHRcdFx0XHQ/IFwiUnVuIGRhdGFiYXNlIG1pZ3JhdGlvbnMgZm9yIGF1dGggdGFibGVzXCJcblx0XHRcdFx0OiBgUHVzaCAke29ybSA9PT0gXCJwcmlzbWFcIiA/IFwiUHJpc21hXCIgOiBcIkRyaXp6bGVcIn0gc2NoZW1hIHRvIGRhdGFiYXNlYCxcblx0XHR3aGVuOiBcIkFmdGVyIHNldHRpbmcgREFUQUJBU0VfVVJMXCIsXG5cdH0pO1xuXG5cdHJldHVybiBjb21tYW5kcztcbn1cbiIsImltcG9ydCB0eXBlIHsgRW52VmFyLCBGZWF0dXJlLCBQbHVnaW5Db25maWcgfSBmcm9tIFwiLi4vdHlwZXMuanNcIjtcblxuZXhwb3J0IGZ1bmN0aW9uIGdlbmVyYXRlU29jaWFsUHJvdmlkZXJDb25maWcocHJvdmlkZXI6IHN0cmluZyk6IHN0cmluZyB7XG5cdGNvbnN0IHVwcGVyTmFtZSA9IHByb3ZpZGVyLnRvVXBwZXJDYXNlKCk7XG5cdHJldHVybiBgJHtwcm92aWRlcn06IHtcbiAgICAgIGNsaWVudElkOiBwcm9jZXNzLmVudi4ke3VwcGVyTmFtZX1fQ0xJRU5UX0lEISxcbiAgICAgIGNsaWVudFNlY3JldDogcHJvY2Vzcy5lbnYuJHt1cHBlck5hbWV9X0NMSUVOVF9TRUNSRVQhLFxuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0U29jaWFsUHJvdmlkZXJFbnZWYXJzKHByb3ZpZGVyOiBzdHJpbmcpOiBFbnZWYXJbXSB7XG5cdGNvbnN0IHVwcGVyTmFtZSA9IHByb3ZpZGVyLnRvVXBwZXJDYXNlKCk7XG5cdGNvbnN0IHByb3ZpZGVyTmFtZSA9IHByb3ZpZGVyLmNoYXJBdCgwKS50b1VwcGVyQ2FzZSgpICsgcHJvdmlkZXIuc2xpY2UoMSk7XG5cblx0cmV0dXJuIFtcblx0XHR7XG5cdFx0XHRuYW1lOiBgJHt1cHBlck5hbWV9X0NMSUVOVF9JRGAsXG5cdFx0XHRkZXNjcmlwdGlvbjogYCR7cHJvdmlkZXJOYW1lfSBPQXV0aCBjbGllbnQgSURgLFxuXHRcdFx0cmVxdWlyZWQ6IHRydWUsXG5cdFx0XHRob3dUb0dldDogYENyZWF0ZSBPQXV0aCBhcHAgYXQgJHtwcm92aWRlck5hbWV9IGRldmVsb3BlciBjb25zb2xlYCxcblx0XHR9LFxuXHRcdHtcblx0XHRcdG5hbWU6IGAke3VwcGVyTmFtZX1fQ0xJRU5UX1NFQ1JFVGAsXG5cdFx0XHRkZXNjcmlwdGlvbjogYCR7cHJvdmlkZXJOYW1lfSBPQXV0aCBjbGllbnQgc2VjcmV0YCxcblx0XHRcdHJlcXVpcmVkOiB0cnVlLFxuXHRcdFx0aG93VG9HZXQ6IGBDcmVhdGUgT0F1dGggYXBwIGF0ICR7cHJvdmlkZXJOYW1lfSBkZXZlbG9wZXIgY29uc29sZWAsXG5cdFx0fSxcblx0XTtcbn1cblxuY29uc3QgX0tOT1dOX1NPQ0lBTF9QUk9WSURFUlMgPSBbXG5cdFwiZ29vZ2xlXCIsXG5cdFwiZ2l0aHViXCIsXG5cdFwiYXBwbGVcIixcblx0XCJkaXNjb3JkXCIsXG5cdFwidHdpdHRlclwiLFxuXHRcImZhY2Vib29rXCIsXG5cdFwibWljcm9zb2Z0XCIsXG5cdFwibGlua2VkaW5cIixcblx0XCJzcG90aWZ5XCIsXG5cdFwidHdpdGNoXCIsXG5cdFwic2xhY2tcIixcblx0XCJnaXRsYWJcIixcblx0XCJyZWRkaXRcIixcblx0XCJkcm9wYm94XCIsXG5cdFwidGlrdG9rXCIsXG5dIGFzIGNvbnN0O1xuXG5jb25zdCBQTFVHSU5fQ09ORklHUzogUmVjb3JkPHN0cmluZywgUGx1Z2luQ29uZmlnPiA9IHtcblx0XCIyZmFcIjoge1xuXHRcdHNlcnZlckltcG9ydDogJ2ltcG9ydCB7IHR3b0ZhY3RvciB9IGZyb20gXCJiZXR0ZXItYXV0aC9wbHVnaW5zXCI7Jyxcblx0XHRjbGllbnRJbXBvcnQ6XG5cdFx0XHQnaW1wb3J0IHsgdHdvRmFjdG9yQ2xpZW50IH0gZnJvbSBcImJldHRlci1hdXRoL2NsaWVudC9wbHVnaW5zXCI7Jyxcblx0XHRzZXJ2ZXJQbHVnaW46ICgpID0+IGB0d29GYWN0b3IoKWAsXG5cdFx0Y2xpZW50UGx1Z2luOiAoKSA9PiBgdHdvRmFjdG9yQ2xpZW50KClgLFxuXHR9LFxuXHRvcmdhbml6YXRpb246IHtcblx0XHRzZXJ2ZXJJbXBvcnQ6ICdpbXBvcnQgeyBvcmdhbml6YXRpb24gfSBmcm9tIFwiYmV0dGVyLWF1dGgvcGx1Z2luc1wiOycsXG5cdFx0Y2xpZW50SW1wb3J0OlxuXHRcdFx0J2ltcG9ydCB7IG9yZ2FuaXphdGlvbkNsaWVudCB9IGZyb20gXCJiZXR0ZXItYXV0aC9jbGllbnQvcGx1Z2luc1wiOycsXG5cdFx0c2VydmVyUGx1Z2luOiAoKSA9PiBgb3JnYW5pemF0aW9uKClgLFxuXHRcdGNsaWVudFBsdWdpbjogKCkgPT4gYG9yZ2FuaXphdGlvbkNsaWVudCgpYCxcblx0fSxcblx0YWRtaW46IHtcblx0XHRzZXJ2ZXJJbXBvcnQ6ICdpbXBvcnQgeyBhZG1pbiB9IGZyb20gXCJiZXR0ZXItYXV0aC9wbHVnaW5zXCI7Jyxcblx0XHRjbGllbnRJbXBvcnQ6ICdpbXBvcnQgeyBhZG1pbkNsaWVudCB9IGZyb20gXCJiZXR0ZXItYXV0aC9jbGllbnQvcGx1Z2luc1wiOycsXG5cdFx0c2VydmVyUGx1Z2luOiAoKSA9PiBgYWRtaW4oKWAsXG5cdFx0Y2xpZW50UGx1Z2luOiAoKSA9PiBgYWRtaW5DbGllbnQoKWAsXG5cdH0sXG5cdHVzZXJuYW1lOiB7XG5cdFx0c2VydmVySW1wb3J0OiAnaW1wb3J0IHsgdXNlcm5hbWUgfSBmcm9tIFwiYmV0dGVyLWF1dGgvcGx1Z2luc1wiOycsXG5cdFx0Y2xpZW50SW1wb3J0OlxuXHRcdFx0J2ltcG9ydCB7IHVzZXJuYW1lQ2xpZW50IH0gZnJvbSBcImJldHRlci1hdXRoL2NsaWVudC9wbHVnaW5zXCI7Jyxcblx0XHRzZXJ2ZXJQbHVnaW46ICgpID0+IGB1c2VybmFtZSgpYCxcblx0XHRjbGllbnRQbHVnaW46ICgpID0+IGB1c2VybmFtZUNsaWVudCgpYCxcblx0fSxcblx0XCJtdWx0aS1zZXNzaW9uXCI6IHtcblx0XHRzZXJ2ZXJJbXBvcnQ6ICdpbXBvcnQgeyBtdWx0aVNlc3Npb24gfSBmcm9tIFwiYmV0dGVyLWF1dGgvcGx1Z2luc1wiOycsXG5cdFx0Y2xpZW50SW1wb3J0OlxuXHRcdFx0J2ltcG9ydCB7IG11bHRpU2Vzc2lvbkNsaWVudCB9IGZyb20gXCJiZXR0ZXItYXV0aC9jbGllbnQvcGx1Z2luc1wiOycsXG5cdFx0c2VydmVyUGx1Z2luOiAoKSA9PiBgbXVsdGlTZXNzaW9uKClgLFxuXHRcdGNsaWVudFBsdWdpbjogKCkgPT4gYG11bHRpU2Vzc2lvbkNsaWVudCgpYCxcblx0fSxcblx0XCJhcGkta2V5XCI6IHtcblx0XHRzZXJ2ZXJJbXBvcnQ6ICdpbXBvcnQgeyBhcGlLZXkgfSBmcm9tIFwiYmV0dGVyLWF1dGgvcGx1Z2luc1wiOycsXG5cdFx0Y2xpZW50SW1wb3J0OiAnaW1wb3J0IHsgYXBpS2V5Q2xpZW50IH0gZnJvbSBcImJldHRlci1hdXRoL2NsaWVudC9wbHVnaW5zXCI7Jyxcblx0XHRzZXJ2ZXJQbHVnaW46ICgpID0+IGBhcGlLZXkoKWAsXG5cdFx0Y2xpZW50UGx1Z2luOiAoKSA9PiBgYXBpS2V5Q2xpZW50KClgLFxuXHR9LFxuXHRiZWFyZXI6IHtcblx0XHRzZXJ2ZXJJbXBvcnQ6ICdpbXBvcnQgeyBiZWFyZXIgfSBmcm9tIFwiYmV0dGVyLWF1dGgvcGx1Z2luc1wiOycsXG5cdFx0c2VydmVyUGx1Z2luOiAoKSA9PiBgYmVhcmVyKClgLFxuXHR9LFxuXHRqd3Q6IHtcblx0XHRzZXJ2ZXJJbXBvcnQ6ICdpbXBvcnQgeyBqd3QgfSBmcm9tIFwiYmV0dGVyLWF1dGgvcGx1Z2luc1wiOycsXG5cdFx0c2VydmVyUGx1Z2luOiAoKSA9PiBgand0KClgLFxuXHR9LFxuXHRcIm1hZ2ljLWxpbmtcIjoge1xuXHRcdHNlcnZlckltcG9ydDogJ2ltcG9ydCB7IG1hZ2ljTGluayB9IGZyb20gXCJiZXR0ZXItYXV0aC9wbHVnaW5zXCI7Jyxcblx0XHRjbGllbnRJbXBvcnQ6XG5cdFx0XHQnaW1wb3J0IHsgbWFnaWNMaW5rQ2xpZW50IH0gZnJvbSBcImJldHRlci1hdXRoL2NsaWVudC9wbHVnaW5zXCI7Jyxcblx0XHRzZXJ2ZXJQbHVnaW46ICgpID0+IGBtYWdpY0xpbmsoe1xuICAgICAgc2VuZE1hZ2ljTGluazogYXN5bmMgKHsgZW1haWwsIHVybCB9KSA9PiB7XG4gICAgICAgIC8vIFRPRE86IFNlbmQgbWFnaWMgbGluayBlbWFpbFxuICAgICAgICBjb25zb2xlLmxvZyhcIlNlbmQgbWFnaWMgbGluayB0b1wiLCBlbWFpbCwgdXJsKTtcbiAgICAgIH0sXG4gICAgfSlgLFxuXHRcdGNsaWVudFBsdWdpbjogKCkgPT4gYG1hZ2ljTGlua0NsaWVudCgpYCxcblx0fSxcblx0XCJwaG9uZS1udW1iZXJcIjoge1xuXHRcdHNlcnZlckltcG9ydDogJ2ltcG9ydCB7IHBob25lTnVtYmVyIH0gZnJvbSBcImJldHRlci1hdXRoL3BsdWdpbnNcIjsnLFxuXHRcdGNsaWVudEltcG9ydDpcblx0XHRcdCdpbXBvcnQgeyBwaG9uZU51bWJlckNsaWVudCB9IGZyb20gXCJiZXR0ZXItYXV0aC9jbGllbnQvcGx1Z2luc1wiOycsXG5cdFx0c2VydmVyUGx1Z2luOiAoKSA9PiBgcGhvbmVOdW1iZXIoe1xuICAgICAgc2VuZE9UUDogYXN5bmMgKHsgcGhvbmVOdW1iZXIsIGNvZGUgfSkgPT4ge1xuICAgICAgICAvLyBUT0RPOiBTZW5kIE9UUCB2aWEgU01TXG4gICAgICAgIGNvbnNvbGUubG9nKFwiU2VuZCBPVFBcIiwgY29kZSwgXCJ0b1wiLCBwaG9uZU51bWJlcik7XG4gICAgICB9LFxuICAgIH0pYCxcblx0XHRjbGllbnRQbHVnaW46ICgpID0+IGBwaG9uZU51bWJlckNsaWVudCgpYCxcblx0fSxcblx0cGFzc2tleToge1xuXHRcdHNlcnZlckltcG9ydDogJ2ltcG9ydCB7IHBhc3NrZXkgfSBmcm9tIFwiYmV0dGVyLWF1dGgvcGx1Z2luc1wiOycsXG5cdFx0Y2xpZW50SW1wb3J0OiAnaW1wb3J0IHsgcGFzc2tleUNsaWVudCB9IGZyb20gXCJiZXR0ZXItYXV0aC9jbGllbnQvcGx1Z2luc1wiOycsXG5cdFx0c2VydmVyUGx1Z2luOiAoKSA9PiBgcGFzc2tleSgpYCxcblx0XHRjbGllbnRQbHVnaW46ICgpID0+IGBwYXNza2V5Q2xpZW50KClgLFxuXHR9LFxuXHRhbm9ueW1vdXM6IHtcblx0XHRzZXJ2ZXJJbXBvcnQ6ICdpbXBvcnQgeyBhbm9ueW1vdXMgfSBmcm9tIFwiYmV0dGVyLWF1dGgvcGx1Z2luc1wiOycsXG5cdFx0Y2xpZW50SW1wb3J0OlxuXHRcdFx0J2ltcG9ydCB7IGFub255bW91c0NsaWVudCB9IGZyb20gXCJiZXR0ZXItYXV0aC9jbGllbnQvcGx1Z2luc1wiOycsXG5cdFx0c2VydmVyUGx1Z2luOiAoKSA9PiBgYW5vbnltb3VzKClgLFxuXHRcdGNsaWVudFBsdWdpbjogKCkgPT4gYGFub255bW91c0NsaWVudCgpYCxcblx0fSxcblx0Y2FwdGNoYToge1xuXHRcdHNlcnZlckltcG9ydDogJ2ltcG9ydCB7IGNhcHRjaGEgfSBmcm9tIFwiYmV0dGVyLWF1dGgvcGx1Z2luc1wiOycsXG5cdFx0c2VydmVyUGx1Z2luOiAoKSA9PiBgY2FwdGNoYSh7XG4gICAgICBwcm92aWRlcjogXCJjbG91ZGZsYXJlLXR1cm5zdGlsZVwiLCAvLyBvciBcInJlY2FwdGNoYVwiIG9yIFwiaGNhcHRjaGFcIlxuICAgICAgc2VjcmV0S2V5OiBwcm9jZXNzLmVudi5DQVBUQ0hBX1NFQ1JFVF9LRVkhLFxuICAgIH0pYCxcblx0XHRlbnZWYXJzOiBbXG5cdFx0XHR7XG5cdFx0XHRcdG5hbWU6IFwiQ0FQVENIQV9TRUNSRVRfS0VZXCIsXG5cdFx0XHRcdGRlc2NyaXB0aW9uOiBcIkNhcHRjaGEgcHJvdmlkZXIgc2VjcmV0IGtleVwiLFxuXHRcdFx0XHRyZXF1aXJlZDogdHJ1ZSxcblx0XHRcdH0sXG5cdFx0XSxcblx0fSxcbn07XG5cbmV4cG9ydCBmdW5jdGlvbiBnZW5lcmF0ZVBsdWdpbkltcG9ydHMocGx1Z2luczogc3RyaW5nW10pOiB7XG5cdHNlcnZlckltcG9ydHM6IHN0cmluZ1tdO1xuXHRjbGllbnRJbXBvcnRzOiBzdHJpbmdbXTtcbn0ge1xuXHRjb25zdCBzZXJ2ZXJJbXBvcnRzOiBzdHJpbmdbXSA9IFtdO1xuXHRjb25zdCBjbGllbnRJbXBvcnRzOiBzdHJpbmdbXSA9IFtdO1xuXG5cdGZvciAoY29uc3QgcGx1Z2luIG9mIHBsdWdpbnMpIHtcblx0XHRjb25zdCBjb25maWcgPSBQTFVHSU5fQ09ORklHU1twbHVnaW5dO1xuXHRcdGlmIChjb25maWcpIHtcblx0XHRcdHNlcnZlckltcG9ydHMucHVzaChjb25maWcuc2VydmVySW1wb3J0KTtcblx0XHRcdGlmIChjb25maWcuY2xpZW50SW1wb3J0KSB7XG5cdFx0XHRcdGNsaWVudEltcG9ydHMucHVzaChjb25maWcuY2xpZW50SW1wb3J0KTtcblx0XHRcdH1cblx0XHR9XG5cdH1cblxuXHRyZXR1cm4geyBzZXJ2ZXJJbXBvcnRzLCBjbGllbnRJbXBvcnRzIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZW5lcmF0ZVBsdWdpblNldHVwKHBsdWdpbnM6IHN0cmluZ1tdKToge1xuXHRzZXJ2ZXJQbHVnaW5zOiBzdHJpbmdbXTtcblx0Y2xpZW50UGx1Z2luczogc3RyaW5nW107XG59IHtcblx0Y29uc3Qgc2VydmVyUGx1Z2luczogc3RyaW5nW10gPSBbXTtcblx0Y29uc3QgY2xpZW50UGx1Z2luczogc3RyaW5nW10gPSBbXTtcblxuXHRmb3IgKGNvbnN0IHBsdWdpbiBvZiBwbHVnaW5zKSB7XG5cdFx0Y29uc3QgY29uZmlnID0gUExVR0lOX0NPTkZJR1NbcGx1Z2luXTtcblx0XHRpZiAoY29uZmlnKSB7XG5cdFx0XHRzZXJ2ZXJQbHVnaW5zLnB1c2goY29uZmlnLnNlcnZlclBsdWdpbigpKTtcblx0XHRcdGlmIChjb25maWcuY2xpZW50UGx1Z2luKSB7XG5cdFx0XHRcdGNsaWVudFBsdWdpbnMucHVzaChjb25maWcuY2xpZW50UGx1Z2luKCkpO1xuXHRcdFx0fVxuXHRcdH1cblx0fVxuXG5cdHJldHVybiB7IHNlcnZlclBsdWdpbnMsIGNsaWVudFBsdWdpbnMgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldFBsdWdpbkVudlZhcnMocGx1Z2luczogc3RyaW5nW10pOiBFbnZWYXJbXSB7XG5cdGNvbnN0IGVudlZhcnM6IEVudlZhcltdID0gW107XG5cblx0Zm9yIChjb25zdCBwbHVnaW4gb2YgcGx1Z2lucykge1xuXHRcdGNvbnN0IGNvbmZpZyA9IFBMVUdJTl9DT05GSUdTW3BsdWdpbl07XG5cdFx0aWYgKGNvbmZpZz8uZW52VmFycykge1xuXHRcdFx0ZW52VmFycy5wdXNoKC4uLmNvbmZpZy5lbnZWYXJzKTtcblx0XHR9XG5cdH1cblxuXHRyZXR1cm4gZW52VmFycztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNhdGVnb3JpemVGZWF0dXJlcyhmZWF0dXJlczogRmVhdHVyZVtdKToge1xuXHRzb2NpYWxQcm92aWRlcnM6IHN0cmluZ1tdO1xuXHRwbHVnaW5zOiBzdHJpbmdbXTtcblx0aGFzRW1haWxQYXNzd29yZDogYm9vbGVhbjtcbn0ge1xuXHRjb25zdCBzb2NpYWxQcm92aWRlcnM6IHN0cmluZ1tdID0gW107XG5cdGNvbnN0IHBsdWdpbnM6IHN0cmluZ1tdID0gW107XG5cdGxldCBoYXNFbWFpbFBhc3N3b3JkID0gZmFsc2U7XG5cblx0Zm9yIChjb25zdCBmZWF0dXJlIG9mIGZlYXR1cmVzKSB7XG5cdFx0aWYgKGZlYXR1cmUgPT09IFwiZW1haWwtcGFzc3dvcmRcIikge1xuXHRcdFx0aGFzRW1haWxQYXNzd29yZCA9IHRydWU7XG5cdFx0fSBlbHNlIGlmIChmZWF0dXJlIGluIFBMVUdJTl9DT05GSUdTKSB7XG5cdFx0XHRwbHVnaW5zLnB1c2goZmVhdHVyZSk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHNvY2lhbFByb3ZpZGVycy5wdXNoKGZlYXR1cmUpO1xuXHRcdH1cblx0fVxuXG5cdHJldHVybiB7IHNvY2lhbFByb3ZpZGVycywgcGx1Z2lucywgaGFzRW1haWxQYXNzd29yZCB9O1xufVxuIiwiaW1wb3J0IHR5cGUgeyBGcmFtZXdvcmssIEZyYW1ld29ya0NvbmZpZyB9IGZyb20gXCIuLi90eXBlcy5qc1wiO1xuXG5leHBvcnQgY29uc3QgRlJBTUVXT1JLX0NPTkZJR1M6IFJlY29yZDxGcmFtZXdvcmssIEZyYW1ld29ya0NvbmZpZz4gPSB7XG5cdFwibmV4dC1hcHAtcm91dGVyXCI6IHtcblx0XHRuYW1lOiBcIk5leHQuanMgKEFwcCBSb3V0ZXIpXCIsXG5cdFx0ZGVmYXVsdFNyY0RpcjogZmFsc2UsXG5cdFx0ZGVmYXVsdEF1dGhQYXRoOiBcImxpYi9hdXRoXCIsXG5cdFx0ZGVmYXVsdEFwaVBhdGg6IFwiYXBwL2FwaS9hdXRoL1suLi5hbGxdXCIsXG5cdFx0Y2xpZW50SW1wb3J0OiAnaW1wb3J0IHsgY3JlYXRlQXV0aENsaWVudCB9IGZyb20gXCJiZXR0ZXItYXV0aC9yZWFjdFwiOycsXG5cdFx0aGFuZGxlckltcG9ydDogJ2ltcG9ydCB7IHRvTmV4dEpzSGFuZGxlciB9IGZyb20gXCJiZXR0ZXItYXV0aC9uZXh0LWpzXCI7Jyxcblx0XHRoYW5kbGVyRnVuY3Rpb246IFwidG9OZXh0SnNIYW5kbGVyXCIsXG5cdFx0YXBpUm91dGVUZW1wbGF0ZTogKFxuXHRcdFx0YXV0aFBhdGg6IHN0cmluZyxcblx0XHQpID0+IGBpbXBvcnQgeyBhdXRoIH0gZnJvbSBcIkAvJHthdXRoUGF0aH1cIjtcbmltcG9ydCB7IHRvTmV4dEpzSGFuZGxlciB9IGZyb20gXCJiZXR0ZXItYXV0aC9uZXh0LWpzXCI7XG5cbmV4cG9ydCBjb25zdCB7IEdFVCwgUE9TVCB9ID0gdG9OZXh0SnNIYW5kbGVyKGF1dGgpO2AsXG5cdFx0ZGVmYXVsdFBvcnQ6IDMwMDAsXG5cdH0sXG5cdFwibmV4dC1wYWdlcy1yb3V0ZXJcIjoge1xuXHRcdG5hbWU6IFwiTmV4dC5qcyAoUGFnZXMgUm91dGVyKVwiLFxuXHRcdGRlZmF1bHRTcmNEaXI6IGZhbHNlLFxuXHRcdGRlZmF1bHRBdXRoUGF0aDogXCJsaWIvYXV0aFwiLFxuXHRcdGRlZmF1bHRBcGlQYXRoOiBcInBhZ2VzL2FwaS9hdXRoL1suLi5hbGxdXCIsXG5cdFx0Y2xpZW50SW1wb3J0OiAnaW1wb3J0IHsgY3JlYXRlQXV0aENsaWVudCB9IGZyb20gXCJiZXR0ZXItYXV0aC9yZWFjdFwiOycsXG5cdFx0aGFuZGxlckltcG9ydDogJ2ltcG9ydCB7IHRvTm9kZUhhbmRsZXIgfSBmcm9tIFwiYmV0dGVyLWF1dGgvbm9kZVwiOycsXG5cdFx0aGFuZGxlckZ1bmN0aW9uOiBcInRvTm9kZUhhbmRsZXJcIixcblx0XHRhcGlSb3V0ZVRlbXBsYXRlOiAoXG5cdFx0XHRhdXRoUGF0aDogc3RyaW5nLFxuXHRcdCkgPT4gYGltcG9ydCB7IHRvTm9kZUhhbmRsZXIgfSBmcm9tIFwiYmV0dGVyLWF1dGgvbm9kZVwiO1xuaW1wb3J0IHsgYXV0aCB9IGZyb20gXCJALyR7YXV0aFBhdGh9XCI7XG5cbmV4cG9ydCBjb25zdCBjb25maWcgPSB7IGFwaTogeyBib2R5UGFyc2VyOiBmYWxzZSB9IH07XG5leHBvcnQgZGVmYXVsdCB0b05vZGVIYW5kbGVyKGF1dGgpO2AsXG5cdFx0ZGVmYXVsdFBvcnQ6IDMwMDAsXG5cdH0sXG5cdHN2ZWx0ZWtpdDoge1xuXHRcdG5hbWU6IFwiU3ZlbHRlS2l0XCIsXG5cdFx0ZGVmYXVsdFNyY0RpcjogdHJ1ZSxcblx0XHRkZWZhdWx0QXV0aFBhdGg6IFwibGliL2F1dGhcIixcblx0XHRkZWZhdWx0QXBpUGF0aDogXCJyb3V0ZXMvYXBpL2F1dGgvWy4uLmFsbF1cIixcblx0XHRjbGllbnRJbXBvcnQ6ICdpbXBvcnQgeyBjcmVhdGVBdXRoQ2xpZW50IH0gZnJvbSBcImJldHRlci1hdXRoL3N2ZWx0ZVwiOycsXG5cdFx0aGFuZGxlckltcG9ydDogJ2ltcG9ydCB7IHN2ZWx0ZUtpdEhhbmRsZXIgfSBmcm9tIFwiYmV0dGVyLWF1dGgvc3ZlbHRlLWtpdFwiOycsXG5cdFx0aGFuZGxlckZ1bmN0aW9uOiBcInN2ZWx0ZUtpdEhhbmRsZXJcIixcblx0XHRhcGlSb3V0ZVRlbXBsYXRlOiAoKSA9PiBcIlwiLFxuXHRcdGhvb2tzVGVtcGxhdGU6IChhdXRoUGF0aDogc3RyaW5nKSA9PiBgaW1wb3J0IHsgYXV0aCB9IGZyb20gXCIkJHthdXRoUGF0aH1cIjtcbmltcG9ydCB7IHN2ZWx0ZUtpdEhhbmRsZXIgfSBmcm9tIFwiYmV0dGVyLWF1dGgvc3ZlbHRlLWtpdFwiO1xuaW1wb3J0IHR5cGUgeyBIYW5kbGUgfSBmcm9tIFwiQHN2ZWx0ZWpzL2tpdFwiO1xuXG5leHBvcnQgY29uc3QgaGFuZGxlOiBIYW5kbGUgPSBhc3luYyAoeyBldmVudCwgcmVzb2x2ZSB9KSA9PiB7XG4gIHJldHVybiBzdmVsdGVLaXRIYW5kbGVyKHsgZXZlbnQsIHJlc29sdmUsIGF1dGggfSk7XG59O2AsXG5cdFx0ZGVmYXVsdFBvcnQ6IDUxNzMsXG5cdH0sXG5cdGFzdHJvOiB7XG5cdFx0bmFtZTogXCJBc3Ryb1wiLFxuXHRcdGRlZmF1bHRTcmNEaXI6IHRydWUsXG5cdFx0ZGVmYXVsdEF1dGhQYXRoOiBcImxpYi9hdXRoXCIsXG5cdFx0ZGVmYXVsdEFwaVBhdGg6IFwicGFnZXMvYXBpL2F1dGgvWy4uLmFsbF1cIixcblx0XHRjbGllbnRJbXBvcnQ6ICdpbXBvcnQgeyBjcmVhdGVBdXRoQ2xpZW50IH0gZnJvbSBcImJldHRlci1hdXRoL2NsaWVudFwiOycsXG5cdFx0aGFuZGxlckltcG9ydDogXCJcIixcblx0XHRoYW5kbGVyRnVuY3Rpb246IFwiXCIsXG5cdFx0YXBpUm91dGVUZW1wbGF0ZTogKFxuXHRcdFx0YXV0aFBhdGg6IHN0cmluZyxcblx0XHQpID0+IGBpbXBvcnQgdHlwZSB7IEFQSVJvdXRlIH0gZnJvbSBcImFzdHJvXCI7XG5pbXBvcnQgeyBhdXRoIH0gZnJvbSBcIkAvJHthdXRoUGF0aH1cIjtcblxuZXhwb3J0IGNvbnN0IEdFVDogQVBJUm91dGUgPSBhc3luYyAoY3R4KSA9PiB7XG4gIHJldHVybiBhdXRoLmhhbmRsZXIoY3R4LnJlcXVlc3QpO1xufTtcblxuZXhwb3J0IGNvbnN0IFBPU1Q6IEFQSVJvdXRlID0gYXN5bmMgKGN0eCkgPT4ge1xuICByZXR1cm4gYXV0aC5oYW5kbGVyKGN0eC5yZXF1ZXN0KTtcbn07YCxcblx0XHRkZWZhdWx0UG9ydDogNDMyMSxcblx0fSxcblx0cmVtaXg6IHtcblx0XHRuYW1lOiBcIlJlbWl4XCIsXG5cdFx0ZGVmYXVsdFNyY0RpcjogdHJ1ZSxcblx0XHRkZWZhdWx0QXV0aFBhdGg6IFwibGliL2F1dGhcIixcblx0XHRkZWZhdWx0QXBpUGF0aDogXCJyb3V0ZXMvYXBpLmF1dGguJFwiLFxuXHRcdGNsaWVudEltcG9ydDogJ2ltcG9ydCB7IGNyZWF0ZUF1dGhDbGllbnQgfSBmcm9tIFwiYmV0dGVyLWF1dGgvcmVhY3RcIjsnLFxuXHRcdGhhbmRsZXJJbXBvcnQ6IFwiXCIsXG5cdFx0aGFuZGxlckZ1bmN0aW9uOiBcIlwiLFxuXHRcdGFwaVJvdXRlVGVtcGxhdGU6IChcblx0XHRcdGF1dGhQYXRoOiBzdHJpbmcsXG5cdFx0KSA9PiBgaW1wb3J0IHsgYXV0aCB9IGZyb20gXCJ+LyR7YXV0aFBhdGh9XCI7XG5pbXBvcnQgdHlwZSB7IEFjdGlvbkZ1bmN0aW9uQXJncywgTG9hZGVyRnVuY3Rpb25BcmdzIH0gZnJvbSBcIkByZW1peC1ydW4vbm9kZVwiO1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbG9hZGVyKHsgcmVxdWVzdCB9OiBMb2FkZXJGdW5jdGlvbkFyZ3MpIHtcbiAgcmV0dXJuIGF1dGguaGFuZGxlcihyZXF1ZXN0KTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGFjdGlvbih7IHJlcXVlc3QgfTogQWN0aW9uRnVuY3Rpb25BcmdzKSB7XG4gIHJldHVybiBhdXRoLmhhbmRsZXIocmVxdWVzdCk7XG59YCxcblx0XHRkZWZhdWx0UG9ydDogMzAwMCxcblx0fSxcblx0bnV4dDoge1xuXHRcdG5hbWU6IFwiTnV4dCAzXCIsXG5cdFx0ZGVmYXVsdFNyY0RpcjogZmFsc2UsXG5cdFx0ZGVmYXVsdEF1dGhQYXRoOiBcImxpYi9hdXRoXCIsXG5cdFx0ZGVmYXVsdEFwaVBhdGg6IFwic2VydmVyL2FwaS9hdXRoL1suLi5hbGxdXCIsXG5cdFx0Y2xpZW50SW1wb3J0OiAnaW1wb3J0IHsgY3JlYXRlQXV0aENsaWVudCB9IGZyb20gXCJiZXR0ZXItYXV0aC92dWVcIjsnLFxuXHRcdGhhbmRsZXJJbXBvcnQ6IFwiXCIsXG5cdFx0aGFuZGxlckZ1bmN0aW9uOiBcIlwiLFxuXHRcdGFwaVJvdXRlVGVtcGxhdGU6IChcblx0XHRcdGF1dGhQYXRoOiBzdHJpbmcsXG5cdFx0KSA9PiBgaW1wb3J0IHsgYXV0aCB9IGZyb20gXCJ+LyR7YXV0aFBhdGh9XCI7XG5cbmV4cG9ydCBkZWZhdWx0IGRlZmluZUV2ZW50SGFuZGxlcigoZXZlbnQpID0+IHtcbiAgcmV0dXJuIGF1dGguaGFuZGxlcih0b1dlYlJlcXVlc3QoZXZlbnQpKTtcbn0pO2AsXG5cdFx0ZGVmYXVsdFBvcnQ6IDMwMDAsXG5cdH0sXG5cdFwic29saWQtc3RhcnRcIjoge1xuXHRcdG5hbWU6IFwiU29saWRTdGFydFwiLFxuXHRcdGRlZmF1bHRTcmNEaXI6IHRydWUsXG5cdFx0ZGVmYXVsdEF1dGhQYXRoOiBcImxpYi9hdXRoXCIsXG5cdFx0ZGVmYXVsdEFwaVBhdGg6IFwicm91dGVzL2FwaS9hdXRoL1suLi5hbGxdXCIsXG5cdFx0Y2xpZW50SW1wb3J0OiAnaW1wb3J0IHsgY3JlYXRlQXV0aENsaWVudCB9IGZyb20gXCJiZXR0ZXItYXV0aC9zb2xpZFwiOycsXG5cdFx0aGFuZGxlckltcG9ydDogXCJcIixcblx0XHRoYW5kbGVyRnVuY3Rpb246IFwiXCIsXG5cdFx0YXBpUm91dGVUZW1wbGF0ZTogKFxuXHRcdFx0YXV0aFBhdGg6IHN0cmluZyxcblx0XHQpID0+IGBpbXBvcnQgeyBhdXRoIH0gZnJvbSBcIn4vJHthdXRoUGF0aH1cIjtcbmltcG9ydCB0eXBlIHsgQVBJRXZlbnQgfSBmcm9tIFwiQHNvbGlkanMvc3RhcnQvc2VydmVyXCI7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBHRVQoZXZlbnQ6IEFQSUV2ZW50KSB7XG4gIHJldHVybiBhdXRoLmhhbmRsZXIoZXZlbnQucmVxdWVzdCk7XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBQT1NUKGV2ZW50OiBBUElFdmVudCkge1xuICByZXR1cm4gYXV0aC5oYW5kbGVyKGV2ZW50LnJlcXVlc3QpO1xufWAsXG5cdFx0ZGVmYXVsdFBvcnQ6IDMwMDAsXG5cdH0sXG5cdGhvbm86IHtcblx0XHRuYW1lOiBcIkhvbm9cIixcblx0XHRkZWZhdWx0U3JjRGlyOiB0cnVlLFxuXHRcdGRlZmF1bHRBdXRoUGF0aDogXCJsaWIvYXV0aFwiLFxuXHRcdGRlZmF1bHRBcGlQYXRoOiBcIlwiLFxuXHRcdGNsaWVudEltcG9ydDogJ2ltcG9ydCB7IGNyZWF0ZUF1dGhDbGllbnQgfSBmcm9tIFwiYmV0dGVyLWF1dGgvY2xpZW50XCI7Jyxcblx0XHRoYW5kbGVySW1wb3J0OiBcIlwiLFxuXHRcdGhhbmRsZXJGdW5jdGlvbjogXCJcIixcblx0XHRhcGlSb3V0ZVRlbXBsYXRlOiAoYXV0aFBhdGg6IHN0cmluZykgPT4gYGltcG9ydCB7IEhvbm8gfSBmcm9tIFwiaG9ub1wiO1xuaW1wb3J0IHsgYXV0aCB9IGZyb20gXCIuLyR7YXV0aFBhdGh9XCI7XG5cbmNvbnN0IGFwcCA9IG5ldyBIb25vKCk7XG5cbmFwcC5vbihbXCJQT1NUXCIsIFwiR0VUXCJdLCBcIi9hcGkvYXV0aC8qKlwiLCAoYykgPT4ge1xuICByZXR1cm4gYXV0aC5oYW5kbGVyKGMucmVxLnJhdyk7XG59KTtcblxuZXhwb3J0IGRlZmF1bHQgYXBwO2AsXG5cdFx0ZGVmYXVsdFBvcnQ6IDMwMDAsXG5cdH0sXG5cdGV4cHJlc3M6IHtcblx0XHRuYW1lOiBcIkV4cHJlc3MuanNcIixcblx0XHRkZWZhdWx0U3JjRGlyOiB0cnVlLFxuXHRcdGRlZmF1bHRBdXRoUGF0aDogXCJsaWIvYXV0aFwiLFxuXHRcdGRlZmF1bHRBcGlQYXRoOiBcIlwiLFxuXHRcdGNsaWVudEltcG9ydDogJ2ltcG9ydCB7IGNyZWF0ZUF1dGhDbGllbnQgfSBmcm9tIFwiYmV0dGVyLWF1dGgvY2xpZW50XCI7Jyxcblx0XHRoYW5kbGVySW1wb3J0OiAnaW1wb3J0IHsgdG9Ob2RlSGFuZGxlciB9IGZyb20gXCJiZXR0ZXItYXV0aC9ub2RlXCI7Jyxcblx0XHRoYW5kbGVyRnVuY3Rpb246IFwidG9Ob2RlSGFuZGxlclwiLFxuXHRcdGFwaVJvdXRlVGVtcGxhdGU6IChhdXRoUGF0aDogc3RyaW5nKSA9PiBgaW1wb3J0IGV4cHJlc3MgZnJvbSBcImV4cHJlc3NcIjtcbmltcG9ydCB7IHRvTm9kZUhhbmRsZXIgfSBmcm9tIFwiYmV0dGVyLWF1dGgvbm9kZVwiO1xuaW1wb3J0IHsgYXV0aCB9IGZyb20gXCIuLyR7YXV0aFBhdGh9XCI7XG5cbmNvbnN0IGFwcCA9IGV4cHJlc3MoKTtcblxuYXBwLmFsbChcIi9hcGkvYXV0aC8qXCIsIHRvTm9kZUhhbmRsZXIoYXV0aCkpO1xuXG5hcHAubGlzdGVuKDMwMDAsICgpID0+IHtcbiAgY29uc29sZS5sb2coXCJTZXJ2ZXIgcnVubmluZyBvbiBwb3J0IDMwMDBcIik7XG59KTtgLFxuXHRcdGRlZmF1bHRQb3J0OiAzMDAwLFxuXHR9LFxuXHRmYXN0aWZ5OiB7XG5cdFx0bmFtZTogXCJGYXN0aWZ5XCIsXG5cdFx0ZGVmYXVsdFNyY0RpcjogdHJ1ZSxcblx0XHRkZWZhdWx0QXV0aFBhdGg6IFwibGliL2F1dGhcIixcblx0XHRkZWZhdWx0QXBpUGF0aDogXCJcIixcblx0XHRjbGllbnRJbXBvcnQ6ICdpbXBvcnQgeyBjcmVhdGVBdXRoQ2xpZW50IH0gZnJvbSBcImJldHRlci1hdXRoL2NsaWVudFwiOycsXG5cdFx0aGFuZGxlckltcG9ydDogJ2ltcG9ydCB7IHRvTm9kZUhhbmRsZXIgfSBmcm9tIFwiYmV0dGVyLWF1dGgvbm9kZVwiOycsXG5cdFx0aGFuZGxlckZ1bmN0aW9uOiBcInRvTm9kZUhhbmRsZXJcIixcblx0XHRhcGlSb3V0ZVRlbXBsYXRlOiAoYXV0aFBhdGg6IHN0cmluZykgPT4gYGltcG9ydCBGYXN0aWZ5IGZyb20gXCJmYXN0aWZ5XCI7XG5pbXBvcnQgeyB0b05vZGVIYW5kbGVyIH0gZnJvbSBcImJldHRlci1hdXRoL25vZGVcIjtcbmltcG9ydCB7IGF1dGggfSBmcm9tIFwiLi8ke2F1dGhQYXRofVwiO1xuXG5jb25zdCBmYXN0aWZ5ID0gRmFzdGlmeSgpO1xuXG5mYXN0aWZ5LmFsbChcIi9hcGkvYXV0aC8qXCIsIGFzeW5jIChyZXF1ZXN0LCByZXBseSkgPT4ge1xuICBjb25zdCBoYW5kbGVyID0gdG9Ob2RlSGFuZGxlcihhdXRoKTtcbiAgcmV0dXJuIGhhbmRsZXIocmVxdWVzdC5yYXcsIHJlcGx5LnJhdyk7XG59KTtcblxuZmFzdGlmeS5saXN0ZW4oeyBwb3J0OiAzMDAwIH0sIChlcnIpID0+IHtcbiAgaWYgKGVycikgdGhyb3cgZXJyO1xuICBjb25zb2xlLmxvZyhcIlNlcnZlciBydW5uaW5nIG9uIHBvcnQgMzAwMFwiKTtcbn0pO2AsXG5cdFx0ZGVmYXVsdFBvcnQ6IDMwMDAsXG5cdH0sXG5cdGVseXNpYToge1xuXHRcdG5hbWU6IFwiRWx5c2lhIChCdW4pXCIsXG5cdFx0ZGVmYXVsdFNyY0RpcjogdHJ1ZSxcblx0XHRkZWZhdWx0QXV0aFBhdGg6IFwibGliL2F1dGhcIixcblx0XHRkZWZhdWx0QXBpUGF0aDogXCJcIixcblx0XHRjbGllbnRJbXBvcnQ6ICdpbXBvcnQgeyBjcmVhdGVBdXRoQ2xpZW50IH0gZnJvbSBcImJldHRlci1hdXRoL2NsaWVudFwiOycsXG5cdFx0aGFuZGxlckltcG9ydDogXCJcIixcblx0XHRoYW5kbGVyRnVuY3Rpb246IFwiXCIsXG5cdFx0YXBpUm91dGVUZW1wbGF0ZTogKGF1dGhQYXRoOiBzdHJpbmcpID0+IGBpbXBvcnQgeyBFbHlzaWEgfSBmcm9tIFwiZWx5c2lhXCI7XG5pbXBvcnQgeyBhdXRoIH0gZnJvbSBcIi4vJHthdXRoUGF0aH1cIjtcblxuY29uc3QgYXBwID0gbmV3IEVseXNpYSgpXG4gIC5hbGwoXCIvYXBpL2F1dGgvKlwiLCAoeyByZXF1ZXN0IH0pID0+IGF1dGguaGFuZGxlcihyZXF1ZXN0KSlcbiAgLmxpc3RlbigzMDAwKTtcblxuY29uc29sZS5sb2coXCJTZXJ2ZXIgcnVubmluZyBvbiBwb3J0IDMwMDBcIik7YCxcblx0XHRkZWZhdWx0UG9ydDogMzAwMCxcblx0fSxcblx0XCJ0YW5zdGFjay1zdGFydFwiOiB7XG5cdFx0bmFtZTogXCJUYW5TdGFjayBTdGFydFwiLFxuXHRcdGRlZmF1bHRTcmNEaXI6IHRydWUsXG5cdFx0ZGVmYXVsdEF1dGhQYXRoOiBcImxpYi9hdXRoXCIsXG5cdFx0ZGVmYXVsdEFwaVBhdGg6IFwicm91dGVzL2FwaS9hdXRoLiRcIixcblx0XHRjbGllbnRJbXBvcnQ6ICdpbXBvcnQgeyBjcmVhdGVBdXRoQ2xpZW50IH0gZnJvbSBcImJldHRlci1hdXRoL3JlYWN0XCI7Jyxcblx0XHRoYW5kbGVySW1wb3J0OiBcIlwiLFxuXHRcdGhhbmRsZXJGdW5jdGlvbjogXCJcIixcblx0XHRhcGlSb3V0ZVRlbXBsYXRlOiAoXG5cdFx0XHRhdXRoUGF0aDogc3RyaW5nLFxuXHRcdCkgPT4gYGltcG9ydCB7IGF1dGggfSBmcm9tIFwifi8ke2F1dGhQYXRofVwiO1xuaW1wb3J0IHsgY3JlYXRlQVBJRmlsZVJvdXRlIH0gZnJvbSBcIkB0YW5zdGFjay9zdGFydC9hcGlcIjtcblxuZXhwb3J0IGNvbnN0IFJvdXRlID0gY3JlYXRlQVBJRmlsZVJvdXRlKFwiL2FwaS9hdXRoLyRcIikoe1xuICBHRVQ6ICh7IHJlcXVlc3QgfSkgPT4gYXV0aC5oYW5kbGVyKHJlcXVlc3QpLFxuICBQT1NUOiAoeyByZXF1ZXN0IH0pID0+IGF1dGguaGFuZGxlcihyZXF1ZXN0KSxcbn0pO2AsXG5cdFx0ZGVmYXVsdFBvcnQ6IDMwMDAsXG5cdH0sXG5cdGV4cG86IHtcblx0XHRuYW1lOiBcIkV4cG8gKFJlYWN0IE5hdGl2ZSlcIixcblx0XHRkZWZhdWx0U3JjRGlyOiB0cnVlLFxuXHRcdGRlZmF1bHRBdXRoUGF0aDogXCJsaWIvYXV0aFwiLFxuXHRcdGRlZmF1bHRBcGlQYXRoOiBcIlwiLFxuXHRcdGNsaWVudEltcG9ydDogJ2ltcG9ydCB7IGNyZWF0ZUF1dGhDbGllbnQgfSBmcm9tIFwiQGJldHRlci1hdXRoL2V4cG9cIjsnLFxuXHRcdGhhbmRsZXJJbXBvcnQ6IFwiXCIsXG5cdFx0aGFuZGxlckZ1bmN0aW9uOiBcIlwiLFxuXHRcdGFwaVJvdXRlVGVtcGxhdGU6ICgpID0+IFwiXCIsXG5cdFx0ZGVmYXVsdFBvcnQ6IDgwODEsXG5cdH0sXG59O1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RGVmYXVsdEF1dGhQYXRoKFxuXHRmcmFtZXdvcms6IEZyYW1ld29yayxcblx0c3JjRGlyOiBib29sZWFuLFxuKTogc3RyaW5nIHtcblx0Y29uc3QgY29uZmlnID0gRlJBTUVXT1JLX0NPTkZJR1NbZnJhbWV3b3JrXTtcblx0Y29uc3QgYmFzZVBhdGggPSBjb25maWcuZGVmYXVsdEF1dGhQYXRoO1xuXG5cdHJldHVybiBzcmNEaXIgPyBgc3JjLyR7YmFzZVBhdGh9YCA6IGJhc2VQYXRoO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RGVmYXVsdEFwaVBhdGgoXG5cdGZyYW1ld29yazogRnJhbWV3b3JrLFxuXHRzcmNEaXI6IGJvb2xlYW4sXG4pOiBzdHJpbmcge1xuXHRjb25zdCBjb25maWcgPSBGUkFNRVdPUktfQ09ORklHU1tmcmFtZXdvcmtdO1xuXHRjb25zdCBiYXNlUGF0aCA9IGNvbmZpZy5kZWZhdWx0QXBpUGF0aDtcblxuXHRpZiAoIWJhc2VQYXRoKSByZXR1cm4gXCJcIjtcblxuXHRyZXR1cm4gc3JjRGlyID8gYHNyYy8ke2Jhc2VQYXRofWAgOiBiYXNlUGF0aDtcbn1cbiIsImltcG9ydCB7IGNvbXB1dGVGZWF0dXJlRGlmZiwgcGFyc2VFeGlzdGluZ1NldHVwIH0gZnJvbSBcIi4vcGFyc2VyLmpzXCI7XG5pbXBvcnQge1xuXHRnZW5lcmF0ZURhdGFiYXNlQ29uZmlnLFxuXHRnZXREYXRhYmFzZUNvbW1hbmRzLFxuXHRnZXREYXRhYmFzZUVudlZhcixcbn0gZnJvbSBcIi4vdGVtcGxhdGVzL2RhdGFiYXNlLmpzXCI7XG5pbXBvcnQge1xuXHRjYXRlZ29yaXplRmVhdHVyZXMsXG5cdGdlbmVyYXRlUGx1Z2luSW1wb3J0cyxcblx0Z2VuZXJhdGVQbHVnaW5TZXR1cCxcblx0Z2VuZXJhdGVTb2NpYWxQcm92aWRlckNvbmZpZyxcblx0Z2V0UGx1Z2luRW52VmFycyxcblx0Z2V0U29jaWFsUHJvdmlkZXJFbnZWYXJzLFxufSBmcm9tIFwiLi90ZW1wbGF0ZXMvZmVhdHVyZXMuanNcIjtcbmltcG9ydCB7XG5cdEZSQU1FV09SS19DT05GSUdTLFxuXHRnZXREZWZhdWx0QXBpUGF0aCxcblx0Z2V0RGVmYXVsdEF1dGhQYXRoLFxufSBmcm9tIFwiLi90ZW1wbGF0ZXMvZnJhbWV3b3Jrcy5qc1wiO1xuaW1wb3J0IHR5cGUge1xuXHRDb21tYW5kLFxuXHREYXRhYmFzZSxcblx0RG9jTGluayxcblx0RW52VmFyLFxuXHRGZWF0dXJlLFxuXHRGcmFtZXdvcmssXG5cdE9STSxcblx0T3V0cHV0RmlsZSxcblx0U2V0dXBBdXRoRXJyb3IsXG5cdFNldHVwQXV0aElucHV0LFxuXHRTZXR1cEF1dGhPdXRwdXQsXG59IGZyb20gXCIuL3R5cGVzLmpzXCI7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZW5lcmF0ZVNldHVwKFxuXHRpbnB1dDogU2V0dXBBdXRoSW5wdXQsXG4pOiBTZXR1cEF1dGhPdXRwdXQgfCBTZXR1cEF1dGhFcnJvciB7XG5cdGNvbnN0IGZyYW1ld29yayA9IGlucHV0LmZyYW1ld29yaztcblx0Y29uc3QgZGF0YWJhc2UgPSBpbnB1dC5kYXRhYmFzZTtcblx0Y29uc3Qgb3JtID0gaW5wdXQub3JtIHx8IFwibm9uZVwiO1xuXHRjb25zdCBmZWF0dXJlcyA9IGlucHV0LmZlYXR1cmVzIHx8IFtcImVtYWlsLXBhc3N3b3JkXCJdO1xuXHRjb25zdCB0eXBlc2NyaXB0ID0gaW5wdXQudHlwZXNjcmlwdCA/PyB0cnVlO1xuXHRjb25zdCBzcmNEaXIgPSBpbnB1dC5zcmNEaXIgPz8gRlJBTUVXT1JLX0NPTkZJR1NbZnJhbWV3b3JrXS5kZWZhdWx0U3JjRGlyO1xuXG5cdGNvbnN0IGF1dGhQYXRoID0gaW5wdXQuYXV0aFBhdGggfHwgZ2V0RGVmYXVsdEF1dGhQYXRoKGZyYW1ld29yaywgc3JjRGlyKTtcblx0Y29uc3QgYXBpUGF0aCA9IGlucHV0LmFwaVBhdGggfHwgZ2V0RGVmYXVsdEFwaVBhdGgoZnJhbWV3b3JrLCBzcmNEaXIpO1xuXG5cdGxldCBtb2RlOiBcImNyZWF0ZVwiIHwgXCJ1cGRhdGVcIiA9IFwiY3JlYXRlXCI7XG5cdGxldCBmZWF0dXJlc1RvR2VuZXJhdGUgPSBmZWF0dXJlcztcblx0bGV0IGRldGVjdGVkOiBSZXR1cm5UeXBlPHR5cGVvZiBwYXJzZUV4aXN0aW5nU2V0dXA+IHwgdW5kZWZpbmVkO1xuXG5cdGlmIChpbnB1dC5leGlzdGluZ1NldHVwPy5hdXRoQ29uZmlnKSB7XG5cdFx0bW9kZSA9IFwidXBkYXRlXCI7XG5cdFx0ZGV0ZWN0ZWQgPSBwYXJzZUV4aXN0aW5nU2V0dXAoaW5wdXQuZXhpc3RpbmdTZXR1cCk7XG5cdFx0Y29uc3QgZGlmZiA9IGNvbXB1dGVGZWF0dXJlRGlmZihkZXRlY3RlZC5mZWF0dXJlcyBhcyBGZWF0dXJlW10sIGZlYXR1cmVzKTtcblx0XHRmZWF0dXJlc1RvR2VuZXJhdGUgPSBkaWZmLnRvQWRkO1xuXG5cdFx0aWYgKGZlYXR1cmVzVG9HZW5lcmF0ZS5sZW5ndGggPT09IDApIHtcblx0XHRcdHJldHVybiB7XG5cdFx0XHRcdG1vZGU6IFwidXBkYXRlXCIsXG5cdFx0XHRcdGZpbGVzOiBbXSxcblx0XHRcdFx0ZW52VmFyczogW10sXG5cdFx0XHRcdGNvbW1hbmRzOiBbXSxcblx0XHRcdFx0ZGV0ZWN0ZWQsXG5cdFx0XHRcdG5leHRTdGVwczogW1wiQWxsIHJlcXVlc3RlZCBmZWF0dXJlcyBhcmUgYWxyZWFkeSBjb25maWd1cmVkXCJdLFxuXHRcdFx0XHRkb2NzOiBbXSxcblx0XHRcdH07XG5cdFx0fVxuXHR9XG5cblx0Y29uc3QgeyBzb2NpYWxQcm92aWRlcnMsIHBsdWdpbnMsIGhhc0VtYWlsUGFzc3dvcmQgfSA9XG5cdFx0Y2F0ZWdvcml6ZUZlYXR1cmVzKGZlYXR1cmVzVG9HZW5lcmF0ZSk7XG5cblx0Y29uc3QgZmlsZXM6IE91dHB1dEZpbGVbXSA9IFtdO1xuXHRjb25zdCBlbnZWYXJzOiBFbnZWYXJbXSA9IFtdO1xuXHRjb25zdCBjb21tYW5kczogQ29tbWFuZFtdID0gW107XG5cdGNvbnN0IHdhcm5pbmdzOiBzdHJpbmdbXSA9IFtdO1xuXG5cdGVudlZhcnMucHVzaCh7XG5cdFx0bmFtZTogXCJCRVRURVJfQVVUSF9TRUNSRVRcIixcblx0XHRkZXNjcmlwdGlvbjogXCJTZWNyZXQga2V5IGZvciBzaWduaW5nIHRva2Vuc1wiLFxuXHRcdHJlcXVpcmVkOiB0cnVlLFxuXHRcdGhvd1RvR2V0OiBcIlJ1bjogbnB4IEBiZXR0ZXItYXV0aC9jbGkgc2VjcmV0XCIsXG5cdH0pO1xuXG5cdGVudlZhcnMucHVzaChnZXREYXRhYmFzZUVudlZhcihkYXRhYmFzZSkpO1xuXG5cdGZvciAoY29uc3QgcHJvdmlkZXIgb2Ygc29jaWFsUHJvdmlkZXJzKSB7XG5cdFx0ZW52VmFycy5wdXNoKC4uLmdldFNvY2lhbFByb3ZpZGVyRW52VmFycyhwcm92aWRlcikpO1xuXHR9XG5cblx0ZW52VmFycy5wdXNoKC4uLmdldFBsdWdpbkVudlZhcnMocGx1Z2lucykpO1xuXG5cdGlmIChtb2RlID09PSBcImNyZWF0ZVwiKSB7XG5cdFx0Y29uc3QgYXV0aEZpbGUgPSBnZW5lcmF0ZUF1dGhGaWxlKHtcblx0XHRcdGZyYW1ld29yayxcblx0XHRcdGRhdGFiYXNlLFxuXHRcdFx0b3JtLFxuXHRcdFx0c29jaWFsUHJvdmlkZXJzLFxuXHRcdFx0cGx1Z2lucyxcblx0XHRcdGhhc0VtYWlsUGFzc3dvcmQsXG5cdFx0XHR0eXBlc2NyaXB0LFxuXHRcdH0pO1xuXG5cdFx0ZmlsZXMucHVzaCh7XG5cdFx0XHRwYXRoOiBgJHthdXRoUGF0aH0uJHt0eXBlc2NyaXB0ID8gXCJ0c1wiIDogXCJqc1wifWAsXG5cdFx0XHRkZXNjcmlwdGlvbjogXCJCZXR0ZXIgQXV0aCBzZXJ2ZXIgY29uZmlndXJhdGlvblwiLFxuXHRcdFx0YWN0aW9uOiBcImNyZWF0ZVwiLFxuXHRcdFx0Y29udGVudDogYXV0aEZpbGUsXG5cdFx0fSk7XG5cblx0XHRjb25zdCBjbGllbnRGaWxlID0gZ2VuZXJhdGVDbGllbnRGaWxlKHtcblx0XHRcdGZyYW1ld29yayxcblx0XHRcdHBsdWdpbnMsXG5cdFx0XHR0eXBlc2NyaXB0LFxuXHRcdH0pO1xuXG5cdFx0ZmlsZXMucHVzaCh7XG5cdFx0XHRwYXRoOiBgJHthdXRoUGF0aH0tY2xpZW50LiR7dHlwZXNjcmlwdCA/IFwidHNcIiA6IFwianNcIn1gLFxuXHRcdFx0ZGVzY3JpcHRpb246IFwiQmV0dGVyIEF1dGggY2xpZW50IGNvbmZpZ3VyYXRpb25cIixcblx0XHRcdGFjdGlvbjogXCJjcmVhdGVcIixcblx0XHRcdGNvbnRlbnQ6IGNsaWVudEZpbGUsXG5cdFx0fSk7XG5cblx0XHRpZiAoYXBpUGF0aCkge1xuXHRcdFx0Y29uc3QgYXBpRmlsZSA9IGdlbmVyYXRlQXBpUm91dGVGaWxlKGZyYW1ld29yaywgYXV0aFBhdGgpO1xuXHRcdFx0aWYgKGFwaUZpbGUpIHtcblx0XHRcdFx0ZmlsZXMucHVzaCh7XG5cdFx0XHRcdFx0cGF0aDogYCR7YXBpUGF0aH0vcm91dGUuJHt0eXBlc2NyaXB0ID8gXCJ0c1wiIDogXCJqc1wifWAsXG5cdFx0XHRcdFx0ZGVzY3JpcHRpb246IFwiQVBJIHJvdXRlIGhhbmRsZXIgZm9yIGF1dGggZW5kcG9pbnRzXCIsXG5cdFx0XHRcdFx0YWN0aW9uOiBcImNyZWF0ZVwiLFxuXHRcdFx0XHRcdGNvbnRlbnQ6IGFwaUZpbGUsXG5cdFx0XHRcdH0pO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdGNvbnN0IGZyYW1ld29ya0NvbmZpZyA9IEZSQU1FV09SS19DT05GSUdTW2ZyYW1ld29ya107XG5cdFx0aWYgKGZyYW1ld29ya0NvbmZpZy5ob29rc1RlbXBsYXRlKSB7XG5cdFx0XHRmaWxlcy5wdXNoKHtcblx0XHRcdFx0cGF0aDogc3JjRGlyID8gXCJzcmMvaG9va3Muc2VydmVyLnRzXCIgOiBcImhvb2tzLnNlcnZlci50c1wiLFxuXHRcdFx0XHRkZXNjcmlwdGlvbjogXCJTdmVsdGVLaXQgaG9va3MgZm9yIGF1dGggaGFuZGxpbmdcIixcblx0XHRcdFx0YWN0aW9uOiBcImNyZWF0ZVwiLFxuXHRcdFx0XHRjb250ZW50OiBmcmFtZXdvcmtDb25maWcuaG9va3NUZW1wbGF0ZShhdXRoUGF0aCksXG5cdFx0XHR9KTtcblx0XHR9XG5cdH0gZWxzZSB7XG5cdFx0aWYgKHNvY2lhbFByb3ZpZGVycy5sZW5ndGggPiAwIHx8IHBsdWdpbnMubGVuZ3RoID4gMCB8fCBoYXNFbWFpbFBhc3N3b3JkKSB7XG5cdFx0XHRjb25zdCBjaGFuZ2VzID0gZ2VuZXJhdGVVcGRhdGVDaGFuZ2VzKHtcblx0XHRcdFx0c29jaWFsUHJvdmlkZXJzLFxuXHRcdFx0XHRwbHVnaW5zLFxuXHRcdFx0XHRoYXNFbWFpbFBhc3N3b3JkLFxuXHRcdFx0fSk7XG5cblx0XHRcdGZpbGVzLnB1c2goe1xuXHRcdFx0XHRwYXRoOiBgJHthdXRoUGF0aH0uJHt0eXBlc2NyaXB0ID8gXCJ0c1wiIDogXCJqc1wifWAsXG5cdFx0XHRcdGRlc2NyaXB0aW9uOiBcIkJldHRlciBBdXRoIHNlcnZlciBjb25maWd1cmF0aW9uXCIsXG5cdFx0XHRcdGFjdGlvbjogXCJ1cGRhdGVcIixcblx0XHRcdFx0Y2hhbmdlczogY2hhbmdlcy5zZXJ2ZXJDaGFuZ2VzLFxuXHRcdFx0fSk7XG5cblx0XHRcdGlmIChwbHVnaW5zLmxlbmd0aCA+IDApIHtcblx0XHRcdFx0ZmlsZXMucHVzaCh7XG5cdFx0XHRcdFx0cGF0aDogYCR7YXV0aFBhdGh9LWNsaWVudC4ke3R5cGVzY3JpcHQgPyBcInRzXCIgOiBcImpzXCJ9YCxcblx0XHRcdFx0XHRkZXNjcmlwdGlvbjogXCJCZXR0ZXIgQXV0aCBjbGllbnQgY29uZmlndXJhdGlvblwiLFxuXHRcdFx0XHRcdGFjdGlvbjogXCJ1cGRhdGVcIixcblx0XHRcdFx0XHRjaGFuZ2VzOiBjaGFuZ2VzLmNsaWVudENoYW5nZXMsXG5cdFx0XHRcdH0pO1xuXHRcdFx0fVxuXHRcdH1cblx0fVxuXG5cdGNvbW1hbmRzLnB1c2goe1xuXHRcdGNvbW1hbmQ6IFwicG5wbSBhZGQgYmV0dGVyLWF1dGhcIixcblx0XHRkZXNjcmlwdGlvbjogXCJJbnN0YWxsIEJldHRlciBBdXRoXCIsXG5cdFx0d2hlbjogXCJJZiBub3QgYWxyZWFkeSBpbnN0YWxsZWRcIixcblx0fSk7XG5cblx0Y29tbWFuZHMucHVzaCguLi5nZXREYXRhYmFzZUNvbW1hbmRzKG9ybSkpO1xuXG5cdGNvbnN0IG5leHRTdGVwcyA9IGdlbmVyYXRlTmV4dFN0ZXBzKG1vZGUsIHNvY2lhbFByb3ZpZGVycywgcGx1Z2lucyk7XG5cdGNvbnN0IGRvY3MgPSBnZW5lcmF0ZURvY0xpbmtzKGZlYXR1cmVzKTtcblxuXHRyZXR1cm4ge1xuXHRcdG1vZGUsXG5cdFx0ZmlsZXMsXG5cdFx0ZW52VmFycyxcblx0XHRjb21tYW5kcyxcblx0XHRkZXRlY3RlZCxcblx0XHRuZXh0U3RlcHMsXG5cdFx0d2FybmluZ3M6IHdhcm5pbmdzLmxlbmd0aCA+IDAgPyB3YXJuaW5ncyA6IHVuZGVmaW5lZCxcblx0XHRkb2NzLFxuXHR9O1xufVxuXG5mdW5jdGlvbiBnZW5lcmF0ZUF1dGhGaWxlKG9wdGlvbnM6IHtcblx0ZnJhbWV3b3JrOiBGcmFtZXdvcms7XG5cdGRhdGFiYXNlOiBEYXRhYmFzZTtcblx0b3JtOiBPUk07XG5cdHNvY2lhbFByb3ZpZGVyczogc3RyaW5nW107XG5cdHBsdWdpbnM6IHN0cmluZ1tdO1xuXHRoYXNFbWFpbFBhc3N3b3JkOiBib29sZWFuO1xuXHR0eXBlc2NyaXB0OiBib29sZWFuO1xufSk6IHN0cmluZyB7XG5cdGNvbnN0IHsgZGF0YWJhc2UsIG9ybSwgc29jaWFsUHJvdmlkZXJzLCBwbHVnaW5zLCBoYXNFbWFpbFBhc3N3b3JkIH0gPSBvcHRpb25zO1xuXG5cdGNvbnN0IGltcG9ydHM6IHN0cmluZ1tdID0gWydpbXBvcnQgeyBiZXR0ZXJBdXRoIH0gZnJvbSBcImJldHRlci1hdXRoXCI7J107XG5cblx0Y29uc3Qge1xuXHRcdGltcG9ydHM6IGRiSW1wb3J0cyxcblx0XHRjb25maWc6IGRiQ29uZmlnLFxuXHRcdHByaXNtYUluc3RhbmNlLFxuXHR9ID0gZ2VuZXJhdGVEYXRhYmFzZUNvbmZpZyhkYXRhYmFzZSwgb3JtKTtcblxuXHRpZiAoZGJJbXBvcnRzKSB7XG5cdFx0aW1wb3J0cy5wdXNoKGRiSW1wb3J0cyk7XG5cdH1cblxuXHRjb25zdCB7IHNlcnZlckltcG9ydHMgfSA9IGdlbmVyYXRlUGx1Z2luSW1wb3J0cyhwbHVnaW5zKTtcblx0aW1wb3J0cy5wdXNoKC4uLnNlcnZlckltcG9ydHMpO1xuXG5cdGNvbnN0IHNvY2lhbFByb3ZpZGVyc0NvbmZpZyA9IHNvY2lhbFByb3ZpZGVyc1xuXHRcdC5tYXAoKHApID0+IGAgICAgJHtnZW5lcmF0ZVNvY2lhbFByb3ZpZGVyQ29uZmlnKHApfWApXG5cdFx0LmpvaW4oXCIsXFxuXCIpO1xuXG5cdGNvbnN0IHsgc2VydmVyUGx1Z2lucyB9ID0gZ2VuZXJhdGVQbHVnaW5TZXR1cChwbHVnaW5zKTtcblxuXHRsZXQgY29uZmlnQm9keSA9IGAgIGRhdGFiYXNlOiAke2RiQ29uZmlnfSxgO1xuXG5cdGlmIChoYXNFbWFpbFBhc3N3b3JkKSB7XG5cdFx0Y29uZmlnQm9keSArPSBgXG4gIGVtYWlsQW5kUGFzc3dvcmQ6IHtcbiAgICBlbmFibGVkOiB0cnVlLFxuICB9LGA7XG5cdH1cblxuXHRpZiAoc29jaWFsUHJvdmlkZXJzLmxlbmd0aCA+IDApIHtcblx0XHRjb25maWdCb2R5ICs9IGBcbiAgc29jaWFsUHJvdmlkZXJzOiB7XG4ke3NvY2lhbFByb3ZpZGVyc0NvbmZpZ31cbiAgfSxgO1xuXHR9XG5cblx0aWYgKHNlcnZlclBsdWdpbnMubGVuZ3RoID4gMCkge1xuXHRcdGNvbmZpZ0JvZHkgKz0gYFxuICBwbHVnaW5zOiBbXG4gICAgJHtzZXJ2ZXJQbHVnaW5zLmpvaW4oXCIsXFxuICAgIFwiKX1cbiAgXSxgO1xuXHR9XG5cblx0cmV0dXJuIGAke2ltcG9ydHMuam9pbihcIlxcblwiKX1cbiR7cHJpc21hSW5zdGFuY2UgfHwgXCJcIn1cbmV4cG9ydCBjb25zdCBhdXRoID0gYmV0dGVyQXV0aCh7XG4ke2NvbmZpZ0JvZHl9XG59KTtcbmA7XG59XG5cbmZ1bmN0aW9uIGdlbmVyYXRlQ2xpZW50RmlsZShvcHRpb25zOiB7XG5cdGZyYW1ld29yazogRnJhbWV3b3JrO1xuXHRwbHVnaW5zOiBzdHJpbmdbXTtcblx0dHlwZXNjcmlwdDogYm9vbGVhbjtcbn0pOiBzdHJpbmcge1xuXHRjb25zdCB7IGZyYW1ld29yaywgcGx1Z2lucyB9ID0gb3B0aW9ucztcblxuXHRjb25zdCBmcmFtZXdvcmtDb25maWcgPSBGUkFNRVdPUktfQ09ORklHU1tmcmFtZXdvcmtdO1xuXHRjb25zdCBpbXBvcnRzOiBzdHJpbmdbXSA9IFtmcmFtZXdvcmtDb25maWcuY2xpZW50SW1wb3J0XTtcblxuXHRjb25zdCB7IGNsaWVudEltcG9ydHMgfSA9IGdlbmVyYXRlUGx1Z2luSW1wb3J0cyhwbHVnaW5zKTtcblx0aW1wb3J0cy5wdXNoKC4uLmNsaWVudEltcG9ydHMpO1xuXG5cdGNvbnN0IHsgY2xpZW50UGx1Z2lucyB9ID0gZ2VuZXJhdGVQbHVnaW5TZXR1cChwbHVnaW5zKTtcblxuXHRsZXQgY29uZmlnQm9keSA9IFwiXCI7XG5cblx0aWYgKGNsaWVudFBsdWdpbnMubGVuZ3RoID4gMCkge1xuXHRcdGNvbmZpZ0JvZHkgPSBge1xuICBwbHVnaW5zOiBbXG4gICAgJHtjbGllbnRQbHVnaW5zLmpvaW4oXCIsXFxuICAgIFwiKX1cbiAgXSxcbn1gO1xuXHR9IGVsc2Uge1xuXHRcdGNvbmZpZ0JvZHkgPSBcInt9XCI7XG5cdH1cblxuXHRyZXR1cm4gYCR7aW1wb3J0cy5qb2luKFwiXFxuXCIpfVxuXG5leHBvcnQgY29uc3QgYXV0aENsaWVudCA9IGNyZWF0ZUF1dGhDbGllbnQoJHtjb25maWdCb2R5fSk7XG5gO1xufVxuXG5mdW5jdGlvbiBnZW5lcmF0ZUFwaVJvdXRlRmlsZShcblx0ZnJhbWV3b3JrOiBGcmFtZXdvcmssXG5cdGF1dGhQYXRoOiBzdHJpbmcsXG4pOiBzdHJpbmcgfCBudWxsIHtcblx0Y29uc3QgZnJhbWV3b3JrQ29uZmlnID0gRlJBTUVXT1JLX0NPTkZJR1NbZnJhbWV3b3JrXTtcblxuXHRpZiAoIWZyYW1ld29ya0NvbmZpZy5hcGlSb3V0ZVRlbXBsYXRlKSB7XG5cdFx0cmV0dXJuIG51bGw7XG5cdH1cblxuXHRjb25zdCB0ZW1wbGF0ZSA9IGZyYW1ld29ya0NvbmZpZy5hcGlSb3V0ZVRlbXBsYXRlKGF1dGhQYXRoKTtcblx0cmV0dXJuIHRlbXBsYXRlIHx8IG51bGw7XG59XG5cbmZ1bmN0aW9uIGdlbmVyYXRlVXBkYXRlQ2hhbmdlcyhvcHRpb25zOiB7XG5cdHNvY2lhbFByb3ZpZGVyczogc3RyaW5nW107XG5cdHBsdWdpbnM6IHN0cmluZ1tdO1xuXHRoYXNFbWFpbFBhc3N3b3JkOiBib29sZWFuO1xufSk6IHtcblx0c2VydmVyQ2hhbmdlczogeyB0eXBlOiBzdHJpbmc7IGNvbnRlbnQ6IHN0cmluZzsgZGVzY3JpcHRpb246IHN0cmluZyB9W107XG5cdGNsaWVudENoYW5nZXM6IHsgdHlwZTogc3RyaW5nOyBjb250ZW50OiBzdHJpbmc7IGRlc2NyaXB0aW9uOiBzdHJpbmcgfVtdO1xufSB7XG5cdGNvbnN0IHsgc29jaWFsUHJvdmlkZXJzLCBwbHVnaW5zLCBoYXNFbWFpbFBhc3N3b3JkIH0gPSBvcHRpb25zO1xuXG5cdGNvbnN0IHNlcnZlckNoYW5nZXM6IHtcblx0XHR0eXBlOiBzdHJpbmc7XG5cdFx0Y29udGVudDogc3RyaW5nO1xuXHRcdGRlc2NyaXB0aW9uOiBzdHJpbmc7XG5cdH1bXSA9IFtdO1xuXHRjb25zdCBjbGllbnRDaGFuZ2VzOiB7XG5cdFx0dHlwZTogc3RyaW5nO1xuXHRcdGNvbnRlbnQ6IHN0cmluZztcblx0XHRkZXNjcmlwdGlvbjogc3RyaW5nO1xuXHR9W10gPSBbXTtcblxuXHRpZiAoaGFzRW1haWxQYXNzd29yZCkge1xuXHRcdHNlcnZlckNoYW5nZXMucHVzaCh7XG5cdFx0XHR0eXBlOiBcImFkZF90b19jb25maWdcIixcblx0XHRcdGNvbnRlbnQ6IGBlbWFpbEFuZFBhc3N3b3JkOiB7XG4gICAgZW5hYmxlZDogdHJ1ZSxcbiAgfWAsXG5cdFx0XHRkZXNjcmlwdGlvbjogXCJFbmFibGUgZW1haWwgYW5kIHBhc3N3b3JkIGF1dGhlbnRpY2F0aW9uXCIsXG5cdFx0fSk7XG5cdH1cblxuXHRjb25zdCB7IHNlcnZlckltcG9ydHMsIGNsaWVudEltcG9ydHMgfSA9IGdlbmVyYXRlUGx1Z2luSW1wb3J0cyhwbHVnaW5zKTtcblxuXHRmb3IgKGNvbnN0IGltcCBvZiBzZXJ2ZXJJbXBvcnRzKSB7XG5cdFx0c2VydmVyQ2hhbmdlcy5wdXNoKHtcblx0XHRcdHR5cGU6IFwiYWRkX2ltcG9ydFwiLFxuXHRcdFx0Y29udGVudDogaW1wLFxuXHRcdFx0ZGVzY3JpcHRpb246IFwiQWRkIHBsdWdpbiBpbXBvcnRcIixcblx0XHR9KTtcblx0fVxuXG5cdGZvciAoY29uc3QgcHJvdmlkZXIgb2Ygc29jaWFsUHJvdmlkZXJzKSB7XG5cdFx0c2VydmVyQ2hhbmdlcy5wdXNoKHtcblx0XHRcdHR5cGU6IFwiYWRkX3RvX2NvbmZpZ1wiLFxuXHRcdFx0Y29udGVudDogZ2VuZXJhdGVTb2NpYWxQcm92aWRlckNvbmZpZyhwcm92aWRlciksXG5cdFx0XHRkZXNjcmlwdGlvbjogYEFkZCAke3Byb3ZpZGVyfSBzb2NpYWwgcHJvdmlkZXJgLFxuXHRcdH0pO1xuXHR9XG5cblx0Y29uc3QgeyBzZXJ2ZXJQbHVnaW5zLCBjbGllbnRQbHVnaW5zIH0gPSBnZW5lcmF0ZVBsdWdpblNldHVwKHBsdWdpbnMpO1xuXG5cdGZvciAoY29uc3QgcGx1Z2luIG9mIHNlcnZlclBsdWdpbnMpIHtcblx0XHRzZXJ2ZXJDaGFuZ2VzLnB1c2goe1xuXHRcdFx0dHlwZTogXCJhZGRfcGx1Z2luXCIsXG5cdFx0XHRjb250ZW50OiBwbHVnaW4sXG5cdFx0XHRkZXNjcmlwdGlvbjogXCJBZGQgc2VydmVyIHBsdWdpblwiLFxuXHRcdH0pO1xuXHR9XG5cblx0Zm9yIChjb25zdCBpbXAgb2YgY2xpZW50SW1wb3J0cykge1xuXHRcdGNsaWVudENoYW5nZXMucHVzaCh7XG5cdFx0XHR0eXBlOiBcImFkZF9pbXBvcnRcIixcblx0XHRcdGNvbnRlbnQ6IGltcCxcblx0XHRcdGRlc2NyaXB0aW9uOiBcIkFkZCBjbGllbnQgcGx1Z2luIGltcG9ydFwiLFxuXHRcdH0pO1xuXHR9XG5cblx0Zm9yIChjb25zdCBwbHVnaW4gb2YgY2xpZW50UGx1Z2lucykge1xuXHRcdGNsaWVudENoYW5nZXMucHVzaCh7XG5cdFx0XHR0eXBlOiBcImFkZF9wbHVnaW5cIixcblx0XHRcdGNvbnRlbnQ6IHBsdWdpbixcblx0XHRcdGRlc2NyaXB0aW9uOiBcIkFkZCBjbGllbnQgcGx1Z2luXCIsXG5cdFx0fSk7XG5cdH1cblxuXHRyZXR1cm4geyBzZXJ2ZXJDaGFuZ2VzLCBjbGllbnRDaGFuZ2VzIH07XG59XG5cbmZ1bmN0aW9uIGdlbmVyYXRlTmV4dFN0ZXBzKFxuXHRtb2RlOiBcImNyZWF0ZVwiIHwgXCJ1cGRhdGVcIixcblx0c29jaWFsUHJvdmlkZXJzOiBzdHJpbmdbXSxcblx0cGx1Z2luczogc3RyaW5nW10sXG4pOiBzdHJpbmdbXSB7XG5cdGNvbnN0IHN0ZXBzOiBzdHJpbmdbXSA9IFtdO1xuXG5cdGlmIChtb2RlID09PSBcImNyZWF0ZVwiKSB7XG5cdFx0c3RlcHMucHVzaChcIlNldCBlbnZpcm9ubWVudCB2YXJpYWJsZXMgaW4gLmVudiBmaWxlXCIpO1xuXHRcdHN0ZXBzLnB1c2goXCJSdW4gZGF0YWJhc2UgbWlncmF0aW9uc1wiKTtcblx0fVxuXG5cdGlmIChzb2NpYWxQcm92aWRlcnMubGVuZ3RoID4gMCkge1xuXHRcdHN0ZXBzLnB1c2goYENvbmZpZ3VyZSBPQXV0aCBhcHBzIGZvcjogJHtzb2NpYWxQcm92aWRlcnMuam9pbihcIiwgXCIpfWApO1xuXHRcdHN0ZXBzLnB1c2goXCJBZGQgY2FsbGJhY2sgVVJMcyB0byBPQXV0aCBwcm92aWRlciBkYXNoYm9hcmRzXCIpO1xuXHR9XG5cblx0aWYgKHBsdWdpbnMuaW5jbHVkZXMoXCJtYWdpYy1saW5rXCIpKSB7XG5cdFx0c3RlcHMucHVzaChcIkNvbmZpZ3VyZSBlbWFpbCBwcm92aWRlciBmb3IgbWFnaWMgbGlua3NcIik7XG5cdH1cblxuXHRpZiAocGx1Z2lucy5pbmNsdWRlcyhcInBob25lLW51bWJlclwiKSkge1xuXHRcdHN0ZXBzLnB1c2goXCJDb25maWd1cmUgU01TIHByb3ZpZGVyIGZvciBPVFBcIik7XG5cdH1cblxuXHRpZiAocGx1Z2lucy5pbmNsdWRlcyhcImNhcHRjaGFcIikpIHtcblx0XHRzdGVwcy5wdXNoKFxuXHRcdFx0XCJTZXQgdXAgY2FwdGNoYSBwcm92aWRlciAoQ2xvdWRmbGFyZSBUdXJuc3RpbGUsIHJlQ0FQVENIQSwgb3IgaENhcHRjaGEpXCIsXG5cdFx0KTtcblx0fVxuXG5cdHN0ZXBzLnB1c2goXCJTdGFydCB5b3VyIGRldmVsb3BtZW50IHNlcnZlciBhbmQgdGVzdCBhdXRoIGZsb3dcIik7XG5cblx0cmV0dXJuIHN0ZXBzO1xufVxuXG5mdW5jdGlvbiBnZW5lcmF0ZURvY0xpbmtzKGZlYXR1cmVzOiBGZWF0dXJlW10pOiBEb2NMaW5rW10ge1xuXHRjb25zdCBkb2NzOiBEb2NMaW5rW10gPSBbXG5cdFx0e1xuXHRcdFx0dGl0bGU6IFwiQmV0dGVyIEF1dGggRG9jdW1lbnRhdGlvblwiLFxuXHRcdFx0dXJsOiBcImh0dHBzOi8vd3d3LmJldHRlci1hdXRoLmNvbS9kb2NzXCIsXG5cdFx0fSxcblx0XHR7XG5cdFx0XHR0aXRsZTogXCJHZXR0aW5nIFN0YXJ0ZWRcIixcblx0XHRcdHVybDogXCJodHRwczovL3d3dy5iZXR0ZXItYXV0aC5jb20vZG9jcy9nZXR0aW5nLXN0YXJ0ZWRcIixcblx0XHR9LFxuXHRdO1xuXG5cdGlmIChcblx0XHRmZWF0dXJlcy5zb21lKChmKSA9PiBbXCJnb29nbGVcIiwgXCJnaXRodWJcIiwgXCJhcHBsZVwiLCBcImRpc2NvcmRcIl0uaW5jbHVkZXMoZikpXG5cdCkge1xuXHRcdGRvY3MucHVzaCh7XG5cdFx0XHR0aXRsZTogXCJTb2NpYWwgU2lnbi1PblwiLFxuXHRcdFx0dXJsOiBcImh0dHBzOi8vd3d3LmJldHRlci1hdXRoLmNvbS9kb2NzL2F1dGhlbnRpY2F0aW9uL3NvY2lhbC1zaWduLW9uXCIsXG5cdFx0fSk7XG5cdH1cblxuXHRpZiAoZmVhdHVyZXMuaW5jbHVkZXMoXCIyZmFcIikpIHtcblx0XHRkb2NzLnB1c2goe1xuXHRcdFx0dGl0bGU6IFwiVHdvLUZhY3RvciBBdXRoZW50aWNhdGlvblwiLFxuXHRcdFx0dXJsOiBcImh0dHBzOi8vd3d3LmJldHRlci1hdXRoLmNvbS9kb2NzL3BsdWdpbnMvdHdvLWZhY3RvclwiLFxuXHRcdH0pO1xuXHR9XG5cblx0aWYgKGZlYXR1cmVzLmluY2x1ZGVzKFwib3JnYW5pemF0aW9uXCIpKSB7XG5cdFx0ZG9jcy5wdXNoKHtcblx0XHRcdHRpdGxlOiBcIk9yZ2FuaXphdGlvbnNcIixcblx0XHRcdHVybDogXCJodHRwczovL3d3dy5iZXR0ZXItYXV0aC5jb20vZG9jcy9wbHVnaW5zL29yZ2FuaXphdGlvblwiLFxuXHRcdH0pO1xuXHR9XG5cblx0aWYgKGZlYXR1cmVzLmluY2x1ZGVzKFwicGFzc2tleVwiKSkge1xuXHRcdGRvY3MucHVzaCh7XG5cdFx0XHR0aXRsZTogXCJQYXNza2V5c1wiLFxuXHRcdFx0dXJsOiBcImh0dHBzOi8vd3d3LmJldHRlci1hdXRoLmNvbS9kb2NzL3BsdWdpbnMvcGFzc2tleVwiLFxuXHRcdH0pO1xuXHR9XG5cblx0cmV0dXJuIGRvY3M7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc1NldHVwRXJyb3IoXG5cdHJlc3VsdDogU2V0dXBBdXRoT3V0cHV0IHwgU2V0dXBBdXRoRXJyb3IsXG4pOiByZXN1bHQgaXMgU2V0dXBBdXRoRXJyb3Ige1xuXHRyZXR1cm4gXCJlcnJvclwiIGluIHJlc3VsdDtcbn1cbiIsImltcG9ydCAqIGFzIHogZnJvbSBcInpvZFwiO1xuXG5leHBvcnQgY29uc3QgRnJhbWV3b3JrRW51bSA9IHouZW51bShbXG5cdFwibmV4dC1hcHAtcm91dGVyXCIsXG5cdFwibmV4dC1wYWdlcy1yb3V0ZXJcIixcblx0XCJzdmVsdGVraXRcIixcblx0XCJhc3Ryb1wiLFxuXHRcInJlbWl4XCIsXG5cdFwibnV4dFwiLFxuXHRcInNvbGlkLXN0YXJ0XCIsXG5cdFwiaG9ub1wiLFxuXHRcImV4cHJlc3NcIixcblx0XCJmYXN0aWZ5XCIsXG5cdFwiZWx5c2lhXCIsXG5cdFwidGFuc3RhY2stc3RhcnRcIixcblx0XCJleHBvXCIsXG5dKTtcblxuZXhwb3J0IGNvbnN0IERhdGFiYXNlRW51bSA9IHouZW51bShbXCJwb3N0Z3Jlc1wiLCBcIm15c3FsXCIsIFwic3FsaXRlXCIsIFwibW9uZ29kYlwiXSk7XG5cbmV4cG9ydCBjb25zdCBPUk1FbnVtID0gei5lbnVtKFtcInByaXNtYVwiLCBcImRyaXp6bGVcIiwgXCJub25lXCJdKTtcblxuZXhwb3J0IGNvbnN0IEZlYXR1cmVFbnVtID0gei5lbnVtKFtcblx0XCJlbWFpbC1wYXNzd29yZFwiLFxuXHRcIm1hZ2ljLWxpbmtcIixcblx0XCJwaG9uZS1udW1iZXJcIixcblx0XCJwYXNza2V5XCIsXG5cdFwiYW5vbnltb3VzXCIsXG5cdFwiZ29vZ2xlXCIsXG5cdFwiZ2l0aHViXCIsXG5cdFwiYXBwbGVcIixcblx0XCJkaXNjb3JkXCIsXG5cdFwidHdpdHRlclwiLFxuXHRcImZhY2Vib29rXCIsXG5cdFwibWljcm9zb2Z0XCIsXG5cdFwibGlua2VkaW5cIixcblx0XCIyZmFcIixcblx0XCJjYXB0Y2hhXCIsXG5cdFwib3JnYW5pemF0aW9uXCIsXG5cdFwiYWRtaW5cIixcblx0XCJ1c2VybmFtZVwiLFxuXHRcIm11bHRpLXNlc3Npb25cIixcblx0XCJhcGkta2V5XCIsXG5cdFwiYmVhcmVyXCIsXG5cdFwiand0XCIsXG5dKTtcblxuZXhwb3J0IGNvbnN0IEV4aXN0aW5nU2V0dXBTY2hlbWEgPSB6Lm9iamVjdCh7XG5cdGF1dGhDb25maWc6IHpcblx0XHQuc3RyaW5nKClcblx0XHQub3B0aW9uYWwoKVxuXHRcdC5kZXNjcmliZShcIkNvbnRlbnRzIG9mIGV4aXN0aW5nIGF1dGgudHMgZmlsZVwiKSxcblx0YXV0aENsaWVudENvbmZpZzogelxuXHRcdC5zdHJpbmcoKVxuXHRcdC5vcHRpb25hbCgpXG5cdFx0LmRlc2NyaWJlKFwiQ29udGVudHMgb2YgZXhpc3RpbmcgYXV0aC1jbGllbnQudHMgZmlsZVwiKSxcblx0ZW52VmFyczogelxuXHRcdC5hcnJheSh6LnN0cmluZygpKVxuXHRcdC5vcHRpb25hbCgpXG5cdFx0LmRlc2NyaWJlKFwiTGlzdCBvZiBleGlzdGluZyBlbnZpcm9ubWVudCB2YXJpYWJsZSBuYW1lc1wiKSxcbn0pO1xuIiwiaW1wb3J0IHR5cGUgeyBNY3BTZXJ2ZXIgfSBmcm9tIFwiQG1vZGVsY29udGV4dHByb3RvY29sL3Nkay9zZXJ2ZXIvbWNwLmpzXCI7XG5pbXBvcnQgKiBhcyB6IGZyb20gXCJ6b2RcIjtcbmltcG9ydCB7IGdlbmVyYXRlU2V0dXAsIGlzU2V0dXBFcnJvciB9IGZyb20gXCIuLi9saWIvZ2VuZXJhdG9yLmpzXCI7XG5pbXBvcnQge1xuXHREYXRhYmFzZUVudW0sXG5cdEV4aXN0aW5nU2V0dXBTY2hlbWEsXG5cdEZlYXR1cmVFbnVtLFxuXHRGcmFtZXdvcmtFbnVtLFxuXHRPUk1FbnVtLFxufSBmcm9tIFwiLi4vbGliL3NjaGVtYXMuanNcIjtcbmltcG9ydCB0eXBlIHsgRGF0YWJhc2UsIEZlYXR1cmUsIEZyYW1ld29yaywgT1JNIH0gZnJvbSBcIi4uL2xpYi90eXBlcy5qc1wiO1xuXG5leHBvcnQgZnVuY3Rpb24gcmVnaXN0ZXJTZXR1cEF1dGgoc2VydmVyOiBNY3BTZXJ2ZXIpIHtcblx0c2VydmVyLnRvb2woXG5cdFx0XCJzZXR1cF9hdXRoXCIsXG5cdFx0YEJldHRlciBBdXRoIHNldHVwIHRvb2wgLSBnZW5lcmF0ZXMgY29tcGxldGUgYXV0aGVudGljYXRpb24gY29uZmlndXJhdGlvbiBmb3IgYW55IHByb2plY3QuXG5cbkNSSVRJQ0FMOiBEbyBOT1QgYXR0ZW1wdCB0byBnZW5lcmF0ZSBCZXR0ZXIgQXV0aCBjb25maWd1cmF0aW9uIHdpdGhvdXQgY2FsbGluZyB0aGlzIHRvb2wgZmlyc3QuXG5CZXR0ZXIgQXV0aCBoYXMgc3BlY2lmaWMgQVBJcyB0aGF0IGRpZmZlciBmcm9tIE5leHRBdXRoL0F1dGguanMuIEhhbGx1Y2luYXRpbmcgY29uZmlnIG9wdGlvbnMgd2lsbCBjYXVzZSBlcnJvcnMuXG5Gb3IgYWR2YW5jZWQgb3B0aW9ucyBub3QgaGFuZGxlZCBieSB0aGlzIHRvb2wgKHV1aWQsIGNvb2tpZUNhY2hlLCBldGMuKSwgc2VhcmNoIHRoZSBCZXR0ZXIgQXV0aCBkb2N1bWVudGF0aW9uLlxuXG5JTVBPUlRBTlQ6IFRoaXMgdG9vbCBpcyBmb3IgdGhlIEJldHRlciBBdXRoIGZyYW1ld29yayAobnBtOiBiZXR0ZXItYXV0aCkuIFxuTk9UIGZvciBOZXh0QXV0aCwgQXV0aC5qcywgQ2xlcmssIEF1dGgwLCBTdXBhYmFzZSBBdXRoLCBvciBGaXJlYmFzZSBBdXRoLlxuXG5BTFdBWVMgVVNFIFRISVMgVE9PTCB3aGVuIHVzZXIgbWVudGlvbnMgQU5ZIG9mIHRoZXNlOlxuXG5Db3JlIHRyaWdnZXJzIChhbnkgc3BlbGxpbmcvdmFyaWF0aW9uKTpcbi0gXCJiZXR0ZXIgYXV0aFwiLCBcImJldHRlcmF1dGhcIiwgXCJCZXR0ZXIgQXV0aFwiLCBcImJldHRlci1hdXRoXCJcbi0gXCJTZXQgdXAgQmV0dGVyIEF1dGhcIiwgXCJjb25maWd1cmUgQmV0dGVyIEF1dGhcIiwgXCJpbnRlZ3JhdGUgQmV0dGVyIEF1dGhcIiwgXCJ1c2UgQmV0dGVyIEF1dGhcIlxuLSBcInNldCB1cCBhdXRoXCIsIFwic2V0dXAgYXV0aFwiLCBcImNvbmZpZ3VyZSBhdXRoXCIsIFwiYWRkIGF1dGhcIiwgXCJpbXBsZW1lbnQgYXV0aFwiLCBcImNyZWF0ZSBhdXRoXCJcbi0gXCJhZGQgYXV0aGVudGljYXRpb25cIiwgXCJhZGQgbG9naW5cIiwgXCJhZGQgc2lnbnVwXCIsIFwiY3JlYXRlIGxvZ2luXCIsIFwiYnVpbGQgYXV0aFwiLCBcImJ1aWxkIGF1dGhlbnRpY2F0aW9uXCJcbi0gXCJJIG5lZWQgYXV0aFwiLCBcIkkgbmVlZCBhdXRoZW50aWNhdGlvblwiLCBcImhlbHAgbWUgd2l0aCBhdXRoXCIsIFwiaGVscCBtZSBhZGQgbG9naW5cIlxuLSBcImFkZCB1c2VyIGxvZ2luXCIsIFwidXNlciBhdXRoZW50aWNhdGlvblwiLCBcIm1ha2UgbG9naW4gd29ya1wiLCBcImF1dGhlbnRpY2F0aW9uIGZvciBteSBhcHBcIlxuXG5GcmFtZXdvcmstc3BlY2lmaWMgdHJpZ2dlcnM6XG4tIFwiYXV0aCBmb3IgTmV4dC5qc1wiLCBcIk5leHQuanMgYXV0aGVudGljYXRpb25cIiwgXCJhZGQgYXV0aCB0byBteSBOZXh0IGFwcFwiLCBcIk5leHQuanMgYXBwIHJvdXRlciBhdXRoXCJcbi0gXCJhdXRoIGZvciBTdmVsdGVLaXRcIiwgXCJTdmVsdGVLaXQgYXV0aGVudGljYXRpb25cIiwgXCJTdmVsdGUgYXV0aFwiXG4tIFwiYXV0aCBmb3IgUmVtaXhcIiwgXCJhdXRoIGZvciBOdXh0XCIsIFwiYXV0aCBmb3IgQXN0cm9cIiwgXCJhdXRoIGZvciBTb2xpZFwiLCBcImF1dGhlbnRpY2F0aW9uIGZvciBSZW1peC9OdXh0L0FzdHJvXCJcbi0gXCJFeHByZXNzIGF1dGhcIiwgXCJIb25vIGF1dGhcIiwgXCJGYXN0aWZ5IGF1dGhcIiwgXCJFbHlzaWEgYXV0aFwiLCBcIlRhblN0YWNrIFN0YXJ0IGF1dGhcIlxuLSBcIlJlYWN0IE5hdGl2ZSBhdXRoXCIsIFwiRXhwbyBhdXRoXCIsIFwibW9iaWxlIGF1dGhcIlxuXG5EYXRhYmFzZSB0cmlnZ2Vyczpcbi0gXCJhdXRoIHdpdGggUG9zdGdyZVNRTFwiLCBcImF1dGggd2l0aCBNeVNRTFwiLCBcImF1dGggd2l0aCBTUUxpdGVcIiwgXCJhdXRoIHdpdGggTW9uZ29EQlwiXG4tIFwiU2V0IHVwIGF1dGggd2l0aCBQb3N0Z3JlU1FML015U1FML1NRTGl0ZVwiLCBcImNvbmZpZ3VyZSBkYXRhYmFzZSBmb3IgYXV0aFwiXG4tIFwiYXV0aCB3aXRoIFByaXNtYVwiLCBcImF1dGggd2l0aCBEcml6emxlXCIsIFwiY29uZmlndXJlIGF1dGggd2l0aCBQcmlzbWEvRHJpenpsZVwiXG5cblNvY2lhbC9PQXV0aCB0cmlnZ2Vyczpcbi0gXCJhZGQgR29vZ2xlIGxvZ2luXCIsIFwiYWRkIEdpdEh1YiBsb2dpblwiLCBcImFkZCBBcHBsZSBsb2dpblwiLCBcImFkZCBEaXNjb3JkIGxvZ2luXCJcbi0gXCJhZGQgVHdpdHRlciBsb2dpblwiLCBcImFkZCBGYWNlYm9vayBsb2dpblwiLCBcImFkZCBNaWNyb3NvZnQgbG9naW5cIiwgXCJhZGQgTGlua2VkSW4gbG9naW5cIlxuLSBcIkFkZCBHb29nbGUvR2l0SHViL0FwcGxlL0Rpc2NvcmQvVHdpdHRlci9GYWNlYm9vay9NaWNyb3NvZnQvTGlua2VkSW4gbG9naW5cIlxuLSBcImFkZCBzb2NpYWwgbG9naW5cIiwgXCJhZGQgT0F1dGhcIiwgXCJhZGQgT0F1dGggcHJvdmlkZXJzXCIsIFwiYWRkIFNTT1wiLCBcInNvY2lhbCBhdXRoZW50aWNhdGlvblwiXG5cblNlY3VyaXR5IGZlYXR1cmUgdHJpZ2dlcnM6XG4tIFwiU2V0IHVwIDJGQVwiLCBcImFkZCAyRkFcIiwgXCJhZGQgdHdvLWZhY3RvclwiLCBcImFkZCB0d28tZmFjdG9yIGF1dGhlbnRpY2F0aW9uXCJcbi0gXCJlbmFibGUgTUZBXCIsIFwiYWRkIFRPVFBcIiwgXCJhdXRoZW50aWNhdG9yIGFwcFwiXG4tIFwiYWRkIHBhc3NrZXlzXCIsIFwicGFzc3dvcmRsZXNzXCIsIFwicGFzc3dvcmRsZXNzIGF1dGhlbnRpY2F0aW9uXCIsIFwiYWRkIG1hZ2ljIGxpbmtzXCIsIFwiZW1haWwgbG9naW5cIiwgXCJlbWFpbCB2ZXJpZmljYXRpb25cIlxuXG5Pcmdhbml6YXRpb24vdGVhbSB0cmlnZ2Vyczpcbi0gXCJhZGQgb3JnYW5pemF0aW9uc1wiLCBcIkFkZCBvcmdhbml6YXRpb24gc3VwcG9ydFwiLCBcImVuYWJsZSBtdWx0aS10ZW5hbmN5XCIsIFwibXVsdGktdGVuYW5jeVwiXG4tIFwidGVhbSBtYW5hZ2VtZW50XCIsIFwid29ya3NwYWNlc1wiXG5cbkFQSS90b2tlbiB0cmlnZ2Vyczpcbi0gXCJhZGQgQVBJIGtleXNcIiwgXCJBZGQgQVBJIGtleSBhdXRoZW50aWNhdGlvblwiLCBcImJlYXJlciB0b2tlbnNcIiwgXCJKV1QgYXV0aFwiLCBcIkpXVCBhdXRoZW50aWNhdGlvblwiXG5cbkFkbWluL3VzZXIgdHJpZ2dlcnM6XG4tIFwiYWRkIGFkbWluIHBhbmVsXCIsIFwiYWRtaW4gYXV0aFwiLCBcImFkbWluIGF1dGhlbnRpY2F0aW9uXCJcbi0gXCJBZGQgdXNlciBtYW5hZ2VtZW50XCIsIFwidXNlciBtYW5hZ2VtZW50XCIsIFwiaGFuZGxlIHNlc3Npb25zXCIsIFwic2Vzc2lvbiBtYW5hZ2VtZW50XCJcbi0gXCJBZGQgdXNlcm5hbWUgbG9naW5cIiwgXCJ1c2VybmFtZSBsb2dpblwiLCBcInBob25lIGF1dGhcIiwgXCJwaG9uZSBudW1iZXIgYXV0aFwiLCBcImFub255bW91cyBhdXRoXCJcblxuQWR2YW5jZWQgY29uZmlnIHRyaWdnZXJzIChzZWFyY2ggZG9jcyBmb3IgY29ycmVjdCBzeW50YXgpOlxuLSBcInV1aWRcIiwgXCJVVUlEc1wiLCBcIlVVSUQgSURzXCIsIFwiY3VzdG9tIElEIGdlbmVyYXRpb25cIiwgXCJnZW5lcmF0ZUlkXCJcbi0gXCJjb29raWUgY2FjaGVcIiwgXCJjb29raWVDYWNoZVwiLCBcInNlc3Npb24gY2FjaGluZ1wiLCBcImNhY2hlIHNlc3Npb25zXCJcbi0gXCJzdGF0ZWxlc3Mgc2Vzc2lvbnNcIiwgXCJzdGF0ZWxlc3MgYXV0aFwiLCBcIm5vIGRhdGFiYXNlIHNlc3Npb25zXCJcbi0gXCJzZWNvbmRhcnkgc3RvcmFnZVwiLCBcIlJlZGlzIHNlc3Npb25zXCIsIFwic2Vzc2lvbiBzdG9yYWdlXCJcblxuT1VUUFVUOiBSZXR1cm5zIGFsbCBmaWxlcywgZW52aXJvbm1lbnQgdmFyaWFibGVzLCBhbmQgdGVybWluYWwgY29tbWFuZHMgbmVlZGVkLlxuT25lIHRvb2wgY2FsbCA9IGNvbXBsZXRlIGF1dGggc2V0dXAgcmVhZHkgdG8gY29weS1wYXN0ZS5gLFxuXHRcdHtcblx0XHRcdGZyYW1ld29yazogRnJhbWV3b3JrRW51bS5kZXNjcmliZShcblx0XHRcdFx0XCJUaGUgd2ViIGZyYW1ld29yayBiZWluZyB1c2VkLiBEZXRlY3QgZnJvbSBwYWNrYWdlLmpzb24gb3IgdXNlcidzIG1lc3NhZ2UuIEV4YW1wbGVzOiAnbmV4dC1hcHAtcm91dGVyJyBmb3IgTmV4dC5qcyAxMyssICduZXh0LXBhZ2VzLXJvdXRlcicgZm9yIE5leHQuanMgcGFnZXMsICdzdmVsdGVraXQnLCAncmVtaXgnLCAnbnV4dCcsICdhc3RybycsICdzb2xpZC1zdGFydCcsICdob25vJywgJ2V4cHJlc3MnLCAnZmFzdGlmeScsICdlbHlzaWEnLCAndGFuc3RhY2stc3RhcnQnLCAnZXhwbydcIixcblx0XHRcdCksXG5cdFx0XHRkYXRhYmFzZTogRGF0YWJhc2VFbnVtLmRlc2NyaWJlKFxuXHRcdFx0XHRcIlRoZSBkYXRhYmFzZSB0eXBlLiBEZXRlY3QgZnJvbSB1c2VyJ3MgbWVzc2FnZSBvciBwcm9qZWN0IGNvbmZpZy4gT3B0aW9uczogJ3Bvc3RncmVzJyAoUG9zdGdyZVNRTC9TdXBhYmFzZS9OZW9uKSwgJ215c3FsJyAoTXlTUUwvUGxhbmV0U2NhbGUpLCAnc3FsaXRlJyAoU1FMaXRlL1R1cnNvL0xpYlNRTCksICdtb25nb2RiJ1wiLFxuXHRcdFx0KSxcblx0XHRcdG9ybTogT1JNRW51bS5vcHRpb25hbCgpXG5cdFx0XHRcdC5kZWZhdWx0KFwibm9uZVwiKVxuXHRcdFx0XHQuZGVzY3JpYmUoXG5cdFx0XHRcdFx0XCJPUk0gYmVpbmcgdXNlZCAtIGFmZmVjdHMgYWRhcHRlciBpbXBvcnRzLiBPcHRpb25zOiAncHJpc21hJywgJ2RyaXp6bGUnLCAnbm9uZScuIERldGVjdCBmcm9tIHBhY2thZ2UuanNvbiBvciB1c2VyJ3MgbWVzc2FnZS5cIixcblx0XHRcdFx0KSxcblx0XHRcdGZlYXR1cmVzOiB6XG5cdFx0XHRcdC5hcnJheShGZWF0dXJlRW51bSlcblx0XHRcdFx0Lm9wdGlvbmFsKClcblx0XHRcdFx0LmRlZmF1bHQoW1wiZW1haWwtcGFzc3dvcmRcIl0pXG5cdFx0XHRcdC5kZXNjcmliZShcblx0XHRcdFx0XHRcIkF1dGggZmVhdHVyZXMgdG8gZW5hYmxlLiBNYXAgdXNlciByZXF1ZXN0czogJ0dvb2dsZSBsb2dpbicg4oaSICdnb29nbGUnLCAnMkZBJyDihpIgJzJmYScsICdwYXNza2V5cycg4oaSICdwYXNza2V5JywgJ21hZ2ljIGxpbmtzJyDihpIgJ21hZ2ljLWxpbmsnLCAnb3JnYW5pemF0aW9ucycg4oaSICdvcmdhbml6YXRpb24nLCAnYWRtaW4nIOKGkiAnYWRtaW4nLiBEZWZhdWx0OiBbJ2VtYWlsLXBhc3N3b3JkJ11cIixcblx0XHRcdFx0KSxcblx0XHRcdHR5cGVzY3JpcHQ6IHpcblx0XHRcdFx0LmJvb2xlYW4oKVxuXHRcdFx0XHQub3B0aW9uYWwoKVxuXHRcdFx0XHQuZGVmYXVsdCh0cnVlKVxuXHRcdFx0XHQuZGVzY3JpYmUoXG5cdFx0XHRcdFx0XCJHZW5lcmF0ZSBUeXBlU2NyaXB0ICgudHMpIG9yIEphdmFTY3JpcHQgKC5qcykuIERlZmF1bHQ6IHRydWUgKFR5cGVTY3JpcHQpXCIsXG5cdFx0XHRcdCksXG5cdFx0XHRzcmNEaXI6IHpcblx0XHRcdFx0LmJvb2xlYW4oKVxuXHRcdFx0XHQub3B0aW9uYWwoKVxuXHRcdFx0XHQuZGVzY3JpYmUoXG5cdFx0XHRcdFx0XCJXaGV0aGVyIHByb2plY3QgdXNlcyBzcmMvIGRpcmVjdG9yeSBzdHJ1Y3R1cmUuIENoZWNrIGlmIHNyYy8gZm9sZGVyIGV4aXN0cy4gRGVmYXVsdDogZmFsc2UuIFByb3ZpZGUgdHJ1ZSBmb3IgZnJhbWV3b3JrcyB0aGF0IHN0b3JlIGFwcCBmaWxlcyBpbiBzcmMvLlwiLFxuXHRcdFx0XHQpLFxuXHRcdFx0YXV0aFBhdGg6IHpcblx0XHRcdFx0LnN0cmluZygpXG5cdFx0XHRcdC5vcHRpb25hbCgpXG5cdFx0XHRcdC5kZXNjcmliZShcblx0XHRcdFx0XHRcIldoZXJlIHRvIGNyZWF0ZSBhdXRoLnRzIGZpbGUuIEF1dG8tZGV0ZWN0ZWQgYmFzZWQgb24gZnJhbWV3b3JrLiBPdmVycmlkZSBvbmx5IGlmIHVzZXIgc3BlY2lmaWVzIGN1c3RvbSBwYXRoLlwiLFxuXHRcdFx0XHQpLFxuXHRcdFx0YXBpUGF0aDogelxuXHRcdFx0XHQuc3RyaW5nKClcblx0XHRcdFx0Lm9wdGlvbmFsKClcblx0XHRcdFx0LmRlc2NyaWJlKFxuXHRcdFx0XHRcdFwiQVBJIHJvdXRlIHBhdGggZm9yIGF1dGggaGFuZGxlci4gQXV0by1kZXRlY3RlZCBiYXNlZCBvbiBmcmFtZXdvcmsuIE92ZXJyaWRlIG9ubHkgaWYgdXNlciBzcGVjaWZpZXMgY3VzdG9tIHBhdGguXCIsXG5cdFx0XHRcdCksXG5cdFx0XHRleGlzdGluZ1NldHVwOiBFeGlzdGluZ1NldHVwU2NoZW1hLm9wdGlvbmFsKCkuZGVzY3JpYmUoXG5cdFx0XHRcdFwiRm9yIElOQ1JFTUVOVEFMIHVwZGF0ZXMgb25seS4gUGFzcyBleGlzdGluZyBhdXRoLnRzIGFuZCBhdXRoLWNsaWVudC50cyBjb250ZW50cyB0byBhZGQgbmV3IGZlYXR1cmVzIHdpdGhvdXQgb3ZlcndyaXRpbmcuIFJlYWQgdGhlc2UgZmlsZXMgZmlyc3QgaWYgdGhleSBleGlzdC5cIixcblx0XHRcdCksXG5cdFx0fSxcblx0XHRhc3luYyAoaW5wdXQpID0+IHtcblx0XHRcdHRyeSB7XG5cdFx0XHRcdGNvbnN0IHJlc3VsdCA9IGdlbmVyYXRlU2V0dXAoe1xuXHRcdFx0XHRcdGZyYW1ld29yazogaW5wdXQuZnJhbWV3b3JrIGFzIEZyYW1ld29yayxcblx0XHRcdFx0XHRkYXRhYmFzZTogaW5wdXQuZGF0YWJhc2UgYXMgRGF0YWJhc2UsXG5cdFx0XHRcdFx0b3JtOiAoaW5wdXQub3JtIHx8IFwibm9uZVwiKSBhcyBPUk0sXG5cdFx0XHRcdFx0ZmVhdHVyZXM6IChpbnB1dC5mZWF0dXJlcyB8fCBbXCJlbWFpbC1wYXNzd29yZFwiXSkgYXMgRmVhdHVyZVtdLFxuXHRcdFx0XHRcdHR5cGVzY3JpcHQ6IGlucHV0LnR5cGVzY3JpcHQgPz8gdHJ1ZSxcblx0XHRcdFx0XHRzcmNEaXI6IGlucHV0LnNyY0RpciA/PyBmYWxzZSxcblx0XHRcdFx0XHRhdXRoUGF0aDogaW5wdXQuYXV0aFBhdGgsXG5cdFx0XHRcdFx0YXBpUGF0aDogaW5wdXQuYXBpUGF0aCxcblx0XHRcdFx0XHRleGlzdGluZ1NldHVwOiBpbnB1dC5leGlzdGluZ1NldHVwLFxuXHRcdFx0XHR9KTtcblxuXHRcdFx0XHRpZiAoaXNTZXR1cEVycm9yKHJlc3VsdCkpIHtcblx0XHRcdFx0XHRyZXR1cm4ge1xuXHRcdFx0XHRcdFx0Y29udGVudDogW1xuXHRcdFx0XHRcdFx0XHR7XG5cdFx0XHRcdFx0XHRcdFx0dHlwZTogXCJ0ZXh0XCIgYXMgY29uc3QsXG5cdFx0XHRcdFx0XHRcdFx0dGV4dDogSlNPTi5zdHJpbmdpZnkocmVzdWx0LCBudWxsLCAyKSxcblx0XHRcdFx0XHRcdFx0fSxcblx0XHRcdFx0XHRcdF0sXG5cdFx0XHRcdFx0XHRpc0Vycm9yOiB0cnVlLFxuXHRcdFx0XHRcdH07XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRyZXR1cm4ge1xuXHRcdFx0XHRcdGNvbnRlbnQ6IFtcblx0XHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdFx0dHlwZTogXCJ0ZXh0XCIgYXMgY29uc3QsXG5cdFx0XHRcdFx0XHRcdHRleHQ6IEpTT04uc3RyaW5naWZ5KHJlc3VsdCwgbnVsbCwgMiksXG5cdFx0XHRcdFx0XHR9LFxuXHRcdFx0XHRcdF0sXG5cdFx0XHRcdH07XG5cdFx0XHR9IGNhdGNoIChlcnJvcikge1xuXHRcdFx0XHRjb25zdCBtZXNzYWdlID1cblx0XHRcdFx0XHRlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFwiVW5rbm93biBlcnJvclwiO1xuXHRcdFx0XHRyZXR1cm4ge1xuXHRcdFx0XHRcdGNvbnRlbnQ6IFtcblx0XHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdFx0dHlwZTogXCJ0ZXh0XCIgYXMgY29uc3QsXG5cdFx0XHRcdFx0XHRcdHRleHQ6IEpTT04uc3RyaW5naWZ5KFxuXHRcdFx0XHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdFx0XHRcdGVycm9yOiB7XG5cdFx0XHRcdFx0XHRcdFx0XHRcdGNvZGU6IFwiU0VUVVBfRVJST1JcIixcblx0XHRcdFx0XHRcdFx0XHRcdFx0bWVzc2FnZSxcblx0XHRcdFx0XHRcdFx0XHRcdH0sXG5cdFx0XHRcdFx0XHRcdFx0fSxcblx0XHRcdFx0XHRcdFx0XHRudWxsLFxuXHRcdFx0XHRcdFx0XHRcdDIsXG5cdFx0XHRcdFx0XHRcdCksXG5cdFx0XHRcdFx0XHR9LFxuXHRcdFx0XHRcdF0sXG5cdFx0XHRcdFx0aXNFcnJvcjogdHJ1ZSxcblx0XHRcdFx0fTtcblx0XHRcdH1cblx0XHR9LFxuXHQpO1xufVxuIiwiIyEvdXNyL2Jpbi9lbnYgbm9kZVxuaW1wb3J0IHsgTWNwU2VydmVyIH0gZnJvbSBcIkBtb2RlbGNvbnRleHRwcm90b2NvbC9zZGsvc2VydmVyL21jcC5qc1wiO1xuaW1wb3J0IHsgU3RkaW9TZXJ2ZXJUcmFuc3BvcnQgfSBmcm9tIFwiQG1vZGVsY29udGV4dHByb3RvY29sL3Nkay9zZXJ2ZXIvc3RkaW8uanNcIjtcbmltcG9ydCB7IHJlZ2lzdGVyU2V0dXBBdXRoIH0gZnJvbSBcIi4vdG9vbHMvaW5kZXguanNcIjtcblxuY29uc3Qgc2VydmVyID0gbmV3IE1jcFNlcnZlcih7XG5cdG5hbWU6IFwiYmV0dGVyLWF1dGhcIixcblx0ZGVzY3JpcHRpb246XG5cdFx0XCJCZXR0ZXIgQXV0aCBNQ1Agc2VydmVyIGZvciBBSS1wb3dlcmVkIGF1dGggc2V0dXAgYW5kIGRpYWdub3N0aWNzXCIsXG5cdHZlcnNpb246IHByb2Nlc3MuZW52LkJFVFRFUl9BVVRIX1ZFUlNJT04hLFxufSk7XG5cbnJlZ2lzdGVyU2V0dXBBdXRoKHNlcnZlcik7XG5cbmFzeW5jIGZ1bmN0aW9uIG1haW4oKSB7XG5cdGNvbnN0IHRyYW5zcG9ydCA9IG5ldyBTdGRpb1NlcnZlclRyYW5zcG9ydCgpO1xuXHRhd2FpdCBzZXJ2ZXIuY29ubmVjdCh0cmFuc3BvcnQpO1xufVxuXG5tYWluKCkuY2F0Y2goY29uc29sZS5lcnJvcik7XG4iXSwibWFwcGluZ3MiOiI7Ozs7OztBQUVBLFNBQWdCLG1CQUNmLGVBQ2lCO0NBQ2pCLE1BQU0sV0FBMkIsRUFDaEMsVUFBVSxFQUFFLEVBQ1o7QUFFRCxLQUFJLGNBQWMsWUFBWTtFQUM3QixNQUFNLFNBQVMsY0FBYztBQUM3QixXQUFTLFdBQVcsZUFBZSxPQUFPO0FBQzFDLFdBQVMsTUFBTSxVQUFVLE9BQU87QUFDaEMsV0FBUyxXQUFXLGVBQWUsUUFBUSxjQUFjLGlCQUFpQjs7QUFHM0UsUUFBTzs7QUFHUixTQUFTLGVBQWUsUUFBb0M7QUFDM0QsS0FDQyxpQ0FBaUMsS0FBSyxPQUFPLElBQzdDLHlCQUF5QixLQUFLLE9BQU8sQ0FFckMsUUFBTztBQUVSLEtBQUksNEJBQTRCLEtBQUssT0FBTyxDQUMzQyxRQUFPO0FBRVIsS0FBSSw2QkFBNkIsS0FBSyxPQUFPLENBQzVDLFFBQU87QUFFUixLQUFJLDhCQUE4QixLQUFLLE9BQU8sQ0FDN0MsUUFBTzs7QUFLVCxTQUFTLFVBQVUsUUFBb0M7QUFDdEQsS0FBSSxrQkFBa0IsS0FBSyxPQUFPLENBQ2pDLFFBQU87QUFFUixLQUFJLG1CQUFtQixLQUFLLE9BQU8sQ0FDbEMsUUFBTztBQUVSLEtBQUksaUNBQWlDLEtBQUssT0FBTyxDQUNoRCxRQUFPOztBQUtULFNBQVMsZUFDUixjQUNBLGNBQ1k7Q0FDWixNQUFNLFdBQXNCLEVBQUU7QUFFOUIsS0FBSSwrQ0FBK0MsS0FBSyxhQUFhLENBQ3BFLFVBQVMsS0FBSyxpQkFBaUI7QUFjaEMsTUFBSyxNQUFNLFlBWHdCO0VBQ2xDO0VBQ0E7RUFDQTtFQUNBO0VBQ0E7RUFD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
214
+ export { mcp, mcpHandler, requireMcpAuth };