UNPKG

@selfage/service_handler

Version:

Http-based service handlers on top of expressjs.

119 lines 19.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BaseRemoteCallHandler = void 0; const getStream = require("get-stream"); const promClient = require("prom-client"); const stream_message_reader_1 = require("./stream_message_reader"); const http_error_1 = require("@selfage/http_error"); const serializer_1 = require("@selfage/message/serializer"); const stringifier_1 = require("@selfage/message/stringifier"); const service_descriptor_1 = require("@selfage/service_descriptor"); let TOTAL_COUNTER = new promClient.Counter({ name: "remote_calls_total", help: "The total number of calls received.", labelNames: ["path"], }); let FAILURE_COUNTER = new promClient.Counter({ name: "remote_calls_failure", help: "The number of failed calls.", labelNames: ["path", "errorCode"], }); class BaseRemoteCallHandler { static create(allowOrigin) { return new BaseRemoteCallHandler(allowOrigin); } constructor(allowOrigin) { this.allowOrigin = allowOrigin; } async handle(remoteCallHandler, req, res) { TOTAL_COUNTER.inc({ path: remoteCallHandler.descriptor.service.path + remoteCallHandler.descriptor.path, }); // Add CORS rules. res.setHeader("Access-Control-Allow-Origin", this.allowOrigin); res.setHeader("Access-Control-Allow-Methods", "*"); res.setHeader("Access-Control-Allow-Headers", "*"); let requestId = `${Math.floor(Date.now() / 1000)}-${Math.floor(Math.random() * 10000)}`; let loggingPrefix = `Request ${requestId}:`; console.info(`${loggingPrefix} ${req.url}.`); try { let response = await this.handleRequest(loggingPrefix, remoteCallHandler, req); await this.sendResponse(res, response, remoteCallHandler.descriptor.response.messageType); } catch (e) { console.error(`${loggingPrefix} ${e.stack ?? e.message ?? e}`); let statusCode = e.statusCode ?? http_error_1.StatusCode.InternalServerError; FAILURE_COUNTER.inc({ path: remoteCallHandler.descriptor.service.path + remoteCallHandler.descriptor.path, errorCode: statusCode, }); res.sendStatus(statusCode); await new Promise((resolve) => res.end(resolve)); } } async handleRequest(loggingPrefix, remoteCallHandler, req) { let args = [loggingPrefix]; if (remoteCallHandler.descriptor.body.messageType) { let bodyBuffer = await getStream.buffer(req, { maxBuffer: 1 * 1024 * 1024, }); args.push(this.deserialize(loggingPrefix, bodyBuffer, remoteCallHandler.descriptor.body.messageType, `body`)); } else if (remoteCallHandler.descriptor.body.streamMessageType) { // NOTE: Doesn't work! Client side requires http2 to enable custom streaming with unspecified content length. let streamReader = new stream_message_reader_1.StreamMessageReader(req, remoteCallHandler.descriptor.body.streamMessageType); args.push(streamReader); } else if (remoteCallHandler.descriptor.body.primitiveType === service_descriptor_1.PrimitveTypeForBody.BYTES) { args.push(req); } else { throw (0, http_error_1.newInternalServerErrorError)(`Unsupported server request body.`); } if (remoteCallHandler.descriptor.metadata) { args.push(this.destringify(loggingPrefix, req.query[remoteCallHandler.descriptor.metadata.key], remoteCallHandler.descriptor.metadata.type, `metadata`)); } if (remoteCallHandler.descriptor.authKey) { let authStr = req.header(remoteCallHandler.descriptor.authKey); if (!authStr) { throw (0, http_error_1.newUnauthorizedError)(`No auth string is found in the header ${remoteCallHandler.descriptor.authKey}.`); } args.push(authStr); } return await remoteCallHandler.handle(...args); } destringify(loggingPrefix, value, messageDescriptor, what) { let ret; try { ret = (0, stringifier_1.destringifyMessage)(value, messageDescriptor); } catch (e) { throw (0, http_error_1.newBadRequestError)(`Unable to destringify ${what}. Raw string: ${value}.`, e); } if (!ret) { throw (0, http_error_1.newBadRequestError)(`${what} is empty.`); } return ret; } deserialize(loggingPrefix, value, messageDescriptor, what) { let ret; try { ret = (0, serializer_1.deserializeMessage)(value, messageDescriptor); } catch (e) { throw (0, http_error_1.newBadRequestError)(`Unable to deserialize ${what}. Raw value as base64: ${Buffer.from(value).toString("base64")}.`, e); } if (!ret) { throw (0, http_error_1.newBadRequestError)(`${loggingPrefix} ${what} is empty.`); } return ret; } async sendResponse(res, handlerResponse, messageDescriptor) { await new Promise((resolve) => res.end((0, serializer_1.serializeMessage)(handlerResponse, messageDescriptor), resolve)); } } exports.BaseRemoteCallHandler = BaseRemoteCallHandler; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFzZV9yZW1vdGVfY2FsbF9oYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiYmFzZV9yZW1vdGVfY2FsbF9oYW5kbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLHdDQUF5QztBQUN6QywwQ0FBMkM7QUFDM0MsbUVBQThEO0FBQzlELG9EQUs2QjtBQUU3Qiw0REFHcUM7QUFDckMsOERBQWtFO0FBQ2xFLG9FQUFrRTtBQUdsRSxJQUFJLGFBQWEsR0FBRyxJQUFJLFVBQVUsQ0FBQyxPQUFPLENBQUM7SUFDekMsSUFBSSxFQUFFLG9CQUFvQjtJQUMxQixJQUFJLEVBQUUscUNBQXFDO0lBQzNDLFVBQVUsRUFBRSxDQUFDLE1BQU0sQ0FBQztDQUNyQixDQUFDLENBQUM7QUFDSCxJQUFJLGVBQWUsR0FBRyxJQUFJLFVBQVUsQ0FBQyxPQUFPLENBQUM7SUFDM0MsSUFBSSxFQUFFLHNCQUFzQjtJQUM1QixJQUFJLEVBQUUsNkJBQTZCO0lBQ25DLFVBQVUsRUFBRSxDQUFDLE1BQU0sRUFBRSxXQUFXLENBQUM7Q0FDbEMsQ0FBQyxDQUFDO0FBRUgsTUFBYSxxQkFBcUI7SUFDekIsTUFBTSxDQUFDLE1BQU0sQ0FBQyxXQUFtQjtRQUN0QyxPQUFPLElBQUkscUJBQXFCLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDaEQsQ0FBQztJQUVELFlBQTJCLFdBQW1CO1FBQW5CLGdCQUFXLEdBQVgsV0FBVyxDQUFRO0lBQUcsQ0FBQztJQUUzQyxLQUFLLENBQUMsTUFBTSxDQUNqQixpQkFBNkMsRUFDN0MsR0FBb0IsRUFDcEIsR0FBcUI7UUFFckIsYUFBYSxDQUFDLEdBQUcsQ0FBQztZQUNoQixJQUFJLEVBQ0YsaUJBQWlCLENBQUMsVUFBVSxDQUFDLE9BQU8sQ0FBQyxJQUFJO2dCQUN6QyxpQkFBaUIsQ0FBQyxVQUFVLENBQUMsSUFBSTtTQUNwQyxDQUFDLENBQUM7UUFDSCxrQkFBa0I7UUFDbEIsR0FBRyxDQUFDLFNBQVMsQ0FBQyw2QkFBNkIs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RjtBQWpLRCxzREFpS0MiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgZXhwcmVzcyA9IHJlcXVpcmUoXCJleHByZXNzXCIpO1xuaW1wb3J0IGdldFN0cmVhbSA9IHJlcXVpcmUoXCJnZXQtc3RyZWFtXCIpO1xuaW1wb3J0IHByb21DbGllbnQgPSByZXF1aXJlKFwicHJvbS1jbGllbnRcIik7XG5pbXBvcnQgeyBTdHJlYW1NZXNzYWdlUmVhZGVyIH0gZnJvbSBcIi4vc3RyZWFtX21lc3NhZ2VfcmVhZGVyXCI7XG5pbXBvcnQge1xuICBTdGF0dXNDb2RlLFxuICBuZXdCYWRSZXF1ZXN0RXJyb3IsXG4gIG5ld0ludGVybmFsU2VydmVyRXJyb3JFcnJvcixcbiAgbmV3VW5hdXRob3JpemVkRXJyb3IsXG59IGZyb20gXCJAc2VsZmFnZS9odHRwX2Vycm9yXCI7XG5pbXBvcnQgeyBNZXNzYWdlRGVzY3JpcHRvciB9IGZyb20gXCJAc2VsZmFnZS9tZXNzYWdlL2Rlc2NyaXB0b3JcIjtcbmltcG9ydCB7XG4gIGRlc2VyaWFsaXplTWVzc2FnZSxcbiAgc2VyaWFsaXplTWVzc2FnZSxcbn0gZnJvbSBcIkBzZWxmYWdlL21lc3NhZ2Uvc2VyaWFsaXplclwiO1xuaW1wb3J0IHsgZGVzdHJpbmdpZnlNZXNzYWdlIH0gZnJvbSBcIkBzZWxmYWdlL21lc3NhZ2Uvc3RyaW5naWZpZXJcIjtcbmltcG9ydCB7IFByaW1pdHZlVHlwZUZvckJvZHkgfSBmcm9tIFwiQHNlbGZhZ2Uvc2VydmljZV9kZXNjcmlwdG9yXCI7XG5pbXBvcnQgeyBSZW1vdGVDYWxsSGFuZGxlckludGVyZmFjZSB9IGZyb20gXCJAc2VsZmFnZS9zZXJ2aWNlX2Rlc2NyaXB0b3IvcmVtb3RlX2NhbGxfaGFuZGxlcl9pbnRlcmZhY2VcIjtcblxubGV0IFRPVEFMX0NPVU5URVIgPSBuZXcgcHJvbUNsaWVudC5Db3VudGVyKHtcbiAgbmFtZTogXCJyZW1vdGVfY2FsbHNfdG90YWxcIixcbiAgaGVscDogXCJUaGUgdG90YWwgbnVtYmVyIG9mIGNhbGxzIHJlY2VpdmVkLlwiLFxuICBsYWJlbE5hbWVzOiBbXCJwYXRoXCJdLFxufSk7XG5sZXQgRkFJTFVSRV9DT1VOVEVSID0gbmV3IHByb21DbGllbnQuQ291bnRlcih7XG4gIG5hbWU6IFwicmVtb3RlX2NhbGxzX2ZhaWx1cmVcIixcbiAgaGVscDogXCJUaGUgbnVtYmVyIG9mIGZhaWxlZCBjYWxscy5cIixcbiAgbGFiZWxOYW1lczogW1wicGF0aFwiLCBcImVycm9yQ29kZVwiXSxcbn0pO1xuXG5leHBvcnQgY2xhc3MgQmFzZVJlbW90ZUNhbGxIYW5kbGVyIHtcbiAgcHVibGljIHN0YXRpYyBjcmVhdGUoYWxsb3dPcmlnaW46IHN0cmluZyk6IEJhc2VSZW1vdGVDYWxsSGFuZGxlciB7XG4gICAgcmV0dXJuIG5ldyBCYXNlUmVtb3RlQ2FsbEhhbmRsZXIoYWxsb3dPcmlnaW4pO1xuICB9XG5cbiAgcHVibGljIGNvbnN0cnVjdG9yKHByaXZhdGUgYWxsb3dPcmlnaW46IHN0cmluZykge31cblxuICBwdWJsaWMgYXN5bmMgaGFuZGxlKFxuICAgIHJlbW90ZUNhbGxIYW5kbGVyOiBSZW1vdGVDYWxsSGFuZGxlckludGVyZmFjZSxcbiAgICByZXE6IGV4cHJlc3MuUmVxdWVzdCxcbiAgICByZXM6IGV4cHJlc3MuUmVzcG9uc2UsXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIFRPVEFMX0NPVU5URVIuaW5jKHtcbiAgICAgIHBhdGg6XG4gICAgICAgIHJlbW90ZUNhbGxIYW5kbGVyLmRlc2NyaXB0b3Iuc2VydmljZS5wYXRoICtcbiAgICAgICAgcmVtb3RlQ2FsbEhhbmRsZXIuZGVzY3JpcHRvci5wYXRoLFxuICAgIH0pO1xuICAgIC8vIEFkZCBDT1JTIHJ1bGVzLlxuICAgIHJlcy5zZXRIZWFkZXIoXCJBY2Nlc3MtQ29udHJvbC1BbGxvdy1PcmlnaW5cIiwgdGhpcy5hbGxvd09yaWdpbik7XG4gICAgcmVzLnNldEhlYWRlcihcIkFjY2Vzcy1Db250cm9sLUFsbG93LU1ldGhvZHNcIiwgXCIqXCIpO1xuICAgIHJlcy5zZXRIZWFkZXIoXCJBY2Nlc3MtQ29udHJvbC1BbGxvdy1IZWFkZXJzXCIsIFwiKlwiKTtcblxuICAgIGxldCByZXF1ZXN0SWQgPSBgJHtNYXRoLmZsb29yKERhdGUubm93KCkgLyAxMDAwKX0tJHtNYXRoLmZsb29yKFxuICAgICAgTWF0aC5yYW5kb20oKSAqIDEwMDAwLFxuICAgICl9YDtcbiAgICBsZXQgbG9nZ2luZ1ByZWZpeCA9IGBSZXF1ZXN0ICR7cmVxdWVzdElkfTpgO1xuICAgIGNvbnNvbGUuaW5mbyhgJHtsb2dnaW5nUHJlZml4fSAke3JlcS51cmx9LmApO1xuXG4gICAgdHJ5IHtcbiAgICAgIGxldCByZXNwb25zZSA9IGF3YWl0IHRoaXMuaGFuZGxlUmVxdWVzdChcbiAgICAgICAgbG9nZ2luZ1ByZWZpeCxcbiAgICAgICAgcmVtb3RlQ2FsbEhhbmRsZXIsXG4gICAgICAgIHJlcSxcbiAgICAgICk7XG4gICAgICBhd2FpdCB0aGlzLnNlbmRSZXNwb25zZShcbiAgICAgICAgcmVzLFxuICAgICAgICByZXNwb25zZSxcbiAgICAgICAgcmVtb3RlQ2FsbEhhbmRsZXIuZGVzY3JpcHRvci5yZXNwb25zZS5tZXNzYWdlVHlwZSxcbiAgICAgICk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgY29uc29sZS5lcnJvcihgJHtsb2dnaW5nUHJlZml4fSAke2Uuc3RhY2sgPz8gZS5tZXNzYWdlID8/IGV9YCk7XG4gICAgICBsZXQgc3RhdHVzQ29kZSA9IGUuc3RhdHVzQ29kZSA/PyBTdGF0dXNDb2RlLkludGVybmFsU2VydmVyRXJyb3I7XG4gICAgICBGQUlMVVJFX0NPVU5URVIuaW5jKHtcbiAgICAgICAgcGF0aDpcbiAgICAgICAgICByZW1vdGVDYWxsSGFuZGxlci5kZXNjcmlwdG9yLnNlcnZpY2UucGF0aCArXG4gICAgICAgICAgcmVtb3RlQ2FsbEhhbmRsZXIuZGVzY3JpcHRvci5wYXRoLFxuICAgICAgICBlcnJvckNvZGU6IHN0YXR1c0NvZGUsXG4gICAgICB9KTtcbiAgICAgIHJlcy5zZW5kU3RhdHVzKHN0YXR1c0NvZGUpO1xuICAgICAgYXdhaXQgbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUpID0+IHJlcy5lbmQocmVzb2x2ZSkpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgaGFuZGxlUmVxdWVzdChcbiAgICBsb2dnaW5nUHJlZml4OiBzdHJpbmcsXG4gICAgcmVtb3RlQ2FsbEhhbmRsZXI6IFJlbW90ZUNhbGxIYW5kbGVySW50ZXJmYWNlLFxuICAgIHJlcTogZXhwcmVzcy5SZXF1ZXN0LFxuICApOiBQcm9taXNlPGFueT4ge1xuICAgIGxldCBhcmdzOiBhbnlbXSA9IFtsb2dnaW5nUHJlZml4XTtcbiAgICBpZiAocmVtb3RlQ2FsbEhhbmRsZXIuZGVzY3JpcHRvci5ib2R5Lm1lc3NhZ2VUeXBlKSB7XG4gICAgICBsZXQgYm9keUJ1ZmZlciA9IGF3YWl0IGdldFN0cmVhbS5idWZmZXIocmVxLCB7XG4gICAgICAgIG1heEJ1ZmZlcjogMSAqIDEwMjQgKiAxMDI0LFxuICAgICAgfSk7XG4gICAgICBhcmdzLnB1c2goXG4gICAgICAgIHRoaXMuZGVzZXJpYWxpemUoXG4gICAgICAgICAgbG9nZ2luZ1ByZWZpeCxcbiAgICAgICAgICBib2R5QnVmZmVyLFxuICAgICAgICAgIHJlbW90ZUNhbGxIYW5kbGVyLmRlc2NyaXB0b3IuYm9keS5tZXNzYWdlVHlwZSxcbiAgICAgICAgICBgYm9keWAsXG4gICAgICAgICksXG4gICAgICApO1xuICAgIH0gZWxzZSBpZiAocmVtb3RlQ2FsbEhhbmRsZXIuZGVzY3JpcHRvci5ib2R5LnN0cmVhbU1lc3NhZ2VUeXBlKSB7XG4gICAgICAvLyBOT1RFOiBEb2Vzbid0IHdvcmshIENsaWVudCBzaWRlIHJlcXVpcmVzIGh0dHAyIHRvIGVuYWJsZSBjdXN0b20gc3RyZWFtaW5nIHdpdGggdW5zcGVjaWZpZWQgY29udGVudCBsZW5ndGguXG4gICAgICBsZXQgc3RyZWFtUmVhZGVyID0gbmV3IFN0cmVhbU1lc3NhZ2VSZWFkZXIoXG4gICAgICAgIHJlcSxcbiAgICAgICAgcmVtb3RlQ2FsbEhhbmRsZXIuZGVzY3JpcHRvci5ib2R5LnN0cmVhbU1lc3NhZ2VUeXBlLFxuICAgICAgKTtcbiAgICAgIGFyZ3MucHVzaChzdHJlYW1SZWFkZXIpO1xuICAgIH0gZWxzZSBpZiAoXG4gICAgICByZW1vdGVDYWxsSGFuZGxlci5kZXNjcmlwdG9yLmJvZHkucHJpbWl0aXZlVHlwZSA9PT1cbiAgICAgIFByaW1pdHZlVHlwZUZvckJvZHkuQllURVNcbiAgICApIHtcbiAgICAgIGFyZ3MucHVzaChyZXEpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBuZXdJbnRlcm5hbFNlcnZlckVycm9yRXJyb3IoYFVuc3VwcG9ydGVkIHNlcnZlciByZXF1ZXN0IGJvZHkuYCk7XG4gICAgfVxuXG4gICAgaWYgKHJlbW90ZUNhbGxIYW5kbGVyLmRlc2NyaXB0b3IubWV0YWRhdGEpIHtcbiAgICAgIGFyZ3MucHVzaChcbiAgICAgICAgdGhpcy5kZXN0cmluZ2lmeShcbiAgICAgICAgICBsb2dnaW5nUHJlZml4LFxuICAgICAgICAgIHJlcS5xdWVyeVtyZW1vdGVDYWxsSGFuZGxlci5kZXNjcmlwdG9yLm1ldGFkYXRhLmtleV0gYXMgc3RyaW5nLFxuICAgICAgICAgIHJlbW90ZUNhbGxIYW5kbGVyLmRlc2NyaXB0b3IubWV0YWRhdGEudHlwZSxcbiAgICAgICAgICBgbWV0YWRhdGFgLFxuICAgICAgICApLFxuICAgICAgKTtcbiAgICB9XG5cbiAgICBpZiAocmVtb3RlQ2FsbEhhbmRsZXIuZGVzY3JpcHRvci5hdXRoS2V5KSB7XG4gICAgICBsZXQgYXV0aFN0ciA9IHJlcS5oZWFkZXIocmVtb3RlQ2FsbEhhbmRsZXIuZGVzY3JpcHRvci5hdXRoS2V5KTtcbiAgICAgIGlmICghYXV0aFN0cikge1xuICAgICAgICB0aHJvdyBuZXdVbmF1dGhvcml6ZWRFcnJvcihcbiAgICAgICAgICBgTm8gYXV0aCBzdHJpbmcgaXMgZm91bmQgaW4gdGhlIGhlYWRlciAke3JlbW90ZUNhbGxIYW5kbGVyLmRlc2NyaXB0b3IuYXV0aEtleX0uYCxcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICAgIGFyZ3MucHVzaChhdXRoU3RyKTtcbiAgICB9XG4gICAgcmV0dXJuIGF3YWl0IHJlbW90ZUNhbGxIYW5kbGVyLmhhbmRsZSguLi5hcmdzKTtcbiAgfVxuXG4gIHByaXZhdGUgZGVzdHJpbmdpZnkoXG4gICAgbG9nZ2luZ1ByZWZpeDogc3RyaW5nLFxuICAgIHZhbHVlOiBzdHJpbmcsXG4gICAgbWVzc2FnZURlc2NyaXB0b3I6IE1lc3NhZ2VEZXNjcmlwdG9yPGFueT4sXG4gICAgd2hhdDogc3RyaW5nLFxuICApOiBhbnkge1xuICAgIGxldCByZXQ6IGFueTtcbiAgICB0cnkge1xuICAgICAgcmV0ID0gZGVzdHJpbmdpZnlNZXNzYWdlKHZhbHVlLCBtZXNzYWdlRGVzY3JpcHRvcik7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgdGhyb3cgbmV3QmFkUmVxdWVzdEVycm9yKFxuICAgICAgICBgVW5hYmxlIHRvIGRlc3RyaW5naWZ5ICR7d2hhdH0uIFJhdyBzdHJpbmc6ICR7dmFsdWV9LmAsXG4gICAgICAgIGUsXG4gICAgICApO1xuICAgIH1cbiAgICBpZiAoIXJldCkge1xuICAgICAgdGhyb3cgbmV3QmFkUmVxdWVzdEVycm9yKGAke3doYXR9IGlzIGVtcHR5LmApO1xuICAgIH1cbiAgICByZXR1cm4gcmV0O1xuICB9XG5cbiAgcHJpdmF0ZSBkZXNlcmlhbGl6ZShcbiAgICBsb2dnaW5nUHJlZml4OiBzdHJpbmcsXG4gICAgdmFsdWU6IFVpbnQ4QXJyYXksXG4gICAgbWVzc2FnZURlc2NyaXB0b3I6IE1lc3NhZ2VEZXNjcmlwdG9yPGFueT4sXG4gICAgd2hhdDogc3RyaW5nLFxuICApOiBhbnkge1xuICAgIGxldCByZXQ6IGFueTtcbiAgICB0cnkge1xuICAgICAgcmV0ID0gZGVzZXJpYWxpemVNZXNzYWdlKHZhbHVlLCBtZXNzYWdlRGVzY3JpcHRvcik7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgdGhyb3cgbmV3QmFkUmVxdWVzdEVycm9yKFxuICAgICAgICBgVW5hYmxlIHRvIGRlc2VyaWFsaXplICR7d2hhdH0uIFJhdyB2YWx1ZSBhcyBiYXNlNjQ6ICR7QnVmZmVyLmZyb20odmFsdWUpLnRvU3RyaW5nKFwiYmFzZTY0XCIpfS5gLFxuICAgICAgICBlLFxuICAgICAgKTtcbiAgICB9XG4gICAgaWYgKCFyZXQpIHtcbiAgICAgIHRocm93IG5ld0JhZFJlcXVlc3RFcnJvcihgJHtsb2dnaW5nUHJlZml4fSAke3doYXR9IGlzIGVtcHR5LmApO1xuICAgIH1cbiAgICByZXR1cm4gcmV0O1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBzZW5kUmVzcG9uc2UoXG4gICAgcmVzOiBleHByZXNzLlJlc3BvbnNlLFxuICAgIGhhbmRsZXJSZXNwb25zZTogYW55LFxuICAgIG1lc3NhZ2VEZXNjcmlwdG9yOiBNZXNzYWdlRGVzY3JpcHRvcjxhbnk+LFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCBuZXcgUHJvbWlzZTx2b2lkPigocmVzb2x2ZSkgPT5cbiAgICAgIHJlcy5lbmQoc2VyaWFsaXplTWVzc2FnZShoYW5kbGVyUmVzcG9uc2UsIG1lc3NhZ2VEZXNjcmlwdG9yKSwgcmVzb2x2ZSksXG4gICAgKTtcbiAgfVxufVxuIl19