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.
Files changed (258) hide show
  1. package/lib/components/ActionList.js +164 -37
  2. package/lib/components/AlertDialog.js +128 -12
  3. package/lib/components/AlertWithIcon.js +88 -29
  4. package/lib/components/ConnectedModal.js +35 -12
  5. package/lib/components/Dropdowns/GroupsDropdown.js +63 -45
  6. package/lib/components/Dropdowns/ManagedNavDropdown.js +92 -67
  7. package/lib/components/Dropdowns/UserDropdown.js +105 -24
  8. package/lib/components/Dropdowns/index.js +4 -10
  9. package/lib/components/EntityOwnerList.js +47 -21
  10. package/lib/components/Error.js +101 -12
  11. package/lib/components/ErrorBoundary.js +127 -38
  12. package/lib/components/ErrorMessage.js +39 -12
  13. package/lib/components/Forms/DateField.js +56 -45
  14. package/lib/components/Forms/TimeField.js +76 -45
  15. package/lib/components/Forms/index.js +3 -5
  16. package/lib/components/Groups/CreateEditCopySaveButtons.js +109 -14
  17. package/lib/components/Groups/ExternalGroups/Attach.js +206 -151
  18. package/lib/components/Groups/ExternalGroups/Table.js +176 -48
  19. package/lib/components/Groups/GroupCreateOrEditCommonProps.js +2 -2
  20. package/lib/components/Groups/RosterSyncInfo.js +142 -23
  21. package/lib/components/HOC/AccessibleAppComponent.js +88 -72
  22. package/lib/components/HOC/ActivityRequiredComponent.js +68 -33
  23. package/lib/components/HOC/AsyncComponent.js +49 -41
  24. package/lib/components/HOC/AuthenticatedComponent.js +55 -44
  25. package/lib/components/HOC/CollectionComponent.js +154 -104
  26. package/lib/components/HOC/CollectionFirstItemComponent.js +45 -40
  27. package/lib/components/HOC/CollectionItemComponent.js +152 -100
  28. package/lib/components/HOC/ConnectedModalComponent.js +87 -69
  29. package/lib/components/HOC/DataDependentComponent.js +26 -27
  30. package/lib/components/HOC/EntityComponent.js +57 -53
  31. package/lib/components/HOC/FullscreenModalComponent.js +139 -108
  32. package/lib/components/HOC/GroupActivityRequiredComponent.js +27 -20
  33. package/lib/components/HOC/GuidComponent.js +20 -20
  34. package/lib/components/HOC/ModelContextDependencyVerifyComponent.js +32 -29
  35. package/lib/components/HOC/ModelErrorRedirectComponent.js +37 -39
  36. package/lib/components/HOC/SearchPersistorComponent.js +237 -173
  37. package/lib/components/HOC/UnauthenticatedComponent.js +32 -30
  38. package/lib/components/HOC/UserComponent.js +6 -8
  39. package/lib/components/Icons/IconAlphaList.js +28 -8
  40. package/lib/components/Icons/IconExternalUser.js +28 -8
  41. package/lib/components/Icons/IconImpersonation.js +28 -8
  42. package/lib/components/Icons/IconStopImpersonating.js +28 -8
  43. package/lib/components/Icons/IconTable.js +29 -9
  44. package/lib/components/Icons/IconTableDeleteCol.js +28 -8
  45. package/lib/components/Icons/IconTableDeleteRow.js +28 -8
  46. package/lib/components/Icons/IconTableInsertCol.js +28 -8
  47. package/lib/components/Icons/IconTableInsertRow.js +28 -8
  48. package/lib/components/Impersonation/Button.js +71 -16
  49. package/lib/components/Impersonation/Link.js +72 -16
  50. package/lib/components/Impersonation/UserDetail.js +60 -11
  51. package/lib/components/Loading.js +23 -8
  52. package/lib/components/LockDownBrowser/Check.js +188 -51
  53. package/lib/components/LockDownBrowser/ExitButton.js +22 -13
  54. package/lib/components/LockDownBrowser/Launch.js +64 -64
  55. package/lib/components/Lti/Confirm.js +147 -14
  56. package/lib/components/Lti/CreateNonLtiGroupAlertDialog.js +165 -36
  57. package/lib/components/Lti/Launch.js +99 -25
  58. package/lib/components/Lti/LaunchGroup.js +81 -16
  59. package/lib/components/ManageTable.js +304 -90
  60. package/lib/components/ManageTableNoDataComponent.js +38 -7
  61. package/lib/components/NewVersionAlert.js +76 -49
  62. package/lib/components/NotFound.js +81 -11
  63. package/lib/components/Notifications.js +179 -129
  64. package/lib/components/PaginationNextButton.js +28 -9
  65. package/lib/components/PaginationPreviousButton.js +28 -9
  66. package/lib/components/Quill/CustomToolbar.js +427 -222
  67. package/lib/components/Quill/Formats/Image.js +67 -67
  68. package/lib/components/Quill/Formats/List.js +38 -47
  69. package/lib/components/Quill/Formats/Video.js +23 -26
  70. package/lib/components/Quill/ImageDropModule.js +136 -114
  71. package/lib/components/Quill/ImageWarning.js +41 -12
  72. package/lib/components/Quill/ImageWithAltTextModal.js +420 -89
  73. package/lib/components/Quill/Specs/CustomImageSpec.js +32 -31
  74. package/lib/components/Quill/Specs/CustomVideoSpec.js +22 -23
  75. package/lib/components/Quill/TableModule/Blots/BaseTableBlot.js +89 -97
  76. package/lib/components/Quill/TableModule/Blots/TableBlot.js +47 -50
  77. package/lib/components/Quill/TableModule/Blots/TableBodyBlot.js +48 -51
  78. package/lib/components/Quill/TableModule/Blots/TableCellBlot.js +219 -224
  79. package/lib/components/Quill/TableModule/Blots/TableContainer.js +75 -86
  80. package/lib/components/Quill/TableModule/Blots/TableRowBlot.js +70 -73
  81. package/lib/components/Quill/TableModule/constants.js +40 -42
  82. package/lib/components/Quill/TableModule/index.js +357 -305
  83. package/lib/components/Quill/TableModule/utils.js +39 -48
  84. package/lib/components/Quill/accessibilityFix.js +219 -223
  85. package/lib/components/Quill/index.js +30 -33
  86. package/lib/components/RefreshIndicator/Bordered.js +44 -10
  87. package/lib/components/RefreshIndicator/Inline.js +43 -12
  88. package/lib/components/RefreshIndicator/index.js +257 -62
  89. package/lib/components/SearchControls.js +211 -14
  90. package/lib/components/SentryRoute.js +5 -7
  91. package/lib/components/Tables/RoleFilter.js +66 -38
  92. package/lib/components/Tables/TextFilter.js +58 -18
  93. package/lib/components/UserRoles/Add.js +193 -99
  94. package/lib/components/UserRoles/Context.js +3 -6
  95. package/lib/components/UserRoles/RoleCell.js +176 -75
  96. package/lib/components/UserRoles/Select.js +151 -20
  97. package/lib/components/UserRoles/Table.js +215 -82
  98. package/lib/components/UserRoles/index.js +526 -386
  99. package/lib/config/eslint/index.js +26 -29
  100. package/lib/config/eslint/lib/order.js +21 -28
  101. package/lib/config/eslint/lib/prettier.js +15 -19
  102. package/lib/config/eslint/lib/typescript.js +87 -113
  103. package/lib/config/eslint/react.js +18 -15
  104. package/lib/constants/baseActivity.js +26 -28
  105. package/lib/constants/baseRole.js +10 -12
  106. package/lib/constants/configuration.js +43 -55
  107. package/lib/constants/externalProviderType.js +6 -8
  108. package/lib/constants/fetchErrorData.js +10 -12
  109. package/lib/constants/index.js +13 -15
  110. package/lib/constants/lockDownBrowser.js +23 -25
  111. package/lib/constants/mockData.js +370 -300
  112. package/lib/constants/modelStatus.js +11 -13
  113. package/lib/constants/notificationType.js +8 -10
  114. package/lib/constants/operatingSystem.js +8 -10
  115. package/lib/constants/shard.js +7 -9
  116. package/lib/constants/table.js +18 -22
  117. package/lib/constants/tier.js +8 -10
  118. package/lib/constants/userRole.js +11 -8
  119. package/lib/endpointMappings.js +191 -182
  120. package/lib/hooks/useCollection.js +79 -65
  121. package/lib/hooks/useCollectionConfiguration.js +220 -80
  122. package/lib/hooks/useCollectionItem.js +151 -57
  123. package/lib/hooks/useGuid.js +16 -9
  124. package/lib/hooks/usePrevious.js +14 -13
  125. package/lib/index.js +11 -26
  126. package/lib/redux/actionCreator.js +44 -35
  127. package/lib/redux/actions/AuthAction.js +45 -32
  128. package/lib/redux/actions/ModalAction.js +6 -8
  129. package/lib/redux/actions/ModelAction.js +95 -43
  130. package/lib/redux/actions/NotificationAction.js +6 -8
  131. package/lib/redux/actions/SearchAction.js +5 -7
  132. package/lib/redux/actions/index.js +6 -8
  133. package/lib/redux/configureReducers.js +48 -46
  134. package/lib/redux/configureStore.js +77 -91
  135. package/lib/redux/helpers.js +2 -5
  136. package/lib/redux/reducers/authReducer.js +44 -43
  137. package/lib/redux/reducers/index.js +7 -14
  138. package/lib/redux/reducers/modalsReducer.js +43 -31
  139. package/lib/redux/reducers/modelsReducer.js +131 -137
  140. package/lib/redux/reducers/notificationsReducer.js +20 -20
  141. package/lib/redux/reducers/searchReducer.js +13 -13
  142. package/lib/redux/sagas/appInsightsSaga.js +19 -21
  143. package/lib/redux/sagas/authSaga.js +248 -234
  144. package/lib/redux/sagas/caliperSaga.js +142 -131
  145. package/lib/redux/sagas/clockOffsetSaga.js +29 -32
  146. package/lib/redux/sagas/configurationSaga.js +8 -10
  147. package/lib/redux/sagas/downtimeApiErrorSaga.js +16 -19
  148. package/lib/redux/sagas/errorSaga.js +23 -24
  149. package/lib/redux/sagas/googleAnalyticsSaga.js +24 -27
  150. package/lib/redux/sagas/identityProviderSaga.js +19 -21
  151. package/lib/redux/sagas/initialDataLoadSaga.js +34 -31
  152. package/lib/redux/sagas/lockDownBrowserErrorSaga.js +25 -22
  153. package/lib/redux/sagas/modelFetchSaga.js +302 -286
  154. package/lib/redux/sagas/noStoreSaga.js +60 -61
  155. package/lib/redux/sagas/postLoginDataSaga.js +37 -32
  156. package/lib/redux/sagas/postLoginRedirectSaga.js +22 -27
  157. package/lib/redux/sagas/rootSaga.js +77 -60
  158. package/lib/redux/sagas/sentrySaga.js +25 -28
  159. package/lib/redux/sagas/userIdSaga.js +13 -15
  160. package/lib/services/codeProviderService.js +21 -21
  161. package/lib/services/dateService.js +6 -8
  162. package/lib/services/documentService.js +10 -11
  163. package/lib/services/fetchService.js +103 -95
  164. package/lib/services/persistenceService.js +27 -30
  165. package/lib/services/ticketProviderService.js +25 -25
  166. package/lib/services/tokenPersistenceService.js +8 -10
  167. package/lib/services/windowService.js +14 -16
  168. package/lib/startup.js +110 -101
  169. package/lib/types/AppConfiguration.js +2 -2
  170. package/lib/types/Artifact.js +7 -9
  171. package/lib/types/BaseReduxState.js +2 -2
  172. package/lib/types/Client.js +2 -2
  173. package/lib/types/Collection.js +2 -2
  174. package/lib/types/Configuration.js +2 -2
  175. package/lib/types/DeepLinkingResponseRequest.js +2 -2
  176. package/lib/types/DeletableModel.js +2 -2
  177. package/lib/types/Event.js +2 -2
  178. package/lib/types/ExternalGroup.js +2 -2
  179. package/lib/types/ExternalProvider.js +2 -2
  180. package/lib/types/ExternalTerm.js +2 -2
  181. package/lib/types/Group.js +2 -2
  182. package/lib/types/IdentityProvider.js +2 -2
  183. package/lib/types/LtiLaunch.js +2 -2
  184. package/lib/types/NameOnlyEntity.js +2 -2
  185. package/lib/types/Notification.js +2 -2
  186. package/lib/types/OptionalRecord.js +2 -2
  187. package/lib/types/OwnerSchedule.js +2 -2
  188. package/lib/types/PropertyOfType.js +2 -2
  189. package/lib/types/Quill.js +2 -2
  190. package/lib/types/RoleDescription.js +2 -2
  191. package/lib/types/Search.js +2 -2
  192. package/lib/types/SimpleLocation.js +2 -2
  193. package/lib/types/UniTime.js +2 -2
  194. package/lib/types/User.js +2 -2
  195. package/lib/types/UserRole.js +2 -2
  196. package/lib/types/auth/AuthState.js +2 -2
  197. package/lib/types/auth/CasV1LoginRequestBody.js +2 -2
  198. package/lib/types/auth/ClientCredentials.js +2 -2
  199. package/lib/types/auth/CodeProviderService.js +2 -2
  200. package/lib/types/auth/LocalLoginRequestBody.js +2 -2
  201. package/lib/types/auth/TicketProviderService.js +2 -2
  202. package/lib/types/auth/TokenPersistenceService.js +2 -2
  203. package/lib/types/auth/index.js +8 -10
  204. package/lib/types/externals.d.js +2 -0
  205. package/lib/types/index.js +29 -31
  206. package/lib/types/net/EndpointConfig.js +2 -2
  207. package/lib/types/net/EndpointMapping.js +2 -2
  208. package/lib/types/net/EndpointMappings.js +2 -2
  209. package/lib/types/net/ErrorHandler.js +2 -2
  210. package/lib/types/net/FetchConfig.js +2 -2
  211. package/lib/types/net/FetchErrorData.js +6 -8
  212. package/lib/types/net/FetchResult.js +2 -2
  213. package/lib/types/net/HTTPMethod.js +2 -2
  214. package/lib/types/net/HTTPStatusCode.js +12 -14
  215. package/lib/types/net/Metadata.js +2 -2
  216. package/lib/types/net/Model.js +2 -2
  217. package/lib/types/net/ModelCollection.js +2 -2
  218. package/lib/types/net/ModelsState.js +2 -2
  219. package/lib/types/net/OAuthToken.js +2 -2
  220. package/lib/types/net/OAuthTokenOrNull.js +2 -2
  221. package/lib/types/net/TokenAccessFunction.js +2 -2
  222. package/lib/types/net/index.js +17 -19
  223. package/lib/utils/baseActivity.js +83 -85
  224. package/lib/utils/baseRole.js +32 -36
  225. package/lib/utils/collection.js +403 -297
  226. package/lib/utils/cookies.js +19 -23
  227. package/lib/utils/date.js +188 -205
  228. package/lib/utils/dom.js +130 -131
  229. package/lib/utils/domainIdentifier.js +4 -8
  230. package/lib/utils/entityUserRole.js +2 -5
  231. package/lib/utils/error.js +14 -19
  232. package/lib/utils/events.js +32 -31
  233. package/lib/utils/externalGroup.js +20 -25
  234. package/lib/utils/externalProviders.js +4 -7
  235. package/lib/utils/externalTerms.js +6 -6
  236. package/lib/utils/fetch.js +168 -176
  237. package/lib/utils/group.js +14 -11
  238. package/lib/utils/groupDates.js +38 -46
  239. package/lib/utils/groupRoles.js +23 -32
  240. package/lib/utils/lockDownBrowser.js +12 -15
  241. package/lib/utils/logger.js +23 -28
  242. package/lib/utils/lti.js +4 -7
  243. package/lib/utils/model.js +28 -43
  244. package/lib/utils/number.js +9 -13
  245. package/lib/utils/promise.js +23 -26
  246. package/lib/utils/quill.js +55 -60
  247. package/lib/utils/route.js +52 -60
  248. package/lib/utils/search.js +72 -87
  249. package/lib/utils/shard.js +33 -42
  250. package/lib/utils/sort.js +47 -50
  251. package/lib/utils/string.js +10 -12
  252. package/lib/utils/table.js +29 -33
  253. package/lib/utils/timezone.js +7 -12
  254. package/lib/utils/url.js +130 -144
  255. package/lib/utils/user.js +54 -64
  256. package/lib/utils/userAgent.js +7 -14
  257. package/lib/utils/userRole.js +36 -39
  258. package/package.json +17 -3
