UNPKG

mutoid

Version:

Reactive library for data fetching, caching, state management

146 lines (145 loc) 5.3 kB
import * as E from 'fp-ts/Either'; import * as Eq from 'fp-ts/Eq'; import { constFalse, flow, pipe } from 'fp-ts/function'; // ------------------------------------------------------------------------------------- // guards // ------------------------------------------------------------------------------------- export const isInit = (r) => r._tag === 'init'; export const isSubmitted = (r) => r._tag === 'submitted'; export const isDone = (r) => r._tag === 'done'; export const isFail = (r) => r._tag === 'fail'; export const isPending = (r) => isInit(r) || isSubmitted(r); export const isStarted = (r) => isSubmitted(r) || isFail(r) || isDone(r); export const isAcknowledged = (r) => isFail(r) || isDone(r); // ------------------------------------------------------------------------------------- // constructors // ------------------------------------------------------------------------------------- export const init = { _tag: 'init' }; export const submitted = { _tag: 'submitted' }; export const done = (d) => ({ _tag: 'done', data: d, }); export const fail = (error) => ({ _tag: 'fail', error: error, }); export const ajaxFail = (error) => ({ _tag: 'fail', error: error, }); export const failAppError = (detail) => ({ _tag: 'fail', error: appError(detail), }); export const appError = (detail) => ({ type: 'appError', detail, }); export const ajaxFailOnly = (error) => ({ _tag: 'fail', error: error, }); export const fromEither = E.fold(e => fail(e), done); export const of = done; // ------------------------------------------------------------------------------------- // Destructors // ------------------------------------------------------------------------------------- export const match = (onInit, onSubmitted, onDone, onFail) => (r) => { switch (r._tag) { case 'init': return onInit(); case 'submitted': return onSubmitted(); case 'done': return onDone(r.data); case 'fail': return onFail(r.error); } }; export const matchD = (dodo) => match(dodo.onInit || dodo.onPending, dodo.onSubmitted || dodo.onPending, dodo.onDone, dodo.onFail); export const match_ = (r) => (onInit, onSubmitted, onDone, onFail) => pipe(r, match(onInit, onSubmitted, onDone, onFail)); // ------------------------------------------------------------------------------------- // instances // ------------------------------------------------------------------------------------- /* istanbul ignore next */ const _map = (fa, f) => pipe(fa, map(f)); /* istanbul ignore next */ const _bimap = (fa, f, g) => pipe(fa, bimap(f, g)); /* istanbul ignore next */ const _mapLeft = (fa, f) => pipe(fa, mapLeft(f)); /* istanbul ignore next */ const _ap = (fa, f) => pipe(fa, ap(f)); /* istanbul ignore next */ const _chain = (fa, f) => pipe(fa, chain(f)); export const URI = 'Resource'; export function getShow(SE, SA) { return { show: ma => { switch (ma._tag) { case 'done': return `done(${SA.show(ma.data)})`; case 'fail': return `fail(${SE.show(ma.error)})`; case 'submitted': return 'submitted'; case 'init': return 'init'; } }, }; } export function getEq(EE, EA) { return Eq.fromEquals((a, b) => pipe(a, match(() => b._tag === 'init', () => b._tag === 'submitted', fa => pipe(b, match(constFalse, constFalse, fb => EA.equals(fa, fb), constFalse)), sa => pipe(b, match(constFalse, constFalse, constFalse, sb => EE.equals(sa, sb)))))); } export const Functor = { URI, map: _map, }; export const Apply = { URI, map: _map, ap: _ap, }; export const Bifunctor = { URI, bimap: _bimap, mapLeft: _mapLeft, }; export const Applicative = { URI, map: _map, ap: _ap, of, }; export const Monad = { URI, map: _map, ap: _ap, of, chain: _chain, }; // ------------------------------------------------------------------------------------- // combinators // ------------------------------------------------------------------------------------- export function swap(ma) { return pipe(ma, match(() => init, () => submitted, a => fail(a), done)); } export function orElseW(onFail) { return ma => (isFail(ma) ? onFail(ma.error) : ma); } export const orElse = orElseW; export const filterOrElseW = (predicate, onFalse) => chainW(a => (predicate(a) ? done(a) : fail(onFalse(a)))); export const filterOrElse = filterOrElseW; // ------------------------------------------------------------------------------------- // type class members // ------------------------------------------------------------------------------------- export const map = f => fa => isDone(fa) ? done(f(fa.data)) : fa; export const bimap = (f, g) => (fa) => pipe(fa, match(() => init, () => submitted, flow(g, done), flow(f, fail))); export const mapLeft = f => fa => isFail(fa) ? fail(f(fa.error)) : fa; export const apW = fa => fab => // eslint-disable-next-line no-nested-ternary isDone(fab) ? (isDone(fa) ? done(fab.data(fa.data)) : fa) : fab; export const ap = apW; export const chainW = (f) => (ma) => isDone(ma) ? f(ma.data) : ma; export const chain = chainW;