ngrx-loading-state
Version:
NgRx Loading State consistently manages loading actions such as API fetches.
224 lines • 30.7 kB
JavaScript
import { createSelector, on } from '@ngrx/store';
import { catchError, of } from 'rxjs';
import { cloneLoadingState, getNewFailureState, getNewLoadState, getNewSuccessState } from './loading-state-functions';
/**
* This class bundles up a set of load, success, and failure actions. It contains helpers to create
* reducers for these actions and helpers for creating selectors.
*
* Do not use this class directly, use the createLoadingActions() helper function.
*
*/
export class LoadingActions {
constructor(options) {
// Could have used createActionGroup() but the string literal typing of Source is giving me trouble. For now,
// just separate types.
this.load = options.load;
this.success = options.success;
this.failure = options.failure;
}
// ----------------------------------------------------------------------------
// Typing
// ----------------------------------------------------------------------------
/**
* Type guard to test if action is of type LoadingActions.load.
*
* @param action Any ngrx action
* @returns True if action if of type LoadingActions.load
*/
instanceOfLoad(action) {
return action.type === this.load.type;
}
/**
* Type guard to test if action is of type LoadingActions.success.
*
* @param action Any ngrx action
* @returns True if action if of type LoadingActions.success
*/
instanceOfSuccess(action) {
return action.type === this.success.type;
}
/**
* Type guard to test if action is of type LoadingActions.failure.
*
* @param action Any ngrx action
* @returns True if action if of type LoadingActions.failure
*/
instanceOfFailure(action) {
return action.type === this.failure.type;
}
// ------------------------------------------------------------------------------------------------
// Reducer
// ------------------------------------------------------------------------------------------------
/**
* Creates reducers for the load, success, failure actions.
*
* @param options.onLoad Call back when action is LoadAction. Return new copy of state if state needs to change.
* @param options.onSuccess Call back when action is SuccessAction. Return new copy of state if state needs to change.
* @param options.onFailure Call back when action is FailureAction. Return new copy of state if state needs to change.
* @returns A tuple of "on()" instances that handles load, success, failure actions in this order.
* @example
* export const reducer = createReducer(
* initialState,
* // Note: (1)
* ...fetchItem.reducer<ItemState>({
* onSuccess: (state, { item }): ItemState => {
* return { ...state, item };
* },
* // You can also customise what happens for LoadAction and FailureAction through onLoad and onFailure.
* // But most of the time, there's nothing to do for those. They are automatically handled by the fetchItem.reducer
* }),
* );
*
* (1) We need this explicit return type because "return type widening" means that the
* type constraints on the onSuccess function is only narrowing, so if you provide more
* fields in the return object that is not in VaultState, there is no error. Because
* the return value can be narrowed to the onSuccess function's constraints. So we have
* to explicitly specify the return type here. This is a well known issue that has been
* raise since at least 2016. So does not look like it will be fixed. Also, it's a recommended
* pattern in ngrx reducers to have this explicit typing.
*
* Ref https://github.com/microsoft/TypeScript/issues/241#issuecomment-327269994
*
* (2): Given that there are explicity return types on the onLoad, onSuccess, onFailure functions
* it should be possible to infer the type of the state here. But can't figure out how.
*
*/
reducer(options) {
const { onLoad, onSuccess, onFailure } = options || {};
return [
on(this.load, (state, action) => {
// Reducer must always create a new copy of the state.
const newState = {
...state,
loadingStates: this.setState(getNewLoadState, action, state.loadingStates)
};
// The updated loadingStates is passed to the user code for maximum
// flexibility in case the user wishes to change the loadingStates.
return (onLoad ? onLoad(newState, action) : newState);
}),
on(this.success, (state, action) => {
const newState = {
...state,
loadingStates: this.setState(getNewSuccessState, action, state.loadingStates)
};
return (onSuccess ? onSuccess(newState, action) : newState);
}),
on(this.failure, (state, action) => {
const newState = {
...state,
loadingStates: this.setState(getNewFailureState, action, state.loadingStates)
};
return (onFailure ? onFailure(newState, action) : newState);
})
];
}
/**
* Catches errors in effect.
*
* @returns rxjs operator that emits a FailureAction.
* @example
*
* fetchCount$ = createEffect(() => {
* return this.actions$.pipe(
* ofType(fetchCount.load),
* filterLoading(this.store.select(fetchCountSelectors.state)),
* switchMap((action) => {
* return apiCAll().pipe(
* map(item => fetchCount.success(item)),
* fetchCount.catchError()
* );
* })
* );
* });
*
*/
catchError() {
return catchError((error) => {
return of(
// AZ: Casting to "any" is less than ideal. But just can't figure out the complex typing here.
this.failure({
error
}));
});
}
// ----------------------------------------------------------------------------
// Selectors
// ----------------------------------------------------------------------------
/**
* Returns a map of selectors for loading, success, error, and the entire loading state. Similar to the ngrx entities adaptor.
*
* Design: The advantage of doing it in a bundle is that we can share the result of createStateSelector().
* If we had separate individual functions, each function might need to call createStateSelector()
* to create a new instance of the selector. We can't cache any created selectors because that will cause
* a memory leak since the cached references are always held in this class and hence does not get released.
*
* @param selectLoadingStates Selector that returns the loadingStats of the feature slice. You can use createLoadingStatesSelector()
* to create it.
* @returns A collection of selectors
* state: the LoadingState
* loading: True if loading
* success: True if last load was successful
* error: any errors from the last API call.
* @example
* // The feature slice selector. Standard ngrx stuff.
* const selectState = createFeatureSelector<SimpleState>(SIMPLE_FEATURE_KEY);
*
* // Selector that selects the loadingStates field from the global store. The createLoadingStatesSelector()
* // is provided as a part of this lib as well.
* const selectLoadingStates = createLoadingStatesSelector(selectState);
*
* // Create the selectors related to the fetchItem loading state.
* export const fetchItemSelectors = fetchItem.createSelectors(selectLoadingStates);
*
* // You can then observe the loading states:
* this.store.select(fetchItemSelectors.state); // The entire LoadingState
* this.store.select(fetchItemSelectors.success); // The boolean success flag
* this.store.select(fetchItemSelectors.loading); // The boolean loading flag
*
*/
createSelectors(selectLoadingStates) {
const state = createSelector(selectLoadingStates, (loadingStates) => {
return this.getLoadingState(loadingStates);
});
const loading = createSelector(state, (loadingState) => loadingState.loading);
const success = createSelector(state, (loadingState) => loadingState.success);
const error = createSelector(state, (loadingState) => loadingState.error);
return {
state,
loading,
success,
error
};
}
// ----------------------------------------------------------------------------
// Helpers
// ----------------------------------------------------------------------------
get key() {
return this.load.type;
}
getLoadingState(loadingStates) {
// We should not be modifying the state without going via the reducer, hence
// returning the immutable "init" object.
return loadingStates[this.key];
}
setState(getNewState, action, loadingStates) {
// We work with LoadingStateBase here to be generic. The idLoadingActions also
// use these functions.
const currentState = cloneLoadingState(this.getLoadingState(loadingStates));
const newState = getNewState(action, currentState);
if (newState) {
// Return new reference only when the state has changed.
return {
...loadingStates,
[this.key]: {
...newState
}
};
}
else {
// No change in state, so no change in parent state.
return loadingStates;
}
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9hZGluZy1zdGF0ZS1hY3Rpb25zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvbmdyeC1sb2FkaW5nLXN0YXRlL3NyYy9saWIvbG9hZGluZy1zdGF0ZS9sb2FkaW5nLXN0YXRlLWFjdGlvbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGNBQWMsRUFBd0MsRUFBRSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBRXZGLE9BQU8sRUFBRSxVQUFVLEVBQUUsRUFBRSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ3RDLE9BQU8sRUFDTCxpQkFBaUIsRUFDakIsa0JBQWtCLEVBQ2xCLGVBQWUsRUFDZixrQkFBa0IsRUFDbkIsTUFBTSwyQkFBMkIsQ0FBQztBQVduQzs7Ozs7O0dBTUc7QUFDSCxNQUFNLE9BQU8sY0FBYztJQVV6QixZQUFZLE9BSVg7UUFDQyw2R0FBNkc7UUFDN0csdUJBQXVCO1FBQ3ZCLElBQUksQ0FBQyxJQUFJLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQztRQUN6QixJQUFJLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUM7UUFDL0IsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDO0lBQ2pDLENBQUM7SUFFRCwrRUFBK0U7SUFDL0UsU0FBUztJQUNULCtFQUErRTtJQUMvRTs7Ozs7T0FLRztJQUNILGNBQWMsQ0FDWixNQUFjO1FBRWQsT0FBTyxNQUFNLENBQUMsSUFBSSxLQUFLLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDO0lBQ3hDLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNILGlCQUFpQixDQUFDLE1BQWM7UUFDOUIsT0FBTyxNQUFNLENBQUMsSUFBSSxLQUFLLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDO0lBQzNDLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNILGlCQUFpQixDQUNmLE1BQWM7UUFFZCxPQUFPLE1BQU0s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Q0FBQyxPQUFPLENBQUMsQ0FBQztRQUM5RSxNQUFNLEtBQUssR0FBRyxjQUFjLENBQUMsS0FBSyxFQUFFLENBQUMsWUFBWSxFQUFFLEVBQUUsQ0FBQyxZQUFZLENBQUMsS0FBSyxDQUFDLENBQUM7UUFFMUUsT0FBTztZQUNMLEtBQUs7WUFDTCxPQUFPO1lBQ1AsT0FBTztZQUNQLEtBQUs7U0FDTixDQUFDO0lBQ0osQ0FBQztJQUVELCtFQUErRTtJQUMvRSxVQUFVO0lBQ1YsK0VBQStFO0lBQy9FLElBQVksR0FBRztRQUNiLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUM7SUFDeEIsQ0FBQztJQUVPLGVBQWUsQ0FBQyxhQUFzQztRQUM1RCw0RUFBNEU7UUFDNUUseUNBQXlDO1FBQ3pDLE9BQU8sYUFBYSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUNqQyxDQUFDO0lBRU8sUUFBUSxDQUNkLFdBR2tDLEVBQ2xDLE1BQTJCLEVBQzNCLGFBQXNDO1FBRXRDLDhFQUE4RTtRQUM5RSx1QkFBdUI7UUFDdkIsTUFBTSxZQUFZLEdBQUcsaUJBQWlCLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDO1FBQzVFLE1BQU0sUUFBUSxHQUFHLFdBQVcsQ0FBQyxNQUFNLEVBQUUsWUFBWSxDQUFDLENBQUM7UUFFbkQsSUFBSSxRQUFRLEVBQUU7WUFDWix3REFBd0Q7WUFDeEQsT0FBTztnQkFDTCxHQUFHLGFBQWE7Z0JBQ2hCLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFO29CQUNWLEdBQUcsUUFBUTtpQkFDWjthQUNGLENBQUM7U0FDSDthQUFNO1lBQ0wsb0RBQW9EO1lBQ3BELE9BQU8sYUFBYSxDQUFDO1NBQ3RCO0lBQ0gsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgY3JlYXRlU2VsZWN0b3IsIERlZmF1bHRQcm9qZWN0b3JGbiwgTWVtb2l6ZWRTZWxlY3Rvciwgb24gfSBmcm9tICdAbmdyeC9zdG9yZSc7XG5pbXBvcnQgeyBBY3Rpb24sIFR5cGVkQWN0aW9uIH0gZnJvbSAnQG5ncngvc3RvcmUvc3JjL21vZGVscyc7XG5pbXBvcnQgeyBjYXRjaEVycm9yLCBvZiB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHtcbiAgY2xvbmVMb2FkaW5nU3RhdGUsXG4gIGdldE5ld0ZhaWx1cmVTdGF0ZSxcbiAgZ2V0TmV3TG9hZFN0YXRlLFxuICBnZXROZXdTdWNjZXNzU3RhdGVcbn0gZnJvbSAnLi9sb2FkaW5nLXN0YXRlLWZ1bmN0aW9ucyc7XG5pbXBvcnQge1xuICBBY3Rpb25GYWN0b3J5UmVzdWx0LFxuICBGYWlsdXJlQWN0aW9uLFxuICBMb2FkQWN0aW9uLFxuICBMb2FkaW5nQWN0aW9uc1JlZHVjZXJUeXBlcyxcbiAgTG9hZGluZ1N0YXRlLFxuICBMb2FkaW5nU3RhdGVzLFxuICBPblN0YXRlXG59IGZyb20gJy4vbG9hZGluZy1zdGF0ZS10eXBlcyc7XG5cbi8qKlxuICogVGhpcyBjbGFzcyBidW5kbGVzIHVwIGEgc2V0IG9mIGxvYWQsIHN1Y2Nlc3MsIGFuZCBmYWlsdXJlIGFjdGlvbnMuIEl0IGNvbnRhaW5zIGhlbHBlcnMgdG8gY3JlYXRlXG4gKiByZWR1Y2VycyBmb3IgdGhlc2UgYWN0aW9ucyBhbmQgaGVscGVycyBmb3IgY3JlYXRpbmcgc2VsZWN0b3JzLlxuICpcbiAqIERvIG5vdCB1c2UgdGhpcyBjbGFzcyBkaXJlY3RseSwgdXNlIHRoZSBjcmVhdGVMb2FkaW5nQWN0aW9ucygpIGhlbHBlciBmdW5jdGlvbi5cbiAqXG4gKi9cbmV4cG9ydCBjbGFzcyBMb2FkaW5nQWN0aW9uczxcbiAgTG9hZFBheWxvYWRUeXBlIGV4dGVuZHMgb2JqZWN0LFxuICBTdWNjZXNzUGF5bG9hZFR5cGUgZXh0ZW5kcyBvYmplY3QsXG4gIEZhaWx1cmVQYXlsb2FkVHlwZSBleHRlbmRzIG9iamVjdFxuPiB7XG4gIC8qKiBUaGUgYWN0aW9ucyB0aGUgdXNlciBjYW4gZGlzcGF0Y2ggKi9cbiAgcmVhZG9ubHkgbG9hZDogQWN0aW9uRmFjdG9yeVJlc3VsdDxMb2FkQWN0aW9uICYgTG9hZFBheWxvYWRUeXBlPjtcbiAgcmVhZG9ubHkgc3VjY2VzczogQWN0aW9uRmFjdG9yeVJlc3VsdDxTdWNjZXNzUGF5bG9hZFR5cGU+O1xuICByZWFkb25seSBmYWlsdXJlOiBBY3Rpb25GYWN0b3J5UmVzdWx0PEZhaWx1cmVBY3Rpb24gJiBGYWlsdXJlUGF5bG9hZFR5cGU+O1xuXG4gIGNvbnN0cnVjdG9yKG9wdGlvbnM6IHtcbiAgICBsb2FkOiBBY3Rpb25GYWN0b3J5UmVzdWx0PExvYWRBY3Rpb24gJiBMb2FkUGF5bG9hZFR5cGU+O1xuICAgIHN1Y2Nlc3M6IEFjdGlvbkZhY3RvcnlSZXN1bHQ8U3VjY2Vzc1BheWxvYWRUeXBlPjtcbiAgICBmYWlsdXJlOiBBY3Rpb25GYWN0b3J5UmVzdWx0PEZhaWx1cmVBY3Rpb24gJiBGYWlsdXJlUGF5bG9hZFR5cGU+O1xuICB9KSB7XG4gICAgLy8gQ291bGQgaGF2ZSB1c2VkIGNyZWF0ZUFjdGlvbkdyb3VwKCkgYnV0IHRoZSBzdHJpbmcgbGl0ZXJhbCB0eXBpbmcgb2YgU291cmNlIGlzIGdpdmluZyBtZSB0cm91YmxlLiBGb3Igbm93LFxuICAgIC8vIGp1c3Qgc2VwYXJhdGUgdHlwZXMuXG4gICAgdGhpcy5sb2FkID0gb3B0aW9ucy5sb2FkO1xuICAgIHRoaXMuc3VjY2VzcyA9IG9wdGlvbnMuc3VjY2VzcztcbiAgICB0aGlzLmZhaWx1cmUgPSBvcHRpb25zLmZhaWx1cmU7XG4gIH1cblxuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gIC8vIFR5cGluZ1xuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gIC8qKlxuICAgKiBUeXBlIGd1YXJkIHRvIHRlc3QgaWYgYWN0aW9uIGlzIG9mIHR5cGUgTG9hZGluZ0FjdGlvbnMubG9hZC5cbiAgICpcbiAgICogQHBhcmFtIGFjdGlvbiBBbnkgbmdyeCBhY3Rpb25cbiAgICogQHJldHVybnMgVHJ1ZSBpZiBhY3Rpb24gaWYgb2YgdHlwZSBMb2FkaW5nQWN0aW9ucy5sb2FkXG4gICAqL1xuICBpbnN0YW5jZU9mTG9hZChcbiAgICBhY3Rpb246IEFjdGlvblxuICApOiBhY3Rpb24gaXMgUmV0dXJuVHlwZTxBY3Rpb25GYWN0b3J5UmVzdWx0PExvYWRBY3Rpb24gJiBMb2FkUGF5bG9hZFR5cGU+PiB7XG4gICAgcmV0dXJuIGFjdGlvbi50eXBlID09PSB0aGlzLmxvYWQudHlwZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUeXBlIGd1YXJkIHRvIHRlc3QgaWYgYWN0aW9uIGlzIG9mIHR5cGUgTG9hZGluZ0FjdGlvbnMuc3VjY2Vzcy5cbiAgICpcbiAgICogQHBhcmFtIGFjdGlvbiBBbnkgbmdyeCBhY3Rpb25cbiAgICogQHJldHVybnMgVHJ1ZSBpZiBhY3Rpb24gaWYgb2YgdHlwZSBMb2FkaW5nQWN0aW9ucy5zdWNjZXNzXG4gICAqL1xuICBpbnN0YW5jZU9mU3VjY2VzcyhhY3Rpb246IEFjdGlvbik6IGFjdGlvbiBpcyBSZXR1cm5UeXBlPEFjdGlvbkZhY3RvcnlSZXN1bHQ8U3VjY2Vzc1BheWxvYWRUeXBlPj4ge1xuICAgIHJldHVybiBhY3Rpb24udHlwZSA9PT0gdGhpcy5zdWNjZXNzLnR5cGU7XG4gIH1cblxuICAvKipcbiAgICogVHlwZSBndWFyZCB0byB0ZXN0IGlmIGFjdGlvbiBpcyBvZiB0eXBlIExvYWRpbmdBY3Rpb25zLmZhaWx1cmUuXG4gICAqXG4gICAqIEBwYXJhbSBhY3Rpb24gQW55IG5ncnggYWN0aW9uXG4gICAqIEByZXR1cm5zIFRydWUgaWYgYWN0aW9uIGlmIG9mIHR5cGUgTG9hZGluZ0FjdGlvbnMuZmFpbHVyZVxuICAgKi9cbiAgaW5zdGFuY2VPZkZhaWx1cmUoXG4gICAgYWN0aW9uOiBBY3Rpb25cbiAgKTogYWN0aW9uIGlzIFJldHVyblR5cGU8QWN0aW9uRmFjdG9yeVJlc3VsdDxGYWlsdXJlQWN0aW9uICYgRmFpbHVyZVBheWxvYWRUeXBlPj4ge1xuICAgIHJldHVybiBhY3Rpb24udHlwZSA9PT0gdGhpcy5mYWlsdXJlLnR5cGU7XG4gIH1cblxuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgLy8gUmVkdWNlclxuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgLyoqXG4gICAqIENyZWF0ZXMgcmVkdWNlcnMgZm9yIHRoZSBsb2FkLCBzdWNjZXNzLCBmYWlsdXJlIGFjdGlvbnMuXG4gICAqXG4gICAqIEBwYXJhbSBvcHRpb25zLm9uTG9hZCBDYWxsIGJhY2sgd2hlbiBhY3Rpb24gaXMgTG9hZEFjdGlvbi4gUmV0dXJuIG5ldyBjb3B5IG9mIHN0YXRlIGlmIHN0YXRlIG5lZWRzIHRvIGNoYW5nZS5cbiAgICogQHBhcmFtIG9wdGlvbnMub25TdWNjZXNzIENhbGwgYmFjayB3aGVuIGFjdGlvbiBpcyBTdWNjZXNzQWN0aW9uLiBSZXR1cm4gbmV3IGNvcHkgb2Ygc3RhdGUgaWYgc3RhdGUgbmVlZHMgdG8gY2hhbmdlLlxuICAgKiBAcGFyYW0gb3B0aW9ucy5vbkZhaWx1cmUgQ2FsbCBiYWNrIHdoZW4gYWN0aW9uIGlzIEZhaWx1cmVBY3Rpb24uIFJldHVybiBuZXcgY29weSBvZiBzdGF0ZSBpZiBzdGF0ZSBuZWVkcyB0byBjaGFuZ2UuXG4gICAqIEByZXR1cm5zIEEgdHVwbGUgb2YgXCJvbigpXCIgaW5zdGFuY2VzIHRoYXQgaGFuZGxlcyBsb2FkLCBzdWNjZXNzLCBmYWlsdXJlIGFjdGlvbnMgaW4gdGhpcyBvcmRlci5cbiAgICogQGV4YW1wbGVcbiAgICogICBleHBvcnQgY29uc3QgcmVkdWNlciA9IGNyZWF0ZVJlZHVjZXIoXG4gICAqICAgICBpbml0aWFsU3RhdGUsXG4gICAqICAgICAgICAgICAgICAgICAgICAvLyBOb3RlOiAoMSlcbiAgICogICAgIC4uLmZldGNoSXRlbS5yZWR1Y2VyPEl0ZW1TdGF0ZT4oe1xuICAgKiAgICAgICBvblN1Y2Nlc3M6IChzdGF0ZSwgeyBpdGVtIH0pOiBJdGVtU3RhdGUgPT4ge1xuICAgKiAgICAgICAgIHJldHVybiB7IC4uLnN0YXRlLCBpdGVtIH07XG4gICAqICAgICAgIH0sXG4gICAqICAgICAgIC8vIFlvdSBjYW4gYWxzbyBjdXN0b21pc2Ugd2hhdCBoYXBwZW5zIGZvciBMb2FkQWN0aW9uIGFuZCBGYWlsdXJlQWN0aW9uIHRocm91Z2ggb25Mb2FkIGFuZCBvbkZhaWx1cmUuXG4gICAqICAgICAgIC8vIEJ1dCBtb3N0IG9mIHRoZSB0aW1lLCB0aGVyZSdzIG5vdGhpbmcgdG8gZG8gZm9yIHRob3NlLiBUaGV5IGFyZSBhdXRvbWF0aWNhbGx5IGhhbmRsZWQgYnkgdGhlIGZldGNoSXRlbS5yZWR1Y2VyXG4gICAqICAgICB9KSxcbiAgICogICApO1xuICAgKlxuICAgKiAoMSkgV2UgbmVlZCB0aGlzIGV4cGxpY2l0IHJldHVybiB0eXBlIGJlY2F1c2UgXCJyZXR1cm4gdHlwZSB3aWRlbmluZ1wiIG1lYW5zIHRoYXQgdGhlXG4gICAqIHR5cGUgY29uc3RyYWludHMgb24gdGhlIG9uU3VjY2VzcyBmdW5jdGlvbiBpcyBvbmx5IG5hcnJvd2luZywgc28gaWYgeW91IHByb3ZpZGUgbW9yZVxuICAgKiBmaWVsZHMgaW4gdGhlIHJldHVybiBvYmplY3QgdGhhdCBpcyBub3QgaW4gVmF1bHRTdGF0ZSwgdGhlcmUgaXMgbm8gZXJyb3IuIEJlY2F1c2VcbiAgICogdGhlIHJldHVybiB2YWx1ZSBjYW4gYmUgbmFycm93ZWQgdG8gdGhlIG9uU3VjY2VzcyBmdW5jdGlvbidzIGNvbnN0cmFpbnRzLiBTbyB3ZSBoYXZlXG4gICAqIHRvIGV4cGxpY2l0bHkgc3BlY2lmeSB0aGUgcmV0dXJuIHR5cGUgaGVyZS4gVGhpcyBpcyBhIHdlbGwga25vd24gaXNzdWUgdGhhdCBoYXMgYmVlblxuICAgKiByYWlzZSBzaW5jZSBhdCBsZWFzdCAyMDE2LiBTbyBkb2VzIG5vdCBsb29rIGxpa2UgaXQgd2lsbCBiZSBmaXhlZC4gQWxzbywgaXQncyBhIHJlY29tbWVuZGVkXG4gICAqIHBhdHRlcm4gaW4gbmdyeCByZWR1Y2VycyB0byBoYXZlIHRoaXMgZXhwbGljaXQgdHlwaW5nLlxuICAgKlxuICAgKiBSZWYgaHR0cHM6Ly9naXRodWIuY29tL21pY3Jvc29mdC9UeXBlU2NyaXB0L2lzc3Vlcy8yNDEjaXNzdWVjb21tZW50LTMyNzI2OTk5NFxuICAgKlxuICAgKiAoMik6IEdpdmVuIHRoYXQgdGhlcmUgYXJlIGV4cGxpY2l0eSByZXR1cm4gdHlwZXMgb24gdGhlIG9uTG9hZCwgb25TdWNjZXNzLCBvbkZhaWx1cmUgZnVuY3Rpb25zXG4gICAqIGl0IHNob3VsZCBiZSBwb3NzaWJsZSB0byBpbmZlciB0aGUgdHlwZSBvZiB0aGUgc3RhdGUgaGVyZS4gQnV0IGNhbid0IGZpZ3VyZSBvdXQgaG93LlxuICAgKlxuICAgKi9cbiAgcmVkdWNlcjxTdGF0ZSBleHRlbmRzIHsgbG9hZGluZ1N0YXRlczogTG9hZGluZ1N0YXRlcyB9PihvcHRpb25zPzoge1xuICAgIG9uTG9hZD86IChcbiAgICAgIHN0YXRlOiBPblN0YXRlPFN0YXRlPixcbiAgICAgIGFjdGlvbjogTG9hZEFjdGlvbiAmIExvYWRQYXlsb2FkVHlwZSAmIFR5cGVkQWN0aW9uPHN0cmluZz5cbiAgICApID0+IFN0YXRlO1xuICAgIG9uU3VjY2Vzcz86IChzdGF0ZTogT25TdGF0ZTxTdGF0ZT4sIGFjdGlvbjogU3VjY2Vzc1BheWxvYWRUeXBlICYgVHlwZWRBY3Rpb248c3RyaW5nPikgPT4gU3RhdGU7XG4gICAgb25GYWlsdXJlPzogKFxuICAgICAgc3RhdGU6IE9uU3RhdGU8U3RhdGU+LFxuICAgICAgYWN0aW9uOiBGYWlsdXJlQWN0aW9uICYgRmFpbHVyZVBheWxvYWRUeXBlICYgVHlwZWRBY3Rpb248c3RyaW5nPlxuICAgICkgPT4gU3RhdGU7XG4gIH0pOiBbXG4gICAgTG9hZGluZ0FjdGlvbnNSZWR1Y2VyVHlwZXM8U3RhdGU+LFxuICAgIExvYWRpbmdBY3Rpb25zUmVkdWNlclR5cGVzPFN0YXRlPixcbiAgICBMb2FkaW5nQWN0aW9uc1JlZHVjZXJUeXBlczxTdGF0ZT5cbiAgXSB7XG4gICAgY29uc3QgeyBvbkxvYWQsIG9uU3VjY2Vzcywgb25GYWlsdXJlIH0gPSBvcHRpb25zIHx8IHt9O1xuICAgIHJldHVybiBbXG4gICAgICBvbih0aGlzLmxvYWQsIChzdGF0ZSwgYWN0aW9uKSA9PiB7XG4gICAgICAgIC8vIFJlZHVjZXIgbXVzdCBhbHdheXMgY3JlYXRlIGEgbmV3IGNvcHkgb2YgdGhlIHN0YXRlLlxuICAgICAgICBjb25zdCBuZXdTdGF0ZSA9IHtcbiAgICAgICAgICAuLi5zdGF0ZSxcbiAgICAgICAgICBsb2FkaW5nU3RhdGVzOiB0aGlzLnNldFN0YXRlKGdldE5ld0xvYWRTdGF0ZSwgYWN0aW9uLCBzdGF0ZS5sb2FkaW5nU3RhdGVzKVxuICAgICAgICB9O1xuXG4gICAgICAgIC8vIFRoZSB1cGRhdGVkIGxvYWRpbmdTdGF0ZXMgaXMgcGFzc2VkIHRvIHRoZSB1c2VyIGNvZGUgZm9yIG1heGltdW1cbiAgICAgICAgLy8gZmxleGliaWxpdHkgaW4gY2FzZSB0aGUgdXNlciB3aXNoZXMgdG8gY2hhbmdlIHRoZSBsb2FkaW5nU3RhdGVzLlxuICAgICAgICByZXR1cm4gKG9uTG9hZCA/IG9uTG9hZChuZXdTdGF0ZSwgYWN0aW9uKSA6IG5ld1N0YXRlKSBhcyBPblN0YXRlPFN0YXRlPjtcbiAgICAgIH0pLFxuICAgICAgb24odGhpcy5zdWNjZXNzLCAoc3RhdGUsIGFjdGlvbikgPT4ge1xuICAgICAgICBjb25zdCBuZXdTdGF0ZSA9IHtcbiAgICAgICAgICAuLi5zdGF0ZSxcbiAgICAgICAgICBsb2FkaW5nU3RhdGVzOiB0aGlzLnNldFN0YXRlKGdldE5ld1N1Y2Nlc3NTdGF0ZSwgYWN0aW9uLCBzdGF0ZS5sb2FkaW5nU3RhdGVzKVxuICAgICAgICB9O1xuXG4gICAgICAgIHJldHVybiAob25TdWNjZXNzID8gb25TdWNjZXNzKG5ld1N0YXRlLCBhY3Rpb24pIDogbmV3U3RhdGUpIGFzIE9uU3RhdGU8U3RhdGU+O1xuICAgICAgfSksXG4gICAgICBvbih0aGlzLmZhaWx1cmUsIChzdGF0ZSwgYWN0aW9uKSA9PiB7XG4gICAgICAgIGNvbnN0IG5ld1N0YXRlID0ge1xuICAgICAgICAgIC4uLnN0YXRlLFxuICAgICAgICAgIGxvYWRpbmdTdGF0ZXM6IHRoaXMuc2V0U3RhdGUoZ2V0TmV3RmFpbHVyZVN0YXRlLCBhY3Rpb24sIHN0YXRlLmxvYWRpbmdTdGF0ZXMpXG4gICAgICAgIH07XG5cbiAgICAgICAgcmV0dXJuIChvbkZhaWx1cmUgPyBvbkZhaWx1cmUobmV3U3RhdGUsIGFjdGlvbikgOiBuZXdTdGF0ZSkgYXMgT25TdGF0ZTxTdGF0ZT47XG4gICAgICB9KVxuICAgIF07XG4gIH1cblxuICAvKipcbiAgICogQ2F0Y2hlcyBlcnJvcnMgaW4gZWZmZWN0LlxuICAgKlxuICAgKiBAcmV0dXJucyByeGpzIG9wZXJhdG9yIHRoYXQgZW1pdHMgYSBGYWlsdXJlQWN0aW9uLlxuICAgKiBAZXhhbXBsZVxuICAgKlxuICAgKiAgZmV0Y2hDb3VudCQgPSBjcmVhdGVFZmZlY3QoKCkgPT4ge1xuICAgKiAgICByZXR1cm4gdGhpcy5hY3Rpb25zJC5waXBlKFxuICAgKiAgICAgIG9mVHlwZShmZXRjaENvdW50LmxvYWQpLFxuICAgKiAgICAgIGZpbHRlckxvYWRpbmcodGhpcy5zdG9yZS5zZWxlY3QoZmV0Y2hDb3VudFNlbGVjdG9ycy5zdGF0ZSkpLFxuICAgKiAgICAgIHN3aXRjaE1hcCgoYWN0aW9uKSA9PiB7XG4gICAqICAgICAgICByZXR1cm4gYXBpQ0FsbCgpLnBpcGUoXG4gICAqICAgICAgICAgIG1hcChpdGVtID0+IGZldGNoQ291bnQuc3VjY2VzcyhpdGVtKSksXG4gICAqICAgICAgICAgIGZldGNoQ291bnQuY2F0Y2hFcnJvcigpXG4gICAqICAgICAgICApO1xuICAgKiAgICAgIH0pXG4gICAqICAgICk7XG4gICAqICB9KTtcbiAgICpcbiAgICovXG4gIGNhdGNoRXJyb3IoKTogUmV0dXJuVHlwZTx0eXBlb2YgY2F0Y2hFcnJvcj4ge1xuICAgIHJldHVybiBjYXRjaEVycm9yKChlcnJvcikgPT4ge1xuICAgICAgcmV0dXJuIG9mKFxuICAgICAgICAvLyBBWjogQ2FzdGluZyB0byBcImFueVwiIGlzIGxlc3MgdGhhbiBpZGVhbC4gQnV0IGp1c3QgY2FuJ3QgZmlndXJlIG91dCB0aGUgY29tcGxleCB0eXBpbmcgaGVyZS5cbiAgICAgICAgdGhpcy5mYWlsdXJlKHtcbiAgICAgICAgICBlcnJvclxuICAgICAgICB9IGFzIGFueSlcbiAgICAgICk7XG4gICAgfSk7XG4gIH1cblxuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gIC8vIFNlbGVjdG9yc1xuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gIC8qKlxuICAgKiBSZXR1cm5zIGEgbWFwIG9mIHNlbGVjdG9ycyBmb3IgbG9hZGluZywgc3VjY2VzcywgZXJyb3IsIGFuZCB0aGUgZW50aXJlIGxvYWRpbmcgc3RhdGUuIFNpbWlsYXIgdG8gdGhlIG5ncnggZW50aXRpZXMgYWRhcHRvci5cbiAgICpcbiAgICogRGVzaWduOiBUaGUgYWR2YW50YWdlIG9mIGRvaW5nIGl0IGluIGEgYnVuZGxlIGlzIHRoYXQgd2UgY2FuIHNoYXJlIHRoZSByZXN1bHQgb2YgY3JlYXRlU3RhdGVTZWxlY3RvcigpLlxuICAgKiBJZiB3ZSBoYWQgc2VwYXJhdGUgaW5kaXZpZHVhbCBmdW5jdGlvbnMsIGVhY2ggZnVuY3Rpb24gbWlnaHQgbmVlZCB0byBjYWxsIGNyZWF0ZVN0YXRlU2VsZWN0b3IoKVxuICAgKiB0byBjcmVhdGUgYSBuZXcgaW5zdGFuY2Ugb2YgdGhlIHNlbGVjdG9yLiBXZSBjYW4ndCBjYWNoZSBhbnkgY3JlYXRlZCBzZWxlY3RvcnMgYmVjYXVzZSB0aGF0IHdpbGwgY2F1c2VcbiAgICogYSBtZW1vcnkgbGVhayBzaW5jZSB0aGUgY2FjaGVkIHJlZmVyZW5jZXMgYXJlIGFsd2F5cyBoZWxkIGluIHRoaXMgY2xhc3MgYW5kIGhlbmNlIGRvZXMgbm90IGdldCByZWxlYXNlZC5cbiAgICpcbiAgICogQHBhcmFtIHNlbGVjdExvYWRpbmdTdGF0ZXMgU2VsZWN0b3IgdGhhdCByZXR1cm5zIHRoZSBsb2FkaW5nU3RhdHMgb2YgdGhlIGZlYXR1cmUgc2xpY2UuIFlvdSBjYW4gdXNlIGNyZWF0ZUxvYWRpbmdTdGF0ZXNTZWxlY3RvcigpXG4gICAqICAgdG8gY3JlYXRlIGl0LlxuICAgKiBAcmV0dXJucyBBIGNvbGxlY3Rpb24gb2Ygc2VsZWN0b3JzXG4gICAqICAgc3RhdGU6IHRoZSBMb2FkaW5nU3RhdGVcbiAgICogICBsb2FkaW5nOiBUcnVlIGlmIGxvYWRpbmdcbiAgICogICBzdWNjZXNzOiBUcnVlIGlmIGxhc3QgbG9hZCB3YXMgc3VjY2Vzc2Z1bFxuICAgKiAgIGVycm9yOiBhbnkgZXJyb3JzIGZyb20gdGhlIGxhc3QgQVBJIGNhbGwuXG4gICAqIEBleGFtcGxlXG4gICAqICAvLyBUaGUgZmVhdHVyZSBzbGljZSBzZWxlY3Rvci4gU3RhbmRhcmQgbmdyeCBzdHVmZi5cbiAgICogIGNvbnN0IHNlbGVjdFN0YXRlID0gY3JlYXRlRmVhdHVyZVNlbGVjdG9yPFNpbXBsZVN0YXRlPihTSU1QTEVfRkVBVFVSRV9LRVkpO1xuICAgKlxuICAgKiAgLy8gU2VsZWN0b3IgdGhhdCBzZWxlY3RzIHRoZSBsb2FkaW5nU3RhdGVzIGZpZWxkIGZyb20gdGhlIGdsb2JhbCBzdG9yZS4gVGhlIGNyZWF0ZUxvYWRpbmdTdGF0ZXNTZWxlY3RvcigpXG4gICAqICAvLyBpcyBwcm92aWRlZCBhcyBhIHBhcnQgb2YgdGhpcyBsaWIgYXMgd2VsbC5cbiAgICogIGNvbnN0IHNlbGVjdExvYWRpbmdTdGF0ZXMgPSBjcmVhdGVMb2FkaW5nU3RhdGVzU2VsZWN0b3Ioc2VsZWN0U3RhdGUpO1xuICAgKlxuICAgKiAgLy8gQ3JlYXRlIHRoZSBzZWxlY3RvcnMgcmVsYXRlZCB0byB0aGUgZmV0Y2hJdGVtIGxvYWRpbmcgc3RhdGUuXG4gICAqICBleHBvcnQgY29uc3QgZmV0Y2hJdGVtU2VsZWN0b3JzID0gZmV0Y2hJdGVtLmNyZWF0ZVNlbGVjdG9ycyhzZWxlY3RMb2FkaW5nU3RhdGVzKTtcbiAgICpcbiAgICogIC8vIFlvdSBjYW4gdGhlbiBvYnNlcnZlIHRoZSBsb2FkaW5nIHN0YXRlczpcbiAgICogIHRoaXMuc3RvcmUuc2VsZWN0KGZldGNoSXRlbVNlbGVjdG9ycy5zdGF0ZSk7IC8vIFRoZSBlbnRpcmUgTG9hZGluZ1N0YXRlXG4gICAqICB0aGlzLnN0b3JlLnNlbGVjdChmZXRjaEl0ZW1TZWxlY3RvcnMuc3VjY2Vzcyk7IC8vIFRoZSBib29sZWFuIHN1Y2Nlc3MgZmxhZ1xuICAgKiAgdGhpcy5zdG9yZS5zZWxlY3QoZmV0Y2hJdGVtU2VsZWN0b3JzLmxvYWRpbmcpOyAvLyBUaGUgYm9vbGVhbiBsb2FkaW5nIGZsYWdcbiAgICpcbiAgICovXG4gIGNyZWF0ZVNlbGVjdG9ycyhcbiAgICBzZWxlY3RMb2FkaW5nU3RhdGVzOiBNZW1vaXplZFNlbGVjdG9yPG9iamVjdCwgTG9hZGluZ1N0YXRlcywgRGVmYXVsdFByb2plY3RvckZuPExvYWRpbmdTdGF0ZXM+PlxuICApOiB7XG4gICAgc3RhdGU6IE1lbW9pemVkU2VsZWN0b3I8b2JqZWN0LCBMb2FkaW5nU3RhdGUsIERlZmF1bHRQcm9qZWN0b3JGbjxMb2FkaW5nU3RhdGU+PjtcbiAgICBsb2FkaW5nOiBNZW1vaXplZFNlbGVjdG9yPG9iamVjdCwgYm9vbGVhbiwgRGVmYXVsdFByb2plY3RvckZuPGJvb2xlYW4+PjtcbiAgICBzdWNjZXNzOiBNZW1vaXplZFNlbGVjdG9yPG9iamVjdCwgYm9vbGVhbiwgRGVmYXVsdFByb2plY3RvckZuPGJvb2xlYW4+PjtcbiAgICBlcnJvcjogTWVtb2l6ZWRTZWxlY3RvcjxvYmplY3QsIGFueSwgRGVmYXVsdFByb2plY3RvckZuPGFueT4+O1xuICB9IHtcbiAgICBjb25zdCBzdGF0ZSA9IGNyZWF0ZVNlbGVjdG9yKHNlbGVjdExvYWRpbmdTdGF0ZXMsIChsb2FkaW5nU3RhdGVzKSA9PiB7XG4gICAgICByZXR1cm4gdGhpcy5nZXRMb2FkaW5nU3RhdGUobG9hZGluZ1N0YXRlcyk7XG4gICAgfSk7XG5cbiAgICBjb25zdCBsb2FkaW5nID0gY3JlYXRlU2VsZWN0b3Ioc3RhdGUsIChsb2FkaW5nU3RhdGUpID0+IGxvYWRpbmdTdGF0ZS5sb2FkaW5nKTtcbiAgICBjb25zdCBzdWNjZXNzID0gY3JlYXRlU2VsZWN0b3Ioc3RhdGUsIChsb2FkaW5nU3RhdGUpID0+IGxvYWRpbmdTdGF0ZS5zdWNjZXNzKTtcbiAgICBjb25zdCBlcnJvciA9IGNyZWF0ZVNlbGVjdG9yKHN0YXRlLCAobG9hZGluZ1N0YXRlKSA9PiBsb2FkaW5nU3RhdGUuZXJyb3IpO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIHN0YXRlLFxuICAgICAgbG9hZGluZyxcbiAgICAgIHN1Y2Nlc3MsXG4gICAgICBlcnJvclxuICAgIH07XG4gIH1cblxuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gIC8vIEhlbHBlcnNcbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICBwcml2YXRlIGdldCBrZXkoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5sb2FkLnR5cGU7XG4gIH1cblxuICBwcml2YXRlIGdldExvYWRpbmdTdGF0ZShsb2FkaW5nU3RhdGVzOiBSZWFkb25seTxMb2FkaW5nU3RhdGVzPik6IFJlYWRvbmx5PExvYWRpbmdTdGF0ZT4ge1xuICAgIC8vIFdlIHNob3VsZCBub3QgYmUgbW9kaWZ5aW5nIHRoZSBzdGF0ZSB3aXRob3V0IGdvaW5nIHZpYSB0aGUgcmVkdWNlciwgaGVuY2VcbiAgICAvLyByZXR1cm5pbmcgdGhlIGltbXV0YWJsZSBcImluaXRcIiBvYmplY3QuXG4gICAgcmV0dXJuIGxvYWRpbmdTdGF0ZXNbdGhpcy5rZXldO1xuICB9XG5cbiAgcHJpdmF0ZSBzZXRTdGF0ZShcbiAgICBnZXROZXdTdGF0ZTogKFxuICAgICAgYWN0aW9uOiBBY3Rpb24gJiBMb2FkQWN0aW9uLFxuICAgICAgb2xkTG9hZGluZ1N0YXRlOiBSZWFkb25seTxMb2FkaW5nU3RhdGU+XG4gICAgKSA9PiBSZWFkb25seTxMb2FkaW5nU3RhdGU+IHwgbnVsbCxcbiAgICBhY3Rpb246IEFjdGlvbiAmIExvYWRBY3Rpb24sXG4gICAgbG9hZGluZ1N0YXRlczogUmVhZG9ubHk8TG9hZGluZ1N0YXRlcz5cbiAgKTogUmVhZG9ubHk8TG9hZGluZ1N0YXRlcz4ge1xuICAgIC8vIFdlIHdvcmsgd2l0aCBMb2FkaW5nU3RhdGVCYXNlIGhlcmUgdG8gYmUgZ2VuZXJpYy4gVGhlIGlkTG9hZGluZ0FjdGlvbnMgYWxzb1xuICAgIC8vIHVzZSB0aGVzZSBmdW5jdGlvbnMuXG4gICAgY29uc3QgY3VycmVudFN0YXRlID0gY2xvbmVMb2FkaW5nU3RhdGUodGhpcy5nZXRMb2FkaW5nU3RhdGUobG9hZGluZ1N0YXRlcykpO1xuICAgIGNvbnN0IG5ld1N0YXRlID0gZ2V0TmV3U3RhdGUoYWN0aW9uLCBjdXJyZW50U3RhdGUpO1xuXG4gICAgaWYgKG5ld1N0YXRlKSB7XG4gICAgICAvLyBSZXR1cm4gbmV3IHJlZmVyZW5jZSBvbmx5IHdoZW4gdGhlIHN0YXRlIGhhcyBjaGFuZ2VkLlxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgLi4ubG9hZGluZ1N0YXRlcyxcbiAgICAgICAgW3RoaXMua2V5XToge1xuICAgICAgICAgIC4uLm5ld1N0YXRlXG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIE5vIGNoYW5nZSBpbiBzdGF0ZSwgc28gbm8gY2hhbmdlIGluIHBhcmVudCBzdGF0ZS5cbiAgICAgIHJldHVybiBsb2FkaW5nU3RhdGVzO1xuICAgIH1cbiAgfVxufVxuIl19