UNPKG

vibetunnel

Version:

Terminal sharing server with web interface - supports macOS, Linux, and headless environments

524 lines 57.7 kB
"use strict"; /** * ProcessUtils - Cross-platform process management utilities * * Provides reliable process existence checking across Windows, macOS, and Linux. */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ProcessUtils = void 0; exports.isProcessRunning = isProcessRunning; exports.getProcessInfo = getProcessInfo; exports.killProcess = killProcess; exports.waitForProcessExit = waitForProcessExit; exports.resolveCommand = resolveCommand; exports.getUserShell = getUserShell; const chalk_1 = __importDefault(require("chalk")); const child_process_1 = require("child_process"); const fs = __importStar(require("fs")); const os = __importStar(require("os")); const path = __importStar(require("path")); const logger_js_1 = require("../utils/logger.js"); const logger = (0, logger_js_1.createLogger)('process-utils'); /** * Get the appropriate shell configuration file for a given shell * @param shellPath The path to the shell executable * @returns The path to the shell config file, or null if none found */ function getShellConfigFile(shellPath) { const homeDir = os.homedir(); const shellName = path.basename(shellPath); // Map of shell names to their config files (in order of preference) const shellConfigs = { zsh: ['.zshrc', '.zshenv'], bash: ['.bashrc', '.bash_profile', '.profile'], sh: ['.profile'], ksh: ['.kshrc', '.profile'], fish: ['.config/fish/config.fish'], tcsh: ['.tcshrc', '.cshrc'], csh: ['.cshrc'], dash: ['.profile'], }; // Get config files for this shell const configFiles = shellConfigs[shellName] || []; // Check each config file in order of preference for (const configFile of configFiles) { const fullPath = path.join(homeDir, configFile); if (existsSync(fullPath)) { return fullPath; } } // Fallback to .profile for unknown shells const profilePath = path.join(homeDir, '.profile'); if (existsSync(profilePath)) { return profilePath; } return null; } /** * Safe file existence check */ function existsSync(filePath) { try { fs.accessSync(filePath, fs.constants.F_OK); return true; } catch { return false; } } /** * Check if a process is currently running by PID * Uses platform-appropriate methods for reliable detection */ function isProcessRunning(pid) { if (!pid || pid <= 0) { return false; } try { if (process.platform === 'win32') { // Windows: Use tasklist command return isProcessRunningWindows(pid); } else { // Unix/Linux/macOS: Use kill with signal 0 return isProcessRunningUnix(pid); } } catch (error) { logger.warn(`error checking if process ${pid} is running:`, error); return false; } } /** * Windows-specific process check using tasklist */ function isProcessRunningWindows(pid) { try { logger.debug(`checking windows process ${pid} with tasklist`); const result = (0, child_process_1.spawnSync)('tasklist', ['/FI', `PID eq ${pid}`, '/NH', '/FO', 'CSV'], { encoding: 'utf8', windowsHide: true, timeout: 5000, // 5 second timeout }); // Check if the command succeeded and PID appears in output if (result.status === 0 && result.stdout) { // tasklist outputs CSV format with PID in quotes const exists = result.stdout.includes(`"${pid}"`); logger.debug(`process ${pid} exists: ${exists}`); return exists; } logger.debug(`tasklist command failed with status ${result.status}`); return false; } catch (error) { logger.warn(`windows process check failed for PID ${pid}:`, error); return false; } } /** * Unix-like systems process check using kill signal 0 */ function isProcessRunningUnix(pid) { try { // Send signal 0 to check if process exists // This doesn't actually kill the process, just checks existence process.kill(pid, 0); return true; } catch (error) { // If we get ESRCH, the process doesn't exist // If we get EPERM, the process exists but we don't have permission const err = error; if (err.code === 'EPERM') { // Process exists but we don't have permission to signal it return true; } // ESRCH or other errors mean process doesn't exist return false; } } /** * Get basic process information if available * Returns null if process is not running or info cannot be retrieved */ function getProcessInfo(pid) { if (!isProcessRunning(pid)) { return null; } return { pid, exists: true, }; } /** * Kill a process with platform-appropriate method * Returns true if the kill signal was sent successfully */ function killProcess(pid, signal = 'SIGTERM') { if (!pid || pid <= 0) { return false; } logger.debug(`attempting to kill process ${pid} with signal ${signal}`); try { if (process.platform === 'win32') { // Windows: Use taskkill command for more reliable termination const result = (0, child_process_1.spawnSync)('taskkill', ['/PID', pid.toString(), '/F'], { windowsHide: true, timeout: 5000, }); if (result.status === 0) { logger.log(chalk_1.default.green(`process ${pid} killed successfully`)); return true; } else { logger.debug(`taskkill failed with status ${result.status}`); return false; } } else { // Unix-like: Use built-in process.kill process.kill(pid, signal); logger.log(chalk_1.default.green(`signal ${signal} sent to process ${pid}`)); return true; } } catch (error) { logger.warn(`error killing process ${pid}:`, error); return false; } } /** * Wait for a process to exit with timeout * Returns true if process exited within timeout, false otherwise */ async function waitForProcessExit(pid, timeoutMs = 5000) { const startTime = Date.now(); const checkInterval = 100; // Check every 100ms logger.debug(`waiting for process ${pid} to exit (timeout: ${timeoutMs}ms)`); while (Date.now() - startTime < timeoutMs) { if (!isProcessRunning(pid)) { const elapsed = Date.now() - startTime; logger.log(chalk_1.default.green(`process ${pid} exited after ${elapsed}ms`)); return true; } await new Promise((resolve) => setTimeout(resolve, checkInterval)); } logger.log(chalk_1.default.yellow(`process ${pid} did not exit within ${timeoutMs}ms timeout`)); return false; } /** * Check if this is an interactive shell session */ function isInteractiveShellCommand(cmdName, args) { // Common shells const shells = ['bash', 'zsh', 'sh', 'fish', 'dash', 'ksh', 'tcsh', 'csh']; const isShell = shells.some((shell) => cmdName === shell || cmdName.endsWith(`/${shell}`)); if (!isShell) return false; // Check for interactive flags const interactiveFlags = ['-i', '--interactive', '-l', '--login']; // If no args, it's interactive by default if (args.length === 0) return true; // Check if any args indicate interactive mode return args.some((arg) => interactiveFlags.includes(arg)); } /** * Determine how to spawn a command, checking if it exists in PATH or needs shell execution * Returns the actual command and args to use for spawning */ function resolveCommand(command) { if (command.length === 0) { throw new Error('No command provided'); } const cmdName = command[0]; const cmdArgs = command.slice(1); // Check if command exists in PATH using 'which' (Unix) or 'where' (Windows) const whichCommand = process.platform === 'win32' ? 'where' : 'which'; try { const result = (0, child_process_1.spawnSync)(whichCommand, [cmdName], { encoding: 'utf8', windowsHide: true, timeout: 2000, // 2 second timeout }); if (result.status === 0 && result.stdout && result.stdout.trim()) { // Command found in PATH logger.debug(`Command '${cmdName}' found at: ${result.stdout.trim()}`); // Check if this is an interactive shell command if (isInteractiveShellCommand(cmdName, cmdArgs)) { logger.log(chalk_1.default.cyan(`✓ Starting ${cmdName} as login shell to load configuration files`)); // Add both -i (interactive) and -l/--login flags for proper shell initialization // This ensures shell RC files are sourced and the environment is properly set up // Don't add flags if they're already present const hasInteractiveFlag = cmdArgs.some((arg) => arg === '-i' || arg === '--interactive'); const hasLoginFlag = cmdArgs.some((arg) => arg === '-l' || arg === '--login'); // Build args array const finalArgs = [...cmdArgs]; // For fish shell, use --login and --interactive instead of -l and -i const isFish = cmdName === 'fish' || cmdName.endsWith('/fish'); if (!hasInteractiveFlag) { finalArgs.unshift(isFish ? '--interactive' : '-i'); } if (!hasLoginFlag) { finalArgs.unshift(isFish ? '--login' : '-l'); } return { command: cmdName, args: finalArgs, useShell: false, resolvedFrom: 'path', originalCommand: cmdName, isInteractive: true, }; } return { command: cmdName, args: cmdArgs, useShell: false, resolvedFrom: 'path', originalCommand: cmdName, }; } } catch (error) { logger.debug(`Failed to check command existence for '${cmdName}':`, error); } // Command not found in PATH, likely an alias or shell builtin // Need to run through shell logger.debug(`Command '${cmdName}' not found in PATH, will use shell`); // Determine user's shell const userShell = getUserShell(); // Check if this is trying to execute a command (not an interactive shell session) // If so, use non-interactive mode to ensure shell exits after execution const isCommand = !isInteractiveShellCommand(cmdName, cmdArgs); // Use interactive shell to execute the command // This ensures aliases and shell functions are available if (process.platform === 'win32') { // Windows shells have different syntax if (userShell.includes('bash')) { // Git Bash on Windows: Use Unix-style syntax if (isCommand) { // Non-interactive command execution return { command: userShell, args: ['-c', command.join(' ')], useShell: true, resolvedFrom: 'shell', }; } else { // Interactive shell session return { command: userShell, args: ['-i', '-c', command.join(' ')], useShell: true, resolvedFrom: 'shell', isInteractive: true, }; } } else if (userShell.includes('pwsh') || userShell.includes('powershell')) { // PowerShell: Use -Command for execution // Note: PowerShell aliases work differently than Unix aliases return { command: userShell, args: ['-NoLogo', '-Command', command.join(' ')], useShell: true, resolvedFrom: 'shell', }; } else { // cmd.exe: Use /C to execute and exit // Note: cmd.exe uses 'doskey' for aliases, not traditional aliases return { command: userShell, args: ['/C', command.join(' ')], useShell: true, resolvedFrom: 'shell', }; } } else { // Unix shells: Choose execution mode based on command type if (isCommand) { // Non-interactive command execution: shell will exit after completion // Use interactive mode to ensure aliases and functions are properly expanded const shellConfig = getShellConfigFile(userShell); if (shellConfig) { // Use interactive mode with login shell to ensure aliases are loaded and expanded // The -i flag enables interactive mode, which loads aliases // The -l flag makes it a login shell, ensuring profile/rc files are sourced return { command: userShell, args: ['-i', '-l', '-c', command.join(' ')], useShell: true, resolvedFrom: 'alias', }; } else { // No shell config found, use basic execution return { command: userShell, args: ['-c', command.join(' ')], useShell: true, resolvedFrom: 'shell', }; } } else { // Interactive shell session: use -i and -l for proper initialization return { command: userShell, args: ['-i', '-l', '-c', command.join(' ')], useShell: true, resolvedFrom: 'shell', isInteractive: true, }; } } } /** * Get the user's preferred shell * Falls back to sensible defaults if SHELL env var is not set */ function getUserShell() { // First try SHELL environment variable (most reliable on Unix) if (process.env.SHELL) { return process.env.SHELL; } // Platform-specific defaults if (process.platform === 'win32') { // Check for modern shells first // 1. Check for PowerShell Core (pwsh) - cross-platform version try { const result = (0, child_process_1.spawnSync)('pwsh', ['-Command', 'echo test'], { encoding: 'utf8', windowsHide: true, timeout: 1000, }); if (result.status === 0) { return 'pwsh'; } } catch (_) { // PowerShell Core not available } // 2. Check for Windows PowerShell (older, Windows-only) const powershellPath = path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe'); try { const result = (0, child_process_1.spawnSync)(powershellPath, ['-Command', 'echo test'], { encoding: 'utf8', windowsHide: true, timeout: 1000, }); if (result.status === 0) { return powershellPath; } } catch (_) { // PowerShell not available } // 3. Check for Git Bash if available const gitBashPaths = [ 'C:\\Program Files\\Git\\bin\\bash.exe', 'C:\\Program Files (x86)\\Git\\bin\\bash.exe', path.join(process.env.ProgramFiles || 'C:\\Program Files', 'Git', 'bin', 'bash.exe'), ]; for (const gitBashPath of gitBashPaths) { try { const result = (0, child_process_1.spawnSync)(gitBashPath, ['-c', 'echo test'], { encoding: 'utf8', windowsHide: true, timeout: 1000, }); if (result.status === 0) { return gitBashPath; } } catch (_) { // Git Bash not at this location } } // 4. Fall back to cmd.exe return process.env.ComSpec || 'cmd.exe'; } else { // Unix-like systems // Node.js os.userInfo() includes shell on some platforms try { const userInfo = os.userInfo(); if ('shell' in userInfo && userInfo.shell) { return userInfo.shell; } } catch (_) { // userInfo might fail in some environments } // Check common shell paths in order of preference // Prefer bash over zsh to avoid first-run configuration issues in CI const commonShells = ['/bin/bash', '/usr/bin/bash', '/bin/zsh', '/usr/bin/zsh', '/bin/sh']; for (const shell of commonShells) { try { // Just check if the shell exists and is executable const result = (0, child_process_1.spawnSync)('test', ['-x', shell], { encoding: 'utf8', timeout: 500, }); if (result.status === 0) { return shell; } } catch (_) { // test command failed, try next shell } } // Final fallback - /bin/sh should always exist on Unix return '/bin/sh'; } } // Re-export as object for backwards compatibility exports.ProcessUtils = { isProcessRunning, getProcessInfo, killProcess, waitForProcessExit, resolveCommand, getUserShell, }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvY2Vzcy11dGlscy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zZXJ2ZXIvcHR5L3Byb2Nlc3MtdXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7O0dBSUc7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQW9FSCw0Q0FpQkM7QUF3REQsd0NBU0M7QUFNRCxrQ0ErQkM7QUFNRCxnREFpQkM7QUEwQkQsd0NBc0tDO0FBTUQsb0NBcUdDO0FBM2ZELGtEQUEwQjtBQUMxQixpREFBMEM7QUFDMUMsdUNBQXlCO0FBQ3pCLHVDQUF5QjtBQUN6QiwyQ0FBNkI7QUFDN0Isa0RBQWtEO0FBRWxELE1BQU0sTUFBTSxHQUFHLElBQUEsd0JBQVksRUFBQyxlQUFlLENBQUMsQ0FBQztBQUU3Qzs7OztHQUlHO0FBQ0gsU0FBUyxrQkFBa0IsQ0FBQyxTQUFpQjtJQUMzQyxNQUFNLE9BQU8sR0FBRyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDN0IsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUUzQyxvRUFBb0U7SUFDcEUsTUFBTSxZQUFZLEdBQTZCO1FBQzdDLEdBQUcsRUFBRSxDQUFDLFFBQVEsRUFBRSxTQUFTLENBQUM7UUFDMUIsSUFBSSxFQUFFLENBQUMsU0FBUyxFQUFFLGVBQWUsRUFBRSxVQUFVLENBQUM7UUFDOUMsRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDO1FBQ2hCLEdBQUcsRUFBRSxDQUFDLFFBQVEsRUFBRSxVQUFVLENBQUM7UUFDM0IsSUFBSSxFQUFFLENBQUMsMEJBQTBCLENBQUM7UUFDbEMsSUFBSSxFQUFFLENBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBQztRQUMzQixHQUFHLEVBQUUsQ0FBQyxRQUFRLENBQUM7UUFDZixJQUFJLEVBQUUsQ0FBQyxVQUFVLENBQUM7S0FDbkIsQ0FBQztJQUVGLGtDQUFrQztJQUNsQyxNQUFNLFdBQVcsR0FBRyxZQUFZLENBQUMsU0FBUyxDQUFDLElBQUksRUFBRSxDQUFDO0lBRWxELGdEQUFnRDtJQUNoRCxLQUFLLE1BQU0sVUFBVSxJQUFJLFdBQVcsRUFBRSxDQUFDO1FBQ3JDLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLFVBQVUsQ0FBQyxDQUFDO1FBQ2hELElBQUksVUFBVSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7WUFDekIsT0FBTyxRQUFRLENBQUM7UUFDbEIsQ0FBQztJQUNILENBQUM7SUFFRCwwQ0FBMEM7SUFDMUMsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUM7SUFDbkQsSUFBSSxVQUFVLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQztRQUM1QixPQUFPLFdBQVcsQ0FBQztJQUNyQixDQUFDO0lBRUQsT0FBTyxJQUFJLENBQUM7QUFDZCxDQUFDO0FBRUQ7O0dBRUc7QUFDSCxTQUFTLFVBQVUsQ0FBQyxRQUFnQjtJQUNsQyxJQUFJLENBQUM7UUFDSCxFQUFFLENBQUMsVUFBVSxDQUFDLFFBQVEsRUFBRSxFQUFFLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzNDLE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUFDLE1BQU0sQ0FBQztRQUNQLE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztBQUNILENBQUM7QUFFRDs7O0dBR0c7QUFDSCxTQUFnQixnQkFBZ0IsQ0FBQyxHQUFXO0lBQzFDLElBQUksQ0FBQyxHQUFHLElBQUksR0FBRyxJQUFJLENBQUMsRUFBRSxDQUFDO1FBQ3JCLE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztJQUVELElBQUksQ0FBQztRQUNILElBQUksT0FBTyxDQUFDLFFBQVEsS0FBSyxPQUFPLEVBQUUsQ0FBQztZQUNqQyxnQ0FBZ0M7WUFDaEMsT0FBTyx1QkFBdUIsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUN0QyxDQUFDO2FBQU0sQ0FBQztZQUNOLDJDQUEyQztZQUMzQyxPQUFPLG9CQUFvQixDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ25DLENBQUM7SUFDSCxDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLE1BQU0sQ0FBQyxJQUFJLENBQUMsNkJBQTZCLEdBQUcsY0FBYyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQ25FLE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztBQUNILENBQUM7QUFFRDs7R0FFRztBQUNILFNBQVMsdUJBQXVCLENBQUMsR0FBVztJQUMxQyxJQUFJLENBQUM7UUFDSCxNQUFNLENBQUMsS0FBSyxDQUFDLDRCQUE0QixHQUFHLGdCQUFnQixDQUFDLENBQUM7UUFDOUQsTUFBTSxNQUFNLEdBQUcsSUFBQSx5QkFBUyxFQUFDLFVBQVUsRUFBRSxDQUFDLEtBQUssRUFBRSxVQUFVLEdBQUcsRUFBRSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsS0FBSyxDQUFDLEVBQUU7WUFDbEYsUUFBUSxFQUFFLE1BQU07WUFDaEIsV0FBVyxFQUFFLElBQUk7WUFDakIsT0FBTyxFQUFFLElBQUksRUFBRSxtQkFBbUI7U0FDbkMsQ0FBQyxDQUFDO1FBRUgsMkRBQTJEO1FBQzNELElBQUksTUFBTSxDQUFDLE1BQU0sS0FBSyxDQUFDLElBQUksTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ3pDLGlEQUFpRDtZQUNqRCxNQUFNLE1BQU0sR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEdBQUcsR0FBRyxDQUFDLENBQUM7WUFDbEQsTUFBTSxDQUFDLEtBQUssQ0FBQyxXQUFXLEdBQUcsWUFBWSxNQUFNLEVBQUUsQ0FBQyxDQUFDO1lBQ2pELE9BQU8sTUFBTSxDQUFDO1FBQ2hCLENBQUM7UUFFRCxNQUFNLENBQUMsS0FBSyxDQUFDLHVDQUF1QyxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztRQUNyRSxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1FBQ2YsTUFBTSxDQUFDLElBQUksQ0FBQyx3Q0FBd0MsR0FBRyxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDbkUsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0FBQ0gsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBUyxvQkFBb0IsQ0FBQyxHQUFXO0lBQ3ZDLElBQUksQ0FBQztRQUNILDJDQUEyQztRQUMzQyxnRUFBZ0U7UUFDaEUsT0FBTyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDckIsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLDZDQUE2QztRQUM3QyxtRUFBbUU7UUFDbkUsTUFBTSxHQUFHLEdBQUcsS0FBOEIsQ0FBQztRQUMzQyxJQUFJLEdBQUcsQ0FBQyxJQUFJLEtBQUssT0FBTyxFQUFFLENBQUM7WUFDekIsMkRBQTJEO1lBQzNELE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztRQUNELG1EQUFtRDtRQUNuRCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7QUFDSCxDQUFDO0FBRUQ7OztHQUdHO0FBQ0gsU0FBZ0IsY0FBYyxDQUFDLEdBQVc7SUFDeEMsSUFBSSxDQUFDLGdCQUFnQixDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDM0IsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQsT0FBTztRQUNMLEdBQUc7UUFDSCxNQUFNLEVBQUUsSUFBSTtLQUNiLENBQUM7QUFDSixDQUFDO0FBRUQ7OztHQUdHO0FBQ0gsU0FBZ0IsV0FBVyxDQUFDLEdBQVcsRUFBRSxTQUFrQyxTQUFTO0lBQ2xGLElBQUksQ0FBQyxHQUFHLElBQUksR0FBRyxJQUFJLENBQUMsRUFBRSxDQUFDO1FBQ3JCLE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztJQUVELE1BQU0sQ0FBQyxLQUFLLENBQUMsOEJBQThCLEdBQUcsZ0JBQWdCLE1BQU0sRUFBRSxDQUFDLENBQUM7SUFFeEUsSUFBSSxDQUFDO1FBQ0gsSUFBSSxPQUFPLENBQUMsUUFBUSxLQUFLLE9BQU8sRUFBRSxDQUFDO1lBQ2pDLDhEQUE4RDtZQUM5RCxNQUFNLE1BQU0sR0FBRyxJQUFBLHlCQUFTLEVBQUMsVUFBVSxFQUFFLENBQUMsTUFBTSxFQUFFLEdBQUcsQ0FBQyxRQUFRLEVBQUUsRUFBRSxJQUFJLENBQUMsRUFBRTtnQkFDbkUsV0FBVyxFQUFFLElBQUk7Z0JBQ2pCLE9BQU8sRUFBRSxJQUFJO2FBQ2QsQ0FBQyxDQUFDO1lBQ0gsSUFBSSxNQUFNLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO2dCQUN4QixNQUFNLENBQUMsR0FBRyxDQUFDLGVBQUssQ0FBQyxLQUFLLENBQUMsV0FBVyxHQUFHLHNCQUFzQixDQUFDLENBQUMsQ0FBQztnQkFDOUQsT0FBTyxJQUFJLENBQUM7WUFDZCxDQUFDO2lCQUFNLENBQUM7Z0JBQ04sTUFBTSxDQUFDLEtBQUssQ0FBQywrQkFBK0IsTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7Z0JBQzdELE9BQU8sS0FBSyxDQUFDO1lBQ2YsQ0FBQztRQUNILENBQUM7YUFBTSxDQUFDO1lBQ04sdUNBQXVDO1lBQ3ZDLE9BQU8sQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLE1BQU0sQ0FBQyxDQUFDO1lBQzFCLE1BQU0sQ0FBQyxHQUFHLENBQUMsZUFBSyxDQUFDLEtBQUssQ0FBQyxVQUFVLE1BQU0sb0JBQW9CLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQztZQUNuRSxPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7SUFDSCxDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLE1BQU0sQ0FBQyxJQUFJLENBQUMseUJBQXlCLEdBQUcsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQ3BELE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztBQUNILENBQUM7QUFFRDs7O0dBR0c7QUFDSSxLQUFLLFVBQVUsa0JBQWtCLENBQUMsR0FBVyxFQUFFLFlBQW9CLElBQUk7SUFDNUUsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO0lBQzdCLE1BQU0sYUFBYSxHQUFHLEdBQUcsQ0FBQyxDQUFDLG9CQUFvQjtJQUUvQyxNQUFNLENBQUMsS0FBSyxDQUFDLHVCQUF1QixHQUFHLHNCQUFzQixTQUFTLEtBQUssQ0FBQyxDQUFDO0lBRTdFLE9BQU8sSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLFNBQVMsR0FBRyxTQUFTLEVBQUUsQ0FBQztRQUMxQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUMzQixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsU0FBUyxDQUFDO1lBQ3ZDLE1BQU0sQ0FBQyxHQUFHLENBQUMsZUFBSyxDQUFDLEtBQUssQ0FBQyxXQUFXLEdBQUcsaUJBQWlCLE9BQU8sSUFBSSxDQUFDLENBQUMsQ0FBQztZQUNwRSxPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7UUFDRCxNQUFNLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxVQUFVLENBQUMsT0FBTyxFQUFFLGFBQWEsQ0FBQyxDQUFDLENBQUM7SUFDckUsQ0FBQztJQUVELE1BQU0sQ0FBQyxHQUFHLENBQUMsZUFBSyxDQUFDLE1BQU0sQ0FBQyxXQUFXLEdBQUcsd0JBQXdCLFNBQVMsWUFBWSxDQUFDLENBQUMsQ0FBQztJQUN0RixPQUFPLEtBQUssQ0FBQztBQUNmLENBQUM7QUFFRDs7R0FFRztBQUNILFNBQVMseUJBQXlCLENBQUMsT0FBZSxFQUFFLElBQWM7SUFDaEUsZ0JBQWdCO0lBQ2hCLE1BQU0sTUFBTSxHQUFHLENBQUMsTUFBTSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQzNFLE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLE9BQU8sS0FBSyxLQUFLLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxJQUFJLEtBQUssRUFBRSxDQUFDLENBQUMsQ0FBQztJQUUzRixJQUFJLENBQUMsT0FBTztRQUFFLE9BQU8sS0FBSyxDQUFDO0lBRTNCLDhCQUE4QjtJQUM5QixNQUFNLGdCQUFnQixHQUFHLENBQUMsSUFBSSxFQUFFLGVBQWUsRUFBRSxJQUFJLEVBQUUsU0FBUyxDQUFDLENBQUM7SUFFbEUsMENBQTBDO0lBQzFDLElBQUksSUFBSSxDQUFDLE1BQU0sS0FBSyxDQUFDO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFFbkMsOENBQThDO0lBQzlDLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsZ0JBQWdCLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7QUFDNUQsQ0FBQztBQUVEOzs7R0FHRztBQUNILFNBQWdCLGNBQWMsQ0FBQyxPQUFpQjtJQVE5QyxJQUFJLE9BQU8sQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDekIsTUFBTSxJQUFJLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDO0lBQ3pDLENBQUM7SUFFRCxNQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDM0IsTUFBTSxPQUFPLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUVqQyw0RUFBNEU7SUFDNUUsTUFBTSxZQUFZLEdBQUcsT0FBTyxDQUFDLFFBQVEsS0FBSyxPQUFPLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDO0lBRXRFLElBQUksQ0FBQztRQUNILE1BQU0sTUFBTSxHQUFHLElBQUEseUJBQVMsRUFBQyxZQUFZLEVBQUUsQ0FBQyxPQUFPLENBQUMsRUFBRTtZQUNoRCxRQUFRLEVBQUUsTUFBTTtZQUNoQixXQUFXLEVBQUUsSUFBSTtZQUNqQixPQUFPLEVBQUUsSUFBSSxFQUFFLG1CQUFtQjtTQUNuQyxDQUFDLENBQUM7UUFFSCxJQUFJLE1BQU0sQ0FBQyxNQUFNLEtBQUssQ0FBQyxJQUFJLE1BQU0sQ0FBQyxNQUFNLElBQUksTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDO1lBQ2pFLHdCQUF3QjtZQUN4QixNQUFNLENBQUMsS0FBSyxDQUFDLFlBQVksT0FBTyxlQUFlLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1lBRXZFLGdEQUFnRDtZQUNoRCxJQUFJLHlCQUF5QixDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUMsRUFBRSxDQUFDO2dCQUNoRCxNQUFNLENBQUMsR0FBRyxDQUFDLGVBQUssQ0FBQyxJQUFJLENBQUMsY0FBYyxPQUFPLDZDQUE2QyxDQUFDLENBQUMsQ0FBQztnQkFDM0YsaUZBQWlGO2dCQUNqRixpRkFBaUY7Z0JBRWpGLDZDQUE2QztnQkFDN0MsTUFBTSxrQkFBa0IsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxHQUFHLEtBQUssSUFBSSxJQUFJLEdBQUcsS0FBSyxlQUFlLENBQUMsQ0FBQztnQkFDMUYsTUFBTSxZQUFZLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsR0FBRyxLQUFLLElBQUksSUFBSSxHQUFHLEtBQUssU0FBUyxDQUFDLENBQUM7Z0JBRTlFLG1CQUFtQjtnQkFDbkIsTUFBTSxTQUFTLEdBQUcsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxDQUFDO2dCQUUvQixxRUFBcUU7Z0JBQ3JFLE1BQU0sTUFBTSxHQUFHLE9BQU8sS0FBSyxNQUFNLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQztnQkFFL0QsSUFBSSxDQUFDLGtCQUFrQixFQUFFLENBQUM7b0JBQ3hCLFNBQVMsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxlQUFlLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUNyRCxDQUFDO2dCQUVELElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztvQkFDbEIsU0FBUyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQy9DLENBQUM7Z0JBRUQsT0FBTztvQkFDTCxPQUFPLEVBQUUsT0FBTztvQkFDaEIsSUFBSSxFQUFFLFNBQVM7b0JBQ2YsUUFBUSxFQUFFLEtBQUs7b0JBQ2YsWUFBWSxFQUFFLE1BQU07b0JBQ3BCLGVBQWUsRUFBRSxPQUFPO29CQUN4QixhQUFhLEVBQUUsSUFBSTtpQkFDcEIsQ0FBQztZQUNKLENBQUM7WUFFRCxPQUFPO2dCQUNMLE9BQU8sRUFBRSxPQUFPO2dCQUNoQixJQUFJLEVBQUUsT0FBTztnQkFDYixRQUFRLEVBQUUsS0FBSztnQkFDZixZQUFZLEVBQUUsTUFBTTtnQkFDcEIsZUFBZSxFQUFFLE9BQU87YUFDekIsQ0FBQztRQUNKLENBQUM7SUFDSCxDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLE1BQU0sQ0FBQyxLQUFLLENBQUMsMENBQTBDLE9BQU8sSUFBSSxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQzdFLENBQUM7SUFFRCw4REFBOEQ7SUFDOUQsNEJBQTRCO0lBQzVCLE1BQU0sQ0FBQyxLQUFLLENBQUMsWUFBWSxPQUFPLHFDQUFxQyxDQUFDLENBQUM7SUFFdkUseUJBQXlCO0lBQ3pCLE1BQU0sU0FBUyxHQUFHLFlBQVksRUFBRSxDQUFDO0lBRWpDLGtGQUFrRjtJQUNsRix3RUFBd0U7SUFDeEUsTUFBTSxTQUFTLEdBQUcsQ0FBQyx5QkFBeUIsQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFFL0QsK0NBQStDO0lBQy9DLHlEQUF5RDtJQUN6RCxJQUFJLE9BQU8sQ0FBQyxRQUFRLEtBQUssT0FBTyxFQUFFLENBQUM7UUFDakMsdUNBQXVDO1FBQ3ZDLElBQUksU0FBUyxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO1lBQy9CLDZDQUE2QztZQUM3QyxJQUFJLFNBQVMsRUFBRSxDQUFDO2dCQUNkLG9DQUFvQztnQkFDcEMsT0FBTztvQkFDTCxPQUFPLEVBQUUsU0FBUztvQkFDbEIsSUFBSSxFQUFFLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7b0JBQy9CLFFBQVEsRUFBRSxJQUFJO29CQUNkLFlBQVksRUFBRSxPQUFPO2lCQUN0QixDQUFDO1lBQ0osQ0FBQztpQkFBTSxDQUFDO2dCQUNOLDRCQUE0QjtnQkFDNUIsT0FBTztvQkFDTCxPQUFPLEVBQUUsU0FBUztvQkFDbEIsSUFBSSxFQUFFLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxPQUFPLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO29CQUNyQyxRQUFRLEVBQUUsSUFBSTtvQkFDZCxZQUFZLEVBQUUsT0FBTztvQkFDckIsYUFBYSxFQUFFLElBQUk7aUJBQ3BCLENBQUM7WUFDSixDQUFDO1FBQ0gsQ0FBQzthQUFNLElBQUksU0FBUyxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsSUFBSSxTQUFTLENBQUMsUUFBUSxDQUFDLFlBQVksQ0FBQyxFQUFFLENBQUM7WUFDMUUseUNBQXlDO1lBQ3pDLDhEQUE4RDtZQUM5RCxPQUFPO2dCQUNMLE9BQU8sRUFBRSxTQUFTO2dCQUNsQixJQUFJLEVBQUUsQ0FBQyxTQUFTLEVBQUUsVUFBVSxFQUFFLE9BQU8sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQ2hELFFBQVEsRUFBRSxJQUFJO2dCQUNkLFlBQVksRUFBRSxPQUFPO2FBQ3RCLENBQUM7UUFDSixDQUFDO2FBQU0sQ0FBQztZQUNOLHNDQUFzQztZQUN0QyxtRUFBbUU7WUFDbkUsT0FBTztnQkFDTCxPQUFPLEVBQUUsU0FBUztnQkFDbEIsSUFBSSxFQUFFLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQy9CLFFBQVEsRUFBRSxJQUFJO2dCQUNkLFlBQVksRUFBRSxPQUFPO2FBQ3RCLENBQUM7UUFDSixDQUFDO0lBQ0gsQ0FBQztTQUFNLENBQUM7UUFDTiwyREFBMkQ7UUFDM0QsSUFBSSxTQUFTLEVBQUUsQ0FBQztZQUNkLHNFQUFzRTtZQUN0RSw2RUFBNkU7WUFDN0UsTUFBTSxXQUFXLEdBQUcsa0JBQWtCLENBQUMsU0FBUyxDQUFDLENBQUM7WUFFbEQsSUFBSSxXQUFXLEVBQUUsQ0FBQztnQkFDaEIsa0ZBQWtGO2dCQUNsRiw0REFBNEQ7Z0JBQzVELDRFQUE0RTtnQkFDNUUsT0FBTztvQkFDTCxPQUFPLEVBQUUsU0FBUztvQkFDbEIsSUFBSSxFQUFFLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsT0FBTyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztvQkFDM0MsUUFBUSxFQUFFLElBQUk7b0JBQ2QsWUFBWSxFQUFFLE9BQU87aUJBQ3RCLENBQUM7WUFDSixDQUFDO2lCQUFNLENBQUM7Z0JBQ04sNkNBQTZDO2dCQUM3QyxPQUFPO29CQUNMLE9BQU8sRUFBRSxTQUFTO29CQUNsQixJQUFJLEVBQUUsQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztvQkFDL0IsUUFBUSxFQUFFLElBQUk7b0JBQ2QsWUFBWSxFQUFFLE9BQU87aUJBQ3RCLENBQUM7WUFDSixDQUFDO1FBQ0gsQ0FBQzthQUFNLENBQUM7WUFDTixxRUFBcUU7WUFDckUsT0FBTztnQkFDTCxPQUFPLEVBQUUsU0FBUztnQkFDbEIsSUFBSSxFQUFFLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsT0FBTyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDM0MsUUFBUSxFQUFFLElBQUk7Z0JBQ2QsWUFBWSxFQUFFLE9BQU87Z0JBQ3JCLGFBQWEsRUFBRSxJQUFJO2FBQ3BCLENBQUM7UUFDSixDQUFDO0lBQ0gsQ0FBQztBQUNILENBQUM7QUFFRDs7O0dBR0c7QUFDSCxTQUFnQixZQUFZO0lBQzFCLCtEQUErRDtJQUMvRCxJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDdEIsT0FBTyxPQUFPLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQztJQUMzQixDQUFDO0lBRUQsNkJBQTZCO0lBQzdCLElBQUksT0FBTyxDQUFDLFFBQVEsS0FBSyxPQUFPLEVBQUUsQ0FBQztRQUNqQyxnQ0FBZ0M7UUFFaEMsK0RBQStEO1FBQy9ELElBQUksQ0FBQztZQUNILE1BQU0sTUFBTSxHQUFHLElBQUEseUJBQVMsRUFBQyxNQUFNLEVBQUUsQ0FBQyxVQUFVLEVBQUUsV0FBVyxDQUFDLEVBQUU7Z0JBQzFELFFBQVEsRUFBRSxNQUFNO2dCQUNoQixXQUFXLEVBQUUsSUFBSTtnQkFDakIsT0FBTyxFQUFFLElBQUk7YUFDZCxDQUFDLENBQUM7WUFDSCxJQUFJLE1BQU0sQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7Z0JBQ3hCLE9BQU8sTUFBTSxDQUFDO1lBQ2hCLENBQUM7UUFDSCxDQUFDO1FBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztZQUNYLGdDQUFnQztRQUNsQyxDQUFDO1FBRUQsd0RBQXdEO1FBQ3hELE1BQU0sY0FBYyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQzlCLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxJQUFJLGFBQWEsRUFDdkMsVUFBVSxFQUNWLG1CQUFtQixFQUNuQixNQUFNLEVBQ04sZ0JBQWdCLENBQ2pCLENBQUM7UUFDRixJQUFJLENBQUM7WUFDSCxNQUFNLE1BQU0sR0FBRyxJQUFBLHlCQUFTLEVBQUMsY0FBYyxFQUFFLENBQUMsVUFBVSxFQUFFLFdBQVcsQ0FBQyxFQUFFO2dCQUNsRSxRQUFRLEVBQUUsTUFBTTtnQkFDaEIsV0FBVyxFQUFFLElBQUk7Z0JBQ2pCLE9BQU8sRUFBRSxJQUFJO2FBQ2QsQ0FBQyxDQUFDO1lBQ0gsSUFBSSxNQUFNLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO2dCQUN4QixPQUFPLGNBQWMsQ0FBQztZQUN4QixDQUFDO1FBQ0gsQ0FBQztRQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDWCwyQkFBMkI7UUFDN0IsQ0FBQztRQUVELHFDQUFxQztRQUNyQyxNQUFNLFlBQVksR0FBRztZQUNuQix1Q0FBdUM7WUFDdkMsNkNBQTZDO1lBQzdDLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxZQUFZLElBQUksbUJBQW1CLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxVQUFVLENBQUM7U0FDckYsQ0FBQztRQUNGLEtBQUssTUFBTSxXQUFXLElBQUksWUFBWSxFQUFFLENBQUM7WUFDdkMsSUFBSSxDQUFDO2dCQUNILE1BQU0sTUFBTSxHQUFHLElBQUEseUJBQVMsRUFBQyxXQUFXLEVBQUUsQ0FBQyxJQUFJLEVBQUUsV0FBVyxDQUFDLEVBQUU7b0JBQ3pELFFBQVEsRUFBRSxNQUFNO29CQUNoQixXQUFXLEVBQUUsSUFBSTtvQkFDakIsT0FBTyxFQUFFLElBQUk7aUJBQ2QsQ0FBQyxDQUFDO2dCQUNILElBQUksTUFBTSxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztvQkFDeEIsT0FBTyxXQUFXLENBQUM7Z0JBQ3JCLENBQUM7WUFDSCxDQUFDO1lBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztnQkFDWCxnQ0FBZ0M7WUFDbEMsQ0FBQztRQUNILENBQUM7UUFFRCwwQkFBMEI7UUFDMUIsT0FBTyxPQUFPLENBQUMsR0FBRyxDQUFDLE9BQU8sSUFBSSxTQUFTLENBQUM7SUFDMUMsQ0FBQztTQUFNLENBQUM7UUFDTixvQkFBb0I7UUFDcEIseURBQXlEO1FBQ3pELElBQUksQ0FBQztZQUNILE1BQU0sUUFBUSxHQUFHLEVBQUUsQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUMvQixJQUFJLE9BQU8sSUFBSSxRQUFRLElBQUksUUFBUSxDQUFDLEtBQUssRUFBRSxDQUFDO2dCQUMxQyxPQUFPLFFBQVEsQ0FBQyxLQUFlLENBQUM7WUFDbEMsQ0FBQztRQUNILENBQUM7UUFBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1lBQ1gsMkNBQTJDO1FBQzdDLENBQUM7UUFFRCxrREFBa0Q7UUFDbEQscUVBQXFFO1FBQ3JFLE1BQU0sWUFBWSxHQUFHLENBQUMsV0FBVyxFQUFFLGVBQWUsRUFBRSxVQUFVLEVBQUUsY0FBYyxFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBQzNGLEtBQUssTUFBTSxLQUFLLElBQUksWUFBWSxFQUFFLENBQUM7WUFDakMsSUFBSSxDQUFDO2dCQUNILG1EQUFtRDtnQkFDbkQsTUFBTSxNQUFNLEdBQUcsSUFBQSx5QkFBUyxFQUFDLE1BQU0sRUFBRSxDQUFDLElBQUksRUFBRSxLQUFLLENBQUMsRUFBRTtvQkFDOUMsUUFBUSxFQUFFLE1BQU07b0JBQ2hCLE9BQU8sRUFBRSxHQUFHO2lCQUNiLENBQUMsQ0FBQztnQkFDSCxJQUFJLE1BQU0sQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7b0JBQ3hCLE9BQU8sS0FBSyxDQUFDO2dCQUNmLENBQUM7WUFDSCxDQUFDO1lBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztnQkFDWCxzQ0FBc0M7WUFDeEMsQ0FBQztRQUNILENBQUM7UUFFRCx1REFBdUQ7UUFDdkQsT0FBTyxTQUFTLENBQUM7SUFDbkIsQ0FBQztBQUNILENBQUM7QUFFRCxrREFBa0Q7QUFDckMsUUFBQSxZQUFZLEdBQUc7SUFDMUIsZ0JBQWdCO0lBQ2hCLGNBQWM7SUFDZCxXQUFXO0lBQ1gsa0JBQWtCO0lBQ2xCLGNBQWM7SUFDZCxZQUFZO0NBQ2IsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogUHJvY2Vzc1V0aWxzIC0gQ3Jvc3MtcGxhdGZvcm0gcHJvY2VzcyBtYW5hZ2VtZW50IHV0aWxpdGllc1xuICpcbiAqIFByb3ZpZGVzIHJlbGlhYmxlIHByb2Nlc3MgZXhpc3RlbmNlIGNoZWNraW5nIGFjcm9zcyBXaW5kb3dzLCBtYWNPUywgYW5kIExpbnV4LlxuICovXG5cbmltcG9ydCBjaGFsayBmcm9tICdjaGFsayc7XG5pbXBvcnQgeyBzcGF3blN5bmMgfSBmcm9tICdjaGlsZF9wcm9jZXNzJztcbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzJztcbmltcG9ydCAqIGFzIG9zIGZyb20gJ29zJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBjcmVhdGVMb2dnZXIgfSBmcm9tICcuLi91dGlscy9sb2dnZXIuanMnO1xuXG5jb25zdCBsb2dnZXIgPSBjcmVhdGVMb2dnZXIoJ3Byb2Nlc3MtdXRpbHMnKTtcblxuLyoqXG4gKiBHZXQgdGhlIGFwcHJvcHJpYXRlIHNoZWxsIGNvbmZpZ3VyYXRpb24gZmlsZSBmb3IgYSBnaXZlbiBzaGVsbFxuICogQHBhcmFtIHNoZWxsUGF0aCBUaGUgcGF0aCB0byB0aGUgc2hlbGwgZXhlY3V0YWJsZVxuICogQHJldHVybnMgVGhlIHBhdGggdG8gdGhlIHNoZWxsIGNvbmZpZyBmaWxlLCBvciBudWxsIGlmIG5vbmUgZm91bmRcbiAqL1xuZnVuY3Rpb24gZ2V0U2hlbGxDb25maWdGaWxlKHNoZWxsUGF0aDogc3RyaW5nKTogc3RyaW5nIHwgbnVsbCB7XG4gIGNvbnN0IGhvbWVEaXIgPSBvcy5ob21lZGlyKCk7XG4gIGNvbnN0IHNoZWxsTmFtZSA9IHBhdGguYmFzZW5hbWUoc2hlbGxQYXRoKTtcblxuICAvLyBNYXAgb2Ygc2hlbGwgbmFtZXMgdG8gdGhlaXIgY29uZmlnIGZpbGVzIChpbiBvcmRlciBvZiBwcmVmZXJlbmNlKVxuICBjb25zdCBzaGVsbENvbmZpZ3M6IFJlY29yZDxzdHJpbmcsIHN0cmluZ1tdPiA9IHtcbiAgICB6c2g6IFsnLnpzaHJjJywgJy56c2hlbnYnXSxcbiAgICBiYXNoOiBbJy5iYXNocmMnLCAnLmJhc2hfcHJvZmlsZScsICcucHJvZmlsZSddLFxuICAgIHNoOiBbJy5wcm9maWxlJ10sXG4gICAga3NoOiBbJy5rc2hyYycsICcucHJvZmlsZSddLFxuICAgIGZpc2g6IFsnLmNvbmZpZy9maXNoL2NvbmZpZy5maXNoJ10sXG4gICAgdGNzaDogWycudGNzaHJjJywgJy5jc2hyYyddLFxuICAgIGNzaDogWycuY3NocmMnXSxcbiAgICBkYXNoOiBbJy5wcm9maWxlJ10sXG4gIH07XG5cbiAgLy8gR2V0IGNvbmZpZyBmaWxlcyBmb3IgdGhpcyBzaGVsbFxuICBjb25zdCBjb25maWdGaWxlcyA9IHNoZWxsQ29uZmlnc1tzaGVsbE5hbWVdIHx8IFtdO1xuXG4gIC8vIENoZWNrIGVhY2ggY29uZmlnIGZpbGUgaW4gb3JkZXIgb2YgcHJlZmVyZW5jZVxuICBmb3IgKGNvbnN0IGNvbmZpZ0ZpbGUgb2YgY29uZmlnRmlsZXMpIHtcbiAgICBjb25zdCBmdWxsUGF0aCA9IHBhdGguam9pbihob21lRGlyLCBjb25maWdGaWxlKTtcbiAgICBpZiAoZXhpc3RzU3luYyhmdWxsUGF0aCkpIHtcbiAgICAgIHJldHVybiBmdWxsUGF0aDtcbiAgICB9XG4gIH1cblxuICAvLyBGYWxsYmFjayB0byAucHJvZmlsZSBmb3IgdW5rbm93biBzaGVsbHNcbiAgY29uc3QgcHJvZmlsZVBhdGggPSBwYXRoLmpvaW4oaG9tZURpciwgJy5wcm9maWxlJyk7XG4gIGlmIChleGlzdHNTeW5jKHByb2ZpbGVQYXRoKSkge1xuICAgIHJldHVybiBwcm9maWxlUGF0aDtcbiAgfVxuXG4gIHJldHVybiBudWxsO1xufVxuXG4vKipcbiAqIFNhZmUgZmlsZSBleGlzdGVuY2UgY2hlY2tcbiAqL1xuZnVuY3Rpb24gZXhpc3RzU3luYyhmaWxlUGF0aDogc3RyaW5nKTogYm9vbGVhbiB7XG4gIHRyeSB7XG4gICAgZnMuYWNjZXNzU3luYyhmaWxlUGF0aCwgZnMuY29uc3RhbnRzLkZfT0spO1xuICAgIHJldHVybiB0cnVlO1xuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbn1cblxuLyoqXG4gKiBDaGVjayBpZiBhIHByb2Nlc3MgaXMgY3VycmVudGx5IHJ1bm5pbmcgYnkgUElEXG4gKiBVc2VzIHBsYXRmb3JtLWFwcHJvcHJpYXRlIG1ldGhvZHMgZm9yIHJlbGlhYmxlIGRldGVjdGlvblxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNQcm9jZXNzUnVubmluZyhwaWQ6IG51bWJlcik6IGJvb2xlYW4ge1xuICBpZiAoIXBpZCB8fCBwaWQgPD0gMCkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIHRyeSB7XG4gICAgaWYgKHByb2Nlc3MucGxhdGZvcm0gPT09ICd3aW4zMicpIHtcbiAgICAgIC8vIFdpbmRvd3M6IFVzZSB0YXNrbGlzdCBjb21tYW5kXG4gICAgICByZXR1cm4gaXNQcm9jZXNzUnVubmluZ1dpbmRvd3MocGlkKTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gVW5peC9MaW51eC9tYWNPUzogVXNlIGtpbGwgd2l0aCBzaWduYWwgMFxuICAgICAgcmV0dXJuIGlzUHJvY2Vzc1J1bm5pbmdVbml4KHBpZCk7XG4gICAgfVxuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGxvZ2dlci53YXJuKGBlcnJvciBjaGVja2luZyBpZiBwcm9jZXNzICR7cGlkfSBpcyBydW5uaW5nOmAsIGVycm9yKTtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbn1cblxuLyoqXG4gKiBXaW5kb3dzLXNwZWNpZmljIHByb2Nlc3MgY2hlY2sgdXNpbmcgdGFza2xpc3RcbiAqL1xuZnVuY3Rpb24gaXNQcm9jZXNzUnVubmluZ1dpbmRvd3MocGlkOiBudW1iZXIpOiBib29sZWFuIHtcbiAgdHJ5IHtcbiAgICBsb2dnZXIuZGVidWcoYGNoZWNraW5nIHdpbmRvd3MgcHJvY2VzcyAke3BpZH0gd2l0aCB0YXNrbGlzdGApO1xuICAgIGNvbnN0IHJlc3VsdCA9IHNwYXduU3luYygndGFza2xpc3QnLCBbJy9GSScsIGBQSUQgZXEgJHtwaWR9YCwgJy9OSCcsICcvRk8nLCAnQ1NWJ10sIHtcbiAgICAgIGVuY29kaW5nOiAndXRmOCcsXG4gICAgICB3aW5kb3dzSGlkZTogdHJ1ZSxcbiAgICAgIHRpbWVvdXQ6IDUwMDAsIC8vIDUgc2Vjb25kIHRpbWVvdXRcbiAgICB9KTtcblxuICAgIC8vIENoZWNrIGlmIHRoZSBjb21tYW5kIHN1Y2NlZWRlZCBhbmQgUElEIGFwcGVhcnMgaW4gb3V0cHV0XG4gICAgaWYgKHJlc3VsdC5zdGF0dXMgPT09IDAgJiYgcmVzdWx0LnN0ZG91dCkge1xuICAgICAgLy8gdGFza2xpc3Qgb3V0cHV0cyBDU1YgZm9ybWF0IHdpdGggUElEIGluIHF1b3Rlc1xuICAgICAgY29uc3QgZXhpc3RzID0gcmVzdWx0LnN0ZG91dC5pbmNsdWRlcyhgXCIke3BpZH1cImApO1xuICAgICAgbG9nZ2VyLmRlYnVnKGBwcm9jZXNzICR7cGlkfSBleGlzdHM6ICR7ZXhpc3RzfWApO1xuICAgICAgcmV0dXJuIGV4aXN0cztcbiAgICB9XG5cbiAgICBsb2dnZXIuZGVidWcoYHRhc2tsaXN0IGNvbW1hbmQgZmFpbGVkIHdpdGggc3RhdHVzICR7cmVzdWx0LnN0YXR1c31gKTtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgbG9nZ2VyLndhcm4oYHdpbmRvd3MgcHJvY2VzcyBjaGVjayBmYWlsZWQgZm9yIFBJRCAke3BpZH06YCwgZXJyb3IpO1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxufVxuXG4vKipcbiAqIFVuaXgtbGlrZSBzeXN0ZW1zIHByb2Nlc3MgY2hlY2sgdXNpbmcga2lsbCBzaWduYWwgMFxuICovXG5mdW5jdGlvbiBpc1Byb2Nlc3NSdW5uaW5nVW5peChwaWQ6IG51bWJlcik6IGJvb2xlYW4ge1xuICB0cnkge1xuICAgIC8vIFNlbmQgc2lnbmFsIDAgdG8gY2hlY2sgaWYgcHJvY2VzcyBleGlzdHNcbiAgICAvLyBUaGlzIGRvZXNuJ3QgYWN0dWFsbHkga2lsbCB0aGUgcHJvY2VzcywganVzdCBjaGVja3MgZXhpc3RlbmNlXG4gICAgcHJvY2Vzcy5raWxsKHBpZCwgMCk7XG4gICAgcmV0dXJuIHRydWU7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgLy8gSWYgd2UgZ2V0IEVTUkNILCB0aGUgcHJvY2VzcyBkb2Vzbid0IGV4aXN0XG4gICAgLy8gSWYgd2UgZ2V0IEVQRVJNLCB0aGUgcHJvY2VzcyBleGlzdHMgYnV0IHdlIGRvbid0IGhhdmUgcGVybWlzc2lvblxuICAgIGNvbnN0IGVyciA9IGVycm9yIGFzIE5vZGVKUy5FcnJub0V4Y2VwdGlvbjtcbiAgICBpZiAoZXJyLmNvZGUgPT09ICdFUEVSTScpIHtcbiAgICAgIC8vIFByb2Nlc3MgZXhpc3RzIGJ1dCB3ZSBkb24ndCBoYXZlIHBlcm1pc3Npb24gdG8gc2lnbmFsIGl0XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgLy8gRVNSQ0ggb3Igb3RoZXIgZXJyb3JzIG1lYW4gcHJvY2VzcyBkb2Vzbid0IGV4aXN0XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG5cbi8qKlxuICogR2V0IGJhc2ljIHByb2Nlc3MgaW5mb3JtYXRpb24gaWYgYXZhaWxhYmxlXG4gKiBSZXR1cm5zIG51bGwgaWYgcHJvY2VzcyBpcyBub3QgcnVubmluZyBvciBpbmZvIGNhbm5vdCBiZSByZXRyaWV2ZWRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFByb2Nlc3NJbmZvKHBpZDogbnVtYmVyKTogeyBwaWQ6IG51bWJlcjsgZXhpc3RzOiBib29sZWFuIH0gfCBudWxsIHtcbiAgaWYgKCFpc1Byb2Nlc3NSdW5uaW5nKHBpZCkpIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgcGlkLFxuICAgIGV4aXN0czogdHJ1ZSxcbiAgfTtcbn1cblxuLyoqXG4gKiBLaWxsIGEgcHJvY2VzcyB3aXRoIHBsYXRmb3JtLWFwcHJvcHJpYXRlIG1ldGhvZFxuICogUmV0dXJucyB0cnVlIGlmIHRoZSBraWxsIHNpZ25hbCB3YXMgc2VudCBzdWNjZXNzZnVsbHlcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGtpbGxQcm9jZXNzKHBpZDogbnVtYmVyLCBzaWduYWw6IE5vZGVKUy5TaWduYWxzIHwgbnVtYmVyID0gJ1NJR1RFUk0nKTogYm9vbGVhbiB7XG4gIGlmICghcGlkIHx8IHBpZCA8PSAwKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgbG9nZ2VyLmRlYnVnKGBhdHRlbXB0aW5nIHRvIGtpbGwgcHJvY2VzcyAke3BpZH0gd2l0aCBzaWduYWwgJHtzaWduYWx9YCk7XG5cbiAgdHJ5IHtcbiAgICBpZiAocHJvY2Vzcy5wbGF0Zm9ybSA9PT0gJ3dpbjMyJykge1xuICAgICAgLy8gV2luZG93czogVXNlIHRhc2traWxsIGNvbW1hbmQgZm9yIG1vcmUgcmVsaWFibGUgdGVybWluYXRpb25cbiAgICAgIGNvbnN0IHJlc3VsdCA9IHNwYXduU3luYygndGFza2tpbGwnLCBbJy9QSUQnLCBwaWQudG9TdHJpbmcoKSwgJy9GJ10sIHtcbiAgICAgICAgd2luZG93c0hpZGU6IHRydWUsXG4gICAgICAgIHRpbWVvdXQ6IDUwMDAsXG4gICAgICB9KTtcbiAgICAgIGlmIChyZXN1bHQuc3RhdHVzID09PSAwKSB7XG4gICAgICAgIGxvZ2dlci5sb2coY2hhbGsuZ3JlZW4oYHByb2Nlc3MgJHtwaWR9IGtpbGxlZCBzdWNjZXNzZnVsbHlgKSk7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKGB0YXNra2lsbCBmYWlsZWQgd2l0aCBzdGF0dXMgJHtyZXN1bHQuc3RhdHVzfWApO1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIFVuaXgtbGlrZTogVXNlIGJ1aWx0LWluIHByb2Nlc3Mua2lsbFxuICAgICAgcHJvY2Vzcy5raWxsKHBpZCwgc2lnbmFsKTtcbiAgICAgIGxvZ2dlci5sb2coY2hhbGsuZ3JlZW4oYHNpZ25hbCAke3NpZ25hbH0gc2VudCB0byBwcm9jZXNzICR7cGlkfWApKTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBsb2dnZXIud2FybihgZXJyb3Iga2lsbGluZyBwcm9jZXNzICR7cGlkfTpgLCBlcnJvcik7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG5cbi8qKlxuICogV2FpdCBmb3IgYSBwcm9jZXNzIHRvIGV4aXQgd2l0aCB0aW1lb3V0XG4gKiBSZXR1cm5zIHRydWUgaWYgcHJvY2VzcyBleGl0ZWQgd2l0aGluIHRpbWVvdXQsIGZhbHNlIG90aGVyd2lzZVxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gd2FpdEZvclByb2Nlc3NFeGl0KHBpZDogbnVtYmVyLCB0aW1lb3V0TXM6IG51bWJlciA9IDUwMDApOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgY29uc3Qgc3RhcnRUaW1lID0gRGF0ZS5ub3coKTtcbiAgY29uc3QgY2hlY2tJbnRlcnZhbCA9IDEwMDsgLy8gQ2hlY2sgZXZlcnkgMTAwbXNcblxuICBsb2dnZXIuZGVidWcoYHdhaXRpbmcgZm9yIHByb2Nlc3MgJHtwaWR9IHRvIGV4aXQgKHRpbWVvdXQ6ICR7dGltZW91dE1zfW1zKWApO1xuXG4gIHdoaWxlIChEYXRlLm5vdygpIC0gc3RhcnRUaW1lIDwgdGltZW91dE1zKSB7XG4gICAgaWYgKCFpc1Byb2Nlc3NSdW5uaW5nKHBpZCkpIHtcbiAgICAgIGNvbnN0IGVsYXBzZWQgPSBEYXRlLm5vdygpIC0gc3RhcnRUaW1lO1xuICAgICAgbG9nZ2VyLmxvZyhjaGFsay5ncmVlbihgcHJvY2VzcyAke3BpZH0gZXhpdGVkIGFmdGVyICR7ZWxhcHNlZH1tc2ApKTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4gc2V0VGltZW91dChyZXNvbHZlLCBjaGVja0ludGVydmFsKSk7XG4gIH1cblxuICBsb2dnZXIubG9nKGNoYWxrLnllbGxvdyhgcHJvY2VzcyAke3BpZH0gZGlkIG5vdCBleGl0IHdpdGhpbiAke3RpbWVvdXRNc31tcyB0aW1lb3V0YCkpO1xuICByZXR1cm4gZmFsc2U7XG59XG5cbi8qKlxuICogQ2hlY2sgaWYgdGhpcyBpcyBhbiBpbnRlcmFjdGl2ZSBzaGVsbCBzZXNzaW9uXG4gKi9cbmZ1bmN0aW9uIGlzSW50ZXJhY3RpdmVTaGVsbENvbW1hbmQoY21kTmFtZTogc3RyaW5nLCBhcmdzOiBzdHJpbmdbXSk6IGJvb2xlYW4ge1xuICAvLyBDb21tb24gc2hlbGxzXG4gIGNvbnN0IHNoZWxscyA9IFsnYmFzaCcsICd6c2gnLCAnc2gnLCAnZmlzaCcsICdkYXNoJywgJ2tzaCcsICd0Y3NoJywgJ2NzaCddO1xuICBjb25zdCBpc1NoZWxsID0gc2hlbGxzLnNvbWUoKHNoZWxsKSA9PiBjbWROYW1lID09PSBzaGVsbCB8fCBjbWROYW1lLmVuZHNXaXRoKGAvJHtzaGVsbH1gKSk7XG5cbiAgaWYgKCFpc1NoZWxsKSByZXR1cm4gZmFsc2U7XG5cbiAgLy8gQ2hlY2sgZm9yIGludGVyYWN0aXZlIGZsYWdzXG4gIGNvbnN0IGludGVyYWN0aXZlRmxhZ3MgPSBbJy1pJywgJy0taW50ZXJhY3RpdmUnLCAnLWwnLCAnLS1sb2dpbiddO1xuXG4gIC8vIElmIG5vIGFyZ3MsIGl0J3MgaW50ZXJhY3RpdmUgYnkgZGVmYXVsdFxuICBpZiAoYXJncy5sZW5ndGggPT09IDApIHJldHVybiB0cnVlO1xuXG4gIC8vIENoZWNrIGlmIGFueSBhcmdzIGluZGljYXRlIGludGVyYWN0aXZlIG1vZGVcbiAgcmV0dXJuIGFyZ3Muc29tZSgoYXJnKSA9PiBpbnRlcmFjdGl2ZUZsYWdzLmluY2x1ZGVzKGFyZykpO1xufVxuXG4vKipcbiAqIERldGVybWluZSBob3cgdG8gc3Bhd24gYSBjb21tYW5kLCBjaGVja2luZyBpZiBpdCBleGlzdHMgaW4gUEFUSCBvciBuZWVkcyBzaGVsbCBleGVjdXRpb25cbiAqIFJldHVybnMgdGhlIGFjdHVhbCBjb21tYW5kIGFuZCBhcmdzIHRvIHVzZSBmb3Igc3Bhd25pbmdcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVDb21tYW5kKGNvbW1hbmQ6IHN0cmluZ1tdKToge1xuICBjb21tYW5kOiBzdHJpbmc7XG4gIGFyZ3M6IHN0cmluZ1tdO1xuICB1c2VTaGVsbDogYm9vbGVhbjtcbiAgaXNJbnRlcmFjdGl2ZT86IGJvb2xlYW47XG4gIHJlc29sdmVkRnJvbT86ICdwYXRoJyB8ICdhbGlhcycgfCAnYnVpbHRpbicgfCAnc2hlbGwnO1xuICBvcmlnaW5hbENvbW1hbmQ/OiBzdHJpbmc7XG59IHtcbiAgaWYgKGNvbW1hbmQubGVuZ3RoID09PSAwKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdObyBjb21tYW5kIHByb3ZpZGVkJyk7XG4gIH1cblxuICBjb25zdCBjbWROYW1lID0gY29tbWFuZFswXTtcbiAgY29uc3QgY21kQXJncyA9IGNvbW1hbmQuc2xpY2UoMSk7XG5cbiAgLy8gQ2hlY2sgaWYgY29tbWFuZCBleGlzdHMgaW4gUEFUSCB1c2luZyAnd2hpY2gnIChVbml4KSBvciAnd2hlcmUnIChXaW5kb3dzKVxuICBjb25zdCB3aGljaENvbW1hbmQgPSBwcm9jZXNzLnBsYXRmb3JtID09PSAnd2luMzInID8gJ3doZXJlJyA6ICd3aGljaCc7XG5cbiAgdHJ5IHtcbiAgICBjb25zdCByZXN1bHQgPSBzcGF3blN5bmMod2hpY2hDb21tYW5kLCBbY21kTmFtZV0sIHtcbiAgICAgIGVuY29kaW5nOiAndXRmOCcsXG4gICAgICB3aW5kb3dzSGlkZTogdHJ1ZSxcbiAgICAgIHRpbWVvdXQ6IDIwMDAsIC8vIDIgc2Vjb25kIHRpbWVvdXRcbiAgICB9KTtcblxuICAgIGlmIChyZXN1bHQuc3RhdHVzID09PSAwICYmIHJlc3VsdC5zdGRvdXQgJiYgcmVzdWx0LnN0ZG91dC50cmltKCkpIHtcbiAgICAgIC8vIENvbW1hbmQgZm91bmQgaW4gUEFUSFxuICAgICAgbG9nZ2VyLmRlYnVnKGBDb21tYW5kICcke2NtZE5hbWV9JyBmb3VuZCBhdDogJHtyZXN1bHQuc3Rkb3V0LnRyaW0oKX1gKTtcblxuICAgICAgLy8gQ2hlY2sgaWYgdGhpcyBpcyBhbiBpbnRlcmFjdGl2ZSBzaGVsbCBjb21tYW5kXG4gICAgICBpZiAoaXNJbnRlcmFjdGl2ZVNoZWxsQ29tbWFuZChjbWROYW1lLCBjbWRBcmdzKSkge1xuICAgICAgICBsb2dnZXIubG9nKGNoYWxrLmN5YW4oYOKckyBTdGFydGluZyAke2NtZE5hbWV9IGFzIGxvZ2luIHNoZWxsIHRvIGxvYWQgY29uZmlndXJhdGlvbiBmaWxlc2ApKTtcbiAgICAgICAgLy8gQWRkIGJvdGggLWkgKGludGVyYWN0aXZlKSBhbmQgLWwvLS1sb2dpbiBmbGFncyBmb3IgcHJvcGVyIHNoZWxsIGluaXRpYWxpemF0aW9uXG4gICAgICAgIC8vIFRoaXMgZW5zdXJlcyBzaGVsbCBSQyBmaWxlcyBhcmUgc291cmNlZCBhbmQgdGhlIGVudmlyb25tZW50IGlzIHByb3Blcmx5IHNldCB1cFxuXG4gICAgICAgIC8vIERvbid0IGFkZCBmbGFncyBpZiB0aGV5J3JlIGFscmVhZHkgcHJlc2VudFxuICAgICAgICBjb25zdCBoYXNJbnRlcmFjdGl2ZUZsYWcgPSBjbWRBcmdzLnNvbWUoKGFyZykgPT4gYXJnID09PSAnLWknIHx8IGFyZyA9PT0gJy0taW50ZXJhY3RpdmUnKTtcbiAgICAgICAgY29uc3QgaGFzTG9naW5GbGFnID0gY21kQXJncy5zb21lKChhcmcpID0+IGFyZyA9PT0gJy1sJyB8fCBhcmcgPT09ICctLWxvZ2luJyk7XG5cbiAgICAgICAgLy8gQnVpbGQgYXJncyBhcnJheVxuICAgICAgICBjb25zdCBmaW5hbEFyZ3MgPSBbLi4uY21kQXJnc107XG5cbiAgICAgICAgLy8gRm9yIGZpc2ggc2hlbGwsIHVzZSAtLWxvZ2luIGFuZCAtLWludGVyYWN0aXZlIGluc3RlYWQgb2YgLWwgYW5kIC1pXG4gICAgICAgIGNvbnN0IGlzRmlzaCA9IGNtZE5hbWUgPT09ICdmaXNoJyB8fCBjbWROYW1lLmVuZHNXaXRoKCcvZmlzaCcpO1xuXG4gICAgICAgIGlmICghaGFzSW50ZXJhY3RpdmVGbGFnKSB7XG4gICAgICAgICAgZmluYWxBcmdzLnVuc2hpZnQoaXNGaXNoID8gJy0taW50ZXJhY3RpdmUnIDogJy1pJyk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIWhhc0xvZ2luRmxhZykge1xuICAgICAgICAgIGZpbmFsQXJncy51bnNoaWZ0KGlzRmlzaCA/ICctLWxvZ2luJyA6ICctbCcpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBjb21tYW5kOiBjbWROYW1lLFxuICAgICAgICAgIGFyZ3M6IGZpbmFsQXJncyxcbiAgICAgICAgICB1c2VTaGVsbDogZmFsc2UsXG4gICAgICAgICAgcmVzb2x2ZWRGcm9tOiAncGF0aCcsXG4gICAgICAgICAgb3JpZ2luYWxDb21tYW5kOiBjbWROYW1lLFxuICAgICAgICAgIGlzSW50ZXJhY3RpdmU6IHRydWUsXG4gICAgICAgIH07XG4gICAgICB9XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIGNvbW1hbmQ6IGNtZE5hbWUsXG4gICAgICAgIGFyZ3M6IGNtZEFyZ3MsXG4gICAgICAgIHVzZVNoZWxsOiBmYWxzZSxcbiAgICAgICAgcmVzb2x2ZWRGcm9tOiAncGF0aCcsXG4gICAgICAgIG9yaWdpbmFsQ29tbWFuZDogY21kTmFtZSxcbiAgICAgIH07XG4gICAgfVxuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGxvZ2dlci5kZWJ1ZyhgRmFpbGVkIHRvIGNoZWNrIGNvbW1hbmQgZXhpc3RlbmNlIGZvciAnJHtjbWROYW1lfSc6YCwgZXJyb3IpO1xuICB9XG5cbiAgLy8gQ29tbWFuZCBub3QgZm91bmQgaW4gUEFUSCwgbGlrZWx5IGFuIGFsaWFzIG9yIHNoZWxsIGJ1aWx0aW5cbiAgLy8gTmVlZCB0byBydW4gdGhyb3VnaCBzaGVsbFxuICBsb2dnZXIuZGVidWcoYENvbW1hbmQgJyR7Y21kTmFtZX0nIG5vdCBmb3VuZCBpbiBQQVRILCB3aWxsIHVzZSBzaGVsbGApO1xuXG4gIC8vIERldGVybWluZSB1c2VyJ3Mgc2hlbGxcbiAgY29uc3QgdXNlclNoZWxsID0gZ2V0VXNlclNoZWxsKCk7XG5cbiAgLy8gQ2hlY2sgaWYgdGhpcyBpcyB0cnlpbmcgdG8gZXhlY3V0ZSBhIGNvbW1hbmQgKG5vdCBhbiBpbnRlcmFjdGl2ZSBzaGVsbCBzZXNzaW9uKVxuICAvLyBJZiBzbywgdXNlIG5vbi1pbnRlcmFjdGl2ZSBtb2RlIHRvIGVuc3VyZSBzaGVsbCBleGl0cyBhZnRlciBleGVjdXRpb25cbiAgY29uc3QgaXNDb21tYW5kID0gIWlzSW50ZXJhY3RpdmVTaGVsbENvbW1hbmQoY21kTmFtZSwgY21kQXJncyk7XG5cbiAgLy8gVXNlIGludGVyYWN0aXZlIHNoZWxsIHRvIGV4ZWN1dGUgdGhlIGNvbW1hbmRcbiAgLy8gVGhpcyBlbnN1cmVzIGFsaWFzZXMgYW5kIHNoZWxsIGZ1bmN0aW9ucyBhcmUgYXZhaWxhYmxlXG4gIGlmIChwcm9jZXNzLnBsYXRmb3JtID09PSAnd2luMzInKSB7XG4gICAgLy8gV2luZG93cyBzaGVsbHMgaGF2ZSBkaWZmZXJlbnQgc3ludGF4XG4gICAgaWYgKHVzZXJTaGVsbC5pbmNsdWRlcygnYmFzaCcpKSB7XG4gICAgICAvLyBHaXQgQmFzaCBvbiBXaW5kb3dzOiBVc2UgVW5peC1zdHlsZSBzeW50YXhcbiAgICAgIGlmIChpc0NvbW1hbmQpIHtcbiAgICAgICAgLy8gTm9uLWludGVyYWN0aXZlIGNvbW1hbmQgZXhlY3V0aW9uXG4gICAgICAgIHJld