import { Id , State , Namespaced , InvalidActionCreator , AttachActionCreator , AttachAction , DetachActionCreator , DetachAction , DeleteAction , DeleteActionCreator , AnyAction , ActionTypes , SelectorTreeSchema , CreateActionCreator , CreateAction , UpdateActionCreator , UpdateAction , MoveAction , MoveActionCreator , MoveAttachedAction , MoveAttachedActionCreator , SortActionCreator , Entity , Compare , SortAttachedActionCreator , BatchActionCreator , SingularAction , SortAction , SetStateActionCreator , InvalidAction , SortAttachedAction , BatchAction , } from './interfaces' ; import { ModelSchemaReader } from './schema' ; import * as messages from './messages' ; import { UpdateActionMethod } from './enums' ; import { cleanData } from './validator' ; export const makeActions = < S extends State > ( schema , namespaced ) => { const BATCH = namespaced ( 'BATCH' ) ; const INVALID = namespaced ( 'INVALID' ) ; const ATTACH = namespaced ( 'ATTACH' ) ; const DETACH = namespaced ( 'DETACH' ) ; const DELETE = namespaced ( 'DELETE' ) ; const CREATE = namespaced ( 'CREATE' ) ; const UPDATE = namespaced ( 'UPDATE' ) ; const MOVE = namespaced ( 'MOVE' ) ; const MOVE_ATTACHED = namespaced ( 'MOVE_ATTACHED' ) ; const SORT = namespaced ( 'SORT' ) ; const SORT_ATTACHED = namespaced ( 'SORT_ATTACHED' ) ; const SET_STATE = namespaced ( 'SET_STATE' ) ; const invalid = ( action , error ) => ( { type : INVALID , error , action } ) ; const batch = ( ... actions ) : BatchAction => { const flattened = flattenActions ( actions ) ; return { type : BATCH , actions : flattened } ; } ; function flattenActions ( actions ) { const accumulator = [ ] ; actions . forEach ( action => { if ( action . type === BATCH ) { const batchAction = action as BatchAction ; const flattened = flattenActions ( batchAction . actions ) ; accumulator . push ( ... flattened ) ; } else { accumulator . push ( action as SingularAction | InvalidAction ) ; } } ) ; return accumulator ; } const attach = ( entityType , id , relation , attachableId , options = { } ) => { const action = { type : ATTACH , entityType , id , relation , attachableId , index : options . index , reciprocalIndex : options . reciprocalIndex } ; if ( ! schema . typeExists ( entityType ) ) { return invalid ( action , messages . entityTypeDne ( entityType ) ) ; } if ( ! schema . type ( entityType ) . resolveRelationKey ( relation ) ) { return invalid ( action , messages . relDne ( entityType , relation ) ) ; } return action ; } ; const detach = ( entityType , id , relation , detachableId ) => { const action = { type : DETACH , entityType , id , relation , detachableId } ; if ( ! schema . typeExists ( entityType ) ) { return invalid ( action , messages . entityTypeDne ( entityType ) ) ; } if ( ! schema . type ( entityType ) . resolveRelationKey ( relation ) ) { return invalid ( action , messages . relDne ( entityType , relation ) ) ; } return action ; } ; const del = ( entityType , id , cascade ? ) => { const action = { type : DELETE , entityType , id , cascade } ; if ( ! schema . typeExists ( entityType ) ) { return invalid ( action , messages . entityTypeDne ( entityType ) ) ; } return action ; } ; const create = ( entityType , id , data = { } , index ? ) => { const action = { type : CREATE , entityType , id , data , index } ; if ( ! schema . typeExists ( entityType ) ) { return invalid ( action , messages . entityTypeDne ( entityType ) ) ; } action . data = cleanData ( data , schema , entityType ) ; return action ; } ; const update = ( entityType , id , data , options = { } ) => { const action = { type : UPDATE , entityType , id , data , method : options . method || UpdateActionMethod . PATCH } ; if ( ! schema . typeExists ( entityType ) ) { return invalid ( action , messages . entityTypeDne ( entityType ) ) ; } action . data = cleanData ( data , schema , entityType ) ; return action ; } ; const move = ( entityType , src , dest ) => { const action = { type : MOVE , entityType , src , dest } ; if ( ! schema . typeExists ( entityType ) ) { return invalid ( action , messages . entityTypeDne ( entityType ) ) ; } if ( src < 0 ) { return invalid ( action , messages . indexLtZero ( 'source' ) ) ; } if ( dest < 0 ) { return invalid ( action , messages . indexLtZero ( 'destination' ) ) ; } return action ; } ; const moveAttached = ( entityType , id , relation , src , dest ) => { const action = { type : MOVE_ATTACHED , entityType , id , relation , src , dest } ; if ( ! schema . typeExists ( entityType ) ) { return invalid ( action , messages . entityTypeDne ( entityType ) ) ; } if ( ! schema . type ( entityType ) . resolveRelationKey ( relation ) ) { return invalid ( action , messages . relDne ( entityType , relation ) ) ; } if ( src < 0 ) { return invalid ( action , messages . indexLtZero ( 'source' ) ) ; } if ( dest < 0 ) { return invalid ( action , messages . indexLtZero ( 'destination' ) ) ; } return action ; } ; const sort = < T extends Entity = Entity > ( entityType , compare ) => { const action = { type : SORT , entityType , compare } as SortAction ; if ( ! schema . typeExists ( entityType ) ) { return invalid ( action as SortAction , messages . entityTypeDne ( entityType ) ) ; } return action ; } ; const sortAttached = < T extends Entity = Entity > ( entityType , id , relation , compare ) => { const action = { type : SORT_ATTACHED , entityType , id , relation , compare } as SortAttachedAction ; if ( ! schema . typeExists ( entityType ) ) { return invalid ( action , messages . entityTypeDne ( entityType ) ) ; } if ( ! schema . type ( entityType ) . resolveRelationKey ( relation ) ) { return invalid ( action , messages . relDne ( entityType , relation ) ) ; } return action ; } ; const setState = ( state ) => { return { type : SET_STATE , state } ; } ; const actionTypes = { BATCH , INVALID , ATTACH , DETACH , DELETE , CREATE , UPDATE , MOVE , MOVE_ATTACHED , SORT , SORT_ATTACHED , SET_STATE } ; const actionCreators = { batch , attach , detach , delete : del , create , update , move , moveAttached , sort , sortAttached , setState } ; const actionUtils = new ActionUtils ( actionTypes ) ; return { actionTypes , actionCreators , actionUtils } ; } ; export class ActionUtils { actionTypes : ActionTypes ; constructor ( actionTypes ) { this . actionTypes = actionTypes ; } isHandleable ( action ) { return Object . values ( this . actionTypes ) . includes ( action . type ) ; } isDerivable ( action ) { const { DETACH , DELETE , ATTACH } = this . actionTypes ; return [ DETACH , DELETE , ATTACH ] . includes ( action . type ) ; } isBatch ( action ) { return action . type === this . actionTypes . BATCH ; } isStateSetter ( action ) { return action . type === this . actionTypes . SET_STATE ; } }