matrix-react-sdk
Version:
SDK for matrix.org using React
417 lines (411 loc) • 72.7 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = _interopRequireDefault(require("react"));
var _classnames = _interopRequireDefault(require("classnames"));
var _logger = require("matrix-js-sdk/src/logger");
var _matrix = require("matrix-js-sdk/src/matrix");
var _languageHandler = require("../../../languageHandler");
var _Login = _interopRequireDefault(require("../../../Login"));
var _ErrorUtils = require("../../../utils/ErrorUtils");
var _AutoDiscoveryUtils = _interopRequireDefault(require("../../../utils/AutoDiscoveryUtils"));
var _AuthPage = _interopRequireDefault(require("../../views/auth/AuthPage"));
var _PlatformPeg = _interopRequireDefault(require("../../../PlatformPeg"));
var _SettingsStore = _interopRequireDefault(require("../../../settings/SettingsStore"));
var _UIFeature = require("../../../settings/UIFeature");
var _PasswordLogin = _interopRequireDefault(require("../../views/auth/PasswordLogin"));
var _InlineSpinner = _interopRequireDefault(require("../../views/elements/InlineSpinner"));
var _Spinner = _interopRequireDefault(require("../../views/elements/Spinner"));
var _SSOButtons = _interopRequireDefault(require("../../views/elements/SSOButtons"));
var _ServerPicker = _interopRequireDefault(require("../../views/elements/ServerPicker"));
var _AuthBody = _interopRequireDefault(require("../../views/auth/AuthBody"));
var _AuthHeader = _interopRequireDefault(require("../../views/auth/AuthHeader"));
var _AccessibleButton = _interopRequireDefault(require("../../views/elements/AccessibleButton"));
var _arrays = require("../../../utils/arrays");
var _Settings = require("../../../settings/Settings");
var _authorize = require("../../../utils/oidc/authorize");
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } /*
Copyright 2024 New Vector Ltd.
Copyright 2015-2021 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
/*
* A wire component which glues together login UI components and Login logic
*/
class LoginComponent extends _react.default.PureComponent {
constructor(props) {
super(props);
// only set on a config level, so we don't need to watch
(0, _defineProperty2.default)(this, "unmounted", false);
(0, _defineProperty2.default)(this, "oidcNativeFlowEnabled", false);
(0, _defineProperty2.default)(this, "loginLogic", void 0);
(0, _defineProperty2.default)(this, "stepRendererMap", void 0);
(0, _defineProperty2.default)(this, "isBusy", () => !!this.state.busy || !!this.props.busy);
(0, _defineProperty2.default)(this, "onPasswordLogin", async (username, phoneCountry, phoneNumber, password) => {
if (!this.state.serverIsAlive) {
this.setState({
busy: true
});
// Do a quick liveliness check on the URLs
let aliveAgain = true;
try {
await _AutoDiscoveryUtils.default.validateServerConfigWithStaticUrls(this.props.serverConfig.hsUrl, this.props.serverConfig.isUrl);
this.setState({
serverIsAlive: true,
errorText: ""
});
} catch (e) {
const componentState = _AutoDiscoveryUtils.default.authComponentStateForError(e);
this.setState(_objectSpread({
busy: false,
busyLoggingIn: false
}, componentState));
aliveAgain = !componentState.serverErrorIsFatal;
}
// Prevent people from submitting their password when something isn't right.
if (!aliveAgain) {
return;
}
}
this.setState({
busy: true,
busyLoggingIn: true,
errorText: null,
loginIncorrect: false
});
this.loginLogic.loginViaPassword(username, phoneCountry, phoneNumber, password).then(data => {
this.setState({
serverIsAlive: true
}); // it must be, we logged in.
this.props.onLoggedIn(data, password);
}, error => {
if (this.unmounted) return;
let errorText;
// Some error strings only apply for logging in
if (error.httpStatus === 400 && username && username.indexOf("@") > 0) {
errorText = (0, _languageHandler._t)("auth|unsupported_auth_email");
} else {
errorText = (0, _ErrorUtils.messageForLoginError)(error, this.props.serverConfig);
}
this.setState({
busy: false,
busyLoggingIn: false,
errorText,
// 401 would be the sensible status code for 'incorrect password'
// but the login API gives a 403 https://matrix.org/jira/browse/SYN-744
// mentions this (although the bug is for UI auth which is not this)
// We treat both as an incorrect password
loginIncorrect: error.httpStatus === 401 || error.httpStatus === 403
});
});
});
(0, _defineProperty2.default)(this, "onUsernameChanged", username => {
this.setState({
username
});
});
(0, _defineProperty2.default)(this, "onUsernameBlur", async username => {
const doWellknownLookup = username[0] === "@";
this.setState({
username: username,
busy: doWellknownLookup,
errorText: null,
canTryLogin: true
});
if (doWellknownLookup) {
const serverName = username.split(":").slice(1).join(":");
try {
const result = await _AutoDiscoveryUtils.default.validateServerName(serverName);
this.props.onServerConfigChange(result);
// We'd like to rely on new props coming in via `onServerConfigChange`
// so that we know the servers have definitely updated before clearing
// the busy state. In the case of a full MXID that resolves to the same
// HS as Element's default HS though, there may not be any server change.
// To avoid this trap, we clear busy here. For cases where the server
// actually has changed, `initLoginLogic` will be called and manages
// busy state for its own liveness check.
this.setState({
busy: false
});
} catch (e) {
_logger.logger.error("Problem parsing URL or unhandled error doing .well-known discovery:", e);
let message = (0, _languageHandler._t)("auth|failed_homeserver_discovery");
if (e instanceof _languageHandler.UserFriendlyError && e.translatedMessage) {
message = e.translatedMessage;
}
let errorText = message;
let discoveryState = {};
if (_AutoDiscoveryUtils.default.isLivelinessError(e)) {
errorText = this.state.errorText;
discoveryState = _AutoDiscoveryUtils.default.authComponentStateForError(e);
}
this.setState(_objectSpread({
busy: false,
errorText
}, discoveryState));
}
}
});
(0, _defineProperty2.default)(this, "onPhoneCountryChanged", phoneCountry => {
this.setState({
phoneCountry
});
});
(0, _defineProperty2.default)(this, "onPhoneNumberChanged", phoneNumber => {
this.setState({
phoneNumber
});
});
(0, _defineProperty2.default)(this, "onRegisterClick", ev => {
ev.preventDefault();
ev.stopPropagation();
this.props.onRegisterClick();
});
(0, _defineProperty2.default)(this, "onTryRegisterClick", ev => {
const hasPasswordFlow = this.state.flows?.find(flow => flow.type === "m.login.password");
const ssoFlow = this.state.flows?.find(flow => flow.type === "m.login.sso" || flow.type === "m.login.cas");
// If has no password flow but an SSO flow guess that the user wants to register with SSO.
// TODO: instead hide the Register button if registration is disabled by checking with the server,
// has no specific errCode currently and uses M_FORBIDDEN.
if (ssoFlow && !hasPasswordFlow) {
ev.preventDefault();
ev.stopPropagation();
const ssoKind = ssoFlow.type === "m.login.sso" ? "sso" : "cas";
_PlatformPeg.default.get()?.startSingleSignOn(this.loginLogic.createTemporaryClient(), ssoKind, this.props.fragmentAfterLogin, undefined, _matrix.SSOAction.REGISTER);
} else {
// Don't intercept - just go through to the register page
this.onRegisterClick(ev);
}
});
(0, _defineProperty2.default)(this, "isSupportedFlow", flow => {
// technically the flow can have multiple steps, but no one does this
// for login and loginLogic doesn't support it so we can ignore it.
if (!this.stepRendererMap[flow.type]) {
_logger.logger.log("Skipping flow", flow, "due to unsupported login type", flow.type);
return false;
}
return true;
});
(0, _defineProperty2.default)(this, "renderPasswordStep", () => {
return /*#__PURE__*/_react.default.createElement(_PasswordLogin.default, {
onSubmit: this.onPasswordLogin,
username: this.state.username,
phoneCountry: this.state.phoneCountry,
phoneNumber: this.state.phoneNumber,
onUsernameChanged: this.onUsernameChanged,
onUsernameBlur: this.onUsernameBlur,
onPhoneCountryChanged: this.onPhoneCountryChanged,
onPhoneNumberChanged: this.onPhoneNumberChanged,
onForgotPasswordClick: this.props.onForgotPasswordClick,
loginIncorrect: this.state.loginIncorrect,
serverConfig: this.props.serverConfig,
disableSubmit: this.isBusy(),
busy: this.props.isSyncing || this.state.busyLoggingIn
});
});
(0, _defineProperty2.default)(this, "renderOidcNativeStep", () => {
const flow = this.state.flows.find(flow => flow.type === "oidcNativeFlow");
return /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, {
className: "mx_Login_fullWidthButton",
kind: "primary",
onClick: async () => {
await (0, _authorize.startOidcLogin)(this.props.serverConfig.delegatedAuthentication, flow.clientId, this.props.serverConfig.hsUrl, this.props.serverConfig.isUrl);
}
}, (0, _languageHandler._t)("action|continue"));
});
(0, _defineProperty2.default)(this, "renderSsoStep", loginType => {
const flow = this.state.flows?.find(flow => flow.type === "m.login." + loginType);
return /*#__PURE__*/_react.default.createElement(_SSOButtons.default, {
matrixClient: this.loginLogic.createTemporaryClient(),
flow: flow,
loginType: loginType,
fragmentAfterLogin: this.props.fragmentAfterLogin,
primary: !this.state.flows?.find(flow => flow.type === "m.login.password"),
action: _matrix.SSOAction.LOGIN,
disabled: this.isBusy()
});
});
this.oidcNativeFlowEnabled = _SettingsStore.default.getValue(_Settings.Features.OidcNativeFlow);
this.state = {
busy: false,
errorText: null,
loginIncorrect: false,
canTryLogin: true,
username: props.defaultUsername ? props.defaultUsername : "",
phoneCountry: "",
phoneNumber: "",
serverIsAlive: true,
serverErrorIsFatal: false,
serverDeadError: ""
};
// map from login step type to a function which will render a control
// letting you do that login type
this.stepRendererMap = {
"m.login.password": this.renderPasswordStep,
// CAS and SSO are the same thing, modulo the url we link to
// eslint-disable-next-line @typescript-eslint/naming-convention
"m.login.cas": () => this.renderSsoStep("cas"),
// eslint-disable-next-line @typescript-eslint/naming-convention
"m.login.sso": () => this.renderSsoStep("sso"),
"oidcNativeFlow": () => this.renderOidcNativeStep()
};
}
componentDidMount() {
this.initLoginLogic(this.props.serverConfig);
}
componentWillUnmount() {
this.unmounted = true;
}
componentDidUpdate(prevProps) {
if (prevProps.serverConfig.hsUrl !== this.props.serverConfig.hsUrl || prevProps.serverConfig.isUrl !== this.props.serverConfig.isUrl ||
// delegatedAuthentication is only set by buildValidatedConfigFromDiscovery and won't be modified
// so shallow comparison is fine
prevProps.serverConfig.delegatedAuthentication !== this.props.serverConfig.delegatedAuthentication) {
// Ensure that we end up actually logging in to the right place
this.initLoginLogic(this.props.serverConfig);
}
}
async checkServerLiveliness({
hsUrl,
isUrl
}) {
// Do a quick liveliness check on the URLs
try {
const {
warning
} = await _AutoDiscoveryUtils.default.validateServerConfigWithStaticUrls(hsUrl, isUrl);
if (warning) {
this.setState(_objectSpread(_objectSpread({}, _AutoDiscoveryUtils.default.authComponentStateForError(warning)), {}, {
errorText: ""
}));
} else {
this.setState({
serverIsAlive: true,
errorText: ""
});
}
} catch (e) {
this.setState(_objectSpread({
busy: false
}, _AutoDiscoveryUtils.default.authComponentStateForError(e)));
}
}
async initLoginLogic({
hsUrl,
isUrl
}) {
let isDefaultServer = false;
if (this.props.serverConfig.isDefault && hsUrl === this.props.serverConfig.hsUrl && isUrl === this.props.serverConfig.isUrl) {
isDefaultServer = true;
}
const fallbackHsUrl = isDefaultServer ? this.props.fallbackHsUrl : null;
this.setState({
busy: true,
loginIncorrect: false
});
await this.checkServerLiveliness({
hsUrl,
isUrl
});
const loginLogic = new _Login.default(hsUrl, isUrl, fallbackHsUrl, {
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
// if native OIDC is enabled in the client pass the server's delegated auth settings
delegatedAuthentication: this.oidcNativeFlowEnabled ? this.props.serverConfig.delegatedAuthentication : undefined
});
this.loginLogic = loginLogic;
loginLogic.getFlows().then(flows => {
// look for a flow where we understand all of the steps.
const supportedFlows = flows.filter(this.isSupportedFlow);
this.setState({
flows: supportedFlows
});
if (supportedFlows.length === 0) {
this.setState({
errorText: (0, _languageHandler._t)("auth|unsupported_auth")
});
}
}, err => {
this.setState({
errorText: (0, _ErrorUtils.messageForConnectionError)(err, this.props.serverConfig),
loginIncorrect: false,
canTryLogin: false
});
}).finally(() => {
this.setState({
busy: false
});
});
}
renderLoginComponentForFlows() {
if (!this.state.flows) return null;
// this is the ideal order we want to show the flows in
const order = ["oidcNativeFlow", "m.login.password", "m.login.sso"];
const flows = (0, _arrays.filterBoolean)(order.map(type => this.state.flows?.find(flow => flow.type === type)));
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, flows.map(flow => {
const stepRenderer = this.stepRendererMap[flow.type];
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, {
key: flow.type
}, stepRenderer());
}));
}
render() {
const loader = this.isBusy() && !this.state.busyLoggingIn ? /*#__PURE__*/_react.default.createElement("div", {
className: "mx_Login_loader"
}, /*#__PURE__*/_react.default.createElement(_Spinner.default, null)) : null;
const errorText = this.state.errorText;
let errorTextSection;
if (errorText) {
errorTextSection = /*#__PURE__*/_react.default.createElement("div", {
className: "mx_Login_error"
}, errorText);
}
let serverDeadSection;
if (!this.state.serverIsAlive) {
const classes = (0, _classnames.default)({
mx_Login_error: true,
mx_Login_serverError: true,
mx_Login_serverErrorNonFatal: !this.state.serverErrorIsFatal
});
serverDeadSection = /*#__PURE__*/_react.default.createElement("div", {
className: classes
}, this.state.serverDeadError);
}
let footer;
if (this.props.isSyncing || this.state.busyLoggingIn) {
footer = /*#__PURE__*/_react.default.createElement("div", {
className: "mx_AuthBody_paddedFooter"
}, /*#__PURE__*/_react.default.createElement("div", {
className: "mx_AuthBody_paddedFooter_title"
}, /*#__PURE__*/_react.default.createElement(_InlineSpinner.default, {
w: 20,
h: 20
}), this.props.isSyncing ? (0, _languageHandler._t)("auth|syncing") : (0, _languageHandler._t)("auth|signing_in")), this.props.isSyncing && /*#__PURE__*/_react.default.createElement("div", {
className: "mx_AuthBody_paddedFooter_subtitle"
}, (0, _languageHandler._t)("auth|sync_footer_subtitle")));
} else if (_SettingsStore.default.getValue(_UIFeature.UIFeature.Registration)) {
footer = /*#__PURE__*/_react.default.createElement("span", {
className: "mx_AuthBody_changeFlow"
}, (0, _languageHandler._t)("auth|create_account_prompt", {}, {
a: sub => /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, {
kind: "link_inline",
onClick: this.onTryRegisterClick
}, sub)
}));
}
return /*#__PURE__*/_react.default.createElement(_AuthPage.default, null, /*#__PURE__*/_react.default.createElement(_AuthHeader.default, {
disableLanguageSelector: this.props.isSyncing || this.state.busyLoggingIn
}), /*#__PURE__*/_react.default.createElement(_AuthBody.default, null, /*#__PURE__*/_react.default.createElement("h1", null, (0, _languageHandler._t)("action|sign_in"), loader), errorTextSection, serverDeadSection, /*#__PURE__*/_react.default.createElement(_ServerPicker.default, {
serverConfig: this.props.serverConfig,
onServerConfigChange: this.props.onServerConfigChange,
disabled: this.isBusy()
}), this.renderLoginComponentForFlows(), footer));
}
}
exports.default = LoginComponent;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfcmVhY3QiLCJfaW50ZXJvcFJlcXVpcmVEZWZhdWx0IiwicmVxdWlyZSIsIl9jbGFzc25hbWVzIiwiX2xvZ2dlciIsIl9tYXRyaXgiLCJfbGFuZ3VhZ2VIYW5kbGVyIiwiX0xvZ2luIiwiX0Vycm9yVXRpbHMiLCJfQXV0b0Rpc2NvdmVyeVV0aWxzIiwiX0F1dGhQYWdlIiwiX1BsYXRmb3JtUGVnIiwiX1NldHRpbmdzU3RvcmUiLCJfVUlGZWF0dXJlIiwiX1Bhc3N3b3JkTG9naW4iLCJfSW5saW5lU3Bpbm5lciIsIl9TcGlubmVyIiwiX1NTT0J1dHRvbnMiLCJfU2VydmVyUGlja2VyIiwiX0F1dGhCb2R5IiwiX0F1dGhIZWFkZXIiLCJfQWNjZXNzaWJsZUJ1dHRvbiIsIl9hcnJheXMiLCJfU2V0dGluZ3MiLCJfYXV0aG9yaXplIiwib3duS2V5cyIsImUiLCJyIiwidCIsIk9iamVjdCIsImtleXMiLCJnZXRPd25Qcm9wZXJ0eVN5bWJvbHMiLCJvIiwiZmlsdGVyIiwiZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yIiwiZW51bWVyYWJsZSIsInB1c2giLCJhcHBseSIsIl9vYmplY3RTcHJlYWQiLCJhcmd1bWVudHMiLCJsZW5ndGgiLCJmb3JFYWNoIiwiX2RlZmluZVByb3BlcnR5MiIsImRlZmF1bHQiLCJnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3JzIiwiZGVmaW5lUHJvcGVydGllcyIsImRlZmluZVByb3BlcnR5IiwiTG9naW5Db21wb25lbnQiLCJSZWFjdCIsIlB1cmVDb21wb25lbnQiLCJjb25zdHJ1Y3RvciIsInByb3BzIiwic3RhdGUiLCJidXN5IiwidXNlcm5hbWUiLCJwaG9uZUNvdW50cnkiLCJwaG9uZU51bWJlciIsInBhc3N3b3JkIiwic2VydmVySXNBbGl2ZSIsInNldFN0YXRlIiwiYWxpdmVBZ2FpbiIsIkF1dG9EaXNjb3ZlcnlVdGlscyIsInZhbGlkYXRlU2VydmVyQ29uZmlnV2l0aFN0YXRpY1VybHMiLCJzZXJ2ZXJDb25maWciLCJoc1VybCIsImlzVXJsIiwiZXJyb3JUZXh0IiwiY29tcG9uZW50U3RhdGUiLCJhdXRoQ29tcG9uZW50U3RhdGVGb3JFcnJvciIsImJ1c3lMb2dnaW5nSW4iLCJzZXJ2ZXJFcnJvcklzRmF0YWwiLCJsb2dpbkluY29ycmVjdCIsImxvZ2luTG9naWMiLCJsb2dpblZpYVBhc3N3b3JkIiwidGhlbiIsImRhdGEiLCJvbkxvZ2dlZEluIiwiZXJyb3IiLCJ1bm1vdW50ZWQiLCJodHRwU3RhdHVzIiwiaW5kZXhPZiIsIl90IiwibWVzc2FnZUZvckxvZ2luRXJyb3IiLCJkb1dlbGxrbm93bkxvb2t1cCIsImNhblRyeUxvZ2luIiwic2VydmVyTmFtZSIsInNwbGl0Iiwic2xpY2UiLCJqb2luIiwicmVzdWx0IiwidmFsaWRhdGVTZXJ2ZXJOYW1lIiwib25TZXJ2ZXJDb25maWdDaGFuZ2UiLCJsb2dnZXIiLCJtZXNzYWdlIiwiVXNlckZyaWVuZGx5RXJyb3IiLCJ0cmFuc2xhdGVkTWVzc2FnZSIsImRpc2NvdmVyeVN0YXRlIiwiaXNMaXZlbGluZXNzRXJyb3IiLCJldiIsInByZXZlbnREZWZhdWx0Iiwic3RvcFByb3BhZ2F0aW9uIiwib25SZWdpc3RlckNsaWNrIiwiaGFzUGFzc3dvcmRGbG93IiwiZmxvd3MiLCJmaW5kIiwiZmxvdyIsInR5cGUiLCJzc29GbG93Iiwic3NvS2luZCIsIlBsYXRmb3JtUGVnIiwiZ2V0Iiwic3RhcnRTaW5nbGVTaWduT24iLCJjcmVhdGVUZW1wb3JhcnlDbGllbnQiLCJmcmFnbWVudEFmdGVyTG9naW4iLCJ1bmRlZmluZWQiLCJTU09BY3Rpb24iLCJSRUdJU1RFUiIsInN0ZXBSZW5kZXJlck1hcCIsImxvZyIsImNyZWF0ZUVsZW1lbnQiLCJvblN1Ym1pdCIsIm9uUGFzc3dvcmRMb2dpbiIsIm9uVXNlcm5hbWVDaGFuZ2VkIiwib25Vc2VybmFtZUJsdXIiLCJvblBob25lQ291bnRyeUNoYW5nZWQiLCJvblBob25lTnVtYmVyQ2hhbmdlZCIsIm9uRm9yZ290UGFzc3dvcmRDbGljayIsImRpc2FibGVTdWJtaXQiLCJpc0J1c3kiLCJpc1N5bmNpbmciLCJjbGFzc05hbWUiLCJraW5kIiwib25DbGljayIsInN0YXJ0T2lkY0xvZ2luIiwiZGVsZWdhdGVkQXV0aGVudGljYXRpb24iLCJjbGllbnRJZCIsImxvZ2luVHlwZSIsIm1hdHJpeENsaWVudCIsInByaW1hcnkiLCJhY3Rpb24iLCJMT0dJTiIsImRpc2FibGVkIiwib2lkY05hdGl2ZUZsb3dFbmFibGVkIiwiU2V0dGluZ3NTdG9yZSIsImdldFZhbHVlIiwiRmVhdHVyZXMiLCJPaWRjTmF0aXZlRmxvdyIsImRlZmF1bHRVc2VybmFtZSIsInNlcnZlckRlYWRFcnJvciIsInJlbmRlclBhc3N3b3JkU3RlcCIsIm0ubG9naW4uY2FzIiwicmVuZGVyU3NvU3RlcCIsIm0ubG9naW4uc3NvIiwib2lkY05hdGl2ZUZsb3ciLCJyZW5kZXJPaWRjTmF0aXZlU3RlcCIsImNvbXBvbmVudERpZE1vdW50IiwiaW5pdExvZ2luTG9naWMiLCJjb21wb25lbnRXaWxsVW5tb3VudCIsImNvbXBvbmVudERpZFVwZGF0ZSIsInByZXZQcm9wcyIsImNoZWNrU2VydmVyTGl2ZWxpbmVzcyIsIndhcm5pbmciLCJpc0RlZmF1bHRTZXJ2ZXIiLCJpc0RlZmF1bHQiLCJmYWxsYmFja0hzVXJsIiwiTG9naW4iLCJkZWZhdWx0RGV2aWNlRGlzcGxheU5hbWUiLCJnZXRGbG93cyIsInN1cHBvcnRlZEZsb3dzIiwiaXNTdXBwb3J0ZWRGbG93IiwiZXJyIiwibWVzc2FnZUZvckNvbm5lY3Rpb25FcnJvciIsImZpbmFsbHkiLCJyZW5kZXJMb2dpbkNvbXBvbmVudEZvckZsb3dzIiwib3JkZXIiLCJmaWx0ZXJCb29sZWFuIiwibWFwIiwiRnJhZ21lbnQiLCJzdGVwUmVuZGVyZXIiLCJrZXkiLCJyZW5kZXIiLCJsb2FkZXIiLCJlcnJvclRleHRTZWN0aW9uIiwic2VydmVyRGVhZFNlY3Rpb24iLCJjbGFzc2VzIiwiY2xhc3NOYW1lcyIsIm14X0xvZ2luX2Vycm9yIiwibXhfTG9naW5fc2VydmVyRXJyb3IiLCJteF9Mb2dpbl9zZXJ2ZXJFcnJvck5vbkZhdGFsIiwiZm9vdGVyIiwidyIsImgiLCJVSUZlYXR1cmUiLCJSZWdpc3RyYXRpb24iLCJhIiwic3ViIiwib25UcnlSZWdpc3RlckNsaWNrIiwiZGlzYWJsZUxhbmd1YWdlU2VsZWN0b3IiLCJleHBvcnRzIl0sInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvc3RydWN0dXJlcy9hdXRoL0xvZ2luLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuQ29weXJpZ2h0IDIwMjQgTmV3IFZlY3RvciBMdGQuXG5Db3B5cmlnaHQgMjAxNS0yMDIxIFRoZSBNYXRyaXgub3JnIEZvdW5kYXRpb24gQy5JLkMuXG5cblNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBR1BMLTMuMC1vbmx5IE9SIEdQTC0zLjAtb25seVxuUGxlYXNlIHNlZSBMSUNFTlNFIGZpbGVzIGluIHRoZSByZXBvc2l0b3J5IHJvb3QgZm9yIGZ1bGwgZGV0YWlscy5cbiovXG5cbmltcG9ydCBSZWFjdCwgeyBSZWFjdE5vZGUgfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCBjbGFzc05hbWVzIGZyb20gXCJjbGFzc25hbWVzXCI7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tIFwibWF0cml4LWpzLXNkay9zcmMvbG9nZ2VyXCI7XG5pbXBvcnQgeyBTU09GbG93LCBTU09BY3Rpb24gfSBmcm9tIFwibWF0cml4LWpzLXNkay9zcmMvbWF0cml4XCI7XG5cbmltcG9ydCB7IF90LCBVc2VyRnJpZW5kbHlFcnJvciB9IGZyb20gXCIuLi8uLi8uLi9sYW5ndWFnZUhhbmRsZXJcIjtcbmltcG9ydCBMb2dpbiwgeyBDbGllbnRMb2dpbkZsb3csIE9pZGNOYXRpdmVGbG93IH0gZnJvbSBcIi4uLy4uLy4uL0xvZ2luXCI7XG5pbXBvcnQgeyBtZXNzYWdlRm9yQ29ubmVjdGlvbkVycm9yLCBtZXNzYWdlRm9yTG9naW5FcnJvciB9IGZyb20gXCIuLi8uLi8uLi91dGlscy9FcnJvclV0aWxzXCI7XG5pbXBvcnQgQXV0b0Rpc2NvdmVyeVV0aWxzIGZyb20gXCIuLi8uLi8uLi91dGlscy9BdXRvRGlzY292ZXJ5VXRpbHNcIjtcbmltcG9ydCBBdXRoUGFnZSBmcm9tIFwiLi4vLi4vdmlld3MvYXV0aC9BdXRoUGFnZVwiO1xuaW1wb3J0IFBsYXRmb3JtUGVnIGZyb20gXCIuLi8uLi8uLi9QbGF0Zm9ybVBlZ1wiO1xuaW1wb3J0IFNldHRpbmdzU3RvcmUgZnJvbSBcIi4uLy4uLy4uL3NldHRpbmdzL1NldHRpbmdzU3RvcmVcIjtcbmltcG9ydCB7IFVJRmVhdHVyZSB9IGZyb20gXCIuLi8uLi8uLi9zZXR0aW5ncy9VSUZlYXR1cmVcIjtcbmltcG9ydCB7IElNYXRyaXhDbGllbnRDcmVkcyB9IGZyb20gXCIuLi8uLi8uLi9NYXRyaXhDbGllbnRQZWdcIjtcbmltcG9ydCBQYXNzd29yZExvZ2luIGZyb20gXCIuLi8uLi92aWV3cy9hdXRoL1Bhc3N3b3JkTG9naW5cIjtcbmltcG9ydCBJbmxpbmVTcGlubmVyIGZyb20gXCIuLi8uLi92aWV3cy9lbGVtZW50cy9JbmxpbmVTcGlubmVyXCI7XG5pbXBvcnQgU3Bpbm5lciBmcm9tIFwiLi4vLi4vdmlld3MvZWxlbWVudHMvU3Bpbm5lclwiO1xuaW1wb3J0IFNTT0J1dHRvbnMgZnJvbSBcIi4uLy4uL3ZpZXdzL2VsZW1lbnRzL1NTT0J1dHRvbnNcIjtcbmltcG9ydCBTZXJ2ZXJQaWNrZXIgZnJvbSBcIi4uLy4uL3ZpZXdzL2VsZW1lbnRzL1NlcnZlclBpY2tlclwiO1xuaW1wb3J0IEF1dGhCb2R5IGZyb20gXCIuLi8uLi92aWV3cy9hdXRoL0F1dGhCb2R5XCI7XG5pbXBvcnQgQXV0aEhlYWRlciBmcm9tIFwiLi4vLi4vdmlld3MvYXV0aC9BdXRoSGVhZGVyXCI7XG5pbXBvcnQgQWNjZXNzaWJsZUJ1dHRvbiwgeyBCdXR0b25FdmVudCB9IGZyb20gXCIuLi8uLi92aWV3cy9lbGVtZW50cy9BY2Nlc3NpYmxlQnV0dG9uXCI7XG5pbXBvcnQgeyBWYWxpZGF0ZWRTZXJ2ZXJDb25maWcgfSBmcm9tIFwiLi4vLi4vLi4vdXRpbHMvVmFsaWRhdGVkU2VydmVyQ29uZmlnXCI7XG5pbXBvcnQgeyBmaWx0ZXJCb29sZWFuIH0gZnJvbSBcIi4uLy4uLy4uL3V0aWxzL2FycmF5c1wiO1xuaW1wb3J0IHsgRmVhdHVyZXMgfSBmcm9tIFwiLi4vLi4vLi4vc2V0dGluZ3MvU2V0dGluZ3NcIjtcbmltcG9ydCB7IHN0YXJ0T2lkY0xvZ2luIH0gZnJvbSBcIi4uLy4uLy4uL3V0aWxzL29pZGMvYXV0aG9yaXplXCI7XG5cbmludGVyZmFjZSBJUHJvcHMge1xuICAgIHNlcnZlckNvbmZpZzogVmFsaWRhdGVkU2VydmVyQ29uZmlnO1xuICAgIC8vIElmIHRydWUsIHRoZSBjb21wb25lbnQgd2lsbCBjb25zaWRlciBpdHNlbGYgYnVzeS5cbiAgICBidXN5PzogYm9vbGVhbjtcbiAgICBpc1N5bmNpbmc/OiBib29sZWFuO1xuICAgIC8vIFNlY29uZGFyeSBIUyB3aGljaCB3ZSB0cnkgdG8gbG9nIGludG8gaWYgdGhlIHVzZXIgaXMgdXNpbmdcbiAgICAvLyB0aGUgZGVmYXVsdCBIUyBidXQgbG9naW4gZmFpbHMuIFVzZWZ1bCBmb3IgbWlncmF0aW5nIHRvIGFcbiAgICAvLyBkaWZmZXJlbnQgaG9tZXNlcnZlciB3aXRob3V0IGNvbmZ1c2luZyB1c2Vycy5cbiAgICBmYWxsYmFja0hzVXJsPzogc3RyaW5nO1xuICAgIGRlZmF1bHREZXZpY2VEaXNwbGF5TmFtZT86IHN0cmluZztcbiAgICBmcmFnbWVudEFmdGVyTG9naW4/OiBzdHJpbmc7XG4gICAgZGVmYXVsdFVzZXJuYW1lPzogc3RyaW5nO1xuXG4gICAgLy8gQ2FsbGVkIHdoZW4gdGhlIHVzZXIgaGFzIGxvZ2dlZCBpbi4gUGFyYW1zOlxuICAgIC8vIC0gVGhlIG9iamVjdCByZXR1cm5lZCBieSB0aGUgbG9naW4gQVBJXG4gICAgLy8gLSBUaGUgdXNlcidzIHBhc3N3b3JkLCBpZiBhcHBsaWNhYmxlLCAobWF5IGJlIGNhY2hlZCBpbiBtZW1vcnkgZm9yIGFcbiAgICAvLyAgIHNob3J0IHRpbWUgc28gdGhlIHVzZXIgaXMgbm90IHJlcXVpcmVkIHRvIHJlLWVudGVyIHRoZWlyIHBhc3N3b3JkXG4gICAgLy8gICBmb3Igb3BlcmF0aW9ucyBsaWtlIHVwbG9hZGluZyBjcm9zcy1zaWduaW5nIGtleXMpLlxuICAgIG9uTG9nZ2VkSW4oZGF0YTogSU1hdHJpeENsaWVudENyZWRzLCBwYXNzd29yZDogc3RyaW5nKTogdm9pZDtcblxuICAgIC8vIGxvZ2luIHNob3VsZG4ndCBrbm93IG9yIGNhcmUgaG93IHJlZ2lzdHJhdGlvbiwgcGFzc3dvcmQgcmVjb3ZlcnksIGV0YyBpcyBkb25lLlxuICAgIG9uUmVnaXN0ZXJDbGljaygpOiB2b2lkO1xuICAgIG9uRm9yZ290UGFzc3dvcmRDbGljaz8oKTogdm9pZDtcbiAgICBvblNlcnZlckNvbmZpZ0NoYW5nZShjb25maWc6IFZhbGlkYXRlZFNlcnZlckNvbmZpZyk6IHZvaWQ7XG59XG5cbmludGVyZmFjZSBJU3RhdGUge1xuICAgIGJ1c3k6IGJvb2xlYW47XG4gICAgYnVzeUxvZ2dpbmdJbj86IGJvb2xlYW47XG4gICAgZXJyb3JUZXh0PzogUmVhY3ROb2RlO1xuICAgIGxvZ2luSW5jb3JyZWN0OiBib29sZWFuO1xuICAgIC8vIGNhbiB3ZSBhdHRlbXB0IHRvIGxvZyBpbiBvciBhcmUgdGhlcmUgdmFsaWRhdGlvbiBlcnJvcnM/XG4gICAgY2FuVHJ5TG9naW46IGJvb2xlYW47XG5cbiAgICBmbG93cz86IENsaWVudExvZ2luRmxvd1tdO1xuXG4gICAgLy8gdXNlZCBmb3IgcHJlc2VydmluZyBmb3JtIHZhbHVlcyB3aGVuIGNoYW5naW5nIGhvbWVzZXJ2ZXJcbiAgICB1c2VybmFtZTogc3RyaW5nO1xuICAgIHBob25lQ291bnRyeTogc3RyaW5nO1xuICAgIHBob25lTnVtYmVyOiBzdHJpbmc7XG5cbiAgICAvLyBXZSBwZXJmb3JtIGxpdmVsaW5lc3MgY2hlY2tzIGxhdGVyLCBidXQgZm9yIG5vdyBzdXBwcmVzcyB0aGUgZXJyb3JzLlxuICAgIC8vIFdlIGFsc28gdHJhY2sgdGhlIHNlcnZlciBkZWFkIGVycm9ycyBpbmRlcGVuZGVudGx5IG9mIHRoZSByZWd1bGFyIGVycm9ycyBzb1xuICAgIC8vIHRoYXQgd2UgY2FuIHJlbmRlciBpdCBkaWZmZXJlbnRseSwgYW5kIG92ZXJyaWRlIGFueSBvdGhlciBlcnJvciB0aGUgdXNlciBtYXlcbiAgICAvLyBiZSBzZWVpbmcuXG4gICAgc2VydmVySXNBbGl2ZTogYm9vbGVhbjtcbiAgICBzZXJ2ZXJFcnJvcklzRmF0YWw6IGJvb2xlYW47XG4gICAgc2VydmVyRGVhZEVycm9yPzogUmVhY3ROb2RlO1xufVxuXG50eXBlIE9uUGFzc3dvcmRMb2dpbiA9IHtcbiAgICAodXNlcm5hbWU6IHN0cmluZywgcGhvbmVDb3VudHJ5OiB1bmRlZmluZWQsIHBob25lTnVtYmVyOiB1bmRlZmluZWQsIHBhc3N3b3JkOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+O1xuICAgICh1c2VybmFtZTogdW5kZWZpbmVkLCBwaG9uZUNvdW50cnk6IHN0cmluZywgcGhvbmVOdW1iZXI6IHN0cmluZywgcGFzc3dvcmQ6IHN0cmluZyk6IFByb21pc2U8dm9pZD47XG59O1xuXG4vKlxuICogQSB3aXJlIGNvbXBvbmVudCB3aGljaCBnbHVlcyB0b2dldGhlciBsb2dpbiBVSSBjb21wb25lbnRzIGFuZCBMb2dpbiBsb2dpY1xuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBMb2dpbkNvbXBvbmVudCBleHRlbmRzIFJlYWN0LlB1cmVDb21wb25lbnQ8SVByb3BzLCBJU3RhdGU+IHtcbiAgICBwcml2YXRlIHVubW91bnRlZCA9IGZhbHNlO1xuICAgIHByaXZhdGUgb2lkY05hdGl2ZUZsb3dFbmFibGVkID0gZmFsc2U7XG4gICAgcHJpdmF0ZSBsb2dpbkxvZ2ljITogTG9naW47XG5cbiAgICBwcml2YXRlIHJlYWRvbmx5IHN0ZXBSZW5kZXJlck1hcDogUmVjb3JkPHN0cmluZywgKCkgPT4gUmVhY3ROb2RlPjtcblxuICAgIHB1YmxpYyBjb25zdHJ1Y3Rvcihwcm9wczogSVByb3BzKSB7XG4gICAgICAgIHN1cGVyKHByb3BzKTtcblxuICAgICAgICAvLyBvbmx5IHNldCBvbiBhIGNvbmZpZyBsZXZlbCwgc28gd2UgZG9uJ3QgbmVlZCB0byB3YXRjaFxuICAgICAgICB0aGlzLm9pZGNOYXRpdmVGbG93RW5hYmxlZCA9IFNldHRpbmdzU3RvcmUuZ2V0VmFsdWUoRmVhdHVyZXMuT2lkY05hdGl2ZUZsb3cpO1xuXG4gICAgICAgIHRoaXMuc3RhdGUgPSB7XG4gICAgICAgICAgICBidXN5OiBmYWxzZSxcbiAgICAgICAgICAgIGVycm9yVGV4dDogbnVsbCxcbiAgICAgICAgICAgIGxvZ2luSW5jb3JyZWN0OiBmYWxzZSxcbiAgICAgICAgICAgIGNhblRyeUxvZ2luOiB0cnVlLFxuXG4gICAgICAgICAgICB1c2VybmFtZTogcHJvcHMuZGVmYXVsdFVzZXJuYW1lID8gcHJvcHMuZGVmYXVsdFVzZXJuYW1lIDogXCJcIixcbiAgICAgICAgICAgIHBob25lQ291bnRyeTogXCJcIixcbiAgICAgICAgICAgIHBob25lTnVtYmVyOiBcIlwiLFxuXG4gICAgICAgICAgICBzZXJ2ZXJJc0FsaXZlOiB0cnVlLFxuICAgICAgICAgICAgc2VydmVyRXJyb3JJc0ZhdGFsOiBmYWxzZSxcbiAgICAgICAgICAgIHNlcnZlckRlYWRFcnJvcjogXCJcIixcbiAgICAgICAgfTtcblxuICAgICAgICAvLyBtYXAgZnJvbSBsb2dpbiBzdGVwIHR5cGUgdG8gYSBmdW5jdGlvbiB3aGljaCB3aWxsIHJlbmRlciBhIGNvbnRyb2xcbiAgICAgICAgLy8gbGV0dGluZyB5b3UgZG8gdGhhdCBsb2dpbiB0eXBlXG4gICAgICAgIHRoaXMuc3RlcFJlbmRlcmVyTWFwID0ge1xuICAgICAgICAgICAgXCJtLmxvZ2luLnBhc3N3b3JkXCI6IHRoaXMucmVuZGVyUGFzc3dvcmRTdGVwLFxuXG4gICAgICAgICAgICAvLyBDQVMgYW5kIFNTTyBhcmUgdGhlIHNhbWUgdGhpbmcsIG1vZHVsbyB0aGUgdXJsIHdlIGxpbmsgdG9cbiAgICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbmFtaW5nLWNvbnZlbnRpb25cbiAgICAgICAgICAgIFwibS5sb2dpbi5jYXNcIjogKCkgPT4gdGhpcy5yZW5kZXJTc29TdGVwKFwiY2FzXCIpLFxuICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uYW1pbmctY29udmVudGlvblxuICAgICAgICAgICAgXCJtLmxvZ2luLnNzb1wiOiAoKSA9PiB0aGlzLnJlbmRlclNzb1N0ZXAoXCJzc29cIiksXG4gICAgICAgICAgICBcIm9pZGNOYXRpdmVGbG93XCI6ICgpID0+IHRoaXMucmVuZGVyT2lkY05hdGl2ZVN0ZXAoKSxcbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICBwdWJsaWMgY29tcG9uZW50RGlkTW91bnQoKTogdm9pZCB7XG4gICAgICAgIHRoaXMuaW5pdExvZ2luTG9naWModGhpcy5wcm9wcy5zZXJ2ZXJDb25maWcpO1xuICAgIH1cblxuICAgIHB1YmxpYyBjb21wb25lbnRXaWxsVW5tb3VudCgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy51bm1vdW50ZWQgPSB0cnVlO1xuICAgIH1cblxuICAgIHB1YmxpYyBjb21wb25lbnREaWRVcGRhdGUocHJldlByb3BzOiBJUHJvcHMpOiB2b2lkIHtcbiAgICAgICAgaWYgKFxuICAgICAgICAgICAgcHJldlByb3BzLnNlcnZlckNvbmZpZy5oc1VybCAhPT0gdGhpcy5wcm9wcy5zZXJ2ZXJDb25maWcuaHNVcmwgfHxcbiAgICAgICAgICAgIHByZXZQcm9wcy5zZXJ2ZXJDb25maWcuaXNVcmwgIT09IHRoaXMucHJvcHMuc2VydmVyQ29uZmlnLmlzVXJsIHx8XG4gICAgICAgICAgICAvLyBkZWxlZ2F0ZWRBdXRoZW50aWNhdGlvbiBpcyBvbmx5IHNldCBieSBidWlsZFZhbGlkYXRlZENvbmZpZ0Zyb21EaXNjb3ZlcnkgYW5kIHdvbid0IGJlIG1vZGlmaWVkXG4gICAgICAgICAgICAvLyBzbyBzaGFsbG93IGNvbXBhcmlzb24gaXMgZmluZVxuICAgICAgICAgICAgcHJldlByb3BzLnNlcnZlckNvbmZpZy5kZWxlZ2F0ZWRBdXRoZW50aWNhdGlvbiAhPT0gdGhpcy5wcm9wcy5zZXJ2ZXJDb25maWcuZGVsZWdhdGVkQXV0aGVudGljYXRpb25cbiAgICAgICAgKSB7XG4gICAgICAgICAgICAvLyBFbnN1cmUgdGhhdCB3ZSBlbmQgdXAgYWN0dWFsbHkgbG9nZ2luZyBpbiB0byB0aGUgcmlnaHQgcGxhY2VcbiAgICAgICAgICAgIHRoaXMuaW5pdExvZ2luTG9naWModGhpcy5wcm9wcy5zZXJ2ZXJDb25maWcpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHVibGljIGlzQnVzeSA9ICgpOiBib29sZWFuID0+ICEhdGhpcy5zdGF0ZS5idXN5IHx8ICEhdGhpcy5wcm9wcy5idXN5O1xuXG4gICAgcHVibGljIG9uUGFzc3dvcmRMb2dpbjogT25QYXNzd29yZExvZ2luID0gYXN5bmMgKFxuICAgICAgICB1c2VybmFtZTogc3RyaW5nIHwgdW5kZWZpbmVkLFxuICAgICAgICBwaG9uZUNvdW50cnk6IHN0cmluZyB8IHVuZGVmaW5lZCxcbiAgICAgICAgcGhvbmVOdW1iZXI6IHN0cmluZyB8IHVuZGVmaW5lZCxcbiAgICAgICAgcGFzc3dvcmQ6IHN0cmluZyxcbiAgICApOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICAgICAgaWYgKCF0aGlzLnN0YXRlLnNlcnZlcklzQWxpdmUpIHtcbiAgICAgICAgICAgIHRoaXMuc2V0U3RhdGUoeyBidXN5OiB0cnVlIH0pO1xuICAgICAgICAgICAgLy8gRG8gYSBxdWljayBsaXZlbGluZXNzIGNoZWNrIG9uIHRoZSBVUkxzXG4gICAgICAgICAgICBsZXQgYWxpdmVBZ2FpbiA9IHRydWU7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIGF3YWl0IEF1dG9EaXNjb3ZlcnlVdGlscy52YWxpZGF0ZVNlcnZlckNvbmZpZ1dpdGhTdGF0aWNVcmxzKFxuICAgICAgICAgICAgICAgICAgICB0aGlzLnByb3BzLnNlcnZlckNvbmZpZy5oc1VybCxcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5wcm9wcy5zZXJ2ZXJDb25maWcuaXNVcmwsXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICB0aGlzLnNldFN0YXRlKHsgc2VydmVySXNBbGl2ZTogdHJ1ZSwgZXJyb3JUZXh0OiBcIlwiIH0pO1xuICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGNvbXBvbmVudFN0YXRlID0gQXV0b0Rpc2NvdmVyeVV0aWxzLmF1dGhDb21wb25lbnRTdGF0ZUZvckVycm9yKGUpO1xuICAgICAgICAgICAgICAgIHRoaXMuc2V0U3RhdGUoe1xuICAgICAgICAgICAgICAgICAgICBidXN5OiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgYnVzeUxvZ2dpbmdJbjogZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgIC4uLmNvbXBvbmVudFN0YXRlLFxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIGFsaXZlQWdhaW4gPSAhY29tcG9uZW50U3RhdGUuc2VydmVyRXJyb3JJc0ZhdGFsO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBQcmV2ZW50IHBlb3BsZSBmcm9tIHN1Ym1pdHRpbmcgdGhlaXIgcGFzc3dvcmQgd2hlbiBzb21ldGhpbmcgaXNuJ3QgcmlnaHQuXG4gICAgICAgICAgICBpZiAoIWFsaXZlQWdhaW4pIHtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLnNldFN0YXRlKHtcbiAgICAgICAgICAgIGJ1c3k6IHRydWUsXG4gICAgICAgICAgICBidXN5TG9nZ2luZ0luOiB0cnVlLFxuICAgICAgICAgICAgZXJyb3JUZXh0OiBudWxsLFxuICAgICAgICAgICAgbG9naW5JbmNvcnJlY3Q6IGZhbHNlLFxuICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLmxvZ2luTG9naWMubG9naW5WaWFQYXNzd29yZCh1c2VybmFtZSwgcGhvbmVDb3VudHJ5LCBwaG9uZU51bWJlciwgcGFzc3dvcmQpLnRoZW4oXG4gICAgICAgICAgICAoZGF0YSkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuc2V0U3RhdGUoeyBzZXJ2ZXJJc0FsaXZlOiB0cnVlIH0pOyAvLyBpdCBtdXN0IGJlLCB3ZSBsb2dnZWQgaW4uXG4gICAgICAgICAgICAgICAgdGhpcy5wcm9wcy5vbkxvZ2dlZEluKGRhdGEsIHBhc3N3b3JkKTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAoZXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy51bm1vdW50ZWQpIHJldHVybjtcblxuICAgICAgICAgICAgICAgIGxldCBlcnJvclRleHQ6IFJlYWN0Tm9kZTtcbiAgICAgICAgICAgICAgICAvLyBTb21lIGVycm9yIHN0cmluZ3Mgb25seSBhcHBseSBmb3IgbG9nZ2luZyBpblxuICAgICAgICAgICAgICAgIGlmIChlcnJvci5odHRwU3RhdHVzID09PSA0MDAgJiYgdXNlcm5hbWUgJiYgdXNlcm5hbWUuaW5kZXhPZihcIkBcIikgPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgIGVycm9yVGV4dCA9IF90KFwiYXV0aHx1bnN1cHBvcnRlZF9hdXRoX2VtYWlsXCIpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGVycm9yVGV4dCA9IG1lc3NhZ2VGb3JMb2dpbkVycm9yKGVycm9yLCB0aGlzLnByb3BzLnNlcnZlckNvbmZpZyk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgdGhpcy5zZXRTdGF0ZSh7XG4gICAgICAgICAgICAgICAgICAgIGJ1c3k6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICBidXN5TG9nZ2luZ0luOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgZXJyb3JUZXh0LFxuICAgICAgICAgICAgICAgICAgICAvLyA0MDEgd291bGQgYmUgdGhlIHNlbnNpYmxlIHN0YXR1cyBjb2RlIGZvciAnaW5jb3JyZWN0IHBhc3N3b3JkJ1xuICAgICAgICAgICAgICAgICAgICAvLyBidXQgdGhlIGxvZ2luIEFQSSBnaXZlcyBhIDQwMyBodHRwczovL21hdHJpeC5vcmcvamlyYS9icm93c2UvU1lOLTc0NFxuICAgICAgICAgICAgICAgICAgICAvLyBtZW50aW9ucyB0aGlzIChhbHRob3VnaCB0aGUgYnVnIGlzIGZvciBVSSBhdXRoIHdoaWNoIGlzIG5vdCB0aGlzKVxuICAgICAgICAgICAgICAgICAgICAvLyBXZSB0cmVhdCBib3RoIGFzIGFuIGluY29ycmVjdCBwYXNzd29yZFxuICAgICAgICAgICAgICAgICAgICBsb2dpbkluY29ycmVjdDogZXJyb3IuaHR0cFN0YXR1cyA9PT0gNDAxIHx8IGVycm9yLmh0dHBTdGF0dXMgPT09IDQwMyxcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICk7XG4gICAgfTtcblxuICAgIHB1YmxpYyBvblVzZXJuYW1lQ2hhbmdlZCA9ICh1c2VybmFtZTogc3RyaW5nKTogdm9pZCA9PiB7XG4gICAgICAgIHRoaXMuc2V0U3RhdGUoeyB1c2VybmFtZSB9KTtcbiAgICB9O1xuXG4gICAgcHVibGljIG9uVXNlcm5hbWVCbHVyID0gYXN5bmMgKHVzZXJuYW1lOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICAgICAgY29uc3QgZG9XZWxsa25vd25Mb29rdXAgPSB1c2VybmFtZVswXSA9PT0gXCJAXCI7XG4gICAgICAgIHRoaXMuc2V0U3RhdGUoe1xuICAgICAgICAgICAgdXNlcm5hbWU6IHVzZXJuYW1lLFxuICAgICAgICAgICAgYnVzeTogZG9XZWxsa25vd25Mb29rdXAsXG4gICAgICAgICAgICBlcnJvclRleHQ6IG51bGwsXG4gICAgICAgICAgICBjYW5UcnlMb2dpbjogdHJ1ZSxcbiAgICAgICAgfSk7XG4gICAgICAgIGlmIChkb1dlbGxrbm93bkxvb2t1cCkge1xuICAgICAgICAgICAgY29uc3Qgc2VydmVyTmFtZSA9IHVzZXJuYW1lLnNwbGl0KFwiOlwiKS5zbGljZSgxKS5qb2luKFwiOlwiKTtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgQXV0b0Rpc2NvdmVyeVV0aWxzLnZhbGlkYXRlU2VydmVyTmFtZShzZXJ2ZXJOYW1lKTtcbiAgICAgICAgICAgICAgICB0aGlzLnByb3BzLm9uU2VydmVyQ29uZmlnQ2hhbmdlKHJlc3VsdCk7XG4gICAgICAgICAgICAgICAgLy8gV2UnZCBsaWtlIHRvIHJlbHkgb24gbmV3IHByb3BzIGNvbWluZyBpbiB2aWEgYG9uU2VydmVyQ29uZmlnQ2hhbmdlYFxuICAgICAgICAgICAgICAgIC8vIHNvIHRoYXQgd2Uga25vdyB0aGUgc2VydmVycyBoYXZlIGRlZmluaXRlbHkgdXBkYXRlZCBiZWZvcmUgY2xlYXJpbmdcbiAgICAgICAgICAgICAgICAvLyB0aGUgYnVzeSBzdGF0ZS4gSW4gdGhlIGNhc2Ugb2YgYSBmdWxsIE1YSUQgdGhhdCByZXNvbHZlcyB0byB0aGUgc2FtZVxuICAgICAgICAgICAgICAgIC8vIEhTIGFzIEVsZW1lbnQncyBkZWZhdWx0IEhTIHRob3VnaCwgdGhlcmUgbWF5IG5vdCBiZSBhbnkgc2VydmVyIGNoYW5nZS5cbiAgICAgICAgICAgICAgICAvLyBUbyBhdm9pZCB0aGlzIHRyYXAsIHdlIGNsZWFyIGJ1c3kgaGVyZS4gRm9yIGNhc2VzIHdoZXJlIHRoZSBzZXJ2ZXJcbiAgICAgICAgICAgICAgICAvLyBhY3R1YWxseSBoYXMgY2hhbmdlZCwgYGluaXRMb2dpbkxvZ2ljYCB3aWxsIGJlIGNhbGxlZCBhbmQgbWFuYWdlc1xuICAgICAgICAgICAgICAgIC8vIGJ1c3kgc3RhdGUgZm9yIGl0cyBvd24gbGl2ZW5lc3MgY2hlY2suXG4gICAgICAgICAgICAgICAgdGhpcy5zZXRTdGF0ZSh7XG4gICAgICAgICAgICAgICAgICAgIGJ1c3k6IGZhbHNlLFxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICAgIGxvZ2dlci5lcnJvcihcIlByb2JsZW0gcGFyc2luZyBVUkwgb3IgdW5oYW5kbGVkIGVycm9yIGRvaW5nIC53ZWxsLWtub3duIGRpc2NvdmVyeTpcIiwgZSk7XG5cbiAgICAgICAgICAgICAgICBsZXQgbWVzc2FnZSA9IF90KFwiYXV0aHxmYWlsZWRfaG9tZXNlcnZlcl9kaXNjb3ZlcnlcIik7XG4gICAgICAgICAgICAgICAgaWYgKGUgaW5zdGFuY2VvZiBVc2VyRnJpZW5kbHlFcnJvciAmJiBlLnRyYW5zbGF0ZWRNZXNzYWdlKSB7XG4gICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBlLnRyYW5zbGF0ZWRNZXNzYWdlO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGxldCBlcnJvclRleHQ6IFJlYWN0Tm9kZSA9IG1lc3NhZ2U7XG4gICAgICAgICAgICAgICAgbGV0IGRpc2NvdmVyeVN0YXRlID0ge307XG4gICAgICAgICAgICAgICAgaWYgKEF1dG9EaXNjb3ZlcnlVdGlscy5pc0xpdmVsaW5lc3NFcnJvcihlKSkge1xuICAgICAgICAgICAgICAgICAgICBlcnJvclRleHQgPSB0aGlzLnN0YXRlLmVycm9yVGV4dDtcbiAgICAgICAgICAgICAgICAgICAgZGlzY292ZXJ5U3RhdGUgPSBBdXRvRGlzY292ZXJ5VXRpbHMuYXV0aENvbXBvbmVudFN0YXRlRm9yRXJyb3IoZSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgdGhpcy5zZXRTdGF0ZSh7XG4gICAgICAgICAgICAgICAgICAgIGJ1c3k6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICBlcnJvclRleHQsXG4gICAgICAgICAgICAgICAgICAgIC4uLmRpc2NvdmVyeVN0YXRlLFxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfTtcblxuICAgIHB1YmxpYyBvblBob25lQ291bnRyeUNoYW5nZWQgPSAocGhvbmVDb3VudHJ5OiBzdHJpbmcpOiB2b2lkID0+IHtcbiAgICAgICAgdGhpcy5zZXRTdGF0ZSh7IHBob25lQ291bnRyeSB9KTtcbiAgICB9O1xuXG4gICAgcHVibGljIG9uUGhvbmVOdW1iZXJDaGFuZ2VkID0gKHBob25lTnVtYmVyOiBzdHJpbmcpOiB2b2lkID0+IHtcbiAgICAgICAgdGhpcy5zZXRTdGF0ZSh7IHBob25lTnVtYmVyIH0pO1xuICAgIH07XG5cbiAgICBwdWJsaWMgb25SZWdpc3RlckNsaWNrID0gKGV2OiBCdXR0b25FdmVudCk6IHZvaWQgPT4ge1xuICAgICAgICBldi5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICBldi5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICAgICAgdGhpcy5wcm9wcy5vblJlZ2lzdGVyQ2xpY2soKTtcbiAgICB9O1xuXG4gICAgcHVibGljIG9uVHJ5UmVnaXN0ZXJDbGljayA9IChldjogQnV0dG9uRXZlbnQpOiB2b2lkID0+IHtcbiAgICAgICAgY29uc3QgaGFzUGFzc3dvcmRGbG93ID0gdGhpcy5zdGF0ZS5mbG93cz8uZmluZCgoZmxvdykgPT4gZmxvdy50eXBlID09PSBcIm0ubG9naW4ucGFzc3dvcmRcIik7XG4gICAgICAgIGNvbnN0IHNzb0Zsb3cgPSB0aGlzLnN0YXRlLmZsb3dzPy5maW5kKChmbG93KSA9PiBmbG93LnR5cGUgPT09IFwibS5sb2dpbi5zc29cIiB8fCBmbG93LnR5cGUgPT09IFwibS5sb2dpbi5jYXNcIik7XG4gICAgICAgIC8vIElmIGhhcyBubyBwYXNzd29yZCBmbG93IGJ1dCBhbiBTU08gZmxvdyBndWVzcyB0aGF0IHRoZSB1c2VyIHdhbnRzIHRvIHJlZ2lzdGVyIHdpdGggU1NPLlxuICAgICAgICAvLyBUT0RPOiBpbnN0ZWFkIGhpZGUgdGhlIFJlZ2lzdGVyIGJ1dHRvbiBpZiByZWdpc3RyYXRpb24gaXMgZGlzYWJsZWQgYnkgY2hlY2tpbmcgd2l0aCB0aGUgc2VydmVyLFxuICAgICAgICAvLyBoYXMgbm8gc3BlY2lmaWMgZXJyQ29kZSBjdXJyZW50bHkgYW5kIHVzZXMgTV9GT1JCSURERU4uXG4gICAgICAgIGlmIChzc29GbG93ICYmICFoYXNQYXNzd29yZEZsb3cpIHtcbiAgICAgICAgICAgIGV2LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICBldi5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICAgICAgICAgIGNvbnN0IHNzb0tpbmQgPSBzc29GbG93LnR5cGUgPT09IFwibS5sb2dpbi5zc29cIiA/IFwic3NvXCIgOiBcImNhc1wiO1xuICAgICAgICAgICAgUGxhdGZvcm1QZWcuZ2V0KCk/LnN0YXJ0U2luZ2xlU2lnbk9uKFxuICAgICAgICAgICAgICAgIHRoaXMubG9naW5Mb2dpYy5jcmVhdGVUZW1wb3JhcnlDbGllbnQoKSxcbiAgICAgICAgICAgICAgICBzc29LaW5kLFxuICAgICAgICAgICAgICAgIHRoaXMucHJvcHMuZnJhZ21lbnRBZnRlckxvZ2luLFxuICAgICAgICAgICAgICAgIHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgICBTU09BY3Rpb24uUkVHSVNURVIsXG4gICAgICAgICAgICApO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gRG9uJ3QgaW50ZXJjZXB0IC0ganVzdCBnbyB0aHJvdWdoIHRvIHRoZSByZWdpc3RlciBwYWdlXG4gICAgICAgICAgICB0aGlzLm9uUmVnaXN0ZXJDbGljayhldik7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgcHJpdmF0ZSBhc3luYyBjaGVja1NlcnZlckxpdmVsaW5lc3Moe1xuICAgICAgICBoc1VybCxcbiAgICAgICAgaXNVcmwsXG4gICAgfTogUGljazxWYWxpZGF0ZWRTZXJ2ZXJDb25maWcsIFwiaHNVcmxcIiB8IFwiaXNVcmxcIj4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgLy8gRG8gYSBxdWljayBsaXZlbGluZXNzIGNoZWNrIG9uIHRoZSBVUkxzXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCB7IHdhcm5pbmcgfSA9IGF3YWl0IEF1dG9EaXNjb3ZlcnlVdGlscy52YWxpZGF0ZVNlcnZlckNvbmZpZ1dpdGhTdGF0aWNVcmxzKGhzVXJsLCBpc1VybCk7XG4gICAgICAgICAgICBpZiAod2FybmluZykge1xuICAgICAgICAgICAgICAgIHRoaXMuc2V0U3RhdGUoe1xuICAgICAgICAgICAgICAgICAgICAuLi5BdXRvRGlzY292ZXJ5VXRpbHMuYXV0aENvbXBvbmVudFN0YXRlRm9yRXJyb3Iod2FybmluZyksXG4gICAgICAgICAgICAgICAgICAgIGVycm9yVGV4dDogXCJcIixcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhpcy5zZXRTdGF0ZSh7XG4gICAgICAgICAgICAgICAgICAgIHNlcnZlcklzQWxpdmU6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIGVycm9yVGV4dDogXCJcIixcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgdGhpcy5zZXRTdGF0ZSh7XG4gICAgICAgICAgICAgICAgYnVzeTogZmFsc2UsXG4gICAgICAgICAgICAgICAgLi4uQXV0b0Rpc2NvdmVyeVV0aWxzLmF1dGhDb21wb25lbnRTdGF0ZUZvckVycm9yKGUgYXMgRXJyb3IpLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGluaXRMb2dpbkxvZ2ljKHsgaHNVcmwsIGlzVXJsIH06IFZhbGlkYXRlZFNlcnZlckNvbmZpZyk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBsZXQgaXNEZWZhdWx0U2VydmVyID0gZmFsc2U7XG4gICAgICAgIGlmIChcbiAgICAgICAgICAgIHRoaXMucHJvcHMuc2VydmVyQ29uZmlnLmlzRGVmYXVsdCAmJlxuICAgICAgICAgICAgaHNVcmwgPT09IHRoaXMucHJvcHMuc2VydmVyQ29uZmlnLmhzVXJsICYmXG4gICAgICAgICAgICBpc1VybCA9PT0gdGhpcy5wcm9wcy5zZXJ2ZXJDb25maWcuaXNVcmxcbiAgICAgICAgKSB7XG4gICAgICAgICAgICBpc0RlZmF1bHRTZXJ2ZXIgPSB0cnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgZmFsbGJhY2tIc1VybCA9IGlzRGVmYXVsdFNlcnZlciA/IHRoaXMucHJvcHMuZmFsbGJhY2tIc1VybCEgOiBudWxsO1xuXG4gICAgICAgIHRoaXMuc2V0U3RhdGUoe1xuICAgICAgICAgICAgYnVzeTogdHJ1ZSxcbiAgICAgICAgICAgIGxvZ2luSW5jb3JyZWN0OiBmYWxzZSxcbiAgICAgICAgfSk7XG5cbiAgICAgICAgYXdhaXQgdGhpcy5jaGVja1NlcnZlckxpdmVsaW5lc3MoeyBoc1VybCwgaXNVcmwgfSk7XG5cbiAgICAgICAgY29uc3QgbG9naW5Mb2dpYyA9IG5ldyBMb2dpbihoc1VybCwgaXNVcmwsIGZhbGxiYWNrSHNVcmwsIHtcbiAgICAgICAgICAgIGRlZmF1bHREZXZpY2VEaXNwbGF5TmFtZTogdGhpcy5wcm9wcy5kZWZhdWx0RGV2aWNlRGlzcGxheU5hbWUsXG4gICAgICAgICAgICAvLyBpZiBuYXRpdmUgT0lEQyBpcyBlbmFibGVkIGluIHRoZSBjbGllbnQgcGFzcyB0aGUgc2VydmVyJ3MgZGVsZWdhdGVkIGF1dGggc2V0dGluZ3NcbiAgICAgICAgICAgIGRlbGVnYXRlZEF1dGhlbnRpY2F0aW9uOiB0aGlzLm9pZGNOYXRpdmVGbG93RW5hYmxlZFxuICAgICAgICAgICAgICAgID8gdGhpcy5wcm9wcy5zZXJ2ZXJDb25maWcuZGVsZWdhdGVkQXV0aGVudGljYXRpb25cbiAgICAgICAgICAgICAgICA6IHVuZGVmaW5lZCxcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMubG9naW5Mb2dpYyA9IGxvZ2luTG9naWM7XG5cbiAgICAgICAgbG9naW5Mb2dpY1xuICAgICAgICAgICAgLmdldEZsb3dzKClcbiAgICAgICAgICAgIC50aGVuKFxuICAgICAgICAgICAgICAgIChmbG93cykgPT4ge1xuICAgICAgICAgICAgICAgICAgICAvLyBsb29rIGZvciBhIGZsb3cgd2hlcmUgd2UgdW5kZXJzdGFuZCBhbGwgb2YgdGhlIHN0ZXBzLlxuICAgICAgICAgICAgICAgICAgICBjb25zdCBzdXBwb3J0ZWRGbG93cyA9IGZsb3dzLmZpbHRlcih0aGlzLmlzU3VwcG9ydGVkRmxvdyk7XG5cbiAgICAgICAgICAgICAgICAgICAgdGhpcy5zZXRTdGF0ZSh7XG4gICAgICAgICAgICAgICAgICAgICAgICBmbG93czogc3VwcG9ydGVkRmxvd3MsXG4gICAgICAgICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAgICAgICAgIGlmIChzdXBwb3J0ZWRGbG93cy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuc2V0U3RhdGUoe1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yVGV4dDogX3QoXCJhdXRofHVuc3VwcG9ydGVkX2F1dGhcIiksXG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgKGVycikgPT4ge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnNldFN0YXRlKHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yVGV4dDogbWVzc2FnZUZvckNvbm5lY3Rpb25FcnJvcihlcnIsIHRoaXMucHJvcHMuc2VydmVyQ29uZmlnKSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGxvZ2luSW5jb3JyZWN0OiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGNhblRyeUxvZ2luOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIC5maW5hbGx5KCgpID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLnNldFN0YXRlKHtcbiAgICAgICAgICAgICAgICAgICAgYnVzeTogZmFsc2UsXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGlzU3VwcG9ydGVkRmxvdyA9IChmbG93OiBDbGllbnRMb2dpbkZsb3cpOiBib29sZWFuID0+IHtcbiAgICAgICAgLy8gdGVjaG5pY2FsbHkgdGhlIGZsb3cgY2FuIGhhdmUgbXVsdGlwbGUgc3RlcHMsIGJ1dCBubyBvbmUgZG9lcyB0aGlzXG4gICAgICAgIC8vIGZvciBsb2dpbiBhbmQgbG9naW5Mb2dpYyBkb2Vzbid0IHN1cHBvcnQgaXQgc28gd2UgY2FuIGlnbm9yZSBpdC5cbiAgICAgICAgaWYgKCF0aGlzLnN0ZXBSZW5kZXJlck1hcFtmbG93LnR5cGVdKSB7XG4gICAgICAgICAgICBsb2dnZXIubG9nKFwiU2tpcHBpbmcgZmxvd1wiLCBmbG93LCBcImR1ZSB0byB1bnN1cHBvcnRlZCBsb2dpbiB0eXBlXCIsIGZsb3cudHlwZSk7XG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfTtcblxuICAgIHB1YmxpYyByZW5kZXJMb2dpbkNvbXBvbmVudEZvckZsb3dzKCk6IFJlYWN0Tm9kZSB7XG4gICAgICAgIGlmICghdGhpcy5zdGF0ZS5mbG93cykgcmV0dXJuIG51bGw7XG5cbiAgICAgICAgLy8gdGhpcyBpcyB0aGUgaWRlYWwgb3JkZXIgd2Ugd2FudCB0byBzaG93IHRoZSBmbG93cyBpblxuICAgICAgICBjb25zdCBvcmRlciA9IFtcIm9pZGNOYXRpdmVGbG93XCIsIFwibS5sb2dpbi5wYXNzd29yZFwiLCBcIm0ubG9naW4uc3NvXCJdO1xuXG4gICAgICAgIGNvbnN0IGZsb3dzID0gZmlsdGVyQm9vbGVhbihvcmRlci5tYXAoKHR5cGUpID0+IHRoaXMuc3RhdGUuZmxvd3M/LmZpbmQoKGZsb3cpID0+IGZsb3cudHlwZSA9PT0gdHlwZSkpKTtcbiAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgIDxSZWFjdC5GcmFnbWVudD5cbiAgICAgICAgICAgICAgICB7Zmxvd3MubWFwKChmbG93KSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHN0ZXBSZW5kZXJlciA9IHRoaXMuc3RlcFJlbmRlcmVyTWFwW2Zsb3cudHlwZV07XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiA8UmVhY3QuRnJhZ21lbnQga2V5PXtmbG93LnR5cGV9PntzdGVwUmVuZGVyZXIoKX08L1JlYWN0LkZyYWdtZW50PjtcbiAgICAgICAgICAgICAgICB9KX1cbiAgICAgICAgICAgIDwvUmVhY3QuRnJhZ21lbnQ+XG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSByZW5kZXJQYXNzd29yZFN0ZXAgPSAoKTogSlNYLkVsZW1lbnQgPT4ge1xuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgPFBhc3N3b3JkTG9naW5cbiAgICAgICAgICAgICAgICBvblN1Ym1pdD17dGhpcy5vblBhc3N3b3JkTG9naW59XG4gICAgICAgICAgICAgICAgdXNlcm5hbWU9e3RoaXMuc3RhdGUudXNlcm5hbWV9XG4gICAgICAgICAgICAgICAgcGhvbmVDb3VudHJ5PXt0aGlzLnN0YXRlLnBob25lQ291bnRyeX1cbiAgICAgICAgICAgICAgICBwaG9uZU51bWJlcj17dGhpcy5zdGF0ZS5waG9uZU51bWJlcn1cbiAgICAgICAgICAgICAgICBvblVzZXJuYW1lQ2hhbmdlZD17dGhpcy5vblVzZXJuYW1lQ2hhbmdlZH1cbiAgICAgICAgICAgICAgICBvblVzZXJuYW1lQmx1cj17dGhpcy5vblVzZXJuYW1lQmx1cn1cbiAgICAgICAgICAgICAgICBvblBob25lQ291bnRyeUNoYW5nZWQ9e3RoaXMub25QaG9uZUNvdW50cnlDaGFuZ2VkfVxuICAgICAgICAgICAgICAgIG9uUGhvbmVOdW1iZXJDaGFuZ2VkPXt0aGlzLm9uUGhvbmVOdW1iZXJDaGFuZ2VkfVxuICAgICAgICAgICAgICAgIG9uRm9yZ290UGFzc3dvcmRDbGljaz17dGhpcy5wcm9wcy5vbkZvcmdvdFBhc3N3b3JkQ2xpY2t9XG4gICAgICAgICAgICAgICAgbG9naW5JbmNvcnJlY3Q9e3RoaXMuc3RhdGUubG9naW5JbmNvcnJlY3R9XG4gICAgICAgICAgICAgICAgc2VydmVyQ29uZmlnPXt0aGlzLnByb3BzLnNlcnZlckNvbmZpZ31cbiAgICAgICAgICAgICAgICBkaXNhYmxlU3VibWl0PXt0aGlzLmlzQnVzeSgpfVxuICAgICAgICAgICAgICAgIGJ1c3k9e3RoaXMucHJvcHMuaXNTeW5jaW5nIHx8IHRoaXMuc3RhdGUuYnVzeUxvZ2dpbmdJbn1cbiAgICAgICAgICAgIC8+XG4gICAgICAgICk7XG4gICAgfTtcblxuICAgIHByaXZhdGUgcmVuZGVyT2lkY05hdGl2ZVN0ZXAgPSAoKTogUmVhY3QuUmVhY3ROb2RlID0+IHtcbiAgICAgICAgY29uc3QgZmxvdyA9IHRoaXMuc3RhdGUuZmxvd3MhLmZpbmQoKGZsb3cpID0+IGZsb3cudHlwZSA9PT0gXCJvaWRjTmF0aXZlRmxvd1wiKSEgYXMgT2lkY05hdGl2ZUZsb3c7XG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICA8QWNjZXNzaWJsZUJ1dHRvblxuICAgICAgICAgICAgICAgIGNsYXNzTmFtZT1cIm14X0xvZ2luX2Z1bGxXaWR0aEJ1dHRvblwiXG4gICAgICAgICAgICAgICAga2luZD1cInByaW1hcnlcIlxuICAgICAgICAgICAgICAgIG9uQ2xpY2s9e2FzeW5jICgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgYXdhaXQgc3RhcnRPaWRjTG9naW4oXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnByb3BzLnNlcnZlckNvbmZpZy5kZWxlZ2F0ZWRBdXRoZW50aWNhdGlvbiEsXG4gICAgICAgICAgICAgICAgICAgICAgICBmbG93LmNsaWVudElkLFxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5wcm9wcy5zZXJ2ZXJDb25maWcuaHNVcmwsXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnByb3BzLnNlcnZlckNvbmZpZy5pc1VybCxcbiAgICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICB9fVxuICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIHtfdChcImFjdGlvbnxjb250aW51ZVwiKX1cbiAgICAgICAgICAgIDwvQWNjZXNzaWJsZUJ1dHRvbj5cbiAgICAgICAgKTtcbiAgICB9O1xuXG4gICAgcHJpdmF0ZSByZW5kZXJTc29TdGVwID0gKGxvZ2luVHlwZTogXCJjYXNcIiB8IFwic3NvXCIpOiBKU1guRWxlbWVudCA9PiB7XG4gICAgICAgIGNvbnN0IGZsb3cgPSB0aGlzLnN0YXRlLmZsb3dzPy5maW5kKChmbG93KSA9PiBmbG93LnR5cGUgPT09IFwibS5sb2dpbi5cIiArIGxvZ2luVHlwZSkgYXMgU1NPRmxvdztcblxuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgPFNTT0J1dHRvbnNcbiAgICAgICAgICAgICAgICBtYXRyaXhDbGllbnQ9e3RoaXMubG9naW5Mb2dpYy5jcmVhdGVUZW1wb3JhcnlDbGllbnQoKX1cbiAgICAgICAgICAgICAgICBmbG93PXtmbG93fVxuICAgICAgICAgICAgICAgIGxvZ2luVHlwZT17bG9naW5UeXBlfVxuICAgICAgICAgICAgICAgIGZyYWdtZW50QWZ0ZXJMb2dpbj17dGhpcy5wcm9wcy5mcmFnbWVudEFmdGVyTG9naW59XG4gICAgICAgICAgICAgICAgcHJpbWFyeT17IXRoaXMuc3RhdGUuZmxvd3M/LmZpbmQoKGZsb3cpID0+IGZsb3cudHlwZSA9PT0gXCJtLmxvZ2luLnBhc3N3b3JkXCIpfVxuICAgICAgICAgICAgICAgIGFjdGlvbj17U1NPQWN0aW9uLkxPR0lOfVxuICAgICAgICAgICAgICAgIGRpc2FibGVkPXt0aGlzLmlzQnVzeSgpfVxuICAgICAgICAgICAgLz5cbiAgICAgICAgKTtcbiAgICB9O1xuXG4gICAgcHVibGljIHJlbmRlcigpOiBSZWFjdC5SZWFjdE5vZGUge1xuICAgICAgICBjb25zdCBsb2FkZXIgPVxuICAgICAgICAgICAgdGhpcy5pc0J1c3koKSAmJiAhdGhpcy5zdGF0ZS5idXN5TG9nZ2luZ0luID8gKFxuICAgICAgICAgICAgICAgIDxkaXYgY2xhc3NOYW1lPVwibXhfTG9naW5fbG9hZGVyXCI+XG4gICAgICAgICAgICAgICAgICAgIDxTcGlubmVyIC8+XG4gICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICApIDogbnVsbDtcblxuICAgICAgICBjb25zdCBlcnJvclRleHQgPSB0aGlzLnN0YXRlLmVycm9yVGV4dDtcblxuICAgICAgICBsZXQgZXJyb3JUZXh0U2VjdGlvbjtcbiAgICAgICAgaWYgKGVycm9yVGV4dCkge1xuICAgICAgICAgICAgZXJyb3JUZXh0U2VjdGlvbiA9IDxkaXYgY2xhc3NOYW1lPVwibXhfTG9naW5fZXJyb3JcIj57ZXJyb3JUZXh0fTwvZGl2PjtcbiAgICAgICAgfVxuXG4gICAgICAgIGxldCBzZXJ2ZXJEZWFkU2VjdGlvbjtcbiAgICAgICAgaWYgKCF0aGlzLnN0YXRlLnNlcnZlcklzQWxpdmUpIHtcbiAgICAgICAgICAgIGNvbnN0IGNsYXNzZXMgPSBjbGF