import { ActionCreator } from 'ts-action'; type MaybePayload

= P extends object | string | boolean | number ? { payload: P; } : { payload?: P }; export type Action = { type: T; } & MaybePayload

; export type Dictionary = { [key: string]: T }; export type Mutation = ( payload: A['payload'], action: A ) => (state: S) => S; export type ActionPayloadGenerator = (...args: any[]) => any; export type MutationEntry = [ ActionCreator | string, Mutation>, boolean? ]; export type GenericActions = Dictionary< ActionCreator { type: string }> >; export type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never; export type StateOf = D extends { initial: infer I } ? I : unknown; export type DuxStateCoduxes = C extends Array ? UnionToIntersection> : unknown; export type DuxStateSubduxes = C extends { '*': infer I } ? { [key: string]: StateOf; [index: number]: StateOf; } : C extends object ? { [K in keyof C]: StateOf } : unknown; type DuxStateGlobSub = S extends { '*': infer I } ? StateOf : unknown; type LocalDuxState = S extends never[] ? unknown[] : S; /** @ignore */ type AggDuxState2 = (L extends never[] ? Array> : L & DuxStateSubduxes) & DuxStateCoduxes; /** @ignore */ export type AggDuxState = unknown extends O ? AggDuxState2 : O; type SelectorsOf = C extends { selectors: infer S } ? S : unknown; /** @ignore */ export type DuxSelectorsSubduxes = C extends object ? UnionToIntersection> : unknown; /** @ignore */ export type DuxSelectorsCoduxes = C extends Array ? UnionToIntersection> : unknown; type MaybeReturnType = X extends (...args: any) => any ? ReturnType : unknown; type RebaseSelector = { [K in keyof X]: (state: S) => MaybeReturnType; }; type ActionsOf = C extends { actions: infer A } ? A : {}; type DuxActionsSubduxes = C extends object ? ActionsOf : unknown; export type DuxActionsCoduxes = C extends Array ? UnionToIntersection> : {}; type ItemsOf = C extends object ? C[keyof C] : unknown; export type DuxActions = A extends object ? A : UnionToIntersection< ActionsOf | ItemsOf> >; export type DuxSelectors = unknown extends X ? RebaseSelector< S, C['selectors'] & DuxSelectorsCoduxes & DuxSelectorsSubduxes > : X; export type Dux = { subduxes: Dictionary; coduxes: Dux[]; initial: AggDuxState; actions: A; subscriptions: Function[]; }; /** * Configuration object given to Updux's constructor. * * #### arguments * * ##### initial * * Default initial state of the reducer. If applicable, is merged with * the subduxes initial states, with the parent having precedence. * * If not provided, defaults to an empty object. * * ##### actions * * [Actions](/concepts/Actions) used by the updux. * * ```js * import { dux } from 'updux'; * import { action, payload } from 'ts-action'; * * const bar = action('BAR', payload()); * const foo = action('FOO'); * * const myDux = dux({ * actions: { * bar * }, * mutations: [ * [ foo, () => state => state ] * ] * }); * * myDux.actions.foo({ x: 1, y: 2 }); // => { type: foo, x:1, y:2 } * myDux.actions.bar(2); // => { type: bar, payload: 2 } * ``` * * New actions used directly in mutations and effects will be added to the * dux actions -- that is, they will be accessible via `dux.actions` -- but will * not appear as part of its Typescript type. * * ##### selectors * * Dictionary of selectors for the current updux. The updux also * inherit its subduxes' selectors. * * The selectors are available via the class' getter. * * ##### mutations * * mutations: [ * [ action, mutation, isSink ], * ... * ] * * or * * mutations: { * action: mutation, * ... * } * * List of mutations for assign to the dux. If you want Typescript goodness, you * probably want to use `addMutation()` instead. * * In its generic array-of-array form, * each mutation tuple contains: the action, the mutation, * and boolean indicating if this is a sink mutation. * * The action can be an action creator function or a string. If it's a string, it's considered to be the * action type and a generic `action( actionName, payload() )` creator will be * generated for it. If an action is not already defined in the `actions` * parameter, it'll be automatically added. * * The pseudo-action type `*` can be used to match any action not explicitly matched by other mutations. * * ```js * const todosUpdux = updux({ * mutations: { * add: todo => state => [ ...state, todo ], * done: done_id => u.map( u.if( ({id} => id === done_id), {done: true} ) ), * '*' (payload,action) => state => { * console.warn( "unexpected action ", action.type ); * return state; * }, * } * }); * ``` * * The signature of the mutations is `(payload,action) => state => newState`. * It is designed to play well with `Updeep` (and [Immer](https://immerjs.github.io/immer/docs/introduction)). This way, instead of doing * * ```js * mutation: { * renameTodo: newName => state => { ...state, name: newName } * } * ``` * * we can do * * ```js * mutation: { * renameTodo: newName => u({ name: newName }) * } * ``` * * The final argument is the optional boolean `isSink`. If it is true, it'll * prevent subduxes' mutations on the same action. It defaults to `false`. * * The object version of the argument can be used as a shortcut when all actions * are strings. In that case, `isSink` is `false` for all mutations. * * ##### groomMutations * * Function that can be provided to alter all local mutations of the updux * (the mutations of subduxes are left untouched). * * Can be used, for example, for Immer integration: * * ```js * import Updux from 'updux'; * import { produce } from 'Immer'; * * const updux = new Updux({ * initial: { counter: 0 }, * groomMutations: mutation => (...args) => produce( mutation(...args) ), * mutations: { * add: (inc=1) => draft => draft.counter += inc * } * }); * ``` * * Or perhaps for debugging: * * ```js * import Updux from 'updux'; * * const updux = new Updux({ * initial: { counter: 0 }, * groomMutations: mutation => (...args) => state => { * console.log( "got action ", args[1] ); * return mutation(...args)(state); * } * }); * ``` * ##### subduxes * * Object mapping slices of the state to sub-upduxes. In addition to creating * sub-reducers for those slices, it'll make the parend updux inherit all the * actions and middleware from its subduxes. * * For example, if in plain Redux you would do * * ```js * import { combineReducers } from 'redux'; * import todosReducer from './todos'; * import statisticsReducer from './statistics'; * * const rootReducer = combineReducers({ * todos: todosReducer, * stats: statisticsReducer, * }); * ``` * * then with Updux you'd do * * ```js * import { updux } from 'updux'; * import todos from './todos'; * import statistics from './statistics'; * * const rootUpdux = updux({ * subduxes: { * todos, * statistics * } * }); * ``` * * ##### effects * * Array of arrays or plain object defining asynchronous actions and side-effects triggered by actions. * The effects themselves are Redux middleware, with the `dispatch` * property of the first argument augmented with all the available actions. * * ``` * updux({ * effects: { * fetch: ({dispatch}) => next => async (action) => { * next(action); * * let result = await fetch(action.payload.url).then( result => result.json() ); * dispatch.fetchSuccess(result); * } * } * }); * ``` * * @example * * ``` * import Updux from 'updux'; * import { actions, payload } from 'ts-action'; * import u from 'updeep'; * * const todoUpdux = new Updux({ * initial: { * done: false, * note: "", * }, * actions: { * finish: action('FINISH', payload()), * edit: action('EDIT', payload()), * }, * mutations: [ * [ edit, note => u({note}) ] * ], * selectors: { * getNote: state => state.note * }, * groomMutations: mutation => transform(mutation), * subduxes: { * foo * }, * effects: { * finish: () => next => action => { * console.log( "Woo! one more bites the dust" ); * } * } * }) * ``` */ export type UpduxConfig = Partial<{ initial: unknown /** foo */; subduxes: Dictionary; coduxes: Dux[]; actions: Dictionary; selectors: Dictionary; mutations: any; groomMutations: (m: Mutation) => Mutation; effects: any; subscriptions: Function[]; }>; export type Upreducer = (action: Action) => (state: S) => S; /** @ignore */ export interface UpduxMiddlewareAPI> { dispatch: Function; getState(): S; selectors: X; actions: Dictionary; } export type UpduxMiddleware, A = Action> = ( api: UpduxMiddlewareAPI ) => (next: Function) => (action: A) => any; export type Selector = (state: S) => unknown; export type DuxState = D extends { initial: infer S } ? S : unknown;