import { ModelSchemaReader } from './schema' ; import { Id , State , Selectors , Entity , GetAllAttachedIds , SelectorTreeSchema , EntityTreeNode , PublicSelectors , InternalSelectors , } from './interfaces' ; const emptyIds = [ ] ; const emptyEntities = { } ; export const makeSelectors = < S extends State > ( schema ) : Selectors < S > => { const getIds = ( state , args ) : Id [ ] => { const typeExists = schema . typeExists ( args . type ) ; if ( ! typeExists ) { return emptyIds ; } const ids = state . ids [ args . type ] as Id [ ] ; return ids || emptyIds ; } ; const getEntities = < E extends Entity > ( state , args ) : Record < Id , E > => { const typeExists = schema . typeExists ( args . type ) ; if ( ! typeExists ) { return emptyEntities ; } const entities = state . entities [ args . type ] ; return ( entities || emptyEntities ) as Record < Id , E > ; } ; const getEntity = < E extends Entity > ( state , args ) : E | undefined => { const typeExists = schema . typeExists ( args . type ) ; if ( ! typeExists ) { return undefined ; } const entities = getEntities < E > ( state , args ) ; return entities [ args . id ] as E ; } ; const getAttached = < E extends Id [ ] | Id > ( state , args ) : E | undefined => { const entitySchema = schema . type ( args . type ) ; if ( ! entitySchema ) { return undefined ; } const relationKey = entitySchema . resolveRelationKey ( args . relation ) ; if ( ! relationKey ) { return undefined ; } const entity = getEntity ( state , args ) ; if ( ! entity ) { return undefined ; } return entity [ relationKey ] ; } ; const getAttachedIds = ( state , args ) => { const { type , id , relation } = args ; const relationKey = schema . type ( type ) . resolveRelationKey ( relation ) ; if ( ! relationKey ) { return [ ] ; } const attached = getAttached ( state , { type , id , relation : relationKey } ) ; if ( attached && typeof attached === 'string' ) { return [ attached ] ; } if ( attached && Array . isArray ( attached ) ) { return attached ; } return [ ] ; } ; const getAllAttachedIds = ( state , { type , id } ) => { const entitySchema = schema . type ( type ) ; if ( ! entitySchema ) { return { } ; } const allAttachedIds = { } ; const relationKeys = entitySchema . getRelationKeys ( ) ; for ( let relationKey of relationKeys ) { const attachedIds = getAttachedIds ( state , { type , id , relation : relationKey } ) ; if ( attachedIds . length ) { allAttachedIds [ relationKey ] = attachedIds ; } } return allAttachedIds ; } ; const getEntityTree = ( state , args ) : EntityTreeNode [ ] => { const { type , id , schema : selectorSchema } = args ; if ( ! schema . type ( type ) ) { return [ ] ; } const rootEntity = getEntity < S > ( state , { type , id } ) ; if ( ! rootEntity ) { return [ ] ; } const nodes = recursivelyGetNodes ( state , type , id , selectorSchema ) ; return Object . values ( nodes ) ; } ; const recursivelyGetNodes = ( state , type , id , selectorSchema , nodes = { } ) : Record < string , EntityTreeNode > => { const entity = getEntity ( state , { type , id } ) ; if ( ! entity ) { return nodes ; } nodes [ `${ type }.${ id }` ] = { id , type , entity } ; if ( typeof selectorSchema === 'function' ) { selectorSchema = selectorSchema ( ) ; } for ( let [ relation , nestedSelectorSchema ] of Object . entries ( selectorSchema ) ) { const relationKey = schema . type ( type ) . resolveRelationKey ( relation ) ; const relationType = schema . type ( type ) . resolveRelationType ( relation ) ; if ( relationKey && relationType ) { const attachedIds = getAttachedIds ( state , { type , id , relation : relationKey } ) ; for ( let attachedId of attachedIds ) { recursivelyGetNodes ( state , relationType , attachedId , nestedSelectorSchema , nodes ) ; } } } return nodes ; } ; return { getIds , getEntities , getEntity , getAttached , getAllAttachedIds , getEntityTree } ; } ; export const getPublicSelectors = < S extends State > ( selectors ) : PublicSelectors < S > => { const { getIds , getEntities , getEntity } = selectors ; return { getIds , getEntities , getEntity } ; } ; export const getInternalSelectors = < S extends State > ( selectors ) : InternalSelectors < S > => { const { getEntityTree , getAllAttachedIds , getAttached } = selectors ; return { getEntityTree , getAllAttachedIds , getAttached } ; } ;