UNPKG

22.5 kBJavaScriptView Raw
1'use strict';
2
3// Small Express router that runs request handlers written as ES6 generators
4// using Node co. On top of that the router does a few useful things, including
5// some logging and error handling using a Node domain.
6
7var _ = require('underscore');
8var express = require('express');
9var domain = require('domain');
10var url = require('url');
11var yieldable = require('abacus-yieldable');
12var transform = require('abacus-transform');
13
14var toArray = _.toArray;
15var map = _.map;
16var extend = _.extend;
17var clone = _.clone;
18
19// Setup debug log
20var debug = require('abacus-debug')('abacus-router');
21
22// Convert a middleware function which can be a regular Express middleware or a
23// generator to a regular Express middleware function.
24var callbackify = function callbackify(m, trusted) {
25
26 // If the callback is a regular function just use it as-is, otherwise it's
27 // a generator and we need to wrap it using the co module
28 var mfunc = yieldable.functioncb(m);
29
30 // Return a middleware function. Middleware functions can be of the form
31 // function(req, res, next) or function(err, req, res, next) so we need to
32 // support both forms
33 return function () {
34 var next = arguments[arguments.length - 1];
35 var res = arguments[1];
36 var params = toArray(arguments).slice(0, arguments.length - 1);
37
38 // Pass errors down the middleware stack, if the middleware is
39 // un-trusted then we mark the error with bailout flag to trigger our
40 // server bailout logic
41 var error = function error(err, type) {
42 debug('Route error - %s - %o', type, err);
43 if (!trusted && !err.status && !err.statusCode) err.bailout = true;
44 next(err);
45 };
46
47 // Call the middleware function
48 try {
49 mfunc.apply(undefined, params.concat([function (err, value) {
50 if (err) error(err, 'generator error');else if (value) {
51 // Store the returned value in the response, it'll be sent
52 // by one of our Express middleware later down the
53 // middleware stack
54 res.value = value;
55 next();
56 }
57 }]));
58 } catch (exc) {
59 error(exc, 'exception');
60 }
61 };
62};
63
64// Return an implementation of the router.use(path, middleware) function that supports
65// middleware implemented as generators in addition to regular callbacks
66var use = function use(original, trusted) {
67 return function (path, m) {
68 return typeof path === 'function' ? original.call(this, callbackify(path, trusted)) : original.call(this, path, callbackify(m, trusted));
69 };
70};
71
72// Return an implementation of the router.route() function that supports middleware implemented
73// as generators in addition to regular callbacks
74var route = function route(original, trusted) {
75 return function () {
76 // Get the route
77 var r = original.apply(this, arguments);
78
79 // Monkey patch its HTTP methods
80 map(['get', 'head', 'post', 'put', 'patch', 'delete', 'options', 'all', 'use'], function (method) {
81 var f = r[method];
82 r[method] = function () {
83 var middleware = map(toArray(arguments), function (m) {
84 return callbackify(m, trusted);
85 });
86 return f.apply(this, middleware);
87 };
88 });
89 return r;
90 };
91};
92
93// Return an Express middleware that uses a Node domain to run the middleware stack and
94// handle any errors not caught in async callbacks
95var catchall = function catchall(trusted) {
96 return function (req, res, next) {
97 var d = domain.create();
98 d.on('error', function (err) {
99 debug('Route domain error %o', err);
100
101 // Pass the error down the middleware stack, if the router runs
102 // un-trusted middleware then mark it with a bailout flag to
103 // trigger our server bailout logic
104 // Warning: mutating variable err
105 if (!trusted && !err.status && !err.statusCode) err.bailout = true;
106 next(err);
107 });
108
109 // Because req and res were created before this domain existed,
110 // we need to explicitly add them. See the explanation of implicit
111 // vs explicit binding in the Node domain docs.
112 d.add(req);
113 d.add(res);
114
115 // Run the middleware stack in our new domain
116 d.run(next);
117 };
118};
119
120// Return an Express router middleware that works with generators
121var router = function router(trusted) {
122 var r = express.Router();
123
124 // Catch all errors down the middleware stack using a Node domain
125 r.use(catchall(trusted));
126
127 // Monkey patch the router function with our implementation of the use
128 // and route functions
129 r.use = use(r.use, trusted);
130 r.route = route(r.route, trusted);
131
132 return r;
133};
134
135// Return an express middleware that runs a batch of requests through the
136// given router
137var batch = function batch(routes) {
138 return function (req, res, next) {
139 if (req.method !== 'POST' || req.path !== '/batch') return next();
140 debug('Handling batch request %o', req.body);
141
142 // Run the batch of requests found in the body through the router
143 transform.map(req.body, function (r, i, reqs, rcb) {
144 // Setup an Express request representing the batched request
145 var rreq = extend(clone(req), { method: r.method, url: url.resolve(req.url, r.uri), body: r.body });
146
147 // Setup an Express response object that will capture the response
148 var rres = extend(clone(res), {
149 status: function status(s) {
150 rres.statusCode = s;
151 return rres;
152 },
153 send: function send(b) {
154 rres.body = b;
155 return rres.end();
156 },
157 json: function json(b) {
158 rres.body = b;
159 return rres.end();
160 },
161 end: function end() {
162 rcb(undefined, { statusCode: rres.statusCode, body: rres.body });
163 return rres;
164 }
165 });
166
167 // Call the given router
168 routes(rreq, rres, function () {
169 if (rres.value) return rcb(undefined, rres.value);
170 next();
171 });
172 }, function (err, bres) {
173 if (err) return res.status(500).end();
174
175 // Return the batch of results
176 res.send(bres);
177 });
178 };
179};
180
181// Export our public functions
182module.exports = router;
183module.exports.batch = batch;
184//# sourceMappingURL=data:application/json;base64,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S0FBSyxFQUNULE9BQU8sR0FBRyxDQUFDLFNBQVMsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7QUFDdEMsb0JBQUksRUFBRSxDQUFDO2FBQ1YsQ0FBQyxDQUFDO1NBRU4sRUFBRSxVQUFDLEdBQUcsRUFBRSxJQUFJLEVBQUs7QUFDZCxnQkFBRyxHQUFHLEVBQUUsT0FBTyxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsRUFBRSxDQUFDOzs7QUFHckMsZUFBRyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUNsQixDQUFDLENBQUM7S0FDTixDQUFDO0NBQ0wsQ0FBQzs7O0FBR0YsTUFBTSxDQUFDLE9BQU8sR0FBRyxNQUFNLENBQUM7QUFDeEIsTUFBTSxDQUFDLE9BQU8sQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDIiwiZmlsZSI6ImluZGV4LmpzIiwic291cmNlc0NvbnRlbnQiOlsiJ3VzZSBzdHJpY3QnO1xuXG4vLyBTbWFsbCBFeHByZXNzIHJvdXRlciB0aGF0IHJ1bnMgcmVxdWVzdCBoYW5kbGVycyB3cml0dGVuIGFzIEVTNiBnZW5lcmF0b3JzXG4vLyB1c2luZyBOb2RlIGNvLiBPbiB0b3Agb2YgdGhhdCB0aGUgcm91dGVyIGRvZXMgYSBmZXcgdXNlZnVsIHRoaW5ncywgaW5jbHVkaW5nXG4vLyBzb21lIGxvZ2dpbmcgYW5kIGVycm9yIGhhbmRsaW5nIHVzaW5nIGEgTm9kZSBkb21haW4uXG5cbmNvbnN0IF8gPSByZXF1aXJlKCd1bmRlcnNjb3JlJyk7XG5jb25zdCBleHByZXNzID0gcmVxdWlyZSgnZXhwcmVzcycpO1xuY29uc3QgZG9tYWluID0gcmVxdWlyZSgnZG9tYWluJyk7XG5jb25zdCB1cmwgPSByZXF1aXJlKCd1cmwnKTtcbmNvbnN0IHlpZWxkYWJsZSA9IHJlcXVpcmUoJ2FiYWN1cy15aWVsZGFibGUnKTtcbmNvbnN0IHRyYW5zZm9ybSA9IHJlcXVpcmUoJ2FiYWN1cy10cmFuc2Zvcm0nKTtcblxuY29uc3QgdG9BcnJheSA9IF8udG9BcnJheTtcbmNvbnN0IG1hcCA9IF8ubWFwO1xuY29uc3QgZXh0ZW5kID0gXy5leHRlbmQ7XG5jb25zdCBjbG9uZSA9IF8uY2xvbmU7XG5cbi8vIFNldHVwIGRlYnVnIGxvZ1xuY29uc3QgZGVidWcgPSByZXF1aXJlKCdhYmFjdXMtZGVidWcnKSgnYWJhY3VzLXJvdXRlcicpO1xuXG4vLyBDb252ZXJ0IGEgbWlkZGxld2FyZSBmdW5jdGlvbiB3aGljaCBjYW4gYmUgYSByZWd1bGFyIEV4cHJlc3MgbWlkZGxld2FyZSBvciBhXG4vLyBnZW5lcmF0b3IgdG8gYSByZWd1bGFyIEV4cHJlc3MgbWlkZGxld2FyZSBmdW5jdGlvbi5cbmNvbnN0IGNhbGxiYWNraWZ5ID0gKG0sIHRydXN0ZWQpID0+IHtcblxuICAgIC8vIElmIHRoZSBjYWxsYmFjayBpcyBhIHJlZ3VsYXIgZnVuY3Rpb24ganVzdCB1c2UgaXQgYXMtaXMsIG90aGVyd2lzZSBpdCdzXG4gICAgLy8gYSBnZW5lcmF0b3IgYW5kIHdlIG5lZWQgdG8gd3JhcCBpdCB1c2luZyB0aGUgY28gbW9kdWxlXG4gICAgY29uc3QgbWZ1bmMgPSB5aWVsZGFibGUuZnVuY3Rpb25jYihtKTtcblxuICAgIC8vIFJldHVybiBhIG1pZGRsZXdhcmUgZnVuY3Rpb24uIE1pZGRsZXdhcmUgZnVuY3Rpb25zIGNhbiBiZSBvZiB0aGUgZm9ybVxuICAgIC8vIGZ1bmN0aW9uKHJlcSwgcmVzLCBuZXh0KSBvciBmdW5jdGlvbihlcnIsIHJlcSwgcmVzLCBuZXh0KSBzbyB3ZSBuZWVkIHRvXG4gICAgLy8gc3VwcG9ydCBib3RoIGZvcm1zXG4gICAgcmV0dXJuIGZ1bmN0aW9uKCkge1xuICAgICAgICBjb25zdCBuZXh0ID0gYXJndW1lbnRzW2FyZ3VtZW50cy5sZW5ndGggLSAxXTtcbiAgICAgICAgY29uc3QgcmVzID0gYXJndW1lbnRzWzFdO1xuICAgICAgICBjb25zdCBwYXJhbXMgPSB0b0FycmF5KGFyZ3VtZW50cykuc2xpY2UoMCwgYXJndW1lbnRzLmxlbmd0aCAtIDEpO1xuXG4gICAgICAgIC8vIFBhc3MgZXJyb3JzIGRvd24gdGhlIG1pZGRsZXdhcmUgc3RhY2ssIGlmIHRoZSBtaWRkbGV3YXJlIGlzXG4gICAgICAgIC8vIHVuLXRydXN0ZWQgdGhlbiB3ZSBtYXJrIHRoZSBlcnJvciB3aXRoIGJhaWxvdXQgZmxhZyB0byB0cmlnZ2VyIG91clxuICAgICAgICAvLyBzZXJ2ZXIgYmFpbG91dCBsb2dpY1xuICAgICAgICBjb25zdCBlcnJvciA9IChlcnIsIHR5cGUpID0+IHtcbiAgICAgICAgICAgIGRlYnVnKCdSb3V0ZSBlcnJvciAtICVzIC0gJW8nLCB0eXBlLCBlcnIpO1xuICAgICAgICAgICAgaWYoIXRydXN0ZWQgJiYgIWVyci5zdGF0dXMgJiYgIWVyci5zdGF0dXNDb2RlKVxuICAgICAgICAgICAgICAgIGVyci5iYWlsb3V0ID0gdHJ1ZTtcbiAgICAgICAgICAgIG5leHQoZXJyKTtcbiAgICAgICAgfTtcblxuICAgICAgICAvLyBDYWxsIHRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9uXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBtZnVuYy5hcHBseSh1bmRlZmluZWQsIHBhcmFtcy5jb25jYXQoWyhlcnIsIHZhbHVlKSA9PiB7XG4gICAgICAgICAgICAgICAgaWYoZXJyKSBlcnJvcihlcnIsICdnZW5lcmF0b3IgZXJyb3InKTtcblxuICAgICAgICAgICAgICAgIGVsc2UgaWYodmFsdWUpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gU3RvcmUgdGhlIHJldHVybmVkIHZhbHVlIGluIHRoZSByZXNwb25zZSwgaXQnbGwgYmUgc2VudFxuICAgICAgICAgICAgICAgICAgICAvLyBieSBvbmUgb2Ygb3VyIEV4cHJlc3MgbWlkZGxld2FyZSBsYXRlciBkb3duIHRoZVxuICAgICAgICAgICAgICAgICAgICAvLyBtaWRkbGV3YXJlIHN0YWNrXG4gICAgICAgICAgICAgICAgICAgIHJlcy52YWx1ZSA9IHZhbHVlO1xuICAgICAgICAgICAgICAgICAgICBuZXh0KCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfV0pKTtcbiAgICAgICAgfVxuICAgICAgICBjYXRjaChleGMpIHtcbiAgICAgICAgICAgIGVycm9yKGV4YywgJ2V4Y2VwdGlvbicpO1xuICAgICAgICB9XG4gICAgfTtcbn07XG5cbi8vIFJldHVybiBhbiBpbXBsZW1lbnRhdGlvbiBvZiB0aGUgcm91dGVyLnVzZShwYXRoLCBtaWRkbGV3YXJlKSBmdW5jdGlvbiB0aGF0IHN1cHBvcnRzXG4vLyBtaWRkbGV3YXJlIGltcGxlbWVudGVkIGFzIGdlbmVyYXRvcnMgaW4gYWRkaXRpb24gdG8gcmVndWxhciBjYWxsYmFja3NcbmNvbnN0IHVzZSA9IChvcmlnaW5hbCwgdHJ1c3RlZCkgPT4ge1xuICAgIHJldHVybiBmdW5jdGlvbihwYXRoLCBtKSB7XG4gICAgICAgIHJldHVybiB0eXBlb2YgcGF0aCA9PT0gJ2Z1bmN0aW9uJyA/IG9yaWdpbmFsLmNhbGwodGhpcywgY2FsbGJhY2tpZnkocGF0aCwgdHJ1c3RlZCkpIDogb3JpZ2luYWwuY2FsbCh0aGlzLCBwYXRoLCBjYWxsYmFja2lmeShtLCB0cnVzdGVkKSk7XG4gICAgfTtcbn07XG5cbi8vIFJldHVybiBhbiBpbXBsZW1lbnRhdGlvbiBvZiB0aGUgcm91dGVyLnJvdXRlKCkgZnVuY3Rpb24gdGhhdCBzdXBwb3J0cyBtaWRkbGV3YXJlIGltcGxlbWVudGVkXG4vLyBhcyBnZW5lcmF0b3JzIGluIGFkZGl0aW9uIHRvIHJlZ3VsYXIgY2FsbGJhY2tzXG5jb25zdCByb3V0ZSA9IChvcmlnaW5hbCwgdHJ1c3RlZCkgPT4ge1xuICAgIHJldHVybiBmdW5jdGlvbigpIHtcbiAgICAgICAgLy8gR2V0IHRoZSByb3V0ZVxuICAgICAgICBjb25zdCByID0gb3JpZ2luYWwuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcblxuICAgICAgICAvLyBNb25rZXkgcGF0Y2ggaXRzIEhUVFAgbWV0aG9kc1xuICAgICAgICBtYXAoWydnZXQnLCAnaGVhZCcsICdwb3N0JywgJ3B1dCcsICdwYXRjaCcsICdkZWxldGUnLCAnb3B0aW9ucycsICdhbGwnLCAndXNlJ10sIChtZXRob2QpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGYgPSByW21ldGhvZF07XG4gICAgICAgICAgICByW21ldGhvZF0gPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBtaWRkbGV3YXJlID0gbWFwKHRvQXJyYXkoYXJndW1lbnRzKSwgZnVuY3Rpb24obSkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gY2FsbGJhY2tpZnkobSwgdHJ1c3RlZCk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGYuYXBwbHkodGhpcywgbWlkZGxld2FyZSk7XG4gICAgICAgICAgICB9O1xuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIHI7XG4gICAgfTtcbn07XG5cbi8vIFJldHVybiBhbiBFeHByZXNzIG1pZGRsZXdhcmUgdGhhdCB1c2VzIGEgTm9kZSBkb21haW4gdG8gcnVuIHRoZSBtaWRkbGV3YXJlIHN0YWNrIGFuZFxuLy8gaGFuZGxlIGFueSBlcnJvcnMgbm90IGNhdWdodCBpbiBhc3luYyBjYWxsYmFja3NcbmNvbnN0IGNhdGNoYWxsID0gKHRydXN0ZWQpID0+IHtcbiAgICByZXR1cm4gKHJlcSwgcmVzLCBuZXh0KSA9PiB7XG4gICAgICAgIGNvbnN0IGQgPSBkb21haW4uY3JlYXRlKCk7XG4gICAgICAgIGQub24oJ2Vycm9yJywgKGVycikgPT4ge1xuICAgICAgICAgICAgZGVidWcoJ1JvdXRlIGRvbWFpbiBlcnJvciAlbycsIGVycik7XG5cbiAgICAgICAgICAgIC8vIFBhc3MgdGhlIGVycm9yIGRvd24gdGhlIG1pZGRsZXdhcmUgc3RhY2ssIGlmIHRoZSByb3V0ZXIgcnVuc1xuICAgICAgICAgICAgLy8gdW4tdHJ1c3RlZCBtaWRkbGV3YXJlIHRoZW4gbWFyayBpdCB3aXRoIGEgYmFpbG91dCBmbGFnIHRvXG4gICAgICAgICAgICAvLyB0cmlnZ2VyIG91ciBzZXJ2ZXIgYmFpbG91dCBsb2dpY1xuICAgICAgICAgICAgLy8gV2FybmluZzogbXV0YXRpbmcgdmFyaWFibGUgZXJyXG4gICAgICAgICAgICBpZighdHJ1c3RlZCAmJiAhZXJyLnN0YXR1cyAmJiAhZXJyLnN0YXR1c0NvZGUpXG4gICAgICAgICAgICAgICAgZXJyLmJhaWxvdXQgPSB0cnVlO1xuICAgICAgICAgICAgbmV4dChlcnIpO1xuICAgICAgICB9KTtcblxuICAgICAgICAvLyBCZWNhdXNlIHJlcSBhbmQgcmVzIHdlcmUgY3JlYXRlZCBiZWZvcmUgdGhpcyBkb21haW4gZXhpc3RlZCxcbiAgICAgICAgLy8gd2UgbmVlZCB0byBleHBsaWNpdGx5IGFkZCB0aGVtLiAgU2VlIHRoZSBleHBsYW5hdGlvbiBvZiBpbXBsaWNpdFxuICAgICAgICAvLyB2cyBleHBsaWNpdCBiaW5kaW5nIGluIHRoZSBOb2RlIGRvbWFpbiBkb2NzLlxuICAgICAgICBkLmFkZChyZXEpO1xuICAgICAgICBkLmFkZChyZXMpO1xuXG4gICAgICAgIC8vIFJ1biB0aGUgbWlkZGxld2FyZSBzdGFjayBpbiBvdXIgbmV3IGRvbWFpblxuICAgICAgICBkLnJ1bihuZXh0KTtcbiAgICB9O1xufTtcblxuLy8gUmV0dXJuIGFuIEV4cHJlc3Mgcm91dGVyIG1pZGRsZXdhcmUgdGhhdCB3b3JrcyB3aXRoIGdlbmVyYXRvcnNcbmNvbnN0IHJvdXRlciA9ICh0cnVzdGVkKSA9PiB7XG4gICAgY29uc3QgciA9IGV4cHJlc3MuUm91dGVyKCk7XG5cbiAgICAvLyBDYXRjaCBhbGwgZXJyb3JzIGRvd24gdGhlIG1pZGRsZXdhcmUgc3RhY2sgdXNpbmcgYSBOb2RlIGRvbWFpblxuICAgIHIudXNlKGNhdGNoYWxsKHRydXN0ZWQpKTtcblxuICAgIC8vIE1vbmtleSBwYXRjaCB0aGUgcm91dGVyIGZ1bmN0aW9uIHdpdGggb3VyIGltcGxlbWVudGF0aW9uIG9mIHRoZSB1c2VcbiAgICAvLyBhbmQgcm91dGUgZnVuY3Rpb25zXG4gICAgci51c2UgPSB1c2Uoci51c2UsIHRydXN0ZWQpO1xuICAgIHIucm91dGUgPSByb3V0ZShyLnJvdXRlLCB0cnVzdGVkKTtcblxuICAgIHJldHVybiByO1xufTtcblxuLy8gUmV0dXJuIGFuIGV4cHJlc3MgbWlkZGxld2FyZSB0aGF0IHJ1bnMgYSBiYXRjaCBvZiByZXF1ZXN0cyB0aHJvdWdoIHRoZVxuLy8gZ2l2ZW4gcm91dGVyXG5jb25zdCBiYXRjaCA9IChyb3V0ZXMpID0+IHtcbiAgICByZXR1cm4gKHJlcSwgcmVzLCBuZXh0KSA9PiB7XG4gICAgICAgIGlmKHJlcS5tZXRob2QgIT09ICdQT1NUJyB8fCByZXEucGF0aCAhPT0gJy9iYXRjaCcpXG4gICAgICAgICAgICByZXR1cm4gbmV4dCgpO1xuICAgICAgICBkZWJ1ZygnSGFuZGxpbmcgYmF0Y2ggcmVxdWVzdCAlbycsIHJlcS5ib2R5KTtcblxuICAgICAgICAvLyBSdW4gdGhlIGJhdGNoIG9mIHJlcXVlc3RzIGZvdW5kIGluIHRoZSBib2R5IHRocm91Z2ggdGhlIHJvdXRlclxuICAgICAgICB0cmFuc2Zvcm0ubWFwKHJlcS5ib2R5LCAociwgaSwgcmVxcywgcmNiKSA9PiB7XG4gICAgICAgICAgICAvLyBTZXR1cCBhbiBFeHByZXNzIHJlcXVlc3QgcmVwcmVzZW50aW5nIHRoZSBiYXRjaGVkIHJlcXVlc3RcbiAgICAgICAgICAgIGNvbnN0IHJyZXEgPSBleHRlbmQoY2xvbmUocmVxKSwgeyBtZXRob2Q6IHIubWV0aG9kLCB1cmw6IHVybC5yZXNvbHZlKHJlcS51cmwsIHIudXJpKSwgYm9keTogci5ib2R5IH0pO1xuXG4gICAgICAgICAgICAvLyBTZXR1cCBhbiBFeHByZXNzIHJlc3BvbnNlIG9iamVjdCB0aGF0IHdpbGwgY2FwdHVyZSB0aGUgcmVzcG9uc2VcbiAgICAgICAgICAgIGNvbnN0IHJyZXMgPSBleHRlbmQoY2xvbmUocmVzKSwge1xuICAgICAgICAgICAgICAgIHN0YXR1czogKHMpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgcnJlcy5zdGF0dXNDb2RlID0gcztcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHJyZXM7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBzZW5kOiAoYikgPT4ge1xuICAgICAgICAgICAgICAgICAgICBycmVzLmJvZHkgPSBiO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gcnJlcy5lbmQoKTtcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIGpzb246IChiKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHJyZXMuYm9keSA9IGI7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBycmVzLmVuZCgpO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgZW5kOiAoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHJjYih1bmRlZmluZWQsIHsgc3RhdHVzQ29kZTogcnJlcy5zdGF0dXNDb2RlLCBib2R5OiBycmVzLmJvZHkgfSk7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBycmVzO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAvLyBDYWxsIHRoZSBnaXZlbiByb3V0ZXJcbiAgICAgICAgICAgIHJvdXRlcyhycmVxLCBycmVzLCAoKSA9PiB7XG4gICAgICAgICAgICAgICAgaWYocnJlcy52YWx1ZSlcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHJjYih1bmRlZmluZWQsIHJyZXMudmFsdWUpO1xuICAgICAgICAgICAgICAgIG5leHQoKTtcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgIH0sIChlcnIsIGJyZXMpID0+IHtcbiAgICAgICAgICAgIGlmKGVycikgcmV0dXJuIHJlcy5zdGF0dXMoNTAwKS5lbmQoKTtcblxuICAgICAgICAgICAgLy8gUmV0dXJuIHRoZSBiYXRjaCBvZiByZXN1bHRzXG4gICAgICAgICAgICByZXMuc2VuZChicmVzKTtcbiAgICAgICAgfSk7XG4gICAgfTtcbn07XG5cbi8vIEV4cG9ydCBvdXIgcHVibGljIGZ1bmN0aW9uc1xubW9kdWxlLmV4cG9ydHMgPSByb3V0ZXI7XG5tb2R1bGUuZXhwb3J0cy5iYXRjaCA9IGJhdGNoO1xuXG4iXX0=
\No newline at end of file