import { ALL } from "../constants" ; import errors from "../errors" ; import { DatabaseOptions , DatabaseSchema , DatabaseState , MapOf , ModelFactory , RecordFactory , Reducer , Schema , Session , SessionOptions , TableMap , TableSchema } from "../types" ; import { ensureArray , ensureParamObject , isEqual , toObject } from "../utils" ; const KEY_SEPARATOR = "_" ; const defaultOptions = { cascadeAsDefault : false , keySeparator : KEY_SEPARATOR } ; const getMappedFunction = < T extends Function > ( map , key , defaultFn ? ) => { if ( ! map ) return defaultFn ; if ( typeof map === "function" ) return map ; else if ( map [ key ] ) return map [ key ] ; else if ( map [ ALL ] ) return map [ ALL ] ; return defaultFn ; } ; export default class Database < T extends TableMap > implements DatabaseSchema { schema : Schema ; tables : TableSchema [ ] ; options : DatabaseOptions ; factory : ModelFactory & RecordFactory ; tableMap : MapOf < TableSchema > ; keySeparator : string ; constructor ( schema , factory , options ? ) { this . schema = ensureParamObject ( "schema" , schema ) ; this . factory = ensureParamObject ( "factory" , factory ) ; this . options = { ... defaultOptions , ... options } ; this . keySeparator = this . options . keySeparator || "" ; this . tables = Object . keys ( schema ) . map ( tableName => this . factory . newTableSchema ( this , tableName , schema [ tableName ] ) ) ; this . tableMap = toObject ( this . tables , table => table . name ) ; this . tables . forEach ( table => table . connect ( this . tableMap ) ) ; } getRecordNormalizer = ( schemaName ) => getMappedFunction ( this . options . onNormalize , schemaName ) ; getPkGenerator = ( schemaName ) => getMappedFunction ( this . options . onGeneratePK , schemaName ) ; getRecordComparer = ( schemaName ) => getMappedFunction ( this . options . onRecordCompare , schemaName , isEqual ) ; getRecordMerger = ( schemaName ) => getMappedFunction ( this . options . onRecordMerge , schemaName ) ; getMissingPkHandler = ( schemaName ) => getMappedFunction ( this . options . onMissingPK , schemaName ) ; combineReducers ( ... reducers ) { return ( state = { } , action ) => this . reduce ( state , action , reducers ) ; } reduce ( state = { } , action ? , reducers ? , ... args ) { const session = this . createSession ( state ) ; ensureArray ( reducers ) . forEach ( reducer => reducer . apply ( this , [ session . tables , action , ... args ] ) ) ; return session . commit ( ) ; } createSession ( state = { } , options ? ) { return this . factory . newSession ( this , state , { readOnly : false , ... options } ) as Session < T > ; } getTableSchema ( name ) { return this . tableMap [ name ] ; } selectTables ( state ) { ensureParamObject ( "state" , state ) ; const tableSchemas = Object . keys ( state ) . filter ( tableName => this . tableMap [ tableName ] ) . map ( tableName => this . tableMap [ tableName ] ) ; const session = this . createSession ( state , { readOnly : true , tableSchemas } ) ; return session . tables ; } selectTable ( tableState , schemaName ? ) { ensureParamObject ( "tableState" , tableState ) ; const { name = schemaName } = tableState ; if ( ! name ) throw new Error ( errors . unknownTableState ( ) ) ; return this . selectTables ( { [ name ] : tableState , } ) [ name ] ; } }