UNPKG

@energica-city/shared-amplify-utils

Version:

Shared utilities for AWS Amplify projects

379 lines 39.1 kB
/** * Defines the log levels for the logger. */ export var LogLevel; (function (LogLevel) { LogLevel[LogLevel["NONE"] = 0] = "NONE"; LogLevel[LogLevel["ERROR"] = 1] = "ERROR"; LogLevel[LogLevel["WARN"] = 2] = "WARN"; LogLevel[LogLevel["INFO"] = 3] = "INFO"; LogLevel[LogLevel["DEBUG"] = 4] = "DEBUG"; })(LogLevel || (LogLevel = {})); /** * A singleton Logger class that provides structured and text-based logging. * It supports different log levels, contextual information, and can adapt * its output format based on the environment (e.g., development, production, AWS Lambda). */ class Logger { static instance; level = LogLevel.INFO; // Default level context = {}; useStructuredLogging = false; environment = 'development'; // Make constructor private to enforce singleton pattern constructor() { // Detect environment using multiple methods this.environment = this.detectEnvironment(); // Enable structured logging based on environment detection this.useStructuredLogging = this.shouldUseStructuredLogging(); } /** * Detects the current runtime environment. * It checks for Amplify-specific environment variables, AWS Lambda function names, * and standard `NODE_ENV`. Defaults to 'development'. * @returns The detected environment name (e.g., 'production', 'development'). */ detectEnvironment() { // Method 1: Explicit environment variable (set in backend.ts) if (process.env.environment === 'prod') { return 'production'; } // Method 2: AWS stack name pattern (matches backend.ts logic) const functionName = process.env.AWS_LAMBDA_FUNCTION_NAME; if (functionName && functionName.includes('-main-')) { return 'production'; } // Method 3: AWS execution environment if (process.env.AWS_EXECUTION_ENV) { return 'aws-lambda'; } // Method 4: Traditional NODE_ENV if (process.env.NODE_ENV === 'production') { return 'production'; } // Default to development return 'development'; } /** * Determines whether to use structured (JSON) logging. * Structured logging is enabled if the `STRUCTURED_LOGGING` environment variable is 'true', * or if the environment is detected as 'pro' or 'aws-lambda'. * @returns `true` if structured logging should be used, otherwise `false`. */ shouldUseStructuredLogging() { // Explicit override if (process.env.STRUCTURED_LOGGING === 'true') { return true; } if (process.env.STRUCTURED_LOGGING === 'false') { return false; } // Auto-enable for production and AWS Lambda environments return (this.environment === 'production' || this.environment === 'aws-lambda'); } /** * Gets the singleton instance of the Logger. * @returns The singleton Logger instance. */ static getInstance() { if (!Logger.instance) { Logger.instance = new Logger(); } return Logger.instance; } /** * Sets the log level for the logger. Messages with a level lower than * the set level will not be logged. * @param level The log level to set. */ setLevel(level) { this.level = level; } /** * Gets the current log level of the logger. * @returns The current LogLevel. */ getLevel() { return this.level; } /** * Gets the name of the current log level. * @returns The name of the current log level (e.g., 'INFO', 'DEBUG'). */ getLevelName() { return LogLevel[this.level]; } /** * Gets the detected runtime environment. * @returns The name of the environment (e.g., 'production', 'development'). */ getEnvironment() { return this.environment; } /** * Enables or disables structured (JSON) logging. * @param enabled `true` to enable structured logging, `false` to disable it. */ setStructuredLogging(enabled) { this.useStructuredLogging = enabled; } /** * Checks if structured logging is currently enabled. * @returns `true` if structured logging is enabled, otherwise `false`. */ isStructuredLoggingEnabled() { return this.useStructuredLogging; } /** * Sets context that will be included in all subsequent log messages. * The new context is merged with any existing context. * @param newContext A partial LogContext object to merge into the current context. */ setContext(newContext) { this.context = { ...this.context, ...newContext }; } /** * Gets a copy of the current log context. * @returns A copy of the current LogContext. */ getContext() { return { ...this.context }; } /** * Clears the current log context. */ clearContext() { this.context = {}; } /** * Retrieves AWS Lambda-specific context from environment variables. * @returns An object containing AWS request ID, function name, trace ID, and environment. */ getAWSContext() { const awsContext = {}; if (process.env.AWS_REQUEST_ID) { awsContext.awsRequestId = process.env.AWS_REQUEST_ID; } if (process.env.AWS_LAMBDA_FUNCTION_NAME) { awsContext.functionName = process.env.AWS_LAMBDA_FUNCTION_NAME; } if (process.env._X_AMZN_TRACE_ID) { awsContext.xrayTraceId = process.env._X_AMZN_TRACE_ID; } // Include detected environment awsContext.environment = this.environment; return awsContext; } /** * Creates a structured log entry object. * @param level The log level name (e.g., 'ERROR'). * @param message The main log message. * @param data Additional data to include in the log. * @returns A StructuredLogEntry object. */ createStructuredLog(level, message, data) { const logEntry = { timestamp: new Date().toISOString(), level, message, ...this.getAWSContext(), }; // Add context if it exists if (Object.keys(this.context).length > 0) { logEntry.context = this.context; } // Add data if provided if (data !== undefined) { logEntry.data = data; } return logEntry; } /** * Formats a log message with context for plain text logging. * @param level The log level name (e.g., 'INFO'). * @param args The array of arguments to log. * @returns A formatted log string. */ formatWithContext(level, args) { const contextStr = Object.keys(this.context).length > 0 ? ` ${JSON.stringify(this.context)}` : ''; const argsStr = args .map(arg => { try { return typeof arg === 'string' ? arg : JSON.stringify(arg); } catch { return String(arg); } }) .join(' '); return `[${level}]${contextStr} ${argsStr}`; } /** * The main logging method that handles both structured and text logging. * It checks the log level and formats the message accordingly. * @param level The log level of the message. * @param levelName The name of the log level. * @param args The arguments to log. */ logMessage(level, levelName, ...args) { if (this.level < level) return; if (this.useStructuredLogging) { // Structured JSON logging const message = args.length > 0 ? String(args[0]) : ''; const data = args.length > 1 ? args.slice(1) : undefined; const structuredLog = this.createStructuredLog(levelName, message, data); const output = JSON.stringify(structuredLog); // Use appropriate console method if (level === LogLevel.ERROR) { // eslint-disable-next-line no-console console.error(output); } else if (level === LogLevel.WARN) { // eslint-disable-next-line no-console console.warn(output); } else if (level === LogLevel.INFO) { // eslint-disable-next-line no-console console.info(output); } else { // eslint-disable-next-line no-console console.debug(output); } } else { // Text logging (existing behavior) const message = this.formatWithContext(levelName, args); if (level === LogLevel.ERROR) { // eslint-disable-next-line no-console console.error(message); } else if (level === LogLevel.WARN) { // eslint-disable-next-line no-console console.warn(message); } else if (level === LogLevel.INFO) { // eslint-disable-next-line no-console console.info(message); } else { // eslint-disable-next-line no-console console.debug(message); } } } /** * Logs an error message. * @param args The arguments to log. */ error(...args) { this.logMessage(LogLevel.ERROR, 'ERROR', ...args); } /** * Logs a warning message. * @param args The arguments to log. */ warn(...args) { this.logMessage(LogLevel.WARN, 'WARN', ...args); } /** * Logs an informational message. * @param args The arguments to log. */ info(...args) { this.logMessage(LogLevel.INFO, 'INFO', ...args); } /** * Logs a debug message. * @param args The arguments to log. */ debug(...args) { this.logMessage(LogLevel.DEBUG, 'DEBUG', ...args); } /** * Logs a message directly to the console without level checking or formatting. * All arguments are stringified. * @param args The arguments to log. */ log(...args) { const stringifiedArgs = args.map(arg => { try { return typeof arg === 'string' ? arg : JSON.stringify(arg); } catch { return String(arg); } }); // eslint-disable-next-line no-console console.log(...stringifiedArgs); } } /** * # Usage in AWS Lambda with CloudWatch * * This logger is optimized for use within AWS Lambda functions, sending structured * JSON logs to Amazon CloudWatch. * * ## Structured Logging * * When running in a detected AWS Lambda environment, the logger automatically * switches to structured JSON format. This provides several advantages in CloudWatch: * * - **Searchable Logs**: Structured logs enable easy searching and filtering in CloudWatch Logs * based on JSON fields (e.g., `level`, `context.requestId`, `data.someValue`). * - **CloudWatch Logs Insights**: Structured logs can be queried with CloudWatch * Logs Insights for powerful analysis and visualization. * - **Automatic Context**: The logger automatically captures Lambda-specific context * like `awsRequestId` and `functionName`. * * ## Example Handler * * ```typescript * import { logger } from './util/log'; * * export const handler = async (event, context) => { * // Set context for all logs in this invocation for easy tracking * logger.setContext({ * awsRequestId: context.awsRequestId, * userId: event.arguments.userId, // Example * }); * * try { * logger.info('Function started', { input: event.arguments }); * * // ... business logic implementation ... * * const result = { success: true }; * logger.info('Function finished successfully', { result }); * return result; * * } catch (error) { * // Log the error with structured data * logger.error('An unhandled error occurred', { * error: error.message, * stack: error.stack, * }); * // Rethrow or handle as appropriate * throw error; * } finally { * // Clear context to avoid leaking information between invocations * logger.clearContext(); * } * }; * ``` */ // Export a singleton instance export const logger = Logger.getInstance(); let logLevel; try { // Remove the debug console.log(process) statement logLevel = parseInt(process?.env?.LOG_LEVEL || '3'); } catch { logLevel = 3; } logger.setLevel(logLevel); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9sb2cvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFDSCxNQUFNLENBQU4sSUFBWSxRQU1YO0FBTkQsV0FBWSxRQUFRO0lBQ2xCLHVDQUFRLENBQUE7SUFDUix5Q0FBUyxDQUFBO0lBQ1QsdUNBQVEsQ0FBQTtJQUNSLHVDQUFRLENBQUE7SUFDUix5Q0FBUyxDQUFBO0FBQ1gsQ0FBQyxFQU5XLFFBQVEsS0FBUixRQUFRLFFBTW5CO0FBNkJEOzs7O0dBSUc7QUFDSCxNQUFNLE1BQU07SUFDRixNQUFNLENBQUMsUUFBUSxDQUFTO0lBQ3hCLEtBQUssR0FBYSxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUMsZ0JBQWdCO0lBQ2pELE9BQU8sR0FBZSxFQUFFLENBQUM7SUFDekIsb0JBQW9CLEdBQVksS0FBSyxDQUFDO0lBQ3RDLFdBQVcsR0FBVyxhQUFhLENBQUM7SUFFNUMsd0RBQXdEO0lBQ3hEO1FBQ0UsNENBQTRDO1FBQzVDLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7UUFFNUMsMkRBQTJEO1FBQzNELElBQUksQ0FBQyxvQkFBb0IsR0FBRyxJQUFJLENBQUMsMEJBQTBCLEVBQUUsQ0FBQztJQUNoRSxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSyxpQkFBaUI7UUFDdkIsOERBQThEO1FBQzlELElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyxXQUFXLEtBQUssTUFBTSxFQUFFLENBQUM7WUFDdkMsT0FBTyxZQUFZLENBQUM7UUFDdEIsQ0FBQztRQUVELDhEQUE4RDtRQUM5RCxNQUFNLFlBQVksR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLHdCQUF3QixDQUFDO1FBQzFELElBQUksWUFBWSxJQUFJLFlBQVksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztZQUNwRCxPQUFPLFlBQVksQ0FBQztRQUN0QixDQUFD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QUFDOUIsTUFBTSxDQUFDLE1BQU0sTUFBTSxHQUFHLE1BQU0sQ0FBQyxXQUFXLEVBQUUsQ0FBQztBQUMzQyxJQUFJLFFBQVEsQ0FBQztBQUNiLElBQUksQ0FBQztJQUNILGtEQUFrRDtJQUNsRCxRQUFRLEdBQUcsUUFBUSxDQUFDLE9BQU8sRUFBRSxHQUFHLEVBQUUsU0FBUyxJQUFJLEdBQUcsQ0FBQyxDQUFDO0FBQ3RELENBQUM7QUFBQyxNQUFNLENBQUM7SUFDUCxRQUFRLEdBQUcsQ0FBQyxDQUFDO0FBQ2YsQ0FBQztBQUNELE1BQU0sQ0FBQyxRQUFRLENBQUMsUUFBb0IsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBEZWZpbmVzIHRoZSBsb2cgbGV2ZWxzIGZvciB0aGUgbG9nZ2VyLlxuICovXG5leHBvcnQgZW51bSBMb2dMZXZlbCB7XG4gIE5PTkUgPSAwLFxuICBFUlJPUiA9IDEsXG4gIFdBUk4gPSAyLFxuICBJTkZPID0gMyxcbiAgREVCVUcgPSA0LFxufVxuXG4vKipcbiAqIERlZmluZXMgdGhlIHN0cnVjdHVyZSBmb3IgY29udGV4dHVhbCBpbmZvcm1hdGlvbiBhdHRhY2hlZCB0byBsb2dzLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIExvZ0NvbnRleHQge1xuICByZXF1ZXN0SWQ/OiBzdHJpbmc7XG4gIG9wZXJhdGlvbj86IHN0cmluZztcbiAgdXNlcklkPzogc3RyaW5nO1xuICBba2V5OiBzdHJpbmddOiB1bmtub3duO1xufVxuXG4vKipcbiAqIERlZmluZXMgdGhlIHN0cnVjdHVyZSBmb3IgYSBzdHJ1Y3R1cmVkIGxvZyBlbnRyeSBpbiBKU09OIGZvcm1hdC5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBTdHJ1Y3R1cmVkTG9nRW50cnkge1xuICB0aW1lc3RhbXA6IHN0cmluZztcbiAgbGV2ZWw6IHN0cmluZztcbiAgbWVzc2FnZTogc3RyaW5nO1xuICBjb250ZXh0PzogTG9nQ29udGV4dDtcbiAgZGF0YT86IHVua25vd247XG4gIC8vIEFXUyBMYW1iZGEgY29udGV4dFxuICBhd3NSZXF1ZXN0SWQ/OiBzdHJpbmc7XG4gIGZ1bmN0aW9uTmFtZT86IHN0cmluZztcbiAgeHJheVRyYWNlSWQ/OiBzdHJpbmc7XG4gIC8vIEVudmlyb25tZW50IGluZm9cbiAgZW52aXJvbm1lbnQ/OiBzdHJpbmc7XG59XG5cbi8qKlxuICogQSBzaW5nbGV0b24gTG9nZ2VyIGNsYXNzIHRoYXQgcHJvdmlkZXMgc3RydWN0dXJlZCBhbmQgdGV4dC1iYXNlZCBsb2dnaW5nLlxuICogSXQgc3VwcG9ydHMgZGlmZmVyZW50IGxvZyBsZXZlbHMsIGNvbnRleHR1YWwgaW5mb3JtYXRpb24sIGFuZCBjYW4gYWRhcHRcbiAqIGl0cyBvdXRwdXQgZm9ybWF0IGJhc2VkIG9uIHRoZSBlbnZpcm9ubWVudCAoZS5nLiwgZGV2ZWxvcG1lbnQsIHByb2R1Y3Rpb24sIEFXUyBMYW1iZGEpLlxuICovXG5jbGFzcyBMb2dnZXIge1xuICBwcml2YXRlIHN0YXRpYyBpbnN0YW5jZTogTG9nZ2VyO1xuICBwcml2YXRlIGxldmVsOiBMb2dMZXZlbCA9IExvZ0xldmVsLklORk87IC8vIERlZmF1bHQgbGV2ZWxcbiAgcHJpdmF0ZSBjb250ZXh0OiBMb2dDb250ZXh0ID0ge307XG4gIHByaXZhdGUgdXNlU3RydWN0dXJlZExvZ2dpbmc6IGJvb2xlYW4gPSBmYWxzZTtcbiAgcHJpdmF0ZSBlbnZpcm9ubWVudDogc3RyaW5nID0gJ2RldmVsb3BtZW50JztcblxuICAvLyBNYWtlIGNvbnN0cnVjdG9yIHByaXZhdGUgdG8gZW5mb3JjZSBzaW5nbGV0b24gcGF0dGVyblxuICBwcml2YXRlIGNvbnN0cnVjdG9yKCkge1xuICAgIC8vIERldGVjdCBlbnZpcm9ubWVudCB1c2luZyBtdWx0aXBsZSBtZXRob2RzXG4gICAgdGhpcy5lbnZpcm9ubWVudCA9IHRoaXMuZGV0ZWN0RW52aXJvbm1lbnQoKTtcblxuICAgIC8vIEVuYWJsZSBzdHJ1Y3R1cmVkIGxvZ2dpbmcgYmFzZWQgb24gZW52aXJvbm1lbnQgZGV0ZWN0aW9uXG4gICAgdGhpcy51c2VTdHJ1Y3R1cmVkTG9nZ2luZyA9IHRoaXMuc2hvdWxkVXNlU3RydWN0dXJlZExvZ2dpbmcoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBEZXRlY3RzIHRoZSBjdXJyZW50IHJ1bnRpbWUgZW52aXJvbm1lbnQuXG4gICAqIEl0IGNoZWNrcyBmb3IgQW1wbGlmeS1zcGVjaWZpYyBlbnZpcm9ubWVudCB2YXJpYWJsZXMsIEFXUyBMYW1iZGEgZnVuY3Rpb24gbmFtZXMsXG4gICAqIGFuZCBzdGFuZGFyZCBgTk9ERV9FTlZgLiBEZWZhdWx0cyB0byAnZGV2ZWxvcG1lbnQnLlxuICAgKiBAcmV0dXJucyBUaGUgZGV0ZWN0ZWQgZW52aXJvbm1lbnQgbmFtZSAoZS5nLiwgJ3Byb2R1Y3Rpb24nLCAnZGV2ZWxvcG1lbnQnKS5cbiAgICovXG4gIHByaXZhdGUgZGV0ZWN0RW52aXJvbm1lbnQoKTogc3RyaW5nIHtcbiAgICAvLyBNZXRob2QgMTogRXhwbGljaXQgZW52aXJvbm1lbnQgdmFyaWFibGUgKHNldCBpbiBiYWNrZW5kLnRzKVxuICAgIGlmIChwcm9jZXNzLmVudi5lbnZpcm9ubWVudCA9PT0gJ3Byb2QnKSB7XG4gICAgICByZXR1cm4gJ3Byb2R1Y3Rpb24nO1xuICAgIH1cblxuICAgIC8vIE1ldGhvZCAyOiBBV1Mgc3RhY2sgbmFtZSBwYXR0ZXJuIChtYXRjaGVzIGJhY2tlbmQudHMgbG9naWMpXG4gICAgY29uc3QgZnVuY3Rpb25OYW1lID0gcHJvY2Vzcy5lbnYuQVdTX0xBTUJEQV9GVU5DVElPTl9OQU1FO1xuICAgIGlmIChmdW5jdGlvbk5hbWUgJiYgZnVuY3Rpb25OYW1lLmluY2x1ZGVzKCctbWFpbi0nKSkge1xuICAgICAgcmV0dXJuICdwcm9kdWN0aW9uJztcbiAgICB9XG5cbiAgICAvLyBNZXRob2QgMzogQVdTIGV4ZWN1dGlvbiBlbnZpcm9ubWVudFxuICAgIGlmIChwcm9jZXNzLmVudi5BV1NfRVhFQ1VUSU9OX0VOVikge1xuICAgICAgcmV0dXJuICdhd3MtbGFtYmRhJztcbiAgICB9XG5cbiAgICAvLyBNZXRob2QgNDogVHJhZGl0aW9uYWwgTk9ERV9FTlZcbiAgICBpZiAocHJvY2Vzcy5lbnYuTk9ERV9FTlYgPT09ICdwcm9kdWN0aW9uJykge1xuICAgICAgcmV0dXJuICdwcm9kdWN0aW9uJztcbiAgICB9XG5cbiAgICAvLyBEZWZhdWx0IHRvIGRldmVsb3BtZW50XG4gICAgcmV0dXJuICdkZXZlbG9wbWVudCc7XG4gIH1cblxuICAvKipcbiAgICogRGV0ZXJtaW5lcyB3aGV0aGVyIHRvIHVzZSBzdHJ1Y3R1cmVkIChKU09OKSBsb2dnaW5nLlxuICAgKiBTdHJ1Y3R1cmVkIGxvZ2dpbmcgaXMgZW5hYmxlZCBpZiB0aGUgYFNUUlVDVFVSRURfTE9HR0lOR2AgZW52aXJvbm1lbnQgdmFyaWFibGUgaXMgJ3RydWUnLFxuICAgKiBvciBpZiB0aGUgZW52aXJvbm1lbnQgaXMgZGV0ZWN0ZWQgYXMgJ3Bybycgb3IgJ2F3cy1sYW1iZGEnLlxuICAgKiBAcmV0dXJucyBgdHJ1ZWAgaWYgc3RydWN0dXJlZCBsb2dnaW5nIHNob3VsZCBiZSB1c2VkLCBvdGhlcndpc2UgYGZhbHNlYC5cbiAgICovXG4gIHByaXZhdGUgc2hvdWxkVXNlU3RydWN0dXJlZExvZ2dpbmcoKTogYm9vbGVhbiB7XG4gICAgLy8gRXhwbGljaXQgb3ZlcnJpZGVcbiAgICBpZiAocHJvY2Vzcy5lbnYuU1RSVUNUVVJFRF9MT0dHSU5HID09PSAndHJ1ZScpIHtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgICBpZiAocHJvY2Vzcy5lbnYuU1RSVUNUVVJFRF9MT0dHSU5HID09PSAnZmFsc2UnKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgLy8gQXV0by1lbmFibGUgZm9yIHByb2R1Y3Rpb24gYW5kIEFXUyBMYW1iZGEgZW52aXJvbm1lbnRzXG4gICAgcmV0dXJuIChcbiAgICAgIHRoaXMuZW52aXJvbm1lbnQgPT09ICdwcm9kdWN0aW9uJyB8fCB0aGlzLmVudmlyb25tZW50ID09PSAnYXdzLWxhbWJkYSdcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldHMgdGhlIHNpbmdsZXRvbiBpbnN0YW5jZSBvZiB0aGUgTG9nZ2VyLlxuICAgKiBAcmV0dXJucyBUaGUgc2luZ2xldG9uIExvZ2dlciBpbnN0YW5jZS5cbiAgICovXG4gIHB1YmxpYyBzdGF0aWMgZ2V0SW5zdGFuY2UoKTogTG9nZ2VyIHtcbiAgICBpZiAoIUxvZ2dlci5pbnN0YW5jZSkge1xuICAgICAgTG9nZ2VyLmluc3RhbmNlID0gbmV3IExvZ2dlcigpO1xuICAgIH1cbiAgICByZXR1cm4gTG9nZ2VyLmluc3RhbmNlO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldHMgdGhlIGxvZyBsZXZlbCBmb3IgdGhlIGxvZ2dlci4gTWVzc2FnZXMgd2l0aCBhIGxldmVsIGxvd2VyIHRoYW5cbiAgICogdGhlIHNldCBsZXZlbCB3aWxsIG5vdCBiZSBsb2dnZWQuXG4gICAqIEBwYXJhbSBsZXZlbCBUaGUgbG9nIGxldmVsIHRvIHNldC5cbiAgICovXG4gIHB1YmxpYyBzZXRMZXZlbChsZXZlbDogTG9nTGV2ZWwpOiB2b2lkIHtcbiAgICB0aGlzLmxldmVsID0gbGV2ZWw7XG4gIH1cblxuICAvKipcbiAgICogR2V0cyB0aGUgY3VycmVudCBsb2cgbGV2ZWwgb2YgdGhlIGxvZ2dlci5cbiAgICogQHJldHVybnMgVGhlIGN1cnJlbnQgTG9nTGV2ZWwuXG4gICAqL1xuICBwdWJsaWMgZ2V0TGV2ZWwoKTogTG9nTGV2ZWwge1xuICAgIHJldHVybiB0aGlzLmxldmVsO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldHMgdGhlIG5hbWUgb2YgdGhlIGN1cnJlbnQgbG9nIGxldmVsLlxuICAgKiBAcmV0dXJucyBUaGUgbmFtZSBvZiB0aGUgY3VycmVudCBsb2cgbGV2ZWwgKGUuZy4sICdJTkZPJywgJ0RFQlVHJykuXG4gICAqL1xuICBwdWJsaWMgZ2V0TGV2ZWxOYW1lKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIExvZ0xldmVsW3RoaXMubGV2ZWxdO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldHMgdGhlIGRldGVjdGVkIHJ1bnRpbWUgZW52aXJvbm1lbnQuXG4gICAqIEByZXR1cm5zIFRoZSBuYW1lIG9mIHRoZSBlbnZpcm9ubWVudCAoZS5nLiwgJ3Byb2R1Y3Rpb24nLCAnZGV2ZWxvcG1lbnQnKS5cbiAgICovXG4gIHB1YmxpYyBnZXRFbnZpcm9ubWVudCgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLmVudmlyb25tZW50O1xuICB9XG5cbiAgLyoqXG4gICAqIEVuYWJsZXMgb3IgZGlzYWJsZXMgc3RydWN0dXJlZCAoSlNPTikgbG9nZ2luZy5cbiAgICogQHBhcmFtIGVuYWJsZWQgYHRydWVgIHRvIGVuYWJsZSBzdHJ1Y3R1cmVkIGxvZ2dpbmcsIGBmYWxzZWAgdG8gZGlzYWJsZSBpdC5cbiAgICovXG4gIHB1YmxpYyBzZXRTdHJ1Y3R1cmVkTG9nZ2luZyhlbmFibGVkOiBib29sZWFuKTogdm9pZCB7XG4gICAgdGhpcy51c2VTdHJ1Y3R1cmVkTG9nZ2luZyA9IGVuYWJsZWQ7XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2tzIGlmIHN0cnVjdHVyZWQgbG9nZ2luZyBpcyBjdXJyZW50bHkgZW5hYmxlZC5cbiAgICogQHJldHVybnMgYHRydWVgIGlmIHN0cnVjdHVyZWQgbG9nZ2luZyBpcyBlbmFibGVkLCBvdGhlcndpc2UgYGZhbHNlYC5cbiAgICovXG4gIHB1YmxpYyBpc1N0cnVjdHVyZWRMb2dnaW5nRW5hYmxlZCgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy51c2VTdHJ1Y3R1cmVkTG9nZ2luZztcbiAgfVxuXG4gIC8qKlxuICAgKiBTZXRzIGNvbnRleHQgdGhhdCB3aWxsIGJlIGluY2x1ZGVkIGluIGFsbCBzdWJzZXF1ZW50IGxvZyBtZXNzYWdlcy5cbiAgICogVGhlIG5ldyBjb250ZXh0IGlzIG1lcmdlZCB3aXRoIGFueSBleGlzdGluZyBjb250ZXh0LlxuICAgKiBAcGFyYW0gbmV3Q29udGV4dCBBIHBhcnRpYWwgTG9nQ29udGV4dCBvYmplY3QgdG8gbWVyZ2UgaW50byB0aGUgY3VycmVudCBjb250ZXh0LlxuICAgKi9cbiAgcHVibGljIHNldENvbnRleHQobmV3Q29udGV4dDogUGFydGlhbDxMb2dDb250ZXh0Pik6IHZvaWQge1xuICAgIHRoaXMuY29udGV4dCA9IHsgLi4udGhpcy5jb250ZXh0LCAuLi5uZXdDb250ZXh0IH07XG4gIH1cblxuICAvKipcbiAgICogR2V0cyBhIGNvcHkgb2YgdGhlIGN1cnJlbnQgbG9nIGNvbnRleHQuXG4gICAqIEByZXR1cm5zIEEgY29weSBvZiB0aGUgY3VycmVudCBMb2dDb250ZXh0LlxuICAgKi9cbiAgcHVibGljIGdldENvbnRleHQoKTogTG9nQ29udGV4dCB7XG4gICAgcmV0dXJuIHsgLi4udGhpcy5jb250ZXh0IH07XG4gIH1cblxuICAvKipcbiAgICogQ2xlYXJzIHRoZSBjdXJyZW50IGxvZyBjb250ZXh0LlxuICAgKi9cbiAgcHVibGljIGNsZWFyQ29udGV4dCgpOiB2b2lkIHtcbiAgICB0aGlzLmNvbnRleHQgPSB7fTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXRyaWV2ZXMgQVdTIExhbWJkYS1zcGVjaWZpYyBjb250ZXh0IGZyb20gZW52aXJvbm1lbnQgdmFyaWFibGVzLlxuICAgKiBAcmV0dXJucyBBbiBvYmplY3QgY29udGFpbmluZyBBV1MgcmVxdWVzdCBJRCwgZnVuY3Rpb24gbmFtZSwgdHJhY2UgSUQsIGFuZCBlbnZpcm9ubWVudC5cbiAgICovXG4gIHByaXZhdGUgZ2V0QVdTQ29udGV4dCgpOiBQYXJ0aWFsPFN0cnVjdHVyZWRMb2dFbnRyeT4ge1xuICAgIGNvbnN0IGF3c0NvbnRleHQ6IFBhcnRpYWw8U3RydWN0dXJlZExvZ0VudHJ5PiA9IHt9O1xuXG4gICAgaWYgKHByb2Nlc3MuZW52LkFXU19SRVFVRVNUX0lEKSB7XG4gICAgICBhd3NDb250ZXh0LmF3c1JlcXVlc3RJZCA9IHByb2Nlc3MuZW52LkFXU19SRVFVRVNUX0lEO1xuICAgIH1cblxuICAgIGlmIChwcm9jZXNzLmVudi5BV1NfTEFNQkRBX0ZVTkNUSU9OX05BTUUpIHtcbiAgICAgIGF3c0NvbnRleHQuZnVuY3Rpb25OYW1lID0gcHJvY2Vzcy5lbnYuQVdTX0xBTUJEQV9GVU5DVElPTl9OQU1FO1xuICAgIH1cblxuICAgIGlmIChwcm9jZXNzLmVudi5fWF9BTVpOX1RSQUNFX0lEKSB7XG4gICAgICBhd3NDb250ZXh0LnhyYXlUcmFjZUlkID0gcHJvY2Vzcy5lbnYuX1hfQU1aTl9UUkFDRV9JRDtcbiAgICB9XG5cbiAgICAvLyBJbmNsdWRlIGRldGVjdGVkIGVudmlyb25tZW50XG4gICAgYXdzQ29udGV4dC5lbnZpcm9ubWVudCA9IHRoaXMuZW52aXJvbm1lbnQ7XG5cbiAgICByZXR1cm4gYXdzQ29udGV4dDtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgc3RydWN0dXJlZCBsb2cgZW50cnkgb2JqZWN0LlxuICAgKiBAcGFyYW0gbGV2ZWwgVGhlIGxvZyBsZXZlbCBuYW1lIChlLmcuLCAnRVJST1InKS5cbiAgICogQHBhcmFtIG1lc3NhZ2UgVGhlIG1haW4gbG9nIG1lc3NhZ2UuXG4gICAqIEBwYXJhbSBkYXRhIEFkZGl0aW9uYWwgZGF0YSB0byBpbmNsdWRlIGluIHRoZSBsb2cuXG4gICAqIEByZXR1cm5zIEEgU3RydWN0dXJlZExvZ0VudHJ5IG9iamVjdC5cbiAgICovXG4gIHByaXZhdGUgY3JlYXRlU3RydWN0dXJlZExvZyhcbiAgICBsZXZlbDogc3RyaW5nLFxuICAgIG1lc3NhZ2U6IHN0cmluZyxcbiAgICBkYXRhPzogdW5rbm93bixcbiAgKTogU3RydWN0dXJlZExvZ0VudHJ5IHtcbiAgICBjb25zdCBsb2dFbnRyeTogU3RydWN0dXJlZExvZ0VudHJ5ID0ge1xuICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgICBsZXZlbCxcbiAgICAgIG1lc3NhZ2UsXG4gICAgICAuLi50aGlzLmdldEFXU0NvbnRleHQoKSxcbiAgICB9O1xuXG4gICAgLy8gQWRkIGNvbnRleHQgaWYgaXQgZXhpc3RzXG4gICAgaWYgKE9iamVjdC5rZXlzKHRoaXMuY29udGV4dCkubGVuZ3RoID4gMCkge1xuICAgICAgbG9nRW50cnkuY29udGV4dCA9IHRoaXMuY29udGV4dDtcbiAgICB9XG5cbiAgICAvLyBBZGQgZGF0YSBpZiBwcm92aWRlZFxuICAgIGlmIChkYXRhICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGxvZ0VudHJ5LmRhdGEgPSBkYXRhO1xuICAgIH1cblxuICAgIHJldHVybiBsb2dFbnRyeTtcbiAgfVxuXG4gIC8qKlxuICAgKiBGb3JtYXRzIGEgbG9nIG1lc3NhZ2Ugd2l0aCBjb250ZXh0IGZvciBwbGFpbiB0ZXh0IGxvZ2dpbmcuXG4gICAqIEBwYXJhbSBsZXZlbCBUaGUgbG9nIGxldmVsIG5hbWUgKGUuZy4sICdJTkZPJykuXG4gICAqIEBwYXJhbSBhcmdzIFRoZSBhcnJheSBvZiBhcmd1bWVudHMgdG8gbG9nLlxuICAgKiBAcmV0dXJucyBBIGZvcm1hdHRlZCBsb2cgc3RyaW5nLlxuICAgKi9cbiAgcHJpdmF0ZSBmb3JtYXRXaXRoQ29udGV4dChsZXZlbDogc3RyaW5nLCBhcmdzOiB1bmtub3duW10pOiBzdHJpbmcge1xuICAgIGNvbnN0IGNvbnRleHRTdHIgPVxuICAgICAgT2JqZWN0LmtleXModGhpcy5jb250ZXh0KS5sZW5ndGggPiAwXG4gICAgICAgID8gYCAke0pTT04uc3RyaW5naWZ5KHRoaXMuY29udGV4dCl9YFxuICAgICAgICA6ICcnO1xuXG4gICAgY29uc3QgYXJnc1N0ciA9IGFyZ3NcbiAgICAgIC5tYXAoYXJnID0+IHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICByZXR1cm4gdHlwZW9mIGFyZyA9PT0gJ3N0cmluZycgPyBhcmcgOiBKU09OLnN0cmluZ2lmeShhcmcpO1xuICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICByZXR1cm4gU3RyaW5nKGFyZyk7XG4gICAgICAgIH1cbiAgICAgIH0pXG4gICAgICAuam9pbignICcpO1xuXG4gICAgcmV0dXJuIGBbJHtsZXZlbH1dJHtjb250ZXh0U3RyfSAke2FyZ3NTdHJ9YDtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgbWFpbiBsb2dnaW5nIG1ldGhvZCB0aGF0IGhhbmRsZXMgYm90aCBzdHJ1Y3R1cmVkIGFuZCB0ZXh0IGxvZ2dpbmcuXG4gICAqIEl0IGNoZWNrcyB0aGUgbG9nIGxldmVsIGFuZCBmb3JtYXRzIHRoZSBtZXNzYWdlIGFjY29yZGluZ2x5LlxuICAgKiBAcGFyYW0gbGV2ZWwgVGhlIGxvZyBsZXZlbCBvZiB0aGUgbWVzc2FnZS5cbiAgICogQHBhcmFtIGxldmVsTmFtZSBUaGUgbmFtZSBvZiB0aGUgbG9nIGxldmVsLlxuICAgKiBAcGFyYW0gYXJncyBUaGUgYXJndW1lbnRzIHRvIGxvZy5cbiAgICovXG4gIHByaXZhdGUgbG9nTWVzc2FnZShcbiAgICBsZXZlbDogTG9nTGV2ZWwsXG4gICAgbGV2ZWxOYW1lOiBzdHJpbmcsXG4gICAgLi4uYXJnczogdW5rbm93bltdXG4gICk6IHZvaWQge1xuICAgIGlmICh0aGlzLmxldmVsIDwgbGV2ZWwpIHJldHVybjtcblxuICAgIGlmICh0aGlzLnVzZVN0cnVjdHVyZWRMb2dnaW5nKSB7XG4gICAgICAvLyBTdHJ1Y3R1cmVkIEpTT04gbG9nZ2luZ1xuICAgICAgY29uc3QgbWVzc2FnZSA9IGFyZ3MubGVuZ3RoID4gMCA/IFN0cmluZyhhcmdzWzBdKSA6ICcnO1xuICAgICAgY29uc3QgZGF0YSA9IGFyZ3MubGVuZ3RoID4gMSA/IGFyZ3Muc2xpY2UoMSkgOiB1bmRlZmluZWQ7XG5cbiAgICAgIGNvbnN0IHN0cnVjdHVyZWRMb2cgPSB0aGlzLmNyZWF0ZVN0cnVjdHVyZWRMb2cobGV2ZWxOYW1lLCBtZXNzYWdlLCBkYXRhKTtcbiAgICAgIGNvbnN0IG91dHB1dCA9IEpTT04uc3RyaW5naWZ5KHN0cnVjdHVyZWRMb2cpO1xuXG4gICAgICAvLyBVc2UgYXBwcm9wcmlhdGUgY29uc29sZSBtZXRob2RcbiAgICAgIGlmIChsZXZlbCA9PT0gTG9nTGV2ZWwuRVJST1IpIHtcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICAgICAgY29uc29sZS5lcnJvcihvdXRwdXQpO1xuICAgICAgfSBlbHNlIGlmIChsZXZlbCA9PT0gTG9nTGV2ZWwuV0FSTikge1xuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICAgICAgICBjb25zb2xlLndhcm4ob3V0cHV0KTtcbiAgICAgIH0gZWxzZSBpZiAobGV2ZWwgPT09IExvZ0xldmVsLklORk8pIHtcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICAgICAgY29uc29sZS5pbmZvKG91dHB1dCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICAgICAgICBjb25zb2xlLmRlYnVnKG91dHB1dCk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIFRleHQgbG9nZ2luZyAoZXhpc3RpbmcgYmVoYXZpb3IpXG4gICAgICBjb25zdCBtZXNzYWdlID0gdGhpcy5mb3JtYXRXaXRoQ29udGV4dChsZXZlbE5hbWUsIGFyZ3MpO1xuXG4gICAgICBpZiAobGV2ZWwgPT09IExvZ0xldmVsLkVSUk9SKSB7XG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlXG4gICAgICAgIGNvbnNvbGUuZXJyb3IobWVzc2FnZSk7XG4gICAgICB9IGVsc2UgaWYgKGxldmVsID09PSBMb2dMZXZlbC5XQVJOKSB7XG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlXG4gICAgICAgIGNvbnNvbGUud2FybihtZXNzYWdlKTtcbiAgICAgIH0gZWxzZSBpZiAobGV2ZWwgPT09IExvZ0xldmVsLklORk8pIHtcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICAgICAgY29uc29sZS5pbmZvKG1lc3NhZ2UpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICAgICAgY29uc29sZS5kZWJ1ZyhtZXNzYWdlKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogTG9ncyBhbiBlcnJvciBtZXNzYWdlLlxuICAgKiBAcGFyYW0gYXJncyBUaGUgYXJndW1lbnRzIHRvIGxvZy5cbiAgICovXG4gIHB1YmxpYyBlcnJvciguLi5hcmdzOiB1bmtub3duW10pOiB2b2lkIHtcbiAgICB0aGlzLmxvZ01lc3NhZ2UoTG9nTGV2ZWwuRVJST1IsICdFUlJPUicsIC4uLmFyZ3MpO1xuICB9XG5cbiAgLyoqXG4gICAqIExvZ3MgYSB3YXJuaW5nIG1lc3NhZ2UuXG4gICAqIEBwYXJhbSBhcmdzIFRoZSBhcmd1bWVudHMgdG8gbG9nLlxuICAgKi9cbiAgcHVibGljIHdhcm4oLi4uYXJnczogdW5rbm93bltdKTogdm9pZCB7XG4gICAgdGhpcy5sb2dNZXNzYWdlKExvZ0xldmVsLldBUk4sICdXQVJOJywgLi4uYXJncyk7XG4gIH1cblxuICAvKipcbiAgICogTG9ncyBhbiBpbmZvcm1hdGlvbmFsIG1lc3NhZ2UuXG4gICAqIEBwYXJhbSBhcmdzIFRoZSBhcmd1bWVudHMgdG8gbG9nLlxuICAgKi9cbiAgcHVibGljIGluZm8oLi4uYXJnczogdW5rbm93bltdKTogdm9pZCB7XG4gICAgdGhpcy5sb2dNZXNzYWdlKExvZ0xldmVsLklORk8sICdJTkZPJywgLi4uYXJncyk7XG4gIH1cblxuICAvKipcbiAgICogTG9ncyBhIGRlYnVnIG1lc3NhZ2UuXG4gICAqIEBwYXJhbSBhcmdzIFRoZSBhcmd1bWVudHMgdG8gbG9nLlxuICAgKi9cbiAgcHVibGljIGRlYnVnKC4uLmFyZ3M6IHVua25vd25bXSk6IHZvaWQge1xuICAgIHRoaXMubG9nTWVzc2FnZShMb2dMZXZlbC5ERUJVRywgJ0RFQlVHJywgLi4uYXJncyk7XG4gIH1cblxuICAvKipcbiAgICogTG9ncyBhIG1lc3NhZ2UgZGlyZWN0bHkgdG8gdGhlIGNvbnNvbGUgd2l0aG91dCBsZXZlbCBjaGVja2luZyBvciBmb3JtYXR0aW5nLlxuICAgKiBBbGwgYXJndW1lbnRzIGFyZSBzdHJpbmdpZmllZC5cbiAgICogQHBhcmFtIGFyZ3MgVGhlIGFyZ3VtZW50cyB0byBsb2cuXG4gICAqL1xuICBwdWJsaWMgbG9nKC4uLmFyZ3M6IHVua25vd25bXSk6IHZvaWQge1xuICAgIGNvbnN0IHN0cmluZ2lmaWVkQXJncyA9IGFyZ3MubWFwKGFyZyA9PiB7XG4gICAgICB0cnkge1xuICAgICAgICByZXR1cm4gdHlwZW9mIGFyZyA9PT0gJ3N0cmluZycgPyBhcmcgOiBKU09OLnN0cmluZ2lmeShhcmcpO1xuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIHJldHVybiBTdHJpbmcoYXJnKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICAgIGNvbnNvbGUubG9nKC4uLnN0cmluZ2lmaWVkQXJncyk7XG4gIH1cbn1cblxuLyoqXG4gKiAjIFVzYWdlIGluIEFXUyBMYW1iZGEgd2l0aCBDbG91ZFdhdGNoXG4gKlxuICogVGhpcyBsb2dnZXIgaXMgb3B0aW1pemVkIGZvciB1c2Ugd2l0aGluIEFXUyBMYW1iZGEgZnVuY3Rpb25zLCBzZW5kaW5nIHN0cnVjdHVyZWRcbiAqIEpTT04gbG9ncyB0byBBbWF6b24gQ2xvdWRXYXRjaC5cbiAqXG4gKiAjIyBTdHJ1Y3R1cmVkIExvZ2dpbmdcbiAqXG4gKiBXaGVuIHJ1bm5pbmcgaW4gYSBkZXRlY3RlZCBBV1MgTGFtYmRhIGVudmlyb25tZW50LCB0aGUgbG9nZ2VyIGF1dG9tYXRpY2FsbHlcbiAqIHN3aXRjaGVzIHRvIHN0cnVjdHVyZWQgSlNPTiBmb3JtYXQuIFRoaXMgcHJvdmlkZXMgc2V2ZXJhbCBhZHZhbnRhZ2VzIGluIENsb3VkV2F0Y2g6XG4gKlxuICogLSAqKlNlYXJjaGFibGUgTG9ncyoqOiBTdHJ1Y3R1cmVkIGxvZ3MgZW5hYmxlIGVhc3kgc2VhcmNoaW5nIGFuZCBmaWx0ZXJpbmcgaW4gQ2xvdWRXYXRjaCBMb2dzXG4gKiAgIGJhc2VkIG9uIEpTT04gZmllbGRzIChlLmcuLCBgbGV2ZWxgLCBgY29udGV4dC5yZXF1ZXN0SWRgLCBgZGF0YS5zb21lVmFsdWVgKS5cbiAqIC0gKipDbG91ZFdhdGNoIExvZ3MgSW5zaWdodHMqKjogU3RydWN0dXJlZCBsb2dzIGNhbiBiZSBxdWVyaWVkIHdpdGggQ2xvdWRXYXRjaFxuICogICBMb2dzIEluc2lnaHRzIGZvciBwb3dlcmZ1bCBhbmFseXNpcyBhbmQgdmlzdWFsaXphdGlvbi5cbiAqIC0gKipBdXRvbWF0aWMgQ29udGV4dCoqOiBUaGUgbG9nZ2VyIGF1dG9tYXRpY2FsbHkgY2FwdHVyZXMgTGFtYmRhLXNwZWNpZmljIGNvbnRleHRcbiAqICAgbGlrZSBgYXdzUmVxdWVzdElkYCBhbmQgYGZ1bmN0aW9uTmFtZWAuXG4gKlxuICogIyMgRXhhbXBsZSBIYW5kbGVyXG4gKlxuICogYGBgdHlwZXNjcmlwdFxuICogaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi91dGlsL2xvZyc7XG4gKlxuICogZXhwb3J0IGNvbnN0IGhhbmRsZXIgPSBhc3luYyAoZXZlbnQsIGNvbnRleHQpID0+IHtcbiAqICAgLy8gU2V0IGNvbnRleHQgZm9yIGFsbCBsb2dzIGluIHRoaXMgaW52b2NhdGlvbiBmb3IgZWFzeSB0cmFja2luZ1xuICogICBsb2dnZXIuc2V0Q29udGV4dCh7XG4gKiAgICAgYXdzUmVxdWVzdElkOiBjb250ZXh0LmF3c1JlcXVlc3RJZCxcbiAqICAgICB1c2VySWQ6IGV2ZW50LmFyZ3VtZW50cy51c2VySWQsIC8vIEV4YW1wbGVcbiAqICAgfSk7XG4gKlxuICogICB0cnkge1xuICogICAgIGxvZ2dlci5pbmZvKCdGdW5jdGlvbiBzdGFydGVkJywgeyBpbnB1dDogZXZlbnQuYXJndW1lbnRzIH0pO1xuICpcbiAqICAgICAvLyAuLi4gYnVzaW5lc3MgbG9naWMgaW1wbGVtZW50YXRpb24gLi4uXG4gKlxuICogICAgIGNvbnN0IHJlc3VsdCA9IHsgc3VjY2VzczogdHJ1ZSB9O1xuICogICAgIGxvZ2dlci5pbmZvKCdGdW5jdGlvbiBmaW5pc2hlZCBzdWNjZXNzZnVsbHknLCB7IHJlc3VsdCB9KTtcbiAqICAgICByZXR1cm4gcmVzdWx0O1xuICpcbiAqICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAqICAgICAvLyBMb2cgdGhlIGVycm9yIHdpdGggc3RydWN0dXJlZCBkYXRhXG4gKiAgICAgbG9nZ2VyLmVycm9yKCdBbiB1bmhhbmRsZWQgZXJyb3Igb2NjdXJyZWQnLCB7XG4gKiAgICAgICBlcnJvcjogZXJyb3IubWVzc2FnZSxcbiAqICAgICAgIHN0YWNrOiBlcnJvci5zdGFjayxcbiAqICAgICB9KTtcbiAqICAgICAvLyBSZXRocm93IG9yIGhhbmRsZSBhcyBhcHByb3ByaWF0ZVxuICogICAgIHRocm93IGVycm9yO1xuICogICB9IGZpbmFsbHkge1xuICogICAgIC8vIENsZWFyIGNvbnRleHQgdG8gYXZvaWQgbGVha2luZyBpbmZvcm1hdGlvbiBiZXR3ZWVuIGludm9jYXRpb25zXG4gKiAgICAgbG9nZ2VyLmNsZWFyQ29udGV4dCgpO1xuICogICB9XG4gKiB9O1xuICogYGBgXG4gKi9cbi8vIEV4cG9ydCBhIHNpbmdsZXRvbiBpbnN0YW5jZVxuZXhwb3J0IGNvbnN0IGxvZ2dlciA9IExvZ2dlci5nZXRJbnN0YW5jZSgpO1xubGV0IGxvZ0xldmVsO1xudHJ5IHtcbiAgLy8gUmVtb3ZlIHRoZSBkZWJ1ZyBjb25zb2xlLmxvZyhwcm9jZXNzKSBzdGF0ZW1lbnRcbiAgbG9nTGV2ZWwgPSBwYXJzZUludChwcm9jZXNzPy5lbnY/LkxPR19MRVZFTCB8fCAnMycpO1xufSBjYXRjaCB7XG4gIGxvZ0xldmVsID0gMztcbn1cbmxvZ2dlci5zZXRMZXZlbChsb2dMZXZlbCBhcyBMb2dMZXZlbCk7XG4iXX0=