UNPKG

ness

Version:

✪ No-effort static sites deployed to your AWS account.

424 lines 58.4 kB
"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