aws-cdk
Version:
CDK Toolkit, the command line tool for CDK apps
437 lines • 54.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ParameterValues = exports.TemplateParameters = exports.stabilizeStack = exports.waitForStackDeploy = exports.waitForStackDelete = exports.changeSetHasNoChanges = exports.waitForChangeSet = exports.CloudFormationStack = void 0;
const cx_api_1 = require("@aws-cdk/cx-api");
const stack_status_1 = require("./cloudformation/stack-status");
const logging_1 = require("../../logging");
const serialize_1 = require("../../serialize");
/**
* Represents an (existing) Stack in CloudFormation
*
* Bundle and cache some information that we need during deployment (so we don't have to make
* repeated calls to CloudFormation).
*/
class CloudFormationStack {
static async lookup(cfn, stackName, retrieveProcessedTemplate = false) {
try {
const response = await cfn.describeStacks({ StackName: stackName }).promise();
return new CloudFormationStack(cfn, stackName, response.Stacks && response.Stacks[0], retrieveProcessedTemplate);
}
catch (e) {
if (e.code === 'ValidationError' && e.message === `Stack with id ${stackName} does not exist`) {
return new CloudFormationStack(cfn, stackName, undefined);
}
throw e;
}
}
/**
* Return a copy of the given stack that does not exist
*
* It's a little silly that it needs arguments to do that, but there we go.
*/
static doesNotExist(cfn, stackName) {
return new CloudFormationStack(cfn, stackName);
}
/**
* From static information (for testing)
*/
static fromStaticInformation(cfn, stackName, stack) {
return new CloudFormationStack(cfn, stackName, stack);
}
constructor(cfn, stackName, stack, retrieveProcessedTemplate = false) {
this.cfn = cfn;
this.stackName = stackName;
this.stack = stack;
this.retrieveProcessedTemplate = retrieveProcessedTemplate;
}
/**
* Retrieve the stack's deployed template
*
* Cached, so will only be retrieved once. Will return an empty
* structure if the stack does not exist.
*/
async template() {
if (!this.exists) {
return {};
}
if (this._template === undefined) {
const response = await this.cfn.getTemplate({
StackName: this.stackName,
TemplateStage: this.retrieveProcessedTemplate ? 'Processed' : 'Original',
}).promise();
this._template = (response.TemplateBody && (0, serialize_1.deserializeStructure)(response.TemplateBody)) || {};
}
return this._template;
}
/**
* Whether the stack exists
*/
get exists() {
return this.stack !== undefined;
}
/**
* The stack's ID
*
* Throws if the stack doesn't exist.
*/
get stackId() {
this.assertExists();
return this.stack.StackId;
}
/**
* The stack's current outputs
*
* Empty object if the stack doesn't exist
*/
get outputs() {
if (!this.exists) {
return {};
}
const result = {};
(this.stack.Outputs || []).forEach(output => {
result[output.OutputKey] = output.OutputValue;
});
return result;
}
/**
* The stack's status
*
* Special status NOT_FOUND if the stack does not exist.
*/
get stackStatus() {
if (!this.exists) {
return new stack_status_1.StackStatus('NOT_FOUND', 'Stack not found during lookup');
}
return stack_status_1.StackStatus.fromStackDescription(this.stack);
}
/**
* The stack's current tags
*
* Empty list of the stack does not exist
*/
get tags() {
return this.stack?.Tags || [];
}
/**
* Return the names of all current parameters to the stack
*
* Empty list if the stack does not exist.
*/
get parameterNames() {
return Object.keys(this.parameters);
}
/**
* Return the names and values of all current parameters to the stack
*
* Empty object if the stack does not exist.
*/
get parameters() {
if (!this.exists) {
return {};
}
const ret = {};
for (const param of this.stack.Parameters ?? []) {
ret[param.ParameterKey] = param.ResolvedValue ?? param.ParameterValue;
}
return ret;
}
/**
* Return the termination protection of the stack
*/
get terminationProtection() {
return this.stack?.EnableTerminationProtection;
}
assertExists() {
if (!this.exists) {
throw new Error(`No stack named '${this.stackName}'`);
}
}
}
exports.CloudFormationStack = CloudFormationStack;
/**
* Describe a changeset in CloudFormation, regardless of its current state.
*
* @param cfn a CloudFormation client
* @param stackName the name of the Stack the ChangeSet belongs to
* @param changeSetName the name of the ChangeSet
* @param fetchAll if true, fetches all pages of the change set description.
*
* @returns CloudFormation information about the ChangeSet
*/
async function describeChangeSet(cfn, stackName, changeSetName, { fetchAll }) {
const response = await cfn.describeChangeSet({ StackName: stackName, ChangeSetName: changeSetName }).promise();
// If fetchAll is true, traverse all pages from the change set description.
while (fetchAll && response.NextToken != null) {
const nextPage = await cfn.describeChangeSet({
StackName: stackName,
ChangeSetName: response.ChangeSetId ?? changeSetName,
NextToken: response.NextToken,
}).promise();
// Consolidate the changes
if (nextPage.Changes != null) {
response.Changes = response.Changes != null
? response.Changes.concat(nextPage.Changes)
: nextPage.Changes;
}
// Forward the new NextToken
response.NextToken = nextPage.NextToken;
}
return response;
}
/**
* Waits for a function to return non-+undefined+ before returning.
*
* @param valueProvider a function that will return a value that is not +undefined+ once the wait should be over
* @param timeout the time to wait between two calls to +valueProvider+
*
* @returns the value that was returned by +valueProvider+
*/
async function waitFor(valueProvider, timeout = 5000) {
while (true) {
const result = await valueProvider();
if (result === null) {
return undefined;
}
else if (result !== undefined) {
return result;
}
await new Promise(cb => setTimeout(cb, timeout));
}
}
/**
* Waits for a ChangeSet to be available for triggering a StackUpdate.
*
* Will return a changeset that is either ready to be executed or has no changes.
* Will throw in other cases.
*
* @param cfn a CloudFormation client
* @param stackName the name of the Stack that the ChangeSet belongs to
* @param changeSetName the name of the ChangeSet
* @param fetchAll if true, fetches all pages of the ChangeSet before returning.
*
* @returns the CloudFormation description of the ChangeSet
*/
// eslint-disable-next-line max-len
async function waitForChangeSet(cfn, stackName, changeSetName, { fetchAll }) {
(0, logging_1.debug)('Waiting for changeset %s on stack %s to finish creating...', changeSetName, stackName);
const ret = await waitFor(async () => {
const description = await describeChangeSet(cfn, stackName, changeSetName, { fetchAll });
// The following doesn't use a switch because tsc will not allow fall-through, UNLESS it is allows
// EVERYWHERE that uses this library directly or indirectly, which is undesirable.
if (description.Status === 'CREATE_PENDING' || description.Status === 'CREATE_IN_PROGRESS') {
(0, logging_1.debug)('Changeset %s on stack %s is still creating', changeSetName, stackName);
return undefined;
}
if (description.Status === 'CREATE_COMPLETE' || changeSetHasNoChanges(description)) {
return description;
}
// eslint-disable-next-line max-len
throw new Error(`Failed to create ChangeSet ${changeSetName} on ${stackName}: ${description.Status || 'NO_STATUS'}, ${description.StatusReason || 'no reason provided'}`);
});
if (!ret) {
throw new Error('Change set took too long to be created; aborting');
}
return ret;
}
exports.waitForChangeSet = waitForChangeSet;
/**
* Return true if the given change set has no changes
*
* This must be determined from the status, not the 'Changes' array on the
* object; the latter can be empty because no resources were changed, but if
* there are changes to Outputs, the change set can still be executed.
*/
function changeSetHasNoChanges(description) {
const noChangeErrorPrefixes = [
// Error message for a regular template
'The submitted information didn\'t contain changes.',
// Error message when a Transform is involved (see #10650)
'No updates are to be performed.',
];
return description.Status === 'FAILED'
&& noChangeErrorPrefixes.some(p => (description.StatusReason ?? '').startsWith(p));
}
exports.changeSetHasNoChanges = changeSetHasNoChanges;
/**
* Waits for a CloudFormation stack to stabilize in a complete/available state
* after a delete operation is issued.
*
* Fails if the stack is in a FAILED state. Will not fail if the stack was
* already deleted.
*
* @param cfn a CloudFormation client
* @param stackName the name of the stack to wait for after a delete
*
* @returns the CloudFormation description of the stabilized stack after the delete attempt
*/
async function waitForStackDelete(cfn, stackName) {
const stack = await stabilizeStack(cfn, stackName);
if (!stack) {
return undefined;
}
const status = stack.stackStatus;
if (status.isFailure) {
throw new Error(`The stack named ${stackName} is in a failed state. You may need to delete it from the AWS console : ${status}`);
}
else if (status.isDeleted) {
return undefined;
}
return stack;
}
exports.waitForStackDelete = waitForStackDelete;
/**
* Waits for a CloudFormation stack to stabilize in a complete/available state
* after an update/create operation is issued.
*
* Fails if the stack is in a FAILED state, ROLLBACK state, or DELETED state.
*
* @param cfn a CloudFormation client
* @param stackName the name of the stack to wait for after an update
*
* @returns the CloudFormation description of the stabilized stack after the update attempt
*/
async function waitForStackDeploy(cfn, stackName) {
const stack = await stabilizeStack(cfn, stackName);
if (!stack) {
return undefined;
}
const status = stack.stackStatus;
if (status.isCreationFailure) {
throw new Error(`The stack named ${stackName} failed creation, it may need to be manually deleted from the AWS console: ${status}`);
}
else if (!status.isDeploySuccess) {
throw new Error(`The stack named ${stackName} failed to deploy: ${status}`);
}
return stack;
}
exports.waitForStackDeploy = waitForStackDeploy;
/**
* Wait for a stack to become stable (no longer _IN_PROGRESS), returning it
*/
async function stabilizeStack(cfn, stackName) {
(0, logging_1.debug)('Waiting for stack %s to finish creating or updating...', stackName);
return waitFor(async () => {
const stack = await CloudFormationStack.lookup(cfn, stackName);
if (!stack.exists) {
(0, logging_1.debug)('Stack %s does not exist', stackName);
return null;
}
const status = stack.stackStatus;
if (status.isInProgress) {
(0, logging_1.debug)('Stack %s has an ongoing operation in progress and is not stable (%s)', stackName, status);
return undefined;
}
else if (status.isReviewInProgress) {
// This may happen if a stack creation operation is interrupted before the ChangeSet execution starts. Recovering
// from this would requiring manual intervention (deleting or executing the pending ChangeSet), and failing to do
// so will result in an endless wait here (the ChangeSet wont delete or execute itself). Instead of blocking
// "forever" we proceed as if the stack was existing and stable. If there is a concurrent operation that just
// hasn't finished proceeding just yet, either this operation or the concurrent one may fail due to the other one
// having made progress. Which is fine. I guess.
(0, logging_1.debug)('Stack %s is in REVIEW_IN_PROGRESS state. Considering this is a stable status (%s)', stackName, status);
}
return stack;
});
}
exports.stabilizeStack = stabilizeStack;
/**
* The set of (formal) parameters that have been declared in a template
*/
class TemplateParameters {
static fromTemplate(template) {
return new TemplateParameters(template.Parameters || {});
}
constructor(params) {
this.params = params;
}
/**
* Calculate stack parameters to pass from the given desired parameter values
*
* Will throw if parameters without a Default value or a Previous value are not
* supplied.
*/
supplyAll(updates) {
return new ParameterValues(this.params, updates);
}
/**
* From the template, the given desired values and the current values, calculate the changes to the stack parameters
*
* Will take into account parameters already set on the template (will emit
* 'UsePreviousValue: true' for those unless the value is changed), and will
* throw if parameters without a Default value or a Previous value are not
* supplied.
*/
updateExisting(updates, previousValues) {
return new ParameterValues(this.params, updates, previousValues);
}
}
exports.TemplateParameters = TemplateParameters;
/**
* The set of parameters we're going to pass to a Stack
*/
class ParameterValues {
constructor(formalParams, updates, previousValues = {}) {
this.formalParams = formalParams;
this.values = {};
this.apiParameters = [];
const missingRequired = new Array();
for (const [key, formalParam] of Object.entries(this.formalParams)) {
// Check updates first, then use the previous value (if available), then use
// the default (if available).
//
// If we don't find a parameter value using any of these methods, then that's an error.
const updatedValue = updates[key];
if (updatedValue !== undefined) {
this.values[key] = updatedValue;
this.apiParameters.push({ ParameterKey: key, ParameterValue: updates[key] });
continue;
}
if (key in previousValues) {
this.values[key] = previousValues[key];
this.apiParameters.push({ ParameterKey: key, UsePreviousValue: true });
continue;
}
if (formalParam.Default !== undefined) {
this.values[key] = formalParam.Default;
continue;
}
// Oh no
missingRequired.push(key);
}
if (missingRequired.length > 0) {
throw new Error(`The following CloudFormation Parameters are missing a value: ${missingRequired.join(', ')}`);
}
// Just append all supplied overrides that aren't really expected (this
// will fail CFN but maybe people made typos that they want to be notified
// of)
const unknownParam = ([key, _]) => this.formalParams[key] === undefined;
const hasValue = ([_, value]) => !!value;
for (const [key, value] of Object.entries(updates).filter(unknownParam).filter(hasValue)) {
this.values[key] = value;
this.apiParameters.push({ ParameterKey: key, ParameterValue: value });
}
}
/**
* Whether this set of parameter updates will change the actual stack values
*/
hasChanges(currentValues) {
// If any of the parameters are SSM parameters, deploying must always happen
// because we can't predict what the values will be. We will allow some
// parameters to opt out of this check by having a magic string in their description.
if (Object.values(this.formalParams).some(p => p.Type.startsWith('AWS::SSM::Parameter::') && !p.Description?.includes(cx_api_1.SSMPARAM_NO_INVALIDATE))) {
return 'ssm';
}
// Otherwise we're dirty if:
// - any of the existing values are removed, or changed
if (Object.entries(currentValues).some(([key, value]) => !(key in this.values) || value !== this.values[key])) {
return true;
}
// - any of the values we're setting are new
if (Object.keys(this.values).some(key => !(key in currentValues))) {
return true;
}
return false;
}
}
exports.ParameterValues = ParameterValues;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xvdWRmb3JtYXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjbG91ZGZvcm1hdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw0Q0FBeUQ7QUFFekQsZ0VBQTREO0FBQzVELDJDQUFzQztBQUN0QywrQ0FBdUQ7QUFrQnZEOzs7OztHQUtHO0FBQ0gsTUFBYSxtQkFBbUI7SUFDdkIsTUFBTSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQ3hCLEdBQW1CLEVBQUUsU0FBaUIsRUFBRSw0QkFBcUMsS0FBSztRQUVsRixJQUFJO1lBQ0YsTUFBTSxRQUFRLEdBQUcsTUFBTSxHQUFHLENBQUMsY0FBYyxDQUFDLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDOUUsT0FBTyxJQUFJLG1CQUFtQixDQUFDLEdBQUcsRUFBRSxTQUFTLEVBQUUsUUFBUSxDQUFDLE1BQU0sSUFBSSxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLHlCQUF5QixDQUFDLENBQUM7U0FDbEg7UUFBQyxPQUFPLENBQU0sRUFBRTtZQUNmLElBQUksQ0FBQyxDQUFDLElBQUksS0FBSyxpQkFBaUIsSUFBSSxDQUFDLENBQUMsT0FBTyxLQUFLLGlCQUFpQixTQUFTLGlCQUFpQixFQUFFO2dCQUM3RixPQUFPLElBQUksbUJBQW1CLENBQUMsR0FBRyxFQUFFLFNBQVMsRUFBRSxTQUFTLENBQUMsQ0FBQzthQUMzRDtZQUNELE1BQU0sQ0FBQyxDQUFDO1NBQ1Q7SUFDSCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLE1BQU0sQ0FBQyxZQUFZLENBQUMsR0FBbUIsRUFBRSxTQUFpQjtRQUMvRCxPQUFPLElBQUksbUJBQW1CLENBQUMsR0FBRyxFQUFFLFNBQVMsQ0FBQyxDQUFDO0lBQ2pELENBQUM7SUFFRDs7T0FFRztJQUNJLE1BQU0sQ0FBQyxxQkFBcUIsQ0FBQyxHQUFtQixFQUFFLFNBQWlCLEVBQUUsS0FBMkI7UUFD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O0lBQ0ksVUFBVSxDQUFDLGFBQXFDO1FBQ3JELDRFQUE0RTtRQUM1RSx1RUFBdUU7UUFDdkUscUZBQXFGO1FBQ3JGLElBQUksTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsdUJBQXVCLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxXQUFXLEVBQUUsUUFBUSxDQUFDLCtCQUFzQixDQUFDLENBQUMsRUFBRTtZQUM5SSxPQUFPLEtBQUssQ0FBQztTQUNkO1FBRUQsNEJBQTRCO1FBQzVCLHVEQUF1RDtRQUN2RCxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsYUFBYSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxHQUFHLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEtBQUssS0FBSyxJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUU7WUFDN0csT0FBTyxJQUFJLENBQUM7U0FDYjtRQUVELDRDQUE0QztRQUM1QyxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxHQUFHLElBQUksYUFBYSxDQUFDLENBQUMsRUFBRTtZQUNqRSxPQUFPLElBQUksQ0FBQztTQUNiO1FBRUQsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0NBQ0Y7QUE3RUQsMENBNkVDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgU1NNUEFSQU1fTk9fSU5WQUxJREFURSB9IGZyb20gJ0Bhd3MtY2RrL2N4LWFwaSc7XG5pbXBvcnQgeyBDbG91ZEZvcm1hdGlvbiB9IGZyb20gJ2F3cy1zZGsnO1xuaW1wb3J0IHsgU3RhY2tTdGF0dXMgfSBmcm9tICcuL2Nsb3VkZm9ybWF0aW9uL3N0YWNrLXN0YXR1cyc7XG5pbXBvcnQgeyBkZWJ1ZyB9IGZyb20gJy4uLy4uL2xvZ2dpbmcnO1xuaW1wb3J0IHsgZGVzZXJpYWxpemVTdHJ1Y3R1cmUgfSBmcm9tICcuLi8uLi9zZXJpYWxpemUnO1xuXG5leHBvcnQgdHlwZSBUZW1wbGF0ZSA9IHtcbiAgUGFyYW1ldGVycz86IFJlY29yZDxzdHJpbmcsIFRlbXBsYXRlUGFyYW1ldGVyPjtcbiAgW2tleTogc3RyaW5nXTogYW55O1xufTtcblxuaW50ZXJmYWNlIFRlbXBsYXRlUGFyYW1ldGVyIHtcbiAgVHlwZTogc3RyaW5nO1xuICBEZWZhdWx0PzogYW55O1xuICBEZXNjcmlwdGlvbj86IHN0cmluZztcbiAgW2tleTogc3RyaW5nXTogYW55O1xufVxuXG5leHBvcnQgdHlwZSBSZXNvdXJjZUlkZW50aWZpZXJQcm9wZXJ0aWVzID0gQ2xvdWRGb3JtYXRpb24uUmVzb3VyY2VJZGVudGlmaWVyUHJvcGVydGllcztcbmV4cG9ydCB0eXBlIFJlc291cmNlSWRlbnRpZmllclN1bW1hcmllcyA9IENsb3VkRm9ybWF0aW9uLlJlc291cmNlSWRlbnRpZmllclN1bW1hcmllcztcbmV4cG9ydCB0eXBlIFJlc291cmNlc1RvSW1wb3J0ID0gQ2xvdWRGb3JtYXRpb24uUmVzb3VyY2VzVG9JbXBvcnQ7XG5cbi8qKlxuICogUmVwcmVzZW50cyBhbiAoZXhpc3RpbmcpIFN0YWNrIGluIENsb3VkRm9ybWF0aW9uXG4gKlxuICogQnVuZGxlIGFuZCBjYWNoZSBzb21lIGluZm9ybWF0aW9uIHRoYXQgd2UgbmVlZCBkdXJpbmcgZGVwbG95bWVudCAoc28gd2UgZG9uJ3QgaGF2ZSB0byBtYWtlXG4gKiByZXBlYXRlZCBjYWxscyB0byBDbG91ZEZvcm1hdGlvbikuXG4gKi9cbmV4cG9ydCBjbGFzcyBDbG91ZEZvcm1hdGlvblN0YWNrIHtcbiAgcHVibGljIHN0YXRpYyBhc3luYyBsb29rdXAoXG4gICAgY2ZuOiBDbG91ZEZvcm1hdGlvbiwgc3RhY2tOYW1lOiBzdHJpbmcsIHJldHJpZXZlUHJvY2Vzc2VkVGVtcGxhdGU6IGJvb2xlYW4gPSBmYWxzZSxcbiAgKTogUHJvbWlzZTxDbG91ZEZvcm1hdGlvblN0YWNrPiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgY2ZuLmRlc2NyaWJlU3RhY2tzKHsgU3RhY2tOYW1lOiBzdGFja05hbWUgfSkucHJvbWlzZSgpO1xuICAgICAgcmV0dXJuIG5ldyBDbG91ZEZvcm1hdGlvblN0YWNrKGNmbiwgc3RhY2tOYW1lLCByZXNwb25zZS5TdGFja3MgJiYgcmVzcG9uc2UuU3RhY2tzWzBdLCByZXRyaWV2ZVByb2Nlc3NlZFRlbXBsYXRlKTtcbiAgICB9IGNhdGNoIChlOiBhbnkpIHtcbiAgICAgIGlmIChlLmNvZGUgPT09ICdWYWxpZGF0aW9uRXJyb3InICYmIGUubWVzc2FnZSA9PT0gYFN0YWNrIHdpdGggaWQgJHtzdGFja05hbWV9IGRvZXMgbm90IGV4aXN0YCkge1xuICAgICAgICByZXR1cm4gbmV3IENsb3VkRm9ybWF0aW9uU3RhY2soY2ZuLCBzdGFja05hbWUsIHVuZGVmaW5lZCk7XG4gICAgICB9XG4gICAgICB0aHJvdyBlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm4gYSBjb3B5IG9mIHRoZSBnaXZlbiBzdGFjayB0aGF0IGRvZXMgbm90IGV4aXN0XG4gICAqXG4gICAqIEl0J3MgYSBsaXR0bGUgc2lsbHkgdGhhdCBpdCBuZWVkcyBhcmd1bWVudHMgdG8gZG8gdGhhdCwgYnV0IHRoZXJlIHdlIGdvLlxuICAgKi9cbiAgcHVibGljIHN0YXRpYyBkb2VzTm90RXhpc3QoY2ZuOiBDbG91ZEZvcm1hdGlvbiwgc3RhY2tOYW1lOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gbmV3IENsb3VkRm9ybWF0aW9uU3RhY2soY2ZuLCBzdGFja05hbWUpO1xuICB9XG5cbiAgLyoqXG4gICAqIEZyb20gc3RhdGljIGluZm9ybWF0aW9uIChmb3IgdGVzdGluZylcbiAgICovXG4gIHB1YmxpYyBzdGF0aWMgZnJvbVN0YXRpY0luZm9ybWF0aW9uKGNmbjogQ2xvdWRGb3JtYXRpb24sIHN0YWNrTmFtZTogc3RyaW5nLCBzdGFjazogQ2xvdWRGb3JtYXRpb24uU3RhY2spIHtcbiAgICByZXR1cm4gbmV3IENsb3VkRm9ybWF0aW9uU3RhY2soY2ZuLCBzdGFja05hbWUsIHN0YWNrKTtcbiAgfVxuXG4gIHByaXZhdGUgX3RlbXBsYXRlOiBhbnk7XG5cbiAgcHJvdGVjdGVkIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgY2ZuOiBDbG91ZEZvcm1hdGlvbiwgcHVibGljIHJlYWRvbmx5IHN0YWNrTmFtZTogc3RyaW5nLCBwcml2YXRlIHJlYWRvbmx5IHN0YWNrPzogQ2xvdWRGb3JtYXRpb24uU3RhY2ssXG4gICAgcHJpdmF0ZSByZWFkb25seSByZXRyaWV2ZVByb2Nlc3NlZFRlbXBsYXRlOiBib29sZWFuID0gZmFsc2UsXG4gICkge1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHJpZXZlIHRoZSBzdGFjaydzIGRlcGxveWVkIHRlbXBsYXRlXG4gICAqXG4gICAqIENhY2hlZCwgc28gd2lsbCBvbmx5IGJlIHJldHJpZXZlZCBvbmNlLiBXaWxsIHJldHVybiBhbiBlbXB0eVxuICAgKiBzdHJ1Y3R1cmUgaWYgdGhlIHN0YWNrIGRvZXMgbm90IGV4aXN0LlxuICAgKi9cbiAgcHVibGljIGFzeW5jIHRlbXBsYXRlKCk6IFByb21pc2U8VGVtcGxhdGU+IHtcbiAgICBpZiAoIXRoaXMuZXhpc3RzKSB7XG4gICAgICByZXR1cm4ge307XG4gICAgfVxuXG4gICAgaWYgKHRoaXMuX3RlbXBsYXRlID09PSB1bmRlZmluZWQpIHtcbiAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgdGhpcy5jZm4uZ2V0VGVtcGxhdGUoe1xuICAgICAgICBTdGFja05hbWU6IHRoaXMuc3RhY2tOYW1lLFxuICAgICAgICBUZW1wbGF0ZVN0YWdlOiB0aGlzLnJldHJpZXZlUHJvY2Vzc2VkVGVtcGxhdGUgPyAnUHJvY2Vzc2VkJyA6ICdPcmlnaW5hbCcsXG4gICAgICB9KS5wcm9taXNlKCk7XG4gICAgICB0aGlzLl90ZW1wbGF0ZSA9IChyZXNwb25zZS5UZW1wbGF0ZUJvZHkgJiYgZGVzZXJpYWxpemVTdHJ1Y3R1cmUocmVzcG9uc2UuVGVtcGxhdGVCb2R5KSkgfHwge307XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl90ZW1wbGF0ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBXaGV0aGVyIHRoZSBzdGFjayBleGlzdHNcbiAgICovXG4gIHB1YmxpYyBnZXQgZXhpc3RzKCkge1xuICAgIHJldHVybiB0aGlzLnN0YWNrICE9PSB1bmRlZmluZWQ7XG4gIH1cblxuICAvKipcbiAgICogVGhlIHN0YWNrJ3MgSURcbiAgICpcbiAgICogVGhyb3dzIGlmIHRoZSBzdGFjayBkb2Vzbid0IGV4aXN0LlxuICAgKi9cbiAgcHVibGljIGdldCBzdGFja0lkKCkge1xuICAgIHRoaXMuYXNzZXJ0RXhpc3RzKCk7XG4gICAgcmV0dXJuIHRoaXMuc3RhY2shLlN0YWNrSWQhO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBzdGFjaydzIGN1cnJlbnQgb3V0cHV0c1xuICAgKlxuICAgKiBFbXB0eSBvYmplY3QgaWYgdGhlIHN0YWNrIGRvZXNuJ3QgZXhpc3RcbiAgICovXG4gIHB1YmxpYyBnZXQgb3V0cHV0cygpOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+IHtcbiAgICBpZiAoIXRoaXMuZXhpc3RzKSB7IHJldHVybiB7fTsgfVxuICAgIGNvbnN0IHJlc3VsdDogeyBbbmFtZTogc3RyaW5nXTogc3RyaW5nIH0gPSB7fTtcbiAgICAodGhpcy5zdGFjayEuT3V0cHV0cyB8fCBbXSkuZm9yRWFjaChvdXRwdXQgPT4ge1xuICAgICAgcmVzdWx0W291dHB1dC5PdXRwdXRLZXkhXSA9IG91dHB1dC5PdXRwdXRWYWx1ZSE7XG4gICAgfSk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgc3RhY2sncyBzdGF0dXNcbiAgICpcbiAgICogU3BlY2lhbCBzdGF0dXMgTk9UX0ZPVU5EIGlmIHRoZSBzdGFjayBkb2VzIG5vdCBleGlzdC5cbiAgICovXG4gIHB1YmxpYyBnZXQgc3RhY2tTdGF0dXMoKTogU3RhY2tTdGF0dXMge1xuICAgIGlmICghdGhpcy5leGlzdHMpIHtcbiAgICAgIHJldHVybiBuZXcgU3RhY2tTdGF0dXMoJ05PVF9GT1VORCcsICdTdGFjayBub3QgZm91bmQgZHVyaW5nIGxvb2t1cCcpO1xuICAgIH1cbiAgICByZXR1cm4gU3RhY2tTdGF0dXMuZnJvbVN0YWNrRGVzY3JpcHRpb24odGhpcy5zdGFjayEpO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBzdGFjaydzIGN1cnJlbnQgdGFnc1xuICAgKlxuICAgKiBFbXB0eSBsaXN0IG9mIHRoZSBzdGFjayBkb2VzIG5vdCBleGlzdFxuICAgKi9cbiAgcHVibGljIGdldCB0YWdzKCk6IENsb3VkRm9ybWF0aW9uLlRhZ3Mge1xuICAgIHJldHVybiB0aGlzLnN0YWNrPy5UYWdzIHx8IFtdO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybiB0aGUgbmFtZXMgb2YgYWxsIGN1cnJlbnQgcGFyYW1ldGVycyB0byB0aGUgc3RhY2tcbiAgICpcbiAgICogRW1wdHkgbGlzdCBpZiB0aGUgc3RhY2sgZG9lcyBub3QgZXhpc3QuXG4gICAqL1xuICBwdWJsaWMgZ2V0IHBhcmFtZXRlck5hbWVzKCk6IHN0cmluZ1tdIHtcbiAgICByZXR1cm4gT2JqZWN0LmtleXModGhpcy5wYXJhbWV0ZXJzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm4gdGhlIG5hbWVzIGFuZCB2YWx1ZXMgb2YgYWxsIGN1cnJlbnQgcGFyYW1ldGVycyB0byB0aGUgc3RhY2tcbiAgICpcbiAgICogRW1wdHkgb2JqZWN0IGlmIHRoZSBzdGFjayBkb2VzIG5vdCBleGlzdC5cbiAgICovXG4gIHB1YmxpYyBnZXQgcGFyYW1ldGVycygpOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+IHtcbiAgICBpZiAoIXRoaXMuZXhpc3RzKSB7IHJldHVybiB7fTsgfVxuICAgIGNvbnN0IHJldDogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHt9O1xuICAgIGZvciAoY29uc3QgcGFyYW0gb2YgdGhpcy5zdGFjayEuUGFyYW1ldGVycyA/PyBbXSkge1xuICAgICAgcmV0W3BhcmFtLlBhcmFtZXRlcktleSFdID0gcGFyYW0uUmVzb2x2ZWRWYWx1ZSA/PyBwYXJhbS5QYXJhbWV0ZXJWYWx1ZSE7XG4gICAgfVxuICAgIHJldHVybiByZXQ7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJuIHRoZSB0ZXJtaW5hdGlvbiBwcm90ZWN0aW9uIG9mIHRoZSBzdGFja1xuICAgKi9cbiAgcHVibGljIGdldCB0ZXJtaW5hdGlvblByb3RlY3Rpb24oKTogYm9vbGVhbiB8IHVuZGVmaW5lZCB7XG4gICAgcmV0dXJuIHRoaXMuc3RhY2s/LkVuYWJsZVRlcm1pbmF0aW9uUHJvdGVjdGlvbjtcbiAgfVxuXG4gIHByaXZhdGUgYXNzZXJ0RXhpc3RzKCkge1xuICAgIGlmICghdGhpcy5leGlzdHMpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgTm8gc3RhY2sgbmFtZWQgJyR7dGhpcy5zdGFja05hbWV9J2ApO1xuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIERlc2NyaWJlIGEgY2hhbmdlc2V0IGluIENsb3VkRm9ybWF0aW9uLCByZWdhcmRsZXNzIG9mIGl0cyBjdXJyZW50IHN0YXRlLlxuICpcbiAqIEBwYXJhbSBjZm4gICAgICAgICAgIGEgQ2xvdWRGb3JtYXRpb24gY2xpZW50XG4gKiBAcGFyYW0gc3RhY2tOYW1lICAgICB0aGUgbmFtZSBvZiB0aGUgU3RhY2sgdGhlIENoYW5nZVNldCBiZWxvbmdzIHRvXG4gKiBAcGFyYW0gY2hhbmdlU2V0TmFtZSB0aGUgbmFtZSBvZiB0aGUgQ2hhbmdlU2V0XG4gKiBAcGFyYW0gZmV0Y2hBbGwgICAgICBpZiB0cnVlLCBmZXRjaGVzIGFsbCBwYWdlcyBvZiB0aGUgY2hhbmdlIHNldCBkZXNjcmlwdGlvbi5cbiAqXG4gKiBAcmV0dXJucyAgICAgICBDbG91ZEZvcm1hdGlvbiBpbmZvcm1hdGlvbiBhYm91dCB0aGUgQ2hhbmdlU2V0XG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGRlc2NyaWJlQ2hhbmdlU2V0KFxuICBjZm46IENsb3VkRm9ybWF0aW9uLFxuICBzdGFja05hbWU6IHN0cmluZyxcbiAgY2hhbmdlU2V0TmFtZTogc3RyaW5nLFxuICB7IGZldGNoQWxsIH06IHsgZmV0Y2hBbGw6IGJvb2xlYW4gfSxcbik6IFByb21pc2U8Q2xvdWRGb3JtYXRpb24uRGVzY3JpYmVDaGFuZ2VTZXRPdXRwdXQ+IHtcbiAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBjZm4uZGVzY3JpYmVDaGFuZ2VTZXQoeyBTdGFja05hbWU6IHN0YWNrTmFtZSwgQ2hhbmdlU2V0TmFtZTogY2hhbmdlU2V0TmFtZSB9KS5wcm9taXNlKCk7XG5cbiAgLy8gSWYgZmV0Y2hBbGwgaXMgdHJ1ZSwgdHJhdmVyc2UgYWxsIHBhZ2VzIGZyb20gdGhlIGNoYW5nZSBzZXQgZGVzY3JpcHRpb24uXG4gIHdoaWxlIChmZXRjaEFsbCAmJiByZXNwb25zZS5OZXh0VG9rZW4gIT0gbnVsbCkge1xuICAgIGNvbnN0IG5leHRQYWdlID0gYXdhaXQgY2ZuLmRlc2NyaWJlQ2hhbmdlU2V0KHtcbiAgICAgIFN0YWNrTmFtZTogc3RhY2tOYW1lLFxuICAgICAgQ2hhbmdlU2V0TmFtZTogcmVzcG9uc2UuQ2hhbmdlU2V0SWQgPz8gY2hhbmdlU2V0TmFtZSxcbiAgICAgIE5leHRUb2tlbjogcmVzcG9uc2UuTmV4dFRva2VuLFxuICAgIH0pLnByb21pc2UoKTtcblxuICAgIC8vIENvbnNvbGlkYXRlIHRoZSBjaGFuZ2VzXG4gICAgaWYgKG5leHRQYWdlLkNoYW5nZXMgIT0gbnVsbCkge1xuICAgICAgcmVzcG9uc2UuQ2hhbmdlcyA9IHJlc3BvbnNlLkNoYW5nZXMgIT0gbnVsbFxuICAgICAgICA/IHJlc3BvbnNlLkNoYW5nZXMuY29uY2F0KG5leHRQYWdlLkNoYW5nZXMpXG4gICAgICAgIDogbmV4dFBhZ2UuQ2hhbmdlcztcbiAgICB9XG5cbiAgICAvLyBGb3J3YXJkIHRoZSBuZXcgTmV4dFRva2VuXG4gICAgcmVzcG9uc2UuTmV4dFRva2VuID0gbmV4dFBhZ2UuTmV4dFRva2VuO1xuICB9XG5cbiAgcmV0dXJuIHJlc3BvbnNlO1xufVxuXG4vKipcbiAqIFdhaXRzIGZvciBhIGZ1bmN0aW9uIHRvIHJldHVybiBub24tK3VuZGVmaW5lZCsgYmVmb3JlIHJldHVybmluZy5cbiAqXG4gKiBAcGFyYW0gdmFsdWVQcm92aWRlciBhIGZ1bmN0aW9uIHRoYXQgd2lsbCByZXR1cm4gYSB2YWx1ZSB0aGF0IGlzIG5vdCArdW5kZWZpbmVkKyBvbmNlIHRoZSB3YWl0IHNob3VsZCBiZSBvdmVyXG4gKiBAcGFyYW0gdGltZW91dCAgICAgdGhlIHRpbWUgdG8gd2FpdCBiZXR3ZWVuIHR3byBjYWxscyB0byArdmFsdWVQcm92aWRlcitcbiAqXG4gKiBAcmV0dXJucyAgICAgICB0aGUgdmFsdWUgdGhhdCB3YXMgcmV0dXJuZWQgYnkgK3ZhbHVlUHJvdmlkZXIrXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHdhaXRGb3I8VD4odmFsdWVQcm92aWRlcjogKCkgPT4gUHJvbWlzZTxUIHwgbnVsbCB8IHVuZGVmaW5lZD4sIHRpbWVvdXQ6IG51bWJlciA9IDUwMDApOiBQcm9taXNlPFQgfCB1bmRlZmluZWQ+IHtcbiAgd2hpbGUgKHRydWUpIHtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB2YWx1ZVByb3ZpZGVyKCk7XG4gICAgaWYgKHJlc3VsdCA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9IGVsc2UgaWYgKHJlc3VsdCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cbiAgICBhd2FpdCBuZXcgUHJvbWlzZShjYiA9PiBzZXRUaW1lb3V0KGNiLCB0aW1lb3V0KSk7XG4gIH1cbn1cblxuLyoqXG4gKiBXYWl0cyBmb3IgYSBDaGFuZ2VTZXQgdG8gYmUgYXZhaWxhYmxlIGZvciB0cmlnZ2VyaW5nIGEgU3RhY2tVcGRhdGUuXG4gKlxuICogV2lsbCByZXR1cm4gYSBjaGFuZ2VzZXQgdGhhdCBpcyBlaXRoZXIgcmVhZHkgdG8gYmUgZXhlY3V0ZWQgb3IgaGFzIG5vIGNoYW5nZXMuXG4gKiBXaWxsIHRocm93IGluIG90aGVyIGNhc2VzLlxuICpcbiAqIEBwYXJhbSBjZm4gICAgICAgICAgIGEgQ2xvdWRGb3JtYXRpb24gY2xpZW50XG4gKiBAcGFyYW0gc3RhY2tOYW1lICAgICB0aGUgbmFtZSBvZiB0aGUgU3RhY2sgdGhhdCB0aGUgQ2hhbmdlU2V0IGJlbG9uZ3MgdG9cbiAqIEBwYXJhbSBjaGFuZ2VTZXROYW1lIHRoZSBuYW1lIG9mIHRoZSBDaGFuZ2VTZXRcbiAqIEBwYXJhbSBmZXRjaEFsbCAgICAgIGlmIHRydWUsIGZldGNoZXMgYWxsIHBhZ2VzIG9mIHRoZSBDaGFuZ2VTZXQgYmVmb3JlIHJldHVybmluZy5cbiAqXG4gKiBAcmV0dXJucyAgICAgICB0aGUgQ2xvdWRGb3JtYXRpb24gZGVzY3JpcHRpb24gb2YgdGhlIENoYW5nZVNldFxuICovXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbWF4LWxlblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHdhaXRGb3JDaGFuZ2VTZXQoXG4gIGNmbjogQ2xvdWRGb3JtYXRpb24sXG4gIHN0YWNrTmFtZTogc3RyaW5nLFxuICBjaGFuZ2VTZXROYW1lOiBzdHJpbmcsXG4gIHsgZmV0Y2hBbGwgfTogeyBmZXRjaEFsbDogYm9vbGVhbiB9LFxuKTogUHJvbWlzZTxDbG91ZEZvcm1hdGlvbi5EZXNjcmliZUNoYW5nZVNldE91dHB1dD4ge1xuICBkZWJ1ZygnV2FpdGluZyBmb3IgY2hhbmdlc2V0ICVzIG9uIHN0YWNrICVzIHRvIGZpbmlzaCBjcmVhdGluZy4uLicsIGNoYW5nZVNldE5hbWUsIHN0YWNrTmFtZSk7XG4gIGNvbnN0IHJldCA9IGF3YWl0IHdhaXRGb3IoYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGRlc2NyaXB0aW9uID0gYXdhaXQgZGVzY3JpYmVDaGFuZ2VTZXQoY2ZuLCBzdGFja05hbWUsIGNoYW5nZVNldE5hbWUsIHsgZmV0Y2hBbGwgfSk7XG4gICAgLy8gVGhlIGZvbGxvd2luZyBkb2Vzbid0IHVzZSBhIHN3aXRjaCBiZWNhdXNlIHRzYyB3aWxsIG5vdCBhbGxvdyBmYWxsLXRocm91Z2gsIFVOTEVTUyBpdCBpcyBhbGxvd3NcbiAgICAvLyBFVkVSWVdIRVJFIHRoYXQgdXNlcyB0aGlzIGxpYnJhcnkgZGlyZWN0bHkgb3IgaW5kaXJlY3RseSwgd2hpY2ggaXMgdW5kZXNpcmFibGUuXG4gICAgaWYgKGRlc2NyaXB0aW9uLlN0YXR1cyA9PT0gJ0NSRUFURV9QRU5ESU5HJyB8fCBkZXNjcmlwdGlvbi5TdGF0dXMgPT09ICdDUkVBVEVfSU5fUFJPR1JFU1MnKSB7XG4gICAgICBkZWJ1ZygnQ2hhbmdlc2V0ICVzIG9uIHN0YWNrICVzIGlzIHN0aWxsIGNyZWF0aW5nJywgY2hhbmdlU2V0TmFtZSwgc3RhY2tOYW1lKTtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgaWYgKGRlc2NyaXB0aW9uLlN0YXR1cyA9PT0gJ0NSRUFURV9DT01QTEVURScgfHwgY2hhbmdlU2V0SGFzTm9DaGFuZ2VzKGRlc2NyaXB0aW9uKSkge1xuICAgICAgcmV0dXJuIGRlc2NyaXB0aW9uO1xuICAgIH1cblxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBtYXgtbGVuXG4gICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gY3JlYXRlIENoYW5nZVNldCAke2NoYW5nZVNldE5hbWV9IG9uICR7c3RhY2tOYW1lfTogJHtkZXNjcmlwdGlvbi5TdGF0dXMgfHwgJ05PX1NUQVRVUyd9LCAke2Rlc2NyaXB0aW9uLlN0YXR1c1JlYXNvbiB8fCAnbm8gcmVhc29uIHByb3ZpZGVkJ31gKTtcbiAgfSk7XG5cbiAgaWYgKCFyZXQpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0NoYW5nZSBzZXQgdG9vayB0b28gbG9uZyB0byBiZSBjcmVhdGVkOyBhYm9ydGluZycpO1xuICB9XG5cbiAgcmV0dXJuIHJldDtcbn1cblxuLyoqXG4gKiBSZXR1cm4gdHJ1ZSBpZiB0aGUgZ2l2ZW4gY2hhbmdlIHNldCBoYXMgbm8gY2hhbmdlc1xuICpcbiAqIFRoaXMgbXVzdCBiZSBkZXRlcm1pbmVkIGZyb20gdGhlIHN0YXR1cywgbm90IHRoZSAnQ2hhbmdlcycgYXJyYXkgb24gdGhlXG4gKiBvYmplY3Q7IHRoZSBsYXR0ZXIgY2FuIGJlIGVtcHR5IGJlY2F1c2Ugbm8gcmVzb3VyY2VzIHdlcmUgY2hhbmdlZCwgYnV0IGlmXG4gKiB0aGVyZSBhcmUgY2hhbmdlcyB0byBPdXRwdXRzLCB0aGUgY2hhbmdlIHNldCBjYW4gc3RpbGwgYmUgZXhlY3V0ZWQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjaGFuZ2VTZXRIYXNOb0NoYW5nZXMoZGVzY3JpcHRpb246IENsb3VkRm9ybWF0aW9uLkRlc2NyaWJlQ2hhbmdlU2V0T3V0cHV0KSB7XG4gIGNvbnN0IG5vQ2hhbmdlRXJyb3JQcmVmaXhlcyA9IFtcbiAgICAvLyBFcnJvciBtZXNzYWdlIGZvciBhIHJlZ3VsYXIgdGVtcGxhdGVcbiAgICAnVGhlIHN1Ym1pdHRlZCBpbmZvcm1hdGlvbiBkaWRuXFwndCBjb250YWluIGNoYW5nZXMuJyxcbiAgICAvLyBFcnJvciBtZXNzYWdlIHdoZW4gYSBUcmFuc2Zvcm0gaXMgaW52b2x2ZWQgKHNlZSAjMTA2NTApXG4gICAgJ05vIHVwZGF0ZXMgYXJlIHRvIGJlIHBlcmZvcm1lZC4nLFxuICBdO1xuXG4gIHJldHVybiBkZXNjcmlwdGlvbi5TdGF0dXMgPT09ICdGQUlMRUQnXG4gICAgJiYgbm9DaGFuZ2VFcnJvclByZWZpeGVzLnNvbWUocCA9PiAoZGVzY3JpcHRpb24uU3RhdHVzUmVhc29uID8/ICcnKS5zdGFydHNXaXRoKHApKTtcbn1cblxuLyoqXG4gKiBXYWl0cyBmb3IgYSBDbG91ZEZvcm1hdGlvbiBzdGFjayB0byBzdGFiaWxpemUgaW4gYSBjb21wbGV0ZS9hdmFpbGFibGUgc3RhdGVcbiAqIGFmdGVyIGEgZGVsZXRlIG9wZXJhdGlvbiBpcyBpc3N1ZWQuXG4gKlxuICogRmFpbHMgaWYgdGhlIHN0YWNrIGlzIGluIGEgRkFJTEVEIHN0YXRlLiBXaWxsIG5vdCBmYWlsIGlmIHRoZSBzdGFjayB3YXNcbiAqIGFscmVhZHkgZGVsZXRlZC5cbiAqXG4gKiBAcGFyYW0gY2ZuICAgICAgICBhIENsb3VkRm9ybWF0aW9uIGNsaWVudFxuICogQHBhcmFtIHN0YWNrTmFtZSAgICAgIHRoZSBuYW1lIG9mIHRoZSBzdGFjayB0byB3YWl0IGZvciBhZnRlciBhIGRlbGV0ZVxuICpcbiAqIEByZXR1cm5zICAgICB0aGUgQ2xvdWRGb3JtYXRpb24gZGVzY3JpcHRpb24gb2YgdGhlIHN0YWJpbGl6ZWQgc3RhY2sgYWZ0ZXIgdGhlIGRlbGV0ZSBhdHRlbXB0XG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiB3YWl0Rm9yU3RhY2tEZWxldGUoXG4gIGNmbjogQ2xvdWRGb3JtYXRpb24sXG4gIHN0YWNrTmFtZTogc3RyaW5nKTogUHJvbWlzZTxDbG91ZEZvcm1hdGlvblN0YWNrIHwgdW5kZWZpbmVkPiB7XG5cbiAgY29uc3Qgc3RhY2sgPSBhd2FpdCBzdGFiaWxpemVTdGFjayhjZm4sIHN0YWNrTmFtZSk7XG4gIGlmICghc3RhY2spIHsgcmV0dXJuIHVuZGVmaW5lZDsgfVxuXG4gIGNvbnN0IHN0YXR1cyA9IHN0YWNrLnN0YWNrU3RhdHVzO1xuICBpZiAoc3RhdHVzLmlzRmFpbHVyZSkge1xuICAgIHRocm93IG5ldyBFcnJvcihgVGhlIHN0YWNrIG5hbWVkICR7c3RhY2tOYW1lfSBpcyBpbiBhIGZhaWxlZCBzdGF0ZS4gWW91IG1heSBuZWVkIHRvIGRlbGV0ZSBpdCBmcm9tIHRoZSBBV1MgY29uc29sZSA6ICR7c3RhdHVzfWApO1xuICB9IGVsc2UgaWYgKHN0YXR1cy5pc0RlbGV0ZWQpIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG4gIHJldHVybiBzdGFjaztcbn1cblxuLyoqXG4gKiBXYWl0cyBmb3IgYSBDbG91ZEZvcm1hdGlvbiBzdGFjayB0byBzdGFiaWxpemUgaW4gYSBjb21wbGV0ZS9hdmFpbGFibGUgc3RhdGVcbiAqIGFmdGVyIGFuIHVwZGF0ZS9jcmVhdGUgb3BlcmF0aW9uIGlzIGlzc3VlZC5cbiAqXG4gKiBGYWlscyBpZiB0aGUgc3RhY2sgaXMgaW4gYSBGQUlMRUQgc3RhdGUsIFJPTExCQUNLIHN0YXRlLCBvciBERUxFVEVEIHN0YXRlLlxuICpcbiAqIEBwYXJhbSBjZm4gICAgICAgIGEgQ2xvdWRGb3JtYXRpb24gY2xpZW50XG4gKiBAcGFyYW0gc3RhY2tOYW1lICAgICAgdGhlIG5hbWUgb2YgdGhlIHN0YWNrIHRvIHdhaXQgZm9yIGFmdGVyIGFuIHVwZGF0ZVxuICpcbiAqIEByZXR1cm5zICAgICB0aGUgQ2xvdWRGb3JtYXRpb24gZGVzY3JpcHRpb24gb2YgdGhlIHN0YWJpbGl6ZWQgc3RhY2sgYWZ0ZXIgdGhlIHVwZGF0ZSBhdHRlbXB0XG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiB3YWl0Rm9yU3RhY2tEZXBsb3koXG4gIGNmbjogQ2xvdWRGb3JtYXRpb24sXG4gIHN0YWNrTmFtZTogc3RyaW5nKTogUHJvbWlzZTxDbG91ZEZvcm1hdGlvblN0YWNrIHwgdW5kZWZpbmVkPiB7XG5cbiAgY29uc3Qgc3RhY2sgPSBhd2FpdCBzdGFiaWxpemVTdGFjayhjZm4sIHN0YWNrTmFtZSk7XG4gIGlmICghc3RhY2spIHsgcmV0dXJuIHVuZGVmaW5lZDsgfVxuXG4gIGNvbnN0IHN0YXR1cyA9IHN0YWNrLnN0YWNrU3RhdHVzO1xuXG4gIGlmIChzdGF0dXMuaXNDcmVhdGlvbkZhaWx1cmUpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYFRoZSBzdGFjayBuYW1lZCAke3N0YWNrTmFtZX0gZmFpbGVkIGNyZWF0aW9uLCBpdCBtYXkgbmVlZCB0byBiZSBtYW51YWxseSBkZWxldGVkIGZyb20gdGhlIEFXUyBjb25zb2xlOiAke3N0YXR1c31gKTtcbiAgfSBlbHNlIGlmICghc3RhdHVzLmlzRGVwbG95U3VjY2Vzcykge1xuICAgIHRocm93IG5ldyBFcnJvcihgVGhlIHN0YWNrIG5hbWVkICR7c3RhY2tOYW1lfSBmYWlsZWQgdG8gZGVwbG95OiAke3N0YXR1c31gKTtcbiAgfVxuXG4gIHJldHVybiBzdGFjaztcbn1cblxuLyoqXG4gKiBXYWl0IGZvciBhIHN0YWNrIHRvIGJlY29tZSBzdGFibGUgKG5vIGxvbmdlciBfSU5fUFJPR1JFU1MpLCByZXR1cm5pbmcgaXRcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHN0YWJpbGl6ZVN0YWNrKGNmbjogQ2xvdWRGb3JtYXRpb24sIHN0YWNrTmFtZTogc3RyaW5nKSB7XG4gIGRlYnVnKCdXYWl0aW5nIGZvciBzdGFjayAlcyB0byBmaW5pc2ggY3JlYXRpbmcgb3IgdXBkYXRpbmcuLi4nLCBzdGFja05hbWUpO1xuICByZXR1cm4gd2FpdEZvcihhc3luYyAoKSA9PiB7XG4gICAgY29uc3Qgc3RhY2sgPSBhd2FpdCBDbG91ZEZvcm1hdGlvblN0YWNrLmxvb2t1cChjZm4sIHN0YWNrTmFtZSk7XG4gICAgaWYgKCFzdGFjay5leGlzdHMpIHtcbiAgICAgIGRlYnVnKCdTdGFjayAlcyBkb2VzIG5vdCBleGlzdCcsIHN0YWNrTmFtZSk7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgY29uc3Qgc3RhdHVzID0gc3RhY2suc3RhY2tTdGF0dXM7XG4gICAgaWYgKHN0YXR1cy5pc0luUHJvZ3Jlc3MpIHtcbiAgICAgIGRlYnVnKCdTdGFjayAlcyBoYXMgYW4gb25nb2luZyBvcGVyYXRpb24gaW4gcHJvZ3Jlc3MgYW5kIGlzIG5vdCBzdGFibGUgKCVzKScsIHN0YWNrTmFtZSwgc3RhdHVzKTtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfSBlbHNlIGlmIChzdGF0dXMuaXNSZXZpZXdJblByb2dyZXNzKSB7XG4gICAgICAvLyBUaGlzIG1heSBoYXBwZW4gaWYgYSBzdGFjayBjcmVhdGlvbiBvcGVyYXRpb24gaXMgaW50ZXJydXB0ZWQgYmVmb3JlIHRoZSBDaGFuZ2VTZXQgZXhlY3V0aW9uIHN0YXJ0cy4gUmVjb3ZlcmluZ1xuICAgICAgLy8gZnJvbSB0aGlzIHdvdWxkIHJlcXVpcmluZyBtYW51YWwgaW50ZXJ2ZW50aW9uIChkZWxldGluZyBvciBleGVjdXRpbmcgdGhlIHBlbmRpbmcgQ2hhbmdlU2V0KSwgYW5kIGZhaWxpbmcgdG8gZG9cbiAgICAgIC8vIHNvIHdpbGwgcmVzdWx0IGluIGFuIGVuZGxlc3Mgd2FpdCBoZXJlICh0aGUgQ2hhbmdlU2V0IHdvbnQgZGVsZXRlIG9yIGV4ZWN1dGUgaXRzZWxmKS4gSW5zdGVhZCBvZiBibG9ja2luZ1xuICAgICAgLy8gXCJmb3JldmVyXCIgd2UgcHJvY2VlZCBhcyBpZiB0aGUgc3RhY2sgd2FzIGV4aXN0aW5nIGFuZCBzdGFibGUuIElmIHRoZXJlIGlzIGEgY29uY3VycmVudCBvcGVyYXRpb24gdGhhdCBqdXN0XG4gICAgICAvLyBoYXNuJ3QgZmluaXNoZWQgcHJvY2VlZGluZyBqdXN0IHlldCwgZWl0aGVyIHRoaXMgb3BlcmF0aW9uIG9yIHRoZSBjb25jdXJyZW50IG9uZSBtYXkgZmFpbCBkdWUgdG8gdGhlIG90aGVyIG9uZVxuICAgICAgLy8gaGF2aW5nIG1hZGUgcHJvZ3Jlc3MuIFdoaWNoIGlzIGZpbmUuIEkgZ3Vlc3MuXG4gICAgICBkZWJ1ZygnU3RhY2sgJXMgaXMgaW4gUkVWSUVXX0lOX1BST0dSRVNTIHN0YXRlLiBDb25zaWRlcmluZyB0aGlzIGlzIGEgc3RhYmxlIHN0YXR1cyAoJXMpJywgc3RhY2tOYW1lLCBzdGF0dXMpO1xuICAgIH1cblxuICAgIHJldHVybiBzdGFjaztcbiAgfSk7XG59XG5cbi8qKlxuICogVGhlIHNldCBvZiAoZm9ybWFsKSBwYXJhbWV0ZXJzIHRoYXQgaGF2ZSBiZWVuIGRlY2xhcmVkIGluIGEgdGVtcGxhdGVcbiAqL1xuZXhwb3J0IGNsYXNzIFRlbXBsYXRlUGFyYW1ldGVycyB7XG4gIHB1YmxpYyBzdGF0aWMgZnJvbVRlbXBsYXRlKHRlbXBsYXRlOiBUZW1wbGF0ZSkge1xuICAgIHJldHVybiBuZXcgVGVtcGxhdGVQYXJhbWV0ZXJzKHRlbXBsYXRlLlBhcmFtZXRlcnMgfHwge30pO1xuICB9XG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSByZWFkb25seSBwYXJhbXM6IFJlY29yZDxzdHJpbmcsIFRlbXBsYXRlUGFyYW1ldGVyPikge1xuICB9XG5cbiAgLyoqXG4gICAqIENhbGN1bGF0ZSBzdGFjayBwYXJhbWV0ZXJzIHRvIHBhc3MgZnJvbSB0aGUgZ2l2ZW4gZGVzaXJlZCBwYXJhbWV0ZXIgdmFsdWVzXG4gICAqXG4gICAqIFdpbGwgdGhyb3cgaWYgcGFyYW1ldGVycyB3aXRob3V0IGEgRGVmYXVsdCB2YWx1ZSBvciBhIFByZXZpb3VzIHZhbHVlIGFyZSBub3RcbiAgICogc3VwcGxpZWQuXG4gICAqL1xuICBwdWJsaWMgc3VwcGx5QWxsKHVwZGF0ZXM6IFJlY29yZDxzdHJpbmcsIHN0cmluZyB8IHVuZGVmaW5lZD4pOiBQYXJhbWV0ZXJWYWx1ZXMge1xuICAgIHJldHVybiBuZXcgUGFyYW1ldGVyVmFsdWVzKHRoaXMucGFyYW1zLCB1cGRhdGVzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBGcm9tIHRoZSB0ZW1wbGF0ZSwgdGhlIGdpdmVuIGRlc2lyZWQgdmFsdWVzIGFuZCB0aGUgY3VycmVudCB2YWx1ZXMsIGNhbGN1bGF0ZSB0aGUgY2hhbmdlcyB0byB0aGUgc3RhY2sgcGFyYW1ldGVyc1xuICAgKlxuICAgKiBXaWxsIHRha2UgaW50byBhY2NvdW50IHBhcmFtZXRlcnMgYWxyZWFkeSBzZXQgb24gdGhlIHRlbXBsYXRlICh3aWxsIGVtaXRcbiAgICogJ1VzZVByZXZpb3VzVmFsdWU6IHRydWUnIGZvciB0aG9zZSB1bmxlc3MgdGhlIHZhbHVlIGlzIGNoYW5nZWQpLCBhbmQgd2lsbFxuICAgKiB0aHJvdyBpZiBwYXJhbWV0ZXJzIHdpdGhvdXQgYSBEZWZhdWx0IHZhbHVlIG9yIGEgUHJldmlvdXMgdmFsdWUgYXJlIG5vdFxuICAgKiBzdXBwbGllZC5cbiAgICovXG4gIHB1YmxpYyB1cGRhdGVFeGlzdGluZyh1cGRhdGVzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmcgfCB1bmRlZmluZWQ+LCBwcmV2aW91c1ZhbHVlc