@nlabs/lex 1.48.7 → 1.49.1
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/.storybook/main.ts +9 -2
- package/.vscode/settings.json +1 -6
- package/README.md +249 -0
- package/eslint.config.mjs +24 -0
- package/examples/lex.config.js +18 -8
- package/examples/serverless-example/README.md +109 -0
- package/examples/serverless-example/dist/handlers/echo.js +15 -0
- package/examples/serverless-example/dist/handlers/graphql.js +137 -0
- package/examples/serverless-example/dist/handlers/hello.js +15 -0
- package/examples/serverless-example/dist/handlers/test.js +17 -0
- package/examples/serverless-example/dist/handlers/websocket.js +14 -0
- package/examples/serverless-example/lex.config.mjs +74 -0
- package/jest.config.mjs +13 -12
- package/{dist → lib}/LexConfig.d.ts +7 -6
- package/lib/LexConfig.js +268 -0
- package/lib/commands/ai/ai.js +303 -0
- package/{dist → lib}/commands/build/build.d.ts +3 -0
- package/lib/commands/build/build.js +494 -0
- package/{dist → lib}/commands/clean/clean.js +1 -1
- package/lib/commands/compile/compile.js +241 -0
- package/lib/commands/copy/copy.js +38 -0
- package/{dist → lib}/commands/create/create.js +1 -1
- package/{dist → lib}/commands/dev/dev.d.ts +2 -0
- package/lib/commands/dev/dev.js +286 -0
- package/{dist → lib}/commands/init/init.js +1 -1
- package/lib/commands/lint/lint.js +962 -0
- package/{dist → lib}/commands/migrate/migrate.js +1 -1
- package/lib/commands/publish/publish.js +104 -0
- package/lib/commands/serverless/serverless.d.ts +17 -0
- package/lib/commands/serverless/serverless.js +662 -0
- package/lib/commands/storybook/storybook.js +249 -0
- package/lib/commands/test/test.js +428 -0
- package/lib/commands/update/update.js +128 -0
- package/{dist → lib}/create/changelog.js +1 -1
- package/{dist → lib}/index.d.ts +1 -0
- package/{dist → lib}/index.js +2 -1
- package/lib/lex.js +73 -0
- package/lib/utils/aiService.d.ts +9 -0
- package/lib/utils/aiService.js +299 -0
- package/{dist → lib}/utils/app.d.ts +3 -0
- package/lib/utils/app.js +296 -0
- package/{dist → lib}/utils/file.d.ts +7 -3
- package/lib/utils/file.js +229 -0
- package/lib/utils/translations.d.ts +1 -0
- package/lib/utils/translations.js +74 -0
- package/package.json +60 -54
- package/postcss.config.js +5 -3
- package/tsconfig.build.json +2 -2
- package/webpack.config.js +229 -39
- package/dist/LexConfig.js +0 -287
- package/dist/commands/ai/ai.js +0 -303
- package/dist/commands/build/build.js +0 -404
- package/dist/commands/compile/compile.js +0 -234
- package/dist/commands/copy/copy.js +0 -38
- package/dist/commands/dev/dev.js +0 -74
- package/dist/commands/lint/lint.js +0 -993
- package/dist/commands/publish/publish.js +0 -104
- package/dist/commands/storybook/storybook.js +0 -249
- package/dist/commands/test/test.js +0 -429
- package/dist/commands/update/update.js +0 -132
- package/dist/lex.js +0 -70
- package/dist/utils/aiService.d.ts +0 -9
- package/dist/utils/aiService.js +0 -299
- package/dist/utils/app.js +0 -267
- package/dist/utils/file.js +0 -185
- package/emptyModule.js +0 -0
- package/eslint.config.js +0 -3
- /package/{dist → lib}/Button.stories.d.ts +0 -0
- /package/{dist → lib}/commands/ai/ai.d.ts +0 -0
- /package/{dist → lib}/commands/ai/index.d.ts +0 -0
- /package/{dist → lib}/commands/ai/index.js +0 -0
- /package/{dist → lib}/commands/clean/clean.d.ts +0 -0
- /package/{dist → lib}/commands/compile/compile.d.ts +0 -0
- /package/{dist → lib}/commands/config/config.d.ts +0 -0
- /package/{dist → lib}/commands/config/config.js +0 -0
- /package/{dist → lib}/commands/copy/copy.d.ts +0 -0
- /package/{dist → lib}/commands/create/create.d.ts +0 -0
- /package/{dist → lib}/commands/init/init.d.ts +0 -0
- /package/{dist → lib}/commands/link/link.d.ts +0 -0
- /package/{dist → lib}/commands/link/link.js +0 -0
- /package/{dist → lib}/commands/lint/autofix.d.ts +0 -0
- /package/{dist → lib}/commands/lint/lint.d.ts +0 -0
- /package/{dist → lib}/commands/migrate/migrate.d.ts +0 -0
- /package/{dist → lib}/commands/publish/publish.d.ts +0 -0
- /package/{dist → lib}/commands/storybook/storybook.d.ts +0 -0
- /package/{dist → lib}/commands/test/test.d.ts +0 -0
- /package/{dist → lib}/commands/update/update.d.ts +0 -0
- /package/{dist → lib}/commands/upgrade/upgrade.d.ts +0 -0
- /package/{dist → lib}/commands/upgrade/upgrade.js +0 -0
- /package/{dist → lib}/commands/versions/versions.d.ts +0 -0
- /package/{dist → lib}/commands/versions/versions.js +0 -0
- /package/{dist → lib}/create/changelog.d.ts +0 -0
- /package/{dist → lib}/lex.d.ts +0 -0
- /package/{dist → lib}/storybook/index.d.ts +0 -0
- /package/{dist → lib}/storybook/index.js +0 -0
- /package/{dist → lib}/test-react/index.d.ts +0 -0
- /package/{dist → lib}/test-react/index.js +0 -0
- /package/{dist → lib}/types.d.ts +0 -0
- /package/{dist → lib}/types.js +0 -0
- /package/{dist → lib}/utils/deepMerge.d.ts +0 -0
- /package/{dist → lib}/utils/deepMerge.js +0 -0
- /package/{dist → lib}/utils/log.d.ts +0 -0
- /package/{dist → lib}/utils/log.js +0 -0
- /package/{dist → lib}/utils/reactShim.d.ts +0 -0
- /package/{dist → lib}/utils/reactShim.js +0 -0
|
@@ -0,0 +1,662 @@
|
|
|
1
|
+
import boxen from "boxen";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import express from "express";
|
|
4
|
+
import { readFileSync, existsSync, mkdirSync, writeFileSync } from "fs";
|
|
5
|
+
import { networkInterfaces, homedir } from "os";
|
|
6
|
+
import { resolve as pathResolve, join } from "path";
|
|
7
|
+
import { WebSocketServer } from "ws";
|
|
8
|
+
import { LexConfig } from "../../LexConfig.js";
|
|
9
|
+
import { createSpinner, removeFiles } from "../../utils/app.js";
|
|
10
|
+
import { log } from "../../utils/log.js";
|
|
11
|
+
const getCacheDir = () => {
|
|
12
|
+
const cacheDir = join(homedir(), ".lex-cache");
|
|
13
|
+
if (!existsSync(cacheDir)) {
|
|
14
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
return cacheDir;
|
|
17
|
+
};
|
|
18
|
+
const getCachePath = () => join(getCacheDir(), "public-ip.json");
|
|
19
|
+
const readPublicIpCache = () => {
|
|
20
|
+
const cachePath = getCachePath();
|
|
21
|
+
if (!existsSync(cachePath)) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const cacheData = readFileSync(cachePath, "utf8");
|
|
26
|
+
const cache = JSON.parse(cacheData);
|
|
27
|
+
const oneWeekMs = 7 * 24 * 60 * 60 * 1e3;
|
|
28
|
+
if (Date.now() - cache.timestamp > oneWeekMs) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return cache;
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const writePublicIpCache = (ip) => {
|
|
37
|
+
const cachePath = getCachePath();
|
|
38
|
+
const cache = {
|
|
39
|
+
ip,
|
|
40
|
+
timestamp: Date.now()
|
|
41
|
+
};
|
|
42
|
+
writeFileSync(cachePath, JSON.stringify(cache, null, 2));
|
|
43
|
+
};
|
|
44
|
+
const fetchPublicIp = (forceRefresh = false) => new Promise((resolve) => {
|
|
45
|
+
if (!forceRefresh) {
|
|
46
|
+
const cached = readPublicIpCache();
|
|
47
|
+
if (cached) {
|
|
48
|
+
resolve(cached.ip);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
fetch("https://api.ipify.org").then((res) => res.text()).then((data) => {
|
|
53
|
+
const ip = data.trim();
|
|
54
|
+
if (ip) {
|
|
55
|
+
writePublicIpCache(ip);
|
|
56
|
+
}
|
|
57
|
+
resolve(ip);
|
|
58
|
+
}).catch(() => resolve(void 0));
|
|
59
|
+
});
|
|
60
|
+
const getNetworkAddresses = () => {
|
|
61
|
+
const interfaces = networkInterfaces();
|
|
62
|
+
const addresses = {
|
|
63
|
+
local: "localhost",
|
|
64
|
+
private: null,
|
|
65
|
+
public: null
|
|
66
|
+
};
|
|
67
|
+
for (const name of Object.keys(interfaces)) {
|
|
68
|
+
const networkInterface = interfaces[name];
|
|
69
|
+
if (!networkInterface) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
for (const iface of networkInterface) {
|
|
73
|
+
if (iface.family === "IPv4" && !iface.internal) {
|
|
74
|
+
const ip = iface.address;
|
|
75
|
+
if (ip.startsWith("10.") || ip.startsWith("192.168.") || ip.startsWith("172.")) {
|
|
76
|
+
if (!addresses.private) {
|
|
77
|
+
addresses.private = ip;
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
if (!addresses.public) {
|
|
81
|
+
addresses.public = ip;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return addresses;
|
|
88
|
+
};
|
|
89
|
+
const displayServerStatus = (httpPort, httpsPort, wsPort, host, quiet, publicIp) => {
|
|
90
|
+
if (quiet) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const httpUrl = `http://${host}:${httpPort}`;
|
|
94
|
+
const httpsUrl = `https://${host}:${httpsPort}`;
|
|
95
|
+
const wsUrl = `ws://${host}:${wsPort}`;
|
|
96
|
+
const wssUrl = `wss://${host}:${wsPort}`;
|
|
97
|
+
let urlLines = `${chalk.green("HTTP:")} ${chalk.underline(httpUrl)}
|
|
98
|
+
`;
|
|
99
|
+
urlLines += `${chalk.green("HTTPS:")} ${chalk.underline(httpsUrl)}
|
|
100
|
+
`;
|
|
101
|
+
urlLines += `${chalk.green("WebSocket:")} ${chalk.underline(wsUrl)}
|
|
102
|
+
`;
|
|
103
|
+
urlLines += `${chalk.green("WSS:")} ${chalk.underline(wssUrl)}
|
|
104
|
+
`;
|
|
105
|
+
if (publicIp) {
|
|
106
|
+
urlLines += `
|
|
107
|
+
${chalk.green("Public:")} ${chalk.underline(`http://${publicIp}:${httpPort}`)}
|
|
108
|
+
`;
|
|
109
|
+
}
|
|
110
|
+
const statusBox = boxen(
|
|
111
|
+
`${chalk.cyan.bold("\u{1F680} Serverless Development Server Running")}
|
|
112
|
+
|
|
113
|
+
${urlLines}
|
|
114
|
+
${chalk.yellow("Press Ctrl+C to stop the server")}`,
|
|
115
|
+
{
|
|
116
|
+
padding: 1,
|
|
117
|
+
margin: 1,
|
|
118
|
+
borderStyle: "round",
|
|
119
|
+
borderColor: "cyan",
|
|
120
|
+
backgroundColor: "#1a1a1a"
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
console.log(`
|
|
124
|
+
${statusBox}
|
|
125
|
+
`);
|
|
126
|
+
};
|
|
127
|
+
const loadHandler = async (handlerPath, outputDir) => {
|
|
128
|
+
try {
|
|
129
|
+
const fullPath = pathResolve(outputDir, handlerPath);
|
|
130
|
+
log(`Loading handler from: ${fullPath}`, "info", false);
|
|
131
|
+
if (!existsSync(fullPath)) {
|
|
132
|
+
throw new Error(`Handler file not found: ${fullPath}`);
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const handlerModule = await import(fullPath);
|
|
136
|
+
log(`Handler module loaded: ${Object.keys(handlerModule)}`, "info", false);
|
|
137
|
+
const handler = handlerModule.default || handlerModule.handler || handlerModule;
|
|
138
|
+
log(`Handler found: ${typeof handler}`, "info", false);
|
|
139
|
+
return handler;
|
|
140
|
+
} catch (importError) {
|
|
141
|
+
log(`Import error for handler ${handlerPath}: ${importError.message}`, "error", false);
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
} catch (error) {
|
|
145
|
+
log(`Error loading handler ${handlerPath}: ${error.message}`, "error", false);
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
const captureConsoleLogs = (handler, quiet) => {
|
|
150
|
+
if (quiet) {
|
|
151
|
+
return handler;
|
|
152
|
+
}
|
|
153
|
+
return async (event, context) => {
|
|
154
|
+
const originalConsoleLog = console.log;
|
|
155
|
+
const originalConsoleError = console.error;
|
|
156
|
+
const originalConsoleWarn = console.warn;
|
|
157
|
+
const originalConsoleInfo = console.info;
|
|
158
|
+
const logs = [];
|
|
159
|
+
console.log = (...args) => {
|
|
160
|
+
logs.push(`[LOG] ${args.join(" ")}`);
|
|
161
|
+
originalConsoleLog(...args);
|
|
162
|
+
};
|
|
163
|
+
console.error = (...args) => {
|
|
164
|
+
logs.push(`[ERROR] ${args.join(" ")}`);
|
|
165
|
+
originalConsoleError(...args);
|
|
166
|
+
};
|
|
167
|
+
console.warn = (...args) => {
|
|
168
|
+
logs.push(`[WARN] ${args.join(" ")}`);
|
|
169
|
+
originalConsoleWarn(...args);
|
|
170
|
+
};
|
|
171
|
+
console.info = (...args) => {
|
|
172
|
+
logs.push(`[INFO] ${args.join(" ")}`);
|
|
173
|
+
originalConsoleInfo(...args);
|
|
174
|
+
};
|
|
175
|
+
try {
|
|
176
|
+
const result = await handler(event, context);
|
|
177
|
+
if (logs.length > 0) {
|
|
178
|
+
console.log(chalk.gray("--- Handler Console Output ---"));
|
|
179
|
+
logs.forEach((log2) => console.log(chalk.gray(log2)));
|
|
180
|
+
console.log(chalk.gray("--- End Handler Console Output ---"));
|
|
181
|
+
}
|
|
182
|
+
return result;
|
|
183
|
+
} finally {
|
|
184
|
+
console.log = originalConsoleLog;
|
|
185
|
+
console.error = originalConsoleError;
|
|
186
|
+
console.warn = originalConsoleWarn;
|
|
187
|
+
console.info = originalConsoleInfo;
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
const createExpressServer = async (config, outputDir, httpPort, host, quiet, debug, printOutput) => {
|
|
192
|
+
const app = express();
|
|
193
|
+
app.use((req, res, next) => {
|
|
194
|
+
res.header("Access-Control-Allow-Origin", "*");
|
|
195
|
+
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
|
|
196
|
+
res.header("Access-Control-Allow-Headers", "*");
|
|
197
|
+
res.header("Access-Control-Allow-Credentials", "true");
|
|
198
|
+
if (req.method === "OPTIONS") {
|
|
199
|
+
res.sendStatus(200);
|
|
200
|
+
} else {
|
|
201
|
+
next();
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
app.use(express.json());
|
|
205
|
+
const loadGraphQLSchema = async () => {
|
|
206
|
+
try {
|
|
207
|
+
let graphqlHandler = null;
|
|
208
|
+
if (config.functions) {
|
|
209
|
+
for (const [functionName, functionConfig] of Object.entries(config.functions)) {
|
|
210
|
+
if (functionConfig.events) {
|
|
211
|
+
for (const event of functionConfig.events) {
|
|
212
|
+
if (event.http && event.http.path) {
|
|
213
|
+
if (event.http.path === "/public" || event.http.path === "/graphql") {
|
|
214
|
+
graphqlHandler = await loadHandler(functionConfig.handler, outputDir);
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (graphqlHandler) {
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (graphqlHandler) {
|
|
226
|
+
log("Found GraphQL handler", "info", quiet);
|
|
227
|
+
return graphqlHandler;
|
|
228
|
+
}
|
|
229
|
+
return null;
|
|
230
|
+
} catch (error) {
|
|
231
|
+
log(`Error loading GraphQL handler: ${error.message}`, "error", quiet);
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
try {
|
|
236
|
+
const graphqlHandler = await loadGraphQLSchema();
|
|
237
|
+
if (graphqlHandler) {
|
|
238
|
+
let graphqlPath = "/graphql";
|
|
239
|
+
if (config.functions) {
|
|
240
|
+
for (const [_functionName, functionConfig] of Object.entries(config.functions)) {
|
|
241
|
+
if (functionConfig.events) {
|
|
242
|
+
for (const event of functionConfig.events) {
|
|
243
|
+
if (event?.http?.path) {
|
|
244
|
+
graphqlPath = event.http.path;
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (graphqlPath !== "/graphql") {
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
app.use(graphqlPath, async (req, res) => {
|
|
255
|
+
if (debug && req.body && req.body.query) {
|
|
256
|
+
log("\u{1F50D} GraphQL Debug Mode: Analyzing request...", "info", false);
|
|
257
|
+
log(`\u{1F4DD} GraphQL Query: ${req.body.query}`, "info", false);
|
|
258
|
+
if (req.body.variables) {
|
|
259
|
+
log(`\u{1F4CA} GraphQL Variables: ${JSON.stringify(req.body.variables, null, 2)}`, "info", false);
|
|
260
|
+
}
|
|
261
|
+
if (req.body.operationName) {
|
|
262
|
+
log(`\u{1F3F7}\uFE0F GraphQL Operation: ${req.body.operationName}`, "info", false);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const originalConsoleLog = console.log;
|
|
266
|
+
const logs = [];
|
|
267
|
+
console.log = (...args) => {
|
|
268
|
+
const logMessage = args.map(
|
|
269
|
+
(arg) => typeof arg === "object" ? JSON.stringify(arg, null, 2) : String(arg)
|
|
270
|
+
).join(" ");
|
|
271
|
+
logs.push(logMessage);
|
|
272
|
+
originalConsoleLog(`[GraphQL] ${logMessage}`);
|
|
273
|
+
};
|
|
274
|
+
const context = {
|
|
275
|
+
req,
|
|
276
|
+
res,
|
|
277
|
+
functionName: "graphql",
|
|
278
|
+
functionVersion: "$LATEST",
|
|
279
|
+
invokedFunctionArn: "arn:aws:lambda:us-east-1:123456789012:function:graphql",
|
|
280
|
+
memoryLimitInMB: "128",
|
|
281
|
+
awsRequestId: "test-request-id",
|
|
282
|
+
logGroupName: "/aws/lambda/graphql",
|
|
283
|
+
logStreamName: "test-log-stream",
|
|
284
|
+
getRemainingTimeInMillis: () => 3e4
|
|
285
|
+
};
|
|
286
|
+
const wrappedHandler = captureConsoleLogs(graphqlHandler, quiet);
|
|
287
|
+
try {
|
|
288
|
+
const result = await wrappedHandler({
|
|
289
|
+
httpMethod: "POST",
|
|
290
|
+
path: graphqlPath,
|
|
291
|
+
headers: req.headers,
|
|
292
|
+
queryStringParameters: {},
|
|
293
|
+
body: JSON.stringify(req.body)
|
|
294
|
+
}, context);
|
|
295
|
+
console.log = originalConsoleLog;
|
|
296
|
+
if (result && typeof result === "object" && result.statusCode) {
|
|
297
|
+
res.status(result.statusCode);
|
|
298
|
+
if (result.headers) {
|
|
299
|
+
Object.entries(result.headers).forEach(([key, value]) => {
|
|
300
|
+
res.setHeader(key, String(value));
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
res.send(result.body);
|
|
304
|
+
} else {
|
|
305
|
+
res.json(result);
|
|
306
|
+
}
|
|
307
|
+
} catch (error) {
|
|
308
|
+
console.log = originalConsoleLog;
|
|
309
|
+
log(`GraphQL handler error: ${error.message}`, "error", false);
|
|
310
|
+
res.status(500).json({ error: error.message });
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
log(`GraphQL endpoint available at http://${host}:${httpPort}${graphqlPath}`, "info", quiet);
|
|
314
|
+
}
|
|
315
|
+
} catch (error) {
|
|
316
|
+
log(`Error setting up GraphQL: ${error.message}`, "error", quiet);
|
|
317
|
+
}
|
|
318
|
+
app.use("/", async (req, res) => {
|
|
319
|
+
try {
|
|
320
|
+
const url = req.url || "/";
|
|
321
|
+
const method = req.method || "GET";
|
|
322
|
+
log(`${method} ${url}`, "info", false);
|
|
323
|
+
let matchedFunction = null;
|
|
324
|
+
if (config.functions) {
|
|
325
|
+
for (const [functionName, functionConfig] of Object.entries(config.functions)) {
|
|
326
|
+
if (functionConfig.events) {
|
|
327
|
+
for (const event of functionConfig.events) {
|
|
328
|
+
if (event.http) {
|
|
329
|
+
const eventPath = event.http.path || "/";
|
|
330
|
+
const eventMethod = event.http.method || "GET";
|
|
331
|
+
if (eventPath && eventPath === url && eventMethod === method) {
|
|
332
|
+
matchedFunction = functionName;
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
if (matchedFunction) {
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (matchedFunction && config.functions[matchedFunction]) {
|
|
344
|
+
const handlerPath = config.functions[matchedFunction].handler;
|
|
345
|
+
const handler = await loadHandler(handlerPath, outputDir);
|
|
346
|
+
if (handler) {
|
|
347
|
+
const wrappedHandler = captureConsoleLogs(handler, quiet);
|
|
348
|
+
const event = {
|
|
349
|
+
body: req.body,
|
|
350
|
+
headers: req.headers,
|
|
351
|
+
httpMethod: method,
|
|
352
|
+
path: url,
|
|
353
|
+
queryStringParameters: req.query
|
|
354
|
+
};
|
|
355
|
+
const context = {
|
|
356
|
+
functionName: matchedFunction,
|
|
357
|
+
functionVersion: "$LATEST",
|
|
358
|
+
invokedFunctionArn: `arn:aws:lambda:us-east-1:123456789012:function:${matchedFunction}`,
|
|
359
|
+
memoryLimitInMB: "128",
|
|
360
|
+
awsRequestId: "test-request-id",
|
|
361
|
+
logGroupName: `/aws/lambda/${matchedFunction}`,
|
|
362
|
+
logStreamName: "test-log-stream",
|
|
363
|
+
getRemainingTimeInMillis: () => 3e4
|
|
364
|
+
};
|
|
365
|
+
try {
|
|
366
|
+
const result = await wrappedHandler(event, context);
|
|
367
|
+
if (result && typeof result === "object" && result.statusCode) {
|
|
368
|
+
res.status(result.statusCode);
|
|
369
|
+
if (result.headers) {
|
|
370
|
+
Object.entries(result.headers).forEach(([key, value]) => {
|
|
371
|
+
res.setHeader(key, String(value));
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
res.send(result.body);
|
|
375
|
+
} else {
|
|
376
|
+
res.json(result);
|
|
377
|
+
}
|
|
378
|
+
} catch (error) {
|
|
379
|
+
log(`Handler error: ${error.message}`, "error", false);
|
|
380
|
+
res.status(500).json({ error: error.message });
|
|
381
|
+
}
|
|
382
|
+
} else {
|
|
383
|
+
res.status(404).json({ error: "Handler not found" });
|
|
384
|
+
}
|
|
385
|
+
} else {
|
|
386
|
+
res.status(404).json({ error: "Function not found" });
|
|
387
|
+
}
|
|
388
|
+
} catch (error) {
|
|
389
|
+
log(`Route handling error: ${error.message}`, "error", false);
|
|
390
|
+
res.status(500).json({ error: error.message });
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
return app;
|
|
394
|
+
};
|
|
395
|
+
const createWebSocketServer = (config, outputDir, wsPort, quiet, debug, printOutput) => {
|
|
396
|
+
const wss = new WebSocketServer({ port: wsPort });
|
|
397
|
+
wss.on("connection", async (ws, req) => {
|
|
398
|
+
log(`WebSocket connection established: ${req.url}`, "info", false);
|
|
399
|
+
ws.on("message", async (message) => {
|
|
400
|
+
try {
|
|
401
|
+
const data = JSON.parse(message.toString());
|
|
402
|
+
let matchedFunction = null;
|
|
403
|
+
if (config.functions) {
|
|
404
|
+
for (const [functionName, functionConfig] of Object.entries(config.functions)) {
|
|
405
|
+
if (functionConfig.events) {
|
|
406
|
+
for (const event of functionConfig.events) {
|
|
407
|
+
if (event.websocket) {
|
|
408
|
+
const route = event.websocket.route || "$connect";
|
|
409
|
+
if (route === "$default" || route === data.action) {
|
|
410
|
+
matchedFunction = functionName;
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
if (matchedFunction) {
|
|
417
|
+
break;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
if (matchedFunction && config.functions[matchedFunction]) {
|
|
422
|
+
const handler = await loadHandler(config.functions[matchedFunction].handler, outputDir);
|
|
423
|
+
if (handler) {
|
|
424
|
+
const wrappedHandler = captureConsoleLogs(handler, quiet);
|
|
425
|
+
const event = {
|
|
426
|
+
requestContext: {
|
|
427
|
+
routeKey: data.action || "$default",
|
|
428
|
+
connectionId: "test-connection-id",
|
|
429
|
+
apiGateway: {
|
|
430
|
+
endpoint: `ws://localhost:${wsPort}`
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
body: data.body || null
|
|
434
|
+
};
|
|
435
|
+
const context = {
|
|
436
|
+
functionName: matchedFunction,
|
|
437
|
+
functionVersion: "$LATEST",
|
|
438
|
+
invokedFunctionArn: `arn:aws:lambda:us-east-1:123456789012:function:${matchedFunction}`,
|
|
439
|
+
memoryLimitInMB: "128",
|
|
440
|
+
awsRequestId: "test-request-id",
|
|
441
|
+
logGroupName: `/aws/lambda/${matchedFunction}`,
|
|
442
|
+
logStreamName: "test-log-stream",
|
|
443
|
+
getRemainingTimeInMillis: () => 3e4
|
|
444
|
+
};
|
|
445
|
+
const result = await wrappedHandler(event, context);
|
|
446
|
+
if (result && typeof result === "object" && result.statusCode) {
|
|
447
|
+
const body = result.body || "";
|
|
448
|
+
ws.send(body);
|
|
449
|
+
} else {
|
|
450
|
+
ws.send(JSON.stringify(result));
|
|
451
|
+
}
|
|
452
|
+
} else {
|
|
453
|
+
ws.send(JSON.stringify({ error: "Handler not found" }));
|
|
454
|
+
}
|
|
455
|
+
} else {
|
|
456
|
+
ws.send(JSON.stringify({ error: "WebSocket function not found" }));
|
|
457
|
+
}
|
|
458
|
+
} catch (error) {
|
|
459
|
+
log(`WebSocket error: ${error.message}`, "error", false);
|
|
460
|
+
ws.send(JSON.stringify({ error: error.message }));
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
ws.on("close", () => {
|
|
464
|
+
log("WebSocket connection closed", "info", false);
|
|
465
|
+
});
|
|
466
|
+
});
|
|
467
|
+
return wss;
|
|
468
|
+
};
|
|
469
|
+
const loadEnvFile = (envPath) => {
|
|
470
|
+
const envVars = {};
|
|
471
|
+
if (!existsSync(envPath)) {
|
|
472
|
+
return envVars;
|
|
473
|
+
}
|
|
474
|
+
try {
|
|
475
|
+
const envContent = readFileSync(envPath, "utf8");
|
|
476
|
+
const lines = envContent.split("\n");
|
|
477
|
+
for (const line of lines) {
|
|
478
|
+
const trimmedLine = line.trim();
|
|
479
|
+
if (!trimmedLine || trimmedLine.startsWith("#")) {
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
const equalIndex = trimmedLine.indexOf("=");
|
|
483
|
+
if (equalIndex > 0) {
|
|
484
|
+
const key = trimmedLine.substring(0, equalIndex).trim();
|
|
485
|
+
const value = trimmedLine.substring(equalIndex + 1).trim();
|
|
486
|
+
const cleanValue = value.replace(/^["']|["']$/g, "");
|
|
487
|
+
if (key) {
|
|
488
|
+
envVars[key] = cleanValue;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
} catch (error) {
|
|
493
|
+
log(`Warning: Could not load .env file at ${envPath}: ${error.message}`, "warn", false);
|
|
494
|
+
}
|
|
495
|
+
return envVars;
|
|
496
|
+
};
|
|
497
|
+
const serverless = async (cmd, callback = () => ({})) => {
|
|
498
|
+
const {
|
|
499
|
+
cliName = "Lex",
|
|
500
|
+
config,
|
|
501
|
+
host = "localhost",
|
|
502
|
+
httpPort = 3e3,
|
|
503
|
+
httpsPort = 3001,
|
|
504
|
+
wsPort = 3002,
|
|
505
|
+
quiet = false,
|
|
506
|
+
remove = false,
|
|
507
|
+
usePublicIp,
|
|
508
|
+
variables,
|
|
509
|
+
debug = false,
|
|
510
|
+
printOutput = false,
|
|
511
|
+
test = false
|
|
512
|
+
} = cmd;
|
|
513
|
+
const spinner = createSpinner(quiet);
|
|
514
|
+
log(`${cliName} starting serverless development server...`, "info", quiet);
|
|
515
|
+
await LexConfig.parseConfig(cmd);
|
|
516
|
+
const { outputFullPath } = LexConfig.config;
|
|
517
|
+
const envPaths = [
|
|
518
|
+
pathResolve(process.cwd(), ".env"),
|
|
519
|
+
pathResolve(process.cwd(), ".env.local"),
|
|
520
|
+
pathResolve(process.cwd(), ".env.development")
|
|
521
|
+
];
|
|
522
|
+
let envVars = {};
|
|
523
|
+
for (const envPath of envPaths) {
|
|
524
|
+
const fileEnvVars = loadEnvFile(envPath);
|
|
525
|
+
if (Object.keys(fileEnvVars).length > 0) {
|
|
526
|
+
log(`Loaded environment variables from: ${envPath}`, "info", quiet);
|
|
527
|
+
}
|
|
528
|
+
envVars = { ...envVars, ...fileEnvVars };
|
|
529
|
+
}
|
|
530
|
+
let variablesObj = { NODE_ENV: "development", ...envVars };
|
|
531
|
+
if (variables) {
|
|
532
|
+
try {
|
|
533
|
+
const cliVars = JSON.parse(variables);
|
|
534
|
+
variablesObj = { ...variablesObj, ...cliVars };
|
|
535
|
+
} catch (_error) {
|
|
536
|
+
log(`
|
|
537
|
+
${cliName} Error: Environment variables option is not a valid JSON object.`, "error", quiet);
|
|
538
|
+
callback(1);
|
|
539
|
+
return 1;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
process.env = { ...process.env, ...variablesObj };
|
|
543
|
+
if (test) {
|
|
544
|
+
log("Test mode: Environment variables loaded, exiting", "info", quiet);
|
|
545
|
+
callback(0);
|
|
546
|
+
return 0;
|
|
547
|
+
}
|
|
548
|
+
if (remove) {
|
|
549
|
+
spinner.start("Cleaning output directory...");
|
|
550
|
+
await removeFiles(outputFullPath || "");
|
|
551
|
+
spinner.succeed("Successfully cleaned output directory!");
|
|
552
|
+
}
|
|
553
|
+
let serverlessConfig = {};
|
|
554
|
+
try {
|
|
555
|
+
const configPath = config || pathResolve(process.cwd(), "lex.config.mjs");
|
|
556
|
+
log(`Loading serverless config from: ${configPath}`, "info", quiet);
|
|
557
|
+
if (existsSync(configPath)) {
|
|
558
|
+
const configModule = await import(configPath);
|
|
559
|
+
serverlessConfig = configModule.default?.serverless || configModule.serverless || {};
|
|
560
|
+
log("Serverless config loaded successfully", "info", quiet);
|
|
561
|
+
log(`Loaded functions: ${Object.keys(serverlessConfig.functions || {}).join(", ")}`, "info", quiet);
|
|
562
|
+
} else {
|
|
563
|
+
log(`No serverless config found at ${configPath}, using defaults`, "warn", quiet);
|
|
564
|
+
}
|
|
565
|
+
} catch (error) {
|
|
566
|
+
log(`Error loading serverless config: ${error.message}`, "error", quiet);
|
|
567
|
+
}
|
|
568
|
+
const finalConfig = {
|
|
569
|
+
...serverlessConfig,
|
|
570
|
+
custom: {
|
|
571
|
+
"serverless-offline": {
|
|
572
|
+
httpPort: serverlessConfig.custom?.["serverless-offline"]?.httpPort || httpPort,
|
|
573
|
+
httpsPort: serverlessConfig.custom?.["serverless-offline"]?.httpsPort || httpsPort,
|
|
574
|
+
wsPort: serverlessConfig.custom?.["serverless-offline"]?.wsPort || wsPort,
|
|
575
|
+
host: serverlessConfig.custom?.["serverless-offline"]?.host || host,
|
|
576
|
+
cors: serverlessConfig.custom?.["serverless-offline"]?.cors !== false
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
const outputDir = outputFullPath || "dist";
|
|
581
|
+
log(`Using output directory: ${outputDir}`, "info", quiet);
|
|
582
|
+
try {
|
|
583
|
+
spinner.start("Starting serverless development server...");
|
|
584
|
+
const httpPort2 = finalConfig.custom["serverless-offline"].httpPort;
|
|
585
|
+
const wsPort2 = finalConfig.custom["serverless-offline"].wsPort;
|
|
586
|
+
const host2 = finalConfig.custom["serverless-offline"].host;
|
|
587
|
+
log(`Creating HTTP server on ${host2}:${httpPort2}`, "info", quiet);
|
|
588
|
+
log(`Creating WebSocket server on port ${wsPort2}`, "info", quiet);
|
|
589
|
+
const expressApp = await createExpressServer(
|
|
590
|
+
finalConfig,
|
|
591
|
+
outputDir,
|
|
592
|
+
httpPort2,
|
|
593
|
+
host2,
|
|
594
|
+
quiet,
|
|
595
|
+
debug,
|
|
596
|
+
printOutput
|
|
597
|
+
);
|
|
598
|
+
const wsServer = createWebSocketServer(
|
|
599
|
+
finalConfig,
|
|
600
|
+
outputDir,
|
|
601
|
+
wsPort2,
|
|
602
|
+
quiet,
|
|
603
|
+
debug,
|
|
604
|
+
printOutput
|
|
605
|
+
);
|
|
606
|
+
wsServer.on("error", (error) => {
|
|
607
|
+
log(`WebSocket server error: ${error.message}`, "error", quiet);
|
|
608
|
+
spinner.fail("Failed to start WebSocket server.");
|
|
609
|
+
callback(1);
|
|
610
|
+
return;
|
|
611
|
+
});
|
|
612
|
+
const server = expressApp.listen(httpPort2, host2, () => {
|
|
613
|
+
spinner.succeed("Serverless development server started.");
|
|
614
|
+
displayServerStatus(
|
|
615
|
+
httpPort2,
|
|
616
|
+
finalConfig.custom["serverless-offline"].httpsPort,
|
|
617
|
+
wsPort2,
|
|
618
|
+
host2,
|
|
619
|
+
quiet
|
|
620
|
+
);
|
|
621
|
+
fetchPublicIp(usePublicIp).then((publicIp) => {
|
|
622
|
+
if (publicIp) {
|
|
623
|
+
displayServerStatus(
|
|
624
|
+
httpPort2,
|
|
625
|
+
finalConfig.custom["serverless-offline"].httpsPort,
|
|
626
|
+
wsPort2,
|
|
627
|
+
host2,
|
|
628
|
+
quiet,
|
|
629
|
+
publicIp
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
});
|
|
634
|
+
server.on("error", (error) => {
|
|
635
|
+
log(`Express server error: ${error.message}`, "error", quiet);
|
|
636
|
+
spinner.fail("Failed to start Express server.");
|
|
637
|
+
callback(1);
|
|
638
|
+
return;
|
|
639
|
+
});
|
|
640
|
+
const shutdown = () => {
|
|
641
|
+
log("\nShutting down serverless development server...", "info", quiet);
|
|
642
|
+
server.close();
|
|
643
|
+
wsServer.close();
|
|
644
|
+
callback(0);
|
|
645
|
+
};
|
|
646
|
+
process.on("SIGINT", shutdown);
|
|
647
|
+
process.on("SIGTERM", shutdown);
|
|
648
|
+
process.stdin.resume();
|
|
649
|
+
log("Serverless development server is running. Press Ctrl+C to stop.", "info", quiet);
|
|
650
|
+
return 0;
|
|
651
|
+
} catch (error) {
|
|
652
|
+
log(`
|
|
653
|
+
${cliName} Error: ${error.message}`, "error", quiet);
|
|
654
|
+
spinner.fail("Failed to start serverless development server.");
|
|
655
|
+
callback(1);
|
|
656
|
+
return 1;
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
export {
|
|
660
|
+
serverless
|
|
661
|
+
};
|
|
662
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjL2NvbW1hbmRzL3NlcnZlcmxlc3Mvc2VydmVybGVzcy50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiLyoqXG4gKiBDb3B5cmlnaHQgKGMpIDIwMTgtUHJlc2VudCwgTml0cm9nZW4gTGFicywgSW5jLlxuICogQ29weXJpZ2h0cyBsaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSB0aGUgYWNjb21wYW55aW5nIExJQ0VOU0UgZmlsZSBmb3IgdGVybXMuXG4gKi9cbmltcG9ydCBib3hlbiBmcm9tICdib3hlbic7XG5pbXBvcnQgY2hhbGsgZnJvbSAnY2hhbGsnO1xuaW1wb3J0IGV4cHJlc3MgZnJvbSAnZXhwcmVzcyc7XG5pbXBvcnQge3JlYWRGaWxlU3luYywgZXhpc3RzU3luYywgbWtkaXJTeW5jLCB3cml0ZUZpbGVTeW5jfSBmcm9tICdmcyc7XG5pbXBvcnQge25ldHdvcmtJbnRlcmZhY2VzLCBob21lZGlyfSBmcm9tICdvcyc7XG5pbXBvcnQge3Jlc29sdmUgYXMgcGF0aFJlc29sdmUsIGpvaW59IGZyb20gJ3BhdGgnO1xuaW1wb3J0IHtXZWJTb2NrZXRTZXJ2ZXJ9IGZyb20gJ3dzJztcblxuaW1wb3J0IHtMZXhDb25maWd9IGZyb20gJy4uLy4uL0xleENvbmZpZy5qcyc7XG5pbXBvcnQge2NyZWF0ZVNwaW5uZXIsIHJlbW92ZUZpbGVzfSBmcm9tICcuLi8uLi91dGlscy9hcHAuanMnO1xuaW1wb3J0IHtsb2d9IGZyb20gJy4uLy4uL3V0aWxzL2xvZy5qcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgU2VydmVybGVzc09wdGlvbnMge1xuICByZWFkb25seSBjbGlOYW1lPzogc3RyaW5nO1xuICByZWFkb25seSBjb25maWc/OiBzdHJpbmc7XG4gIHJlYWRvbmx5IGhvc3Q/OiBzdHJpbmc7XG4gIHJlYWRvbmx5IGh0dHBQb3J0PzogbnVtYmVyO1xuICByZWFkb25seSBodHRwc1BvcnQ/OiBudW1iZXI7XG4gIHJlYWRvbmx5IHdzUG9ydD86IG51bWJlcjtcbiAgcmVhZG9ubHkgcXVpZXQ/OiBib29sZWFuO1xuICByZWFkb25seSByZW1vdmU/OiBib29sZWFuO1xuICByZWFkb25seSB2YXJpYWJsZXM/OiBzdHJpbmc7XG4gIHJlYWRvbmx5IHVzZVB1YmxpY0lwPzogYm9vbGVhbjtcbiAgcmVhZG9ubHkgZGVidWc/OiBib29sZWFuO1xuICByZWFkb25seSBwcmludE91dHB1dD86IGJvb2xlYW47XG4gIHJlYWRvbmx5IHRlc3Q/OiBib29sZWFuO1xufVxuXG5leHBvcnQgdHlwZSBTZXJ2ZXJsZXNzQ2FsbGJhY2sgPSAoc3RhdHVzOiBudW1iZXIpID0+IHZvaWQ7XG5cbmludGVyZmFjZSBQdWJsaWNJcENhY2hlIHtcbiAgaXA6IHN0cmluZztcbiAgdGltZXN0YW1wOiBudW1iZXI7XG59XG5cbmludGVyZmFjZSBTZXJ2ZXJsZXNzSGFuZGxlciB7XG4gIHJlYWRvbmx5IGhhbmRsZXI6IHN0cmluZztcbiAgcmVhZG9ubHkgZXZlbnRzPzogQXJyYXk8e1xuICAgIHJlYWRvbmx5IGh0dHA/OiB7XG4gICAgICByZWFkb25seSBwYXRoPzogc3RyaW5nO1xuICAgICAgcmVhZG9ubHkgbWV0aG9kPzogc3RyaW5nO1xuICAgICAgcmVhZG9ubHkgY29ycz86IGJvb2xlYW47XG4gICAgfTtcbiAgICByZWFkb25seSB3ZWJzb2NrZXQ/OiB7XG4gICAgICByZWFkb25seSByb3V0ZT86IHN0cmluZztcbiAgICB9O1xuICB9Pjtcbn1cblxuaW50ZXJmYWNlIFNlcnZlcmxlc3NDb25maWcge1xuICByZWFkb25seSBmdW5jdGlvbnM/OiBSZWNvcmQ8c3RyaW5nLCBTZXJ2ZXJsZXNzSGFuZGxlcj47XG4gIHJlYWRvbmx5IGN1c3RvbT86IHtcbiAgICByZWFkb25seSAnc2VydmVybGVzcy1vZmZsaW5lJz86IHtcbiAgICAgIHJlYWRvbmx5IGh0dHBQb3J0PzogbnVtYmVyO1xuICAgICAgcmVhZG9ubHkgaHR0cHNQb3J0PzogbnVtYmVyO1xuICAgICAgcmVhZG9ubHkgd3NQb3J0PzogbnVtYmVyO1xuICAgICAgcmVhZG9ubHkgaG9zdD86IHN0cmluZztcbiAgICAgIHJlYWRvbmx5IGNvcnM/OiBib29sZWFuO1xuICAgIH07XG4gIH07XG59XG5cbmNvbnN0IGdldENhY2hlRGlyID0gKCk6IHN0cmluZyA9PiB7XG4gIGNvbnN0IGNhY2hlRGlyID0gam9pbihob21lZGlyKCksICcubGV4LWNhY2hlJyk7XG4gIGlmKCFleGlzdHNTeW5jKGNhY2hlRGlyKSkge1xuICAgIG1rZGlyU3luYyhjYWNoZURpciwge3JlY3Vyc2l2ZTogdHJ1ZX0pO1xuICB9XG4gIHJldHVybiBjYWNoZURpcjtcbn07XG5cbmNvbnN0IGdldENhY2hlUGF0aCA9ICgpOiBzdHJpbmcgPT4gam9pbihnZXRDYWNoZURpcigpLCAncHVibGljLWlwLmpzb24nKTtcblxuY29uc3QgcmVhZFB1YmxpY0lwQ2FjaGUgPSAoKTogUHVibGljSXBDYWNoZSB8IG51bGwgPT4ge1xuICBjb25zdCBjYWNoZVBhdGggPSBnZXRDYWNoZVBhdGgoKTtcbiAgaWYoIWV4aXN0c1N5bmMoY2FjaGVQYXRoKSkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgdHJ5IHtcbiAgICBjb25zdCBjYWNoZURhdGEgPSByZWFkRmlsZVN5bmMoY2FjaGVQYXRoLCAndXRmOCcpO1xuICAgIGNvbnN0IGNhY2hlOiBQdWJsaWNJcENhY2hlID0gSlNPTi5wYXJzZShjYWNoZURhdGEpO1xuXG4gICAgLy8gQ2hlY2sgaWYgY2FjaGUgaXMgb2xkZXIgdGhhbiAxIHdlZWtcbiAgICBjb25zdCBvbmVXZWVrTXMgPSA3ICogMjQgKiA2MCAqIDYwICogMTAwMDtcbiAgICBpZihEYXRlLm5vdygpIC0gY2FjaGUudGltZXN0YW1wID4gb25lV2Vla01zKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICByZXR1cm4gY2FjaGU7XG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiBudWxsO1xuICB9XG59O1xuXG5jb25zdCB3cml0ZVB1YmxpY0lwQ2FjaGUgPSAoaXA6IHN0cmluZyk6IHZvaWQgPT4ge1xuICBjb25zdCBjYWNoZVBhdGggPSBnZXRDYWNoZVBhdGgoKTtcbiAgY29uc3QgY2FjaGU6IFB1YmxpY0lwQ2FjaGUgPSB7XG4gICAgaXAsXG4gICAgdGltZXN0YW1wOiBEYXRlLm5vdygpXG4gIH07XG4gIHdyaXRlRmlsZVN5bmMoY2FjaGVQYXRoLCBKU09OLnN0cmluZ2lmeShjYWNoZSwgbnVsbCwgMikpO1xufTtcblxuY29uc3QgZmV0Y2hQdWJsaWNJcCA9IChmb3JjZVJlZnJlc2g6IGJvb2xlYW4gPSBmYWxzZSk6IFByb21pc2U8c3RyaW5nIHwgdW5kZWZpbmVkPiA9PiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICBpZighZm9yY2VSZWZyZXNoKSB7XG4gICAgY29uc3QgY2FjaGVkID0gcmVhZFB1YmxpY0lwQ2FjaGUoKTtcbiAgICBpZihjYWNoZWQpIHtcbiAgICAgIHJlc29sdmUoY2FjaGVkLmlwKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gIH1cblxuICAvLyBVc2UgZmV0Y2ggaW5zdGVhZCBvZiBodHRwc1xuICBmZXRjaCgnaHR0cHM6Ly9hcGkuaXBpZnkub3JnJylcbiAgICAudGhlbigocmVzKSA9PiByZXMudGV4dCgpKVxuICAgIC50aGVuKChkYXRhKSA9PiB7XG4gICAgICBjb25zdCBpcCA9IGRhdGEudHJpbSgpO1xuICAgICAgaWYoaXApIHtcbiAgICAgICAgd3JpdGVQdWJsaWNJcENhY2hlKGlwKTtcbiAgICAgIH1cbiAgICAgIHJlc29sdmUoaXApO1xuICAgIH0pXG4gICAgLmNhdGNoKCgpID0+IHJlc29sdmUodW5kZWZpbmVkKSk7XG59KTtcblxuY29uc3QgZ2V0TmV0d29ya0FkZHJlc3NlcyA9ICgpID0+IHtcbiAgY29uc3QgaW50ZXJmYWNlcyA9IG5ldHdvcmtJbnRlcmZhY2VzKCk7XG4gIGNvbnN0IGFkZHJlc3NlcyA9IHtcbiAgICBsb2NhbDogJ2xvY2FsaG9zdCcsXG4gICAgcHJpdmF0ZTogbnVsbCxcbiAgICBwdWJsaWM6IG51bGxcbiAgfTtcblxuICBmb3IoY29uc3QgbmFtZSBvZiBPYmplY3Qua2V5cyhpbnRlcmZhY2VzKSkge1xuICAgIGNvbnN0IG5ldHdvcmtJbnRlcmZhY2UgPSBpbnRlcmZhY2VzW25hbWVdO1xuICAgIGlmKCFuZXR3b3JrSW50ZXJmYWNlKSB7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG5cbiAgICBmb3IoY29uc3QgaWZhY2Ugb2YgbmV0d29ya0ludGVyZmFjZSkge1xuICAgICAgaWYoaWZhY2UuZmFtaWx5ID09PSAnSVB2NCcgJiYgIWlmYWNlLmludGVybmFsKSB7XG4gICAgICAgIGNvbnN0IGlwID0gaWZhY2UuYWRkcmVzcztcblxuICAgICAgICBpZihpcC5zdGFydHNXaXRoKCcxMC4nKSB8fCBpcC5zdGFydHNXaXRoKCcxOTIuMTY4LicpIHx8IGlwLnN0YXJ0c1dpdGgoJzE3Mi4nKSkge1xuICAgICAgICAgIGlmKCFhZGRyZXNzZXMucHJpdmF0ZSkge1xuICAgICAgICAgICAgYWRkcmVzc2VzLnByaXZhdGUgPSBpcDtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgaWYoIWFkZHJlc3Nlcy5wdWJsaWMpIHtcbiAgICAgICAgICAgIGFkZHJlc3Nlcy5wdWJsaWMgPSBpcDtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICByZXR1cm4gYWRkcmVzc2VzO1xufTtcblxuY29uc3QgZGlzcGxheVNlcnZlclN0YXR1cyA9IChcbiAgaHR0cFBvcnQ6IG51bWJlcixcbiAgaHR0cHNQb3J0OiBudW1iZXIsXG4gIHdzUG9ydDogbnVtYmVyLFxuICBob3N0OiBzdHJpbmcsXG4gIHF1aWV0OiBib29sZWFuLFxuICBwdWJsaWNJcD86IHN0cmluZ1xuKSA9PiB7XG4gIGlmKHF1aWV0KSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgY29uc3QgaHR0cFVybCA9IGBodHRwOi8vJHtob3N0fToke2h0dHBQb3J0fWA7XG4gIGNvbnN0IGh0dHBzVXJsID0gYGh0dHBzOi8vJHtob3N0fToke2h0dHBzUG9ydH1gO1xuICBjb25zdCB3c1VybCA9IGB3czovLyR7aG9zdH06JHt3c1BvcnR9YDtcbiAgY29uc3Qgd3NzVXJsID0gYHdzczovLyR7aG9zdH06JHt3c1BvcnR9YDtcblxuICBsZXQgdXJsTGluZXMgPSBgJHtjaGFsay5ncmVlbignSFRUUDonKX0gICAgICAke2NoYWxrLnVuZGVybGluZShodHRwVXJsKX1cXG5gO1xuICB1cmxMaW5lcyArPSBgJHtjaGFsay5ncmVlbignSFRUUFM6Jyl9ICAgICAke2NoYWxrLnVuZGVybGluZShodHRwc1VybCl9XFxuYDtcbiAgdXJsTGluZXMgKz0gYCR7Y2hhbGsuZ3JlZW4oJ1dlYlNvY2tldDonKX0gJHtjaGFsay51bmRlcmxpbmUod3NVcmwpfVxcbmA7XG4gIHVybExpbmVzICs9IGAke2NoYWxrLmdyZWVuKCdXU1M6Jyl9ICAgICAgICR7Y2hhbGsudW5kZXJsaW5lKHdzc1VybCl9XFxuYDtcblxuICBpZihwdWJsaWNJcCkge1xuICAgIHVybExpbmVzICs9IGBcXG4ke2NoYWxrLmdyZWVuKCdQdWJsaWM6Jyl9ICAgICR7Y2hhbGsudW5kZXJsaW5lKGBodHRwOi8vJHtwdWJsaWNJcH06JHtodHRwUG9ydH1gKX1cXG5gO1xuICB9XG5cbiAgY29uc3Qgc3RhdHVzQm94ID0gYm94ZW4oXG4gICAgYCR7Y2hhbGsuY3lhbi5ib2xkKCdcdUQ4M0RcdURFODAgU2VydmVybGVzcyBEZXZlbG9wbWVudCBTZXJ2ZXIgUnVubmluZycpfVxcblxcbiR7dXJsTGluZXN9XFxuYCArXG4gICAgYCR7Y2hhbGsueWVsbG93KCdQcmVzcyBDdHJsK0MgdG8gc3RvcCB0aGUgc2VydmVyJyl9YCxcbiAgICB7XG4gICAgICBwYWRkaW5nOiAxLFxuICAgICAgbWFyZ2luOiAxLFxuICAgICAgYm9yZGVyU3R5bGU6ICdyb3VuZCcsXG4gICAgICBib3JkZXJDb2xvcjogJ2N5YW4nLFxuICAgICAgYmFja2dyb3VuZENvbG9yOiAnIzFhMWExYSdcbiAgICB9XG4gICk7XG5cbiAgY29uc29sZS5sb2coYFxcbiR7c3RhdHVzQm94fVxcbmApO1xufTtcblxuY29uc3QgbG9hZEhhbmRsZXIgPSBhc3luYyAoaGFuZGxlclBhdGg6IHN0cmluZywgb3V0cHV0RGlyOiBzdHJpbmcpID0+IHtcbiAgdHJ5IHtcbiAgICBjb25zdCBmdWxsUGF0aCA9IHBhdGhSZXNvbHZlKG91dHB1dERpciwgaGFuZGxlclBhdGgpO1xuICAgIGxvZyhgTG9hZGluZyBoYW5kbGVyIGZyb206ICR7ZnVsbFBhdGh9YCwgJ2luZm8nLCBmYWxzZSk7XG5cbiAgICBpZighZXhpc3RzU3luYyhmdWxsUGF0aCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgSGFuZGxlciBmaWxlIG5vdCBmb3VuZDogJHtmdWxsUGF0aH1gKTtcbiAgICB9XG5cbiAgICAvLyBEeW5hbWljIGltcG9ydCBvZiB0aGUgaGFuZGxlciB3aXRoIGJldHRlciBlcnJvciBoYW5kbGluZ1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBoYW5kbGVyTW9kdWxlID0gYXdhaXQgaW1wb3J0KGZ1bGxQYXRoKTtcbiAgICAgIGxvZyhgSGFuZGxlciBtb2R1bGUgbG9hZGVkOiAke09iamVjdC5rZXlzKGhhbmRsZXJNb2R1bGUpfWAsICdpbmZvJywgZmFsc2UpO1xuXG4gICAgICBjb25zdCBoYW5kbGVyID0gaGFuZGxlck1vZHVsZS5kZWZhdWx0IHx8IGhhbmRsZXJNb2R1bGUuaGFuZGxlciB8fCBoYW5kbGVyTW9kdWxlO1xuICAgICAgbG9nKGBIYW5kbGVyIGZvdW5kOiAke3R5cGVvZiBoYW5kbGVyfWAsICdpbmZvJywgZmFsc2UpO1xuXG4gICAgICByZXR1cm4gaGFuZGxlcjtcbiAgICB9IGNhdGNoIChpbXBvcnRFcnJvcikge1xuICAgICAgbG9nKGBJbXBvcnQgZXJyb3IgZm9yIGhhbmRsZXIgJHtoYW5kbGVyUGF0aH06ICR7aW1wb3J0RXJyb3IubWVzc2FnZX1gLCAnZXJyb3InLCBmYWxzZSk7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgbG9nKGBFcnJvciBsb2FkaW5nIGhhbmRsZXIgJHtoYW5kbGVyUGF0aH06ICR7ZXJyb3IubWVzc2FnZX1gLCAnZXJyb3InLCBmYWxzZSk7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbn07XG5cbmNvbnN0IGNhcHR1cmVDb25zb2xlTG9ncyA9IChoYW5kbGVyOiBGdW5jdGlvbiwgcXVpZXQ6IGJvb2xlYW4pID0+IHtcbiAgaWYocXVpZXQpIHtcbiAgICByZXR1cm4gaGFuZGxlcjtcbiAgfVxuXG4gIHJldHVybiBhc3luYyAoZXZlbnQ6IGFueSwgY29udGV4dDogYW55KSA9PiB7XG4gICAgLy8gQ2FwdHVyZSBjb25zb2xlLmxvZywgY29uc29sZS5lcnJvciwgZXRjLlxuICAgIGNvbnN0IG9yaWdpbmFsQ29uc29sZUxvZyA9IGNvbnNvbGUubG9nO1xuICAgIGNvbnN0IG9yaWdpbmFsQ29uc29sZUVycm9yID0gY29uc29sZS5lcnJvcjtcbiAgICBjb25zdCBvcmlnaW5hbENvbnNvbGVXYXJuID0gY29uc29sZS53YXJuO1xuICAgIGNvbnN0IG9yaWdpbmFsQ29uc29sZUluZm8gPSBjb25zb2xlLmluZm87XG5cbiAgICBjb25zdCBsb2dzOiBzdHJpbmdbXSA9IFtdO1xuXG4gICAgY29uc29sZS5sb2cgPSAoLi4uYXJnczogYW55W10pID0+IHtcbiAgICAgIGxvZ3MucHVzaChgW0xPR10gJHthcmdzLmpvaW4oJyAnKX1gKTtcbiAgICAgIG9yaWdpbmFsQ29uc29sZUxvZyguLi5hcmdzKTtcbiAgICB9O1xuXG4gICAgY29uc29sZS5lcnJvciA9ICguLi5hcmdzOiBhbnlbXSkgPT4ge1xuICAgICAgbG9ncy5wdXNoKGBbRVJST1JdICR7YXJncy5qb2luKCcgJyl9YCk7XG4gICAgICBvcmlnaW5hbENvbnNvbGVFcnJvciguLi5hcmdzKTtcbiAgICB9O1xuXG4gICAgY29uc29sZS53YXJuID0gKC4uLmFyZ3M6IGFueVtdKSA9PiB7XG4gICAgICBsb2dzLnB1c2goYFtXQVJOXSAke2FyZ3Muam9pbignICcpfWApO1xuICAgICAgb3JpZ2luYWxDb25zb2xlV2FybiguLi5hcmdzKTtcbiAgICB9O1xuXG4gICAgY29uc29sZS5pbmZvID0gKC4uLmFyZ3M6IGFueVtdKSA9PiB7XG4gICAgICBsb2dzLnB1c2goYFtJTkZPXSAke2FyZ3Muam9pbignICcpfWApO1xuICAgICAgb3JpZ2luYWxDb25zb2xlSW5mbyguLi5hcmdzKTtcbiAgICB9O1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGhhbmRsZXIoZXZlbnQsIGNvbnRleHQpO1xuXG4gICAgICAvLyBPdXRwdXQgY2FwdHVyZWQgbG9nc1xuICAgICAgaWYobG9ncy5sZW5ndGggPiAwKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGNoYWxrLmdyYXkoJy0tLSBIYW5kbGVyIENvbnNvbGUgT3V0cHV0IC0tLScpKTtcbiAgICAgICAgbG9ncy5mb3JFYWNoKChsb2cpID0+IGNvbnNvbGUubG9nKGNoYWxrLmdyYXkobG9nKSkpO1xuICAgICAgICBjb25zb2xlLmxvZyhjaGFsay5ncmF5KCctLS0gRW5kIEhhbmRsZXIgQ29uc29sZSBPdXRwdXQgLS0tJykpO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH0gZmluYWxseSB7XG4gICAgICAvLyBSZXN0b3JlIG9yaWdpbmFsIGNvbnNvbGUgbWV0aG9kc1xuICAgICAgY29uc29sZS5sb2cgPSBvcmlnaW5hbENvbnNvbGVMb2c7XG4gICAgICBjb25zb2xlLmVycm9yID0gb3JpZ2luYWxDb25zb2xlRXJyb3I7XG4gICAgICBjb25zb2xlLndhcm4gPSBvcmlnaW5hbENvbnNvbGVXYXJuO1xuICAgICAgY29uc29sZS5pbmZvID0gb3JpZ2luYWxDb25zb2xlSW5mbztcbiAgICB9XG4gIH07XG59O1xuXG5jb25zdCBjcmVhdGVFeHByZXNzU2VydmVyID0gYXN5bmMgKGNvbmZpZzogU2VydmVybGVzc0NvbmZpZywgb3V0cHV0RGlyOiBzdHJpbmcsIGh0dHBQb3J0OiBudW1iZXIsIGhvc3Q6IHN0cmluZywgcXVpZXQ6IGJvb2xlYW4sIGRlYnVnOiBib29sZWFuLCBwcmludE91dHB1dDogYm9vbGVhbikgPT4ge1xuICBjb25zdCBhcHAgPSBleHByZXNzKCk7XG5cbiAgLy8gRW5hYmxlIENPUlNcbiAgYXBwLnVzZSgocmVxLCByZXMsIG5leHQpID0+IHtcbiAgICByZXMuaGVhZGVyKCdBY2Nlc3MtQ29udHJvbC1BbGxvdy1PcmlnaW4nLCAnKicpO1xuICAgIHJlcy5oZWFkZXIoJ0FjY2Vzcy1Db250cm9sLUFsbG93LU1ldGhvZHMnLCAnR0VULCBQT1NULCBQVVQsIERFTEVURSwgUEFUQ0gsIE9QVElPTlMnKTtcbiAgICByZXMuaGVhZGVyKCdBY2Nlc3MtQ29udHJvbC1BbGxvdy1IZWFkZXJzJywgJyonKTtcbiAgICByZXMuaGVhZGVyKCdBY2Nlc3MtQ29udHJvbC1BbGxvdy1DcmVkZW50aWFscycsICd0cnVlJyk7XG5cbiAgICBpZihyZXEubWV0aG9kID09PSAnT1BUSU9OUycpIHtcbiAgICAgIHJlcy5zZW5kU3RhdHVzKDIwMCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIG5leHQoKTtcbiAgICB9XG4gIH0pO1xuXG4gIC8vIFBhcnNlIEpTT04gYm9kaWVzXG4gIGFwcC51c2UoZXhwcmVzcy5qc29uKCkpO1xuXG4gIC8vIExvYWQgR3JhcGhRTCBoYW5kbGVyXG4gIGNvbnN0IGxvYWRHcmFwaFFMU2NoZW1hID0gYXN5bmMgKCkgPT4ge1xuICAgIHRyeSB7XG4gICAgICAvLyBUcnkgdG8gZmluZCBhIEdyYXBoUUwgaGFuZGxlclxuICAgICAgbGV0IGdyYXBocWxIYW5kbGVyID0gbnVsbDtcblxuICAgICAgaWYoY29uZmlnLmZ1bmN0aW9ucykge1xuICAgICAgICBmb3IoY29uc3QgW2Z1bmN0aW9uTmFtZSwgZnVuY3Rpb25Db25maWddIG9mIE9iamVjdC5lbnRyaWVzKGNvbmZpZy5mdW5jdGlvbnMpKSB7XG4gICAgICAgICAgaWYoZnVuY3Rpb25Db25maWcuZXZlbnRzKSB7XG4gICAgICAgICAgICBmb3IoY29uc3QgZXZlbnQgb2YgZnVuY3Rpb25Db25maWcuZXZlbnRzKSB7XG4gICAgICAgICAgICAgIGlmKGV2ZW50Lmh0dHAgJiYgZXZlbnQuaHR0cC5wYXRoKSB7XG4gICAgICAgICAgICAgICAgLy8gTG9vayBmb3IgR3JhcGhRTCBlbmRwb2ludHNcbiAgICAgICAgICAgICAgICBpZihldmVudC5odHRwLnBhdGggPT09ICcvcHVibGljJyB8fCBldmVudC5odHRwLnBhdGggPT09ICcvZ3JhcGhxbCcpIHtcbiAgICAgICAgICAgICAgICAgIGdyYXBocWxIYW5kbGVyID0gYXdhaXQgbG9hZEhhbmRsZXIoZnVuY3Rpb25Db25maWcuaGFuZGxlciwgb3V0cHV0RGlyKTtcbiAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBpZihncmFwaHFsSGFuZGxlcikge1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmKGdyYXBocWxIYW5kbGVyKSB7XG4gICAgICAgIGxvZygnRm91bmQgR3JhcGhRTCBoYW5kbGVyJywgJ2luZm8nLCBxdWlldCk7XG4gICAgICAgIHJldHVybiBncmFwaHFsSGFuZGxlcjtcbiAgICAgIH1cbiAgICAgIHJldHVybiBudWxsO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2coYEVycm9yIGxvYWRpbmcgR3JhcGhRTCBoYW5kbGVyOiAke2Vycm9yLm1lc3NhZ2V9YCwgJ2Vycm9yJywgcXVpZXQpO1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICB9O1xuXG4gIC8vIFNldCB1cCBHcmFwaFFMIGhhbmRsZXIgZm9yIEdyYXBoUUwgcmVxdWVzdHNcbiAgdHJ5IHtcbiAgICBjb25zdCBncmFwaHFsSGFuZGxlciA9IGF3YWl0IGxvYWRHcmFwaFFMU2NoZW1hKCk7XG4gICAgaWYoZ3JhcGhxbEhhbmRsZXIpIHtcbiAgICAgIC8vIEZpbmQgdGhlIEdyYXBoUUwgcGF0aCBmcm9tIHRoZSBzZXJ2ZXJsZXNzIGNvbmZpZ1xuICAgICAgbGV0IGdyYXBocWxQYXRoID0gJy9ncmFwaHFsJzsgLy8gZGVmYXVsdCBmYWxsYmFja1xuXG4gICAgICBpZihjb25maWcuZnVuY3Rpb25zKSB7XG4gICAgICAgIGZvcihjb25zdCBbX2Z1bmN0aW9uTmFtZSwgZnVuY3Rpb25Db25maWddIG9mIE9iamVjdC5lbnRyaWVzKGNvbmZpZy5mdW5jdGlvbnMpKSB7XG4gICAgICAgICAgaWYoZnVuY3Rpb25Db25maWcuZXZlbnRzKSB7XG4gICAgICAgICAgICBmb3IoY29uc3QgZXZlbnQgb2YgZnVuY3Rpb25Db25maWcuZXZlbnRzKSB7XG4gICAgICAgICAgICAgIGlmKGV2ZW50Py5odHRwPy5wYXRoKSB7XG4gICAgICAgICAgICAgICAgZ3JhcGhxbFBhdGggPSBldmVudC5odHRwLnBhdGg7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgaWYoZ3JhcGhxbFBhdGggIT09ICcvZ3JhcGhxbCcpIHtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBTZXQgdXAgR3JhcGhRTCBlbmRwb2ludCB3aXRoIGVuaGFuY2VkIGNvbnNvbGUubG9nIGNhcHR1cmVcbiAgICAgIGFwcC51c2UoZ3JhcGhxbFBhdGgsIGFzeW5jIChyZXEsIHJlcykgPT4ge1xuICAgICAgICAvLyBHcmFwaFFMIERlYnVnIExvZ2dpbmdcbiAgICAgICAgaWYoZGVidWcgJiYgcmVxLmJvZHkgJiYgcmVxLmJvZHkucXVlcnkpIHtcbiAgICAgICAgICBsb2coJ1x1RDgzRFx1REQwRCBHcmFwaFFMIERlYnVnIE1vZGU6IEFuYWx5emluZyByZXF1ZXN0Li4uJywgJ2luZm8nLCBmYWxzZSk7XG4gICAgICAgICAgbG9nKGBcdUQ4M0RcdURDREQgR3JhcGhRTCBRdWVyeTogJHtyZXEuYm9keS5xdWVyeX1gLCAnaW5mbycsIGZhbHNlKTtcbiAgICAgICAgICBpZihyZXEuYm9keS52YXJpYWJsZXMpIHtcbiAgICAgICAgICAgIGxvZyhgXHVEODNEXHVEQ0NBIEdyYXBoUUwgVmFyaWFibGVzOiAke0pTT04uc3RyaW5naWZ5KHJlcS5ib2R5LnZhcmlhYmxlcywgbnVsbCwgMil9YCwgJ2luZm8nLCBmYWxzZSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmKHJlcS5ib2R5Lm9wZXJhdGlvbk5hbWUpIHtcbiAgICAgICAgICAgIGxvZyhgXHVEODNDXHVERkY3XHVGRTBGICBHcmFwaFFMIE9wZXJhdGlvbjogJHtyZXEuYm9keS5vcGVyYXRpb25OYW1lfWAsICdpbmZvJywgZmFsc2UpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEVuaGFuY2VkIGNvbnNvbGUubG9nIGNhcHR1cmVcbiAgICAgICAgY29uc3Qgb3JpZ2luYWxDb25zb2xlTG9nID0gY29uc29sZS5sb2c7XG4gICAgICAgIGNvbnN0IGxvZ3M6IHN0cmluZ1tdID0gW107XG5cbiAgICAgICAgY29uc29sZS5sb2cgPSAoLi4uYXJncykgPT4ge1xuICAgICAgICAgIGNvbnN0IGxvZ01lc3NhZ2UgPSBhcmdzLm1hcCgoYXJnKSA9PlxuICAgICAgICAgICAgKHR5cGVvZiBhcmcgPT09ICdvYmplY3QnID8gSlNPTi5zdHJpbmdpZnkoYXJnLCBudWxsLCAyKSA6IFN0cmluZyhhcmcpKVxuICAgICAgICAgICkuam9pbignICcpO1xuICAgICAgICAgIGxvZ3MucHVzaChsb2dNZXNzYWdlKTtcbiAgICAgICAgICBvcmlnaW5hbENvbnNvbGVMb2coYFtHcmFwaFFMXSAke2xvZ01lc3NhZ2V9YCk7XG4gICAgICAgIH07XG5cbiAgICAgICAgLy8gQ3JlYXRlIGNvbnRleHQgZm9yIHRoZSBoYW5kbGVyXG4gICAgICAgIGNvbnN0IGNvbnRleHQgPSB7XG4gICAgICAgICAgcmVxLFxuICAgICAgICAgIHJlcyxcbiAgICAgICAgICBmdW5jdGlvbk5hbWU6ICdncmFwaHFsJyxcbiAgICAgICAgICBmdW5jdGlvblZlcnNpb246ICckTEFURVNUJyxcbiAgICAgICAgICBpbnZva2VkRnVuY3Rpb25Bcm46ICdhcm46YXdzOmxhbWJkYTp1cy1lYXN0LTE6MTIzNDU2Nzg5MDEyOmZ1bmN0aW9uOmdyYXBocWwnLFxuICAgICAgICAgIG1lbW9yeUxpbWl0SW5NQjogJzEyOCcsXG4gICAgICAgICAgYXdzUmVxdWVzdElkOiAndGVzdC1yZXF1ZXN0LWlkJyxcbiAgICAgICAgICBsb2dHcm91cE5hbWU6ICcvYXdzL2xhbWJkYS9ncmFwaHFsJyxcbiAgICAgICAgICBsb2dTdHJlYW1OYW1lOiAndGVzdC1sb2ctc3RyZWFtJyxcbiAgICAgICAgICBnZXRSZW1haW5pbmdUaW1lSW5NaWxsaXM6ICgpID0+IDMwMDAwXG4gICAgICAgIH07XG5cbiAgICAgICAgLy8gV3JhcCBoYW5kbGVyIHdpdGggY29uc29sZSBsb2cgY2FwdHVyZVxuICAgICAgICBjb25zdCB3cmFwcGVkSGFuZGxlciA9IGNhcHR1cmVDb25zb2xlTG9ncyhncmFwaHFsSGFuZGxlciwgcXVpZXQpO1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgLy8gQ2FsbCB0aGUgaGFuZGxlciB3aXRoIEdyYXBoUUwgcGFyYW1ldGVyc1xuICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHdyYXBwZWRIYW5kbGVyKHtcbiAgICAgICAgICAgIGh0dHBNZXRob2Q6ICdQT1NUJyxcbiAgICAgICAgICAgIHBhdGg6IGdyYXBocWxQYXRoLFxuICAgICAgICAgICAgaGVhZGVyczogcmVxLmhlYWRlcnMsXG4gICAgICAgICAgICBxdWVyeVN0cmluZ1BhcmFtZXRlcnM6IHt9LFxuICAgICAgICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnkocmVxLmJvZHkpXG4gICAgICAgICAgfSwgY29udGV4dCk7XG5cbiAgICAgICAgICAvLyBSZXN0b3JlIGNvbnNvbGUubG9nXG4gICAgICAgICAgY29uc29sZS5sb2cgPSBvcmlnaW5hbENvbnNvbGVMb2c7XG5cbiAgICAgICAgICAvLyBIYW5kbGUgdGhlIHJlc3VsdFxuICAgICAgICAgIGlmKHJlc3VsdCAmJiB0eXBlb2YgcmVzdWx0ID09PSAnb2JqZWN0JyAmJiByZXN1bHQuc3RhdHVzQ29kZSkge1xuICAgICAgICAgICAgcmVzLnN0YXR1cyhyZXN1bHQuc3RhdHVzQ29kZSk7XG4gICAgICAgICAgICBpZihyZXN1bHQuaGVhZGVycykge1xuICAgICAgICAgICAgICBPYmplY3QuZW50cmllcyhyZXN1bHQuaGVhZGVycykuZm9yRWFjaCgoW2tleSwgdmFsdWVdKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzLnNldEhlYWRlcihrZXksIFN0cmluZyh2YWx1ZSkpO1xuICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJlcy5zZW5kKHJlc3VsdC5ib2R5KTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmVzLmpzb24ocmVzdWx0KTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgLy8gUmVzdG9yZSBjb25zb2xlLmxvZ1xuICAgICAgICAgIGNvbnNvbGUubG9nID0gb3JpZ2luYWxDb25zb2xlTG9nO1xuICAgICAgICAgIGxvZyhgR3JhcGhRTCBoYW5kbGVyIGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCwgJ2Vycm9yJywgZmFsc2UpO1xuICAgICAgICAgIHJlcy5zdGF0dXMoNTAwKS5qc29uKHtlcnJvcjogZXJyb3IubWVzc2FnZX0pO1xuICAgICAgICB9XG4gICAgICB9KTtcblxuICAgICAgbG9nKGBHcmFwaFFMIGVuZHBvaW50IGF2YWlsYWJsZSBhdCBodHRwOi8vJHtob3N0fToke2h0dHBQb3J0fSR7Z3JhcGhxbFBhdGh9YCwgJ2luZm8nLCBxdWlldCk7XG4gICAgfVxuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGxvZyhgRXJyb3Igc2V0dGluZyB1cCBHcmFwaFFMOiAke2Vycm9yLm1lc3NhZ2V9YCwgJ2Vycm9yJywgcXVpZXQpO1xuICB9XG5cbiAgLy8gRmFsbGJhY2sgZm9yIG5vbi1HcmFwaFFMIHJvdXRlcyAtIGhhbmRsZSBhbGwgcmVtYWluaW5nIHJvdXRlc1xuICBhcHAudXNlKCcvJywgYXN5bmMgKHJlcSwgcmVzKSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHVybCA9IHJlcS51cmwgfHwgJy8nO1xuICAgICAgY29uc3QgbWV0aG9kID0gcmVxLm1ldGhvZCB8fCAnR0VUJztcblxuICAgICAgbG9nKGAke21ldGhvZH0gJHt1cmx9YCwgJ2luZm8nLCBmYWxzZSk7XG5cbiAgICAgIC8vIEZpbmQgbWF0Y2hpbmcgZnVuY3Rpb25cbiAgICAgIGxldCBtYXRjaGVkRnVuY3Rpb24gPSBudWxsO1xuXG4gICAgICBpZihjb25maWcuZnVuY3Rpb25zKSB7XG4gICAgICAgIGZvcihjb25zdCBbZnVuY3Rpb25OYW1lLCBmdW5jdGlvbkNvbmZpZ10gb2YgT2JqZWN0LmVudHJpZXMoY29uZmlnLmZ1bmN0aW9ucykpIHtcbiAgICAgICAgICBpZihmdW5jdGlvbkNvbmZpZy5ldmVudHMpIHtcbiAgICAgICAgICAgIGZvcihjb25zdCBldmVudCBvZiBmdW5jdGlvbkNvbmZpZy5ldmVudHMpIHtcbiAgICAgICAgICAgICAgaWYoZXZlbnQuaHR0cCkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGV2ZW50UGF0aCA9IGV2ZW50Lmh0dHAucGF0aCB8fCAnLyc7XG4gICAgICAgICAgICAgICAgY29uc3QgZXZlbnRNZXRob2QgPSBldmVudC5odHRwLm1ldGhvZCB8fCAnR0VUJztcblxuICAgICAgICAgICAgICAgIC8vIFNpbXBsZSBwYXRoIG1hdGNoaW5nIC0gYXZvaWQgY29tcGxleCByZWdleFxuICAgICAgICAgICAgICAgIGlmKGV2ZW50UGF0aCAmJiBldmVudFBhdGggPT09IHVybCAmJiBldmVudE1ldGhvZCA9PT0gbWV0aG9kKSB7XG4gICAgICAgICAgICAgICAgICBtYXRjaGVkRnVuY3Rpb24gPSBmdW5jdGlvbk5hbWU7XG4gICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgaWYobWF0Y2hlZEZ1bmN0aW9uKSB7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYobWF0Y2hlZEZ1bmN0aW9uICYmIGNvbmZpZy5mdW5jdGlvbnNbbWF0Y2hlZEZ1bmN0aW9uXSkge1xuICAgICAgICAvLyBSZXNvbHZlIGhhbmRsZXIgcGF0aCByZWxhdGl2ZSB0byBvdXRwdXQgZGlyZWN0b3J5XG4gICAgICAgIGNvbnN0IGhhbmRsZXJQYXRoID0gY29uZmlnLmZ1bmN0aW9uc1ttYXRjaGVkRnVuY3Rpb25dLmhhbmRsZXI7XG4gICAgICAgIGNvbnN0IGhhbmRsZXIgPSBhd2FpdCBsb2FkSGFuZGxlcihoYW5kbGVyUGF0aCwgb3V0cHV0RGlyKTtcblxuICAgICAgICBpZihoYW5kbGVyKSB7XG4gICAgICAgICAgY29uc3Qgd3JhcHBlZEhhbmRsZXIgPSBjYXB0dXJlQ29uc29sZUxvZ3MoaGFuZGxlciwgcXVpZXQpO1xuXG4gICAgICAgICAgY29uc3QgZXZlbnQgPSB7XG4gICAgICAgICAgICBib2R5OiByZXEuYm9keSxcbiAgICAgICAgICAgIGhlYWRlcnM6IHJlcS5oZWFkZXJzLFxuICAgICAgICAgICAgaHR0cE1ldGhvZDogbWV0aG9kLFxuICAgICAgICAgICAgcGF0aDogdXJsLFxuICAgICAgICAgICAgcXVlcnlTdHJpbmdQYXJhbWV0ZXJzOiByZXEucXVlcnlcbiAgICAgICAgICB9O1xuXG4gICAgICAgICAgY29uc3QgY29udGV4dCA9IHtcbiAgICAgICAgICAgIGZ1bmN0aW9uTmFtZTogbWF0Y2hlZEZ1bmN0aW9uLFxuICAgICAgICAgICAgZnVuY3Rpb25WZXJzaW9uOiAnJExBVEVTVCcsXG4gICAgICAgICAgICBpbnZva2VkRnVuY3Rpb25Bcm46IGBhcm46YXdzOmxhbWJkYTp1cy1lYXN0LTE6MTIzNDU2Nzg5MDEyOmZ1bmN0aW9uOiR7bWF0Y2hlZEZ1bmN0aW9ufWAsXG4gICAgICAgICAgICBtZW1vcnlMaW1pdEluTUI6ICcxMjgnLFxuICAgICAgICAgICAgYXdzUmVxdWVzdElkOiAndGVzdC1yZXF1ZXN0LWlkJyxcbiAgICAgICAgICAgIGxvZ0dyb3VwTmFtZTogYC9hd3MvbGFtYmRhLyR7bWF0Y2hlZEZ1bmN0aW9ufWAsXG4gICAgICAgICAgICBsb2dTdHJlYW1OYW1lOiAndGVzdC1sb2ctc3RyZWFtJyxcbiAgICAgICAgICAgIGdldFJlbWFpbmluZ1RpbWVJbk1pbGxpczogKCkgPT4gMzAwMDBcbiAgICAgICAgICB9O1xuXG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHdyYXBwZWRIYW5kbGVyKGV2ZW50LCBjb250ZXh0KTtcblxuICAgICAgICAgICAgaWYocmVzdWx0ICYmIHR5cGVvZiByZXN1bHQgPT09ICdvYmplY3QnICYmIHJlc3VsdC5zdGF0dXNDb2RlKSB7XG4gICAgICAgICAgICAgIHJlcy5zdGF0dXMocmVzdWx0LnN0YXR1c0NvZGUpO1xuICAgICAgICAgICAgICBpZihyZXN1bHQuaGVhZGVycykge1xuICAgICAgICAgICAgICAgIE9iamVjdC5lbnRyaWVzKHJlc3VsdC5oZWFkZXJzKS5mb3JFYWNoKChba2V5LCB2YWx1ZV0pID0+IHtcbiAgICAgICAgICAgICAgICAgIHJlcy5zZXRIZWFkZXIoa2V5LCBTdHJpbmcodmFsdWUpKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICByZXMuc2VuZChyZXN1bHQuYm9keSk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICByZXMuanNvbihyZXN1bHQpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICBsb2coYEhhbmRsZXIgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gLCAnZXJyb3InLCBmYWxzZSk7XG4gICAgICAgICAgICByZXMuc3RhdHVzKDUwMCkuanNvbih7ZXJyb3I6IGVycm9yLm1lc3NhZ2V9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmVzLnN0YXR1cyg0MDQpLmpzb24oe2Vycm9yOiAnSGFuZGxlciBub3QgZm91bmQnfSk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJlcy5zdGF0dXMoNDA0KS5qc29uKHtlcnJvcjogJ0Z1bmN0aW9uIG5vdCBmb3VuZCd9KTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nKGBSb3V0ZSBoYW5kbGluZyBlcnJvcjogJHtlcnJvci5tZXNzYWdlfWAsICdlcnJvcicsIGZhbHNlKTtcbiAgICAgIHJlcy5zdGF0dXMoNTAwKS5qc29uKHtlcnJvcjogZXJyb3IubWVzc2FnZX0pO1xuICAgIH1cbiAgfSk7XG5cbiAgcmV0dXJuIGFwcDtcbn07XG5cbmNvbnN0IGNyZWF0ZVdlYlNvY2tldFNlcnZlciA9IChjb25maWc6IFNlcnZlcmxlc3NDb25maWcsIG91dHB1dERpcjogc3RyaW5nLCB3c1BvcnQ6IG51bWJlciwgcXVpZXQ6IGJvb2xlYW4sIGRlYnVnOiBib29sZWFuLCBwcmludE91dHB1dDogYm9vbGVhbikgPT4ge1xuICBjb25zdCB3c3MgPSBuZXcgV2ViU29ja2V0U2VydmVyKHtwb3J0OiB3c1BvcnR9KTtcblxuICB3c3Mub24oJ2Nvbm5lY3Rpb24nLCBhc3luYyAod3MsIHJlcSkgPT4ge1xuICAgIGxvZyhgV2ViU29ja2V0IGNvbm5lY3Rpb24gZXN0YWJsaXNoZWQ6ICR7cmVxLnVybH1gLCAnaW5mbycsIGZhbHNlKTtcblxuICAgIHdzLm9uKCdtZXNzYWdlJywgYXN5bmMgKG1lc3NhZ2UpID0+IHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IGRhdGEgPSBKU09OLnBhcnNlKG1lc3NhZ2UudG9TdHJpbmcoKSk7XG5cbiAgICAgICAgLy8gRmluZCBtYXRjaGluZyBXZWJTb2NrZXQgZnVuY3Rpb25cbiAgICAgICAgbGV0IG1hdGNoZWRGdW5jdGlvbiA9IG51bGw7XG5cbiAgICAgICAgaWYoY29uZmlnLmZ1bmN0aW9ucykge1xuICAgICAgICAgIGZvcihjb25zdCBbZnVuY3Rpb25OYW1lLCBmdW5jdGlvbkNvbmZpZ10gb2YgT2JqZWN0LmVudHJpZXMoY29uZmlnLmZ1bmN0aW9ucykpIHtcbiAgICAgICAgICAgIGlmKGZ1bmN0aW9uQ29uZmlnLmV2ZW50cykge1xuICAgICAgICAgICAgICBmb3IoY29uc3QgZXZlbnQgb2YgZnVuY3Rpb25Db25maWcuZXZlbnRzKSB7XG4gICAgICAgICAgICAgICAgaWYoZXZlbnQud2Vic29ja2V0KSB7XG4gICAgICAgICAgICAgICAgICBjb25zdCByb3V0ZSA9IGV2ZW50LndlYnNvY2tldC5yb3V0ZSB8fCAnJGNvbm5lY3QnO1xuICAgICAgICAgICAgICAgICAgaWYocm91dGUgPT09ICckZGVmYXVsdCcgfHwgcm91dGUgPT09IGRhdGEuYWN0aW9uKSB7XG4gICAgICAgICAgICAgICAgICAgIG1hdGNoZWRGdW5jdGlvbiA9IGZ1bmN0aW9uTmFtZTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZihtYXRjaGVkRnVuY3Rpb24pIHtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYobWF0Y2hlZEZ1bmN0aW9uICYmIGNvbmZpZy5mdW5jdGlvbnNbbWF0Y2hlZEZ1bmN0aW9uXSkge1xuICAgICAgICAgIGNvbnN0IGhhbmRsZXIgPSBhd2FpdCBsb2FkSGFuZGxlcihjb25maWcuZnVuY3Rpb25zW21hdGNoZWRGdW5jdGlvbl0uaGFuZGxlciwgb3V0cHV0RGlyKTtcblxuICAgICAgICAgIGlmKGhhbmRsZXIpIHtcbiAgICAgICAgICAgIC8vIFdyYXAgaGFuZGxlciB3aXRoIGNvbnNvbGUgbG9nIGNhcHR1cmVcbiAgICAgICAgICAgIGNvbnN0IHdyYXBwZWRIYW5kbGVyID0gY2FwdHVyZUNvbnNvbGVMb2dzKGhhbmRsZXIsIHF1aWV0KTtcbiAgICAgICAgICAgIGNvbnN0IGV2ZW50ID0ge1xuICAgICAgICAgICAgICByZXF1ZXN0Q29udGV4dDoge1xuICAgICAgICAgICAgICAgIHJvdXRlS2V5OiBkYXRhLmFjdGlvbiB8fCAnJGRlZmF1bHQnLFxuICAgICAgICAgICAgICAgIGNvbm5lY3Rpb25JZDogJ3Rlc3QtY29ubmVjdGlvbi1pZCcsXG4gICAgICAgICAgICAgICAgYXBpR2F0ZXdheToge1xuICAgICAgICAgICAgICAgICAgZW5kcG9pbnQ6IGB3czovL2xvY2FsaG9zdDoke3dzUG9ydH1gXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICBib2R5OiBkYXRhLmJvZHkgfHwgbnVsbFxuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgY29uc3QgY29udGV4dCA9IHtcbiAgICAgICAgICAgICAgZnVuY3Rpb25OYW1lOiBtYXRjaGVkRnVuY3Rpb24sXG4gICAgICAgICAgICAgIGZ1bmN0aW9uVmVyc2lvbjogJyRMQVRFU1QnLFxuICAgICAgICAgICAgICBpbnZva2VkRnVuY3Rpb25Bcm46IGBhcm46YXdzOmxhbWJkYTp1cy1lYXN0LTE6MTIzNDU2Nzg5MDEyOmZ1bmN0aW9uOiR7bWF0Y2hlZEZ1bmN0aW9ufWAsXG4gICAgICAgICAgICAgIG1lbW9yeUxpbWl0SW5NQjogJzEyOCcsXG4gICAgICAgICAgICAgIGF3c1JlcXVlc3RJZDogJ3Rlc3QtcmVxdWVzdC1pZCcsXG4gICAgICAgICAgICAgIGxvZ0dyb3VwTmFtZTogYC9hd3MvbGFtYmRhLyR7bWF0Y2hlZEZ1bmN0aW9ufWAsXG4gICAgICAgICAgICAgIGxvZ1N0cmVhbU5hbWU6ICd0ZXN0LWxvZy1zdHJlYW0nLFxuICAgICAgICAgICAgICBnZXRSZW1haW5pbmdUaW1lSW5NaWxsaXM6ICgpID0+IDMwMDAwXG4gICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB3cmFwcGVkSGFuZGxlcihldmVudCwgY29udGV4dCk7XG5cbiAgICAgICAgICAgIC8vIEhhbmRsZSBMYW1iZGEgcmVzcG9uc2UgZm9ybWF0IGZvciBXZWJTb2NrZXRcbiAgICAgICAgICAgIGlmKHJlc3VsdCAmJiB0eXBlb2YgcmVzdWx0ID09PSAnb2JqZWN0JyAmJiByZXN1bHQuc3RhdHVzQ29kZSkge1xuICAgICAgICAgICAgICAvLyBUaGlzIGlzIGEgTGFtYmRhIHJlc3BvbnNlIG9iamVjdCwgZXh0cmFjdCB0aGUgYm9keVxuICAgICAgICAgICAgICBjb25zdCBib2R5ID0gcmVzdWx0LmJvZHkgfHwgJyc7XG4gICAgICAgICAgICAgIHdzLnNlbmQoYm9keSk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAvLyBUaGlzIGlzIGEgZGlyZWN0IHJlc3BvbnNlLCBzdHJpbmdpZnkgaXRcbiAgICAgICAgICAgICAgd3Muc2VuZChKU09OLnN0cmluZ2lmeShyZXN1bHQpKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgd3Muc2VuZChKU09OLnN0cmluZ2lmeSh7ZXJyb3I6ICdIYW5kbGVyIG5vdCBmb3VuZCd9KSk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHdzLnNlbmQoSlNPTi5zdHJpbmdpZnkoe2Vycm9yOiAnV2ViU29ja2V0IGZ1bmN0aW9uIG5vdCBmb3VuZCd9KSk7XG4gICAgICAgIH1cbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGxvZyhgV2ViU29ja2V0IGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCwgJ2Vycm9yJywgZmFsc2UpO1xuICAgICAgICB3cy5zZW5kKEpTT04uc3RyaW5naWZ5KHtlcnJvcjogZXJyb3IubWVzc2FnZX0pKTtcbiAgICAgIH1cbiAgICB9KTtcblxuICAgIHdzLm9uKCdjbG9zZScsICgpID0+IHtcbiAgICAgIGxvZygnV2ViU29ja2V0IGNvbm5lY3Rpb24gY2xvc2VkJywgJ2luZm8nLCBmYWxzZSk7XG4gICAgfSk7XG4gIH0pO1xuXG4gIHJldHVybiB3c3M7XG59O1xuXG5jb25zdCBsb2FkRW52RmlsZSA9IChlbnZQYXRoOiBzdHJpbmcpOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0+IHtcbiAgY29uc3QgZW52VmFyczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHt9O1xuXG4gIGlmKCFleGlzdHNTeW5jKGVudlBhdGgpKSB7XG4gICAgcmV0dXJuIGVudlZhcnM7XG4gIH1cblxuICB0cnkge1xuICAgIGNvbnN0IGVudkNvbnRlbnQgPSByZWFkRmlsZVN5bmMoZW52UGF0aCwgJ3V0ZjgnKTtcbiAgICBjb25zdCBsaW5lcyA9IGVudkNvbnRlbnQuc3BsaXQoJ1xcbicpO1xuXG4gICAgZm9yKGNvbnN0IGxpbmUgb2YgbGluZXMpIHtcbiAgICAgIGNvbnN0IHRyaW1tZWRMaW5lID0gbGluZS50cmltKCk7XG5cbiAgICAgIC8vIFNraXAgZW1wdHkgbGluZXMgYW5kIGNvbW1lbnRzXG4gICAgICBpZighdHJpbW1lZExpbmUgfHwgdHJpbW1lZExpbmUuc3RhcnRzV2l0aCgnIycpKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICAvLyBQYXJzZSBLRVk9dmFsdWUgZm9ybWF0XG4gICAgICBjb25zdCBlcXVhbEluZGV4ID0gdHJpbW1lZExpbmUuaW5kZXhPZignPScpO1xuICAgICAgaWYoZXF1YWxJbmRleCA+IDApIHtcbiAgICAgICAgY29uc3Qga2V5ID0gdHJpbW1lZExpbmUuc3Vic3RyaW5nKDAsIGVxdWFsSW5kZXgpLnRyaW0oKTtcbiAgICAgICAgY29uc3QgdmFsdWUgPSB0cmltbWVkTGluZS5zdWJzdHJpbmcoZXF1YWxJbmRleCArIDEpLnRyaW0oKTtcblxuICAgICAgICAvLyBSZW1vdmUgcXVvdGVzIGlmIHByZXNlbnRcbiAgICAgICAgY29uc3QgY2xlYW5WYWx1ZSA9IHZhbHVlLnJlcGxhY2UoL15bXCInXXxbXCInXSQvZywgJycpO1xuXG4gICAgICAgIGlmKGtleSkge1xuICAgICAgICAgIGVudlZhcnNba2V5XSA9IGNsZWFuVmFsdWU7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgbG9nKGBXYXJuaW5nOiBDb3VsZCBub3QgbG9hZCAuZW52IGZpbGUgYXQgJHtlbnZQYXRofTogJHtlcnJvci5tZXNzYWdlfWAsICd3YXJuJywgZmFsc2UpO1xuICB9XG5cbiAgcmV0dXJuIGVudlZhcnM7XG59O1xuXG5leHBvcnQgY29uc3Qgc2VydmVybGVzcyA9IGFzeW5jIChjbWQ6IFNlcnZlcmxlc3NPcHRpb25zLCBjYWxsYmFjazogU2VydmVybGVzc0NhbGxiYWNrID0gKCkgPT4gKHt9KSk6IFByb21pc2U8bnVtYmVyPiA9PiB7XG4gIGNvbnN0IHtcbiAgICBjbGlOYW1lID0gJ0xleCcsXG4gICAgY29uZmlnLFxuICAgIGhvc3QgPSAnbG9jYWxob3N0JyxcbiAgICBodHRwUG9ydCA9IDMwMDAsXG4gICAgaHR0cHNQb3J0ID0gMzAwMSxcbiAgICB3c1BvcnQgPSAzMDAyLFxuICAgIHF1aWV0ID0gZmFsc2UsXG4gICAgcmVtb3ZlID0gZmFsc2UsXG4gICAgdXNlUHVibGljSXAsXG4gICAgdmFyaWFibGVzLFxuICAgIGRlYnVnID0gZmFsc2UsXG4gICAgcHJpbnRPdXRwdXQgPSBmYWxzZSxcbiAgICB0ZXN0ID0gZmFsc2VcbiAgfSA9IGNtZDtcblxuICBjb25zdCBzcGlubmVyID0gY3JlYXRlU3Bpbm5lcihxdWlldCk7XG5cbiAgbG9nKGAke2NsaU5hbWV9IHN0YXJ0aW5nIHNlcnZlcmxlc3MgZGV2ZWxvcG1lbnQgc2VydmVyLi4uYCwgJ2luZm8nLCBxdWlldCk7XG5cbiAgYXdhaXQgTGV4Q29uZmlnLnBhcnNlQ29uZmlnKGNtZCk7XG5cbiAgY29uc3Qge291dHB1dEZ1bGxQYXRofSA9IExleENvbmZpZy5jb25maWc7XG5cbiAgLy8gTG9hZCBlbnZpcm9ubWVudCB2YXJpYWJsZXMgZnJvbSAuZW52IGZpbGVzXG4gIGNvbnN0IGVudlBhdGhzID0gW1xuICAgIHBhdGhSZXNvbHZlKHByb2Nlc3MuY3dkKCksICcuZW52JyksXG4gICAgcGF0aFJlc29sdmUocHJvY2Vzcy5jd2QoKSwgJy5lbnYubG9jYWwnKSxcbiAgICBwYXRoUmVzb2x2ZShwcm9jZXNzLmN3ZCgpLCAnLmVudi5kZXZlbG9wbWVudCcpXG4gIF07XG5cbiAgbGV0IGVudlZhcnM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7fTtcblxuICAvLyBMb2FkIGZyb20gLmVudiBmaWxlcyBpbiBvcmRlciAobGF0ZXIgZmlsZXMgb3ZlcnJpZGUgZWFybGllciBvbmVzKVxuICBmb3IoY29uc3QgZW52UGF0aCBvZiBlbnZQYXRocykge1xuICAgIGNvbnN0IGZpbGVFbnZWYXJzID0gbG9hZEVudkZpbGUoZW52UGF0aCk7XG4gICAgaWYoT2JqZWN0LmtleXMoZmlsZUVudlZhcnMpLmxlbmd0aCA+IDApIHtcbiAgICAgIGxvZyhgTG9hZGVkIGVudmlyb25tZW50IHZhcmlhYmxlcyBmcm9tOiAke2VudlBhdGh9YCwgJ2luZm8nLCBxdWlldCk7XG4gICAgfVxuICAgIGVudlZhcnMgPSB7Li4uZW52VmFycywgLi4uZmlsZUVudlZhcnN9O1xuICB9XG5cbiAgLy8gU3RhcnQgd2l0aCBkZWZhdWx0IE5PREVfRU5WIGFuZCBsb2FkZWQgLmVudiB2YXJpYWJsZXNcbiAgbGV0IHZhcmlhYmxlc09iajogb2JqZWN0ID0ge05PREVfRU5WOiAnZGV2ZWxvcG1lbnQnLCAuLi5lbnZWYXJzfTtcblxuICAvLyBPdmVycmlkZSB3aXRoIGNvbW1hbmQgbGluZSB2YXJpYWJsZXMgaWYgcHJvdmlkZWRcbiAgaWYodmFyaWFibGVzKSB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGNsaVZhcnMgPSBKU09OLnBhcnNlKHZhcmlhYmxlcyk7XG4gICAgICB2YXJpYWJsZXNPYmogPSB7Li4udmFyaWFibGVzT2JqLCAuLi5jbGlWYXJzfTtcbiAgICB9IGNhdGNoIChfZXJyb3IpIHtcbiAgICAgIGxvZyhgXFxuJHtjbGlOYW1lfSBFcnJvcjogRW52aXJvbm1lbnQgdmFyaWFibGVzIG9wdGlvbiBpcyBub3QgYSB2YWxpZCBKU09OIG9iamVjdC5gLCAnZXJyb3InLCBxdWlldCk7XG4gICAgICBjYWxsYmFjaygxKTtcbiAgICAgIHJldHVybiAxO1xuICAgIH1cbiAgfVxuXG4gIHByb2Nlc3MuZW52ID0gey4uLnByb2Nlc3MuZW52LCAuLi52YXJpYWJsZXNPYmp9O1xuXG4gIC8vIElmIGluIHRlc3QgbW9kZSwgZXhpdCBlYXJseSBhZnRlciBsb2FkaW5nIGVudmlyb25tZW50IHZhcmlhYmxlc1xuICBpZih0ZXN0KSB7XG4gICAgbG9nKCdUZXN0IG1vZGU6IEVudmlyb25tZW50IHZhcmlhYmxlcyBsb2FkZWQsIGV4aXRpbmcnLCAnaW5mbycsIHF1aWV0KTtcbiAgICBjYWxsYmFjaygwKTtcbiAgICByZXR1cm4gMDtcbiAgfVxuXG4gIGlmKHJlbW92ZSkge1xuICAgIHNwaW5uZXIuc3RhcnQoJ0NsZWFuaW5nIG91dHB1dCBkaXJlY3RvcnkuLi4nKTtcbiAgICBhd2FpdCByZW1vdmVGaWxlcyhvdXRwdXRGdWxsUGF0aCB8fCAnJyk7XG4gICAgc3Bpbm5lci5zdWNjZWVkKCdTdWNjZXNzZnVsbHkgY2xlYW5lZCBvdXRwdXQgZGlyZWN0b3J5IScpO1xuICB9XG5cbiAgLy8gTG9hZCBzZXJ2ZXJsZXNzIGNvbmZpZ3VyYXRpb25cbiAgbGV0IHNlcnZlcmxlc3NDb25maWc6IFNlcnZlcmxlc3NDb25maWcgPSB7fTtcblxuICB0cnkge1xuICAgIGNvbnN0IGNvbmZpZ1BhdGggPSBjb25maWcgfHwgcGF0aFJlc29sdmUocHJvY2Vzcy5jd2QoKSwgJ2xleC5jb25maWcubWpzJyk7XG4gICAgbG9nKGBMb2FkaW5nIHNlcnZlcmxlc3MgY29uZmlnIGZyb206ICR7Y29uZmlnUGF0aH1gLCAnaW5mbycsIHF1aWV0KTtcblxuICAgIGlmKGV4aXN0c1N5bmMoY29uZmlnUGF0aCkpIHtcbiAgICAgIGNvbnN0IGNvbmZpZ01vZHVsZSA9IGF3YWl0IGltcG9ydChjb25maWdQYXRoKTtcbiAgICAgIHNlcnZlcmxlc3NDb25maWcgPSBjb25maWdNb2R1bGUuZGVmYXVsdD8uc2VydmVybGVzcyB8fCBjb25maWdNb2R1bGUuc2VydmVybGVzcyB8fCB7fTtcbiAgICAgIGxvZygnU2VydmVybGVzcyBjb25maWcgbG9hZGVkIHN1Y2Nlc3NmdWxseScsICdpbmZvJywgcXVpZXQpO1xuICAgICAgbG9nKGBMb2FkZWQgZnVuY3Rpb25zOiAke09iamVjdC5rZXlzKHNlcnZlcmxlc3NDb25maWcuZnVuY3Rpb25zIHx8IHt9KS5qb2luKCcsICcpfWAsICdpbmZvJywgcXVpZXQpO1xuICAgIH0gZWxzZSB7XG4gICAgICBsb2coYE5vIHNlcnZlcmxlc3MgY29uZmlnIGZvdW5kIGF0ICR7Y29uZmlnUGF0aH0sIHVzaW5nIGRlZmF1bHRzYCwgJ3dhcm4nLCBxdWlldCk7XG4gICAgfVxuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGxvZyhgRXJyb3IgbG9hZGluZyBzZXJ2ZXJsZXNzIGNvbmZpZzogJHtlcnJvci5tZXNzYWdlfWAsICdlcnJvcicsIHF1aWV0KTtcbiAgICAvLyBEb24ndCBleGl0LCBjb250aW51ZSB3aXRoIGVtcHR5IGNvbmZpZ1xuICB9XG5cbiAgLy8gTWVyZ2UgY29uZmlnIHdpdGggY29tbWFuZCBsaW5lIG9wdGlvbnNcbiAgY29uc3QgZmluYWxDb25maWc6IFNlcnZlcmxlc3NDb25maWcgPSB7XG4gICAgLi4uc2VydmVybGVzc0NvbmZpZyxcbiAgICBjdXN0b206IHtcbiAgICAgICdzZXJ2ZXJsZXNzLW9mZmxpbmUnOiB7XG4gICAgICAgIGh0dHBQb3J0OiBzZXJ2ZXJsZXNzQ29uZmlnLmN1c3RvbT8uWydzZXJ2ZXJsZXNzLW9mZmxpbmUnXT8uaHR0cFBvcnQgfHwgaHR0cFBvcnQsXG4gICAgICAgIGh0dHBzUG9ydDogc2VydmVybGVzc0NvbmZpZy5jdXN0b20/Llsnc2VydmVybGVzcy1vZmZsaW5lJ10/Lmh0dHBzUG9ydCB8fCBodHRwc1BvcnQsXG4gICAgICAgIHdzUG9ydDogc2VydmVybGVzc0NvbmZpZy5jdXN0b20/Llsnc2VydmVybGVzcy1vZmZsaW5lJ10/LndzUG9ydCB8fCB3c1BvcnQsXG4gICAgICAgIGhvc3Q6IHNlcnZlcmxlc3NDb25maWcuY3VzdG9tPy5bJ3NlcnZlcmxlc3Mtb2ZmbGluZSddPy5ob3N0IHx8IGhvc3QsXG4gICAgICAgIGNvcnM6IHNlcnZlcmxlc3NDb25maWcuY3VzdG9tPy5bJ3NlcnZlcmxlc3Mtb2ZmbGluZSddPy5jb3JzICE9PSBmYWxzZVxuICAgICAgfVxuICAgIH1cbiAgfTtcblxuICBjb25zdCBvdXRwdXREaXIgPSBvdXRwdXRGdWxsUGF0aCB8fCAnZGlzdCc7XG4gIGxvZyhgVXNpbmcgb3V0cHV0IGRpcmVjdG9yeTogJHtvdXRwdXREaXJ9YCwgJ2luZm8nLCBxdWlldCk7XG5cbiAgdHJ5IHtcbiAgICBzcGlubmVyLnN0YXJ0KCdTdGFydGluZyBzZXJ2ZXJsZXNzIGRldmVsb3BtZW50IHNlcnZlci4uLicpO1xuXG4gICAgY29uc3QgaHR0cFBvcnQgPSBmaW5hbENvbmZpZy5jdXN0b20hWydzZXJ2ZXJsZXNzLW9mZmxpbmUnXSEuaHR0cFBvcnQhO1xuICAgIGNvbnN0IHdzUG9ydCA9IGZpbmFsQ29uZmlnLmN1c3RvbSFbJ3NlcnZlcmxlc3Mtb2ZmbGluZSddIS53c1BvcnQhO1xuICAgIGNvbnN0IGhvc3QgPSBmaW5hbENvbmZpZy5jdXN0b20hWydzZXJ2ZXJsZXNzLW9mZmxpbmUnXSEuaG9zdCE7XG5cbiAgICBsb2coYENyZWF0aW5nIEhUVFAgc2VydmVyIG9uICR7aG9zdH06JHtodHRwUG9ydH1gLCAnaW5mbycsIHF1aWV0KTtcbiAgICBsb2coYENyZWF0aW5nIFdlYlNvY2tldCBzZXJ2ZXIgb24gcG9ydCAke3dzUG9ydH1gLCAnaW5mbycsIHF1aWV0KTtcblxuICAgIC8vIENyZWF0ZSBFeHByZXNzIHNlcnZlclxuICAgIGNvbnN0IGV4cHJlc3NBcHAgPSBhd2FpdCBjcmVhdGVFeHByZXNzU2VydmVyKFxuICAgICAgZmluYWxDb25maWcsXG4gICAgICBvdXRwdXREaXIsXG4gICAgICBodHRwUG9ydCxcbiAgICAgIGhvc3QsXG4gICAgICBxdWlldCxcbiAgICAgIGRlYnVnLFxuICAgICAgcHJpbnRPdXRwdXRcbiAgICApO1xuXG4gICAgLy8gQ3JlYXRlIFdlYlNvY2tldCBzZXJ2ZXJcbiAgICBjb25zdCB3c1NlcnZlciA9IGNyZWF0ZVdlYlNvY2tldFNlcnZlcihcbiAgICAgIGZpbmFsQ29uZmlnLFxuICAgICAgb3V0cHV0RGlyLFxuICAgICAgd3NQb3J0LFxuICAgICAgcXVpZXQsXG4gICAgICBkZWJ1ZyxcbiAgICAgIHByaW50T3V0cHV0XG4gICAgKTtcblxuICAgIC8vIEhhbmRsZSBzZXJ2ZXIgZXJyb3JzXG4gICAgd3NTZXJ2ZXIub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XG4gICAgICBsb2coYFdlYlNvY2tldCBzZXJ2ZXIgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gLCAnZXJyb3InLCBxdWlldCk7XG4gICAgICBzcGlubmVyLmZhaWwoJ0ZhaWxlZCB0byBzdGFydCBXZWJTb2NrZXQgc2VydmVyLicpO1xuICAgICAgY2FsbGJhY2soMSk7XG4gICAgICByZXR1cm47XG4gICAgfSk7XG5cbiAgICAvLyBTdGFydCBFeHByZXNzIHNlcnZlclxuICAgIGNvbnN0IHNlcnZlciA9IGV4cHJlc3NBcHAubGlzdGVuKGh0dHBQb3J0LCBob3N0LCAoKSA9PiB7XG4gICAgICBzcGlubmVyLnN1Y2NlZWQoJ1NlcnZlcmxlc3MgZGV2ZWxvcG1lbnQgc2VydmVyIHN0YXJ0ZWQuJyk7XG5cbiAgICAgIGRpc3BsYXlTZXJ2ZXJTdGF0dXMoXG4gICAgICAgIGh0dHBQb3J0LFxuICAgICAgICBmaW5hbENvbmZpZy5jdXN0b20hWydzZXJ2ZXJsZXNzLW9mZmxpbmUnXSEuaHR0cHNQb3J0ISxcbiAgICAgICAgd3NQb3J0LFxuICAgICAgICBob3N0LFxuICAgICAgICBxdWlldFxuICAgICAgKTtcblxuICAgICAgZmV0Y2hQdWJsaWNJcCh1c2VQdWJsaWNJcCkudGhlbigocHVibGljSXApID0+IHtcbiAgICAgICAgaWYocHVibGljSXApIHtcbiAgICAgICAgICBkaXNwbGF5U2VydmVyU3RhdHVzKFxuICAgICAgICAgICAgaHR0cFBvcnQsXG4gICAgICAgICAgICBmaW5hbENvbmZpZy5jdXN0b20hWydzZXJ2ZXJsZXNzLW9mZmxpbmUnXSEuaHR0cHNQb3J0ISxcbiAgICAgICAgICAgIHdzUG9ydCxcbiAgICAgICAgICAgIGhvc3QsXG4gICAgICAgICAgICBxdWlldCxcbiAgICAgICAgICAgIHB1YmxpY0lwXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfSk7XG5cbiAgICAvLyBIYW5kbGUgRXhwcmVzcyBzZXJ2ZXIgZXJyb3JzXG4gICAgc2VydmVyLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xuICAgICAgbG9nKGBFeHByZXNzIHNlcnZlciBlcnJvcjogJHtlcnJvci5tZXNzYWdlfWAsICdlcnJvcicsIHF1aWV0KTtcbiAgICAgIHNwaW5uZXIuZmFpbCgnRmFpbGVkIHRvIHN0YXJ0IEV4cHJlc3Mgc2VydmVyLicpO1xuICAgICAgY2FsbGJhY2soMSk7XG4gICAgICByZXR1cm47XG4gICAgfSk7XG5cbiAgICAvLyBIYW5kbGUgZ3JhY2VmdWwgc2h1dGRvd25cbiAgICBjb25zdCBzaHV0ZG93biA9ICgpID0+IHtcbiAgICAgIGxvZygnXFxuU2h1dHRpbmcgZG93biBzZXJ2ZXJsZXNzIGRldmVsb3BtZW50IHNlcnZlci4uLicsICdpbmZvJywgcXVpZXQpO1xuICAgICAgc2VydmVyLmNsb3NlKCk7XG4gICAgICB3c1NlcnZlci5jbG9zZSgpO1xuICAgICAgY2FsbGJhY2soMCk7XG4gICAgfTtcblxuICAgIHByb2Nlc3Mub24oJ1NJR0lOVCcsIHNodXRkb3duKTtcbiAgICBwcm9jZXNzLm9uKCdTSUdURVJNJywgc2h1dGRvd24pO1xuXG4gICAgLy8gS2VlcCB0aGUgcHJvY2VzcyBhbGl2ZVxuICAgIHByb2Nlc3Muc3RkaW4ucmVzdW1lKCk7XG5cbiAgICBsb2coJ1NlcnZlcmxlc3MgZGV2ZWxvcG1lbnQgc2VydmVyIGlzIHJ1bm5pbmcuIFByZXNzIEN0cmwrQyB0byBzdG9wLicsICdpbmZvJywgcXVpZXQpO1xuXG4gICAgLy8gRG9uJ3QgY2FsbCBjYWxsYmFjayBoZXJlLCBsZXQgdGhlIHByb2Nlc3Mgc3RheSBhbGl2ZVxuICAgIHJldHVybiAwO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGxvZyhgXFxuJHtjbGlOYW1lfSBFcnJvcjogJHtlcnJvci5tZXNzYWdlfWAsICdlcnJvcicsIHF1aWV0KTtcbiAgICBzcGlubmVyLmZhaWwoJ0ZhaWxlZCB0byBzdGFydCBzZXJ2ZXJsZXNzIGRldmVsb3BtZW50IHNlcnZlci4nKTtcbiAgICBjYWxsYmFjaygxKTtcbiAgICByZXR1cm4gMTtcbiAgfVxufTsiXSwKICAibWFwcGluZ3MiOiAiQUFJQSxPQUFPLFdBQVc7QUFDbEIsT0FBTyxXQUFXO0FBQ2xCLE9BQU8sYUFBYTtBQUNwQixTQUFRLGNBQWMsWUFBWSxXQUFXLHFCQUFvQjtBQUNqRSxTQUFRLG1CQUFtQixlQUFjO0FBQ3pDLFNBQVEsV0FBVyxhQUFhLFlBQVc7QUFDM0MsU0FBUSx1QkFBc0I7QUFFOUIsU0FBUSxpQkFBZ0I7QUFDeEIsU0FBUSxlQUFlLG1CQUFrQjtBQUN6QyxTQUFRLFdB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
|