UNPKG

@mhio/koa-handle

Version:

Koa API Promise Handler

215 lines (194 loc) 28 kB
"use strict";var _bluebird = require("bluebird");const fs = require('fs'); const path = require('path'); const debug = require('debug')('mh:KoaHandle'); const base62 = require('base62-random'); const Promise = require('bluebird'); const { Exception } = require('@mhio/exception'); const cons = require('consolidate'); class KoaHandleException extends Exception {} class KoaHandle { static _initialiseClass() { this.powers = ['Bananas', 'Electrons', 'Lemons', 'DeveloperTears']; const random_power_i = Math.floor(Math.random() * this.powers.length); this.power = this.powers[random_power_i]; this.views_path = null; this.views_engine = null; this.views_extension = null; } /** * Setup view engine and path. Only a single */ static views({ engine, path: views_path, extension }) { if (extension) { if (!extension.replace) throw new Error(`Extension doesn't appear to be a string [${extension}]`); this.views_extension = extension.replace(/^\./, ''); } if (engine) { if (!cons[engine]) throw new Error(`No template engine [${engine}] available in consolidate`); this.views_engine = engine; } if (views_path) { if (!pathExists(views_path)) throw new Error(`No path exists [${views_path}] to lookup templates`); this.views_path = views_path; } } /** * @summary Run a promise to return html */ static responseSend(object, method) { return /*#__PURE__*/function () {var _ref = (0, _bluebird.coroutine)(function* (ctx, next) { ctx.body = yield object[method](ctx, next); // eslint-disable-line require-atomic-updates });return function (_x, _x2) {return _ref.apply(this, arguments);};}(); } /** * @summary Run a promise to populate a template * @param {object} object - The object containing the function to call * @param {string} method - The method to call on `object` * @param {object} template - handlebars template */ static responseTemplate(object, method, template, engine_override) { const template_with_ext = this.views_extension ? `${template}.${this.views_extension}` : template; const template_path = this.views_path ? path.join(this.views_path, template_with_ext) : template_with_ext; const template_file_exists = pathExists(template_path); if (!template || !template_file_exists) { if (!this.views_path) throw new Error(`No views path has been set on KoaHandle to find [${template}]`); throw new Error(`Couldn't find template [${template}] in [${this.views_path}]`); } if (!engine_override && !this.views_engine) { throw new Error('No views engine has been set on KoaHandle'); } const engine = engine_override || this.views_engine; return /*#__PURE__*/function () {var _ref2 = (0, _bluebird.coroutine)(function* (ctx, next) { const variables = yield object[method](ctx, next); debug('responseTemplate about to render vars', variables, template_path, engine); //ctx.body = yield ctx.render(template, variables) // maybe merge state indead? ctx.state = variables; // eslint-disable-line require-atomic-updates const res = yield cons[engine](template_path, variables); debug('responseTemplate res', res); ctx.body = res; });return function (_x3, _x4) {return _ref2.apply(this, arguments);};}(); } /** * @summary Handle the response with either a template or straight response */ static response(object, method, options) { if (options && options.template) return this.responseTemplate(object, method, options.template, options.engine); return this.responseSend(object, method); } /** * @summary 404 middleware, after routes * @params {object} options - Options * @params {string} options.error_template - The template to use for "not found" 404's */ static notFound(options = {}) { const error_template = options.error_template || '<notfound/>'; return function (ctx, next) {// eslint-disable-line no-unused-vars debug('notFound', ctx.request._mh_id, ctx.request.ip, ctx.request.method, ctx.request.url); ctx.status = 404; ctx.body = error_template; }; } /** * @summary Middleware (early) to start tracking request times and ids * @returns Promise<undefined> */ static tracking() { const power = this.power; return Promise.coroutine(function* tracking(ctx, next) { const start = Date.now(); let request_id = base62(18); ctx.set('x-request-id', request_id); if (ctx.get('x-transaction-id') === '') { ctx.set('x-transaction-id', request_id); } else { debug('tracking transaction id attached "%s"', ctx.get('x-transaction-id')); } ctx.set('x-powered-by', power); debug('tracking request', request_id, ctx.ip, ctx.method, ctx.url); yield next(); const ms = Date.now() - start; ctx.set('x-response-time', `${ms}ms`); debug('tracking response', ctx.get('x-request-id'), ms, ctx.url); }); } /** * * @param {object} options * @param {object} options.logger - Logger instance following pino API * @return {object} - Pinoish logger object */ static setOptionsLogger(options) { if (options.logger === false) return { error: function () {} }; if (options.logger) return options.logger; return console; } /** * @summary Error middleware * @param {object} options - Options * @param {object} options.logger - Logger instance following pino API * @param {object} options.template - Template to use in production (recieves `message`, `error`, `ctx`) * @param {object} options.template_dev - Dev template to use in !production (recieves `message`, `error`, `ctx`) */ static error(options = {}) { const loggerObj = this.setOptionsLogger(options); const error_template = options.error_template; const error_template_development = options.error_template_development; return Promise.coroutine(function* error(ctx, next) { try { yield next(); } catch (error) { debug('KoaHandle request caught error', error.status, error.message, error); if (!error.status) error.status = 500; if (!error.label) error.label = 'Request Error'; // if (!error.simple) error.simple = 'Request Error' if (!error.id) error.id = base62(12); loggerObj.error('KoaHandle caught error', error); let message = error.simple || 'The request failed'; ctx.status = error.status; if (process.env.NODE_ENV === 'production') { ctx.body = error_template && error_template({ message, error, ctx }) || `<gahh><h3>Error</h3><p>${message}</p><p>ID: ${error.id}</p></gahh>`; } else { ctx.body = error_template_development && error_template_development({ message, error, ctx }) || `<gahh><h3>Error</h3><p>${message}</p><pre>${JSON.stringify(error, undefined, 2)}</pre></gahh>`; } } }); }} KoaHandle._initialiseClass(); /* KoaHandle.tracking = function* tracking( ctx, next ){ const start = Date.now() ctx.set('X-Request-Id', base62(16)) if ( ctx.get('X-Transaction-Id') === undefined ){ ctx.set('X-Transaction-Id', ctx.get('X-Request-Id')) } else { debug('transaction id attached', ctx.get('X-Transaction-Id')) } ctx.set('x-powered-by', 'Lemons') await next() const ms = Date.now() - start ctx.set('x-response-time', `${ms}ms`) } */ function pathExists(test_path) { try { fs.statSync(test_path); return true; } catch (err) { if (err.code !== 'ENOENT') throw err; return false; } } module.exports = { KoaHandle, KoaHandleException, Exception, pathExists }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9Lb2FIYW5kbGUuanMiXSwibmFtZXMiOlsiZnMiLCJyZXF1aXJlIiwicGF0aCIsImRlYnVnIiwiYmFzZTYyIiwiUHJvbWlzZSIsIkV4Y2VwdGlvbiIsImNvbnMiLCJLb2FIYW5kbGVFeGNlcHRpb24iLCJLb2FIYW5kbGUiLCJfaW5pdGlhbGlzZUNsYXNzIiwicG93ZXJzIiwicmFuZG9tX3Bvd2VyX2kiLCJNYXRoIiwiZmxvb3IiLCJyYW5kb20iLCJsZW5ndGgiLCJwb3dlciIsInZpZXdzX3BhdGgiLCJ2aWV3c19lbmdpbmUiLCJ2aWV3c19leHRlbnNpb24iLCJ2aWV3cyIsImVuZ2luZSIsImV4dGVuc2lvbiIsInJlcGxhY2UiLCJFcnJvciIsInBhdGhFeGlzdHMiLCJyZXNwb25zZVNlbmQiLCJvYmplY3QiLCJtZXRob2QiLCJjdHgiLCJuZXh0IiwiYm9keSIsInJlc3BvbnNlVGVtcGxhdGUiLCJ0ZW1wbGF0ZSIsImVuZ2luZV9vdmVycmlkZSIsInRlbXBsYXRlX3dpdGhfZXh0IiwidGVtcGxhdGVfcGF0aCIsImpvaW4iLCJ0ZW1wbGF0ZV9maWxlX2V4aXN0cyIsInZhcmlhYmxlcyIsInN0YXRlIiwicmVzIiwicmVzcG9uc2UiLCJvcHRpb25zIiwibm90Rm91bmQiLCJlcnJvcl90ZW1wbGF0ZSIsInJlcXVlc3QiLCJfbWhfaWQiLCJpcCIsInVybCIsInN0YXR1cyIsInRyYWNraW5nIiwiY29yb3V0aW5lIiwic3RhcnQiLCJEYXRlIiwibm93IiwicmVxdWVzdF9pZCIsInNldCIsImdldCIsIm1zIiwic2V0T3B0aW9uc0xvZ2dlciIsImxvZ2dlciIsImVycm9yIiwiY29uc29sZSIsImxvZ2dlck9iaiIsImVycm9yX3RlbXBsYXRlX2RldmVsb3BtZW50IiwibWVzc2FnZSIsImxhYmVsIiwiaWQiLCJzaW1wbGUiLCJwcm9jZXNzIiwiZW52IiwiTk9ERV9FTlYiLCJKU09OIiwic3RyaW5naWZ5IiwidW5kZWZpbmVkIiwidGVzdF9wYXRoIiwic3RhdFN5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ZTtBQUlmb0IsRUFBQUEsVUFKZSxFQUFqQiIsInNvdXJjZXNDb250ZW50IjpbImNvbnN0IGZzID0gcmVxdWlyZSgnZnMnKVxuY29uc3QgcGF0aCA9IHJlcXVpcmUoJ3BhdGgnKVxuY29uc3QgZGVidWcgPSByZXF1aXJlKCdkZWJ1ZycpKCdtaDpLb2FIYW5kbGUnKVxuY29uc3QgYmFzZTYyID0gcmVxdWlyZSgnYmFzZTYyLXJhbmRvbScpXG5jb25zdCBQcm9taXNlID0gcmVxdWlyZSgnYmx1ZWJpcmQnKVxuY29uc3QgeyBFeGNlcHRpb24gfSA9IHJlcXVpcmUoJ0BtaGlvL2V4Y2VwdGlvbicpXG5cbmNvbnN0IGNvbnMgPSByZXF1aXJlKCdjb25zb2xpZGF0ZScpXG5cbmNsYXNzIEtvYUhhbmRsZUV4Y2VwdGlvbiBleHRlbmRzIEV4Y2VwdGlvbiB7fVxuXG5jbGFzcyBLb2FIYW5kbGUge1xuICBcbiAgc3RhdGljIF9pbml0aWFsaXNlQ2xhc3MoKXtcbiAgICB0aGlzLnBvd2VycyA9IFsgJ0JhbmFuYXMnLCAnRWxlY3Ryb25zJywgJ0xlbW9ucycsICdEZXZlbG9wZXJUZWFycycgXVxuICAgIGNvbnN0IHJhbmRvbV9wb3dlcl9pID0gTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogdGhpcy5wb3dlcnMubGVuZ3RoKVxuICAgIHRoaXMucG93ZXIgPSB0aGlzLnBvd2Vyc1tyYW5kb21fcG93ZXJfaV1cbiAgICB0aGlzLnZpZXdzX3BhdGggPSBudWxsXG4gICAgdGhpcy52aWV3c19lbmdpbmUgPSBudWxsXG4gICAgdGhpcy52aWV3c19leHRlbnNpb24gPSBudWxsXG4gIH1cblxuICAvKipcbiAgICogU2V0dXAgdmlldyBlbmdpbmUgYW5kIHBhdGguIE9ubHkgYSBzaW5nbGUgXG4gICAqL1xuICBzdGF0aWMgdmlld3MoeyBlbmdpbmUsIHBhdGg6IHZpZXdzX3BhdGgsIGV4dGVuc2lvbiB9KXtcbiAgICBpZiAoZXh0ZW5zaW9uKSB7XG4gICAgICBpZiAoIWV4dGVuc2lvbi5yZXBsYWNlKSB0aHJvdyBuZXcgRXJyb3IoYEV4dGVuc2lvbiBkb2Vzbid0IGFwcGVhciB0byBiZSBhIHN0cmluZyBbJHtleHRlbnNpb259XWApXG4gICAgICB0aGlzLnZpZXdzX2V4dGVuc2lvbiA9IGV4dGVuc2lvbi5yZXBsYWNlKC9eXFwuLywgJycpXG4gICAgfVxuICAgIGlmIChlbmdpbmUpeyBcbiAgICAgIGlmICghY29uc1tlbmdpbmVdKSB0aHJvdyBuZXcgRXJyb3IoYE5vIHRlbXBsYXRlIGVuZ2luZSBbJHtlbmdpbmV9XSBhdmFpbGFibGUgaW4gY29uc29saWRhdGVgKVxuICAgICAgdGhpcy52aWV3c19lbmdpbmUgPSBlbmdpbmVcbiAgICB9XG4gICAgaWYgKHZpZXdzX3BhdGgpIHtcbiAgICAgIGlmICghcGF0aEV4aXN0cyh2aWV3c19wYXRoKSkgdGhyb3cgbmV3IEVycm9yKGBObyBwYXRoIGV4aXN0cyBbJHt2aWV3c19wYXRofV0gdG8gbG9va3VwIHRlbXBsYXRlc2ApXG4gICAgICB0aGlzLnZpZXdzX3BhdGggPSB2aWV3c19wYXRoXG4gICAgfVxuICB9XG4gXG4gIC8qKlxuICAgKiBAc3VtbWFyeSAgUnVuIGEgcHJvbWlzZSB0byByZXR1cm4gaHRtbFxuICAgKi9cbiAgc3RhdGljIHJlc3BvbnNlU2VuZCggb2JqZWN0LCBtZXRob2QgKXtcbiAgICByZXR1cm4gYXN5bmMgZnVuY3Rpb24oY3R4LG5leHQpe1xuICAgICAgY3R4LmJvZHkgPSBhd2FpdCBvYmplY3RbbWV0aG9kXShjdHgsIG5leHQpIC8vIGVzbGludC1kaXNhYmxlLWxpbmUgcmVxdWlyZS1hdG9taWMtdXBkYXRlc1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAc3VtbWFyeSAgUnVuIGEgcHJvbWlzZSB0byBwb3B1bGF0ZSBhIHRlbXBsYXRlXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBvYmplY3QgICAgICAgLSBUaGUgb2JqZWN0IGNvbnRhaW5pbmcgdGhlIGZ1bmN0aW9uIHRvIGNhbGxcbiAgICogQHBhcmFtIHtzdHJpbmd9IG1ldGhvZCAgICAgICAtIFRoZSBtZXRob2QgdG8gY2FsbCBvbiBgb2JqZWN0YFxuICAgKiBAcGFyYW0ge29iamVjdH0gdGVtcGxhdGUgICAgIC0gaGFuZGxlYmFycyB0ZW1wbGF0ZSBcbiAgICovXG4gIHN0YXRpYyByZXNwb25zZVRlbXBsYXRlKCBvYmplY3QsIG1ldGhvZCwgdGVtcGxhdGUsIGVuZ2luZV9vdmVycmlkZSApe1xuICAgIGNvbnN0IHRlbXBsYXRlX3dpdGhfZXh0ID0gKHRoaXMudmlld3NfZXh0ZW5zaW9uKVxuICAgICAgPyBgJHt0ZW1wbGF0ZX0uJHt0aGlzLnZpZXdzX2V4dGVuc2lvbn1gXG4gICAgICA6IHRlbXBsYXRlXG4gICAgY29uc3QgdGVtcGxhdGVfcGF0aCA9ICh0aGlzLnZpZXdzX3BhdGgpXG4gICAgICA/IHBhdGguam9pbih0aGlzLnZpZXdzX3BhdGgsIHRlbXBsYXRlX3dpdGhfZXh0KVxuICAgICAgOiB0ZW1wbGF0ZV93aXRoX2V4dFxuICAgIGNvbnN0IHRlbXBsYXRlX2ZpbGVfZXhpc3RzID0gcGF0aEV4aXN0cyh0ZW1wbGF0ZV9wYXRoKVxuICAgIGlmICghdGVtcGxhdGUgfHwgIXRlbXBsYXRlX2ZpbGVfZXhpc3RzKSB7XG4gICAgICBpZiAoIXRoaXMudmlld3NfcGF0aCkgdGhyb3cgbmV3IEVycm9yKGBObyB2aWV3cyBwYXRoIGhhcyBiZWVuIHNldCBvbiBLb2FIYW5kbGUgdG8gZmluZCBbJHt0ZW1wbGF0ZX1dYClcbiAgICAgIHRocm93IG5ldyBFcnJvcihgQ291bGRuJ3QgZmluZCB0ZW1wbGF0ZSBbJHt0ZW1wbGF0ZX1dIGluIFske3RoaXMudmlld3NfcGF0aH1dYClcbiAgICB9XG4gICAgaWYgKCFlbmdpbmVfb3ZlcnJpZGUgJiYgIXRoaXMudmlld3NfZW5naW5lKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ05vIHZpZXdzIGVuZ2luZSBoYXMgYmVlbiBzZXQgb24gS29hSGFuZGxlJylcbiAgICB9XG4gICAgY29uc3QgZW5naW5lID0gZW5naW5lX292ZXJyaWRlIHx8IHRoaXMudmlld3NfZW5naW5lXG4gICAgcmV0dXJuIGFzeW5jIGZ1bmN0aW9uKGN0eCxuZXh0KXtcbiAgICAgIGNvbnN0IHZhcmlhYmxlcyA9IGF3YWl0IG9iamVjdFttZXRob2RdKGN0eCwgbmV4dClcbiAgICAgIGRlYnVnKCdyZXNwb25zZVRlbXBsYXRlIGFib3V0IHRvIHJlbmRlciB2YXJzJywgdmFyaWFibGVzLCB0ZW1wbGF0ZV9wYXRoLCBlbmdpbmUpXG4gICAgICAvL2N0eC5ib2R5ID0geWllbGQgY3R4LnJlbmRlcih0ZW1wbGF0ZSwgdmFyaWFibGVzKSAgXG4gICAgICAvLyBtYXliZSBtZXJnZSBzdGF0ZSBpbmRlYWQ/XG4gICAgICBjdHguc3RhdGUgPSB2YXJpYWJsZXMgIC8vIGVzbGludC1kaXNhYmxlLWxpbmUgcmVxdWlyZS1hdG9taWMtdXBkYXRlc1xuICAgICAgY29uc3QgcmVzID0gYXdhaXQgY29uc1tlbmdpbmVdKHRlbXBsYXRlX3BhdGgsIHZhcmlhYmxlcylcbiAgICAgIGRlYnVnKCdyZXNwb25zZVRlbXBsYXRlIHJlcycscmVzKVxuICAgICAgY3R4LmJvZHkgPSByZXNcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQHN1bW1hcnkgIEhhbmRsZSB0aGUgcmVzcG9uc2Ugd2l0aCBlaXRoZXIgYSB0ZW1wbGF0ZSBvciBzdHJhaWdodCByZXNwb25zZVxuICAgKi9cbiAgc3RhdGljIHJlc3BvbnNlKCBvYmplY3QsIG1ldGhvZCwgb3B0aW9ucyApe1xuICAgIGlmICggb3B0aW9ucyAmJiBvcHRpb25zLnRlbXBsYXRlICkgcmV0dXJuIHRoaXMucmVzcG9uc2VUZW1wbGF0ZShvYmplY3QsIG1ldGhvZCwgb3B0aW9ucy50ZW1wbGF0ZSwgb3B0aW9ucy5lbmdpbmUpXG4gICAgcmV0dXJuIHRoaXMucmVzcG9uc2VTZW5kKG9iamVjdCwgbWV0aG9kKVxuICB9XG5cbiAgLyoqXG4gICAqIEBzdW1tYXJ5ICA0MDQgbWlkZGxld2FyZSwgYWZ0ZXIgcm91dGVzXG4gICAqIEBwYXJhbXMge29iamVjdH0gb3B0aW9ucyAgICAgICAgICAgICAgICAgICAtIE9wdGlvbnNcbiAgICogQHBhcmFtcyB7c3RyaW5nfSBvcHRpb25zLmVycm9yX3RlbXBsYXRlICAgIC0gVGhlIHRlbXBsYXRlIHRvIHVzZSBmb3IgXCJub3QgZm91bmRcIiA0MDQnc1xuICAgKi9cbiAgc3RhdGljIG5vdEZvdW5kKG9wdGlvbnMgPSB7fSl7XG4gICAgY29uc3QgZXJyb3JfdGVtcGxhdGUgPSBvcHRpb25zLmVycm9yX3RlbXBsYXRlIHx8ICc8bm90Zm91bmQvPidcbiAgICByZXR1cm4gZnVuY3Rpb24oIGN0eCwgbmV4dCApeyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIG5vLXVudXNlZC12YXJzXG4gICAgICBkZWJ1Zygnbm90Rm91bmQnLCBjdHgucmVxdWVzdC5fbWhfaWQsIGN0eC5yZXF1ZXN0LmlwLCBjdHgucmVxdWVzdC5tZXRob2QsIGN0eC5yZXF1ZXN0LnVybClcbiAgICAgIGN0eC5zdGF0dXMgPSA0MDRcbiAgICAgIGN0eC5ib2R5ID0gZXJyb3JfdGVtcGxhdGVcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQHN1bW1hcnkgIE1pZGRsZXdhcmUgKGVhcmx5KSB0byBzdGFydCB0cmFja2luZyByZXF1ZXN0IHRpbWVzIGFuZCBpZHNcbiAgICogQHJldHVybnMgUHJvbWlzZTx1bmRlZmluZWQ+XG4gICAqL1xuICBzdGF0aWMgdHJhY2tpbmcoKXtcbiAgICBjb25zdCBwb3dlciA9IHRoaXMucG93ZXJcbiAgICByZXR1cm4gUHJvbWlzZS5jb3JvdXRpbmUoZnVuY3Rpb24qIHRyYWNraW5nKCBjdHgsIG5leHQgKXtcbiAgICAgIGNvbnN0IHN0YXJ0ID0gRGF0ZS5ub3coKVxuICAgICAgXG4gICAgICBsZXQgcmVxdWVzdF9pZCA9IGJhc2U2MigxOClcbiAgICAgIGN0eC5zZXQoJ3gtcmVxdWVzdC1pZCcsIHJlcXVlc3RfaWQpXG4gICAgICBcbiAgICAgIGlmICggY3R4LmdldCgneC10cmFuc2FjdGlvbi1pZCcpID09PSAnJyApe1xuICAgICAgICBjdHguc2V0KCd4LXRyYW5zYWN0aW9uLWlkJywgcmVxdWVzdF9pZClcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGRlYnVnKCd0cmFja2luZyB0cmFuc2FjdGlvbiBpZCBhdHRhY2hlZCBcIiVzXCInLCBjdHguZ2V0KCd4LXRyYW5zYWN0aW9uLWlkJykpXG4gICAgICB9XG4gICAgICBjdHguc2V0KCd4LXBvd2VyZWQtYnknLCBwb3dlcilcbiAgICAgIGRlYnVnKCd0cmFja2luZyByZXF1ZXN0JywgcmVxdWVzdF9pZCwgY3R4LmlwLCBjdHgubWV0aG9kLCBjdHgudXJsKVxuICAgICAgXG4gICAgICB5aWVsZCBuZXh0KClcbiAgICAgIFxuICAgICAgY29uc3QgbXMgPSBEYXRlLm5vdygpIC0gc3RhcnRcbiAgICAgIGN0eC5zZXQoJ3gtcmVzcG9uc2UtdGltZScsIGAke21zfW1zYClcbiAgICAgIGRlYnVnKCd0cmFja2luZyByZXNwb25zZScsIGN0eC5nZXQoJ3gtcmVxdWVzdC1pZCcpLCBtcywgY3R4LnVybClcbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIFxuICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyBcbiAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMubG9nZ2VyICAgICAgICAgLSBMb2dnZXIgaW5zdGFuY2UgZm9sbG93aW5nIHBpbm8gQVBJXG4gICAqIEByZXR1cm4ge29iamVjdH0gICAgICAgICAgICAgICAgICAgICAgIC0gUGlub2lzaCBsb2dnZXIgb2JqZWN0XG4gICAqL1xuICBzdGF0aWMgc2V0T3B0aW9uc0xvZ2dlcihvcHRpb25zKXtcbiAgICBpZiAoIG9wdGlvbnMubG9nZ2VyID09PSBmYWxzZSApIHJldHVybiB7IGVycm9yOiBmdW5jdGlvbigpe30gfVxuICAgIGlmICggb3B0aW9ucy5sb2dnZXIgKSByZXR1cm4gb3B0aW9ucy5sb2dnZXJcbiAgICByZXR1cm4gY29uc29sZVxuICB9XG5cbiAgLyoqXG4gICAqIEBzdW1tYXJ5ICBFcnJvciBtaWRkbGV3YXJlXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zICAgICAgICAgICAgICAgIC0gT3B0aW9uc1xuICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucy5sb2dnZXIgICAgICAgICAtIExvZ2dlciBpbnN0YW5jZSBmb2xsb3dpbmcgcGlubyBBUElcbiAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMudGVtcGxhdGUgICAgICAgLSBUZW1wbGF0ZSB0byB1c2UgaW4gcHJvZHVjdGlvbiAocmVjaWV2ZXMgYG1lc3NhZ2VgLCBgZXJyb3JgLCBgY3R4YClcbiAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMudGVtcGxhdGVfZGV2ICAgLSBEZXYgdGVtcGxhdGUgdG8gdXNlIGluICFwcm9kdWN0aW9uIChyZWNpZXZlcyBgbWVzc2FnZWAsIGBlcnJvcmAsIGBjdHhgKVxuICAgKi9cbiAgc3RhdGljIGVycm9yKG9wdGlvbnMgPSB7fSl7XG4gICAgY29uc3QgbG9nZ2VyT2JqID0gdGhpcy5zZXRPcHRpb25zTG9nZ2VyKG9wdGlvbnMpXG4gICAgY29uc3QgZXJyb3JfdGVtcGxhdGUgPSBvcHRpb25zLmVycm9yX3RlbXBsYXRlXG4gICAgY29uc3QgZXJyb3JfdGVtcGxhdGVfZGV2ZWxvcG1lbnQgPSBvcHRpb25zLmVycm9yX3RlbXBsYXRlX2RldmVsb3BtZW50IFxuICAgIHJldHVybiBQcm9taXNlLmNvcm91dGluZShmdW5jdGlvbiogZXJyb3IoY3R4LCBuZXh0KXtcbiAgICAgIHRyeSB7XG4gICAgICAgIHlpZWxkIG5leHQoKVxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgZGVidWcoJ0tvYUhhbmRsZSByZXF1ZXN0IGNhdWdodCBlcnJvcicsIGVycm9yLnN0YXR1cywgZXJyb3IubWVzc2FnZSwgZXJyb3IpXG4gICAgICAgIGlmICghZXJyb3Iuc3RhdHVzKSBlcnJvci5zdGF0dXMgPSA1MDBcbiAgICAgICAgaWYgKCFlcnJvci5sYWJlbCkgIGVycm9yLmxhYmVsID0gJ1JlcXVlc3QgRXJyb3InXG4gICAgICAgIC8vIGlmICghZXJyb3Iuc2ltcGxlKSBlcnJvci5zaW1wbGUgPSAnUmVxdWVzdCBFcnJvcidcbiAgICAgICAgaWYgKCFlcnJvci5pZCkgICAgIGVycm9yLmlkID0gYmFzZTYyKDEyKVxuICAgICAgICBsb2dnZXJPYmouZXJyb3IoJ0tvYUhhbmRsZSBjYXVnaHQgZXJyb3InLCBlcnJvcilcbiAgICAgICAgbGV0IG1lc3NhZ2UgPSBlcnJvci5zaW1wbGUgfHwgJ1RoZSByZXF1ZXN0IGZhaWxlZCdcbiAgICAgICAgY3R4LnN0YXR1cyA9IGVycm9yLnN0YXR1c1xuICAgICAgICBpZiAocHJvY2Vzcy5lbnYuTk9ERV9FTlYgPT09ICdwcm9kdWN0aW9uJykge1xuICAgICAgICAgIGN0eC5ib2R5ID0gZXJyb3JfdGVtcGxhdGUgJiYgZXJyb3JfdGVtcGxhdGUoeyBtZXNzYWdlLCBlcnJvciwgY3R4IH0pXG4gICAgICAgICAgICB8fCBgPGdhaGg+PGgzPkVycm9yPC9oMz48cD4ke21lc3NhZ2V9PC9wPjxwPklEOiAke2Vycm9yLmlkfTwvcD48L2dhaGg+YFxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGN0eC5ib2R5ID0gZXJyb3JfdGVtcGxhdGVfZGV2ZWxvcG1lbnQgJiYgZXJyb3JfdGVtcGxhdGVfZGV2ZWxvcG1lbnQoeyBtZXNzYWdlLCBlcnJvciwgY3R4IH0pXG4gICAgICAgICAgICB8fCBgPGdhaGg+PGgzPkVycm9yPC9oMz48cD4ke21lc3NhZ2V9PC9wPjxwcmU+JHtKU09OLnN0cmluZ2lmeShlcnJvciwgdW5kZWZpbmVkLCAyKX08L3ByZT48L2dhaGg+YFxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSlcbiAgfVxuXG5cbn1cbktvYUhhbmRsZS5faW5pdGlhbGlzZUNsYXNzKClcbi8qXG5Lb2FIYW5kbGUudHJhY2tpbmcgPSBmdW5jdGlvbiogdHJhY2tpbmcoIGN0eCwgbmV4dCApe1xuICBjb25zdCBzdGFydCA9IERhdGUubm93KClcbiAgY3R4LnNldCgnWC1SZXF1ZXN0LUlkJywgYmFzZTYyKDE2KSlcbiAgaWYgKCBjdHguZ2V0KCdYLVRyYW5zYWN0aW9uLUlkJykgPT09IHVuZGVmaW5lZCApe1xuICAgIGN0eC5zZXQoJ1gtVHJhbnNhY3Rpb24tSWQnLCBjdHguZ2V0KCdYLVJlcXVlc3QtSWQnKSlcbiAgfSBlbHNlIHtcbiAgICBkZWJ1ZygndHJhbnNhY3Rpb24gaWQgYXR0YWNoZWQnLCBjdHguZ2V0KCdYLVRyYW5zYWN0aW9uLUlkJykpXG4gIH1cbiAgY3R4LnNldCgneC1wb3dlcmVkLWJ5JywgJ0xlbW9ucycpXG4gIGF3YWl0IG5leHQoKVxuICBjb25zdCBtcyA9IERhdGUubm93KCkgLSBzdGFydFxuICBjdHguc2V0KCd4LXJlc3BvbnNlLXRpbWUnLCBgJHttc31tc2ApXG59XG4qL1xuXG5mdW5jdGlvbiBwYXRoRXhpc3RzKHRlc3RfcGF0aCl7XG4gIHRyeSB7XG4gICAgZnMuc3RhdFN5bmModGVzdF9wYXRoKVxuICAgIHJldHVybiB0cnVlXG4gIH0gXG4gIGNhdGNoIChlcnIpIHtcbiAgICBpZiAoZXJyLmNvZGUgIT09ICdFTk9FTlQnKSB0aHJvdyBlcnJcbiAgICByZXR1cm4gZmFsc2VcbiAgfVxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgS29hSGFuZGxlLFxuICBLb2FIYW5kbGVFeGNlcHRpb24sXG4gIEV4Y2VwdGlvbixcbiAgcGF0aEV4aXN0cyxcbn1cbiJdfQ==