aws-cdk
Version:
CDK Toolkit, the command line tool for CDK apps
108 lines • 17.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.isHotswappableS3BucketDeploymentChange = exports.REQUIRED_BY_CFN = void 0;
const common_1 = require("./common");
/**
* This means that the value is required to exist by CloudFormation's Custom Resource API (or our S3 Bucket Deployment Lambda's API)
* but the actual value specified is irrelevant
*/
exports.REQUIRED_BY_CFN = 'required-to-be-present-by-cfn';
async function isHotswappableS3BucketDeploymentChange(logicalId, change, evaluateCfnTemplate) {
// In old-style synthesis, the policy used by the lambda to copy assets Ref's the assets directly,
// meaning that the changes made to the Policy are artifacts that can be safely ignored
const ret = [];
if (change.newValue.Type === 'AWS::IAM::Policy') {
return changeIsForS3DeployCustomResourcePolicy(logicalId, change, evaluateCfnTemplate);
}
if (change.newValue.Type !== 'Custom::CDKBucketDeployment') {
return [];
}
// no classification to be done here; all the properties of this custom resource thing are hotswappable
const customResourceProperties = await evaluateCfnTemplate.evaluateCfnExpression({
...change.newValue.Properties,
ServiceToken: undefined,
});
ret.push({
hotswappable: true,
resourceType: change.newValue.Type,
propsChanged: ['*'],
service: 'custom-s3-deployment',
resourceNames: [`Contents of S3 Bucket '${customResourceProperties.DestinationBucketName}'`],
apply: async (sdk) => {
// note that this gives the ARN of the lambda, not the name. This is fine though, the invoke() sdk call will take either
const functionName = await evaluateCfnTemplate.evaluateCfnExpression(change.newValue.Properties?.ServiceToken);
if (!functionName) {
return;
}
await sdk.lambda().invoke({
FunctionName: functionName,
// Lambda refuses to take a direct JSON object and requires it to be stringify()'d
Payload: JSON.stringify({
RequestType: 'Update',
ResponseURL: exports.REQUIRED_BY_CFN,
PhysicalResourceId: exports.REQUIRED_BY_CFN,
StackId: exports.REQUIRED_BY_CFN,
RequestId: exports.REQUIRED_BY_CFN,
LogicalResourceId: exports.REQUIRED_BY_CFN,
ResourceProperties: stringifyObject(customResourceProperties), // JSON.stringify() doesn't turn the actual objects to strings, but the lambda expects strings
}),
}).promise();
},
});
return ret;
}
exports.isHotswappableS3BucketDeploymentChange = isHotswappableS3BucketDeploymentChange;
async function changeIsForS3DeployCustomResourcePolicy(iamPolicyLogicalId, change, evaluateCfnTemplate) {
const roles = change.newValue.Properties?.Roles;
if (!roles) {
return (0, common_1.reportNonHotswappableResource)(change, 'This IAM Policy does not have have any Roles');
}
for (const role of roles) {
const roleArn = await evaluateCfnTemplate.evaluateCfnExpression(role);
const roleLogicalId = await evaluateCfnTemplate.findLogicalIdForPhysicalName(roleArn);
if (!roleLogicalId) {
return (0, common_1.reportNonHotswappableResource)(change, `could not find logicalId for role with name '${roleArn}'`);
}
const roleRefs = evaluateCfnTemplate.findReferencesTo(roleLogicalId);
for (const roleRef of roleRefs) {
if (roleRef.Type === 'AWS::Lambda::Function') {
const lambdaRefs = evaluateCfnTemplate.findReferencesTo(roleRef.LogicalId);
for (const lambdaRef of lambdaRefs) {
// If S3Deployment -> Lambda -> Role and IAM::Policy -> Role, then this IAM::Policy change is an
// artifact of old-style synthesis
if (lambdaRef.Type !== 'Custom::CDKBucketDeployment') {
return (0, common_1.reportNonHotswappableResource)(change, `found an AWS::IAM::Policy that has Role '${roleLogicalId}' that is referred to by AWS::Lambda::Function '${roleRef.LogicalId}' that is referred to by ${lambdaRef.Type} '${lambdaRef.LogicalId}', which does not have type 'Custom::CDKBucketDeployment'`);
}
}
}
else if (roleRef.Type === 'AWS::IAM::Policy') {
if (roleRef.LogicalId !== iamPolicyLogicalId) {
return (0, common_1.reportNonHotswappableResource)(change, `found an AWS::IAM::Policy that has Role '${roleLogicalId}' that is referred to by AWS::IAM::Policy '${roleRef.LogicalId}' that is not the policy of the s3 bucket deployment`);
}
}
else {
return (0, common_1.reportNonHotswappableResource)(change, `found a resource which refers to the role '${roleLogicalId}' that is not of type AWS::Lambda::Function or AWS::IAM::Policy, so the bucket deployment cannot be hotswapped`);
}
}
}
// this doesn't block the hotswap, but it also isn't a hotswappable change by itself. Return
// an empty change to signify this.
return [];
}
function stringifyObject(obj) {
if (obj == null) {
return obj;
}
if (Array.isArray(obj)) {
return obj.map(stringifyObject);
}
if (typeof obj !== 'object') {
return obj.toString();
}
const ret = {};
for (const [k, v] of Object.entries(obj)) {
ret[k] = stringifyObject(v);
}
return ret;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiczMtYnVja2V0LWRlcGxveW1lbnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiczMtYnVja2V0LWRlcGxveW1lbnRzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHFDQUEyRztBQUkzRzs7O0dBR0c7QUFDVSxRQUFBLGVBQWUsR0FBRywrQkFBK0IsQ0FBQztBQUV4RCxLQUFLLFVBQVUsc0NBQXNDLENBQzFELFNBQWlCLEVBQUUsTUFBbUMsRUFBRSxtQkFBbUQ7SUFFM0csa0dBQWtHO0lBQ2xHLHVGQUF1RjtJQUN2RixNQUFNLEdBQUcsR0FBd0IsRUFBRSxDQUFDO0lBQ3BDLElBQUksTUFBTSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEtBQUssa0JBQWtCLEVBQUU7UUFDL0MsT0FBTyx1Q0FBdUMsQ0FBQyxTQUFTLEVBQUUsTUFBTSxFQUFFLG1CQUFtQixDQUFDLENBQUM7S0FDeEY7SUFFRCxJQUFJLE1BQU0sQ0FBQyxRQUFRLENBQUMsSUFBSSxLQUFLLDZCQUE2QixFQUFFO1FBQzFELE9BQU8sRUFBRSxDQUFDO0tBQ1g7SUFFRCx1R0FBdUc7SUFDdkcsTUFBTSx3QkFBd0IsR0FBRyxNQUFNLG1CQUFtQixDQUFDLHFCQUFxQixDQUFDO1FBQy9FLEdBQUcsTUFBTSxDQUFDLFFBQVEsQ0FBQyxVQUFVO1FBQzdCLFlBQVksRUFBRSxTQUFTO0tBQ3hCLENBQUMsQ0FBQztJQUVILEdBQUcsQ0FBQyxJQUFJLENBQUM7UUFDUCxZQUFZLEVBQUUsSUFBSTtRQUNsQixZQUFZLEVBQUUsTUFBTSxDQUFDLFFBQVEsQ0FBQyxJQUFJO1FBQ2xDLFlBQVksRUFBRSxDQUFDLEdBQUcsQ0FBQztRQUNuQixPQUFPLEVBQUUsc0JBQXNCO1FBQy9CLGFBQWEsRUFBRSxDQUFDLDBCQUEwQix3QkFBd0IsQ0FBQyxxQkFBcUIsR0FBRyxDQUFDO1FBQzVGLEtBQUssRUFBRSxLQUFLLEVBQUUsR0FBUyxFQUFFLEVBQUU7WUFDekIsd0hBQXdIO1lBQ3hILE1BQU0sWUFB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RUFBRSxDQUFDO0FBQ1osQ0FBQztBQUVELFNBQVMsZUFBZSxDQUFDLEdBQVE7SUFDL0IsSUFBSSxHQUFHLElBQUksSUFBSSxFQUFFO1FBQ2YsT0FBTyxHQUFHLENBQUM7S0FDWjtJQUNELElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsRUFBRTtRQUN0QixPQUFPLEdBQUcsQ0FBQyxHQUFHLENBQUMsZUFBZSxDQUFDLENBQUM7S0FDakM7SUFDRCxJQUFJLE9BQU8sR0FBRyxLQUFLLFFBQVEsRUFBRTtRQUMzQixPQUFPLEdBQUcsQ0FBQyxRQUFRLEVBQUUsQ0FBQztLQUN2QjtJQUVELE1BQU0sR0FBRyxHQUF5QixFQUFFLENBQUM7SUFDckMsS0FBSyxNQUFNLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUU7UUFDeEMsR0FBRyxDQUFDLENBQUMsQ0FBQyxHQUFHLGVBQWUsQ0FBQyxDQUFDLENBQUMsQ0FBQztLQUM3QjtJQUNELE9BQU8sR0FBRyxDQUFDO0FBQ2IsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENoYW5nZUhvdHN3YXBSZXN1bHQsIEhvdHN3YXBwYWJsZUNoYW5nZUNhbmRpZGF0ZSwgcmVwb3J0Tm9uSG90c3dhcHBhYmxlUmVzb3VyY2UgfSBmcm9tICcuL2NvbW1vbic7XG5pbXBvcnQgeyBJU0RLIH0gZnJvbSAnLi4vYXdzLWF1dGgnO1xuaW1wb3J0IHsgRXZhbHVhdGVDbG91ZEZvcm1hdGlvblRlbXBsYXRlIH0gZnJvbSAnLi4vZXZhbHVhdGUtY2xvdWRmb3JtYXRpb24tdGVtcGxhdGUnO1xuXG4vKipcbiAqIFRoaXMgbWVhbnMgdGhhdCB0aGUgdmFsdWUgaXMgcmVxdWlyZWQgdG8gZXhpc3QgYnkgQ2xvdWRGb3JtYXRpb24ncyBDdXN0b20gUmVzb3VyY2UgQVBJIChvciBvdXIgUzMgQnVja2V0IERlcGxveW1lbnQgTGFtYmRhJ3MgQVBJKVxuICogYnV0IHRoZSBhY3R1YWwgdmFsdWUgc3BlY2lmaWVkIGlzIGlycmVsZXZhbnRcbiAqL1xuZXhwb3J0IGNvbnN0IFJFUVVJUkVEX0JZX0NGTiA9ICdyZXF1aXJlZC10by1iZS1wcmVzZW50LWJ5LWNmbic7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBpc0hvdHN3YXBwYWJsZVMzQnVja2V0RGVwbG95bWVudENoYW5nZShcbiAgbG9naWNhbElkOiBzdHJpbmcsIGNoYW5nZTogSG90c3dhcHBhYmxlQ2hhbmdlQ2FuZGlkYXRlLCBldmFsdWF0ZUNmblRlbXBsYXRlOiBFdmFsdWF0ZUNsb3VkRm9ybWF0aW9uVGVtcGxhdGUsXG4pOiBQcm9taXNlPENoYW5nZUhvdHN3YXBSZXN1bHQ+IHtcbiAgLy8gSW4gb2xkLXN0eWxlIHN5bnRoZXNpcywgdGhlIHBvbGljeSB1c2VkIGJ5IHRoZSBsYW1iZGEgdG8gY29weSBhc3NldHMgUmVmJ3MgdGhlIGFzc2V0cyBkaXJlY3RseSxcbiAgLy8gbWVhbmluZyB0aGF0IHRoZSBjaGFuZ2VzIG1hZGUgdG8gdGhlIFBvbGljeSBhcmUgYXJ0aWZhY3RzIHRoYXQgY2FuIGJlIHNhZmVseSBpZ25vcmVkXG4gIGNvbnN0IHJldDogQ2hhbmdlSG90c3dhcFJlc3VsdCA9IFtdO1xuICBpZiAoY2hhbmdlLm5ld1ZhbHVlLlR5cGUgPT09ICdBV1M6OklBTTo6UG9saWN5Jykge1xuICAgIHJldHVybiBjaGFuZ2VJc0ZvclMzRGVwbG95Q3VzdG9tUmVzb3VyY2VQb2xpY3kobG9naWNhbElkLCBjaGFuZ2UsIGV2YWx1YXRlQ2ZuVGVtcGxhdGUpO1xuICB9XG5cbiAgaWYgKGNoYW5nZS5uZXdWYWx1ZS5UeXBlICE9PSAnQ3VzdG9tOjpDREtCdWNrZXREZXBsb3ltZW50Jykge1xuICAgIHJldHVybiBbXTtcbiAgfVxuXG4gIC8vIG5vIGNsYXNzaWZpY2F0aW9uIHRvIGJlIGRvbmUgaGVyZTsgYWxsIHRoZSBwcm9wZXJ0aWVzIG9mIHRoaXMgY3VzdG9tIHJlc291cmNlIHRoaW5nIGFyZSBob3Rzd2FwcGFibGVcbiAgY29uc3QgY3VzdG9tUmVzb3VyY2VQcm9wZXJ0aWVzID0gYXdhaXQgZXZhbHVhdGVDZm5UZW1wbGF0ZS5ldmFsdWF0ZUNmbkV4cHJlc3Npb24oe1xuICAgIC4uLmNoYW5nZS5uZXdWYWx1ZS5Qcm9wZXJ0aWVzLFxuICAgIFNlcnZpY2VUb2tlbjogdW5kZWZpbmVkLFxuICB9KTtcblxuICByZXQucHVzaCh7XG4gICAgaG90c3dhcHBhYmxlOiB0cnVlLFxuICAgIHJlc291cmNlVHlwZTogY2hhbmdlLm5ld1ZhbHVlLlR5cGUsXG4gICAgcHJvcHNDaGFuZ2VkOiBbJyonXSxcbiAgICBzZXJ2aWNlOiAnY3VzdG9tLXMzLWRlcGxveW1lbnQnLFxuICAgIHJlc291cmNlTmFtZXM6IFtgQ29udGVudHMgb2YgUzMgQnVja2V0ICcke2N1c3RvbVJlc291cmNlUHJvcGVydGllcy5EZXN0aW5hdGlvbkJ1Y2tldE5hbWV9J2BdLFxuICAgIGFwcGx5OiBhc3luYyAoc2RrOiBJU0RLKSA9PiB7XG4gICAgICAvLyBub3RlIHRoYXQgdGhpcyBnaXZlcyB0aGUgQVJOIG9mIHRoZSBsYW1iZGEsIG5vdCB0aGUgbmFtZS4gVGhpcyBpcyBmaW5lIHRob3VnaCwgdGhlIGludm9rZSgpIHNkayBjYWxsIHdpbGwgdGFrZSBlaXRoZXJcbiAgICAgIGNvbnN0IGZ1bmN0aW9uTmFtZSA9IGF3YWl0IGV2YWx1YXRlQ2ZuVGVtcGxhdGUuZXZhbHVhdGVDZm5FeHByZXNzaW9uKGNoYW5nZS5uZXdWYWx1ZS5Qcm9wZXJ0aWVzPy5TZXJ2aWNlVG9rZW4pO1xuICAgICAgaWYgKCFmdW5jdGlvbk5hbWUpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBhd2FpdCBzZGsubGFtYmRhKCkuaW52b2tlKHtcbiAgICAgICAgRnVuY3Rpb25OYW1lOiBmdW5jdGlvbk5hbWUsXG4gICAgICAgIC8vIExhbWJkYSByZWZ1c2VzIHRvIHRha2UgYSBkaXJlY3QgSlNPTiBvYmplY3QgYW5kIHJlcXVpcmVzIGl0IHRvIGJlIHN0cmluZ2lmeSgpJ2RcbiAgICAgICAgUGF5bG9hZDogSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICAgIFJlcXVlc3RUeXBlOiAnVXBkYXRlJyxcbiAgICAgICAgICBSZXNwb25zZVVSTDogUkVRVUlSRURfQllfQ0ZOLFxuICAgICAgICAgIFBoeXNpY2FsUmVzb3VyY2VJZDogUkVRVUlSRURfQllfQ0ZOLFxuICAgICAgICAgIFN0YWNrSWQ6IFJFUVVJUkVEX0JZX0NGTixcbiAgICAgICAgICBSZXF1ZXN0SWQ6IFJFUVVJUkVEX0JZX0NGTixcbiAgICAgICAgICBMb2dpY2FsUmVzb3VyY2VJZDogUkVRVUlSRURfQllfQ0ZOLFxuICAgICAgICAgIFJlc291cmNlUHJvcGVydGllczogc3RyaW5naWZ5T2JqZWN0KGN1c3RvbVJlc291cmNlUHJvcGVydGllcyksIC8vIEpTT04uc3RyaW5naWZ5KCkgZG9lc24ndCB0dXJuIHRoZSBhY3R1YWwgb2JqZWN0cyB0byBzdHJpbmdzLCBidXQgdGhlIGxhbWJkYSBleHBlY3RzIHN0cmluZ3NcbiAgICAgICAgfSksXG4gICAgICB9KS5wcm9taXNlKCk7XG4gICAgfSxcbiAgfSk7XG5cbiAgcmV0dXJuIHJldDtcbn1cblxuYXN5bmMgZnVuY3Rpb24gY2hhbmdlSXNGb3JTM0RlcGxveUN1c3RvbVJlc291cmNlUG9saWN5KFxuICBpYW1Qb2xpY3lMb2dpY2FsSWQ6IHN0cmluZywgY2hhbmdlOiBIb3Rzd2FwcGFibGVDaGFuZ2VDYW5kaWRhdGUsIGV2YWx1YXRlQ2ZuVGVtcGxhdGU6IEV2YWx1YXRlQ2xvdWRGb3JtYXRpb25UZW1wbGF0ZSxcbik6IFByb21pc2U8Q2hhbmdlSG90c3dhcFJlc3VsdD4ge1xuICBjb25zdCByb2xlcyA9IGNoYW5nZS5uZXdWYWx1ZS5Qcm9wZXJ0aWVzPy5Sb2xlcztcbiAgaWYgKCFyb2xlcykge1xuICAgIHJldHVybiByZXBvcnROb25Ib3Rzd2FwcGFibGVSZXNvdXJjZShcbiAgICAgIGNoYW5nZSxcbiAgICAgICdUaGlzIElBTSBQb2xpY3kgZG9lcyBub3QgaGF2ZSBoYXZlIGFueSBSb2xlcycsXG4gICAgKTtcbiAgfVxuXG4gIGZvciAoY29uc3Qgcm9sZSBvZiByb2xlcykge1xuICAgIGNvbnN0IHJvbGVBcm4gPSBhd2FpdCBldmFsdWF0ZUNmblRlbXBsYXRlLmV2YWx1YXRlQ2ZuRXhwcmVzc2lvbihyb2xlKTtcbiAgICBjb25zdCByb2xlTG9naWNhbElkID0gYXdhaXQgZXZhbHVhdGVDZm5UZW1wbGF0ZS5maW5kTG9naWNhbElkRm9yUGh5c2ljYWxOYW1lKHJvbGVBcm4pO1xuICAgIGlmICghcm9sZUxvZ2ljYWxJZCkge1xuICAgICAgcmV0dXJuIHJlcG9ydE5vbkhvdHN3YXBwYWJsZVJlc291cmNlKFxuICAgICAgICBjaGFuZ2UsXG4gICAgICAgIGBjb3VsZCBub3QgZmluZCBsb2dpY2FsSWQgZm9yIHJvbGUgd2l0aCBuYW1lICcke3JvbGVBcm59J2AsXG4gICAgICApO1xuICAgIH1cblxuICAgIGNvbnN0IHJvbGVSZWZzID0gZXZhbHVhdGVDZm5UZW1wbGF0ZS5maW5kUmVmZXJlbmNlc1RvKHJvbGVMb2dpY2FsSWQpO1xuICAgIGZvciAoY29uc3Qgcm9sZVJlZiBvZiByb2xlUmVmcykge1xuICAgICAgaWYgKHJvbGVSZWYuVHlwZSA9PT0gJ0FXUzo6TGFtYmRhOjpGdW5jdGlvbicpIHtcbiAgICAgICAgY29uc3QgbGFtYmRhUmVmcyA9IGV2YWx1YXRlQ2ZuVGVtcGxhdGUuZmluZFJlZmVyZW5jZXNUbyhyb2xlUmVmLkxvZ2ljYWxJZCk7XG4gICAgICAgIGZvciAoY29uc3QgbGFtYmRhUmVmIG9mIGxhbWJkYVJlZnMpIHtcbiAgICAgICAgICAvLyBJZiBTM0RlcGxveW1lbnQgLT4gTGFtYmRhIC0+IFJvbGUgYW5kIElBTTo6UG9saWN5IC0+IFJvbGUsIHRoZW4gdGhpcyBJQU06OlBvbGljeSBjaGFuZ2UgaXMgYW5cbiAgICAgICAgICAvLyBhcnRpZmFjdCBvZiBvbGQtc3R5bGUgc3ludGhlc2lzXG4gICAgICAgICAgaWYgKGxhbWJkYVJlZi5UeXBlICE9PSAnQ3VzdG9tOjpDREtCdWNrZXREZXBsb3ltZW50Jykge1xuICAgICAgICAgICAgcmV0dXJuIHJlcG9ydE5vbkhvdHN3YXBwYWJsZVJlc291cmNlKFxuICAgICAgICAgICAgICBjaGFuZ2UsXG4gICAgICAgICAgICAgIGBmb3VuZCBhbiBBV1M6OklBTTo6UG9saWN5IHRoYXQgaGFzIFJvbGUgJyR7cm9sZUxvZ2ljYWxJZH0nIHRoYXQgaXMgcmVmZXJyZWQgdG8gYnkgQVdTOjpMYW1iZGE6OkZ1bmN0aW9uICcke3JvbGVSZWYuTG9naWNhbElkfScgdGhhdCBpcyByZWZlcnJlZCB0byBieSAke2xhbWJkYVJlZi5UeXBlfSAnJHtsYW1iZGFSZWYuTG9naWNhbElkfScsIHdoaWNoIGRvZXMgbm90IGhhdmUgdHlwZSAnQ3VzdG9tOjpDREtCdWNrZXREZXBsb3ltZW50J2AsXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChyb2xlUmVmLlR5cGUgPT09ICdBV1M6OklBTTo6UG9saWN5Jykge1xuICAgICAgICBpZiAocm9sZVJlZi5Mb2dpY2FsSWQgIT09IGlhbVBvbGljeUxvZ2ljYWxJZCkge1xuICAgICAgICAgIHJldHVybiByZXBvcnROb25Ib3Rzd2FwcGFibGVSZXNvdXJjZShcbiAgICAgICAgICAgIGNoYW5nZSxcbiAgICAgICAgICAgIGBmb3VuZCBhbiBBV1M6OklBTTo6UG9saWN5IHRoYXQgaGFzIFJvbGUgJyR7cm9sZUxvZ2ljYWxJZH0nIHRoYXQgaXMgcmVmZXJyZWQgdG8gYnkgQVdTOjpJQU06OlBvbGljeSAnJHtyb2xlUmVmLkxvZ2ljYWxJZH0nIHRoYXQgaXMgbm90IHRoZSBwb2xpY3kgb2YgdGhlIHMzIGJ1Y2tldCBkZXBsb3ltZW50YCxcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gcmVwb3J0Tm9uSG90c3dhcHBhYmxlUmVzb3VyY2UoXG4gICAgICAgICAgY2hhbmdlLFxuICAgICAgICAgIGBmb3VuZCBhIHJlc291cmNlIHdoaWNoIHJlZmVycyB0byB0aGUgcm9sZSAnJHtyb2xlTG9naWNhbElkfScgdGhhdCBpcyBub3Qgb2YgdHlwZSBBV1M6OkxhbWJkYTo6RnVuY3Rpb24gb3IgQVdTOjpJQU06OlBvbGljeSwgc28gdGhlIGJ1Y2tldCBkZXBsb3ltZW50IGNhbm5vdCBiZSBob3Rzd2FwcGVkYCxcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLyB0aGlzIGRvZXNuJ3QgYmxvY2sgdGhlIGhvdHN3YXAsIGJ1dCBpdCBhbHNvIGlzbid0IGEgaG90c3dhcHBhYmxlIGNoYW5nZSBieSBpdHNlbGYuIFJldHVyblxuICAvLyBhbiBlbXB0eSBjaGFuZ2UgdG8gc2lnbmlmeSB0aGlzLlxuICByZXR1cm4gW107XG59XG5cbmZ1bmN0aW9uIHN0cmluZ2lmeU9iamVjdChvYmo6IGFueSk6IGFueSB7XG4gIGlmIChvYmogPT0gbnVsbCkge1xuICAgIHJldHVybiBvYmo7XG4gIH1cbiAgaWYgKEFycmF5LmlzQXJyYXkob2JqKSkge1xuICAgIHJldHVybiBvYmoubWFwKHN0cmluZ2lmeU9iamVjdCk7XG4gIH1cbiAgaWYgKHR5cGVvZiBvYmogIT09ICdvYmplY3QnKSB7XG4gICAgcmV0dXJuIG9iai50b1N0cmluZygpO1xuICB9XG5cbiAgY29uc3QgcmV0OiB7IFtrOiBzdHJpbmddOiBhbnkgfSA9IHt9O1xuICBmb3IgKGNvbnN0IFtrLCB2XSBvZiBPYmplY3QuZW50cmllcyhvYmopKSB7XG4gICAgcmV0W2tdID0gc3RyaW5naWZ5T2JqZWN0KHYpO1xuICB9XG4gIHJldHVybiByZXQ7XG59XG4iXX0=