@@ -1,342 +1,353 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.defaultErrorHandler = exports.defaultTokenAccessFunction = exports.takeMatchesTerminationAction = exports.matchesTerminationAction = exports.TRY_LIMIT = void 0;
4
- exports.setEndpointMappings = setEndpointMappings;
5
- exports.modelFetch = modelFetch;
6
- exports.modelFetchLoop = modelFetchLoop;
7
- exports.modelFetchRecurring = modelFetchRecurring;
8
- exports.default = modelFetchSaga;
9
- const tslib_1 = require("tslib");
10
- const lodash_1 = require("lodash");
11
- const moment_timezone_1 = tslib_1.__importDefault(require("moment-timezone"));
12
- const effects_1 = require("redux-saga/effects");
13
- const fetchErrorData_1 = require("../../constants/fetchErrorData");
14
- const dateService_1 = require("../../services/dateService");
15
- const fetchService_1 = require("../../services/fetchService");
16
- const windowService_1 = require("../../services/windowService");
17
- const types_1 = require("../../types");
18
- const error_1 = require("../../utils/error");
19
- const fetch_1 = require("../../utils/fetch");
20
- const logger_1 = require("../../utils/logger");
21
- const actions_1 = require("../actions");
1
+ import { isNil, isPlainObject, merge } from 'lodash';
2
+ import moment from 'moment-timezone';
3
+ import { call, cancel, cancelled, delay, fork, put, take, takeEvery } from 'redux-saga/effects';
4
+ import { networkOfflineErrorData, unknownErrorData } from '../../constants/fetchErrorData';
5
+ import { getLastActivityDate } from '../../services/dateService';
6
+ import { sendFetch, setApiRoot } from '../../services/fetchService';
7
+ import { windowService } from '../../services/windowService';
8
+ import { HTTP_STATUS_CODE } from '../../types';
9
+ import { constructErrorFromCaughtError } from '../../utils/error';
10
+ import { isFetchErrorData, prepareFetch } from '../../utils/fetch';
11
+ import { getLogger } from '../../utils/logger';
12
+ import { MODEL_FETCH_ERROR_ACTION_TYPE, MODEL_FETCH_REQUEST_ACTION_TYPE, MODEL_FETCH_RESULT_ACTION_TYPE, MODEL_FETCH_START_ACTION_TYPE, MODEL_REMOVE_KEY_ACTION_TYPE, PERIODIC_MODEL_FETCH_TERMINATION_ACTION_TYPE } from '../actions';
13
+
22
14
  /** The total number of tries for `fetchData`, including the initial request. */
23
- exports.TRY_LIMIT = 5;
15
+ export const TRY_LIMIT = 5;
16
+
24
17
  //#region Helpers
25
- const matchesTerminationAction = (incomingAction, dataRequestAction) => incomingAction.type === actions_1.PERIODIC_MODEL_FETCH_TERMINATION_ACTION_TYPE.TERMINATE &&
26
- incomingAction.taskId === dataRequestAction.taskId;
27
- exports.matchesTerminationAction = matchesTerminationAction;
28
- const takeMatchesTerminationAction = (dataRequestAction) => (incomingAction) => (0, exports.matchesTerminationAction)(incomingAction, dataRequestAction);
29
- exports.takeMatchesTerminationAction = takeMatchesTerminationAction;
18
+
19
+ export const matchesTerminationAction = (incomingAction, dataRequestAction) => incomingAction.type === PERIODIC_MODEL_FETCH_TERMINATION_ACTION_TYPE.TERMINATE && incomingAction.taskId === dataRequestAction.taskId;
20
+ export const takeMatchesTerminationAction = dataRequestAction => incomingAction => matchesTerminationAction(incomingAction, dataRequestAction);
21
+
30
22
  /* istanbul ignore next */
31
23
  /* eslint-disable-next-line require-yield */
