UNPKG

matrix-react-sdk

Version:
417 lines (411 loc) 72.7 kB
"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