UNPKG

aws-cdk

Version:

CDK Toolkit, the command line tool for CDK apps

480 lines 86.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.destroyStack = exports.makeBodyParameterAndUpload = exports.deployStack = void 0; const cxapi = require("@aws-cdk/cx-api"); const chalk = require("chalk"); const fs = require("fs-extra"); const uuid = require("uuid"); const evaluate_cloudformation_template_1 = require("./evaluate-cloudformation-template"); const common_1 = require("./hotswap/common"); const hotswap_deployments_1 = require("./hotswap-deployments"); const cloudformation_1 = require("./util/cloudformation"); const stack_activity_monitor_1 = require("./util/cloudformation/stack-activity-monitor"); const assets_1 = require("../assets"); const logging_1 = require("../logging"); const serialize_1 = require("../serialize"); const asset_manifest_builder_1 = require("../util/asset-manifest-builder"); const asset_publishing_1 = require("../util/asset-publishing"); const content_hash_1 = require("../util/content-hash"); const LARGE_TEMPLATE_SIZE_KB = 50; async function deployStack(options) { const stackArtifact = options.stack; const stackEnv = options.resolvedEnvironment; options.sdk.appendCustomUserAgent(options.extraUserAgent); const cfn = options.sdk.cloudFormation(); const deployName = options.deployName || stackArtifact.stackName; let cloudFormationStack = await cloudformation_1.CloudFormationStack.lookup(cfn, deployName); if (cloudFormationStack.stackStatus.isCreationFailure) { (0, logging_1.debug)(`Found existing stack ${deployName} that had previously failed creation. Deleting it before attempting to re-create it.`); await cfn.deleteStack({ StackName: deployName }).promise(); const deletedStack = await (0, cloudformation_1.waitForStackDelete)(cfn, deployName); if (deletedStack && deletedStack.stackStatus.name !== 'DELETE_COMPLETE') { throw new Error(`Failed deleting stack ${deployName} 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, deployName); } // Detect "legacy" assets (which remain in the metadata) and publish them via // an ad-hoc asset manifest, while passing their locations via template // parameters. const legacyAssets = new asset_manifest_builder_1.AssetManifestBuilder(); const assetParams = await (0, assets_1.addMetadataAssetsToManifest)(stackArtifact, legacyAssets, options.envResources, options.reuseAssets); const finalParameterValues = { ...options.parameters, ...assetParams }; const templateParams = cloudformation_1.TemplateParameters.fromTemplate(stackArtifact.template); const stackParams = options.usePreviousParameters ? templateParams.updateExisting(finalParameterValues, cloudFormationStack.parameters) : templateParams.supplyAll(finalParameterValues); if (await canSkipDeploy(options, cloudFormationStack, stackParams.hasChanges(cloudFormationStack.parameters))) { (0, logging_1.debug)(`${deployName}: skipping deployment (use --force to override)`); // if we can skip deployment and we are performing a hotswap, let the user know // that no hotswap deployment happened if (options.hotswap !== common_1.HotswapMode.FULL_DEPLOYMENT) { (0, logging_1.print)(`\n ${common_1.ICON} %s\n`, chalk.bold('hotswap deployment skipped - no changes were detected (use --force to override)')); } return { noOp: true, outputs: cloudFormationStack.outputs, stackArn: cloudFormationStack.stackId, }; } else { (0, logging_1.debug)(`${deployName}: deploying...`); } const bodyParameter = await makeBodyParameter(stackArtifact, options.resolvedEnvironment, legacyAssets, options.envResources, options.sdk, options.overrideTemplate); await (0, asset_publishing_1.publishAssets)(legacyAssets.toManifest(stackArtifact.assembly.directory), options.sdkProvider, stackEnv, { parallel: options.assetParallelism, }); const hotswapMode = options.hotswap; if (hotswapMode && hotswapMode !== common_1.HotswapMode.FULL_DEPLOYMENT) { // attempt to short-circuit the deployment if possible try { const hotswapDeploymentResult = await (0, hotswap_deployments_1.tryHotswapDeployment)(options.sdkProvider, stackParams.values, cloudFormationStack, stackArtifact, hotswapMode); if (hotswapDeploymentResult) { return hotswapDeploymentResult; } (0, logging_1.print)('Could not perform a hotswap deployment, as the stack %s contains non-Asset changes', stackArtifact.displayName); } catch (e) { if (!(e instanceof evaluate_cloudformation_template_1.CfnEvaluationException)) { throw e; } (0, logging_1.print)('Could not perform a hotswap deployment, because the CloudFormation template could not be resolved: %s', e.message); } if (hotswapMode === common_1.HotswapMode.FALL_BACK) { (0, logging_1.print)('Falling back to doing a full deployment'); options.sdk.appendCustomUserAgent('cdk-hotswap/fallback'); } else { return { noOp: true, stackArn: cloudFormationStack.stackId, outputs: cloudFormationStack.outputs }; } } // could not short-circuit the deployment, perform a full CFN deploy instead const fullDeployment = new FullCloudFormationDeployment(options, cloudFormationStack, stackArtifact, stackParams, bodyParameter); return fullDeployment.performDeployment(); } exports.deployStack = deployStack; /** * This class shares state and functionality between the different full deployment modes */ class FullCloudFormationDeployment { constructor(options, cloudFormationStack, stackArtifact, stackParams, bodyParameter) { this.options = options; this.cloudFormationStack = cloudFormationStack; this.stackArtifact = stackArtifact; this.stackParams = stackParams; this.bodyParameter = bodyParameter; this.cfn = options.sdk.cloudFormation(); this.stackName = options.deployName ?? stackArtifact.stackName; this.update = cloudFormationStack.exists && cloudFormationStack.stackStatus.name !== 'REVIEW_IN_PROGRESS'; this.verb = this.update ? 'update' : 'create'; this.uuid = uuid.v4(); } async performDeployment() { const deploymentMethod = this.options.deploymentMethod ?? { method: 'change-set' }; if (deploymentMethod.method === 'direct' && this.options.resourcesToImport) { throw new Error('Importing resources requires a changeset deployment'); } switch (deploymentMethod.method) { case 'change-set': return this.changeSetDeployment(deploymentMethod); case 'direct': return this.directDeployment(); } } async changeSetDeployment(deploymentMethod) { const changeSetName = deploymentMethod.changeSetName ?? 'cdk-deploy-change-set'; const execute = deploymentMethod.execute ?? true; const changeSetDescription = await this.createChangeSet(changeSetName, execute); await this.updateTerminationProtection(); if ((0, cloudformation_1.changeSetHasNoChanges)(changeSetDescription)) { (0, logging_1.debug)('No changes are to be performed on %s.', this.stackName); if (execute) { (0, logging_1.debug)('Deleting empty change set %s', changeSetDescription.ChangeSetId); await this.cfn.deleteChangeSet({ StackName: this.stackName, ChangeSetName: changeSetName }).promise(); } return { noOp: true, outputs: this.cloudFormationStack.outputs, stackArn: changeSetDescription.StackId }; } if (!execute) { (0, logging_1.print)('Changeset %s created and waiting in review for manual execution (--no-execute)', changeSetDescription.ChangeSetId); return { noOp: false, outputs: this.cloudFormationStack.outputs, stackArn: changeSetDescription.StackId }; } return this.executeChangeSet(changeSetDescription); } async createChangeSet(changeSetName, willExecute) { await this.cleanupOldChangeset(changeSetName); (0, logging_1.debug)(`Attempting to create ChangeSet with name ${changeSetName} to ${this.verb} stack ${this.stackName}`); (0, logging_1.print)('%s: creating CloudFormation changeset...', chalk.bold(this.stackName)); const changeSet = await this.cfn.createChangeSet({ StackName: this.stackName, ChangeSetName: changeSetName, ChangeSetType: this.options.resourcesToImport ? 'IMPORT' : this.update ? 'UPDATE' : 'CREATE', ResourcesToImport: this.options.resourcesToImport, Description: `CDK Changeset for execution ${this.uuid}`, ClientToken: `create${this.uuid}`, ...this.commonPrepareOptions(), }).promise(); (0, logging_1.debug)('Initiated creation of changeset: %s; waiting for it to finish creating...', changeSet.Id); // Fetching all pages if we'll execute, so we can have the correct change count when monitoring. return (0, cloudformation_1.waitForChangeSet)(this.cfn, this.stackName, changeSetName, { fetchAll: willExecute }); } async executeChangeSet(changeSet) { (0, logging_1.debug)('Initiating execution of changeset %s on stack %s', changeSet.ChangeSetId, this.stackName); await this.cfn.executeChangeSet({ StackName: this.stackName, ChangeSetName: changeSet.ChangeSetName, ClientRequestToken: `exec${this.uuid}`, ...this.commonExecuteOptions(), }).promise(); (0, logging_1.debug)('Execution of changeset %s on stack %s has started; waiting for the update to complete...', changeSet.ChangeSetId, this.stackName); // +1 for the extra event emitted from updates. const changeSetLength = (changeSet.Changes ?? []).length + (this.update ? 1 : 0); return this.monitorDeployment(changeSet.CreationTime, changeSetLength); } async cleanupOldChangeset(changeSetName) { if (this.cloudFormationStack.exists) { // Delete any existing change sets generated by CDK since change set names must be unique. // The delete request is successful as long as the stack exists (even if the change set does not exist). (0, logging_1.debug)(`Removing existing change set with name ${changeSetName} if it exists`); await this.cfn.deleteChangeSet({ StackName: this.stackName, ChangeSetName: changeSetName }).promise(); } } async updateTerminationProtection() { // Update termination protection only if it has changed. const terminationProtection = this.stackArtifact.terminationProtection ?? false; if (!!this.cloudFormationStack.terminationProtection !== terminationProtection) { (0, logging_1.debug)('Updating termination protection from %s to %s for stack %s', this.cloudFormationStack.terminationProtection, terminationProtection, this.stackName); await this.cfn.updateTerminationProtection({ StackName: this.stackName, EnableTerminationProtection: terminationProtection, }).promise(); (0, logging_1.debug)('Termination protection updated to %s for stack %s', terminationProtection, this.stackName); } } async directDeployment() { (0, logging_1.print)('%s: %s stack...', chalk.bold(this.stackName), this.update ? 'updating' : 'creating'); const startTime = new Date(); if (this.update) { await this.cfn.updateStack({ StackName: this.stackName, ClientRequestToken: `update${this.uuid}`, ...this.commonPrepareOptions(), ...this.commonExecuteOptions(), }).promise(); const ret = await this.monitorDeployment(startTime, undefined); await this.updateTerminationProtection(); return ret; } else { // Take advantage of the fact that we can set termination protection during create const terminationProtection = this.stackArtifact.terminationProtection ?? false; await this.cfn.createStack({ StackName: this.stackName, ClientRequestToken: `create${this.uuid}`, ...terminationProtection ? { EnableTerminationProtection: true } : undefined, ...this.commonPrepareOptions(), ...this.commonExecuteOptions(), }).promise(); return this.monitorDeployment(startTime, undefined); } } async monitorDeployment(startTime, expectedChanges) { const monitor = this.options.quiet ? undefined : stack_activity_monitor_1.StackActivityMonitor.withDefaultPrinter(this.cfn, this.stackName, this.stackArtifact, { resourcesTotal: expectedChanges, progress: this.options.progress, changeSetCreationTime: startTime, ci: this.options.ci, }).start(); let finalState = this.cloudFormationStack; try { const successStack = await (0, cloudformation_1.waitForStackDeploy)(this.cfn, this.stackName); // This shouldn't really happen, but catch it anyway. You never know. if (!successStack) { throw new Error('Stack deploy failed (the stack disappeared while we were deploying it)'); } finalState = successStack; } catch (e) { throw new Error(suffixWithErrors(e.message, monitor?.errors)); } finally { await monitor?.stop(); } (0, logging_1.debug)('Stack %s has completed updating', this.stackName); return { noOp: false, outputs: finalState.outputs, stackArn: finalState.stackId }; } /** * Return the options that are shared between CreateStack, UpdateStack and CreateChangeSet */ commonPrepareOptions() { return { Capabilities: ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND'], NotificationARNs: this.options.notificationArns, Parameters: this.stackParams.apiParameters, RoleARN: this.options.roleArn, TemplateBody: this.bodyParameter.TemplateBody, TemplateURL: this.bodyParameter.TemplateURL, Tags: this.options.tags, }; } /** * Return the options that are shared between UpdateStack and CreateChangeSet * * Be careful not to add in keys for options that aren't used, as the features may not have been * deployed everywhere yet. */ commonExecuteOptions() { const shouldDisableRollback = this.options.rollback === false; return { StackName: this.stackName, ...shouldDisableRollback ? { DisableRollback: true } : undefined, }; } } /** * Prepares the body parameter for +CreateChangeSet+. * * If the template is small enough to be inlined into the API call, just return * it immediately. * * Otherwise, add it to the asset manifest to get uploaded to the staging * bucket and return its coordinates. If there is no staging bucket, an error * is thrown. * * @param stack the synthesized stack that provides the CloudFormation template * @param toolkitInfo information about the toolkit stack */ async function makeBodyParameter(stack, resolvedEnvironment, assetManifest, resources, sdk, overrideTemplate) { // If the template has already been uploaded to S3, just use it from there. if (stack.stackTemplateAssetObjectUrl && !overrideTemplate) { return { TemplateURL: restUrlFromManifest(stack.stackTemplateAssetObjectUrl, resolvedEnvironment, sdk) }; } // Otherwise, pass via API call (if small) or upload here (if large) const templateJson = (0, serialize_1.toYAML)(overrideTemplate ?? stack.template); if (templateJson.length <= LARGE_TEMPLATE_SIZE_KB * 1024) { return { TemplateBody: templateJson }; } const toolkitInfo = await resources.lookupToolkit(); if (!toolkitInfo.found) { (0, logging_1.error)(`The template for stack "${stack.displayName}" is ${Math.round(templateJson.length / 1024)}KiB. ` + `Templates larger than ${LARGE_TEMPLATE_SIZE_KB}KiB must be uploaded to S3.\n` + 'Run the following command in order to setup an S3 bucket in this environment, and then re-deploy:\n\n', chalk.blue(`\t$ cdk bootstrap ${resolvedEnvironment.name}\n`)); throw new Error('Template too large to deploy ("cdk bootstrap" is required)'); } const templateHash = (0, content_hash_1.contentHash)(templateJson); const key = `cdk/${stack.id}/${templateHash}.yml`; let templateFile = stack.templateFile; if (overrideTemplate) { // Add a variant of this template templateFile = `${stack.templateFile}-${templateHash}.yaml`; await fs.writeFile(templateFile, templateJson, { encoding: 'utf-8' }); } assetManifest.addFileAsset(templateHash, { path: templateFile, }, { bucketName: toolkitInfo.bucketName, objectKey: key, }); const templateURL = `${toolkitInfo.bucketUrl}/${key}`; (0, logging_1.debug)('Storing template in S3 at:', templateURL); return { TemplateURL: templateURL }; } /** * Prepare a body parameter for CFN, performing the upload * * Return it as-is if it is small enough to pass in the API call, * upload to S3 and return the coordinates if it is not. */ async function makeBodyParameterAndUpload(stack, resolvedEnvironment, resources, sdkProvider, sdk, overrideTemplate) { // We don't have access to the actual asset manifest here, so pretend that the // stack doesn't have a pre-published URL. const forceUploadStack = Object.create(stack, { stackTemplateAssetObjectUrl: { value: undefined }, }); const builder = new asset_manifest_builder_1.AssetManifestBuilder(); const bodyparam = await makeBodyParameter(forceUploadStack, resolvedEnvironment, builder, resources, sdk, overrideTemplate); const manifest = builder.toManifest(stack.assembly.directory); await (0, asset_publishing_1.publishAssets)(manifest, sdkProvider, resolvedEnvironment, { quiet: true }); return bodyparam; } exports.makeBodyParameterAndUpload = makeBodyParameterAndUpload; async function destroyStack(options) { const deployName = options.deployName || options.stack.stackName; const cfn = options.sdk.cloudFormation(); const currentStack = await cloudformation_1.CloudFormationStack.lookup(cfn, deployName); if (!currentStack.exists) { return; } const monitor = options.quiet ? undefined : stack_activity_monitor_1.StackActivityMonitor.withDefaultPrinter(cfn, deployName, options.stack, { ci: options.ci, }).start(); try { await cfn.deleteStack({ StackName: deployName, RoleARN: options.roleArn }).promise(); const destroyedStack = await (0, cloudformation_1.waitForStackDelete)(cfn, deployName); if (destroyedStack && destroyedStack.stackStatus.name !== 'DELETE_COMPLETE') { throw new Error(`Failed to destroy ${deployName}: ${destroyedStack.stackStatus}`); } } catch (e) { throw new Error(suffixWithErrors(e.message, monitor?.errors)); } finally { if (monitor) { await monitor.stop(); } } } exports.destroyStack = destroyStack; /** * Checks whether we can skip deployment * * We do this in a complicated way by preprocessing (instead of just * looking at the changeset), because if there are nested stacks involved * the changeset will always show the nested stacks as needing to be * updated, and the deployment will take a long time to in effect not * do anything. */ async function canSkipDeploy(deployStackOptions, cloudFormationStack, parameterChanges) { const deployName = deployStackOptions.deployName || deployStackOptions.stack.stackName; (0, logging_1.debug)(`${deployName}: checking if we can skip deploy`); // Forced deploy if (deployStackOptions.force) { (0, logging_1.debug)(`${deployName}: forced deployment`); return false; } // Creating changeset only (default true), never skip if (deployStackOptions.deploymentMethod?.method === 'change-set' && deployStackOptions.deploymentMethod.execute === false) { (0, logging_1.debug)(`${deployName}: --no-execute, always creating change set`); return false; } // No existing stack if (!cloudFormationStack.exists) { (0, logging_1.debug)(`${deployName}: no existing stack`); return false; } // Template has changed (assets taken into account here) if (JSON.stringify(deployStackOptions.stack.template) !== JSON.stringify(await cloudFormationStack.template())) { (0, logging_1.debug)(`${deployName}: template has changed`); return false; } // Tags have changed if (!compareTags(cloudFormationStack.tags, deployStackOptions.tags ?? [])) { (0, logging_1.debug)(`${deployName}: tags have changed`); return false; } // Termination protection has been updated if (!!deployStackOptions.stack.terminationProtection !== !!cloudFormationStack.terminationProtection) { (0, logging_1.debug)(`${deployName}: termination protection has been updated`); return false; } // Parameters have changed if (parameterChanges) { if (parameterChanges === 'ssm') { (0, logging_1.debug)(`${deployName}: some parameters come from SSM so we have to assume they may have changed`); } else { (0, logging_1.debug)(`${deployName}: parameters have changed`); } return false; } // Existing stack is in a failed state if (cloudFormationStack.stackStatus.isFailure) { (0, logging_1.debug)(`${deployName}: stack is in a failure state`); return false; } // We can skip deploy return true; } /** * Compares two list of tags, returns true if identical. */ function compareTags(a, b) { if (a.length !== b.length) { return false; } for (const aTag of a) { const bTag = b.find(tag => tag.Key === aTag.Key); if (!bTag || bTag.Value !== aTag.Value) { return false; } } return true; } /** * Format an S3 URL in the manifest for use with CloudFormation * * Replaces environment placeholders (which this field may contain), * and reformats s3://.../... urls into S3 REST URLs (which CloudFormation * expects) */ function restUrlFromManifest(url, environment, sdk) { const doNotUseMarker = '**DONOTUSE**'; // This URL may contain placeholders, so still substitute those. url = cxapi.EnvironmentPlaceholders.replace(url, { accountId: environment.account, region: environment.region, partition: doNotUseMarker, }); // Yes, this is extremely crude, but we don't actually need this so I'm not inclined to spend // a lot of effort trying to thread the right value to this location. if (url.indexOf(doNotUseMarker) > -1) { throw new Error('Cannot use \'${AWS::Partition}\' in the \'stackTemplateAssetObjectUrl\' field'); } const s3Url = url.match(/s3:\/\/([^/]+)\/(.*)$/); if (!s3Url) { return url; } // We need to pass an 'https://s3.REGION.amazonaws.com[.cn]/bucket/object' URL to CloudFormation, but we // got an 's3://bucket/object' URL instead. Construct the rest API URL here. const bucketName = s3Url[1]; const objectKey = s3Url[2]; const urlSuffix = sdk.getEndpointSuffix(environment.region); return `https://s3.${environment.region}.${urlSuffix}/${bucketName}/${objectKey}`; } function suffixWithErrors(msg, errors) { return errors && errors.length > 0 ? `${msg}: ${errors.join(', ')}` : msg; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwbG95LXN0YWNrLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZGVwbG95LXN0YWNrLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHlDQUF5QztBQUV6QywrQkFBK0I7QUFDL0IsK0JBQStCO0FBQy9CLDZCQUE2QjtBQUc3Qix5RkFBNEU7QUFDNUUsNkNBQXFEO0FBQ3JELCtEQUE2RDtBQUM3RCwwREFHK0I7QUFDL0IseUZBQTJHO0FBQzNHLHNDQUF3RDtBQUV4RCx3Q0FBaUQ7QUFDakQsNENBQXNDO0FBQ3RDLDJFQUFzRTtBQUN0RSwrREFBeUQ7QUFDekQsdURBQW1EO0FBc05uRCxNQUFNLHNCQUFzQixHQUFHLEVBQUUsQ0FBQztBQUUzQixLQUFLLFVBQVUsV0FBVyxDQUFDLE9BQTJCO0lBQzNELE1BQU0sYUFBYSxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUM7SUFFcEMsTUFBTSxRQUFRLEdBQUcsT0FBTyxDQUFDLG1CQUFtQixDQUFDO0lBRTdDLE9BQU8sQ0FBQyxHQUFHLENBQUMscUJBQXFCLENBQUMsT0FBTyxDQUFDLGNBQWMsQ0FBQyxDQUFDO0lBQzFELE1BQU0sR0FBRyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsY0FBYyxFQUFFLENBQUM7SUFDekMsTUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDLFVBQVUsSUFBSSxhQUFhLENBQUMsU0FBUyxDQUFDO0lBQ2pFLElBQUksbUJBQW1CLEdBQUcsTUFBTSxvQ0FBbUIsQ0FBQyxNQUFNLENBQUMsR0FBRyxFQUFFLFVBQVUsQ0FBQyxDQUFDO0lBRTVFLElBQUksbUJBQW1CLENBQUMsV0FBVyxDQUFDLGlCQUFpQixFQUFFO1FBQ3JELElBQUEsZUFBSyxFQUFDLHdCQUF3QixVQUFVLHNGQUFzRixDQUFDLENBQUM7UUFDaEksTUFBTSxHQUFHLENBQUMsV0FBVyxDQUFDLEVBQUUsU0FBUyxFQUFFLFVBQVUsRUFBRSxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDM0QsTUFBTSxZQUFZLEdBQUcsTUFBTSxJQUFBLG1DQUFrQixFQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsQ0FBQztRQUMvRCxJQUFJLFlBQVksSUFBSSxZQUFZLENBQUMsV0FBVyxDQUFDLElBQUksS0FBSyxpQkFBaUIsRUFBRTtZQUN2RSxNQUFNLElBQUksS0FBSyxDQUFDLHlCQUF5QixVQUFVLHdEQUF3RCxZQUFZLENBQUMsV0FBVyxHQUFHLENBQUMsQ0FBQztTQUN6STtRQUNELDJFQUEyRTtRQUMzRSwwRUFBMEU7UUFDMUUsdUJBQXVCO1FBQ3ZCLG1CQUFtQixHQUFHLG9DQUFtQixDQUFDLFlBQVksQ0FBQyxHQUFHLEVBQUUsVUFBVSxDQUFDLENBQUM7S0FDekU7SUFFRCw2RUFBNkU7SUFDN0UsdUVBQXVFO0lBQ3ZFLGNBQWM7SUFDZCxNQUFNLFlBQVksR0FBRyxJQUFJLDZDQUFvQixFQUFFLENBQUM7SUFDaEQsTUFBTSxXQUFXLEdBQUcsTUFBTSxJQUFBLG9DQUEyQixFQUFDLGFBQWEsRUFBRSxZQUFZLEVBQUUsT0FBTyxDQUFDLFlBQVksRUFBRSxPQUFPLENBQUMsV0FBVyxDQUFDLENBQUM7SUFFOUgsTUFBTSxvQkFBb0IsR0FBRyxFQUFFLEdBQUcsT0FBTyxDQUFDLFVBQVUsRUFBRSxHQUFHLFdBQVcsRUFBRSxDQUFDO0lBRXZFLE1BQU0sY0FBYyxHQUFHLG1DQUFrQixDQUFDLFlBQVksQ0FBQyxhQUFhLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDL0UsTUFBTSxXQUFXLEdBQUcsT0FBTyxDQUFDLHFCQUFxQjtRQUMvQyxDQUFDLENBQUMsY0FBYyxDQUFDLGNBQWMsQ0FBQyxvQkFBb0IsRUFBRSxtQkFBbUIsQ0FBQyxVQUFVLENBQUM7UUFDckYsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxTQUFTLENBQUMsb0JBQW9CLENBQUMsQ0FBQztJQUVuRCxJQUFJLE1BQU0sYUFBYSxDQUFDLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxXQUFXLENBQUMsVUFBVSxDQUFDLG1CQUFtQixDQUFDLFVBQVUsQ0FBQyxDQUFDLEVBQUU7UUFDN0csSUFBQSxlQUFLLEVBQUMsR0FBRyxVQUFVLGlEQUFpRCxDQUFDLENBQUM7UUFDdEUsK0VBQStFO1FBQy9FLHNDQUFzQztRQUN0QyxJQUFJLE9BQU8sQ0FBQyxPQUFPLEtBQUssb0JBQVcsQ0FBQyxlQUFlLEVBQUU7WUFDbkQsSUFBQSxlQUFLLEVBQUMsTUFBTSxhQUFJLE9BQU8sRUFBRSxLQUFLLENBQUMsSUFBSSxDQUFDLGlGQUFpRixDQUFDLENBQUMsQ0FBQztTQUN6SDtRQUNELE9BQU87WUFDTCxJQUFJLEVBQUUsSUFBSTtZQUNWLE9BQU8sRUFBRSxtQkFBbUIsQ0FBQyxPQUFPO1lBQ3BDLFFBQVEsRUFBRSxtQkFBbUIsQ0FBQyxPQUFPO1NBQ3RDLENBQUM7S0FDSDtTQUFNO1FBQ0wsSUFBQSxlQUFLLEVBQUMsR0FBRyxVQUFVLGdCQUFnQixDQUFDLENBQUM7S0FDdEM7SUFFRCxNQUFNLGFBQWEsR0FBRyxNQUFNLGlCQUFpQixDQUMzQyxhQUFhLEVBQ2IsT0FBTyxDQUFDLG1CQUFtQixFQUMzQixZQUFZLEVBQ1osT0FBTyxDQUFDLFlBQVksRUFDcEIsT0FBTyxDQUFDLEdBQUcsRUFDWCxPQUFPLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztJQUM1QixNQUFNLElBQUEsZ0NBQWEsRUFBQyxZQUFZLENBQUMsVUFBVSxDQUFDLGFBQWEsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLEVBQUUsT0FBTyxDQUFDLFdBQVcsRUFBRSxRQUFRLEVBQUU7UUFDNUcsUUFBUSxFQUFFLE9BQU8sQ0FBQyxnQkFBZ0I7S0FDbkMsQ0FBQyxDQUFDO0lBRUgsTUFBTSxXQUFXLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQztJQUNwQyxJQUFJLFdBQVcsSUFBSSxXQUFXLEtBQUssb0JBQVcsQ0FBQyxlQUFlLEVBQUU7UUFDOUQsc0RBQXNEO1FBQ3RELElBQUk7WUFDRixNQUFNLHVCQUF1QixHQUFHLE1BQU0sSUFBQSwwQ0FBb0IsRUFDeEQsT0FBTyxDQUFDLFdBQVcsRUFBRSxXQUFXLENBQUMsTUFBTSxFQUFFLG1CQUFtQixFQUFFLGFBQWEsRUFBRSxXQUFXLENBQ3pGLENBQUM7WUFDRixJQUFJLHVCQUF1QixFQUFFO2dCQUMzQixPQUFPLHVCQUF1QixDQUFDO2FBQ2hDO1lBQ0QsSUFBQSxlQUFLLEVBQUMsb0ZBQW9GLEVBQUUsYUFBYSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1NBQ3hIO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDVixJQUFJLENBQUMsQ0FBQyxDQUFDLFlBQVkseURBQXNCLENBQUMsRUFBRTtnQkFDMUMsTUFBTSxDQUFDLENBQUM7YUFDVDtZQUNELElBQUEsZUFBSyxFQUFDLHVHQUF1RyxFQUFFLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQztTQUMzSDtRQUVELElBQUksV0FBVyxLQUFLLG9CQUFXLENBQUMsU0FBUyxFQUFFO1lBQ3pDLElBQUEsZUFBSyxFQUFDLHlDQUF5QyxDQUFDLENBQUM7WUFDakQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxxQkFBcUIsQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO1NBQzNEO2FBQU07WUFDTCxPQUFPLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsbUJBQW1CLENBQUMsT0FBTyxFQUFFLE9BQU8sRUFBRSxtQkFBbUIsQ0FBQyxPQUFPLEVBQUUsQ0FBQztTQUNwRztLQUNGO0lBRUQsNEVBQTRFO0lBQzVFLE1BQU0sY0FBYyxHQUFHLElBQUksNEJBQTRCLENBQUMsT0FBTyxFQUFFLG1CQUFtQixFQUFFLGFBQWEsRUFBRSxXQUFXLEVBQUUsYUFBYSxDQUFDLENBQUM7SUFDakksT0FBTyxjQUFjLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztBQUM1QyxDQUFDO0FBNUZELGtDQTRGQztBQVdEOztHQUVHO0FBQ0gsTUFBTSw0QkFBNEI7SUFPaEMsWUFDbUIsT0FBMkIsRUFDM0IsbUJBQXdDLEVBQ3hDLGFBQWdELEVBQ2hELFdBQTRCLEVBQzVCLGFBQW9DO1FBSnBDLFlBQU8sR0FBUCxPQUFPLENBQW9CO1FBQzNCLHdCQUFtQixHQUFuQixtQkFBbUIsQ0FBcUI7UUFDeEMsa0JBQWEsR0FBYixhQUFhLENBQW1DO1FBQ2hELGdCQUFXLEdBQVgsV0FBVyxDQUFpQjtRQUM1QixrQkFBYSxHQUFiLGFBQWEsQ0FBdUI7UUFFckQsSUFBSSxDQUFDLEdBQUcsR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLGNBQWMsRUFBRSxDQUFDO1FBQ3hDLElBQUksQ0FBQyxTQUFTLEdBQUcsT0FBTyxDQUFDLFVBQVUsSUFBSSxhQUFhLENBQUMsU0FBUyxDQUFDO1FBRS9ELElBQUksQ0FBQyxNQUFNLEdBQUcsbUJBQW1CLENBQUMsTUFBTSxJQUFJLG1CQUFtQixDQUFDLFdBQVcsQ0FBQyxJQUFJLEtBQUssb0JBQW9CLENBQUM7UUFDMUcsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQztRQUM5QyxJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQztJQUN4QixDQUFDO0lBRU0sS0FBSyxDQUFDLGlCQUFpQjtRQUM1QixNQUFNLGdCQUFnQixHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLElBQUksRUFBRSxNQUFNLEVBQUUsWUFBWSxFQUFFLENBQUM7UUFFbkYsSUFBSSxnQkFBZ0IsQ0FBQyxNQUFNLEtBQUssUUFBUSxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsaUJBQWlCLEVBQUU7WUFDMUUsTUFBTSxJQUFJLEtBQUssQ0FBQyxxREFBcUQsQ0FBQyxDQUFDO1NBQ3hFO1FBRUQsUUFBUSxnQkFBZ0IsQ0FBQyxNQUFNLEVBQUU7WUFDL0IsS0FBSyxZQUFZO2dCQUNmLE9BQU8sSUFBSSxDQUFDLG1CQUFtQixDQUFDLGdCQUFnQixDQUFDLENBQUM7WUFFcEQsS0FBSyxRQUFRO2dCQUNYLE9BQU8sSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7U0FDbEM7SUFDSCxDQUFDO0lBRU8sS0FBSyxDQUFDLG1CQUFtQixDQUFDLGdCQUEyQztRQUMzRSxNQUFNLGFBQWEsR0FBRyxnQkFBZ0IsQ0FBQyxhQUFhLElBQUksdUJBQXVCLENBQUM7UUFDaEYsTUFBTSxPQUFPLEdBQUcsZ0JBQWdCLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQztRQUNqRCxNQUFNLG9CQUFvQixHQUFHLE1BQU0sSUFBSSxDQUFDLGVBQWUsQ0FBQyxhQUFhLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDaEYsTUFBTSxJQUFJLENBQUMsMkJBQTJCLEVBQUUsQ0FBQztRQUV6QyxJQUFJLElBQUEsc0NBQXFCLEVBQUMsb0JBQW9CLENBQUMsRUFBRTtZQUMvQyxJQUFBLGVBQUssRUFBQyx1Q0FBdUMsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDL0QsSUFBSSxPQUFPLEVBQUU7Z0JBQ1gsSUFBQSxlQUFLLEVBQUMsOEJBQThCLEVBQUUsb0JBQW9CLENBQUMsV0FBVyxDQUFDLENBQUM7Z0JBQ3hFLE1BQU0sSUFBSSxDQUFDLEdBQUcsQ0FBQyxlQUFlLENBQUMsRUFBRSxTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVMsRUFBRSxhQUFhLEVBQUUsYUFBYSxFQUFFLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQzthQUN2RztZQUNELE9BQU8sRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxJQUFJLENBQUMsbUJBQW1CLENBQUMsT0FBTyxFQUFFLFFBQVEsRUFBRSxvQkFBb0IsQ0FBQyxPQUFRLEVBQUUsQ0FBQztTQUMzRztRQUVELElBQUksQ0FBQyxPQUFPLEVBQUU7WUFDWixJQUFBLGVBQUssRUFBQyxnRkFBZ0YsRUFBRSxvQkFBb0IsQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUMxSCxPQUFPLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsSUFBSSxDQUFDLG1CQUFtQixDQUFDLE9BQU8sRUFBRSxRQUFRLEVBQUUsb0JBQW9CLENBQUMsT0FBUSxFQUFFLENBQUM7U0FDNUc7UUFFRCxPQUFPLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO0lBQ3JELENBQUM7SUFFTyxLQUFLLENBQUMsZUFBZSxDQUFDLGFBQXFCLEVBQUUsV0FBb0I7UUFDdkUsTUFBTSxJQUFJLENBQUMsbUJBQW1CLENBQUMsYUFBYSxDQUFDLENBQUM7UUFFOUMsSUFBQSxlQUFLLEVBQUMsNENBQTRDLGFBQWEsT0FBTyxJQUFJLENBQUMsSUFBSSxVQUFVLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQyxDQUFDO1FBQzNHLElBQUEsZUFBSyxFQUFDLDBDQUEwQyxFQUFFLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUM7UUFDOUUsTUFBTSxTQUFTLEdBQUcsTUFBTSxJQUFJLENBQUMsR0FBRyxDQUFDLGVBQWUsQ0FBQztZQUMvQyxTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVM7WUFDekIsYUFBYSxFQUFFLGFBQWE7WUFDNUIsYUFBYSxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsaUJBQWlCLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxRQUFRO1lBQzVGLGlCQUFpQixFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsaUJBQWlCO1lBQ2pELFdBQVcsRUFBRSwrQkFBK0IsSUFBSSxDQUFDLElBQUksRUFBRTtZQUN2RCxXQUFXLEVBQUUsU0FBUyxJQUFJLENBQUMsSUFBSSxFQUFFO1lBQ2pDLEdBQUcsSUFBSSxDQUFDLG9CQUFvQixFQUFFO1NBQy9CLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUViLElBQUEsZUFBSyxFQUFDLDJFQUEyRSxFQUFFLFNBQVMsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUNqRyxnR0FBZ0c7UUFDaEcsT0FBTyxJQUFBLGlDQUFnQixFQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLFNBQVMsRUFBRSxhQUFhLEVBQUUsRUFBRSxRQUFRLEVBQUUsV0FBVyxFQUFFLENBQUMsQ0FBQztJQUM5RixDQUFDO0lBRU8sS0FBSyxDQUFDLGdCQUFnQixDQUFDLFNBQWlEO1FBQzlFLElBQUEsZUFBSyxFQUFDLGtEQUFrRCxFQUFFLFNBQVMsQ0FBQyxXQUFXLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBRWpHLE1BQU0sSUFBSSxDQUFDLEdBQUcsQ0FBQyxnQkFBZ0IsQ0FBQztZQUM5QixTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVM7WUFDekIsYUFBYSxFQUFFLFNBQVMsQ0FBQyxhQUFjO1lBQ3ZDLGtCQUFrQixFQUFFLE9BQU8sSUFBSSxDQUFDLElBQUksRUFBRTtZQUN0QyxHQUFHLElBQUksQ0FBQyxvQkFBb0IsRUFBRTtTQUMvQixDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7UUFFYixJQUFBLGVBQUssRUFBQywwRkFBMEYsRUFBRSxTQUFTLENBQUMsV0FBVyxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUV6SSwrQ0FBK0M7UUFDL0MsTUFBTSxlQUFlLEdBQVcsQ0FBQyxTQUFTLENBQUMsT0FBTyxJQUFJLEVBQUUsQ0FBQyxDQUFDLE1BQU0sR0FBRyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDekYsT0FBTyxJQUFJLENBQUMsaUJBQWlCLENBQUMsU0FBUyxDQUFDLFlBQWEsRUFBRSxlQUFlLENBQUMsQ0FBQztJQUMxRSxDQUFDO0lBRU8sS0FBSyxDQUFDLG1CQUFtQixDQUFDLGFBQXFCO1FBQ3JELElBQUksSUFBSSxDQUFDLG1CQUFtQixDQUFDLE1BQU0sRUFBRTtZQUNuQywwRkFBMEY7WUFDMUYsd0dBQXdHO1lBQ3hHLElBQUEsZUFBSyxFQUFDLDBDQUEwQyxhQUFhLGVBQWUsQ0FBQyxDQUFDO1lBQzlFLE1BQU0sSUFBSSxDQUFDLEdBQUcsQ0FBQyxlQUFlLENBQUMsRUFBRSxTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVMsRUFBRSxhQUFhLEVBQUUsYUFBYSxFQUFFLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztTQUN2RztJQUNILENBQUM7SUFFTyxLQUFLLENBQUMsMkJBQTJCO1FBQ3ZDLHdEQUF3RDtRQUN4RCxNQUFNLHFCQUFxQixHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMscUJBQXFCLElBQUksS0FBSyxDQUFDO1FBQ2hGLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxxQkFBcUIsS0FBSyxxQkFBcUIsRUFBRTtZQUM5RSxJQUFBLGVBQUssRUFBQyw0REFBNEQsRUFBRSxJQUFJLENBQUMsbUJBQW1CLENBQUMscUJBQXFCLEVBQUUscUJBQXFCLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1lBQzNKLE1BQU0sSUFBSSxDQUFDLEdBQUcsQ0FBQywyQkFBMkIsQ0FBQztnQkFDekMsU0FBUyxFQUFFLElBQUksQ0FBQyxTQUFTO2dCQUN6QiwyQkFBMkIsRUFBRSxxQkFBcUI7YUFDbkQsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ2IsSUFBQSxlQUFLLEVBQUMsbURBQW1ELEVBQUUscUJBQXFCLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1NBQ25HO0lBQ0gsQ0FBQztJQUVPLEtBQUssQ0FBQyxnQkFBZ0I7UUFDNUIsSUFBQSxlQUFLLEVBQUMsaUJBQWlCLEVBQUUsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUU1RixNQUFNLFNBQVMsR0FBRyxJQUFJLElBQUksRUFBRSxDQUFDO1FBRTdCLElBQUksSUFBSSxDQUFDLE1BQU0sRUFBRTtZQUNmLE1BQU0sSUFBSSxDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUM7Z0JBQ3pCLFNBQVMsRUFBRSxJQUFJLENBQUMsU0FBUztnQkFDekIsa0JBQWtCLEVBQUUsU0FBUyxJQUFJLENBQUMsSUFBSSxFQUFFO2dCQUN4QyxHQUFHLElBQUksQ0FBQyxvQkFBb0IsRUFBRTtnQkFDOUIsR0FBRyxJQUFJLENBQUMsb0JBQW9CLEVBQUU7YUFDL0IsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBRWIsTUFBTSxHQUFHLEdBQUcsTUFBTSxJQUFJLENBQUMsaUJBQWlCLENBQUMsU0FBUyxFQUFFLFNBQVMsQ0FBQyxDQUFDO1lBQy9ELE1BQU0sSUFBSSxDQUFDLDJCQUEyQixFQUFFLENBQUM7WUFDekMsT0FBTyxHQUFHLENBQUM7U0FDWjthQUFNO1lBQ0wsa0ZBQWtGO1lBQ2xGLE1BQU0scUJBQXFCLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxxQkFBcUIsSUFBSSxLQUFLLENBQUM7WUFFaEYsTUFBTSxJQUFJLENBQUMsR0FBRyxDQUFDLFdBQVcsQ0FBQztnQkFDekIsU0FBUyxFQUFFLElBQUksQ0FBQyxTQUFTO2dCQUN6QixrQkFBa0IsRUFBRSxTQUFTLElBQUksQ0FBQyxJQUFJLEVBQUU7Z0JBQ3hDLEdBQUcscUJBQXFCLENBQUMsQ0FBQyxDQUFDLEVBQUUsMkJBQTJCLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLFNBQVM7Z0JBQzVFLEdBQUcsSUFBSSxDQUFDLG9CQUFvQixFQUFFO2dCQUM5QixHQUFHLElBQUksQ0FBQyxvQkFBb0IsRUFBRTthQUMvQixDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7WUFFYixPQUFPLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxTQUFTLEVBQUUsU0FBUyxDQUFDLENBQUM7U0FDckQ7SUFDSCxDQUFDO0lBRU8sS0FBSyxDQUFDLGlCQUFpQixDQUFDLFNBQWUsRUFBRSxlQUFtQztRQUNsRixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyw2Q0FBb0IsQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxTQUFTLEVBQUUsSUFBSSxDQUFDLGFBQWEsRUFBRTtZQUNySSxjQUFjLEVBQUUsZUFBZTtZQUMvQixRQUFRLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRO1lBQy9CLHFCQUFxQixFQUFFLFNBQVM7WUFDaEMsRUFBRSxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsRUFBRTtTQUNwQixDQUFDLENBQUMsS0FBSyxFQUFFLENBQUM7UUFFWCxJQUFJLFVBQVUsR0FBRyxJQUFJLENBQUMsbUJBQW1CLENBQUM7UUFDMUMsSUFBSTtZQUNGLE1BQU0sWUFBWSxHQUFHLE1BQU0sSUFBQSxtQ0FBa0IsRUFBQyxJQUFJLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUV4RSxxRUFBcUU7WUFDckUsSUFBSSxDQUFDLFlBQVksRUFBRTtnQkFBRSxNQUFNLElBQUksS0FBSyxDQUFDLHdFQUF3RSxDQUFDLENBQUM7YUFBRTtZQUNqSCxVQUFVLEdBQUcsWUFBWSxDQUFDO1NBQzNCO1FBQUMsT0FBTyxDQUFNLEVBQUU7WUFDZixNQUFNLElBQUksS0FBSyxDQUFDLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7U0FDL0Q7Z0JBQVM7WUFDUixNQUFNLE9BQU8sRUFBRSxJQUFJLEVBQUUsQ0FBQztTQUN2QjtRQUNELElBQUEsZUFBSyxFQUFDLGlDQUFpQyxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUN6RCxPQUFPLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsVUFBVSxDQUFDLE9BQU8sRUFBRSxRQUFRLEVBQUUsVUFBVSxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBQ3BGLENBQUM7SUFFRDs7T0FFRztJQUNLLG9CQUFvQjtRQUMxQixPQUFPO1lBQ0wsWUFBWSxFQUFFLENBQUMsZ0JBQWdCLEVBQUUsc0JBQXNCLEVBQUUsd0JBQXdCLENBQUM7WUFDbEYsZ0JBQWdCLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0I7WUFDL0MsVUFBVSxFQUFFLElBQUksQ0FBQyxXQUFXLENBQUMsYUFBYTtZQUMxQyxPQUFPLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPO1lBQzdCLFlBQVksRUFBRSxJQUFJLENBQUMsYUFBYSxDQUFDLFlBQVk7WUFDN0MsV0FBVyxFQUFFLElBQUksQ0FBQyxhQUFhLENBQUMsV0FBVztZQUMzQyxJQUFJLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJO1NBQ3hCLENBQUM7SUFDSixDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSyxvQkFBb0I7UUFDMUIsTUFBTSxxQkFBcUIsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsS0FBSyxLQUFLLENBQUM7UUFFOUQsT0FBTztZQUNMLFNBQVMsRUFBRSxJQUFJLENBQUMsU0FBUztZQUN6QixHQUFHLHFCQUFxQixDQUFDLENBQUMsQ0FBQyxFQUFFLGVBQWUsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUztTQUNqRSxDQUFDO0lBQ0osQ0FBQztDQUNGO0FBRUQ7Ozs7Ozs7Ozs7OztHQVlHO0FBQ0gsS0FBSyxVQUFVLGlCQUFpQixDQUM5QixLQUF3QyxFQUN4QyxtQkFBc0MsRUFDdEMsYUFBbUMsRUFDbkMsU0FBK0IsRUFDL0IsR0FBUyxFQUNULGdCQUFzQjtJQUd0QiwyRUFBMkU7SUFDM0UsSUFBSSxLQUFLLENBQUMsMkJBQTJCLElBQUksQ0FBQyxnQkFBZ0IsRUFBRTtRQUMxRCxPQUFPLEVBQUUsV0FBVyxFQUFFLG1CQUFtQixDQUFDLEtBQUssQ0FBQywyQkFBMkIsRUFBRSxtQkFBbUIsRUFBRSxHQUFHLENBQUMsRUFBRSxDQUFDO0tBQzFHO0lBRUQsb0VBQW9FO0lBQ3BFLE1BQU0sWUFBWSxHQUFHLElBQUEsa0JBQU0sRUFBQyxnQkFBZ0IsSUFBSSxLQUFLLENBQUMsUUFBUSxDQUFDLENBQUM7SUFFaEUsSUFBSSxZQUFZLENBQUMsTUFBTSxJQUFJLHNCQUFzQixHQUFHLElBQUksRUFBRTtRQUN4RCxPQUFPLEVBQUUsWUFBWSxFQUFFLFlBQVksRUFBRSxDQUFDO0tBQ3ZDO0lBRUQsTUFBTSxXQUFXLEdBQUcsTUFBTSxTQUFTLENBQUMsYUFBYSxFQUFFLENBQUM7SUFDcEQsSUFBSSxDQUFDLFdBQVcsQ0FBQyxLQUFLLEVBQUU7UUFDdEIsSUFBQSxlQUFLLEVBQ0gsMkJBQTJCLEtBQUssQ0FBQyxXQUFXLFFBQVEsSUFBSSxDQUFDLEtBQUssQ0FBQyxZQUFZLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQyxPQUFPO1lBQ2pHLHlCQUF5QixzQkFBc0IsK0JBQStCO1lBQzlFLHVHQUF1RyxFQUN2RyxLQUFLLENBQUMsSUFBSSxDQUFDLHFCQUFxQixtQkFBbUIsQ0FBQyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUM7UUFFakUsTUFBTSxJQUFJLEtBQUssQ0FBQyw0REFBNEQsQ0FBQyxDQUFDO0tBQy9FO0lBRUQsTUFBTSxZQUFZLEdBQUcsSUFBQSwwQkFBVyxFQUFDLFlBQVksQ0FBQyxDQUFDO0lBQy9DLE1BQU0sR0FBRyxHQUFHLE9BQU8sS0FBSyxDQUFDLEVBQUUsSUFBSSxZQUFZLE1BQU0sQ0FBQztJQUVsRCxJQUFJLFlBQVksR0FBRyxLQUFLLENBQUMsWUFBWSxDQUFDO0lBQ3RDLElBQUksZ0JBQWdCLEVBQUU7UUFDcEIsaUNBQWlDO1FBQ2pDLFlBQVksR0FBRyxHQUFHLEtBQUssQ0FBQyxZQUFZLElBQUksWUFBWSxPQUFPLENBQUM7UUFDNUQsTUFBTSxFQUFFLENBQUMsU0FBUyxDQUFDLFlBQVksRUFBRSxZQUFZLEVBQUUsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztLQUN2RTtJQUVELGFBQWEsQ0FBQyxZQUFZLENBQUMsWUFBWSxFQUFFO1FBQ3ZDLElBQUksRUFBRSxZQUFZO0tBQ25CLEVBQUU7UUFDRCxVQUFVLEVBQUUsV0FBVyxDQUFDLFVBQVU7UUFDbEMsU0FBUyxFQUFFLEdBQUc7S0FDZixDQUFDLENBQUM7SUFFSCxNQUFNLFdBQVcsR0FBRyxHQUFHLFdBQVcsQ0FBQyxTQUFTLElBQUksR0FBRyxFQUFFLENBQUM7SUFDdEQsSUFBQSxlQUFLLEVBQUMsNEJBQTRCLEVBQUUsV0FBVyxDQUFDLENBQUM7SUFDakQsT0FBTyxFQUFFLFdBQVcsRUFBRSxXQUFXLEVBQUUsQ0FBQztBQUN0QyxDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSSxLQUFLLFVBQVUsMEJBQTBCLENBQzlDLEtBQXdDLEVBQ3hDLG1CQUFzQyxFQUN0QyxTQUErQixFQUMvQixXQUF3QixFQUN4QixHQUFTLEVBQ1QsZ0JBQXNCO0lBRXRCLDhFQUE4RTtJQUM5RSwwQ0FBMEM7SUFDMUMsTUFBTSxnQkFBZ0IsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRTtRQUM1QywyQkFBMkIsRUFBRSxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUU7S0FDbEQsQ0FBQyxDQUFDO0lBRUgsTUFBTSxPQUFPLEdBQUcsSUFBSSw2Q0FBb0IsRUFBRSxDQUFDO0lBQzNDLE1BQU0sU0FBUyxHQUFHLE1BQU0saUJBQWlCLENBQUMsZ0JBQWdCLEVBQUUsbUJBQW1CLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxHQUFHLEVBQUUsZ0JBQWdCLENBQUMsQ0FBQztJQUM1SCxNQUFNLFFBQVEsR0FBRyxPQUFPLENBQUMsVUFBVSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDOUQsTUFBTSxJQUFBLGdDQUFhLEVBQUMsUUFBUSxFQUFFLFdBQVcsRUFBRSxtQkFBbUIsRUFBRSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ2pGLE9BQU8sU0FBUyxDQUFDO0FBQ25CLENBQUM7QUFuQkQsZ0VBbUJDO0FBZU0sS0FBSyxVQUFVLFlBQVksQ0FBQyxPQUE0QjtJQUM3RCxNQUFNLFVBQVUsR0FBRyxPQUFPLENBQUMsVUFBVSxJQUFJLE9BQU8sQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDO0lBQ2pFLE1BQU0sR0FBRyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsY0FBYyxFQUFFLENBQUM7SUFFekMsTUFBTSxZQUFZLEdBQUcsTUFBTSxvQ0FBbUIsQ0FBQyxNQUFNLENBQUMsR0FBRyxFQUFFLFVBQVUsQ0FBQyxDQUFDO0lBQ3ZFLElBQUksQ0FBQyxZQUFZLENBQUMsTUFBTSxFQUFFO1FBQ3hCLE9BQU87S0FDUjtJQUNELE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsNkNBQW9CLENBQUMsa0JBQWtCLENBQUMsR0FBRyxFQUFFLFVBQVUsRUFBRSxPQUFPLENBQUMsS0FBSyxFQUFFO1FBQ2xILEVBQUUsRUFBRSxPQUFPLENBQUMsRUFBRTtLQUNmLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUVYLElBQUk7UUFDRixNQUFNLEdBQUcsQ0FBQyxXQUFXLENBQUMsRUFBRSxTQUFTLEVBQUUsVUFBVSxFQUFFLE9BQU8sRUFBRSxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNyRixNQUFNLGNBQWMsR0FBRyxNQUFNLElBQUEsbUNBQWtCLEVBQUMsR0FBRyxFQUFFLFVBQVUsQ0FBQyxDQUFDO1FBQ2pFLElBQUksY0FBYyxJQUFJLGNBQWMsQ0FBQyxXQUFXLENBQUMsSUFBSSxLQUFLLGlCQUFpQixFQUFFO1lBQzNFLE1BQU0sSUFBSSxLQUFLLENBQUMscUJBQXFCLFVBQVUsS0FBSyxjQUFjLENBQUMsV0FBVyxFQUFFLENBQUMsQ0FBQztTQUNuRjtLQUNGO0lBQUMsT0FBTyxDQUFNLEVBQUU7UUFDZixNQUFNLElBQUksS0FBSyxDQUFDLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7S0FDL0Q7WUFBUztRQUNSLElBQUksT0FBTyxFQUFFO1lBQUUsTUFBTSxPQUFPLENBQUMsSUFBSSxFQUFFLENBQUM7U0FBRTtLQUN2QztBQUNILENBQUM7QUF2QkQsb0NBdUJDO0FBRUQ7Ozs7Ozs7O0dBUUc7QUFDSCxLQUFLLFVBQVUsYUFBYSxDQUMxQixrQkFBc0MsRUFDdEMsbUJBQXdDLEVBQ3hDLGdCQUFrQztJQUVsQyxNQUFNLFVBQVUsR0FBRyxrQkFBa0IsQ0FBQyxVQUFVLElBQUksa0JBQWtCLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQztJQUN2RixJQUFBLGVBQUssRUFBQyxHQUFHLFVBQVUsa0NBQWtDLENBQUMsQ0FBQztJQUV2RCxnQkFBZ0I7SUFDaEIsSUFBSSxrQkFBa0IsQ0FBQyxLQUFLLEVBQUU7UUFDNUIsSUFBQSxlQUFLLEVBQUMsR0FBRyxVQUFVLHFCQUFxQixDQUFDLENBQUM7UUFDMUMsT0FBTyxLQUFLLENBQUM7S0FDZDtJQUVELHFEQUFxRDtJQUNyRCxJQUFJLGtCQUFrQixDQUFDLGdCQUFnQixFQUFFLE1BQU0sS0FBSyxZQUFZLElBQUksa0JBQWtCLENBQUMsZ0JBQWdCLENBQUMsT0FBTyxLQUFLLEtBQUssRUFBRTtRQUN6SCxJQUFBLGVBQUssRUFBQyxHQUFHLFVBQVUsNENBQTRDLENBQUMsQ0FBQztRQUNqRSxPQUFPLEtBQUssQ0FBQztLQUNkO0lBRUQsb0JBQW9CO0lBQ3BCLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxNQUFNLEVBQUU7UUFDL0IsSUFBQSxlQUFLLEVBQUMsR0FBRyxVQUFVLHFCQUFxQixDQUFDLENBQUM7UUFDMUMsT0FBTyxLQUFLLENBQUM7S0FDZDtJQUVELHdEQUF3RDtJQUN4RCxJQUFJLElBQUksQ0FBQyxTQUFTLENBQUMsa0JBQWtCLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxLQUFLLElBQUksQ0FBQyxTQUFTLENBQUMsTUFBTSxtQkFBbUIsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxFQUFFO1FBQzlHLElBQUEsZUFBSyxFQUFDLEdBQUcsVUFBVSx3QkFBd0IsQ0FBQyxDQUFDO1FBQzdDLE9BQU8sS0FBSyxDQUFDO0tBQ2Q7SUFFRCxvQkFBb0I7SUFDcEIsSUFBSSxDQUFDLFdBQVcsQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJLEVBQUUsa0JBQWtCLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQyxFQUFFO1FBQ3pFLElBQUEsZUFBSyxFQUFDLEdBQUcsVUFBVSxxQkFBcUIsQ0FBQyxDQUFDO1FBQzFDLE9BQU8sS0FBSyxDQUFDO0tBQ2Q7SUFFRCwwQ0FBMEM7SUFDMUMsSUFBSSxDQUFDLENBQUMsa0JBQWtCLENBQUMsS0FBSyxDQUFDLHFCQUFxQixLQUFLLENBQUMsQ0FBQyxtQkFBbUIsQ0FBQyxxQkFBcUIsRUFBRTtRQUNwRyxJQUFBLGVBQUssRUFBQyxHQUFHLFVBQVUsMkNBQTJDLENBQUMsQ0FBQztRQUNoRSxPQUFPLEtBQUssQ0FBQztLQUNkO0lBRUQsMEJBQTBCO0lBQzFCLElBQUksZ0JBQWdCLEVBQUU7UUFDcEIsSUFBSSxnQkFBZ0IsS0FBSyxLQUFLLEVBQUU7WUFDOUIsSUFBQSxlQUFLLEVBQUMsR0FBRyxVQUFVLDRFQUE0RSxDQUFDLENBQUM7U0FDbEc7YUFBTTtZQUNMLElBQUEsZUFBSyxFQUFDLEdBQUcsVUFBVSwyQkFBMkIsQ0FBQyxDQUFDO1NBQ2pEO1FBQ0QsT0FBTyxLQUFLLENBQUM7S0FDZDtJQUVELHNDQUFzQztJQUN0QyxJQUFJLG1CQUFtQixDQUFDLFdBQVcsQ0FBQyxTQUFTLEVBQUU7UUFDN0MsSUFBQSxlQUFLLEVBQUMsR0FBRyxVQUFVLCtCQUErQixDQUFDLENBQUM7UUFDcEQsT0FBTyxLQUFLLENBQUM7S0FDZDtJQUVELHFCQUFxQjtJQUNyQixPQUFPLElBQUksQ0FBQztBQUNkLENBQUM7QUFFRDs7R0FFRztBQUNILFNBQVMsV0FBVyxDQUFDLENBQVEsRUFBRSxDQUFRO0lBQ3JDLElBQUksQ0FBQyxDQUFDLE1BQU0sS0FBSyxDQUFDLENBQUMsTUFBTSxFQUFFO1FBQ3pCLE9BQU8sS0FBSyxDQUFDO0tBQ2Q7SUFFRCxLQUFLLE1BQU0sSUFBSSxJQUFJLENBQUMsRUFBRTtRQUNwQixNQUFNLElBQUksR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLEdBQUcsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7UUFFakQsSUFBSSxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsS0FBSyxLQUFLLElBQUksQ0FBQyxLQUFLLEVBQUU7WUFDdEMsT0FBTyxLQUFLLENBQUM7U0FDZDtLQUNGO0lBRUQsT0FBTyxJQUFJLENBQUM7QUFDZCxDQUFDO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsU0FBUyxtQkFBbUIsQ0FBQyxHQUFXLEVBQUUsV0FBOEIsRUFBRSxHQUFTO0lBQ2pGLE1BQU0sY0FBYyxHQUFHLGNBQWMsQ0FBQztJQUN0QyxnRUFBZ0U7SUFDaEUsR0FBRyxHQUFHLEtBQUssQ0FBQyx1QkFBdUIsQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFO1FBQy9DLFNBQVMsRUFBRSxXQUFXLENBQUMsT0FBTztRQUM5QixNQUFNLEVBQUUsV0FBVyxDQUFDLE1BQU07UUFDMUIsU0FBUyxFQUFFLGNBQWM7S0FDMUIsQ0FBQyxDQUFDO0lBRUgsNkZBQTZGO0lBQzdGLHFFQUFxRTtJQUNyRSxJQUFJLEdBQUcsQ0FBQyxPQUFPLENBQUMsY0FBYyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUU7UUFDcEMsTUFBTSxJQUFJLEtBQUssQ0FBQywrRUFBK0UsQ0FBQyxDQUFDO0tBQ2xHO0lBRUQsTUFBTSxLQUFLLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDO0lBQ2pELElBQUksQ0FBQyxLQUFLLEVBQUU7UUFBRSxPQUFPLEdBQUcsQ0FBQztLQUFFO0lBRTNCLHdHQUF3RztJQUN4Ryw0RUFBNEU7SUFDNUUsTUFBTSxVQUFVLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzVCLE1BQU0sU0FBUyxHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUUzQixNQUFNLFNBQVMsR0FBVyxHQUFHLENBQUMsaUJBQWlCLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ3BFLE9BQU8sY0FBYyxXQUFXLENBQUMsTUFBTSxJQUFJLFNBQVMsSUFBSSxVQUFVLElBQUksU0FBUyxFQUFFLENBQUM7QUFDcEYsQ0FBQztBQUVELFNBQVMsZ0JBQWdCLENBQUMsR0FBVyxFQUFFLE1BQWlCO0lBQ3RELE9BQU8sTUFBTSxJQUFJLE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQztRQUNoQyxDQUFDLENBQUMsR0FBRyxHQUFHLEtBQUssTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRTtRQUNoQyxDQUFDLENBQUMsR0FBRyxDQUFDO0FBQ1YsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGN4YXBpIGZyb20gJ0Bhd3MtY2RrL2N4LWFwaSc7XG5pbXBvcnQgdHlwZSB7IENsb3VkRm9ybWF0aW9uIH0gZnJvbSAnYXdzLXNkayc7XG5pbXBvcnQgKiBhcyBjaGFsayBmcm9tICdjaGFsayc7XG5pbXBvcnQgKiBhcyBmcyBmcm9tICdmcy1leHRyYSc7XG5pbXBvcnQgKiBhcyB1dWlkIGZyb20gJ3V1aWQnO1xuaW1wb3J0IHsgSVNESywgU2RrUHJvdmlkZXIgfSBmcm9tICcuL2F3cy1hdXRoJztcbmltcG9ydCB7IEVudmlyb25tZW50UmVzb3VyY2VzIH0gZnJvbSAnLi9lbnZpcm9ubWVudC1yZXNvdXJjZXMnO1xuaW1wb3J0IHsgQ2ZuRXZhbHVhdGlvbkV4Y2VwdGlvbiB9IGZyb20gJy4vZXZhbHVhdGUtY2xvdWRmb3JtYXRpb24tdGVtcGxhdGUnO1xuaW1wb3J0IHsgSG90c3dhcE1vZGUsIElDT04gfSBmcm9tICcuL2hvdHN3YXAvY29tbW9uJztcbmltcG9ydCB7IHRyeUhvdHN3YXBEZXBsb3ltZW50IH0gZnJvbSAnLi9ob3Rzd2FwLWRlcGxveW1lbnRzJztcbmltcG9ydCB7XG4gIGNoYW5nZVNldEhhc05vQ2hhbmdlcywgQ2xvdWRGb3JtYXRpb25TdGFjaywgVGVtcGxhdGVQYXJhbWV0ZXJzLCB3YWl0Rm9yQ2hhbmdlU2V0LFxuICB3YWl0Rm9yU3RhY2tEZXBsb3ksIHdhaXRGb3JTdGFja0RlbGV0ZSwgUGFyYW1ldGVyVmFsdWVzLCBQYXJhbWV0ZXJDaGFuZ2VzLCBSZXNvdXJjZXNUb0ltcG9ydCxcbn0gZnJvbSAnLi91dGlsL2Nsb3VkZm9ybWF0aW9uJztcbmltcG9ydCB7IFN0YWNrQWN0aXZpdHlNb25pdG9yLCBTdGFja0FjdGl2aXR5UHJvZ3Jlc3MgfSBmcm9tICcuL3V0aWwvY2xvdWRmb3JtYXRpb24vc3RhY2stYWN0aXZpdHktbW9uaXRvcic7XG5pbXBvcnQgeyBhZGRNZXRhZGF0YUFzc2V0c1RvTWFuaWZlc3QgfSBmcm9tICcuLi9hc3NldHMnO1xuaW1wb3J0IHsgVGFnIH0gZnJvbSAnLi4vY2RrLXRvb2xraXQnO1xuaW1wb3J0IHsgZGVidWcsIGVycm9yLCBwcmludCB9IGZyb20gJy4uL2xvZ2dpbmcnO1xuaW1wb3J0IHsgdG9ZQU1MIH0gZnJvbSAnLi4vc2VyaWFsaXplJztcbmltcG9ydCB7IEFzc2V0TWFuaWZlc3RCdWlsZGVyIH0gZnJvbSAnLi4vdXRpbC9hc3NldC1tYW5pZmVzdC1idWlsZGVyJztcbmltcG9ydCB7IHB1Ymxpc2hBc3NldHMgfSBmcm9tICcuLi91dGlsL2Fzc2V0LXB1Ymxpc2hpbmcnO1xuaW1wb3J0IHsgY29udGVudEhhc2ggfSBmcm9tICcuLi91dGlsL2NvbnRlbnQtaGFzaCc7XG5cbnR5cGUgVGVtcGxhdGVCb2R5UGFyYW1ldGVyID0ge1xuICBUZW1wbGF0ZUJvZHk/OiBzdHJpbmdcbiAgVGVtcGxhdGVVUkw/OiBzdHJpbmdcbn07XG5cbmV4cG9ydCBpbnRlcmZhY2UgRGVwbG95U3RhY2tSZXN1bHQge1xuICByZWFkb25seSBub09wOiBib29sZWFuO1xuICByZWFkb25seSBvdXRwdXRzOiB7IFtuYW1lOiBzdHJpbmddOiBzdHJpbmcgfTtcbiAgcmVhZG9ubHkgc3RhY2tBcm46IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBEZXBsb3lTdGFja09wdGlvbnMge1xuICAvKipcbiAgICogVGhlIHN0YWNrIHRvIGJlIGRlcGxveWVkXG4gICAqL1xuICByZWFkb25seSBzdGFjazogY3hhcGkuQ2xvdWRGb3JtYXRpb25TdGFja0FydGlmYWN0O1xuXG4gIC8qKlxuICAgKiBUaGUgZW52aXJvbm1lbnQgdG8gZGVwbG95IHRoaXMgc3RhY2sgaW5cbiAgICpcbiAgICogVGhlIGVudmlyb25tZW50IG9uIHRoZSBzdGFjayBhcnRpZmFjdCBtYXkgYmUgdW5yZXNvbHZlZCwgdGhpcyBvbmVcbiAgICogbXVzdCBiZSByZXNvbHZlZC5cbiAgICovXG4gIHJlYWRvbmx5IHJlc29sdmVkRW52aXJvbm1lbnQ6IGN4YXBpLkVudmlyb25tZW50O1xuXG4gIC8qKlxuICAgKiBUaGUgU0RLIHRvIHVzZSBmb3IgZGVwbG95aW5nIHRoZSBzdGFja1xuICAgKlxuICAgKiBTaG91bGQgaGF2ZSBiZWVuIGluaXRpYWxpemVkIHdpdGggdGhlIGNvcnJlY3Qgcm9sZSB3aXRoIHdoaWNoXG4gICAqIHN0YWNrIG9wZXJhdGlvbnMgc2hvdWxkIGJlIHBlcmZvcm1lZC5cbiAgICovXG4gIHJlYWRvbmx5IHNkazogSVNESztcblxuICAvKipcbiAgICogU0RLIHByb3ZpZGVyIChzZWVkZWQgd2l0aCBkZWZhdWx0IGNyZWRlbnRpYWxzKVxuICAgKlxuICAgKiBXaWxsIGV4Y2x1c2l2ZWx5IGJlIHVzZWQgdG8gYXNzdW1lIHB1Ymxpc2hpbmcgY3JlZGVudGlhbHMgKHdoaWNoIG11c3RcbiAgICogc3RhcnQgb3V0IGZyb20gY3VycmVudCBjcmVkZW50aWFscyByZWdhcmRsZXNzIG9mIHdoZXRoZXIgd2UndmUgYXNzdW1lZCBhblxuICAgKiBhY3Rpb24gcm9sZSB0byB0b3VjaCB0aGUgc3RhY2sgb3Igbm90KS5cbiAgICpcbiAgICogVXNlZCBmb3IgdGhlIGZvbGxvd2luZyBwdXJwb3NlczpcbiAgICpcbiAgICogLSBQdWJsaXNoIGxlZ2FjeSBhc3NldHMuXG4gICAqIC0gVXBsb2FkIGxhcmdlIENsb3VkRm9ybWF0aW9uIHRlbXBsYXRlcyB0byB0aGUgc3RhZ2luZyBidWNrZXQuXG4gICAqL1xuICByZWFkb25seSBzZGtQcm92aWRlcjogU2RrUHJvdmlkZXI7XG5cbiAgLyoqXG4gICAqIEluZm9ybWF0aW9uIGFib3V0IHRoZSBib290c3RyYXAgc3RhY2sgZm91bmQgaW4gdGhlIHRhcmdldCBlbnZpcm9ubWVudFxuICAgKi9cbiAgcmVhZG9ubHkgZW52UmVzb3VyY2VzOiBFbnZpcm9ubWVudFJlc291cmNlcztcblxuICAvKipcbiAgICogUm9sZSB0byBwYXNzIHRvIENsb3VkRm9ybWF0aW9uIHRvIGV4ZWN1dGUgdGhlIGNoYW5nZSBzZXRcbiAgICpcbiAgICogQGRlZmF1bHQgLSBSb2xlIHNwZWNpZmllZCBvbiBzdGFjaywgb3RoZXJ3aXNlIGN1cnJlbnRcbiAgICovXG4gIHJlYWRvbmx5IHJvbGVBcm4/OiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIE5vdGlmaWNhdGlvbiBBUk5zIHRvIHBhc3MgdG8gQ2xvdWRGb3JtYXRpb2