UNPKG

fusion-plugin-rpc

Version:

Fetch data on the server and client with an RPC style interface.

226 lines (222 loc) 27.9 kB
/** Copyright (c) 2018 Uber Technologies, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * */ /* eslint-env node */ import bodyparser from 'koa-bodyparser'; import formidable from 'formidable'; import { createPlugin, memoize, RouteTagsToken } from 'fusion-core'; import { UniversalEventsToken } from 'fusion-plugin-universal-events'; import MissingHandlerError from './missing-handler-error'; import ResponseError from './response-error'; import { BodyParserOptionsToken, RPCHandlersToken, RPCHandlersConfigToken } from './tokens'; import { formatApiPath } from './utils'; const statKey = 'rpc:method'; /* Helper function */ function hasHandler(handlers, method) { return Object.prototype.hasOwnProperty.call(handlers, method); } class RPC { constructor(emitter, handlers, ctx) { if (!ctx || !ctx.headers) { throw new Error('fusion-plugin-rpc requires `ctx`'); } this.ctx = ctx; this.emitter = emitter; this.handlers = handlers; return this; } async request(method, args) { const startTime = ms(); if (!this.ctx) { throw new Error('fusion-plugin-rpc requires `ctx`'); } if (!this.emitter) { throw new Error('fusion-plugin-rpc requires `emitter`'); } const scopedEmitter = this.emitter.from(this.ctx); if (!this.handlers) { throw new Error('fusion-plugin-rpc requires `handlers`'); } if (!hasHandler(this.handlers, method)) { const e = new MissingHandlerError(method); if (scopedEmitter) { scopedEmitter.emit('rpc:error', { method, origin: 'server', error: e }); } throw e; } try { const result = await this.handlers[method](args, this.ctx); if (scopedEmitter) { scopedEmitter.emit(statKey, { method, status: 'success', origin: 'server', timing: ms() - startTime }); } return result; } catch (e) { if (scopedEmitter) { scopedEmitter.emit(statKey, { method, error: e, status: 'failure', origin: 'server', timing: ms() - startTime }); } throw e; } } } const pluginFactory = () => createPlugin({ deps: { RouteTags: RouteTagsToken.optional, emitter: UniversalEventsToken, handlers: RPCHandlersToken, bodyParserOptions: BodyParserOptionsToken.optional, rpcConfig: RPCHandlersConfigToken.optional }, provides: deps => { const { emitter, handlers } = deps; const service = { from: memoize(ctx => new RPC(emitter, handlers, ctx)) }; return service; }, middleware: deps => { const { emitter, handlers, bodyParserOptions, rpcConfig } = deps; if (!handlers) throw new Error('Missing handlers registered to RPCHandlersToken'); if (!emitter) throw new Error('Missing emitter registered to UniversalEventsToken'); const parseBody = bodyparser(bodyParserOptions); const apiPath = formatApiPath(rpcConfig && rpcConfig.apiPath ? rpcConfig.apiPath : 'api'); return async (ctx, next) => { await next(); const routeTags = deps.RouteTags && deps.RouteTags.from(ctx) || {}; const scopedEmitter = emitter.from(ctx); if (ctx.method === 'POST' && ctx.path.startsWith(apiPath)) { const startTime = ms(); // eslint-disable-next-line no-useless-escape const pathMatch = new RegExp(`${apiPath}([^/]+)`, 'i'); const [, method] = ctx.path.match(pathMatch) || []; if (hasHandler(handlers, method)) { routeTags.name = method; let body; try { if (ctx.req && ctx.req.headers && ctx.req.headers['content-type'] && ctx.req.headers['content-type'].indexOf('multipart/form-data') !== -1) { const form = new formidable.IncomingForm(); body = await new Promise((resolve, reject) => { form.parse(ctx.req, (err, fields, files) => { if (err) { reject(err); } resolve({ ...fields, ...files }); }); }); } else { await parseBody(ctx, () => Promise.resolve()); } } catch (e) { ctx.body = { status: 'failure', data: { message: e.message, code: e.type || 'ERR_BAD_BODY', meta: e.meta } }; if (scopedEmitter) { scopedEmitter.emit(statKey, { method, error: e, status: 'failure', origin: 'browser', timing: ms() - startTime }); } // don't try to call handler return; } try { const result = await handlers[method](body || ctx.request.body, ctx); ctx.body = { status: 'success', data: result }; if (scopedEmitter) { scopedEmitter.emit(statKey, { method, status: 'success', origin: 'browser', timing: ms() - startTime }); } } catch (e) { const error = e instanceof ResponseError ? e : new Error(process.env.NODE_ENV !== "production" ? 'UnknownError - Use ResponseError from fusion-plugin-rpc (or fusion-plugin-rpc-redux-react if you are using React) package for more detailed error messages' : 'Internal Server Error'); ctx.body = { status: 'failure', data: { message: error.message, // @ts-expect-error code: error.code, // @ts-expect-error meta: error.meta } }; if (scopedEmitter) { scopedEmitter.emit(statKey, { method, error: e, status: 'failure', origin: 'browser', timing: ms() - startTime }); } } } else { const e = new MissingHandlerError(method); ctx.body = { status: 'failure', data: { message: e.message, code: e.code } }; ctx.status = 404; if (scopedEmitter) { scopedEmitter.emit('rpc:error', { origin: 'browser', method, error: e }); } } } }; } }); /* Helper functions */ function ms() { const [seconds, ns] = process.hrtime(); return Math.round(seconds * 1000 + ns / 1e6); } export default true && pluginFactory(); //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJib2R5cGFyc2VyIiwiZm9ybWlkYWJsZSIsImNyZWF0ZVBsdWdpbiIsIm1lbW9pemUiLCJSb3V0ZVRhZ3NUb2tlbiIsIlVuaXZlcnNhbEV2ZW50c1Rva2VuIiwiTWlzc2luZ0hhbmRsZXJFcnJvciIsIlJlc3BvbnNlRXJyb3IiLCJCb2R5UGFyc2VyT3B0aW9uc1Rva2VuIiwiUlBDSGFuZGxlcnNUb2tlbiIsIlJQQ0hhbmRsZXJzQ29uZmlnVG9rZW4iLCJmb3JtYXRBcGlQYXRoIiwic3RhdEtleSIsImhhc0hhbmRsZXIiLCJoYW5kbGVycyIsIm1ldGhvZCIsIk9iamVjdCIsInByb3RvdHlwZSIsImhhc093blByb3BlcnR5IiwiY2FsbCIsIlJQQyIsImNvbnN0cnVjdG9yIiwiZW1pdHRlciIsImN0eCIsImhlYWRlcnMiLCJFcnJvciIsInJlcXVlc3QiLCJhcmdzIiwic3RhcnRUaW1lIiwibXMiLCJzY29wZWRFbWl0dGVyIiwiZnJvbSIsImUiLCJlbWl0Iiwib3JpZ2luIiwiZXJyb3IiLCJyZXN1bHQiLCJzdGF0dXMiLCJ0aW1pbmciLCJwbHVnaW5GYWN0b3J5IiwiZGVwcyIsIlJvdXRlVGFncyIsIm9wdGlvbmFsIiwiYm9keVBhcnNlck9wdGlvbnMiLCJycGNDb25maWciLCJwcm92aWRlcyIsInNlcnZpY2UiLCJtaWRkbGV3YXJlIiwicGFyc2VCb2R5IiwiYXBpUGF0aCIsIm5leHQiLCJyb3V0ZVRhZ3MiLCJwYXRoIiwic3RhcnRzV2l0aCIsInBhdGhNYXRjaCIsIlJlZ0V4cCIsIm1hdGNoIiwibmFtZSIsImJvZHkiLCJyZXEiLCJpbmRleE9mIiwiZm9ybSIsIkluY29taW5nRm9ybSIsIlByb21pc2UiLCJyZXNvbHZlIiwicmVqZWN0IiwicGFyc2UiLCJlcnIiLCJmaWVsZHMiLCJmaWxlcyIsImRhdGEiLCJtZXNzYWdlIiwiY29kZSIsInR5cGUiLCJtZXRhIiwic2Vjb25kcyIsIm5zIiwicHJvY2VzcyIsImhydGltZSIsIk1hdGgiLCJyb3VuZCJdLCJzb3VyY2VzIjpbInNyYy9zZXJ2ZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqIENvcHlyaWdodCAoYykgMjAxOCBVYmVyIFRlY2hub2xvZ2llcywgSW5jLlxuICpcbiAqIFRoaXMgc291cmNlIGNvZGUgaXMgbGljZW5zZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlIGZvdW5kIGluIHRoZVxuICogTElDRU5TRSBmaWxlIGluIHRoZSByb290IGRpcmVjdG9yeSBvZiB0aGlzIHNvdXJjZSB0cmVlLlxuICpcbiAqL1xuXG4vKiBlc2xpbnQtZW52IG5vZGUgKi9cblxuaW1wb3J0IGJvZHlwYXJzZXIgZnJvbSAna29hLWJvZHlwYXJzZXInO1xuaW1wb3J0IGZvcm1pZGFibGUgZnJvbSAnZm9ybWlkYWJsZSc7XG5cbmltcG9ydCB7Y3JlYXRlUGx1Z2luLCBtZW1vaXplLCBSb3V0ZVRhZ3NUb2tlbn0gZnJvbSAnZnVzaW9uLWNvcmUnO1xuaW1wb3J0IHR5cGUge0NvbnRleHR9IGZyb20gJ2Z1c2lvbi1jb3JlJztcbmltcG9ydCB7VW5pdmVyc2FsRXZlbnRzVG9rZW59IGZyb20gJ2Z1c2lvbi1wbHVnaW4tdW5pdmVyc2FsLWV2ZW50cyc7XG5pbXBvcnQgdHlwZSB7RmV0Y2h9IGZyb20gJ2Z1c2lvbi10b2tlbnMnO1xuXG5pbXBvcnQgTWlzc2luZ0hhbmRsZXJFcnJvciBmcm9tICcuL21pc3NpbmctaGFuZGxlci1lcnJvcic7XG5pbXBvcnQgUmVzcG9uc2VFcnJvciBmcm9tICcuL3Jlc3BvbnNlLWVycm9yJztcbmltcG9ydCB7XG4gIEJvZHlQYXJzZXJPcHRpb25zVG9rZW4sXG4gIFJQQ0hhbmRsZXJzVG9rZW4sXG4gIFJQQ0hhbmRsZXJzQ29uZmlnVG9rZW4sXG59IGZyb20gJy4vdG9rZW5zJztcbmltcG9ydCB0eXBlIHtIYW5kbGVyVHlwZX0gZnJvbSAnLi90b2tlbnMnO1xuaW1wb3J0IHR5cGUge1JQQ1BsdWdpblR5cGUsIElFbWl0dGVyfSBmcm9tICcuL3R5cGVzJztcbmltcG9ydCB7Zm9ybWF0QXBpUGF0aH0gZnJvbSAnLi91dGlscyc7XG5cbmNvbnN0IHN0YXRLZXkgPSAncnBjOm1ldGhvZCc7XG5cbi8qIEhlbHBlciBmdW5jdGlvbiAqL1xuZnVuY3Rpb24gaGFzSGFuZGxlcihoYW5kbGVyczogSGFuZGxlclR5cGUsIG1ldGhvZDogc3RyaW5nKTogYm9vbGVhbiB7XG4gIHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoaGFuZGxlcnMsIG1ldGhvZCk7XG59XG5cbmNsYXNzIFJQQyB7XG4gIGN0eDogQ29udGV4dCB8IHVuZGVmaW5lZCB8IG51bGw7XG4gIGVtaXR0ZXI6IElFbWl0dGVyIHwgdW5kZWZpbmVkIHwgbnVsbDtcbiAgaGFuZGxlcnM6IEhhbmRsZXJUeXBlIHwgdW5kZWZpbmVkIHwgbnVsbDtcbiAgZmV0Y2g6IEZldGNoIHwgdW5kZWZpbmVkIHwgbnVsbDtcblxuICBjb25zdHJ1Y3RvcihlbWl0dGVyOiBJRW1pdHRlciwgaGFuZGxlcnM6IGFueSwgY3R4OiBDb250ZXh0KSB7XG4gICAgaWYgKCFjdHggfHwgIWN0eC5oZWFkZXJzKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2Z1c2lvbi1wbHVnaW4tcnBjIHJlcXVpcmVzIGBjdHhgJyk7XG4gICAgfVxuICAgIHRoaXMuY3R4ID0gY3R4O1xuICAgIHRoaXMuZW1pdHRlciA9IGVtaXR0ZXI7XG4gICAgdGhpcy5oYW5kbGVycyA9IGhhbmRsZXJzO1xuXG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICBhc3luYyByZXF1ZXN0PFRBcmdzLCBUUmVzdWx0PihtZXRob2Q6IHN0cmluZywgYXJnczogVEFyZ3MpOiBQcm9taXNlPFRSZXN1bHQ+IHtcbiAgICBjb25zdCBzdGFydFRpbWUgPSBtcygpO1xuXG4gICAgaWYgKCF0aGlzLmN0eCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdmdXNpb24tcGx1Z2luLXJwYyByZXF1aXJlcyBgY3R4YCcpO1xuICAgIH1cbiAgICBpZiAoIXRoaXMuZW1pdHRlcikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdmdXNpb24tcGx1Z2luLXJwYyByZXF1aXJlcyBgZW1pdHRlcmAnKTtcbiAgICB9XG4gICAgY29uc3Qgc2NvcGVkRW1pdHRlciA9IHRoaXMuZW1pdHRlci5mcm9tKHRoaXMuY3R4KTtcblxuICAgIGlmICghdGhpcy5oYW5kbGVycykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdmdXNpb24tcGx1Z2luLXJwYyByZXF1aXJlcyBgaGFuZGxlcnNgJyk7XG4gICAgfVxuICAgIGlmICghaGFzSGFuZGxlcih0aGlzLmhhbmRsZXJzLCBtZXRob2QpKSB7XG4gICAgICBjb25zdCBlID0gbmV3IE1pc3NpbmdIYW5kbGVyRXJyb3IobWV0aG9kKTtcbiAgICAgIGlmIChzY29wZWRFbWl0dGVyKSB7XG4gICAgICAgIHNjb3BlZEVtaXR0ZXIuZW1pdCgncnBjOmVycm9yJywge1xuICAgICAgICAgIG1ldGhvZCxcbiAgICAgICAgICBvcmlnaW46ICdzZXJ2ZXInLFxuICAgICAgICAgIGVycm9yOiBlLFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICAgIHRocm93IGU7XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB0aGlzLmhhbmRsZXJzW21ldGhvZF0oYXJncywgdGhpcy5jdHgpO1xuICAgICAgaWYgKHNjb3BlZEVtaXR0ZXIpIHtcbiAgICAgICAgc2NvcGVkRW1pdHRlci5lbWl0KHN0YXRLZXksIHtcbiAgICAgICAgICBtZXRob2QsXG4gICAgICAgICAgc3RhdHVzOiAnc3VjY2VzcycsXG4gICAgICAgICAgb3JpZ2luOiAnc2VydmVyJyxcbiAgICAgICAgICB0aW1pbmc6IG1zKCkgLSBzdGFydFRpbWUsXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBpZiAoc2NvcGVkRW1pdHRlcikge1xuICAgICAgICBzY29wZWRFbWl0dGVyLmVtaXQoc3RhdEtleSwge1xuICAgICAgICAgIG1ldGhvZCxcbiAgICAgICAgICBlcnJvcjogZSxcbiAgICAgICAgICBzdGF0dXM6ICdmYWlsdXJlJyxcbiAgICAgICAgICBvcmlnaW46ICdzZXJ2ZXInLFxuICAgICAgICAgIHRpbWluZzogbXMoKSAtIHN0YXJ0VGltZSxcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICB0aHJvdyBlO1xuICAgIH1cbiAgfVxufVxuXG5jb25zdCBwbHVnaW5GYWN0b3J5OiAoKSA9PiBSUENQbHVnaW5UeXBlID0gKCkgPT5cbiAgY3JlYXRlUGx1Z2luKHtcbiAgICBkZXBzOiB7XG4gICAgICBSb3V0ZVRhZ3M6IFJvdXRlVGFnc1Rva2VuLm9wdGlvbmFsLFxuICAgICAgZW1pdHRlcjogVW5pdmVyc2FsRXZlbnRzVG9rZW4sXG4gICAgICBoYW5kbGVyczogUlBDSGFuZGxlcnNUb2tlbixcbiAgICAgIGJvZHlQYXJzZXJPcHRpb25zOiBCb2R5UGFyc2VyT3B0aW9uc1Rva2VuLm9wdGlvbmFsLFxuICAgICAgcnBjQ29uZmlnOiBSUENIYW5kbGVyc0NvbmZpZ1Rva2VuLm9wdGlvbmFsLFxuICAgIH0sXG5cbiAgICBwcm92aWRlczogKGRlcHMpID0+IHtcbiAgICAgIGNvbnN0IHtlbWl0dGVyLCBoYW5kbGVyc30gPSBkZXBzO1xuXG4gICAgICBjb25zdCBzZXJ2aWNlID0ge1xuICAgICAgICBmcm9tOiBtZW1vaXplKChjdHgpID0+IG5ldyBSUEMoZW1pdHRlciwgaGFuZGxlcnMsIGN0eCkpLFxuICAgICAgfTtcbiAgICAgIHJldHVybiBzZXJ2aWNlO1xuICAgIH0sXG5cbiAgICBtaWRkbGV3YXJlOiAoZGVwcykgPT4ge1xuICAgICAgY29uc3Qge2VtaXR0ZXIsIGhhbmRsZXJzLCBib2R5UGFyc2VyT3B0aW9ucywgcnBjQ29uZmlnfSA9IGRlcHM7XG4gICAgICBpZiAoIWhhbmRsZXJzKVxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01pc3NpbmcgaGFuZGxlcnMgcmVnaXN0ZXJlZCB0byBSUENIYW5kbGVyc1Rva2VuJyk7XG4gICAgICBpZiAoIWVtaXR0ZXIpXG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTWlzc2luZyBlbWl0dGVyIHJlZ2lzdGVyZWQgdG8gVW5pdmVyc2FsRXZlbnRzVG9rZW4nKTtcbiAgICAgIGNvbnN0IHBhcnNlQm9keSA9IGJvZHlwYXJzZXIoYm9keVBhcnNlck9wdGlvbnMpO1xuXG4gICAgICBjb25zdCBhcGlQYXRoID0gZm9ybWF0QXBpUGF0aChcbiAgICAgICAgcnBjQ29uZmlnICYmIHJwY0NvbmZpZy5hcGlQYXRoID8gcnBjQ29uZmlnLmFwaVBhdGggOiAnYXBpJ1xuICAgICAgKTtcblxuICAgICAgcmV0dXJuIGFzeW5jIChjdHgsIG5leHQpID0+IHtcbiAgICAgICAgYXdhaXQgbmV4dCgpO1xuICAgICAgICBjb25zdCByb3V0ZVRhZ3MgPSAoZGVwcy5Sb3V0ZVRhZ3MgJiYgZGVwcy5Sb3V0ZVRhZ3MuZnJvbShjdHgpKSB8fCB7fTtcbiAgICAgICAgY29uc3Qgc2NvcGVkRW1pdHRlciA9IGVtaXR0ZXIuZnJvbShjdHgpO1xuICAgICAgICBpZiAoY3R4Lm1ldGhvZCA9PT0gJ1BPU1QnICYmIGN0eC5wYXRoLnN0YXJ0c1dpdGgoYXBpUGF0aCkpIHtcbiAgICAgICAgICBjb25zdCBzdGFydFRpbWUgPSBtcygpO1xuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11c2VsZXNzLWVzY2FwZVxuICAgICAgICAgIGNvbnN0IHBhdGhNYXRjaCA9IG5ldyBSZWdFeHAoYCR7YXBpUGF0aH0oW14vXSspYCwgJ2knKTtcbiAgICAgICAgICBjb25zdCBbLCBtZXRob2RdID0gY3R4LnBhdGgubWF0Y2gocGF0aE1hdGNoKSB8fCBbXTtcbiAgICAgICAgICBpZiAoaGFzSGFuZGxlcihoYW5kbGVycywgbWV0aG9kKSkge1xuICAgICAgICAgICAgcm91dGVUYWdzLm5hbWUgPSBtZXRob2Q7XG4gICAgICAgICAgICBsZXQgYm9keTtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICBjdHgucmVxICYmXG4gICAgICAgICAgICAgICAgY3R4LnJlcS5oZWFkZXJzICYmXG4gICAgICAgICAgICAgICAgY3R4LnJlcS5oZWFkZXJzWydjb250ZW50LXR5cGUnXSAmJlxuICAgICAgICAgICAgICAgIGN0eC5yZXEuaGVhZGVyc1snY29udGVudC10eXBlJ10uaW5kZXhPZihcbiAgICAgICAgICAgICAgICAgICdtdWx0aXBhcnQvZm9ybS1kYXRhJ1xuICAgICAgICAgICAgICAgICkgIT09IC0xXG4gICAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGZvcm0gPSBuZXcgZm9ybWlkYWJsZS5JbmNvbWluZ0Zvcm0oKTtcbiAgICAgICAgICAgICAgICBib2R5ID0gYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgICAgICAgZm9ybS5wYXJzZShcbiAgICAgICAgICAgICAgICAgICAgY3R4LnJlcSxcbiAgICAgICAgICAgICAgICAgICAgKFxuICAgICAgICAgICAgICAgICAgICAgIGVycixcbiAgICAgICAgICAgICAgICAgICAgICBmaWVsZHM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFt4OiBzdHJpbmddOiBhbnk7XG4gICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICBmaWxlc1xuICAgICAgICAgICAgICAgICAgICApID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICBpZiAoZXJyKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZWplY3QoZXJyKTtcbiAgICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC4uLmZpZWxkcyxcbiAgICAgICAgICAgICAgICAgICAgICAgIC4uLmZpbGVzLFxuICAgICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGF3YWl0IHBhcnNlQm9keShjdHgsICgpID0+IFByb21pc2UucmVzb2x2ZSgpKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICBjdHguYm9keSA9IHtcbiAgICAgICAgICAgICAgICBzdGF0dXM6ICdmYWlsdXJlJyxcbiAgICAgICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgICBtZXNzYWdlOiBlLm1lc3NhZ2UsXG4gICAgICAgICAgICAgICAgICBjb2RlOiBlLnR5cGUgfHwgJ0VSUl9CQURfQk9EWScsXG4gICAgICAgICAgICAgICAgICBtZXRhOiBlLm1ldGEsXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgaWYgKHNjb3BlZEVtaXR0ZXIpIHtcbiAgICAgICAgICAgICAgICBzY29wZWRFbWl0dGVyLmVtaXQoc3RhdEtleSwge1xuICAgICAgICAgICAgICAgICAgbWV0aG9kLFxuICAgICAgICAgICAgICAgICAgZXJyb3I6IGUsXG4gICAgICAgICAgICAgICAgICBzdGF0dXM6ICdmYWlsdXJlJyxcbiAgICAgICAgICAgICAgICAgIG9yaWdpbjogJ2Jyb3dzZXInLFxuICAgICAgICAgICAgICAgICAgdGltaW5nOiBtcygpIC0gc3RhcnRUaW1lLFxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIC8vIGRvbid0IHRyeSB0byBjYWxsIGhhbmRsZXJcbiAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBoYW5kbGVyc1ttZXRob2RdKFxuICAgICAgICAgICAgICAgIGJvZHkgfHwgY3R4LnJlcXVlc3QuYm9keSxcbiAgICAgICAgICAgICAgICBjdHhcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgY3R4LmJvZHkgPSB7XG4gICAgICAgICAgICAgICAgc3RhdHVzOiAnc3VjY2VzcycsXG4gICAgICAgICAgICAgICAgZGF0YTogcmVzdWx0LFxuICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICBpZiAoc2NvcGVkRW1pdHRlcikge1xuICAgICAgICAgICAgICAgIHNjb3BlZEVtaXR0ZXIuZW1pdChzdGF0S2V5LCB7XG4gICAgICAgICAgICAgICAgICBtZXRob2QsXG4gICAgICAgICAgICAgICAgICBzdGF0dXM6ICdzdWNjZXNzJyxcbiAgICAgICAgICAgICAgICAgIG9yaWdpbjogJ2Jyb3dzZXInLFxuICAgICAgICAgICAgICAgICAgdGltaW5nOiBtcygpIC0gc3RhcnRUaW1lLFxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgIGNvbnN0IGVycm9yID1cbiAgICAgICAgICAgICAgICBlIGluc3RhbmNlb2YgUmVzcG9uc2VFcnJvclxuICAgICAgICAgICAgICAgICAgPyBlXG4gICAgICAgICAgICAgICAgICA6IG5ldyBFcnJvcihcbiAgICAgICAgICAgICAgICAgICAgICBfX0RFVl9fXG4gICAgICAgICAgICAgICAgICAgICAgICA/ICdVbmtub3duRXJyb3IgLSBVc2UgUmVzcG9uc2VFcnJvciBmcm9tIGZ1c2lvbi1wbHVnaW4tcnBjIChvciBmdXNpb24tcGx1Z2luLXJwYy1yZWR1eC1yZWFjdCBpZiB5b3UgYXJlIHVzaW5nIFJlYWN0KSBwYWNrYWdlIGZvciBtb3JlIGRldGFpbGVkIGVycm9yIG1lc3NhZ2VzJ1xuICAgICAgICAgICAgICAgICAgICAgICAgOiAnSW50ZXJuYWwgU2VydmVyIEVycm9yJ1xuICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICBjdHguYm9keSA9IHtcbiAgICAgICAgICAgICAgICBzdGF0dXM6ICdmYWlsdXJlJyxcbiAgICAgICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgICBtZXNzYWdlOiBlcnJvci5tZXNzYWdlLFxuICAgICAgICAgICAgICAgICAgLy8gQHRzLWV4cGVjdC1lcnJvclxuICAgICAgICAgICAgICAgICAgY29kZTogZXJyb3IuY29kZSxcbiAgICAgICAgICAgICAgICAgIC8vIEB0cy1leHBlY3QtZXJyb3JcbiAgICAgICAgICAgICAgICAgIG1ldGE6IGVycm9yLm1ldGEsXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgaWYgKHNjb3BlZEVtaXR0ZXIpIHtcbiAgICAgICAgICAgICAgICBzY29wZWRFbWl0dGVyLmVtaXQoc3RhdEtleSwge1xuICAgICAgICAgICAgICAgICAgbWV0aG9kLFxuICAgICAgICAgICAgICAgICAgZXJyb3I6IGUsXG4gICAgICAgICAgICAgICAgICBzdGF0dXM6ICdmYWlsdXJlJyxcbiAgICAgICAgICAgICAgICAgIG9yaWdpbjogJ2Jyb3dzZXInLFxuICAgICAgICAgICAgICAgICAgdGltaW5nOiBtcygpIC0gc3RhcnRUaW1lLFxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGNvbnN0IGUgPSBuZXcgTWlzc2luZ0hhbmRsZXJFcnJvcihtZXRob2QpO1xuICAgICAgICAgICAgY3R4LmJvZHkgPSB7XG4gICAgICAgICAgICAgIHN0YXR1czogJ2ZhaWx1cmUnLFxuICAgICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgbWVzc2FnZTogZS5tZXNzYWdlLFxuICAgICAgICAgICAgICAgIGNvZGU6IGUuY29kZSxcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBjdHguc3RhdHVzID0gNDA0O1xuICAgICAgICAgICAgaWYgKHNjb3BlZEVtaXR0ZXIpIHtcbiAgICAgICAgICAgICAgc2NvcGVkRW1pdHRlci5lbWl0KCdycGM6ZXJyb3InLCB7XG4gICAgICAgICAgICAgICAgb3JpZ2luOiAnYnJvd3NlcicsXG4gICAgICAgICAgICAgICAgbWV0aG9kLFxuICAgICAgICAgICAgICAgIGVycm9yOiBlLFxuICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgfSxcbiAgfSk7XG5cbi8qIEhlbHBlciBmdW5jdGlvbnMgKi9cbmZ1bmN0aW9uIG1zKCkge1xuICBjb25zdCBbc2Vjb25kcywgbnNdID0gcHJvY2Vzcy5ocnRpbWUoKTtcbiAgcmV0dXJuIE1hdGgucm91bmQoc2Vjb25kcyAqIDEwMDAgKyBucyAvIDFlNik7XG59XG5cbmV4cG9ydCBkZWZhdWx0IF9fTk9ERV9fICYmIChwbHVnaW5GYWN0b3J5KCkgYXMgYW55IGFzIFJQQ1BsdWdpblR5cGUpO1xuIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUEsT0FBT0EsVUFBVSxNQUFNLGdCQUFnQjtBQUN2QyxPQUFPQyxVQUFVLE1BQU0sWUFBWTtBQUVuQyxTQUFRQyxZQUFZLEVBQUVDLE9BQU8sRUFBRUMsY0FBYyxRQUFPLGFBQWE7QUFFakUsU0FBUUMsb0JBQW9CLFFBQU8sZ0NBQWdDO0FBR25FLE9BQU9DLG1CQUFtQixNQUFNLHlCQUF5QjtBQUN6RCxPQUFPQyxhQUFhLE1BQU0sa0JBQWtCO0FBQzVDLFNBQ0VDLHNCQUFzQixFQUN0QkMsZ0JBQWdCLEVBQ2hCQyxzQkFBc0IsUUFDakIsVUFBVTtBQUdqQixTQUFRQyxhQUFhLFFBQU8sU0FBUztBQUVyQyxNQUFNQyxPQUFPLEdBQUcsWUFBWTs7QUFFNUI7QUFDQSxTQUFTQyxVQUFVLENBQUNDLFFBQXFCLEVBQUVDLE1BQWMsRUFBVztFQUNsRSxPQUFPQyxNQUFNLENBQUNDLFNBQVMsQ0FBQ0MsY0FBYyxDQUFDQyxJQUFJLENBQUNMLFFBQVEsRUFBRUMsTUFBTSxDQUFDO0FBQy9EO0FBRUEsTUFBTUssR0FBRyxDQUFDO0VBTVJDLFdBQVcsQ0FBQ0MsT0FBaUIsRUFBRVIsUUFBYSxFQUFFUyxHQUFZLEVBQUU7SUFDMUQsSUFBSSxDQUFDQSxHQUFHLElBQUksQ0FBQ0EsR0FBRyxDQUFDQyxPQUFPLEVBQUU7TUFDeEIsTUFBTSxJQUFJQyxLQUFLLENBQUMsa0NBQWtDLENBQUM7SUFDckQ7SUFDQSxJQUFJLENBQUNGLEdBQUcsR0FBR0EsR0FBRztJQUNkLElBQUksQ0FBQ0QsT0FBTyxHQUFHQSxPQUFPO0lBQ3RCLElBQUksQ0FBQ1IsUUFBUSxHQUFHQSxRQUFRO0lBRXhCLE9BQU8sSUFBSTtFQUNiO0VBRUEsTUFBTVksT0FBTyxDQUFpQlgsTUFBYyxFQUFFWSxJQUFXLEVBQW9CO0lBQzNFLE1BQU1DLFNBQVMsR0FBR0MsRUFBRSxFQUFFO0lBRXRCLElBQUksQ0FBQyxJQUFJLENBQUNOLEdBQUcsRUFBRTtNQUNiLE1BQU0sSUFBSUUsS0FBSyxDQUFDLGtDQUFrQyxDQUFDO0lBQ3JEO0lBQ0EsSUFBSSxDQUFDLElBQUksQ0FBQ0gsT0FBTyxFQUFFO01BQ2pCLE1BQU0sSUFBSUcsS0FBSyxDQUFDLHNDQUFzQyxDQUFDO0lBQ3pEO0lBQ0EsTUFBTUssYUFBYSxHQUFHLElBQUksQ0FBQ1IsT0FBTyxDQUFDUyxJQUFJLENBQUMsSUFBSSxDQUFDUixHQUFHLENBQUM7SUFFakQsSUFBSSxDQUFDLElBQUksQ0FBQ1QsUUFBUSxFQUFFO01BQ2xCLE1BQU0sSUFBSVcsS0FBSyxDQUFDLHVDQUF1QyxDQUFDO0lBQzFEO0lBQ0EsSUFBSSxDQUFDWixVQUFVLENBQUMsSUFBSSxDQUFDQyxRQUFRLEVBQUVDLE1BQU0sQ0FBQyxFQUFFO01BQ3RDLE1BQU1pQixDQUFDLEdBQUcsSUFBSTFCLG1CQUFtQixDQUFDUyxNQUFNLENBQUM7TUFDekMsSUFBSWUsYUFBYSxFQUFFO1FBQ2pCQSxhQUFhLENBQUNHLElBQUksQ0FBQyxXQUFXLEVBQUU7VUFDOUJsQixNQUFNO1VBQ05tQixNQUFNLEVBQUUsUUFBUTtVQUNoQkMsS0FBSyxFQUFFSDtRQUNULENBQUMsQ0FBQztNQUNKO01BQ0EsTUFBTUEsQ0FBQztJQUNUO0lBQ0EsSUFBSTtNQUNGLE1BQU1JLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQ3RCLFFBQVEsQ0FBQ0MsTUFBTSxDQUFDLENBQUNZLElBQUksRUFBRSxJQUFJLENBQUNKLEdBQUcsQ0FBQztNQUMxRCxJQUFJTyxhQUFhLEVBQUU7UUFDakJBLGFBQWEsQ0FBQ0csSUFBSSxDQUFDckIsT0FBTyxFQUFFO1VBQzFCRyxNQUFNO1VBQ05zQixNQUFNLEVBQUUsU0FBUztVQUNqQkgsTUFBTSxFQUFFLFFBQVE7VUFDaEJJLE1BQU0sRUFBRVQsRUFBRSxFQUFFLEdBQUdEO1FBQ2pCLENBQUMsQ0FBQztNQUNKO01BQ0EsT0FBT1EsTUFBTTtJQUNmLENBQUMsQ0FBQyxPQUFPSixDQUFDLEVBQUU7TUFDVixJQUFJRixhQUFhLEVBQUU7UUFDakJBLGFBQWEsQ0FBQ0csSUFBSSxDQUFDckIsT0FBTyxFQUFFO1VBQzFCRyxNQUFNO1VBQ05vQixLQUFLLEVBQUVILENBQUM7VUFDUkssTUFBTSxFQUFFLFNBQVM7VUFDakJILE1BQU0sRUFBRSxRQUFRO1VBQ2hCSSxNQUFNLEVBQUVULEVBQUUsRUFBRSxHQUFHRDtRQUNqQixDQUFDLENBQUM7TUFDSjtNQUNBLE1BQU1JLENBQUM7SUFDVDtFQUNGO0FBQ0Y7QUFFQSxNQUFNTyxhQUFrQyxHQUFHLE1BQ3pDckMsWUFBWSxDQUFDO0VBQ1hzQyxJQUFJLEVBQUU7SUFDSkMsU0FBUyxFQUFFckMsY0FBYyxDQUFDc0MsUUFBUTtJQUNsQ3BCLE9BQU8sRUFBRWpCLG9CQUFvQjtJQUM3QlMsUUFBUSxFQUFFTCxnQkFBZ0I7SUFDMUJrQyxpQkFBaUIsRUFBRW5DLHNCQUFzQixDQUFDa0MsUUFBUTtJQUNsREUsU0FBUyxFQUFFbEMsc0JBQXNCLENBQUNnQztFQUNwQyxDQUFDO0VBRURHLFFBQVEsRUFBR0wsSUFBSSxJQUFLO0lBQ2xCLE1BQU07TUFBQ2xCLE9BQU87TUFBRVI7SUFBUSxDQUFDLEdBQUcwQixJQUFJO0lBRWhDLE1BQU1NLE9BQU8sR0FBRztNQUNkZixJQUFJLEVBQUU1QixPQUFPLENBQUVvQixHQUFHLElBQUssSUFBSUgsR0FBRyxDQUFDRSxPQUFPLEVBQUVSLFFBQVEsRUFBRVMsR0FBRyxDQUFDO0lBQ3hELENBQUM7SUFDRCxPQUFPdUIsT0FBTztFQUNoQixDQUFDO0VBRURDLFVBQVUsRUFBR1AsSUFBSSxJQUFLO0lBQ3BCLE1BQU07TUFBQ2xCLE9BQU87TUFBRVIsUUFBUTtNQUFFNkIsaUJBQWlCO01BQUVDO0lBQVMsQ0FBQyxHQUFHSixJQUFJO0lBQzlELElBQUksQ0FBQzFCLFFBQVEsRUFDWCxNQUFNLElBQUlXLEtBQUssQ0FBQyxpREFBaUQsQ0FBQztJQUNwRSxJQUFJLENBQUNILE9BQU8sRUFDVixNQUFNLElBQUlHLEtBQUssQ0FBQyxvREFBb0QsQ0FBQztJQUN2RSxNQUFNdUIsU0FBUyxHQUFHaEQsVUFBVSxDQUFDMkMsaUJBQWlCLENBQUM7SUFFL0MsTUFBTU0sT0FBTyxHQUFHdEMsYUFBYSxDQUMzQmlDLFNBQVMsSUFBSUEsU0FBUyxDQUFDSyxPQUFPLEdBQUdMLFNBQVMsQ0FBQ0ssT0FBTyxHQUFHLEtBQUssQ0FDM0Q7SUFFRCxPQUFPLE9BQU8xQixHQUFHLEVBQUUyQixJQUFJLEtBQUs7TUFDMUIsTUFBTUEsSUFBSSxFQUFFO01BQ1osTUFBTUMsU0FBUyxHQUFJWCxJQUFJLENBQUNDLFNBQVMsSUFBSUQsSUFBSSxDQUFDQyxTQUFTLENBQUNWLElBQUksQ0FBQ1IsR0FBRyxDQUFDLElBQUssQ0FBQyxDQUFDO01BQ3BFLE1BQU1PLGFBQWEsR0FBR1IsT0FBTyxDQUFDUyxJQUFJLENBQUNSLEdBQUcsQ0FBQztNQUN2QyxJQUFJQSxHQUFHLENBQUNSLE1BQU0sS0FBSyxNQUFNLElBQUlRLEdBQUcsQ0FBQzZCLElBQUksQ0FBQ0MsVUFBVSxDQUFDSixPQUFPLENBQUMsRUFBRTtRQUN6RCxNQUFNckIsU0FBUyxHQUFHQyxFQUFFLEVBQUU7UUFDdEI7UUFDQSxNQUFNeUIsU0FBUyxHQUFHLElBQUlDLE1BQU0sQ0FBRSxHQUFFTixPQUFRLFNBQVEsRUFBRSxHQUFHLENBQUM7UUFDdEQsTUFBTSxHQUFHbEMsTUFBTSxDQUFDLEdBQUdRLEdBQUcsQ0FBQzZCLElBQUksQ0FBQ0ksS0FBSyxDQUFDRixTQUFTLENBQUMsSUFBSSxFQUFFO1FBQ2xELElBQUl6QyxVQUFVLENBQUNDLFFBQVEsRUFBRUMsTUFBTSxDQUFDLEVBQUU7VUFDaENvQyxTQUFTLENBQUNNLElBQUksR0FBRzFDLE1BQU07VUFDdkIsSUFBSTJDLElBQUk7VUFDUixJQUFJO1lBQ0YsSUFDRW5DLEdBQUcsQ0FBQ29DLEdBQUcsSUFDUHBDLEdBQUcsQ0FBQ29DLEdBQUcsQ0FBQ25DLE9BQU8sSUFDZkQsR0FBRyxDQUFDb0MsR0FBRyxDQUFDbkMsT0FBTyxDQUFDLGNBQWMsQ0FBQyxJQUMvQkQsR0FBRyxDQUFDb0MsR0FBRyxDQUFDbkMsT0FBTyxDQUFDLGNBQWMsQ0FBQyxDQUFDb0MsT0FBTyxDQUNyQyxxQkFBcUIsQ0FDdEIsS0FBSyxDQUFDLENBQUMsRUFDUjtjQUNBLE1BQU1DLElBQUksR0FBRyxJQUFJNUQsVUFBVSxDQUFDNkQsWUFBWSxFQUFFO2NBQzFDSixJQUFJLEdBQUcsTUFBTSxJQUFJSyxPQUFPLENBQUMsQ0FBQ0MsT0FBTyxFQUFFQyxNQUFNLEtBQUs7Z0JBQzVDSixJQUFJLENBQUNLLEtBQUssQ0FDUjNDLEdBQUcsQ0FBQ29DLEdBQUcsRUFDUCxDQUNFUSxHQUFHLEVBQ0hDLE1BRUMsRUFDREMsS0FBSyxLQUNGO2tCQUNILElBQUlGLEdBQUcsRUFBRTtvQkFDUEYsTUFBTSxDQUFDRSxHQUFHLENBQUM7a0JBQ2I7a0JBRUFILE9BQU8sQ0FBQztvQkFDTixHQUFHSSxNQUFNO29CQUNULEdBQUdDO2tCQUNMLENBQUMsQ0FBQztnQkFDSixDQUFDLENBQ0Y7Y0FDSCxDQUFDLENBQUM7WUFDSixDQUFDLE1BQU07Y0FDTCxNQUFNckIsU0FBUyxDQUFDekIsR0FBRyxFQUFFLE1BQU13QyxPQUFPLENBQUNDLE9BQU8sRUFBRSxDQUFDO1lBQy9DO1VBQ0YsQ0FBQyxDQUFDLE9BQU9oQyxDQUFDLEVBQUU7WUFDVlQsR0FBRyxDQUFDbUMsSUFBSSxHQUFHO2NBQ1RyQixNQUFNLEVBQUUsU0FBUztjQUNqQmlDLElBQUksRUFBRTtnQkFDSkMsT0FBTyxFQUFFdkMsQ0FBQyxDQUFDdUMsT0FBTztnQkFDbEJDLElBQUksRUFBRXhDLENBQUMsQ0FBQ3lDLElBQUksSUFBSSxjQUFjO2dCQUM5QkMsSUFBSSxFQUFFMUMsQ0FBQyxDQUFDMEM7Y0FDVjtZQUNGLENBQUM7WUFDRCxJQUFJNUMsYUFBYSxFQUFFO2NBQ2pCQSxhQUFhLENBQUNHLElBQUksQ0FBQ3JCLE9BQU8sRUFBRTtnQkFDMUJHLE1BQU07Z0JBQ05vQixLQUFLLEVBQUVILENBQUM7Z0JBQ1JLLE1BQU0sRUFBRSxTQUFTO2dCQUNqQkgsTUFBTSxFQUFFLFNBQVM7Z0JBQ2pCSSxNQUFNLEVBQUVULEVBQUUsRUFBRSxHQUFHRDtjQUNqQixDQUFDLENBQUM7WUFDSjtZQUNBO1lBQ0E7VUFDRjtVQUVBLElBQUk7WUFDRixNQUFNUSxNQUFNLEdBQUcsTUFBTXRCLFFBQVEsQ0FBQ0MsTUFBTSxDQUFDLENBQ25DMkMsSUFBSSxJQUFJbkMsR0FBRyxDQUFDRyxPQUFPLENBQUNnQyxJQUFJLEVBQ3hCbkMsR0FBRyxDQUNKO1lBQ0RBLEdBQUcsQ0FBQ21DLElBQUksR0FBRztjQUNUckIsTUFBTSxFQUFFLFNBQVM7Y0FDakJpQyxJQUFJLEVBQUVsQztZQUNSLENBQUM7WUFDRCxJQUFJTixhQUFhLEVBQUU7Y0FDakJBLGFBQWEsQ0FBQ0csSUFBSSxDQUFDckIsT0FBTyxFQUFFO2dCQUMxQkcsTUFBTTtnQkFDTnNCLE1BQU0sRUFBRSxTQUFTO2dCQUNqQkgsTUFBTSxFQUFFLFNBQVM7Z0JBQ2pCSSxNQUFNLEVBQUVULEVBQUUsRUFBRSxHQUFHRDtjQUNqQixDQUFDLENBQUM7WUFDSjtVQUNGLENBQUMsQ0FBQyxPQUFPSSxDQUFDLEVBQUU7WUFDVixNQUFNRyxLQUFLLEdBQ1RILENBQUMsWUFBWXpCLGFBQWEsR0FDdEJ5QixDQUFDLEdBQ0QsSUFBSVAsS0FBSyxDQUNQLHdDQUNJLDRKQUE0SixHQUM1Six1QkFBdUIsQ0FDNUI7WUFDUEYsR0FBRyxDQUFDbUMsSUFBSSxHQUFHO2NBQ1RyQixNQUFNLEVBQUUsU0FBUztjQUNqQmlDLElBQUksRUFBRTtnQkFDSkMsT0FBTyxFQUFFcEMsS0FBSyxDQUFDb0MsT0FBTztnQkFDdEI7Z0JBQ0FDLElBQUksRUFBRXJDLEtBQUssQ0FBQ3FDLElBQUk7Z0JBQ2hCO2dCQUNBRSxJQUFJLEVBQUV2QyxLQUFLLENBQUN1QztjQUNkO1lBQ0YsQ0FBQztZQUNELElBQUk1QyxhQUFhLEVBQUU7Y0FDakJBLGFBQWEsQ0FBQ0csSUFBSSxDQUFDckIsT0FBTyxFQUFFO2dCQUMxQkcsTUFBTTtnQkFDTm9CLEtBQUssRUFBRUgsQ0FBQztnQkFDUkssTUFBTSxFQUFFLFNBQVM7Z0JBQ2pCSCxNQUFNLEVBQUUsU0FBUztnQkFDakJJLE1BQU0sRUFBRVQsRUFBRSxFQUFFLEdBQUdEO2NBQ2pCLENBQUMsQ0FBQztZQUNKO1VBQ0Y7UUFDRixDQUFDLE1BQU07VUFDTCxNQUFNSSxDQUFDLEdBQUcsSUFBSTFCLG1CQUFtQixDQUFDUyxNQUFNLENBQUM7VUFDekNRLEdBQUcsQ0FBQ21DLElBQUksR0FBRztZQUNUckIsTUFBTSxFQUFFLFNBQVM7WUFDakJpQyxJQUFJLEVBQUU7Y0FDSkMsT0FBTyxFQUFFdkMsQ0FBQyxDQUFDdUMsT0FBTztjQUNsQkMsSUFBSSxFQUFFeEMsQ0FBQyxDQUFDd0M7WUFDVjtVQUNGLENBQUM7VUFDRGpELEdBQUcsQ0FBQ2MsTUFBTSxHQUFHLEdBQUc7VUFDaEIsSUFBSVAsYUFBYSxFQUFFO1lBQ2pCQSxhQUFhLENBQUNHLElBQUksQ0FBQyxXQUFXLEVBQUU7Y0FDOUJDLE1BQU0sRUFBRSxTQUFTO2NBQ2pCbkIsTUFBTTtjQUNOb0IsS0FBSyxFQUFFSDtZQUNULENBQUMsQ0FBQztVQUNKO1FBQ0Y7TUFDRjtJQUNGLENBQUM7RUFDSDtBQUNGLENBQUMsQ0FBQzs7QUFFSjtBQUNBLFNBQVNILEVBQUUsR0FBRztFQUNaLE1BQU0sQ0FBQzhDLE9BQU8sRUFBRUMsRUFBRSxDQUFDLEdBQUdDLE9BQU8sQ0FBQ0MsTUFBTSxFQUFFO0VBQ3RDLE9BQU9DLElBQUksQ0FBQ0MsS0FBSyxDQUFDTCxPQUFPLEdBQUcsSUFBSSxHQUFHQyxFQUFFLEdBQUcsR0FBRyxDQUFDO0FBQzlDO0FBRUEsZUFBZSxRQUFhckMsYUFBYSxFQUEyQiJ9