UNPKG

@aws-cdk/core

Version:

AWS Cloud Development Kit Core Library

437 lines 49.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isNameOfCloudFormationIntrinsic = exports.minimalCloudFormationJoin = exports.CLOUDFORMATION_TOKEN_RESOLVER = exports.CloudFormationLang = void 0; const lazy_1 = require("../lazy"); const resolvable_1 = require("../resolvable"); const stack_1 = require("../stack"); const token_1 = require("../token"); const cfn_utils_provider_1 = require("./cfn-utils-provider"); const resolve_1 = require("./resolve"); /** * Routines that know how to do operations at the CloudFormation document language level */ class CloudFormationLang { /** * Turn an arbitrary structure potentially containing Tokens into a JSON string. * * Returns a Token which will evaluate to CloudFormation expression that * will be evaluated by CloudFormation to the JSON representation of the * input structure. * * All Tokens substituted in this way must return strings, or the evaluation * in CloudFormation will fail. * * @param obj The object to stringify * @param space Indentation to use (default: no pretty-printing) */ static toJSON(obj, space) { return lazy_1.Lazy.uncachedString({ // We used to do this by hooking into `JSON.stringify()` by adding in objects // with custom `toJSON()` functions, but it's ultimately simpler just to // reimplement the `stringify()` function from scratch. produce: (ctx) => tokenAwareStringify(obj, space ?? 0, ctx), }); } /** * Produce a CloudFormation expression to concat two arbitrary expressions when resolving */ static concat(left, right) { if (left === undefined && right === undefined) { return ''; } const parts = new Array(); if (left !== undefined) { parts.push(left); } if (right !== undefined) { parts.push(right); } // Some case analysis to produce minimal expressions if (parts.length === 1) { return parts[0]; } if (parts.length === 2 && isConcatable(parts[0]) && isConcatable(parts[1])) { return `${parts[0]}${parts[1]}`; } // Otherwise return a Join intrinsic (already in the target document language to avoid taking // circular dependencies on FnJoin & friends) return fnJoinConcat(parts); } } exports.CloudFormationLang = CloudFormationLang; /** * Return a CFN intrinsic mass concatting any number of CloudFormation expressions */ function fnJoinConcat(parts) { return { 'Fn::Join': ['', minimalCloudFormationJoin('', parts)] }; } /** * Perform a JSON.stringify()-like operation, except aware of Tokens and CloudFormation intrincics * * Tokens will be resolved and if any resolve to CloudFormation intrinsics, the intrinsics * will be lifted to the top of a giant `{ Fn::Join }` expression. * * If Tokens resolve to primitive types (for example, by using Lazies), we'll * use the primitive type to determine how to encode the value into the JSON. * * If Tokens resolve to CloudFormation intrinsics, we'll use the type of the encoded * value as a type hint to determine how to encode the value into the JSON. The difference * is that we add quotes (") around strings, and don't add anything around non-strings. * * The following structure: * * { SomeAttr: resource.someAttr } * * Will JSONify to either: * * '{ "SomeAttr": "' ++ { Fn::GetAtt: [Resource, SomeAttr] } ++ '" }' * or '{ "SomeAttr": ' ++ { Fn::GetAtt: [Resource, SomeAttr] } ++ ' }' * * Depending on whether `someAttr` is type-hinted to be a string or not. * * (Where ++ is the CloudFormation string-concat operation (`{ Fn::Join }`). * * ----------------------- * * This work requires 2 features from the `resolve()` function: * * - INTRINSICS TYPE HINTS: intrinsics are represented by values like * `{ Ref: 'XYZ' }`. These values can reference either a string or a list/number at * deploy time, and from the value alone there's no way to know which. We need * to know the type to know whether to JSONify this reference to: * * '{ "referencedValue": "' ++ { Ref: XYZ } ++ '"}' * or '{ "referencedValue": ' ++ { Ref: XYZ } ++ '}' * * I.e., whether or not we need to enclose the reference in quotes or not. * * We COULD have done this by resolving one token at a time, and looking at the * type of the encoded token we were resolving to obtain a type hint. However, * the `resolve()` and Token system resist a level-at-a-time resolve * operation: because of the existence of post-processors, we must have done a * complete recursive resolution of a token before we can look at its result * (after which any type information about the sources of nested resolved * values is lost). * * To fix this, "type hints" have been added to the `resolve()` function, * giving an idea of the type of the source value for compplex result values. * This only works for objects (not strings and numbers) but fortunately * we only care about the types of intrinsics, which are always complex values. * * Type hinting could have been added to the `IResolvable` protocol as well, * but for now we just use the type of an encoded value as a type hint. That way * we don't need to annotate anything more at the L1 level--we will use the type * encodings added by construct authors at the L2 levels. L1 users can escape the * default decision of "string" by using `Token.asList()`. * * - COMPLEX KEYS: since tokens can be string-encoded, we can use string-encoded tokens * as the keys in JavaScript objects. However, after resolution, those string-encoded * tokens could resolve to intrinsics (`{ Ref: ... }`), which CANNOT be stored in * JavaScript objects anymore. * * We therefore need a protocol to store the resolved values somewhere in the JavaScript * type model, which can be returned by `resolve()`, and interpreted by `tokenAwareStringify()` * to produce the correct JSON. * * And example will quickly show the point: * * User writes: * { [resource.resourceName]: 'SomeValue' } * ------ string actually looks like ------> * { '${Token[1234]}': 'SomeValue' } * ------ resolve -------> * { '$IntrinsicKey$0': [ {Ref: Resource}, 'SomeValue' ] } * ------ tokenAwareStringify -------> * '{ "' ++ { Ref: Resource } ++ '": "SomeValue" }' */ function tokenAwareStringify(root, space, ctx) { let indent = 0; const ret = new Array(); // First completely resolve the tree, then encode to JSON while respecting the type // hints we got for the resolved intrinsics. recurse(ctx.resolve(root, { allowIntrinsicKeys: true })); switch (ret.length) { case 0: return undefined; case 1: return renderSegment(ret[0]); default: return fnJoinConcat(ret.map(renderSegment)); } /** * Stringify a JSON element */ function recurse(obj) { if (obj === undefined) { return; } if (token_1.Token.isUnresolved(obj)) { throw new Error('This shouldnt happen anymore'); } if (Array.isArray(obj)) { return renderCollection('[', ']', obj, recurse); } if (typeof obj === 'object' && obj != null && !(obj instanceof Date)) { // Treat as an intrinsic if this LOOKS like a CFN intrinsic (`{ Ref: ... }`) // AND it's the result of a token resolution. Otherwise, we just treat this // value as a regular old JSON object (that happens to look a lot like an intrinsic). if (isIntrinsic(obj) && resolve_1.resolvedTypeHint(obj)) { renderIntrinsic(obj); return; } return renderCollection('{', '}', definedEntries(obj), ([key, value]) => { if (key.startsWith(resolve_1.INTRINSIC_KEY_PREFIX)) { [key, value] = value; } recurse(key); pushLiteral(prettyPunctuation(':')); recurse(value); }); } // Otherwise we have a scalar, defer to JSON.stringify()s serialization pushLiteral(JSON.stringify(obj)); } /** * Render an object or list */ function renderCollection(pre, post, xs, each) { pushLiteral(pre); indent += space; let atLeastOne = false; for (const [comma, item] of sepIter(xs)) { if (comma) { pushLiteral(','); } pushLineBreak(); each(item); atLeastOne = true; } indent -= space; if (atLeastOne) { pushLineBreak(); } pushLiteral(post); } function renderIntrinsic(intrinsic) { switch (resolve_1.resolvedTypeHint(intrinsic)) { case resolve_1.ResolutionTypeHint.STRING: pushLiteral('"'); pushIntrinsic(deepQuoteStringLiterals(intrinsic)); pushLiteral('"'); return; case resolve_1.ResolutionTypeHint.LIST: // We need this to look like: // // '{"listValue":' ++ STRINGIFY(CFN_EVAL({ Ref: MyList })) ++ '}' // // However, STRINGIFY would need to execute at CloudFormation deployment time, and that doesn't exist. // // We could *ALMOST* use: // // '{"listValue":["' ++ JOIN('","', { Ref: MyList }) ++ '"]}' // // But that has the unfortunate side effect that if `CFN_EVAL({ Ref: MyList }) == []`, then it would // evaluate to `[""]`, which is a different value. Since CloudFormation does not have arbitrary // conditionals there's no way to deal with this case properly. // // Therefore, if we encounter lists we need to defer to a custom resource to handle // them properly at deploy time. const stack = stack_1.Stack.of(ctx.scope); // Because this will be called twice (once during `prepare`, once during `resolve`), // we need to make sure to be idempotent, so use a cache. const stringifyResponse = stringifyCache.obtain(stack, JSON.stringify(intrinsic), () => cfn_utils_provider_1.CfnUtils.stringify(stack, `CdkJsonStringify${stringifyCounter++}`, intrinsic)); pushIntrinsic(stringifyResponse); return; case resolve_1.ResolutionTypeHint.NUMBER: pushIntrinsic(intrinsic); return; } throw new Error(`Unexpected type hint: ${resolve_1.resolvedTypeHint(intrinsic)}`); } /** * Push a literal onto the current segment if it's also a literal, otherwise open a new Segment */ function pushLiteral(lit) { let last = ret[ret.length - 1]; if (last?.type !== 'literal') { last = { type: 'literal', parts: [] }; ret.push(last); } last.parts.push(lit); } /** * Add a new intrinsic segment */ function pushIntrinsic(intrinsic) { ret.push({ type: 'intrinsic', intrinsic }); } /** * Push a line break if we are pretty-printing, otherwise don't */ function pushLineBreak() { if (space > 0) { pushLiteral(`\n${' '.repeat(indent)}`); } } /** * Add a space after the punctuation if we are pretty-printing, no space if not */ function prettyPunctuation(punc) { return space > 0 ? `${punc} ` : punc; } } /** * Render a segment */ function renderSegment(s) { switch (s.type) { case 'literal': return s.parts.join(''); case 'intrinsic': return s.intrinsic; } } const CLOUDFORMATION_CONCAT = { join(left, right) { return CloudFormationLang.concat(left, right); }, }; /** * Default Token resolver for CloudFormation templates */ exports.CLOUDFORMATION_TOKEN_RESOLVER = new resolvable_1.DefaultTokenResolver(CLOUDFORMATION_CONCAT); /** * Do an intelligent CloudFormation join on the given values, producing a minimal expression */ function minimalCloudFormationJoin(delimiter, values) { let i = 0; while (i < values.length) { const el = values[i]; if (isSplicableFnJoinIntrinsic(el)) { values.splice(i, 1, ...el['Fn::Join'][1]); } else if (i > 0 && isConcatable(values[i - 1]) && isConcatable(values[i])) { values[i - 1] = `${values[i - 1]}${delimiter}${values[i]}`; values.splice(i, 1); } else { i += 1; } } return values; function isSplicableFnJoinIntrinsic(obj) { if (!isIntrinsic(obj)) { return false; } if (Object.keys(obj)[0] !== 'Fn::Join') { return false; } const [delim, list] = obj['Fn::Join']; if (delim !== delimiter) { return false; } if (token_1.Token.isUnresolved(list)) { return false; } if (!Array.isArray(list)) { return false; } return true; } } exports.minimalCloudFormationJoin = minimalCloudFormationJoin; function isConcatable(obj) { return ['string', 'number'].includes(typeof obj) && !token_1.Token.isUnresolved(obj); } /** * Return whether the given value represents a CloudFormation intrinsic */ function isIntrinsic(x) { if (Array.isArray(x) || x === null || typeof x !== 'object') { return false; } const keys = Object.keys(x); if (keys.length !== 1) { return false; } return keys[0] === 'Ref' || isNameOfCloudFormationIntrinsic(keys[0]); } function isNameOfCloudFormationIntrinsic(name) { if (!name.startsWith('Fn::')) { return false; } // these are 'fake' intrinsics, only usable inside the parameter overrides of a CFN CodePipeline Action return name !== 'Fn::GetArtifactAtt' && name !== 'Fn::GetParam'; } exports.isNameOfCloudFormationIntrinsic = isNameOfCloudFormationIntrinsic; /** * Separated iterator */ function* sepIter(xs) { let comma = false; for (const item of xs) { yield [comma, item]; comma = true; } } /** * Object.entries() but skipping undefined values */ function* definedEntries(xs) { for (const [key, value] of Object.entries(xs)) { if (value !== undefined) { yield [key, value]; } } } /** * Quote string literals inside an intrinsic * * Formally, this should only match string literals that will be interpreted as * string literals. Fortunately, the strings that should NOT be quoted are * Logical IDs and attribute names, which cannot contain quotes anyway. Hence, * we can get away not caring about the distinction and just quoting everything. */ function deepQuoteStringLiterals(x) { if (Array.isArray(x)) { return x.map(deepQuoteStringLiterals); } if (typeof x === 'object' && x != null) { const ret = {}; for (const [key, value] of Object.entries(x)) { ret[deepQuoteStringLiterals(key)] = deepQuoteStringLiterals(value); } return ret; } if (typeof x === 'string') { return quoteString(x); } return x; } /** * Quote the characters inside a string, for use inside toJSON */ function quoteString(s) { s = JSON.stringify(s); return s.substring(1, s.length - 1); } let stringifyCounter = 1; /** * A cache scoped to object instances, that's maintained externally to the object instances */ class ScopedCache { constructor() { this.cache = new WeakMap(); } obtain(object, key, init) { let kvMap = this.cache.get(object); if (!kvMap) { kvMap = new Map(); this.cache.set(object, kvMap); } let ret = kvMap.get(key); if (ret === undefined) { ret = init(); kvMap.set(key, ret); } return ret; } } const stringifyCache = new ScopedCache(); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xvdWRmb3JtYXRpb24tbGFuZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImNsb3VkZm9ybWF0aW9uLWxhbmcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsa0NBQStCO0FBQy9CLDhDQUE2RjtBQUM3RixvQ0FBaUM7QUFDakMsb0NBQWlDO0FBQ2pDLDZEQUFnRDtBQUNoRCx1Q0FBdUY7QUFFdkY7O0dBRUc7QUFDSCxNQUFhLGtCQUFrQjtJQUM3Qjs7Ozs7Ozs7Ozs7O09BWUc7SUFDSSxNQUFNLENBQUMsTUFBTSxDQUFDLEdBQVEsRUFBRSxLQUFjO1FBQzNDLE9BQU8sV0FBSSxDQUFDLGNBQWMsQ0FBQztZQUN6Qiw2RUFBNkU7WUFDN0Usd0VBQXdFO1lBQ3hFLHVEQUF1RDtZQUN2RCxPQUFPLEVBQUUsQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLG1CQUFtQixDQUFDLEdBQUcsRUFBRSxLQUFLLElBQUksQ0FBQyxFQUFFLEdBQUcsQ0FBQztTQUM1RCxDQUFDLENBQUM7S0FDSjtJQUVEOztPQUVHO0lBQ0ksTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFxQixFQUFFLEtBQXNCO1FBQ2hFLElBQUksSUFBSSxLQUFLLFNBQVMsSUFBSSxLQUFLLEtBQUssU0FBUyxFQUFFO1lBQUUsT0FBTyxFQUFFLENBQUM7U0FBRTtRQUU3RCxNQUFNLEtBQUssR0FBRyxJQUFJLEtBQUssRUFBTyxDQUFDO1FBQy9CLElBQUksSUFBSSxLQUFLLFNBQVMsRUFBRTtZQUFFLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7U0FBRTtRQUM3QyxJQUFJLEtBQUssS0FBSyxTQUFTLEVBQUU7WUFBRSxLQUFLLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO1NBQUU7UUFFL0Msb0RBQW9EO1FBQ3BELElBQUksS0FBSyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUU7WUFBRSxPQUFPLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztTQUFFO1FBQzVDLElB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dFRva2VuUmVzb2x2ZXIsIElGcmFnbWVudENvbmNhdGVuYXRvciwgSVJlc29sdmVDb250ZXh0IH0gZnJvbSAnLi4vcmVzb2x2YWJsZSc7XG5pbXBvcnQgeyBTdGFjayB9IGZyb20gJy4uL3N0YWNrJztcbmltcG9ydCB7IFRva2VuIH0gZnJvbSAnLi4vdG9rZW4nO1xuaW1wb3J0IHsgQ2ZuVXRpbHMgfSBmcm9tICcuL2Nmbi11dGlscy1wcm92aWRlcic7XG5pbXBvcnQgeyBJTlRSSU5TSUNfS0VZX1BSRUZJWCwgUmVzb2x1dGlvblR5cGVIaW50LCByZXNvbHZlZFR5cGVIaW50IH0gZnJvbSAnLi9yZXNvbHZlJztcblxuLyoqXG4gKiBSb3V0aW5lcyB0aGF0IGtub3cgaG93IHRvIGRvIG9wZXJhdGlvbnMgYXQgdGhlIENsb3VkRm9ybWF0aW9uIGRvY3VtZW50IGxhbmd1YWdlIGxldmVsXG4gKi9cbmV4cG9ydCBjbGFzcyBDbG91ZEZvcm1hdGlvbkxhbmcge1xuICAvKipcbiAgICogVHVybiBhbiBhcmJpdHJhcnkgc3RydWN0dXJlIHBvdGVudGlhbGx5IGNvbnRhaW5pbmcgVG9rZW5zIGludG8gYSBKU09OIHN0cmluZy5cbiAgICpcbiAgICogUmV0dXJucyBhIFRva2VuIHdoaWNoIHdpbGwgZXZhbHVhdGUgdG8gQ2xvdWRGb3JtYXRpb24gZXhwcmVzc2lvbiB0aGF0XG4gICAqIHdpbGwgYmUgZXZhbHVhdGVkIGJ5IENsb3VkRm9ybWF0aW9uIHRvIHRoZSBKU09OIHJlcHJlc2VudGF0aW9uIG9mIHRoZVxuICAgKiBpbnB1dCBzdHJ1Y3R1cmUuXG4gICAqXG4gICAqIEFsbCBUb2tlbnMgc3Vic3RpdHV0ZWQgaW4gdGhpcyB3YXkgbXVzdCByZXR1cm4gc3RyaW5ncywgb3IgdGhlIGV2YWx1YXRpb25cbiAgICogaW4gQ2xvdWRGb3JtYXRpb24gd2lsbCBmYWlsLlxuICAgKlxuICAgKiBAcGFyYW0gb2JqIFRoZSBvYmplY3QgdG8gc3RyaW5naWZ5XG4gICAqIEBwYXJhbSBzcGFjZSBJbmRlbnRhdGlvbiB0byB1c2UgKGRlZmF1bHQ6IG5vIHByZXR0eS1wcmludGluZylcbiAgICovXG4gIHB1YmxpYyBzdGF0aWMgdG9KU09OKG9iajogYW55LCBzcGFjZT86IG51bWJlcik6IHN0cmluZyB7XG4gICAgcmV0dXJuIExhenkudW5jYWNoZWRTdHJpbmcoe1xuICAgICAgLy8gV2UgdXNlZCB0byBkbyB0aGlzIGJ5IGhvb2tpbmcgaW50byBgSlNPTi5zdHJpbmdpZnkoKWAgYnkgYWRkaW5nIGluIG9iamVjdHNcbiAgICAgIC8vIHdpdGggY3VzdG9tIGB0b0pTT04oKWAgZnVuY3Rpb25zLCBidXQgaXQncyB1bHRpbWF0ZWx5IHNpbXBsZXIganVzdCB0b1xuICAgICAgLy8gcmVpbXBsZW1lbnQgdGhlIGBzdHJpbmdpZnkoKWAgZnVuY3Rpb24gZnJvbSBzY3JhdGNoLlxuICAgICAgcHJvZHVjZTogKGN0eCkgPT4gdG9rZW5Bd2FyZVN0cmluZ2lmeShvYmosIHNwYWNlID8/IDAsIGN0eCksXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogUHJvZHVjZSBhIENsb3VkRm9ybWF0aW9uIGV4cHJlc3Npb24gdG8gY29uY2F0IHR3byBhcmJpdHJhcnkgZXhwcmVzc2lvbnMgd2hlbiByZXNvbHZpbmdcbiAgICovXG4gIHB1YmxpYyBzdGF0aWMgY29uY2F0KGxlZnQ6IGFueSB8IHVuZGVmaW5lZCwgcmlnaHQ6IGFueSB8IHVuZGVmaW5lZCk6IGFueSB7XG4gICAgaWYgKGxlZnQgPT09IHVuZGVmaW5lZCAmJiByaWdodCA9PT0gdW5kZWZpbmVkKSB7IHJldHVybiAnJzsgfVxuXG4gICAgY29uc3QgcGFydHMgPSBuZXcgQXJyYXk8YW55PigpO1xuICAgIGlmIChsZWZ0ICE9PSB1bmRlZmluZWQpIHsgcGFydHMucHVzaChsZWZ0KTsgfVxuICAgIGlmIChyaWdodCAhPT0gdW5kZWZpbmVkKSB7IHBhcnRzLnB1c2gocmlnaHQpOyB9XG5cbiAgICAvLyBTb21lIGNhc2UgYW5hbHlzaXMgdG8gcHJvZHVjZSBtaW5pbWFsIGV4cHJlc3Npb25zXG4gICAgaWYgKHBhcnRzLmxlbmd0aCA9PT0gMSkgeyByZXR1cm4gcGFydHNbMF07IH1cbiAgICBpZiAocGFydHMubGVuZ3RoID09PSAyICYmIGlzQ29uY2F0YWJsZShwYXJ0c1swXSkgJiYgaXNDb25jYXRhYmxlKHBhcnRzWzFdKSkge1xuICAgICAgcmV0dXJuIGAke3BhcnRzWzBdfSR7cGFydHNbMV19YDtcbiAgICB9XG5cbiAgICAvLyBPdGhlcndpc2UgcmV0dXJuIGEgSm9pbiBpbnRyaW5zaWMgKGFscmVhZHkgaW4gdGhlIHRhcmdldCBkb2N1bWVudCBsYW5ndWFnZSB0byBhdm9pZCB0YWtpbmdcbiAgICAvLyBjaXJjdWxhciBkZXBlbmRlbmNpZXMgb24gRm5Kb2luICYgZnJpZW5kcylcbiAgICByZXR1cm4gZm5Kb2luQ29uY2F0KHBhcnRzKTtcbiAgfVxufVxuXG4vKipcbiAqIFJldHVybiBhIENGTiBpbnRyaW5zaWMgbWFzcyBjb25jYXR0aW5nIGFueSBudW1iZXIgb2YgQ2xvdWRGb3JtYXRpb24gZXhwcmVzc2lvbnNcbiAqL1xuZnVuY3Rpb24gZm5Kb2luQ29uY2F0KHBhcnRzOiBhbnlbXSkge1xuICByZXR1cm4geyAnRm46OkpvaW4nOiBbJycsIG1pbmltYWxDbG91ZEZvcm1hdGlvbkpvaW4oJycsIHBhcnRzKV0gfTtcbn1cblxuLyoqXG4gKiBQZXJmb3JtIGEgSlNPTi5zdHJpbmdpZnkoKS1saWtlIG9wZXJhdGlvbiwgZXhjZXB0IGF3YXJlIG9mIFRva2VucyBhbmQgQ2xvdWRGb3JtYXRpb24gaW50cmluY2ljc1xuICpcbiAqIFRva2VucyB3aWxsIGJlIHJlc29sdmVkIGFuZCBpZiBhbnkgcmVzb2x2ZSB0byBDbG91ZEZvcm1hdGlvbiBpbnRyaW5zaWNzLCB0aGUgaW50cmluc2ljc1xuICogd2lsbCBiZSBsaWZ0ZWQgdG8gdGhlIHRvcCBvZiBhIGdpYW50IGB7IEZuOjpKb2luIH1gIGV4cHJlc3Npb24uXG4gKlxuICogSWYgVG9rZW5zIHJlc29sdmUgdG8gcHJpbWl0aXZlIHR5cGVzIChmb3IgZXhhbXBsZSwgYnkgdXNpbmcgTGF6aWVzKSwgd2UnbGxcbiAqIHVzZSB0aGUgcHJpbWl0aXZlIHR5cGUgdG8gZGV0ZXJtaW5lIGhvdyB0byBlbmNvZGUgdGhlIHZhbHVlIGludG8gdGhlIEpTT04uXG4gKlxuICogSWYgVG9rZW5zIHJlc29sdmUgdG8gQ2xvdWRGb3JtYXRpb24gaW50cmluc2ljcywgd2UnbGwgdXNlIHRoZSB0eXBlIG9mIHRoZSBlbmNvZGVkXG4gKiB2YWx1ZSBhcyBhIHR5cGUgaGludCB0byBkZXRlcm1pbmUgaG93IHRvIGVuY29kZSB0aGUgdmFsdWUgaW50byB0aGUgSlNPTi4gVGhlIGRpZmZlcmVuY2VcbiAqIGlzIHRoYXQgd2UgYWRkIHF1b3RlcyAoXCIpIGFyb3VuZCBzdHJpbmdzLCBhbmQgZG9uJ3QgYWRkIGFueXRoaW5nIGFyb3VuZCBub24tc3RyaW5ncy5cbiAqXG4gKiBUaGUgZm9sbG93aW5nIHN0cnVjdHVyZTpcbiAqXG4gKiAgICB7IFNvbWVBdHRyOiByZXNvdXJjZS5zb21lQXR0ciB9XG4gKlxuICogV2lsbCBKU09OaWZ5IHRvIGVpdGhlcjpcbiAqXG4gKiAgICAneyBcIlNvbWVBdHRyXCI6IFwiJyArKyB7IEZuOjpHZXRBdHQ6IFtSZXNvdXJjZSwgU29tZUF0dHJdIH0gKysgJ1wiIH0nXG4gKiBvciAneyBcIlNvbWVBdHRyXCI6ICcgKysgeyBGbjo6R2V0QXR0OiBbUmVzb3VyY2UsIFNvbWVBdHRyXSB9ICsrICcgfSdcbiAqXG4gKiBEZXBlbmRpbmcgb24gd2hldGhlciBgc29tZUF0dHJgIGlzIHR5cGUtaGludGVkIHRvIGJlIGEgc3RyaW5nIG9yIG5vdC5cbiAqXG4gKiAoV2hlcmUgKysgaXMgdGhlIENsb3VkRm9ybWF0aW9uIHN0cmluZy1jb25jYXQgb3BlcmF0aW9uIChgeyBGbjo6Sm9pbiB9YCkuXG4gKlxuICogLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqXG4gKiBUaGlzIHdvcmsgcmVxdWlyZXMgMiBmZWF0dXJlcyBmcm9tIHRoZSBgcmVzb2x2ZSgpYCBmdW5jdGlvbjpcbiAqXG4gKiAtIElOVFJJTlNJQ1MgVFlQRSBISU5UUzogaW50cmluc2ljcyBhcmUgcmVwcmVzZW50ZWQgYnkgdmFsdWVzIGxpa2VcbiAqICAgYHsgUmVmOiAnWFlaJyB9YC4gVGhlc2UgdmFsdWVzIGNhbiByZWZlcmVuY2UgZWl0aGVyIGEgc3RyaW5nIG9yIGEgbGlzdC9udW1iZXIgYXRcbiAqICAgZGVwbG95IHRpbWUsIGFuZCBmcm9tIHRoZSB2YWx1ZSBhbG9uZSB0aGVyZSdzIG5vIHdheSB0byBrbm93IHdoaWNoLiBXZSBuZWVkXG4gKiAgIHRvIGtub3cgdGhlIHR5cGUgdG8ga25vdyB3aGV0aGVyIHRvIEpTT05pZnkgdGhpcyByZWZlcmVuY2UgdG86XG4gKlxuICogICAgICAneyBcInJlZmVyZW5jZWRWYWx1ZVwiOiBcIicgKysgeyBSZWY6IFhZWiB9ICsrICdcIn0nXG4gKiAgIG9yICd7IFwicmVmZXJlbmNlZFZhbHVlXCI6ICcgKysgeyBSZWY6IFhZWiB9ICsrICd9J1xuICpcbiAqICAgSS5lLiwgd2hldGhlciBvciBub3Qgd2UgbmVlZCB0byBlbmNsb3NlIHRoZSByZWZlcmVuY2UgaW4gcXVvdGVzIG9yIG5vdC5cbiAqXG4gKiAgIFdlIENPVUxEIGhhdmUgZG9uZSB0aGlzIGJ5IHJlc29sdmluZyBvbmUgdG9rZW4gYXQgYSB0aW1lLCBhbmQgbG9va2luZyBhdCB0aGVcbiAqICAgdHlwZSBvZiB0aGUgZW5jb2RlZCB0b2tlbiB3ZSB3ZXJlIHJlc29sdmluZyB0byBvYnRhaW4gYSB0eXBlIGhpbnQuIEhvd2V2ZXIsXG4gKiAgIHRoZSBgcmVzb2x2ZSgpYCBhbmQgVG9rZW4gc3lzdGVtIHJlc2lzdCBhIGxldmVsLWF0LWEtdGltZSByZXNvbHZlXG4gKiAgIG9wZXJhdGlvbjogYmVjYXVzZSBvZiB0aGUgZXhpc3RlbmNlIG9mIHBvc3QtcHJvY2Vzc29ycywgd2UgbXVzdCBoYXZlIGRvbmUgYVxuICogICBjb21wbGV0ZSByZWN1cnNpdmUgcmVzb2x1dGlvbiBvZiBhIHRva2VuIGJlZm9yZSB3ZSBjYW4gbG9vayBhdCBpdHMgcmVzdWx0XG4gKiAgIChhZnRlciB3aGljaCBhbnkgdHlwZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc291cmNlcyBvZiBuZXN0ZWQgcmVzb2x2ZWRcbiAqICAgdmFsdWVzIGlzIGxvc3QpLlxuICpcbiAqICAgVG8gZml4IHRoaXMsIFwidHlwZSBoaW50c1wiIGhhdmUgYmVlbiBhZGRlZCB0byB0aGUgYHJlc29sdmUoKWAgZnVuY3Rpb24sXG4gKiAgIGdpdmluZyBhbiBpZGVhIG9mIHRoZSB0eXBlIG9mIHRoZSBzb3VyY2UgdmFsdWUgZm9yIGNvbXBwbGV4IHJlc3VsdCB2YWx1ZXMuXG4gKiAgIFRoaXMgb25seSB3b3JrcyBmb3Igb2JqZWN0cyAobm90IHN0cmluZ3MgYW5kIG51bWJlcnMpIGJ1dCBmb3J0dW5hdGVseVxuICogICB3ZSBvbmx5IGNhcmUgYWJvdXQgdGhlIHR5cGVzIG9mIGludHJpbnNpY3MsIHdoaWNoIGFyZSBhbHdheXMgY29tcGxleCB2YWx1ZXMuXG4gKlxuICogICBUeXBlIGhpbnRpbmcgY291bGQgaGF2ZSBiZWVuIGFkZGVkIHRvIHRoZSBgSVJlc29sdmFibGVgIHByb3RvY29sIGFzIHdlbGwsXG4gKiAgIGJ1dCBmb3Igbm93IHdlIGp1c3QgdXNlIHRoZSB0eXBlIG9mIGFuIGVuY29kZWQgdmFsdWUgYXMgYSB0eXBlIGhpbnQuIFRoYXQgd2F5XG4gKiAgIHdlIGRvbid0IG5lZWQgdG8gYW5ub3RhdGUgYW55dGhpbmcgbW9yZSBhdCB0aGUgTDEgbGV2ZWwtLXdlIHdpbGwgdXNlIHRoZSB0eXBlXG4gKiAgIGVuY29kaW5ncyBhZGRlZCBieSBjb25zdHJ1Y3QgYXV0aG9ycyBhdCB0aGUgTDIgbGV2ZWxzLiBMMSB1c2VycyBjYW4gZXNjYXBlIHRoZVxuICogICBkZWZhdWx0IGRlY2lzaW9uIG9mIFwic3RyaW5nXCIgYnkgdXNpbmcgYFRva2VuLmFzTGlzdCgpYC5cbiAqXG4gKiAtIENPTVBMRVggS0VZUzogc2luY2UgdG9rZW5zIGNhbiBiZSBzdHJpbmctZW5jb2RlZCwgd2UgY2FuIHVzZSBzdHJpbmctZW5jb2RlZCB0b2tlbnNcbiAqICAgYXMgdGhlIGtleXMgaW4gSmF2YVNjcmlwdCBvYmplY3RzLiBIb3dldmVyLCBhZnRlciByZXNvbHV0aW9uLCB0aG9zZSBzdHJpbmctZW5jb2RlZFxuICogICB0b2tlbnMgY291bGQgcmVzb2x2ZSB0byBpbnRyaW5zaWNzIChgeyBSZWY6IC4uLiB9YCksIHdoaWNoIENBTk5PVCBiZSBzdG9yZWQgaW5cbiAqICAgSmF2YVNjcmlwdCBvYmplY3RzIGFueW1vcmUuXG4gKlxuICogICBXZSB0aGVyZWZvcmUgbmVlZCBhIHByb3RvY29sIHRvIHN0b3JlIHRoZSByZXNvbHZlZCB2YWx1ZXMgc29tZXdoZXJlIGluIHRoZSBKYXZhU2NyaXB0XG4gKiAgIHR5cGUgbW9kZWwsICB3aGljaCBjYW4gYmUgcmV0dXJuZWQgYnkgYHJlc29sdmUoKWAsIGFuZCBpbnRlcnByZXRlZCBieSBgdG9rZW5Bd2FyZVN0cmluZ2lmeSgpYFxuICogICB0byBwcm9kdWNlIHRoZSBjb3JyZWN0IEpTT04uXG4gKlxuICogICBBbmQgZXhhbXBsZSB3aWxsIHF1aWNrbHkgc2hvdyB0aGUgcG9pbnQ6XG4gKlxuICogICAgVXNlciB3cml0ZXM6XG4gKiAgICAgICB7IFtyZXNvdXJjZS5yZXNvdXJjZU5hbWVdOiAnU29tZVZhbHVlJyB9XG4gKiAgICAtLS0tLS0gc3RyaW5nIGFjdHVhbGx5IGxvb2tzIGxpa2UgLS0tLS0tPlxuICogICAgICAgeyAnJHtUb2tlblsxMjM0XX0nOiAnU29tZVZhbHVlJyB9XG4gKiAgICAtLS0tLS0gcmVzb2x2ZSAtLS0tLS0tPlxuICogICAgICAgeyAnJEludHJpbnNpY0tleSQwJzogWyB7UmVmOiBSZXNvdXJjZX0sICdTb21lVmFsdWUnIF0gfVxuICogICAgLS0tLS0tIHRva2VuQXdhcmVTdHJpbmdpZnkgLS0tLS0tLT5cbiAqICAgICAgICd7IFwiJyArKyB7IFJlZjogUmVzb3VyY2UgfSArKyAnXCI6IFwiU29tZVZhbHVlXCIgfSdcbiAqL1xuZnVuY3Rpb24gdG9rZW5Bd2FyZVN0cmluZ2lmeShyb290OiBhbnksIHNwYWNlOiBudW1iZXIsIGN0eDogSVJlc29sdmVDb250ZXh0KSB7XG4gIGxldCBpbmRlbnQgPSAwO1xuXG4gIGNvbnN0IHJldCA9IG5ldyBBcnJheTxTZWdtZW50PigpO1xuXG4gIC8vIEZpcnN0IGNvbXBsZXRlbHkgcmVzb2x2ZSB0aGUgdHJlZSwgdGhlbiBlbmNvZGUgdG8gSlNPTiB3aGlsZSByZXNwZWN0aW5nIHRoZSB0eXBlXG4gIC8vIGhpbnRzIHdlIGdvdCBmb3IgdGhlIHJlc29sdmVkIGludHJpbnNpY3MuXG4gIHJlY3Vyc2UoY3R4LnJlc29sdmUocm9vdCwgeyBhbGxvd0ludHJpbnNpY0tleXM6IHRydWUgfSkpO1xuXG4gIHN3aXRjaCAocmV0Lmxlbmd0aCkge1xuICAgIGNhc2UgMDogcmV0dXJuIHVuZGVmaW5lZDtcbiAgICBjYXNlIDE6IHJldHVybiByZW5kZXJTZWdtZW50KHJldFswXSk7XG4gICAgZGVmYXVsdDpcbiAgICAgIHJldHVybiBmbkpvaW5Db25jYXQocmV0Lm1hcChyZW5kZXJTZWdtZW50KSk7XG4gIH1cblxuICAvKipcbiAgICogU3RyaW5naWZ5IGEgSlNPTiBlbGVtZW50XG4gICAqL1xuICBmdW5jdGlvbiByZWN1cnNlKG9iajogYW55KTogdm9pZCB7XG4gICAgaWYgKG9iaiA9PT0gdW5kZWZpbmVkKSB7IHJldHVybjsgfVxuXG4gICAgaWYgKFRva2VuLmlzVW5yZXNvbHZlZChvYmopKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1RoaXMgc2hvdWxkbnQgaGFwcGVuIGFueW1vcmUnKTtcbiAgICB9XG4gICAgaWYgKEFycmF5LmlzQXJyYXkob2JqKSkge1xuICAgICAgcmV0dXJuIHJlbmRlckNvbGxlY3Rpb24oJ1snLCAnXScsIG9iaiwgcmVjdXJzZSk7XG4gICAgfVxuICAgIGlmICh0eXBlb2Ygb2JqID09PSAnb2JqZWN0JyAmJiBvYmogIT0gbnVsbCAmJiAhKG9iaiBpbnN0YW5jZW9mIERhdGUpKSB7XG4gICAgICAvLyBUcmVhdCBhcyBhbiBpbnRyaW5zaWMgaWYgdGhpcyBMT09LUyBsaWtlIGEgQ0ZOIGludHJpbnNpYyAoYHsgUmVmOiAuLi4gfWApXG4gICAgICAvLyBBTkQgaXQncyB0aGUgcmVzdWx0IG9mIGEgdG9rZW4gcmVzb2x1dGlvbi4gT3RoZXJ3aXNlLCB3ZSBqdXN0IHRyZWF0IHRoaXNcbiAgICAgIC8vIHZhbHVlIGFzIGEgcmVndWxhciBvbGQgSlNPTiBvYmplY3QgKHRoYXQgaGFwcGVucyB0byBsb29rIGEgbG90IGxpa2UgYW4gaW50cmluc2ljKS5cbiAgICAgIGlmIChpc0ludHJpbnNpYyhvYmopICYmIHJlc29sdmVkVHlwZUhpbnQob2JqKSkge1xuICAgICAgICByZW5kZXJJbnRyaW5zaWMob2JqKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gcmVuZGVyQ29sbGVjdGlvbigneycsICd9JywgZGVmaW5lZEVudHJpZXMob2JqKSwgKFtrZXksIHZhbHVlXSkgPT4ge1xuICAgICAgICBpZiAoa2V5LnN0YXJ0c1dpdGgoSU5UUklOU0lDX0tFWV9QUkVGSVgpKSB7XG4gICAgICAgICAgW2tleSwgdmFsdWVdID0gdmFsdWU7XG4gICAgICAgIH1cblxuICAgICAgICByZWN1cnNlKGtleSk7XG4gICAgICAgIHB1c2hMaXRlcmFsKHByZXR0eVB1bmN0dWF0aW9uKCc6JykpO1xuICAgICAgICByZWN1cnNlKHZhbHVlKTtcbiAgICAgIH0pO1xuICAgIH1cbiAgICAvLyBPdGhlcndpc2Ugd2UgaGF2ZSBhIHNjYWxhciwgZGVmZXIgdG8gSlNPTi5zdHJpbmdpZnkoKXMgc2VyaWFsaXphdGlvblxuICAgIHB1c2hMaXRlcmFsKEpTT04uc3RyaW5naWZ5KG9iaikpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbmRlciBhbiBvYmplY3Qgb3IgbGlzdFxuICAgKi9cbiAgZnVuY3Rpb24gcmVuZGVyQ29sbGVjdGlvbjxBPihwcmU6IHN0cmluZywgcG9zdDogc3RyaW5nLCB4czogSXRlcmFibGU8QT4sIGVhY2g6ICh4OiBBKSA9PiB2b2lkKSB7XG4gICAgcHVzaExpdGVyYWwocHJlKTtcbiAgICBpbmRlbnQgKz0gc3BhY2U7XG4gICAgbGV0IGF0TGVhc3RPbmUgPSBmYWxzZTtcbiAgICBmb3IgKGNvbnN0IFtjb21tYSwgaXRlbV0gb2Ygc2VwSXRlcih4cykpIHtcbiAgICAgIGlmIChjb21tYSkgeyBwdXNoTGl0ZXJhbCgnLCcpOyB9XG4gICAgICBwdXNoTGluZUJyZWFrKCk7XG4gICAgICBlYWNoKGl0ZW0pO1xuICAgICAgYXRMZWFzdE9uZSA9IHRydWU7XG4gICAgfVxuICAgIGluZGVudCAtPSBzcGFjZTtcbiAgICBpZiAoYXRMZWFzdE9uZSkgeyBwdXNoTGluZUJyZWFrKCk7IH1cbiAgICBwdXNoTGl0ZXJhbChwb3N0KTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHJlbmRlckludHJpbnNpYyhpbnRyaW5zaWM6IGFueSkge1xuICAgIHN3aXRjaCAocmVzb2x2ZWRUeXBlSGludChpbnRyaW5zaWMpKSB7XG4gICAgICBjYXNlIFJlc29sdXRpb25UeXBlSGludC5TVFJJTkc6XG4gICAgICAgIHB1c2hMaXRlcmFsKCdcIicpO1xuICAgICAgICBwdXNoSW50cmluc2ljKGRlZXBRdW90ZVN0cmluZ0xpdGVyYWxzKGludHJpbnNpYykpO1xuICAgICAgICBwdXNoTGl0ZXJhbCgnXCInKTtcbiAgICAgICAgcmV0dXJuO1xuXG4gICAgICBjYXNlIFJlc29sdXRpb25UeXBlSGludC5MSVNUOlxuICAgICAgICAvLyBXZSBuZWVkIHRoaXMgdG8gbG9vayBsaWtlOlxuICAgICAgICAvL1xuICAgICAgICAvLyAgICAne1wibGlzdFZhbHVlXCI6JyArKyBTVFJJTkdJRlkoQ0ZOX0VWQUwoeyBSZWY6IE15TGlzdCB9KSkgKysgJ30nXG4gICAgICAgIC8vXG4gICAgICAgIC8vIEhvd2V2ZXIsIFNUUklOR0lGWSB3b3VsZCBuZWVkIHRvIGV4ZWN1dGUgYXQgQ2xvdWRGb3JtYXRpb24gZGVwbG95bWVudCB0aW1lLCBhbmQgdGhhdCBkb2Vzbid0IGV4aXN0LlxuICAgICAgICAvL1xuICAgICAgICAvLyBXZSBjb3VsZCAqQUxNT1NUKiB1c2U6XG4gICAgICAgIC8vXG4gICAgICAgIC8vICAgJ3tcImxpc3RWYWx1ZVwiOltcIicgKysgSk9JTignXCIsXCInLCB7IFJlZjogTXlMaXN0IH0pICsrICdcIl19J1xuICAgICAgICAvL1xuICAgICAgICAvLyBCdXQgdGhhdCBoYXMgdGhlIHVuZm9ydHVuYXRlIHNpZGUgZWZmZWN0IHRoYXQgaWYgYENGTl9FVkFMKHsgUmVmOiBNeUxpc3QgfSkgPT0gW11gLCB0aGVuIGl0IHdvdWxkXG4gICAgICAgIC8vIGV2YWx1YXRlIHRvIGBbXCJcIl1gLCB3aGljaCBpcyBhIGRpZmZlcmVudCB2YWx1ZS4gU2luY2UgQ2xvdWRGb3JtYXRpb24gZG9lcyBub3QgaGF2ZSBhcmJpdHJhcnlcbiAgICAgICAgLy8gY29uZGl0aW9uYWxzIHRoZXJlJ3Mgbm8gd2F5IHRvIGRlYWwgd2l0aCB0aGlzIGNhc2UgcHJvcGVybHkuXG4gICAgICAgIC8vXG4gICAgICAgIC8vIFRoZXJlZm9yZSwgaWYgd2UgZW5jb3VudGVyIGxpc3RzIHdlIG5lZWQgdG8gZGVmZXIgdG8gYSBjdXN0b20gcmVzb3VyY2UgdG8gaGFuZGxlXG4gICAgICAgIC8vIHRoZW0gcHJvcGVybHkgYXQgZGVwbG95IHRpbWUuXG4gICAgICAgIGNvbnN0IHN0YWNrID0gU3RhY2sub2YoY3R4LnNjb3BlKTtcblxuICAgICAgICAvLyBCZWNhdXNlIHRoaXMgd2lsbCBiZSBjYWxsZWQgdHdpY2UgKG9uY2UgZHVyaW5nIGBwcmVwYXJlYCwgb25jZSBkdXJpbmcgYHJlc29sdmVgKSxcbiAgICAgICAgLy8gd2UgbmVlZCB0byBtYWtlIHN1cmUgdG8gYmUgaWRlbXBvdGVudCwgc28gdXNlIGEgY2FjaGUuXG4gICAgICAgIGNvbnN0IHN0cmluZ2lmeVJlc3BvbnNlID0gc3RyaW5naWZ5Q2FjaGUub2J0YWluKHN0YWNrLCBKU09OLnN0cmluZ2lmeShpbnRyaW5zaWMpLCAoKSA9PlxuICAgICAgICAgIENmblV0aWxzLnN0cmluZ2lmeShzdGFjaywgYENka0pzb25TdHJpbmdpZnkke3N0cmluZ2lmeUNvdW50ZXIrK31gLCBpbnRyaW5zaWMpLFxuICAgICAgICApO1xuXG4gICAgICAgIHB1c2hJbnRyaW5zaWMoc3RyaW5naWZ5UmVzcG9uc2UpO1xuICAgICAgICByZXR1cm47XG5cbiAgICAgIGNhc2UgUmVzb2x1dGlvblR5cGVIaW50Lk5VTUJFUjpcbiAgICAgICAgcHVzaEludHJpbnNpYyhpbnRyaW5zaWMpO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhyb3cgbmV3IEVycm9yKGBVbmV4cGVjdGVkIHR5cGUgaGludDogJHtyZXNvbHZlZFR5cGVIaW50KGludHJpbnNpYyl9YCk7XG4gIH1cblxuICAvKipcbiAgICogUHVzaCBhIGxpdGVyYWwgb250byB0aGUgY3VycmVudCBzZWdtZW50IGlmIGl0J3MgYWxzbyBhIGxpdGVyYWwsIG90aGVyd2lzZSBvcGVuIGEgbmV3IFNlZ21lbnRcbiAgICovXG4gIGZ1bmN0aW9uIHB1c2hMaXRlcmFsKGxpdDogc3RyaW5nKSB7XG4gICAgbGV0IGxhc3QgPSByZXRbcmV0Lmxlbmd0aCAtIDFdO1xuICAgIGlmIChsYXN0Py50eXBlICE9PSAnbGl0ZXJhbCcpIHtcbiAgICAgIGxhc3QgPSB7IHR5cGU6ICdsaXRlcmFsJywgcGFydHM6IFtdIH07XG4gICAgICByZXQucHVzaChsYXN0KTtcbiAgICB9XG4gICAgbGFzdC5wYXJ0cy5wdXNoKGxpdCk7XG4gIH1cblxuICAvKipcbiAgICogQWRkIGEgbmV3IGludHJpbnNpYyBzZWdtZW50XG4gICAqL1xuICBmdW5jdGlvbiBwdXNoSW50cmluc2ljKGludHJpbnNpYzogYW55KSB7XG4gICAgcmV0LnB1c2goeyB0eXBlOiAnaW50cmluc2ljJywgaW50cmluc2ljIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFB1c2ggYSBsaW5lIGJyZWFrIGlmIHdlIGFyZSBwcmV0dHktcHJpbnRpbmcsIG90aGVyd2lzZSBkb24ndFxuICAgKi9cbiAgZnVuY3Rpb24gcHVzaExpbmVCcmVhaygpIHtcbiAgICBpZiAoc3BhY2UgPiAwKSB7XG4gICAgICBwdXNoTGl0ZXJhbChgXFxuJHsnICcucmVwZWF0KGluZGVudCl9YCk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEFkZCBhIHNwYWNlIGFmdGVyIHRoZSBwdW5jdHVhdGlvbiBpZiB3ZSBhcmUgcHJldHR5LXByaW50aW5nLCBubyBzcGFjZSBpZiBub3RcbiAgICovXG4gIGZ1bmN0aW9uIHByZXR0eVB1bmN0dWF0aW9uKHB1bmM6IHN0cmluZykge1xuICAgIHJldHVybiBzcGFjZSA+IDAgPyBgJHtwdW5jfSBgIDogcHVuYztcbiAgfVxufVxuXG4vKipcbiAqIEEgU2VnbWVudCBpcyBlaXRoZXIgYSBsaXRlcmFsIHN0cmluZyBvciBhIENsb3VkRm9ybWF0aW9uIGludHJpbnNpY1xuICovXG50eXBlIFNlZ21lbnQgPSB7IHR5cGU6ICdsaXRlcmFsJzsgcGFydHM6IHN0cmluZ1tdIH0gfCB7IHR5cGU6ICdpbnRyaW5zaWMnOyBpbnRyaW5zaWM6IGFueSB9O1xuXG4vKipcbiAqIFJlbmRlciBhIHNlZ21lbnRcbiAqL1xuZnVuY3Rpb24gcmVuZGVyU2VnbWVudChzOiBTZWdtZW50KTogTm9uTnVsbGFibGU8YW55PiB7XG4gIHN3aXRjaCAocy50eXBlKSB7XG4gICAgY2FzZSAnbGl0ZXJhbCc6IHJldHVybiBzLnBhcnRzLmpvaW4oJycpO1xuICAgIGNhc2UgJ2ludHJpbnNpYyc6IHJldHVybiBzLmludHJpbnNpYztcbiAgfVxufVxuXG5jb25zdCBDTE9VREZPUk1BVElPTl9DT05DQVQ6IElGcmFnbWVudENvbmNhdGVuYXRvciA9IHtcbiAgam9pbihsZWZ0OiBhbnksIHJpZ2h0OiBhbnkpIHtcbiAgICByZXR1cm4gQ2xvdWRGb3JtYXRpb25MYW5nLmNvbmNhdChsZWZ0LCByaWdodCk7XG4gIH0sXG59O1xuXG4vKipcbiAqIERlZmF1bHQgVG9rZW4gcmVzb2x2ZXIgZm9yIENsb3VkRm9ybWF0aW9uIHRlbXBsYXRlc1xuICovXG5leHBvcnQgY29uc3QgQ0xPVURGT1JNQVRJT05fVE9LRU5fUkVTT0xWRVIgPSBuZXcgRGVmYXVsdFRva2VuUmVzb2x2ZXIoQ0xPVURGT1JNQVRJT05fQ09OQ0FUKTtcblxuLyoqXG4gKiBEbyBhbiBpbnRlbGxpZ2VudCBDbG91ZEZvcm1hdGlvbiBqb2luIG9uIHRoZSBnaXZlbiB2YWx1ZXMsIHByb2R1Y2luZyBhIG1pbmltYWwgZXhwcmVzc2lvblxuICovXG5leHBvcnQgZnVuY3Rpb24gbWluaW1hbENsb3VkRm9ybWF0aW9uSm9pbihkZWxpbWl0ZXI6IHN0cmluZywgdmFsdWVzOiBhbnlbXSk6IGFueVtdIHtcbiAgbGV0IGkgPSAwO1xuICB3aGlsZSAoaSA8IHZhbHVlcy5sZW5ndGgpIHtcbiAgICBjb25zdCBlbCA9IHZhbHVlc1tpXTtcbiAgICBpZiAoaXNTcGxpY2FibGVGbkpvaW5JbnRyaW5zaWMoZWwpKSB7XG4gICAgICB2YWx1ZXMuc3BsaWNlKGksIDEsIC4uLmVsWydGbjo6Sm9pbiddWzFdKTtcbiAgICB9IGVsc2UgaWYgKGkgPiAwICYmIGlzQ29uY2F0YWJsZSh2YWx1ZXNbaSAtIDFdKSAmJiBpc0NvbmNhdGFibGUodmFsdWVzW2ldKSkge1xuICAgICAgdmFsdWVzW2kgLSAxXSA9IGAke3ZhbHVlc1tpLTFdfSR7ZGVsaW1pdGVyfSR7dmFsdWVzW2ldfWA7XG4gICAgICB2YWx1ZXMuc3BsaWNlKGksIDEpO1xuICAgIH0gZWxzZSB7XG4gICAgICBpICs9IDE7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHZhbHVlcztcblxuICBmdW5jdGlvbiBpc1NwbGljYWJsZUZuSm9pbkludHJpbnNpYyhvYmo6IGFueSk6IGJvb2xlYW4ge1xuICAgIGlmICghaXNJbnRyaW5zaWMob2JqKSkgeyByZXR1cm4gZmFsc2U7IH1cbiAgICBpZiAoT2JqZWN0LmtleXMob2JqKVswXSAhPT0gJ0ZuOjpKb2luJykgeyByZXR1cm4gZmFsc2U7IH1cblxuICAgIGNvbnN0IFtkZWxpbSwgbGlzdF0gPSBvYmpbJ0ZuOjpKb2luJ107XG4gICAgaWYgKGRlbGltICE9PSBkZWxpbWl0ZXIpIHsgcmV0dXJuIGZhbHNlOyB9XG5cbiAgICBpZiAoVG9rZW4uaXNVbnJlc29sdmVkKGxpc3QpKSB7IHJldHVybiBmYWxzZTsgfVxuICAgIGlmICghQXJyYXkuaXNBcnJheShsaXN0KSkgeyByZXR1cm4gZmFsc2U7IH1cblxuICAgIHJldHVybiB0cnVlO1xuICB9XG59XG5cbmZ1bmN0aW9uIGlzQ29uY2F0YWJsZShvYmo6IGFueSk6IGJvb2xlYW4ge1xuICByZXR1cm4gWydzdHJpbmcnLCAnbnVtYmVyJ10uaW5jbHVkZXModHlwZW9mIG9iaikgJiYgIVRva2VuLmlzVW5yZXNvbHZlZChvYmopO1xufVxuXG5cbi8qKlxuICogUmV0dXJuIHdoZXRoZXIgdGhlIGdpdmVuIHZhbHVlIHJlcHJlc2VudHMgYSBDbG91ZEZvcm1hdGlvbiBpbnRyaW5zaWNcbiAqL1xuZnVuY3Rpb24gaXNJbnRyaW5zaWMoeDogYW55KSB7XG4gIGlmIChBcnJheS5pc0FycmF5KHgpIHx8IHggPT09IG51bGwgfHwgdHlwZW9mIHggIT09ICdvYmplY3QnKSB7IHJldHVybiBmYWxzZTsgfVxuXG4gIGNvbnN0IGtleXMgPSBPYmplY3Qua2V5cyh4KTtcbiAgaWYgKGtleXMubGVuZ3RoICE9PSAxKSB7IHJldHVybiBmYWxzZTsgfVxuXG4gIHJldHVybiBrZXlzWzBdID09PSAnUmVmJyB8fCBpc05hbWVPZkNsb3VkRm9ybWF0aW9uSW50cmluc2ljKGtleXNbMF0pO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gaXNOYW1lT2ZDbG91ZEZvcm1hdGlvbkludHJpbnNpYyhuYW1lOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgaWYgKCFuYW1lLnN0YXJ0c1dpdGgoJ0ZuOjonKSkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuICAvLyB0aGVzZSBhcmUgJ2Zha2UnIGludHJpbnNpY3MsIG9ubHkgdXNhYmxlIGluc2lkZSB0aGUgcGFyYW1ldGVyIG92ZXJyaWRlcyBvZiBhIENGTiBDb2RlUGlwZWxpbmUgQWN0aW9uXG4gIHJldHVybiBuYW1lICE9PSAnRm46OkdldEFydGlmYWN0QXR0JyAmJiBuYW1lICE9PSAnRm46OkdldFBhcmFtJztcbn1cblxuLyoqXG4gKiBTZXBhcmF0ZWQgaXRlcmF0b3JcbiAqL1xuZnVuY3Rpb24qIHNlcEl0ZXI8QT4oeHM6IEl0ZXJhYmxlPEE+KTogSXRlcmFibGVJdGVyYXRvcjxbYm9vbGVhbiwgQV0+IHtcbiAgbGV0IGNvbW1hID0gZmFsc2U7XG4gIGZvciAoY29uc3QgaXRlbSBvZiB4cykge1xuICAgIHlpZWxkIFtjb21tYSwgaXRlbV07XG4gICAgY29tbWEgPSB0cnVlO1xuICB9XG59XG5cbi8qKlxuICogT2JqZWN0LmVudHJpZXMoKSBidXQgc2tpcHBpbmcgdW5kZWZpbmVkIHZhbHVlc1xuICovXG5mdW5jdGlvbiogZGVmaW5lZEVudHJpZXM8QSBleHRlbmRzIG9iamVjdD4oeHM6IEEpOiBJdGVyYWJsZUl0ZXJhdG9yPFtzdHJpbmcsIGFueV0+IHtcbiAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoeHMpKSB7XG4gICAgaWYgKHZhbHVlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIHlpZWxkIFtrZXksIHZhbHVlXTtcbiAgICB9XG4gIH1cbn1cblxuLyoqXG4gKiBRdW90ZSBzdHJpbmcgbGl0ZXJhbHMgaW5zaWRlIGFuIGludHJpbnNpY1xuICpcbiAqIEZvcm1hbGx5LCB0aGlzIHNob3VsZCBvbmx5IG1hdGNoIHN0cmluZyBsaXRlcmFscyB0aGF0IHdpbGwgYmUgaW50ZXJwcmV0ZWQgYXNcbiAqIHN0cmluZyBsaXRlcmFscy4gRm9ydHVuYXRlbHksIHRoZSBzdHJpbmdzIHRoYXQgc2hvdWxkIE5PVCBiZSBxdW90ZWQgYXJlXG4gKiBMb2dpY2FsIElEcyBhbmQgYXR0cmlidXRlIG5hbWVzLCB3aGljaCBjYW5ub3QgY29udGFpbiBxdW90ZXMgYW55d2F5LiBIZW5jZSxcbiAqIHdlIGNhbiBnZXQgYXdheSBub3QgY2FyaW5nIGFib3V0IHRoZSBkaXN0aW5jdGlvbiBhbmQganVzdCBxdW90aW5nIGV2ZXJ5dGhpbmcuXG4gKi9cbmZ1bmN0aW9uIGRlZXBRdW90ZVN0cmluZ0xpdGVyYWxzKHg6IGFueSk6IGFueSB7XG4gIGlmIChBcnJheS5pc0FycmF5KHgpKSB7XG4gICAgcmV0dXJuIHgubWFwKGRlZXBRdW90ZVN0cmluZ0xpdGVyYWxzKTtcbiAgfVxuICBpZiAodHlwZW9mIHggPT09ICdvYmplY3QnICYmIHggIT0gbnVsbCkge1xuICAgIGNvbnN0IHJldDogYW55ID0ge307XG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoeCkpIHtcbiAgICAgIHJldFtkZWVwUXVvdGVTdHJpbmdMaXRlcmFscyhrZXkpXSA9IGRlZXBRdW90ZVN0cmluZ0xpdGVyYWxzKHZhbHVlKTtcbiAgICB9XG4gICAgcmV0dXJuIHJldDtcbiAgfVxuICBpZiAodHlwZW9mIHggPT09ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuIHF1b3RlU3RyaW5nKHgpO1xuICB9XG4gIHJldHVybiB4O1xufVxuXG4vKipcbiAqIFF1b3RlIHRoZSBjaGFyYWN0ZXJzIGluc2lkZSBhIHN0cmluZywgZm9yIHVzZSBpbnNpZGUgdG9KU09OXG4gKi9cbmZ1bmN0aW9uIHF1b3RlU3RyaW5nKHM6IHN0cmluZykge1xuICBzID0gSlNPTi5zdHJpbmdpZnkocyk7XG4gIHJldHVybiBzLnN1YnN0cmluZygxLCBzLmxlbmd0aCAtIDEpO1xufVxuXG5sZXQgc3RyaW5naWZ5Q291bnRlciA9IDE7XG5cbi8qKlxuICogQSBjYWNoZSBzY29wZWQgdG8gb2JqZWN0IGluc3RhbmNlcywgdGhhdCdzIG1haW50YWluZWQgZXh0ZXJuYWxseSB0byB0aGUgb2JqZWN0IGluc3RhbmNlc1xuICovXG5jbGFzcyBTY29wZWRDYWNoZTxPIGV4dGVuZHMgb2JqZWN0LCBLLCBWPiB7XG4gIHByaXZhdGUgY2FjaGUgPSBuZXcgV2Vha01hcDxPLCBNYXA8SywgVj4+KCk7XG5cbiAgcHVibGljIG9idGFpbihvYmplY3Q6IE8sIGtleTogSywgaW5pdDogKCkgPT4gVik6IFYge1xuICAgIGxldCBrdk1hcCA9IHRoaXMuY2FjaGUuZ2V0KG9iamVjdCk7XG4gICAgaWYgKCFrdk1hcCkge1xuICAgICAga3ZNYXAgPSBuZXcgTWFwKCk7XG4gICAgICB0aGlzLmNhY2hlLnNldChvYmplY3QsIGt2TWFwKTtcbiAgICB9XG5cbiAgICBsZXQgcmV0ID0ga3ZNYXAuZ2V0KGtleSk7XG4gICAgaWYgKHJldCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXQgPSBpbml0KCk7XG4gICAgICBrdk1hcC5zZXQoa2V5LCByZXQpO1xuICAgIH1cbiAgICByZXR1cm4gcmV0O1xuICB9XG59XG5cbmNvbnN0IHN0cmluZ2lmeUNhY2hlID0gbmV3IFNjb3BlZENhY2hlPFN0YWNrLCBzdHJpbmcsIHN0cmluZz4oKTsiXX0=