import R from 'remeda'; const composeMw = (mws) => (api) => (original_next) => mws.reduceRight((next, mw) => mw(api)(next), original_next); export function augmentMiddlewareApi(api, actions, selectors) { const getState = () => api.getState(); const dispatch = (action) => api.dispatch(action); Object.assign( getState, R.mapValues(selectors, (selector) => { return (...args) => { let result = selector(api.getState()); if (typeof result === 'function') return result(...args); return result; }; }), ); Object.assign( dispatch, R.mapValues(actions, (action) => { return (...args) => api.dispatch(action(...args)); }), ); return { ...api, getState, dispatch, actions, selectors, }; } const sliceMw = (slice, mw) => (api) => { const getSliceState = () => get(api.getState(), slice); return mw({ ...api, getState: getSliceState }); }; const middlewareFor = (type, middleware) => (api) => (next) => (action) => { if (type !== '*' && action.type !== type) return next(action); return middleware(api)(next)(action); }; export const effectToMiddleware = (effect, actions, selectors) => { let mw = effect; let action = '*'; if (Array.isArray(effect)) { action = effect[0]; mw = effect[1]; mw = middlewareFor(action, mw); } return (api) => mw(augmentMiddlewareApi(api, actions, selectors)); }; export function buildMiddleware( effects = [], actions = {}, selectors = {}, subduxes = {}, ) { let inner = R.compact( Object.entries(subduxes).map((slice, [{ middleware }]) => slice !== '*' && middleware ? sliceMw(slice, middleware) : null, ), ); const local = effects.map((effect) => effectToMiddleware(effect, actions, selectors), ); let mws = [...local, ...inner]; return composeMw(mws); }