UNPKG

aws-cdk

Version:

AWS CDK Toolkit CLI, the command line tool for CDK apps

347 lines • 45.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.trackLeakedHandles = trackLeakedHandles; const node_async_hooks_1 = require("node:async_hooks"); const node_fs_1 = require("node:fs"); const node_url_1 = require("node:url"); const chalk_1 = __importDefault(require("chalk")); /** * Async resource types we track, with a plain-language description for the report. * * Only libuv *handles*, the resources that can hold the event loop open. Requests * (a DNS lookup, a stream write) have completed by the time the report runs. * `PROMISE` and `TickObject` are excluded on volume alone: they dominate every * async resource a synth creates, and each one would cost a stack capture. * * Every type here exposes `hasRef()`, the only way to tell a handle holding the * loop open from one already closed. `TLSWRAP` has neither `hasRef()` nor a * `destroy` hook, so including it would report every completed HTTPS request as a * leak forever. A leaked TLS connection still surfaces as the `TCPWRAP` under it. */ const TRACKED_TYPES = { TCPWRAP: 'open network connection', TCPSERVERWRAP: 'listening TCP server', PIPEWRAP: 'open pipe', PIPESERVERWRAP: 'listening pipe server', UDPWRAP: 'open UDP socket', TTYWRAP: 'open terminal stream', Timeout: 'timer from setTimeout or setInterval', Immediate: 'pending setImmediate callback', PROCESSWRAP: 'spawned child process still running', FSEVENTWRAP: 'file-system watcher', STATWATCHER: 'file-system stat watcher', SIGNALWRAP: 'OS signal handler still registered', WORKER: 'worker thread still running', MESSAGEPORT: 'open worker-thread message channel', }; /** * How many stack frames to capture per resource. * * Node's default of 10 is too shallow here. A socket opened through an HTTPS agent * sits behind enough frames of `node:internal/tls/wrap`, `node:https`, * `node:_http_agent` and `node:_http_client` to consume that whole budget, leaving * no application frame for the report to show. */ const STACK_CAPTURE_DEPTH = 60; /** * Grace period before the handle report fires. The timer is unref'd, so it never * fires when Node exits cleanly within this window. */ const HANDLE_DUMP_GRACE_MS = 1000; /** * Cap on how many resources we hold provenance for at once. * * A long-lived command (`cdk watch`, a deploy of many stacks) opens and closes * handles continuously. The `destroy` hook normally removes them again, but types * that never fire it would accumulate for the life of the process. The report says * when the cap was hit, so a truncated list is not read as a complete one. */ const MAX_TRACKED_HANDLES = 10_000; /** * How much of a source line to show beneath a stack frame. Long enough to read a * statement, short enough that one minified line from the bundled CLI cannot * flood the report. */ const SOURCE_SNIPPET_CHARS = 200; /** * Tracks async resources via async_hooks and, on demand, reports the ones still * keeping the event loop alive, with where they were created. * * Nothing is tracked until `start()` runs. Not exported as a value, so the only * way to get one is `trackLeakedHandles()`, which returns a started tracker. */ class LeakedHandleTracker { watched = new Map(); /** * Whether we stopped recording because {@link MAX_TRACKED_HANDLES} was reached. * Reported, so a short list is never mistaken for a complete one. */ atCapacity = false; hook = (0, node_async_hooks_1.createHook)({ init: (asyncId, type, _triggerAsyncId, resource) => { if (!(type in TRACKED_TYPES)) { return; } if (this.watched.size >= MAX_TRACKED_HANDLES) { this.atCapacity = true; return; } // An exception thrown from an async_hooks callback is not catchable by the // application. Node prints "Error in AsyncHook callback" and aborts the // process, so anything that goes wrong here must cost no more than one // handle's provenance. try { this.watched.set(asyncId, { type, handleRef: new WeakRef(resource), creationStack: captureCreationStack(), }); } catch { this.watched.delete(asyncId); } }, destroy: (asyncId) => { this.watched.delete(asyncId); }, }); /** * Begin watching async resources. Must run before the resources we care about * are created, and only when the user opted in, since the hook adds a small * per-resource cost. */ start() { this.hook.enable(); } /** * Stop watching and discard all tracked state. */ stop() { this.hook.disable(); this.watched.clear(); } /** * Report the leaked handles once the grace period has passed. The timer is * unref'd, so it never fires if the process exits cleanly within the window and * never holds the process open itself. */ scheduleReport(ioHelper) { setTimeout(() => { // Runs detached from the CLI's error handling: the command has finished and // there is no one left to hand a rejection to. A diagnostic must not crash // the process it is diagnosing. this.report(ioHelper).catch(() => undefined); }, HANDLE_DUMP_GRACE_MS).unref(); } /** * Report every resource still holding the event loop open, each with the source * location where it was created. Call at the very end of execution, by which * point only genuinely leaked handles should remain. * * Emitted as one message, so unrelated output cannot interleave into the middle * of a stack trace, and at DEBUG, which is the level `--debug-cli` raises the * CLI to. */ async report(ioHelper) { this.hook.disable(); const leaks = [...this.watched.values()].filter((r) => { const handle = r.handleRef.deref(); // Already garbage collected, so no longer keeping the loop alive. if (handle === undefined) { return false; } // A fired one-shot timer answers `hasRef() === true` for the rest of the // process and only marks itself `_destroyed`. The `destroy` hook does drop // it from `watched`, but a tick later than this, and timers are the CLI's // most common resource. if (handle._destroyed === true) { return false; } // Ask the handle itself, and believe only a clear yes. A closed socket // answers false, and anything that cannot answer is not evidence of a leak. return typeof handle.hasRef === 'function' && handle.hasRef(); }); this.watched.clear(); await ioHelper.defaults.debug(this.renderReport(leaks).join('\n')); } renderReport(leaks) { if (leaks.length === 0) { // This report only runs because the process was still alive, so something // is holding the loop open. Finding nothing means the cause is outside what // we track, so hand over what Node itself sees. return [ 'The CLI process is still alive, but no tracked handle explains it.', `Node reports these resources still active: ${activeResourceSummary()}`, 'The cause may be a handle opened before tracking started, or a type this build does not track.', ]; } const lines = [`${leaks.length} ${leaks.length === 1 ? 'handle' : 'handles'} still keeping the CLI process alive:`]; if (this.atCapacity) { lines.push(`Stopped recording after ${MAX_TRACKED_HANDLES} handles, so this list may be incomplete.`); } // One report pass can revisit the same file for every frame of every handle, // and in the published CLI that file is the entire bundle. const sources = new SourceCache(); for (const leak of leaks) { lines.push(...describeLeak(leak, sources)); } if (sources.sawMinifiedLine) { // The published CLI is one bundled, minified file with no source map, so a // location is a position in the bundle and the snippet beneath it is the // only part that maps back to something recognisable. lines.push(''); lines.push('Locations above are positions in the bundled CLI, not in the original source files.'); } return lines; } } function describeLeak(leak, sources) { const frames = actionableFrames(leak.creationStack); const lines = ['', chalk_1.default.bold(`# ${leak.type} (${TRACKED_TYPES[leak.type]})`)]; if (frames.length === 0) { lines.push(' (opened entirely inside Node internals, no CLI frames to show)'); return lines; } // Headline the function only when it has a real name. Sockets often open from // anonymous internal callbacks, where the name says nothing. const [origin] = frames; if (origin.func && origin.func !== '<anonymous>') { lines.push(` created in ${origin.func}()`); } lines.push(' call stack:'); for (const frame of frames) { // The location is what identifies the frame, so print it even when the file // cannot be read. lines.push(` ${frame.func} (${frame.file}:${frame.line}:${frame.column})`); const snippet = sources.snippetAt(frame); if (snippet) { lines.push(` ${chalk_1.default.dim(snippet)}`); } } return lines; } /** * Capture the call site of the current async resource as structured frames. * * Installs a structured `prepareStackTrace`, captures with this function as the * cut-off, then restores the previous formatter so other stacks are unaffected. * * The cut-off removes this function and everything above it, but the caller is the * tracker's own `init` hook, which sits just below and would otherwise be the top * frame. We drop that frame by position rather than by filename, since after * bundling every frame shares one filename. */ function captureCreationStack() { const carrier = {}; // `Error.prepareStackTrace` is a V8 formatting hook we save and restore, not a // method we invoke, so the unbound-method concern does not apply. // eslint-disable-next-line @typescript-eslint/unbound-method const previous = Error.prepareStackTrace; const previousLimit = Error.stackTraceLimit; Error.stackTraceLimit = STACK_CAPTURE_DEPTH; Error.prepareStackTrace = (_error, callSites) => callSites.map((site) => { const file = site.getFileName() ?? ''; return { func: site.getFunctionName() ?? '<anonymous>', file: file.startsWith('file://') ? (0, node_url_1.fileURLToPath)(file) : file, line: site.getLineNumber() ?? 0, column: site.getColumnNumber() ?? 0, }; }); try { Error.captureStackTrace(carrier, captureCreationStack); // Drop the `init` hook frame, always the top one, so the report points at the // code that created the resource rather than at this tracker. return carrier.stack?.slice(1) ?? []; } finally { Error.prepareStackTrace = previous; Error.stackTraceLimit = previousLimit; } } /** * What Node itself reports as still holding the loop open, as `2x Timeout, 1x TCP`. * * This is the ground truth our own tracking tries to explain, so printing it tells * the reader whether we missed something or the hang is somewhere else. */ function activeResourceSummary() { const counts = new Map(); for (const type of process.getActiveResourcesInfo()) { counts.set(type, (counts.get(type) ?? 0) + 1); } if (counts.size === 0) { return '(none)'; } return [...counts].map(([type, count]) => `${count}x ${type}`).join(', '); } /** * Keep only frames the user can act on by dropping Node internals. Our own `init` * frame is already removed at capture time, see {@link captureCreationStack}. */ function actionableFrames(frames) { return frames.filter((frame) => frame.file && !frame.file.startsWith('node:')); } /** * Reads the line of code behind a stack frame, holding each file's lines so a * report pass reads any given file at most once. * * The published CLI is one large bundle, and a single report can hold many frames * pointing into it. Re-reading and re-splitting it per frame stalls the report, at * the moment the user is already waiting on a hung CLI. */ class SourceCache { /** * Whether any line we read was long enough to be minified rather than * hand-written. Used to warn that the printed locations are bundle-relative. */ sawMinifiedLine = false; files = new Map(); snippetAt(frame) { const line = this.linesOf(frame.file)?.[frame.line - 1]; if (line === undefined) { return undefined; } if (line.length <= SOURCE_SNIPPET_CHARS) { return line.trim() || undefined; } // Too long to be source anyone wrote, so show a window centred on the column // rather than the start of the line. this.sawMinifiedLine = true; const start = Math.max(0, frame.column - 1 - Math.floor(SOURCE_SNIPPET_CHARS / 2)); const end = Math.min(line.length, start + SOURCE_SNIPPET_CHARS); const window = line.slice(start, end).trim(); return `${start > 0 ? '…' : ''}${window}${end < line.length ? '…' : ''}`; } linesOf(file) { if (!this.files.has(file)) { this.files.set(file, readLines(file)); } return this.files.get(file); } } function readLines(file) { try { return (0, node_fs_1.readFileSync)(file, 'utf-8').split(/\r?\n/); } catch { // Bundled and eval'd frames have filenames that are not on disk. The location // is still reported on its own, so reporting continues without the snippet. return undefined; } } /** * Start watching async resources and return the tracker doing so. * * Call as early as possible, and only when the user asked for it. The tracker can * only report on resources created after it starts. */ function trackLeakedHandles() { const tracker = new LeakedHandleTracker(); tracker.start(); return tracker; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVidWctaGFuZGxlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImRlYnVnLWhhbmRsZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFrWkEsZ0RBSUM7QUF0WkQsdURBQThDO0FBQzlDLHFDQUF1QztBQUN2Qyx1Q0FBeUM7QUFDekMsa0RBQTBCO0FBRzFCOzs7Ozs7Ozs7Ozs7R0FZRztBQUNILE1BQU0sYUFBYSxHQUFxQztJQUN0RCxPQUFPLEVBQUUseUJBQXlCO0lBQ2xDLGFBQWEsRUFBRSxzQkFBc0I7SUFDckMsUUFBUSxFQUFFLFdBQVc7SUFDckIsY0FBYyxFQUFFLHVCQUF1QjtJQUN2QyxPQUFPLEVBQUUsaUJBQWlCO0lBQzFCLE9BQU8sRUFBRSxzQkFBc0I7SUFDL0IsT0FBTyxFQUFFLHNDQUFzQztJQUMvQyxTQUFTLEVBQUUsK0JBQStCO0lBQzFDLFdBQVcsRUFBRSxxQ0FBcUM7SUFDbEQsV0FBVyxFQUFFLHFCQUFxQjtJQUNsQyxXQUFXLEVBQUUsMEJBQTBCO0lBQ3ZDLFVBQVUsRUFBRSxvQ0FBb0M7SUFDaEQsTUFBTSxFQUFFLDZCQUE2QjtJQUNyQyxXQUFXLEVBQUUsb0NBQW9DO0NBQ2xELENBQUM7QUFFRjs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxtQkFBbUIsR0FBRyxFQUFFLENBQUM7QUFFL0I7OztHQUdHO0FBQ0gsTUFBTSxvQkFBb0IsR0FBRyxJQUFJLENBQUM7QUFFbEM7Ozs7Ozs7R0FPRztBQUNILE1BQU0sbUJBQW1CLEdBQUcsTUFBTSxDQUFDO0FBRW5DOzs7O0dBSUc7QUFDSCxNQUFNLG9CQUFvQixHQUFHLEdBQUcsQ0FBQztBQXFDakM7Ozs7OztHQU1HO0FBQ0gsTUFBTSxtQkFBbUI7SUFDTixPQUFPLEdBQUcsSUFBSSxHQUFHLEVBQTJCLENBQUM7SUFFOUQ7OztPQUdHO0lBQ0ssVUFBVSxHQUFHLEtBQUssQ0FBQztJQUVWLElBQUksR0FBRyxJQUFBLDZCQUFVLEVBQUM7UUFDakMsSUFBSSxFQUFFLENBQUMs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QyxJQUFJLEVBQUUsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDeEMsQ0FBQztRQUNELE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDOUIsQ0FBQztDQUNGO0FBRUQsU0FBUyxTQUFTLENBQUMsSUFBWTtJQUM3QixJQUFJLENBQUM7UUFDSCxPQUFPLElBQUEsc0JBQVksRUFBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ3BELENBQUM7SUFBQyxNQUFNLENBQUM7UUFDUCw4RUFBOEU7UUFDOUUsNEVBQTRFO1FBQzVFLE9BQU8sU0FBUyxDQUFDO0lBQ25CLENBQUM7QUFDSCxDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFnQixrQkFBa0I7SUFDaEMsTUFBTSxPQUFPLEdBQUcsSUFBSSxtQkFBbUIsRUFBRSxDQUFDO0lBQzFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUNoQixPQUFPLE9BQU8sQ0FBQztBQUNqQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgY3JlYXRlSG9vayB9IGZyb20gJ25vZGU6YXN5bmNfaG9va3MnO1xuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnbm9kZTpmcyc7XG5pbXBvcnQgeyBmaWxlVVJMVG9QYXRoIH0gZnJvbSAnbm9kZTp1cmwnO1xuaW1wb3J0IGNoYWxrIGZyb20gJ2NoYWxrJztcbmltcG9ydCB0eXBlIHsgSW9IZWxwZXIgfSBmcm9tICcuLi9hcGktcHJpdmF0ZSc7XG5cbi8qKlxuICogQXN5bmMgcmVzb3VyY2UgdHlwZXMgd2UgdHJhY2ssIHdpdGggYSBwbGFpbi1sYW5ndWFnZSBkZXNjcmlwdGlvbiBmb3IgdGhlIHJlcG9ydC5cbiAqXG4gKiBPbmx5IGxpYnV2ICpoYW5kbGVzKiwgdGhlIHJlc291cmNlcyB0aGF0IGNhbiBob2xkIHRoZSBldmVudCBsb29wIG9wZW4uIFJlcXVlc3RzXG4gKiAoYSBETlMgbG9va3VwLCBhIHN0cmVhbSB3cml0ZSkgaGF2ZSBjb21wbGV0ZWQgYnkgdGhlIHRpbWUgdGhlIHJlcG9ydCBydW5zLlxuICogYFBST01JU0VgIGFuZCBgVGlja09iamVjdGAgYXJlIGV4Y2x1ZGVkIG9uIHZvbHVtZSBhbG9uZTogdGhleSBkb21pbmF0ZSBldmVyeVxuICogYXN5bmMgcmVzb3VyY2UgYSBzeW50aCBjcmVhdGVzLCBhbmQgZWFjaCBvbmUgd291bGQgY29zdCBhIHN0YWNrIGNhcHR1cmUuXG4gKlxuICogRXZlcnkgdHlwZSBoZXJlIGV4cG9zZXMgYGhhc1JlZigpYCwgdGhlIG9ubHkgd2F5IHRvIHRlbGwgYSBoYW5kbGUgaG9sZGluZyB0aGVcbiAqIGxvb3Agb3BlbiBmcm9tIG9uZSBhbHJlYWR5IGNsb3NlZC4gYFRMU1dSQVBgIGhhcyBuZWl0aGVyIGBoYXNSZWYoKWAgbm9yIGFcbiAqIGBkZXN0cm95YCBob29rLCBzbyBpbmNsdWRpbmcgaXQgd291bGQgcmVwb3J0IGV2ZXJ5IGNvbXBsZXRlZCBIVFRQUyByZXF1ZXN0IGFzIGFcbiAqIGxlYWsgZm9yZXZlci4gQSBsZWFrZWQgVExTIGNvbm5lY3Rpb24gc3RpbGwgc3VyZmFjZXMgYXMgdGhlIGBUQ1BXUkFQYCB1bmRlciBpdC5cbiAqL1xuY29uc3QgVFJBQ0tFRF9UWVBFUzogUmVhZG9ubHk8UmVjb3JkPHN0cmluZywgc3RyaW5nPj4gPSB7XG4gIFRDUFdSQVA6ICdvcGVuIG5ldHdvcmsgY29ubmVjdGlvbicsXG4gIFRDUFNFUlZFUldSQVA6ICdsaXN0ZW5pbmcgVENQIHNlcnZlcicsXG4gIFBJUEVXUkFQOiAnb3BlbiBwaXBlJyxcbiAgUElQRVNFUlZFUldSQVA6ICdsaXN0ZW5pbmcgcGlwZSBzZXJ2ZXInLFxuICBVRFBXUkFQOiAnb3BlbiBVRFAgc29ja2V0JyxcbiAgVFRZV1JBUDogJ29wZW4gdGVybWluYWwgc3RyZWFtJyxcbiAgVGltZW91dDogJ3RpbWVyIGZyb20gc2V0VGltZW91dCBvciBzZXRJbnRlcnZhbCcsXG4gIEltbWVkaWF0ZTogJ3BlbmRpbmcgc2V0SW1tZWRpYXRlIGNhbGxiYWNrJyxcbiAgUFJPQ0VTU1dSQVA6ICdzcGF3bmVkIGNoaWxkIHByb2Nlc3Mgc3RpbGwgcnVubmluZycsXG4gIEZTRVZFTlRXUkFQOiAnZmlsZS1zeXN0ZW0gd2F0Y2hlcicsXG4gIFNUQVRXQVRDSEVSOiAnZmlsZS1zeXN0ZW0gc3RhdCB3YXRjaGVyJyxcbiAgU0lHTkFMV1JBUDogJ09TIHNpZ25hbCBoYW5kbGVyIHN0aWxsIHJlZ2lzdGVyZWQnLFxuICBXT1JLRVI6ICd3b3JrZXIgdGhyZWFkIHN0aWxsIHJ1bm5pbmcnLFxuICBNRVNTQUdFUE9SVDogJ29wZW4gd29ya2VyLXRocmVhZCBtZXNzYWdlIGNoYW5uZWwnLFxufTtcblxuLyoqXG4gKiBIb3cgbWFueSBzdGFjayBmcmFtZXMgdG8gY2FwdHVyZSBwZXIgcmVzb3VyY2UuXG4gKlxuICogTm9kZSdzIGRlZmF1bHQgb2YgMTAgaXMgdG9vIHNoYWxsb3cgaGVyZS4gQSBzb2NrZXQgb3BlbmVkIHRocm91Z2ggYW4gSFRUUFMgYWdlbnRcbiAqIHNpdHMgYmVoaW5kIGVub3VnaCBmcmFtZXMgb2YgYG5vZGU6aW50ZXJuYWwvdGxzL3dyYXBgLCBgbm9kZTpodHRwc2AsXG4gKiBgbm9kZTpfaHR0cF9hZ2VudGAgYW5kIGBub2RlOl9odHRwX2NsaWVudGAgdG8gY29uc3VtZSB0aGF0IHdob2xlIGJ1ZGdldCwgbGVhdmluZ1xuICogbm8gYXBwbGljYXRpb24gZnJhbWUgZm9yIHRoZSByZXBvcnQgdG8gc2hvdy5cbiAqL1xuY29uc3QgU1RBQ0tfQ0FQVFVSRV9ERVBUSCA9IDYwO1xuXG4vKipcbiAqIEdyYWNlIHBlcmlvZCBiZWZvcmUgdGhlIGhhbmRsZSByZXBvcnQgZmlyZXMuIFRoZSB0aW1lciBpcyB1bnJlZidkLCBzbyBpdCBuZXZlclxuICogZmlyZXMgd2hlbiBOb2RlIGV4aXRzIGNsZWFubHkgd2l0aGluIHRoaXMgd2luZG93LlxuICovXG5jb25zdCBIQU5ETEVfRFVNUF9HUkFDRV9NUyA9IDEwMDA7XG5cbi8qKlxuICogQ2FwIG9uIGhvdyBtYW55IHJlc291cmNlcyB3ZSBob2xkIHByb3ZlbmFuY2UgZm9yIGF0IG9uY2UuXG4gKlxuICogQSBsb25nLWxpdmVkIGNvbW1hbmQgKGBjZGsgd2F0Y2hgLCBhIGRlcGxveSBvZiBtYW55IHN0YWNrcykgb3BlbnMgYW5kIGNsb3Nlc1xuICogaGFuZGxlcyBjb250aW51b3VzbHkuIFRoZSBgZGVzdHJveWAgaG9vayBub3JtYWxseSByZW1vdmVzIHRoZW0gYWdhaW4sIGJ1dCB0eXBlc1xuICogdGhhdCBuZXZlciBmaXJlIGl0IHdvdWxkIGFjY3VtdWxhdGUgZm9yIHRoZSBsaWZlIG9mIHRoZSBwcm9jZXNzLiBUaGUgcmVwb3J0IHNheXNcbiAqIHdoZW4gdGhlIGNhcCB3YXMgaGl0LCBzbyBhIHRydW5jYXRlZCBsaXN0IGlzIG5vdCByZWFkIGFzIGEgY29tcGxldGUgb25lLlxuICovXG5jb25zdCBNQVhfVFJBQ0tFRF9IQU5ETEVTID0gMTBfMDAwO1xuXG4vKipcbiAqIEhvdyBtdWNoIG9mIGEgc291cmNlIGxpbmUgdG8gc2hvdyBiZW5lYXRoIGEgc3RhY2sgZnJhbWUuIExvbmcgZW5vdWdoIHRvIHJlYWQgYVxuICogc3RhdGVtZW50LCBzaG9ydCBlbm91Z2ggdGhhdCBvbmUgbWluaWZpZWQgbGluZSBmcm9tIHRoZSBidW5kbGVkIENMSSBjYW5ub3RcbiAqIGZsb29kIHRoZSByZXBvcnQuXG4gKi9cbmNvbnN0IFNPVVJDRV9TTklQUEVUX0NIQVJTID0gMjAwO1xuXG4vKipcbiAqIE9uZSBzdGFjayBmcmFtZTogdGhlIGZ1bmN0aW9uIG5hbWUsIHBsdXMgdGhlIGZpbGUsIGxpbmUgYW5kIGNvbHVtbiwgdXNlZCBib3RoIHRvXG4gKiBwcmludCB0aGUgbG9jYXRpb24gYW5kIHRvIHJlYWQgYmFjayB0aGUgbGluZSBvZiBjb2RlLlxuICpcbiAqIFRoZSBjb2x1bW4gaXMgbG9hZC1iZWFyaW5nLiBJbiB0aGUgcHVibGlzaGVkIENMSSBldmVyeSBmcmFtZSBwb2ludHMgaW50byBvbmVcbiAqIGJ1bmRsZWQsIHdoaXRlc3BhY2UtbWluaWZpZWQgZmlsZSwgd2hlcmUgdGhlIGxpbmUgbnVtYmVyIGJhcmVseSBkaXN0aW5ndWlzaGVzXG4gKiBvbmUgZnJhbWUgZnJvbSBhbm90aGVyLlxuICovXG5pbnRlcmZhY2UgU291cmNlRnJhbWUge1xuICByZWFkb25seSBmdW5jOiBzdHJpbmc7XG4gIHJlYWRvbmx5IGZpbGU6IHN0cmluZztcbiAgcmVhZG9ubHkgbGluZTogbnVtYmVyO1xuICByZWFkb25seSBjb2x1bW46IG51bWJlcjtcbn1cblxuLyoqXG4gKiBBIHJlc291cmNlIHdlIGFyZSB3YXRjaGluZzogYSB3ZWFrIHJlZmVyZW5jZSB0byB0aGUgaGFuZGxlLCBzbyB0cmFja2luZyBkb2VzIG5vdFxuICogaXRzZWxmIGtlZXAgaXQgYWxpdmUsIGFuZCB0aGUgc3RhY2sgb2Ygd2hlcmUgaXQgd2FzIGNyZWF0ZWQuXG4gKi9cbmludGVyZmFjZSBXYXRjaGVkUmVzb3VyY2Uge1xuICByZWFkb25seSB0eXBlOiBzdHJpbmc7XG4gIHJlYWRvbmx5IGhhbmRsZVJlZjogV2Vha1JlZjxUcmFja2VkSGFuZGxlPjtcbiAgcmVhZG9ubHkgY3JlYXRpb25TdGFjazogU291cmNlRnJhbWVbXTtcbn1cblxuLyoqXG4gKiBUaGUgcGFydHMgb2YgYSBsaWJ1diBoYW5kbGUgd2UgaW50ZXJyb2dhdGUuIEJvdGggYXJlIG9wdGlvbmFsIGJlY2F1c2UgbmVpdGhlciBpc1xuICogZ3VhcmFudGVlZDogYGhhc1JlZmAgZXhpc3RzIG9ubHkgb24gYEhhbmRsZVdyYXBgLWJhY2tlZCB0eXBlcywgYW5kIGBfZGVzdHJveWVkYFxuICogaXMgYSBOb2RlIGludGVybmFsIHRoYXQgb25seSB0aW1lcnMgc2V0LlxuICovXG5pbnRlcmZhY2UgVHJhY2tlZEhhbmRsZSB7XG4gIF9kZXN0cm95ZWQ/OiBib29sZWFuO1xuICBoYXNSZWY/KCk6IGJvb2xlYW47XG59XG5cbi8qKlxuICogVHJhY2tzIGFzeW5jIHJlc291cmNlcyB2aWEgYXN5bmNfaG9va3MgYW5kLCBvbiBkZW1hbmQsIHJlcG9ydHMgdGhlIG9uZXMgc3RpbGxcbiAqIGtlZXBpbmcgdGhlIGV2ZW50IGxvb3AgYWxpdmUsIHdpdGggd2hlcmUgdGhleSB3ZXJlIGNyZWF0ZWQuXG4gKlxuICogTm90aGluZyBpcyB0cmFja2VkIHVudGlsIGBzdGFydCgpYCBydW5zLiBOb3QgZXhwb3J0ZWQgYXMgYSB2YWx1ZSwgc28gdGhlIG9ubHlcbiAqIHdheSB0byBnZXQgb25lIGlzIGB0cmFja0xlYWtlZEhhbmRsZXMoKWAsIHdoaWNoIHJldHVybnMgYSBzdGFydGVkIHRyYWNrZXIuXG4gKi9cbmNsYXNzIExlYWtlZEhhbmRsZVRyYWNrZXIge1xuICBwcml2YXRlIHJlYWRvbmx5IHdhdGNoZWQgPSBuZXcgTWFwPG51bWJlciwgV2F0Y2hlZFJlc291cmNlPigpO1xuXG4gIC8qKlxuICAgKiBXaGV0aGVyIHdlIHN0b3BwZWQgcmVjb3JkaW5nIGJlY2F1c2Uge0BsaW5rIE1BWF9UUkFDS0VEX0hBTkRMRVN9IHdhcyByZWFjaGVkLlxuICAgKiBSZXBvcnRlZCwgc28gYSBzaG9ydCBsaXN0IGlzIG5ldmVyIG1pc3Rha2VuIGZvciBhIGNvbXBsZXRlIG9uZS5cbiAgICovXG4gIHByaXZhdGUgYXRDYXBhY2l0eSA9IGZhbHNlO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgaG9vayA9IGNyZWF0ZUhvb2soe1xuICAgIGluaXQ6IChhc3luY0lkLCB0eXBlLCBfdHJpZ2dlckFzeW5jSWQsIHJlc291cmNlKSA9PiB7XG4gICAgICBpZiAoISh0eXBlIGluIFRSQUNLRURfVFlQRVMpKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGlmICh0aGlzLndhdGNoZWQuc2l6ZSA+PSBNQVhfVFJBQ0tFRF9IQU5ETEVTKSB7XG4gICAgICAgIHRoaXMuYXRDYXBhY2l0eSA9IHRydWU7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIC8vIEFuIGV4Y2VwdGlvbiB0aHJvd24gZnJvbSBhbiBhc3luY19ob29rcyBjYWxsYmFjayBpcyBub3QgY2F0Y2hhYmxlIGJ5IHRoZVxuICAgICAgLy8gYXBwbGljYXRpb24uIE5vZGUgcHJpbnRzIFwiRXJyb3IgaW4gQXN5bmNIb29rIGNhbGxiYWNrXCIgYW5kIGFib3J0cyB0aGVcbiAgICAgIC8vIHByb2Nlc3MsIHNvIGFueXRoaW5nIHRoYXQgZ29lcyB3cm9uZyBoZXJlIG11c3QgY29zdCBubyBtb3JlIHRoYW4gb25lXG4gICAgICAvLyBoYW5kbGUncyBwcm92ZW5hbmNlLlxuICAgICAgdHJ5IHtcbiAgICAgICAgdGhpcy53YXRjaGVkLnNldChhc3luY0lkLCB7XG4gICAgICAgICAgdHlwZSxcbiAgICAgICAgICBoYW5kbGVSZWY6IG5ldyBXZWFrUmVmKHJlc291cmNlIGFzIFRyYWNrZWRIYW5kbGUpLFxuICAgICAgICAgIGNyZWF0aW9uU3RhY2s6IGNhcHR1cmVDcmVhdGlvblN0YWNrKCksXG4gICAgICAgIH0pO1xuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIHRoaXMud2F0Y2hlZC5kZWxldGUoYXN5bmNJZCk7XG4gICAgICB9XG4gICAgfSxcbiAgICBkZXN0cm95OiAoYXN5bmNJZCkgPT4ge1xuICAgICAgdGhpcy53YXRjaGVkLmRlbGV0ZShhc3luY0lkKTtcbiAgICB9LFxuICB9KTtcblxuICAvKipcbiAgICogQmVnaW4gd2F0Y2hpbmcgYXN5bmMgcmVzb3VyY2VzLiBNdXN0IHJ1biBiZWZvcmUgdGhlIHJlc291cmNlcyB3ZSBjYXJlIGFib3V0XG4gICAqIGFyZSBjcmVhdGVkLCBhbmQgb25seSB3aGVuIHRoZSB1c2VyIG9wdGVkIGluLCBzaW5jZSB0aGUgaG9vayBhZGRzIGEgc21hbGxcbiAgICogcGVyLXJlc291cmNlIGNvc3QuXG4gICAqL1xuICBwdWJsaWMgc3RhcnQoKTogdm9pZCB7XG4gICAgdGhpcy5ob29rLmVuYWJsZSgpO1xuICB9XG5cbiAgLyoqXG4gICAqIFN0b3Agd2F0Y2hpbmcgYW5kIGRpc2NhcmQgYWxsIHRyYWNrZWQgc3RhdGUuXG4gICAqL1xuICBwdWJsaWMgc3RvcCgpOiB2b2lkIHtcbiAgICB0aGlzLmhvb2suZGlzYWJsZSgpO1xuICAgIHRoaXMud2F0Y2hlZC5jbGVhcigpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlcG9ydCB0aGUgbGVha2VkIGhhbmRsZXMgb25jZSB0aGUgZ3JhY2UgcGVyaW9kIGhhcyBwYXNzZWQuIFRoZSB0aW1lciBpc1xuICAgKiB1bnJlZidkLCBzbyBpdCBuZXZlciBmaXJlcyBpZiB0aGUgcHJvY2VzcyBleGl0cyBjbGVhbmx5IHdpdGhpbiB0aGUgd2luZG93IGFuZFxuICAgKiBuZXZlciBob2xkcyB0aGUgcHJvY2VzcyBvcGVuIGl0c2VsZi5cbiAgICovXG4gIHB1YmxpYyBzY2hlZHVsZVJlcG9ydChpb0hlbHBlcjogSW9IZWxwZXIpOiB2b2lkIHtcbiAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIC8vIFJ1bnMgZGV0YWNoZWQgZnJvbSB0aGUgQ0xJJ3MgZXJyb3IgaGFuZGxpbmc6IHRoZSBjb21tYW5kIGhhcyBmaW5pc2hlZCBhbmRcbiAgICAgIC8vIHRoZXJlIGlzIG5vIG9uZSBsZWZ0IHRvIGhhbmQgYSByZWplY3Rpb24gdG8uIEEgZGlhZ25vc3RpYyBtdXN0IG5vdCBjcmFzaFxuICAgICAgLy8gdGhlIHByb2Nlc3MgaXQgaXMgZGlhZ25vc2luZy5cbiAgICAgIHRoaXMucmVwb3J0KGlvSGVscGVyKS5jYXRjaCgoKSA9PiB1bmRlZmluZWQpO1xuICAgIH0sIEhBTkRMRV9EVU1QX0dSQUNFX01TKS51bnJlZigpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlcG9ydCBldmVyeSByZXNvdXJjZSBzdGlsbCBob2xkaW5nIHRoZSBldmVudCBsb29wIG9wZW4sIGVhY2ggd2l0aCB0aGUgc291cmNlXG4gICAqIGxvY2F0aW9uIHdoZXJlIGl0IHdhcyBjcmVhdGVkLiBDYWxsIGF0IHRoZSB2ZXJ5IGVuZCBvZiBleGVjdXRpb24sIGJ5IHdoaWNoXG4gICAqIHBvaW50IG9ubHkgZ2VudWluZWx5IGxlYWtlZCBoYW5kbGVzIHNob3VsZCByZW1haW4uXG4gICAqXG4gICAqIEVtaXR0ZWQgYXMgb25lIG1lc3NhZ2UsIHNvIHVucmVsYXRlZCBvdXRwdXQgY2Fubm90IGludGVybGVhdmUgaW50byB0aGUgbWlkZGxlXG4gICAqIG9mIGEgc3RhY2sgdHJhY2UsIGFuZCBhdCBERUJVRywgd2hpY2ggaXMgdGhlIGxldmVsIGAtLWRlYnVnLWNsaWAgcmFpc2VzIHRoZVxuICAgKiBDTEkgdG8uXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgcmVwb3J0KGlvSGVscGVyOiBJb0hlbHBlcik6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuaG9vay5kaXNhYmxlKCk7XG5cbiAgICBjb25zdCBsZWFrcyA9IFsuLi50aGlzLndhdGNoZWQudmFsdWVzKCldLmZpbHRlcigocikgPT4ge1xuICAgICAgY29uc3QgaGFuZGxlID0gci5oYW5kbGVSZWYuZGVyZWYoKTtcbiAgICAgIC8vIEFscmVhZHkgZ2FyYmFnZSBjb2xsZWN0ZWQsIHNvIG5vIGxvbmdlciBrZWVwaW5nIHRoZSBsb29wIGFsaXZlLlxuICAgICAgaWYgKGhhbmRsZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICAgIC8vIEEgZmlyZWQgb25lLXNob3QgdGltZXIgYW5zd2VycyBgaGFzUmVmKCkgPT09IHRydWVgIGZvciB0aGUgcmVzdCBvZiB0aGVcbiAgICAgIC8vIHByb2Nlc3MgYW5kIG9ubHkgbWFya3MgaXRzZWxmIGBfZGVzdHJveWVkYC4gVGhlIGBkZXN0cm95YCBob29rIGRvZXMgZHJvcFxuICAgICAgLy8gaXQgZnJvbSBgd2F0Y2hlZGAsIGJ1dCBhIHRpY2sgbGF0ZXIgdGhhbiB0aGlzLCBhbmQgdGltZXJzIGFyZSB0aGUgQ0xJJ3NcbiAgICAgIC8vIG1vc3QgY29tbW9uIHJlc291cmNlLlxuICAgICAgaWYgKGhhbmRsZS5fZGVzdHJveWVkID09PSB0cnVlKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICAgIC8vIEFzayB0aGUgaGFuZGxlIGl0c2VsZiwgYW5kIGJlbGlldmUgb25seSBhIGNsZWFyIHllcy4gQSBjbG9zZWQgc29ja2V0XG4gICAgICAvLyBhbnN3ZXJzIGZhbHNlLCBhbmQgYW55dGhpbmcgdGhhdCBjYW5ub3QgYW5zd2VyIGlzIG5vdCBldmlkZW5jZSBvZiBhIGxlYWsuXG4gICAgICByZXR1cm4gdHlwZW9mIGhhbmRsZS5oYXNSZWYgPT09ICdmdW5jdGlvbicgJiYgaGFuZGxlLmhhc1JlZigpO1xuICAgIH0pO1xuICAgIHRoaXMud2F0Y2hlZC5jbGVhcigpO1xuXG4gICAgYXdhaXQgaW9IZWxwZXIuZGVmYXVsdHMuZGVidWcodGhpcy5yZW5kZXJSZXBvcnQobGVha3MpLmpvaW4oJ1xcbicpKTtcbiAgfVxuXG4gIHByaXZhdGUgcmVuZGVyUmVwb3J0KGxlYWtzOiBXYXRjaGVkUmVzb3VyY2VbXSk6IHN0cmluZ1tdIHtcbiAgICBpZiAobGVha3MubGVuZ3RoID09PSAwKSB7XG4gICAgICAvLyBUaGlzIHJlcG9ydCBvbmx5IHJ1bnMgYmVjYXVzZSB0aGUgcHJvY2VzcyB3YXMgc3RpbGwgYWxpdmUsIHNvIHNvbWV0aGluZ1xuICAgICAgLy8gaXMgaG9sZGluZyB0aGUgbG9vcCBvcGVuLiBGaW5kaW5nIG5vdGhpbmcgbWVhbnMgdGhlIGNhdXNlIGlzIG91dHNpZGUgd2hhdFxuICAgICAgLy8gd2UgdHJhY2ssIHNvIGhhbmQgb3ZlciB3aGF0IE5vZGUgaXRzZWxmIHNlZXMuXG4gICAgICByZXR1cm4gW1xuICAgICAgICAnVGhlIENMSSBwcm9jZXNzIGlzIHN0aWxsIGFsaXZlLCBidXQgbm8gdHJhY2tlZCBoYW5kbGUgZXhwbGFpbnMgaXQuJyxcbiAgICAgICAgYE5vZGUgcmVwb3J0cyB0aGVzZSByZXNvdXJjZXMgc3RpbGwgYWN0aXZlOiAke2FjdGl2ZVJlc291cmNlU3VtbWFyeSgpfWAsXG4gICAgICAgICdUaGUgY2F1c2UgbWF5IGJlIGEgaGFuZGxlIG9wZW5lZCBiZWZvcmUgdHJhY2tpbmcgc3RhcnRlZCwgb3IgYSB0eXBlIHRoaXMgYnVpbGQgZG9lcyBub3QgdHJhY2suJyxcbiAgICAgIF07XG4gICAgfVxuXG4gICAgY29uc3QgbGluZXMgPSBbYCR7bGVha3MubGVuZ3RofSAke2xlYWtzLmxlbmd0aCA9PT0gMSA/ICdoYW5kbGUnIDogJ2hhbmRsZXMnfSBzdGlsbCBrZWVwaW5nIHRoZSBDTEkgcHJvY2VzcyBhbGl2ZTpgXTtcbiAgICBpZiAodGhpcy5hdENhcGFjaXR5KSB7XG4gICAgICBsaW5lcy5wdXNoKGBTdG9wcGVkIHJlY29yZGluZyBhZnRlciAke01BWF9UUkFDS0VEX0hBTkRMRVN9IGhhbmRsZXMsIHNvIHRoaXMgbGlzdCBtYXkgYmUgaW5jb21wbGV0ZS5gKTtcbiAgICB9XG5cbiAgICAvLyBPbmUgcmVwb3J0IHBhc3MgY2FuIHJldmlzaXQgdGhlIHNhbWUgZmlsZSBmb3IgZXZlcnkgZnJhbWUgb2YgZXZlcnkgaGFuZGxlLFxuICAgIC8vIGFuZCBpbiB0aGUgcHVibGlzaGVkIENMSSB0aGF0IGZpbGUgaXMgdGhlIGVudGlyZSBidW5kbGUuXG4gICAgY29uc3Qgc291cmNlcyA9IG5ldyBTb3VyY2VDYWNoZSgpO1xuICAgIGZvciAoY29uc3QgbGVhayBvZiBsZWFrcykge1xuICAgICAgbGluZXMucHVzaCguLi5kZXNjcmliZUxlYWsobGVhaywgc291cmNlcykpO1xuICAgIH1cblxuICAgIGlmIChzb3VyY2VzLnNhd01pbmlmaWVkTGluZSkge1xuICAgICAgLy8gVGhlIHB1Ymxpc2hlZCBDTEkgaXMgb25lIGJ1bmRsZWQsIG1pbmlmaWVkIGZpbGUgd2l0aCBubyBzb3VyY2UgbWFwLCBzbyBhXG4gICAgICAvLyBsb2NhdGlvbiBpcyBhIHBvc2l0aW9uIGluIHRoZSBidW5kbGUgYW5kIHRoZSBzbmlwcGV0IGJlbmVhdGggaXQgaXMgdGhlXG4gICAgICAvLyBvbmx5IHBhcnQgdGhhdCBtYXBzIGJhY2sgdG8gc29tZXRoaW5nIHJlY29nbmlzYWJsZS5cbiAgICAgIGxpbmVzLnB1c2goJycpO1xuICAgICAgbGluZXMucHVzaCgnTG9jYXRpb25zIGFib3ZlIGFyZSBwb3NpdGlvbnMgaW4gdGhlIGJ1bmRsZWQgQ0xJLCBub3QgaW4gdGhlIG9yaWdpbmFsIHNvdXJjZSBmaWxlcy4nKTtcbiAgICB9XG5cbiAgICByZXR1cm4gbGluZXM7XG4gIH1cbn1cblxuZnVuY3Rpb24gZGVzY3JpYmVMZWFrKGxlYWs6IFdhdGNoZWRSZXNvdXJjZSwgc291cmNlczogU291cmNlQ2FjaGUpOiBzdHJpbmdbXSB7XG4gIGNvbnN0IGZyYW1lcyA9IGFjdGlvbmFibGVGcmFtZXMobGVhay5jcmVhdGlvblN0YWNrKTtcbiAgY29uc3QgbGluZXMgPSBbJycsIGNoYWxrLmJvbGQoYCMgJHtsZWFrLnR5cGV9ICgke1RSQUNLRURfVFlQRVNbbGVhay50eXBlXX0pYCldO1xuXG4gIGlmIChmcmFtZXMubGVuZ3RoID09PSAwKSB7XG4gICAgbGluZXMucHVzaCgnICAob3BlbmVkIGVudGlyZWx5IGluc2lkZSBOb2RlIGludGVybmFscywgbm8gQ0xJIGZyYW1lcyB0byBzaG93KScpO1xuICAgIHJldHVybiBsaW5lcztcbiAgfVxuXG4gIC8vIEhlYWRsaW5lIHRoZSBmdW5jdGlvbiBvbmx5IHdoZW4gaXQgaGFzIGEgcmVhbCBuYW1lLiBTb2NrZXRzIG9mdGVuIG9wZW4gZnJvbVxuICAvLyBhbm9ueW1vdXMgaW50ZXJuYWwgY2FsbGJhY2tzLCB3aGVyZSB0aGUgbmFtZSBzYXlzIG5vdGhpbmcuXG4gIGNvbnN0IFtvcmlnaW5dID0gZnJhbWVzO1xuICBpZiAob3JpZ2luLmZ1bmMgJiYgb3JpZ2luLmZ1bmMgIT09ICc8YW5vbnltb3VzPicpIHtcbiAgICBsaW5lcy5wdXNoKGAgIGNyZWF0ZWQgaW4gJHtvcmlnaW4uZnVuY30oKWApO1xuICB9XG5cbiAgbGluZXMucHVzaCgnICBjYWxsIHN0YWNrOicpO1xuICBmb3IgKGNvbnN0IGZyYW1lIG9mIGZyYW1lcykge1xuICAgIC8vIFRoZSBsb2NhdGlvbiBpcyB3aGF0IGlkZW50aWZpZXMgdGhlIGZyYW1lLCBzbyBwcmludCBpdCBldmVuIHdoZW4gdGhlIGZpbGVcbiAgICAvLyBjYW5ub3QgYmUgcmVhZC5cbiAgICBsaW5lcy5wdXNoKGAgICAgJHtmcmFtZS5mdW5jfSAoJHtmcmFtZS5maWxlfToke2ZyYW1lLmxpbmV9OiR7ZnJhbWUuY29sdW1ufSlgKTtcbiAgICBjb25zdCBzbmlwcGV0ID0gc291cmNlcy5zbmlwcGV0QXQoZnJhbWUpO1xuICAgIGlmIChzbmlwcGV0KSB7XG4gICAgICBsaW5lcy5wdXNoKGAgICAgICAke2NoYWxrLmRpbShzbmlwcGV0KX1gKTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIGxpbmVzO1xufVxuXG4vKipcbiAqIENhcHR1cmUgdGhlIGNhbGwgc2l0ZSBvZiB0aGUgY3VycmVudCBhc3luYyByZXNvdXJjZSBhcyBzdHJ1Y3R1cmVkIGZyYW1lcy5cbiAqXG4gKiBJbnN0YWxscyBhIHN0cnVjdHVyZWQgYHByZXBhcmVTdGFja1RyYWNlYCwgY2FwdHVyZXMgd2l0aCB0aGlzIGZ1bmN0aW9uIGFzIHRoZVxuICogY3V0LW9mZiwgdGhlbiByZXN0b3JlcyB0aGUgcHJldmlvdXMgZm9ybWF0dGVyIHNvIG90aGVyIHN0YWNrcyBhcmUgdW5hZmZlY3RlZC5cbiAqXG4gKiBUaGUgY3V0LW9mZiByZW1vdmVzIHRoaXMgZnVuY3Rpb24gYW5kIGV2ZXJ5dGhpbmcgYWJvdmUgaXQsIGJ1dCB0aGUgY2FsbGVyIGlzIHRoZVxuICogdHJhY2tlcidzIG93biBgaW5pdGAgaG9vaywgd2hpY2ggc2l0cyBqdXN0IGJlbG93IGFuZCB3b3VsZCBvdGhlcndpc2UgYmUgdGhlIHRvcFxuICogZnJhbWUuIFdlIGRyb3AgdGhhdCBmcmFtZSBieSBwb3NpdGlvbiByYXRoZXIgdGhhbiBieSBmaWxlbmFtZSwgc2luY2UgYWZ0ZXJcbiAqIGJ1bmRsaW5nIGV2ZXJ5IGZyYW1lIHNoYXJlcyBvbmUgZmlsZW5hbWUuXG4gKi9cbmZ1bmN0aW9uIGNhcHR1cmVDcmVhdGlvblN0YWNrKCk6IFNvdXJjZUZyYW1lW10ge1xuICBjb25zdCBjYXJyaWVyOiB7IHN0YWNrPzogU291cmNlRnJhbWVbXSB9ID0ge307XG5cbiAgLy8gYEVycm9yLnByZXBhcmVTdGFja1RyYWNlYCBpcyBhIFY4IGZvcm1hdHRpbmcgaG9vayB3ZSBzYXZlIGFuZCByZXN0b3JlLCBub3QgYVxuICAvLyBtZXRob2Qgd2UgaW52b2tlLCBzbyB0aGUgdW5ib3VuZC1tZXRob2QgY29uY2VybiBkb2VzIG5vdCBhcHBseS5cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC91bmJvdW5kLW1ldGhvZFxuICBjb25zdCBwcmV2aW91cyA9IEVycm9yLnByZXBhcmVTdGFja1RyYWNlO1xuICBjb25zdCBwcmV2aW91c0xpbWl0ID0gRXJyb3Iuc3RhY2tUcmFjZUxpbWl0O1xuICBFcnJvci5zdGFja1RyYWNlTGltaXQgPSBTVEFDS19DQVBUVVJFX0RFUFRIO1xuICBFcnJvci5wcmVwYXJlU3RhY2tUcmFjZSA9IChfZXJyb3IsIGNhbGxTaXRlcykgPT4gY2FsbFNpdGVzLm1hcCgoc2l0ZSkgPT4ge1xuICAgIGNvbnN0IGZpbGUgPSBzaXRlLmdldEZpbGVOYW1lKCkgPz8gJyc7XG4gICAgcmV0dXJuIHtcbiAgICAgIGZ1bmM6IHNpdGUuZ2V0RnVuY3Rpb25OYW1lKCkgPz8gJzxhbm9ueW1vdXM+JyxcbiAgICAgIGZpbGU6IGZpbGUuc3RhcnRzV2l0aCgnZmlsZTovLycpID8gZmlsZVVSTFRvUGF0aChmaWxlKSA6IGZpbGUsXG4gICAgICBsaW5lOiBzaXRlLmdldExpbmVOdW1iZXIoKSA/PyAwLFxuICAgICAgY29sdW1uOiBzaXRlLmdldENvbHVtbk51bWJlcigpID8/IDAsXG4gICAgfTtcbiAgfSk7XG4gIHRyeSB7XG4gICAgRXJyb3IuY2FwdHVyZVN0YWNrVHJhY2UoY2FycmllciwgY2FwdHVyZUNyZWF0aW9uU3RhY2spO1xuICAgIC8vIERyb3AgdGhlIGBpbml0YCBob29rIGZyYW1lLCBhbHdheXMgdGhlIHRvcCBvbmUsIHNvIHRoZSByZXBvcnQgcG9pbnRzIGF0IHRoZVxuICAgIC8vIGNvZGUgdGhhdCBjcmVhdGVkIHRoZSByZXNvdXJjZSByYXRoZXIgdGhhbiBhdCB0aGlzIHRyYWNrZXIuXG4gICAgcmV0dXJuIGNhcnJpZXIuc3RhY2s/LnNsaWNlKDEpID8/IFtdO1xuICB9IGZpbmFsbHkge1xuICAgIEVycm9yLnByZXBhcmVTdGFja1RyYWNlID0gcHJldmlvdXM7XG4gICAgRXJyb3Iuc3RhY2tUcmFjZUxpbWl0ID0gcHJldmlvdXNMaW1pdDtcbiAgfVxufVxuXG4vKipcbiAqIFdoYXQgTm9kZSBpdHNlbGYgcmVwb3J0cyBhcyBzdGlsbCBob2xkaW5nIHRoZSBsb29wIG9wZW4sIGFzIGAyeCBUaW1lb3V0LCAxeCBUQ1BgLlxuICpcbiAqIFRoaXMgaXMgdGhlIGdyb3VuZCB0cnV0aCBvdXIgb3duIHRyYWNraW5nIHRyaWVzIHRvIGV4cGxhaW4sIHNvIHByaW50aW5nIGl0IHRlbGxzXG4gKiB0aGUgcmVhZGVyIHdoZXRoZXIgd2UgbWlzc2VkIHNvbWV0aGluZyBvciB0aGUgaGFuZyBpcyBzb21ld2hlcmUgZWxzZS5cbiAqL1xuZnVuY3Rpb24gYWN0aXZlUmVzb3VyY2VTdW1tYXJ5KCk6IHN0cmluZyB7XG4gIGNvbnN0IGNvdW50cyA9IG5ldyBNYXA8c3RyaW5nLCBudW1iZXI+KCk7XG4gIGZvciAoY29uc3QgdHlwZSBvZiBwcm9jZXNzLmdldEFjdGl2ZVJlc291cmNlc0luZm8oKSkge1xuICAgIGNvdW50cy5zZXQodHlwZSwgKGNvdW50cy5nZXQodHlwZSkgPz8gMCkgKyAxKTtcbiAgfVxuICBpZiAoY291bnRzLnNpemUgPT09IDApIHtcbiAgICByZXR1cm4gJyhub25lKSc7XG4gIH1cbiAgcmV0dXJuIFsuLi5jb3VudHNdLm1hcCgoW3R5cGUsIGNvdW50XSkgPT4gYCR7Y291bnR9eCAke3R5cGV9YCkuam9pbignLCAnKTtcbn1cblxuLyoqXG4gKiBLZWVwIG9ubHkgZnJhbWVzIHRoZSB1c2VyIGNhbiBhY3Qgb24gYnkgZHJvcHBpbmcgTm9kZSBpbnRlcm5hbHMuIE91ciBvd24gYGluaXRgXG4gKiBmcmFtZSBpcyBhbHJlYWR5IHJlbW92ZWQgYXQgY2FwdHVyZSB0aW1lLCBzZWUge0BsaW5rIGNhcHR1cmVDcmVhdGlvblN0YWNrfS5cbiAqL1xuZnVuY3Rpb24gYWN0aW9uYWJsZUZyYW1lcyhmcmFtZXM6IFNvdXJjZUZyYW1lW10pOiBTb3VyY2VGcmFtZVtdIHtcbiAgcmV0dXJuIGZyYW1lcy5maWx0ZXIoKGZyYW1lKSA9PiBmcmFtZS5maWxlICYmICFmcmFtZS5maWxlLnN0YXJ0c1dpdGgoJ25vZGU6JykpO1xufVxuXG4vKipcbiAqIFJlYWRzIHRoZSBsaW5lIG9mIGNvZGUgYmVoaW5kIGEgc3RhY2sgZnJhbWUsIGhvbGRpbmcgZWFjaCBmaWxlJ3MgbGluZXMgc28gYVxuICogcmVwb3J0IHBhc3MgcmVhZHMgYW55IGdpdmVuIGZpbGUgYXQgbW9zdCBvbmNlLlxuICpcbiAqIFRoZSBwdWJsaXNoZWQgQ0xJIGlzIG9uZSBsYXJnZSBidW5kbGUsIGFuZCBhIHNpbmdsZSByZXBvcnQgY2FuIGhvbGQgbWFueSBmcmFtZXNcbiAqIHBvaW50aW5nIGludG8gaXQuIFJlLXJlYWRpbmcgYW5kIHJlLXNwbGl0dGluZyBpdCBwZXIgZnJhbWUgc3RhbGxzIHRoZSByZXBvcnQsIGF0XG4gKiB0aGUgbW9tZW50IHRoZSB1c2VyIGlzIGFscmVhZHkgd2FpdGluZyBvbiBhIGh1bmcgQ0xJLlxuICovXG5jbGFzcyBTb3VyY2VDYWNoZSB7XG4gIC8qKlxuICAgKiBXaGV0aGVyIGFueSBsaW5lIHdlIHJlYWQgd2FzIGxvbmcgZW5vdWdoIHRvIGJlIG1pbmlmaWVkIHJhdGhlciB0aGFuXG4gICAqIGhhbmQtd3JpdHRlbi4gVXNlZCB0byB3YXJuIHRoYXQgdGhlIHByaW50ZWQgbG9jYXRpb25zIGFyZSBidW5kbGUtcmVsYXRpdmUuXG4gICAqL1xuICBwdWJsaWMgc2F3TWluaWZpZWRMaW5lID0gZmFsc2U7XG5cbiAgcHJpdmF0ZSByZWFkb25seSBmaWxlcyA9IG5ldyBNYXA8c3RyaW5nLCBzdHJpbmdbXSB8IHVuZGVmaW5lZD4oKTtcblxuICBwdWJsaWMgc25pcHBldEF0KGZyYW1lOiBTb3VyY2VGcmFtZSk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gICAgY29uc3QgbGluZSA9IHRoaXMubGluZXNPZihmcmFtZS5maWxlKT8uW2ZyYW1lLmxpbmUgLSAxXTtcbiAgICBpZiAobGluZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIH1cbiAgICBpZiAobGluZS5sZW5ndGggPD0gU09VUkNFX1NOSVBQRVRfQ0hBUlMpIHtcbiAgICAgIHJldHVybiBsaW5lLnRyaW0oKSB8fCB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgLy8gVG9vIGxvbmcgdG8gYmUgc291cmNlIGFueW9uZSB3cm90ZSwgc28gc2hvdyBhIHdpbmRvdyBjZW50cmVkIG9uIHRoZSBjb2x1bW5cbiAgICAvLyByYXRoZXIgdGhhbiB0aGUgc3RhcnQgb2YgdGhlIGxpbmUuXG4gICAgdGhpcy5zYXdNaW5pZmllZExpbmUgPSB0cnVlO1xuICAgIGNvbnN0IHN0YXJ0ID0gTWF0aC5tYXgoMCwgZnJhbWUuY29sdW1uIC0gMSAtIE1hdGguZmxvb3IoU09VUkNFX1NOSVBQRVRfQ0hBUlMgLyAyKSk7XG4gICAgY29uc3QgZW5kID0gTWF0aC5taW4obGluZS5sZW5ndGgsIHN0YXJ0ICsgU09VUkNFX1NOSVBQRVRfQ0hBUlMpO1xuICAgIGNvbnN0IHdpbmRvdyA9IGxpbmUuc2xpY2Uoc3RhcnQsIGVuZCkudHJpbSgpO1xuICAgIHJldHVybiBgJHtzdGFydCA+IDAgPyAn4oCmJyA6ICcnfSR7d2luZG93fSR7ZW5kIDwgbGluZS5sZW5ndGggPyAn4oCmJyA6ICcnfWA7XG4gIH1cblxuICBwcml2YXRlIGxpbmVzT2YoZmlsZTogc3RyaW5nKTogc3RyaW5nW10gfCB1bmRlZmluZWQge1xuICAgIGlmICghdGhpcy5maWxlcy5oYXMoZmlsZSkpIHtcbiAgICAgIHRoaXMuZmlsZXMuc2V0KGZpbGUsIHJlYWRMaW5lcyhmaWxlKSk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmZpbGVzLmdldChmaWxlKTtcbiAgfVxufVxuXG5mdW5jdGlvbiByZWFkTGluZXMoZmlsZTogc3RyaW5nKTogc3RyaW5nW10gfCB1bmRlZmluZWQge1xuICB0cnkge1xuICAgIHJldHVybiByZWFkRmlsZVN5bmMoZmlsZSwgJ3V0Zi04Jykuc3BsaXQoL1xccj9cXG4vKTtcbiAgfSBjYXRjaCB7XG4gICAgLy8gQnVuZGxlZCBhbmQgZXZhbCdkIGZyYW1lcyBoYXZlIGZpbGVuYW1lcyB0aGF0IGFyZSBub3Qgb24gZGlzay4gVGhlIGxvY2F0aW9uXG4gICAgLy8gaXMgc3RpbGwgcmVwb3J0ZWQgb24gaXRzIG93biwgc28gcmVwb3J0aW5nIGNvbnRpbnVlcyB3aXRob3V0IHRoZSBzbmlwcGV0LlxuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbn1cblxuLyoqXG4gKiBTdGFydCB3YXRjaGluZyBhc3luYyByZXNvdXJjZXMgYW5kIHJldHVybiB0aGUgdHJhY2tlciBkb2luZyBzby5cbiAqXG4gKiBDYWxsIGFzIGVhcmx5IGFzIHBvc3NpYmxlLCBhbmQgb25seSB3aGVuIHRoZSB1c2VyIGFza2VkIGZvciBpdC4gVGhlIHRyYWNrZXIgY2FuXG4gKiBvbmx5IHJlcG9ydCBvbiByZXNvdXJjZXMgY3JlYXRlZCBhZnRlciBpdCBzdGFydHMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0cmFja0xlYWtlZEhhbmRsZXMoKTogTGVha2VkSGFuZGxlVHJhY2tlciB7XG4gIGNvbnN0IHRyYWNrZXIgPSBuZXcgTGVha2VkSGFuZGxlVHJhY2tlcigpO1xuICB0cmFja2VyLnN0YXJ0KCk7XG4gIHJldHVybiB0cmFja2VyO1xufVxuXG5leHBvcnQgdHlwZSB7IExlYWtlZEhhbmRsZVRyYWNrZXIgfTtcbiJdfQ==