@energica-city/shared-amplify-utils
Version:
Shared utilities for AWS Amplify projects
379 lines • 39.1 kB
JavaScript
/**
* 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=