UNPKG

aws-cdk

Version:

CDK Toolkit, the command line tool for CDK apps

437 lines 54.4 kB
"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