ness
Version:
✪ No-effort static sites deployed to your AWS account.
424 lines • 58.4 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (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 (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.syncLocalToS3 = exports.clearS3Bucket = exports.getCloudFormationFailureReason = exports.getCloudFormationStackOutputs = exports.deleteCloudFormationStack = exports.deployStack = exports.getStack = exports.getHostedZoneNameservers = exports.getCertificateArn = exports.invalidateDistribution = exports.getDistribution = exports.cleanupHostedZoneRecords = exports.deleteHostedZoneRecords = exports.getHostedZoneARecord = exports.getHostedZoneRecordSets = exports.getHostedZone = void 0;
const path = __importStar(require("path"));
const fs = __importStar(require("fs-extra"));
const crypto = __importStar(require("crypto"));
const client_route_53_1 = require("@aws-sdk/client-route-53");
const client_s3_1 = require("@aws-sdk/client-s3");
const client_acm_1 = require("@aws-sdk/client-acm");
const client_cloudformation_1 = require("@aws-sdk/client-cloudformation");
const client_cloudfront_1 = require("@aws-sdk/client-cloudfront");
const uuid = __importStar(require("uuid"));
const mime = __importStar(require("mime"));
const cloudformation_1 = require("./cloudformation");
const context_1 = require("../../context");
const yaml = __importStar(require("./yaml"));
const file_1 = require("../../utils/file");
const region = 'us-east-1';
/**
* Get an existing HostedZone for a given domain
*
* @param domain Domain to lookup
*/
async function getHostedZone(domain) {
var _a;
try {
const route53 = new client_route_53_1.Route53({ region });
const response = await route53.listHostedZonesByName({ DNSName: domain });
const hostedZone = (_a = response.HostedZones) === null || _a === void 0 ? void 0 : _a.find((zone) => { var _a; return zone.Name === `${domain}.` && ((_a = zone.Config) === null || _a === void 0 ? void 0 : _a.Comment) !== 'Created by Ness'; });
if (!hostedZone || !hostedZone.Id)
return undefined;
const id = path.basename(hostedZone.Id);
const name = hostedZone.Name;
return {
id,
name,
};
}
catch (_b) {
return undefined;
}
}
exports.getHostedZone = getHostedZone;
/**
* Get record sets from a Route53 hosted zone.
*
* @param hostedZoneId HostedZoneId of the hosted zone that should be cleaned up
*/
async function getHostedZoneRecordSets(hostedZoneId) {
try {
const route53 = new client_route_53_1.Route53({ region });
const recordSets = await route53.listResourceRecordSets({ HostedZoneId: hostedZoneId });
return recordSets.ResourceRecordSets;
}
catch (_a) {
return undefined;
}
}
exports.getHostedZoneRecordSets = getHostedZoneRecordSets;
/**
* Get any existing A record for a given hosted zone.
*
* @param hostedZoneId HostedZoneId of the hosted zone that should be cleaned up
*/
async function getHostedZoneARecord(hostedZoneId, domain) {
try {
const recordSets = await getHostedZoneRecordSets(hostedZoneId);
if (!recordSets)
return;
const aRecord = recordSets.find((record) => record.Name === domain && record.Type === 'A');
return aRecord;
}
catch (_a) {
return undefined;
}
}
exports.getHostedZoneARecord = getHostedZoneARecord;
/**
* Deletes record sets from a hosted zone.
*
* @param hostedZoneId HostedZoneId of the hosted zone that should be cleaned up
* @param records Record sets to be deleted
*/
async function deleteHostedZoneRecords(hostedZoneId, records) {
const route53 = new client_route_53_1.Route53({ region });
const changes = records.map((record) => ({
Action: 'DELETE',
ResourceRecordSet: record,
}));
await route53.changeResourceRecordSets({
HostedZoneId: hostedZoneId,
ChangeBatch: { Changes: changes },
});
}
exports.deleteHostedZoneRecords = deleteHostedZoneRecords;
/**
* Clear out any records in a Hosted Zone that were created by validating
* a DNS Validated Certificate through ACM.
*
* @param hostedZoneId HostedZoneId of the hosted zone that should be cleaned up
*/
async function cleanupHostedZoneRecords(hostedZoneId) {
const recordSets = await getHostedZoneRecordSets(hostedZoneId);
if (!recordSets)
return;
const targets = recordSets.filter((record) => {
var _a;
return record.Type === 'CNAME' &&
((_a = record.ResourceRecords) === null || _a === void 0 ? void 0 : _a.find((v) => { var _a; return (_a = v.Value) === null || _a === void 0 ? void 0 : _a.endsWith('acm-validations.aws.'); }));
});
if (!targets || targets.length === 0)
return;
await deleteHostedZoneRecords(hostedZoneId, targets);
}
exports.cleanupHostedZoneRecords = cleanupHostedZoneRecords;
/**
* Get an existing Cloudfront Distribution for a given domain
*
* @param domain Domain to lookup
*/
async function getDistribution(domain) {
var _a, _b;
try {
const cloudfront = new client_cloudfront_1.CloudFront({ region });
const response = await cloudfront.listDistributions({});
const distribution = (_b = (_a = response.DistributionList) === null || _a === void 0 ? void 0 : _a.Items) === null || _b === void 0 ? void 0 : _b.find((distro) => { var _a, _b; return ((_b = (_a = distro.Aliases) === null || _a === void 0 ? void 0 : _a.Items) === null || _b === void 0 ? void 0 : _b.find((a) => a === domain)) && distro.Comment !== 'Created by Ness'; });
return distribution;
}
catch (_c) {
return undefined;
}
}
exports.getDistribution = getDistribution;
async function invalidateDistribution(distributionId, paths = ['/*']) {
var _a;
const cloudfront = new client_cloudfront_1.CloudFront({ region });
const invalidation = await cloudfront.createInvalidation({
DistributionId: distributionId,
InvalidationBatch: {
CallerReference: Date.now().toString(),
Paths: { Quantity: paths.length, Items: paths },
},
});
await (0, client_cloudfront_1.waitForInvalidationCompleted)({ client: cloudfront, maxWaitTime: 5 * 60 }, { DistributionId: distributionId, Id: (_a = invalidation.Invalidation) === null || _a === void 0 ? void 0 : _a.Id });
}
exports.invalidateDistribution = invalidateDistribution;
/**
* Get an existing certificate ARN for a given domain
*
* @param domain Domain to lookup
*/
async function getCertificateArn(domain) {
var _a;
try {
const acm = new client_acm_1.ACM({ region });
let nextToken = undefined;
do {
const certificates = await acm.listCertificates({
CertificateStatuses: ['ISSUED'],
NextToken: nextToken,
});
const certificate = (_a = certificates === null || certificates === void 0 ? void 0 : certificates.CertificateSummaryList) === null || _a === void 0 ? void 0 : _a.find((cert) => cert.DomainName === domain);
if (certificate)
return certificate.CertificateArn;
nextToken = certificates === null || certificates === void 0 ? void 0 : certificates.NextToken;
} while (nextToken);
return undefined;
}
catch (_b) {
return undefined;
}
}
exports.getCertificateArn = getCertificateArn;
async function getHostedZoneNameservers(hostedZoneId) {
var _a;
try {
const route53 = new client_route_53_1.Route53({ region });
const hostedZone = await route53.getHostedZone({ Id: hostedZoneId });
if (!hostedZone)
return undefined;
return (_a = hostedZone.DelegationSet) === null || _a === void 0 ? void 0 : _a.NameServers;
}
catch (_b) {
return undefined;
}
}
exports.getHostedZoneNameservers = getHostedZoneNameservers;
async function getStack(stack, parameters) {
const stackName = (0, context_1.getStackId)(stack);
const contents = await fs.readFile(path.resolve(__dirname, `../../../static/stacks/${stack}.yaml`), 'utf-8');
const template = yaml.deserialize(contents);
const getCodeFromLambda = (resource) => {
const lambda = resource;
if (!lambda)
return undefined;
return lambda.Properties.Code.ZipFile ? lambda.Properties.Code.ZipFile['Fn::Sub'] : undefined;
};
const getS3KeyFromLambda = (resource) => {
var _a;
const lambda = resource;
if (!lambda)
return undefined;
return (_a = lambda.Properties.Code.S3Key) === null || _a === void 0 ? void 0 : _a.Ref;
};
const resources = template.Resources;
const functions = Object.keys(resources)
.filter((resource) => resources[resource].Type === 'AWS::Lambda::Function')
.map((resource) => ({
key: resource,
code: getCodeFromLambda(resources[resource]),
s3key: getS3KeyFromLambda(resources[resource]),
}))
.filter(({ code, s3key }) => code || s3key);
const versions = Object.keys(resources)
.filter((resource) => resources[resource].Type === 'AWS::Lambda::Version')
.map((resource) => ({
key: resource,
functionName: resources[resource].Properties
.FunctionName.Ref,
}));
let updatedContents = contents;
for (const lambdaFunction of functions) {
const { key, code, s3key } = lambdaFunction;
let hash = '';
if (code) {
let codeWithReplacements = code;
for (const param of Object.keys(parameters)) {
const value = parameters[param];
if (value === undefined)
continue;
codeWithReplacements = codeWithReplacements.replace('${' + param + '}', value);
}
hash = crypto.createHash('sha256').update(codeWithReplacements).digest('hex');
}
else {
if (!s3key || !parameters[s3key])
continue;
const key = parameters[s3key];
// default-lambda.<hash>.zip
hash = key.split('.')[1];
}
const version = versions.find((version) => version.functionName === key);
if (!version)
continue;
const regex = new RegExp(version.key, 'gi');
updatedContents = updatedContents.replace(regex, hash);
}
return {
stackName,
parameters,
template: yaml.deserialize(updatedContents),
};
}
exports.getStack = getStack;
async function deployStack(options) {
const { stack } = options;
const { stackName, parameters, template } = stack;
const cfn = new client_cloudformation_1.CloudFormation({ region });
let cloudFormationStack = await cloudformation_1.CloudFormationStack.lookup(cfn, stackName);
if (cloudFormationStack.stackStatus.isCreationFailure) {
await cfn.deleteStack({ StackName: stackName });
const deletedStack = await (0, cloudformation_1.waitForStackDelete)(cfn, stackName);
if (deletedStack && deletedStack.stackStatus.name !== 'DELETE_COMPLETE') {
throw new Error(`Failed deleting stack ${stackName} that had previously failed creation (current state: ${deletedStack.stackStatus})`);
}
// Update variable to mark that the stack does not exist anymore, but avoid
// doing an actual lookup in CloudFormation (which would be silly to do if
// we just deleted it).
cloudFormationStack = cloudformation_1.CloudFormationStack.doesNotExist(cfn, stackName);
}
const templateParams = cloudformation_1.TemplateParameters.fromTemplate(template);
const stackParams = templateParams.supplyAll(parameters);
const templateJson = (0, cloudformation_1.toYAML)(stack.template);
const executionId = uuid.v4();
const changeSetName = `ness-${executionId}`;
const update = cloudFormationStack.exists && cloudFormationStack.stackStatus.name !== 'REVIEW_IN_PROGRESS';
await cfn.createChangeSet({
StackName: stackName,
ChangeSetName: changeSetName,
ChangeSetType: update ? 'UPDATE' : 'CREATE',
Description: `Ness changeset for execution ${executionId}`,
TemplateBody: templateJson,
Parameters: stackParams.apiParameters,
Capabilities: ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND'],
// RoleARN: options.roleArn,
// NotificationARNs: options.notificationArns,
// Tags: options.tags,
});
const changeSetDescription = await (0, cloudformation_1.waitForChangeSet)(cfn, stackName, changeSetName);
if ((0, cloudformation_1.changeSetHasNoChanges)(changeSetDescription)) {
await cfn.deleteChangeSet({ StackName: stackName, ChangeSetName: changeSetName });
return cloudFormationStack.outputs;
}
await cfn.executeChangeSet({ StackName: stackName, ChangeSetName: changeSetName });
const finalStack = await (0, cloudformation_1.waitForStackDeploy)(cfn, stackName);
// This shouldn't really happen, but catch it anyway. You never know.
if (!finalStack) {
throw new Error('Stack deploy failed (the stack disappeared while we were deploying it)');
}
cloudFormationStack = finalStack;
return cloudFormationStack.outputs;
}
exports.deployStack = deployStack;
async function deleteCloudFormationStack(stack, retain = undefined) {
const cfn = new client_cloudformation_1.CloudFormation({ region });
const currentStack = await cloudformation_1.CloudFormationStack.lookup(cfn, stack);
if (!currentStack.exists) {
return;
}
await cfn.deleteStack({ StackName: stack, RetainResources: retain });
const destroyedStack = await (0, cloudformation_1.waitForStackDelete)(cfn, stack);
if (destroyedStack && destroyedStack.stackStatus.name !== 'DELETE_COMPLETE') {
throw new Error(`Failed to destroy ${stack}: ${destroyedStack.stackStatus}`);
}
}
exports.deleteCloudFormationStack = deleteCloudFormationStack;
async function getCloudFormationStackOutputs(stack) {
const cfn = new client_cloudformation_1.CloudFormation({ region });
const currentStack = await cloudformation_1.CloudFormationStack.lookup(cfn, stack);
if (!currentStack.exists) {
return undefined;
}
return currentStack.outputs;
}
exports.getCloudFormationStackOutputs = getCloudFormationStackOutputs;
async function getCloudFormationFailureReason(stack) {
const cf = new client_cloudformation_1.CloudFormation({ region });
const { StackEvents: events } = (await cf.describeStackEvents({ StackName: stack })) || {};
if (!events)
return undefined;
for (const event of events) {
const { ResourceStatus: status, ResourceStatusReason: reason } = event;
if (!status || !reason)
continue;
if (['CREATE_FAILED', 'UPDATE_FAILED'].includes(status) &&
reason !== 'Resource creation cancelled') {
return reason;
}
}
return undefined;
}
exports.getCloudFormationFailureReason = getCloudFormationFailureReason;
async function clearS3Bucket(bucket, prefix) {
var _a;
const s3 = new client_s3_1.S3({ region });
let nextToken = undefined;
do {
const response = await s3.listObjectsV2({
Bucket: bucket,
Prefix: prefix,
ContinuationToken: nextToken,
});
if (!(response === null || response === void 0 ? void 0 : response.Contents) || response.Contents.length === 0)
return;
const objects = (_a = response.Contents) === null || _a === void 0 ? void 0 : _a.map((item) => ({ Key: item.Key }));
await s3.deleteObjects({ Bucket: bucket, Delete: { Objects: objects } });
nextToken = response.NextContinuationToken;
} while (nextToken);
}
exports.clearS3Bucket = clearS3Bucket;
async function syncLocalToS3(props) {
const { dir, bucket, cacheControl } = props;
const prune = props.prune || false;
const verbose = props.verbose || false;
const prefix = props.prefix || '';
if (prune) {
await clearS3Bucket(bucket, props.prefix);
}
const localPath = path.resolve(dir);
const s3 = new client_s3_1.S3({ useAccelerateEndpoint: true });
const cacheMustRevalidate = 'public, max-age=0, must-revalidate';
const cacheImmutable = 'public, max-age=31536000, immutable';
const mustRevalidate = (path) => {
// For now, we're handling Gatsby specific cache settings based on
// https://github.com/gatsbyjs/gatsby/blob/master/docs/docs/caching.md
const isHtml = path.endsWith('.html');
const isPageDataJson = /page-data\/.*\.json$/.test(path);
const isSwJs = path === 'sw.js';
return isHtml || isPageDataJson || isSwJs;
};
const files = await (0, file_1.walk)(localPath);
await Promise.all(files.map((file) => {
const content = fs.readFileSync(file);
const relativeToBaseFilePath = path.normalize(path.relative(localPath, file));
const relativeToBaseFilePathForS3 = relativeToBaseFilePath.split(path.sep).join('/');
const contentType = mime.getType(file) || undefined;
const isFont = contentType && contentType.includes('font');
if (verbose) {
console.log(`${file}: ${contentType}`);
}
const CacheControl = isFont
? cacheImmutable
: cacheControl ||
(mustRevalidate(relativeToBaseFilePathForS3) ? cacheMustRevalidate : cacheImmutable);
return s3.putObject({
Bucket: bucket,
Key: `${prefix}${relativeToBaseFilePathForS3}`,
Body: content,
ContentType: contentType,
CacheControl,
});
}));
}
exports.syncLocalToS3 = syncLocalToS3;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvcHJvdmlkZXJzL2F3cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsMkNBQTRCO0FBQzVCLDZDQUE4QjtBQUM5QiwrQ0FBZ0M7QUFFaEMsOERBQW1FO0FBQ25FLGtEQUFpRTtBQUNqRSxvREFBc0U7QUFDdEUsMEVBQTZEO0FBQzdELGtFQUltQztBQUNuQywyQ0FBNEI7QUFDNUIsMkNBQTRCO0FBRTVCLHFEQVF5QjtBQUN6QiwyQ0FBd0M7QUFDeEMsNkNBQThCO0FBQzlCLDJDQUFxQztBQUVyQyxNQUFNLE1BQU0sR0FBRyxXQUFXLENBQUE7QUFPMUI7Ozs7R0FJRztBQUNJLEtBQUssVUFBVSxhQUFhLENBQUMsTUFBYzs7SUFDaEQsSUFBSTtRQUNGLE1BQU0sT0FBTyxHQUFHLElBQUkseUJBQU8sQ0FBQyxFQUFDLE1BQU0sRUFBQyxDQUFDLENBQUE7UUFDckMsTUFBTSxRQUFRLEdBQUcsTUFBTSxPQUFPLENBQUMscUJBQXFCLENBQUMsRUFBQyxPQUFPLEVBQUUsTUFBTSxFQUFDLENBQUMsQ0FBQTtRQUN2RSxNQUFNLFVBQVUsR0FBRyxNQUFBLFFBQVEsQ0FBQyxXQUFXLDBDQUFFLElBQUksQ0FDM0MsQ0FBQyxJQUFJLEVBQUUsRUFBRSxXQUFDLE9BQUEsSUFBSSxDQUFDLElBQUksS0FBSyxHQUFHLE1BQU0sR0FBRyxJQUFJLENBQUEsTUFBQSxJQUFJLENBQUMsTUFBTSwwQ0FBRSxPQUFPLE1BQUssaUJBQWlCLENBQUEsRUFBQSxDQUNuRixDQUFBO1FBQ0QsSUFBSSxDQUFDLFVBQVUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxFQUFFO1lBQUUsT0FBTyxTQUFTLENBQUE7UUFFbkQsTUFBTSxFQUFFLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDLENBQUE7UUFDdkMsTUFBTSxJQUFJLEdB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LFdBQVcsSUFBSSxXQUFXLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFBO1FBRTFELElBQUksT0FBTyxFQUFFO1lBQ1gsT0FBTyxDQUFDLEdBQUcsQ0FBQyxHQUFHLElBQUksS0FBSyxXQUFXLEVBQUUsQ0FBQyxDQUFBO1NBQ3ZDO1FBRUQsTUFBTSxZQUFZLEdBQUcsTUFBTTtZQUN6QixDQUFDLENBQUMsY0FBYztZQUNoQixDQUFDLENBQUMsWUFBWTtnQkFDWixDQUFDLGNBQWMsQ0FBQywyQkFBMkIsQ0FBQyxDQUFDLENBQUMsQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDLENBQUMsY0FBYyxDQUFDLENBQUE7UUFFeEYsT0FBTyxFQUFFLENBQUMsU0FBUyxDQUFDO1lBQ2xCLE1BQU0sRUFBRSxNQUFNO1lBQ2QsR0FBRyxFQUFFLEdBQUcsTUFBTSxHQUFHLDJCQUEyQixFQUFFO1lBQzlDLElBQUksRUFBRSxPQUFPO1lBQ2IsV0FBVyxFQUFFLFdBQVc7WUFDeEIsWUFBWTtTQUNiLENBQUMsQ0FBQTtJQUNKLENBQUMsQ0FBQyxDQUNILENBQUE7QUFDSCxDQUFDO0FBckRELHNDQXFEQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCdcbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzLWV4dHJhJ1xuaW1wb3J0ICogYXMgY3J5cHRvIGZyb20gJ2NyeXB0bydcblxuaW1wb3J0IHtSb3V0ZTUzLCBSZXNvdXJjZVJlY29yZFNldH0gZnJvbSAnQGF3cy1zZGsvY2xpZW50LXJvdXRlLTUzJ1xuaW1wb3J0IHtTMywgTGlzdE9iamVjdHNWMkNvbW1hbmRPdXRwdXR9IGZyb20gJ0Bhd3Mtc2RrL2NsaWVudC1zMydcbmltcG9ydCB7QUNNLCBMaXN0Q2VydGlmaWNhdGVzQ29tbWFuZE91dHB1dH0gZnJvbSAnQGF3cy1zZGsvY2xpZW50LWFjbSdcbmltcG9ydCB7Q2xvdWRGb3JtYXRpb259IGZyb20gJ0Bhd3Mtc2RrL2NsaWVudC1jbG91ZGZvcm1hdGlvbidcbmltcG9ydCB7XG4gIENsb3VkRnJvbnQsXG4gIERpc3RyaWJ1dGlvblN1bW1hcnksXG4gIHdhaXRGb3JJbnZhbGlkYXRpb25Db21wbGV0ZWQsXG59IGZyb20gJ0Bhd3Mtc2RrL2NsaWVudC1jbG91ZGZyb250J1xuaW1wb3J0ICogYXMgdXVpZCBmcm9tICd1dWlkJ1xuaW1wb3J0ICogYXMgbWltZSBmcm9tICdtaW1lJ1xuXG5pbXBvcnQge1xuICBjaGFuZ2VTZXRIYXNOb0NoYW5nZXMsXG4gIENsb3VkRm9ybWF0aW9uU3RhY2ssXG4gIFRlbXBsYXRlUGFyYW1ldGVycyxcbiAgdG9ZQU1MLFxuICB3YWl0Rm9yQ2hhbmdlU2V0LFxuICB3YWl0Rm9yU3RhY2tEZWxldGUsXG4gIHdhaXRGb3JTdGFja0RlcGxveSxcbn0gZnJvbSAnLi9jbG91ZGZvcm1hdGlvbidcbmltcG9ydCB7Z2V0U3RhY2tJZH0gZnJvbSAnLi4vLi4vY29udGV4dCdcbmltcG9ydCAqIGFzIHlhbWwgZnJvbSAnLi95YW1sJ1xuaW1wb3J0IHt3YWxrfSBmcm9tICcuLi8uLi91dGlscy9maWxlJ1xuXG5jb25zdCByZWdpb24gPSAndXMtZWFzdC0xJ1xuXG5leHBvcnQgaW50ZXJmYWNlIEhvc3RlZFpvbmUge1xuICBpZDogc3RyaW5nXG4gIG5hbWU/OiBzdHJpbmdcbn1cblxuLyoqXG4gKiBHZXQgYW4gZXhpc3RpbmcgSG9zdGVkWm9uZSBmb3IgYSBnaXZlbiBkb21haW5cbiAqXG4gKiBAcGFyYW0gZG9tYWluIERvbWFpbiB0byBsb29rdXBcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdldEhvc3RlZFpvbmUoZG9tYWluOiBzdHJpbmcpOiBQcm9taXNlPEhvc3RlZFpvbmUgfCB1bmRlZmluZWQ+IHtcbiAgdHJ5IHtcbiAgICBjb25zdCByb3V0ZTUzID0gbmV3IFJvdXRlNTMoe3JlZ2lvbn0pXG4gICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCByb3V0ZTUzLmxpc3RIb3N0ZWRab25lc0J5TmFtZSh7RE5TTmFtZTogZG9tYWlufSlcbiAgICBjb25zdCBob3N0ZWRab25lID0gcmVzcG9uc2UuSG9zdGVkWm9uZXM/LmZpbmQoXG4gICAgICAoem9uZSkgPT4gem9uZS5OYW1lID09PSBgJHtkb21haW59LmAgJiYgem9uZS5Db25maWc/LkNvbW1lbnQgIT09ICdDcmVhdGVkIGJ5IE5lc3MnLFxuICAgIClcbiAgICBpZiAoIWhvc3RlZFpvbmUgfHwgIWhvc3RlZFpvbmUuSWQpIHJldHVybiB1bmRlZmluZWRcblxuICAgIGNvbnN0IGlkID0gcGF0aC5iYXNlbmFtZShob3N0ZWRab25lLklkKVxuICAgIGNvbnN0IG5hbWUgPSBob3N0ZWRab25lLk5hbWVcblxuICAgIHJldHVybiB7XG4gICAgICBpZCxcbiAgICAgIG5hbWUsXG4gICAgfVxuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkXG4gIH1cbn1cblxuLyoqXG4gKiBHZXQgcmVjb3JkIHNldHMgZnJvbSBhIFJvdXRlNTMgaG9zdGVkIHpvbmUuXG4gKlxuICogQHBhcmFtIGhvc3RlZFpvbmVJZCBIb3N0ZWRab25lSWQgb2YgdGhlIGhvc3RlZCB6b25lIHRoYXQgc2hvdWxkIGJlIGNsZWFuZWQgdXBcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdldEhvc3RlZFpvbmVSZWNvcmRTZXRzKFxuICBob3N0ZWRab25lSWQ6IHN0cmluZyxcbik6IFByb21pc2U8UmVzb3VyY2VSZWNvcmRTZXRbXSB8IHVuZGVmaW5lZD4ge1xuICB0cnkge1xuICAgIGNvbnN0IHJvdXRlNTMgPSBuZXcgUm91dGU1Myh7cmVnaW9ufSlcblxuICAgIGNvbnN0IHJlY29yZFNldHMgPSBhd2FpdCByb3V0ZTUzLmxpc3RSZXNvdXJjZVJlY29yZFNldHMoe0hvc3RlZFpvbmVJZDogaG9zdGVkWm9uZUlkfSlcbiAgICByZXR1cm4gcmVjb3JkU2V0cy5SZXNvdXJjZVJlY29yZFNldHNcbiAgfSBjYXRjaCB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZFxuICB9XG59XG5cbi8qKlxuICogR2V0IGFueSBleGlzdGluZyBBIHJlY29yZCBmb3IgYSBnaXZlbiBob3N0ZWQgem9uZS5cbiAqXG4gKiBAcGFyYW0gaG9zdGVkWm9uZUlkIEhvc3RlZFpvbmVJZCBvZiB0aGUgaG9zdGVkIHpvbmUgdGhhdCBzaG91bGQgYmUgY2xlYW5lZCB1cFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0SG9zdGVkWm9uZUFSZWNvcmQoXG4gIGhvc3RlZFpvbmVJZDogc3RyaW5nLFxuICBkb21haW46IHN0cmluZyB8IHVuZGVmaW5lZCxcbik6IFByb21pc2U8UmVzb3VyY2VSZWNvcmRTZXQgfCB1bmRlZmluZWQ+IHtcbiAgdHJ5IHtcbiAgICBjb25zdCByZWNvcmRTZXRzID0gYXdhaXQgZ2V0SG9zdGVkWm9uZVJlY29yZFNldHMoaG9zdGVkWm9uZUlkKVxuICAgIGlmICghcmVjb3JkU2V0cykgcmV0dXJuXG5cbiAgICBjb25zdCBhUmVjb3JkID0gcmVjb3JkU2V0cy5maW5kKChyZWNvcmQpID0+IHJlY29yZC5OYW1lID09PSBkb21haW4gJiYgcmVjb3JkLlR5cGUgPT09ICdBJylcbiAgICByZXR1cm4gYVJlY29yZFxuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkXG4gIH1cbn1cblxuLyoqXG4gKiBEZWxldGVzIHJlY29yZCBzZXRzIGZyb20gYSBob3N0ZWQgem9uZS5cbiAqXG4gKiBAcGFyYW0gaG9zdGVkWm9uZUlkIEhvc3RlZFpvbmVJZCBvZiB0aGUgaG9zdGVkIHpvbmUgdGhhdCBzaG91bGQgYmUgY2xlYW5lZCB1cFxuICogQHBhcmFtIHJlY29yZHMgUmVjb3JkIHNldHMgdG8gYmUgZGVsZXRlZFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZGVsZXRlSG9zdGVkWm9uZVJlY29yZHMoXG4gIGhvc3RlZFpvbmVJZDogc3RyaW5nLFxuICByZWNvcmRzOiBSZXNvdXJjZVJlY29yZFNldFtdLFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IHJvdXRlNTMgPSBuZXcgUm91dGU1Myh7cmVnaW9ufSlcblxuICBjb25zdCBjaGFuZ2VzID0gcmVjb3Jkcy5tYXAoKHJlY29yZCkgPT4gKHtcbiAgICBBY3Rpb246ICdERUxFVEUnLFxuICAgIFJlc291cmNlUmVjb3JkU2V0OiByZWNvcmQsXG4gIH0pKVxuXG4gIGF3YWl0IHJvdXRlNTMuY2hhbmdlUmVzb3VyY2VSZWNvcmRTZXRzKHtcbiAgICBIb3N0ZWRab25lSWQ6IGhvc3RlZFpvbmVJZCxcbiAgICBDaGFuZ2VCYXRjaDoge0NoYW5nZXM6IGNoYW5nZXN9LFxuICB9KVxufVxuXG4vKipcbiAqIENsZWFyIG91dCBhbnkgcmVjb3JkcyBpbiBhIEhvc3RlZCBab25lIHRoYXQgd2VyZSBjcmVhdGVkIGJ5IHZhbGlkYXRpbmdcbiAqIGEgRE5TIFZhbGlkYXRlZCBDZXJ0aWZpY2F0ZSB0aHJvdWdoIEFDTS5cbiAqXG4gKiBAcGFyYW0gaG9zdGVkWm9uZUlkIEhvc3RlZFpvbmVJZCBvZiB0aGUgaG9zdGVkIHpvbmUgdGhhdCBzaG91bGQgYmUgY2xlYW5lZCB1cFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xlYW51cEhvc3RlZFpvbmVSZWNvcmRzKGhvc3RlZFpvbmVJZDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IHJlY29yZFNldHMgPSBhd2FpdCBnZXRIb3N0ZWRab25lUmVjb3JkU2V0cyhob3N0ZWRab25lSWQpXG4gIGlmICghcmVjb3JkU2V0cykgcmV0dXJuXG5cbiAgY29uc3QgdGFyZ2V0cyA9IHJlY29yZFNldHMuZmlsdGVyKFxuICAgIChyZWNvcmQpID0+XG4gICAgICByZWNvcmQuVHlwZSA9PT0gJ0NOQU1FJyAmJlxuICAgICAgcmVjb3JkLlJlc291cmNlUmVjb3Jkcz8uZmluZCgodikgPT4gdi5WYWx1ZT8uZW5kc1dpdGgoJ2FjbS12YWxpZGF0aW9ucy5hd3MuJykpLFxuICApXG4gIGlmICghdGFyZ2V0cyB8fCB0YXJnZXRzLmxlbmd0aCA9PT0gMCkgcmV0dXJuXG5cbiAgYXdhaXQgZGVsZXRlSG9zdGVkWm9uZVJlY29yZHMoaG9zdGVkWm9uZUlkLCB0YXJnZXRzKVxufVxuXG4vKipcbiAqIEdldCBhbiBleGlzdGluZyBDbG91ZGZyb250IERpc3RyaWJ1dGlvbiBmb3IgYSBnaXZlbiBkb21haW5cbiAqXG4gKiBAcGFyYW0gZG9tYWluIERvbWFpbiB0byBsb29rdXBcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdldERpc3RyaWJ1dGlvbihkb21haW46IHN0cmluZyk6IFByb21pc2U8RGlzdHJpYnV0aW9uU3VtbWFyeSB8IHVuZGVmaW5lZD4ge1xuICB0cnkge1xuICAgIGNvbnN0IGNsb3VkZnJvbnQgPSBuZXcgQ2xvdWRGcm9udCh7cmVnaW9ufSlcbiAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGNsb3VkZnJvbnQubGlzdERpc3RyaWJ1dGlvbnMoe30pXG4gICAgY29uc3QgZGlzdHJpYnV0aW9uID0gcmVzcG9uc2UuRGlzdHJpYnV0aW9uTGlzdD8uSXRlbXM/LmZpbmQoXG4gICAgICAoZGlzdHJvKSA9PlxuICAgICAgICBkaXN0cm8uQWxpYXNlcz8uSXRlbXM/LmZpbmQoKGEpID0+IGEgPT09IGRvbWFpbikgJiYgZGlzdHJvLkNvbW1lbnQgIT09ICdDcmVhdGVkIGJ5IE5lc3MnLFxuICAgIClcbiAgICByZXR1cm4gZGlzdHJpYnV0aW9uXG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiB1bmRlZmluZWRcbiAgfVxufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaW52YWxpZGF0ZURpc3RyaWJ1dGlvbihcbiAgZGlzdHJpYnV0aW9uSWQ6IHN0cmluZyxcbiAgcGF0aHM6IHN0cmluZ1tdID0gWycvKiddLFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IGNsb3VkZnJvbnQgPSBuZXcgQ2xvdWRGcm9udCh7cmVnaW9ufSlcbiAgY29uc3QgaW52YWxpZGF0aW9uID0gYXdhaXQgY2xvdWRmcm9udC5jcmVhdGVJbnZhbGlkYXRpb24oe1xuICAgIERpc3RyaWJ1dGlvbklkOiBkaXN0cmlidXRpb25JZCxcbiAgICBJbnZhbGlkYXRpb25CYXRjaDoge1xuICAgICAgQ2FsbGVyUmVmZXJlbmNlOiBEYXRlLm5vdygpLnRvU3RyaW5nKCksXG4gICAgICBQYXRoczoge1F1YW50aXR5OiBwYXRocy5sZW5ndGgsIEl0ZW1zOiBwYXRoc30sXG4gICAgfSxcbiAgfSlcblxuICBhd2FpdCB3YWl0Rm9ySW52YWxpZGF0aW9uQ29tcGxldGVkKFxuICAgIHtjbGllbnQ6IGNsb3VkZnJvbnQsIG1heFdhaXRUaW1lOiA1ICogNjB9LFxuICAgIHtEaXN0cmlidXRpb25JZDogZGlzdHJpYnV0aW9uSWQsIElkOiBpbnZhbGlkYXRpb24uSW52YWxpZGF0aW9uPy5JZH0sXG4gIClcbn1cblxuLyoqXG4gKiBHZXQgYW4gZXhpc3RpbmcgY2VydGlmaWNhdGUgQVJOIGZvciBhIGdpdmVuIGRvbWFpblxuICpcbiAqIEBwYXJhbSBkb21haW4gRG9tYWluIHRvIGxvb2t1cFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0Q2VydGlmaWNhdGVBcm4oZG9tYWluOiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZyB8IHVuZGVmaW5lZD4ge1xuICB0cnkge1xuICAgIGNvbnN0IGFjbSA9IG5ldyBBQ00oe3JlZ2lvbn0pXG5cbiAgICBsZXQgbmV4dFRva2VuID0gdW5kZWZpbmVkXG4gICAgZG8ge1xuICAgICAgY29uc3QgY2VydGlmaWNhdGVzOiBMaXN0Q2VydGlmaWNhdGVzQ29tbWFuZE91dHB1dCA9IGF3YWl0IGFjbS5saXN0Q2VydGlmaWNhdGVzKHtcbiAgICAgICAgQ2VydGlmaWNhdGVTdGF0dXNlczogWydJU1NVRUQnXSxcbiAgICAgICAgTmV4dFRva2VuOiBuZXh0VG9rZW4sXG4gICAgICB9KVxuXG4gICAgICBjb25zdCBjZXJ0aWZpY2F0ZSA9IGNlcnRpZmljYXRlcz8uQ2VydGlmaWNhdGVTdW1tYXJ5TGlzdD8uZmluZChcbiAgICAgICAgKGNlcnQpID0+IGNlcnQuRG9tYWluTmFtZSA9PT0gZG9tYWluLFxuICAgICAgKVxuICAgICAgaWYgKGNlcnRpZmljYXRlKSByZXR1cm4gY2VydGlmaWNhdGUuQ2VydGlmaWNhdGVBcm5cblxuICAgICAgbmV4dFRva2VuID0gY2VydGlmaWNhdGVzPy5OZXh0VG9rZW5cbiAgICB9IHdoaWxlIChuZXh0VG9rZW4pXG5cbiAgICByZXR1cm4gdW5kZWZpbmVkXG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiB1bmRlZmluZWRcbiAgfVxufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0SG9zdGVkWm9uZU5hbWVzZXJ2ZXJzKFxuICBob3N0ZWRab25lSWQ6IHN0cmluZyxcbik6IFByb21pc2U8c3RyaW5nW10gfCB1bmRlZmluZWQ+IHtcbiAgdHJ5IHtcbiAgICBjb25zdCByb3V0ZTUzID0gbmV3IFJvdXRlNTMoe3JlZ2lvbn0pXG4gICAgY29uc3QgaG9zdGVkWm9uZSA9IGF3YWl0IHJvdXRlNTMuZ2V0SG9zdGVkWm9uZSh7SWQ6IGhvc3RlZFpvbmVJZH0pXG4gICAgaWYgKCFob3N0ZWRab25lKSByZXR1cm4gdW5kZWZpbmVkXG5cbiAgICByZXR1cm4gaG9zdGVkWm9uZS5EZWxlZ2F0aW9uU2V0Py5OYW1lU2VydmVyc1xuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkXG4gIH1cbn1cblxuZXhwb3J0IHR5cGUgTmVzc1N0YWNrID0gJ2RvbWFpbicgfCAnd2ViJyB8ICdhbGlhcycgfCAnc3VwcG9ydCdcblxuZXhwb3J0IGludGVyZmFjZSBTdGFjayB7XG4gIC8qKlxuICAgKiBUaGUgcGh5c2ljYWwgbmFtZSBvZiB0aGlzIHN0YWNrLlxuICAgKi9cbiAgc3RhY2tOYW1lOiBzdHJpbmdcblxuICAvKipcbiAgICogQ2xvdWRGb3JtYXRpb24gcGFyYW1ldGVycyB0byBwYXNzIHRvIHRoZSBzdGFjay5cbiAgICovXG4gIHBhcmFtZXRlcnM6IHtbaWQ6IHN0cmluZ106IHN0cmluZyB8IHVuZGVmaW5lZH1cblxuICAvKipcbiAgICogVGhlIHN0YWNrIHRlbXBsYXRlLlxuICAgKi9cbiAgdGVtcGxhdGU6IGFueVxufVxuXG5pbnRlcmZhY2UgQ2xvdWRGb3JtYXRpb25SZXNvdXJjZSB7XG4gIFR5cGU6IHN0cmluZ1xufVxuXG5pbnRlcmZhY2UgQ2xvdWRGb3JtYXRpb25MYW1iZGFGdW5jdGlvbiBleHRlbmRzIENsb3VkRm9ybWF0aW9uUmVzb3VyY2Uge1xuICBQcm9wZXJ0aWVzOiB7XG4gICAgQ29kZToge1xuICAgICAgUzNLZXk/OiB7XG4gICAgICAgIFJlZjogc3RyaW5nXG4gICAgICB9XG4gICAgICBaaXBGaWxlPzoge1xuICAgICAgICAnRm46OlN1Yic6IHN0cmluZ1xuICAgICAgfVxuICAgIH1cbiAgfVxufVxuXG5pbnRlcmZhY2UgQ2xvdWRGb3JtYXRpb25MYW1iZGFGdW5jdGlvblZlcnNpb24gZXh0ZW5kcyBDbG91ZEZvcm1hdGlvblJlc291cmNlIHtcbiAgUHJvcGVydGllczoge1xuICAgIEZ1bmN0aW9uTmFtZToge1xuICAgICAgUmVmOiBzdHJpbmdcbiAgICB9XG4gIH1cbn1cblxuaW50ZXJmYWNlIENsb3VkRm9ybWF0aW9uVGVtcGxhdGUge1xuICBSZXNvdXJjZXM6IFJlY29yZDxzdHJpbmcsIENsb3VkRm9ybWF0aW9uUmVzb3VyY2U+XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRTdGFjayhcbiAgc3RhY2s6IE5lc3NTdGFjayxcbiAgcGFyYW1ldGVyczoge1tpZDogc3RyaW5nXTogc3RyaW5nIHwgdW5kZWZpbmVkfSxcbik6IFByb21pc2U8U3RhY2s+IHtcbiAgY29uc3Qgc3RhY2tOYW1lID0gZ2V0U3RhY2tJZChzdGFjaylcbiAgY29uc3QgY29udGVudHMgPSBhd2FpdCBmcy5yZWFkRmlsZShcbiAgICBwYXRoLnJlc29sdmUoX19kaXJuYW1lLCBgLi4vLi4vLi4vc3RhdGljL3N0YWNrcy8ke3N0YWNrfS55YW1sYCksXG4gICAgJ3V0Zi04JyxcbiAgKVxuXG4gIGNvbnN0IHRlbXBsYXRlOiBDbG91ZEZvcm1hdGlvblRlbXBsYXRlID0geWFtbC5kZXNlcmlhbGl6ZShjb250ZW50cylcblxuICBjb25zdCBnZXRDb2RlRnJvbUxhbWJkYSA9IChyZXNvdXJjZTogQ2xvdWRGb3JtYXRpb25SZXNvdXJjZSk6IHN0cmluZyB8IHVuZGVmaW5lZCA9PiB7XG4gICAgY29uc3QgbGFtYmRhID0gcmVzb3VyY2UgYXMgQ2xvdWRGb3JtYXRpb25MYW1iZGFGdW5jdGlvblxuICAgIGlmICghbGFtYmRhKSByZXR1cm4gdW5kZWZpbmVkXG5cbiAgICByZXR1cm4gbGFtYmRhLlByb3BlcnRpZXMuQ29kZS5aaXBGaWxlID8gbGFtYmRhLlByb3BlcnRpZXMuQ29kZS5aaXBGaWxlWydGbjo6U3ViJ10gOiB1bmRlZmluZWRcbiAgfVxuXG4gIGNvbnN0IGdldFMzS2V5RnJvbUxhbWJkYSA9IChyZXNvdXJjZTogQ2xvdWRGb3JtYXRpb25SZXNvdXJjZSk6IHN0cmluZyB8IHVuZGVmaW5lZCA9PiB7XG4gICAgY29uc3QgbGFtYmRhID0gcmVzb3VyY2UgYXMgQ2xvdWRGb3JtYXRpb25MYW1iZGFGdW5jdGlvblxuICAgIGlmICghbGFtYmRhKSByZXR1cm4gdW5kZWZpbmVkXG5cbiAgICByZXR1cm4gbGFtYmRhLlByb3BlcnRpZXMuQ29kZS5TM0tleT8uUmVmXG4gIH1cblxuICBjb25zdCByZXNvdXJjZXMgPSB0ZW1wbGF0ZS5SZXNvdXJjZXNcbiAgY29uc3QgZnVuY3Rpb25zID0gT2JqZWN0LmtleXMocmVzb3VyY2VzKVxuICAgIC5maWx0ZXIoKHJlc291cmNlKSA9PiByZXNvdXJjZXNbcmVzb3VyY2VdLlR5cGUgPT09ICdBV1M6OkxhbWJkYTo6RnVuY3Rpb24nKVxuICAgIC5tYXAoKHJlc291cmNlKSA9PiAoe1xuICAgICAga2V5OiByZXNvdXJjZSxcbiAgICAgIGNvZGU6IGdldENvZGVGcm9tTGFtYmRhKHJlc291cmNlc1tyZXNvdXJjZV0pLFxuICAgICAgczNrZXk6IGdldFMzS2V5RnJvbUxhbWJkYShyZXNvdXJjZXNbcmVzb3VyY2VdKSxcbiAgICB9KSlcbiAgICAuZmlsdGVyKCh7Y29kZSwgczNrZXl9KSA9PiBjb2RlIHx8IHMza2V5KVxuXG4gIGNvbnN0IHZlcnNpb25zID0gT2JqZWN0LmtleXMocmVzb3VyY2VzKVxuICAgIC5maWx0ZXIoKHJlc291cmNlKSA9PiByZXNvdXJjZXNbcmVzb3VyY2VdLlR5cGUgPT09ICdBV1M6OkxhbWJkYTo6VmVyc2lvbicpXG4gICAgLm1hcCgocmVzb3VyY2UpID0+ICh7XG4gICAgICBrZXk6IHJlc291cmNlLFxuICAgICAgZnVuY3Rpb25OYW1lOiAocmVzb3VyY2VzW3Jlc291cmNlXSBhcyBDbG91ZEZvcm1hdGlvbkxhbWJkYUZ1bmN0aW9uVmVyc2lvbikuUHJvcGVydGllc1xuICAgICAgICAuRnVuY3Rpb25OYW1lLlJlZixcbiAgICB9KSlcblxuICBsZXQgdXBkYXRlZENvbnRlbnRzID0gY29udGVudHNcblxuICBmb3IgKGNvbnN0IGxhbWJkYUZ1bmN0aW9uIG9mIGZ1bmN0aW9ucykge1xuICAgIGNvbnN0IHtrZXksIGNvZGUsIHMza2V5fSA9IGxhbWJkYUZ1bmN0aW9uXG5cbiAgICBsZXQgaGFzaDogc3RyaW5nID0gJydcblxuICAgIGlmIChjb2RlKSB7XG4gICAgICBsZXQgY29kZVdpdGhSZXBsYWNlbWVudHMgPSBjb2RlXG4gICAgICBmb3IgKGNvbnN0IHBhcmFtIG9mIE9iamVjdC5rZXlzKHBhcmFtZXRlcnMpKSB7XG4gICAgICAgIGNvbnN0IHZhbHVlID0gcGFyYW1ldGVyc1twYXJhbV1cbiAgICAgICAgaWYgKHZhbHVlID09PSB1bmRlZmluZWQpIGNvbnRpbnVlXG5cbiAgICAgICAgY29kZVdpdGhSZXBsYWNlbWVudHMgPSBjb2RlV2l0aFJlcGxhY2VtZW50cy5yZXBsYWNlKCckeycgKyBwYXJhbSArICd9JywgdmFsdWUpXG4gICAgICB9XG5cbiAgICAgIGhhc2ggPSBjcnlwdG8uY3JlYXRlSGFzaCgnc2hhMjU2JykudXBkYXRlKGNvZGVXaXRoUmVwbGFjZW1lbnRzKS5kaWdlc3QoJ2hleCcpXG4gICAgfSBlbHNlIHtcbiAgICAgIGlmICghczNrZXkgfHwgIXBhcmFtZXRlcnNbczNrZXldKSBjb250aW51ZVxuXG4gICAgICBjb25zdCBrZXkgPSBwYXJhbWV0ZXJzW3Mza2V5XSBhcyBzdHJpbmdcbiAgICAgIC8vIGRlZmF1bHQtbGFtYmRhLjxoYXNoPi56aXBcbiAgICAgIGhhc2ggPSBrZXkuc3BsaXQoJy4nKVsxXVxuICAgIH1cblxuICAgIGNvbnN0IHZlcnNpb24gPSB2ZXJzaW9ucy5maW5kKCh2ZXJzaW9uKSA9PiB2ZXJzaW9uLmZ1bmN0aW9uTmFtZSA9PT0ga2V5KVxuICAgIGlmICghdmVyc2lvbikgY29udGludWVcblxuICAgIGNvbnN0IHJlZ2V4ID0gbmV3IFJlZ0V4cCh2ZXJzaW9uLmtleSwgJ2dpJylcbiAgICB1cGRhdGVkQ29udGVudHMgPSB1cGRhdGVkQ29udGVudHMucmVwbGFjZShyZWdleCwgaGFzaClcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgc3RhY2tOYW1lLFxuICAgIHBhcmFtZXRlcnMsXG4gICAgdGVtcGxhdGU6IHlhbWwuZGVzZXJpYWxpemUodXBkYXRlZENvbnRlbnRzKSxcbiAgfVxufVxuXG5leHBvcnQgaW50ZXJmYWNlIERlcGxveVN0YWNrT3B0aW9ucyB7XG4gIC8qKlxuICAgKiBUaGUgc3RhY2sgdG8gYmUgZGVwbG95ZWRcbiAgICovXG4gIHN0YWNrOiBTdGFja1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZGVwbG95U3RhY2sob3B0aW9uczogRGVwbG95U3RhY2tPcHRpb25zKTogUHJvbWlzZTx7W25hbWU6IHN0cmluZ106IHN0cmluZ30+IHtcbiAgY29uc3Qge3N0YWNrfSA9IG9wdGlvbnNcbiAgY29uc3Qge3N0YWNrTmFtZSwgcGFyYW1ldGVycywgdGVtcGxhdGV9ID0gc3RhY2tcblxuICBjb25zdCBjZm4gPSBuZXcgQ2xvdWRGb3JtYXRpb24oe3JlZ2lvbn0pXG4gIGxldCBjbG91ZEZvcm1hdGlvblN0YWNrID0gYXdhaXQgQ2xvdWRGb3JtYXRpb25TdGFjay5sb29rdXAoY2ZuLCBzdGFja05hbWUpXG5cbiAgaWYgKGNsb3VkRm9ybWF0aW9uU3RhY2suc3RhY2tTdGF0dXMuaXNDcmVhdGlvbkZhaWx1cmUpIHtcbiAgICBhd2FpdCBjZm4uZGVsZXRlU3RhY2soe1N0YWNrTmFtZTogc3RhY2tOYW1lfSlcbiAgICBjb25zdCBkZWxldGVkU3RhY2sgPSBhd2FpdCB3YWl0Rm9yU3RhY2tEZWxldGU