import { produce as createNextState, isDraft } from 'immer' import type { Draft } from 'immer' import type { EntityId, DraftableEntityState, PreventAny } from './models' import type { PayloadAction } from '../createAction' import { isFSA } from '../createAction' export const isDraftTyped = isDraft as ( value: T | Draft, ) => value is Draft export function createSingleArgumentStateOperator( mutator: (state: DraftableEntityState) => void, ) { const operator = createStateOperator( (_: undefined, state: DraftableEntityState) => mutator(state), ) return function operation>( state: PreventAny, ): S { return operator(state as S, undefined) } } export function createStateOperator( mutator: (arg: R, state: DraftableEntityState) => void, ) { return function operation>( state: S, arg: R | PayloadAction, ): S { function isPayloadActionArgument( arg: R | PayloadAction, ): arg is PayloadAction { return isFSA(arg) } const runMutator = (draft: DraftableEntityState) => { if (isPayloadActionArgument(arg)) { mutator(arg.payload, draft) } else { mutator(arg, draft) } } if (isDraftTyped>(state)) { // we must already be inside a `createNextState` call, likely because // this is being wrapped in `createReducer` or `createSlice`. // It's safe to just pass the draft to the mutator. runMutator(state) // since it's a draft, we'll just return it return state } return createNextState(state, runMutator) } }