@dollhousemcp/mcp-server 2.0.10 → 2.0.11

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 (36) hide show
  1. package/dist/auto-dollhouse/portDiscovery.d.ts +23 -0
  2. package/dist/auto-dollhouse/portDiscovery.d.ts.map +1 -0
  3. package/dist/auto-dollhouse/portDiscovery.js +77 -0
  4. package/dist/cli/console-token.d.ts +18 -0
  5. package/dist/cli/console-token.d.ts.map +1 -0
  6. package/dist/cli/console-token.js +187 -0
  7. package/dist/generated/version.d.ts +2 -2
  8. package/dist/generated/version.js +3 -3
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +24 -5
  11. package/dist/web/console/consoleToken.d.ts +403 -0
  12. package/dist/web/console/consoleToken.d.ts.map +1 -0
  13. package/dist/web/console/consoleToken.js +930 -0
  14. package/dist/web/middleware/authMiddleware.d.ts +64 -0
  15. package/dist/web/middleware/authMiddleware.d.ts.map +1 -0
  16. package/dist/web/middleware/authMiddleware.js +174 -0
  17. package/dist/web/routes/consoleRouteHelpers.d.ts +33 -0
  18. package/dist/web/routes/consoleRouteHelpers.d.ts.map +1 -0
  19. package/dist/web/routes/consoleRouteHelpers.js +60 -0
  20. package/dist/web/routes/tokenRoutes.d.ts +37 -0
  21. package/dist/web/routes/tokenRoutes.d.ts.map +1 -0
  22. package/dist/web/routes/tokenRoutes.js +95 -0
  23. package/dist/web/routes/totpRoutes.d.ts +45 -0
  24. package/dist/web/routes/totpRoutes.d.ts.map +1 -0
  25. package/dist/web/routes/totpRoutes.js +187 -0
  26. package/package.json +1 -1
  27. package/server.json +2 -2
  28. package/dist/constants/version.d.ts +0 -3
  29. package/dist/constants/version.d.ts.map +0 -1
  30. package/dist/constants/version.js +0 -4
  31. package/dist/logging/sinks/SSELogSink.d.ts +0 -35
  32. package/dist/logging/sinks/SSELogSink.d.ts.map +0 -1
  33. package/dist/logging/sinks/SSELogSink.js +0 -181
  34. package/dist/logging/viewer/viewerHtml.d.ts +0 -8
  35. package/dist/logging/viewer/viewerHtml.d.ts.map +0 -1
  36. package/dist/logging/viewer/viewerHtml.js +0 -204
