fusion-plugin-rpc
Version:
Fetch data on the server and client with an RPC style interface.
253 lines (220 loc) • 28.1 kB
JavaScript
/** 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.js';
import { formatApiPath } from './utils.js';
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,
// $FlowFixMe
code: error.code,
// $FlowFixMe
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 false && pluginFactory();
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNyYy9zZXJ2ZXIuanMiXSwibmFtZXMiOlsiYm9keXBhcnNlciIsImZvcm1pZGFibGUiLCJjcmVhdGVQbHVnaW4iLCJtZW1vaXplIiwiUm91dGVUYWdzVG9rZW4iLCJVbml2ZXJzYWxFdmVudHNUb2tlbiIsIk1pc3NpbmdIYW5kbGVyRXJyb3IiLCJSZXNwb25zZUVycm9yIiwiQm9keVBhcnNlck9wdGlvbnNUb2tlbiIsIlJQQ0hhbmRsZXJzVG9rZW4iLCJSUENIYW5kbGVyc0NvbmZpZ1Rva2VuIiwiZm9ybWF0QXBpUGF0aCIsInN0YXRLZXkiLCJoYXNIYW5kbGVyIiwiaGFuZGxlcnMiLCJtZXRob2QiLCJPYmplY3QiLCJwcm90b3R5cGUiLCJoYXNPd25Qcm9wZXJ0eSIsImNhbGwiLCJSUEMiLCJjb25zdHJ1Y3RvciIsImVtaXR0ZXIiLCJjdHgiLCJoZWFkZXJzIiwiRXJyb3IiLCJyZXF1ZXN0IiwiYXJncyIsInN0YXJ0VGltZSIsIm1zIiwic2NvcGVkRW1pdHRlciIsImZyb20iLCJlIiwiZW1pdCIsIm9yaWdpbiIsImVycm9yIiwicmVzdWx0Iiwic3RhdHVzIiwidGltaW5nIiwicGx1Z2luRmFjdG9yeSIsImRlcHMiLCJSb3V0ZVRhZ3MiLCJvcHRpb25hbCIsImJvZHlQYXJzZXJPcHRpb25zIiwicnBjQ29uZmlnIiwicHJvdmlkZXMiLCJzZXJ2aWNlIiwibWlkZGxld2FyZSIsInBhcnNlQm9keSIsImFwaVBhdGgiLCJuZXh0Iiwicm91dGVUYWdzIiwicGF0aCIsInN0YXJ0c1dpdGgiLCJwYXRoTWF0Y2giLCJSZWdFeHAiLCJtYXRjaCIsIm5hbWUiLCJib2R5IiwicmVxIiwiaW5kZXhPZiIsImZvcm0iLCJJbmNvbWluZ0Zvcm0iLCJQcm9taXNlIiwicmVzb2x2ZSIsInJlamVjdCIsInBhcnNlIiwiZXJyIiwiZmllbGRzIiwiZmlsZXMiLCJkYXRhIiwibWVzc2FnZSIsImNvZGUiLCJ0eXBlIiwibWV0YSIsInNlY29uZHMiLCJucyIsInByb2Nlc3MiLCJocnRp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QUY4QjtBQUc5Qm9CLGNBQUFBLEtBQUssRUFBRUg7QUFIdUIsYUFBaEM7QUFLRDtBQUNGO0FBQ0Y7QUFDRixLQTVIRDtBQTZIRDtBQTNKVSxDQUFELENBRGQ7QUErSkE7OztBQUNBLFNBQVNILEVBQVQsR0FBYztBQUNaLFFBQU0sQ0FBQzhDLE9BQUQsRUFBVUMsRUFBVixJQUFnQkMsT0FBTyxDQUFDQyxNQUFSLEVBQXRCO0FBQ0EsU0FBT0MsSUFBSSxDQUFDQyxLQUFMLENBQVdMLE9BQU8sR0FBRyxJQUFWLEdBQWlCQyxFQUFFLEdBQUcsR0FBakMsQ0FBUDtBQUNEOztBQUVELGVBQWlCLFNBQVlyQyxhQUFhLEVBQTFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqIENvcHlyaWdodCAoYykgMjAxOCBVYmVyIFRlY2hub2xvZ2llcywgSW5jLlxuICpcbiAqIFRoaXMgc291cmNlIGNvZGUgaXMgbGljZW5zZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlIGZvdW5kIGluIHRoZVxuICogTElDRU5TRSBmaWxlIGluIHRoZSByb290IGRpcmVjdG9yeSBvZiB0aGlzIHNvdXJjZSB0cmVlLlxuICpcbiAqIEBmbG93XG4gKi9cblxuLyogZXNsaW50LWVudiBub2RlICovXG5cbmltcG9ydCBib2R5cGFyc2VyIGZyb20gJ2tvYS1ib2R5cGFyc2VyJztcbmltcG9ydCBmb3JtaWRhYmxlIGZyb20gJ2Zvcm1pZGFibGUnO1xuXG5pbXBvcnQge2NyZWF0ZVBsdWdpbiwgbWVtb2l6ZSwgUm91dGVUYWdzVG9rZW59IGZyb20gJ2Z1c2lvbi1jb3JlJztcbmltcG9ydCB0eXBlIHtDb250ZXh0fSBmcm9tICdmdXNpb24tY29yZSc7XG5pbXBvcnQge1VuaXZlcnNhbEV2ZW50c1Rva2VufSBmcm9tICdmdXNpb24tcGx1Z2luLXVuaXZlcnNhbC1ldmVudHMnO1xuaW1wb3J0IHR5cGUge0ZldGNofSBmcm9tICdmdXNpb24tdG9rZW5zJztcblxuaW1wb3J0IE1pc3NpbmdIYW5kbGVyRXJyb3IgZnJvbSAnLi9taXNzaW5nLWhhbmRsZXItZXJyb3InO1xuaW1wb3J0IFJlc3BvbnNlRXJyb3IgZnJvbSAnLi9yZXNwb25zZS1lcnJvcic7XG5pbXBvcnQge1xuICBCb2R5UGFyc2VyT3B0aW9uc1Rva2VuLFxuICBSUENIYW5kbGVyc1Rva2VuLFxuICBSUENIYW5kbGVyc0NvbmZpZ1Rva2VuLFxufSBmcm9tICcuL3Rva2Vucy5qcyc7XG5pbXBvcnQgdHlwZSB7SGFuZGxlclR5cGV9IGZyb20gJy4vdG9rZW5zLmpzJztcbmltcG9ydCB0eXBlIHtSUENQbHVnaW5UeXBlLCBJRW1pdHRlcn0gZnJvbSAnLi90eXBlcy5qcyc7XG5pbXBvcnQge2Zvcm1hdEFwaVBhdGh9IGZyb20gJy4vdXRpbHMuanMnO1xuXG5jb25zdCBzdGF0S2V5ID0gJ3JwYzptZXRob2QnO1xuXG4vKiBIZWxwZXIgZnVuY3Rpb24gKi9cbmZ1bmN0aW9uIGhhc0hhbmRsZXIoaGFuZGxlcnM6IEhhbmRsZXJUeXBlLCBtZXRob2Q6IHN0cmluZyk6IGJvb2xlYW4ge1xuICByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGhhbmRsZXJzLCBtZXRob2QpO1xufVxuXG5jbGFzcyBSUEMge1xuICBjdHg6ID9Db250ZXh0O1xuICBlbWl0dGVyOiA/SUVtaXR0ZXI7XG4gIGhhbmRsZXJzOiA/SGFuZGxlclR5cGU7XG4gIGZldGNoOiA/RmV0Y2g7XG5cbiAgY29uc3RydWN0b3IoZW1pdHRlcjogSUVtaXR0ZXIsIGhhbmRsZXJzOiBhbnksIGN0eDogQ29udGV4dCk6IFJQQyB7XG4gICAgaWYgKCFjdHggfHwgIWN0eC5oZWFkZXJzKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2Z1c2lvbi1wbHVnaW4tcnBjIHJlcXVpcmVzIGBjdHhgJyk7XG4gICAgfVxuICAgIHRoaXMuY3R4ID0gY3R4O1xuICAgIHRoaXMuZW1pdHRlciA9IGVtaXR0ZXI7XG4gICAgdGhpcy5oYW5kbGVycyA9IGhhbmRsZXJzO1xuXG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICBhc3luYyByZXF1ZXN0PFRBcmdzLCBUUmVzdWx0PihtZXRob2Q6IHN0cmluZywgYXJnczogVEFyZ3MpOiBQcm9taXNlPFRSZXN1bHQ+IHtcbiAgICBjb25zdCBzdGFydFRpbWUgPSBtcygpO1xuXG4gICAgaWYgKCF0aGlzLmN0eCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdmdXNpb24tcGx1Z2luLXJwYyByZXF1aXJlcyBgY3R4YCcpO1xuICAgIH1cbiAgICBpZiAoIXRoaXMuZW1pdHRlcikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdmdXNpb24tcGx1Z2luLXJwYyByZXF1aXJlcyBgZW1pdHRlcmAnKTtcbiAgICB9XG4gICAgY29uc3Qgc2NvcGVkRW1pdHRlciA9IHRoaXMuZW1pdHRlci5mcm9tKHRoaXMuY3R4KTtcblxuICAgIGlmICghdGhpcy5oYW5kbGVycykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdmdXNpb24tcGx1Z2luLXJwYyByZXF1aXJlcyBgaGFuZGxlcnNgJyk7XG4gICAgfVxuICAgIGlmICghaGFzSGFuZGxlcih0aGlzLmhhbmRsZXJzLCBtZXRob2QpKSB7XG4gICAgICBjb25zdCBlID0gbmV3IE1pc3NpbmdIYW5kbGVyRXJyb3IobWV0aG9kKTtcbiAgICAgIGlmIChzY29wZWRFbWl0dGVyKSB7XG4gICAgICAgIHNjb3BlZEVtaXR0ZXIuZW1pdCgncnBjOmVycm9yJywge1xuICAgICAgICAgIG1ldGhvZCxcbiAgICAgICAgICBvcmlnaW46ICdzZXJ2ZXInLFxuICAgICAgICAgIGVycm9yOiBlLFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICAgIHRocm93IGU7XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB0aGlzLmhhbmRsZXJzW21ldGhvZF0oYXJncywgdGhpcy5jdHgpO1xuICAgICAgaWYgKHNjb3BlZEVtaXR0ZXIpIHtcbiAgICAgICAgc2NvcGVkRW1pdHRlci5lbWl0KHN0YXRLZXksIHtcbiAgICAgICAgICBtZXRob2QsXG4gICAgICAgICAgc3RhdHVzOiAnc3VjY2VzcycsXG4gICAgICAgICAgb3JpZ2luOiAnc2VydmVyJyxcbiAgICAgICAgICB0aW1pbmc6IG1zKCkgLSBzdGFydFRpbWUsXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBpZiAoc2NvcGVkRW1pdHRlcikge1xuICAgICAgICBzY29wZWRFbWl0dGVyLmVtaXQoc3RhdEtleSwge1xuICAgICAgICAgIG1ldGhvZCxcbiAgICAgICAgICBlcnJvcjogZSxcbiAgICAgICAgICBzdGF0dXM6ICdmYWlsdXJlJyxcbiAgICAgICAgICBvcmlnaW46ICdzZXJ2ZXInLFxuICAgICAgICAgIHRpbWluZzogbXMoKSAtIHN0YXJ0VGltZSxcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICB0aHJvdyBlO1xuICAgIH1cbiAgfVxufVxuXG5jb25zdCBwbHVnaW5GYWN0b3J5OiAoKSA9PiBSUENQbHVnaW5UeXBlID0gKCkgPT5cbiAgY3JlYXRlUGx1Z2luKHtcbiAgICBkZXBzOiB7XG4gICAgICBSb3V0ZVRhZ3M6IFJvdXRlVGFnc1Rva2VuLm9wdGlvbmFsLFxuICAgICAgZW1pdHRlcjogVW5pdmVyc2FsRXZlbnRzVG9rZW4sXG4gICAgICBoYW5kbGVyczogUlBDSGFuZGxlcnNUb2tlbixcbiAgICAgIGJvZHlQYXJzZXJPcHRpb25zOiBCb2R5UGFyc2VyT3B0aW9uc1Rva2VuLm9wdGlvbmFsLFxuICAgICAgcnBjQ29uZmlnOiBSUENIYW5kbGVyc0NvbmZpZ1Rva2VuLm9wdGlvbmFsLFxuICAgIH0sXG5cbiAgICBwcm92aWRlczogKGRlcHMpID0+IHtcbiAgICAgIGNvbnN0IHtlbWl0dGVyLCBoYW5kbGVyc30gPSBkZXBzO1xuXG4gICAgICBjb25zdCBzZXJ2aWNlID0ge1xuICAgICAgICBmcm9tOiBtZW1vaXplKChjdHgpID0+IG5ldyBSUEMoZW1pdHRlciwgaGFuZGxlcnMsIGN0eCkpLFxuICAgICAgfTtcbiAgICAgIHJldHVybiBzZXJ2aWNlO1xuICAgIH0sXG5cbiAgICBtaWRkbGV3YXJlOiAoZGVwcykgPT4ge1xuICAgICAgY29uc3Qge2VtaXR0ZXIsIGhhbmRsZXJzLCBib2R5UGFyc2VyT3B0aW9ucywgcnBjQ29uZmlnfSA9IGRlcHM7XG4gICAgICBpZiAoIWhhbmRsZXJzKVxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01pc3NpbmcgaGFuZGxlcnMgcmVnaXN0ZXJlZCB0byBSUENIYW5kbGVyc1Rva2VuJyk7XG4gICAgICBpZiAoIWVtaXR0ZXIpXG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTWlzc2luZyBlbWl0dGVyIHJlZ2lzdGVyZWQgdG8gVW5pdmVyc2FsRXZlbnRzVG9rZW4nKTtcbiAgICAgIGNvbnN0IHBhcnNlQm9keSA9IGJvZHlwYXJzZXIoYm9keVBhcnNlck9wdGlvbnMpO1xuXG4gICAgICBjb25zdCBhcGlQYXRoID0gZm9ybWF0QXBpUGF0aChcbiAgICAgICAgcnBjQ29uZmlnICYmIHJwY0NvbmZpZy5hcGlQYXRoID8gcnBjQ29uZmlnLmFwaVBhdGggOiAnYXBpJ1xuICAgICAgKTtcblxuICAgICAgcmV0dXJuIGFzeW5jIChjdHgsIG5leHQpID0+IHtcbiAgICAgICAgYXdhaXQgbmV4dCgpO1xuICAgICAgICBjb25zdCByb3V0ZVRhZ3MgPSAoZGVwcy5Sb3V0ZVRhZ3MgJiYgZGVwcy5Sb3V0ZVRhZ3MuZnJvbShjdHgpKSB8fCB7fTtcbiAgICAgICAgY29uc3Qgc2NvcGVkRW1pdHRlciA9IGVtaXR0ZXIuZnJvbShjdHgpO1xuICAgICAgICBpZiAoY3R4Lm1ldGhvZCA9PT0gJ1BPU1QnICYmIGN0eC5wYXRoLnN0YXJ0c1dpdGgoYXBpUGF0aCkpIHtcbiAgICAgICAgICBjb25zdCBzdGFydFRpbWUgPSBtcygpO1xuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11c2VsZXNzLWVzY2FwZVxuICAgICAgICAgIGNvbnN0IHBhdGhNYXRjaCA9IG5ldyBSZWdFeHAoYCR7YXBpUGF0aH0oW14vXSspYCwgJ2knKTtcbiAgICAgICAgICBjb25zdCBbLCBtZXRob2RdID0gY3R4LnBhdGgubWF0Y2gocGF0aE1hdGNoKSB8fCBbXTtcbiAgICAgICAgICBpZiAoaGFzSGFuZGxlcihoYW5kbGVycywgbWV0aG9kKSkge1xuICAgICAgICAgICAgcm91dGVUYWdzLm5hbWUgPSBtZXRob2Q7XG4gICAgICAgICAgICBsZXQgYm9keTtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICBjdHgucmVxICYmXG4gICAgICAgICAgICAgICAgY3R4LnJlcS5oZWFkZXJzICYmXG4gICAgICAgICAgICAgICAgY3R4LnJlcS5oZWFkZXJzWydjb250ZW50LXR5cGUnXSAmJlxuICAgICAgICAgICAgICAgIGN0eC5yZXEuaGVhZGVyc1snY29udGVudC10eXBlJ10uaW5kZXhPZihcbiAgICAgICAgICAgICAgICAgICdtdWx0aXBhcnQvZm9ybS1kYXRhJ1xuICAgICAgICAgICAgICAgICkgIT09IC0xXG4gICAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGZvcm0gPSBuZXcgZm9ybWlkYWJsZS5JbmNvbWluZ0Zvcm0oKTtcbiAgICAgICAgICAgICAgICBib2R5ID0gYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgICAgICAgZm9ybS5wYXJzZShjdHgucmVxLCAoZXJyLCBmaWVsZHM6IHtbc3RyaW5nXTogYW55fSwgZmlsZXMpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGVycikge1xuICAgICAgICAgICAgICAgICAgICAgIHJlamVjdChlcnIpO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgICAgLi4uZmllbGRzLFxuICAgICAgICAgICAgICAgICAgICAgIC4uLmZpbGVzLFxuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGF3YWl0IHBhcnNlQm9keShjdHgsICgpID0+IFByb21pc2UucmVzb2x2ZSgpKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICBjdHguYm9keSA9IHtcbiAgICAgICAgICAgICAgICBzdGF0dXM6ICdmYWlsdXJlJyxcbiAgICAgICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgICBtZXNzYWdlOiBlLm1lc3NhZ2UsXG4gICAgICAgICAgICAgICAgICBjb2RlOiBlLnR5cGUgfHwgJ0VSUl9CQURfQk9EWScsXG4gICAgICAgICAgICAgICAgICBtZXRhOiBlLm1ldGEsXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgaWYgKHNjb3BlZEVtaXR0ZXIpIHtcbiAgICAgICAgICAgICAgICBzY29wZWRFbWl0dGVyLmVtaXQoc3RhdEtleSwge1xuICAgICAgICAgICAgICAgICAgbWV0aG9kLFxuICAgICAgICAgICAgICAgICAgZXJyb3I6IGUsXG4gICAgICAgICAgICAgICAgICBzdGF0dXM6ICdmYWlsdXJlJyxcbiAgICAgICAgICAgICAgICAgIG9yaWdpbjogJ2Jyb3dzZXInLFxuICAgICAgICAgICAgICAgICAgdGltaW5nOiBtcygpIC0gc3RhcnRUaW1lLFxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIC8vIGRvbid0IHRyeSB0byBjYWxsIGhhbmRsZXJcbiAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBoYW5kbGVyc1ttZXRob2RdKFxuICAgICAgICAgICAgICAgIGJvZHkgfHwgY3R4LnJlcXVlc3QuYm9keSxcbiAgICAgICAgICAgICAgICBjdHhcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgY3R4LmJvZHkgPSB7XG4gICAgICAgICAgICAgICAgc3RhdHVzOiAnc3VjY2VzcycsXG4gICAgICAgICAgICAgICAgZGF0YTogcmVzdWx0LFxuICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICBpZiAoc2NvcGVkRW1pdHRlcikge1xuICAgICAgICAgICAgICAgIHNjb3BlZEVtaXR0ZXIuZW1pdChzdGF0S2V5LCB7XG4gICAgICAgICAgICAgICAgICBtZXRob2QsXG4gICAgICAgICAgICAgICAgICBzdGF0dXM6ICdzdWNjZXNzJyxcbiAgICAgICAgICAgICAgICAgIG9yaWdpbjogJ2Jyb3dzZXInLFxuICAgICAgICAgICAgICAgICAgdGltaW5nOiBtcygpIC0gc3RhcnRUaW1lLFxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgIGNvbnN0IGVycm9yID1cbiAgICAgICAgICAgICAgICBlIGluc3RhbmNlb2YgUmVzcG9uc2VFcnJvclxuICAgICAgICAgICAgICAgICAgPyBlXG4gICAgICAgICAgICAgICAgICA6IG5ldyBFcnJvcihcbiAgICAgICAgICAgICAgICAgICAgICBfX0RFVl9fXG4gICAgICAgICAgICAgICAgICAgICAgICA/ICdVbmtub3duRXJyb3IgLSBVc2UgUmVzcG9uc2VFcnJvciBmcm9tIGZ1c2lvbi1wbHVnaW4tcnBjIChvciBmdXNpb24tcGx1Z2luLXJwYy1yZWR1eC1yZWFjdCBpZiB5b3UgYXJlIHVzaW5nIFJlYWN0KSBwYWNrYWdlIGZvciBtb3JlIGRldGFpbGVkIGVycm9yIG1lc3NhZ2VzJ1xuICAgICAgICAgICAgICAgICAgICAgICAgOiAnSW50ZXJuYWwgU2VydmVyIEVycm9yJ1xuICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICBjdHguYm9keSA9IHtcbiAgICAgICAgICAgICAgICBzdGF0dXM6ICdmYWlsdXJlJyxcbiAgICAgICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgICBtZXNzYWdlOiBlcnJvci5tZXNzYWdlLFxuICAgICAgICAgICAgICAgICAgLy8gJEZsb3dGaXhNZVxuICAgICAgICAgICAgICAgICAgY29kZTogZXJyb3IuY29kZSxcbiAgICAgICAgICAgICAgICAgIC8vICRGbG93Rml4TWVcbiAgICAgICAgICAgICAgICAgIG1ldGE6IGVycm9yLm1ldGEsXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgaWYgKHNjb3BlZEVtaXR0ZXIpIHtcbiAgICAgICAgICAgICAgICBzY29wZWRFbWl0dGVyLmVtaXQoc3RhdEtleSwge1xuICAgICAgICAgICAgICAgICAgbWV0aG9kLFxuICAgICAgICAgICAgICAgICAgZXJyb3I6IGUsXG4gICAgICAgICAgICAgICAgICBzdGF0dXM6ICdmYWlsdXJlJyxcbiAgICAgICAgICAgICAgICAgIG9yaWdpbjogJ2Jyb3dzZXInLFxuICAgICAgICAgICAgICAgICAgdGltaW5nOiBtcygpIC0gc3RhcnRUaW1lLFxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGNvbnN0IGUgPSBuZXcgTWlzc2luZ0hhbmRsZXJFcnJvcihtZXRob2QpO1xuICAgICAgICAgICAgY3R4LmJvZHkgPSB7XG4gICAgICAgICAgICAgIHN0YXR1czogJ2ZhaWx1cmUnLFxuICAgICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgbWVzc2FnZTogZS5tZXNzYWdlLFxuICAgICAgICAgICAgICAgIGNvZGU6IGUuY29kZSxcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBjdHguc3RhdHVzID0gNDA0O1xuICAgICAgICAgICAgaWYgKHNjb3BlZEVtaXR0ZXIpIHtcbiAgICAgICAgICAgICAgc2NvcGVkRW1pdHRlci5lbWl0KCdycGM6ZXJyb3InLCB7XG4gICAgICAgICAgICAgICAgb3JpZ2luOiAnYnJvd3NlcicsXG4gICAgICAgICAgICAgICAgbWV0aG9kLFxuICAgICAgICAgICAgICAgIGVycm9yOiBlLFxuICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgfSxcbiAgfSk7XG5cbi8qIEhlbHBlciBmdW5jdGlvbnMgKi9cbmZ1bmN0aW9uIG1zKCkge1xuICBjb25zdCBbc2Vjb25kcywgbnNdID0gcHJvY2Vzcy5ocnRpbWUoKTtcbiAgcmV0dXJuIE1hdGgucm91bmQoc2Vjb25kcyAqIDEwMDAgKyBucyAvIDFlNik7XG59XG5cbmV4cG9ydCBkZWZhdWx0ICgoX19OT0RFX18gJiYgcGx1Z2luRmFjdG9yeSgpOiBhbnkpOiBSUENQbHVnaW5UeXBlKTtcbiJdfQ==