aws-cdk
Version:
CDK Toolkit, the command line tool for CDK apps
285 lines • 44.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ResourceImporter = void 0;
const cfnDiff = require("@aws-cdk/cloudformation-diff");
const chalk = require("chalk");
const fs = require("fs-extra");
const promptly = require("promptly");
const logging_1 = require("./logging");
/**
* Resource importing utility class
*
* - Determines the resources added to a template (compared to the deployed version)
* - Look up the identification information
* - Load them from a file, or
* - Ask the user, based on information supplied to us by CloudFormation's GetTemplateSummary
* - Translate the input to a structure expected by CloudFormation, update the template to add the
* importable resources, then run an IMPORT changeset.
*/
class ResourceImporter {
constructor(stack, cfn) {
this.stack = stack;
this.cfn = cfn;
}
/**
* Ask the user for resources to import
*/
async askForResourceIdentifiers(available) {
const ret = { importResources: [], resourceMap: {} };
const resourceIdentifiers = await this.resourceIdentifiers();
for (const resource of available) {
const identifier = await this.askForResourceIdentifier(resourceIdentifiers, resource);
if (!identifier) {
continue;
}
ret.importResources.push(resource);
ret.resourceMap[resource.logicalId] = identifier;
}
return ret;
}
/**
* Load the resources to import from a file
*/
async loadResourceIdentifiers(available, filename) {
const contents = await fs.readJson(filename);
const ret = { importResources: [], resourceMap: {} };
for (const resource of available) {
const descr = this.describeResource(resource.logicalId);
const idProps = contents[resource.logicalId];
if (idProps) {
(0, logging_1.print)('%s: importing using %s', chalk.blue(descr), chalk.blue(fmtdict(idProps)));
ret.importResources.push(resource);
ret.resourceMap[resource.logicalId] = idProps;
delete contents[resource.logicalId];
}
else {
(0, logging_1.print)('%s: skipping', chalk.blue(descr));
}
}
const unknown = Object.keys(contents);
if (unknown.length > 0) {
(0, logging_1.warning)(`Unrecognized resource identifiers in mapping file: ${unknown.join(', ')}`);
}
return ret;
}
/**
* Based on the provided resource mapping, prepare CFN structures for import (template,
* ResourcesToImport structure) and perform the import operation (CloudFormation deployment)
*
* @param importMap Mapping from CDK construct tree path to physical resource import identifiers
* @param options Options to pass to CloudFormation deploy operation
*/
async importResources(importMap, options) {
const resourcesToImport = await this.makeResourcesToImport(importMap);
const updatedTemplate = await this.currentTemplateWithAdditions(importMap.importResources);
try {
const result = await this.cfn.deployStack({
...options,
overrideTemplate: updatedTemplate,
resourcesToImport,
});
const message = result.noOp
? ' ✅ %s (no changes)'
: ' ✅ %s';
(0, logging_1.success)('\n' + message, options.stack.displayName);
}
catch (e) {
(0, logging_1.error)('\n ❌ %s failed: %s', chalk.bold(options.stack.displayName), e);
throw e;
}
}
/**
* Perform a diff between the currently running and the new template, ensure that it is valid
* for importing and return a list of resources that are being added in the new version
*
* @return mapping logicalResourceId -> resourceDifference
*/
async discoverImportableResources(allowNonAdditions = false) {
const currentTemplate = await this.currentTemplate();
const diff = cfnDiff.diffTemplate(currentTemplate, this.stack.template);
// Ignore changes to CDKMetadata
const resourceChanges = Object.entries(diff.resources.changes)
.filter(([logicalId, _]) => logicalId !== 'CDKMetadata');
// Split the changes into additions and non-additions. Imports only make sense
// for newly-added resources.
const nonAdditions = resourceChanges.filter(([_, dif]) => !dif.isAddition);
const additions = resourceChanges.filter(([_, dif]) => dif.isAddition);
if (nonAdditions.length) {
const offendingResources = nonAdditions.map(([logId, _]) => this.describeResource(logId));
if (allowNonAdditions) {
(0, logging_1.warning)(`Ignoring updated/deleted resources (--force): ${offendingResources.join(', ')}`);
}
else {
throw new Error('No resource updates or deletes are allowed on import operation. Make sure to resolve pending changes ' +
`to existing resources, before attempting an import. Updated/deleted resources: ${offendingResources.join(', ')} (--force to override)`);
}
}
// Resources in the new template, that are not present in the current template, are a potential import candidates
return {
additions: additions.map(([logicalId, resourceDiff]) => ({
logicalId,
resourceDiff,
resourceDefinition: addDefaultDeletionPolicy(this.stack.template?.Resources?.[logicalId] ?? {}),
})),
hasNonAdditions: nonAdditions.length > 0,
};
}
/**
* Get currently deployed template of the given stack (SINGLETON)
*
* @returns Currently deployed CloudFormation template
*/
async currentTemplate() {
if (!this._currentTemplate) {
this._currentTemplate = await this.cfn.readCurrentTemplate(this.stack);
}
return this._currentTemplate;
}
/**
* Return the current template, with the given resources added to it
*/
async currentTemplateWithAdditions(additions) {
const template = await this.currentTemplate();
if (!template.Resources) {
template.Resources = {};
}
for (const add of additions) {
template.Resources[add.logicalId] = add.resourceDefinition;
}
return template;
}
/**
* Get a list of import identifiers for all resource types used in the given
* template that do support the import operation (SINGLETON)
*
* @returns a mapping from a resource type to a list of property names that together identify the resource for import
*/
async resourceIdentifiers() {
const ret = {};
const resourceIdentifierSummaries = await this.cfn.resourceIdentifierSummaries(this.stack);
for (const summary of resourceIdentifierSummaries) {
if ('ResourceType' in summary && summary.ResourceType && 'ResourceIdentifiers' in summary && summary.ResourceIdentifiers) {
ret[summary.ResourceType] = (summary.ResourceIdentifiers ?? [])?.map(x => x.split(','));
}
}
return ret;
}
/**
* Ask for the importable identifier for the given resource
*
* There may be more than one identifier under which a resource can be imported. The `import`
* operation needs exactly one of them.
*
* - If we can get one from the template, we will use one.
* - Otherwise, we will ask the user for one of them.
*/
async askForResourceIdentifier(resourceIdentifiers, chg) {
const resourceName = this.describeResource(chg.logicalId);
// Skip resources that do not support importing
const resourceType = chg.resourceDiff.newResourceType;
if (resourceType === undefined || !(resourceType in resourceIdentifiers)) {
(0, logging_1.warning)(`${resourceName}: unsupported resource type ${resourceType}, skipping import.`);
return undefined;
}
const idPropSets = resourceIdentifiers[resourceType];
// Retain only literal strings: strip potential CFN intrinsics
const resourceProps = Object.fromEntries(Object.entries(chg.resourceDefinition.Properties ?? {})
.filter(([_, v]) => typeof v === 'string'));
// Find property sets that are fully satisfied in the template, ask the user to confirm them
const satisfiedPropSets = idPropSets.filter(ps => ps.every(p => resourceProps[p]));
for (const satisfiedPropSet of satisfiedPropSets) {
const candidateProps = Object.fromEntries(satisfiedPropSet.map(p => [p, resourceProps[p]]));
const displayCandidateProps = fmtdict(candidateProps);
if (await promptly.confirm(`${chalk.blue(resourceName)} (${resourceType}): import with ${chalk.yellow(displayCandidateProps)} (yes/no) [default: yes]? `, { default: 'yes' })) {
return candidateProps;
}
}
// If we got here and the user rejected any available identifiers, then apparently they don't want the resource at all
if (satisfiedPropSets.length > 0) {
(0, logging_1.print)(chalk.grey(`Skipping import of ${resourceName}`));
return undefined;
}
// We cannot auto-import this, ask the user for one of the props
// The only difference between these cases is what we print: for multiple properties, we print a preamble
const prefix = `${chalk.blue(resourceName)} (${resourceType})`;
let preamble;
let promptPattern;
if (idPropSets.length > 1) {
preamble = `${prefix}: enter one of ${idPropSets.map(x => chalk.blue(x.join('+'))).join(', ')} to import (all empty to skip)`;
promptPattern = `${prefix}: enter %`;
}
else {
promptPattern = `${prefix}: enter %`;
}
// Do the input loop here
if (preamble) {
(0, logging_1.print)(preamble);
}
for (const idProps of idPropSets) {
const input = {};
for (const idProp of idProps) {
// If we have a value from the template, use it as default. This will only be a partial
// identifier if present, otherwise we would have done the import already above.
const defaultValue = typeof resourceProps[idProp] ?? '';
const prompt = [
promptPattern.replace(/%/g, chalk.blue(idProp)),
defaultValue
? `[${defaultValue}]`
: '(empty to skip)',
].join(' ') + ':';
const response = await promptly.prompt(prompt, { default: defaultValue, trim: true });
if (!response) {
break;
}
input[idProp] = response;
// Also stick this property into 'resourceProps', so that it may be reused by a subsequent question
// (for a different compound identifier that involves the same property). Just a small UX enhancement.
resourceProps[idProp] = response;
}
// If the user gave inputs for all values, we are complete
if (Object.keys(input).length === idProps.length) {
return input;
}
}
(0, logging_1.print)(chalk.grey(`Skipping import of ${resourceName}`));
return undefined;
}
/**
* Convert the internal "resource mapping" structure to CloudFormation accepted "ResourcesToImport" structure
*/
async makeResourcesToImport(resourceMap) {
return resourceMap.importResources.map(res => ({
LogicalResourceId: res.logicalId,
ResourceType: res.resourceDiff.newResourceType,
ResourceIdentifier: resourceMap.resourceMap[res.logicalId],
}));
}
/**
* Convert CloudFormation logical resource ID to CDK construct tree path
*
* @param logicalId CloudFormation logical ID of the resource (the key in the template's Resources section)
* @returns Forward-slash separated path of the resource in CDK construct tree, e.g. MyStack/MyBucket/Resource
*/
describeResource(logicalId) {
return this.stack.template?.Resources?.[logicalId]?.Metadata?.['aws:cdk:path'] ?? logicalId;
}
}
exports.ResourceImporter = ResourceImporter;
function fmtdict(xs) {
return Object.entries(xs).map(([k, v]) => `${k}=${v}`).join(', ');
}
/**
* Add a default `DeletionPolicy` policy.
* The default value is set to 'Retain', to lower risk of unintentionally
* deleting stateful resources in the process of importing to CDK.
*/
function addDefaultDeletionPolicy(resource) {
if (resource.DeletionPolicy) {
return resource;
}
return {
...resource,
DeletionPolicy: 'Retain',
};
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW1wb3J0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiaW1wb3J0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHdEQUF3RDtBQUd4RCwrQkFBK0I7QUFDL0IsK0JBQStCO0FBQy9CLHFDQUFxQztBQUdyQyx1Q0FBMkQ7QUFpQzNEOzs7Ozs7Ozs7R0FTRztBQUNILE1BQWEsZ0JBQWdCO0lBRzNCLFlBQ21CLEtBQXdDLEVBQ3hDLEdBQWdCO1FBRGhCLFVBQUssR0FBTCxLQUFLLENBQW1DO1FBQ3hDLFFBQUcsR0FBSCxHQUFHLENBQWE7SUFBSSxDQUFDO0lBRXhDOztPQUVHO0lBQ0ksS0FBSyxDQUFDLHlCQUF5QixDQUFDLFNBQStCO1FBQ3BFLE1BQU0sR0FBRyxHQUFjLEVBQUUsZUFBZSxFQUFFLEVBQUUsRUFBRSxXQUFXLEVBQUUsRUFBRSxFQUFFLENBQUM7UUFDaEUsTUFBTSxtQkFBbUIsR0FBRyxNQUFNLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxDQUFDO1FBRTdELEtBQUssTUFBTSxRQUFRLElBQUksU0FBUyxFQUFFO1lBQ2hDLE1BQU0sVUFBVSxHQUFHLE1BQU0sSUFBSSxDQUFDLHdCQUF3QixDQUFDLG1CQUFtQixFQUFFLFFBQVEsQ0FBQyxDQUFDO1lBQ3RGLElBQUksQ0FBQyxVQUFVLEVBQUU7Z0JBQ2YsU0FBUzthQUNWO1lBRUQsR0FBRyxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDbkMsR0FBRyxDQUFDLFdBQVcsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLEdBQUcsVUFBVSxDQUFDO1NBQ2xEO1FBRUQsT0FBTyxHQUFHLENBQUM7SUFDYixDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsdUJBQXVCLENBQUMsU0FBK0IsRUFBRSxRQUFnQjtRQUNwRixNQUFNLFFBQVEsR0FBRyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLENBQUM7UUFFN0MsTUFBTSxHQUFHLEdBQWMs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RDs7OztHQUlHO0FBQ0gsU0FBUyx3QkFBd0IsQ0FBQyxRQUFhO0lBQzdDLElBQUksUUFBUSxDQUFDLGNBQWMsRUFBRTtRQUFFLE9BQU8sUUFBUSxDQUFDO0tBQUU7SUFFakQsT0FBTztRQUNMLEdBQUcsUUFBUTtRQUNYLGNBQWMsRUFBRSxRQUFRO0tBQ3pCLENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgY2ZuRGlmZiBmcm9tICdAYXdzLWNkay9jbG91ZGZvcm1hdGlvbi1kaWZmJztcbmltcG9ydCB7IFJlc291cmNlRGlmZmVyZW5jZSB9IGZyb20gJ0Bhd3MtY2RrL2Nsb3VkZm9ybWF0aW9uLWRpZmYnO1xuaW1wb3J0ICogYXMgY3hhcGkgZnJvbSAnQGF3cy1jZGsvY3gtYXBpJztcbmltcG9ydCAqIGFzIGNoYWxrIGZyb20gJ2NoYWxrJztcbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzLWV4dHJhJztcbmltcG9ydCAqIGFzIHByb21wdGx5IGZyb20gJ3Byb21wdGx5JztcbmltcG9ydCB7IERlcGxveW1lbnRzLCBEZXBsb3lTdGFja09wdGlvbnMgfSBmcm9tICcuL2FwaS9kZXBsb3ltZW50cyc7XG5pbXBvcnQgeyBSZXNvdXJjZUlkZW50aWZpZXJQcm9wZXJ0aWVzLCBSZXNvdXJjZXNUb0ltcG9ydCB9IGZyb20gJy4vYXBpL3V0aWwvY2xvdWRmb3JtYXRpb24nO1xuaW1wb3J0IHsgZXJyb3IsIHByaW50LCBzdWNjZXNzLCB3YXJuaW5nIH0gZnJvbSAnLi9sb2dnaW5nJztcblxuLyoqXG4gKiBTZXQgb2YgcGFyYW1ldGVycyB0aGF0IHVuaXF1ZWx5IGlkZW50aWZ5IGEgcGh5c2ljYWwgcmVzb3VyY2Ugb2YgYSBnaXZlbiB0eXBlXG4gKiBmb3IgdGhlIGltcG9ydCBvcGVyYXRpb24sIGV4YW1wbGU6XG4gKlxuICogYGBgXG4gKiB7XG4gKiAgIFwiQVdTOjpTMzo6QnVja2V0XCI6IFtbXCJCdWNrZXROYW1lXCJdXSxcbiAqICAgXCJBV1M6OkR5bmFtb0RCOjpHbG9iYWxUYWJsZVwiOiBbW1wiVGFibGVOYW1lXCJdLCBbXCJUYWJsZUFyblwiXSwgW1wiVGFibGVTdHJlYW1Bcm5cIl1dLFxuICogICBcIkFXUzo6Um91dGU1Mzo6S2V5U2lnbmluZ0tleVwiOiBbW1wiSG9zdGVkWm9uZUlkXCIsIFwiTmFtZVwiXV0sXG4gKiB9XG4gKiBgYGBcbiAqL1xuZXhwb3J0IHR5cGUgUmVzb3VyY2VJZGVudGlmaWVycyA9IHsgW3Jlc291cmNlVHlwZTogc3RyaW5nXTogc3RyaW5nW11bXSB9O1xuXG4vKipcbiAqIE1hcHBpbmcgb2YgQ0RLIHJlc291cmNlcyAoTDEgY29uc3RydWN0cykgdG8gcGh5c2ljYWwgcmVzb3VyY2VzIHRvIGJlIGltcG9ydGVkXG4gKiBpbiB0aGVpciBwbGFjZSwgZXhhbXBsZTpcbiAqXG4gKiBgYGBcbiAqIHtcbiAqICAgXCJNeVN0YWNrL015UzNCdWNrZXQvUmVzb3VyY2VcIjoge1xuICogICAgIFwiQnVja2V0TmFtZVwiOiBcIm15LW1hbnVhbGx5LWNyZWF0ZWQtczMtYnVja2V0XCJcbiAqICAgfSxcbiAqICAgXCJNeVN0YWNrL015VnBjL1Jlc291cmNlXCI6IHtcbiAqICAgICBcIlZwY0lkXCI6IFwidnBjLTEyMzQ1Njc4OVwiXG4gKiAgIH1cbiAqIH1cbiAqIGBgYFxuICovXG5leHBvcnQgdHlwZSBSZXNvdXJjZU1hcCA9IHsgW2xvZ2ljYWxSZXNvdXJjZTogc3RyaW5nXTogUmVzb3VyY2VJZGVudGlmaWVyUHJvcGVydGllcyB9O1xuXG4vKipcbiAqIFJlc291cmNlIGltcG9ydGluZyB1dGlsaXR5IGNsYXNzXG4gKlxuICogLSBEZXRlcm1pbmVzIHRoZSByZXNvdXJjZXMgYWRkZWQgdG8gYSB0ZW1wbGF0ZSAoY29tcGFyZWQgdG8gdGhlIGRlcGxveWVkIHZlcnNpb24pXG4gKiAtIExvb2sgdXAgdGhlIGlkZW50aWZpY2F0aW9uIGluZm9ybWF0aW9uXG4gKiAgIC0gTG9hZCB0aGVtIGZyb20gYSBmaWxlLCBvclxuICogICAtIEFzayB0aGUgdXNlciwgYmFzZWQgb24gaW5mb3JtYXRpb24gc3VwcGxpZWQgdG8gdXMgYnkgQ2xvdWRGb3JtYXRpb24ncyBHZXRUZW1wbGF0ZVN1bW1hcnlcbiAqIC0gVHJhbnNsYXRlIHRoZSBpbnB1dCB0byBhIHN0cnVjdHVyZSBleHBlY3RlZCBieSBDbG91ZEZvcm1hdGlvbiwgdXBkYXRlIHRoZSB0ZW1wbGF0ZSB0byBhZGQgdGhlXG4gKiAgIGltcG9ydGFibGUgcmVzb3VyY2VzLCB0aGVuIHJ1biBhbiBJTVBPUlQgY2hhbmdlc2V0LlxuICovXG5leHBvcnQgY2xhc3MgUmVzb3VyY2VJbXBvcnRlciB7XG4gIHByaXZhdGUgX2N1cnJlbnRUZW1wbGF0ZTogYW55O1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgc3RhY2s6IGN4YXBpLkNsb3VkRm9ybWF0aW9uU3RhY2tBcnRpZmFjdCxcbiAgICBwcml2YXRlIHJlYWRvbmx5IGNmbjogRGVwbG95bWVudHMpIHsgfVxuXG4gIC8qKlxuICAgKiBBc2sgdGhlIHVzZXIgZm9yIHJlc291cmNlcyB0byBpbXBvcnRcbiAgICovXG4gIHB1YmxpYyBhc3luYyBhc2tGb3JSZXNvdXJjZUlkZW50aWZpZXJzKGF2YWlsYWJsZTogSW1wb3J0YWJsZVJlc291cmNlW10pOiBQcm9taXNlPEltcG9ydE1hcD4ge1xuICAgIGNvbnN0IHJldDogSW1wb3J0TWFwID0geyBpbXBvcnRSZXNvdXJjZXM6IFtdLCByZXNvdXJjZU1hcDoge30gfTtcbiAgICBjb25zdCByZXNvdXJjZUlkZW50aWZpZXJzID0gYXdhaXQgdGhpcy5yZXNvdXJjZUlkZW50aWZpZXJzKCk7XG5cbiAgICBmb3IgKGNvbnN0IHJlc291cmNlIG9mIGF2YWlsYWJsZSkge1xuICAgICAgY29uc3QgaWRlbnRpZmllciA9IGF3YWl0IHRoaXMuYXNrRm9yUmVzb3VyY2VJZGVudGlmaWVyKHJlc291cmNlSWRlbnRpZmllcnMsIHJlc291cmNlKTtcbiAgICAgIGlmICghaWRlbnRpZmllcikge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgcmV0LmltcG9ydFJlc291cmNlcy5wdXNoKHJlc291cmNlKTtcbiAgICAgIHJldC5yZXNvdXJjZU1hcFtyZXNvdXJjZS5sb2dpY2FsSWRdID0gaWRlbnRpZmllcjtcbiAgICB9XG5cbiAgICByZXR1cm4gcmV0O1xuICB9XG5cbiAgLyoqXG4gICAqIExvYWQgdGhlIHJlc291cmNlcyB0byBpbXBvcnQgZnJvbSBhIGZpbGVcbiAgICovXG4gIHB1YmxpYyBhc3luYyBsb2FkUmVzb3VyY2VJZGVudGlmaWVycyhhdmFpbGFibGU6IEltcG9ydGFibGVSZXNvdXJjZVtdLCBmaWxlbmFtZTogc3RyaW5nKTogUHJvbWlzZTxJbXBvcnRNYXA+IHtcbiAgICBjb25zdCBjb250ZW50cyA9IGF3YWl0IGZzLnJlYWRKc29uKGZpbGVuYW1lKTtcblxuICAgIGNvbnN0IHJldDogSW1wb3J0TWFwID0geyBpbXBvcnRSZXNvdXJjZXM6IFtdLCByZXNvdXJjZU1hcDoge30gfTtcbiAgICBmb3IgKGNvbnN0IHJlc291cmNlIG9mIGF2YWlsYWJsZSkge1xuICAgICAgY29uc3QgZGVzY3IgPSB0aGlzLmRlc2NyaWJlUmVzb3VyY2UocmVzb3VyY2UubG9naWNhbElkKTtcbiAgICAgIGNvbnN0IGlkUHJvcHMgPSBjb250ZW50c1tyZXNvdXJjZS5sb2dpY2FsSWRdO1xuICAgICAgaWYgKGlkUHJvcHMpIHtcbiAgICAgICAgcHJpbnQoJyVzOiBpbXBvcnRpbmcgdXNpbmcgJXMnLCBjaGFsay5ibHVlKGRlc2NyKSwgY2hhbGsuYmx1ZShmbXRkaWN0KGlkUHJvcHMpKSk7XG5cbiAgICAgICAgcmV0LmltcG9ydFJlc291cmNlcy5wdXNoKHJlc291cmNlKTtcbiAgICAgICAgcmV0LnJlc291cmNlTWFwW3Jlc291cmNlLmxvZ2ljYWxJZF0gPSBpZFByb3BzO1xuICAgICAgICBkZWxldGUgY29udGVudHNbcmVzb3VyY2UubG9naWNhbElkXTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHByaW50KCclczogc2tpcHBpbmcnLCBjaGFsay5ibHVlKGRlc2NyKSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3QgdW5rbm93biA9IE9iamVjdC5rZXlzKGNvbnRlbnRzKTtcbiAgICBpZiAodW5rbm93bi5sZW5ndGggPiAwKSB7XG4gICAgICB3YXJuaW5nKGBVbnJlY29nbml6ZWQgcmVzb3VyY2UgaWRlbnRpZmllcnMgaW4gbWFwcGluZyBmaWxlOiAke3Vua25vd24uam9pbignLCAnKX1gKTtcbiAgICB9XG5cbiAgICByZXR1cm4gcmV0O1xuICB9XG5cbiAgLyoqXG4gICAqIEJhc2VkIG9uIHRoZSBwcm92aWRlZCByZXNvdXJjZSBtYXBwaW5nLCBwcmVwYXJlIENGTiBzdHJ1Y3R1cmVzIGZvciBpbXBvcnQgKHRlbXBsYXRlLFxuICAgKiBSZXNvdXJjZXNUb0ltcG9ydCBzdHJ1Y3R1cmUpIGFuZCBwZXJmb3JtIHRoZSBpbXBvcnQgb3BlcmF0aW9uIChDbG91ZEZvcm1hdGlvbiBkZXBsb3ltZW50KVxuICAgKlxuICAgKiBAcGFyYW0gaW1wb3J0TWFwIE1hcHBpbmcgZnJvbSBDREsgY29uc3RydWN0IHRyZWUgcGF0aCB0byBwaHlzaWNhbCByZXNvdXJjZSBpbXBvcnQgaWRlbnRpZmllcnNcbiAgICogQHBhcmFtIG9wdGlvbnMgT3B0aW9ucyB0byBwYXNzIHRvIENsb3VkRm9ybWF0aW9uIGRlcGxveSBvcGVyYXRpb25cbiAgICovXG4gIHB1YmxpYyBhc3luYyBpbXBvcnRSZXNvdXJjZXMoaW1wb3J0TWFwOiBJbXBvcnRNYXAsIG9wdGlvbnM6IERlcGxveVN0YWNrT3B0aW9ucykge1xuICAgIGNvbnN0IHJlc291cmNlc1RvSW1wb3J0OiBSZXNvdXJjZXNUb0ltcG9ydCA9IGF3YWl0IHRoaXMubWFrZVJlc291cmNlc1RvSW1wb3J0KGltcG9ydE1hcCk7XG4gICAgY29uc3QgdXBkYXRlZFRlbXBsYXRlID0gYXdhaXQgdGhpcy5jdXJyZW50VGVtcGxhdGVXaXRoQWRkaXRpb25zKGltcG9ydE1hcC5pbXBvcnRSZXNvdXJjZXMpO1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMuY2ZuLmRlcGxveVN0YWNrKHtcbiAgICAgICAgLi4ub3B0aW9ucyxcbiAgICAgICAgb3ZlcnJpZGVUZW1wbGF0ZTogdXBkYXRlZFRlbXBsYXRlLFxuICAgICAgICByZXNvdXJjZXNUb0ltcG9ydCxcbiAgICAgIH0pO1xuXG4gICAgICBjb25zdCBtZXNzYWdlID0gcmVzdWx0Lm5vT3BcbiAgICAgICAgPyAnIOKchSAgJXMgKG5vIGNoYW5nZXMpJ1xuICAgICAgICA6ICcg4pyFICAlcyc7XG5cbiAgICAgIHN1Y2Nlc3MoJ1xcbicgKyBtZXNzYWdlLCBvcHRpb25zLnN0YWNrLmRpc3BsYXlOYW1lKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBlcnJvcignXFxuIOKdjCAgJXMgZmFpbGVkOiAlcycsIGNoYWxrLmJvbGQob3B0aW9ucy5zdGFjay5kaXNwbGF5TmFtZSksIGUpO1xuICAgICAgdGhyb3cgZTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUGVyZm9ybSBhIGRpZmYgYmV0d2VlbiB0aGUgY3VycmVudGx5IHJ1bm5pbmcgYW5kIHRoZSBuZXcgdGVtcGxhdGUsIGVuc3VyZSB0aGF0IGl0IGlzIHZhbGlkXG4gICAqIGZvciBpbXBvcnRpbmcgYW5kIHJldHVybiBhIGxpc3Qgb2YgcmVzb3VyY2VzIHRoYXQgYXJlIGJlaW5nIGFkZGVkIGluIHRoZSBuZXcgdmVyc2lvblxuICAgKlxuICAgKiBAcmV0dXJuIG1hcHBpbmcgbG9naWNhbFJlc291cmNlSWQgLT4gcmVzb3VyY2VEaWZmZXJlbmNlXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgZGlzY292ZXJJbXBvcnRhYmxlUmVzb3VyY2VzKGFsbG93Tm9uQWRkaXRpb25zID0gZmFsc2UpOiBQcm9taXNlPERpc2NvdmVySW1wb3J0YWJsZVJlc291cmNlc1Jlc3VsdD4ge1xuICAgIGNvbnN0IGN1cnJlbnRUZW1wbGF0ZSA9IGF3YWl0IHRoaXMuY3VycmVudFRlbXBsYXRlKCk7XG5cbiAgICBjb25zdCBkaWZmID0gY2ZuRGlmZi5kaWZmVGVtcGxhdGUoY3VycmVudFRlbXBsYXRlLCB0aGlzLnN0YWNrLnRlbXBsYXRlKTtcblxuICAgIC8vIElnbm9yZSBjaGFuZ2VzIHRvIENES01ldGFkYXRhXG4gICAgY29uc3QgcmVzb3VyY2VDaGFuZ2VzID0gT2JqZWN0LmVudHJpZXMoZGlmZi5yZXNvdXJjZXMuY2hhbmdlcylcbiAgICAgIC5maWx0ZXIoKFtsb2dpY2FsSWQsIF9dKSA9PiBsb2dpY2FsSWQgIT09ICdDREtNZXRhZGF0YScpO1xuXG4gICAgLy8gU3BsaXQgdGhlIGNoYW5nZXMgaW50byBhZGRpdGlvbnMgYW5kIG5vbi1hZGRpdGlvbnMuIEltcG9ydHMgb25seSBtYWtlIHNlbnNlXG4gICAgLy8gZm9yIG5ld2x5LWFkZGVkIHJlc291cmNlcy5cbiAgICBjb25zdCBub25BZGRpdGlvbnMgPSByZXNvdXJjZUNoYW5nZXMuZmlsdGVyKChbXywgZGlmXSkgPT4gIWRpZi5pc0FkZGl0aW9uKTtcbiAgICBjb25zdCBhZGRpdGlvbnMgPSByZXNvdXJjZUNoYW5nZXMuZmlsdGVyKChbXywgZGlmXSkgPT4gZGlmLmlzQWRkaXRpb24pO1xuXG4gICAgaWYgKG5vbkFkZGl0aW9ucy5sZW5ndGgpIHtcbiAgICAgIGNvbnN0IG9mZmVuZGluZ1Jlc291cmNlcyA9IG5vbkFkZGl0aW9ucy5tYXAoKFtsb2dJZCwgX10pID0+IHRoaXMuZGVzY3JpYmVSZXNvdXJjZShsb2dJZCkpO1xuXG4gICAgICBpZiAoYWxsb3dOb25BZGRpdGlvbnMpIHtcbiAgICAgICAgd2FybmluZyhgSWdub3JpbmcgdXBkYXRlZC9kZWxldGVkIHJlc291cmNlcyAoLS1mb3JjZSk6ICR7b2ZmZW5kaW5nUmVzb3VyY2VzLmpvaW4oJywgJyl9YCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ05vIHJlc291cmNlIHVwZGF0ZXMgb3IgZGVsZXRlcyBhcmUgYWxsb3dlZCBvbiBpbXBvcnQgb3BlcmF0aW9uLiBNYWtlIHN1cmUgdG8gcmVzb2x2ZSBwZW5kaW5nIGNoYW5nZXMgJyArXG4gICAgICAgICAgYHRvIGV4aXN0aW5nIHJlc291cmNlcywgYmVmb3JlIGF0dGVtcHRpbmcgYW4gaW1wb3J0LiBVcGRhdGVkL2RlbGV0ZWQgcmVzb3VyY2VzOiAke29mZmVuZGluZ1Jlc291cmNlcy5qb2luKCcsICcpfSAoLS1mb3JjZSB0byBvdmVycmlkZSlgKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBSZXNvdXJjZXMgaW4gdGhlIG5ldyB0ZW1wbGF0ZSwgdGhhdCBhcmUgbm90IHByZXNlbnQgaW4gdGhlIGN1cnJlbnQgdGVtcGxhdGUsIGFyZSBhIHBvdGVudGlhbCBpbXBvcnQgY2FuZGlkYXRlc1xuICAgIHJldHVybiB7XG4gICAgICBhZGRpdGlvbnM6IGFkZGl0aW9ucy5tYXAoKFtsb2dpY2FsSWQsIHJlc291cmNlRGlmZl0pID0+ICh7XG4gICAgICAgIGxvZ2ljYWxJZCxcbiAgICAgICAgcmVzb3VyY2VEaWZmLFxuICAgICAgICByZXNvdXJjZURlZmluaXRpb246IGFkZERlZmF1bHREZWxldGlvblBvbGljeSh0aGlzLnN0YWNrLnRlbXBsYXRlPy5SZXNvdXJjZXM/Lltsb2dpY2FsSWRdID8/IHt9KSxcbiAgICAgIH0pKSxcbiAgICAgIGhhc05vbkFkZGl0aW9uczogbm9uQWRkaXRpb25zLmxlbmd0aCA+IDAsXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgY3VycmVudGx5IGRlcGxveWVkIHRlbXBsYXRlIG9mIHRoZSBnaXZlbiBzdGFjayAoU0lOR0xFVE9OKVxuICAgKlxuICAgKiBAcmV0dXJucyBDdXJyZW50bHkgZGVwbG95ZWQgQ2xvdWRGb3JtYXRpb24gdGVtcGxhdGVcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgY3VycmVudFRlbXBsYXRlKCk6IFByb21pc2U8YW55PiB7XG4gICAgaWYgKCF0aGlzLl9jdXJyZW50VGVtcGxhdGUpIHtcbiAgICAgIHRoaXMuX2N1cnJlbnRUZW1wbGF0ZSA9IGF3YWl0IHRoaXMuY2ZuLnJlYWRDdXJyZW50VGVtcGxhdGUodGhpcy5zdGFjayk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl9jdXJyZW50VGVtcGxhdGU7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJuIHRoZSBjdXJyZW50IHRlbXBsYXRlLCB3aXRoIHRoZSBnaXZlbiByZXNvdXJjZXMgYWRkZWQgdG8gaXRcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgY3VycmVudFRlbXBsYXRlV2l0aEFkZGl0aW9ucyhhZGRpdGlvbnM6IEltcG9ydGFibGVSZXNvdXJjZVtdKTogUHJvbWlzZTxhbnk+IHtcbiAgICBjb25zdCB0ZW1wbGF0ZSA9IGF3YWl0IHRoaXMuY3VycmVudFRlbXBsYXRlKCk7XG4gICAgaWYgKCF0ZW1wbGF0ZS5SZXNvdXJjZXMpIHtcbiAgICAgIHRlbXBsYXRlLlJlc291cmNlcyA9IHt9O1xuICAgIH1cblxuICAgIGZvciAoY29uc3QgYWRkIG9mIGFkZGl0aW9ucykge1xuICAgICAgdGVtcGxhdGUuUmVzb3VyY2VzW2FkZC5sb2dpY2FsSWRdID0gYWRkLnJlc291cmNlRGVmaW5pdGlvbjtcbiAgICB9XG5cbiAgICByZXR1cm4gdGVtcGxhdGU7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGEgbGlzdCBvZiBpbXBvcnQgaWRlbnRpZmllcnMgZm9yIGFsbCByZXNvdXJjZSB0eXBlcyB1c2VkIGluIHRoZSBnaXZlblxuICAgKiB0ZW1wbGF0ZSB0aGF0IGRvIHN1cHBvcnQgdGhlIGltcG9ydCBvcGVyYXRpb24gKFNJTkdMRVRPTilcbiAgICpcbiAgICogQHJldHVybnMgYSBtYXBwaW5nIGZyb20gYSByZXNvdXJjZSB0eXBlIHRvIGEgbGlzdCBvZiBwcm9wZXJ0eSBuYW1lcyB0aGF0IHRvZ2V0aGVyIGlkZW50aWZ5IHRoZSByZXNvdXJjZSBmb3IgaW1wb3J0XG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHJlc291cmNlSWRlbnRpZmllcnMoKTogUHJvbWlzZTxSZXNvdXJjZUlkZW50aWZpZXJzPiB7XG4gICAgY29uc3QgcmV0OiBSZXNvdXJjZUlkZW50aWZpZXJzID0ge307XG4gICAgY29uc3QgcmVzb3VyY2VJZGVudGlmaWVyU3VtbWFyaWVzID0gYXdhaXQgdGhpcy5jZm4ucmVzb3VyY2VJZGVudGlmaWVyU3VtbWFyaWVzKHRoaXMuc3RhY2spO1xuICAgIGZvciAoY29uc3Qgc3VtbWFyeSBvZiByZXNvdXJjZUlkZW50aWZpZXJTdW1tYXJpZXMpIHtcbiAgICAgIGlmICgnUmVzb3VyY2VUeXBlJyBpbiBzdW1tYXJ5ICYmIHN1bW1hcnkuUmVzb3VyY2VUeXBlICYmICdSZXNvdXJjZUlkZW50aWZpZXJzJyBpbiBzdW1tYXJ5ICYmIHN1bW1hcnkuUmVzb3VyY2VJZGVudGlmaWVycykge1xuICAgICAgICByZXRbc3VtbWFyeS5SZXNvdXJjZVR5cGVdID0gKHN1bW1hcnkuUmVzb3VyY2VJZGVudGlmaWVycyA/PyBbXSk/Lm1hcCh4ID0+IHguc3BsaXQoJywnKSk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZXQ7XG4gIH1cblxuICAvKipcbiAgICogQXNrIGZvciB0aGUgaW1wb3J0YWJsZSBpZGVudGlmaWVyIGZvciB0aGUgZ2l2ZW4gcmVzb3VyY2VcbiAgICpcbiAgICogVGhlcmUgbWF5IGJlIG1vcmUgdGhhbiBvbmUgaWRlbnRpZmllciB1bmRlciB3aGljaCBhIHJlc291cmNlIGNhbiBiZSBpbXBvcnRlZC4gVGhlIGBpbXBvcnRgXG4gICAqIG9wZXJhdGlvbiBuZWVkcyBleGFjdGx5IG9uZSBvZiB0aGVtLlxuICAgKlxuICAgKiAtIElmIHdlIGNhbiBnZXQgb25lIGZyb20gdGhlIHRlbXBsYXRlLCB3ZSB3aWxsIHVzZSBvbmUuXG4gICAqIC0gT3RoZXJ3aXNlLCB3ZSB3aWxsIGFzayB0aGUgdXNlciBmb3Igb25lIG9mIHRoZW0uXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGFza0ZvclJlc291cmNlSWRlbnRpZmllcihcbiAgICByZXNvdXJjZUlkZW50aWZpZXJzOiBSZXNvdXJjZUlkZW50aWZpZXJzLFxuICAgIGNoZzogSW1wb3J0YWJsZVJlc291cmNlLFxuICApOiBQcm9taXNlPFJlc291cmNlSWRlbnRpZmllclByb3BlcnRpZXMgfCB1bmRlZmluZWQ+IHtcbiAgICBjb25zdCByZXNvdXJjZU5hbWUgPSB0aGlzLmRlc2NyaWJlUmVzb3VyY2UoY2hnLmxvZ2ljYWxJZCk7XG5cbiAgICAvLyBTa2lwIHJlc291cmNlcyB0aGF0IGRvIG5vdCBzdXBwb3J0IGltcG9ydGluZ1xuICAgIGNvbnN0IHJlc291cmNlVHlwZSA9IGNoZy5yZXNvdXJjZURpZmYubmV3UmVzb3VyY2VUeXBlO1xuICAgIGlmIChyZXNvdXJjZVR5cGUgPT09IHVuZGVmaW5lZCB8fCAhKHJlc291cmNlVHlwZSBpbiByZXNvdXJjZUlkZW50aWZpZXJzKSkge1xuICAgICAgd2FybmluZyhgJHtyZXNvdXJjZU5hbWV9OiB1bnN1cHBvcnRlZCByZXNvdXJjZSB0eXBlICR7cmVzb3VyY2VUeXBlfSwgc2tpcHBpbmcgaW1wb3J0LmApO1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG5cbiAgICBjb25zdCBpZFByb3BTZXRzID0gcmVzb3VyY2VJZGVudGlmaWVyc1tyZXNvdXJjZVR5cGVdO1xuXG4gICAgLy8gUmV0YWluIG9ubHkgbGl0ZXJhbCBzdHJpbmdzOiBzdHJpcCBwb3RlbnRpYWwgQ0ZOIGludHJpbnNpY3NcbiAgICBjb25zdCByZXNvdXJjZVByb3BzID0gT2JqZWN0LmZyb21FbnRyaWVzKE9iamVjdC5lbnRyaWVzKGNoZy5yZXNvdXJjZURlZmluaXRpb24uUHJvcGVydGllcyA/PyB7fSlcbiAgICAgIC5maWx0ZXIoKFtfLCB2XSkgPT4gdHlwZW9mIHYgPT09ICdzdHJpbmcnKSkgYXMgUmVjb3JkPHN0cmluZywgc3RyaW5nPjtcblxuICAgIC8vIEZpbmQgcHJvcGVydHkgc2V0cyB0aGF0IGFyZSBmdWxseSBzYXRpc2ZpZWQgaW4gdGhlIHRlbXBsYXRlLCBhc2sgdGhlIHVzZXIgdG8gY29uZmlybSB0aGVtXG4gICAgY29uc3Qgc2F0aXNmaWVkUHJvcFNldHMgPSBpZFByb3BTZXRzLmZpbHRlcihwcyA9PiBwcy5ldmVyeShwID0+IHJlc291cmNlUHJvcHNbcF0pKTtcbiAgICBmb3IgKGNvbnN0IHNhdGlzZmllZFByb3BTZXQgb2Ygc2F0aXNmaWVkUHJvcFNldHMpIHtcbiAgICAgIGNvbnN0IGNhbmRpZGF0ZVByb3BzID0gT2JqZWN0LmZyb21FbnRyaWVzKHNhdGlzZmllZFByb3BTZXQubWFwKHAgPT4gW3AsIHJlc291cmNlUHJvcHNbcF1dKSk7XG4gICAgICBjb25zdCBkaXNwbGF5Q2FuZGlkYXRlUHJvcHMgPSBmbXRkaWN0KGNhbmRpZGF0ZVByb3BzKTtcblxuICAgICAgaWYgKGF3YWl0IHByb21wdGx5LmNvbmZpcm0oXG4gICAgICAgIGAke2NoYWxrLmJsdWUocmVzb3VyY2VOYW1lKX0gKCR7cmVzb3VyY2VUeXBlfSk6IGltcG9ydCB3aXRoICR7Y2hhbGsueWVsbG93KGRpc3BsYXlDYW5kaWRhdGVQcm9wcyl9ICh5ZXMvbm8pIFtkZWZhdWx0OiB5ZXNdPyBgLFxuICAgICAgICB7IGRlZmF1bHQ6ICd5ZXMnIH0sXG4gICAgICApKSB7XG4gICAgICAgIHJldHVybiBjYW5kaWRhdGVQcm9wcztcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBJZiB3ZSBnb3QgaGVyZSBhbmQgdGhlIHVzZXIgcmVqZWN0ZWQgYW55IGF2YWlsYWJsZSBpZGVudGlmaWVycywgdGhlbiBhcHBhcmVudGx5IHRoZXkgZG9uJ3Qgd2FudCB0aGUgcmVzb3VyY2UgYXQgYWxsXG4gICAgaWYgKHNhdGlzZmllZFByb3BTZXRzLmxlbmd0aCA+IDApIHtcbiAgICAgIHByaW50KGNoYWxrLmdyZXkoYFNraXBwaW5nIGltcG9ydCBvZiAke3Jlc291cmNlTmFtZX1gKSk7XG4gICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIH1cblxuICAgIC8vIFdlIGNhbm5vdCBhdXRvLWltcG9ydCB0aGlzLCBhc2sgdGhlIHVzZXIgZm9yIG9uZSBvZiB0aGUgcHJvcHNcbiAgICAvLyBUaGUgb25seSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlc2UgY2FzZXMgaXMgd2hhdCB3ZSBwcmludDogZm9yIG11bHRpcGxlIHByb3BlcnRpZXMsIHdlIHByaW50IGEgcHJlYW1ibGVcbiAgICBjb25zdCBwcmVmaXggPSBgJHtjaGFsay5ibHVlKHJlc291cmNlTmFtZSl9ICgke3Jlc291cmNlVHlwZX0pYDtcbiAgICBsZXQgcHJlYW1ibGU7XG4gICAgbGV0IHByb21wdFBhdHRlcm47XG4gICAgaWYgKGlkUHJvcFNldHMubGVuZ3RoID4gMSkge1xuICAgICAgcHJlYW1ibGUgPSBgJHtwcmVmaXh9OiBlbnRlciBvbmUgb2YgJHtpZFByb3BTZXRzLm1hcCh4ID0+IGNoYWxrLmJsdWUoeC5qb2luKCcrJykpKS5qb2luKCcsICcpfSB0byBpbXBvcnQgKGFsbCBlbXB0eSB0byBza2lwKWA7XG4gICAgICBwcm9tcHRQYXR0ZXJuID0gYCR7cHJlZml4fTogZW50ZXIgJWA7XG4gICAgfSBlbHNlIHtcbiAgICAgIHByb21wdFBhdHRlcm4gPSBgJHtwcmVmaXh9OiBlbnRlciAlYDtcbiAgICB9XG5cbiAgICAvLyBEbyB0aGUgaW5wdXQgbG9vcCBoZXJlXG4gICAgaWYgKHByZWFtYmxlKSB7XG4gICAgICBwcmludChwcmVhbWJsZSk7XG4gICAgfVxuICAgIGZvciAoY29uc3QgaWRQcm9wcyBvZiBpZFByb3BTZXRzKSB7XG4gICAgICBjb25zdCBpbnB1dDogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHt9O1xuICAgICAgZm9yIChjb25zdCBpZFByb3Agb2YgaWRQcm9wcykge1xuICAgICAgICAvLyBJZiB3ZSBoYXZlIGEgdmFsdWUgZnJvbSB0aGUgdGVtcGxhdGUsIHVzZSBpdCBhcyBkZWZhdWx0LiBUaGlzIHdpbGwgb25seSBiZSBhIHBhcnRpYWxcbiAgICAgICAgLy8gaWRlbnRpZmllciBpZiBwcmVzZW50LCBvdGhlcndpc2Ugd2Ugd291bGQgaGF2ZSBkb25lIHRoZSBpbXBvcnQgYWxyZWFkeSBhYm92ZS5cbiAgICAgICAgY29uc3QgZGVmYXVsdFZhbHVlID0gdHlwZW9mIHJlc291cmNlUHJvcHNbaWRQcm9wXSA/PyAnJztcblxuICAgICAgICBjb25zdCBwcm9tcHQgPSBbXG4gICAgICAgICAgcHJvbXB0UGF0dGVybi5yZXBsYWNlKC8lL2csIGNoYWxrLmJsdWUoaWRQcm9wKSksXG4gICAgICAgICAgZGVmYXVsdFZhbHVlXG4gICAgICAgICAgICA/IGBbJHtkZWZhdWx0VmFsdWV9XWBcbiAgICAgICAgICAgIDogJyhlbXB0eSB0byBza2lwKScsXG4gICAgICAgIF0uam9pbignICcpICsgJzonO1xuICAgICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IHByb21wdGx5LnByb21wdChwcm9tcHQsXG4gICAgICAgICAgeyBkZWZhdWx0OiBkZWZhdWx0VmFsdWUsIHRyaW06IHRydWUgfSxcbiAgICAgICAgKTtcblxuICAgICAgICBpZiAoIXJlc3BvbnNlKSB7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cblxuICAgICAgICBpbnB1dFtpZFByb3BdID0gcmVzcG9uc2U7XG4gICAgICAgIC8vIEFsc28gc3RpY2sgdGhpcyBwcm9wZXJ0eSBpbnRvICdyZXNvdXJjZVByb3BzJywgc28gdGhhdCBpdCBtYXkgYmUgcmV1c2VkIGJ5IGEgc3Vic2VxdWVudCBxdWVzdGlvblxuICAgICAgICAvLyAoZm9yIGEgZGlmZmVyZW50IGNvbXBvdW5kIGlkZW50aWZpZXIgdGhhdCBpbnZvbHZlcyB0aGUgc2FtZSBwcm9wZXJ0eSkuIEp1c3QgYSBzbWFsbCBVWCBlbmhhbmNlbWVudC5cbiAgICAgICAgcmVzb3VyY2VQcm9wc1tpZFByb3BdID0gcmVzcG9uc2U7XG4gICAgICB9XG5cbiAgICAgIC8vIElmIHRoZSB1c2VyIGdhdmUgaW5wdXRzIGZvciBhbGwgdmFsdWVzLCB3ZSBhcmUgY29tcGxldGVcbiAgICAgIGlmIChPYmplY3Qua2V5cyhpbnB1dCkubGVuZ3RoID09PSBpZFByb3BzLmxlbmd0aCkge1xuICAgICAgICByZXR1cm4gaW5wdXQ7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcHJpbnQoY2hhbGsuZ3JleShgU2tpcHBpbmcgaW1wb3J0IG9mICR7cmVzb3VyY2VOYW1lfWApKTtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbnZlcnQgdGhlIGludGVybmFsIFwicmVzb3VyY2UgbWFwcGluZ1wiIHN0cnVjdHVyZSB0byBDbG91ZEZvcm1hdGlvbiBhY2NlcHRlZCBcIlJlc291cmNlc1RvSW1wb3J0XCIgc3RydWN0dXJlXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIG1ha2VSZXNvdXJjZXNUb0ltcG9ydChyZXNvdXJjZU1hcDogSW1wb3J0TWFwKTogUHJvbWlzZTxSZXNvdXJjZXNUb0ltcG9ydD4ge1xuICAgIHJldHVybiByZXNvdXJjZU1hcC5pbXBvcnRSZXNvdXJjZXMubWFwKHJlcyA9PiAoe1xuICAgICAgTG9naWNhbFJlc291cmNlSWQ6IHJlcy5sb2dpY2FsSWQsXG4gICAgICBSZXNvdXJjZVR5cGU6IHJlcy5yZXNvdXJjZURpZmYubmV3UmVzb3VyY2VUeXBlISxcbiAgICAgIFJlc291cmNlSWRlbnRpZmllcjogcmVzb3VyY2VNYXAucmVzb3VyY2VNYXBbcmVzLmxvZ2ljYWxJZF0sXG4gICAgfSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbnZlcnQgQ2xvdWRGb3JtYXRpb24gbG9naWNhbCByZXNvdXJjZSBJRCB0byBDREsgY29uc3RydWN0IHRyZWUgcGF0aFxuICAgKlxuICAgKiBAcGFyYW0gbG9naWNhbElkIENsb3VkRm9ybWF0aW9uIGxvZ2ljYWwgSUQgb2YgdGhlIHJlc291cmNlICh0aGUga2V5IGluIHRoZSB0ZW1wbGF0ZSdzIFJlc291cmNlcyBzZWN0aW9uKVxuICAgKiBAcmV0dXJucyBGb3J3YXJkLXNsYXNoIHNlcGFyYXRlZCBwYXRoIG9mIHRoZSByZXNvdXJjZSBpbiBDREsgY29uc3RydWN0IHRyZWUsIGUuZy4gTXlTdGFjay9NeUJ1Y2tldC9SZXNvdXJjZVxuICAgKi9cbiAgcHJpdmF0ZSBkZXNjcmliZVJlc291cmNlKGxvZ2ljYWxJZDogc3RyaW5nKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5zdGFjay50ZW1wbGF0ZT8uUmVzb3VyY2VzPy5bbG9naWNhbElkXT8uTWV0YWRhdGE/LlsnYXdzOmNkazpwYXRoJ10gPz8gbG9naWNhbElkO1xuICB9XG59XG5cbi8qKlxuICogSW5mb3JtYXRpb24gYWJvdXQgYSByZXNvdXJjZSBpbiB0aGUgdGVtcGxhdGUgdGhhdCBpcyBpbXBvcnRhYmxlXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgSW1wb3J0YWJsZVJlc291cmNlIHtcbiAgLyoqXG4gICAqIFRoZSBsb2dpY2FsIElEIG9mIHRoZSByZXNvdXJjZVxuICAgKi9cbiAgcmVhZG9ubHkgbG9naWNhbElkOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFRoZSByZXNvdXJjZSBkZWZpbml0aW9uIGluIHRoZSBuZXcgdGVtcGxhdGVcbiAgICovXG4gIHJlYWRvbmx5IHJlc291cmNlRGVmaW5pdGlvbjogYW55O1xuXG4gIC8qKlxuICAgKiBUaGUgZGlmZiBhcyByZXBvcnRlZCBieSBgY2xvdWRmb3JtYXRpb24tZGlmZmAuXG4gICAqL1xuICByZWFkb25seSByZXNvdXJjZURpZmY6IFJlc291cmNlRGlmZmVyZW5jZTtcbn1cblxuLyoqXG4gKiBUaGUgaW5mb3JtYXRpb24gbmVjZXNzYXJ5IHRvIGV4ZWN1dGUgYW4gaW1wb3J0IG9wZXJhdGlvblxuICovXG5leHBvcnQgaW50ZXJmYWNlIEltcG9ydE1hcCB7XG4gIC8qKlxuICAgKiBNYXBwaW5nIGxvZ2ljYWwgSURzIHRvIHBoeXNpY2FsIG5hbWVzXG4gICAqL1xuICByZWFkb25seSByZXNvdXJjZU1hcDogUmVzb3VyY2VNYXA7XG5cbiAgLyoqXG4gICAqIFRoZSBzZWxlY3Rpb24gb2YgcmVzb3VyY2VzIHdlIGFyZSBhY3R1YWxseSBpbXBvcnRpbmdcbiAgICpcbiAgICogRm9yIGVhY2ggb2YgdGhlIHJlc291cmNlcyBpbiB0aGlzIGxpc3QsIHRoZXJlIGlzIGEgY29ycmVzcG9uZGluZyBlbnRyeSBpblxuICAgKiB0aGUgYHJlc291cmNlTWFwYCBtYXAuXG4gICAqL1xuICByZWFkb25seSBpbXBvcnRSZXNvdXJjZXM6IEltcG9ydGFibGVSZXNvdXJjZVtdO1xufVxuXG5mdW5jdGlvbiBmbXRkaWN0PEE+KHhzOiBSZWNvcmQ8c3RyaW5nLCBBPikge1xuICByZXR1cm4gT2JqZWN0LmVudHJpZXMoeHMpLm1hcCgoW2ssIHZdKSA9PiBgJHtrfT0ke3Z9YCkuam9pbignLCAnKTtcbn1cblxuLyoqXG4gKiBBZGQgYSBkZWZhdWx0IGBEZWxldGlvblBvbGljeWAgcG9saWN5LlxuICogVGhlIGRlZmF1bHQgdmFsdWUgaXMgc2V0IHRvICdSZXRhaW4nLCB0byBsb3dlciByaXNrIG9mIHVuaW50ZW50aW9uYWxseVxuICogZGVsZXRpbmcgc3RhdGVmdWwgcmVzb3VyY2VzIGluIHRoZSBwcm9jZXNzIG9mIGltcG9ydGluZyB0byBDREsuXG4gKi9cbmZ1bmN0aW9uIGFkZERlZmF1bHREZWxldGlvblBvbGljeShyZXNvdXJjZTogYW55KTogYW55IHtcbiAgaWYgKHJlc291cmNlLkRlbGV0aW9uUG9saWN5KSB7IHJldHVybiByZXNvdXJjZTsgfVxuXG4gIHJldHVybiB7XG4gICAgLi4ucmVzb3VyY2UsXG4gICAgRGVsZXRpb25Qb2xpY3k6ICdSZXRhaW4nLFxuICB9O1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIERpc2NvdmVySW1wb3J0YWJsZVJlc291cmNlc1Jlc3VsdCB7XG4gIHJlYWRvbmx5IGFkZGl0aW9uczogSW1wb3J0YWJsZVJlc291cmNlW107XG4gIHJlYWRvbmx5IGhhc05vbkFkZGl0aW9uczogYm9vbGVhbjtcbn1cbiJdfQ==