UNPKG

24.5 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6
7var _tls = require('tls');
8
9var _tls2 = _interopRequireDefault(_tls);
10
11var _net = require('net');
12
13var _net2 = _interopRequireDefault(_net);
14
15var _winston = require('winston');
16
17var _winston2 = _interopRequireDefault(_winston);
18
19var _genericPool = require('generic-pool');
20
21var _genericPool2 = _interopRequireDefault(_genericPool);
22
23var _events = require('events');
24
25function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
26
27class ConnectionInterface extends _events.EventEmitter {
28 constructor(socket, id) {
29 super();
30 this.id = id;
31 this.socket = socket;
32 this.socket.on('error', e => this.emit('error', e));
33 this.socket.on('close', e => this.emit('close', e));
34 }
35
36 send(buffer) {
37 this.socket.write(buffer);
38 }
39
40 destroy() {
41 this.socket.destroy();
42 }
43
44 end() {
45 this.socket.end();
46 }
47
48 get readyState() {
49 return this.socket.readyState;
50 }
51}
52
53class TcpPool {
54 constructor(interfaceConstructor, options) {
55 this.Parser = interfaceConstructor;
56 this.options = Object.assign({}, options);
57 this.name = this.options.name || `${interfaceConstructor.name} Connection Pool`;
58 this.connectionCount = 0;
59
60 const factory = {
61 create: () => this.connect(),
62 destroy: client => this.disconnect(client),
63 validate: client => this.validate(client)
64 };
65 const config = {
66 max: this.options.max || 10,
67 min: this.options.min || 1,
68 acquireTimeoutMillis: this.options.acquireTimeoutMillis || 15000,
69 idleTimeoutMillis: this.options.idleTimeoutMillis || 30000,
70 testOnBorrow: true
71 };
72 this.pool = _genericPool2.default.createPool(factory, config);
73 }
74
75 async acquire(context) {
76 const logger = this.loggerForContext(context);
77 logger.info(`Acquiring connection from ${this.name} pool`);
78 try {
79 const conn = await this.pool.acquire();
80 logger.info(`Returning connection #${conn.id} from ${this.name} pool`);
81 conn.context = context;
82 return conn;
83 } catch (error) {
84 logger.error(`Failed to acquire connection from ${this.name} pool`, {
85 error: error.message || error
86 });
87 throw error;
88 }
89 }
90
91 release(conn) {
92 const logger = this.loggerForContext(conn.context);
93 logger.info(`Releasing connection #${conn.id} into ${this.name} pool`);
94 this.reset(conn);
95 // eslint-disable-next-line no-param-reassign
96 delete conn.context;
97 this.pool.release(conn);
98 }
99
100 destroy(conn) {
101 const logger = this.loggerForContext(conn.context);
102 logger.info(`Destroying connection #${conn.id} of ${this.name} pool`);
103 this.reset(conn);
104 // eslint-disable-next-line no-param-reassign
105 delete conn.context;
106 this.pool.destroy(conn);
107 }
108
109 async destroyAllNow() {
110 _winston2.default.debug(`Pool ${this.name} shutting down`);
111 await this.pool.drain();
112 _winston2.default.debug(`Pool ${this.name} drained`);
113 await this.pool.clear();
114 _winston2.default.debug(`Pool ${this.name} cleared`);
115 }
116
117 async connect() {
118 this.connectionCount += 1;
119 const myId = this.connectionCount;
120 _winston2.default.info(`Pool ${this.name} socket #${myId} connecting`);
121 let attemptCompleted = false;
122 let socket;
123
124 return new Promise((accept, reject) => {
125 let resolved = false;
126 const connectionHandler = async () => {
127 if (!attemptCompleted) {
128 _winston2.default.info(`Pool ${this.name} socket #${myId} connected`);
129 attemptCompleted = true;
130 socket.removeAllListeners();
131 const connectionParser = new this.Parser(socket, myId);
132 if (typeof connectionParser.initializeConnection === 'function') {
133 try {
134 await connectionParser.initializeConnection();
135 } catch (error) {
136 reject(error);
137 return;
138 }
139 }
140 this.reset(connectionParser);
141 resolved = true;
142 accept(connectionParser);
143 }
144 };
145
146 try {
147 if (this.options.insecure === true) {
148 socket = _net2.default.connect({
149 host: this.options.host,
150 port: this.options.port
151 }, connectionHandler);
152 } else {
153 const tlsOptions = Object.assign({
154 secureProtocol: 'TLSv1_2_client_method',
155 host: this.options.host,
156 port: this.options.port
157 }, this.options.tlsOptions);
158 socket = _tls2.default.connect(tlsOptions, connectionHandler);
159 }
160
161 socket.once('error', error => {
162 _winston2.default.error(`Error on Pool ${this.name} socket #${myId}`, {
163 message: error.message,
164 stack: error.stack
165 });
166 if (!attemptCompleted) {
167 attemptCompleted = true;
168 socket.end();
169 // Reject after a second to give some backoff time
170 if (!resolved) {
171 setTimeout(() => reject(error), 1000);
172 resolved = true;
173 }
174 }
175 });
176 } catch (error) {
177 _winston2.default.error(`Error on Pool ${this.name}`, {
178 message: error.message,
179 stack: error.stack
180 });
181 if (!resolved) {
182 reject(error);
183 }
184 }
185 });
186 }
187
188 loggerForContext(context) {
189 if (this.options.loggerFromContext) {
190 return this.options.loggerFromContext(context) || _winston2.default;
191 }
192 return _winston2.default;
193 }
194
195 reset(conn) {
196 conn.removeAllListeners();
197 conn.on('error', error => this.onError(conn, error));
198 conn.on('close', error => this.onClose(conn, error));
199 }
200
201 onError(conn, error) {
202 const logger = this.loggerForContext(conn.context);
203 logger.error(`Error on Pool ${this.name} socket #${conn.id}`, {
204 message: error.message,
205 stack: error.stack
206 });
207 conn.end();
208 this.pool.destroy(conn);
209 }
210
211 onClose(conn) {
212 const logger = this.loggerForContext(conn.context);
213 logger.info(`Pool ${this.name} socket #${conn.id} closed`);
214 }
215
216 validate(conn) {
217 return new Promise(accept => {
218 if (typeof conn.validate === 'function') {
219 Promise.resolve(conn.validate()).then(isValid => accept(isValid));
220 } else {
221 if (conn.readyState === 'open') {
222 accept(true);
223 return;
224 }
225 _winston2.default.error(`Invalid connection in Pool ${this.name} socket #${conn.id}`);
226 accept(false);
227 }
228 });
229 }
230
231 disconnect(conn) {
232 return new Promise((accept, reject) => {
233 try {
234 _winston2.default.debug(`Pool ${this.name} socket #${conn.id} closing`);
235 conn.destroy();
236 accept();
237 } catch (error) {
238 reject(error);
239 }
240 });
241 }
242}
243
244exports.default = TcpPool;
245TcpPool.ConnectionInterface = ConnectionInterface;
246//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9pbmRleC5qcyJdLCJuYW1lcyI6WyJDb25uZWN0aW9uSW50ZXJmYWNlIiwiY29uc3RydWN0b3IiLCJzb2NrZXQiLCJpZCIsIm9uIiwiZSIsImVtaXQiLCJzZW5kIiwiYnVmZmVyIiwid3JpdGUiLCJkZXN0cm95IiwiZW5kIiwicmVhZHlTdGF0ZSIsIlRjcFBvb2wiLCJpbnRlcmZhY2VDb25zdHJ1Y3RvciIsIm9wdGlvbnMiLCJQYXJzZXIiLCJPYmplY3QiLCJhc3NpZ24iLCJuYW1lIiwiY29ubmVjdGlvbkNvdW50IiwiZmFjdG9yeSIsImNyZWF0ZSIsImNvbm5lY3QiLCJjbGllbnQiLCJkaXNjb25uZWN0IiwidmFsaWRhdGUiLCJjb25maWciLCJtYXgiLCJtaW4iLCJhY3F1aXJlVGltZW91dE1pbGxpcyIsImlkbGVUaW1lb3V0TWlsbGlzIiwidGVzdE9uQm9ycm93IiwicG9vbCIsImNyZWF0ZVBvb2wiLCJhY3F1aXJlIiwiY29udGV4dCIsImxvZ2dlciIsImxvZ2dlckZvckNvbnRleHQiLCJpbmZvIiwiY29ubiIsImVycm9yIiwibWVzc2FnZSIsInJlbGVhc2UiLCJyZXNldCIsImRlc3Ryb3lBbGxOb3ciLCJkZWJ1ZyIsImRyYWluIiwiY2xlYXIiLCJteUlkIiwiYXR0ZW1wdENvbXBsZXRlZCIsIlByb21pc2UiLCJhY2NlcHQiLCJyZWplY3QiLCJyZXNvbHZlZCIsImNvbm5lY3Rpb25IYW5kbGVyIiwicmVtb3ZlQWxsTGlzdGVuZXJzIiwiY29ubmVjdGlvblBhcnNlciIsImluaXRpYWxpemVDb25uZWN0aW9uIiwiaW5zZWN1cmUiLCJob3N0IiwicG9ydCIsInRsc09wdGlvbnMiLCJzZWN1cmVQcm90b2NvbCIsIm9uY2UiLCJzdGFjayIsInNldFRpbWVvdXQiLCJsb2dnZXJGcm9tQ29udGV4dCIsIm9uRXJyb3IiLCJvbkNsb3NlIiwicmVzb2x2ZSIsInRoZW4iLCJpc1ZhbGlkIl0sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQTs7OztBQUNBOzs7O0FBQ0E7Ozs7QUFDQTs7OztBQUNBOzs7O0FBRUEsTUFBTUEsbUJBQU4sOEJBQStDO0FBQzdDQyxjQUFZQyxNQUFaLEVBQW9CQyxFQUFwQixFQUF3QjtBQUN0QjtBQUNBLFNBQUtBLEVBQUwsR0FBVUEsRUFBVjtBQUNBLFNBQUtELE1BQUwsR0FBY0EsTUFBZDtBQUNBLFNBQUtBLE1BQUwsQ0FBWUUsRUFBWixDQUFlLE9BQWYsRUFBd0JDLEtBQUssS0FBS0MsSUFBTCxDQUFVLE9BQVYsRUFBbUJELENBQW5CLENBQTdCO0FBQ0EsU0FBS0gsTUFBTCxDQUFZRSxFQUFaLENBQWUsT0FBZixFQUF3QkMsS0FBSyxLQUFLQyxJQUFMLENBQVUsT0FBVixFQUFtQkQsQ0FBbkIsQ0FBN0I7QUFDRDs7QUFFREUsT0FBS0MsTUFBTCxFQUFhO0FBQ1gsU0FBS04sTUFBTCxDQUFZTyxLQUFaLENBQWtCRCxNQUFsQjtBQUNEOztBQUVERSxZQUFVO0FBQ1IsU0FBS1IsTUFBTCxDQUFZUSxPQUFaO0FBQ0Q7O0FBRURDLFFBQU07QUFDSixTQUFLVCxNQUFMLENBQVlTLEdBQVo7QUFDRDs7QUFFRCxNQUFJQyxVQUFKLEdBQWlCO0FBQ2YsV0FBTyxLQUFLVixNQUFMLENBQVlVLFVBQW5CO0FBQ0Q7QUF2QjRDOztBQTBCaEMsTUFBTUMsT0FBTixDQUFjO0FBQzNCWixjQUFZYSxvQkFBWixFQUFrQ0MsT0FBbEMsRUFBMkM7QUFDekMsU0FBS0MsTUFBTCxHQUFjRixvQkFBZDtBQUNBLFNBQUtDLE9BQUwsR0FBZUUsT0FBT0MsTUFBUCxDQUFjLEVBQWQsRUFBa0JILE9BQWxCLENBQWY7QUFDQSxTQUFLSSxJQUFMLEdBQVksS0FBS0osT0FBTCxDQUFhSSxJQUFiLElBQXNCLEdBQUVMLHFCQUFxQkssSUFBSyxrQkFBOUQ7QUFDQSxTQUFLQyxlQUFMLEdBQXVCLENBQXZCOztBQUVBLFVBQU1DLFVBQVU7QUFDZEMsY0FBUSxNQUFNLEtBQUtDLE9BQUwsRUFEQTtBQUVkYixlQUFTYyxVQUFVLEtBQUtDLFVBQUwsQ0FBZ0JELE1BQWhCLENBRkw7QUFHZEUsZ0JBQVVGLFVBQVUsS0FBS0UsUUFBTCxDQUFjRixNQUFkO0FBSE4sS0FBaEI7QUFLQSxVQUFNRyxTQUFTO0FBQ2JDLFdBQUssS0FBS2IsT0FBTCxDQUFhYSxHQUFiLElBQW9CLEVBRFo7QUFFYkMsV0FBSyxLQUFLZCxPQUFMLENBQWFjLEdBQWIsSUFBb0IsQ0FGWjtBQUdiQyw0QkFBc0IsS0FBS2YsT0FBTCxDQUFhZSxvQkFBYixJQUFxQyxLQUg5QztBQUliQyx5QkFBbUIsS0FBS2hCLE9BQUwsQ0FBYWdCLGlCQUFiLElBQWtDLEtBSnhDO0FBS2JDLG9CQUFjO0FBTEQsS0FBZjtBQU9BLFNBQUtDLElBQUwsR0FBWSxzQkFBS0MsVUFBTCxDQUFnQmIsT0FBaEIsRUFBeUJNLE1BQXpCLENBQVo7QUFDRDs7QUFFRCxRQUFNUSxPQUFOLENBQWNDLE9BQWQsRUFBdUI7QUFDckIsVUFBTUMsU0FBUyxLQUFLQyxnQkFBTCxDQUFzQkYsT0FBdEIsQ0FBZjtBQUNBQyxXQUFPRSxJQUFQLENBQWEsNkJBQTRCLEtBQUtwQixJQUFLLE9BQW5EO0FBQ0EsUUFBSTtBQUNGLFlBQU1xQixPQUFPLE1BQU0sS0FBS1AsSUFBTCxDQUFVRSxPQUFWLEVBQW5CO0FBQ0FFLGFBQU9FLElBQVAsQ0FBYSx5QkFBd0JDLEtBQUtyQyxFQUFHLFNBQVEsS0FBS2dCLElBQUssT0FBL0Q7QUFDQXFCLFdBQUtKLE9BQUwsR0FBZUEsT0FBZjtBQUNBLGFBQU9JLElBQVA7QUFDRCxLQUxELENBS0UsT0FBT0MsS0FBUCxFQUFjO0FBQ2RKLGFBQU9JLEtBQVAsQ0FBYyxxQ0FBb0MsS0FBS3RCLElBQUssT0FBNUQsRUFBb0U7QUFDbEVzQixlQUFPQSxNQUFNQyxPQUFOLElBQWlCRDtBQUQwQyxPQUFwRTtBQUdBLFlBQU1BLEtBQU47QUFDRDtBQUNGOztBQUVERSxVQUFRSCxJQUFSLEVBQWM7QUFDWixVQUFNSCxTQUFTLEtBQUtDLGdCQUFMLENBQXNCRSxLQUFLSixPQUEzQixDQUFmO0FBQ0FDLFdBQU9FLElBQVAsQ0FBYSx5QkFBd0JDLEtBQUtyQyxFQUFHLFNBQVEsS0FBS2dCLElBQUssT0FBL0Q7QUFDQSxTQUFLeUIsS0FBTCxDQUFXSixJQUFYO0FBQ0E7QUFDQSxXQUFPQSxLQUFLSixPQUFaO0FBQ0EsU0FBS0gsSUFBTCxDQUFVVSxPQUFWLENBQWtCSCxJQUFsQjtBQUNEOztBQUVEOUIsVUFBUThCLElBQVIsRUFBYztBQUNaLFVBQU1ILFNBQVMsS0FBS0MsZ0JBQUwsQ0FBc0JFLEtBQUtKLE9BQTNCLENBQWY7QUFDQUMsV0FBT0UsSUFBUCxDQUFhLDBCQUF5QkMsS0FBS3JDLEVBQUcsT0FBTSxLQUFLZ0IsSUFBSyxPQUE5RDtBQUNBLFNBQUt5QixLQUFMLENBQVdKLElBQVg7QUFDQTtBQUNBLFdBQU9BLEtBQUtKLE9BQVo7QUFDQSxTQUFLSCxJQUFMLENBQVV2QixPQUFWLENBQWtCOEIsSUFBbEI7QUFDRDs7QUFFRCxRQUFNSyxhQUFOLEdBQXNCO0FBQ3BCLHNCQUFRQyxLQUFSLENBQWUsUUFBTyxLQUFLM0IsSUFBSyxnQkFBaEM7QUFDQSxVQUFNLEtBQUtjLElBQUwsQ0FBVWMsS0FBVixFQUFOO0FBQ0Esc0JBQVFELEtBQVIsQ0FBZSxRQUFPLEtBQUszQixJQUFLLFVBQWhDO0FBQ0EsVUFBTSxLQUFLYyxJQUFMLENBQVVlLEtBQVYsRUFBTjtBQUNBLHNCQUFRRixLQUFSLENBQWUsUUFBTyxLQUFLM0IsSUFBSyxVQUFoQztBQUNEOztBQUVELFFBQU1JLE9BQU4sR0FBZ0I7QUFDZCxTQUFLSCxlQUFMLElBQXdCLENBQXhCO0FBQ0EsVUFBTTZCLE9BQU8sS0FBSzdCLGVBQWxCO0FBQ0Esc0JBQVFtQixJQUFSLENBQWMsUUFBTyxLQUFLcEIsSUFBSyxZQUFXOEIsSUFBSyxhQUEvQztBQUNBLFFBQUlDLG1CQUFtQixLQUF2QjtBQUNBLFFBQUloRCxNQUFKOztBQUVBLFdBQU8sSUFBSWlELE9BQUosQ0FBWSxDQUFDQyxNQUFELEVBQVNDLE1BQVQsS0FBb0I7QUFDckMsVUFBSUMsV0FBVyxLQUFmO0FBQ0EsWUFBTUMsb0JBQW9CLFlBQVk7QUFDcEMsWUFBSSxDQUFDTCxnQkFBTCxFQUF1QjtBQUNyQiw0QkFBUVgsSUFBUixDQUFjLFFBQU8sS0FBS3BCLElBQUssWUFBVzhCLElBQUssWUFBL0M7QUFDQUMsNkJBQW1CLElBQW5CO0FBQ0FoRCxpQkFBT3NELGtCQUFQO0FBQ0EsZ0JBQU1DLG1CQUFtQixJQUFLLEtBQUt6QyxNQUFWLENBQWtCZCxNQUFsQixFQUEwQitDLElBQTFCLENBQXpCO0FBQ0EsY0FBSSxPQUFPUSxpQkFBaUJDLG9CQUF4QixLQUFpRCxVQUFyRCxFQUFpRTtBQUMvRCxnQkFBSTtBQUNGLG9CQUFNRCxpQkFBaUJDLG9CQUFqQixFQUFOO0FBQ0QsYUFGRCxDQUVFLE9BQU9qQixLQUFQLEVBQWM7QUFDZFkscUJBQU9aLEtBQVA7QUFDQTtBQUNEO0FBQ0Y7QUFDRCxlQUFLRyxLQUFMLENBQVdhLGdCQUFYO0FBQ0FILHFCQUFXLElBQVg7QUFDQUYsaUJBQU9LLGdCQUFQO0FBQ0Q7QUFDRixPQWxCRDs7QUFvQkEsVUFBSTtBQUNGLFlBQUksS0FBSzFDLE9BQUwsQ0FBYTRDLFFBQWIsS0FBMEIsSUFBOUIsRUFBb0M7QUFDbEN6RCxtQkFBUyxjQUFJcUIsT0FBSixDQUFZO0FBQ25CcUMsa0JBQU0sS0FBSzdDLE9BQUwsQ0FBYTZDLElBREE7QUFFbkJDLGtCQUFNLEtBQUs5QyxPQUFMLENBQWE4QztBQUZBLFdBQVosRUFHTk4saUJBSE0sQ0FBVDtBQUlELFNBTEQsTUFLTztBQUNMLGdCQUFNTyxhQUFhN0MsT0FBT0MsTUFBUCxDQUFjO0FBQy9CNkMsNEJBQWdCLHVCQURlO0FBRS9CSCxrQkFBTSxLQUFLN0MsT0FBTCxDQUFhNkMsSUFGWTtBQUcvQkMsa0JBQU0sS0FBSzlDLE9BQUwsQ0FBYThDO0FBSFksV0FBZCxFQUloQixLQUFLOUMsT0FBTCxDQUFhK0MsVUFKRyxDQUFuQjtBQUtBNUQsbUJBQVMsY0FBSXFCLE9BQUosQ0FBWXVDLFVBQVosRUFBd0JQLGlCQUF4QixDQUFUO0FBQ0Q7O0FBRURyRCxlQUFPOEQsSUFBUCxDQUFZLE9BQVosRUFBc0J2QixLQUFELElBQVc7QUFDOUIsNEJBQVFBLEtBQVIsQ0FBZSxpQkFBZ0IsS0FBS3RCLElBQUssWUFBVzhCLElBQUssRUFBekQsRUFBNEQ7QUFDMURQLHFCQUFTRCxNQUFNQyxPQUQyQztBQUUxRHVCLG1CQUFPeEIsTUFBTXdCO0FBRjZDLFdBQTVEO0FBSUEsY0FBSSxDQUFDZixnQkFBTCxFQUF1QjtBQUNyQkEsK0JBQW1CLElBQW5CO0FBQ0FoRCxtQkFBT1MsR0FBUDtBQUNBO0FBQ0EsZ0JBQUksQ0FBQzJDLFFBQUwsRUFBZTtBQUNiWSx5QkFBVyxNQUFNYixPQUFPWixLQUFQLENBQWpCLEVBQWdDLElBQWhDO0FBQ0FhLHlCQUFXLElBQVg7QUFDRDtBQUNGO0FBQ0YsU0FkRDtBQWVELE9BOUJELENBOEJFLE9BQU9iLEtBQVAsRUFBYztBQUNkLDBCQUFRQSxLQUFSLENBQWUsaUJBQWdCLEtBQUt0QixJQUFLLEVBQXpDLEVBQTRDO0FBQzFDdUIsbUJBQVNELE1BQU1DLE9BRDJCO0FBRTFDdUIsaUJBQU94QixNQUFNd0I7QUFGNkIsU0FBNUM7QUFJQSxZQUFJLENBQUNYLFFBQUwsRUFBZTtBQUNiRCxpQkFBT1osS0FBUDtBQUNEO0FBQ0Y7QUFDRixLQTdETSxDQUFQO0FBOEREOztBQUVESCxtQkFBaUJGLE9BQWpCLEVBQTBCO0FBQ3hCLFFBQUksS0FBS3JCLE9BQUwsQ0FBYW9ELGlCQUFqQixFQUFvQztBQUNsQyxhQUFPLEtBQUtwRCxPQUFMLENBQWFvRCxpQkFBYixDQUErQi9CLE9BQS9CLHNCQUFQO0FBQ0Q7QUFDRDtBQUNEOztBQUVEUSxRQUFNSixJQUFOLEVBQVk7QUFDVkEsU0FBS2dCLGtCQUFMO0FBQ0FoQixTQUFLcEMsRUFBTCxDQUFRLE9BQVIsRUFBaUJxQyxTQUFTLEtBQUsyQixPQUFMLENBQWE1QixJQUFiLEVBQW1CQyxLQUFuQixDQUExQjtBQUNBRCxTQUFLcEMsRUFBTCxDQUFRLE9BQVIsRUFBaUJxQyxTQUFTLEtBQUs0QixPQUFMLENBQWE3QixJQUFiLEVBQW1CQyxLQUFuQixDQUExQjtBQUNEOztBQUVEMkIsVUFBUTVCLElBQVIsRUFBY0MsS0FBZCxFQUFxQjtBQUNuQixVQUFNSixTQUFTLEtBQUtDLGdCQUFMLENBQXNCRSxLQUFLSixPQUEzQixDQUFmO0FBQ0FDLFdBQU9JLEtBQVAsQ0FBYyxpQkFBZ0IsS0FBS3RCLElBQUssWUFBV3FCLEtBQUtyQyxFQUFHLEVBQTNELEVBQThEO0FBQzVEdUMsZUFBU0QsTUFBTUMsT0FENkM7QUFFNUR1QixhQUFPeEIsTUFBTXdCO0FBRitDLEtBQTlEO0FBSUF6QixTQUFLN0IsR0FBTDtBQUNBLFNBQUtzQixJQUFMLENBQVV2QixPQUFWLENBQWtCOEIsSUFBbEI7QUFDRDs7QUFFRDZCLFVBQVE3QixJQUFSLEVBQWM7QUFDWixVQUFNSCxTQUFTLEtBQUtDLGdCQUFMLENBQXNCRSxLQUFLSixPQUEzQixDQUFmO0FBQ0FDLFdBQU9FLElBQVAsQ0FBYSxRQUFPLEtBQUtwQixJQUFLLFlBQVdxQixLQUFLckMsRUFBRyxTQUFqRDtBQUNEOztBQUVEdUIsV0FBU2MsSUFBVCxFQUFlO0FBQ2IsV0FBTyxJQUFJVyxPQUFKLENBQWFDLE1BQUQsSUFBWTtBQUM3QixVQUFJLE9BQU9aLEtBQUtkLFFBQVosS0FBeUIsVUFBN0IsRUFBeUM7QUFDdkN5QixnQkFBUW1CLE9BQVIsQ0FBZ0I5QixLQUFLZCxRQUFMLEVBQWhCLEVBQ0c2QyxJQURILENBQ1FDLFdBQVdwQixPQUFPb0IsT0FBUCxDQURuQjtBQUVELE9BSEQsTUFHTztBQUNMLFlBQUloQyxLQUFLNUIsVUFBTCxLQUFvQixNQUF4QixFQUFnQztBQUM5QndDLGlCQUFPLElBQVA7QUFDQTtBQUNEO0FBQ0QsMEJBQVFYLEtBQVIsQ0FBZSw4QkFBNkIsS0FBS3RCLElBQUssWUFBV3FCLEtBQUtyQyxFQUFHLEVBQXpFO0FBQ0FpRCxlQUFPLEtBQVA7QUFDRDtBQUNGLEtBWk0sQ0FBUDtBQWFEOztBQUVEM0IsYUFBV2UsSUFBWCxFQUFpQjtBQUNmLFdBQU8sSUFBSVcsT0FBSixDQUFZLENBQUNDLE1BQUQsRUFBU0MsTUFBVCxLQUFvQjtBQUNyQyxVQUFJO0FBQ0YsMEJBQVFQLEtBQVIsQ0FBZSxRQUFPLEtBQUszQixJQUFLLFlBQVdxQixLQUFLckMsRUFBRyxVQUFuRDtBQUNBcUMsYUFBSzlCLE9BQUw7QUFDQTBDO0FBQ0QsT0FKRCxDQUlFLE9BQU9YLEtBQVAsRUFBYztBQUNkWSxlQUFPWixLQUFQO0FBQ0Q7QUFDRixLQVJNLENBQVA7QUFTRDtBQTdMMEI7O2tCQUFSNUIsTztBQWdNckJBLFFBQVFiLG1CQUFSLEdBQThCQSxtQkFBOUIiLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdGxzIGZyb20gJ3Rscyc7XG5pbXBvcnQgbmV0IGZyb20gJ25ldCc7XG5pbXBvcnQgd2luc3RvbiBmcm9tICd3aW5zdG9uJztcbmltcG9ydCBwb29sIGZyb20gJ2dlbmVyaWMtcG9vbCc7XG5pbXBvcnQgeyBFdmVudEVtaXR0ZXIgfSBmcm9tICdldmVudHMnO1xuXG5jbGFzcyBDb25uZWN0aW9uSW50ZXJmYWNlIGV4dGVuZHMgRXZlbnRFbWl0dGVyIHtcbiAgY29uc3RydWN0b3Ioc29ja2V0LCBpZCkge1xuICAgIHN1cGVyKCk7XG4gICAgdGhpcy5pZCA9IGlkO1xuICAgIHRoaXMuc29ja2V0ID0gc29ja2V0O1xuICAgIHRoaXMuc29ja2V0Lm9uKCdlcnJvcicsIGUgPT4gdGhpcy5lbWl0KCdlcnJvcicsIGUpKTtcbiAgICB0aGlzLnNvY2tldC5vbignY2xvc2UnLCBlID0+IHRoaXMuZW1pdCgnY2xvc2UnLCBlKSk7XG4gIH1cblxuICBzZW5kKGJ1ZmZlcikge1xuICAgIHRoaXMuc29ja2V0LndyaXRlKGJ1ZmZlcik7XG4gIH1cblxuICBkZXN0cm95KCkge1xuICAgIHRoaXMuc29ja2V0LmRlc3Ryb3koKTtcbiAgfVxuXG4gIGVuZCgpIHtcbiAgICB0aGlzLnNvY2tldC5lbmQoKTtcbiAgfVxuXG4gIGdldCByZWFkeVN0YXRlKCkge1xuICAgIHJldHVybiB0aGlzLnNvY2tldC5yZWFkeVN0YXRlO1xuICB9XG59XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFRjcFBvb2wge1xuICBjb25zdHJ1Y3RvcihpbnRlcmZhY2VDb25zdHJ1Y3Rvciwgb3B0aW9ucykge1xuICAgIHRoaXMuUGFyc2VyID0gaW50ZXJmYWNlQ29uc3RydWN0b3I7XG4gICAgdGhpcy5vcHRpb25zID0gT2JqZWN0LmFzc2lnbih7fSwgb3B0aW9ucyk7XG4gICAgdGhpcy5uYW1lID0gdGhpcy5vcHRpb25zLm5hbWUgfHwgYCR7aW50ZXJmYWNlQ29uc3RydWN0b3IubmFtZX0gQ29ubmVjdGlvbiBQb29sYDtcbiAgICB0aGlzLmNvbm5lY3Rpb25Db3VudCA9IDA7XG5cbiAgICBjb25zdCBmYWN0b3J5ID0ge1xuICAgICAgY3JlYXRlOiAoKSA9PiB0aGlzLmNvbm5lY3QoKSxcbiAgICAgIGRlc3Ryb3k6IGNsaWVudCA9PiB0aGlzLmRpc2Nvbm5lY3QoY2xpZW50KSxcbiAgICAgIHZhbGlkYXRlOiBjbGllbnQgPT4gdGhpcy52YWxpZGF0ZShjbGllbnQpLFxuICAgIH07XG4gICAgY29uc3QgY29uZmlnID0ge1xuICAgICAgbWF4OiB0aGlzLm9wdGlvbnMubWF4IHx8IDEwLFxuICAgICAgbWluOiB0aGlzLm9wdGlvbnMubWluIHx8IDEsXG4gICAgICBhY3F1aXJlVGltZW91dE1pbGxpczogdGhpcy5vcHRpb25zLmFjcXVpcmVUaW1lb3V0TWlsbGlzIHx8IDE1MDAwLFxuICAgICAgaWRsZVRpbWVvdXRNaWxsaXM6IHRoaXMub3B0aW9ucy5pZGxlVGltZW91dE1pbGxpcyB8fCAzMDAwMCxcbiAgICAgIHRlc3RPbkJvcnJvdzogdHJ1ZSxcbiAgICB9O1xuICAgIHRoaXMucG9vbCA9IHBvb2wuY3JlYXRlUG9vbChmYWN0b3J5LCBjb25maWcpO1xuICB9XG5cbiAgYXN5bmMgYWNxdWlyZShjb250ZXh0KSB7XG4gICAgY29uc3QgbG9nZ2VyID0gdGhpcy5sb2dnZXJGb3JDb250ZXh0KGNvbnRleHQpO1xuICAgIGxvZ2dlci5pbmZvKGBBY3F1aXJpbmcgY29ubmVjdGlvbiBmcm9tICR7dGhpcy5uYW1lfSBwb29sYCk7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGNvbm4gPSBhd2FpdCB0aGlzLnBvb2wuYWNxdWlyZSgpO1xuICAgICAgbG9nZ2VyLmluZm8oYFJldHVybmluZyBjb25uZWN0aW9uICMke2Nvbm4uaWR9IGZyb20gJHt0aGlzLm5hbWV9IHBvb2xgKTtcbiAgICAgIGNvbm4uY29udGV4dCA9IGNvbnRleHQ7XG4gICAgICByZXR1cm4gY29ubjtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKGBGYWlsZWQgdG8gYWNxdWlyZSBjb25uZWN0aW9uIGZyb20gJHt0aGlzLm5hbWV9IHBvb2xgLCB7XG4gICAgICAgIGVycm9yOiBlcnJvci5tZXNzYWdlIHx8IGVycm9yLFxuICAgICAgfSk7XG4gICAgICB0aHJvdyBlcnJvcjtcbiAgICB9XG4gIH1cblxuICByZWxlYXNlKGNvbm4pIHtcbiAgICBjb25zdCBsb2dnZXIgPSB0aGlzLmxvZ2dlckZvckNvbnRleHQoY29ubi5jb250ZXh0KTtcbiAgICBsb2dnZXIuaW5mbyhgUmVsZWFzaW5nIGNvbm5lY3Rpb24gIyR7Y29ubi5pZH0gaW50byAke3RoaXMubmFtZX0gcG9vbGApO1xuICAgIHRoaXMucmVzZXQoY29ubik7XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXBhcmFtLXJlYXNzaWduXG4gICAgZGVsZXRlIGNvbm4uY29udGV4dDtcbiAgICB0aGlzLnBvb2wucmVsZWFzZShjb25uKTtcbiAgfVxuXG4gIGRlc3Ryb3koY29ubikge1xuICAgIGNvbnN0IGxvZ2dlciA9IHRoaXMubG9nZ2VyRm9yQ29udGV4dChjb25uLmNvbnRleHQpO1xuICAgIGxvZ2dlci5pbmZvKGBEZXN0cm95aW5nIGNvbm5lY3Rpb24gIyR7Y29ubi5pZH0gb2YgJHt0aGlzLm5hbWV9IHBvb2xgKTtcbiAgICB0aGlzLnJlc2V0KGNvbm4pO1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1wYXJhbS1yZWFzc2lnblxuICAgIGRlbGV0ZSBjb25uLmNvbnRleHQ7XG4gICAgdGhpcy5wb29sLmRlc3Ryb3koY29ubik7XG4gIH1cblxuICBhc3luYyBkZXN0cm95QWxsTm93KCkge1xuICAgIHdpbnN0b24uZGVidWcoYFBvb2wgJHt0aGlzLm5hbWV9IHNodXR0aW5nIGRvd25gKTtcbiAgICBhd2FpdCB0aGlzLnBvb2wuZHJhaW4oKTtcbiAgICB3aW5zdG9uLmRlYnVnKGBQb29sICR7dGhpcy5uYW1lfSBkcmFpbmVkYCk7XG4gICAgYXdhaXQgdGhpcy5wb29sLmNsZWFyKCk7XG4gICAgd2luc3Rvbi5kZWJ1ZyhgUG9vbCAke3RoaXMubmFtZX0gY2xlYXJlZGApO1xuICB9XG5cbiAgYXN5bmMgY29ubmVjdCgpIHtcbiAgICB0aGlzLmNvbm5lY3Rpb25Db3VudCArPSAxO1xuICAgIGNvbnN0IG15SWQgPSB0aGlzLmNvbm5lY3Rpb25Db3VudDtcbiAgICB3aW5zdG9uLmluZm8oYFBvb2wgJHt0aGlzLm5hbWV9IHNvY2tldCAjJHtteUlkfSBjb25uZWN0aW5nYCk7XG4gICAgbGV0IGF0dGVtcHRDb21wbGV0ZWQgPSBmYWxzZTtcbiAgICBsZXQgc29ja2V0O1xuXG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKChhY2NlcHQsIHJlamVjdCkgPT4ge1xuICAgICAgbGV0IHJlc29sdmVkID0gZmFsc2U7XG4gICAgICBjb25zdCBjb25uZWN0aW9uSGFuZGxlciA9IGFzeW5jICgpID0+IHtcbiAgICAgICAgaWYgKCFhdHRlbXB0Q29tcGxldGVkKSB7XG4gICAgICAgICAgd2luc3Rvbi5pbmZvKGBQb29sICR7dGhpcy5uYW1lfSBzb2NrZXQgIyR7bXlJZH0gY29ubmVjdGVkYCk7XG4gICAgICAgICAgYXR0ZW1wdENvbXBsZXRlZCA9IHRydWU7XG4gICAgICAgICAgc29ja2V0LnJlbW92ZUFsbExpc3RlbmVycygpO1xuICAgICAgICAgIGNvbnN0IGNvbm5lY3Rpb25QYXJzZXIgPSBuZXcgKHRoaXMuUGFyc2VyKShzb2NrZXQsIG15SWQpO1xuICAgICAgICAgIGlmICh0eXBlb2YgY29ubmVjdGlvblBhcnNlci5pbml0aWFsaXplQ29ubmVjdGlvbiA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgYXdhaXQgY29ubmVjdGlvblBhcnNlci5pbml0aWFsaXplQ29ubmVjdGlvbigpO1xuICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgcmVqZWN0KGVycm9yKTtcbiAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICB0aGlzLnJlc2V0KGNvbm5lY3Rpb25QYXJzZXIpO1xuICAgICAgICAgIHJlc29sdmVkID0gdHJ1ZTtcbiAgICAgICAgICBhY2NlcHQoY29ubmVjdGlvblBhcnNlcik7XG4gICAgICAgIH1cbiAgICAgIH07XG5cbiAgICAgIHRyeSB7XG4gICAgICAgIGlmICh0aGlzLm9wdGlvbnMuaW5zZWN1cmUgPT09IHRydWUpIHtcbiAgICAgICAgICBzb2NrZXQgPSBuZXQuY29ubmVjdCh7XG4gICAgICAgICAgICBob3N0OiB0aGlzLm9wdGlvbnMuaG9zdCxcbiAgICAgICAgICAgIHBvcnQ6IHRoaXMub3B0aW9ucy5wb3J0LFxuICAgICAgICAgIH0sIGNvbm5lY3Rpb25IYW5kbGVyKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjb25zdCB0bHNPcHRpb25zID0gT2JqZWN0LmFzc2lnbih7XG4gICAgICAgICAgICBzZWN1cmVQcm90b2NvbDogJ1RMU3YxXzJfY2xpZW50X21ldGhvZCcsXG4gICAgICAgICAgICBob3N0OiB0aGlzLm9wdGlvbnMuaG9zdCxcbiAgICAgICAgICAgIHBvcnQ6IHRoaXMub3B0aW9ucy5wb3J0LFxuICAgICAgICAgIH0sIHRoaXMub3B0aW9ucy50bHNPcHRpb25zKTtcbiAgICAgICAgICBzb2NrZXQgPSB0bHMuY29ubmVjdCh0bHNPcHRpb25zLCBjb25uZWN0aW9uSGFuZGxlcik7XG4gICAgICAgIH1cblxuICAgICAgICBzb2NrZXQub25jZSgnZXJyb3InLCAoZXJyb3IpID0+IHtcbiAgICAgICAgICB3aW5zdG9uLmVycm9yKGBFcnJvciBvbiBQb29sICR7dGhpcy5uYW1lfSBzb2NrZXQgIyR7bXlJZH1gLCB7XG4gICAgICAgICAgICBtZXNzYWdlOiBlcnJvci5tZXNzYWdlLFxuICAgICAgICAgICAgc3RhY2s6IGVycm9yLnN0YWNrLFxuICAgICAgICAgIH0pO1xuICAgICAgICAgIGlmICghYXR0ZW1wdENvbXBsZXRlZCkge1xuICAgICAgICAgICAgYXR0ZW1wdENvbXBsZXRlZCA9IHRydWU7XG4gICAgICAgICAgICBzb2NrZXQuZW5kKCk7XG4gICAgICAgICAgICAvLyBSZWplY3QgYWZ0ZXIgYSBzZWNvbmQgdG8gZ2l2ZSBzb21lIGJhY2tvZmYgdGltZVxuICAgICAgICAgICAgaWYgKCFyZXNvbHZlZCkge1xuICAgICAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHJlamVjdChlcnJvciksIDEwMDApO1xuICAgICAgICAgICAgICByZXNvbHZlZCA9IHRydWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIHdpbnN0b24uZXJyb3IoYEVycm9yIG9uIFBvb2wgJHt0aGlzLm5hbWV9YCwge1xuICAgICAgICAgIG1lc3NhZ2U6IGVycm9yLm1lc3NhZ2UsXG4gICAgICAgICAgc3RhY2s6IGVycm9yLnN0YWNrLFxuICAgICAgICB9KTtcbiAgICAgICAgaWYgKCFyZXNvbHZlZCkge1xuICAgICAgICAgIHJlamVjdChlcnJvcik7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIGxvZ2dlckZvckNvbnRleHQoY29udGV4dCkge1xuICAgIGlmICh0aGlzLm9wdGlvbnMubG9nZ2VyRnJvbUNvbnRleHQpIHtcbiAgICAgIHJldHVybiB0aGlzLm9wdGlvbnMubG9nZ2VyRnJvbUNvbnRleHQoY29udGV4dCkgfHwgd2luc3RvbjtcbiAgICB9XG4gICAgcmV0dXJuIHdpbnN0b247XG4gIH1cblxuICByZXNldChjb25uKSB7XG4gICAgY29ubi5yZW1vdmVBbGxMaXN0ZW5lcnMoKTtcbiAgICBjb25uLm9uKCdlcnJvcicsIGVycm9yID0+IHRoaXMub25FcnJvcihjb25uLCBlcnJvcikpO1xuICAgIGNvbm4ub24oJ2Nsb3NlJywgZXJyb3IgPT4gdGhpcy5vbkNsb3NlKGNvbm4sIGVycm9yKSk7XG4gIH1cblxuICBvbkVycm9yKGNvbm4sIGVycm9yKSB7XG4gICAgY29uc3QgbG9nZ2VyID0gdGhpcy5sb2dnZXJGb3JDb250ZXh0KGNvbm4uY29udGV4dCk7XG4gICAgbG9nZ2VyLmVycm9yKGBFcnJvciBvbiBQb29sICR7dGhpcy5uYW1lfSBzb2NrZXQgIyR7Y29ubi5pZH1gLCB7XG4gICAgICBtZXNzYWdlOiBlcnJvci5tZXNzYWdlLFxuICAgICAgc3RhY2s6IGVycm9yLnN0YWNrLFxuICAgIH0pO1xuICAgIGNvbm4uZW5kKCk7XG4gICAgdGhpcy5wb29sLmRlc3Ryb3koY29ubik7XG4gIH1cblxuICBvbkNsb3NlKGNvbm4pIHtcbiAgICBjb25zdCBsb2dnZXIgPSB0aGlzLmxvZ2dlckZvckNvbnRleHQoY29ubi5jb250ZXh0KTtcbiAgICBsb2dnZXIuaW5mbyhgUG9vbCAke3RoaXMubmFtZX0gc29ja2V0ICMke2Nvbm4uaWR9IGNsb3NlZGApO1xuICB9XG5cbiAgdmFsaWRhdGUoY29ubikge1xuICAgIHJldHVybiBuZXcgUHJvbWlzZSgoYWNjZXB0KSA9PiB7XG4gICAgICBpZiAodHlwZW9mIGNvbm4udmFsaWRhdGUgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgUHJvbWlzZS5yZXNvbHZlKGNvbm4udmFsaWRhdGUoKSlcbiAgICAgICAgICAudGhlbihpc1ZhbGlkID0+IGFjY2VwdChpc1ZhbGlkKSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBpZiAoY29ubi5yZWFkeVN0YXRlID09PSAnb3BlbicpIHtcbiAgICAgICAgICBhY2NlcHQodHJ1ZSk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIHdpbnN0b24uZXJyb3IoYEludmFsaWQgY29ubmVjdGlvbiBpbiBQb29sICR7dGhpcy5uYW1lfSBzb2NrZXQgIyR7Y29ubi5pZH1gKTtcbiAgICAgICAgYWNjZXB0KGZhbHNlKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIGRpc2Nvbm5lY3QoY29ubikge1xuICAgIHJldHVybiBuZXcgUHJvbWlzZSgoYWNjZXB0LCByZWplY3QpID0+IHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHdpbnN0b24uZGVidWcoYFBvb2wgJHt0aGlzLm5hbWV9IHNvY2tldCAjJHtjb25uLmlkfSBjbG9zaW5nYCk7XG4gICAgICAgIGNvbm4uZGVzdHJveSgpO1xuICAgICAgICBhY2NlcHQoKTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIHJlamVjdChlcnJvcik7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbn1cblxuVGNwUG9vbC5Db25uZWN0aW9uSW50ZXJmYWNlID0gQ29ubmVjdGlvbkludGVyZmFjZTtcbiJdfQ==
\No newline at end of file