aws-cdk
Version:
AWS CDK Toolkit CLI, the command line tool for CDK apps
842 lines • 118 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FromScan = exports.CfnTemplateGeneratorProvider = exports.FilterType = exports.ScanStatus = exports.TemplateSourceOptions = void 0;
exports.generateCdkApp = generateCdkApp;
exports.generateStack = generateStack;
exports.readFromPath = readFromPath;
exports.readFromStack = readFromStack;
exports.generateTemplate = generateTemplate;
exports.chunks = chunks;
exports.setEnvironment = setEnvironment;
exports.parseSourceOptions = parseSourceOptions;
exports.printBar = printBar;
exports.printDots = printDots;
exports.rewriteLine = rewriteLine;
exports.writeMigrateJsonFile = writeMigrateJsonFile;
exports.getMigrateScanType = getMigrateScanType;
exports.isThereAWarning = isThereAWarning;
exports.buildGeneratedTemplateOutput = buildGeneratedTemplateOutput;
exports.buildCfnClient = buildCfnClient;
exports.appendWarningsToReadme = appendWarningsToReadme;
/* eslint-disable @typescript-eslint/no-require-imports */
/* eslint-disable @typescript-eslint/no-var-requires */
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const cloud_assembly_api_1 = require("@aws-cdk/cloud-assembly-api");
const toolkit_lib_1 = require("@aws-cdk/toolkit-lib");
const cdk_from_cfn = __importStar(require("cdk-from-cfn"));
const chalk_1 = __importDefault(require("chalk"));
const init_1 = require("./init");
const cloudformation_1 = require("../api/cloudformation");
const plugin_1 = require("../api/plugin");
const tools_1 = require("../private/tools");
const camelCase = require('camelcase');
const decamelize = require('decamelize');
/** The list of languages supported by the built-in noctilucent binary. */
const MIGRATE_SUPPORTED_LANGUAGES = cdk_from_cfn.supported_languages();
/**
* Generates a CDK app from a yaml or json template.
*
* @param stackName - The name to assign to the stack in the generated app
* @param stack - The yaml or json template for the stack
* @param language - The language to generate the CDK app in
* @param outputPath - The path at which to generate the CDK app
*/
async function generateCdkApp(ioHelper, stackName, stack, language, outputPath, compress) {
const resolvedOutputPath = path.join(outputPath ?? process.cwd(), stackName);
const formattedStackName = decamelize(stackName);
try {
fs.rmSync(resolvedOutputPath, { recursive: true, force: true });
fs.mkdirSync(resolvedOutputPath, { recursive: true });
const generateOnly = compress;
await (0, init_1.cliInit)({
ioHelper,
type: 'app',
language,
canUseNetwork: true,
generateOnly,
workDir: resolvedOutputPath,
stackName,
migrate: true,
});
let stackFileName;
switch (language) {
case 'typescript':
stackFileName = `${resolvedOutputPath}/lib/${formattedStackName}-stack.ts`;
break;
case 'java':
stackFileName = `${resolvedOutputPath}/src/main/java/com/myorg/${camelCase(formattedStackName, { pascalCase: true })}Stack.java`;
break;
case 'python':
stackFileName = `${resolvedOutputPath}/${formattedStackName.replace(/-/g, '_')}/${formattedStackName.replace(/-/g, '_')}_stack.py`;
break;
case 'csharp':
stackFileName = `${resolvedOutputPath}/src/${camelCase(formattedStackName, { pascalCase: true })}/${camelCase(formattedStackName, { pascalCase: true })}Stack.cs`;
break;
case 'go':
stackFileName = `${resolvedOutputPath}/${formattedStackName}.go`;
break;
default:
throw new toolkit_lib_1.ToolkitError('UnsupportedMigrateLanguage', `${language} is not supported by CDK Migrate. Please choose from: ${MIGRATE_SUPPORTED_LANGUAGES.join(', ')}`);
}
fs.writeFileSync(stackFileName, stack);
if (compress) {
await (0, tools_1.zipDirectory)(resolvedOutputPath, `${resolvedOutputPath}.zip`);
fs.rmSync(resolvedOutputPath, { recursive: true, force: true });
}
}
catch (error) {
fs.rmSync(resolvedOutputPath, { recursive: true, force: true });
throw error;
}
}
/**
* Generates a CDK stack file.
* @param template - The template to translate into a CDK stack
* @param stackName - The name to assign to the stack
* @param language - The language to generate the stack in
* @returns A string representation of a CDK stack file
*/
function generateStack(template, stackName, language) {
const formattedStackName = `${camelCase(decamelize(stackName), { pascalCase: true })}Stack`;
try {
return cdk_from_cfn.transmute(template, language, formattedStackName);
}
catch (e) {
throw new toolkit_lib_1.ToolkitError('StackGenerationFailed', `${formattedStackName} could not be generated because ${e.message}`);
}
}
/**
* Reads and returns a stack template from a local path.
*
* @param inputPath - The location of the template
* @returns A string representation of the template if present, otherwise undefined
*/
function readFromPath(inputPath) {
let readFile;
try {
readFile = fs.readFileSync(inputPath, 'utf8');
}
catch (e) {
throw new toolkit_lib_1.ToolkitError('InvalidPath', `'${inputPath}' is not a valid path.`);
}
if (readFile == '') {
throw new toolkit_lib_1.ToolkitError('EmptyTemplateFile', `Cloudformation template filepath: '${inputPath}' is an empty file.`);
}
return readFile;
}
/**
* Reads and returns a stack template from a deployed CloudFormation stack.
*
* @param stackName - The name of the stack
* @param sdkProvider - The sdk provider for making CloudFormation calls
* @param environment - The account and region where the stack is deployed
* @returns A string representation of the template if present, otherwise undefined
*/
async function readFromStack(stackName, sdkProvider, environment) {
const cloudFormation = (await sdkProvider.forEnvironment(environment, plugin_1.Mode.ForReading)).sdk.cloudFormation();
const stack = await cloudformation_1.CloudFormationStack.lookup(cloudFormation, stackName, true);
if (stack.stackStatus.isDeploySuccess || stack.stackStatus.isRollbackSuccess) {
return JSON.stringify(await stack.template());
}
else {
throw new toolkit_lib_1.ToolkitError('UnhealthyStackStatus', `Stack '${stackName}' in account ${environment.account} and region ${environment.region} has a status of '${stack.stackStatus.name}' due to '${stack.stackStatus.reason}'. The stack cannot be migrated until it is in a healthy state.`);
}
}
/**
* Takes in a stack name and account and region and returns a generated cloudformation template using the cloudformation
* template generator.
*
* @param GenerateTemplateOptions - An object containing the stack name, filters, sdkProvider, environment, and newScan flag
* @returns a generated cloudformation template
*/
async function generateTemplate(options) {
const cfn = new CfnTemplateGeneratorProvider(await buildCfnClient(options.sdkProvider, options.environment), options.ioHelper);
const ioHelper = options.ioHelper;
const scanId = await findLastSuccessfulScan(cfn, options);
// if a customer accidentally ctrl-c's out of the command and runs it again, this will continue the progress bar where it left off
const curScan = await cfn.describeResourceScan(scanId);
if (curScan.Status == ScanStatus.IN_PROGRESS) {
await ioHelper.defaults.info('Resource scan in progress. Please wait, this can take 10 minutes or longer.');
await scanProgressBar(ioHelper, scanId, cfn);
}
await displayTimeDiff(ioHelper, new Date(), new Date(curScan.StartTime));
let resources = await cfn.listResourceScanResources(scanId, options.filters);
await ioHelper.defaults.info('finding related resources.');
let relatedResources = await cfn.getResourceScanRelatedResources(scanId, resources);
await ioHelper.defaults.info(`Found ${relatedResources.length} resources.`);
await ioHelper.defaults.info('Generating CFN template from scanned resources.');
const templateArn = (await cfn.createGeneratedTemplate(options.stackName, relatedResources)).GeneratedTemplateId;
let generatedTemplate = await cfn.describeGeneratedTemplate(templateArn);
await ioHelper.defaults.info('Please wait, template creation in progress. This may take a couple minutes.');
while (generatedTemplate.Status !== ScanStatus.COMPLETE && generatedTemplate.Status !== ScanStatus.FAILED) {
await printDots(`[${generatedTemplate.Status}] Template Creation in Progress`, 400);
generatedTemplate = await cfn.describeGeneratedTemplate(templateArn);
}
await ioHelper.defaults.info('\nTemplate successfully generated!');
return buildGeneratedTemplateOutput(generatedTemplate, (await cfn.getGeneratedTemplate(templateArn)).TemplateBody, templateArn);
}
async function findLastSuccessfulScan(cfn, options) {
const ioHelper = options.ioHelper;
let resourceScanSummaries = [];
const clientRequestToken = `cdk-migrate-${options.environment.account}-${options.environment.region}`;
if (options.fromScan === FromScan.NEW) {
await ioHelper.defaults.info(`Starting new scan for account ${options.environment.account} in region ${options.environment.region}`);
try {
await cfn.startResourceScan(clientRequestToken);
resourceScanSummaries = (await cfn.listResourceScans()).ResourceScanSummaries;
}
catch (e) {
// continuing here because if the scan fails on a new-scan it is very likely because there is either already a scan in progress
// or the customer hit a rate limit. In either case we want to continue with the most recent scan.
// If this happens to fail for a credential error then that will be caught immediately after anyway.
await ioHelper.defaults.info(`Scan failed to start due to error '${e.message}', defaulting to latest scan.`);
}
}
else {
resourceScanSummaries = (await cfn.listResourceScans()).ResourceScanSummaries;
await cfn.checkForResourceScan(resourceScanSummaries, options, clientRequestToken);
}
// get the latest scan, which we know will exist
resourceScanSummaries = (await cfn.listResourceScans()).ResourceScanSummaries;
let scanId = resourceScanSummaries[0].ResourceScanId;
// find the most recent scan that isn't in a failed state in case we didn't start a new one
for (const summary of resourceScanSummaries) {
if (summary.Status !== ScanStatus.FAILED) {
scanId = summary.ResourceScanId;
break;
}
}
return scanId;
}
/**
* Takes a string of filters in the format of key1=value1,key2=value2 and returns a map of the filters.
*
* @param filters - a string of filters in the format of key1=value1,key2=value2
* @returns a map of the filters
*/
function parseFilters(filters) {
if (!filters) {
return {
'resource-identifier': undefined,
'resource-type-prefix': undefined,
'tag-key': undefined,
'tag-value': undefined,
};
}
const filterShorthands = {
'identifier': FilterType.RESOURCE_IDENTIFIER,
'id': FilterType.RESOURCE_IDENTIFIER,
'type': FilterType.RESOURCE_TYPE_PREFIX,
'type-prefix': FilterType.RESOURCE_TYPE_PREFIX,
};
const filterList = filters.split(',');
let filterMap = {
[FilterType.RESOURCE_IDENTIFIER]: undefined,
[FilterType.RESOURCE_TYPE_PREFIX]: undefined,
[FilterType.TAG_KEY]: undefined,
[FilterType.TAG_VALUE]: undefined,
};
for (const fil of filterList) {
const filter = fil.split('=');
let filterKey = filter[0];
const filterValue = filter[1];
// if the key is a shorthand, replace it with the full name
if (filterKey in filterShorthands) {
filterKey = filterShorthands[filterKey];
}
if (Object.values(FilterType).includes(filterKey)) {
filterMap[filterKey] = filterValue;
}
else {
throw new toolkit_lib_1.ToolkitError('InvalidFilter', `Invalid filter: ${filterKey}`);
}
}
return filterMap;
}
/**
* Takes a list of any type and breaks it up into chunks of a specified size.
*
* @param list - The list to break up
* @param chunkSize - The size of each chunk
* @returns A list of lists of the specified size
*/
function chunks(list, chunkSize) {
const chunkedList = [];
for (let i = 0; i < list.length; i += chunkSize) {
chunkedList.push(list.slice(i, i + chunkSize));
}
return chunkedList;
}
/**
* Sets the account and region for making CloudFormation calls.
* @param account - The account to use
* @param region - The region to use
* @returns The environment object
*/
function setEnvironment(account, region) {
return {
account: account ?? cloud_assembly_api_1.UNKNOWN_ACCOUNT,
region: region ?? cloud_assembly_api_1.UNKNOWN_REGION,
name: 'cdk-migrate-env',
};
}
/**
* Enum for the source options for the template
*/
var TemplateSourceOptions;
(function (TemplateSourceOptions) {
TemplateSourceOptions["PATH"] = "path";
TemplateSourceOptions["STACK"] = "stack";
TemplateSourceOptions["SCAN"] = "scan";
})(TemplateSourceOptions || (exports.TemplateSourceOptions = TemplateSourceOptions = {}));
/**
* Enum for the status of a resource scan
*/
var ScanStatus;
(function (ScanStatus) {
ScanStatus["IN_PROGRESS"] = "IN_PROGRESS";
ScanStatus["COMPLETE"] = "COMPLETE";
ScanStatus["FAILED"] = "FAILED";
})(ScanStatus || (exports.ScanStatus = ScanStatus = {}));
var FilterType;
(function (FilterType) {
FilterType["RESOURCE_IDENTIFIER"] = "resource-identifier";
FilterType["RESOURCE_TYPE_PREFIX"] = "resource-type-prefix";
FilterType["TAG_KEY"] = "tag-key";
FilterType["TAG_VALUE"] = "tag-value";
})(FilterType || (exports.FilterType = FilterType = {}));
/**
* Validates that exactly one source option has been provided.
* @param fromPath - The content of the flag `--from-path`
* @param fromStack - the content of the flag `--from-stack`
*/
function parseSourceOptions(fromPath, fromStack, stackName) {
if (fromPath && fromStack) {
throw new toolkit_lib_1.ToolkitError('ConflictingSourceOptions', 'Only one of `--from-path` or `--from-stack` may be provided.');
}
if (!stackName) {
throw new toolkit_lib_1.ToolkitError('MissingStackName', '`--stack-name` is a required field.');
}
if (!fromPath && !fromStack) {
return { source: TemplateSourceOptions.SCAN };
}
if (fromPath) {
return { source: TemplateSourceOptions.PATH, templatePath: fromPath };
}
return { source: TemplateSourceOptions.STACK, stackName: stackName };
}
/**
* Takes a set of resources and removes any with the managedbystack flag set to true.
*
* @param resourceList - the list of resources provided by the list scanned resources calls
* @returns a list of resources not managed by cfn stacks
*/
function excludeManaged(resourceList) {
return resourceList
.filter((r) => !r.ManagedByStack)
.map((r) => ({
ResourceType: r.ResourceType,
ResourceIdentifier: r.ResourceIdentifier,
}));
}
/**
* Transforms a list of resources into a list of resource identifiers by removing the ManagedByStack flag.
* Setting the value of the field to undefined effectively removes it from the object.
*
* @param resourceList - the list of resources provided by the list scanned resources calls
* @returns a list of ScannedResourceIdentifier[]
*/
function resourceIdentifiers(resourceList) {
const identifiers = [];
resourceList.forEach((r) => {
const identifier = {
ResourceType: r.ResourceType,
ResourceIdentifier: r.ResourceIdentifier,
};
identifiers.push(identifier);
});
return identifiers;
}
/**
* Takes a scan id and maintains a progress bar to display the progress of a scan to the user.
*
* @param scanId - A string representing the scan id
* @param cloudFormation - The CloudFormation sdk client to use
*/
async function scanProgressBar(ioHelper, scanId, cfn) {
let curProgress = 0.5;
// we know it's in progress initially since we wouldn't have gotten here if it wasn't
let curScan = {
Status: ScanStatus.IN_PROGRESS,
$metadata: {},
};
while (curScan.Status == ScanStatus.IN_PROGRESS) {
curScan = await cfn.describeResourceScan(scanId);
curProgress = curScan.PercentageCompleted ?? curProgress;
printBar(30, curProgress);
await new Promise((resolve) => setTimeout(resolve, 2000));
}
await ioHelper.defaults.info('\n✅ Scan Complete!');
}
/**
* Prints a progress bar to the console. To be used in a while loop to show progress of a long running task.
* The progress bar deletes the current line on the console and rewrites it with the progress amount.
*
* @param width - The width of the progress bar
* @param progress - The current progress to display as a percentage of 100
*/
function printBar(width, progress) {
if (!process.env.MIGRATE_INTEG_TEST) {
const FULL_BLOCK = '█';
const PARTIAL_BLOCK = ['', '▏', '▎', '▍', '▌', '▋', '▊', '▉'];
const fraction = Math.min(progress / 100, 1);
const innerWidth = Math.max(1, width - 2);
const chars = innerWidth * fraction;
const remainder = chars - Math.floor(chars);
const fullChars = FULL_BLOCK.repeat(Math.floor(chars));
const partialChar = PARTIAL_BLOCK[Math.floor(remainder * PARTIAL_BLOCK.length)];
const filler = '·'.repeat(innerWidth - Math.floor(chars) - (partialChar ? 1 : 0));
const color = chalk_1.default.green;
rewriteLine('[' + color(fullChars + partialChar) + filler + `] (${progress}%)`);
}
}
/**
* Prints a message to the console with a series periods appended to it. To be used in a while loop to show progress of a long running task.
* The message deletes the current line and rewrites it several times to display 1-3 periods to show the user that the task is still running.
*
* @param message - The message to display
* @param timeoutx4 - The amount of time to wait before printing the next period
*/
async function printDots(message, timeoutx4) {
if (!process.env.MIGRATE_INTEG_TEST) {
rewriteLine(message + ' .');
await new Promise((resolve) => setTimeout(resolve, timeoutx4));
rewriteLine(message + ' ..');
await new Promise((resolve) => setTimeout(resolve, timeoutx4));
rewriteLine(message + ' ...');
await new Promise((resolve) => setTimeout(resolve, timeoutx4));
rewriteLine(message);
await new Promise((resolve) => setTimeout(resolve, timeoutx4));
}
}
/**
* Rewrites the current line on the console and writes a new message to it.
* This is a helper funciton for printDots and printBar.
*
* @param message - The message to display
*/
function rewriteLine(message) {
process.stdout.clearLine(0);
process.stdout.cursorTo(0);
process.stdout.write(message);
}
/**
* Prints the time difference between two dates in days, hours, and minutes.
*
* @param time1 - The first date to compare
* @param time2 - The second date to compare
*/
async function displayTimeDiff(ioHelper, time1, time2) {
const diff = Math.abs(time1.getTime() - time2.getTime());
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
await ioHelper.defaults.info(`Using the latest successful scan which is ${days} days, ${hours} hours, and ${minutes} minutes old.`);
}
/**
* Writes a migrate.json file to the output directory.
*
* @param outputPath - The path to write the migrate.json file to
* @param stackName - The name of the stack
* @param generatedOutput - The output of the template generator
*/
function writeMigrateJsonFile(outputPath, stackName, migrateJson) {
const outputToJson = {
'//': 'This file is generated by cdk migrate. It will be automatically deleted after the first successful deployment of this app to the environment of the original resources.',
'Source': migrateJson.source,
'Resources': migrateJson.resources,
};
fs.writeFileSync(`${path.join(outputPath ?? process.cwd(), stackName)}/migrate.json`, JSON.stringify(outputToJson, null, 2));
}
/**
* Takes a string representing the from-scan flag and returns a FromScan enum value.
*
* @param scanType - A string representing the from-scan flag
* @returns A FromScan enum value
*/
function getMigrateScanType(scanType) {
switch (scanType) {
case 'new':
return FromScan.NEW;
case 'most-recent':
return FromScan.MOST_RECENT;
case '':
return FromScan.DEFAULT;
case undefined:
return FromScan.DEFAULT;
default:
throw new toolkit_lib_1.ToolkitError('UnknownScanType', `Unknown scan type: ${scanType}`);
}
}
/**
* Takes a generatedTemplateOutput objct and returns a boolean representing whether there are any warnings on any rescources.
*
* @param generatedTemplateOutput - A GenerateTemplateOutput object
* @returns A boolean representing whether there are any warnings on any rescources
*/
function isThereAWarning(generatedTemplateOutput) {
if (generatedTemplateOutput.resources) {
for (const resource of generatedTemplateOutput.resources) {
if (resource.Warnings && resource.Warnings.length > 0) {
return true;
}
}
}
return false;
}
/**
* Builds the GenerateTemplateOutput object from the DescribeGeneratedTemplateOutput and the template body.
*
* @param generatedTemplateSummary - The output of the describe generated template call
* @param templateBody - The body of the generated template
* @returns A GenerateTemplateOutput object
*/
function buildGeneratedTemplateOutput(generatedTemplateSummary, templateBody, source) {
const resources = generatedTemplateSummary.Resources;
const migrateJson = {
templateBody: templateBody,
source: source,
resources: generatedTemplateSummary.Resources.map((r) => ({
ResourceType: r.ResourceType,
LogicalResourceId: r.LogicalResourceId,
ResourceIdentifier: r.ResourceIdentifier,
})),
};
const templateId = generatedTemplateSummary.GeneratedTemplateId;
return {
migrateJson: migrateJson,
resources: resources,
templateId: templateId,
};
}
/**
* Builds a CloudFormation sdk client for making requests with the CFN template generator.
*
* @param sdkProvider - The sdk provider for making CloudFormation calls
* @param environment - The account and region where the stack is deployed
* @returns A CloudFormation sdk client
*/
async function buildCfnClient(sdkProvider, environment) {
const sdk = (await sdkProvider.forEnvironment(environment, plugin_1.Mode.ForReading)).sdk;
sdk.appendCustomUserAgent('cdk-migrate');
return sdk.cloudFormation();
}
/**
* Appends a list of warnings to a readme file.
*
* @param filepath - The path to the readme file
* @param resources - A list of resources to append warnings for
*/
function appendWarningsToReadme(filepath, resources) {
const readme = fs.readFileSync(filepath, 'utf8');
const lines = readme.split('\n');
const index = lines.findIndex((line) => line.trim() === 'Enjoy!');
let linesToAdd = ['\n## Warnings'];
linesToAdd.push('### Write-only properties');
linesToAdd.push("Write-only properties are resource property values that can be written to but can't be read by AWS CloudFormation or CDK Migrate. For more information, see [IaC generator and write-only properties](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/generate-IaC-write-only-properties.html).");
linesToAdd.push('\n');
linesToAdd.push('Write-only properties discovered during migration are organized here by resource ID and categorized by write-only property type. Resolve write-only properties by providing property values in your CDK app. For guidance, see [Resolve write-only properties](https://docs.aws.amazon.com/cdk/v2/guide/migrate.html#migrate-resources-writeonly).');
for (const resource of resources) {
if (resource.Warnings && resource.Warnings.length > 0) {
linesToAdd.push(`### ${resource.LogicalResourceId}`);
for (const warning of resource.Warnings) {
linesToAdd.push(`- **${warning.Type}**: `);
for (const property of warning.Properties) {
linesToAdd.push(` - ${property.PropertyPath}: ${property.Description}`);
}
}
}
}
lines.splice(index, 0, ...linesToAdd);
fs.writeFileSync(filepath, lines.join('\n'));
}
/**
* takes a list of resources and returns a list of unique resources based on the resource type and logical resource id.
*
* @param resources - A list of resources to deduplicate
* @returns A list of unique resources
*/
function deduplicateResources(resources) {
let uniqueResources = {};
for (const resource of resources) {
// Creating our unique identifier using the resource type and the full resource identifier.
// The resource identifier can be made up of more than one key/value pair (e.g. a resource
// like AWS::Route53::KeySigningKey is identified by the pair HostedZoneId + Name), so every
// key must be included -- using only the first key would incorrectly collapse distinct
// resources that merely share the value of their first identifier component.
const sortedIdentifierEntries = Object.entries(resource.ResourceIdentifier).sort(([a], [b]) => a.localeCompare(b));
const uniqueIdentifer = `${resource.ResourceType}:${sortedIdentifierEntries.map(([k, v]) => `${k}=${v}`).join(',')}`;
uniqueResources[uniqueIdentifer] = resource;
}
return Object.values(uniqueResources);
}
/**
* Class for making CloudFormation template generator calls
*/
class CfnTemplateGeneratorProvider {
cfn;
ioHelper;
constructor(cfn, ioHelper) {
this.cfn = cfn;
this.ioHelper = ioHelper;
}
async checkForResourceScan(resourceScanSummaries, options, clientRequestToken) {
if (!resourceScanSummaries || resourceScanSummaries.length === 0) {
if (options.fromScan === FromScan.MOST_RECENT) {
throw new toolkit_lib_1.ToolkitError('NoScansFound', 'No scans found. Please either start a new scan with the `--from-scan` new or do not specify a `--from-scan` option.');
}
else {
await this.ioHelper.defaults.info('No scans found. Initiating a new resource scan.');
await this.startResourceScan(clientRequestToken);
}
}
}
/**
* Retrieves a tokenized list of resources and their associated scan. If a token is present the function
* will loop through all pages and combine them into a single list of ScannedRelatedResources
*
* @param scanId - scan id for the to list resources for
* @param resources - A list of resources to find related resources for
*/
async getResourceScanRelatedResources(scanId, resources) {
let relatedResourceList = resources;
// break the list of resources into chunks of 100 to avoid hitting the 100 resource limit
for (const chunk of chunks(resources, 100)) {
// get the first page of related resources
const res = await this.cfn.listResourceScanRelatedResources({
ResourceScanId: scanId,
Resources: chunk,
});
// add the first page to the list
relatedResourceList.push(...(res.RelatedResources ?? []));
let nextToken = res.NextToken;
// if there are more pages, cycle through them and add them to the list before moving on to the next chunk
while (nextToken) {
const nextRelatedResources = await this.cfn.listResourceScanRelatedResources({
ResourceScanId: scanId,
Resources: resourceIdentifiers(resources),
NextToken: nextToken,
});
nextToken = nextRelatedResources.NextToken;
relatedResourceList.push(...(nextRelatedResources.RelatedResources ?? []));
}
}
relatedResourceList = deduplicateResources(relatedResourceList);
// prune the managedbystack flag off of them again.
return process.env.MIGRATE_INTEG_TEST
? resourceIdentifiers(relatedResourceList)
: resourceIdentifiers(excludeManaged(relatedResourceList));
}
/**
* Kicks off a scan of a customers account, returning the scan id. A scan can take
* 10 minutes or longer to complete. However this will return a scan id as soon as
* the scan has begun.
*
* @returns A string representing the scan id
*/
async startResourceScan(requestToken) {
return (await this.cfn.startResourceScan({
ClientRequestToken: requestToken,
})).ResourceScanId;
}
/**
* Gets the most recent scans a customer has completed
*
* @returns a list of resource scan summaries
*/
async listResourceScans() {
return this.cfn.listResourceScans();
}
/**
* Retrieves a tokenized list of resources from a resource scan. If a token is present, this function
* will loop through all pages and combine them into a single list of ScannedResource[].
* Additionally will apply any filters provided by the customer.
*
* @param scanId - scan id for the to list resources for
* @param filters - a string of filters in the format of key1=value1,key2=value2
* @returns a combined list of all resources from the scan
*/
async listResourceScanResources(scanId, filters = []) {
let resourceList = [];
let resourceScanInputs;
if (filters.length > 0) {
await this.ioHelper.defaults.info('Applying filters to resource scan.');
for (const filter of filters) {
const filterList = parseFilters(filter);
resourceScanInputs = {
ResourceScanId: scanId,
ResourceIdentifier: filterList[FilterType.RESOURCE_IDENTIFIER],
ResourceTypePrefix: filterList[FilterType.RESOURCE_TYPE_PREFIX],
TagKey: filterList[FilterType.TAG_KEY],
TagValue: filterList[FilterType.TAG_VALUE],
};
const resources = await this.cfn.listResourceScanResources(resourceScanInputs);
resourceList = resourceList.concat(resources.Resources ?? []);
let nextToken = resources.NextToken;
// cycle through the pages adding all resources to the list until we run out of pages
while (nextToken) {
resourceScanInputs.NextToken = nextToken;
const nextResources = await this.cfn.listResourceScanResources(resourceScanInputs);
nextToken = nextResources.NextToken;
resourceList = resourceList.concat(nextResources.Resources ?? []);
}
}
}
else {
await this.ioHelper.defaults.info('No filters provided. Retrieving all resources from scan.');
resourceScanInputs = {
ResourceScanId: scanId,
};
const resources = await this.cfn.listResourceScanResources(resourceScanInputs);
resourceList = resourceList.concat(resources.Resources ?? []);
let nextToken = resources.NextToken;
// cycle through the pages adding all resources to the list until we run out of pages
while (nextToken) {
resourceScanInputs.NextToken = nextToken;
const nextResources = await this.cfn.listResourceScanResources(resourceScanInputs);
nextToken = nextResources.NextToken;
resourceList = resourceList.concat(nextResources.Resources ?? []);
}
}
if (resourceList.length === 0) {
throw new toolkit_lib_1.ToolkitError('NoResourcesFound', `No resources found with filters ${filters.join(' ')}. Please try again with different filters.`);
}
resourceList = deduplicateResources(resourceList);
return process.env.MIGRATE_INTEG_TEST
? resourceIdentifiers(resourceList)
: resourceIdentifiers(excludeManaged(resourceList));
}
/**
* Retrieves information about a resource scan.
*
* @param scanId - scan id for the to list resources for
* @returns information about the scan
*/
async describeResourceScan(scanId) {
return this.cfn.describeResourceScan({
ResourceScanId: scanId,
});
}
/**
* Describes the current status of the template being generated.
*
* @param templateId - A string representing the template id
* @returns DescribeGeneratedTemplateOutput an object containing the template status and results
*/
async describeGeneratedTemplate(templateId) {
const generatedTemplate = await this.cfn.describeGeneratedTemplate({
GeneratedTemplateName: templateId,
});
if (generatedTemplate.Status == ScanStatus.FAILED) {
throw new toolkit_lib_1.ToolkitError('TemplateGenerationFailed', generatedTemplate.StatusReason);
}
return generatedTemplate;
}
/**
* Retrieves a completed generated cloudformation template from the template generator.
*
* @param templateId - A string representing the template id
* @param cloudFormation - The CloudFormation sdk client to use
* @returns DescribeGeneratedTemplateOutput an object containing the template status and body
*/
async getGeneratedTemplate(templateId) {
return this.cfn.getGeneratedTemplate({
GeneratedTemplateName: templateId,
});
}
/**
* Kicks off a template generation for a set of resources.
*
* @param stackName - The name of the stack
* @param resources - A list of resources to generate the template from
* @returns CreateGeneratedTemplateOutput an object containing the template arn to query on later
*/
async createGeneratedTemplate(stackName, resources) {
const createTemplateOutput = await this.cfn.createGeneratedTemplate({
Resources: resources,
GeneratedTemplateName: stackName,
});
if (createTemplateOutput.GeneratedTemplateId === undefined) {
throw new toolkit_lib_1.ToolkitError('MissingTemplateArn', 'CreateGeneratedTemplate failed to return an Arn.');
}
return createTemplateOutput;
}
/**
* Deletes a generated template from the template generator.
*
* @param templateArn - The arn of the template to delete
* @returns A promise that resolves when the template has been deleted
*/
async deleteGeneratedTemplate(templateArn) {
await this.cfn.deleteGeneratedTemplate({
GeneratedTemplateName: templateArn,
});
}
}
exports.CfnTemplateGeneratorProvider = CfnTemplateGeneratorProvider;
/**
* The possible ways to choose a scan to generate a CDK application from
*/
var FromScan;
(function (FromScan) {
/**
* Initiate a new resource scan to build the CDK application from.
*/
FromScan[FromScan["NEW"] = 0] = "NEW";
/**
* Use the last successful scan to build the CDK application from. Will fail if no scan is found.
*/
FromScan[FromScan["MOST_RECENT"] = 1] = "MOST_RECENT";
/**
* Starts a scan if none exists, otherwise uses the most recent successful scan to build the CDK application from.
*/
FromScan[FromScan["DEFAULT"] = 2] = "DEFAULT";
})(FromScan || (exports.FromScan = FromScan = {}));
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWlncmF0ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIm1pZ3JhdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBd0NBLHdDQTBEQztBQVNELHNDQU9DO0FBUUQsb0NBV0M7QUFVRCxzQ0FnQkM7QUFTRCw0Q0FzQ0M7QUFpR0Qsd0JBTUM7QUFRRCx3Q0FNQztBQXdDRCxnREFjQztBQWlFRCw0QkFpQkM7QUFTRCw4QkFjQztBQVFELGtDQUlDO0FBeUJELG9EQWNDO0FBUUQsZ0RBYUM7QUFRRCwwQ0FTQztBQVNELG9FQXFCQztBQVNELHdDQUlDO0FBUUQsd0RBMEJDO0FBeG9CRCwwREFBMEQ7QUFDMUQsdURBQXVEO0FBQ3ZELHVDQUF5QjtBQUN6QiwyQ0FBNkI7QUFFN0Isb0VBQThFO0FBQzlFLHNEQUFvRDtBQWFwRCwyREFBNkM7QUFDN0Msa0RBQTBCO0FBQzFCLGlDQUFpQztBQUVqQywwREFBNEQ7QUFDNUQsMENBQXFDO0FBRXJDLDRDQUFnRDtBQUNoRCxNQUFNLFNBQVMsR0FBRyxPQUFPLENBQUMsV0FBVyxDQUFDLENBQUM7QUFDdkMsTUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFDO0FBQ3pDLDBFQUEwRTtBQUMxRSxNQUFNLDJCQUEyQixHQUFzQixZQUFZLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztBQUUxRjs7Ozs7OztHQU9HO0FBQ0ksS0FBSyxVQUFVLGNBQWMsQ0FDbEMsUUFBa0IsRUFDbEIsU0FBaUIsRUFDakIsS0FBYSxFQUNiLFFBQWdCLEVBQ2hCLFVBQW1CLEVBQ25CLFFBQWtCO0lBRWxCLE1BQU0sa0JBQWtCLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLElBQUksT0FBTyxDQUFDLEdBQUcsRUFBRSxFQUFFLFNBQVMsQ0FBQyxDQUFDO0lBQzdFLE1BQU0sa0JBQWtCLEdBQUcsVUFBVSxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBRWpE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dC9uby1yZXF1aXJlLWltcG9ydHMgKi9cbi8qIGVzbGludC1kaXNhYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby12YXItcmVxdWlyZXMgKi9cbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgdHlwZSB7IEVudmlyb25tZW50IH0gZnJvbSAnQGF3cy1jZGsvY2xvdWQtYXNzZW1ibHktYXBpJztcbmltcG9ydCB7IFVOS05PV05fQUNDT1VOVCwgVU5LTk9XTl9SRUdJT04gfSBmcm9tICdAYXdzLWNkay9jbG91ZC1hc3NlbWJseS1hcGknO1xuaW1wb3J0IHsgVG9vbGtpdEVycm9yIH0gZnJvbSAnQGF3cy1jZGsvdG9vbGtpdC1saWInO1xuaW1wb3J0IHR5cGUge1xuICBEZXNjcmliZUdlbmVyYXRlZFRlbXBsYXRlQ29tbWFuZE91dHB1dCxcbiAgRGVzY3JpYmVSZXNvdXJjZVNjYW5Db21tYW5kT3V0cHV0LFxuICBHZXRHZW5lcmF0ZWRUZW1wbGF0ZUNvbW1hbmRPdXRwdXQsXG4gIExpc3RSZXNvdXJjZVNjYW5SZXNvdXJjZXNDb21tYW5kSW5wdXQsXG4gIFJlc291cmNlRGVmaW5pdGlvbixcbiAgUmVzb3VyY2VEZXRhaWwsXG4gIFJlc291cmNlSWRlbnRpZmllclN1bW1hcnksXG4gIFJlc291cmNlU2NhblN1bW1hcnksXG4gIFNjYW5uZWRSZXNvdXJjZSxcbiAgU2Nhbm5lZFJlc291cmNlSWRlbnRpZmllcixcbn0gZnJvbSAnQGF3cy1zZGsvY2xpZW50LWNsb3VkZm9ybWF0aW9uJztcbmltcG9ydCAqIGFzIGNka19mcm9tX2NmbiBmcm9tICdjZGstZnJvbS1jZm4nO1xuaW1wb3J0IGNoYWxrIGZyb20gJ2NoYWxrJztcbmltcG9ydCB7IGNsaUluaXQgfSBmcm9tICcuL2luaXQnO1xuaW1wb3J0IHR5cGUgeyBJQ2xvdWRGb3JtYXRpb25DbGllbnQsIFNka1Byb3ZpZGVyIH0gZnJvbSAnLi4vYXBpL2F3cy1hdXRoJztcbmltcG9ydCB7IENsb3VkRm9ybWF0aW9uU3RhY2sgfSBmcm9tICcuLi9hcGkvY2xvdWRmb3JtYXRpb24nO1xuaW1wb3J0IHsgTW9kZSB9IGZyb20gJy4uL2FwaS9wbHVnaW4nO1xuaW1wb3J0IHR5cGUgeyBJb0hlbHBlciB9IGZyb20gJy4uL2FwaS1wcml2YXRlJztcbmltcG9ydCB7IHppcERpcmVjdG9yeSB9IGZyb20gJy4uL3ByaXZhdGUvdG9vbHMnO1xuY29uc3QgY2FtZWxDYXNlID0gcmVxdWlyZSgnY2FtZWxjYXNlJyk7XG5jb25zdCBkZWNhbWVsaXplID0gcmVxdWlyZSgnZGVjYW1lbGl6ZScpO1xuLyoqIFRoZSBsaXN0IG9mIGxhbmd1YWdlcyBzdXBwb3J0ZWQgYnkgdGhlIGJ1aWx0LWluIG5vY3RpbHVjZW50IGJpbmFyeS4gKi9cbmNvbnN0IE1JR1JBVEVfU1VQUE9SVEVEX0xBTkdVQUdFUzogcmVhZG9ubHkgc3RyaW5nW10gPSBjZGtfZnJvbV9jZm4uc3VwcG9ydGVkX2xhbmd1YWdlcygpO1xuXG4vKipcbiAqIEdlbmVyYXRlcyBhIENESyBhcHAgZnJvbSBhIHlhbWwgb3IganNvbiB0ZW1wbGF0ZS5cbiAqXG4gKiBAcGFyYW0gc3RhY2tOYW1lIC0gVGhlIG5hbWUgdG8gYXNzaWduIHRvIHRoZSBzdGFjayBpbiB0aGUgZ2VuZXJhdGVkIGFwcFxuICogQHBhcmFtIHN0YWNrIC0gVGhlIHlhbWwgb3IganNvbiB0ZW1wbGF0ZSBmb3IgdGhlIHN0YWNrXG4gKiBAcGFyYW0gbGFuZ3VhZ2UgLSBUaGUgbGFuZ3VhZ2UgdG8gZ2VuZXJhdGUgdGhlIENESyBhcHAgaW5cbiAqIEBwYXJhbSBvdXRwdXRQYXRoIC0gVGhlIHBhdGggYXQgd2hpY2ggdG8gZ2VuZXJhdGUgdGhlIENESyBhcHBcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdlbmVyYXRlQ2RrQXBwKFxuICBpb0hlbHBlcjogSW9IZWxwZXIsXG4gIHN0YWNrTmFtZTogc3RyaW5nLFxuICBzdGFjazogc3RyaW5nLFxuICBsYW5ndWFnZTogc3RyaW5nLFxuICBvdXRwdXRQYXRoPzogc3RyaW5nLFxuICBjb21wcmVzcz86IGJvb2xlYW4sXG4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3QgcmVzb2x2ZWRPdXRwdXRQYXRoID0gcGF0aC5qb2luKG91dHB1dFBhdGggPz8gcHJvY2Vzcy5jd2QoKSwgc3RhY2tOYW1lKTtcbiAgY29uc3QgZm9ybWF0dGVkU3RhY2tOYW1lID0gZGVjYW1lbGl6ZShzdGFja05hbWUpO1xuXG4gIHRyeSB7XG4gICAgZnMucm1TeW5jKHJlc29sdmVkT3V0cHV0UGF0aCwgeyByZWN1cnNpdmU6IHRydWUsIGZvcmNlOiB0cnVlIH0pO1xuICAgIGZzLm1rZGlyU3luYyhyZXNvbHZlZE91dHB1dFBhdGgsIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICAgIGNvbnN0IGdlbmVyYXRlT25seSA9IGNvbXByZXNzO1xuICAgIGF3YWl0IGNsaUluaXQoe1xuICAgICAgaW9IZWxwZXIsXG4gICAgICB0eXBlOiAnYXBwJyxcbiAgICAgIGxhbmd1YWdlLFxuICAgICAgY2FuVXNlTmV0d29yazogdHJ1ZSxcbiAgICAgIGdlbmVyYXRlT25seSxcbiAgICAgIHdvcmtEaXI6IHJlc29sdmVkT3V0cHV0UGF0aCxcbiAgICAgIHN0YWNrTmFtZSxcbiAgICAgIG1pZ3JhdGU6IHRydWUsXG4gICAgfSk7XG5cbiAgICBsZXQgc3RhY2tGaWxlTmFtZTogc3RyaW5nO1xuICAgIHN3aXRjaCAobGFuZ3VhZ2UpIHtcbiAgICAgIGNhc2UgJ3R5cGVzY3JpcHQnOlxuICAgICAgICBzdGFja0ZpbGVOYW1lID0gYCR7cmVzb2x2ZWRPdXRwdXRQYXRofS9saWIvJHtmb3JtYXR0ZWRTdGFja05hbWV9LXN0YWNrLnRzYDtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdqYXZhJzpcbiAgICAgICAgc3RhY2tGaWxlTmFtZSA9IGAke3Jlc29sdmVkT3V0cHV0UGF0aH0vc3JjL21haW4vamF2YS9jb20vbXlvcmcvJHtjYW1lbENhc2UoZm9ybWF0dGVkU3RhY2tOYW1lLCB7IHBhc2NhbENhc2U6IHRydWUgfSl9U3RhY2suamF2YWA7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAncHl0aG9uJzpcbiAgICAgICAgc3RhY2tGaWxlTmFtZSA9IGAke3Jlc29sdmVkT3V0cHV0UGF0aH0vJHtmb3JtYXR0ZWRTdGFja05hbWUucmVwbGFjZSgvLS9nLCAnXycpfS8ke2Zvcm1hdHRlZFN0YWNrTmFtZS5yZXBsYWNlKC8tL2csICdfJyl9X3N0YWNrLnB5YDtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdjc2hhcnAnOlxuICAgICAgICBzdGFja0ZpbGVOYW1lID0gYCR7cmVzb2x2ZWRPdXRwdXRQYXRofS9zcmMvJHtjYW1lbENhc2UoZm9ybWF0dGVkU3RhY2tOYW1lLCB7IHBhc2NhbENhc2U6IHRydWUgfSl9LyR7Y2FtZWxDYXNlKGZvcm1hdHRlZFN0YWNrTmFtZSwgeyBwYXNjYWxDYXNlOiB0cnVlIH0pfVN0YWNrLmNzYDtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdnbyc6XG4gICAgICAgIHN0YWNrRmlsZU5hbWUgPSBgJHtyZXNvbHZlZE91dHB1dFBhdGh9LyR7Zm9ybWF0dGVkU3RhY2tOYW1lfS5nb2A7XG4gICAgICAgIGJyZWFrO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgdGhyb3cgbmV3IFRvb2xraXRFcnJvcihcbiAgICAgICAgICAnVW5zdXBwb3J0ZWRNaWdyYXRlTGFuZ3VhZ2UnLFxuICAgICAgICAgIGAke2xhbmd1YWdlfSBpcyBub3Qgc3VwcG9ydGVkIGJ5IENESyBNaWdyYXRlLiBQbGVhc2UgY2hvb3NlIGZyb206ICR7TUlHUkFURV9TVVBQT1JURURfTEFOR1VBR0VTLmpvaW4oJywgJyl9YCxcbiAgICAgICAgKTtcbiAgICB9XG4gICAgZnMud3JpdGVGaWxlU3luYyhzdGFja0ZpbGVOYW1lLCBzdGFjayk7XG4gICAgaWYgKGNvbXByZXNzKSB7XG4gICAgICBhd2FpdCB6aXBEaXJlY3RvcnkocmVzb2x2ZWRPdXRwdXRQYXRoLCBgJHtyZXNvbHZlZE91dHB1dFBhdGh9LnppcGApO1xuICAgICAgZnMucm1TeW5jKHJlc29sdmVkT3V0cHV0UGF0aCwgeyByZWN1cnNpdmU6IHRydWUsIGZvcmNlOiB0cnVlIH0pO1xuICAgIH1cbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBmcy5ybVN5bmMocmVzb2x2ZWRPdXRwdXRQYXRoLCB7IHJlY3Vyc2l2ZTogdHJ1ZSwgZm9yY2U6IHRydWUgfSk7XG4gICAgdGhyb3cgZXJyb3I7XG4gIH1cbn1cblxuLyoqXG4gKiBHZW5lcmF0ZXMgYSBDREsgc3RhY2sgZmlsZS5cbiAqIEBwYXJhbSB0ZW1wbGF0ZSAtIFRoZSB0ZW1wbGF0ZSB0byB0cmFuc2xhdGUgaW50byBhIENESyBzdGFja1xuICogQHBhcmFtIHN0YWNrTmFtZSAtIFRoZSBuYW1lIHRvIGFzc2lnbiB0byB0aGUgc3RhY2tcbiAqIEBwYXJhbSBsYW5ndWFnZSAtIFRoZSBsYW5ndWFnZSB0byBnZW5lcmF0ZSB0aGUgc3RhY2sgaW5cbiAqIEByZXR1cm5zIEEgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIGEgQ0RLIHN0YWNrIGZpbGVcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdlbmVyYXRlU3RhY2sodGVtcGxhdGU6IHN0cmluZywgc3RhY2tOYW1lOiBzdHJpbmcsIGxhbmd1YWdlOiBzdHJpbmcpIHtcbiAgY29uc3QgZm9ybWF0dGVkU3RhY2tOYW1lID0gYCR7Y2FtZWxDYXNlKGRlY2FtZWxpemUoc3RhY2tOYW1lKSwgeyBwYXNjYWxDYXNlOiB0cnVlIH0pfVN0YWNrYDtcbiAgdHJ5IHtcbiAgICByZXR1cm4gY2RrX2Zyb21fY2ZuLnRyYW5zbXV0ZSh0ZW1wbGF0ZSwgbGFuZ3VhZ2UsIGZvcm1hdHRlZFN0YWNrTmFtZSk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICB0aHJvdyBuZXcgVG9vbGtpdEVycm9yKCdTdGFja0dlbmVyYXRpb25GYWlsZWQnLCBgJHtmb3JtYXR0ZWRTdGFja05hbWV9IGNvdWxkIG5vdCBiZSBnZW5lcmF0ZWQgYmVjYXVzZSAkeyhlIGFzIEVycm9yKS5tZXNzYWdlfWApO1xuICB9XG59XG5cbi8qKlxuICogUmVhZHMgYW5kIHJldHVybnMgYSBzdGFjayB0ZW1wbGF0ZSBmcm9tIGEgbG9jYWwgcGF0aC5cbiAqXG4gKiBAcGFyYW0gaW5wdXRQYXRoIC0gVGhlIGxvY2F0aW9uIG9mIHRoZSB0ZW1wbGF0ZVxuICogQHJldHVybnMgQSBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgdGhlIHRlbXBsYXRlIGlmIHByZXNlbnQsIG90aGVyd2lzZSB1bmRlZmluZWRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlYWRGcm9tUGF0aChpbnB1dFBhdGg6IHN0cmluZyk6IHN0cmluZyB7XG4gIGxldCByZWFkRmlsZTogc3RyaW5nO1xuICB0cnkge1xuICAgIHJlYWRGaWxlID0gZnMucmVhZEZpbGVTeW5jKGlucHV0UGF0aCwgJ3V0ZjgnKTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIHRocm93IG5ldyBUb29sa2l0RXJyb3IoJ0ludmFsaWRQYXRoJywgYCcke2lucHV0UGF0aH0nIGlzIG5vdCBhIHZhbGlkIHBhdGguYCk7XG4gIH1cbiAgaWYgKHJlYWRGaWxlID09ICcnKSB7XG4gICAgdGhyb3cgbmV3IFRvb2xraXRFcnJvcignRW1wdHlUZW1wbGF0ZUZpbGUnLCBgQ2xvdWRmb3JtYXRpb24gdGVtcGxhdGUgZmlsZXBhdGg6ICcke2lucHV0UGF0aH0nIGlzIGFuIGVtcHR5IGZpbGUuYCk7XG4gIH1cbiAgcmV0dXJuIHJlYWRGaWxlO1xufVxuXG4vKipcbiAqIFJlYWRzIGFuZCByZXR1cm5zIGEgc3RhY2sgdGVtcGxhdGUgZnJvbSBhIGRlcGxveWVkIENsb3VkRm9ybWF0aW9uIHN0YWNrLlxuICpcbiAqIEBwYXJhbSBzdGFja05hbWUgLSBUaGUgbmFtZSBvZiB0aGUgc3RhY2tcbiAqIEBwYXJhbSBzZGtQcm92aWRlciAtIFRoZSBzZGsgcHJvdmlkZXIgZm9yIG1ha2luZyBDbG91ZEZvcm1hdGlvbiBjYWxsc1xuICogQHBhcmFtIGVudmlyb25tZW50IC0gVGhlIGFjY291bnQgYW5kIHJlZ2lvbiB3aGVyZSB0aGUgc3RhY2sgaXMgZGVwbG95ZWRcbiAqIEByZXR1cm5zIEEgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSB0ZW1wbGF0ZSBpZiBwcmVzZW50LCBvdGhlcndpc2UgdW5kZWZpbmVkXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZWFkRnJvbVN0YWNrKFxuICBzdGFja05hbWU6IHN0cmluZyxcbiAgc2RrUHJvdmlkZXI6IFNka1Byb3ZpZGVyLFxuICBlbnZpcm9ubWVudDogRW52aXJvbm1lbnQsXG4pOiBQcm9taXNlPHN0cmluZyB8IHVuZGVmaW5lZD4ge1xuICBjb25zdCBjbG91ZEZvcm1hdGlvbiA9IChhd2FpdCBzZGtQcm92aWRlci5mb3JFbnZpcm9ubWVudChlbnZpcm9ubWVudCwgTW9kZS5Gb3JSZWFkaW5nKSkuc2RrLmNsb3VkRm9ybWF0aW9uKCk7XG5cbiAgY29uc3Qgc3RhY2sgPSBhd2FpdCBDbG91ZEZvcm1hdGlvblN0YWNrLmxvb2t1cChjbG91ZEZvcm1hdGlvbiwgc3RhY2tOYW1lLCB0cnVlKTtcbiAgaWYgKHN0YWNrLnN0YWNrU3RhdHVzLmlzRGVwbG95U3VjY2VzcyB8fCBzdGFjay5zdGFja1N0YXR1cy5pc1JvbGxiYWNrU3VjY2Vzcykge1xuICAgIHJldHVybiBKU09OLnN0cmluZ2lmeShhd2FpdCBzdGFjay50ZW1wbGF0ZSgpKTtcbiAgfSBlbHNlIHtcbiAgICB0aHJvdyBuZXcgVG9vbGtpdEVycm9yKFxuICAgICAgJ1VuaGVhbHRoeVN0YWNrU3RhdHVzJyxcbiAgICAgIGBTdGFjayAnJHtzdGFja05hbWV9JyBpbiBhY2NvdW50ICR7ZW52aXJvbm1lbnQuYWNjb3VudH0gYW5kIHJlZ2lvbiAke2Vudmlyb25tZW50LnJlZ2lvbn0gaGFzIGEgc3RhdHVzIG9mICcke3N0YWNrLnN0YWNrU3RhdHVzLm5hbWV9JyBkdWUgdG8gJyR7c3RhY2suc3RhY2tTdGF0dXMucmVhc29ufScuIFRoZSBzdGFjayBjYW5ub3QgYmUgbWlncmF0ZWQgdW50aWwgaXQgaXMgaW4gYSBoZWFsdGh5IHN0YXRlLmAsXG4gICAgKTtcbiAgfVxufVxuXG4vKipcbiAqIFRha2VzIGluIGEgc3RhY2sgbmFtZSBhbmQgYWNjb3VudCBhbmQgcmVnaW9uIGFuZCByZXR1cm5zIGEgZ2VuZXJhdGVkIGNsb3VkZm9ybWF0aW9uIHRlbXBsYXRlIHVzaW5nIHRoZSBjbG91ZGZvcm1hdGlvblxuICogdGVtcGxhdGUgZ2VuZXJhdG9yLlxuICpcbiAqIEBwYXJhbSBHZW5lcmF0ZVRlbXBsYXRlT3B0aW9ucyAtIEFuIG9iamVjdCBjb250YWluaW5nIHRoZSBzdGFjayBuYW1lLCBmaWx0ZXJzLCBzZGtQcm92aWRlciwgZW52aXJvbm1lbnQsIGFuZCBuZXdTY2FuIGZsYWdcbiAqIEByZXR1cm5zIGEgZ2VuZXJhdGVkIGNsb3VkZm9ybWF0aW9uIHRlbXBsYXRlXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZW5lcmF0ZVRlbXBsYXRlKG9wdGlvbnM6IEdlbmVyYXRlVGVtcGxhdGVPcHRpb25zKTogUHJvbWlzZTxHZW5lcmF0ZVRlbXBsYXRlT3V0cHV0PiB7XG4gIGNvbnN0IGNmbiA9IG5ldyBDZm5UZW1wbGF0ZUdlbmVyYXRvclByb3ZpZGVyKGF3YWl0IGJ1aWxkQ2ZuQ2xpZW50KG9wdGlvbnMuc2RrUHJvdmlkZXIsIG9wdGlvbnMuZW52aXJvbm1lbnQpLCBvcHRpb25zLmlvSGVscGVyKTtcbiAgY29uc3QgaW9IZWxwZXIgPSBvcHRpb25zLmlvSGVscGVyO1xuXG4gIGNvbnN0IHNjYW5JZCA9IGF3YWl0IGZpbmRMYXN0U3VjY2Vzc2Z1bFNjYW4oY2ZuLCBvcHRpb25zKTtcblxuICAvLyBpZiBhIGN1c3RvbWVyIGFjY2lkZW50YWxseSBjdHJsLWMncyBvdXQgb2YgdGhlIGNvbW1hbmQgYW5kIHJ1bnMgaXQgYWdhaW4sIHRoaXMgd2lsbCBjb250aW51ZSB0aGUgcHJvZ3Jlc3MgYmFyIHdoZXJlIGl0IGxlZnQgb2ZmXG4gIGNvbnN0IGN1clNjYW4gPSBhd2FpdCBjZm4uZGVzY3JpYmVSZXNvdXJjZVNjYW4oc2NhbklkKTtcbiAgaWYgKGN1clNjYW4uU3RhdHVzID09IFNjYW5TdGF0dXMuSU5fUFJPR1JFU1MpIHtcbiAgICBhd2FpdCBpb0hlbHBlci5kZWZhdWx0cy5pbmZvKCdSZXNvdXJjZSBzY2FuIGluIHByb2dyZXNzLiBQbGVhc2Ugd2FpdCwgdGhpcyBjYW4gdGFrZSAxMCBtaW51dGVzIG9yIGxvbmdlci4nKTtcbiAgICBhd2FpdCBzY2FuUHJvZ3Jlc3NCYXIoaW9IZWxwZXIsIHNjYW5JZCwgY2ZuKTtcbiAgfVxuXG4gIGF3YWl0IGRpc3BsYXlUaW1lRGlmZihpb0hlbHBlciwgbmV3IERhdGUoKSwgbmV3IERhdGUoY3VyU2Nhbi5TdGFydFRpbWUhKSk7XG5cbiAgbGV0IHJlc291cmNlczogU2Nhbm5lZFJlc291cmNlW10gPSBhd2FpdCBjZm4ubGlzdFJlc291cmNlU2NhblJlc291cmNlcyhzY2FuSWQhLCBvcHRpb25zLmZpbHRlcnMpO1xuXG4gIGF3YWl0IGlvSGVscGVyLmRlZmF1bHRzLmluZm8oJ2ZpbmRpbmcgcmVsYXRlZCByZXNvdXJjZXMuJyk7XG4gIGxldCByZWxhdGVkUmVzb3VyY2VzID0gYXdhaXQgY2ZuLmdldFJlc291cmNlU2NhblJlbGF0ZWRSZXNvdXJjZXMoc2NhbklkISwgcmVzb3VyY2VzKTtcblxuICBhd2FpdCBpb0hlbHBlci5kZWZhdWx0cy5pbmZvKGBGb3VuZCAke3JlbGF0ZWRSZXNvdXJjZXMubGVuZ3RofSByZXNvdXJjZXMuYCk7XG5cbiAgYXdhaXQgaW9IZWxwZXIuZGVmYXVsdHMuaW5mbygnR2VuZXJhdGluZyBDRk4gdGVtcGxhdGUgZnJvbSBzY2FubmVkIHJlc291cmNlcy4nKTtcbiAgY29uc3QgdGVtcGxhdGVBcm4gPSAoYXdhaXQgY2ZuLmNyZWF0ZUdlbmVyYXRlZFRlbXBsYXRlKG9wdGlvbnMuc3RhY2tOYW1lLCByZWxhdGVkUmVzb3VyY2VzKSkuR2VuZXJhdGVkVGVtcGxhdGVJZCE7XG5cbiAgbGV0IGdlbmVyYXRlZFRlbXBsYXRlID0gYXdhaXQgY2ZuLmRlc2NyaWJlR2VuZXJhdGVkVGVtcGxhdGUodGVtcGxhdGVBcm4pO1xuXG4gIGF3YWl0IGlvSGVscGVyLmRlZmF1bHRzLmluZm8oJ1BsZWFzZSB3YWl0LCB0ZW1wbGF0ZSBjcmVhdGlvbiBpbiBwcm9ncmVzcy4gVGhpcyBtYXkgdGFrZSBhIGNvdXBsZSBtaW51dGVzLicpO1xuICB3aGlsZSAoZ2VuZXJhdGVkVGVtcGxhdGUuU3RhdHVzICE9PSBTY2FuU3RhdHVzLkNPTVBMRVRFICYmIGdlbmVyYXRlZFRlbXBsYXRlLlN0YXR1cyAhPT0gU2NhblN0YXR1cy5GQUlMRUQpIHtcbiAgICBhd2FpdCBwcmludERvdHMoYFske2dlbmVyYXRlZFRlbXBsYXRlLlN0YXR1c31dIFRlbXBsYXRlIENyZWF0aW9uIGluIFByb2dyZXNzYCwgNDAwKTtcbiAgICBnZW5lcmF0ZWRUZW1wbGF0ZSA9IGF3YWl0IGNmbi5kZXNjcmliZUdlbmVyYXRlZFRlbXBsYXRlKHRlbXBsYXRlQXJuKTtcbiAgfVxuICBhd2FpdCBpb0hlbHBlci5kZWZhdWx0cy5pbmZvKCdcXG5UZW1wbGF0ZSBzdWNjZXNzZnVsbHkgZ2VuZXJhdGVkIScpO1xuICByZXR1cm4gYnVpbGRHZW5lcmF0ZWRUZW1wbGF0ZU91dHB1dChcbiAgICBnZW5lcmF0ZWRUZW1wbGF0ZSxcbiAgICAoYXdhaXQgY2ZuLmdldEdlbmVyYXRlZFRlbXBsYXRlKHRlbXBsYXRlQXJuKSkuVGVtcGxhdGVCb2R5ISxcbiAgICB0ZW1wbGF0ZUFybixcbiAgKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZmluZExhc3RTdWNjZXNzZnVsU2NhbihcbiAgY2ZuOiBDZm5UZW1wbGF0ZUdlbmVyYXRvclByb3ZpZGVyLFxuICBvcHRpb25zOiBHZW5lcmF0ZVRlbXBsYXRlT3B0aW9ucyxcbik6IFByb21pc2U8c3RyaW5nPiB7XG4gIGNvbnN0IGlvSGVscGVyID0gb3B0aW9ucy5pb0hlbHBlcjtcbiAgbGV0IHJlc291cmNlU2NhblN1bW1hcmllczogUmVzb3VyY2VTY2FuU3VtbWFyeVtdIHwgdW5kZWZpbmVkID0gW107XG4gIGNvbnN0IGNsaWVudFJlcXVlc3RUb2tlbiA9IGBjZGstbWlncmF0ZS0ke29wdGlvbnMuZW52aXJvbm1lbnQuYWNjb3VudH0tJHtvcHRpb25zLmVudmlyb25tZW50LnJlZ2lvbn1gO1xuICBpZiAob3B0aW9ucy5mcm9tU2NhbiA9PT0gRnJvbVNjYW4uTkVXKSB7XG4gICAgYXdhaXQgaW9IZWxwZXIuZGVmYXVsdHMuaW5mbyhgU3RhcnRpbmcgbmV3IHNjYW4gZm9yIGFjY291bnQgJHtvcHRpb25zLmVudmlyb25tZW50LmFjY291bnR9IGluIHJlZ2lvbiAke29wdGlvbnMuZW52aXJvbm1lbnQucmVnaW9ufWApO1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCBjZm4uc3RhcnRSZXNvdXJjZVNjYW4oY2xpZW50UmVxdWVzdFRva2VuKTtcbiAgICAgIHJlc291cmNlU2NhblN1bW1hcmllcyA9IChhd2FpdCBjZm4ubGlzdFJlc291cmNlU2NhbnMoKSkuUmVzb3VyY2VTY2FuU3VtbWFyaWVzO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIC8vIGNvbnRpbnVpbmcgaGVyZSBiZWNhdXNlIGlmIHRoZSBzY2FuIGZhaWxzIG9uIGEgbmV3LXNjYW4gaXQgaXMgdmVyeSBsaWtlbHkgYmVjYXVzZSB0aGVyZSBpcyBlaXRoZXIgYWxyZWFkeSBhIHNjYW4gaW4gcHJvZ3Jlc3NcbiAgICAgIC8vIG9yIHRoZSBjdXN0b21lciBoaXQgYSByYXRlIGxpbWl0LiBJbiBlaXRoZXIgY2FzZSB3ZSB3YW50IHRvIGNvbnRpbnVlIHdpdGggdGhlIG1vc3QgcmVjZW50IHNjYW4uXG4gICAgICAvLyBJZiB0aGlzIGhhcHBlbnMgdG8gZmFpbCBmb3IgYSBjcmVkZW50aWFsIGVycm9yIHRoZW4gdGhhdCB3aWxsIGJlIGNhdWdodCBpbW1lZGlhdGVseSBhZnRlciBhbnl3YXkuXG4gICAgICBhd2FpdCBpb0hlbHBlci5kZWZhdWx0cy5pbmZvKGBTY2FuIGZhaWxlZCB0byBzdGFydCBkdWUgdG8gZXJyb3IgJyR7KGUgYXMgRXJyb3IpLm1lc3NhZ2V9JywgZGVmYXVsdGluZyB0byBsYXRlc3Qgc2Nhbi5gKTtcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgcmVzb3VyY2VTY2FuU3VtbWFyaWVzID0gKGF3YWl0IGNmbi5saXN0UmVzb3VyY2VTY2FucygpKS5SZXNvdXJjZVNjYW5TdW1tYXJpZXM7XG4gICAgYXdhaXQgY2ZuLmNoZWNrRm9yUmVzb3VyY2VTY2FuKHJlc291cmNlU2NhblN1bW1hcmllcywgb3B0aW9ucywgY2xpZW50UmVxdWVzdFRva2VuKTtcbiAgfVxuICAvLyBnZXQgdGhlIGxhdGVzdCBzY2FuLCB3aGljaCB3ZSBrbm93IHdpbGwgZXhpc3RcbiAgcmVzb3VyY2VTY2FuU3VtbWFyaWVzID0gKGF3YWl0IGNmbi5saXN0UmVzb3VyY2VTY2FucygpKS5SZXNvdXJjZVNjYW5TdW1tYXJpZXM7XG4gIGxldCBzY2FuSWQ6IHN0cmluZyB8IHVuZGVmaW5lZCA9IHJlc291cmNlU2NhblN1bW1hcmllcyFbMF0uUmVzb3VyY2VTY2FuSWQ7XG5cbiAgLy8gZmluZCB0aGUgbW9zdCByZWNlbnQgc2NhbiB0aGF0IGlzbid0IGluIGEgZmFpbGVkIHN0YXRlIGluIGNhc2Ugd2UgZGlkbid0IHN0YXJ0IGEgbmV3IG9uZVxuICBmb3IgKGNvbnN0IHN1bW1hcnkgb2YgcmVzb3VyY2VTY2FuU3VtbWFyaWVzISkge1xuICAgIGlmIChzdW1tYXJ5LlN0YXR1cyAhPT0gU2NhblN0YXR1cy5GQUlMRUQpIHtcbiAgICAgIHNjYW5JZCA9IHN1bW1hcnkuUmVzb3VyY2VTY2FuSWQhO1xuICAgICAgYnJlYWs7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHNjYW5JZCE7XG59XG5cbi8qKlxuICogVGFrZXMgYSBzdHJpbmcgb2YgZmlsdGVycyBpbiB0aGUgZm9ybWF0IG9mIGtleTE9dmFsdWUxLGtleTI9dmFsdWUyIGFuZCByZXR1cm5zIGEgbWFwIG9mIHRoZSBmaWx0ZXJzLlxuICpcbiAqIEBwYXJhbSBmaWx0ZXJzIC0gYSBzdHJpbmcgb2YgZmlsdGVycyBpbiB0aGUgZm9ybWF0IG9mIGtleTE9dmFsdWUxLGtleTI9dmFsdWUyXG4gKiBAcmV0dXJucyBhIG1hcCBvZiB0aGUgZmlsdGVyc1xuICovXG5mdW5jdGlvbiBwYXJzZUZpbHRlcnMoZmlsdGVyczogc3RyaW5nKToge1xuICBba2V5IGluIEZpbHRlclR5cGVdOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG59IHtcbiAgaWYgKCFmaWx0ZXJzKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgICdyZXNvdXJjZS1pZGVudGlmaWVyJzogdW5kZWZpbmVkLFxuICAgICAgJ3Jlc291cmNlLXR5cGUtcHJlZml4JzogdW5kZWZpbmVkLFxuICAgICAgJ3RhZy1rZXknOiB1bmRlZmluZWQsXG4gICAgICAndGFnLXZhbHVlJzogdW5kZWZpbmVkLFxuICAgIH07XG4gIH1cblxuICBjb25zdCBmaWx0ZXJTaG9ydGhhbmRzOiB7IFtrZXk6IHN0cmluZ106IEZpbHRlclR5cGUgfSA9IHtcbiAgICAnaWRlbnRpZmllcic6IEZpbHRlclR5cGUuUkVTT1VSQ0VfSURFTlRJRklFUixcbiAgICAnaWQnOiBGaWx0ZXJUeXBlLlJFU09VUkNFX0lERU5USUZJRVIsXG4gICAgJ3R5cGUnOiBGaWx0ZXJUeXBlLlJFU09VUkNFX1RZUEVfUFJFRklYLFxuICAgICd0eXBlLXByZWZpeCc6IEZpbHRlclR5cGUuUkVTT1VSQ0VfVFlQRV9QUkVGSVgsXG4gIH07XG5cbiAgY29uc3QgZmlsdGVyTGlzdCA9IGZpbHRlcnMuc3BsaXQoJywnKTtcblxuICBsZXQgZmlsdGVyTWFwOiB7IFtrZXkgaW4gRmlsdGVyVHlwZV06IHN0cmluZyB8IHVuZGVmaW5lZCB9ID0ge1xuICAgIFtGaWx0ZXJUeXBlLlJFU09VUkNFX0lERU5USUZJRVJdOiB1bmRlZmluZWQsXG4gICAgW0ZpbHRlclR5cGUuUkVTT1VSQ0VfVFlQRV9QUkVGSVhdOiB1bmRlZmluZWQsXG4gICAgW0ZpbHRlclR5cGUuVEFHX0tFWV06IHVuZGVmaW5lZCxcbiAgICBbRmlsdGVyVHlwZS5UQUdfVkFMVUVdOiB1bmRlZmluZWQsXG4gIH07XG5cbiAgZm9yIChjb25zdCBmaWwgb2YgZmlsdGVyTGlzdCkge1xuICAgIGNvbnN0IGZpbHRlciA9IGZpbC5zcGxpdCgnPScpO1xuICAgIGxldCBmaWx0ZXJLZXkgPSBmaWx0ZXJbMF07XG4gICAgY29uc3QgZmlsdGVyVmFsdWUgPSBmaWx0ZXJbMV07XG4gICAgLy8gaWYgdGhlIGtleSBpcyBhIHNob3J0aGFuZCwgcmVwbGFjZSBpdCB3aXRoIHRoZSBmdWxsIG5hbWVcbiAgICBpZiAoZmlsdGVyS2V5IGluIGZpbHRlclNob3J0aGFuZHMpIHtcbiAgICAgIGZpbHRlcktleSA9IGZpbHRlclNob3J0aGFuZHNbZmlsdGVyS2V5XTtcbiAgICB9XG4gICAgaWYgKE9iamVjdC52YWx1ZXMoRmlsdGVyVHlwZSkuaW5jbHVkZXMoZmlsdGVyS2V5IGFzIGFueSkpIHtcbiAgICAgIGZpbHRlck1hcFtmaWx0ZXJLZXkgYXMga2V5b2YgdHlwZW9mIGZpbHRlck1hcF0gPSBmaWx0ZXJWYWx1ZTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IFRvb2xraXRFcnJvcignSW52YWxpZEZpbHRlcicsIGBJbnZhbGlkIGZpbHRlcjogJHtmaWx0ZXJLZXl9YCk7XG4gICAgfVxuICB9XG4gIHJldHVybiBmaWx0ZXJNYXA7XG59XG5cbi8qKlxuICogVGFrZXMgYSBsaXN0IG9mIGFueSB0eXBlIGFuZCBicmVha3MgaXQgdXAgaW50byBjaHVua3Mgb2YgYSBzcGVjaWZpZWQgc2l6ZS5cbiAqXG4gKiBAcGFyYW0gbGlzdCAtIFRoZSBsaXN0IHRvIGJyZWFrIHVwXG4gKiBAcGFyYW0gY2h1bmtTaXplIC0gVGhlIHNpemUgb2YgZWFjaCBjaHVua1xuICogQHJldHVybnMgQSBsaXN0IG9mIGxpc3RzIG9mIHRoZSBzcGVjaWZpZWQgc2l6ZVxuICovXG5leHBvcnQgZnVuY3Rpb24gY2h1bmtzKGxpc3Q6IGFueVtdLCBjaHVua1NpemU6IG51bWJlcik6IGFueVtdW10ge1xuICBjb25zdCBjaHVua2VkTGlzdDogYW55W11bXSA9IFtdO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGxpc3QubGVuZ3RoOyBpICs9IGNodW5rU2l6ZSkge1xuICAgIGNodW5rZWRMaXN0LnB1c2gobGlzdC5zbGljZShpLCBpICsgY2h1bmtTaXplKSk7XG4gIH1cbiAgcmV0dXJuIGNodW5rZWRMaXN0O1xufVxuXG4vKipcbiAqIFNldHMgdGhlIGFjY291bnQgYW5kIHJlZ2lvbiBmb3IgbWFraW5nIENsb3VkRm9ybWF0aW9uIGNhbGxzLlxuICogQHBhcmFtIGFjY291bnQgLSBUaGUgYWNjb3VudCB0byB1c2VcbiAqIEBwYXJhbSByZWdpb24gLSBUaGUgcmVnaW9uIHRvIHVzZVxuICogQHJldHVybnMgVGhlIGVudmlyb25tZW50IG9iamVjdFxuICovXG5leHBvcnQgZnVuY3Rpb24gc2V0RW52aXJvbm1lbnQoYWNjb3VudD86IHN0cmluZywgcmVnaW9uPzogc3RyaW5nKTogRW52aXJvbm1lbnQge1xuICByZXR1cm4ge1xuICAgIGFjY291bnQ6IGFjY291bnQgPz8gVU5LTk9XTl9BQ0NPVU5ULFxuICAgIHJlZ2lvbjogcmVnaW9uID8/IFVOS05PV05fUkVHSU9OLFxuICAgIG5hbWU6ICdjZGstbWlncmF0ZS1lbnYnLFxuICB9O1xufVxuXG4vKipcbiAqIEVudW0gZm9yIHRoZSBzb3VyY2Ugb3B0aW9ucyBmb3IgdGhlIHRlbXBsYXRlXG4gKi9cbmV4cG9ydCBlbnVtIFRlbXBsYXRlU291cmNlT3B0aW9ucyB7XG4gIFBBVEggPSAncGF0aCcsXG4gIFNUQUNLID0gJ3N0YWNrJyxcbiAgU0NBTiA9ICdzY2FuJyxcbn1cblxuLyoqXG4gKiBBbiBvYmplY3QgcmVwcmVzZW50aW5nIHRoZSBzb3VyY2Ugb2YgYSB0ZW1wbGF0ZS5cbiAqL1xudHlwZSBUZW1wbGF0ZVNvdXJjZSA9XG4gIHwgeyBzb3VyY2U6IFRlbXBsYXRlU291cmNlT3B0aW9ucy5TQ0FOIH1cbiAgfCB7IHNvdXJjZTogVGVtcGxhdGVTb3VyY2VPcHRpb25zLlBBVEg7IHRlbXBsYXRlUGF0aDogc3RyaW5nIH1cbiAgfCB7IHNvdXJjZTogVGVtcGxhdGVTb3VyY2VPcHRpb25zLlNUQUNLOyBzdGFja05hbWU6IHN0cmluZyB9O1xuXG4vKipcbiAqIEVudW0gZm9yIHRoZSBzdGF0dXMgb2YgYSByZXNvdXJjZSBzY2FuXG4gKi9cbmV4cG9ydCBlbnVtIFNjYW5TdGF0dXMge1xuICBJTl9QUk9HUkVTUyA9ICdJTl9QUk9HUkVTUycsXG4gIENPTVBMRVRFID0gJ0NPTVBMRVRFJyxcbiAgRkFJTEVEID0gJ0ZBSUxFRCcsXG59XG5cbmV4cG9ydCBlbnVtIEZpbHRlclR5cGUge1xuICBSRVNPVVJDRV9JREVOVElGSUVSID0gJ3Jlc291cmNlLWlkZW50aWZpZXInLFxuICBSRVNPVVJDRV9UWVBFX1BSRUZJWCA9ICdyZXNvdXJjZS10eXBlLXByZWZpeCcsXG4gIFRBR19LRVkgPSAndGFnLWtleScsXG4gIFRBR19WQUxVRSA9ICd0YWctdmFsdWUnLFxufVxuXG4vKipcbiAqIFZhbGlkYXRlcyB0aGF0IGV4YWN0bHkgb25lIHNvdXJjZSBvcHRpb24gaGFzIGJlZW4gcHJvdmlkZWQuXG4gKiBAcGFyYW0gZnJvbVBhdGggLSBUaGUgY29udGVudCBvZiB0aGUgZmxhZyBgLS1mcm9tLXBhdGhgXG4gKiBAcGFyYW0gZnJvbVN0YWNrIC0gdGhlIGNvbnRlbnQgb2YgdGhlIGZsYWcgYC0tZnJvbS1zdGFja2BcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlU291cmNlT3B0aW9ucyhmcm9tUGF0aD86IHN0cmluZywgZnJvbVN0YWNrPzogYm9vbGVhbiwgc3RhY2tOYW1lPzogc3RyaW5nKTogVGVtcGxhdGVTb3VyY2Uge1xuICBpZiAoZnJvbVBhdGggJiYgZnJvbVN0YWNrKSB7XG4gICAgdGhyb3cgbmV3IFRvb2xraXRFcnJvcignQ29uZmxpY3RpbmdTb3VyY2VPcHRpb25zJywgJ09ubHkgb25lIG9mIGAtLWZyb20tcGF0aGAgb3IgYC0tZnJvbS1zdGFja2AgbWF5IGJlIHByb3ZpZGVkLicpO1xuICB9XG4gIGlmICghc3RhY2tOYW1lKSB7XG4gICAgdGhyb3cgbmV3IFRvb2xraXRFcnJvcignTWlzc2luZ1N0YWNrTmFtZScsICdgLS1zdGFjay1uYW1lYCBpcyBhIHJlcXVpcmVkIGZpZWxkLicpO1xuICB9XG4gIGlmICghZnJvbVBhdGggJiYgIWZyb21TdGFjaykge1xuICAgIHJldHVybiB7IHNvdXJjZTogVGVtcGxhdGVTb3VyY2VPcHRpb25zLlNDQU4gfTtcbiAgfVxuICBpZiAoZnJvbVBhdGgpIHtcbiAgICByZXR1cm4geyBzb3VyY2U6IFRlbXBsYXRlU291cmNlT3B0aW9ucy5QQVRILCB0ZW1wbGF0ZVBhdGg6IGZyb21QYXRoIH07XG4gIH1cbiAgcmV0dXJuIHsgc291cmNlOiBUZW1wbGF0ZVNvdXJjZU9wdGlvbnMuU1RBQ0ssIHN0YWNrTmFtZTogc3RhY2tOYW1lISB9O1xufVxuXG4vKipcbiAqIFRha2VzIGEgc2V0IG9mIHJlc291cmNlcyBhbmQgcmVtb3ZlcyBhbnkgd2l0aCB0aGUgbWFuYWdlZGJ5c3RhY2sgZmxhZyBzZXQgdG8gdHJ1ZS5cbiAqXG4gKiBAcGFyYW0gcmVzb3VyY2VMaXN0IC0gdGhlIGxpc3Qgb2YgcmVzb3VyY2VzIHByb3ZpZGVkIGJ5IHRoZSBsaXN0IHNjYW5uZWQgcmVzb3VyY2VzIGNhbGxzXG4gKiBAcmV0dXJucyBhIGxpc3Qgb2YgcmVzb3VyY2VzIG5vdCBtYW5hZ2VkIGJ5IGNmbiBzdGFja3NcbiAqL1xuZnVuY3Rpb24gZXhjbHVkZU1hbmFnZWQocmVzb3VyY2VMaXN0OiBTY2FubmVkUmVzb3VyY2VbXSk6IFNjYW5uZWRSZXNvdXJjZUlkZW50aWZpZXJbXSB7XG4gIHJldHVybiByZXNvdXJjZUxpc3RcbiAgICAuZmlsdGVyKChyKSA9PiAhci5NYW5hZ2VkQnlTdGFjaylcbiAgICAubWFwKChyKSA9PiAoe1xuICAgICAgUmVzb3VyY2VUeXBlOiByLlJlc291cmNlVHlwZSEsXG4gICAgICBSZXNvdXJjZUlkZW50aWZpZXI6IHIuUmVzb3VyY2VJZGVudGlmaWVyISxcbiAgICB9KSk7XG59XG5cbi8qKlxuICogVHJhbnNmb3JtcyBhIGxpc3Qgb2YgcmVzb3VyY2VzIGludG8gYSBsaXN0IG9mIHJlc291cmNlIGlkZW50aWZpZXJzIGJ5IHJlbW92aW5nIHRoZSBNYW5hZ2VkQnlTdGFjayBmbGFnLlxuICogU2V0dGluZyB0aGUgdmFsdWUgb2YgdGhlIGZpZWxkIHRvIHVuZGVmaW5lZCBlZmZlY3RpdmVseSByZW1vdmVzIGl0IGZyb20gdGhlIG9iamVjdC5cbiAqXG4gKiBAcGFyYW0gcmVzb3VyY2VMaXN0IC0gdGhlIGxpc3Qgb2YgcmVzb3VyY2VzIHByb3ZpZGVkIGJ5IHRoZSBsaXN0IHNjYW5uZWQgcmVzb3VyY2VzIGNhbGxzXG4gKiBAcmV0dXJucyBhIGxpc3Qgb2YgU2Nhbm5lZFJlc291cmNlSWRlbnRpZmllcltdXG4gKi9cbmZ1bmN0aW9uIHJlc291cmNlSWRlbnRpZmllcnMocmVzb3VyY2VMaXN0OiBTY2FubmVkUmVzb3VyY2VbXSk6IFNjYW5uZWRSZXNvdXJjZUlkZW50aWZpZXJbXSB7XG4gIGNvbnN0IGlkZW50aWZpZXJzOiBTY2FubmVkUmVzb3VyY2VJZGVudGlmaWVyW10gPSBbXTtcbiAgcmVzb3VyY2VMaXN0LmZvckVhY2goKHIpID0+IHtcbiAgICBjb25zdCBpZGVudGlmaWVyOiBTY2FubmVkUmVzb3VyY2VJZGVudGlmaWVyID0ge1xuICAgICAgUmVzb3VyY2VUeXBlOiByLlJlc291cmNlVHlwZSEsXG4gICAgICBSZXNvdXJjZUlkZW50aWZpZXI6IHIuUmVzb3VyY2VJZGVudGlmaWVyISxcbiAgICB9O1xuICAgIGlkZW50aWZpZXJzLnB1c2goaWRlbnRpZmllcik7XG4gIH0pO1xuICByZXR1cm4gaWRlbnRpZmllcnM7XG59XG5cbi8qKlxuICogVGFrZXMgYSBzY2FuIGlkIGFuZCBtYWludGFpbnMgYSBwcm9ncmVzcyBiYXIgdG8gZGlzcGxheSB0aGUgcHJvZ3Jlc3Mgb2YgYSBzY2FuIHRvIHRoZSB1c2VyLlxuICpcbiAqIEBwYXJhbSBzY2FuSWQgLSBBIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIHNjYW4gaWRcbiAqIEBwYXJhbSBjbG91ZEZvcm1hdGlvbiAtIFRoZSBDbG91ZEZvcm1hdGlvbiBzZGsgY2xpZW50IHRvIHVzZVxuICovXG5hc3luYyBmdW5jdGlvbiBzY2FuUHJvZ3Jlc3NCYXIoaW9IZWxwZXI6IElvSGVscGVyLCBzY2FuSWQ6IHN0cmluZywgY2ZuOiBDZm5UZW1wbGF0ZUdlbmVyYXRvclByb3ZpZGVyKSB7XG4gIGxldCBjdXJQcm9ncmVzcyA9IDAuNTtcbiAgLy8gd2Uga25vdyBpdCdzIGluIHByb2dyZXNzIGluaXRpYWxseSBzaW5jZSB3ZSB3b3VsZG4ndCBoYXZlIGdvdHRlbiBoZXJlIGlmIGl0IHdhc24ndFxuICBsZXQgY3VyU2NhbjogRGVzY3JpYmVSZXNvdXJjZVNjYW5Db21tYW5kT3V0cHV0ID0ge1xuICAgIFN0YXR1czogU2NhblN0YXR1cy5JTl9QUk9HUkVTUyxcbiAgICAkbWV0YWRhdGE6IHt9LFxuICB9O1xuICB3aGlsZSAoY3VyU2Nhbi5TdGF0dXMgPT0gU2NhblN0YXR1cy5JTl9QUk9HUkVTUykge1xuICAgIGN1clNjYW4gPSBhd2FpdCBjZm4uZGVzY3JpYmVSZXNvdXJjZVNjYW4oc2NhbklkKTtcbiAgICBjdXJQcm9ncmVzcyA9IGN1clNjYW4uUGVyY2VudGFnZUNvbXBsZXRlZCA/PyBjdXJQcm9ncmVzcztcbiAgICBwcmludEJhcigzMCwgY3VyUHJvZ3Jlc3MpO1xuICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIDIwMDApKTtcbiAgfVxuICBhd2FpdCBpb0hlbHBlci5kZWZhdWx0cy5pbmZvKCdcXG7inIUgU2NhbiBDb21wbGV0ZSEnKTtcbn1cblxuLyoqXG4gKiBQcmludHMgYSBwcm9ncmVzcyBiYXIgdG8gdGhlIGNvbnNvbGUuIFRvIGJlIHVzZWQgaW4gYSB3aGlsZSBsb29wIHRvIHNob3cgcHJvZ3Jlc3Mgb2YgYSBsb25nIHJ1bm5pbmcgdGFzay5cbiAqIFRoZSBwcm9ncmVzcyBiYXIgZGVsZXRlcyB0aGUgY3VycmVudCBsaW5lIG9uIHRoZSBjb25zb2xlIGFuZCByZXdyaXRlcyBpdCB3aXRoIHRoZSBwcm9ncmVzcyBhbW91bnQuXG4gKlxuICogQHBhcmFtIHdpZHRoIC0gVGhlIHdpZHRoIG9mIHRoZSBwcm9ncmVzcyBiYXJcbiAqIEBwYXJhbSBwcm9ncmVzcyAtIFRoZSBjdXJyZW50IHByb2dyZXNzIHRvIGRpc3BsYXkgYXMgYSBwZXJjZW50YWdlIG9mIDEwMFxuICovXG5leHBvcnQgZnVuY3Rpb24gcHJpbnRCYXIod2lkdGg6IG51bWJlciwgcHJvZ3Jlc3M6IG51bWJlcikge1xuICBpZiAoIXByb2Nlc3MuZW52Lk1JR1JBVEVfSU5URUdfVEVTVCkge1xuICAgIGNvbnN0IEZVTExfQkxPQ0sgPSAn4paIJztcbiAgICBjb25zdCBQQVJUSUFMX0JMT0NLID0gWycnLCAn4paPJywgJ+KWjicsICfilo0nLCAn4paMJywgJ+KWiycsICfiloonLCAn4paJJ107XG4gICAgY29uc3QgZnJhY3Rpb24gPSBNYXRoLm1pbihwcm9ncmVzcyAvIDEwMCwgMSk7XG4gICAgY29uc3QgaW5uZXJXaWR0aCA9IE1hdGgubWF4KDEsIHdpZHRoIC0gMik7XG4gICAgY29uc3QgY2hhcnMgPSBpbm5lcldpZHRoICogZnJhY3Rpb247XG4gICAgY29uc3QgcmVtYWluZGVyID0gY2hhcnMgLSBNYXRoLmZsb29yKGNoYXJzKTtcblxuICAgIGNvbnN0IGZ1bGxDaGFycyA9IEZVTExfQkxPQ0sucmVwZWF0KE1hdGguZmxvb3IoY2hhcnMpKTtcbiAgICBjb25zdCBwYXJ0aWFsQ2hhciA9IFBBUlRJQUxfQkxPQ0tbTWF0aC5mbG9vcihyZW1haW5kZXIgKiBQQVJUSUFMX0JMT0NLLmxlbmd0aCldO1xuICAgIGNvbnN0IGZpbGxlciA9ICfCtycucmVwZWF0KGlubmVyV2lkdGggLSBNYXRoLmZsb29yKGNoYXJzKSAtIChwYXJ0aWFsQ2hhciA/IDEgOiAwKSk7XG5cbiAgICBjb25zdCBjb2xvciA9IGNoYWxrLmdyZWVuO1xuXG4gICAgcmV3cml0ZUxpbmUoJ1snICsgY29sb3IoZnVsbENoYXJzICsgcGFydGlhbENoYXIpICsgZmlsbGVyICsgYF0gKCR7cHJvZ3Jlc3N9JSlgKTtcbiAgfVxufVxuXG4vKipcbiAqIFByaW50cyBhIG1lc3NhZ2UgdG8gdGhlIGNvbnNvbGUgd2l0aCBhIHNlcmllcyBwZXJpb2RzIGFwcGVuZGVkIHRvIGl0LiBUbyBiZSB1c2VkIGluIGEgd2hpbGUgbG9vcCB0byBzaG93IHByb2dyZXNzIG9mIGEgbG9uZyBydW5uaW5nIHRhc2suXG4gKiBUaGUgbWVzc2FnZSBkZWxldGVzIHRoZSBjdXJyZW50IGxpbmUgYW5kIHJld3JpdGVzIGl0IHNldmVyYWwgdGltZXMgdG8gZGlzcGxheSAxLTMgcGVyaW9kcyB0byBzaG93IHRoZSB1c2VyIHRoYXQgdGhlIHRhc2sgaXMgc3RpbGwgcnVubmluZy5cbiAqXG4gKiBAcGFyYW0gbWVzc2FnZSAtIFRoZSBtZXNzYWdlIHRvIGRpc3BsYXlcbiAqIEBwYXJhbSB0aW1lb3V0eDQgLSBUaGUgYW1vdW50IG9mIHRpbWUgdG8gd2FpdCBiZWZvcmUgcHJpbnRpbmcgdGhlIG5leHQgcGVyaW9kXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwcmludERvdHMobWVzc2FnZTogc3RyaW5nLCB0aW1lb3V0eDQ6IG51bWJlcikge1xuICBpZiAoIXByb2Nlc3MuZW52Lk1JR1JBVEVfSU5URUdfVEVTVCkge1xuICAgIHJld3JpdGVMaW5lKG1lc3NhZ2UgKyAnIC4nKTtcbiAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4gc2V0VGltZW91dChyZXNvbHZlLCB0aW1lb3V0eDQpKTtcblxuICAgIHJld3JpdGVMaW5lKG1lc3NhZ2UgKyAnIC4uJyk7XG4gICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgdGltZW91dHg0KSk7XG5cbiAgICByZXdyaXRlTGluZShtZXNzYWdlICsgJyAuLi4nKTtcbiAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4gc2V0VGltZW91dChyZXNvbHZlLCB0aW1lb3V0eDQpKTtcblxuICAgIHJld3JpdGVMaW5lKG1lc3NhZ2UpO1xuICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIHRpbWVvdXR4NCkpO1xuICB9XG59XG5cbi8qKlxuICogUmV3cml0ZXMgdGhlIGN1cnJlbnQgbGluZSBvbiB0aGUgY29uc29sZSBhbmQgd3JpdGVzIGEgbmV3IG1lc3NhZ2UgdG8gaXQuXG4gKiBUaGlzIGlzIGEgaGVscGVyIGZ1bmNpdG9uIGZvciBwcmludERvdHMgYW5kIHByaW50QmFyLlxuICpcbiAqIEBwYXJhbSBtZXNzYWdlIC0gVGhlIG1lc3NhZ2UgdG8gZGlzcGxheVxuICovXG5leHBvcnQgZnVuY3Rpb24gcmV3cml0ZUxpbmUobWVzc2FnZTogc3RyaW5nKSB7XG4gIHByb2Nlc3Muc3Rkb3V0LmNsZWFyTGluZSgwKTtcbiAgcHJvY2Vzcy5zdGRvdXQuY3Vyc29yVG8oMCk7XG4gIHByb2Nlc3Muc3Rkb3V0LndyaXRlKG1lc3NhZ2UpO1xufVxuXG4vKipcbiAqIFByaW50cyB0aGUgdGltZSBkaWZmZXJlbmNlIGJldHdlZW4gdHdvIGRhdGVzIGluIGRheXMsIGhvdXJzLCBhbmQgbWludXRlcy5cbiAqXG4gKiBAcGFyYW0gdGltZTEgLSBUaGUgZmlyc3QgZGF0ZSB0byBjb21wYXJlXG4gKiBAcGFyYW0gdGltZTIgLSBUaGUgc2Vjb25kIGRhdGUgdG8gY29tcGFyZVxuICovXG5hc3luYyBmdW5jdGlvbiBkaXNwbGF5VGltZURpZmYoaW9IZWxwZXI6IElvSGVscGVyLCB0aW1lMTogRGF0ZSwgdGltZTI6IERhdGUpOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3QgZGlmZiA9IE1hdGguYWJzKHRpbWUxLmdldFRpbWUoKSAtIHRpbWUyLmdldFRpbWUoKSk7XG5cbiAgY29uc3QgZGF5cyA9IE1hdGguZmxvb3IoZGlmZiAvICgxMDAwICogNjAgKiA2MCAqIDI0KSk7XG4gIGNvbnN0IGhvdXJzID0gTWF0aC5mbG9vcigoZGlmZiAlICgxMDAwICogNjAgKiA2MCAqIDI0KSkgLyAoMTAwMCAqIDYwICogNjApKTtcbiAgY29uc3QgbWludXRlcyA9IE1hdGguZmxvb3IoKGRpZmYgJSAoMTAwMCAqIDYwICogNjApKSAvICgxMDAwICogNjApKTtcblxuICBhd2FpdCBpb0hlbHBlci5kZWZhdWx0cy5pbmZvKGBVc2luZyB0aGUgbGF0ZXN0IHN1Y2Nlc3NmdWwgc2NhbiB3aGljaCBpcyAke2RheXN9IGRheXMsICR7aG91cnN9IGhvdXJzLCBhbmQgJHttaW51dGVzfSBtaW51dGVzIG9sZC5gKTtcbn1cblxuLyoqXG4gKiBXcml0ZXMgYSBtaWdyYXRlLmpzb24gZmlsZSB0byB0aGUgb3V0cHV0IGRpcmVjdG9yeS5cbiAqXG4gKiBAcGFyYW0gb3V0cHV0UGF0aCAtIFRoZSBwYXRoIHRvIHdyaXRlIHRoZSBtaWdyYXRlLmpzb24gZmlsZSB0b1xuICogQHBhcmFtIHN0YWNrTmFtZSAtIFRoZSBuYW1lIG9mIHRoZSBzdGFja1xuICogQHBhcmFtIGdlbmVyYXRlZE91dHB1dCAtIFRoZSBvdXRwdXQgb2YgdGhlIHRlbXBsYXRlIGdlbmVyYXRvclxuICovXG5leHBvcnQgZnVuY3Rpb24gd3JpdGVNaWdyYXRlSnNvbkZpbGUoXG4gIG91dHB1dFBhdGg6IHN0cmluZyB8IHVuZGVmaW5lZCxcbiAgc3RhY2tOYW1lOiBzdHJpbmcsXG4gIG1pZ3JhdGVKc29uOiBNaWdyYXRlSnNvbkZvcm1hdCxcbikge1xuICBjb25zdCBvdXRwdXRUb0pzb24gPSB7XG4gICAgJy8vJzogJ1RoaXMgZmlsZSBpcyBnZW5lcmF0ZWQgYnkgY2RrIG1pZ3JhdGUuIEl0IHdpbGwgYmUgYXV0b21hdGljYWxseSBkZWxldGVkIGFmdGVyIHRoZSBmaXJzdCBzdWNjZXNzZnVsIGRlcGxveW1lbnQgb2YgdGhpcyBhcHAgdG8gdGhlIGVudmlyb25tZW50IG9mIHRoZSBvcmlnaW5hbCByZXNvdXJjZXMuJyxcbiAgICAnU291cmNlJzogbWlncmF0ZUpzb24uc291cmNlLFxuICAgICdSZXNvdXJjZXMnOiBtaWdyYXRlSnNvbi5yZXNvdXJjZXMsXG4gIH07XG4gIGZzLndyaXRlRmlsZVN5bmMoXG4gICAgYCR7cGF0aC5qb2luKG91dHB1dFBhdGggPz8gcHJvY2Vzcy5jd2QoKSwgc3RhY2tOYW1lKX0vbWlncmF0ZS5qc29uYCxcbiAgICBKU09OLnN0cmluZ2lmeShvdXRwdXRUb0pzb24sIG51bGwsIDIpLFxuICApO1xufVxuXG4vKipcbiAqIFRha2VzIGEgc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgZnJvbS1zY2FuIGZsYWcgYW5kIHJldHVybnMgYSBGcm9tU2NhbiBlbnVtIHZhbHVlLlxuICpcbiAqIEBwYXJhbSBzY2FuVHlwZSAtIEEgc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgZnJvbS1zY2FuIGZsYWdcbiAqIEByZXR1cm5zIEEgRnJvbVNjYW4gZW51bSB2YWx1ZVxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TWlncmF0ZVNjYW5UeXBlKHNjYW5UeXBlOiBzdHJpbmcpIHtcbiAgc3dpdGNoIChzY2FuVHlwZSkge1xuICAgIGNhc2UgJ25ldyc6XG4gICAgICByZXR1cm4gRnJvbVNjYW4uTkVXO1xuICAgIGNhc2UgJ21vc3QtcmVjZW50JzpcbiAgICAgIHJldHVybiBGcm9tU2Nhbi5NT1NUX1JFQ0VOVDtcbiAgICBjYXNlICcnOlxuICAgICAgcmV0dXJuIEZyb21TY2FuLkRFRkFVTFQ7XG4gICAgY2FzZSB1bmRlZmluZWQ6XG4gICAgICByZXR1cm4gRnJvbVNjYW4uREVGQVVMVDtcbiAgICBkZWZhdWx0OlxuICAgICAgdGhyb3cgbmV3IFRvb2xraXRFcnJvcignVW5rbm93blNjYW5UeXBlJywgYFVua25vd24gc2NhbiB0eXBlOiAke3NjYW5UeXBlfWApO1xuICB9XG59XG5cbi8qKlxuICogVGFrZXMgYSBnZW5lcmF0ZWRUZW1wbGF0ZU91dHB1dCBvYmpjdCBhbmQgcmV0dXJucyBhIGJvb2xlYW4gcmVwcmVzZW50aW5nIHdoZXRoZXIgdGhlcmUgYXJlIGFueSB3YXJuaW5ncyBvbiBhbnkgcmVzY291cmNlcy5cbiAqXG4gKiBAcGFyYW0gZ2VuZXJhdGVkVGVtcGxhdGVPdXRwdXQgLSBBIEdlbmVyYXRlVGVtcGxhdGVPdXRwdXQgb2JqZWN0XG4gKiBAcmV0dXJucyBBIGJvb2xlYW4gcmVwcmVzZW50aW5nIHdoZXRoZXIgdGhlcmUgYXJlIGFueSB3YXJuaW5ncyBvbiBhbnkgcmVzY291cmNlc1xuICovXG5leHBvcnQgZnVuY3Rpb24gaXNUaGVyZUFXYXJuaW5nKGdlbmVyYXRlZFRlbXBsYXRlT3V0cHV0OiBHZW5lcmF0ZVRlbXBsYXRlT3V0cHV0KSB7XG4gIGlmIChnZW5lcmF0ZWRUZW1wbGF0ZU91dHB1dC5yZXNvdXJjZXMpIHtcbiAgICBmb3IgKGNvbnN0IHJlc291cmNlIG9mIGdlbmVyYXRlZFRlbXBsYXRlT3V0cHV0LnJlc291cmNlcykge1xuICAgICAgaWYgKHJlc291cmNlLldhcm5pbmdzICYmIHJlc291cmNlLldhcm5pbmdzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIHJldHVybiBmYWxzZTtcbn1cblxuLyoqXG4gKiBCdWlsZHMgdGhlIEdlbmVyYXRlVGVtcGxhdGVPdXRwdXQgb2JqZWN0IGZyb20gdGhlIERlc2NyaWJlR2VuZXJhdGVkVGVtcGxhdGVPdXRwdXQgYW5kIHRoZSB0ZW1wbGF0ZSBib2R5LlxuICpcbiAqIEBwYXJhbSBnZW5lcmF0ZWRUZW1wbGF0ZVN1bW1hcnkgLSBUaGUgb3V0cHV0IG9mIHRoZSBkZXNjcmliZSBnZW5lcmF0ZWQgdGVtcGxhdGUgY2FsbFxuICogQHBhcmFtIHRlbXBsYXRlQm9keSAtIFRoZSBib2R5IG9mIHRoZSBnZW5lcmF0ZWQgdGVtcGxhdGVcbiAqIEByZXR1cm5zIEEgR2VuZXJhdGVUZW1wbGF0ZU91dHB1dCBvYmplY3RcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJ1aWxkR2VuZXJhdGVkVGVtcGxhdGVPdXRwdXQoXG4gIGdlbmVyYXRlZFRlbXBsYXRlU3VtbWFyeTogRGVzY3JpYmVHZW5lcmF0ZWRUZW1wbGF0ZUNvbW1hbmRPdXRwdXQsXG4gIHRlbXBsYXRlQm9keTogc3RyaW5nLFxuICBzb3VyY2U6IHN0cmluZyxcbik6IEdlbmVyYXRlVGVtcGxhdGVPdXRwdXQge1xuICBjb25zdCByZXNvdXJjZXM6IFJlc291cmNlRGV0YWlsW10gfCB1bmRlZmluZWQgPSBnZW5lcmF0ZWRUZW1wbGF0ZVN1bW1hcnkuUmVzb3VyY2VzO1xuICBjb25zdCBtaWdyYXRlSnNvbjogTWlncmF0ZUpzb25Gb3JtYXQgPSB7XG4gICAgdGVtcGxhdGVCb2R5OiB0ZW1wbGF0ZUJvZHksXG4gICAgc291cmNlOiBzb3VyY2UsXG4gICAgcmVzb3VyY2VzOiBnZW5lcmF0ZWRUZW1wbGF0ZVN1bW1hcnkuUmVzb3VyY2VzIS5tYXAoKHIpID0+ICh7XG4gICAgICBSZXNvdXJjZVR5cGU6IHIuUmVzb3VyY2VUeXBlISxcbiAgICAgIExvZ2ljYWxSZXNvdXJjZUlkOiByLkxvZ2ljYWxSZXNvdXJjZUlkISxcbiAgICAgIFJlc291cmNlSWRlbnRpZmllcjogci5SZXNvdXJjZUlkZW50aWZpZXIhLFxuICAgIH0pKSxcbiAgfTtcbiAgY29uc3QgdGVtcGxhdGVJZCA9IGdlbmVyYXRlZFRlbXBsYXRlU3VtbWFyeS5HZW5lcmF0ZWRUZW1wbGF0ZUlkITtcbiAgcmV0dXJuIHtcbiAgICBtaWdyYXRlSnNvbjogbWlncmF0ZUpzb24sXG4gICAgcmVzb3VyY2VzOiByZXNvdXJjZXMsXG4gICAgdGVtcGxhdGVJZDogdGVtcGxhdGVJZCxcbiAgfTtcbn1cblxuLyoqXG4gKiBCdWlsZHMgYSBDbG91ZEZvcm1hdGlvbiBzZGsgY2xpZW50IGZvciBtYWtpbmcgcmVxdWVzdHMgd2l0aCB0aGUgQ0ZOIHRlbXBsYXRlIGdlbmVyYXRvci5cbiAqXG4gKiBAcGFyYW0gc2RrUHJvdmlkZXIgLSBUaGUgc2RrIHByb3ZpZGVyIGZvciBtYWtpbmcgQ2xvdWRGb3JtYXRpb24gY2FsbHNcbiAqIEBwYXJhbSBlbnZpcm9ubWVudCAtIFRoZSBhY2NvdW50IGFuZCByZWdpb24gd2hlcmUgdGhlIHN0YWNrIGlzIGRlcGxveWVkXG4gKiBAcmV0dXJucyBBIENsb3VkRm9ybWF0aW9uIHNkayBjbGllbnRcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGJ1aWxkQ2ZuQ2xpZW50KHNka1Byb3ZpZGVyOiBTZGtQcm92aWRlciwgZW52aXJvbm1lbnQ6IEVudmlyb25tZW50KSB7XG4gIGNvbnN0IHNkayA9IChhd2FpdCBzZGtQcm92aWRlci5mb3JFbnZpcm9ubWVudChlbnZpcm9ubWVudCwgTW9kZS5Gb3JSZWFkaW5nKSkuc2RrO1xuICBzZGsuYXBwZW5kQ3VzdG9tVXNlckFnZW50KCdjZGstbWlncmF0ZScpO1xuICByZXR1cm4gc2RrLmNsb3VkRm9ybWF0aW9uKCk7XG59XG5cbi8qKlxuICogQXBwZW5kcyBhIGxpc3Qgb2Ygd2FybmluZ3MgdG8gYSByZWFkbWUgZmlsZS5cbiAqXG4gKiBAcGFyYW0gZmlsZXBhdGggLSBUaGUgcGF0aCB0byB0aGUgcmVhZG1lIGZpbGVcbiAqIEBwYXJhbSByZXNvdXJjZXMgLSBBIGxpc3Qgb2YgcmVzb3VyY2VzIHRvIGFwcGVuZCB3YXJuaW5ncyBmb3JcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGFwcGVuZFdhcm5pbmdzVG9SZWFkbWUoZmlsZXBhdGg6IHN0cmluZywgcmVzb3VyY2VzOiBSZXNvdXJjZURldGFpbFtdKSB7XG4gIGNvbnN0IHJlYWRtZSA9IGZzLnJlYWRGaWxlU3luYyhmaWxlcGF0aCwgJ3V0ZjgnKTtcbiAgY29uc3QgbGluZXMgPSByZWFkbWUuc3BsaXQoJ1xcbicpO1xuICBjb25zdCBpbmRleCA9IGxpbmVzLmZpbmRJbmRleCgobGluZSkgPT4gbGluZS50cmltKCkgPT09ICdFbmpveSEnKTtcbiAgbGV0IGxpbmVzVG9BZGQgPSBbJ1xcbiMjIFdhcm5pbmdzJ107XG4gIGxpbmVzVG9BZGQucHVzaCgnIyMjIFdyaXRlLW9ubHkgcHJvcGVydGllcycpO1xuICBsaW5lc1RvQWRkLnB1c2goXG4gICAgXCJXcml0ZS1vbmx5IHByb3BlcnRpZXMgYXJlIHJlc291cmNlIHByb3BlcnR5IHZhbHVlcyB0aGF0IGNhbiBiZSB3cml0dGVuIHRvIGJ1dCBjYW4ndCBiZSByZWFkIGJ5IEFXUyBDbG91ZEZvcm1hdGlvbiBvciBDREsgTWlncmF0ZS4gRm9yIG1vcmUgaW5mb3JtYXRpb24sIHNlZSBbSWFDIGdlbmVyYXRvciBhbmQgd3JpdGUtb25seSBwcm9wZXJ0aWVzXShodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vQVdTQ2xvdWRGb3JtYXRpb24vbGF0ZXN0L1VzZXJHdWlkZS9nZW5lcmF0ZS1JYUMtd3JpdGUtb25seS1wcm9wZXJ0aWVzLmh0bWwpLlwiLFxuICApO1xuICBsaW5lc1RvQWRkLnB1c2goJ1xcbicpO1xuICBsaW5lc1RvQWRkLnB1c2goXG4gICAgJ1dyaXRlLW9ubHkgcHJvcGVydGllcyBkaXNjb3ZlcmVkIGR1cmluZyBtaWdyYXRpb24gYXJlIG9yZ2FuaXplZCBoZXJlIGJ5IHJlc291cmNlIElEIGFuZCBjYXRlZ29yaXplZCBieSB3cml0ZS1vbmx5IHByb3BlcnR5IHR5cGUuIFJlc29sdmUgd3JpdGUtb25seSBwcm9wZXJ0aWVzIGJ5IHByb3ZpZGluZyBwcm9wZXJ0eSB2YWx1ZXMgaW4geW91ciBDREsgYXBwLiBGb3IgZ3VpZGFuY2UsIHNlZSBbUmVzb2x2ZSB3cml0ZS1vbmx5IHByb3BlcnRpZXNdKGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9jZGsvdjIvZ3VpZGUvbWlncmF0ZS5odG1sI21pZ3JhdGUtcmVzb3VyY2VzLXdyaXRlb25seSkuJyxcbiAgKTtcbiAgZm9yIChjb25zdCByZXNvdXJjZSBvZiByZXNvdXJjZXMpIHtcbiAgICBpZiAocmVzb3VyY2UuV2FybmluZ3MgJiYgcmVzb3VyY2UuV2FybmluZ3MubGVuZ3RoID4gMCkge1xuICAgICAgbGluZXNUb0FkZC5wdXNoKGAjIyMgJHtyZXNvdXJjZS5Mb2dpY2FsUmVzb3VyY2VJZH1gKTtcbiAgICAgIGZvciAoY29uc3Qgd2FybmluZyBvZiByZXNvdXJjZS5XYXJuaW5ncykge1xuICAgICAgICBsaW5lc1RvQWRkLnB1c2goYC0gKioke3dhcm5pbmcuVHlwZX0qKjogYCk7XG4gICAgICAgIGZvciAoY29uc3QgcHJvcGVydHkgb2Ygd2FybmluZy5Qcm9wZXJ0aWVzISkge1xuICAgICAgICAgIGxpbmVzVG9BZGQucHVzaChgICAtICR7cHJvcGVydHkuUHJvcGVydHlQYXRofTogJHtwcm9wZXJ0eS5EZXNjcmlwdGlvbn1gKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuICBsaW5lcy5zcGxpY2UoaW5kZXgsIDAsIC4uLmxpbmVzVG9BZGQpO1xuICBmcy53cml0ZUZpbGVTeW5jKGZpbGVwYXRoLCBsaW5lcy5qb2luKCdcXG4nKSk7XG59XG5cbi8qKlxuICogdGFrZXMgYSBsaXN0IG9mIHJlc291cmNlcyBhbmQgcmV0dXJucyBhIGxpc3Qgb2YgdW5pcXVlIHJlc291cmNlcyBiYXNlZCBvbiB0aGUgcmVzb3VyY2UgdHlwZSBhbmQgbG9naWNhbCByZXNvdXJjZSBpZC5cbiAqXG4gKiBAcGFyYW0gcmVzb3VyY2VzIC0gQSBsaXN0IG9mIHJlc291cmNlcyB0byBkZWR1cGxpY2F0ZVxuICogQHJldHVybnMgQSBsaXN0IG9mIHVuaXF1ZSByZXNvdXJjZXNcbiAqL1xuZnVuY3Rpb24gZGVkdXBsaWNhdGVSZXNvdXJjZXMocmVzb3VyY2VzOiBSZXNvdXJjZURldGFpbFtdKSB7XG4gIGxldCB1bmlxdWVSZXNvdXJjZXM6IHsgW2tleTogc3RyaW5nXTogUmVzb3VyY2VEZXRhaWwgfSA9IHt9O1xuXG4gIGZvciAoY29uc3QgcmVzb3VyY2Ugb2YgcmVzb3VyY2VzKSB7XG4gICAgLy8gQ3JlYXRpbmcgb3VyIHVuaXF1ZSBpZGVudGlmaWVyIHVzaW5nIHRoZSByZXNvdXJjZSB0eXBlIGFuZCB0aGUgZnVsbCByZXNvdXJjZSBpZGVudGlmaWVyLlxuICAgIC8vIFRoZSByZXNvdXJjZSBpZGVudGlmaWVyIGNhbiBiZSBtYWRlIHVwIG9mIG1vcmUgdGhhbiBvbmUga2V5L3ZhbHVlIHBhaXIgKGUuZy4gYSByZXNvdXJjZVxuICAgIC8vIGxpa2UgQVdTOjpSb3V0ZTUzOjpLZXlTaWduaW5nS2V5IGlzIGlkZW50aWZpZWQgYnkgdGhlIHBhaXIgSG9zdGVkWm9uZUlkICsgTmFtZSksIHNvIGV2ZXJ5XG4gICAgLy8ga2V5IG11c3QgYmUgaW5jbHVkZWQgLS0gdXNpbmcgb25seSB0aGUgZmlyc3Qga2V5IHdvdWxkIGluY29ycmVjdGx5IGNvbGxhcHNlIGRpc3RpbmN0XG4gICAgLy8gcmVzb3VyY2VzIHRoYXQgbWVyZWx5IHNoYXJlIHRoZSB2YWx1ZSBvZiB0aGVpciBmaXJzdCBpZGVudGlmaWVyIGNvbXBvbmVudC5cbiAgICBjb25zdCBzb3J0ZWRJZGVudGlmaWVyRW50cmllcyA9IE9iamVjdC5lbnRyaWVzKHJlc291cmNlLlJlc291cmNlSWRlbnRpZmllciEpLnNvcnQoKFthXSwgW2JdKSA9PiBhLmxvY2FsZUNvbXBhcmUoYikpO1xuICAgIGNvbnN0IHVuaXF1ZUlkZW50aWZlciA9IGAke3Jlc291cmNlLlJlc291cmNlVHlwZX06JHtzb3J0ZWRJZGVudGlmaWVyRW50cmllcy5tYXAoKFtrLCB2XSkgPT4gYCR7a309JHt2fWApLmpvaW4oJywnKX1gO1xuICAgIHVuaXF1ZVJlc291cmNlc1t1bmlxdWVJZGVudGlmZXJdID0gcmVzb3VyY2U7XG4gIH1cblxuICByZXR1cm4gT2JqZWN0LnZhbHVlcyh1bmlxdWVSZXNvdXJjZXMpO1xufVxuXG4vKipcbiAqIENsYXNzIGZvciBtYWtpbmcgQ2xvdWRGb3JtYXRpb24gdGVtcGxhdGUgZ2VuZXJhdG9yIGNhbGxzXG4gKi9cbmV4cG9ydCBjbGFzcyBDZm5UZW1wbGF0ZUdlbmVyYXRvclByb3ZpZGVyIHtcbiAgcHJpdmF0ZSBjZm46IElDbG91ZEZvcm1hdGlvbkNsaWVudDtcbiAgcHJpdmF0ZSBpb0hlbHBlcjogSW9IZWxwZXI7XG4gIGNvbnN0cnVjdG9yKGNmbjogSUNsb3VkRm9ybWF0aW9uQ2xpZW50LCBpb0hlbHBlcjogSW9IZWxwZXIpIHtcbiAgICB0aGlzLmNmbiA9IGNmbjtcbiAgICB0aGlzLmlvSGVscGVyID0gaW9IZWxwZXI7XG4gIH1cblxuICBhc3luYyBjaGVja0ZvclJlc291cmNlU2NhbihcbiAgICByZXNvdXJjZVNjYW5TdW1tYXJpZXM6IFJlc291cmNlU2NhblN1bW1hcnlbXSB8IHVuZGVmaW5lZCxcbiAgICBvcHRpb25zOiBHZW5lcmF0ZVRlbXBsYXRlT3B0aW9ucyxcbiAgICBjbGllbnRSZXF1ZXN0VG9rZW46IHN0cmluZyxcbiAgKSB7XG4gICAgaWYgKCFyZXNvdXJjZVNjYW5TdW1tYXJpZXMgfHwgcmVzb3VyY2VTY2FuU3VtbWFyaWVzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgaWYgKG9wdGlvbnMuZnJvbVNjYW4gPT09IEZyb21TY2FuLk1PU1RfUkVDRU5UKSB7XG4gICAgICAgIHRocm93IG5ldyBUb29sa2l0RXJyb3IoXG4gICAgICAgICAgJ05vU2NhbnNGb3VuZCcsXG4gICAgICAgICAgJ05vIHNjYW5zIGZvdW5kLiBQbGVhc2UgZWl0aGVyIHN0YXJ0IGEgbmV3IHNjYW4gd2l0aCB0aGUgYC0tZnJvbS1zY2FuYCBuZXcgb3IgZG8gbm90IHNwZWNpZnkgYSBgLS1mcm9tLXNjYW5gIG9wdGlvbi4nLFxuICAgICAgICApO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgYXdhaXQgdGhpcy5pb0hlbHBlci5kZWZhdWx0cy5pbmZvKCdObyBzY2FucyBmb3VuZC4gSW5pdGlhdGluZyBhIG5ldyByZXNvdXJjZSBzY2FuLicpO1xuICAgICAgICBhd2FpdCB0aGlzLnN0YXJ0UmVzb3VyY2VTY2FuKGNsaWVudFJlcXVlc3RUb2tlbik7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJldHJpZXZlcyBhIHRva2VuaXplZCBsaXN0IG9mIHJlc291cmNlcyBhbmQgdGhlaXIgYXNzb2NpYXRlZCBzY2FuLiBJZiBhIHRva2VuIGlzIHByZXNlbnQgdGhlIGZ1bmN0aW9uXG4gICAqIHdpbGwgbG9vcCB0aHJvdWdoIGFsbCBwYWdlcyBhbmQgY29tYmluZSB0aGVtIGludG8gYSBzaW5nbGUgbGlzdCBvZiBTY2FubmVkUmVsYXRlZFJlc291cmNlc1xuICAgKlxuICAgKiBAcGFyYW0gc2NhbklkIC0gc2NhbiBpZCBmb3IgdGhlIHRvIGxpc3QgcmVzb3VyY2VzIGZvclxuICAgKiBAcGFyYW0gcmVzb3VyY2VzIC0gQSBsaXN0IG9mIHJlc291cmNlcyB0byBmaW5kIHJlbGF0ZWQgcmVzb3VyY2VzIGZvclxuICAgKi9cbiAgYXN5bmMgZ2V0UmVzb3VyY2VTY2FuUmVsYXRlZFJlc291cmNlcyhcbiAgICBzY2FuSWQ6IHN0cmluZyxcbiAgICByZXNvdXJjZXM6IFNjYW5uZWRSZXNvdXJjZVtdLFxuICApOiBQcm9taXNlPFNjYW5uZWRSZXNvdXJjZUlkZW50aWZpZXJbXT4ge1xuICAgIGxldCByZWxhdGVkUmVzb3VyY2VMaXN0ID0gcmVzb3VyY2VzO1xuXG4gICAgLy8gYnJlYWsgdGhlIGxpc3Qgb2YgcmVzb3VyY2VzIGludG8gY2h1bmtzIG9mIDEwMCB0byBhdm9pZCBoaXR0aW5nIHRoZSAxMDAgcmVzb3VyY2UgbGltaXRcbiAgICBmb3IgKGNvbnN0IGNodW5rIG9mIGNodW5rcyhyZXNvdXJjZXMsIDEwMCkpIHtcbiAgICAgIC8vIGdldCB0aGUgZmlyc3QgcGFnZSBvZiByZWxhdGVkIHJlc291cmNlc1xuICAgICAgY29uc3QgcmVzID0gYXdhaXQgdGhpcy5jZm4ubGlzdFJlc291cmNlU2NhblJlbGF0ZWRSZXNvdXJjZXMoe1xuICAgICAgICBSZXNvdXJjZVNjYW5JZDogc2NhbklkLFxuICAgICAgICBSZXNvdXJjZXM6IGNodW5rLFxuICAgICAgfSk7XG5cbiAgICAgIC8vIGFkZCB0aGUgZmlyc3QgcGFnZSB0byB0aGUgbGlzdFxuICAgICAgcmVsYXRlZFJlc291cmNlTGlzdC5wdXNoKC4uLihyZXMuUmVsYXRlZFJlc291cmNlcyA/PyBbXSkpO1xuICAgICAgbGV0IG5leHRUb2tlbiA9IHJlcy5OZXh0VG9rZW47XG5cbiAgICAgIC8vIGlmIHRoZXJlIGFyZSBtb3JlIHBhZ2VzLCBjeWNsZSB0aHJvdWdoIHRoZW0gYW5kIGFkZCB0aGVtIHRvIHRoZSBsaXN0IGJlZm9yZSBtb3Zpbmcgb24gdG8gdGhlIG5leHQgY2h1bmtcbiAgICAgIHdoaWxlIChuZXh0VG9rZW4pIHtcbiAgICAgICAgY29uc3QgbmV4dFJlbGF0ZWRSZXNvdXJjZXMgPSBhd2FpdCB0aGlzLmNmbi5saXN0UmVzb3VyY2VTY2FuUmVsYXRlZFJlc291cmNlcyh7XG4gICAgICAgICAgUmVzb3VyY2VTY2FuSWQ6IHNjYW5JZCxcbiAgICAgICAgICBSZXNvdXJjZXM6IHJlc291cmNlSWRlbnRpZmllcnMocmVzb3VyY2VzKSxcbiAgICAgICAgICBOZXh0VG9rZW46IG5leHRUb2tlbixcbiAgICAgICAgfSk7XG4gICAgICAgIG5leHRUb2tlbiA9IG5leHRSZWxhdGVkUmVzb3VyY2VzLk5leHRUb2tlbjtcbiAgICAgICAgcmVsYXRlZFJlc291cmNlTGlzdC5wdXNoKC4uLihuZXh0UmVsYXRlZFJlc291cmNlcy5SZWxhdGVkUmVzb3VyY2VzID8/IFtdKSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmVsYXRlZFJlc291cmNlTGlzdCA9IGRlZHVwbGljYXRlUmVzb3VyY2VzKHJlbGF0ZWRSZXNvdXJjZUxpc3QpO1xuXG4gICAgLy8gcHJ1bmUgdGhlIG1hbmFnZWRieXN0YWNrIGZsYWcgb2ZmIG9mIHRoZW0gYWdhaW4uXG4gICAgcmV0dXJuIHByb2Nlc3MuZW52Lk1JR1JBVEVfSU5URUdfVEVTVFxuICAgICAgPyByZXNvdXJjZUlkZW50aWZpZXJzKHJlbGF0ZWRSZXNvdXJjZUxpc3QpXG4gICAgICA6IHJlc291cmNlSWRlbnRpZmllcnMoZXhjbHVkZU1hbmFnZWQocmVsYXRlZFJlc291cmNlTGlzdCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIEtpY2tzIG9mZiBhIHNjYW4gb2YgYSBjdXN0b21lcnMgYWNjb3VudCwgcmV0dXJuaW5nIHRoZSBzY2FuIGlkLiBBIHNjYW4gY2FuIHRha2VcbiAgICogMTAgbWludXRlcyBvciBsb25nZXIgdG8gY29tcGxldGUuIEhvd2V2ZXIgdGhpcyB3aWxsIHJldHVybiBhIHNjYW4gaWQgYXMgc29vbiBhc1xuICAgKiB0aGUgc2NhbiBoYXMgYmVndW4uXG4gICAqXG4gICAqIEByZXR1cm5zIEEgc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgc2NhbiBpZFxuICAgKi9cbiAgYXN5bmMgc3RhcnRSZXNvdXJjZVNjYW4ocmVxdWVzdFRva2VuOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gKFxuICAgICAgYXdhaXQgdGhpcy5jZm4uc3RhcnRSZXNvdXJjZVNjYW4oe1xuICAgICAgICBDbGllbnRSZXF1ZXN0VG9rZW46IHJlcXVlc3RUb2tlbixcbiAgICAgIH0pXG4gICAgKS5SZXNvdXJjZVNjYW5JZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXRzIHRoZSBtb3N0IHJlY2VudCBzY2FucyBhIGN1c3RvbWVyIGhhcyBjb21wbGV0ZWRcbiAgICpcbiAgICogQHJldHVybnMgYSBsaXN0IG9mIHJlc291cmNlIHNjYW4gc3VtbWFyaWVzXG4gICAqL1xuICBhc3luYyBsaXN0UmVzb3VyY2VTY2FucygpIHtcbiAgICByZXR1cm4gdGhpcy5jZm4ubGlzdFJlc291cmNlU2NhbnMoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXRyaWV2ZXMgYSB0b2tlbml6ZWQgbGlzdCBvZiByZXNvdXJjZXMgZnJvbSBhIHJlc291cmNlIHNjYW4uIElmIGEgdG9rZW4gaXMgcHJlc2VudCwgdGhpcyBmdW5jdGlvblxuICAgKiB3aWxsIGxvb3AgdGhyb3VnaCBhbGwgcGFnZXMgYW5kIGNvbWJpbmUgdGhlbSBpbnRvIGEgc2luZ2xlIGxpc3Qgb2YgU2Nhbm5lZFJlc291cmNlW10uXG4gICAqIEFkZGl0aW9uYWxseSB3aWxsIGFwcGx5IGFueSBmaWx0ZXJzIHByb3ZpZGVkIGJ5IHRoZSBjdXN0b21lci5cbiAgICpcbiAgICogQHBhcmFtIHNjYW5JZCAtIHNjYW4gaWQgZm9yIHRoZSB0byBsaXN0IHJlc291cmNlcyBmb3JcbiAgICogQHBhcmFtIGZpbHRlcnMgLSBhIHN0cmluZyBvZiBmaWx0ZXJzIGluIHRoZSBmb3JtYXQgb2Yga2V5MT12YWx1ZTEsa2V5Mj12YWx1ZTJcbiAgICogQHJldHVybnMgYSBjb21iaW5lZCBsaXN0IG9mIGFsbCByZXNvdXJjZXMgZnJvbSB0aGUgc2NhblxuICAgKi9cbiAgYXN5bmMgbGlzdFJlc291cmNlU2NhblJlc291cmNlcyhzY2FuSWQ6IHN0cmluZywgZmlsdGVyczogc3RyaW5nW10gPSBbXSk6IFByb21pc2U8U2Nhbm5lZFJlc291cmNlSWRlbnRpZmllcltdPiB7XG4gICAgbGV0IHJlc291cmNlTGlzdDogU2Nhbm5lZFJlc291cmNlW10gPSBbXTtcbiAgICBsZXQgcmVzb3VyY2VTY2FuSW5wdXRzOiBMaXN0UmVzb3VyY2VTY2FuUmVzb3VyY2VzQ29tbWFuZElucHV0O1xuXG4gICAgaWYgKGZpbHRlcnMubGVuZ3RoID4gMCkge1xuICAgICAgYXdhaXQgdGhpcy5pb0hlbHBlci5kZWZhdWx0cy5pbmZvKCdBcHBseWluZyBmaWx0ZXJzIHRvIHJlc291cmNlIHNjYW4uJyk7XG4gICAgICBmb3IgKGNvbnN0IGZpbHRlciBvZiBmaWx0ZXJzKSB7XG4gICAgICAgIGNvbnN0IGZpbHRlckxpc3QgPSBwYXJzZUZpbHRlcnMoZmlsdGVyKTtcbiAgICAgICAgcmVzb3VyY2VTY2FuSW5wdXRzID0ge1xuICAgICAgICAgIFJlc291cmNlU2NhbklkOiBzY2FuSWQsXG4gICAgICAgICAgUmVzb3VyY2VJZGVudGlmaWVyOiBmaWx0ZXJMaXN0W0ZpbHRlclR5cGUuUkVTT1VSQ0VfSURFTlRJRklFUl0sXG4gICAgICAgICAgUmVzb3VyY2VUeXBlUHJlZml4OiBmaWx0ZXJMaXN0W0ZpbHRlclR5cGUuUkVTT1VSQ0VfVFlQRV9QUkVGSVhdLFxuICAgICAgICAgIFRhZ0tleTogZmlsdGVyTGlzdFtGaWx0ZXJUeXBlLlRBR19LRVldLFxuICAgICAgICAgIFRhZ1ZhbHVlOiBmaWx0ZXJMaXN0W0ZpbHRlclR5cGUuVEFHX1ZBTFVFXSxcbiAgICAgICAgfTtcbiAgICAgICAgY29uc3QgcmVzb3VyY2VzID0gYXdhaXQgdGhpcy5jZm4ubGlzdFJlc291cmNlU2NhblJlc291cmNlcyhyZXNvdXJjZVNjYW5JbnB1dHMpO1xuICAgICAgICByZXNvdXJjZUxpc3QgPSByZXNvdXJjZUxpc3QuY29uY2F0KHJlc291cmNlcy5SZXNvdXJjZXMgPz8gW10pO1xuICAgICAgICBsZXQgbmV4dFRva2VuID0gcmVzb3VyY2VzLk5leHRUb2tlbjtcblxuICAgICAgICAvLyBjeWNsZSB0aHJvdWdoIHRoZSBwYWdlcyBhZGRpbmcgYWxsIHJlc291cmNlcyB0byB0aGUgbGlzdCB1bnRpbCB3ZSBydW4gb3V0IG9mIHBhZ2VzXG4gICAgICAgIHdoaWxlIChuZXh0VG9rZW4pIHtcbiAgICAgICAgICByZXNvdXJjZVNjYW5JbnB1dHMuTmV4dFRva2VuID0gbmV4dFRva2VuO1xuICAgICAgICAgIGNvbnN0IG5leHRSZXNvdXJjZXMgPSBhd2FpdCB0aGlzLmNmbi5saXN0UmVzb3VyY2VTY2FuUmVzb3VyY2VzKHJlc291cmNlU2NhbklucHV0cyk7XG4gICAgICAgICAgbmV4dFRva2VuID0gbmV4dFJlc291cmNlcy5OZXh0VG9rZW47XG4gICAgICAgICAgcmVzb3VyY2VMaXN0ID0gcmVzb3VyY2VMaXN0IS5jb25jYXQobmV4dFJlc291cmNlcy5SZXNvdXJjZXMgPz8gW10pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGF3YWl0IHRoaXMuaW9IZWxwZXIuZGVmYXVsdHMuaW5mbygnTm8gZmlsdGVycyBwcm92aWRlZC4gUmV0cmlldmluZyBhbGwgcmVzb3VyY2VzIGZyb20gc2Nhbi4nKTtcbiAgICAgIHJlc291cmNlU2NhbklucHV0cyA9IHtcbiAgICAgICAgUmVzb3VyY2VTY2FuSWQ6IHNjYW5JZCxcbiAgICAgIH07XG4gICAgICBjb25zdCByZXNvdXJjZXMgPSBhd2FpdCB0aGlzLmNmbi5saXN0UmVzb3VyY2VTY2FuUmVzb3VyY2VzKHJlc291cmNlU2NhbklucHV0cyk7XG4gICAgICByZXNvdXJjZUxpc3QgPSByZXNvdXJjZUxpc3QhLmNvbmNhdChyZXNvdXJjZXMuUmVzb3VyY2VzID8/IFtdKTtcbiAgICAgIGxldCBuZXh0VG9rZW4gPSByZXNvdXJjZXMuTmV4dFRva2VuO1xuXG4gICAgICAvLyBjeWNsZSB0aHJvdWdoIHRoZSBwYWdlcyBhZGRpbmcgYWxsIHJlc291cmNlcyB0byB0aGUgbGlzdCB1bnRpbCB3ZSBydW4gb3V0IG9mIHBhZ2VzXG4gICAgICB3aGlsZSAobmV4dFRva2VuKSB7XG4gICAgICAgIHJlc291cmNlU2NhbklucHV0cy5OZXh0VG9rZW4gPSBuZXh0VG9rZW47XG4gICAgICAgIGNvbnN0IG5leHRSZXNvdXJjZXMgPSBhd2FpdCB0aGlzLmNmbi5saXN0UmVzb3VyY2VTY2FuUmVzb3VyY2VzKHJlc291cmNlU2NhbklucHV0cyk7XG4gICAgICAgIG5leHRUb2tlbiA9IG5leHRSZXNvdXJjZXMuTmV4dFRva2VuO1xuICAgICAgICByZXNvdXJjZUxpc3QgPSByZXNvdXJjZUxpc3QhLmNvbmNhdChuZXh0UmVzb3VyY2VzLlJlc291cmNlcyA/PyBbXSk7XG4gICAgICB9XG4gICAgfVxuICAgIGlmIChyZXNvdXJjZUxpc3QubGVuZ3RoID09PSAwKSB7XG4gICAgICB0aHJvdyBuZXcgVG9vbGtpdEVycm9yKCdOb1Jlc291cmNlc0ZvdW5kJywgYE5vIHJlc291cmNlcyBmb3VuZCB3aXRoIGZpbHRlcnMgJHtmaWx0ZXJzLmpvaW4oJyAnKX0uIFBsZWFzZSB0cnkgYWdhaW4gd2l0aCBkaWZmZXJlbnQgZmlsdGVycy5gKTtcbiAgICB9XG4gICAgcmVzb3VyY2VMaXN0ID0gZGVkdXBsaWNhdGVSZXNvdXJjZXMocmVzb3VyY2VMaXN0KTtcblxuICAgIHJldHVybiBwcm9jZXNzLmVudi5NSUdSQVRFX0lOVEVHX1RFU1RcbiAgICAgID8gcmVzb3VyY2VJZGVudGlmaWVycyhyZXNvdXJjZUxpc3QpXG4gICAgICA6IHJlc291cmNlSWRlbnRpZmllcnMoZXhjbHVkZU1hbmFnZWQocmVzb3VyY2VMaXN0KSk7XG4gIH1cblxuICAvKipcbiAgICogUmV0cmlldmVzIGluZm9ybWF0aW9uIGFib3V0IGEgcmVzb3VyY2Ugc2Nhbi5cbiAgICpcbiAgICogQHBhcmFtIHNjYW5JZCAtIHNjYW4gaWQgZm9yIHRoZSB0byBsaXN0IHJlc291cmNlcyBmb3JcbiAgICogQHJldHVybnMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHNjYW5cbiAgICovXG4gIGFzeW5jIGRlc2NyaWJlUmVzb3VyY2VTY2FuKHNjYW5JZDogc3RyaW5nKTogUHJvbWlzZTxEZXNjcmliZVJlc291cmNlU2NhbkNvbW1hbmRPdXRwdXQ+IHtcbiAgICByZXR1cm4gdGhpcy5jZm4uZGVzY3JpYmVSZXNvdXJjZVNjYW4oe1xuICAgICAgUmVzb3VyY2VTY2FuSWQ6IHNjYW5JZCxcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBEZXNjcmliZXMgdGhlIGN1cnJlbnQgc3RhdHVzIG9mIHRoZSB0ZW1wbGF0ZSBiZWluZyBnZW5lcmF0ZWQuXG4gICAqXG4gICAqIEBwYXJhbSB0ZW1wbGF0ZUlkIC0gQSBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSB0ZW1wbGF0ZSBpZFxuICAgKiBAcmV0dXJucyBEZXNjcmliZUdlbmVyYXRlZFRlbXBsYXRlT3V0cHV0IGFuIG9iamVjdCBjb250YWluaW5nIHRoZSB0ZW1wbGF0ZSBzdGF0dXMgYW5kIHJlc3VsdHNcbiAgICovXG4gIGFzeW5jIGRlc2NyaWJlR2VuZXJhdGVkVGVtcGxhdGUodGVtcGxhdGVJZDogc3RyaW5nKTogUHJvbWlzZTxEZXNjcmliZUdlbmVyYXRlZFRlbXBsYXRlQ29tbWFuZE91dHB1dD4ge1xuICAgIGNvbnN0IGdlbmVyYXRlZFRlbXBsYXRlID0gYXdhaXQgdGhpcy5jZm4uZGVzY3JpYmVHZW5lcmF0ZWRUZW1wbGF0ZSh7XG4gICAgICBHZW5lcmF0ZWRUZW1wbGF0ZU5hbWU6IHRlbXBsYXRlSWQsXG4gICAgfSk7XG5cbiAgICBpZiAoZ2VuZXJhdGVkVGVtcGxhdGUuU3RhdHVzID09IFNjYW5TdGF0dXMuRkFJTEVEKSB7XG4gICAgICB0aHJvdyBuZXcgVG9vbGtpdEVycm9yKCdUZW1wbGF0ZUdlbmVyYXRpb25GYWlsZWQnLCBnZW5lcmF0ZWRUZW1wbGF0ZS5TdGF0dXNSZWFzb24hKTtcbiAgICB9XG5cbiAgICByZXR1cm4gZ2VuZXJhdGVkVGVtcGxhdGU7XG4gIH1cblxuICAvKipcbiAgICogUmV0cmlldmVzIGEgY29tcGxldGVkIGdlbmVyYXRlZCBjbG91ZGZvcm1hdGlvbiB0ZW1wbGF0ZSBmcm9tIHRoZSB0ZW1wbGF0ZSBnZW5lcmF0b3IuXG4gICAqXG4gICAqIEBwYXJhbSB0ZW1wbGF0ZUlkIC0gQSBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSB0ZW1wbGF0ZSBpZFxuICAgKiBAcGFyYW0gY2xvdWRGb3JtYXRpb24gLSBUaGUgQ2xvdWRGb3JtYXRpb24gc2RrIGNsaWVudCB0byB1c2VcbiAgICogQHJldHVybnMgRGVzY3JpYmVHZW5lcmF0ZWRUZW1wbGF0ZU91dHB1dCBhbiBvYmplY3QgY29udGFpbmluZyB0aGUgdGVtcGxhdGUgc3RhdHVzIGFuZCBib2R5XG4gICAqL1xuICBhc3luYyBnZXRHZW5lcmF0ZWRUZW1wbGF0ZSh0ZW1wbGF0ZUlkOiBzdHJpbmcpOiBQcm9taXNlPEdldEdlbmVyYXRlZFRlbXBsYXRlQ29tbWFuZE91dHB1dD4ge1xuICAgIHJldHVybiB0aGlzLmNmbi5nZXRHZW5lcmF0ZWRUZW1wbGF0ZSh7XG4gICAgICBHZW5lcmF0ZWRUZW1wbGF0ZU5hbWU6IHRlbXBsYXRlSWQsXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogS2lja3Mgb2ZmIGEgdGVtcGxhdGUgZ2VuZXJhdGlvbiBmb3IgYSBzZXQgb2YgcmVzb3VyY2VzLlxuICAgKlxuICAgKiBAcGFyYW0gc3RhY2tOYW1lIC0gVGhlIG5hbWUgb2YgdGhlIHN0YWNrXG4gICAqIEBwYXJhbSByZXNvdXJjZXMgLSBBIGxpc3Qgb2YgcmVzb3VyY2VzIHRvIGdlbmVyYXRlIHRoZSB0ZW1wbGF0ZSBmcm9tXG4gICAqIEByZXR1cm5zIENyZWF0ZUdlbmVyYXRlZFRlbXBsYXRlT3V0cHV0IGFuIG9iamVjdCBjb250YWluaW5nIHRoZSB0ZW1wbGF0ZSBhcm4gdG8gcXVlcnkgb24gbGF0ZXJcbiAgICovXG4gIGFzeW5jIGNyZWF0ZUdlbmVyYXRlZFRlbXBsYXRlKHN0YWNrTmFtZTogc3RyaW5nLCByZXNvdXJjZXM6IFJlc291cmNlRGVmaW5pdGlvbltdKSB7XG4gICAgY29uc3QgY3JlYXRlVGVtcGxhdGVPdXRwdXQgPSBhd2FpdCB0aGlzLmNmbi5jcmVhdGVHZW5lcmF0ZWRUZW1wbGF0ZSh7XG4gICAgICBSZXNvdXJjZXM6IHJlc291cmNlcyxcbiAgICAgIEdlbmVyYXRlZFRlbXBsYXRlTmFtZTogc3RhY2tOYW1lLFxuICAgIH0pO1xuXG4gICAgaWYgKGNyZWF0ZVRlbXBsYXRlT3V0cHV0LkdlbmVyYXRlZFRlbXBsYXRlSWQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgdGhyb3cgbmV3IFRvb2xraXRFcnJvcignTWlzc2luZ1RlbXBsYXRlQXJuJywgJ0NyZWF0ZUdlbmVyYXRlZFRlbXBsYXRlIGZhaWxlZCB0byByZXR1cm4gYW4gQXJuLicpO1xuICAgIH1cbiAgICByZXR1cm4gY3JlYXRlVGVtcGxhdGVPdXRwdXQ7XG4gIH1cblxuICAvKipcbiAgICogRGVsZXRlcyBhIGdlbmVyYXRlZCB0ZW1wbGF0ZSBmcm9tIHRoZSB0ZW1wbGF0ZSBnZW5lcmF0b3IuXG4gICAqXG4gICAqIEBwYXJhbSB0ZW1wbGF0ZUFybiAtIFRoZSBhcm4gb2YgdGhlIHRlbXBsYXRlIHRvIGRlbGV0ZVxuICAgKiBAcmV0dXJucyBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSB0ZW1wbGF0ZSBoYXMgYmVlbiBkZWxldGVkXG4gICAqL1xuICBhc3luYyBkZWxldGVHZW5lcmF0ZWRUZW1wbGF0ZSh0ZW1wbGF0ZUFybjogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgdGhpcy5jZm4uZGVsZXRlR2VuZXJhdGVkVGVtcGxhdGUoe1xuICAgICAgR2VuZXJhdGVkVGVtcGxhdGVOYW1lOiB0ZW1wbGF0ZUFybixcbiAgICB9KTtcbiAgfVxufVxuXG4vKipcbiAqIFRoZSBwb3NzaWJsZSB3YXlzIHRvIGNob29zZSBhIHNjYW4gdG8gZ2VuZXJhdGUgYSBDREsgYXBwbGljYXRpb24gZnJvbVxuICovXG5leHBvcnQgZW51bSBGcm9tU2NhbiB7XG4gIC8qKlxuICAgKiBJbml0aWF0ZSBhIG5ldyByZXNvdXJjZSBzY2FuIHRvIGJ1aWxkIHRoZSBDREsgYXBwbGljYXRpb24gZnJvbS5cbiAgICovXG4gIE5FVyxcblxuICAvKipcbiAgICogVXNlIHRoZSBsYXN0IHN1Y2Nlc3NmdWwgc2NhbiB0byBidWlsZCB0aGUgQ0RLIGFwcGxpY2F0aW9uIGZyb20uIFdpbGwgZmFpbCBpZiBubyBzY2FuIGlzIGZvdW5kLlxuICAgKi9cbiAgTU9TVF9SRUNFTlQsXG5cbiAgLyoqXG4gICAqIFN0YXJ0cyBhIHNjYW4gaWYgbm9uZSBleGlzdHMsIG90aGVyd2lzZSB1c2VzIHRoZSBtb3N0IHJlY2VudCBzdWNjZXNzZnVsIHNjYW4gdG8gYnVpbGQgdGhlIENESyBhcHBsaWNhdGlvbiBmcm9tLlxuICAgKi9cbiAgREVGQVVMVCxcbn1cblxuLyoqXG4gKiBJbnRlcmZhY2UgZm9yIHRoZSBvcHRpb25zIG9iamVjdCBwYXNzZWQgdG8gdGhlIGdlbmVyYXRlVGVtcGxhdGUgZnVuY3Rpb25cbiAqXG4gKiBAcGFyYW0gc3RhY2tOYW1lIC0gVGhlIG5hbWUgb2YgdGhlIHN0YWNrXG4gKiBAcGFyYW0gZmlsdGVycyAtIEEgbGlzdCBvZiBmaWx0ZXJzIHRvIGFwcGx5IHRvIHRoZSBzY2FuXG4gKiBAcGFyYW0gZnJvbVNjYW4gLSBBbiBlbnVtIHZhbHVlIHNwZWNpZnlpbmcgd2hldGhlciBhIG5ldyBzY2FuIHNob3VsZCBiZSBzdGFydGVkIG9yIHRoZSBtb3N0IHJlY2VudCBzdWNjZXNzZnVsIHNjYW4gc2hvdWxkIGJlIHVzZWRcbiAqIEBwYXJhbSBzZGtQcm92aWRlciAtIFRoZSBzZGsgcHJvdmlkZXIgZm9yIG1ha2luZyBDbG91ZEZvcm1hdGlvbiBjYWxsc1xuICogQHBhcmFtIGVudmlyb25tZW50IC0gVGhlIGFjY291bnQgYW5kIHJlZ2lvbiB3aGVyZSB0aGUgc3RhY2sgaXMgZGVwbG95ZWRcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBHZW5lcmF0ZVRlbXBsYXRlT3B0aW9ucyB7XG4gIHN0YWNrTmFtZTogc3RyaW5nO1xuICBmaWx0ZXJzPzogc3RyaW5nW107XG4gIGZyb21TY2FuPzogRnJvbVNjYW47XG4gIHNka1Byb3ZpZGVyOiBTZGtQcm92aWRlcjtcbiAgZW52aXJvbm1lbnQ6IEVudmlyb25tZW50O1xuICBpb0hlbHBlcjogSW9IZWxwZXI7XG59XG5cbi8qKlxuICogSW50ZXJmYWNlIGZvciB0aGUgb3V0cHV0IG9mIHRoZSBnZW5lcmF0ZVRlbXBsYXRlIGZ1bmN0aW9uXG4gKlxuICogQHBhcmFtIG1pZ3JhdGVKc29uIC0gVGhlIGdlbmVyYXRlZCBNaWdyYXRlLmpzb24gZmlsZVxuICogQHBhcmFtIHJlc291cmNlcyAtIFRoZSBnZW5lcmF0ZWQgdGVtcGxhdGVcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBHZW5lcmF0ZVRlbXBsYXRlT3V0cHV0IHtcbiAgbWlncmF0ZUpzb246IE1pZ3JhdGVKc29uRm9ybWF0O1xuICByZXNvdXJjZXM/OiBSZXNvdXJjZURldGFpbFtdO1xuICB0ZW1wbGF0ZUlkPzogc3RyaW5nO1xufVxuXG4vKipcbiAqIEludGVyZmFjZSBkZWZpbmluZyB0aGUgZm9ybWF0IG9mIHRoZSBnZW5lcmF0ZWQgTWlncmF0ZS5qc29uIGZpbGVcbiAqXG4gKiBAcGFyYW0gVGVtcGxhdGVCb2R5IC0gVGhlIGdlbmVyYXRlZCB0ZW1wbGF0ZVxuICogQHBhcmFtIFNvdXJjZSAtIFRoZSBzb3VyY2Ugb2YgdGhlIHRlbXBsYXRlXG4gKiBAcGFyYW0gUmVzb3VyY2VzIC0gQSBsaXN0IG9mIHJlc291cmNlcyB0aGF0IHdlcmUgdXNlZCB0byBnZW5lcmF0ZSB0aGUgdGVtcGxhdGVcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBNaWdyYXRlSnNvbkZvcm1hdCB7XG4gIHRlbXBsYXRlQm9keTogc3RyaW5nO1xuICBzb3VyY2U6IHN0cmluZztcbiAgcmVzb3VyY2VzPzogR2VuZXJhdGVkUmVzb3VyY2VJbXBvcnRJZGVudGlmaWVyW107XG59XG5cbi8qKlxuICogSW50ZXJmYWNlIHJlcHJlc2VudGluZyB0aGUgZm9ybWF0IG9mIGEgcmVzb3VyY2UgaWRlbnRpZmllciByZXF1aXJlZCBmb3IgcmVzb3VyY2UgaW1wb3J0XG4gKlxuICogQHBhcmFtIFJlc291cmNlVHlwZSAtIFRoZSB0eXBlIG9mIHJlc291cmNlXG4gKiBAcGFyYW0gTG9naWNhbFJlc291cmNlSWQgLSBUaGUgbG9naWNhbCBpZCBvZiB0aGUgcmVzb3VyY2VcbiAqIEBwYXJhbSBSZXNvdXJjZUlkZW50aWZpZXIgLSBUaGUgcmVzb3VyY2UgaWRlbnRpZmllciBvZiB0aGUgcmVzb3VyY2VcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBHZW5lcmF0ZWRSZXNvdXJjZUltcG9ydElkZW50aWZpZXIge1xuICAvLyBjZGsgZGVwbG95IGV4cGVjdHMgdGhlIG1pZ3JhdGUuanNvbiByZXNvdXJjZSBpZGVudGlmaWVycyB0byBiZSBQYXNjYWxDYXNlLCBub3QgY2FtZWxDYXNlLlxuICBSZXNvdXJjZVR5cGU6IHN0cmluZztcbiAgTG9naWNhbFJlc291cmNlSWQ6IHN0cmluZztcbiAgUmVzb3VyY2VJZGVudGlmaWVyOiBSZXNvdXJjZUlkZW50aWZpZXJTdW1tYXJ5O1xufVxuIl19