UNPKG

36.6 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.default = void 0;
7
8require("@babel/polyfill");
9
10var _lodash = _interopRequireDefault(require("lodash"));
11
12var _compressionUtils = require("./compressionUtils");
13
14var _asyncUtils = require("./asyncUtils");
15
16var _System = _interopRequireDefault(require("./System"));
17
18var _Module = _interopRequireDefault(require("./Module"));
19
20var _errorUtils = require("./errorUtils");
21
22function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
23
24function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
25
26function _awaitAsyncGenerator(value) { return new _AwaitValue(value); }
27
28function _wrapAsyncGenerator(fn) { return function () { return new _AsyncGenerator(fn.apply(this, arguments)); }; }
29
30function _AsyncGenerator(gen) { var front, back; function send(key, arg) { return new Promise(function (resolve, reject) { var request = { key: key, arg: arg, resolve: resolve, reject: reject, next: null }; if (back) { back = back.next = request; } else { front = back = request; resume(key, arg); } }); } function resume(key, arg) { try { var result = gen[key](arg); var value = result.value; var wrappedAwait = value instanceof _AwaitValue; Promise.resolve(wrappedAwait ? value.wrapped : value).then(function (arg) { if (wrappedAwait) { resume("next", arg); return; } settle(result.done ? "return" : "normal", arg); }, function (err) { resume("throw", err); }); } catch (err) { settle("throw", err); } } function settle(type, value) { switch (type) { case "return": front.resolve({ value: value, done: true }); break; case "throw": front.reject(value); break; default: front.resolve({ value: value, done: false }); break; } front = front.next; if (front) { resume(front.key, front.arg); } else { back = null; } } this._invoke = send; if (typeof gen.return !== "function") { this.return = undefined; } }
31
32if (typeof Symbol === "function" && Symbol.asyncIterator) { _AsyncGenerator.prototype[Symbol.asyncIterator] = function () { return this; }; }
33
34_AsyncGenerator.prototype.next = function (arg) { return this._invoke("next", arg); };
35
36_AsyncGenerator.prototype.throw = function (arg) { return this._invoke("throw", arg); };
37
38_AsyncGenerator.prototype.return = function (arg) { return this._invoke("return", arg); };
39
40function _AwaitValue(value) { this.wrapped = value; }
41
42function _asyncIterator(iterable) { var method; if (typeof Symbol === "function") { if (Symbol.asyncIterator) { method = iterable[Symbol.asyncIterator]; if (method != null) return method.call(iterable); } if (Symbol.iterator) { method = iterable[Symbol.iterator]; if (method != null) return method.call(iterable); } } throw new TypeError("Object is not async iterable"); }
43
44const {
45 log,
46 warn,
47 error,
48 noteGauge,
49 noteCount,
50 noteTimer,
51 trackOp
52} = new _Module.default(__filename); // eslint-disable-line no-unused-vars
53
54const MAX_PARTITION_CACHE_SIZE = 1000;
55
56class AthenaClient {
57 constructor(dependencies) {
58 _defineProperty(this, "_awsClient", void 0);
59
60 _defineProperty(this, "_addedPartitions", void 0);
61
62 _defineProperty(this, "writeAthenaFile", async (items, options) => {
63 await Promise.all([// persist data to S3
64 (async () => {
65 const gzippedBody = await (0, _compressionUtils.gzipCompress)(items.map(item => JSON.stringify(item)).join('\n'));
66 await this._awsClient.runS3(s3 => s3.putObject({
67 Bucket: options.bucket,
68 Key: `${options.dirPath}/${options.fileName}.jsons.gz`,
69 Body: gzippedBody,
70 ContentType: 'text/plain',
71 ContentEncoding: 'gzip',
72 Metadata: {
73 'x-amz-meta-items': items.length.toString()
74 }
75 }), {
76 noLog: true
77 });
78 })(), // create athena partition
79 (async () => {
80 const athenaFullTableName = options.athenaFullTableName;
81 if (!athenaFullTableName) return;
82 const partitionDesc = options.dirPath.split('/').map(part => part.split('=')).filter(partParts => partParts.length === 2).map(([field, value]) => `${field} = '${value}'`).join(', ');
83
84 const existingPartition = this._addedPartitions.find(p => p === partitionDesc);
85
86 if (!existingPartition) {
87 const query = `ALTER TABLE ${athenaFullTableName} ADD IF NOT EXISTS PARTITION (${partitionDesc}) LOCATION 's3://${options.bucket}/${options.dirPath}';`;
88 await this.executeAthenaQuery(query, 'add-athena-partition', {
89 dontWaitForCompletion: true,
90 noLog: true
91 }); // maintain partition cache
92
93 this._addedPartitions.unshift(partitionDesc);
94
95 if (this._addedPartitions.length > MAX_PARTITION_CACHE_SIZE) this._addedPartitions.pop();
96 }
97 })()]);
98 });
99
100 _defineProperty(this, "executeAthenaQuery", async (query, queryName, options) => {
101 options = options || {};
102 const opts = {
103 dontWaitForCompletion: options.dontWaitForCompletion || false,
104 maxResults: options.maxResults || 0,
105 noLog: options.noLog || false
106 };
107 return await trackOp(async () => {
108 try {
109 let results = [];
110 const asyncIterator = await this.executeAthenaQueryAndGetResultIterator(query, queryName);
111 if (opts.dontWaitForCompletion) return [];
112 var _iteratorNormalCompletion = true;
113 var _didIteratorError = false;
114
115 var _iteratorError;
116
117 try {
118 for (var _iterator = _asyncIterator(asyncIterator), _step, _value; _step = await _iterator.next(), _iteratorNormalCompletion = _step.done, _value = await _step.value, !_iteratorNormalCompletion; _iteratorNormalCompletion = true) {
119 const items = _value;
120 const reachedMaxResults = opts.maxResults > 0 && results.length + items.length > opts.maxResults;
121 const itemsToAdd = reachedMaxResults ? items.slice(0, opts.maxResults - results.length) : items;
122 results = [...results, ...itemsToAdd];
123 if (reachedMaxResults) break;
124 }
125 } catch (err) {
126 _didIteratorError = true;
127 _iteratorError = err;
128 } finally {
129 try {
130 if (!_iteratorNormalCompletion && _iterator.return != null) {
131 await _iterator.return();
132 }
133 } finally {
134 if (_didIteratorError) {
135 throw _iteratorError;
136 }
137 }
138 }
139
140 return results;
141 } catch (err) {
142 error('Athena query execution failed', {
143 queryName: queryName,
144 query: query,
145 err
146 });
147 throw err;
148 }
149 }, `athena-query-${queryName}`, null, {
150 log: !opts.noLog
151 });
152 });
153
154 _defineProperty(this, "executeAthenaQueryAndGetResultIterator", async (query, queryName) => {
155 try {
156 const awsConfig = _System.default.getConfig().aws;
157
158 if (!awsConfig) throw new Error('aws config not set in system config');
159 const startMS = Date.now();
160 const OUTPUT_LOCATION = `s3://aws-athena-query-results-${awsConfig.accountId || (0, _errorUtils.throwUnsetArg)('systemConfig.aws.accountId')}-${awsConfig.defaultRegion || (0, _errorUtils.throwUnsetArg)('systemConfig.aws.defaultRegion')}/${_System.default.getConfig().env}/${queryName}/`; // execute query
161
162 const startRes = await this._awsClient.runAthena(athena => athena.startQueryExecution({
163 QueryString: query,
164 ResultConfiguration: {
165 OutputLocation: OUTPUT_LOCATION
166 }
167 }));
168 return iterateAthenaQueryResults(this._awsClient, startRes.result.QueryExecutionId, queryName, query, startMS);
169 } catch (err) {
170 error('Athena query execution failed', {
171 queryName: queryName,
172 query: query,
173 err
174 });
175 throw err;
176 }
177 });
178
179 this._awsClient = dependencies.awsClient;
180 this._addedPartitions = [];
181 }
182
183}
184
185exports.default = AthenaClient;
186
187function iterateAthenaQueryResults(_x, _x2, _x3, _x4, _x5) {
188 return _iterateAthenaQueryResults.apply(this, arguments);
189}
190
191function _iterateAthenaQueryResults() {
192 _iterateAthenaQueryResults = _wrapAsyncGenerator(function* (awsClient, queryExecutionId, queryName, query, startMS) {
193 try {
194 // wait for query execution to end
195 let dataScannedInBytes = 0;
196
197 while (true) {
198 const statusRes = yield _awaitAsyncGenerator(awsClient.runAthena(athena => athena.getQueryExecution({
199 QueryExecutionId: queryExecutionId
200 })));
201 const {
202 State,
203 StateChangeReason
204 } = statusRes.result.QueryExecution.Status;
205
206 if (State === 'SUCCEEDED') {
207 dataScannedInBytes = _lodash.default.get(statusRes.result.QueryExecution, 'Statistics.DataScannedInBytes');
208 break;
209 } else if (['QUEUED', 'RUNNING'].includes(State)) {
210 yield _awaitAsyncGenerator((0, _asyncUtils.sleep)(500));
211 } else {
212 throw new _errorUtils.ExtendedError('Athena query failed', {
213 queryName: queryName,
214 state: State,
215 reason: StateChangeReason,
216 query: query
217 });
218 }
219 }
220
221 const kbScanned = Math.round(dataScannedInBytes / 1000);
222 const kbCharged = Math.max(kbScanned, 10 * 1000); // Athena charges a minimum of 10mb for every query
223
224 const costDollar = 5 * kbCharged / 1000000000; // Athena charges 5$ for a TB of data
225
226 noteCount(`${queryName}.kbScanned`, kbScanned);
227 noteCount(`${queryName}.kbCharged`, kbCharged);
228 noteCount(`${queryName}.dollarsCharged`, costDollar); // fetch response
229
230 let nextToken = null;
231
232 do {
233 const resultsRes = yield _awaitAsyncGenerator(awsClient.runAthena(athena => athena.getQueryResults({
234 QueryExecutionId: queryExecutionId,
235 MaxResults: Infinity,
236 NextToken: nextToken
237 })));
238 const athenaResults = getAthenaQueryResultLines(resultsRes.result);
239 const newResultObjects = csvArrayToObjects(athenaResults);
240 yield newResultObjects;
241 nextToken = resultsRes.result.NextToken;
242 } while (nextToken);
243
244 const durationMS = Date.now() - startMS; // log(`Athena query complete`, { queryName, kbScanned, kbCharged, costDollar, resultsLoaded: resultObjects.length, durationMS: durationMS })
245
246 noteTimer(`${queryName}.duration`, durationMS);
247 } catch (err) {
248 error('Athena query execution failed', {
249 queryName: queryName,
250 query: query,
251 err
252 });
253 throw err;
254 }
255 });
256 return _iterateAthenaQueryResults.apply(this, arguments);
257}
258
259function stripQuotes(input) {
260 if (typeof input !== 'string') return input;
261 if (input === '') return null;
262 const length = input.length;
263 if (length < 2 || !input.startsWith('"') || !input.endsWith('"')) return input;
264 return input.substr(1, length - 2);
265}
266
267function getAthenaQueryResultLines(res) {
268 const rolloutLines = [];
269 const numericTypeNames = ['integer', 'bigint', 'double'];
270 const columnMetas = res.ResultSet.ResultSetMetadata.ColumnInfo;
271 rolloutLines.push(columnMetas.map(cm => cm.Name));
272 res.ResultSet.Rows.splice(1) // skip header
273 .forEach(row => rolloutLines.push(row.Data.map((datum, i) => numericTypeNames.includes(columnMetas[i].Type) ? datum.VarCharValue === undefined ? null : Number(datum.VarCharValue) : datum.VarCharValue === undefined ? null : `"${datum.VarCharValue}"`)));
274 const csvContent = rolloutLines;
275 return csvContent;
276}
277
278function csvArrayToObjects(csvArray) {
279 const propNames = csvArray[0];
280 const valueLines = csvArray.slice(1);
281 const objects = valueLines.map(valueLine => {
282 const obj = {};
283 valueLine.forEach((value, i) => obj[propNames[i]] = stripQuotes(value));
284 return obj;
285 });
286 return objects;
287}
288//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9BdGhlbmFDbGllbnQuanMiXSwibmFtZXMiOlsibG9nIiwid2FybiIsImVycm9yIiwibm90ZUdhdWdlIiwibm90ZUNvdW50Iiwibm90ZVRpbWVyIiwidHJhY2tPcCIsIk1vZHVsZSIsIl9fZmlsZW5hbWUiLCJNQVhfUEFSVElUSU9OX0NBQ0hFX1NJWkUiLCJBdGhlbmFDbGllbnQiLCJjb25zdHJ1Y3RvciIsImRlcGVuZGVuY2llcyIsIml0ZW1zIiwib3B0aW9ucyIsIlByb21pc2UiLCJhbGwiLCJnemlwcGVkQm9keSIsIm1hcCIsIml0ZW0iLCJKU09OIiwic3RyaW5naWZ5Iiwiam9pbiIsIl9hd3NDbGllbnQiLCJydW5TMyIsInMzIiwicHV0T2JqZWN0IiwiQnVja2V0IiwiYnVja2V0IiwiS2V5IiwiZGlyUGF0aCIsImZpbGVOYW1lIiwiQm9keSIsIkNvbnRlbnRUeXBlIiwiQ29udGVudEVuY29kaW5nIiwiTWV0YWRhdGEiLCJsZW5ndGgiLCJ0b1N0cmluZyIsIm5vTG9nIiwiYXRoZW5hRnVsbFRhYmxlTmFtZSIsInBhcnRpdGlvbkRlc2MiLCJzcGxpdCIsInBhcnQiLCJmaWx0ZXIiLCJwYXJ0UGFydHMiLCJmaWVsZCIsInZhbHVlIiwiZXhpc3RpbmdQYXJ0aXRpb24iLCJfYWRkZWRQYXJ0aXRpb25zIiwiZmluZCIsInAiLCJxdWVyeSIsImV4ZWN1dGVBdGhlbmFRdWVyeSIsImRvbnRXYWl0Rm9yQ29tcGxldGlvbiIsInVuc2hpZnQiLCJwb3AiLCJxdWVyeU5hbWUiLCJvcHRzIiwibWF4UmVzdWx0cyIsInJlc3VsdHMiLCJhc3luY0l0ZXJhdG9yIiwiZXhlY3V0ZUF0aGVuYVF1ZXJ5QW5kR2V0UmVzdWx0SXRlcmF0b3IiLCJyZWFjaGVkTWF4UmVzdWx0cyIsIml0ZW1zVG9BZGQiLCJzbGljZSIsImVyciIsImF3c0NvbmZpZyIsIlN5c3RlbSIsImdldENvbmZpZyIsImF3cyIsIkVycm9yIiwic3RhcnRNUyIsIkRhdGUiLCJub3ciLCJPVVRQVVRfTE9DQVRJT04iLCJhY2NvdW50SWQiLCJkZWZhdWx0UmVnaW9uIiwiZW52Iiwic3RhcnRSZXMiLCJydW5BdGhlbmEiLCJhdGhlbmEiLCJzdGFydFF1ZXJ5RXhlY3V0aW9uIiwiUXVlcnlTdHJpbmciLCJSZXN1bHRDb25maWd1cmF0aW9uIiwiT3V0cHV0TG9jYXRpb24iLCJpdGVyYXRlQXRoZW5hUXVlcnlSZXN1bHRzIiwicmVzdWx0IiwiUXVlcnlFeGVjdXRpb25JZCIsImF3c0NsaWVudCIsInF1ZXJ5RXhlY3V0aW9uSWQiLCJkYXRhU2Nhbm5lZEluQnl0ZXMiLCJzdGF0dXNSZXMiLCJnZXRRdWVyeUV4ZWN1dGlvbiIsIlN0YXRlIiwiU3RhdGVDaGFuZ2VSZWFzb24iLCJRdWVyeUV4ZWN1dGlvbiIsIlN0YXR1cyIsIl8iLCJnZXQiLCJpbmNsdWRlcyIsIkV4dGVuZGVkRXJyb3IiLCJzdGF0ZSIsInJlYXNvbiIsImtiU2Nhbm5lZCIsIk1hdGgiLCJyb3VuZCIsImtiQ2hhcmdlZCIsIm1heCIsImNvc3REb2xsYXIiLCJuZXh0VG9rZW4iLCJyZXN1bHRzUmVzIiwiZ2V0UXVlcnlSZXN1bHRzIiwiTWF4UmVzdWx0cyIsIkluZmluaXR5IiwiTmV4dFRva2VuIiwiYXRoZW5hUmVzdWx0cyIsImdldEF0aGVuYVF1ZXJ5UmVzdWx0TGluZXMiLCJuZXdSZXN1bHRPYmplY3RzIiwiY3N2QXJyYXlUb09iamVjdHMiLCJkdXJhdGlvbk1TIiwic3RyaXBRdW90ZXMiLCJpbnB1dCIsInN0YXJ0c1dpdGgiLCJlbmRzV2l0aCIsInN1YnN0ciIsInJlcyIsInJvbGxvdXRMaW5lcyIsIm51bWVyaWNUeXBlTmFtZXMiLCJjb2x1bW5NZXRhcyIsIlJlc3VsdFNldCIsIlJlc3VsdFNldE1ldGFkYXRhIiwiQ29sdW1uSW5mbyIsInB1c2giLCJjbSIsIk5hbWUiLCJSb3dzIiwic3BsaWNlIiwiZm9yRWFjaCIsInJvdyIsIkRhdGEiLCJkYXR1bSIsImkiLCJUeXBlIiwiVmFyQ2hhclZhbHVlIiwidW5kZWZpbmVkIiwiTnVtYmVyIiwiY3N2Q29udGVudCIsImNzdkFycmF5IiwicHJvcE5hbWVzIiwidmFsdWVMaW5lcyIsIm9iamVjdHMiLCJ2YWx1ZUxpbmUiLCJvYmoiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7QUFFQTs7QUFFQTs7QUFFQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBSUEsTUFBTTtBQUFFQSxFQUFBQSxHQUFGO0FBQU9DLEVBQUFBLElBQVA7QUFBYUMsRUFBQUEsS0FBYjtBQUFvQkMsRUFBQUEsU0FBcEI7QUFBK0JDLEVBQUFBLFNBQS9CO0FBQTBDQyxFQUFBQSxTQUExQztBQUFxREMsRUFBQUE7QUFBckQsSUFBaUUsSUFBSUMsZUFBSixDQUFXQyxVQUFYLENBQXZFLEMsQ0FBOEY7O0FBRTlGLE1BQU1DLHdCQUF3QixHQUFHLElBQWpDOztBQUVlLE1BQU1DLFlBQU4sQ0FBbUI7QUFNaENDLEVBQUFBLFdBQVcsQ0FBQ0MsWUFBRCxFQUF5QztBQUFBOztBQUFBOztBQUFBLDZDQU1sQyxPQUFPQyxLQUFQLEVBQTZCQyxPQUE3QixLQU1JO0FBQ3BCLFlBQU1DLE9BQU8sQ0FBQ0MsR0FBUixDQUFZLENBRWhCO0FBQ0EsT0FBQyxZQUFZO0FBQ1gsY0FBTUMsV0FBVyxHQUFHLE1BQU0sb0NBQWFKLEtBQUssQ0FBQ0ssR0FBTixDQUFVQyxJQUFJLElBQUlDLElBQUksQ0FBQ0MsU0FBTCxDQUFlRixJQUFmLENBQWxCLEVBQXdDRyxJQUF4QyxDQUE2QyxJQUE3QyxDQUFiLENBQTFCO0FBQ0EsY0FBTSxLQUFLQyxVQUFMLENBQWdCQyxLQUFoQixDQUFzQkMsRUFBRSxJQUFJQSxFQUFFLENBQUNDLFNBQUgsQ0FBYTtBQUM3Q0MsVUFBQUEsTUFBTSxFQUFFYixPQUFPLENBQUNjLE1BRDZCO0FBRTdDQyxVQUFBQSxHQUFHLEVBQUcsR0FBRWYsT0FBTyxDQUFDZ0IsT0FBUSxJQUFHaEIsT0FBTyxDQUFDaUIsUUFBUyxXQUZDO0FBRzdDQyxVQUFBQSxJQUFJLEVBQUVmLFdBSHVDO0FBSTdDZ0IsVUFB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R0FBbUMsSUFBbkMsR0FBMENDLE1BQU0sQ0FBQ0wsS0FBSyxDQUFDRyxZQUFQLENBRnJDLEdBR1hILEtBQUssQ0FBQ0csWUFBTixLQUF1QkMsU0FBdkIsR0FBbUMsSUFBbkMsR0FBMkMsSUFBR0osS0FBSyxDQUFDRyxZQUFhLEdBSnRFLENBREssQ0FIWDtBQVlBLFFBQU1HLFVBQVUsR0FBR3BCLFlBQW5CO0FBQ0EsU0FBT29CLFVBQVA7QUFDRDs7QUFFRCxTQUFTNUIsaUJBQVQsQ0FBMkI2QixRQUEzQixFQUEyRDtBQUN6RCxRQUFNQyxTQUFTLEdBQUdELFFBQVEsQ0FBQyxDQUFELENBQTFCO0FBQ0EsUUFBTUUsVUFBVSxHQUFHRixRQUFRLENBQUNuRixLQUFULENBQWUsQ0FBZixDQUFuQjtBQUNBLFFBQU1zRixPQUFPLEdBQUdELFVBQVUsQ0FBQ25JLEdBQVgsQ0FBZXFJLFNBQVMsSUFBSTtBQUMxQyxVQUFNQyxHQUFHLEdBQUcsRUFBWjtBQUNBRCxJQUFBQSxTQUFTLENBQUNkLE9BQVYsQ0FBa0IsQ0FBQzNGLEtBQUQsRUFBUStGLENBQVIsS0FBY1csR0FBRyxDQUFDSixTQUFTLENBQUNQLENBQUQsQ0FBVixDQUFILEdBQW9CckIsV0FBVyxDQUFDMUUsS0FBRCxDQUEvRDtBQUNBLFdBQU8wRyxHQUFQO0FBQ0QsR0FKZSxDQUFoQjtBQUtBLFNBQU9GLE9BQVA7QUFDRCIsInNvdXJjZXNDb250ZW50IjpbIi8vIEBmbG93XG5cbmltcG9ydCBcIkBiYWJlbC9wb2x5ZmlsbFwiXG5cbmltcG9ydCBfIGZyb20gJ2xvZGFzaCdcblxuaW1wb3J0IHsgZ3ppcENvbXByZXNzIH0gZnJvbSAnLi9jb21wcmVzc2lvblV0aWxzJ1xuaW1wb3J0IHsgc2xlZXAgfSBmcm9tICcuL2FzeW5jVXRpbHMnXG5pbXBvcnQgU3lzdGVtIGZyb20gXCIuL1N5c3RlbVwiXG5pbXBvcnQgTW9kdWxlIGZyb20gXCIuL01vZHVsZVwiXG5pbXBvcnQgeyBFeHRlbmRlZEVycm9yLCB0aHJvd1Vuc2V0QXJnIH0gZnJvbSAnLi9lcnJvclV0aWxzJyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIG5vLXVudXNlZC12YXJzXG5cbmltcG9ydCB0eXBlIEFXU0NsaWVudCBmcm9tICcuL0FXU0NsaWVudCdcblxuY29uc3QgeyBsb2csIHdhcm4sIGVycm9yLCBub3RlR2F1Z2UsIG5vdGVDb3VudCwgbm90ZVRpbWVyLCB0cmFja09wIH0gPSBuZXcgTW9kdWxlKF9fZmlsZW5hbWUpIC8vIGVzbGludC1kaXNhYmxlLWxpbmUgbm8tdW51c2VkLXZhcnNcblxuY29uc3QgTUFYX1BBUlRJVElPTl9DQUNIRV9TSVpFID0gMTAwMFxuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBBdGhlbmFDbGllbnQge1xuXG4gIF9hd3NDbGllbnQ6IEFXU0NsaWVudFxuICBfYWRkZWRQYXJ0aXRpb25zOiBBcnJheTxzdHJpbmc+XG5cblxuICBjb25zdHJ1Y3RvcihkZXBlbmRlbmNpZXM6IHsgYXdzQ2xpZW50OiBBV1NDbGllbnQgfSkge1xuICAgIHRoaXMuX2F3c0NsaWVudCA9IGRlcGVuZGVuY2llcy5hd3NDbGllbnRcbiAgICB0aGlzLl9hZGRlZFBhcnRpdGlvbnMgPSBbXVxuICB9XG5cblxuICB3cml0ZUF0aGVuYUZpbGUgPSBhc3luYyAoaXRlbXM6IEFycmF5PE9iamVjdD4sIG9wdGlvbnM6IHt8XG4gICAgYnVja2V0OiBzdHJpbmcsXG4gICAgZGlyUGF0aDogc3RyaW5nLFxuICAgIGZpbGVOYW1lOiBzdHJpbmcsXG4gICAgYXRoZW5hRnVsbFRhYmxlTmFtZT86ID9zdHJpbmcsXG4gICAgbm9Mb2c/OiA/Ym9vbGVhbixcbiAgfH0pOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICBhd2FpdCBQcm9taXNlLmFsbChbXG4gIFxuICAgICAgLy8gcGVyc2lzdCBkYXRhIHRvIFMzXG4gICAgICAoYXN5bmMgKCkgPT4ge1xuICAgICAgICBjb25zdCBnemlwcGVkQm9keSA9IGF3YWl0IGd6aXBDb21wcmVzcyhpdGVtcy5tYXAoaXRlbSA9PiBKU09OLnN0cmluZ2lmeShpdGVtKSkuam9pbignXFxuJykpXG4gICAgICAgIGF3YWl0IHRoaXMuX2F3c0NsaWVudC5ydW5TMyhzMyA9PiBzMy5wdXRPYmplY3Qoe1xuICAgICAgICAgIEJ1Y2tldDogb3B0aW9ucy5idWNrZXQsXG4gICAgICAgICAgS2V5OiBgJHtvcHRpb25zLmRpclBhdGh9LyR7b3B0aW9ucy5maWxlTmFtZX0uanNvbnMuZ3pgLFxuICAgICAgICAgIEJvZHk6IGd6aXBwZWRCb2R5LCBcbiAgICAgICAgICBDb250ZW50VHlwZTogJ3RleHQvcGxhaW4nLFxuICAgICAgICAgIENvbnRlbnRFbmNvZGluZzogJ2d6aXAnLFxuICAgICAgICAgIE1ldGFkYXRhOiB7XG4gICAgICAgICAgICAneC1hbXotbWV0YS1pdGVtcyc6IGl0ZW1zLmxlbmd0aC50b1N0cmluZygpLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0pLCB7XG4gICAgICAgICAgbm9Mb2c6IHRydWUsXG4gICAgICAgIH0pXG4gICAgICB9KSgpLFxuICBcbiAgICAgIC8vIGNyZWF0ZSBhdGhlbmEgcGFydGl0aW9uXG4gICAgICAoYXN5bmMgKCkgPT4ge1xuICAgICAgICBjb25zdCBhdGhlbmFGdWxsVGFibGVOYW1lID0gb3B0aW9ucy5hdGhlbmFGdWxsVGFibGVOYW1lXG4gICAgICAgIGlmICghYXRoZW5hRnVsbFRhYmxlTmFtZSlcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgICBcbiAgICAgICAgY29uc3QgcGFydGl0aW9uRGVzYyA9IG9wdGlvbnMuZGlyUGF0aFxuICAgICAgICAgIC5zcGxpdCgnLycpXG4gICAgICAgICAgLm1hcChwYXJ0ID0+IHBhcnQuc3BsaXQoJz0nKSlcbiAgICAgICAgICAuZmlsdGVyKHBhcnRQYXJ0cyA9PiBwYXJ0UGFydHMubGVuZ3RoID09PSAyKVxuICAgICAgICAgIC5tYXAoKFsgZmllbGQsIHZhbHVlIF0pPT4gYCR7ZmllbGR9ID0gJyR7dmFsdWV9J2ApXG4gICAgICAgICAgLmpvaW4oJywgJylcblxuICAgICAgICBjb25zdCBleGlzdGluZ1BhcnRpdGlvbiA9IHRoaXMuX2FkZGVkUGFydGl0aW9ucy5maW5kKHAgPT4gcCA9PT0gcGFydGl0aW9uRGVzYylcbiAgICAgICAgaWYgKCFleGlzdGluZ1BhcnRpdGlvbikge1xuICAgICAgICAgIGNvbnN0IHF1ZXJ5ID0gYEFMVEVSIFRBQkxFICR7YXRoZW5hRnVsbFRhYmxlTmFtZX0gQUREIElGIE5PVCBFWElTVFMgUEFSVElUSU9OICgke3BhcnRpdGlvbkRlc2N9KSBMT0NBVElPTiAnczM6Ly8ke29wdGlvbnMuYnVja2V0fS8ke29wdGlvbnMuZGlyUGF0aH0nO2AgICAgIFxuICAgICAgICAgIGF3YWl0IHRoaXMuZXhlY3V0ZUF0aGVuYVF1ZXJ5KHF1ZXJ5LCAnYWRkLWF0aGVuYS1wYXJ0aXRpb24nLCB7IGRvbnRXYWl0Rm9yQ29tcGxldGlvbjogdHJ1ZSwgbm9Mb2c6IHRydWUgfSlcbiAgICAgICAgICBcbiAgICAgICAgICAvLyBtYWludGFpbiBwYXJ0aXRpb24gY2FjaGVcbiAgICAgICAgICB0aGlzLl9hZGRlZFBhcnRpdGlvbnMudW5zaGlmdChwYXJ0aXRpb25EZXNjKVxuICAgICAgICAgIGlmICh0aGlzLl9hZGRlZFBhcnRpdGlvbnMubGVuZ3RoID4gTUFYX1BBUlRJVElPTl9DQUNIRV9TSVpFKVxuICAgICAgICAgICAgdGhpcy5fYWRkZWRQYXJ0aXRpb25zLnBvcCgpXG4gICAgICAgIH1cbiAgICAgIH0pKCksXG4gICAgICBcbiAgICBdKVxuICB9XG4gIFxuICBleGVjdXRlQXRoZW5hUXVlcnkgPSBhc3luYyAocXVlcnk6IHN0cmluZywgcXVlcnlOYW1lOiBzdHJpbmcsIG9wdGlvbnM6ID97IGRvbnRXYWl0Rm9yQ29tcGxldGlvbj86IGJvb2xlYW4sIG1heFJlc3VsdHM/OiBudW1iZXIsIG5vTG9nPzogYm9vbGVhbiB9KTogUHJvbWlzZTxBcnJheTxPYmplY3Q+PiA9PiB7XG4gICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge31cbiAgICBjb25zdCBvcHRzID0ge1xuICAgICAgZG9udFdhaXRGb3JDb21wbGV0aW9uOiBvcHRpb25zLmRvbnRXYWl0Rm9yQ29tcGxldGlvbiB8fCBmYWxzZSxcbiAgICAgIG1heFJlc3VsdHM6IG9wdGlvbnMubWF4UmVzdWx0cyB8fCAwLFxuICAgICAgbm9Mb2c6IG9wdGlvbnMubm9Mb2cgfHwgZmFsc2UsXG4gICAgfVxuICAgIFxuICAgIHJldHVybiBhd2FpdCB0cmFja09wKGFzeW5jICgpID0+IHtcbiAgICAgIHRyeSB7ICAgICBcbiAgICAgICAgbGV0IHJlc3VsdHM6IEFycmF5PE9iamVjdD4gPSBbXVxuICAgICAgICBjb25zdCBhc3luY0l0ZXJhdG9yID0gYXdhaXQgdGhpcy5leGVjdXRlQXRoZW5hUXVlcnlBbmRHZXRSZXN1bHRJdGVyYXRvcihxdWVyeSwgcXVlcnlOYW1lKVxuICAgICAgICBpZiAob3B0cy5kb250V2FpdEZvckNvbXBsZXRpb24pXG4gICAgICAgICAgcmV0dXJuIFtdXG4gICAgICAgIGZvciBhd2FpdCAoY29uc3QgaXRlbXMgb2YgYXN5bmNJdGVyYXRvcikge1xuICAgICAgICAgIGNvbnN0IHJlYWNoZWRNYXhSZXN1bHRzID0gb3B0cy5tYXhSZXN1bHRzID4gMCAmJiByZXN1bHRzLmxlbmd0aCArIGl0ZW1zLmxlbmd0aCA+IG9wdHMubWF4UmVzdWx0c1xuICAgICAgICAgIGNvbnN0IGl0ZW1zVG9BZGQgPSByZWFjaGVkTWF4UmVzdWx0cyA/IGl0ZW1zLnNsaWNlKDAsIG9wdHMubWF4UmVzdWx0cyAtIHJlc3VsdHMubGVuZ3RoKSA6IGl0ZW1zXG4gICAgICAgICAgcmVzdWx0cyA9IFsgLi4ucmVzdWx0cywgLi4uaXRlbXNUb0FkZCBdXG4gICAgICAgICAgaWYgKHJlYWNoZWRNYXhSZXN1bHRzKVxuICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcmVzdWx0c1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGVycm9yKCdBdGhlbmEgcXVlcnkgZXhlY3V0aW9uIGZhaWxlZCcsIHsgcXVlcnlOYW1lOiBxdWVyeU5hbWUsIHF1ZXJ5OiBxdWVyeSwgZXJyIH0pXG4gICAgICAgIHRocm93IGVyclxuICAgICAgfVxuICAgIH0sIGBhdGhlbmEtcXVlcnktJHtxdWVyeU5hbWV9YCwgbnVsbCwgeyBsb2c6ICFvcHRzLm5vTG9nIH0pXG4gIH1cbiAgXG4gIGV4ZWN1dGVBdGhlbmFRdWVyeUFuZEdldFJlc3VsdEl0ZXJhdG9yID0gYXN5bmMgKHF1ZXJ5OiBzdHJpbmcsIHF1ZXJ5TmFtZTogc3RyaW5nKTogUHJvbWlzZTxBc3luY0dlbmVyYXRvcjxBcnJheTxPYmplY3Q+LCB2b2lkLCBhbnk+PiA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGF3c0NvbmZpZyA9IFN5c3RlbS5nZXRDb25maWcoKS5hd3NcbiAgICAgIGlmICghYXdzQ29uZmlnKVxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ2F3cyBjb25maWcgbm90IHNldCBpbiBzeXN0ZW0gY29uZmlnJylcbiAgXG4gICAgICBjb25zdCBzdGFydE1TID0gRGF0ZS5ub3coKVxuICAgICAgY29uc3QgT1VUUFVUX0xPQ0FUSU9OID0gYHMzOi8vYXdzLWF0aGVuYS1xdWVyeS1yZXN1bHRzLSR7IGF3c0NvbmZpZy5hY2NvdW50SWQgfHwgdGhyb3dVbnNldEFyZygnc3lzdGVtQ29uZmlnLmF3cy5hY2NvdW50SWQnKSB9LSR7IGF3c0NvbmZpZy5kZWZhdWx0UmVnaW9uIHx8IHRocm93VW5zZXRBcmcoJ3N5c3RlbUNvbmZpZy5hd3MuZGVmYXVsdFJlZ2lvbicpIH0vJHtTeXN0ZW0uZ2V0Q29uZmlnKCkuZW52fS8ke3F1ZXJ5TmFtZX0vYFxuICBcbiAgICAgIC8vIGV4ZWN1dGUgcXVlcnlcbiAgICAgIGNvbnN0IHN0YXJ0UmVzID0gYXdhaXQgdGhpcy5fYXdzQ2xpZW50LnJ1bkF0aGVuYShhdGhlbmEgPT4gYXRoZW5hLnN0YXJ0UXVlcnlFeGVjdXRpb24oe1xuICAgICAgICBRdWVyeVN0cmluZzogcXVlcnksXG4gICAgICAgIFJlc3VsdENvbmZpZ3VyYXRpb246IHtcbiAgICAgICAgICBPdXRwdXRMb2NhdGlvbjogT1VUUFVUX0xPQ0FUSU9OLFxuICAgICAgICB9LFxuICAgICAgfSkpXG4gIFxuICAgICAgcmV0dXJuIGl0ZXJhdGVBdGhlbmFRdWVyeVJlc3VsdHModGhpcy5fYXdzQ2xpZW50LCBzdGFydFJlcy5yZXN1bHQuUXVlcnlFeGVjdXRpb25JZCwgcXVlcnlOYW1lLCBxdWVyeSwgc3RhcnRNUylcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGVycm9yKCdBdGhlbmEgcXVlcnkgZXhlY3V0aW9uIGZhaWxlZCcsIHsgcXVlcnlOYW1lOiBxdWVyeU5hbWUsIHF1ZXJ5OiBxdWVyeSwgZXJyIH0pXG4gICAgICB0aHJvdyBlcnJcbiAgICB9XG4gIH1cbn1cblxuXG5cbmFzeW5jIGZ1bmN0aW9uKiBpdGVyYXRlQXRoZW5hUXVlcnlSZXN1bHRzKGF3c0NsaWVudDogQVdTQ2xpZW50LCBxdWVyeUV4ZWN1dGlvbklkOiBzdHJpbmcsIHF1ZXJ5TmFtZTogc3RyaW5nLCBxdWVyeTogc3RyaW5nLCBzdGFydE1TOiBudW1iZXIpOiBBc3luY0dlbmVyYXRvcjxBcnJheTxPYmplY3Q+LCB2b2lkLCBhbnk+IHtcbiAgdHJ5IHtcbiAgICAvLyB3YWl0IGZvciBxdWVyeSBleGVjdXRpb24gdG8gZW5kXG4gICAgbGV0IGRhdGFTY2FubmVkSW5CeXRlcyA9IDBcbiAgICB3aGlsZSAodHJ1ZSkge1xuICAgICAgY29uc3Qgc3RhdHVzUmVzID0gYXdhaXQgYXdzQ2xpZW50LnJ1bkF0aGVuYShhdGhlbmEgPT4gYXRoZW5hLmdldFF1ZXJ5RXhlY3V0aW9uKHtcbiAgICAgICAgUXVlcnlFeGVjdXRpb25JZDogcXVlcnlFeGVjdXRpb25JZCxcbiAgICAgIH0pKVxuICAgICAgXG4gICAgICBjb25zdCB7IFN0YXRlLCBTdGF0ZUNoYW5nZVJlYXNvbiB9ID0gc3RhdHVzUmVzLnJlc3VsdC5RdWVyeUV4ZWN1dGlvbi5TdGF0dXNcbiAgICAgIGlmIChTdGF0ZSA9PT0gJ1NVQ0NFRURFRCcpIHtcbiAgICAgICAgZGF0YVNjYW5uZWRJbkJ5dGVzID0gXy5nZXQoc3RhdHVzUmVzLnJlc3VsdC5RdWVyeUV4ZWN1dGlvbiwgJ1N0YXRpc3RpY3MuRGF0YVNjYW5uZWRJbkJ5dGVzJylcbiAgICAgICAgYnJlYWtcbiAgICAgIH0gZWxzZSBpZiAoWydRVUVVRUQnLCAnUlVOTklORyddLmluY2x1ZGVzKFN0YXRlKSkge1xuICAgICAgICBhd2FpdCBzbGVlcCg1MDApXG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aHJvdyBuZXcgRXh0ZW5kZWRFcnJvcignQXRoZW5hIHF1ZXJ5IGZhaWxlZCcsIHsgcXVlcnlOYW1lOiBxdWVyeU5hbWUsIHN0YXRlOiBTdGF0ZSwgcmVhc29uOiBTdGF0ZUNoYW5nZVJlYXNvbiwgcXVlcnk6IHF1ZXJ5IH0pXG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3Qga2JTY2FubmVkID0gTWF0aC5yb3VuZChkYXRhU2Nhbm5lZEluQnl0ZXMgLyAxMDAwKVxuICAgIGNvbnN0IGtiQ2hhcmdlZCA9IE1hdGgubWF4KGtiU2Nhbm5lZCwgMTAgKiAxMDAwKSAvLyBBdGhlbmEgY2hhcmdlcyBhIG1pbmltdW0gb2YgMTBtYiBmb3IgZXZlcnkgcXVlcnlcbiAgICBjb25zdCBjb3N0RG9sbGFyID0gNSAqIGtiQ2hhcmdlZCAvIDEwMDAwMDAwMDAgLy8gQXRoZW5hIGNoYXJnZXMgNSQgZm9yIGEgVEIgb2YgZGF0YVxuICAgIG5vdGVDb3VudChgJHtxdWVyeU5hbWV9LmtiU2Nhbm5lZGAsIGtiU2Nhbm5lZClcbiAgICBub3RlQ291bnQoYCR7cXVlcnlOYW1lfS5rYkNoYXJnZWRgLCBrYkNoYXJnZWQpXG4gICAgbm90ZUNvdW50KGAke3F1ZXJ5TmFtZX0uZG9sbGFyc0NoYXJnZWRgLCBjb3N0RG9sbGFyKVxuXG4gICAgLy8gZmV0Y2ggcmVzcG9uc2VcbiAgICBsZXQgbmV4dFRva2VuOiA/c3RyaW5nID0gbnVsbFxuICAgIGRvIHtcbiAgICAgIGNvbnN0IHJlc3VsdHNSZXMgPSBhd2FpdCBhd3NDbGllbnQucnVuQXRoZW5hKGF0aGVuYSA9PiBhdGhlbmEuZ2V0UXVlcnlSZXN1bHRzKHtcbiAgICAgICAgUXVlcnlFeGVjdXRpb25JZDogcXVlcnlFeGVjdXRpb25JZCxcbiAgICAgICAgTWF4UmVzdWx0czogSW5maW5pdHksXG4gICAgICAgIE5leHRUb2tlbjogbmV4dFRva2VuLFxuICAgICAgfSkpXG5cbiAgICAgIGNvbnN0IGF0aGVuYVJlc3VsdHMgPSBnZXRBdGhlbmFRdWVyeVJlc3VsdExpbmVzKHJlc3VsdHNSZXMucmVzdWx0KVxuICAgICAgY29uc3QgbmV3UmVzdWx0T2JqZWN0cyA9IGNzdkFycmF5VG9PYmplY3RzKGF0aGVuYVJlc3VsdHMpXG5cbiAgICAgIHlpZWxkIG5ld1Jlc3VsdE9iamVjdHNcblxuICAgICAgbmV4dFRva2VuID0gcmVzdWx0c1Jlcy5yZXN1bHQuTmV4dFRva2VuICAgICAgXG4gICAgfSB3aGlsZSAobmV4dFRva2VuKVxuXG4gICAgY29uc3QgZHVyYXRpb25NUyA9IERhdGUubm93KCkgLSBzdGFydE1TXG4gICAgLy8gbG9nKGBBdGhlbmEgcXVlcnkgY29tcGxldGVgLCB7IHF1ZXJ5TmFtZSwga2JTY2FubmVkLCBrYkNoYXJnZWQsIGNvc3REb2xsYXIsIHJlc3VsdHNMb2FkZWQ6IHJlc3VsdE9iamVjdHMubGVuZ3RoLCBkdXJhdGlvbk1TOiBkdXJhdGlvbk1TIH0pXG4gICAgbm90ZVRpbWVyKGAke3F1ZXJ5TmFtZX0uZHVyYXRpb25gLCBkdXJhdGlvbk1TKVxuICB9IGNhdGNoIChlcnIpIHtcbiAgICBlcnJvcignQXRoZW5hIHF1ZXJ5IGV4ZWN1dGlvbiBmYWlsZWQnLCB7IHF1ZXJ5TmFtZTogcXVlcnlOYW1lLCBxdWVyeTogcXVlcnksIGVyciB9KVxuICAgIHRocm93IGVyclxuICB9XG59XG5cbmZ1bmN0aW9uIHN0cmlwUXVvdGVzKGlucHV0OiAqKTogKiB7XG4gIGlmICh0eXBlb2YgaW5wdXQgIT09ICdzdHJpbmcnKVxuICAgIHJldHVybiBpbnB1dFxuICBpZiAoaW5wdXQgPT09ICcnKVxuICAgIHJldHVybiBudWxsXG4gIGNvbnN0IGxlbmd0aCA9IGlucHV0Lmxlbmd0aFxuICBpZiAobGVuZ3RoIDwgMiB8fCAhaW5wdXQuc3RhcnRzV2l0aCgnXCInKSB8fCAhaW5wdXQuZW5kc1dpdGgoJ1wiJykpXG4gICAgcmV0dXJuIGlucHV0XG4gIHJldHVybiBpbnB1dC5zdWJzdHIoMSwgbGVuZ3RoIC0gMilcbn1cblxuZnVuY3Rpb24gZ2V0QXRoZW5hUXVlcnlSZXN1bHRMaW5lcyhyZXM6IE9iamVjdCk6IEFycmF5PEFycmF5PHN0cmluZz4+IHtcbiAgY29uc3Qgcm9sbG91dExpbmVzOiBBcnJheTxBcnJheTxzdHJpbmc+PiA9IFtdICAgICBcbiAgY29uc3QgbnVtZXJpY1R5cGVOYW1lcyA9IFsnaW50ZWdlcicsICdiaWdpbnQnLCAnZG91YmxlJ11cbiAgY29uc3QgY29sdW1uTWV0YXMgPSByZXMuUmVzdWx0U2V0LlJlc3VsdFNldE1ldGFkYXRhLkNvbHVtbkluZm9cbiAgcm9sbG91dExpbmVzLnB1c2goY29sdW1uTWV0YXMubWFwKGNtID0+IGNtLk5hbWUpKVxuICByZXMuUmVzdWx0U2V0LlJvd3NcbiAgICAuc3BsaWNlKDEpIC8vIHNraXAgaGVhZGVyXG4gICAgLmZvckVhY2goXG4gICAgICByb3cgPT4gcm9sbG91dExpbmVzLnB1c2goXG4gICAgICAgIHJvdy5EYXRhLm1hcChcbiAgICAgICAgICAoZGF0dW0sIGkpID0+IG51bWVyaWNUeXBlTmFtZXNcbiAgICAgICAgICAgIC5pbmNsdWRlcyhjb2x1bW5NZXRhc1tpXS5UeXBlKSA/IFxuICAgICAgICAgICAgKGRhdHVtLlZhckNoYXJWYWx1ZSA9PT0gdW5kZWZpbmVkID8gbnVsbCA6IE51bWJlcihkYXR1bS5WYXJDaGFyVmFsdWUpKSA6IFxuICAgICAgICAgICAgKGRhdHVtLlZhckNoYXJWYWx1ZSA9PT0gdW5kZWZpbmVkID8gbnVsbCA6IGBcIiR7ZGF0dW0uVmFyQ2hhclZhbHVlfVwiYClcbiAgICAgICAgKVxuICAgICAgKVxuICAgIClcbiAgY29uc3QgY3N2Q29udGVudCA9IHJvbGxvdXRMaW5lc1xuICByZXR1cm4gY3N2Q29udGVudFxufVxuXG5mdW5jdGlvbiBjc3ZBcnJheVRvT2JqZWN0cyhjc3ZBcnJheTogQXJyYXk8QXJyYXk8c3RyaW5nPj4pIHtcbiAgY29uc3QgcHJvcE5hbWVzID0gY3N2QXJyYXlbMF1cbiAgY29uc3QgdmFsdWVMaW5lcyA9IGNzdkFycmF5LnNsaWNlKDEpXG4gIGNvbnN0IG9iamVjdHMgPSB2YWx1ZUxpbmVzLm1hcCh2YWx1ZUxpbmUgPT4ge1xuICAgIGNvbnN0IG9iaiA9IHt9XG4gICAgdmFsdWVMaW5lLmZvckVhY2goKHZhbHVlLCBpKSA9PiBvYmpbcHJvcE5hbWVzW2ldXSA9IHN0cmlwUXVvdGVzKHZhbHVlKSlcbiAgICByZXR1cm4gb2JqXG4gIH0pXG4gIHJldHVybiBvYmplY3RzXG59Il19
\No newline at end of file