@api-buddy/firebase
Version:
Firebase integration for API Buddy
1 lines • 17.7 kB
Source Map (JSON)
{"version":3,"sources":["../src/firebase-setup.ts","../src/firestore-operations.ts","../src/hooks/useFirestore.ts"],"sourcesContent":["import { initializeApp, getApps, getApp, FirebaseApp } from 'firebase/app';\nimport { getAuth } from 'firebase/auth';\nimport { getFirestore } from 'firebase/firestore';\nimport { getStorage } from 'firebase/storage';\n\n// Firebase configuration\nconst firebaseConfig = {\n // These should be set via environment variables in a real app\n apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,\n authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,\n projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,\n storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,\n messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,\n appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,\n measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,\n};\n\n// Initialize Firebase\nlet firebaseApp: FirebaseApp;\n\nif (!getApps().length) {\n firebaseApp = initializeApp(firebaseConfig);\n} else {\n firebaseApp = getApp();\n}\n\n// Initialize Firebase services\nconst auth = getAuth(firebaseApp);\nconst db = getFirestore(firebaseApp);\nconst storage = getStorage(firebaseApp);\n\nexport { firebaseApp, auth, db, storage };\n\n// Utility functions\nexport const isFirebaseInitialized = () => {\n return getApps().length > 0;\n};\n\n// Re-export commonly used Firebase types\nexport type { User } from 'firebase/auth';\nexport type { DocumentData, QueryDocumentSnapshot } from 'firebase/firestore';\nexport type { FirebaseStorage, UploadResult } from 'firebase/storage';\n","import {\n collection,\n doc,\n getDoc,\n getDocs,\n setDoc,\n updateDoc,\n deleteDoc,\n query,\n where,\n orderBy,\n limit as limitQuery,\n startAfter,\n endBefore,\n limitToLast,\n onSnapshot,\n writeBatch,\n runTransaction,\n type QueryConstraint,\n type DocumentData,\n type QueryDocumentSnapshot,\n type Query,\n type CollectionReference,\n type Firestore,\n type Unsubscribe,\n type DocumentSnapshot,\n type WhereFilterOp,\n type OrderByDirection,\n} from 'firebase/firestore';\nimport { db } from './firebase-setup';\nimport type { WithId } from './types';\nimport type { \n QueryOptions, \n WhereClause, \n OrderByClause \n} from './types/query-options';\n\n// Re-export commonly used types for backward compatibility\nexport type { WithId } from './types';\nexport type { QueryOptions, WhereClause, OrderByClause } from './types/query-options';\n\nexport class FirestoreService<T extends DocumentData> {\n private readonly collectionRef: CollectionReference<T>;\n\n constructor(collectionName: string, private readonly firestore: Firestore = db) {\n this.collectionRef = collection(this.firestore, collectionName) as CollectionReference<T>;\n }\n\n // Create\n async create(id: string, data: T): Promise<WithId<T>> {\n const docRef = doc(this.collectionRef, id);\n await setDoc(docRef, data);\n return { id, ...data };\n }\n\n // Read\n async get(id: string): Promise<WithId<T> | null> {\n const docRef = doc(this.collectionRef, id);\n const docSnap = await getDoc(docRef);\n return docSnap.exists() ? { id: docSnap.id, ...docSnap.data() } : null;\n }\n\n // Update\n async update(id: string, data: Partial<T>): Promise<void> {\n const docRef = doc(this.collectionRef, id);\n await updateDoc(docRef, data as any);\n }\n\n // Delete\n async delete(id: string): Promise<void> {\n const docRef = doc(this.collectionRef, id);\n await deleteDoc(docRef);\n }\n\n // Query with filters\n async query(options: QueryOptions<T> = {}): Promise<WithId<T>[]> {\n let q: Query<T> = this.collectionRef;\n const queryConstraints: QueryConstraint[] = [];\n\n // Apply where clauses\n if (options.where && options.where.length > 0) {\n for (const clause of options.where) {\n queryConstraints.push(\n where(\n clause.field as string,\n clause.operator,\n clause.value\n )\n );\n }\n }\n\n // Apply order by\n if (options.orderBy && options.orderBy.length > 0) {\n for (const clause of options.orderBy) {\n queryConstraints.push(\n orderBy(\n clause.field as string,\n clause.direction || 'asc'\n )\n );\n }\n }\n\n // Apply pagination\n if (options.limit) {\n if (options.last) {\n queryConstraints.push(limitToLast(options.limit));\n } else {\n queryConstraints.push(limitQuery(options.limit));\n }\n }\n\n if (options.startAfter) {\n queryConstraints.push(startAfter(options.startAfter));\n }\n\n if (options.endBefore) {\n queryConstraints.push(endBefore(options.endBefore));\n }\n\n // Apply all constraints\n if (queryConstraints.length > 0) {\n q = query(this.collectionRef, ...queryConstraints);\n }\n\n const querySnapshot = await getDocs(q);\n return querySnapshot.docs.map((doc) => ({\n id: doc.id,\n ...doc.data(),\n } as WithId<T>));\n }\n\n // Subscribe to real-time updates\n subscribe(\n callback: (data: WithId<T>[]) => void,\n options: Omit<QueryOptions<T>, 'startAfter' | 'endBefore' | 'last'> = {}\n ): Unsubscribe {\n let q: Query<T> = this.collectionRef;\n const queryConstraints: QueryConstraint[] = [];\n\n // Apply where clauses\n if (options.where && options.where.length > 0) {\n for (const clause of options.where) {\n queryConstraints.push(\n where(\n clause.field as string,\n clause.operator,\n clause.value\n )\n );\n }\n }\n\n\n // Apply order by\n if (options.orderBy && options.orderBy.length > 0) {\n for (const clause of options.orderBy) {\n queryConstraints.push(\n orderBy(\n clause.field as string,\n clause.direction || 'asc'\n )\n );\n }\n }\n // Apply limit\n if (options.limit) {\n queryConstraints.push(limitQuery(options.limit));\n }\n\n // Apply all constraints\n if (queryConstraints.length > 0) {\n q = query(this.collectionRef, ...queryConstraints);\n }\n\n return onSnapshot(q, (querySnapshot) => {\n const data = querySnapshot.docs.map((doc) => ({\n id: doc.id,\n ...doc.data(),\n } as WithId<T>));\n callback(data);\n });\n }\n\n // Batch operations\n static createBatch() {\n return writeBatch(db);\n }\n\n // Transaction support\n static async runTransaction<T>(\n updateFunction: (transaction: any) => Promise<T>\n ): Promise<T> {\n return runTransaction(db, updateFunction);\n }\n}\n","import { useEffect, useRef, useState } from 'react';\nimport { FirestoreService } from '../firestore-operations';\nimport type { WithId } from '../types';\nimport type { QueryOptions, WhereClause, OrderByClause } from '../types/query-options';\n\n/**\n * Hook to fetch a single document from Firestore\n * @param service Instance of FirestoreService\n * @param id Document ID to fetch\n * @returns { data, loading, error }\n */\nexport function useDocument<T extends object>(\n service: FirestoreService<T>,\n id: string\n) {\n const [data, setData] = useState<WithId<T> | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n const fetchData = async () => {\n try {\n setLoading(true);\n const doc = await service.get(id);\n setData(doc);\n } catch (err) {\n console.error('Error fetching document:', err);\n setError(err as Error);\n } finally {\n setLoading(false);\n }\n };\n\n if (id) {\n fetchData();\n } else {\n setLoading(false);\n }\n }, [service, id]);\n\n return { data, loading, error };\n}\n\n/**\n * Hook to fetch a collection from Firestore with pagination support\n * @param service Instance of FirestoreService\n * @param options Query options (where, orderBy, limit)\n * @returns { data, loading, error, hasMore, fetchMore, refresh }\n */\nexport function useCollection<T extends object>(\n service: FirestoreService<T>,\n options: Omit<QueryOptions<T>, 'startAfter' | 'endBefore' | 'last'> = {}\n) {\n const [data, setData] = useState<WithId<T>[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const [hasMore, setHasMore] = useState(true);\n const lastDocRef = useRef<any>(null);\n\n const fetchData = async (reset = false) => {\n if ((!hasMore && !reset) || loading) return;\n \n try {\n setLoading(true);\n \n const queryOptions = {\n ...options,\n ...(lastDocRef.current && !reset && { startAfter: lastDocRef.current })\n };\n \n // We need to cast here because of TypeScript limitations with spread operator and generics\n const result = await (service as any).query(queryOptions);\n \n setData(prev => reset ? result : [...prev, ...result]);\n setHasMore(result.length === (options.limit || 20));\n \n if (result.length > 0) {\n lastDocRef.current = result[result.length - 1];\n }\n } catch (err) {\n console.error('Error fetching collection:', err);\n setError(err as Error);\n } finally {\n setLoading(false);\n }\n };\n\n const refresh = () => {\n lastDocRef.current = null;\n return fetchData(true);\n };\n\n useEffect(() => {\n refresh();\n }, [JSON.stringify(options)]); // Re-run when options change\n\n return { \n data, \n loading, \n error, \n hasMore, \n fetchMore: () => fetchData(false), \n refresh \n };\n}\n\n/**\n * Hook to subscribe to real-time updates for a collection\n * @param service Instance of FirestoreService\n * @param options Query options (where, orderBy, limit)\n * @returns { data, loading, error }\n */\nexport function useRealtimeCollection<T extends object>(\n service: FirestoreService<T>,\n options: Omit<QueryOptions<T>, 'startAfter' | 'endBefore' | 'last'> = {}\n) {\n const [data, setData] = useState<WithId<T>[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const unsubscribeRef = useRef<() => void>();\n const isMounted = useRef(true);\n\n useEffect(() => {\n isMounted.current = true;\n setLoading(true);\n \n try {\n unsubscribeRef.current = service.subscribe(\n (items) => {\n if (isMounted.current) {\n setData(items);\n setLoading(false);\n }\n },\n options\n );\n } catch (err) {\n console.error('Error setting up realtime subscription:', err);\n if (isMounted.current) {\n setError(err as Error);\n setLoading(false);\n }\n }\n\n // Cleanup subscription on unmount\n return () => {\n isMounted.current = false;\n if (unsubscribeRef.current) {\n unsubscribeRef.current();\n }\n };\n }, [service, JSON.stringify(options)]);\n\n return { data, loading, error };\n}\n\n/**\n * Hook to get a Firestore service instance\n * @param collectionName Name of the Firestore collection\n * @returns Instance of FirestoreService\n */\nexport function useFirestoreService<T extends object>(collectionName: string) {\n const serviceRef = useRef<FirestoreService<T>>();\n \n if (!serviceRef.current) {\n serviceRef.current = new FirestoreService<T>(collectionName);\n }\n \n return serviceRef.current;\n}\n"],"mappings":"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","names":["initializeApp","getApps","getApp","getAuth","getFirestore","getStorage","firebaseConfig","firebaseApp","auth","db","storage","isFirebaseInitialized","getApps","collection","doc","getDoc","getDocs","setDoc","updateDoc","deleteDoc","query","where","orderBy","limitQuery","startAfter","endBefore","limitToLast","onSnapshot","writeBatch","runTransaction","FirestoreService","collectionName","firestore","db","collection","id","data","docRef","doc","setDoc","docSnap","getDoc","updateDoc","deleteDoc","options","q","queryConstraints","clause","where","orderBy","limitToLast","limitQuery","startAfter","endBefore","query","getDocs","callback","onSnapshot","querySnapshot","writeBatch","updateFunction","runTransaction","useEffect","useRef","useState","useDocument","service","id","data","setData","useState","loading","setLoading","error","setError","useEffect","doc","err","useCollection","options","hasMore","setHasMore","lastDocRef","useRef","fetchData","reset","queryOptions","result","prev","refresh","useRealtimeCollection","unsubscribeRef","isMounted","items","useFirestoreService","collectionName","serviceRef","FirestoreService"]}