studiokit-scaffolding-js 7.0.12-next.1.3 → 7.0.12-next.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/ActionList.js +164 -37
- package/lib/components/AlertDialog.js +128 -12
- package/lib/components/AlertWithIcon.js +88 -29
- package/lib/components/ConnectedModal.js +35 -12
- package/lib/components/Dropdowns/GroupsDropdown.js +63 -45
- package/lib/components/Dropdowns/ManagedNavDropdown.js +92 -67
- package/lib/components/Dropdowns/UserDropdown.js +105 -24
- package/lib/components/Dropdowns/index.js +4 -10
- package/lib/components/EntityOwnerList.js +47 -21
- package/lib/components/Error.js +101 -12
- package/lib/components/ErrorBoundary.js +127 -38
- package/lib/components/ErrorMessage.js +39 -12
- package/lib/components/Forms/DateField.js +56 -45
- package/lib/components/Forms/TimeField.js +76 -45
- package/lib/components/Forms/index.js +3 -5
- package/lib/components/Groups/CreateEditCopySaveButtons.js +109 -14
- package/lib/components/Groups/ExternalGroups/Attach.js +206 -151
- package/lib/components/Groups/ExternalGroups/Table.js +176 -48
- package/lib/components/Groups/GroupCreateOrEditCommonProps.js +2 -2
- package/lib/components/Groups/RosterSyncInfo.js +142 -23
- package/lib/components/HOC/AccessibleAppComponent.js +88 -72
- package/lib/components/HOC/ActivityRequiredComponent.js +68 -33
- package/lib/components/HOC/AsyncComponent.js +49 -41
- package/lib/components/HOC/AuthenticatedComponent.js +55 -44
- package/lib/components/HOC/CollectionComponent.js +154 -104
- package/lib/components/HOC/CollectionFirstItemComponent.js +45 -40
- package/lib/components/HOC/CollectionItemComponent.js +152 -100
- package/lib/components/HOC/ConnectedModalComponent.js +87 -69
- package/lib/components/HOC/DataDependentComponent.js +26 -27
- package/lib/components/HOC/EntityComponent.js +57 -53
- package/lib/components/HOC/FullscreenModalComponent.js +139 -108
- package/lib/components/HOC/GroupActivityRequiredComponent.js +27 -20
- package/lib/components/HOC/GuidComponent.js +20 -20
- package/lib/components/HOC/ModelContextDependencyVerifyComponent.js +32 -29
- package/lib/components/HOC/ModelErrorRedirectComponent.js +37 -39
- package/lib/components/HOC/SearchPersistorComponent.js +237 -173
- package/lib/components/HOC/UnauthenticatedComponent.js +32 -30
- package/lib/components/HOC/UserComponent.js +6 -8
- package/lib/components/Icons/IconAlphaList.js +28 -8
- package/lib/components/Icons/IconExternalUser.js +28 -8
- package/lib/components/Icons/IconImpersonation.js +28 -8
- package/lib/components/Icons/IconStopImpersonating.js +28 -8
- package/lib/components/Icons/IconTable.js +29 -9
- package/lib/components/Icons/IconTableDeleteCol.js +28 -8
- package/lib/components/Icons/IconTableDeleteRow.js +28 -8
- package/lib/components/Icons/IconTableInsertCol.js +28 -8
- package/lib/components/Icons/IconTableInsertRow.js +28 -8
- package/lib/components/Impersonation/Button.js +71 -16
- package/lib/components/Impersonation/Link.js +72 -16
- package/lib/components/Impersonation/UserDetail.js +60 -11
- package/lib/components/Loading.js +23 -8
- package/lib/components/LockDownBrowser/Check.js +188 -51
- package/lib/components/LockDownBrowser/ExitButton.js +22 -13
- package/lib/components/LockDownBrowser/Launch.js +64 -64
- package/lib/components/Lti/Confirm.js +147 -14
- package/lib/components/Lti/CreateNonLtiGroupAlertDialog.js +165 -36
- package/lib/components/Lti/Launch.js +99 -25
- package/lib/components/Lti/LaunchGroup.js +81 -16
- package/lib/components/ManageTable.js +304 -90
- package/lib/components/ManageTableNoDataComponent.js +38 -7
- package/lib/components/NewVersionAlert.js +76 -49
- package/lib/components/NotFound.js +81 -11
- package/lib/components/Notifications.js +179 -129
- package/lib/components/PaginationNextButton.js +28 -9
- package/lib/components/PaginationPreviousButton.js +28 -9
- package/lib/components/Quill/CustomToolbar.js +427 -222
- package/lib/components/Quill/Formats/Image.js +67 -67
- package/lib/components/Quill/Formats/List.js +38 -47
- package/lib/components/Quill/Formats/Video.js +23 -26
- package/lib/components/Quill/ImageDropModule.js +136 -114
- package/lib/components/Quill/ImageWarning.js +41 -12
- package/lib/components/Quill/ImageWithAltTextModal.js +420 -89
- package/lib/components/Quill/Specs/CustomImageSpec.js +32 -31
- package/lib/components/Quill/Specs/CustomVideoSpec.js +22 -23
- package/lib/components/Quill/TableModule/Blots/BaseTableBlot.js +89 -97
- package/lib/components/Quill/TableModule/Blots/TableBlot.js +47 -50
- package/lib/components/Quill/TableModule/Blots/TableBodyBlot.js +48 -51
- package/lib/components/Quill/TableModule/Blots/TableCellBlot.js +219 -224
- package/lib/components/Quill/TableModule/Blots/TableContainer.js +75 -86
- package/lib/components/Quill/TableModule/Blots/TableRowBlot.js +70 -73
- package/lib/components/Quill/TableModule/constants.js +40 -42
- package/lib/components/Quill/TableModule/index.js +357 -305
- package/lib/components/Quill/TableModule/utils.js +39 -48
- package/lib/components/Quill/accessibilityFix.js +219 -223
- package/lib/components/Quill/index.js +30 -33
- package/lib/components/RefreshIndicator/Bordered.js +44 -10
- package/lib/components/RefreshIndicator/Inline.js +43 -12
- package/lib/components/RefreshIndicator/index.js +257 -62
- package/lib/components/SearchControls.js +211 -14
- package/lib/components/SentryRoute.js +5 -7
- package/lib/components/Tables/RoleFilter.js +66 -38
- package/lib/components/Tables/TextFilter.js +58 -18
- package/lib/components/UserRoles/Add.js +193 -99
- package/lib/components/UserRoles/Context.js +3 -6
- package/lib/components/UserRoles/RoleCell.js +176 -75
- package/lib/components/UserRoles/Select.js +151 -20
- package/lib/components/UserRoles/Table.js +215 -82
- package/lib/components/UserRoles/index.js +526 -386
- package/lib/config/eslint/index.js +26 -29
- package/lib/config/eslint/lib/order.js +21 -28
- package/lib/config/eslint/lib/prettier.js +15 -19
- package/lib/config/eslint/lib/typescript.js +87 -113
- package/lib/config/eslint/react.js +18 -15
- package/lib/constants/baseActivity.js +26 -28
- package/lib/constants/baseRole.js +10 -12
- package/lib/constants/configuration.js +43 -55
- package/lib/constants/externalProviderType.js +6 -8
- package/lib/constants/fetchErrorData.js +10 -12
- package/lib/constants/index.js +13 -15
- package/lib/constants/lockDownBrowser.js +23 -25
- package/lib/constants/mockData.js +370 -300
- package/lib/constants/modelStatus.js +11 -13
- package/lib/constants/notificationType.js +8 -10
- package/lib/constants/operatingSystem.js +8 -10
- package/lib/constants/shard.js +7 -9
- package/lib/constants/table.js +18 -22
- package/lib/constants/tier.js +8 -10
- package/lib/constants/userRole.js +11 -8
- package/lib/endpointMappings.js +191 -182
- package/lib/hooks/useCollection.js +79 -65
- package/lib/hooks/useCollectionConfiguration.js +220 -80
- package/lib/hooks/useCollectionItem.js +151 -57
- package/lib/hooks/useGuid.js +16 -9
- package/lib/hooks/usePrevious.js +14 -13
- package/lib/index.js +11 -26
- package/lib/redux/actionCreator.js +44 -35
- package/lib/redux/actions/AuthAction.js +45 -32
- package/lib/redux/actions/ModalAction.js +6 -8
- package/lib/redux/actions/ModelAction.js +95 -43
- package/lib/redux/actions/NotificationAction.js +6 -8
- package/lib/redux/actions/SearchAction.js +5 -7
- package/lib/redux/actions/index.js +6 -8
- package/lib/redux/configureReducers.js +48 -46
- package/lib/redux/configureStore.js +77 -91
- package/lib/redux/helpers.js +2 -5
- package/lib/redux/reducers/authReducer.js +44 -43
- package/lib/redux/reducers/index.js +7 -14
- package/lib/redux/reducers/modalsReducer.js +43 -31
- package/lib/redux/reducers/modelsReducer.js +131 -137
- package/lib/redux/reducers/notificationsReducer.js +20 -20
- package/lib/redux/reducers/searchReducer.js +13 -13
- package/lib/redux/sagas/appInsightsSaga.js +19 -21
- package/lib/redux/sagas/authSaga.js +248 -234
- package/lib/redux/sagas/caliperSaga.js +142 -131
- package/lib/redux/sagas/clockOffsetSaga.js +29 -32
- package/lib/redux/sagas/configurationSaga.js +8 -10
- package/lib/redux/sagas/downtimeApiErrorSaga.js +16 -19
- package/lib/redux/sagas/errorSaga.js +23 -24
- package/lib/redux/sagas/googleAnalyticsSaga.js +24 -27
- package/lib/redux/sagas/identityProviderSaga.js +19 -21
- package/lib/redux/sagas/initialDataLoadSaga.js +34 -31
- package/lib/redux/sagas/lockDownBrowserErrorSaga.js +25 -22
- package/lib/redux/sagas/modelFetchSaga.js +302 -286
- package/lib/redux/sagas/noStoreSaga.js +60 -61
- package/lib/redux/sagas/postLoginDataSaga.js +37 -32
- package/lib/redux/sagas/postLoginRedirectSaga.js +22 -27
- package/lib/redux/sagas/rootSaga.js +77 -60
- package/lib/redux/sagas/sentrySaga.js +25 -28
- package/lib/redux/sagas/userIdSaga.js +13 -15
- package/lib/services/codeProviderService.js +21 -21
- package/lib/services/dateService.js +6 -8
- package/lib/services/documentService.js +10 -11
- package/lib/services/fetchService.js +103 -95
- package/lib/services/persistenceService.js +27 -30
- package/lib/services/ticketProviderService.js +25 -25
- package/lib/services/tokenPersistenceService.js +8 -10
- package/lib/services/windowService.js +14 -16
- package/lib/startup.js +110 -101
- package/lib/types/AppConfiguration.js +2 -2
- package/lib/types/Artifact.js +7 -9
- package/lib/types/BaseReduxState.js +2 -2
- package/lib/types/Client.js +2 -2
- package/lib/types/Collection.js +2 -2
- package/lib/types/Configuration.js +2 -2
- package/lib/types/DeepLinkingResponseRequest.js +2 -2
- package/lib/types/DeletableModel.js +2 -2
- package/lib/types/Event.js +2 -2
- package/lib/types/ExternalGroup.js +2 -2
- package/lib/types/ExternalProvider.js +2 -2
- package/lib/types/ExternalTerm.js +2 -2
- package/lib/types/Group.js +2 -2
- package/lib/types/IdentityProvider.js +2 -2
- package/lib/types/LtiLaunch.js +2 -2
- package/lib/types/NameOnlyEntity.js +2 -2
- package/lib/types/Notification.js +2 -2
- package/lib/types/OptionalRecord.js +2 -2
- package/lib/types/OwnerSchedule.js +2 -2
- package/lib/types/PropertyOfType.js +2 -2
- package/lib/types/Quill.js +2 -2
- package/lib/types/RoleDescription.js +2 -2
- package/lib/types/Search.js +2 -2
- package/lib/types/SimpleLocation.js +2 -2
- package/lib/types/UniTime.js +2 -2
- package/lib/types/User.js +2 -2
- package/lib/types/UserRole.js +2 -2
- package/lib/types/auth/AuthState.js +2 -2
- package/lib/types/auth/CasV1LoginRequestBody.js +2 -2
- package/lib/types/auth/ClientCredentials.js +2 -2
- package/lib/types/auth/CodeProviderService.js +2 -2
- package/lib/types/auth/LocalLoginRequestBody.js +2 -2
- package/lib/types/auth/TicketProviderService.js +2 -2
- package/lib/types/auth/TokenPersistenceService.js +2 -2
- package/lib/types/auth/index.js +8 -10
- package/lib/types/externals.d.js +2 -0
- package/lib/types/index.js +29 -31
- package/lib/types/net/EndpointConfig.js +2 -2
- package/lib/types/net/EndpointMapping.js +2 -2
- package/lib/types/net/EndpointMappings.js +2 -2
- package/lib/types/net/ErrorHandler.js +2 -2
- package/lib/types/net/FetchConfig.js +2 -2
- package/lib/types/net/FetchErrorData.js +6 -8
- package/lib/types/net/FetchResult.js +2 -2
- package/lib/types/net/HTTPMethod.js +2 -2
- package/lib/types/net/HTTPStatusCode.js +12 -14
- package/lib/types/net/Metadata.js +2 -2
- package/lib/types/net/Model.js +2 -2
- package/lib/types/net/ModelCollection.js +2 -2
- package/lib/types/net/ModelsState.js +2 -2
- package/lib/types/net/OAuthToken.js +2 -2
- package/lib/types/net/OAuthTokenOrNull.js +2 -2
- package/lib/types/net/TokenAccessFunction.js +2 -2
- package/lib/types/net/index.js +17 -19
- package/lib/utils/baseActivity.js +83 -85
- package/lib/utils/baseRole.js +32 -36
- package/lib/utils/collection.js +403 -297
- package/lib/utils/cookies.js +19 -23
- package/lib/utils/date.js +188 -205
- package/lib/utils/dom.js +130 -131
- package/lib/utils/domainIdentifier.js +4 -8
- package/lib/utils/entityUserRole.js +2 -5
- package/lib/utils/error.js +14 -19
- package/lib/utils/events.js +32 -31
- package/lib/utils/externalGroup.js +20 -25
- package/lib/utils/externalProviders.js +4 -7
- package/lib/utils/externalTerms.js +6 -6
- package/lib/utils/fetch.js +168 -176
- package/lib/utils/group.js +14 -11
- package/lib/utils/groupDates.js +38 -46
- package/lib/utils/groupRoles.js +23 -32
- package/lib/utils/lockDownBrowser.js +12 -15
- package/lib/utils/logger.js +23 -28
- package/lib/utils/lti.js +4 -7
- package/lib/utils/model.js +28 -43
- package/lib/utils/number.js +9 -13
- package/lib/utils/promise.js +23 -26
- package/lib/utils/quill.js +55 -60
- package/lib/utils/route.js +52 -60
- package/lib/utils/search.js +72 -87
- package/lib/utils/shard.js +33 -42
- package/lib/utils/sort.js +47 -50
- package/lib/utils/string.js +10 -12
- package/lib/utils/table.js +29 -33
- package/lib/utils/timezone.js +7 -12
- package/lib/utils/url.js +130 -144
- package/lib/utils/user.js +54 -64
- package/lib/utils/userAgent.js +7 -14
- package/lib/utils/userRole.js +36 -39
- package/package.json +17 -3
|
@@ -1,274 +1,288 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
exports.casV1LoginFlow = casV1LoginFlow;
|
|
10
|
-
exports.localLoginFlow = localLoginFlow;
|
|
11
|
-
exports.casTicketLoginFlow = casTicketLoginFlow;
|
|
12
|
-
exports.handleAuthFailure = handleAuthFailure;
|
|
13
|
-
exports.default = authSaga;
|
|
14
|
-
const effects_1 = require("redux-saga/effects");
|
|
15
|
-
const codeProviderService_1 = require("../../services/codeProviderService");
|
|
16
|
-
const ticketProviderService_1 = require("../../services/ticketProviderService");
|
|
17
|
-
const tokenPersistenceService_1 = require("../../services/tokenPersistenceService");
|
|
18
|
-
const types_1 = require("../../types");
|
|
19
|
-
const logger_1 = require("../../utils/logger");
|
|
20
|
-
const actions_1 = require("../actions");
|
|
1
|
+
import { all, call, put, race, take, takeEvery } from 'redux-saga/effects';
|
|
2
|
+
import { codeProviderService as defaultCodeProviderService } from '../../services/codeProviderService';
|
|
3
|
+
import { ticketProviderService as defaultTicketProviderService } from '../../services/ticketProviderService';
|
|
4
|
+
import { tokenPersistenceService as defaultTokenPersistenceService } from '../../services/tokenPersistenceService';
|
|
5
|
+
import { HTTP_STATUS_CODE } from '../../types';
|
|
6
|
+
import { getLogger } from '../../utils/logger';
|
|
7
|
+
import { AUTH_ACTION_TYPE, AUTH_CAS_V1_LOGIN_REQUEST_ACTION_TYPE, AUTH_LOCAL_LOGIN_REQUEST_ACTION_TYPE, AUTH_TOKEN_ACTION_TYPE, AUTH_TOKEN_SUCCESS_ACTION_TYPE, isTransientModelFetchErrorAction, isTransientModelFetchResultAction, MODEL_FETCH_ERROR_ACTION_TYPE, MODEL_FETCH_REQUEST_ACTION_TYPE, MODEL_REMOVE_KEY_ACTION_TYPE } from '../actions';
|
|
8
|
+
|
|
21
9
|
//#region Helpers
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const takeMatchesModelFetchReceived =
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
const takeMatchesModelFetchFailed = (modelName) => (incomingAction) => (0, exports.matchesModelFetchFailed)(incomingAction, modelName);
|
|
29
|
-
exports.takeMatchesModelFetchFailed = takeMatchesModelFetchFailed;
|
|
10
|
+
|
|
11
|
+
export const matchesModelFetchReceived = (action, modelName) => isTransientModelFetchResultAction(action) && action.modelPath === modelName;
|
|
12
|
+
export const takeMatchesModelFetchReceived = modelName => incomingAction => matchesModelFetchReceived(incomingAction, modelName);
|
|
13
|
+
export const matchesModelFetchFailed = (action, modelName) => isTransientModelFetchErrorAction(action) && action.modelPath === modelName;
|
|
14
|
+
export const takeMatchesModelFetchFailed = modelName => incomingAction => matchesModelFetchFailed(incomingAction, modelName);
|
|
15
|
+
|
|
30
16
|
//#endregion Helpers
|
|
17
|
+
|
|
31
18
|
//#region Local Variables
|
|
19
|
+
|
|
32
20
|
let clientCredentials;
|
|
33
21
|
let oauthToken = null;
|
|
34
22
|
let tokenPersistenceService;
|
|
35
23
|
let refreshLock;
|
|
36
24
|
let logger;
|
|
25
|
+
|
|
37
26
|
//#endregion Local Variables
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
27
|
+
|
|
28
|
+
export function* getTokenFromCode(code) {
|
|
29
|
+
const getTokenModelName = 'getToken';
|
|
30
|
+
// Manually creating form-url-encoded body here because NOTHING else uses this content-type
|
|
31
|
+
// but the OAuth spec requires it
|
|
32
|
+
const formBody = ['grant_type=authorization_code', `client_id=${clientCredentials.client_id}`, `client_secret=${clientCredentials.client_secret}`, `code=${encodeURIComponent(code)}`];
|
|
33
|
+
const formBodyString = formBody.join('&');
|
|
34
|
+
yield put({
|
|
35
|
+
type: MODEL_FETCH_REQUEST_ACTION_TYPE.FETCH_REQUEST,
|
|
36
|
+
modelName: getTokenModelName,
|
|
37
|
+
body: formBodyString,
|
|
38
|
+
noStore: true
|
|
39
|
+
});
|
|
40
|
+
const {
|
|
41
|
+
fetchResultAction,
|
|
42
|
+
fetchErrorAction
|
|
43
|
+
} = yield race({
|
|
44
|
+
fetchResultAction: take(takeMatchesModelFetchReceived(getTokenModelName)),
|
|
45
|
+
fetchErrorAction: take(takeMatchesModelFetchFailed(getTokenModelName))
|
|
46
|
+
});
|
|
47
|
+
if (fetchErrorAction || !fetchResultAction?.data) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return fetchResultAction.data;
|
|
51
|
+
}
|
|
52
|
+
export function* getTokenFromRefreshToken(oauthTokenParam) {
|
|
53
|
+
const getTokenModelName = 'getToken';
|
|
54
|
+
// Manually creating form-url-encoded body here because NOTHING else uses this content-type
|
|
55
|
+
// but the OAuth spec requires it
|
|
56
|
+
const formBody = ['grant_type=refresh_token', `client_id=${clientCredentials.client_id}`, `client_secret=${clientCredentials.client_secret}`, `refresh_token=${encodeURIComponent(oauthTokenParam.refresh_token)}`];
|
|
57
|
+
const formBodyString = formBody.join('&');
|
|
58
|
+
yield put({
|
|
59
|
+
type: MODEL_FETCH_REQUEST_ACTION_TYPE.FETCH_REQUEST,
|
|
60
|
+
modelName: getTokenModelName,
|
|
61
|
+
body: formBodyString,
|
|
62
|
+
noStore: true
|
|
63
|
+
});
|
|
64
|
+
const {
|
|
65
|
+
fetchResultAction,
|
|
66
|
+
fetchErrorAction
|
|
67
|
+
} = yield race({
|
|
68
|
+
fetchResultAction: take(takeMatchesModelFetchReceived(getTokenModelName)),
|
|
69
|
+
fetchErrorAction: take(takeMatchesModelFetchFailed(getTokenModelName))
|
|
70
|
+
});
|
|
71
|
+
// any error response
|
|
72
|
+
if (fetchErrorAction) {
|
|
73
|
+
// ignore server errors
|
|
74
|
+
if (fetchErrorAction.errorData?.status && fetchErrorAction.errorData.status >= HTTP_STATUS_CODE.INTERNAL_SERVER_ERROR) {
|
|
75
|
+
return oauthTokenParam;
|
|
61
76
|
}
|
|
62
|
-
return
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
// for some reason the response had no body
|
|
80
|
+
if (!fetchResultAction?.data) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return fetchResultAction.data;
|
|
63
84
|
}
|
|
64
|
-
function*
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
'grant_type=refresh_token',
|
|
71
|
-
`client_id=${clientCredentials.client_id}`,
|
|
72
|
-
`client_secret=${clientCredentials.client_secret}`,
|
|
73
|
-
`refresh_token=${encodeURIComponent(oauthTokenParam.refresh_token)}`
|
|
74
|
-
];
|
|
75
|
-
const formBodyString = formBody.join('&');
|
|
76
|
-
yield (0, effects_1.put)({
|
|
77
|
-
type: actions_1.MODEL_FETCH_REQUEST_ACTION_TYPE.FETCH_REQUEST,
|
|
78
|
-
modelName: getTokenModelName,
|
|
79
|
-
body: formBodyString,
|
|
80
|
-
noStore: true
|
|
85
|
+
export function* performTokenRefresh() {
|
|
86
|
+
if (refreshLock || !oauthToken) {
|
|
87
|
+
// already refreshing. wait for the current refresh to succeed or fail.
|
|
88
|
+
yield race({
|
|
89
|
+
refreshSuccess: take(AUTH_TOKEN_SUCCESS_ACTION_TYPE.TOKEN_REFRESH_SUCCEEDED),
|
|
90
|
+
refreshFailed: take(AUTH_ACTION_TYPE.TOKEN_REFRESH_FAILED)
|
|
81
91
|
});
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
logger.debug('Refreshing OAuth token');
|
|
95
|
+
refreshLock = true;
|
|
96
|
+
// oauthToken will be set to:
|
|
97
|
+
// 1. new token (success)
|
|
98
|
+
// 2. same token (failed from timeout or server error)
|
|
99
|
+
// 3. null (fail)
|
|
100
|
+
const originalAccessToken = oauthToken.access_token;
|
|
101
|
+
oauthToken = yield call(getTokenFromRefreshToken, oauthToken);
|
|
102
|
+
if (!!oauthToken && oauthToken.access_token !== originalAccessToken) {
|
|
103
|
+
logger.debug('OAuth token refreshed');
|
|
104
|
+
yield call(tokenPersistenceService.persistToken, oauthToken);
|
|
105
|
+
yield put({
|
|
106
|
+
type: AUTH_TOKEN_SUCCESS_ACTION_TYPE.TOKEN_REFRESH_SUCCEEDED,
|
|
107
|
+
oauthToken
|
|
85
108
|
});
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (!(fetchResultAction === null || fetchResultAction === void 0 ? void 0 : fetchResultAction.data)) {
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
return fetchResultAction.data;
|
|
100
|
-
}
|
|
101
|
-
function* performTokenRefresh() {
|
|
102
|
-
if (refreshLock || !oauthToken) {
|
|
103
|
-
// already refreshing. wait for the current refresh to succeed or fail.
|
|
104
|
-
yield (0, effects_1.race)({
|
|
105
|
-
refreshSuccess: (0, effects_1.take)(actions_1.AUTH_TOKEN_SUCCESS_ACTION_TYPE.TOKEN_REFRESH_SUCCEEDED),
|
|
106
|
-
refreshFailed: (0, effects_1.take)(actions_1.AUTH_ACTION_TYPE.TOKEN_REFRESH_FAILED)
|
|
107
|
-
});
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
logger.debug('Refreshing OAuth token');
|
|
111
|
-
refreshLock = true;
|
|
112
|
-
// oauthToken will be set to:
|
|
113
|
-
// 1. new token (success)
|
|
114
|
-
// 2. same token (failed from timeout or server error)
|
|
115
|
-
// 3. null (fail)
|
|
116
|
-
const originalAccessToken = oauthToken.access_token;
|
|
117
|
-
oauthToken = yield (0, effects_1.call)(getTokenFromRefreshToken, oauthToken);
|
|
118
|
-
if (!!oauthToken && oauthToken.access_token !== originalAccessToken) {
|
|
119
|
-
logger.debug('OAuth token refreshed');
|
|
120
|
-
yield (0, effects_1.call)(tokenPersistenceService.persistToken, oauthToken);
|
|
121
|
-
yield (0, effects_1.put)({ type: actions_1.AUTH_TOKEN_SUCCESS_ACTION_TYPE.TOKEN_REFRESH_SUCCEEDED, oauthToken });
|
|
122
|
-
}
|
|
123
|
-
else if (oauthToken === null) {
|
|
124
|
-
logger.debug('OAuth token failed to refresh');
|
|
125
|
-
// This should never happen outside of the token having been revoked on the server side
|
|
126
|
-
yield (0, effects_1.all)({
|
|
127
|
-
refreshFailed: (0, effects_1.put)({ type: actions_1.AUTH_ACTION_TYPE.TOKEN_REFRESH_FAILED }),
|
|
128
|
-
logOut: (0, effects_1.put)({ type: actions_1.AUTH_ACTION_TYPE.LOG_OUT_REQUESTED })
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
refreshLock = false;
|
|
132
|
-
}
|
|
133
|
-
function* loginFlow(modelFetchRequestAction) {
|
|
134
|
-
var _a;
|
|
135
|
-
yield (0, effects_1.put)(modelFetchRequestAction);
|
|
136
|
-
const { fetchResultAction, fetchErrorAction } = yield (0, effects_1.race)({
|
|
137
|
-
fetchResultAction: (0, effects_1.take)((0, exports.takeMatchesModelFetchReceived)(modelFetchRequestAction.modelName)),
|
|
138
|
-
fetchErrorAction: (0, effects_1.take)((0, exports.takeMatchesModelFetchFailed)(modelFetchRequestAction.modelName))
|
|
109
|
+
} else if (oauthToken === null) {
|
|
110
|
+
logger.debug('OAuth token failed to refresh');
|
|
111
|
+
// This should never happen outside of the token having been revoked on the server side
|
|
112
|
+
yield all({
|
|
113
|
+
refreshFailed: put({
|
|
114
|
+
type: AUTH_ACTION_TYPE.TOKEN_REFRESH_FAILED
|
|
115
|
+
}),
|
|
116
|
+
logOut: put({
|
|
117
|
+
type: AUTH_ACTION_TYPE.LOG_OUT_REQUESTED
|
|
118
|
+
})
|
|
139
119
|
});
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
const code = (_a = fetchResultAction === null || fetchResultAction === void 0 ? void 0 : fetchResultAction.data) === null || _a === void 0 ? void 0 : _a.code;
|
|
144
|
-
if (!code) {
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
147
|
-
return yield (0, effects_1.call)(getTokenFromCode, code);
|
|
120
|
+
}
|
|
121
|
+
refreshLock = false;
|
|
148
122
|
}
|
|
149
|
-
function*
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
123
|
+
export function* loginFlow(modelFetchRequestAction) {
|
|
124
|
+
yield put(modelFetchRequestAction);
|
|
125
|
+
const {
|
|
126
|
+
fetchResultAction,
|
|
127
|
+
fetchErrorAction
|
|
128
|
+
} = yield race({
|
|
129
|
+
fetchResultAction: take(takeMatchesModelFetchReceived(modelFetchRequestAction.modelName)),
|
|
130
|
+
fetchErrorAction: take(takeMatchesModelFetchFailed(modelFetchRequestAction.modelName))
|
|
131
|
+
});
|
|
132
|
+
if (fetchErrorAction) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
const code = fetchResultAction?.data?.code;
|
|
136
|
+
if (!code) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
return yield call(getTokenFromCode, code);
|
|
161
140
|
}
|
|
162
|
-
function*
|
|
163
|
-
|
|
141
|
+
export function* credentialsLoginFlow(action, modelName) {
|
|
142
|
+
const modelFetchRequestAction = {
|
|
143
|
+
// set required defaults
|
|
144
|
+
type: MODEL_FETCH_REQUEST_ACTION_TYPE.FETCH_REQUEST,
|
|
145
|
+
modelName,
|
|
146
|
+
noStore: true,
|
|
147
|
+
// force no retry
|
|
148
|
+
noRetry: true,
|
|
149
|
+
// pass thru body
|
|
150
|
+
body: action.body
|
|
151
|
+
};
|
|
152
|
+
return yield call(loginFlow, modelFetchRequestAction);
|
|
164
153
|
}
|
|
165
|
-
function*
|
|
166
|
-
|
|
154
|
+
export function* casV1LoginFlow(action) {
|
|
155
|
+
return yield call(credentialsLoginFlow, action, 'codeFromCasV1');
|
|
167
156
|
}
|
|
168
|
-
function*
|
|
169
|
-
|
|
170
|
-
type: actions_1.MODEL_FETCH_REQUEST_ACTION_TYPE.FETCH_REQUEST,
|
|
171
|
-
modelName: 'codeFromCasTicket',
|
|
172
|
-
noStore: true,
|
|
173
|
-
queryParams: {
|
|
174
|
-
ticket,
|
|
175
|
-
service
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
return yield (0, effects_1.call)(loginFlow, modelFetchRequestAction);
|
|
157
|
+
export function* localLoginFlow(action) {
|
|
158
|
+
return yield call(credentialsLoginFlow, action, 'codeFromLocalCredentials');
|
|
179
159
|
}
|
|
180
|
-
function*
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
160
|
+
export function* casTicketLoginFlow(ticket, service) {
|
|
161
|
+
const modelFetchRequestAction = {
|
|
162
|
+
type: MODEL_FETCH_REQUEST_ACTION_TYPE.FETCH_REQUEST,
|
|
163
|
+
modelName: 'codeFromCasTicket',
|
|
164
|
+
noStore: true,
|
|
165
|
+
queryParams: {
|
|
166
|
+
ticket,
|
|
167
|
+
service
|
|
186
168
|
}
|
|
169
|
+
};
|
|
170
|
+
return yield call(loginFlow, modelFetchRequestAction);
|
|
187
171
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
172
|
+
export function* handleAuthFailure(action) {
|
|
173
|
+
// This should be unlikely since we normally have a refresh token loop happening
|
|
174
|
+
// but if the app is backgrounded, the loop might not be caught up yet
|
|
175
|
+
if (oauthToken && action.errorData && action.errorData.status === HTTP_STATUS_CODE.UNAUTHORIZED) {
|
|
176
|
+
logger.debug('token expired - refreshing');
|
|
177
|
+
yield call(performTokenRefresh);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
export const getOAuthToken = function* (modelName) {
|
|
181
|
+
// Don't try to refresh the token if we're already in a request to refresh the token
|
|
182
|
+
if (modelName === 'getToken') {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
if (oauthToken && oauthToken['.expires']) {
|
|
186
|
+
const thirtySecondsFromNow = new Date();
|
|
187
|
+
thirtySecondsFromNow.setSeconds(thirtySecondsFromNow.getSeconds() + 30);
|
|
188
|
+
if (new Date(oauthToken['.expires']) < thirtySecondsFromNow) {
|
|
189
|
+
// start a token refresh and wait for the success action in case another refresh is currently happening
|
|
190
|
+
yield call(performTokenRefresh);
|
|
191
|
+
return oauthToken;
|
|
201
192
|
}
|
|
202
|
-
|
|
193
|
+
}
|
|
194
|
+
return oauthToken;
|
|
203
195
|
};
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
196
|
+
export default function authSaga(clientCredentialsParam) {
|
|
197
|
+
let tokenPersistenceServiceParam = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultTokenPersistenceService;
|
|
198
|
+
let ticketProviderService = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : defaultTicketProviderService;
|
|
199
|
+
let codeProviderService = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : defaultCodeProviderService;
|
|
200
|
+
return function* () {
|
|
201
|
+
logger = getLogger();
|
|
207
202
|
clientCredentials = clientCredentialsParam;
|
|
208
203
|
tokenPersistenceService = tokenPersistenceServiceParam;
|
|
204
|
+
|
|
209
205
|
// Try to get persisted token (normally in AsyncStorage or LocalStorage)
|
|
210
|
-
oauthToken = yield
|
|
206
|
+
oauthToken = yield call(tokenPersistenceService.getPersistedToken);
|
|
207
|
+
|
|
211
208
|
// If no token, try to get CAS ticket (normally in the URL), use it to get a token
|
|
212
209
|
if (!oauthToken) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
210
|
+
const casTicket = ticketProviderService.getTicket();
|
|
211
|
+
ticketProviderService.removeTicket();
|
|
212
|
+
const service = ticketProviderService.getAppServiceName();
|
|
213
|
+
if (casTicket && service) {
|
|
214
|
+
oauthToken = yield call(casTicketLoginFlow, casTicket, service);
|
|
215
|
+
}
|
|
219
216
|
}
|
|
217
|
+
|
|
220
218
|
// If OAuth Code exists (normally in the URL), use it to get a token
|
|
221
219
|
// e.g. LTI, Shibboleth, Facebook, Google
|
|
222
220
|
const code = codeProviderService.getCode();
|
|
223
221
|
if (code) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
222
|
+
// Log out the current user if a new token is about to be generated from the OAuth Code
|
|
223
|
+
if (oauthToken) {
|
|
224
|
+
yield all({
|
|
225
|
+
clearUserData: put({
|
|
226
|
+
type: MODEL_REMOVE_KEY_ACTION_TYPE,
|
|
227
|
+
modelPath: 'user'
|
|
228
|
+
}),
|
|
229
|
+
clearPersistentToken: call(tokenPersistenceService.persistToken, null)
|
|
230
|
+
});
|
|
231
|
+
oauthToken = null;
|
|
232
|
+
}
|
|
233
|
+
oauthToken = yield call(getTokenFromCode, code);
|
|
233
234
|
}
|
|
234
235
|
codeProviderService.removeCode();
|
|
235
|
-
yield
|
|
236
|
-
|
|
236
|
+
yield put({
|
|
237
|
+
type: AUTH_TOKEN_ACTION_TYPE.AUTH_INITIALIZED,
|
|
238
|
+
oauthToken
|
|
239
|
+
});
|
|
240
|
+
yield takeEvery(MODEL_FETCH_ERROR_ACTION_TYPE.TRY_FETCH_FAILED, handleAuthFailure);
|
|
237
241
|
do {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
if (
|
|
252
|
-
|
|
253
|
-
yield (0, effects_1.all)({
|
|
254
|
-
loginSuccess: (0, effects_1.put)({
|
|
255
|
-
type: actions_1.AUTH_TOKEN_SUCCESS_ACTION_TYPE.GET_TOKEN_SUCCEEDED,
|
|
256
|
-
oauthToken
|
|
257
|
-
}),
|
|
258
|
-
getUserInfo: (0, effects_1.put)({
|
|
259
|
-
type: actions_1.MODEL_FETCH_REQUEST_ACTION_TYPE.FETCH_REQUEST,
|
|
260
|
-
modelName: 'user.userInfo'
|
|
261
|
-
}),
|
|
262
|
-
logOut: (0, effects_1.take)(actions_1.AUTH_ACTION_TYPE.LOG_OUT_REQUESTED)
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
else {
|
|
266
|
-
yield (0, effects_1.put)({ type: actions_1.AUTH_ACTION_TYPE.LOGIN_FAILED });
|
|
242
|
+
if (!oauthToken) {
|
|
243
|
+
const {
|
|
244
|
+
casV1Action,
|
|
245
|
+
localLoginAction
|
|
246
|
+
} = yield race({
|
|
247
|
+
casV1Action: take(AUTH_CAS_V1_LOGIN_REQUEST_ACTION_TYPE.CAS_V1_LOGIN_REQUEST),
|
|
248
|
+
localLoginAction: take(AUTH_LOCAL_LOGIN_REQUEST_ACTION_TYPE.LOCAL_LOGIN_REQUEST)
|
|
249
|
+
});
|
|
250
|
+
yield put({
|
|
251
|
+
type: AUTH_ACTION_TYPE.LOGIN_REQUESTED
|
|
252
|
+
});
|
|
253
|
+
if (casV1Action) {
|
|
254
|
+
oauthToken = yield call(casV1LoginFlow, casV1Action);
|
|
255
|
+
} else if (localLoginAction) {
|
|
256
|
+
oauthToken = yield call(localLoginFlow, localLoginAction);
|
|
267
257
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
258
|
+
}
|
|
259
|
+
if (oauthToken) {
|
|
260
|
+
yield call(tokenPersistenceService.persistToken, oauthToken);
|
|
261
|
+
yield all({
|
|
262
|
+
loginSuccess: put({
|
|
263
|
+
type: AUTH_TOKEN_SUCCESS_ACTION_TYPE.GET_TOKEN_SUCCEEDED,
|
|
264
|
+
oauthToken
|
|
265
|
+
}),
|
|
266
|
+
getUserInfo: put({
|
|
267
|
+
type: MODEL_FETCH_REQUEST_ACTION_TYPE.FETCH_REQUEST,
|
|
268
|
+
modelName: 'user.userInfo'
|
|
269
|
+
}),
|
|
270
|
+
logOut: take(AUTH_ACTION_TYPE.LOG_OUT_REQUESTED)
|
|
271
271
|
});
|
|
272
|
-
|
|
272
|
+
} else {
|
|
273
|
+
yield put({
|
|
274
|
+
type: AUTH_ACTION_TYPE.LOGIN_FAILED
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
yield all({
|
|
278
|
+
clearUserData: put({
|
|
279
|
+
type: MODEL_REMOVE_KEY_ACTION_TYPE,
|
|
280
|
+
modelPath: 'user'
|
|
281
|
+
}),
|
|
282
|
+
clearPersistentToken: call(tokenPersistenceService.persistToken, null)
|
|
283
|
+
});
|
|
284
|
+
oauthToken = null;
|
|
273
285
|
} while (true);
|
|
286
|
+
}();
|
|
274
287
|
}
|
|
288
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJhbGwiLCJjYWxsIiwicHV0IiwicmFjZSIsInRha2UiLCJ0YWtlRXZlcnkiLCJjb2RlUHJvdmlkZXJTZXJ2aWNlIiwiZGVmYXVsdENvZGVQcm92aWRlclNlcnZpY2UiLCJ0aWNrZXRQcm92aWRlclNlcnZpY2UiLCJkZWZhdWx0VGlja2V0UHJvdmlkZXJTZXJ2aWNlIiwidG9rZW5QZXJzaXN0ZW5jZVNlcnZpY2UiLCJkZWZhdWx0VG9rZW5QZXJzaXN0ZW5jZVNlcnZpY2UiLCJIVFRQX1NUQVRVU19DT0RFIiwiZ2V0TG9nZ2VyIiwiQVVUSF9BQ1RJT05fVFlQRSIsIkFVVEhfQ0FTX1YxX0xPR0lOX1JFUVVFU1RfQUNUSU9OX1RZUEUiLCJBVVRIX0xPQ0FMX0xPR0lOX1JFUVVFU1RfQUNUSU9OX1RZUEUiLCJBVVRIX1RPS0VOX0FDVElPTl9UWVBFIiwiQVVUSF9UT0tFTl9TVUNDRVNTX0FDVElPTl9UWVBFIiwiaXNUcmFuc2llbnRNb2RlbEZldGNoRXJyb3JBY3Rpb24iLCJpc1RyYW5zaWVudE1vZGVsRmV0Y2hSZXN1bHRBY3Rpb24iLCJNT0RFTF9GRVRDSF9FUlJPUl9BQ1RJT05fVFlQRSIsIk1PREVMX0ZFVENIX1JFUVVFU1RfQUNUSU9OX1RZUEUiLCJNT0RFTF9SRU1PVkVfS0VZX0FDVElPTl9UWVBFIiwibWF0Y2hlc01vZGVsRmV0Y2hSZWNlaXZlZCIsImFjdGlvbiIsIm1vZGVsTmFtZSIsIm1vZGVsUGF0aCIsInRha2VNYXRjaGVzTW9kZWxGZXRjaFJlY2VpdmVkIiwiaW5jb21pbmdBY3Rpb24iLCJtYXRjaGVzTW9kZWxGZXRjaEZhaWxlZCIsInRha2VNYXRjaGVzTW9kZWxGZXRjaEZhaWxlZCIsImNsaWVudENyZWRlbnRpYWxzIiwib2F1dGhUb2tlbiIsInJlZnJlc2hMb2NrIiwibG9nZ2VyIiwiZ2V0VG9rZW5Gcm9tQ29kZSIsImNvZGUiLCJnZXRUb2tlbk1vZGVsTmFtZSIsImZvcm1Cb2R5IiwiY2xpZW50X2lkIiwiY2xpZW50X3NlY3JldCIsImVuY29kZVVSSUNvbXBvbmVudCIsImZvcm1Cb2R5U3RyaW5nIiwiam9pbiIsInR5cGUiLCJGRVRDSF9SRVFVRVNUIiwiYm9keSIsIm5vU3RvcmUiLCJmZXRjaFJlc3VsdEFjdGlvbiIsImZldGNoRXJyb3JBY3Rpb24iLCJkYXRhIiwiZ2V0VG9rZW5Gcm9tUmVmcmVzaFRva2VuIiwib2F1dGhUb2tlblBhcmFtIiwicmVmcmVzaF90b2tlbiIsImVycm9yRGF0YSIsInN0YXR1cyIsIklOVEVSTkFMX1NFUlZFUl9FUlJPUiIsInBlcmZvcm1Ub2tlblJlZnJlc2giLCJyZWZyZXNoU3VjY2VzcyIsIlRPS0VOX1JFRlJFU0hfU1VDQ0VFREVEIiwicmVmcmVzaEZhaWxlZCIsIlRPS0VOX1JFRlJFU0hfRkFJTEVEIiwiZGVidWciLCJvcmlnaW5hbEFjY2Vzc1Rva2VuIiwiYWNjZXNzX3Rva2VuIiwicGVyc2lzdFRva2VuIiwibG9nT3V0IiwiTE9HX09VVF9SRVFVRVNURUQiLCJsb2dpbkZsb3ciLCJtb2RlbEZldGNoUmVxdWVzdEFjdGlvbiIsImNyZWRlbnRpYWxzTG9naW5GbG93Iiwibm9SZXRyeSIsImNhc1YxTG9naW5GbG93IiwibG9jYWxMb2dpbkZsb3ciLCJjYXNUaWNrZXRMb2dpbkZsb3ciLCJ0aWNrZXQiLCJzZXJ2aWNlIiwicXVlcnlQYXJhbXMiLCJoYW5kbGVBdXRoRmFpbHVyZSIsIlVOQVVUSE9SSVpFRCIsImdldE9BdXRoVG9rZW4iLCJ0aGlydHlTZWNvbmRzRnJvbU5vdyIsIkRhdGUiLCJzZXRTZWNvbmRzIiwiZ2V0U2Vjb25kcyIsImF1dGhTYWdhIiwiY2xpZW50Q3JlZGVudGlhbHNQYXJhbSIsInRva2VuUGVyc2lzdGVuY2VTZXJ2aWNlUGFyYW0iLCJhcmd1bWVudHMiLCJsZW5ndGgiLCJ1bmRlZmluZWQiLCJnZXRQZXJzaXN0ZWRUb2tlbiIsImNhc1RpY2tldCIsImdldFRpY2tldCIsInJlbW92ZVRpY2tldCIsImdldEFwcFNlcnZpY2VOYW1lIiwiZ2V0Q29kZSIsImNsZWFyVXNlckRhdGEiLCJjbGVhclBlcnNpc3RlbnRUb2tlbiIsInJlbW92ZUNvZGUiLCJBVVRIX0lOSVRJQUxJWkVEIiwiVFJZX0ZFVENIX0ZBSUxFRCIsImNhc1YxQWN0aW9uIiwibG9jYWxMb2dpbkFjdGlvbiIsIkNBU19WMV9MT0dJTl9SRVFVRVNUIiwiTE9DQUxfTE9HSU5fUkVRVUVTVCIsIkxPR0lOX1JFUVVFU1RFRCIsImxvZ2luU3VjY2VzcyIsIkdFVF9UT0tFTl9TVUNDRUVERUQiLCJnZXRVc2VySW5mbyIsIkxPR0lOX0ZBSUxFRCJdLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9yZWR1eC9zYWdhcy9hdXRoU2FnYS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBTYWdhSXRlcmF0b3IgfSBmcm9tICdAcmVkdXgtc2FnYS9jb3JlJ1xuaW1wb3J0IHsgQW55QWN0aW9uIH0gZnJvbSAncmVkdXgnXG5pbXBvcnQgeyBhbGwsIGNhbGwsIHB1dCwgcmFjZSwgdGFrZSwgdGFrZUV2ZXJ5IH0gZnJvbSAncmVkdXgtc2FnYS9lZmZlY3RzJ1xuaW1wb3J0IHsgY29kZVByb3ZpZGVyU2VydmljZSBhcyBkZWZhdWx0Q29kZVByb3ZpZGVyU2VydmljZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL2NvZGVQcm92aWRlclNlcnZpY2UnXG5pbXBvcnQgeyB0aWNrZXRQcm92aWRlclNlcnZpY2UgYXMgZGVmYXVsdFRpY2tldFByb3ZpZGVyU2VydmljZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL3RpY2tldFByb3ZpZGVyU2VydmljZSdcbmltcG9ydCB7IHRva2VuUGVyc2lzdGVuY2VTZXJ2aWNlIGFzIGRlZmF1bHRUb2tlblBlcnNpc3RlbmNlU2VydmljZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL3Rva2VuUGVyc2lzdGVuY2VTZXJ2aWNlJ1xuaW1wb3J0IHtcblx0Q2xpZW50Q3JlZGVudGlhbHMsXG5cdENvZGVQcm92aWRlclNlcnZpY2UsXG5cdEhUVFBfU1RBVFVTX0NPREUsXG5cdE9BdXRoVG9rZW4sXG5cdE9BdXRoVG9rZW5Pck51bGwsXG5cdFRpY2tldFByb3ZpZGVyU2VydmljZSxcblx0VG9rZW5BY2Nlc3NGdW5jdGlvbixcblx0VG9rZW5QZXJzaXN0ZW5jZVNlcnZpY2Vcbn0gZnJvbSAnLi4vLi4vdHlwZXMnXG5pbXBvcnQgeyBnZXRMb2dnZXIsIExvZ2dlciB9IGZyb20gJy4uLy4uL3V0aWxzL2xvZ2dlcidcbmltcG9ydCB7XG5cdEFVVEhfQUNUSU9OX1RZUEUsXG5cdEFVVEhfQ0FTX1YxX0xPR0lOX1JFUVVFU1RfQUNUSU9OX1RZUEUsXG5cdEFVVEhfTE9DQUxfTE9HSU5fUkVRVUVTVF9BQ1RJT05fVFlQRSxcblx0QVVUSF9UT0tFTl9BQ1RJT05fVFlQRSxcblx0QVVUSF9UT0tFTl9TVUNDRVNTX0FDVElPTl9UWVBFLFxuXHRBdXRoQWN0aW9uLFxuXHRBdXRoQ2FzVjFMb2dpblJlcXVlc3RBY3Rpb24sXG5cdEF1dGhMb2NhbExvZ2luUmVxdWVzdEFjdGlvbixcblx0QXV0aFRva2VuQWN0aW9uLFxuXHRBdXRoVG9rZW5TdWNjZXNzQWN0aW9uLFxuXHRpc1RyYW5zaWVudE1vZGVsRmV0Y2hFcnJvckFjdGlvbixcblx0aXNUcmFuc2llbnRNb2RlbEZldGNoUmVzdWx0QWN0aW9uLFxuXHRNT0RFTF9GRVRDSF9FUlJPUl9BQ1RJT05fVFlQRSxcblx0TU9ERUxfRkVUQ0hfUkVRVUVTVF9BQ1RJT05fVFlQRSxcblx0TU9ERUxfUkVNT1ZFX0tFWV9BQ1RJT05fVFlQRSxcblx0TW9kZWxGZXRjaEVycm9yQWN0aW9uLFxuXHRNb2RlbEZldGNoUmVxdWVzdEFjdGlvbixcblx0TW9kZWxGZXRjaFJlc3VsdEFjdGlvbixcblx0TW9kZWxSZW1vdmVLZXlBY3Rpb25cbn0gZnJvbSAnLi4vYWN0aW9ucydcblxuLy8jcmVnaW9uIEhlbHBlcnNcblxuZXhwb3J0IGNvbnN0IG1hdGNoZXNNb2RlbEZldGNoUmVjZWl2ZWQgPSAoYWN0aW9uOiBBbnlBY3Rpb24sIG1vZGVsTmFtZTogc3RyaW5nKSA9PlxuXHRpc1RyYW5zaWVudE1vZGVsRmV0Y2hSZXN1bHRBY3Rpb24oYWN0aW9uKSAmJiBhY3Rpb24ubW9kZWxQYXRoID09PSBtb2RlbE5hbWVcblxuZXhwb3J0IGNvbnN0IHRha2VNYXRjaGVzTW9kZWxGZXRjaFJlY2VpdmVkID0gKG1vZGVsTmFtZTogc3RyaW5nKSA9PiAoaW5jb21pbmdBY3Rpb246IEFueUFjdGlvbikgPT5cblx0bWF0Y2hlc01vZGVsRmV0Y2hSZWNlaXZlZChpbmNvbWluZ0FjdGlvbiwgbW9kZWxOYW1lKVxuXG5leHBvcnQgY29uc3QgbWF0Y2hlc01vZGVsRmV0Y2hGYWlsZWQgPSAoYWN0aW9uOiBBbnlBY3Rpb24sIG1vZGVsTmFtZTogc3RyaW5nKSA9PlxuXHRpc1RyYW5zaWVudE1vZGVsRmV0Y2hFcnJvckFjdGlvbihhY3Rpb24pICYmIGFjdGlvbi5tb2RlbFBhdGggPT09IG1vZGVsTmFtZVxuXG5leHBvcnQgY29uc3QgdGFrZU1hdGNoZXNNb2RlbEZldGNoRmFpbGVkID0gKG1vZGVsTmFtZTogc3RyaW5nKSA9PiAoaW5jb21pbmdBY3Rpb246IEFueUFjdGlvbikgPT5cblx0bWF0Y2hlc01vZGVsRmV0Y2hGYWlsZWQoaW5jb21pbmdBY3Rpb24sIG1vZGVsTmFtZSlcblxuLy8jZW5kcmVnaW9uIEhlbHBlcnNcblxuLy8jcmVnaW9uIExvY2FsIFZhcmlhYmxlc1xuXG5sZXQgY2xpZW50Q3JlZGVudGlhbHM6IENsaWVudENyZWRlbnRpYWxzXG5sZXQgb2F1dGhUb2tlbjogT0F1dGhUb2tlbk9yTnVsbCA9IG51bGxcbmxldCB0b2tlblBlcnNpc3RlbmNlU2VydmljZTogVG9rZW5QZXJzaXN0ZW5jZVNlcnZpY2VcbmxldCByZWZyZXNoTG9jazogYm9vbGVhblxubGV0IGxvZ2dlcjogTG9nZ2VyXG5cbi8vI2VuZHJlZ2lvbiBMb2NhbCBWYXJpYWJsZXNcblxuZXhwb3J0IGZ1bmN0aW9uKiBnZXRUb2tlbkZyb21Db2RlKGNvZGU6IHN0cmluZyk6IFNhZ2FJdGVyYXRvciB7XG5cdGNvbnN0IGdldFRva2VuTW9kZWxOYW1lID0gJ2dldFRva2VuJ1xuXHQvLyBNYW51YWxseSBjcmVhdGluZyBmb3JtLXVybC1lbmNvZGVkIGJvZHkgaGVyZSBiZWNhdXNlIE5PVEhJTkcgZWxzZSB1c2VzIHRoaXMgY29udGVudC10eXBlXG5cdC8vIGJ1dCB0aGUgT0F1dGggc3BlYyByZXF1aXJlcyBpdFxuXHRjb25zdCBmb3JtQm9keSA9IFtcblx0XHQnZ3JhbnRfdHlwZT1hdXRob3JpemF0aW9uX2NvZGUnLFxuXHRcdGBjbGllbnRfaWQ9JHtjbGllbnRDcmVkZW50aWFscy5jbGllbnRfaWR9YCxcblx0XHRgY2xpZW50X3NlY3JldD0ke2NsaWVudENyZWRlbnRpYWxzLmNsaWVudF9zZWNyZXR9YCxcblx0XHRgY29kZT0ke2VuY29kZVVSSUNvbXBvbmVudChjb2RlKX1gXG5cdF1cblx0Y29uc3QgZm9ybUJvZHlTdHJpbmcgPSBmb3JtQm9keS5qb2luKCcmJylcblx0eWllbGQgcHV0PE1vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uPih7XG5cdFx0dHlwZTogTU9ERUxfRkVUQ0hfUkVRVUVTVF9BQ1RJT05fVFlQRS5GRVRDSF9SRVFVRVNULFxuXHRcdG1vZGVsTmFtZTogZ2V0VG9rZW5Nb2RlbE5hbWUsXG5cdFx0Ym9keTogZm9ybUJvZHlTdHJpbmcsXG5cdFx0bm9TdG9yZTogdHJ1ZVxuXHR9KVxuXHRjb25zdCB7XG5cdFx0ZmV0Y2hSZXN1bHRBY3Rpb24sXG5cdFx0ZmV0Y2hFcnJvckFjdGlvblxuXHR9OiB7IGZldGNoUmVzdWx0QWN0aW9uPzogTW9kZWxGZXRjaFJlc3VsdEFjdGlvbjsgZmV0Y2hFcnJvckFjdGlvbj86IE1vZGVsRmV0Y2hFcnJvckFjdGlvbiB9ID0geWllbGQgcmFjZSh7XG5cdFx0ZmV0Y2hSZXN1bHRBY3Rpb246IHRha2UodGFrZU1hdGNoZXNNb2RlbEZldGNoUmVjZWl2ZWQoZ2V0VG9rZW5Nb2RlbE5hbWUpKSxcblx0XHRmZXRjaEVycm9yQWN0aW9uOiB0YWtlKHRha2VNYXRjaGVzTW9kZWxGZXRjaEZhaWxlZChnZXRUb2tlbk1vZGVsTmFtZSkpXG5cdH0pXG5cdGlmIChmZXRjaEVycm9yQWN0aW9uIHx8ICFmZXRjaFJlc3VsdEFjdGlvbj8uZGF0YSkge1xuXHRcdHJldHVybiBudWxsXG5cdH1cblx0cmV0dXJuIGZldGNoUmVzdWx0QWN0aW9uLmRhdGFcbn1cblxuZXhwb3J0IGZ1bmN0aW9uKiBnZXRUb2tlbkZyb21SZWZyZXNoVG9rZW4ob2F1dGhUb2tlblBhcmFtOiBPQXV0aFRva2VuKTogU2FnYUl0ZXJhdG9yIHtcblx0Y29uc3QgZ2V0VG9rZW5Nb2RlbE5hbWUgPSAnZ2V0VG9rZW4nXG5cdC8vIE1hbnVhbGx5IGNyZWF0aW5nIGZvcm0tdXJsLWVuY29kZWQgYm9keSBoZXJlIGJlY2F1c2UgTk9USElORyBlbHNlIHVzZXMgdGhpcyBjb250ZW50LXR5cGVcblx0Ly8gYnV0IHRoZSBPQXV0aCBzcGVjIHJlcXVpcmVzIGl0XG5cdGNvbnN0IGZvcm1Cb2R5ID0gW1xuXHRcdCdncmFudF90eXBlPXJlZnJlc2hfdG9rZW4nLFxuXHRcdGBjbGllbnRfaWQ9JHtjbGllbnRDcmVkZW50aWFscy5jbGllbnRfaWR9YCxcblx0XHRgY2xpZW50X3NlY3JldD0ke2NsaWVudENyZWRlbnRpYWxzLmNsaWVudF9zZWNyZXR9YCxcblx0XHRgcmVmcmVzaF90b2tlbj0ke2VuY29kZVVSSUNvbXBvbmVudChvYXV0aFRva2VuUGFyYW0ucmVmcmVzaF90b2tlbil9YFxuXHRdXG5cdGNvbnN0IGZvcm1Cb2R5U3RyaW5nID0gZm9ybUJvZHkuam9pbignJicpXG5cdHlpZWxkIHB1dDxNb2RlbEZldGNoUmVxdWVzdEFjdGlvbj4oe1xuXHRcdHR5cGU6IE1PREVMX0ZFVENIX1JFUVVFU1RfQUNUSU9OX1RZUEUuRkVUQ0hfUkVRVUVTVCxcblx0XHRtb2RlbE5hbWU6IGdldFRva2VuTW9kZWxOYW1lLFxuXHRcdGJvZHk6IGZvcm1Cb2R5U3RyaW5nLFxuXHRcdG5vU3RvcmU6IHRydWVcblx0fSlcblx0Y29uc3Qge1xuXHRcdGZldGNoUmVzdWx0QWN0aW9uLFxuXHRcdGZldGNoRXJyb3JBY3Rpb25cblx0fTogeyBmZXRjaFJlc3VsdEFjdGlvbj86IE1vZGVsRmV0Y2hSZXN1bHRBY3Rpb247IGZldGNoRXJyb3JBY3Rpb24/OiBNb2RlbEZldGNoRXJyb3JBY3Rpb24gfSA9IHlpZWxkIHJhY2Uoe1xuXHRcdGZldGNoUmVzdWx0QWN0aW9uOiB0YWtlKHRha2VNYXRjaGVzTW9kZWxGZXRjaFJlY2VpdmVkKGdldFRva2VuTW9kZWxOYW1lKSksXG5cdFx0ZmV0Y2hFcnJvckFjdGlvbjogdGFrZSh0YWtlTWF0Y2hlc01vZGVsRmV0Y2hGYWlsZWQoZ2V0VG9rZW5Nb2RlbE5hbWUpKVxuXHR9KVxuXHQvLyBhbnkgZXJyb3IgcmVzcG9uc2Vcblx0aWYgKGZldGNoRXJyb3JBY3Rpb24pIHtcblx0XHQvLyBpZ25vcmUgc2VydmVyIGVycm9yc1xuXHRcdGlmIChcblx0XHRcdGZldGNoRXJyb3JBY3Rpb24uZXJyb3JEYXRhPy5zdGF0dXMgJiZcblx0XHRcdGZldGNoRXJyb3JBY3Rpb24uZXJyb3JEYXRhLnN0YXR1cyA+PSBIVFRQX1NUQVRVU19DT0RFLklOVEVSTkFMX1NFUlZFUl9FUlJPUlxuXHRcdCkge1xuXHRcdFx0cmV0dXJuIG9hdXRoVG9rZW5QYXJhbVxuXHRcdH1cblx0XHRyZXR1cm4gbnVsbFxuXHR9XG5cdC8vIGZvciBzb21lIHJlYXNvbiB0aGUgcmVzcG9uc2UgaGFkIG5vIGJvZHlcblx0aWYgKCFmZXRjaFJlc3VsdEFjdGlvbj8uZGF0YSkge1xuXHRcdHJldHVybiBudWxsXG5cdH1cblx0cmV0dXJuIGZldGNoUmVzdWx0QWN0aW9uLmRhdGFcbn1cblxuZXhwb3J0IGZ1bmN0aW9uKiBwZXJmb3JtVG9rZW5SZWZyZXNoKCk6IFNhZ2FJdGVyYXRvciB7XG5cdGlmIChyZWZyZXNoTG9jayB8fCAhb2F1dGhUb2tlbikge1xuXHRcdC8vIGFscmVhZHkgcmVmcmVzaGluZy4gd2FpdCBmb3IgdGhlIGN1cnJlbnQgcmVmcmVzaCB0byBzdWNjZWVkIG9yIGZhaWwuXG5cdFx0eWllbGQgcmFjZSh7XG5cdFx0XHRyZWZyZXNoU3VjY2VzczogdGFrZShBVVRIX1RPS0VOX1NVQ0NFU1NfQUNUSU9OX1RZUEUuVE9LRU5fUkVGUkVTSF9TVUNDRUVERUQpLFxuXHRcdFx0cmVmcmVzaEZhaWxlZDogdGFrZShBVVRIX0FDVElPTl9UWVBFLlRPS0VOX1JFRlJFU0hfRkFJTEVEKVxuXHRcdH0pXG5cdFx0cmV0dXJuXG5cdH1cblx0bG9nZ2VyLmRlYnVnKCdSZWZyZXNoaW5nIE9BdXRoIHRva2VuJylcblx0cmVmcmVzaExvY2sgPSB0cnVlXG5cdC8vIG9hdXRoVG9rZW4gd2lsbCBiZSBzZXQgdG86XG5cdC8vIDEuIG5ldyB0b2tlbiAoc3VjY2Vzcylcblx0Ly8gMi4gc2FtZSB0b2tlbiAoZmFpbGVkIGZyb20gdGltZW91dCBvciBzZXJ2ZXIgZXJyb3IpXG5cdC8vIDMuIG51bGwgKGZhaWwpXG5cdGNvbnN0IG9yaWdpbmFsQWNjZXNzVG9rZW4gPSBvYXV0aFRva2VuLmFjY2Vzc190b2tlblxuXHRvYXV0aFRva2VuID0geWllbGQgY2FsbChnZXRUb2tlbkZyb21SZWZyZXNoVG9rZW4sIG9hdXRoVG9rZW4pXG5cdGlmICghIW9hdXRoVG9rZW4gJiYgb2F1dGhUb2tlbi5hY2Nlc3NfdG9rZW4gIT09IG9yaWdpbmFsQWNjZXNzVG9rZW4pIHtcblx0XHRsb2dnZXIuZGVidWcoJ09BdXRoIHRva2VuIHJlZnJlc2hlZCcpXG5cdFx0eWllbGQgY2FsbCh0b2tlblBlcnNpc3RlbmNlU2VydmljZS5wZXJzaXN0VG9rZW4sIG9hdXRoVG9rZW4pXG5cdFx0eWllbGQgcHV0PEF1dGhUb2tlblN1Y2Nlc3NBY3Rpb24+KHsgdHlwZTogQVVUSF9UT0tFTl9TVUNDRVNTX0FDVElPTl9UWVBFLlRPS0VOX1JFRlJFU0hfU1VDQ0VFREVELCBvYXV0aFRva2VuIH0pXG5cdH0gZWxzZSBpZiAob2F1dGhUb2tlbiA9PT0gbnVsbCkge1xuXHRcdGxvZ2dlci5kZWJ1ZygnT0F1dGggdG9rZW4gZmFpbGVkIHRvIHJlZnJlc2gnKVxuXHRcdC8vIFRoaXMgc2hvdWxkIG5ldmVyIGhhcHBlbiBvdXRzaWRlIG9mIHRoZSB0b2tlbiBoYXZpbmcgYmVlbiByZXZva2VkIG9uIHRoZSBzZXJ2ZXIgc2lkZVxuXHRcdHlpZWxkIGFsbCh7XG5cdFx0XHRyZWZyZXNoRmFpbGVkOiBwdXQ8QXV0aEFjdGlvbj4oeyB0eXBlOiBBVVRIX0FDVElPTl9UWVBFLlRPS0VOX1JFRlJFU0hfRkFJTEVEIH0pLFxuXHRcdFx0bG9nT3V0OiBwdXQ8QXV0aEFjdGlvbj4oeyB0eXBlOiBBVVRIX0FDVElPTl9UWVBFLkxPR19PVVRfUkVRVUVTVEVEIH0pXG5cdFx0fSlcblx0fVxuXHRyZWZyZXNoTG9jayA9IGZhbHNlXG59XG5cbmV4cG9ydCBmdW5jdGlvbiogbG9naW5GbG93KG1vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uOiBNb2RlbEZldGNoUmVxdWVzdEFjdGlvbik6IFNhZ2FJdGVyYXRvciB7XG5cdHlpZWxkIHB1dChtb2RlbEZldGNoUmVxdWVzdEFjdGlvbilcblx0Y29uc3Qge1xuXHRcdGZldGNoUmVzdWx0QWN0aW9uLFxuXHRcdGZldGNoRXJyb3JBY3Rpb25cblx0fTogeyBmZXRjaFJlc3VsdEFjdGlvbj86IE1vZGVsRmV0Y2hSZXN1bHRBY3Rpb247IGZldGNoRXJyb3JBY3Rpb24/OiBNb2RlbEZldGNoRXJyb3JBY3Rpb24gfSA9IHlpZWxkIHJhY2Uoe1xuXHRcdGZldGNoUmVzdWx0QWN0aW9uOiB0YWtlKHRha2VNYXRjaGVzTW9kZWxGZXRjaFJlY2VpdmVkKG1vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uLm1vZGVsTmFtZSkpLFxuXHRcdGZldGNoRXJyb3JBY3Rpb246IHRha2UodGFrZU1hdGNoZXNNb2RlbEZldGNoRmFpbGVkKG1vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uLm1vZGVsTmFtZSkpXG5cdH0pXG5cdGlmIChmZXRjaEVycm9yQWN0aW9uKSB7XG5cdFx0cmV0dXJuIG51bGxcblx0fVxuXHRjb25zdCBjb2RlOiBzdHJpbmcgfCB1bmRlZmluZWQgPSBmZXRjaFJlc3VsdEFjdGlvbj8uZGF0YT8uY29kZVxuXHRpZiAoIWNvZGUpIHtcblx0XHRyZXR1cm4gbnVsbFxuXHR9XG5cdHJldHVybiB5aWVsZCBjYWxsKGdldFRva2VuRnJvbUNvZGUsIGNvZGUpXG59XG5cbmV4cG9ydCBmdW5jdGlvbiogY3JlZGVudGlhbHNMb2dpbkZsb3coXG5cdGFjdGlvbjogQXV0aENhc1YxTG9naW5SZXF1ZXN0QWN0aW9uIHwgQXV0aExvY2FsTG9naW5SZXF1ZXN0QWN0aW9uLFxuXHRtb2RlbE5hbWU6IHN0cmluZ1xuKTogU2FnYUl0ZXJhdG9yIHtcblx0Y29uc3QgbW9kZWxGZXRjaFJlcXVlc3RBY3Rpb246IE1vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uID0ge1xuXHRcdC8vIHNldCByZXF1aXJlZCBkZWZhdWx0c1xuXHRcdHR5cGU6IE1PREVMX0ZFVENIX1JFUVVFU1RfQUNUSU9OX1RZUEUuRkVUQ0hfUkVRVUVTVCxcblx0XHRtb2RlbE5hbWUsXG5cdFx0bm9TdG9yZTogdHJ1ZSxcblx0XHQvLyBmb3JjZSBubyByZXRyeVxuXHRcdG5vUmV0cnk6IHRydWUsXG5cdFx0Ly8gcGFzcyB0aHJ1IGJvZHlcblx0XHRib2R5OiBhY3Rpb24uYm9keVxuXHR9XG5cdHJldHVybiB5aWVsZCBjYWxsKGxvZ2luRmxvdywgbW9kZWxGZXRjaFJlcXVlc3RBY3Rpb24pXG59XG5cbmV4cG9ydCBmdW5jdGlvbiogY2FzVjFMb2dpbkZsb3coYWN0aW9uOiBBdXRoQ2FzVjFMb2dpblJlcXVlc3RBY3Rpb24pOiBTYWdhSXRlcmF0b3Ige1xuXHRyZXR1cm4geWllbGQgY2FsbChjcmVkZW50aWFsc0xvZ2luRmxvdywgYWN0aW9uLCAnY29kZUZyb21DYXNWMScpXG59XG5cbmV4cG9ydCBmdW5jdGlvbiogbG9jYWxMb2dpbkZsb3coYWN0aW9uOiBBdXRoTG9jYWxMb2dpblJlcXVlc3RBY3Rpb24pOiBTYWdhSXRlcmF0b3Ige1xuXHRyZXR1cm4geWllbGQgY2FsbChjcmVkZW50aWFsc0xvZ2luRmxvdywgYWN0aW9uLCAnY29kZUZyb21Mb2NhbENyZWRlbnRpYWxzJylcbn1cblxuZXhwb3J0IGZ1bmN0aW9uKiBjYXNUaWNrZXRMb2dpbkZsb3codGlja2V0OiBzdHJpbmcsIHNlcnZpY2U6IHN0cmluZyk6IFNhZ2FJdGVyYXRvciB7XG5cdGNvbnN0IG1vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uOiBNb2RlbEZldGNoUmVxdWVzdEFjdGlvbiA9IHtcblx0XHR0eXBlOiBNT0RFTF9GRVRDSF9SRVFVRVNUX0FDVElPTl9UWVBFLkZFVENIX1JFUVVFU1QsXG5cdFx0bW9kZWxOYW1lOiAnY29kZUZyb21DYXNUaWNrZXQnLFxuXHRcdG5vU3RvcmU6IHRydWUsXG5cdFx0cXVlcnlQYXJhbXM6IHtcblx0XHRcdHRpY2tldCxcblx0XHRcdHNlcnZpY2Vcblx0XHR9XG5cdH1cblx0cmV0dXJuIHlpZWxkIGNhbGwobG9naW5GbG93LCBtb2RlbEZldGNoUmVxdWVzdEFjdGlvbilcbn1cblxuZXhwb3J0IGZ1bmN0aW9uKiBoYW5kbGVBdXRoRmFpbHVyZShhY3Rpb246IE1vZGVsRmV0Y2hFcnJvckFjdGlvbik6IFNhZ2FJdGVyYXRvciB7XG5cdC8vIFRoaXMgc2hvdWxkIGJlIHVubGlrZWx5IHNpbmNlIHdlIG5vcm1hbGx5IGhhdmUgYSByZWZyZXNoIHRva2VuIGxvb3AgaGFwcGVuaW5nXG5cdC8vIGJ1dCBpZiB0aGUgYXBwIGlzIGJhY2tncm91bmRlZCwgdGhlIGxvb3AgbWlnaHQgbm90IGJlIGNhdWdodCB1cCB5ZXRcblx0aWYgKG9hdXRoVG9rZW4gJiYgYWN0aW9uLmVycm9yRGF0YSAmJiBhY3Rpb24uZXJyb3JEYXRhLnN0YXR1cyA9PT0gSFRUUF9TVEFUVVNfQ09ERS5VTkFVVEhPUklaRUQpIHtcblx0XHRsb2dnZXIuZGVidWcoJ3Rva2VuIGV4cGlyZWQgLSByZWZyZXNoaW5nJylcblx0XHR5aWVsZCBjYWxsKHBlcmZvcm1Ub2tlblJlZnJlc2gpXG5cdH1cbn1cblxuZXhwb3J0IGNvbnN0IGdldE9BdXRoVG9rZW46IFRva2VuQWNjZXNzRnVuY3Rpb24gPSBmdW5jdGlvbiogKG1vZGVsTmFtZTogc3RyaW5nKSB7XG5cdC8vIERvbid0IHRyeSB0byByZWZyZXNoIHRoZSB0b2tlbiBpZiB3ZSdyZSBhbHJlYWR5IGluIGEgcmVxdWVzdCB0byByZWZyZXNoIHRoZSB0b2tlblxuXHRpZiAobW9kZWxOYW1lID09PSAnZ2V0VG9rZW4nKSB7XG5cdFx0cmV0dXJuIG51bGxcblx0fVxuXHRpZiAob2F1dGhUb2tlbiAmJiBvYXV0aFRva2VuWycuZXhwaXJlcyddKSB7XG5cdFx0Y29uc3QgdGhpcnR5U2Vjb25kc0Zyb21Ob3cgPSBuZXcgRGF0ZSgpXG5cdFx0dGhpcnR5U2Vjb25kc0Zyb21Ob3cuc2V0U2Vjb25kcyh0aGlydHlTZWNvbmRzRnJvbU5vdy5nZXRTZWNvbmRzKCkgKyAzMClcblx0XHRpZiAobmV3IERhdGUob2F1dGhUb2tlblsnLmV4cGlyZXMnXSkgPCB0aGlydHlTZWNvbmRzRnJvbU5vdykge1xuXHRcdFx0Ly8gc3RhcnQgYSB0b2tlbiByZWZyZXNoIGFuZCB3YWl0IGZvciB0aGUgc3VjY2VzcyBhY3Rpb24gaW4gY2FzZSBhbm90aGVyIHJlZnJlc2ggaXMgY3VycmVudGx5IGhhcHBlbmluZ1xuXHRcdFx0eWllbGQgY2FsbChwZXJmb3JtVG9rZW5SZWZyZXNoKVxuXHRcdFx0cmV0dXJuIG9hdXRoVG9rZW5cblx0XHR9XG5cdH1cblx0cmV0dXJuIG9hdXRoVG9rZW5cbn1cblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24qIGF1dGhTYWdhKFxuXHRjbGllbnRDcmVkZW50aWFsc1BhcmFtOiBDbGllbnRDcmVkZW50aWFscyxcblx0dG9rZW5QZXJzaXN0ZW5jZVNlcnZpY2VQYXJhbTogVG9rZW5QZXJzaXN0ZW5jZVNlcnZpY2UgPSBkZWZhdWx0VG9rZW5QZXJzaXN0ZW5jZVNlcnZpY2UsXG5cdHRpY2tldFByb3ZpZGVyU2VydmljZTogVGlja2V0UHJvdmlkZXJTZXJ2aWNlID0gZGVmYXVsdFRpY2tldFByb3ZpZGVyU2VydmljZSxcblx0Y29kZVByb3ZpZGVyU2VydmljZTogQ29kZVByb3ZpZGVyU2VydmljZSA9IGRlZmF1bHRDb2RlUHJvdmlkZXJTZXJ2aWNlXG4pOiBTYWdhSXRlcmF0b3Ige1xuXHRsb2dnZXIgPSBnZXRMb2dnZXIoKVxuXG5cdGNsaWVudENyZWRlbnRpYWxzID0gY2xpZW50Q3JlZGVudGlhbHNQYXJhbVxuXHR0b2tlblBlcnNpc3RlbmNlU2VydmljZSA9IHRva2VuUGVyc2lzdGVuY2VTZXJ2aWNlUGFyYW1cblxuXHQvLyBUcnkgdG8gZ2V0IHBlcnNpc3RlZCB0b2tlbiAobm9ybWFsbHkgaW4gQXN5bmNTdG9yYWdlIG9yIExvY2FsU3RvcmFnZSlcblx0b2F1dGhUb2tlbiA9IHlpZWxkIGNhbGwodG9rZW5QZXJzaXN0ZW5jZVNlcnZpY2UuZ2V0UGVyc2lzdGVkVG9rZW4pXG5cblx0Ly8gSWYgbm8gdG9rZW4sIHRyeSB0byBnZXQgQ0FTIHRpY2tldCAobm9ybWFsbHkgaW4gdGhlIFVSTCksIHVzZSBpdCB0byBnZXQgYSB0b2tlblxuXHRpZiAoIW9hdXRoVG9rZW4pIHtcblx0XHRjb25zdCBjYXNUaWNrZXQgPSB0aWNrZXRQcm92aWRlclNlcnZpY2UuZ2V0VGlja2V0KClcblx0XHR0aWNrZXRQcm92aWRlclNlcnZpY2UucmVtb3ZlVGlja2V0KClcblx0XHRjb25zdCBzZXJ2aWNlID0gdGlja2V0UHJvdmlkZXJTZXJ2aWNlLmdldEFwcFNlcnZpY2VOYW1lKClcblx0XHRpZiAoY2FzVGlja2V0ICYmIHNlcnZpY2UpIHtcblx0XHRcdG9hdXRoVG9rZW4gPSB5aWVsZCBjYWxsKGNhc1RpY2tldExvZ2luRmxvdywgY2FzVGlja2V0LCBzZXJ2aWNlKVxuXHRcdH1cblx0fVxuXG5cdC8vIElmIE9BdXRoIENvZGUgZXhpc3RzIChub3JtYWxseSBpbiB0aGUgVVJMKSwgdXNlIGl0IHRvIGdldCBhIHRva2VuXG5cdC8vIGUuZy4gTFRJLCBTaGliYm9sZXRoLCBGYWNlYm9vaywgR29vZ2xlXG5cdGNvbnN0IGNvZGUgPSBjb2RlUHJvdmlkZXJTZXJ2aWNlLmdldENvZGUoKVxuXHRpZiAoY29kZSkge1xuXHRcdC8vIExvZyBvdXQgdGhlIGN1cnJlbnQgdXNlciBpZiBhIG5ldyB0b2tlbiBpcyBhYm91dCB0byBiZSBnZW5lcmF0ZWQgZnJvbSB0aGUgT0F1dGggQ29kZVxuXHRcdGlmIChvYXV0aFRva2VuKSB7XG5cdFx0XHR5aWVsZCBhbGwoe1xuXHRcdFx0XHRjbGVhclVzZXJEYXRhOiBwdXQ8TW9kZWxSZW1vdmVLZXlBY3Rpb24+KHsgdHlwZTogTU9ERUxfUkVNT1ZFX0tFWV9BQ1RJT05fVFlQRSwgbW9kZWxQYXRoOiAndXNlcicgfSksXG5cdFx0XHRcdGNsZWFyUGVyc2lzdGVudFRva2VuOiBjYWxsKHRva2VuUGVyc2lzdGVuY2VTZXJ2aWNlLnBlcnNpc3RUb2tlbiwgbnVsbClcblx0XHRcdH0pXG5cdFx0XHRvYXV0aFRva2VuID0gbnVsbFxuXHRcdH1cblx0XHRvYXV0aFRva2VuID0geWllbGQgY2FsbChnZXRUb2tlbkZyb21Db2RlLCBjb2RlKVxuXHR9XG5cdGNvZGVQcm92aWRlclNlcnZpY2UucmVtb3ZlQ29kZSgpXG5cblx0eWllbGQgcHV0PEF1dGhUb2tlbkFjdGlvbj4oeyB0eXBlOiBBVVRIX1RPS0VOX0FDVElPTl9UWVBFLkFVVEhfSU5JVElBTElaRUQsIG9hdXRoVG9rZW4gfSlcblxuXHR5aWVsZCB0YWtlRXZlcnkoTU9ERUxfRkVUQ0hfRVJST1JfQUNUSU9OX1RZUEUuVFJZX0ZFVENIX0ZBSUxFRCwgaGFuZGxlQXV0aEZhaWx1cmUpXG5cblx0ZG8ge1xuXHRcdGlmICghb2F1dGhUb2tlbikge1xuXHRcdFx0Y29uc3Qge1xuXHRcdFx0XHRjYXNWMUFjdGlvbixcblx0XHRcdFx0bG9jYWxMb2dpbkFjdGlvblxuXHRcdFx0fToge1xuXHRcdFx0XHRjYXNWMUFjdGlvbj86IEF1dGhDYXNWMUxvZ2luUmVxdWVzdEFjdGlvblxuXHRcdFx0XHRsb2NhbExvZ2luQWN0aW9uPzogQXV0aExvY2FsTG9naW5SZXF1ZXN0QWN0aW9uXG5cdFx0XHR9ID0geWllbGQgcmFjZSh7XG5cdFx0XHRcdGNhc1YxQWN0aW9uOiB0YWtlKEFVVEhfQ0FTX1YxX0xPR0lOX1JFUVVFU1RfQUNUSU9OX1RZUEUuQ0FTX1YxX0xPR0lOX1JFUVVFU1QpLFxuXHRcdFx0XHRsb2NhbExvZ2luQWN0aW9uOiB0YWtlKEFVVEhfTE9DQUxfTE9HSU5fUkVRVUVTVF9BQ1RJT05fVFlQRS5MT0NBTF9MT0dJTl9SRVFVRVNUKVxuXHRcdFx0fSlcblxuXHRcdFx0eWllbGQgcHV0PEF1dGhBY3Rpb24+KHsgdHlwZTogQVVUSF9BQ1RJT05fVFlQRS5MT0dJTl9SRVFVRVNURUQgfSlcblx0XHRcdGlmIChjYXNWMUFjdGlvbikge1xuXHRcdFx0XHRvYXV0aFRva2VuID0geWllbGQgY2FsbChjYXNWMUxvZ2luRmxvdywgY2FzVjFBY3Rpb24pXG5cdFx0XHR9IGVsc2UgaWYgKGxvY2FsTG9naW5BY3Rpb24pIHtcblx0XHRcdFx0b2F1dGhUb2tlbiA9IHlpZWxkIGNhbGwobG9jYWxMb2dpbkZsb3csIGxvY2FsTG9naW5BY3Rpb24pXG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0aWYgKG9hdXRoVG9rZW4pIHtcblx0XHRcdHlpZWxkIGNhbGwodG9rZW5QZXJzaXN0ZW5jZVNlcnZpY2UucGVyc2lzdFRva2VuLCBvYXV0aFRva2VuKVxuXHRcdFx0eWllbGQgYWxsKHtcblx0XHRcdFx0bG9naW5TdWNjZXNzOiBwdXQ8QXV0aFRva2VuU3VjY2Vzc0FjdGlvbj4oe1xuXHRcdFx0XHRcdHR5cGU6IEFVVEhfVE9LRU5fU1VDQ0VTU19BQ1RJT05fVFlQRS5HRVRfVE9LRU5fU1VDQ0VFREVELFxuXHRcdFx0XHRcdG9hdXRoVG9rZW5cblx0XHRcdFx0fSksXG5cdFx0XHRcdGdldFVzZXJJbmZvOiBwdXQ8TW9kZWxGZXRjaFJlcXVlc3RBY3Rpb24+KHtcblx0XHRcdFx0XHR0eXBlOiBNT0RFTF9GRVRDSF9SRVFVRVNUX0FDVElPTl9UWVBFLkZFVENIX1JFUVVFU1QsXG5cdFx0XHRcdFx0bW9kZWxOYW1lOiAndXNlci51c2VySW5mbydcblx0XHRcdFx0fSksXG5cdFx0XHRcdGxvZ091dDogdGFrZShBVVRIX0FDVElPTl9UWVBFLkxPR19PVVRfUkVRVUVTVEVEKVxuXHRcdFx0fSlcblx0XHR9IGVsc2Uge1xuXHRcdFx0eWllbGQgcHV0PEF1dGhBY3Rpb24+KHsgdHlwZTogQVVUSF9BQ1RJT05fVFlQRS5MT0dJTl9GQUlMRUQgfSlcblx0XHR9XG5cblx0XHR5aWVsZCBhbGwoe1xuXHRcdFx0Y2xlYXJVc2VyRGF0YTogcHV0PE1vZGVsUmVtb3ZlS2V5QWN0aW9uPih7IHR5cGU6IE1PREVMX1JFTU9WRV9LRVlfQUNUSU9OX1RZUEUsIG1vZGVsUGF0aDogJ3VzZXInIH0pLFxuXHRcdFx0Y2xlYXJQZXJzaXN0ZW50VG9rZW46IGNhbGwodG9rZW5QZXJzaXN0ZW5jZVNlcnZpY2UucGVyc2lzdFRva2VuLCBudWxsKVxuXHRcdH0pXG5cdFx0b2F1dGhUb2tlbiA9IG51bGxcblx0fSB3aGlsZSAodHJ1ZSlcbn1cbiJdLCJtYXBwaW5ncyI6IkFBRUEsU0FBU0EsR0FBRyxFQUFFQyxJQUFJLEVBQUVDLEdBQUcsRUFBRUMsSUFBSSxFQUFFQyxJQUFJLEVBQUVDLFNBQVMsUUFBUSxvQkFBb0I7QUFDMUUsU0FBU0MsbUJBQW1CLElBQUlDLDBCQUEwQixRQUFRLG9DQUFvQztBQUN0RyxTQUFTQyxxQkFBcUIsSUFBSUMsNEJBQTRCLFFBQVEsc0NBQXNDO0FBQzVHLFNBQVNDLHVCQUF1QixJQUFJQyw4QkFBOEIsUUFBUSx3Q0FBd0M7QUFDbEgsU0FHQ0MsZ0JBQWdCLFFBTVYsYUFBYTtBQUNwQixTQUFTQyxTQUFTLFFBQWdCLG9CQUFvQjtBQUN0RCxTQUNDQyxnQkFBZ0IsRUFDaEJDLHFDQUFxQyxFQUNyQ0Msb0NBQW9DLEVBQ3BDQyxzQkFBc0IsRUFDdEJDLDhCQUE4QixFQU05QkMsZ0NBQWdDLEVBQ2hDQyxpQ0FBaUMsRUFDakNDLDZCQUE2QixFQUM3QkMsK0JBQStCLEVBQy9CQyw0QkFBNEIsUUFLdEIsWUFBWTs7QUFFbkI7O0FBRUEsT0FBTyxNQUFNQyx5QkFBeUIsR0FBR0EsQ0FBQ0MsTUFBaUIsRUFBRUMsU0FBaUIsS0FDN0VOLGlDQUFpQyxDQUFDSyxNQUFNLENBQUMsSUFBSUEsTUFBTSxDQUFDRSxTQUFTLEtBQUtELFNBQVM7QUFFNUUsT0FBTyxNQUFNRSw2QkFBNkIsR0FBSUYsU0FBaUIsSUFBTUcsY0FBeUIsSUFDN0ZMLHlCQUF5QixDQUFDSyxjQUFjLEVBQUVILFNBQVMsQ0FBQztBQUVyRCxPQUFPLE1BQU1JLHVCQUF1QixHQUFHQSxDQUFDTCxNQUFpQixFQUFFQyxTQUFpQixLQUMzRVAsZ0NBQWdDLENBQUNNLE1BQU0sQ0FBQyxJQUFJQSxNQUFNLENBQUNFLFNBQVMsS0FBS0QsU0FBUztBQUUzRSxPQUFPLE1BQU1LLDJCQUEyQixHQUFJTCxTQUFpQixJQUFNRyxjQUF5QixJQUMzRkMsdUJBQXVCLENBQUNELGNBQWMsRUFBRUgsU0FBUyxDQUFDOztBQUVuRDs7QUFFQTs7QUFFQSxJQUFJTSxpQkFBb0M7QUFDeEMsSUFBSUMsVUFBNEIsR0FBRyxJQUFJO0FBQ3ZDLElBQUl2Qix1QkFBZ0Q7QUFDcEQsSUFBSXdCLFdBQW9CO0FBQ3hCLElBQUlDLE1BQWM7O0FBRWxCOztBQUVBLE9BQU8sVUFBVUMsZ0JBQWdCQSxDQUFDQyxJQUFZLEVBQWdCO0VBQzdELE1BQU1DLGlCQUFpQixHQUFHLFVBQVU7RUFDcEM7RUFDQTtFQUNBLE1BQU1DLFFBQVEsR0FBRyxDQUNoQiwrQkFBK0IsRUFDL0IsYUFBYVAsaUJBQWlCLENBQUNRLFNBQVMsRUFBRSxFQUMxQyxpQkFBaUJSLGlCQUFpQixDQUFDUyxhQUFhLEVBQUUsRUFDbEQsUUFBUUMsa0JBQWtCLENBQUNMLElBQUksQ0FBQyxFQUFFLENBQ2xDO0VBQ0QsTUFBTU0sY0FBYyxHQUFHSixRQUFRLENBQUNLLElBQUksQ0FBQyxHQUFHLENBQUM7RUFDekMsTUFBTTFDLEdBQUcsQ0FBMEI7SUFDbEMyQyxJQUFJLEVBQUV2QiwrQkFBK0IsQ0FBQ3dCLGFBQWE7SUFDbkRwQixTQUFTLEVBQUVZLGlCQUFpQjtJQUM1QlMsSUFBSSxFQUFFSixjQUFjO0lBQ3BCSyxPQUFPLEVBQUU7RUFDVixDQUFDLENBQUM7RUFDRixNQUFNO0lBQ0xDLGlCQUFpQjtJQUNqQkM7RUFDeUYsQ0FBQyxHQUFHLE1BQU0vQyxJQUFJLENBQUM7SUFDeEc4QyxpQkFBaUIsRUFBRTdDLElBQUksQ0FBQ3dCLDZCQUE2QixDQUFDVSxpQkFBaUIsQ0FBQyxDQUFDO0lBQ3pFWSxnQkFBZ0IsRUFBRTlDLElBQUksQ0FBQzJCLDJCQUEyQixDQUFDTyxpQkFBaUIsQ0FBQztFQUN0RSxDQUFDLENBQUM7RUFDRixJQUFJWSxnQkFBZ0IsSUFBSSxDQUFDRCxpQkFBaUIsRUFBRUUsSUFBSSxFQUFFO0lBQ2pELE9BQU8sSUFBSTtFQUNaO0VBQ0EsT0FBT0YsaUJBQWlCLENBQUNFLElBQUk7QUFDOUI7QUFFQSxPQUFPLFVBQVVDLHdCQUF3QkEsQ0FBQ0MsZUFBMkIsRUFBZ0I7RUFDcEYsTUFBTWYsaUJBQWlCLEdBQUcsVUFBVTtFQUNwQztFQUNBO0VBQ0EsTUFBTUMsUUFBUSxHQUFHLENBQ2hCLDBCQUEwQixFQUMxQixhQUFhUCxpQkFBaUIsQ0FBQ1EsU0FBUyxFQUFFLEVBQzFDLGlCQUFpQlIsaUJBQWlCLENBQUNTLGFBQWEsRUFBRSxFQUNsRCxpQkFBaUJDLGtCQUFrQixDQUFDVyxlQUFlLENBQUNDLGFBQWEsQ0FBQyxFQUFFLENBQ3BFO0VBQ0QsTUFBTVgsY0FBYyxHQUFHSixRQUFRLENBQUNLLElBQUksQ0FBQyxHQUFHLENBQUM7RUFDekMsTUFBTTFDLEdBQUcsQ0FBMEI7SUFDbEMyQyxJQUFJLEVBQUV2QiwrQkFBK0IsQ0FBQ3dCLGFBQWE7SUFDbkRwQixTQUFTLEVBQUVZLGlCQUFpQjtJQUM1QlMsSUFBSSxFQUFFSixjQUFjO0lBQ3BCSyxPQUFPLEVBQUU7RUFDVixDQUFDLENBQUM7RUFDRixNQUFNO0lBQ0xDLGlCQUFpQjtJQUNqQkM7RUFDeUYsQ0FBQyxHQUFHLE1BQU0vQyxJQUFJLENBQUM7SUFDeEc4QyxpQkFBaUIsRUFBRTdDLElBQUksQ0FBQ3dCLDZCQUE2QixDQUFDVSxpQkFBaUIsQ0FBQyxDQUFDO0lBQ3pFWSxnQkFBZ0IsRUFBRTlDLElBQUksQ0FBQzJCLDJCQUEyQixDQUFDTyxpQkFBaUIsQ0FBQztFQUN0RSxDQUFDLENBQUM7RUFDRjtFQUNBLElBQUlZLGdCQUFnQixFQUFFO0lBQ3JCO0lBQ0EsSUFDQ0EsZ0JBQWdCLENBQUNLLFNBQVMsRUFBRUMsTUFBTSxJQUNsQ04sZ0JBQWdCLENBQUNLLFNBQVMsQ0FBQ0MsTUFBTSxJQUFJNUMsZ0JBQWdCLENBQUM2QyxxQkFBcUIsRUFDMUU7TUFDRCxPQUFPSixlQUFlO0lBQ3ZCO0lBQ0EsT0FBTyxJQUFJO0VBQ1o7RUFDQTtFQUNBLElBQUksQ0FBQ0osaUJBQWlCLEVBQUVFLElBQUksRUFBRTtJQUM3QixPQUFPLElBQUk7RUFDWjtFQUNBLE9BQU9GLGlCQUFpQixDQUFDRSxJQUFJO0FBQzlCO0FBRUEsT0FBTyxVQUFVTyxtQkFBbUJBLENBQUEsRUFBaUI7RUFDcEQsSUFBSXhCLFdBQVcsSUFBSSxDQUFDRCxVQUFVLEVBQUU7SUFDL0I7SUFDQSxNQUFNOUIsSUFBSSxDQUFDO01BQ1Z3RCxjQUFjLEVBQUV2RCxJQUFJLENBQUNjLDhCQUE4QixDQUFDMEMsdUJBQXVCLENBQUM7TUFDNUVDLGFBQWEsRUFBRXpELElBQUksQ0FBQ1UsZ0JBQWdCLENBQUNnRCxvQkFBb0I7SUFDMUQsQ0FBQyxDQUFDO0lBQ0Y7RUFDRDtFQUNBM0IsTUFBTSxDQUFDNEIsS0FBSyxDQUFDLHdCQUF3QixDQUFDO0VBQ3RDN0IsV0FBVyxHQUFHLElBQUk7RUFDbEI7RUFDQTtFQUNBO0VBQ0E7RUFDQSxNQUFNOEIsbUJBQW1CLEdBQUcvQixVQUFVLENBQUNnQyxZQUFZO0VBQ25EaEMsVUFBVSxHQUFHLE1BQU1oQyxJQUFJLENBQUNtRCx3QkFBd0IsRUFBRW5CLFVBQVUsQ0FBQztFQUM3RCxJQUFJLENBQUMsQ0FBQ0EsVUFBVSxJQUFJQSxVQUFVLENBQUNnQyxZQUFZLEtBQUtELG1CQUFtQixFQUFFO0lBQ3BFN0IsTUFBTSxDQUFDNEIsS0FBSyxDQUFDLHVCQUF1QixDQUFDO0lBQ3JDLE1BQU05RCxJQUFJLENBQUNTLHVCQUF1QixDQUFDd0QsWUFBWSxFQUFFakMsVUFBVSxDQUFDO0lBQzVELE1BQU0vQixHQUFHLENBQXlCO01BQUUyQyxJQUFJLEVBQUUzQiw4QkFBOEIsQ0FBQzBDLHVCQUF1QjtNQUFFM0I7SUFBVyxDQUFDLENBQUM7RUFDaEgsQ0FBQyxNQUFNLElBQUlBLFVBQVUsS0FBSyxJQUFJLEVBQUU7SUFDL0JFLE1BQU0sQ0FBQzRCLEtBQUssQ0FBQywrQkFBK0IsQ0FBQztJQUM3QztJQUNBLE1BQU0vRCxHQUFHLENBQUM7TUFDVDZELGFBQWEsRUFBRTNELEdBQUcsQ0FBYTtRQUFFMkMsSUFBSSxFQUFFL0IsZ0JBQWdCLENBQUNnRDtNQUFxQixDQUFDLENBQUM7TUFDL0VLLE1BQU0sRUFBRWpFLEdBQUcsQ0FBYTtRQUFFMkMsSUFBSSxFQUFFL0IsZ0JBQWdCLENBQUNzRDtNQUFrQixDQUFDO0lBQ3JFLENBQUMsQ0FBQztFQUNIO0VBQ0FsQyxXQUFXLEdBQUcsS0FBSztBQUNwQjtBQUVBLE9BQU8sVUFBVW1DLFNBQVNBLENBQUNDLHVCQUFnRCxFQUFnQjtFQUMxRixNQUFNcEUsR0FBRyxDQUFDb0UsdUJBQXVCLENBQUM7RUFDbEMsTUFBTTtJQUNMckIsaUJBQWlCO0lBQ2pCQztFQUN5RixDQUFDLEdBQUcsTUFBTS9DLElBQUksQ0FBQztJQUN4RzhDLGlCQUFpQixFQUFFN0MsSUFBSSxDQUFDd0IsNkJBQTZCLENBQUMwQyx1QkFBdUIsQ0FBQzVDLFNBQVMsQ0FBQyxDQUFDO0lBQ3pGd0IsZ0JBQWdCLEVBQUU5QyxJQUFJLENBQUMyQiwyQkFBMkIsQ0FBQ3VDLHVCQUF1QixDQUFDNUMsU0FBUyxDQUFDO0VBQ3RGLENBQUMsQ0FBQztFQUNGLElBQUl3QixnQkFBZ0IsRUFBRTtJQUNyQixPQUFPLElBQUk7RUFDWjtFQUNBLE1BQU1iLElBQXdCLEdBQUdZLGlCQUFpQixFQUFFRSxJQUFJLEVBQUVkLElBQUk7RUFDOUQsSUFBSSxDQUFDQSxJQUFJLEVBQUU7SUFDVixPQUFPLElBQUk7RUFDWjtFQUNBLE9BQU8sTUFBTXBDLElBQUksQ0FBQ21DLGdCQUFnQixFQUFFQyxJQUFJLENBQUM7QUFDMUM7QUFFQSxPQUFPLFVBQVVrQyxvQkFBb0JBLENBQ3BDOUMsTUFBaUUsRUFDakVDLFNBQWlCLEVBQ0Y7RUFDZixNQUFNNEMsdUJBQWdELEdBQUc7SUFDeEQ7SUFDQXpCLElBQUksRUFBRXZCLCtCQUErQixDQUFDd0IsYUFBYTtJQUNuRHBCLFNBQVM7SUFDVHNCLE9BQU8sRUFBRSxJQUFJO0lBQ2I7SUFDQXdCLE9BQU8sRUFBRSxJQUFJO0lBQ2I7SUFDQXpCLElBQUksRUFBRXRCLE1BQU0sQ0FBQ3NCO0VBQ2QsQ0FBQztFQUNELE9BQU8sTUFBTTlDLElBQUksQ0FBQ29FLFNBQVMsRUFBRUMsdUJBQXVCLENBQUM7QUFDdEQ7QUFFQSxPQUFPLFVBQVVHLGNBQWNBLENBQUNoRCxNQUFtQyxFQUFnQjtFQUNsRixPQUFPLE1BQU14QixJQUFJLENBQUNzRSxvQkFBb0IsRUFBRTlDLE1BQU0sRUFBRSxlQUFlLENBQUM7QUFDakU7QUFFQSxPQUFPLFVBQVVpRCxjQUFjQSxDQUFDakQsTUFBbUMsRUFBZ0I7RUFDbEYsT0FBTyxNQUFNeEIsSUFBSSxDQUFDc0Usb0JBQW9CLEVBQUU5QyxNQUFNLEVBQUUsMEJBQTBCLENBQUM7QUFDNUU7QUFFQSxPQUFPLFVBQVVrRCxrQkFBa0JBLENBQUNDLE1BQWMsRUFBRUMsT0FBZSxFQUFnQjtFQUNsRixNQUFNUCx1QkFBZ0QsR0FBRztJQUN4RHpCLElBQUksRUFBRXZCLCtCQUErQixDQUFDd0IsYUFBYTtJQUNuRHBCLFNBQVMsRUFBRSxtQkFBbUI7SUFDOUJzQixPQUFPLEVBQUUsSUFBSTtJQUNiOEIsV0FBVyxFQUFFO01BQ1pGLE1BQU07TUFDTkM7SUFDRDtFQUNELENBQUM7RUFDRCxPQUFPLE1BQU01RSxJQUFJLENBQUNvRSxTQUFTLEVBQUVDLHVCQUF1QixDQUFDO0FBQ3REO0FBRUEsT0FBTyxVQUFVUyxpQkFBaUJBLENBQUN0RCxNQUE2QixFQUFnQjtFQUMvRTtFQUNBO0VBQ0EsSUFBSVEsVUFBVSxJQUFJUixNQUFNLENBQUM4QixTQUFTLElBQUk5QixNQUFNLENBQUM4QixTQUFTLENBQUNDLE1BQU0sS0FBSzVDLGdCQUFnQixDQUFDb0UsWUFBWSxFQUFFO0lBQ2hHN0MsTUFBTSxDQUFDNEIsS0FBSyxDQUFDLDRCQUE0QixDQUFDO0lBQzFDLE1BQU05RCxJQUFJLENBQUN5RCxtQkFBbUIsQ0FBQztFQUNoQztBQUNEO0FBRUEsT0FBTyxNQUFNdUIsYUFBa0MsR0FBRyxVQUFBQSxDQUFXdkQsU0FBaUIsRUFBRTtFQUMvRTtFQUNBLElBQUlBLFNBQVMsS0FBSyxVQUFVLEVBQUU7SUFDN0IsT0FBTyxJQUFJO0VBQ1o7RUFDQSxJQUFJTyxVQUFVLElBQUlBLFVBQVUsQ0FBQyxVQUFVLENBQUMsRUFBRTtJQUN6QyxNQUFNaUQsb0JBQW9CLEdBQUcsSUFBSUMsSUFBSSxDQUFDLENBQUM7SUFDdkNELG9CQUFvQixDQUFDRSxVQUFVLENBQUNGLG9CQUFvQixDQUFDRyxVQUFVLENBQUMsQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUN2RSxJQUFJLElBQUlGLElBQUksQ0FBQ2xELFVBQVUsQ0FBQyxVQUFVLENBQUMsQ0FBQyxHQUFHaUQsb0JBQW9CLEVBQUU7TUFDNUQ7TUFDQSxNQUFNakYsSUFBSSxDQUFDeUQsbUJBQW1CLENBQUM7TUFDL0IsT0FBT3pCLFVBQVU7SUFDbEI7RUFDRDtFQUNBLE9BQU9BLFVBQVU7QUFDbEIsQ0FBQztBQUVELGVBQWUsU0FBVXFELFFBQVFBLENBQ2hDQyxzQkFBeUM7RUFBQSxJQUN6Q0MsNEJBQXFELEdBQUFDLFNBQUEsQ0FBQUMsTUFBQSxRQUFBRCxTQUFBLFFBQUFFLFNBQUEsR0FBQUYsU0FBQSxNQUFHOUUsOEJBQThCO0VBQUEsSUFDdEZILHFCQUE0QyxHQUFBaUYsU0FBQSxDQUFBQyxNQUFBLFFBQUFELFNBQUEsUUFBQUUsU0FBQSxHQUFBRixTQUFBLE1BQUdoRiw0QkFBNEI7RUFBQSxJQUMzRUgsbUJBQXdDLEdBQUFtRixTQUFBLENBQUFDLE1BQUEsUUFBQUQsU0FBQSxRQUFBRSxTQUFBLEdBQUFGLFNBQUEsTUFBR2xGLDBCQUEwQjtFQUFBLG9CQUN0RDtJQUNmNEIsTUFBTSxHQUFHdEIsU0FBUyxDQUFDLENBQUM7SUFFcEJtQixpQkFBaUIsR0FBR3VELHNCQUFzQjtJQUMxQzdFLHVCQUF1QixHQUFHOEUsNEJBQTRCOztJQUV0RDtJQUNBdkQsVUFBVSxHQUFHLE1BQU1oQyxJQUFJLENBQUNTLHVCQUF1QixDQUFDa0YsaUJBQWlCLENBQUM7O0lBRWxFO0lBQ0EsSUFBSSxDQUFDM0QsVUFBVSxFQUFFO01BQ2hCLE1BQU00RCxTQUFTLEdBQUdyRixxQkFBcUIsQ0FBQ3NGLFNBQVMsQ0FBQyxDQUFDO01BQ25EdEYscUJBQXFCLENBQUN1RixZQUFZLENBQUMsQ0FBQztNQUNwQyxNQUFNbEIsT0FBTyxHQUFHckUscUJBQXFCLENBQUN3RixpQkFBaUIsQ0FBQyxDQUFDO01BQ3pELElBQUlILFNBQVMsSUFBSWhCLE9BQU8sRUFBRTtRQUN6QjVDLFVBQVUsR0FBRyxNQUFNaEMsSUFBSSxDQUFDMEUsa0JBQWtCLEVBQUVrQixTQUFTLEVBQUVoQixPQUFPLENBQUM7TUFDaEU7SUFDRDs7SUFFQTtJQUNBO0lBQ0EsTUFBTXhDLElBQUksR0FBRy9CLG1CQUFtQixDQUFDMkYsT0FBTyxDQUFDLENBQUM7SUFDMUMsSUFBSTVELElBQUksRUFBRTtNQUNUO01BQ0EsSUFBSUosVUFBVSxFQUFFO1FBQ2YsTUFBTWpDLEdBQUcsQ0FBQztVQUNUa0csYUFBYSxFQUFFaEcsR0FBRyxDQUF1QjtZQUFFMkMsSUFBSSxFQUFFdEIsNEJBQTRCO1lBQUVJLFNBQVMsRUFBRTtVQUFPLENBQUMsQ0FBQztVQUNuR3dFLG9CQUFvQixFQUFFbEcsSUFBSSxDQUFDUyx1QkFBdUIsQ0FBQ3dELFlBQVksRUFBRSxJQUFJO1FBQ3RFLENBQUMsQ0FBQztRQUNGakMsVUFBVSxHQUFHLElBQUk7TUFDbEI7TUFDQUEsVUFBVSxHQUFHLE1BQU1oQyxJQUFJLENBQUNtQyxnQkFBZ0IsRUFBRUMsSUFBSSxDQUFDO0lBQ2hEO0lBQ0EvQixtQkFBbUIsQ0FBQzhGLFVBQVUsQ0FBQyxDQUFDO0lBRWhDLE1BQU1sRyxHQUFHLENBQWtCO01BQUUyQyxJQUFJLEVBQUU1QixzQkFBc0IsQ0FBQ29GLGdCQUFnQjtNQUFFcEU7SUFBVyxDQUFDLENBQUM7SUFFekYsTUFBTTVCLFNBQVMsQ0FBQ2dCLDZCQUE2QixDQUFDaUYsZ0JBQWdCLEVBQUV2QixpQkFBaUIsQ0FBQztJQUVsRixHQUFHO01BQ0YsSUFBSSxDQUFDOUMsVUFBVSxFQUFFO1FBQ2hCLE1BQU07VUFDTHNFLFdBQVc7VUFDWEM7UUFJRCxDQUFDLEdBQUcsTUFBTXJHLElBQUksQ0FBQztVQUNkb0csV0FBVyxFQUFFbkcsSUFBSSxDQUFDVyxxQ0FBcUMsQ0FBQzBGLG9CQUFvQixDQUFDO1VBQzdFRCxnQkFBZ0IsRUFBRXBHLElBQUksQ0FBQ1ksb0NBQW9DLENBQUMwRixtQkFBbUI7UUFDaEYsQ0FBQyxDQUFDO1FBRUYsTUFBTXhHLEdBQUcsQ0FBYTtVQUFFMkMsSUFBSSxFQUFFL0IsZ0JBQWdCLENBQUM2RjtRQUFnQixDQUFDLENBQUM7UUFDakUsSUFBSUosV0FBVyxFQUFFO1VBQ2hCdEUsVUFBVSxHQUFHLE1BQU1oQyxJQUFJLENBQUN3RSxjQUFjLEVBQUU4QixXQUFXLENBQUM7UUFDckQsQ0FBQyxNQUFNLElBQUlDLGdCQUFnQixFQUFFO1VBQzVCdkUsVUFBVSxHQUFHLE1BQU1oQyxJQUFJLENBQUN5RSxjQUFjLEVBQUU4QixnQkFBZ0IsQ0FBQztRQUMxRDtNQUNEO01BRUEsSUFBSXZFLFVBQVUsRUFBRTtRQUNmLE1BQU1oQyxJQUFJLENBQUNTLHVCQUF1QixDQUFDd0QsWUFBWSxFQUFFakMsVUFBVSxDQUFDO1FBQzVELE1BQU1qQyxHQUFHLENBQUM7VUFDVDRHLFlBQVksRUFBRTFHLEdBQUcsQ0FBeUI7WUFDekMyQyxJQUFJLEVBQUUzQiw4QkFBOEIsQ0FBQzJGLG1CQUFtQjtZQUN4RDVFO1VBQ0QsQ0FBQyxDQUFDO1VBQ0Y2RSxXQUFXLEVBQUU1RyxHQUFHLENBQTBCO1lBQ3pDMkMsSUFBSSxFQUFFdkIsK0JBQStCLENBQUN3QixhQUFhO1lBQ25EcEIsU0FBUyxFQUFFO1VBQ1osQ0FBQyxDQUFDO1VBQ0Z5QyxNQUFNLEVBQUUvRCxJQUFJLENBQUNVLGdCQUFnQixDQUFDc0QsaUJBQWlCO1FBQ2hELENBQUMsQ0FBQztNQUNILENBQUMsTUFBTTtRQUNOLE1BQU1sRSxHQUFHLENBQWE7VUFBRTJDLElBQUksRUFBRS9CLGdCQUFnQixDQUFDaUc7UUFBYSxDQUFDLENBQUM7TUFDL0Q7TUFFQSxNQUFNL0csR0FBRyxDQUFDO1FBQ1RrRyxhQUFhLEVBQUVoRyxHQUFHLENBQXVCO1VBQUUyQyxJQUFJLEVBQUV0Qiw0QkFBNEI7VUFBRUksU0FBUyxFQUFFO1FBQU8sQ0FBQyxDQUFDO1FBQ25Hd0Usb0JBQW9CLEVBQUVsRyxJQUFJLENBQUNTLHVCQUF1QixDQUFDd0QsWUFBWSxFQUFFLElBQUk7TUFDdEUsQ0FBQyxDQUFDO01BQ0ZqQyxVQUFVLEdBQUcsSUFBSTtJQUNsQixDQUFDLFFBQVEsSUFBSTtFQUNkLENBQUM7QUFBQSIsImlnbm9yZUxpc3QiOltdfQ==
|