32
- const defaultTokenAccessFunction = function* () {
33
- return null;
24
+ export const defaultTokenAccessFunction = function* () {
25
+ return null;
34
26
  };
35
- exports.defaultTokenAccessFunction = defaultTokenAccessFunction;
27
+
36
28
  /* istanbul ignore next */
37
- const defaultErrorHandler = () => {
38
- return;
29
+ export const defaultErrorHandler = () => {
30
+ return;
39
31
  };
40
- exports.defaultErrorHandler = defaultErrorHandler;
41
- const getChildCollectionKeys = (endpointMapping) => Object.keys(endpointMapping).reduce((out, key) => {
42
- const value = endpointMapping[key];
43
- if (!value || !Object.prototype.hasOwnProperty.call(value, '_config')) {
44
- return out;
45
- }
46
- const em = value;
47
- if (!!em._config && !!em._config.isCollection) {
48
- out.push(key);
49
- }
32
+ const getChildCollectionKeys = endpointMapping => Object.keys(endpointMapping).reduce((out, key) => {
33
+ const value = endpointMapping[key];
34
+ if (!value || !Object.prototype.hasOwnProperty.call(value, '_config')) {
50
35
  return out;
36
+ }
37
+ const em = value;
38
+ if (!!em._config && !!em._config.isCollection) {
39
+ out.push(key);
40
+ }
41
+ return out;
51
42
  }, []);
52
43
  const convertCollections = (endpointMapping, prepareFetchResult, data) => {
53
- const { endpointConfig, fetchConfig, isCollectionItemCreate, isCollectionItemFetch } = prepareFetchResult;
54
- const childCollectionKeys = getChildCollectionKeys(endpointMapping);
55
- const hasChildCollections = childCollectionKeys.length > 0;
56
- // no collections, return as-is
57
- if (!endpointConfig.isCollection && !hasChildCollections) {
58
- return data;
59
- }
60
- // delete returns no data
61
- if (fetchConfig.method === 'DELETE') {
62
- return {};
63
- }
64
- // data is a collection item, convert any child collections in item response
65
- if (isCollectionItemFetch || isCollectionItemCreate) {
66
- return convertDataCollectionItem(data, childCollectionKeys, endpointMapping);
67
- }
68
- // data is a collection, convert collection response, and any child collections in each item
69
- return endpointConfig.isCollection
70
- ? convertDataToCollection(data, childCollectionKeys, endpointMapping)
71
- : convertDataCollectionItem(data, childCollectionKeys, endpointMapping);
44
+ const {
45
+ endpointConfig,
46
+ fetchConfig,
47
+ isCollectionItemCreate,
48
+ isCollectionItemFetch
49
+ } = prepareFetchResult;
50
+ const childCollectionKeys = getChildCollectionKeys(endpointMapping);
51
+ const hasChildCollections = childCollectionKeys.length > 0;
52
+ // no collections, return as-is
53
+ if (!endpointConfig.isCollection && !hasChildCollections) {
54
+ return data;
55
+ }
56
+ // delete returns no data
57
+ if (fetchConfig.method === 'DELETE') {
58
+ return {};
59
+ }
60
+ // data is a collection item, convert any child collections in item response
61
+ if (isCollectionItemFetch || isCollectionItemCreate) {
62
+ return convertDataCollectionItem(data, childCollectionKeys, endpointMapping);
63
+ }
64
+ // data is a collection, convert collection response, and any child collections in each item
65
+ return endpointConfig.isCollection ? convertDataToCollection(data, childCollectionKeys, endpointMapping) : convertDataCollectionItem(data, childCollectionKeys, endpointMapping);
72
66
  };
73
67
  const convertDataCollectionItem = (data, collectionKeys, endpointMapping) => {
74
- if ((0, lodash_1.isNil)(data)) {
75
- return data;
68
+ if (isNil(data)) {
69
+ return data;
70
+ }
71
+ return Object.keys(data).reduce((out, key) => {
72
+ let value = data[key];
73
+ if (collectionKeys.includes(key)) {
74
+ const childEndpointMapping = endpointMapping[key];
75
+ const childCollectionKeys = getChildCollectionKeys(childEndpointMapping);
76
+ value = convertDataToCollection(value, childCollectionKeys, childEndpointMapping);
76
77
  }
77
- return Object.keys(data).reduce((out, key) => {
78
- let value = data[key];
79
- if (collectionKeys.includes(key)) {
80
- const childEndpointMapping = endpointMapping[key];
81
- const childCollectionKeys = getChildCollectionKeys(childEndpointMapping);
82
- value = convertDataToCollection(value, childCollectionKeys, childEndpointMapping);
83
- }
84
- out[key] = value;
85
- return out;
86
- }, {});
78
+ out[key] = value;
79
+ return out;
80
+ }, {});
87
81
  };
88
82
  const convertDataToCollection = (data, collectionKeys, endpointMapping) => {
89
- if ((0, lodash_1.isNil)(data)) {
90
- return data;
83
+ if (isNil(data)) {
84
+ return data;
85
+ }
86
+ return Object.keys(data).reduce((out, key) => {
87
+ const value = convertDataCollectionItem(data[key], collectionKeys, endpointMapping);
88
+ /* istanbul ignore else */
89
+ if (value && value.id) {
90
+ out[value.id] = value;
91
91
  }
92
- return Object.keys(data).reduce((out, key) => {
93
- const value = convertDataCollectionItem(data[key], collectionKeys, endpointMapping);
94
- /* istanbul ignore else */
95
- if (value && value.id) {
96
- out[value.id] = value;
97
- }
98
- return out;
99
- }, {});
92
+ return out;
93
+ }, {});
100
94
  };
95
+
101
96
  /**
102
97
  * Using the `lastActivityDate` set in local storage by "studiokit-caliper-js",
103
98
  * determine if the user has been idle, e.g. has had no activity in the last 15 minutes.
104
99
  */
105
100
  function isUserIdle() {
106
- const lastActivityDateString = (0, dateService_1.getLastActivityDate)();
107
- if (!lastActivityDateString) {
108
- return false;
109
- }
110
- const lastActivityDateUtc = moment_timezone_1.default.utc(lastActivityDateString);
111
- const nowUtc = moment_timezone_1.default.utc();
112
- const idleMinutesThreshold = 1000 * 60 * 15; // 15 minutes
113
- return nowUtc.diff(lastActivityDateUtc) >= idleMinutesThreshold;
101
+ const lastActivityDateString = getLastActivityDate();
102
+ if (!lastActivityDateString) {
103
+ return false;
104
+ }
105
+ const lastActivityDateUtc = moment.utc(lastActivityDateString);
106
+ const nowUtc = moment.utc();
107
+ const idleMinutesThreshold = 1000 * 60 * 15; // 15 minutes
108
+
109
+ return nowUtc.diff(lastActivityDateUtc) >= idleMinutesThreshold;
114
110
  }
111
+
115
112
  //#endregion Helpers
113
+
116
114
  //#region Local Variables
115
+
117
116
  let endpointMappings;
118
117
  let tokenAccessFunction;
119
118
  let errorHandler;
120
119
  let logger;
120
+
121
121
  //#endregion Local Variables
122
+
122
123
  /** Set the shared EndpointMappings variable, exposed for testability */
123
- function setEndpointMappings(endpointMappingsParam) {
124
- endpointMappings = endpointMappingsParam;
124
+ export function setEndpointMappings(endpointMappingsParam) {
125
+ endpointMappings = endpointMappingsParam;
125
126
  }
127
+
126
128
  /**
127
129
  * Construct a request based on the provided dataRequestAction, make a request with a configurable retry,
128
130
  * and handle errors, logging and dispatching all steps.
129
131
  *
130
132
  * @param modelFetchRequestAction A model fetch request action with the request configuration
131
133
  */
132
- function* modelFetch(modelFetchRequestAction) {
133
- let prepareFetchResult = undefined;
134
+ export function* modelFetch(modelFetchRequestAction) {
135
+ let prepareFetchResult = undefined;
136
+ try {
137
+ prepareFetchResult = prepareFetch(modelFetchRequestAction, endpointMappings);
138
+ } catch (error) {
139
+ // Note: `prepareFetch` already ensures that `error` is an actual `Error`
140
+ logger.error(error);
141
+ errorHandler(error, modelFetchRequestAction);
142
+ return;
143
+ }
144
+
145
+ // get prepared variables
146
+ const {
147
+ modelPath,
148
+ fetchConfig,
149
+ endpointMapping,
150
+ isCollectionItemCreate,
151
+ isCollectionItemDelete
152
+ } = prepareFetchResult;
153
+
154
+ // Configure retry
155
+ const tryLimit = modelFetchRequestAction.noRetry ? 1 : TRY_LIMIT;
156
+ let tryCount = 0;
157
+ let didFail = false;
158
+ let lastFetchResult;
159
+ let lastError;
160
+ let lastErrorData;
161
+ const fetchResultUndefinedErrorMessage = "'fetchResult' is undefined";
162
+ const fetchResultNotOkErrorMessage = "'fetchResult.ok' is false";
163
+
164
+ // Run retry loop
165
+ do {
166
+ didFail = false;
167
+ tryCount++;
168
+
169
+ // dispatch that the fetch request started, which updates `_metadata` and `guid`, if provided, at the target redux `modelPath`
170
+ yield put({
171
+ type: modelFetchRequestAction.noStore ? MODEL_FETCH_START_ACTION_TYPE.TRANSIENT_FETCH_START : MODEL_FETCH_START_ACTION_TYPE.FETCH_START,
172
+ modelPath,
173
+ guid: modelFetchRequestAction.guid
174
+ });
175
+ let fetchResult = undefined;
134
176
  try {
135
- prepareFetchResult = (0, fetch_1.prepareFetch)(modelFetchRequestAction, endpointMappings);
136
- }
137
- catch (error) {
138
- // Note: `prepareFetch` already ensures that `error` is an actual `Error`
139
- logger.error(error);
140
- errorHandler(error, modelFetchRequestAction);
141
- return;
142
- }
143
- // get prepared variables
144
- const { modelPath, fetchConfig, endpointMapping, isCollectionItemCreate, isCollectionItemDelete } = prepareFetchResult;
145
- // Configure retry
146
- const tryLimit = modelFetchRequestAction.noRetry ? 1 : exports.TRY_LIMIT;
147
- let tryCount = 0;
148
- let didFail = false;
149
- let lastFetchResult;
150
- let lastError;
151
- let lastErrorData;
152
- const fetchResultUndefinedErrorMessage = "'fetchResult' is undefined";
153
- const fetchResultNotOkErrorMessage = "'fetchResult.ok' is false";
154
- // Run retry loop
155
- do {
156
- didFail = false;
157
- tryCount++;
158
- // dispatch that the fetch request started, which updates `_metadata` and `guid`, if provided, at the target redux `modelPath`
159
- yield (0, effects_1.put)({
160
- type: modelFetchRequestAction.noStore
161
- ? actions_1.MODEL_FETCH_START_ACTION_TYPE.TRANSIENT_FETCH_START
162
- : actions_1.MODEL_FETCH_START_ACTION_TYPE.FETCH_START,
163
- modelPath,
164
- guid: modelFetchRequestAction.guid
177
+ // get the OAuth Token, if any, and call `fetch`
178
+ const oauthToken = yield call(tokenAccessFunction, modelFetchRequestAction.modelName);
179
+ if (oauthToken?.access_token) {
180
+ fetchConfig.headers = merge({}, fetchConfig.headers, {
181
+ Authorization: `Bearer ${oauthToken.access_token}`
165
182
  });
166
- let fetchResult = undefined;
167
- try {
168
- // get the OAuth Token, if any, and call `fetch`
169
- const oauthToken = yield (0, effects_1.call)(tokenAccessFunction, modelFetchRequestAction.modelName);
170
- if (oauthToken === null || oauthToken === void 0 ? void 0 : oauthToken.access_token) {
171
- fetchConfig.headers = (0, lodash_1.merge)({}, fetchConfig.headers, {
172
- Authorization: `Bearer ${oauthToken.access_token}`
173
- });
174
- }
175
- fetchResult = yield (0, effects_1.call)(fetchService_1.sendFetch, fetchConfig);
176
- // store `fetchResult` for reference after the do-while loop
177
- lastFetchResult = fetchResult;
178
- // throw on unsuccessful result to short-circuit and trigger catch+retry
179
- if (!fetchResult)
180
- throw new Error(fetchResultUndefinedErrorMessage);
181
- if (!fetchResult.ok)
182
- throw new Error(fetchResultNotOkErrorMessage);
183
- // handle success
184
- const resultActionType = modelFetchRequestAction.noStore
185
- ? actions_1.MODEL_FETCH_RESULT_ACTION_TYPE.TRANSIENT_FETCH_RESULT_RECEIVED
186
- : actions_1.MODEL_FETCH_RESULT_ACTION_TYPE.FETCH_RESULT_RECEIVED;
187
- const data = convertCollections(endpointMapping, prepareFetchResult, fetchResult.data);
188
- // attach guid to result data if it is an object
189
- if (modelFetchRequestAction.guid && (0, lodash_1.isPlainObject)(data)) {
190
- data.guid = modelFetchRequestAction.guid;
191
- }
192
- // POST new collection item
193
- if (isCollectionItemCreate) {
194
- const modelPathLevels = modelPath.split('.');
195
- // remove guid
196
- modelPathLevels.pop();
197
- // add by new result's id
198
- yield (0, effects_1.put)({
199
- type: resultActionType,
200
- modelPath: `${modelPathLevels.join('.')}.${data.id}`,
201
- guid: modelFetchRequestAction.guid,
202
- replaceValue: modelFetchRequestAction.replaceValue,
203
- data
204
- });
205
- // remove temp item under guid key
206
- yield (0, effects_1.put)({
207
- type: actions_1.MODEL_REMOVE_KEY_ACTION_TYPE,
208
- modelPath: modelPath
209
- });
210
- }
211
- // DELETE collection item
212
- else if (isCollectionItemDelete) {
213
- yield (0, effects_1.put)({
214
- type: actions_1.MODEL_REMOVE_KEY_ACTION_TYPE,
215
- modelPath: modelPath
216
- });
217
- }
218
- else {
219
- yield (0, effects_1.put)({
220
- type: resultActionType,
221
- modelPath: modelPath,
222
- guid: modelFetchRequestAction.guid,
223
- replaceValue: modelFetchRequestAction.replaceValue,
224
- data
225
- });
226
- }
227
- }
228
- catch (caughtError) {
229
- // construct `Error` using `caughtError`
230
- const baseErrorMessage = `'modelFetch' failed for '${modelPath}'`;
231
- const error = (0, error_1.constructErrorFromCaughtError)(caughtError, 'ModelFetchError', baseErrorMessage);
232
- // create a default `errorData` object
233
- const isNetworkOnline = windowService_1.windowService.getIsOnline();
234
- let errorData = isNetworkOnline ? fetchErrorData_1.unknownErrorData : fetchErrorData_1.networkOfflineErrorData;
235
- // update `errorData` from the `fetchResult.data`
236
- // for when the fetch got a result, but it was an unsuccessful status
237
- if ((fetchResult === null || fetchResult === void 0 ? void 0 : fetchResult.data) && (0, fetch_1.isFetchErrorData)(fetchResult.data)) {
238
- errorData = fetchResult.data;
239
- // update the error's message to include info from the fetch result
240
- error.message = `${baseErrorMessage}.\n${fetchConfig.method || 'GET'} ${fetchConfig.path} failed.\n${errorData.title} (${errorData.status})`;
241
- if (errorData.detail) {
242
- error.message += `: ${errorData.detail}`;
243
- }
244
- }
245
- // track `errorData` and `error` for after the while-loop
246
- lastErrorData = errorData;
247
- lastError = error;
248
- // dispatch that this try failed
249
- yield (0, effects_1.put)({
250
- type: actions_1.MODEL_FETCH_ERROR_ACTION_TYPE.TRY_FETCH_FAILED,
251
- modelPath: modelPath,
252
- guid: modelFetchRequestAction.guid,
253
- errorData
254
- });
255
- // log to the console
256
- logger.error(error);
257
- didFail = true;
258
- // Do not retry if the response is between 400-500 (except 408: request timeout)
259
- if (errorData.status >= types_1.HTTP_STATUS_CODE.BAD_REQUEST &&
260
- errorData.status < types_1.HTTP_STATUS_CODE.INTERNAL_SERVER_ERROR &&
261
- errorData.status !== types_1.HTTP_STATUS_CODE.REQUEST_TIMEOUT) {
262
- tryCount = tryLimit;
263
- }
264
- // if there are tries remaining, perform an exponential backoff
265
- if (tryCount < tryLimit) {
266
- yield (0, effects_1.delay)(Math.pow(2, (tryCount - 1)) * 100); // 100, 200, 400, 800
267
- }
268
- }
269
- } while (tryCount < tryLimit && didFail);
270
- // if fetch failed completely after all retries
271
- if (tryCount === tryLimit && didFail) {
272
- // these will always exist
273
- const errorData = lastErrorData;
274
- const error = lastError;
275
- // dispatch that the fetch failed, which updates `_metadata` (and `guid`, if provided) at the target redux `modelPath`
276
- yield (0, effects_1.put)({
277
- type: modelFetchRequestAction.noStore
278
- ? actions_1.MODEL_FETCH_ERROR_ACTION_TYPE.TRANSIENT_FETCH_FAILED
279
- : actions_1.MODEL_FETCH_ERROR_ACTION_TYPE.FETCH_FAILED,
280
- modelPath: modelPath,
281
- guid: modelFetchRequestAction.guid,
282
- errorData
183
+ }
184
+ fetchResult = yield call(sendFetch, fetchConfig);
185
+ // store `fetchResult` for reference after the do-while loop
186
+ lastFetchResult = fetchResult;
187
+
188
+ // throw on unsuccessful result to short-circuit and trigger catch+retry
189
+ if (!fetchResult) throw new Error(fetchResultUndefinedErrorMessage);
190
+ if (!fetchResult.ok) throw new Error(fetchResultNotOkErrorMessage);
191
+
192
+ // handle success
193
+ const resultActionType = modelFetchRequestAction.noStore ? MODEL_FETCH_RESULT_ACTION_TYPE.TRANSIENT_FETCH_RESULT_RECEIVED : MODEL_FETCH_RESULT_ACTION_TYPE.FETCH_RESULT_RECEIVED;
194
+ const data = convertCollections(endpointMapping, prepareFetchResult, fetchResult.data);
195
+ // attach guid to result data if it is an object
196
+ if (modelFetchRequestAction.guid && isPlainObject(data)) {
197
+ data.guid = modelFetchRequestAction.guid;
198
+ }
199
+
200
+ // POST new collection item
201
+ if (isCollectionItemCreate) {
202
+ const modelPathLevels = modelPath.split('.');
203
+ // remove guid
204
+ modelPathLevels.pop();
205
+ // add by new result's id
206
+ yield put({
207
+ type: resultActionType,
208
+ modelPath: `${modelPathLevels.join('.')}.${data.id}`,
209
+ guid: modelFetchRequestAction.guid,
210
+ replaceValue: modelFetchRequestAction.replaceValue,
211
+ data
212
+ });
213
+ // remove temp item under guid key
214
+ yield put({
215
+ type: MODEL_REMOVE_KEY_ACTION_TYPE,
216
+ modelPath: modelPath
283
217
  });
284
- // Send error to error handler, except for 401s
285
- if (errorData.status !== types_1.HTTP_STATUS_CODE.UNAUTHORIZED) {
286
- errorHandler(error, modelFetchRequestAction, fetchConfig, lastFetchResult, errorData);
218
+ }
219
+ // DELETE collection item
220
+ else if (isCollectionItemDelete) {
221
+ yield put({
222
+ type: MODEL_REMOVE_KEY_ACTION_TYPE,
223
+ modelPath: modelPath
224
+ });
225
+ } else {
226
+ yield put({
227
+ type: resultActionType,
228
+ modelPath: modelPath,
229
+ guid: modelFetchRequestAction.guid,
230
+ replaceValue: modelFetchRequestAction.replaceValue,
231
+ data
232
+ });
233
+ }
234
+ } catch (caughtError) {
235
+ // construct `Error` using `caughtError`
236
+ const baseErrorMessage = `'modelFetch' failed for '${modelPath}'`;
237
+ const error = constructErrorFromCaughtError(caughtError, 'ModelFetchError', baseErrorMessage);
238
+ // create a default `errorData` object
239
+ const isNetworkOnline = windowService.getIsOnline();
240
+ let errorData = isNetworkOnline ? unknownErrorData : networkOfflineErrorData;
241
+ // update `errorData` from the `fetchResult.data`
242
+ // for when the fetch got a result, but it was an unsuccessful status
243
+ if (fetchResult?.data && isFetchErrorData(fetchResult.data)) {
244
+ errorData = fetchResult.data;
245
+ // update the error's message to include info from the fetch result
246
+ error.message = `${baseErrorMessage}.\n${fetchConfig.method || 'GET'} ${fetchConfig.path} failed.\n${errorData.title} (${errorData.status})`;
247
+ if (errorData.detail) {
248
+ error.message += `: ${errorData.detail}`;
287
249
  }
250
+ }
251
+
252
+ // track `errorData` and `error` for after the while-loop
253
+ lastErrorData = errorData;
254
+ lastError = error;
255
+
256
+ // dispatch that this try failed
257
+ yield put({
258
+ type: MODEL_FETCH_ERROR_ACTION_TYPE.TRY_FETCH_FAILED,
259
+ modelPath: modelPath,
260
+ guid: modelFetchRequestAction.guid,
261
+ errorData
262
+ });
263
+
264
+ // log to the console
265
+ logger.error(error);
266
+ didFail = true;
267
+
268
+ // Do not retry if the response is between 400-500 (except 408: request timeout)
269
+ if (errorData.status >= HTTP_STATUS_CODE.BAD_REQUEST && errorData.status < HTTP_STATUS_CODE.INTERNAL_SERVER_ERROR && errorData.status !== HTTP_STATUS_CODE.REQUEST_TIMEOUT) {
270
+ tryCount = tryLimit;
271
+ }
272
+
273
+ // if there are tries remaining, perform an exponential backoff
274
+ if (tryCount < tryLimit) {
275
+ yield delay(2 ** (tryCount - 1) * 100); // 100, 200, 400, 800
276
+ }
277
+ }
278
+ } while (tryCount < tryLimit && didFail);
279
+
280
+ // if fetch failed completely after all retries
281
+ if (tryCount === tryLimit && didFail) {
282
+ // these will always exist
283
+ const errorData = lastErrorData;
284
+ const error = lastError;
285
+
286
+ // dispatch that the fetch failed, which updates `_metadata` (and `guid`, if provided) at the target redux `modelPath`
287
+ yield put({
288
+ type: modelFetchRequestAction.noStore ? MODEL_FETCH_ERROR_ACTION_TYPE.TRANSIENT_FETCH_FAILED : MODEL_FETCH_ERROR_ACTION_TYPE.FETCH_FAILED,
289
+ modelPath: modelPath,
290
+ guid: modelFetchRequestAction.guid,
291
+ errorData
292
+ });
293
+
294
+ // Send error to error handler, except for 401s
295
+ if (errorData.status !== HTTP_STATUS_CODE.UNAUTHORIZED) {
296
+ errorHandler(error, modelFetchRequestAction, fetchConfig, lastFetchResult, errorData);
288
297
  }
298
+ }
289
299
  }
300
+
290
301
  /**
291
302
  * The loop saga that makes the request every {action.period} milliseconds until cancelled
292
303
  *
293
304
  * @param action An action with the request configuration
294
305
  */
295
- function* modelFetchLoop(action) {
296
- try {
297
- let hasFetched = false;
298
- do {
299
- // call the fetch if this is the first loop iteration,
300
- // or if the user is not idle
301
- if (!hasFetched || !isUserIdle()) {
302
- yield (0, effects_1.call)(modelFetch, action);
303
- hasFetched = true;
304
- }
305
- yield (0, effects_1.delay)(action.period);
306
- } while (true);
307
- }
308
- catch (caughtError) {
309
- const error = (0, error_1.constructErrorFromCaughtError)(caughtError, 'ModelFetchLoopError', 'modelFetchLoop failed');
310
- logger.error(error);
311
- // catch and log any unexpected errors not caught inside `fetchData`
312
- errorHandler(error, action);
313
- }
314
- finally {
315
- if (yield (0, effects_1.cancelled)()) {
316
- yield (0, effects_1.put)({
317
- type: actions_1.PERIODIC_MODEL_FETCH_TERMINATION_ACTION_TYPE.SUCCEEDED,
318
- taskId: action.taskId
319
- });
320
- }
306
+ export function* modelFetchLoop(action) {
307
+ try {
308
+ let hasFetched = false;
309
+ do {
310
+ // call the fetch if this is the first loop iteration,
311
+ // or if the user is not idle
312
+ if (!hasFetched || !isUserIdle()) {
313
+ yield call(modelFetch, action);
314
+ hasFetched = true;
315
+ }
316
+ yield delay(action.period);
317
+ } while (true);
318
+ } catch (caughtError) {
319
+ const error = constructErrorFromCaughtError(caughtError, 'ModelFetchLoopError', 'modelFetchLoop failed');
320
+ logger.error(error);
321
+ // catch and log any unexpected errors not caught inside `fetchData`
322
+ errorHandler(error, action);
323
+ } finally {
324
+ if (yield cancelled()) {
325
+ yield put({
326
+ type: PERIODIC_MODEL_FETCH_TERMINATION_ACTION_TYPE.SUCCEEDED,
327
+ taskId: action.taskId
328
+ });
321
329
  }
330
+ }
322
331
  }
332
+
323
333
  /**
324
334
  * Call the fetchData saga every {action.period} milliseconds. This saga requires the 'period' and 'taskId' properties
325
335
  * on the action parameter.
326
336
  *
327
337
  * @param action An action with the request configuration
328
338
  */
329
- function* modelFetchRecurring(action) {
330
- if (!action.period) {
331
- throw new Error("'period' is required");
332
- }
333
- if (!action.taskId) {
334
- throw new Error("'taskId' is required");
335
- }
336
- const bgSyncTask = yield (0, effects_1.fork)(modelFetchLoop, action);
337
- yield (0, effects_1.take)((0, exports.takeMatchesTerminationAction)(action));
338
- yield (0, effects_1.cancel)(bgSyncTask);
339
+ export function* modelFetchRecurring(action) {
340
+ if (!action.period) {
341
+ throw new Error("'period' is required");
342
+ }
343
+ if (!action.taskId) {
344
+ throw new Error("'taskId' is required");
345
+ }
346
+ const bgSyncTask = yield fork(modelFetchLoop, action);
347
+ yield take(takeMatchesTerminationAction(action));
348
+ yield cancel(bgSyncTask);
339
349
  }
350
+
340
351
  /**
341
352
  * The main saga for fetching models. Must be initialized with an EndpointMappings object that can be fetched
342
353
  * and an API root to prepend to any partial URLs specified in the EndpointMappings object. A logger should normally be provided
@@ -369,12 +380,17 @@ function* modelFetchRecurring(action) {
369
380
  * @param tokenAccessFunctionParam function that returns an optional OAuth token
370
381
  * @param errorHandlerParam A function that is called when any fetch exceptions occur
371
382
  */
372
- function* modelFetchSaga(endpointMappingsParam, apiRootParam, tokenAccessFunctionParam = exports.defaultTokenAccessFunction, errorHandlerParam = exports.defaultErrorHandler) {
373
- (0, fetchService_1.setApiRoot)(apiRootParam);
383
+ export default function modelFetchSaga(endpointMappingsParam, apiRootParam) {
384
+ let tokenAccessFunctionParam = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : defaultTokenAccessFunction;
385
+ let errorHandlerParam = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : defaultErrorHandler;
386
+ return function* () {
387
+ setApiRoot(apiRootParam);
374
388
  setEndpointMappings(endpointMappingsParam);
375
389
  errorHandler = errorHandlerParam;
376
390
  tokenAccessFunction = tokenAccessFunctionParam;
377
- logger = (0, logger_1.getLogger)();
378
- yield (0, effects_1.takeEvery)(actions_1.MODEL_FETCH_REQUEST_ACTION_TYPE.FETCH_REQUEST, modelFetch);
379
- yield (0, effects_1.takeEvery)(actions_1.MODEL_FETCH_REQUEST_ACTION_TYPE.PERIODIC_FETCH_REQUEST, modelFetchRecurring);
391
+ logger = getLogger();
392
+ yield takeEvery(MODEL_FETCH_REQUEST_ACTION_TYPE.FETCH_REQUEST, modelFetch);
393
+ yield takeEvery(MODEL_FETCH_REQUEST_ACTION_TYPE.PERIODIC_FETCH_REQUEST, modelFetchRecurring);
394
+ }();
380
395
  }
396
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJpc05pbCIsImlzUGxhaW5PYmplY3QiLCJtZXJnZSIsIm1vbWVudCIsImNhbGwiLCJjYW5jZWwiLCJjYW5jZWxsZWQiLCJkZWxheSIsImZvcmsiLCJwdXQiLCJ0YWtlIiwidGFrZUV2ZXJ5IiwibmV0d29ya09mZmxpbmVFcnJvckRhdGEiLCJ1bmtub3duRXJyb3JEYXRhIiwiZ2V0TGFzdEFjdGl2aXR5RGF0ZSIsInNlbmRGZXRjaCIsInNldEFwaVJvb3QiLCJ3aW5kb3dTZXJ2aWNlIiwiSFRUUF9TVEFUVVNfQ09ERSIsImNvbnN0cnVjdEVycm9yRnJvbUNhdWdodEVycm9yIiwiaXNGZXRjaEVycm9yRGF0YSIsInByZXBhcmVGZXRjaCIsImdldExvZ2dlciIsIk1PREVMX0ZFVENIX0VSUk9SX0FDVElPTl9UWVBFIiwiTU9ERUxfRkVUQ0hfUkVRVUVTVF9BQ1RJT05fVFlQRSIsIk1PREVMX0ZFVENIX1JFU1VMVF9BQ1RJT05fVFlQRSIsIk1PREVMX0ZFVENIX1NUQVJUX0FDVElPTl9UWVBFIiwiTU9ERUxfUkVNT1ZFX0tFWV9BQ1RJT05fVFlQRSIsIlBFUklPRElDX01PREVMX0ZFVENIX1RFUk1JTkFUSU9OX0FDVElPTl9UWVBFIiwiVFJZX0xJTUlUIiwibWF0Y2hlc1Rlcm1pbmF0aW9uQWN0aW9uIiwiaW5jb21pbmdBY3Rpb24iLCJkYXRhUmVxdWVzdEFjdGlvbiIsInR5cGUiLCJURVJNSU5BVEUiLCJ0YXNrSWQiLCJ0YWtlTWF0Y2hlc1Rlcm1pbmF0aW9uQWN0aW9uIiwiZGVmYXVsdFRva2VuQWNjZXNzRnVuY3Rpb24iLCJkZWZhdWx0RXJyb3JIYW5kbGVyIiwiZ2V0Q2hpbGRDb2xsZWN0aW9uS2V5cyIsImVuZHBvaW50TWFwcGluZyIsIk9iamVjdCIsImtleXMiLCJyZWR1Y2UiLCJvdXQiLCJrZXkiLCJ2YWx1ZSIsInByb3RvdHlwZSIsImhhc093blByb3BlcnR5IiwiZW0iLCJfY29uZmlnIiwiaXNDb2xsZWN0aW9uIiwicHVzaCIsImNvbnZlcnRDb2xsZWN0aW9ucyIsInByZXBhcmVGZXRjaFJlc3VsdCIsImRhdGEiLCJlbmRwb2ludENvbmZpZyIsImZldGNoQ29uZmlnIiwiaXNDb2xsZWN0aW9uSXRlbUNyZWF0ZSIsImlzQ29sbGVjdGlvbkl0ZW1GZXRjaCIsImNoaWxkQ29sbGVjdGlvbktleXMiLCJoYXNDaGlsZENvbGxlY3Rpb25zIiwibGVuZ3RoIiwibWV0aG9kIiwiY29udmVydERhdGFDb2xsZWN0aW9uSXRlbSIsImNvbnZlcnREYXRhVG9Db2xsZWN0aW9uIiwiY29sbGVjdGlvbktleXMiLCJpbmNsdWRlcyIsImNoaWxkRW5kcG9pbnRNYXBwaW5nIiwiaWQiLCJpc1VzZXJJZGxlIiwibGFzdEFjdGl2aXR5RGF0ZVN0cmluZyIsImxhc3RBY3Rpdml0eURhdGVVdGMiLCJ1dGMiLCJub3dVdGMiLCJpZGxlTWludXRlc1RocmVzaG9sZCIsImRpZmYiLCJlbmRwb2ludE1hcHBpbmdzIiwidG9rZW5BY2Nlc3NGdW5jdGlvbiIsImVycm9ySGFuZGxlciIsImxvZ2dlciIsInNldEVuZHBvaW50TWFwcGluZ3MiLCJlbmRwb2ludE1hcHBpbmdzUGFyYW0iLCJtb2RlbEZldGNoIiwibW9kZWxGZXRjaFJlcXVlc3RBY3Rpb24iLCJ1bmRlZmluZWQiLCJlcnJvciIsIm1vZGVsUGF0aCIsImlzQ29sbGVjdGlvbkl0ZW1EZWxldGUiLCJ0cnlMaW1pdCIsIm5vUmV0cnkiLCJ0cnlDb3VudCIsImRpZEZhaWwiLCJsYXN0RmV0Y2hSZXN1bHQiLCJsYXN0RXJyb3IiLCJsYXN0RXJyb3JEYXRhIiwiZmV0Y2hSZXN1bHRVbmRlZmluZWRFcnJvck1lc3NhZ2UiLCJmZXRjaFJlc3VsdE5vdE9rRXJyb3JNZXNzYWdlIiwibm9TdG9yZSIsIlRSQU5TSUVOVF9GRVRDSF9TVEFSVCIsIkZFVENIX1NUQVJUIiwiZ3VpZCIsImZldGNoUmVzdWx0Iiwib2F1dGhUb2tlbiIsIm1vZGVsTmFtZSIsImFjY2Vzc190b2tlbiIsImhlYWRlcnMiLCJBdXRob3JpemF0aW9uIiwiRXJyb3IiLCJvayIsInJlc3VsdEFjdGlvblR5cGUiLCJUUkFOU0lFTlRfRkVUQ0hfUkVTVUxUX1JFQ0VJVkVEIiwiRkVUQ0hfUkVTVUxUX1JFQ0VJVkVEIiwibW9kZWxQYXRoTGV2ZWxzIiwic3BsaXQiLCJwb3AiLCJqb2luIiwicmVwbGFjZVZhbHVlIiwiY2F1Z2h0RXJyb3IiLCJiYXNlRXJyb3JNZXNzYWdlIiwiaXNOZXR3b3JrT25saW5lIiwiZ2V0SXNPbmxpbmUiLCJlcnJvckRhdGEiLCJtZXNzYWdlIiwicGF0aCIsInRpdGxlIiwic3RhdHVzIiwiZGV0YWlsIiwiVFJZX0ZFVENIX0ZBSUxFRCIsIkJBRF9SRVFVRVNUIiwiSU5URVJOQUxfU0VSVkVSX0VSUk9SIiwiUkVRVUVTVF9USU1FT1VUIiwiVFJBTlNJRU5UX0ZFVENIX0ZBSUxFRCIsIkZFVENIX0ZBSUxFRCIsIlVOQVVUSE9SSVpFRCIsIm1vZGVsRmV0Y2hMb29wIiwiYWN0aW9uIiwiaGFzRmV0Y2hlZCIsInBlcmlvZCIsIlNVQ0NFRURFRCIsIm1vZGVsRmV0Y2hSZWN1cnJpbmciLCJiZ1N5bmNUYXNrIiwibW9kZWxGZXRjaFNhZ2EiLCJhcGlSb290UGFyYW0iLCJ0b2tlbkFjY2Vzc0Z1bmN0aW9uUGFyYW0iLCJhcmd1bWVudHMiLCJlcnJvckhhbmRsZXJQYXJhbSIsIkZFVENIX1JFUVVFU1QiLCJQRVJJT0RJQ19GRVRDSF9SRVFVRVNUIl0sInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3JlZHV4L3NhZ2FzL21vZGVsRmV0Y2hTYWdhLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFNhZ2FJdGVyYXRvciwgVGFzayB9IGZyb20gJ0ByZWR1eC1zYWdhL2NvcmUnXG5pbXBvcnQgeyBpc05pbCwgaXNQbGFpbk9iamVjdCwgbWVyZ2UgfSBmcm9tICdsb2Rhc2gnXG5pbXBvcnQgbW9tZW50IGZyb20gJ21vbWVudC10aW1lem9uZSdcbmltcG9ydCB7IEFueUFjdGlvbiB9IGZyb20gJ3JlZHV4J1xuaW1wb3J0IHsgY2FsbCwgY2FuY2VsLCBjYW5jZWxsZWQsIGRlbGF5LCBmb3JrLCBwdXQsIHRha2UsIHRha2VFdmVyeSB9IGZyb20gJ3JlZHV4LXNhZ2EvZWZmZWN0cydcbmltcG9ydCB7IG5ldHdvcmtPZmZsaW5lRXJyb3JEYXRhLCB1bmtub3duRXJyb3JEYXRhIH0gZnJvbSAnLi4vLi4vY29uc3RhbnRzL2ZldGNoRXJyb3JEYXRhJ1xuaW1wb3J0IHsgZ2V0TGFzdEFjdGl2aXR5RGF0ZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL2RhdGVTZXJ2aWNlJ1xuaW1wb3J0IHsgc2VuZEZldGNoLCBzZXRBcGlSb290IH0gZnJvbSAnLi4vLi4vc2VydmljZXMvZmV0Y2hTZXJ2aWNlJ1xuaW1wb3J0IHsgd2luZG93U2VydmljZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL3dpbmRvd1NlcnZpY2UnXG5pbXBvcnQge1xuXHRFbmRwb2ludE1hcHBpbmcsXG5cdEVuZHBvaW50TWFwcGluZ3MsXG5cdEVycm9ySGFuZGxlcixcblx0RmV0Y2hFcnJvckRhdGEsXG5cdEZldGNoUmVzdWx0LFxuXHRIVFRQX1NUQVRVU19DT0RFLFxuXHRNb2RlbCxcblx0TW9kZWxDb2xsZWN0aW9uLFxuXHRPQXV0aFRva2VuT3JOdWxsLFxuXHRUb2tlbkFjY2Vzc0Z1bmN0aW9uXG59IGZyb20gJy4uLy4uL3R5cGVzJ1xuaW1wb3J0IHsgY29uc3RydWN0RXJyb3JGcm9tQ2F1Z2h0RXJyb3IgfSBmcm9tICcuLi8uLi91dGlscy9lcnJvcidcbmltcG9ydCB7IGlzRmV0Y2hFcnJvckRhdGEsIHByZXBhcmVGZXRjaCwgUHJlcGFyZUZldGNoUmVzdWx0IH0gZnJvbSAnLi4vLi4vdXRpbHMvZmV0Y2gnXG5pbXBvcnQgeyBnZXRMb2dnZXIsIExvZ2dlciB9IGZyb20gJy4uLy4uL3V0aWxzL2xvZ2dlcidcbmltcG9ydCB7XG5cdE1PREVMX0ZFVENIX0VSUk9SX0FDVElPTl9UWVBFLFxuXHRNT0RFTF9GRVRDSF9SRVFVRVNUX0FDVElPTl9UWVBFLFxuXHRNT0RFTF9GRVRDSF9SRVNVTFRfQUNUSU9OX1RZUEUsXG5cdE1PREVMX0ZFVENIX1NUQVJUX0FDVElPTl9UWVBFLFxuXHRNT0RFTF9SRU1PVkVfS0VZX0FDVElPTl9UWVBFLFxuXHRNb2RlbEZldGNoRXJyb3JBY3Rpb24sXG5cdE1vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uLFxuXHRNb2RlbEZldGNoUmVzdWx0QWN0aW9uLFxuXHRNb2RlbEZldGNoU3RhcnRBY3Rpb24sXG5cdE1vZGVsUmVtb3ZlS2V5QWN0aW9uLFxuXHRQRVJJT0RJQ19NT0RFTF9GRVRDSF9URVJNSU5BVElPTl9BQ1RJT05fVFlQRSxcblx0UGVyaW9kaWNNb2RlbEZldGNoUmVxdWVzdEFjdGlvbixcblx0UGVyaW9kaWNNb2RlbEZldGNoVGVybWluYXRpb25BY3Rpb25cbn0gZnJvbSAnLi4vYWN0aW9ucydcblxuLyoqIFRoZSB0b3RhbCBudW1iZXIgb2YgdHJpZXMgZm9yIGBmZXRjaERhdGFgLCBpbmNsdWRpbmcgdGhlIGluaXRpYWwgcmVxdWVzdC4gKi9cbmV4cG9ydCBjb25zdCBUUllfTElNSVQgPSA1XG5cbi8vI3JlZ2lvbiBIZWxwZXJzXG5cbmV4cG9ydCBjb25zdCBtYXRjaGVzVGVybWluYXRpb25BY3Rpb24gPSAoXG5cdGluY29taW5nQWN0aW9uOiBBbnlBY3Rpb24sXG5cdGRhdGFSZXF1ZXN0QWN0aW9uOiBQZXJpb2RpY01vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uXG4pID0+XG5cdGluY29taW5nQWN0aW9uLnR5cGUgPT09IFBFUklPRElDX01PREVMX0ZFVENIX1RFUk1JTkFUSU9OX0FDVElPTl9UWVBFLlRFUk1JTkFURSAmJlxuXHRpbmNvbWluZ0FjdGlvbi50YXNrSWQgPT09IGRhdGFSZXF1ZXN0QWN0aW9uLnRhc2tJZFxuXG5leHBvcnQgY29uc3QgdGFrZU1hdGNoZXNUZXJtaW5hdGlvbkFjdGlvbiA9XG5cdChkYXRhUmVxdWVzdEFjdGlvbjogUGVyaW9kaWNNb2RlbEZldGNoUmVxdWVzdEFjdGlvbikgPT4gKGluY29taW5nQWN0aW9uOiBBbnlBY3Rpb24pID0+XG5cdFx0bWF0Y2hlc1Rlcm1pbmF0aW9uQWN0aW9uKGluY29taW5nQWN0aW9uLCBkYXRhUmVxdWVzdEFjdGlvbilcblxuLyogaXN0YW5idWwgaWdub3JlIG5leHQgKi9cbi8qIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSByZXF1aXJlLXlpZWxkICovXG5leHBvcnQgY29uc3QgZGVmYXVsdFRva2VuQWNjZXNzRnVuY3Rpb246IFRva2VuQWNjZXNzRnVuY3Rpb24gPSBmdW5jdGlvbiogKCkge1xuXHRyZXR1cm4gbnVsbFxufVxuXG4vKiBpc3RhbmJ1bCBpZ25vcmUgbmV4dCAqL1xuZXhwb3J0IGNvbnN0IGRlZmF1bHRFcnJvckhhbmRsZXI6IEVycm9ySGFuZGxlciA9ICgpID0+IHtcblx0cmV0dXJuXG59XG5cbmludGVyZmFjZSBHZW5lcmljTW9kZWwgZXh0ZW5kcyBNb2RlbCwgUmVjb3JkPHN0cmluZywgYW55PiB7fVxuXG5jb25zdCBnZXRDaGlsZENvbGxlY3Rpb25LZXlzID0gKGVuZHBvaW50TWFwcGluZzogRW5kcG9pbnRNYXBwaW5nKSA9PlxuXHRPYmplY3Qua2V5cyhlbmRwb2ludE1hcHBpbmcpLnJlZHVjZSgob3V0OiBzdHJpbmdbXSwga2V5KSA9PiB7XG5cdFx0Y29uc3QgdmFsdWUgPSBlbmRwb2ludE1hcHBpbmdba2V5XVxuXHRcdGlmICghdmFsdWUgfHwgIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh2YWx1ZSwgJ19jb25maWcnKSkge1xuXHRcdFx0cmV0dXJuIG91dFxuXHRcdH1cblx0XHRjb25zdCBlbSA9IHZhbHVlIGFzIEVuZHBvaW50TWFwcGluZ1xuXHRcdGlmICghIWVtLl9jb25maWcgJiYgISFlbS5fY29uZmlnLmlzQ29sbGVjdGlvbikge1xuXHRcdFx0b3V0LnB1c2goa2V5KVxuXHRcdH1cblx0XHRyZXR1cm4gb3V0XG5cdH0sIFtdKVxuXG5jb25zdCBjb252ZXJ0Q29sbGVjdGlvbnMgPSAoZW5kcG9pbnRNYXBwaW5nOiBFbmRwb2ludE1hcHBpbmcsIHByZXBhcmVGZXRjaFJlc3VsdDogUHJlcGFyZUZldGNoUmVzdWx0LCBkYXRhOiBhbnkpID0+IHtcblx0Y29uc3QgeyBlbmRwb2ludENvbmZpZywgZmV0Y2hDb25maWcsIGlzQ29sbGVjdGlvbkl0ZW1DcmVhdGUsIGlzQ29sbGVjdGlvbkl0ZW1GZXRjaCB9ID0gcHJlcGFyZUZldGNoUmVzdWx0XG5cdGNvbnN0IGNoaWxkQ29sbGVjdGlvbktleXMgPSBnZXRDaGlsZENvbGxlY3Rpb25LZXlzKGVuZHBvaW50TWFwcGluZylcblx0Y29uc3QgaGFzQ2hpbGRDb2xsZWN0aW9ucyA9IGNoaWxkQ29sbGVjdGlvbktleXMubGVuZ3RoID4gMFxuXHQvLyBubyBjb2xsZWN0aW9ucywgcmV0dXJuIGFzLWlzXG5cdGlmICghZW5kcG9pbnRDb25maWcuaXNDb2xsZWN0aW9uICYmICFoYXNDaGlsZENvbGxlY3Rpb25zKSB7XG5cdFx0cmV0dXJuIGRhdGFcblx0fVxuXHQvLyBkZWxldGUgcmV0dXJucyBubyBkYXRhXG5cdGlmIChmZXRjaENvbmZpZy5tZXRob2QgPT09ICdERUxFVEUnKSB7XG5cdFx0cmV0dXJuIHt9XG5cdH1cblx0Ly8gZGF0YSBpcyBhIGNvbGxlY3Rpb24gaXRlbSwgY29udmVydCBhbnkgY2hpbGQgY29sbGVjdGlvbnMgaW4gaXRlbSByZXNwb25zZVxuXHRpZiAoaXNDb2xsZWN0aW9uSXRlbUZldGNoIHx8IGlzQ29sbGVjdGlvbkl0ZW1DcmVhdGUpIHtcblx0XHRyZXR1cm4gY29udmVydERhdGFDb2xsZWN0aW9uSXRlbShkYXRhLCBjaGlsZENvbGxlY3Rpb25LZXlzLCBlbmRwb2ludE1hcHBpbmcpXG5cdH1cblx0Ly8gZGF0YSBpcyBhIGNvbGxlY3Rpb24sIGNvbnZlcnQgY29sbGVjdGlvbiByZXNwb25zZSwgYW5kIGFueSBjaGlsZCBjb2xsZWN0aW9ucyBpbiBlYWNoIGl0ZW1cblx0cmV0dXJuIGVuZHBvaW50Q29uZmlnLmlzQ29sbGVjdGlvblxuXHRcdD8gY29udmVydERhdGFUb0NvbGxlY3Rpb24oZGF0YSwgY2hpbGRDb2xsZWN0aW9uS2V5cywgZW5kcG9pbnRNYXBwaW5nKVxuXHRcdDogY29udmVydERhdGFDb2xsZWN0aW9uSXRlbShkYXRhLCBjaGlsZENvbGxlY3Rpb25LZXlzLCBlbmRwb2ludE1hcHBpbmcpXG59XG5cbmNvbnN0IGNvbnZlcnREYXRhQ29sbGVjdGlvbkl0ZW0gPSAoZGF0YTogYW55LCBjb2xsZWN0aW9uS2V5czogc3RyaW5nW10sIGVuZHBvaW50TWFwcGluZzogRW5kcG9pbnRNYXBwaW5nKSA9PiB7XG5cdGlmIChpc05pbChkYXRhKSkge1xuXHRcdHJldHVybiBkYXRhXG5cdH1cblx0cmV0dXJuIE9iamVjdC5rZXlzKGRhdGEpLnJlZHVjZSgob3V0OiBHZW5lcmljTW9kZWwsIGtleSkgPT4ge1xuXHRcdGxldCB2YWx1ZSA9IGRhdGFba2V5XVxuXHRcdGlmIChjb2xsZWN0aW9uS2V5cy5pbmNsdWRlcyhrZXkpKSB7XG5cdFx0XHRjb25zdCBjaGlsZEVuZHBvaW50TWFwcGluZyA9IGVuZHBvaW50TWFwcGluZ1trZXldIGFzIEVuZHBvaW50TWFwcGluZ1xuXHRcdFx0Y29uc3QgY2hpbGRDb2xsZWN0aW9uS2V5cyA9IGdldENoaWxkQ29sbGVjdGlvbktleXMoY2hpbGRFbmRwb2ludE1hcHBpbmcpXG5cdFx0XHR2YWx1ZSA9IGNvbnZlcnREYXRhVG9Db2xsZWN0aW9uKHZhbHVlLCBjaGlsZENvbGxlY3Rpb25LZXlzLCBjaGlsZEVuZHBvaW50TWFwcGluZylcblx0XHR9XG5cdFx0b3V0W2tleV0gPSB2YWx1ZVxuXHRcdHJldHVybiBvdXRcblx0fSwge30pXG59XG5cbmNvbnN0IGNvbnZlcnREYXRhVG9Db2xsZWN0aW9uID0gKGRhdGE6IGFueSwgY29sbGVjdGlvbktleXM6IHN0cmluZ1tdLCBlbmRwb2ludE1hcHBpbmc6IEVuZHBvaW50TWFwcGluZykgPT4ge1xuXHRpZiAoaXNOaWwoZGF0YSkpIHtcblx0XHRyZXR1cm4gZGF0YVxuXHR9XG5cdHJldHVybiBPYmplY3Qua2V5cyhkYXRhKS5yZWR1Y2UoKG91dDogTW9kZWxDb2xsZWN0aW9uPGFueT4sIGtleSkgPT4ge1xuXHRcdGNvbnN0IHZhbHVlID0gY29udmVydERhdGFDb2xsZWN0aW9uSXRlbShkYXRhW2tleV0sIGNvbGxlY3Rpb25LZXlzLCBlbmRwb2ludE1hcHBpbmcpXG5cdFx0LyogaXN0YW5idWwgaWdub3JlIGVsc2UgKi9cblx0XHRpZiAodmFsdWUgJiYgdmFsdWUuaWQpIHtcblx0XHRcdG91dFt2YWx1ZS5pZF0gPSB2YWx1ZVxuXHRcdH1cblx0XHRyZXR1cm4gb3V0XG5cdH0sIHt9KVxufVxuXG4vKipcbiAqIFVzaW5nIHRoZSBgbGFzdEFjdGl2aXR5RGF0ZWAgc2V0IGluIGxvY2FsIHN0b3JhZ2UgYnkgXCJzdHVkaW9raXQtY2FsaXBlci1qc1wiLFxuICogZGV0ZXJtaW5lIGlmIHRoZSB1c2VyIGhhcyBiZWVuIGlkbGUsIGUuZy4gaGFzIGhhZCBubyBhY3Rpdml0eSBpbiB0aGUgbGFzdCAxNSBtaW51dGVzLlxuICovXG5mdW5jdGlvbiBpc1VzZXJJZGxlKCkge1xuXHRjb25zdCBsYXN0QWN0aXZpdHlEYXRlU3RyaW5nID0gZ2V0TGFzdEFjdGl2aXR5RGF0ZSgpXG5cdGlmICghbGFzdEFjdGl2aXR5RGF0ZVN0cmluZykge1xuXHRcdHJldHVybiBmYWxzZVxuXHR9XG5cblx0Y29uc3QgbGFzdEFjdGl2aXR5RGF0ZVV0YyA9IG1vbWVudC51dGMobGFzdEFjdGl2aXR5RGF0ZVN0cmluZylcblx0Y29uc3Qgbm93VXRjID0gbW9tZW50LnV0YygpXG5cdGNvbnN0IGlkbGVNaW51dGVzVGhyZXNob2xkID0gMTAwMCAqIDYwICogMTUgLy8gMTUgbWludXRlc1xuXG5cdHJldHVybiBub3dVdGMuZGlmZihsYXN0QWN0aXZpdHlEYXRlVXRjKSA+PSBpZGxlTWludXRlc1RocmVzaG9sZFxufVxuXG4vLyNlbmRyZWdpb24gSGVscGVyc1xuXG4vLyNyZWdpb24gTG9jYWwgVmFyaWFibGVzXG5cbmxldCBlbmRwb2ludE1hcHBpbmdzOiBFbmRwb2ludE1hcHBpbmdzXG5sZXQgdG9rZW5BY2Nlc3NGdW5jdGlvbjogVG9rZW5BY2Nlc3NGdW5jdGlvblxubGV0IGVycm9ySGFuZGxlcjogRXJyb3JIYW5kbGVyXG5sZXQgbG9nZ2VyOiBMb2dnZXJcblxuLy8jZW5kcmVnaW9uIExvY2FsIFZhcmlhYmxlc1xuXG4vKiogU2V0IHRoZSBzaGFyZWQgRW5kcG9pbnRNYXBwaW5ncyB2YXJpYWJsZSwgZXhwb3NlZCBmb3IgdGVzdGFiaWxpdHkgKi9cbmV4cG9ydCBmdW5jdGlvbiBzZXRFbmRwb2ludE1hcHBpbmdzKGVuZHBvaW50TWFwcGluZ3NQYXJhbTogRW5kcG9pbnRNYXBwaW5ncykge1xuXHRlbmRwb2ludE1hcHBpbmdzID0gZW5kcG9pbnRNYXBwaW5nc1BhcmFtXG59XG5cbi8qKlxuICogQ29uc3RydWN0IGEgcmVxdWVzdCBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgZGF0YVJlcXVlc3RBY3Rpb24sIG1ha2UgYSByZXF1ZXN0IHdpdGggYSBjb25maWd1cmFibGUgcmV0cnksXG4gKiBhbmQgaGFuZGxlIGVycm9ycywgbG9nZ2luZyBhbmQgZGlzcGF0Y2hpbmcgYWxsIHN0ZXBzLlxuICpcbiAqIEBwYXJhbSBtb2RlbEZldGNoUmVxdWVzdEFjdGlvbiBBIG1vZGVsIGZldGNoIHJlcXVlc3QgYWN0aW9uIHdpdGggdGhlIHJlcXVlc3QgY29uZmlndXJhdGlvblxuICovXG5leHBvcnQgZnVuY3Rpb24qIG1vZGVsRmV0Y2goXG5cdG1vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uOiBNb2RlbEZldGNoUmVxdWVzdEFjdGlvbiB8IFBlcmlvZGljTW9kZWxGZXRjaFJlcXVlc3RBY3Rpb25cbik6IFNhZ2FJdGVyYXRvciB7XG5cdGxldCBwcmVwYXJlRmV0Y2hSZXN1bHQ6IFByZXBhcmVGZXRjaFJlc3VsdCB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZFxuXHR0cnkge1xuXHRcdHByZXBhcmVGZXRjaFJlc3VsdCA9IHByZXBhcmVGZXRjaChtb2RlbEZldGNoUmVxdWVzdEFjdGlvbiwgZW5kcG9pbnRNYXBwaW5ncylcblx0fSBjYXRjaCAoZXJyb3IpIHtcblx0XHQvLyBOb3RlOiBgcHJlcGFyZUZldGNoYCBhbHJlYWR5IGVuc3VyZXMgdGhhdCBgZXJyb3JgIGlzIGFuIGFjdHVhbCBgRXJyb3JgXG5cdFx0bG9nZ2VyLmVycm9yKGVycm9yKVxuXHRcdGVycm9ySGFuZGxlcihlcnJvciBhcyBFcnJvciwgbW9kZWxGZXRjaFJlcXVlc3RBY3Rpb24pXG5cdFx0cmV0dXJuXG5cdH1cblxuXHQvLyBnZXQgcHJlcGFyZWQgdmFyaWFibGVzXG5cdGNvbnN0IHsgbW9kZWxQYXRoLCBmZXRjaENvbmZpZywgZW5kcG9pbnRNYXBwaW5nLCBpc0NvbGxlY3Rpb25JdGVtQ3JlYXRlLCBpc0NvbGxlY3Rpb25JdGVtRGVsZXRlIH0gPVxuXHRcdHByZXBhcmVGZXRjaFJlc3VsdFxuXG5cdC8vIENvbmZpZ3VyZSByZXRyeVxuXHRjb25zdCB0cnlMaW1pdDogbnVtYmVyID0gbW9kZWxGZXRjaFJlcXVlc3RBY3Rpb24ubm9SZXRyeSA/IDEgOiBUUllfTElNSVRcblx0bGV0IHRyeUNvdW50ID0gMFxuXHRsZXQgZGlkRmFpbCA9IGZhbHNlXG5cdGxldCBsYXN0RmV0Y2hSZXN1bHQ6IEZldGNoUmVzdWx0IHwgdW5kZWZpbmVkXG5cdGxldCBsYXN0RXJyb3I6IEVycm9yIHwgdW5kZWZpbmVkXG5cdGxldCBsYXN0RXJyb3JEYXRhOiBGZXRjaEVycm9yRGF0YSB8IHVuZGVmaW5lZFxuXG5cdGNvbnN0IGZldGNoUmVzdWx0VW5kZWZpbmVkRXJyb3JNZXNzYWdlID0gXCInZmV0Y2hSZXN1bHQnIGlzIHVuZGVmaW5lZFwiXG5cdGNvbnN0IGZldGNoUmVzdWx0Tm90T2tFcnJvck1lc3NhZ2UgPSBcIidmZXRjaFJlc3VsdC5vaycgaXMgZmFsc2VcIlxuXG5cdC8vIFJ1biByZXRyeSBsb29wXG5cdGRvIHtcblx0XHRkaWRGYWlsID0gZmFsc2Vcblx0XHR0cnlDb3VudCsrXG5cblx0XHQvLyBkaXNwYXRjaCB0aGF0IHRoZSBmZXRjaCByZXF1ZXN0IHN0YXJ0ZWQsIHdoaWNoIHVwZGF0ZXMgYF9tZXRhZGF0YWAgYW5kIGBndWlkYCwgaWYgcHJvdmlkZWQsIGF0IHRoZSB0YXJnZXQgcmVkdXggYG1vZGVsUGF0aGBcblx0XHR5aWVsZCBwdXQ8TW9kZWxGZXRjaFN0YXJ0QWN0aW9uPih7XG5cdFx0XHR0eXBlOiBtb2RlbEZldGNoUmVxdWVzdEFjdGlvbi5ub1N0b3JlXG5cdFx0XHRcdD8gTU9ERUxfRkVUQ0hfU1RBUlRfQUNUSU9OX1RZUEUuVFJBTlNJRU5UX0ZFVENIX1NUQVJUXG5cdFx0XHRcdDogTU9ERUxfRkVUQ0hfU1RBUlRfQUNUSU9OX1RZUEUuRkVUQ0hfU1RBUlQsXG5cdFx0XHRtb2RlbFBhdGgsXG5cdFx0XHRndWlkOiBtb2RlbEZldGNoUmVxdWVzdEFjdGlvbi5ndWlkXG5cdFx0fSlcblxuXHRcdGxldCBmZXRjaFJlc3VsdDogRmV0Y2hSZXN1bHQgfCB1bmRlZmluZWQgPSB1bmRlZmluZWRcblx0XHR0cnkge1xuXHRcdFx0Ly8gZ2V0IHRoZSBPQXV0aCBUb2tlbiwgaWYgYW55LCBhbmQgY2FsbCBgZmV0Y2hgXG5cdFx0XHRjb25zdCBvYXV0aFRva2VuOiBPQXV0aFRva2VuT3JOdWxsID0geWllbGQgY2FsbCh0b2tlbkFjY2Vzc0Z1bmN0aW9uLCBtb2RlbEZldGNoUmVxdWVzdEFjdGlvbi5tb2RlbE5hbWUpXG5cdFx0XHRpZiAob2F1dGhUb2tlbj8uYWNjZXNzX3Rva2VuKSB7XG5cdFx0XHRcdGZldGNoQ29uZmlnLmhlYWRlcnMgPSBtZXJnZSh7fSwgZmV0Y2hDb25maWcuaGVhZGVycywge1xuXHRcdFx0XHRcdEF1dGhvcml6YXRpb246IGBCZWFyZXIgJHtvYXV0aFRva2VuLmFjY2Vzc190b2tlbn1gXG5cdFx0XHRcdH0pXG5cdFx0XHR9XG5cdFx0XHRmZXRjaFJlc3VsdCA9IHlpZWxkIGNhbGwoc2VuZEZldGNoLCBmZXRjaENvbmZpZylcblx0XHRcdC8vIHN0b3JlIGBmZXRjaFJlc3VsdGAgZm9yIHJlZmVyZW5jZSBhZnRlciB0aGUgZG8td2hpbGUgbG9vcFxuXHRcdFx0bGFzdEZldGNoUmVzdWx0ID0gZmV0Y2hSZXN1bHRcblxuXHRcdFx0Ly8gdGhyb3cgb24gdW5zdWNjZXNzZnVsIHJlc3VsdCB0byBzaG9ydC1jaXJjdWl0IGFuZCB0cmlnZ2VyIGNhdGNoK3JldHJ5XG5cdFx0XHRpZiAoIWZldGNoUmVzdWx0KSB0aHJvdyBuZXcgRXJyb3IoZmV0Y2hSZXN1bHRVbmRlZmluZWRFcnJvck1lc3NhZ2UpXG5cdFx0XHRpZiAoIWZldGNoUmVzdWx0Lm9rKSB0aHJvdyBuZXcgRXJyb3IoZmV0Y2hSZXN1bHROb3RPa0Vycm9yTWVzc2FnZSlcblxuXHRcdFx0Ly8gaGFuZGxlIHN1Y2Nlc3Ncblx0XHRcdGNvbnN0IHJlc3VsdEFjdGlvblR5cGUgPSBtb2RlbEZldGNoUmVxdWVzdEFjdGlvbi5ub1N0b3JlXG5cdFx0XHRcdD8gTU9ERUxfRkVUQ0hfUkVTVUxUX0FDVElPTl9UWVBFLlRSQU5TSUVOVF9GRVRDSF9SRVNVTFRfUkVDRUlWRURcblx0XHRcdFx0OiBNT0RFTF9GRVRDSF9SRVNVTFRfQUNUSU9OX1RZUEUuRkVUQ0hfUkVTVUxUX1JFQ0VJVkVEXG5cblx0XHRcdGNvbnN0IGRhdGEgPSBjb252ZXJ0Q29sbGVjdGlvbnMoZW5kcG9pbnRNYXBwaW5nLCBwcmVwYXJlRmV0Y2hSZXN1bHQsIGZldGNoUmVzdWx0LmRhdGEpXG5cdFx0XHQvLyBhdHRhY2ggZ3VpZCB0byByZXN1bHQgZGF0YSBpZiBpdCBpcyBhbiBvYmplY3Rcblx0XHRcdGlmIChtb2RlbEZldGNoUmVxdWVzdEFjdGlvbi5ndWlkICYmIGlzUGxhaW5PYmplY3QoZGF0YSkpIHtcblx0XHRcdFx0ZGF0YS5ndWlkID0gbW9kZWxGZXRjaFJlcXVlc3RBY3Rpb24uZ3VpZFxuXHRcdFx0fVxuXG5cdFx0XHQvLyBQT1NUIG5ldyBjb2xsZWN0aW9uIGl0ZW1cblx0XHRcdGlmIChpc0NvbGxlY3Rpb25JdGVtQ3JlYXRlKSB7XG5cdFx0XHRcdGNvbnN0IG1vZGVsUGF0aExldmVscyA9IG1vZGVsUGF0aC5zcGxpdCgnLicpXG5cdFx0XHRcdC8vIHJlbW92ZSBndWlkXG5cdFx0XHRcdG1vZGVsUGF0aExldmVscy5wb3AoKVxuXHRcdFx0XHQvLyBhZGQgYnkgbmV3IHJlc3VsdCdzIGlkXG5cdFx0XHRcdHlpZWxkIHB1dDxNb2RlbEZldGNoUmVzdWx0QWN0aW9uPih7XG5cdFx0XHRcdFx0dHlwZTogcmVzdWx0QWN0aW9uVHlwZSxcblx0XHRcdFx0XHRtb2RlbFBhdGg6IGAke21vZGVsUGF0aExldmVscy5qb2luKCcuJyl9LiR7ZGF0YS5pZH1gLFxuXHRcdFx0XHRcdGd1aWQ6IG1vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uLmd1aWQsXG5cdFx0XHRcdFx0cmVwbGFjZVZhbHVlOiBtb2RlbEZldGNoUmVxdWVzdEFjdGlvbi5yZXBsYWNlVmFsdWUsXG5cdFx0XHRcdFx0ZGF0YVxuXHRcdFx0XHR9KVxuXHRcdFx0XHQvLyByZW1vdmUgdGVtcCBpdGVtIHVuZGVyIGd1aWQga2V5XG5cdFx0XHRcdHlpZWxkIHB1dDxNb2RlbFJlbW92ZUtleUFjdGlvbj4oe1xuXHRcdFx0XHRcdHR5cGU6IE1PREVMX1JFTU9WRV9LRVlfQUNUSU9OX1RZUEUsXG5cdFx0XHRcdFx0bW9kZWxQYXRoOiBtb2RlbFBhdGhcblx0XHRcdFx0fSlcblx0XHRcdH1cblx0XHRcdC8vIERFTEVURSBjb2xsZWN0aW9uIGl0ZW1cblx0XHRcdGVsc2UgaWYgKGlzQ29sbGVjdGlvbkl0ZW1EZWxldGUpIHtcblx0XHRcdFx0eWllbGQgcHV0PE1vZGVsUmVtb3ZlS2V5QWN0aW9uPih7XG5cdFx0XHRcdFx0dHlwZTogTU9ERUxfUkVNT1ZFX0tFWV9BQ1RJT05fVFlQRSxcblx0XHRcdFx0XHRtb2RlbFBhdGg6IG1vZGVsUGF0aFxuXHRcdFx0XHR9KVxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0eWllbGQgcHV0PE1vZGVsRmV0Y2hSZXN1bHRBY3Rpb24+KHtcblx0XHRcdFx0XHR0eXBlOiByZXN1bHRBY3Rpb25UeXBlLFxuXHRcdFx0XHRcdG1vZGVsUGF0aDogbW9kZWxQYXRoLFxuXHRcdFx0XHRcdGd1aWQ6IG1vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uLmd1aWQsXG5cdFx0XHRcdFx0cmVwbGFjZVZhbHVlOiBtb2RlbEZldGNoUmVxdWVzdEFjdGlvbi5yZXBsYWNlVmFsdWUsXG5cdFx0XHRcdFx0ZGF0YVxuXHRcdFx0XHR9KVxuXHRcdFx0fVxuXHRcdH0gY2F0Y2ggKGNhdWdodEVycm9yOiB1bmtub3duKSB7XG5cdFx0XHQvLyBjb25zdHJ1Y3QgYEVycm9yYCB1c2luZyBgY2F1Z2h0RXJyb3JgXG5cdFx0XHRjb25zdCBiYXNlRXJyb3JNZXNzYWdlID0gYCdtb2RlbEZldGNoJyBmYWlsZWQgZm9yICcke21vZGVsUGF0aH0nYFxuXHRcdFx0Y29uc3QgZXJyb3IgPSBjb25zdHJ1Y3RFcnJvckZyb21DYXVnaHRFcnJvcihjYXVnaHRFcnJvciwgJ01vZGVsRmV0Y2hFcnJvcicsIGJhc2VFcnJvck1lc3NhZ2UpXG5cdFx0XHQvLyBjcmVhdGUgYSBkZWZhdWx0IGBlcnJvckRhdGFgIG9iamVjdFxuXHRcdFx0Y29uc3QgaXNOZXR3b3JrT25saW5lID0gd2luZG93U2VydmljZS5nZXRJc09ubGluZSgpXG5cdFx0XHRsZXQgZXJyb3JEYXRhOiBGZXRjaEVycm9yRGF0YSA9IGlzTmV0d29ya09ubGluZSA/IHVua25vd25FcnJvckRhdGEgOiBuZXR3b3JrT2ZmbGluZUVycm9yRGF0YVxuXHRcdFx0Ly8gdXBkYXRlIGBlcnJvckRhdGFgIGZyb20gdGhlIGBmZXRjaFJlc3VsdC5kYXRhYFxuXHRcdFx0Ly8gZm9yIHdoZW4gdGhlIGZldGNoIGdvdCBhIHJlc3VsdCwgYnV0IGl0IHdhcyBhbiB1bnN1Y2Nlc3NmdWwgc3RhdHVzXG5cdFx0XHRpZiAoZmV0Y2hSZXN1bHQ/LmRhdGEgJiYgaXNGZXRjaEVycm9yRGF0YShmZXRjaFJlc3VsdC5kYXRhKSkge1xuXHRcdFx0XHRlcnJvckRhdGEgPSBmZXRjaFJlc3VsdC5kYXRhXG5cdFx0XHRcdC8vIHVwZGF0ZSB0aGUgZXJyb3IncyBtZXNzYWdlIHRvIGluY2x1ZGUgaW5mbyBmcm9tIHRoZSBmZXRjaCByZXN1bHRcblx0XHRcdFx0ZXJyb3IubWVzc2FnZSA9IGAke2Jhc2VFcnJvck1lc3NhZ2V9LlxcbiR7ZmV0Y2hDb25maWcubWV0aG9kIHx8ICdHRVQnfSAke2ZldGNoQ29uZmlnLnBhdGh9IGZhaWxlZC5cXG4ke1xuXHRcdFx0XHRcdGVycm9yRGF0YS50aXRsZVxuXHRcdFx0XHR9ICgke2Vycm9yRGF0YS5zdGF0dXN9KWBcblx0XHRcdFx0aWYgKGVycm9yRGF0YS5kZXRhaWwpIHtcblx0XHRcdFx0XHRlcnJvci5tZXNzYWdlICs9IGA6ICR7ZXJyb3JEYXRhLmRldGFpbH1gXG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0Ly8gdHJhY2sgYGVycm9yRGF0YWAgYW5kIGBlcnJvcmAgZm9yIGFmdGVyIHRoZSB3aGlsZS1sb29wXG5cdFx0XHRsYXN0RXJyb3JEYXRhID0gZXJyb3JEYXRhXG5cdFx0XHRsYXN0RXJyb3IgPSBlcnJvclxuXG5cdFx0XHQvLyBkaXNwYXRjaCB0aGF0IHRoaXMgdHJ5IGZhaWxlZFxuXHRcdFx0eWllbGQgcHV0PE1vZGVsRmV0Y2hFcnJvckFjdGlvbj4oe1xuXHRcdFx0XHR0eXBlOiBNT0RFTF9GRVRDSF9FUlJPUl9BQ1RJT05fVFlQRS5UUllfRkVUQ0hfRkFJTEVELFxuXHRcdFx0XHRtb2RlbFBhdGg6IG1vZGVsUGF0aCxcblx0XHRcdFx0Z3VpZDogbW9kZWxGZXRjaFJlcXVlc3RBY3Rpb24uZ3VpZCxcblx0XHRcdFx0ZXJyb3JEYXRhXG5cdFx0XHR9KVxuXG5cdFx0XHQvLyBsb2cgdG8gdGhlIGNvbnNvbGVcblx0XHRcdGxvZ2dlci5lcnJvcihlcnJvcilcblxuXHRcdFx0ZGlkRmFpbCA9IHRydWVcblxuXHRcdFx0Ly8gRG8gbm90IHJldHJ5IGlmIHRoZSByZXNwb25zZSBpcyBiZXR3ZWVuIDQwMC01MDAgKGV4Y2VwdCA0MDg6IHJlcXVlc3QgdGltZW91dClcblx0XHRcdGlmIChcblx0XHRcdFx0ZXJyb3JEYXRhLnN0YXR1cyA+PSBIVFRQX1NUQVRVU19DT0RFLkJBRF9SRVFVRVNUICYmXG5cdFx0XHRcdGVycm9yRGF0YS5zdGF0dXMgPCBIVFRQX1NUQVRVU19DT0RFLklOVEVSTkFMX1NFUlZFUl9FUlJPUiAmJlxuXHRcdFx0XHRlcnJvckRhdGEuc3RhdHVzICE9PSBIVFRQX1NUQVRVU19DT0RFLlJFUVVFU1RfVElNRU9VVFxuXHRcdFx0KSB7XG5cdFx0XHRcdHRyeUNvdW50ID0gdHJ5TGltaXRcblx0XHRcdH1cblxuXHRcdFx0Ly8gaWYgdGhlcmUgYXJlIHRyaWVzIHJlbWFpbmluZywgcGVyZm9ybSBhbiBleHBvbmVudGlhbCBiYWNrb2ZmXG5cdFx0XHRpZiAodHJ5Q291bnQgPCB0cnlMaW1pdCkge1xuXHRcdFx0XHR5aWVsZCBkZWxheSgyICoqICh0cnlDb3VudCAtIDEpICogMTAwKSAvLyAxMDAsIDIwMCwgNDAwLCA4MDBcblx0XHRcdH1cblx0XHR9XG5cdH0gd2hpbGUgKHRyeUNvdW50IDwgdHJ5TGltaXQgJiYgZGlkRmFpbClcblxuXHQvLyBpZiBmZXRjaCBmYWlsZWQgY29tcGxldGVseSBhZnRlciBhbGwgcmV0cmllc1xuXHRpZiAodHJ5Q291bnQgPT09IHRyeUxpbWl0ICYmIGRpZEZhaWwpIHtcblx0XHQvLyB0aGVzZSB3aWxsIGFsd2F5cyBleGlzdFxuXHRcdGNvbnN0IGVycm9yRGF0YSA9IGxhc3RFcnJvckRhdGEgYXMgRmV0Y2hFcnJvckRhdGFcblx0XHRjb25zdCBlcnJvciA9IGxhc3RFcnJvciBhcyBFcnJvclxuXG5cdFx0Ly8gZGlzcGF0Y2ggdGhhdCB0aGUgZmV0Y2ggZmFpbGVkLCB3aGljaCB1cGRhdGVzIGBfbWV0YWRhdGFgIChhbmQgYGd1aWRgLCBpZiBwcm92aWRlZCkgYXQgdGhlIHRhcmdldCByZWR1eCBgbW9kZWxQYXRoYFxuXHRcdHlpZWxkIHB1dDxNb2RlbEZldGNoRXJyb3JBY3Rpb24+KHtcblx0XHRcdHR5cGU6IG1vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uLm5vU3RvcmVcblx0XHRcdFx0PyBNT0RFTF9GRVRDSF9FUlJPUl9BQ1RJT05fVFlQRS5UUkFOU0lFTlRfRkVUQ0hfRkFJTEVEXG5cdFx0XHRcdDogTU9ERUxfRkVUQ0hfRVJST1JfQUNUSU9OX1RZUEUuRkVUQ0hfRkFJTEVELFxuXHRcdFx0bW9kZWxQYXRoOiBtb2RlbFBhdGgsXG5cdFx0XHRndWlkOiBtb2RlbEZldGNoUmVxdWVzdEFjdGlvbi5ndWlkLFxuXHRcdFx0ZXJyb3JEYXRhXG5cdFx0fSlcblxuXHRcdC8vIFNlbmQgZXJyb3IgdG8gZXJyb3IgaGFuZGxlciwgZXhjZXB0IGZvciA0MDFzXG5cdFx0aWYgKGVycm9yRGF0YS5zdGF0dXMgIT09IEhUVFBfU1RBVFVTX0NPREUuVU5BVVRIT1JJWkVEKSB7XG5cdFx0XHRlcnJvckhhbmRsZXIoZXJyb3IsIG1vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uLCBmZXRjaENvbmZpZywgbGFzdEZldGNoUmVzdWx0LCBlcnJvckRhdGEpXG5cdFx0fVxuXHR9XG59XG5cbi8qKlxuICogVGhlIGxvb3Agc2FnYSB0aGF0IG1ha2VzIHRoZSByZXF1ZXN0IGV2ZXJ5IHthY3Rpb24ucGVyaW9kfSBtaWxsaXNlY29uZHMgdW50aWwgY2FuY2VsbGVkXG4gKlxuICogQHBhcmFtIGFjdGlvbiBBbiBhY3Rpb24gd2l0aCB0aGUgcmVxdWVzdCBjb25maWd1cmF0aW9uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiogbW9kZWxGZXRjaExvb3AoYWN0aW9uOiBQZXJpb2RpY01vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uKTogU2FnYUl0ZXJhdG9yIHtcblx0dHJ5IHtcblx0XHRsZXQgaGFzRmV0Y2hlZCA9IGZhbHNlXG5cdFx0ZG8ge1xuXHRcdFx0Ly8gY2FsbCB0aGUgZmV0Y2ggaWYgdGhpcyBpcyB0aGUgZmlyc3QgbG9vcCBpdGVyYXRpb24sXG5cdFx0XHQvLyBvciBpZiB0aGUgdXNlciBpcyBub3QgaWRsZVxuXHRcdFx0aWYgKCFoYXNGZXRjaGVkIHx8ICFpc1VzZXJJZGxlKCkpIHtcblx0XHRcdFx0eWllbGQgY2FsbChtb2RlbEZldGNoLCBhY3Rpb24pXG5cdFx0XHRcdGhhc0ZldGNoZWQgPSB0cnVlXG5cdFx0XHR9XG5cdFx0XHR5aWVsZCBkZWxheShhY3Rpb24ucGVyaW9kKVxuXHRcdH0gd2hpbGUgKHRydWUpXG5cdH0gY2F0Y2ggKGNhdWdodEVycm9yOiB1bmtub3duKSB7XG5cdFx0Y29uc3QgZXJyb3IgPSBjb25zdHJ1Y3RFcnJvckZyb21DYXVnaHRFcnJvcihjYXVnaHRFcnJvciwgJ01vZGVsRmV0Y2hMb29wRXJyb3InLCAnbW9kZWxGZXRjaExvb3AgZmFpbGVkJylcblx0XHRsb2dnZXIuZXJyb3IoZXJyb3IpXG5cdFx0Ly8gY2F0Y2ggYW5kIGxvZyBhbnkgdW5leHBlY3RlZCBlcnJvcnMgbm90IGNhdWdodCBpbnNpZGUgYGZldGNoRGF0YWBcblx0XHRlcnJvckhhbmRsZXIoZXJyb3IsIGFjdGlvbilcblx0fSBmaW5hbGx5IHtcblx0XHRpZiAoeWllbGQgY2FuY2VsbGVkKCkpIHtcblx0XHRcdHlpZWxkIHB1dDxQZXJpb2RpY01vZGVsRmV0Y2hUZXJtaW5hdGlvbkFjdGlvbj4oe1xuXHRcdFx0XHR0eXBlOiBQRVJJT0RJQ19NT0RFTF9GRVRDSF9URVJNSU5BVElPTl9BQ1RJT05fVFlQRS5TVUNDRUVERUQsXG5cdFx0XHRcdHRhc2tJZDogYWN0aW9uLnRhc2tJZFxuXHRcdFx0fSlcblx0XHR9XG5cdH1cbn1cblxuLyoqXG4gKiBDYWxsIHRoZSBmZXRjaERhdGEgc2FnYSBldmVyeSB7YWN0aW9uLnBlcmlvZH0gbWlsbGlzZWNvbmRzLiBUaGlzIHNhZ2EgcmVxdWlyZXMgdGhlICdwZXJpb2QnIGFuZCAndGFza0lkJyBwcm9wZXJ0aWVzXG4gKiBvbiB0aGUgYWN0aW9uIHBhcmFtZXRlci5cbiAqXG4gKiBAcGFyYW0gYWN0aW9uIEFuIGFjdGlvbiB3aXRoIHRoZSByZXF1ZXN0IGNvbmZpZ3VyYXRpb25cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uKiBtb2RlbEZldGNoUmVjdXJyaW5nKGFjdGlvbjogUGVyaW9kaWNNb2RlbEZldGNoUmVxdWVzdEFjdGlvbik6IFNhZ2FJdGVyYXRvciB7XG5cdGlmICghYWN0aW9uLnBlcmlvZCkge1xuXHRcdHRocm93IG5ldyBFcnJvcihcIidwZXJpb2QnIGlzIHJlcXVpcmVkXCIpXG5cdH1cblx0aWYgKCFhY3Rpb24udGFza0lkKSB7XG5cdFx0dGhyb3cgbmV3IEVycm9yKFwiJ3Rhc2tJZCcgaXMgcmVxdWlyZWRcIilcblx0fVxuXG5cdGNvbnN0IGJnU3luY1Rhc2s6IFRhc2sgPSB5aWVsZCBmb3JrKG1vZGVsRmV0Y2hMb29wLCBhY3Rpb24pXG5cdHlpZWxkIHRha2UodGFrZU1hdGNoZXNUZXJtaW5hdGlvbkFjdGlvbihhY3Rpb24pKVxuXHR5aWVsZCBjYW5jZWwoYmdTeW5jVGFzaylcbn1cblxuLyoqXG4gKiBUaGUgbWFpbiBzYWdhIGZvciBmZXRjaGluZyBtb2RlbHMuIE11c3QgYmUgaW5pdGlhbGl6ZWQgd2l0aCBhbiBFbmRwb2ludE1hcHBpbmdzIG9iamVjdCB0aGF0IGNhbiBiZSBmZXRjaGVkXG4gKiBhbmQgYW4gQVBJIHJvb3QgdG8gcHJlcGVuZCB0byBhbnkgcGFydGlhbCBVUkxzIHNwZWNpZmllZCBpbiB0aGUgRW5kcG9pbnRNYXBwaW5ncyBvYmplY3QuIEEgbG9nZ2VyIHNob3VsZCBub3JtYWxseSBiZSBwcm92aWRlZFxuICogYXMgd2VsbC5cbiAqXG4gKiBgRW5kcG9pbnRNYXBwaW5nc2Agb2JqZWN0IHJlcXVpcmUgYSBmb3JtIGFzIGZvbGxvd3MgKHdpdGggb3B0aW9uYWwgbmVzdGVkIG1vZGVscyk6XG4gKiBgYGBcbiAqIHtcbiAqIFx0ZnJ5TW9kZWw6IHtcbiAqIFx0XHRwYXRoOiAnL2FwaS9Gb28nXG4gKiBcdH0sXG4gKiBcdGdyb3VwT2ZNb2RlbHM6IHtcbiAqIFx0XHRsZWVsYU1vZGVsOiB7XG4gKiBcdFx0XHRwYXRoOiAnL2FwaS9CYXInXG4gKiBcdFx0fSxcbiAqIFx0XHRiZW5kZXJNb2RlbDoge1xuICogXHRcdFx0cGF0aDogJy9hcGkvQmF6J1xuICogXHRcdH1cbiAqIFx0fVxuICogfVxuICogYGBgXG4gKiBgRW5kcG9pbnRNYXBwaW5nYCBvYmplY3RzIGFyZSByZWZlcmVuY2VkIGluIHRoZSBhY3Rpb25zLkRBVEFfUkVRVUVTVEVEIGFjdGlvbiBieSBwYXRoLCBpLmUuXG4gKiBgeyB0eXBlOiBhY3Rpb25zLkRBVEFfUkVRVUVTVEVELCB7IG1vZGVsTmFtZTogJ2ZyeU1vZGVsJyB9IH1gXG4gKiAtLSBvciAtLVxuICogYHsgdHlwZTogYWN0aW9ucy5EQVRBX1JFUVVFU1RFRCwgeyBtb2RlbE5hbWU6ICdncm91cE9mTW9kZWxzLmxlZWxhTW9kZWwnIH0gfWBcbiAqXG4gKiBAZXhwb3J0XG4gKiBAcGFyYW0gZW5kcG9pbnRNYXBwaW5nc1BhcmFtIEEgbWFwcGluZyBvZiBBUEkgZW5kcG9pbnRzIGF2YWlsYWJsZSBpbiB0aGUgYXBwbGljYXRpb25cbiAqIEBwYXJhbSBhcGlSb290UGFyYW0gQSB1cmwgdG8gd2hpY2ggcGFydGlhbCBVUkxzIGFyZSBhcHBlbmRlZCAoaS5lLikgJ2h0dHBzOi8vbXlhcHAuY29tJ1xuICogQHBhcmFtIHRva2VuQWNjZXNzRnVuY3Rpb25QYXJhbSBmdW5jdGlvbiB0aGF0IHJldHVybnMgYW4gb3B0aW9uYWwgT0F1dGggdG9rZW5cbiAqIEBwYXJhbSBlcnJvckhhbmRsZXJQYXJhbSBBIGZ1bmN0aW9uIHRoYXQgaXMgY2FsbGVkIHdoZW4gYW55IGZldGNoIGV4Y2VwdGlvbnMgb2NjdXJcbiAqL1xuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24qIG1vZGVsRmV0Y2hTYWdhKFxuXHRlbmRwb2ludE1hcHBpbmdzUGFyYW06IEVuZHBvaW50TWFwcGluZ3MsXG5cdGFwaVJvb3RQYXJhbT86IHN0cmluZyxcblx0dG9rZW5BY2Nlc3NGdW5jdGlvblBhcmFtOiBUb2tlbkFjY2Vzc0Z1bmN0aW9uIHwgdW5kZWZpbmVkID0gZGVmYXVsdFRva2VuQWNjZXNzRnVuY3Rpb24sXG5cdGVycm9ySGFuZGxlclBhcmFtOiBFcnJvckhhbmRsZXIgfCB1bmRlZmluZWQgPSBkZWZhdWx0RXJyb3JIYW5kbGVyXG4pOiBTYWdhSXRlcmF0b3Ige1xuXHRzZXRBcGlSb290KGFwaVJvb3RQYXJhbSlcblx0c2V0RW5kcG9pbnRNYXBwaW5ncyhlbmRwb2ludE1hcHBpbmdzUGFyYW0pXG5cdGVycm9ySGFuZGxlciA9IGVycm9ySGFuZGxlclBhcmFtXG5cdHRva2VuQWNjZXNzRnVuY3Rpb24gPSB0b2tlbkFjY2Vzc0Z1bmN0aW9uUGFyYW1cblx0bG9nZ2VyID0gZ2V0TG9nZ2VyKClcblxuXHR5aWVsZCB0YWtlRXZlcnkoTU9ERUxfRkVUQ0hfUkVRVUVTVF9BQ1RJT05fVFlQRS5GRVRDSF9SRVFVRVNULCBtb2RlbEZldGNoKVxuXHR5aWVsZCB0YWtlRXZlcnkoTU9ERUxfRkVUQ0hfUkVRVUVTVF9BQ1RJT05fVFlQRS5QRVJJT0RJQ19GRVRDSF9SRVFVRVNULCBtb2RlbEZldGNoUmVjdXJyaW5nKVxufVxuIl0sIm1hcHBpbmdzIjoiQUFDQSxTQUFTQSxLQUFLLEVBQUVDLGFBQWEsRUFBRUMsS0FBSyxRQUFRLFFBQVE7QUFDcEQsT0FBT0MsTUFBTSxNQUFNLGlCQUFpQjtBQUVwQyxTQUFTQyxJQUFJLEVBQUVDLE1BQU0sRUFBRUMsU0FBUyxFQUFFQyxLQUFLLEVBQUVDLElBQUksRUFBRUMsR0FBRyxFQUFFQyxJQUFJLEVBQUVDLFNBQVMsUUFBUSxvQkFBb0I7QUFDL0YsU0FBU0MsdUJBQXVCLEVBQUVDLGdCQUFnQixRQUFRLGdDQUFnQztBQUMxRixTQUFTQyxtQkFBbUIsUUFBUSw0QkFBNEI7QUFDaEUsU0FBU0MsU0FBUyxFQUFFQyxVQUFVLFFBQVEsNkJBQTZCO0FBQ25FLFNBQVNDLGFBQWEsUUFBUSw4QkFBOEI7QUFDNUQsU0FNQ0MsZ0JBQWdCLFFBS1YsYUFBYTtBQUNwQixTQUFTQyw2QkFBNkIsUUFBUSxtQkFBbUI7QUFDakUsU0FBU0MsZ0JBQWdCLEVBQUVDLFlBQVksUUFBNEIsbUJBQW1CO0FBQ3RGLFNBQVNDLFNBQVMsUUFBZ0Isb0JBQW9CO0FBQ3RELFNBQ0NDLDZCQUE2QixFQUM3QkMsK0JB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