@aws-solutions-constructs/core
Version:
Core CDK Construct for patterns library
390 lines • 59.3 kB
JavaScript
"use strict";
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
* with the License. A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
* OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildSagemakerNotebook = buildSagemakerNotebook;
exports.BuildSagemakerEndpoint = BuildSagemakerEndpoint;
exports.deploySagemakerEndpoint = deploySagemakerEndpoint;
exports.createSagemakerModel = createSagemakerModel;
exports.createSagemakerEndpointConfig = createSagemakerEndpointConfig;
exports.createSagemakerEndpoint = createSagemakerEndpoint;
exports.CheckSagemakerProps = CheckSagemakerProps;
/*
* The functions found here in the core library are for internal use and can be changed
* or removed outside of a major release. We recommend against calling them directly from client code.
*/
const sagemaker = require("aws-cdk-lib/aws-sagemaker");
const ec2 = require("aws-cdk-lib/aws-ec2");
const kms_helper_1 = require("./kms-helper");
const sagemaker_defaults_1 = require("./sagemaker-defaults");
const cdk = require("aws-cdk-lib");
const utils_1 = require("./utils");
const vpc_helper_1 = require("./vpc-helper");
const iam = require("aws-cdk-lib/aws-iam");
const aws_cdk_lib_1 = require("aws-cdk-lib");
const vpc_defaults_1 = require("./vpc-defaults");
const security_group_helper_1 = require("./security-group-helper");
function addPermissions(role, props) {
// Grant permissions to NoteBookInstance for creating and training the model
role.addToPolicy(new iam.PolicyStatement({
resources: [`arn:${aws_cdk_lib_1.Aws.PARTITION}:sagemaker:${aws_cdk_lib_1.Aws.REGION}:${aws_cdk_lib_1.Aws.ACCOUNT_ID}:*`],
actions: [
'sagemaker:CreateTrainingJob',
'sagemaker:DescribeTrainingJob',
'sagemaker:CreateModel',
'sagemaker:DescribeModel',
'sagemaker:DeleteModel',
'sagemaker:CreateEndpoint',
'sagemaker:CreateEndpointConfig',
'sagemaker:DescribeEndpoint',
'sagemaker:DescribeEndpointConfig',
'sagemaker:DeleteEndpoint',
'sagemaker:DeleteEndpointConfig',
'sagemaker:InvokeEndpoint',
],
}));
// Grant CloudWatch Logging permissions
role.addToPolicy(new iam.PolicyStatement({
resources: [`arn:${cdk.Aws.PARTITION}:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:log-group:/aws/sagemaker/*`],
actions: [
'logs:CreateLogGroup',
'logs:CreateLogStream',
'logs:DescribeLogStreams',
'logs:GetLogEvents',
'logs:PutLogEvents',
],
}));
// To place the Sagemaker endpoint in a VPC
if (props && props.vpc) {
role.addToPolicy(new iam.PolicyStatement({
resources: ['*'],
actions: [
'ec2:CreateNetworkInterface',
'ec2:CreateNetworkInterfacePermission',
'ec2:DeleteNetworkInterface',
'ec2:DeleteNetworkInterfacePermission',
'ec2:DescribeNetworkInterfaces',
'ec2:AssignPrivateIpAddresses',
'ec2:UnassignPrivateIpAddresses',
'ec2:DescribeVpcs',
'ec2:DescribeDhcpOptions',
'ec2:DescribeSubnets',
'ec2:DescribeSecurityGroups',
],
}));
}
// To create a Sagemaker model using Bring-Your-Own-Model (BYOM) algorithm image
// The image URL is specified in the modelProps
role.addToPolicy(new iam.PolicyStatement({
resources: [`arn:${cdk.Aws.PARTITION}:ecr:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:repository/*`],
actions: [
'ecr:BatchCheckLayerAvailability',
'ecr:GetDownloadUrlForLayer',
'ecr:DescribeRepositories',
'ecr:DescribeImages',
'ecr:BatchGetImage',
],
}));
// Add GetAuthorizationToken (it can not be bound to resources other than *)
role.addToPolicy(new iam.PolicyStatement({
resources: ['*'],
actions: ['ecr:GetAuthorizationToken'],
}));
// add permission to use Elastic Inference accelerator
if (props && props.endpointConfigProps) {
// Get the acceleratorType, if any
const acceleratorType = (props.endpointConfigProps
?.productionVariants)[0].acceleratorType;
if (acceleratorType !== undefined) {
role.addToPolicy(new iam.PolicyStatement({
resources: ['*'],
actions: ['elastic-inference:Connect'],
}));
}
}
// add kms permissions
role.addToPolicy(new iam.PolicyStatement({
// the kmsKeyId in the endpointConfigProps can be any of the following formats:
// Key ID: 1234abcd-12ab-34cd-56ef-1234567890ab
// Key ARN: arn:aws:kms:<region>:<accountID>:key/1234abcd-12ab-34cd-56ef-1234567890ab
// Alias name: alias/ExampleAlias
// Alias name ARN: arn:aws:kms:<region>:<accountID>:alias/ExampleAlias
// the key is used to encrypt/decrypt data captured by the Sagemaker endpoint and stored in S3 bucket
resources: [
`arn:${cdk.Aws.PARTITION}:kms:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:key/*`,
`arn:${cdk.Aws.PARTITION}:kms:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:alias/*`,
],
actions: ['kms:Encrypt', 'kms:Decrypt', 'kms:ReEncrypt*', 'kms:GenerateDataKey*', 'kms:DescribeKey'],
}));
// Add S3 permissions to get Model artifact, put data capture files, etc.
role.addToPolicy(new iam.PolicyStatement({
actions: ['s3:GetObject', 's3:PutObject', 's3:DeleteObject', 's3:ListBucket'],
resources: [`arn:aws:s3:::*`],
}));
// Grant GetRole permissions to the Sagemaker service
role.addToPolicy(new iam.PolicyStatement({
resources: [role.roleArn],
actions: ['iam:GetRole'],
}));
// Grant PassRole permissions to the Sagemaker service
role.addToPolicy(new iam.PolicyStatement({
resources: [role.roleArn],
actions: ['iam:PassRole'],
conditions: {
StringLike: { 'iam:PassedToService': 'sagemaker.amazonaws.com' },
},
}));
// Add CFN NAG uppress to allow for "Resource": "*" for ENI access in VPC,
// ECR authorization token for custom model images, and elastic inference
// Add CFN NAG for Complex Role because Sagmaker needs permissions to access several services
const roleDefaultPolicy = role.node.tryFindChild('DefaultPolicy')?.node.findChild('Resource');
(0, utils_1.addCfnSuppressRules)(roleDefaultPolicy, [
{
id: 'W12',
reason: `Sagemaker needs the following minimum required permissions to access ENIs in a VPC, ECR for custom model images, and elastic inference.`,
},
{
id: 'W76',
reason: 'Complex role becuase Sagemaker needs permissions to access several services',
}
]);
(0, utils_1.addCfnGuardSuppressRules)(roleDefaultPolicy, ["IAM_POLICY_NON_COMPLIANT_ARN"]);
}
/**
* @internal This is an internal core function and should not be called directly by Solutions Constructs clients.
*
* The constructs deploying Sagemaker services only deploy Sagemaker Endpoints, Endpoint
* Configurations and (already developed) Models. There are no Constructs that deploy Notebooks.
* There doesn't appear to be any references to this function outside the unit test file - so
* this function is unneccessary.
*/
function buildSagemakerNotebook(scope, id, props) {
// Setup the notebook properties
let sagemakerNotebookProps;
let vpcInstance;
let securityGroup;
let kmsKeyId;
let subnetId;
// Conditional Sagemaker Notebook creation
if (props.existingNotebookObj) {
return { notebook: props.existingNotebookObj };
}
if (CheckNotebookVpcProps(props)) {
throw new Error('Must define both sagemakerNotebookProps.subnetId and sagemakerNotebookProps.securityGroupIds');
}
addPermissions(props.role);
if (props.sagemakerNotebookProps?.kmsKeyId === undefined) {
kmsKeyId = (0, kms_helper_1.buildEncryptionKey)(scope, id).keyId;
}
else {
kmsKeyId = props.sagemakerNotebookProps.kmsKeyId;
}
if (props.deployInsideVpc === undefined || props.deployInsideVpc) {
if (props.sagemakerNotebookProps?.subnetId === undefined &&
props.sagemakerNotebookProps?.securityGroupIds === undefined) {
vpcInstance = (0, vpc_helper_1.buildVpc)(scope, {
defaultVpcProps: (0, vpc_defaults_1.DefaultPublicPrivateVpcProps)(),
});
securityGroup = (0, security_group_helper_1.buildSecurityGroup)(scope, 'SecurityGroup', {
vpc: vpcInstance,
allowAllOutbound: false,
}, [], [{ peer: ec2.Peer.anyIpv4(), connection: ec2.Port.tcp(443) }]);
subnetId = vpcInstance.privateSubnets[0].subnetId;
sagemakerNotebookProps = (0, sagemaker_defaults_1.DefaultSagemakerNotebookProps)(props.role.roleArn, kmsKeyId, subnetId, [
securityGroup.securityGroupId,
]);
}
else {
sagemakerNotebookProps = (0, sagemaker_defaults_1.DefaultSagemakerNotebookProps)(props.role.roleArn, kmsKeyId, props.sagemakerNotebookProps?.subnetId, props.sagemakerNotebookProps?.securityGroupIds);
}
}
else {
sagemakerNotebookProps = (0, sagemaker_defaults_1.DefaultSagemakerNotebookProps)(props.role.roleArn, kmsKeyId);
}
sagemakerNotebookProps = (0, utils_1.consolidateProps)(sagemakerNotebookProps, props.sagemakerNotebookProps);
// Create the notebook
// NOSONAR: (typescript:S6319)
// keyID is created above in the if (props.sagemakerNotebookProps?.kmsKeyId === undefined)
// block. It is then passed to DefaultSagemakerNotebookProps()
// This behavior is validated in unit test
const sagemakerInstance = new sagemaker.CfnNotebookInstance(scope, 'SagemakerNotebook', sagemakerNotebookProps // NOSONAR
);
if (vpcInstance) {
return { notebook: sagemakerInstance, vpc: vpcInstance, securityGroup };
}
else {
return { notebook: sagemakerInstance };
}
}
/**
* @internal This is an internal core function and should not be called directly by Solutions Constructs clients.
*/
function CheckNotebookVpcProps(props) {
if ((props.sagemakerNotebookProps?.subnetId && props.sagemakerNotebookProps?.securityGroupIds === undefined) ||
(props.sagemakerNotebookProps?.subnetId === undefined && props.sagemakerNotebookProps?.securityGroupIds)) {
return true;
}
return false;
}
/**
* @internal This is an internal core function and should not be called directly by Solutions Constructs clients.
*/
function BuildSagemakerEndpoint(scope, id, props) {
/** Conditional Sagemaker endpoint creation */
if (!props.existingSagemakerEndpointObj) {
if (props.modelProps) {
const deploySagemakerEndpointResponse = deploySagemakerEndpoint(scope, id, props);
return { ...deploySagemakerEndpointResponse };
}
else {
throw Error('Either existingSagemakerEndpointObj or at least modelProps is required');
}
}
else {
/** Otherwise, return [endpoint] */
return { endpoint: props.existingSagemakerEndpointObj };
}
}
/**
* @internal This is an internal core function and should not be called directly by Solutions Constructs clients.
*/
function deploySagemakerEndpoint(scope, id, props) {
let model;
let endpointConfig;
let endpoint;
let sagemakerRole;
// Create Sagemaker's model, endpointConfig, and endpoint
if (props.modelProps) {
// Check if the client has provided executionRoleArn
if (props.modelProps.executionRoleArn) {
sagemakerRole = iam.Role.fromRoleArn(scope, 'SagemakerRoleCustomer', props.modelProps.executionRoleArn);
}
else {
// Create the Sagemaker Role
sagemakerRole = new iam.Role(scope, 'SagemakerRole', {
assumedBy: new iam.ServicePrincipal('sagemaker.amazonaws.com'),
});
// Add required permissions
addPermissions(sagemakerRole, props);
(0, utils_1.addCfnGuardSuppressRules)(sagemakerRole, ["IAM_NO_INLINE_POLICY_CHECK"]);
}
// Create Sagemaker Model
model = createSagemakerModel(scope, props.modelProps, sagemakerRole, props.vpc);
// Create Sagemaker EndpointConfig
endpointConfig = createSagemakerEndpointConfig(scope, `${id}`, model.attrModelName, props.endpointConfigProps);
// Add dependency on model
endpointConfig.addDependency(model);
// Create Sagemaker Endpoint
endpoint = createSagemakerEndpoint(scope, endpointConfig.attrEndpointConfigName, props.endpointProps);
// Add dependency on EndpointConfig
endpoint.addDependency(endpointConfig);
return { endpoint, endpointConfig, model };
}
else {
throw Error('You need to provide at least modelProps to create Sagemaker Endpoint');
}
}
/**
* @internal This is an internal core function and should not be called directly by Solutions Constructs clients.
*/
function createSagemakerModel(scope, modelProps, role, vpc) {
let finalModelProps;
let primaryContainer;
let vpcConfig;
let model;
if (vpc) {
const modelDefaultSecurityGroup = new ec2.SecurityGroup(scope, 'ReplaceModelDefaultSecurityGroup', {
vpc,
allowAllOutbound: true,
});
// Allow https traffic from within the VPC
modelDefaultSecurityGroup.addIngressRule(ec2.Peer.ipv4(vpc.vpcCidrBlock), ec2.Port.tcp(443));
const cfnSecurityGroup = modelDefaultSecurityGroup.node.findChild('Resource');
(0, utils_1.addCfnSuppressRules)(cfnSecurityGroup, [
{
id: 'W5',
reason: 'Egress of 0.0.0.0/0 is default and generally considered OK',
},
{
id: 'W40',
reason: 'Egress IPProtocol of -1 is default and generally considered OK',
}
]);
// Throw an error if the VPC does not contain private or isolated subnets
if (vpc.privateSubnets.length === 0 && vpc.isolatedSubnets.length === 0) {
throw Error('VPC must contain private or isolated subnets to deploy the Sagemaker endpoint in a vpc');
}
vpcConfig = {
// default SubnetType.PRIVATE (or ISOLATED or PUBLIC if there are no PRIVATE subnets)
// So, private subnets will be used if provided by customer. Otherwise, use the default isolated subnets,
subnets: vpc.selectSubnets({
onePerAz: true,
}).subnetIds,
securityGroupIds: [modelDefaultSecurityGroup.securityGroupId],
};
}
if (modelProps.primaryContainer) {
// Get user provided Model's primary container
primaryContainer = modelProps.primaryContainer;
// Get default Model props
finalModelProps = (0, utils_1.consolidateProps)((0, sagemaker_defaults_1.DefaultSagemakerModelProps)(role.roleArn, primaryContainer, vpcConfig), modelProps);
// Create the Sagemaker's Model
model = new sagemaker.CfnModel(scope, 'SagemakerModel', finalModelProps);
// Add dependency on the Sagemaker's role
model.node.addDependency(role);
return model;
}
else {
throw Error('You need to provide at least primaryContainer to create Sagemaker Model');
}
}
/**
* @internal This is an internal core function and should not be called directly by Solutions Constructs clients.
*/
function createSagemakerEndpointConfig(scope, id, modelName, endpointConfigProps) {
let kmsKeyId;
// Create encryption key if one is not provided
if (endpointConfigProps && endpointConfigProps.kmsKeyId) {
kmsKeyId = endpointConfigProps.kmsKeyId;
}
else {
kmsKeyId = (0, kms_helper_1.buildEncryptionKey)(scope, id).keyId;
}
// Overwrite default EndpointConfig properties
const finalEndpointConfigProps = (0, utils_1.consolidateProps)((0, sagemaker_defaults_1.DefaultSagemakerEndpointConfigProps)(modelName, kmsKeyId), endpointConfigProps);
// Create the Sagemaker's EndpointConfig
const endpointConfig = new sagemaker.CfnEndpointConfig(scope, 'SagemakerEndpointConfig', finalEndpointConfigProps);
return endpointConfig;
}
/**
* @internal This is an internal core function and should not be called directly by Solutions Constructs clients.
*/
function createSagemakerEndpoint(scope, endpointConfigName, endpointProps) {
// Overwrite default Endpoint properties
const finalEndpointProps = (0, utils_1.consolidateProps)((0, sagemaker_defaults_1.DefaultSagemakerEndpointProps)(endpointConfigName), endpointProps);
// Create the Sagemaker's Endpoint
const endpoint = new sagemaker.CfnEndpoint(scope, 'SagemakerEndpoint', finalEndpointProps);
return endpoint;
}
function CheckSagemakerProps(propsObject) {
let errorMessages = '';
let errorFound = false;
if (propsObject.existingSagemakerEndpointObj && propsObject.endpointProps) {
errorMessages += 'Error - Either provide endpointProps or existingSagemakerEndpointObj, but not both.\n';
errorFound = true;
}
if (errorFound) {
throw new Error(errorMessages);
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2FnZW1ha2VyLWhlbHBlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInNhZ2VtYWtlci1oZWxwZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7Ozs7OztHQVdHOztBQWlPSCx3REFrRkM7QUF3REQsd0RBaUJDO0FBV0QsMERBNENDO0FBS0Qsb0RBOERDO0FBS0Qsc0VBdUJDO0FBS0QsMERBYUM7QUFPRCxrREFZQztBQXJqQkQ7OztHQUdHO0FBRUgsdURBQXVEO0FBQ3ZELDJDQUEyQztBQUMzQyw2Q0FBa0Q7QUFDbEQsNkRBSzhCO0FBQzlCLG1DQUFtQztBQUNuQyxtQ0FBMEY7QUFDMUYsNkNBQXdDO0FBQ3hDLDJDQUEyQztBQUMzQyw2Q0FBa0M7QUFDbEMsaURBQThEO0FBQzlELG1FQUE2RDtBQWdDN0QsU0FBUyxjQUFjLENBQUMsSUFBYyxFQUFFLEtBQW1DO0lBQ3pFLDRFQUE0RTtJQUM1RSxJQUFJLENBQUMsV0FBVyxDQUNkLElBQUksR0FBRyxDQUFDLGVBQWUsQ0FBQztRQUN0QixTQUFTLEVBQUUsQ0FBQyxPQUFPLGlCQUFHLENBQUMsU0FBUyxjQUFjLGlCQUFHLENBQUMsTUFBTSxJQUFJLGlCQUFHLENBQUMsVUFBVSxJQUFJLENBQUM7UUFDL0UsT0FBTyxFQUFFO1lBQ1AsNkJBQTZCO1lBQzdCLCtCQUErQjtZQUMvQix1QkFBdUI7WUFDdkIseUJBQXlCO1lBQ3pCLHVCQUF1QjtZQUN2QiwwQkFBMEI7WUFDMUIsZ0NBQWdDO1lBQ2hDLDRCQUE0QjtZQUM1QixrQ0FBa0M7WUFDbEMsMEJBQTBCO1lBQzFCLGdDQUFnQztZQUNoQywwQkFBMEI7U0FDM0I7S0FDRixDQUFDLENBQ0gsQ0FBQztJQUVGLHVDQUF1QztJQUN2QyxJQUFJLENBQUMsV0FBVyxDQUNkLElBQUksR0FBRyxDQUFDLGVBQWUsQ0FBQztRQUN0QixTQUFTLEVBQUUsQ0FBQyxPQUFPLEdBQUcsQ0FBQyxHQUFHLENBQUMsU0FBUyxTQUFTLEdBQUcsQ0FBQyxHQUFHLENBQUMsTUFBTSxJQUFJLEdBQUcsQ0FBQyxHQUFHLENBQUMsVUFBVSw2QkFBNkIsQ0FBQztRQUMvRyxPQUFPLEVBQUU7WUFDUCxxQkFBcUI7WUFDckIsc0JBQXNCO1lBQ3RCLHlCQUF5QjtZQUN6QixtQkFBbUI7WUFDbkIsbUJBQW1CO1NBQ3BCO0tBQ0YsQ0FBQyxDQUNILENBQUM7SUFFRiwyQ0FBMkM7SUFDM0MsSUFBSSxLQUFLLElBQUksS0FBSyxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxXQUFXLENBQ2QsSUFBSSxHQUFHLENBQUMsZUFBZSxDQUFDO1lBQ3RCLFNBQVMsRUFBRSxDQUFDLEdBQUcsQ0FBQztZQUNoQixPQUFPLEVBQUU7Z0JBQ1AsNEJBQTRCO2dCQUM1QixzQ0FBc0M7Z0JBQ3RDLDRCQUE0QjtnQkFDNUIsc0NBQXNDO2dCQUN0QywrQkFBK0I7Z0JBQy9CLDhCQUE4QjtnQkFDOUIsZ0NBQWdDO2dCQUNoQyxrQkFBa0I7Z0JBQ2xCLHlCQUF5QjtnQkFDekIscUJBQXFCO2dCQUNyQiw0QkFBNEI7YUFDN0I7U0FDRixDQUFDLENBQ0gsQ0FBQztJQUNKLENBQUM7SUFFRCxnRkFBZ0Y7SUFDaEYsK0NBQStDO0lBQy9DLElBQUksQ0FBQyxXQUFXLENBQ2QsSUFBSSxHQUFHLENBQUMsZUFBZSxDQUFDO1FBQ3RCLFNBQVMsRUFBRSxDQUFDLE9BQU8sR0FBRyxDQUFDLEdBQUcsQ0FBQyxTQUFTLFFBQVEsR0FBRyxDQUFDLEdBQUcsQ0FBQyxNQUFNLElBQUksR0FBRyxDQUFDLEdBQUcsQ0FBQyxVQUFVLGVBQWUsQ0FBQztRQUNoRyxPQUFPLEVBQUU7WUFDUCxpQ0FBaUM7WUFDakMsNEJBQTRCO1lBQzVCLDBCQUEwQjtZQUMxQixvQkFBb0I7WUFDcEIsbUJBQW1CO1NBQ3BCO0tBQ0YsQ0FBQyxDQUNILENBQUM7SUFFRiw0RUFBNEU7SUFDNUUsSUFBSSxDQUFDLFdBQVcsQ0FDZCxJQUFJLEdBQUcsQ0FBQyxlQUFlLENBQUM7UUFDdEIsU0FBUyxFQUFFLENBQUMsR0FBRyxDQUFDO1FBQ2hCLE9BQU8sRUFBRSxDQUFDLDJCQUEyQixDQUFDO0tBQ3ZDLENBQUMsQ0FDSCxDQUFDO0lBRUYsc0RBQXNEO0lBQ3RELElBQUksS0FBSyxJQUFJLEtBQUssQ0FBQyxtQkFBbUIsRUFBRSxDQUFDO1FBQ3ZDLGtDQUFrQztRQUNsQyxNQUFNLGVBQWUsR0FBRyxDQUFDLEtBQUssQ0FBQyxtQkFBbUI7WUFDaEQsRUFBRSxrQkFBOEUsQ0FBQSxDQUFDLENBQUMsQ0FBQyxDQUFDLGVBQWUsQ0FBQztRQUN0RyxJQUFJLGVBQWUsS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUNsQyxJQUFJLENBQUMsV0FBVyxDQUNkLElBQUksR0FBRyxDQUFDLGVBQWUsQ0FBQztnQkFDdEIsU0FBUyxFQUFFLENBQUMsR0FBRyxDQUFDO2dCQUNoQixPQUFPLEVBQUUsQ0FBQywyQkFBMkIsQ0FBQzthQUN2QyxDQUFDLENBQ0gsQ0FBQztRQUNKLENBQUM7SUFDSCxDQUFDO0lBRUQsc0JBQXNCO0lBQ3RCLElBQUksQ0FBQyxXQUFXLENBQ2QsSUFBSSxHQUFHLENBQUMsZUFBZSxDQUFDO1FBQ3RCLCtFQUErRTtRQUMvRSwrQ0FBK0M7UUFDL0MscUZBQXFGO1FBQ3JGLGlDQUFpQztRQUNqQyxzRUFBc0U7UUFDdEUscUdBQXFHO1FBQ3JHLFNBQVMsRUFBRTtZQUNULE9BQU8sR0FBRyxDQUFDLEdBQUcsQ0FBQyxTQUFTLFFBQVEsR0FBRyxDQUFDLEdBQUcsQ0FBQyxNQUFNLElBQUksR0FBRyxDQUFDLEdBQUcsQ0FBQyxVQUFVLFFBQVE7WUFDNUUsT0FBTyxHQUFHLENBQUMsR0FBRyxDQUFDLFNBQVMsUUFBUSxHQUFHLENBQUMsR0FBRyxDQUFDLE1BQU0sSUFBSSxHQUFHLENBQUMsR0FBRyxDQUFDLFVBQVUsVUFBVTtTQUMvRTtRQUNELE9BQU8sRUFBRSxDQUFDLGFBQWEsRUFBRSxhQUFhLEVBQUUsZ0JBQWdCLEVBQUUsc0JBQXNCLEVBQUUsaUJBQWlCLENBQUM7S0FDckcsQ0FBQyxDQUNILENBQUM7SUFFRix5RUFBeUU7SUFDekUsSUFBSSxDQUFDLFdBQVcsQ0FDZCxJQUFJLEdBQUcsQ0FBQyxlQUFlLENBQUM7UUFDdEIsT0FBTyxFQUFFLENBQUMsY0FBYyxFQUFFLGNBQWMsRUFBRSxpQkFBaUIsRUFBRSxlQUFlLENBQUM7UUFDN0UsU0FBUyxFQUFFLENBQUMsZ0JBQWdCLENBQUM7S0FDOUIsQ0FBQyxDQUNILENBQUM7SUFFRixxREFBcUQ7SUFDckQsSUFBSSxDQUFDLFdBQVcsQ0FDZCxJQUFJLEdBQUcsQ0FBQyxlQUFlLENBQUM7UUFDdEIsU0FBUyxFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQztRQUN6QixPQUFPLEVBQUUsQ0FBQyxhQUFhLENBQUM7S0FDekIsQ0FBQyxDQUNILENBQUM7SUFFRixzREFBc0Q7SUFDdEQsSUFBSSxDQUFDLFdBQVcsQ0FDZCxJQUFJLEdBQUcsQ0FBQyxlQUFlLENBQUM7UUFDdEIsU0FBUyxFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQztRQUN6QixPQUFPLEVBQUUsQ0FBQyxjQUFjLENBQUM7UUFDekIsVUFBVSxFQUFFO1lBQ1YsVUFBVSxFQUFFLEVBQUUscUJBQXFCLEVBQUUseUJBQXlCLEVBQUU7U0FDakU7S0FDRixDQUFDLENBQ0gsQ0FBQztJQUVGLDBFQUEwRTtJQUMxRSx5RUFBeUU7SUFDekUsNkZBQTZGO0lBQzdGLE1BQU0saUJBQWlCLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsZUFBZSxDQUFDLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxVQUFVLENBQWtCLENBQUM7SUFDL0csSUFBQSwyQkFBbUIsRUFBQyxpQkFBaUIsRUFBRTtRQUNyQztZQUNFLEVBQUUsRUFBRSxLQUFLO1lBQ1QsTUFBTSxFQUFFLHlJQUF5STtTQUNsSjtRQUNEO1lBQ0UsRUFBRSxFQUFFLEtBQUs7WUFDVCxNQUFNLEVBQUUsNkVBQTZFO1NBQ3RGO0tBQ0YsQ0FBQyxDQUFDO0lBQ0gsSUFBQSxnQ0FBd0IsRUFBQyxpQkFBaUIsRUFBRSxDQUFDLDhCQUE4QixDQUFDLENBQUMsQ0FBQztBQUNoRixDQUFDO0FBUUQ7Ozs7Ozs7R0FPRztBQUNILFNBQWdCLHNCQUFzQixDQUNwQyxLQUFnQixFQUNoQixFQUFVLEVBQ1YsS0FBa0M7SUFFbEMsZ0NBQWdDO0lBQ2hDLElBQUksc0JBQXNCLENBQUM7SUFDM0IsSUFBSSxXQUFXLENBQUM7SUFDaEIsSUFBSSxhQUFhLENBQUM7SUFDbEIsSUFBSSxRQUFnQixDQUFDO0lBQ3JCLElBQUksUUFBZ0IsQ0FBQztJQUVyQiwwQ0FBMEM7SUFDMUMsSUFBSSxLQUFLLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztRQUM5QixPQUFPLEVBQUUsUUFBUSxFQUFFLEtBQUssQ0FBQyxtQkFBbUIsRUFBRSxDQUFDO0lBQ2pELENBQUM7SUFFRCxJQUFJLHFCQUFxQixDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDakMsTUFBTSxJQUFJLEtBQUssQ0FBQyw4RkFBOEYsQ0FBQyxDQUFDO0lBQ2xILENBQUM7SUFFRCxjQUFjLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBRTNCLElBQUksS0FBSyxDQUFDLHNCQUFzQixFQUFFLFFBQVEsS0FBSyxTQUFTLEVBQUUsQ0FBQztRQUN6RCxRQUFRLEdBQUcsSUFBQSwrQkFBa0IsRUFBQyxLQUFLLEVBQUcsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDO0lBQ2xELENBQUM7U0FBTSxDQUFDO1FBQ04sUUFBUSxHQUFHLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQyxRQUFRLENBQUM7SUFDbkQsQ0FBQztJQUVELElBQUksS0FBSyxDQUFDLGVBQWUsS0FBSyxTQUFTLElBQUksS0FBSyxDQUFDLGVBQWUsRUFBRSxDQUFDO1FBQ2pFLElBQ0UsS0FBSyxDQUFDLHNCQUFzQixFQUFFLFFBQVEsS0FBSyxTQUFTO1lBQ3BELEtBQUssQ0FBQyxzQkFBc0IsRUFBRSxnQkFBZ0IsS0FBSyxTQUFTLEVBQzVELENBQUM7WUFDRCxXQUFXLEdBQUcsSUFBQSxxQkFBUSxFQUFDLEtBQUssRUFBRTtnQkFDNUIsZUFBZSxFQUFFLElBQUEsMkNBQTRCLEdBQUU7YUFDaEQsQ0FBQyxDQUFDO1lBQ0gsYUFBYSxHQUFHLElBQUEsMENBQWtCLEVBQ2hDLEtBQUssRUFDTCxlQUFlLEVBQ2Y7Z0JBQ0UsR0FBRyxFQUFFLFdBQVc7Z0JBQ2hCLGdCQUFnQixFQUFFLEtBQUs7YUFDeEIsRUFDRCxFQUFFLEVBQ0YsQ0FBQyxFQUFFLElBQUksRUFBRSxHQUFHLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxFQUFFLFVBQVUsRUFBRSxHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQzlELENBQUM7WUFFRixRQUFRLEdBQUcsV0FBVyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUM7WUFFbEQsc0JBQXNCLEdBQUcsSUFBQSxrREFBNkIsRUFBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxRQUFRLEVBQUUsUUFBUSxFQUFFO2dCQUM3RixhQUFhLENBQUMsZUFBZTthQUM5QixDQUFDLENBQUM7UUFDTCxDQUFDO2FBQU0sQ0FBQztZQUNOLHNCQUFzQixHQUFHLElBQUEsa0RBQTZCLEVBQ3BELEtBQUssQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUNsQixRQUFRLEVBQ1IsS0FBSyxDQUFDLHNCQUFzQixFQUFFLFFBQVEsRUFDdEMsS0FBSyxDQUFDLHNCQUFzQixFQUFFLGdCQUFnQixDQUMvQyxDQUFDO1FBQ0osQ0FBQztJQUNILENBQUM7U0FBTSxDQUFDO1FBQ04sc0JBQXNCLEdBQUcsSUFBQSxrREFBNkIsRUFBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxRQUFRLENBQUMsQ0FBQztJQUN2RixDQUFDO0lBRUQsc0JBQXNCLEdBQUcsSUFBQSx3QkFBZ0IsRUFBQyxzQkFBc0IsRUFBRSxLQUFLLENBQUMsc0JBQXNCLENBQUMsQ0FBQztJQUVoRyxzQkFBc0I7SUFDdEIsOEJBQThCO0lBQzlCLDBGQUEwRjtJQUMxRiw4REFBOEQ7SUFDOUQsMENBQTBDO0lBQzFDLE1BQU0saUJBQWlCLEdBQWtDLElBQUksU0FBUyxDQUFDLG1CQUFtQixDQUN4RixLQUFLLEVBQ0wsbUJBQW1CLEVBQ25CLHNCQUFzQixDQUFDLFVBQVU7S0FDbEMsQ0FBQztJQUNGLElBQUksV0FBVyxFQUFFLENBQUM7UUFDaEIsT0FBTyxFQUFFLFFBQVEsRUFBRSxpQkFBaUIsRUFBRSxHQUFHLEVBQUUsV0FBVyxFQUFFLGFBQWEsRUFBRSxDQUFDO0lBQzFFLENBQUM7U0FBTSxDQUFDO1FBQ04sT0FBTyxFQUFFLFFBQVEsRUFBRSxpQkFBaUIsRUFBRSxDQUFDO0lBQ3pDLENBQUM7QUFDSCxDQUFDO0FBRUQ7O0dBRUc7QUFDSCxTQUFTLHFCQUFxQixDQUFDLEtBQWtDO0lBQy9ELElBQUksQ0FBQyxLQUFLLENBQUMsc0JBQXNCLEVBQUUsUUFBUSxJQUFJLEtBQUssQ0FBQyxzQkFBc0IsRUFBRSxnQkFBZ0IsS0FBSyxTQUFTLENBQUM7UUFDMUcsQ0FBQyxLQUFLLENBQUMsc0JBQXNCLEVBQUUsUUFBUSxLQUFLLFNBQVMsSUFBSSxLQUFLLENBQUMsc0JBQXNCLEVBQUUsZ0JBQWdCLENBQUMsRUFDeEcsQ0FBQztRQUNELE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUNELE9BQU8sS0FBSyxDQUFDO0FBQ2YsQ0FBQztBQXlDRDs7R0FFRztBQUNILFNBQWdCLHNCQUFzQixDQUNwQyxLQUFnQixFQUNoQixFQUFVLEVBQ1YsS0FBa0M7SUFFbEMsOENBQThDO0lBQzlDLElBQUksQ0FBQyxLQUFLLENBQUMsNEJBQTRCLEVBQUUsQ0FBQztRQUN4QyxJQUFJLEtBQUssQ0FBQyxVQUFVLEVBQUUsQ0FBQztZQUNyQixNQUFNLCtCQUErQixHQUFHLHVCQUF1QixDQUFDLEtBQUssRUFBRSxFQUFFLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFDbEYsT0FBTyxFQUFFLEdBQUcsK0JBQStCLEVBQUUsQ0FBQztRQUNoRCxDQUFDO2FBQU0sQ0FBQztZQUNOLE1BQU0sS0FBSyxDQUFDLHdFQUF3RSxDQUFDLENBQUM7UUFDeEYsQ0FBQztJQUNILENBQUM7U0FBTSxDQUFDO1FBQ04sbUNBQW1DO1FBQ25DLE9BQU8sRUFBRSxRQUFRLEVBQUUsS0FBSyxDQUFDLDRCQUE0QixFQUFFLENBQUM7SUFDMUQsQ0FBQztBQUNILENBQUM7QUFRRDs7R0FFRztBQUNILFNBQWdCLHVCQUF1QixDQUNyQyxLQUFnQixFQUNoQixFQUFVLEVBQ1YsS0FBa0M7SUFFbEMsSUFBSSxLQUF5QixDQUFDO0lBQzlCLElBQUksY0FBMkMsQ0FBQztJQUNoRCxJQUFJLFFBQStCLENBQUM7SUFDcEMsSUFBSSxhQUF1QixDQUFDO0lBRTVCLHlEQUF5RDtJQUN6RCxJQUFJLEtBQUssQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUNyQixvREFBb0Q7UUFDcEQsSUFBSSxLQUFLLENBQUMsVUFBVSxDQUFDLGdCQUFnQixFQUFFLENBQUM7WUFDdEMsYUFBYSxHQUFHLEdBQUcsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUNsQyxLQUFLLEVBQ0wsdUJBQXVCLEVBQ3ZCLEtBQUssQ0FBQyxVQUFVLENBQUMsZ0JBQWdCLENBQ3RCLENBQUM7UUFDaEIsQ0FBQzthQUFNLENBQUM7WUFDTiw0QkFBNEI7WUFDNUIsYUFBYSxHQUFHLElBQUksR0FBRyxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsZUFBZSxFQUFFO2dCQUNuRCxTQUFTLEVBQUUsSUFBSSxHQUFHLENBQUMsZ0JBQWdCLENBQUMseUJBQXlCLENBQUM7YUFDL0QsQ0FBQyxDQUFDO1lBQ0gsMkJBQTJCO1lBQzNCLGNBQWMsQ0FBQyxhQUFhLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFDckMsSUFBQSxnQ0FBd0IsRUFBQyxhQUFhLEVBQUUsQ0FBQyw0QkFBNEIsQ0FBQyxDQUFDLENBQUM7UUFDMUUsQ0FBQztRQUVELHlCQUF5QjtRQUN6QixLQUFLLEdBQUcsb0JBQW9CLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxVQUFVLEVBQUUsYUFBYSxFQUFFLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNoRixrQ0FBa0M7UUFDbEMsY0FBYyxHQUFHLDZCQUE2QixDQUFDLEtBQUssRUFBRSxHQUFHLEVBQUUsRUFBRSxFQUFFLEtBQUssQ0FBQyxhQUFhLEVBQUUsS0FBSyxDQUFDLG1CQUFtQixDQUFDLENBQUM7UUFDL0csMEJBQTBCO1FBQzFCLGNBQWMsQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDcEMsNEJBQTRCO1FBQzVCLFFBQVEsR0FBRyx1QkFBdUIsQ0FBQyxLQUFLLEVBQUUsY0FBYyxDQUFDLHNCQUFzQixFQUFFLEtBQUssQ0FBQyxhQUFhLENBQUMsQ0FBQztRQUN0RyxtQ0FBbUM7UUFDbkMsUUFBUSxDQUFDLGFBQWEsQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUV2QyxPQUFPLEVBQUUsUUFBUSxFQUFFLGNBQWMsRUFBRSxLQUFLLEVBQUUsQ0FBQztJQUM3QyxDQUFDO1NBQU0sQ0FBQztRQUNOLE1BQU0sS0FBSyxDQUFDLHNFQUFzRSxDQUFDLENBQUM7SUFDdEYsQ0FBQztBQUNILENBQUM7QUFFRDs7R0FFRztBQUNILFNBQWdCLG9CQUFvQixDQUNsQyxLQUFnQixFQUNoQixVQUFtQyxFQUNuQyxJQUFjLEVBQ2QsR0FBYztJQUVkLElBQUksZUFBd0MsQ0FBQztJQUM3QyxJQUFJLGdCQUFnRSxDQUFDO0lBQ3JFLElBQUksU0FBMkQsQ0FBQztJQUNoRSxJQUFJLEtBQXlCLENBQUM7SUFFOUIsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUNSLE1BQU0seUJBQXlCLEdBQUcsSUFBSSxHQUFHLENBQUMsYUFBYSxDQUFDLEtBQUssRUFBRSxrQ0FBa0MsRUFBRTtZQUNqRyxHQUFHO1lBQ0gsZ0JBQWdCLEVBQUUsSUFBSTtTQUN2QixDQUFDLENBQUM7UUFFSCwwQ0FBMEM7UUFDMUMseUJBQXlCLENBQUMsY0FBYyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxZQUFZLENBQUMsRUFBRSxHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBRTdGLE1BQU0sZ0JBQWdCLEdBQUcseUJBQXlCLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxVQUFVLENBQXlCLENBQUM7UUFDdEcsSUFBQSwyQkFBbUIsRUFBQyxnQkFBZ0IsRUFBRTtZQUNwQztnQkFDRSxFQUFFLEVBQUUsSUFBSTtnQkFDUixNQUFNLEVBQUUsNERBQTREO2FBQ3JFO1lBQ0Q7Z0JBQ0UsRUFBRSxFQUFFLEtBQUs7Z0JBQ1QsTUFBTSxFQUFFLGdFQUFnRTthQUN6RTtTQUNGLENBQUMsQ0FBQztRQUVILHlFQUF5RTtRQUN6RSxJQUFJLEdBQUcsQ0FBQyxjQUFjLENBQUMsTUFBTSxLQUFLLENBQUMsSUFBSSxHQUFHLENBQUMsZUFBZSxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUN4RSxNQUFNLEtBQUssQ0FBQyx3RkFBd0YsQ0FBQyxDQUFDO1FBQ3hHLENBQUM7UUFFRCxTQUFTLEdBQUc7WUFDVixxRkFBcUY7WUFDckYseUdBQXlHO1lBQ3pHLE9BQU8sRUFBRSxHQUFHLENBQUMsYUFBYSxDQUFDO2dCQUN6QixRQUFRLEVBQUUsSUFBSTthQUNmLENBQUMsQ0FBQyxTQUFTO1lBQ1osZ0JBQWdCLEVBQUUsQ0FBQyx5QkFBeUIsQ0FBQyxlQUFlLENBQUM7U0FDOUQsQ0FBQztJQUNKLENBQUM7SUFFRCxJQUFJLFVBQVUsQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1FBQ2hDLDhDQUE4QztRQUM5QyxnQkFBZ0IsR0FBRyxVQUFVLENBQUMsZ0JBQWtFLENBQUM7UUFDakcsMEJBQTBCO1FBQzFCLGVBQWUsR0FBRyxJQUFBLHdCQUFnQixFQUFDLElBQUEsK0NBQTBCLEVBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxTQUFTLENBQUMsRUFBRSxVQUFVLENBQUMsQ0FBQztRQUV0SCwrQkFBK0I7UUFDL0IsS0FBSyxHQUFHLElBQUksU0FBUyxDQUFDLFFBQVEsQ0FBQyxLQUFLLEVBQUUsZ0JBQWdCLEVBQUUsZUFBZSxDQUFDLENBQUM7UUFDekUseUNBQXlDO1FBQ3pDLEtBQUssQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBRS9CLE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztTQUFNLENBQUM7UUFDTixNQUFNLEtBQUssQ0FBQyx5RUFBeUUsQ0FBQyxDQUFDO0lBQ3pGLENBQUM7QUFDSCxDQUFDO0FBRUQ7O0dBRUc7QUFDSCxTQUFnQiw2QkFBNkIsQ0FDM0MsS0FBZ0IsRUFDaEIsRUFBVSxFQUNWLFNBQWlCLEVBQ2pCLG1CQUFzRDtJQUV0RCxJQUFJLFFBQWdCLENBQUM7SUFFckIsK0NBQStDO0lBQy9DLElBQUksbUJBQW1CLElBQUksbUJBQW1CLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDeEQsUUFBUSxHQUFHLG1CQUFtQixDQUFDLFFBQVEsQ0FBQztJQUMxQyxDQUFDO1NBQU0sQ0FBQztRQUNOLFFBQVEsR0FBRyxJQUFBLCtCQUFrQixFQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUM7SUFDakQsQ0FBQztJQUVELDhDQUE4QztJQUM5QyxNQUFNLHdCQUF3QixHQUM1QixJQUFBLHdCQUFnQixFQUFDLElBQUEsd0RBQW1DLEVBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBQyxFQUFFLG1CQUFtQixDQUFDLENBQUM7SUFFbEcsd0NBQXdDO0lBQ3hDLE1BQU0sY0FBYyxHQUFnQyxJQUFJLFNBQVMsQ0FBQyxpQkFBaUIsQ0FBQyxLQUFLLEVBQUUseUJBQXlCLEVBQUUsd0JBQXdCLENBQUMsQ0FBQztJQUVoSixPQUFPLGNBQWMsQ0FBQztBQUN4QixDQUFDO0FBRUQ7O0dBRUc7QUFDSCxTQUFnQix1QkFBdUIsQ0FDckMsS0FBZ0IsRUFDaEIsa0JBQTBCLEVBQzFCLGFBQTBDO0lBRzFDLHdDQUF3QztJQUN4QyxNQUFNLGtCQUFrQixHQUErQixJQUFBLHdCQUFnQixFQUFDLElBQUEsa0RBQTZCLEVBQUMsa0JBQWtCLENBQUMsRUFBRSxhQUFhLENBQUMsQ0FBQztJQUUxSSxrQ0FBa0M7SUFDbEMsTUFBTSxRQUFRLEdBQTBCLElBQUksU0FBUyxDQUFDLFdBQVcsQ0FBQyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUsa0JBQWtCLENBQUMsQ0FBQztJQUVsSCxPQUFPLFFBQVEsQ0FBQztBQUNsQixDQUFDO0FBT0QsU0FBZ0IsbUJBQW1CLENBQUMsV0FBaUM7SUFDbkUsSUFBSSxhQUFhLEdBQUcsRUFBRSxDQUFDO0lBQ3ZCLElBQUksVUFBVSxHQUFHLEtBQUssQ0FBQztJQUV2QixJQUFJLFdBQVcsQ0FBQyw0QkFBNEIsSUFBSSxXQUFXLENBQUMsYUFBYSxFQUFFLENBQUM7UUFDMUUsYUFBYSxJQUFJLHVGQUF1RixDQUFDO1FBQ3pHLFVBQVUsR0FBRyxJQUFJLENBQUM7SUFDcEIsQ0FBQztJQUVELElBQUksVUFBVSxFQUFFLENBQUM7UUFDZixNQUFNLElBQUksS0FBSyxDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBQ2pDLENBQUM7QUFDSCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiAgQ29weXJpZ2h0IEFtYXpvbi5jb20sIEluYy4gb3IgaXRzIGFmZmlsaWF0ZXMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIikuIFlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiAqICB3aXRoIHRoZSBMaWNlbnNlLiBBIGNvcHkgb2YgdGhlIExpY2Vuc2UgaXMgbG9jYXRlZCBhdFxuICpcbiAqICAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogIG9yIGluIHRoZSAnbGljZW5zZScgZmlsZSBhY2NvbXBhbnlpbmcgdGhpcyBmaWxlLiBUaGlzIGZpbGUgaXMgZGlzdHJpYnV0ZWQgb24gYW4gJ0FTIElTJyBCQVNJUywgV0lUSE9VVCBXQVJSQU5USUVTXG4gKiAgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZXhwcmVzcyBvciBpbXBsaWVkLiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnNcbiAqICBhbmQgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKi9cblxuLypcbiAqICBUaGUgZnVuY3Rpb25zIGZvdW5kIGhlcmUgaW4gdGhlIGNvcmUgbGlicmFyeSBhcmUgZm9yIGludGVybmFsIHVzZSBhbmQgY2FuIGJlIGNoYW5nZWRcbiAqICBvciByZW1vdmVkIG91dHNpZGUgb2YgYSBtYWpvciByZWxlYXNlLiBXZSByZWNvbW1lbmQgYWdhaW5zdCBjYWxsaW5nIHRoZW0gZGlyZWN0bHkgZnJvbSBjbGllbnQgY29kZS5cbiAqL1xuXG5pbXBvcnQgKiBhcyBzYWdlbWFrZXIgZnJvbSAnYXdzLWNkay1saWIvYXdzLXNhZ2VtYWtlcic7XG5pbXBvcnQgKiBhcyBlYzIgZnJvbSAnYXdzLWNkay1saWIvYXdzLWVjMic7XG5pbXBvcnQgeyBidWlsZEVuY3J5cHRpb25LZXkgfSBmcm9tICcuL2ttcy1oZWxwZXInO1xuaW1wb3J0IHtcbiAgRGVmYXVsdFNhZ2VtYWtlck5vdGVib29rUHJvcHMsXG4gIERlZmF1bHRTYWdlbWFrZXJNb2RlbFByb3BzLFxuICBEZWZhdWx0U2FnZW1ha2VyRW5kcG9pbnRDb25maWdQcm9wcyxcbiAgRGVmYXVsdFNhZ2VtYWtlckVuZHBvaW50UHJvcHMsXG59IGZyb20gJy4vc2FnZW1ha2VyLWRlZmF1bHRzJztcbmltcG9ydCAqIGFzIGNkayBmcm9tICdhd3MtY2RrLWxpYic7XG5pbXBvcnQgeyBhZGRDZm5HdWFyZFN1cHByZXNzUnVsZXMsIGFkZENmblN1cHByZXNzUnVsZXMsIGNvbnNvbGlkYXRlUHJvcHMgfSBmcm9tICcuL3V0aWxzJztcbmltcG9ydCB7IGJ1aWxkVnBjIH0gZnJvbSAnLi92cGMtaGVscGVyJztcbmltcG9ydCAqIGFzIGlhbSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtaWFtJztcbmltcG9ydCB7IEF3cyB9IGZyb20gJ2F3cy1jZGstbGliJztcbmltcG9ydCB7IERlZmF1bHRQdWJsaWNQcml2YXRlVnBjUHJvcHMgfSBmcm9tICcuL3ZwYy1kZWZhdWx0cyc7XG5pbXBvcnQgeyBidWlsZFNlY3VyaXR5R3JvdXAgfSBmcm9tICcuL3NlY3VyaXR5LWdyb3VwLWhlbHBlcic7XG4vLyBOb3RlOiBUbyBlbnN1cmUgQ0RLdjIgY29tcGF0aWJpbGl0eSwga2VlcCB0aGUgaW1wb3J0IHN0YXRlbWVudCBmb3IgQ29uc3RydWN0IHNlcGFyYXRlXG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdjb25zdHJ1Y3RzJztcblxuZXhwb3J0IGludGVyZmFjZSBCdWlsZFNhZ2VtYWtlck5vdGVib29rUHJvcHMge1xuICAvKipcbiAgICogT3B0aW9uYWwgdXNlciBwcm92aWRlZCBwcm9wcyBmb3IgQ2ZuTm90ZWJvb2tJbnN0YW5jZVByb3BzXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gRGVmYXVsdCBwcm9wcyBhcmUgdXNlZFxuICAgKi9cbiAgcmVhZG9ubHkgc2FnZW1ha2VyTm90ZWJvb2tQcm9wcz86IHNhZ2VtYWtlci5DZm5Ob3RlYm9va0luc3RhbmNlUHJvcHMgfCBhbnk7XG4gIC8qKlxuICAgKiBPcHRpb25hbCB1c2VyIHByb3ZpZGVkIHByb3BzIHRvIGRlcGxveSBpbnNpZGUgdnBjXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gdHJ1ZVxuICAgKi9cbiAgcmVhZG9ubHkgZGVwbG95SW5zaWRlVnBjPzogYm9vbGVhbjtcbiAgLyoqXG4gICAqIEFuIG9wdGlvbmFsLCBFeGlzdGluZyBpbnN0YW5jZSBvZiBub3RlYm9vayBvYmplY3QuXG4gICAqIElmIHRoaXMgaXMgc2V0IHRoZW4gdGhlIHNhZ2VtYWtlck5vdGVib29rUHJvcHMgaXMgaWdub3JlZFxuICAgKlxuICAgKiBAZGVmYXVsdCAtIE5vbmVcbiAgICovXG4gIHJlYWRvbmx5IGV4aXN0aW5nTm90ZWJvb2tPYmo/OiBzYWdlbWFrZXIuQ2ZuTm90ZWJvb2tJbnN0YW5jZTtcbiAgLyoqXG4gICAqIElBTSBSb2xlIEFybiBmb3IgU2FnZW1ha2VyIE5vdGVCb29rSW5zdGFuY2VcbiAgICpcbiAgICogQGRlZmF1bHQgLSBOb25lXG4gICAqL1xuICByZWFkb25seSByb2xlOiBpYW0uUm9sZTtcbn1cblxuZnVuY3Rpb24gYWRkUGVybWlzc2lvbnMocm9sZTogaWFtLlJvbGUsIHByb3BzPzogQnVpbGRTYWdlbWFrZXJFbmRwb2ludFByb3BzKSB7XG4gIC8vIEdyYW50IHBlcm1pc3Npb25zIHRvIE5vdGVCb29rSW5zdGFuY2UgZm9yIGNyZWF0aW5nIGFuZCB0cmFpbmluZyB0aGUgbW9kZWxcbiAgcm9sZS5hZGRUb1BvbGljeShcbiAgICBuZXcgaWFtLlBvbGljeVN0YXRlbWVudCh7XG4gICAgICByZXNvdXJjZXM6IFtgYXJuOiR7QXdzLlBBUlRJVElPTn06c2FnZW1ha2VyOiR7QXdzLlJFR0lPTn06JHtBd3MuQUNDT1VOVF9JRH06KmBdLFxuICAgICAgYWN0aW9uczogW1xuICAgICAgICAnc2FnZW1ha2VyOkNyZWF0ZVRyYWluaW5nSm9iJyxcbiAgICAgICAgJ3NhZ2VtYWtlcjpEZXNjcmliZVRyYWluaW5nSm9iJyxcbiAgICAgICAgJ3NhZ2VtYWtlcjpDcmVhdGVNb2RlbCcsXG4gICAgICAgICdzYWdlbWFrZXI6RGVzY3JpYmVNb2RlbCcsXG4gICAgICAgICdzYWdlbWFrZXI6RGVsZXRlTW9kZWwnLFxuICAgICAgICAnc2FnZW1ha2VyOkNyZWF0ZUVuZHBvaW50JyxcbiAgICAgICAgJ3NhZ2VtYWtlcjpDcmVhdGVFbmRwb2ludENvbmZpZycsXG4gICAgICAgICdzYWdlbWFrZXI6RGVzY3JpYmVFbmRwb2ludCcsXG4gICAgICAgICdzYWdlbWFrZXI6RGVzY3JpYmVFbmRwb2ludENvbmZpZycsXG4gICAgICAgICdzYWdlbWFrZXI6RGVsZXRlRW5kcG9pbnQnLFxuICAgICAgICAnc2FnZW1ha2VyOkRlbGV0ZUVuZHBvaW50Q29uZmlnJyxcbiAgICAgICAgJ3NhZ2VtYWtlcjpJbnZva2VFbmRwb2ludCcsXG4gICAgICBdLFxuICAgIH0pXG4gICk7XG5cbiAgLy8gR3JhbnQgQ2xvdWRXYXRjaCBMb2dnaW5nIHBlcm1pc3Npb25zXG4gIHJvbGUuYWRkVG9Qb2xpY3koXG4gICAgbmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgcmVzb3VyY2VzOiBbYGFybjoke2Nkay5Bd3MuUEFSVElUSU9OfTpsb2dzOiR7Y2RrLkF3cy5SRUdJT059OiR7Y2RrLkF3cy5BQ0NPVU5UX0lEfTpsb2ctZ3JvdXA6L2F3cy9zYWdlbWFrZXIvKmBdLFxuICAgICAgYWN0aW9uczogW1xuICAgICAgICAnbG9nczpDcmVhdGVMb2dHcm91cCcsXG4gICAgICAgICdsb2dzOkNyZWF0ZUxvZ1N0cmVhbScsXG4gICAgICAgICdsb2dzOkRlc2NyaWJlTG9nU3RyZWFtcycsXG4gICAgICAgICdsb2dzOkdldExvZ0V2ZW50cycsXG4gICAgICAgICdsb2dzOlB1dExvZ0V2ZW50cycsXG4gICAgICBdLFxuICAgIH0pXG4gICk7XG5cbiAgLy8gVG8gcGxhY2UgdGhlIFNhZ2VtYWtlciBlbmRwb2ludCBpbiBhIFZQQ1xuICBpZiAocHJvcHMgJiYgcHJvcHMudnBjKSB7XG4gICAgcm9sZS5hZGRUb1BvbGljeShcbiAgICAgIG5ldyBpYW0uUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgcmVzb3VyY2VzOiBbJyonXSxcbiAgICAgICAgYWN0aW9uczogW1xuICAgICAgICAgICdlYzI6Q3JlYXRlTmV0d29ya0ludGVyZmFjZScsXG4gICAgICAgICAgJ2VjMjpDcmVhdGVOZXR3b3JrSW50ZXJmYWNlUGVybWlzc2lvbicsXG4gICAgICAgICAgJ2VjMjpEZWxldGVOZXR3b3JrSW50ZXJmYWNlJyxcbiAgICAgICAgICAnZWMyOkRlbGV0ZU5ldHdvcmtJbnRlcmZhY2VQZXJtaXNzaW9uJyxcbiAgICAgICAgICAnZWMyOkRlc2NyaWJlTmV0d29ya0ludGVyZmFjZXMnLFxuICAgICAgICAgICdlYzI6QXNzaWduUHJpdmF0ZUlwQWRkcmVzc2VzJyxcbiAgICAgICAgICAnZWMyOlVuYXNzaWduUHJpdmF0ZUlwQWRkcmVzc2VzJyxcbiAgICAgICAgICAnZWMyOkRlc2NyaWJlVnBjcycsXG4gICAgICAgICAgJ2VjMjpEZXNjcmliZURoY3BPcHRpb25zJyxcbiAgICAgICAgICAnZWMyOkRlc2NyaWJlU3VibmV0cycsXG4gICAgICAgICAgJ2VjMjpEZXNjcmliZVNlY3VyaXR5R3JvdXBzJyxcbiAgICAgICAgXSxcbiAgICAgIH0pXG4gICAgKTtcbiAgfVxuXG4gIC8vIFRvIGNyZWF0ZSBhIFNhZ2VtYWtlciBtb2RlbCB1c2luZyBCcmluZy1Zb3VyLU93bi1Nb2RlbCAoQllPTSkgYWxnb3JpdGhtIGltYWdlXG4gIC8vIFRoZSBpbWFnZSBVUkwgaXMgc3BlY2lmaWVkIGluIHRoZSBtb2RlbFByb3BzXG4gIHJvbGUuYWRkVG9Qb2xpY3koXG4gICAgbmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgcmVzb3VyY2VzOiBbYGFybjoke2Nkay5Bd3MuUEFSVElUSU9OfTplY3I6JHtjZGsuQXdzLlJFR0lPTn06JHtjZGsuQXdzLkFDQ09VTlRfSUR9OnJlcG9zaXRvcnkvKmBdLFxuICAgICAgYWN0aW9uczogW1xuICAgICAgICAnZWNyOkJhdGNoQ2hlY2tMYXllckF2YWlsYWJpbGl0eScsXG4gICAgICAgICdlY3I6R2V0RG93bmxvYWRVcmxGb3JMYXllcicsXG4gICAgICAgICdlY3I6RGVzY3JpYmVSZXBvc2l0b3JpZXMnLFxuICAgICAgICAnZWNyOkRlc2NyaWJlSW1hZ2VzJyxcbiAgICAgICAgJ2VjcjpCYXRjaEdldEltYWdlJyxcbiAgICAgIF0sXG4gICAgfSlcbiAgKTtcblxuICAvLyBBZGQgR2V0QXV0aG9yaXphdGlvblRva2VuIChpdCBjYW4gbm90IGJlIGJvdW5kIHRvIHJlc291cmNlcyBvdGhlciB0aGFuICopXG4gIHJvbGUuYWRkVG9Qb2xpY3koXG4gICAgbmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgcmVzb3VyY2VzOiBbJyonXSxcbiAgICAgIGFjdGlvbnM6IFsnZWNyOkdldEF1dGhvcml6YXRpb25Ub2tlbiddLFxuICAgIH0pXG4gICk7XG5cbiAgLy8gYWRkIHBlcm1pc3Npb24gdG8gdXNlIEVsYXN0aWMgSW5mZXJlbmNlIGFjY2VsZXJhdG9yXG4gIGlmIChwcm9wcyAmJiBwcm9wcy5lbmRwb2ludENvbmZpZ1Byb3BzKSB7XG4gICAgLy8gR2V0IHRoZSBhY2NlbGVyYXRvclR5cGUsIGlmIGFueVxuICAgIGNvbnN0IGFjY2VsZXJhdG9yVHlwZSA9IChwcm9wcy5lbmRwb2ludENvbmZpZ1Byb3BzXG4gICAgICA/LnByb2R1Y3Rpb25WYXJpYW50cyBhcyBzYWdlbWFrZXIuQ2ZuRW5kcG9pbnRDb25maWcuUHJvZHVjdGlvblZhcmlhbnRQcm9wZXJ0eVtdKVswXS5hY2NlbGVyYXRvclR5cGU7XG4gICAgaWYgKGFjY2VsZXJhdG9yVHlwZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICByb2xlLmFkZFRvUG9saWN5KFxuICAgICAgICBuZXcgaWFtLlBvbGljeVN0YXRlbWVudCh7XG4gICAgICAgICAgcmVzb3VyY2VzOiBbJyonXSxcbiAgICAgICAgICBhY3Rpb25zOiBbJ2VsYXN0aWMtaW5mZXJlbmNlOkNvbm5lY3QnXSxcbiAgICAgICAgfSlcbiAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgLy8gYWRkIGttcyBwZXJtaXNzaW9uc1xuICByb2xlLmFkZFRvUG9saWN5KFxuICAgIG5ldyBpYW0uUG9saWN5U3RhdGVtZW50KHtcbiAgICAgIC8vIHRoZSBrbXNLZXlJZCBpbiB0aGUgZW5kcG9pbnRDb25maWdQcm9wcyBjYW4gYmUgYW55IG9mIHRoZSBmb2xsb3dpbmcgZm9ybWF0czpcbiAgICAgIC8vIEtleSBJRDogMTIzNGFiY2QtMTJhYi0zNGNkLTU2ZWYtMTIzNDU2Nzg5MGFiXG4gICAgICAvLyBLZXkgQVJOOiBhcm46YXdzOmttczo8cmVnaW9uPjo8YWNjb3VudElEPjprZXkvMTIzNGFiY2QtMTJhYi0zNGNkLTU2ZWYtMTIzNDU2Nzg5MGFiXG4gICAgICAvLyBBbGlhcyBuYW1lOiBhbGlhcy9FeGFtcGxlQWxpYXNcbiAgICAgIC8vIEFsaWFzIG5hbWUgQVJOOiBhcm46YXdzOmttczo8cmVnaW9uPjo8YWNjb3VudElEPjphbGlhcy9FeGFtcGxlQWxpYXNcbiAgICAgIC8vIHRoZSBrZXkgaXMgdXNlZCB0byBlbmNyeXB0L2RlY3J5cHQgZGF0YSBjYXB0dXJlZCBieSB0aGUgU2FnZW1ha2VyIGVuZHBvaW50IGFuZCBzdG9yZWQgaW4gUzMgYnVja2V0XG4gICAgICByZXNvdXJjZXM6IFtcbiAgICAgICAgYGFybjoke2Nkay5Bd3MuUEFSVElUSU9OfTprbXM6JHtjZGsuQXdzLlJFR0lPTn06JHtjZGsuQXdzLkFDQ09VTlRfSUR9OmtleS8qYCxcbiAgICAgICAgYGFybjoke2Nkay5Bd3MuUEFSVElUSU9OfTprbXM6JHtjZGsuQXdzLlJFR0lPTn06JHtjZGsuQXdzLkFDQ09VTlRfSUR9OmFsaWFzLypgLFxuICAgICAgXSxcbiAgICAgIGFjdGlvbnM6IFsna21zOkVuY3J5cHQnLCAna21zOkRlY3J5cHQnLCAna21zOlJlRW5jcnlwdConLCAna21zOkdlbmVyYXRlRGF0YUtleSonLCAna21zOkRlc2NyaWJlS2V5J10sXG4gICAgfSlcbiAgKTtcblxuICAvLyBBZGQgUzMgcGVybWlzc2lvbnMgdG8gZ2V0IE1vZGVsIGFydGlmYWN0LCBwdXQgZGF0YSBjYXB0dXJlIGZpbGVzLCBldGMuXG4gIHJvbGUuYWRkVG9Qb2xpY3koXG4gICAgbmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgYWN0aW9uczogWydzMzpHZXRPYmplY3QnLCAnczM6UHV0T2JqZWN0JywgJ3MzOkRlbGV0ZU9iamVjdCcsICdzMzpMaXN0QnVja2V0J10sXG4gICAgICByZXNvdXJjZXM6IFtgYXJuOmF3czpzMzo6OipgXSxcbiAgICB9KVxuICApO1xuXG4gIC8vIEdyYW50IEdldFJvbGUgcGVybWlzc2lvbnMgdG8gdGhlIFNhZ2VtYWtlciBzZXJ2aWNlXG4gIHJvbGUuYWRkVG9Qb2xpY3koXG4gICAgbmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgcmVzb3VyY2VzOiBbcm9sZS5yb2xlQXJuXSxcbiAgICAgIGFjdGlvbnM6IFsnaWFtOkdldFJvbGUnXSxcbiAgICB9KVxuICApO1xuXG4gIC8vIEdyYW50IFBhc3NSb2xlIHBlcm1pc3Npb25zIHRvIHRoZSBTYWdlbWFrZXIgc2VydmljZVxuICByb2xlLmFkZFRvUG9saWN5KFxuICAgIG5ldyBpYW0uUG9saWN5U3RhdGVtZW50KHtcbiAgICAgIHJlc291cmNlczogW3JvbGUucm9sZUFybl0sXG4gICAgICBhY3Rpb25zOiBbJ2lhbTpQYXNzUm9sZSddLFxuICAgICAgY29uZGl0aW9uczoge1xuICAgICAgICBTdHJpbmdMaWtlOiB7ICdpYW06UGFzc2VkVG9TZXJ2aWNlJzogJ3NhZ2VtYWtlci5hbWF6b25hd3MuY29tJyB9LFxuICAgICAgfSxcbiAgICB9KVxuICApO1xuXG4gIC8vIEFkZCBDRk4gTkFHIHVwcHJlc3MgdG8gYWxsb3cgZm9yIFwiUmVzb3VyY2VcIjogXCIqXCIgZm9yIEVOSSBhY2Nlc3MgaW4gVlBDLFxuICAvLyBFQ1IgYXV0aG9yaXphdGlvbiB0b2tlbiBmb3IgY3VzdG9tIG1vZGVsIGltYWdlcywgYW5kIGVsYXN0aWMgaW5mZXJlbmNlXG4gIC8vIEFkZCBDRk4gTkFHIGZvciBDb21wbGV4IFJvbGUgYmVjYXVzZSBTYWdtYWtlciBuZWVkcyBwZXJtaXNzaW9ucyB0byBhY2Nlc3Mgc2V2ZXJhbCBzZXJ2aWNlc1xuICBjb25zdCByb2xlRGVmYXVsdFBvbGljeSA9IHJvbGUubm9kZS50cnlGaW5kQ2hpbGQoJ0RlZmF1bHRQb2xpY3knKT8ubm9kZS5maW5kQ2hpbGQoJ1Jlc291cmNlJykgYXMgaWFtLkNmblBvbGljeTtcbiAgYWRkQ2ZuU3VwcHJlc3NSdWxlcyhyb2xlRGVmYXVsdFBvbGljeSwgW1xuICAgIHtcbiAgICAgIGlkOiAnVzEyJyxcbiAgICAgIHJlYXNvbjogYFNhZ2VtYWtlciBuZWVkcyB0aGUgZm9sbG93aW5nIG1pbmltdW0gcmVxdWlyZWQgcGVybWlzc2lvbnMgdG8gYWNjZXNzIEVOSXMgaW4gYSBWUEMsIEVDUiBmb3IgY3VzdG9tIG1vZGVsIGltYWdlcywgYW5kIGVsYXN0aWMgaW5mZXJlbmNlLmAsXG4gICAgfSxcbiAgICB7XG4gICAgICBpZDogJ1c3NicsXG4gICAgICByZWFzb246ICdDb21wbGV4IHJvbGUgYmVjdWFzZSBTYWdlbWFrZXIgbmVlZHMgcGVybWlzc2lvbnMgdG8gYWNjZXNzIHNldmVyYWwgc2VydmljZXMnLFxuICAgIH1cbiAgXSk7XG4gIGFkZENmbkd1YXJkU3VwcHJlc3NSdWxlcyhyb2xlRGVmYXVsdFBvbGljeSwgW1wiSUFNX1BPTElDWV9OT05fQ09NUExJQU5UX0FSTlwiXSk7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQnVpbGRTYWdlbWFrZXJOb3RlYm9va1Jlc3BvbnNlIHtcbiAgcmVhZG9ubHkgbm90ZWJvb2s6IHNhZ2VtYWtlci5DZm5Ob3RlYm9va0luc3RhbmNlLFxuICByZWFkb25seSB2cGM/OiBlYzIuSVZwYyxcbiAgcmVhZG9ubHkgc2VjdXJpdHlHcm91cD86IGVjMi5TZWN1cml0eUdyb3VwXG59XG5cbi8qKlxuICogQGludGVybmFsIFRoaXMgaXMgYW4gaW50ZXJuYWwgY29yZSBmdW5jdGlvbiBhbmQgc2hvdWxkIG5vdCBiZSBjYWxsZWQgZGlyZWN0bHkgYnkgU29sdXRpb25zIENvbnN0cnVjdHMgY2xpZW50cy5cbiAqXG4gKiBUaGUgY29uc3RydWN0cyBkZXBsb3lpbmcgU2FnZW1ha2VyIHNlcnZpY2VzIG9ubHkgZGVwbG95IFNhZ2VtYWtlciBFbmRwb2ludHMsIEVuZHBvaW50XG4gKiBDb25maWd1cmF0aW9ucyBhbmQgKGFscmVhZHkgZGV2ZWxvcGVkKSBNb2RlbHMuIFRoZXJlIGFyZSBubyBDb25zdHJ1Y3RzIHRoYXQgZGVwbG95IE5vdGVib29rcy5cbiAqIFRoZXJlIGRvZXNuJ3QgYXBwZWFyIHRvIGJlIGFueSByZWZlcmVuY2VzIHRvIHRoaXMgZnVuY3Rpb24gb3V0c2lkZSB0aGUgdW5pdCB0ZXN0IGZpbGUgLSBzb1xuICogdGhpcyBmdW5jdGlvbiBpcyB1bm5lY2Nlc3NhcnkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBidWlsZFNhZ2VtYWtlck5vdGVib29rKFxuICBzY29wZTogQ29uc3RydWN0LFxuICBpZDogc3RyaW5nLFxuICBwcm9wczogQnVpbGRTYWdlbWFrZXJOb3RlYm9va1Byb3BzXG4pOiBCdWlsZFNhZ2VtYWtlck5vdGVib29rUmVzcG9uc2Uge1xuICAvLyBTZXR1cCB0aGUgbm90ZWJvb2sgcHJvcGVydGllc1xuICBsZXQgc2FnZW1ha2VyTm90ZWJvb2tQcm9wcztcbiAgbGV0IHZwY0luc3RhbmNlO1xuICBsZXQgc2VjdXJpdHlHcm91cDtcbiAgbGV0IGttc0tleUlkOiBzdHJpbmc7XG4gIGxldCBzdWJuZXRJZDogc3RyaW5nO1xuXG4gIC8vIENvbmRpdGlvbmFsIFNhZ2VtYWtlciBOb3RlYm9vayBjcmVhdGlvblxuICBpZiAocHJvcHMuZXhpc3RpbmdOb3RlYm9va09iaikge1xuICAgIHJldHVybiB7IG5vdGVib29rOiBwcm9wcy5leGlzdGluZ05vdGVib29rT2JqIH07XG4gIH1cblxuICBpZiAoQ2hlY2tOb3RlYm9va1ZwY1Byb3BzKHByb3BzKSkge1xuICAgIHRocm93IG5ldyBFcnJvcignTXVzdCBkZWZpbmUgYm90aCBzYWdlbWFrZXJOb3RlYm9va1Byb3BzLnN1Ym5ldElkIGFuZCBzYWdlbWFrZXJOb3RlYm9va1Byb3BzLnNlY3VyaXR5R3JvdXBJZHMnKTtcbiAgfVxuXG4gIGFkZFBlcm1pc3Npb25zKHByb3BzLnJvbGUpO1xuXG4gIGlmIChwcm9wcy5zYWdlbWFrZXJOb3RlYm9va1Byb3BzPy5rbXNLZXlJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAga21zS2V5SWQgPSBidWlsZEVuY3J5cHRpb25LZXkoc2NvcGUsICBpZCkua2V5SWQ7XG4gIH0gZWxzZSB7XG4gICAga21zS2V5SWQgPSBwcm9wcy5zYWdlbWFrZXJOb3RlYm9va1Byb3BzLmttc0tleUlkO1xuICB9XG5cbiAgaWYgKHByb3BzLmRlcGxveUluc2lkZVZwYyA9PT0gdW5kZWZpbmVkIHx8IHByb3BzLmRlcGxveUluc2lkZVZwYykge1xuICAgIGlmIChcbiAgICAgIHByb3BzLnNhZ2VtYWtlck5vdGVib29rUHJvcHM/LnN1Ym5ldElkID09PSB1bmRlZmluZWQgJiZcbiAgICAgIHByb3BzLnNhZ2VtYWtlck5vdGVib29rUHJvcHM/LnNlY3VyaXR5R3JvdXBJZHMgPT09IHVuZGVmaW5lZFxuICAgICkge1xuICAgICAgdnBjSW5zdGFuY2UgPSBidWlsZFZwYyhzY29wZSwge1xuICAgICAgICBkZWZhdWx0VnBjUHJvcHM6IERlZmF1bHRQdWJsaWNQcml2YXRlVnBjUHJvcHMoKSxcbiAgICAgIH0pO1xuICAgICAgc2VjdXJpdHlHcm91cCA9IGJ1aWxkU2VjdXJpdHlHcm91cChcbiAgICAgICAgc2NvcGUsXG4gICAgICAgICdTZWN1cml0eUdyb3VwJyxcbiAgICAgICAge1xuICAgICAgICAgIHZwYzogdnBjSW5zdGFuY2UsXG4gICAgICAgICAgYWxsb3dBbGxPdXRib3VuZDogZmFsc2UsXG4gICAgICAgIH0sXG4gICAgICAgIFtdLFxuICAgICAgICBbeyBwZWVyOiBlYzIuUGVlci5hbnlJcHY0KCksIGNvbm5lY3Rpb246IGVjMi5Qb3J0LnRjcCg0NDMpIH1dXG4gICAgICApO1xuXG4gICAgICBzdWJuZXRJZCA9IHZwY0luc3RhbmNlLnByaXZhdGVTdWJuZXRzWzBdLnN1Ym5ldElkO1xuXG4gICAgICBzYWdlbWFrZXJOb3RlYm9va1Byb3BzID0gRGVmYXVsdFNhZ2VtYWtlck5vdGVib29rUHJvcHMocHJvcHMucm9sZS5yb2xlQXJuLCBrbXNLZXlJZCwgc3VibmV0SWQsIFtcbiAgICAgICAgc2VjdXJpdHlHcm91cC5zZWN1cml0eUdyb3VwSWQsXG4gICAgICBdKTtcbiAgICB9IGVsc2Uge1xuICAgICAgc2FnZW1ha2VyTm90ZWJvb2tQcm9wcyA9IERlZmF1bHRTYWdlbWFrZXJOb3RlYm9va1Byb3BzKFxuICAgICAgICBwcm9wcy5yb2xlLnJvbGVBcm4sXG4gICAgICAgIGttc0tleUlkLFxuICAgICAgICBwcm9wcy5zYWdlbWFrZXJOb3RlYm9va1Byb3BzPy5zdWJuZXRJZCxcbiAgICAgICAgcHJvcHMuc2FnZW1ha2VyTm90ZWJvb2tQcm9wcz8uc2VjdXJpdHlHcm91cElkc1xuICAgICAgKTtcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgc2FnZW1ha2VyTm90ZWJvb2tQcm9wcyA9IERlZmF1bHRTYWdlbWFrZXJOb3RlYm9va1Byb3BzKHByb3BzLnJvbGUucm9sZUFybiwga21zS2V5SWQpO1xuICB9XG5cbiAgc2FnZW1ha2VyTm90ZWJvb2tQcm9wcyA9IGNvbnNvbGlkYXRlUHJvcHMoc2FnZW1ha2VyTm90ZWJvb2tQcm9wcywgcHJvcHMuc2FnZW1ha2VyTm90ZWJvb2tQcm9wcyk7XG5cbiAgLy8gQ3JlYXRlIHRoZSBub3RlYm9va1xuICAvLyBOT1NPTkFSOiAodHlwZXNjcmlwdDpTNjMxOSlcbiAgLy8ga2V5SUQgaXMgY3JlYXRlZCBhYm92ZSBpbiB0aGUgaWYgKHByb3BzLnNhZ2VtYWtlck5vdGVib29rUHJvcHM/Lmttc0tleUlkID09PSB1bmRlZmluZWQpXG4gIC8vIGJsb2NrLiBJdCBpcyB0aGVuIHBhc3NlZCB0byBEZWZhdWx0U2FnZW1ha2VyTm90ZWJvb2tQcm9wcygpXG4gIC8vIFRoaXMgYmVoYXZpb3IgaXMgdmFsaWRhdGVkIGluIHVuaXQgdGVzdFxuICBjb25zdCBzYWdlbWFrZXJJbnN0YW5jZTogc2FnZW1ha2VyLkNmbk5vdGVib29rSW5zdGFuY2UgPSBuZXcgc2FnZW1ha2VyLkNmbk5vdGVib29rSW5zdGFuY2UoXG4gICAgc2NvcGUsXG4gICAgJ1NhZ2VtYWtlck5vdGVib29rJyxcbiAgICBzYWdlbWFrZXJOb3RlYm9va1Byb3BzIC8vIE5PU09OQVJcbiAgKTtcbiAgaWYgKHZwY0luc3RhbmNlKSB7XG4gICAgcmV0dXJuIHsgbm90ZWJvb2s6IHNhZ2VtYWtlckluc3RhbmNlLCB2cGM6IHZwY0luc3RhbmNlLCBzZWN1cml0eUdyb3VwIH07XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIHsgbm90ZWJvb2s6IHNhZ2VtYWtlckluc3RhbmNlIH07XG4gIH1cbn1cblxuLyoqXG4gKiBAaW50ZXJuYWwgVGhpcyBpcyBhbiBpbnRlcm5hbCBjb3JlIGZ1bmN0aW9uIGFuZCBzaG91bGQgbm90IGJlIGNhbGxlZCBkaXJlY3RseSBieSBTb2x1dGlvbnMgQ29uc3RydWN0cyBjbGllbnRzLlxuICovXG5mdW5jdGlvbiBDaGVja05vdGVib29rVnBjUHJvcHMocHJvcHM6IEJ1aWxkU2FnZW1ha2VyTm90ZWJvb2tQcm9wcyk6IGJvb2xlYW4ge1xuICBpZiAoKHByb3BzLnNhZ2VtYWtlck5vdGVib29rUHJvcHM/LnN1Ym5ldElkICYmIHByb3BzLnNhZ2VtYWtlck5vdGVib29rUHJvcHM/LnNlY3VyaXR5R3JvdXBJZHMgPT09IHVuZGVmaW5lZCkgfHxcbiAgICAocHJvcHMuc2FnZW1ha2VyTm90ZWJvb2tQcm9wcz8uc3VibmV0SWQgPT09IHVuZGVmaW5lZCAmJiBwcm9wcy5zYWdlbWFrZXJOb3RlYm9va1Byb3BzPy5zZWN1cml0eUdyb3VwSWRzKVxuICApIHtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuICByZXR1cm4gZmFsc2U7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQnVpbGRTYWdlbWFrZXJFbmRwb2ludFByb3BzIHtcbiAgLyoqXG4gICAqIEV4aXN0aW5nIFNhZ2VtYWtlciBFbmRwb2ludCBvYmplY3QsIGlmIHRoaXMgaXMgc2V0IHRoZW4gdGhlIG1vZGVsUHJvcHMsIGVuZHBvaW50Q29uZmlnUHJvcHMsIGFuZCBlbmRwb2ludFByb3BzIGFyZSBpZ25vcmVkXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gTm9uZVxuICAgKi9cbiAgcmVhZG9ubHkgZXhpc3RpbmdTYWdlbWFrZXJFbmRwb2ludE9iaj86IHNhZ2VtYWtlci5DZm5FbmRwb2ludDtcbiAgLyoqXG4gICAqIFVzZXIgcHJvdmlkZWQgcHJvcHMgdG8gY3JlYXRlIFNhZ2VtYWtlciBNb2RlbFxuICAgKlxuICAgKiBAZGVmYXVsdCAtIE5vbmVcbiAgICovXG4gIHJlYWRvbmx5IG1vZGVsUHJvcHM/OiBzYWdlbWFrZXIuQ2ZuTW9kZWxQcm9wcyB8IGFueTtcbiAgLyoqXG4gICAqIFVzZXIgcHJvdmlkZWQgcHJvcHMgdG8gY3JlYXRlIFNhZ2VtYWtlciBFbmRwb2ludCBDb25maWd1cmF0aW9uXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gTm9uZVxuICAgKi9cbiAgcmVhZG9ubHkgZW5kcG9pbnRDb25maWdQcm9wcz86IHNhZ2VtYWtlci5DZm5FbmRwb2ludENvbmZpZ1Byb3BzO1xuICAvKipcbiAgICogVXNlciBwcm92aWRlZCBwcm9wcyB0byBjcmVhdGUgU2FnZW1ha2VyIEVuZHBvaW50XG4gICAqXG4gICAqIEBkZWZhdWx0IC0gTm9uZVxuICAgKi9cbiAgcmVhZG9ubHkgZW5kcG9pbnRQcm9wcz86IHNhZ2VtYWtlci5DZm5FbmRwb2ludFByb3BzO1xuICAvKipcbiAgICogQSBWUEMgd2hlcmUgdGhlIFNhZ2VtYWtlciBFbmRwb2ludCB3aWxsIGJlIHBsYWNlZFxuICAgKlxuICAgKiBAZGVmYXVsdCAtIE5vbmVcbiAgICovXG4gIHJlYWRvbmx5IHZwYz86IGVjMi5JVnBjO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEJ1aWxkU2FnZW1ha2VyRW5kcG9pbnRSZXNwb25zZSB7XG4gIHJlYWRvbmx5IGVuZHBvaW50OiBzYWdlbWFrZXIuQ2ZuRW5kcG9pbnQsXG4gIHJlYWRvbmx5IGVuZHBvaW50Q29uZmlnPzogc2FnZW1ha2VyLkNmbkVuZHBvaW50Q29uZmlnLFxuICByZWFkb25seSBtb2RlbD86IHNhZ2VtYWtlci5DZm5Nb2RlbFxufVxuXG4vKipcbiAqIEBpbnRlcm5hbCBUaGlzIGlzIGFuIGludGVybmFsIGNvcmUgZnVuY3Rpb24gYW5kIHNob3VsZCBub3QgYmUgY2FsbGVkIGRpcmVjdGx5IGJ5IFNvbHV0aW9ucyBDb25zdHJ1Y3RzIGNsaWVudHMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBCdWlsZFNhZ2VtYWtlckVuZHBvaW50KFxuICBzY29wZTogQ29uc3RydWN0LFxuICBpZDogc3RyaW5nLFxuICBwcm9wczogQnVpbGRTYWdlbWFrZXJFbmRwb2ludFByb3BzXG4pOiBCdWlsZFNhZ2VtYWtlckVuZHBvaW50UmVzcG9uc2Uge1xuICAvKiogQ29uZGl0aW9uYWwgU2FnZW1ha2VyIGVuZHBvaW50IGNyZWF0aW9uICovXG4gIGlmICghcHJvcHMuZXhpc3RpbmdTYWdlbWFrZXJFbmRwb2ludE9iaikge1xuICAgIGlmIChwcm9wcy5tb2RlbFByb3BzKSB7XG4gICAgICBjb25zdCBkZXBsb3lTYWdlbWFrZXJFbmRwb2ludFJlc3BvbnNlID0gZGVwbG95U2FnZW1ha2VyRW5kcG9pbnQoc2NvcGUsIGlkLCBwcm9wcyk7XG4gICAgICByZXR1cm4geyAuLi5kZXBsb3lTYWdlbWFrZXJFbmRwb2ludFJlc3BvbnNlIH07XG4gICAgfSBlbHNlIHtcbiAgICAgIHRocm93IEVycm9yKCdFaXRoZXIgZXhpc3RpbmdTYWdlbWFrZXJFbmRwb2ludE9iaiBvciBhdCBsZWFzdCBtb2RlbFByb3BzIGlzIHJlcXVpcmVkJyk7XG4gICAgfVxuICB9IGVsc2Uge1xuICAgIC8qKiBPdGhlcndpc2UsIHJldHVybiBbZW5kcG9pbnRdICovXG4gICAgcmV0dXJuIHsgZW5kcG9pbnQ6IHByb3BzLmV4aXN0aW5nU2FnZW1ha2VyRW5kcG9pbnRPYmogfTtcbi