UNPKG

@energica-city/shared-amplify-utils

Version:

Shared utilities for AWS Amplify projects

127 lines 17.4 kB
import { logger } from '../../log'; import { extractErrorMessage, createErrorContext } from '../../error'; /** * Helper to check if error is from our error library. * This looks for properties that throwError always adds to identify our errors. */ function isOurError(error) { return (error instanceof Error && Object.prototype.hasOwnProperty.call(error, '__fromErrorLibrary')); } /** * Helper to safely extract AppSync event properties. */ function extractAppSyncEventInfo(event) { const eventInfo = event.info; return { arguments: event.arguments || {}, identity: event.identity || null, info: eventInfo ? { fieldName: eventInfo.fieldName, parentTypeName: eventInfo.parentTypeName, variables: eventInfo.variables || {}, } : undefined, }; } /** * Helper to build structured log context for GraphQL errors. */ function buildGraphQLLogContext(input, error, defaultContext) { const eventInfo = extractAppSyncEventInfo(input.event); const errorWithMiddleware = error; const context = input.context; const getMiddlewareChainString = () => { const chain = errorWithMiddleware.middlewareChain; if (Array.isArray(chain)) { return chain.join(' -> '); } return undefined; }; return createErrorContext({ ...defaultContext, // Include any additional properties from the error first ...Object.fromEntries(Object.entries(error).filter(([key]) => !['name', 'message', 'stack'].includes(key))), requestId: context.awsRequestId || 'unknown', functionName: context.functionName, graphql: { typeName: eventInfo.info?.parentTypeName, fieldName: eventInfo.info?.fieldName, arguments: eventInfo.arguments, }, identity: eventInfo.identity, failedMiddleware: errorWithMiddleware.middlewareName, middlewareChain: getMiddlewareChainString(), }); } /** * Creates an error handling middleware for AppSync GraphQL Lambda resolvers. * * This middleware catches all errors from downstream middleware or the resolver, * provides structured logging with GraphQL-specific context, and then re-throws * the error to let AppSync handle the final response to the client. This ensures * consistent error logging without interfering with AppSync's native error handling. * * It should be the first middleware in the chain to catch all subsequent errors. * * @param config - Configuration options for error handling behavior. * @returns A middleware function that handles and logs errors. * * @example * ```typescript * const chain = MiddlewareChain.createLambdaChain(); * * // Add error handling (should be the first middleware) * chain.use('errorHandler', createGraphQLErrorHandler({ * includeStackTrace: process.env.NODE_ENV === 'development', * defaultContext: { service: 'inventory-api', version: '1.2.0' } * })); * * // Add other middleware * chain.use('auth', authMiddleware); * * const handler = wrapLambdaHandler(chain, async (event, context) => { * // No try/catch needed here; errors are handled automatically * const item = await getItemById(event.arguments.id); * return item; * }); * ``` */ export function createGraphQLErrorHandler(config = {}) { const { includeStackTrace = process.env.NODE_ENV === 'development', defaultContext = {}, } = config; return async (input, next) => { try { return await next(); } catch (error) { let standardError; let errorMessage; // Handle non-standard errors by creating a standard error with context if (!isOurError(error)) { logger.warn('Non-standard error thrown, wrapping with throwError', { error, }); // Create a standard error manually with the same structure as throwError const wrappedMessage = 'Non-standard error thrown to GraphQL error handler'; standardError = new Error(wrappedMessage); // Add properties that throwError would have added standardError.errorType = typeof error; standardError.originalError = error; errorMessage = wrappedMessage; } else { standardError = error; errorMessage = extractErrorMessage(standardError); } const logContext = buildGraphQLLogContext(input, standardError, defaultContext); logger.error(`GraphQL resolver error: ${errorMessage}`, { ...logContext, ...(includeStackTrace && { stack: standardError.stack }), originalError: standardError.originalError || standardError, }); throw standardError; } }; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiR3JhcGhRTEVycm9ySGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL21pZGRsZXdhcmUvZ3JhcGhxbC9HcmFwaFFMRXJyb3JIYW5kbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDbkMsT0FBTyxFQUFFLG1CQUFtQixFQUFFLGtCQUFrQixFQUFFLE1BQU0sYUFBYSxDQUFDO0FBYXRFOzs7R0FHRztBQUNILFNBQVMsVUFBVSxDQUNqQixLQUFjO0lBRWQsT0FBTyxDQUNMLEtBQUssWUFBWSxLQUFLO1FBQ3RCLE1BQU0sQ0FBQyxTQUFTLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsb0JBQW9CLENBQUMsQ0FDbEUsQ0FBQztBQUNKLENBQUM7QUFFRDs7R0FFRztBQUNILFNBQVMsdUJBQXVCLENBQUMsS0FBOEI7SUFXN0QsTUFBTSxTQUFTLEdBQUcsS0FBSyxDQUFDLElBQTJDLENBQUM7SUFFcEUsT0FBTztRQUNMLFNBQVMsRUFBRyxLQUFLLENBQUMsU0FBcUMsSUFBSSxFQUFFO1FBQzdELFFBQVEsRUFBRyxLQUFLLENBQUMsUUFBMkMsSUFBSSxJQUFJO1FBQ3BFLElBQUksRUFBRSxTQUFTO1lBQ2IsQ0FBQyxDQUFDO2dCQUNFLFNBQVMsRUFBRSxTQUFTLENBQUMsU0FBK0I7Z0JBQ3BELGNBQWMsRUFBRSxTQUFTLENBQUMsY0FBb0M7Z0JBQzlELFNBQVMsRUFBRyxTQUFTLENBQUMsU0FBcUMsSUFBSSxFQUFFO2FBQ2xFO1lBQ0gsQ0FBQyxDQUFDLFNBQVM7S0FDZCxDQUFDO0FBQ0osQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBUyxzQkFBc0IsQ0FDN0IsS0FHQyxFQUNELEtBQXlDLEVBQ3pDLGNBQXVDO0lBRXZDLE1BQU0sU0FBUyxHQUFHLHVCQUF1QixDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUN2RCxNQUFNLG1CQUFtQixHQUFHLEtBQXdCLENBQUM7SUFDckQsTUFBTSxPQUFPLEdBQUcsS0FBSyxDQUFDLE9BQWtDLENBQUM7SUFFekQsTUFBTSx3QkFBd0IsR0FBRyxHQUF1QixFQUFFO1FBQ3hELE1BQU0sS0FBSyxHQUFHLG1CQUFtQixDQUFDLGVBQWUsQ0FBQztRQUNsRCxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUN6QixPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDNUIsQ0FBQztRQUNELE9BQU8sU0FBUyxDQUFDO0lBQ25CLENBQUMsQ0FBQztJQUVGLE9BQU8sa0JBQWtCLENBQUM7UUFDeEIsR0FBRyxjQUFjO1FBQ2pCLHlEQUF5RDtRQUN6RCxHQUFHLE1BQU0sQ0FBQyxXQUFXLENBQ25CLE1BQU0sQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsTUFBTSxDQUMxQixDQUFDLENBQUMsR0FBRyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxNQUFNLEVBQUUsU0FBUyxFQUFFLE9BQU8sQ0FBQyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FDdkQsQ0FDRjtRQUNELFNBQVMsRUFBRSxPQUFPLENBQUMsWUFBWSxJQUFJLFNBQVM7UUFDNUMsWUFBWSxFQUFFLE9BQU8sQ0FBQyxZQUFZO1FBQ2xDLE9BQU8sRUFBRTtZQUNQLFFBQVEsRUFBRSxTQUFTLENBQUMsSUFBSSxFQUFFLGNBQWM7WUFDeEMsU0FBUyxFQUFFLFNBQVMsQ0FBQyxJQUFJLEVBQUUsU0FBUztZQUNwQyxTQUFTLEVBQUUsU0FBUyxDQUFDLFNBQVM7U0FDL0I7UUFDRCxRQUFRLEVBQUUsU0FBUyxDQUFDLFFBQVE7UUFDNUIsZ0JBQWdCLEVBQUUsbUJBQW1CLENBQUMsY0FBYztRQUNwRCxlQUFlLEVBQUUsd0JBQXdCLEVBQUU7S0FDNUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWdDRztBQUNILE1BQU0sVUFBVSx5QkFBeUIsQ0FNdkMsU0FBb0MsRUFBRTtJQUN0QyxNQUFNLEVBQ0osaUJBQWlCLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxRQUFRLEtBQUssYUFBYSxFQUMxRCxjQUFjLEdBQUcsRUFBRSxHQUNwQixHQUFHLE1BQU0sQ0FBQztJQUVYLE9BQU8sS0FBSyxFQUFFLEtBQWEsRUFBRSxJQUE0QixFQUFFLEVBQUU7UUFDM0QsSUFBSSxDQUFDO1lBQ0gsT0FBTyxNQUFNLElBQUksRUFBRSxDQUFDO1FBQ3RCLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsSUFBSSxhQUFpRCxDQUFDO1lBQ3RELElBQUksWUFBb0IsQ0FBQztZQUV6Qix1RUFBdUU7WUFDdkUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO2dCQUN2QixNQUFNLENBQUMsSUFBSSxDQUFDLHFEQUFxRCxFQUFFO29CQUNqRSxLQUFLO2lCQUNOLENBQUMsQ0FBQztnQkFFSCx5RUFBeUU7Z0JBQ3pFLE1BQU0sY0FBYyxHQUNsQixvREFBb0QsQ0FBQztnQkFDdkQsYUFBYSxHQUFHLElBQUksS0FBSyxDQUFDLGNBQWMsQ0FFdkMsQ0FBQztnQkFFRixrREFBa0Q7Z0JBQ2xELGFBQWEsQ0FBQyxTQUFTLEdBQUcsT0FBTyxLQUFLLENBQUM7Z0JBQ3ZDLGFBQWEsQ0FBQyxhQUFhLEdBQUcsS0FBSyxDQUFDO2dCQUVwQyxZQUFZLEdBQUcsY0FBYyxDQUFDO1lBQ2hDLENBQUM7aUJBQU0sQ0FBQztnQkFDTixhQUFhLEdBQUcsS0FBSyxDQUFDO2dCQUN0QixZQUFZLEdBQUcsbUJBQW1CLENBQUMsYUFBYSxDQUFDLENBQUM7WUFDcEQsQ0FBQztZQUVELE1BQU0sVUFBVSxHQUFHLHNCQUFzQixDQUN2QyxLQUFLLEVBQ0wsYUFBYSxFQUNiLGNBQWMsQ0FDZixDQUFDO1lBRUYsTUFBTSxDQUFDLEtBQUssQ0FBQywyQkFBMkIsWUFBWSxFQUFFLEVBQUU7Z0JBQ3RELEdBQUcsVUFBVTtnQkFDYixHQUFHLENBQUMsaUJBQWlCLElBQUksRUFBRSxLQUFLLEVBQUUsYUFBYSxDQUFDLEtBQUssRUFBRSxDQUFDO2dCQUN4RCxhQUFhLEVBQUUsYUFBYSxDQUFDLGFBQWEsSUFBSSxhQUFhO2FBQzVELENBQUMsQ0FBQztZQUVILE1BQU0sYUFBYSxDQUFDO1FBQ3RCLENBQUM7SUFDSCxDQUFDLENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vLi4vbG9nJztcbmltcG9ydCB7IGV4dHJhY3RFcnJvck1lc3NhZ2UsIGNyZWF0ZUVycm9yQ29udGV4dCB9IGZyb20gJy4uLy4uL2Vycm9yJztcbmltcG9ydCB0eXBlIHsgTWlkZGxld2FyZSwgTWlkZGxld2FyZUVycm9yIH0gZnJvbSAnLi4vbWlkZGxld2FyZUNoYWluJztcblxuLyoqXG4gKiBDb25maWd1cmF0aW9uIG9wdGlvbnMgZm9yIHRoZSBHcmFwaFFMIGVycm9yIGhhbmRsaW5nIG1pZGRsZXdhcmUuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgR3JhcGhRTEVycm9ySGFuZGxlckNvbmZpZyB7XG4gIC8qKiBXaGV0aGVyIHRvIGluY2x1ZGUgc3RhY2sgdHJhY2VzIGluIGxvZ3MgKGRlZmF1bHQ6IGZhbHNlIGluIHByb2R1Y3Rpb24pLiAqL1xuICBpbmNsdWRlU3RhY2tUcmFjZT86IGJvb2xlYW47XG4gIC8qKiBBZGRpdGlvbmFsIGNvbnRleHQgdG8gaW5jbHVkZSB3aXRoIGFsbCBsb2dnZWQgZXJyb3JzLiAqL1xuICBkZWZhdWx0Q29udGV4dD86IFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xufVxuXG4vKipcbiAqIEhlbHBlciB0byBjaGVjayBpZiBlcnJvciBpcyBmcm9tIG91ciBlcnJvciBsaWJyYXJ5LlxuICogVGhpcyBsb29rcyBmb3IgcHJvcGVydGllcyB0aGF0IHRocm93RXJyb3IgYWx3YXlzIGFkZHMgdG8gaWRlbnRpZnkgb3VyIGVycm9ycy5cbiAqL1xuZnVuY3Rpb24gaXNPdXJFcnJvcihcbiAgZXJyb3I6IHVua25vd24sXG4pOiBlcnJvciBpcyBFcnJvciAmIHsgW2tleTogc3RyaW5nXTogdW5rbm93biB9IHtcbiAgcmV0dXJuIChcbiAgICBlcnJvciBpbnN0YW5jZW9mIEVycm9yICYmXG4gICAgT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGVycm9yLCAnX19mcm9tRXJyb3JMaWJyYXJ5JylcbiAgKTtcbn1cblxuLyoqXG4gKiBIZWxwZXIgdG8gc2FmZWx5IGV4dHJhY3QgQXBwU3luYyBldmVudCBwcm9wZXJ0aWVzLlxuICovXG5mdW5jdGlvbiBleHRyYWN0QXBwU3luY0V2ZW50SW5mbyhldmVudDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4pOiB7XG4gIGFyZ3VtZW50czogUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gIGlkZW50aXR5OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IG51bGw7XG4gIGluZm86XG4gICAgfCB7XG4gICAgICAgIGZpZWxkTmFtZTogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICAgICAgICBwYXJlbnRUeXBlTmFtZTogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICAgICAgICB2YXJpYWJsZXM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICAgICAgfVxuICAgIHwgdW5kZWZpbmVkO1xufSB7XG4gIGNvbnN0IGV2ZW50SW5mbyA9IGV2ZW50LmluZm8gYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfCB1bmRlZmluZWQ7XG5cbiAgcmV0dXJuIHtcbiAgICBhcmd1bWVudHM6IChldmVudC5hcmd1bWVudHMgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pIHx8IHt9LFxuICAgIGlkZW50aXR5OiAoZXZlbnQuaWRlbnRpdHkgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfCBudWxsKSB8fCBudWxsLFxuICAgIGluZm86IGV2ZW50SW5mb1xuICAgICAgPyB7XG4gICAgICAgICAgZmllbGROYW1lOiBldmVudEluZm8uZmllbGROYW1lIGFzIHN0cmluZyB8IHVuZGVmaW5lZCxcbiAgICAgICAgICBwYXJlbnRUeXBlTmFtZTogZXZlbnRJbmZvLnBhcmVudFR5cGVOYW1lIGFzIHN0cmluZyB8IHVuZGVmaW5lZCxcbiAgICAgICAgICB2YXJpYWJsZXM6IChldmVudEluZm8udmFyaWFibGVzIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KSB8fCB7fSxcbiAgICAgICAgfVxuICAgICAgOiB1bmRlZmluZWQsXG4gIH07XG59XG5cbi8qKlxuICogSGVscGVyIHRvIGJ1aWxkIHN0cnVjdHVyZWQgbG9nIGNvbnRleHQgZm9yIEdyYXBoUUwgZXJyb3JzLlxuICovXG5mdW5jdGlvbiBidWlsZEdyYXBoUUxMb2dDb250ZXh0KFxuICBpbnB1dDoge1xuICAgIGV2ZW50OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICBjb250ZXh0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IG9iamVjdDtcbiAgfSxcbiAgZXJyb3I6IEVycm9yICYgeyBba2V5OiBzdHJpbmddOiB1bmtub3duIH0sXG4gIGRlZmF1bHRDb250ZXh0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbik6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgY29uc3QgZXZlbnRJbmZvID0gZXh0cmFjdEFwcFN5bmNFdmVudEluZm8oaW5wdXQuZXZlbnQpO1xuICBjb25zdCBlcnJvcldpdGhNaWRkbGV3YXJlID0gZXJyb3IgYXMgTWlkZGxld2FyZUVycm9yO1xuICBjb25zdCBjb250ZXh0ID0gaW5wdXQuY29udGV4dCBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcblxuICBjb25zdCBnZXRNaWRkbGV3YXJlQ2hhaW5TdHJpbmcgPSAoKTogc3RyaW5nIHwgdW5kZWZpbmVkID0+IHtcbiAgICBjb25zdCBjaGFpbiA9IGVycm9yV2l0aE1pZGRsZXdhcmUubWlkZGxld2FyZUNoYWluO1xuICAgIGlmIChBcnJheS5pc0FycmF5KGNoYWluKSkge1xuICAgICAgcmV0dXJuIGNoYWluLmpvaW4oJyAtPiAnKTtcbiAgICB9XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfTtcblxuICByZXR1cm4gY3JlYXRlRXJyb3JDb250ZXh0KHtcbiAgICAuLi5kZWZhdWx0Q29udGV4dCxcbiAgICAvLyBJbmNsdWRlIGFueSBhZGRpdGlvbmFsIHByb3BlcnRpZXMgZnJvbSB0aGUgZXJyb3IgZmlyc3RcbiAgICAuLi5PYmplY3QuZnJvbUVudHJpZXMoXG4gICAgICBPYmplY3QuZW50cmllcyhlcnJvcikuZmlsdGVyKFxuICAgICAgICAoW2tleV0pID0+ICFbJ25hbWUnLCAnbWVzc2FnZScsICdzdGFjayddLmluY2x1ZGVzKGtleSksXG4gICAgICApLFxuICAgICksXG4gICAgcmVxdWVzdElkOiBjb250ZXh0LmF3c1JlcXVlc3RJZCB8fCAndW5rbm93bicsXG4gICAgZnVuY3Rpb25OYW1lOiBjb250ZXh0LmZ1bmN0aW9uTmFtZSxcbiAgICBncmFwaHFsOiB7XG4gICAgICB0eXBlTmFtZTogZXZlbnRJbmZvLmluZm8/LnBhcmVudFR5cGVOYW1lLFxuICAgICAgZmllbGROYW1lOiBldmVudEluZm8uaW5mbz8uZmllbGROYW1lLFxuICAgICAgYXJndW1lbnRzOiBldmVudEluZm8uYXJndW1lbnRzLFxuICAgIH0sXG4gICAgaWRlbnRpdHk6IGV2ZW50SW5mby5pZGVudGl0eSxcbiAgICBmYWlsZWRNaWRkbGV3YXJlOiBlcnJvcldpdGhNaWRkbGV3YXJlLm1pZGRsZXdhcmVOYW1lLFxuICAgIG1pZGRsZXdhcmVDaGFpbjogZ2V0TWlkZGxld2FyZUNoYWluU3RyaW5nKCksXG4gIH0pO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgYW4gZXJyb3IgaGFuZGxpbmcgbWlkZGxld2FyZSBmb3IgQXBwU3luYyBHcmFwaFFMIExhbWJkYSByZXNvbHZlcnMuXG4gKlxuICogVGhpcyBtaWRkbGV3YXJlIGNhdGNoZXMgYWxsIGVycm9ycyBmcm9tIGRvd25zdHJlYW0gbWlkZGxld2FyZSBvciB0aGUgcmVzb2x2ZXIsXG4gKiBwcm92aWRlcyBzdHJ1Y3R1cmVkIGxvZ2dpbmcgd2l0aCBHcmFwaFFMLXNwZWNpZmljIGNvbnRleHQsIGFuZCB0aGVuIHJlLXRocm93c1xuICogdGhlIGVycm9yIHRvIGxldCBBcHBTeW5jIGhhbmRsZSB0aGUgZmluYWwgcmVzcG9uc2UgdG8gdGhlIGNsaWVudC4gVGhpcyBlbnN1cmVzXG4gKiBjb25zaXN0ZW50IGVycm9yIGxvZ2dpbmcgd2l0aG91dCBpbnRlcmZlcmluZyB3aXRoIEFwcFN5bmMncyBuYXRpdmUgZXJyb3IgaGFuZGxpbmcuXG4gKlxuICogSXQgc2hvdWxkIGJlIHRoZSBmaXJzdCBtaWRkbGV3YXJlIGluIHRoZSBjaGFpbiB0byBjYXRjaCBhbGwgc3Vic2VxdWVudCBlcnJvcnMuXG4gKlxuICogQHBhcmFtIGNvbmZpZyAtIENvbmZpZ3VyYXRpb24gb3B0aW9ucyBmb3IgZXJyb3IgaGFuZGxpbmcgYmVoYXZpb3IuXG4gKiBAcmV0dXJucyBBIG1pZGRsZXdhcmUgZnVuY3Rpb24gdGhhdCBoYW5kbGVzIGFuZCBsb2dzIGVycm9ycy5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogY29uc3QgY2hhaW4gPSBNaWRkbGV3YXJlQ2hhaW4uY3JlYXRlTGFtYmRhQ2hhaW4oKTtcbiAqXG4gKiAvLyBBZGQgZXJyb3IgaGFuZGxpbmcgKHNob3VsZCBiZSB0aGUgZmlyc3QgbWlkZGxld2FyZSlcbiAqIGNoYWluLnVzZSgnZXJyb3JIYW5kbGVyJywgY3JlYXRlR3JhcGhRTEVycm9ySGFuZGxlcih7XG4gKiAgIGluY2x1ZGVTdGFja1RyYWNlOiBwcm9jZXNzLmVudi5OT0RFX0VOViA9PT0gJ2RldmVsb3BtZW50JyxcbiAqICAgZGVmYXVsdENvbnRleHQ6IHsgc2VydmljZTogJ2ludmVudG9yeS1hcGknLCB2ZXJzaW9uOiAnMS4yLjAnIH1cbiAqIH0pKTtcbiAqXG4gKiAvLyBBZGQgb3RoZXIgbWlkZGxld2FyZVxuICogY2hhaW4udXNlKCdhdXRoJywgYXV0aE1pZGRsZXdhcmUpO1xuICpcbiAqIGNvbnN0IGhhbmRsZXIgPSB3cmFwTGFtYmRhSGFuZGxlcihjaGFpbiwgYXN5bmMgKGV2ZW50LCBjb250ZXh0KSA9PiB7XG4gKiAgIC8vIE5vIHRyeS9jYXRjaCBuZWVkZWQgaGVyZTsgZXJyb3JzIGFyZSBoYW5kbGVkIGF1dG9tYXRpY2FsbHlcbiAqICAgY29uc3QgaXRlbSA9IGF3YWl0IGdldEl0ZW1CeUlkKGV2ZW50LmFyZ3VtZW50cy5pZCk7XG4gKiAgIHJldHVybiBpdGVtO1xuICogfSk7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUdyYXBoUUxFcnJvckhhbmRsZXI8XG4gIFRJbnB1dCBleHRlbmRzIHtcbiAgICBldmVudDogUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gICAgY29udGV4dDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfCBvYmplY3Q7XG4gIH0sXG4gIFRPdXRwdXQgPSB1bmtub3duLFxuPihjb25maWc6IEdyYXBoUUxFcnJvckhhbmRsZXJDb25maWcgPSB7fSk6IE1pZGRsZXdhcmU8VElucHV0LCBUT3V0cHV0PiB7XG4gIGNvbnN0IHtcbiAgICBpbmNsdWRlU3RhY2tUcmFjZSA9IHByb2Nlc3MuZW52Lk5PREVfRU5WID09PSAnZGV2ZWxvcG1lbnQnLFxuICAgIGRlZmF1bHRDb250ZXh0ID0ge30sXG4gIH0gPSBjb25maWc7XG5cbiAgcmV0dXJuIGFzeW5jIChpbnB1dDogVElucHV0LCBuZXh0OiAoKSA9PiBQcm9taXNlPFRPdXRwdXQ+KSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiBhd2FpdCBuZXh0KCk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxldCBzdGFuZGFyZEVycm9yOiBFcnJvciAmIHsgW2tleTogc3RyaW5nXTogdW5rbm93biB9O1xuICAgICAgbGV0IGVycm9yTWVzc2FnZTogc3RyaW5nO1xuXG4gICAgICAvLyBIYW5kbGUgbm9uLXN0YW5kYXJkIGVycm9ycyBieSBjcmVhdGluZyBhIHN0YW5kYXJkIGVycm9yIHdpdGggY29udGV4dFxuICAgICAgaWYgKCFpc091ckVycm9yKGVycm9yKSkge1xuICAgICAgICBsb2dnZXIud2FybignTm9uLXN0YW5kYXJkIGVycm9yIHRocm93biwgd3JhcHBpbmcgd2l0aCB0aHJvd0Vycm9yJywge1xuICAgICAgICAgIGVycm9yLFxuICAgICAgICB9KTtcblxuICAgICAgICAvLyBDcmVhdGUgYSBzdGFuZGFyZCBlcnJvciBtYW51YWxseSB3aXRoIHRoZSBzYW1lIHN0cnVjdHVyZSBhcyB0aHJvd0Vycm9yXG4gICAgICAgIGNvbnN0IHdyYXBwZWRNZXNzYWdlID1cbiAgICAgICAgICAnTm9uLXN0YW5kYXJkIGVycm9yIHRocm93biB0byBHcmFwaFFMIGVycm9yIGhhbmRsZXInO1xuICAgICAgICBzdGFuZGFyZEVycm9yID0gbmV3IEVycm9yKHdyYXBwZWRNZXNzYWdlKSBhcyBFcnJvciAmIHtcbiAgICAgICAgICBba2V5OiBzdHJpbmddOiB1bmtub3duO1xuICAgICAgICB9O1xuXG4gICAgICAgIC8vIEFkZCBwcm9wZXJ0aWVzIHRoYXQgdGhyb3dFcnJvciB3b3VsZCBoYXZlIGFkZGVkXG4gICAgICAgIHN0YW5kYXJkRXJyb3IuZXJyb3JUeXBlID0gdHlwZW9mIGVycm9yO1xuICAgICAgICBzdGFuZGFyZEVycm9yLm9yaWdpbmFsRXJyb3IgPSBlcnJvcjtcblxuICAgICAgICBlcnJvck1lc3NhZ2UgPSB3cmFwcGVkTWVzc2FnZTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHN0YW5kYXJkRXJyb3IgPSBlcnJvcjtcbiAgICAgICAgZXJyb3JNZXNzYWdlID0gZXh0cmFjdEVycm9yTWVzc2FnZShzdGFuZGFyZEVycm9yKTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgbG9nQ29udGV4dCA9IGJ1aWxkR3JhcGhRTExvZ0NvbnRleHQoXG4gICAgICAgIGlucHV0LFxuICAgICAgICBzdGFuZGFyZEVycm9yLFxuICAgICAgICBkZWZhdWx0Q29udGV4dCxcbiAgICAgICk7XG5cbiAgICAgIGxvZ2dlci5lcnJvcihgR3JhcGhRTCByZXNvbHZlciBlcnJvcjogJHtlcnJvck1lc3NhZ2V9YCwge1xuICAgICAgICAuLi5sb2dDb250ZXh0LFxuICAgICAgICAuLi4oaW5jbHVkZVN0YWNrVHJhY2UgJiYgeyBzdGFjazogc3RhbmRhcmRFcnJvci5zdGFjayB9KSxcbiAgICAgICAgb3JpZ2luYWxFcnJvcjogc3RhbmRhcmRFcnJvci5vcmlnaW5hbEVycm9yIHx8IHN0YW5kYXJkRXJyb3IsXG4gICAgICB9KTtcblxuICAgICAgdGhyb3cgc3RhbmRhcmRFcnJvcjtcbiAgICB9XG4gIH07XG59XG4iXX0=