aws-cdk
Version:
AWS CDK CLI, the command line tool for CDK apps
438 lines • 67.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ParameterValues = exports.TemplateParameters = void 0;
exports.waitForChangeSet = waitForChangeSet;
exports.createDiffChangeSet = createDiffChangeSet;
exports.uploadStackTemplateAssets = uploadStackTemplateAssets;
exports.createChangeSet = createChangeSet;
exports.changeSetHasNoChanges = changeSetHasNoChanges;
exports.waitForStackDelete = waitForStackDelete;
exports.waitForStackDeploy = waitForStackDeploy;
exports.stabilizeStack = stabilizeStack;
const util_1 = require("util");
const cxapi = require("@aws-cdk/cx-api");
const cx_api_1 = require("@aws-cdk/cx-api");
const client_cloudformation_1 = require("@aws-sdk/client-cloudformation");
const cdk_assets_1 = require("cdk-assets");
const asset_manifest_builder_1 = require("./asset-manifest-builder");
const api_1 = require("../../../../@aws-cdk/tmp-toolkit-helpers/src/api");
const private_1 = require("../../../../@aws-cdk/tmp-toolkit-helpers/src/api/io/private");
const cloudformation_1 = require("../cloudformation");
/**
* Describe a changeset in CloudFormation, regardless of its current state.
*
* @param cfn a CloudFormation client
* @param stackName the name of the Stack the ChangeSet belongs to
* @param changeSetName the name of the ChangeSet
* @param fetchAll if true, fetches all pages of the change set description.
*
* @returns CloudFormation information about the ChangeSet
*/
async function describeChangeSet(cfn, stackName, changeSetName, { fetchAll }) {
const response = await cfn.describeChangeSet({
StackName: stackName,
ChangeSetName: changeSetName,
});
// If fetchAll is true, traverse all pages from the change set description.
while (fetchAll && response.NextToken != null) {
const nextPage = await cfn.describeChangeSet({
StackName: stackName,
ChangeSetName: response.ChangeSetId ?? changeSetName,
NextToken: response.NextToken,
});
// Consolidate the changes
if (nextPage.Changes != null) {
response.Changes = response.Changes != null ? response.Changes.concat(nextPage.Changes) : nextPage.Changes;
}
// Forward the new NextToken
response.NextToken = nextPage.NextToken;
}
return response;
}
/**
* Waits for a function to return non-+undefined+ before returning.
*
* @param valueProvider a function that will return a value that is not +undefined+ once the wait should be over
* @param timeout the time to wait between two calls to +valueProvider+
*
* @returns the value that was returned by +valueProvider+
*/
async function waitFor(valueProvider, timeout = 5000) {
while (true) {
const result = await valueProvider();
if (result === null) {
return undefined;
}
else if (result !== undefined) {
return result;
}
await new Promise((cb) => setTimeout(cb, timeout));
}
}
/**
* Waits for a ChangeSet to be available for triggering a StackUpdate.
*
* Will return a changeset that is either ready to be executed or has no changes.
* Will throw in other cases.
*
* @param cfn a CloudFormation client
* @param stackName the name of the Stack that the ChangeSet belongs to
* @param changeSetName the name of the ChangeSet
* @param fetchAll if true, fetches all pages of the ChangeSet before returning.
*
* @returns the CloudFormation description of the ChangeSet
*/
async function waitForChangeSet(cfn, ioHelper, stackName, changeSetName, { fetchAll }) {
await ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg((0, util_1.format)('Waiting for changeset %s on stack %s to finish creating...', changeSetName, stackName)));
const ret = await waitFor(async () => {
const description = await describeChangeSet(cfn, stackName, changeSetName, {
fetchAll,
});
// The following doesn't use a switch because tsc will not allow fall-through, UNLESS it is allows
// EVERYWHERE that uses this library directly or indirectly, which is undesirable.
if (description.Status === 'CREATE_PENDING' || description.Status === 'CREATE_IN_PROGRESS') {
await ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg((0, util_1.format)('Changeset %s on stack %s is still creating', changeSetName, stackName)));
return undefined;
}
if (description.Status === client_cloudformation_1.ChangeSetStatus.CREATE_COMPLETE || changeSetHasNoChanges(description)) {
return description;
}
// eslint-disable-next-line max-len
throw new api_1.ToolkitError(`Failed to create ChangeSet ${changeSetName} on ${stackName}: ${description.Status || 'NO_STATUS'}, ${description.StatusReason || 'no reason provided'}`);
});
if (!ret) {
throw new api_1.ToolkitError('Change set took too long to be created; aborting');
}
return ret;
}
/**
* Create a changeset for a diff operation
*/
async function createDiffChangeSet(ioHelper, options) {
// `options.stack` has been modified to include any nested stack templates directly inline with its own template, under a special `NestedTemplate` property.
// Thus the parent template's Resources section contains the nested template's CDK metadata check, which uses Fn::Equals.
// This causes CreateChangeSet to fail with `Template Error: Fn::Equals cannot be partially collapsed`.
for (const resource of Object.values(options.stack.template.Resources ?? {})) {
if (resource.Type === 'AWS::CloudFormation::Stack') {
await ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg('This stack contains one or more nested stacks, falling back to template-only diff...'));
return undefined;
}
}
return uploadBodyParameterAndCreateChangeSet(ioHelper, options);
}
/**
* Returns all file entries from an AssetManifestArtifact that look like templates.
*
* This is used in the `uploadBodyParameterAndCreateChangeSet` function to find
* all template asset files to build and publish.
*
* Returns a tuple of [AssetManifest, FileManifestEntry[]]
*/
function templatesFromAssetManifestArtifact(artifact) {
const assets = [];
const fileName = artifact.file;
const assetManifest = cdk_assets_1.AssetManifest.fromFile(fileName);
assetManifest.entries.forEach((entry) => {
if (entry.type === 'file') {
const source = entry.source;
if (source.path && source.path.endsWith('.template.json')) {
assets.push(entry);
}
}
});
return [assetManifest, assets];
}
async function uploadBodyParameterAndCreateChangeSet(ioHelper, options) {
try {
await uploadStackTemplateAssets(options.stack, options.deployments);
const env = await options.deployments.envs.accessStackForMutableStackOperations(options.stack);
const bodyParameter = await (0, cloudformation_1.makeBodyParameter)(ioHelper, options.stack, env.resolvedEnvironment, new asset_manifest_builder_1.AssetManifestBuilder(), env.resources);
const cfn = env.sdk.cloudFormation();
const exists = (await cloudformation_1.CloudFormationStack.lookup(cfn, options.stack.stackName, false)).exists;
const executionRoleArn = await env.replacePlaceholders(options.stack.cloudFormationExecutionRoleArn);
await ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_INFO.msg('Hold on while we create a read-only change set to get a diff with accurate replacement information (use --no-change-set to use a less accurate but faster template-only diff)\n'));
return await createChangeSet(ioHelper, {
cfn,
changeSetName: 'cdk-diff-change-set',
stack: options.stack,
exists,
uuid: options.uuid,
willExecute: options.willExecute,
bodyParameter,
parameters: options.parameters,
resourcesToImport: options.resourcesToImport,
role: executionRoleArn,
});
}
catch (e) {
await ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(String(e)));
await ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_INFO.msg('Could not create a change set, will base the diff on template differences (run again with -v to see the reason)\n'));
return undefined;
}
}
/**
* Uploads the assets that look like templates for this CloudFormation stack
*
* This is necessary for any CloudFormation call that needs the template, it may need
* to be uploaded to an S3 bucket first. We have to follow the instructions in the
* asset manifest, because technically that is the only place that knows about
* bucket and assumed roles and such.
*/
async function uploadStackTemplateAssets(stack, deployments) {
for (const artifact of stack.dependencies) {
// Skip artifact if it is not an Asset Manifest Artifact
if (!cxapi.AssetManifestArtifact.isAssetManifestArtifact(artifact)) {
continue;
}
const [assetManifest, file_entries] = templatesFromAssetManifestArtifact(artifact);
for (const entry of file_entries) {
await deployments.buildSingleAsset(artifact, assetManifest, entry, {
stack,
});
await deployments.publishSingleAsset(assetManifest, entry, {
stack,
});
}
}
}
async function createChangeSet(ioHelper, options) {
await cleanupOldChangeset(options.cfn, ioHelper, options.changeSetName, options.stack.stackName);
await ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`Attempting to create ChangeSet with name ${options.changeSetName} for stack ${options.stack.stackName}`));
const templateParams = TemplateParameters.fromTemplate(options.stack.template);
const stackParams = templateParams.supplyAll(options.parameters);
const changeSet = await options.cfn.createChangeSet({
StackName: options.stack.stackName,
ChangeSetName: options.changeSetName,
ChangeSetType: options.resourcesToImport ? 'IMPORT' : options.exists ? 'UPDATE' : 'CREATE',
Description: `CDK Changeset for diff ${options.uuid}`,
ClientToken: `diff${options.uuid}`,
TemplateURL: options.bodyParameter.TemplateURL,
TemplateBody: options.bodyParameter.TemplateBody,
Parameters: stackParams.apiParameters,
ResourcesToImport: options.resourcesToImport,
RoleARN: options.role,
Tags: toCfnTags(options.stack.tags),
Capabilities: ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND'],
});
await ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg((0, util_1.format)('Initiated creation of changeset: %s; waiting for it to finish creating...', changeSet.Id)));
// Fetching all pages if we'll execute, so we can have the correct change count when monitoring.
const createdChangeSet = await waitForChangeSet(options.cfn, ioHelper, options.stack.stackName, options.changeSetName, {
fetchAll: options.willExecute,
});
await cleanupOldChangeset(options.cfn, ioHelper, options.changeSetName, options.stack.stackName);
return createdChangeSet;
}
function toCfnTags(tags) {
return Object.entries(tags).map(([k, v]) => ({
Key: k,
Value: v,
}));
}
async function cleanupOldChangeset(cfn, ioHelper, changeSetName, stackName) {
// Delete any existing change sets generated by CDK since change set names must be unique.
// The delete request is successful as long as the stack exists (even if the change set does not exist).
await ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`Removing existing change set with name ${changeSetName} if it exists`));
await cfn.deleteChangeSet({
StackName: stackName,
ChangeSetName: changeSetName,
});
}
/**
* Return true if the given change set has no changes
*
* This must be determined from the status, not the 'Changes' array on the
* object; the latter can be empty because no resources were changed, but if
* there are changes to Outputs, the change set can still be executed.
*/
function changeSetHasNoChanges(description) {
const noChangeErrorPrefixes = [
// Error message for a regular template
"The submitted information didn't contain changes.",
// Error message when a Transform is involved (see #10650)
'No updates are to be performed.',
];
return (description.Status === 'FAILED' && noChangeErrorPrefixes.some((p) => (description.StatusReason ?? '').startsWith(p)));
}
/**
* Waits for a CloudFormation stack to stabilize in a complete/available state
* after a delete operation is issued.
*
* Fails if the stack is in a FAILED state. Will not fail if the stack was
* already deleted.
*
* @param cfn a CloudFormation client
* @param stackName the name of the stack to wait for after a delete
*
* @returns the CloudFormation description of the stabilized stack after the delete attempt
*/
async function waitForStackDelete(cfn, ioHelper, stackName) {
const stack = await stabilizeStack(cfn, ioHelper, stackName);
if (!stack) {
return undefined;
}
const status = stack.stackStatus;
if (status.isFailure) {
throw new api_1.ToolkitError(`The stack named ${stackName} is in a failed state. You may need to delete it from the AWS console : ${status}`);
}
else if (status.isDeleted) {
return undefined;
}
return stack;
}
/**
* Waits for a CloudFormation stack to stabilize in a complete/available state
* after an update/create operation is issued.
*
* Fails if the stack is in a FAILED state, ROLLBACK state, or DELETED state.
*
* @param cfn a CloudFormation client
* @param stackName the name of the stack to wait for after an update
*
* @returns the CloudFormation description of the stabilized stack after the update attempt
*/
async function waitForStackDeploy(cfn, ioHelper, stackName) {
const stack = await stabilizeStack(cfn, ioHelper, stackName);
if (!stack) {
return undefined;
}
const status = stack.stackStatus;
if (status.isCreationFailure) {
throw new api_1.ToolkitError(`The stack named ${stackName} failed creation, it may need to be manually deleted from the AWS console: ${status}`);
}
else if (!status.isDeploySuccess) {
throw new api_1.ToolkitError(`The stack named ${stackName} failed to deploy: ${status}`);
}
return stack;
}
/**
* Wait for a stack to become stable (no longer _IN_PROGRESS), returning it
*/
async function stabilizeStack(cfn, ioHelper, stackName) {
await ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg((0, util_1.format)('Waiting for stack %s to finish creating or updating...', stackName)));
return waitFor(async () => {
const stack = await cloudformation_1.CloudFormationStack.lookup(cfn, stackName);
if (!stack.exists) {
await ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg((0, util_1.format)('Stack %s does not exist', stackName)));
return null;
}
const status = stack.stackStatus;
if (status.isInProgress) {
await ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg((0, util_1.format)('Stack %s has an ongoing operation in progress and is not stable (%s)', stackName, status)));
return undefined;
}
else if (status.isReviewInProgress) {
// This may happen if a stack creation operation is interrupted before the ChangeSet execution starts. Recovering
// from this would requiring manual intervention (deleting or executing the pending ChangeSet), and failing to do
// so will result in an endless wait here (the ChangeSet wont delete or execute itself). Instead of blocking
// "forever" we proceed as if the stack was existing and stable. If there is a concurrent operation that just
// hasn't finished proceeding just yet, either this operation or the concurrent one may fail due to the other one
// having made progress. Which is fine. I guess.
await ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg((0, util_1.format)('Stack %s is in REVIEW_IN_PROGRESS state. Considering this is a stable status (%s)', stackName, status)));
}
return stack;
});
}
/**
* The set of (formal) parameters that have been declared in a template
*/
class TemplateParameters {
static fromTemplate(template) {
return new TemplateParameters(template.Parameters || {});
}
constructor(params) {
this.params = params;
}
/**
* Calculate stack parameters to pass from the given desired parameter values
*
* Will throw if parameters without a Default value or a Previous value are not
* supplied.
*/
supplyAll(updates) {
return new ParameterValues(this.params, updates);
}
/**
* From the template, the given desired values and the current values, calculate the changes to the stack parameters
*
* Will take into account parameters already set on the template (will emit
* 'UsePreviousValue: true' for those unless the value is changed), and will
* throw if parameters without a Default value or a Previous value are not
* supplied.
*/
updateExisting(updates, previousValues) {
return new ParameterValues(this.params, updates, previousValues);
}
}
exports.TemplateParameters = TemplateParameters;
/**
* The set of parameters we're going to pass to a Stack
*/
class ParameterValues {
constructor(formalParams, updates, previousValues = {}) {
this.formalParams = formalParams;
this.values = {};
this.apiParameters = [];
const missingRequired = new Array();
for (const [key, formalParam] of Object.entries(this.formalParams)) {
// Check updates first, then use the previous value (if available), then use
// the default (if available).
//
// If we don't find a parameter value using any of these methods, then that's an error.
const updatedValue = updates[key];
if (updatedValue !== undefined) {
this.values[key] = updatedValue;
this.apiParameters.push({
ParameterKey: key,
ParameterValue: updates[key],
});
continue;
}
if (key in previousValues) {
this.values[key] = previousValues[key];
this.apiParameters.push({ ParameterKey: key, UsePreviousValue: true });
continue;
}
if (formalParam.Default !== undefined) {
this.values[key] = formalParam.Default;
continue;
}
// Oh no
missingRequired.push(key);
}
if (missingRequired.length > 0) {
throw new api_1.ToolkitError(`The following CloudFormation Parameters are missing a value: ${missingRequired.join(', ')}`);
}
// Just append all supplied overrides that aren't really expected (this
// will fail CFN but maybe people made typos that they want to be notified
// of)
const unknownParam = ([key, _]) => this.formalParams[key] === undefined;
const hasValue = ([_, value]) => !!value;
for (const [key, value] of Object.entries(updates).filter(unknownParam).filter(hasValue)) {
this.values[key] = value;
this.apiParameters.push({ ParameterKey: key, ParameterValue: value });
}
}
/**
* Whether this set of parameter updates will change the actual stack values
*/
hasChanges(currentValues) {
// If any of the parameters are SSM parameters, deploying must always happen
// because we can't predict what the values will be. We will allow some
// parameters to opt out of this check by having a magic string in their description.
if (Object.values(this.formalParams).some((p) => p.Type.startsWith('AWS::SSM::Parameter::') && !p.Description?.includes(cx_api_1.SSMPARAM_NO_INVALIDATE))) {
return 'ssm';
}
// Otherwise we're dirty if:
// - any of the existing values are removed, or changed
if (Object.entries(currentValues).some(([key, value]) => !(key in this.values) || value !== this.values[key])) {
return true;
}
// - any of the values we're setting are new
if (Object.keys(this.values).some((key) => !(key in currentValues))) {
return true;
}
return false;
}
}
exports.ParameterValues = ParameterValues;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2ZuLWFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImNmbi1hcGkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBb0dBLDRDQWtDQztBQTRCRCxrREFnQkM7QUFpRkQsOERBaUJDO0FBRUQsMENBa0NDO0FBK0JELHNEQVdDO0FBY0QsZ0RBbUJDO0FBYUQsZ0RBcUJDO0FBS0Qsd0NBNEJDO0FBdGNELCtCQUE4QjtBQUM5Qix5Q0FBeUM7QUFDekMsNENBQXlEO0FBT3pELDBFQUV3QztBQUV4QywyQ0FBMkM7QUFDM0MscUVBQWdFO0FBRWhFLDBFQUFnRjtBQUNoRix5RkFBZ0c7QUFHaEcsc0RBQTJFO0FBRzNFOzs7Ozs7Ozs7R0FTRztBQUNILEtBQUssVUFBVSxpQkFBaUIsQ0FDOUIsR0FBMEIsRUFDMUIsU0FBaUIsRUFDakIsYUFBcUIsRUFDckIsRUFBRSxRQUFRLEVBQXlCO0lBRW5DLE1BQU0sUUFBUSxHQUFHLE1BQU0sR0FBRyxDQUFDLGlCQUFpQixDQUFDO1FBQzNDLFNBQVMsRUFBRSxTQUFTO1FBQ3BCLGFBQWEsRUFBRSxhQUFhO0tBQzdCLENBQUMsQ0FBQztJQUVILDJFQUEyRTtJQUMzRSxPQUFPLFFBQVEsSUFBSSxRQUFRLENBQUMsU0FBUyxJQUFJLElBQUksRUFBRSxDQUFDO1FBQzlDLE1BQU0sUUFBUSxHQUFHLE1BQU0sR0FBRyxDQUFDLGlCQUFpQixDQUFDO1lBQzNDLFNBQVMsRUFBRSxTQUFTO1lBQ3BCLGFBQWEsRUFBRSxRQUFRLENBQUMsV0FBVyxJQUFJLGFBQWE7WUFDcEQsU0FBUyxFQUFFLFFBQVEsQ0FBQyxTQUFTO1NBQzlCLENBQUMsQ0FBQztRQUVILDBCQUEwQjtRQUMxQixJQUFJLFFBQVEsQ0FBQyxPQUFPLElBQUksSUFBSSxFQUFFLENBQUM7WUFDN0IsUUFBUSxDQUFDLE9BQU8sR0FBRyxRQUFRLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMs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Q0FBQyxhQUFxQztRQUNyRCw0RUFBNEU7UUFDNUUsdUVBQXVFO1FBQ3ZFLHFGQUFxRjtRQUNyRixJQUNFLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDLElBQUksQ0FDbkMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLHVCQUF1QixDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsV0FBVyxFQUFFLFFBQVEsQ0FBQywrQkFBc0IsQ0FBQyxDQUN0RyxFQUNELENBQUM7WUFDRCxPQUFPLEtBQUssQ0FBQztRQUNmLENBQUM7UUFFRCw0QkFBNEI7UUFDNUIsdURBQXVEO1FBQ3ZELElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxhQUFhLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksS0FBSyxLQUFLLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxDQUFDO1lBQzlHLE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztRQUVELDRDQUE0QztRQUM1QyxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxhQUFhLENBQUMsQ0FBQyxFQUFFLENBQUM7WUFDcEUsT0FBTyxJQUFJLENBQUM7UUFDZCxDQUFDO1FBRUQsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0NBQ0Y7QUFwRkQsMENBb0ZDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZm9ybWF0IH0gZnJvbSAndXRpbCc7XG5pbXBvcnQgKiBhcyBjeGFwaSBmcm9tICdAYXdzLWNkay9jeC1hcGknO1xuaW1wb3J0IHsgU1NNUEFSQU1fTk9fSU5WQUxJREFURSB9IGZyb20gJ0Bhd3MtY2RrL2N4LWFwaSc7XG5pbXBvcnQgdHlwZSB7XG4gIERlc2NyaWJlQ2hhbmdlU2V0Q29tbWFuZE91dHB1dCxcbiAgUGFyYW1ldGVyLFxuICBSZXNvdXJjZVRvSW1wb3J0LFxuICBUYWcsXG59IGZyb20gJ0Bhd3Mtc2RrL2NsaWVudC1jbG91ZGZvcm1hdGlvbic7XG5pbXBvcnQge1xuICBDaGFuZ2VTZXRTdGF0dXMsXG59IGZyb20gJ0Bhd3Mtc2RrL2NsaWVudC1jbG91ZGZvcm1hdGlvbic7XG5pbXBvcnQgdHlwZSB7IEZpbGVNYW5pZmVzdEVudHJ5IH0gZnJvbSAnY2RrLWFzc2V0cyc7XG5pbXBvcnQgeyBBc3NldE1hbmlmZXN0IH0gZnJvbSAnY2RrLWFzc2V0cyc7XG5pbXBvcnQgeyBBc3NldE1hbmlmZXN0QnVpbGRlciB9IGZyb20gJy4vYXNzZXQtbWFuaWZlc3QtYnVpbGRlcic7XG5pbXBvcnQgdHlwZSB7IERlcGxveW1lbnRzIH0gZnJvbSAnLi9kZXBsb3ltZW50cyc7XG5pbXBvcnQgeyBUb29sa2l0RXJyb3IgfSBmcm9tICcuLi8uLi8uLi8uLi9AYXdzLWNkay90bXAtdG9vbGtpdC1oZWxwZXJzL3NyYy9hcGknO1xuaW1wb3J0IHsgSU8sIHR5cGUgSW9IZWxwZXIgfSBmcm9tICcuLi8uLi8uLi8uLi9AYXdzLWNkay90bXAtdG9vbGtpdC1oZWxwZXJzL3NyYy9hcGkvaW8vcHJpdmF0ZSc7XG5pbXBvcnQgdHlwZSB7IElDbG91ZEZvcm1hdGlvbkNsaWVudCwgU2RrUHJvdmlkZXIgfSBmcm9tICcuLi9hd3MtYXV0aCc7XG5pbXBvcnQgdHlwZSB7IFRlbXBsYXRlLCBUZW1wbGF0ZUJvZHlQYXJhbWV0ZXIsIFRlbXBsYXRlUGFyYW1ldGVyIH0gZnJvbSAnLi4vY2xvdWRmb3JtYXRpb24nO1xuaW1wb3J0IHsgQ2xvdWRGb3JtYXRpb25TdGFjaywgbWFrZUJvZHlQYXJhbWV0ZXIgfSBmcm9tICcuLi9jbG91ZGZvcm1hdGlvbic7XG5pbXBvcnQgdHlwZSB7IFJlc291cmNlc1RvSW1wb3J0IH0gZnJvbSAnLi4vcmVzb3VyY2UtaW1wb3J0JztcblxuLyoqXG4gKiBEZXNjcmliZSBhIGNoYW5nZXNldCBpbiBDbG91ZEZvcm1hdGlvbiwgcmVnYXJkbGVzcyBvZiBpdHMgY3VycmVudCBzdGF0ZS5cbiAqXG4gKiBAcGFyYW0gY2ZuICAgICAgICAgICBhIENsb3VkRm9ybWF0aW9uIGNsaWVudFxuICogQHBhcmFtIHN0YWNrTmFtZSAgICAgdGhlIG5hbWUgb2YgdGhlIFN0YWNrIHRoZSBDaGFuZ2VTZXQgYmVsb25ncyB0b1xuICogQHBhcmFtIGNoYW5nZVNldE5hbWUgdGhlIG5hbWUgb2YgdGhlIENoYW5nZVNldFxuICogQHBhcmFtIGZldGNoQWxsICAgICAgaWYgdHJ1ZSwgZmV0Y2hlcyBhbGwgcGFnZXMgb2YgdGhlIGNoYW5nZSBzZXQgZGVzY3JpcHRpb24uXG4gKlxuICogQHJldHVybnMgICAgICAgQ2xvdWRGb3JtYXRpb24gaW5mb3JtYXRpb24gYWJvdXQgdGhlIENoYW5nZVNldFxuICovXG5hc3luYyBmdW5jdGlvbiBkZXNjcmliZUNoYW5nZVNldChcbiAgY2ZuOiBJQ2xvdWRGb3JtYXRpb25DbGllbnQsXG4gIHN0YWNrTmFtZTogc3RyaW5nLFxuICBjaGFuZ2VTZXROYW1lOiBzdHJpbmcsXG4gIHsgZmV0Y2hBbGwgfTogeyBmZXRjaEFsbDogYm9vbGVhbiB9LFxuKTogUHJvbWlzZTxEZXNjcmliZUNoYW5nZVNldENvbW1hbmRPdXRwdXQ+IHtcbiAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBjZm4uZGVzY3JpYmVDaGFuZ2VTZXQoe1xuICAgIFN0YWNrTmFtZTogc3RhY2tOYW1lLFxuICAgIENoYW5nZVNldE5hbWU6IGNoYW5nZVNldE5hbWUsXG4gIH0pO1xuXG4gIC8vIElmIGZldGNoQWxsIGlzIHRydWUsIHRyYXZlcnNlIGFsbCBwYWdlcyBmcm9tIHRoZSBjaGFuZ2Ugc2V0IGRlc2NyaXB0aW9uLlxuICB3aGlsZSAoZmV0Y2hBbGwgJiYgcmVzcG9uc2UuTmV4dFRva2VuICE9IG51bGwpIHtcbiAgICBjb25zdCBuZXh0UGFnZSA9IGF3YWl0IGNmbi5kZXNjcmliZUNoYW5nZVNldCh7XG4gICAgICBTdGFja05hbWU6IHN0YWNrTmFtZSxcbiAgICAgIENoYW5nZVNldE5hbWU6IHJlc3BvbnNlLkNoYW5nZVNldElkID8/IGNoYW5nZVNldE5hbWUsXG4gICAgICBOZXh0VG9rZW46IHJlc3BvbnNlLk5leHRUb2tlbixcbiAgICB9KTtcblxuICAgIC8vIENvbnNvbGlkYXRlIHRoZSBjaGFuZ2VzXG4gICAgaWYgKG5leHRQYWdlLkNoYW5nZXMgIT0gbnVsbCkge1xuICAgICAgcmVzcG9uc2UuQ2hhbmdlcyA9IHJlc3BvbnNlLkNoYW5nZXMgIT0gbnVsbCA/IHJlc3BvbnNlLkNoYW5nZXMuY29uY2F0KG5leHRQYWdlLkNoYW5nZXMpIDogbmV4dFBhZ2UuQ2hhbmdlcztcbiAgICB9XG5cbiAgICAvLyBGb3J3YXJkIHRoZSBuZXcgTmV4dFRva2VuXG4gICAgcmVzcG9uc2UuTmV4dFRva2VuID0gbmV4dFBhZ2UuTmV4dFRva2VuO1xuICB9XG5cbiAgcmV0dXJuIHJlc3BvbnNlO1xufVxuXG4vKipcbiAqIFdhaXRzIGZvciBhIGZ1bmN0aW9uIHRvIHJldHVybiBub24tK3VuZGVmaW5lZCsgYmVmb3JlIHJldHVybmluZy5cbiAqXG4gKiBAcGFyYW0gdmFsdWVQcm92aWRlciBhIGZ1bmN0aW9uIHRoYXQgd2lsbCByZXR1cm4gYSB2YWx1ZSB0aGF0IGlzIG5vdCArdW5kZWZpbmVkKyBvbmNlIHRoZSB3YWl0IHNob3VsZCBiZSBvdmVyXG4gKiBAcGFyYW0gdGltZW91dCAgICAgdGhlIHRpbWUgdG8gd2FpdCBiZXR3ZWVuIHR3byBjYWxscyB0byArdmFsdWVQcm92aWRlcitcbiAqXG4gKiBAcmV0dXJucyAgICAgICB0aGUgdmFsdWUgdGhhdCB3YXMgcmV0dXJuZWQgYnkgK3ZhbHVlUHJvdmlkZXIrXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHdhaXRGb3I8VD4oXG4gIHZhbHVlUHJvdmlkZXI6ICgpID0+IFByb21pc2U8VCB8IG51bGwgfCB1bmRlZmluZWQ+LFxuICB0aW1lb3V0OiBudW1iZXIgPSA1MDAwLFxuKTogUHJvbWlzZTxUIHwgdW5kZWZpbmVkPiB7XG4gIHdoaWxlICh0cnVlKSB7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdmFsdWVQcm92aWRlcigpO1xuICAgIGlmIChyZXN1bHQgPT09IG51bGwpIHtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfSBlbHNlIGlmIChyZXN1bHQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9XG4gICAgYXdhaXQgbmV3IFByb21pc2UoKGNiKSA9PiBzZXRUaW1lb3V0KGNiLCB0aW1lb3V0KSk7XG4gIH1cbn1cblxuLyoqXG4gKiBXYWl0cyBmb3IgYSBDaGFuZ2VTZXQgdG8gYmUgYXZhaWxhYmxlIGZvciB0cmlnZ2VyaW5nIGEgU3RhY2tVcGRhdGUuXG4gKlxuICogV2lsbCByZXR1cm4gYSBjaGFuZ2VzZXQgdGhhdCBpcyBlaXRoZXIgcmVhZHkgdG8gYmUgZXhlY3V0ZWQgb3IgaGFzIG5vIGNoYW5nZXMuXG4gKiBXaWxsIHRocm93IGluIG90aGVyIGNhc2VzLlxuICpcbiAqIEBwYXJhbSBjZm4gICAgICAgICAgIGEgQ2xvdWRGb3JtYXRpb24gY2xpZW50XG4gKiBAcGFyYW0gc3RhY2tOYW1lICAgICB0aGUgbmFtZSBvZiB0aGUgU3RhY2sgdGhhdCB0aGUgQ2hhbmdlU2V0IGJlbG9uZ3MgdG9cbiAqIEBwYXJhbSBjaGFuZ2VTZXROYW1lIHRoZSBuYW1lIG9mIHRoZSBDaGFuZ2VTZXRcbiAqIEBwYXJhbSBmZXRjaEFsbCAgICAgIGlmIHRydWUsIGZldGNoZXMgYWxsIHBhZ2VzIG9mIHRoZSBDaGFuZ2VTZXQgYmVmb3JlIHJldHVybmluZy5cbiAqXG4gKiBAcmV0dXJucyAgICAgICB0aGUgQ2xvdWRGb3JtYXRpb24gZGVzY3JpcHRpb24gb2YgdGhlIENoYW5nZVNldFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gd2FpdEZvckNoYW5nZVNldChcbiAgY2ZuOiBJQ2xvdWRGb3JtYXRpb25DbGllbnQsXG4gIGlvSGVscGVyOiBJb0hlbHBlcixcbiAgc3RhY2tOYW1lOiBzdHJpbmcsXG4gIGNoYW5nZVNldE5hbWU6IHN0cmluZyxcbiAgeyBmZXRjaEFsbCB9OiB7IGZldGNoQWxsOiBib29sZWFuIH0sXG4pOiBQcm9taXNlPERlc2NyaWJlQ2hhbmdlU2V0Q29tbWFuZE91dHB1dD4ge1xuICBhd2FpdCBpb0hlbHBlci5ub3RpZnkoSU8uREVGQVVMVF9UT09MS0lUX0RFQlVHLm1zZyhmb3JtYXQoJ1dhaXRpbmcgZm9yIGNoYW5nZXNldCAlcyBvbiBzdGFjayAlcyB0byBmaW5pc2ggY3JlYXRpbmcuLi4nLCBjaGFuZ2VTZXROYW1lLCBzdGFja05hbWUpKSk7XG4gIGNvbnN0IHJldCA9IGF3YWl0IHdhaXRGb3IoYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGRlc2NyaXB0aW9uID0gYXdhaXQgZGVzY3JpYmVDaGFuZ2VTZXQoY2ZuLCBzdGFja05hbWUsIGNoYW5nZVNldE5hbWUsIHtcbiAgICAgIGZldGNoQWxsLFxuICAgIH0pO1xuICAgIC8vIFRoZSBmb2xsb3dpbmcgZG9lc24ndCB1c2UgYSBzd2l0Y2ggYmVjYXVzZSB0c2Mgd2lsbCBub3QgYWxsb3cgZmFsbC10aHJvdWdoLCBVTkxFU1MgaXQgaXMgYWxsb3dzXG4gICAgLy8gRVZFUllXSEVSRSB0aGF0IHVzZXMgdGhpcyBsaWJyYXJ5IGRpcmVjdGx5IG9yIGluZGlyZWN0bHksIHdoaWNoIGlzIHVuZGVzaXJhYmxlLlxuICAgIGlmIChkZXNjcmlwdGlvbi5TdGF0dXMgPT09ICdDUkVBVEVfUEVORElORycgfHwgZGVzY3JpcHRpb24uU3RhdHVzID09PSAnQ1JFQVRFX0lOX1BST0dSRVNTJykge1xuICAgICAgYXdhaXQgaW9IZWxwZXIubm90aWZ5KElPLkRFRkFVTFRfVE9PTEtJVF9ERUJVRy5tc2coZm9ybWF0KCdDaGFuZ2VzZXQgJXMgb24gc3RhY2sgJXMgaXMgc3RpbGwgY3JlYXRpbmcnLCBjaGFuZ2VTZXROYW1lLCBzdGFja05hbWUpKSk7XG4gICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIH1cblxuICAgIGlmIChkZXNjcmlwdGlvbi5TdGF0dXMgPT09IENoYW5nZVNldFN0YXR1cy5DUkVBVEVfQ09NUExFVEUgfHwgY2hhbmdlU2V0SGFzTm9DaGFuZ2VzKGRlc2NyaXB0aW9uKSkge1xuICAgICAgcmV0dXJuIGRlc2NyaXB0aW9uO1xuICAgIH1cblxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBtYXgtbGVuXG4gICAgdGhyb3cgbmV3IFRvb2xraXRFcnJvcihcbiAgICAgIGBGYWlsZWQgdG8gY3JlYXRlIENoYW5nZVNldCAke2NoYW5nZVNldE5hbWV9IG9uICR7c3RhY2tOYW1lfTogJHtkZXNjcmlwdGlvbi5TdGF0dXMgfHwgJ05PX1NUQVRVUyd9LCAke2Rlc2NyaXB0aW9uLlN0YXR1c1JlYXNvbiB8fCAnbm8gcmVhc29uIHByb3ZpZGVkJ31gLFxuICAgICk7XG4gIH0pO1xuXG4gIGlmICghcmV0KSB7XG4gICAgdGhyb3cgbmV3IFRvb2xraXRFcnJvcignQ2hhbmdlIHNldCB0b29rIHRvbyBsb25nIHRvIGJlIGNyZWF0ZWQ7IGFib3J0aW5nJyk7XG4gIH1cblxuICByZXR1cm4gcmV0O1xufVxuXG5leHBvcnQgdHlwZSBQcmVwYXJlQ2hhbmdlU2V0T3B0aW9ucyA9IHtcbiAgc3RhY2s6IGN4YXBpLkNsb3VkRm9ybWF0aW9uU3RhY2tBcnRpZmFjdDtcbiAgZGVwbG95bWVudHM6IERlcGxveW1lbnRzO1xuICB1dWlkOiBzdHJpbmc7XG4gIHdpbGxFeGVjdXRlOiBib29sZWFuO1xuICBzZGtQcm92aWRlcjogU2RrUHJvdmlkZXI7XG4gIHBhcmFtZXRlcnM6IHsgW25hbWU6IHN0cmluZ106IHN0cmluZyB8IHVuZGVmaW5lZCB9O1xuICByZXNvdXJjZXNUb0ltcG9ydD86IFJlc291cmNlc1RvSW1wb3J0O1xufVxuXG5leHBvcnQgdHlwZSBDcmVhdGVDaGFuZ2VTZXRPcHRpb25zID0ge1xuICBjZm46IElDbG91ZEZvcm1hdGlvbkNsaWVudDtcbiAgY2hhbmdlU2V0TmFtZTogc3RyaW5nO1xuICB3aWxsRXhlY3V0ZTogYm9vbGVhbjtcbiAgZXhpc3RzOiBib29sZWFuO1xuICB1dWlkOiBzdHJpbmc7XG4gIHN0YWNrOiBjeGFwaS5DbG91ZEZvcm1hdGlvblN0YWNrQXJ0aWZhY3Q7XG4gIGJvZHlQYXJhbWV0ZXI6IFRlbXBsYXRlQm9keVBhcmFtZXRlcjtcbiAgcGFyYW1ldGVyczogeyBbbmFtZTogc3RyaW5nXTogc3RyaW5nIHwgdW5kZWZpbmVkIH07XG4gIHJlc291cmNlc1RvSW1wb3J0PzogUmVzb3VyY2VUb0ltcG9ydFtdO1xuICByb2xlPzogc3RyaW5nO1xufTtcblxuLyoqXG4gKiBDcmVhdGUgYSBjaGFuZ2VzZXQgZm9yIGEgZGlmZiBvcGVyYXRpb25cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZURpZmZDaGFuZ2VTZXQoXG4gIGlvSGVscGVyOiBJb0hlbHBlcixcbiAgb3B0aW9uczogUHJlcGFyZUNoYW5nZVNldE9wdGlvbnMsXG4pOiBQcm9taXNlPERlc2NyaWJlQ2hhbmdlU2V0Q29tbWFuZE91dHB1dCB8IHVuZGVmaW5lZD4ge1xuICAvLyBgb3B0aW9ucy5zdGFja2AgaGFzIGJlZW4gbW9kaWZpZWQgdG8gaW5jbHVkZSBhbnkgbmVzdGVkIHN0YWNrIHRlbXBsYXRlcyBkaXJlY3RseSBpbmxpbmUgd2l0aCBpdHMgb3duIHRlbXBsYXRlLCB1bmRlciBhIHNwZWNpYWwgYE5lc3RlZFRlbXBsYXRlYCBwcm9wZXJ0eS5cbiAgLy8gVGh1cyB0aGUgcGFyZW50IHRlbXBsYXRlJ3MgUmVzb3VyY2VzIHNlY3Rpb24gY29udGFpbnMgdGhlIG5lc3RlZCB0ZW1wbGF0ZSdzIENESyBtZXRhZGF0YSBjaGVjaywgd2hpY2ggdXNlcyBGbjo6RXF1YWxzLlxuICAvLyBUaGlzIGNhdXNlcyBDcmVhdGVDaGFuZ2VTZXQgdG8gZmFpbCB3aXRoIGBUZW1wbGF0ZSBFcnJvcjogRm46OkVxdWFscyBjYW5ub3QgYmUgcGFydGlhbGx5IGNvbGxhcHNlZGAuXG4gIGZvciAoY29uc3QgcmVzb3VyY2Ugb2YgT2JqZWN0LnZhbHVlcyhvcHRpb25zLnN0YWNrLnRlbXBsYXRlLlJlc291cmNlcyA/PyB7fSkpIHtcbiAgICBpZiAoKHJlc291cmNlIGFzIGFueSkuVHlwZSA9PT0gJ0FXUzo6Q2xvdWRGb3JtYXRpb246OlN0YWNrJykge1xuICAgICAgYXdhaXQgaW9IZWxwZXIubm90aWZ5KElPLkRFRkFVTFRfVE9PTEtJVF9ERUJVRy5tc2coJ1RoaXMgc3RhY2sgY29udGFpbnMgb25lIG9yIG1vcmUgbmVzdGVkIHN0YWNrcywgZmFsbGluZyBiYWNrIHRvIHRlbXBsYXRlLW9ubHkgZGlmZi4uLicpKTtcblxuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG4gIH1cblxuICByZXR1cm4gdXBsb2FkQm9keVBhcmFtZXRlckFuZENyZWF0ZUNoYW5nZVNldChpb0hlbHBlciwgb3B0aW9ucyk7XG59XG5cbi8qKlxuICogUmV0dXJucyBhbGwgZmlsZSBlbnRyaWVzIGZyb20gYW4gQXNzZXRNYW5pZmVzdEFydGlmYWN0IHRoYXQgbG9vayBsaWtlIHRlbXBsYXRlcy5cbiAqXG4gKiBUaGlzIGlzIHVzZWQgaW4gdGhlIGB1cGxvYWRCb2R5UGFyYW1ldGVyQW5kQ3JlYXRlQ2hhbmdlU2V0YCBmdW5jdGlvbiB0byBmaW5kXG4gKiBhbGwgdGVtcGxhdGUgYXNzZXQgZmlsZXMgdG8gYnVpbGQgYW5kIHB1Ymxpc2guXG4gKlxuICogUmV0dXJucyBhIHR1cGxlIG9mIFtBc3NldE1hbmlmZXN0LCBGaWxlTWFuaWZlc3RFbnRyeVtdXVxuICovXG5mdW5jdGlvbiB0ZW1wbGF0ZXNGcm9tQXNzZXRNYW5pZmVzdEFydGlmYWN0KFxuICBhcnRpZmFjdDogY3hhcGkuQXNzZXRNYW5pZmVzdEFydGlmYWN0LFxuKTogW0Fzc2V0TWFuaWZlc3QsIEZpbGVNYW5pZmVzdEVudHJ5W11dIHtcbiAgY29uc3QgYXNzZXRzOiBGaWxlTWFuaWZlc3RFbnRyeVtdID0gW107XG4gIGNvbnN0IGZpbGVOYW1lID0gYXJ0aWZhY3QuZmlsZTtcbiAgY29uc3QgYXNzZXRNYW5pZmVzdCA9IEFzc2V0TWFuaWZlc3QuZnJvbUZpbGUoZmlsZU5hbWUpO1xuXG4gIGFzc2V0TWFuaWZlc3QuZW50cmllcy5mb3JFYWNoKChlbnRyeSkgPT4ge1xuICAgIGlmIChlbnRyeS50eXBlID09PSAnZmlsZScpIHtcbiAgICAgIGNvbnN0IHNvdXJjZSA9IChlbnRyeSBhcyBGaWxlTWFuaWZlc3RFbnRyeSkuc291cmNlO1xuICAgICAgaWYgKHNvdXJjZS5wYXRoICYmIHNvdXJjZS5wYXRoLmVuZHNXaXRoKCcudGVtcGxhdGUuanNvbicpKSB7XG4gICAgICAgIGFzc2V0cy5wdXNoKGVudHJ5IGFzIEZpbGVNYW5pZmVzdEVudHJ5KTtcbiAgICAgIH1cbiAgICB9XG4gIH0pO1xuICByZXR1cm4gW2Fzc2V0TWFuaWZlc3QsIGFzc2V0c107XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHVwbG9hZEJvZHlQYXJhbWV0ZXJBbmRDcmVhdGVDaGFuZ2VTZXQoXG4gIGlvSGVscGVyOiBJb0hlbHBlcixcbiAgb3B0aW9uczogUHJlcGFyZUNoYW5nZVNldE9wdGlvbnMsXG4pOiBQcm9taXNlPERlc2NyaWJlQ2hhbmdlU2V0Q29tbWFuZE91dHB1dCB8IHVuZGVmaW5lZD4ge1xuICB0cnkge1xuICAgIGF3YWl0IHVwbG9hZFN0YWNrVGVtcGxhdGVBc3NldHMob3B0aW9ucy5zdGFjaywgb3B0aW9ucy5kZXBsb3ltZW50cyk7XG4gICAgY29uc3QgZW52ID0gYXdhaXQgb3B0aW9ucy5kZXBsb3ltZW50cy5lbnZzLmFjY2Vzc1N0YWNrRm9yTXV0YWJsZVN0YWNrT3BlcmF0aW9ucyhvcHRpb25zLnN0YWNrKTtcblxuICAgIGNvbnN0IGJvZHlQYXJhbWV0ZXIgPSBhd2FpdCBtYWtlQm9keVBhcmFtZXRlcihcbiAgICAgIGlvSGVscGVyLFxuICAgICAgb3B0aW9ucy5zdGFjayxcbiAgICAgIGVudi5yZXNvbHZlZEVudmlyb25tZW50LFxuICAgICAgbmV3IEFzc2V0TWFuaWZlc3RCdWlsZGVyKCksXG4gICAgICBlbnYucmVzb3VyY2VzLFxuICAgICk7XG4gICAgY29uc3QgY2ZuID0gZW52LnNkay5jbG91ZEZvcm1hdGlvbigpO1xuICAgIGNvbnN0IGV4aXN0cyA9IChhd2FpdCBDbG91ZEZvcm1hdGlvblN0YWNrLmxvb2t1cChjZm4sIG9wdGlvbnMuc3RhY2suc3RhY2tOYW1lLCBmYWxzZSkpLmV4aXN0cztcblxuICAgIGNvbnN0IGV4ZWN1dGlvblJvbGVBcm4gPSBhd2FpdCBlbnYucmVwbGFjZVBsYWNlaG9sZGVycyhvcHRpb25zLnN0YWNrLmNsb3VkRm9ybWF0aW9uRXhlY3V0aW9uUm9sZUFybik7XG4gICAgYXdhaXQgaW9IZWxwZXIubm90aWZ5KElPLkRFRkFVTFRfVE9PTEtJVF9JTkZPLm1zZyhcbiAgICAgICdIb2xkIG9uIHdoaWxlIHdlIGNyZWF0ZSBhIHJlYWQtb25seSBjaGFuZ2Ugc2V0IHRvIGdldCBhIGRpZmYgd2l0aCBhY2N1cmF0ZSByZXBsYWNlbWVudCBpbmZvcm1hdGlvbiAodXNlIC0tbm8tY2hhbmdlLXNldCB0byB1c2UgYSBsZXNzIGFjY3VyYXRlIGJ1dCBmYXN0ZXIgdGVtcGxhdGUtb25seSBkaWZmKVxcbicsXG4gICAgKSk7XG5cbiAgICByZXR1cm4gYXdhaXQgY3JlYXRlQ2hhbmdlU2V0KGlvSGVscGVyLCB7XG4gICAgICBjZm4sXG4gICAgICBjaGFuZ2VTZXROYW1lOiAnY2RrLWRpZmYtY2hhbmdlLXNldCcsXG4gICAgICBzdGFjazogb3B0aW9ucy5zdGFjayxcbiAgICAgIGV4aXN0cyxcbiAgICAgIHV1aWQ6IG9wdGlvbnMudXVpZCxcbiAgICAgIHdpbGxFeGVjdXRlOiBvcHRpb25zLndpbGxFeGVjdXRlLFxuICAgICAgYm9keVBhcmFtZXRlcixcbiAgICAgIHBhcmFtZXRlcnM6IG9wdGlvbnMucGFyYW1ldGVycyxcbiAgICAgIHJlc291cmNlc1RvSW1wb3J0OiBvcHRpb25zLnJlc291cmNlc1RvSW1wb3J0LFxuICAgICAgcm9sZTogZXhlY3V0aW9uUm9sZUFybixcbiAgICB9KTtcbiAgfSBjYXRjaCAoZTogYW55KSB7XG4gICAgYXdhaXQgaW9IZWxwZXIubm90aWZ5KElPLkRFRkFVTFRfVE9PTEtJVF9ERUJVRy5tc2coU3RyaW5nKGUpKSk7XG4gICAgYXdhaXQgaW9IZWxwZXIubm90aWZ5KElPLkRFRkFV