UNPKG

ngrx-loading-state

Version:

NgRx Loading State consistently manages loading actions such as API fetches.

107 lines 13.4 kB
// These classes are basically serving the same purpose as props<T> in createAction() where it // just holds the type and allows you to name the class to make it easier to read. The alternative // is to explicitly specify the type when calling createLoadingActions<...>(). But the template import { createSelector } from '@ngrx/store'; import { actionFactory } from '../loading-state/loading-state-functions'; import { IdLoadingActions } from './id-loading-state-actions'; export function idLoad() { return new IdLoad(); } export function idSuccess() { return new IdSuccess(); } export function idFailure() { return new IdFailure(); } /** * Creates a set of IdLoadAction, IdSuccessAction, and IdFailureAction. Selectors and reducers are always bundled into * the same structure. * * The difference between IdLoadAction and LoadAction is that IdLoadAction is parameterized by an id field. So you can * display loading states for multiple items that are all loading in parallel. Typical use case is you have a list of items, * and you can issue load actions for each one, parameterized by an id of your own choosing, and observe the loading state * of each item, again parameterized by the id. * * @param type The "type" of the action. * @param _idLoad See usage example * @param _idSuccess See usage example * @param _idFailure See usage example * @returns An instance of LoadingActions class that bundles together actions, selectors and reducers. * @example * export const fetchItem = createIdLoadingActions( * 'Fetch Item', * // An id field is already included in each of LoadAction, SuccessAction, FailureAction * load<{}>(), * success<{ item: object }>(), * failure<{}>() * ); * * // Dispatch load action * const id = "123"; * this.store.dispatch(fetchItem.idLoad({ id })); * * // Using ngrx's createFeatureSelector to select the feature slice from global store. * const selectState = createFeatureSelector<SimpleState>(SIMPLE_FEATURE_KEY); * const selectLoadingStates = createLoadingStatesSelector(selectState); * * const fetchItemSelectors = fetchItem.createIdSelectors(selectLoadingStates); * * // Observe the loading state. * this.store.select(fetchItemSelectors.state(id)); * */ export function createIdLoadingActions(actionTypePrefix, _idLoad, _idSuccess, _idFailure) { return new IdLoadingActions({ idLoad: actionFactory(`${actionTypePrefix}`), idSuccess: actionFactory(`${actionTypePrefix} Success`), idFailure: actionFactory(`${actionTypePrefix} Failure`) }); } /** * * @param featureSelector Selector that selects the current feature slice of the store. * @returns Selector that selects the loadingStates field from the store * @example * // Using ngrx's createFeatureSelector to select the feature slice from global store. * const selectState = createFeatureSelector<SimpleState>(SIMPLE_FEATURE_KEY); * const selectLoadingStates = createLoadingStatesSelector(selectState); * * You can then use selectLoadingStates to compose other selectors. eg. * * export const fetchItem = createIdLoadingActions( * 'Fetch Item', * load<{}>(), * success<{ item: object }>(), * failure<{}>() * ); * * export const fetchItemSelectors = fetchItem.createSelectors(selectLoadingStates); * */ export function createIdLoadingStatesSelector(featureSelector) { return createSelector(featureSelector, (state) => { return state.idLoadingStates; }); } // ------------------------------------------------------------------------ // Internal // ------------------------------------------------------------------------ // types are positional only, so not easy to read. class IdLoad { constructor() { // These variables with constant string typing prevents Load and Success instances from being // assignable to each other. this.type = 'ID_LOAD'; } } class IdSuccess { constructor() { this.type = 'ID_SUCCESS'; } } class IdFailure { constructor() { this.type = 'ID_FAILURE'; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaWQtbG9hZGluZy1zdGF0ZS1jcmVhdG9ycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL25ncngtbG9hZGluZy1zdGF0ZS9zcmMvbGliL2lkLWxvYWRpbmctc3RhdGUvaWQtbG9hZGluZy1zdGF0ZS1jcmVhdG9ycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSw4RkFBOEY7QUFDOUYsa0dBQWtHO0FBQ2xHLGdHQUFnRztBQUVoRyxPQUFPLEVBQUUsY0FBYyxFQUF3QyxNQUFNLGFBQWEsQ0FBQztBQUNuRixPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sMENBQTBDLENBQUM7QUFFekUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFTOUQsTUFBTSxVQUFVLE1BQU07SUFHcEIsT0FBTyxJQUFJLE1BQU0sRUFBbUIsQ0FBQztBQUN2QyxDQUFDO0FBRUQsTUFBTSxVQUFVLFNBQVM7SUFHdkIsT0FBTyxJQUFJLFNBQVMsRUFBc0IsQ0FBQztBQUM3QyxDQUFDO0FBRUQsTUFBTSxVQUFVLFNBQVM7SUFHdkIsT0FBTyxJQUFJLFNBQVMsRUFBc0IsQ0FBQztBQUM3QyxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW9DRztBQUNILE1BQU0sVUFBVSxzQkFBc0IsQ0FLcEMsZ0JBQXdCLEVBQ3hCLE9BQWdDLEVBQ2hDLFVBQXlDLEVBQ3pDLFVBQXlDO0lBRXpDLE9BQU8sSUFBSSxnQkFBZ0IsQ0FBQztRQUMxQixNQUFNLEVBQUUsYUFBYSxDQUFpQyxHQUFHLGdCQUFnQixFQUFFLENBQUM7UUFDNUUsU0FBUyxFQUFFLGFBQWEsQ0FBdUMsR0FBRyxnQkFBZ0IsVUFBVSxDQUFDO1FBQzdGLFNBQVMsRUFBRSxhQUFhLENBQXVDLEdBQUcsZ0JBQWdCLFVBQVUsQ0FBQztLQUM5RixDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBb0JHO0FBQ0gsTUFBTSxVQUFVLDZCQUE2QixDQUMzQyxlQUEyRTtJQUUzRSxPQUFPLGNBQWMsQ0FBQyxlQUFlLEVBQUUsQ0FBQyxLQUFLLEVBQUUsRUFBRTtRQUMvQyxPQUFPLEtBQUssQ0FBQyxlQUFlLENBQUM7SUFDL0IsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQsMkVBQTJFO0FBQzNFLFdBQVc7QUFDWCwyRUFBMkU7QUFFM0Usa0RBQWtEO0FBQ2xELE1BQU0sTUFBTTtJQUFaO1FBQ0UsNkZBQTZGO1FBQzdGLDRCQUE0QjtRQUM1QixTQUFJLEdBQWMsU0FBUyxDQUFDO0lBQzlCLENBQUM7Q0FBQTtBQUNELE1BQU0sU0FBUztJQUFmO1FBQ0UsU0FBSSxHQUFpQixZQUFZLENBQUM7SUFDcEMsQ0FBQztDQUFBO0FBQ0QsTUFBTSxTQUFTO0lBQWY7UUFDRSxTQUFJLEdBQWlCLFlBQVksQ0FBQztJQUNwQyxDQUFDO0NBQUEiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBUaGVzZSBjbGFzc2VzIGFyZSBiYXNpY2FsbHkgc2VydmluZyB0aGUgc2FtZSBwdXJwb3NlIGFzIHByb3BzPFQ+IGluIGNyZWF0ZUFjdGlvbigpIHdoZXJlIGl0XG4vLyBqdXN0IGhvbGRzIHRoZSB0eXBlIGFuZCBhbGxvd3MgeW91IHRvIG5hbWUgdGhlIGNsYXNzIHRvIG1ha2UgaXQgZWFzaWVyIHRvIHJlYWQuIFRoZSBhbHRlcm5hdGl2ZVxuLy8gaXMgdG8gIGV4cGxpY2l0bHkgc3BlY2lmeSB0aGUgdHlwZSB3aGVuIGNhbGxpbmcgY3JlYXRlTG9hZGluZ0FjdGlvbnM8Li4uPigpLiBCdXQgdGhlIHRlbXBsYXRlXG5cbmltcG9ydCB7IGNyZWF0ZVNlbGVjdG9yLCBEZWZhdWx0UHJvamVjdG9yRm4sIE1lbW9pemVkU2VsZWN0b3IgfSBmcm9tICdAbmdyeC9zdG9yZSc7XG5pbXBvcnQgeyBhY3Rpb25GYWN0b3J5IH0gZnJvbSAnLi4vbG9hZGluZy1zdGF0ZS9sb2FkaW5nLXN0YXRlLWZ1bmN0aW9ucyc7XG5pbXBvcnQgeyBOb0ludGVyc2VjdGlvbiB9IGZyb20gJy4uL3V0aWxzJztcbmltcG9ydCB7IElkTG9hZGluZ0FjdGlvbnMgfSBmcm9tICcuL2lkLWxvYWRpbmctc3RhdGUtYWN0aW9ucyc7XG5pbXBvcnQge1xuICBJZEZhaWx1cmVBY3Rpb24sXG4gIElkTG9hZEFjdGlvbixcbiAgSWRMb2FkaW5nU3RhdGVzLFxuICBJZFN1Y2Nlc3NBY3Rpb24sXG4gIFdpdGhJZExvYWRpbmdTdGF0ZXNPbmx5XG59IGZyb20gJy4vaWQtbG9hZGluZy1zdGF0ZS10eXBlcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBpZExvYWQ8XG4gIExvYWRQYXlsb2FkVHlwZSBleHRlbmRzIE5vdElkTG9hZGluZ0FjdGlvbjxMb2FkUGF5bG9hZFR5cGU+XG4+KCk6IElkTG9hZDxMb2FkUGF5bG9hZFR5cGU+IHtcbiAgcmV0dXJuIG5ldyBJZExvYWQ8TG9hZFBheWxvYWRUeXBlPigpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gaWRTdWNjZXNzPFxuICBTdWNjZXNzUGF5bG9hZFR5cGUgZXh0ZW5kcyBOb3RJZFN1Y2Nlc3NBY3Rpb248U3VjY2Vzc1BheWxvYWRUeXBlPlxuPigpOiBJZFN1Y2Nlc3M8U3VjY2Vzc1BheWxvYWRUeXBlPiB7XG4gIHJldHVybiBuZXcgSWRTdWNjZXNzPFN1Y2Nlc3NQYXlsb2FkVHlwZT4oKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGlkRmFpbHVyZTxcbiAgRmFpbHVyZVBheWxvYWRUeXBlIGV4dGVuZHMgTm90SWRGYWlsdXJlQWN0aW9uPEZhaWx1cmVQYXlsb2FkVHlwZT5cbj4oKTogSWRGYWlsdXJlPEZhaWx1cmVQYXlsb2FkVHlwZT4ge1xuICByZXR1cm4gbmV3IElkRmFpbHVyZTxGYWlsdXJlUGF5bG9hZFR5cGU+KCk7XG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIHNldCBvZiBJZExvYWRBY3Rpb24sIElkU3VjY2Vzc0FjdGlvbiwgYW5kIElkRmFpbHVyZUFjdGlvbi4gU2VsZWN0b3JzIGFuZCByZWR1Y2VycyBhcmUgYWx3YXlzIGJ1bmRsZWQgaW50b1xuICogdGhlIHNhbWUgc3RydWN0dXJlLlxuICpcbiAqIFRoZSBkaWZmZXJlbmNlIGJldHdlZW4gSWRMb2FkQWN0aW9uIGFuZCBMb2FkQWN0aW9uIGlzIHRoYXQgSWRMb2FkQWN0aW9uIGlzIHBhcmFtZXRlcml6ZWQgYnkgYW4gaWQgZmllbGQuIFNvIHlvdSBjYW5cbiAqIGRpc3BsYXkgbG9hZGluZyBzdGF0ZXMgZm9yIG11bHRpcGxlIGl0ZW1zIHRoYXQgYXJlIGFsbCBsb2FkaW5nIGluIHBhcmFsbGVsLiBUeXBpY2FsIHVzZSBjYXNlIGlzIHlvdSBoYXZlIGEgbGlzdCBvZiBpdGVtcyxcbiAqIGFuZCB5b3UgY2FuIGlzc3VlIGxvYWQgYWN0aW9ucyBmb3IgZWFjaCBvbmUsIHBhcmFtZXRlcml6ZWQgYnkgYW4gaWQgb2YgeW91ciBvd24gY2hvb3NpbmcsIGFuZCBvYnNlcnZlIHRoZSBsb2FkaW5nIHN0YXRlXG4gKiBvZiBlYWNoIGl0ZW0sIGFnYWluIHBhcmFtZXRlcml6ZWQgYnkgdGhlIGlkLlxuICpcbiAqIEBwYXJhbSB0eXBlIFRoZSBcInR5cGVcIiBvZiB0aGUgYWN0aW9uLlxuICogQHBhcmFtIF9pZExvYWQgU2VlIHVzYWdlIGV4YW1wbGVcbiAqIEBwYXJhbSBfaWRTdWNjZXNzIFNlZSB1c2FnZSBleGFtcGxlXG4gKiBAcGFyYW0gX2lkRmFpbHVyZSBTZWUgdXNhZ2UgZXhhbXBsZVxuICogQHJldHVybnMgQW4gaW5zdGFuY2Ugb2YgTG9hZGluZ0FjdGlvbnMgY2xhc3MgdGhhdCBidW5kbGVzIHRvZ2V0aGVyIGFjdGlvbnMsIHNlbGVjdG9ycyBhbmQgcmVkdWNlcnMuXG4gKiBAZXhhbXBsZVxuICogIGV4cG9ydCBjb25zdCBmZXRjaEl0ZW0gPSBjcmVhdGVJZExvYWRpbmdBY3Rpb25zKFxuICogICAgJ0ZldGNoIEl0ZW0nLFxuICogICAgLy8gQW4gaWQgZmllbGQgaXMgYWxyZWFkeSBpbmNsdWRlZCBpbiBlYWNoIG9mIExvYWRBY3Rpb24sIFN1Y2Nlc3NBY3Rpb24sIEZhaWx1cmVBY3Rpb25cbiAqICAgIGxvYWQ8e30+KCksXG4gKiAgICBzdWNjZXNzPHsgaXRlbTogb2JqZWN0IH0+KCksXG4gKiAgICBmYWlsdXJlPHt9PigpXG4gKiAgKTtcbiAqXG4gKiAgLy8gRGlzcGF0Y2ggbG9hZCBhY3Rpb25cbiAqICBjb25zdCBpZCA9IFwiMTIzXCI7XG4gKiAgdGhpcy5zdG9yZS5kaXNwYXRjaChmZXRjaEl0ZW0uaWRMb2FkKHsgaWQgfSkpO1xuICpcbiAqICAvLyBVc2luZyBuZ3J4J3MgY3JlYXRlRmVhdHVyZVNlbGVjdG9yIHRvIHNlbGVjdCB0aGUgZmVhdHVyZSBzbGljZSBmcm9tIGdsb2JhbCBzdG9yZS5cbiAqICBjb25zdCBzZWxlY3RTdGF0ZSA9IGNyZWF0ZUZlYXR1cmVTZWxlY3RvcjxTaW1wbGVTdGF0ZT4oU0lNUExFX0ZFQVRVUkVfS0VZKTtcbiAqICBjb25zdCBzZWxlY3RMb2FkaW5nU3RhdGVzID0gY3JlYXRlTG9hZGluZ1N0YXRlc1NlbGVjdG9yKHNlbGVjdFN0YXRlKTtcbiAqXG4gKiAgY29uc3QgZmV0Y2hJdGVtU2VsZWN0b3JzID0gZmV0Y2hJdGVtLmNyZWF0ZUlkU2VsZWN0b3JzKHNlbGVjdExvYWRpbmdTdGF0ZXMpO1xuICpcbiAqICAvLyBPYnNlcnZlIHRoZSBsb2FkaW5nIHN0YXRlLlxuICogIHRoaXMuc3RvcmUuc2VsZWN0KGZldGNoSXRlbVNlbGVjdG9ycy5zdGF0ZShpZCkpO1xuICpcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUlkTG9hZGluZ0FjdGlvbnM8XG4gIExvYWRQYXlsb2FkVHlwZSBleHRlbmRzIG9iamVjdCxcbiAgU3VjY2Vzc1BheWxvYWRUeXBlIGV4dGVuZHMgb2JqZWN0LFxuICBGYWlsdXJlUGF5bG9hZFR5cGUgZXh0ZW5kcyBvYmplY3Rcbj4oXG4gIGFjdGlvblR5cGVQcmVmaXg6IHN0cmluZyxcbiAgX2lkTG9hZDogSWRMb2FkPExvYWRQYXlsb2FkVHlwZT4sXG4gIF9pZFN1Y2Nlc3M6IElkU3VjY2VzczxTdWNjZXNzUGF5bG9hZFR5cGU+LFxuICBfaWRGYWlsdXJlOiBJZEZhaWx1cmU8RmFpbHVyZVBheWxvYWRUeXBlPlxuKTogSWRMb2FkaW5nQWN0aW9uczxMb2FkUGF5bG9hZFR5cGUsIFN1Y2Nlc3NQYXlsb2FkVHlwZSwgRmFpbHVyZVBheWxvYWRUeXBlPiB7XG4gIHJldHVybiBuZXcgSWRMb2FkaW5nQWN0aW9ucyh7XG4gICAgaWRMb2FkOiBhY3Rpb25GYWN0b3J5PElkTG9hZEFjdGlvbiAmIExvYWRQYXlsb2FkVHlwZT4oYCR7YWN0aW9uVHlwZVByZWZpeH1gKSxcbiAgICBpZFN1Y2Nlc3M6IGFjdGlvbkZhY3Rvcnk8SWRTdWNjZXNzQWN0aW9uICYgU3VjY2Vzc1BheWxvYWRUeXBlPihgJHthY3Rpb25UeXBlUHJlZml4fSBTdWNjZXNzYCksXG4gICAgaWRGYWlsdXJlOiBhY3Rpb25GYWN0b3J5PElkRmFpbHVyZUFjdGlvbiAmIEZhaWx1cmVQYXlsb2FkVHlwZT4oYCR7YWN0aW9uVHlwZVByZWZpeH0gRmFpbHVyZWApXG4gIH0pO1xufVxuXG4vKipcbiAqXG4gKiBAcGFyYW0gZmVhdHVyZVNlbGVjdG9yIFNlbGVjdG9yIHRoYXQgc2VsZWN0cyB0aGUgY3VycmVudCBmZWF0dXJlIHNsaWNlIG9mIHRoZSBzdG9yZS5cbiAqIEByZXR1cm5zIFNlbGVjdG9yIHRoYXQgc2VsZWN0cyB0aGUgbG9hZGluZ1N0YXRlcyBmaWVsZCBmcm9tIHRoZSBzdG9yZVxuICogQGV4YW1wbGVcbiAqICAvLyBVc2luZyBuZ3J4J3MgY3JlYXRlRmVhdHVyZVNlbGVjdG9yIHRvIHNlbGVjdCB0aGUgZmVhdHVyZSBzbGljZSBmcm9tIGdsb2JhbCBzdG9yZS5cbiAqICBjb25zdCBzZWxlY3RTdGF0ZSA9IGNyZWF0ZUZlYXR1cmVTZWxlY3RvcjxTaW1wbGVTdGF0ZT4oU0lNUExFX0ZFQVRVUkVfS0VZKTtcbiAqICBjb25zdCBzZWxlY3RMb2FkaW5nU3RhdGVzID0gY3JlYXRlTG9hZGluZ1N0YXRlc1NlbGVjdG9yKHNlbGVjdFN0YXRlKTtcbiAqXG4gKiAgWW91IGNhbiB0aGVuIHVzZSBzZWxlY3RMb2FkaW5nU3RhdGVzIHRvIGNvbXBvc2Ugb3RoZXIgc2VsZWN0b3JzLiBlZy5cbiAqXG4gKiAgZXhwb3J0IGNvbnN0IGZldGNoSXRlbSA9IGNyZWF0ZUlkTG9hZGluZ0FjdGlvbnMoXG4gKiAgICAnRmV0Y2ggSXRlbScsXG4gKiAgICBsb2FkPHt9PigpLFxuICogICAgc3VjY2Vzczx7IGl0ZW06IG9iamVjdCB9PigpLFxuICogICAgZmFpbHVyZTx7fT4oKVxuICogICk7XG4gKlxuICogIGV4cG9ydCBjb25zdCBmZXRjaEl0ZW1TZWxlY3RvcnMgPSBmZXRjaEl0ZW0uY3JlYXRlU2VsZWN0b3JzKHNlbGVjdExvYWRpbmdTdGF0ZXMpO1xuICpcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUlkTG9hZGluZ1N0YXRlc1NlbGVjdG9yPFN0YXRlIGV4dGVuZHMgV2l0aElkTG9hZGluZ1N0YXRlc09ubHk+KFxuICBmZWF0dXJlU2VsZWN0b3I6IE1lbW9pemVkU2VsZWN0b3I8b2JqZWN0LCBTdGF0ZSwgRGVmYXVsdFByb2plY3RvckZuPFN0YXRlPj5cbik6IE1lbW9pemVkU2VsZWN0b3I8b2JqZWN0LCBJZExvYWRpbmdTdGF0ZXMsIERlZmF1bHRQcm9qZWN0b3JGbjxJZExvYWRpbmdTdGF0ZXM+PiB7XG4gIHJldHVybiBjcmVhdGVTZWxlY3RvcihmZWF0dXJlU2VsZWN0b3IsIChzdGF0ZSkgPT4ge1xuICAgIHJldHVybiBzdGF0ZS5pZExvYWRpbmdTdGF0ZXM7XG4gIH0pO1xufVxuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIEludGVybmFsXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLy8gdHlwZXMgYXJlIHBvc2l0aW9uYWwgb25seSwgc28gbm90IGVhc3kgdG8gcmVhZC5cbmNsYXNzIElkTG9hZDxfTG9hZFBheWxvYWRUeXBlPiB7XG4gIC8vIFRoZXNlIHZhcmlhYmxlcyB3aXRoIGNvbnN0YW50IHN0cmluZyB0eXBpbmcgcHJldmVudHMgTG9hZCBhbmQgU3VjY2VzcyBpbnN0YW5jZXMgZnJvbSBiZWluZ1xuICAvLyBhc3NpZ25hYmxlIHRvIGVhY2ggb3RoZXIuXG4gIHR5cGU6ICdJRF9MT0FEJyA9ICdJRF9MT0FEJztcbn1cbmNsYXNzIElkU3VjY2VzczxfU3VjY2Vzc1BheWxvYWRUeXBlPiB7XG4gIHR5cGU6ICdJRF9TVUNDRVNTJyA9ICdJRF9TVUNDRVNTJztcbn1cbmNsYXNzIElkRmFpbHVyZTxfRmFpbHVyZVBheWxvYWRUeXBlPiB7XG4gIHR5cGU6ICdJRF9GQUlMVVJFJyA9ICdJRF9GQUlMVVJFJztcbn1cblxuLy8gVGhpcyBlbnN1cmVzIHRoYXQgd2UgZG9uJ3QgcmVkZWZpbmUgdGhlIGV4aXN0aW5nIGZpZWxkcyBpbiB0aGUgYWN0aW9ucy5cbnR5cGUgTm90SWRMb2FkaW5nQWN0aW9uPFQ+ID0gTm9JbnRlcnNlY3Rpb248VCwgSWRMb2FkQWN0aW9uPjtcbnR5cGUgTm90SWRTdWNjZXNzQWN0aW9uPFQ+ID0gTm9JbnRlcnNlY3Rpb248VCwgSWRTdWNjZXNzQWN0aW9uPjtcbnR5cGUgTm90SWRGYWlsdXJlQWN0aW9uPFQ+ID0gTm9JbnRlcnNlY3Rpb248VCwgSWRGYWlsdXJlQWN0aW9uPjtcbiJdfQ==