@enterthenamehere/esdoc
Version:
Good Documentation Generator For JavaScript, updated for new decade
856 lines (825 loc) • 95.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _CommentParser = _interopRequireDefault(require("../Parser/CommentParser.js"));
var _FileDoc = _interopRequireDefault(require("../Doc/FileDoc.js"));
var _ClassDoc = _interopRequireDefault(require("../Doc/ClassDoc.js"));
var _MethodDoc = _interopRequireDefault(require("../Doc/MethodDoc.js"));
var _ClassPropertyDoc = _interopRequireDefault(require("../Doc/ClassPropertyDoc"));
var _MemberDoc = _interopRequireDefault(require("../Doc/MemberDoc.js"));
var _FunctionDoc = _interopRequireDefault(require("../Doc/FunctionDoc.js"));
var _VariableDoc = _interopRequireDefault(require("../Doc/VariableDoc.js"));
var _AssignmentDoc = _interopRequireDefault(require("../Doc/AssignmentDoc.js"));
var _TypedefDoc = _interopRequireDefault(require("../Doc/TypedefDoc.js"));
var _ExternalDoc = _interopRequireDefault(require("../Doc/ExternalDoc.js"));
var _ASTUtil = _interopRequireDefault(require("../Util/ASTUtil.js"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var already = Symbol('already');
var debug = require('debug')('ESDoc:DocFactory');
/**
* Doc factory class.
*
* @example
* let factory = new DocFactory(ast, pathResolver);
* factory.push(node, parentNode);
* let results = factory.results;
*/
class DocFactory {
/**
* @type {DocObject[]}
*/
get results() {
return [...this._results];
}
/**
* create instance.
* @param {AST} ast - AST of source code.
* @param {PathResolver} pathResolver - path resolver of source code.
*/
constructor(ast, pathResolver) {
this._ast = ast;
this._pathResolver = pathResolver;
this._results = [];
this._processedClassNodes = [];
this._inspectExportDefaultDeclaration();
this._inspectExportNamedDeclaration();
// file doc
var doc = new _FileDoc.default(ast, ast, pathResolver, []);
this._results.push(doc.value);
// ast does not child, so only comment.
if (ast.program.body.length === 0 && ast.program.innerComments) {
var results = this._traverseComments(ast, null, ast.program.innerComments);
this._results.push(...results);
}
}
/**
* inspect ExportDefaultDeclaration.
*
* case1: separated export
*
* ```javascript
* class Foo {}
* export default Foo;
* ```
*
* case2: export instance(directly).
*
* ```javascript
* class Foo {}
* export default new Foo();
* ```
*
* case3: export instance(indirectly).
*
* ```javascript
* class Foo {}
* let foo = new Foo();
* export default foo;
* ```
*
* @private
* @todo support function export.
*/
_inspectExportDefaultDeclaration() {
var pseudoExportNodes = [];
for (var exportNode of this._ast.program.body) {
if (exportNode.type !== 'ExportDefaultDeclaration') continue;
var targetClassName = null;
var targetVariableName = null;
var pseudoClassExport = false;
switch (exportNode.declaration.type) {
case 'NewExpression':
if (exportNode.declaration.callee.type === 'Identifier') {
targetClassName = exportNode.declaration.callee.name;
} else if (exportNode.declaration.callee.type === 'MemberExpression') {
targetClassName = exportNode.declaration.callee.property.name;
} else {
targetClassName = '';
}
targetVariableName = targetClassName.replace(/^(?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/, c => {
return c.toLowerCase();
});
pseudoClassExport = true;
break;
case 'Identifier':
{
var varNode = _ASTUtil.default.findVariableDeclarationAndNewExpressionNode(exportNode.declaration.name, this._ast);
if (varNode) {
targetClassName = varNode.declarations[0].init.callee.name;
targetVariableName = exportNode.declaration.name;
pseudoClassExport = true;
_ASTUtil.default.sanitize(varNode);
} else {
targetClassName = exportNode.declaration.name;
pseudoClassExport = false;
}
break;
}
default:
debug("Unknown export declaration type. type = \"".concat(exportNode.declaration.type, "\""));
break;
}
var {
classNode,
exported
} = _ASTUtil.default.findClassDeclarationNode(targetClassName, this._ast);
if (classNode) {
if (!exported) {
var pseudoExportNode1 = this._copy(exportNode);
pseudoExportNode1.declaration = this._copy(classNode);
pseudoExportNode1.leadingComments = null;
pseudoExportNode1.declaration.__PseudoExport__ = pseudoClassExport;
pseudoExportNodes.push(pseudoExportNode1);
_ASTUtil.default.sanitize(classNode);
}
if (targetVariableName) {
var pseudoExportNode2 = this._copy(exportNode);
pseudoExportNode2.declaration = _ASTUtil.default.createVariableDeclarationAndNewExpressionNode(targetVariableName, targetClassName, exportNode.loc);
pseudoExportNodes.push(pseudoExportNode2);
}
_ASTUtil.default.sanitize(exportNode);
}
var functionNode = _ASTUtil.default.findFunctionDeclarationNode(exportNode.declaration.name, this._ast);
if (functionNode) {
var pseudoExportNode = this._copy(exportNode);
pseudoExportNode.declaration = this._copy(functionNode);
_ASTUtil.default.sanitize(exportNode);
_ASTUtil.default.sanitize(functionNode);
pseudoExportNodes.push(pseudoExportNode);
}
var variableNode = _ASTUtil.default.findVariableDeclarationNode(exportNode.declaration.name, this._ast);
if (variableNode) {
var _pseudoExportNode = this._copy(exportNode);
_pseudoExportNode.declaration = this._copy(variableNode);
_ASTUtil.default.sanitize(exportNode);
_ASTUtil.default.sanitize(variableNode);
pseudoExportNodes.push(_pseudoExportNode);
}
}
this._ast.program.body.push(...pseudoExportNodes);
}
/* eslint-disable max-statements */
/**
* inspect ExportNamedDeclaration.
*
* case1: separated export
*
* ```javascript
* class Foo {}
* export {Foo};
* ```
*
* case2: export instance(indirectly).
*
* ```javascript
* class Foo {}
* let foo = new Foo();
* export {foo};
* ```
*
* @private
* @todo support function export.
*/
_inspectExportNamedDeclaration() {
var pseudoExportNodes = [];
for (var exportNode of this._ast.program.body) {
if (exportNode.type !== 'ExportNamedDeclaration') continue;
if (exportNode.declaration && exportNode.declaration.type === 'VariableDeclaration') {
for (var declaration of exportNode.declaration.declarations) {
if (!declaration.init || declaration.init.type !== 'NewExpression') continue;
var {
classNode,
exported
} = _ASTUtil.default.findClassDeclarationNode(declaration.init.callee.name, this._ast);
if (classNode && !exported) {
var pseudoExportNode = this._copy(exportNode);
pseudoExportNode.declaration = this._copy(classNode);
pseudoExportNode.leadingComments = null;
pseudoExportNodes.push(pseudoExportNode);
pseudoExportNode.declaration.__PseudoExport__ = true;
_ASTUtil.default.sanitize(classNode);
}
}
continue;
}
for (var specifier of exportNode.specifiers) {
if (specifier.type !== 'ExportSpecifier') continue;
var targetClassName = null;
var pseudoClassExport = false;
var varNode = _ASTUtil.default.findVariableDeclarationAndNewExpressionNode(specifier.exported.name, this._ast);
if (varNode) {
targetClassName = varNode.declarations[0].init.callee.name;
pseudoClassExport = true;
var _pseudoExportNode2 = this._copy(exportNode);
_pseudoExportNode2.declaration = this._copy(varNode);
_pseudoExportNode2.specifiers = null;
pseudoExportNodes.push(_pseudoExportNode2);
_ASTUtil.default.sanitize(varNode);
} else {
targetClassName = specifier.exported.name;
pseudoClassExport = false;
}
var {
classNode: _classNode,
exported: _exported
} = _ASTUtil.default.findClassDeclarationNode(targetClassName, this._ast);
if (_classNode && !_exported) {
var _pseudoExportNode3 = this._copy(exportNode);
_pseudoExportNode3.declaration = this._copy(_classNode);
_pseudoExportNode3.leadingComments = null;
_pseudoExportNode3.specifiers = null;
_pseudoExportNode3.declaration.__PseudoExport__ = pseudoClassExport;
pseudoExportNodes.push(_pseudoExportNode3);
_ASTUtil.default.sanitize(_classNode);
}
var functionNode = _ASTUtil.default.findFunctionDeclarationNode(specifier.exported.name, this._ast);
if (functionNode) {
var _pseudoExportNode4 = this._copy(exportNode);
_pseudoExportNode4.declaration = this._copy(functionNode);
_pseudoExportNode4.leadingComments = null;
_pseudoExportNode4.specifiers = null;
_ASTUtil.default.sanitize(functionNode);
pseudoExportNodes.push(_pseudoExportNode4);
}
var variableNode = _ASTUtil.default.findVariableDeclarationNode(specifier.exported.name, this._ast);
if (variableNode) {
var _pseudoExportNode5 = this._copy(exportNode);
_pseudoExportNode5.declaration = this._copy(variableNode);
_pseudoExportNode5.leadingComments = null;
_pseudoExportNode5.specifiers = null;
_ASTUtil.default.sanitize(variableNode);
pseudoExportNodes.push(_pseudoExportNode5);
}
}
}
this._ast.program.body.push(...pseudoExportNodes);
}
/**
* push node, and factory processes node.
* @param {ASTNode} node - target node.
* @param {ASTNode} parentNode - parent node of target node.
*/
push(node, parentNode) {
if (node === this._ast) return;
if (node[already]) return;
var isLastNodeInParent = this._isLastNodeInParent(node, parentNode);
node[already] = true;
Reflect.defineProperty(node, 'parent', {
value: parentNode
});
// unwrap export declaration
if (['ExportDefaultDeclaration', 'ExportNamedDeclaration'].includes(node.type)) {
parentNode = node;
node = this._unwrapExportDeclaration(node);
if (!node) return;
node[already] = true;
Reflect.defineProperty(node, 'parent', {
value: parentNode
});
}
// if node has decorators, leading comments is attached to decorators.
if (node.decorators && node.decorators[0].leadingComments) {
if (!node.leadingComments || !node.leadingComments.length) {
node.leadingComments = node.decorators[0].leadingComments;
}
}
var results = [];
results = this._traverseComments(parentNode, node, node.leadingComments);
this._results.push(...results);
// for trailing comments.
// traverse with only last node, because prevent duplication of trailing comments.
if (node.trailingComments && isLastNodeInParent) {
results = this._traverseComments(parentNode, null, node.trailingComments);
this._results.push(...results);
}
}
/**
* traverse comments of node, and create doc object.
* @param {ASTNode|AST} parentNode - parent of target node.
* @param {?ASTNode} node - target node.
* @param {ASTNode[]} comments - comment nodes.
* @returns {DocObject[]} created doc objects.
* @private
*/
_traverseComments(parentNode, node, comments) {
if (!node) {
var virtualNode = {};
Reflect.defineProperty(virtualNode, 'parent', {
value: parentNode
});
node = virtualNode;
}
if (comments && comments.length) {
var temp = [];
for (var comment of comments) {
if (_CommentParser.default.isESDoc(comment)) temp.push(comment);
}
comments = temp;
} else {
comments = [];
}
if (comments.length === 0) {
comments = [{
type: 'CommentBlock',
value: '* @undocument'
}];
}
var results = [];
var lastComment = comments[comments.length - 1];
for (var _comment of comments) {
var tags = _CommentParser.default.parse(_comment);
var doc = null;
if (_comment === lastComment) {
if (node.declarations && node.declarations[0].id.type === "ArrayPattern") {
// HACK implementing multiple variables from array pattern. e.g. export cont [a, b] = arr
// Uses elementIndex which we add to node so VariableDoc knows which element to pick.
var length = node.declarations[0].id.elements.length;
for (var ii = 0; ii < length; ii = ii + 1) {
if (typeof node.elementIndex === 'undefined') {
Reflect.defineProperty(node, 'elementIndex', {
writable: true
});
}
node.elementIndex = ii;
doc = this._createDoc(node, tags);
if (doc) results.push(doc.value);
}
} else if (node.declarations && node.declarations[0].id.type === 'ObjectPattern') {
// HACK implementing multiple variables from object pattern. e.g. export const {a, b} = obj
// Uses propertyIndex which we add to node so VariableDoc knows which element to pick.
var _length = node.declarations[0].id.properties.length;
for (var _ii = 0; _ii < _length; _ii = _ii + 1) {
if (typeof node.propertyIndex === 'undefined') {
Reflect.defineProperty(node, 'propertyIndex', {
writable: true
});
}
node.propertyIndex = _ii;
doc = this._createDoc(node, tags);
if (doc) results.push(doc.value);
}
} else {
doc = this._createDoc(node, tags);
if (doc) results.push(doc.value);
}
} else {
var _virtualNode = {};
Reflect.defineProperty(_virtualNode, 'parent', {
value: parentNode
});
doc = this._createDoc(_virtualNode, tags);
if (doc) results.push(doc.value);
}
}
return results;
}
/**
* create Doc.
* @param {ASTNode} node - target node.
* @param {Tag[]} tags - tags of target node.
* @returns {AbstractDoc} created Doc.
* @private
*/
_createDoc(node, tags) {
var result = this._decideType(tags, node);
var type = result.type;
node = result.node;
if (!type) return null;
if (type === 'Class') {
this._processedClassNodes.push(node);
}
var Clazz = null;
/* eslint-disable max-statements-per-line */
switch (type) {
case 'Class':
Clazz = _ClassDoc.default;
break;
case 'Method':
Clazz = _MethodDoc.default;
break;
case 'ClassProperty':
Clazz = _ClassPropertyDoc.default;
break;
case 'Member':
Clazz = _MemberDoc.default;
break;
case 'Function':
Clazz = _FunctionDoc.default;
break;
case 'Variable':
Clazz = _VariableDoc.default;
break;
case 'Assignment':
Clazz = _AssignmentDoc.default;
break;
case 'Typedef':
Clazz = _TypedefDoc.default;
break;
case 'External':
Clazz = _ExternalDoc.default;
break;
default:
throw new Error("unexpected type: ".concat(type));
}
if (!Clazz) return null;
if (!node.type) node.type = type;
return new Clazz(this._ast, node, this._pathResolver, tags);
}
/**
* decide Doc type by using tags and node.
* @param {Tag[]} tags - tags of node.
* @param {ASTNode} node - target node.
* @returns {{type: ?string, node: ?ASTNode}} decided type.
* @private
*/
_decideType(tags, node) {
var type = null;
for (var tag of tags) {
var tagName = tag.tagName;
/* eslint-disable default-case */
switch (tagName) {
case '@typedef':
type = 'Typedef';
break;
case '@external':
type = 'External';
break;
}
}
if (type) return {
type,
node
};
if (!node) return {
type,
node
};
/* eslint-disable default-case */
switch (node.type) {
case 'ClassDeclaration':
return this._decideClassDeclarationType(node);
case 'ClassMethod': // intended fallthrough
case 'ClassPrivateMethod':
return this._decideMethodDefinitionType(node);
case 'ClassProperty': // intended fallthrough
case 'ClassPrivateProperty':
return this._decideClassPropertyType(node);
case 'ExpressionStatement':
return this._decideExpressionStatementType(node);
case 'FunctionDeclaration':
return this._decideFunctionDeclarationType(node);
case 'FunctionExpression':
return this._decideFunctionExpressionType(node);
case 'VariableDeclaration':
return this._decideVariableType(node);
case 'AssignmentExpression':
return this._decideAssignmentType(node);
case 'ArrowFunctionExpression':
return this._decideArrowFunctionExpressionType(node);
}
return {
type: null,
node: null
};
}
/**
* decide Doc type from class declaration node.
* @param {ASTNode} node - target node that is class declaration node.
* @returns {{type: string, node: ASTNode}} decided type.
* @private
*/
_decideClassDeclarationType(node) {
if (!this._isTopDepthInBody(node, this._ast.program.body)) return {
type: null,
node: null
};
return {
type: 'Class',
node: node
};
}
/**
* decide Doc type from method definition node.
* @param {ASTNode} node - target node that is method definition node.
* @returns {{type: ?string, node: ?ASTNode}} decided type.
* @private
*/
_decideMethodDefinitionType(node) {
var classNode = this._findUp(node, ['ClassDeclaration', 'ClassExpression']);
if (this._processedClassNodes.includes(classNode)) {
return {
type: 'Method',
node: node
};
}
debug('This method is not in class', node);
return {
type: null,
node: null
};
}
/**
* decide Doc type from class property node.
* @param {ASTNode} node - target node that is class property node.
* @returns {{type: ?string, node: ?ASTNode}} decided type.
* @private
*/
_decideClassPropertyType(node) {
var classNode = this._findUp(node, ['ClassDeclaration', 'ClassExpression']);
if (this._processedClassNodes.includes(classNode)) {
return {
type: 'ClassProperty',
node: node
};
}
debug('This class property is not in class', node);
return {
type: null,
node: null
};
}
/**
* decide Doc type from function declaration node.
* @param {ASTNode} node - target node that is function declaration node.
* @returns {{type: string, node: ASTNode}} decided type.
* @private
*/
_decideFunctionDeclarationType(node) {
if (!this._isTopDepthInBody(node, this._ast.program.body)) return {
type: null,
node: null
};
return {
type: 'Function',
node: node
};
}
/**
* decide Doc type from function expression node.
* babylon 6.11.2 judges`export default async function foo(){}` to be `FunctionExpression`.
* I expect `FunctionDeclaration`. this behavior may be bug of babylon.
* for now, workaround for it with this method.
* @param {ASTNode} node - target node that is function expression node.
* @returns {{type: string, node: ASTNode}} decided type.
* @private
* @todo inspect with newer babylon.
*/
_decideFunctionExpressionType(node) {
if (!node.async) return {
type: null,
node: null
};
if (!this._isTopDepthInBody(node, this._ast.program.body)) return {
type: null,
node: null
};
return {
type: 'Function',
node: node
};
}
/**
* decide Doc type from arrow function expression node.
* @param {ASTNode} node - target node that is arrow function expression node.
* @returns {{type: string, node: ASTNode}} decided type.
* @private
*/
_decideArrowFunctionExpressionType(node) {
if (!this._isTopDepthInBody(node, this._ast.program.body)) return {
type: null,
node: null
};
return {
type: 'Function',
node: node
};
}
/**
* decide Doc type from expression statement node.
* @param {ASTNode} node - target node that is expression statement node.
* @returns {{type: ?string, node: ?ASTNode}} decided type.
* @private
*/
_decideExpressionStatementType(node) {
var isTop = this._isTopDepthInBody(node, this._ast.program.body);
Reflect.defineProperty(node.expression, 'parent', {
value: node
});
node = node.expression;
node[already] = true;
var innerType = '';
var innerNode = [];
if (!node.right) return {
type: null,
node: null
};
switch (node.right.type) {
case 'FunctionExpression':
innerType = 'Function';
break;
case 'ClassExpression':
innerType = 'Class';
break;
default:
if (node.left.type === 'MemberExpression' && node.left.object.type === 'ThisExpression') {
var classNode = this._findUp(node, ['ClassExpression', 'ClassDeclaration']);
if (!this._processedClassNodes.includes(classNode)) {
debug('This member is not in class.', this._pathResolver.filePath, node);
return {
type: null,
node: null
};
}
return {
type: 'Member',
node: node
};
}
return {
type: null,
node: null
};
}
if (!isTop) return {
type: null,
node: null
};
if (!node.left.id || !node.left.property) {
// TODO: AssignmentExpression's left doesn't have id or property. Check why and what are the consequences.
return {
type: null,
node: null
};
}
/* eslint-disable prefer-const */
innerNode = node.right;
innerNode.id = this._copy(node.left.id || node.left.property);
Reflect.defineProperty(innerNode, 'parent', {
value: node
});
innerNode[already] = true;
return {
type: innerType,
node: innerNode
};
}
/**
* decide Doc type from variable node.
* @param {ASTNode} node - target node that is variable node.
* @returns {{type: string, node: ASTNode}} decided type.
* @private
*/
_decideVariableType(node) {
if (!this._isTopDepthInBody(node, this._ast.program.body)) return {
type: null,
node: null
};
var innerType = null;
var innerNode = null;
if (!node.declarations[0].init) return {
type: innerType,
node: innerNode
};
switch (node.declarations[0].init.type) {
case 'FunctionExpression':
innerType = 'Function';
break;
case 'ClassExpression':
innerType = 'Class';
break;
case 'ArrowFunctionExpression':
innerType = 'Function';
break;
default:
return {
type: 'Variable',
node: node
};
}
innerNode = node.declarations[0].init;
innerNode.id = this._copy(node.declarations[0].id);
Reflect.defineProperty(innerNode, 'parent', {
value: node
});
innerNode[already] = true;
return {
type: innerType,
node: innerNode
};
}
/**
* decide Doc type from assignment node.
* @param {ASTNode} node - target node that is assignment node.
* @returns {{type: string, node: ASTNode}} decided type.
* @private
*/
_decideAssignmentType(node) {
if (!this._isTopDepthInBody(node, this._ast.program.body)) return {
type: null,
node: null
};
var innerType = '';
var innerNode = [];
switch (node.right.type) {
case 'FunctionExpression':
innerType = 'Function';
break;
case 'ClassExpression':
innerType = 'Class';
break;
default:
return {
type: 'Assignment',
node: node
};
}
/* eslint-disable prefer-const */
innerNode = node.right;
innerNode.id = this._copy(node.left.id || node.left.property);
Reflect.defineProperty(innerNode, 'parent', {
value: node
});
innerNode[already] = true;
return {
type: innerType,
node: innerNode
};
}
/**
* unwrap exported node.
* @param {ASTNode} node - target node that is export declaration node.
* @returns {ASTNode|null} unwrapped child node of exported node.
* @private
*/
_unwrapExportDeclaration(node) {
// e.g. `export A from './A.js'` has not declaration
if (!node.declaration) return null;
var exportedASTNode = node.declaration;
if (!exportedASTNode.leadingComments) exportedASTNode.leadingComments = [];
exportedASTNode.leadingComments.push(...(node.leadingComments || []));
if (!exportedASTNode.trailingComments) exportedASTNode.trailingComments = [];
exportedASTNode.trailingComments.push(...(node.trailingComments || []));
return exportedASTNode;
}
/**
* judge node is last in parent.
* @param {ASTNode} node - target node.
* @param {ASTNode} parentNode - target parent node.
* @returns {boolean} if true, the node is last in parent.
* @private
*/
_isLastNodeInParent(node, parentNode) {
if (parentNode && parentNode.body) {
var lastNode = parentNode.body[parentNode.body.length - 1];
return node === lastNode;
}
return false;
}
/**
* judge node is top in body.
* @param {ASTNode} node - target node.
* @param {ASTNode[]} body - target body node.
* @returns {boolean} if true, the node is top in body.
* @private
*/
_isTopDepthInBody(node, body) {
if (!body) return false;
if (!Array.isArray(body)) return false;
var parentNode = node.parent;
if (['ExportDefaultDeclaration', 'ExportNamedDeclaration'].includes(parentNode.type)) {
node = parentNode;
}
for (var _node of body) {
if (node === _node) return true;
}
return false;
}
/**
* deep copy object.
* @param {Object} obj - target object.
* @return {Object} copied object.
* @private
*/
_copy(obj) {
return JSON.parse(JSON.stringify(obj));
}
/**
* find node while goes up.
* @param {ASTNode} node - start node.
* @param {string[]} types - ASTNode types.
* @returns {ASTNode|null} found first node.
* @private
*/
_findUp(node, types) {
var parent = node.parent;
while (parent) {
if (types.includes(parent.type)) return parent;
parent = parent.parent;
}
return null;
}
}
exports.default = DocFactory;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfQ29tbWVudFBhcnNlciIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJyZXF1aXJlIiwiX0ZpbGVEb2MiLCJfQ2xhc3NEb2MiLCJfTWV0aG9kRG9jIiwiX0NsYXNzUHJvcGVydHlEb2MiLCJfTWVtYmVyRG9jIiwiX0Z1bmN0aW9uRG9jIiwiX1ZhcmlhYmxlRG9jIiwiX0Fzc2lnbm1lbnREb2MiLCJfVHlwZWRlZkRvYyIsIl9FeHRlcm5hbERvYyIsIl9BU1RVdGlsIiwib2JqIiwiX19lc01vZHVsZSIsImRlZmF1bHQiLCJhbHJlYWR5IiwiU3ltYm9sIiwiZGVidWciLCJEb2NGYWN0b3J5IiwicmVzdWx0cyIsIl9yZXN1bHRzIiwiY29uc3RydWN0b3IiLCJhc3QiLCJwYXRoUmVzb2x2ZXIiLCJfYXN0IiwiX3BhdGhSZXNvbHZlciIsIl9wcm9jZXNzZWRDbGFzc05vZGVzIiwiX2luc3BlY3RFeHBvcnREZWZhdWx0RGVjbGFyYXRpb24iLCJfaW5zcGVjdEV4cG9ydE5hbWVkRGVjbGFyYXRpb24iLCJkb2MiLCJGaWxlRG9jIiwicHVzaCIsInZhbHVlIiwicHJvZ3JhbSIsImJvZHkiLCJsZW5ndGgiLCJpbm5lckNvbW1lbnRzIiwiX3RyYXZlcnNlQ29tbWVudHMiLCJwc2V1ZG9FeHBvcnROb2RlcyIsImV4cG9ydE5vZGUiLCJ0eXBlIiwidGFyZ2V0Q2xhc3NOYW1lIiwidGFyZ2V0VmFyaWFibGVOYW1lIiwicHNldWRvQ2xhc3NFeHBvcnQiLCJkZWNsYXJhdGlvbiIsImNhbGxlZSIsIm5hbWUiLCJwcm9wZXJ0eSIsInJlcGxhY2UiLCJjIiwidG9Mb3dlckNhc2UiLCJ2YXJOb2RlIiwiQVNUVXRpbCIsImZpbmRWYXJpYWJsZURlY2xhcmF0aW9uQW5kTmV3RXhwcmVzc2lvbk5vZGUiLCJkZWNsYXJhdGlvbnMiLCJpbml0Iiwic2FuaXRpemUiLCJjb25jYXQiLCJjbGFzc05vZGUiLCJleHBvcnRlZCIsImZpbmRDbGFzc0RlY2xhcmF0aW9uTm9kZSIsInBzZXVkb0V4cG9ydE5vZGUxIiwiX2NvcHkiLCJsZWFkaW5nQ29tbWVudHMiLCJfX1BzZXVkb0V4cG9ydF9fIiwicHNldWRvRXhwb3J0Tm9kZTIiLCJjcmVhdGVWYXJpYWJsZURlY2xhcmF0aW9uQW5kTmV3RXhwcmVzc2lvbk5vZGUiLCJsb2MiLCJmdW5jdGlvbk5vZGUiLCJmaW5kRnVuY3Rpb25EZWNsYXJhdGlvbk5vZGUiLCJwc2V1ZG9FeHBvcnROb2RlIiwidmFyaWFibGVOb2RlIiwiZmluZFZhcmlhYmxlRGVjbGFyYXRpb25Ob2RlIiwic3BlY2lmaWVyIiwic3BlY2lmaWVycyIsIm5vZGUiLCJwYXJlbnROb2RlIiwiaXNMYXN0Tm9kZUluUGFyZW50IiwiX2lzTGFzdE5vZGVJblBhcmVudCIsIlJlZmxlY3QiLCJkZWZpbmVQcm9wZXJ0eSIsImluY2x1ZGVzIiwiX3Vud3JhcEV4cG9ydERlY2xhcmF0aW9uIiwiZGVjb3JhdG9ycyIsInRyYWlsaW5nQ29tbWVudHMiLCJjb21tZW50cyIsInZpcnR1YWxOb2RlIiwidGVtcCIsImNvbW1lbnQiLCJDb21tZW50UGFyc2VyIiwiaXNFU0RvYyIsImxhc3RDb21tZW50IiwidGFncyIsInBhcnNlIiwiaWQiLCJlbGVtZW50cyIsImlpIiwiZWxlbWVudEluZGV4Iiwid3JpdGFibGUiLCJfY3JlYXRlRG9jIiwicHJvcGVydGllcyIsInByb3BlcnR5SW5kZXgiLCJyZXN1bHQiLCJfZGVjaWRlVHlwZSIsIkNsYXp6IiwiQ2xhc3NEb2MiLCJNZXRob2REb2MiLCJDbGFzc1Byb3BlcnR5IiwiTWVtYmVyRG9jIiwiRnVuY3Rpb25Eb2MiLCJWYXJpYWJsZURvYyIsIkFzc2lnbm1lbnREb2MiLCJUeXBlZGVmRG9jIiwiRXh0ZXJuYWxEb2MiLCJFcnJvciIsInRhZyIsInRhZ05hbWUiLCJfZGVjaWRlQ2xhc3NEZWNsYXJhdGlvblR5cGUiLCJfZGVjaWRlTWV0aG9kRGVmaW5pdGlvblR5cGUiLCJfZGVjaWRlQ2xhc3NQcm9wZXJ0eVR5cGUiLCJfZGVjaWRlRXhwcmVzc2lvblN0YXRlbWVudFR5cGUiLCJfZGVjaWRlRnVuY3Rpb25EZWNsYXJhdGlvblR5cGUiLCJfZGVjaWRlRnVuY3Rpb25FeHByZXNzaW9uVHlwZSIsIl9kZWNpZGVWYXJpYWJsZVR5cGUiLCJfZGVjaWRlQXNzaWdubWVudFR5cGUiLCJfZGVjaWRlQXJyb3dGdW5jdGlvbkV4cHJlc3Npb25UeXBlIiwiX2lzVG9wRGVwdGhJbkJvZHkiLCJfZmluZFVwIiwiYXN5bmMiLCJpc1RvcCIsImV4cHJlc3Npb24iLCJpbm5lclR5cGUiLCJpbm5lck5vZGUiLCJyaWdodCIsImxlZnQiLCJvYmplY3QiLCJmaWxlUGF0aCIsImV4cG9ydGVkQVNUTm9kZSIsImxhc3ROb2RlIiwiQXJyYXkiLCJpc0FycmF5IiwicGFyZW50IiwiX25vZGUiLCJKU09OIiwic3RyaW5naWZ5IiwidHlwZXMiLCJleHBvcnRzIl0sInNvdXJjZXMiOlsiLi4vLi4vc3JjL0ZhY3RvcnkvRG9jRmFjdG9yeS5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgQ29tbWVudFBhcnNlciBmcm9tICcuLi9QYXJzZXIvQ29tbWVudFBhcnNlci5qcyc7XHJcbmltcG9ydCBGaWxlRG9jIGZyb20gJy4uL0RvYy9GaWxlRG9jLmpzJztcclxuaW1wb3J0IENsYXNzRG9jIGZyb20gJy4uL0RvYy9DbGFzc0RvYy5qcyc7XHJcbmltcG9ydCBNZXRob2REb2MgZnJvbSAnLi4vRG9jL01ldGhvZERvYy5qcyc7XHJcbmltcG9ydCBDbGFzc1Byb3BlcnR5IGZyb20gJy4uL0RvYy9DbGFzc1Byb3BlcnR5RG9jJztcclxuaW1wb3J0IE1lbWJlckRvYyBmcm9tICcuLi9Eb2MvTWVtYmVyRG9jLmpzJztcclxuaW1wb3J0IEZ1bmN0aW9uRG9jIGZyb20gJy4uL0RvYy9GdW5jdGlvbkRvYy5qcyc7XHJcbmltcG9ydCBWYXJpYWJsZURvYyBmcm9tICcuLi9Eb2MvVmFyaWFibGVEb2MuanMnO1xyXG5pbXBvcnQgQXNzaWdubWVudERvYyBmcm9tICcuLi9Eb2MvQXNzaWdubWVudERvYy5qcyc7XHJcbmltcG9ydCBUeXBlZGVmRG9jIGZyb20gJy4uL0RvYy9UeXBlZGVmRG9jLmpzJztcclxuaW1wb3J0IEV4dGVybmFsRG9jIGZyb20gJy4uL0RvYy9FeHRlcm5hbERvYy5qcyc7XHJcbmltcG9ydCBBU1RVdGlsIGZyb20gJy4uL1V0aWwvQVNUVXRpbC5qcyc7XHJcblxyXG5jb25zdCBhbHJlYWR5ID0gU3ltYm9sKCdhbHJlYWR5Jyk7XHJcbmNvbnN0IGRlYnVnID0gcmVxdWlyZSgnZGVidWcnKSgnRVNEb2M6RG9jRmFjdG9yeScpO1xyXG5cclxuLyoqXHJcbiAqIERvYyBmYWN0b3J5IGNsYXNzLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKiBsZXQgZmFjdG9yeSA9IG5ldyBEb2NGYWN0b3J5KGFzdCwgcGF0aFJlc29sdmVyKTtcclxuICogZmFjdG9yeS5wdXNoKG5vZGUsIHBhcmVudE5vZGUpO1xyXG4gKiBsZXQgcmVzdWx0cyA9IGZhY3RvcnkucmVzdWx0cztcclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGNsYXNzIERvY0ZhY3Rvcnkge1xyXG4gIC8qKlxyXG4gICAqIEB0eXBlIHtEb2NPYmplY3RbXX1cclxuICAgKi9cclxuICBnZXQgcmVzdWx0cygpIHtcclxuICAgIHJldHVybiBbLi4udGhpcy5fcmVzdWx0c107XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBjcmVhdGUgaW5zdGFuY2UuXHJcbiAgICogQHBhcmFtIHtBU1R9IGFzdCAtIEFTVCBvZiBzb3VyY2UgY29kZS5cclxuICAgKiBAcGFyYW0ge1BhdGhSZXNvbHZlcn0gcGF0aFJlc29sdmVyIC0gcGF0aCByZXNvbHZlciBvZiBzb3VyY2UgY29kZS5cclxuICAgKi9cclxuICBjb25zdHJ1Y3Rvcihhc3QsIHBhdGhSZXNvbHZlcikge1xyXG4gICAgdGhpcy5fYXN0ID0gYXN0O1xyXG4gICAgdGhpcy5fcGF0aFJlc29sdmVyID0gcGF0aFJlc29sdmVyO1xyXG4gICAgdGhpcy5fcmVzdWx0cyA9IFtdO1xyXG4gICAgdGhpcy5fcHJvY2Vzc2VkQ2xhc3NOb2RlcyA9IFtdO1xyXG5cclxuICAgIHRoaXMuX2luc3BlY3RFeHBvcnREZWZhdWx0RGVjbGFyYXRpb24oKTtcclxuICAgIHRoaXMuX2luc3BlY3RFeHBvcnROYW1lZERlY2xhcmF0aW9uKCk7XHJcblxyXG4gICAgLy8gZmlsZSBkb2NcclxuICAgIGNvbnN0IGRvYyA9IG5ldyBGaWxlRG9jKGFzdCwgYXN0LCBwYXRoUmVzb2x2ZXIsIFtdKTtcclxuICAgIHRoaXMuX3Jlc3VsdHMucHVzaChkb2MudmFsdWUpO1xyXG5cclxuICAgIC8vIGFzdCBkb2VzIG5vdCBjaGlsZCwgc28gb25seSBjb21tZW50LlxyXG4gICAgaWYgKGFzdC5wcm9ncmFtLmJvZHkubGVuZ3RoID09PSAwICYmIGFzdC5wcm9ncmFtLmlubmVyQ29tbWVudHMpIHtcclxuICAgICAgY29uc3QgcmVzdWx0cyA9IHRoaXMuX3RyYXZlcnNlQ29tbWVudHMoYXN0LCBudWxsLCBhc3QucHJvZ3JhbS5pbm5lckNvbW1lbnRzKTtcclxuICAgICAgdGhpcy5fcmVzdWx0cy5wdXNoKC4uLnJlc3VsdHMpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogaW5zcGVjdCBFeHBvcnREZWZhdWx0RGVjbGFyYXRpb24uXHJcbiAgICpcclxuICAgKiBjYXNlMTogc2VwYXJhdGVkIGV4cG9ydFxyXG4gICAqXHJcbiAgICogYGBgamF2YXNjcmlwdFxyXG4gICAqIGNsYXNzIEZvbyB7fVxyXG4gICAqIGV4cG9ydCBkZWZhdWx0IEZvbztcclxuICAgKiBgYGBcclxuICAgKlxyXG4gICAqIGNhc2UyOiBleHBvcnQgaW5zdGFuY2UoZGlyZWN0bHkpLlxyXG4gICAqXHJcbiAgICogYGBgamF2YXNjcmlwdFxyXG4gICAqIGNsYXNzIEZvbyB7fVxyXG4gICAqIGV4cG9ydCBkZWZhdWx0IG5ldyBGb28oKTtcclxuICAgKiBgYGBcclxuICAgKlxyXG4gICAqIGNhc2UzOiBleHBvcnQgaW5zdGFuY2UoaW5kaXJlY3RseSkuXHJcbiAgICpcclxuICAgKiBgYGBqYXZhc2NyaXB0XHJcbiAgICogY2xhc3MgRm9vIHt9XHJcbiAgICogbGV0IGZvbyA9IG5ldyBGb28oKTtcclxuICAgKiBleHBvcnQgZGVmYXVsdCBmb287XHJcbiAgICogYGBgXHJcbiAgICpcclxuICAgKiBAcHJpdmF0ZVxyXG4gICAqIEB0b2RvIHN1cHBvcnQgZnVuY3Rpb24gZXhwb3J0LlxyXG4gICAqL1xyXG4gIF9pbnNwZWN0RXhwb3J0RGVmYXVsdERlY2xhcmF0aW9uKCkge1xyXG4gICAgY29uc3QgcHNldWRvRXhwb3J0Tm9kZXMgPSBbXTtcclxuXHJcbiAgICBmb3IgKGNvbnN0IGV4cG9ydE5vZGUgb2YgdGhpcy5fYXN0LnByb2dyYW0uYm9keSkge1xyXG4gICAgICBpZiAoZXhwb3J0Tm9kZS50eXBlICE9PSAnRXhwb3J0RGVmYXVsdERlY2xhcmF0aW9uJykgY29udGludWU7XHJcblxyXG4gICAgICBsZXQgdGFyZ2V0Q2xhc3NOYW1lID0gbnVsbDtcclxuICAgICAgbGV0IHRhcmdldFZhcmlhYmxlTmFtZSA9IG51bGw7XHJcbiAgICAgIGxldCBwc2V1ZG9DbGFzc0V4cG9ydCA9IGZhbHNlO1xyXG5cclxuICAgICAgc3dpdGNoIChleHBvcnROb2RlLmRlY2xhcmF0aW9uLnR5cGUpIHtcclxuICAgICAgICBjYXNlICdOZXdFeHByZXNzaW9uJzpcclxuICAgICAgICAgIGlmIChleHBvcnROb2RlLmRlY2xhcmF0aW9uLmNhbGxlZS50eXBlID09PSAnSWRlbnRpZmllcicpIHtcclxuICAgICAgICAgICAgdGFyZ2V0Q2xhc3NOYW1lID0gZXhwb3J0Tm9kZS5kZWNsYXJhdGlvbi5jYWxsZWUubmFtZTtcclxuICAgICAgICAgIH0gZWxzZSBpZiAoZXhwb3J0Tm9kZS5kZWNsYXJhdGlvbi5jYWxsZWUudHlwZSA9PT0gJ01lbWJlckV4cHJlc3Npb24nKSB7XHJcbiAgICAgICAgICAgIHRhcmdldENsYXNzTmFtZSA9IGV4cG9ydE5vZGUuZGVjbGFyYXRpb24uY2FsbGVlLnByb3BlcnR5Lm5hbWU7XHJcbiAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICB0YXJnZXRDbGFzc05hbWUgPSAnJztcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIHRhcmdldFZhcmlhYmxlTmFtZSA9IHRhcmdldENsYXNzTmFtZS5yZXBsYWNlKC9eLi91LCAoYykgPT4geyByZXR1cm4gYy50b0xvd2VyQ2FzZSgpOyB9KTtcclxuICAgICAgICAgIHBzZXVkb0NsYXNzRXhwb3J0ID0gdHJ1ZTtcclxuICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgIGNhc2UgJ0lkZW50aWZpZXInOiB7XHJcbiAgICAgICAgICBjb25zdCB2YXJOb2RlID0gQVNUVXRpbC5maW5kVmFyaWFibGVEZWNsYXJhdGlvbkFuZE5ld0V4cHJlc3Npb25Ob2RlKGV4cG9ydE5vZGUuZGVjbGFyYXRpb24ubmFtZSwgdGhpcy5fYXN0KTtcclxuICAgICAgICAgIGlmICh2YXJOb2RlKSB7XHJcbiAgICAgICAgICAgIHRhcmdldENsYXNzTmFtZSA9IHZhck5vZGUuZGVjbGFyYXRpb25zWzBdLmluaXQuY2FsbGVlLm5hbWU7XHJcbiAgICAgICAgICAgIHRhcmdldFZhcmlhYmxlTmFtZSA9IGV4cG9ydE5vZGUuZGVjbGFyYXRpb24ubmFtZTtcclxuICAgICAgICAgICAgcHNldWRvQ2xhc3NFeHBvcnQgPSB0cnVlO1xyXG4gICAgICAgICAgICBBU1RVdGlsLnNhbml0aXplKHZhck5vZGUpO1xyXG4gICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgdGFyZ2V0Q2xhc3NOYW1lID0gZXhwb3J0Tm9kZS5kZWNsYXJhdGlvbi5uYW1lO1xyXG4gICAgICAgICAgICBwc2V1ZG9DbGFzc0V4cG9ydCA9IGZhbHNlO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGRlZmF1bHQ6XHJcbiAgICAgICAgICBkZWJ1ZyhgVW5rbm93biBleHBvcnQgZGVjbGFyYXRpb24gdHlwZS4gdHlwZSA9IFwiJHtleHBvcnROb2RlLmRlY2xhcmF0aW9uLnR5cGV9XCJgKTtcclxuICAgICAgICAgIGJyZWFrO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBjb25zdCB7Y2xhc3NOb2RlLCBleHBvcnRlZH0gPSBBU1RVdGlsLmZpbmRDbGFzc0RlY2xhcmF0aW9uTm9kZSh0YXJnZXRDbGFzc05hbWUsIHRoaXMuX2FzdCk7XHJcbiAgICAgIGlmIChjbGFzc05vZGUpIHtcclxuICAgICAgICBpZiAoIWV4cG9ydGVkKSB7XHJcbiAgICAgICAgICBjb25zdCBwc2V1ZG9FeHBvcnROb2RlMSA9IHRoaXMuX2NvcHkoZXhwb3J0Tm9kZSk7XHJcbiAgICAgICAgICBwc2V1ZG9FeHBvcnROb2RlMS5kZWNsYXJhdGlvbiA9IHRoaXMuX2NvcHkoY2xhc3NOb2RlKTtcclxuICAgICAgICAgIHBzZXVkb0V4cG9ydE5vZGUxLmxlYWRpbmdDb21tZW50cyA9IG51bGw7XHJcbiAgICAgICAgICBwc2V1ZG9FeHBvcnROb2RlMS5kZWNsYXJhdGlvbi5fX1BzZXVkb0V4cG9ydF9fID0gcHNldWRvQ2xhc3NFeHBvcnQ7XHJcbiAgICAgICAgICBwc2V1ZG9FeHBvcnROb2Rlcy5wdXNoKHBzZXVkb0V4cG9ydE5vZGUxKTtcclxuICAgICAgICAgIEFTVFV0aWwuc2FuaXRpemUoY2xhc3NOb2RlKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmICh0YXJnZXRWYXJpYWJsZU5hbWUpIHtcclxuICAgICAgICAgIGNvbnN0IHBzZXVkb0V4cG9ydE5vZGUyID0gdGhpcy5fY29weShleHBvcnROb2RlKTtcclxuICAgICAgICAgIHBzZXVkb0V4cG9ydE5vZGUyLmRlY2xhcmF0aW9uID0gQVNUVXRpbC5jcmVhdGVWYXJpYWJsZURlY2xhcmF0aW9uQW5kTmV3RXhwcmVzc2lvbk5vZGUodGFyZ2V0VmFyaWFibGVOYW1lLCB0YXJnZXRDbGFzc05hbWUsIGV4cG9ydE5vZGUubG9jKTtcclxuICAgICAgICAgIHBzZXVkb0V4cG9ydE5vZGVzLnB1c2gocHNldWRvRXhwb3J0Tm9kZTIpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgQVNUVXRpbC5zYW5pdGl6ZShleHBvcnROb2RlKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgY29uc3QgZnVuY3Rpb25Ob2RlID0gQVNUVXRpbC5maW5kRnVuY3Rpb25EZWNsYXJhdGlvbk5vZGUoZXhwb3J0Tm9kZS5kZWNsYXJhdGlvbi5uYW1lLCB0aGlzLl9hc3QpO1xyXG4gICAgICBpZiAoZnVuY3Rpb25Ob2RlKSB7XHJcbiAgICAgICAgY29uc3QgcHNldWRvRXhwb3J0Tm9kZSA9IHRoaXMuX2NvcHkoZXhwb3J0Tm9kZSk7XHJcbiAgICAgICAgcHNldWRvRXhwb3J0Tm9kZS5kZWNsYXJhdGlvbiA9IHRoaXMuX2NvcHkoZnVuY3Rpb25Ob2RlKTtcclxuICAgICAgICBBU1RVdGlsLnNhbml0aXplKGV4cG9ydE5vZGUpO1xyXG4gICAgICAgIEFTVFV0aWwuc2FuaXRpemUoZnVuY3Rpb25Ob2RlKTtcclxuICAgICAgICBwc2V1ZG9FeHBvcnROb2Rlcy5wdXNoKHBzZXVkb0V4cG9ydE5vZGUpO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBjb25zdCB2YXJpYWJsZU5vZGUgPSBBU1RVdGlsLmZpbmRWYXJpYWJsZURlY2xhcmF0aW9uTm9kZShleHBvcnROb2RlLmRlY2xhcmF0aW9uLm5hbWUsIHRoaXMuX2FzdCk7XHJcbiAgICAgIGlmICh2YXJpYWJsZU5vZGUpIHtcclxuICAgICAgICBjb25zdCBwc2V1ZG9FeHBvcnROb2RlID0gdGhpcy5fY29weShleHBvcnROb2RlKTtcclxuICAgICAgICBwc2V1ZG9FeHBvcnROb2RlLmRlY2xhcmF0aW9uID0gdGhpcy5fY29weSh2YXJpYWJsZU5vZGUpO1xyXG4gICAgICAgIEFTVFV0aWwuc2FuaXRpemUoZXhwb3J0Tm9kZSk7XHJcbiAgICAgICAgQVNUVXRpbC5zYW5pdGl6ZSh2YXJpYWJsZU5vZGUpO1xyXG4gICAgICAgIHBzZXVkb0V4cG9ydE5vZGVzLnB1c2gocHNldWRvRXhwb3J0Tm9kZSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICB0aGlzLl9hc3QucHJvZ3JhbS5ib2R5LnB1c2goLi4ucHNldWRvRXhwb3J0Tm9kZXMpO1xyXG4gIH1cclxuXHJcbiAgLyogZXNsaW50LWRpc2FibGUgbWF4LXN0YXRlbWVudHMgKi9cclxuICAvKipcclxuICAgKiBpbnNwZWN0IEV4cG9ydE5hbWVkRGVjbGFyYXRpb24uXHJcbiAgICpcclxuICAgKiBjYXNlMTogc2VwYXJhdGVkIGV4cG9ydFxyXG4gICAqXHJcbiAgICogYGBgamF2YXNjcmlwdFxyXG4gICAqIGNsYXNzIEZvbyB7fVxyXG4gICAqIGV4cG9ydCB7Rm9vfTtcclxuICAgKiBgYGBcclxuICAgKlxyXG4gICAqIGNhc2UyOiBleHBvcnQgaW5zdGFuY2UoaW5kaXJlY3RseSkuXHJcbiAgICpcclxuICAgKiBgYGBqYXZhc2NyaXB0XHJcbiAgICogY2xhc3MgRm9vIHt9XHJcbiAgICogbGV0IGZvbyA9IG5ldyBGb28oKTtcclxuICAgKiBleHBvcnQge2Zvb307XHJcbiAgICogYGBgXHJcbiAgICpcclxuICAgKiBAcHJpdmF0ZVxyXG4gICAqIEB0b2RvIHN1cHBvcnQgZnVuY3Rpb24gZXhwb3J0LlxyXG4gICAqL1xyXG4gIF9pbnNwZWN0RXhwb3J0TmFtZWREZWNsYXJhdGlvbigpIHtcclxuICAgIGNvbnN0IHBzZXVkb0V4cG9ydE5vZGVzID0gW107XHJcblxyXG4gICAgZm9yIChjb25zdCBleHBvcnROb2RlIG9mIHRoaXMuX2FzdC5wcm9ncmFtLmJvZHkpIHtcclxuICAgICAgaWYgKGV4cG9ydE5vZGUudHlwZSAhPT0gJ0V4cG9ydE5hbWVkRGVjbGFyYXRpb24nKSBjb250aW51ZTtcclxuXHJcbiAgICAgIGlmIChleHBvcnROb2RlLmRlY2xhcmF0aW9uICYmIGV4cG9ydE5vZGUuZGVjbGFyYXRpb24udHlwZSA9PT0gJ1ZhcmlhYmxlRGVjbGFyYXRpb24nKSB7XHJcbiAgICAgICAgZm9yIChjb25zdCBkZWNsYXJhdGlvbiBvZiBleHBvcnROb2RlLmRlY2xhcmF0aW9uLmRlY2xhcmF0aW9ucykge1xyXG4gICAgICAgICAgaWYgKCFkZWNsYXJhdGlvbi5pbml0IHx8IGRlY2xhcmF0aW9uLmluaXQudHlwZSAhPT0gJ05ld0V4cHJlc3Npb24nKSBjb250aW51ZTtcclxuXHJcbiAgICAgICAgICBjb25zdCB7Y2xhc3NOb2RlLCBleHBvcnRlZH0gPSBBU1RVdGlsLmZpbmRDbGFzc0RlY2xhcmF0aW9uTm9kZShkZWNsYXJhdGlvbi5pbml0LmNhbGxlZS5uYW1lLCB0aGlzLl9hc3QpO1xyXG4gICAgICAgICAgaWYgKGNsYXNzTm9kZSAmJiAhZXhwb3J0ZWQpIHtcclxuICAgICAgICAgICAgY29uc3QgcHNldWRvRXhwb3J0Tm9kZSA9IHRoaXMuX2NvcHkoZXhwb3J0Tm9kZSk7XHJcbiAgICAgICAgICAgIHBzZXVkb0V4cG9ydE5vZGUuZGVjbGFyYXRpb24gPSB0aGlzLl9jb3B5KGNsYXNzTm9kZSk7XHJcbiAgICAgICAgICAgIHBzZXVkb0V4cG9ydE5vZGUubGVhZGluZ0NvbW1lbnRzID0gbnVsbDtcclxuICAgICAgICAgICAgcHNldWRvRXhwb3J0Tm9kZXMucHVzaChwc2V1ZG9FeHBvcnROb2RlKTtcclxuICAgICAgICAgICAgcHNldWRvRXhwb3J0Tm9kZS5kZWNsYXJhdGlvbi5fX1BzZXVkb0V4cG9ydF9fID0gdHJ1ZTtcclxuICAgICAgICAgICAgQVNUVXRpbC5zYW5pdGl6ZShjbGFzc05vZGUpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgICBjb250aW51ZTtcclxuICAgICAgfVxyXG5cclxuICAgICAgZm9yIChjb25zdCBzcGVjaWZpZXIgb2YgZXhwb3J0Tm9kZS5zcGVjaWZpZXJzKSB7XHJcbiAgICAgICAgaWYgKHNwZWNpZmllci50eXBlICE9PSAnRXhwb3J0U3BlY2lmaWVyJykgY29udGludWU7XHJcblxyXG4gICAgICAgIGxldCB0YXJnZXRDbGFzc05hbWUgPSBudWxsO1xyXG4gICAgICAgIGxldCBwc2V1ZG9DbGFzc0V4cG9ydCA9IGZhbHNlO1xyXG5cclxuICAgICAgICBjb25zdCB2YXJOb2RlID0gQVNUVXRpbC5maW5kVmFyaWFibGVEZWNsYXJhdGlvbkFuZE5ld0V4cHJlc3Npb25Ob2RlKHNwZWNpZmllci5leHBvcnRlZC5uYW1lLCB0aGlzLl9hc3QpO1xyXG4gICAgICAgIGlmICh2YXJOb2RlKSB7XHJcbiAgICAgICAgICB0YXJnZXRDbGFzc05hbWUgPSB2YXJOb2RlLmRlY2xhcmF0aW9uc1swXS5pbml0LmNhbGxlZS5uYW1lO1xyXG4gICAgICAgICAgcHNldWRvQ2xhc3NFeHBvcnQgPSB0cnVlO1xyXG5cclxuICAgICAgICAgIGNvbnN0IHBzZXVkb0V4cG9ydE5vZGUgPSB0aGlzLl9jb3B5KGV4cG9ydE5vZGUpO1xyXG4gICAgICAgICAgcHNldWRvRXhwb3J0Tm9kZS5kZWNsYXJhdGlvbiA9IHRoaXMuX2NvcHkodmFyTm9kZSk7XHJcbiAgICAgICAgICBwc2V1ZG9FeHBvcnROb2RlLnNwZWNpZmllcnMgPSBudWxsO1xyXG4gICAgICAgICAgcHNldWRvRXhwb3J0Tm9kZXMucHVzaChwc2V1ZG9FeHBvcnROb2RlKTtcclxuXHJcbiAgICAgICAgICBBU1RVdGlsLnNhbml0aXplKHZhck5vZGUpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICB0YXJnZXRDbGFzc05hbWUgPSBzcGVjaWZpZXIuZXhwb3J0ZWQubmFtZTtcclxuICAgICAgICAgIHBzZXVkb0NsYXNzRXhwb3J0ID0gZmFsc2U7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBjb25zdCB7Y2xhc3NOb2RlLCBleHBvcnRlZH0gPSBBU1RVdGlsLmZpbmRDbGFzc0RlY2xhcmF0aW9uTm9kZSh0YXJnZXRDbGFzc05hbWUsIHRoaXMuX2FzdCk7XHJcbiAgICAgICAgaWYgKGNsYXNzTm9kZSAmJiAhZXhwb3J0ZWQpIHtcclxuICAgICAgICAgIGNvbnN0IHBzZXVkb0V4cG9ydE5vZGUgPSB0aGlzLl9jb3B5KGV4cG9ydE5vZGUpO1xyXG4gICAgICAgICAgcHNldWRvRXhwb3J0Tm9kZS5kZWNsYXJhdGlvbiA9IHRoaXMuX2NvcHkoY2xhc3NOb2RlKTtcclxuICAgICAgICAgIHBzZXVkb0V4cG9ydE5vZGUubGVhZGluZ0NvbW1lbnRzID0gbnVsbDtcclxuICAgICAgICAgIHBzZXVkb0V4cG9ydE5vZGUuc3BlY2lmaWVycyA9IG51bGw7XHJcbiAgICAgICAgICBwc2V1ZG9FeHBvcnROb2RlLmRlY2xhcmF0aW9uLl9fUHNldWRvRXhwb3J0X18gPSBwc2V1ZG9DbGFzc0V4cG9ydDtcclxuICAgICAgICAgIHBzZXVkb0V4cG9ydE5vZGVzLnB1c2gocHNldWRvRXhwb3J0Tm9kZSk7XHJcbiAgICAgICAgICBBU1RVdGlsLnNhbml0aXplKGNsYXNzTm9kZSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBjb25zdCBmdW5jdGlvbk5vZGUgPSBBU1RVdGlsLmZpbmRGdW5jdGlvbkRlY2xhcmF0aW9uTm9kZShzcGVjaWZpZXIuZXhwb3J0ZWQubmFtZSwgdGhpcy5fYXN0KTtcclxuICAgICAgICBpZiAoZnVuY3Rpb25Ob2RlKSB7XHJcbiAgICAgICAgICBjb25zdCBwc2V1ZG9FeHBvcnROb2RlID0gdGhpcy5fY29weShleHBvcnROb2RlKTtcclxuICAgICAgICAgIHBzZXVkb0V4cG9ydE5vZGUuZGVjbGFyYXRpb24gPSB0aGlzLl9jb3B5KGZ1bmN0aW9uTm9kZSk7XHJcbiAgICAgICAgICBwc2V1ZG9FeHBvcnROb2RlLmxlYWRpbmdDb21tZW50cyA9IG51bGw7XHJcbiAgICAgICAgICBwc2V1ZG9FeHBvcnROb2RlLnNwZWNpZmllcnMgPSBudWxsO1xyXG4gICAgICAgICAgQVNUVXRpbC5zYW5pdGl6ZShmdW5jdGlvbk5vZGUpO1xyXG4gICAgICAgICAgcHNldWRvRXhwb3J0Tm9kZXMucHVzaChwc2V1ZG9FeHBvcnROb2RlKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGNvbnN0IHZhcmlhYmxlTm9kZSA9IEFTVFV0aWwuZmluZFZhcmlhYmxlRGVjbGFyYXRpb25Ob2RlKHNwZWNpZmllci5leHBvcnRlZC5uYW1lLCB0aGlzLl9hc3QpO1xyXG4gICAgICAgIGlmICh2YXJpYWJsZU5vZGUpIHtcclxuICAgICAgICAgIGNvbnN0IHBzZXVkb0V4cG9ydE5vZGUgPSB0aGlzLl9jb3B5KGV4cG9ydE5vZGUpO1xyXG4gICAgICAgICAgcHNldWRvRXhwb3J0Tm9kZS5kZWNsYXJhdGlvbiA9IHRoaXMuX2NvcHkodmFyaWFibGVOb2RlKTtcclxuICAgICAgICAgIHBzZXVkb0V4cG9ydE5vZGUubGVhZGluZ0NvbW1lbnRzID0gbnVsbDtcclxuICAgICAgICAgIHBzZXVkb0V4cG9ydE5vZGUuc3BlY2lmaWVycyA9IG51bGw7XHJcbiAgICAgICAgICBBU1RVdGlsLnNhbml0aXplKHZhcmlhYmxlTm9kZSk7XHJcbiAgICAgICAgICBwc2V1ZG9FeHBvcnROb2Rlcy5wdXNoKHBzZXVkb0V4cG9ydE5vZGUpO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHRoaXMuX2FzdC5wcm9ncmFtLmJvZHkucHVzaCguLi5wc2V1ZG9FeHBvcnROb2Rlcyk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBwdXNoIG5vZGUsIGFuZCBmYWN0b3J5IHByb2Nlc3NlcyBub2RlLlxyXG4gICAqIEBwYXJhbSB7QVNUTm9kZX0gbm9kZSAtIHRhcmdldCBub2RlLlxyXG4gICAqIEBwYXJhbSB7QVNUTm9kZX0gcGFyZW50Tm9kZSAtIHBhcmVudCBub2RlIG9mIHRhcmdldCBub2RlLlxyXG4gICAqL1xyXG4gIHB1c2gobm9kZSwgcGFyZW50Tm9kZSkge1xyXG4gICAgaWYgKG5vZGUgPT09IHRoaXMuX2FzdCkgcmV0dXJuO1xyXG5cclxuICAgIGlmIChub2RlW2FscmVhZHldKSByZXR1cm47XHJcblxyXG4gICAgY29uc3QgaXNMYXN0Tm9kZUluUGFyZW50ID0gdGhpcy5faXNMYXN0Tm9kZUluUGFyZW50KG5vZGUsIHBhcmVudE5vZGUpO1xyXG5cclxuICAgIG5vZGVbYWxyZWFkeV0gPSB0cnVlO1xyXG4gICAgUmVmbGVjdC5kZWZpbmVQcm9wZXJ0eShub2RlLCAncGFyZW50Jywge3ZhbHVlOiBwYXJlbnROb2RlfSk7XHJcblxyXG4gICAgLy8gdW53cmFwIGV4cG9ydCBkZWNsYXJhdGlvblxyXG4gICAgaWYgKFsnRXhwb3J0RGVmYXVsdERlY2xhcmF0aW9uJywgJ0V4cG9ydE5hbWVkRGVjbGFyYXRpb24nXS5pbmNsdWRlcyhub2RlLnR5cGUpKSB7XHJcbiAgICAgIHBhcmVudE5vZGUgPSBub2RlO1xyXG4gICAgICBub2RlID0gdGhpcy5fdW53cmFwRXhwb3J0RGVjbGFyYXRpb24obm9kZSk7XHJcbiAgICAgIGlmICghbm9kZSkgcmV0dXJuO1xyXG4gICAgICBub2RlW2FscmVhZHldID0gdHJ1ZTtcclxuICAgICAgUmVmbGVjdC5kZWZpbmVQcm9wZXJ0eShub2RlLCAncGFyZW50Jywge3ZhbHVlOiBwYXJlbnROb2RlfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gaWYgbm9kZSBoYXMgZGVjb3JhdG9ycywgbGVhZGluZyBjb21tZW50cyBpcyBhdHRhY2hlZCB0byBkZWNvcmF0b3JzLlxyXG4gICAgaWYgKG5vZGUuZGVjb3JhdG9ycyAmJiBub2RlLmRlY29yYXRvcnNbMF0ubGVhZGluZ0NvbW1lbnRzKSB7XHJcbiAgICAgIGlmICghbm9kZS5sZWFkaW5nQ29tbWVudHMgfHwgIW5vZGUubGVhZGluZ0NvbW1lbnRzLmxlbmd0aCkge1xyXG4gICAgICAgIG5vZGUubGVhZGluZ0NvbW1lbnRzID0gbm9kZS5kZWNvcmF0b3JzWzBdLmxlYWRpbmdDb21tZW50cztcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGxldCByZXN1bHRzID0gW107XHJcbiAgICByZXN1bHRzID0gdGhpcy5fdHJhdmVyc2VDb21tZW50cyhwYXJlbnROb2RlLCBub2RlLCBub2RlLmxlYWRpbmdDb21tZW50cyk7XHJcbiAgICB0aGlzLl9yZXN1bHRzLnB1c2goLi4ucmVzdWx0cyk7XHJcblxyXG4gICAgLy8gZm9yIHRyYWlsaW5nIGNvbW1lbnRzLlxyXG4gICAgLy8gdHJhdmVyc2Ugd2l0aCBvbmx5IGxhc3Qgbm9kZSwgYmVjYXVzZSBwcmV2ZW50IGR1cGxpY2F0aW9uIG9mIHRyYWlsaW5nIGNvbW1lbnRzLlxyXG4gICAgaWYgKG5vZGUudHJhaWxpbmdDb21tZW50cyAmJiBpc0xhc3ROb2RlSW5QYXJlbnQpIHtcclxuICAgICAgcmVzdWx0cyA9IHRoaXMuX3RyYXZlcnNlQ29tbWVudHMocGFyZW50Tm9kZSwgbnVsbCwgbm9kZS50cmFpbGluZ0NvbW1lbnRzKTtcclxuICAgICAgdGhpcy5fcmVzdWx0cy5wdXNoKC4uLnJlc3VsdHMpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogdHJhdmVyc2UgY29tbWVudHMgb2Ygbm9kZSwgYW5kIGNyZWF0ZSBkb2Mgb2JqZWN0LlxyXG4gICAqIEBwYXJhbSB7QVNUTm9kZXxBU1R9IHBhcmVudE5vZGUgLSBwYXJlbnQgb2YgdGFyZ2V0IG5vZGUuXHJcbiAgICogQHBhcmFtIHs/QVNUTm9kZX0gbm9kZSAtIHRhcmdldCBub2RlLlxyXG4gICAqIEBwYXJhbSB7QVNUTm9kZVtdfSBjb21tZW50cyAtIGNvbW1lbnQgbm9kZXMuXHJcbiAgICogQHJldHVybnMge0RvY09iamVjdFtdfSBjcmVhdGVkIGRvYyBvYmplY3RzLlxyXG4gICAqIEBwcml2YXRlXHJcbiAgICovXHJcbiAgX3RyYXZlcnNlQ29tbWVudHMocGFyZW50Tm9kZSwgbm9kZSwgY29tbWVudHMpIHtcclxuICAgIGlmICghbm9kZSkge1xyXG4gICAgICBjb25zdCB2aXJ0dWFsTm9kZSA9IHt9O1xyXG4gICAgICBSZWZsZWN0LmRlZmluZVByb3BlcnR5KHZpcnR1YWxOb2RlLCAncGFyZW50Jywge3ZhbHVlOiBwYXJlbnROb2RlfSk7XHJcbiAgICAgIG5vZGUgPSB2aXJ0dWFsTm9kZTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoY29tbWVudHMgJiYgY29tbWVudHMubGVuZ3RoKSB7XHJcbiAgICAgIGNvbnN0IHRlbXAgPSBbXTtcclxuICAgICAgZm9yIChjb25zdCBjb21tZW50IG9mIGNvbW1lbnRzKSB7XHJcbiAgICAgICAgaWYgKENvbW1lbnRQYXJzZXIuaXNFU0RvYyhjb21tZW50KSkgdGVtcC5wdXNoKGNvbW1lbnQpO1xyXG4gICAgICB9XHJcbiAgICAgIGNvbW1lbnRzID0gdGVtcDtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGNvbW1lbnRzID0gW107XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKGNvbW1lbnRzLmxlbmd0aCA9PT0gMCkge1xyXG4gICAgICBjb21tZW50cyA9IFt7dHlwZTogJ0NvbW1lbnRCbG9jaycsIHZhbHVlOiAnKiBAdW5kb2N1bWVudCd9XTtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCByZXN1bHRzID0gW107XHJcbiAgICBjb25zdCBsYXN0Q29tbWVudCA9IGNvbW1lbnRzW2NvbW1lbnRzLmxlbmd0aCAtIDFdO1xyXG4gICAgZm9yIChjb25zdCBjb21tZW50IG9mIGNvbW1lbnRzKSB7XHJcbiAgICAgIGNvbnN0IHRhZ3MgPSBDb21tZW50UGFyc2VyLnBhcnNlKGNvbW1lbnQpO1xyXG5cclxuICAgICAgbGV0IGRvYyA9IG51bGw7XHJcbiAgICAgIGlmIChjb21tZW50ID09PSBsYXN0Q29tbWVudCkge1xyXG4gICAgICAgIGlmIChub2RlLmRlY2xhcmF0aW9ucyAmJiBub2RlLmRlY2xhcmF0aW9uc1swXS5pZC50eXBlID09PSBcIkFycmF5UGF0dGVyblwiKSB7XHJcbiAgICAgICAgICAvLyBIQUNLIGltcGxlbWVudGluZyBtdWx0aXBsZSB2YXJpYWJsZXMgZnJvbSBhcnJheSBwYXR0ZXJuLiBlLmcuIGV4cG9ydCBjb250IFthLCBiXSA9IGFyclxyXG4gICAgICAgICAgLy8gVXNlcyBlbGVtZW50SW5kZXggd2hpY2ggd2UgYWRkIHRvIG5vZGUgc28gVmFyaWFibGVEb2Mga25vd3Mgd2hpY2ggZWxlbWVudCB0byBwaWNrLlxyXG4gICAgICAgICAgY29uc3QgbGVuZ3RoID0gbm9kZS5kZWNsYXJhdGlvbnNbMF0uaWQuZWxlbWVudHMubGVuZ3RoO1xyXG4gICAgICAgICAgZm9yIChsZXQgaWkgPSAwOyBpaSA8IGxlbmd0aDsgaWkgPSBpaSArIDEpIHtcclxuICAgICAgICAgICAgaWYgKHR5cGVvZihub2RlLmVsZW1lbnRJbmRleCkgPT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgICAgICAgICAgUmVmbGVjdC5kZWZpbmVQcm9wZXJ0eShub2RlLCAnZWxlbWVudEluZGV4Jywge3dyaXRhYmxlOiB0cnVlfSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgbm9kZS5lbGVtZW50SW5kZXggPSBpaTtcclxuICAgICAgICAgICAgZG9jID0gdGhpcy5fY3JlYXRlRG9jKG5vZGUsIHRhZ3MpO1xyXG4gICAgICAgICAgICBpZiAoZG9jKSByZXN1bHRzLnB1c2goZG9jLnZhbHVlKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9IGVsc2UgaWYgKG5vZGUuZGVjbGFyYXRpb25zICYmIG5vZGUuZGVjbGFyYXRpb25zWzBdLmlkLnR5cGUgPT09ICdPYmplY3RQYXR0ZXJuJykge1xyXG4gICAgICAgICAgLy8gSEFDSyBpbXBsZW1lbnRpbmcgbXVsdGlwbGUgdmFyaWFibGVzIGZyb20gb2JqZWN0IHBhdHRlcm4uIGUuZy4gZXhwb3J0IGNvbnN0IHthLCBifSA9IG9ialxyXG4gICAgICAgICAgLy8gVXNlcyBwcm9wZXJ0eUluZGV4IHdoaWNoIHdlIGFkZCB0byBub2RlIHNvIFZhcmlhYmxlRG9jIGtub3dzIHdoaWNoIGVsZW1lbnQgdG8gcGljay5cclxuICAgICAgICAgIGNvbnN0IGxlbmd0aCA9IG5vZGUuZGVjbGFyYXRpb25zWzBdLmlkLnByb3BlcnRpZXMubGVuZ3RoO1xyXG4gICAgICAgICAgZm9yIChsZXQgaWkgPSAwOyBpaSA8IGxlbmd0aDsgaWkgPSBpaSArIDEpIHtcclxuICAgICAgICAgICAgaWYgKHR5cGVvZihub2RlLnByb3BlcnR5SW5kZXgpID09PSAndW5kZWZpbmVkJykge1xyXG4gICAgICAgICAgICAgIFJlZmxlY3QuZGVmaW5lUHJvcGVydHkobm9kZSwgJ3Byb3BlcnR5SW5kZXgnLCB7d3JpdGFibGU6IHRydWV9KTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBub2RlLnByb3BlcnR5SW5kZXggPSBpaTtcclxuICAgICAgICAgICAgZG9jID0gdGhpcy5fY3JlYXRlRG9jKCBub2RlLCB0YWdzICk7XHJcbiAgICAgICAgICAgIGlmIChkb2MpIHJlc3VsdHMucHVzaChkb2MudmFsdWUpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICBkb2MgPSB0aGlzLl9jcmVhdGVEb2Mobm9kZSwgdGFncyk7XHJcbiAgICAgICAgICBpZiAoZG9jKSByZXN1bHRzLnB1c2goZG9jLnZhbHVlKTtcclxuICAgICAgICB9XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgY29uc3QgdmlydHVhbE5vZGUgPSB7fTtcclxuICAgICAgICBSZWZsZWN0LmRlZmluZVByb3BlcnR5KHZpcnR1YWxOb2RlLCAncGFyZW50Jywge3ZhbHVlOiBwYXJlbnROb2RlfSk7XHJcbiAgICAgICAgZG9jID0gdGhpcy5fY3JlYXR