1 | ;
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 | exports.default = void 0;
|
7 |
|
8 | require("@babel/polyfill");
|
9 |
|
10 | var _lodash = _interopRequireDefault(require("lodash"));
|
11 |
|
12 | var _compressionUtils = require("./compressionUtils");
|
13 |
|
14 | var _asyncUtils = require("./asyncUtils");
|
15 |
|
16 | var _System = _interopRequireDefault(require("./System"));
|
17 |
|
18 | var _Module = _interopRequireDefault(require("./Module"));
|
19 |
|
20 | var _errorUtils = require("./errorUtils");
|
21 |
|
22 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
23 |
|
24 | function _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 |
|
26 | function _awaitAsyncGenerator(value) { return new _AwaitValue(value); }
|
27 |
|
28 | function _wrapAsyncGenerator(fn) { return function () { return new _AsyncGenerator(fn.apply(this, arguments)); }; }
|
29 |
|
30 | function _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 |
|
32 | if (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 |
|
40 | function _AwaitValue(value) { this.wrapped = value; }
|
41 |
|
42 | function _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 |
|
44 | const {
|
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 |
|
54 | const MAX_PARTITION_CACHE_SIZE = 1000;
|
55 |
|
56 | class 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 |
|
185 | exports.default = AthenaClient;
|
186 |
|
187 | function iterateAthenaQueryResults(_x, _x2, _x3, _x4, _x5) {
|
188 | return _iterateAthenaQueryResults.apply(this, arguments);
|
189 | }
|
190 |
|
191 | function _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 |
|
259 | function 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 |
|
267 | function 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 |
|
278 | function 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 |