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.
- package/README.md +10 -1
- package/THIRD_PARTY_LICENSES +2 -2
- package/build-info.json +2 -2
- package/db.json.gz +0 -0
- package/lib/cli/cdk-toolkit.d.ts +8 -0
- package/lib/cli/cdk-toolkit.js +2 -3
- package/lib/cli/cli-config.js +3 -2
- package/lib/cli/cli-type-registry.json +5 -1
- package/lib/cli/cli.js +49 -16
- package/lib/cli/convert-to-user-input.js +3 -1
- package/lib/cli/debug-handles.d.ts +52 -0
- package/lib/cli/debug-handles.js +347 -0
- package/lib/cli/parse-command-line-arguments.js +7 -2
- package/lib/cli/user-input.d.ts +7 -1
- package/lib/cli/user-input.js +1 -1
- package/lib/index.js +905 -546
- package/lib/index_bg.wasm +0 -0
- package/lib/init-templates/.init-version.json +1 -1
- package/package.json +9 -9
|
@@ -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==
|