UNPKG

49.9 kBJavaScriptView Raw
1"use strict";
2var _a;
3Object.defineProperty(exports, "__esModule", { value: true });
4exports.Arn = exports.ArnFormat = void 0;
5const jsiiDeprecationWarnings = require("../.warnings.jsii.js");
6const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
7const cfn_fn_1 = require("./cfn-fn");
8const token_1 = require("./token");
9const util_1 = require("./util");
10/**
11 * An enum representing the various ARN formats that different services use.
12 */
13var ArnFormat;
14(function (ArnFormat) {
15 /**
16 * This represents a format where there is no 'resourceName' part.
17 * This format is used for S3 resources,
18 * like 'arn:aws:s3:::bucket'.
19 * Everything after the last colon is considered the 'resource',
20 * even if it contains slashes,
21 * like in 'arn:aws:s3:::bucket/object.zip'.
22 */
23 ArnFormat["NO_RESOURCE_NAME"] = "arn:aws:service:region:account:resource";
24 /**
25 * This represents a format where the 'resource' and 'resourceName'
26 * parts are separated with a colon.
27 * Like in: 'arn:aws:service:region:account:resource:resourceName'.
28 * Everything after the last colon is considered the 'resourceName',
29 * even if it contains slashes,
30 * like in 'arn:aws:apigateway:region:account:resource:/test/mydemoresource/*'.
31 */
32 ArnFormat["COLON_RESOURCE_NAME"] = "arn:aws:service:region:account:resource:resourceName";
33 /**
34 * This represents a format where the 'resource' and 'resourceName'
35 * parts are separated with a slash.
36 * Like in: 'arn:aws:service:region:account:resource/resourceName'.
37 * Everything after the separating slash is considered the 'resourceName',
38 * even if it contains colons,
39 * like in 'arn:aws:cognito-sync:region:account:identitypool/us-east-1:1a1a1a1a-ffff-1111-9999-12345678:bla'.
40 */
41 ArnFormat["SLASH_RESOURCE_NAME"] = "arn:aws:service:region:account:resource/resourceName";
42 /**
43 * This represents a format where the 'resource' and 'resourceName'
44 * parts are seperated with a slash,
45 * but there is also an additional slash after the colon separating 'account' from 'resource'.
46 * Like in: 'arn:aws:service:region:account:/resource/resourceName'.
47 * Note that the leading slash is _not_ included in the parsed 'resource' part.
48 */
49 ArnFormat["SLASH_RESOURCE_SLASH_RESOURCE_NAME"] = "arn:aws:service:region:account:/resource/resourceName";
50})(ArnFormat = exports.ArnFormat || (exports.ArnFormat = {}));
51class Arn {
52 constructor() { }
53 /**
54 * Creates an ARN from components.
55 *
56 * If `partition`, `region` or `account` are not specified, the stack's
57 * partition, region and account will be used.
58 *
59 * If any component is the empty string, an empty string will be inserted
60 * into the generated ARN at the location that component corresponds to.
61 *
62 * The ARN will be formatted as follows:
63 *
64 * arn:{partition}:{service}:{region}:{account}:{resource}{sep}{resource-name}
65 *
66 * The required ARN pieces that are omitted will be taken from the stack that
67 * the 'scope' is attached to. If all ARN pieces are supplied, the supplied scope
68 * can be 'undefined'.
69 */
70 static format(components, stack) {
71 try {
72 jsiiDeprecationWarnings._aws_cdk_core_ArnComponents(components);
73 jsiiDeprecationWarnings._aws_cdk_core_Stack(stack);
74 }
75 catch (error) {
76 if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
77 Error.captureStackTrace(error, this.format);
78 }
79 throw error;
80 }
81 const partition = components.partition ?? stack?.partition;
82 const region = components.region ?? stack?.region;
83 const account = components.account ?? stack?.account;
84 // Catch both 'null' and 'undefined'
85 if (partition == null || region == null || account == null) {
86 throw new Error(`Arn.format: partition (${partition}), region (${region}), and account (${account}) must all be passed if stack is not passed.`);
87 }
88 const sep = components.sep ?? (components.arnFormat === ArnFormat.COLON_RESOURCE_NAME ? ':' : '/');
89 const values = [
90 'arn', ':', partition, ':', components.service, ':', region, ':', account, ':',
91 ...(components.arnFormat === ArnFormat.SLASH_RESOURCE_SLASH_RESOURCE_NAME ? ['/'] : []),
92 components.resource,
93 ];
94 if (sep !== '/' && sep !== ':' && sep !== '') {
95 throw new Error('resourcePathSep may only be ":", "/" or an empty string');
96 }
97 if (components.resourceName != null) {
98 values.push(sep);
99 values.push(components.resourceName);
100 }
101 return values.join('');
102 }
103 /**
104 * Given an ARN, parses it and returns components.
105 *
106 * IF THE ARN IS A CONCRETE STRING...
107 *
108 * ...it will be parsed and validated. The separator (`sep`) will be set to '/'
109 * if the 6th component includes a '/', in which case, `resource` will be set
110 * to the value before the '/' and `resourceName` will be the rest. In case
111 * there is no '/', `resource` will be set to the 6th components and
112 * `resourceName` will be set to the rest of the string.
113 *
114 * IF THE ARN IS A TOKEN...
115 *
116 * ...it cannot be validated, since we don't have the actual value yet at the
117 * time of this function call. You will have to supply `sepIfToken` and
118 * whether or not ARNs of the expected format usually have resource names
119 * in order to parse it properly. The resulting `ArnComponents` object will
120 * contain tokens for the subexpressions of the ARN, not string literals.
121 *
122 * If the resource name could possibly contain the separator char, the actual
123 * resource name cannot be properly parsed. This only occurs if the separator
124 * char is '/', and happens for example for S3 object ARNs, IAM Role ARNs,
125 * IAM OIDC Provider ARNs, etc. To properly extract the resource name from a
126 * Tokenized ARN, you must know the resource type and call
127 * `Arn.extractResourceName`.
128 *
129 * @param arn The ARN to parse
130 * @param sepIfToken The separator used to separate resource from resourceName
131 * @param hasName Whether there is a name component in the ARN at all. For
132 * example, SNS Topics ARNs have the 'resource' component contain the topic
133 * name, and no 'resourceName' component.
134 *
135 * @returns an ArnComponents object which allows access to the various
136 * components of the ARN.
137 *
138 * @returns an ArnComponents object which allows access to the various
139 * components of the ARN.
140 *
141 * @deprecated use split instead
142 */
143 static parse(arn, sepIfToken = '/', hasName = true) {
144 try {
145 jsiiDeprecationWarnings.print("@aws-cdk/core.Arn#parse", "use split instead");
146 }
147 catch (error) {
148 if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
149 Error.captureStackTrace(error, this.parse);
150 }
151 throw error;
152 }
153 let arnFormat;
154 if (!hasName) {
155 arnFormat = ArnFormat.NO_RESOURCE_NAME;
156 }
157 else {
158 arnFormat = sepIfToken === '/' ? ArnFormat.SLASH_RESOURCE_NAME : ArnFormat.COLON_RESOURCE_NAME;
159 }
160 return this.split(arn, arnFormat);
161 }
162 /**
163 * Splits the provided ARN into its components.
164 * Works both if 'arn' is a string like 'arn:aws:s3:::bucket',
165 * and a Token representing a dynamic CloudFormation expression
166 * (in which case the returned components will also be dynamic CloudFormation expressions,
167 * encoded as Tokens).
168 *
169 * @param arn the ARN to split into its components
170 * @param arnFormat the expected format of 'arn' - depends on what format the service 'arn' represents uses
171 */
172 static split(arn, arnFormat) {
173 try {
174 jsiiDeprecationWarnings._aws_cdk_core_ArnFormat(arnFormat);
175 }
176 catch (error) {
177 if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
178 Error.captureStackTrace(error, this.split);
179 }
180 throw error;
181 }
182 const components = parseArnShape(arn);
183 if (components === 'token') {
184 return parseTokenArn(arn, arnFormat);
185 }
186 const [, partition, service, region, account, resourceTypeOrName, ...rest] = components;
187 let resource;
188 let resourceName;
189 let sep;
190 let resourcePartStartIndex = 0;
191 let detectedArnFormat;
192 let slashIndex = resourceTypeOrName.indexOf('/');
193 if (slashIndex === 0) {
194 // new-style ARNs are of the form 'arn:aws:s4:us-west-1:12345:/resource-type/resource-name'
195 slashIndex = resourceTypeOrName.indexOf('/', 1);
196 resourcePartStartIndex = 1;
197 detectedArnFormat = ArnFormat.SLASH_RESOURCE_SLASH_RESOURCE_NAME;
198 }
199 if (slashIndex !== -1) {
200 // the slash is only a separator if ArnFormat is not NO_RESOURCE_NAME
201 if (arnFormat === ArnFormat.NO_RESOURCE_NAME) {
202 sep = undefined;
203 slashIndex = -1;
204 detectedArnFormat = ArnFormat.NO_RESOURCE_NAME;
205 }
206 else {
207 sep = '/';
208 detectedArnFormat = resourcePartStartIndex === 0
209 ? ArnFormat.SLASH_RESOURCE_NAME
210 // need to repeat this here, as otherwise the compiler thinks 'detectedArnFormat' is not initialized in all paths
211 : ArnFormat.SLASH_RESOURCE_SLASH_RESOURCE_NAME;
212 }
213 }
214 else if (rest.length > 0) {
215 sep = ':';
216 slashIndex = -1;
217 detectedArnFormat = ArnFormat.COLON_RESOURCE_NAME;
218 }
219 else {
220 sep = undefined;
221 detectedArnFormat = ArnFormat.NO_RESOURCE_NAME;
222 }
223 if (slashIndex !== -1) {
224 resource = resourceTypeOrName.substring(resourcePartStartIndex, slashIndex);
225 resourceName = resourceTypeOrName.substring(slashIndex + 1);
226 }
227 else {
228 resource = resourceTypeOrName;
229 }
230 if (rest.length > 0) {
231 if (!resourceName) {
232 resourceName = '';
233 }
234 else {
235 resourceName += ':';
236 }
237 resourceName += rest.join(':');
238 }
239 // "|| undefined" will cause empty strings to be treated as "undefined".
240 // Optional ARN attributes (e.g. region, account) should return as empty string
241 // if they are provided as such.
242 return util_1.filterUndefined({
243 service: service || undefined,
244 resource: resource || undefined,
245 partition: partition || undefined,
246 region,
247 account,
248 resourceName,
249 sep,
250 arnFormat: detectedArnFormat,
251 });
252 }
253 /**
254 * Extract the full resource name from an ARN
255 *
256 * Necessary for resource names (paths) that may contain the separator, like
257 * `arn:aws:iam::111111111111:role/path/to/role/name`.
258 *
259 * Only works if we statically know the expected `resourceType` beforehand, since we're going
260 * to use that to split the string on ':<resourceType>/' (and take the right-hand side).
261 *
262 * We can't extract the 'resourceType' from the ARN at hand, because CloudFormation Expressions
263 * only allow literals in the 'separator' argument to `{ Fn::Split }`, and so it can't be
264 * `{ Fn::Select: [5, { Fn::Split: [':', ARN] }}`.
265 *
266 * Only necessary for ARN formats for which the type-name separator is `/`.
267 */
268 static extractResourceName(arn, resourceType) {
269 const components = parseArnShape(arn);
270 if (components === 'token') {
271 return cfn_fn_1.Fn.select(1, cfn_fn_1.Fn.split(`:${resourceType}/`, arn));
272 }
273 // Apparently we could just parse this right away. Validate that we got the right
274 // resource type (to notify authors of incorrect assumptions right away).
275 const parsed = Arn.split(arn, ArnFormat.SLASH_RESOURCE_NAME);
276 if (!token_1.Token.isUnresolved(parsed.resource) && parsed.resource !== resourceType) {
277 throw new Error(`Expected resource type '${resourceType}' in ARN, got '${parsed.resource}' in '${arn}'`);
278 }
279 if (!parsed.resourceName) {
280 throw new Error(`Expected resource name in ARN, didn't find one: '${arn}'`);
281 }
282 return parsed.resourceName;
283 }
284}
285exports.Arn = Arn;
286_a = JSII_RTTI_SYMBOL_1;
287Arn[_a] = { fqn: "@aws-cdk/core.Arn", version: "1.204.0" };
288/**
289 * Given a Token evaluating to ARN, parses it and returns components.
290 *
291 * The ARN cannot be validated, since we don't have the actual value yet
292 * at the time of this function call. You will have to know the separator
293 * and the type of ARN.
294 *
295 * The resulting `ArnComponents` object will contain tokens for the
296 * subexpressions of the ARN, not string literals.
297 *
298 * WARNING: this function cannot properly parse the complete final
299 * 'resourceName' part if it contains colons,
300 * like 'arn:aws:cognito-sync:region:account:identitypool/us-east-1:1a1a1a1a-ffff-1111-9999-12345678:bla'.
301 *
302 * @param arnToken The input token that contains an ARN
303 * @param arnFormat the expected format of 'arn' - depends on what format the service the ARN represents uses
304 */
305function parseTokenArn(arnToken, arnFormat) {
306 // ARN looks like:
307 // arn:partition:service:region:account:resource
308 // arn:partition:service:region:account:resource:resourceName
309 // arn:partition:service:region:account:resource/resourceName
310 // arn:partition:service:region:account:/resource/resourceName
311 const components = cfn_fn_1.Fn.split(':', arnToken);
312 const partition = cfn_fn_1.Fn.select(1, components).toString();
313 const service = cfn_fn_1.Fn.select(2, components).toString();
314 const region = cfn_fn_1.Fn.select(3, components).toString();
315 const account = cfn_fn_1.Fn.select(4, components).toString();
316 let resource;
317 let resourceName;
318 let sep;
319 if (arnFormat === ArnFormat.NO_RESOURCE_NAME || arnFormat === ArnFormat.COLON_RESOURCE_NAME) {
320 // we know that the 'resource' part will always be the 6th segment in this case
321 resource = cfn_fn_1.Fn.select(5, components);
322 if (arnFormat === ArnFormat.COLON_RESOURCE_NAME) {
323 resourceName = cfn_fn_1.Fn.select(6, components);
324 sep = ':';
325 }
326 else {
327 resourceName = undefined;
328 sep = undefined;
329 }
330 }
331 else {
332 // we know that the 'resource' and 'resourceName' parts are separated by slash here,
333 // so we split the 6th segment from the colon-separated ones with a slash
334 const lastComponents = cfn_fn_1.Fn.split('/', cfn_fn_1.Fn.select(5, components));
335 if (arnFormat === ArnFormat.SLASH_RESOURCE_NAME) {
336 resource = cfn_fn_1.Fn.select(0, lastComponents);
337 resourceName = cfn_fn_1.Fn.select(1, lastComponents);
338 }
339 else {
340 // arnFormat is ArnFormat.SLASH_RESOURCE_SLASH_RESOURCE_NAME,
341 // which means there's an extra slash there at the beginning that we need to skip
342 resource = cfn_fn_1.Fn.select(1, lastComponents);
343 resourceName = cfn_fn_1.Fn.select(2, lastComponents);
344 }
345 sep = '/';
346 }
347 return { partition, service, region, account, resource, resourceName, sep, arnFormat };
348}
349/**
350 * Validate that a string is either unparseable or looks mostly like an ARN
351 */
352function parseArnShape(arn) {
353 // assume anything that starts with 'arn:' is an ARN,
354 // so we can report better errors
355 const looksLikeArn = arn.startsWith('arn:');
356 if (!looksLikeArn) {
357 if (token_1.Token.isUnresolved(arn)) {
358 return 'token';
359 }
360 else {
361 throw new Error(`ARNs must start with "arn:" and have at least 6 components: ${arn}`);
362 }
363 }
364 // If the ARN merely contains Tokens, but otherwise *looks* mostly like an ARN,
365 // it's a string of the form 'arn:${partition}:service:${region}:${account}:resource/xyz'.
366 // Parse fields out to the best of our ability.
367 // Tokens won't contain ":", so this won't break them.
368 const components = arn.split(':');
369 const partition = components.length > 1 ? components[1] : undefined;
370 if (!partition) {
371 throw new Error('The `partition` component (2nd component) of an ARN is required: ' + arn);
372 }
373 const service = components.length > 2 ? components[2] : undefined;
374 if (!service) {
375 throw new Error('The `service` component (3rd component) of an ARN is required: ' + arn);
376 }
377 const resource = components.length > 5 ? components[5] : undefined;
378 if (!resource) {
379 throw new Error('The `resource` component (6th component) of an ARN is required: ' + arn);
380 }
381 // Region can be missing in global ARNs (such as used by IAM)
382 // Account can be missing in some ARN types (such as used for S3 buckets)
383 return components;
384}
385//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXJuLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiYXJuLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLHFDQUE4QjtBQUU5QixtQ0FBZ0M7QUFDaEMsaUNBQXlDO0FBRXpDOztHQUVHO0FBQ0gsSUFBWSxTQXVDWDtBQXZDRCxXQUFZLFNBQVM7SUFDbkI7Ozs7Ozs7T0FPRztJQUNILHlFQUE0RCxDQUFBO0lBRTVEOzs7Ozs7O09BT0c7SUFDSCx5RkFBNEUsQ0FBQTtJQUU1RTs7Ozs7OztPQU9HO0lBQ0gseUZBQTRFLENBQUE7SUFFNUU7Ozs7OztPQU1HO0lBQ0gseUdBQTRGLENBQUE7QUFDOUYsQ0FBQyxFQXZDVyxTQUFTLEdBQVQsaUJBQVMsS0FBVCxpQkFBUyxRQXVDcEI7QUFtRUQsTUFBYSxHQUFHO0lBd05kLGlCQUF5QjtJQXZOekI7Ozs7Ozs7Ozs7Ozs7Ozs7T0FnQkc7SUFDSSxNQUFNLENBQUMsTUFBTSxDQUFDLFVBQXlCLEVBQUUsS0FBYTs7Ozs7Ozs7Ozs7UUFDM0QsTUFBTSxTQUFTLEdBQUcsVUFBVSxDQUFDLFNBQVMsSUFBSSxLQUFLLEVBQUUsU0FBUyxDQUFDO1FBQzNELE1BQU0sTUFBTSxHQUFHLFVBQVUsQ0FBQyxNQUFNLElBQUksS0FBSyxFQUFFLE1BQU0sQ0FBQztRQUNsRCxNQUFNLE9BQU8sR0FBRyxVQUFVLENBQUMsT0FBTyxJQUFJLEtBQUssRUFBRSxPQUFPLENBQUM7UUFFckQsb0NBQW9DO1FBQ3BDLElBQUksU0FBUyxJQUFJLElBQUksSUFBSSxNQUFNLElBQUksSUFBSSxJQUFJLE9BQU8sSUFBSSxJQUFJLEVBQUU7WUFDMUQsTUFBTSxJQUFJLEtBQUssQ0FBQywwQkFBMEIsU0FBUyxjQUFjLE1BQU0sbUJBQW1CLE9BQU8sOENBQThDLENBQUMsQ0FBQztTQUNsSjtRQUVELE1BQU0sR0FBRyxHQUFHLFVBQVUsQ0FBQyxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsU0FBUyxLQUFLLFNBQVMsQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUVuRyxNQUFNLE1BQU0sR0FBRztZQUNiLEtBQUssRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxVQUFVLENBQUMsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLE9BQU8sRUFBRSxHQUFHO1lBQzlFLEdBQUcsQ0FBQyxVQUFVLENBQUMsU0FBUyxLQUFLLFNBQVMsQ0FBQyxrQ0FBa0MsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1lBQ3ZGLFVBQVUsQ0FBQyxRQUFRO1NBQ3BCLENBQUM7UUFFRixJQUFJLEdBQUcsS0FBSyxHQUFHLElBQUksR0FBRyxLQUFLLEdBQUcsSUFBSSxHQUFHLEtBQUssRUFBRSxFQUFFO1lBQzVDLE1BQU0sSUFBSSxLQUFLLENBQUMseURBQXlELENBQUMsQ0FBQztTQUM1RTtRQUVELElBQUksVUFBVSxDQUFDLFlBQVksSUFBSSxJQUFJLEVBQUU7WUFDbkMsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUNqQixNQUFNLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxZQUFZLENBQUMsQ0FBQztTQUN0QztRQUVELE9BQU8sTUFBTSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztLQUN4QjtJQUVEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7T0F1Q0c7SUFDSSxNQUFNLENBQUMsS0FBSyxDQUFDLEdBQVcsRUFBRSxhQUFxQixHQUFHLEVBQUUsVUFBbUIsSUFBSTs7Ozs7Ozs7OztRQUNoRixJQUFJLFNBQW9CLENBQUM7UUFDekIsSUFBSSxDQUFDLE9BQU8sRUFBRTtZQUNaLFNBQVMsR0FBRyxTQUFTLENBQUMsZ0JBQWdCLENBQUM7U0FDeEM7YUFBTTtZQUNMLFNBQVMsR0FBRyxVQUFVLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsbUJBQW1CLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxtQkFBbUIsQ0FBQztTQUNoRztRQUNELE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsU0FBUyxDQUFDLENBQUM7S0FDbkM7SUFFRDs7Ozs7Ozs7O09BU0c7SUFDSSxNQUFNLENBQUMsS0FBSyxDQUFDLEdBQVcsRUFBRSxTQUFvQjs7Ozs7Ozs7OztRQUNuRCxNQUFNLFVBQVUsR0FBRyxhQUFhLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDdEMsSUFBSSxVQUFVLEtBQUssT0FBTyxFQUFFO1lBQzFCLE9BQU8sYUFBYSxDQUFDLEdBQUcsRUFBRSxTQUFTLENBQUMsQ0FBQztTQUN0QztRQUVELE1BQU0sQ0FBQyxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxHQUFHLElBQUksQ0FBQyxHQUFHLFVBQVUsQ0FBQztRQUV4RixJQUFJLFFBQWdCLENBQUM7UUFDckIsSUFBSSxZQUFnQyxDQUFDO1FBQ3JDLElBQUksR0FBdUIsQ0FBQztRQUM1QixJQUFJLHNCQUFzQixHQUFHLENBQUMsQ0FBQztRQUMvQixJQUFJLGlCQUE0QixDQUFDO1FBRWpDLElBQUksVUFBVSxHQUFHLGtCQUFrQixDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNqRCxJQUFJLFVBQVUsS0FBSyxDQUFDLEVBQUU7WUFDcEIsMkZBQTJGO1lBQzNGLFVBQVUsR0FBRyxrQkFBa0IsQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQyxDQUFDO1lBQ2hELHNCQUFzQixHQUFHLENBQUMsQ0FBQztZQUMzQixpQkFBaUIsR0FBRyxTQUFTLENBQUMsa0NBQWtDLENBQUM7U0FDbEU7UUFDRCxJQUFJLFVBQVUsS0FBSyxDQUFDLENBQUMsRUFBRTtZQUNyQixxRUFBcUU7WUFDckUsSUFBSSxTQUFTLEtBQUssU0FBUyxDQUFDLGdCQUFnQixFQUFFO2dCQUM1QyxHQUFHLEdBQUcsU0FBUyxDQUFDO2dCQUNoQixVQUFVLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQ2hCLGlCQUFpQixHQUFHLFNBQVMsQ0FBQyxnQkFBZ0IsQ0FBQzthQUNoRDtpQkFBTTtnQkFDTCxHQUFHLEdBQUcsR0FBRyxDQUFDO2dCQUNWLGlCQUFpQixHQUFHLHNCQUFzQixLQUFLLENBQUM7b0JBQzlDLENBQUMsQ0FBQyxTQUFTLENBQUMsbUJBQW1CO29CQUMvQixpSEFBaUg7b0JBQ2pILENBQUMsQ0FBQyxTQUFTLENBQUMsa0NBQWtDLENBQUM7YUFDbEQ7U0FDRjthQUFNLElBQUksSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUU7WUFDMUIsR0FBRyxHQUFHLEdBQUcsQ0FBQztZQUNWLFVBQVUsR0FBRyxDQUFDLENBQUMsQ0FBQztZQUNoQixpQkFBaUIsR0FBRyxTQUFTLENBQUMsbUJBQW1CLENBQUM7U0FDbkQ7YUFBTTtZQUNMLEdBQUcsR0FBRyxTQUFTLENBQUM7WUFDaEIsaUJBQWlCLEdBQUcsU0FBUyxDQUFDLGdCQUFnQixDQUFDO1NBQ2hEO1FBRUQsSUFBSSxVQUFVLEtBQUssQ0FBQyxDQUFDLEVBQUU7WUFDckIsUUFBUSxHQUFHLGtCQUFrQixDQUFDLFNBQVMsQ0FBQyxzQkFBc0IsRUFBRSxVQUFVLENBQUMsQ0FBQztZQUM1RSxZQUFZLEdBQUcsa0JBQWtCLENBQUMsU0FBUyxDQUFDLFVBQVUsR0FBRyxDQUFDLENBQUMsQ0FBQztTQUM3RDthQUFNO1lBQ0wsUUFBUSxHQUFHLGtCQUFrQixDQUFDO1NBQy9CO1FBRUQsSUFBSSxJQUFJLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTtZQUNuQixJQUFJLENBQUMsWUFBWSxFQUFFO2dCQUNqQixZQUFZLEdBQUcsRUFBRSxDQUFDO2FBQ25CO2lCQUFNO2dCQUNMLFlBQVksSUFBSSxHQUFHLENBQUM7YUFDckI7WUFFRCxZQUFZLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztTQUNoQztRQUVELHdFQUF3RTtRQUN4RSwrRUFBK0U7UUFDL0UsZ0NBQWdDO1FBQ2hDLE9BQU8sc0JBQWUsQ0FBQztZQUNyQixPQUFPLEVBQUUsT0FBTyxJQUFJLFNBQVM7WUFDN0IsUUFBUSxFQUFFLFFBQVEsSUFBSSxTQUFTO1lBQy9CLFNBQVMsRUFBRSxTQUFTLElBQUksU0FBUztZQUNqQyxNQUFNO1lBQ04sT0FBTztZQUNQLFlBQVk7WUFDWixHQUFHO1lBQ0gsU0FBUyxFQUFFLGlCQUFpQjtTQUM3QixDQUFDLENBQUM7S0FDSjtJQUVEOzs7Ozs7Ozs7Ozs7OztPQWNHO0lBQ0ksTUFBTSxDQUFDLG1CQUFtQixDQUFDLEdBQVcsRUFBRSxZQUFvQjtRQUNqRSxNQUFNLFVBQVUsR0FBRyxhQUFhLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDdEMsSUFBSSxVQUFVLEtBQUssT0FBTyxFQUFFO1lBQzFCLE9BQU8sV0FBRSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsV0FBRSxDQUFDLEtBQUssQ0FBQyxJQUFJLFlBQVksR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUM7U0FDekQ7UUFFRCxpRkFBaUY7UUFDakYseUVBQXlFO1FBQ3pFLE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLFNBQVMsQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDO1FBQzdELElBQUksQ0FBQyxhQUFLLENBQUMsWUFBWSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsSUFBSSxNQUFNLENBQUMsUUFBUSxLQUFLLFlBQVksRUFBRTtZQUM1RSxNQUFNLElBQUksS0FBSyxDQUFDLDJCQUEyQixZQUFZLGtCQUFrQixNQUFNLENBQUMsUUFBUSxTQUFTLEdBQUcsR0FBRyxDQUFDLENBQUM7U0FDMUc7UUFDRCxJQUFJLENBQUMsTUFBTSxDQUFDLFlBQVksRUFBRTtZQUN4QixNQUFNLElBQUksS0FBSyxDQUFDLG9EQUFvRCxHQUFHLEdBQUcsQ0FBQyxDQUFDO1NBQzdFO1FBQ0QsT0FBTyxNQUFNLENBQUMsWUFBWSxDQUFDO0tBQzVCOztBQXROSCxrQkF5TkM7OztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7O0dBZ0JHO0FBQ0gsU0FBUyxhQUFhLENBQUMsUUFBZ0IsRUFBRSxTQUFvQjtJQUMzRCxrQkFBa0I7SUFDbEIsZ0RBQWdEO0lBQ2hELDZEQUE2RDtJQUM3RCw2REFBNkQ7SUFDN0QsOERBQThEO0lBRTlELE1BQU0sVUFBVSxHQUFHLFdBQUUsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLFFBQVEsQ0FBQyxDQUFDO0lBRTNDLE1BQU0sU0FBUyxHQUFHLFdBQUUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLFVBQVUsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQ3RELE1BQU0sT0FBTyxHQUFHLFdBQUUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLFVBQVUsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQ3BELE1BQU0sTUFBTSxHQUFHLFdBQUUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLFVBQVUsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQ25ELE1BQU0sT0FBTyxHQUFHLFdBQUUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLFVBQVUsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQ3BELElBQUksUUFBZ0IsQ0FBQztJQUNyQixJQUFJLFlBQWdDLENBQUM7SUFDckMsSUFBSSxHQUF1QixDQUFDO0lBRTVCLElBQUksU0FBUyxLQUFLLFNBQVMsQ0FBQyxnQkFBZ0IsSUFBSSxTQUFTLEtBQUssU0FBUyxDQUFDLG1CQUFtQixFQUFFO1FBQzNGLCtFQUErRTtRQUMvRSxRQUFRLEdBQUcsV0FBRSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsVUFBVSxDQUFDLENBQUM7UUFDcEMsSUFBSSxTQUFTLEtBQUssU0FBUyxDQUFDLG1CQUFtQixFQUFFO1lBQy9DLFlBQVksR0FBRyxXQUFFLENBQUMsTUFBTSxDQUFDLENBQUMsRUFBRSxVQUFVLENBQUMsQ0FBQztZQUN4QyxHQUFHLEdBQUcsR0FBRyxDQUFDO1NBQ1g7YUFBTTtZQUNMLFlBQVksR0FBRyxTQUFTLENBQUM7WUFDekIsR0FBRyxHQUFHLFNBQVMsQ0FBQztTQUNqQjtLQUNGO1NBQU07UUFDTCxvRkFBb0Y7UUFDcEYseUVBQXlFO1FBQ3pFLE1BQU0sY0FBYyxHQUFHLFdBQUUsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLFdBQUUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLFVBQVUsQ0FBQyxDQUFDLENBQUM7UUFFL0QsSUFBSSxTQUFTLEtBQUssU0FBUyxDQUFDLG1CQUFtQixFQUFFO1lBQy9DLFFBQVEsR0FBRyxXQUFFLENBQUMsTUFBTSxDQUFDLENBQUMsRUFBRSxjQUFjLENBQUMsQ0FBQztZQUN4QyxZQUFZLEdBQUcsV0FBRSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsY0FBYyxDQUFDLENBQUM7U0FDN0M7YUFBTTtZQUNMLDZEQUE2RDtZQUM3RCxpRkFBaUY7WUFDakYsUUFBUSxHQUFHLFdBQUUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLGNBQWMsQ0FBQyxDQUFDO1lBQ3hDLFlBQVksR0FBRyxXQUFFLENBQUMsTUFBTSxDQUFDLENBQUMsRUFBRSxjQUFjLENBQUMsQ0FBQztTQUM3QztRQUNELEdBQUcsR0FBRyxHQUFHLENBQUM7S0FDWDtJQUVELE9BQU8sRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLFlBQVksRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLENBQUM7QUFDekYsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBUyxhQUFhLENBQUMsR0FBVztJQUNoQyxxREFBcUQ7SUFDckQsaUNBQWlDO0lBQ2pDLE1BQU0sWUFBWSxHQUFHLEdBQUcsQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUM7SUFFNUMsSUFBSSxDQUFDLFlBQVksRUFBRTtRQUNqQixJQUFJLGFBQUssQ0FBQyxZQUFZLENBQUMsR0FBRyxDQUFDLEVBQUU7WUFDM0IsT0FBTyxPQUFPLENBQUM7U0FDaEI7YUFBTTtZQUNMLE1BQU0sSUFBSSxLQUFLLENBQUMsK0RBQStELEdBQUcsRUFBRSxDQUFDLENBQUM7U0FDdkY7S0FDRjtJQUVELCtFQUErRTtJQUMvRSwwRkFBMEY7SUFDMUYsK0NBQStDO0lBQy9DLHNEQUFzRDtJQUN0RCxNQUFNLFVBQVUsR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBRWxDLE1BQU0sU0FBUyxHQUFHLFVBQVUsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztJQUNwRSxJQUFJLENBQUMsU0FBUyxFQUFFO1FBQ2QsTUFBTSxJQUFJLEtBQUssQ0FBQyxtRUFBbUUsR0FBRyxHQUFHLENBQUMsQ0FBQztLQUM1RjtJQUVELE1BQU0sT0FBTyxHQUFHLFVBQVUsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztJQUNsRSxJQUFJLENBQUMsT0FBTyxFQUFFO1FBQ1osTUFBTSxJQUFJLEtBQUssQ0FBQyxpRUFBaUUsR0FBRyxHQUFHLENBQUMsQ0FBQztLQUMxRjtJQUVELE1BQU0sUUFBUSxHQUFHLFVBQVUsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztJQUNuRSxJQUFJLENBQUMsUUFBUSxFQUFFO1FBQ2IsTUFBTSxJQUFJLEtBQUssQ0FBQyxrRUFBa0UsR0FBRyxHQUFHLENBQUMsQ0FBQztLQUMzRjtJQUVELDZEQUE2RDtJQUU3RCx5RUFBeUU7SUFFekUsT0FBTyxVQUFVLENBQUM7QUFDcEIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEZuIH0gZnJvbSAnLi9jZm4tZm4nO1xuaW1wb3J0IHsgU3RhY2sgfSBmcm9tICcuL3N0YWNrJztcbmltcG9ydCB7IFRva2VuIH0gZnJvbSAnLi90b2tlbic7XG5pbXBvcnQgeyBmaWx0ZXJVbmRlZmluZWQgfSBmcm9tICcuL3V0aWwnO1xuXG4vKipcbiAqIEFuIGVudW0gcmVwcmVzZW50aW5nIHRoZSB2YXJpb3VzIEFSTiBmb3JtYXRzIHRoYXQgZGlmZmVyZW50IHNlcnZpY2VzIHVzZS5cbiAqL1xuZXhwb3J0IGVudW0gQXJuRm9ybWF0IHtcbiAgLyoqXG4gICAqIFRoaXMgcmVwcmVzZW50cyBhIGZvcm1hdCB3aGVyZSB0aGVyZSBpcyBubyAncmVzb3VyY2VOYW1lJyBwYXJ0LlxuICAgKiBUaGlzIGZvcm1hdCBpcyB1c2VkIGZvciBTMyByZXNvdXJjZXMsXG4gICAqIGxpa2UgJ2Fybjphd3M6czM6OjpidWNrZXQnLlxuICAgKiBFdmVyeXRoaW5nIGFmdGVyIHRoZSBsYXN0IGNvbG9uIGlzIGNvbnNpZGVyZWQgdGhlICdyZXNvdXJjZScsXG4gICAqIGV2ZW4gaWYgaXQgY29udGFpbnMgc2xhc2hlcyxcbiAgICogbGlrZSBpbiAnYXJuOmF3czpzMzo6OmJ1Y2tldC9vYmplY3QuemlwJy5cbiAgICovXG4gIE5PX1JFU09VUkNFX05BTUUgPSAnYXJuOmF3czpzZXJ2aWNlOnJlZ2lvbjphY2NvdW50OnJlc291cmNlJyxcblxuICAvKipcbiAgICogVGhpcyByZXByZXNlbnRzIGEgZm9ybWF0IHdoZXJlIHRoZSAncmVzb3VyY2UnIGFuZCAncmVzb3VyY2VOYW1lJ1xuICAgKiBwYXJ0cyBhcmUgc2VwYXJhdGVkIHdpdGggYSBjb2xvbi5cbiAgICogTGlrZSBpbjogJ2Fybjphd3M6c2VydmljZTpyZWdpb246YWNjb3VudDpyZXNvdXJjZTpyZXNvdXJjZU5hbWUnLlxuICAgKiBFdmVyeXRoaW5nIGFmdGVyIHRoZSBsYXN0IGNvbG9uIGlzIGNvbnNpZGVyZWQgdGhlICdyZXNvdXJjZU5hbWUnLFxuICAgKiBldmVuIGlmIGl0IGNvbnRhaW5zIHNsYXNoZXMsXG4gICAqIGxpa2UgaW4gJ2Fybjphd3M6YXBpZ2F0ZXdheTpyZWdpb246YWNjb3VudDpyZXNvdXJjZTovdGVzdC9teWRlbW9yZXNvdXJjZS8qJy5cbiAgICovXG4gIENPTE9OX1JFU09VUkNFX05BTUUgPSAnYXJuOmF3czpzZXJ2aWNlOnJlZ2lvbjphY2NvdW50OnJlc291cmNlOnJlc291cmNlTmFtZScsXG5cbiAgLyoqXG4gICAqIFRoaXMgcmVwcmVzZW50cyBhIGZvcm1hdCB3aGVyZSB0aGUgJ3Jlc291cmNlJyBhbmQgJ3Jlc291cmNlTmFtZSdcbiAgICogcGFydHMgYXJlIHNlcGFyYXRlZCB3aXRoIGEgc2xhc2guXG4gICAqIExpa2UgaW46ICdhcm46YXdzOnNlcnZpY2U6cmVnaW9uOmFjY291bnQ6cmVzb3VyY2UvcmVzb3VyY2VOYW1lJy5cbiAgICogRXZlcnl0aGluZyBhZnRlciB0aGUgc2VwYXJhdGluZyBzbGFzaCBpcyBjb25zaWRlcmVkIHRoZSAncmVzb3VyY2VOYW1lJyxcbiAgICogZXZlbiBpZiBpdCBjb250YWlucyBjb2xvbnMsXG4gICAqIGxpa2UgaW4gJ2Fybjphd3M6Y29nbml0by1zeW5jOnJlZ2lvbjphY2NvdW50OmlkZW50aXR5cG9vbC91cy1lYXN0LTE6MWExYTFhMWEtZmZmZi0xMTExLTk5OTktMTIzNDU2Nzg6YmxhJy5cbiAgICovXG4gIFNMQVNIX1JFU09VUkNFX05BTUUgPSAnYXJuOmF3czpzZXJ2aWNlOnJlZ2lvbjphY2NvdW50OnJlc291cmNlL3Jlc291cmNlTmFtZScsXG5cbiAgLyoqXG4gICAqIFRoaXMgcmVwcmVzZW50cyBhIGZvcm1hdCB3aGVyZSB0aGUgJ3Jlc291cmNlJyBhbmQgJ3Jlc291cmNlTmFtZSdcbiAgICogcGFydHMgYXJlIHNlcGVyYXRlZCB3aXRoIGEgc2xhc2gsXG4gICAqIGJ1dCB0aGVyZSBpcyBhbHNvIGFuIGFkZGl0aW9uYWwgc2xhc2ggYWZ0ZXIgdGhlIGNvbG9uIHNlcGFyYXRpbmcgJ2FjY291bnQnIGZyb20gJ3Jlc291cmNlJy5cbiAgICogTGlrZSBpbjogJ2Fybjphd3M6c2VydmljZTpyZWdpb246YWNjb3VudDovcmVzb3VyY2UvcmVzb3VyY2VOYW1lJy5cbiAgICogTm90ZSB0aGF0IHRoZSBsZWFkaW5nIHNsYXNoIGlzIF9ub3RfIGluY2x1ZGVkIGluIHRoZSBwYXJzZWQgJ3Jlc291cmNlJyBwYXJ0LlxuICAgKi9cbiAgU0xBU0hfUkVTT1VSQ0VfU0xBU0hfUkVTT1VSQ0VfTkFNRSA9ICdhcm46YXdzOnNlcnZpY2U6cmVnaW9uOmFjY291bnQ6L3Jlc291cmNlL3Jlc291cmNlTmFtZScsXG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQXJuQ29tcG9uZW50cyB7XG4gIC8qKlxuICAgKiBUaGUgcGFydGl0aW9uIHRoYXQgdGhlIHJlc291cmNlIGlzIGluLiBGb3Igc3RhbmRhcmQgQVdTIHJlZ2lvbnMsIHRoZVxuICAgKiBwYXJ0aXRpb24gaXMgYXdzLiBJZiB5b3UgaGF2ZSByZXNvdXJjZXMgaW4gb3RoZXIgcGFydGl0aW9ucywgdGhlXG4gICAqIHBhcnRpdGlvbiBpcyBhd3MtcGFydGl0aW9ubmFtZS4gRm9yIGV4YW1wbGUsIHRoZSBwYXJ0aXRpb24gZm9yIHJlc291cmNlc1xuICAgKiBpbiB0aGUgQ2hpbmEgKEJlaWppbmcpIHJlZ2lvbiBpcyBhd3MtY24uXG4gICAqXG4gICAqIEBkZWZhdWx0IFRoZSBBV1MgcGFydGl0aW9uIHRoZSBzdGFjayBpcyBkZXBsb3llZCB0by5cbiAgICovXG4gIHJlYWRvbmx5IHBhcnRpdGlvbj86IHN0cmluZztcblxuICAvKipcbiAgICogVGhlIHNlcnZpY2UgbmFtZXNwYWNlIHRoYXQgaWRlbnRpZmllcyB0aGUgQVdTIHByb2R1Y3QgKGZvciBleGFtcGxlLFxuICAgKiAnczMnLCAnaWFtJywgJ2NvZGVwaXBsaW5lJykuXG4gICAqL1xuICByZWFkb25seSBzZXJ2aWNlOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFRoZSByZWdpb24gdGhlIHJlc291cmNlIHJlc2lkZXMgaW4uIE5vdGUgdGhhdCB0aGUgQVJOcyBmb3Igc29tZSByZXNvdXJjZXNcbiAgICogZG8gbm90IHJlcXVpcmUgYSByZWdpb24sIHNvIHRoaXMgY29tcG9uZW50IG1pZ2h0IGJlIG9taXR0ZWQuXG4gICAqXG4gICAqIEBkZWZhdWx0IFRoZSByZWdpb24gdGhlIHN0YWNrIGlzIGRlcGxveWVkIHRvLlxuICAgKi9cbiAgcmVhZG9ubHkgcmVnaW9uPzogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBUaGUgSUQgb2YgdGhlIEFXUyBhY2NvdW50IHRoYXQgb3ducyB0aGUgcmVzb3VyY2UsIHdpdGhvdXQgdGhlIGh5cGhlbnMuXG4gICAqIEZvciBleGFtcGxlLCAxMjM0NTY3ODkwMTIuIE5vdGUgdGhhdCB0aGUgQVJOcyBmb3Igc29tZSByZXNvdXJjZXMgZG9uJ3RcbiAgICogcmVxdWlyZSBhbiBhY2NvdW50IG51bWJlciwgc28gdGhpcyBjb21wb25lbnQgbWlnaHQgYmUgb21pdHRlZC5cbiAgICpcbiAgICogQGRlZmF1bHQgVGhlIGFjY291bnQgdGhlIHN0YWNrIGlzIGRlcGxveWVkIHRvLlxuICAgKi9cbiAgcmVhZG9ubHkgYWNjb3VudD86IHN0cmluZztcblxuICAvKipcbiAgICogUmVzb3VyY2UgdHlwZSAoZS5nLiBcInRhYmxlXCIsIFwiYXV0b1NjYWxpbmdHcm91cFwiLCBcImNlcnRpZmljYXRlXCIpLlxuICAgKiBGb3Igc29tZSByZXNvdXJjZSB0eXBlcywgZS5nLiBTMyBidWNrZXRzLCB0aGlzIGZpZWxkIGRlZmluZXMgdGhlIGJ1Y2tldCBuYW1lLlxuICAgKi9cbiAgcmVhZG9ubHkgcmVzb3VyY2U6IHN0cmluZztcblxuICAvKipcbiAgICogU2VwYXJhdG9yIGJldHdlZW4gcmVzb3VyY2UgdHlwZSBhbmQgdGhlIHJlc291cmNlLlxuICAgKlxuICAgKiBDYW4gYmUgZWl0aGVyICcvJywgJzonIG9yIGFuIGVtcHR5IHN0cmluZy4gV2lsbCBvbmx5IGJlIHVzZWQgaWYgcmVzb3VyY2VOYW1lIGlzIGRlZmluZWQuXG4gICAqIEBkZWZhdWx0ICcvJ1xuICAgKlxuICAgKiBAZGVwcmVjYXRlZCB1c2UgYXJuRm9ybWF0IGluc3RlYWRcbiAgICovXG4gIHJlYWRvbmx5IHNlcD86IHN0cmluZztcblxuICAvKipcbiAgICogUmVzb3VyY2UgbmFtZSBvciBwYXRoIHdpdGhpbiB0aGUgcmVzb3VyY2UgKGkuZS4gUzMgYnVja2V0IG9iamVjdCBrZXkpIG9yXG4gICAqIGEgd2lsZGNhcmQgc3VjaCBhcyBgYFwiKlwiYGAuIFRoaXMgaXMgc2VydmljZS1kZXBlbmRlbnQuXG4gICAqL1xuICByZWFkb25seSByZXNvdXJjZU5hbWU/OiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFRoZSBzcGVjaWZpYyBBUk4gZm9ybWF0IHRvIHVzZSBmb3IgdGhpcyBBUk4gdmFsdWUuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gdXNlcyB2YWx1ZSBvZiBgc2VwYCBhcyB0aGUgc2VwYXJhdG9yIGZvciBmb3JtYXR0aW5nLFxuICAgKiAgIGBBcm5Gb3JtYXQuU0xBU0hfUkVTT1VSQ0VfTkFNRWAgaWYgdGhhdCBwcm9wZXJ0eSB3YXMgYWxzbyBub3QgcHJvdmlkZWRcbiAgICovXG4gIHJlYWRvbmx5IGFybkZvcm1hdD86IEFybkZvcm1hdDtcbn1cblxuZXhwb3J0IGNsYXNzIEFybiB7XG4gIC8qKlxuICAgKiBDcmVhdGVzIGFuIEFSTiBmcm9tIGNvbXBvbmVudHMuXG4gICAqXG4gICAqIElmIGBwYXJ0aXRpb25gLCBgcmVnaW9uYCBvciBgYWNjb3VudGAgYXJlIG5vdCBzcGVjaWZpZWQsIHRoZSBzdGFjaydzXG4gICAqIHBhcnRpdGlvbiwgcmVnaW9uIGFuZCBhY2NvdW50IHdpbGwgYmUgdXNlZC5cbiAgICpcbiAgICogSWYgYW55IGNvbXBvbmVudCBpcyB0aGUgZW1wdHkgc3RyaW5nLCBhbiBlbXB0eSBzdHJpbmcgd2lsbCBiZSBpbnNlcnRlZFxuICAgKiBpbnRvIHRoZSBnZW5lcmF0ZWQgQVJOIGF0IHRoZSBsb2NhdGlvbiB0aGF0IGNvbXBvbmVudCBjb3JyZXNwb25kcyB0by5cbiAgICpcbiAgICogVGhlIEFSTiB3aWxsIGJlIGZvcm1hdHRlZCBhcyBmb2xsb3dzOlxuICAgKlxuICAgKiAgIGFybjp7cGFydGl0aW9ufTp7c2VydmljZX06e3JlZ2lvbn06e2FjY291bnR9OntyZXNvdXJjZX17c2VwfXtyZXNvdXJjZS1uYW1lfVxuICAgKlxuICAgKiBUaGUgcmVxdWlyZWQgQVJOIHBpZWNlcyB0aGF0IGFyZSBvbWl0dGVkIHdpbGwgYmUgdGFrZW4gZnJvbSB0aGUgc3RhY2sgdGhhdFxuICAgKiB0aGUgJ3Njb3BlJyBpcyBhdHRhY2hlZCB0by4gSWYgYWxsIEFSTiBwaWVjZXMgYXJlIHN1cHBsaWVkLCB0aGUgc3VwcGxpZWQgc2NvcGVcbiAgICogY2FuIGJlICd1bmRlZmluZWQnLlxuICAgKi9cbiAgcHVibGljIHN0YXRpYyBmb3JtYXQoY29tcG9uZW50czogQXJuQ29tcG9uZW50cywgc3RhY2s/OiBTdGFjayk6IHN0cmluZyB7XG4gICAgY29uc3QgcGFydGl0aW9uID0gY29tcG9uZW50cy5wYXJ0aXRpb24gPz8gc3RhY2s/LnBhcnRpdGlvbjtcbiAgICBjb25zdCByZWdpb24gPSBjb21wb25lbnRzLnJlZ2lvbiA/PyBzdGFjaz8ucmVnaW9uO1xuICAgIGNvbnN0IGFjY291bnQgPSBjb21wb25lbnRzLmFjY291bnQgPz8gc3RhY2s/LmFjY291bnQ7XG5cbiAgICAvLyBDYXRjaCBib3RoICdudWxsJyBhbmQgJ3VuZGVmaW5lZCdcbiAgICBpZiAocGFydGl0aW9uID09IG51bGwgfHwgcmVnaW9uID09IG51bGwgfHwgYWNjb3VudCA9PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEFybi5mb3JtYXQ6IHBhcnRpdGlvbiAoJHtwYXJ0aXRpb259KSwgcmVnaW9uICgke3JlZ2lvbn0pLCBhbmQgYWNjb3VudCAoJHthY2NvdW50fSkgbXVzdCBhbGwgYmUgcGFzc2VkIGlmIHN0YWNrIGlzIG5vdCBwYXNzZWQuYCk7XG4gICAgfVxuXG4gICAgY29uc3Qgc2VwID0gY29tcG9uZW50cy5zZXAgPz8gKGNvbXBvbmVudHMuYXJuRm9ybWF0ID09PSBBcm5Gb3JtYXQuQ09MT05fUkVTT1VSQ0VfTkFNRSA/ICc6JyA6ICcvJyk7XG5cbiAgICBjb25zdCB2YWx1ZXMgPSBbXG4gICAgICAnYXJuJywgJzonLCBwYXJ0aXRpb24sICc6JywgY29tcG9uZW50cy5zZXJ2aWNlLCAnOicsIHJlZ2lvbiwgJzonLCBhY2NvdW50LCAnOicsXG4gICAgICAuLi4oY29tcG9uZW50cy5hcm5Gb3JtYXQgPT09IEFybkZvcm1hdC5TTEFTSF9SRVNPVVJDRV9TTEFTSF9SRVNPVVJDRV9OQU1FID8gWycvJ10gOiBbXSksXG4gICAgICBjb21wb25lbnRzLnJlc291cmNlLFxuICAgIF07XG5cbiAgICBpZiAoc2VwICE9PSAnLycgJiYgc2VwICE9PSAnOicgJiYgc2VwICE9PSAnJykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdyZXNvdXJjZVBhdGhTZXAgbWF5IG9ubHkgYmUgXCI6XCIsIFwiL1wiIG9yIGFuIGVtcHR5IHN0cmluZycpO1xuICAgIH1cblxuICAgIGlmIChjb21wb25lbnRzLnJlc291cmNlTmFtZSAhPSBudWxsKSB7XG4gICAgICB2YWx1ZXMucHVzaChzZXApO1xuICAgICAgdmFsdWVzLnB1c2goY29tcG9uZW50cy5yZXNvdXJjZU5hbWUpO1xuICAgIH1cblxuICAgIHJldHVybiB2YWx1ZXMuam9pbignJyk7XG4gIH1cblxuICAvKipcbiAgICogR2l2ZW4gYW4gQVJOLCBwYXJzZXMgaXQgYW5kIHJldHVybnMgY29tcG9uZW50cy5cbiAgICpcbiAgICogSUYgVEhFIEFSTiBJUyBBIENPTkNSRVRFIFNUUklORy4uLlxuICAgKlxuICAgKiAuLi5pdCB3aWxsIGJlIHBhcnNlZCBhbmQgdmFsaWRhdGVkLiBUaGUgc2VwYXJhdG9yIChgc2VwYCkgd2lsbCBiZSBzZXQgdG8gJy8nXG4gICAqIGlmIHRoZSA2dGggY29tcG9uZW50IGluY2x1ZGVzIGEgJy8nLCBpbiB3aGljaCBjYXNlLCBgcmVzb3VyY2VgIHdpbGwgYmUgc2V0XG4gICAqIHRvIHRoZSB2YWx1ZSBiZWZvcmUgdGhlICcvJyBhbmQgYHJlc291cmNlTmFtZWAgd2lsbCBiZSB0aGUgcmVzdC4gSW4gY2FzZVxuICAgKiB0aGVyZSBpcyBubyAnLycsIGByZXNvdXJjZWAgd2lsbCBiZSBzZXQgdG8gdGhlIDZ0aCBjb21wb25lbnRzIGFuZFxuICAgKiBgcmVzb3VyY2VOYW1lYCB3aWxsIGJlIHNldCB0byB0aGUgcmVzdCBvZiB0aGUgc3RyaW5nLlxuICAgKlxuICAgKiBJRiBUSEUgQVJOIElTIEEgVE9LRU4uLi5cbiAgICpcbiAgICogLi4uaXQgY2Fubm90IGJlIHZhbGlkYXRlZCwgc2luY2Ugd2UgZG9uJ3QgaGF2ZSB0aGUgYWN0dWFsIHZhbHVlIHlldCBhdCB0aGVcbiAgICogdGltZSBvZiB0aGlzIGZ1bmN0aW9uIGNhbGwuIFlvdSB3aWxsIGhhdmUgdG8gc3VwcGx5IGBzZXBJZlRva2VuYCBhbmRcbiAgICogd2hldGhlciBvciBub3QgQVJOcyBvZiB0aGUgZXhwZWN0ZWQgZm9ybWF0IHVzdWFsbHkgaGF2ZSByZXNvdXJjZSBuYW1lc1xuICAgKiBpbiBvcmRlciB0byBwYXJzZSBpdCBwcm9wZXJseS4gVGhlIHJlc3VsdGluZyBgQXJuQ29tcG9uZW50c2Agb2JqZWN0IHdpbGxcbiAgICogY29udGFpbiB0b2tlbnMgZm9yIHRoZSBzdWJleHByZXNzaW9ucyBvZiB0aGUgQVJOLCBub3Qgc3RyaW5nIGxpdGVyYWxzLlxuICAgKlxuICAgKiBJZiB0aGUgcmVzb3VyY2UgbmFtZSBjb3VsZCBwb3NzaWJseSBjb250YWluIHRoZSBzZXBhcmF0b3IgY2hhciwgdGhlIGFjdHVhbFxuICAgKiByZXNvdXJjZSBuYW1lIGNhbm5vdCBiZSBwcm9wZXJseSBwYXJzZWQuIFRoaXMgb25seSBvY2N1cnMgaWYgdGhlIHNlcGFyYXRvclxuICAgKiBjaGFyIGlzICcvJywgYW5kIGhhcHBlbnMgZm9yIGV4YW1wbGUgZm9yIFMzIG9iamVjdCBBUk5zLCBJQU0gUm9sZSBBUk5zLFxuICAgKiBJQU0gT0lEQyBQcm92aWRlciBBUk5zLCBldGMuIFRvIHByb3Blcmx5IGV4dHJhY3QgdGhlIHJlc291cmNlIG5hbWUgZnJvbSBhXG4gICAqIFRva2VuaXplZCBBUk4sIHlvdSBtdXN0IGtub3cgdGhlIHJlc291cmNlIHR5cGUgYW5kIGNhbGxcbiAgICogYEFybi5leHRyYWN0UmVzb3VyY2VOYW1lYC5cbiAgICpcbiAgICogQHBhcmFtIGFybiBUaGUgQVJOIHRvIHBhcnNlXG4gICAqIEBwYXJhbSBzZXBJZlRva2VuIFRoZSBzZXBhcmF0b3IgdXNlZCB0byBzZXBhcmF0ZSByZXNvdXJjZSBmcm9tIHJlc291cmNlTmFtZVxuICAgKiBAcGFyYW0gaGFzTmFtZSBXaGV0aGVyIHRoZXJlIGlzIGEgbmFtZSBjb21wb25lbnQgaW4gdGhlIEFSTiBhdCBhbGwuIEZvclxuICAgKiBleGFtcGxlLCBTTlMgVG9waWNzIEFSTnMgaGF2ZSB0aGUgJ3Jlc291cmNlJyBjb21wb25lbnQgY29udGFpbiB0aGUgdG9waWNcbiAgICogbmFtZSwgYW5kIG5vICdyZXNvdXJjZU5hbWUnIGNvbXBvbmVudC5cbiAgICpcbiAgICogQHJldHVybnMgYW4gQXJuQ29tcG9uZW50cyBvYmplY3Qgd2hpY2ggYWxsb3dzIGFjY2VzcyB0byB0aGUgdmFyaW91c1xuICAgKiBjb21wb25lbnRzIG9mIHRoZSBBUk4uXG4gICAqXG4gICAqIEByZXR1cm5zIGFuIEFybkNvbXBvbmVudHMgb2JqZWN0IHdoaWNoIGFsbG93cyBhY2Nlc3MgdG8gdGhlIHZhcmlvdXNcbiAgICogICAgICBjb21wb25lbnRzIG9mIHRoZSBBUk4uXG4gICAqXG4gICAqIEBkZXByZWNhdGVkIHVzZSBzcGxpdCBpbnN0ZWFkXG4gICAqL1xuICBwdWJsaWMgc3RhdGljIHBhcnNlKGFybjogc3RyaW5nLCBzZXBJZlRva2VuOiBzdHJpbmcgPSAnLycsIGhhc05hbWU6IGJvb2xlYW4gPSB0cnVlKTogQXJuQ29tcG9uZW50cyB7XG4gICAgbGV0IGFybkZvcm1hdDogQXJuRm9ybWF0O1xuICAgIGlmICghaGFzTmFtZSkge1xuICAgICAgYXJuRm9ybWF0ID0gQXJuRm9ybWF0Lk5PX1JFU09VUkNFX05BTUU7XG4gICAgfSBlbHNlIHtcbiAgICAgIGFybkZvcm1hdCA9IHNlcElmVG9rZW4gPT09ICcvJyA/IEFybkZvcm1hdC5TTEFTSF9SRVNPVVJDRV9OQU1FIDogQXJuRm9ybWF0LkNPTE9OX1JFU09VUkNFX05BTUU7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLnNwbGl0KGFybiwgYXJuRm9ybWF0KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTcGxpdHMgdGhlIHByb3ZpZGVkIEFSTiBpbnRvIGl0cyBjb21wb25lbnRzLlxuICAgKiBXb3JrcyBib3RoIGlmICdhcm4nIGlzIGEgc3RyaW5nIGxpa2UgJ2Fybjphd3M6czM6OjpidWNrZXQnLFxuICAgKiBhbmQgYSBUb2tlbiByZXByZXNlbnRpbmcgYSBkeW5hbWljIENsb3VkRm9ybWF0aW9uIGV4cHJlc3Npb25cbiAgICogKGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIGNvbXBvbmVudHMgd2lsbCBhbHNvIGJlIGR5bmFtaWMgQ2xvdWRGb3JtYXRpb24gZXhwcmVzc2lvbnMsXG4gICAqIGVuY29kZWQgYXMgVG9rZW5zKS5cbiAgICpcbiAgICogQHBhcmFtIGFybiB0aGUgQVJOIHRvIHNwbGl0IGludG8gaXRzIGNvbXBvbmVudHNcbiAgICogQHBhcmFtIGFybkZvcm1hdCB0aGUgZXhwZWN0ZWQgZm9ybWF0IG9mICdhcm4nIC0gZGVwZW5kcyBvbiB3aGF0IGZvcm1hdCB0aGUgc2VydmljZSAnYXJuJyByZXByZXNlbnRzIHVzZXNcbiAgICovXG4gIHB1YmxpYyBzdGF0aWMgc3BsaXQoYXJuOiBzdHJpbmcsIGFybkZvcm1hdDogQXJuRm9ybWF0KTogQXJuQ29tcG9uZW50cyB7XG4gICAgY29uc3QgY29tcG9uZW50cyA9IHBhcnNlQXJuU2hhcGUoYXJuKTtcbiAgICBpZiAoY29tcG9uZW50cyA9PT0gJ3Rva2VuJykge1xuICAgICAgcmV0dXJuIHBhcnNlVG9rZW5Bcm4oYXJuLCBhcm5Gb3JtYXQpO1xuICAgIH1cblxuICAgIGNvbnN0IFssIHBhcnRpdGlvbiwgc2VydmljZSwgcmVnaW9uLCBhY2NvdW50LCByZXNvdXJjZVR5cGVPck5hbWUsIC4uLnJlc3RdID0gY29tcG9uZW50cztcblxuICAgIGxldCByZXNvdXJjZTogc3RyaW5nO1xuICAgIGxldCByZXNvdXJjZU5hbWU6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgICBsZXQgc2VwOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gICAgbGV0IHJlc291cmNlUGFydFN0YXJ0SW5kZXggPSAwO1xuICAgIGxldCBkZXRlY3RlZEFybkZvcm1hdDogQXJuRm9ybWF0O1xuXG4gICAgbGV0IHNsYXNoSW5kZXggPSByZXNvdXJjZVR5cGVPck5hbWUuaW5kZXhPZignLycpO1xuICAgIGlmIChzbGFzaEluZGV4ID09PSAwKSB7XG4gICAgICAvLyBuZXctc3R5bGUgQVJOcyBhcmUgb2YgdGhlIGZvcm0gJ2Fybjphd3M6czQ6dXMtd2VzdC0xOjEyMzQ1Oi9yZXNvdXJjZS10eXBlL3Jlc291cmNlLW5hbWUnXG4gICAgICBzbGFzaEluZGV4ID0gcmVzb3VyY2VUeXBlT3JOYW1lLmluZGV4T2YoJy8nLCAxKTtcbiAgICAgIHJlc291cmNlUGFydFN0YXJ0SW5kZXggPSAxO1xuICAgICAgZGV0ZWN0ZWRBcm5Gb3JtYXQgPSBBcm5Gb3JtYXQuU0xBU0hfUkVTT1VSQ0VfU0xBU0hfUkVTT1VSQ0VfTkFNRTtcbiAgICB9XG4gICAgaWYgKHNsYXNoSW5kZXggIT09IC0xKSB7XG4gICAgICAvLyB0aGUgc2xhc2ggaXMgb25seSBhIHNlcGFyYXRvciBpZiBBcm5Gb3JtYXQgaXMgbm90IE5PX1JFU09VUkNFX05BTUVcbiAgICAgIGlmIChhcm5Gb3JtYXQgPT09IEFybkZvcm1hdC5OT19SRVNPVVJDRV9OQU1FKSB7XG4gICAgICAgIHNlcCA9IHVuZGVmaW5lZDtcbiAgICAgICAgc2xhc2hJbmRleCA9IC0xO1xuICAgICAgICBkZXRlY3RlZEFybkZvcm1hdCA9IEFybkZvcm1hdC5OT19SRVNPVVJDRV9OQU1FO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgc2VwID0gJy8nO1xuICAgICAgICBkZXRlY3RlZEFybkZvcm1hdCA9IHJlc291cmNlUGFydFN0YXJ0SW5kZXggPT09IDBcbiAgICAgICAgICA/IEFybkZvcm1hdC5TTEFTSF9SRVNPVVJDRV9OQU1FXG4gICAgICAgICAgLy8gbmVlZCB0byByZXBlYXQgdGhpcyBoZXJlLCBhcyBvdGhlcndpc2UgdGhlIGNvbXBpbGVyIHRoaW5rcyAnZGV0ZWN0ZWRBcm5Gb3JtYXQnIGlzIG5vdCBpbml0aWFsaXplZCBpbiBhbGwgcGF0aHNcbiAgICAgICAgICA6IEFybkZvcm1hdC5TTEFTSF9SRVNPVVJDRV9TTEFTSF9SRVNPVVJDRV9OQU1FO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAocmVzdC5sZW5ndGggPiAwKSB7XG4gICAgICBzZXAgPSAnOic7XG4gICAgICBzbGFzaEluZGV4ID0gLTE7XG4gICAgICBkZXRlY3RlZEFybkZvcm1hdCA9IEFybkZvcm1hdC5DT0xPTl9SRVNPVVJDRV9OQU1FO1xuICAgIH0gZWxzZSB7XG4gICAgICBzZXAgPSB1bmRlZmluZWQ7XG4gICAgICBkZXRlY3RlZEFybkZvcm1hdCA9IEFybkZvcm1hdC5OT19SRVNPVVJDRV9OQU1FO1xuICAgIH1cblxuICAgIGlmIChzbGFzaEluZGV4ICE9PSAtMSkge1xuICAgICAgcmVzb3VyY2UgPSByZXNvdXJjZVR5cGVPck5hbWUuc3Vic3RyaW5nKHJlc291cmNlUGFydFN0YXJ0SW5kZXgsIHNsYXNoSW5kZXgpO1xuICAgICAgcmVzb3VyY2VOYW1lID0gcmVzb3VyY2VUeXBlT3JOYW1lLnN1YnN0cmluZyhzbGFzaEluZGV4ICsgMSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJlc291cmNlID0gcmVzb3VyY2VUeXBlT3JOYW1lO1xuICAgIH1cblxuICAgIGlmIChyZXN0Lmxlbmd0aCA+IDApIHtcbiAgICAgIGlmICghcmVzb3VyY2VOYW1lKSB7XG4gICAgICAgIHJlc291cmNlTmFtZSA9ICcnO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmVzb3VyY2VOYW1lICs9ICc6JztcbiAgICAgIH1cblxuICAgICAgcmVzb3VyY2VOYW1lICs9IHJlc3Quam9pbignOicpO1xuICAgIH1cblxuICAgIC8vIFwifHwgdW5kZWZpbmVkXCIgd2lsbCBjYXVzZSBlbXB0eSBzdHJpbmdzIHRvIGJlIHRyZWF0ZWQgYXMgXCJ1bmRlZmluZWRcIi5cbiAgICAvLyBPcHRpb25hbCBBUk4gYXR0cmlidXRlcyAoZS5nLiByZWdpb24sIGFjY291bnQpIHNob3VsZCByZXR1cm4gYXMgZW1wdHkgc3RyaW5nXG4gICAgLy8gaWYgdGhleSBhcmUgcHJvdmlkZWQgYXMgc3VjaC5cbiAgICByZXR1cm4gZmlsdGVyVW5kZWZpbmVkKHtcbiAgICAgIHNlcnZpY2U6IHNlcnZpY2UgfHwgdW5kZWZpbmVkLFxuICAgICAgcmVzb3VyY2U6IHJlc291cmNlIHx8IHVuZGVmaW5lZCxcbiAgICAgIHBhcnRpdGlvbjogcGFydGl0aW9uIHx8IHVuZGVmaW5lZCxcbiAgICAgIHJlZ2lvbixcbiAgICAgIGFjY291bnQsXG4gICAgICByZXNvdXJjZU5hbWUsXG4gICAgICBzZXAsXG4gICAgICBhcm5Gb3JtYXQ6IGRldGVjdGVkQXJuRm9ybWF0LFxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEV4dHJhY3QgdGhlIGZ1bGwgcmVzb3VyY2UgbmFtZSBmcm9tIGFuIEFSTlxuICAgKlxuICAgKiBOZWNlc3NhcnkgZm9yIHJlc291cmNlIG5hbWVzIChwYXRocykgdGhhdCBtYXkgY29udGFpbiB0aGUgc2VwYXJhdG9yLCBsaWtlXG4gICAqIGBhcm46YXdzOmlhbTo6MTExMTExMTExMTExOnJvbGUvcGF0aC90by9yb2xlL25hbWVgLlxuICAgKlxuICAgKiBPbmx5IHdvcmtzIGlmIHdlIHN0YXRpY2FsbHkga25vdyB0aGUgZXhwZWN0ZWQgYHJlc291cmNlVHlwZWAgYmVmb3JlaGFuZCwgc2luY2Ugd2UncmUgZ29pbmdcbiAgICogdG8gdXNlIHRoYXQgdG8gc3BsaXQgdGhlIHN0cmluZyBvbiAnOjxyZXNvdXJjZVR5cGU+LycgKGFuZCB0YWtlIHRoZSByaWdodC1oYW5kIHNpZGUpLlxuICAgKlxuICAgKiBXZSBjYW4ndCBleHRyYWN0IHRoZSAncmVzb3VyY2VUeXBlJyBmcm9tIHRoZSBBUk4gYXQgaGFuZCwgYmVjYXVzZSBDbG91ZEZvcm1hdGlvbiBFeHByZXNzaW9uc1xuICAgKiBvbmx5IGFsbG93IGxpdGVyYWxzIGluIHRoZSAnc2VwYXJhdG9yJyBhcmd1bWVudCB0byBgeyBGbjo6U3BsaXQgfWAsIGFuZCBzbyBpdCBjYW4ndCBiZVxuICAgKiBgeyBGbjo6U2VsZWN0OiBbNSwgeyBGbjo6U3BsaXQ6IFsnOicsIEFSTl0gfX1gLlxuICAgKlxuICAgKiBPbmx5IG5lY2Vzc2FyeSBmb3IgQVJOIGZvcm1hdHMgZm9yIHdoaWNoIHRoZSB0eXBlLW5hbWUgc2VwYXJhdG9yIGlzIGAvYC5cbiAgICovXG4gIHB1YmxpYyBzdGF0aWMgZXh0cmFjdFJlc291cmNlTmFtZShhcm46IHN0cmluZywgcmVzb3VyY2VUeXBlOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIGNvbnN0IGNvbXBvbmVudHMgPSBwYXJzZUFyblNoYXBlKGFybik7XG4gICAgaWYgKGNvbXBvbmVudHMgPT09ICd0b2tlbicpIHtcbiAgICAgIHJldHVybiBGbi5zZWxlY3QoMSwgRm4uc3BsaXQoYDoke3Jlc291cmNlVHlwZX0vYCwgYXJuKSk7XG4gICAgfVxuXG4gICAgLy8gQXBwYXJlbnRseSB3ZSBjb3VsZCBqdXN0IHBhcnNlIHRoaXMgcmlnaHQgYXdheS4gVmFsaWRhdGUgdGhhdCB3ZSBnb3QgdGhlIHJpZ2h0XG4gICAgLy8gcmVzb3VyY2UgdHlwZSAodG8gbm90aWZ5IGF1dGhvcnMgb2YgaW5jb3JyZWN0IGFzc3VtcHRpb25zIHJpZ2h0IGF3YXkpLlxuICAgIGNvbnN0IHBhcnNlZCA9IEFybi5zcGxpdChhcm4sIEFybkZvcm1hdC5TTEFTSF9SRVNPVVJDRV9OQU1FKTtcbiAgICBpZiAoIVRva2VuLmlzVW5yZXNvbHZlZChwYXJzZWQucmVzb3VyY2UpICYmIHBhcnNlZC5yZXNvdXJjZSAhPT0gcmVzb3VyY2VUeXBlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkIHJlc291cmNlIHR5cGUgJyR7cmVzb3VyY2VUeXBlfScgaW4gQVJOLCBnb3QgJyR7cGFyc2VkLnJlc291cmNlfScgaW4gJyR7YXJufSdgKTtcbiAgICB9XG4gICAgaWYgKCFwYXJzZWQucmVzb3VyY2VOYW1lKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkIHJlc291cmNlIG5hbWUgaW4gQVJOLCBkaWRuJ3QgZmluZCBvbmU6ICcke2Fybn0nYCk7XG4gICAgfVxuICAgIHJldHVybiBwYXJzZWQucmVzb3VyY2VOYW1lO1xuICB9XG5cbiAgcHJpdmF0ZSBjb25zdHJ1Y3RvcigpIHsgfVxufVxuXG4vKipcbiAqIEdpdmVuIGEgVG9rZW4gZXZhbHVhdGluZyB0byBBUk4sIHBhcnNlcyBpdCBhbmQgcmV0dXJucyBjb21wb25lbnRzLlxuICpcbiAqIFRoZSBBUk4gY2Fubm90IGJlIHZhbGlkYXRlZCwgc2luY2Ugd2UgZG9uJ3QgaGF2ZSB0aGUgYWN0dWFsIHZhbHVlIHlldFxuICogYXQgdGhlIHRpbWUgb2YgdGhpcyBmdW5jdGlvbiBjYWxsLiBZb3Ugd2lsbCBoYXZlIHRvIGtub3cgdGhlIHNlcGFyYXRvclxuICogYW5kIHRoZSB0eXBlIG9mIEFSTi5cbiAqXG4gKiBUaGUgcmVzdWx0aW5nIGBBcm5Db21wb25lbnRzYCBvYmplY3Qgd2lsbCBjb250YWluIHRva2VucyBmb3IgdGhlXG4gKiBzdWJleHByZXNzaW9ucyBvZiB0aGUgQVJOLCBub3Qgc3RyaW5nIGxpdGVyYWxzLlxuICpcbiAqIFdBUk5JTkc6IHRoaXMgZnVuY3Rpb24gY2Fubm90IHByb3Blcmx5IHBhcnNlIHRoZSBjb21wbGV0ZSBmaW5hbFxuICogJ3Jlc291cmNlTmFtZScgcGFydCBpZiBpdCBjb250YWlucyBjb2xvbnMsXG4gKiBsaWtlICdhcm46YXdzOmNvZ25pdG8tc3luYzpyZWdpb246YWNjb3VudDppZGVudGl0eXBvb2wvdXMtZWFzdC0xOjFhMWExYTFhLWZmZmYtMTExMS05OTk5LTEyMzQ1Njc4OmJsYScuXG4gKlxuICogQHBhcmFtIGFyblRva2VuIFRoZSBpbnB1dCB0b2tlbiB0aGF0IGNvbnRhaW5zIGFuIEFSTlxuICogQHBhcmFtIGFybkZvcm1hdCB0aGUgZXhwZWN0ZWQgZm9ybWF0IG9mICdhcm4nIC0gZGVwZW5kcyBvbiB3aGF0IGZvcm1hdCB0aGUgc2VydmljZSB0aGUgQVJOIHJlcHJlc2VudHMgdXNlc1xuICovXG5mdW5jdGlvbiBwYXJzZVRva2VuQXJuKGFyblRva2VuOiBzdHJpbmcsIGFybkZvcm1hdDogQXJuRm9ybWF0KTogQXJuQ29tcG9uZW50cyB7XG4gIC8vIEFSTiBsb29rcyBsaWtlOlxuICAvLyBhcm46cGFydGl0aW9uOnNlcnZpY2U6cmVnaW9uOmFjY291bnQ6cmVzb3VyY2VcbiAgLy8gYXJuOnBhcnRpdGlvbjpzZXJ2aWNlOnJlZ2lvbjphY2NvdW50OnJlc291cmNlOnJlc291cmNlTmFtZVxuICAvLyBhcm46cGFydGl0aW9uOnNlcnZpY2U6cmVnaW9uOmFjY291bnQ6cmVzb3VyY2UvcmVzb3VyY2VOYW1lXG4gIC8vIGFybjpwYXJ0aXRpb246c2VydmljZTpyZWdpb246YWNjb3VudDovcmVzb3VyY2UvcmVzb3VyY2VOYW1lXG5cbiAgY29uc3QgY29tcG9uZW50cyA9IEZuLnNwbGl0KCc6JywgYXJuVG9rZW4pO1xuXG4gIGNvbnN0IHBhcnRpdGlvbiA9IEZuLnNlbGVjdCgxLCBjb21wb25lbnRzKS50b1N0cmluZygpO1xuICBjb25zdCBzZXJ2aWNlID0gRm4uc2VsZWN0KDIsIGNvbXBvbmVudHMpLnRvU3RyaW5nKCk7XG4gIGNvbnN0IHJlZ2lvbiA9IEZuLnNlbGVjdCgzLCBjb21wb25lbnRzKS50b1N0cmluZygpO1xuICBjb25zdCBhY2NvdW50ID0gRm4uc2VsZWN0KDQsIGNvbXBvbmVudHMpLnRvU3RyaW5nKCk7XG4gIGxldCByZXNvdXJjZTogc3RyaW5nO1xuICBsZXQgcmVzb3VyY2VOYW1lOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIGxldCBzZXA6IHN0cmluZyB8IHVuZGVmaW5lZDtcblxuICBpZiAoYXJuRm9ybWF0ID09PSBBcm5Gb3JtYXQuTk9fUkVTT1VSQ0VfTkFNRSB8fCBhcm5Gb3JtYXQgPT09IEFybkZvcm1hdC5DT0xPTl9SRVNPVVJDRV9OQU1FKSB7XG4gICAgLy8gd2Uga25vdyB0aGF0IHRoZSAncmVzb3VyY2UnIHBhcnQgd2lsbCBhbHdheXMgYmUgdGhlIDZ0aCBzZWdtZW50IGluIHRoaXMgY2FzZVxuICAgIHJlc291cmNlID0gRm4uc2VsZWN0KDUsIGNvbXBvbmVudHMpO1xuICAgIGlmIChhcm5Gb3JtYXQgPT09IEFybkZvcm1hdC5DT0xPTl9SRVNPVVJDRV9OQU1FKSB7XG4gICAgICByZXNvdXJjZU5hbWUgPSBGbi5zZWxlY3QoNiwgY29tcG9uZW50cyk7XG4gICAgICBzZXAgPSAnOic7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJlc291cmNlTmFtZSA9IHVuZGVmaW5lZDtcbiAgICAgIHNlcCA9IHVuZGVmaW5lZDtcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgLy8gd2Uga25vdyB0aGF0IHRoZSAncmVzb3VyY2UnIGFuZCAncmVzb3VyY2VOYW1lJyBwYXJ0cyBhcmUgc2VwYXJhdGVkIGJ5IHNsYXNoIGhlcmUsXG4gICAgLy8gc28gd2Ugc3BsaXQgdGhlIDZ0aCBzZWdtZW50IGZyb20gdGhlIGNvbG9uLXNlcGFyYXRlZCBvbmVzIHdpdGggYSBzbGFzaFxuICAgIGNvbnN0IGxhc3RDb21wb25lbnRzID0gRm4uc3BsaXQoJy8nLCBGbi5zZWxlY3QoNSwgY29tcG9uZW50cykpO1xuXG4gICAgaWYgKGFybkZvcm1hdCA9PT0gQXJuRm9ybWF0LlNMQVNIX1JFU09VUkNFX05BTUUpIHtcbiAgICAgIHJlc291cmNlID0gRm4uc2VsZWN0KDAsIGxhc3RDb21wb25lbnRzKTtcbiAgICAgIHJlc291cmNlTmFtZSA9IEZuLnNlbGVjdCgxLCBsYXN0Q29tcG9uZW50cyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIGFybkZvcm1hdCBpcyBBcm5Gb3JtYXQuU0xBU0hfUkVTT1VSQ0VfU0xBU0hfUkVTT1VSQ0VfTkFNRSxcbiAgICAgIC8vIHdoaWNoIG1lYW5zIHRoZXJlJ3MgYW4gZXh0cmEgc2xhc2ggdGhlcmUgYXQgdGhlIGJlZ2lubmluZyB0aGF0IHdlIG5lZWQgdG8gc2tpcFxuICAgICAgcmVzb3VyY2UgPSBGbi5zZWxlY3QoMSwgbGFzdENvbXBvbmVudHMpO1xuICAgICAgcmVzb3VyY2VOYW1lID0gRm4uc2VsZWN0KDIsIGxhc3RDb21wb25lbnRzKTtcbiAgICB9XG4gICAgc2VwID0gJy8nO1xuICB9XG5cbiAgcmV0dXJuIHsgcGFydGl0aW9uLCBzZXJ2aWNlLCByZWdpb24sIGFjY291bnQsIHJlc291cmNlLCByZXNvdXJjZU5hbWUsIHNlcCwgYXJuRm9ybWF0IH07XG59XG5cbi8qKlxuICogVmFsaWRhdGUgdGhhdCBhIHN0cmluZyBpcyBlaXRoZXIgdW5wYXJzZWFibGUgb3IgbG9va3MgbW9zdGx5IGxpa2UgYW4gQVJOXG4gKi9cbmZ1bmN0aW9uIHBhcnNlQXJuU2hhcGUoYXJuOiBzdHJpbmcpOiAndG9rZW4nIHwgc3RyaW5nW10ge1xuICAvLyBhc3N1bWUgYW55dGhpbmcgdGhhdCBzdGFydHMgd2l0aCAnYXJuOicgaXMgYW4gQVJOLFxuICAvLyBzbyB3ZSBjYW4gcmVwb3J0IGJldHRlciBlcnJvcnNcbiAgY29uc3QgbG9va3NMaWtlQXJuID0gYXJuLnN0YXJ0c1dpdGgoJ2FybjonKTtcblxuICBpZiAoIWxvb2tzTGlrZUFybikge1xuICAgIGlmIChUb2tlbi5pc1VucmVzb2x2ZWQoYXJuKSkge1xuICAgICAgcmV0dXJuICd0b2tlbic7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgQVJOcyBtdXN0IHN0YXJ0IHdpdGggXCJhcm46XCIgYW5kIGhhdmUgYXQgbGVhc3QgNiBjb21wb25lbnRzOiAke2Fybn1gKTtcbiAgICB9XG4gIH1cblxuICAvLyBJZiB0aGUgQVJOIG1lcmVseSBjb250YWlucyBUb2tlbnMsIGJ1dCBvdGhlcndpc2UgKmxvb2tzKiBtb3N0bHkgbGlrZSBhbiBBUk4sXG4gIC8vIGl0J3MgYSBzdHJpbmcgb2YgdGhlIGZvcm0gJ2Fybjoke3BhcnRpdGlvbn06c2VydmljZToke3JlZ2lvbn06JHthY2NvdW50fTpyZXNvdXJjZS94eXonLlxuICAvLyBQYXJzZSBmaWVsZHMgb3V0IHRvIHRoZSBiZXN0IG9mIG91ciBhYmlsaXR5LlxuICAvLyBUb2tlbnMgd29uJ3QgY29udGFpbiBcIjpcIiwgc28gdGhpcyB3b24ndCBicmVhayB0aGVtLlxuICBjb25zdCBjb21wb25lbnRzID0gYXJuLnNwbGl0KCc6Jyk7XG5cbiAgY29uc3QgcGFydGl0aW9uID0gY29tcG9uZW50cy5sZW5ndGggPiAxID8gY29tcG9uZW50c1sxXSA6IHVuZGVmaW5lZDtcbiAgaWYgKCFwYXJ0aXRpb24pIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ1RoZSBgcGFydGl0aW9uYCBjb21wb25lbnQgKDJuZCBjb21wb25lbnQpIG9mIGFuIEFSTiBpcyByZXF1aXJlZDogJyArIGFybik7XG4gIH1cblxuICBjb25zdCBzZXJ2aWNlID0gY29tcG9uZW50cy5sZW5ndGggPiAyID8gY29tcG9uZW50c1syXSA6IHVuZGVmaW5lZDtcbiAgaWYgKCFzZXJ2aWNlKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdUaGUgYHNlcnZpY2VgIGNvbXBvbmVudCAoM3JkIGNvbXBvbmVudCkgb2YgYW4gQVJOIGlzIHJlcXVpcmVkOiAnICsgYXJuKTtcbiAgfVxuXG4gIGNvbnN0IHJlc291cmNlID0gY29tcG9uZW50cy5sZW5ndGggPiA1ID8gY29tcG9uZW50c1s1XSA6IHVuZGVmaW5lZDtcbiAgaWYgKCFyZXNvdXJjZSkge1xuICAgIHRocm93IG5ldyBFcnJvcignVGhlIGByZXNvdXJjZWAgY29tcG9uZW50ICg2dGggY29tcG9uZW50KSBvZiBhbiBBUk4gaXMgcmVxdWlyZWQ6ICcgKyBhcm4pO1xuICB9XG5cbiAgLy8gUmVnaW9uIGNhbiBiZSBtaXNzaW5nIGluIGdsb2JhbCBBUk5zIChzdWNoIGFzIHVzZWQgYnkgSUFNKVxuXG4gIC8vIEFjY291bnQgY2FuIGJlIG1pc3NpbmcgaW4gc29tZSBBUk4gdHlwZXMgKHN1Y2ggYXMgdXNlZCBmb3IgUzMgYnVja2V0cylcblxuICByZXR1cm4gY29tcG9uZW50cztcbn1cbiJdfQ==
\No newline at end of file