@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/README.md +21 -0
- package/dist/client/adapters.d.mts +56 -0
- package/dist/client/adapters.mjs +126 -0
- package/dist/client/index.d.mts +52 -0
- package/dist/client/index.mjs +244 -0
- package/dist/index.d.mts +123 -1
- package/dist/index.mjs +199 -1025
- package/package.json +64 -28
package/dist/index.mjs
CHANGED
|
@@ -1,1040 +1,214 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
153
|
-
|
|
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
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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/
|
|
585
|
-
|
|
586
|
-
const
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
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
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
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
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
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
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
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 };
|