@energica-city/shared-amplify-utils
Version:
Shared utilities for AWS Amplify projects
283 lines • 32.8 kB
JavaScript
import { logger } from '../log';
/**
* Generic middleware chain implementation
*
* Provides Express-style middleware functionality for AWS Lambda handlers and GraphQL resolvers.
* Middleware executes in an "onion model" where each middleware wraps the next one in the chain.
*
* **Execution Flow:**
* ```
* Middleware 1 (before) →
* Middleware 2 (before) →
* Final Handler →
* Middleware 2 (after) ←
* Middleware 1 (after) ←
* ```
*
* **Input Mutation:**
* - Middleware can mutate the input object directly
* - Changes are visible to all subsequent middleware and the final handler
* - Consider input immutability for complex applications
*
* **Error Handling:**
* - Errors thrown by middleware are enhanced with chain context
* - Execution stops at the first error (no subsequent middleware execute)
* - Configure `onError` handler for centralized error processing
*
* **Performance:**
* - Each execution creates a closure chain - consider reusing chains for high-frequency operations
* - Debug logging adds overhead - disable in production
*
* @template TInput - Type of input data passed through the chain
* @template TOutput - Type of output returned by the chain
*
* @example
* ```typescript
* const chain = new MiddlewareChain<MyInput, MyOutput>({
* enableDebugLogging: true,
* onError: (error, middlewareName) => {
* console.error(`Middleware ${middlewareName} failed:`, error);
* }
* });
*
* chain
* .use('auth', authMiddleware)
* .use('logging', loggingMiddleware)
* .use('validation', validationMiddleware);
*
* const result = await chain.execute(input, finalHandler);
* ```
*/
export class MiddlewareChain {
middlewares = [];
config;
constructor(config = {}) {
this.config = config;
}
/**
* Create a middleware chain specifically for AWS Lambda handlers
*
* This is a convenience method that creates a chain with the standard
* Lambda input structure: `{ event: TEvent; context: TContext }`
*
* @template TEvent - Type of the Lambda event
* @template TContext - Type of the Lambda context
* @template TReturn - Type of the Lambda return value
* @param config - Configuration options for the chain
* @returns A new middleware chain configured for Lambda handlers
*
* @example
* ```typescript
* const chain = MiddlewareChain.createLambdaChain<APIGatewayProxyEvent, Context, APIGatewayProxyResult>();
* ```
*/
static createLambdaChain(config = {}) {
return new MiddlewareChain(config);
}
/**
* Create a middleware chain specifically for GraphQL resolvers
*
* This is a convenience method that creates a chain with the standard
* GraphQL resolver input structure: `{ source, args, context, info }`
*
* @template TArgs - Type of the GraphQL arguments
* @template TSource - Type of the GraphQL source
* @template TReturn - Type of the GraphQL return value
* @param config - Configuration options for the chain
* @returns A new middleware chain configured for GraphQL resolvers
*
* @example
* ```typescript
* const chain = MiddlewareChain.createGraphQLChain<MyArgs, MySource, MyReturn>();
* ```
*/
static createGraphQLChain(config = {}) {
return new MiddlewareChain(config);
}
/**
* Add middleware to the chain
*
* Middleware functions are executed in the order they are added.
* Each middleware receives the input and a `next` function to continue
* to the next middleware or final handler.
*
* @param name - Descriptive name for the middleware (used in error messages and logging)
* @param middleware - The middleware function to add
* @returns This chain instance for method chaining
*
* @example
* ```typescript
* chain
* .use('authentication', authMiddleware)
* .use('logging', loggingMiddleware)
* .use('validation', validationMiddleware);
* ```
*/
use(name, middleware) {
this.middlewares.push({ name, middleware });
return this;
}
/**
* Execute the middleware chain with the given input and final handler
*
* This method runs all middleware in the chain, followed by the final handler.
* If any middleware throws an error, execution stops and the error is enhanced
* with chain context before being re-thrown.
*
* @param input - The input data to pass through the middleware chain
* @param finalHandler - The final handler function that processes the input
* @returns Promise resolving to the output from the final handler
*
* @example
* ```typescript
* const result = await chain.execute(
* { userId: '123', data: { name: 'John' } },
* async (input) => {
* return await fetchUserData(input.userId);
* }
* );
* ```
*/
async execute(input, finalHandler) {
if (this.middlewares.length === 0) {
return finalHandler(input);
}
let index = 0;
const executeNext = async (modifiedInput) => {
if (index >= this.middlewares.length) {
return finalHandler(modifiedInput ?? input);
}
const { name, middleware } = this.middlewares[index];
index++;
if (this.config.enableDebugLogging) {
logger.debug(`Executing middleware: ${name}`);
}
try {
return await middleware(modifiedInput ?? input, executeNext);
}
catch (error) {
// Create enhanced error with chain context
const enhancedError = error instanceof Error ? error : new Error(String(error));
// Add middleware context without mutating original error
if (!enhancedError.middlewareName) {
enhancedError.middlewareName = name;
enhancedError.middlewareIndex = index - 1;
enhancedError.totalMiddlewares = this.middlewares.length;
enhancedError.middlewareChain = this.middlewares.map(m => m.name);
// Store original error if we created a new Error object
if (!(error instanceof Error)) {
enhancedError.originalError = error;
}
}
if (this.config.onError) {
this.config.onError(enhancedError, name);
}
throw enhancedError;
}
};
return executeNext();
}
/**
* Get the number of middlewares in the chain
*
* @returns The count of middleware functions currently in the chain
*
* @example
* ```typescript
* const chain = new MiddlewareChain();
* console.log(chain.length); // 0
*
* chain.use('auth', authMiddleware);
* chain.use('logging', loggingMiddleware);
* console.log(chain.length); // 2
* ```
*/
get length() {
return this.middlewares.length;
}
/**
* Clear all middlewares from the chain
*
* Removes all middleware functions, resetting the chain to empty state.
* Useful for reusing chain instances or cleaning up during testing.
*
* @returns This chain instance for method chaining
*
* @example
* ```typescript
* const chain = new MiddlewareChain();
* chain.use('auth', authMiddleware);
* chain.use('logging', loggingMiddleware);
*
* chain.clear(); // Chain is now empty
* console.log(chain.length); // 0
* ```
*/
clear() {
this.middlewares = [];
return this;
}
}
/**
* Wrap a Lambda handler with middleware chain functionality
*
* This function creates a new Lambda handler that executes the middleware chain
* before calling the original handler. The middleware chain receives the Lambda
* event and context as input.
*
* @template TEvent - Type of the Lambda event
* @template TContext - Type of the Lambda context
* @template TReturn - Type of the Lambda return value
* @param chain - The middleware chain to execute
* @param handler - The original Lambda handler function
* @returns A new Lambda handler function that includes middleware execution
*
* @example
* ```typescript
* const wrappedHandler = wrapLambdaHandler(
* chain,
* async (event, context) => {
* return { statusCode: 200, body: 'Hello World' };
* }
* );
* ```
*/
export function wrapLambdaHandler(chain, handler) {
return async (event, context) => {
return await chain.execute({ event, context }, async (input) => {
return await handler(input.event, input.context);
});
};
}
/**
* Wrap a GraphQL resolver with middleware chain functionality
*
* This function creates a new GraphQL resolver that executes the middleware chain
* before calling the original resolver. The middleware chain receives the GraphQL
* source, args, context, and info as input.
*
* @template TArgs - Type of the GraphQL arguments
* @template TSource - Type of the GraphQL source
* @template TReturn - Type of the GraphQL return value
* @param chain - The middleware chain to execute
* @param resolver - The original GraphQL resolver function
* @returns A new GraphQL resolver function that includes middleware execution
*
* @example
* ```typescript
* const wrappedResolver = wrapGraphQLResolver(
* chain,
* async (source, args, context, info) => {
* return await fetchUserData(args.id);
* }
* );
* ```
*/
export function wrapGraphQLResolver(chain, resolver) {
return async (source, args, context, info) => {
return await chain.execute({ source, args, context, info }, async (input) => {
return await resolver(input.source, input.args, input.context, input.info);
});
};
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWlkZGxld2FyZUNoYWluLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vbWlkZGxld2FyZS9taWRkbGV3YXJlQ2hhaW4udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLFFBQVEsQ0FBQztBQTBEaEM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWdERztBQUNILE1BQU0sT0FBTyxlQUFlO0lBQ2xCLFdBQVcsR0FHZCxFQUFFLENBQUM7SUFDQSxNQUFNLENBQXdCO0lBRXRDLFlBQVksU0FBZ0MsRUFBRTtRQUM1QyxJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQztJQUN2QixDQUFDO0lBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7T0FnQkc7SUFDSCxNQUFNLENBQUMsaUJBQWlCLENBS3RCLFNBQWdDLEVBQUU7UUFFbEMsT0FBTyxJQUFJLGVBQWUsQ0FDeEIsTUFBTSxDQUNQLENBQUM7SUFDSixDQUFDO0lBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7T0FnQkc7SUFDSCxNQUFNLENBQUMsa0JBQWtCLENBS3ZCLFNBQWdDLEVBQUU7UUFVbEMsT0FBTyxJQUFJLGVBQWUsQ0FReEIsTUFBTSxDQUFDLENBQUM7SUFDWixDQUFDO0lBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7OztPQWtCRztJQUNILEdBQUcsQ0FBQyxJQUFZLEVBQUUsVUFBdUM7UUFDdkQsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsRUFBRSxJQUFJLEVBQUUsVUFBVSxFQUFFLENBQUMsQ0FBQztRQUM1QyxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7T0FvQkc7SUFDSCxLQUFLLENBQUMsT0FBTyxDQUNYLEtBQWEsRUFDYixZQUFpRDtRQUVqRCxJQUFJLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQ2xDLE9BQU8sWUFBWSxD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QUMsQ0FBQztJQUNMLENBQUMsQ0FBQztBQUNKLENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F1Qkc7QUFDSCxNQUFNLFVBQVUsbUJBQW1CLENBS2pDLEtBUUMsRUFDRCxRQUsrQjtJQU8vQixPQUFPLEtBQUssRUFDVixNQUFlLEVBQ2YsSUFBVyxFQUNYLE9BQWdDLEVBQ2hDLElBQTZCLEVBQzdCLEVBQUU7UUFDRixPQUFPLE1BQU0sS0FBSyxDQUFDLE9BQU8sQ0FBQyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxFQUFFLEtBQUssRUFBQyxLQUFLLEVBQUMsRUFBRTtZQUN4RSxPQUFPLE1BQU0sUUFBUSxDQUNuQixLQUFLLENBQUMsTUFBTSxFQUNaLEtBQUssQ0FBQyxJQUFJLEVBQ1YsS0FBSyxDQUFDLE9BQU8sRUFDYixLQUFLLENBQUMsSUFBSSxDQUNYLENBQUM7UUFDSixDQUFDLENBQUMsQ0FBQztJQUNMLENBQUMsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuLi9sb2cnO1xuXG4vLyBGdXR1cmUgRW5oYW5jZW1lbnQ6IFJlc3BvbnNlIFRyYW5zZm9ybWF0aW9uIFN1cHBvcnRcbi8vIFBsYW5uZWQgZmVhdHVyZXMgZm9yIHJlc3BvbnNlIHByb2Nlc3Npbmc6XG4vLyAtIHVzZVJlc3BvbnNlVHJhbnNmb3JtZXIoKSBtZXRob2QgZm9yIHJlc3BvbnNlLW9ubHkgdHJhbnNmb3JtYXRpb25zXG4vLyAtIHVzZUZ1bGwoKSBtZXRob2QgZm9yIGNvbWJpbmVkIHJlcXVlc3QvcmVzcG9uc2UgbWlkZGxld2FyZVxuLy8gLSBSZXNwb25zZVRyYW5zZm9ybWVyPFRJbnB1dCwgVE91dHB1dD4gdHlwZSBmb3IgdHlwZS1zYWZlIHJlc3BvbnNlIG1vZGlmaWNhdGlvbnNcbi8vIFRoZXNlIGZlYXR1cmVzIHdpbGwgcHJvdmlkZSBjbGVhbmVyIHBhdHRlcm5zIGZvciByZXNwb25zZSBwcm9jZXNzaW5nICh0aW1lc3RhbXBzLCBjb21wcmVzc2lvbiwgaGVhZGVycywgZXRjLilcblxuLyoqXG4gKiBCYXNlIG1pZGRsZXdhcmUgZnVuY3Rpb24gdHlwZVxuICpcbiAqIE1pZGRsZXdhcmUgZm9sbG93cyB0aGUgXCJvbmlvbiBtb2RlbFwiIHBhdHRlcm46XG4gKiAxLiBNaWRkbGV3YXJlIGV4ZWN1dGVzIGluIHRoZSBvcmRlciB0aGV5IHdlcmUgYWRkZWRcbiAqIDIuIEVhY2ggbWlkZGxld2FyZSBjYW4gbW9kaWZ5IGlucHV0IGJlZm9yZSBjYWxsaW5nIG5leHQoKVxuICogMy4gRWFjaCBtaWRkbGV3YXJlIGNhbiBwcm9jZXNzIHRoZSByZXN1bHQgYWZ0ZXIgbmV4dCgpIHJldHVybnNcbiAqIDQuIElmIG5leHQoKSBpcyBub3QgY2FsbGVkLCB0aGUgY2hhaW4gc3RvcHMgYW5kIHJlbWFpbmluZyBtaWRkbGV3YXJlL2hhbmRsZXIgYXJlIHNraXBwZWRcbiAqXG4gKiBAdGVtcGxhdGUgVElucHV0IC0gVHlwZSBvZiBpbnB1dCBwYXNzZWQgdG8gbWlkZGxld2FyZVxuICogQHRlbXBsYXRlIFRPdXRwdXQgLSBUeXBlIG9mIG91dHB1dCByZXR1cm5lZCBieSBtaWRkbGV3YXJlIGNoYWluXG4gKiBAcGFyYW0gaW5wdXQgLSBUaGUgaW5wdXQgZGF0YSBwYXNzZWQgdGhyb3VnaCB0aGUgY2hhaW5cbiAqIEBwYXJhbSBuZXh0IC0gRnVuY3Rpb24gdG8gY29udGludWUgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBvciBmaW5hbCBoYW5kbGVyXG4gKiBAcmV0dXJucyBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgb3V0cHV0IG9mIHRoZSBjaGFpblxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBjb25zdCBsb2dnaW5nTWlkZGxld2FyZTogTWlkZGxld2FyZTxNeUlucHV0LCBNeU91dHB1dD4gPSBhc3luYyAoaW5wdXQsIG5leHQpID0+IHtcbiAqICAgY29uc29sZS5sb2coJ0JlZm9yZSBoYW5kbGVyJyk7XG4gKiAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IG5leHQoKTtcbiAqICAgY29uc29sZS5sb2coJ0FmdGVyIGhhbmRsZXInKTtcbiAqICAgcmV0dXJuIHJlc3VsdDtcbiAqIH07XG4gKiBgYGBcbiAqL1xuZXhwb3J0IHR5cGUgTWlkZGxld2FyZTxUSW5wdXQgPSB1bmtub3duLCBUT3V0cHV0ID0gdW5rbm93bj4gPSAoXG4gIGlucHV0OiBUSW5wdXQsXG4gIG5leHQ6IChpbnB1dD86IFRJbnB1dCkgPT4gUHJvbWlzZTxUT3V0cHV0PixcbikgPT4gUHJvbWlzZTxUT3V0cHV0PjtcblxuLyoqXG4gKiBFbmhhbmNlZCBlcnJvciB3aXRoIG1pZGRsZXdhcmUgY2hhaW4gY29udGV4dFxuICovXG5leHBvcnQgaW50ZXJmYWNlIE1pZGRsZXdhcmVFcnJvciBleHRlbmRzIEVycm9yIHtcbiAgbWlkZGxld2FyZU5hbWU/OiBzdHJpbmc7XG4gIG1pZGRsZXdhcmVJbmRleD86IG51bWJlcjtcbiAgdG90YWxNaWRkbGV3YXJlcz86IG51bWJlcjtcbiAgbWlkZGxld2FyZUNoYWluPzogc3RyaW5nW107XG4gIG9yaWdpbmFsRXJyb3I/OiB1bmtub3duO1xufVxuXG4vKipcbiAqIENvbmZpZ3VyYXRpb24gZm9yIG1pZGRsZXdhcmUgY2hhaW5cbiAqL1xuaW50ZXJmYWNlIE1pZGRsZXdhcmVDaGFpbkNvbmZpZyB7XG4gIGVuYWJsZURlYnVnTG9nZ2luZz86IGJvb2xlYW47XG4gIG9uRXJyb3I/OiAoZXJyb3I6IHVua25vd24sIG1pZGRsZXdhcmVOYW1lOiBzdHJpbmcpID0+IHZvaWQ7XG59XG5cbi8qKlxuICogR2VuZXJpYyBtaWRkbGV3YXJlIGNoYWluIGltcGxlbWVudGF0aW9uXG4gKlxuICogUHJvdmlkZXMgRXhwcmVzcy1zdHlsZSBtaWRkbGV3YXJlIGZ1bmN0aW9uYWxpdHkgZm9yIEFXUyBMYW1iZGEgaGFuZGxlcnMgYW5kIEdyYXBoUUwgcmVzb2x2ZXJzLlxuICogTWlkZGxld2FyZSBleGVjdXRlcyBpbiBhbiBcIm9uaW9uIG1vZGVsXCIgd2hlcmUgZWFjaCBtaWRkbGV3YXJlIHdyYXBzIHRoZSBuZXh0IG9uZSBpbiB0aGUgY2hhaW4uXG4gKlxuICogKipFeGVjdXRpb24gRmxvdzoqKlxuICogYGBgXG4gKiBNaWRkbGV3YXJlIDEgKGJlZm9yZSkg4oaSXG4gKiAgIE1pZGRsZXdhcmUgMiAoYmVmb3JlKSDihpJcbiAqICAgICBGaW5hbCBIYW5kbGVyIOKGklxuICogICBNaWRkbGV3YXJlIDIgKGFmdGVyKSDihpBcbiAqIE1pZGRsZXdhcmUgMSAoYWZ0ZXIpIOKGkFxuICogYGBgXG4gKlxuICogKipJbnB1dCBNdXRhdGlvbjoqKlxuICogLSBNaWRkbGV3YXJlIGNhbiBtdXRhdGUgdGhlIGlucHV0IG9iamVjdCBkaXJlY3RseVxuICogLSBDaGFuZ2VzIGFyZSB2aXNpYmxlIHRvIGFsbCBzdWJzZXF1ZW50IG1pZGRsZXdhcmUgYW5kIHRoZSBmaW5hbCBoYW5kbGVyXG4gKiAtIENvbnNpZGVyIGlucHV0IGltbXV0YWJpbGl0eSBmb3IgY29tcGxleCBhcHBsaWNhdGlvbnNcbiAqXG4gKiAqKkVycm9yIEhhbmRsaW5nOioqXG4gKiAtIEVycm9ycyB0aHJvd24gYnkgbWlkZGxld2FyZSBhcmUgZW5oYW5jZWQgd2l0aCBjaGFpbiBjb250ZXh0XG4gKiAtIEV4ZWN1dGlvbiBzdG9wcyBhdCB0aGUgZmlyc3QgZXJyb3IgKG5vIHN1YnNlcXVlbnQgbWlkZGxld2FyZSBleGVjdXRlKVxuICogLSBDb25maWd1cmUgYG9uRXJyb3JgIGhhbmRsZXIgZm9yIGNlbnRyYWxpemVkIGVycm9yIHByb2Nlc3NpbmdcbiAqXG4gKiAqKlBlcmZvcm1hbmNlOioqXG4gKiAtIEVhY2ggZXhlY3V0aW9uIGNyZWF0ZXMgYSBjbG9zdXJlIGNoYWluIC0gY29uc2lkZXIgcmV1c2luZyBjaGFpbnMgZm9yIGhpZ2gtZnJlcXVlbmN5IG9wZXJhdGlvbnNcbiAqIC0gRGVidWcgbG9nZ2luZyBhZGRzIG92ZXJoZWFkIC0gZGlzYWJsZSBpbiBwcm9kdWN0aW9uXG4gKlxuICogQHRlbXBsYXRlIFRJbnB1dCAtIFR5cGUgb2YgaW5wdXQgZGF0YSBwYXNzZWQgdGhyb3VnaCB0aGUgY2hhaW5cbiAqIEB0ZW1wbGF0ZSBUT3V0cHV0IC0gVHlwZSBvZiBvdXRwdXQgcmV0dXJuZWQgYnkgdGhlIGNoYWluXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGNvbnN0IGNoYWluID0gbmV3IE1pZGRsZXdhcmVDaGFpbjxNeUlucHV0LCBNeU91dHB1dD4oe1xuICogICBlbmFibGVEZWJ1Z0xvZ2dpbmc6IHRydWUsXG4gKiAgIG9uRXJyb3I6IChlcnJvciwgbWlkZGxld2FyZU5hbWUpID0+IHtcbiAqICAgICBjb25zb2xlLmVycm9yKGBNaWRkbGV3YXJlICR7bWlkZGxld2FyZU5hbWV9IGZhaWxlZDpgLCBlcnJvcik7XG4gKiAgIH1cbiAqIH0pO1xuICpcbiAqIGNoYWluXG4gKiAgIC51c2UoJ2F1dGgnLCBhdXRoTWlkZGxld2FyZSlcbiAqICAgLnVzZSgnbG9nZ2luZycsIGxvZ2dpbmdNaWRkbGV3YXJlKVxuICogICAudXNlKCd2YWxpZGF0aW9uJywgdmFsaWRhdGlvbk1pZGRsZXdhcmUpO1xuICpcbiAqIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGNoYWluLmV4ZWN1dGUoaW5wdXQsIGZpbmFsSGFuZGxlcik7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGNsYXNzIE1pZGRsZXdhcmVDaGFpbjxUSW5wdXQgPSB1bmtub3duLCBUT3V0cHV0ID0gdW5rbm93bj4ge1xuICBwcml2YXRlIG1pZGRsZXdhcmVzOiBBcnJheTx7XG4gICAgbmFtZTogc3RyaW5nO1xuICAgIG1pZGRsZXdhcmU6IE1pZGRsZXdhcmU8VElucHV0LCBUT3V0cHV0PjtcbiAgfT4gPSBbXTtcbiAgcHJpdmF0ZSBjb25maWc6IE1pZGRsZXdhcmVDaGFpbkNvbmZpZztcblxuICBjb25zdHJ1Y3Rvcihjb25maWc6IE1pZGRsZXdhcmVDaGFpbkNvbmZpZyA9IHt9KSB7XG4gICAgdGhpcy5jb25maWcgPSBjb25maWc7XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlIGEgbWlkZGxld2FyZSBjaGFpbiBzcGVjaWZpY2FsbHkgZm9yIEFXUyBMYW1iZGEgaGFuZGxlcnNcbiAgICpcbiAgICogVGhpcyBpcyBhIGNvbnZlbmllbmNlIG1ldGhvZCB0aGF0IGNyZWF0ZXMgYSBjaGFpbiB3aXRoIHRoZSBzdGFuZGFyZFxuICAgKiBMYW1iZGEgaW5wdXQgc3RydWN0dXJlOiBgeyBldmVudDogVEV2ZW50OyBjb250ZXh0OiBUQ29udGV4dCB9YFxuICAgKlxuICAgKiBAdGVtcGxhdGUgVEV2ZW50IC0gVHlwZSBvZiB0aGUgTGFtYmRhIGV2ZW50XG4gICAqIEB0ZW1wbGF0ZSBUQ29udGV4dCAtIFR5cGUgb2YgdGhlIExhbWJkYSBjb250ZXh0XG4gICAqIEB0ZW1wbGF0ZSBUUmV0dXJuIC0gVHlwZSBvZiB0aGUgTGFtYmRhIHJldHVybiB2YWx1ZVxuICAgKiBAcGFyYW0gY29uZmlnIC0gQ29uZmlndXJhdGlvbiBvcHRpb25zIGZvciB0aGUgY2hhaW5cbiAgICogQHJldHVybnMgQSBuZXcgbWlkZGxld2FyZSBjaGFpbiBjb25maWd1cmVkIGZvciBMYW1iZGEgaGFuZGxlcnNcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBjb25zdCBjaGFpbiA9IE1pZGRsZXdhcmVDaGFpbi5jcmVhdGVMYW1iZGFDaGFpbjxBUElHYXRld2F5UHJveHlFdmVudCwgQ29udGV4dCwgQVBJR2F0ZXdheVByb3h5UmVzdWx0PigpO1xuICAgKiBgYGBcbiAgICovXG4gIHN0YXRpYyBjcmVhdGVMYW1iZGFDaGFpbjxcbiAgICBURXZlbnQgPSBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbiAgICBUQ29udGV4dCA9IFJlY29yZDxzdHJpbmcsIHVua25vd24+LFxuICAgIFRSZXR1cm4gPSBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbiAgPihcbiAgICBjb25maWc6IE1pZGRsZXdhcmVDaGFpbkNvbmZpZyA9IHt9LFxuICApOiBNaWRkbGV3YXJlQ2hhaW48eyBldmVudDogVEV2ZW50OyBjb250ZXh0OiBUQ29udGV4dCB9LCBUUmV0dXJuPiB7XG4gICAgcmV0dXJuIG5ldyBNaWRkbGV3YXJlQ2hhaW48eyBldmVudDogVEV2ZW50OyBjb250ZXh0OiBUQ29udGV4dCB9LCBUUmV0dXJuPihcbiAgICAgIGNvbmZpZyxcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZSBhIG1pZGRsZXdhcmUgY2hhaW4gc3BlY2lmaWNhbGx5IGZvciBHcmFwaFFMIHJlc29sdmVyc1xuICAgKlxuICAgKiBUaGlzIGlzIGEgY29udmVuaWVuY2UgbWV0aG9kIHRoYXQgY3JlYXRlcyBhIGNoYWluIHdpdGggdGhlIHN0YW5kYXJkXG4gICAqIEdyYXBoUUwgcmVzb2x2ZXIgaW5wdXQgc3RydWN0dXJlOiBgeyBzb3VyY2UsIGFyZ3MsIGNvbnRleHQsIGluZm8gfWBcbiAgICpcbiAgICogQHRlbXBsYXRlIFRBcmdzIC0gVHlwZSBvZiB0aGUgR3JhcGhRTCBhcmd1bWVudHNcbiAgICogQHRlbXBsYXRlIFRTb3VyY2UgLSBUeXBlIG9mIHRoZSBHcmFwaFFMIHNvdXJjZVxuICAgKiBAdGVtcGxhdGUgVFJldHVybiAtIFR5cGUgb2YgdGhlIEdyYXBoUUwgcmV0dXJuIHZhbHVlXG4gICAqIEBwYXJhbSBjb25maWcgLSBDb25maWd1cmF0aW9uIG9wdGlvbnMgZm9yIHRoZSBjaGFpblxuICAgKiBAcmV0dXJucyBBIG5ldyBtaWRkbGV3YXJlIGNoYWluIGNvbmZpZ3VyZWQgZm9yIEdyYXBoUUwgcmVzb2x2ZXJzXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogY29uc3QgY2hhaW4gPSBNaWRkbGV3YXJlQ2hhaW4uY3JlYXRlR3JhcGhRTENoYWluPE15QXJncywgTXlTb3VyY2UsIE15UmV0dXJuPigpO1xuICAgKiBgYGBcbiAgICovXG4gIHN0YXRpYyBjcmVhdGVHcmFwaFFMQ2hhaW48XG4gICAgVEFyZ3MgPSBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbiAgICBUU291cmNlID0gdW5rbm93bixcbiAgICBUUmV0dXJuID0gdW5rbm93bixcbiAgPihcbiAgICBjb25maWc6IE1pZGRsZXdhcmVDaGFpbkNvbmZpZyA9IHt9LFxuICApOiBNaWRkbGV3YXJlQ2hhaW48XG4gICAge1xuICAgICAgc291cmNlOiBUU291cmNlO1xuICAgICAgYXJnczogVEFyZ3M7XG4gICAgICBjb250ZXh0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICAgIGluZm86IFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICAgIH0sXG4gICAgVFJldHVyblxuICA+IHtcbiAgICByZXR1cm4gbmV3IE1pZGRsZXdhcmVDaGFpbjxcbiAgICAgIHtcbiAgICAgICAgc291cmNlOiBUU291cmNlO1xuICAgICAgICBhcmdzOiBUQXJncztcbiAgICAgICAgY29udGV4dDogUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gICAgICAgIGluZm86IFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICAgICAgfSxcbiAgICAgIFRSZXR1cm5cbiAgICA+KGNvbmZpZyk7XG4gIH1cblxuICAvKipcbiAgICogQWRkIG1pZGRsZXdhcmUgdG8gdGhlIGNoYWluXG4gICAqXG4gICAqIE1pZGRsZXdhcmUgZnVuY3Rpb25zIGFyZSBleGVjdXRlZCBpbiB0aGUgb3JkZXIgdGhleSBhcmUgYWRkZWQuXG4gICAqIEVhY2ggbWlkZGxld2FyZSByZWNlaXZlcyB0aGUgaW5wdXQgYW5kIGEgYG5leHRgIGZ1bmN0aW9uIHRvIGNvbnRpbnVlXG4gICAqIHRvIHRoZSBuZXh0IG1pZGRsZXdhcmUgb3IgZmluYWwgaGFuZGxlci5cbiAgICpcbiAgICogQHBhcmFtIG5hbWUgLSBEZXNjcmlwdGl2ZSBuYW1lIGZvciB0aGUgbWlkZGxld2FyZSAodXNlZCBpbiBlcnJvciBtZXNzYWdlcyBhbmQgbG9nZ2luZylcbiAgICogQHBhcmFtIG1pZGRsZXdhcmUgLSBUaGUgbWlkZGxld2FyZSBmdW5jdGlvbiB0byBhZGRcbiAgICogQHJldHVybnMgVGhpcyBjaGFpbiBpbnN0YW5jZSBmb3IgbWV0aG9kIGNoYWluaW5nXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogY2hhaW5cbiAgICogICAudXNlKCdhdXRoZW50aWNhdGlvbicsIGF1dGhNaWRkbGV3YXJlKVxuICAgKiAgIC51c2UoJ2xvZ2dpbmcnLCBsb2dnaW5nTWlkZGxld2FyZSlcbiAgICogICAudXNlKCd2YWxpZGF0aW9uJywgdmFsaWRhdGlvbk1pZGRsZXdhcmUpO1xuICAgKiBgYGBcbiAgICovXG4gIHVzZShuYW1lOiBzdHJpbmcsIG1pZGRsZXdhcmU6IE1pZGRsZXdhcmU8VElucHV0LCBUT3V0cHV0Pik6IHRoaXMge1xuICAgIHRoaXMubWlkZGxld2FyZXMucHVzaCh7IG5hbWUsIG1pZGRsZXdhcmUgfSk7XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICAvKipcbiAgICogRXhlY3V0ZSB0aGUgbWlkZGxld2FyZSBjaGFpbiB3aXRoIHRoZSBnaXZlbiBpbnB1dCBhbmQgZmluYWwgaGFuZGxlclxuICAgKlxuICAgKiBUaGlzIG1ldGhvZCBydW5zIGFsbCBtaWRkbGV3YXJlIGluIHRoZSBjaGFpbiwgZm9sbG93ZWQgYnkgdGhlIGZpbmFsIGhhbmRsZXIuXG4gICAqIElmIGFueSBtaWRkbGV3YXJlIHRocm93cyBhbiBlcnJvciwgZXhlY3V0aW9uIHN0b3BzIGFuZCB0aGUgZXJyb3IgaXMgZW5oYW5jZWRcbiAgICogd2l0aCBjaGFpbiBjb250ZXh0IGJlZm9yZSBiZWluZyByZS10aHJvd24uXG4gICAqXG4gICAqIEBwYXJhbSBpbnB1dCAtIFRoZSBpbnB1dCBkYXRhIHRvIHBhc3MgdGhyb3VnaCB0aGUgbWlkZGxld2FyZSBjaGFpblxuICAgKiBAcGFyYW0gZmluYWxIYW5kbGVyIC0gVGhlIGZpbmFsIGhhbmRsZXIgZnVuY3Rpb24gdGhhdCBwcm9jZXNzZXMgdGhlIGlucHV0XG4gICAqIEByZXR1cm5zIFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSBvdXRwdXQgZnJvbSB0aGUgZmluYWwgaGFuZGxlclxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGNoYWluLmV4ZWN1dGUoXG4gICAqICAgeyB1c2VySWQ6ICcxMjMnLCBkYXRhOiB7IG5hbWU6ICdKb2huJyB9IH0sXG4gICAqICAgYXN5bmMgKGlucHV0KSA9PiB7XG4gICAqICAgICByZXR1cm4gYXdhaXQgZmV0Y2hVc2VyRGF0YShpbnB1dC51c2VySWQpO1xuICAgKiAgIH1cbiAgICogKTtcbiAgICogYGBgXG4gICAqL1xuICBhc3luYyBleGVjdXRlKFxuICAgIGlucHV0OiBUSW5wdXQsXG4gICAgZmluYWxIYW5kbGVyOiAoaW5wdXQ6IFRJbnB1dCkgPT4gUHJvbWlzZTxUT3V0cHV0PixcbiAgKTogUHJvbWlzZTxUT3V0cHV0PiB7XG4gICAgaWYgKHRoaXMubWlkZGxld2FyZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICByZXR1cm4gZmluYWxIYW5kbGVyKGlucHV0KTtcbiAgICB9XG5cbiAgICBsZXQgaW5kZXggPSAwO1xuXG4gICAgY29uc3QgZXhlY3V0ZU5leHQgPSBhc3luYyAobW9kaWZpZWRJbnB1dD86IFRJbnB1dCk6IFByb21pc2U8VE91dHB1dD4gPT4ge1xuICAgICAgaWYgKGluZGV4ID49IHRoaXMubWlkZGxld2FyZXMubGVuZ3RoKSB7XG4gICAgICAgIHJldHVybiBmaW5hbEhhbmRsZXIobW9kaWZpZWRJbnB1dCA/PyBpbnB1dCk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHsgbmFtZSwgbWlkZGxld2FyZSB9ID0gdGhpcy5taWRkbGV3YXJlc1tpbmRleF07XG4gICAgICBpbmRleCsrO1xuXG4gICAgICBpZiAodGhpcy5jb25maWcuZW5hYmxlRGVidWdMb2dnaW5nKSB7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZyhgRXhlY3V0aW5nIG1pZGRsZXdhcmU6ICR7bmFtZX1gKTtcbiAgICAgIH1cblxuICAgICAgdHJ5IHtcbiAgICAgICAgcmV0dXJuIGF3YWl0IG1pZGRsZXdhcmUobW9kaWZpZWRJbnB1dCA/PyBpbnB1dCwgZXhlY3V0ZU5leHQpO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgLy8gQ3JlYXRlIGVuaGFuY2VkIGVycm9yIHdpdGggY2hhaW4gY29udGV4dFxuICAgICAgICBjb25zdCBlbmhhbmNlZEVycm9yOiBNaWRkbGV3YXJlRXJyb3IgPVxuICAgICAgICAgIGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvciA6IG5ldyBFcnJvcihTdHJpbmcoZXJyb3IpKTtcblxuICAgICAgICAvLyBBZGQgbWlkZGxld2FyZSBjb250ZXh0IHdpdGhvdXQgbXV0YXRpbmcgb3JpZ2luYWwgZXJyb3JcbiAgICAgICAgaWYgKCFlbmhhbmNlZEVycm9yLm1pZGRsZXdhcmVOYW1lKSB7XG4gICAgICAgICAgZW5oYW5jZWRFcnJvci5taWRkbGV3YXJlTmFtZSA9IG5hbWU7XG4gICAgICAgICAgZW5oYW5jZWRFcnJvci5taWRkbGV3YXJlSW5kZXggPSBpbmRleCAtIDE7XG4gICAgICAgICAgZW5oYW5jZWRFcnJvci50b3RhbE1pZGRsZXdhcmVzID0gdGhpcy5taWRkbGV3YXJlcy5sZW5ndGg7XG4gICAgICAgICAgZW5oYW5jZWRFcnJvci5taWRkbGV3YXJlQ2hhaW4gPSB0aGlzLm1pZGRsZXdhcmVzLm1hcChtID0+IG0ubmFtZSk7XG5cbiAgICAgICAgICAvLyBTdG9yZSBvcmlnaW5hbCBlcnJvciBpZiB3ZSBjcmVhdGVkIGEgbmV3IEVycm9yIG9iamVjdFxuICAgICAgICAgIGlmICghKGVycm9yIGluc3RhbmNlb2YgRXJyb3IpKSB7XG4gICAgICAgICAgICBlbmhhbmNlZEVycm9yLm9yaWdpbmFsRXJyb3IgPSBlcnJvcjtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5jb25maWcub25FcnJvcikge1xuICAgICAgICAgIHRoaXMuY29uZmlnLm9uRXJyb3IoZW5oYW5jZWRFcnJvciwgbmFtZSk7XG4gICAgICAgIH1cbiAgICAgICAgdGhyb3cgZW5oYW5jZWRFcnJvcjtcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgcmV0dXJuIGV4ZWN1dGVOZXh0KCk7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSBudW1iZXIgb2YgbWlkZGxld2FyZXMgaW4gdGhlIGNoYWluXG4gICAqXG4gICAqIEByZXR1cm5zIFRoZSBjb3VudCBvZiBtaWRkbGV3YXJlIGZ1bmN0aW9ucyBjdXJyZW50bHkgaW4gdGhlIGNoYWluXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogY29uc3QgY2hhaW4gPSBuZXcgTWlkZGxld2FyZUNoYWluKCk7XG4gICAqIGNvbnNvbGUubG9nKGNoYWluLmxlbmd0aCk7IC8vIDBcbiAgICpcbiAgICogY2hhaW4udXNlKCdhdXRoJywgYXV0aE1pZGRsZXdhcmUpO1xuICAgKiBjaGFpbi51c2UoJ2xvZ2dpbmcnLCBsb2dnaW5nTWlkZGxld2FyZSk7XG4gICAqIGNvbnNvbGUubG9nKGNoYWluLmxlbmd0aCk7IC8vIDJcbiAgICogYGBgXG4gICAqL1xuICBnZXQgbGVuZ3RoKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMubWlkZGxld2FyZXMubGVuZ3RoO1xuICB9XG5cbiAgLyoqXG4gICAqIENsZWFyIGFsbCBtaWRkbGV3YXJlcyBmcm9tIHRoZSBjaGFpblxuICAgKlxuICAgKiBSZW1vdmVzIGFsbCBtaWRkbGV3YXJlIGZ1bmN0aW9ucywgcmVzZXR0aW5nIHRoZSBjaGFpbiB0byBlbXB0eSBzdGF0ZS5cbiAgICogVXNlZnVsIGZvciByZXVzaW5nIGNoYWluIGluc3RhbmNlcyBvciBjbGVhbmluZyB1cCBkdXJpbmcgdGVzdGluZy5cbiAgICpcbiAgICogQHJldHVybnMgVGhpcyBjaGFpbiBpbnN0YW5jZSBmb3IgbWV0aG9kIGNoYWluaW5nXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogY29uc3QgY2hhaW4gPSBuZXcgTWlkZGxld2FyZUNoYWluKCk7XG4gICAqIGNoYWluLnVzZSgnYXV0aCcsIGF1dGhNaWRkbGV3YXJlKTtcbiAgICogY2hhaW4udXNlKCdsb2dnaW5nJywgbG9nZ2luZ01pZGRsZXdhcmUpO1xuICAgKlxuICAgKiBjaGFpbi5jbGVhcigpOyAvLyBDaGFpbiBpcyBub3cgZW1wdHlcbiAgICogY29uc29sZS5sb2coY2hhaW4ubGVuZ3RoKTsgLy8gMFxuICAgKiBgYGBcbiAgICovXG4gIGNsZWFyKCk6IHRoaXMge1xuICAgIHRoaXMubWlkZGxld2FyZXMgPSBbXTtcbiAgICByZXR1cm4gdGhpcztcbiAgfVxufVxuXG4vKipcbiAqIFdyYXAgYSBMYW1iZGEgaGFuZGxlciB3aXRoIG1pZGRsZXdhcmUgY2hhaW4gZnVuY3Rpb25hbGl0eVxuICpcbiAqIFRoaXMgZnVuY3Rpb24gY3JlYXRlcyBhIG5ldyBMYW1iZGEgaGFuZGxlciB0aGF0IGV4ZWN1dGVzIHRoZSBtaWRkbGV3YXJlIGNoYWluXG4gKiBiZWZvcmUgY2FsbGluZyB0aGUgb3JpZ2luYWwgaGFuZGxlci4gVGhlIG1pZGRsZXdhcmUgY2hhaW4gcmVjZWl2ZXMgdGhlIExhbWJkYVxuICogZXZlbnQgYW5kIGNvbnRleHQgYXMgaW5wdXQuXG4gKlxuICogQHRlbXBsYXRlIFRFdmVudCAtIFR5cGUgb2YgdGhlIExhbWJkYSBldmVudFxuICogQHRlbXBsYXRlIFRDb250ZXh0IC0gVHlwZSBvZiB0aGUgTGFtYmRhIGNvbnRleHRcbiAqIEB0ZW1wbGF0ZSBUUmV0dXJuIC0gVHlwZSBvZiB0aGUgTGFtYmRhIHJldHVybiB2YWx1ZVxuICogQHBhcmFtIGNoYWluIC0gVGhlIG1pZGRsZXdhcmUgY2hhaW4gdG8gZXhlY3V0ZVxuICogQHBhcmFtIGhhbmRsZXIgLSBUaGUgb3JpZ2luYWwgTGFtYmRhIGhhbmRsZXIgZnVuY3Rpb25cbiAqIEByZXR1cm5zIEEgbmV3IExhbWJkYSBoYW5kbGVyIGZ1bmN0aW9uIHRoYXQgaW5jbHVkZXMgbWlkZGxld2FyZSBleGVjdXRpb25cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogY29uc3Qgd3JhcHBlZEhhbmRsZXIgPSB3cmFwTGFtYmRhSGFuZGxlcihcbiAqICAgY2hhaW4sXG4gKiAgIGFzeW5jIChldmVudCwgY29udGV4dCkgPT4ge1xuICogICAgIHJldHVybiB7IHN0YXR1c0NvZGU6IDIwMCwgYm9keTogJ0hlbGxvIFdvcmxkJyB9O1xuICogICB9XG4gKiApO1xuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB3cmFwTGFtYmRhSGFuZGxlcjxcbiAgVEV2ZW50ID0gUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gIFRDb250ZXh0ID0gUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gIFRSZXR1cm4gPSB1bmtub3duLFxuPihcbiAgY2hhaW46IE1pZGRsZXdhcmVDaGFpbjx7IGV2ZW50OiBURXZlbnQ7IGNvbnRleHQ6IFRDb250ZXh0IH0sIFRSZXR1cm4+LFxuICBoYW5kbGVyOiAoZXZlbnQ6IFRFdmVudCwgY29udGV4dDogVENvbnRleHQpID0+IFByb21pc2U8VFJldHVybj4sXG4pOiAoZXZlbnQ6IFRFdmVudCwgY29udGV4dDogVENvbnRleHQpID0+IFByb21pc2U8VFJldHVybj4ge1xuICByZXR1cm4gYXN5bmMgKGV2ZW50OiBURXZlbnQsIGNvbnRleHQ6IFRDb250ZXh0KSA9PiB7XG4gICAgcmV0dXJuIGF3YWl0IGNoYWluLmV4ZWN1dGUoeyBldmVudCwgY29udGV4dCB9LCBhc3luYyBpbnB1dCA9PiB7XG4gICAgICByZXR1cm4gYXdhaXQgaGFuZGxlcihpbnB1dC5ldmVudCwgaW5wdXQuY29udGV4dCk7XG4gICAgfSk7XG4gIH07XG59XG5cbi8qKlxuICogV3JhcCBhIEdyYXBoUUwgcmVzb2x2ZXIgd2l0aCBtaWRkbGV3YXJlIGNoYWluIGZ1bmN0aW9uYWxpdHlcbiAqXG4gKiBUaGlzIGZ1bmN0aW9uIGNyZWF0ZXMgYSBuZXcgR3JhcGhRTCByZXNvbHZlciB0aGF0IGV4ZWN1dGVzIHRoZSBtaWRkbGV3YXJlIGNoYWluXG4gKiBiZWZvcmUgY2FsbGluZyB0aGUgb3JpZ2luYWwgcmVzb2x2ZXIuIFRoZSBtaWRkbGV3YXJlIGNoYWluIHJlY2VpdmVzIHRoZSBHcmFwaFFMXG4gKiBzb3VyY2UsIGFyZ3MsIGNvbnRleHQsIGFuZCBpbmZvIGFzIGlucHV0LlxuICpcbiAqIEB0ZW1wbGF0ZSBUQXJncyAtIFR5cGUgb2YgdGhlIEdyYXBoUUwgYXJndW1lbnRzXG4gKiBAdGVtcGxhdGUgVFNvdXJjZSAtIFR5cGUgb2YgdGhlIEdyYXBoUUwgc291cmNlXG4gKiBAdGVtcGxhdGUgVFJldHVybiAtIFR5cGUgb2YgdGhlIEdyYXBoUUwgcmV0dXJuIHZhbHVlXG4gKiBAcGFyYW0gY2hhaW4gLSBUaGUgbWlkZGxld2FyZSBjaGFpbiB0byBleGVjdXRlXG4gKiBAcGFyYW0gcmVzb2x2ZXIgLSBUaGUgb3JpZ2luYWwgR3JhcGhRTCByZXNvbHZlciBmdW5jdGlvblxuICogQHJldHVybnMgQSBuZXcgR3JhcGhRTCByZXNvbHZlciBmdW5jdGlvbiB0aGF0IGluY2x1ZGVzIG1pZGRsZXdhcmUgZXhlY3V0aW9uXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGNvbnN0IHdyYXBwZWRSZXNvbHZlciA9IHdyYXBHcmFwaFFMUmVzb2x2ZXIoXG4gKiAgIGNoYWluLFxuICogICBhc3luYyAoc291cmNlLCBhcmdzLCBjb250ZXh0LCBpbmZvKSA9PiB7XG4gKiAgICAgcmV0dXJuIGF3YWl0IGZldGNoVXNlckRhdGEoYXJncy5pZCk7XG4gKiAgIH1cbiAqICk7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHdyYXBHcmFwaFFMUmVzb2x2ZXI8XG4gIFRBcmdzID0gUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gIFRTb3VyY2UgPSB1bmtub3duLFxuICBUUmV0dXJuID0gdW5rbm93bixcbj4oXG4gIGNoYWluOiBNaWRkbGV3YXJlQ2hhaW48XG4gICAge1xuICAgICAgc291cmNlOiBUU291cmNlO1xuICAgICAgYXJnczogVEFyZ3M7XG4gICAgICBjb250ZXh0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICAgIGluZm86IFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICAgIH0sXG4gICAgVFJldHVyblxuICA+LFxuICByZXNvbHZlcjogKFxuICAgIHNvdXJjZTogVFNvdXJjZSxcbiAgICBhcmdzOiBUQXJncyxcbiAgICBjb250ZXh0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbiAgICBpbmZvOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbiAgKSA9PiBQcm9taXNlPFRSZXR1cm4+IHwgVFJldHVybixcbik6IChcbiAgc291cmNlOiBUU291cmNlLFxuICBhcmdzOiBUQXJncyxcbiAgY29udGV4dDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gIGluZm86IFJlY29yZDxzdHJpbmcsIHVua25vd24+LFxuKSA9PiBQcm9taXNlPFRSZXR1cm4+IHtcbiAgcmV0dXJuIGFzeW5jIChcbiAgICBzb3VyY2U6IFRTb3VyY2UsXG4gICAgYXJnczogVEFyZ3MsXG4gICAgY29udGV4dDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gICAgaW5mbzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gICkgPT4ge1xuICAgIHJldHVybiBhd2FpdCBjaGFpbi5leGVjdXRlKHsgc291cmNlLCBhcmdzLCBjb250ZXh0LCBpbmZvIH0sIGFzeW5jIGlucHV0ID0+IHtcbiAgICAgIHJldHVybiBhd2FpdCByZXNvbHZlcihcbiAgICAgICAgaW5wdXQuc291cmNlLFxuICAgICAgICBpbnB1dC5hcmdzLFxuICAgICAgICBpbnB1dC5jb250ZXh0LFxuICAgICAgICBpbnB1dC5pbmZvLFxuICAgICAgKTtcbiAgICB9KTtcbiAgfTtcbn1cbiJdfQ==