socket-function 1.2.2 → 1.2.5

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.
Files changed (52) hide show
  1. package/SocketFunction.d.ts +1 -1
  2. package/SocketFunctionTypes.d.ts +1 -1
  3. package/hot/HotReloadController.d.ts +3 -3
  4. package/index.d.ts +25 -10
  5. package/package.json +1 -1
  6. package/require/CSSShim.d.ts +1 -1
  7. package/require/RequireController.d.ts +2 -2
  8. package/require/compileFlags.d.ts +1 -1
  9. package/require/require.d.ts +1 -1
  10. package/src/callManager.d.ts +1 -1
  11. package/src/webSocketServer.ts +44 -35
  12. package/test.ts +5 -11
  13. package/time/trueTimeShim.d.ts +16 -1
  14. package/time/trueTimeShim.ts +43 -7
  15. package/dist/SetProcessVariables.ts.cache +0 -9
  16. package/dist/SocketFunction.ts.cache +0 -467
  17. package/hot/dist/HotReloadController.ts.cache +0 -252
  18. package/require/dist/RequireController.ts.cache +0 -386
  19. package/require/dist/compileFlags.ts.cache +0 -41
  20. package/require/dist/extMapper.ts.cache +0 -368
  21. package/require/dist/require.ts.cache +0 -676
  22. package/src/JSONLACKS/dist/JSONLACKS.ts.cache +0 -469
  23. package/src/dist/CallFactory.ts.cache +0 -930
  24. package/src/dist/Zip.ts.cache +0 -156
  25. package/src/dist/batching.ts.cache +0 -377
  26. package/src/dist/bits.ts.cache +0 -121
  27. package/src/dist/buffers.ts.cache +0 -76
  28. package/src/dist/caching.ts.cache +0 -302
  29. package/src/dist/callHTTPHandler.ts.cache +0 -264
  30. package/src/dist/callManager.ts.cache +0 -159
  31. package/src/dist/certStore.ts.cache +0 -59
  32. package/src/dist/createSingleton.ts.cache +0 -62
  33. package/src/dist/fixLargeNetworkCalls.ts.cache +0 -17
  34. package/src/dist/forwardPort.ts.cache +0 -222
  35. package/src/dist/https.ts.cache +0 -141
  36. package/src/dist/misc.ts.cache +0 -499
  37. package/src/dist/nodeCache.ts.cache +0 -136
  38. package/src/dist/nodeProxy.ts.cache +0 -57
  39. package/src/dist/protocolNegotiation.ts.cache +0 -101
  40. package/src/dist/tlsParsing.ts.cache +0 -85
  41. package/src/dist/types.ts.cache +0 -9
  42. package/src/dist/webSocketServer.ts.cache +0 -365
  43. package/src/dist/websocketFactory.ts.cache +0 -53
  44. package/src/formatting/dist/colors.ts.cache +0 -98
  45. package/src/formatting/dist/format.ts.cache +0 -396
  46. package/src/formatting/dist/logColors.ts.cache +0 -28
  47. package/src/lz4/dist/LZ4.ts.cache +0 -53
  48. package/src/profiling/dist/getOwnTime.ts.cache +0 -100
  49. package/src/profiling/dist/measure.ts.cache +0 -338
  50. package/src/profiling/dist/stats.ts.cache +0 -202
  51. package/src/profiling/dist/statsFormat.ts.cache +0 -45
  52. package/time/dist/trueTimeShim.ts.cache +0 -707
