@ponceca/firestore-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +692 -0
- package/dist/app.d.mts +51 -0
- package/dist/app.d.ts +51 -0
- package/dist/app.js +16 -0
- package/dist/app.js.map +1 -0
- package/dist/app.mjs +16 -0
- package/dist/app.mjs.map +1 -0
- package/dist/auth/index.d.mts +43 -0
- package/dist/auth/index.d.ts +43 -0
- package/dist/auth/index.js +18 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/index.mjs +18 -0
- package/dist/auth/index.mjs.map +1 -0
- package/dist/chunk-2RQUHE2K.js +719 -0
- package/dist/chunk-2RQUHE2K.js.map +1 -0
- package/dist/chunk-4CV4JOE5.js +27 -0
- package/dist/chunk-4CV4JOE5.js.map +1 -0
- package/dist/chunk-57XXMSJA.js +65 -0
- package/dist/chunk-57XXMSJA.js.map +1 -0
- package/dist/chunk-6J3LNKUQ.js +213 -0
- package/dist/chunk-6J3LNKUQ.js.map +1 -0
- package/dist/chunk-BXV7KTHB.js +645 -0
- package/dist/chunk-BXV7KTHB.js.map +1 -0
- package/dist/chunk-C3PCJJX4.mjs +645 -0
- package/dist/chunk-C3PCJJX4.mjs.map +1 -0
- package/dist/chunk-C6SKWUQV.mjs +213 -0
- package/dist/chunk-C6SKWUQV.mjs.map +1 -0
- package/dist/chunk-DXPQJR5D.mjs +2469 -0
- package/dist/chunk-DXPQJR5D.mjs.map +1 -0
- package/dist/chunk-MRVKMKSO.mjs +65 -0
- package/dist/chunk-MRVKMKSO.mjs.map +1 -0
- package/dist/chunk-NFEGQTCC.mjs +27 -0
- package/dist/chunk-NFEGQTCC.mjs.map +1 -0
- package/dist/chunk-RSBBZLDE.js +128 -0
- package/dist/chunk-RSBBZLDE.js.map +1 -0
- package/dist/chunk-RZWTSZSJ.js +2469 -0
- package/dist/chunk-RZWTSZSJ.js.map +1 -0
- package/dist/chunk-SZKHE2TQ.mjs +719 -0
- package/dist/chunk-SZKHE2TQ.mjs.map +1 -0
- package/dist/chunk-ZJ4A4Y2T.mjs +128 -0
- package/dist/chunk-ZJ4A4Y2T.mjs.map +1 -0
- package/dist/firestore/index.d.mts +1476 -0
- package/dist/firestore/index.d.ts +1476 -0
- package/dist/firestore/index.js +156 -0
- package/dist/firestore/index.js.map +1 -0
- package/dist/firestore/index.mjs +156 -0
- package/dist/firestore/index.mjs.map +1 -0
- package/dist/http-A2S5CWEV.js +10 -0
- package/dist/http-A2S5CWEV.js.map +1 -0
- package/dist/http-SZFONH6Z.mjs +10 -0
- package/dist/http-SZFONH6Z.mjs.map +1 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +171 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +171 -0
- package/dist/index.mjs.map +1 -0
- package/dist/indexeddb-mutation-queue-5EB7C2D5.js +192 -0
- package/dist/indexeddb-mutation-queue-5EB7C2D5.js.map +1 -0
- package/dist/indexeddb-mutation-queue-M2MAH4E4.mjs +192 -0
- package/dist/indexeddb-mutation-queue-M2MAH4E4.mjs.map +1 -0
- package/dist/indexeddb-store-D23ZY3PR.mjs +162 -0
- package/dist/indexeddb-store-D23ZY3PR.mjs.map +1 -0
- package/dist/indexeddb-store-DNWBZUQE.js +162 -0
- package/dist/indexeddb-store-DNWBZUQE.js.map +1 -0
- package/dist/snapshot-MCQVLVHL.js +22 -0
- package/dist/snapshot-MCQVLVHL.js.map +1 -0
- package/dist/snapshot-ZWZFIFZD.mjs +22 -0
- package/dist/snapshot-ZWZFIFZD.mjs.map +1 -0
- package/dist/types-meoR-Ecp.d.mts +269 -0
- package/dist/types-meoR-Ecp.d.ts +269 -0
- package/package.json +78 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/persistence/local-store.ts","../src/persistence/mutation-queue.ts","../src/persistence/persistence-manager.ts","../src/firestore/firestore.ts"],"sourcesContent":["/**\r\n * Firestore SDK - Local Store (Cache de Documentos)\r\n * Almacena documentos en memoria para acceso offline y compensación de latencia.\r\n * Equivalente al IndexedDB/SQLite store del SDK real de Firebase.\r\n */\r\n\r\nimport type { DocumentData } from '../firestore/types';\r\nimport type { CachedDocument, CachedQuery } from './types';\r\n\r\n/**\r\n * Cache local de documentos y queries.\r\n * Usa Map en memoria — ligero y rápido.\r\n */\r\nexport class LocalStore {\r\n /** Cache de documentos indexado por path */\r\n private readonly documents = new Map<string, CachedDocument>();\r\n /** Cache de resultados de queries indexado por key */\r\n private readonly queries = new Map<string, CachedQuery>();\r\n\r\n // ===========================================================================\r\n // DOCUMENTOS\r\n // ===========================================================================\r\n\r\n /** Almacena o actualiza un documento en cache */\r\n putDocument(path: string, data: DocumentData | null, exists: boolean, updateTime?: string): void {\r\n this.documents.set(path, {\r\n path,\r\n data: data ? structuredClonePolyfill(data) : null,\r\n exists,\r\n updateTime,\r\n cachedAt: Date.now(),\r\n });\r\n }\r\n\r\n /** Obtiene un documento del cache (undefined si no existe en cache) */\r\n getDocument(path: string): CachedDocument | undefined {\r\n return this.documents.get(path);\r\n }\r\n\r\n /** Elimina un documento del cache */\r\n removeDocument(path: string): void {\r\n this.documents.delete(path);\r\n }\r\n\r\n /** Marca un documento como eliminado en cache (sin removerlo del mapa) */\r\n markDeleted(path: string): void {\r\n this.documents.set(path, {\r\n path,\r\n data: null,\r\n exists: false,\r\n cachedAt: Date.now(),\r\n });\r\n }\r\n\r\n /** Obtiene todos los documentos de una colección desde el cache */\r\n getDocumentsByCollection(collectionPath: string): CachedDocument[] {\r\n const results: CachedDocument[] = [];\r\n const prefix = collectionPath + '/';\r\n\r\n for (const [path, doc] of this.documents) {\r\n // Solo documentos directos de esta colección (no subcolecciones)\r\n if (path.startsWith(prefix) && !path.substring(prefix.length).includes('/')) {\r\n if (doc.exists) {\r\n results.push(doc);\r\n }\r\n }\r\n }\r\n\r\n return results;\r\n }\r\n\r\n // ===========================================================================\r\n // QUERIES\r\n // ===========================================================================\r\n\r\n /** Almacena el resultado de una query */\r\n putQuery(key: string, documentPaths: string[]): void {\r\n this.queries.set(key, {\r\n key,\r\n documentPaths: [...documentPaths],\r\n cachedAt: Date.now(),\r\n });\r\n }\r\n\r\n /** Obtiene un resultado de query del cache */\r\n getQuery(key: string): CachedQuery | undefined {\r\n return this.queries.get(key);\r\n }\r\n\r\n // ===========================================================================\r\n // MANTENIMIENTO\r\n // ===========================================================================\r\n\r\n /** Limpia todo el cache */\r\n clear(): void {\r\n this.documents.clear();\r\n this.queries.clear();\r\n }\r\n\r\n /** Número de documentos en cache */\r\n get documentCount(): number {\r\n return this.documents.size;\r\n }\r\n\r\n /** Número de queries en cache */\r\n get queryCount(): number {\r\n return this.queries.size;\r\n }\r\n}\r\n\r\n/**\r\n * Clona profundamente un objeto de datos para evitar mutaciones externas.\r\n * Usa structuredClone si está disponible, sino JSON parse/stringify.\r\n */\r\nfunction structuredClonePolyfill<T>(obj: T): T {\r\n if (typeof globalThis.structuredClone === 'function') {\r\n try {\r\n return globalThis.structuredClone(obj);\r\n } catch {\r\n // Fallback para objetos no clonables (Timestamp, GeoPoint, etc.)\r\n }\r\n }\r\n return JSON.parse(JSON.stringify(obj));\r\n}\r\n","/**\r\n * Firestore SDK - Mutation Queue (Cola de Mutaciones)\r\n * Almacena operaciones de escritura pendientes de sincronización.\r\n * Las mutaciones se mantienen en orden FIFO y se replican al servidor\r\n * cuando la conexión se restablece.\r\n */\r\n\r\nimport type { DocumentData } from '../firestore/types';\r\nimport type { Mutation, MutationType } from './types';\r\n\r\n/** Contador global para IDs únicos de mutaciones */\r\nlet mutationCounter = 0;\r\n\r\n/**\r\n * Cola de mutaciones para operaciones offline.\r\n * Mantiene escrituras pendientes en orden de creación.\r\n */\r\nexport class MutationQueue {\r\n /** Cola ordenada de mutaciones pendientes */\r\n private readonly queue: Mutation[] = [];\r\n /** Callbacks que esperan a que la cola se vacíe */\r\n private readonly pendingWaiters: Array<() => void> = [];\r\n\r\n /**\r\n * Añade una mutación a la cola\r\n * @returns ID de la mutación creada\r\n */\r\n add(type: MutationType, path: string, data: DocumentData | null, options?: { merge?: boolean; mergeFields?: string[] }): string {\r\n const id = `mutation_${++mutationCounter}_${Date.now()}`;\r\n this.queue.push({\r\n id,\r\n type,\r\n path,\r\n data: data ? { ...data } : null,\r\n options: options ? { ...options } : undefined,\r\n timestamp: Date.now(),\r\n });\r\n return id;\r\n }\r\n\r\n /** Obtiene la siguiente mutación sin removerla */\r\n peek(): Mutation | undefined {\r\n return this.queue[0];\r\n }\r\n\r\n /** Remueve y retorna la primera mutación de la cola */\r\n shift(): Mutation | undefined {\r\n const mutation = this.queue.shift();\r\n // Si la cola quedó vacía, notificar a los waiters\r\n if (this.queue.length === 0) {\r\n this.notifyWaiters();\r\n }\r\n return mutation;\r\n }\r\n\r\n /** Remueve una mutación específica por ID */\r\n remove(id: string): boolean {\r\n const idx = this.queue.findIndex(m => m.id === id);\r\n if (idx === -1) return false;\r\n this.queue.splice(idx, 1);\r\n if (this.queue.length === 0) {\r\n this.notifyWaiters();\r\n }\r\n return true;\r\n }\r\n\r\n /** Verifica si hay mutaciones pendientes para un path */\r\n hasPendingForPath(path: string): boolean {\r\n return this.queue.some(m => m.path === path);\r\n }\r\n\r\n /** Obtiene todas las mutaciones pendientes para un path (en orden) */\r\n getForPath(path: string): Mutation[] {\r\n return this.queue.filter(m => m.path === path);\r\n }\r\n\r\n /** Obtiene todas las mutaciones pendientes para una colección */\r\n getForCollection(collectionPath: string): Mutation[] {\r\n const prefix = collectionPath + '/';\r\n return this.queue.filter(m => m.path.startsWith(prefix) && !m.path.substring(prefix.length).includes('/'));\r\n }\r\n\r\n /** Retorna todas las mutaciones pendientes (copia) */\r\n getAll(): Mutation[] {\r\n return [...this.queue];\r\n }\r\n\r\n /** Número de mutaciones pendientes */\r\n get size(): number {\r\n return this.queue.length;\r\n }\r\n\r\n /** Si la cola está vacía */\r\n get isEmpty(): boolean {\r\n return this.queue.length === 0;\r\n }\r\n\r\n /** Limpia toda la cola */\r\n clear(): void {\r\n this.queue.length = 0;\r\n this.notifyWaiters();\r\n }\r\n\r\n /**\r\n * Espera a que todas las mutaciones pendientes se sincronicen.\r\n * Resuelve inmediatamente si la cola ya está vacía.\r\n */\r\n waitForEmpty(): Promise<void> {\r\n if (this.queue.length === 0) {\r\n return Promise.resolve();\r\n }\r\n return new Promise<void>(resolve => {\r\n this.pendingWaiters.push(resolve);\r\n });\r\n }\r\n\r\n /** Notifica a todos los waiters que la cola está vacía */\r\n private notifyWaiters(): void {\r\n const waiters = this.pendingWaiters.splice(0);\r\n for (const resolve of waiters) {\r\n resolve();\r\n }\r\n }\r\n}\r\n","/**\r\n * Firestore SDK - Persistence Manager\r\n * Orquesta el cache local, la cola de mutaciones y el estado de red.\r\n * Implementa el patrón de Firestore: cache-first + mutation queue + latency compensation.\r\n *\r\n * Uso:\r\n * - Se activa al llamar enableIndexedDbPersistence(db)\r\n * - Si no se activa, el SDK funciona exactamente como antes (directo al servidor)\r\n */\r\n\r\nimport type { DocumentData, FirestoreInstance } from '../firestore/types';\r\nimport type { NetworkState, DocumentStore, MutationStore } from './types';\r\nimport { LocalStore } from './local-store';\r\nimport { MutationQueue } from './mutation-queue';\r\nimport { getHttpClient } from '../transport/http';\r\nimport { toFirestoreFields } from '../firestore/snapshot';\r\n\r\n// =============================================================================\r\n// WeakMap global: asocia FirestoreInstance → PersistenceManager\r\n// =============================================================================\r\n\r\nconst managers = new WeakMap<FirestoreInstance, PersistenceManager>();\r\n\r\n/**\r\n * Obtiene el PersistenceManager de una instancia Firestore.\r\n * Retorna undefined si la persistencia no está habilitada.\r\n */\r\nexport function getPersistenceManager(firestore: FirestoreInstance): PersistenceManager | undefined {\r\n return managers.get(firestore);\r\n}\r\n\r\n/**\r\n * Crea y registra un PersistenceManager con stores en memoria (default).\r\n * Lanza error si ya tiene uno.\r\n */\r\nexport function createPersistenceManager(firestore: FirestoreInstance): PersistenceManager {\r\n if (managers.has(firestore)) {\r\n throw new Error('Persistence already enabled for this Firestore instance');\r\n }\r\n const pm = new PersistenceManager(firestore, new LocalStore(), new MutationQueue());\r\n managers.set(firestore, pm);\r\n return pm;\r\n}\r\n\r\n/**\r\n * Crea y registra un PersistenceManager con stores personalizados (ej: IndexedDB).\r\n * Lanza error si ya tiene uno.\r\n */\r\nexport function createPersistenceManagerWithStores(\r\n firestore: FirestoreInstance,\r\n store: DocumentStore,\r\n mutations: MutationStore,\r\n): PersistenceManager {\r\n if (managers.has(firestore)) {\r\n throw new Error('Persistence already enabled for this Firestore instance');\r\n }\r\n const pm = new PersistenceManager(firestore, store, mutations);\r\n managers.set(firestore, pm);\r\n return pm;\r\n}\r\n\r\n/**\r\n * Elimina el PersistenceManager de una instancia (para clearIndexedDbPersistence).\r\n */\r\nexport async function removePersistenceManager(firestore: FirestoreInstance): Promise<void> {\r\n const pm = managers.get(firestore);\r\n if (pm) {\r\n await pm.destroy();\r\n managers.delete(firestore);\r\n }\r\n}\r\n\r\n// =============================================================================\r\n// PersistenceManager\r\n// =============================================================================\r\n\r\n/**\r\n * Gestiona la persistencia offline del SDK.\r\n * Tres pilares:\r\n * 1. DocumentStore: cache de documentos (memoria o IndexedDB)\r\n * 2. MutationStore: cola de escrituras pendientes (memoria o IndexedDB)\r\n * 3. NetworkState: controla si el SDK intenta usar la red\r\n */\r\nexport class PersistenceManager {\r\n readonly store: DocumentStore;\r\n readonly mutations: MutationStore;\r\n private _networkState: NetworkState = 'online';\r\n private _flushing = false;\r\n private readonly firestore: FirestoreInstance;\r\n\r\n constructor(firestore: FirestoreInstance, store: DocumentStore, mutations: MutationStore) {\r\n this.firestore = firestore;\r\n this.store = store;\r\n this.mutations = mutations;\r\n }\r\n\r\n // ===========================================================================\r\n // ESTADO DE RED\r\n // ===========================================================================\r\n\r\n /** Si la red está habilitada */\r\n get isOnline(): boolean {\r\n return this._networkState === 'online';\r\n }\r\n\r\n /** Habilita la red y comienza a sincronizar mutaciones pendientes */\r\n async enableNetwork(): Promise<void> {\r\n this._networkState = 'online';\r\n await this.flushMutations();\r\n }\r\n\r\n /** Deshabilita la red — toda operación será local */\r\n disableNetwork(): void {\r\n this._networkState = 'offline';\r\n }\r\n\r\n // ===========================================================================\r\n // CACHE DE DOCUMENTOS\r\n // ===========================================================================\r\n\r\n /** Almacena un documento en cache (después de leerlo del servidor) */\r\n async cacheDocument(path: string, data: DocumentData | null, exists: boolean, updateTime?: string): Promise<void> {\r\n await this.store.putDocument(path, data, exists, updateTime);\r\n }\r\n\r\n /** Lee un documento del cache */\r\n async getCachedDocument(path: string): Promise<{ data: DocumentData | null; exists: boolean; updateTime?: string } | undefined> {\r\n const cached = await this.store.getDocument(path);\r\n if (!cached) return undefined;\r\n\r\n // Aplicar mutaciones pendientes sobre los datos cacheados\r\n return this.applyPendingMutations(path, cached.data, cached.exists);\r\n }\r\n\r\n /** Lee documentos de una colección del cache */\r\n async getCachedCollection(collectionPath: string): Promise<Array<{ path: string; data: DocumentData; exists: boolean }>> {\r\n const docs = await this.store.getDocumentsByCollection(collectionPath);\r\n const results: Array<{ path: string; data: DocumentData; exists: boolean }> = [];\r\n\r\n // Incluir documentos existentes con mutaciones aplicadas\r\n const seenPaths = new Set<string>();\r\n for (const doc of docs) {\r\n seenPaths.add(doc.path);\r\n const applied = await this.applyPendingMutations(doc.path, doc.data, doc.exists);\r\n if (applied.exists && applied.data) {\r\n results.push({ path: doc.path, data: applied.data, exists: true });\r\n }\r\n }\r\n\r\n // Incluir documentos creados localmente (en mutation queue pero no en cache)\r\n const collectionMutations = await this.mutations.getForCollection(collectionPath);\r\n for (const mutation of collectionMutations) {\r\n if (!seenPaths.has(mutation.path) && mutation.type !== 'delete' && mutation.data) {\r\n results.push({ path: mutation.path, data: mutation.data, exists: true });\r\n }\r\n }\r\n\r\n return results;\r\n }\r\n\r\n /** Almacena resultados de query en cache */\r\n async cacheQuery(key: string, documentPaths: string[]): Promise<void> {\r\n await this.store.putQuery(key, documentPaths);\r\n }\r\n\r\n /** Lee resultados de query del cache */\r\n async getCachedQuery(key: string): Promise<string[] | undefined> {\r\n const cached = await this.store.getQuery(key);\r\n return cached?.documentPaths;\r\n }\r\n\r\n // ===========================================================================\r\n // COLA DE MUTACIONES\r\n // ===========================================================================\r\n\r\n /** Registra una escritura local (optimistic update + queue) */\r\n async addLocalWrite(\r\n type: 'set' | 'update' | 'delete',\r\n path: string,\r\n data: DocumentData | null,\r\n options?: { merge?: boolean; mergeFields?: string[] }\r\n ): Promise<void> {\r\n // Actualizar cache local inmediatamente (latency compensation)\r\n if (type === 'delete') {\r\n await this.store.markDeleted(path);\r\n } else if (type === 'set' && !options?.merge && !options?.mergeFields) {\r\n // Set sin merge: reemplaza completamente\r\n await this.store.putDocument(path, data, true);\r\n } else {\r\n // Set con merge o update: fusionar con datos existentes\r\n const existing = await this.store.getDocument(path);\r\n const mergedData = this.mergeData(existing?.data ?? {}, data ?? {}, options);\r\n await this.store.putDocument(path, mergedData, true);\r\n }\r\n\r\n // Añadir a la cola de mutaciones\r\n await this.mutations.add(type, path, data, options);\r\n }\r\n\r\n /** Remueve una mutación completada de la cola */\r\n async completeMutation(id: string): Promise<void> {\r\n await this.mutations.remove(id);\r\n }\r\n\r\n /** Verifica si un documento tiene escrituras pendientes */\r\n async hasPendingWrites(path: string): Promise<boolean> {\r\n return this.mutations.hasPendingForPath(path);\r\n }\r\n\r\n /** Espera a que todas las escrituras pendientes se sincronicen */\r\n waitForPendingWrites(): Promise<void> {\r\n return this.mutations.waitForEmpty();\r\n }\r\n\r\n // ===========================================================================\r\n // SINCRONIZACIÓN\r\n // ===========================================================================\r\n\r\n /**\r\n * Intenta sincronizar todas las mutaciones pendientes con el servidor.\r\n * Procesa en orden FIFO. Si una falla con error de red, para y reintenta después.\r\n */\r\n async flushMutations(): Promise<void> {\r\n const empty = await this.mutations.isEmpty;\r\n if (this._flushing || !this.isOnline || empty) return;\r\n\r\n this._flushing = true;\r\n try {\r\n const client = getHttpClient(this.firestore);\r\n\r\n let pending = !(await this.mutations.isEmpty);\r\n while (pending && this.isOnline) {\r\n const mutation = await this.mutations.peek();\r\n if (!mutation) break;\r\n\r\n try {\r\n await this.executeMutation(client, mutation);\r\n await this.mutations.shift();\r\n } catch (error) {\r\n const code = (error as { code?: string })?.code;\r\n // Errores de red: parar y reintentar después\r\n if (code === 'unavailable' || code === 'deadline-exceeded') {\r\n break;\r\n }\r\n // Errores permanentes (permission-denied, not-found para update, etc.):\r\n // descartar la mutación y hacer rollback del cache\r\n await this.mutations.shift();\r\n await this.rollbackDocument(mutation.path);\r\n }\r\n }\r\n } finally {\r\n this._flushing = false;\r\n }\r\n }\r\n\r\n // ===========================================================================\r\n // LIMPIEZA\r\n // ===========================================================================\r\n\r\n /** Limpia todo (cache + cola) */\r\n async clear(): Promise<void> {\r\n await this.store.clear();\r\n await this.mutations.clear();\r\n }\r\n\r\n /** Destruye el manager */\r\n async destroy(): Promise<void> {\r\n await this.clear();\r\n this._networkState = 'offline';\r\n }\r\n\r\n // ===========================================================================\r\n // HELPERS PRIVADOS\r\n // ===========================================================================\r\n\r\n /**\r\n * Aplica mutaciones pendientes a un documento cacheado.\r\n * Esto es la \"compensación de latencia\" — el usuario ve sus cambios inmediatamente.\r\n */\r\n private async applyPendingMutations(\r\n path: string,\r\n baseData: DocumentData | null,\r\n baseExists: boolean\r\n ): Promise<{ data: DocumentData | null; exists: boolean }> {\r\n const pending = await this.mutations.getForPath(path);\r\n if (pending.length === 0) return { data: baseData, exists: baseExists };\r\n\r\n let data = baseData ? { ...baseData } : null;\r\n let exists = baseExists;\r\n\r\n for (const mutation of pending) {\r\n switch (mutation.type) {\r\n case 'set':\r\n if (!mutation.options?.merge && !mutation.options?.mergeFields) {\r\n data = mutation.data ? { ...mutation.data } : null;\r\n } else {\r\n data = this.mergeData(data ?? {}, mutation.data ?? {}, mutation.options);\r\n }\r\n exists = true;\r\n break;\r\n case 'update':\r\n if (exists && data) {\r\n data = { ...data, ...(mutation.data ?? {}) };\r\n }\r\n break;\r\n case 'delete':\r\n data = null;\r\n exists = false;\r\n break;\r\n }\r\n }\r\n\r\n return { data, exists };\r\n }\r\n\r\n /** Fusiona datos para set con merge o update */\r\n private mergeData(\r\n existing: DocumentData,\r\n incoming: DocumentData,\r\n options?: { merge?: boolean; mergeFields?: string[] }\r\n ): DocumentData {\r\n if (options?.mergeFields) {\r\n const result = { ...existing };\r\n for (const field of options.mergeFields) {\r\n if (field in incoming) {\r\n result[field] = incoming[field];\r\n }\r\n }\r\n return result;\r\n }\r\n return { ...existing, ...incoming };\r\n }\r\n\r\n /**\r\n * Ejecuta una mutación contra el servidor.\r\n */\r\n private async executeMutation(client: ReturnType<typeof getHttpClient>, mutation: {\r\n type: string;\r\n path: string;\r\n data: DocumentData | null;\r\n options?: { merge?: boolean; mergeFields?: string[] };\r\n }): Promise<void> {\r\n const fields = mutation.data ? toFirestoreFields(mutation.data) : {};\r\n\r\n switch (mutation.type) {\r\n case 'set': {\r\n if (mutation.options?.merge || mutation.options?.mergeFields) {\r\n const fieldPaths = mutation.options.mergeFields || Object.keys(mutation.data ?? {});\r\n await client.patch(`/documents/${mutation.path}`, {\r\n fields,\r\n updateMask: { fieldPaths },\r\n });\r\n } else {\r\n const parts = mutation.path.split('/');\r\n const parentPath = parts.slice(0, -1).join('/');\r\n const documentId = parts[parts.length - 1];\r\n await client.post(`/documents/${parentPath}?documentId=${documentId}`, { fields });\r\n }\r\n break;\r\n }\r\n case 'update': {\r\n const fieldPaths = Object.keys(mutation.data ?? {});\r\n await client.patch(`/documents/${mutation.path}`, {\r\n fields,\r\n updateMask: { fieldPaths },\r\n });\r\n break;\r\n }\r\n case 'delete': {\r\n await client.delete(`/documents/${mutation.path}`);\r\n break;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Rollback: refresca el documento del servidor para corregir el cache.\r\n * Si falla, simplemente remueve del cache.\r\n */\r\n private async rollbackDocument(path: string): Promise<void> {\r\n try {\r\n const client = getHttpClient(this.firestore);\r\n const doc = await client.get<{ fields?: Record<string, unknown>; updateTime?: string }>(`/documents/${path}`);\r\n if (doc?.fields) {\r\n await this.store.putDocument(path, doc.fields as DocumentData, true, doc.updateTime);\r\n } else {\r\n await this.store.removeDocument(path);\r\n }\r\n } catch {\r\n // Si no podemos refrescar, al menos limpiar el cache corrupto\r\n await this.store.removeDocument(path);\r\n }\r\n }\r\n}\r\n","/**\r\n * Firestore SDK - Firestore Instance\r\n * getFirestore, initializeFirestore, connectFirestoreEmulator\r\n * API 100% compatible con Firebase v9+\r\n */\r\n\r\nimport type {\r\n Firestore,\r\n FirestoreSettings,\r\n FirestoreInstance,\r\n AuthTokenProvider,\r\n} from './types';\r\nimport type { FirebaseApp } from '../app';\r\nimport {\r\n createPersistenceManager,\r\n createPersistenceManagerWithStores,\r\n getPersistenceManager,\r\n removePersistenceManager,\r\n} from '../persistence/persistence-manager';\r\n\r\n// Almacén global de instancias Firestore\r\nconst firestoreInstances = new Map<string, FirestoreImpl>();\r\n\r\n// Almacén de Auth providers por app\r\nconst authProviders = new Map<string, AuthTokenProvider>();\r\n\r\n/**\r\n * Configuración interna normalizada\r\n */\r\ninterface InternalConfig {\r\n host: string;\r\n ssl: boolean;\r\n timeout: number;\r\n authToken?: string;\r\n authTokenProvider?: AuthTokenProvider;\r\n allowFlexibleQueries?: boolean;\r\n maxRetries: number;\r\n retryInitialDelayMs: number;\r\n retryMaxDelayMs: number;\r\n}\r\n\r\n/**\r\n * Implementación interna de Firestore\r\n */\r\nclass FirestoreImpl implements FirestoreInstance {\r\n readonly type = 'firestore' as const;\r\n readonly _projectId: string;\r\n readonly _databaseId: string;\r\n readonly _config: InternalConfig;\r\n private _terminated = false;\r\n private _emulatorConfig: { host: string; port: number } | null = null;\r\n private _authUnsubscribe: (() => void) | null = null;\r\n \r\n constructor(\r\n readonly app: FirebaseApp,\r\n settings?: Partial<FirestoreSettings>\r\n ) {\r\n // Por defecto usar configuración de la app o localhost:3000\r\n const defaultHost = app.options.baseUrl \r\n ? new URL(app.options.baseUrl).host \r\n : 'localhost:3000';\r\n \r\n this._config = {\r\n host: settings?.host || defaultHost,\r\n ssl: settings?.ssl ?? (defaultHost.includes('https') || defaultHost.includes('443')),\r\n timeout: settings?.timeout || 12000,\r\n authToken: settings?.authToken,\r\n authTokenProvider: settings?.authTokenProvider,\r\n allowFlexibleQueries: settings?.allowFlexibleQueries ?? false,\r\n maxRetries: settings?.maxRetries ?? 2,\r\n retryInitialDelayMs: settings?.retryInitialDelayMs ?? 150,\r\n retryMaxDelayMs: settings?.retryMaxDelayMs ?? 2000,\r\n };\r\n this._projectId = app.options.projectId;\r\n this._databaseId = app.options.databaseId || '(default)';\r\n \r\n // Si hay un auth provider registrado para esta app, usarlo\r\n const provider = authProviders.get(app.name);\r\n if (provider) {\r\n this._setupAuthProvider(provider);\r\n }\r\n }\r\n \r\n /**\r\n * Configura un proveedor de autenticación\r\n * @internal\r\n */\r\n _setupAuthProvider(provider: AuthTokenProvider): void {\r\n this._config.authTokenProvider = provider;\r\n \r\n // Suscribirse a cambios de auth\r\n if (provider.onAuthStateChanged) {\r\n this._authUnsubscribe = provider.onAuthStateChanged((token) => {\r\n this._config.authToken = token ?? undefined;\r\n });\r\n }\r\n }\r\n \r\n /**\r\n * Configura el emulador\r\n * @internal\r\n */\r\n _useEmulator(host: string, port: number): void {\r\n this._emulatorConfig = { host, port };\r\n this._config.host = `${host}:${port}`;\r\n this._config.ssl = false;\r\n }\r\n \r\n /**\r\n * Obtiene la configuración del emulador\r\n */\r\n get emulatorConfig(): { host: string; port: number } | null {\r\n return this._emulatorConfig;\r\n }\r\n \r\n /**\r\n * Obtiene la configuración actual\r\n */\r\n get settings(): FirestoreSettings {\r\n return { ...this._config };\r\n }\r\n \r\n /**\r\n * Construye la URL base para peticiones HTTP\r\n */\r\n get baseUrl(): string {\r\n const protocol = this._config.ssl ? 'https' : 'http';\r\n return `${protocol}://${this._config.host}/v1/projects/${this._projectId}/databases/${this._databaseId}`;\r\n }\r\n \r\n /**\r\n * Construye la URL para WebSocket\r\n */\r\n get wsUrl(): string {\r\n const protocol = this._config.ssl ? 'wss' : 'ws';\r\n return `${protocol}://${this._config.host}/ws/projects/${this._projectId}/databases/${this._databaseId}`;\r\n }\r\n \r\n /**\r\n * Token de autenticación actual\r\n */\r\n get authToken(): string | undefined {\r\n return this._config.authToken;\r\n }\r\n \r\n /**\r\n * Actualiza el token de autenticación\r\n */\r\n setAuthToken(token: string | undefined): void {\r\n this._config.authToken = token;\r\n }\r\n \r\n /**\r\n * Verifica si la instancia está terminada\r\n */\r\n get terminated(): boolean {\r\n return this._terminated;\r\n }\r\n \r\n /**\r\n * Obtiene el token de autenticación actual.\r\n * Prioridad: 1) Auth provider (si retorna token), 2) Token estático (setAuthToken).\r\n * Compatible con Firebase: auth module y token manual coexisten.\r\n */\r\n async getToken(): Promise<string | null> {\r\n // Si hay un provider, intentar obtener token fresco\r\n if (this._config.authTokenProvider) {\r\n const providerToken = await this._config.authTokenProvider.getToken();\r\n if (providerToken) {\r\n return providerToken;\r\n }\r\n }\r\n // Fallback: token estático (establecido con setAuthToken o connectFirestoreEmulator)\r\n return this._config.authToken ?? null;\r\n }\r\n \r\n /**\r\n * Termina la instancia de Firestore\r\n */\r\n terminate(): void {\r\n // Desuscribirse de auth changes\r\n if (this._authUnsubscribe) {\r\n this._authUnsubscribe();\r\n this._authUnsubscribe = null;\r\n }\r\n this._terminated = true;\r\n firestoreInstances.delete(this.app.name);\r\n }\r\n}\r\n\r\n/**\r\n * Registra un proveedor de autenticación para una app\r\n * Esto permite que Firestore use automáticamente los tokens de Auth\r\n * \r\n * @param app Aplicación Firebase\r\n * @param provider Proveedor de tokens\r\n * @internal\r\n */\r\nexport function _registerAuthProvider(app: FirebaseApp, provider: AuthTokenProvider): void {\r\n authProviders.set(app.name, provider);\r\n \r\n // Si ya existe una instancia de Firestore, configurar el provider\r\n const instance = firestoreInstances.get(app.name);\r\n if (instance) {\r\n instance._setupAuthProvider(provider);\r\n }\r\n}\r\n\r\n/**\r\n * Obtiene la instancia de Firestore para una app\r\n * \r\n * @param app Aplicación Firebase (opcional, usa la app por defecto)\r\n * @returns Instancia de Firestore\r\n * \r\n * @example\r\n * ```typescript\r\n * import { initializeApp } from '@ponceca/firestore-sdk';\r\n * import { getFirestore } from '@ponceca/firestore-sdk/firestore';\r\n * \r\n * const app = initializeApp({\r\n * projectId: 'my-project'\r\n * });\r\n * \r\n * const db = getFirestore(app);\r\n * ```\r\n */\r\nexport function getFirestore(app?: FirebaseApp): Firestore {\r\n // Obtener app por defecto si no se especifica\r\n if (!app) {\r\n // Importar dinámicamente para evitar dependencia circular\r\n const { getApp } = require('../app');\r\n app = getApp() as FirebaseApp;\r\n }\r\n \r\n // Verificar si ya existe instancia\r\n const existing = firestoreInstances.get(app!.name);\r\n if (existing) {\r\n return existing;\r\n }\r\n \r\n // Crear nueva instancia con settings por defecto\r\n const instance = new FirestoreImpl(app!);\r\n firestoreInstances.set(app!.name, instance);\r\n \r\n return instance;\r\n}\r\n\r\n/**\r\n * Inicializa Firestore con configuración personalizada\r\n * \r\n * @param app Aplicación Firebase\r\n * @param settings Configuración de Firestore\r\n * @returns Instancia de Firestore configurada\r\n * \r\n * @example\r\n * ```typescript\r\n * const db = initializeFirestore(app, {\r\n * host: 'firestore.example.com',\r\n * ssl: true\r\n * });\r\n * ```\r\n */\r\nexport function initializeFirestore(\r\n app: FirebaseApp,\r\n settings: FirestoreSettings\r\n): Firestore {\r\n // Verificar si ya existe instancia\r\n if (firestoreInstances.has(app.name)) {\r\n throw new Error(\r\n `Firestore ya ha sido inicializado para la app '${app.name}'. ` +\r\n 'Usa getFirestore() para obtener la instancia existente.'\r\n );\r\n }\r\n \r\n // Crear nueva instancia con settings personalizados\r\n const instance = new FirestoreImpl(app, settings);\r\n firestoreInstances.set(app.name, instance);\r\n \r\n return instance;\r\n}\r\n\r\n/**\r\n * Termina la instancia de Firestore\r\n * \r\n * @param firestore Instancia de Firestore\r\n */\r\nexport async function terminate(firestore: Firestore): Promise<void> {\r\n (firestore as unknown as FirestoreImpl).terminate();\r\n}\r\n\r\n/**\r\n * Limpia la persistencia local.\r\n * Borra todo el cache de documentos y la cola de mutaciones pendientes.\r\n * Debe llamarse cuando Firestore NO está en uso (antes de inicializar o después de terminar).\r\n * \r\n * @param firestore Instancia de Firestore\r\n */\r\nexport async function clearIndexedDbPersistence(firestore: Firestore): Promise<void> {\r\n await removePersistenceManager(firestore);\r\n\r\n // Eliminar bases de datos IndexedDB si están disponibles\r\n if (typeof indexedDB !== 'undefined') {\r\n const projectId = (firestore as unknown as FirestoreImpl)._projectId;\r\n const { IndexedDBStore } = await import('../persistence/indexeddb-store');\r\n const { IndexedDBMutationQueue } = await import('../persistence/indexeddb-mutation-queue');\r\n await IndexedDBStore.deleteDatabase(projectId);\r\n await IndexedDBMutationQueue.deleteDatabase(projectId);\r\n }\r\n}\r\n\r\n/**\r\n * Habilita la persistencia offline para Firestore.\r\n * Activa el cache local de documentos y la cola de mutaciones para operaciones offline.\r\n * Debe llamarse antes de cualquier otra operación con Firestore.\r\n * \r\n * @param firestore Instancia de Firestore\r\n * \r\n * @example\r\n * ```typescript\r\n * const db = getFirestore(app);\r\n * await enableIndexedDbPersistence(db);\r\n * // Ahora las lecturas/escrituras funcionan offline\r\n * ```\r\n */\r\nexport async function enableIndexedDbPersistence(firestore: Firestore): Promise<void> {\r\n if (getPersistenceManager(firestore)) {\r\n return; // Ya habilitada, no-op (compatible con Firebase)\r\n }\r\n\r\n // En navegador con IndexedDB disponible → usar IndexedDB real\r\n if (typeof indexedDB !== 'undefined') {\r\n const { IndexedDBStore } = await import('../persistence/indexeddb-store');\r\n const { IndexedDBMutationQueue } = await import('../persistence/indexeddb-mutation-queue');\r\n const projectId = (firestore as unknown as FirestoreImpl)._projectId;\r\n const store = new IndexedDBStore(projectId);\r\n const mutations = new IndexedDBMutationQueue(projectId);\r\n await store.init();\r\n await mutations.init();\r\n createPersistenceManagerWithStores(firestore, store, mutations);\r\n } else {\r\n // Fallback a memoria (Node.js, SSR, etc.)\r\n createPersistenceManager(firestore);\r\n }\r\n}\r\n\r\n/**\r\n * Habilita la persistencia offline multi-tab.\r\n * Activa la misma persistencia que enableIndexedDbPersistence.\r\n * En esta implementación, multi-tab opera igual que single-tab.\r\n * \r\n * @param firestore Instancia de Firestore\r\n */\r\nexport async function enableMultiTabIndexedDbPersistence(firestore: Firestore): Promise<void> {\r\n if (getPersistenceManager(firestore)) {\r\n return;\r\n }\r\n createPersistenceManager(firestore);\r\n}\r\n\r\n/**\r\n * Habilita la red — reanuda la comunicación con el servidor.\r\n * Sincroniza las mutaciones pendientes en la cola.\r\n * Si no hay persistencia habilitada, es un no-op.\r\n * \r\n * @param firestore Instancia de Firestore\r\n */\r\nexport async function enableNetwork(firestore: Firestore): Promise<void> {\r\n const pm = getPersistenceManager(firestore);\r\n if (pm) {\r\n await pm.enableNetwork();\r\n }\r\n}\r\n\r\n/**\r\n * Deshabilita la red — todas las operaciones serán locales.\r\n * Las escrituras se acumulan en la cola de mutaciones hasta que se re-habilite.\r\n * Las lecturas se servirán desde el cache local.\r\n * Si no hay persistencia habilitada, es un no-op.\r\n * \r\n * @param firestore Instancia de Firestore\r\n */\r\nexport async function disableNetwork(firestore: Firestore): Promise<void> {\r\n const pm = getPersistenceManager(firestore);\r\n if (pm) {\r\n pm.disableNetwork();\r\n }\r\n}\r\n\r\n/**\r\n * Espera a que todas las operaciones pendientes se sincronicen con el servidor.\r\n * Resuelve inmediatamente si no hay escrituras pendientes o si la persistencia no está habilitada.\r\n * \r\n * @param firestore Instancia de Firestore\r\n */\r\nexport async function waitForPendingWrites(firestore: Firestore): Promise<void> {\r\n const pm = getPersistenceManager(firestore);\r\n if (pm) {\r\n await pm.waitForPendingWrites();\r\n }\r\n}\r\n\r\n/**\r\n * Limpia instancias para testing\r\n * @internal\r\n */\r\nexport function _clearFirestoreInstances(): void {\r\n firestoreInstances.clear();\r\n}\r\n\r\n/**\r\n * Conecta Firestore al emulador local\r\n * Compatible con Firebase: connectFirestoreEmulator(db, 'localhost', 8080)\r\n * \r\n * @param firestore Instancia de Firestore\r\n * @param host Host del emulador (ej: 'localhost')\r\n * @param port Puerto del emulador (ej: 3000)\r\n * @param options Opciones adicionales (mockUserToken para auth)\r\n * \r\n * @example\r\n * ```typescript\r\n * import { getFirestore, connectFirestoreEmulator } from '@ponceca/firestore-sdk/firestore';\r\n * \r\n * const db = getFirestore(app);\r\n * \r\n * // Conectar al emulador local\r\n * connectFirestoreEmulator(db, 'localhost', 3000);\r\n * ```\r\n */\r\nexport function connectFirestoreEmulator(\r\n firestore: Firestore,\r\n host: string,\r\n port: number,\r\n options?: { mockUserToken?: string | object }\r\n): void {\r\n const impl = firestore as unknown as FirestoreImpl;\r\n \r\n if (impl.terminated) {\r\n throw new Error('Firestore has already been terminated.');\r\n }\r\n \r\n if (impl.emulatorConfig) {\r\n throw new Error('Firestore emulator has already been connected.');\r\n }\r\n \r\n impl._useEmulator(host, port);\r\n \r\n // Si se proporciona mockUserToken, configurarlo\r\n if (options?.mockUserToken) {\r\n const token = typeof options.mockUserToken === 'string' \r\n ? options.mockUserToken \r\n : JSON.stringify(options.mockUserToken);\r\n impl.setAuthToken(token);\r\n }\r\n}\r\n/**\r\n * Establece el token de autenticación para Firestore\r\n * Útil para integrar con sistemas de auth externos o JWT personalizados\r\n * \r\n * @param firestore Instancia de Firestore\r\n * @param token Token de autenticación (JWT, API Key, etc.) o null para eliminar\r\n * \r\n * @example\r\n * ```typescript\r\n * import { getFirestore, setAuthToken } from '@ponceca/firestore-sdk/firestore';\r\n * \r\n * const db = getFirestore(app);\r\n * \r\n * // Establecer token JWT\r\n * setAuthToken(db, 'eyJhbGciOiJIUzI1NiIs...');\r\n * \r\n * // O con un token de tu sistema de auth\r\n * const userToken = await myAuthSystem.getToken();\r\n * setAuthToken(db, userToken);\r\n * \r\n * // Eliminar token (logout)\r\n * setAuthToken(db, null);\r\n * ```\r\n */\r\nexport function setAuthToken(firestore: Firestore, token: string | null): void {\r\n const impl = firestore as unknown as FirestoreImpl;\r\n impl.setAuthToken(token ?? undefined);\r\n}\r\n\r\n/**\r\n * Obtiene el token de autenticación actual\r\n * \r\n * @param firestore Instancia de Firestore\r\n * @returns Token actual o undefined si no hay\r\n */\r\nexport function getAuthToken(firestore: Firestore): string | undefined {\r\n const impl = firestore as unknown as FirestoreImpl;\r\n return impl.authToken;\r\n}"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import {
|
|
2
|
+
_registerAuthProvider
|
|
3
|
+
} from "./chunk-SZKHE2TQ.mjs";
|
|
4
|
+
|
|
5
|
+
// src/auth/index.ts
|
|
6
|
+
var UserImpl = class {
|
|
7
|
+
constructor(uid, _token, email = null, displayName = null, photoURL = null, emailVerified = false, isAnonymous = false) {
|
|
8
|
+
this.uid = uid;
|
|
9
|
+
this._token = _token;
|
|
10
|
+
this.email = email;
|
|
11
|
+
this.displayName = displayName;
|
|
12
|
+
this.photoURL = photoURL;
|
|
13
|
+
this.emailVerified = emailVerified;
|
|
14
|
+
this.isAnonymous = isAnonymous;
|
|
15
|
+
}
|
|
16
|
+
async getIdToken(_forceRefresh) {
|
|
17
|
+
return this._token;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var AuthImpl = class {
|
|
21
|
+
constructor(app) {
|
|
22
|
+
this.app = app;
|
|
23
|
+
this.currentUser = null;
|
|
24
|
+
this.observers = [];
|
|
25
|
+
_registerAuthProvider(app, {
|
|
26
|
+
getToken: async () => {
|
|
27
|
+
if (!this.currentUser) return null;
|
|
28
|
+
return this.currentUser.getIdToken();
|
|
29
|
+
},
|
|
30
|
+
onAuthStateChanged: (callback) => {
|
|
31
|
+
const observer = (user) => {
|
|
32
|
+
if (user) {
|
|
33
|
+
user.getIdToken().then((token) => callback(token));
|
|
34
|
+
} else {
|
|
35
|
+
callback(null);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
this.onAuthStateChanged(observer);
|
|
39
|
+
return () => {
|
|
40
|
+
this.observers = this.observers.filter((o) => o !== observer);
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async signOut() {
|
|
46
|
+
await this.updateCurrentUser(null);
|
|
47
|
+
}
|
|
48
|
+
onAuthStateChanged(observer) {
|
|
49
|
+
this.observers.push(observer);
|
|
50
|
+
observer(this.currentUser);
|
|
51
|
+
return () => {
|
|
52
|
+
this.observers = this.observers.filter((o) => o !== observer);
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
async updateCurrentUser(user) {
|
|
56
|
+
this.currentUser = user;
|
|
57
|
+
this.notifyObservers();
|
|
58
|
+
}
|
|
59
|
+
notifyObservers() {
|
|
60
|
+
this.observers.forEach((observer) => observer(this.currentUser));
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
var authInstances = /* @__PURE__ */ new Map();
|
|
64
|
+
function getAuth(app) {
|
|
65
|
+
let auth = authInstances.get(app.name);
|
|
66
|
+
if (!auth) {
|
|
67
|
+
auth = new AuthImpl(app);
|
|
68
|
+
authInstances.set(app.name, auth);
|
|
69
|
+
}
|
|
70
|
+
return auth;
|
|
71
|
+
}
|
|
72
|
+
async function signInWithCustomToken(auth, token) {
|
|
73
|
+
const parts = token.split(".");
|
|
74
|
+
if (parts.length !== 3) {
|
|
75
|
+
throw new Error("Invalid custom token format");
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const payload = JSON.parse(atob(parts[1]));
|
|
79
|
+
const uid = payload.sub || payload.user_id;
|
|
80
|
+
if (!uid || typeof uid !== "string") {
|
|
81
|
+
throw new Error('Token must contain a valid "sub" or "user_id" claim');
|
|
82
|
+
}
|
|
83
|
+
if (typeof payload.exp !== "number") {
|
|
84
|
+
throw new Error('Token must contain a valid "exp" (expiration) claim');
|
|
85
|
+
}
|
|
86
|
+
if (typeof payload.iat !== "number") {
|
|
87
|
+
throw new Error('Token must contain a valid "iat" (issued at) claim');
|
|
88
|
+
}
|
|
89
|
+
const user = new UserImpl(
|
|
90
|
+
uid,
|
|
91
|
+
token,
|
|
92
|
+
payload.email || null,
|
|
93
|
+
payload.name || null,
|
|
94
|
+
payload.picture || null,
|
|
95
|
+
payload.emailVerified || payload.email_verified || false,
|
|
96
|
+
payload.provider_id === "anonymous"
|
|
97
|
+
);
|
|
98
|
+
await auth.updateCurrentUser(user);
|
|
99
|
+
return user;
|
|
100
|
+
} catch (e) {
|
|
101
|
+
throw new Error("Failed to parse custom token: " + e);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async function signOut(auth) {
|
|
105
|
+
return auth.signOut();
|
|
106
|
+
}
|
|
107
|
+
function onAuthStateChanged(auth, observer) {
|
|
108
|
+
return auth.onAuthStateChanged(observer);
|
|
109
|
+
}
|
|
110
|
+
function atob(str) {
|
|
111
|
+
str = str.replace(/-/g, "+").replace(/_/g, "/");
|
|
112
|
+
while (str.length % 4) {
|
|
113
|
+
str += "=";
|
|
114
|
+
}
|
|
115
|
+
if (typeof window !== "undefined" && window.atob) {
|
|
116
|
+
return window.atob(str);
|
|
117
|
+
} else {
|
|
118
|
+
return Buffer.from(str, "base64").toString("binary");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export {
|
|
123
|
+
getAuth,
|
|
124
|
+
signInWithCustomToken,
|
|
125
|
+
signOut,
|
|
126
|
+
onAuthStateChanged
|
|
127
|
+
};
|
|
128
|
+
//# sourceMappingURL=chunk-ZJ4A4Y2T.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/auth/index.ts"],"sourcesContent":["/**\r\n * Firebase Auth Compatibility Layer\r\n * Permite usar el SDK de Firestore con una API de Auth familiar.\r\n * Se integra automáticamente con Firestore para inyectar el token en las peticiones.\r\n */\r\n\r\nimport { FirebaseApp } from '../app';\r\nimport { _registerAuthProvider } from '../firestore/firestore';\r\n\r\n// Tipos de usuario y auth\r\nexport interface User {\r\n uid: string;\r\n email: string | null;\r\n displayName: string | null;\r\n photoURL: string | null;\r\n emailVerified: boolean;\r\n isAnonymous: boolean;\r\n getIdToken(forceRefresh?: boolean): Promise<string>;\r\n}\r\n\r\nexport interface Auth {\r\n app: FirebaseApp;\r\n currentUser: User | null;\r\n signOut(): Promise<void>;\r\n onAuthStateChanged(observer: (user: User | null) => void): () => void;\r\n updateCurrentUser(user: User | null): Promise<void>;\r\n}\r\n\r\n/**\r\n * Implementación interna de User\r\n */\r\nclass UserImpl implements User {\r\n constructor(\r\n readonly uid: string,\r\n private _token: string,\r\n readonly email: string | null = null,\r\n readonly displayName: string | null = null,\r\n readonly photoURL: string | null = null,\r\n readonly emailVerified: boolean = false,\r\n readonly isAnonymous: boolean = false\r\n ) { }\r\n\r\n async getIdToken(_forceRefresh?: boolean): Promise<string> {\r\n return this._token; // En esta implementación simple, retornamos el token estático\r\n }\r\n}\r\n\r\n/**\r\n * Implementación interna de Auth\r\n */\r\nclass AuthImpl implements Auth {\r\n currentUser: User | null = null;\r\n private observers: ((user: User | null) => void)[] = [];\r\n\r\n constructor(readonly app: FirebaseApp) {\r\n // Registrar este auth provider en Firestore\r\n _registerAuthProvider(app, {\r\n getToken: async (): Promise<string | null> => {\r\n if (!this.currentUser) return null;\r\n return this.currentUser.getIdToken();\r\n },\r\n onAuthStateChanged: (callback: (token: string | null) => void) => {\r\n // Adaptador: Auth notifica User, Firestore quiere Token\r\n const observer = (user: User | null) => {\r\n if (user) {\r\n user.getIdToken().then(token => callback(token));\r\n } else {\r\n callback(null);\r\n }\r\n };\r\n // Suscribirse internamente\r\n this.onAuthStateChanged(observer);\r\n\r\n // Retornar unsubscribe (la interfaz AuthTokenProvider espera () => void)\r\n return () => {\r\n this.observers = this.observers.filter((o) => o !== observer);\r\n };\r\n }\r\n });\r\n }\r\n\r\n async signOut(): Promise<void> {\r\n await this.updateCurrentUser(null);\r\n }\r\n\r\n onAuthStateChanged(observer: (user: User | null) => void): () => void {\r\n this.observers.push(observer);\r\n // Notificar estado actual inmediatamente\r\n observer(this.currentUser);\r\n return () => {\r\n this.observers = this.observers.filter((o) => o !== observer);\r\n };\r\n }\r\n\r\n async updateCurrentUser(user: User | null): Promise<void> {\r\n this.currentUser = user;\r\n this.notifyObservers();\r\n }\r\n\r\n private notifyObservers(): void {\r\n this.observers.forEach((observer) => observer(this.currentUser));\r\n }\r\n}\r\n\r\n// Almacén de instancias Auth\r\nconst authInstances = new Map<string, AuthImpl>();\r\n\r\n/**\r\n * Obtiene la instancia de Auth para una app\r\n */\r\nexport function getAuth(app: FirebaseApp): Auth {\r\n let auth = authInstances.get(app.name);\r\n if (!auth) {\r\n auth = new AuthImpl(app);\r\n authInstances.set(app.name, auth);\r\n }\r\n return auth;\r\n}\r\n\r\n/**\r\n * Inicia sesión con un token personalizado (JWT)\r\n * Útil para probar con tokens generados por tu backend o scripts.\r\n */\r\nexport async function signInWithCustomToken(auth: Auth, token: string): Promise<User> {\r\n // Decodificar token (sin validación criptográfica, solo para extraer datos del usuario)\r\n // En un SDK real de producción usaríamos una librería JWT, aquí hacemos un decode simple\r\n const parts = token.split('.');\r\n if (parts.length !== 3) {\r\n throw new Error('Invalid custom token format');\r\n }\r\n\r\n try {\r\n const payload = JSON.parse(atob(parts[1]));\r\n\r\n // Validar claims esenciales del JWT\r\n const uid = payload.sub || payload.user_id;\r\n if (!uid || typeof uid !== 'string') {\r\n throw new Error('Token must contain a valid \"sub\" or \"user_id\" claim');\r\n }\r\n if (typeof payload.exp !== 'number') {\r\n throw new Error('Token must contain a valid \"exp\" (expiration) claim');\r\n }\r\n if (typeof payload.iat !== 'number') {\r\n throw new Error('Token must contain a valid \"iat\" (issued at) claim');\r\n }\r\n\r\n const user = new UserImpl(\r\n uid,\r\n token,\r\n payload.email || null,\r\n payload.name || null,\r\n payload.picture || null,\r\n payload.emailVerified || payload.email_verified || false,\r\n payload.provider_id === 'anonymous'\r\n );\r\n\r\n // AuthImpl implementa updateCurrentUser\r\n await auth.updateCurrentUser(user);\r\n return user;\r\n } catch (e) {\r\n throw new Error('Failed to parse custom token: ' + e);\r\n }\r\n}\r\n\r\n/**\r\n * Cierra la sesión\r\n */\r\nexport async function signOut(auth: Auth): Promise<void> {\r\n return auth.signOut();\r\n}\r\n\r\n/**\r\n * Suscribe a los cambios de estado de autenticación\r\n */\r\nexport function onAuthStateChanged(auth: Auth, observer: (user: User | null) => void): () => void {\r\n return auth.onAuthStateChanged(observer);\r\n}\r\n\r\n// Helper para decodificar base64url (compatible con navegador y node)\r\nfunction atob(str: string): string {\r\n // Normalizar base64url a base64\r\n str = str.replace(/-/g, '+').replace(/_/g, '/');\r\n\r\n // Pad if necessary\r\n while (str.length % 4) {\r\n str += '=';\r\n }\r\n\r\n if (typeof window !== 'undefined' && window.atob) {\r\n return window.atob(str);\r\n } else {\r\n return Buffer.from(str, 'base64').toString('binary');\r\n }\r\n}\r\n"],"mappings":";;;;;AA+BA,IAAM,WAAN,MAA+B;AAAA,EAC3B,YACa,KACD,QACC,QAAuB,MACvB,cAA6B,MAC7B,WAA0B,MAC1B,gBAAyB,OACzB,cAAuB,OAClC;AAPW;AACD;AACC;AACA;AACA;AACA;AACA;AAAA,EACT;AAAA,EAEJ,MAAM,WAAW,eAA0C;AACvD,WAAO,KAAK;AAAA,EAChB;AACJ;AAKA,IAAM,WAAN,MAA+B;AAAA,EAI3B,YAAqB,KAAkB;AAAlB;AAHrB,uBAA2B;AAC3B,SAAQ,YAA6C,CAAC;AAIlD,0BAAsB,KAAK;AAAA,MACvB,UAAU,YAAoC;AAC1C,YAAI,CAAC,KAAK,YAAa,QAAO;AAC9B,eAAO,KAAK,YAAY,WAAW;AAAA,MACvC;AAAA,MACA,oBAAoB,CAAC,aAA6C;AAE9D,cAAM,WAAW,CAAC,SAAsB;AACpC,cAAI,MAAM;AACN,iBAAK,WAAW,EAAE,KAAK,WAAS,SAAS,KAAK,CAAC;AAAA,UACnD,OAAO;AACH,qBAAS,IAAI;AAAA,UACjB;AAAA,QACJ;AAEA,aAAK,mBAAmB,QAAQ;AAGhC,eAAO,MAAM;AACT,eAAK,YAAY,KAAK,UAAU,OAAO,CAAC,MAAM,MAAM,QAAQ;AAAA,QAChE;AAAA,MACJ;AAAA,IACJ,CAAC;AAAA,EACL;AAAA,EAEA,MAAM,UAAyB;AAC3B,UAAM,KAAK,kBAAkB,IAAI;AAAA,EACrC;AAAA,EAEA,mBAAmB,UAAmD;AAClE,SAAK,UAAU,KAAK,QAAQ;AAE5B,aAAS,KAAK,WAAW;AACzB,WAAO,MAAM;AACT,WAAK,YAAY,KAAK,UAAU,OAAO,CAAC,MAAM,MAAM,QAAQ;AAAA,IAChE;AAAA,EACJ;AAAA,EAEA,MAAM,kBAAkB,MAAkC;AACtD,SAAK,cAAc;AACnB,SAAK,gBAAgB;AAAA,EACzB;AAAA,EAEQ,kBAAwB;AAC5B,SAAK,UAAU,QAAQ,CAAC,aAAa,SAAS,KAAK,WAAW,CAAC;AAAA,EACnE;AACJ;AAGA,IAAM,gBAAgB,oBAAI,IAAsB;AAKzC,SAAS,QAAQ,KAAwB;AAC5C,MAAI,OAAO,cAAc,IAAI,IAAI,IAAI;AACrC,MAAI,CAAC,MAAM;AACP,WAAO,IAAI,SAAS,GAAG;AACvB,kBAAc,IAAI,IAAI,MAAM,IAAI;AAAA,EACpC;AACA,SAAO;AACX;AAMA,eAAsB,sBAAsB,MAAY,OAA8B;AAGlF,QAAM,QAAQ,MAAM,MAAM,GAAG;AAC7B,MAAI,MAAM,WAAW,GAAG;AACpB,UAAM,IAAI,MAAM,6BAA6B;AAAA,EACjD;AAEA,MAAI;AACA,UAAM,UAAU,KAAK,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC;AAGzC,UAAM,MAAM,QAAQ,OAAO,QAAQ;AACnC,QAAI,CAAC,OAAO,OAAO,QAAQ,UAAU;AACjC,YAAM,IAAI,MAAM,qDAAqD;AAAA,IACzE;AACA,QAAI,OAAO,QAAQ,QAAQ,UAAU;AACjC,YAAM,IAAI,MAAM,qDAAqD;AAAA,IACzE;AACA,QAAI,OAAO,QAAQ,QAAQ,UAAU;AACjC,YAAM,IAAI,MAAM,oDAAoD;AAAA,IACxE;AAEA,UAAM,OAAO,IAAI;AAAA,MACb;AAAA,MACA;AAAA,MACA,QAAQ,SAAS;AAAA,MACjB,QAAQ,QAAQ;AAAA,MAChB,QAAQ,WAAW;AAAA,MACnB,QAAQ,iBAAiB,QAAQ,kBAAkB;AAAA,MACnD,QAAQ,gBAAgB;AAAA,IAC5B;AAGA,UAAM,KAAK,kBAAkB,IAAI;AACjC,WAAO;AAAA,EACX,SAAS,GAAG;AACR,UAAM,IAAI,MAAM,mCAAmC,CAAC;AAAA,EACxD;AACJ;AAKA,eAAsB,QAAQ,MAA2B;AACrD,SAAO,KAAK,QAAQ;AACxB;AAKO,SAAS,mBAAmB,MAAY,UAAmD;AAC9F,SAAO,KAAK,mBAAmB,QAAQ;AAC3C;AAGA,SAAS,KAAK,KAAqB;AAE/B,QAAM,IAAI,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG;AAG9C,SAAO,IAAI,SAAS,GAAG;AACnB,WAAO;AAAA,EACX;AAEA,MAAI,OAAO,WAAW,eAAe,OAAO,MAAM;AAC9C,WAAO,OAAO,KAAK,GAAG;AAAA,EAC1B,OAAO;AACH,WAAO,OAAO,KAAK,KAAK,QAAQ,EAAE,SAAS,QAAQ;AAAA,EACvD;AACJ;","names":[]}
|