sliftutils 1.7.103 → 1.7.105

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,399 @@
1
+ "use strict"; var console = (function shimConsole() { let baseConsole = globalThis.console; if (typeof window !== "undefined") return baseConsole; let name = __filename.replaceAll("\\", "/").split("/").pop(); let dirName = __dirname.replaceAll("\\", "/").split("/").pop(); let context = { __FILE__: __filename, __DIR__: __dirname, __NAME__: name, __DIRNAME__: dirName }; let newConsole = { ...baseConsole }; for (let shim of ["log", "info", "warn", "error"]) { newConsole[shim] = (...args) => { args.push(context); baseConsole[shim](...args); }; } newConsole.__context = context; return newConsole; })(); "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule && mod.default) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
6
+ //exports.StreamingLogs = exports.decodeLogFile = exports.createLogSearcher = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const misc_1 = require("socket-function/src/misc");
10
+ const LZ4_1 = require("socket-function/src/lz4/LZ4");
11
+ // Generic append-only JSON-lines logging over a folder. Every process streams to its OWN file (named by pid + process start time + thread id, which together identify a process incarnation), rotating to a new file at a size limit and LZ4-compressing the finished one. Every process using the folder also MAINTAINS it on a randomized interval: dead processes' files are compressed for them, duplicate compressions are resolved, and the oldest files are deleted once the folder outgrows its byte budget. Instantiate one per folder and call log().
12
+ // Appends are coalesced for this long; a crash loses at most this much
13
+ const FLUSH_DELAY = 5000;
14
+ // A file reaching this size is rotated out and compressed
15
+ const MAX_FILE_BYTES = 100 * 1024 * 1024;
16
+ // The folder's total budget; once exceeded, the oldest files are deleted
17
+ const TOTAL_LIMIT_BYTES = 30 * 1024 * 1024 * 1024;
18
+ // Maintenance runs at a random point in this range, so the processes sharing the folder drift apart instead of stepping on each other every pass
19
+ const MAINTENANCE_MIN_INTERVAL = 30 * 60 * 1000;
20
+ const MAINTENANCE_MAX_INTERVAL = 60 * 60 * 1000;
21
+ // A pid is only the same PROCESS if its start time matches the file name's - within this much, because our own start time is derived from process.uptime and the OS's is read off /proc
22
+ const PROCESS_START_TOLERANCE = 60 * 1000;
23
+ // If flushing fails persistently (disk gone), pending entries are capped rather than growing forever
24
+ const MAX_PENDING_ENTRIES = 10000;
25
+ const UNCOMPRESSED_EXT = ".jsonl";
26
+ const COMPRESSED_EXT = ".jsonl.lz4";
27
+ function parseLogFileName(name) {
28
+ // The time fields accept a fractional part: hosts that shim Date.now to a true-time clock return fractional milliseconds, and files written before the writer rounded them are already on disk everywhere - they must stay listable (and maintainable, or they would never be compressed or cleaned).
29
+ let match = /^pid(\d+)_born(\d+)_thread([\w-]+?)_from(\d+(?:\.\d+)?)(?:_to(\d+(?:\.\d+)?)_count(\d+)_id([a-z0-9]+))?\.jsonl(\.lz4)?$/.exec(name);
30
+ if (!match)
31
+ return undefined;
32
+ let compressed = !!match[8];
33
+ // A compressed file without its compression fields (or the reverse) is not one of ours
34
+ if (compressed !== (match[5] !== undefined))
35
+ return undefined;
36
+ return {
37
+ pid: +match[1],
38
+ processStartTime: +match[2],
39
+ threadId: match[3],
40
+ startTime: +match[4],
41
+ endTime: match[5] !== undefined && +match[5] || undefined,
42
+ entryCount: match[6] !== undefined && +match[6] || undefined,
43
+ compressed,
44
+ originKey: `pid${match[1]}_born${match[2]}_thread${match[3]}_from${match[4]}`,
45
+ };
46
+ }
47
+ /** Whether the pid is alive AND is the same process incarnation the file name described. On Linux /proc gives the real start time; elsewhere a live pid is conservatively treated as the same process (never compress under a running process). */
48
+ async function isProcessAlive(pid, processStartTime) {
49
+ try {
50
+ process.kill(pid, 0);
51
+ }
52
+ catch (_a) {
53
+ return false;
54
+ }
55
+ try {
56
+ let stat = await fs_1.default.promises.stat(`/proc/${pid}`);
57
+ return Math.abs(stat.ctimeMs - processStartTime) < PROCESS_START_TOLERANCE;
58
+ }
59
+ catch (_b) {
60
+ return true;
61
+ }
62
+ }
63
+ /**
64
+ * A searcher over one log file (as readFileCompressed returns it - always LZ4) that never decodes
65
+ * the whole thing: JSON escapes line breaks inside strings, so the only ACTUAL line breaks in the
66
+ * file are the separators between log statements - a plain substring search over the raw text,
67
+ * bounded out to the surrounding line breaks, finds exactly the matching statements, and only THOSE
68
+ * are decoded and returned as objects. Every search string must appear in a statement's raw JSON for
69
+ * it to match (so search for values as they are encoded - e.g. quoted).
70
+ */
71
+ function createLogSearcher(data) {
72
+ let text = LZ4_1.LZ4.decompress(data).toString("utf8");
73
+ return searches => {
74
+ if (!searches.length) {
75
+ throw new Error(`Provide at least one search string (an empty search would just be decodeLogFile)`);
76
+ }
77
+ let entries = [];
78
+ let index = 0;
79
+ while (true) {
80
+ let found = text.indexOf(searches[0], index);
81
+ if (found === -1)
82
+ break;
83
+ let lineStart = text.lastIndexOf("\n", found) + 1;
84
+ let lineEnd = text.indexOf("\n", found);
85
+ if (lineEnd === -1) {
86
+ lineEnd = text.length;
87
+ }
88
+ // Past this line either way, so several matches inside one statement return it once
89
+ index = lineEnd + 1;
90
+ let line = text.slice(lineStart, lineEnd);
91
+ let matchesAll = true;
92
+ for (let other of searches.slice(1)) {
93
+ if (!line.includes(other)) {
94
+ matchesAll = false;
95
+ break;
96
+ }
97
+ }
98
+ if (!matchesAll)
99
+ continue;
100
+ try {
101
+ entries.push(JSON.parse(line));
102
+ }
103
+ catch (_a) {
104
+ // A torn final line (the writer crashed mid-append)
105
+ continue;
106
+ }
107
+ }
108
+ return entries;
109
+ };
110
+ }
111
+ exports.createLogSearcher = createLogSearcher;
112
+ /** Decodes a log file's bytes (as readFileCompressed returns them - always LZ4) back into the logged objects. A torn final line (the writer crashed mid-append) is skipped. */
113
+ function decodeLogFile(data) {
114
+ let text = LZ4_1.LZ4.decompress(data).toString("utf8");
115
+ let entries = [];
116
+ for (let line of text.split("\n")) {
117
+ if (!line)
118
+ continue;
119
+ try {
120
+ entries.push(JSON.parse(line));
121
+ }
122
+ catch (_a) {
123
+ continue;
124
+ }
125
+ }
126
+ return entries;
127
+ }
128
+ exports.decodeLogFile = decodeLogFile;
129
+ class StreamingLogs {
130
+ constructor(config) {
131
+ this.config = config;
132
+ this.processStartTime = Math.round(Date.now() - process.uptime() * 1000);
133
+ this.threadId = (this.config.threadId || "none").replace(/[^\w-]/g, "_");
134
+ this.pending = [];
135
+ // Serializes everything that touches the current file, so appends, rotation, and in-memory compression of the live file never interleave
136
+ this.writeChain = Promise.resolve();
137
+ this.currentBytes = 0;
138
+ this.disposed = false;
139
+ this.scheduleMaintenance();
140
+ }
141
+ /** Queues one entry (anything JSON-serializable). Never throws - logging must not take down the caller. */
142
+ log(entry) {
143
+ try {
144
+ if (this.pending.length >= MAX_PENDING_ENTRIES) {
145
+ // The disk is broken (flushes are failing) - dropping the oldest beats growing forever
146
+ this.pending.shift();
147
+ }
148
+ this.pending.push(JSON.stringify(entry));
149
+ this.scheduleFlush();
150
+ }
151
+ catch (e) {
152
+ console.warn(`Could not queue a log entry (${e.message})`);
153
+ }
154
+ }
155
+ scheduleFlush() {
156
+ var _a, _b;
157
+ if (this.flushTimer !== undefined || this.disposed)
158
+ return;
159
+ this.flushTimer = setTimeout(() => {
160
+ this.flushTimer = undefined;
161
+ void this.flush().catch((e) => { var _a; return console.warn(`Flushing logs in ${this.config.folder} failed: ${(_a = e.stack) !== null && _a !== void 0 ? _a : e}`); });
162
+ }, FLUSH_DELAY);
163
+ (_b = (_a = this.flushTimer).unref) === null || _b === void 0 ? void 0 : _b.call(_a);
164
+ }
165
+ async flush() {
166
+ let result = this.writeChain.then(() => this.writePending());
167
+ // The chain only serializes; a failure belongs to the caller that asked for this flush, not every one after it
168
+ this.writeChain = result.catch(() => { });
169
+ await result;
170
+ }
171
+ async writePending() {
172
+ if (!this.pending.length)
173
+ return;
174
+ let lines = this.pending;
175
+ this.pending = [];
176
+ let data = lines.join("\n") + "\n";
177
+ try {
178
+ if (!this.currentPath) {
179
+ await fs_1.default.promises.mkdir(this.config.folder, { recursive: true });
180
+ // Rounded, as a shimmed Date.now can return fractional milliseconds, and a "." in the name breaks name parsing for any reader without the fractional-tolerant regex
181
+ this.currentPath = path_1.default.join(this.config.folder, `pid${process.pid}_born${this.processStartTime}_thread${this.threadId}_from${Math.round(Date.now())}${UNCOMPRESSED_EXT}`);
182
+ this.currentBytes = 0;
183
+ }
184
+ await fs_1.default.promises.appendFile(this.currentPath, data);
185
+ this.currentBytes += Buffer.byteLength(data);
186
+ }
187
+ catch (e) {
188
+ this.pending = lines.concat(this.pending);
189
+ this.scheduleFlush();
190
+ throw e;
191
+ }
192
+ if (this.currentBytes >= (this.config.maxFileBytes || MAX_FILE_BYTES)) {
193
+ let finished = this.currentPath;
194
+ this.currentPath = undefined;
195
+ this.currentBytes = 0;
196
+ void this.compressFile(finished).catch((e) => {
197
+ var _a;
198
+ // Left uncompressed: maintenance (ours after death, or any peer's) compresses it later
199
+ console.warn(`Compressing rotated log ${finished} failed: ${(_a = e.stack) !== null && _a !== void 0 ? _a : e}`);
200
+ });
201
+ }
202
+ }
203
+ /**
204
+ * Compresses one finished log file: LZ4 into a temp SUBFOLDER (never a temp name in the log
205
+ * folder itself - a crashed write must not leave a corrupt file where the maintenance scans are),
206
+ * renamed into place (same drive, so the rename is atomic), verified, and only THEN is the
207
+ * original deleted - at no point is the data in fewer than one complete file.
208
+ */
209
+ async compressFile(filePath) {
210
+ let name = path_1.default.basename(filePath);
211
+ let parsed = parseLogFileName(name);
212
+ if (!parsed || parsed.compressed)
213
+ return;
214
+ let raw = await fs_1.default.promises.readFile(filePath);
215
+ let entryCount = 0;
216
+ for (let i = 0; i < raw.length; i++) {
217
+ if (raw[i] === 10)
218
+ entryCount++;
219
+ }
220
+ let compressed = LZ4_1.LZ4.compress(raw);
221
+ let id = Math.random().toString(36).slice(2, 10);
222
+ let finalName = `${parsed.originKey}_to${Math.round(Date.now())}_count${entryCount}_id${id}${COMPRESSED_EXT}`;
223
+ let tempDir = path_1.default.join(this.config.folder, ".tmp");
224
+ await fs_1.default.promises.mkdir(tempDir, { recursive: true });
225
+ let tempPath = path_1.default.join(tempDir, finalName);
226
+ let finalPath = path_1.default.join(this.config.folder, finalName);
227
+ await fs_1.default.promises.writeFile(tempPath, compressed);
228
+ await fs_1.default.promises.rename(tempPath, finalPath);
229
+ // Paranoia before deleting the only other copy
230
+ await fs_1.default.promises.stat(finalPath);
231
+ await fs_1.default.promises.unlink(filePath);
232
+ console.log(`Compressed log ${name} -> ${finalName} (${raw.length} -> ${compressed.length} bytes, ${entryCount} entries)`);
233
+ }
234
+ // ── listing / reading ──
235
+ async listFiles() {
236
+ let names;
237
+ try {
238
+ names = await fs_1.default.promises.readdir(this.config.folder);
239
+ }
240
+ catch (e) {
241
+ if (e.code === "ENOENT")
242
+ return [];
243
+ throw e;
244
+ }
245
+ let files = [];
246
+ for (let name of names) {
247
+ let parsed = parseLogFileName(name);
248
+ if (!parsed)
249
+ continue;
250
+ let stat = await fs_1.default.promises.stat(path_1.default.join(this.config.folder, name)).catch(() => undefined);
251
+ if (!stat)
252
+ continue;
253
+ let active = !parsed.compressed && await isProcessAlive(parsed.pid, parsed.processStartTime);
254
+ files.push({
255
+ name,
256
+ size: stat.size,
257
+ pid: parsed.pid,
258
+ processStartTime: parsed.processStartTime,
259
+ threadId: parsed.threadId,
260
+ startTime: parsed.startTime,
261
+ endTime: parsed.endTime,
262
+ entryCount: parsed.entryCount,
263
+ compressed: parsed.compressed,
264
+ active,
265
+ });
266
+ }
267
+ (0, misc_1.sort)(files, x => x.startTime);
268
+ return files;
269
+ }
270
+ /** One file's bytes, ALWAYS LZ4-compressed: compressed files are sent as-is, a still-streaming file is flushed and compressed in memory (smaller over the network; decodeLogFile handles both identically). */
271
+ async readFileCompressed(name) {
272
+ let parsed = parseLogFileName(name);
273
+ if (!parsed || name.includes("/") || name.includes("\\")) {
274
+ throw new Error(`Not a log file name: ${JSON.stringify(name.slice(0, 200))}`);
275
+ }
276
+ let filePath = path_1.default.join(this.config.folder, name);
277
+ if (parsed.compressed) {
278
+ return await fs_1.default.promises.readFile(filePath);
279
+ }
280
+ // Flush so our own live file includes everything logged up to now
281
+ await this.flush();
282
+ return LZ4_1.LZ4.compress(await fs_1.default.promises.readFile(filePath));
283
+ }
284
+ // ── maintenance ──
285
+ scheduleMaintenance() {
286
+ var _a, _b;
287
+ if (this.disposed)
288
+ return;
289
+ let wait = MAINTENANCE_MIN_INTERVAL + Math.random() * (MAINTENANCE_MAX_INTERVAL - MAINTENANCE_MIN_INTERVAL);
290
+ this.maintenanceTimer = setTimeout(() => {
291
+ void this.runMaintenance().catch((e) => { var _a; return console.warn(`Log maintenance of ${this.config.folder} failed: ${(_a = e.stack) !== null && _a !== void 0 ? _a : e}`); }).finally(() => this.scheduleMaintenance());
292
+ }, wait);
293
+ (_b = (_a = this.maintenanceTimer).unref) === null || _b === void 0 ? void 0 : _b.call(_a);
294
+ }
295
+ /**
296
+ * Shared upkeep of the folder - every process using it runs this, so nothing depends on any one
297
+ * process surviving: dead processes' uncompressed files are compressed for them; duplicate
298
+ * compressions of one stream (two maintainers racing) are resolved by keeping the OLDEST copy;
299
+ * an uncompressed original whose compression already exists is deleted (its compressor died
300
+ * between rename and unlink); and the folder's total size is brought under its budget by
301
+ * deleting the oldest files.
302
+ */
303
+ async runMaintenance() {
304
+ var _a;
305
+ let names;
306
+ try {
307
+ names = await fs_1.default.promises.readdir(this.config.folder);
308
+ }
309
+ catch (e) {
310
+ if (e.code === "ENOENT")
311
+ return;
312
+ throw e;
313
+ }
314
+ let parsedByName = new Map();
315
+ for (let name of names) {
316
+ let parsed = parseLogFileName(name);
317
+ if (parsed)
318
+ parsedByName.set(name, parsed);
319
+ }
320
+ let compressedOrigins = new Map();
321
+ for (let [name, parsed] of parsedByName) {
322
+ if (!parsed.compressed)
323
+ continue;
324
+ let list = compressedOrigins.get(parsed.originKey);
325
+ if (!list) {
326
+ list = [];
327
+ compressedOrigins.set(parsed.originKey, list);
328
+ }
329
+ list.push(name);
330
+ }
331
+ // Duplicate compressions: keep the oldest (first written), delete the rest
332
+ for (let [origin, list] of compressedOrigins) {
333
+ if (list.length < 2)
334
+ continue;
335
+ (0, misc_1.sort)(list, name => { var _a; return ((_a = parsedByName.get(name)) === null || _a === void 0 ? void 0 : _a.endTime) || 0; });
336
+ for (let duplicate of list.slice(1)) {
337
+ console.log(`Deleting duplicate compressed log ${duplicate} (another maintainer compressed ${origin} first, keeping ${list[0]})`);
338
+ await fs_1.default.promises.unlink(path_1.default.join(this.config.folder, duplicate)).catch(() => { });
339
+ parsedByName.delete(duplicate);
340
+ }
341
+ }
342
+ for (let [name, parsed] of parsedByName) {
343
+ if (parsed.compressed)
344
+ continue;
345
+ let filePath = path_1.default.join(this.config.folder, name);
346
+ if (filePath === this.currentPath)
347
+ continue;
348
+ if (compressedOrigins.has(parsed.originKey)) {
349
+ // Already compressed by someone whose cleanup died between rename and unlink
350
+ console.log(`Deleting uncompressed log ${name}: its compressed version already exists`);
351
+ await fs_1.default.promises.unlink(filePath).catch(() => { });
352
+ continue;
353
+ }
354
+ // A live process owns its uncompressed file; only a DEAD process's files are compressed for it (our own pid always reads as alive, which correctly protects our current file - our rotated leftovers were excluded above only if current, so compress those too)
355
+ if (parsed.pid !== process.pid && await isProcessAlive(parsed.pid, parsed.processStartTime))
356
+ continue;
357
+ try {
358
+ await this.compressFile(filePath);
359
+ }
360
+ catch (e) {
361
+ console.warn(`Compressing dead process's log ${name} failed (the next maintenance pass retries): ${(_a = e.stack) !== null && _a !== void 0 ? _a : e}`);
362
+ }
363
+ }
364
+ await this.enforceTotalLimit();
365
+ }
366
+ async enforceTotalLimit() {
367
+ let limit = this.config.totalLimitBytes || TOTAL_LIMIT_BYTES;
368
+ let files = await this.listFiles();
369
+ let total = files.reduce((sum, x) => sum + x.size, 0);
370
+ if (total <= limit)
371
+ return;
372
+ // Oldest first, by the end of what they cover; live files last (they are the newest data by definition)
373
+ let deletable = files.filter(x => !x.active);
374
+ (0, misc_1.sort)(deletable, x => x.endTime || x.startTime);
375
+ let deleted = 0;
376
+ let deletedBytes = 0;
377
+ for (let file of deletable) {
378
+ if (total <= limit)
379
+ break;
380
+ await fs_1.default.promises.unlink(path_1.default.join(this.config.folder, file.name)).catch(() => { });
381
+ total -= file.size;
382
+ deleted++;
383
+ deletedBytes += file.size;
384
+ }
385
+ console.log(`Logs in ${this.config.folder} outgrew their ${limit} byte budget: deleted the ${deleted} oldest files (${deletedBytes} bytes), ${total} bytes remain`);
386
+ }
387
+ dispose() {
388
+ this.disposed = true;
389
+ if (this.flushTimer) {
390
+ clearTimeout(this.flushTimer);
391
+ }
392
+ if (this.maintenanceTimer) {
393
+ clearTimeout(this.maintenanceTimer);
394
+ }
395
+ }
396
+ }
397
+ exports.StreamingLogs = StreamingLogs;
398
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU3RyZWFtaW5nTG9ncy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIlN0cmVhbWluZ0xvZ3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEsNENBQW9CO0FBQ3BCLGdEQUF3QjtBQUN4QixtREFBZ0Q7QUFDaEQscURBQWtEO0FBRWxELGdpQkFBZ2lCO0FBRWhpQix1RUFBdUU7QUFDdkUsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDO0FBQ3pCLDBEQUEwRDtBQUMxRCxNQUFNLGNBQWMsR0FBRyxHQUFHLEdBQUcsSUFBSSxHQUFHLElBQUksQ0FBQztBQUN6Qyx5RUFBeUU7QUFDekUsTUFBTSxpQkFBaUIsR0FBRyxFQUFFLEdBQUcsSUFBSSxHQUFHLElBQUksR0FBRyxJQUFJLENBQUM7QUFDbEQsaUpBQWlKO0FBQ2pKLE1BQU0sd0JBQXdCLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUM7QUFDaEQsTUFBTSx3QkFBd0IsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQztBQUNoRCx3TEFBd0w7QUFDeEwsTUFBTSx1QkFBdUIsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO0FBQzFDLHFHQUFxRztBQUNyRyxNQUFNLG1CQUFtQixHQUFHLEtBQUssQ0FBQztBQUVsQyxNQUFNLGdCQUFnQixHQUFHLFFBQVEsQ0FBQztBQUNsQyxNQUFNLGNBQWMsR0FBRyxZQUFZLENBQUM7QUFpQ3BDLFNBQVMsZ0JBQWdCLENBQUMsSUFBWTtJQUNsQyxzU0FBc1M7SUFDdFMsSUFBSSxLQUFLLEdBQUcseUhBQXlILENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ2pKLElBQUksQ0FBQyxLQUFLO1FBQUUsT0FBTyxTQUFTLENBQUM7SUFDN0IsSUFBSSxVQUFVLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUM1Qix1RkFBdUY7SUFDdkYsSUFBSSxVQUFVLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FB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QUUsQ0FBQztZQUNWLFlBQVksSUFBSSxJQUFJLENBQUMsSUFBSSxDQUFDO1FBQzlCLENBQUM7UUFDRCxPQUFPLENBQUMsR0FBRyxDQUFDLFdBQVcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLGtCQUFrQixLQUFLLDZCQUE2QixPQUFPLGtCQUFrQixZQUFZLFlBQVksS0FBSyxlQUFlLENBQUMsQ0FBQztJQUN4SyxDQUFDO0lBRU0sT0FBTztRQUNWLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDO1FBQ3JCLElBQUksSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ2xCLFlBQVksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDbEMsQ0FBQztRQUNELElBQUksSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7WUFDeEIsWUFBWSxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO1FBQ3hDLENBQUM7SUFDTCxDQUFDO0NBQ0o7QUF2UUQsc0NBdVFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGZzIGZyb20gXCJmc1wiO1xuaW1wb3J0IHBhdGggZnJvbSBcInBhdGhcIjtcbmltcG9ydCB7IHNvcnQgfSBmcm9tIFwic29ja2V0LWZ1bmN0aW9uL3NyYy9taXNjXCI7XG5pbXBvcnQgeyBMWjQgfSBmcm9tIFwic29ja2V0LWZ1bmN0aW9uL3NyYy9sejQvTFo0XCI7XG5cbi8vIEdlbmVyaWMgYXBwZW5kLW9ubHkgSlNPTi1saW5lcyBsb2dnaW5nIG92ZXIgYSBmb2xkZXIuIEV2ZXJ5IHByb2Nlc3Mgc3RyZWFtcyB0byBpdHMgT1dOIGZpbGUgKG5hbWVkIGJ5IHBpZCArIHByb2Nlc3Mgc3RhcnQgdGltZSArIHRocmVhZCBpZCwgd2hpY2ggdG9nZXRoZXIgaWRlbnRpZnkgYSBwcm9jZXNzIGluY2FybmF0aW9uKSwgcm90YXRpbmcgdG8gYSBuZXcgZmlsZSBhdCBhIHNpemUgbGltaXQgYW5kIExaNC1jb21wcmVzc2luZyB0aGUgZmluaXNoZWQgb25lLiBFdmVyeSBwcm9jZXNzIHVzaW5nIHRoZSBmb2xkZXIgYWxzbyBNQUlOVEFJTlMgaXQgb24gYSByYW5kb21pemVkIGludGVydmFsOiBkZWFkIHByb2Nlc3NlcycgZmlsZXMgYXJlIGNvbXByZXNzZWQgZm9yIHRoZW0sIGR1cGxpY2F0ZSBjb21wcmVzc2lvbnMgYXJlIHJlc29sdmVkLCBhbmQgdGhlIG9sZGVzdCBmaWxlcyBhcmUgZGVsZXRlZCBvbmNlIHRoZSBmb2xkZXIgb3V0Z3Jvd3MgaXRzIGJ5dGUgYnVkZ2V0LiBJbnN0YW50aWF0ZSBvbmUgcGVyIGZvbGRlciBhbmQgY2FsbCBsb2coKS5cblxuLy8gQXBwZW5kcyBhcmUgY29hbGVzY2VkIGZvciB0aGlzIGxvbmc7IGEgY3Jhc2ggbG9zZXMgYXQgbW9zdCB0aGlzIG11Y2hcbmNvbnN0IEZMVVNIX0RFTEFZID0gNTAwMDtcbi8vIEEgZmlsZSByZWFjaGluZyB0aGlzIHNpemUgaXMgcm90YXRlZCBvdXQgYW5kIGNvbXByZXNzZWRcbmNvbnN0IE1BWF9GSUxFX0JZVEVTID0gMTAwICogMTAyNCAqIDEwMjQ7XG4vLyBUaGUgZm9sZGVyJ3MgdG90YWwgYnVkZ2V0OyBvbmNlIGV4Y2VlZGVkLCB0aGUgb2xkZXN0IGZpbGVzIGFyZSBkZWxldGVkXG5jb25zdCBUT1RBTF9MSU1JVF9CWVRFUyA9IDMwICogMTAyNCAqIDEwMjQgKiAxMDI0O1xuLy8gTWFpbnRlbmFuY2UgcnVucyBhdCBhIHJhbmRvbSBwb2ludCBpbiB0aGlzIHJhbmdlLCBzbyB0aGUgcHJvY2Vzc2VzIHNoYXJpbmcgdGhlIGZvbGRlciBkcmlmdCBhcGFydCBpbnN0ZWFkIG9mIHN0ZXBwaW5nIG9uIGVhY2ggb3RoZXIgZXZlcnkgcGFzc1xuY29uc3QgTUFJTlRFTkFOQ0VfTUlOX0lOVEVSVkFMID0gMzAgKiA2MCAqIDEwMDA7XG5jb25zdCBNQUlOVEVOQU5DRV9NQVhfSU5URVJWQUwgPSA2MCAqIDYwICogMTAwMDtcbi8vIEEgcGlkIGlzIG9ubHkgdGhlIHNhbWUgUFJPQ0VTUyBpZiBpdHMgc3RhcnQgdGltZSBtYXRjaGVzIHRoZSBmaWxlIG5hbWUncyAtIHdpdGhpbiB0aGlzIG11Y2gsIGJlY2F1c2Ugb3VyIG93biBzdGFydCB0aW1lIGlzIGRlcml2ZWQgZnJvbSBwcm9jZXNzLnVwdGltZSBhbmQgdGhlIE9TJ3MgaXMgcmVhZCBvZmYgL3Byb2NcbmNvbnN0IFBST0NFU1NfU1RBUlRfVE9MRVJBTkNFID0gNjAgKiAxMDAwO1xuLy8gSWYgZmx1c2hpbmcgZmFpbHMgcGVyc2lzdGVudGx5IChkaXNrIGdvbmUpLCBwZW5kaW5nIGVudHJpZXMgYXJlIGNhcHBlZCByYXRoZXIgdGhhbiBncm93aW5nIGZvcmV2ZXJcbmNvbnN0IE1BWF9QRU5ESU5HX0VOVFJJRVMgPSAxMDAwMDtcblxuY29uc3QgVU5DT01QUkVTU0VEX0VYVCA9IFwiLmpzb25sXCI7XG5jb25zdCBDT01QUkVTU0VEX0VYVCA9IFwiLmpzb25sLmx6NFwiO1xuXG4vKiogRXZlcnl0aGluZyBhIGxvZyBmaWxlJ3MgTkFNRSBzYXlzIGFib3V0IGl0IChwbHVzIGl0cyBvbi1kaXNrIHNpemUpLiBUaW1lcyBib3VuZCB0aGUgZW50cmllcyByb3VnaGx5OiBmcm9tIGlzIHdoZW4gaXRzIHByb2Nlc3Mgc3RhcnRlZCB3cml0aW5nIGl0LCB0byBpcyB3aGVuIGl0IHdhcyBjb21wcmVzc2VkLiAqL1xuZXhwb3J0IHR5cGUgTG9nRmlsZUluZm8gPSB7XG4gICAgbmFtZTogc3RyaW5nO1xuICAgIC8qKiBCeXRlcyBvbiBkaXNrIChjb21wcmVzc2VkIGJ5dGVzIGZvciBjb21wcmVzc2VkIGZpbGVzKSAqL1xuICAgIHNpemU6IG51bWJlcjtcbiAgICBwaWQ6IG51bWJlcjtcbiAgICBwcm9jZXNzU3RhcnRUaW1lOiBudW1iZXI7XG4gICAgdGhyZWFkSWQ6IHN0cmluZztcbiAgICAvKiogV2hlbiB0aGUgcHJvY2VzcyBzdGFydGVkIHdyaXRpbmcgdGhpcyBmaWxlICovXG4gICAgc3RhcnRUaW1lOiBudW1iZXI7XG4gICAgLyoqIFdoZW4gdGhlIGZpbGUgd2FzIGNvbXByZXNzZWQgLSBhYnNlbnQgd2hpbGUgaXQgaXMgc3RpbGwgYmVpbmcgc3RyZWFtZWQgdG8gKi9cbiAgICBlbmRUaW1lPzogbnVtYmVyO1xuICAgIC8qKiBIb3cgbWFueSBlbnRyaWVzIGl0IGhvbGRzIC0gb25seSBjb3VudGVkIGF0IGNvbXByZXNzaW9uLCBzbyBhYnNlbnQgb24gbGl2ZSBmaWxlcyAqL1xuICAgIGVudHJ5Q291bnQ/OiBudW1iZXI7XG4gICAgY29tcHJlc3NlZDogYm9vbGVhbjtcbiAgICAvKiogV2hldGhlciB0aGUgd3JpdGluZyBwcm9jZXNzIGlzIHN0aWxsIGFsaXZlIChhbHdheXMgZmFsc2UgZm9yIGNvbXByZXNzZWQgZmlsZXMpICovXG4gICAgYWN0aXZlOiBib29sZWFuO1xufTtcblxudHlwZSBQYXJzZWROYW1lID0ge1xuICAgIHBpZDogbnVtYmVyO1xuICAgIHByb2Nlc3NTdGFydFRpbWU6IG51bWJlcjtcbiAgICB0aHJlYWRJZDogc3RyaW5nO1xuICAgIHN0YXJ0VGltZTogbnVtYmVyO1xuICAgIGVuZFRpbWU/OiBudW1iZXI7XG4gICAgZW50cnlDb3VudD86IG51bWJlcjtcbiAgICBjb21wcmVzc2VkOiBib29sZWFuO1xuICAgIC8qKiBUaGUgcGFydCBpZGVudGlmeWluZyB0aGUgT1JJR0lOQUwgc3RyZWFtIChwaWQrYm9ybit0aHJlYWQrZnJvbSkgLSB3aGF0IGR1cGxpY2F0ZSBjb21wcmVzc2lvbnMgc2hhcmUgKi9cbiAgICBvcmlnaW5LZXk6IHN0cmluZztcbn07XG5cbmZ1bmN0aW9uIHBhcnNlTG9nRmlsZU5hbWUobmFtZTogc3RyaW5nKTogUGFyc2VkTmFtZSB8IHVuZGVmaW5lZCB7XG4gICAgLy8gVGhlIHRpbWUgZmllbGRzIGFjY2VwdCBhIGZyYWN0aW9uYWwgcGFydDogaG9zdHMgdGhhdCBzaGltIERhdGUubm93IHRvIGEgdHJ1ZS10aW1lIGNsb2NrIHJldHVybiBmcmFjdGlvbmFsIG1pbGxpc2Vjb25kcywgYW5kIGZpbGVzIHdyaXR0ZW4gYmVmb3JlIHRoZSB3cml0ZXIgcm91bmRlZCB0aGVtIGFyZSBhbHJlYWR5IG9uIGRpc2sgZXZlcnl3aGVyZSAtIHRoZXkgbXVzdCBzdGF5IGxpc3RhYmxlIChhbmQgbWFpbnRhaW5hYmxlLCBvciB0aGV5IHdvdWxkIG5ldmVyIGJlIGNvbXByZXNzZWQgb3IgY2xlYW5lZCkuXG4gICAgbGV0IG1hdGNoID0gL15waWQoXFxkKylfYm9ybihcXGQrKV90aHJlYWQoW1xcdy1dKz8pX2Zyb20oXFxkKyg/OlxcLlxcZCspPykoPzpfdG8oXFxkKyg/OlxcLlxcZCspPylfY291bnQoXFxkKylfaWQoW2EtejAtOV0rKSk/XFwuanNvbmwoXFwubHo0KT8kLy5leGVjKG5hbWUpO1xuICAgIGlmICghbWF0Y2gpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgbGV0IGNvbXByZXNzZWQgPSAhIW1hdGNoWzhdO1xuICAgIC8vIEEgY29tcHJlc3NlZCBmaWxlIHdpdGhvdXQgaXRzIGNvbXByZXNzaW9uIGZpZWxkcyAob3IgdGhlIHJldmVyc2UpIGlzIG5vdCBvbmUgb2Ygb3Vyc1xuICAgIGlmIChjb21wcmVzc2VkICE9PSAobWF0Y2hbNV0gIT09IHVuZGVmaW5lZCkpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgcmV0dXJuIHtcbiAgICAgICAgcGlkOiArbWF0Y2hbMV0sXG4gICAgICAgIHByb2Nlc3NTdGFydFRpbWU6ICttYXRjaFsyXSxcbiAgICAgICAgdGhyZWFkSWQ6IG1hdGNoWzNdLFxuICAgICAgICBzdGFydFRpbWU6ICttYXRjaFs0XSxcbiAgICAgICAgZW5kVGltZTogbWF0Y2hbNV0gIT09IHVuZGVmaW5lZCAmJiArbWF0Y2hbNV0gfHwgdW5kZWZpbmVkLFxuICAgICAgICBlbnRyeUNvdW50OiBtYXRjaFs2XSAhPT0gdW5kZWZpbmVkICYmICttYXRjaFs2XSB8fCB1bmRlZmluZWQsXG4gICAgICAgIGNvbXByZXNzZWQsXG4gICAgICAgIG9yaWdpbktleTogYHBpZCR7bWF0Y2hbMV19X2Jvcm4ke21hdGNoWzJdfV90aHJlYWQke21hdGNoWzNdfV9mcm9tJHttYXRjaFs0XX1gLFxuICAgIH07XG59XG5cbi8qKiBXaGV0aGVyIHRoZSBwaWQgaXMgYWxpdmUgQU5EIGlzIHRoZSBzYW1lIHByb2Nlc3MgaW5jYXJuYXRpb24gdGhlIGZpbGUgbmFtZSBkZXNjcmliZWQuIE9uIExpbnV4IC9wcm9jIGdpdmVzIHRoZSByZWFsIHN0YXJ0IHRpbWU7IGVsc2V3aGVyZSBhIGxpdmUgcGlkIGlzIGNvbnNlcnZhdGl2ZWx5IHRyZWF0ZWQgYXMgdGhlIHNhbWUgcHJvY2VzcyAobmV2ZXIgY29tcHJlc3MgdW5kZXIgYSBydW5uaW5nIHByb2Nlc3MpLiAqL1xuYXN5bmMgZnVuY3Rpb24gaXNQcm9jZXNzQWxpdmUocGlkOiBudW1iZXIsIHByb2Nlc3NTdGFydFRpbWU6IG51bWJlcik6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIHRyeSB7XG4gICAgICAgIHByb2Nlc3Mua2lsbChwaWQsIDApO1xuICAgIH0gY2F0Y2gge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICAgIGxldCBzdGF0ID0gYXdhaXQgZnMucHJvbWlzZXMuc3RhdChgL3Byb2MvJHtwaWR9YCk7XG4gICAgICAgIHJldHVybiBNYXRoLmFicyhzdGF0LmN0aW1lTXMgLSBwcm9jZXNzU3RhcnRUaW1lKSA8IFBST0NFU1NfU1RBUlRfVE9MRVJBTkNFO1xuICAgIH0gY2F0Y2gge1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG59XG5cbi8qKlxuICogQSBzZWFyY2hlciBvdmVyIG9uZSBsb2cgZmlsZSAoYXMgcmVhZEZpbGVDb21wcmVzc2VkIHJldHVybnMgaXQgLSBhbHdheXMgTFo0KSB0aGF0IG5ldmVyIGRlY29kZXNcbiAqIHRoZSB3aG9sZSB0aGluZzogSlNPTiBlc2NhcGVzIGxpbmUgYnJlYWtzIGluc2lkZSBzdHJpbmdzLCBzbyB0aGUgb25seSBBQ1RVQUwgbGluZSBicmVha3MgaW4gdGhlXG4gKiBmaWxlIGFyZSB0aGUgc2VwYXJhdG9ycyBiZXR3ZWVuIGxvZyBzdGF0ZW1lbnRzIC0gYSBwbGFpbiBzdWJzdHJpbmcgc2VhcmNoIG92ZXIgdGhlIHJhdyB0ZXh0LFxuICogYm91bmRlZCBvdXQgdG8gdGhlIHN1cnJvdW5kaW5nIGxpbmUgYnJlYWtzLCBmaW5kcyBleGFjdGx5IHRoZSBtYXRjaGluZyBzdGF0ZW1lbnRzLCBhbmQgb25seSBUSE9TRVxuICogYXJlIGRlY29kZWQgYW5kIHJldHVybmVkIGFzIG9iamVjdHMuIEV2ZXJ5IHNlYXJjaCBzdHJpbmcgbXVzdCBhcHBlYXIgaW4gYSBzdGF0ZW1lbnQncyByYXcgSlNPTiBmb3JcbiAqIGl0IHRvIG1hdGNoIChzbyBzZWFyY2ggZm9yIHZhbHVlcyBhcyB0aGV5IGFyZSBlbmNvZGVkIC0gZS5nLiBxdW90ZWQpLlxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlTG9nU2VhcmNoZXIoZGF0YTogQnVmZmVyKTogKHNlYXJjaGVzOiBzdHJpbmdbXSkgPT4gdW5rbm93bltdIHtcbiAgICBsZXQgdGV4dCA9IExaNC5kZWNvbXByZXNzKGRhdGEpLnRvU3RyaW5nKFwidXRmOFwiKTtcbiAgICByZXR1cm4gc2VhcmNoZXMgPT4ge1xuICAgICAgICBpZiAoIXNlYXJjaGVzLmxlbmd0aCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBQcm92aWRlIGF0IGxlYXN0IG9uZSBzZWFyY2ggc3RyaW5nIChhbiBlbXB0eSBzZWFyY2ggd291bGQganVzdCBiZSBkZWNvZGVMb2dGaWxlKWApO1xuICAgICAgICB9XG4gICAgICAgIGxldCBlbnRyaWVzOiB1bmtub3duW10gPSBbXTtcbiAgICAgICAgbGV0IGluZGV4ID0gMDtcbiAgICAgICAgd2hpbGUgKHRydWUpIHtcbiAgICAgICAgICAgIGxldCBmb3VuZCA9IHRleHQuaW5kZXhPZihzZWFyY2hlc1swXSwgaW5kZXgpO1xuICAgICAgICAgICAgaWYgKGZvdW5kID09PSAtMSkgYnJlYWs7XG4gICAgICAgICAgICBsZXQgbGluZVN0YXJ0ID0gdGV4dC5sYXN0SW5kZXhPZihcIlxcblwiLCBmb3VuZCkgKyAxO1xuICAgICAgICAgICAgbGV0IGxpbmVFbmQgPSB0ZXh0LmluZGV4T2YoXCJcXG5cIiwgZm91bmQpO1xuICAgICAgICAgICAgaWYgKGxpbmVFbmQgPT09IC0xKSB7XG4gICAgICAgICAgICAgICAgbGluZUVuZCA9IHRleHQubGVuZ3RoO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gUGFzdCB0aGlzIGxpbmUgZWl0aGVyIHdheSwgc28gc2V2ZXJhbCBtYXRjaGVzIGluc2lkZSBvbmUgc3RhdGVtZW50IHJldHVybiBpdCBvbmNlXG4gICAgICAgICAgICBpbmRleCA9IGxpbmVFbmQgKyAxO1xuICAgICAgICAgICAgbGV0IGxpbmUgPSB0ZXh0LnNsaWNlKGxpbmVTdGFydCwgbGluZUVuZCk7XG4gICAgICAgICAgICBsZXQgbWF0Y2hlc0FsbCA9IHRydWU7XG4gICAgICAgICAgICBmb3IgKGxldCBvdGhlciBvZiBzZWFyY2hlcy5zbGljZSgxKSkge1xuICAgICAgICAgICAgICAgIGlmICghbGluZS5pbmNsdWRlcyhvdGhlcikpIHtcbiAgICAgICAgICAgICAgICAgICAgbWF0Y2hlc0FsbCA9IGZhbHNlO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoIW1hdGNoZXNBbGwpIGNvbnRpbnVlO1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBlbnRyaWVzLnB1c2goSlNPTi5wYXJzZShsaW5lKSk7XG4gICAgICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAgICAgICAvLyBBIHRvcm4gZmluYWwgbGluZSAodGhlIHdyaXRlciBjcmFzaGVkIG1pZC1hcHBlbmQpXG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGVudHJpZXM7XG4gICAgfTtcbn1cblxuLyoqIERlY29kZXMgYSBsb2cgZmlsZSdzIGJ5dGVzIChhcyByZWFkRmlsZUNvbXByZXNzZWQgcmV0dXJucyB0aGVtIC0gYWx3YXlzIExaNCkgYmFjayBpbnRvIHRoZSBsb2dnZWQgb2JqZWN0cy4gQSB0b3JuIGZpbmFsIGxpbmUgKHRoZSB3cml0ZXIgY3Jhc2hlZCBtaWQtYXBwZW5kKSBpcyBza2lwcGVkLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRlY29kZUxvZ0ZpbGUoZGF0YTogQnVmZmVyKTogdW5rbm93bltdIHtcbiAgICBsZXQgdGV4dCA9IExaNC5kZWNvbXByZXNzKGRhdGEpLnRvU3RyaW5nKFwidXRmOFwiKTtcbiAgICBsZXQgZW50cmllczogdW5rbm93bltdID0gW107XG4gICAgZm9yIChsZXQgbGluZSBvZiB0ZXh0LnNwbGl0KFwiXFxuXCIpKSB7XG4gICAgICAgIGlmICghbGluZSkgY29udGludWU7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBlbnRyaWVzLnB1c2goSlNPTi5wYXJzZShsaW5lKSk7XG4gICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGVudHJpZXM7XG59XG5cbmV4cG9ydCBjbGFzcyBTdHJlYW1pbmdMb2dzIHtcbiAgICBjb25zdHJ1Y3Rvcihwcml2YXRlIGNvbmZpZzoge1xuICAgICAgICBmb2xkZXI6IHN0cmluZztcbiAgICAgICAgLyoqIEluY2x1ZGVkIGluIGV2ZXJ5IGZpbGUgbmFtZSAtIHNlZSBtaXNjL2h0dHBzL2NlcnRzLnRzIGdldE93blRocmVhZElkLiBQcm9jZXNzZXMgd2l0aG91dCBvbmUgd3JpdGUgXCJub25lXCIuICovXG4gICAgICAgIHRocmVhZElkPzogc3RyaW5nO1xuICAgICAgICBtYXhGaWxlQnl0ZXM/OiBudW1iZXI7XG4gICAgICAgIHRvdGFsTGltaXRCeXRlcz86IG51bWJlcjtcbiAgICB9KSB7XG4gICAgICAgIHRoaXMuc2NoZWR1bGVNYWludGVuYW5jZSgpO1xuICAgIH1cblxuICAgIHByaXZhdGUgcHJvY2Vzc1N0YXJ0VGltZSA9IE1hdGgucm91bmQoRGF0ZS5ub3coKSAtIHByb2Nlc3MudXB0aW1lKCkgKiAxMDAwKTtcbiAgICBwcml2YXRlIHRocmVhZElkID0gKHRoaXMuY29uZmlnLnRocmVhZElkIHx8IFwibm9uZVwiKS5yZXBsYWNlKC9bXlxcdy1dL2csIFwiX1wiKTtcbiAgICBwcml2YXRlIHBlbmRpbmc6IHN0cmluZ1tdID0gW107XG4gICAgcHJpdmF0ZSBmbHVzaFRpbWVyOiBSZXR1cm5UeXBlPHR5cGVvZiBzZXRUaW1lb3V0PiB8IHVuZGVmaW5lZDtcbiAgICBwcml2YXRlIG1haW50ZW5hbmNlVGltZXI6IFJldHVyblR5cGU8dHlwZW9mIHNldFRpbWVvdXQ+IHwgdW5kZWZpbmVkO1xuICAgIC8vIFNlcmlhbGl6ZXMgZXZlcnl0aGluZyB0aGF0IHRvdWNoZXMgdGhlIGN1cnJlbnQgZmlsZSwgc28gYXBwZW5kcywgcm90YXRpb24sIGFuZCBpbi1tZW1vcnkgY29tcHJlc3Npb24gb2YgdGhlIGxpdmUgZmlsZSBuZXZlciBpbnRlcmxlYXZlXG4gICAgcHJpdmF0ZSB3cml0ZUNoYWluOiBQcm9taXNlPHZvaWQ+ID0gUHJvbWlzZS5yZXNvbHZlKCk7XG4gICAgcHJpdmF0ZSBjdXJyZW50UGF0aDogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICAgIHByaXZhdGUgY3VycmVudEJ5dGVzID0gMDtcbiAgICBwcml2YXRlIGRpc3Bvc2VkID0gZmFsc2U7XG5cbiAgICAvKiogUXVldWVzIG9uZSBlbnRyeSAoYW55dGhpbmcgSlNPTi1zZXJpYWxpemFibGUpLiBOZXZlciB0aHJvd3MgLSBsb2dnaW5nIG11c3Qgbm90IHRha2UgZG93biB0aGUgY2FsbGVyLiAqL1xuICAgIHB1YmxpYyBsb2coZW50cnk6IHVua25vd24pOiB2b2lkIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGlmICh0aGlzLnBlbmRpbmcubGVuZ3RoID49IE1BWF9QRU5ESU5HX0VOVFJJRVMpIHtcbiAgICAgICAgICAgICAgICAvLyBUaGUgZGlzayBpcyBicm9rZW4gKGZsdXNoZXMgYXJlIGZhaWxpbmcpIC0gZHJvcHBpbmcgdGhlIG9sZGVzdCBiZWF0cyBncm93aW5nIGZvcmV2ZXJcbiAgICAgICAgICAgICAgICB0aGlzLnBlbmRpbmcuc2hpZnQoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMucGVuZGluZy5wdXNoKEpTT04uc3RyaW5naWZ5KGVudHJ5KSk7XG4gICAgICAgICAgICB0aGlzLnNjaGVkdWxlRmx1c2goKTtcbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKGBDb3VsZCBub3QgcXVldWUgYSBsb2cgZW50cnkgKCR7KGUgYXMgRXJyb3IpLm1lc3NhZ2V9KWApO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBzY2hlZHVsZUZsdXNoKCk6IHZvaWQge1xuICAgICAgICBpZiAodGhpcy5mbHVzaFRpbWVyICE9PSB1bmRlZmluZWQgfHwgdGhpcy5kaXNwb3NlZCkgcmV0dXJuO1xuICAgICAgICB0aGlzLmZsdXNoVGltZXIgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgIHRoaXMuZmx1c2hUaW1lciA9IHVuZGVmaW5lZDtcbiAgICAgICAgICAgIHZvaWQgdGhpcy5mbHVzaCgpLmNhdGNoKChlOiBFcnJvcikgPT4gY29uc29sZS53YXJuKGBGbHVzaGluZyBsb2dzIGluICR7dGhpcy5jb25maWcuZm9sZGVyfSBmYWlsZWQ6ICR7ZS5zdGFjayA/PyBlfWApKTtcbiAgICAgICAgfSwgRkxVU0hfREVMQVkpO1xuICAgICAgICAodGhpcy5mbHVzaFRpbWVyIGFzIHsgdW5yZWY/OiAoKSA9PiB2b2lkIH0pLnVucmVmPy4oKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgZmx1c2goKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGxldCByZXN1bHQgPSB0aGlzLndyaXRlQ2hhaW4udGhlbigoKSA9PiB0aGlzLndyaXRlUGVuZGluZygpKTtcbiAgICAgICAgLy8gVGhlIGNoYWluIG9ubHkgc2VyaWFsaXplczsgYSBmYWlsdXJlIGJlbG9uZ3MgdG8gdGhlIGNhbGxlciB0aGF0IGFza2VkIGZvciB0aGlzIGZsdXNoLCBub3QgZXZlcnkgb25lIGFmdGVyIGl0XG4gICAgICAgIHRoaXMud3JpdGVDaGFpbiA9IHJlc3VsdC5jYXRjaCgoKSA9PiB7IH0pO1xuICAgICAgICBhd2FpdCByZXN1bHQ7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyB3cml0ZVBlbmRpbmcoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGlmICghdGhpcy5wZW5kaW5nLmxlbmd0aCkgcmV0dXJuO1xuICAgICAgICBsZXQgbGluZXMgPSB0aGlzLnBlbmRpbmc7XG4gICAgICAgIHRoaXMucGVuZGluZyA9IFtdO1xuICAgICAgICBsZXQgZGF0YSA9IGxpbmVzLmpvaW4oXCJcXG5cIikgKyBcIlxcblwiO1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgaWYgKCF0aGlzLmN1cnJlbnRQYXRoKSB7XG4gICAgICAgICAgICAgICAgYXdhaXQgZnMucHJvbWlzZXMubWtkaXIodGhpcy5jb25maWcuZm9sZGVyLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICAgICAgICAgICAgICAvLyBSb3VuZGVkLCBhcyBhIHNoaW1tZWQgRGF0ZS5ub3cgY2FuIHJldHVybiBmcmFjdGlvbmFsIG1pbGxpc2Vjb25kcywgYW5kIGEgXCIuXCIgaW4gdGhlIG5hbWUgYnJlYWtzIG5hbWUgcGFyc2luZyBmb3IgYW55IHJlYWRlciB3aXRob3V0IHRoZSBmcmFjdGlvbmFsLXRvbGVyYW50IHJlZ2V4XG4gICAgICAgICAgICAgICAgdGhpcy5jdXJyZW50UGF0aCA9IHBhdGguam9pbih0aGlzLmNvbmZpZy5mb2xkZXIsIGBwaWQke3Byb2Nlc3MucGlkfV9ib3JuJHt0aGlzLnByb2Nlc3NTdGFydFRpbWV9X3RocmVhZCR7dGhpcy50aHJlYWRJZH1fZnJvbSR7TWF0aC5yb3VuZChEYXRlLm5vdygpKX0ke1VOQ09NUFJFU1NFRF9FWFR9YCk7XG4gICAgICAgICAgICAgICAgdGhpcy5jdXJyZW50Qnl0ZXMgPSAwO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgYXdhaXQgZnMucHJvbWlzZXMuYXBwZW5kRmlsZSh0aGlzLmN1cnJlbnRQYXRoLCBkYXRhKTtcbiAgICAgICAgICAgIHRoaXMuY3VycmVudEJ5dGVzICs9IEJ1ZmZlci5ieXRlTGVuZ3RoKGRhdGEpO1xuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICB0aGlzLnBlbmRpbmcgPSBsaW5lcy5jb25jYXQodGhpcy5wZW5kaW5nKTtcbiAgICAgICAgICAgIHRoaXMuc2NoZWR1bGVGbHVzaCgpO1xuICAgICAgICAgICAgdGhyb3cgZTtcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5jdXJyZW50Qnl0ZXMgPj0gKHRoaXMuY29uZmlnLm1heEZpbGVCeXRlcyB8fCBNQVhfRklMRV9CWVRFUykpIHtcbiAgICAgICAgICAgIGxldCBmaW5pc2hlZCA9IHRoaXMuY3VycmVudFBhdGg7XG4gICAgICAgICAgICB0aGlzLmN1cnJlbnRQYXRoID0gdW5kZWZpbmVkO1xuICAgICAgICAgICAgdGhpcy5jdXJyZW50Qnl0ZXMgPSAwO1xuICAgICAgICAgICAgdm9pZCB0aGlzLmNvbXByZXNzRmlsZShmaW5pc2hlZCkuY2F0Y2goKGU6IEVycm9yKSA9PiB7XG4gICAgICAgICAgICAgICAgLy8gTGVmdCB1bmNvbXByZXNzZWQ6IG1haW50ZW5hbmNlIChvdXJzIGFmdGVyIGRlYXRoLCBvciBhbnkgcGVlcidzKSBjb21wcmVzc2VzIGl0IGxhdGVyXG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKGBDb21wcmVzc2luZyByb3RhdGVkIGxvZyAke2ZpbmlzaGVkfSBmYWlsZWQ6ICR7ZS5zdGFjayA/PyBlfWApO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDb21wcmVzc2VzIG9uZSBmaW5pc2hlZCBsb2cgZmlsZTogTFo0IGludG8gYSB0ZW1wIFNVQkZPTERFUiAobmV2ZXIgYSB0ZW1wIG5hbWUgaW4gdGhlIGxvZ1xuICAgICAqIGZvbGRlciBpdHNlbGYgLSBhIGNyYXNoZWQgd3JpdGUgbXVzdCBub3QgbGVhdmUgYSBjb3JydXB0IGZpbGUgd2hlcmUgdGhlIG1haW50ZW5hbmNlIHNjYW5zIGFyZSksXG4gICAgICogcmVuYW1lZCBpbnRvIHBsYWNlIChzYW1lIGRyaXZlLCBzbyB0aGUgcmVuYW1lIGlzIGF0b21pYyksIHZlcmlmaWVkLCBhbmQgb25seSBUSEVOIGlzIHRoZVxuICAgICAqIG9yaWdpbmFsIGRlbGV0ZWQgLSBhdCBubyBwb2ludCBpcyB0aGUgZGF0YSBpbiBmZXdlciB0aGFuIG9uZSBjb21wbGV0ZSBmaWxlLlxuICAgICAqL1xuICAgIHByaXZhdGUgYXN5bmMgY29tcHJlc3NGaWxlKGZpbGVQYXRoOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgbGV0IG5hbWUgPSBwYXRoLmJhc2VuYW1lKGZpbGVQYXRoKTtcbiAgICAgICAgbGV0IHBhcnNlZCA9IHBhcnNlTG9nRmlsZU5hbWUobmFtZSk7XG4gICAgICAgIGlmICghcGFyc2VkIHx8IHBhcnNlZC5jb21wcmVzc2VkKSByZXR1cm47XG4gICAgICAgIGxldCByYXcgPSBhd2FpdCBmcy5wcm9taXNlcy5yZWFkRmlsZShmaWxlUGF0aCk7XG4gICAgICAgIGxldCBlbnRyeUNvdW50ID0gMDtcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCByYXcubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGlmIChyYXdbaV0gPT09IDEwKSBlbnRyeUNvdW50Kys7XG4gICAgICAgIH1cbiAgICAgICAgbGV0IGNvbXByZXNzZWQgPSBMWjQuY29tcHJlc3MocmF3KTtcbiAgICAgICAgbGV0IGlkID0gTWF0aC5yYW5kb20oKS50b1N0cmluZygzNikuc2xpY2UoMiwgMTApO1xuICAgICAgICBsZXQgZmluYWxOYW1lID0gYCR7cGFyc2VkLm9yaWdpbktleX1fdG8ke01hdGgucm91bmQoRGF0ZS5ub3coKSl9X2NvdW50JHtlbnRyeUNvdW50fV9pZCR7aWR9JHtDT01QUkVTU0VEX0VYVH1gO1xuICAgICAgICBsZXQgdGVtcERpciA9IHBhdGguam9pbih0aGlzLmNvbmZpZy5mb2xkZXIsIFwiLnRtcFwiKTtcbiAgICAgICAgYXdhaXQgZnMucHJvbWlzZXMubWtkaXIodGVtcERpciwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgICAgIGxldCB0ZW1wUGF0aCA9IHBhdGguam9pbih0ZW1wRGlyLCBmaW5hbE5hbWUpO1xuICAgICAgICBsZXQgZmluYWxQYXRoID0gcGF0aC5qb2luKHRoaXMuY29uZmlnLmZvbGRlciwgZmluYWxOYW1lKTtcbiAgICAgICAgYXdhaXQgZnMucHJvbWlzZXMud3JpdGVGaWxlKHRlbXBQYXRoLCBjb21wcmVzc2VkKTtcbiAgICAgICAgYXdhaXQgZnMucHJvbWlzZXMucmVuYW1lKHRlbXBQYXRoLCBmaW5hbFBhdGgpO1xuICAgICAgICAvLyBQYXJhbm9pYSBiZWZvcmUgZGVsZXRpbmcgdGhlIG9ubHkgb3RoZXIgY29weVxuICAgICAgICBhd2FpdCBmcy5wcm9taXNlcy5zdGF0KGZpbmFsUGF0aCk7XG4gICAgICAgIGF3YWl0IGZzLnByb21pc2VzLnVubGluayhmaWxlUGF0aCk7XG4gICAgICAgIGNvbnNvbGUubG9nKGBDb21wcmVzc2VkIGxvZyAke25hbWV9IC0+ICR7ZmluYWxOYW1lfSAoJHtyYXcubGVuZ3RofSAtPiAke2NvbXByZXNzZWQubGVuZ3RofSBieXRlcywgJHtlbnRyeUNvdW50fSBlbnRyaWVzKWApO1xuICAgIH1cblxuICAgIC8vIOKUgOKUgCBsaXN0aW5nIC8gcmVhZGluZyDilIDilIBcblxuICAgIHB1YmxpYyBhc3luYyBsaXN0RmlsZXMoKTogUHJvbWlzZTxMb2dGaWxlSW5mb1tdPiB7XG4gICAgICAgIGxldCBuYW1lczogc3RyaW5nW107XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBuYW1lcyA9IGF3YWl0IGZzLnByb21pc2VzLnJlYWRkaXIodGhpcy5jb25maWcuZm9sZGVyKTtcbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgaWYgKChlIGFzIHsgY29kZT86IHN0cmluZyB9KS5jb2RlID09PSBcIkVOT0VOVFwiKSByZXR1cm4gW107XG4gICAgICAgICAgICB0aHJvdyBlO1xuICAgICAgICB9XG4gICAgICAgIGxldCBmaWxlczogTG9nRmlsZUluZm9bXSA9IFtdO1xuICAgICAgICBmb3IgKGxldCBuYW1lIG9mIG5hbWVzKSB7XG4gICAgICAgICAgICBsZXQgcGFyc2VkID0gcGFyc2VMb2dGaWxlTmFtZShuYW1lKTtcbiAgICAgICAgICAgIGlmICghcGFyc2VkKSBjb250aW51ZTtcbiAgICAgICAgICAgIGxldCBzdGF0ID0gYXdhaXQgZnMucHJvbWlzZXMuc3RhdChwYXRoLmpvaW4odGhpcy5jb25maWcuZm9sZGVyLCBuYW1lKSkuY2F0Y2goKCkgPT4gdW5kZWZpbmVkKTtcbiAgICAgICAgICAgIGlmICghc3RhdCkgY29udGludWU7XG4gICAgICAgICAgICBsZXQgYWN0aXZlID0gIXBhcnNlZC5jb21wcmVzc2VkICYmIGF3YWl0IGlzUHJvY2Vzc0FsaXZlKHBhcnNlZC5waWQsIHBhcnNlZC5wcm9jZXNzU3RhcnRUaW1lKTtcbiAgICAgICAgICAgIGZpbGVzLnB1c2goe1xuICAgICAgICAgICAgICAgIG5hbWUsXG4gICAgICAgICAgICAgICAgc2l6ZTogc3RhdC5zaXplLFxuICAgICAgICAgICAgICAgIHBpZDogcGFyc2VkLnBpZCxcbiAgICAgICAgICAgICAgICBwcm9jZXNzU3RhcnRUaW1lOiBwYXJzZWQucHJvY2Vzc1N0YXJ0VGltZSxcbiAgICAgICAgICAgICAgICB0aHJlYWRJZDogcGFyc2VkLnRocmVhZElkLFxuICAgICAgICAgICAgICAgIHN0YXJ0VGltZTogcGFyc2VkLnN0YXJ0VGltZSxcbiAgICAgICAgICAgICAgICBlbmRUaW1lOiBwYXJzZWQuZW5kVGltZSxcbiAgICAgICAgICAgICAgICBlbnRyeUNvdW50OiBwYXJzZWQuZW50cnlDb3VudCxcbiAgICAgICAgICAgICAgICBjb21wcmVzc2VkOiBwYXJzZWQuY29tcHJlc3NlZCxcbiAgICAgICAgICAgICAgICBhY3RpdmUsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICBzb3J0KGZpbGVzLCB4ID0+IHguc3RhcnRUaW1lKTtcbiAgICAgICAgcmV0dXJuIGZpbGVzO1xuICAgIH1cblxuICAgIC8qKiBPbmUgZmlsZSdzIGJ5dGVzLCBBTFdBWVMgTFo0LWNvbXByZXNzZWQ6IGNvbXByZXNzZWQgZmlsZXMgYXJlIHNlbnQgYXMtaXMsIGEgc3RpbGwtc3RyZWFtaW5nIGZpbGUgaXMgZmx1c2hlZCBhbmQgY29tcHJlc3NlZCBpbiBtZW1vcnkgKHNtYWxsZXIgb3ZlciB0aGUgbmV0d29yazsgZGVjb2RlTG9nRmlsZSBoYW5kbGVzIGJvdGggaWRlbnRpY2FsbHkpLiAqL1xuICAgIHB1YmxpYyBhc3luYyByZWFkRmlsZUNvbXByZXNzZWQobmFtZTogc3RyaW5nKTogUHJvbWlzZTxCdWZmZXI+IHtcbiAgICAgICAgbGV0IHBhcnNlZCA9IHBhcnNlTG9nRmlsZU5hbWUobmFtZSk7XG4gICAgICAgIGlmICghcGFyc2VkIHx8IG5hbWUuaW5jbHVkZXMoXCIvXCIpIHx8IG5hbWUuaW5jbHVkZXMoXCJcXFxcXCIpKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYE5vdCBhIGxvZyBmaWxlIG5hbWU6ICR7SlNPTi5zdHJpbmdpZnkobmFtZS5zbGljZSgwLCAyMDApKX1gKTtcbiAgICAgICAgfVxuICAgICAgICBsZXQgZmlsZVBhdGggPSBwYXRoLmpvaW4odGhpcy5jb25maWcuZm9sZGVyLCBuYW1lKTtcbiAgICAgICAgaWYgKHBhcnNlZC5jb21wcmVzc2VkKSB7XG4gICAgICAgICAgICByZXR1cm4gYXdhaXQgZnMucHJvbWlzZXMucmVhZEZpbGUoZmlsZVBhdGgpO1xuICAgICAgICB9XG4gICAgICAgIC8vIEZsdXNoIHNvIG91ciBvd24gbGl2ZSBmaWxlIGluY2x1ZGVzIGV2ZXJ5dGhpbmcgbG9nZ2VkIHVwIHRvIG5vd1xuICAgICAgICBhd2FpdCB0aGlzLmZsdXNoKCk7XG4gICAgICAgIHJldHVybiBMWjQuY29tcHJlc3MoYXdhaXQgZnMucHJvbWlzZXMucmVhZEZpbGUoZmlsZVBhdGgpKTtcbiAgICB9XG5cbiAgICAvLyDilIDilIAgbWFpbnRlbmFuY2Ug4pSA4pSAXG5cbiAgICBwcml2YXRlIHNjaGVkdWxlTWFpbnRlbmFuY2UoKTogdm9pZCB7XG4gICAgICAgIGlmICh0aGlzLmRpc3Bvc2VkKSByZXR1cm47XG4gICAgICAgIGxldCB3YWl0ID0gTUFJTlRFTkFOQ0VfTUlOX0lOVEVSVkFMICsgTWF0aC5yYW5kb20oKSAqIChNQUlOVEVOQU5DRV9NQVhfSU5URVJWQUwgLSBNQUlOVEVOQU5DRV9NSU5fSU5URVJWQUwpO1xuICAgICAgICB0aGlzLm1haW50ZW5hbmNlVGltZXIgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgIHZvaWQgdGhpcy5ydW5NYWludGVuYW5jZSgpLmNhdGNoKChlOiBFcnJvcikgPT4gY29uc29sZS53YXJuKGBMb2cgbWFpbnRlbmFuY2Ugb2YgJHt0aGlzLmNvbmZpZy5mb2xkZXJ9IGZhaWxlZDogJHtlLnN0YWNrID8/IGV9YCkpLmZpbmFsbHkoKCkgPT4gdGhpcy5zY2hlZHVsZU1haW50ZW5hbmNlKCkpO1xuICAgICAgICB9LCB3YWl0KTtcbiAgICAgICAgKHRoaXMubWFpbnRlbmFuY2VUaW1lciBhcyB7IHVucmVmPzogKCkgPT4gdm9pZCB9KS51bnJlZj8uKCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU2hhcmVkIHVwa2VlcCBvZiB0aGUgZm9sZGVyIC0gZXZlcnkgcHJvY2VzcyB1c2luZyBpdCBydW5zIHRoaXMsIHNvIG5vdGhpbmcgZGVwZW5kcyBvbiBhbnkgb25lXG4gICAgICogcHJvY2VzcyBzdXJ2aXZpbmc6IGRlYWQgcHJvY2Vzc2VzJyB1bmNvbXByZXNzZWQgZmlsZXMgYXJlIGNvbXByZXNzZWQgZm9yIHRoZW07IGR1cGxpY2F0ZVxuICAgICAqIGNvbXByZXNzaW9ucyBvZiBvbmUgc3RyZWFtICh0d28gbWFpbnRhaW5lcnMgcmFjaW5nKSBhcmUgcmVzb2x2ZWQgYnkga2VlcGluZyB0aGUgT0xERVNUIGNvcHk7XG4gICAgICogYW4gdW5jb21wcmVzc2VkIG9yaWdpbmFsIHdob3NlIGNvbXByZXNzaW9uIGFscmVhZHkgZXhpc3RzIGlzIGRlbGV0ZWQgKGl0cyBjb21wcmVzc29yIGRpZWRcbiAgICAgKiBiZXR3ZWVuIHJlbmFtZSBhbmQgdW5saW5rKTsgYW5kIHRoZSBmb2xkZXIncyB0b3RhbCBzaXplIGlzIGJyb3VnaHQgdW5kZXIgaXRzIGJ1ZGdldCBieVxuICAgICAqIGRlbGV0aW5nIHRoZSBvbGRlc3QgZmlsZXMuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHJ1bk1haW50ZW5hbmNlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBsZXQgbmFtZXM6IHN0cmluZ1tdO1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgbmFtZXMgPSBhd2FpdCBmcy5wcm9taXNlcy5yZWFkZGlyKHRoaXMuY29uZmlnLmZvbGRlcik7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIGlmICgoZSBhcyB7IGNvZGU/OiBzdHJpbmcgfSkuY29kZSA9PT0gXCJFTk9FTlRcIikgcmV0dXJuO1xuICAgICAgICAgICAgdGhyb3cgZTtcbiAgICAgICAgfVxuICAgICAgICBsZXQgcGFyc2VkQnlOYW1lID0gbmV3IE1hcDxzdHJpbmcsIFBhcnNlZE5hbWU+KCk7XG4gICAgICAgIGZvciAobGV0IG5hbWUgb2YgbmFtZXMpIHtcbiAgICAgICAgICAgIGxldCBwYXJzZWQgPSBwYXJzZUxvZ0ZpbGVOYW1lKG5hbWUpO1xuICAgICAgICAgICAgaWYgKHBhcnNlZCkgcGFyc2VkQnlOYW1lLnNldChuYW1lLCBwYXJzZWQpO1xuICAgICAgICB9XG4gICAgICAgIGxldCBjb21wcmVzc2VkT3JpZ2lucyA9IG5ldyBNYXA8c3RyaW5nLCBzdHJpbmdbXT4oKTtcbiAgICAgICAgZm9yIChsZXQgW25hbWUsIHBhcnNlZF0gb2YgcGFyc2VkQnlOYW1lKSB7XG4gICAgICAgICAgICBpZiAoIXBhcnNlZC5jb21wcmVzc2VkKSBjb250aW51ZTtcbiAgICAgICAgICAgIGxldCBsaXN0ID0gY29tcHJlc3NlZE9yaWdpbnMuZ2V0KHBhcnNlZC5vcmlnaW5LZXkpO1xuICAgICAgICAgICAgaWYgKCFsaXN0KSB7XG4gICAgICAgICAgICAgICAgbGlzdCA9IFtdO1xuICAgICAgICAgICAgICAgIGNvbXByZXNzZWRPcmlnaW5zLnNldChwYXJzZWQub3JpZ2luS2V5LCBsaXN0KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGxpc3QucHVzaChuYW1lKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBEdXBsaWNhdGUgY29tcHJlc3Npb25zOiBrZWVwIHRoZSBvbGRlc3QgKGZpcnN0IHdyaXR0ZW4pLCBkZWxldGUgdGhlIHJlc3RcbiAgICAgICAgZm9yIChsZXQgW29yaWdpbiwgbGlzdF0gb2YgY29tcHJlc3NlZE9yaWdpbnMpIHtcbiAgICAgICAgICAgIGlmIChsaXN0Lmxlbmd0aCA8IDIpIGNvbnRpbnVlO1xuICAgICAgICAgICAgc29ydChsaXN0LCBuYW1lID0+IHBhcnNlZEJ5TmFtZS5nZXQobmFtZSk/LmVuZFRpbWUgfHwgMCk7XG4gICAgICAgICAgICBmb3IgKGxldCBkdXBsaWNhdGUgb2YgbGlzdC5zbGljZSgxKSkge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGBEZWxldGluZyBkdXBsaWNhdGUgY29tcHJlc3NlZCBsb2cgJHtkdXBsaWNhdGV9IChhbm90aGVyIG1haW50YWluZXIgY29tcHJlc3NlZCAke29yaWdpbn0gZmlyc3QsIGtlZXBpbmcgJHtsaXN0WzBdfSlgKTtcbiAgICAgICAgICAgICAgICBhd2FpdCBmcy5wcm9taXNlcy51bmxpbmsocGF0aC5qb2luKHRoaXMuY29uZmlnLmZvbGRlciwgZHVwbGljYXRlKSkuY2F0Y2goKCkgPT4geyB9KTtcbiAgICAgICAgICAgICAgICBwYXJzZWRCeU5hbWUuZGVsZXRlKGR1cGxpY2F0ZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZm9yIChsZXQgW25hbWUsIHBhcnNlZF0gb2YgcGFyc2VkQnlOYW1lKSB7XG4gICAgICAgICAgICBpZiAocGFyc2VkLmNvbXByZXNzZWQpIGNvbnRpbnVlO1xuICAgICAgICAgICAgbGV0IGZpbGVQYXRoID0gcGF0aC5qb2luKHRoaXMuY29uZmlnLmZvbGRlciwgbmFtZSk7XG4gICAgICAgICAgICBpZiAoZmlsZVBhdGggPT09IHRoaXMuY3VycmVudFBhdGgpIGNvbnRpbnVlO1xuICAgICAgICAgICAgaWYgKGNvbXByZXNzZWRPcmlnaW5zLmhhcyhwYXJzZWQub3JpZ2luS2V5KSkge1xuICAgICAgICAgICAgICAgIC8vIEFscmVhZHkgY29tcHJlc3NlZCBieSBzb21lb25lIHdob3NlIGNsZWFudXAgZGllZCBiZXR3ZWVuIHJlbmFtZSBhbmQgdW5saW5rXG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coYERlbGV0aW5nIHVuY29tcHJlc3NlZCBsb2cgJHtuYW1lfTogaXRzIGNvbXByZXNzZWQgdmVyc2lvbiBhbHJlYWR5IGV4aXN0c2ApO1xuICAgICAgICAgICAgICAgIGF3YWl0IGZzLnByb21pc2VzLnVubGluayhmaWxlUGF0aCkuY2F0Y2goKCkgPT4geyB9KTtcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIEEgbGl2ZSBwcm9jZXNzIG93bnMgaXRzIHVuY29tcHJlc3NlZCBmaWxlOyBvbmx5IGEgREVBRCBwcm9jZXNzJ3MgZmlsZXMgYXJlIGNvbXByZXNzZWQgZm9yIGl0IChvdXIgb3duIHBpZCBhbHdheXMgcmVhZHMgYXMgYWxpdmUsIHdoaWNoIGNvcnJlY3RseSBwcm90ZWN0cyBvdXIgY3VycmVudCBmaWxlIC0gb3VyIHJvdGF0ZWQgbGVmdG92ZXJzIHdlcmUgZXhjbHVkZWQgYWJvdmUgb25seSBpZiBjdXJyZW50LCBzbyBjb21wcmVzcyB0aG9zZSB0b28pXG4gICAgICAgICAgICBpZiAocGFyc2VkLnBpZCAhPT0gcHJvY2Vzcy5waWQgJiYgYXdhaXQgaXNQcm9jZXNzQWxpdmUocGFyc2VkLnBpZCwgcGFyc2VkLnByb2Nlc3NTdGFydFRpbWUpKSBjb250aW51ZTtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgYXdhaXQgdGhpcy5jb21wcmVzc0ZpbGUoZmlsZVBhdGgpO1xuICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybihgQ29tcHJlc3NpbmcgZGVhZCBwcm9jZXNzJ3MgbG9nICR7bmFtZX0gZmFpbGVkICh0aGUgbmV4dCBtYWludGVuYW5jZSBwYXNzIHJldHJpZXMpOiAkeyhlIGFzIEVycm9yKS5zdGFjayA/PyBlfWApO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGF3YWl0IHRoaXMuZW5mb3JjZVRvdGFsTGltaXQoKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGVuZm9yY2VUb3RhbExpbWl0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBsZXQgbGltaXQgPSB0aGlzLmNvbmZpZy50b3RhbExpbWl0Qnl0ZXMgfHwgVE9UQUxfTElNSVRfQllURVM7XG4gICAgICAgIGxldCBmaWxlcyA9IGF3YWl0IHRoaXMubGlzdEZpbGVzKCk7XG4gICAgICAgIGxldCB0b3RhbCA9IGZpbGVzLnJlZHVjZSgoc3VtLCB4KSA9PiBzdW0gKyB4LnNpemUsIDApO1xuICAgICAgICBpZiAodG90YWwgPD0gbGltaXQpIHJldHVybjtcbiAgICAgICAgLy8gT2xkZXN0IGZpcnN0LCBieSB0aGUgZW5kIG9mIHdoYXQgdGhleSBjb3ZlcjsgbGl2ZSBmaWxlcyBsYXN0ICh0aGV5IGFyZSB0aGUgbmV3ZXN0IGRhdGEgYnkgZGVmaW5pdGlvbilcbiAgICAgICAgbGV0IGRlbGV0YWJsZSA9IGZpbGVzLmZpbHRlcih4ID0+ICF4LmFjdGl2ZSk7XG4gICAgICAgIHNvcnQoZGVsZXRhYmxlLCB4ID0+IHguZW5kVGltZSB8fCB4LnN0YXJ0VGltZSk7XG4gICAgICAgIGxldCBkZWxldGVkID0gMDtcbiAgICAgICAgbGV0IGRlbGV0ZWRCeXRlcyA9IDA7XG4gICAgICAgIGZvciAobGV0IGZpbGUgb2YgZGVsZXRhYmxlKSB7XG4gICAgICAgICAgICBpZiAodG90YWwgPD0gbGltaXQpIGJyZWFrO1xuICAgICAgICAgICAgYXdhaXQgZnMucHJvbWlzZXMudW5saW5rKHBhdGguam9pbih0aGlzLmNvbmZpZy5mb2xkZXIsIGZpbGUubmFtZSkpLmNhdGNoKCgpID0+IHsgfSk7XG4gICAgICAgICAgICB0b3RhbCAtPSBmaWxlLnNpemU7XG4gICAgICAgICAgICBkZWxldGVkKys7XG4gICAgICAgICAgICBkZWxldGVkQnl0ZXMgKz0gZmlsZS5zaXplO1xuICAgICAgICB9XG4gICAgICAgIGNvbnNvbGUubG9nKGBMb2dzIGluICR7dGhpcy5jb25maWcuZm9sZGVyfSBvdXRncmV3IHRoZWlyICR7bGltaXR9IGJ5dGUgYnVkZ2V0OiBkZWxldGVkIHRoZSAke2RlbGV0ZWR9IG9sZGVzdCBmaWxlcyAoJHtkZWxldGVkQnl0ZXN9IGJ5dGVzKSwgJHt0b3RhbH0gYnl0ZXMgcmVtYWluYCk7XG4gICAgfVxuXG4gICAgcHVibGljIGRpc3Bvc2UoKTogdm9pZCB7XG4gICAgICAgIHRoaXMuZGlzcG9zZWQgPSB0cnVlO1xuICAgICAgICBpZiAodGhpcy5mbHVzaFRpbWVyKSB7XG4gICAgICAgICAgICBjbGVhclRpbWVvdXQodGhpcy5mbHVzaFRpbWVyKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5tYWludGVuYW5jZVRpbWVyKSB7XG4gICAgICAgICAgICBjbGVhclRpbWVvdXQodGhpcy5tYWludGVuYW5jZVRpbWVyKTtcbiAgICAgICAgfVxuICAgIH1cbn1cbiJdfQ==
399
+ /* _JS_SOURCE_HASH = "e469b5a680d38d12efd2071191e27c81bcbebfb6ac34330bdf9686d27e2b89da"; */