1 | ;
|
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 |
|
7 | var _ = require('underscore');
|
8 | var express = require('express');
|
9 | var domain = require('domain');
|
10 | var url = require('url');
|
11 | var yieldable = require('abacus-yieldable');
|
12 | var transform = require('abacus-transform');
|
13 |
|
14 | var toArray = _.toArray;
|
15 | var map = _.map;
|
16 | var extend = _.extend;
|
17 | var clone = _.clone;
|
18 |
|
19 | // Setup debug log
|
20 | var 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.
|
24 | var 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
|
66 | var 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
|
74 | var 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
|
95 | var 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
|
121 | var 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
|
137 | var 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
|
182 | module.exports = router;
|
183 | module.exports.batch = batch;
|
184 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9pbmRleC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxZQUFZLENBQUM7Ozs7OztBQU1iLElBQU0sQ0FBQyxHQUFHLE9BQU8sQ0FBQyxZQUFZLENBQUMsQ0FBQztBQUNoQyxJQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7QUFDbkMsSUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDO0FBQ2pDLElBQU0sR0FBRyxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQztBQUMzQixJQUFNLFNBQVMsR0FBRyxPQUFPLENBQUMsa0JBQWtCLENBQUMsQ0FBQztBQUM5QyxJQUFNLFNBQVMsR0FBRyxPQUFPLENBQUMsa0JBQWtCLENBQUMsQ0FBQzs7QUFFOUMsSUFBTSxPQUFPLEdBQUcsQ0FBQyxDQUFDLE9BQU8sQ0FBQztBQUMxQixJQUFNLEdBQUcsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDO0FBQ2xCLElBQU0sTUFBTSxHQUFHLENBQUMsQ0FBQyxNQUFNLENBQUM7QUFDeEIsSUFBTSxLQUFLLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQzs7O0FBR3RCLElBQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0FBQyxlQUFlLENBQUMsQ0FBQzs7OztBQUl2RCxJQUFNLFdBQVcsR0FBRyxTQUFkLFdBQVcsQ0FBSSxDQUFDLEVBQUUsT0FBTyxFQUFLOzs7O0FBSWhDLFFBQU0sS0FBSyxHQUFHLFNBQVMsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUM7Ozs7O0FBS3RDLFdBQU8sWUFBVztBQUNkLFlBQU0sSUFBSSxHQUFHLFNBQVMsQ0FBQyxTQUFTLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDO0FBQzdDLFlBQU0sR0FBRyxHQUFHLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUN6QixZQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxTQUFTLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDOzs7OztBQUtqRSxZQUFNLEtBQUssR0FBRyxTQUFSLEtBQUssQ0FBSSxHQUFHLEVBQUUsSUFBSSxFQUFLO0FBQ3pCLGlCQUFLLENBQUMsdUJBQXVCLEVBQUUsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUFDO0FBQzFDLGdCQUFHLENBQUMsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sSUFBSSxDQUFDLEdBQUcsQ0FBQyxVQUFVLEVBQ3pDLEdBQUcsQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDO0FBQ3ZCLGdCQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7U0FDYixDQUFDOzs7QUFHRixZQUFJO0FBQ0EsaUJBQUssQ0FBQyxLQUFLLENBQUMsU0FBUyxFQUFFLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxVQUFDLEdBQUcsRUFBRSxLQUFLLEVBQUs7QUFDbEQsb0JBQUcsR0FBRyxFQUFFLEtBQUssQ0FBQyxHQUFHLEVBQUUsaUJBQWlCLENBQUMsQ0FBQyxLQUVqQyxJQUFHLEtBQUssRUFBRTs7OztBQUlYLHVCQUFHLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQztBQUNsQix3QkFBSSxFQUFFLENBQUM7aUJBQ1Y7YUFDSixDQUFDLENBQUMsQ0FBQyxDQUFDO1NBQ1IsQ0FDRCxPQUFNLEdBQUcsRUFBRTtBQUNQLGlCQUFLLENBQUMsR0FBRyxFQUFFLFdBQVcsQ0FBQyxDQUFDO1NBQzNCO0tBQ0osQ0FBQztDQUNMLENBQUM7Ozs7QUFJRixJQUFNLEdBQUcsR0FBRyxTQUFOLEdBQUcsQ0FBSSxRQUFRLEVBQUUsT0FBTyxFQUFLO0FBQy9CLFdBQU8sVUFBUyxJQUFJLEVBQUUsQ0FBQyxFQUFFO0FBQ3JCLGVBQU8sT0FBTyxJQUFJLEtBQUssVUFBVSxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLFdBQVcsQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLENBQUMsR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxJQUFJLEVBQUUsV0FBVyxDQUFDLENBQUMsRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDO0tBQzVJLENBQUM7Q0FDTCxDQUFDOzs7O0FBSUYsSUFBTSxLQUFLLEdBQUcsU0FBUixLQUFLLENBQUksUUFBUSxFQUFFLE9BQU8sRUFBSztBQUNqQyxXQUFPLFlBQVc7O0FBRWQsWUFBTSxDQUFDLEdBQUcsUUFBUSxDQUFDLEtBQUssQ0FBQyxJQUFJLEVBQUUsU0FBUyxDQUFDLENBQUM7OztBQUcxQyxXQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLEtBQUssQ0FBQyxFQUFFLFVBQUMsTUFBTSxFQUFLO0FBQ3hGLGdCQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUM7QUFDcEIsYUFBQyxDQUFDLE1BQU0sQ0FBQyxHQUFHLFlBQVc7QUFDbkIsb0JBQU0sVUFBVSxHQUFHLEdBQUcsQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsVUFBUyxDQUFDLEVBQUU7QUFDbkQsMkJBQU8sV0FBVyxDQUFDLENBQUMsRUFBRSxPQUFPLENBQUMsQ0FBQztpQkFDbEMsQ0FBQyxDQUFDO0FBQ0gsdUJBQU8sQ0FBQyxDQUFDLEtBQUssQ0FBQyxJQUFJLEVBQUUsVUFBVSxDQUFDLENBQUM7YUFDcEMsQ0FBQztTQUNMLENBQUMsQ0FBQztBQUNILGVBQU8sQ0FBQyxDQUFDO0tBQ1osQ0FBQztDQUNMLENBQUM7Ozs7QUFJRixJQUFNLFFBQVEsR0FBRyxTQUFYLFFBQVEsQ0FBSSxPQUFPLEVBQUs7QUFDMUIsV0FBTyxVQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFLO0FBQ3ZCLFlBQU0sQ0FBQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQztBQUMxQixTQUFDLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxVQUFDLEdBQUcsRUFBSztBQUNuQixpQkFBSyxDQUFDLHVCQUF1QixFQUFFLEdBQUcsQ0FBQyxDQUFDOzs7Ozs7QUFNcEMsZ0JBQUcsQ0FBQyxPQUFPLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxJQUFJLENBQUMsR0FBRyxDQUFDLFVBQVUsRUFDekMsR0FBRyxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUM7QUFDdkIsZ0JBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztTQUNiLENBQUMsQ0FBQzs7Ozs7QUFLSCxTQUFDLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0FBQ1gsU0FBQyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQzs7O0FBR1gsU0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztLQUNmLENBQUM7Q0FDTCxDQUFDOzs7QUFHRixJQUFNLE1BQU0sR0FBRyxTQUFULE1BQU0sQ0FBSSxPQUFPLEVBQUs7QUFDeEIsUUFBTSxDQUFDLEdBQUcsT0FBTyxDQUFDLE1BQU0sRUFBRSxDQUFDOzs7QUFHM0IsS0FBQyxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQzs7OztBQUl6QixLQUFDLENBQUMsR0FBRyxHQUFHLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FBRyxFQUFFLE9BQU8sQ0FBQyxDQUFDO0FBQzVCLEtBQUMsQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLEVBQUUsT0FBTyxDQUFDLENBQUM7O0FBRWxDLFdBQU8sQ0FBQyxDQUFDO0NBQ1osQ0FBQzs7OztBQUlGLElBQU0sS0FBSyxHQUFHLFNBQVIsS0FBSyxDQUFJLE1BQU0sRUFBSztBQUN0QixXQUFPLFVBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUs7QUFDdkIsWUFBRyxHQUFHLENBQUMsTUFBTSxLQUFLLE1BQU0sSUFBSSxHQUFHLENBQUMsSUFBSSxLQUFLLFFBQVEsRUFDN0MsT0FBTyxJQUFJLEVBQUUsQ0FBQztBQUNsQixhQUFLLENBQUMsMkJBQTJCLEVBQUUsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDOzs7QUFHN0MsaUJBQVMsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxVQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsSUFBSSxFQUFFLEdBQUcsRUFBSzs7QUFFekMsZ0JBQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEVBQUUsRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRSxHQUFHLEVBQUUsR0FBRyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7OztBQUd0RyxnQkFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsRUFBRTtBQUM1QixzQkFBTSxFQUFFLGdCQUFDLENBQUMsRUFBSztBQUNYLHdCQUFJLENBQUMsVUFBVSxHQUFHLENBQUMsQ0FBQztBQUNwQiwyQkFBTyxJQUFJLENBQUM7aUJBQ2Y7QUFDRCxvQkFBSSxFQUFFLGNBQUMsQ0FBQyxFQUFLO0FBQ1Qsd0JBQUksQ0FBQyxJQUFJLEdBQUcsQ0FBQyxDQUFDO0FBQ2QsMkJBQU8sSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO2lCQUNyQjtBQUNELG9CQUFJLEVBQUUsY0FBQyxDQUFDLEVBQUs7QUFDVCx3QkFBSSxDQUFDLElBQUksR0FBRyxDQUFDLENBQUM7QUFDZCwyQkFBTyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7aUJBQ3JCO0FBQ0QsbUJBQUcsRUFBRSxlQUFNO0FBQ1AsdUJBQUcsQ0FBQyxTQUFTLEVBQUUsRUFBRSxVQUFVLEVBQUUsSUFBSSxDQUFDLFVBQVUsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7QUFDakUsMkJBQU8sSUFBSSxDQUFDO2lCQUNmO2FBQ0osQ0FBQyxDQUFDOzs7QUFHSCxrQkFBTSxDQUFDLElBQUksRUFBRSxJQUFJLEVBQUUsWUFBTTtBQUNyQixvQkFBRyxJQUFJLENBQUMsS0FBSyxFQUNULE9BQU8sR0FBRyxDQUFDLFNBQVMsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7QUFDdEMsb0JBQUksRUFBRSxDQUFDO2FBQ1YsQ0FBQyxDQUFDO1NBRU4sRUFBRSxVQUFDLEdBQUcsRUFBRSxJQUFJLEVBQUs7QUFDZCxnQkFBRyxHQUFHLEVBQUUsT0FBTyxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsRUFBRSxDQUFDOzs7QUFHckMsZUFBRyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUNsQixDQUFDLENBQUM7S0FDTixDQUFDO0NBQ0wsQ0FBQzs7O0FBR0YsTUFBTSxDQUFDLE9BQU8sR0FBRyxNQUFNLENBQUM7QUFDeEIsTUFBTSxDQUFDLE9BQU8sQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDIiwiZmlsZSI6ImluZGV4LmpzIiwic291cmNlc0NvbnRlbnQiOlsiJ3VzZSBzdHJpY3QnO1xuXG4vLyBTbWFsbCBFeHByZXNzIHJvdXRlciB0aGF0IHJ1bnMgcmVxdWVzdCBoYW5kbGVycyB3cml0dGVuIGFzIEVTNiBnZW5lcmF0b3JzXG4vLyB1c2luZyBOb2RlIGNvLiBPbiB0b3Agb2YgdGhhdCB0aGUgcm91dGVyIGRvZXMgYSBmZXcgdXNlZnVsIHRoaW5ncywgaW5jbHVkaW5nXG4vLyBzb21lIGxvZ2dpbmcgYW5kIGVycm9yIGhhbmRsaW5nIHVzaW5nIGEgTm9kZSBkb21haW4uXG5cbmNvbnN0IF8gPSByZXF1aXJlKCd1bmRlcnNjb3JlJyk7XG5jb25zdCBleHByZXNzID0gcmVxdWlyZSgnZXhwcmVzcycpO1xuY29uc3QgZG9tYWluID0gcmVxdWlyZSgnZG9tYWluJyk7XG5jb25zdCB1cmwgPSByZXF1aXJlKCd1cmwnKTtcbmNvbnN0IHlpZWxkYWJsZSA9IHJlcXVpcmUoJ2FiYWN1cy15aWVsZGFibGUnKTtcbmNvbnN0IHRyYW5zZm9ybSA9IHJlcXVpcmUoJ2FiYWN1cy10cmFuc2Zvcm0nKTtcblxuY29uc3QgdG9BcnJheSA9IF8udG9BcnJheTtcbmNvbnN0IG1hcCA9IF8ubWFwO1xuY29uc3QgZXh0ZW5kID0gXy5leHRlbmQ7XG5jb25zdCBjbG9uZSA9IF8uY2xvbmU7XG5cbi8vIFNldHVwIGRlYnVnIGxvZ1xuY29uc3QgZGVidWcgPSByZXF1aXJlKCdhYmFjdXMtZGVidWcnKSgnYWJhY3VzLXJvdXRlcicpO1xuXG4vLyBDb252ZXJ0IGEgbWlkZGxld2FyZSBmdW5jdGlvbiB3aGljaCBjYW4gYmUgYSByZWd1bGFyIEV4cHJlc3MgbWlkZGxld2FyZSBvciBhXG4vLyBnZW5lcmF0b3IgdG8gYSByZWd1bGFyIEV4cHJlc3MgbWlkZGxld2FyZSBmdW5jdGlvbi5cbmNvbnN0IGNhbGxiYWNraWZ5ID0gKG0sIHRydXN0ZWQpID0+IHtcblxuICAgIC8vIElmIHRoZSBjYWxsYmFjayBpcyBhIHJlZ3VsYXIgZnVuY3Rpb24ganVzdCB1c2UgaXQgYXMtaXMsIG90aGVyd2lzZSBpdCdzXG4gICAgLy8gYSBnZW5lcmF0b3IgYW5kIHdlIG5lZWQgdG8gd3JhcCBpdCB1c2luZyB0aGUgY28gbW9kdWxlXG4gICAgY29uc3QgbWZ1bmMgPSB5aWVsZGFibGUuZnVuY3Rpb25jYihtKTtcblxuICAgIC8vIFJldHVybiBhIG1pZGRsZXdhcmUgZnVuY3Rpb24uIE1pZGRsZXdhcmUgZnVuY3Rpb25zIGNhbiBiZSBvZiB0aGUgZm9ybVxuICAgIC8vIGZ1bmN0aW9uKHJlcSwgcmVzLCBuZXh0KSBvciBmdW5jdGlvbihlcnIsIHJlcSwgcmVzLCBuZXh0KSBzbyB3ZSBuZWVkIHRvXG4gICAgLy8gc3VwcG9ydCBib3RoIGZvcm1zXG4gICAgcmV0dXJuIGZ1bmN0aW9uKCkge1xuICAgICAgICBjb25zdCBuZXh0ID0gYXJndW1lbnRzW2FyZ3VtZW50cy5sZW5ndGggLSAxXTtcbiAgICAgICAgY29uc3QgcmVzID0gYXJndW1lbnRzWzFdO1xuICAgICAgICBjb25zdCBwYXJhbXMgPSB0b0FycmF5KGFyZ3VtZW50cykuc2xpY2UoMCwgYXJndW1lbnRzLmxlbmd0aCAtIDEpO1xuXG4gICAgICAgIC8vIFBhc3MgZXJyb3JzIGRvd24gdGhlIG1pZGRsZXdhcmUgc3RhY2ssIGlmIHRoZSBtaWRkbGV3YXJlIGlzXG4gICAgICAgIC8vIHVuLXRydXN0ZWQgdGhlbiB3ZSBtYXJrIHRoZSBlcnJvciB3aXRoIGJhaWxvdXQgZmxhZyB0byB0cmlnZ2VyIG91clxuICAgICAgICAvLyBzZXJ2ZXIgYmFpbG91dCBsb2dpY1xuICAgICAgICBjb25zdCBlcnJvciA9IChlcnIsIHR5cGUpID0+IHtcbiAgICAgICAgICAgIGRlYnVnKCdSb3V0ZSBlcnJvciAtICVzIC0gJW8nLCB0eXBlLCBlcnIpO1xuICAgICAgICAgICAgaWYoIXRydXN0ZWQgJiYgIWVyci5zdGF0dXMgJiYgIWVyci5zdGF0dXNDb2RlKVxuICAgICAgICAgICAgICAgIGVyci5iYWlsb3V0ID0gdHJ1ZTtcbiAgICAgICAgICAgIG5leHQoZXJyKTtcbiAgICAgICAgfTtcblxuICAgICAgICAvLyBDYWxsIHRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9uXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBtZnVuYy5hcHBseSh1bmRlZmluZWQsIHBhcmFtcy5jb25jYXQoWyhlcnIsIHZhbHVlKSA9PiB7XG4gICAgICAgICAgICAgICAgaWYoZXJyKSBlcnJvcihlcnIsICdnZW5lcmF0b3IgZXJyb3InKTtcblxuICAgICAgICAgICAgICAgIGVsc2UgaWYodmFsdWUpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gU3RvcmUgdGhlIHJldHVybmVkIHZhbHVlIGluIHRoZSByZXNwb25zZSwgaXQnbGwgYmUgc2VudFxuICAgICAgICAgICAgICAgICAgICAvLyBieSBvbmUgb2Ygb3VyIEV4cHJlc3MgbWlkZGxld2FyZSBsYXRlciBkb3duIHRoZVxuICAgICAgICAgICAgICAgICAgICAvLyBtaWRkbGV3YXJlIHN0YWNrXG4gICAgICAgICAgICAgICAgICAgIHJlcy52YWx1ZSA9IHZhbHVlO1xuICAgICAgICAgICAgICAgICAgICBuZXh0KCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfV0pKTtcbiAgICAgICAgfVxuICAgICAgICBjYXRjaChleGMpIHtcbiAgICAgICAgICAgIGVycm9yKGV4YywgJ2V4Y2VwdGlvbicpO1xuICAgICAgICB9XG4gICAgfTtcbn07XG5cbi8vIFJldHVybiBhbiBpbXBsZW1lbnRhdGlvbiBvZiB0aGUgcm91dGVyLnVzZShwYXRoLCBtaWRkbGV3YXJlKSBmdW5jdGlvbiB0aGF0IHN1cHBvcnRzXG4vLyBtaWRkbGV3YXJlIGltcGxlbWVudGVkIGFzIGdlbmVyYXRvcnMgaW4gYWRkaXRpb24gdG8gcmVndWxhciBjYWxsYmFja3NcbmNvbnN0IHVzZSA9IChvcmlnaW5hbCwgdHJ1c3RlZCkgPT4ge1xuICAgIHJldHVybiBmdW5jdGlvbihwYXRoLCBtKSB7XG4gICAgICAgIHJldHVybiB0eXBlb2YgcGF0aCA9PT0gJ2Z1bmN0aW9uJyA/IG9yaWdpbmFsLmNhbGwodGhpcywgY2FsbGJhY2tpZnkocGF0aCwgdHJ1c3RlZCkpIDogb3JpZ2luYWwuY2FsbCh0aGlzLCBwYXRoLCBjYWxsYmFja2lmeShtLCB0cnVzdGVkKSk7XG4gICAgfTtcbn07XG5cbi8vIFJldHVybiBhbiBpbXBsZW1lbnRhdGlvbiBvZiB0aGUgcm91dGVyLnJvdXRlKCkgZnVuY3Rpb24gdGhhdCBzdXBwb3J0cyBtaWRkbGV3YXJlIGltcGxlbWVudGVkXG4vLyBhcyBnZW5lcmF0b3JzIGluIGFkZGl0aW9uIHRvIHJlZ3VsYXIgY2FsbGJhY2tzXG5jb25zdCByb3V0ZSA9IChvcmlnaW5hbCwgdHJ1c3RlZCkgPT4ge1xuICAgIHJldHVybiBmdW5jdGlvbigpIHtcbiAgICAgICAgLy8gR2V0IHRoZSByb3V0ZVxuICAgICAgICBjb25zdCByID0gb3JpZ2luYWwuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcblxuICAgICAgICAvLyBNb25rZXkgcGF0Y2ggaXRzIEhUVFAgbWV0aG9kc1xuICAgICAgICBtYXAoWydnZXQnLCAnaGVhZCcsICdwb3N0JywgJ3B1dCcsICdwYXRjaCcsICdkZWxldGUnLCAnb3B0aW9ucycsICdhbGwnLCAndXNlJ10sIChtZXRob2QpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGYgPSByW21ldGhvZF07XG4gICAgICAgICAgICByW21ldGhvZF0gPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBtaWRkbGV3YXJlID0gbWFwKHRvQXJyYXkoYXJndW1lbnRzKSwgZnVuY3Rpb24obSkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gY2FsbGJhY2tpZnkobSwgdHJ1c3RlZCk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGYuYXBwbHkodGhpcywgbWlkZGxld2FyZSk7XG4gICAgICAgICAgICB9O1xuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIHI7XG4gICAgfTtcbn07XG5cbi8vIFJldHVybiBhbiBFeHByZXNzIG1pZGRsZXdhcmUgdGhhdCB1c2VzIGEgTm9kZSBkb21haW4gdG8gcnVuIHRoZSBtaWRkbGV3YXJlIHN0YWNrIGFuZFxuLy8gaGFuZGxlIGFueSBlcnJvcnMgbm90IGNhdWdodCBpbiBhc3luYyBjYWxsYmFja3NcbmNvbnN0IGNhdGNoYWxsID0gKHRydXN0ZWQpID0+IHtcbiAgICByZXR1cm4gKHJlcSwgcmVzLCBuZXh0KSA9PiB7XG4gICAgICAgIGNvbnN0IGQgPSBkb21haW4uY3JlYXRlKCk7XG4gICAgICAgIGQub24oJ2Vycm9yJywgKGVycikgPT4ge1xuICAgICAgICAgICAgZGVidWcoJ1JvdXRlIGRvbWFpbiBlcnJvciAlbycsIGVycik7XG5cbiAgICAgICAgICAgIC8vIFBhc3MgdGhlIGVycm9yIGRvd24gdGhlIG1pZGRsZXdhcmUgc3RhY2ssIGlmIHRoZSByb3V0ZXIgcnVuc1xuICAgICAgICAgICAgLy8gdW4tdHJ1c3RlZCBtaWRkbGV3YXJlIHRoZW4gbWFyayBpdCB3aXRoIGEgYmFpbG91dCBmbGFnIHRvXG4gICAgICAgICAgICAvLyB0cmlnZ2VyIG91ciBzZXJ2ZXIgYmFpbG91dCBsb2dpY1xuICAgICAgICAgICAgLy8gV2FybmluZzogbXV0YXRpbmcgdmFyaWFibGUgZXJyXG4gICAgICAgICAgICBpZighdHJ1c3RlZCAmJiAhZXJyLnN0YXR1cyAmJiAhZXJyLnN0YXR1c0NvZGUpXG4gICAgICAgICAgICAgICAgZXJyLmJhaWxvdXQgPSB0cnVlO1xuICAgICAgICAgICAgbmV4dChlcnIpO1xuICAgICAgICB9KTtcblxuICAgICAgICAvLyBCZWNhdXNlIHJlcSBhbmQgcmVzIHdlcmUgY3JlYXRlZCBiZWZvcmUgdGhpcyBkb21haW4gZXhpc3RlZCxcbiAgICAgICAgLy8gd2UgbmVlZCB0byBleHBsaWNpdGx5IGFkZCB0aGVtLiAgU2VlIHRoZSBleHBsYW5hdGlvbiBvZiBpbXBsaWNpdFxuICAgICAgICAvLyB2cyBleHBsaWNpdCBiaW5kaW5nIGluIHRoZSBOb2RlIGRvbWFpbiBkb2NzLlxuICAgICAgICBkLmFkZChyZXEpO1xuICAgICAgICBkLmFkZChyZXMpO1xuXG4gICAgICAgIC8vIFJ1biB0aGUgbWlkZGxld2FyZSBzdGFjayBpbiBvdXIgbmV3IGRvbWFpblxuICAgICAgICBkLnJ1bihuZXh0KTtcbiAgICB9O1xufTtcblxuLy8gUmV0dXJuIGFuIEV4cHJlc3Mgcm91dGVyIG1pZGRsZXdhcmUgdGhhdCB3b3JrcyB3aXRoIGdlbmVyYXRvcnNcbmNvbnN0IHJvdXRlciA9ICh0cnVzdGVkKSA9PiB7XG4gICAgY29uc3QgciA9IGV4cHJlc3MuUm91dGVyKCk7XG5cbiAgICAvLyBDYXRjaCBhbGwgZXJyb3JzIGRvd24gdGhlIG1pZGRsZXdhcmUgc3RhY2sgdXNpbmcgYSBOb2RlIGRvbWFpblxuICAgIHIudXNlKGNhdGNoYWxsKHRydXN0ZWQpKTtcblxuICAgIC8vIE1vbmtleSBwYXRjaCB0aGUgcm91dGVyIGZ1bmN0aW9uIHdpdGggb3VyIGltcGxlbWVudGF0aW9uIG9mIHRoZSB1c2VcbiAgICAvLyBhbmQgcm91dGUgZnVuY3Rpb25zXG4gICAgci51c2UgPSB1c2Uoci51c2UsIHRydXN0ZWQpO1xuICAgIHIucm91dGUgPSByb3V0ZShyLnJvdXRlLCB0cnVzdGVkKTtcblxuICAgIHJldHVybiByO1xufTtcblxuLy8gUmV0dXJuIGFuIGV4cHJlc3MgbWlkZGxld2FyZSB0aGF0IHJ1bnMgYSBiYXRjaCBvZiByZXF1ZXN0cyB0aHJvdWdoIHRoZVxuLy8gZ2l2ZW4gcm91dGVyXG5jb25zdCBiYXRjaCA9IChyb3V0ZXMpID0+IHtcbiAgICByZXR1cm4gKHJlcSwgcmVzLCBuZXh0KSA9PiB7XG4gICAgICAgIGlmKHJlcS5tZXRob2QgIT09ICdQT1NUJyB8fCByZXEucGF0aCAhPT0gJy9iYXRjaCcpXG4gICAgICAgICAgICByZXR1cm4gbmV4dCgpO1xuICAgICAgICBkZWJ1ZygnSGFuZGxpbmcgYmF0Y2ggcmVxdWVzdCAlbycsIHJlcS5ib2R5KTtcblxuICAgICAgICAvLyBSdW4gdGhlIGJhdGNoIG9mIHJlcXVlc3RzIGZvdW5kIGluIHRoZSBib2R5IHRocm91Z2ggdGhlIHJvdXRlclxuICAgICAgICB0cmFuc2Zvcm0ubWFwKHJlcS5ib2R5LCAociwgaSwgcmVxcywgcmNiKSA9PiB7XG4gICAgICAgICAgICAvLyBTZXR1cCBhbiBFeHByZXNzIHJlcXVlc3QgcmVwcmVzZW50aW5nIHRoZSBiYXRjaGVkIHJlcXVlc3RcbiAgICAgICAgICAgIGNvbnN0IHJyZXEgPSBleHRlbmQoY2xvbmUocmVxKSwgeyBtZXRob2Q6IHIubWV0aG9kLCB1cmw6IHVybC5yZXNvbHZlKHJlcS51cmwsIHIudXJpKSwgYm9keTogci5ib2R5IH0pO1xuXG4gICAgICAgICAgICAvLyBTZXR1cCBhbiBFeHByZXNzIHJlc3BvbnNlIG9iamVjdCB0aGF0IHdpbGwgY2FwdHVyZSB0aGUgcmVzcG9uc2VcbiAgICAgICAgICAgIGNvbnN0IHJyZXMgPSBleHRlbmQoY2xvbmUocmVzKSwge1xuICAgICAgICAgICAgICAgIHN0YXR1czogKHMpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgcnJlcy5zdGF0dXNDb2RlID0gcztcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHJyZXM7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBzZW5kOiAoYikgPT4ge1xuICAgICAgICAgICAgICAgICAgICBycmVzLmJvZHkgPSBiO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gcnJlcy5lbmQoKTtcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIGpzb246IChiKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHJyZXMuYm9keSA9IGI7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBycmVzLmVuZCgpO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgZW5kOiAoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHJjYih1bmRlZmluZWQsIHsgc3RhdHVzQ29kZTogcnJlcy5zdGF0dXNDb2RlLCBib2R5OiBycmVzLmJvZHkgfSk7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBycmVzO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAvLyBDYWxsIHRoZSBnaXZlbiByb3V0ZXJcbiAgICAgICAgICAgIHJvdXRlcyhycmVxLCBycmVzLCAoKSA9PiB7XG4gICAgICAgICAgICAgICAgaWYocnJlcy52YWx1ZSlcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHJjYih1bmRlZmluZWQsIHJyZXMudmFsdWUpO1xuICAgICAgICAgICAgICAgIG5leHQoKTtcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgIH0sIChlcnIsIGJyZXMpID0+IHtcbiAgICAgICAgICAgIGlmKGVycikgcmV0dXJuIHJlcy5zdGF0dXMoNTAwKS5lbmQoKTtcblxuICAgICAgICAgICAgLy8gUmV0dXJuIHRoZSBiYXRjaCBvZiByZXN1bHRzXG4gICAgICAgICAgICByZXMuc2VuZChicmVzKTtcbiAgICAgICAgfSk7XG4gICAgfTtcbn07XG5cbi8vIEV4cG9ydCBvdXIgcHVibGljIGZ1bmN0aW9uc1xubW9kdWxlLmV4cG9ydHMgPSByb3V0ZXI7XG5tb2R1bGUuZXhwb3J0cy5iYXRjaCA9IGJhdGNoO1xuXG4iXX0= |
\ | No newline at end of file |