@nlabs/lex 1.57.0 → 1.58.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/README.md +8 -8
- package/examples/serverless-example/README.md +2 -2
- package/lib/commands/{package-lambda/package-lambda.d.ts → serverless-deploy/serverless-deploy.d.ts} +3 -3
- package/lib/commands/serverless-deploy/serverless-deploy.js +110 -0
- package/lib/commands/{serverless/serverless.d.ts → serverless-dev/serverless-dev.d.ts} +3 -3
- package/lib/commands/serverless-dev/serverless-dev.js +952 -0
- package/lib/index.d.ts +2 -2
- package/lib/index.js +3 -3
- package/lib/lex.js +6 -6
- package/package.json +11 -6
- package/lib/commands/package-lambda/package-lambda.js +0 -110
- package/lib/commands/serverless/serverless.js +0 -952
|
@@ -0,0 +1,952 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2018-Present, Nitrogen Labs, Inc.
|
|
3
|
+
* Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
|
|
4
|
+
*/ import boxen from 'boxen';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { randomUUID } from 'crypto';
|
|
7
|
+
import express from 'express';
|
|
8
|
+
import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
9
|
+
import { homedir } from 'os';
|
|
10
|
+
import { resolve as pathResolve, join, isAbsolute } from 'path';
|
|
11
|
+
import { pathToFileURL } from 'url';
|
|
12
|
+
import { WebSocketServer } from 'ws';
|
|
13
|
+
import { LexConfig, getPackageDir } from '../../LexConfig.js';
|
|
14
|
+
import { createSpinner, removeFiles } from '../../utils/app.js';
|
|
15
|
+
import { log } from '../../utils/log.js';
|
|
16
|
+
const getCacheDir = ()=>{
|
|
17
|
+
const cacheDir = join(homedir(), '.lex-cache');
|
|
18
|
+
if (!existsSync(cacheDir)) {
|
|
19
|
+
mkdirSync(cacheDir, {
|
|
20
|
+
recursive: true
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return cacheDir;
|
|
24
|
+
};
|
|
25
|
+
const getCachePath = ()=>join(getCacheDir(), 'public-ip.json');
|
|
26
|
+
const readPublicIpCache = ()=>{
|
|
27
|
+
const cachePath = getCachePath();
|
|
28
|
+
if (!existsSync(cachePath)) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const cacheData = readFileSync(cachePath, 'utf8');
|
|
33
|
+
const cache = JSON.parse(cacheData);
|
|
34
|
+
// Check if cache is older than 1 week
|
|
35
|
+
const oneWeekMs = 7 * 24 * 60 * 60 * 1000;
|
|
36
|
+
if (Date.now() - cache.timestamp > oneWeekMs) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return cache;
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const writePublicIpCache = (ip)=>{
|
|
45
|
+
const cachePath = getCachePath();
|
|
46
|
+
const cache = {
|
|
47
|
+
ip,
|
|
48
|
+
timestamp: Date.now()
|
|
49
|
+
};
|
|
50
|
+
writeFileSync(cachePath, JSON.stringify(cache, null, 2));
|
|
51
|
+
};
|
|
52
|
+
const fetchPublicIp = (forceRefresh = false)=>new Promise((resolve)=>{
|
|
53
|
+
if (!forceRefresh) {
|
|
54
|
+
const cached = readPublicIpCache();
|
|
55
|
+
if (cached) {
|
|
56
|
+
resolve(cached.ip);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Use fetch instead of https
|
|
61
|
+
fetch('https://api.ipify.org').then((res)=>res.text()).then((data)=>{
|
|
62
|
+
const ip = data.trim();
|
|
63
|
+
if (ip) {
|
|
64
|
+
writePublicIpCache(ip);
|
|
65
|
+
}
|
|
66
|
+
resolve(ip);
|
|
67
|
+
}).catch(()=>resolve(undefined));
|
|
68
|
+
});
|
|
69
|
+
const resolvePublicIpForDisplay = async (forceRefresh = false)=>{
|
|
70
|
+
const timeoutMs = 1500;
|
|
71
|
+
const timeoutPromise = new Promise((resolve)=>{
|
|
72
|
+
setTimeout(()=>resolve(undefined), timeoutMs);
|
|
73
|
+
});
|
|
74
|
+
try {
|
|
75
|
+
return await Promise.race([
|
|
76
|
+
fetchPublicIp(forceRefresh),
|
|
77
|
+
timeoutPromise
|
|
78
|
+
]);
|
|
79
|
+
} catch {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const displayServerStatus = (httpPort, httpsPort, wsPort, host, quiet, publicIp)=>{
|
|
84
|
+
if (quiet) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const httpUrl = `http://${host}:${httpPort}`;
|
|
88
|
+
const httpsUrl = `https://${host}:${httpsPort}`;
|
|
89
|
+
const wsUrl = `ws://${host}:${wsPort}`;
|
|
90
|
+
const wssUrl = `wss://${host}:${wsPort}`;
|
|
91
|
+
let urlLines = `${chalk.green('HTTP:')} ${chalk.underline(httpUrl)}\n`;
|
|
92
|
+
urlLines += `${chalk.green('HTTPS:')} ${chalk.underline(httpsUrl)}\n`;
|
|
93
|
+
urlLines += `${chalk.green('WebSocket:')} ${chalk.underline(wsUrl)}\n`;
|
|
94
|
+
urlLines += `${chalk.green('WSS:')} ${chalk.underline(wssUrl)}\n`;
|
|
95
|
+
if (publicIp) {
|
|
96
|
+
urlLines += `\n${chalk.green('Public:')} ${chalk.underline(`http://${publicIp}:${httpPort}`)}\n`;
|
|
97
|
+
}
|
|
98
|
+
const statusBox = boxen(`${chalk.cyan.bold('🚀 Serverless Development Server Running')}\n\n${urlLines}\n` + `${chalk.yellow('Press Ctrl+C to stop the server')}`, {
|
|
99
|
+
backgroundColor: '#1a1a1a',
|
|
100
|
+
borderColor: 'cyan',
|
|
101
|
+
borderStyle: 'round',
|
|
102
|
+
margin: 1,
|
|
103
|
+
padding: 1
|
|
104
|
+
});
|
|
105
|
+
console.log(`\n${statusBox}\n`);
|
|
106
|
+
};
|
|
107
|
+
const loadHandler = async (handlerPath, outputDir)=>{
|
|
108
|
+
try {
|
|
109
|
+
console.log(`[Serverless] Parsing handler path: ${handlerPath}`);
|
|
110
|
+
// Parse AWS Lambda handler format: "path/to/file.exportName" or "file.exportName"
|
|
111
|
+
// Examples: "index.handler", "handlers/api.handler", "src/index.default"
|
|
112
|
+
const handlerParts = handlerPath.split('.');
|
|
113
|
+
console.log('[Serverless] Handler parts after split:', handlerParts);
|
|
114
|
+
let filePath;
|
|
115
|
+
let exportName = null;
|
|
116
|
+
if (handlerParts.length > 1) {
|
|
117
|
+
// AWS Lambda format: "file.exportName"
|
|
118
|
+
// Take the last part as export name, rest as file path
|
|
119
|
+
// Handle cases like "index.handler" -> file: "index", export: "handler"
|
|
120
|
+
// Or "handlers/api.handler" -> file: "handlers/api", export: "handler"
|
|
121
|
+
exportName = handlerParts[handlerParts.length - 1] || null;
|
|
122
|
+
filePath = handlerParts.slice(0, -1).join('.');
|
|
123
|
+
console.log(`[Serverless] Parsed AWS Lambda format - filePath: "${filePath}", exportName: "${exportName}"`);
|
|
124
|
+
} else {
|
|
125
|
+
// Simple format: just the file path
|
|
126
|
+
filePath = handlerPath;
|
|
127
|
+
console.log(`[Serverless] Simple format - filePath: "${filePath}"`);
|
|
128
|
+
}
|
|
129
|
+
// Ensure filePath doesn't have the export name in it
|
|
130
|
+
if (filePath.includes('.handler') || filePath.includes('.default')) {
|
|
131
|
+
console.error(`[Serverless] WARNING: filePath still contains export name! filePath: "${filePath}"`);
|
|
132
|
+
// Try to fix it - remove the last part if it looks like an export name
|
|
133
|
+
const pathParts = filePath.split('.');
|
|
134
|
+
if (pathParts.length > 1) {
|
|
135
|
+
const lastPart = pathParts[pathParts.length - 1];
|
|
136
|
+
if ([
|
|
137
|
+
'handler',
|
|
138
|
+
'default',
|
|
139
|
+
'index'
|
|
140
|
+
].includes(lastPart) && !exportName) {
|
|
141
|
+
exportName = lastPart;
|
|
142
|
+
filePath = pathParts.slice(0, -1).join('.');
|
|
143
|
+
console.log(`[Serverless] Fixed - filePath: "${filePath}", exportName: "${exportName}"`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Handle both relative paths and absolute paths
|
|
148
|
+
let fullPath;
|
|
149
|
+
if (isAbsolute(filePath)) {
|
|
150
|
+
fullPath = filePath;
|
|
151
|
+
} else {
|
|
152
|
+
fullPath = pathResolve(outputDir, filePath);
|
|
153
|
+
}
|
|
154
|
+
console.log(`[Serverless] Resolved fullPath (before extensions): ${fullPath}`);
|
|
155
|
+
// Try different extensions if file doesn't exist
|
|
156
|
+
if (!existsSync(fullPath)) {
|
|
157
|
+
const extensions = [
|
|
158
|
+
'.js',
|
|
159
|
+
'.mjs',
|
|
160
|
+
'.cjs'
|
|
161
|
+
];
|
|
162
|
+
const pathWithoutExt = fullPath.replace(/\.(js|mjs|cjs)$/, '');
|
|
163
|
+
console.log(`[Serverless] Trying extensions. Base path: ${pathWithoutExt}`);
|
|
164
|
+
for (const ext of extensions){
|
|
165
|
+
const candidatePath = pathWithoutExt + ext;
|
|
166
|
+
console.log(`[Serverless] Checking: ${candidatePath} (exists: ${existsSync(candidatePath)})`);
|
|
167
|
+
if (existsSync(candidatePath)) {
|
|
168
|
+
fullPath = candidatePath;
|
|
169
|
+
console.log(`[Serverless] Found file with extension: ${fullPath}`);
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
console.log(`[Serverless] File exists without trying extensions: ${fullPath}`);
|
|
175
|
+
}
|
|
176
|
+
console.log(`[Serverless] Final fullPath: ${fullPath}`);
|
|
177
|
+
console.log(`[Serverless] Export name: ${exportName || 'default/handler'}`);
|
|
178
|
+
console.log(`[Serverless] File exists: ${existsSync(fullPath)}`);
|
|
179
|
+
if (!existsSync(fullPath)) {
|
|
180
|
+
console.error(`[Serverless] Handler file not found: ${fullPath}`);
|
|
181
|
+
console.error(`[Serverless] Output directory: ${outputDir}`);
|
|
182
|
+
console.error(`[Serverless] Handler path from config: ${handlerPath}`);
|
|
183
|
+
throw new Error(`Handler file not found: ${fullPath}`);
|
|
184
|
+
}
|
|
185
|
+
// Dynamic import of the handler with better error handling
|
|
186
|
+
// Add .js extension if importing TypeScript compiled output
|
|
187
|
+
const importPath = fullPath.endsWith('.ts') ? fullPath.replace(/\.ts$/, '.js') : fullPath;
|
|
188
|
+
try {
|
|
189
|
+
// Convert to file:// URL for ES module imports (required for absolute paths)
|
|
190
|
+
// Use pathToFileURL to ensure proper file:// URL format
|
|
191
|
+
const importUrl = pathToFileURL(importPath).href;
|
|
192
|
+
console.log(`[Serverless] Importing handler from: ${importUrl}`);
|
|
193
|
+
console.log(`[Serverless] File path: ${importPath}`);
|
|
194
|
+
// Use import() with the file URL
|
|
195
|
+
// Note: If the handler file has import errors (like missing dependencies),
|
|
196
|
+
// those will surface here, but that's a handler code issue, not a loader issue
|
|
197
|
+
const handlerModule = await import(importUrl);
|
|
198
|
+
console.log(`[Serverless] Handler module loaded successfully. Exports: ${Object.keys(handlerModule).join(', ')}`);
|
|
199
|
+
// Get the handler based on export name or try defaults
|
|
200
|
+
let handler;
|
|
201
|
+
if (exportName) {
|
|
202
|
+
handler = handlerModule[exportName] || handlerModule.default?.[exportName] || handlerModule['module.exports']?.[exportName];
|
|
203
|
+
if (!handler) {
|
|
204
|
+
console.error(`[Serverless] Export "${exportName}" not found in module. Available exports: ${Object.keys(handlerModule).join(', ')}`);
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
} else {
|
|
208
|
+
// Try default, handler, or the module itself
|
|
209
|
+
handler = handlerModule.default || handlerModule.handler || handlerModule;
|
|
210
|
+
}
|
|
211
|
+
console.log(`[Serverless] Handler found: ${typeof handler}, isFunction: ${typeof handler === 'function'}`);
|
|
212
|
+
if (typeof handler !== 'function') {
|
|
213
|
+
console.error(`[Serverless] Handler is not a function. Type: ${typeof handler}, Value:`, handler);
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
return handler;
|
|
217
|
+
} catch (importError) {
|
|
218
|
+
console.error(`[Serverless] Import error for handler ${handlerPath}:`, importError.message);
|
|
219
|
+
console.error('[Serverless] Import error stack:', importError.stack);
|
|
220
|
+
// Check if this is a dependency resolution error (common with ES modules)
|
|
221
|
+
if (importError.message && importError.message.includes('Cannot find module')) {
|
|
222
|
+
console.error('[Serverless] This appears to be a dependency resolution error.');
|
|
223
|
+
console.error('[Serverless] The handler file exists, but one of its imports is failing.');
|
|
224
|
+
console.error('[Serverless] Check that all dependencies in the handler file are properly installed.');
|
|
225
|
+
console.error(`[Serverless] Handler file: ${importPath}`);
|
|
226
|
+
console.error('[Serverless] Make sure the handler and its dependencies are compiled correctly.');
|
|
227
|
+
}
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
} catch (error) {
|
|
231
|
+
console.error(`[Serverless] Error loading handler ${handlerPath}:`, error.message);
|
|
232
|
+
console.error('[Serverless] Error stack:', error.stack);
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
const captureConsoleLogs = (handler, quiet)=>{
|
|
237
|
+
if (quiet) {
|
|
238
|
+
return handler;
|
|
239
|
+
}
|
|
240
|
+
return async (event, context)=>{
|
|
241
|
+
// Capture console.log, console.error, etc.
|
|
242
|
+
const originalConsoleLog = console.log;
|
|
243
|
+
const originalConsoleError = console.error;
|
|
244
|
+
const originalConsoleWarn = console.warn;
|
|
245
|
+
const originalConsoleInfo = console.info;
|
|
246
|
+
const logs = [];
|
|
247
|
+
console.log = (...args)=>{
|
|
248
|
+
logs.push(`[LOG] ${args.join(' ')}`);
|
|
249
|
+
originalConsoleLog(...args);
|
|
250
|
+
};
|
|
251
|
+
console.error = (...args)=>{
|
|
252
|
+
logs.push(`[ERROR] ${args.join(' ')}`);
|
|
253
|
+
originalConsoleError(...args);
|
|
254
|
+
};
|
|
255
|
+
console.warn = (...args)=>{
|
|
256
|
+
logs.push(`[WARN] ${args.join(' ')}`);
|
|
257
|
+
originalConsoleWarn(...args);
|
|
258
|
+
};
|
|
259
|
+
console.info = (...args)=>{
|
|
260
|
+
logs.push(`[INFO] ${args.join(' ')}`);
|
|
261
|
+
originalConsoleInfo(...args);
|
|
262
|
+
};
|
|
263
|
+
try {
|
|
264
|
+
const result = await handler(event, context);
|
|
265
|
+
// Output captured logs
|
|
266
|
+
if (logs.length > 0) {
|
|
267
|
+
console.log(chalk.gray('--- Handler Console Output ---'));
|
|
268
|
+
logs.forEach((log)=>console.log(chalk.gray(log)));
|
|
269
|
+
console.log(chalk.gray('--- End Handler Console Output ---'));
|
|
270
|
+
}
|
|
271
|
+
return result;
|
|
272
|
+
} finally{
|
|
273
|
+
// Restore original console methods
|
|
274
|
+
console.log = originalConsoleLog;
|
|
275
|
+
console.error = originalConsoleError;
|
|
276
|
+
console.warn = originalConsoleWarn;
|
|
277
|
+
console.info = originalConsoleInfo;
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
};
|
|
281
|
+
const createExpressServer = async (config, outputDir, httpPort, host, connectionRegistry, quiet, debug)=>{
|
|
282
|
+
const app = express();
|
|
283
|
+
// Enable CORS
|
|
284
|
+
app.use((req, res, next)=>{
|
|
285
|
+
res.header('Access-Control-Allow-Origin', '*');
|
|
286
|
+
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
|
|
287
|
+
res.header('Access-Control-Allow-Headers', '*');
|
|
288
|
+
res.header('Access-Control-Allow-Credentials', 'true');
|
|
289
|
+
if (req.method === 'OPTIONS') {
|
|
290
|
+
res.sendStatus(200);
|
|
291
|
+
} else {
|
|
292
|
+
next();
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
// Parse raw websocket management API payloads
|
|
296
|
+
app.use(express.raw({
|
|
297
|
+
limit: '10mb',
|
|
298
|
+
type: 'application/octet-stream'
|
|
299
|
+
}));
|
|
300
|
+
// Preserve multipart uploads as raw Lambda-style bodies so upload handlers can parse boundaries.
|
|
301
|
+
app.use(express.raw({
|
|
302
|
+
limit: config.custom?.['serverless-offline']?.bodyLimit || '25mb',
|
|
303
|
+
type: (req)=>String(req.headers['content-type'] || '').includes('multipart/form-data')
|
|
304
|
+
}));
|
|
305
|
+
// Parse JSON bodies
|
|
306
|
+
app.use(express.json({
|
|
307
|
+
limit: config.custom?.['serverless-offline']?.bodyLimit || '25mb'
|
|
308
|
+
}));
|
|
309
|
+
app.post([
|
|
310
|
+
'/@connections/:connectionId',
|
|
311
|
+
'/:stage/@connections/:connectionId'
|
|
312
|
+
], (req, res)=>{
|
|
313
|
+
const connectionId = String(req.params.connectionId || '');
|
|
314
|
+
const ws = connectionRegistry.get(connectionId);
|
|
315
|
+
if (!ws) {
|
|
316
|
+
res.status(410).json({
|
|
317
|
+
message: 'Gone'
|
|
318
|
+
});
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
let body = '';
|
|
322
|
+
if (Buffer.isBuffer(req.body)) {
|
|
323
|
+
body = req.body.toString('utf8');
|
|
324
|
+
} else if (typeof req.body === 'string') {
|
|
325
|
+
body = req.body;
|
|
326
|
+
} else if (req.body !== undefined && req.body !== null && req.body !== '') {
|
|
327
|
+
body = JSON.stringify(req.body);
|
|
328
|
+
}
|
|
329
|
+
if (body) {
|
|
330
|
+
ws.send(body);
|
|
331
|
+
}
|
|
332
|
+
res.status(200).json({
|
|
333
|
+
ok: true
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
// Load GraphQL handler
|
|
337
|
+
const loadGraphQLSchema = async ()=>{
|
|
338
|
+
try {
|
|
339
|
+
// Try to find a GraphQL handler
|
|
340
|
+
let graphqlHandler = null;
|
|
341
|
+
if (config.functions) {
|
|
342
|
+
for (const [functionName, functionConfig] of Object.entries(config.functions)){
|
|
343
|
+
if (functionConfig.events) {
|
|
344
|
+
for (const event of functionConfig.events){
|
|
345
|
+
if (event.http && event.http.path) {
|
|
346
|
+
// Look for GraphQL endpoints
|
|
347
|
+
if (event.http.path === '/public' || event.http.path === '/graphql') {
|
|
348
|
+
graphqlHandler = await loadHandler(functionConfig.handler, outputDir);
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (graphqlHandler) {
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
if (graphqlHandler) {
|
|
360
|
+
log('Found GraphQL handler', 'info', quiet);
|
|
361
|
+
return graphqlHandler;
|
|
362
|
+
}
|
|
363
|
+
return null;
|
|
364
|
+
} catch (error) {
|
|
365
|
+
log(`Error loading GraphQL handler: ${error.message}`, 'error', quiet);
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
// Set up GraphQL handler for GraphQL requests
|
|
370
|
+
try {
|
|
371
|
+
const graphqlHandler = await loadGraphQLSchema();
|
|
372
|
+
if (graphqlHandler) {
|
|
373
|
+
// Find the GraphQL path from the serverless config
|
|
374
|
+
let graphqlPath = '/graphql'; // default fallback
|
|
375
|
+
if (config.functions) {
|
|
376
|
+
for (const [_functionName, functionConfig] of Object.entries(config.functions)){
|
|
377
|
+
if (functionConfig.events) {
|
|
378
|
+
for (const event of functionConfig.events){
|
|
379
|
+
if (event?.http?.path) {
|
|
380
|
+
graphqlPath = event.http.path;
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (graphqlPath !== '/graphql') {
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
// Set up GraphQL endpoint with enhanced console.log capture
|
|
391
|
+
app.use(graphqlPath, async (req, res)=>{
|
|
392
|
+
// GraphQL Debug Logging
|
|
393
|
+
if (debug && req.body && req.body.query) {
|
|
394
|
+
log('🔍 GraphQL Debug Mode: Analyzing request...', 'info', false);
|
|
395
|
+
log(`📝 GraphQL Query: ${req.body.query}`, 'info', false);
|
|
396
|
+
if (req.body.variables) {
|
|
397
|
+
log(`📊 GraphQL Variables: ${JSON.stringify(req.body.variables, null, 2)}`, 'info', false);
|
|
398
|
+
}
|
|
399
|
+
if (req.body.operationName) {
|
|
400
|
+
log(`🏷️ GraphQL Operation: ${req.body.operationName}`, 'info', false);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
// Enhanced console.log capture
|
|
404
|
+
const originalConsoleLog = console.log;
|
|
405
|
+
const logs = [];
|
|
406
|
+
console.log = (...args)=>{
|
|
407
|
+
const logMessage = args.map((arg)=>typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)).join(' ');
|
|
408
|
+
logs.push(logMessage);
|
|
409
|
+
originalConsoleLog(`[GraphQL] ${logMessage}`);
|
|
410
|
+
};
|
|
411
|
+
// Create context for the handler
|
|
412
|
+
const context = {
|
|
413
|
+
awsRequestId: 'test-request-id',
|
|
414
|
+
functionName: 'graphql',
|
|
415
|
+
functionVersion: '$LATEST',
|
|
416
|
+
getRemainingTimeInMillis: ()=>30000,
|
|
417
|
+
invokedFunctionArn: 'arn:aws:lambda:us-east-1:123456789012:function:graphql',
|
|
418
|
+
logGroupName: '/aws/lambda/graphql',
|
|
419
|
+
logStreamName: 'test-log-stream',
|
|
420
|
+
req,
|
|
421
|
+
res
|
|
422
|
+
};
|
|
423
|
+
// Wrap handler with console log capture
|
|
424
|
+
const wrappedHandler = captureConsoleLogs(graphqlHandler, quiet);
|
|
425
|
+
try {
|
|
426
|
+
// Call the handler with GraphQL parameters
|
|
427
|
+
const result = await wrappedHandler({
|
|
428
|
+
body: JSON.stringify(req.body),
|
|
429
|
+
headers: req.headers,
|
|
430
|
+
httpMethod: 'POST',
|
|
431
|
+
path: graphqlPath,
|
|
432
|
+
queryStringParameters: {}
|
|
433
|
+
}, context);
|
|
434
|
+
// Restore console.log
|
|
435
|
+
console.log = originalConsoleLog;
|
|
436
|
+
// Handle the result
|
|
437
|
+
if (result && typeof result === 'object' && result.statusCode) {
|
|
438
|
+
res.status(result.statusCode);
|
|
439
|
+
if (result.headers) {
|
|
440
|
+
Object.entries(result.headers).forEach(([key, value])=>{
|
|
441
|
+
res.setHeader(key, String(value));
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
res.send(result.body);
|
|
445
|
+
} else {
|
|
446
|
+
res.json(result);
|
|
447
|
+
}
|
|
448
|
+
} catch (error) {
|
|
449
|
+
// Restore console.log
|
|
450
|
+
console.log = originalConsoleLog;
|
|
451
|
+
log(`GraphQL handler error: ${error.message}`, 'error', false);
|
|
452
|
+
res.status(500).json({
|
|
453
|
+
error: error.message
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
log(`GraphQL endpoint available at http://${host}:${httpPort}${graphqlPath}`, 'info', quiet);
|
|
458
|
+
}
|
|
459
|
+
} catch (error) {
|
|
460
|
+
log(`Error setting up GraphQL: ${error.message}`, 'error', quiet);
|
|
461
|
+
}
|
|
462
|
+
// Fallback for non-GraphQL routes - handle all remaining routes
|
|
463
|
+
app.use('/', async (req, res)=>{
|
|
464
|
+
try {
|
|
465
|
+
const url = req.url || '/';
|
|
466
|
+
const method = req.method || 'GET';
|
|
467
|
+
const pathname = req.path || url.split('?')[0]; // Extract pathname without query string
|
|
468
|
+
// Always log requests (not affected by quiet flag for debugging)
|
|
469
|
+
console.log(`[Serverless] ${method} ${url} (pathname: ${pathname})`);
|
|
470
|
+
// Find matching function
|
|
471
|
+
let matchedFunction = null;
|
|
472
|
+
const functions = config.functions || {};
|
|
473
|
+
if (config.functions) {
|
|
474
|
+
const functionNames = Object.keys(functions);
|
|
475
|
+
console.log(`[Serverless] Available functions: ${functionNames.join(', ')}`);
|
|
476
|
+
console.log('[Serverless] Config functions:', JSON.stringify(functions, null, 2));
|
|
477
|
+
for (const [functionName, functionConfig] of Object.entries(functions)){
|
|
478
|
+
if (functionConfig.events) {
|
|
479
|
+
for (const event of functionConfig.events){
|
|
480
|
+
if (event.http) {
|
|
481
|
+
const eventPath = event.http.path || '/';
|
|
482
|
+
const eventMethod = (event.http.method || 'GET').toUpperCase();
|
|
483
|
+
const requestMethod = method.toUpperCase();
|
|
484
|
+
console.log(`[Serverless] Checking function ${functionName}: path="${eventPath}", method="${eventMethod}" against pathname="${pathname}", method="${requestMethod}"`);
|
|
485
|
+
// Improved path matching - compare pathname without query string
|
|
486
|
+
// Normalize paths (remove trailing slashes for comparison)
|
|
487
|
+
const normalizedEventPath = eventPath.replace(/\/$/, '') || '/';
|
|
488
|
+
const normalizedPathname = pathname.replace(/\/$/, '') || '/';
|
|
489
|
+
if (normalizedEventPath === normalizedPathname && eventMethod === requestMethod) {
|
|
490
|
+
matchedFunction = functionName;
|
|
491
|
+
console.log(`[Serverless] ✓ Matched function: ${matchedFunction}`);
|
|
492
|
+
break;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
if (matchedFunction) {
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
} else {
|
|
502
|
+
console.log('[Serverless] No functions found in config');
|
|
503
|
+
}
|
|
504
|
+
if (matchedFunction && functions[matchedFunction]) {
|
|
505
|
+
// Resolve handler path relative to output directory
|
|
506
|
+
const handlerPath = functions[matchedFunction].handler;
|
|
507
|
+
console.log(`[Serverless] Loading handler: ${handlerPath} from outputDir: ${outputDir}`);
|
|
508
|
+
const handler = await loadHandler(handlerPath, outputDir);
|
|
509
|
+
if (handler) {
|
|
510
|
+
console.log(`[Serverless] Handler loaded successfully, type: ${typeof handler}`);
|
|
511
|
+
const wrappedHandler = captureConsoleLogs(handler, quiet);
|
|
512
|
+
const isRawBody = Buffer.isBuffer(req.body);
|
|
513
|
+
const event = {
|
|
514
|
+
body: isRawBody ? req.body.toString('base64') : req.body,
|
|
515
|
+
headers: req.headers,
|
|
516
|
+
httpMethod: method,
|
|
517
|
+
isBase64Encoded: isRawBody,
|
|
518
|
+
path: url,
|
|
519
|
+
queryStringParameters: req.query
|
|
520
|
+
};
|
|
521
|
+
const context = {
|
|
522
|
+
awsRequestId: 'test-request-id',
|
|
523
|
+
functionName: matchedFunction,
|
|
524
|
+
functionVersion: '$LATEST',
|
|
525
|
+
getRemainingTimeInMillis: ()=>30000,
|
|
526
|
+
invokedFunctionArn: `arn:aws:lambda:us-east-1:123456789012:function:${matchedFunction}`,
|
|
527
|
+
logGroupName: `/aws/lambda/${matchedFunction}`,
|
|
528
|
+
logStreamName: 'test-log-stream',
|
|
529
|
+
memoryLimitInMB: '128'
|
|
530
|
+
};
|
|
531
|
+
try {
|
|
532
|
+
console.log('[Serverless] Calling handler with event:', JSON.stringify(event, null, 2));
|
|
533
|
+
const result = await wrappedHandler(event, context);
|
|
534
|
+
console.log('[Serverless] Handler returned:', JSON.stringify(result, null, 2));
|
|
535
|
+
if (result && typeof result === 'object' && result.statusCode) {
|
|
536
|
+
res.status(result.statusCode);
|
|
537
|
+
if (result.headers) {
|
|
538
|
+
Object.entries(result.headers).forEach(([key, value])=>{
|
|
539
|
+
res.setHeader(key, String(value));
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
res.send(result.body);
|
|
543
|
+
} else {
|
|
544
|
+
res.json(result);
|
|
545
|
+
}
|
|
546
|
+
} catch (error) {
|
|
547
|
+
console.error('[Serverless] Handler error:', error.message);
|
|
548
|
+
console.error('[Serverless] Handler error stack:', error.stack);
|
|
549
|
+
log(`Handler error: ${error.message}`, 'error', false);
|
|
550
|
+
res.status(500).json({
|
|
551
|
+
error: error.message
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
} else {
|
|
555
|
+
console.error(`[Serverless] Handler not found for function: ${matchedFunction}`);
|
|
556
|
+
console.error(`[Serverless] Handler path: ${handlerPath}, Output dir: ${outputDir}`);
|
|
557
|
+
res.status(404).json({
|
|
558
|
+
error: 'Handler not found'
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
} else {
|
|
562
|
+
console.error(`[Serverless] Function not found for pathname: ${pathname}, method: ${method}`);
|
|
563
|
+
console.error(`[Serverless] Available functions: ${config.functions ? Object.keys(functions).join(', ') : 'none'}`);
|
|
564
|
+
res.status(404).json({
|
|
565
|
+
error: 'Function not found'
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
} catch (error) {
|
|
569
|
+
log(`Route handling error: ${error.message}`, 'error', false);
|
|
570
|
+
res.status(500).json({
|
|
571
|
+
error: error.message
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
return app;
|
|
576
|
+
};
|
|
577
|
+
const createWebSocketServer = (config, outputDir, wsPort, connectionRegistry, quiet, debug)=>{
|
|
578
|
+
const wss = new WebSocketServer({
|
|
579
|
+
port: wsPort
|
|
580
|
+
});
|
|
581
|
+
const getWebSocketFunctionByRoute = (routeKey)=>{
|
|
582
|
+
const functions = config.functions || {};
|
|
583
|
+
let defaultFunction = null;
|
|
584
|
+
for (const [functionName, functionConfig] of Object.entries(functions)){
|
|
585
|
+
if (functionConfig.events) {
|
|
586
|
+
for (const event of functionConfig.events){
|
|
587
|
+
if (event.websocket) {
|
|
588
|
+
const route = event.websocket.route || '$connect';
|
|
589
|
+
if (route === routeKey) {
|
|
590
|
+
return functionName;
|
|
591
|
+
}
|
|
592
|
+
if (route === '$default' && !defaultFunction) {
|
|
593
|
+
defaultFunction = functionName;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
return defaultFunction;
|
|
600
|
+
};
|
|
601
|
+
wss.on('connection', async (ws, req)=>{
|
|
602
|
+
log(`WebSocket connection established: ${req.url}`, 'info', false);
|
|
603
|
+
const connectionId = randomUUID().replace(/-/g, '').slice(0, 32);
|
|
604
|
+
connectionRegistry.set(connectionId, ws);
|
|
605
|
+
const requestUrl = new URL(req.url || '/', `ws://localhost:${wsPort}`);
|
|
606
|
+
const queryStringParameters = Object.fromEntries(requestUrl.searchParams.entries());
|
|
607
|
+
const multiValueQueryStringParameters = Object.fromEntries(Array.from(new Set(Array.from(requestUrl.searchParams.keys()))).map((key)=>[
|
|
608
|
+
key,
|
|
609
|
+
requestUrl.searchParams.getAll(key)
|
|
610
|
+
]));
|
|
611
|
+
const connectFunction = getWebSocketFunctionByRoute('$connect');
|
|
612
|
+
if (connectFunction && config.functions?.[connectFunction]) {
|
|
613
|
+
const connectHandler = await loadHandler(config.functions[connectFunction].handler, outputDir);
|
|
614
|
+
if (connectHandler) {
|
|
615
|
+
const wrappedConnectHandler = captureConsoleLogs(connectHandler, quiet);
|
|
616
|
+
await wrappedConnectHandler({
|
|
617
|
+
body: null,
|
|
618
|
+
multiValueQueryStringParameters,
|
|
619
|
+
queryStringParameters,
|
|
620
|
+
requestContext: {
|
|
621
|
+
apiGateway: {
|
|
622
|
+
endpoint: `ws://localhost:${wsPort}`
|
|
623
|
+
},
|
|
624
|
+
connectionId,
|
|
625
|
+
eventType: 'CONNECT',
|
|
626
|
+
routeKey: '$connect'
|
|
627
|
+
}
|
|
628
|
+
}, {
|
|
629
|
+
awsRequestId: 'test-request-id',
|
|
630
|
+
functionName: connectFunction,
|
|
631
|
+
functionVersion: '$LATEST',
|
|
632
|
+
getRemainingTimeInMillis: ()=>30000,
|
|
633
|
+
invokedFunctionArn: `arn:aws:lambda:us-east-1:123456789012:function:${connectFunction}`,
|
|
634
|
+
logGroupName: `/aws/lambda/${connectFunction}`,
|
|
635
|
+
logStreamName: 'test-log-stream',
|
|
636
|
+
memoryLimitInMB: '128'
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
ws.on('message', async (message)=>{
|
|
641
|
+
try {
|
|
642
|
+
const data = JSON.parse(message.toString());
|
|
643
|
+
// Find matching WebSocket function
|
|
644
|
+
const functions = config.functions || {};
|
|
645
|
+
const matchedFunction = getWebSocketFunctionByRoute(String(data.action || '$default'));
|
|
646
|
+
if (matchedFunction && functions[matchedFunction]) {
|
|
647
|
+
const handler = await loadHandler(functions[matchedFunction].handler, outputDir);
|
|
648
|
+
if (handler) {
|
|
649
|
+
// Wrap handler with console log capture
|
|
650
|
+
const wrappedHandler = captureConsoleLogs(handler, quiet);
|
|
651
|
+
const event = {
|
|
652
|
+
body: message.toString(),
|
|
653
|
+
requestContext: {
|
|
654
|
+
apiGateway: {
|
|
655
|
+
endpoint: `ws://localhost:${wsPort}`
|
|
656
|
+
},
|
|
657
|
+
connectionId,
|
|
658
|
+
routeKey: data.action || '$default'
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
const context = {
|
|
662
|
+
awsRequestId: 'test-request-id',
|
|
663
|
+
functionName: matchedFunction,
|
|
664
|
+
functionVersion: '$LATEST',
|
|
665
|
+
getRemainingTimeInMillis: ()=>30000,
|
|
666
|
+
invokedFunctionArn: `arn:aws:lambda:us-east-1:123456789012:function:${matchedFunction}`,
|
|
667
|
+
logGroupName: `/aws/lambda/${matchedFunction}`,
|
|
668
|
+
logStreamName: 'test-log-stream',
|
|
669
|
+
memoryLimitInMB: '128'
|
|
670
|
+
};
|
|
671
|
+
const result = await wrappedHandler(event, context);
|
|
672
|
+
// Handle Lambda response format for WebSocket
|
|
673
|
+
if (result && typeof result === 'object' && result.statusCode) {
|
|
674
|
+
// Only send a frame when the handler returned a real body.
|
|
675
|
+
const body = typeof result.body === 'string' ? result.body : '';
|
|
676
|
+
if (body) {
|
|
677
|
+
ws.send(body);
|
|
678
|
+
}
|
|
679
|
+
} else if (result !== undefined) {
|
|
680
|
+
// This is a direct response, stringify it.
|
|
681
|
+
ws.send(JSON.stringify(result));
|
|
682
|
+
}
|
|
683
|
+
} else {
|
|
684
|
+
ws.send(JSON.stringify({
|
|
685
|
+
error: 'Handler not found'
|
|
686
|
+
}));
|
|
687
|
+
}
|
|
688
|
+
} else {
|
|
689
|
+
ws.send(JSON.stringify({
|
|
690
|
+
error: 'WebSocket function not found'
|
|
691
|
+
}));
|
|
692
|
+
}
|
|
693
|
+
} catch (error) {
|
|
694
|
+
log(`WebSocket error: ${error.message}`, 'error', false);
|
|
695
|
+
ws.send(JSON.stringify({
|
|
696
|
+
error: error.message
|
|
697
|
+
}));
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
ws.on('close', ()=>{
|
|
701
|
+
connectionRegistry.delete(connectionId);
|
|
702
|
+
const disconnectFunction = getWebSocketFunctionByRoute('$disconnect');
|
|
703
|
+
if (disconnectFunction && config.functions?.[disconnectFunction]) {
|
|
704
|
+
void loadHandler(config.functions[disconnectFunction].handler, outputDir).then((disconnectHandler)=>{
|
|
705
|
+
if (!disconnectHandler) {
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
const wrappedDisconnectHandler = captureConsoleLogs(disconnectHandler, quiet);
|
|
709
|
+
return wrappedDisconnectHandler({
|
|
710
|
+
body: null,
|
|
711
|
+
requestContext: {
|
|
712
|
+
apiGateway: {
|
|
713
|
+
endpoint: `ws://localhost:${wsPort}`
|
|
714
|
+
},
|
|
715
|
+
connectionId,
|
|
716
|
+
eventType: 'DISCONNECT',
|
|
717
|
+
routeKey: '$disconnect'
|
|
718
|
+
}
|
|
719
|
+
}, {
|
|
720
|
+
awsRequestId: 'test-request-id',
|
|
721
|
+
functionName: disconnectFunction,
|
|
722
|
+
functionVersion: '$LATEST',
|
|
723
|
+
getRemainingTimeInMillis: ()=>30000,
|
|
724
|
+
invokedFunctionArn: `arn:aws:lambda:us-east-1:123456789012:function:${disconnectFunction}`,
|
|
725
|
+
logGroupName: `/aws/lambda/${disconnectFunction}`,
|
|
726
|
+
logStreamName: 'test-log-stream',
|
|
727
|
+
memoryLimitInMB: '128'
|
|
728
|
+
});
|
|
729
|
+
}).catch((error)=>{
|
|
730
|
+
log(`WebSocket disconnect handler error: ${error.message}`, 'error', false);
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
log('WebSocket connection closed', 'info', false);
|
|
734
|
+
});
|
|
735
|
+
});
|
|
736
|
+
return wss;
|
|
737
|
+
};
|
|
738
|
+
const loadEnvFile = (envPath)=>{
|
|
739
|
+
const envVars = {};
|
|
740
|
+
if (!existsSync(envPath)) {
|
|
741
|
+
return envVars;
|
|
742
|
+
}
|
|
743
|
+
try {
|
|
744
|
+
const envContent = readFileSync(envPath, 'utf8');
|
|
745
|
+
const lines = envContent.split('\n');
|
|
746
|
+
for (const line of lines){
|
|
747
|
+
const trimmedLine = line.trim();
|
|
748
|
+
// Skip empty lines and comments
|
|
749
|
+
if (!trimmedLine || trimmedLine.startsWith('#')) {
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
752
|
+
// Parse KEY=value format
|
|
753
|
+
const equalIndex = trimmedLine.indexOf('=');
|
|
754
|
+
if (equalIndex > 0) {
|
|
755
|
+
const key = trimmedLine.substring(0, equalIndex).trim();
|
|
756
|
+
const value = trimmedLine.substring(equalIndex + 1).trim();
|
|
757
|
+
// Remove quotes if present
|
|
758
|
+
const cleanValue = value.replace(/^["']|["']$/g, '');
|
|
759
|
+
if (key) {
|
|
760
|
+
envVars[key] = cleanValue;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
} catch (error) {
|
|
765
|
+
log(`Warning: Could not load .env file at ${envPath}: ${error.message}`, 'warn', false);
|
|
766
|
+
}
|
|
767
|
+
return envVars;
|
|
768
|
+
};
|
|
769
|
+
export const serverlessDev = async (cmd, callback = ()=>({}))=>{
|
|
770
|
+
const { cliName = 'Lex', config, debug = false, host: cliHost, httpPort: cliHttpPort, httpsPort: cliHttpsPort, quiet = false, remove = false, test = false, usePublicIp, variables, wsPort: cliWsPort } = cmd;
|
|
771
|
+
const spinner = createSpinner(quiet);
|
|
772
|
+
log(`${cliName} starting serverless development server...`, 'info', quiet);
|
|
773
|
+
await LexConfig.parseConfig(cmd);
|
|
774
|
+
const { outputFullPath } = LexConfig.config;
|
|
775
|
+
// Load environment variables from .env files
|
|
776
|
+
const envPaths = [
|
|
777
|
+
pathResolve(process.cwd(), '.env'),
|
|
778
|
+
pathResolve(process.cwd(), '.env.local'),
|
|
779
|
+
pathResolve(process.cwd(), '.env.development')
|
|
780
|
+
];
|
|
781
|
+
let envVars = {};
|
|
782
|
+
// Load from .env files in order (later files override earlier ones)
|
|
783
|
+
for (const envPath of envPaths){
|
|
784
|
+
const fileEnvVars = loadEnvFile(envPath);
|
|
785
|
+
if (Object.keys(fileEnvVars).length > 0) {
|
|
786
|
+
log(`Loaded environment variables from: ${envPath}`, 'info', quiet);
|
|
787
|
+
}
|
|
788
|
+
envVars = {
|
|
789
|
+
...envVars,
|
|
790
|
+
...fileEnvVars
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
// Start with default NODE_ENV and loaded .env variables
|
|
794
|
+
let variablesObj = {
|
|
795
|
+
NODE_ENV: 'development',
|
|
796
|
+
...envVars
|
|
797
|
+
};
|
|
798
|
+
// Override with command line variables if provided
|
|
799
|
+
if (variables) {
|
|
800
|
+
try {
|
|
801
|
+
const cliVars = JSON.parse(variables);
|
|
802
|
+
variablesObj = {
|
|
803
|
+
...variablesObj,
|
|
804
|
+
...cliVars
|
|
805
|
+
};
|
|
806
|
+
} catch (_error) {
|
|
807
|
+
log(`\n${cliName} Error: Environment variables option is not a valid JSON object.`, 'error', quiet);
|
|
808
|
+
callback(1);
|
|
809
|
+
return 1;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
process.env = {
|
|
813
|
+
...process.env,
|
|
814
|
+
...variablesObj
|
|
815
|
+
};
|
|
816
|
+
// If in test mode, exit early after loading environment variables
|
|
817
|
+
if (test) {
|
|
818
|
+
log('Test mode: Environment variables loaded, exiting', 'info', quiet);
|
|
819
|
+
callback(0);
|
|
820
|
+
return 0;
|
|
821
|
+
}
|
|
822
|
+
if (remove) {
|
|
823
|
+
spinner.start('Cleaning output directory...');
|
|
824
|
+
await removeFiles(outputFullPath || '');
|
|
825
|
+
spinner.succeed('Successfully cleaned output directory!');
|
|
826
|
+
}
|
|
827
|
+
// Load serverless configuration
|
|
828
|
+
let serverlessConfig = {};
|
|
829
|
+
try {
|
|
830
|
+
// Use getPackageDir to handle npm workspaces correctly
|
|
831
|
+
const packageDir = getPackageDir();
|
|
832
|
+
// Try multiple config file formats
|
|
833
|
+
const configFormats = config ? [
|
|
834
|
+
config
|
|
835
|
+
] : [
|
|
836
|
+
pathResolve(packageDir, 'lex.config.mjs'),
|
|
837
|
+
pathResolve(packageDir, 'lex.config.js'),
|
|
838
|
+
pathResolve(packageDir, 'lex.config.cjs'),
|
|
839
|
+
pathResolve(packageDir, 'lex.config.ts')
|
|
840
|
+
];
|
|
841
|
+
let configPath = null;
|
|
842
|
+
for (const candidatePath of configFormats){
|
|
843
|
+
if (existsSync(candidatePath)) {
|
|
844
|
+
configPath = candidatePath;
|
|
845
|
+
break;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
if (configPath) {
|
|
849
|
+
log(`Loading serverless config from: ${configPath}`, 'info', quiet);
|
|
850
|
+
const configModule = await import(configPath);
|
|
851
|
+
serverlessConfig = configModule.default?.serverless || configModule.serverless || {};
|
|
852
|
+
log('Serverless config loaded successfully', 'info', quiet);
|
|
853
|
+
const functionNames = Object.keys(serverlessConfig.functions || {});
|
|
854
|
+
log(`Loaded functions: ${functionNames.length > 0 ? functionNames.join(', ') : 'none'}`, 'info', quiet);
|
|
855
|
+
// Debug: Print full config if debug mode
|
|
856
|
+
if (debug) {
|
|
857
|
+
log(`Full serverless config: ${JSON.stringify(serverlessConfig, null, 2)}`, 'info', false);
|
|
858
|
+
}
|
|
859
|
+
} else {
|
|
860
|
+
log(`No serverless config found. Tried: ${configFormats.join(', ')}`, 'warn', quiet);
|
|
861
|
+
}
|
|
862
|
+
} catch (error) {
|
|
863
|
+
log(`Error loading serverless config: ${error.message}`, 'error', quiet);
|
|
864
|
+
if (debug) {
|
|
865
|
+
log(`Config error stack: ${error.stack}`, 'error', false);
|
|
866
|
+
}
|
|
867
|
+
// Don't exit, continue with empty config
|
|
868
|
+
}
|
|
869
|
+
// Merge config with command line options
|
|
870
|
+
// Determine effective host/ports with correct precedence: CLI > config > defaults
|
|
871
|
+
const configOffline = serverlessConfig.custom?.['serverless-offline'] || {};
|
|
872
|
+
const effectiveHost = cliHost ?? configOffline.host ?? 'localhost';
|
|
873
|
+
const toNumber = (v, fallback)=>{
|
|
874
|
+
if (v === undefined || v === null || v === '') {
|
|
875
|
+
return fallback;
|
|
876
|
+
}
|
|
877
|
+
const n = typeof v === 'number' ? v : parseInt(String(v));
|
|
878
|
+
return Number.isFinite(n) ? n : fallback;
|
|
879
|
+
};
|
|
880
|
+
const effectiveHttpPort = toNumber(cliHttpPort ?? configOffline.httpPort, 3100);
|
|
881
|
+
const effectiveHttpsPort = toNumber(cliHttpsPort ?? configOffline.httpsPort, 3101);
|
|
882
|
+
const effectiveWsPort = toNumber(cliWsPort ?? configOffline.wsPort, 3102);
|
|
883
|
+
const finalConfig = {
|
|
884
|
+
...serverlessConfig,
|
|
885
|
+
custom: {
|
|
886
|
+
'serverless-offline': {
|
|
887
|
+
cors: serverlessConfig.custom?.['serverless-offline']?.cors !== false,
|
|
888
|
+
host: effectiveHost,
|
|
889
|
+
httpPort: effectiveHttpPort,
|
|
890
|
+
httpsPort: effectiveHttpsPort,
|
|
891
|
+
wsPort: effectiveWsPort
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
};
|
|
895
|
+
const outputDir = outputFullPath || 'lib';
|
|
896
|
+
log(`Using output directory: ${outputDir}`, 'info', quiet);
|
|
897
|
+
try {
|
|
898
|
+
spinner.start('Starting serverless development server...');
|
|
899
|
+
const httpPort = finalConfig.custom['serverless-offline'].httpPort;
|
|
900
|
+
const wsPort = finalConfig.custom['serverless-offline'].wsPort;
|
|
901
|
+
const host = finalConfig.custom['serverless-offline'].host;
|
|
902
|
+
const connectionRegistry = new Map();
|
|
903
|
+
log(`Creating HTTP server on ${host}:${httpPort}`, 'info', quiet);
|
|
904
|
+
log(`Creating WebSocket server on port ${wsPort}`, 'info', quiet);
|
|
905
|
+
// Create Express server
|
|
906
|
+
const expressApp = await createExpressServer(finalConfig, outputDir, httpPort, host, connectionRegistry, quiet, debug);
|
|
907
|
+
// Create WebSocket server
|
|
908
|
+
const wsServer = createWebSocketServer(finalConfig, outputDir, wsPort, connectionRegistry, quiet, debug);
|
|
909
|
+
// Handle server errors
|
|
910
|
+
wsServer.on('error', (error)=>{
|
|
911
|
+
log(`WebSocket server error: ${error.message}`, 'error', quiet);
|
|
912
|
+
spinner.fail('Failed to start WebSocket server.');
|
|
913
|
+
callback(1);
|
|
914
|
+
return;
|
|
915
|
+
});
|
|
916
|
+
// Start Express server
|
|
917
|
+
const server = expressApp.listen(httpPort, host, ()=>{
|
|
918
|
+
spinner.succeed('Serverless development server started.');
|
|
919
|
+
void resolvePublicIpForDisplay(usePublicIp).then((publicIp)=>{
|
|
920
|
+
displayServerStatus(httpPort, finalConfig.custom['serverless-offline'].httpsPort, wsPort, host, quiet, publicIp);
|
|
921
|
+
});
|
|
922
|
+
});
|
|
923
|
+
// Handle Express server errors
|
|
924
|
+
server.on('error', (error)=>{
|
|
925
|
+
log(`Express server error: ${error.message}`, 'error', quiet);
|
|
926
|
+
spinner.fail('Failed to start Express server.');
|
|
927
|
+
callback(1);
|
|
928
|
+
return;
|
|
929
|
+
});
|
|
930
|
+
// Handle graceful shutdown
|
|
931
|
+
const shutdown = ()=>{
|
|
932
|
+
log('\nShutting down serverless development server...', 'info', quiet);
|
|
933
|
+
server.close();
|
|
934
|
+
wsServer.close();
|
|
935
|
+
callback(0);
|
|
936
|
+
};
|
|
937
|
+
process.on('SIGINT', shutdown);
|
|
938
|
+
process.on('SIGTERM', shutdown);
|
|
939
|
+
// Keep the process alive
|
|
940
|
+
process.stdin.resume();
|
|
941
|
+
log('Serverless development server is running. Press Ctrl+C to stop.', 'info', quiet);
|
|
942
|
+
// Don't call callback here, let the process stay alive
|
|
943
|
+
return 0;
|
|
944
|
+
} catch (error) {
|
|
945
|
+
log(`\n${cliName} Error: ${error.message}`, 'error', quiet);
|
|
946
|
+
spinner.fail('Failed to start serverless development server.');
|
|
947
|
+
callback(1);
|
|
948
|
+
return 1;
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
|
|
952
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9jb21tYW5kcy9zZXJ2ZXJsZXNzLWRldi9zZXJ2ZXJsZXNzLWRldi50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIENvcHlyaWdodCAoYykgMjAxOC1QcmVzZW50LCBOaXRyb2dlbiBMYWJzLCBJbmMuXG4gKiBDb3B5cmlnaHRzIGxpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIHRoZSBhY2NvbXBhbnlpbmcgTElDRU5TRSBmaWxlIGZvciB0ZXJtcy5cbiAqL1xuaW1wb3J0IGJveGVuIGZyb20gJ2JveGVuJztcbmltcG9ydCBjaGFsayBmcm9tICdjaGFsayc7XG5pbXBvcnQge3JhbmRvbVVVSUR9IGZyb20gJ2NyeXB0byc7XG5pbXBvcnQgZXhwcmVzcyBmcm9tICdleHByZXNzJztcbmltcG9ydCB7cmVhZEZpbGVTeW5jLCBleGlzdHNTeW5jLCBta2RpclN5bmMsIHdyaXRlRmlsZVN5bmN9IGZyb20gJ2ZzJztcbmltcG9ydCB7aG9tZWRpcn0gZnJvbSAnb3MnO1xuaW1wb3J0IHtyZXNvbHZlIGFzIHBhdGhSZXNvbHZlLCBqb2luLCBpc0Fic29sdXRlfSBmcm9tICdwYXRoJztcbmltcG9ydCB7cGF0aFRvRmlsZVVSTH0gZnJvbSAndXJsJztcbmltcG9ydCB7V2ViU29ja2V0U2VydmVyfSBmcm9tICd3cyc7XG5cbmltcG9ydCB7TGV4Q29uZmlnLCBnZXRQYWNrYWdlRGlyfSBmcm9tICcuLi8uLi9MZXhDb25maWcuanMnO1xuaW1wb3J0IHtjcmVhdGVTcGlubmVyLCByZW1vdmVGaWxlc30gZnJvbSAnLi4vLi4vdXRpbHMvYXBwLmpzJztcbmltcG9ydCB7bG9nfSBmcm9tICcuLi8uLi91dGlscy9sb2cuanMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIFNlcnZlcmxlc3NEZXZPcHRpb25zIHtcbiAgcmVhZG9ubHkgY2xpTmFtZT86IHN0cmluZztcbiAgcmVhZG9ubHkgY29uZmlnPzogc3RyaW5nO1xuICByZWFkb25seSBkZWJ1Zz86IGJvb2xlYW47XG4gIHJlYWRvbmx5IGhvc3Q/OiBzdHJpbmc7XG4gIHJlYWRvbmx5IGh0dHBQb3J0PzogbnVtYmVyO1xuICByZWFkb25seSBodHRwc1BvcnQ/OiBudW1iZXI7XG4gIHJlYWRvbmx5IHF1aWV0PzogYm9vbGVhbjtcbiAgcmVhZG9ubHkgcmVtb3ZlPzogYm9vbGVhbjtcbiAgcmVhZG9ubHkgdGVzdD86IGJvb2xlYW47XG4gIHJlYWRvbmx5IHVzZVB1YmxpY0lwPzogYm9vbGVhbjtcbiAgcmVhZG9ubHkgdmFyaWFibGVzPzogc3RyaW5nO1xuICByZWFkb25seSB3c1BvcnQ/OiBudW1iZXI7XG59XG5cbmV4cG9ydCB0eXBlIFNlcnZlcmxlc3NEZXZDYWxsYmFjayA9IChzdGF0dXM6IG51bWJlcikgPT4gdm9pZDtcblxuaW50ZXJmYWNlIFB1YmxpY0lwQ2FjaGUge1xuICBpcDogc3RyaW5nO1xuICB0aW1lc3RhbXA6IG51bWJlcjtcbn1cblxuaW50ZXJmYWNlIFNlcnZlcmxlc3NIYW5kbGVyIHtcbiAgcmVhZG9ubHkgaGFuZGxlcjogc3RyaW5nO1xuICByZWFkb25seSBldmVudHM/OiBBcnJheTx7XG4gICAgcmVhZG9ubHkgaHR0cD86IHtcbiAgICAgIHJlYWRvbmx5IGNvcnM/OiBib29sZWFuO1xuICAgICAgcmVhZG9ubHkgbWV0aG9kPzogc3RyaW5nO1xuICAgICAgcmVhZG9ubHkgcGF0aD86IHN0cmluZztcbiAgICB9O1xuICAgIHJlYWRvbmx5IHdlYnNvY2tldD86IHtcbiAgICAgIHJlYWRvbmx5IHJvdXRlPzogc3RyaW5nO1xuICAgIH07XG4gIH0+O1xufVxuXG5pbnRlcmZhY2UgU2VydmVybGVzc0NvbmZpZyB7XG4gIHJlYWRvbmx5IGN1c3RvbT86IHtcbiAgICByZWFkb25seSAnc2VydmVybGVzcy1vZmZsaW5lJz86IHtcbiAgICAgIHJlYWRvbmx5IGJvZHlMaW1pdD86IHN0cmluZztcbiAgICAgIHJlYWRvbmx5IGNvcnM/OiBib29sZWFuO1xuICAgICAgcmVhZG9ubHkgaG9zdD86IHN0cmluZztcbiAgICAgIHJlYWRvbmx5IGh0dHBQb3J0PzogbnVtYmVyO1xuICAgICAgcmVhZG9ubHkgaHR0cHNQb3J0PzogbnVtYmVyO1xuICAgICAgcmVhZG9ubHkgd3NQb3J0PzogbnVtYmVyO1xuICAgIH07XG4gIH07XG4gIHJlYWRvbmx5IGZ1bmN0aW9ucz86IFJlY29yZDxzdHJpbmcsIFNlcnZlcmxlc3NIYW5kbGVyPjtcbn1cblxuaW50ZXJmYWNlIFdlYlNvY2tldENsaWVudExpa2Uge1xuICBvbihldmVudDogc3RyaW5nLCBsaXN0ZW5lcjogKC4uLmFyZ3M6IGFueVtdKSA9PiB2b2lkKTogdm9pZDtcbiAgcmVhZHlTdGF0ZT86IG51bWJlcjtcbiAgc2VuZChkYXRhOiBzdHJpbmcpOiB2b2lkO1xufVxuXG5jb25zdCBnZXRDYWNoZURpciA9ICgpOiBzdHJpbmcgPT4ge1xuICBjb25zdCBjYWNoZURpciA9IGpvaW4oaG9tZWRpcigpLCAnLmxleC1jYWNoZScpO1xuICBpZighZXhpc3RzU3luYyhjYWNoZURpcikpIHtcbiAgICBta2RpclN5bmMoY2FjaGVEaXIsIHtyZWN1cnNpdmU6IHRydWV9KTtcbiAgfVxuICByZXR1cm4gY2FjaGVEaXI7XG59O1xuXG5jb25zdCBnZXRDYWNoZVBhdGggPSAoKTogc3RyaW5nID0+IGpvaW4oZ2V0Q2FjaGVEaXIoKSwgJ3B1YmxpYy1pcC5qc29uJyk7XG5cbmNvbnN0IHJlYWRQdWJsaWNJcENhY2hlID0gKCk6IFB1YmxpY0lwQ2FjaGUgfCBudWxsID0+IHtcbiAgY29uc3QgY2FjaGVQYXRoID0gZ2V0Q2FjaGVQYXRoKCk7XG4gIGlmKCFleGlzdHNTeW5jKGNhY2hlUGF0aCkpIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIHRyeSB7XG4gICAgY29uc3QgY2FjaGVEYXRhID0gcmVhZEZpbGVTeW5jKGNhY2hlUGF0aCwgJ3V0ZjgnKTtcbiAgICBjb25zdCBjYWNoZTogUHVibGljSXBDYWNoZSA9IEpTT04ucGFyc2UoY2FjaGVEYXRhKTtcblxuICAgIC8vIENoZWNrIGlmIGNhY2hlIGlzIG9sZGVyIHRoYW4gMSB3ZWVrXG4gICAgY29uc3Qgb25lV2Vla01zID0gNyAqIDI0ICogNjAgKiA2MCAqIDEwMDA7XG4gICAgaWYoRGF0ZS5ub3coKSAtIGNhY2hlLnRpbWVzdGFtcCA+IG9uZVdlZWtNcykge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgcmV0dXJuIGNhY2hlO1xuICB9IGNhdGNoe1xuICAgIHJldHVybiBudWxsO1xuICB9XG59O1xuXG5jb25zdCB3cml0ZVB1YmxpY0lwQ2FjaGUgPSAoaXA6IHN0cmluZyk6IHZvaWQgPT4ge1xuICBjb25zdCBjYWNoZVBhdGggPSBnZXRDYWNoZVBhdGgoKTtcbiAgY29uc3QgY2FjaGU6IFB1YmxpY0lwQ2FjaGUgPSB7XG4gICAgaXAsXG4gICAgdGltZXN0YW1wOiBEYXRlLm5vdygpXG4gIH07XG4gIHdyaXRlRmlsZVN5bmMoY2FjaGVQYXRoLCBKU09OLnN0cmluZ2lmeShjYWNoZSwgbnVsbCwgMikpO1xufTtcblxuY29uc3QgZmV0Y2hQdWJsaWNJcCA9IChmb3JjZVJlZnJlc2g6IGJvb2xlYW4gPSBmYWxzZSk6IFByb21pc2U8c3RyaW5nIHwgdW5kZWZpbmVkPiA9PiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICBpZighZm9yY2VSZWZyZXNoKSB7XG4gICAgY29uc3QgY2FjaGVkID0gcmVhZFB1YmxpY0lwQ2FjaGUoKTtcbiAgICBpZihjYWNoZWQpIHtcbiAgICAgIHJlc29sdmUoY2FjaGVkLmlwKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gIH1cblxuICAvLyBVc2UgZmV0Y2ggaW5zdGVhZCBvZiBodHRwc1xuICBmZXRjaCgnaHR0cHM6Ly9hcGkuaXBpZnkub3JnJylcbiAgICAudGhlbigocmVzKSA9PiByZXMudGV4dCgpKVxuICAgIC50aGVuKChkYXRhKSA9PiB7XG4gICAgICBjb25zdCBpcCA9IGRhdGEudHJpbSgpO1xuICAgICAgaWYoaXApIHtcbiAgICAgICAgd3JpdGVQdWJsaWNJcENhY2hlKGlwKTtcbiAgICAgIH1cbiAgICAgIHJlc29sdmUoaXApO1xuICAgIH0pXG4gICAgLmNhdGNoKCgpID0+IHJlc29sdmUodW5kZWZpbmVkKSk7XG59KTtcblxuY29uc3QgcmVzb2x2ZVB1YmxpY0lwRm9yRGlzcGxheSA9IGFzeW5jIChmb3JjZVJlZnJlc2g6IGJvb2xlYW4gPSBmYWxzZSk6IFByb21pc2U8c3RyaW5nIHwgdW5kZWZpbmVkPiA9PiB7XG4gIGNvbnN0IHRpbWVvdXRNcyA9IDE1MDA7XG4gIGNvbnN0IHRpbWVvdXRQcm9taXNlID0gbmV3IFByb21pc2U8dW5kZWZpbmVkPigocmVzb2x2ZSkgPT4ge1xuICAgIHNldFRpbWVvdXQoKCkgPT4gcmVzb2x2ZSh1bmRlZmluZWQpLCB0aW1lb3V0TXMpO1xuICB9KTtcblxuICB0cnkge1xuICAgIHJldHVybiBhd2FpdCBQcm9taXNlLnJhY2UoW2ZldGNoUHVibGljSXAoZm9yY2VSZWZyZXNoKSwgdGltZW91dFByb21pc2VdKTtcbiAgfSBjYXRjaHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG59O1xuXG5jb25zdCBkaXNwbGF5U2VydmVyU3RhdHVzID0gKFxuICBodHRwUG9ydDogbnVtYmVyLFxuICBodHRwc1BvcnQ6IG51bWJlcixcbiAgd3NQb3J0OiBudW1iZXIsXG4gIGhvc3Q6IHN0cmluZyxcbiAgcXVpZXQ6IGJvb2xlYW4sXG4gIHB1YmxpY0lwPzogc3RyaW5nXG4pID0+IHtcbiAgaWYocXVpZXQpIHtcbiAgICByZXR1cm47XG4gIH1cblxuICBjb25zdCBodHRwVXJsID0gYGh0dHA6Ly8ke2hvc3R9OiR7aHR0cFBvcnR9YDtcbiAgY29uc3QgaHR0cHNVcmwgPSBgaHR0cHM6Ly8ke2hvc3R9OiR7aHR0cHNQb3J0fWA7XG4gIGNvbnN0IHdzVXJsID0gYHdzOi8vJHtob3N0fToke3dzUG9ydH1gO1xuICBjb25zdCB3c3NVcmwgPSBgd3NzOi8vJHtob3N0fToke3dzUG9ydH1gO1xuXG4gIGxldCB1cmxMaW5lcyA9IGAke2NoYWxrLmdyZWVuKCdIVFRQOicpfSAgICAgICR7Y2hhbGsudW5kZXJsaW5lKGh0dHBVcmwpfVxcbmA7XG4gIHVybExpbmVzICs9IGAke2NoYWxrLmdyZWVuKCdIVFRQUzonKX0gICAgICR7Y2hhbGsudW5kZXJsaW5lKGh0dHBzVXJsKX1cXG5gO1xuICB1cmxMaW5lcyArPSBgJHtjaGFsay5ncmVlbignV2ViU29ja2V0OicpfSAke2NoYWxrLnVuZGVybGluZSh3c1VybCl9XFxuYDtcbiAgdXJsTGluZXMgKz0gYCR7Y2hhbGsuZ3JlZW4oJ1dTUzonKX0gICAgICAgJHtjaGFsay51bmRlcmxpbmUod3NzVXJsKX1cXG5gO1xuXG4gIGlmKHB1YmxpY0lwKSB7XG4gICAgdXJsTGluZXMgKz0gYFxcbiR7Y2hhbGsuZ3JlZW4oJ1B1YmxpYzonKX0gICAgJHtjaGFsay51bmRlcmxpbmUoYGh0dHA6Ly8ke3B1YmxpY0lwfToke2h0dHBQb3J0fWApfVxcbmA7XG4gIH1cblxuICBjb25zdCBzdGF0dXNCb3ggPSBib3hlbihcbiAgICBgJHtjaGFsay5jeWFuLmJvbGQoJ/CfmoAgU2VydmVybGVzcyBEZXZlbG9wbWVudCBTZXJ2ZXIgUnVubmluZycpfVxcblxcbiR7dXJsTGluZXN9XFxuYCArXG4gICAgYCR7Y2hhbGsueWVsbG93KCdQcmVzcyBDdHJsK0MgdG8gc3RvcCB0aGUgc2VydmVyJyl9YCxcbiAgICB7XG4gICAgICBiYWNrZ3JvdW5kQ29sb3I6ICcjMWExYTFhJyxcbiAgICAgIGJvcmRlckNvbG9yOiAnY3lhbicsXG4gICAgICBib3JkZXJTdHlsZTogJ3JvdW5kJyxcbiAgICAgIG1hcmdpbjogMSxcbiAgICAgIHBhZGRpbmc6IDFcbiAgICB9XG4gICk7XG5cbiAgY29uc29sZS5sb2coYFxcbiR7c3RhdHVzQm94fVxcbmApO1xufTtcblxuY29uc3QgbG9hZEhhbmRsZXIgPSBhc3luYyAoaGFuZGxlclBhdGg6IHN0cmluZywgb3V0cHV0RGlyOiBzdHJpbmcpID0+IHtcbiAgdHJ5IHtcbiAgICBjb25zb2xlLmxvZyhgW1NlcnZlcmxlc3NdIFBhcnNpbmcgaGFuZGxlciBwYXRoOiAke2hhbmRsZXJQYXRofWApO1xuXG4gICAgLy8gUGFyc2UgQVdTIExhbWJkYSBoYW5kbGVyIGZvcm1hdDogXCJwYXRoL3RvL2ZpbGUuZXhwb3J0TmFtZVwiIG9yIFwiZmlsZS5leHBvcnROYW1lXCJcbiAgICAvLyBFeGFtcGxlczogXCJpbmRleC5oYW5kbGVyXCIsIFwiaGFuZGxlcnMvYXBpLmhhbmRsZXJcIiwgXCJzcmMvaW5kZXguZGVmYXVsdFwiXG4gICAgY29uc3QgaGFuZGxlclBhcnRzID0gaGFuZGxlclBhdGguc3BsaXQoJy4nKTtcbiAgICBjb25zb2xlLmxvZygnW1NlcnZlcmxlc3NdIEhhbmRsZXIgcGFydHMgYWZ0ZXIgc3BsaXQ6JywgaGFuZGxlclBhcnRzKTtcblxuICAgIGxldCBmaWxlUGF0aDogc3RyaW5nO1xuICAgIGxldCBleHBvcnROYW1lOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcblxuICAgIGlmKGhhbmRsZXJQYXJ0cy5sZW5ndGggPiAxKSB7XG4gICAgICAvLyBBV1MgTGFtYmRhIGZvcm1hdDogXCJmaWxlLmV4cG9ydE5hbWVcIlxuICAgICAgLy8gVGFrZSB0aGUgbGFzdCBwYXJ0IGFzIGV4cG9ydCBuYW1lLCByZXN0IGFzIGZpbGUgcGF0aFxuICAgICAgLy8gSGFuZGxlIGNhc2VzIGxpa2UgXCJpbmRleC5oYW5kbGVyXCIgLT4gZmlsZTogXCJpbmRleFwiLCBleHBvcnQ6IFwiaGFuZGxlclwiXG4gICAgICAvLyBPciBcImhhbmRsZXJzL2FwaS5oYW5kbGVyXCIgLT4gZmlsZTogXCJoYW5kbGVycy9hcGlcIiwgZXhwb3J0OiBcImhhbmRsZXJcIlxuICAgICAgZXhwb3J0TmFtZSA9IGhhbmRsZXJQYXJ0c1toYW5kbGVyUGFydHMubGVuZ3RoIC0gMV0gfHwgbnVsbDtcbiAgICAgIGZpbGVQYXRoID0gaGFuZGxlclBhcnRzLnNsaWNlKDAsIC0xKS5qb2luKCcuJyk7XG4gICAgICBjb25zb2xlLmxvZyhgW1NlcnZlcmxlc3NdIFBhcnNlZCBBV1MgTGFtYmRhIGZvcm1hdCAtIGZpbGVQYXRoOiBcIiR7ZmlsZVBhdGh9XCIsIGV4cG9ydE5hbWU6IFwiJHtleHBvcnROYW1lfVwiYCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIFNpbXBsZSBmb3JtYXQ6IGp1c3QgdGhlIGZpbGUgcGF0aFxuICAgICAgZmlsZVBhdGggPSBoYW5kbGVyUGF0aDtcbiAgICAgIGNvbnNvbGUubG9nKGBbU2VydmVybGVzc10gU2ltcGxlIGZvcm1hdCAtIGZpbGVQYXRoOiBcIiR7ZmlsZVBhdGh9XCJgKTtcbiAgICB9XG5cbiAgICAvLyBFbnN1cmUgZmlsZVBhdGggZG9lc24ndCBoYXZlIHRoZSBleHBvcnQgbmFtZSBpbiBpdFxuICAgIGlmKGZpbGVQYXRoLmluY2x1ZGVzKCcuaGFuZGxlcicpIHx8IGZpbGVQYXRoLmluY2x1ZGVzKCcuZGVmYXVsdCcpKSB7XG4gICAgICBjb25zb2xlLmVycm9yKGBbU2VydmVybGVzc10gV0FSTklORzogZmlsZVBhdGggc3RpbGwgY29udGFpbnMgZXhwb3J0IG5hbWUhIGZpbGVQYXRoOiBcIiR7ZmlsZVBhdGh9XCJgKTtcbiAgICAgIC8vIFRyeSB0byBmaXggaXQgLSByZW1vdmUgdGhlIGxhc3QgcGFydCBpZiBpdCBsb29rcyBsaWtlIGFuIGV4cG9ydCBuYW1lXG4gICAgICBjb25zdCBwYXRoUGFydHMgPSBmaWxlUGF0aC5zcGxpdCgnLicpO1xuICAgICAgaWYocGF0aFBhcnRzLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgY29uc3QgbGFzdFBhcnQgPSBwYXRoUGFydHNbcGF0aFBhcnRzLmxlbmd0aCAtIDFdO1xuICAgICAgICBpZihbJ2hhbmRsZXInLCAnZGVmYXVsdCcsICdpbmRleCddLmluY2x1ZGVzKGxhc3RQYXJ0KSAmJiAhZXhwb3J0TmFtZSkge1xuICAgICAgICAgIGV4cG9ydE5hbWUgPSBsYXN0UGFydDtcbiAgICAgICAgICBmaWxlUGF0aCA9IHBhdGhQYXJ0cy5zbGljZSgwLCAtMSkuam9pbignLicpO1xuICAgICAgICAgIGNvbnNvbGUubG9nKGBbU2VydmVybGVzc10gRml4ZWQgLSBmaWxlUGF0aDogXCIke2ZpbGVQYXRofVwiLCBleHBvcnROYW1lOiBcIiR7ZXhwb3J0TmFtZX1cImApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gSGFuZGxlIGJvdGggcmVsYXRpdmUgcGF0aHMgYW5kIGFic29sdXRlIHBhdGhzXG4gICAgbGV0IGZ1bGxQYXRoOiBzdHJpbmc7XG4gICAgaWYoaXNBYnNvbHV0ZShmaWxlUGF0aCkpIHtcbiAgICAgIGZ1bGxQYXRoID0gZmlsZVBhdGg7XG4gICAgfSBlbHNlIHtcbiAgICAgIGZ1bGxQYXRoID0gcGF0aFJlc29sdmUob3V0cHV0RGlyLCBmaWxlUGF0aCk7XG4gICAgfVxuICAgIGNvbnNvbGUubG9nKGBbU2VydmVybGVzc10gUmVzb2x2ZWQgZnVsbFBhdGggKGJlZm9yZSBleHRlbnNpb25zKTogJHtmdWxsUGF0aH1gKTtcblxuICAgIC8vIFRyeSBkaWZmZXJlbnQgZXh0ZW5zaW9ucyBpZiBmaWxlIGRvZXNuJ3QgZXhpc3RcbiAgICBpZighZXhpc3RzU3luYyhmdWxsUGF0aCkpIHtcbiAgICAgIGNvbnN0IGV4dGVuc2lvbnMgPSBbJy5qcycsICcubWpzJywgJy5janMnXTtcbiAgICAgIGNvbnN0IHBhdGhXaXRob3V0RXh0ID0gZnVsbFBhdGgucmVwbGFjZSgvXFwuKGpzfG1qc3xjanMpJC8sICcnKTtcbiAgICAgIGNvbnNvbGUubG9nKGBbU2VydmVybGVzc10gVHJ5aW5nIGV4dGVuc2lvbnMuIEJhc2UgcGF0aDogJHtwYXRoV2l0aG91dEV4dH1gKTtcbiAgICAgIGZvcihjb25zdCBleHQgb2YgZXh0ZW5zaW9ucykge1xuICAgICAgICBjb25zdCBjYW5kaWRhdGVQYXRoID0gcGF0aFdpdGhvdXRFeHQgKyBleHQ7XG4gICAgICAgIGNvbnNvbGUubG9nKGBbU2VydmVybGVzc10gQ2hlY2tpbmc6ICR7Y2FuZGlkYXRlUGF0aH0gKGV4aXN0czogJHtleGlzdHNTeW5jKGNhbmRpZGF0ZVBhdGgpfSlgKTtcbiAgICAgICAgaWYoZXhpc3RzU3luYyhjYW5kaWRhdGVQYXRoKSkge1xuICAgICAgICAgIGZ1bGxQYXRoID0gY2FuZGlkYXRlUGF0aDtcbiAgICAgICAgICBjb25zb2xlLmxvZyhgW1NlcnZlcmxlc3NdIEZvdW5kIGZpbGUgd2l0aCBleHRlbnNpb246ICR7ZnVsbFBhdGh9YCk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgY29uc29sZS5sb2coYFtTZXJ2ZXJsZXNzXSBGaWxlIGV4aXN0cyB3aXRob3V0IHRyeWluZyBleHRlbnNpb25zOiAke2Z1bGxQYXRofWApO1xuICAgIH1cblxuICAgIGNvbnNvbGUubG9nKGBbU2VydmVybGVzc10gRmluYWwgZnVsbFBhdGg6ICR7ZnVsbFBhdGh9YCk7XG4gICAgY29uc29sZS5sb2coYFtTZXJ2ZXJsZXNzXSBFeHBvcnQgbmFtZTogJHtleHBvcnROYW1lIHx8ICdkZWZhdWx0L2hhbmRsZXInfWApO1xuICAgIGNvbnNvbGUubG9nKGBbU2VydmVybGVzc10gRmlsZSBleGlzdHM6ICR7ZXhpc3RzU3luYyhmdWxsUGF0aCl9YCk7XG5cbiAgICBpZighZXhpc3RzU3luYyhmdWxsUGF0aCkpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoYFtTZXJ2ZXJsZXNzXSBIYW5kbGVyIGZpbGUgbm90IGZvdW5kOiAke2Z1bGxQYXRofWApO1xuICAgICAgY29uc29sZS5lcnJvcihgW1NlcnZlcmxlc3NdIE91dHB1dCBkaXJlY3Rvcnk6ICR7b3V0cHV0RGlyfWApO1xuICAgICAgY29uc29sZS5lcnJvcihgW1NlcnZlcmxlc3NdIEhhbmRsZXIgcGF0aCBmcm9tIGNvbmZpZzogJHtoYW5kbGVyUGF0aH1gKTtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgSGFuZGxlciBmaWxlIG5vdCBmb3VuZDogJHtmdWxsUGF0aH1gKTtcbiAgICB9XG5cbiAgICAvLyBEeW5hbWljIGltcG9ydCBvZiB0aGUgaGFuZGxlciB3aXRoIGJldHRlciBlcnJvciBoYW5kbGluZ1xuICAgIC8vIEFkZCAuanMgZXh0ZW5zaW9uIGlmIGltcG9ydGluZyBUeXBlU2NyaXB0IGNvbXBpbGVkIG91dHB1dFxuICAgIGNvbnN0IGltcG9ydFBhdGggPSBmdWxsUGF0aC5lbmRzV2l0aCgnLnRzJykgPyBmdWxsUGF0aC5yZXBsYWNlKC9cXC50cyQvLCAnLmpzJykgOiBmdWxsUGF0aDtcblxuICAgIHRyeSB7XG4gICAgICAvLyBDb252ZXJ0IHRvIGZpbGU6Ly8gVVJMIGZvciBFUyBtb2R1bGUgaW1wb3J0cyAocmVxdWlyZWQgZm9yIGFic29sdXRlIHBhdGhzKVxuICAgICAgLy8gVXNlIHBhdGhUb0ZpbGVVUkwgdG8gZW5zdXJlIHByb3BlciBmaWxlOi8vIFVSTCBmb3JtYXRcbiAgICAgIGNvbnN0IGltcG9ydFVybCA9IHBhdGhUb0ZpbGVVUkwoaW1wb3J0UGF0aCkuaHJlZjtcbiAgICAgIGNvbnNvbGUubG9nKGBbU2VydmVybGVzc10gSW1wb3J0aW5nIGhhbmRsZXIgZnJvbTogJHtpbXBvcnRVcmx9YCk7XG4gICAgICBjb25zb2xlLmxvZyhgW1NlcnZlcmxlc3NdIEZpbGUgcGF0aDogJHtpbXBvcnRQYXRofWApO1xuXG4gICAgICAvLyBVc2UgaW1wb3J0KCkgd2l0aCB0aGUgZmlsZSBVUkxcbiAgICAgIC8vIE5vdGU6IElmIHRoZSBoYW5kbGVyIGZpbGUgaGFzIGltcG9ydCBlcnJvcnMgKGxpa2UgbWlzc2luZyBkZXBlbmRlbmNpZXMpLFxuICAgICAgLy8gdGhvc2Ugd2lsbCBzdXJmYWNlIGhlcmUsIGJ1dCB0aGF0J3MgYSBoYW5kbGVyIGNvZGUgaXNzdWUsIG5vdCBhIGxvYWRlciBpc3N1ZVxuICAgICAgY29uc3QgaGFuZGxlck1vZHVsZSA9IGF3YWl0IGltcG9ydChpbXBvcnRVcmwpO1xuICAgICAgY29uc29sZS5sb2coYFtTZXJ2ZXJsZXNzXSBIYW5kbGVyIG1vZHVsZSBsb2FkZWQgc3VjY2Vzc2Z1bGx5LiBFeHBvcnRzOiAke09iamVjdC5rZXlzKGhhbmRsZXJNb2R1bGUpLmpvaW4oJywgJyl9YCk7XG5cbiAgICAgIC8vIEdldCB0aGUgaGFuZGxlciBiYXNlZCBvbiBleHBvcnQgbmFtZSBvciB0cnkgZGVmYXVsdHNcbiAgICAgIGxldCBoYW5kbGVyOiBhbnk7XG4gICAgICBpZihleHBvcnROYW1lKSB7XG4gICAgICAgIGhhbmRsZXIgPSBoYW5kbGVyTW9kdWxlW2V4cG9ydE5hbWVdXG4gICAgICAgICAgfHwgaGFuZGxlck1vZHVsZS5kZWZhdWx0Py5bZXhwb3J0TmFtZV1cbiAgICAgICAgICB8fCBoYW5kbGVyTW9kdWxlWydtb2R1bGUuZXhwb3J0cyddPy5bZXhwb3J0TmFtZV07XG4gICAgICAgIGlmKCFoYW5kbGVyKSB7XG4gICAgICAgICAgY29uc29sZS5lcnJvcihgW1NlcnZlcmxlc3NdIEV4cG9ydCBcIiR7ZXhwb3J0TmFtZX1cIiBub3QgZm91bmQgaW4gbW9kdWxlLiBBdmFpbGFibGUgZXhwb3J0czogJHtPYmplY3Qua2V5cyhoYW5kbGVyTW9kdWxlKS5qb2luKCcsICcpfWApO1xuICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBUcnkgZGVmYXVsdCwgaGFuZGxlciwgb3IgdGhlIG1vZHVsZSBpdHNlbGZcbiAgICAgICAgaGFuZGxlciA9IGhhbmRsZXJNb2R1bGUuZGVmYXVsdCB8fCBoYW5kbGVyTW9kdWxlLmhhbmRsZXIgfHwgaGFuZGxlck1vZHVsZTtcbiAgICAgIH1cblxuICAgICAgY29uc29sZS5sb2coYFtTZXJ2ZXJsZXNzXSBIYW5kbGVyIGZvdW5kOiAke3R5cGVvZiBoYW5kbGVyfSwgaXNGdW5jdGlvbjogJHt0eXBlb2YgaGFuZGxlciA9PT0gJ2Z1bmN0aW9uJ31gKTtcblxuICAgICAgaWYodHlwZW9mIGhhbmRsZXIgIT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihgW1NlcnZlcmxlc3NdIEhhbmRsZXIgaXMgbm90IGEgZnVuY3Rpb24uIFR5cGU6ICR7dHlwZW9mIGhhbmRsZXJ9LCBWYWx1ZTpgLCBoYW5kbGVyKTtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBoYW5kbGVyO1xuICAgIH0gY2F0Y2goaW1wb3J0RXJyb3I6IGFueSkge1xuICAgICAgY29uc29sZS5lcnJvcihgW1NlcnZlcmxlc3NdIEltcG9ydCBlcnJvciBmb3IgaGFuZGxlciAke2hhbmRsZXJQYXRofTpgLCBpbXBvcnRFcnJvci5tZXNzYWdlKTtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ1tTZXJ2ZXJsZXNzXSBJbXBvcnQgZXJyb3Igc3RhY2s6JywgaW1wb3J0RXJyb3Iuc3RhY2spO1xuXG4gICAgICAvLyBDaGVjayBpZiB0aGlzIGlzIGEgZGVwZW5kZW5jeSByZXNvbHV0aW9uIGVycm9yIChjb21tb24gd2l0aCBFUyBtb2R1bGVzKVxuICAgICAgaWYoaW1wb3J0RXJyb3IubWVzc2FnZSAmJiBpbXBvcnRFcnJvci5tZXNzYWdlLmluY2x1ZGVzKCdDYW5ub3QgZmluZCBtb2R1bGUnKSkge1xuICAgICAgICBjb25zb2xlLmVycm9yKCdbU2VydmVybGVzc10gVGhpcyBhcHBlYXJzIHRvIGJlIGEgZGVwZW5kZW5jeSByZXNvbHV0aW9uIGVycm9yLicpO1xuICAgICAgICBjb25zb2xlLmVycm9yKCdbU2VydmVybGVzc10gVGhlIGhhbmRsZXIgZmlsZSBleGlzdHMsIGJ1dCBvbmUgb2YgaXRzIGltcG9ydHMgaXMgZmFpbGluZy4nKTtcbiAgICAgICAgY29uc29sZS5lcnJvcignW1NlcnZlcmxlc3NdIENoZWNrIHRoYXQgYWxsIGRlcGVuZGVuY2llcyBpbiB0aGUgaGFuZGxlciBmaWxlIGFyZSBwcm9wZXJseSBpbnN0YWxsZWQuJyk7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoYFtTZXJ2ZXJsZXNzXSBIYW5kbGVyIGZpbGU6ICR7aW1wb3J0UGF0aH1gKTtcbiAgICAgICAgY29uc29sZS5lcnJvcignW1NlcnZlcmxlc3NdIE1ha2Ugc3VyZSB0aGUgaGFuZGxlciBhbmQgaXRzIGRlcGVuZGVuY2llcyBhcmUgY29tcGlsZWQgY29ycmVjdGx5LicpO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gIH0gY2F0Y2goZXJyb3I6IGFueSkge1xuICAgIGNvbnNvbGUuZXJyb3IoYFtTZXJ2ZXJsZXNzXSBFcnJvciBsb2FkaW5nIGhhbmRsZXIgJHtoYW5kbGVyUGF0aH06YCwgZXJyb3IubWVzc2FnZSk7XG4gICAgY29uc29sZS5lcnJvcignW1NlcnZlcmxlc3NdIEVycm9yIHN0YWNrOicsIGVycm9yLnN0YWNrKTtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxufTtcblxuY29uc3QgY2FwdHVyZUNvbnNvbGVMb2dzID0gKGhhbmRsZXI6IChldmVudDogYW55LCBjb250ZXh0OiBhbnkpID0+IFByb21pc2U8YW55PiwgcXVpZXQ6IGJvb2xlYW4pID0+IHtcbiAgaWYocXVpZXQpIHtcbiAgICByZXR1cm4gaGFuZGxlcjtcbiAgfVxuXG4gIHJldHVybiBhc3luYyAoZXZlbnQ6IGFueSwgY29udGV4dDogYW55KSA9PiB7XG4gICAgLy8gQ2FwdHVyZSBjb25zb2xlLmxvZywgY29uc29sZS5lcnJvciwgZXRjLlxuICAgIGNvbnN0IG9yaWdpbmFsQ29uc29sZUxvZyA9IGNvbnNvbGUubG9nO1xuICAgIGNvbnN0IG9yaWdpbmFsQ29uc29sZUVycm9yID0gY29uc29sZS5lcnJvcjtcbiAgICBjb25zdCBvcmlnaW5hbENvbnNvbGVXYXJuID0gY29uc29sZS53YXJuO1xuICAgIGNvbnN0IG9yaWdpbmFsQ29uc29sZUluZm8gPSBjb25zb2xlLmluZm87XG5cbiAgICBjb25zdCBsb2dzOiBzdHJpbmdbXSA9IFtdO1xuXG4gICAgY29uc29sZS5sb2cgPSAoLi4uYXJnczogYW55W10pID0+IHtcbiAgICAgIGxvZ3MucHVzaChgW0xPR10gJHthcmdzLmpvaW4oJyAnKX1gKTtcbiAgICAgIG9yaWdpbmFsQ29uc29sZUxvZyguLi5hcmdzKTtcbiAgICB9O1xuXG4gICAgY29uc29sZS5lcnJvciA9ICguLi5hcmdzOiBhbnlbXSkgPT4ge1xuICAgICAgbG9ncy5wdXNoKGBbRVJST1JdICR7YXJncy5qb2luKCcgJyl9YCk7XG4gICAgICBvcmlnaW5hbENvbnNvbGVFcnJvciguLi5hcmdzKTtcbiAgICB9O1xuXG4gICAgY29uc29sZS53YXJuID0gKC4uLmFyZ3M6IGFueVtdKSA9PiB7XG4gICAgICBsb2dzLnB1c2goYFtXQVJOXSAke2FyZ3Muam9pbignICcpfWApO1xuICAgICAgb3JpZ2luYWxDb25zb2xlV2FybiguLi5hcmdzKTtcbiAgICB9O1xuXG4gICAgY29uc29sZS5pbmZvID0gKC4uLmFyZ3M6IGFueVtdKSA9PiB7XG4gICAgICBsb2dzLnB1c2goYFtJTkZPXSAke2FyZ3Muam9pbignICcpfWApO1xuICAgICAgb3JpZ2luYWxDb25zb2xlSW5mbyguLi5hcmdzKTtcbiAgICB9O1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGhhbmRsZXIoZXZlbnQsIGNvbnRleHQpO1xuXG4gICAgICAvLyBPdXRwdXQgY2FwdHVyZWQgbG9nc1xuICAgICAgaWYobG9ncy5sZW5ndGggPiAwKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGNoYWxrLmdyYXkoJy0tLSBIYW5kbGVyIENvbnNvbGUgT3V0cHV0IC0tLScpKTtcbiAgICAgICAgbG9ncy5mb3JFYWNoKChsb2cpID0+IGNvbnNvbGUubG9nKGNoYWxrLmdyYXkobG9nKSkpO1xuICAgICAgICBjb25zb2xlLmxvZyhjaGFsay5ncmF5KCctLS0gRW5kIEhhbmRsZXIgQ29uc29sZSBPdXRwdXQgLS0tJykpO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH0gZmluYWxseSB7XG4gICAgICAvLyBSZXN0b3JlIG9yaWdpbmFsIGNvbnNvbGUgbWV0aG9kc1xuICAgICAgY29uc29sZS5sb2cgPSBvcmlnaW5hbENvbnNvbGVMb2c7XG4gICAgICBjb25zb2xlLmVycm9yID0gb3JpZ2luYWxDb25zb2xlRXJyb3I7XG4gICAgICBjb25zb2xlLndhcm4gPSBvcmlnaW5hbENvbnNvbGVXYXJuO1xuICAgICAgY29uc29sZS5pbmZvID0gb3JpZ2luYWxDb25zb2xlSW5mbztcbiAgICB9XG4gIH07XG59O1xuXG5jb25zdCBjcmVhdGVFeHByZXNzU2VydmVyID0gYXN5bmMgKFxuICBjb25maWc6IFNlcnZlcmxlc3NDb25maWcsXG4gIG91dHB1dERpcjogc3RyaW5nLFxuICBodHRwUG9ydDogbnVtYmVyLFxuICBob3N0OiBzdHJpbmcsXG4gIGNvbm5lY3Rpb25SZWdpc3RyeTogTWFwPHN0cmluZywgV2ViU29ja2V0Q2xpZW50TGlrZT4sXG4gIHF1aWV0OiBib29sZWFuLFxuICBkZWJ1ZzogYm9vbGVhblxuKSA9PiB7XG4gIGNvbnN0IGFwcCA9IGV4cHJlc3MoKTtcblxuICAvLyBFbmFibGUgQ09SU1xuICBhcHAudXNlKChyZXEsIHJlcywgbmV4dCkgPT4ge1xuICAgIHJlcy5oZWFkZXIoJ0FjY2Vzcy1Db250cm9sLUFsbG93LU9yaWdpbicsICcqJyk7XG4gICAgcmVzLmhlYWRlcignQWNjZXNzLUNvbnRyb2wtQWxsb3ctTWV0aG9kcycsICdHRVQsIFBPU1QsIFBVVCwgREVMRVRFLCBQQVRDSCwgT1BUSU9OUycpO1xuICAgIHJlcy5oZWFkZXIoJ0FjY2Vzcy1Db250cm9sLUFsbG93LUhlYWRlcnMnLCAnKicpO1xuICAgIHJlcy5oZWFkZXIoJ0FjY2Vzcy1Db250cm9sLUFsbG93LUNyZWRlbnRpYWxzJywgJ3RydWUnKTtcblxuICAgIGlmKHJlcS5tZXRob2QgPT09ICdPUFRJT05TJykge1xuICAgICAgcmVzLnNlbmRTdGF0dXMoMjAwKTtcbiAgICB9IGVsc2Uge1xuICAgICAgbmV4dCgpO1xuICAgIH1cbiAgfSk7XG5cbiAgLy8gUGFyc2UgcmF3IHdlYnNvY2tldCBtYW5hZ2VtZW50IEFQSSBwYXlsb2Fkc1xuICBhcHAudXNlKGV4cHJlc3MucmF3KHtsaW1pdDogJzEwbWInLCB0eXBlOiAnYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtJ30pKTtcblxuICAvLyBQcmVzZXJ2ZSBtdWx0aXBhcnQgdXBsb2FkcyBhcyByYXcgTGFtYmRhLXN0eWxlIGJvZGllcyBzbyB1cGxvYWQgaGFuZGxlcnMgY2FuIHBhcnNlIGJvdW5kYXJpZXMuXG4gIGFwcC51c2UoZXhwcmVzcy5yYXcoe1xuICAgIGxpbWl0OiBjb25maWcuY3VzdG9tPy5bJ3NlcnZlcmxlc3Mtb2ZmbGluZSddPy5ib2R5TGltaXQgfHwgJzI1bWInLFxuICAgIHR5cGU6IChyZXEpID0+IFN0cmluZyhyZXEuaGVhZGVyc1snY29udGVudC10eXBlJ10gfHwgJycpLmluY2x1ZGVzKCdtdWx0aXBhcnQvZm9ybS1kYXRhJylcbiAgfSkpO1xuXG4gIC8vIFBhcnNlIEpTT04gYm9kaWVzXG4gIGFwcC51c2UoZXhwcmVzcy5qc29uKHtsaW1pdDogY29uZmlnLmN1c3RvbT8uWydzZXJ2ZXJsZXNzLW9mZmxpbmUnXT8uYm9keUxpbWl0IHx8ICcyNW1iJ30pKTtcblxuICBhcHAucG9zdChbJy9AY29ubmVjdGlvbnMvOmNvbm5lY3Rpb25JZCcsICcvOnN0YWdlL0Bjb25uZWN0aW9ucy86Y29ubmVjdGlvbklkJ10sIChyZXEsIHJlcykgPT4ge1xuICAgIGNvbnN0IGNvbm5lY3Rpb25JZCA9IFN0cmluZyhyZXEucGFyYW1zLmNvbm5lY3Rpb25JZCB8fCAnJyk7XG4gICAgY29uc3Qgd3MgPSBjb25uZWN0aW9uUmVnaXN0cnkuZ2V0KGNvbm5lY3Rpb25JZCk7XG5cbiAgICBpZighd3MpIHtcbiAgICAgIHJlcy5zdGF0dXMoNDEwKS5qc29uKHttZXNzYWdlOiAnR29uZSd9KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBsZXQgYm9keSA9ICcnO1xuXG4gICAgaWYoQnVmZmVyLmlzQnVmZmVyKHJlcS5ib2R5KSkge1xuICAgICAgYm9keSA9IHJlcS5ib2R5LnRvU3RyaW5nKCd1dGY4Jyk7XG4gICAgfSBlbHNlIGlmKHR5cGVvZiByZXEuYm9keSA9PT0gJ3N0cmluZycpIHtcbiAgICAgIGJvZHkgPSByZXEuYm9keTtcbiAgICB9IGVsc2UgaWYocmVxLmJvZHkgIT09IHVuZGVmaW5lZCAmJiByZXEuYm9keSAhPT0gbnVsbCAmJiByZXEuYm9keSAhPT0gJycpIHtcbiAgICAgIGJvZHkgPSBKU09OLnN0cmluZ2lmeShyZXEuYm9keSk7XG4gICAgfVxuXG4gICAgaWYoYm9keSkge1xuICAgICAgd3Muc2VuZChib2R5KTtcbiAgICB9XG5cbiAgICByZXMuc3RhdHVzKDIwMCkuanNvbih7b2s6IHRydWV9KTtcbiAgfSk7XG5cbiAgLy8gTG9hZCBHcmFwaFFMIGhhbmRsZXJcbiAgY29uc3QgbG9hZEdyYXBoUUxTY2hlbWEgPSBhc3luYyAoKSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIFRyeSB0byBmaW5kIGEgR3JhcGhRTCBoYW5kbGVyXG4gICAgICBsZXQgZ3JhcGhxbEhhbmRsZXIgPSBudWxsO1xuXG4gICAgICBpZihjb25maWcuZnVuY3Rpb25zKSB7XG4gICAgICAgIGZvcihjb25zdCBbZnVuY3Rpb25OYW1lLCBmdW5jdGlvbkNvbmZpZ10gb2YgT2JqZWN0LmVudHJpZXMoY29uZmlnLmZ1bmN0aW9ucykpIHtcbiAgICAgICAgICBpZihmdW5jdGlvbkNvbmZpZy5ldmVudHMpIHtcbiAgICAgICAgICAgIGZvcihjb25zdCBldmVudCBvZiBmdW5jdGlvbkNvbmZpZy5ldmVudHMpIHtcbiAgICAgICAgICAgICAgaWYoZXZlbnQuaHR0cCAmJiBldmVudC5odHRwLnBhdGgpIHtcbiAgICAgICAgICAgICAgICAvLyBMb29rIGZvciBHcmFwaFFMIGVuZHBvaW50c1xuICAgICAgICAgICAgICAgIGlmKGV2ZW50Lmh0dHAucGF0aCA9PT0gJy9wdWJsaWMnIHx8IGV2ZW50Lmh0dHAucGF0aCA9PT0gJy9ncmFwaHFsJykge1xuICAgICAgICAgICAgICAgICAgZ3JhcGhxbEhhbmRsZXIgPSBhd2FpdCBsb2FkSGFuZGxlcihmdW5jdGlvbkNvbmZpZy5oYW5kbGVyLCBvdXRwdXREaXIpO1xuICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmKGdyYXBocWxIYW5kbGVyKSB7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYoZ3JhcGhxbEhhbmRsZXIpIHtcbiAgICAgICAgbG9nKCdGb3VuZCBHcmFwaFFMIGhhbmRsZXInLCAnaW5mbycsIHF1aWV0KTtcbiAgICAgICAgcmV0dXJuIGdyYXBocWxIYW5kbGVyO1xuICAgICAgfVxuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfSBjYXRjaChlcnJvcikge1xuICAgICAgbG9nKGBFcnJvciBsb2FkaW5nIEdyYXBoUUwgaGFuZGxlcjogJHtlcnJvci5tZXNzYWdlfWAsICdlcnJvcicsIHF1aWV0KTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfTtcblxuICAvLyBTZXQgdXAgR3JhcGhRTCBoYW5kbGVyIGZvciBHcmFwaFFMIHJlcXVlc3RzXG4gIHRyeSB7XG4gICAgY29uc3QgZ3JhcGhxbEhhbmRsZXIgPSBhd2FpdCBsb2FkR3JhcGhRTFNjaGVtYSgpO1xuICAgIGlmKGdyYXBocWxIYW5kbGVyKSB7XG4gICAgICAvLyBGaW5kIHRoZSBHcmFwaFFMIHBhdGggZnJvbSB0aGUgc2VydmVybGVzcyBjb25maWdcbiAgICAgIGxldCBncmFwaHFsUGF0aCA9ICcvZ3JhcGhxbCc7IC8vIGRlZmF1bHQgZmFsbGJhY2tcblxuICAgICAgaWYoY29uZmlnLmZ1bmN0aW9ucykge1xuICAgICAgICBmb3IoY29uc3QgW19mdW5jdGlvbk5hbWUsIGZ1bmN0aW9uQ29uZmlnXSBvZiBPYmplY3QuZW50cmllcyhjb25maWcuZnVuY3Rpb25zKSkge1xuICAgICAgICAgIGlmKGZ1bmN0aW9uQ29uZmlnLmV2ZW50cykge1xuICAgICAgICAgICAgZm9yKGNvbnN0IGV2ZW50IG9mIGZ1bmN0aW9uQ29uZmlnLmV2ZW50cykge1xuICAgICAgICAgICAgICBpZihldmVudD8uaHR0cD8ucGF0aCkge1xuICAgICAgICAgICAgICAgIGdyYXBocWxQYXRoID0gZXZlbnQuaHR0cC5wYXRoO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmKGdyYXBocWxQYXRoICE9PSAnL2dyYXBocWwnKSB7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gU2V0IHVwIEdyYXBoUUwgZW5kcG9pbnQgd2l0aCBlbmhhbmNlZCBjb25zb2xlLmxvZyBjYXB0dXJlXG4gICAgICBhcHAudXNlKGdyYXBocWxQYXRoLCBhc3luYyAocmVxLCByZXMpID0+IHtcbiAgICAgICAgLy8gR3JhcGhRTCBEZWJ1ZyBMb2dnaW5nXG4gICAgICAgIGlmKGRlYnVnICYmIHJlcS5ib2R5ICYmIHJlcS5ib2R5LnF1ZXJ5KSB7XG4gICAgICAgICAgbG9nKCfwn5SNIEdyYXBoUUwgRGVidWcgTW9kZTogQW5hbHl6aW5nIHJlcXVlc3QuLi4nLCAnaW5mbycsIGZhbHNlKTtcbiAgICAgICAgICBsb2coYPCfk50gR3JhcGhRTCBRdWVyeTogJHtyZXEuYm9keS5xdWVyeX1gLCAnaW5mbycsIGZhbHNlKTtcbiAgICAgICAgICBpZihyZXEuYm9keS52YXJpYWJsZXMpIHtcbiAgICAgICAgICAgIGxvZyhg8J+TiiBHcmFwaFFMIFZhcmlhYmxlczogJHtKU09OLnN0cmluZ2lmeShyZXEuYm9keS52YXJpYWJsZXMsIG51bGwsIDIpfWAsICdpbmZvJywgZmFsc2UpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZihyZXEuYm9keS5vcGVyYXRpb25OYW1lKSB7XG4gICAgICAgICAgICBsb2coYPCfj7fvuI8gIEdyYXBoUUwgT3BlcmF0aW9uOiAke3JlcS5ib2R5Lm9wZXJhdGlvbk5hbWV9YCwgJ2luZm8nLCBmYWxzZSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gRW5oYW5jZWQgY29uc29sZS5sb2cgY2FwdHVyZVxuICAgICAgICBjb25zdCBvcmlnaW5hbENvbnNvbGVMb2cgPSBjb25zb2xlLmxvZztcbiAgICAgICAgY29uc3QgbG9nczogc3RyaW5nW10gPSBbXTtcblxuICAgICAgICBjb25zb2xlLmxvZyA9ICguLi5hcmdzKSA9PiB7XG4gICAgICAgICAgY29uc3QgbG9nTWVzc2FnZSA9IGFyZ3MubWFwKChhcmcpID0+XG4gICAgICAgICAgICAodHlwZW9mIGFyZyA9PT0gJ29iamVjdCcgPyBKU09OLnN0cmluZ2lmeShhcmcsIG51bGwsIDIpIDogU3RyaW5nKGFyZykpXG4gICAgICAgICAgKS5qb2luKCcgJyk7XG4gICAgICAgICAgbG9ncy5wdXNoKGxvZ01lc3NhZ2UpO1xuICAgICAgICAgIG9yaWdpbmFsQ29uc29sZUxvZyhgW0dyYXBoUUxdICR7bG9nTWVzc2FnZX1gKTtcbiAgICAgICAgfTtcblxuICAgICAgICAvLyBDcmVhdGUgY29udGV4dCBmb3IgdGhlIGhhbmRsZXJcbiAgICAgICAgY29uc3QgY29udGV4dCA9IHtcbiAgICAgICAgICBhd3NSZXF1ZXN0SWQ6ICd0ZXN0LXJlcXVlc3QtaWQnLFxuICAgICAgICAgIGZ1bmN0aW9uTmFtZTogJ2dyYXBocWwnLFxuICAgICAgICAgIGZ1bmN0aW9uVmVyc2lvbjogJyRMQVRFU1QnLFxuICAgICAgICAgIGdldFJlbWFpbmluZ1RpbWVJbk1pbGxpczogKCkgPT4gMzAwMDAsXG4gICAgICAgICAgaW52b2tlZEZ1bmN0aW9uQXJuOiAnYXJuOmF3czpsYW1iZGE6dXMtZWFzdC0xOjEyMzQ1Njc4OTAxMjpmdW5jdGlvbjpncmFwaHFsJyxcbiAgICAgICAgICBsb2dHcm91cE5hbWU6ICcvYXdzL2xhbWJkYS9ncmFwaHFsJyxcbiAgICAgICAgICBsb2dTdHJlYW1OYW1lOiAndGVzdC1sb2ctc3RyZWFtJyxcbiAgICAgICAgICByZXEsXG4gICAgICAgICAgcmVzXG4gICAgICAgIH07XG5cbiAgICAgICAgLy8gV3JhcCBoYW5kbGVyIHdpdGggY29uc29sZSBsb2cgY2FwdHVyZVxuICAgICAgICBjb25zdCB3cmFwcGVkSGFuZGxlciA9IGNhcHR1cmVDb25zb2xlTG9ncyhncmFwaHFsSGFuZGxlciwgcXVpZXQpO1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgLy8gQ2FsbCB0aGUgaGFuZGxlciB3aXRoIEdyYXBoUUwgcGFyYW1ldGVyc1xuICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHdyYXBwZWRIYW5kbGVyKHtcbiAgICAgICAgICAgIGJvZHk6IEpTT04uc3RyaW5naWZ5KHJlcS5ib2R5KSxcbiAgICAgICAgICAgIGhlYWRlcnM6IHJlcS5oZWFkZXJzLFxuICAgICAgICAgICAgaHR0cE1ldGhvZDogJ1BPU1QnLFxuICAgICAgICAgICAgcGF0aDogZ3JhcGhxbFBhdGgsXG4gICAgICAgICAgICBxdWVyeVN0cmluZ1BhcmFtZXRlcnM6IHt9XG4gICAgICAgICAgfSwgY29udGV4dCk7XG5cbiAgICAgICAgICAvLyBSZXN0b3JlIGNvbnNvbGUubG9nXG4gICAgICAgICAgY29uc29sZS5sb2cgPSBvcmlnaW5hbENvbnNvbGVMb2c7XG5cbiAgICAgICAgICAvLyBIYW5kbGUgdGhlIHJlc3VsdFxuICAgICAgICAgIGlmKHJlc3VsdCAmJiB0eXBlb2YgcmVzdWx0ID09PSAnb2JqZWN0JyAmJiByZXN1bHQuc3RhdHVzQ29kZSkge1xuICAgICAgICAgICAgcmVzLnN0YXR1cyhyZXN1bHQuc3RhdHVzQ29kZSk7XG4gICAgICAgICAgICBpZihyZXN1bHQuaGVhZGVycykge1xuICAgICAgICAgICAgICBPYmplY3QuZW50cmllcyhyZXN1bHQuaGVhZGVycykuZm9yRWFjaCgoW2tleSwgdmFsdWVdKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzLnNldEhlYWRlcihrZXksIFN0cmluZyh2YWx1ZSkpO1xuICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJlcy5zZW5kKHJlc3VsdC5ib2R5KTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmVzLmpzb24ocmVzdWx0KTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcbiAgICAgICAgICAvLyBSZXN0b3JlIGNvbnNvbGUubG9nXG4gICAgICAgICAgY29uc29sZS5sb2cgPSBvcmlnaW5hbENvbnNvbGVMb2c7XG4gICAgICAgICAgbG9nKGBHcmFwaFFMIGhhbmRsZXIgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gLCAnZXJyb3InLCBmYWxzZSk7XG4gICAgICAgICAgcmVzLnN0YXR1cyg1MDApLmpzb24oe2Vycm9yOiBlcnJvci5tZXNzYWdlfSk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuXG4gICAgICBsb2coYEdyYXBoUUwgZW5kcG9pbnQgYXZhaWxhYmxlIGF0IGh0dHA6Ly8ke2hvc3R9OiR7aHR0cFBvcnR9JHtncmFwaHFsUGF0aH1gLCAnaW5mbycsIHF1aWV0KTtcbiAgICB9XG4gIH0gY2F0Y2goZXJyb3IpIHtcbiAgICBsb2coYEVycm9yIHNldHRpbmcgdXAgR3JhcGhRTDogJHtlcnJvci5tZXNzYWdlfWAsICdlcnJvcicsIHF1aWV0KTtcbiAgfVxuXG4gIC8vIEZhbGxiYWNrIGZvciBub24tR3JhcGhRTCByb3V0ZXMgLSBoYW5kbGUgYWxsIHJlbWFpbmluZyByb3V0ZXNcbiAgYXBwLnVzZSgnLycsIGFzeW5jIChyZXEsIHJlcykgPT4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCB1cmwgPSByZXEudXJsIHx8ICcvJztcbiAgICAgIGNvbnN0IG1ldGhvZCA9IHJlcS5tZXRob2QgfHwgJ0dFVCc7XG4gICAgICBjb25zdCBwYXRobmFtZSA9IHJlcS5wYXRoIHx8IHVybC5zcGxpdCgnPycpWzBdOyAvLyBFeHRyYWN0IHBhdGhuYW1lIHdpdGhvdXQgcXVlcnkgc3RyaW5nXG5cbiAgICAgIC8vIEFsd2F5cyBsb2cgcmVxdWVzdHMgKG5vdCBhZmZlY3RlZCBieSBxdWlldCBmbGFnIGZvciBkZWJ1Z2dpbmcpXG4gICAgICBjb25zb2xlLmxvZyhgW1NlcnZlcmxlc3NdICR7bWV0aG9kfSAke3VybH0gKHBhdGhuYW1lOiAke3BhdGhuYW1lfSlgKTtcblxuICAgICAgLy8gRmluZCBtYXRjaGluZyBmdW5jdGlvblxuICAgICAgbGV0IG1hdGNoZWRGdW5jdGlvbiA9IG51bGw7XG4gICAgICBjb25zdCBmdW5jdGlvbnMgPSBjb25maWcuZnVuY3Rpb25zIHx8IHt9O1xuXG4gICAgICBpZihjb25maWcuZnVuY3Rpb25zKSB7XG4gICAgICAgIGNvbnN0IGZ1bmN0aW9uTmFtZXMgPSBPYmplY3Qua2V5cyhmdW5jdGlvbnMpO1xuICAgICAgICBjb25zb2xlLmxvZyhgW1NlcnZlcmxlc3NdIEF2YWlsYWJsZSBmdW5jdGlvbnM6ICR7ZnVuY3Rpb25OYW1lcy5qb2luKCcsICcpfWApO1xuICAgICAgICBjb25zb2xlLmxvZygnW1NlcnZlcmxlc3NdIENvbmZpZyBmdW5jdGlvbnM6JywgSlNPTi5zdHJpbmdpZnkoZnVuY3Rpb25zLCBudWxsLCAyKSk7XG5cbiAgICAgICAgZm9yKGNvbnN0IFtmdW5jdGlvbk5hbWUsIGZ1bmN0aW9uQ29uZmlnXSBvZiBPYmplY3QuZW50cmllcyhmdW5jdGlvbnMpKSB7XG4gICAgICAgICAgaWYoZnVuY3Rpb25Db25maWcuZXZlbnRzKSB7XG4gICAgICAgICAgICBmb3IoY29uc3QgZXZlbnQgb2YgZnVuY3Rpb25Db25maWcuZXZlbnRzKSB7XG4gICAgICAgICAgICAgIGlmKGV2ZW50Lmh0dHApIHtcbiAgICAgICAgICAgICAgICBjb25zdCBldmVudFBhdGggPSBldmVudC5odHRwLnBhdGggfHwgJy8nO1xuICAgICAgICAgICAgICAgIGNvbnN0IGV2ZW50TWV0aG9kID0gKGV2ZW50Lmh0dHAubWV0aG9kIHx8ICdHRVQnKS50b1VwcGVyQ2FzZSgpO1xuICAgICAgICAgICAgICAgIGNvbnN0IHJlcXVlc3RNZXRob2QgPSBtZXRob2QudG9VcHBlckNhc2UoKTtcblxuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGBbU2VydmVybGVzc10gQ2hlY2tpbmcgZnVuY3Rpb24gJHtmdW5jdGlvbk5hbWV9OiBwYXRoPVwiJHtldmVudFBhdGh9XCIsIG1ldGhvZD1cIiR7ZXZlbnRNZXRob2R9XCIgYWdhaW5zdCBwYXRobmFtZT1cIiR7cGF0aG5hbWV9XCIsIG1ldGhvZD1cIiR7cmVxdWVzdE1ldGhvZH1cImApO1xuXG4gICAgICAgICAgICAgICAgLy8gSW1wcm92ZWQgcGF0aCBtYXRjaGluZyAtIGNvbXBhcmUgcGF0aG5hbWUgd2l0aG91dCBxdWVyeSBzdHJpbmdcbiAgICAgICAgICAgICAgICAvLyBOb3JtYWxpemUgcGF0aHMgKHJlbW92ZSB0cmFpbGluZyBzbGFzaGVzIGZvciBjb21wYXJpc29uKVxuICAgICAgICAgICAgICAgIGNvbnN0IG5vcm1hbGl6ZWRFdmVudFBhdGggPSBldmVudFBhdGgucmVwbGFjZSgvXFwvJC8sICcnKSB8fCAnLyc7XG4gICAgICAgICAgICAgICAgY29uc3Qgbm9ybWFsaXplZFBhdGhuYW1lID0gcGF0aG5hbWUucmVwbGFjZSgvXFwvJC8sICcnKSB8fCAnLyc7XG5cbiAgICAgICAgICAgICAgICBpZihub3JtYWxpemVkRXZlbnRQYXRoID09PSBub3JtYWxpemVkUGF0aG5hbWUgJiYgZXZlbnRNZXRob2QgPT09IHJlcXVlc3RNZXRob2QpIHtcbiAgICAgICAgICAgICAgICAgIG1hdGNoZWRGdW5jdGlvbiA9IGZ1bmN0aW9uTmFtZTtcbiAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGBbU2VydmVybGVzc10g4pyTIE1hdGNoZWQgZnVuY3Rpb246ICR7bWF0Y2hlZEZ1bmN0aW9ufWApO1xuICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmKG1hdGNoZWRGdW5jdGlvbikge1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zb2xlLmxvZygnW1NlcnZlcmxlc3NdIE5vIGZ1bmN0aW9ucyBmb3VuZCBpbiBjb25maWcnKTtcbiAgICAgIH1cblxuICAgICAgaWYobWF0Y2hlZEZ1bmN0aW9uICYmIGZ1bmN0aW9uc1ttYXRjaGVkRnVuY3Rpb25dKSB7XG4gICAgICAgIC8vIFJlc29sdmUgaGFuZGxlciBwYXRoIHJlbGF0aXZlIHRvIG91dHB1dCBkaXJlY3RvcnlcbiAgICAgICAgY29uc3QgaGFuZGxlclBhdGggPSBmdW5jdGlvbnNbbWF0Y2hlZEZ1bmN0aW9uXS5oYW5kbGVyO1xuICAgICAgICBjb25zb2xlLmxvZyhgW1NlcnZlcmxlc3NdIExvYWRpbmcgaGFuZGxlcjogJHtoYW5kbGVyUGF0aH0gZnJvbSBvdXRwdXREaXI6ICR7b3V0cHV0RGlyfWApO1xuICAgICAgICBjb25zdCBoYW5kbGVyID0gYXdhaXQgbG9hZEhhbmRsZXIoaGFuZGxlclBhdGgsIG91dHB1dERpcik7XG5cbiAgICAgICAgaWYoaGFuZGxlcikge1xuICAgICAgICAgIGNvbnNvbGUubG9nKGBbU2VydmVybGVzc10gSGFuZGxlciBsb2FkZWQgc3VjY2Vzc2Z1bGx5LCB0eXBlOiAke3R5cGVvZiBoYW5kbGVyfWApO1xuICAgICAgICAgIGNvbnN0IHdyYXBwZWRIYW5kbGVyID0gY2FwdHVyZUNvbnNvbGVMb2dzKGhhbmRsZXIsIHF1aWV0KTtcblxuICAgICAgICAgIGNvbnN0IGlzUmF3Qm9keSA9IEJ1ZmZlci5pc0J1ZmZlcihyZXEuYm9keSk7XG4gICAgICAgICAgY29uc3QgZXZlbnQgPSB7XG4gICAgICAgICAgICBib2R5OiBpc1Jhd0JvZHkgPyByZXEuYm9keS50b1N0cmluZygnYmFzZTY0JykgOiByZXEuYm9keSxcbiAgICAgICAgICAgIGhlYWRlcnM6IHJlcS5oZWFkZXJzLFxuICAgICAgICAgICAgaHR0cE1ldGhvZDogbWV0aG9kLFxuICAgICAgICAgICAgaXNCYXNlNjRFbmNvZGVkOiBpc1Jhd0JvZHksXG4gICAgICAgICAgICBwYXRoOiB1cmwsXG4gICAgICAgICAgICBxdWVyeVN0cmluZ1BhcmFtZXRlcnM6IHJlcS5xdWVyeVxuICAgICAgICAgIH07XG5cbiAgICAgICAgICBjb25zdCBjb250ZXh0ID0ge1xuICAgICAgICAgICAgYXdzUmVxdWVzdElkOiAndGVzdC1yZXF1ZXN0LWlkJyxcbiAgICAgICAgICAgIGZ1bmN0aW9uTmFtZTogbWF0Y2hlZEZ1bmN0aW9uLFxuICAgICAgICAgICAgZnVuY3Rpb25WZXJzaW9uOiAnJExBVEVTVCcsXG4gICAgICAgICAgICBnZXRSZW1haW5pbmdUaW1lSW5NaWxsaXM6ICgpID0+IDMwMDAwLFxuICAgICAgICAgICAgaW52b2tlZEZ1bmN0aW9uQXJuOiBgYXJuOmF3czpsYW1iZGE6dXMtZWFzdC0xOjEyMzQ1Njc4OTAxMjpmdW5jdGlvbjoke21hdGNoZWRGdW5jdGlvbn1gLFxuICAgICAgICAgICAgbG9nR3JvdXBOYW1lOiBgL2F3cy9sYW1iZGEvJHttYXRjaGVkRnVuY3Rpb259YCxcbiAgICAgICAgICAgIGxvZ1N0cmVhbU5hbWU6ICd0ZXN0LWxvZy1zdHJlYW0nLFxuICAgICAgICAgICAgbWVtb3J5TGltaXRJbk1COiAnMTI4J1xuICAgICAgICAgIH07XG5cbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ1tTZXJ2ZXJsZXNzXSBDYWxsaW5nIGhhbmRsZXIgd2l0aCBldmVudDonLCBKU09OLnN0cmluZ2lmeShldmVudCwgbnVsbCwgMikpO1xuICAgICAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgd3JhcHBlZEhhbmRsZXIoZXZlbnQsIGNvbnRleHQpO1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ1tTZXJ2ZXJsZXNzXSBIYW5kbGVyIHJldHVybmVkOicsIEpTT04uc3RyaW5naWZ5KHJlc3VsdCwgbnVsbCwgMikpO1xuXG4gICAgICAgICAgICBpZihyZXN1bHQgJiYgdHlwZW9mIHJlc3VsdCA9PT0gJ29iamVjdCcgJiYgcmVzdWx0LnN0YXR1c0NvZGUpIHtcbiAgICAgICAgICAgICAgcmVzLnN0YXR1cyhyZXN1bHQuc3RhdHVzQ29kZSk7XG4gICAgICAgICAgICAgIGlmKHJlc3VsdC5oZWFkZXJzKSB7XG4gICAgICAgICAgICAgICAgT2JqZWN0LmVudHJpZXMocmVzdWx0LmhlYWRlcnMpLmZvckVhY2goKFtrZXksIHZhbHVlXSkgPT4ge1xuICAgICAgICAgICAgICAgICAgcmVzLnNldEhlYWRlcihrZXksIFN0cmluZyh2YWx1ZSkpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHJlcy5zZW5kKHJlc3VsdC5ib2R5KTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIHJlcy5qc29uKHJlc3VsdCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBjYXRjaChlcnJvcjogYW55KSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdbU2VydmVybGVzc10gSGFuZGxlciBlcnJvcjonLCBlcnJvci5tZXNzYWdlKTtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ1tTZXJ2ZXJsZXNzXSBIYW5kbGVyIGVycm9yIHN0YWNrOicsIGVycm9yLnN0YWNrKTtcbiAgICAgICAgICAgIGxvZyhgSGFuZGxlciBlcnJvcjogJHtlcnJvci5tZXNzYWdlfWAsICdlcnJvcicsIGZhbHNlKTtcbiAgICAgICAgICAgIHJlcy5zdGF0dXMoNTAwKS5qc29uKHtlcnJvcjogZXJyb3IubWVzc2FnZX0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjb25zb2xlLmVycm9yKGBbU2VydmVybGVzc10gSGFuZGxlciBub3QgZm91bmQgZm9yIGZ1bmN0aW9uOiAke21hdGNoZWRGdW5jdGlvbn1gKTtcbiAgICAgICAgICBjb25zb2xlLmVycm9yKGBbU2VydmVybGVzc10gSGFuZGxlciBwYXRoOiAke2hhbmRsZXJQYXRofSwgT3V0cHV0IGRpcjogJHtvdXRwdXREaXJ9YCk7XG4gICAgICAgICAgcmVzLnN0YXR1cyg0MDQpLmpzb24oe2Vycm9yOiAnSGFuZGxlciBub3QgZm91bmQnfSk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoYFtTZXJ2ZXJsZXNzXSBGdW5jdGlvbiBub3QgZm91bmQgZm9yIHBhdGhuYW1lOiAke3BhdGhuYW1lfSwgbWV0aG9kOiAke21ldGhvZH1gKTtcbiAgICAgICAgY29uc29sZS5lcnJvcihgW1NlcnZlcmxlc3NdIEF2YWlsYWJsZSBmdW5jdGlvbnM6ICR7Y29uZmlnLmZ1bmN0aW9ucyA/IE9iamVjdC5rZXlzKGZ1bmN0aW9ucykuam9pbignLCAnKSA6ICdub25lJ31gKTtcbiAgICAgICAgcmVzLnN0YXR1cyg0MDQpLmpzb24oe2Vycm9yOiAnRnVuY3Rpb24gbm90IGZvdW5kJ30pO1xuICAgICAgfVxuICAgIH0gY2F0Y2goZXJyb3IpIHtcbiAgICAgIGxvZyhgUm91dGUgaGFuZGxpbmcgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gLCAnZXJyb3InLCBmYWxzZSk7XG4gICAgICByZXMuc3RhdHVzKDUwMCkuanNvbih7ZXJyb3I6IGVycm9yLm1lc3NhZ2V9KTtcbiAgICB9XG4gIH0pO1xuXG4gIHJldHVybiBhcHA7XG59O1xuXG5jb25zdCBjcmVhdGVXZWJTb2NrZXRTZXJ2ZXIgPSAoXG4gIGNvbmZpZzogU2VydmVybGVzc0NvbmZpZyxcbiAgb3V0cHV0RGlyOiBzdHJpbmcsXG4gIHdzUG9ydDogbnVtYmVyLFxuICBjb25uZWN0aW9uUmVnaXN0cnk6IE1hcDxzdHJpbmcsIFdlYlNvY2tldENsaWVudExpa2U+LFxuICBxdWlldDogYm9vbGVhbixcbiAgZGVidWc6IGJvb2xlYW5cbikgPT4ge1xuICBjb25zdCB3c3MgPSBuZXcgV2ViU29ja2V0U2VydmVyKHtwb3J0OiB3c1BvcnR9KTtcblxuICBjb25zdCBnZXRXZWJTb2NrZXRGdW5jdGlvbkJ5Um91dGUgPSAocm91dGVLZXk6IHN0cmluZyk6IHN0cmluZyB8IG51bGwgPT4ge1xuICAgIGNvbnN0IGZ1bmN0aW9ucyA9IGNvbmZpZy5mdW5jdGlvbnMgfHwge307XG4gICAgbGV0IGRlZmF1bHRGdW5jdGlvbjogc3RyaW5nIHwgbnVsbCA9IG51bGw7XG5cbiAgICBmb3IoY29uc3QgW2Z1bmN0aW9uTmFtZSwgZnVuY3Rpb25Db25maWddIG9mIE9iamVjdC5lbnRyaWVzKGZ1bmN0aW9ucykpIHtcbiAgICAgIGlmKGZ1bmN0aW9uQ29uZmlnLmV2ZW50cykge1xuICAgICAgICBmb3IoY29uc3QgZXZlbnQgb2YgZnVuY3Rpb25Db25maWcuZXZlbnRzKSB7XG4gICAgICAgICAgaWYoZXZlbnQud2Vic29ja2V0KSB7XG4gICAgICAgICAgICBjb25zdCByb3V0ZSA9IGV2ZW50LndlYnNvY2tldC5yb3V0ZSB8fCAnJGNvbm5lY3QnO1xuXG4gICAgICAgICAgICBpZihyb3V0ZSA9PT0gcm91dGVLZXkpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIGZ1bmN0aW9uTmFtZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYocm91dGUgPT09ICckZGVmYXVsdCcgJiYgIWRlZmF1bHRGdW5jdGlvbikge1xuICAgICAgICAgICAgICBkZWZhdWx0RnVuY3Rpb24gPSBmdW5jdGlvbk5hbWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIGRlZmF1bHRGdW5jdGlvbjtcbiAgfTtcblxuICB3c3Mub24oJ2Nvbm5lY3Rpb24nLCBhc3luYyAod3M6IFdlYlNvY2tldENsaWVudExpa2UsIHJlcTogYW55KSA9PiB7XG4gICAgbG9nKGBXZWJTb2NrZXQgY29ubmVjdGlvbiBlc3RhYmxpc2hlZDogJHtyZXEudXJsfWAsICdpbmZvJywgZmFsc2UpO1xuICAgIGNvbnN0IGNvbm5lY3Rpb25JZCA9IHJhbmRvbVVVSUQoKS5yZXBsYWNlKC8tL2csICcnKS5zbGljZSgwLCAzMik7XG4gICAgY29ubmVjdGlvblJlZ2lzdHJ5LnNldChjb25uZWN0aW9uSWQsIHdzKTtcbiAgICBjb25zdCByZXF1ZXN0VXJsID0gbmV3IFVSTChyZXEudXJsIHx8ICcvJywgYHdzOi8vbG9jYWxob3N0OiR7d3NQb3J0fWApO1xuICAgIGNvbnN0IHF1ZXJ5U3RyaW5nUGFyYW1ldGVycyA9IE9iamVjdC5mcm9tRW50cmllcyhyZXF1ZXN0VXJsLnNlYXJjaFBhcmFtcy5lbnRyaWVzKCkpO1xuICAgIGNvbnN0IG11bHRpVmFsdWVRdWVyeVN0cmluZ1BhcmFtZXRlcnMgPSBPYmplY3QuZnJvbUVudHJpZXMoXG4gICAgICBBcnJheS5mcm9tKG5ldyBTZXQoQXJyYXkuZnJvbShyZXF1ZXN0VXJsLnNlYXJjaFBhcmFtcy5rZXlzKCkpKSlcbiAgICAgICAgLm1hcCgoa2V5KSA9PiBba2V5LCByZXF1ZXN0VXJsLnNlYXJjaFBhcmFtcy5nZXRBbGwoa2V5KV0pXG4gICAgKTtcblxuICAgIGNvbnN0IGNvbm5lY3RGdW5jdGlvbiA9IGdldFdlYlNvY2tldEZ1bmN0aW9uQnlSb3V0ZSgnJGNvbm5lY3QnKTtcblxuICAgIGlmKGNvbm5lY3RGdW5jdGlvbiAmJiBjb25maWcuZnVuY3Rpb25zPy5bY29ubmVjdEZ1bmN0aW9uXSkge1xuICAgICAgY29uc3QgY29ubmVjdEhhbmRsZXIgPSBhd2FpdCBsb2FkSGFuZGxlcihjb25maWcuZnVuY3Rpb25zW2Nvbm5lY3RGdW5jdGlvbl0uaGFuZGxlciwgb3V0cHV0RGlyKTtcblxuICAgICAgaWYoY29ubmVjdEhhbmRsZXIpIHtcbiAgICAgICAgY29uc3Qgd3JhcHBlZENvbm5lY3RIYW5kbGVyID0gY2FwdHVyZUNvbnNvbGVMb2dzKGNvbm5lY3RIYW5kbGVyLCBxdWlldCk7XG4gICAgICAgIGF3YWl0IHdyYXBwZWRDb25uZWN0SGFuZGxlcih7XG4gICAgICAgICAgYm9keTogbnVsbCxcbiAgICAgICAgICBtdWx0aVZhbHVlUXVlcnlTdHJpbmdQYXJhbWV0ZXJzLFxuICAgICAgICAgIHF1ZXJ5U3RyaW5nUGFyYW1ldGVycyxcbiAgICAgICAgICByZXF1ZXN0Q29udGV4dDoge1xuICAgICAgICAgICAgYXBpR2F0ZXdheToge1xuICAgICAgICAgICAgICBlbmRwb2ludDogYHdzOi8vbG9jYWxob3N0OiR7d3NQb3J0fWBcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBjb25uZWN0aW9uSWQsXG4gICAgICAgICAgICBldmVudFR5cGU6ICdDT05ORUNUJyxcbiAgICAgICAgICAgIHJvdXRlS2V5OiAnJGNvbm5lY3QnXG4gICAgICAgICAgfVxuICAgICAgICB9LCB7XG4gICAgICAgICAgYXdzUmVxdWVzdElkOiAndGVzdC1yZXF1ZXN0LWlkJyxcbiAgICAgICAgICBmdW5jdGlvbk5hbWU6IGNvbm5lY3RGdW5jdGlvbixcbiAgICAgICAgICBmdW5jdGlvblZlcnNpb246ICckTEFURVNUJyxcbiAgICAgICAgICBnZXRSZW1haW5pbmdUaW1lSW5NaWxsaXM6ICgpID0+IDMwMDAwLFxuICAgICAgICAgIGludm9rZWRGdW5jdGlvbkFybjogYGFybjphd3M6bGFtYmRhOnVzLWVhc3QtMToxMjM0NTY3ODkwMTI6ZnVuY3Rpb246JHtjb25uZWN0RnVuY3Rpb259YCxcbiAgICAgICAgICBsb2dHcm91cE5hbWU6IGAvYXdzL2xhbWJkYS8ke2Nvbm5lY3RGdW5jdGlvbn1gLFxuICAgICAgICAgIGxvZ1N0cmVhbU5hbWU6ICd0ZXN0LWxvZy1zdHJlYW0nLFxuICAgICAgICAgIG1lbW9yeUxpbWl0SW5NQjogJzEyOCdcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgd3Mub24oJ21lc3NhZ2UnLCBhc3luYyAobWVzc2FnZTogYW55KSA9PiB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBkYXRhID0gSlNPTi5wYXJzZShtZXNzYWdlLnRvU3RyaW5nKCkpO1xuXG4gICAgICAgIC8vIEZpbmQgbWF0Y2hpbmcgV2ViU29ja2V0IGZ1bmN0aW9uXG4gICAgICAgIGNvbnN0IGZ1bmN0aW9ucyA9IGNvbmZpZy5mdW5jdGlvbnMgfHwge307XG4gICAgICAgIGNvbnN0IG1hdGNoZWRGdW5jdGlvbiA9IGdldFdlYlNvY2tldEZ1bmN0aW9uQnlSb3V0ZShTdHJpbmcoZGF0YS5hY3Rpb24gfHwgJyRkZWZhdWx0JykpO1xuXG4gICAgICAgIGlmKG1hdGNoZWRGdW5jdGlvbiAmJiBmdW5jdGlvbnNbbWF0Y2hlZEZ1bmN0aW9uXSkge1xuICAgICAgICAgIGNvbnN0IGhhbmRsZXIgPSBhd2FpdCBsb2FkSGFuZGxlcihmdW5jdGlvbnNbbWF0Y2hlZEZ1bmN0aW9uXS5oYW5kbGVyLCBvdXRwdXREaXIpO1xuXG4gICAgICAgICAgaWYoaGFuZGxlcikge1xuICAgICAgICAgICAgLy8gV3JhcCBoYW5kbGVyIHdpdGggY29uc29sZSBsb2cgY2FwdHVyZVxuICAgICAgICAgICAgY29uc3Qgd3JhcHBlZEhhbmRsZXIgPSBjYXB0dXJlQ29uc29sZUxvZ3MoaGFuZGxlciwgcXVpZXQpO1xuICAgICAgICAgICAgY29uc3QgZXZlbnQgPSB7XG4gICAgICAgICAgICAgIGJvZHk6IG1lc3NhZ2UudG9TdHJpbmcoKSxcbiAgICAgICAgICAgICAgcmVxdWVzdENvbnRleHQ6IHtcbiAgICAgICAgICAgICAgICBhcGlHYXRld2F5OiB7XG4gICAgICAgICAgICAgICAgICBlbmRwb2ludDogYHdzOi8vbG9jYWxob3N0OiR7d3NQb3J0fWBcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIGNvbm5lY3Rpb25JZCxcbiAgICAgICAgICAgICAgICByb3V0ZUtleTogZGF0YS5hY3Rpb24gfHwgJyRkZWZhdWx0J1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICBjb25zdCBjb250ZXh0ID0ge1xuICAgICAgICAgICAgICBhd3NSZXF1ZXN0SWQ6ICd0ZXN0LXJlcXVlc3QtaWQnLFxuICAgICAgICAgICAgICBmdW5jdGlvbk5hbWU6IG1hdGNoZWRGdW5jdGlvbixcbiAgICAgICAgICAgICAgZnVuY3Rpb25WZXJzaW9uOiAnJExBVEVTVCcsXG4gICAgICAgICAgICAgIGdldFJlbWFpbmluZ1RpbWVJbk1pbGxpczogKCkgPT4gMzAwMDAsXG4gICAgICAgICAgICAgIGludm9rZWRGdW5jdGlvbkFybjogYGFybjphd3M6bGFtYmRhOnVzLWVhc3QtMToxMjM0NTY3ODkwMTI6ZnVuY3Rpb246JHttYXRjaGVkRnVuY3Rpb259YCxcbiAgICAgICAgICAgICAgbG9nR3JvdXBOYW1lOiBgL2F3cy9sYW1iZGEvJHttYXRjaGVkRnVuY3Rpb259YCxcbiAgICAgICAgICAgICAgbG9nU3RyZWFtTmFtZTogJ3Rlc3QtbG9nLXN0cmVhbScsXG4gICAgICAgICAgICAgIG1lbW9yeUxpbWl0SW5NQjogJzEyOCdcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHdyYXBwZWRIYW5kbGVyKGV2ZW50LCBjb250ZXh0KTtcblxuICAgICAgICAgICAgLy8gSGFuZGxlIExhbWJkYSByZXNwb25zZSBmb3JtYXQgZm9yIFdlYlNvY2tldFxuICAgICAgICAgICAgaWYocmVzdWx0ICYmIHR5cGVvZiByZXN1bHQgPT09ICdvYmplY3QnICYmIHJlc3VsdC5zdGF0dXNDb2RlKSB7XG4gICAgICAgICAgICAgIC8vIE9ubHkgc2VuZCBhIGZyYW1lIHdoZW4gdGhlIGhhbmRsZXIgcmV0dXJuZWQgYSByZWFsIGJvZHkuXG4gICAgICAgICAgICAgIGNvbnN0IGJvZHkgPSB0eXBlb2YgcmVzdWx0LmJvZHkgPT09ICdzdHJpbmcnID8gcmVzdWx0LmJvZHkgOiAnJztcbiAgICAgICAgICAgICAgaWYoYm9keSkge1xuICAgICAgICAgICAgICAgIHdzLnNlbmQoYm9keSk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSBpZihyZXN1bHQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAvLyBUaGlzIGlzIGEgZGlyZWN0IHJlc3BvbnNlLCBzdHJpbmdpZnkgaXQuXG4gICAgICAgICAgICAgIHdzLnNlbmQoSlNPTi5zdHJpbmdpZnkocmVzdWx0KSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHdzLnNlbmQoSlNPTi5zdHJpbmdpZnkoe2Vycm9yOiAnSGFuZGxlciBub3QgZm91bmQnfSkpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB3cy5zZW5kKEpTT04uc3RyaW5naWZ5KHtlcnJvcjogJ1dlYlNvY2tldCBmdW5jdGlvbiBub3QgZm91bmQnfSkpO1xuICAgICAgICB9XG4gICAgICB9IGNhdGNoKGVycm9yKSB7XG4gICAgICAgIGxvZyhgV2ViU29ja2V0IGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCwgJ2Vycm9yJywgZmFsc2UpO1xuICAgICAgICB3cy5zZW5kKEpTT04uc3RyaW5naWZ5KHtlcnJvcjogZXJyb3IubWVzc2FnZX0pKTtcbiAgICAgIH1cbiAgICB9KTtcblxuICAgIHdzLm9uKCdjbG9zZScsICgpID0+IHtcbiAgICAgIGNvbm5lY3Rpb25SZWdpc3RyeS5kZWxldGUoY29ubmVjdGlvbklkKTtcbiAgICAgIGNvbnN0IGRpc2Nvbm5lY3RGdW5jdGlvbiA9IGdldFdlYlNvY2tldEZ1bmN0aW9uQnlSb3V0ZSgnJGRpc2Nvbm5lY3QnKTtcblxuICAgICAgaWYoZGlzY29ubmVjdEZ1bmN0aW9uICYmIGNvbmZpZy5mdW5jdGlvbnM/LltkaXNjb25uZWN0RnVuY3Rpb25dKSB7XG4gICAgICAgIHZvaWQgbG9hZEhhbmRsZXIoY29uZmlnLmZ1bmN0aW9uc1tkaXNjb25uZWN0RnVuY3Rpb25dLmhhbmRsZXIsIG91dHB1dERpcilcbiAgICAgICAgICAudGhlbigoZGlzY29ubmVjdEhhbmRsZXIpID0+IHtcbiAgICAgICAgICAgIGlmKCFkaXNjb25uZWN0SGFuZGxlcikge1xuICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IHdyYXBwZWREaXNjb25uZWN0SGFuZGxlciA9IGNhcHR1cmVDb25zb2xlTG9ncyhkaXNjb25uZWN0SGFuZGxlciwgcXVpZXQpO1xuICAgICAgICAgICAgcmV0dXJuIHdyYXBwZWREaXNjb25uZWN0SGFuZGxlcih7XG4gICAgICAgICAgICAgIGJvZHk6IG51bGwsXG4gICAgICAgICAgICAgIHJlcXVlc3RDb250ZXh0OiB7XG4gICAgICAgICAgICAgICAgYXBpR2F0ZXdheToge1xuICAgICAgICAgICAgICAgICAgZW5kcG9pbnQ6IGB3czovL2xvY2FsaG9zdDoke3dzUG9ydH1gXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBjb25uZWN0aW9uSWQsXG4gICAgICAgICAgICAgICAgZXZlbnRUeXBlOiAnRElTQ09OTkVDVCcsXG4gICAgICAgICAgICAgICAgcm91dGVLZXk6ICckZGlzY29ubmVjdCdcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSwge1xuICAgICAgICAgICAgICBhd3NSZXF1ZXN0SWQ6ICd0ZXN0LXJlcXVlc3QtaWQnLFxuICAgICAgICAgICAgICBmdW5jdGlvbk5hbWU6IGRpc2Nvbm5lY3RGdW5jdGlvbixcbiAgICAgICAgICAgICAgZnVuY3Rpb25WZXJzaW9uOiAnJExBVEVTVCcsXG4gICAgICAgICAgICAgIGdldFJlbWFpbmluZ1RpbWVJbk1pbGxpczogKCkgPT4gMzAwMDAsXG4gICAgICAgICAgICAgIGludm9rZWRGdW5jdGlvbkFybjogYGFybjphd3M6bGFtYmRhOnVzLWVhc3QtMToxMjM0NTY3ODkwMTI6ZnVuY3Rpb246JHtkaXNjb25uZWN0RnVuY3Rpb259YCxcbiAgICAgICAgICAgICAgbG9nR3JvdXBOYW1lOiBgL2F3cy9sYW1iZGEvJHtkaXNjb25uZWN0RnVuY3Rpb259YCxcbiAgICAgICAgICAgICAgbG9nU3RyZWFtTmFtZTogJ3Rlc3QtbG9nLXN0cmVhbScsXG4gICAgICAgICAgICAgIG1lbW9yeUxpbWl0SW5NQjogJzEyOCdcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH0pXG4gICAgICAgICAgLmNhdGNoKChlcnJvcjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgIGxvZyhgV2ViU29ja2V0IGRpc2Nvbm5lY3QgaGFuZGxlciBlcnJvcjogJHtlcnJvci5tZXNzYWdlfWAsICdlcnJvcicsIGZhbHNlKTtcbiAgICAgICAgICB9KTtcbiAgICAgIH1cblxuICAgICAgbG9nKCdXZWJTb2NrZXQgY29ubmVjdGlvbiBjbG9zZWQnLCAnaW5mbycsIGZhbHNlKTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgcmV0dXJuIHdzcztcbn07XG5cbmNvbnN0IGxvYWRFbnZGaWxlID0gKGVudlBhdGg6IHN0cmluZyk6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPT4ge1xuICBjb25zdCBlbnZWYXJzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG5cbiAgaWYoIWV4aXN0c1N5bmMoZW52UGF0aCkpIHtcbiAgICByZXR1cm4gZW52VmFycztcbiAgfVxuXG4gIHRyeSB7XG4gICAgY29uc3QgZW52Q29udGVudCA9IHJlYWRGaWxlU3luYyhlbnZQYXRoLCAndXRmOCcpO1xuICAgIGNvbnN0IGxpbmVzID0gZW52Q29udGVudC5zcGxpdCgnXFxuJyk7XG5cbiAgICBmb3IoY29uc3QgbGluZSBvZiBsaW5lcykge1xuICAgICAgY29uc3QgdHJpbW1lZExpbmUgPSBsaW5lLnRyaW0oKTtcblxuICAgICAgLy8gU2tpcCBlbXB0eSBsaW5lcyBhbmQgY29tbWVudHNcbiAgICAgIGlmKCF0cmltbWVkTGluZSB8fCB0cmltbWVkTGluZS5zdGFydHNXaXRoKCcjJykpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIC8vIFBhcnNlIEtFWT12YWx1ZSBmb3JtYXRcbiAgICAgIGNvbnN0IGVxdWFsSW5kZXggPSB0cmltbWVkTGluZS5pbmRleE9mKCc9Jyk7XG4gICAgICBpZihlcXVhbEluZGV4ID4gMCkge1xuICAgICAgICBjb25zdCBrZXkgPSB0cmltbWVkTGluZS5zdWJzdHJpbmcoMCwgZXF1YWxJbmRleCkudHJpbSgpO1xuICAgICAgICBjb25zdCB2YWx1ZSA9IHRyaW1tZWRMaW5lLnN1YnN0cmluZyhlcXVhbEluZGV4ICsgMSkudHJpbSgpO1xuXG4gICAgICAgIC8vIFJlbW92ZSBxdW90ZXMgaWYgcHJlc2VudFxuICAgICAgICBjb25zdCBjbGVhblZhbHVlID0gdmFsdWUucmVwbGFjZSgvXltcIiddfFtcIiddJC9nLCAnJyk7XG5cbiAgICAgICAgaWYoa2V5KSB7XG4gICAgICAgICAgZW52VmFyc1trZXldID0gY2xlYW5WYWx1ZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfSBjYXRjaChlcnJvcikge1xuICAgIGxvZyhgV2FybmluZzogQ291bGQgbm90IGxvYWQgLmVudiBmaWxlIGF0ICR7ZW52UGF0aH06ICR7ZXJyb3IubWVzc2FnZX1gLCAnd2FybicsIGZhbHNlKTtcbiAgfVxuXG4gIHJldHVybiBlbnZWYXJzO1xufTtcblxuZXhwb3J0IGNvbnN0IHNlcnZlcmxlc3NEZXYgPSBhc3luYyAoXG4gIGNtZDogU2VydmVybGVzc0Rldk9wdGlvbnMsXG4gIGNhbGxiYWNrOiBTZXJ2ZXJsZXNzRGV2Q2FsbGJhY2sgPSAoKSA9PiAoe30pXG4pOiBQcm9taXNlPG51bWJlcj4gPT4ge1xuICBjb25zdCB7XG4gICAgY2xpTmFtZSA9ICdMZXgnLFxuICAgIGNvbmZpZyxcbiAgICBkZWJ1ZyA9IGZhbHNlLFxuICAgIGhvc3Q6IGNsaUhvc3QsXG4gICAgaHR0cFBvcnQ6IGNsaUh0dHBQb3J0LFxuICAgIGh0dHBzUG9ydDogY2xpSHR0cHNQb3J0LFxuICAgIHF1aWV0ID0gZmFsc2UsXG4gICAgcmVtb3ZlID0gZmFsc2UsXG4gICAgdGVzdCA9IGZhbHNlLFxuICAgIHVzZVB1YmxpY0lwLFxuICAgIHZhcmlhYmxlcyxcbiAgICB3c1BvcnQ6IGNsaVdzUG9ydFxuICB9ID0gY21kO1xuXG4gIGNvbnN0IHNwaW5uZXIgPSBjcmVhdGVTcGlubmVyKHF1aWV0KTtcblxuICBsb2coYCR7Y2xpTmFtZX0gc3RhcnRpbmcgc2VydmVybGVzcyBkZXZlbG9wbWVudCBzZXJ2ZXIuLi5gLCAnaW5mbycsIHF1aWV0KTtcblxuICBhd2FpdCBMZXhDb25maWcucGFyc2VDb25maWcoY21kKTtcblxuICBjb25zdCB7b3V0cHV0RnVsbFBhdGh9ID0gTGV4Q29uZmlnLmNvbmZpZztcblxuICAvLyBMb2FkIGVudmlyb25tZW50IHZhcmlhYmxlcyBmcm9tIC5lbnYgZmlsZXNcbiAgY29uc3QgZW52UGF0aHMgPSBbXG4gICAgcGF0aFJlc29sdmUocHJvY2Vzcy5jd2QoKSwgJy5lbnYnKSxcbiAgICBwYXRoUmVzb2x2ZShwcm9jZXNzLmN3ZCgpLCAnLmVudi5sb2NhbCcpLFxuICAgIHBhdGhSZXNvbHZlKHByb2Nlc3MuY3dkKCksICcuZW52LmRldmVsb3BtZW50JylcbiAgXTtcblxuICBsZXQgZW52VmFyczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHt9O1xuXG4gIC8vIExvYWQgZnJvbSAuZW52IGZpbGVzIGluIG9yZGVyIChsYXRlciBmaWxlcyBvdmVycmlkZSBlYXJsaWVyIG9uZXMpXG4gIGZvcihjb25zdCBlbnZQYXRoIG9mIGVudlBhdGhzKSB7XG4gICAgY29uc3QgZmlsZUVudlZhcnMgPSBsb2FkRW52RmlsZShlbnZQYXRoKTtcbiAgICBpZihPYmplY3Qua2V5cyhmaWxlRW52VmFycykubGVuZ3RoID4gMCkge1xuICAgICAgbG9nKGBMb2FkZWQgZW52aXJvbm1lbnQgdmFyaWFibGVzIGZyb206ICR7ZW52UGF0aH1gLCAnaW5mbycsIHF1aWV0KTtcbiAgICB9XG4gICAgZW52VmFycyA9IHsuLi5lbnZWYXJzLCAuLi5maWxlRW52VmFyc307XG4gIH1cblxuICAvLyBTdGFydCB3aXRoIGRlZmF1bHQgTk9ERV9FTlYgYW5kIGxvYWRlZCAuZW52IHZhcmlhYmxlc1xuICBsZXQgdmFyaWFibGVzT2JqOiBvYmplY3QgPSB7Tk9ERV9FTlY6ICdkZXZlbG9wbWVudCcsIC4uLmVudlZhcnN9O1xuXG4gIC8vIE92ZXJyaWRlIHdpdGggY29tbWFuZCBsaW5lIHZhcmlhYmxlcyBpZiBwcm92aWRlZFxuICBpZih2YXJpYWJsZXMpIHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgY2xpVmFycyA9IEpTT04ucGFyc2UodmFyaWFibGVzKTtcbiAgICAgIHZhcmlhYmxlc09iaiA9IHsuLi52YXJpYWJsZXNPYmosIC4uLmNsaVZhcnN9O1xuICAgIH0gY2F0Y2goX2Vycm9yKSB7XG4gICAgICBsb2coYFxcbiR7Y2xpTmFtZX0gRXJyb3I6IEVudmlyb25tZW50IHZhcmlhYmxlcyBvcHRpb24gaXMgbm90IGEgdmFsaWQgSlNPTiBvYmplY3QuYCwgJ2Vycm9yJywgcXVpZXQpO1xuICAgICAgY2FsbGJhY2soMSk7XG4gICAgICByZXR1cm4gMTtcbiAgICB9XG4gIH1cblxuICBwcm9jZXNzLmVudiA9IHsuLi5wcm9jZXNzLmVudiwgLi4udmFyaWFibGVzT2JqfTtcblxuICAvLyBJZiBpbiB0ZXN0IG1vZGUsIGV4aXQgZWFybHkgYWZ0ZXIgbG9hZGluZyBlbnZpcm9ubWVudCB2YXJpYWJsZXNcbiAgaWYodGVzdCkge1xuICAgIGxvZygnVGVzdCBtb2RlOiBFbnZpcm9ubWVudCB2YXJpYWJsZXMgbG9hZGVkLCBleGl0aW5nJywgJ2luZm8nLCBxdWlldCk7XG4gICAgY2FsbGJhY2soMCk7XG4gICAgcmV0dXJuIDA7XG4gIH1cblxuICBpZihyZW1vdmUpIHtcbiAgICBzcGlubmVyLnN0YXJ0KCdDbGVhbmluZyBvdXRwdXQgZGlyZWN0b3J5Li4uJyk7XG4gICAgYXdhaXQgcmVtb3ZlRmlsZXMob3V0cHV0RnVsbFBhdGggfHwgJycpO1xuICAgIHNwaW5uZXIuc3VjY2VlZCgnU3VjY2Vzc2Z1bGx5IGNsZWFuZWQgb3V0cHV0IGRpcmVjdG9yeSEnKTtcbiAgfVxuXG4gIC8vIExvYWQgc2VydmVybGVzcyBjb25maWd1cmF0aW9uXG4gIGxldCBzZXJ2ZXJsZXNzQ29uZmlnOiBTZXJ2ZXJsZXNzQ29uZmlnID0ge307XG5cbiAgdHJ5IHtcbiAgICAvLyBVc2UgZ2V0UGFja2FnZURpciB0byBoYW5kbGUgbnBtIHdvcmtzcGFjZXMgY29ycmVjdGx5XG4gICAgY29uc3QgcGFja2FnZURpciA9IGdldFBhY2thZ2VEaXIoKTtcblxuICAgIC8vIFRyeSBtdWx0aXBsZSBjb25maWcgZmlsZSBmb3JtYXRzXG4gICAgY29uc3QgY29uZmlnRm9ybWF0cyA9IGNvbmZpZyA/IFtjb25maWddIDogW1xuICAgICAgcGF0aFJlc29sdmUocGFja2FnZURpciwgJ2xleC5jb25maWcubWpzJyksXG4gICAgICBwYXRoUmVzb2x2ZShwYWNrYWdlRGlyLCAnbGV4LmNvbmZpZy5qcycpLFxuICAgICAgcGF0aFJlc29sdmUocGFja2FnZURpciwgJ2xleC5jb25maWcuY2pzJyksXG4gICAgICBwYXRoUmVzb2x2ZShwYWNrYWdlRGlyLCAnbGV4LmNvbmZpZy50cycpXG4gICAgXTtcblxuICAgIGxldCBjb25maWdQYXRoOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgICBmb3IoY29uc3QgY2FuZGlkYXRlUGF0aCBvZiBjb25maWdGb3JtYXRzKSB7XG4gICAgICBpZihleGlzdHNTeW5jKGNhbmRpZGF0ZVBhdGgpKSB7XG4gICAgICAgIGNvbmZpZ1BhdGggPSBjYW5kaWRhdGVQYXRoO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZihjb25maWdQYXRoKSB7XG4gICAgICBsb2coYExvYWRpbmcgc2VydmVybGVzcyBjb25maWcgZnJvbTogJHtjb25maWdQYXRofWAsICdpbmZvJywgcXVpZXQpO1xuICAgICAgY29uc3QgY29uZmlnTW9kdWxlID0gYXdhaXQgaW1wb3J0KGNvbmZpZ1BhdGgpO1xuICAgICAgc2VydmVybGVzc0NvbmZpZyA9IGNvbmZpZ01vZHVsZS5kZWZhdWx0Py5zZXJ2ZXJsZXNzIHx8IGNvbmZpZ01vZHVsZS5zZXJ2ZXJsZXNzIHx8IHt9O1xuICAgICAgbG9nKCdTZXJ2ZXJsZXNzIGNvbmZpZyBsb2FkZWQgc3VjY2Vzc2Z1bGx5JywgJ2luZm8nLCBxdWlldCk7XG4gICAgICBjb25zdCBmdW5jdGlvbk5hbWVzID0gT2JqZWN0LmtleXMoc2VydmVybGVzc0NvbmZpZy5mdW5jdGlvbnMgfHwge30pO1xuICAgICAgbG9nKGBMb2FkZWQgZnVuY3Rpb25zOiAke2Z1bmN0aW9uTmFtZXMubGVuZ3RoID4gMCA/IGZ1bmN0aW9uTmFtZXMuam9pbignLCAnKSA6ICdub25lJ31gLCAnaW5mbycsIHF1aWV0KTtcblxuICAgICAgLy8gRGVidWc6IFByaW50IGZ1bGwgY29uZmlnIGlmIGRlYnVnIG1vZGVcbiAgICAgIGlmKGRlYnVnKSB7XG4gICAgICAgIGxvZyhgRnVsbCBzZXJ2ZXJsZXNzIGNvbmZpZzogJHtKU09OLnN0cmluZ2lmeShzZXJ2ZXJsZXNzQ29uZmlnLCBudWxsLCAyKX1gLCAnaW5mbycsIGZhbHNlKTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgbG9nKGBObyBzZXJ2ZXJsZXNzIGNvbmZpZyBmb3VuZC4gVHJpZWQ6ICR7Y29uZmlnRm9ybWF0cy5qb2luKCcsICcpfWAsICd3YXJuJywgcXVpZXQpO1xuICAgIH1cbiAgfSBjYXRjaChlcnJvcikge1xuICAgIGxvZyhgRXJyb3IgbG9hZGluZyBzZXJ2ZXJsZXNzIGNvbmZpZzogJHtlcnJvci5tZXNzYWdlfWAsICdlcnJvcicsIHF1aWV0KTtcbiAgICBpZihkZWJ1Zykge1xuICAgICAgbG9nKGBDb25maWcgZXJyb3Igc3RhY2s6ICR7ZXJyb3Iuc3RhY2t9YCwgJ2Vycm9yJywgZmFsc2UpO1xuICAgIH1cbiAgICAvLyBEb24ndCBleGl0LCBjb250aW51ZSB3aXRoIGVtcHR5IGNvbmZpZ1xuICB9XG5cbiAgLy8gTWVyZ2UgY29uZmlnIHdpdGggY29tbWFuZCBsaW5lIG9wdGlvbnNcbiAgLy8gRGV0ZXJtaW5lIGVmZmVjdGl2ZSBob3N0L3BvcnRzIHdpdGggY29ycmVjdCBwcmVjZWRlbmNlOiBDTEkgPiBjb25maWcgPiBkZWZhdWx0c1xuICBjb25zdCBjb25maWdPZmZsaW5lID0gc2VydmVybGVzc0NvbmZpZy5jdXN0b20/Llsnc2VydmVybGVzcy1vZmZsaW5lJ10gfHwge307XG4gIGNvbnN0IGVmZmVjdGl2ZUhvc3QgPSAoY2xpSG9zdCA/PyBjb25maWdPZmZsaW5lLmhvc3QgPz8gJ2xvY2FsaG9zdCcpIGFzIHN0cmluZztcbiAgY29uc3QgdG9OdW1iZXIgPSAodjogYW55LCBmYWxsYmFjazogbnVtYmVyKTogbnVtYmVyID0+IHtcbiAgICBpZih2ID09PSB1bmRlZmluZWQgfHwgdiA9PT0gbnVsbCB8fCB2ID09PSAnJykge1xuICAgICAgcmV0dXJuIGZhbGxiYWNrO1xuICAgIH1cblxuICAgIGNvbnN0IG4gPSB0eXBlb2YgdiA9PT0gJ251bWJlcicgPyB2IDogcGFyc2VJbnQoU3RyaW5nKHYpKTtcbiAgICByZXR1cm4gTnVtYmVyLmlzRmluaXRlKG4pID8gbiA6IGZhbGxiYWNrO1xuICB9O1xuICBjb25zdCBlZmZlY3RpdmVIdHRwUG9ydCA9IHRvTnVtYmVyKGNsaUh0dHBQb3J0ID8/IGNvbmZpZ09mZmxpbmUuaHR0cFBvcnQsIDMxMDApO1xuICBjb25zdCBlZmZlY3RpdmVIdHRwc1BvcnQgPSB0b051bWJlcihjbGlIdHRwc1BvcnQgPz8gY29uZmlnT2ZmbGluZS5odHRwc1BvcnQsIDMxMDEpO1xuICBjb25zdCBlZmZlY3RpdmVXc1BvcnQgPSB0b051bWJlcihjbGlXc1BvcnQgPz8gY29uZmlnT2ZmbGluZS53c1BvcnQsIDMxMDIpO1xuXG4gIGNvbnN0IGZpbmFsQ29uZmlnOiBTZXJ2ZXJsZXNzQ29uZmlnID0ge1xuICAgIC4uLnNlcnZlcmxlc3NDb25maWcsXG4gICAgY3VzdG9tOiB7XG4gICAgICAnc2VydmVybGVzcy1vZmZsaW5lJzoge1xuICAgICAgICBjb3JzOiBzZXJ2ZXJsZXNzQ29uZmlnLmN1c3RvbT8uWydzZXJ2ZXJsZXNzLW9mZmxpbmUnXT8uY29ycyAhPT0gZmFsc2UsXG4gICAgICAgIGhvc3Q6IGVmZmVjdGl2ZUhvc3QsXG4gICAgICAgIGh0dHBQb3J0OiBlZmZlY3RpdmVIdHRwUG9ydCxcbiAgICAgICAgaHR0cHNQb3J0OiBlZmZlY3RpdmVIdHRwc1BvcnQsXG4gICAgICAgIHdzUG9ydDogZWZmZWN0aXZlV3NQb3J0XG4gICAgICB9XG4gICAgfVxuICB9O1xuXG4gIGNvbnN0IG91dHB1dERpciA9IG91dHB1dEZ1bGxQYXRoIHx8ICdsaWInO1xuICBsb2coYFVzaW5nIG91dHB1dCBkaXJlY3Rvcnk6ICR7b3V0cHV0RGlyfWAsICdpbmZvJywgcXVpZXQpO1xuXG4gIHRyeSB7XG4gICAgc3Bpbm5lci5zdGFydCgnU3RhcnRpbmcgc2VydmVybGVzcyBkZXZlbG9wbWVudCBzZXJ2ZXIuLi4nKTtcblxuICAgIGNvbnN0IGh0dHBQb3J0ID0gZmluYWxDb25maWcuY3VzdG9tIVsnc2VydmVybGVzcy1vZmZsaW5lJ10hLmh0dHBQb3J0ITtcbiAgICBjb25zdCB3c1BvcnQgPSBmaW5hbENvbmZpZy5jdXN0b20hWydzZXJ2ZXJsZXNzLW9mZmxpbmUnXSEud3NQb3J0ITtcbiAgICBjb25zdCBob3N0ID0gZmluYWxDb25maWcuY3VzdG9tIVsnc2VydmVybGVzcy1vZmZsaW5lJ10hLmhvc3QhO1xuICAgIGNvbnN0IGNvbm5lY3Rpb25SZWdpc3RyeSA9IG5ldyBNYXA8c3RyaW5nLCBXZWJTb2NrZXRDbGllbnRMaWtlPigpO1xuXG4gICAgbG9nKGBDcmVhdGluZyBIVFRQIHNlcnZlciBvbiAke2hvc3R9OiR7aHR0cFBvcnR9YCwgJ2luZm8nLCBxdWlldCk7XG4gICAgbG9nKGBDcmVhdGluZyBXZWJTb2NrZXQgc2VydmVyIG9uIHBvcnQgJHt3c1BvcnR9YCwgJ2luZm8nLCBxdWlldCk7XG5cbiAgICAvLyBDcmVhdGUgRXhwcmVzcyBzZXJ2ZXJcbiAgICBjb25zdCBleHByZXNzQXBwID0gYXdhaXQgY3JlYXRlRXhwcmVzc1NlcnZlcihcbiAgICAgIGZpbmFsQ29uZmlnLFxuICAgICAgb3V0cHV0RGlyLFxuICAgICAgaHR0cFBvcnQsXG4gICAgICBob3N0LFxuICAgICAgY29ubmVjdGlvblJlZ2lzdHJ5LFxuICAgICAgcXVpZXQsXG4gICAgICBkZWJ1Z1xuICAgICk7XG5cbiAgICAvLyBDcmVhdGUgV2ViU29ja2V0IHNlcnZlclxuICAgIGNvbnN0IHdzU2VydmVyID0gY3JlYXRlV2ViU29ja2V0U2VydmVyKFxuICAgICAgZmluYWxDb25maWcsXG4gICAgICBvdXRwdXREaXIsXG4gICAgICB3c1BvcnQsXG4gICAgICBjb25uZWN0aW9uUmVnaXN0cnksXG4gICAgICBxdWlldCxcbiAgICAgIGRlYnVnXG4gICAgKTtcblxuICAgIC8vIEhhbmRsZSBzZXJ2ZXIgZXJyb3JzXG4gICAgd3NTZXJ2ZXIub24oJ2Vycm9yJywgKGVycm9yOiBFcnJvcikgPT4ge1xuICAgICAgbG9nKGBXZWJTb2NrZXQgc2VydmVyIGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCwgJ2Vycm9yJywgcXVpZXQpO1xuICAgICAgc3Bpbm5lci5mYWlsKCdGYWlsZWQgdG8gc3RhcnQgV2ViU29ja2V0IHNlcnZlci4nKTtcbiAgICAgIGNhbGxiYWNrKDEpO1xuICAgICAgcmV0dXJuO1xuICAgIH0pO1xuXG4gICAgLy8gU3RhcnQgRXhwcmVzcyBzZXJ2ZXJcbiAgICBjb25zdCBzZXJ2ZXIgPSBleHByZXNzQXBwLmxpc3RlbihodHRwUG9ydCwgaG9zdCwgKCkgPT4ge1xuICAgICAgc3Bpbm5lci5zdWNjZWVkKCdTZXJ2ZXJsZXNzIGRldmVsb3BtZW50IHNlcnZlciBzdGFydGVkLicpO1xuICAgICAgdm9pZCByZXNvbHZlUHVibGljSXBGb3JEaXNwbGF5KHVzZVB1YmxpY0lwKS50aGVuKChwdWJsaWNJcCkgPT4ge1xuICAgICAgICBkaXNwbGF5U2VydmVyU3RhdHVzKFxuICAgICAgICAgIGh0dHBQb3J0LFxuICAgICAgICAgIGZpbmFsQ29uZmlnLmN1c3RvbSFbJ3NlcnZlcmxlc3Mtb2ZmbGluZSddIS5odHRwc1BvcnQhLFxuICAgICAgICAgIHdzUG9ydCxcbiAgICAgICAgICBob3N0LFxuICAgICAgICAgIHF1aWV0LFxuICAgICAgICAgIHB1YmxpY0lwXG4gICAgICAgICk7XG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgIC8vIEhhbmRsZSBFeHByZXNzIHNlcnZlciBlcnJvcnNcbiAgICBzZXJ2ZXIub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XG4gICAgICBsb2coYEV4cHJlc3Mgc2VydmVyIGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCwgJ2Vycm9yJywgcXVpZXQpO1xuICAgICAgc3Bpbm5lci5mYWlsKCdGYWlsZWQgdG8gc3RhcnQgRXhwcmVzcyBzZXJ2ZXIuJyk7XG4gICAgICBjYWxsYmFjaygxKTtcbiAgICAgIHJldHVybjtcbiAgICB9KTtcblxuICAgIC8vIEhhbmRsZSBncmFjZWZ1bCBzaHV0ZG93blxuICAgIGNvbnN0IHNodXRkb3duID0gKCkgPT4ge1xuICAgICAgbG9nKCdcXG5TaHV0dGluZyBkb3duIHNlcnZlcmxlc3MgZGV2ZWxvcG1lbnQgc2VydmVyLi4uJywgJ2luZm8nLCBxdWlldCk7XG4gICAgICBzZXJ2ZXIuY2xvc2UoKTtcbiAgICAgIHdzU2VydmVyLmNsb3NlKCk7XG4gICAgICBjYWxsYmFjaygwKTtcbiAgICB9O1xuXG4gICAgcHJvY2Vzcy5vbignU0lHSU5UJywgc2h1dGRvd24pO1xuICAgIHByb2Nlc3Mub24oJ1NJR1RFUk0nLCBzaHV0ZG93bik7XG5cbiAgICAvLyBLZWVwIHRoZSBwcm9jZXNzIGFsaXZlXG4gICAgcHJvY2Vzcy5zdGRpbi5yZXN1bWUoKTtcblxuICAgIGxvZygnU2VydmVybGVzcyBkZXZlbG9wbWVudCBzZXJ2ZXIgaXMgcnVubmluZy4gUHJlc3MgQ3RybCtDIHRvIHN0b3AuJywgJ2luZm8nLCBxdWlldCk7XG5cbiAgICAvLyBEb24ndCBjYWxsIGNhbGxiYWNrIGhlcmUsIGxldCB0aGUgcHJvY2VzcyBzdGF5IGFsaXZlXG4gICAgcmV0dXJuIDA7XG4gIH0gY2F0Y2goZXJyb3IpIHtcbiAgICBsb2coYFxcbiR7Y2xpTmFtZX0gRXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gLCAnZXJyb3InLCBxdWlldCk7XG4gICAgc3Bpbm5lci5mYWlsKCdGYWlsZWQgdG8gc3RhcnQgc2VydmVybGVzcyBkZXZlbG9wbWVudCBzZXJ2ZXIuJyk7XG4gICAgY2FsbGJhY2soMSk7XG4gICAgcmV0dXJuIDE7XG4gIH1cbn07XG4iXSwibmFtZXMiOlsiYm94ZW4iLCJjaGFsayIsInJhbmRvbVVVSUQiLCJleHByZXNzIiwicmVhZEZpbGVTeW5jIiwiZXhpc3RzU3luYyIsIm1rZGlyU3luYyIsIndyaXRlRmlsZVN5bmMiLCJob21lZGlyIiwicmVzb2x2ZSIsInBhdGhSZXNvbHZlIiwiam9pbiIsImlzQWJzb2x1dGUiLCJwYXRoVG9GaWxlVVJMIiwiV2ViU29ja2V0U2VydmVyIiwiTGV4Q29uZmlnIiwiZ2V0UGFja2FnZURpciIsImNyZWF0ZVNwaW5uZXIiLCJyZW1vdmVGaWxlcyIsImxvZyIsImdldENhY2hlRGlyIiwiY2FjaGVEaXIiLCJyZWN1cnNpdmUiLCJnZXRDYWNoZVBhdGgiLCJyZWFkUHVibGljSXBDYWNoZSIsImNhY2hlUGF0aCIsImNhY2hlRGF0YSIsImNhY2hlIiwiSlNPTiIsInBhcnNlIiwib25lV2Vla01zIiwiRGF0ZSIsIm5vdyIsInRpbWVzdGFtcCIsIndyaXRlUHVibGljSXBDYWNoZSIsImlwIiwic3RyaW5naWZ5IiwiZmV0Y2hQdWJsaWNJcCIsImZvcmNlUmVmcmVzaCIsIlByb21pc2UiLCJjYWNoZWQiLCJmZXRjaCIsInRoZW4iLCJyZXMiLCJ0ZXh0IiwiZGF0YSIsInRyaW0iLCJjYXRjaCIsInVuZGVmaW5lZCIsInJlc29sdmVQdWJsaWNJcEZvckRpc3BsYXkiLCJ0aW1lb3V0TXMiLCJ0aW1lb3V0UHJvbWlzZSIsInNldFRpbWVvdXQiLCJyYWNlIiwiZGlzcGxheVNlcnZlclN0YXR1cyIsImh0dHBQb3J0IiwiaHR0cHNQb3J0Iiwid3NQb3J0IiwiaG9zdCIsInF1aWV0IiwicHVibGljSXAiLCJodHRwVXJsIiwiaHR0cHNVcmwiLCJ3c1VybCIsIndzc1VybCIsInVybExpbmVzIiwiZ3JlZW4iLCJ1bmRlcmxpbmUiLCJzdGF0dXNCb3giLCJjeWFuIiwiYm9sZCIsInllbGxvdyIsImJhY2tncm91bmRDb2xvciIsImJvcmRlckNvbG9yIiwiYm9yZGVyU3R5bGUiLCJtYXJnaW4iLCJwYWRkaW5nIiwiY29uc29sZSIsImxvYWRIYW5kbGVyIiwiaGFuZGxlclBhdGgiLCJvdXRwdXREaXIiLCJoYW5kbGVyUGFydHMiLCJzcGxpdCIsImZpbGVQYXRoIiwiZXhwb3J0TmFtZSIsImxlbmd0aCIsInNsaWNlIiwiaW5jbHVkZXMiLCJlcnJvciIsInBhdGhQYXJ0cyIsImxhc3RQYXJ0IiwiZnVsbFBhdGgiLCJleHRlbnNpb25zIiwicGF0aFdpdGhvdXRFeHQiLCJyZXBsYWNlIiwiZXh0IiwiY2FuZGlkYXRlUGF0aCIsIkVycm9yIiwiaW1wb3J0UGF0aCIsImVuZHNXaXRoIiwiaW1wb3J0VXJsIiwiaHJlZiIsImhhbmRsZXJNb2R1bGUiLCJPYmplY3QiLCJrZXlzIiwiaGFuZGxlciIsImRlZmF1bHQiLCJpbXBvcnRFcnJvciIsIm1lc3NhZ2UiLCJzdGFjayIsImNhcHR1cmVDb25zb2xlTG9ncyIsImV2ZW50IiwiY29udGV4dCIsIm9yaWdpbmFsQ29uc29sZUxvZyIsIm9yaWdpbmFsQ29uc29sZUVycm9yIiwib3JpZ2luYWxDb25zb2xlV2FybiIsIndhcm4iLCJvcmlnaW5hbENvbnNvbGVJbmZvIiwiaW5mbyIsImxvZ3MiLCJhcmdzIiwicHVzaCIsInJlc3VsdCIsImdyYXkiLCJmb3JFYWNoIiwiY3JlYXRlRXhwcmVzc1NlcnZlciIsImNvbmZpZyIsImNvbm5lY3Rpb25SZWdpc3RyeSIsImRlYnVnIiwiYXBwIiwidXNlIiwicmVxIiwibmV4dCIsImhlYWRlciIsIm1ldGhvZCIsInNlbmRTdGF0dXMiLCJyYXciLCJsaW1pdCIsInR5cGUiLCJjdXN0b20iLCJib2R5TGltaXQiLCJTdHJpbmciLCJoZWFkZXJzIiwianNvbiIsInBvc3QiLCJjb25uZWN0aW9uSWQiLCJwYXJhbXMiLCJ3cyIsImdldCIsInN0YXR1cyIsImJvZHkiLCJCdWZmZXIiLCJpc0J1ZmZlciIsInRvU3RyaW5nIiwic2VuZCIsIm9rIiwibG9hZEdyYXBoUUxTY2hlbWEiLCJncmFwaHFsSGFuZGxlciIsImZ1bmN0aW9ucyIsImZ1bmN0aW9uTmFtZSIsImZ1bmN0aW9uQ29uZmlnIiwiZW50cmllcyIsImV2ZW50cyIsImh0dHAiLCJwYXRoIiwiZ3JhcGhxbFBhdGgiLCJfZnVuY3Rpb25OYW1lIiwicXVlcnkiLCJ2YXJpYWJsZXMiLCJvcGVyYXRpb25OYW1lIiwibG9nTWVzc2FnZSIsIm1hcCIsImFyZyIsImF3c1JlcXVlc3RJZCIsImZ1bmN0aW9uVmVyc2lvbiIsImdldFJlbWFpbmluZ1RpbWVJbk1pbGxpcyIsImludm9rZWRGdW5jdGlvbkFybiIsImxvZ0dyb3VwTmFtZSIsImxvZ1N0cmVhbU5hbWUiLCJ3cmFwcGVkSGFuZGxlciIsImh0dHBNZXRob2QiLCJxdWVyeVN0cmluZ1BhcmFtZXRlcnMiLCJzdGF0dXNDb2RlIiwia2V5IiwidmFsdWUiLCJzZXRIZWFkZXIiLCJ1cmwiLCJwYXRobmFtZSIsIm1hdGNoZWRGdW5jdGlvbiIsImZ1bmN0aW9uTmFtZXMiLCJldmVudFBhdGgiLCJldmVudE1ldGhvZCIsInRvVXBwZXJDYXNlIiwicmVxdWVzdE1ldGhvZCIsIm5vcm1hbGl6ZWRFdmVudFBhdGgiLCJub3JtYWxpemVkUGF0aG5hbWUiLCJpc1Jhd0JvZHkiLCJpc0Jhc2U2NEVuY29kZWQiLCJtZW1vcnlMaW1pdEluTUIiLCJjcmVhdGVXZWJTb2NrZXRTZXJ2ZXIiLCJ3c3MiLCJwb3J0IiwiZ2V0V2ViU29ja2V0RnVuY3Rpb25CeVJvdXRlIiwicm91dGVLZXkiLCJkZWZhdWx0RnVuY3Rpb24iLCJ3ZWJzb2NrZXQiLCJyb3V0ZSIsIm9uIiwic2V0IiwicmVxdWVzdFVybCIsIlVSTCIsImZyb21FbnRyaWVzIiwic2VhcmNoUGFyYW1zIiwibXVsdGlWYWx1ZVF1ZXJ5U3RyaW5nUGFyYW1ldGVycyIsIkFycmF5IiwiZnJvbSIsIlNldCIsImdldEFsbCIsImNvbm5lY3RGdW5jdGlvbiIsImNvbm5lY3RIYW5kbGVyIiwid3JhcHBlZENvbm5lY3RIYW5kbGVyIiwicmVxdWVzdENvbnRleHQiLCJhcGlHYXRld2F5IiwiZW5kcG9pbnQiLCJldmVudFR5cGUiLCJhY3Rpb24iLCJkZWxldGUiLCJkaXNjb25uZWN0RnVuY3Rpb24iLCJkaXNjb25uZWN0SGFuZGxlciIsIndyYXBwZWREaXNjb25uZWN0SGFuZGxlciIsImxvYWRFbnZGaWxlIiwiZW52UGF0aCIsImVudlZhcnMiLCJlbnZDb250ZW50IiwibGluZXMiLCJsaW5lIiwidHJpbW1lZExpbmUiLCJzdGFydHNXaXRoIiwiZXF1YWxJbmRleCIsImluZGV4T2YiLCJzdWJzdHJpbmciLCJjbGVhblZhbHVlIiwic2VydmVybGVzc0RldiIsImNtZCIsImNhbGxiYWNrIiwiY2xpTmFtZSIsImNsaUhvc3QiLCJjbGlIdHRwUG9ydCIsImNsaUh0dHBzUG9ydCIsInJlbW92ZSIsInRlc3QiLCJ1c2VQdWJsaWNJcCIsImNsaVdzUG9ydCIsInNwaW5uZXIiLCJwYXJzZUNvbmZpZyIsIm91dHB1dEZ1bGxQYXRoIiwiZW52UGF0aHMiLCJwcm9jZXNzIiwiY3dkIiwiZmlsZUVudlZhcnMiLCJ2YXJpYWJsZXNPYmoiLCJOT0RFX0VOViIsImNsaVZhcnMiLCJfZXJyb3IiLCJlbnYiLCJzdGFydCIsInN1Y2NlZWQiLCJzZXJ2ZXJsZXNzQ29uZmlnIiwicGFja2FnZURpciIsImNvbmZpZ0Zvcm1hdHMiLCJjb25maWdQYXRoIiwiY29uZmlnTW9kdWxlIiwic2VydmVybGVzcyIsImNvbmZpZ09mZmxpbmUiLCJlZmZlY3RpdmVIb3N0IiwidG9OdW1iZXIiLCJ2IiwiZmFsbGJhY2siLCJuIiwicGFyc2VJbnQiLCJOdW1iZXIiLCJpc0Zpbml0ZSIsImVmZmVjdGl2ZUh0dHBQb3J0IiwiZWZmZWN0aXZlSHR0cHNQb3J0IiwiZWZmZWN0aXZlV3NQb3J0IiwiZmluYWxDb25maWciLCJjb3JzIiwiTWFwIiwiZXhwcmVzc0FwcCIsIndzU2VydmVyIiwiZmFpbCIsInNlcnZlciIsImxpc3RlbiIsInNodXRkb3duIiwiY2xvc2UiLCJzdGRpbiIsInJlc3VtZSJdLCJtYXBwaW5ncyI6IkFBQUE7OztDQUdDLEdBQ0QsT0FBT0EsV0FBVyxRQUFRO0FBQzFCLE9BQU9DLFdBQVcsUUFBUTtBQUMxQixTQUFRQyxVQUFVLFFBQU8sU0FBUztBQUNsQyxPQUFPQyxhQUFhLFVBQVU7QUFDOUIsU0FBUUMsWUFBWSxFQUFFQyxVQUFVLEVBQUVDLFNBQVMsRUFBRUMsYUFBYSxRQUFPLEtBQUs7QUFDdEUsU0FBUUMsT0FBTyxRQUFPLEtBQUs7QUFDM0IsU0FBUUMsV0FBV0MsV0FBVyxFQUFFQyxJQUFJLEVBQUVDLFVBQVUsUUFBTyxPQUFPO0FBQzlELFNBQVFDLGFBQWEsUUFBTyxNQUFNO0FBQ2xDLFNBQVFDLGVBQWUsUUFBTyxLQUFLO0FBRW5DLFNBQVFDLFNBQVMsRUFBRUMsYUFBYSxRQUFPLHFCQUFxQjtBQUM1RCxTQUFRQyxhQUFhLEVBQUVDLFdBQVcsUUFBTyxxQkFBcUI7QUFDOUQsU0FBUUMsR0FBRyxRQUFPLHFCQUFxQjtBQTBEdkMsTUFBTUMsY0FBYztJQUNsQixNQUFNQyxXQUFXVixLQUFLSCxXQUFXO0lBQ2pDLElBQUcsQ0FBQ0gsV0FBV2dCLFdBQVc7UUFDeEJmLFVBQVVlLFVBQVU7WUFBQ0MsV0FBVztRQUFJO0lBQ3RDO0lBQ0EsT0FBT0Q7QUFDVDtBQUVBLE1BQU1FLGVBQWUsSUFBY1osS0FBS1MsZUFBZTtBQUV2RCxNQUFNSSxvQkFBb0I7SUFDeEIsTUFBTUMsWUFBWUY7SUFDbEIsSUFBRyxDQUFDbEIsV0FBV29CLFlBQVk7UUFDekIsT0FBTztJQUNUO0lBRUEsSUFBSTtRQUNGLE1BQU1DLFlBQVl0QixhQUFhcUIsV0FBVztRQUMxQyxNQUFNRSxRQUF1QkMsS0FBS0MsS0FBSyxDQUFDSDtRQUV4QyxzQ0FBc0M7UUFDdEMsTUFBTUksWUFBWSxJQUFJLEtBQUssS0FB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
|