import * as Redux from 'redux' ; import { ITrackHistoryState , IJsonDiff , IStoreEnhancer , IReduxCreateStore , ITrackHistoryStoreEnhancer } from './ITypes' ; let jsonDiffPatch = require ( 'jsondiffpatch' ) ; let initialState = { stateHistory : { history : [ ] , timestamps : [ ] , actions : [ ] , current : - 1 , } } ; function calculateDiff ( lhs , rhs , removeKeys = [ ] ) { const lhsClone = Object . assign ( { } , lhs ) ; const rhsClone = Object . assign ( { } , rhs ) ; removeKeys . forEach ( key => { delete lhsClone [ key ] ; delete rhsClone [ key ] ; } ) ; return jsonDiffPatch . diff ( lhsClone , rhsClone ) ; } /**
 * Store enhancer that adds state history (stored within the state)
 */ const trackHistoryStoreEnhancer = ( hydratingState = initialState ) => ( createStore ) => ( reducer , state = hydratingState ) => createStore ( trackReducerChanges ( reducer , state ) ) ; /**
 * Higher-order reducer-wrapper to log all state changes generated by said reducer
 *
 * TODO: When hydrateState is passed, we replace the history state by the given one after the first reducer has run
 */ function trackReducerChanges ( reducer , hydrateState = initialState ) { return function ( state = hydrateState , action ) { const newAppState = reducer ( state , action ) ; if ( state === newAppState ) { return newAppState ; } if ( action . meta === 'SKIP_HISTORY_TRACKING' ) { return newAppState ; } const delta = calculateDiff ( state , newAppState , [ 'stateHistory' ] ) ; if ( typeof delta === 'undefined' ) { return newAppState ; } const clonedState = Object . assign ( { } , newAppState , { stateHistory : Object . assign ( { } , newAppState . stateHistory , { history : Object . assign ( [ ] , newAppState . stateHistory . history ) , timestamps : Object . assign ( [ ] , newAppState . stateHistory . timestamps ) , actions : Object . assign ( [ ] , newAppState . stateHistory . actions ) , current : newAppState . stateHistory . current } ) } ) ; clonedState . stateHistory . history . push ( delta ) ; clonedState . stateHistory . timestamps . push ( Date . now ( ) ) ; clonedState . stateHistory . actions . push ( action . type ) ; clonedState . stateHistory . current ++ ; return clonedState ; } ; } export default trackHistoryStoreEnhancer ;