@aws-cdk/core
Version:
AWS Cloud Development Kit Core Library
567 lines • 76.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const cxapi = require("@aws-cdk/cx-api");
const cx_api_1 = require("@aws-cdk/cx-api");
const fs = require("fs");
const path = require("path");
const construct_1 = require("./construct");
const context_provider_1 = require("./context-provider");
const cloudformation_lang_1 = require("./private/cloudformation-lang");
const logical_id_1 = require("./private/logical-id");
const resolve_1 = require("./private/resolve");
const uniqueid_1 = require("./private/uniqueid");
const STACK_SYMBOL = Symbol.for('@aws-cdk/core.Stack');
const VALID_STACK_NAME_REGEX = /^[A-Za-z][A-Za-z0-9-]*$/;
/**
* A root construct which represents a single CloudFormation stack.
*/
class Stack extends construct_1.Construct {
/**
* Creates a new stack.
*
* @param scope Parent of this stack, usually a Program instance.
* @param name The name of the CloudFormation stack. Defaults to "Stack".
* @param props Stack properties.
*/
constructor(scope, name, props = {}) {
// For unit test convenience parents are optional, so bypass the type check when calling the parent.
super(scope, name);
/**
* Options for CloudFormation template (like version, transform, description).
*/
this.templateOptions = {};
/**
* Other stacks this stack depends on
*/
this._stackDependencies = new Set();
/**
* Lists all missing contextual information.
* This is returned when the stack is synthesized under the 'missing' attribute
* and allows tooling to obtain the context and re-synthesize.
*/
this._missingContext = new Array();
Object.defineProperty(this, STACK_SYMBOL, { value: true });
this._logicalIds = new logical_id_1.LogicalIDs();
const { account, region, environment } = this.parseEnvironment(props.env);
this.account = account;
this.region = region;
this.environment = environment;
this.stackName = props.stackName !== undefined ? props.stackName : this.calculateStackName();
this.tags = new tag_manager_1.TagManager(cfn_resource_1.TagType.KEY_VALUE, 'aws:cdk:stack', props.tags);
if (!VALID_STACK_NAME_REGEX.test(this.stackName)) {
throw new Error(`Stack name must match the regular expression: ${VALID_STACK_NAME_REGEX.toString()}, got '${name}'`);
}
this.templateFile = `${this.stackName}.template.json`;
}
/**
* Return whether the given object is a Stack.
*
* We do attribute detection since we can't reliably use 'instanceof'.
*/
static isStack(x) {
return x !== null && typeof (x) === 'object' && STACK_SYMBOL in x;
}
/**
* Looks up the first stack scope in which `construct` is defined. Fails if there is no stack up the tree.
* @param construct The construct to start the search from.
*/
static of(construct) {
return _lookup(construct);
function _lookup(c) {
if (Stack.isStack(c)) {
return c;
}
if (!c.node.scope) {
throw new Error(`No stack could be identified for the construct at path ${construct.node.path}`);
}
return _lookup(c.node.scope);
}
}
/**
* Resolve a tokenized value in the context of the current stack.
*/
resolve(obj) {
return resolve_1.resolve(obj, {
scope: this,
prefix: [],
resolver: cloudformation_lang_1.CLOUDFORMATION_TOKEN_RESOLVER,
preparing: false
});
}
/**
* Convert an object, potentially containing tokens, to a JSON string
*/
toJsonString(obj, space) {
return cloudformation_lang_1.CloudFormationLang.toJSON(obj, space).toString();
}
/**
* Indicate that a context key was expected
*
* Contains instructions which will be emitted into the cloud assembly on how
* the key should be supplied.
*
* @param report The set of parameters needed to obtain the context
*/
reportMissingContext(report) {
this._missingContext.push(report);
}
/**
* Rename a generated logical identities
*
* To modify the naming scheme strategy, extend the `Stack` class and
* override the `createNamingScheme` method.
*/
renameLogicalId(oldId, newId) {
this._logicalIds.addRename(oldId, newId);
}
/**
* Allocates a stack-unique CloudFormation-compatible logical identity for a
* specific resource.
*
* This method is called when a `CfnElement` is created and used to render the
* initial logical identity of resources. Logical ID renames are applied at
* this stage.
*
* This method uses the protected method `allocateLogicalId` to render the
* logical ID for an element. To modify the naming scheme, extend the `Stack`
* class and override this method.
*
* @param element The CloudFormation element for which a logical identity is
* needed.
*/
getLogicalId(element) {
const logicalId = this.allocateLogicalId(element);
return this._logicalIds.applyRename(logicalId);
}
/**
* Add a dependency between this stack and another stack
*/
addDependency(stack, reason) {
if (stack === this) {
return;
} // Can ignore a dependency on self
reason = reason || 'dependency added using stack.addDependency()';
const dep = stack.stackDependencyReasons(this);
if (dep !== undefined) {
// tslint:disable-next-line:max-line-length
throw new Error(`'${stack.node.path}' depends on '${this.node.path}' (${dep.join(', ')}). Adding this dependency (${reason}) would create a cyclic reference.`);
}
this._stackDependencies.add({ stack, reason });
}
/**
* Return the stacks this stack depends on
*/
get dependencies() {
return Array.from(this._stackDependencies.values()).map(d => d.stack);
}
/**
* The partition in which this stack is defined
*/
get partition() {
// Always return a non-scoped partition intrinsic. These will usually
// be used to construct an ARN, but there are no cross-partition
// calls anyway.
return cfn_pseudo_1.Aws.PARTITION;
}
/**
* The Amazon domain suffix for the region in which this stack is defined
*/
get urlSuffix() {
// Since URL Suffix always follows partition, it is unscoped like partition is.
return cfn_pseudo_1.Aws.URL_SUFFIX;
}
/**
* The ID of the stack
*
* @example After resolving, looks like arn:aws:cloudformation:us-west-2:123456789012:stack/teststack/51af3dc0-da77-11e4-872e-1234567db123
*/
get stackId() {
return new cfn_pseudo_1.ScopedAws(this).stackId;
}
/**
* Returns the list of notification Amazon Resource Names (ARNs) for the current stack.
*/
get notificationArns() {
return new cfn_pseudo_1.ScopedAws(this).notificationArns;
}
/**
* Creates an ARN from components.
*
* If `partition`, `region` or `account` are not specified, the stack's
* partition, region and account will be used.
*
* If any component is the empty string, an empty string will be inserted
* into the generated ARN at the location that component corresponds to.
*
* The ARN will be formatted as follows:
*
* arn:{partition}:{service}:{region}:{account}:{resource}{sep}}{resource-name}
*
* The required ARN pieces that are omitted will be taken from the stack that
* the 'scope' is attached to. If all ARN pieces are supplied, the supplied scope
* can be 'undefined'.
*/
formatArn(components) {
return arn_1.Arn.format(components, this);
}
/**
* Given an ARN, parses it and returns components.
*
* If the ARN is a concrete string, it will be parsed and validated. The
* separator (`sep`) will be set to '/' if the 6th component includes a '/',
* in which case, `resource` will be set to the value before the '/' and
* `resourceName` will be the rest. In case there is no '/', `resource` will
* be set to the 6th components and `resourceName` will be set to the rest
* of the string.
*
* If the ARN includes tokens (or is a token), the ARN cannot be validated,
* since we don't have the actual value yet at the time of this function
* call. You will have to know the separator and the type of ARN. The
* resulting `ArnComponents` object will contain tokens for the
* subexpressions of the ARN, not string literals. In this case this
* function cannot properly parse the complete final resourceName (path) out
* of ARNs that use '/' to both separate the 'resource' from the
* 'resourceName' AND to subdivide the resourceName further. For example, in
* S3 ARNs:
*
* arn:aws:s3:::my_corporate_bucket/path/to/exampleobject.png
*
* After parsing the resourceName will not contain
* 'path/to/exampleobject.png' but simply 'path'. This is a limitation
* because there is no slicing functionality in CloudFormation templates.
*
* @param arn The ARN string to parse
* @param sepIfToken The separator used to separate resource from resourceName
* @param hasName Whether there is a name component in the ARN at all. For
* example, SNS Topics ARNs have the 'resource' component contain the topic
* name, and no 'resourceName' component.
*
* @returns an ArnComponents object which allows access to the various
* components of the ARN.
*
* @returns an ArnComponents object which allows access to the various
* components of the ARN.
*/
parseArn(arn, sepIfToken = '/', hasName = true) {
return arn_1.Arn.parse(arn, sepIfToken, hasName);
}
/**
* Returnst the list of AZs that are availability in the AWS environment
* (account/region) associated with this stack.
*
* If the stack is environment-agnostic (either account and/or region are
* tokens), this property will return an array with 2 tokens that will resolve
* at deploy-time to the first two availability zones returned from CloudFormation's
* `Fn::GetAZs` intrinsic function.
*
* If they are not available in the context, returns a set of dummy values and
* reports them as missing, and let the CLI resolve them by calling EC2
* `DescribeAvailabilityZones` on the target environment.
*/
get availabilityZones() {
// if account/region are tokens, we can't obtain AZs through the context
// provider, so we fallback to use Fn::GetAZs. the current lowest common
// denominator is 2 AZs across all AWS regions.
const agnostic = token_1.Token.isUnresolved(this.account) || token_1.Token.isUnresolved(this.region);
if (agnostic) {
return this.node.tryGetContext(cxapi.AVAILABILITY_ZONE_FALLBACK_CONTEXT_KEY) || [
cfn_fn_1.Fn.select(0, cfn_fn_1.Fn.getAzs()),
cfn_fn_1.Fn.select(1, cfn_fn_1.Fn.getAzs())
];
}
const value = context_provider_1.ContextProvider.getValue(this, {
provider: cxapi.AVAILABILITY_ZONE_PROVIDER,
dummyValue: ['dummy1a', 'dummy1b', 'dummy1c'],
}).value;
if (!Array.isArray(value)) {
throw new Error(`Provider ${cxapi.AVAILABILITY_ZONE_PROVIDER} expects a list`);
}
return value;
}
/**
* Returns the naming scheme used to allocate logical IDs. By default, uses
* the `HashedAddressingScheme` but this method can be overridden to customize
* this behavior.
*
* In order to make sure logical IDs are unique and stable, we hash the resource
* construct tree path (i.e. toplevel/secondlevel/.../myresource) and add it as
* a suffix to the path components joined without a separator (CloudFormation
* IDs only allow alphanumeric characters).
*
* The result will be:
*
* <path.join('')><md5(path.join('/')>
* "human" "hash"
*
* If the "human" part of the ID exceeds 240 characters, we simply trim it so
* the total ID doesn't exceed CloudFormation's 255 character limit.
*
* We only take 8 characters from the md5 hash (0.000005 chance of collision).
*
* Special cases:
*
* - If the path only contains a single component (i.e. it's a top-level
* resource), we won't add the hash to it. The hash is not needed for
* disamiguation and also, it allows for a more straightforward migration an
* existing CloudFormation template to a CDK stack without logical ID changes
* (or renames).
* - For aesthetic reasons, if the last components of the path are the same
* (i.e. `L1/L2/Pipeline/Pipeline`), they will be de-duplicated to make the
* resulting human portion of the ID more pleasing: `L1L2Pipeline<HASH>`
* instead of `L1L2PipelinePipeline<HASH>`
* - If a component is named "Default" it will be omitted from the path. This
* allows refactoring higher level abstractions around constructs without affecting
* the IDs of already deployed resources.
* - If a component is named "Resource" it will be omitted from the user-visible
* path, but included in the hash. This reduces visual noise in the human readable
* part of the identifier.
*
* @param cfnElement The element for which the logical ID is allocated.
*/
allocateLogicalId(cfnElement) {
const scopes = cfnElement.node.scopes;
const stackIndex = scopes.indexOf(cfnElement.stack);
const pathComponents = scopes.slice(stackIndex + 1).map(x => x.node.id);
return uniqueid_1.makeUniqueId(pathComponents);
}
/**
* Validate stack name
*
* CloudFormation stack names can include dashes in addition to the regular identifier
* character classes, and we don't allow one of the magic markers.
*
* @internal
*/
_validateId(name) {
if (name && !VALID_STACK_NAME_REGEX.test(name)) {
throw new Error(`Stack name must match the regular expression: ${VALID_STACK_NAME_REGEX.toString()}, got '${name}'`);
}
}
/**
* Prepare stack
*
* Find all CloudFormation references and tell them we're consuming them.
*
* Find all dependencies as well and add the appropriate DependsOn fields.
*/
prepare() {
// References
for (const ref of this.node.references) {
if (cfn_reference_1.CfnReference.isCfnReference(ref.reference)) {
ref.reference.consumeFromStack(this, ref.source);
}
}
// Resource dependencies
for (const dependency of this.node.dependencies) {
const theirStack = Stack.of(dependency.target);
if (theirStack !== undefined && theirStack !== this) {
this.addDependency(theirStack);
}
else {
for (const target of findResources([dependency.target])) {
for (const source of findResources([dependency.source])) {
source.addDependsOn(target);
}
}
}
}
if (this.tags.hasTags()) {
this.node.addMetadata(cxapi.STACK_TAGS_METADATA_KEY, this.tags.renderTags());
}
}
synthesize(session) {
const builder = session.assembly;
// write the CloudFormation template as a JSON file
const outPath = path.join(builder.outdir, this.templateFile);
fs.writeFileSync(outPath, JSON.stringify(this._toCloudFormation(), undefined, 2));
const deps = this.dependencies.map(s => s.stackName);
const meta = this.collectMetadata();
const properties = {
templateFile: this.templateFile
};
// add an artifact that represents this stack
builder.addArtifact(this.stackName, {
type: cxapi.ArtifactType.AWS_CLOUDFORMATION_STACK,
environment: this.environment,
properties,
dependencies: deps.length > 0 ? deps : undefined,
metadata: Object.keys(meta).length > 0 ? meta : undefined,
});
for (const ctx of this._missingContext) {
builder.addMissing(ctx);
}
}
/**
* Returns the CloudFormation template for this stack by traversing
* the tree and invoking _toCloudFormation() on all Entity objects.
*
* @internal
*/
_toCloudFormation() {
if (this.templateOptions.transform) {
// tslint:disable-next-line: max-line-length
this.node.addWarning('This stack is using the deprecated `templateOptions.transform` property. Consider switching to `templateOptions.transforms`.');
if (!this.templateOptions.transforms) {
this.templateOptions.transforms = [];
}
if (this.templateOptions.transforms.indexOf(this.templateOptions.transform) === -1) {
this.templateOptions.transforms.unshift(this.templateOptions.transform);
}
}
const template = {
Description: this.templateOptions.description,
Transform: extractSingleValue(this.templateOptions.transforms),
AWSTemplateFormatVersion: this.templateOptions.templateFormatVersion,
Metadata: this.templateOptions.metadata
};
const elements = cfnElements(this);
const fragments = elements.map(e => this.resolve(e._toCloudFormation()));
// merge in all CloudFormation fragments collected from the tree
for (const fragment of fragments) {
merge(template, fragment);
}
// resolve all tokens and remove all empties
const ret = this.resolve(template) || {};
this._logicalIds.assertAllRenamesApplied();
return ret;
}
/**
* Determine the various stack environment attributes.
*
*/
parseEnvironment(env = {}) {
// if an environment property is explicitly specified when the stack is
// created, it will be used. if not, use tokens for account and region but
// they do not need to be scoped, the only situation in which
// export/fn::importvalue would work if { Ref: "AWS::AccountId" } is the
// same for provider and consumer anyway.
const account = env.account || cfn_pseudo_1.Aws.ACCOUNT_ID;
const region = env.region || cfn_pseudo_1.Aws.REGION;
// this is the "aws://" env specification that will be written to the cloud assembly
// manifest. it will use "unknown-account" and "unknown-region" to indicate
// environment-agnosticness.
const envAccount = !token_1.Token.isUnresolved(account) ? account : cxapi.UNKNOWN_ACCOUNT;
const envRegion = !token_1.Token.isUnresolved(region) ? region : cxapi.UNKNOWN_REGION;
return {
account, region,
environment: cx_api_1.EnvironmentUtils.format(envAccount, envRegion)
};
}
/**
* Check whether this stack has a (transitive) dependency on another stack
*
* Returns the list of reasons on the dependency path, or undefined
* if there is no dependency.
*/
stackDependencyReasons(other) {
if (this === other) {
return [];
}
for (const dep of this._stackDependencies) {
const ret = dep.stack.stackDependencyReasons(other);
if (ret !== undefined) {
return [dep.reason].concat(ret);
}
}
return undefined;
}
collectMetadata() {
const output = {};
const stack = this;
visit(this);
return output;
function visit(node) {
if (node.node.metadata.length > 0) {
// Make the path absolute
output[construct_1.ConstructNode.PATH_SEP + node.node.path] = node.node.metadata.map(md => stack.resolve(md));
}
for (const child of node.node.children) {
visit(child);
}
}
}
/**
* Calculcate the stack name based on the construct path
*/
calculateStackName() {
// In tests, it's possible for this stack to be the root object, in which case
// we need to use it as part of the root path.
const rootPath = this.node.scope !== undefined ? this.node.scopes.slice(1) : [this];
const ids = rootPath.map(c => c.node.id);
// Special case, if rootPath is length 1 then just use ID (backwards compatibility)
// otherwise use a unique stack name (including hash). This logic is already
// in makeUniqueId, *however* makeUniqueId will also strip dashes from the name,
// which *are* allowed and also used, so we short-circuit it.
if (ids.length === 1) {
// Could be empty in a unit test, so just pretend it's named "Stack" then
return ids[0] || 'Stack';
}
return uniqueid_1.makeUniqueId(ids);
}
}
exports.Stack = Stack;
function merge(template, part) {
for (const section of Object.keys(part)) {
const src = part[section];
// create top-level section if it doesn't exist
let dest = template[section];
if (!dest) {
template[section] = dest = src;
}
else {
// add all entities from source section to destination section
for (const id of Object.keys(src)) {
if (id in dest) {
throw new Error(`section '${section}' already contains '${id}'`);
}
dest[id] = src[id];
}
}
}
}
/**
* Collect all CfnElements from a Stack
*
* @param node Root node to collect all CfnElements from
* @param into Array to append CfnElements to
* @returns The same array as is being collected into
*/
function cfnElements(node, into = []) {
if (cfn_element_1.CfnElement.isCfnElement(node)) {
into.push(node);
}
for (const child of node.node.children) {
// Don't recurse into a substack
if (Stack.isStack(child)) {
continue;
}
cfnElements(child, into);
}
return into;
}
// These imports have to be at the end to prevent circular imports
const arn_1 = require("./arn");
const cfn_element_1 = require("./cfn-element");
const cfn_fn_1 = require("./cfn-fn");
const cfn_pseudo_1 = require("./cfn-pseudo");
const cfn_resource_1 = require("./cfn-resource");
const cfn_reference_1 = require("./private/cfn-reference");
const tag_manager_1 = require("./tag-manager");
const token_1 = require("./token");
/**
* Find all resources in a set of constructs
*/
function findResources(roots) {
const ret = new Array();
for (const root of roots) {
ret.push(...root.node.findAll().filter(cfn_resource_1.CfnResource.isCfnResource));
}
return ret;
}
function extractSingleValue(array) {
if (array && array.length === 1) {
return array[0];
}
return array;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhY2suanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJzdGFjay50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLHlDQUEwQztBQUMxQyw0Q0FBbUQ7QUFDbkQseUJBQTBCO0FBQzFCLDZCQUE4QjtBQUM5QiwyQ0FBc0Y7QUFDdEYseURBQXFEO0FBRXJELHVFQUFrRztBQUNsRyxxREFBa0Q7QUFDbEQsK0NBQTRDO0FBQzVDLGlEQUFrRDtBQUVsRCxNQUFNLFlBQVksR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLHFCQUFxQixDQUFDLENBQUM7QUFDdkQsTUFBTSxzQkFBc0IsR0FBRyx5QkFBeUIsQ0FBQztBQTBCekQ7O0dBRUc7QUFDSCxNQUFhLEtBQU0sU0FBUSxxQkFBUztJQTBJbEM7Ozs7OztPQU1HO0lBQ0gsWUFBbUIsS0FBaUIsRUFBRSxJQUFhLEVBQUUsUUFBb0IsRUFBRTtRQUN6RSxvR0FBb0c7UUFDcEcsS0FBSyxDQUFDLEtBQU0sRUFBRSxJQUFLLENBQUMsQ0FBQztRQWhIdkI7O1dBRUc7UUFDYSxvQkFBZSxHQUFxQixFQUFFLENBQUM7UUF3RnZEOztXQUVHO1FBQ2MsdUJBQWtCLEdBQUcsSUFBSSxHQUFHLEVBQW1CLENBQUM7UUFFakU7Ozs7V0FJRztRQUNjLG9CQUFlLEdBQUcsSUFBSSxLQUFLLEVBQXdCLENBQUM7UUFhbkUsTUFBTSxDQUFDLGNBQWMsQ0FBQyxJQUFJLEVBQUUsWUFBWSxFQUFFLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7UUFFM0QsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLHVCQUFVLEVBQUUsQ0FBQztRQUVwQyxNQUFNLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsR0FBRyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBRTFFLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxNQUFNLEdBQUcs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QyxFQUFFO1lBQUUsU0FBUztTQUFFO1FBRXZDLFdBQVcsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUM7S0FDMUI7SUFFRCxPQUFPLElBQUksQ0FBQztBQUNkLENBQUM7QUFFRCxrRUFBa0U7QUFDbEUsK0JBQTJDO0FBQzNDLCtDQUEyQztBQUMzQyxxQ0FBOEI7QUFDOUIsNkNBQThDO0FBQzlDLGlEQUFzRDtBQUN0RCwyREFBdUQ7QUFDdkQsK0NBQXNEO0FBQ3RELG1DQUFnQztBQUVoQzs7R0FFRztBQUNILFNBQVMsYUFBYSxDQUFDLEtBQTJCO0lBQ2hELE1BQU0sR0FBRyxHQUFHLElBQUksS0FBSyxFQUFlLENBQUM7SUFDckMsS0FBSyxNQUFNLElBQUksSUFBSSxLQUFLLEVBQUU7UUFDeEIsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUMsTUFBTSxDQUFDLDBCQUFXLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQztLQUNwRTtJQUNELE9BQU8sR0FBRyxDQUFDO0FBQ2IsQ0FBQztBQU9ELFNBQVMsa0JBQWtCLENBQUksS0FBc0I7SUFDbkQsSUFBSSxLQUFLLElBQUksS0FBSyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUU7UUFDL0IsT0FBTyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7S0FDakI7SUFDRCxPQUFPLEtBQUssQ0FBQztBQUNmLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgY3hhcGkgPSByZXF1aXJlKCdAYXdzLWNkay9jeC1hcGknKTtcbmltcG9ydCB7IEVudmlyb25tZW50VXRpbHMgfSBmcm9tICdAYXdzLWNkay9jeC1hcGknO1xuaW1wb3J0IGZzID0gcmVxdWlyZSgnZnMnKTtcbmltcG9ydCBwYXRoID0gcmVxdWlyZSgncGF0aCcpO1xuaW1wb3J0IHsgQ29uc3RydWN0LCBDb25zdHJ1Y3ROb2RlLCBJQ29uc3RydWN0LCBJU3ludGhlc2lzU2Vzc2lvbiB9IGZyb20gJy4vY29uc3RydWN0JztcbmltcG9ydCB7IENvbnRleHRQcm92aWRlciB9IGZyb20gJy4vY29udGV4dC1wcm92aWRlcic7XG5pbXBvcnQgeyBFbnZpcm9ubWVudCB9IGZyb20gJy4vZW52aXJvbm1lbnQnO1xuaW1wb3J0IHsgQ0xPVURGT1JNQVRJT05fVE9LRU5fUkVTT0xWRVIsIENsb3VkRm9ybWF0aW9uTGFuZyB9IGZyb20gJy4vcHJpdmF0ZS9jbG91ZGZvcm1hdGlvbi1sYW5nJztcbmltcG9ydCB7IExvZ2ljYWxJRHMgfSBmcm9tICcuL3ByaXZhdGUvbG9naWNhbC1pZCc7XG5pbXBvcnQgeyByZXNvbHZlIH0gZnJvbSAnLi9wcml2YXRlL3Jlc29sdmUnO1xuaW1wb3J0IHsgbWFrZVVuaXF1ZUlkIH0gZnJvbSAnLi9wcml2YXRlL3VuaXF1ZWlkJztcblxuY29uc3QgU1RBQ0tfU1lNQk9MID0gU3ltYm9sLmZvcignQGF3cy1jZGsvY29yZS5TdGFjaycpO1xuY29uc3QgVkFMSURfU1RBQ0tfTkFNRV9SRUdFWCA9IC9eW0EtWmEtel1bQS1aYS16MC05LV0qJC87XG5cbmV4cG9ydCBpbnRlcmZhY2UgU3RhY2tQcm9wcyB7XG4gIC8qKlxuICAgKiBUaGUgQVdTIGVudmlyb25tZW50IChhY2NvdW50L3JlZ2lvbikgd2hlcmUgdGhpcyBzdGFjayB3aWxsIGJlIGRlcGxveWVkLlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIFRoZSBgZGVmYXVsdC1hY2NvdW50YCBhbmQgYGRlZmF1bHQtcmVnaW9uYCBjb250ZXh0IHBhcmFtZXRlcnMgd2lsbCBiZVxuICAgKiB1c2VkLiBJZiB0aGV5IGFyZSB1bmRlZmluZWQsIGl0IHdpbGwgbm90IGJlIHBvc3NpYmxlIHRvIGRlcGxveSB0aGUgc3RhY2suXG4gICAqL1xuICByZWFkb25seSBlbnY/OiBFbnZpcm9ubWVudDtcblxuICAvKipcbiAgICogTmFtZSB0byBkZXBsb3kgdGhlIHN0YWNrIHdpdGhcbiAgICpcbiAgICogQGRlZmF1bHQgLSBEZXJpdmVkIGZyb20gY29uc3RydWN0IHBhdGguXG4gICAqL1xuICByZWFkb25seSBzdGFja05hbWU/OiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFN0YWNrIHRhZ3MgdGhhdCB3aWxsIGJlIGFwcGxpZWQgdG8gYWxsIHRoZSB0YWdnYWJsZSByZXNvdXJjZXMgYW5kIHRoZSBzdGFjayBpdHNlbGYuXG4gICAqXG4gICAqIEBkZWZhdWx0IHt9XG4gICAqL1xuICByZWFkb25seSB0YWdzPzogeyBba2V5OiBzdHJpbmddOiBzdHJpbmcgfTtcbn1cblxuLyoqXG4gKiBBIHJvb3QgY29uc3RydWN0IHdoaWNoIHJlcHJlc2VudHMgYSBzaW5nbGUgQ2xvdWRGb3JtYXRpb24gc3RhY2suXG4gKi9cbmV4cG9ydCBjbGFzcyBTdGFjayBleHRlbmRzIENvbnN0cnVjdCBpbXBsZW1lbnRzIElUYWdnYWJsZSB7XG4gIC8qKlxuICAgKiBSZXR1cm4gd2hldGhlciB0aGUgZ2l2ZW4gb2JqZWN0IGlzIGEgU3RhY2suXG4gICAqXG4gICAqIFdlIGRvIGF0dHJpYnV0ZSBkZXRlY3Rpb24gc2luY2Ugd2UgY2FuJ3QgcmVsaWFibHkgdXNlICdpbnN0YW5jZW9mJy5cbiAgICovXG4gIHB1YmxpYyBzdGF0aWMgaXNTdGFjayh4OiBhbnkpOiB4IGlzIFN0YWNrIHtcbiAgICByZXR1cm4geCAhPT0gbnVsbCAmJiB0eXBlb2YoeCkgPT09ICdvYmplY3QnICYmIFNUQUNLX1NZTUJPTCBpbiB4O1xuICB9XG5cbiAgLyoqXG4gICAqIExvb2tzIHVwIHRoZSBmaXJzdCBzdGFjayBzY29wZSBpbiB3aGljaCBgY29uc3RydWN0YCBpcyBkZWZpbmVkLiBGYWlscyBpZiB0aGVyZSBpcyBubyBzdGFjayB1cCB0aGUgdHJlZS5cbiAgICogQHBhcmFtIGNvbnN0cnVjdCBUaGUgY29uc3RydWN0IHRvIHN0YXJ0IHRoZSBzZWFyY2ggZnJvbS5cbiAgICovXG4gIHB1YmxpYyBzdGF0aWMgb2YoY29uc3RydWN0OiBJQ29uc3RydWN0KTogU3RhY2sge1xuICAgIHJldHVybiBfbG9va3VwKGNvbnN0cnVjdCk7XG5cbiAgICBmdW5jdGlvbiBfbG9va3VwKGM6IElDb25zdHJ1Y3QpOiBTdGFjayAge1xuICAgICAgaWYgKFN0YWNrLmlzU3RhY2soYykpIHtcbiAgICAgICAgcmV0dXJuIGM7XG4gICAgICB9XG5cbiAgICAgIGlmICghYy5ub2RlLnNjb3BlKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgTm8gc3RhY2sgY291bGQgYmUgaWRlbnRpZmllZCBmb3IgdGhlIGNvbnN0cnVjdCBhdCBwYXRoICR7Y29uc3RydWN0Lm5vZGUucGF0aH1gKTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIF9sb29rdXAoYy5ub2RlLnNjb3BlKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVGFncyB0byBiZSBhcHBsaWVkIHRvIHRoZSBzdGFjay5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSB0YWdzOiBUYWdNYW5hZ2VyO1xuXG4gIC8qKlxuICAgKiBPcHRpb25zIGZvciBDbG91ZEZvcm1hdGlvbiB0ZW1wbGF0ZSAobGlrZSB2ZXJzaW9uLCB0cmFuc2Zvcm0sIGRlc2NyaXB0aW9uKS5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSB0ZW1wbGF0ZU9wdGlvbnM6IElUZW1wbGF0ZU9wdGlvbnMgPSB7fTtcblxuICAvKipcbiAgICogVGhlIGNvbmNyZXRlIENsb3VkRm9ybWF0aW9uIHBoeXNpY2FsIHN0YWNrIG5hbWUuXG4gICAqXG4gICAqIFRoaXMgaXMgZWl0aGVyIHRoZSBuYW1lIGRlZmluZWQgZXhwbGljaXRseSBpbiB0aGUgYHN0YWNrTmFtZWAgcHJvcCBvclxuICAgKiBhbGxvY2F0ZWQgYmFzZWQgb24gdGhlIHN0YWNrJ3MgbG9jYXRpb24gaW4gdGhlIGNvbnN0cnVjdCB0cmVlLiBTdGFja3MgdGhhdFxuICAgKiBhcmUgZGlyZWN0bHkgZGVmaW5lZCB1bmRlciB0aGUgYXBwIHVzZSB0aGVpciBjb25zdHJ1Y3QgYGlkYCBhcyB0aGVpciBzdGFja1xuICAgKiBuYW1lLiBTdGFja3MgdGhhdCBhcmUgZGVmaW5lZCBkZWVwZXIgd2l0aGluIHRoZSB0cmVlIHdpbGwgdXNlIGEgaGFzaGVkIG5hbWluZ1xuICAgKiBzY2hlbWUgYmFzZWQgb24gdGhlIGNvbnN0cnVjdCBwYXRoIHRvIGVuc3VyZSB1bmlxdWVuZXNzLlxuICAgKlxuICAgKiBJZiB5b3Ugd2lzaCB0byBvYnRhaW4gdGhlIGRlcGxveS10aW1lIEFXUzo6U3RhY2tOYW1lIGludHJpbnNpYyxcbiAgICogeW91IGNhbiB1c2UgYEF3cy5zdGFja05hbWVgIGRpcmVjdGx5LlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IHN0YWNrTmFtZTogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBUaGUgQVdTIHJlZ2lvbiBpbnRvIHdoaWNoIHRoaXMgc3RhY2sgd2lsbCBiZSBkZXBsb3llZCAoZS5nLiBgdXMtd2VzdC0yYCkuXG4gICAqXG4gICAqIFRoaXMgdmFsdWUgaXMgcmVzb2x2ZWQgYWNjb3JkaW5nIHRvIHRoZSBmb2xsb3dpbmcgcnVsZXM6XG4gICAqXG4gICAqIDEuIFRoZSB2YWx1ZSBwcm92aWRlZCB0byBgZW52LnJlZ2lvbmAgd2hlbiB0aGUgc3RhY2sgaXMgZGVmaW5lZC4gVGhpcyBjYW5cbiAgICogICAgZWl0aGVyIGJlIGEgY29uY2VyZXRlIHJlZ2lvbiAoZS5nLiBgdXMtd2VzdC0yYCkgb3IgdGhlIGBBd3MucmVnaW9uYFxuICAgKiAgICB0b2tlbi5cbiAgICogMy4gYEF3cy5yZWdpb25gLCB3aGljaCBpcyByZXByZXNlbnRzIHRoZSBDbG91ZEZvcm1hdGlvbiBpbnRyaW5zaWMgcmVmZXJlbmNlXG4gICAqICAgIGB7IFwiUmVmXCI6IFwiQVdTOjpSZWdpb25cIiB9YCBlbmNvZGVkIGFzIGEgc3RyaW5nIHRva2VuLlxuICAgKlxuICAgKiBQcmVmZXJhYmx5LCB5b3Ugc2hvdWxkIHVzZSB0aGUgcmV0dXJuIHZhbHVlIGFzIGFuIG9wYXF1ZSBzdHJpbmcgYW5kIG5vdFxuICAgKiBhdHRlbXB0IHRvIHBhcnNlIGl0IHRvIGltcGxlbWVudCB5b3VyIGxvZ2ljLiBJZiB5b3UgZG8sIHlvdSBtdXN0IGZpcnN0XG4gICAqIGNoZWNrIHRoYXQgaXQgaXMgYSBjb25jZXJldGUgdmFsdWUgYW4gbm90IGFuIHVucmVzb2x2ZWQgdG9rZW4uIElmIHRoaXNcbiAgICogdmFsdWUgaXMgYW4gdW5yZXNvbHZlZCB0b2tlbiAoYFRva2VuLmlzVW5yZXNvbHZlZChzdGFjay5yZWdpb24pYCByZXR1cm5zXG4gICAqIGB0cnVlYCksIHRoaXMgaW1wbGllcyB0aGF0IHRoZSB1c2VyIHdpc2hlcyB0aGF0IHRoaXMgc3RhY2sgd2lsbCBzeW50aGVzaXplXG4gICAqIGludG8gYSAqKnJlZ2lvbi1hZ25vc3RpYyB0ZW1wbGF0ZSoqLiBJbiB0aGlzIGNhc2UsIHlvdXIgY29kZSBzaG91bGQgZWl0aGVyXG4gICAqIGZhaWwgKHRocm93IGFuIGVycm9yLCBlbWl0IGEgc3ludGggZXJyb3IgdXNpbmcgYG5vZGUuYWRkRXJyb3JgKSBvclxuICAgKiBpbXBsZW1lbnQgc29tZSBvdGhlciByZWdpb24tYWdub3N0aWMgYmVoYXZpb3IuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgcmVnaW9uOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFRoZSBBV1MgYWNjb3VudCBpbnRvIHdoaWNoIHRoaXMgc3RhY2sgd2lsbCBiZSBkZXBsb3llZC5cbiAgICpcbiAgICogVGhpcyB2YWx1ZSBpcyByZXNvbHZlZCBhY2NvcmRpbmcgdG8gdGhlIGZvbGxvd2luZyBydWxlczpcbiAgICpcbiAgICogMS4gVGhlIHZhbHVlIHByb3ZpZGVkIHRvIGBlbnYuYWNjb3VudGAgd2hlbiB0aGUgc3RhY2sgaXMgZGVmaW5lZC4gVGhpcyBjYW5cbiAgICogICAgZWl0aGVyIGJlIGEgY29uY2VyZXRlIGFjY291bnQgKGUuZy4gYDU4NTY5NTAzMTExMWApIG9yIHRoZVxuICAgKiAgICBgQXdzLmFjY291bnRJZGAgdG9rZW4uXG4gICAqIDMuIGBBd3MuYWNjb3VudElkYCwgd2hpY2ggcmVwcmVzZW50cyB0aGUgQ2xvdWRGb3JtYXRpb24gaW50cmluc2ljIHJlZmVyZW5jZVxuICAgKiAgICBgeyBcIlJlZlwiOiBcIkFXUzo6QWNjb3VudElkXCIgfWAgZW5jb2RlZCBhcyBhIHN0cmluZyB0b2tlbi5cbiAgICpcbiAgICogUHJlZmVyYWJseSwgeW91IHNob3VsZCB1c2UgdGhlIHJldHVybiB2YWx1ZSBhcyBhbiBvcGFxdWUgc3RyaW5nIGFuZCBub3RcbiAgICogYXR0ZW1wdCB0byBwYXJzZSBpdCB0byBpbXBsZW1lbnQgeW91ciBsb2dpYy4gSWYgeW91IGRvLCB5b3UgbXVzdCBmaXJzdFxuICAgKiBjaGVjayB0aGF0IGl0IGlzIGEgY29uY2VyZXRlIHZhbHVlIGFuIG5vdCBhbiB1bnJlc29sdmVkIHRva2VuLiBJZiB0aGlzXG4gICAqIHZhbHVlIGlzIGFuIHVucmVzb2x2ZWQgdG9rZW4gKGBUb2tlbi5pc1VucmVzb2x2ZWQoc3RhY2suYWNjb3VudClgIHJldHVybnNcbiAgICogYHRydWVgKSwgdGhpcyBpbXBsaWVzIHRoYXQgdGhlIHVzZXIgd2lzaGVzIHRoYXQgdGhpcyBzdGFjayB3aWxsIHN5bnRoZXNpemVcbiAgICogaW50byBhICoqYWNjb3VudC1hZ25vc3RpYyB0ZW1wbGF0ZSoqLiBJbiB0aGlzIGNhc2UsIHlvdXIgY29kZSBzaG91bGQgZWl0aGVyXG4gICAqIGZhaWwgKHRocm93IGFuIGVycm9yLCBlbWl0IGEgc3ludGggZXJyb3IgdXNpbmcgYG5vZGUuYWRkRXJyb3JgKSBvclxuICAgKiBpbXBsZW1lbnQgc29tZSBvdGhlciByZWdpb24tYWdub3N0aWMgYmVoYXZpb3IuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgYWNjb3VudDogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBUaGUgZW52aXJvbm1lbnQgY29vcmRpbmF0ZXMgaW4gd2hpY2ggdGhpcyBzdGFjayBpcyBkZXBsb3llZC4gSW4gdGhlIGZvcm1cbiAgICogYGF3czovL2FjY291bnQvcmVnaW9uYC4gVXNlIGBzdGFjay5hY2NvdW50YCBhbmQgYHN0YWNrLnJlZ2lvbmAgdG8gb2J0YWluXG4gICAqIHRoZSBzcGVjaWZpYyB2YWx1ZXMsIG5vIG5lZWQgdG8gcGFyc2UuXG4gICAqXG4gICAqIFlvdSBjYW4gdXNlIHRoaXMgdmFsdWUgdG8gZGV0ZXJtaW5lIGlmIHR3byBzdGFja3MgYXJlIHRhcmdldGluZyB0aGUgc2FtZVxuICAgKiBlbnZpcm9ubWVudC5cbiAgICpcbiAgICogSWYgZWl0aGVyIGBzdGFjay5hY2NvdW50YCBvciBgc3RhY2sucmVnaW9uYCBhcmUgbm90IGNvbmNyZXRlIHZhbHVlcyAoZS5nLlxuICAgKiBgQXdzLmFjY291bnRgIG9yIGBBd3MucmVnaW9uYCkgdGhlIHNwZWNpYWwgc3RyaW5ncyBgdW5rbm93bi1hY2NvdW50YCBhbmQvb3JcbiAgICogYHVua25vd24tcmVnaW9uYCB3aWxsIGJlIHVzZWQgcmVzcGVjdGl2ZWx5IHRvIGluZGljYXRlIHRoaXMgc3RhY2sgaXNcbiAgICogcmVnaW9uL2FjY291bnQtYWdub3N0aWMuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgZW52aXJvbm1lbnQ6IHN0cmluZztcblxuICAvKipcbiAgICogVGhlIG5hbWUgb2YgdGhlIENsb3VkRm9ybWF0aW9uIHRlbXBsYXRlIGZpbGUgZW1pdHRlZCB0byB0aGUgb3V0cHV0XG4gICAqIGRpcmVjdG9yeSBkdXJpbmcgc3ludGhlc2lzLlxuICAgKlxuICAgKiBAZXhhbXBsZSBNeVN0YWNrLnRlbXBsYXRlLmpzb25cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSB0ZW1wbGF0ZUZpbGU6IHN0cmluZztcblxuICAvKipcbiAgICogTG9naWNhbCBJRCBnZW5lcmF0aW9uIHN0cmF0ZWd5XG4gICAqL1xuICBwcml2YXRlIHJlYWRvbmx5IF9sb2dpY2FsSWRzOiBMb2dpY2FsSURzO1xuXG4gIC8qKlxuICAgKiBPdGhlciBzdGFja3MgdGhpcyBzdGFjayBkZXBlbmRzIG9uXG4gICAqL1xuICBwcml2YXRlIHJlYWRvbmx5IF9zdGFja0RlcGVuZGVuY2llcyA9IG5ldyBTZXQ8U3RhY2tEZXBlbmRlbmN5PigpO1xuXG4gIC8qKlxuICAgKiBMaXN0cyBhbGwgbWlzc2luZyBjb250ZXh0dWFsIGluZm9ybWF0aW9uLlxuICAgKiBUaGlzIGlzIHJldHVybmVkIHdoZW4gdGhlIHN0YWNrIGlzIHN5bnRoZXNpemVkIHVuZGVyIHRoZSAnbWlzc2luZycgYXR0cmlidXRlXG4gICAqIGFuZCBhbGxvd3MgdG9vbGluZyB0byBvYnRhaW4gdGhlIGNvbnRleHQgYW5kIHJlLXN5bnRoZXNpemUuXG4gICAqL1xuICBwcml2YXRlIHJlYWRvbmx5IF9taXNzaW5nQ29udGV4dCA9IG5ldyBBcnJheTxjeGFwaS5NaXNzaW5nQ29udGV4dD4oKTtcblxuICAvKipcbiAgICogQ3JlYXRlcyBhIG5ldyBzdGFjay5cbiAgICpcbiAgICogQHBhcmFtIHNjb3BlIFBhcmVudCBvZiB0aGlzIHN0YWNrLCB1c3VhbGx5IGEgUHJvZ3JhbSBpbnN0YW5jZS5cbiAgICogQHBhcmFtIG5hbWUgVGhlIG5hbWUgb2YgdGhlIENsb3VkRm9ybWF0aW9uIHN0YWNrLiBEZWZhdWx0cyB0byBcIlN0YWNrXCIuXG4gICAqIEBwYXJhbSBwcm9wcyBTdGFjayBwcm9wZXJ0aWVzLlxuICAgKi9cbiAgcHVibGljIGNvbnN0cnVjdG9yKHNjb3BlPzogQ29uc3RydWN0LCBuYW1lPzogc3RyaW5nLCBwcm9wczogU3RhY2tQcm9wcyA9IHt9KSB7XG4gICAgLy8gRm9yIHVuaXQgdGVzdCBjb252ZW5pZW5jZSBwYXJlbnRzIGFyZSBvcHRpb25hbCwgc28gYnlwYXNzIHRoZSB0eXBlIGNoZWNrIHdoZW4gY2FsbGluZyB0aGUgcGFyZW50LlxuICAgIHN1cGVyKHNjb3BlISwgbmFtZSEpO1xuXG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRoaXMsIFNUQUNLX1NZTUJPTCwgeyB2YWx1ZTogdHJ1ZSB9KTtcblxuICAgIHRoaXMuX2xvZ2ljYWxJZHMgPSBuZXcgTG9naWNhbElEcygpO1xuXG4gICAgY29uc3QgeyBhY2NvdW50LCByZWdpb24sIGVudmlyb25tZW50IH0gPSB0aGlzLnBhcnNlRW52aXJvbm1lbnQocHJvcHMuZW52KTtcblxuICAgIHRoaXMuYWNjb3VudCA9IGFjY291bnQ7XG4gICAgdGhpcy5yZWdpb24gPSByZWdpb247XG4gICAgdGhpcy5lbnZpcm9ubWVudCA9IGVudmlyb25tZW50O1xuXG4gICAgdGhpcy5zdGFja05hbWUgPSBwcm9wcy5zdGFja05hbWUgIT09IHVuZGVmaW5lZCA/IHByb3BzLnN0YWNrTmFtZSA6IHRoaXMuY2FsY3VsYXRlU3RhY2tOYW1lKCk7XG4gICAgdGhpcy50YWdzID0gbmV3IFRhZ01hbmFnZXIoVGFnVHlwZS5LRVlfVkFMVUUsICdhd3M6Y2RrOnN0YWNrJywgcHJvcHMudGFncyk7XG5cbiAgICBpZiAoIVZBTElEX1NUQUNLX05BTUVfUkVHRVgudGVzdCh0aGlzLnN0YWNrTmFtZSkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgU3RhY2sgbmFtZSBtdXN0IG1hdGNoIHRoZSByZWd1bGFyIGV4cHJlc3Npb246ICR7VkFMSURfU1RBQ0tfTkFNRV9SRUdFWC50b1N0cmluZygpfSwgZ290ICcke25hbWV9J2ApO1xuICAgIH1cblxuICAgIHRoaXMudGVtcGxhdGVGaWxlID0gYCR7dGhpcy5zdGFja05hbWV9LnRlbXBsYXRlLmpzb25gO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlc29sdmUgYSB0b2tlbml6ZWQgdmFsdWUgaW4gdGhlIGNvbnRleHQgb2YgdGhlIGN1cnJlbnQgc3RhY2suXG4gICAqL1xuICBwdWJsaWMgcmVzb2x2ZShvYmo6IGFueSk6IGFueSB7XG4gICAgcmV0dXJuIHJlc29sdmUob2JqLCB7XG4gICAgICBzY29wZTogdGhpcyxcbiAgICAgIHByZWZpeDogW10sXG4gICAgICByZXNvbHZlcjogQ0xPVURGT1JNQVRJT05fVE9LRU5fUkVTT0xWRVIsXG4gICAgICBwcmVwYXJpbmc6IGZhbHNlXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogQ29udmVydCBhbiBvYmplY3QsIHBvdGVudGlhbGx5IGNvbnRhaW5pbmcgdG9rZW5zLCB0byBhIEpTT04gc3RyaW5nXG4gICAqL1xuICBwdWJsaWMgdG9Kc29uU3RyaW5nKG9iajogYW55LCBzcGFjZT86IG51bWJlcik6IHN0cmluZyB7XG4