aws-cdk 2.1142.0 → 2.1143.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,347 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.trackLeakedHandles = trackLeakedHandles;
7
+ const node_async_hooks_1 = require("node:async_hooks");
8
+ const node_fs_1 = require("node:fs");
9
+ const node_url_1 = require("node:url");
10
+ const chalk_1 = __importDefault(require("chalk"));
11
+ /**
12
+ * Async resource types we track, with a plain-language description for the report.
13
+ *
14
+ * Only libuv *handles*, the resources that can hold the event loop open. Requests
15
+ * (a DNS lookup, a stream write) have completed by the time the report runs.
16
+ * `PROMISE` and `TickObject` are excluded on volume alone: they dominate every
17
+ * async resource a synth creates, and each one would cost a stack capture.
18
+ *
19
+ * Every type here exposes `hasRef()`, the only way to tell a handle holding the
20
+ * loop open from one already closed. `TLSWRAP` has neither `hasRef()` nor a
21
+ * `destroy` hook, so including it would report every completed HTTPS request as a
22
+ * leak forever. A leaked TLS connection still surfaces as the `TCPWRAP` under it.
23
+ */
24
+ const TRACKED_TYPES = {
25
+ TCPWRAP: 'open network connection',
26
+ TCPSERVERWRAP: 'listening TCP server',
27
+ PIPEWRAP: 'open pipe',
28
+ PIPESERVERWRAP: 'listening pipe server',
29
+ UDPWRAP: 'open UDP socket',
30
+ TTYWRAP: 'open terminal stream',
31
+ Timeout: 'timer from setTimeout or setInterval',
32
+ Immediate: 'pending setImmediate callback',
33
+ PROCESSWRAP: 'spawned child process still running',
34
+ FSEVENTWRAP: 'file-system watcher',
35
+ STATWATCHER: 'file-system stat watcher',
36
+ SIGNALWRAP: 'OS signal handler still registered',
37
+ WORKER: 'worker thread still running',
38
+ MESSAGEPORT: 'open worker-thread message channel',
39
+ };
40
+ /**
41
+ * How many stack frames to capture per resource.
42
+ *
43
+ * Node's default of 10 is too shallow here. A socket opened through an HTTPS agent
44
+ * sits behind enough frames of `node:internal/tls/wrap`, `node:https`,
45
+ * `node:_http_agent` and `node:_http_client` to consume that whole budget, leaving
46
+ * no application frame for the report to show.
47
+ */
48
+ const STACK_CAPTURE_DEPTH = 60;
49
+ /**
50
+ * Grace period before the handle report fires. The timer is unref'd, so it never
51
+ * fires when Node exits cleanly within this window.
52
+ */
53
+ const HANDLE_DUMP_GRACE_MS = 1000;
54
+ /**
55
+ * Cap on how many resources we hold provenance for at once.
56
+ *
57
+ * A long-lived command (`cdk watch`, a deploy of many stacks) opens and closes
58
+ * handles continuously. The `destroy` hook normally removes them again, but types
59
+ * that never fire it would accumulate for the life of the process. The report says
60
+ * when the cap was hit, so a truncated list is not read as a complete one.
61
+ */
62
+ const MAX_TRACKED_HANDLES = 10_000;
63
+ /**
64
+ * How much of a source line to show beneath a stack frame. Long enough to read a
65
+ * statement, short enough that one minified line from the bundled CLI cannot
66
+ * flood the report.
67
+ */
68
+ const SOURCE_SNIPPET_CHARS = 200;
69
+ /**
70
+ * Tracks async resources via async_hooks and, on demand, reports the ones still
71
+ * keeping the event loop alive, with where they were created.
72
+ *
73
+ * Nothing is tracked until `start()` runs. Not exported as a value, so the only
74
+ * way to get one is `trackLeakedHandles()`, which returns a started tracker.
75
+ */
76
+ class LeakedHandleTracker {
77
+ watched = new Map();
78
+ /**
79
+ * Whether we stopped recording because {@link MAX_TRACKED_HANDLES} was reached.
80
+ * Reported, so a short list is never mistaken for a complete one.
81
+ */
82
+ atCapacity = false;
83
+ hook = (0, node_async_hooks_1.createHook)({
84
+ init: (asyncId, type, _triggerAsyncId, resource) => {
85
+ if (!(type in TRACKED_TYPES)) {
86
+ return;
87
+ }
88
+ if (this.watched.size >= MAX_TRACKED_HANDLES) {
89
+ this.atCapacity = true;
90
+ return;
91
+ }
92
+ // An exception thrown from an async_hooks callback is not catchable by the
93
+ // application. Node prints "Error in AsyncHook callback" and aborts the
94
+ // process, so anything that goes wrong here must cost no more than one
95
+ // handle's provenance.
96
+ try {
97
+ this.watched.set(asyncId, {
98
+ type,
99
+ handleRef: new WeakRef(resource),
100
+ creationStack: captureCreationStack(),
101
+ });
102
+ }
103
+ catch {
104
+ this.watched.delete(asyncId);
105
+ }
106
+ },
107
+ destroy: (asyncId) => {
108
+ this.watched.delete(asyncId);
109
+ },
110
+ });
111
+ /**
112
+ * Begin watching async resources. Must run before the resources we care about
113
+ * are created, and only when the user opted in, since the hook adds a small
114
+ * per-resource cost.
115
+ */
116
+ start() {
117
+ this.hook.enable();
118
+ }
119
+ /**
120
+ * Stop watching and discard all tracked state.
121
+ */
122
+ stop() {
123
+ this.hook.disable();
124
+ this.watched.clear();
125
+ }
126
+ /**
127
+ * Report the leaked handles once the grace period has passed. The timer is
128
+ * unref'd, so it never fires if the process exits cleanly within the window and
129
+ * never holds the process open itself.
130
+ */
131
+ scheduleReport(ioHelper) {
132
+ setTimeout(() => {
133
+ // Runs detached from the CLI's error handling: the command has finished and
134
+ // there is no one left to hand a rejection to. A diagnostic must not crash
135
+ // the process it is diagnosing.
136
+ this.report(ioHelper).catch(() => undefined);
137
+ }, HANDLE_DUMP_GRACE_MS).unref();
138
+ }
139
+ /**
140
+ * Report every resource still holding the event loop open, each with the source
141
+ * location where it was created. Call at the very end of execution, by which
142
+ * point only genuinely leaked handles should remain.
143
+ *
144
+ * Emitted as one message, so unrelated output cannot interleave into the middle
145
+ * of a stack trace, and at DEBUG, which is the level `--debug-cli` raises the
146
+ * CLI to.
147
+ */
148
+ async report(ioHelper) {
149
+ this.hook.disable();
150
+ const leaks = [...this.watched.values()].filter((r) => {
151
+ const handle = r.handleRef.deref();
152
+ // Already garbage collected, so no longer keeping the loop alive.
153
+ if (handle === undefined) {
154
+ return false;
155
+ }
156
+ // A fired one-shot timer answers `hasRef() === true` for the rest of the
157
+ // process and only marks itself `_destroyed`. The `destroy` hook does drop
158
+ // it from `watched`, but a tick later than this, and timers are the CLI's
159
+ // most common resource.
160
+ if (handle._destroyed === true) {
161
+ return false;
162
+ }
163
+ // Ask the handle itself, and believe only a clear yes. A closed socket
164
+ // answers false, and anything that cannot answer is not evidence of a leak.
165
+ return typeof handle.hasRef === 'function' && handle.hasRef();
166
+ });
167
+ this.watched.clear();
168
+ await ioHelper.defaults.debug(this.renderReport(leaks).join('\n'));
169
+ }
170
+ renderReport(leaks) {
171
+ if (leaks.length === 0) {
172
+ // This report only runs because the process was still alive, so something
173
+ // is holding the loop open. Finding nothing means the cause is outside what
174
+ // we track, so hand over what Node itself sees.
175
+ return [
176
+ 'The CLI process is still alive, but no tracked handle explains it.',
177
+ `Node reports these resources still active: ${activeResourceSummary()}`,
178
+ 'The cause may be a handle opened before tracking started, or a type this build does not track.',
179
+ ];
180
+ }
181
+ const lines = [`${leaks.length} ${leaks.length === 1 ? 'handle' : 'handles'} still keeping the CLI process alive:`];
182
+ if (this.atCapacity) {
183
+ lines.push(`Stopped recording after ${MAX_TRACKED_HANDLES} handles, so this list may be incomplete.`);
184
+ }
185
+ // One report pass can revisit the same file for every frame of every handle,
186
+ // and in the published CLI that file is the entire bundle.
187
+ const sources = new SourceCache();
188
+ for (const leak of leaks) {
189
+ lines.push(...describeLeak(leak, sources));
190
+ }
191
+ if (sources.sawMinifiedLine) {
192
+ // The published CLI is one bundled, minified file with no source map, so a
193
+ // location is a position in the bundle and the snippet beneath it is the
194
+ // only part that maps back to something recognisable.
195
+ lines.push('');
196
+ lines.push('Locations above are positions in the bundled CLI, not in the original source files.');
197
+ }
198
+ return lines;
199
+ }
200
+ }
201
+ function describeLeak(leak, sources) {
202
+ const frames = actionableFrames(leak.creationStack);
203
+ const lines = ['', chalk_1.default.bold(`# ${leak.type} (${TRACKED_TYPES[leak.type]})`)];
204
+ if (frames.length === 0) {
205
+ lines.push(' (opened entirely inside Node internals, no CLI frames to show)');
206
+ return lines;
207
+ }
208
+ // Headline the function only when it has a real name. Sockets often open from
209
+ // anonymous internal callbacks, where the name says nothing.
210
+ const [origin] = frames;
211
+ if (origin.func && origin.func !== '<anonymous>') {
212
+ lines.push(` created in ${origin.func}()`);
213
+ }
214
+ lines.push(' call stack:');
215
+ for (const frame of frames) {
216
+ // The location is what identifies the frame, so print it even when the file
217
+ // cannot be read.
218
+ lines.push(` ${frame.func} (${frame.file}:${frame.line}:${frame.column})`);
219
+ const snippet = sources.snippetAt(frame);
220
+ if (snippet) {
221
+ lines.push(` ${chalk_1.default.dim(snippet)}`);
222
+ }
223
+ }
224
+ return lines;
225
+ }
226
+ /**
227
+ * Capture the call site of the current async resource as structured frames.
228
+ *
229
+ * Installs a structured `prepareStackTrace`, captures with this function as the
230
+ * cut-off, then restores the previous formatter so other stacks are unaffected.
231
+ *
232
+ * The cut-off removes this function and everything above it, but the caller is the
233
+ * tracker's own `init` hook, which sits just below and would otherwise be the top
234
+ * frame. We drop that frame by position rather than by filename, since after
235
+ * bundling every frame shares one filename.
236
+ */
237
+ function captureCreationStack() {
238
+ const carrier = {};
239
+ // `Error.prepareStackTrace` is a V8 formatting hook we save and restore, not a
240
+ // method we invoke, so the unbound-method concern does not apply.
241
+ // eslint-disable-next-line @typescript-eslint/unbound-method
242
+ const previous = Error.prepareStackTrace;
243
+ const previousLimit = Error.stackTraceLimit;
244
+ Error.stackTraceLimit = STACK_CAPTURE_DEPTH;
245
+ Error.prepareStackTrace = (_error, callSites) => callSites.map((site) => {
246
+ const file = site.getFileName() ?? '';
247
+ return {
248
+ func: site.getFunctionName() ?? '<anonymous>',
249
+ file: file.startsWith('file://') ? (0, node_url_1.fileURLToPath)(file) : file,
250
+ line: site.getLineNumber() ?? 0,
251
+ column: site.getColumnNumber() ?? 0,
252
+ };
253
+ });
254
+ try {
255
+ Error.captureStackTrace(carrier, captureCreationStack);
256
+ // Drop the `init` hook frame, always the top one, so the report points at the
257
+ // code that created the resource rather than at this tracker.
258
+ return carrier.stack?.slice(1) ?? [];
259
+ }
260
+ finally {
261
+ Error.prepareStackTrace = previous;
262
+ Error.stackTraceLimit = previousLimit;
263
+ }
264
+ }
265
+ /**
266
+ * What Node itself reports as still holding the loop open, as `2x Timeout, 1x TCP`.
267
+ *
268
+ * This is the ground truth our own tracking tries to explain, so printing it tells
269
+ * the reader whether we missed something or the hang is somewhere else.
270
+ */
271
+ function activeResourceSummary() {
272
+ const counts = new Map();
273
+ for (const type of process.getActiveResourcesInfo()) {
274
+ counts.set(type, (counts.get(type) ?? 0) + 1);
275
+ }
276
+ if (counts.size === 0) {
277
+ return '(none)';
278
+ }
279
+ return [...counts].map(([type, count]) => `${count}x ${type}`).join(', ');
280
+ }
281
+ /**
282
+ * Keep only frames the user can act on by dropping Node internals. Our own `init`
283
+ * frame is already removed at capture time, see {@link captureCreationStack}.
284
+ */
285
+ function actionableFrames(frames) {
286
+ return frames.filter((frame) => frame.file && !frame.file.startsWith('node:'));
287
+ }
288
+ /**
289
+ * Reads the line of code behind a stack frame, holding each file's lines so a
290
+ * report pass reads any given file at most once.
291
+ *
292
+ * The published CLI is one large bundle, and a single report can hold many frames
293
+ * pointing into it. Re-reading and re-splitting it per frame stalls the report, at
294
+ * the moment the user is already waiting on a hung CLI.
295
+ */
296
+ class SourceCache {
297
+ /**
298
+ * Whether any line we read was long enough to be minified rather than
299
+ * hand-written. Used to warn that the printed locations are bundle-relative.
300
+ */
301
+ sawMinifiedLine = false;
302
+ files = new Map();
303
+ snippetAt(frame) {
304
+ const line = this.linesOf(frame.file)?.[frame.line - 1];
305
+ if (line === undefined) {
306
+ return undefined;
307
+ }
308
+ if (line.length <= SOURCE_SNIPPET_CHARS) {
309
+ return line.trim() || undefined;
310
+ }
311
+ // Too long to be source anyone wrote, so show a window centred on the column
312
+ // rather than the start of the line.
313
+ this.sawMinifiedLine = true;
314
+ const start = Math.max(0, frame.column - 1 - Math.floor(SOURCE_SNIPPET_CHARS / 2));
315
+ const end = Math.min(line.length, start + SOURCE_SNIPPET_CHARS);
316
+ const window = line.slice(start, end).trim();
317
+ return `${start > 0 ? '…' : ''}${window}${end < line.length ? '…' : ''}`;
318
+ }
319
+ linesOf(file) {
320
+ if (!this.files.has(file)) {
321
+ this.files.set(file, readLines(file));
322
+ }
323
+ return this.files.get(file);
324
+ }
325
+ }
326
+ function readLines(file) {
327
+ try {
328
+ return (0, node_fs_1.readFileSync)(file, 'utf-8').split(/\r?\n/);
329
+ }
330
+ catch {
331
+ // Bundled and eval'd frames have filenames that are not on disk. The location
332
+ // is still reported on its own, so reporting continues without the snippet.
333
+ return undefined;
334
+ }
335
+ }
336
+ /**
337
+ * Start watching async resources and return the tracker doing so.
338
+ *
339
+ * Call as early as possible, and only when the user asked for it. The tracker can
340
+ * only report on resources created after it starts.
341
+ */
342
+ function trackLeakedHandles() {
343
+ const tracker = new LeakedHandleTracker();
344
+ tracker.start();
345
+ return tracker;
346
+ }
347
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVidWctaGFuZGxlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImRlYnVnLWhhbmRsZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFrWkEsZ0RBSUM7QUF0WkQsdURBQThDO0FBQzlDLHFDQUF1QztBQUN2Qyx1Q0FBeUM7QUFDekMsa0RBQTBCO0FBRzFCOzs7Ozs7Ozs7Ozs7R0FZRztBQUNILE1BQU0sYUFBYSxHQUFxQztJQUN0RCxPQUFPLEVBQUUseUJBQXlCO0lBQ2xDLGFBQWEsRUFBRSxzQkFBc0I7SUFDckMsUUFBUSxFQUFFLFdBQVc7SUFDckIsY0FBYyxFQUFFLHVCQUF1QjtJQUN2QyxPQUFPLEVBQUUsaUJBQWlCO0lBQzFCLE9BQU8sRUFBRSxzQkFBc0I7SUFDL0IsT0FBTyxFQUFFLHNDQUFzQztJQUMvQyxTQUFTLEVBQUUsK0JBQStCO0lBQzFDLFdBQVcsRUFBRSxxQ0FBcUM7SUFDbEQsV0FBVyxFQUFFLHFCQUFxQjtJQUNsQyxXQUFXLEVBQUUsMEJBQTBCO0lBQ3ZDLFVBQVUsRUFBRSxvQ0FBb0M7SUFDaEQsTUFBTSxFQUFFLDZCQUE2QjtJQUNyQyxXQUFXLEVBQUUsb0NBQW9DO0NBQ2xELENBQUM7QUFFRjs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxtQkFBbUIsR0FBRyxFQUFFLENBQUM7QUFFL0I7OztHQUdHO0FBQ0gsTUFBTSxvQkFBb0IsR0FBRyxJQUFJLENBQUM7QUFFbEM7Ozs7Ozs7R0FPRztBQUNILE1BQU0sbUJBQW1CLEdBQUcsTUFBTSxDQUFDO0FBRW5DOzs7O0dBSUc7QUFDSCxNQUFNLG9CQUFvQixHQUFHLEdBQUcsQ0FBQztBQXFDakM7Ozs7OztHQU1HO0FBQ0gsTUFBTSxtQkFBbUI7SUFDTixPQUFPLEdBQUcsSUFBSSxHQUFHLEVBQTJCLENBQUM7SUFFOUQ7OztPQUdHO0lBQ0ssVUFBVSxHQUFHLEtBQUssQ0FBQztJQUVWLElBQUksR0FBRyxJQUFBLDZCQUFVLEVBQUM7UUFDakMsSUFBSSxFQUFFLENBQUMs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QyxJQUFJLEVBQUUsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDeEMsQ0FBQztRQUNELE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDOUIsQ0FBQztDQUNGO0FBRUQsU0FBUyxTQUFTLENBQUMsSUFBWTtJQUM3QixJQUFJLENBQUM7UUFDSCxPQUFPLElBQUEsc0JBQVksRUFBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ3BELENBQUM7SUFBQyxNQUFNLENBQUM7UUFDUCw4RUFBOEU7UUFDOUUsNEVBQTRFO1FBQzVFLE9BQU8sU0FBUyxDQUFDO0lBQ25CLENBQUM7QUFDSCxDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFnQixrQkFBa0I7SUFDaEMsTUFBTSxPQUFPLEdBQUcsSUFBSSxtQkFBbUIsRUFBRSxDQUFDO0lBQzFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUNoQixPQUFPLE9BQU8sQ0FBQztBQUNqQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgY3JlYXRlSG9vayB9IGZyb20gJ25vZGU6YXN5bmNfaG9va3MnO1xuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnbm9kZTpmcyc7XG5pbXBvcnQgeyBmaWxlVVJMVG9QYXRoIH0gZnJvbSAnbm9kZTp1cmwnO1xuaW1wb3J0IGNoYWxrIGZyb20gJ2NoYWxrJztcbmltcG9ydCB0eXBlIHsgSW9IZWxwZXIgfSBmcm9tICcuLi9hcGktcHJpdmF0ZSc7XG5cbi8qKlxuICogQXN5bmMgcmVzb3VyY2UgdHlwZXMgd2UgdHJhY2ssIHdpdGggYSBwbGFpbi1sYW5ndWFnZSBkZXNjcmlwdGlvbiBmb3IgdGhlIHJlcG9ydC5cbiAqXG4gKiBPbmx5IGxpYnV2ICpoYW5kbGVzKiwgdGhlIHJlc291cmNlcyB0aGF0IGNhbiBob2xkIHRoZSBldmVudCBsb29wIG9wZW4uIFJlcXVlc3RzXG4gKiAoYSBETlMgbG9va3VwLCBhIHN0cmVhbSB3cml0ZSkgaGF2ZSBjb21wbGV0ZWQgYnkgdGhlIHRpbWUgdGhlIHJlcG9ydCBydW5zLlxuICogYFBST01JU0VgIGFuZCBgVGlja09iamVjdGAgYXJlIGV4Y2x1ZGVkIG9uIHZvbHVtZSBhbG9uZTogdGhleSBkb21pbmF0ZSBldmVyeVxuICogYXN5bmMgcmVzb3VyY2UgYSBzeW50aCBjcmVhdGVzLCBhbmQgZWFjaCBvbmUgd291bGQgY29zdCBhIHN0YWNrIGNhcHR1cmUuXG4gKlxuICogRXZlcnkgdHlwZSBoZXJlIGV4cG9zZXMgYGhhc1JlZigpYCwgdGhlIG9ubHkgd2F5IHRvIHRlbGwgYSBoYW5kbGUgaG9sZGluZyB0aGVcbiAqIGxvb3Agb3BlbiBmcm9tIG9uZSBhbHJlYWR5IGNsb3NlZC4gYFRMU1dSQVBgIGhhcyBuZWl0aGVyIGBoYXNSZWYoKWAgbm9yIGFcbiAqIGBkZXN0cm95YCBob29rLCBzbyBpbmNsdWRpbmcgaXQgd291bGQgcmVwb3J0IGV2ZXJ5IGNvbXBsZXRlZCBIVFRQUyByZXF1ZXN0IGFzIGFcbiAqIGxlYWsgZm9yZXZlci4gQSBsZWFrZWQgVExTIGNvbm5lY3Rpb24gc3RpbGwgc3VyZmFjZXMgYXMgdGhlIGBUQ1BXUkFQYCB1bmRlciBpdC5cbiAqL1xuY29uc3QgVFJBQ0tFRF9UWVBFUzogUmVhZG9ubHk8UmVjb3JkPHN0cmluZywgc3RyaW5nPj4gPSB7XG4gIFRDUFdSQVA6ICdvcGVuIG5ldHdvcmsgY29ubmVjdGlvbicsXG4gIFRDUFNFUlZFUldSQVA6ICdsaXN0ZW5pbmcgVENQIHNlcnZlcicsXG4gIFBJUEVXUkFQOiAnb3BlbiBwaXBlJyxcbiAgUElQRVNFUlZFUldSQVA6ICdsaXN0ZW5pbmcgcGlwZSBzZXJ2ZXInLFxuICBVRFBXUkFQOiAnb3BlbiBVRFAgc29ja2V0JyxcbiAgVFRZV1JBUDogJ29wZW4gdGVybWluYWwgc3RyZWFtJyxcbiAgVGltZW91dDogJ3RpbWVyIGZyb20gc2V0VGltZW91dCBvciBzZXRJbnRlcnZhbCcsXG4gIEltbWVkaWF0ZTogJ3BlbmRpbmcgc2V0SW1tZWRpYXRlIGNhbGxiYWNrJyxcbiAgUFJPQ0VTU1dSQVA6ICdzcGF3bmVkIGNoaWxkIHByb2Nlc3Mgc3RpbGwgcnVubmluZycsXG4gIEZTRVZFTlRXUkFQOiAnZmlsZS1zeXN0ZW0gd2F0Y2hlcicsXG4gIFNUQVRXQVRDSEVSOiAnZmlsZS1zeXN0ZW0gc3RhdCB3YXRjaGVyJyxcbiAgU0lHTkFMV1JBUDogJ09TIHNpZ25hbCBoYW5kbGVyIHN0aWxsIHJlZ2lzdGVyZWQnLFxuICBXT1JLRVI6ICd3b3JrZXIgdGhyZWFkIHN0aWxsIHJ1bm5pbmcnLFxuICBNRVNTQUdFUE9SVDogJ29wZW4gd29ya2VyLXRocmVhZCBtZXNzYWdlIGNoYW5uZWwnLFxufTtcblxuLyoqXG4gKiBIb3cgbWFueSBzdGFjayBmcmFtZXMgdG8gY2FwdHVyZSBwZXIgcmVzb3VyY2UuXG4gKlxuICogTm9kZSdzIGRlZmF1bHQgb2YgMTAgaXMgdG9vIHNoYWxsb3cgaGVyZS4gQSBzb2NrZXQgb3BlbmVkIHRocm91Z2ggYW4gSFRUUFMgYWdlbnRcbiAqIHNpdHMgYmVoaW5kIGVub3VnaCBmcmFtZXMgb2YgYG5vZGU6aW50ZXJuYWwvdGxzL3dyYXBgLCBgbm9kZTpodHRwc2AsXG4gKiBgbm9kZTpfaHR0cF9hZ2VudGAgYW5kIGBub2RlOl9odHRwX2NsaWVudGAgdG8gY29uc3VtZSB0aGF0IHdob2xlIGJ1ZGdldCwgbGVhdmluZ1xuICogbm8gYXBwbGljYXRpb24gZnJhbWUgZm9yIHRoZSByZXBvcnQgdG8gc2hvdy5cbiAqL1xuY29uc3QgU1RBQ0tfQ0FQVFVSRV9ERVBUSCA9IDYwO1xuXG4vKipcbiAqIEdyYWNlIHBlcmlvZCBiZWZvcmUgdGhlIGhhbmRsZSByZXBvcnQgZmlyZXMuIFRoZSB0aW1lciBpcyB1bnJlZidkLCBzbyBpdCBuZXZlclxuICogZmlyZXMgd2hlbiBOb2RlIGV4aXRzIGNsZWFubHkgd2l0aGluIHRoaXMgd2luZG93LlxuICovXG5jb25zdCBIQU5ETEVfRFVNUF9HUkFDRV9NUyA9IDEwMDA7XG5cbi8qKlxuICogQ2FwIG9uIGhvdyBtYW55IHJlc291cmNlcyB3ZSBob2xkIHByb3ZlbmFuY2UgZm9yIGF0IG9uY2UuXG4gKlxuICogQSBsb25nLWxpdmVkIGNvbW1hbmQgKGBjZGsgd2F0Y2hgLCBhIGRlcGxveSBvZiBtYW55IHN0YWNrcykgb3BlbnMgYW5kIGNsb3Nlc1xuICogaGFuZGxlcyBjb250aW51b3VzbHkuIFRoZSBgZGVzdHJveWAgaG9vayBub3JtYWxseSByZW1vdmVzIHRoZW0gYWdhaW4sIGJ1dCB0eXBlc1xuICogdGhhdCBuZXZlciBmaXJlIGl0IHdvdWxkIGFjY3VtdWxhdGUgZm9yIHRoZSBsaWZlIG9mIHRoZSBwcm9jZXNzLiBUaGUgcmVwb3J0IHNheXNcbiAqIHdoZW4gdGhlIGNhcCB3YXMgaGl0LCBzbyBhIHRydW5jYXRlZCBsaXN0IGlzIG5vdCByZWFkIGFzIGEgY29tcGxldGUgb25lLlxuICovXG5jb25zdCBNQVhfVFJBQ0tFRF9IQU5ETEVTID0gMTBfMDAwO1xuXG4vKipcbiAqIEhvdyBtdWNoIG9mIGEgc291cmNlIGxpbmUgdG8gc2hvdyBiZW5lYXRoIGEgc3RhY2sgZnJhbWUuIExvbmcgZW5vdWdoIHRvIHJlYWQgYVxuICogc3RhdGVtZW50LCBzaG9ydCBlbm91Z2ggdGhhdCBvbmUgbWluaWZpZWQgbGluZSBmcm9tIHRoZSBidW5kbGVkIENMSSBjYW5ub3RcbiAqIGZsb29kIHRoZSByZXBvcnQuXG4gKi9cbmNvbnN0IFNPVVJDRV9TTklQUEVUX0NIQVJTID0gMjAwO1xuXG4vKipcbiAqIE9uZSBzdGFjayBmcmFtZTogdGhlIGZ1bmN0aW9uIG5hbWUsIHBsdXMgdGhlIGZpbGUsIGxpbmUgYW5kIGNvbHVtbiwgdXNlZCBib3RoIHRvXG4gKiBwcmludCB0aGUgbG9jYXRpb24gYW5kIHRvIHJlYWQgYmFjayB0aGUgbGluZSBvZiBjb2RlLlxuICpcbiAqIFRoZSBjb2x1bW4gaXMgbG9hZC1iZWFyaW5nLiBJbiB0aGUgcHVibGlzaGVkIENMSSBldmVyeSBmcmFtZSBwb2ludHMgaW50byBvbmVcbiAqIGJ1bmRsZWQsIHdoaXRlc3BhY2UtbWluaWZpZWQgZmlsZSwgd2hlcmUgdGhlIGxpbmUgbnVtYmVyIGJhcmVseSBkaXN0aW5ndWlzaGVzXG4gKiBvbmUgZnJhbWUgZnJvbSBhbm90aGVyLlxuICovXG5pbnRlcmZhY2UgU291cmNlRnJhbWUge1xuICByZWFkb25seSBmdW5jOiBzdHJpbmc7XG4gIHJlYWRvbmx5IGZpbGU6IHN0cmluZztcbiAgcmVhZG9ubHkgbGluZTogbnVtYmVyO1xuICByZWFkb25seSBjb2x1bW46IG51bWJlcjtcbn1cblxuLyoqXG4gKiBBIHJlc291cmNlIHdlIGFyZSB3YXRjaGluZzogYSB3ZWFrIHJlZmVyZW5jZSB0byB0aGUgaGFuZGxlLCBzbyB0cmFja2luZyBkb2VzIG5vdFxuICogaXRzZWxmIGtlZXAgaXQgYWxpdmUsIGFuZCB0aGUgc3RhY2sgb2Ygd2hlcmUgaXQgd2FzIGNyZWF0ZWQuXG4gKi9cbmludGVyZmFjZSBXYXRjaGVkUmVzb3VyY2Uge1xuICByZWFkb25seSB0eXBlOiBzdHJpbmc7XG4gIHJlYWRvbmx5IGhhbmRsZVJlZjogV2Vha1JlZjxUcmFja2VkSGFuZGxlPjtcbiAgcmVhZG9ubHkgY3JlYXRpb25TdGFjazogU291cmNlRnJhbWVbXTtcbn1cblxuLyoqXG4gKiBUaGUgcGFydHMgb2YgYSBsaWJ1diBoYW5kbGUgd2UgaW50ZXJyb2dhdGUuIEJvdGggYXJlIG9wdGlvbmFsIGJlY2F1c2UgbmVpdGhlciBpc1xuICogZ3VhcmFudGVlZDogYGhhc1JlZmAgZXhpc3RzIG9ubHkgb24gYEhhbmRsZVdyYXBgLWJhY2tlZCB0eXBlcywgYW5kIGBfZGVzdHJveWVkYFxuICogaXMgYSBOb2RlIGludGVybmFsIHRoYXQgb25seSB0aW1lcnMgc2V0LlxuICovXG5pbnRlcmZhY2UgVHJhY2tlZEhhbmRsZSB7XG4gIF9kZXN0cm95ZWQ/OiBib29sZWFuO1xuICBoYXNSZWY/KCk6IGJvb2xlYW47XG59XG5cbi8qKlxuICogVHJhY2tzIGFzeW5jIHJlc291cmNlcyB2aWEgYXN5bmNfaG9va3MgYW5kLCBvbiBkZW1hbmQsIHJlcG9ydHMgdGhlIG9uZXMgc3RpbGxcbiAqIGtlZXBpbmcgdGhlIGV2ZW50IGxvb3AgYWxpdmUsIHdpdGggd2hlcmUgdGhleSB3ZXJlIGNyZWF0ZWQuXG4gKlxuICogTm90aGluZyBpcyB0cmFja2VkIHVudGlsIGBzdGFydCgpYCBydW5zLiBOb3QgZXhwb3J0ZWQgYXMgYSB2YWx1ZSwgc28gdGhlIG9ubHlcbiAqIHdheSB0byBnZXQgb25lIGlzIGB0cmFja0xlYWtlZEhhbmRsZXMoKWAsIHdoaWNoIHJldHVybnMgYSBzdGFydGVkIHRyYWNrZXIuXG4gKi9cbmNsYXNzIExlYWtlZEhhbmRsZVRyYWNrZXIge1xuICBwcml2YXRlIHJlYWRvbmx5IHdhdGNoZWQgPSBuZXcgTWFwPG51bWJlciwgV2F0Y2hlZFJlc291cmNlPigpO1xuXG4gIC8qKlxuICAgKiBXaGV0aGVyIHdlIHN0b3BwZWQgcmVjb3JkaW5nIGJlY2F1c2Uge0BsaW5rIE1BWF9UUkFDS0VEX0hBTkRMRVN9IHdhcyByZWFjaGVkLlxuICAgKiBSZXBvcnRlZCwgc28gYSBzaG9ydCBsaXN0IGlzIG5ldmVyIG1pc3Rha2VuIGZvciBhIGNvbXBsZXRlIG9uZS5cbiAgICovXG4gIHByaXZhdGUgYXRDYXBhY2l0eSA9IGZhbHNlO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgaG9vayA9IGNyZWF0ZUhvb2soe1xuICAgIGluaXQ6IChhc3luY0lkLCB0eXBlLCBfdHJpZ2dlckFzeW5jSWQsIHJlc291cmNlKSA9PiB7XG4gICAgICBpZiAoISh0eXBlIGluIFRSQUNLRURfVFlQRVMpKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGlmICh0aGlzLndhdGNoZWQuc2l6ZSA+PSBNQVhfVFJBQ0tFRF9IQU5ETEVTKSB7XG4gICAgICAgIHRoaXMuYXRDYXBhY2l0eSA9IHRydWU7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIC8vIEFuIGV4Y2VwdGlvbiB0aHJvd24gZnJvbSBhbiBhc3luY19ob29rcyBjYWxsYmFjayBpcyBub3QgY2F0Y2hhYmxlIGJ5IHRoZVxuICAgICAgLy8gYXBwbGljYXRpb24uIE5vZGUgcHJpbnRzIFwiRXJyb3IgaW4gQXN5bmNIb29rIGNhbGxiYWNrXCIgYW5kIGFib3J0cyB0aGVcbiAgICAgIC8vIHByb2Nlc3MsIHNvIGFueXRoaW5nIHRoYXQgZ29lcyB3cm9uZyBoZXJlIG11c3QgY29zdCBubyBtb3JlIHRoYW4gb25lXG4gICAgICAvLyBoYW5kbGUncyBwcm92ZW5hbmNlLlxuICAgICAgdHJ5IHtcbiAgICAgICAgdGhpcy53YXRjaGVkLnNldChhc3luY0lkLCB7XG4gICAgICAgICAgdHlwZSxcbiAgICAgICAgICBoYW5kbGVSZWY6IG5ldyBXZWFrUmVmKHJlc291cmNlIGFzIFRyYWNrZWRIYW5kbGUpLFxuICAgICAgICAgIGNyZWF0aW9uU3RhY2s6IGNhcHR1cmVDcmVhdGlvblN0YWNrKCksXG4gICAgICAgIH0pO1xuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIHRoaXMud2F0Y2hlZC5kZWxldGUoYXN5bmNJZCk7XG4gICAgICB9XG4gICAgfSxcbiAgICBkZXN0cm95OiAoYXN5bmNJZCkgPT4ge1xuICAgICAgdGhpcy53YXRjaGVkLmRlbGV0ZShhc3luY0lkKTtcbiAgICB9LFxuICB9KTtcblxuICAvKipcbiAgICogQmVnaW4gd2F0Y2hpbmcgYXN5bmMgcmVzb3VyY2VzLiBNdXN0IHJ1biBiZWZvcmUgdGhlIHJlc291cmNlcyB3ZSBjYXJlIGFib3V0XG4gICAqIGFyZSBjcmVhdGVkLCBhbmQgb25seSB3aGVuIHRoZSB1c2VyIG9wdGVkIGluLCBzaW5jZSB0aGUgaG9vayBhZGRzIGEgc21hbGxcbiAgICogcGVyLXJlc291cmNlIGNvc3QuXG4gICAqL1xuICBwdWJsaWMgc3RhcnQoKTogdm9pZCB7XG4gICAgdGhpcy5ob29rLmVuYWJsZSgpO1xuICB9XG5cbiAgLyoqXG4gICAqIFN0b3Agd2F0Y2hpbmcgYW5kIGRpc2NhcmQgYWxsIHRyYWNrZWQgc3RhdGUuXG4gICAqL1xuICBwdWJsaWMgc3RvcCgpOiB2b2lkIHtcbiAgICB0aGlzLmhvb2suZGlzYWJsZSgpO1xuICAgIHRoaXMud2F0Y2hlZC5jbGVhcigpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlcG9ydCB0aGUgbGVha2VkIGhhbmRsZXMgb25jZSB0aGUgZ3JhY2UgcGVyaW9kIGhhcyBwYXNzZWQuIFRoZSB0aW1lciBpc1xuICAgKiB1bnJlZidkLCBzbyBpdCBuZXZlciBmaXJlcyBpZiB0aGUgcHJvY2VzcyBleGl0cyBjbGVhbmx5IHdpdGhpbiB0aGUgd2luZG93IGFuZFxuICAgKiBuZXZlciBob2xkcyB0aGUgcHJvY2VzcyBvcGVuIGl0c2VsZi5cbiAgICovXG4gIHB1YmxpYyBzY2hlZHVsZVJlcG9ydChpb0hlbHBlcjogSW9IZWxwZXIpOiB2b2lkIHtcbiAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIC8vIFJ1bnMgZGV0YWNoZWQgZnJvbSB0aGUgQ0xJJ3MgZXJyb3IgaGFuZGxpbmc6IHRoZSBjb21tYW5kIGhhcyBmaW5pc2hlZCBhbmRcbiAgICAgIC8vIHRoZXJlIGlzIG5vIG9uZSBsZWZ0IHRvIGhhbmQgYSByZWplY3Rpb24gdG8uIEEgZGlhZ25vc3RpYyBtdXN0IG5vdCBjcmFzaFxuICAgICAgLy8gdGhlIHByb2Nlc3MgaXQgaXMgZGlhZ25vc2luZy5cbiAgICAgIHRoaXMucmVwb3J0KGlvSGVscGVyKS5jYXRjaCgoKSA9PiB1bmRlZmluZWQpO1xuICAgIH0sIEhBTkRMRV9EVU1QX0dSQUNFX01TKS51bnJlZigpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlcG9ydCBldmVyeSByZXNvdXJjZSBzdGlsbCBob2xkaW5nIHRoZSBldmVudCBsb29wIG9wZW4sIGVhY2ggd2l0aCB0aGUgc291cmNlXG4gICAqIGxvY2F0aW9uIHdoZXJlIGl0IHdhcyBjcmVhdGVkLiBDYWxsIGF0IHRoZSB2ZXJ5IGVuZCBvZiBleGVjdXRpb24sIGJ5IHdoaWNoXG4gICAqIHBvaW50IG9ubHkgZ2VudWluZWx5IGxlYWtlZCBoYW5kbGVzIHNob3VsZCByZW1haW4uXG4gICAqXG4gICAqIEVtaXR0ZWQgYXMgb25lIG1lc3NhZ2UsIHNvIHVucmVsYXRlZCBvdXRwdXQgY2Fubm90IGludGVybGVhdmUgaW50byB0aGUgbWlkZGxlXG4gICAqIG9mIGEgc3RhY2sgdHJhY2UsIGFuZCBhdCBERUJVRywgd2hpY2ggaXMgdGhlIGxldmVsIGAtLWRlYnVnLWNsaWAgcmFpc2VzIHRoZVxuICAgKiBDTEkgdG8uXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgcmVwb3J0KGlvSGVscGVyOiBJb0hlbHBlcik6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuaG9vay5kaXNhYmxlKCk7XG5cbiAgICBjb25zdCBsZWFrcyA9IFsuLi50aGlzLndhdGNoZWQudmFsdWVzKCldLmZpbHRlcigocikgPT4ge1xuICAgICAgY29uc3QgaGFuZGxlID0gci5oYW5kbGVSZWYuZGVyZWYoKTtcbiAgICAgIC8vIEFscmVhZHkgZ2FyYmFnZSBjb2xsZWN0ZWQsIHNvIG5vIGxvbmdlciBrZWVwaW5nIHRoZSBsb29wIGFsaXZlLlxuICAgICAgaWYgKGhhbmRsZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICAgIC8vIEEgZmlyZWQgb25lLXNob3QgdGltZXIgYW5zd2VycyBgaGFzUmVmKCkgPT09IHRydWVgIGZvciB0aGUgcmVzdCBvZiB0aGVcbiAgICAgIC8vIHByb2Nlc3MgYW5kIG9ubHkgbWFya3MgaXRzZWxmIGBfZGVzdHJveWVkYC4gVGhlIGBkZXN0cm95YCBob29rIGRvZXMgZHJvcFxuICAgICAgLy8gaXQgZnJvbSBgd2F0Y2hlZGAsIGJ1dCBhIHRpY2sgbGF0ZXIgdGhhbiB0aGlzLCBhbmQgdGltZXJzIGFyZSB0aGUgQ0xJJ3NcbiAgICAgIC8vIG1vc3QgY29tbW9uIHJlc291cmNlLlxuICAgICAgaWYgKGhhbmRsZS5fZGVzdHJveWVkID09PSB0cnVlKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICAgIC8vIEFzayB0aGUgaGFuZGxlIGl0c2VsZiwgYW5kIGJlbGlldmUgb25seSBhIGNsZWFyIHllcy4gQSBjbG9zZWQgc29ja2V0XG4gICAgICAvLyBhbnN3ZXJzIGZhbHNlLCBhbmQgYW55dGhpbmcgdGhhdCBjYW5ub3QgYW5zd2VyIGlzIG5vdCBldmlkZW5jZSBvZiBhIGxlYWsuXG4gICAgICByZXR1cm4gdHlwZW9mIGhhbmRsZS5oYXNSZWYgPT09ICdmdW5jdGlvbicgJiYgaGFuZGxlLmhhc1JlZigpO1xuICAgIH0pO1xuICAgIHRoaXMud2F0Y2hlZC5jbGVhcigpO1xuXG4gICAgYXdhaXQgaW9IZWxwZXIuZGVmYXVsdHMuZGVidWcodGhpcy5yZW5kZXJSZXBvcnQobGVha3MpLmpvaW4oJ1xcbicpKTtcbiAgfVxuXG4gIHByaXZhdGUgcmVuZGVyUmVwb3J0KGxlYWtzOiBXYXRjaGVkUmVzb3VyY2VbXSk6IHN0cmluZ1tdIHtcbiAgICBpZiAobGVha3MubGVuZ3RoID09PSAwKSB7XG4gICAgICAvLyBUaGlzIHJlcG9ydCBvbmx5IHJ1bnMgYmVjYXVzZSB0aGUgcHJvY2VzcyB3YXMgc3RpbGwgYWxpdmUsIHNvIHNvbWV0aGluZ1xuICAgICAgLy8gaXMgaG9sZGluZyB0aGUgbG9vcCBvcGVuLiBGaW5kaW5nIG5vdGhpbmcgbWVhbnMgdGhlIGNhdXNlIGlzIG91dHNpZGUgd2hhdFxuICAgICAgLy8gd2UgdHJhY2ssIHNvIGhhbmQgb3ZlciB3aGF0IE5vZGUgaXRzZWxmIHNlZXMuXG4gICAgICByZXR1cm4gW1xuICAgICAgICAnVGhlIENMSSBwcm9jZXNzIGlzIHN0aWxsIGFsaXZlLCBidXQgbm8gdHJhY2tlZCBoYW5kbGUgZXhwbGFpbnMgaXQuJyxcbiAgICAgICAgYE5vZGUgcmVwb3J0cyB0aGVzZSByZXNvdXJjZXMgc3RpbGwgYWN0aXZlOiAke2FjdGl2ZVJlc291cmNlU3VtbWFyeSgpfWAsXG4gICAgICAgICdUaGUgY2F1c2UgbWF5IGJlIGEgaGFuZGxlIG9wZW5lZCBiZWZvcmUgdHJhY2tpbmcgc3RhcnRlZCwgb3IgYSB0eXBlIHRoaXMgYnVpbGQgZG9lcyBub3QgdHJhY2suJyxcbiAgICAgIF07XG4gICAgfVxuXG4gICAgY29uc3QgbGluZXMgPSBbYCR7bGVha3MubGVuZ3RofSAke2xlYWtzLmxlbmd0aCA9PT0gMSA/ICdoYW5kbGUnIDogJ2hhbmRsZXMnfSBzdGlsbCBrZWVwaW5nIHRoZSBDTEkgcHJvY2VzcyBhbGl2ZTpgXTtcbiAgICBpZiAodGhpcy5hdENhcGFjaXR5KSB7XG4gICAgICBsaW5lcy5wdXNoKGBTdG9wcGVkIHJlY29yZGluZyBhZnRlciAke01BWF9UUkFDS0VEX0hBTkRMRVN9IGhhbmRsZXMsIHNvIHRoaXMgbGlzdCBtYXkgYmUgaW5jb21wbGV0ZS5gKTtcbiAgICB9XG5cbiAgICAvLyBPbmUgcmVwb3J0IHBhc3MgY2FuIHJldmlzaXQgdGhlIHNhbWUgZmlsZSBmb3IgZXZlcnkgZnJhbWUgb2YgZXZlcnkgaGFuZGxlLFxuICAgIC8vIGFuZCBpbiB0aGUgcHVibGlzaGVkIENMSSB0aGF0IGZpbGUgaXMgdGhlIGVudGlyZSBidW5kbGUuXG4gICAgY29uc3Qgc291cmNlcyA9IG5ldyBTb3VyY2VDYWNoZSgpO1xuICAgIGZvciAoY29uc3QgbGVhayBvZiBsZWFrcykge1xuICAgICAgbGluZXMucHVzaCguLi5kZXNjcmliZUxlYWsobGVhaywgc291cmNlcykpO1xuICAgIH1cblxuICAgIGlmIChzb3VyY2VzLnNhd01pbmlmaWVkTGluZSkge1xuICAgICAgLy8gVGhlIHB1Ymxpc2hlZCBDTEkgaXMgb25lIGJ1bmRsZWQsIG1pbmlmaWVkIGZpbGUgd2l0aCBubyBzb3VyY2UgbWFwLCBzbyBhXG4gICAgICAvLyBsb2NhdGlvbiBpcyBhIHBvc2l0aW9uIGluIHRoZSBidW5kbGUgYW5kIHRoZSBzbmlwcGV0IGJlbmVhdGggaXQgaXMgdGhlXG4gICAgICAvLyBvbmx5IHBhcnQgdGhhdCBtYXBzIGJhY2sgdG8gc29tZXRoaW5nIHJlY29nbmlzYWJsZS5cbiAgICAgIGxpbmVzLnB1c2goJycpO1xuICAgICAgbGluZXMucHVzaCgnTG9jYXRpb25zIGFib3ZlIGFyZSBwb3NpdGlvbnMgaW4gdGhlIGJ1bmRsZWQgQ0xJLCBub3QgaW4gdGhlIG9yaWdpbmFsIHNvdXJjZSBmaWxlcy4nKTtcbiAgICB9XG5cbiAgICByZXR1cm4gbGluZXM7XG4gIH1cbn1cblxuZnVuY3Rpb24gZGVzY3JpYmVMZWFrKGxlYWs6IFdhdGNoZWRSZXNvdXJjZSwgc291cmNlczogU291cmNlQ2FjaGUpOiBzdHJpbmdbXSB7XG4gIGNvbnN0IGZyYW1lcyA9IGFjdGlvbmFibGVGcmFtZXMobGVhay5jcmVhdGlvblN0YWNrKTtcbiAgY29uc3QgbGluZXMgPSBbJycsIGNoYWxrLmJvbGQoYCMgJHtsZWFrLnR5cGV9ICgke1RSQUNLRURfVFlQRVNbbGVhay50eXBlXX0pYCldO1xuXG4gIGlmIChmcmFtZXMubGVuZ3RoID09PSAwKSB7XG4gICAgbGluZXMucHVzaCgnICAob3BlbmVkIGVudGlyZWx5IGluc2lkZSBOb2RlIGludGVybmFscywgbm8gQ0xJIGZyYW1lcyB0byBzaG93KScpO1xuICAgIHJldHVybiBsaW5lcztcbiAgfVxuXG4gIC8vIEhlYWRsaW5lIHRoZSBmdW5jdGlvbiBvbmx5IHdoZW4gaXQgaGFzIGEgcmVhbCBuYW1lLiBTb2NrZXRzIG9mdGVuIG9wZW4gZnJvbVxuICAvLyBhbm9ueW1vdXMgaW50ZXJuYWwgY2FsbGJhY2tzLCB3aGVyZSB0aGUgbmFtZSBzYXlzIG5vdGhpbmcuXG4gIGNvbnN0IFtvcmlnaW5dID0gZnJhbWVzO1xuICBpZiAob3JpZ2luLmZ1bmMgJiYgb3JpZ2luLmZ1bmMgIT09ICc8YW5vbnltb3VzPicpIHtcbiAgICBsaW5lcy5wdXNoKGAgIGNyZWF0ZWQgaW4gJHtvcmlnaW4uZnVuY30oKWApO1xuICB9XG5cbiAgbGluZXMucHVzaCgnICBjYWxsIHN0YWNrOicpO1xuICBmb3IgKGNvbnN0IGZyYW1lIG9mIGZyYW1lcykge1xuICAgIC8vIFRoZSBsb2NhdGlvbiBpcyB3aGF0IGlkZW50aWZpZXMgdGhlIGZyYW1lLCBzbyBwcmludCBpdCBldmVuIHdoZW4gdGhlIGZpbGVcbiAgICAvLyBjYW5ub3QgYmUgcmVhZC5cbiAgICBsaW5lcy5wdXNoKGAgICAgJHtmcmFtZS5mdW5jfSAoJHtmcmFtZS5maWxlfToke2ZyYW1lLmxpbmV9OiR7ZnJhbWUuY29sdW1ufSlgKTtcbiAgICBjb25zdCBzbmlwcGV0ID0gc291cmNlcy5zbmlwcGV0QXQoZnJhbWUpO1xuICAgIGlmIChzbmlwcGV0KSB7XG4gICAgICBsaW5lcy5wdXNoKGAgICAgICAke2NoYWxrLmRpbShzbmlwcGV0KX1gKTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIGxpbmVzO1xufVxuXG4vKipcbiAqIENhcHR1cmUgdGhlIGNhbGwgc2l0ZSBvZiB0aGUgY3VycmVudCBhc3luYyByZXNvdXJjZSBhcyBzdHJ1Y3R1cmVkIGZyYW1lcy5cbiAqXG4gKiBJbnN0YWxscyBhIHN0cnVjdHVyZWQgYHByZXBhcmVTdGFja1RyYWNlYCwgY2FwdHVyZXMgd2l0aCB0aGlzIGZ1bmN0aW9uIGFzIHRoZVxuICogY3V0LW9mZiwgdGhlbiByZXN0b3JlcyB0aGUgcHJldmlvdXMgZm9ybWF0dGVyIHNvIG90aGVyIHN0YWNrcyBhcmUgdW5hZmZlY3RlZC5cbiAqXG4gKiBUaGUgY3V0LW9mZiByZW1vdmVzIHRoaXMgZnVuY3Rpb24gYW5kIGV2ZXJ5dGhpbmcgYWJvdmUgaXQsIGJ1dCB0aGUgY2FsbGVyIGlzIHRoZVxuICogdHJhY2tlcidzIG93biBgaW5pdGAgaG9vaywgd2hpY2ggc2l0cyBqdXN0IGJlbG93IGFuZCB3b3VsZCBvdGhlcndpc2UgYmUgdGhlIHRvcFxuICogZnJhbWUuIFdlIGRyb3AgdGhhdCBmcmFtZSBieSBwb3NpdGlvbiByYXRoZXIgdGhhbiBieSBmaWxlbmFtZSwgc2luY2UgYWZ0ZXJcbiAqIGJ1bmRsaW5nIGV2ZXJ5IGZyYW1lIHNoYXJlcyBvbmUgZmlsZW5hbWUuXG4gKi9cbmZ1bmN0aW9uIGNhcHR1cmVDcmVhdGlvblN0YWNrKCk6IFNvdXJjZUZyYW1lW10ge1xuICBjb25zdCBjYXJyaWVyOiB7IHN0YWNrPzogU291cmNlRnJhbWVbXSB9ID0ge307XG5cbiAgLy8gYEVycm9yLnByZXBhcmVTdGFja1RyYWNlYCBpcyBhIFY4IGZvcm1hdHRpbmcgaG9vayB3ZSBzYXZlIGFuZCByZXN0b3JlLCBub3QgYVxuICAvLyBtZXRob2Qgd2UgaW52b2tlLCBzbyB0aGUgdW5ib3VuZC1tZXRob2QgY29uY2VybiBkb2VzIG5vdCBhcHBseS5cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC91bmJvdW5kLW1ldGhvZFxuICBjb25zdCBwcmV2aW91cyA9IEVycm9yLnByZXBhcmVTdGFja1RyYWNlO1xuICBjb25zdCBwcmV2aW91c0xpbWl0ID0gRXJyb3Iuc3RhY2tUcmFjZUxpbWl0O1xuICBFcnJvci5zdGFja1RyYWNlTGltaXQgPSBTVEFDS19DQVBUVVJFX0RFUFRIO1xuICBFcnJvci5wcmVwYXJlU3RhY2tUcmFjZSA9IChfZXJyb3IsIGNhbGxTaXRlcykgPT4gY2FsbFNpdGVzLm1hcCgoc2l0ZSkgPT4ge1xuICAgIGNvbnN0IGZpbGUgPSBzaXRlLmdldEZpbGVOYW1lKCkgPz8gJyc7XG4gICAgcmV0dXJuIHtcbiAgICAgIGZ1bmM6IHNpdGUuZ2V0RnVuY3Rpb25OYW1lKCkgPz8gJzxhbm9ueW1vdXM+JyxcbiAgICAgIGZpbGU6IGZpbGUuc3RhcnRzV2l0aCgnZmlsZTovLycpID8gZmlsZVVSTFRvUGF0aChmaWxlKSA6IGZpbGUsXG4gICAgICBsaW5lOiBzaXRlLmdldExpbmVOdW1iZXIoKSA/PyAwLFxuICAgICAgY29sdW1uOiBzaXRlLmdldENvbHVtbk51bWJlcigpID8/IDAsXG4gICAgfTtcbiAgfSk7XG4gIHRyeSB7XG4gICAgRXJyb3IuY2FwdHVyZVN0YWNrVHJhY2UoY2FycmllciwgY2FwdHVyZUNyZWF0aW9uU3RhY2spO1xuICAgIC8vIERyb3AgdGhlIGBpbml0YCBob29rIGZyYW1lLCBhbHdheXMgdGhlIHRvcCBvbmUsIHNvIHRoZSByZXBvcnQgcG9pbnRzIGF0IHRoZVxuICAgIC8vIGNvZGUgdGhhdCBjcmVhdGVkIHRoZSByZXNvdXJjZSByYXRoZXIgdGhhbiBhdCB0aGlzIHRyYWNrZXIuXG4gICAgcmV0dXJuIGNhcnJpZXIuc3RhY2s/LnNsaWNlKDEpID8/IFtdO1xuICB9IGZpbmFsbHkge1xuICAgIEVycm9yLnByZXBhcmVTdGFja1RyYWNlID0gcHJldmlvdXM7XG4gICAgRXJyb3Iuc3RhY2tUcmFjZUxpbWl0ID0gcHJldmlvdXNMaW1pdDtcbiAgfVxufVxuXG4vKipcbiAqIFdoYXQgTm9kZSBpdHNlbGYgcmVwb3J0cyBhcyBzdGlsbCBob2xkaW5nIHRoZSBsb29wIG9wZW4sIGFzIGAyeCBUaW1lb3V0LCAxeCBUQ1BgLlxuICpcbiAqIFRoaXMgaXMgdGhlIGdyb3VuZCB0cnV0aCBvdXIgb3duIHRyYWNraW5nIHRyaWVzIHRvIGV4cGxhaW4sIHNvIHByaW50aW5nIGl0IHRlbGxzXG4gKiB0aGUgcmVhZGVyIHdoZXRoZXIgd2UgbWlzc2VkIHNvbWV0aGluZyBvciB0aGUgaGFuZyBpcyBzb21ld2hlcmUgZWxzZS5cbiAqL1xuZnVuY3Rpb24gYWN0aXZlUmVzb3VyY2VTdW1tYXJ5KCk6IHN0cmluZyB7XG4gIGNvbnN0IGNvdW50cyA9IG5ldyBNYXA8c3RyaW5nLCBudW1iZXI+KCk7XG4gIGZvciAoY29uc3QgdHlwZSBvZiBwcm9jZXNzLmdldEFjdGl2ZVJlc291cmNlc0luZm8oKSkge1xuICAgIGNvdW50cy5zZXQodHlwZSwgKGNvdW50cy5nZXQodHlwZSkgPz8gMCkgKyAxKTtcbiAgfVxuICBpZiAoY291bnRzLnNpemUgPT09IDApIHtcbiAgICByZXR1cm4gJyhub25lKSc7XG4gIH1cbiAgcmV0dXJuIFsuLi5jb3VudHNdLm1hcCgoW3R5cGUsIGNvdW50XSkgPT4gYCR7Y291bnR9eCAke3R5cGV9YCkuam9pbignLCAnKTtcbn1cblxuLyoqXG4gKiBLZWVwIG9ubHkgZnJhbWVzIHRoZSB1c2VyIGNhbiBhY3Qgb24gYnkgZHJvcHBpbmcgTm9kZSBpbnRlcm5hbHMuIE91ciBvd24gYGluaXRgXG4gKiBmcmFtZSBpcyBhbHJlYWR5IHJlbW92ZWQgYXQgY2FwdHVyZSB0aW1lLCBzZWUge0BsaW5rIGNhcHR1cmVDcmVhdGlvblN0YWNrfS5cbiAqL1xuZnVuY3Rpb24gYWN0aW9uYWJsZUZyYW1lcyhmcmFtZXM6IFNvdXJjZUZyYW1lW10pOiBTb3VyY2VGcmFtZVtdIHtcbiAgcmV0dXJuIGZyYW1lcy5maWx0ZXIoKGZyYW1lKSA9PiBmcmFtZS5maWxlICYmICFmcmFtZS5maWxlLnN0YXJ0c1dpdGgoJ25vZGU6JykpO1xufVxuXG4vKipcbiAqIFJlYWRzIHRoZSBsaW5lIG9mIGNvZGUgYmVoaW5kIGEgc3RhY2sgZnJhbWUsIGhvbGRpbmcgZWFjaCBmaWxlJ3MgbGluZXMgc28gYVxuICogcmVwb3J0IHBhc3MgcmVhZHMgYW55IGdpdmVuIGZpbGUgYXQgbW9zdCBvbmNlLlxuICpcbiAqIFRoZSBwdWJsaXNoZWQgQ0xJIGlzIG9uZSBsYXJnZSBidW5kbGUsIGFuZCBhIHNpbmdsZSByZXBvcnQgY2FuIGhvbGQgbWFueSBmcmFtZXNcbiAqIHBvaW50aW5nIGludG8gaXQuIFJlLXJlYWRpbmcgYW5kIHJlLXNwbGl0dGluZyBpdCBwZXIgZnJhbWUgc3RhbGxzIHRoZSByZXBvcnQsIGF0XG4gKiB0aGUgbW9tZW50IHRoZSB1c2VyIGlzIGFscmVhZHkgd2FpdGluZyBvbiBhIGh1bmcgQ0xJLlxuICovXG5jbGFzcyBTb3VyY2VDYWNoZSB7XG4gIC8qKlxuICAgKiBXaGV0aGVyIGFueSBsaW5lIHdlIHJlYWQgd2FzIGxvbmcgZW5vdWdoIHRvIGJlIG1pbmlmaWVkIHJhdGhlciB0aGFuXG4gICAqIGhhbmQtd3JpdHRlbi4gVXNlZCB0byB3YXJuIHRoYXQgdGhlIHByaW50ZWQgbG9jYXRpb25zIGFyZSBidW5kbGUtcmVsYXRpdmUuXG4gICAqL1xuICBwdWJsaWMgc2F3TWluaWZpZWRMaW5lID0gZmFsc2U7XG5cbiAgcHJpdmF0ZSByZWFkb25seSBmaWxlcyA9IG5ldyBNYXA8c3RyaW5nLCBzdHJpbmdbXSB8IHVuZGVmaW5lZD4oKTtcblxuICBwdWJsaWMgc25pcHBldEF0KGZyYW1lOiBTb3VyY2VGcmFtZSk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gICAgY29uc3QgbGluZSA9IHRoaXMubGluZXNPZihmcmFtZS5maWxlKT8uW2ZyYW1lLmxpbmUgLSAxXTtcbiAgICBpZiAobGluZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIH1cbiAgICBpZiAobGluZS5sZW5ndGggPD0gU09VUkNFX1NOSVBQRVRfQ0hBUlMpIHtcbiAgICAgIHJldHVybiBsaW5lLnRyaW0oKSB8fCB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgLy8gVG9vIGxvbmcgdG8gYmUgc291cmNlIGFueW9uZSB3cm90ZSwgc28gc2hvdyBhIHdpbmRvdyBjZW50cmVkIG9uIHRoZSBjb2x1bW5cbiAgICAvLyByYXRoZXIgdGhhbiB0aGUgc3RhcnQgb2YgdGhlIGxpbmUuXG4gICAgdGhpcy5zYXdNaW5pZmllZExpbmUgPSB0cnVlO1xuICAgIGNvbnN0IHN0YXJ0ID0gTWF0aC5tYXgoMCwgZnJhbWUuY29sdW1uIC0gMSAtIE1hdGguZmxvb3IoU09VUkNFX1NOSVBQRVRfQ0hBUlMgLyAyKSk7XG4gICAgY29uc3QgZW5kID0gTWF0aC5taW4obGluZS5sZW5ndGgsIHN0YXJ0ICsgU09VUkNFX1NOSVBQRVRfQ0hBUlMpO1xuICAgIGNvbnN0IHdpbmRvdyA9IGxpbmUuc2xpY2Uoc3RhcnQsIGVuZCkudHJpbSgpO1xuICAgIHJldHVybiBgJHtzdGFydCA+IDAgPyAn4oCmJyA6ICcnfSR7d2luZG93fSR7ZW5kIDwgbGluZS5sZW5ndGggPyAn4oCmJyA6ICcnfWA7XG4gIH1cblxuICBwcml2YXRlIGxpbmVzT2YoZmlsZTogc3RyaW5nKTogc3RyaW5nW10gfCB1bmRlZmluZWQge1xuICAgIGlmICghdGhpcy5maWxlcy5oYXMoZmlsZSkpIHtcbiAgICAgIHRoaXMuZmlsZXMuc2V0KGZpbGUsIHJlYWRMaW5lcyhmaWxlKSk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmZpbGVzLmdldChmaWxlKTtcbiAgfVxufVxuXG5mdW5jdGlvbiByZWFkTGluZXMoZmlsZTogc3RyaW5nKTogc3RyaW5nW10gfCB1bmRlZmluZWQge1xuICB0cnkge1xuICAgIHJldHVybiByZWFkRmlsZVN5bmMoZmlsZSwgJ3V0Zi04Jykuc3BsaXQoL1xccj9cXG4vKTtcbiAgfSBjYXRjaCB7XG4gICAgLy8gQnVuZGxlZCBhbmQgZXZhbCdkIGZyYW1lcyBoYXZlIGZpbGVuYW1lcyB0aGF0IGFyZSBub3Qgb24gZGlzay4gVGhlIGxvY2F0aW9uXG4gICAgLy8gaXMgc3RpbGwgcmVwb3J0ZWQgb24gaXRzIG93biwgc28gcmVwb3J0aW5nIGNvbnRpbnVlcyB3aXRob3V0IHRoZSBzbmlwcGV0LlxuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbn1cblxuLyoqXG4gKiBTdGFydCB3YXRjaGluZyBhc3luYyByZXNvdXJjZXMgYW5kIHJldHVybiB0aGUgdHJhY2tlciBkb2luZyBzby5cbiAqXG4gKiBDYWxsIGFzIGVhcmx5IGFzIHBvc3NpYmxlLCBhbmQgb25seSB3aGVuIHRoZSB1c2VyIGFza2VkIGZvciBpdC4gVGhlIHRyYWNrZXIgY2FuXG4gKiBvbmx5IHJlcG9ydCBvbiByZXNvdXJjZXMgY3JlYXRlZCBhZnRlciBpdCBzdGFydHMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0cmFja0xlYWtlZEhhbmRsZXMoKTogTGVha2VkSGFuZGxlVHJhY2tlciB7XG4gIGNvbnN0IHRyYWNrZXIgPSBuZXcgTGVha2VkSGFuZGxlVHJhY2tlcigpO1xuICB0cmFja2VyLnN0YXJ0KCk7XG4gIHJldHVybiB0cmFja2VyO1xufVxuXG5leHBvcnQgdHlwZSB7IExlYWtlZEhhbmRsZVRyYWNrZXIgfTtcbiJdfQ==