UNPKG

ngrx-loading-state

Version:

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

190 lines 23 kB
import { createAction, props } from '@ngrx/store'; import { lodash } from '../lodash'; import { FailureHandlerState } from './loading-state-types'; /** * See if a new API fetch should be issued. * * @param currentState The current loading state. * @param action The the LoadAction dispatched by the user. * @returns True if a new API fetch should be issued. */ export function shouldIssueFetch(currentState, action) { if (currentState == null) { return true; } const maxAge = action?.maxAge; // If action not given, then we err on the side of caution and always do a reload, even if // an API fetch is already happening. if (maxAge == null) { return true; } else { // Check if data not loaded or if too old. const reload = !currentState.successTimestamp || Date.now() - currentState.successTimestamp >= maxAge; // Do not issue duplicate loads if a fetch is already in progress return reload && !currentState.loading; } } /** * Return the type of error handler that should be used. * * @param currentState the current state * @param action the load action dispatched by the user * @param issueFetch whether the load action should issue a new API call. * @returns */ export function getFailureHandlerState(currentState, action, issueFetch) { if (currentState == null) { // If currentState does not exist, then errorHandlerState can be assumed to be initialised to ErrorHandlerState.INIT if (action.localError) { return FailureHandlerState.LOCAL; } else { return FailureHandlerState.GLOBAL; } } // If loading or issuing API fetch, then there is guaranteed to be a success/failure // action that the global error handler might need to handle. if (currentState.loading || issueFetch) { // If any load action sets the localError to true, then it disables the global error hander // until the success/failure action is handled. if (action.localError) { return FailureHandlerState.LOCAL; } else if (currentState.failureHandlerState == FailureHandlerState.INIT) { // If it's in the INIT state, then we use the default global handler because the // loading action has not requests for localError handler. return FailureHandlerState.GLOBAL; } // else just fall through and return the existing errorHandler unchanged. } return currentState.failureHandlerState; } /** * This function make it easier to define the type of prop<T>. Internal use only. * * T extends object meets the condition of props function * * ref: https://stackoverflow.com/questions/65888508/how-to-use-generic-type-in-ngrx-createaction-props * * @param type String type of the action * @returns An action creator function */ export function actionFactory(type) { // Restricting config type to match createAction requirements // eslint-disable-next-line @typescript-eslint/no-explicit-any return createAction(type, props()); } /** * Make a clone of any object that implements the LoadingState interface, but copy over only * those fields that are in the LoadingState interface. * * @param src Any object that implements the LoadingState interface * @returns A copy of src with only fields from LoadingState */ export function cloneLoadingState(src) { // Since the "src" could contain extra fields, we want to make sure we exclude other fields in the // comparison. Using Required<> to make sure we don't miss any fields. // Would be better if we can iterate all the fields in the LoadingStateBase interface. But unless we change // LoadingStateBase to a class, it doesn't seem likely. Below is verbose, but at least it should capture all // the fields. if (src == null) { return src; } else { // Using Required<> to ensure we don't miss any fields from LoadingStateBase. const ret = lodash.pick(src, [ 'isLoadingState', 'loading', 'success', 'issueFetch', 'failureHandlerState', 'successTimestamp', 'error' ]); return ret; } } /** * Combine the currentState and the user dispatched LoadAction into newState. * * @param action User dispatched LoadAction * @param currentState Current store state * @returns A new state if the state should change, null otherwise. */ export function getNewLoadState(action, currentState) { const issueFetch = shouldIssueFetch(currentState, action); const failureHandlerState = getFailureHandlerState(currentState, action, issueFetch); const newState = issueFetch ? { isLoadingState: true, loading: true, success: false, issueFetch, failureHandlerState, successTimestamp: currentState?.successTimestamp, error: undefined } : { isLoadingState: true, // Deliberately avoiding the use of the spread operator, i.e. no ...currentState // because we want to be 100% explicit about the states we are setting. Using ...currentState // makes it difficult to read. Being explicit means we need to specify all fields // from LoadingState. If we ever add more states to LoadingState the typing will catch any // missing states. There's also just the loading(), success(), failure() functions // so not too cumbersome to be explicit. loading: currentState.loading, success: currentState.success, issueFetch, failureHandlerState, successTimestamp: currentState.successTimestamp, error: currentState.error }; // Note that even if issueFetch is false, the errorHandlerState could still change. So we should // compare every field from old to new state. return lodash.isEqual(currentState, newState) ? null : newState; } /** * @returns Always returns a new success state. */ export function getNewSuccessState() { const ret = { isLoadingState: true, loading: false, success: true, issueFetch: false, // Each load action will set this again, so here we just set it back to default. failureHandlerState: FailureHandlerState.INIT, // Since this time field changes, all SuccessAction will cause a state update. successTimestamp: Date.now(), error: undefined }; return ret; } /** * Returns a new FailureState. * * @param action User dispatched FailureAction * @param currentState Current loading state * @returns A new loading state. */ export function getNewFailureState(action, currentState) { return { isLoadingState: true, loading: false, success: false, issueFetch: false, // Leading this as is for the global failure handler to check. failureHandlerState: currentState.failureHandlerState, successTimestamp: currentState.successTimestamp, error: action.error }; } export function combineLoadingStates(loadingStates) { return { loading: loadingStates.some((state) => !!state?.loading), success: loadingStates.every((state) => !!state?.success), error: loadingStates.find((state) => !!state?.error)?.error }; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9hZGluZy1zdGF0ZS1mdW5jdGlvbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9uZ3J4LWxvYWRpbmctc3RhdGUvc3JjL2xpYi9sb2FkaW5nLXN0YXRlL2xvYWRpbmctc3RhdGUtZnVuY3Rpb25zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBc0IsWUFBWSxFQUFtQixLQUFLLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFFdkYsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUNuQyxPQUFPLEVBSUwsbUJBQW1CLEVBR3BCLE1BQU0sdUJBQXVCLENBQUM7QUFFL0I7Ozs7OztHQU1HO0FBQ0gsTUFBTSxVQUFVLGdCQUFnQixDQUM5QixZQUFvQyxFQUNwQyxNQUE0QjtJQUU1QixJQUFJLFlBQVksSUFBSSxJQUFJLEVBQUU7UUFDeEIsT0FBTyxJQUFJLENBQUM7S0FDYjtJQUVELE1BQU0sTUFBTSxHQUFHLE1BQU0sRUFBRSxNQUFNLENBQUM7SUFFOUIsMEZBQTBGO0lBQzFGLHFDQUFxQztJQUNyQyxJQUFJLE1BQU0sSUFBSSxJQUFJLEVBQUU7UUFDbEIsT0FBTyxJQUFJLENBQUM7S0FDYjtTQUFNO1FBQ0wsMENBQTBDO1FBQzFDLE1BQU0sTUFBTSxHQUNWLENBQUMsWUFBWSxDQUFDLGdCQUFnQixJQUFJLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxZQUFZLENBQUMsZ0JBQWdCLElBQUksTUFBTSxDQUFDO1FBRXpGLGlFQUFpRTtRQUNqRSxPQUFPLE1BQU0sSUFBSSxDQUFDLFlBQVksQ0FBQyxPQUFPLENBQUM7S0FDeEM7QUFDSCxDQUFDO0FBRUQ7Ozs7Ozs7R0FPRztBQUNILE1BQU0sVUFBVSxzQkFBc0IsQ0FDcEMsWUFBb0MsRUFDcEMsTUFBNEIsRUFDNUIsVUFBbUI7SUFFbkIsSUFBSSxZQUFZLElBQUksSUFBSSxFQUFFO1FBQ3hCLG9IQUFvSDtRQUNwSCxJQUFJ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dGlvbkNyZWF0b3JQcm9wcywgY3JlYXRlQWN0aW9uLCBOb3RBbGxvd2VkQ2hlY2ssIHByb3BzIH0gZnJvbSAnQG5ncngvc3RvcmUnO1xuaW1wb3J0IHsgQWN0aW9uIH0gZnJvbSAnQG5ncngvc3RvcmUvc3JjL21vZGVscyc7XG5pbXBvcnQgeyBsb2Rhc2ggfSBmcm9tICcuLi9sb2Rhc2gnO1xuaW1wb3J0IHtcbiAgQWN0aW9uRmFjdG9yeVJlc3VsdCxcbiAgQ29tYmluZWRMb2FkaW5nU3RhdGUsXG4gIEZhaWx1cmVBY3Rpb24sXG4gIEZhaWx1cmVIYW5kbGVyU3RhdGUsXG4gIExvYWRBY3Rpb24sXG4gIExvYWRpbmdTdGF0ZVxufSBmcm9tICcuL2xvYWRpbmctc3RhdGUtdHlwZXMnO1xuXG4vKipcbiAqIFNlZSBpZiBhIG5ldyBBUEkgZmV0Y2ggc2hvdWxkIGJlIGlzc3VlZC5cbiAqXG4gKiBAcGFyYW0gY3VycmVudFN0YXRlIFRoZSBjdXJyZW50IGxvYWRpbmcgc3RhdGUuXG4gKiBAcGFyYW0gYWN0aW9uIFRoZSB0aGUgTG9hZEFjdGlvbiBkaXNwYXRjaGVkIGJ5IHRoZSB1c2VyLlxuICogQHJldHVybnMgVHJ1ZSBpZiBhIG5ldyBBUEkgZmV0Y2ggc2hvdWxkIGJlIGlzc3VlZC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNob3VsZElzc3VlRmV0Y2goXG4gIGN1cnJlbnRTdGF0ZTogUmVhZG9ubHk8TG9hZGluZ1N0YXRlPixcbiAgYWN0aW9uOiBSZWFkb25seTxMb2FkQWN0aW9uPlxuKTogYm9vbGVhbiB7XG4gIGlmIChjdXJyZW50U3RhdGUgPT0gbnVsbCkge1xuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgY29uc3QgbWF4QWdlID0gYWN0aW9uPy5tYXhBZ2U7XG5cbiAgLy8gSWYgYWN0aW9uIG5vdCBnaXZlbiwgdGhlbiB3ZSBlcnIgb24gdGhlIHNpZGUgb2YgY2F1dGlvbiBhbmQgYWx3YXlzIGRvIGEgcmVsb2FkLCBldmVuIGlmXG4gIC8vIGFuIEFQSSBmZXRjaCBpcyBhbHJlYWR5IGhhcHBlbmluZy5cbiAgaWYgKG1heEFnZSA9PSBudWxsKSB7XG4gICAgcmV0dXJuIHRydWU7XG4gIH0gZWxzZSB7XG4gICAgLy8gQ2hlY2sgaWYgZGF0YSBub3QgbG9hZGVkIG9yIGlmIHRvbyBvbGQuXG4gICAgY29uc3QgcmVsb2FkID1cbiAgICAgICFjdXJyZW50U3RhdGUuc3VjY2Vzc1RpbWVzdGFtcCB8fCBEYXRlLm5vdygpIC0gY3VycmVudFN0YXRlLnN1Y2Nlc3NUaW1lc3RhbXAgPj0gbWF4QWdlO1xuXG4gICAgLy8gRG8gbm90IGlzc3VlIGR1cGxpY2F0ZSBsb2FkcyBpZiBhIGZldGNoIGlzIGFscmVhZHkgaW4gcHJvZ3Jlc3NcbiAgICByZXR1cm4gcmVsb2FkICYmICFjdXJyZW50U3RhdGUubG9hZGluZztcbiAgfVxufVxuXG4vKipcbiAqIFJldHVybiB0aGUgdHlwZSBvZiBlcnJvciBoYW5kbGVyIHRoYXQgc2hvdWxkIGJlIHVzZWQuXG4gKlxuICogQHBhcmFtIGN1cnJlbnRTdGF0ZSB0aGUgY3VycmVudCBzdGF0ZVxuICogQHBhcmFtIGFjdGlvbiB0aGUgbG9hZCBhY3Rpb24gZGlzcGF0Y2hlZCBieSB0aGUgdXNlclxuICogQHBhcmFtIGlzc3VlRmV0Y2ggd2hldGhlciB0aGUgbG9hZCBhY3Rpb24gc2hvdWxkIGlzc3VlIGEgbmV3IEFQSSBjYWxsLlxuICogQHJldHVybnNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldEZhaWx1cmVIYW5kbGVyU3RhdGUoXG4gIGN1cnJlbnRTdGF0ZTogUmVhZG9ubHk8TG9hZGluZ1N0YXRlPixcbiAgYWN0aW9uOiBSZWFkb25seTxMb2FkQWN0aW9uPixcbiAgaXNzdWVGZXRjaDogYm9vbGVhblxuKTogRmFpbHVyZUhhbmRsZXJTdGF0ZSB7XG4gIGlmIChjdXJyZW50U3RhdGUgPT0gbnVsbCkge1xuICAgIC8vIElmIGN1cnJlbnRTdGF0ZSBkb2VzIG5vdCBleGlzdCwgdGhlbiBlcnJvckhhbmRsZXJTdGF0ZSBjYW4gYmUgYXNzdW1lZCB0byBiZSBpbml0aWFsaXNlZCB0byBFcnJvckhhbmRsZXJTdGF0ZS5JTklUXG4gICAgaWYgKGFjdGlvbi5sb2NhbEVycm9yKSB7XG4gICAgICByZXR1cm4gRmFpbHVyZUhhbmRsZXJTdGF0ZS5MT0NBTDtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIEZhaWx1cmVIYW5kbGVyU3RhdGUuR0xPQkFMO1xuICAgIH1cbiAgfVxuXG4gIC8vIElmIGxvYWRpbmcgb3IgaXNzdWluZyBBUEkgZmV0Y2gsIHRoZW4gdGhlcmUgaXMgZ3VhcmFudGVlZCB0byBiZSBhIHN1Y2Nlc3MvZmFpbHVyZVxuICAvLyBhY3Rpb24gdGhhdCB0aGUgZ2xvYmFsIGVycm9yIGhhbmRsZXIgbWlnaHQgbmVlZCB0byBoYW5kbGUuXG4gIGlmIChjdXJyZW50U3RhdGUubG9hZGluZyB8fCBpc3N1ZUZldGNoKSB7XG4gICAgLy8gSWYgYW55IGxvYWQgYWN0aW9uIHNldHMgdGhlIGxvY2FsRXJyb3IgdG8gdHJ1ZSwgdGhlbiBpdCBkaXNhYmxlcyB0aGUgZ2xvYmFsIGVycm9yIGhhbmRlclxuICAgIC8vIHVudGlsIHRoZSBzdWNjZXNzL2ZhaWx1cmUgYWN0aW9uIGlzIGhhbmRsZWQuXG4gICAgaWYgKGFjdGlvbi5sb2NhbEVycm9yKSB7XG4gICAgICByZXR1cm4gRmFpbHVyZUhhbmRsZXJTdGF0ZS5MT0NBTDtcbiAgICB9IGVsc2UgaWYgKGN1cnJlbnRTdGF0ZS5mYWlsdXJlSGFuZGxlclN0YXRlID09IEZhaWx1cmVIYW5kbGVyU3RhdGUuSU5JVCkge1xuICAgICAgLy8gSWYgaXQncyBpbiB0aGUgSU5JVCBzdGF0ZSwgdGhlbiB3ZSB1c2UgdGhlIGRlZmF1bHQgZ2xvYmFsIGhhbmRsZXIgYmVjYXVzZSB0aGVcbiAgICAgIC8vIGxvYWRpbmcgYWN0aW9uIGhhcyBub3QgcmVxdWVzdHMgZm9yIGxvY2FsRXJyb3IgaGFuZGxlci5cbiAgICAgIHJldHVybiBGYWlsdXJlSGFuZGxlclN0YXRlLkdMT0JBTDtcbiAgICB9XG4gICAgLy8gZWxzZSBqdXN0IGZhbGwgdGhyb3VnaCBhbmQgcmV0dXJuIHRoZSBleGlzdGluZyBlcnJvckhhbmRsZXIgdW5jaGFuZ2VkLlxuICB9XG5cbiAgcmV0dXJuIGN1cnJlbnRTdGF0ZS5mYWlsdXJlSGFuZGxlclN0YXRlO1xufVxuXG4vKipcbiAqIFRoaXMgZnVuY3Rpb24gbWFrZSBpdCBlYXNpZXIgdG8gZGVmaW5lIHRoZSB0eXBlIG9mIHByb3A8VD4uIEludGVybmFsIHVzZSBvbmx5LlxuICpcbiAqIFQgZXh0ZW5kcyBvYmplY3QgbWVldHMgdGhlIGNvbmRpdGlvbiBvZiBwcm9wcyBmdW5jdGlvblxuICpcbiAqIHJlZjogaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNjU4ODg1MDgvaG93LXRvLXVzZS1nZW5lcmljLXR5cGUtaW4tbmdyeC1jcmVhdGVhY3Rpb24tcHJvcHNcbiAqXG4gKiBAcGFyYW0gdHlwZSBTdHJpbmcgdHlwZSBvZiB0aGUgYWN0aW9uXG4gKiBAcmV0dXJucyBBbiBhY3Rpb24gY3JlYXRvciBmdW5jdGlvblxuICovXG5leHBvcnQgZnVuY3Rpb24gYWN0aW9uRmFjdG9yeTxUIGV4dGVuZHMgb2JqZWN0Pih0eXBlOiBzdHJpbmcpOiBBY3Rpb25GYWN0b3J5UmVzdWx0PFQ+IHtcbiAgLy8gUmVzdHJpY3RpbmcgY29uZmlnIHR5cGUgdG8gbWF0Y2ggY3JlYXRlQWN0aW9uIHJlcXVpcmVtZW50c1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWV4cGxpY2l0LWFueVxuICByZXR1cm4gY3JlYXRlQWN0aW9uKHR5cGUsIHByb3BzPGFueT4oKSBhcyBBY3Rpb25DcmVhdG9yUHJvcHM8VD4gJiBOb3RBbGxvd2VkQ2hlY2s8VD4pO1xufVxuXG4vKipcbiAqIE1ha2UgYSBjbG9uZSBvZiBhbnkgb2JqZWN0IHRoYXQgaW1wbGVtZW50cyB0aGUgTG9hZGluZ1N0YXRlIGludGVyZmFjZSwgYnV0IGNvcHkgb3ZlciBvbmx5XG4gKiB0aG9zZSBmaWVsZHMgdGhhdCBhcmUgaW4gdGhlIExvYWRpbmdTdGF0ZSBpbnRlcmZhY2UuXG4gKlxuICogQHBhcmFtIHNyYyBBbnkgb2JqZWN0IHRoYXQgaW1wbGVtZW50cyB0aGUgTG9hZGluZ1N0YXRlIGludGVyZmFjZVxuICogQHJldHVybnMgQSBjb3B5IG9mIHNyYyB3aXRoIG9ubHkgZmllbGRzIGZyb20gTG9hZGluZ1N0YXRlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjbG9uZUxvYWRpbmdTdGF0ZShzcmM6IExvYWRpbmdTdGF0ZSk6IExvYWRpbmdTdGF0ZSB7XG4gIC8vIFNpbmNlIHRoZSBcInNyY1wiIGNvdWxkIGNvbnRhaW4gZXh0cmEgZmllbGRzLCB3ZSB3YW50IHRvIG1ha2Ugc3VyZSB3ZSBleGNsdWRlIG90aGVyIGZpZWxkcyBpbiB0aGVcbiAgLy8gY29tcGFyaXNvbi4gVXNpbmcgUmVxdWlyZWQ8PiB0byBtYWtlIHN1cmUgd2UgZG9uJ3QgbWlzcyBhbnkgZmllbGRzLlxuICAvLyBXb3VsZCBiZSBiZXR0ZXIgaWYgd2UgY2FuIGl0ZXJhdGUgYWxsIHRoZSBmaWVsZHMgaW4gdGhlIExvYWRpbmdTdGF0ZUJhc2UgaW50ZXJmYWNlLiBCdXQgdW5sZXNzIHdlIGNoYW5nZVxuICAvLyBMb2FkaW5nU3RhdGVCYXNlIHRvIGEgY2xhc3MsIGl0IGRvZXNuJ3Qgc2VlbSBsaWtlbHkuIEJlbG93IGlzIHZlcmJvc2UsIGJ1dCBhdCBsZWFzdCBpdCBzaG91bGQgY2FwdHVyZSBhbGxcbiAgLy8gdGhlIGZpZWxkcy5cblxuICBpZiAoc3JjID09IG51bGwpIHtcbiAgICByZXR1cm4gc3JjO1xuICB9IGVsc2Uge1xuICAgIC8vIFVzaW5nIFJlcXVpcmVkPD4gdG8gZW5zdXJlIHdlIGRvbid0IG1pc3MgYW55IGZpZWxkcyBmcm9tIExvYWRpbmdTdGF0ZUJhc2UuXG4gICAgY29uc3QgcmV0OiBSZXF1aXJlZDxMb2FkaW5nU3RhdGU+ID0gbG9kYXNoLnBpY2soc3JjIGFzIFJlcXVpcmVkPExvYWRpbmdTdGF0ZT4sIFtcbiAgICAgICdpc0xvYWRpbmdTdGF0ZScsXG4gICAgICAnbG9hZGluZycsXG4gICAgICAnc3VjY2VzcycsXG4gICAgICAnaXNzdWVGZXRjaCcsXG4gICAgICAnZmFpbHVyZUhhbmRsZXJTdGF0ZScsXG4gICAgICAnc3VjY2Vzc1RpbWVzdGFtcCcsXG4gICAgICAnZXJyb3InXG4gICAgXSk7XG5cbiAgICByZXR1cm4gcmV0O1xuICB9XG59XG5cbi8qKlxuICogQ29tYmluZSB0aGUgY3VycmVudFN0YXRlIGFuZCB0aGUgdXNlciBkaXNwYXRjaGVkIExvYWRBY3Rpb24gaW50byBuZXdTdGF0ZS5cbiAqXG4gKiBAcGFyYW0gYWN0aW9uIFVzZXIgZGlzcGF0Y2hlZCBMb2FkQWN0aW9uXG4gKiBAcGFyYW0gY3VycmVudFN0YXRlIEN1cnJlbnQgc3RvcmUgc3RhdGVcbiAqIEByZXR1cm5zIEEgbmV3IHN0YXRlIGlmIHRoZSBzdGF0ZSBzaG91bGQgY2hhbmdlLCBudWxsIG90aGVyd2lzZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldE5ld0xvYWRTdGF0ZShcbiAgYWN0aW9uOiBMb2FkQWN0aW9uICYgQWN0aW9uLFxuICBjdXJyZW50U3RhdGU6IExvYWRpbmdTdGF0ZVxuKTogUmVhZG9ubHk8TG9hZGluZ1N0YXRlPiB8IG51bGwge1xuICBjb25zdCBpc3N1ZUZldGNoID0gc2hvdWxkSXNzdWVGZXRjaChjdXJyZW50U3RhdGUsIGFjdGlvbik7XG5cbiAgY29uc3QgZmFpbHVyZUhhbmRsZXJTdGF0ZSA9IGdldEZhaWx1cmVIYW5kbGVyU3RhdGUoY3VycmVudFN0YXRlLCBhY3Rpb24sIGlzc3VlRmV0Y2gpO1xuXG4gIGNvbnN0IG5ld1N0YXRlOiBMb2FkaW5nU3RhdGUgPSBpc3N1ZUZldGNoXG4gICAgPyB7XG4gICAgICAgIGlzTG9hZGluZ1N0YXRlOiB0cnVlLFxuICAgICAgICBsb2FkaW5nOiB0cnVlLFxuICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgaXNzdWVGZXRjaCxcbiAgICAgICAgZmFpbHVyZUhhbmRsZXJTdGF0ZSxcbiAgICAgICAgc3VjY2Vzc1RpbWVzdGFtcDogY3VycmVudFN0YXRlPy5zdWNjZXNzVGltZXN0YW1wLFxuICAgICAgICBlcnJvcjogdW5kZWZpbmVkXG4gICAgICB9XG4gICAgOiB7XG4gICAgICAgIGlzTG9hZGluZ1N0YXRlOiB0cnVlLFxuICAgICAgICAvLyBEZWxpYmVyYXRlbHkgYXZvaWRpbmcgdGhlIHVzZSBvZiB0aGUgc3ByZWFkIG9wZXJhdG9yLCBpLmUuIG5vIC4uLmN1cnJlbnRTdGF0ZVxuICAgICAgICAvLyBiZWNhdXNlIHdlIHdhbnQgdG8gYmUgMTAwJSBleHBsaWNpdCBhYm91dCB0aGUgc3RhdGVzIHdlIGFyZSBzZXR0aW5nLiBVc2luZyAuLi5jdXJyZW50U3RhdGVcbiAgICAgICAgLy8gbWFrZXMgaXQgZGlmZmljdWx0IHRvIHJlYWQuIEJlaW5nIGV4cGxpY2l0IG1lYW5zIHdlIG5lZWQgdG8gc3BlY2lmeSBhbGwgZmllbGRzXG4gICAgICAgIC8vIGZyb20gTG9hZGluZ1N0YXRlLiBJZiB3ZSBldmVyIGFkZCBtb3JlIHN0YXRlcyB0byBMb2FkaW5nU3RhdGUgdGhlIHR5cGluZyB3aWxsIGNhdGNoIGFueVxuICAgICAgICAvLyBtaXNzaW5nIHN0YXRlcy4gVGhlcmUncyBhbHNvIGp1c3QgdGhlIGxvYWRpbmcoKSwgc3VjY2VzcygpLCBmYWlsdXJlKCkgZnVuY3Rpb25zXG4gICAgICAgIC8vIHNvIG5vdCB0b28gY3VtYmVyc29tZSB0byBiZSBleHBsaWNpdC5cbiAgICAgICAgbG9hZGluZzogY3VycmVudFN0YXRlLmxvYWRpbmcsXG4gICAgICAgIHN1Y2Nlc3M6IGN1cnJlbnRTdGF0ZS5zdWNjZXNzLFxuICAgICAgICBpc3N1ZUZldGNoLFxuICAgICAgICBmYWlsdXJlSGFuZGxlclN0YXRlLFxuICAgICAgICBzdWNjZXNzVGltZXN0YW1wOiBjdXJyZW50U3RhdGUuc3VjY2Vzc1RpbWVzdGFtcCxcbiAgICAgICAgZXJyb3I6IGN1cnJlbnRTdGF0ZS5lcnJvclxuICAgICAgfTtcblxuICAvLyBOb3RlIHRoYXQgZXZlbiBpZiBpc3N1ZUZldGNoIGlzIGZhbHNlLCB0aGUgZXJyb3JIYW5kbGVyU3RhdGUgY291bGQgc3RpbGwgY2hhbmdlLiBTbyB3ZSBzaG91bGRcbiAgLy8gY29tcGFyZSBldmVyeSBmaWVsZCBmcm9tIG9sZCB0byBuZXcgc3RhdGUuXG4gIHJldHVybiBsb2Rhc2guaXNFcXVhbChjdXJyZW50U3RhdGUsIG5ld1N0YXRlKSA/IG51bGwgOiBuZXdTdGF0ZTtcbn1cblxuLyoqXG4gKiBAcmV0dXJucyBBbHdheXMgcmV0dXJucyBhIG5ldyBzdWNjZXNzIHN0YXRlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TmV3U3VjY2Vzc1N0YXRlKCk6IFJlYWRvbmx5PExvYWRpbmdTdGF0ZT4ge1xuICBjb25zdCByZXQ6IExvYWRpbmdTdGF0ZSA9IHtcbiAgICBpc0xvYWRpbmdTdGF0ZTogdHJ1ZSxcbiAgICBsb2FkaW5nOiBmYWxzZSxcbiAgICBzdWNjZXNzOiB0cnVlLFxuICAgIGlzc3VlRmV0Y2g6IGZhbHNlLFxuICAgIC8vIEVhY2ggbG9hZCBhY3Rpb24gd2lsbCBzZXQgdGhpcyBhZ2Fpbiwgc28gaGVyZSB3ZSBqdXN0IHNldCBpdCBiYWNrIHRvIGRlZmF1bHQuXG4gICAgZmFpbHVyZUhhbmRsZXJTdGF0ZTogRmFpbHVyZUhhbmRsZXJTdGF0ZS5JTklULFxuICAgIC8vIFNpbmNlIHRoaXMgdGltZSBmaWVsZCBjaGFuZ2VzLCBhbGwgU3VjY2Vzc0FjdGlvbiB3aWxsIGNhdXNlIGEgc3RhdGUgdXBkYXRlLlxuICAgIHN1Y2Nlc3NUaW1lc3RhbXA6IERhdGUubm93KCksXG4gICAgZXJyb3I6IHVuZGVmaW5lZFxuICB9O1xuXG4gIHJldHVybiByZXQ7XG59XG5cbi8qKlxuICogUmV0dXJucyBhIG5ldyBGYWlsdXJlU3RhdGUuXG4gKlxuICogQHBhcmFtIGFjdGlvbiBVc2VyIGRpc3BhdGNoZWQgRmFpbHVyZUFjdGlvblxuICogQHBhcmFtIGN1cnJlbnRTdGF0ZSBDdXJyZW50IGxvYWRpbmcgc3RhdGVcbiAqIEByZXR1cm5zIEEgbmV3IGxvYWRpbmcgc3RhdGUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXROZXdGYWlsdXJlU3RhdGUoXG4gIGFjdGlvbjogRmFpbHVyZUFjdGlvbiAmIEFjdGlvbixcbiAgY3VycmVudFN0YXRlOiBMb2FkaW5nU3RhdGVcbik6IFJlYWRvbmx5PExvYWRpbmdTdGF0ZT4ge1xuICByZXR1cm4ge1xuICAgIGlzTG9hZGluZ1N0YXRlOiB0cnVlLFxuICAgIGxvYWRpbmc6IGZhbHNlLFxuICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgIGlzc3VlRmV0Y2g6IGZhbHNlLFxuICAgIC8vIExlYWRpbmcgdGhpcyBhcyBpcyBmb3IgdGhlIGdsb2JhbCBmYWlsdXJlIGhhbmRsZXIgdG8gY2hlY2suXG4gICAgZmFpbHVyZUhhbmRsZXJTdGF0ZTogY3VycmVudFN0YXRlLmZhaWx1cmVIYW5kbGVyU3RhdGUsXG4gICAgc3VjY2Vzc1RpbWVzdGFtcDogY3VycmVudFN0YXRlLnN1Y2Nlc3NUaW1lc3RhbXAsXG4gICAgZXJyb3I6IGFjdGlvbi5lcnJvclxuICB9O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29tYmluZUxvYWRpbmdTdGF0ZXMobG9hZGluZ1N0YXRlczogTG9hZGluZ1N0YXRlW10pOiBDb21iaW5lZExvYWRpbmdTdGF0ZSB7XG4gIHJldHVybiB7XG4gICAgbG9hZGluZzogbG9hZGluZ1N0YXRlcy5zb21lKChzdGF0ZSkgPT4gISFzdGF0ZT8ubG9hZGluZyksXG4gICAgc3VjY2VzczogbG9hZGluZ1N0YXRlcy5ldmVyeSgoc3RhdGUpID0+ICEhc3RhdGU/LnN1Y2Nlc3MpLFxuICAgIGVycm9yOiBsb2FkaW5nU3RhdGVzLmZpbmQoKHN0YXRlKSA9PiAhIXN0YXRlPy5lcnJvcik/LmVycm9yXG4gIH07XG59XG4iXX0=