import type { ErrorContext, WorkflowPlugin, WriteContext } from '../types' interface ReduxLike { dispatch: (action: { type: string; payload?: unknown }) => void getState: () => Record } type ActionCreator = (data: unknown) => { type: string; payload?: unknown } export interface ReduxSyncConfig { store: ReduxLike /** Action name suffix for auto-mapped keys. Default: 'set' → dispatches `{type: 'user/set'}` */ defaultAction?: string /** Action name suffix when syncing errors. Default: 'error' → dispatches `{type: 'user/error'}` */ errorAction?: string /** Explicit action creators that override auto-mapping for specific keys */ mapping?: Record /** Keys to skip entirely (auto-map and explicit mapping both skipped) */ exclude?: string[] /** Which result statuses trigger a dispatch. Default: success and partialSuccess. Add 'error' to sync failures. */ syncOn?: Array<'success' | 'partialSuccess' | 'error'> } export interface ReduxSyncAdapter extends WorkflowPlugin { /** Dispatch directly to Redux without going through Zustand or the pipeline */ dispatch: (key: string, data: unknown) => void } export function createReduxSyncAdapter( config: ReduxSyncConfig, ): ReduxSyncAdapter { const { store: reduxStore, defaultAction = 'set', errorAction = 'error', mapping = {}, exclude = [], syncOn = ['success', 'partialSuccess'], } = config function dispatchToRedux(key: string, data: unknown): void { const creator = mapping[key] if (creator) { reduxStore.dispatch(creator(data)) return } const reduxState = reduxStore.getState() if (key in reduxState) { reduxStore.dispatch({ type: `${key}/${defaultAction}`, payload: data }) } } function dispatchErrorToRedux( key: string, error: ErrorContext['error'], ): void { const creator = mapping[key] if (creator) { reduxStore.dispatch(creator(null)) return } const reduxState = reduxStore.getState() if (key in reduxState) { reduxStore.dispatch({ type: `${key}/${errorAction}`, payload: { phase: error.phase, message: error.cause.message }, }) } } return { name: 'redux-sync', afterWrite: async (ctx: WriteContext): Promise => { if (ctx.syncToRedux === false) return if (exclude.includes(ctx.key)) return if (!syncOn.includes(ctx.status)) return dispatchToRedux(ctx.key, ctx.data) }, onError: async (ctx: ErrorContext): Promise => { if (!syncOn.includes('error')) return if (exclude.includes(ctx.key)) return dispatchErrorToRedux(ctx.key, ctx.error) }, dispatch(key: string, data: unknown): void { dispatchToRedux(key, data) }, } }