import { HasId , OmitId , Encodable , Decodable , OptionalIdStorable , Storable , PartialStorable , QueryKey , DocumentSnapshot , WhereFilterOp , FieldPath , OrderByDirection , QuerySnapshot , CollectionReference , DocumentReference } from './types' ; import { Context } from './context' ; import { Converter } from './converter' ; import { Query } from './query' ; export class Collection < T extends HasId , S = OmitId < T > > { context : Context ; collectionRef : CollectionReference ; private converter : Converter < T , S > ; constructor ( { context , path , encode , decode } ) { this . context = context ; this . collectionRef = context . firestore . collection ( path ) ; this . converter = new Converter ( { encode , decode } ) ; } toObject ( documentSnapshot ) { return this . converter . decode ( documentSnapshot ) ; } docRef ( id ? ) { if ( id ) return this . collectionRef . doc ( id ) ; return this . collectionRef . doc ( ) ; } async fetch ( id ) { const docRef = this . docRef ( id ) ; const snapshot = ( this . context . tx ) ? await this . context . tx . get ( docRef ) : await docRef . get ( ) ; if ( ! snapshot . exists ) return undefined ; return this . toObject ( snapshot ) ; } async fetchAll ( ) { if ( this . context . tx ) throw new Error ( 'Web SDK transaction.get() does not support QuerySnapshot' ) ; const snapshot = await this . collectionRef . get ( ) ; return snapshot . docs . map ( ( snapshot ) => this . toObject ( snapshot ) ) ; } async add ( obj ) { let docRef ; const doc = this . converter . encode ( obj ) ; if ( this . context . tx ) { docRef = this . docRef ( ) ; this . context . tx . set ( docRef , doc ) ; } else if ( this . context . batch ) { docRef = this . docRef ( ) ; this . context . batch . set ( docRef , doc ) ; } else { docRef = await this . collectionRef . add ( doc ) ; } return docRef . id ; } async set ( obj ) { if ( ! obj . id ) throw new Error ( 'Argument object must have "id" property' ) ; const docRef = this . docRef ( obj . id ) ; const setDoc = this . converter . encode ( obj ) ; if ( this . context . tx ) { this . context . tx . set ( docRef , setDoc ) ; } else if ( this . context . batch ) { this . context . batch . set ( docRef , setDoc ) ; } else { await docRef . set ( setDoc ) ; } return obj . id ; } addOrSet ( obj ) { if ( 'id' in obj ) { return this . set ( obj as Storable < T > ) ; } return this . add ( obj ) ; } async update ( obj ) { if ( ! obj . id ) throw new Error ( 'Argument object must have "id" property' ) ; const docRef = this . docRef ( obj . id ) ; const { id , ... updateDoc } = { ... obj } ; if ( this . context . tx ) { this . context . tx . update ( docRef , updateDoc ) ; } else if ( this . context . batch ) { this . context . batch . update ( docRef , updateDoc ) ; } else { await docRef . update ( updateDoc ) ; } return obj . id ; } async delete ( id ) { const docRef = this . docRef ( id ) ; if ( this . context . tx ) { this . context . tx . delete ( docRef ) ; } else if ( this . context . batch ) { this . context . batch . delete ( docRef ) ; } else { await docRef . delete ( ) ; } return id ; } async bulkAdd ( objects ) { return this . context . runBatch ( async ( ) => { for ( const obj of objects ) { await this . add ( obj ) ; } } ) ; } async bulkSet ( objects ) { return this . context . runBatch ( async ( ) => { for ( const obj of objects ) { await this . set ( obj ) ; } } ) ; } async bulkDelete ( docIds ) { return this . context . runBatch ( async ( ) => { for ( const docId of docIds ) { await this . delete ( docId ) ; } } ) ; } where ( fieldPath , opStr , value ) { const query = this . collectionRef . where ( fieldPath as string | FieldPath , opStr , value ) ; return new Query < T , S > ( this . converter , this . context , query ) ; } orderBy ( fieldPath , directionStr ? ) { const query = this . collectionRef . orderBy ( fieldPath as string | FieldPath , directionStr ) ; return new Query < T , S > ( this . converter , this . context , query ) ; } limit ( limit ) { const query = this . collectionRef . limit ( limit ) ; return new Query < T , S > ( this . converter , this . context , query ) ; } onSnapshot ( callback ) { return this . collectionRef . onSnapshot ( ( _querySnapshot ) => { callback ( _querySnapshot , this . toObject . bind ( this ) ) ; } ) ; } }