@@ -1,707 +0,0 @@
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"; 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 __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
26
- exports.setGetTimeOffsetBase = exports.getBrowserTime = exports.shimDateNow = exports.waitForFirstTimeSync = exports.getTrueTimeProof = exports.getTrueTimeOffset = exports.getTrueTime = exports.getTimeComponents = exports.getTimeComponentsDetailed = void 0;
27
- const SocketFunction_1 = require("../SocketFunction");
28
- const logColors_1 = require("../src/formatting/logColors");
29
- const misc_1 = require("../src/misc");
30
- // IMPORTANT! We don't ensure that the times of return are unique. We cannot ensure they are unique because the amount of precision is only about ten thousand date times per millisecond, Which would mean if the calling code called date.now frequently enough, which doesn't even have to be that frequent, it could slowly drift farther and farther ahead of the real time, which would be really bad.
31
- module.allowclient = true;
32
- const UPDATE_VERIFY_COUNT = 3;
33
- const UPDATE_TRANSITION_GAP = 1000 * 60 * 20;
34
- const UPDATE_CHECK_INTERVAL = 1000 * 60 * 5;
35
- const DEBUG_TIME_SYNC = false;
36
- const FETCH_RETRY_INITIAL_DELAY = 1000;
37
- const FETCH_RETRY_MAX_DELAY = 1000 * 30;
38
- const FETCH_MAX_FAILURES = 8;
39
- const NTP_TIMEOUT = 1000 * 10;
40
- // The on-disk LMDB database can become corrupt (ex, a process killed mid-write). After this many
41
- // consecutive failed operations we stop just reopening the handle and delete the files to start
42
- // fresh - the data is only a cached time offset, so wiping it merely forces a re-measurement.
43
- const LMDB_MAX_CORRUPTION_FAILURES = 3;
44
- // Time can never go backwards, but we can run at a slower rate until the output time allows
45
- // the real time to catch up with it.
46
- const MINIMUM_TIME_RATE = 0.5;
47
- const THROW_ON_ERROR = false;
48
- // Hugely important as if we don't synchronize between processes, it means our logs are going to be confusing and out of order.
49
- // - Of course, cross-machine, the logs could be out of order. However, due to the latency between machines, that's less likely. The latency will probably be a few milliseconds, and hopefully, our time isn't more than a few milliseconds off of the real time. However, between processes, the latency could easily be microseconds, and our time will absolutely certainly be microseconds off of the real time.
50
- let USE_LMDB_PROCESS_SYNC = true;
51
- // Browser tabs synchronize via localStorage, for the same reason processes synchronize via LMDB.
52
- const TIME_OFFSET_LOCAL_STORAGE_KEY = "socket-function-time-offset";
53
- function debugLog(...args) {
54
- if (DEBUG_TIME_SYNC) {
55
- console.log("[TimeSync]", ...args);
56
- }
57
- }
58
- let cachedTimeOffsetData = undefined;
59
- let didFirstTimeSync = false;
60
- let onFirstTimeSync;
61
- let firstTimeSyncPromise = new Promise((resolve) => {
62
- onFirstTimeSync = resolve;
63
- });
64
- function markFirstTimeSync() {
65
- if (didFirstTimeSync)
66
- return;
67
- didFirstTimeSync = true;
68
- onFirstTimeSync();
69
- }
70
- const baseGetTime = Date.now;
71
- let lastTime = 0;
72
- let lastBaseTime = 0;
73
- function getTimeComponentsDetailed() {
74
- const systemTime = baseGetTime();
75
- const data = cachedTimeOffsetData;
76
- if (!data) {
77
- return {
78
- systemTime,
79
- fromOffset: 0,
80
- toOffset: 0,
81
- fromTime: systemTime,
82
- toTime: systemTime,
83
- };
84
- }
85
- if (systemTime < data.lastUpdateTime) {
86
- // Before everything (shouldn't happen)
87
- if (THROW_ON_ERROR)
88
- throw new Error(`systemTime ${systemTime} is before lastUpdateTime ${data.lastUpdateTime}`);
89
- return {
90
- systemTime,
91
- fromOffset: data.lastOffset,
92
- toOffset: data.lastOffset,
93
- fromTime: data.lastUpdateTime,
94
- toTime: data.lastUpdateTime,
95
- };
96
- }
97
- else if (systemTime < data.updateTime) {
98
- // Smear between lastOffset and offset
99
- return {
100
- systemTime,
101
- fromOffset: data.lastOffset,
102
- toOffset: data.offset,
103
- fromTime: data.lastUpdateTime,
104
- toTime: data.updateTime,
105
- };
106
- }
107
- else if (systemTime < data.nextUpdateTime) {
108
- // Smear between offset and nextOffset
109
- return {
110
- systemTime,
111
- fromOffset: data.offset,
112
- toOffset: data.nextOffset,
113
- fromTime: data.updateTime,
114
- toTime: data.nextUpdateTime,
115
- };
116
- }
117
- else {
118
- // Past everything (shouldn't happen often)
119
- if (THROW_ON_ERROR)
120
- throw new Error(`systemTime ${systemTime} is past nextUpdateTime ${data.nextUpdateTime}`);
121
- return {
122
- systemTime,
123
- fromOffset: data.nextOffset,
124
- toOffset: data.nextOffset,
125
- fromTime: data.nextUpdateTime,
126
- toTime: data.nextUpdateTime,
127
- };
128
- }
129
- }
130
- exports.getTimeComponentsDetailed = getTimeComponentsDetailed;
131
- function getTimeComponents() {
132
- const detailed = getTimeComponentsDetailed();
133
- const elapsed = detailed.systemTime - detailed.fromTime;
134
- const duration = detailed.toTime - detailed.fromTime;
135
- const fraction = duration > 0 ? Math.min(1, elapsed / duration) : 0;
136
- const offset = detailed.fromOffset + (detailed.toOffset - detailed.fromOffset) * fraction;
137
- return { systemTime: detailed.systemTime, offset };
138
- }
139
- exports.getTimeComponents = getTimeComponents;
140
- function getTrueTime() {
141
- const { systemTime, offset } = getTimeComponents();
142
- let time = systemTime + offset;
143
- // Only adjust time once we have a time offset. Otherwise systems with a really bad clock
144
- // might take days be correct. It is better for the time to jump once at startup, rather
145
- // than be off by days, for days at a time.
146
- if (lastTime && offset) {
147
- if (time < lastTime) {
148
- let diff = systemTime - lastBaseTime;
149
- if (diff >= 0) {
150
- // Some time passed, so we have a baseline for how much to increase the time by.
151
- // This allows the real time to catch up with our time naturally.
152
- time = lastTime + diff * MINIMUM_TIME_RATE;
153
- }
154
- else {
155
- // The issue is the system time going backwards. In this case, allow the time to change
156
- }
157
- }
158
- }
159
- lastTime = time;
160
- lastBaseTime = systemTime;
161
- return time;
162
- }
163
- exports.getTrueTime = getTrueTime;
164
- function getTrueTimeOffset() {
165
- const { offset } = getTimeComponents();
166
- return offset;
167
- }
168
- exports.getTrueTimeOffset = getTrueTimeOffset;
169
- function getTrueTimeProof() {
170
- return cachedTimeOffsetData === null || cachedTimeOffsetData === void 0 ? void 0 : cachedTimeOffsetData.proof;
171
- }
172
- exports.getTrueTimeProof = getTrueTimeProof;
173
- function waitForFirstTimeSync() {
174
- if (didFirstTimeSync)
175
- return undefined;
176
- return firstTimeSyncPromise;
177
- }
178
- exports.waitForFirstTimeSync = waitForFirstTimeSync;
179
- let shimmed = false;
180
- function shimDateNow() {
181
- if (shimmed)
182
- return;
183
- shimmed = true;
184
- if (globalThis.TRUE_TIME_ALREADY_SHIMMED)
185
- return;
186
- globalThis.TRUE_TIME_ALREADY_SHIMMED = true;
187
- Date.now = getTrueTime;
188
- }
189
- exports.shimDateNow = shimDateNow;
190
- function getBrowserTime() {
191
- return baseGetTime();
192
- }
193
- exports.getBrowserTime = getBrowserTime;
194
- function setGetTimeOffsetBase(base) {
195
- getTimeOffsetBase = base;
196
- }
197
- exports.setGetTimeOffsetBase = setGetTimeOffsetBase;
198
- function makeMeasurement(sendTime, receiveTime, serverTime) {
199
- const predictedServerToClientLatency = (receiveTime - sendTime) / 2;
200
- const offset = serverTime + predictedServerToClientLatency - receiveTime;
201
- return { offset, proof: { sendTime, receiveTime, serverTime, offset } };
202
- }
203
- async function defaultGetTimeOffset() {
204
- if (!(0, misc_1.isNode)()) {
205
- let sendTime = baseGetTime();
206
- let serverTrueTime = await TimeController.nodes[SocketFunction_1.SocketFunction.browserNodeId()].getTrueTime();
207
- let receiveTime = baseGetTime();
208
- return makeMeasurement(sendTime, receiveTime, serverTrueTime);
209
- }
210
- const dgram = await Promise.resolve().then(() => __importStar(require("dgram")));
211
- const NTP_SERVER = "time.google.com";
212
- const NTP_PORT = 123;
213
- const NTP_PACKET_SIZE = 48;
214
- const NTP_EPOCH_OFFSET = 2208988800000; // Number of milliseconds between 1900-01-01 and 1970-01-01
215
- return new Promise((resolve, reject) => {
216
- const client = dgram.createSocket("udp4");
217
- const message = Buffer.alloc(NTP_PACKET_SIZE);
218
- // Set the first byte to represent NTP client request (LI = 0, VN = 3, Mode = 3)
219
- message[0] = 0x1B;
220
- const sendTime = baseGetTime();
221
- // NTP is UDP, so a lost packet means the "message" event simply never fires. Without a timeout that leaves updateTimeOffset stuck (updatingOffset stays true forever), permanently stopping all future syncs.
222
- const timeout = setTimeout(() => {
223
- client.close();
224
- reject(new Error(`NTP request to ${NTP_SERVER} timed out`));
225
- }, NTP_TIMEOUT);
226
- client.send(message, 0, message.length, NTP_PORT, NTP_SERVER);
227
- client.on("error", (err) => {
228
- clearTimeout(timeout);
229
- client.close();
230
- reject(err);
231
- });
232
- client.on("message", (msg) => {
233
- const receiveTime = baseGetTime();
234
- // A throw in this handler would be an uncaught exception, not a rejection, so validate before reading fixed offsets.
235
- if (msg.length < NTP_PACKET_SIZE)
236
- return;
237
- clearTimeout(timeout);
238
- // Extract the transmit timestamp from the server response
239
- const transmitTimestampSeconds = msg.readUInt32BE(40);
240
- const transmitTimestampFraction = msg.readUInt32BE(44);
241
- const transmitTimestamp = (transmitTimestampSeconds * 1000) + (transmitTimestampFraction * 1000 / 0x100000000) - NTP_EPOCH_OFFSET;
242
- client.close();
243
- resolve(makeMeasurement(sendTime, receiveTime, transmitTimestamp));
244
- });
245
- });
246
- }
247
- function isValidTimeOffsetData(data) {
248
- const d = data;
249
- return (!!d &&
250
- typeof d.lastOffset === "number" &&
251
- typeof d.lastUpdateTime === "number" &&
252
- typeof d.offset === "number" &&
253
- typeof d.updateTime === "number" &&
254
- typeof d.nextOffset === "number" &&
255
- typeof d.nextUpdateTime === "number");
256
- }
257
- let timeOffsetDb = undefined;
258
- let timeOffsetDbPath = undefined;
259
- async function openTimeOffsetDb() {
260
- var _a;
261
- try {
262
- const lmdb = await Promise.resolve().then(() => __importStar(require("lmdb")));
263
- const path = await Promise.resolve().then(() => __importStar(require("path")));
264
- const os = await Promise.resolve().then(() => __importStar(require("os")));
265
- timeOffsetDbPath = path.join(os.tmpdir(), "socket-function-time-offset-2");
266
- return lmdb.open({
267
- path: timeOffsetDbPath,
268
- // Enable versioning for conditional writes
269
- useVersions: true,
270
- });
271
- }
272
- catch (e) {
273
- console.error("Error opening LMDB database:", (_a = e.stack) !== null && _a !== void 0 ? _a : e);
274
- return undefined;
275
- }
276
- }
277
- async function getTimeOffsetDb() {
278
- if (!USE_LMDB_PROCESS_SYNC)
279
- return undefined;
280
- if (!(0, misc_1.isNode)())
281
- return undefined;
282
- if (timeOffsetDb)
283
- return timeOffsetDb;
284
- timeOffsetDb = await openTimeOffsetDb();
285
- return timeOffsetDb;
286
- }
287
- // Closes the current handle (so the next op reopens a fresh one) and, when wipe is set, deletes the
288
- // on-disk files so a corrupt database is replaced with an empty one. lmdb writes the main file at
289
- // the path plus a sibling lock file, so we clear the path and its lock variants.
290
- async function recreateTimeOffsetDb(config) {
291
- var _a, _b;
292
- let db = timeOffsetDb;
293
- timeOffsetDb = undefined;
294
- if (db) {
295
- try {
296
- await db.close();
297
- }
298
- catch (e) {
299
- console.error("Error closing LMDB database:", (_a = e.stack) !== null && _a !== void 0 ? _a : e);
300
- }
301
- }
302
- if (config.wipe && timeOffsetDbPath) {
303
- try {
304
- const fs = await Promise.resolve().then(() => __importStar(require("fs")));
305
- for (let suffix of ["", "-lock", ".lock"]) {
306
- await fs.promises.rm(timeOffsetDbPath + suffix, { recursive: true, force: true });
307
- }
308
- console.log((0, logColors_1.yellow)(`Wiped corrupt time-offset LMDB database at ${timeOffsetDbPath}`));
309
- }
310
- catch (e) {
311
- console.error("Error wiping corrupt LMDB database:", (_b = e.stack) !== null && _b !== void 0 ? _b : e);
312
- }
313
- }
314
- }
315
- // A corrupt database throws on every read and on the read-back inside every write, and we can't
316
- // repair it. So we run each op through here: on success we reset the failure count, and on error
317
- // we reopen the handle (wiping the files once we've hit the threshold) and return the fallback so
318
- // callers degrade to running unsynced instead of throwing. Ops are serialized by updatingOffset,
319
- // so there is no concurrent access to worry about.
320
- let lmdbCorruptionFailures = 0;
321
- async function runLmdbOp(name, op, fallback) {
322
- var _a;
323
- const db = await getTimeOffsetDb();
324
- if (!db)
325
- return fallback;
326
- try {
327
- const result = await op(db);
328
- lmdbCorruptionFailures = 0;
329
- return result;
330
- }
331
- catch (e) {
332
- lmdbCorruptionFailures++;
333
- console.error(`Error during LMDB ${name} (failure ${lmdbCorruptionFailures}/${LMDB_MAX_CORRUPTION_FAILURES}):`, (_a = e.stack) !== null && _a !== void 0 ? _a : e);
334
- let wipe = lmdbCorruptionFailures >= LMDB_MAX_CORRUPTION_FAILURES;
335
- await recreateTimeOffsetDb({ wipe });
336
- if (wipe)
337
- lmdbCorruptionFailures = 0;
338
- return fallback;
339
- }
340
- }
341
- function getLmdbStore() {
342
- return {
343
- async getEntry() {
344
- return runLmdbOp("getEntry", async (db) => {
345
- const entry = await db.getEntry("timeOffset"); // Gets {value, version} atomically
346
- if (!entry)
347
- return undefined;
348
- if (typeof entry.version !== "number" || !isValidTimeOffsetData(entry.value))
349
- return undefined;
350
- return { data: entry.value, version: entry.version };
351
- }, undefined);
352
- },
353
- async putIfVersion(data, expectedVersion) {
354
- return runLmdbOp("putIfVersion", async (db) => {
355
- // Use random version to minimize collision probability on retries
356
- const newVersion = Math.random();
357
- const success = await db.ifVersion("timeOffset", expectedVersion, () => {
358
- return db.put("timeOffset", data, newVersion);
359
- });
360
- return success !== undefined;
361
- }, false);
362
- },
363
- async putInitial(data) {
364
- return runLmdbOp("putInitial", async (db) => {
365
- const newVersion = Math.random();
366
- await db.put("timeOffset", data, newVersion);
367
- // Read back to see what actually got written
368
- const actualEntry = await db.getEntry("timeOffset");
369
- return (actualEntry === null || actualEntry === void 0 ? void 0 : actualEntry.version) === newVersion;
370
- }, false);
371
- },
372
- };
373
- }
374
- function getLocalStorageStore() {
375
- try {
376
- if (typeof localStorage === "undefined")
377
- return undefined;
378
- }
379
- catch (_a) {
380
- return undefined;
381
- }
382
- function readStored() {
383
- var _a;
384
- try {
385
- const raw = localStorage.getItem(TIME_OFFSET_LOCAL_STORAGE_KEY);
386
- if (!raw)
387
- return undefined;
388
- const parsed = JSON.parse(raw);
389
- if (!parsed || typeof parsed.version !== "number" || !isValidTimeOffsetData(parsed.data))
390
- return undefined;
391
- return parsed;
392
- }
393
- catch (e) {
394
- console.error("Error reading time offset from localStorage:", (_a = e.stack) !== null && _a !== void 0 ? _a : e);
395
- return undefined;
396
- }
397
- }
398
- function writeStored(data) {
399
- var _a;
400
- try {
401
- const version = Math.random();
402
- localStorage.setItem(TIME_OFFSET_LOCAL_STORAGE_KEY, JSON.stringify({ version, data }));
403
- return version;
404
- }
405
- catch (e) {
406
- console.error("Error writing time offset to localStorage:", (_a = e.stack) !== null && _a !== void 0 ? _a : e);
407
- return undefined;
408
- }
409
- }
410
- return {
411
- async getEntry() {
412
- const stored = readStored();
413
- if (!stored)
414
- return undefined;
415
- return { data: stored.data, version: stored.version };
416
- },
417
- // localStorage has no atomic compare-and-swap, but access within a tab is synchronous, so re-reading the version immediately before writing shrinks the race window to effectively nothing. Worst case two tabs both fetch an offset, which is harmless.
418
- async putIfVersion(data, expectedVersion) {
419
- const stored = readStored();
420
- if ((stored === null || stored === void 0 ? void 0 : stored.version) !== expectedVersion)
421
- return false;
422
- return writeStored(data) !== undefined;
423
- },
424
- async putInitial(data) {
425
- var _a;
426
- const version = writeStored(data);
427
- if (version === undefined)
428
- return false;
429
- return ((_a = readStored()) === null || _a === void 0 ? void 0 : _a.version) === version;
430
- },
431
- };
432
- }
433
- let timeOffsetStore = undefined;
434
- let timeOffsetStoreResolved = false;
435
- async function getTimeOffsetStore() {
436
- if (timeOffsetStoreResolved)
437
- return timeOffsetStore;
438
- if ((0, misc_1.isNode)()) {
439
- const db = await getTimeOffsetDb();
440
- if (db) {
441
- timeOffsetStore = getLmdbStore();
442
- }
443
- }
444
- else {
445
- timeOffsetStore = getLocalStorageStore();
446
- }
447
- timeOffsetStoreResolved = true;
448
- return timeOffsetStore;
449
- }
450
- let getTimeOffsetBase = defaultGetTimeOffset;
451
- // Returns undefined if we couldn't get any measurements. Callers must NOT treat that as an offset of 0 - a fabricated offset written to the shared store would poison every other process/tab for the full transition window.
452
- async function fetchNewOffset() {
453
- var _a;
454
- let measurements = [];
455
- let failures = 0;
456
- let retryDelay = FETCH_RETRY_INITIAL_DELAY;
457
- while (measurements.length < UPDATE_VERIFY_COUNT) {
458
- // The tab may have been hidden mid-fetch. Measurements from a throttled tab are worse than none, so stop and use whatever we already collected.
459
- if (!canMeasureOffset())
460
- break;
461
- try {
462
- const result = await getTimeOffsetBase();
463
- measurements.push(typeof result === "number" ? { offset: result } : result);
464
- }
465
- catch (e) {
466
- console.error("Error getting time offset:", (_a = e.stack) !== null && _a !== void 0 ? _a : e);
467
- failures++;
468
- if (failures >= FETCH_MAX_FAILURES)
469
- break;
470
- await new Promise(resolve => setTimeout(resolve, retryDelay));
471
- retryDelay = Math.min(retryDelay * 2, FETCH_RETRY_MAX_DELAY);
472
- }
473
- }
474
- if (measurements.length === 0) {
475
- return undefined;
476
- }
477
- // Pick the middle offset
478
- measurements.sort((a, b) => a.offset - b.offset);
479
- let measurement = measurements[Math.floor(measurements.length / 2)];
480
- let offset = measurement.offset;
481
- // Log if offset is significant
482
- let offsetRound = Math.abs(Math.round(offset));
483
- let offsetColored = (Math.abs(offset) > 600 && (0, logColors_1.red)(offsetRound + "ms")
484
- || Math.abs(offset) > 300 && (0, logColors_1.yellow)(offsetRound + "ms")
485
- || (0, logColors_1.green)(offsetRound + "ms"));
486
- if (Math.abs(offset) > 500) {
487
- console.log(`${(0, logColors_1.blue)("Synchronized time")}, local clock was ${offset > 0 ? "behind" : "ahead"} by ${offsetColored} @ ${(0, logColors_1.blue)(Date.now() + "")}`);
488
- }
489
- return measurement;
490
- }
491
- function canMeasureOffset() {
492
- if ((0, misc_1.isNode)())
493
- return true;
494
- if (typeof document === "undefined")
495
- return true;
496
- // Hidden tabs get their timers and message delivery throttled, which corrupts the round-trip latency estimate and therefore the offset. A visible tab will measure and share via localStorage instead.
497
- return document.visibilityState !== "hidden";
498
- }
499
- let updatingOffset = false;
500
- async function updateTimeOffset() {
501
- var _a, _b;
502
- if (updatingOffset)
503
- return;
504
- updatingOffset = true;
505
- try {
506
- const store = await getTimeOffsetStore();
507
- if (!store) {
508
- // IMPORTANT: Always use baseGetTime() for scheduling, never getTrueTime().
509
- // Our update schedule must be based on the stable system clock, not the
510
- // offset-adjusted time which changes as we synchronize.
511
- const currentTime = baseGetTime();
512
- let cachedData = cachedTimeOffsetData;
513
- if (cachedData && currentTime >= cachedData.nextUpdateTime) {
514
- // Past the end - reset and reinitialize
515
- cachedData = undefined;
516
- debugLog("Past nextUpdateTime, resetting");
517
- }
518
- const needsFetch = !cachedData || currentTime >= cachedData.updateTime;
519
- if (needsFetch && !canMeasureOffset()) {
520
- // Keep using whatever offset we have (even a stale one is better than a measurement skewed by background throttling).
521
- }
522
- else if (!cachedData) {
523
- // First time initialization
524
- const measurement = await fetchNewOffset();
525
- if (measurement) {
526
- // Re-read the time, as fetchNewOffset can take minutes when it has to retry.
527
- const initTime = baseGetTime();
528
- const offset = measurement.offset;
529
- cachedTimeOffsetData = {
530
- lastOffset: offset,
531
- lastUpdateTime: initTime,
532
- offset: offset,
533
- updateTime: initTime + UPDATE_TRANSITION_GAP,
534
- nextOffset: offset,
535
- nextUpdateTime: initTime + UPDATE_TRANSITION_GAP * 2,
536
- proof: measurement.proof,
537
- };
538
- debugLog("Initialized - time offset:", offset, "ms, next update in", UPDATE_TRANSITION_GAP, "ms");
539
- }
540
- // On total failure we leave the data unset (getTrueTime then uses the raw system clock) and the next check interval retries. We never fabricate an offset.
541
- }
542
- else if (currentTime >= cachedData.updateTime) {
543
- // Time to rotate
544
- const newMeasurement = await fetchNewOffset();
545
- if (newMeasurement) {
546
- cachedTimeOffsetData = {
547
- lastOffset: cachedData.offset,
548
- lastUpdateTime: cachedData.updateTime,
549
- offset: cachedData.nextOffset,
550
- updateTime: cachedData.nextUpdateTime,
551
- nextOffset: newMeasurement.offset,
552
- nextUpdateTime: cachedData.nextUpdateTime + UPDATE_TRANSITION_GAP,
553
- proof: newMeasurement.proof,
554
- };
555
- const timeUntilNext = cachedTimeOffsetData.nextUpdateTime - baseGetTime();
556
- debugLog("Advancing time offset - current:", cachedTimeOffsetData.offset, "ms, next:", cachedTimeOffsetData.nextOffset, "ms, next update in", timeUntilNext, "ms");
557
- }
558
- }
559
- // Always resolve the first sync, even on failure - SocketFunction.mount awaits this, and hanging forever is worse than temporarily running on the unadjusted system clock.
560
- markFirstTimeSync();
561
- return;
562
- }
563
- // At this point we have a shared store (LMDB for processes, localStorage for tabs) with atomic-ish versioned writes.
564
- while (true) {
565
- const entry = await store.getEntry();
566
- // IMPORTANT: Always use baseGetTime() for scheduling, never getTrueTime().
567
- // Our update schedule must be based on the stable system clock, not the
568
- // offset-adjusted time which changes as we synchronize.
569
- const currentTime = baseGetTime();
570
- let cachedData = entry === null || entry === void 0 ? void 0 : entry.data;
571
- const readVersion = entry === null || entry === void 0 ? void 0 : entry.version;
572
- if (cachedData && currentTime >= cachedData.nextUpdateTime) {
573
- // Past the end - reset and reinitialize
574
- cachedData = undefined;
575
- debugLog("Past nextUpdateTime, resetting");
576
- }
577
- const needsFetch = !cachedData || currentTime >= cachedData.updateTime;
578
- if (needsFetch && !canMeasureOffset()) {
579
- // Keep using whatever offset we have (even a stale one is better than a measurement skewed by background throttling). If we have nothing, run unsynced until we become visible or another tab writes to the store.
580
- cachedTimeOffsetData = (_a = entry === null || entry === void 0 ? void 0 : entry.data) !== null && _a !== void 0 ? _a : cachedTimeOffsetData;
581
- break;
582
- }
583
- if (!cachedData || !readVersion) {
584
- // First time initialization - use conditional write to handle race
585
- const measurement = await fetchNewOffset();
586
- if (!measurement) {
587
- // Total failure - keep any stale data rather than writing a fabricated offset to the shared store. The next check interval retries.
588
- cachedTimeOffsetData = (_b = entry === null || entry === void 0 ? void 0 : entry.data) !== null && _b !== void 0 ? _b : cachedTimeOffsetData;
589
- break;
590
- }
591
- // Re-read the time, as fetchNewOffset can take minutes when it has to retry.
592
- const initTime = baseGetTime();
593
- const offset = measurement.offset;
594
- const initData = {
595
- lastOffset: offset,
596
- lastUpdateTime: initTime,
597
- offset: offset,
598
- updateTime: initTime + UPDATE_TRANSITION_GAP,
599
- nextOffset: offset,
600
- nextUpdateTime: initTime + UPDATE_TRANSITION_GAP * 2,
601
- proof: measurement.proof,
602
- };
603
- if (readVersion) {
604
- const success = await store.putIfVersion(initData, readVersion);
605
- if (!success) {
606
- debugLog("Lost the race, retrying");
607
- // Lost the race, retry
608
- continue;
609
- }
610
- cachedTimeOffsetData = initData;
611
- console.log("Successfully wrote atomic reset");
612
- break;
613
- }
614
- if (!await store.putInitial(initData)) {
615
- // Lost the race, another writer wrote after us
616
- // Retry from the top to read their data
617
- debugLog("Value was changed by another process, retrying");
618
- continue;
619
- }
620
- // We won the race, use our data
621
- cachedTimeOffsetData = initData;
622
- debugLog("Initialized - time offset:", offset, "ms, next update in", UPDATE_TRANSITION_GAP, "ms");
623
- break;
624
- }
625
- if (currentTime >= cachedData.updateTime) {
626
- // Time to rotate
627
- const newMeasurement = await fetchNewOffset();
628
- if (!newMeasurement) {
629
- // Total failure - keep the existing data unrotated (the offset just flattens out at nextOffset). The next check interval retries the rotation.
630
- cachedTimeOffsetData = cachedData;
631
- break;
632
- }
633
- const newData = {
634
- lastOffset: cachedData.offset,
635
- lastUpdateTime: cachedData.updateTime,
636
- offset: cachedData.nextOffset,
637
- updateTime: cachedData.nextUpdateTime,
638
- nextOffset: newMeasurement.offset,
639
- nextUpdateTime: cachedData.nextUpdateTime + UPDATE_TRANSITION_GAP,
640
- proof: newMeasurement.proof,
641
- };
642
- const success = await store.putIfVersion(newData, readVersion);
643
- if (!success) {
644
- // Lost the race, retry
645
- continue;
646
- }
647
- const timeUntilNext = newData.nextUpdateTime - baseGetTime();
648
- debugLog("Advancing time offset - current:", newData.offset, "ms, next:", newData.nextOffset, "ms, next update in", timeUntilNext, "ms");
649
- cachedTimeOffsetData = newData;
650
- break;
651
- }
652
- else {
653
- cachedTimeOffsetData = cachedData;
654
- const timeUntilNext = cachedData.updateTime - baseGetTime();
655
- debugLog("Loaded from store - current:", cachedData.offset, "ms, next:", cachedData.nextOffset, "ms, next update in", timeUntilNext, "ms");
656
- break;
657
- }
658
- }
659
- markFirstTimeSync();
660
- }
661
- finally {
662
- updatingOffset = false;
663
- }
664
- }
665
- function triggerUpdateTimeOffset() {
666
- updateTimeOffset().catch((e) => {
667
- console.warn("Error updating time offset:", e);
668
- });
669
- }
670
- setInterval(triggerUpdateTimeOffset, UPDATE_CHECK_INTERVAL);
671
- setImmediate(() => {
672
- updateTimeOffset().catch((e) => {
673
- console.error("Error updating initial offset:", e);
674
- });
675
- });
676
- if (!(0, misc_1.isNode)() && typeof window !== "undefined" && typeof document !== "undefined") {
677
- // If we loaded in a hidden tab we skip measuring, so re-check as soon as we become visible.
678
- document.addEventListener("visibilitychange", triggerUpdateTimeOffset);
679
- window.addEventListener("focus", triggerUpdateTimeOffset);
680
- // Pick up offsets other tabs write, so only one tab ever has to measure.
681
- window.addEventListener("storage", (e) => {
682
- if (e.key === TIME_OFFSET_LOCAL_STORAGE_KEY) {
683
- triggerUpdateTimeOffset();
684
- }
685
- });
686
- }
687
- class TimeControllerBase {
688
- async getTrueTime() {
689
- await waitForFirstTimeSync();
690
- return getTrueTime();
691
- }
692
- }
693
- const TimeController = SocketFunction_1.SocketFunction.register("TimeController-ddf4753e-fc8a-413f-8cc2-b927dd449976", new TimeControllerBase(), () => ({
694
- getTrueTime: {
695
- // No hooks, as this needs to run very early on. Also, it is basically just a ping,
696
- // so it should be safe for anyone to use (we might even make it just a regular HTTPS endpoint,
697
- // or even just set up a dedicated domain for this).
698
- noDefaultHooks: true,
699
- noClientHooks: true,
700
- },
701
- }), () => ({}), {
702
- // NOTE: Autoexpose, because our exposed endpoints are incredibly lightweight
703
- // (just a ping), and don't expose really expose any data.
704
- // noAutoExpose: true
705
- });
706
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJ1ZVRpbWVTaGltLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsidHJ1ZVRpbWVTaGltLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsc0RBQW1EO0FBQ25ELDJEQUF1RTtBQUN2RSxzQ0FBcUM7QUFFckMsNFlBQTRZO0FBRTVZLE1BQU0sQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDO0FBRzFCLE1BQU0sbUJBQW1CLEdBQUcsQ0FBQyxDQUFDO0FBRTlCLE1BQU0scUJBQXFCLEdBQUcsSUFBSSxHQUFHLEVBQUUsR0FBRyxFQUFFLENBQUM7QUFDN0MsTUFBTSxxQkFBcUIsR0FBRyxJQUFJLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQztBQUM1QyxNQUFNLGVBQWUsR0FBRyxLQUFLLENBQUM7QUFFOUIsTUFBTSx5QkFBeUIsR0FBRyxJQUFJLENBQUM7QUFDdkMsTUFBTSxxQkFBcUIsR0FBRyxJQUFJLEdBQUcsRUFBRSxDQUFDO0FBQ3hDLE1BQU0sa0JBQWtCLEdBQUcsQ0FBQyxDQUFDO0FBQzdCLE1BQU0sV0FBVyxHQUFHLElBQUksR0FBRyxFQUFFLENBQUM7QUFFOUIsaUdBQWlHO0FBQ2pHLGlHQUFpRztBQUNqRywrRkFBK0Y7QUFDL0YsTUFBTSw0QkFBNEIsR0FBRyxDQUFDLENBQUM7QUFFdkMsNEZBQTRGO0FBQzVGLDBDQUEwQztBQUMxQyxNQUFNLGlCQUFpQixHQUFHLEdBQUcsQ0FBQztBQUU5QixNQUFNLGNBQWMsR0FBRyxLQUFLLENBQUM7QUFFN0IsK0hBQStIO0FBQy9ILHNaQUFzWjtBQUN0WixJQUFJLHFCQUFxQixHQUFHLElBQUksQ0FBQztBQUVqQyxpR0FBaUc7QUFDakcsTUFBTSw2QkFBNkIsR0FBRyw2QkFBNkIsQ0FBQztBQUVwRSxTQUFTLFFBQVEsQ0FBQyxHQUFHLElBQVc7SUFDNUIsSUFBSSxlQUFlLEVBQUUsQ0FBQztRQUNsQixPQUFPLENBQUMsR0FBRyxDQUFDLFlBQVksRUFBRSxHQUFHLElBQUksQ0FBQyxDQUFDO0lBQ3ZDLENBQUM7QUFDTCxDQUFDO0FBMEJELElBQUksb0JBQW9CLEdBQStCLFNBQVMsQ0FBQztBQUNqRSxJQUFJLGdCQUFnQixHQUFHLEtBQUssQ0FBQztBQUM3QixJQUFJLGVBQTRCLENBQUM7QUFDakMsSUFBSSxvQkFBb0IsR0FBRyxJQUFJLE9BQU8sQ0FBTyxDQUFDLE9BQU8sRUFBRSxFQUFFO0lBQ3JELGVBQWUsR0FBRyxPQUFPLENBQUM7QUFDOUIsQ0FBQyxDQUFDLENBQUM7QUFDSCxTQUFTLGlCQUFpQjtJQUN0QixJQUFJLGdCQUFnQjtRQUFFLE9BQU87SUFDN0IsZ0JBQWdCLEdBQUcsSUFBSSxDQUFDO0lBQ3hCLGVBQWUsRUFBRSxDQUFDO0FBQ3RCLENBQUM7QUFFRCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDO0FBQzdCLElBQUksUUFBUSxHQUFHLENBQUMsQ0FBQztBQUNqQixJQUFJLFlBQVksR0FBRyxDQUFDLENBQUM7QUFFckIsU0FBZ0IseUJBQXlCO0lBT3JDLE1BQU0sVUFBVSxHQUFHLFdBQVcsRUFBRSxDQUFDO0lBQ2pDLE1BQU0sSUFBSSxHQUFHLG9CQUFvQixDQUFDO0lBRWxDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNSLE9BQU87WUFDSCxVQUFVO1lBQ1YsVUFBVSxFQUFFLENBQUM7WUFDYixRQUFRLEVBQUUsQ0FBQztZQUNYLFFBQVEsRUFBRSxVQUFVO1lBQ3BCLE1BQU0sRUFBRSxVQUFVO1NBQ3JCLENBQUM7SUFDTixDQUFDO0lBRUQsSUFBSSxVQUFVLEdBQUcsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1FBQ25DLHVDQUF1QztRQUN2QyxJQUFJLGNBQWM7WUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLGNBQWMsVUFBVSw2QkFBNkIsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDLENBQUM7UUFDaEgsT0FBTztZQUNILFVBQVU7WUFDVixVQUFVLEVBQUUsSUFBSSxDQUFDLFVBQVU7WUFDM0IsUUFBUSxFQUFFLElBQUksQ0FBQyxVQUFVO1lBQ3pCLFFBQVEsRUFBRSxJQUFJLENBQUMsY0FBYztZQUM3QixNQUFNLEVBQUUsSUFBSSxDQUFDLGNBQWM7U0FDOUIsQ0FBQztJQUNOLENBQUM7U0FBTSxJQUFJLFVBQVUsR0FBRyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7UUFDdEMsc0NBQXNDO1FBQ3RDLE9BQU87WUFDSCxVQUFVO1lBQ1YsVUFBVSxFQUFFLElBQUksQ0FBQyxVQUFVO1lBQzNCLFFBQVEsRUFBRSxJQUFJLENBQUMsTUFBTTtZQUNyQixRQUFRLEVBQUUsSUFBSSxDQUFDLGNBQWM7WUFDN0IsTUFBTSxFQUFFLElBQUksQ0FBQyxVQUFVO1NBQzFCLENBQUM7SUFDTixDQUFDO1NBQU0sSUFBSSxVQUFVLEdBQUcsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1FBQzFDLHNDQUFzQztRQUN0QyxPQUFPO1lBQ0gsVUFBVTtZQUNWLFVBQVUsRUFBRSxJQUFJLENBQUMsTUFBTTtZQUN2QixRQUFRLEVBQUUsSUFBSSxDQUFDLFVBQVU7WUFDekIsUUFBUSxFQUFFLElBQUksQ0FBQyxVQUFVO1lBQ3pCLE1BQU0sRUFBRSxJQUFJLENBQUMsY0FBYztTQUM5QixDQUFDO0lBQ04sQ0FBQztTQUFNLENBQUM7UUFDSiwyQ0FBMkM7UUFDM0MsSUFBSSxjQUFjO1lBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQyxjQUFjLFVBQVUsMkJBQTJCLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQyxDQUFDO1FBQzlHLE9BQU87WUFDSCxVQUFVO1lBQ1YsVUFBVSxFQUFFLElBQUksQ0FBQyxVQUFVO1lBQzNCLFFBQVEsRUFBRSxJQUFJLENBQUMsVUFBVTtZQUN6QixRQUFRLEVBQUUsSUFBSSxDQUFDLGNBQWM7WUFDN0IsTUFBTSxFQUFFLElBQUksQ0FBQyxjQUFjO1NBQzlCLENBQUM7SUFDTixDQUFDO0FBQ0wsQ0FBQztBQTNERCw4REEyREM7QUFFRCxTQUFnQixpQkFBaUI7SUFDN0IsTUFBTSxRQUFRLEdBQUcseUJBQXlCLEVBQUUsQ0FBQztJQUM3QyxNQUFNLE9BQU8sR0FBRyxRQUFRLENBQUMsVUFBVSxHQUFHLFFBQVEsQ0FBQyxRQUFRLENBQUM7SUFDeEQsTUFBTSxRQUFRLEdBQUcsUUFBUSxDQUFDLE1BQU0sR0FBRyxRQUFRLENBQUMsUUFBUSxDQUFDO0lBQ3JELE1BQU0sUUFBUSxHQUFHLFFBQVEsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLE9BQU8sR0FBRyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3BFLE1BQU0sTUFBTSxHQUFHLFFBQVEsQ0FBQyxVQUFVLEdBQUcsQ0FBQyxRQUFRLENBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQyxVQUFVLENBQUMsR0FBRyxRQUFRLENBQUM7SUFDMUYsT0FBTyxFQUFFLFVBQVUsRUFBRSxRQUFRLENBQUMsVUFBVSxFQUFFLE1BQU0sRUFBRSxDQUFDO0FBQ3ZELENBQUM7QUFQRCw4Q0FPQztBQUVELFNBQWdCLFdBQVc7SUFDdkIsTUFBTSxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsR0FBRyxpQkFBaUIsRUFBRSxDQUFDO0lBQ25ELElBQUksSUFBSSxHQUFHLFVBQVUsR0FBRyxNQUFNLENBQUM7SUFFL0IseUZBQXlGO0lBQ3pGLHlGQUF5RjtJQUN6Riw0Q0FBNEM7SUFDNUMsSUFBSSxRQUFRLElBQUksTUFBTSxFQUFFLENBQUM7UUFDckIsSUFBSSxJQUFJLEdBQUcsUUFBUSxFQUFFLENBQUM7WUFDbEIsSUFBSSxJQUFJLEdBQUcsVUFBVSxHQUFHLFlBQVksQ0FBQztZQUNyQyxJQUFJLElBQUksSUFBSSxDQUFDLEVBQUUsQ0FBQztnQkFDWixnRkFBZ0Y7Z0JBQ2hGLGtFQUFrRTtnQkFDbEUsSUFBSSxHQUFHLFFBQVEsR0FBRyxJQUFJLEdBQUcsaUJBQWlCLENBQUM7WUFDL0MsQ0FBQztpQkFBTSxDQUFDO2dCQUNKLHVGQUF1RjtZQUMzRixDQUFDO1FBQ0wsQ0FBQztJQUNMLENBQUM7SUFDRCxRQUFRLEdBQUcsSUFBSSxDQUFDO0lBQ2hCLFlBQVksR0FBRyxVQUFVLENBQUM7SUFDMUIsT0FBTyxJQUFJLENBQUM7QUFDaEIsQ0FBQztBQXRCRCxrQ0FzQkM7QUFDRCxTQUFnQixpQkFBaUI7SUFDN0IsTUFBTSxFQUFFLE1BQU0sRUFBRSxHQUFHLGlCQUFpQixFQUFFLENBQUM7SUFDdkMsT0FBTyxNQUFNLENBQUM7QUFDbEIsQ0FBQztBQUhELDhDQUdDO0FBQ0QsU0FBZ0IsZ0JBQWdCO0lBQzVCLE9BQU8sb0JBQW9CLGFBQXBCLG9CQUFvQix1QkFBcEIsb0JBQW9CLENBQUUsS0FBSyxDQUFDO0FBQ3ZDLENBQUM7QUFGRCw0Q0FFQztBQUNELFNBQWdCLG9CQUFvQjtJQUNoQyxJQUFJLGdCQUFnQjtRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQ3ZDLE9BQU8sb0JBQW9CLENBQUM7QUFDaEMsQ0FBQztBQUhELG9EQUdDO0FBSUQsSUFBSSxPQUFPLEdBQUcsS0FBSyxDQUFDO0FBQ3BCLFNBQWdCLFdBQVc7SUFDdkIsSUFBSSxPQUFPO1FBQUUsT0FBTztJQUNwQixPQUFPLEdBQUcsSUFBSSxDQUFDO0lBQ2YsSUFBSSxVQUFVLENBQUMseUJBQXlCO1FBQUUsT0FBTztJQUNqRCxVQUFVLENBQUMseUJBQXlCLEdBQUcsSUFBSSxDQUFDO0lBQzVDLElBQUksQ0FBQyxHQUFHLEdBQUcsV0FBVyxDQUFDO0FBQzNCLENBQUM7QUFORCxrQ0FNQztBQUNELFNBQWdCLGNBQWM7SUFDMUIsT0FBTyxXQUFXLEVBQUUsQ0FBQztBQUN6QixDQUFDO0FBRkQsd0NBRUM7QUFFRCxTQUFnQixvQkFBb0IsQ0FBQyxJQUFtRDtJQUNwRixpQkFBaUIsR0FBRyxJQUFJLENBQUM7QUFDN0IsQ0FBQztBQUZELG9EQUVDO0FBRUQsU0FBUyxlQUFlLENBQUMsUUFBZ0IsRUFBRSxXQUFtQixFQUFFLFVBQWtCO0lBQzlFLE1BQU0sOEJBQThCLEdBQUcsQ0FBQyxXQUFXLEdBQUcsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ3BFLE1BQU0sTUFBTSxHQUFHLFVBQVUsR0FBRyw4QkFBOEIsR0FBRyxXQUFXLENBQUM7SUFDekUsT0FBTyxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsRUFBRSxRQUFRLEVBQUUsV0FBVyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsRUFBRSxDQUFDO0FBQzVFLENBQUM7QUFFRCxLQUFLLFVBQVUsb0JBQW9CO0lBQy9CLElBQUksQ0FBQyxJQUFBLGFBQU0sR0FBRSxFQUFFLENBQUM7UUFDWixJQUFJLFFBQVEsR0FBRyxXQUFXLEVBQUUsQ0FBQztRQUM3QixJQUFJLGNBQWMsR0FBRyxNQUFNLGNBQWMsQ0FBQyxLQUFLLENBQUMsK0JBQWMsQ0FBQyxhQUFhLEVBQUUsQ0FBQyxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQzlGLElBQUksV0FBVyxHQUFHLFdBQVcsRUFBRSxDQUFDO1FBQ2hDLE9BQU8sZUFBZSxDQUFDLFFBQVEsRUFBRSxXQUFXLEVBQUUsY0FBYyxDQUFDLENBQUM7SUFDbEUsQ0FBQztJQUVELE1BQU0sS0FBSyxHQUFHLHdEQUFhLE9BQU8sR0FBQyxDQUFDO0lBQ3BDLE1BQU0sVUFBVSxHQUFHLGlCQUFpQixDQUFDO0lBQ3JDLE1BQU0sUUFBUSxHQUFHLEdBQUcsQ0FBQztJQUNyQixNQUFNLGVBQWUsR0FBRyxFQUFFLENBQUM7SUFDM0IsTUFBTSxnQkFBZ0IsR0FBRyxhQUFhLENBQUMsQ0FBQywyREFBMkQ7SUFDbkcsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtRQUNuQyxNQUFNLE1BQU0sR0FBRyxLQUFLLENBQUMsWUFBWSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQzFDLE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsZUFBZSxDQUFDLENBQUM7UUFFOUMsZ0ZBQWdGO1FBQ2hGLE9BQU8sQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUM7UUFFbEIsTUFBTSxRQUFRLEdBQUcsV0FBVyxFQUFFLENBQUM7UUFFL0IsOE1BQThNO1FBQzlNLE1BQU0sT0FBTyxHQUFHLFVBQVUsQ0FBQyxHQUFHLEVBQUU7WUFDNUIsTUFBTSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ2YsTUFBTSxDQUFDLElBQUksS0FBSyxDQUFDLGtCQUFrQixVQUFVLFlBQVksQ0FBQyxDQUFDLENBQUM7UUFDaEUsQ0FBQyxFQUFFLFdBQVcsQ0FBQyxDQUFDO1FBRWhCLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUMsRUFBRSxPQUFPLENBQUMsTUFBTSxFQUFFLFFBQVEsRUFBRSxVQUFVLENBQUMsQ0FBQztRQUM5RCxNQUFNLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDLEdBQUcsRUFBRSxFQUFFO1lBQ3ZCLFlBQVksQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUN0QixNQUFNLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDZixNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDaEIsQ0FBQyxDQUFDLENBQUM7UUFFSCxNQUFNLENBQUMsRUFBRSxDQUFDLFNBQVMsRUFBRSxDQUFDLEdBQUcsRUFBRSxFQUFFO1lBQ3pCLE1BQU0sV0FBVyxHQUFHLFdBQVcsRUFBRSxDQUFDO1lBQ2xDLHFIQUFxSDtZQUNySCxJQUFJLEdBQUcsQ0FBQyxNQUFNLEdBQUcsZUFBZTtnQkFBRSxPQUFPO1lBQ3pDLFlBQVksQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUV0QiwwREFBMEQ7WUFDMUQsTUFBTSx3QkFBd0IsR0FBRyxHQUFHLENBQUMsWUFBWSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQ3RELE1BQU0seUJBQXlCLEdBQUcsR0FBRyxDQUFDLFlBQVksQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUN2RCxNQUFNLGlCQUFpQixHQUFHLENBQUMsd0JBQXdCLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyx5QkFBeUIsR0FBRyxJQUFJLEdBQUcsV0FBVyxDQUFDLEdBQUcsZ0JBQWdCLENBQUM7WUFFbEksTUFBTSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ2YsT0FBTyxDQUFDLGVBQWUsQ0FBQyxRQUFRLEVBQUUsV0FBVyxFQUFFLGlCQUFpQixDQUFDLENBQUMsQ0FBQztRQUN2RSxDQUFDLENBQUMsQ0FBQztJQUNQLENBQUMsQ0FBQyxDQUFDO0FBQ1AsQ0FBQztBQUVELFNBQVMscUJBQXFCLENBQUMsSUFBYTtJQUN4QyxNQUFNLENBQUMsR0FBRyxJQUFrQyxDQUFDO0lBQzdDLE9BQU8sQ0FDSCxDQUFDLENBQUMsQ0FBQztRQUNILE9BQU8sQ0FBQyxDQUFDLFVBQVUsS0FBSyxRQUFRO1FBQ2hDLE9BQU8sQ0FBQyxDQUFDLGNBQWMsS0FBSyxRQUFRO1FBQ3BDLE9BQU8sQ0FBQyxDQUFDLE1BQU0sS0FBSyxRQUFRO1FBQzVCLE9BQU8sQ0FBQyxDQUFDLFVBQVUsS0FBSyxRQUFRO1FBQ2hDLE9BQU8sQ0FBQyxDQUFDLFVBQVUsS0FBSyxRQUFRO1FBQ2hDLE9BQU8sQ0FBQyxDQUFDLGNBQWMsS0FBSyxRQUFRLENBQ3ZDLENBQUM7QUFDTixDQUFDO0FBZUQsSUFBSSxZQUFZLEdBQTZCLFNBQVMsQ0FBQztBQUN2RCxJQUFJLGdCQUFnQixHQUF1QixTQUFTLENBQUM7QUFFckQsS0FBSyxVQUFVLGdCQUFnQjs7SUFDM0IsSUFBSSxDQUFDO1FBQ0QsTUFBTSxJQUFJLEdBQUcsd0RBQWEsTUFBTSxHQUFDLENBQUM7UUFDbEMsTUFBTSxJQUFJLEdBQUcsd0RBQWEsTUFBTSxHQUFDLENBQUM7UUFDbEMsTUFBTSxFQUFFLEdBQUcsd0RBQWEsSUFBSSxHQUFDLENBQUM7UUFFOUIsZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLEVBQUUsK0JBQStCLENBQUMsQ0FBQztRQUMzRSxPQUFPLElBQUksQ0FBQyxJQUFJLENBQXlCO1lBQ3JDLElBQUksRUFBRSxnQkFBZ0I7WUFDdEIsMkNBQTJDO1lBQzNDLFdBQVcsRUFBRSxJQUFJO1NBQ3BCLENBQUMsQ0FBQztJQUNQLENBQUM7SUFBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1FBQ1QsT0FBTyxDQUFDLEtBQUssQ0FBQyw4QkFBOEIsRUFBRSxNQUFDLENBQVcsQ0FBQyxLQUFLLG1DQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ3ZFLE9BQU8sU0FBUyxDQUFDO0lBQ3JCLENBQUM7QUFDTCxDQUFDO0FBRUQsS0FBSyxVQUFVLGVBQWU7SUFDMUIsSUFBSSxDQUFDLHFCQUFxQjtRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQzdDLElBQUksQ0FBQyxJQUFBLGFBQU0sR0FBRTtRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQ2hDLElBQUksWUFBWTtRQUFFLE9BQU8sWUFBWSxDQUFDO0lBQ3RDLFlBQVksR0FBRyxNQUFNLGdCQUFnQixFQUFFLENBQUM7SUFDeEMsT0FBTyxZQUFZLENBQUM7QUFDeEIsQ0FBQztBQUVELG9HQUFvRztBQUNwRyxtR0FBbUc7QUFDbkcsa0ZBQWtGO0FBQ2xGLEtBQUssVUFBVSxvQkFBb0IsQ0FBQyxNQUF5Qjs7SUFDekQsSUFBSSxFQUFFLEdBQUcsWUFBWSxDQUFDO0lBQ3RCLFlBQVksR0FBRyxTQUFTLENBQUM7SUFDekIsSUFBSSxFQUFFLEVBQUUsQ0FBQztRQUNMLElBQUksQ0FBQztZQUNELE1BQU0sRUFBRSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ3JCLENBQUM7UUFBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1lBQ1QsT0FBTyxDQUFDLEtBQUssQ0FBQyw4QkFBOEIsRUFBRSxNQUFDLENBQVcsQ0FBQyxLQUFLLG1DQUFJLENBQUMsQ0FBQyxDQUFDO1FBQzNFLENBQUM7SUFDTCxDQUFDO0lBQ0QsSUFBSSxNQUFNLENBQUMsSUFBSSxJQUFJLGdCQUFnQixFQUFFLENBQUM7UUFDbEMsSUFBSSxDQUFDO1lBQ0QsTUFBTSxFQUFFLEdBQUcsd0RBQWEsSUFBSSxHQUFDLENBQUM7WUFDOUIsS0FBSyxJQUFJLE1BQU0sSUFBSSxDQUFDLEVBQUUsRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLEVBQUUsQ0FBQztnQkFDeEMsTUFBTSxFQUFFLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxnQkFBZ0IsR0FBRyxNQUFNLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1lBQ3RGLENBQUM7WUFDRCxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUEsa0JBQU0sRUFBQyw4Q0FBOEMsZ0JBQWdCLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDMUYsQ0FBQztRQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDVCxPQUFPLENBQUMsS0FBSyxDQUFDLHFDQUFxQyxFQUFFLE1BQUMsQ0FBVyxDQUFDLEtBQUssbUNBQUksQ0FBQyxDQUFDLENBQUM7UUFDbEYsQ0FBQztJQUNMLENBQUM7QUFDTCxDQUFDO0FBRUQsZ0dBQWdHO0FBQ2hHLGtHQUFrRztBQUNsRyxtR0FBbUc7QUFDbkcsa0dBQWtHO0FBQ2xHLG9EQUFvRDtBQUNwRCxJQUFJLHNCQUFzQixHQUFHLENBQUMsQ0FBQztBQUMvQixLQUFLLFVBQVUsU0FBUyxDQUFJLElBQVksRUFBRSxFQUFvQyxFQUFFLFFBQVc7O0lBQ3ZGLE1BQU0sRUFBRSxHQUFHLE1BQU0sZUFBZSxFQUFFLENBQUM7SUFDbkMsSUFBSSxDQUFDLEVBQUU7UUFBRSxPQUFPLFFBQVEsQ0FBQztJQUN6QixJQUFJLENBQUM7UUFDRCxNQUFNLE1BQU0sR0FBRyxNQUFNLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUM1QixzQkFBc0IsR0FBRyxDQUFDLENBQUM7UUFDM0IsT0FBTyxNQUFNLENBQUM7SUFDbEIsQ0FBQztJQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7UUFDVCxzQkFBc0IsRUFBRSxDQUFDO1FBQ3pCLE9BQU8sQ0FBQyxLQUFLLENBQUMscUJBQXFCLElBQUksYUFBYSxzQkFBc0IsSUFBSSw0QkFBNEIsSUFBSSxFQUFFLE1BQUMsQ0FBVyxDQUFDLEtBQUssbUNBQUksQ0FBQyxDQUFDLENBQUM7UUFDekksSUFBSSxJQUFJLEdBQUcsc0JBQXNCLElBQUksNEJBQTRCLENBQUM7UUFDbEUsTUFBTSxvQkFBb0IsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7UUFDckMsSUFBSSxJQUFJO1lBQUUsc0JBQXNCLEdBQUcsQ0FBQyxDQUFDO1FBQ3JDLE9BQU8sUUFBUSxDQUFDO0lBQ3BCLENBQUM7QUFDTCxDQUFDO0FBRUQsU0FBUyxZQUFZO0lBQ2pCLE9BQU87UUFDSCxLQUFLLENBQUMsUUFBUTtZQUNWLE9BQU8sU0FBUyxDQUFDLFVBQVUsRUFBRSxLQUFLLEVBQUUsRUFBRSxFQUFFLEVBQUU7Z0JBQ3RDLE1BQU0sS0FBSyxHQUFHLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLG1DQUFtQztnQkFDbEYsSUFBSSxDQUFDLEtBQUs7b0JBQUUsT0FBTyxTQUFTLENBQUM7Z0JBQzdCLElBQUksT0FBTyxLQUFLLENBQUMsT0FBTyxLQUFLLFFBQVEsSUFBSSxDQUFDLHFCQUFxQixDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUM7b0JBQUUsT0FBTyxTQUFTLENBQUM7Z0JBQy9GLE9BQU8sRUFBRSxJQUFJLEVBQUUsS0FBSyxDQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsS0FBSyxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ3pELENBQUMsRUFBRSxTQUFTLENBQUMsQ0FBQztRQUNsQixDQUFDO1FBQ0QsS0FBSyxDQUFDLFlBQVksQ0FBQyxJQUFJLEVBQUUsZUFBZTtZQUNwQyxPQUFPLFNBQVMsQ0FBQyxjQUFjLEVBQUUsS0FBSyxFQUFFLEVBQUUsRUFBRSxFQUFFO2dCQUMxQyxrRUFBa0U7Z0JBQ2xFLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztnQkFDakMsTUFBTSxPQUFPLEdBQUcsTUFBTSxFQUFFLENBQUMsU0FBUyxDQUFDLFlBQVksRUFBRSxlQUFlLEVBQUUsR0FBRyxFQUFFO29CQUNuRSxPQUFPLEVBQUUsQ0FBQyxHQUFHLENBQUMsWUFBWSxFQUFFLElBQUksRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDbEQsQ0FBQyxDQUFDLENBQUM7Z0JBQ0gsT0FBTyxPQUFPLEtBQUssU0FBUyxDQUFDO1lBQ2pDLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUNkLENBQUM7UUFDRCxLQUFLLENBQUMsVUFBVSxDQUFDLElBQUk7WUFDakIsT0FBTyxTQUFTLENBQUMsWUFBWSxFQUFFLEtBQUssRUFBRSxFQUFFLEVBQUUsRUFBRTtnQkFDeEMsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO2dCQUNqQyxNQUFNLEVBQUUsQ0FBQyxHQUFHLENBQUMsWUFBWSxFQUFFLElBQUksRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDN0MsNkNBQTZDO2dCQUM3QyxNQUFNLFdBQVcsR0FBRyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsWUFBWSxDQUFDLENBQUM7Z0JBQ3BELE9BQU8sQ0FBQSxXQUFXLGFBQVgsV0FBVyx1QkFBWCxXQUFXLENBQUUsT0FBTyxNQUFLLFVBQVUsQ0FBQztZQUMvQyxDQUFDLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDZCxDQUFDO0tBQ0osQ0FBQztBQUNOLENBQUM7QUFFRCxTQUFTLG9CQUFvQjtJQUN6QixJQUFJLENBQUM7UUFDRCxJQUFJLE9BQU8sWUFBWSxLQUFLLFdBQVc7WUFBRSxPQUFPLFNBQVMsQ0FBQztJQUM5RCxDQUFDO0lBQUMsV0FBTSxDQUFDO1FBQ0wsT0FBTyxTQUFTLENBQUM7SUFDckIsQ0FBQztJQUtELFNBQVMsVUFBVTs7UUFDZixJQUFJLENBQUM7WUFDRCxNQUFNLEdBQUcsR0FBRyxZQUFZLENBQUMsT0FBTyxDQUFDLDZCQUE2QixDQUFDLENBQUM7WUFDaEUsSUFBSSxDQUFDLEdBQUc7Z0JBQUUsT0FBTyxTQUFTLENBQUM7WUFDM0IsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQWdCLENBQUM7WUFDOUMsSUFBSSxDQUFDLE1BQU0sSUFBSSxPQUFPLE1BQU0sQ0FBQyxPQUFPLEtBQUssUUFBUSxJQUFJLENBQUMscUJBQXFCLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQztnQkFBRSxPQUFPLFNBQVMsQ0FBQztZQUMzRyxPQUFPLE1BQU0sQ0FBQztRQUNsQixDQUFDO1FBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztZQUNULE9BQU8sQ0FBQyxLQUFLLENBQUMsOENBQThDLEVBQUUsTUFBQyxDQUFXLENBQUMsS0FBSyxtQ0FBSSxDQUFDLENBQUMsQ0FBQztZQUN2RixPQUFPLFNBQVMsQ0FBQztRQUNyQixDQUFDO0lBQ0wsQ0FBQztJQUNELFNBQVMsV0FBVyxDQUFDLElBQW9COztRQUNyQyxJQUFJLENBQUM7WUFDRCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDOUIsWUFBWSxDQUFDLE9BQU8sQ0FBQyw2QkFBNkIsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQztZQUN2RixPQUFPLE9BQU8sQ0FBQztRQUNuQixDQUFDO1FBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztZQUNULE9BQU8sQ0FBQyxLQUFLLENBQUMsNENBQTRDLEVBQUUsTUFBQyxDQUFXLENBQUMsS0FBSyxtQ0FBSSxDQUFDLENBQUMsQ0FBQztZQUNyRixPQUFPLFNBQVMsQ0FBQztRQUNyQixDQUFDO0lBQ0wsQ0FBQztJQUNELE9BQU87UUFDSCxLQUFLLENBQUMsUUFBUTtZQUNWLE1BQU0sTUFBTSxHQUFHLFVBQVUsRUFBRSxDQUFDO1lBQzVCLElBQUksQ0FBQyxNQUFNO2dCQUFFLE9BQU8sU0FBUyxDQUFDO1lBQzlCLE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksRUFBRSxPQUFPLEVBQUUsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQzFELENBQUM7UUFDRCx5UEFBeVA7UUFDelAsS0FBSyxDQUFDLFlBQVksQ0FBQyxJQUFJLEVBQUUsZUFBZTtZQUNwQyxNQUFNLE1BQU0sR0FBRyxVQUFVLEVBQUUsQ0FBQztZQUM1QixJQUFJLENBQUEsTUFBTSxhQUFOLE1BQU0sdUJBQU4sTUFBTSxDQUFFLE9BQU8sTUFBSyxlQUFlO2dCQUFFLE9BQU8sS0FBSyxDQUFDO1lBQ3RELE9BQU8sV0FBVyxDQUFDLElBQUksQ0FBQyxLQUFLLFNBQVMsQ0FBQztRQUMzQyxDQUFDO1FBQ0QsS0FBSyxDQUFDLFVBQVUsQ0FBQyxJQUFJOztZQUNqQixNQUFNLE9BQU8sR0FBRyxXQUFXLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDbEMsSUFBSSxPQUFPLEtBQUssU0FBUztnQkFBRSxPQUFPLEtBQUssQ0FBQztZQUN4QyxPQUFPLENBQUEsTUFBQSxVQUFVLEVBQUUsMENBQUUsT0FBTyxNQUFLLE9BQU8sQ0FBQztRQUM3QyxDQUFDO0tBQ0osQ0FBQztBQUNOLENBQUM7QUFFRCxJQUFJLGVBQWUsR0FBZ0MsU0FBUyxDQUFDO0FBQzdELElBQUksdUJBQXVCLEdBQUcsS0FBSyxDQUFDO0FBQ3BDLEtBQUssVUFBVSxrQkFBa0I7SUFDN0IsSUFBSSx1QkFBdUI7UUFBRSxPQUFPLGVBQWUsQ0FBQztJQUNwRCxJQUFJLElBQUEsYUFBTSxHQUFFLEVBQUUsQ0FBQztRQUNYLE1BQU0sRUFBRSxHQUFHLE1BQU0sZUFBZSxFQUFFLENBQUM7UUFDbkMsSUFBSSxFQUFFLEVBQUUsQ0FBQztZQUNMLGVBQWUsR0FBRyxZQUFZLEVBQUUsQ0FBQztRQUNyQyxDQUFDO0lBQ0wsQ0FBQztTQUFNLENBQUM7UUFDSixlQUFlLEdBQUcsb0JBQW9CLEVBQUUsQ0FBQztJQUM3QyxDQUFDO0lBQ0QsdUJBQXVCLEdBQUcsSUFBSSxDQUFDO0lBQy9CLE9BQU8sZUFBZSxDQUFDO0FBQzNCLENBQUM7QUFFRCxJQUFJLGlCQUFpQixHQUFrRCxvQkFBb0IsQ0FBQztBQUU1Riw4TkFBOE47QUFDOU4sS0FBSyxVQUFVLGNBQWM7O0lBQ3pCLElBQUksWUFBWSxHQUE0QixFQUFFLENBQUM7SUFDL0MsSUFBSSxRQUFRLEdBQUcsQ0FBQyxDQUFDO0lBQ2pCLElBQUksVUFBVSxHQUFHLHlCQUF5QixDQUFDO0lBQzNDLE9BQU8sWUFBWSxDQUFDLE1BQU0sR0FBRyxtQkFBbUIsRUFBRSxDQUFDO1FBQy9DLGdKQUFnSjtRQUNoSixJQUFJLENBQUMsZ0JBQWdCLEVBQUU7WUFBRSxNQUFNO1FBQy9CLElBQUksQ0FBQztZQUNELE1BQU0sTUFBTSxHQUFHLE1BQU0saUJBQWlCLEVBQUUsQ0FBQztZQUN6QyxZQUFZLENBQUMsSUFBSSxDQUFDLE9BQU8sTUFBTSxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ2hGLENBQUM7UUFBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1lBQ1QsT0FBTyxDQUFDLEtBQUssQ0FBQyw0QkFBNEIsRUFBRSxNQUFDLENBQVcsQ0FBQyxLQUFLLG1DQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ3JFLFFBQVEsRUFBRSxDQUFDO1lBQ1gsSUFBSSxRQUFRLElBQUksa0JBQWtCO2dCQUFFLE1BQU07WUFDMUMsTUFBTSxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLFVBQVUsQ0FBQyxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUMsQ0FBQztZQUM5RCxVQUFVLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxVQUFVLEdBQUcsQ0FBQyxFQUFFLHFCQUFxQixDQUFDLENBQUM7UUFDakUsQ0FBQztJQUNMLENBQUM7SUFFRCxJQUFJLFlBQVksQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDNUIsT0FBTyxTQUFTLENBQUM7SUFDckIsQ0FBQztJQUVELHlCQUF5QjtJQUN6QixZQUFZLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDakQsSUFBSSxXQUFXLEdBQUcsWUFBWSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3BFLElBQUksTUFBTSxHQUFHLFdBQVcsQ0FBQyxNQUFNLENBQUM7SUFFaEMsK0JBQStCO0lBQy9CLElBQUksV0FBVyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO0lBQy9DLElBQUksYUFBYSxHQUFHLENBQ2hCLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsR0FBRyxJQUFJLElBQUEsZUFBRyxFQUFDLFdBQVcsR0FBRyxJQUFJLENBQUM7V0FDOUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxHQUFHLElBQUksSUFBQSxrQkFBTSxFQUFDLFdBQVcsR0FBRyxJQUFJLENBQUM7V0FDcEQsSUFBQSxpQkFBSyxFQUFDLFdBQVcsR0FBRyxJQUFJLENBQUMsQ0FDL0IsQ0FBQztJQUNGLElBQUksSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxHQUFHLEVBQUUsQ0FBQztRQUN6QixPQUFPLENBQUMsR0FBRyxDQUFDLEdBQUcsSUFBQSxnQkFBSSxFQUFDLG1CQUFtQixDQUFDLHFCQUFxQixNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLE9BQU8sT0FBTyxhQUFhLE1BQU0sSUFBQSxnQkFBSSxFQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDbkosQ0FBQztJQUVELE9BQU8sV0FBVyxDQUFDO0FBQ3ZCLENBQUM7QUFFRCxTQUFTLGdCQUFnQjtJQUNyQixJQUFJLElBQUEsYUFBTSxHQUFFO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFDMUIsSUFBSSxPQUFPLFFBQVEsS0FBSyxXQUFXO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFDakQsdU1BQXVNO0lBQ3ZNLE9BQU8sUUFBUSxDQUFDLGVBQWUsS0FBSyxRQUFRLENBQUM7QUFDakQsQ0FBQztBQUVELElBQUksY0FBYyxHQUFHLEtBQUssQ0FBQztBQUMzQixLQUFLLFVBQVUsZ0JBQWdCOztJQUMzQixJQUFJLGNBQWM7UUFBRSxPQUFPO0lBQzNCLGNBQWMsR0FBRyxJQUFJLENBQUM7SUFFdEIsSUFBSSxDQUFDO1FBQ0QsTUFBTSxLQUFLLEdBQUcsTUFBTSxrQkFBa0IsRUFBRSxDQUFDO1FBQ3pDLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUNULDJFQUEyRTtZQUMzRSx3RUFBd0U7WUFDeEUsd0RBQXdEO1lBQ3hELE1BQU0sV0FBVyxHQUFHLFdBQVcsRUFBRSxDQUFDO1lBQ2xDLElBQUksVUFBVSxHQUFHLG9CQUFvQixDQUFDO1lBQ3RDLElBQUksVUFBVSxJQUFJLFdBQVcsSUFBSSxVQUFVLENBQUMsY0FBYyxFQUFFLENBQUM7Z0JBQ3pELHdDQUF3QztnQkFDeEMsVUFBVSxHQUFHLFNBQVMsQ0FBQztnQkFDdkIsUUFBUSxDQUFDLGdDQUFnQyxDQUFDLENBQUM7WUFDL0MsQ0FBQztZQUVELE1BQU0sVUFBVSxHQUFHLENBQUMsVUFBVSxJQUFJLFdBQVcsSUFBSSxVQUFVLENBQUMsVUFBVSxDQUFDO1lBQ3ZFLElBQUksVUFBVSxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsRUFBRSxDQUFDO2dCQUNwQyxzSEFBc0g7WUFDMUgsQ0FBQztpQkFBTSxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7Z0JBQ3JCLDRCQUE0QjtnQkFDNUIsTUFBTSxXQUFXLEdBQUcsTUFBTSxjQUFjLEVBQUUsQ0FBQztnQkFDM0MsSUFBSSxXQUFXLEVBQUUsQ0FBQztvQkFDZCw2RUFBNkU7b0JBQzdFLE1BQU0sUUFBUSxHQUFHLFdBQVcsRUFBRSxDQUFDO29CQUMvQixNQUFNLE1BQU0sR0FBRyxXQUFXLENBQUMsTUFBTSxDQUFDO29CQUNsQyxvQkFBb0IsR0FBRzt3QkFDbkIsVUFBVSxFQUFFLE1BQU07d0JBQ2xCLGNBQWMsRUFBRSxRQUFRO3dCQUN4QixNQUFNLEVBQUUsTUFBTTt3QkFDZCxVQUFVLEVBQUUsUUFBUSxHQUFHLHFCQUFxQjt3QkFDNUMsVUFBVSxFQUFFLE1BQU07d0JBQ2xCLGNBQWMsRUFBRSxRQUFRLEdBQUcscUJBQXFCLEdBQUcsQ0FBQzt3QkFDcEQsS0FBSyxFQUFFLFdBQVcsQ0FBQyxLQUFLO3FCQUMzQixDQUFDO29CQUNGLFFBQVEsQ0FBQyw0QkFBNEIsRUFBRSxNQUFNLEVBQUUsb0JBQW9CLEVBQUUscUJBQXFCLEVBQUUsSUFBSSxDQUFDLENBQUM7Z0JBQ3RHLENBQUM7Z0JBQ0QsMkpBQTJKO1lBQy9KLENBQUM7aUJBQU0sSUFBSSxXQUFXLElBQUksVUFBVSxDQUFDLFVBQVUsRUFBRSxDQUFDO2dCQUM5QyxpQkFBaUI7Z0JBQ2pCLE1BQU0sY0FBYyxHQUFHLE1BQU0sY0FBYyxFQUFFLENBQUM7Z0JBQzlDLElBQUksY0FBYyxFQUFFLENBQUM7b0JBQ2pCLG9CQUFvQixHQUFHO3dCQUNuQixVQUFVLEVBQUUsVUFBVSxDQUFDLE1BQU07d0JBQzdCLGNBQWMsRUFBRSxVQUFVLENBQUMsVUFBVTt3QkFDckMsTUFBTSxFQUFFLFVBQVUsQ0FBQyxVQUFVO3dCQUM3QixVQUFVLEVBQUUsVUFBVSxDQUFDLGNBQWM7d0JBQ3JDLFVBQVUsRUFBRSxjQUFjLENBQUMsTUFBTTt3QkFDakMsY0FBYyxFQUFFLFVBQVUsQ0FBQyxjQUFjLEdBQUcscUJBQXFCO3dCQUNqRSxLQUFLLEVBQUUsY0FBYyxDQUFDLEtBQUs7cUJBQzlCLENBQUM7b0JBQ0YsTUFBTSxhQUFhLEdBQUcsb0JBQW9CLENBQUMsY0FBYyxHQUFHLFdBQVcsRUFBRSxDQUFDO29CQUMxRSxRQUFRLENBQUMsa0NBQWtDLEVBQUUsb0JBQW9CLENBQUMsTUFBTSxFQUFFLFdBQVcsRUFBRSxvQkFBb0IsQ0FBQyxVQUFVLEVBQUUsb0JBQW9CLEVBQUUsYUFBYSxFQUFFLElBQUksQ0FBQyxDQUFDO2dCQUN2SyxDQUFDO1lBQ0wsQ0FBQztZQUVELDJLQUEySztZQUMzSyxpQkFBaUIsRUFBRSxDQUFDO1lBQ3BCLE9BQU87UUFDWCxDQUFDO1FBRUQscUhBQXFIO1FBQ3JILE9BQU8sSUFBSSxFQUFFLENBQUM7WUFDVixNQUFNLEtBQUssR0FBRyxNQUFNLEtBQUssQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUNyQywyRUFBMkU7WUFDM0Usd0VBQXdFO1lBQ3hFLHdEQUF3RDtZQUN4RCxNQUFNLFdBQVcsR0FBRyxXQUFXLEVBQUUsQ0FBQztZQUVsQyxJQUFJLFVBQVUsR0FBRyxLQUFLLGFBQUwsS0FBSyx1QkFBTCxLQUFLLENBQUUsSUFBSSxDQUFDO1lBQzdCLE1BQU0sV0FBVyxHQUFHLEtBQUssYUFBTCxLQUFLLHVCQUFMLEtBQUssQ0FBRSxPQUFPLENBQUM7WUFFbkMsSUFBSSxVQUFVLElBQUksV0FBVyxJQUFJLFVBQVUsQ0FBQyxjQUFjLEVBQUUsQ0FBQztnQkFDekQsd0NBQXdDO2dCQUN4QyxVQUFVLEdBQUcsU0FBUyxDQUFDO2dCQUN2QixRQUFRLENBQUMsZ0NBQWdDLENBQUMsQ0FBQztZQUMvQyxDQUFDO1lBRUQsTUFBTSxVQUFVLEdBQUcsQ0FBQyxVQUFVLElBQUksV0FBVyxJQUFJLFVBQVUsQ0FBQyxVQUFVLENBQUM7WUFDdkUsSUFBSSxVQUFVLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxFQUFFLENBQUM7Z0JBQ3BDLG1OQUFtTjtnQkFDbk4sb0JBQW9CLEdBQUcsTUFBQSxLQUFLLGFBQUwsS0FBSyx1QkFBTCxLQUFLLENBQUUsSUFBSSxtQ0FBSSxvQkFBb0IsQ0FBQztnQkFDM0QsTUFBTTtZQUNWLENBQUM7WUFFRCxJQUFJLENBQUMsVUFBVSxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7Z0JBQzlCLG1FQUFtRTtnQkFDbkUsTUFBTSxXQUFXLEdBQUcsTUFBTSxjQUFjLEVBQUUsQ0FBQztnQkFDM0MsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO29CQUNmLG9JQUFvSTtvQkFDcEksb0JBQW9CLEdBQUcsTUFBQSxLQUFLLGFBQUwsS0FBSyx1QkFBTCxLQUFLLENBQUUsSUFBSSxtQ0FBSSxvQkFBb0IsQ0FBQztvQkFDM0QsTUFBTTtnQkFDVixDQUFDO2dCQUNELDZFQUE2RTtnQkFDN0UsTUFBTSxRQUFRLEdBQUcsV0FBVyxFQUFFLENBQUM7Z0JBQy9CLE1BQU0sTUFBTSxHQUFHLFdBQVcsQ0FBQyxNQUFNLENBQUM7Z0JBQ2xDLE1BQU0sUUFBUSxHQUFtQjtvQkFDN0IsVUFBVSxFQUFFLE1BQU07b0JBQ2xCLGNBQWMsRUFBRSxRQUFRO29CQUN4QixNQUFNLEVBQUUsTUFBTTtvQkFDZCxVQUFVLEVBQUUsUUFBUSxHQUFHLHFCQUFxQjtvQkFDNUMsVUFBVSxFQUFFLE1BQU07b0JBQ2xCLGNBQWMsRUFBRSxRQUFRLEdBQUcscUJBQXFCLEdBQUcsQ0FBQztvQkFDcEQsS0FBSyxFQUFFLFdBQVcsQ0FBQyxLQUFLO2lCQUMzQixDQUFDO2dCQUVGLElBQUksV0FBVyxFQUFFLENBQUM7b0JBQ2QsTUFBTSxPQUFPLEdBQUcsTUFBTSxLQUFLLENBQUMsWUFBWSxDQUFDLFFBQVEsRUFBRSxXQUFXLENBQUMsQ0FBQztvQkFDaEUsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO3dCQUNYLFFBQVEsQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDO3dCQUNwQyx1QkFBdUI7d0JBQ3ZCLFNBQVM7b0JBQ2IsQ0FBQztvQkFDRCxvQkFBb0IsR0FBRyxRQUFRLENBQUM7b0JBQ2hDLE9BQU8sQ0FBQyxHQUFHLENBQUMsaUNBQWlDLENBQUMsQ0FBQztvQkFDL0MsTUFBTTtnQkFDVixDQUFDO2dCQUVELElBQUksQ0FBQyxNQUFNLEtBQUssQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztvQkFDcEMsK0NBQStDO29CQUMvQyx3Q0FBd0M7b0JBQ3hDLFFBQVEsQ0FBQyxnREFBZ0QsQ0FBQyxDQUFDO29CQUMzRCxTQUFTO2dCQUNiLENBQUM7Z0JBRUQsZ0NBQWdDO2dCQUNoQyxvQkFBb0IsR0FBRyxRQUFRLENBQUM7Z0JBQ2hDLFFBQVEsQ0FBQyw0QkFBNEIsRUFBRSxNQUFNLEVBQUUsb0JBQW9CLEVBQUUscUJBQXFCLEVBQUUsSUFBSSxDQUFDLENBQUM7Z0JBQ2xHLE1BQU07WUFDVixDQUFDO1lBRUQsSUFBSSxXQUFXLElBQUksVUFBVSxDQUFDLFVBQVUsRUFBRSxDQUFDO2dCQUN2QyxpQkFBaUI7Z0JBQ2pCLE1BQU0sY0FBYyxHQUFHLE1BQU0sY0FBYyxFQUFFLENBQUM7Z0JBQzlDLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztvQkFDbEIsK0lBQStJO29CQUMvSSxvQkFBb0IsR0FBRyxVQUFVLENBQUM7b0JBQ2xDLE1BQU07Z0JBQ1YsQ0FBQztnQkFDRCxNQUFNLE9BQU8sR0FBbUI7b0JBQzVCLFVBQVUsRUFBRSxVQUFVLENBQUMsTUFBTTtvQkFDN0IsY0FBYyxFQUFFLFVBQVUsQ0FBQyxVQUFVO29CQUNyQyxNQUFNLEVBQUUsVUFBVSxDQUFDLFVBQVU7b0JBQzdCLFVBQVUsRUFBRSxVQUFVLENBQUMsY0FBYztvQkFDckMsVUFBVSxFQUFFLGNBQWMsQ0FBQyxNQUFNO29CQUNqQyxjQUFjLEVBQUUsVUFBVSxDQUFDLGNBQWMsR0FBRyxxQkFBcUI7b0JBQ2pFLEtBQUssRUFBRSxjQUFjLENBQUMsS0FBSztpQkFDOUIsQ0FBQztnQkFFRixNQUFNLE9BQU8sR0FBRyxNQUFNLEtBQUssQ0FBQyxZQUFZLENBQUMsT0FBTyxFQUFFLFdBQVcsQ0FBQyxDQUFDO2dCQUMvRCxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7b0JBQ1gsdUJBQXVCO29CQUN2QixTQUFTO2dCQUNiLENBQUM7Z0JBRUQsTUFBTSxhQUFhLEdBQUcsT0FBTyxDQUFDLGNBQWMsR0FBRyxXQUFXLEVBQUUsQ0FBQztnQkFDN0QsUUFBUSxDQUFDLGtDQUFrQyxFQUFFLE9BQU8sQ0FBQyxNQUFNLEVBQUUsV0FBVyxFQUFFLE9BQU8sQ0FBQyxVQUFVLEVBQUUsb0JBQW9CLEVBQUUsYUFBYSxFQUFFLElBQUksQ0FBQyxDQUFDO2dCQUN6SSxvQkFBb0IsR0FBRyxPQUFPLENBQUM7Z0JBQy9CLE1BQU07WUFDVixDQUFDO2lCQUFNLENBQUM7Z0JBQ0osb0JBQW9CLEdBQUcsVUFBVSxDQUFDO2dCQUNsQyxNQUFNLGFBQWEsR0FBRyxVQUFVLENBQUMsVUFBVSxHQUFHLFdBQVcsRUFBRSxDQUFDO2dCQUM1RCxRQUFRLENBQUMsOEJBQThCLEVBQUUsVUFBVSxDQUFDLE1BQU0sRUFBRSxXQUFXLEVBQUUsVUFBVSxDQUFDLFVBQVUsRUFBRSxvQkFBb0IsRUFBRSxhQUFhLEVBQUUsSUFBSSxDQUFDLENBQUM7Z0JBQzNJLE1BQU07WUFDVixDQUFDO1FBQ0wsQ0FBQztRQUVELGlCQUFpQixFQUFFLENBQUM7SUFDeEIsQ0FBQztZQUFTLENBQUM7UUFDUCxjQUFjLEdBQUcsS0FBSyxDQUFDO0lBQzNCLENBQUM7QUFDTCxDQUFDO0FBRUQsU0FBUyx1QkFBdUI7SUFDNUIsZ0JBQWdCLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRTtRQUMzQixPQUFPLENBQUMsSUFBSSxDQUFDLDZCQUE2QixFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ25ELENBQUMsQ0FBQyxDQUFDO0FBQ1AsQ0FBQztBQUVELFdBQVcsQ0FBQyx1QkFBdUIsRUFBRSxxQkFBcUIsQ0FBQyxDQUFDO0FBQzVELFlBQVksQ0FBQyxHQUFHLEVBQUU7SUFDZCxnQkFBZ0IsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFO1FBQzNCLE9BQU8sQ0FBQyxLQUFLLENBQUMsZ0NBQWdDLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDdkQsQ0FBQyxDQUFDLENBQUM7QUFDUCxDQUFDLENBQUMsQ0FBQztBQUVILElBQUksQ0FBQyxJQUFBLGFBQU0sR0FBRSxJQUFJLE9BQU8sTUFBTSxLQUFLLFdBQVcsSUFBSSxPQUFPLFFBQVEsS0FBSyxXQUFXLEVBQUUsQ0FBQztJQUNoRiw0RkFBNEY7SUFDNUYsUUFBUSxDQUFDLGdCQUFnQixDQUFDLGtCQUFrQixFQUFFLHVCQUF1QixDQUFDLENBQUM7SUFDdkUsTUFBTSxDQUFDLGdCQUFnQixDQUFDLE9BQU8sRUFBRSx1QkFBdUIsQ0FBQyxDQUFDO0lBQzFELHlFQUF5RTtJQUN6RSxNQUFNLENBQUMsZ0JBQWdCLENBQUMsU0FBUyxFQUFFLENBQUMsQ0FBQyxFQUFFLEVBQUU7UUFDckMsSUFBSSxDQUFDLENBQUMsR0FBRyxLQUFLLDZCQUE2QixFQUFFLENBQUM7WUFDMUMsdUJBQXVCLEVBQUUsQ0FBQztRQUM5QixDQUFDO0lBQ0wsQ0FBQyxDQUFDLENBQUM7QUFDUCxDQUFDO0FBR0QsTUFBTSxrQkFBa0I7SUFDYixLQUFLLENBQUMsV0FBVztRQUNwQixNQUFNLG9CQUFvQixFQUFFLENBQUM7UUFDN0IsT0FBTyxXQUFXLEVBQUUsQ0FBQztJQUN6QixDQUFDO0NBQ0o7QUFFRCxNQUFNLGNBQWMsR0FBRywrQkFBYyxDQUFDLFFBQVEsQ0FDMUMscURBQXFELEVBQ3JELElBQUksa0JBQWtCLEVBQUUsRUFDeEIsR0FBRyxFQUFFLENBQUMsQ0FBQztJQUNILFdBQVcsRUFBRTtRQUNULG1GQUFtRjtRQUNuRixnR0FBZ0c7UUFDaEcscURBQXFEO1FBQ3JELGNBQWMsRUFBRSxJQUFJO1FBQ3BCLGFBQWEsRUFBRSxJQUFJO0tBQ3RCO0NBQ0osQ0FBQyxFQUNGLEdBQUcsRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQ1Y7QUFDSSw2RUFBNkU7QUFDN0UsMkRBQTJEO0FBQzNELHFCQUFxQjtDQUN4QixDQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBTb2NrZXRGdW5jdGlvbiB9IGZyb20gXCIuLi9Tb2NrZXRGdW5jdGlvblwiO1xuaW1wb3J0IHsgYmx1ZSwgZ3JlZW4sIHJlZCwgeWVsbG93IH0gZnJvbSBcIi4uL3NyYy9mb3JtYXR0aW5nL2xvZ0NvbG9yc1wiO1xuaW1wb3J0IHsgaXNOb2RlIH0gZnJvbSBcIi4uL3NyYy9taXNjXCI7XG5cbi8vIElNUE9SVEFOVCEgV2UgZG9uJ3QgZW5zdXJlIHRoYXQgdGhlIHRpbWVzIG9mIHJldHVybiBhcmUgdW5pcXVlLiBXZSBjYW5ub3QgZW5zdXJlIHRoZXkgYXJlIHVuaXF1ZSBiZWNhdXNlIHRoZSBhbW91bnQgb2YgcHJlY2lzaW9uIGlzIG9ubHkgYWJvdXQgdGVuIHRob3VzYW5kIGRhdGUgdGltZXMgcGVyIG1pbGxpc2Vjb25kLCBXaGljaCB3b3VsZCBtZWFuIGlmIHRoZSBjYWxsaW5nIGNvZGUgY2FsbGVkIGRhdGUubm93IGZyZXF1ZW50bHkgZW5vdWdoLCB3aGljaCBkb2Vzbid0IGV2ZW4gaGF2ZSB0byBiZSB0aGF0IGZyZXF1ZW50LCBpdCBjb3VsZCBzbG93bHkgZHJpZnQgZmFydGhlciBhbmQgZmFydGhlciBhaGVhZCBvZiB0aGUgcmVhbCB0aW1lLCB3aGljaCB3b3VsZCBiZSByZWFsbHkgYmFkLlxuXG5tb2R1bGUuYWxsb3djbGllbnQgPSB0cnVlO1xuXG5cbmNvbnN0IFVQREFURV9WRVJJRllfQ09VTlQgPSAzO1xuXG5jb25zdCBVUERBVEVfVFJBTlNJVElPTl9HQVAgPSAxMDAwICogNjAgKiAyMDtcbmNvbnN0IFVQREFURV9DSEVDS19JTlRFUlZBTCA9IDEwMDAgKiA2MCAqIDU7XG5jb25zdCBERUJVR19USU1FX1NZTkMgPSBmYWxzZTtcblxuY29uc3QgRkVUQ0hfUkVUUllfSU5JVElBTF9ERUxBWSA9IDEwMDA7XG5jb25zdCBGRVRDSF9SRVRSWV9NQVhfREVMQVkgPSAxMDAwICogMzA7XG5jb25zdCBGRVRDSF9NQVhfRkFJTFVSRVMgPSA4O1xuY29uc3QgTlRQX1RJTUVPVVQgPSAxMDAwICogMTA7XG5cbi8vIFRoZSBvbi1kaXNrIExNREIgZGF0YWJhc2UgY2FuIGJlY29tZSBjb3JydXB0IChleCwgYSBwcm9jZXNzIGtpbGxlZCBtaWQtd3JpdGUpLiBBZnRlciB0aGlzIG1hbnlcbi8vICBjb25zZWN1dGl2ZSBmYWlsZWQgb3BlcmF0aW9ucyB3ZSBzdG9wIGp1c3QgcmVvcGVuaW5nIHRoZSBoYW5kbGUgYW5kIGRlbGV0ZSB0aGUgZmlsZXMgdG8gc3RhcnRcbi8vICBmcmVzaCAtIHRoZSBkYXRhIGlzIG9ubHkgYSBjYWNoZWQgdGltZSBvZmZzZXQsIHNvIHdpcGluZyBpdCBtZXJlbHkgZm9yY2VzIGEgcmUtbWVhc3VyZW1lbnQuXG5jb25zdCBMTURCX01BWF9DT1JSVVBUSU9OX0ZBSUxVUkVTID0gMztcblxuLy8gVGltZSBjYW4gbmV2ZXIgZ28gYmFja3dhcmRzLCBidXQgd2UgY2FuIHJ1biBhdCBhIHNsb3dlciByYXRlIHVudGlsIHRoZSBvdXRwdXQgdGltZSBhbGxvd3Ncbi8vICAgICAgdGhlIHJlYWwgdGltZSB0byBjYXRjaCB1cCB3aXRoIGl0LlxuY29uc3QgTUlOSU1VTV9USU1FX1JBVEUgPSAwLjU7XG5cbmNvbnN0IFRIUk9XX09OX0VSUk9SID0gZmFsc2U7XG5cbi8vIEh1Z2VseSBpbXBvcnRhbnQgYXMgaWYgd2UgZG9uJ3Qgc3luY2hyb25pemUgYmV0d2VlbiBwcm9jZXNzZXMsIGl0IG1lYW5zIG91ciBsb2dzIGFyZSBnb2luZyB0byBiZSBjb25mdXNpbmcgYW5kIG91dCBvZiBvcmRlci5cbi8vICAtIE9mIGNvdXJzZSwgY3Jvc3MtbWFjaGluZSwgdGhlIGxvZ3MgY291bGQgYmUgb3V0IG9mIG9yZGVyLiBIb3dldmVyLCBkdWUgdG8gdGhlIGxhdGVuY3kgYmV0d2VlbiBtYWNoaW5lcywgdGhhdCdzIGxlc3MgbGlrZWx5LiBUaGUgbGF0ZW5jeSB3aWxsIHByb2JhYmx5IGJlIGEgZmV3IG1pbGxpc2Vjb25kcywgYW5kIGhvcGVmdWxseSwgb3VyIHRpbWUgaXNuJ3QgbW9yZSB0aGFuIGEgZmV3IG1pbGxpc2Vjb25kcyBvZmYgb2YgdGhlIHJlYWwgdGltZS4gSG93ZXZlciwgYmV0d2VlbiBwcm9jZXNzZXMsIHRoZSBsYXRlbmN5IGNvdWxkIGVhc2lseSBiZSBtaWNyb3NlY29uZHMsIGFuZCBvdXIgdGltZSB3aWxsIGFic29sdXRlbHkgY2VydGFpbmx5IGJlIG1pY3Jvc2Vjb25kcyBvZmYgb2YgdGhlIHJlYWwgdGltZS5cbmxldCBVU0VfTE1EQl9QUk9DRVNTX1NZTkMgPSB0cnVlO1xuXG4vLyBCcm93c2VyIHRhYnMgc3luY2hyb25pemUgdmlhIGxvY2FsU3RvcmFnZSwgZm9yIHRoZSBzYW1lIHJlYXNvbiBwcm9jZXNzZXMgc3luY2hyb25pemUgdmlhIExNREIuXG5jb25zdCBUSU1FX09GRlNFVF9MT0NBTF9TVE9SQUdFX0tFWSA9IFwic29ja2V0LWZ1bmN0aW9uLXRpbWUtb2Zmc2V0XCI7XG5cbmZ1bmN0aW9uIGRlYnVnTG9nKC4uLmFyZ3M6IGFueVtdKSB7XG4gICAgaWYgKERFQlVHX1RJTUVfU1lOQykge1xuICAgICAgICBjb25zb2xlLmxvZyhcIltUaW1lU3luY11cIiwgLi4uYXJncyk7XG4gICAgfVxufVxuXG4vLyBUaGUgcmF3IGV2aWRlbmNlIGJlaGluZCBhbiBvZmZzZXQgbWVhc3VyZW1lbnQuIHNlbmRUaW1lL3JlY2VpdmVUaW1lIGFyZSBvbiB0aGUgbG9jYWwgc3lzdGVtIGNsb2NrLCBzZXJ2ZXJUaW1lIGlzIHRoZSByZW1vdGUgY2xvY2sncyB0aW1lc3RhbXAsIGFuZCBvZmZzZXQgaXMgZGVyaXZlZCBmcm9tIHRoZW0gYnkgYXNzdW1pbmcgdGhlIHNlcnZlciB0aW1lc3RhbXBlZCBhdCB0aGUgbWlkcG9pbnQgb2YgdGhlIHJvdW5kIHRyaXAuXG5leHBvcnQgdHlwZSBUaW1lT2Zmc2V0UHJvb2YgPSB7XG4gICAgc2VuZFRpbWU6IG51bWJlcjtcbiAgICByZWNlaXZlVGltZTogbnVtYmVyO1xuICAgIHNlcnZlclRpbWU6IG51bWJlcjtcbiAgICBvZmZzZXQ6IG51bWJlcjtcbn07XG5cbmV4cG9ydCB0eXBlIFRpbWVPZmZzZXRNZWFzdXJlbWVudCA9IHtcbiAgICBvZmZzZXQ6IG51bWJlcjtcbiAgICBwcm9vZj86IFRpbWVPZmZzZXRQcm9vZjtcbn07XG5cbnR5cGUgVGltZU9mZnNldERhdGEgPSB7XG4gICAgbGFzdE9mZnNldDogbnVtYmVyO1xuICAgIGxhc3RVcGRhdGVUaW1lOiBudW1iZXI7XG4gICAgb2Zmc2V0OiBudW1iZXI7XG4gICAgdXBkYXRlVGltZTogbnVtYmVyO1xuICAgIG5leHRPZmZzZXQ6IG51bWJlcjtcbiAgICBuZXh0VXBkYXRlVGltZTogbnVtYmVyO1xuICAgIC8vIFByb29mIG9mIHRoZSBtb3N0IHJlY2VudCBtZWFzdXJlbWVudCAodGhlIG9uZSB0aGF0IHByb2R1Y2VkIG5leHRPZmZzZXQpLlxuICAgIHByb29mPzogVGltZU9mZnNldFByb29mO1xufTtcblxubGV0IGNhY2hlZFRpbWVPZmZzZXREYXRhOiBUaW1lT2Zmc2V0RGF0YSB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZDtcbmxldCBkaWRGaXJzdFRpbWVTeW5jID0gZmFsc2U7XG5sZXQgb25GaXJzdFRpbWVTeW5jITogKCkgPT4gdm9pZDtcbmxldCBmaXJzdFRpbWVTeW5jUHJvbWlzZSA9IG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlKSA9PiB7XG4gICAgb25GaXJzdFRpbWVTeW5jID0gcmVzb2x2ZTtcbn0pO1xuZnVuY3Rpb24gbWFya0ZpcnN0VGltZVN5bmMoKSB7XG4gICAgaWYgKGRpZEZpcnN0VGltZVN5bmMpIHJldHVybjtcbiAgICBkaWRGaXJzdFRpbWVTeW5jID0gdHJ1ZTtcbiAgICBvbkZpcnN0VGltZVN5bmMoKTtcbn1cblxuY29uc3QgYmFzZUdldFRpbWUgPSBEYXRlLm5vdztcbmxldCBsYXN0VGltZSA9IDA7XG5sZXQgbGFzdEJhc2VUaW1lID0gMDtcblxuZXhwb3J0IGZ1bmN0aW9uIGdldFRpbWVDb21wb25lbnRzRGV0YWlsZWQoKToge1xuICAgIHN5c3RlbVRpbWU6IG51bWJlcjtcbiAgICBmcm9tT2Zmc2V0OiBudW1iZXI7XG4gICAgdG9PZmZzZXQ6IG51bWJlcjtcbiAgICBmcm9tVGltZTogbnVtYmVyO1xuICAgIHRvVGltZTogbnVtYmVyO1xufSB7XG4gICAgY29uc3Qgc3lzdGVtVGltZSA9IGJhc2VHZXRUaW1lKCk7XG4gICAgY29uc3QgZGF0YSA9IGNhY2hlZFRpbWVPZmZzZXREYXRhO1xuXG4gICAgaWYgKCFkYXRhKSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBzeXN0ZW1UaW1lLFxuICAgICAgICAgICAgZnJvbU9mZnNldDogMCxcbiAgICAgICAgICAgIHRvT2Zmc2V0OiAwLFxuICAgICAgICAgICAgZnJvbVRpbWU6IHN5c3RlbVRpbWUsXG4gICAgICAgICAgICB0b1RpbWU6IHN5c3RlbVRpbWUsXG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgaWYgKHN5c3RlbVRpbWUgPCBkYXRhLmxhc3RVcGRhdGVUaW1lKSB7XG4gICAgICAgIC8vIEJlZm9yZSBldmVyeXRoaW5nIChzaG91bGRuJ3QgaGFwcGVuKVxuICAgICAgICBpZiAoVEhST1dfT05fRVJST1IpIHRocm93IG5ldyBFcnJvcihgc3lzdGVtVGltZSAke3N5c3RlbVRpbWV9IGlzIGJlZm9yZSBsYXN0VXBkYXRlVGltZSAke2RhdGEubGFzdFVwZGF0ZVRpbWV9YCk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBzeXN0ZW1UaW1lLFxuICAgICAgICAgICAgZnJvbU9mZnNldDogZGF0YS5sYXN0T2Zmc2V0LFxuICAgICAgICAgICAgdG9PZmZzZXQ6IGRhdGEubGFzdE9mZnNldCxcbiAgICAgICAgICAgIGZyb21UaW1lOiBkYXRhLmxhc3RVcGRhdGVUaW1lLFxuICAgICAgICAgICAgdG9UaW1lOiBkYXRhLmxhc3RVcGRhdGVUaW1lLFxuICAgICAgICB9O1xuICAgIH0gZWxzZSBpZiAoc3lzdGVtVGltZSA8IGRhdGEudXBkYXRlVGltZSkge1xuICAgICAgICAvLyBTbWVhciBiZXR3ZWVuIGxhc3RPZmZzZXQgYW5kIG9mZnNldFxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3lzdGVtVGltZSxcbiAgICAgICAgICAgIGZyb21PZmZzZXQ6IGRhdGEubGFzdE9mZnNldCxcbiAgICAgICAgICAgIHRvT2Zmc2V0OiBkYXRhLm9mZnNldCxcbiAgICAgICAgICAgIGZyb21UaW1lOiBkYXRhLmxhc3RVcGRhdGVUaW1lLFxuICAgICAgICAgICAgdG9UaW1lOiBkYXRhLnVwZGF0ZVRpbWUsXG4gICAgICAgIH07XG4gICAgfSBlbHNlIGlmIChzeXN0ZW1UaW1lIDwgZGF0YS5uZXh0VXBkYXRlVGltZSkge1xuICAgICAgICAvLyBTbWVhciBiZXR3ZWVuIG9mZnNldCBhbmQgbmV4dE9mZnNldFxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3lzdGVtVGltZSxcbiAgICAgICAgICAgIGZyb21PZmZzZXQ6IGRhdGEub2Zmc2V0LFxuICAgICAgICAgICAgdG9PZmZzZXQ6IGRhdGEubmV4dE9mZnNldCxcbiAgICAgICAgICAgIGZyb21UaW1lOiBkYXRhLnVwZGF0ZVRpbWUsXG4gICAgICAgICAgICB0b1RpbWU6IGRhdGEubmV4dFVwZGF0ZVRpbWUsXG4gICAgICAgIH07XG4gICAgfSBlbHNlIHtcbiAgICAgICAgLy8gUGFzdCBldmVyeXRoaW5nIChzaG91bGRuJ3QgaGFwcGVuIG9mdGVuKVxuICAgICAgICBpZiAoVEhST1dfT05fRVJST1IpIHRocm93IG5ldyBFcnJvcihgc3lzdGVtVGltZSAke3N5c3RlbVRpbWV9IGlzIHBhc3QgbmV4dFVwZGF0ZVRpbWUgJHtkYXRhLm5leHRVcGRhdGVUaW1lfWApO1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3lzdGVtVGltZSxcbiAgICAgICAgICAgIGZyb21PZmZzZXQ6IGRhdGEubmV4dE9mZnNldCxcbiAgICAgICAgICAgIHRvT2Zmc2V0OiBkYXRhLm5leHRPZmZzZXQsXG4gICAgICAgICAgICBmcm9tVGltZTogZGF0YS5uZXh0VXBkYXRlVGltZSxcbiAgICAgICAgICAgIHRvVGltZTogZGF0YS5uZXh0VXBkYXRlVGltZSxcbiAgICAgICAgfTtcbiAgICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRUaW1lQ29tcG9uZW50cygpOiB7IHN5c3RlbVRpbWU6IG51bWJlcjsgb2Zmc2V0OiBudW1iZXIgfSB7XG4gICAgY29uc3QgZGV0YWlsZWQgPSBnZXRUaW1lQ29tcG9uZW50c0RldGFpbGVkKCk7XG4gICAgY29uc3QgZWxhcHNlZCA9IGRldGFpbGVkLnN5c3RlbVRpbWUgLSBkZXRhaWxlZC5mcm9tVGltZTtcbiAgICBjb25zdCBkdXJhdGlvbiA9IGRldGFpbGVkLnRvVGltZSAtIGRldGFpbGVkLmZyb21UaW1lO1xuICAgIGNvbnN0IGZyYWN0aW9uID0gZHVyYXRpb24gPiAwID8gTWF0aC5taW4oMSwgZWxhcHNlZCAvIGR1cmF0aW9uKSA6IDA7XG4gICAgY29uc3Qgb2Zmc2V0ID0gZGV0YWlsZWQuZnJvbU9mZnNldCArIChkZXRhaWxlZC50b09mZnNldCAtIGRldGFpbGVkLmZyb21PZmZzZXQpICogZnJhY3Rpb247XG4gICAgcmV0dXJuIHsgc3lzdGVtVGltZTogZGV0YWlsZWQuc3lzdGVtVGltZSwgb2Zmc2V0IH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRUcnVlVGltZSgpIHtcbiAgICBjb25zdCB7IHN5c3RlbVRpbWUsIG9mZnNldCB9ID0gZ2V0VGltZUNvbXBvbmVudHMoKTtcbiAgICBsZXQgdGltZSA9IHN5c3RlbVRpbWUgKyBvZmZzZXQ7XG5cbiAgICAvLyBPbmx5IGFkanVzdCB0aW1lIG9uY2Ugd2UgaGF2ZSBhIHRpbWUgb2Zmc2V0LiBPdGhlcndpc2Ugc3lzdGVtcyB3aXRoIGEgcmVhbGx5IGJhZCBjbG9ja1xuICAgIC8vICBtaWdodCB0YWtlIGRheXMgYmUgY29ycmVjdC4gSXQgaXMgYmV0dGVyIGZvciB0aGUgdGltZSB0byBqdW1wIG9uY2UgYXQgc3RhcnR1cCwgcmF0aGVyXG4gICAgLy8gIHRoYW4gYmUgb2ZmIGJ5IGRheXMsIGZvciBkYXlzIGF0IGEgdGltZS5cbiAgICBpZiAobGFzdFRpbWUgJiYgb2Zmc2V0KSB7XG4gICAgICAgIGlmICh0aW1lIDwgbGFzdFRpbWUpIHtcbiAgICAgICAgICAgIGxldCBkaWZmID0gc3lzdGVtVGltZSAtIGxhc3RCYXNlVGltZTtcbiAgICAgICAgICAgIGlmIChkaWZmID49IDApIHtcbiAgICAgICAgICAgICAgICAvLyBTb21lIHRpbWUgcGFzc2VkLCBzbyB3ZSBoYXZlIGEgYmFzZWxpbmUgZm9yIGhvdyBtdWNoIHRvIGluY3JlYXNlIHRoZSB0aW1lIGJ5LlxuICAgICAgICAgICAgICAgIC8vICBUaGlzIGFsbG93cyB0aGUgcmVhbCB0aW1lIHRvIGNhdGNoIHVwIHdpdGggb3VyIHRpbWUgbmF0dXJhbGx5LlxuICAgICAgICAgICAgICAgIHRpbWUgPSBsYXN0VGltZSArIGRpZmYgKiBNSU5JTVVNX1RJTUVfUkFURTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgLy8gVGhlIGlzc3VlIGlzIHRoZSBzeXN0ZW0gdGltZSBnb2luZyBiYWNrd2FyZHMuIEluIHRoaXMgY2FzZSwgYWxsb3cgdGhlIHRpbWUgdG8gY2hhbmdlXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG4gICAgbGFzdFRpbWUgPSB0aW1lO1xuICAgIGxhc3RCYXNlVGltZSA9IHN5c3RlbVRpbWU7XG4gICAgcmV0dXJuIHRpbWU7XG59XG5leHBvcnQgZnVuY3Rpb24gZ2V0VHJ1ZVRpbWVPZmZzZXQoKSB7XG4gICAgY29uc3QgeyBvZmZzZXQgfSA9IGdldFRpbWVDb21wb25lbnRzKCk7XG4gICAgcmV0dXJuIG9mZnNldDtcbn1cbmV4cG9ydCBmdW5jdGlvbiBnZXRUcnVlVGltZVByb29mKCk6IFRpbWVPZmZzZXRQcm9vZiB8IHVuZGVmaW5lZCB7XG4gICAgcmV0dXJuIGNhY2hlZFRpbWVPZmZzZXREYXRhPy5wcm9vZjtcbn1cbmV4cG9ydCBmdW5jdGlvbiB3YWl0Rm9yRmlyc3RUaW1lU3luYygpOiBQcm9taXNlPHZvaWQ+IHwgdW5kZWZpbmVkIHtcbiAgICBpZiAoZGlkRmlyc3RUaW1lU3luYykgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICByZXR1cm4gZmlyc3RUaW1lU3luY1Byb21pc2U7XG59XG5kZWNsYXJlIGdsb2JhbCB7XG4gICAgdmFyIFRSVUVfVElNRV9BTFJFQURZX1NISU1NRUQ6IGJvb2xlYW47XG59XG5sZXQgc2hpbW1lZCA9IGZhbHNlO1xuZXhwb3J0IGZ1bmN0aW9uIHNoaW1EYXRlTm93KCkge1xuICAgIGlmIChzaGltbWVkKSByZXR1cm47XG4gICAgc2hpbW1lZCA9IHRydWU7XG4gICAgaWYgKGdsb2JhbFRoaXMuVFJVRV9USU1FX0FMUkVBRFlfU0hJTU1FRCkgcmV0dXJuO1xuICAgIGdsb2JhbFRoaXMuVFJVRV9USU1FX0FMUkVBRFlfU0hJTU1FRCA9IHRydWU7XG4gICAgRGF0ZS5ub3cgPSBnZXRUcnVlVGltZTtcbn1cbmV4cG9ydCBmdW5jdGlvbiBnZXRCcm93c2VyVGltZSgpIHtcbiAgICByZXR1cm4gYmFzZUdldFRpbWUoKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNldEdldFRpbWVPZmZzZXRCYXNlKGJhc2U6ICgpID0+IFByb21pc2U8bnVtYmVyIHwgVGltZU9mZnNldE1lYXN1cmVtZW50Pikge1xuICAgIGdldFRpbWVPZmZzZXRCYXNlID0gYmFzZTtcbn1cblxuZnVuY3Rpb24gbWFrZU1lYXN1cmVtZW50KHNlbmRUaW1lOiBudW1iZXIsIHJlY2VpdmVUaW1lOiBudW1iZXIsIHNlcnZlclRpbWU6IG51bWJlcik6IFRpbWVPZmZzZXRNZWFzdXJlbWVudCB7XG4gICAgY29uc3QgcHJlZGljdGVkU2VydmVyVG9DbGllbnRMYXRlbmN5ID0gKHJlY2VpdmVUaW1lIC0gc2VuZFRpbWUpIC8gMjtcbiAgICBjb25zdCBvZmZzZXQgPSBzZXJ2ZXJUaW1lICsgcHJlZGljdGVkU2VydmVyVG9DbGllbnRMYXRlbmN5IC0gcmVjZWl2ZVRpbWU7XG4gICAgcmV0dXJuIHsgb2Zmc2V0LCBwcm9vZjogeyBzZW5kVGltZSwgcmVjZWl2ZVRpbWUsIHNlcnZlclRpbWUsIG9mZnNldCB9IH07XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGRlZmF1bHRHZXRUaW1lT2Zmc2V0KCk6IFByb21pc2U8VGltZU9mZnNldE1lYXN1cmVtZW50PiB7XG4gICAgaWYgKCFpc05vZGUoKSkge1xuICAgICAgICBsZXQgc2VuZFRpbWUgPSBiYXNlR2V0VGltZSgpO1xuICAgICAgICBsZXQgc2VydmVyVHJ1ZVRpbWUgPSBhd2FpdCBUaW1lQ29udHJvbGxlci5ub2Rlc1tTb2NrZXRGdW5jdGlvbi5icm93c2VyTm9kZUlkKCldLmdldFRydWVUaW1lKCk7XG4gICAgICAgIGxldCByZWNlaXZlVGltZSA9IGJhc2VHZXRUaW1lKCk7XG4gICAgICAgIHJldHVybiBtYWtlTWVhc3VyZW1lbnQoc2VuZFRpbWUsIHJlY2VpdmVUaW1lLCBzZXJ2ZXJUcnVlVGltZSk7XG4gICAgfVxuXG4gICAgY29uc3QgZGdyYW0gPSBhd2FpdCBpbXBvcnQoXCJkZ3JhbVwiKTtcbiAgICBjb25zdCBOVFBfU0VSVkVSID0gXCJ0aW1lLmdvb2dsZS5jb21cIjtcbiAgICBjb25zdCBOVFBfUE9SVCA9IDEyMztcbiAgICBjb25zdCBOVFBfUEFDS0VUX1NJWkUgPSA0ODtcbiAgICBjb25zdCBOVFBfRVBPQ0hfT0ZGU0VUID0gMjIwODk4ODgwMDAwMDsgLy8gTnVtYmVyIG9mIG1pbGxpc2Vjb25kcyBiZXR3ZWVuIDE5MDAtMDEtMDEgYW5kIDE5NzAtMDEtMDFcbiAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICBjb25zdCBjbGllbnQgPSBkZ3JhbS5jcmVhdGVTb2NrZXQoXCJ1ZHA0XCIpO1xuICAgICAgICBjb25zdCBtZXNzYWdlID0gQnVmZmVyLmFsbG9jKE5UUF9QQUNLRVRfU0laRSk7XG5cbiAgICAgICAgLy8gU2V0IHRoZSBmaXJzdCBieXRlIHRvIHJlcHJlc2VudCBOVFAgY2xpZW50IHJlcXVlc3QgKExJID0gMCwgVk4gPSAzLCBNb2RlID0gMylcbiAgICAgICAgbWVzc2FnZVswXSA9IDB4MUI7XG5cbiAgICAgICAgY29uc3Qgc2VuZFRpbWUgPSBiYXNlR2V0VGltZSgpO1xuXG4gICAgICAgIC8vIE5UUCBpcyBVRFAsIHNvIGEgbG9zdCBwYWNrZXQgbWVhbnMgdGhlIFwibWVzc2FnZVwiIGV2ZW50IHNpbXBseSBuZXZlciBmaXJlcy4gV2l0aG91dCBhIHRpbWVvdXQgdGhhdCBsZWF2ZXMgdXBkYXRlVGltZU9mZnNldCBzdHVjayAodXBkYXRpbmdPZmZzZXQgc3RheXMgdHJ1ZSBmb3JldmVyKSwgcGVybWFuZW50bHkgc3RvcHBpbmcgYWxsIGZ1dHVyZSBzeW5jcy5cbiAgICAgICAgY29uc3QgdGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgY2xpZW50LmNsb3NlKCk7XG4gICAgICAgICAgICByZWplY3QobmV3IEVycm9yKGBOVFAgcmVxdWVzdCB0byAke05UUF9TRVJWRVJ9IHRpbWVkIG91dGApKTtcbiAgICAgICAgfSwgTlRQX1RJTUVPVVQpO1xuXG4gICAgICAgIGNsaWVudC5zZW5kKG1lc3NhZ2UsIDAsIG1lc3NhZ2UubGVuZ3RoLCBOVFBfUE9SVCwgTlRQX1NFUlZFUik7XG4gICAgICAgIGNsaWVudC5vbihcImVycm9yXCIsIChlcnIpID0+IHtcbiAgICAgICAgICAgIGNsZWFyVGltZW91dCh0aW1lb3V0KTtcbiAgICAgICAgICAgIGNsaWVudC5jbG9zZSgpO1xuICAgICAgICAgICAgcmVqZWN0KGVycik7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGNsaWVudC5vbihcIm1lc3NhZ2VcIiwgKG1zZykgPT4ge1xuICAgICAgICAgICAgY29uc3QgcmVjZWl2ZVRpbWUgPSBiYXNlR2V0VGltZSgpO1xuICAgICAgICAgICAgLy8gQSB0aHJvdyBpbiB0aGlzIGhhbmRsZXIgd291bGQgYmUgYW4gdW5jYXVnaHQgZXhjZXB0aW9uLCBub3QgYSByZWplY3Rpb24sIHNvIHZhbGlkYXRlIGJlZm9yZSByZWFkaW5nIGZpeGVkIG9mZnNldHMuXG4gICAgICAgICAgICBpZiAobXNnLmxlbmd0aCA8IE5UUF9QQUNLRVRfU0laRSkgcmV0dXJuO1xuICAgICAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXQpO1xuXG4gICAgICAgICAgICAvLyBFeHRyYWN0IHRoZSB0cmFuc21pdCB0aW1lc3RhbXAgZnJvbSB0aGUgc2VydmVyIHJlc3BvbnNlXG4gICAgICAgICAgICBjb25zdCB0cmFuc21pdFRpbWVzdGFtcFNlY29uZHMgPSBtc2cucmVhZFVJbnQzMkJFKDQwKTtcbiAgICAgICAgICAgIGNvbnN0IHRyYW5zbWl0VGltZXN0YW1wRnJhY3Rpb24gPSBtc2cucmVhZFVJbnQzMkJFKDQ0KTtcbiAgICAgICAgICAgIGNvbnN0IHRyYW5zbWl0VGltZXN0YW1wID0gKHRyYW5zbWl0VGltZXN0YW1wU2Vjb25kcyAqIDEwMDApICsgKHRyYW5zbWl0VGltZXN0YW1wRnJhY3Rpb24gKiAxMDAwIC8gMHgxMDAwMDAwMDApIC0gTlRQX0VQT0NIX09GRlNFVDtcblxuICAgICAgICAgICAgY2xpZW50LmNsb3NlKCk7XG4gICAgICAgICAgICByZXNvbHZlKG1ha2VNZWFzdXJlbWVudChzZW5kVGltZSwgcmVjZWl2ZVRpbWUsIHRyYW5zbWl0VGltZXN0YW1wKSk7XG4gICAgICAgIH0pO1xuICAgIH0pO1xufVxuXG5mdW5jdGlvbiBpc1ZhbGlkVGltZU9mZnNldERhdGEoZGF0YTogdW5rbm93bik6IGRhdGEgaXMgVGltZU9mZnNldERhdGEge1xuICAgIGNvbnN0IGQgPSBkYXRhIGFzIFRpbWVPZmZzZXREYXRhIHwgdW5kZWZpbmVkO1xuICAgIHJldHVybiAoXG4gICAgICAgICEhZCAmJlxuICAgICAgICB0eXBlb2YgZC5sYXN0T2Zmc2V0ID09PSBcIm51bWJlclwiICYmXG4gICAgICAgIHR5cGVvZiBkLmxhc3RVcGRhdGVUaW1lID09PSBcIm51bWJlclwiICYmXG4gICAgICAgIHR5cGVvZiBkLm9mZnNldCA9PT0gXCJudW1iZXJcIiAmJlxuICAgICAgICB0eXBlb2YgZC51cGRhdGVUaW1lID09PSBcIm51bWJlclwiICYmXG4gICAgICAgIHR5cGVvZiBkLm5leHRPZmZzZXQgPT09IFwibnVtYmVyXCIgJiZcbiAgICAgICAgdHlwZW9mIGQubmV4dFVwZGF0ZVRpbWUgPT09IFwibnVtYmVyXCJcbiAgICApO1xufVxuXG50eXBlIFRpbWVPZmZzZXRTdG9yZUVudHJ5ID0ge1xuICAgIGRhdGE6IFRpbWVPZmZzZXREYXRhO1xuICAgIHZlcnNpb246IG51bWJlcjtcbn07XG50eXBlIFRpbWVPZmZzZXRTdG9yZSA9IHtcbiAgICBnZXRFbnRyeSgpOiBQcm9taXNlPFRpbWVPZmZzZXRTdG9yZUVudHJ5IHwgdW5kZWZpbmVkPjtcbiAgICAvLyBBdG9taWMgY29uZGl0aW9uYWwgd3JpdGUsIG9ubHkgc3VjY2VlZHMgaWYgdGhlIHN0b3JlZCB2ZXJzaW9uIG1hdGNoZXMgZXhwZWN0ZWRWZXJzaW9uLlxuICAgIHB1dElmVmVyc2lvbihkYXRhOiBUaW1lT2Zmc2V0RGF0YSwgZXhwZWN0ZWRWZXJzaW9uOiBudW1iZXIpOiBQcm9taXNlPGJvb2xlYW4+O1xuICAgIC8vIFVuY29uZGl0aW9uYWwgd3JpdGUgd2hlbiBubyBlbnRyeSBleGlzdHMgeWV0LiBSZXR1cm5zIGZhbHNlIGlmIGFub3RoZXIgd3JpdGVyIHJhY2VkIHVzLCBpbiB3aGljaCBjYXNlIHRoZSBjYWxsZXIgc2hvdWxkIHJlLXJlYWQgYW5kIHVzZSB0aGVpciBkYXRhLlxuICAgIHB1dEluaXRpYWwoZGF0YTogVGltZU9mZnNldERhdGEpOiBQcm9taXNlPGJvb2xlYW4+O1xufTtcblxudHlwZSBUaW1lT2Zmc2V0RGIgPSBpbXBvcnQoXCJsbWRiXCIpLlJvb3REYXRhYmFzZTxUaW1lT2Zmc2V0RGF0YSwgc3RyaW5nPjtcbmxldCB0aW1lT2Zmc2V0RGI6IFRpbWVPZmZzZXREYiB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZDtcbmxldCB0aW1lT2Zmc2V0RGJQYXRoOiBzdHJpbmcgfCB1bmRlZmluZWQgPSB1bmRlZmluZWQ7XG5cbmFzeW5jIGZ1bmN0aW9uIG9wZW5UaW1lT2Zmc2V0RGIoKTogUHJvbWlzZTxUaW1lT2Zmc2V0RGIgfCB1bmRlZmluZWQ+IHtcbiAgICB0cnkge1xuICAgICAgICBjb25zdCBsbWRiID0gYXdhaXQgaW1wb3J0KFwibG1kYlwiKTtcbiAgICAgICAgY29uc3QgcGF0aCA9IGF3YWl0IGltcG9ydChcInBhdGhcIik7XG4gICAgICAgIGNvbnN0IG9zID0gYXdhaXQgaW1wb3J0KFwib3NcIik7XG5cbiAgICAgICAgdGltZU9mZnNldERiUGF0aCA9IHBhdGguam9pbihvcy50bXBkaXIoKSwgXCJzb2NrZXQtZnVuY3Rpb24tdGltZS1vZmZzZXQtMlwiKTtcbiAgICAgICAgcmV0dXJuIGxtZGIub3BlbjxUaW1lT2Zmc2V0RGF0YSwgc3RyaW5nPih7XG4gICAgICAgICAgICBwYXRoOiB0aW1lT2Zmc2V0RGJQYXRoLFxuICAgICAgICAgICAgLy8gRW5hYmxlIHZlcnNpb25pbmcgZm9yIGNvbmRpdGlvbmFsIHdyaXRlc1xuICAgICAgICAgICAgdXNlVmVyc2lvbnM6IHRydWUsXG4gICAgICAgIH0pO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihcIkVycm9yIG9wZW5pbmcgTE1EQiBkYXRhYmFzZTpcIiwgKGUgYXMgRXJyb3IpLnN0YWNrID8/IGUpO1xuICAgICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gZ2V0VGltZU9mZnNldERiKCkge1xuICAgIGlmICghVVNFX0xNREJfUFJPQ0VTU19TWU5DKSByZXR1cm4gdW5kZWZpbmVkO1xuICAgIGlmICghaXNOb2RlKCkpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgaWYgKHRpbWVPZmZzZXREYikgcmV0dXJuIHRpbWVPZmZzZXREYjtcbiAgICB0aW1lT2Zmc2V0RGIgPSBhd2FpdCBvcGVuVGltZU9mZnNldERiKCk7XG4gICAgcmV0dXJuIHRpbWVPZmZzZXREYjtcbn1cblxuLy8gQ2xvc2VzIHRoZSBjdXJyZW50IGhhbmRsZSAoc28gdGhlIG5leHQgb3AgcmVvcGVucyBhIGZyZXNoIG9uZSkgYW5kLCB3aGVuIHdpcGUgaXMgc2V0LCBkZWxldGVzIHRoZVxuLy8gIG9uLWRpc2sgZmlsZXMgc28gYSBjb3JydXB0IGRhdGFiYXNlIGlzIHJlcGxhY2VkIHdpdGggYW4gZW1wdHkgb25lLiBsbWRiIHdyaXRlcyB0aGUgbWFpbiBmaWxlIGF0XG4vLyAgdGhlIHBhdGggcGx1cyBhIHNpYmxpbmcgbG9jayBmaWxlLCBzbyB3ZSBjbGVhciB0aGUgcGF0aCBhbmQgaXRzIGxvY2sgdmFyaWFudHMuXG5hc3luYyBmdW5jdGlvbiByZWNyZWF0ZVRpbWVPZmZzZXREYihjb25maWc6IHsgd2lwZTogYm9vbGVhbiB9KSB7XG4gICAgbGV0IGRiID0gdGltZU9mZnNldERiO1xuICAgIHRpbWVPZmZzZXREYiA9IHVuZGVmaW5lZDtcbiAgICBpZiAoZGIpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGF3YWl0IGRiLmNsb3NlKCk7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJFcnJvciBjbG9zaW5nIExNREIgZGF0YWJhc2U6XCIsIChlIGFzIEVycm9yKS5zdGFjayA/PyBlKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICBpZiAoY29uZmlnLndpcGUgJiYgdGltZU9mZnNldERiUGF0aCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgZnMgPSBhd2FpdCBpbXBvcnQoXCJmc1wiKTtcbiAgICAgICAgICAgIGZvciAobGV0IHN1ZmZpeCBvZiBbXCJcIiwgXCItbG9ja1wiLCBcIi5sb2NrXCJdKSB7XG4gICAgICAgICAgICAgICAgYXdhaXQgZnMucHJvbWlzZXMucm0odGltZU9mZnNldERiUGF0aCArIHN1ZmZpeCwgeyByZWN1cnNpdmU6IHRydWUsIGZvcmNlOiB0cnVlIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uc29sZS5sb2coeWVsbG93KGBXaXBlZCBjb3JydXB0IHRpbWUtb2Zmc2V0IExNREIgZGF0YWJhc2UgYXQgJHt0aW1lT2Zmc2V0RGJQYXRofWApKTtcbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIkVycm9yIHdpcGluZyBjb3JydXB0IExNREIgZGF0YWJhc2U6XCIsIChlIGFzIEVycm9yKS5zdGFjayA/PyBlKTtcbiAgICAgICAgfVxuICAgIH1cbn1cblxuLy8gQSBjb3JydXB0IGRhdGFiYXNlIHRocm93cyBvbiBldmVyeSByZWFkIGFuZCBvbiB0aGUgcmVhZC1iYWNrIGluc2lkZSBldmVyeSB3cml0ZSwgYW5kIHdlIGNhbid0XG4vLyAgcmVwYWlyIGl0LiBTbyB3ZSBydW4gZWFjaCBvcCB0aHJvdWdoIGhlcmU6IG9uIHN1Y2Nlc3Mgd2UgcmVzZXQgdGhlIGZhaWx1cmUgY291bnQsIGFuZCBvbiBlcnJvclxuLy8gIHdlIHJlb3BlbiB0aGUgaGFuZGxlICh3aXBpbmcgdGhlIGZpbGVzIG9uY2Ugd2UndmUgaGl0IHRoZSB0aHJlc2hvbGQpIGFuZCByZXR1cm4gdGhlIGZhbGxiYWNrIHNvXG4vLyAgY2FsbGVycyBkZWdyYWRlIHRvIHJ1bm5pbmcgdW5zeW5jZWQgaW5zdGVhZCBvZiB0aHJvd2luZy4gT3BzIGFyZSBzZXJpYWxpemVkIGJ5IHVwZGF0aW5nT2Zmc2V0LFxuLy8gIHNvIHRoZXJlIGlzIG5vIGNvbmN1cnJlbnQgYWNjZXNzIHRvIHdvcnJ5IGFib3V0LlxubGV0IGxtZGJDb3JydXB0aW9uRmFpbHVyZXMgPSAwO1xuYXN5bmMgZnVuY3Rpb24gcnVuTG1kYk9wPFQ+KG5hbWU6IHN0cmluZywgb3A6IChkYjogVGltZU9mZnNldERiKSA9PiBQcm9taXNlPFQ+LCBmYWxsYmFjazogVCk6IFByb21pc2U8VD4ge1xuICAgIGNvbnN0IGRiID0gYXdhaXQgZ2V0VGltZU9mZnNldERiKCk7XG4gICAgaWYgKCFkYikgcmV0dXJuIGZhbGxiYWNrO1xuICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IG9wKGRiKTtcbiAgICAgICAgbG1kYkNvcnJ1cHRpb25GYWlsdXJlcyA9IDA7XG4gICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgICBsbWRiQ29ycnVwdGlvbkZhaWx1cmVzKys7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoYEVycm9yIGR1cmluZyBMTURCICR7bmFtZX0gKGZhaWx1cmUgJHtsbWRiQ29ycnVwdGlvbkZhaWx1cmVzfS8ke0xNREJfTUFYX0NPUlJVUFRJT05fRkFJTFVSRVN9KTpgLCAoZSBhcyBFcnJvcikuc3RhY2sgPz8gZSk7XG4gICAgICAgIGxldCB3aXBlID0gbG1kYkNvcnJ1cHRpb25GYWlsdXJlcyA+PSBMTURCX01BWF9DT1JSVVBUSU9OX0ZBSUxVUkVTO1xuICAgICAgICBhd2FpdCByZWNyZWF0ZVRpbWVPZmZzZXREYih7IHdpcGUgfSk7XG4gICAgICAgIGlmICh3aXBlKSBsbWRiQ29ycnVwdGlvbkZhaWx1cmVzID0gMDtcbiAgICAgICAgcmV0dXJuIGZhbGxiYWNrO1xuICAgIH1cbn1cblxuZnVuY3Rpb24gZ2V0TG1kYlN0b3JlKCk6IFRpbWVPZmZzZXRTdG9yZSB7XG4gICAgcmV0dXJuIHtcbiAgICAgICAgYXN5bmMgZ2V0RW50cnkoKSB7XG4gICAgICAgICAgICByZXR1cm4gcnVuTG1kYk9wKFwiZ2V0RW50cnlcIiwgYXN5bmMgKGRiKSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgZW50cnkgPSBhd2FpdCBkYi5nZXRFbnRyeShcInRpbWVPZmZzZXRcIik7IC8vIEdldHMge3ZhbHVlLCB2ZXJzaW9ufSBhdG9taWNhbGx5XG4gICAgICAgICAgICAgICAgaWYgKCFlbnRyeSkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICAgICAgICAgICAgICBpZiAodHlwZW9mIGVudHJ5LnZlcnNpb24gIT09IFwibnVtYmVyXCIgfHwgIWlzVmFsaWRUaW1lT2Zmc2V0RGF0YShlbnRyeS52YWx1ZSkpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHsgZGF0YTogZW50cnkudmFsdWUsIHZlcnNpb246IGVudHJ5LnZlcnNpb24gfTtcbiAgICAgICAgICAgIH0sIHVuZGVmaW5lZCk7XG4gICAgICAgIH0sXG4gICAgICAgIGFzeW5jIHB1dElmVmVyc2lvbihkYXRhLCBleHBlY3RlZFZlcnNpb24pIHtcbiAgICAgICAgICAgIHJldHVybiBydW5MbWRiT3AoXCJwdXRJZlZlcnNpb25cIiwgYXN5bmMgKGRiKSA9PiB7XG4gICAgICAgICAgICAgICAgLy8gVXNlIHJhbmRvbSB2ZXJzaW9uIHRvIG1pbmltaXplIGNvbGxpc2lvbiBwcm9iYWJpbGl0eSBvbiByZXRyaWVzXG4gICAgICAgICAgICAgICAgY29uc3QgbmV3VmVyc2lvbiA9IE1hdGgucmFuZG9tKCk7XG4gICAgICAgICAgICAgICAgY29uc3Qgc3VjY2VzcyA9IGF3YWl0IGRiLmlmVmVyc2lvbihcInRpbWVPZmZzZXRcIiwgZXhwZWN0ZWRWZXJzaW9uLCAoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBkYi5wdXQoXCJ0aW1lT2Zmc2V0XCIsIGRhdGEsIG5ld1ZlcnNpb24pO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIHJldHVybiBzdWNjZXNzICE9PSB1bmRlZmluZWQ7XG4gICAgICAgICAgICB9LCBmYWxzZSk7XG4gICAgICAgIH0sXG4gICAgICAgIGFzeW5jIHB1dEluaXRpYWwoZGF0YSkge1xuICAgICAgICAgICAgcmV0dXJuIHJ1bkxtZGJPcChcInB1dEluaXRpYWxcIiwgYXN5bmMgKGRiKSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgbmV3VmVyc2lvbiA9IE1hdGgucmFuZG9tKCk7XG4gICAgICAgICAgICAgICAgYXdhaXQgZGIucHV0KFwidGltZU9mZnNldFwiLCBkYXRhLCBuZXdWZXJzaW9uKTtcbiAgICAgICAgICAgICAgICAvLyBSZWFkIGJhY2sgdG8gc2VlIHdoYXQgYWN0dWFsbHkgZ290IHdyaXR0ZW5cbiAgICAgICAgICAgICAgICBjb25zdCBhY3R1YWxFbnRyeSA9IGF3YWl0IGRiLmdldEVudHJ5KFwidGltZU9mZnNldFwiKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gYWN0dWFsRW50cnk/LnZlcnNpb24gPT09IG5ld1ZlcnNpb247XG4gICAgICAgICAgICB9LCBmYWxzZSk7XG4gICAgICAgIH0sXG4gICAgfTtcbn1cblxuZnVuY3Rpb24gZ2V0TG9jYWxTdG9yYWdlU3RvcmUoKTogVGltZU9mZnNldFN0b3JlIHwgdW5kZWZpbmVkIHtcbiAgICB0cnkge1xuICAgICAgICBpZiAodHlwZW9mIGxvY2FsU3RvcmFnZSA9PT0gXCJ1bmRlZmluZWRcIikgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9IGNhdGNoIHtcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG4gICAgdHlwZSBTdG9yZWRWYWx1ZSA9IHtcbiAgICAgICAgdmVyc2lvbjogbnVtYmVyO1xuICAgICAgICBkYXRhOiBUaW1lT2Zmc2V0RGF0YTtcbiAgICB9O1xuICAgIGZ1bmN0aW9uIHJlYWRTdG9yZWQoKTogU3RvcmVkVmFsdWUgfCB1bmRlZmluZWQge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgcmF3ID0gbG9jYWxTdG9yYWdlLmdldEl0ZW0oVElNRV9PRkZTRVRfTE9DQUxfU1RPUkFHRV9LRVkpO1xuICAgICAgICAgICAgaWYgKCFyYXcpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgICAgICAgICBjb25zdCBwYXJzZWQgPSBKU09OLnBhcnNlKHJhdykgYXMgU3RvcmVkVmFsdWU7XG4gICAgICAgICAgICBpZiAoIXBhcnNlZCB8fCB0eXBlb2YgcGFyc2VkLnZlcnNpb24gIT09IFwibnVtYmVyXCIgfHwgIWlzVmFsaWRUaW1lT2Zmc2V0RGF0YShwYXJzZWQuZGF0YSkpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgICAgICAgICByZXR1cm4gcGFyc2VkO1xuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiRXJyb3IgcmVhZGluZyB0aW1lIG9mZnNldCBmcm9tIGxvY2FsU3RvcmFnZTpcIiwgKGUgYXMgRXJyb3IpLnN0YWNrID8/IGUpO1xuICAgICAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICAgICAgfVxuICAgIH1cbiAgICBmdW5jdGlvbiB3cml0ZVN0b3JlZChkYXRhOiBUaW1lT2Zmc2V0RGF0YSk6IG51bWJlciB8IHVuZGVmaW5lZCB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCB2ZXJzaW9uID0gTWF0aC5yYW5kb20oKTtcbiAgICAgICAgICAgIGxvY2FsU3RvcmFnZS5zZXRJdGVtKFRJTUVfT0ZGU0VUX0xPQ0FMX1NUT1JBR0VfS0VZLCBKU09OLnN0cmluZ2lmeSh7IHZlcnNpb24sIGRhdGEgfSkpO1xuICAgICAgICAgICAgcmV0dXJuIHZlcnNpb247XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJFcnJvciB3cml0aW5nIHRpbWUgb2Zmc2V0IHRvIGxvY2FsU3RvcmFnZTpcIiwgKGUgYXMgRXJyb3IpLnN0YWNrID8/IGUpO1xuICAgICAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4ge1xuICAgICAgICBhc3luYyBnZXRFbnRyeSgpIHtcbiAgICAgICAgICAgIGNvbnN0IHN0b3JlZCA9IHJlYWRTdG9yZWQoKTtcbiAgICAgICAgICAgIGlmICghc3RvcmVkKSByZXR1cm4gdW5kZWZpbmVkO1xuICAgICAgICAgICAgcmV0dXJuIHsgZGF0YTogc3RvcmVkLmRhdGEsIHZlcnNpb246IHN0b3JlZC52ZXJzaW9uIH07XG4gICAgICAgIH0sXG4gICAgICAgIC8vIGxvY2FsU3RvcmFnZSBoYXMgbm8gYXRvbWljIGNvbXBhcmUtYW5kLXN3YXAsIGJ1dCBhY2Nlc3Mgd2l0aGluIGEgdGFiIGlzIHN5bmNocm9ub3VzLCBzbyByZS1yZWFkaW5nIHRoZSB2ZXJzaW9uIGltbWVkaWF0ZWx5IGJlZm9yZSB3cml0aW5nIHNocmlua3MgdGhlIHJhY2Ugd2luZG93IHRvIGVmZmVjdGl2ZWx5IG5vdGhpbmcuIFdvcnN0IGNhc2UgdHdvIHRhYnMgYm90aCBmZXRjaCBhbiBvZmZzZXQsIHdoaWNoIGlzIGhhcm1sZXNzLlxuICAgICAgICBhc3luYyBwdXRJZlZlcnNpb24oZGF0YSwgZXhwZWN0ZWRWZXJzaW9uKSB7XG4gICAgICAgICAgICBjb25zdCBzdG9yZWQgPSByZWFkU3RvcmVkKCk7XG4gICAgICAgICAgICBpZiAoc3RvcmVkPy52ZXJzaW9uICE9PSBleHBlY3RlZFZlcnNpb24pIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgIHJldHVybiB3cml0ZVN0b3JlZChkYXRhKSAhPT0gdW5kZWZpbmVkO1xuICAgICAgICB9LFxuICAgICAgICBhc3luYyBwdXRJbml0aWFsKGRhdGEpIHtcbiAgICAgICAgICAgIGNvbnN0IHZlcnNpb24gPSB3cml0ZVN0b3JlZChkYXRhKTtcbiAgICAgICAgICAgIGlmICh2ZXJzaW9uID09PSB1bmRlZmluZWQpIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgIHJldHVybiByZWFkU3RvcmVkKCk/LnZlcnNpb24gPT09IHZlcnNpb247XG4gICAgICAgIH0sXG4gICAgfTtcbn1cblxubGV0IHRpbWVPZmZzZXRTdG9yZTogVGltZU9mZnNldFN0b3JlIHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkO1xubGV0IHRpbWVPZmZzZXRTdG9yZVJlc29sdmVkID0gZmFsc2U7XG5hc3luYyBmdW5jdGlvbiBnZXRUaW1lT2Zmc2V0U3RvcmUoKTogUHJvbWlzZTxUaW1lT2Zmc2V0U3RvcmUgfCB1bmRlZmluZWQ+IHtcbiAgICBpZiAodGltZU9mZnNldFN0b3JlUmVzb2x2ZWQpIHJldHVybiB0aW1lT2Zmc2V0U3RvcmU7XG4gICAgaWYgKGlzTm9kZSgpKSB7XG4gICAgICAgIGNvbnN0IGRiID0gYXdhaXQgZ2V0VGltZU9mZnNldERiKCk7XG4gICAgICAgIGlmIChkYikge1xuICAgICAgICAgICAgdGltZU9mZnNldFN0b3JlID0gZ2V0TG1kYlN0b3JlKCk7XG4gICAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgICB0aW1lT2Zmc2V0U3RvcmUgPSBnZXRMb2NhbFN0b3JhZ2VTdG9yZSgpO1xuICAgIH1cbiAgICB0aW1lT2Zmc2V0U3RvcmVSZXNvbHZlZCA9IHRydWU7XG4gICAgcmV0dXJuIHRpbWVPZmZzZXRTdG9yZTtcbn1cblxubGV0IGdldFRpbWVPZmZzZXRCYXNlOiAoKSA9PiBQcm9taXNlPG51bWJlciB8IFRpbWVPZmZzZXRNZWFzdXJlbWVudD4gPSBkZWZhdWx0R2V0VGltZU9mZnNldDtcblxuLy8gUmV0dXJucyB1bmRlZmluZWQgaWYgd2UgY291bGRuJ3QgZ2V0IGFueSBtZWFzdXJlbWVudHMuIENhbGxlcnMgbXVzdCBOT1QgdHJlYXQgdGhhdCBhcyBhbiBvZmZzZXQgb2YgMCAtIGEgZmFicmljYXRlZCBvZmZzZXQgd3JpdHRlbiB0byB0aGUgc2hhcmVkIHN0b3JlIHdvdWxkIHBvaXNvbiBldmVyeSBvdGhlciBwcm9jZXNzL3RhYiBmb3IgdGhlIGZ1bGwgdHJhbnNpdGlvbiB3aW5kb3cuXG5hc3luYyBmdW5jdGlvbiBmZXRjaE5ld09mZnNldCgpOiBQcm9taXNlPFRpbWVPZmZzZXRNZWFzdXJlbWVudCB8IHVuZGVmaW5lZD4ge1xuICAgIGxldCBtZWFzdXJlbWVudHM6IFRpbWVPZmZzZXRNZWFzdXJlbWVudFtdID0gW107XG4gICAgbGV0IGZhaWx1cmVzID0gMDtcbiAgICBsZXQgcmV0cnlEZWxheSA9IEZFVENIX1JFVFJZX0lOSVRJQUxfREVMQVk7XG4gICAgd2hpbGUgKG1lYXN1cmVtZW50cy5sZW5ndGggPCBVUERBVEVfVkVSSUZZX0NPVU5UKSB7XG4gICAgICAgIC8vIFRoZSB0YWIgbWF5IGhhdmUgYmVlbiBoaWRkZW4gbWlkLWZldGNoLiBNZWFzdXJlbWVudHMgZnJvbSBhIHRocm90dGxlZCB0YWIgYXJlIHdvcnNlIHRoYW4gbm9uZSwgc28gc3RvcCBhbmQgdXNlIHdoYXRldmVyIHdlIGFscmVhZHkgY29sbGVjdGVkLlxuICAgICAgICBpZiAoIWNhbk1lYXN1cmVPZmZzZXQoKSkgYnJlYWs7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBnZXRUaW1lT2Zmc2V0QmFzZSgpO1xuICAgICAgICAgICAgbWVhc3VyZW1lbnRzLnB1c2godHlwZW9mIHJlc3VsdCA9PT0gXCJudW1iZXJcIiA/IHsgb2Zmc2V0OiByZXN1bHQgfSA6IHJlc3VsdCk7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJFcnJvciBnZXR0aW5nIHRpbWUgb2Zmc2V0OlwiLCAoZSBhcyBFcnJvcikuc3RhY2sgPz8gZSk7XG4gICAgICAgICAgICBmYWlsdXJlcysrO1xuICAgICAgICAgICAgaWYgKGZhaWx1cmVzID49IEZFVENIX01BWF9GQUlMVVJFUykgYnJlYWs7XG4gICAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgcmV0cnlEZWxheSkpO1xuICAgICAgICAgICAgcmV0cnlEZWxheSA9IE1hdGgubWluKHJldHJ5RGVsYXkgKiAyLCBGRVRDSF9SRVRSWV9NQVhfREVMQVkpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgaWYgKG1lYXN1cmVtZW50cy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG5cbiAgICAvLyBQaWNrIHRoZSBtaWRkbGUgb2Zmc2V0XG4gICAgbWVhc3VyZW1lbnRzLnNvcnQoKGEsIGIpID0+IGEub2Zmc2V0IC0gYi5vZmZzZXQpO1xuICAgIGxldCBtZWFzdXJlbWVudCA9IG1lYXN1cmVtZW50c1tNYXRoLmZsb29yKG1lYXN1cmVtZW50cy5sZW5ndGggLyAyKV07XG4gICAgbGV0IG9mZnNldCA9IG1lYXN1cmVtZW50Lm9mZnNldDtcblxuICAgIC8vIExvZyBpZiBvZmZzZXQgaXMgc2lnbmlmaWNhbnRcbiAgICBsZXQgb2Zmc2V0Um91bmQgPSBNYXRoLmFicyhNYXRoLnJvdW5kKG9mZnNldCkpO1xuICAgIGxldCBvZmZzZXRDb2xvcmVkID0gKFxuICAgICAgICBNYXRoLmFicyhvZmZzZXQpID4gNjAwICYmIHJlZChvZmZzZXRSb3VuZCArIFwibXNcIilcbiAgICAgICAgfHwgTWF0aC5hYnMob2Zmc2V0KSA+IDMwMCAmJiB5ZWxsb3cob2Zmc2V0Um91bmQgKyBcIm1zXCIpXG4gICAgICAgIHx8IGdyZWVuKG9mZnNldFJvdW5kICsgXCJtc1wiKVxuICAgICk7XG4gICAgaWYgKE1hdGguYWJzKG9mZnNldCkgPiA1MDApIHtcbiAgICAgICAgY29uc29sZS5sb2coYCR7Ymx1ZShcIlN5bmNocm9uaXplZCB0aW1lXCIpfSwgbG9jYWwgY2xvY2sgd2FzICR7b2Zmc2V0ID4gMCA/IFwiYmVoaW5kXCIgOiBcImFoZWFkXCJ9IGJ5ICR7b2Zmc2V0Q29sb3JlZH0gQCAke2JsdWUoRGF0ZS5ub3coKSArIFwiXCIpfWApO1xuICAgIH1cblxuICAgIHJldHVybiBtZWFzdXJlbWVudDtcbn1cblxuZnVuY3Rpb24gY2FuTWVhc3VyZU9mZnNldCgpIHtcbiAgICBpZiAoaXNOb2RlKCkpIHJldHVybiB0cnVlO1xuICAgIGlmICh0eXBlb2YgZG9jdW1lbnQgPT09IFwidW5kZWZpbmVkXCIpIHJldHVybiB0cnVlO1xuICAgIC8vIEhpZGRlbiB0YWJzIGdldCB0aGVpciB0aW1lcnMgYW5kIG1lc3NhZ2UgZGVsaXZlcnkgdGhyb3R0bGVkLCB3aGljaCBjb3JydXB0cyB0aGUgcm91bmQtdHJpcCBsYXRlbmN5IGVzdGltYXRlIGFuZCB0aGVyZWZvcmUgdGhlIG9mZnNldC4gQSB2aXNpYmxlIHRhYiB3aWxsIG1lYXN1cmUgYW5kIHNoYXJlIHZpYSBsb2NhbFN0b3JhZ2UgaW5zdGVhZC5cbiAgICByZXR1cm4gZG9jdW1lbnQudmlzaWJpbGl0eVN0YXRlICE9PSBcImhpZGRlblwiO1xufVxuXG5sZXQgdXBkYXRpbmdPZmZzZXQgPSBmYWxzZTtcbmFzeW5jIGZ1bmN0aW9uIHVwZGF0ZVRpbWVPZmZzZXQoKSB7XG4gICAgaWYgKHVwZGF0aW5nT2Zmc2V0KSByZXR1cm47XG4gICAgdXBkYXRpbmdPZmZzZXQgPSB0cnVlO1xuXG4gICAgdHJ5IHtcbiAgICAgICAgY29uc3Qgc3RvcmUgPSBhd2FpdCBnZXRUaW1lT2Zmc2V0U3RvcmUoKTtcbiAgICAgICAgaWYgKCFzdG9yZSkge1xuICAgICAgICAgICAgLy8gSU1QT1JUQU5UOiBBbHdheXMgdXNlIGJhc2VHZXRUaW1lKCkgZm9yIHNjaGVkdWxpbmcsIG5ldmVyIGdldFRydWVUaW1lKCkuXG4gICAgICAgICAgICAvLyBPdXIgdXBkYXRlIHNjaGVkdWxlIG11c3QgYmUgYmFzZWQgb24gdGhlIHN0YWJsZSBzeXN0ZW0gY2xvY2ssIG5vdCB0aGVcbiAgICAgICAgICAgIC8vIG9mZnNldC1hZGp1c3RlZCB0aW1lIHdoaWNoIGNoYW5nZXMgYXMgd2Ugc3luY2hyb25pemUuXG4gICAgICAgICAgICBjb25zdCBjdXJyZW50VGltZSA9IGJhc2VHZXRUaW1lKCk7XG4gICAgICAgICAgICBsZXQgY2FjaGVkRGF0YSA9IGNhY2hlZFRpbWVPZmZzZXREYXRhO1xuICAgICAgICAgICAgaWYgKGNhY2hlZERhdGEgJiYgY3VycmVudFRpbWUgPj0gY2FjaGVkRGF0YS5uZXh0VXBkYXRlVGltZSkge1xuICAgICAgICAgICAgICAgIC8vIFBhc3QgdGhlIGVuZCAtIHJlc2V0IGFuZCByZWluaXRpYWxpemVcbiAgICAgICAgICAgICAgICBjYWNoZWREYXRhID0gdW5kZWZpbmVkO1xuICAgICAgICAgICAgICAgIGRlYnVnTG9nKFwiUGFzdCBuZXh0VXBkYXRlVGltZSwgcmVzZXR0aW5nXCIpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCBuZWVkc0ZldGNoID0gIWNhY2hlZERhdGEgfHwgY3VycmVudFRpbWUgPj0gY2FjaGVkRGF0YS51cGRhdGVUaW1lO1xuICAgICAgICAgICAgaWYgKG5lZWRzRmV0Y2ggJiYgIWNhbk1lYXN1cmVPZmZzZXQoKSkge1xuICAgICAgICAgICAgICAgIC8vIEtlZXAgdXNpbmcgd2hhdGV2ZXIgb2Zmc2V0IHdlIGhhdmUgKGV2ZW4gYSBzdGFsZSBvbmUgaXMgYmV0dGVyIHRoYW4gYSBtZWFzdXJlbWVudCBza2V3ZWQgYnkgYmFja2dyb3VuZCB0aHJvdHRsaW5nKS5cbiAgICAgICAgICAgIH0gZWxzZSBpZiAoIWNhY2hlZERhdGEpIHtcbiAgICAgICAgICAgICAgICAvLyBGaXJzdCB0aW1lIGluaXRpYWxpemF0aW9uXG4gICAgICAgICAgICAgICAgY29uc3QgbWVhc3VyZW1lbnQgPSBhd2FpdCBmZXRjaE5ld09mZnNldCgpO1xuICAgICAgICAgICAgICAgIGlmIChtZWFzdXJlbWVudCkge1xuICAgICAgICAgICAgICAgICAgICAvLyBSZS1yZWFkIHRoZSB0aW1lLCBhcyBmZXRjaE5ld09mZnNldCBjYW4gdGFrZSBtaW51dGVzIHdoZW4gaXQgaGFzIHRvIHJldHJ5LlxuICAgICAgICAgICAgICAgICAgICBjb25zdCBpbml0VGltZSA9IGJhc2VHZXRUaW1lKCk7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IG9mZnNldCA9IG1lYXN1cmVtZW50Lm9mZnNldDtcbiAgICAgICAgICAgICAgICAgICAgY2FjaGVkVGltZU9mZnNldERhdGEgPSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBsYXN0T2Zmc2V0OiBvZmZzZXQsXG4gICAgICAgICAgICAgICAgICAgICAgICBsYXN0VXBkYXRlVGltZTogaW5pdFRpbWUsXG4gICAgICAgICAgICAgICAgICAgICAgICBvZmZzZXQ6IG9mZnNldCxcbiAgICAgICAgICAgICAgICAgICAgICAgIHVwZGF0ZVRpbWU6IGluaXRUaW1lICsgVVBEQVRFX1RSQU5TSVRJT05fR0FQLFxuICAgICAgICAgICAgICAgICAgICAgICAgbmV4dE9mZnNldDogb2Zmc2V0LFxuICAgICAgICAgICAgICAgICAgICAgICAgbmV4dFVwZGF0ZVRpbWU6IGluaXRUaW1lICsgVVBEQVRFX1RSQU5TSVRJT05fR0FQICogMixcbiAgICAgICAgICAgICAgICAgICAgICAgIHByb29mOiBtZWFzdXJlbWVudC5wcm9vZixcbiAgICAgICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICAgICAgZGVidWdMb2coXCJJbml0aWFsaXplZCAtIHRpbWUgb2Zmc2V0OlwiLCBvZmZzZXQsIFwibXMsIG5leHQgdXBkYXRlIGluXCIsIFVQREFURV9UUkFOU0lUSU9OX0dBUCwgXCJtc1wiKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgLy8gT24gdG90YWwgZmFpbHVyZSB3ZSBsZWF2ZSB0aGUgZGF0YSB1bnNldCAoZ2V0VHJ1ZVRpbWUgdGhlbiB1c2VzIHRoZSByYXcgc3lzdGVtIGNsb2NrKSBhbmQgdGhlIG5leHQgY2hlY2sgaW50ZXJ2YWwgcmV0cmllcy4gV2UgbmV2ZXIgZmFicmljYXRlIGFuIG9mZnNldC5cbiAgICAgICAgICAgIH0gZWxzZSBpZiAoY3VycmVudFRpbWUgPj0gY2FjaGVkRGF0YS51cGRhdGVUaW1lKSB7XG4gICAgICAgICAgICAgICAgLy8gVGltZSB0byByb3RhdGVcbiAgICAgICAgICAgICAgICBjb25zdCBuZXdNZWFzdXJlbWVudCA9IGF3YWl0IGZldGNoTmV3T2Zmc2V0KCk7XG4gICAgICAgICAgICAgICAgaWYgKG5ld01lYXN1cmVtZW50KSB7XG4gICAgICAgICAgICAgICAgICAgIGNhY2hlZFRpbWVPZmZzZXREYXRhID0ge1xuICAgICAgICAgICAgICAgICAgICAgICAgbGFzdE9mZnNldDogY2FjaGVkRGF0YS5vZmZzZXQsXG4gICAgICAgICAgICAgICAgICAgICAgICBsYXN0VXBkYXRlVGltZTogY2FjaGVkRGF0YS51cGRhdGVUaW1lLFxuICAgICAgICAgICAgICAgICAgICAgICAgb2Zmc2V0OiBjYWNoZWREYXRhLm5leHRPZmZzZXQsXG4gICAgICAgICAgICAgICAgICAgICAgICB1cGRhdGVUaW1lOiBjYWNoZWREYXRhLm5leHRVcGRhdGVUaW1lLFxuICAgICAgICAgICAgICAgICAgICAgICAgbmV4dE9mZnNldDogbmV3TWVhc3VyZW1lbnQub2Zmc2V0LFxuICAgICAgICAgICAgICAgICAgICAgICAgbmV4dFVwZGF0ZVRpbWU6IGNhY2hlZERhdGEubmV4dFVwZGF0ZVRpbWUgKyBVUERBVEVfVFJBTlNJVElPTl9HQVAsXG4gICAgICAgICAgICAgICAgICAgICAgICBwcm9vZjogbmV3TWVhc3VyZW1lbnQucHJvb2YsXG4gICAgICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHRpbWVVbnRpbE5leHQgPSBjYWNoZWRUaW1lT2Zmc2V0RGF0YS5uZXh0VXBkYXRlVGltZSAtIGJhc2VHZXRUaW1lKCk7XG4gICAgICAgICAgICAgICAgICAgIGRlYnVnTG9nKFwiQWR2YW5jaW5nIHRpbWUgb2Zmc2V0IC0gY3VycmVudDpcIiwgY2FjaGVkVGltZU9mZnNldERhdGEub2Zmc2V0LCBcIm1zLCBuZXh0OlwiLCBjYWNoZWRUaW1lT2Zmc2V0RGF0YS5uZXh0T2Zmc2V0LCBcIm1zLCBuZXh0IHVwZGF0ZSBpblwiLCB0aW1lVW50aWxOZXh0LCBcIm1zXCIpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gQWx3YXlzIHJlc29sdmUgdGhlIGZpcnN0IHN5bmMsIGV2ZW4gb24gZmFpbHVyZSAtIFNvY2tldEZ1bmN0aW9uLm1vdW50IGF3YWl0cyB0aGlzLCBhbmQgaGFuZ2luZyBmb3JldmVyIGlzIHdvcnNlIHRoYW4gdGVtcG9yYXJpbHkgcnVubmluZyBvbiB0aGUgdW5hZGp1c3RlZCBzeXN0ZW0gY2xvY2suXG4gICAgICAgICAgICBtYXJrRmlyc3RUaW1lU3luYygpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gQXQgdGhpcyBwb2ludCB3ZSBoYXZlIGEgc2hhcmVkIHN0b3JlIChMTURCIGZvciBwcm9jZXNzZXMsIGxvY2FsU3RvcmFnZSBmb3IgdGFicykgd2l0aCBhdG9taWMtaXNoIHZlcnNpb25lZCB3cml0ZXMuXG4gICAgICAgIHdoaWxlICh0cnVlKSB7XG4gICAgICAgICAgICBjb25zdCBlbnRyeSA9IGF3YWl0IHN0b3JlLmdldEVudHJ5KCk7XG4gICAgICAgICAgICAvLyBJTVBPUlRBTlQ6IEFsd2F5cyB1c2UgYmFzZUdldFRpbWUoKSBmb3Igc2NoZWR1bGluZywgbmV2ZXIgZ2V0VHJ1ZVRpbWUoKS5cbiAgICAgICAgICAgIC8vIE91ciB1cGRhdGUgc2NoZWR1bGUgbXVzdCBiZSBiYXNlZCBvbiB0aGUgc3RhYmxlIHN5c3RlbSBjbG9jaywgbm90IHRoZVxuICAgICAgICAgICAgLy8gb2Zmc2V0LWFkanVzdGVkIHRpbWUgd2hpY2ggY2hhbmdlcyBhcyB3ZSBzeW5jaHJvbml6ZS5cbiAgICAgICAgICAgIGNvbnN0IGN1cnJlbnRUaW1lID0gYmFzZUdldFRpbWUoKTtcblxuICAgICAgICAgICAgbGV0IGNhY2hlZERhdGEgPSBlbnRyeT8uZGF0YTtcbiAgICAgICAgICAgIGNvbnN0IHJlYWRWZXJzaW9uID0gZW50cnk/LnZlcnNpb247XG5cbiAgICAgICAgICAgIGlmIChjYWNoZWREYXRhICYmIGN1cnJlbnRUaW1lID49IGNhY2hlZERhdGEubmV4dFVwZGF0ZVRpbWUpIHtcbiAgICAgICAgICAgICAgICAvLyBQYXN0IHRoZSBlbmQgLSByZXNldCBhbmQgcmVpbml0aWFsaXplXG4gICAgICAgICAgICAgICAgY2FjaGVkRGF0YSA9IHVuZGVmaW5lZDtcbiAgICAgICAgICAgICAgICBkZWJ1Z0xvZyhcIlBhc3QgbmV4dFVwZGF0ZVRpbWUsIHJlc2V0dGluZ1wiKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3QgbmVlZHNGZXRjaCA9ICFjYWNoZWREYXRhIHx8IGN1cnJlbnRUaW1lID49IGNhY2hlZERhdGEudXBkYXRlVGltZTtcbiAgICAgICAgICAgIGlmIChuZWVkc0ZldGNoICYmICFjYW5NZWFzdXJlT2Zmc2V0KCkpIHtcbiAgICAgICAgICAgICAgICAvLyBLZWVwIHVzaW5nIHdoYXRldmVyIG9mZnNldCB3ZSBoYXZlIChldmVuIGEgc3RhbGUgb25lIGlzIGJldHRlciB0aGFuIGEgbWVhc3VyZW1lbnQgc2tld2VkIGJ5IGJhY2tncm91bmQgdGhyb3R0bGluZykuIElmIHdlIGhhdmUgbm90aGluZywgcnVuIHVuc3luY2VkIHVudGlsIHdlIGJlY29tZSB2aXNpYmxlIG9yIGFub3RoZXIgdGFiIHdyaXRlcyB0byB0aGUgc3RvcmUuXG4gICAgICAgICAgICAgICAgY2FjaGVkVGltZU9mZnNldERhdGEgPSBlbnRyeT8uZGF0YSA/PyBjYWNoZWRUaW1lT2Zmc2V0RGF0YTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKCFjYWNoZWREYXRhIHx8ICFyZWFkVmVyc2lvbikge1xuICAgICAgICAgICAgICAgIC8vIEZpcnN0IHRpbWUgaW5pdGlhbGl6YXRpb24gLSB1c2UgY29uZGl0aW9uYWwgd3JpdGUgdG8gaGFuZGxlIHJhY2VcbiAgICAgICAgICAgICAgICBjb25zdCBtZWFzdXJlbWVudCA9IGF3YWl0IGZldGNoTmV3T2Zmc2V0KCk7XG4gICAgICAgICAgICAgICAgaWYgKCFtZWFzdXJlbWVudCkge1xuICAgICAgICAgICAgICAgICAgICAvLyBUb3RhbCBmYWlsdXJlIC0ga2VlcCBhbnkgc3RhbGUgZGF0YSByYXRoZXIgdGhhbiB3cml0aW5nIGEgZmFicmljYXRlZCBvZmZzZXQgdG8gdGhlIHNoYXJlZCBzdG9yZS4gVGhlIG5leHQgY2hlY2sgaW50ZXJ2YWwgcmV0cmllcy5cbiAgICAgICAgICAgICAgICAgICAgY2FjaGVkVGltZU9mZnNldERhdGEgPSBlbnRyeT8uZGF0YSA/PyBjYWNoZWRUaW1lT2Zmc2V0RGF0YTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIC8vIFJlLXJlYWQgdGhlIHRpbWUsIGFzIGZldGNoTmV3T2Zmc2V0IGNhbiB0YWtlIG1pbnV0ZXMgd2hlbiBpdCBoYXMgdG8gcmV0cnkuXG4gICAgICAgICAgICAgICAgY29uc3QgaW5pdFRpbWUgPSBiYXNlR2V0VGltZSgpO1xuICAgICAgICAgICAgICAgIGNvbnN0IG9mZnNldCA9IG1lYXN1cmVtZW50Lm9mZnNldDtcbiAgICAgICAgICAgICAgICBjb25zdCBpbml0RGF0YTogVGltZU9mZnNldERhdGEgPSB7XG4gICAgICAgICAgICAgICAgICAgIGxhc3RPZmZzZXQ6IG9mZnNldCxcbiAgICAgICAgICAgICAgICAgICAgbGFzdFVwZGF0ZVRpbWU6IGluaXRUaW1lLFxuICAgICAgICAgICAgICAgICAgICBvZmZzZXQ6IG9mZnNldCxcbiAgICAgICAgICAgICAgICAgICAgdXBkYXRlVGltZTogaW5pdFRpbWUgKyBVUERBVEVfVFJBTlNJVElPTl9HQVAsXG4gICAgICAgICAgICAgICAgICAgIG5leHRPZmZzZXQ6IG9mZnNldCxcbiAgICAgICAgICAgICAgICAgICAgbmV4dFVwZGF0ZVRpbWU6IGluaXRUaW1lICsgVVBEQVRFX1RSQU5TSVRJT05fR0FQICogMixcbiAgICAgICAgICAgICAgICAgICAgcHJvb2Y6IG1lYXN1cmVtZW50LnByb29mLFxuICAgICAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgICAgICBpZiAocmVhZFZlcnNpb24pIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3Qgc3VjY2VzcyA9IGF3YWl0IHN0b3JlLnB1dElmVmVyc2lvbihpbml0RGF0YSwgcmVhZFZlcnNpb24pO1xuICAgICAgICAgICAgICAgICAgICBpZiAoIXN1Y2Nlc3MpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGRlYnVnTG9nKFwiTG9zdCB0aGUgcmFjZSwgcmV0cnlpbmdcIik7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBMb3N0IHRoZSByYWNlLCByZXRyeVxuICAgICAgICAgICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgY2FjaGVkVGltZU9mZnNldERhdGEgPSBpbml0RGF0YTtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coXCJTdWNjZXNzZnVsbHkgd3JvdGUgYXRvbWljIHJlc2V0XCIpO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZiAoIWF3YWl0IHN0b3JlLnB1dEluaXRpYWwoaW5pdERhdGEpKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIExvc3QgdGhlIHJhY2UsIGFub3RoZXIgd3JpdGVyIHdyb3RlIGFmdGVyIHVzXG4gICAgICAgICAgICAgICAgICAgIC8vIFJldHJ5IGZyb20gdGhlIHRvcCB0byByZWFkIHRoZWlyIGRhdGFcbiAgICAgICAgICAgICAgICAgICAgZGVidWdMb2coXCJWYWx1ZSB3YXMgY2hhbmdlZCBieSBhbm90aGVyIHByb2Nlc3MsIHJldHJ5aW5nXCIpO1xuICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBXZSB3b24gdGhlIHJhY2UsIHVzZSBvdXIgZGF0YVxuICAgICAgICAgICAgICAgIGNhY2hlZFRpbWVPZmZzZXREYXRhID0gaW5pdERhdGE7XG4gICAgICAgICAgICAgICAgZGVidWdMb2coXCJJbml0aWFsaXplZCAtIHRpbWUgb2Zmc2V0OlwiLCBvZmZzZXQsIFwibXMsIG5leHQgdXBkYXRlIGluXCIsIFVQREFURV9UUkFOU0lUSU9OX0dBUCwgXCJtc1wiKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKGN1cnJlbnRUaW1lID49IGNhY2hlZERhdGEudXBkYXRlVGltZSkge1xuICAgICAgICAgICAgICAgIC8vIFRpbWUgdG8gcm90YXRlXG4gICAgICAgICAgICAgICAgY29uc3QgbmV3TWVhc3VyZW1lbnQgPSBhd2FpdCBmZXRjaE5ld09mZnNldCgpO1xuICAgICAgICAgICAgICAgIGlmICghbmV3TWVhc3VyZW1lbnQpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gVG90YWwgZmFpbHVyZSAtIGtlZXAgdGhlIGV4aXN0aW5nIGRhdGEgdW5yb3RhdGVkICh0aGUgb2Zmc2V0IGp1c3QgZmxhdHRlbnMgb3V0IGF0IG5leHRPZmZzZXQpLiBUaGUgbmV4dCBjaGVjayBpbnRlcnZhbCByZXRyaWVzIHRoZSByb3RhdGlvbi5cbiAgICAgICAgICAgICAgICAgICAgY2FjaGVkVGltZU9mZnNldERhdGEgPSBjYWNoZWREYXRhO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgY29uc3QgbmV3RGF0YTogVGltZU9mZnNldERhdGEgPSB7XG4gICAgICAgICAgICAgICAgICAgIGxhc3RPZmZzZXQ6IGNhY2hlZERhdGEub2Zmc2V0LFxuICAgICAgICAgICAgICAgICAgICBsYXN0VXBkYXRlVGltZTogY2FjaGVkRGF0YS51cGRhdGVUaW1lLFxuICAgICAgICAgICAgICAgICAgICBvZmZzZXQ6IGNhY2hlZERhdGEubmV4dE9mZnNldCxcbiAgICAgICAgICAgICAgICAgICAgdXBkYXRlVGltZTogY2FjaGVkRGF0YS5uZXh0VXBkYXRlVGltZSxcbiAgICAgICAgICAgICAgICAgICAgbmV4dE9mZnNldDogbmV3TWVhc3VyZW1lbnQub2Zmc2V0LFxuICAgICAgICAgICAgICAgICAgICBuZXh0VXBkYXRlVGltZTogY2FjaGVkRGF0YS5uZXh0VXBkYXRlVGltZSArIFVQREFURV9UUkFOU0lUSU9OX0dBUCxcbiAgICAgICAgICAgICAgICAgICAgcHJvb2Y6IG5ld01lYXN1cmVtZW50LnByb29mLFxuICAgICAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgICAgICBjb25zdCBzdWNjZXNzID0gYXdhaXQgc3RvcmUucHV0SWZWZXJzaW9uKG5ld0RhdGEsIHJlYWRWZXJzaW9uKTtcbiAgICAgICAgICAgICAgICBpZiAoIXN1Y2Nlc3MpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gTG9zdCB0aGUgcmFjZSwgcmV0cnlcbiAgICAgICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgY29uc3QgdGltZVVudGlsTmV4dCA9IG5ld0RhdGEubmV4dFVwZGF0ZVRpbWUgLSBiYXNlR2V0VGltZSgpO1xuICAgICAgICAgICAgICAgIGRlYnVnTG9nKFwiQWR2YW5jaW5nIHRpbWUgb2Zmc2V0IC0gY3VycmVudDpcIiwgbmV3RGF0YS5vZmZzZXQsIFwibXMsIG5leHQ6XCIsIG5ld0RhdGEubmV4dE9mZnNldCwgXCJtcywgbmV4dCB1cGRhdGUgaW5cIiwgdGltZVVudGlsTmV4dCwgXCJtc1wiKTtcbiAgICAgICAgICAgICAgICBjYWNoZWRUaW1lT2Zmc2V0RGF0YSA9IG5ld0RhdGE7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGNhY2hlZFRpbWVPZmZzZXREYXRhID0gY2FjaGVkRGF0YTtcbiAgICAgICAgICAgICAgICBjb25zdCB0aW1lVW50aWxOZXh0ID0gY2FjaGVkRGF0YS51cGRhdGVUaW1lIC0gYmFzZUdldFRpbWUoKTtcbiAgICAgICAgICAgICAgICBkZWJ1Z0xvZyhcIkxvYWRlZCBmcm9tIHN0b3JlIC0gY3VycmVudDpcIiwgY2FjaGVkRGF0YS5vZmZzZXQsIFwibXMsIG5leHQ6XCIsIGNhY2hlZERhdGEubmV4dE9mZnNldCwgXCJtcywgbmV4dCB1cGRhdGUgaW5cIiwgdGltZVVudGlsTmV4dCwgXCJtc1wiKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIG1hcmtGaXJzdFRpbWVTeW5jKCk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgICAgdXBkYXRpbmdPZmZzZXQgPSBmYWxzZTtcbiAgICB9XG59XG5cbmZ1bmN0aW9uIHRyaWdnZXJVcGRhdGVUaW1lT2Zmc2V0KCkge1xuICAgIHVwZGF0ZVRpbWVPZmZzZXQoKS5jYXRjaCgoZSkgPT4ge1xuICAgICAgICBjb25zb2xlLndhcm4oXCJFcnJvciB1cGRhdGluZyB0aW1lIG9mZnNldDpcIiwgZSk7XG4gICAgfSk7XG59XG5cbnNldEludGVydmFsKHRyaWdnZXJVcGRhdGVUaW1lT2Zmc2V0LCBVUERBVEVfQ0hFQ0tfSU5URVJWQUwpO1xuc2V0SW1tZWRpYXRlKCgpID0+IHtcbiAgICB1cGRhdGVUaW1lT2Zmc2V0KCkuY2F0Y2goKGUpID0+IHtcbiAgICAgICAgY29uc29sZS5lcnJvcihcIkVycm9yIHVwZGF0aW5nIGluaXRpYWwgb2Zmc2V0OlwiLCBlKTtcbiAgICB9KTtcbn0pO1xuXG5pZiAoIWlzTm9kZSgpICYmIHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIgJiYgdHlwZW9mIGRvY3VtZW50ICE9PSBcInVuZGVmaW5lZFwiKSB7XG4gICAgLy8gSWYgd2UgbG9hZGVkIGluIGEgaGlkZGVuIHRhYiB3ZSBza2lwIG1lYXN1cmluZywgc28gcmUtY2hlY2sgYXMgc29vbiBhcyB3ZSBiZWNvbWUgdmlzaWJsZS5cbiAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKFwidmlzaWJpbGl0eWNoYW5nZVwiLCB0cmlnZ2VyVXBkYXRlVGltZU9mZnNldCk7XG4gICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoXCJmb2N1c1wiLCB0cmlnZ2VyVXBkYXRlVGltZU9mZnNldCk7XG4gICAgLy8gUGljayB1cCBvZmZzZXRzIG90aGVyIHRhYnMgd3JpdGUsIHNvIG9ubHkgb25lIHRhYiBldmVyIGhhcyB0byBtZWFzdXJlLlxuICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKFwic3RvcmFnZVwiLCAoZSkgPT4ge1xuICAgICAgICBpZiAoZS5rZXkgPT09IFRJTUVfT0ZGU0VUX0xPQ0FMX1NUT1JBR0VfS0VZKSB7XG4gICAgICAgICAgICB0cmlnZ2VyVXBkYXRlVGltZU9mZnNldCgpO1xuICAgICAgICB9XG4gICAgfSk7XG59XG5cblxuY2xhc3MgVGltZUNvbnRyb2xsZXJCYXNlIHtcbiAgICBwdWJsaWMgYXN5bmMgZ2V0VHJ1ZVRpbWUoKSB7XG4gICAgICAgIGF3YWl0IHdhaXRGb3JGaXJzdFRpbWVTeW5jKCk7XG4gICAgICAgIHJldHVybiBnZXRUcnVlVGltZSgpO1xuICAgIH1cbn1cblxuY29uc3QgVGltZUNvbnRyb2xsZXIgPSBTb2NrZXRGdW5jdGlvbi5yZWdpc3RlcihcbiAgICBcIlRpbWVDb250cm9sbGVyLWRkZjQ3NTNlLWZjOGEtNDEzZi04Y2MyLWI5MjdkZDQ0OTk3NlwiLFxuICAgIG5ldyBUaW1lQ29udHJvbGxlckJhc2UoKSxcbiAgICAoKSA9PiAoe1xuICAgICAgICBnZXRUcnVlVGltZToge1xuICAgICAgICAgICAgLy8gTm8gaG9va3MsIGFzIHRoaXMgbmVlZHMgdG8gcnVuIHZlcnkgZWFybHkgb24uIEFsc28sIGl0IGlzIGJhc2ljYWxseSBqdXN0IGEgcGluZyxcbiAgICAgICAgICAgIC8vICBzbyBpdCBzaG91bGQgYmUgc2FmZSBmb3IgYW55b25lIHRvIHVzZSAod2UgbWlnaHQgZXZlbiBtYWtlIGl0IGp1c3QgYSByZWd1bGFyIEhUVFBTIGVuZHBvaW50LFxuICAgICAgICAgICAgLy8gIG9yIGV2ZW4ganVzdCBzZXQgdXAgYSBkZWRpY2F0ZWQgZG9tYWluIGZvciB0aGlzKS5cbiAgICAgICAgICAgIG5vRGVmYXVsdEhvb2tzOiB0cnVlLFxuICAgICAgICAgICAgbm9DbGllbnRIb29rczogdHJ1ZSxcbiAgICAgICAgfSxcbiAgICB9KSxcbiAgICAoKSA9PiAoe30pLFxuICAgIHtcbiAgICAgICAgLy8gTk9URTogQXV0b2V4cG9zZSwgYmVjYXVzZSBvdXIgZXhwb3NlZCBlbmRwb2ludHMgYXJlIGluY3JlZGlibHkgbGlnaHR3ZWlnaHRcbiAgICAgICAgLy8gIChqdXN0IGEgcGluZyksIGFuZCBkb24ndCBleHBvc2UgcmVhbGx5IGV4cG9zZSBhbnkgZGF0YS5cbiAgICAgICAgLy8gbm9BdXRvRXhwb3NlOiB0cnVlXG4gICAgfVxuKTtcbiJdfQ==
707
- /* _JS_SOURCE_HASH = "f3fc7513e6579ca962c1f14ae7c8bfc7532715030dc583b23c82e0fda18c71dd"; */