UNPKG

aws-cdk

Version:

CDK Toolkit, the command line tool for CDK apps

323 lines 50.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isHotswappableLambdaFunctionChange = void 0; const stream_1 = require("stream"); const AWS = require("aws-sdk"); const common_1 = require("./common"); const util_1 = require("../../util"); const evaluate_cloudformation_template_1 = require("../evaluate-cloudformation-template"); // namespace object imports won't work in the bundle for function exports // eslint-disable-next-line @typescript-eslint/no-require-imports const archiver = require('archiver'); async function isHotswappableLambdaFunctionChange(logicalId, change, evaluateCfnTemplate) { // if the change is for a Lambda Version, // ignore it by returning an empty hotswap operation - // we will publish a new version when we get to hotswapping the actual Function this Version points to, below // (Versions can't be changed in CloudFormation anyway, they're immutable) if (change.newValue.Type === 'AWS::Lambda::Version') { return [{ hotswappable: true, resourceType: 'AWS::Lambda::Version', resourceNames: [], propsChanged: [], service: 'lambda', apply: async (_sdk) => { }, }]; } // we handle Aliases specially too if (change.newValue.Type === 'AWS::Lambda::Alias') { return classifyAliasChanges(change); } if (change.newValue.Type !== 'AWS::Lambda::Function') { return []; } const ret = []; const classifiedChanges = (0, common_1.classifyChanges)(change, ['Code', 'Environment', 'Description']); classifiedChanges.reportNonHotswappablePropertyChanges(ret); const functionName = await evaluateCfnTemplate.establishResourcePhysicalName(logicalId, change.newValue.Properties?.FunctionName); const namesOfHotswappableChanges = Object.keys(classifiedChanges.hotswappableProps); if (namesOfHotswappableChanges.length > 0) { ret.push({ hotswappable: true, resourceType: change.newValue.Type, propsChanged: namesOfHotswappableChanges, service: 'lambda', resourceNames: [ `Lambda Function '${functionName}'`, // add Version here if we're publishing a new one ...await renderVersions(logicalId, evaluateCfnTemplate, [`Lambda Version for Function '${functionName}'`]), // add any Aliases that we are hotswapping here ...await renderAliases(logicalId, evaluateCfnTemplate, async (alias) => `Lambda Alias '${alias}' for Function '${functionName}'`), ], apply: async (sdk) => { const lambdaCodeChange = await evaluateLambdaFunctionProps(classifiedChanges.hotswappableProps, change.newValue.Properties?.Runtime, evaluateCfnTemplate); if (lambdaCodeChange === undefined) { return; } if (!functionName) { return; } const { versionsReferencingFunction, aliasesNames } = await versionsAndAliases(logicalId, evaluateCfnTemplate); const lambda = sdk.lambda(); const operations = []; if (lambdaCodeChange.code !== undefined || lambdaCodeChange.configurations !== undefined) { if (lambdaCodeChange.code !== undefined) { const updateFunctionCodeResponse = await lambda.updateFunctionCode({ FunctionName: functionName, S3Bucket: lambdaCodeChange.code.s3Bucket, S3Key: lambdaCodeChange.code.s3Key, ImageUri: lambdaCodeChange.code.imageUri, ZipFile: lambdaCodeChange.code.functionCodeZip, S3ObjectVersion: lambdaCodeChange.code.s3ObjectVersion, }).promise(); await waitForLambdasPropertiesUpdateToFinish(updateFunctionCodeResponse, lambda, functionName); } if (lambdaCodeChange.configurations !== undefined) { const updateRequest = { FunctionName: functionName, }; if (lambdaCodeChange.configurations.description !== undefined) { updateRequest.Description = lambdaCodeChange.configurations.description; } if (lambdaCodeChange.configurations.environment !== undefined) { updateRequest.Environment = lambdaCodeChange.configurations.environment; } const updateFunctionCodeResponse = await lambda.updateFunctionConfiguration(updateRequest).promise(); await waitForLambdasPropertiesUpdateToFinish(updateFunctionCodeResponse, lambda, functionName); } // only if the code changed is there any point in publishing a new Version if (versionsReferencingFunction.length > 0) { const publishVersionPromise = lambda.publishVersion({ FunctionName: functionName, }).promise(); if (aliasesNames.length > 0) { // we need to wait for the Version to finish publishing const versionUpdate = await publishVersionPromise; for (const alias of aliasesNames) { operations.push(lambda.updateAlias({ FunctionName: functionName, Name: alias, FunctionVersion: versionUpdate.Version, }).promise()); } } else { operations.push(publishVersionPromise); } } } // run all of our updates in parallel await Promise.all(operations); }, }); } return ret; } exports.isHotswappableLambdaFunctionChange = isHotswappableLambdaFunctionChange; /** * Determines which changes to this Alias are hotswappable or not */ function classifyAliasChanges(change) { const ret = []; const classifiedChanges = (0, common_1.classifyChanges)(change, ['FunctionVersion']); classifiedChanges.reportNonHotswappablePropertyChanges(ret); const namesOfHotswappableChanges = Object.keys(classifiedChanges.hotswappableProps); if (namesOfHotswappableChanges.length > 0) { ret.push({ hotswappable: true, resourceType: change.newValue.Type, propsChanged: [], service: 'lambda', resourceNames: [], apply: async (_sdk) => { }, }); } return ret; } /** * Evaluates the hotswappable properties of an AWS::Lambda::Function and * Returns a `LambdaFunctionChange` if the change is hotswappable. * Returns `undefined` if the change is not hotswappable. */ async function evaluateLambdaFunctionProps(hotswappablePropChanges, runtime, evaluateCfnTemplate) { /* * At first glance, we would want to initialize these using the "previous" values (change.oldValue), * in case only one of them changed, like the key, and the Bucket stayed the same. * However, that actually fails for old-style synthesis, which uses CFN Parameters! * Because the names of the Parameters depend on the hash of the Asset, * the Parameters used for the "old" values no longer exist in `assetParams` at this point, * which means we don't have the correct values available to evaluate the CFN expression with. * Fortunately, the diff will always include both the s3Bucket and s3Key parts of the Lambda's Code property, * even if only one of them was actually changed, * which means we don't need the "old" values at all, and we can safely initialize these with just `''`. */ let code = undefined; let description = undefined; let environment = undefined; for (const updatedPropName in hotswappablePropChanges) { const updatedProp = hotswappablePropChanges[updatedPropName]; switch (updatedPropName) { case 'Code': let s3Bucket, s3Key, s3ObjectVersion, imageUri, functionCodeZip; for (const newPropName in updatedProp.newValue) { switch (newPropName) { case 'S3Bucket': s3Bucket = await evaluateCfnTemplate.evaluateCfnExpression(updatedProp.newValue[newPropName]); break; case 'S3Key': s3Key = await evaluateCfnTemplate.evaluateCfnExpression(updatedProp.newValue[newPropName]); break; case 'S3ObjectVersion': s3ObjectVersion = await evaluateCfnTemplate.evaluateCfnExpression(updatedProp.newValue[newPropName]); break; case 'ImageUri': imageUri = await evaluateCfnTemplate.evaluateCfnExpression(updatedProp.newValue[newPropName]); break; case 'ZipFile': // We must create a zip package containing a file with the inline code const functionCode = await evaluateCfnTemplate.evaluateCfnExpression(updatedProp.newValue[newPropName]); const functionRuntime = await evaluateCfnTemplate.evaluateCfnExpression(runtime); if (!functionRuntime) { return undefined; } // file extension must be chosen depending on the runtime const codeFileExt = determineCodeFileExtFromRuntime(functionRuntime); functionCodeZip = await zipString(`index.${codeFileExt}`, functionCode); break; } } code = { s3Bucket, s3Key, s3ObjectVersion, imageUri, functionCodeZip, }; break; case 'Description': description = await evaluateCfnTemplate.evaluateCfnExpression(updatedProp.newValue); break; case 'Environment': environment = await evaluateCfnTemplate.evaluateCfnExpression(updatedProp.newValue); break; default: // we will never get here, but just in case we do throw an error throw new Error('while apply()ing, found a property that cannot be hotswapped. Please report this at github.com/aws/aws-cdk/issues/new/choose'); } } const configurations = description || environment ? { description, environment } : undefined; return code || configurations ? { code, configurations } : undefined; } /** * Compress a string as a file, returning a promise for the zip buffer * https://github.com/archiverjs/node-archiver/issues/342 */ function zipString(fileName, rawString) { return new Promise((resolve, reject) => { const buffers = []; const converter = new stream_1.Writable(); converter._write = (chunk, _, callback) => { buffers.push(chunk); process.nextTick(callback); }; converter.on('finish', () => { resolve(Buffer.concat(buffers)); }); const archive = archiver('zip'); archive.on('error', (err) => { reject(err); }); archive.pipe(converter); archive.append(rawString, { name: fileName, date: new Date('1980-01-01T00:00:00.000Z'), // Add date to make resulting zip file deterministic }); void archive.finalize(); }); } /** * After a Lambda Function is updated, it cannot be updated again until the * `State=Active` and the `LastUpdateStatus=Successful`. * * Depending on the configuration of the Lambda Function this could happen relatively quickly * or very slowly. For example, Zip based functions _not_ in a VPC can take ~1 second whereas VPC * or Container functions can take ~25 seconds (and 'idle' VPC functions can take minutes). */ async function waitForLambdasPropertiesUpdateToFinish(currentFunctionConfiguration, lambda, functionName) { const functionIsInVpcOrUsesDockerForCode = currentFunctionConfiguration.VpcConfig?.VpcId || currentFunctionConfiguration.PackageType === 'Image'; // if the function is deployed in a VPC or if it is a container image function // then the update will take much longer and we can wait longer between checks // otherwise, the update will be quick, so a 1-second delay is fine const delaySeconds = functionIsInVpcOrUsesDockerForCode ? 5 : 1; // configure a custom waiter to wait for the function update to complete lambda.api.waiters.updateFunctionPropertiesToFinish = { name: 'UpdateFunctionPropertiesToFinish', operation: 'getFunction', // equates to 1 minute for zip function not in a VPC and // 5 minutes for container functions or function in a VPC maxAttempts: 60, delay: delaySeconds, acceptors: [ { matcher: 'path', argument: "Configuration.LastUpdateStatus == 'Successful' && Configuration.State == 'Active'", expected: true, state: 'success', }, { matcher: 'path', argument: 'Configuration.LastUpdateStatus', expected: 'Failed', state: 'failure', }, ], }; const updateFunctionPropertiesWaiter = new AWS.ResourceWaiter(lambda, 'updateFunctionPropertiesToFinish'); await updateFunctionPropertiesWaiter.wait({ FunctionName: functionName, }).promise(); } /** * Get file extension from Lambda runtime string. * We use this extension to create a deployment package from Lambda inline code. */ function determineCodeFileExtFromRuntime(runtime) { if (runtime.startsWith('node')) { return 'js'; } if (runtime.startsWith('python')) { return 'py'; } // Currently inline code only supports Node.js and Python, ignoring other runtimes. // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#aws-properties-lambda-function-code-properties throw new evaluate_cloudformation_template_1.CfnEvaluationException(`runtime ${runtime} is unsupported, only node.js and python runtimes are currently supported.`); } /** * Finds all Versions that reference an AWS::Lambda::Function with logical ID `logicalId` * and Aliases that reference those Versions. */ async function versionsAndAliases(logicalId, evaluateCfnTemplate) { // find all Lambda Versions that reference this Function const versionsReferencingFunction = evaluateCfnTemplate.findReferencesTo(logicalId) .filter(r => r.Type === 'AWS::Lambda::Version'); // find all Lambda Aliases that reference the above Versions const aliasesReferencingVersions = (0, util_1.flatMap)(versionsReferencingFunction, v => evaluateCfnTemplate.findReferencesTo(v.LogicalId)); const aliasesNames = await Promise.all(aliasesReferencingVersions.map(a => evaluateCfnTemplate.evaluateCfnExpression(a.Properties?.Name))); return { versionsReferencingFunction, aliasesNames }; } /** * Renders the string used in displaying Alias resource names that reference the specified Lambda Function */ async function renderAliases(logicalId, evaluateCfnTemplate, callbackfn) { const aliasesNames = (await versionsAndAliases(logicalId, evaluateCfnTemplate)).aliasesNames; return Promise.all(aliasesNames.map(callbackfn)); } /** * Renders the string used in displaying Version resource names that reference the specified Lambda Function */ async function renderVersions(logicalId, evaluateCfnTemplate, versionString) { const versions = (await versionsAndAliases(logicalId, evaluateCfnTemplate)).versionsReferencingFunction; return versions.length > 0 ? versionString : []; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGFtYmRhLWZ1bmN0aW9ucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImxhbWJkYS1mdW5jdGlvbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsbUNBQWtDO0FBQ2xDLCtCQUErQjtBQUMvQixxQ0FBd0c7QUFDeEcscUNBQXFDO0FBRXJDLDBGQUE2RztBQUU3Ryx5RUFBeUU7QUFDekUsaUVBQWlFO0FBQ2pFLE1BQU0sUUFBUSxHQUFHLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQztBQUU5QixLQUFLLFVBQVUsa0NBQWtDLENBQ3RELFNBQWlCLEVBQUUsTUFBbUMsRUFBRSxtQkFBbUQ7SUFFM0cseUNBQXlDO0lBQ3pDLHNEQUFzRDtJQUN0RCw2R0FBNkc7SUFDN0csMEVBQTBFO0lBQzFFLElBQUksTUFBTSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEtBQUssc0JBQXNCLEVBQUU7UUFDbkQsT0FBTyxDQUFDO2dCQUNOLFlBQVksRUFBRSxJQUFJO2dCQUNsQixZQUFZLEVBQUUsc0JBQXNCO2dCQUNwQyxhQUFhLEVBQUUsRUFBRTtnQkFDakIsWUFBWSxFQUFFLEVBQUU7Z0JBQ2hCLE9BQU8sRUFBRSxRQUFRO2dCQUNqQixLQUFLLEVBQUUsS0FBSyxFQUFFLElBQVUsRUFBRSxFQUFFLEdBQUUsQ0FBQzthQUNoQyxDQUFDLENBQUM7S0FDSjtJQUVELGtDQUFrQztJQUNsQyxJQUFJLE1BQU0sQ0FBQyxRQUFRLENBQUMsSUFBSSxLQUFLLG9CQUFvQixFQUFFO1FBQ2pELE9BQU8sb0JBQW9CLENBQUMsTUFBTSxDQUFDLENBQUM7S0FDckM7SUFFRCxJQUFJLE1BQU0sQ0FBQyxRQUFRLENBQUMsSUFBSSxLQUFLLHVCQUF1QixFQUFFO1FBQ3BELE9BQU8sRUFBRSxDQUFDO0tBQ1g7SUFFRCxNQUFNLEdBQUcsR0FBd0IsRUFBRSxDQUFDO0lBQ3BDLE1BQU0saUJBQWlCLEdBQUcsSUFBQSx3QkFBZSxFQUFDLE1BQU0sRUFBRSxDQUFDLE1BQU0sRUFBRSxh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TyxRQUFRLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7QUFDbEQsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFdyaXRhYmxlIH0gZnJvbSAnc3RyZWFtJztcbmltcG9ydCAqIGFzIEFXUyBmcm9tICdhd3Mtc2RrJztcbmltcG9ydCB7IENoYW5nZUhvdHN3YXBSZXN1bHQsIGNsYXNzaWZ5Q2hhbmdlcywgSG90c3dhcHBhYmxlQ2hhbmdlQ2FuZGlkYXRlLCBQcm9wRGlmZnMgfSBmcm9tICcuL2NvbW1vbic7XG5pbXBvcnQgeyBmbGF0TWFwIH0gZnJvbSAnLi4vLi4vdXRpbCc7XG5pbXBvcnQgeyBJU0RLIH0gZnJvbSAnLi4vYXdzLWF1dGgnO1xuaW1wb3J0IHsgQ2ZuRXZhbHVhdGlvbkV4Y2VwdGlvbiwgRXZhbHVhdGVDbG91ZEZvcm1hdGlvblRlbXBsYXRlIH0gZnJvbSAnLi4vZXZhbHVhdGUtY2xvdWRmb3JtYXRpb24tdGVtcGxhdGUnO1xuXG4vLyBuYW1lc3BhY2Ugb2JqZWN0IGltcG9ydHMgd29uJ3Qgd29yayBpbiB0aGUgYnVuZGxlIGZvciBmdW5jdGlvbiBleHBvcnRzXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0c1xuY29uc3QgYXJjaGl2ZXIgPSByZXF1aXJlKCdhcmNoaXZlcicpO1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaXNIb3Rzd2FwcGFibGVMYW1iZGFGdW5jdGlvbkNoYW5nZShcbiAgbG9naWNhbElkOiBzdHJpbmcsIGNoYW5nZTogSG90c3dhcHBhYmxlQ2hhbmdlQ2FuZGlkYXRlLCBldmFsdWF0ZUNmblRlbXBsYXRlOiBFdmFsdWF0ZUNsb3VkRm9ybWF0aW9uVGVtcGxhdGUsXG4pOiBQcm9taXNlPENoYW5nZUhvdHN3YXBSZXN1bHQ+IHtcbiAgLy8gaWYgdGhlIGNoYW5nZSBpcyBmb3IgYSBMYW1iZGEgVmVyc2lvbixcbiAgLy8gaWdub3JlIGl0IGJ5IHJldHVybmluZyBhbiBlbXB0eSBob3Rzd2FwIG9wZXJhdGlvbiAtXG4gIC8vIHdlIHdpbGwgcHVibGlzaCBhIG5ldyB2ZXJzaW9uIHdoZW4gd2UgZ2V0IHRvIGhvdHN3YXBwaW5nIHRoZSBhY3R1YWwgRnVuY3Rpb24gdGhpcyBWZXJzaW9uIHBvaW50cyB0bywgYmVsb3dcbiAgLy8gKFZlcnNpb25zIGNhbid0IGJlIGNoYW5nZWQgaW4gQ2xvdWRGb3JtYXRpb24gYW55d2F5LCB0aGV5J3JlIGltbXV0YWJsZSlcbiAgaWYgKGNoYW5nZS5uZXdWYWx1ZS5UeXBlID09PSAnQVdTOjpMYW1iZGE6OlZlcnNpb24nKSB7XG4gICAgcmV0dXJuIFt7XG4gICAgICBob3Rzd2FwcGFibGU6IHRydWUsXG4gICAgICByZXNvdXJjZVR5cGU6ICdBV1M6OkxhbWJkYTo6VmVyc2lvbicsXG4gICAgICByZXNvdXJjZU5hbWVzOiBbXSxcbiAgICAgIHByb3BzQ2hhbmdlZDogW10sXG4gICAgICBzZXJ2aWNlOiAnbGFtYmRhJyxcbiAgICAgIGFwcGx5OiBhc3luYyAoX3NkazogSVNESykgPT4ge30sXG4gICAgfV07XG4gIH1cblxuICAvLyB3ZSBoYW5kbGUgQWxpYXNlcyBzcGVjaWFsbHkgdG9vXG4gIGlmIChjaGFuZ2UubmV3VmFsdWUuVHlwZSA9PT0gJ0FXUzo6TGFtYmRhOjpBbGlhcycpIHtcbiAgICByZXR1cm4gY2xhc3NpZnlBbGlhc0NoYW5nZXMoY2hhbmdlKTtcbiAgfVxuXG4gIGlmIChjaGFuZ2UubmV3VmFsdWUuVHlwZSAhPT0gJ0FXUzo6TGFtYmRhOjpGdW5jdGlvbicpIHtcbiAgICByZXR1cm4gW107XG4gIH1cblxuICBjb25zdCByZXQ6IENoYW5nZUhvdHN3YXBSZXN1bHQgPSBbXTtcbiAgY29uc3QgY2xhc3NpZmllZENoYW5nZXMgPSBjbGFzc2lmeUNoYW5nZXMoY2hhbmdlLCBbJ0NvZGUnLCAnRW52aXJvbm1lbnQnLCAnRGVzY3JpcHRpb24nXSk7XG4gIGNsYXNzaWZpZWRDaGFuZ2VzLnJlcG9ydE5vbkhvdHN3YXBwYWJsZVByb3BlcnR5Q2hhbmdlcyhyZXQpO1xuXG4gIGNvbnN0IGZ1bmN0aW9uTmFtZSA9IGF3YWl0IGV2YWx1YXRlQ2ZuVGVtcGxhdGUuZXN0YWJsaXNoUmVzb3VyY2VQaHlzaWNhbE5hbWUobG9naWNhbElkLCBjaGFuZ2UubmV3VmFsdWUuUHJvcGVydGllcz8uRnVuY3Rpb25OYW1lKTtcbiAgY29uc3QgbmFtZXNPZkhvdHN3YXBwYWJsZUNoYW5nZXMgPSBPYmplY3Qua2V5cyhjbGFzc2lmaWVkQ2hhbmdlcy5ob3Rzd2FwcGFibGVQcm9wcyk7XG4gIGlmIChuYW1lc09mSG90c3dhcHBhYmxlQ2hhbmdlcy5sZW5ndGggPiAwKSB7XG4gICAgcmV0LnB1c2goe1xuICAgICAgaG90c3dhcHBhYmxlOiB0cnVlLFxuICAgICAgcmVzb3VyY2VUeXBlOiBjaGFuZ2UubmV3VmFsdWUuVHlwZSxcbiAgICAgIHByb3BzQ2hhbmdlZDogbmFtZXNPZkhvdHN3YXBwYWJsZUNoYW5nZXMsXG4gICAgICBzZXJ2aWNlOiAnbGFtYmRhJyxcbiAgICAgIHJlc291cmNlTmFtZXM6IFtcbiAgICAgICAgYExhbWJkYSBGdW5jdGlvbiAnJHtmdW5jdGlvbk5hbWV9J2AsXG4gICAgICAgIC8vIGFkZCBWZXJzaW9uIGhlcmUgaWYgd2UncmUgcHVibGlzaGluZyBhIG5ldyBvbmVcbiAgICAgICAgLi4uYXdhaXQgcmVuZGVyVmVyc2lvbnMobG9naWNhbElkLCBldmFsdWF0ZUNmblRlbXBsYXRlLCBbYExhbWJkYSBWZXJzaW9uIGZvciBGdW5jdGlvbiAnJHtmdW5jdGlvbk5hbWV9J2BdKSxcbiAgICAgICAgLy8gYWRkIGFueSBBbGlhc2VzIHRoYXQgd2UgYXJlIGhvdHN3YXBwaW5nIGhlcmVcbiAgICAgICAgLi4uYXdhaXQgcmVuZGVyQWxpYXNlcyhsb2dpY2FsSWQsIGV2YWx1YXRlQ2ZuVGVtcGxhdGUsIGFzeW5jIChhbGlhcykgPT4gYExhbWJkYSBBbGlhcyAnJHthbGlhc30nIGZvciBGdW5jdGlvbiAnJHtmdW5jdGlvbk5hbWV9J2ApLFxuICAgICAgXSxcbiAgICAgIGFwcGx5OiBhc3luYyAoc2RrOiBJU0RLKSA9PiB7XG4gICAgICAgIGNvbnN0IGxhbWJkYUNvZGVDaGFuZ2UgPSBhd2FpdCBldmFsdWF0ZUxhbWJkYUZ1bmN0aW9uUHJvcHMoXG4gICAgICAgICAgY2xhc3NpZmllZENoYW5nZXMuaG90c3dhcHBhYmxlUHJvcHMsIGNoYW5nZS5uZXdWYWx1ZS5Qcm9wZXJ0aWVzPy5SdW50aW1lLCBldmFsdWF0ZUNmblRlbXBsYXRlLFxuICAgICAgICApO1xuICAgICAgICBpZiAobGFtYmRhQ29kZUNoYW5nZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCFmdW5jdGlvbk5hbWUpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCB7IHZlcnNpb25zUmVmZXJlbmNpbmdGdW5jdGlvbiwgYWxpYXNlc05hbWVzIH0gPSBhd2FpdCB2ZXJzaW9uc0FuZEFsaWFzZXMobG9naWNhbElkLCBldmFsdWF0ZUNmblRlbXBsYXRlKTtcbiAgICAgICAgY29uc3QgbGFtYmRhID0gc2RrLmxhbWJkYSgpO1xuICAgICAgICBjb25zdCBvcGVyYXRpb25zOiBQcm9taXNlPGFueT5bXSA9IFtdO1xuXG4gICAgICAgIGlmIChsYW1iZGFDb2RlQ2hhbmdlLmNvZGUgIT09IHVuZGVmaW5lZCB8fCBsYW1iZGFDb2RlQ2hhbmdlLmNvbmZpZ3VyYXRpb25zICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICBpZiAobGFtYmRhQ29kZUNoYW5nZS5jb2RlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGNvbnN0IHVwZGF0ZUZ1bmN0aW9uQ29kZVJlc3BvbnNlID0gYXdhaXQgbGFtYmRhLnVwZGF0ZUZ1bmN0aW9uQ29kZSh7XG4gICAgICAgICAgICAgIEZ1bmN0aW9uTmFtZTogZnVuY3Rpb25OYW1lLFxuICAgICAgICAgICAgICBTM0J1Y2tldDogbGFtYmRhQ29kZUNoYW5nZS5jb2RlLnMzQnVja2V0LFxuICAgICAgICAgICAgICBTM0tleTogbGFtYmRhQ29kZUNoYW5nZS5jb2RlLnMzS2V5LFxuICAgICAgICAgICAgICBJbWFnZVVyaTogbGFtYmRhQ29kZUNoYW5nZS5jb2RlLmltYWdlVXJpLFxuICAgICAgICAgICAgICBaaXBGaWxlOiBsYW1iZGFDb2RlQ2hhbmdlLmNvZGUuZnVuY3Rpb25Db2RlWmlwLFxuICAgICAgICAgICAgICBTM09iamVjdFZlcnNpb246IGxhbWJkYUNvZGVDaGFuZ2UuY29kZS5zM09iamVjdFZlcnNpb24sXG4gICAgICAgICAgICB9KS5wcm9taXNlKCk7XG5cbiAgICAgICAgICAgIGF3YWl0IHdhaXRGb3JMYW1iZGFzUHJvcGVydGllc1VwZGF0ZVRvRmluaXNoKHVwZGF0ZUZ1bmN0aW9uQ29kZVJlc3BvbnNlLCBsYW1iZGEsIGZ1bmN0aW9uTmFtZSk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaWYgKGxhbWJkYUNvZGVDaGFuZ2UuY29uZmlndXJhdGlvbnMgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgY29uc3QgdXBkYXRlUmVxdWVzdDogQVdTLkxhbWJkYS5VcGRhdGVGdW5jdGlvbkNvbmZpZ3VyYXRpb25SZXF1ZXN0ID0ge1xuICAgICAgICAgICAgICBGdW5jdGlvbk5hbWU6IGZ1bmN0aW9uTmFtZSxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBpZiAobGFtYmRhQ29kZUNoYW5nZS5jb25maWd1cmF0aW9ucy5kZXNjcmlwdGlvbiAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgIHVwZGF0ZVJlcXVlc3QuRGVzY3JpcHRpb24gPSBsYW1iZGFDb2RlQ2hhbmdlLmNvbmZpZ3VyYXRpb25zLmRlc2NyaXB0aW9uO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKGxhbWJkYUNvZGVDaGFuZ2UuY29uZmlndXJhdGlvbnMuZW52aXJvbm1lbnQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICB1cGRhdGVSZXF1ZXN0LkVudmlyb25tZW50ID0gbGFtYmRhQ29kZUNoYW5nZS5jb25maWd1cmF0aW9ucy5lbnZpcm9ubWVudDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IHVwZGF0ZUZ1bmN0aW9uQ29kZVJlc3BvbnNlID0gYXdhaXQgbGFtYmRhLnVwZGF0ZUZ1bmN0aW9uQ29uZmlndXJhdGlvbih1cGRhdGVSZXF1ZXN0KS5wcm9taXNlKCk7XG4gICAgICAgICAgICBhd2FpdCB3YWl0Rm9yTGFtYmRhc1Byb3BlcnRpZXNVcGRhdGVUb0ZpbmlzaCh1cGRhdGVGdW5jdGlvbkNvZGVSZXNwb25zZSwgbGFtYmRhLCBmdW5jdGlvbk5hbWUpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIG9ubHkgaWYgdGhlIGNvZGUgY2hhbmdlZCBpcyB0aGVyZSBhbnkgcG9pbnQgaW4gcHVibGlzaGluZyBhIG5ldyBWZXJzaW9uXG4gICAgICAgICAgaWYgKHZlcnNpb25zUmVmZXJlbmNpbmdGdW5jdGlvbi5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBjb25zdCBwdWJsaXNoVmVyc2lvblByb21pc2UgPSBsYW1iZGEucHVibGlzaFZlcnNpb24oe1xuICAgICAgICAgICAgICBGdW5jdGlvbk5hbWU6IGZ1bmN0aW9uTmFtZSxcbiAgICAgICAgICAgIH0pLnByb21pc2UoKTtcblxuICAgICAgICAgICAgaWYgKGFsaWFzZXNOYW1lcy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgIC8vIHdlIG5lZWQgdG8gd2FpdCBmb3IgdGhlIFZlcnNpb24gdG8gZmluaXNoIHB1Ymxpc2hpbmdcbiAgICAgICAgICAgICAgY29uc3QgdmVyc2lvblVwZGF0ZSA9IGF3YWl0IHB1Ymxpc2hWZXJzaW9uUHJvbWlzZTtcbiAgICAgICAgICAgICAgZm9yIChjb25zdCBhbGlhcyBvZiBhbGlhc2VzTmFtZXMpIHtcbiAgICAgICAgICAgICAgICBvcGVyYXRpb25zLnB1c2gobGFtYmRhLnVwZGF0ZUFsaWFzKHtcbiAgICAgICAgICAgICAgICAgIEZ1bmN0aW9uTmFtZTogZnVuY3Rpb25OYW1lLFxuICAgICAgICAgICAgICAgICAgTmFtZTogYWxpYXMsXG4gICAgICAgICAgICAgICAgICBGdW5jdGlvblZlcnNpb246IHZlcnNpb25VcGRhdGUuVmVyc2lvbixcbiAgICAgICAgICAgICAgICB9KS5wcm9taXNlKCkpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBvcGVyYXRpb25zLnB1c2gocHVibGlzaFZlcnNpb25Qcm9taXNlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBydW4gYWxsIG9mIG91ciB1cGRhdGVzIGluIHBhcmFsbGVsXG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKG9wZXJhdGlvbnMpO1xuICAgICAgfSxcbiAgICB9KTtcbiAgfVxuXG4gIHJldHVybiByZXQ7XG59XG5cbi8qKlxuICogRGV0ZXJtaW5lcyB3aGljaCBjaGFuZ2VzIHRvIHRoaXMgQWxpYXMgYXJlIGhvdHN3YXBwYWJsZSBvciBub3RcbiAqL1xuZnVuY3Rpb24gY2xhc3NpZnlBbGlhc0NoYW5nZXMoY2hhbmdlOiBIb3Rzd2FwcGFibGVDaGFuZ2VDYW5kaWRhdGUpOiBDaGFuZ2VIb3Rzd2FwUmVzdWx0IHtcbiAgY29uc3QgcmV0OiBDaGFuZ2VIb3Rzd2FwUmVzdWx0ID0gW107XG4gIGNvbnN0IGNsYXNzaWZpZWRDaGFuZ2VzID0gY2xhc3NpZnlDaGFuZ2VzKGNoYW5nZSwgWydGdW5jdGlvblZlcnNpb24nXSk7XG4gIGNsYXNzaWZpZWRDaGFuZ2VzLnJlcG9ydE5vbkhvdHN3YXBwYWJsZVByb3BlcnR5Q2hhbmdlcyhyZXQpO1xuXG4gIGNvbnN0IG5hbWVzT2ZIb3Rzd2FwcGFibGVDaGFuZ2VzID0gT2JqZWN0LmtleXMoY2xhc3NpZmllZENoYW5nZXMuaG90c3dhcHBhYmxlUHJvcHMpO1xuICBpZiAobmFtZXNPZkhvdHN3YXBwYWJsZUNoYW5nZXMubGVuZ3RoID4gMCkge1xuICAgIHJldC5wdXNoKHtcbiAgICAgIGhvdHN3YXBwYWJsZTogdHJ1ZSxcbiAgICAgIHJlc291cmNlVHlwZTogY2hhbmdlLm5ld1ZhbHVlLlR5cGUsXG4gICAgICBwcm9wc0NoYW5nZWQ6IFtdLFxuICAgICAgc2VydmljZTogJ2xhbWJkYScsXG4gICAgICByZXNvdXJjZU5hbWVzOiBbXSxcbiAgICAgIGFwcGx5OiBhc3luYyAoX3NkazogSVNESykgPT4ge30sXG4gICAgfSk7XG4gIH1cblxuICByZXR1cm4gcmV0O1xufVxuXG4vKipcbiAqIEV2YWx1YXRlcyB0aGUgaG90c3dhcHBhYmxlIHByb3BlcnRpZXMgb2YgYW4gQVdTOjpMYW1iZGE6OkZ1bmN0aW9uIGFuZFxuICogUmV0dXJucyBhIGBMYW1iZGFGdW5jdGlvbkNoYW5nZWAgaWYgdGhlIGNoYW5nZSBpcyBob3Rzd2FwcGFibGUuXG4gKiBSZXR1cm5zIGB1bmRlZmluZWRgIGlmIHRoZSBjaGFuZ2UgaXMgbm90IGhvdHN3YXBwYWJsZS5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gZXZhbHVhdGVMYW1iZGFGdW5jdGlvblByb3BzKFxuICBob3Rzd2FwcGFibGVQcm9wQ2hhbmdlczogUHJvcERpZmZzLCBydW50aW1lOiBzdHJpbmcsIGV2YWx1YXRlQ2ZuVGVtcGxhdGU6IEV2YWx1YXRlQ2xvdWRGb3JtYXRpb25UZW1wbGF0ZSxcbik6IFByb21pc2U8TGFtYmRhRnVuY3Rpb25DaGFuZ2UgfCB1bmRlZmluZWQ+IHtcbiAgLypcbiAgICogQXQgZmlyc3QgZ2xhbmNlLCB3ZSB3b3VsZCB3YW50IHRvIGluaXRpYWxpemUgdGhlc2UgdXNpbmcgdGhlIFwicHJldmlvdXNcIiB2YWx1ZXMgKGNoYW5nZS5vbGRWYWx1ZSksXG4gICAqIGluIGNhc2Ugb25seSBvbmUgb2YgdGhlbSBjaGFuZ2VkLCBsaWtlIHRoZSBrZXksIGFuZCB0aGUgQnVja2V0IHN0YXllZCB0aGUgc2FtZS5cbiAgICogSG93ZXZlciwgdGhhdCBhY3R1YWxseSBmYWlscyBmb3Igb2xkLXN0eWxlIHN5bnRoZXNpcywgd2hpY2ggdXNlcyBDRk4gUGFyYW1ldGVycyFcbiAgICogQmVjYXVzZSB0aGUgbmFtZXMgb2YgdGhlIFBhcmFtZXRlcnMgZGVwZW5kIG9uIHRoZSBoYXNoIG9mIHRoZSBBc3NldCxcbiAgICogdGhlIFBhcmFtZXRlcnMgdXNlZCBmb3IgdGhlIFwib2xkXCIgdmFsdWVzIG5vIGxvbmdlciBleGlzdCBpbiBgYXNzZXRQYXJhbXNgIGF0IHRoaXMgcG9pbnQsXG4gICAqIHdoaWNoIG1lYW5zIHdlIGRvbid0IGhhdmUgdGhlIGNvcnJlY3QgdmFsdWVzIGF2YWlsYWJsZSB0byBldmFsdWF0ZSB0aGUgQ0ZOIGV4cHJlc3Npb24gd2l0aC5cbiAgICogRm9ydHVuYXRlbHksIHRoZSBkaWZmIHdpbGwgYWx3YXlzIGluY2x1ZGUgYm90aCB0aGUgczNCdWNrZXQgYW5kIHMzS2V5IHBhcnRzIG9mIHRoZSBMYW1iZGEncyBDb2RlIHByb3BlcnR5LFxuICAgKiBldmVuIGlmIG9ubHkgb25lIG9mIHRoZW0gd2FzIGFjdHVhbGx5IGNoYW5nZWQsXG4gICAqIHdoaWNoIG1lYW5zIHdlIGRvbid0IG5lZWQgdGhlIFwib2xkXCIgdmFsdWVzIGF0IGFsbCwgYW5kIHdlIGNhbiBzYWZlbHkgaW5pdGlhbGl6ZSB0aGVzZSB3aXRoIGp1c3QgYCcnYC5cbiAgICovXG4gIGxldCBjb2RlOiBMYW1iZGFGdW5jdGlvbkNvZGUgfCB1bmRlZmluZWQgPSB1bmRlZmluZWQ7XG4gIGxldCBkZXNjcmlwdGlvbjogc3RyaW5nIHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkO1xuICBsZXQgZW52aXJvbm1lbnQ6IHsgW2tleTogc3RyaW5nXTogc3RyaW5nIH0gfCB1bmRlZmluZWQgPSB1bmRlZmluZWQ7XG5cbiAgZm9yIChjb25zdCB1cGRhdGVkUHJvcE5hbWUgaW4gaG90c3dhcHBhYmxlUHJvcENoYW5nZXMpIHtcbiAgICBjb25zdCB1cGRhdGVkUHJvcCA9IGhvdHN3YXBwYWJsZVByb3BDaGFuZ2VzW3VwZGF0ZWRQcm9wTmFtZV07XG5cbiAgICBzd2l0Y2ggKHVwZGF0ZWRQcm9wTmFtZSkge1xuICAgICAgY2FzZSAnQ29kZSc6XG4gICAgICAgIGxldCBzM0J1Y2tldCwgczNLZXksIHMzT2JqZWN0VmVyc2lvbiwgaW1hZ2VVcmksIGZ1bmN0aW9uQ29kZVppcDtcblxuICAgICAgICBmb3IgKGNvbnN0IG5ld1Byb3BOYW1lIGluIHVwZGF0ZWRQcm9wLm5ld1ZhbHVlKSB7XG4gICAgICAgICAgc3dpdGNoIChuZXdQcm9wTmFtZSkge1xuICAgICAgICAgICAgY2FzZSAnUzNCdWNrZXQnOlxuICAgICAgICAgICAgICBzM0J1Y2tldCA9IGF3YWl0IGV2YWx1YXRlQ2ZuVGVtcGxhdGUuZXZhbHVhdGVDZm5FeHByZXNzaW9uKHVwZGF0ZWRQcm9wLm5ld1ZhbHVlW25ld1Byb3BOYW1lXSk7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSAnUzNLZXknOlxuICAgICAgICAgICAgICBzM0tleSA9IGF3YWl0IGV2YWx1YXRlQ2ZuVGVtcGxhdGUuZXZhbHVhdGVDZm5FeHByZXNzaW9uKHVwZGF0ZWRQcm9wLm5ld1ZhbHVlW25ld1Byb3BOYW1lXSk7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSAnUzNPYmplY3RWZXJzaW9uJzpcbiAgICAgICAgICAgICAgczNPYmplY3RWZXJzaW9uID0gYXdhaXQgZXZhbHVhdGVDZm5UZW1wbGF0ZS5ldmFsdWF0ZUNmbkV4cHJlc3Npb24odXBkYXRlZFByb3AubmV3VmFsdWVbbmV3UHJvcE5hbWVdKTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdJbWFnZVVyaSc6XG4gICAgICAgICAgICAgIGltYWdlVXJpID0gYXdhaXQgZXZhbHVhdGVDZm5UZW1wbGF0ZS5ldmFsdWF0ZUNmbkV4cHJlc3Npb24odXBkYXRlZFByb3AubmV3VmFsdWVbbmV3UHJvcE5hbWVdKTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdaaXBGaWxlJzpcbiAgICAgICAgICAgICAgLy8gV2UgbXVzdCBjcmVhdGUgYSB6aXAgcGFja2FnZSBjb250YWluaW5nIGEgZmlsZSB3aXRoIHRoZSBpbmxpbmUgY29kZVxuICAgICAgICAgICAgICBjb25zdCBmdW5jdGlvbkNvZGUgPSBhd2FpdCBldmFsdWF0ZUNmblRlbXBsYXRlLmV2YWx1YXRlQ2ZuRXhwcmVzc2lvbih1cGRhdGVkUHJvcC5uZXdWYWx1ZVtuZXdQcm9wTmFtZV0pO1xuICAgICAgICAgICAgICBjb25zdCBmdW5jdGlvblJ1bnRpbWUgPSBhd2FpdCBldmFsdWF0ZUNmblRlbXBsYXRlLmV2YWx1YXRlQ2ZuRXhwcmVzc2lvbihydW50aW1lKTtcbiAgICAgICAgICAgICAgaWYgKCFmdW5jdGlvblJ1bnRpbWUpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIC8vIGZpbGUgZXh0ZW5zaW9uIG11c3QgYmUgY2hvc2VuIGRlcGVuZGluZyBvbiB0aGUgcnVudGltZVxuICAgICAgICAgICAgICBjb25zdCBjb2RlRmlsZUV4dCA9IGRldGVybWluZUNvZGVGaWxlRXh0RnJvbVJ1bnRpbWUoZnVuY3Rpb25SdW50aW1lKTtcbiAgICAgICAgICAgICAgZnVuY3Rpb25Db2RlWmlwID0gYXdhaXQgemlwU3RyaW5nKGBpbmRleC4ke2NvZGVGaWxlRXh0fWAsIGZ1bmN0aW9uQ29kZSk7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBjb2RlID0ge1xuICAgICAgICAgIHMzQnVja2V0LFxuICAgICAgICAgIHMzS2V5LFxuICAgICAgICAgIHMzT2JqZWN0VmVyc2lvbixcbiAgICAgICAgICBpbWFnZVVyaSxcbiAgICAgICAgICBmdW5jdGlvbkNvZGVaaXAsXG4gICAgICAgIH07XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnRGVzY3JpcHRpb24nOlxuICAgICAgICBkZXNjcmlwdGlvbiA9IGF3YWl0IGV2YWx1YXRlQ2ZuVGVtcGxhdGUuZXZhbHVhdGVDZm5FeHByZXNzaW9uKHVwZGF0ZWRQcm9wLm5ld1ZhbHVlKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdFbnZpcm9ubWVudCc6XG4gICAgICAgIGVudmlyb25tZW50ID0gYXdhaXQgZXZhbHVhdGVDZm5UZW1wbGF0ZS5ldmFsdWF0ZUNmbkV4cHJlc3Npb24odXBkYXRlZFByb3AubmV3VmFsdWUpO1xuICAgICAgICBicmVhaztcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIC8vIHdlIHdpbGwgbmV2ZXIgZ2V0IGhlcmUsIGJ1dCBqdXN0IGluIGNhc2Ugd2UgZG8gdGhyb3cgYW4gZXJyb3JcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yICgnd2hpbGUgYXBwbHkoKWluZywgZm91bmQgYSBwcm9wZXJ0eSB0aGF0IGNhbm5vdCBiZSBob3Rzd2FwcGVkLiBQbGVhc2UgcmVwb3J0IHRoaXMgYXQgZ2l0aHViLmNvbS9hd3MvYXdzLWNkay9pc3N1ZXMvbmV3L2Nob29zZScpO1xuICAgIH1cbiAgfVxuXG4gIGNvbnN0IGNvbmZpZ3VyYXRpb25zID0gZGVzY3JpcHRpb24gfHwgZW52aXJvbm1lbnQgPyB7IGRlc2NyaXB0aW9uLCBlbnZpcm9ubWVudCB9IDogdW5kZWZpbmVkO1xuICByZXR1cm4gY29kZSB8fCBjb25maWd1cmF0aW9ucyA/IHsgY29kZSwgY29uZmlndXJhdGlvbnMgfSA6IHVuZGVmaW5lZDtcbn1cblxuaW50ZXJmYWNlIExhbWJkYUZ1bmN0aW9uQ29kZSB7XG4gIHJlYWRvbmx5IHMzQnVja2V0Pzogc3RyaW5nO1xuICByZWFkb25seSBzM0tleT86IHN0cmluZztcbiAgcmVhZG9ubHkgczNPYmplY3RWZXJzaW9uPzogc3RyaW5nO1xuICByZWFkb25seSBpbWFnZVVyaT86IHN0cmluZztcbiAgcmVhZG9ubHkgZnVuY3Rpb25Db2RlWmlwPzogQnVmZmVyO1xufVxuXG5pbnRlcmZhY2UgTGFtYmRhRnVuY3Rpb25Db25maWd1cmF0aW9ucyB7XG4gIHJlYWRvbmx5IGRlc2NyaXB0aW9uPzogc3RyaW5nO1xuICByZWFkb25seSBlbnZpcm9ubWVudD86IHsgW2tleTogc3RyaW5nXTogc3RyaW5nIH07XG59XG5cbmludGVyZmFjZSBMYW1iZGFGdW5jdGlvbkNoYW5nZSB7XG4gIHJlYWRvbmx5IGNvZGU/OiBMYW1iZGFGdW5jdGlvbkNvZGU7XG4gIHJlYWRvbmx5IGNvbmZpZ3VyYXRpb25zPzogTGFtYmRhRnVuY3Rpb25Db25maWd1cmF0aW9ucztcbn1cblxuLyoqXG4gKiBDb21wcmVzcyBhIHN0cmluZyBhcyBhIGZpbGUsIHJldHVybmluZyBhIHByb21pc2UgZm9yIHRoZSB6aXAgYnVmZmVyXG4gKiBodHRwczovL2dpdGh1Yi5jb20vYXJjaGl2ZXJqcy9ub2RlLWFyY2hpdmVyL2lzc3Vlcy8zNDJcbiAqL1xuZnVuY3Rpb24gemlwU3RyaW5nKGZpbGVOYW1lOiBzdHJpbmcsIHJhd1N0cmluZzogc3RyaW5nKTogUHJvbWlzZTxCdWZmZXI+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICBjb25zdCBidWZmZXJzOiBCdWZmZXJbXSA9IFtdO1xuXG4gICAgY29uc3QgY29udmVydGVyID0gbmV3IFdyaXRhYmxlKCk7XG5cbiAgICBjb252ZXJ0ZXIuX3dyaXRlID0gKGNodW5rOiBCdWZmZXIsIF86IHN0cmluZywgY2FsbGJhY2s6ICgpID0+IHZvaWQpID0+IHtcbiAgICAgIGJ1ZmZlcnMucHVzaChjaHVuayk7XG4gICAgICBwcm9jZXNzLm5leHRUaWNrKGNhbGxiYWNrKTtcbiAgICB9O1xuXG4gICAgY29udmVydGVyLm9uKCdmaW5pc2gnLCAoKSA9PiB7XG4gICAgICByZXNvbHZlKEJ1ZmZlci5jb25jYXQoYnVmZmVycykpO1xuICAgIH0pO1xuXG4gICAgY29uc3QgYXJjaGl2ZSA9IGFyY2hpdmVyKCd6aXAnKTtcblxuICAgIGFyY2hpdmUub24oJ2Vycm9yJywgKGVycjogYW55KSA9PiB7XG4gICAgICByZWplY3QoZXJyKTtcbiAgICB9KTtcblxuICAgIGFyY2hpdmUucGlwZShjb252ZXJ0ZXIpO1xuXG4gICAgYXJjaGl2ZS5hcHBlbmQocmF3U3RyaW5nLCB7XG4gICAgICBuYW1lOiBmaWxlTmFtZSxcbiAgICAgIGRhdGU6IG5ldyBEYXRlKCcxOTgwLTAxLTAxVDAwOjAwOjAwLjAwMFonKSwgLy8gQWRkIGRhdGUgdG8gbWFrZSByZXN1bHRpbmcgemlwIGZpbGUgZGV0ZXJtaW5pc3RpY1xuICAgIH0pO1xuXG4gICAgdm9pZCBhcmNoaXZlLmZpbmFsaXplKCk7XG4gIH0pO1xufVxuXG4vKipcbiAgKiBBZnRlciBhIExhbWJkYSBGdW5jdGlvbiBpcyB1cGRhdGVkLCBpdCBjYW5ub3QgYmUgdXBkYXRlZCBhZ2FpbiB1bnRpbCB0aGVcbiAgKiBgU3RhdGU9QWN0aXZlYCBhbmQgdGhlIGBMYXN0VXBkYXRlU3RhdHVzPVN1Y2Nlc3NmdWxgLlxuICAqXG4gICogRGVwZW5kaW5nIG9uIHRoZSBjb25maWd1cmF0aW9uIG9mIHRoZSBMYW1iZGEgRnVuY3Rpb24gdGhpcyBjb3VsZCBoYXBwZW4gcmVsYXRpdmVseSBxdWlja2x5XG4gICogb3IgdmVyeSBzbG93bHkuIEZvciBleGFtcGxlLCBaaXAgYmFzZWQgZnVuY3Rpb25zIF9ub3RfIGluIGEgVlBDIGNhbiB0YWtlIH4xIHNlY29uZCB3aGVyZWFzIFZQQ1xuICAqIG9yIENvbnRhaW5lciBmdW5jdGlvbnMgY2FuIHRha2UgfjI1IHNlY29uZHMgKGFuZCAnaWRsZScgVlBDIGZ1bmN0aW9ucyBjYW4gdGFrZSBtaW51dGVzKS5cbiAgKi9cbmFzeW5jIGZ1bmN0aW9uIHdhaXRGb3JMYW1iZGFzUHJvcGVydGllc1VwZGF0ZVRvRmluaXNoKFxuICBjdXJyZW50RnVuY3Rpb25Db25maWd1cmF0aW9uOiBBV1MuTGFtYmRhLkZ1bmN0aW9uQ29uZmlndXJhdGlvbiwgbGFtYmRhOiBBV1MuTGFtYmRhLCBmdW5jdGlvbk5hbWU6IHN0cmluZyxcbik6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBmdW5jdGlvbklzSW5WcGNPclVzZXNEb2NrZXJGb3JDb2RlID0gY3VycmVudEZ1bmN0aW9uQ29uZmlndXJhdGlvbi5WcGNDb25maWc/LlZwY0lkIHx8XG4gICAgICBjdXJyZW50RnVuY3Rpb25Db25maWd1cmF0aW9uLlBhY2thZ2VUeXBlID09PSAnSW1hZ2UnO1xuXG4gIC8vIGlmIHRoZSBmdW5jdGlvbiBpcyBkZXBsb3llZCBpbiBhIFZQQyBvciBpZiBpdCBpcyBhIGNvbnRhaW5lciBpbWFnZSBmdW5jdGlvblxuICAvLyB0aGVuIHRoZSB1cGRhdGUgd2lsbCB0YWtlIG11Y2ggbG9uZ2VyIGFuZCB3ZSBjYW4gd2FpdCBsb25nZXIgYmV0d2VlbiBjaGVja3NcbiAgLy8gb3RoZXJ3aXNlLCB0aGUgdXBkYXRlIHdpbGwgYmUgcXVpY2ssIHNvIGEgMS1zZWNvbmQgZGVsYXkgaXMgZmluZVxuICBjb25zdCBkZWxheVNlY29uZHMgPSBmdW5jdGlvbklzSW5WcGNPclVzZXNEb2NrZXJGb3JDb2RlID8gNSA6IDE7XG5cbiAgLy8gY29uZmlndXJlIGEgY3VzdG9tIHdhaXRlciB0byB3YWl0IGZvciB0aGUgZnVuY3Rpb24gdXBkYXRlIHRvIGNvbXBsZXRlXG4gIChsYW1iZGEgYXMgYW55KS5hcGkud2FpdGVycy51cGRhdGVGdW5jdGlvblByb3BlcnRpZXNUb0ZpbmlzaCA9IHtcbiAgICBuYW1lOiAnVXBkYXRlRnVuY3Rpb25Qcm9wZXJ0aWVzVG9GaW5pc2gnLFxuICAgIG9wZXJhdGlvbjogJ2dldEZ1bmN0aW9uJyxcbiAgICAvLyBlcXVhdGVzIHRvIDEgbWludXRlIGZvciB6aXAgZnVuY3Rpb24gbm90IGluIGEgVlBDIGFuZFxuICAgIC8vIDUgbWludXRlcyBmb3IgY29udGFpbmVyIGZ1bmN0aW9ucyBvciBmdW5jdGlvbiBpbiBhIFZQQ1xuICAgIG1heEF0dGVtcHRzOiA2MCxcbiAgICBkZWxheTogZGVsYXlTZWNvbmRzLFxuICAgIGFjY2VwdG9yczogW1xuICAgICAge1xuICAgICAgICBtYXRjaGVyOiAncGF0aCcsXG4gICAgICAgIGFyZ3VtZW50OiBcIkNvbmZpZ3VyYXRpb24uTGFzdFVwZGF0ZVN0YXR1cyA9PSAnU3VjY2Vzc2Z1bCcgJiYgQ29uZmlndXJhdGlvbi5TdGF0ZSA9PSAnQWN0aXZlJ1wiLFxuICAgICAgICBleHBlY3RlZDogdHJ1ZSxcbiAgICAgICAgc3RhdGU6ICdzdWNjZXNzJyxcbiAgICAgIH0sXG4gICAgICB7XG4gICAgICAgIG1hdGNoZXI6ICdwYXRoJyxcbiAgICAgICAgYXJndW1lbnQ6ICdDb25maWd1cmF0aW9uLkxhc3RVcGRhdGVTdGF0dXMnLFxuICAgICAgICBleHBlY3RlZDogJ0ZhaWxlZCcsXG4gICAgICAgIHN0YXRlOiAnZmFpbHVyZScsXG4gICAgICB9LFxuICAgIF0sXG4gIH07XG5cbiAgY29uc3QgdXBkYXRlRnVuY3Rpb25Qcm9wZXJ0aWVzV2FpdGVyID0gbmV3IChBV1MgYXMgYW55KS5SZXNvdXJjZVdhaXRlcihsYW1iZGEsICd1cGRhdGVGdW5jdGlvblByb3BlcnRpZXNUb0ZpbmlzaCcpO1xuICBhd2FpdCB1cGRhdGVGdW5jdGlvblByb3BlcnRpZXNXYWl0ZXIud2FpdCh7XG4gICAgRnVuY3Rpb25OYW1lOiBmdW5jdGlvbk5hbWUsXG4gIH0pLnByb21pc2UoKTtcbn1cblxuLyoqXG4gKiBHZXQgZmlsZSBleHRlbnNpb24gZnJvbSBMYW1iZGEgcnVudGltZSBzdHJpbmcuXG4gKiBXZSB1c2UgdGhpcyBleHRlbnNpb24gdG8gY3JlYXRlIGEgZGVwbG95bWVudCBwYWNrYWdlIGZyb20gTGFtYmRhIGlubGluZSBjb2RlLlxuICovXG5mdW5jdGlvbiBkZXRlcm1pbmVDb2RlRmlsZUV4dEZyb21SdW50aW1lKHJ1bnRpbWU6IHN0cmluZyk6IHN0cmluZyB7XG4gIGlmIChydW50aW1lLnN0YXJ0c1dpdGgoJ25vZGUnKSkge1xuICAgIHJldHVybiAnanMnO1xuICB9XG4gIGlmIChydW50aW1lLnN0YXJ0c1dpdGgoJ3B5dGhvbicpKSB7XG4gICAgcmV0dXJuICdweSc7XG4gIH1cbiAgLy8gQ3VycmVudGx5IGlubGluZSBjb2RlIG9ubHkgc3VwcG9ydHMgTm9kZS5qcyBhbmQgUHl0aG9uLCBpZ25vcmluZyBvdGhlciBydW50aW1lcy5cbiAgLy8gaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL0FXU0Nsb3VkRm9ybWF0aW9uL2xhdGVzdC9Vc2VyR3VpZGUvYXdzLXByb3BlcnRpZXMtbGFtYmRhLWZ1bmN0aW9uLWNvZGUuaHRtbCNhd3MtcHJvcGVydGllcy1sYW1iZGEtZnVuY3Rpb24tY29kZS1wcm9wZXJ0aWVzXG4gIHRocm93IG5ldyBDZm5FdmFsdWF0aW9uRXhjZXB0aW9uKGBydW50aW1lICR7cnVudGltZX0gaXMgdW5zdXBwb3J0ZWQsIG9ubHkgbm9kZS5qcyBhbmQgcHl0aG9uIHJ1bnRpbWVzIGFyZSBjdXJyZW50bHkgc3VwcG9ydGVkLmApO1xufVxuXG4vKipcbiAqIEZpbmRzIGFsbCBWZXJzaW9ucyB0aGF0IHJlZmVyZW5jZSBhbiBBV1M6OkxhbWJkYTo6RnVuY3Rpb24gd2l0aCBsb2dpY2FsIElEIGBsb2dpY2FsSWRgXG4gKiBhbmQgQWxpYXNlcyB0aGF0IHJlZmVyZW5jZSB0aG9zZSBWZXJzaW9ucy5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gdmVyc2lvbnNBbmRBbGlhc2VzKGxvZ2ljYWxJZDogc3RyaW5nLCBldmFsdWF0ZUNmblRlbXBsYXRlOiBFdmFsdWF0ZUNsb3VkRm9ybWF0aW9uVGVtcGxhdGUpIHtcbiAgLy8gZmluZCBhbGwgTGFtYmRhIFZlcnNpb25zIHRoYXQgcmVmZXJlbmNlIHRoaXMgRnVuY3Rpb25cbiAgY29uc3QgdmVyc2lvbnNSZWZlcmVuY2luZ0Z1bmN0aW9uID0gZXZhbHVhdGVDZm5UZW1wbGF0ZS5maW5kUmVmZXJlbmNlc1RvKGxvZ2ljYWxJZClcbiAgICAuZmlsdGVyKHIgPT4gci5UeXBlID09PSAnQVdTOjpMYW1iZGE6OlZlcnNpb24nKTtcbiAgLy8gZmluZCBhbGwgTGFtYmRhIEFsaWFzZXMgdGhhdCByZWZlcmVuY2UgdGhlIGFib3ZlIFZlcnNpb25zXG4gIGNvbnN0IGFsaWFzZXNSZWZlcmVuY2luZ1ZlcnNpb25zID0gZmxhdE1hcCh2ZXJzaW9uc1JlZmVyZW5jaW5nRnVuY3Rpb24sIHYgPT5cbiAgICBldmFsdWF0ZUNmblRlbXBsYXRlLmZpbmRSZWZlcmVuY2VzVG8odi5Mb2dpY2FsSWQpKTtcbiAgY29uc3QgYWxpYXNlc05hbWVzID0gYXdhaXQgUHJvbWlzZS5hbGwoYWxpYXNlc1JlZmVyZW5jaW5nVmVyc2lvbnMubWFwKGEgPT5cbiAgICBldmFsdWF0ZUNmblRlbXBsYXRlLmV2YWx1YXRlQ2ZuRXhwcmVzc2lvbihhLlByb3BlcnRpZXM/Lk5hbWUpKSk7XG5cbiAgcmV0dXJuIHsgdmVyc2lvbnNSZWZlcmVuY2luZ0Z1bmN0aW9uLCBhbGlhc2VzTmFtZXMgfTtcbn1cblxuLyoqXG4gKiBSZW5kZXJzIHRoZSBzdHJpbmcgdXNlZCBpbiBkaXNwbGF5aW5nIEFsaWFzIHJlc291cmNlIG5hbWVzIHRoYXQgcmVmZXJlbmNlIHRoZSBzcGVjaWZpZWQgTGFtYmRhIEZ1bmN0aW9uXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHJlbmRlckFsaWFzZXMoXG4gIGxvZ2ljYWxJZDogc3RyaW5nLFxuICBldmFsdWF0ZUNmblRlbXBsYXRlOiBFdmFsdWF0ZUNsb3VkRm9ybWF0aW9uVGVtcGxhdGUsXG4gIGNhbGxiYWNrZm46ICh2YWx1ZTogYW55LCBpbmRleD