@@ -0,0 +1,930 @@
1
+ /**
2
+ * Console session token storage and verification (#1780).
3
+ *
4
+ * Manages the file at `~/.dollhouse/run/console-token.auth.json` which
5
+ * holds the Bearer tokens that authenticate requests to the web console.
6
+ * The file is created on first leader election and persists across
7
+ * restarts — tokens only rotate when explicitly requested.
8
+ *
9
+ * The `.auth` filename suffix keeps this state isolated from any legacy
10
+ * no-authentication DollhouseMCP installation running on the same
11
+ * machine. The port, lock file, and token file all share the same
12
+ * isolation strategy — see `src/config/env.ts` for the port, and
13
+ * `LeaderElection.ts` for the lock file.
14
+ *
15
+ * Schema is forward-compatible with multi-device, multi-tenant, and
16
+ * scope-restricted tokens for Phase 2+. Phase 1 uses a single "console"
17
+ * kind token and stubs the scope/boundary checks.
18
+ *
19
+ * File format (version 1):
20
+ * ```json
21
+ * {
22
+ * "version": 1,
23
+ * "tokens": [
24
+ * {
25
+ * "id": "018e1a2b-...",
26
+ * "name": "Kermit on mick-MacBook-Pro",
27
+ * "kind": "console",
28
+ * "token": "<64-hex>",
29
+ * "scopes": ["admin"],
30
+ * "elementBoundaries": null,
31
+ * "tenant": null,
32
+ * "platform": "local",
33
+ * "labels": {},
34
+ * "createdAt": "2026-04-04T20:00:00.000Z",
35
+ * "lastUsedAt": null,
36
+ * "createdVia": "initial-setup"
37
+ * }
38
+ * ],
39
+ * "totp": { "enrolled": false, "secret": null, "backupCodes": [] }
40
+ * }
41
+ * ```
42
+ *
43
+ * @since v2.1.0 — Issue #1780
44
+ */
45
+ import { homedir, hostname, platform } from 'node:os';
46
+ import { join } from 'node:path';
47
+ import { mkdir, readFile, rename, writeFile, chmod, unlink, copyFile } from 'node:fs/promises';
48
+ import { createHash, randomBytes, randomUUID, timingSafeEqual } from 'node:crypto';
49
+ import { Secret, TOTP } from 'otpauth';
50
+ import { UnicodeValidator } from '../../security/validators/unicodeValidator.js';
51
+ import { SecurityMonitor } from '../../security/securityMonitor.js';
52
+ import { logger } from '../../utils/logger.js';
53
+ /** Directory for runtime state files — same as LeaderElection. */
54
+ const RUN_DIR = join(homedir(), '.dollhouse', 'run');
55
+ /**
56
+ * Default path to the authenticated console's token file.
57
+ *
58
+ * The `.auth` suffix isolates this from any legacy no-authentication
59
+ * DollhouseMCP installation that may also be running on the same
60
+ * machine. Legacy installs did not write a token file at all; the
61
+ * authenticated console writes `console-token.auth.json`. Combined with
62
+ * the port and lock-file separation, this gives the two generations of
63
+ * the console fully independent state trees under `~/.dollhouse/run/`.
64
+ *
65
+ * Callers can override via `DOLLHOUSE_CONSOLE_TOKEN_FILE` in the env.
66
+ */
67
+ const DEFAULT_TOKEN_FILE = join(RUN_DIR, 'console-token.auth.json');
68
+ /** Current token file schema version. */
69
+ const TOKEN_FILE_VERSION = 1;
70
+ /** Token length in bytes (produces a 64-character hex string). */
71
+ const TOKEN_BYTES = 32;
72
+ /** File mode for the token file — owner read/write only. */
73
+ const TOKEN_FILE_MODE = 0o600;
74
+ /**
75
+ * TOTP configuration — RFC 6238 defaults. These are compiled-in so the
76
+ * otpauth URI is deterministic and compatible with every common authenticator
77
+ * app (Google Authenticator, 1Password, Authy, Bitwarden, etc.).
78
+ */
79
+ const TOTP_ISSUER = 'DollhouseMCP';
80
+ const TOTP_ALGORITHM = 'SHA1';
81
+ const TOTP_DIGITS = 6;
82
+ const TOTP_PERIOD_SECONDS = 30;
83
+ const TOTP_SECRET_SIZE_BYTES = 20; // 160 bits — the RFC 6238 recommendation
84
+ /**
85
+ * ±2 time step tolerance (±60s drift) → 150 second total validity window.
86
+ *
87
+ * The naive choice would be `window: 1` (±30s, 90s total) which is the RFC
88
+ * 6238 default and matches most web login flows where the user types a code
89
+ * they just generated, with sub-second latency to the server. That doesn't
90
+ * cover our real deployment profile:
91
+ *
92
+ * 1. **Async bridge flows** — DollhouseBridge over Zulip (or similar chat
93
+ * transports) can add 10-60 seconds of latency between the user typing
94
+ * the code and the MCP server verifying it. A code typed with a few
95
+ * seconds of lifetime remaining can slide out of the validation window
96
+ * before reaching the server.
97
+ *
98
+ * 2. **Clock drift** — containers, VMs, and phones with lazy NTP can
99
+ * disagree with the server by tens of seconds. ±30s gives zero margin
100
+ * against any drift at all.
101
+ *
102
+ * `window: 2` gives 5 valid codes out of 10^6 per attempt (vs. 3 with
103
+ * window=1). Combined with the 10/min rate limiter and the always-on auth
104
+ * gate, brute-force success probability remains below 5e-5 per minute —
105
+ * well within the security envelope. `window: 3` (±90s, 210s total) is
106
+ * defensible but reserved for future deployment pressure; easier to widen
107
+ * later than to narrow later.
108
+ *
109
+ * Note: the window is anchored to the **server's verification clock**, not
110
+ * the user's code-generation clock. When a user complains that a code they
111
+ * "just typed" was rejected, the error message nudges them to submit codes
112
+ * with plenty of step-lifetime remaining.
113
+ */
114
+ const TOTP_VALIDATE_WINDOW = 2;
115
+ /**
116
+ * Error message for a failed TOTP code check. Includes a hint about the
117
+ * common failure mode (code aged out in transit) so users on high-latency
118
+ * channels know what to try differently on retry.
119
+ */
120
+ const INVALID_TOTP_MESSAGE = 'Invalid TOTP code. When copying from an authenticator app, use a code ' +
121
+ 'with plenty of lifetime remaining — codes can age out in transit on ' +
122
+ 'slower channels (e.g. chat bridges). Wait for a fresh code if unsure.';
123
+ /** Pending enrollments expire this long after begin() to limit in-memory secret lifetime. */
124
+ const TOTP_PENDING_TTL_MS = 10 * 60 * 1000; // 10 minutes
125
+ /**
126
+ * Hard cap on concurrent pending enrollments. An authenticated caller who
127
+ * misbehaves could otherwise fill the in-memory Map with 10-minute-TTL
128
+ * secrets. Realistic users need at most 1 pending enrollment at a time;
129
+ * 10 is generous headroom for retry-during-enrollment scenarios.
130
+ *
131
+ * When the cap is hit, `beginTotpEnrollment` throws `TOO_MANY_PENDING`
132
+ * (mapped to HTTP 429). Rejecting loudly is preferred over silently
133
+ * evicting someone else's legitimate pending entry.
134
+ */
135
+ const MAX_PENDING_ENROLLMENTS = 10;
136
+ /**
137
+ * Grace window for old tokens after rotation — 15 seconds.
138
+ *
139
+ * After a rotation, the previous token value stays valid for this period to
140
+ * cover in-flight requests from the rotating tab and any other local processes
141
+ * that read the token file but haven't picked up the new value yet. All
142
+ * consumers are loopback-only (127.0.0.1), so network latency is negligible
143
+ * and 15s is generous.
144
+ *
145
+ * Grace entries are per-rotation and in-memory only — they are never persisted
146
+ * to disk. A leader crash kills all grace entries, which is the safe failure
147
+ * mode (callers re-auth with the new token from the file).
148
+ */
149
+ const ROTATION_GRACE_MS = 15_000;
150
+ /** Number of backup codes generated on enrollment. */
151
+ const BACKUP_CODE_COUNT = 10;
152
+ /** Characters per backup code — Crockford base32-ish, no ambiguous chars. */
153
+ const BACKUP_CODE_LENGTH = 8;
154
+ /**
155
+ * Backup code alphabet — Crockford base32 (32 chars, excludes I/L/O/U).
156
+ * 5 bits per char × 8 chars = 40 bits per code, plenty for one-shot use.
157
+ */
158
+ const BACKUP_CODE_ALPHABET = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
159
+ /**
160
+ * Strict format for console tokens — 64 lowercase hex characters.
161
+ * Used as a defense-in-depth check in verify(): even if the caller forgot
162
+ * to sanitize the presented value, we reject anything that isn't a legitimate
163
+ * 256-bit hex token before reaching the constant-time comparison.
164
+ * DMCP-SEC-004 mitigation.
165
+ */
166
+ const TOKEN_FORMAT = /^[0-9a-f]{64}$/;
167
+ /**
168
+ * Typed error for TOTP store operations. Carries a machine-readable
169
+ * `code` field so the HTTP layer can map failures to consistent response
170
+ * codes without parsing free-form error messages. Callers (CLI, UI) can
171
+ * branch on `code` instead of the human-readable `message`.
172
+ */
173
+ export class TotpError extends Error {
174
+ code;
175
+ constructor(message, code) {
176
+ super(message);
177
+ this.code = code;
178
+ this.name = 'TotpError';
179
+ }
180
+ }
181
+ /**
182
+ * Generate a cryptographically random token.
183
+ * Returns 64 hex characters (256 bits of entropy).
184
+ */
185
+ function generateTokenValue() {
186
+ return randomBytes(TOKEN_BYTES).toString('hex');
187
+ }
188
+ /**
189
+ * Mask a token for display — shows first 8 chars only.
190
+ */
191
+ function maskToken(token) {
192
+ if (token.length <= 8)
193
+ return '••••••••';
194
+ return `${token.slice(0, 8)}${'•'.repeat(Math.min(56, token.length - 8))}`;
195
+ }
196
+ /**
197
+ * Build a human-readable default name from a puppet name and the machine hostname.
198
+ * Example: "Kermit on mick-MacBook-Pro".
199
+ *
200
+ * The puppet name is passed in rather than imported to avoid a circular dependency
201
+ * with SessionNames (which only generates per-process names).
202
+ */
203
+ function defaultTokenName(puppetName) {
204
+ const host = hostname() || 'localhost';
205
+ return `${puppetName} on ${host}`;
206
+ }
207
+ /**
208
+ * Generate a batch of random backup codes. Each code is `BACKUP_CODE_LENGTH`
209
+ * characters drawn uniformly from `BACKUP_CODE_ALPHABET` (Crockford base32,
210
+ * 32 characters, 5 bits per char). Uses rejection sampling against 256-bit
211
+ * random bytes to avoid modulo bias.
212
+ */
213
+ function generateBackupCodes() {
214
+ // 32-char alphabet divides 256 evenly (256 / 32 = 8), so the simple
215
+ // mod-32 mapping has no bias. Generate one byte per character.
216
+ const codes = [];
217
+ for (let i = 0; i < BACKUP_CODE_COUNT; i++) {
218
+ const bytes = randomBytes(BACKUP_CODE_LENGTH);
219
+ let code = '';
220
+ for (let j = 0; j < BACKUP_CODE_LENGTH; j++) {
221
+ code += BACKUP_CODE_ALPHABET[bytes[j] & 0x1f];
222
+ }
223
+ codes.push(code);
224
+ }
225
+ return codes;
226
+ }
227
+ /**
228
+ * Hash a backup code for storage. sha256 hex is plenty — these codes are
229
+ * high-entropy (40 bits) and we only need to detect a tamper, not resist
230
+ * password-cracking on a leaked hash.
231
+ */
232
+ function hashBackupCode(code) {
233
+ return createHash('sha256').update(code, 'utf8').digest('hex');
234
+ }
235
+ /**
236
+ * Normalize a user-entered backup code before hashing: uppercase, strip
237
+ * whitespace, strip dashes (users often type codes in groups like
238
+ * "XXXX-XXXX"). Returns the canonical form that matches what we stored.
239
+ */
240
+ function normalizeBackupCode(raw) {
241
+ return raw.replaceAll(/[\s-]/g, '').toUpperCase();
242
+ }
243
+ /**
244
+ * Build the full otpauth:// URI for a given secret and display label.
245
+ * The label is URI-encoded by `otpauth` internally via URIComponent.
246
+ */
247
+ function buildTotpUri(secret, label) {
248
+ const totp = new TOTP({
249
+ issuer: TOTP_ISSUER,
250
+ label,
251
+ algorithm: TOTP_ALGORITHM,
252
+ digits: TOTP_DIGITS,
253
+ period: TOTP_PERIOD_SECONDS,
254
+ secret,
255
+ });
256
+ return totp.toString();
257
+ }
258
+ /**
259
+ * Validate a single token entry object. Returns true if the entry has all
260
+ * required fields with the correct types. Extracted from validateTokenFile
261
+ * to keep the top-level validator's cognitive complexity manageable.
262
+ */
263
+ function isValidTokenEntry(raw) {
264
+ if (!raw || typeof raw !== 'object')
265
+ return false;
266
+ const e = raw;
267
+ return (typeof e.id === 'string' && e.id.length > 0 &&
268
+ typeof e.name === 'string' &&
269
+ typeof e.token === 'string' && e.token.length > 0 &&
270
+ typeof e.kind === 'string' &&
271
+ Array.isArray(e.scopes) &&
272
+ typeof e.createdAt === 'string');
273
+ }
274
+ /**
275
+ * Validate that a parsed JSON object conforms to the expected token file schema.
276
+ * Returns a typed ConsoleTokenFile or null if invalid.
277
+ *
278
+ * Strict validation — an unrecognized version or missing required fields
279
+ * causes the file to be treated as corrupt so a fresh one can be written.
280
+ */
281
+ function validateTokenFile(raw) {
282
+ if (!raw || typeof raw !== 'object')
283
+ return null;
284
+ const obj = raw;
285
+ if (obj.version !== TOKEN_FILE_VERSION)
286
+ return null;
287
+ if (!Array.isArray(obj.tokens))
288
+ return null;
289
+ if (!obj.totp || typeof obj.totp !== 'object')
290
+ return null;
291
+ if (!obj.tokens.every(isValidTokenEntry))
292
+ return null;
293
+ return raw;
294
+ }
295
+ /**
296
+ * Stateful store that owns the console token file and verifies presented tokens.
297
+ *
298
+ * Designed to live on the leader process. Followers should not construct this —
299
+ * they read the file directly via `readTokenFileRaw()` for their own HTTP calls.
300
+ */
301
+ export class ConsoleTokenStore {
302
+ filePath;
303
+ data = null;
304
+ /**
305
+ * Pre-converted Buffer cache keyed by entry id. Populated whenever `this.data`
306
+ * is assigned (load, create, future rotation). Verify() reuses the stored
307
+ * buffers so the hot path doesn't re-allocate per-token on every request.
308
+ * Negligible win with 1 token today; meaningful with Phase 2 multi-token
309
+ * lookups. Not serialized — buffers are never written to disk.
310
+ */
311
+ tokenBuffers = new Map();
312
+ /**
313
+ * In-memory pending TOTP enrollments, keyed by opaque pendingId. Nothing
314
+ * lives on disk until confirmTotpEnrollment() succeeds, which limits the
315
+ * window in which a half-completed enrollment leaks a secret via file read.
316
+ * Entries expire after TOTP_PENDING_TTL_MS (#1794).
317
+ */
318
+ pendingEnrollments = new Map();
319
+ /**
320
+ * In-memory grace buffer for recently-rotated tokens (#1795). After a
321
+ * rotation, the old token value is stashed here with a per-rotation expiry
322
+ * so in-flight requests that were sent before the rotation response arrived
323
+ * still authenticate. Entries are per-rotation (concurrent rotations each
324
+ * get their own grace slot) and never persisted to disk.
325
+ */
326
+ graceEntries = [];
327
+ constructor(filePath = DEFAULT_TOKEN_FILE) {
328
+ this.filePath = filePath;
329
+ }
330
+ /**
331
+ * Rebuild the token buffer cache after a data load, create, or mutation.
332
+ * Keeps the hot verify() path allocation-free for the stored side.
333
+ */
334
+ rebuildTokenBuffers() {
335
+ this.tokenBuffers.clear();
336
+ if (!this.data)
337
+ return;
338
+ for (const entry of this.data.tokens) {
339
+ this.tokenBuffers.set(entry.id, Buffer.from(entry.token, 'utf8'));
340
+ }
341
+ }
342
+ /**
343
+ * Read the existing token file, or create a new one with a single initial
344
+ * token if none exists. Idempotent — safe to call on every leader election.
345
+ *
346
+ * @param puppetName - A puppet name picked by the caller (e.g. from SessionNames)
347
+ * used to build the default display name on first run.
348
+ * @returns The primary (first) token entry — convenient for server startup
349
+ * to inject into HTML and stamp on followers.
350
+ */
351
+ async ensureInitialized(puppetName) {
352
+ const readResult = await this.readWithStatus();
353
+ if (readResult.status === 'ok' && readResult.data.tokens.length > 0) {
354
+ this.data = readResult.data;
355
+ this.rebuildTokenBuffers();
356
+ logger.debug('[ConsoleToken] Loaded existing token file', {
357
+ path: this.filePath,
358
+ count: readResult.data.tokens.length,
359
+ });
360
+ return readResult.data.tokens[0];
361
+ }
362
+ // If the file existed but was corrupt, back it up before overwriting.
363
+ // Users may have hand-edited the file with custom names/labels — don't
364
+ // destroy their data silently. A timestamped copy lets them recover.
365
+ if (readResult.status === 'corrupt') {
366
+ await this.backupCorruptFile();
367
+ }
368
+ // Create a fresh file with one initial token
369
+ const now = new Date().toISOString();
370
+ const initial = {
371
+ id: randomUUID(),
372
+ name: defaultTokenName(puppetName),
373
+ kind: 'console',
374
+ token: generateTokenValue(),
375
+ scopes: ['admin'],
376
+ elementBoundaries: null,
377
+ tenant: null,
378
+ platform: 'local',
379
+ labels: {},
380
+ createdAt: now,
381
+ lastUsedAt: null,
382
+ createdVia: 'initial-setup',
383
+ };
384
+ const file = {
385
+ version: TOKEN_FILE_VERSION,
386
+ tokens: [initial],
387
+ totp: { enrolled: false, secret: null, backupCodes: [], enrolledAt: null },
388
+ };
389
+ await this.write(file);
390
+ this.data = file;
391
+ this.rebuildTokenBuffers();
392
+ logger.info('[ConsoleToken] Created new token file', {
393
+ path: this.filePath,
394
+ id: initial.id,
395
+ name: initial.name,
396
+ });
397
+ return initial;
398
+ }
399
+ /**
400
+ * Verify a presented Bearer token against the stored entries.
401
+ * Uses timing-safe comparison to prevent side-channel attacks.
402
+ *
403
+ * Updates `lastUsedAt` on the matched entry (in memory only; disk write
404
+ * is debounced to avoid disk thrash on every request — Phase 2 feature).
405
+ *
406
+ * @returns The matching entry, or null if no match.
407
+ */
408
+ verify(presented) {
409
+ if (!this.data || !presented)
410
+ return null;
411
+ // DMCP-SEC-004: Normalize the presented token to NFC and validate the
412
+ // strict hex format before any comparison. This blocks Unicode abuse
413
+ // (homographs, zero-width, bidi overrides) from reaching timingSafeEqual.
414
+ // Defense-in-depth — the middleware already sanitizes, but verify() is a
415
+ // public API that any future caller could invoke directly.
416
+ const normalized = UnicodeValidator.normalize(presented).normalizedContent;
417
+ if (!TOKEN_FORMAT.test(normalized))
418
+ return null;
419
+ // Only the presented side is allocated per-request; stored buffers are
420
+ // pre-converted in the tokenBuffers cache so the hot loop is allocation-free.
421
+ const presentedBuf = Buffer.from(normalized, 'utf8');
422
+ for (const entry of this.data.tokens) {
423
+ const storedBuf = this.tokenBuffers.get(entry.id);
424
+ if (!storedBuf || storedBuf.length !== presentedBuf.length)
425
+ continue;
426
+ if (timingSafeEqual(presentedBuf, storedBuf)) {
427
+ entry.lastUsedAt = new Date().toISOString();
428
+ return entry;
429
+ }
430
+ }
431
+ // Check grace buffer — recently-rotated tokens that haven't expired yet.
432
+ // Returns the primary entry on match so the caller gets the same admin
433
+ // shape; the grace window just extends the authentication period.
434
+ this.sweepExpiredGraceEntries();
435
+ for (const grace of this.graceEntries) {
436
+ if (grace.buf.length === presentedBuf.length && timingSafeEqual(presentedBuf, grace.buf)) {
437
+ // Touch the primary entry — the request is still "using" this token slot.
438
+ if (this.data.tokens.length > 0) {
439
+ this.data.tokens[0].lastUsedAt = new Date().toISOString();
440
+ }
441
+ return this.data.tokens[0] ?? null;
442
+ }
443
+ }
444
+ return null;
445
+ }
446
+ /**
447
+ * Get the primary token value for injection into HTML or forwarder config.
448
+ * Returns the first entry's token string, or null if uninitialized.
449
+ */
450
+ getPrimaryTokenValue() {
451
+ if (!this.data || this.data.tokens.length === 0)
452
+ return null;
453
+ return this.data.tokens[0].token;
454
+ }
455
+ /**
456
+ * Return all tokens with the secret value masked — safe to serialize for
457
+ * the Security tab UI or `GET /api/console/token/info` responses.
458
+ */
459
+ listMasked() {
460
+ if (!this.data)
461
+ return [];
462
+ return this.data.tokens.map(({ token, ...rest }) => ({
463
+ ...rest,
464
+ tokenPreview: maskToken(token),
465
+ }));
466
+ }
467
+ /**
468
+ * Get the path to the token file on disk.
469
+ */
470
+ getFilePath() {
471
+ return this.filePath;
472
+ }
473
+ // --------------------------------------------------------------------
474
+ // TOTP — Phase 2 (#1794)
475
+ // --------------------------------------------------------------------
476
+ /**
477
+ * Returns a safe-to-serialize view of TOTP enrollment state. Never leaks
478
+ * the secret or any backup code material.
479
+ */
480
+ getTotpStatus() {
481
+ const totp = this.data?.totp;
482
+ if (!totp?.enrolled) {
483
+ return { enrolled: false, enrolledAt: null, backupCodesRemaining: 0 };
484
+ }
485
+ return {
486
+ enrolled: true,
487
+ enrolledAt: totp.enrolledAt ?? null,
488
+ backupCodesRemaining: totp.backupCodes.length,
489
+ };
490
+ }
491
+ /** Convenience: true if the user has a confirmed TOTP secret. */
492
+ isTotpEnrolled() {
493
+ return Boolean(this.data?.totp?.enrolled && this.data.totp.secret);
494
+ }
495
+ /**
496
+ * Begin a TOTP enrollment. Generates a fresh secret, holds it in the
497
+ * in-memory pending map, and returns the data the UI needs to render a
498
+ * QR code and manual-entry fallback. Nothing is persisted until
499
+ * `confirmTotpEnrollment` succeeds.
500
+ *
501
+ * Callers may call this multiple times; each call produces a new pendingId
502
+ * and secret. Old pending entries expire after TOTP_PENDING_TTL_MS.
503
+ *
504
+ * @throws Error if TOTP is already enrolled — callers must disable first.
505
+ */
506
+ beginTotpEnrollment(label) {
507
+ if (this.isTotpEnrolled()) {
508
+ throw new TotpError('TOTP is already enrolled — disable existing enrollment before enrolling again', 'ALREADY_ENROLLED');
509
+ }
510
+ this.sweepExpiredEnrollments();
511
+ // Reject if we're already at the per-process cap on concurrent pendings.
512
+ // Sweep ran above, so any slot still held is a live, unexpired enrollment
513
+ // belonging to somebody else — evicting it could silently kill their flow.
514
+ if (this.pendingEnrollments.size >= MAX_PENDING_ENROLLMENTS) {
515
+ throw new TotpError(`Too many pending enrollments (max ${MAX_PENDING_ENROLLMENTS}); wait for existing pendings to expire or be confirmed`, 'TOO_MANY_PENDING');
516
+ }
517
+ // Derive a display label from the primary token name if the caller
518
+ // didn't provide one. Authenticator apps show "<Issuer>:<label>", so
519
+ // including the token name gives multi-device users a way to tell
520
+ // enrollments apart.
521
+ const displayLabel = label
522
+ ?? this.data?.tokens[0]?.name
523
+ ?? 'console';
524
+ const secret = new Secret({ size: TOTP_SECRET_SIZE_BYTES });
525
+ const pendingId = randomUUID();
526
+ const expiresAt = Date.now() + TOTP_PENDING_TTL_MS;
527
+ this.pendingEnrollments.set(pendingId, {
528
+ secret: secret.base32,
529
+ label: displayLabel,
530
+ expiresAt,
531
+ });
532
+ logger.debug('[ConsoleToken] TOTP enrollment begun', { pendingId, label: displayLabel });
533
+ return {
534
+ pendingId,
535
+ secret: secret.base32,
536
+ otpauthUri: buildTotpUri(secret, displayLabel),
537
+ expiresAt,
538
+ };
539
+ }
540
+ /**
541
+ * Confirm a pending TOTP enrollment. Verifies the code against the pending
542
+ * secret; on success, generates 10 plaintext backup codes, hashes them for
543
+ * storage, and persists the enrollment. Returns the plaintext backup codes
544
+ * exactly once — the caller is responsible for showing them to the user
545
+ * and then discarding them.
546
+ *
547
+ * Wrong codes do NOT consume or invalidate the pending enrollment — the
548
+ * user can retry until it expires. This matches user expectations for
549
+ * "oops, typed the wrong code" and limits the damage from a fat-fingered
550
+ * first attempt.
551
+ *
552
+ * @throws Error if pendingId is unknown, expired, or code invalid.
553
+ */
554
+ async confirmTotpEnrollment(pendingId, code) {
555
+ if (!this.data) {
556
+ throw new TotpError('Token store not initialized', 'STORE_NOT_INITIALIZED');
557
+ }
558
+ this.sweepExpiredEnrollments();
559
+ const pending = this.pendingEnrollments.get(pendingId);
560
+ if (!pending) {
561
+ throw new TotpError('Pending enrollment not found or expired', 'PENDING_NOT_FOUND');
562
+ }
563
+ // Verify the presented code against the pending secret. `validate` returns
564
+ // the time-step delta (a number, possibly 0) on match, or null on mismatch.
565
+ const totp = new TOTP({
566
+ issuer: TOTP_ISSUER,
567
+ label: pending.label,
568
+ algorithm: TOTP_ALGORITHM,
569
+ digits: TOTP_DIGITS,
570
+ period: TOTP_PERIOD_SECONDS,
571
+ secret: Secret.fromBase32(pending.secret),
572
+ });
573
+ const sanitized = code.replaceAll(/\s/g, '');
574
+ const delta = totp.validate({ token: sanitized, window: TOTP_VALIDATE_WINDOW });
575
+ if (delta === null) {
576
+ SecurityMonitor.logSecurityEvent({
577
+ type: 'TOTP_VERIFICATION_FAILED',
578
+ severity: 'MEDIUM',
579
+ source: 'ConsoleTokenStore.confirmTotpEnrollment',
580
+ details: 'Pending TOTP enrollment failed code verification',
581
+ });
582
+ throw new TotpError(INVALID_TOTP_MESSAGE, 'INVALID_TOTP_CODE');
583
+ }
584
+ // Code is valid — commit enrollment.
585
+ const plaintextCodes = generateBackupCodes();
586
+ const hashedCodes = plaintextCodes.map(hashBackupCode);
587
+ const enrolledAt = new Date().toISOString();
588
+ this.data.totp = {
589
+ enrolled: true,
590
+ secret: pending.secret,
591
+ backupCodes: hashedCodes,
592
+ enrolledAt,
593
+ };
594
+ await this.write(this.data);
595
+ this.pendingEnrollments.delete(pendingId);
596
+ logger.info('[ConsoleToken] TOTP enrollment confirmed', {
597
+ enrolledAt,
598
+ backupCodes: hashedCodes.length,
599
+ });
600
+ SecurityMonitor.logSecurityEvent({
601
+ type: 'TOTP_ENROLLED',
602
+ severity: 'MEDIUM',
603
+ source: 'ConsoleTokenStore.confirmTotpEnrollment',
604
+ details: 'Console TOTP second factor enrolled',
605
+ additionalData: { enrolledAt, backupCodes: hashedCodes.length },
606
+ });
607
+ return { backupCodes: plaintextCodes, enrolledAt };
608
+ }
609
+ /**
610
+ * Verify a user-presented code. Accepts either a live TOTP code or a
611
+ * single-use backup code. On successful backup-code match, the consumed
612
+ * code's hash is removed from storage and the file is re-written.
613
+ *
614
+ * Returns a discriminated result so the caller can distinguish "consumed
615
+ * a backup code" (which the UI should surface with a warning about
616
+ * remaining count) from "valid TOTP code" (normal case). Returns
617
+ * `{ ok: false }` on any failure — the caller should not retry within
618
+ * the same request lifecycle.
619
+ */
620
+ async verifyTotp(code) {
621
+ if (!this.data?.totp?.enrolled || !this.data.totp.secret) {
622
+ return { ok: false };
623
+ }
624
+ const sanitized = code.replaceAll(/\s/g, '');
625
+ if (!sanitized)
626
+ return { ok: false };
627
+ // Try live TOTP first — fast path, no disk write.
628
+ const totp = new TOTP({
629
+ issuer: TOTP_ISSUER,
630
+ label: this.data.tokens[0]?.name ?? 'console',
631
+ algorithm: TOTP_ALGORITHM,
632
+ digits: TOTP_DIGITS,
633
+ period: TOTP_PERIOD_SECONDS,
634
+ secret: Secret.fromBase32(this.data.totp.secret),
635
+ });
636
+ if (totp.validate({ token: sanitized, window: TOTP_VALIDATE_WINDOW }) !== null) {
637
+ return { ok: true, method: 'totp', backupCodesRemaining: this.data.totp.backupCodes.length };
638
+ }
639
+ // Fall back to backup code — normalize, hash, constant-time search.
640
+ const normalizedInput = normalizeBackupCode(sanitized);
641
+ const inputHash = hashBackupCode(normalizedInput);
642
+ const inputHashBuf = Buffer.from(inputHash, 'hex');
643
+ let matchIndex = -1;
644
+ for (let i = 0; i < this.data.totp.backupCodes.length; i++) {
645
+ const storedBuf = Buffer.from(this.data.totp.backupCodes[i], 'hex');
646
+ if (storedBuf.length === inputHashBuf.length && timingSafeEqual(inputHashBuf, storedBuf)) {
647
+ matchIndex = i;
648
+ break;
649
+ }
650
+ }
651
+ if (matchIndex === -1) {
652
+ // Both TOTP and backup code paths exhausted without a match. Emit a
653
+ // single audit event so the security monitor can alert on aggregate
654
+ // failure rates (SecurityMonitor dedups the same type+source within
655
+ // a 60s window, so rapid-fire attacks are collapsed into one entry).
656
+ SecurityMonitor.logSecurityEvent({
657
+ type: 'TOTP_VERIFICATION_FAILED',
658
+ severity: 'MEDIUM',
659
+ source: 'ConsoleTokenStore.verifyTotp',
660
+ details: 'Presented TOTP code matched neither the live secret nor any backup code',
661
+ });
662
+ return { ok: false };
663
+ }
664
+ // Consume the matched backup code — remove from storage and persist.
665
+ this.data.totp.backupCodes.splice(matchIndex, 1);
666
+ await this.write(this.data);
667
+ logger.info('[ConsoleToken] Backup code consumed', {
668
+ remaining: this.data.totp.backupCodes.length,
669
+ });
670
+ SecurityMonitor.logSecurityEvent({
671
+ type: 'TOTP_BACKUP_CODE_CONSUMED',
672
+ severity: 'MEDIUM',
673
+ source: 'ConsoleTokenStore.verifyTotp',
674
+ details: 'TOTP backup code consumed for console authentication',
675
+ additionalData: { remaining: this.data.totp.backupCodes.length },
676
+ });
677
+ return { ok: true, method: 'backup', backupCodesRemaining: this.data.totp.backupCodes.length };
678
+ }
679
+ /**
680
+ * Disable TOTP. Requires a valid code (TOTP or backup) as confirmation so
681
+ * an attacker who momentarily has access to a live session can't silently
682
+ * strip the second factor.
683
+ *
684
+ * @throws Error if not enrolled or code invalid.
685
+ */
686
+ async disableTotp(code) {
687
+ if (!this.data) {
688
+ throw new TotpError('Token store not initialized', 'STORE_NOT_INITIALIZED');
689
+ }
690
+ if (!this.isTotpEnrolled()) {
691
+ throw new TotpError('TOTP is not currently enrolled', 'NOT_ENROLLED');
692
+ }
693
+ const result = await this.verifyTotp(code);
694
+ if (!result.ok) {
695
+ throw new TotpError(INVALID_TOTP_MESSAGE, 'INVALID_TOTP_CODE');
696
+ }
697
+ this.data.totp = {
698
+ enrolled: false,
699
+ secret: null,
700
+ backupCodes: [],
701
+ enrolledAt: null,
702
+ };
703
+ await this.write(this.data);
704
+ logger.info('[ConsoleToken] TOTP disabled');
705
+ SecurityMonitor.logSecurityEvent({
706
+ type: 'TOTP_DISABLED',
707
+ severity: 'HIGH',
708
+ source: 'ConsoleTokenStore.disableTotp',
709
+ details: 'Console TOTP second factor disabled — single-factor auth restored',
710
+ });
711
+ }
712
+ /** Remove any pending enrollments whose TTL has passed. */
713
+ sweepExpiredEnrollments() {
714
+ const now = Date.now();
715
+ for (const [id, pending] of this.pendingEnrollments) {
716
+ if (pending.expiresAt <= now) {
717
+ this.pendingEnrollments.delete(id);
718
+ }
719
+ }
720
+ }
721
+ // --------------------------------------------------------------------
722
+ // end TOTP
723
+ // --------------------------------------------------------------------
724
+ // --------------------------------------------------------------------
725
+ // Token rotation — #1795
726
+ // --------------------------------------------------------------------
727
+ /**
728
+ * Rotate the primary console token. Requires TOTP confirmation (Pattern B).
729
+ *
730
+ * Flow:
731
+ * 1. Verify the confirmation code via `verifyTotp()` (live TOTP or backup code).
732
+ * 2. Stash the old token value in the in-memory grace buffer so in-flight
733
+ * requests from the rotating tab (and any other local process that read
734
+ * the file before the rotation) still authenticate for ROTATION_GRACE_MS.
735
+ * 3. Generate a new 32-byte token, mutate the primary entry in place, write
736
+ * the file atomically, and rebuild the buffer cache.
737
+ * 4. Return the new token inline so the caller can update `DollhouseAuth.token`
738
+ * without a page reload.
739
+ *
740
+ * Pattern A (OS dialog fallback for users without TOTP) is deferred — the
741
+ * caller should gate the UI so the rotate action is only available when
742
+ * TOTP is enrolled.
743
+ *
744
+ * @throws TotpError STORE_NOT_INITIALIZED — store never loaded
745
+ * @throws TotpError TOTP_REQUIRED — TOTP not enrolled, rotation requires second-factor confirmation
746
+ * @throws TotpError INVALID_TOTP_CODE — wrong code
747
+ */
748
+ async rotatePrimary(confirmationCode) {
749
+ if (!this.data) {
750
+ throw new TotpError('Token store not initialized', 'STORE_NOT_INITIALIZED');
751
+ }
752
+ if (!this.isTotpEnrolled()) {
753
+ throw new TotpError('Token rotation requires TOTP enrollment — enroll a second factor before rotating', 'TOTP_REQUIRED');
754
+ }
755
+ // Step 1: verify the confirmation code.
756
+ const verification = await this.verifyTotp(confirmationCode);
757
+ if (!verification.ok) {
758
+ throw new TotpError(INVALID_TOTP_MESSAGE, 'INVALID_TOTP_CODE');
759
+ }
760
+ // Step 2: stash the old token in the grace buffer.
761
+ const primary = this.data.tokens[0];
762
+ const graceDeadline = Date.now() + ROTATION_GRACE_MS;
763
+ this.sweepExpiredGraceEntries();
764
+ this.graceEntries.push({
765
+ buf: Buffer.from(primary.token, 'utf8'),
766
+ expiresAt: graceDeadline,
767
+ });
768
+ // Step 3: generate a new token, mutate the primary entry, persist.
769
+ const rotatedAt = new Date().toISOString();
770
+ primary.token = generateTokenValue();
771
+ primary.createdAt = rotatedAt;
772
+ primary.lastUsedAt = null;
773
+ primary.createdVia = 'rotation';
774
+ await this.write(this.data);
775
+ this.rebuildTokenBuffers();
776
+ logger.info('[ConsoleToken] Primary token rotated', {
777
+ id: primary.id,
778
+ rotatedAt,
779
+ graceMs: ROTATION_GRACE_MS,
780
+ });
781
+ SecurityMonitor.logSecurityEvent({
782
+ type: 'CONSOLE_TOKEN_ROTATED',
783
+ severity: 'HIGH',
784
+ source: 'ConsoleTokenStore.rotatePrimary',
785
+ details: 'Console primary token rotated via TOTP confirmation',
786
+ additionalData: {
787
+ tokenId: primary.id,
788
+ rotatedAt,
789
+ graceMs: ROTATION_GRACE_MS,
790
+ confirmationMethod: verification.method,
791
+ },
792
+ });
793
+ return {
794
+ token: primary.token,
795
+ rotatedAt,
796
+ graceUntil: graceDeadline,
797
+ };
798
+ }
799
+ /** Remove expired grace entries so they stop matching in verify(). */
800
+ sweepExpiredGraceEntries() {
801
+ const now = Date.now();
802
+ let i = 0;
803
+ while (i < this.graceEntries.length) {
804
+ if (this.graceEntries[i].expiresAt <= now) {
805
+ this.graceEntries.splice(i, 1);
806
+ }
807
+ else {
808
+ i++;
809
+ }
810
+ }
811
+ }
812
+ // --------------------------------------------------------------------
813
+ // end rotation
814
+ // --------------------------------------------------------------------
815
+ /**
816
+ * Read the token file and distinguish missing from corrupt.
817
+ *
818
+ * Returning a tagged union lets `ensureInitialized()` back up corrupt files
819
+ * before overwriting them — users who hand-edited their tokens with custom
820
+ * names or labels deserve a recovery path instead of a silent destroy.
821
+ */
822
+ async readWithStatus() {
823
+ let content;
824
+ try {
825
+ content = await readFile(this.filePath, 'utf8');
826
+ }
827
+ catch (err) {
828
+ if (err.code === 'ENOENT')
829
+ return { status: 'missing' };
830
+ return { status: 'corrupt', reason: err instanceof Error ? err.message : String(err) };
831
+ }
832
+ try {
833
+ const parsed = JSON.parse(content);
834
+ const validated = validateTokenFile(parsed);
835
+ if (!validated)
836
+ return { status: 'corrupt', reason: 'schema validation failed' };
837
+ return { status: 'ok', data: validated };
838
+ }
839
+ catch (err) {
840
+ return { status: 'corrupt', reason: err instanceof Error ? err.message : String(err) };
841
+ }
842
+ }
843
+ /**
844
+ * Copy the current (presumed corrupt) token file to a timestamped backup
845
+ * alongside it so the user can recover hand-edited data after an accidental
846
+ * syntax error. Best-effort — failure to back up does not block creating
847
+ * a fresh file, since the primary goal is keeping the console usable.
848
+ */
849
+ async backupCorruptFile() {
850
+ const timestamp = new Date().toISOString().replaceAll(/[:.]/g, '-');
851
+ const backupPath = `${this.filePath}.corrupt-${timestamp}`;
852
+ try {
853
+ await copyFile(this.filePath, backupPath);
854
+ logger.warn(`[ConsoleToken] Corrupt token file backed up to ${backupPath} — a fresh token will be created`);
855
+ }
856
+ catch (err) {
857
+ logger.warn('[ConsoleToken] Could not back up corrupt token file, will overwrite in place', {
858
+ error: err instanceof Error ? err.message : String(err),
859
+ });
860
+ }
861
+ }
862
+ /**
863
+ * Atomically write the token file with owner-only permissions.
864
+ * Uses temp+rename to avoid partial writes on crash.
865
+ *
866
+ * On Windows, `chmod(0o600)` is effectively a no-op because the file
867
+ * system uses ACLs instead of POSIX modes. We log a one-time warning so
868
+ * users on Windows know the token file does not have OS-enforced access
869
+ * control and can decide whether to use additional tooling (icacls, NTFS
870
+ * permissions, or a different storage location).
871
+ */
872
+ async write(file) {
873
+ await mkdir(RUN_DIR, { recursive: true });
874
+ const tmpFile = `${this.filePath}.${process.pid}.tmp`;
875
+ try {
876
+ await writeFile(tmpFile, JSON.stringify(file, null, 2), 'utf8');
877
+ await chmod(tmpFile, TOKEN_FILE_MODE);
878
+ await rename(tmpFile, this.filePath);
879
+ this.warnIfWindowsPermissions();
880
+ }
881
+ catch (err) {
882
+ try {
883
+ await unlink(tmpFile);
884
+ }
885
+ catch { /* ignore */ }
886
+ throw err;
887
+ }
888
+ }
889
+ /** One-shot flag so the Windows permissions warning is logged at most once. */
890
+ windowsWarningLogged = false;
891
+ warnIfWindowsPermissions() {
892
+ if (this.windowsWarningLogged)
893
+ return;
894
+ if (platform() !== 'win32')
895
+ return;
896
+ this.windowsWarningLogged = true;
897
+ logger.warn(`[ConsoleToken] Token file at ${this.filePath} has no OS-enforced access control on Windows ` +
898
+ `(chmod 0o600 is a no-op on this platform). Any process running as the same user can read the file. ` +
899
+ `Consider using NTFS ACLs via 'icacls' for stronger isolation in multi-user environments.`);
900
+ }
901
+ }
902
+ /**
903
+ * Read the raw token file from disk without constructing a store.
904
+ * Intended for follower processes that need the primary token to attach
905
+ * to their ingest POSTs. Returns null if the file does not exist or is invalid.
906
+ *
907
+ * @param filePath - Optional override for the token file location
908
+ */
909
+ export async function readTokenFileRaw(filePath = DEFAULT_TOKEN_FILE) {
910
+ try {
911
+ const content = await readFile(filePath, 'utf8');
912
+ return validateTokenFile(JSON.parse(content));
913
+ }
914
+ catch {
915
+ return null;
916
+ }
917
+ }
918
+ /**
919
+ * Get the primary token value from the token file on disk.
920
+ * Convenience helper for followers and external consumers.
921
+ */
922
+ export async function getPrimaryTokenFromFile(filePath = DEFAULT_TOKEN_FILE) {
923
+ const file = await readTokenFileRaw(filePath);
924
+ if (!file || file.tokens.length === 0)
925
+ return null;
926
+ return file.tokens[0].token;
927
+ }
928
+ /** Export the default file path so callers can reference it in logs/docs. */
929
+ export { DEFAULT_TOKEN_FILE };
930
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc29sZVRva2VuLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3dlYi9jb25zb2xlL2NvbnNvbGVUb2tlbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTJDRztBQUVILE9BQU8sRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLFFBQVEsRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUN0RCxPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ2pDLE9BQU8sRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUMvRixPQUFPLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxVQUFVLEVBQUUsZUFBZSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ25GLE9BQU8sRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQ3ZDLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLCtDQUErQyxDQUFDO0FBQ2pGLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUNwRSxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFFL0Msa0VBQWtFO0FBQ2xFLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxPQUFPLEVBQUUsRUFBRSxZQUFZLEVBQUUsS0FBSyxDQUFDLENBQUM7QUFFckQ7Ozs7Ozs7Ozs7O0dBV0c7QUFDSCxNQUFNLGtCQUFrQixHQUFHLElBQUksQ0FBQyxPQUFPLEVBQUUseUJBQXlCLENBQUMsQ0FBQztBQUVwRSx5Q0FBeUM7QUFDekMsTUFBTSxrQkFBa0IsR0FBRyxDQUFVLENBQUM7QUFFdEMsa0VBQWtFO0FBQ2xFLE1BQU0sV0FBVyxHQUFHLEVBQUUsQ0FBQztBQUV2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Z2UgYW5kIHZlcmlmaWNhdGlvbiAoIzE3ODApLlxuICpcbiAqIE1hbmFnZXMgdGhlIGZpbGUgYXQgYH4vLmRvbGxob3VzZS9ydW4vY29uc29sZS10b2tlbi5hdXRoLmpzb25gIHdoaWNoXG4gKiBob2xkcyB0aGUgQmVhcmVyIHRva2VucyB0aGF0IGF1dGhlbnRpY2F0ZSByZXF1ZXN0cyB0byB0aGUgd2ViIGNvbnNvbGUuXG4gKiBUaGUgZmlsZSBpcyBjcmVhdGVkIG9uIGZpcnN0IGxlYWRlciBlbGVjdGlvbiBhbmQgcGVyc2lzdHMgYWNyb3NzXG4gKiByZXN0YXJ0cyDigJQgdG9rZW5zIG9ubHkgcm90YXRlIHdoZW4gZXhwbGljaXRseSByZXF1ZXN0ZWQuXG4gKlxuICogVGhlIGAuYXV0aGAgZmlsZW5hbWUgc3VmZml4IGtlZXBzIHRoaXMgc3RhdGUgaXNvbGF0ZWQgZnJvbSBhbnkgbGVnYWN5XG4gKiBuby1hdXRoZW50aWNhdGlvbiBEb2xsaG91c2VNQ1AgaW5zdGFsbGF0aW9uIHJ1bm5pbmcgb24gdGhlIHNhbWVcbiAqIG1hY2hpbmUuIFRoZSBwb3J0LCBsb2NrIGZpbGUsIGFuZCB0b2tlbiBmaWxlIGFsbCBzaGFyZSB0aGUgc2FtZVxuICogaXNvbGF0aW9uIHN0cmF0ZWd5IOKAlCBzZWUgYHNyYy9jb25maWcvZW52LnRzYCBmb3IgdGhlIHBvcnQsIGFuZFxuICogYExlYWRlckVsZWN0aW9uLnRzYCBmb3IgdGhlIGxvY2sgZmlsZS5cbiAqXG4gKiBTY2hlbWEgaXMgZm9yd2FyZC1jb21wYXRpYmxlIHdpdGggbXVsdGktZGV2aWNlLCBtdWx0aS10ZW5hbnQsIGFuZFxuICogc2NvcGUtcmVzdHJpY3RlZCB0b2tlbnMgZm9yIFBoYXNlIDIrLiBQaGFzZSAxIHVzZXMgYSBzaW5nbGUgXCJjb25zb2xlXCJcbiAqIGtpbmQgdG9rZW4gYW5kIHN0dWJzIHRoZSBzY29wZS9ib3VuZGFyeSBjaGVja3MuXG4gKlxuICogRmlsZSBmb3JtYXQgKHZlcnNpb24gMSk6XG4gKiBgYGBqc29uXG4gKiB7XG4gKiAgIFwidmVyc2lvblwiOiAxLFxuICogICBcInRva2Vuc1wiOiBbXG4gKiAgICAge1xuICogICAgICAgXCJpZFwiOiBcIjAxOGUxYTJiLS4uLlwiLFxuICogICAgICAgXCJuYW1lXCI6IFwiS2VybWl0IG9uIG1pY2stTWFjQm9vay1Qcm9cIixcbiAqICAgICAgIFwia2luZFwiOiBcImNvbnNvbGVcIixcbiAqICAgICAgIFwidG9rZW5cIjogXCI8NjQtaGV4PlwiLFxuICogICAgICAgXCJzY29wZXNcIjogW1wiYWRtaW5cIl0sXG4gKiAgICAgICBcImVsZW1lbnRCb3VuZGFyaWVzXCI6IG51bGwsXG4gKiAgICAgICBcInRlbmFudFwiOiBudWxsLFxuICogICAgICAgXCJwbGF0Zm9ybVwiOiBcImxvY2FsXCIsXG4gKiAgICAgICBcImxhYmVsc1wiOiB7fSxcbiAqICAgICAgIFwiY3JlYXRlZEF0XCI6IFwiMjAyNi0wNC0wNFQyMDowMDowMC4wMDBaXCIsXG4gKiAgICAgICBcImxhc3RVc2VkQXRcIjogbnVsbCxcbiAqICAgICAgIFwiY3JlYXRlZFZpYVwiOiBcImluaXRpYWwtc2V0dXBcIlxuICogICAgIH1cbiAqICAgXSxcbiAqICAgXCJ0b3RwXCI6IHsgXCJlbnJvbGxlZFwiOiBmYWxzZSwgXCJzZWNyZXRcIjogbnVsbCwgXCJiYWNrdXBDb2Rlc1wiOiBbXSB9XG4gKiB9XG4gKiBgYGBcbiAqXG4gKiBAc2luY2UgdjIuMS4wIOKAlCBJc3N1ZSAjMTc4MFxuICovXG5cbmltcG9ydCB7IGhvbWVkaXIsIGhvc3RuYW1lLCBwbGF0Zm9ybSB9IGZyb20gJ25vZGU6b3MnO1xuaW1wb3J0IHsgam9pbiB9IGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgeyBta2RpciwgcmVhZEZpbGUsIHJlbmFtZSwgd3JpdGVGaWxlLCBjaG1vZCwgdW5saW5rLCBjb3B5RmlsZSB9IGZyb20gJ25vZGU6ZnMvcHJvbWlzZXMnO1xuaW1wb3J0IHsgY3JlYXRlSGFzaCwgcmFuZG9tQnl0ZXMsIHJhbmRvbVVVSUQsIHRpbWluZ1NhZmVFcXVhbCB9IGZyb20gJ25vZGU6Y3J5cHRvJztcbmltcG9ydCB7IFNlY3JldCwgVE9UUCB9IGZyb20gJ290cGF1dGgnO1xuaW1wb3J0IHsgVW5pY29kZVZhbGlkYXRvciB9IGZyb20gJy4uLy4uL3NlY3VyaXR5L3ZhbGlkYXRvcnMvdW5pY29kZVZhbGlkYXRvci5qcyc7XG5pbXBvcnQgeyBTZWN1cml0eU1vbml0b3IgfSBmcm9tICcuLi8uLi9zZWN1cml0eS9zZWN1cml0eU1vbml0b3IuanMnO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vLi4vdXRpbHMvbG9nZ2VyLmpzJztcblxuLyoqIERpcmVjdG9yeSBmb3IgcnVudGltZSBzdGF0ZSBmaWxlcyDigJQgc2FtZSBhcyBMZWFkZXJFbGVjdGlvbi4gKi9cbmNvbnN0IFJVTl9ESVIgPSBqb2luKGhvbWVkaXIoKSwgJy5kb2xsaG91c2UnLCAncnVuJyk7XG5cbi8qKlxuICogRGVmYXVsdCBwYXRoIHRvIHRoZSBhdXRoZW50aWNhdGVkIGNvbnNvbGUncyB0b2tlbiBmaWxlLlxuICpcbiAqIFRoZSBgLmF1dGhgIHN1ZmZpeCBpc29sYXRlcyB0aGlzIGZyb20gYW55IGxlZ2FjeSBuby1hdXRoZW50aWNhdGlvblxuICogRG9sbGhvdXNlTUNQIGluc3RhbGxhdGlvbiB0aGF0IG1heSBhbHNvIGJlIHJ1bm5pbmcgb24gdGhlIHNhbWVcbiAqIG1hY2hpbmUuIExlZ2FjeSBpbnN0YWxscyBkaWQgbm90IHdyaXRlIGEgdG9rZW4gZmlsZSBhdCBhbGw7IHRoZVxuICogYXV0aGVudGljYXRlZCBjb25zb2xlIHdyaXRlcyBgY29uc29sZS10b2tlbi5hdXRoLmpzb25gLiBDb21iaW5lZCB3aXRoXG4gKiB0aGUgcG9ydCBhbmQgbG9jay1maWxlIHNlcGFyYXRpb24sIHRoaXMgZ2l2ZXMgdGhlIHR3byBnZW5lcmF0aW9ucyBvZlxuICogdGhlIGNvbnNvbGUgZnVsbHkgaW5kZXBlbmRlbnQgc3RhdGUgdHJlZXMgdW5kZXIgYH4vLmRvbGxob3VzZS9ydW4vYC5cbiAqXG4gKiBDYWxsZXJzIGNhbiBvdmVycmlkZSB2aWEgYERPTExIT1VTRV9DT05TT0xFX1RPS0VOX0ZJTEVgIGluIHRoZSBlbnYuXG4gKi9cbmNvbnN0IERFRkFVTFRfVE9LRU5fRklMRSA9IGpvaW4oUlVOX0RJUiwgJ2NvbnNvbGUtdG9rZW4uYXV0aC5qc29uJyk7XG5cbi8qKiBDdXJyZW50IHRva2VuIGZpbGUgc2NoZW1hIHZlcnNpb24uICovXG5jb25zdCBUT0tFTl9GSUxFX1ZFUlNJT04gPSAxIGFzIGNvbnN0O1xuXG4vKiogVG9rZW4gbGVuZ3RoIGluIGJ5dGVzIChwcm9kdWNlcyBhIDY0LWNoYXJhY3RlciBoZXggc3RyaW5nKS4gKi9cbmNvbnN0IFRPS0VOX0JZVEVTID0gMzI7XG5cbi8qKiBGaWxlIG1vZGUgZm9yIHRoZSB0b2tlbiBmaWxlIOKAlCBvd25lciByZWFkL3dyaXRlIG9ubHkuICovXG5jb25zdCBUT0tFTl9GSUxFX01PREUgPSAwbzYwMDtcblxuLyoqXG4gKiBUT1RQIGNvbmZpZ3VyYXRpb24g4oCUIFJGQyA2MjM4IGRlZmF1bHRzLiBUaGVzZSBhcmUgY29tcGlsZWQtaW4gc28gdGhlXG4gKiBvdHBhdXRoIFVSSSBpcyBkZXRlcm1pbmlzdGljIGFuZCBjb21wYXRpYmxlIHdpdGggZXZlcnkgY29tbW9uIGF1dGhlbnRpY2F0b3JcbiAqIGFwcCAoR29vZ2xlIEF1dGhlbnRpY2F0b3IsIDFQYXNzd29yZCwgQXV0aHksIEJpdHdhcmRlbiwgZXRjLikuXG4gKi9cbmNvbnN0IFRPVFBfSVNTVUVSID0gJ0RvbGxob3VzZU1DUCc7XG5jb25zdCBUT1RQX0FMR09SSVRITSA9ICdTSEExJyBhcyBjb25zdDtcbmNvbnN0IFRPVFBfRElHSVRTID0gNjtcbmNvbnN0IFRPVFBfUEVSSU9EX1NFQ09ORFMgPSAzMDtcbmNvbnN0IFRPVFBfU0VDUkVUX1NJWkVfQllURVMgPSAyMDsgLy8gMTYwIGJpdHMg4oCUIHRoZSBSRkMgNjIzOCByZWNvbW1lbmRhdGlvblxuLyoqXG4gKiDCsTIgdGltZSBzdGVwIHRvbGVyYW5jZSAowrE2MHMgZHJpZnQpIOKGkiAxNTAgc2Vjb25kIHRvdGFsIHZhbGlkaXR5IHdpbmRvdy5cbiAqXG4gKiBUaGUgbmFpdmUgY2hvaWNlIHdvdWxkIGJlIGB3aW5kb3c6IDFgICjCsTMwcywgOTBzIHRvdGFsKSB3aGljaCBpcyB0aGUgUkZDXG4gKiA2MjM4IGRlZmF1bHQgYW5kIG1hdGNoZXMgbW9zdCB3ZWIgbG9naW4gZmxvd3Mgd2hlcmUgdGhlIHVzZXIgdHlwZXMgYSBjb2RlXG4gKiB0aGV5IGp1c3QgZ2VuZXJhdGVkLCB3aXRoIHN1Yi1zZWNvbmQgbGF0ZW5jeSB0byB0aGUgc2VydmVyLiBUaGF0IGRvZXNuJ3RcbiAqIGNvdmVyIG91ciByZWFsIGRlcGxveW1lbnQgcHJvZmlsZTpcbiAqXG4gKiAxLiAqKkFzeW5jIGJyaWRnZSBmbG93cyoqIOKAlCBEb2xsaG91c2VCcmlkZ2Ugb3ZlciBadWxpcCAob3Igc2ltaWxhciBjaGF0XG4gKiAgICB0cmFuc3BvcnRzKSBjYW4gYWRkIDEwLTYwIHNlY29uZHMgb2YgbGF0ZW5jeSBiZXR3ZWVuIHRoZSB1c2VyIHR5cGluZ1xuICogICAgdGhlIGNvZGUgYW5kIHRoZSBNQ1Agc2VydmVyIHZlcmlmeWluZyBpdC4gQSBjb2RlIHR5cGVkIHdpdGggYSBmZXdcbiAqICAgIHNlY29uZHMgb2YgbGlmZXRpbWUgcmVtYWluaW5nIGNhbiBzbGlkZSBvdXQgb2YgdGhlIHZhbGlkYXRpb24gd2luZG93XG4gKiAgICBiZWZvcmUgcmVhY2hpbmcgdGhlIHNlcnZlci5cbiAqXG4gKiAyLiAqKkNsb2NrIGRyaWZ0Kiog4oCUIGNvbnRhaW5lcnMsIFZNcywgYW5kIHBob25lcyB3aXRoIGxhenkgTlRQIGNhblxuICogICAgZGlzYWdyZWUgd2l0aCB0aGUgc2VydmVyIGJ5IHRlbnMgb2Ygc2Vjb25kcy4gwrEzMHMgZ2l2ZXMgemVybyBtYXJnaW5cbiAqICAgIGFnYWluc3QgYW55IGRyaWZ0IGF0IGFsbC5cbiAqXG4gKiBgd2luZG93OiAyYCBnaXZlcyA1IHZhbGlkIGNvZGVzIG91dCBvZiAxMF42IHBlciBhdHRlbXB0ICh2cy4gMyB3aXRoXG4gKiB3aW5kb3c9MSkuIENvbWJpbmVkIHdpdGggdGhlIDEwL21pbiByYXRlIGxpbWl0ZXIgYW5kIHRoZSBhbHdheXMtb24gYXV0aFxuICogZ2F0ZSwgYnJ1dGUtZm9yY2Ugc3VjY2VzcyBwcm9iYWJpbGl0eSByZW1haW5zIGJlbG93IDVlLTUgcGVyIG1pbnV0ZSDigJRcbiAqIHdlbGwgd2l0aGluIHRoZSBzZWN1cml0eSBlbnZlbG9wZS4gYHdpbmRvdzogM2AgKMKxOTBzLCAyMTBzIHRvdGFsKSBpc1xuICogZGVmZW5zaWJsZSBidXQgcmVzZXJ2ZWQgZm9yIGZ1dHVyZSBkZXBsb3ltZW50IHByZXNzdXJlOyBlYXNpZXIgdG8gd2lkZW5cbiAqIGxhdGVyIHRoYW4gdG8gbmFycm93IGxhdGVyLlxuICpcbiAqIE5vdGU6IHRoZSB3aW5kb3cgaXMgYW5jaG9yZWQgdG8gdGhlICoqc2VydmVyJ3MgdmVyaWZpY2F0aW9uIGNsb2NrKiosIG5vdFxuICogdGhlIHVzZXIncyBjb2RlLWdlbmVyYXRpb24gY2xvY2suIFdoZW4gYSB1c2VyIGNvbXBsYWlucyB0aGF0IGEgY29kZSB0aGV5XG4gKiBcImp1c3QgdHlwZWRcIiB3YXMgcmVqZWN0ZWQsIHRoZSBlcnJvciBtZXNzYWdlIG51ZGdlcyB0aGVtIHRvIHN1Ym1pdCBjb2Rlc1xuICogd2l0aCBwbGVudHkgb2Ygc3RlcC1saWZldGltZSByZW1haW5pbmcuXG4gKi9cbmNvbnN0IFRPVFBfVkFMSURBVEVfV0lORE9XID0gMjtcblxuLyoqXG4gKiBFcnJvciBtZXNzYWdlIGZvciBhIGZhaWxlZCBUT1RQIGNvZGUgY2hlY2suIEluY2x1ZGVzIGEgaGludCBhYm91dCB0aGVcbiAqIGNvbW1vbiBmYWlsdXJlIG1vZGUgKGNvZGUgYWdlZCBvdXQgaW4gdHJhbnNpdCkgc28gdXNlcnMgb24gaGlnaC1sYXRlbmN5XG4gKiBjaGFubmVscyBrbm93IHdoYXQgdG8gdHJ5IGRpZmZlcmVudGx5IG9uIHJldHJ5LlxuICovXG5jb25zdCBJTlZBTElEX1RPVFBfTUVTU0FHRSA9XG4gICdJbnZhbGlkIFRPVFAgY29kZS4gV2hlbiBjb3B5aW5nIGZyb20gYW4gYXV0aGVudGljYXRvciBhcHAsIHVzZSBhIGNvZGUgJyArXG4gICd3aXRoIHBsZW50eSBvZiBsaWZldGltZSByZW1haW5pbmcg4oCUIGNvZGVzIGNhbiBhZ2Ugb3V0IGluIHRyYW5zaXQgb24gJyArXG4gICdzbG93ZXIgY2hhbm5lbHMgKGUuZy4gY2hhdCBicmlkZ2VzKS4gV2FpdCBmb3IgYSBmcmVzaCBjb2RlIGlmIHVuc3VyZS4nO1xuLyoqIFBlbmRpbmcgZW5yb2xsbWVudHMgZXhwaXJlIHRoaXMgbG9uZyBhZnRlciBiZWdpbigpIHRvIGxpbWl0IGluLW1lbW9yeSBzZWNyZXQgbGlmZXRpbWUuICovXG5jb25zdCBUT1RQX1BFTkRJTkdfVFRMX01TID0gMTAgKiA2MCAqIDEwMDA7IC8vIDEwIG1pbnV0ZXNcbi8qKlxuICogSGFyZCBjYXAgb24gY29uY3VycmVudCBwZW5kaW5nIGVucm9sbG1lbnRzLiBBbiBhdXRoZW50aWNhdGVkIGNhbGxlciB3aG9cbiAqIG1pc2JlaGF2ZXMgY291bGQgb3RoZXJ3aXNlIGZpbGwgdGhlIGluLW1lbW9yeSBNYXAgd2l0aCAxMC1taW51dGUtVFRMXG4gKiBzZWNyZXRzLiBSZWFsaXN0aWMgdXNlcnMgbmVlZCBhdCBtb3N0IDEgcGVuZGluZyBlbnJvbGxtZW50IGF0IGEgdGltZTtcbiAqIDEwIGlzIGdlbmVyb3VzIGhlYWRyb29tIGZvciByZXRyeS1kdXJpbmctZW5yb2xsbWVudCBzY2VuYXJpb3MuXG4gKlxuICogV2hlbiB0aGUgY2FwIGlzIGhpdCwgYGJlZ2luVG90cEVucm9sbG1lbnRgIHRocm93cyBgVE9PX01BTllfUEVORElOR2BcbiAqIChtYXBwZWQgdG8gSFRUUCA0MjkpLiBSZWplY3RpbmcgbG91ZGx5IGlzIHByZWZlcnJlZCBvdmVyIHNpbGVudGx5XG4gKiBldmljdGluZyBzb21lb25lIGVsc2UncyBsZWdpdGltYXRlIHBlbmRpbmcgZW50cnkuXG4gKi9cbmNvbnN0IE1BWF9QRU5ESU5HX0VOUk9MTE1FTlRTID0gMTA7XG5cbi8qKlxuICogR3JhY2Ugd2luZG93IGZvciBvbGQgdG9rZW5zIGFmdGVyIHJvdGF0aW9uIOKAlCAxNSBzZWNvbmRzLlxuICpcbiAqIEFmdGVyIGEgcm90YXRpb24sIHRoZSBwcmV2aW91cyB0b2tlbiB2YWx1ZSBzdGF5cyB2YWxpZCBmb3IgdGhpcyBwZXJpb2QgdG9cbiAqIGNvdmVyIGluLWZsaWdodCByZXF1ZXN0cyBmcm9tIHRoZSByb3RhdGluZyB0YWIgYW5kIGFueSBvdGhlciBsb2NhbCBwcm9jZXNzZXNcbiAqIHRoYXQgcmVhZCB0aGUgdG9rZW4gZmlsZSBidXQgaGF2ZW4ndCBwaWNrZWQgdXAgdGhlIG5ldyB2YWx1ZSB5ZXQuIEFsbFxuICogY29uc3VtZXJzIGFyZSBsb29wYmFjay1vbmx5ICgxMjcuMC4wLjEpLCBzbyBuZXR3b3JrIGxhdGVuY3kgaXMgbmVnbGlnaWJsZVxuICogYW5kIDE1cyBpcyBnZW5lcm91cy5cbiAqXG4gKiBHcmFjZSBlbnRyaWVzIGFyZSBwZXItcm90YXRpb24gYW5kIGluLW1lbW9yeSBvbmx5IOKAlCB0aGV5IGFyZSBuZXZlciBwZXJzaXN0ZWRcbiAqIHRvIGRpc2suIEEgbGVhZGVyIGNyYXNoIGtpbGxzIGFsbCBncmFjZSBlbnRyaWVzLCB3aGljaCBpcyB0aGUgc2FmZSBmYWlsdXJlXG4gKiBtb2RlIChjYWxsZXJzIHJlLWF1dGggd2l0aCB0aGUgbmV3IHRva2VuIGZyb20gdGhlIGZpbGUpLlxuICovXG5jb25zdCBST1RBVElPTl9HUkFDRV9NUyA9IDE1XzAwMDtcblxuLyoqIE51bWJlciBvZiBiYWNrdXAgY29kZXMgZ2VuZXJhdGVkIG9uIGVucm9sbG1lbnQuICovXG5jb25zdCBCQUNLVVBfQ09ERV9DT1VOVCA9IDEwO1xuLyoqIENoYXJhY3RlcnMgcGVyIGJhY2t1cCBjb2RlIOKAlCBDcm9ja2ZvcmQgYmFzZTMyLWlzaCwgbm8gYW1iaWd1b3VzIGNoYXJzLiAqL1xuY29uc3QgQkFDS1VQX0NPREVfTEVOR1RIID0gODtcbi8qKlxuICogQmFja3VwIGNvZGUgYWxwaGFiZXQg4oCUIENyb2NrZm9yZCBiYXNlMzIgKDMyIGNoYXJzLCBleGNsdWRlcyBJL0wvTy9VKS5cbiAqIDUgYml0cyBwZXIgY2hhciDDlyA4IGNoYXJzID0gNDAgYml0cyBwZXIgY29kZSwgcGxlbnR5IGZvciBvbmUtc2hvdCB1c2UuXG4gKi9cbmNvbnN0IEJBQ0tVUF9DT0RFX0FMUEhBQkVUID0gJzAxMjM0NTY3ODlBQkNERUZHSEpLTU5QUVJTVFZXWFlaJztcblxuLyoqXG4gKiBTdHJpY3QgZm9ybWF0IGZvciBjb25zb2xlIHRva2VucyDigJQgNjQgbG93ZXJjYXNlIGhleCBjaGFyYWN0ZXJzLlxuICogVXNlZCBhcyBhIGRlZmVuc2UtaW4tZGVwdGggY2hlY2sgaW4gdmVyaWZ5KCk6IGV2ZW4gaWYgdGhlIGNhbGxlciBmb3Jnb3RcbiAqIHRvIHNhbml0aXplIHRoZSBwcmVzZW50ZWQgdmFsdWUsIHdlIHJlamVjdCBhbnl0aGluZyB0aGF0IGlzbid0IGEgbGVnaXRpbWF0ZVxuICogMjU2LWJpdCBoZXggdG9rZW4gYmVmb3JlIHJlYWNoaW5nIHRoZSBjb25zdGFudC10aW1lIGNvbXBhcmlzb24uXG4gKiBETUNQLVNFQy0wMDQgbWl0aWdhdGlvbi5cbiAqL1xuY29uc3QgVE9LRU5fRk9STUFUID0gL15bMC05YS1mXXs2NH0kLztcblxuLyoqXG4gKiBFbGVtZW50IHZpc2liaWxpdHkgYm91bmRhcnkg4oCUIFBoYXNlIDMgZW50ZXJwcmlzZSBmZWF0dXJlLlxuICogUGhhc2UgMSBhbHdheXMgc3RvcmVzIGBudWxsYDsgdGhlIGZpZWxkIGV4aXN0cyBzbyB0aGUgc2NoZW1hIGlzIHN0YWJsZS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBFbGVtZW50Qm91bmRhcnkge1xuICBhbGxvd0NhdGVnb3JpZXM/OiBzdHJpbmdbXTtcbiAgZGVueUNhdGVnb3JpZXM/OiBzdHJpbmdbXTtcbiAgYWxsb3dUeXBlcz86IHN0cmluZ1tdO1xuICBkZW55VHlwZXM/OiBzdHJpbmdbXTtcbn1cblxuLyoqXG4gKiBBIHNpbmdsZSB0b2tlbiBlbnRyeSBpbiB0aGUgY29uc29sZSB0b2tlbiBmaWxlLlxuICpcbiAqIEVudGVycHJpc2UtcmVhZHkgZmllbGRzIChgc2NvcGVzYCwgYGVsZW1lbnRCb3VuZGFyaWVzYCwgYHRlbmFudGAsIGBwbGF0Zm9ybWAsXG4gKiBgbGFiZWxzYCkgYXJlIHByZXNlbnQgZnJvbSBQaGFzZSAxIGJ1dCBub3QgZW5mb3JjZWQg4oCUIHRoZSBtaWRkbGV3YXJlIHRyZWF0c1xuICogZXZlcnkgdmFsaWQgdG9rZW4gYXMgYWRtaW4tc2NvcGVkLCBzaW5nbGUtdGVuYW50LCBhbGwtZWxlbWVudHMgZm9yIG5vdy5cbiAqIFBoYXNlIDIrIGZsaXBzIGVuZm9yY2VtZW50IG9uIHdpdGhvdXQgcmVxdWlyaW5nIGEgc2NoZW1hIG1pZ3JhdGlvbi5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBDb25zb2xlVG9rZW5FbnRyeSB7XG4gIC8qKiBTdGFibGUgdW5pcXVlIGlkZW50aWZpZXIgZm9yIHRoaXMgdG9rZW4gKFVVSUQgdjQpLiAqL1xuICBpZDogc3RyaW5nO1xuICAvKiogSHVtYW4tcmVhZGFibGUgbmFtZSBzaG93biBpbiB0aGUgU2VjdXJpdHkgdGFiLiAqL1xuICBuYW1lOiBzdHJpbmc7XG4gIC8qKiBXaGF0IGtpbmQgb2YgY2xpZW50IHRoaXMgdG9rZW4gaXMgZm9yLiAqL1xuICBraW5kOiAnY29uc29sZScgfCAnZGV2aWNlJyB8ICdhdXRvbWF0aW9uJztcbiAgLyoqIFRoZSBzZWNyZXQgQmVhcmVyIHZhbHVlLiA2NCBoZXggY2hhcnMgKDI1NiBiaXRzIG9mIGVudHJvcHkpLiAqL1xuICB0b2tlbjogc3RyaW5nO1xuICAvKiogU2NvcGVzIGdyYW50ZWQgdG8gdGhpcyB0b2tlbi4gUGhhc2UgMSA9IGFsd2F5cyBgW1wiYWRtaW5cIl1gLiAqL1xuICBzY29wZXM6IHN0cmluZ1tdO1xuICAvKiogRWxlbWVudCB2aXNpYmlsaXR5IHJlc3RyaWN0aW9uLiBQaGFzZSAxID0gYWx3YXlzIGBudWxsYC4gKi9cbiAgZWxlbWVudEJvdW5kYXJpZXM6IEVsZW1lbnRCb3VuZGFyeSB8IG51bGw7XG4gIC8qKiBUZW5hbnQgaWRlbnRpZmllciBmb3IgbXVsdGktdGVuYW50IGRlcGxveW1lbnRzLiBQaGFzZSAxID0gYWx3YXlzIGBudWxsYC4gKi9cbiAgdGVuYW50OiBzdHJpbmcgfCBudWxsO1xuICAvKiogV2hlcmUgdGhpcyB0b2tlbiBpcyB1c2VkLiBQaGFzZSAxID0gYWx3YXlzIGBcImxvY2FsXCJgLiAqL1xuICBwbGF0Zm9ybTogc3RyaW5nO1xuICAvKiogT3BhcXVlIG1ldGFkYXRhIGZvciBlbnRlcnByaXNlIHRvb2xpbmcuIFBoYXNlIDEgPSBhbHdheXMgYHt9YC4gKi9cbiAgbGFiZWxzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xuICAvKiogSVNPIHRpbWVzdGFtcCBvZiB0b2tlbiBjcmVhdGlvbi4gKi9cbiAgY3JlYXRlZEF0OiBzdHJpbmc7XG4gIC8qKiBJU08gdGltZXN0YW1wIG9mIG1vc3QgcmVjZW50IHVzZSwgb3IgbnVsbCBpZiBuZXZlciB1c2VkLiAqL1xuICBsYXN0VXNlZEF0OiBzdHJpbmcgfCBudWxsO1xuICAvKiogSG93IHRoaXMgdG9rZW4gd2FzIGNyZWF0ZWQg4oCUIFwiaW5pdGlhbC1zZXR1cFwiLCBcInBhaXJpbmdcIiwgXCJyb3RhdGlvblwiLCBldGMuICovXG4gIGNyZWF0ZWRWaWE6IHN0cmluZztcbn1cblxuLyoqXG4gKiBUT1RQIGVucm9sbG1lbnQgc3RhdGUuXG4gKlxuICogUG9wdWxhdGVkIGJ5IFBoYXNlIDIgKCMxNzk0KS4gVGhlIGBzZWNyZXRgIGZpZWxkIGhvbGRzIHRoZSBiYXNlMzItZW5jb2RlZFxuICogUkZDIDYyMzggc2VjcmV0IGluIHBsYWludGV4dCDigJQgdGhlIHN1cnJvdW5kaW5nIGZpbGUgaXMgMDYwMCwgd2hpY2ggbWF0Y2hlc1xuICogc3RhbmRhcmQgZmlsZS1iYXNlZCBUT1RQIHN0b3JhZ2UgcHJhY3RpY2UgKFNTSCBrZXlzLCBhZ2UgaWRlbnRpdHkgZmlsZXMsXG4gKiAxUGFzc3dvcmQgdmF1bHQgYmFja3VwcykuIEVuY3J5cHRpbmcgdGhlIHNlY3JldCBhdCByZXN0IHdpdGggYW4gT1Mga2V5Y2hhaW5cbiAqIGlzIGEgZnV0dXJlIGVuaGFuY2VtZW50LlxuICpcbiAqIEJhY2t1cCBjb2RlcyBhcmUgc3RvcmVkIGFzICoqc2hhMjU2IGhleCBoYXNoZXMqKiwgbmV2ZXIgcGxhaW50ZXh0LiBUaGVcbiAqIHBsYWludGV4dCBjb2RlcyBhcmUgc2hvd24gdG8gdGhlIHVzZXIgZXhhY3RseSBvbmNlIGF0IGVucm9sbG1lbnQgY29uZmlybVxuICogdGltZS4gRWFjaCBiYWNrdXAgY29kZSBpcyBzaW5nbGUtdXNlOiBjb25zdW1pbmcgb25lIHJlbW92ZXMgaXRzIGhhc2ggZnJvbVxuICogdGhpcyBhcnJheS5cbiAqXG4gKiBgZW5yb2xsZWRBdGAgaXMgb3B0aW9uYWwgZm9yIGJhY2t3YXJkIGNvbXBhdGliaWxpdHkgd2l0aCBQaGFzZSAxIGZpbGVzLFxuICogd2hpY2ggd2VyZSB3cml0dGVuIHdpdGggb25seSBge2Vucm9sbGVkLCBzZWNyZXQsIGJhY2t1cENvZGVzfWAuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgVG90cFN0YXRlIHtcbiAgZW5yb2xsZWQ6IGJvb2xlYW47XG4gIHNlY3JldDogc3RyaW5nIHwgbnVsbDtcbiAgYmFja3VwQ29kZXM6IHN0cmluZ1tdO1xuICBlbnJvbGxlZEF0Pzogc3RyaW5nIHwgbnVsbDtcbn1cblxuLyoqXG4gKiBQdWJsaWMtc2FmZSB2aWV3IG9mIFRPVFAgc3RhdGUg4oCUIG5ldmVyIGxlYWtzIHNlY3JldCBtYXRlcmlhbC5cbiAqIFJldHVybmVkIGZyb20gdGhlIHN0YXR1cyBlbmRwb2ludCBhbmQgZnJvbSBzdG9yZSBtZXRob2RzIHRoYXQgbmVlZCB0b1xuICogcmVwb3J0IGVucm9sbG1lbnQgc3RhdGUgdG8gY2FsbGVycy5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBUb3RwU3RhdHVzIHtcbiAgZW5yb2xsZWQ6IGJvb2xlYW47XG4gIGVucm9sbGVkQXQ6IHN0cmluZyB8IG51bGw7XG4gIGJhY2t1cENvZGVzUmVtYWluaW5nOiBudW1iZXI7XG59XG5cbi8qKlxuICogUmVzdWx0IG9mIGBiZWdpblRvdHBFbnJvbGxtZW50YCDigJQgdGhlIGNhbGxlciBuZWVkcyBhbGwgb2YgdGhlc2UgdG8gc2hvdyBhXG4gKiBRUiBjb2RlIGFuZCBtYW51YWwtZW50cnkgZmFsbGJhY2sgaW4gdGhlIFVJLiBOb25lIG9mIHRoaXMgaXMgcGVyc2lzdGVkO1xuICogdGhlIHBlbmRpbmcgc3RhdGUgb25seSBsaXZlcyBpbi1tZW1vcnkgdW50aWwgY29uZmlybWVkLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFRvdHBFbnJvbGxtZW50QmVnaW4ge1xuICBwZW5kaW5nSWQ6IHN0cmluZztcbiAgLyoqIEJhc2UzMi1lbmNvZGVkIFRPVFAgc2VjcmV0IGZvciBtYW51YWwgZW50cnkgKGdyb3VwZWQgZGlzcGxheSBpcyBjYWxsZXIncyBqb2IpLiAqL1xuICBzZWNyZXQ6IHN0cmluZztcbiAgLyoqIEZ1bGwgYG90cGF1dGg6Ly9gIFVSSSBmb3IgYXV0aGVudGljYXRvciBhcHBzIHRvIGltcG9ydC4gKi9cbiAgb3RwYXV0aFVyaTogc3RyaW5nO1xuICAvKiogVGltZXN0YW1wIChtcyBzaW5jZSBlcG9jaCkgd2hlbiB0aGlzIHBlbmRpbmcgZW5yb2xsbWVudCBleHBpcmVzLiAqL1xuICBleHBpcmVzQXQ6IG51bWJlcjtcbn1cblxuLyoqXG4gKiBSZXN1bHQgb2YgYGNvbmZpcm1Ub3RwRW5yb2xsbWVudGAg4oCUIHRoZSBwbGFpbnRleHQgYmFja3VwIGNvZGVzIGFyZVxuICogcmV0dXJuZWQgdG8gdGhlIGNhbGxlciBleGFjdGx5IG9uY2UsIGF0IHRoaXMgcG9pbnQsIGFuZCB0aGVuIG9ubHkgdGhlaXJcbiAqIGhhc2hlcyBhcmUgcmV0YWluZWQuIFRoZSBjYWxsZXIgbXVzdCBkaXNwbGF5IHRoZW0gdG8gdGhlIHVzZXIgaW1tZWRpYXRlbHlcbiAqIGFuZCBuZXZlciBsb2cgdGhlbS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBUb3RwRW5yb2xsbWVudENvbmZpcm0ge1xuICBiYWNrdXBDb2Rlczogc3RyaW5nW107XG4gIGVucm9sbGVkQXQ6IHN0cmluZztcbn1cblxuLyoqXG4gKiBSZXN1bHQgb2YgYHJvdGF0ZVByaW1hcnlgIOKAlCByZXR1cm5lZCB0byB0aGUgY2FsbGVyIHNvIHRoZXkgY2FuIHVwZGF0ZVxuICogdGhlIGJyb3dzZXIgdG9rZW4gaW4tcGxhY2UgYW5kIGRpc3BsYXkgdGhlIGdyYWNlLXdpbmRvdyBkZWFkbGluZS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBSb3RhdGlvblJlc3VsdCB7XG4gIC8qKiBUaGUgbmV3IDY0LWhleCB0b2tlbiB2YWx1ZS4gVGhlIGNhbGxlciBtdXN0IHRyZWF0IHRoaXMgYXMgYSBzZWNyZXQuICovXG4gIHRva2VuOiBzdHJpbmc7XG4gIC8qKiBJU08gdGltZXN0YW1wIG9mIHRoZSByb3RhdGlvbi4gKi9cbiAgcm90YXRlZEF0OiBzdHJpbmc7XG4gIC8qKiBtcy1zaW5jZS1lcG9jaCBkZWFkbGluZSBhZnRlciB3aGljaCB0aGUgb2xkIHRva2VuIHN0b3BzIHZlcmlmeWluZy4gKi9cbiAgZ3JhY2VVbnRpbDogbnVtYmVyO1xufVxuXG4vKiogSW4tbWVtb3J5IGdyYWNlIGVudHJ5IGZvciBhIHJlY2VudGx5LXJvdGF0ZWQgdG9rZW4uICovXG5pbnRlcmZhY2UgR3JhY2VFbnRyeSB7XG4gIGJ1ZjogQnVmZmVyO1xuICBleHBpcmVzQXQ6IG51bWJlcjtcbn1cblxuLyoqIEludGVybmFsIHBlbmRpbmctZW5yb2xsbWVudCBzdGF0ZSDigJQgc2VjcmV0IGhlbGQgaW4gbWVtb3J5IG9ubHkuICovXG5pbnRlcmZhY2UgUGVuZGluZ0Vucm9sbG1lbnQge1xuICBzZWNyZXQ6IHN0cmluZztcbiAgbGFiZWw6IHN0cmluZztcbiAgZXhwaXJlc0F0OiBudW1iZXI7XG59XG5cbi8qKlxuICogVHlwZWQgZXJyb3IgZm9yIFRPVFAgc3RvcmUgb3BlcmF0aW9ucy4gQ2FycmllcyBhIG1hY2hpbmUtcmVhZGFibGVcbiAqIGBjb2RlYCBmaWVsZCBzbyB0aGUgSFRUUCBsYXllciBjYW4gbWFwIGZhaWx1cmVzIHRvIGNvbnNpc3RlbnQgcmVzcG9uc2VcbiAqIGNvZGVzIHdpdGhvdXQgcGFyc2luZyBmcmVlLWZvcm0gZXJyb3IgbWVzc2FnZXMuIENhbGxlcnMgKENMSSwgVUkpIGNhblxuICogYnJhbmNoIG9uIGBjb2RlYCBpbnN0ZWFkIG9mIHRoZSBodW1hbi1yZWFkYWJsZSBgbWVzc2FnZWAuXG4gKi9cbmV4cG9ydCBjbGFzcyBUb3RwRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2U6IHN0cmluZywgcHVibGljIHJlYWRvbmx5IGNvZGU6IFRvdHBFcnJvckNvZGUpIHtcbiAgICBzdXBlcihtZXNzYWdlKTtcbiAgICB0aGlzLm5hbWUgPSAnVG90cEVycm9yJztcbiAgfVxufVxuXG4vKiogRGlzY3JpbWluYXRlZCBzZXQgb2YgVE9UUCBmYWlsdXJlIHJlYXNvbnMgc3VyZmFjZWQgYnkgdGhlIHN0b3JlLiAqL1xuZXhwb3J0IHR5cGUgVG90cEVycm9yQ29kZSA9XG4gIHwgJ0FMUkVBRFlfRU5ST0xMRUQnXG4gIHwgJ05PVF9FTlJPTExFRCdcbiAgfCAnUEVORElOR19OT1RfRk9VTkQnXG4gIHwgJ0lOVkFMSURfVE9UUF9DT0RFJ1xuICB8ICdTVE9SRV9OT1RfSU5JVElBTElaRUQnXG4gIHwgJ1RPT19NQU5ZX1BFTkRJTkcnXG4gIHwgJ1RPVFBfUkVRVUlSRUQnO1xuXG4vKipcbiAqIFRoZSBmdWxsIG9uLWRpc2sgdG9rZW4gZmlsZSBzdHJ1Y3R1cmUuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQ29uc29sZVRva2VuRmlsZSB7XG4gIHZlcnNpb246IHR5cGVvZiBUT0tFTl9GSUxFX1ZFUlNJT047XG4gIHRva2VuczogQ29uc29sZVRva2VuRW50cnlbXTtcbiAgdG90cDogVG90cFN0YXRlO1xufVxuXG4vKipcbiAqIEEgc2FmZS10by1sb2cgdmlldyBvZiBhIHRva2VuIGVudHJ5IOKAlCB0aGUgc2VjcmV0IGB0b2tlbmAgZmllbGQgaXNcbiAqIHJlcGxhY2VkIHdpdGggYSBtYXNrZWQgcHJldmlldyBzbyBpdCBuZXZlciBhcHBlYXJzIGluIGxvZ3Mgb3IgQVBJIHJlc3BvbnNlcy5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBNYXNrZWRUb2tlbkVudHJ5IGV4dGVuZHMgT21pdDxDb25zb2xlVG9rZW5FbnRyeSwgJ3Rva2VuJz4ge1xuICB0b2tlblByZXZpZXc6IHN0cmluZztcbn1cblxuLyoqXG4gKiBHZW5lcmF0ZSBhIGNyeXB0b2dyYXBoaWNhbGx5IHJhbmRvbSB0b2tlbi5cbiAqIFJldHVybnMgNjQgaGV4IGNoYXJhY3RlcnMgKDI1NiBiaXRzIG9mIGVudHJvcHkpLlxuICovXG5mdW5jdGlvbiBnZW5lcmF0ZVRva2VuVmFsdWUoKTogc3RyaW5nIHtcbiAgcmV0dXJuIHJhbmRvbUJ5dGVzKFRPS0VOX0JZVEVTKS50b1N0cmluZygnaGV4Jyk7XG59XG5cbi8qKlxuICogTWFzayBhIHRva2VuIGZvciBkaXNwbGF5IOKAlCBzaG93cyBmaXJzdCA4IGNoYXJzIG9ubHkuXG4gKi9cbmZ1bmN0aW9uIG1hc2tUb2tlbih0b2tlbjogc3RyaW5nKTogc3RyaW5nIHtcbiAgaWYgKHRva2VuLmxlbmd0aCA8PSA4KSByZXR1cm4gJ+KAouKAouKAouKAouKAouKAouKAouKAoic7XG4gIHJldHVybiBgJHt0b2tlbi5zbGljZSgwLCA4KX0keyfigKInLnJlcGVhdChNYXRoLm1pbig1NiwgdG9rZW4ubGVuZ3RoIC0gOCkpfWA7XG59XG5cbi8qKlxuICogQnVpbGQgYSBodW1hbi1yZWFkYWJsZSBkZWZhdWx0IG5hbWUgZnJvbSBhIHB1cHBldCBuYW1lIGFuZCB0aGUgbWFjaGluZSBob3N0bmFtZS5cbiAqIEV4YW1wbGU6IFwiS2VybWl0IG9uIG1pY2stTWFjQm9vay1Qcm9cIi5cbiAqXG4gKiBUaGUgcHVwcGV0IG5hbWUgaXMgcGFzc2VkIGluIHJhdGhlciB0aGFuIGltcG9ydGVkIHRvIGF2b2lkIGEgY2lyY3VsYXIgZGVwZW5kZW5jeVxuICogd2l0aCBTZXNzaW9uTmFtZXMgKHdoaWNoIG9ubHkgZ2VuZXJhdGVzIHBlci1wcm9jZXNzIG5hbWVzKS5cbiAqL1xuZnVuY3Rpb24gZGVmYXVsdFRva2VuTmFtZShwdXBwZXROYW1lOiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBob3N0ID0gaG9zdG5hbWUoKSB8fCAnbG9jYWxob3N0JztcbiAgcmV0dXJuIGAke3B1cHBldE5hbWV9IG9uICR7aG9zdH1gO1xufVxuXG4vKipcbiAqIEdlbmVyYXRlIGEgYmF0Y2ggb2YgcmFuZG9tIGJhY2t1cCBjb2Rlcy4gRWFjaCBjb2RlIGlzIGBCQUNLVVBfQ09ERV9MRU5HVEhgXG4gKiBjaGFyYWN0ZXJzIGRyYXduIHVuaWZvcm1seSBmcm9tIGBCQUNLVVBfQ09ERV9BTFBIQUJFVGAgKENyb2NrZm9yZCBiYXNlMzIsXG4gKiAzMiBjaGFyYWN0ZXJzLCA1IGJpdHMgcGVyIGNoYXIpLiBVc2VzIHJlamVjdGlvbiBzYW1wbGluZyBhZ2FpbnN0IDI1Ni1iaXRcbiAqIHJhbmRvbSBieXRlcyB0byBhdm9pZCBtb2R1bG8gYmlhcy5cbiAqL1xuZnVuY3Rpb24gZ2VuZXJhdGVCYWNrdXBDb2RlcygpOiBzdHJpbmdbXSB7XG4gIC8vIDMyLWNoYXIgYWxwaGFiZXQgZGl2aWRlcyAyNTYgZXZlbmx5ICgyNTYgLyAzMiA9IDgpLCBzbyB0aGUgc2ltcGxlXG4gIC8vIG1vZC0zMiBtYXBwaW5nIGhhcyBubyBiaWFzLiBHZW5lcmF0ZSBvbmUgYnl0ZSBwZXIgY2hhcmFjdGVyLlxuICBjb25zdCBjb2Rlczogc3RyaW5nW10gPSBbXTtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBCQUNLVVBfQ09ERV9DT1VOVDsgaSsrKSB7XG4gICAgY29uc3QgYnl0ZXMgPSByYW5kb21CeXRlcyhCQUNLVVBfQ09ERV9MRU5HVEgpO1xuICAgIGxldCBjb2RlID0gJyc7XG4gICAgZm9yIChsZXQgaiA9IDA7IGogPCBCQUNLVVBfQ09ERV9MRU5HVEg7IGorKykge1xuICAgICAgY29kZSArPSBCQUNLVVBfQ09ERV9BTFBIQUJFVFtieXRlc1tqXSAmIDB4MWZdO1xuICAgIH1cbiAgICBjb2Rlcy5wdXNoKGNvZGUpO1xuICB9XG4gIHJldHVybiBjb2Rlcztcbn1cblxuLyoqXG4gKiBIYXNoIGEgYmFja3VwIGNvZGUgZm9yIHN0b3JhZ2UuIHNoYTI1NiBoZXggaXMgcGxlbnR5IOKAlCB0aGVzZSBjb2RlcyBhcmVcbiAqIGhpZ2gtZW50cm9weSAoNDAgYml0cykgYW5kIHdlIG9ubHkgbmVlZCB0byBkZXRlY3QgYSB0YW1wZXIsIG5vdCByZXNpc3RcbiAqIHBhc3N3b3JkLWNyYWNraW5nIG9uIGEgbGVha2VkIGhhc2guXG4gKi9cbmZ1bmN0aW9uIGhhc2hCYWNrdXBDb2RlKGNvZGU6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiBjcmVhdGVIYXNoKCdzaGEyNTYnKS51cGRhdGUoY29kZSwgJ3V0ZjgnKS5kaWdlc3QoJ2hleCcpO1xufVxuXG4vKipcbiAqIE5vcm1hbGl6ZSBhIHVzZXItZW50ZXJlZCBiYWNrdXAgY29kZSBiZWZvcmUgaGFzaGluZzogdXBwZXJjYXNlLCBzdHJpcFxuICogd2hpdGVzcGFjZSwgc3RyaXAgZGFzaGVzICh1c2VycyBvZnRlbiB0eXBlIGNvZGVzIGluIGdyb3VwcyBsaWtlXG4gKiBcIlhYWFgtWFhYWFwiKS4gUmV0dXJucyB0aGUgY2Fub25pY2FsIGZvcm0gdGhhdCBtYXRjaGVzIHdoYXQgd2Ugc3RvcmVkLlxuICovXG5mdW5jdGlvbiBub3JtYWxpemVCYWNrdXBDb2RlKHJhdzogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIHJhdy5yZXBsYWNlQWxsKC9bXFxzLV0vZywgJycpLnRvVXBwZXJDYXNlKCk7XG59XG5cbi8qKlxuICogQnVpbGQgdGhlIGZ1bGwgb3RwYXV0aDovLyBVUkkgZm9yIGEgZ2l2ZW4gc2VjcmV0IGFuZCBkaXNwbGF5IGxhYmVsLlxuICogVGhlIGxhYmVsIGlzIFVSSS1lbmNvZGVkIGJ5IGBvdHBhdXRoYCBpbnRlcm5hbGx5IHZpYSBVUklDb21wb25lbnQuXG4gKi9cbmZ1bmN0aW9uIGJ1aWxkVG90cFVyaShzZWNyZXQ6IFNlY3JldCwgbGFiZWw6IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IHRvdHAgPSBuZXcgVE9UUCh7XG4gICAgaXNzdWVyOiBUT1RQX0lTU1VFUixcbiAgICBsYWJlbCxcbiAgICBhbGdvcml0aG06IFRPVFBfQUxHT1JJVEhNLFxuICAgIGRpZ2l0czogVE9UUF9ESUdJVFMsXG4gICAgcGVyaW9kOiBUT1RQX1BFUklPRF9TRUNPTkRTLFxuICAgIHNlY3JldCxcbiAgfSk7XG4gIHJldHVybiB0b3RwLnRvU3RyaW5nKCk7XG59XG5cbi8qKlxuICogVmFsaWRhdGUgYSBzaW5nbGUgdG9rZW4gZW50cnkgb2JqZWN0LiBSZXR1cm5zIHRydWUgaWYgdGhlIGVudHJ5IGhhcyBhbGxcbiAqIHJlcXVpcmVkIGZpZWxkcyB3aXRoIHRoZSBjb3JyZWN0IHR5cGVzLiBFeHRyYWN0ZWQgZnJvbSB2YWxpZGF0ZVRva2VuRmlsZVxuICogdG8ga2VlcCB0aGUgdG9wLWxldmVsIHZhbGlkYXRvcidzIGNvZ25pdGl2ZSBjb21wbGV4aXR5IG1hbmFnZWFibGUuXG4gKi9cbmZ1bmN0aW9uIGlzVmFsaWRUb2tlbkVudHJ5KHJhdzogdW5rbm93bik6IGJvb2xlYW4ge1xuICBpZiAoIXJhdyB8fCB0eXBlb2YgcmF3ICE9PSAnb2JqZWN0JykgcmV0dXJuIGZhbHNlO1xuICBjb25zdCBlID0gcmF3IGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICByZXR1cm4gKFxuICAgIHR5cGVvZiBlLmlkID09PSAnc3RyaW5nJyAmJiBlLmlkLmxlbmd0aCA+IDAgJiZcbiAgICB0eXBlb2YgZS5uYW1lID09PSAnc3RyaW5nJyAmJlxuICAgIHR5cGVvZiBlLnRva2VuID09PSAnc3RyaW5nJyAmJiBlLnRva2VuLmxlbmd0aCA+IDAgJiZcbiAgICB0eXBlb2YgZS5raW5kID09PSAnc3RyaW5nJyAmJlxuICAgIEFycmF5LmlzQXJyYXkoZS5zY29wZXMpICYmXG4gICAgdHlwZW9mIGUuY3JlYXRlZEF0ID09PSAnc3RyaW5nJ1xuICApO1xufVxuXG4vKipcbiAqIFZhbGlkYXRlIHRoYXQgYSBwYXJzZWQgSlNPTiBvYmplY3QgY29uZm9ybXMgdG8gdGhlIGV4cGVjdGVkIHRva2VuIGZpbGUgc2NoZW1hLlxuICogUmV0dXJucyBhIHR5cGVkIENvbnNvbGVUb2tlbkZpbGUgb3IgbnVsbCBpZiBpbnZhbGlkLlxuICpcbiAqIFN0cmljdCB2YWxpZGF0aW9uIOKAlCBhbiB1bnJlY29nbml6ZWQgdmVyc2lvbiBvciBtaXNzaW5nIHJlcXVpcmVkIGZpZWxkc1xuICogY2F1c2VzIHRoZSBmaWxlIHRvIGJlIHRyZWF0ZWQgYXMgY29ycnVwdCBzbyBhIGZyZXNoIG9uZSBjYW4gYmUgd3JpdHRlbi5cbiAqL1xuZnVuY3Rpb24gdmFsaWRhdGVUb2tlbkZpbGUocmF3OiB1bmtub3duKTogQ29uc29sZVRva2VuRmlsZSB8IG51bGwge1xuICBpZiAoIXJhdyB8fCB0eXBlb2YgcmF3ICE9PSAnb2JqZWN0JykgcmV0dXJuIG51bGw7XG4gIGNvbnN0IG9iaiA9IHJhdyBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcblxuICBpZiAob2JqLnZlcnNpb24gIT09IFRPS0VOX0ZJTEVfVkVSU0lPTikgcmV0dXJuIG51bGw7XG4gIGlmICghQXJyYXkuaXNBcnJheShvYmoudG9rZW5zKSkgcmV0dXJuIG51bGw7XG4gIGlmICghb2JqLnRvdHAgfHwgdHlwZW9mIG9iai50b3RwICE9PSAnb2JqZWN0JykgcmV0dXJuIG51bGw7XG4gIGlmICghb2JqLnRva2Vucy5ldmVyeShpc1ZhbGlkVG9rZW5FbnRyeSkpIHJldHVybiBudWxsO1xuXG4gIHJldHVybiByYXcgYXMgQ29uc29sZVRva2VuRmlsZTtcbn1cblxuLyoqXG4gKiBTdGF0ZWZ1bCBzdG9yZSB0aGF0IG93bnMgdGhlIGNvbnNvbGUgdG9rZW4gZmlsZSBhbmQgdmVyaWZpZXMgcHJlc2VudGVkIHRva2Vucy5cbiAqXG4gKiBEZXNpZ25lZCB0byBsaXZlIG9uIHRoZSBsZWFkZXIgcHJvY2Vzcy4gRm9sbG93ZXJzIHNob3VsZCBub3QgY29uc3RydWN0IHRoaXMg4oCUXG4gKiB0aGV5IHJlYWQgdGhlIGZpbGUgZGlyZWN0bHkgdmlhIGByZWFkVG9rZW5GaWxlUmF3KClgIGZvciB0aGVpciBvd24gSFRUUCBjYWxscy5cbiAqL1xuZXhwb3J0IGNsYXNzIENvbnNvbGVUb2tlblN0b3JlIHtcbiAgcHJpdmF0ZSByZWFkb25seSBmaWxlUGF0aDogc3RyaW5nO1xuICBwcml2YXRlIGRhdGE6IENvbnNvbGVUb2tlbkZpbGUgfCBudWxsID0gbnVsbDtcbiAgLyoqXG4gICAqIFByZS1jb252ZXJ0ZWQgQnVmZmVyIGNhY2hlIGtleWVkIGJ5IGVudHJ5IGlkLiBQb3B1bGF0ZWQgd2hlbmV2ZXIgYHRoaXMuZGF0YWBcbiAgICogaXMgYXNzaWduZWQgKGxvYWQsIGNyZWF0ZSwgZnV0dXJlIHJvdGF0aW9uKS4gVmVyaWZ5KCkgcmV1c2VzIHRoZSBzdG9yZWRcbiAgICogYnVmZmVycyBzbyB0aGUgaG90IHBhdGggZG9lc24ndCByZS1hbGxvY2F0ZSBwZXItdG9rZW4gb24gZXZlcnkgcmVxdWVzdC5cbiAgICogTmVnbGlnaWJsZSB3aW4gd2l0aCAxIHRva2VuIHRvZGF5OyBtZWFuaW5nZnVsIHdpdGggUGhhc2UgMiBtdWx0aS10b2tlblxuICAgKiBsb29rdXBzLiBOb3Qgc2VyaWFsaXplZCDigJQgYnVmZmVycyBhcmUgbmV2ZXIgd3JpdHRlbiB0byBkaXNrLlxuICAgKi9cbiAgcHJpdmF0ZSByZWFkb25seSB0b2tlbkJ1ZmZlcnMgPSBuZXcgTWFwPHN0cmluZywgQnVmZmVyPigpO1xuICAvKipcbiAgICogSW4tbWVtb3J5IHBlbmRpbmcgVE9UUCBlbnJvbGxtZW50cywga2V5ZWQgYnkgb3BhcXVlIHBlbmRpbmdJZC4gTm90aGluZ1xuICAgKiBsaXZlcyBvbiBkaXNrIHVudGlsIGNvbmZpcm1Ub3RwRW5yb2xsbWVudCgpIHN1Y2NlZWRzLCB3aGljaCBsaW1pdHMgdGhlXG4gICAqIHdpbmRvdyBpbiB3aGljaCBhIGhhbGYtY29tcGxldGVkIGVucm9sbG1lbnQgbGVha3MgYSBzZWNyZXQgdmlhIGZpbGUgcmVhZC5cbiAgICogRW50cmllcyBleHBpcmUgYWZ0ZXIgVE9UUF9QRU5ESU5HX1RUTF9NUyAoIzE3OTQpLlxuICAgKi9cbiAgcHJpdmF0ZSByZWFkb25seSBwZW5kaW5nRW5yb2xsbWVudHMgPSBuZXcgTWFwPHN0cmluZywgUGVuZGluZ0Vucm9sbG1lbnQ+KCk7XG4gIC8qKlxuICAgKiBJbi1tZW1vcnkgZ3JhY2UgYnVmZmVyIGZvciByZWNlbnRseS1yb3RhdGVkIHRva2VucyAoIzE3OTUpLiBBZnRlciBhXG4gICAqIHJvdGF0aW9uLCB0aGUgb2xkIHRva2VuIHZhbHVlIGlzIHN0YXNoZWQgaGVyZSB3aXRoIGEgcGVyLXJvdGF0aW9uIGV4cGlyeVxuICAgKiBzbyBpbi1mbGlnaHQgcmVxdWVzdHMgdGhhdCB3ZXJlIHNlbnQgYmVmb3JlIHRoZSByb3RhdGlvbiByZXNwb25zZSBhcnJpdmVkXG4gICAqIHN0aWxsIGF1dGhlbnRpY2F0ZS4gRW50cmllcyBhcmUgcGVyLXJvdGF0aW9uIChjb25jdXJyZW50IHJvdGF0aW9ucyBlYWNoXG4gICAqIGdldCB0aGVpciBvd24gZ3JhY2Ugc2xvdCkgYW5kIG5ldmVyIHBlcnNpc3RlZCB0byBkaXNrLlxuICAgKi9cbiAgcHJpdmF0ZSByZWFkb25seSBncmFjZUVudHJpZXM6IEdyYWNlRW50cnlbXSA9IFtdO1xuXG4gIGNvbnN0cnVjdG9yKGZpbGVQYXRoOiBzdHJpbmcgPSBERUZBVUxUX1RPS0VOX0ZJTEUpIHtcbiAgICB0aGlzLmZpbGVQYXRoID0gZmlsZVBhdGg7XG4gIH1cblxuICAvKipcbiAgICogUmVidWlsZCB0aGUgdG9rZW4gYnVmZmVyIGNhY2hlIGFmdGVyIGEgZGF0YSBsb2FkLCBjcmVhdGUsIG9yIG11dGF0aW9uLlxuICAgKiBLZWVwcyB0aGUgaG90IHZlcmlmeSgpIHBhdGggYWxsb2NhdGlvbi1mcmVlIGZvciB0aGUgc3RvcmVkIHNpZGUuXG4gICAqL1xuICBwcml2YXRlIHJlYnVpbGRUb2tlbkJ1ZmZlcnMoKTogdm9pZCB7XG4gICAgdGhpcy50b2tlbkJ1ZmZlcnMuY2xlYXIoKTtcbiAgICBpZiAoIXRoaXMuZGF0YSkgcmV0dXJuO1xuICAgIGZvciAoY29uc3QgZW50cnkgb2YgdGhpcy5kYXRhLnRva2Vucykge1xuICAgICAgdGhpcy50b2tlbkJ1ZmZlcnMuc2V0KGVudHJ5LmlkLCBCdWZmZXIuZnJvbShlbnRyeS50b2tlbiwgJ3V0ZjgnKSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJlYWQgdGhlIGV4aXN0aW5nIHRva2VuIGZpbGUsIG9yIGNyZWF0ZSBhIG5ldyBvbmUgd2l0aCBhIHNpbmdsZSBpbml0aWFsXG4gICAqIHRva2VuIGlmIG5vbmUgZXhpc3RzLiBJZGVtcG90ZW50IOKAlCBzYWZlIHRvIGNhbGwgb24gZXZlcnkgbGVhZGVyIGVsZWN0aW9uLlxuICAgKlxuICAgKiBAcGFyYW0gcHVwcGV0TmFtZSAtIEEgcHVwcGV0IG5hbWUgcGlja2VkIGJ5IHRoZSBjYWxsZXIgKGUuZy4gZnJvbSBTZXNzaW9uTmFtZXMpXG4gICAqICAgICAgICAgICAgICAgICAgICAgdXNlZCB0byBidWlsZCB0aGUgZGVmYXVsdCBkaXNwbGF5IG5hbWUgb24gZmlyc3QgcnVuLlxuICAgKiBAcmV0dXJucyBUaGUgcHJpbWFyeSAoZmlyc3QpIHRva2VuIGVudHJ5IOKAlCBjb252ZW5pZW50IGZvciBzZXJ2ZXIgc3RhcnR1cFxuICAgKiAgICAgICAgICB0byBpbmplY3QgaW50byBIVE1MIGFuZCBzdGFtcCBvbiBmb2xsb3dlcnMuXG4gICAqL1xuICBhc3luYyBlbnN1cmVJbml0aWFsaXplZChwdXBwZXROYW1lOiBzdHJpbmcpOiBQcm9taXNlPENvbnNvbGVUb2tlbkVudHJ5PiB7XG4gICAgY29uc3QgcmVhZFJlc3VsdCA9IGF3YWl0IHRoaXMucmVhZFdpdGhTdGF0dXMoKTtcbiAgICBpZiAocmVhZFJlc3VsdC5zdGF0dXMgPT09ICdvaycgJiYgcmVhZFJlc3VsdC5kYXRhLnRva2Vucy5sZW5ndGggPiAwKSB7XG4gICAgICB0aGlzLmRhdGEgPSByZWFkUmVzdWx0LmRhdGE7XG4gICAgICB0aGlzLnJlYnVpbGRUb2tlbkJ1ZmZlcnMoKTtcbiAgICAgIGxvZ2dlci5kZWJ1ZygnW0NvbnNvbGVUb2tlbl0gTG9hZGVkIGV4aXN0aW5nIHRva2VuIGZpbGUnLCB7XG4gICAgICAgIHBhdGg6IHRoaXMuZmlsZVBhdGgsXG4gICAgICAgIGNvdW50OiByZWFkUmVzdWx0LmRhdGEudG9rZW5zLmxlbmd0aCxcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuIHJlYWRSZXN1bHQuZGF0YS50b2tlbnNbMF07XG4gICAgfVxuXG4gICAgLy8gSWYgdGhlIGZpbGUgZXhpc3RlZCBidXQgd2FzIGNvcnJ1cHQsIGJhY2sgaXQgdXAgYmVmb3JlIG92ZXJ3cml0aW5nLlxuICAgIC8vIFVzZXJzIG1heSBoYXZlIGhhbmQtZWRpdGVkIHRoZSBmaWxlIHdpdGggY3VzdG9tIG5hbWVzL2xhYmVscyDigJQgZG9uJ3RcbiAgICAvLyBkZXN0cm95IHRoZWlyIGRhdGEgc2lsZW50bHkuIEEgdGltZXN0YW1wZWQgY29weSBsZXRzIHRoZW0gcmVjb3Zlci5cbiAgICBpZiAocmVhZFJlc3VsdC5zdGF0dXMgPT09ICdjb3JydXB0Jykge1xuICAgICAgYXdhaXQgdGhpcy5iYWNrdXBDb3JydXB0RmlsZSgpO1xuICAgIH1cblxuICAgIC8vIENyZWF0ZSBhIGZyZXNoIGZpbGUgd2l0aCBvbmUgaW5pdGlhbCB0b2tlblxuICAgIGNvbnN0IG5vdyA9IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKTtcbiAgICBjb25zdCBpbml0aWFsOiBDb25zb2xlVG9rZW5FbnRyeSA9IHtcbiAgICAgIGlkOiByYW5kb21VVUlEKCksXG4gICAgICBuYW1lOiBkZWZhdWx0VG9rZW5OYW1lKHB1cHBldE5hbWUpLFxuICAgICAga2luZDogJ2NvbnNvbGUnLFxuICAgICAgdG9rZW46IGdlbmVyYXRlVG9rZW5WYWx1ZSgpLFxuICAgICAgc2NvcGVzOiBbJ2FkbWluJ10sXG4gICAgICBlbGVtZW50Qm91bmRhcmllczogbnVsbCxcbiAgICAgIHRlbmFudDogbnVsbCxcbiAgICAgIHBsYXRmb3JtOiAnbG9jYWwnLFxuICAgICAgbGFiZWxzOiB7fSxcbiAgICAgIGNyZWF0ZWRBdDogbm93LFxuICAgICAgbGFzdFVzZWRBdDogbnVsbCxcbiAgICAgIGNyZWF0ZWRWaWE6ICdpbml0aWFsLXNldHVwJyxcbiAgICB9O1xuXG4gICAgY29uc3QgZmlsZTogQ29uc29sZVRva2VuRmlsZSA9IHtcbiAgICAgIHZlcnNpb246IFRPS0VOX0ZJTEVfVkVSU0lPTixcbiAgICAgIHRva2VuczogW2luaXRpYWxdLFxuICAgICAgdG90cDogeyBlbnJvbGxlZDogZmFsc2UsIHNlY3JldDogbnVsbCwgYmFja3VwQ29kZXM6IFtdLCBlbnJvbGxlZEF0OiBudWxsIH0sXG4gICAgfTtcblxuICAgIGF3YWl0IHRoaXMud3JpdGUoZmlsZSk7XG4gICAgdGhpcy5kYXRhID0gZmlsZTtcbiAgICB0aGlzLnJlYnVpbGRUb2tlbkJ1ZmZlcnMoKTtcbiAgICBsb2dnZXIuaW5mbygnW0NvbnNvbGVUb2tlbl0gQ3JlYXRlZCBuZXcgdG9rZW4gZmlsZScsIHtcbiAgICAgIHBhdGg6IHRoaXMuZmlsZVBhdGgsXG4gICAgICBpZDogaW5pdGlhbC5pZCxcbiAgICAgIG5hbWU6IGluaXRpYWwubmFtZSxcbiAgICB9KTtcbiAgICByZXR1cm4gaW5pdGlhbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBWZXJpZnkgYSBwcmVzZW50ZWQgQmVhcmVyIHRva2VuIGFnYWluc3QgdGhlIHN0b3JlZCBlbnRyaWVzLlxuICAgKiBVc2VzIHRpbWluZy1zYWZlIGNvbXBhcmlzb24gdG8gcHJldmVudCBzaWRlLWNoYW5uZWwgYXR0YWNrcy5cbiAgICpcbiAgICogVXBkYXRlcyBgbGFzdFVzZWRBdGAgb24gdGhlIG1hdGNoZWQgZW50cnkgKGluIG1lbW9yeSBvbmx5OyBkaXNrIHdyaXRlXG4gICAqIGlzIGRlYm91bmNlZCB0byBhdm9pZCBkaXNrIHRocmFzaCBvbiBldmVyeSByZXF1ZXN0IOKAlCBQaGFzZSAyIGZlYXR1cmUpLlxuICAgKlxuICAgKiBAcmV0dXJucyBUaGUgbWF0Y2hpbmcgZW50cnksIG9yIG51bGwgaWYgbm8gbWF0Y2guXG4gICAqL1xuICB2ZXJpZnkocHJlc2VudGVkOiBzdHJpbmcpOiBDb25zb2xlVG9rZW5FbnRyeSB8IG51bGwge1xuICAgIGlmICghdGhpcy5kYXRhIHx8ICFwcmVzZW50ZWQpIHJldHVybiBudWxsO1xuXG4gICAgLy8gRE1DUC1TRUMtMDA0OiBOb3JtYWxpemUgdGhlIHByZXNlbnRlZCB0b2tlbiB0byBORkMgYW5kIHZhbGlkYXRlIHRoZVxuICAgIC8vIHN0cmljdCBoZXggZm9ybWF0IGJlZm9yZSBhbnkgY29tcGFyaXNvbi4gVGhpcyBibG9ja3MgVW5pY29kZSBhYnVzZVxuICAgIC8vIChob21vZ3JhcGhzLCB6ZXJvLXdpZHRoLCBiaWRpIG92ZXJyaWRlcykgZnJvbSByZWFjaGluZyB0aW1pbmdTYWZlRXF1YWwuXG4gICAgLy8gRGVmZW5zZS1pbi1kZXB0aCDigJQgdGhlIG1pZGRsZXdhcmUgYWxyZWFkeSBzYW5pdGl6ZXMsIGJ1dCB2ZXJpZnkoKSBpcyBhXG4gICAgLy8gcHVibGljIEFQSSB0aGF0IGFueSBmdXR1cmUgY2FsbGVyIGNvdWxkIGludm9rZSBkaXJlY3RseS5cbiAgICBjb25zdCBub3JtYWxpemVkID0gVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUocHJlc2VudGVkKS5ub3JtYWxpemVkQ29udGVudDtcbiAgICBpZiAoIVRPS0VOX0ZPUk1BVC50ZXN0KG5vcm1hbGl6ZWQpKSByZXR1cm4gbnVsbDtcblxuICAgIC8vIE9ubHkgdGhlIHByZXNlbnRlZCBzaWRlIGlzIGFsbG9jYXRlZCBwZXItcmVxdWVzdDsgc3RvcmVkIGJ1ZmZlcnMgYXJlXG4gICAgLy8gcHJlLWNvbnZlcnRlZCBpbiB0aGUgdG9rZW5CdWZmZXJzIGNhY2hlIHNvIHRoZSBob3QgbG9vcCBpcyBhbGxvY2F0aW9uLWZyZWUuXG4gICAgY29uc3QgcHJlc2VudGVkQnVmID0gQnVmZmVyLmZyb20obm9ybWFsaXplZCwgJ3V0ZjgnKTtcblxuICAgIGZvciAoY29uc3QgZW50cnkgb2YgdGhpcy5kYXRhLnRva2Vucykge1xuICAgICAgY29uc3Qgc3RvcmVkQnVmID0gdGhpcy50b2tlbkJ1ZmZlcnMuZ2V0KGVudHJ5LmlkKTtcbiAgICAgIGlmICghc3RvcmVkQnVmIHx8IHN0b3JlZEJ1Zi5sZW5ndGggIT09IHByZXNlbnRlZEJ1Zi5sZW5ndGgpIGNvbnRpbnVlO1xuICAgICAgaWYgKHRpbWluZ1NhZmVFcXVhbChwcmVzZW50ZWRCdWYsIHN0b3JlZEJ1ZikpIHtcbiAgICAgICAgZW50cnkubGFzdFVzZWRBdCA9IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKTtcbiAgICAgICAgcmV0dXJuIGVudHJ5O1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIENoZWNrIGdyYWNlIGJ1ZmZlciDigJQgcmVjZW50bHktcm90YXRlZCB0b2tlbnMgdGhhdCBoYXZlbid0IGV4cGlyZWQgeWV0LlxuICAgIC8vIFJldHVybnMgdGhlIHByaW1hcnkgZW50cnkgb24gbWF0Y2ggc28gdGhlIGNhbGxlciBnZXRzIHRoZSBzYW1lIGFkbWluXG4gICAgLy8gc2hhcGU7IHRoZSBncmFjZSB3aW5kb3cganVzdCBleHRlbmRzIHRoZSBhdXRoZW50aWNhdGlvbiBwZXJpb2QuXG4gICAgdGhpcy5zd2VlcEV4cGlyZWRHcmFjZUVudHJpZXMoKTtcbiAgICBmb3IgKGNvbnN0IGdyYWNlIG9mIHRoaXMuZ3JhY2VFbnRyaWVzKSB7XG4gICAgICBpZiAoZ3JhY2UuYnVmLmxlbmd0aCA9PT0gcHJlc2VudGVkQnVmLmxlbmd0aCAmJiB0aW1pbmdTYWZlRXF1YWwocHJlc2VudGVkQnVmLCBncmFjZS5idWYpKSB7XG4gICAgICAgIC8vIFRvdWNoIHRoZSBwcmltYXJ5IGVudHJ5IOKAlCB0aGUgcmVxdWVzdCBpcyBzdGlsbCBcInVzaW5nXCIgdGhpcyB0b2tlbiBzbG90LlxuICAgICAgICBpZiAodGhpcy5kYXRhLnRva2Vucy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgdGhpcy5kYXRhLnRva2Vuc1swXS5sYXN0VXNlZEF0ID0gbmV3IERhdGUoKS50b0lTT1N0cmluZygpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzLmRhdGEudG9rZW5zWzBdID8/IG51bGw7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSBwcmltYXJ5IHRva2VuIHZhbHVlIGZvciBpbmplY3Rpb24gaW50byBIVE1MIG9yIGZvcndhcmRlciBjb25maWcuXG4gICAqIFJldHVybnMgdGhlIGZpcnN0IGVudHJ5J3MgdG9rZW4gc3RyaW5nLCBvciBudWxsIGlmIHVuaW5pdGlhbGl6ZWQuXG4gICAqL1xuICBnZXRQcmltYXJ5VG9rZW5WYWx1ZSgpOiBzdHJpbmcgfCBudWxsIHtcbiAgICBpZiAoIXRoaXMuZGF0YSB8fCB0aGlzLmRhdGEudG9rZW5zLmxlbmd0aCA9PT0gMCkgcmV0dXJuIG51bGw7XG4gICAgcmV0dXJuIHRoaXMuZGF0YS50b2tlbnNbMF0udG9rZW47XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJuIGFsbCB0b2tlbnMgd2l0aCB0aGUgc2VjcmV0IHZhbHVlIG1hc2tlZCDigJQgc2FmZSB0byBzZXJpYWxpemUgZm9yXG4gICAqIHRoZSBTZWN1cml0eSB0YWIgVUkgb3IgYEdFVCAvYXBpL2NvbnNvbGUvdG9rZW4vaW5mb2AgcmVzcG9uc2VzLlxuICAgKi9cbiAgbGlzdE1hc2tlZCgpOiBNYXNrZWRUb2tlbkVudHJ5W10ge1xuICAgIGlmICghdGhpcy5kYXRhKSByZXR1cm4gW107XG4gICAgcmV0dXJuIHRoaXMuZGF0YS50b2tlbnMubWFwKCh7IHRva2VuLCAuLi5yZXN0IH0pID0+ICh7XG4gICAgICAuLi5yZXN0LFxuICAgICAgdG9rZW5QcmV2aWV3OiBtYXNrVG9rZW4odG9rZW4pLFxuICAgIH0pKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgdGhlIHBhdGggdG8gdGhlIHRva2VuIGZpbGUgb24gZGlzay5cbiAgICovXG4gIGdldEZpbGVQYXRoKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuZmlsZVBhdGg7XG4gIH1cblxuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAvLyBUT1RQIOKAlCBQaGFzZSAyICgjMTc5NClcbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuICAvKipcbiAgICogUmV0dXJucyBhIHNhZmUtdG8tc2VyaWFsaXplIHZpZXcgb2YgVE9UUCBlbnJvbGxtZW50IHN0YXRlLiBOZXZlciBsZWFrc1xuICAgKiB0aGUgc2VjcmV0IG9yIGFueSBiYWNrdXAgY29kZSBtYXRlcmlhbC5cbiAgICovXG4gIGdldFRvdHBTdGF0dXMoKTogVG90cFN0YXR1cyB7XG4gICAgY29uc3QgdG90cCA9IHRoaXMuZGF0YT8udG90cDtcbiAgICBpZiAoIXRvdHA/LmVucm9sbGVkKSB7XG4gICAgICByZXR1cm4geyBlbnJvbGxlZDogZmFsc2UsIGVucm9sbGVkQXQ6IG51bGwsIGJhY2t1cENvZGVzUmVtYWluaW5nOiAwIH07XG4gICAgfVxuICAgIHJldHVybiB7XG4gICAgICBlbnJvbGxlZDogdHJ1ZSxcbiAgICAgIGVucm9sbGVkQXQ6IHRvdHAuZW5yb2xsZWRBdCA/PyBudWxsLFxuICAgICAgYmFja3VwQ29kZXNSZW1haW5pbmc6IHRvdHAuYmFja3VwQ29kZXMubGVuZ3RoLFxuICAgIH07XG4gIH1cblxuICAvKiogQ29udmVuaWVuY2U6IHRydWUgaWYgdGhlIHVzZXIgaGFzIGEgY29uZmlybWVkIFRPVFAgc2VjcmV0LiAqL1xuICBpc1RvdHBFbnJvbGxlZCgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gQm9vbGVhbih0aGlzLmRhdGE/LnRvdHA/LmVucm9sbGVkICYmIHRoaXMuZGF0YS50b3RwLnNlY3JldCk7XG4gIH1cblxuICAvKipcbiAgICogQmVnaW4gYSBUT1RQIGVucm9sbG1lbnQuIEdlbmVyYXRlcyBhIGZyZXNoIHNlY3JldCwgaG9sZHMgaXQgaW4gdGhlXG4gICAqIGluLW1lbW9yeSBwZW5kaW5nIG1hcCwgYW5kIHJldHVybnMgdGhlIGRhdGEgdGhlIFVJIG5lZWRzIHRvIHJlbmRlciBhXG4gICAqIFFSIGNvZGUgYW5kIG1hbnVhbC1lbnRyeSBmYWxsYmFjay4gTm90aGluZyBpcyBwZXJzaXN0ZWQgdW50aWxcbiAgICogYGNvbmZpcm1Ub3RwRW5yb2xsbWVudGAgc3VjY2VlZHMuXG4gICAqXG4gICAqIENhbGxlcnMgbWF5IGNhbGwgdGhpcyBtdWx0aXBsZSB0aW1lczsgZWFjaCBjYWxsIHByb2R1Y2VzIGEgbmV3IHBlbmRpbmdJZFxuICAgKiBhbmQgc2VjcmV0LiBPbGQgcGVuZGluZyBlbnRyaWVzIGV4cGlyZSBhZnRlciBUT1RQX1BFTkRJTkdfVFRMX01TLlxuICAgKlxuICAgKiBAdGhyb3dzIEVycm9yIGlmIFRPVFAgaXMgYWxyZWFkeSBlbnJvbGxlZCDigJQgY2FsbGVycyBtdXN0IGRpc2FibGUgZmlyc3QuXG4gICAqL1xuICBiZWdpblRvdHBFbnJvbGxtZW50KGxhYmVsPzogc3RyaW5nKTogVG90cEVucm9sbG1lbnRCZWdpbiB7XG4gICAgaWYgKHRoaXMuaXNUb3RwRW5yb2xsZWQoKSkge1xuICAgICAgdGhyb3cgbmV3IFRvdHBFcnJvcihcbiAgICAgICAgJ1RPVFAgaXMgYWxyZWFkeSBlbnJvbGxlZCDigJQgZGlzYWJsZSBleGlzdGluZyBlbnJvbGxtZW50IGJlZm9yZSBlbnJvbGxpbmcgYWdhaW4nLFxuICAgICAgICAnQUxSRUFEWV9FTlJPTExFRCcsXG4gICAgICApO1xuICAgIH1cbiAgICB0aGlzLnN3ZWVwRXhwaXJlZEVucm9sbG1lbnRzKCk7XG4gICAgLy8gUmVqZWN0IGlmIHdlJ3JlIGFscmVhZHkgYXQgdGhlIHBlci1wcm9jZXNzIGNhcCBvbiBjb25jdXJyZW50IHBlbmRpbmdzLlxuICAgIC8vIFN3ZWVwIHJhbiBhYm92ZSwgc28gYW55IHNsb3Qgc3RpbGwgaGVsZCBpcyBhIGxpdmUsIHVuZXhwaXJlZCBlbnJvbGxtZW50XG4gICAgLy8gYmVsb25naW5nIHRvIHNvbWVib2R5IGVsc2Ug4oCUIGV2aWN0aW5nIGl0IGNvdWxkIHNpbGVudGx5IGtpbGwgdGhlaXIgZmxvdy5cbiAgICBpZiAodGhpcy5wZW5kaW5nRW5yb2xsbWVudHMuc2l6ZSA+PSBNQVhfUEVORElOR19FTlJPTExNRU5UUykge1xuICAgICAgdGhyb3cgbmV3IFRvdHBFcnJvcihcbiAgICAgICAgYFRvbyBtYW55IHBlbmRpbmcgZW5yb2xsbWVudHMgKG1heCAke01BWF9QRU5ESU5HX0VOUk9MTE1FTlRTfSk7IHdhaXQgZm9yIGV4aXN0aW5nIHBlbmRpbmdzIHRvIGV4cGlyZSBvciBiZSBjb25maXJtZWRgLFxuICAgICAgICAnVE9PX01BTllfUEVORElORycsXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIERlcml2ZSBhIGRpc3BsYXkgbGFiZWwgZnJvbSB0aGUgcHJpbWFyeSB0b2tlbiBuYW1lIGlmIHRoZSBjYWxsZXJcbiAgICAvLyBkaWRuJ3QgcHJvdmlkZSBvbmUuIEF1dGhlbnRpY2F0b3IgYXBwcyBzaG93IFwiPElzc3Vlcj46PGxhYmVsPlwiLCBzb1xuICAgIC8vIGluY2x1ZGluZyB0aGUgdG9rZW4gbmFtZSBnaXZlcyBtdWx0aS1kZXZpY2UgdXNlcnMgYSB3YXkgdG8gdGVsbFxuICAgIC8vIGVucm9sbG1lbnRzIGFwYXJ0LlxuICAgIGNvbnN0IGRpc3BsYXlMYWJlbCA9IGxhYmVsXG4gICAgICA/PyB0aGlzLmRhdGE/LnRva2Vuc1swXT8ubmFtZVxuICAgICAgPz8gJ2NvbnNvbGUnO1xuXG4gICAgY29uc3Qgc2VjcmV0ID0gbmV3IFNlY3JldCh7IHNpemU6IFRPVFBfU0VDUkVUX1NJWkVfQllURVMgfSk7XG4gICAgY29uc3QgcGVuZGluZ0lkID0gcmFuZG9tVVVJRCgpO1xuICAgIGNvbnN0IGV4cGlyZXNBdCA9IERhdGUubm93KCkgKyBUT1RQX1BFTkRJTkdfVFRMX01TO1xuICAgIHRoaXMucGVuZGluZ0Vucm9sbG1lbnRzLnNldChwZW5kaW5nSWQsIHtcbiAgICAgIHNlY3JldDogc2VjcmV0LmJhc2UzMixcbiAgICAgIGxhYmVsOiBkaXNwbGF5TGFiZWwsXG4gICAgICBleHBpcmVzQXQsXG4gICAgfSk7XG5cbiAgICBsb2dnZXIuZGVidWcoJ1tDb25zb2xlVG9rZW5dIFRPVFAgZW5yb2xsbWVudCBiZWd1bicsIHsgcGVuZGluZ0lkLCBsYWJlbDogZGlzcGxheUxhYmVsIH0pO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIHBlbmRpbmdJZCxcbiAgICAgIHNlY3JldDogc2VjcmV0LmJhc2UzMixcbiAgICAgIG90cGF1dGhVcmk6IGJ1aWxkVG90cFVyaShzZWNyZXQsIGRpc3BsYXlMYWJlbCksXG4gICAgICBleHBpcmVzQXQsXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb25maXJtIGEgcGVuZGluZyBUT1RQIGVucm9sbG1lbnQuIFZlcmlmaWVzIHRoZSBjb2RlIGFnYWluc3QgdGhlIHBlbmRpbmdcbiAgICogc2VjcmV0OyBvbiBzdWNjZXNzLCBnZW5lcmF0ZXMgMTAgcGxhaW50ZXh0IGJhY2t1cCBjb2RlcywgaGFzaGVzIHRoZW0gZm9yXG4gICAqIHN0b3JhZ2UsIGFuZCBwZXJzaXN0cyB0aGUgZW5yb2xsbWVudC4gUmV0dXJucyB0aGUgcGxhaW50ZXh0IGJhY2t1cCBjb2Rlc1xuICAgKiBleGFjdGx5IG9uY2Ug4oCUIHRoZSBjYWxsZXIgaXMgcmVzcG9uc2libGUgZm9yIHNob3dpbmcgdGhlbSB0byB0aGUgdXNlclxuICAgKiBhbmQgdGhlbiBkaXNjYXJkaW5nIHRoZW0uXG4gICAqXG4gICAqIFdyb25nIGNvZGVzIGRvIE5PVCBjb25zdW1lIG9yIGludmFsaWRhdGUgdGhlIHBlbmRpbmcgZW5yb2xsbWVudCDigJQgdGhlXG4gICAqIHVzZXIgY2FuIHJldHJ5IHVudGlsIGl0IGV4cGlyZXMuIFRoaXMgbWF0Y2hlcyB1c2VyIGV4cGVjdGF0aW9ucyBmb3JcbiAgICogXCJvb3BzLCB0eXBlZCB0aGUgd3JvbmcgY29kZVwiIGFuZCBsaW1pdHMgdGhlIGRhbWFnZSBmcm9tIGEgZmF0LWZpbmdlcmVkXG4gICAqIGZpcnN0IGF0dGVtcHQuXG4gICAqXG4gICAqIEB0aHJvd3MgRXJyb3IgaWYgcGVuZGluZ0lkIGlzIHVua25vd24sIGV4cGlyZWQsIG9yIGNvZGUgaW52YWxpZC5cbiAgICovXG4gIGFzeW5jIGNvbmZpcm1Ub3RwRW5yb2xsbWVudChwZW5kaW5nSWQ6IHN0cmluZywgY29kZTogc3RyaW5nKTogUHJvbWlzZTxUb3RwRW5yb2xsbWVudENvbmZpcm0+IHtcbiAgICBpZiAoIXRoaXMuZGF0YSkge1xuICAgICAgdGhyb3cgbmV3IFRvdHBFcnJvcignVG9rZW4gc3RvcmUgbm90IGluaXRpYWxpemVkJywgJ1NUT1JFX05PVF9JTklUSUFMSVpFRCcpO1xuICAgIH1cbiAgICB0aGlzLnN3ZWVwRXhwaXJlZEVucm9sbG1lbnRzKCk7XG4gICAgY29uc3QgcGVuZGluZyA9IHRoaXMucGVuZGluZ0Vucm9sbG1lbnRzLmdldChwZW5kaW5nSWQpO1xuICAgIGlmICghcGVuZGluZykge1xuICAgICAgdGhyb3cgbmV3IFRvdHBFcnJvcignUGVuZGluZyBlbnJvbGxtZW50IG5vdCBmb3VuZCBvciBleHBpcmVkJywgJ1BFTkRJTkdfTk9UX0ZPVU5EJyk7XG4gICAgfVxuXG4gICAgLy8gVmVyaWZ5IHRoZSBwcmVzZW50ZWQgY29kZSBhZ2FpbnN0IHRoZSBwZW5kaW5nIHNlY3JldC4gYHZhbGlkYXRlYCByZXR1cm5zXG4gICAgLy8gdGhlIHRpbWUtc3RlcCBkZWx0YSAoYSBudW1iZXIsIHBvc3NpYmx5IDApIG9uIG1hdGNoLCBvciBudWxsIG9uIG1pc21hdGNoLlxuICAgIGNvbnN0IHRvdHAgPSBuZXcgVE9UUCh7XG4gICAgICBpc3N1ZXI6IFRPVFBfSVNTVUVSLFxuICAgICAgbGFiZWw6IHBlbmRpbmcubGFiZWwsXG4gICAgICBhbGdvcml0aG06IFRPVFBfQUxHT1JJVEhNLFxuICAgICAgZGlnaXRzOiBUT1RQX0RJR0lUUyxcbiAgICAgIHBlcmlvZDogVE9UUF9QRVJJT0RfU0VDT05EUyxcbiAgICAgIHNlY3JldDogU2VjcmV0LmZyb21CYXNlMzIocGVuZGluZy5zZWNyZXQpLFxuICAgIH0pO1xuICAgIGNvbnN0IHNhbml0aXplZCA9IGNvZGUucmVwbGFjZUFsbCgvXFxzL2csICcnKTtcbiAgICBjb25zdCBkZWx0YSA9IHRvdHAudmFsaWRhdGUoeyB0b2tlbjogc2FuaXRpemVkLCB3aW5kb3c6IFRPVFBfVkFMSURBVEVfV0lORE9XIH0pO1xuICAgIGlmIChkZWx0YSA9PT0gbnVsbCkge1xuICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICB0eXBlOiAnVE9UUF9WRVJJRklDQVRJT05fRkFJTEVEJyxcbiAgICAgICAgc2V2ZXJpdHk6ICdNRURJVU0nLFxuICAgICAgICBzb3VyY2U6ICdDb25zb2xlVG9rZW5TdG9yZS5jb25maXJtVG90cEVucm9sbG1lbnQnLFxuICAgICAgICBkZXRhaWxzOiAnUGVuZGluZyBUT1RQIGVucm9sbG1lbnQgZmFpbGVkIGNvZGUgdmVyaWZpY2F0aW9uJyxcbiAgICAgIH0pO1xuICAgICAgdGhyb3cgbmV3IFRvdHBFcnJvcihJTlZBTElEX1RPVFBfTUVTU0FHRSwgJ0lOVkFMSURfVE9UUF9DT0RFJyk7XG4gICAgfVxuXG4gICAgLy8gQ29kZSBpcyB2YWxpZCDigJQgY29tbWl0IGVucm9sbG1lbnQuXG4gICAgY29uc3QgcGxhaW50ZXh0Q29kZXMgPSBnZW5lcmF0ZUJhY2t1cENvZGVzKCk7XG4gICAgY29uc3QgaGFzaGVkQ29kZXMgPSBwbGFpbnRleHRDb2Rlcy5tYXAoaGFzaEJhY2t1cENvZGUpO1xuICAgIGNvbnN0IGVucm9sbGVkQXQgPSBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCk7XG5cbiAgICB0aGlzLmRhdGEudG90cCA9IHtcbiAgICAgIGVucm9sbGVkOiB0cnVlLFxuICAgICAgc2VjcmV0OiBwZW5kaW5nLnNlY3JldCxcbiAgICAgIGJhY2t1cENvZGVzOiBoYXNoZWRDb2RlcyxcbiAgICAgIGVucm9sbGVkQXQsXG4gICAgfTtcbiAgICBhd2FpdCB0aGlzLndyaXRlKHRoaXMuZGF0YSk7XG4gICAgdGhpcy5wZW5kaW5nRW5yb2xsbWVudHMuZGVsZXRlKHBlbmRpbmdJZCk7XG5cbiAgICBsb2dnZXIuaW5mbygnW0NvbnNvbGVUb2tlbl0gVE9UUCBlbnJvbGxtZW50IGNvbmZpcm1lZCcsIHtcbiAgICAgIGVucm9sbGVkQXQsXG4gICAgICBiYWNrdXBDb2RlczogaGFzaGVkQ29kZXMubGVuZ3RoLFxuICAgIH0pO1xuICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgIHR5cGU6ICdUT1RQX0VOUk9MTEVEJyxcbiAgICAgIHNldmVyaXR5OiAnTUVESVVNJyxcbiAgICAgIHNvdXJjZTogJ0NvbnNvbGVUb2tlblN0b3JlLmNvbmZpcm1Ub3RwRW5yb2xsbWVudCcsXG4gICAgICBkZXRhaWxzOiAnQ29uc29sZSBUT1RQIHNlY29uZCBmYWN0b3IgZW5yb2xsZWQnLFxuICAgICAgYWRkaXRpb25hbERhdGE6IHsgZW5yb2xsZWRBdCwgYmFja3VwQ29kZXM6IGhhc2hlZENvZGVzLmxlbmd0aCB9LFxuICAgIH0pO1xuXG4gICAgcmV0dXJuIHsgYmFja3VwQ29kZXM6IHBsYWludGV4dENvZGVzLCBlbnJvbGxlZEF0IH07XG4gIH1cblxuICAvKipcbiAgICogVmVyaWZ5IGEgdXNlci1wcmVzZW50ZWQgY29kZS4gQWNjZXB0cyBlaXRoZXIgYSBsaXZlIFRPVFAgY29kZSBvciBhXG4gICAqIHNpbmdsZS11c2UgYmFja3VwIGNvZGUuIE9uIHN1Y2Nlc3NmdWwgYmFja3VwLWNvZGUgbWF0Y2gsIHRoZSBjb25zdW1lZFxuICAgKiBjb2RlJ3MgaGFzaCBpcyByZW1vdmVkIGZyb20gc3RvcmFnZSBhbmQgdGhlIGZpbGUgaXMgcmUtd3JpdHRlbi5cbiAgICpcbiAgICogUmV0dXJucyBhIGRpc2NyaW1pbmF0ZWQgcmVzdWx0IHNvIHRoZSBjYWxsZXIgY2FuIGRpc3Rpbmd1aXNoIFwiY29uc3VtZWRcbiAgICogYSBiYWNrdXAgY29kZVwiICh3aGljaCB0aGUgVUkgc2hvdWxkIHN1cmZhY2Ugd2l0aCBhIHdhcm5pbmcgYWJvdXRcbiAgICogcmVtYWluaW5nIGNvdW50KSBmcm9tIFwidmFsaWQgVE9UUCBjb2RlXCIgKG5vcm1hbCBjYXNlKS4gUmV0dXJuc1xuICAgKiBgeyBvazogZmFsc2UgfWAgb24gYW55IGZhaWx1cmUg4oCUIHRoZSBjYWxsZXIgc2hvdWxkIG5vdCByZXRyeSB3aXRoaW5cbiAgICogdGhlIHNhbWUgcmVxdWVzdCBsaWZlY3ljbGUuXG4gICAqL1xuICBhc3luYyB2ZXJpZnlUb3RwKGNvZGU6IHN0cmluZyk6IFByb21pc2U8eyBvazogdHJ1ZTsgbWV0aG9kOiAndG90cCcgfCAnYmFja3VwJzsgYmFja3VwQ29kZXNSZW1haW5pbmc6IG51bWJlciB9IHwgeyBvazogZmFsc2UgfT4ge1xuICAgIGlmICghdGhpcy5kYXRhPy50b3RwPy5lbnJvbGxlZCB8fCAhdGhpcy5kYXRhLnRvdHAuc2VjcmV0KSB7XG4gICAgICByZXR1cm4geyBvazogZmFsc2UgfTtcbiAgICB9XG4gICAgY29uc3Qgc2FuaXRpemVkID0gY29kZS5yZXBsYWNlQWxsKC9cXHMvZywgJycpO1xuICAgIGlmICghc2FuaXRpemVkKSByZXR1cm4geyBvazogZmFsc2UgfTtcblxuICAgIC8vIFRyeSBsaXZlIFRPVFAgZmlyc3Qg4oCUIGZhc3QgcGF0aCwgbm8gZGlzayB3cml0ZS5cbiAgICBjb25zdCB0b3RwID0gbmV3IFRPVFAoe1xuICAgICAgaXNzdWVyOiBUT1RQX0lTU1VFUixcbiAgICAgIGxhYmVsOiB0aGlzLmRhdGEudG9rZW5zWzBdPy5uYW1lID8/ICdjb25zb2xlJyxcbiAgICAgIGFsZ29yaXRobTogVE9UUF9BTEdPUklUSE0sXG4gICAgICBkaWdpdHM6IFRPVFBfRElHSVRTLFxuICAgICAgcGVyaW9kOiBUT1RQX1BFUklPRF9TRUNPTkRTLFxuICAgICAgc2VjcmV0OiBTZWNyZXQuZnJvbUJhc2UzMih0aGlzLmRhdGEudG90cC5zZWNyZXQpLFxuICAgIH0pO1xuICAgIGlmICh0b3RwLnZhbGlkYXRlKHsgdG9rZW46IHNhbml0aXplZCwgd2luZG93OiBUT1RQX1ZBTElEQVRFX1dJTkRPVyB9KSAhPT0gbnVsbCkge1xuICAgICAgcmV0dXJuIHsgb2s6IHRydWUsIG1ldGhvZDogJ3RvdHAnLCBiYWNrdXBDb2Rlc1JlbWFpbmluZzogdGhpcy5kYXRhLnRvdHAuYmFja3VwQ29kZXMubGVuZ3RoIH07XG4gICAgfVxuXG4gICAgLy8gRmFsbCBiYWNrIHRvIGJhY2t1cCBjb2RlIOKAlCBub3JtYWxpemUsIGhhc2gsIGNvbnN0YW50LXRpbWUgc2VhcmNoLlxuICAgIGNvbnN0IG5vcm1hbGl6ZWRJbnB1dCA9IG5vcm1hbGl6ZUJhY2t1cENvZGUoc2FuaXRpemVkKTtcbiAgICBjb25zdCBpbnB1dEhhc2ggPSBoYXNoQmFja3VwQ29kZShub3JtYWxpemVkSW5wdXQpO1xuICAgIGNvbnN0IGlucHV0SGFzaEJ1ZiA9IEJ1ZmZlci5mcm9tKGlucHV0SGFzaCwgJ2hleCcpO1xuICAgIGxldCBtYXRjaEluZGV4ID0gLTE7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLmRhdGEudG90cC5iYWNrdXBDb2Rlcy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3Qgc3RvcmVkQnVmID0gQnVmZmVyLmZyb20odGhpcy5kYXRhLnRvdHAuYmFja3VwQ29kZXNbaV0sICdoZXgnKTtcbiAgICAgIGlmIChzdG9yZWRCdWYubGVuZ3RoID09PSBpbnB1dEhhc2hCdWYubGVuZ3RoICYmIHRpbWluZ1NhZmVFcXVhbChpbnB1dEhhc2hCdWYsIHN0b3JlZEJ1ZikpIHtcbiAgICAgICAgbWF0Y2hJbmRleCA9IGk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAobWF0Y2hJbmRleCA9PT0gLTEpIHtcbiAgICAgIC8vIEJvdGggVE9UUCBhbmQgYmFja3VwIGNvZGUgcGF0aHMgZXhoYXVzdGVkIHdpdGhvdXQgYSBtYXRjaC4gRW1pdCBhXG4gICAgICAvLyBzaW5nbGUgYXVkaXQgZXZlbnQgc28gdGhlIHNlY3VyaXR5IG1vbml0b3IgY2FuIGFsZXJ0IG9uIGFnZ3JlZ2F0ZVxuICAgICAgLy8gZmFpbHVyZSByYXRlcyAoU2VjdXJpdHlNb25pdG9yIGRlZHVwcyB0aGUgc2FtZSB0eXBlK3NvdXJjZSB3aXRoaW5cbiAgICAgIC8vIGEgNjBzIHdpbmRvdywgc28gcmFwaWQtZmlyZSBhdHRhY2tzIGFyZSBjb2xsYXBzZWQgaW50byBvbmUgZW50cnkpLlxuICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICB0eXBlOiAnVE9UUF9WRVJJRklDQVRJT05fRkFJTEVEJyxcbiAgICAgICAgc2V2ZXJpdHk6ICdNRURJVU0nLFxuICAgICAgICBzb3VyY2U6ICdDb25zb2xlVG9rZW5TdG9yZS52ZXJpZnlUb3RwJyxcbiAgICAgICAgZGV0YWlsczogJ1ByZXNlbnRlZCBUT1RQIGNvZGUgbWF0Y2hlZCBuZWl0aGVyIHRoZSBsaXZlIHNlY3JldCBub3IgYW55IGJhY2t1cCBjb2RlJyxcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuIHsgb2s6IGZhbHNlIH07XG4gICAgfVxuXG4gICAgLy8gQ29uc3VtZSB0aGUgbWF0Y2hlZCBiYWNrdXAgY29kZSDigJQgcmVtb3ZlIGZyb20gc3RvcmFnZSBhbmQgcGVyc2lzdC5cbiAgICB0aGlzLmRhdGEudG90cC5iYWNrdXBDb2Rlcy5zcGxpY2UobWF0Y2hJbmRleCwgMSk7XG4gICAgYXdhaXQgdGhpcy53cml0ZSh0aGlzLmRhdGEpO1xuICAgIGxvZ2dlci5pbmZvKCdbQ29uc29sZVRva2VuXSBCYWNrdXAgY29kZSBjb25zdW1lZCcsIHtcbiAgICAgIHJlbWFpbmluZzogdGhpcy5kYXRhLnRvdHAuYmFja3VwQ29kZXMubGVuZ3RoLFxuICAgIH0pO1xuICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgIHR5cGU6ICdUT1RQX0JBQ0tVUF9DT0RFX0NPTlNVTUVEJyxcbiAgICAgIHNldmVyaXR5OiAnTUVESVVNJyxcbiAgICAgIHNvdXJjZTogJ0NvbnNvbGVUb2tlblN0b3JlLnZlcmlmeVRvdHAnLFxuICAgICAgZGV0YWlsczogJ1RPVFAgYmFja3VwIGNvZGUgY29uc3VtZWQgZm9yIGNvbnNvbGUgYXV0aGVudGljYXRpb24nLFxuICAgICAgYWRkaXRpb25hbERhdGE6IHsgcmVtYWluaW5nOiB0aGlzLmRhdGEudG90cC5iYWNrdXBDb2Rlcy5sZW5ndGggfSxcbiAgICB9KTtcbiAgICByZXR1cm4geyBvazogdHJ1ZSwgbWV0aG9kOiAnYmFja3VwJywgYmFja3VwQ29kZXNSZW1haW5pbmc6IHRoaXMuZGF0YS50b3RwLmJhY2t1cENvZGVzLmxlbmd0aCB9O1xuICB9XG5cbiAgLyoqXG4gICAqIERpc2FibGUgVE9UUC4gUmVxdWlyZXMgYSB2YWxpZCBjb2RlIChUT1RQIG9yIGJhY2t1cCkgYXMgY29uZmlybWF0aW9uIHNvXG4gICAqIGFuIGF0dGFja2VyIHdobyBtb21lbnRhcmlseSBoYXMgYWNjZXNzIHRvIGEgbGl2ZSBzZXNzaW9uIGNhbid0IHNpbGVudGx5XG4gICAqIHN0cmlwIHRoZSBzZWNvbmQgZmFjdG9yLlxuICAgKlxuICAgKiBAdGhyb3dzIEVycm9yIGlmIG5vdCBlbnJvbGxlZCBvciBjb2RlIGludmFsaWQuXG4gICAqL1xuICBhc3luYyBkaXNhYmxlVG90cChjb2RlOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAoIXRoaXMuZGF0YSkge1xuICAgICAgdGhyb3cgbmV3IFRvdHBFcnJvcignVG9rZW4gc3RvcmUgbm90IGluaXRpYWxpemVkJywgJ1NUT1JFX05PVF9JTklUSUFMSVpFRCcpO1xuICAgIH1cbiAgICBpZiAoIXRoaXMuaXNUb3RwRW5yb2xsZWQoKSkge1xuICAgICAgdGhyb3cgbmV3IFRvdHBFcnJvcignVE9UUCBpcyBub3QgY3VycmVudGx5IGVucm9sbGVkJywgJ05PVF9FTlJPTExFRCcpO1xuICAgIH1cbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB0aGlzLnZlcmlmeVRvdHAoY29kZSk7XG4gICAgaWYgKCFyZXN1bHQub2spIHtcbiAgICAgIHRocm93IG5ldyBUb3RwRXJyb3IoSU5WQUxJRF9UT1RQX01FU1NBR0UsICdJTlZBTElEX1RPVFBfQ09ERScpO1xuICAgIH1cbiAgICB0aGlzLmRhdGEudG90cCA9IHtcbiAgICAgIGVucm9sbGVkOiBmYWxzZSxcbiAgICAgIHNlY3JldDogbnVsbCxcbiAgICAgIGJhY2t1cENvZGVzOiBbXSxcbiAgICAgIGVucm9sbGVkQXQ6IG51bGwsXG4gICAgfTtcbiAgICBhd2FpdCB0aGlzLndyaXRlKHRoaXMuZGF0YSk7XG4gICAgbG9nZ2VyLmluZm8oJ1tDb25zb2xlVG9rZW5dIFRPVFAgZGlzYWJsZWQnKTtcbiAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICB0eXBlOiAnVE9UUF9ESVNBQkxFRCcsXG4gICAgICBzZXZlcml0eTogJ0hJR0gnLFxuICAgICAgc291cmNlOiAnQ29uc29sZVRva2VuU3RvcmUuZGlzYWJsZVRvdHAnLFxuICAgICAgZGV0YWlsczogJ0NvbnNvbGUgVE9UUCBzZWNvbmQgZmFjdG9yIGRpc2FibGVkIOKAlCBzaW5nbGUtZmFjdG9yIGF1dGggcmVzdG9yZWQnLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqIFJlbW92ZSBhbnkgcGVuZGluZyBlbnJvbGxtZW50cyB3aG9zZSBUVEwgaGFzIHBhc3NlZC4gKi9cbiAgcHJpdmF0ZSBzd2VlcEV4cGlyZWRFbnJvbGxtZW50cygpOiB2b2lkIHtcbiAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICAgIGZvciAoY29uc3QgW2lkLCBwZW5kaW5nXSBvZiB0aGlzLnBlbmRpbmdFbnJvbGxtZW50cykge1xuICAgICAgaWYgKHBlbmRpbmcuZXhwaXJlc0F0IDw9IG5vdykge1xuICAgICAgICB0aGlzLnBlbmRpbmdFbnJvbGxtZW50cy5kZWxldGUoaWQpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gIC8vIGVuZCBUT1RQXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgLy8gVG9rZW4gcm90YXRpb24g4oCUICMxNzk1XG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbiAgLyoqXG4gICAqIFJvdGF0ZSB0aGUgcHJpbWFyeSBjb25zb2xlIHRva2VuLiBSZXF1aXJlcyBUT1RQIGNvbmZpcm1hdGlvbiAoUGF0dGVybiBCKS5cbiAgICpcbiAgICogRmxvdzpcbiAgICogMS4gVmVyaWZ5IHRoZSBjb25maXJtYXRpb24gY29kZSB2aWEgYHZlcmlmeVRvdHAoKWAgKGxpdmUgVE9UUCBvciBiYWNrdXAgY29kZSkuXG4gICAqIDIuIFN0YXNoIHRoZSBvbGQgdG9rZW4gdmFsdWUgaW4gdGhlIGluLW1lbW9yeSBncmFjZSBidWZmZXIgc28gaW4tZmxpZ2h0XG4gICAqICAgIHJlcXVlc3RzIGZyb20gdGhlIHJvdGF0aW5nIHRhYiAoYW5kIGFueSBvdGhlciBsb2NhbCBwcm9jZXNzIHRoYXQgcmVhZFxuICAgKiAgICB0aGUgZmlsZSBiZWZvcmUgdGhlIHJvdGF0aW9uKSBzdGlsbCBhdXRoZW50aWNhdGUgZm9yIFJPVEFUSU9OX0dSQUNFX01TLlxuICAgKiAzLiBHZW5lcmF0ZSBhIG5ldyAzMi1ieXRlIHRva2VuLCBtdXRhdGUgdGhlIHByaW1hcnkgZW50cnkgaW4gcGxhY2UsIHdyaXRlXG4gICAqICAgIHRoZSBmaWxlIGF0b21pY2FsbHksIGFuZCByZWJ1aWxkIHRoZSBidWZmZXIgY2FjaGUuXG4gICAqIDQuIFJldHVybiB0aGUgbmV3IHRva2VuIGlubGluZSBzbyB0aGUgY2FsbGVyIGNhbiB1cGRhdGUgYERvbGxob3VzZUF1dGgudG9rZW5gXG4gICAqICAgIHdpdGhvdXQgYSBwYWdlIHJlbG9hZC5cbiAgICpcbiAgICogUGF0dGVybiBBIChPUyBkaWFsb2cgZmFsbGJhY2sgZm9yIHVzZXJzIHdpdGhvdXQgVE9UUCkgaXMgZGVmZXJyZWQg4oCUIHRoZVxuICAgKiBjYWxsZXIgc2hvdWxkIGdhdGUgdGhlIFVJIHNvIHRoZSByb3RhdGUgYWN0aW9uIGlzIG9ubHkgYXZhaWxhYmxlIHdoZW5cbiAgICogVE9UUCBpcyBlbnJvbGxlZC5cbiAgICpcbiAgICogQHRocm93cyBUb3RwRXJyb3IgU1RPUkVfTk9UX0lOSVRJQUxJWkVEIOKAlCBzdG9yZSBuZXZlciBsb2FkZWRcbiAgICogQHRocm93cyBUb3RwRXJyb3IgVE9UUF9SRVFVSVJFRCDigJQgVE9UUCBub3QgZW5yb2xsZWQsIHJvdGF0aW9uIHJlcXVpcmVzIHNlY29uZC1mYWN0b3IgY29uZmlybWF0aW9uXG4gICAqIEB0aHJvd3MgVG90cEVycm9yIElOVkFMSURfVE9UUF9DT0RFIOKAlCB3cm9uZyBjb2RlXG4gICAqL1xuICBhc3luYyByb3RhdGVQcmltYXJ5KGNvbmZpcm1hdGlvbkNvZGU6IHN0cmluZyk6IFByb21pc2U8Um90YXRpb25SZXN1bHQ+IHtcbiAgICBpZiAoIXRoaXMuZGF0YSkge1xuICAgICAgdGhyb3cgbmV3IFRvdHBFcnJvcignVG9rZW4gc3RvcmUgbm90IGluaXRpYWxpemVkJywgJ1NUT1JFX05PVF9JTklUSUFMSVpFRCcpO1xuICAgIH1cbiAgICBpZiAoIXRoaXMuaXNUb3RwRW5yb2xsZWQoKSkge1xuICAgICAgdGhyb3cgbmV3IFRvdHBFcnJvcihcbiAgICAgICAgJ1Rva2VuIHJvdGF0aW9uIHJlcXVpcmVzIFRPVFAgZW5yb2xsbWVudCDigJQgZW5yb2xsIGEgc2Vjb25kIGZhY3RvciBiZWZvcmUgcm90YXRpbmcnLFxuICAgICAgICAnVE9UUF9SRVFVSVJFRCcsXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIFN0ZXAgMTogdmVyaWZ5IHRoZSBjb25maXJtYXRpb24gY29kZS5cbiAgICBjb25zdCB2ZXJpZmljYXRpb24gPSBhd2FpdCB0aGlzLnZlcmlmeVRvdHAoY29uZmlybWF0aW9uQ29kZSk7XG4gICAgaWYgKCF2ZXJpZmljYXRpb24ub2spIHtcbiAgICAgIHRocm93IG5ldyBUb3RwRXJyb3IoSU5WQUxJRF9UT1RQX01FU1NBR0UsICdJTlZBTElEX1RPVFBfQ09ERScpO1xuICAgIH1cblxuICAgIC8vIFN0ZXAgMjogc3Rhc2ggdGhlIG9sZCB0b2tlbiBpbiB0aGUgZ3JhY2UgYnVmZmVyLlxuICAgIGNvbnN0IHByaW1hcnkgPSB0aGlzLmRhdGEudG9rZW5zWzBdO1xuICAgIGNvbnN0IGdyYWNlRGVhZGxpbmUgPSBEYXRlLm5vdygpICsgUk9UQVRJT05fR1JBQ0VfTVM7XG4gICAgdGhpcy5zd2VlcEV4cGlyZWRHcmFjZUVudHJpZXMoKTtcbiAgICB0aGlzLmdyYWNlRW50cmllcy5wdXNoKHtcbiAgICAgIGJ1ZjogQnVmZmVyLmZyb20ocHJpbWFyeS50b2tlbiwgJ3V0ZjgnKSxcbiAgICAgIGV4cGlyZXNBdDogZ3JhY2VEZWFkbGluZSxcbiAgICB9KTtcblxuICAgIC8vIFN0ZXAgMzogZ2VuZXJhdGUgYSBuZXcgdG9rZW4sIG11dGF0ZSB0aGUgcHJpbWFyeSBlbnRyeSwgcGVyc2lzdC5cbiAgICBjb25zdCByb3RhdGVkQXQgPSBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCk7XG4gICAgcHJpbWFyeS50b2tlbiA9IGdlbmVyYXRlVG9rZW5WYWx1ZSgpO1xuICAgIHByaW1hcnkuY3JlYXRlZEF0ID0gcm90YXRlZEF0O1xuICAgIHByaW1hcnkubGFzdFVzZWRBdCA9IG51bGw7XG4gICAgcHJpbWFyeS5jcmVhdGVkVmlhID0gJ3JvdGF0aW9uJztcblxuICAgIGF3YWl0IHRoaXMud3JpdGUodGhpcy5kYXRhKTtcbiAgICB0aGlzLnJlYnVpbGRUb2tlbkJ1ZmZlcnMoKTtcblxuICAgIGxvZ2dlci5pbmZvKCdbQ29uc29sZVRva2VuXSBQcmltYXJ5IHRva2VuIHJvdGF0ZWQnLCB7XG4gICAgICBpZDogcHJpbWFyeS5pZCxcbiAgICAgIHJvdGF0ZWRBdCxcbiAgICAgIGdyYWNlTXM6IFJPVEFUSU9OX0dSQUNFX01TLFxuICAgIH0pO1xuICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgIHR5cGU6ICdDT05TT0xFX1RPS0VOX1JPVEFURUQnLFxuICAgICAgc2V2ZXJpdHk6ICdISUdIJyxcbiAgICAgIHNvdXJjZTogJ0NvbnNvbGVUb2tlblN0b3JlLnJvdGF0ZVByaW1hcnknLFxuICAgICAgZGV0YWlsczogJ0NvbnNvbGUgcHJpbWFyeSB0b2tlbiByb3RhdGVkIHZpYSBUT1RQIGNvbmZpcm1hdGlvbicsXG4gICAgICBhZGRpdGlvbmFsRGF0YToge1xuICAgICAgICB0b2tlbklkOiBwcmltYXJ5LmlkLFxuICAgICAgICByb3RhdGVkQXQsXG4gICAgICAgIGdyYWNlTXM6IFJPVEFUSU9OX0dSQUNFX01TLFxuICAgICAgICBjb25maXJtYXRpb25NZXRob2Q6IHZlcmlmaWNhdGlvbi5tZXRob2QsXG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIHRva2VuOiBwcmltYXJ5LnRva2VuLFxuICAgICAgcm90YXRlZEF0LFxuICAgICAgZ3JhY2VVbnRpbDogZ3JhY2VEZWFkbGluZSxcbiAgICB9O1xuICB9XG5cbiAgLyoqIFJlbW92ZSBleHBpcmVkIGdyYWNlIGVudHJpZXMgc28gdGhleSBzdG9wIG1hdGNoaW5nIGluIHZlcmlmeSgpLiAqL1xuICBwcml2YXRlIHN3ZWVwRXhwaXJlZEdyYWNlRW50cmllcygpOiB2b2lkIHtcbiAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICAgIGxldCBpID0gMDtcbiAgICB3aGlsZSAoaSA8IHRoaXMuZ3JhY2VFbnRyaWVzLmxlbmd0aCkge1xuICAgICAgaWYgKHRoaXMuZ3JhY2VFbnRyaWVzW2ldLmV4cGlyZXNBdCA8PSBub3cpIHtcbiAgICAgICAgdGhpcy5ncmFjZUVudHJpZXMuc3BsaWNlKGksIDEpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaSsrO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gIC8vIGVuZCByb3RhdGlvblxuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4gIC8qKlxuICAgKiBSZWFkIHRoZSB0b2tlbiBmaWxlIGFuZCBkaXN0aW5ndWlzaCBtaXNzaW5nIGZyb20gY29ycnVwdC5cbiAgICpcbiAgICogUmV0dXJuaW5nIGEgdGFnZ2VkIHVuaW9uIGxldHMgYGVuc3VyZUluaXRpYWxpemVkKClgIGJhY2sgdXAgY29ycnVwdCBmaWxlc1xuICAgKiBiZWZvcmUgb3ZlcndyaXRpbmcgdGhlbSDigJQgdXNlcnMgd2hvIGhhbmQtZWRpdGVkIHRoZWlyIHRva2VucyB3aXRoIGN1c3RvbVxuICAgKiBuYW1lcyBvciBsYWJlbHMgZGVzZXJ2ZSBhIHJlY292ZXJ5IHBhdGggaW5zdGVhZCBvZiBhIHNpbGVudCBkZXN0cm95LlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyByZWFkV2l0aFN0YXR1cygpOiBQcm9taXNlPFxuICAgIHwgeyBzdGF0dXM6ICdvayc7IGRhdGE6IENvbnNvbGVUb2tlbkZpbGUgfVxuICAgIHwgeyBzdGF0dXM6ICdtaXNzaW5nJyB9XG4gICAgfCB7IHN0YXR1czogJ2NvcnJ1cHQnOyByZWFzb246IHN0cmluZyB9XG4gID4ge1xuICAgIGxldCBjb250ZW50OiBzdHJpbmc7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnRlbnQgPSBhd2FpdCByZWFkRmlsZSh0aGlzLmZpbGVQYXRoLCAndXRmOCcpO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgaWYgKChlcnIgYXMgTm9kZUpTLkVycm5vRXhjZXB0aW9uKS5jb2RlID09PSAnRU5PRU5UJykgcmV0dXJuIHsgc3RhdHVzOiAnbWlzc2luZycgfTtcbiAgICAgIHJldHVybiB7IHN0YXR1czogJ2NvcnJ1cHQnLCByZWFzb246IGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiBTdHJpbmcoZXJyKSB9O1xuICAgIH1cbiAgICB0cnkge1xuICAgICAgY29uc3QgcGFyc2VkID0gSlNPTi5wYXJzZShjb250ZW50KTtcbiAgICAgIGNvbnN0IHZhbGlkYXRlZCA9IHZhbGlkYXRlVG9rZW5GaWxlKHBhcnNlZCk7XG4gICAgICBpZiAoIXZhbGlkYXRlZCkgcmV0dXJuIHsgc3RhdHVzOiAnY29ycnVwdCcsIHJlYXNvbjogJ3NjaGVtYSB2YWxpZGF0aW9uIGZhaWxlZCcgfTtcbiAgICAgIHJldHVybiB7IHN0YXR1czogJ29rJywgZGF0YTogdmFsaWRhdGVkIH07XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICByZXR1cm4geyBzdGF0dXM6ICdjb3JydXB0JywgcmVhc29uOiBlcnIgaW5zdGFuY2VvZiBFcnJvciA/IGVyci5tZXNzYWdlIDogU3RyaW5nKGVycikgfTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ29weSB0aGUgY3VycmVudCAocHJlc3VtZWQgY29ycnVwdCkgdG9rZW4gZmlsZSB0byBhIHRpbWVzdGFtcGVkIGJhY2t1cFxuICAgKiBhbG9uZ3NpZGUgaXQgc28gdGhlIHVzZXIgY2FuIHJlY292ZXIgaGFuZC1lZGl0ZWQgZGF0YSBhZnRlciBhbiBhY2NpZGVudGFsXG4gICAqIHN5bnRheCBlcnJvci4gQmVzdC1lZmZvcnQg4oCUIGZhaWx1cmUgdG8gYmFjayB1cCBkb2VzIG5vdCBibG9jayBjcmVhdGluZ1xuICAgKiBhIGZyZXNoIGZpbGUsIHNpbmNlIHRoZSBwcmltYXJ5IGdvYWwgaXMga2VlcGluZyB0aGUgY29uc29sZSB1c2FibGUuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGJhY2t1cENvcnJ1cHRGaWxlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHRpbWVzdGFtcCA9IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKS5yZXBsYWNlQWxsKC9bOi5dL2csICctJyk7XG4gICAgY29uc3QgYmFja3VwUGF0aCA9IGAke3RoaXMuZmlsZVBhdGh9LmNvcnJ1cHQtJHt0aW1lc3RhbXB9YDtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgY29weUZpbGUodGhpcy5maWxlUGF0aCwgYmFja3VwUGF0aCk7XG4gICAgICBsb2dnZXIud2FybihgW0NvbnNvbGVUb2tlbl0gQ29ycnVwdCB0b2tlbiBmaWxlIGJhY2tlZCB1cCB0byAke2JhY2t1cFBhdGh9IOKAlCBhIGZyZXNoIHRva2VuIHdpbGwgYmUgY3JlYXRlZGApO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgbG9nZ2VyLndhcm4oJ1tDb25zb2xlVG9rZW5dIENvdWxkIG5vdCBiYWNrIHVwIGNvcnJ1cHQgdG9rZW4gZmlsZSwgd2lsbCBvdmVyd3JpdGUgaW4gcGxhY2UnLCB7XG4gICAgICAgIGVycm9yOiBlcnIgaW5zdGFuY2VvZiBFcnJvciA/IGVyci5tZXNzYWdlIDogU3RyaW5nKGVyciksXG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQXRvbWljYWxseSB3cml0ZSB0aGUgdG9rZW4gZmlsZSB3aXRoIG93bmVyLW9ubHkgcGVybWlzc2lvbnMuXG4gICAqIFVzZXMgdGVtcCtyZW5hbWUgdG8gYXZvaWQgcGFydGlhbCB3cml0ZXMgb24gY3Jhc2guXG4gICAqXG4gICAqIE9uIFdpbmRvd3MsIGBjaG1vZCgwbzYwMClgIGlzIGVmZmVjdGl2ZWx5IGEgbm8tb3AgYmVjYXVzZSB0aGUgZmlsZVxuICAgKiBzeXN0ZW0gdXNlcyBBQ0xzIGluc3RlYWQgb2YgUE9TSVggbW9kZXMuIFdlIGxvZyBhIG9uZS10aW1lIHdhcm5pbmcgc29cbiAgICogdXNlcnMgb24gV2luZG93cyBrbm93IHRoZSB0b2tlbiBmaWxlIGRvZXMgbm90IGhhdmUgT1MtZW5mb3JjZWQgYWNjZXNzXG4gICAqIGNvbnRyb2wgYW5kIGNhbiBkZWNpZGUgd2hldGhlciB0byB1c2UgYWRkaXRpb25hbCB0b29saW5nIChpY2FjbHMsIE5URlNcbiAgICogcGVybWlzc2lvbnMsIG9yIGEgZGlmZmVyZW50IHN0b3JhZ2UgbG9jYXRpb24pLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyB3cml0ZShmaWxlOiBDb25zb2xlVG9rZW5GaWxlKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgbWtkaXIoUlVOX0RJUiwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgY29uc3QgdG1wRmlsZSA9IGAke3RoaXMuZmlsZVBhdGh9LiR7cHJvY2Vzcy5waWR9LnRtcGA7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHdyaXRlRmlsZSh0bXBGaWxlLCBKU09OLnN0cmluZ2lmeShmaWxlLCBudWxsLCAyKSwgJ3V0ZjgnKTtcbiAgICAgIGF3YWl0IGNobW9kKHRtcEZpbGUsIFRPS0VOX0ZJTEVfTU9ERSk7XG4gICAgICBhd2FpdCByZW5hbWUodG1wRmlsZSwgdGhpcy5maWxlUGF0aCk7XG4gICAgICB0aGlzLndhcm5JZldpbmRvd3NQZXJtaXNzaW9ucygpO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgdHJ5IHsgYXdhaXQgdW5saW5rKHRtcEZpbGUpOyB9IGNhdGNoIHsgLyogaWdub3JlICovIH1cbiAgICAgIHRocm93IGVycjtcbiAgICB9XG4gIH1cblxuICAvKiogT25lLXNob3QgZmxhZyBzbyB0aGUgV2luZG93cyBwZXJtaXNzaW9ucyB3YXJuaW5nIGlzIGxvZ2dlZCBhdCBtb3N0IG9uY2UuICovXG4gIHByaXZhdGUgd2luZG93c1dhcm5pbmdMb2dnZWQgPSBmYWxzZTtcblxuICBwcml2YXRlIHdhcm5JZldpbmRvd3NQZXJtaXNzaW9ucygpOiB2b2lkIHtcbiAgICBpZiAodGhpcy53aW5kb3dzV2FybmluZ0xvZ2dlZCkgcmV0dXJuO1xuICAgIGlmIChwbGF0Zm9ybSgpICE9PSAnd2luMzInKSByZXR1cm47XG4gICAgdGhpcy53aW5kb3dzV2FybmluZ0xvZ2dlZCA9IHRydWU7XG4gICAgbG9nZ2VyLndhcm4oXG4gICAgICBgW0NvbnNvbGVUb2tlbl0gVG9rZW4gZmlsZSBhdCAke3RoaXMuZmlsZVBhdGh9IGhhcyBubyBPUy1lbmZvcmNlZCBhY2Nlc3MgY29udHJvbCBvbiBXaW5kb3dzIGAgK1xuICAgICAgYChjaG1vZCAwbzYwMCBpcyBhIG5vLW9wIG9uIHRoaXMgcGxhdGZvcm0pLiBBbnkgcHJvY2VzcyBydW5uaW5nIGFzIHRoZSBzYW1lIHVzZXIgY2FuIHJlYWQgdGhlIGZpbGUuIGAgK1xuICAgICAgYENvbnNpZGVyIHVzaW5nIE5URlMgQUNMcyB2aWEgJ2ljYWNscycgZm9yIHN0cm9uZ2VyIGlzb2xhdGlvbiBpbiBtdWx0aS11c2VyIGVudmlyb25tZW50cy5gLFxuICAgICk7XG4gIH1cbn1cblxuLyoqXG4gKiBSZWFkIHRoZSByYXcgdG9rZW4gZmlsZSBmcm9tIGRpc2sgd2l0aG91dCBjb25zdHJ1Y3RpbmcgYSBzdG9yZS5cbiAqIEludGVuZGVkIGZvciBmb2xsb3dlciBwcm9jZXNzZXMgdGhhdCBuZWVkIHRoZSBwcmltYXJ5IHRva2VuIHRvIGF0dGFjaFxuICogdG8gdGhlaXIgaW5nZXN0IFBPU1RzLiBSZXR1cm5zIG51bGwgaWYgdGhlIGZpbGUgZG9lcyBub3QgZXhpc3Qgb3IgaXMgaW52YWxpZC5cbiAqXG4gKiBAcGFyYW0gZmlsZVBhdGggLSBPcHRpb25hbCBvdmVycmlkZSBmb3IgdGhlIHRva2VuIGZpbGUgbG9jYXRpb25cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlYWRUb2tlbkZpbGVSYXcoZmlsZVBhdGg6IHN0cmluZyA9IERFRkFVTFRfVE9LRU5fRklMRSk6IFByb21pc2U8Q29uc29sZVRva2VuRmlsZSB8IG51bGw+IHtcbiAgdHJ5IHtcbiAgICBjb25zdCBjb250ZW50ID0gYXdhaXQgcmVhZEZpbGUoZmlsZVBhdGgsICd1dGY4Jyk7XG4gICAgcmV0dXJuIHZhbGlkYXRlVG9rZW5GaWxlKEpTT04ucGFyc2UoY29udGVudCkpO1xuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxufVxuXG4vKipcbiAqIEdldCB0aGUgcHJpbWFyeSB0b2tlbiB2YWx1ZSBmcm9tIHRoZSB0b2tlbiBmaWxlIG9uIGRpc2suXG4gKiBDb252ZW5pZW5jZSBoZWxwZXIgZm9yIGZvbGxvd2VycyBhbmQgZXh0ZXJuYWwgY29uc3VtZXJzLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0UHJpbWFyeVRva2VuRnJvbUZpbGUoZmlsZVBhdGg6IHN0cmluZyA9IERFRkFVTFRfVE9LRU5fRklMRSk6IFByb21pc2U8c3RyaW5nIHwgbnVsbD4ge1xuICBjb25zdCBmaWxlID0gYXdhaXQgcmVhZFRva2VuRmlsZVJhdyhmaWxlUGF0aCk7XG4gIGlmICghZmlsZSB8fCBmaWxlLnRva2Vucy5sZW5ndGggPT09IDApIHJldHVybiBudWxsO1xuICByZXR1cm4gZmlsZS50b2tlbnNbMF0udG9rZW47XG59XG5cbi8qKiBFeHBvcnQgdGhlIGRlZmF1bHQgZmlsZSBwYXRoIHNvIGNhbGxlcnMgY2FuIHJlZmVyZW5jZSBpdCBpbiBsb2dzL2RvY3MuICovXG5leHBvcnQgeyBERUZBVUxUX1RPS0VOX0ZJTEUgfTtcbiJdfQ==