@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.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +692 -0
  3. package/dist/app.d.mts +51 -0
  4. package/dist/app.d.ts +51 -0
  5. package/dist/app.js +16 -0
  6. package/dist/app.js.map +1 -0
  7. package/dist/app.mjs +16 -0
  8. package/dist/app.mjs.map +1 -0
  9. package/dist/auth/index.d.mts +43 -0
  10. package/dist/auth/index.d.ts +43 -0
  11. package/dist/auth/index.js +18 -0
  12. package/dist/auth/index.js.map +1 -0
  13. package/dist/auth/index.mjs +18 -0
  14. package/dist/auth/index.mjs.map +1 -0
  15. package/dist/chunk-2RQUHE2K.js +719 -0
  16. package/dist/chunk-2RQUHE2K.js.map +1 -0
  17. package/dist/chunk-4CV4JOE5.js +27 -0
  18. package/dist/chunk-4CV4JOE5.js.map +1 -0
  19. package/dist/chunk-57XXMSJA.js +65 -0
  20. package/dist/chunk-57XXMSJA.js.map +1 -0
  21. package/dist/chunk-6J3LNKUQ.js +213 -0
  22. package/dist/chunk-6J3LNKUQ.js.map +1 -0
  23. package/dist/chunk-BXV7KTHB.js +645 -0
  24. package/dist/chunk-BXV7KTHB.js.map +1 -0
  25. package/dist/chunk-C3PCJJX4.mjs +645 -0
  26. package/dist/chunk-C3PCJJX4.mjs.map +1 -0
  27. package/dist/chunk-C6SKWUQV.mjs +213 -0
  28. package/dist/chunk-C6SKWUQV.mjs.map +1 -0
  29. package/dist/chunk-DXPQJR5D.mjs +2469 -0
  30. package/dist/chunk-DXPQJR5D.mjs.map +1 -0
  31. package/dist/chunk-MRVKMKSO.mjs +65 -0
  32. package/dist/chunk-MRVKMKSO.mjs.map +1 -0
  33. package/dist/chunk-NFEGQTCC.mjs +27 -0
  34. package/dist/chunk-NFEGQTCC.mjs.map +1 -0
  35. package/dist/chunk-RSBBZLDE.js +128 -0
  36. package/dist/chunk-RSBBZLDE.js.map +1 -0
  37. package/dist/chunk-RZWTSZSJ.js +2469 -0
  38. package/dist/chunk-RZWTSZSJ.js.map +1 -0
  39. package/dist/chunk-SZKHE2TQ.mjs +719 -0
  40. package/dist/chunk-SZKHE2TQ.mjs.map +1 -0
  41. package/dist/chunk-ZJ4A4Y2T.mjs +128 -0
  42. package/dist/chunk-ZJ4A4Y2T.mjs.map +1 -0
  43. package/dist/firestore/index.d.mts +1476 -0
  44. package/dist/firestore/index.d.ts +1476 -0
  45. package/dist/firestore/index.js +156 -0
  46. package/dist/firestore/index.js.map +1 -0
  47. package/dist/firestore/index.mjs +156 -0
  48. package/dist/firestore/index.mjs.map +1 -0
  49. package/dist/http-A2S5CWEV.js +10 -0
  50. package/dist/http-A2S5CWEV.js.map +1 -0
  51. package/dist/http-SZFONH6Z.mjs +10 -0
  52. package/dist/http-SZFONH6Z.mjs.map +1 -0
  53. package/dist/index.d.mts +4 -0
  54. package/dist/index.d.ts +4 -0
  55. package/dist/index.js +171 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/index.mjs +171 -0
  58. package/dist/index.mjs.map +1 -0
  59. package/dist/indexeddb-mutation-queue-5EB7C2D5.js +192 -0
  60. package/dist/indexeddb-mutation-queue-5EB7C2D5.js.map +1 -0
  61. package/dist/indexeddb-mutation-queue-M2MAH4E4.mjs +192 -0
  62. package/dist/indexeddb-mutation-queue-M2MAH4E4.mjs.map +1 -0
  63. package/dist/indexeddb-store-D23ZY3PR.mjs +162 -0
  64. package/dist/indexeddb-store-D23ZY3PR.mjs.map +1 -0
  65. package/dist/indexeddb-store-DNWBZUQE.js +162 -0
  66. package/dist/indexeddb-store-DNWBZUQE.js.map +1 -0
  67. package/dist/snapshot-MCQVLVHL.js +22 -0
  68. package/dist/snapshot-MCQVLVHL.js.map +1 -0
  69. package/dist/snapshot-ZWZFIFZD.mjs +22 -0
  70. package/dist/snapshot-ZWZFIFZD.mjs.map +1 -0
  71. package/dist/types-meoR-Ecp.d.mts +269 -0
  72. package/dist/types-meoR-Ecp.d.ts +269 -0
  73. package/package.json +78 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/firestore/reference.ts","../src/firestore/local-write-tracker.ts","../src/firestore/utilities.ts","../src/firestore/crud.ts","../src/firestore/query.ts","../src/transport/websocket.ts","../src/firestore/realtime.ts","../src/firestore/batch.ts","../src/firestore/advanced-query.ts","../src/firestore/aggregations.ts","../src/firestore/advanced-search.ts"],"sourcesContent":["/**\r\n * Firestore SDK - Referencias\r\n * doc(), collection(), DocumentReference, CollectionReference\r\n */\r\n\r\nimport type {\r\n DocumentData,\r\n DocumentReference,\r\n CollectionReference,\r\n FirestoreInstance,\r\n} from './types';\r\n\r\n/**\r\n * Implementación interna de DocumentReference\r\n */\r\nclass DocumentReferenceImpl<T = DocumentData> implements DocumentReference<T> {\r\n readonly type = 'document' as const;\r\n readonly path: string;\r\n readonly id: string;\r\n readonly firestore: FirestoreInstance;\r\n readonly parent: CollectionReference<T>;\r\n\r\n constructor(\r\n firestore: FirestoreInstance,\r\n path: string,\r\n parent: CollectionReference<T>\r\n ) {\r\n this.firestore = firestore;\r\n this.path = path;\r\n this.id = path.split('/').pop() || '';\r\n this.parent = parent;\r\n }\r\n}\r\n\r\n/**\r\n * Implementación interna de CollectionReference\r\n */\r\nclass CollectionReferenceImpl<T = DocumentData> implements CollectionReference<T> {\r\n readonly type = 'collection' as const;\r\n readonly path: string;\r\n readonly id: string;\r\n readonly firestore: FirestoreInstance;\r\n readonly parent: DocumentReference | null;\r\n\r\n constructor(\r\n firestore: FirestoreInstance,\r\n path: string,\r\n parent: DocumentReference | null = null\r\n ) {\r\n this.firestore = firestore;\r\n this.path = path;\r\n this.id = path.split('/').pop() || '';\r\n this.parent = parent;\r\n }\r\n}\r\n\r\n/**\r\n * Normaliza un path eliminando barras duplicadas y trailing slashes\r\n */\r\nfunction normalizePath(path: string): string {\r\n return path\r\n .replace(/\\/+/g, '/') // Reemplazar múltiples barras\r\n .replace(/^\\//, '') // Eliminar barra inicial\r\n .replace(/\\/$/, ''); // Eliminar barra final\r\n}\r\n\r\n/**\r\n * Valida que un path sea válido\r\n */\r\nfunction validatePath(path: string, expectedType: 'document' | 'collection'): void {\r\n const segments = path.split('/').filter(s => s.length > 0);\r\n \r\n if (segments.length === 0) {\r\n throw new Error(`Invalid ${expectedType} path: path cannot be empty`);\r\n }\r\n\r\n for (const segment of segments) {\r\n if (segment.startsWith('.')) {\r\n throw new Error(`Invalid path segment: \"${segment}\" - segments cannot start with a dot`);\r\n }\r\n if (segment.includes('..')) {\r\n throw new Error(`Invalid path segment: \"${segment}\" - segments cannot contain double dots`);\r\n }\r\n }\r\n\r\n const isEven = segments.length % 2 === 0;\r\n if (expectedType === 'document' && !isEven) {\r\n throw new Error(`Invalid document path: \"${path}\" - document paths must have an even number of segments`);\r\n }\r\n if (expectedType === 'collection' && isEven) {\r\n throw new Error(`Invalid collection path: \"${path}\" - collection paths must have an odd number of segments`);\r\n }\r\n}\r\n\r\n/**\r\n * Crea una referencia a un documento\r\n * \r\n * @param firestore Instancia de Firestore\r\n * @param pathSegments Segmentos del path\r\n * @returns DocumentReference\r\n * \r\n * @example\r\n * ```typescript\r\n * // Path directo\r\n * const ref = doc(db, 'users', 'user123');\r\n * \r\n * // Desde colección\r\n * const ref = doc(collection(db, 'users'), 'user123');\r\n * \r\n * // Subcolección\r\n * const ref = doc(db, 'users', 'user123', 'posts', 'post456');\r\n * ```\r\n */\r\nexport function doc<T = DocumentData>(\r\n firestore: FirestoreInstance,\r\n ...pathSegments: string[]\r\n): DocumentReference<T>;\r\nexport function doc<T = DocumentData>(\r\n reference: CollectionReference<T>,\r\n ...pathSegments: string[]\r\n): DocumentReference<T>;\r\nexport function doc<T = DocumentData>(\r\n firestoreOrRef: FirestoreInstance | CollectionReference<T>,\r\n ...pathSegments: string[]\r\n): DocumentReference<T> {\r\n let firestore: FirestoreInstance;\r\n let fullPath: string;\r\n\r\n if ('type' in firestoreOrRef && firestoreOrRef.type === 'collection') {\r\n // Llamado con CollectionReference\r\n const collRef = firestoreOrRef;\r\n firestore = collRef.firestore;\r\n \r\n // Si no hay pathSegments, generar ID automático\r\n if (pathSegments.length === 0) {\r\n fullPath = `${collRef.path}/${generateId()}`;\r\n } else {\r\n fullPath = `${collRef.path}/${pathSegments.join('/')}`;\r\n }\r\n } else {\r\n // Llamado con Firestore\r\n firestore = firestoreOrRef as FirestoreInstance;\r\n fullPath = pathSegments.join('/');\r\n }\r\n\r\n fullPath = normalizePath(fullPath);\r\n validatePath(fullPath, 'document');\r\n\r\n // Obtener path de la colección padre\r\n const segments = fullPath.split('/');\r\n const parentPath = segments.slice(0, -1).join('/');\r\n \r\n // Crear referencia padre\r\n const parentRef = new CollectionReferenceImpl<T>(firestore, parentPath);\r\n \r\n return new DocumentReferenceImpl<T>(firestore, fullPath, parentRef);\r\n}\r\n\r\n/**\r\n * Crea una referencia a una colección\r\n * \r\n * @param firestore Instancia de Firestore\r\n * @param pathSegments Segmentos del path\r\n * @returns CollectionReference\r\n * \r\n * @example\r\n * ```typescript\r\n * // Colección de primer nivel\r\n * const ref = collection(db, 'users');\r\n * \r\n * // Subcolección\r\n * const ref = collection(db, 'users', 'user123', 'posts');\r\n * \r\n * // Desde documento\r\n * const ref = collection(doc(db, 'users', 'user123'), 'posts');\r\n * ```\r\n */\r\nexport function collection<T = DocumentData>(\r\n firestore: FirestoreInstance,\r\n ...pathSegments: string[]\r\n): CollectionReference<T>;\r\nexport function collection<T = DocumentData>(\r\n reference: DocumentReference,\r\n ...pathSegments: string[]\r\n): CollectionReference<T>;\r\nexport function collection<T = DocumentData>(\r\n firestoreOrRef: FirestoreInstance | DocumentReference,\r\n ...pathSegments: string[]\r\n): CollectionReference<T> {\r\n let firestore: FirestoreInstance;\r\n let fullPath: string;\r\n let parentDoc: DocumentReference | null = null;\r\n\r\n if ('type' in firestoreOrRef && firestoreOrRef.type === 'document') {\r\n // Llamado con DocumentReference\r\n const docRef = firestoreOrRef;\r\n firestore = docRef.firestore;\r\n fullPath = `${docRef.path}/${pathSegments.join('/')}`;\r\n parentDoc = docRef;\r\n } else {\r\n // Llamado con Firestore\r\n firestore = firestoreOrRef as FirestoreInstance;\r\n fullPath = pathSegments.join('/');\r\n }\r\n\r\n fullPath = normalizePath(fullPath);\r\n validatePath(fullPath, 'collection');\r\n\r\n if (!parentDoc) {\r\n const segments = fullPath.split('/').filter(Boolean);\r\n if (segments.length >= 3) {\r\n const parentPath = segments.slice(0, -1).join('/');\r\n const parentCollectionPath = segments.slice(0, -2).join('/');\r\n const parentCollection = new CollectionReferenceImpl<DocumentData>(\r\n firestore,\r\n parentCollectionPath\r\n );\r\n parentDoc = new DocumentReferenceImpl<DocumentData>(\r\n firestore,\r\n parentPath,\r\n parentCollection\r\n );\r\n }\r\n }\r\n\r\n return new CollectionReferenceImpl<T>(firestore, fullPath, parentDoc);\r\n}\r\n\r\n/**\r\n * Genera un ID único para documentos\r\n * Compatible con el formato de Firestore\r\n */\r\nexport function generateId(): string {\r\n const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\r\n let result = '';\r\n for (let i = 0; i < 20; i++) {\r\n result += chars.charAt(Math.floor(Math.random() * chars.length));\r\n }\r\n return result;\r\n}\r\n","/**\r\n * Firestore SDK - Local Write Tracker\r\n * Permite notificar listeners locales para metadata hasPendingWrites.\r\n */\r\n\r\nexport type LocalWriteListener = () => void;\r\n\r\nconst documentListeners = new Map<string, Set<LocalWriteListener>>();\r\n\r\nexport function registerLocalWriteListener(path: string, listener: LocalWriteListener): () => void {\r\n let listeners = documentListeners.get(path);\r\n if (!listeners) {\r\n listeners = new Set();\r\n documentListeners.set(path, listeners);\r\n }\r\n listeners.add(listener);\r\n\r\n return () => {\r\n const current = documentListeners.get(path);\r\n if (!current) return;\r\n current.delete(listener);\r\n if (current.size === 0) {\r\n documentListeners.delete(path);\r\n }\r\n };\r\n}\r\n\r\nexport function notifyLocalWrite(path: string): void {\r\n const listeners = documentListeners.get(path);\r\n if (!listeners) return;\r\n listeners.forEach(listener => listener());\r\n}\r\n","/**\r\n * Firestore SDK - Utilidades\r\n * withConverter, refEqual, queryEqual, snapshotEqual, onSnapshotsInSync\r\n * Compatible con Firebase Firestore v9\r\n */\r\n\r\nimport type {\r\n DocumentData,\r\n DocumentReference,\r\n CollectionReference,\r\n Query,\r\n DocumentSnapshot,\r\n QuerySnapshot,\r\n FirestoreInstance,\r\n Unsubscribe,\r\n} from './types';\r\n\r\n// ============================================================================\r\n// Data Converters\r\n// ============================================================================\r\n\r\n/**\r\n * Convertidor de datos para tipado fuerte.\r\n * Transforma datos entre el formato de la aplicación y Firestore.\r\n */\r\nexport interface FirestoreDataConverter<T> {\r\n /**\r\n * Convierte un objeto de la aplicación a datos de Firestore.\r\n */\r\n toFirestore(modelObject: T): DocumentData;\r\n \r\n /**\r\n * Convierte un snapshot de Firestore a un objeto de la aplicación.\r\n */\r\n fromFirestore(snapshot: QueryDocumentSnapshot<DocumentData>): T;\r\n}\r\n\r\n/**\r\n * QueryDocumentSnapshot garantiza que el documento existe.\r\n */\r\nexport interface QueryDocumentSnapshot<T = DocumentData> extends DocumentSnapshot<T> {\r\n exists(): true;\r\n data(): T;\r\n}\r\n\r\n/**\r\n * Referencia a documento con converter.\r\n */\r\nexport interface DocumentReferenceWithConverter<T> extends DocumentReference<T> {\r\n /** Converter asociado */\r\n readonly converter: FirestoreDataConverter<T> | null;\r\n}\r\n\r\n/**\r\n * Referencia a colección con converter.\r\n */\r\nexport interface CollectionReferenceWithConverter<T> extends CollectionReference<T> {\r\n /** Converter asociado */\r\n readonly converter: FirestoreDataConverter<T> | null;\r\n}\r\n\r\n/**\r\n * Query con converter.\r\n */\r\nexport interface QueryWithConverter<T> extends Query<T> {\r\n /** Converter asociado */\r\n readonly converter: FirestoreDataConverter<T> | null;\r\n}\r\n\r\n// ============================================================================\r\n// withConverter Implementation\r\n// ============================================================================\r\n\r\n/**\r\n * Aplica un converter a una referencia de documento.\r\n * \r\n * @param reference Referencia de documento\r\n * @param converter Convertidor de datos\r\n * @returns Referencia con converter aplicado\r\n * \r\n * @example\r\n * ```typescript\r\n * interface User {\r\n * name: string;\r\n * age: number;\r\n * }\r\n * \r\n * const userConverter: FirestoreDataConverter<User> = {\r\n * toFirestore: (user) => ({ name: user.name, age: user.age }),\r\n * fromFirestore: (snap) => {\r\n * const data = snap.data();\r\n * return { name: data.name, age: data.age };\r\n * }\r\n * };\r\n * \r\n * const userRef = doc(db, 'users', '123').withConverter(userConverter);\r\n * const userSnap = await getDoc(userRef);\r\n * const user = userSnap.data(); // Tipo: User | undefined\r\n * ```\r\n */\r\nexport function withConverter<T>(\r\n reference: DocumentReference<DocumentData>,\r\n converter: FirestoreDataConverter<T> | null\r\n): DocumentReference<T>;\r\n\r\nexport function withConverter<T>(\r\n reference: CollectionReference<DocumentData>,\r\n converter: FirestoreDataConverter<T> | null\r\n): CollectionReference<T>;\r\n\r\nexport function withConverter<T>(\r\n reference: Query<DocumentData>,\r\n converter: FirestoreDataConverter<T> | null\r\n): Query<T>;\r\n\r\nexport function withConverter<T>(\r\n reference: DocumentReference<DocumentData> | CollectionReference<DocumentData> | Query<DocumentData>,\r\n converter: FirestoreDataConverter<T> | null\r\n): DocumentReference<T> | CollectionReference<T> | Query<T> {\r\n // Crear una copia de la referencia con el converter adjunto\r\n const refWithConverter = Object.create(Object.getPrototypeOf(reference));\r\n Object.assign(refWithConverter, reference);\r\n (refWithConverter as { converter: FirestoreDataConverter<T> | null }).converter = converter;\r\n return refWithConverter as DocumentReference<T> | CollectionReference<T> | Query<T>;\r\n}\r\n\r\n// ============================================================================\r\n// Comparison Functions\r\n// ============================================================================\r\n\r\n/**\r\n * Compara dos referencias de documento.\r\n * \r\n * @param left Primera referencia\r\n * @param right Segunda referencia\r\n * @returns true si apuntan al mismo documento\r\n * \r\n * @example\r\n * ```typescript\r\n * const ref1 = doc(db, 'users', '123');\r\n * const ref2 = doc(db, 'users', '123');\r\n * console.log(refEqual(ref1, ref2)); // true\r\n * ```\r\n */\r\nexport function refEqual<T>(\r\n left: DocumentReference<T> | CollectionReference<T>,\r\n right: DocumentReference<T> | CollectionReference<T>\r\n): boolean {\r\n // Misma instancia\r\n if (left === right) return true;\r\n \r\n // Comparar propiedades\r\n return (\r\n left.firestore === right.firestore &&\r\n left.path === right.path &&\r\n left.type === right.type\r\n );\r\n}\r\n\r\n/**\r\n * Compara dos queries.\r\n * \r\n * @param left Primera query\r\n * @param right Segunda query\r\n * @returns true si son equivalentes\r\n * \r\n * @example\r\n * ```typescript\r\n * const q1 = query(collection(db, 'users'), where('age', '>', 18));\r\n * const q2 = query(collection(db, 'users'), where('age', '>', 18));\r\n * console.log(queryEqual(q1, q2)); // true\r\n * ```\r\n */\r\nexport function queryEqual<T>(\r\n left: Query<T>,\r\n right: Query<T>\r\n): boolean {\r\n // Misma instancia\r\n if (left === right) return true;\r\n \r\n // Comparar propiedades básicas\r\n if (left.firestore !== right.firestore) return false;\r\n if (left.path !== right.path) return false;\r\n if (left.type !== right.type) return false;\r\n \r\n // Comparar constraints si existen\r\n const leftConstraints = (left as unknown as { constraints?: unknown[] }).constraints || [];\r\n const rightConstraints = (right as unknown as { constraints?: unknown[] }).constraints || [];\r\n \r\n if (leftConstraints.length !== rightConstraints.length) return false;\r\n \r\n // Comparación profunda de constraints\r\n return JSON.stringify(leftConstraints) === JSON.stringify(rightConstraints);\r\n}\r\n\r\n/**\r\n * Compara dos snapshots de documento.\r\n * \r\n * @param left Primer snapshot\r\n * @param right Segundo snapshot\r\n * @returns true si representan el mismo estado\r\n * \r\n * @example\r\n * ```typescript\r\n * const snap1 = await getDoc(docRef);\r\n * const snap2 = await getDoc(docRef);\r\n * console.log(snapshotEqual(snap1, snap2)); // true si no hubo cambios\r\n * ```\r\n */\r\nexport function snapshotEqual<T>(\r\n left: DocumentSnapshot<T> | QuerySnapshot<T>,\r\n right: DocumentSnapshot<T> | QuerySnapshot<T>\r\n): boolean {\r\n // Misma instancia\r\n if (left === right) return true;\r\n \r\n // Verificar tipo\r\n const leftIsDoc = 'ref' in left;\r\n const rightIsDoc = 'ref' in right;\r\n \r\n if (leftIsDoc !== rightIsDoc) return false;\r\n \r\n if (leftIsDoc && rightIsDoc) {\r\n // Comparar DocumentSnapshots\r\n const l = left as DocumentSnapshot<T>;\r\n const r = right as DocumentSnapshot<T>;\r\n \r\n if (!refEqual(l.ref, r.ref)) return false;\r\n if (l.exists() !== r.exists()) return false;\r\n \r\n // Comparar datos\r\n return JSON.stringify(l.data()) === JSON.stringify(r.data());\r\n } else {\r\n // Comparar QuerySnapshots\r\n const l = left as QuerySnapshot<T>;\r\n const r = right as QuerySnapshot<T>;\r\n \r\n if (l.size !== r.size) return false;\r\n \r\n // Comparar cada documento\r\n for (let i = 0; i < l.docs.length; i++) {\r\n if (!snapshotEqual(l.docs[i], r.docs[i])) {\r\n return false;\r\n }\r\n }\r\n \r\n return true;\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// onSnapshotsInSync\r\n// ============================================================================\r\n\r\n/**\r\n * Escucha cuando todos los snapshots están sincronizados.\r\n * Se llama cuando todos los listeners activos han recibido una actualización.\r\n * \r\n * @param firestore Instancia de Firestore\r\n * @param onSync Callback a ejecutar cuando todos estén sincronizados\r\n * @returns Función para cancelar la suscripción\r\n * \r\n * @example\r\n * ```typescript\r\n * const unsubscribe = onSnapshotsInSync(db, () => {\r\n * console.log('All snapshots are in sync');\r\n * });\r\n * \r\n * // Más tarde...\r\n * unsubscribe();\r\n * ```\r\n */\r\nexport function onSnapshotsInSync(\r\n firestore: FirestoreInstance,\r\n onSync: () => void\r\n): Unsubscribe {\r\n // Implementación simplificada:\r\n // En un cliente real, esto se integraría con el WebSocket manager\r\n // para detectar cuando todos los listeners han recibido updates.\r\n \r\n // Por ahora, usamos un debounce simple para detectar \"sincronización\"\r\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\r\n let isActive = true;\r\n \r\n // Función que se llamará internamente cuando hay actualizaciones\r\n const scheduleSync = () => {\r\n if (!isActive) return;\r\n \r\n // Cancelar timeout anterior\r\n if (timeoutId) {\r\n clearTimeout(timeoutId);\r\n }\r\n \r\n // Programar callback después de un pequeño delay (sincronización)\r\n timeoutId = setTimeout(() => {\r\n if (isActive) {\r\n onSync();\r\n }\r\n }, 50);\r\n };\r\n \r\n // Registrar en Firestore para ser notificado de updates\r\n const firestoreInternal = firestore as unknown as { \r\n _syncCallbacks?: Set<() => void> \r\n };\r\n \r\n if (!firestoreInternal._syncCallbacks) {\r\n firestoreInternal._syncCallbacks = new Set();\r\n }\r\n firestoreInternal._syncCallbacks.add(scheduleSync);\r\n \r\n // Llamar inmediatamente si ya está sincronizado\r\n scheduleSync();\r\n \r\n // Retornar función de unsubscribe\r\n return () => {\r\n isActive = false;\r\n if (timeoutId) {\r\n clearTimeout(timeoutId);\r\n }\r\n firestoreInternal._syncCallbacks?.delete(scheduleSync);\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// Helpers adicionales\r\n// ============================================================================\r\n\r\n/**\r\n * Crea un QueryDocumentSnapshot desde un DocumentSnapshot.\r\n * Solo para uso interno.\r\n */\r\nexport function asQueryDocumentSnapshot<T>(\r\n snapshot: DocumentSnapshot<T>\r\n): QueryDocumentSnapshot<T> | null {\r\n if (!snapshot.exists()) {\r\n return null;\r\n }\r\n \r\n return {\r\n ...snapshot,\r\n exists: () => true as const,\r\n data: () => snapshot.data()!,\r\n } as QueryDocumentSnapshot<T>;\r\n}\r\n\r\n/**\r\n * Aplica un converter a un DocumentSnapshot si corresponde.\r\n */\r\nexport function applyConverterToDocumentSnapshot<T>(\r\n snapshot: DocumentSnapshot<DocumentData>,\r\n converter: FirestoreDataConverter<T> | null | undefined\r\n): DocumentSnapshot<T> {\r\n if (!converter || !snapshot.exists()) {\r\n return snapshot as DocumentSnapshot<T>;\r\n }\r\n\r\n const querySnap = asQueryDocumentSnapshot(snapshot);\r\n if (!querySnap) {\r\n return snapshot as DocumentSnapshot<T>;\r\n }\r\n\r\n const converted = converter.fromFirestore(querySnap);\r\n return Object.assign(Object.create(Object.getPrototypeOf(snapshot)), snapshot, {\r\n data: () => converted,\r\n }) as DocumentSnapshot<T>;\r\n}\r\n\r\n/**\r\n * Aplica un converter a un QuerySnapshot si corresponde.\r\n */\r\nexport function applyConverterToQuerySnapshot<T>(\r\n snapshot: QuerySnapshot<DocumentData>,\r\n converter: FirestoreDataConverter<T> | null | undefined\r\n): QuerySnapshot<T> {\r\n if (!converter) {\r\n return snapshot as QuerySnapshot<T>;\r\n }\r\n\r\n const convertedDocs = snapshot.docs.map((doc) =>\r\n applyConverterToDocumentSnapshot(doc as DocumentSnapshot<DocumentData>, converter)\r\n );\r\n const convertedChanges = snapshot.docChanges().map((change) => ({\r\n ...change,\r\n doc: applyConverterToDocumentSnapshot(\r\n change.doc as DocumentSnapshot<DocumentData>,\r\n converter\r\n ),\r\n }));\r\n\r\n return Object.assign(Object.create(Object.getPrototypeOf(snapshot)), snapshot, {\r\n docs: convertedDocs,\r\n docChanges: () => convertedChanges,\r\n }) as QuerySnapshot<T>;\r\n}\r\n","/**\r\n * Firestore SDK - CRUD Operations\r\n * getDoc, setDoc, updateDoc, deleteDoc, addDoc\r\n */\r\n\r\nimport type {\r\n DocumentData,\r\n DocumentReference,\r\n CollectionReference,\r\n DocumentSnapshot,\r\n SetOptions,\r\n FirestoreDocument,\r\n FirestoreError,\r\n FirestoreErrorCode,\r\n} from './types';\r\nimport { getHttpClient } from '../transport/http';\r\nimport { createDocumentSnapshot, toFirestoreFields, DocumentSnapshotImpl } from './snapshot';\r\nimport { doc, generateId } from './reference';\r\nimport { notifyLocalWrite } from './local-write-tracker';\r\nimport { applyConverterToDocumentSnapshot, type FirestoreDataConverter } from './utilities';\r\nimport { getPersistenceManager } from '../persistence/persistence-manager';\r\n\r\n/**\r\n * Obtiene un documento de Firestore\r\n * \r\n * @param reference Referencia al documento\r\n * @returns Promise con DocumentSnapshot\r\n * \r\n * @example\r\n * ```typescript\r\n * const docRef = doc(db, 'users', 'user123');\r\n * const snapshot = await getDoc(docRef);\r\n * \r\n * if (snapshot.exists()) {\r\n * console.log('Data:', snapshot.data());\r\n * } else {\r\n * console.log('No existe');\r\n * }\r\n * ```\r\n */\r\nexport async function getDoc<T = DocumentData>(\r\n reference: DocumentReference<T>\r\n): Promise<DocumentSnapshot<T>> {\r\n if (!reference || !reference.path) {\r\n throw new Error('getDoc requires a valid DocumentReference');\r\n }\r\n\r\n const pm = getPersistenceManager(reference.firestore);\r\n\r\n // Sin persistencia: comportamiento directo al servidor\r\n if (!pm) {\r\n return getDocDirect(reference);\r\n }\r\n\r\n // Con persistencia habilitada\r\n if (pm.isOnline) {\r\n try {\r\n const snapshot = await getDocDirect(reference);\r\n // Almacenar en cache\r\n await pm.cacheDocument(reference.path, snapshot.data() as DocumentData ?? null, snapshot.exists(), (snapshot as any)._updateTime);\r\n // Indicar si hay escrituras pendientes\r\n const hasPending = await pm.hasPendingWrites(reference.path);\r\n if (hasPending) {\r\n return createSnapshotWithMetadata(reference, snapshot, { hasPendingWrites: true });\r\n }\r\n return snapshot;\r\n } catch (error) {\r\n // Error de red: fallback a cache\r\n const code = (error as { code?: string })?.code;\r\n if (code === 'unavailable' || code === 'deadline-exceeded') {\r\n return getDocFromLocalCache(reference, pm);\r\n }\r\n throw error;\r\n }\r\n }\r\n\r\n // Offline: leer de cache\r\n return getDocFromLocalCache(reference, pm);\r\n}\r\n\r\n/**\r\n * Lee un documento directamente del servidor (sin cache)\r\n */\r\nasync function getDocDirect<T = DocumentData>(\r\n reference: DocumentReference<T>\r\n): Promise<DocumentSnapshot<T>> {\r\n const client = getHttpClient(reference.firestore);\r\n try {\r\n const doc = await client.get<FirestoreDocument>(\r\n `/documents/${reference.path}`\r\n );\r\n const snapshot = createDocumentSnapshot(reference, doc) as DocumentSnapshot<DocumentData>;\r\n return applyConverterToDocumentSnapshot(snapshot, getConverter(reference));\r\n } catch (error) {\r\n if ((error as { code?: string }).code === 'not-found') {\r\n const snapshot = createDocumentSnapshot(reference, null) as DocumentSnapshot<DocumentData>;\r\n return applyConverterToDocumentSnapshot(snapshot, getConverter(reference));\r\n }\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Lee un documento del cache local\r\n */\r\nasync function getDocFromLocalCache<T = DocumentData>(\r\n reference: DocumentReference<T>,\r\n pm: NonNullable<ReturnType<typeof getPersistenceManager>>\r\n): Promise<DocumentSnapshot<T>> {\r\n const cached = await pm.getCachedDocument(reference.path);\r\n if (cached) {\r\n return new DocumentSnapshotImpl<T>(\r\n reference,\r\n (cached.exists ? cached.data : undefined) as T | undefined,\r\n cached.exists,\r\n { fromCache: true, hasPendingWrites: await pm.hasPendingWrites(reference.path) },\r\n cached.updateTime\r\n );\r\n }\r\n // Sin datos en cache: retornar snapshot vacío con fromCache\r\n return new DocumentSnapshotImpl<T>(\r\n reference,\r\n undefined,\r\n false,\r\n { fromCache: true, hasPendingWrites: false }\r\n );\r\n}\r\n\r\n/**\r\n * Obtiene un documento exclusivamente del cache local.\r\n * Compatible con Firestore Web SDK: getDocFromCache(ref)\r\n */\r\nexport async function getDocFromCache<T = DocumentData>(\r\n reference: DocumentReference<T>\r\n): Promise<DocumentSnapshot<T>> {\r\n if (!reference || !reference.path) {\r\n throw new Error('getDocFromCache requires a valid DocumentReference');\r\n }\r\n\r\n const pm = getPersistenceManager(reference.firestore);\r\n if (!pm) {\r\n throw createFirestoreError(\r\n 'failed-precondition',\r\n 'Failed to get document from cache. Persistence is not enabled.'\r\n );\r\n }\r\n\r\n const cached = await pm.getCachedDocument(reference.path);\r\n if (!cached) {\r\n throw createFirestoreError(\r\n 'unavailable',\r\n 'Failed to get document from cache. Document is not cached.'\r\n );\r\n }\r\n\r\n return await getDocFromLocalCache(reference, pm);\r\n}\r\n\r\n/**\r\n * Crea un snapshot con metadata personalizada\r\n */\r\nfunction createSnapshotWithMetadata<T>(\r\n reference: DocumentReference<T>,\r\n source: DocumentSnapshot<T>,\r\n metadata: { fromCache?: boolean; hasPendingWrites?: boolean }\r\n): DocumentSnapshot<T> {\r\n return new DocumentSnapshotImpl<T>(\r\n reference,\r\n source.data() as T | undefined,\r\n source.exists(),\r\n {\r\n fromCache: metadata.fromCache ?? source.metadata.fromCache,\r\n hasPendingWrites: metadata.hasPendingWrites ?? source.metadata.hasPendingWrites,\r\n },\r\n (source as any)._updateTime\r\n );\r\n}\r\n\r\n/**\r\n * Escribe un documento en Firestore\r\n * Sobrescribe el documento si existe, lo crea si no existe\r\n * \r\n * @param reference Referencia al documento\r\n * @param data Datos a escribir\r\n * @param options Opciones de escritura\r\n * \r\n * @example\r\n * ```typescript\r\n * // Sobrescribir completamente\r\n * await setDoc(doc(db, 'users', 'user123'), {\r\n * name: 'Juan',\r\n * age: 25\r\n * });\r\n * \r\n * // Merge con datos existentes\r\n * await setDoc(doc(db, 'users', 'user123'), {\r\n * age: 26\r\n * }, { merge: true });\r\n * ```\r\n */\r\nexport async function setDoc<T = DocumentData>(\r\n reference: DocumentReference<T>,\r\n data: T,\r\n options?: SetOptions\r\n): Promise<void> {\r\n if (!reference || !reference.path) {\r\n throw new Error('setDoc requires a valid DocumentReference');\r\n }\r\n if (!data || typeof data !== 'object') {\r\n throw new Error('setDoc requires a valid data object');\r\n }\r\n \r\n const dataToWrite = applyConverterToData(reference, data);\r\n assertValidTypes(dataToWrite);\r\n \r\n const client = getHttpClient(reference.firestore);\r\n assertDocumentDepth(dataToWrite as DocumentData);\r\n const fields = toFirestoreFields(dataToWrite as DocumentData);\r\n assertDocumentSize(fields);\r\n\r\n notifyLocalWrite(reference.path);\r\n\r\n const pm = getPersistenceManager(reference.firestore);\r\n\r\n // Registrar escritura local (optimistic update) si hay persistencia\r\n if (pm) {\r\n await pm.addLocalWrite('set', reference.path, dataToWrite as DocumentData, options);\r\n }\r\n\r\n // Construir body\r\n const body: { fields: typeof fields; updateMask?: { fieldPaths: string[] } } = {\r\n fields,\r\n };\r\n\r\n // Intentar enviar al servidor\r\n const sendToServer = async () => {\r\n if (options?.merge || options?.mergeFields) {\r\n const fieldPaths = options.mergeFields || Object.keys(dataToWrite as object);\r\n body.updateMask = { fieldPaths };\r\n await client.patch(`/documents/${reference.path}`, body);\r\n } else {\r\n const parentPath = reference.path.split('/').slice(0, -1).join('/');\r\n const documentId = reference.id;\r\n await client.post(`/documents/${parentPath}?documentId=${documentId}`, { fields });\r\n }\r\n };\r\n\r\n if (pm && !pm.isOnline) {\r\n // Offline: la mutación ya está en la cola; no enviar al servidor\r\n return;\r\n }\r\n\r\n try {\r\n await sendToServer();\r\n // Si hay PM, marcar la mutación como completada (la última para este path)\r\n if (pm) {\r\n const mutations = await pm.mutations.getForPath(reference.path);\r\n if (mutations.length > 0) {\r\n await pm.completeMutation(mutations[mutations.length - 1].id);\r\n }\r\n }\r\n } catch (error) {\r\n // Si hay persistencia y es error de red, acepta silenciosamente\r\n if (pm) {\r\n const code = (error as { code?: string })?.code;\r\n if (code === 'unavailable' || code === 'deadline-exceeded') {\r\n return; // La mutación queda en la cola para sincronizar después\r\n }\r\n }\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Obtiene un documento forzando lectura desde el servidor\r\n * Siempre hace la petición al servidor, ignorando el cache local.\r\n * Actualiza el cache si la persistencia está habilitada.\r\n * \r\n * @param reference Referencia al documento\r\n * @returns Promise con DocumentSnapshot\r\n */\r\nexport async function getDocFromServer<T = DocumentData>(\r\n reference: DocumentReference<T>\r\n): Promise<DocumentSnapshot<T>> {\r\n const snapshot = await getDocDirect(reference);\r\n // Actualizar cache si hay persistencia\r\n const pm = getPersistenceManager(reference.firestore);\r\n if (pm) {\r\n await pm.cacheDocument(reference.path, snapshot.data() as DocumentData ?? null, snapshot.exists(), (snapshot as any)._updateTime);\r\n }\r\n return snapshot;\r\n}\r\n\r\n/**\r\n * Actualiza campos específicos de un documento\r\n * El documento debe existir\r\n * \r\n * Soporta dos formas de invocación:\r\n * 1. updateDoc(ref, { field: value, ... })\r\n * 2. updateDoc(ref, field, value, ...moreFieldsAndValues)\r\n * \r\n * @param reference Referencia al documento\r\n * @param dataOrField Objeto con campos o nombre del primer campo\r\n * @param moreFieldsAndValues Valores alternados field, value, field, value...\r\n * \r\n * @example\r\n * ```typescript\r\n * // Forma objeto\r\n * await updateDoc(doc(db, 'users', 'user123'), {\r\n * age: 26,\r\n * 'address.city': 'Madrid'\r\n * });\r\n * \r\n * // Forma alternada campo/valor\r\n * await updateDoc(doc(db, 'users', 'user123'), 'age', 26, 'name', 'Ana');\r\n * ```\r\n */\r\nexport async function updateDoc<T = DocumentData>(\r\n reference: DocumentReference<T>,\r\n dataOrField: Partial<T> | string,\r\n ...moreFieldsAndValues: unknown[]\r\n): Promise<void> {\r\n if (!reference || !reference.path) {\r\n throw new Error('updateDoc requires a valid DocumentReference');\r\n }\r\n\r\n // Si el segundo argumento es string, convertir pares campo/valor a objeto\r\n // Patrón: updateDoc(ref, field1, value1, field2, value2, ...)\r\n // moreFieldsAndValues = [value1, field2, value2, ...] → longitud debe ser impar (>= 1)\r\n let data: Partial<T>;\r\n if (typeof dataOrField === 'string') {\r\n if (moreFieldsAndValues.length === 0 || moreFieldsAndValues.length % 2 === 0) {\r\n throw createFirestoreError(\r\n 'invalid-argument',\r\n 'updateDoc with field/value pairs requires an even number of field/value arguments'\r\n );\r\n }\r\n const obj: Record<string, unknown> = { [dataOrField]: moreFieldsAndValues[0] };\r\n for (let i = 1; i < moreFieldsAndValues.length; i += 2) {\r\n const field = moreFieldsAndValues[i];\r\n if (typeof field !== 'string') {\r\n throw createFirestoreError(\r\n 'invalid-argument',\r\n 'Field paths must be strings in updateDoc'\r\n );\r\n }\r\n obj[field] = moreFieldsAndValues[i + 1];\r\n }\r\n data = obj as Partial<T>;\r\n } else {\r\n data = dataOrField;\r\n }\r\n\r\n if (!data || typeof data !== 'object') {\r\n throw new Error('updateDoc requires a valid data object');\r\n }\r\n \r\n const dataToWrite = applyConverterToData(reference, data as T);\r\n assertValidTypes(dataToWrite);\r\n \r\n const client = getHttpClient(reference.firestore);\r\n assertDocumentDepth(dataToWrite as DocumentData);\r\n const fields = toFirestoreFields(dataToWrite as DocumentData);\r\n assertDocumentSize(fields);\r\n\r\n notifyLocalWrite(reference.path);\r\n\r\n const pm = getPersistenceManager(reference.firestore);\r\n\r\n // Registrar escritura local (optimistic update) si hay persistencia\r\n if (pm) {\r\n await pm.addLocalWrite('update', reference.path, dataToWrite as DocumentData);\r\n }\r\n\r\n // Obtener campos a actualizar (incluyendo campos anidados)\r\n const fieldPaths = extractFieldPaths(dataToWrite as DocumentData);\r\n\r\n const sendToServer = async () => {\r\n await client.patch(`/documents/${reference.path}`, {\r\n fields,\r\n updateMask: { fieldPaths },\r\n });\r\n };\r\n\r\n if (pm && !pm.isOnline) {\r\n return; // Offline: mutación en cola\r\n }\r\n\r\n try {\r\n await sendToServer();\r\n if (pm) {\r\n const mutations = await pm.mutations.getForPath(reference.path);\r\n if (mutations.length > 0) {\r\n await pm.completeMutation(mutations[mutations.length - 1].id);\r\n }\r\n }\r\n } catch (error) {\r\n if (pm) {\r\n const code = (error as { code?: string })?.code;\r\n if (code === 'unavailable' || code === 'deadline-exceeded') {\r\n return;\r\n }\r\n }\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Extrae los paths de campos de un objeto (soporta dot notation)\r\n */\r\nfunction extractFieldPaths(data: DocumentData, prefix = ''): string[] {\r\n const paths: string[] = [];\r\n\r\n for (const [key, value] of Object.entries(data)) {\r\n if (value === undefined) {\r\n continue;\r\n }\r\n const fullPath = prefix ? `${prefix}.${key}` : key;\r\n\r\n // Si el key contiene un punto, es una actualización de campo anidado\r\n if (key.includes('.')) {\r\n paths.push(key);\r\n } else if (isFieldValueForExtract(value)) {\r\n // FieldValue especial - usar el path directamente, no recurrir\r\n paths.push(fullPath);\r\n } else if (value !== null && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date)) {\r\n // Objeto anidado - recurrir\r\n paths.push(...extractFieldPaths(value as DocumentData, fullPath));\r\n } else {\r\n paths.push(fullPath);\r\n }\r\n }\r\n\r\n return paths;\r\n}\r\n\r\n// =============================================================================\r\n// VALIDACIONES\r\n// =============================================================================\r\n\r\nconst MAX_DOCUMENT_SIZE_BYTES = 1024 * 1024; // 1 MiB\r\nconst MAX_DOCUMENT_DEPTH = 20;\r\n\r\nfunction createFirestoreError(code: FirestoreErrorCode, message: string): FirestoreError {\r\n const error = new Error(message) as FirestoreError;\r\n (error as { code: FirestoreErrorCode }).code = code;\r\n return error;\r\n}\r\n\r\nfunction assertDocumentSize(fields: Record<string, unknown>): void {\r\n const json = JSON.stringify({ fields });\r\n const size = typeof TextEncoder !== 'undefined'\r\n ? new TextEncoder().encode(json).length\r\n : json.length;\r\n\r\n if (size > MAX_DOCUMENT_SIZE_BYTES) {\r\n throw createFirestoreError('invalid-argument', 'Document size exceeds 1 MiB');\r\n }\r\n}\r\n\r\nfunction assertDocumentDepth(fields: Record<string, unknown>): void {\r\n if (getDepth(fields, 0) > MAX_DOCUMENT_DEPTH) {\r\n throw createFirestoreError('invalid-argument', 'Document exceeds maximum depth');\r\n }\r\n}\r\n\r\n/**\r\n * Valida que el documento no contenga tipos inválidos (funciones, símbolos)\r\n * undefined se ignora silenciosamente (comportamiento compatible con ignoreUndefinedProperties)\r\n * Excluye FieldValues que son objetos válidos con funciones (isEqual)\r\n */\r\nfunction assertValidTypes(data: unknown, path: string = ''): void {\r\n // undefined se ignora silenciosamente - será filtrado en toFirestoreFields\r\n if (data === undefined) {\r\n return;\r\n }\r\n\r\n if (typeof data === 'function') {\r\n throw createFirestoreError('invalid-argument', \r\n `Function setDoc() called with invalid data. Unsupported field value: a function${path ? ` (found in field ${path})` : ''}`);\r\n }\r\n\r\n if (typeof data === 'symbol') {\r\n throw createFirestoreError('invalid-argument', \r\n `Function setDoc() called with invalid data. Unsupported field value: a symbol${path ? ` (found in field ${path})` : ''}`);\r\n }\r\n\r\n if (data === null) {\r\n return;\r\n }\r\n\r\n if (Array.isArray(data)) {\r\n for (let i = 0; i < data.length; i++) {\r\n assertValidTypes(data[i], `${path}[${i}]`);\r\n }\r\n return;\r\n }\r\n\r\n if (typeof data === 'object') {\r\n // FieldValues tienen _type - no validar recursivamente\r\n if ('_type' in data && typeof (data as Record<string, unknown>)._type === 'string') {\r\n return;\r\n }\r\n \r\n for (const [key, value] of Object.entries(data as Record<string, unknown>)) {\r\n const fieldPath = path ? `${path}.${key}` : key;\r\n assertValidTypes(value, fieldPath);\r\n }\r\n }\r\n}\r\n\r\nfunction getDepth(value: unknown, current: number): number {\r\n if (value === null || value === undefined) {\r\n return current;\r\n }\r\n\r\n if (Array.isArray(value)) {\r\n let maxDepth = current;\r\n for (const item of value) {\r\n const depth = getDepth(item, current);\r\n if (depth > maxDepth) {\r\n maxDepth = depth;\r\n }\r\n }\r\n return maxDepth;\r\n }\r\n\r\n if (typeof value === 'object') {\r\n let maxDepth = current + 1;\r\n for (const item of Object.values(value as Record<string, unknown>)) {\r\n const depth = getDepth(item, current + 1);\r\n if (depth > maxDepth) {\r\n maxDepth = depth;\r\n }\r\n }\r\n return maxDepth;\r\n }\r\n\r\n return current;\r\n}\r\n\r\n/**\r\n * Verifica si un valor es un FieldValue (para extractFieldPaths)\r\n */\r\nfunction isFieldValueForExtract(value: unknown): boolean {\r\n return value !== null &&\r\n typeof value === 'object' &&\r\n '_type' in value &&\r\n typeof (value as { _type: unknown })._type === 'string';\r\n}\r\n\r\n/**\r\n * Elimina un documento de Firestore\r\n * \r\n * @param reference Referencia al documento\r\n * \r\n * @example\r\n * ```typescript\r\n * await deleteDoc(doc(db, 'users', 'user123'));\r\n * ```\r\n */\r\nexport async function deleteDoc(\r\n reference: DocumentReference\r\n): Promise<void> {\r\n if (!reference || !reference.path) {\r\n throw new Error('deleteDoc requires a valid DocumentReference');\r\n }\r\n const client = getHttpClient(reference.firestore);\r\n notifyLocalWrite(reference.path);\r\n\r\n const pm = getPersistenceManager(reference.firestore);\r\n\r\n // Registrar escritura local (optimistic update) si hay persistencia\r\n if (pm) {\r\n await pm.addLocalWrite('delete', reference.path, null);\r\n }\r\n\r\n if (pm && !pm.isOnline) {\r\n return; // Offline: mutación en cola\r\n }\r\n\r\n try {\r\n await client.delete(`/documents/${reference.path}`);\r\n if (pm) {\r\n const mutations = await pm.mutations.getForPath(reference.path);\r\n if (mutations.length > 0) {\r\n await pm.completeMutation(mutations[mutations.length - 1].id);\r\n }\r\n }\r\n } catch (error) {\r\n if (pm) {\r\n const code = (error as { code?: string })?.code;\r\n if (code === 'unavailable' || code === 'deadline-exceeded') {\r\n return;\r\n }\r\n }\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Añade un nuevo documento a una colección con ID auto-generado\r\n * \r\n * @param reference Referencia a la colección\r\n * @param data Datos del documento\r\n * @returns Referencia al nuevo documento\r\n * \r\n * @example\r\n * ```typescript\r\n * const newDocRef = await addDoc(collection(db, 'users'), {\r\n * name: 'Juan',\r\n * age: 25\r\n * });\r\n * console.log('Nuevo ID:', newDocRef.id);\r\n * ```\r\n */\r\nexport async function addDoc<T = DocumentData>(\r\n reference: CollectionReference<T>,\r\n data: T\r\n): Promise<DocumentReference<T>> {\r\n if (!reference || !reference.path) {\r\n throw new Error('addDoc requires a valid CollectionReference');\r\n }\r\n if (!data || typeof data !== 'object') {\r\n throw new Error('addDoc requires a valid data object');\r\n }\r\n const newId = generateId();\r\n const docRef = doc(reference, newId) as DocumentReference<T>;\r\n\r\n const converter = getConverter(reference);\r\n if (converter) {\r\n (docRef as { converter?: FirestoreDataConverter<T> | null }).converter = converter;\r\n }\r\n\r\n await setDoc(docRef, data);\r\n\r\n return docRef;\r\n}\r\n\r\n// =============================================================================\r\n// CONVERTERS\r\n// =============================================================================\r\n\r\nfunction getConverter<T>(\r\n reference: DocumentReference<T> | CollectionReference<T>\r\n): FirestoreDataConverter<T> | null | undefined {\r\n return (reference as { converter?: FirestoreDataConverter<T> | null }).converter;\r\n}\r\n\r\nfunction applyConverterToData<T>(\r\n reference: DocumentReference<T> | CollectionReference<T>,\r\n data: T\r\n): DocumentData {\r\n const converter = getConverter(reference);\r\n return converter ? converter.toFirestore(data) : (data as DocumentData);\r\n}\r\n","/**\r\n * Firestore SDK - Query Builder\r\n * query, where, orderBy, limit, limitToLast, startAt, startAfter, endAt, endBefore, getDocs\r\n */\r\n\r\nimport type {\r\n DocumentData,\r\n Query,\r\n CollectionReference,\r\n QuerySnapshot,\r\n QueryConstraint,\r\n WhereConstraint,\r\n OrderByConstraint,\r\n LimitConstraint,\r\n StartAtConstraint,\r\n EndAtConstraint,\r\n AndConstraint,\r\n OrConstraint,\r\n WhereFilterOp,\r\n OrderByDirection,\r\n FirestoreStructuredQuery,\r\n FirestoreValue,\r\n FirestoreDocument,\r\n FirestoreError,\r\n FirestoreErrorCode,\r\n} from './types';\r\nimport { getHttpClient } from '../transport/http';\r\nimport { createQuerySnapshot, toFirestoreValue, DocumentSnapshotImpl } from './snapshot';\r\nimport { applyConverterToQuerySnapshot, type FirestoreDataConverter } from './utilities';\r\nimport { getPersistenceManager } from '../persistence/persistence-manager';\r\nimport { QuerySnapshotImpl } from './snapshot';\r\n\r\n// ============================================================================\r\n// Query Implementation\r\n// ============================================================================\r\n\r\n/**\r\n * Implementación interna de Query\r\n */\r\nclass QueryImpl<T = DocumentData> implements Query<T> {\r\n readonly type = 'query';\r\n\r\n constructor(\r\n readonly firestore: any,\r\n readonly path: string,\r\n readonly constraints: QueryConstraint[] = []\r\n ) { }\r\n}\r\n\r\n// ============================================================================\r\n// Query Constraints\r\n// ============================================================================\r\n\r\n/**\r\n * Crea una nueva query con las restricciones especificadas\r\n * \r\n * @param reference Referencia a la colección\r\n * @param constraints Restricciones de query\r\n * @returns Nueva Query\r\n * \r\n * @example\r\n * ```typescript\r\n * const q = query(\r\n * collection(db, 'users'),\r\n * where('age', '>=', 18),\r\n * orderBy('age', 'desc'),\r\n * limit(10)\r\n * );\r\n * ```\r\n */\r\nexport function query<T = DocumentData>(\r\n reference: CollectionReference<T> | Query<T>,\r\n ...constraints: QueryConstraint[]\r\n): Query<T> {\r\n const existingConstraints = 'constraints' in reference\r\n ? (reference as QueryImpl<T>).constraints\r\n : [];\r\n\r\n const allConstraints = [...existingConstraints, ...constraints];\r\n \r\n // Validar conflictos de query\r\n validateQueryConflicts(allConstraints);\r\n\r\n const newQuery = new QueryImpl<T>(\r\n reference.firestore,\r\n reference.path,\r\n allConstraints\r\n );\r\n\r\n const converter = (reference as { converter?: FirestoreDataConverter<T> | null }).converter;\r\n if (converter) {\r\n (newQuery as { converter?: FirestoreDataConverter<T> | null }).converter = converter;\r\n }\r\n\r\n return newQuery;\r\n}\r\n\r\n/**\r\n * Valida conflictos críticos de query (in+not-in, !=+not-in, múltiples array-contains)\r\n */\r\nfunction validateQueryConflicts(constraints: QueryConstraint[]): void {\r\n const whereConstraints = constraints.filter((c): c is WhereConstraint => c.type === 'where');\r\n \r\n let hasIn = false;\r\n let hasNotIn = false;\r\n let hasNotEqual = false;\r\n let arrayContainsCount = 0;\r\n\r\n for (const w of whereConstraints) {\r\n if (w.op === 'in') hasIn = true;\r\n if (w.op === 'not-in') hasNotIn = true;\r\n if (w.op === '!=') hasNotEqual = true;\r\n if (w.op === 'array-contains') arrayContainsCount++;\r\n }\r\n\r\n // No puede haber in + not-in juntos\r\n if (hasIn && hasNotIn) {\r\n throw new Error(`Invalid Query. You cannot use 'in' and 'not-in' filters in the same query.`);\r\n }\r\n\r\n // No puede haber != + not-in juntos\r\n if (hasNotEqual && hasNotIn) {\r\n throw new Error(`Invalid Query. You cannot use '!=' and 'not-in' filters in the same query.`);\r\n }\r\n\r\n // No puede haber múltiples array-contains\r\n if (arrayContainsCount > 1) {\r\n throw new Error(`Invalid Query. You cannot use more than one 'array-contains' filter in a single query.`);\r\n }\r\n}\r\n\r\n/**\r\n * Crea una restricción WHERE\r\n * \r\n * @param field Campo a filtrar\r\n * @param op Operador de comparación\r\n * @param value Valor a comparar\r\n * \r\n * @example\r\n * ```typescript\r\n * where('age', '>=', 18)\r\n * where('status', '==', 'active')\r\n * where('tags', 'array-contains', 'tech')\r\n * ```\r\n */\r\n// Operadores válidos para validación en runtime\r\nconst VALID_OPS = new Set<string>([\r\n '<', '<=', '==', '!=', '>=', '>', \r\n 'array-contains', 'array-contains-any', 'in', 'not-in'\r\n]);\r\n\r\n// Operadores que aceptan arrays con límite de 30 elementos\r\nconst ARRAY_LIMIT_OPS = new Set<string>(['in', 'not-in', 'array-contains-any']);\r\n\r\nexport function where(\r\n field: string,\r\n op: WhereFilterOp,\r\n value: unknown\r\n): WhereConstraint {\r\n // Validar operador válido\r\n if (!VALID_OPS.has(op)) {\r\n throw new Error(`Invalid operator '${op}' passed to where(). Valid operators are: <, <=, ==, !=, >=, >, array-contains, array-contains-any, in, not-in.`);\r\n }\r\n\r\n // Validar que no sea undefined\r\n if (value === undefined) {\r\n throw new Error(`Function Query.where() requires a valid third argument, but it was undefined.`);\r\n }\r\n\r\n // Validar que no sea función\r\n if (typeof value === 'function') {\r\n throw new Error(`Function Query.where() requires a valid third argument, but it was a function.`);\r\n }\r\n\r\n // Validar que no sea símbolo\r\n if (typeof value === 'symbol') {\r\n throw new Error(`Function Query.where() requires a valid third argument, but it was a symbol.`);\r\n }\r\n\r\n // Validar límite de 30 elementos para in/not-in/array-contains-any\r\n if (ARRAY_LIMIT_OPS.has(op)) {\r\n if (!Array.isArray(value)) {\r\n throw new Error(`Invalid argument: in/not-in/array-contains-any requires an array value.`);\r\n }\r\n if (value.length > 30) {\r\n throw new Error(`Invalid Query. '${op}' filters support a maximum of 30 elements in the value array.`);\r\n }\r\n }\r\n\r\n return {\r\n type: 'where',\r\n field,\r\n op,\r\n value,\r\n };\r\n}\r\n\r\n/**\r\n * Crea una restricción ORDER BY\r\n * \r\n * @param field Campo por el cual ordenar\r\n * @param direction Dirección del orden ('asc' | 'desc')\r\n * \r\n * @example\r\n * ```typescript\r\n * orderBy('createdAt', 'desc')\r\n * orderBy('name') // 'asc' por defecto\r\n * ```\r\n */\r\nexport function orderBy(\r\n field: string,\r\n direction: OrderByDirection = 'asc'\r\n): OrderByConstraint {\r\n return {\r\n type: 'orderBy',\r\n field,\r\n direction,\r\n };\r\n}\r\n\r\n/**\r\n * Limita el número de resultados\r\n * \r\n * @param n Número máximo de documentos\r\n * \r\n * @example\r\n * ```typescript\r\n * limit(10)\r\n * ```\r\n */\r\nexport function limit(n: number): LimitConstraint {\r\n if (n <= 0) {\r\n throw new Error(`Function limit() requires a positive number, but it was: ${n}`);\r\n }\r\n return {\r\n type: 'limit',\r\n limit: n,\r\n };\r\n}\r\n\r\n/**\r\n * Limita el número de resultados desde el final\r\n * \r\n * @param n Número máximo de documentos\r\n * \r\n * @example\r\n * ```typescript\r\n * limitToLast(5)\r\n * ```\r\n */\r\nexport function limitToLast(n: number): LimitConstraint {\r\n if (n <= 0) {\r\n throw new Error(`Function limitToLast() requires a positive number, but it was: ${n}`);\r\n }\r\n return {\r\n type: 'limitToLast',\r\n limit: n,\r\n };\r\n}\r\n\r\n/**\r\n * Inicia la query desde un punto específico (inclusivo)\r\n * \r\n * @param values Valores de cursor\r\n * \r\n * @example\r\n * ```typescript\r\n * query(ref, orderBy('age'), startAt(18))\r\n * ```\r\n */\r\nexport function startAt(...values: unknown[]): StartAtConstraint {\r\n return {\r\n type: 'startAt',\r\n values,\r\n inclusive: true,\r\n };\r\n}\r\n\r\n/**\r\n * Inicia la query después de un punto específico (exclusivo)\r\n * \r\n * @param values Valores de cursor\r\n * \r\n * @example\r\n * ```typescript\r\n * query(ref, orderBy('age'), startAfter(18))\r\n * ```\r\n */\r\nexport function startAfter(...values: unknown[]): StartAtConstraint {\r\n return {\r\n type: 'startAfter',\r\n values,\r\n inclusive: false,\r\n };\r\n}\r\n\r\n/**\r\n * Termina la query en un punto específico (inclusivo)\r\n * \r\n * @param values Valores de cursor\r\n * \r\n * @example\r\n * ```typescript\r\n * query(ref, orderBy('age'), endAt(65))\r\n * ```\r\n */\r\nexport function endAt(...values: unknown[]): EndAtConstraint {\r\n return {\r\n type: 'endAt',\r\n values,\r\n inclusive: true,\r\n };\r\n}\r\n\r\n/**\r\n * Termina la query antes de un punto específico (exclusivo)\r\n * \r\n * @param values Valores de cursor\r\n * \r\n * @example\r\n * ```typescript\r\n * query(ref, orderBy('age'), endBefore(65))\r\n * ```\r\n */\r\nexport function endBefore(...values: unknown[]): EndAtConstraint {\r\n return {\r\n type: 'endBefore',\r\n values,\r\n inclusive: false,\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// Query Execution\r\n// ============================================================================\r\n\r\n/**\r\n * Ejecuta una query y obtiene los documentos\r\n * \r\n * @param queryRef Query a ejecutar\r\n * @returns Promise con QuerySnapshot\r\n * \r\n * @example\r\n * ```typescript\r\n * const q = query(collection(db, 'users'), where('age', '>=', 18));\r\n * const snapshot = await getDocs(q);\r\n * \r\n * snapshot.forEach(doc => {\r\n * console.log(doc.id, '=>', doc.data());\r\n * });\r\n * ```\r\n */\r\nexport async function getDocs<T = DocumentData>(\r\n queryRef: Query<T> | CollectionReference<T>\r\n): Promise<QuerySnapshot<T>> {\r\n const pm = getPersistenceManager(queryRef.firestore);\r\n\r\n // Sin persistencia: comportamiento original directo al servidor\r\n if (!pm) {\r\n return getDocsDirect(queryRef);\r\n }\r\n\r\n // Con persistencia habilitada\r\n if (pm.isOnline) {\r\n try {\r\n const snapshot = await getDocsDirect(queryRef);\r\n // Cachear cada documento del resultado\r\n for (const doc of snapshot.docs) {\r\n await pm.cacheDocument(doc.ref.path, doc.data() as DocumentData ?? null, doc.exists(), (doc as any)._updateTime);\r\n }\r\n // Cachear la query\r\n const queryKey = buildQueryCacheKey(queryRef);\r\n await pm.cacheQuery(queryKey, snapshot.docs.map(d => d.ref.path));\r\n return snapshot;\r\n } catch (error) {\r\n const code = (error as { code?: string })?.code;\r\n if (code === 'unavailable' || code === 'deadline-exceeded') {\r\n return getDocsFromLocalCache(queryRef, pm);\r\n }\r\n throw error;\r\n }\r\n }\r\n\r\n // Offline: leer de cache\r\n return getDocsFromLocalCache(queryRef, pm);\r\n}\r\n\r\n/**\r\n * Ejecuta la query directamente contra el servidor (sin cache)\r\n */\r\nasync function getDocsDirect<T = DocumentData>(\r\n queryRef: Query<T> | CollectionReference<T>\r\n): Promise<QuerySnapshot<T>> {\r\n const client = getHttpClient(queryRef.firestore);\r\n const constraints = 'constraints' in queryRef\r\n ? (queryRef as QueryImpl<T>).constraints\r\n : [];\r\n\r\n const flexibleQueries = Boolean((queryRef.firestore as { _config?: { allowFlexibleQueries?: boolean } })._config?.allowFlexibleQueries);\r\n validateQueryConstraints(constraints, { allowFlexibleQueries: flexibleQueries });\r\n\r\n // Detectar si es collectionGroup\r\n const isCollectionGroupQuery = (queryRef as { type?: string }).type === 'collectionGroup';\r\n\r\n // Construir StructuredQuery\r\n const structuredQuery = buildStructuredQuery(queryRef.path, constraints, isCollectionGroupQuery);\r\n\r\n // Ejecutar query\r\n const response = await client.post<{ document?: FirestoreDocument }[]>(\r\n '/documents:runQuery',\r\n { structuredQuery }\r\n );\r\n\r\n // Filtrar respuestas vacías y crear snapshot\r\n const documents = response\r\n .filter(r => r.document)\r\n .map(r => r.document!);\r\n\r\n // Convertir a Query si es CollectionReference\r\n const queryForSnapshot: Query<T> = 'constraints' in queryRef\r\n ? queryRef as Query<T>\r\n : new QueryImpl<T>(queryRef.firestore, queryRef.path, []);\r\n\r\n const snapshot = createQuerySnapshot(queryForSnapshot, documents) as QuerySnapshot<DocumentData>;\r\n const converter = (queryRef as { converter?: FirestoreDataConverter<T> | null }).converter;\r\n return applyConverterToQuerySnapshot(snapshot, converter);\r\n}\r\n\r\n/**\r\n * Lee documentos del cache cuando no hay conectividad\r\n */\r\nasync function getDocsFromLocalCache<T = DocumentData>(\r\n queryRef: Query<T> | CollectionReference<T>,\r\n pm: NonNullable<ReturnType<typeof getPersistenceManager>>\r\n): Promise<QuerySnapshot<T>> {\r\n const queryForSnapshot: Query<T> = 'constraints' in queryRef\r\n ? queryRef as Query<T>\r\n : new QueryImpl<T>(queryRef.firestore, queryRef.path, []);\r\n\r\n // Intentar usar el cache de query\r\n const queryKey = buildQueryCacheKey(queryRef);\r\n const cachedPaths = await pm.getCachedQuery(queryKey);\r\n\r\n if (cachedPaths) {\r\n // Reconstruir documentos desde el cache\r\n const docs: DocumentSnapshotImpl<T>[] = [];\r\n for (const path of cachedPaths) {\r\n const cached = await pm.getCachedDocument(path);\r\n if (!cached || !cached.exists) continue;\r\n const segments = path.split('/');\r\n const ref = {\r\n type: 'document' as const,\r\n path,\r\n id: segments[segments.length - 1],\r\n firestore: queryRef.firestore,\r\n parent: {\r\n type: 'collection' as const,\r\n path: segments.slice(0, -1).join('/'),\r\n id: segments[segments.length - 2] || '',\r\n firestore: queryRef.firestore,\r\n parent: null,\r\n },\r\n } as any;\r\n docs.push(new DocumentSnapshotImpl<T>(\r\n ref,\r\n cached.data as T,\r\n true,\r\n { fromCache: true, hasPendingWrites: await pm.hasPendingWrites(path) }\r\n ));\r\n }\r\n\r\n return new QuerySnapshotImpl<T>(queryForSnapshot, docs, [], { fromCache: true }) as QuerySnapshot<T>;\r\n }\r\n\r\n // Sin cache de query: intentar obtener de la colección\r\n const collectionDocs = await pm.getCachedCollection(queryRef.path);\r\n const docs = [];\r\n for (const cached of collectionDocs) {\r\n const segments = cached.path.split('/');\r\n const ref = {\r\n type: 'document' as const,\r\n path: cached.path,\r\n id: segments[segments.length - 1],\r\n firestore: queryRef.firestore,\r\n parent: {\r\n type: 'collection' as const,\r\n path: segments.slice(0, -1).join('/'),\r\n id: segments[segments.length - 2] || '',\r\n firestore: queryRef.firestore,\r\n parent: null,\r\n },\r\n } as any;\r\n docs.push(new DocumentSnapshotImpl<T>(\r\n ref,\r\n cached.data as T,\r\n true,\r\n { fromCache: true, hasPendingWrites: await pm.hasPendingWrites(cached.path) }\r\n ));\r\n }\r\n\r\n return new QuerySnapshotImpl<T>(queryForSnapshot, docs, [], { fromCache: true }) as QuerySnapshot<T>;\r\n}\r\n\r\n/**\r\n * Obtiene documentos exclusivamente del cache local.\r\n * Compatible con Firestore Web SDK: getDocsFromCache(query)\r\n */\r\nexport async function getDocsFromCache<T = DocumentData>(\r\n queryRef: Query<T> | CollectionReference<T>\r\n): Promise<QuerySnapshot<T>> {\r\n const pm = getPersistenceManager(queryRef.firestore);\r\n if (!pm) {\r\n throw createFirestoreError(\r\n 'failed-precondition',\r\n 'Failed to get documents from cache. Persistence is not enabled.'\r\n );\r\n }\r\n\r\n return getDocsFromLocalCache(queryRef, pm);\r\n}\r\n\r\n/**\r\n * Genera una clave de cache para una query basada en path + constraints\r\n */\r\nfunction buildQueryCacheKey<T>(queryRef: Query<T> | CollectionReference<T>): string {\r\n const constraints = 'constraints' in queryRef\r\n ? (queryRef as QueryImpl<T>).constraints\r\n : [];\r\n return `${queryRef.path}:${JSON.stringify(constraints)}`;\r\n}\r\n\r\n// =============================================================================\r\n/**\r\n * Obtiene documentos forzando lectura desde el servidor\r\n * Siempre toca el servidor, ignorando el cache local.\r\n * Actualiza el cache si la persistencia está habilitada.\r\n * \r\n * @param queryRef Query o referencia a colección \r\n * @returns Promise con QuerySnapshot\r\n */\r\nexport async function getDocsFromServer<T = DocumentData>(\r\n queryRef: Query<T> | CollectionReference<T>\r\n): Promise<QuerySnapshot<T>> {\r\n const snapshot = await getDocsDirect(queryRef);\r\n // Actualizar cache si hay persistencia\r\n const pm = getPersistenceManager(queryRef.firestore);\r\n if (pm) {\r\n for (const doc of snapshot.docs) {\r\n await pm.cacheDocument(doc.ref.path, doc.data() as DocumentData ?? null, doc.exists(), (doc as any)._updateTime);\r\n }\r\n }\r\n return snapshot;\r\n}\r\n\r\n// VALIDACIONES\r\n// =============================================================================\r\n\r\nconst INEQUALITY_OPS = new Set<WhereFilterOp>(['<', '<=', '>', '>=', '!=', 'not-in']);\r\nconst ARRAY_VALUE_OPS = new Set<WhereFilterOp>(['in', 'not-in', 'array-contains-any']);\r\n\r\nfunction createFirestoreError(code: FirestoreErrorCode, message: string): FirestoreError {\r\n const error = new Error(message) as FirestoreError;\r\n (error as { code: FirestoreErrorCode }).code = code;\r\n return error;\r\n}\r\n\r\nexport function validateQueryConstraints(\r\n constraints: QueryConstraint[],\r\n options?: { allowFlexibleQueries?: boolean }\r\n): void {\r\n const whereConstraints = collectWhereConstraints(constraints);\r\n const orderByConstraints = constraints.filter(c => c.type === 'orderBy') as OrderByConstraint[];\r\n const allowFlexibleQueries = options?.allowFlexibleQueries ?? false;\r\n\r\n const inequalityFields = new Set<string>();\r\n for (const w of whereConstraints) {\r\n if (ARRAY_VALUE_OPS.has(w.op)) {\r\n validateArrayOperator(w);\r\n }\r\n if (INEQUALITY_OPS.has(w.op)) {\r\n inequalityFields.add(w.field);\r\n }\r\n }\r\n\r\n if (inequalityFields.size > 1) {\r\n throw createFirestoreError(\r\n 'invalid-argument',\r\n 'Firestore requires inequality filters on a single field'\r\n );\r\n }\r\n\r\n if (!allowFlexibleQueries && inequalityFields.size === 1 && orderByConstraints.length > 0) {\r\n const inequalityField = [...inequalityFields][0];\r\n const firstOrderBy = orderByConstraints[0];\r\n if (firstOrderBy.field !== inequalityField) {\r\n throw createFirestoreError(\r\n 'invalid-argument',\r\n `Firestore requires an orderBy on '${inequalityField}' to appear first`\r\n );\r\n }\r\n }\r\n}\r\n\r\nfunction validateArrayOperator(where: WhereConstraint): void {\r\n if (!Array.isArray(where.value)) {\r\n throw createFirestoreError(\r\n 'invalid-argument',\r\n `Operator '${where.op}' requires an array value`\r\n );\r\n }\r\n\r\n if (where.value.length === 0 || where.value.length > 10) {\r\n throw createFirestoreError(\r\n 'invalid-argument',\r\n `Operator '${where.op}' requires a non-empty array with at most 10 elements`\r\n );\r\n }\r\n}\r\n\r\nfunction collectWhereConstraints(constraints: QueryConstraint[]): WhereConstraint[] {\r\n const result: WhereConstraint[] = [];\r\n for (const constraint of constraints) {\r\n if (constraint.type === 'where') {\r\n result.push(constraint as WhereConstraint);\r\n } else if (constraint.type === 'and' || constraint.type === 'or') {\r\n const composite = constraint as AndConstraint | OrConstraint;\r\n result.push(...collectWhereConstraints(composite.constraints));\r\n }\r\n }\r\n return result;\r\n}\r\n\r\n/**\r\n * Construye un StructuredQuery a partir de constraints\r\n */\r\nfunction buildStructuredQuery(\r\n collectionPath: string,\r\n constraints: QueryConstraint[],\r\n isCollectionGroup = false\r\n): FirestoreStructuredQuery {\r\n const query: FirestoreStructuredQuery = {\r\n from: [{\r\n collectionId: getCollectionSelectorPath(collectionPath, isCollectionGroup),\r\n allDescendants: isCollectionGroup,\r\n }],\r\n };\r\n\r\n // Procesar constraints\r\n const wheres: WhereConstraint[] = [];\r\n const compositeFilters: (AndConstraint | OrConstraint)[] = [];\r\n const orderBys: OrderByConstraint[] = [];\r\n let limitValue: number | undefined;\r\n let limitToLastValue: number | undefined;\r\n let offsetValue: number | undefined;\r\n let startCursor: { values: FirestoreValue[]; before?: boolean } | undefined;\r\n let endCursor: { values: FirestoreValue[]; before?: boolean } | undefined;\r\n\r\n for (const constraint of constraints) {\r\n switch (constraint.type) {\r\n case 'where':\r\n wheres.push(constraint as WhereConstraint);\r\n break;\r\n case 'and':\r\n case 'or':\r\n compositeFilters.push(constraint as AndConstraint | OrConstraint);\r\n break;\r\n case 'orderBy':\r\n orderBys.push(constraint as OrderByConstraint);\r\n break;\r\n case 'limit':\r\n limitValue = (constraint as LimitConstraint).limit;\r\n break;\r\n case 'limitToLast':\r\n limitToLastValue = (constraint as LimitConstraint).limit;\r\n break;\r\n case 'offset':\r\n offsetValue = (constraint as { type: 'offset'; count: number }).count;\r\n break;\r\n case 'startAt':\r\n case 'startAfter': {\r\n const c = constraint as StartAtConstraint;\r\n // Extraer valores de cursor, manejando DocumentSnapshots\r\n const cursorValues = extractCursorValues(c.values, orderBys);\r\n startCursor = {\r\n values: cursorValues.map(v => toFirestoreValue(v)),\r\n before: c.inclusive,\r\n };\r\n break;\r\n }\r\n case 'endAt':\r\n case 'endBefore': {\r\n const c = constraint as EndAtConstraint;\r\n // Extraer valores de cursor, manejando DocumentSnapshots\r\n const cursorValues = extractCursorValues(c.values, orderBys);\r\n endCursor = {\r\n values: cursorValues.map(v => toFirestoreValue(v)),\r\n before: !c.inclusive,\r\n };\r\n break;\r\n }\r\n }\r\n }\r\n\r\n // Construir WHERE con soporte para filtros compuestos\r\n const allFilters: object[] = [];\r\n\r\n // Añadir wheres simples\r\n for (const w of wheres) {\r\n const filter = buildWhereFilter(w);\r\n if (filter) {\r\n allFilters.push(filter);\r\n }\r\n }\r\n\r\n // Añadir filtros compuestos (and/or)\r\n for (const cf of compositeFilters) {\r\n allFilters.push(buildCompositeFilterQuery(cf));\r\n }\r\n\r\n if (allFilters.length === 1) {\r\n query.where = allFilters[0] as FirestoreStructuredQuery['where'];\r\n } else if (allFilters.length > 1) {\r\n query.where = {\r\n compositeFilter: {\r\n op: 'AND',\r\n filters: allFilters,\r\n },\r\n };\r\n }\r\n\r\n // Construir ORDER BY\r\n if (orderBys.length > 0) {\r\n query.orderBy = orderBys.map(ob => ({\r\n field: { fieldPath: ob.field },\r\n direction: ob.direction === 'desc' ? 'DESCENDING' : 'ASCENDING',\r\n }));\r\n }\r\n\r\n // Límites\r\n if (limitValue !== undefined) {\r\n query.limit = limitValue;\r\n }\r\n\r\n // Para limitToLast, enviar como limitToLast (el backend maneja la inversión)\r\n if (limitToLastValue !== undefined) {\r\n query.limitToLast = limitToLastValue;\r\n }\r\n\r\n // Offset (paginación directa)\r\n if (offsetValue !== undefined) {\r\n query.offset = offsetValue;\r\n }\r\n\r\n // Cursores\r\n if (startCursor) {\r\n query.startAt = startCursor;\r\n }\r\n if (endCursor) {\r\n query.endAt = endCursor;\r\n }\r\n\r\n return query;\r\n}\r\n\r\n/**\r\n * Construye un filtro WHERE para la API\r\n */\r\nfunction buildWhereFilter(where: WhereConstraint): FirestoreStructuredQuery['where'] {\r\n const fieldPath = where.field;\r\n const value = toFirestoreValue(where.value);\r\n\r\n // Mapear operadores a formato Firestore\r\n const opMap: Record<WhereFilterOp, string> = {\r\n '<': 'LESS_THAN',\r\n '<=': 'LESS_THAN_OR_EQUAL',\r\n '==': 'EQUAL',\r\n '!=': 'NOT_EQUAL',\r\n '>': 'GREATER_THAN',\r\n '>=': 'GREATER_THAN_OR_EQUAL',\r\n 'array-contains': 'ARRAY_CONTAINS',\r\n 'array-contains-any': 'ARRAY_CONTAINS_ANY',\r\n 'in': 'IN',\r\n 'not-in': 'NOT_IN',\r\n };\r\n\r\n // Operadores especiales que usan formato diferente\r\n if (where.op === 'array-contains-any' || where.op === 'in' || where.op === 'not-in') {\r\n return {\r\n fieldFilter: {\r\n field: { fieldPath },\r\n op: opMap[where.op] as any,\r\n value,\r\n },\r\n };\r\n }\r\n\r\n return {\r\n fieldFilter: {\r\n field: { fieldPath },\r\n op: opMap[where.op] as any,\r\n value,\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Construye un filtro compuesto (AND/OR) recursivamente\r\n */\r\nfunction buildCompositeFilterQuery(\r\n constraint: AndConstraint | OrConstraint\r\n): object {\r\n const op = constraint.type === 'and' ? 'AND' : 'OR';\r\n\r\n const filters = constraint.constraints.map(c => {\r\n if (c.type === 'and' || c.type === 'or') {\r\n // Recursivo para filtros anidados\r\n return buildCompositeFilterQuery(c as AndConstraint | OrConstraint);\r\n }\r\n // Es un WhereConstraint simple\r\n return buildWhereFilter(c as WhereConstraint);\r\n });\r\n\r\n return {\r\n compositeFilter: {\r\n op,\r\n filters,\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Obtiene el ID de la colección desde el path completo\r\n */\r\nfunction getCollectionId(path: string): string {\r\n const parts = path.split('/');\r\n return parts[parts.length - 1];\r\n}\r\n\r\n/**\r\n * Retorna el selector de colección para StructuredQuery.\r\n * - Para collectionGroup: solo el ID final.\r\n * - Para colección normal: path completo (incluye subcolecciones).\r\n */\r\nfunction getCollectionSelectorPath(path: string, isCollectionGroup: boolean): string {\r\n if (isCollectionGroup) {\r\n return getCollectionId(path);\r\n }\r\n return path;\r\n}\r\n\r\n/**\r\n * Extrae valores de cursor, manejando DocumentSnapshots\r\n * Si el valor es un DocumentSnapshot, extrae los valores de los campos ordenados\r\n */\r\nfunction extractCursorValues(values: unknown[], orderBys: OrderByConstraint[]): unknown[] {\r\n if (values.length === 0) return values;\r\n\r\n const first = values[0];\r\n\r\n // Verificar si es un DocumentSnapshot (tiene métodos exists() y data())\r\n if (isDocumentSnapshot(first)) {\r\n const snapshot = first as { data: () => Record<string, unknown> | undefined; id: string };\r\n const data = snapshot.data();\r\n\r\n if (!data) {\r\n return values; // Snapshot sin data, usar valores directamente\r\n }\r\n\r\n // Si hay orderBys, extraer valores de esos campos\r\n if (orderBys.length > 0) {\r\n return orderBys.map(ob => {\r\n // Manejar __name__ (documentId)\r\n if (ob.field === '__name__') {\r\n return snapshot.id;\r\n }\r\n return getNestedValue(data, ob.field);\r\n });\r\n }\r\n\r\n // Sin orderBys explícitos, Firestore ordena por __name__ por defecto\r\n return [snapshot.id];\r\n }\r\n\r\n // No es un snapshot, retornar valores directamente\r\n return values;\r\n}\r\n\r\n/**\r\n * Verifica si un valor parece ser un DocumentSnapshot\r\n */\r\nfunction isDocumentSnapshot(value: unknown): boolean {\r\n if (!value || typeof value !== 'object') return false;\r\n const obj = value as Record<string, unknown>;\r\n return typeof obj.exists === 'function' &&\r\n typeof obj.data === 'function' &&\r\n typeof obj.id === 'string';\r\n}\r\n\r\n/**\r\n * Obtiene un valor anidado de un objeto usando un path con puntos\r\n */\r\nfunction getNestedValue(obj: Record<string, unknown>, path: string): unknown {\r\n const parts = path.split('.');\r\n let current: unknown = obj;\r\n\r\n for (const part of parts) {\r\n if (current === null || current === undefined) return undefined;\r\n if (typeof current !== 'object') return undefined;\r\n current = (current as Record<string, unknown>)[part];\r\n }\r\n\r\n return current;\r\n}\r\n","/**\r\n * Firestore SDK - Cliente WebSocket\r\n * Conexión real-time con el servidor\r\n * Soporta navegador y Node.js (requiere inyectar WebSocket global en Node)\r\n */\r\n\r\nimport type {\r\n FirestoreInstance,\r\n WebSocketMessage,\r\n Unsubscribe,\r\n} from '../firestore/types';\r\n\r\n/** Estado de conexión */\r\ntype ConnectionState = 'connecting' | 'connected' | 'disconnected' | 'error';\r\n\r\n/** Callback para cambios de documento */\r\ntype ChangeCallback = (change: WebSocketMessage) => void;\r\n\r\n/** Callback para errores */\r\ntype ErrorCallback = (error: Error) => void;\r\n\r\n/** WebSocket isomórfico: usa el global del navegador o de Node.js */\r\nfunction getWebSocketConstructor(): typeof WebSocket | null {\r\n // Verificar si existe WebSocket global (navegador o Node.js con polyfill)\r\n if (typeof globalThis.WebSocket !== 'undefined') {\r\n return globalThis.WebSocket;\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Normaliza el host para WebSocket\r\n */\r\nfunction normalizeWebSocketHost(rawHost: string, ssl: boolean): string {\r\n const trimmed = rawHost.trim().replace(/\\/+$/, '');\r\n\r\n if (trimmed.startsWith('ws://') || trimmed.startsWith('wss://')) {\r\n return trimmed;\r\n }\r\n\r\n if (trimmed.startsWith('http://')) {\r\n return `ws://${trimmed.slice('http://'.length)}`;\r\n }\r\n\r\n if (trimmed.startsWith('https://')) {\r\n return `wss://${trimmed.slice('https://'.length)}`;\r\n }\r\n\r\n const protocol = ssl ? 'wss' : 'ws';\r\n return `${protocol}://${trimmed}`;\r\n}\r\n\r\n/**\r\n * Cliente WebSocket para Firestore real-time\r\n */\r\nexport class WebSocketClient {\r\n private readonly firestore: FirestoreInstance;\r\n private readonly url: string;\r\n private socket: WebSocket | null = null;\r\n private state: ConnectionState = 'disconnected';\r\n private reconnectAttempts = 0;\r\n private readonly maxReconnectAttempts = 5;\r\n private readonly reconnectDelay = 1000;\r\n private authToken: string | null = null;\r\n private syncTimer: ReturnType<typeof setTimeout> | null = null;\r\n\r\n // Suscripciones activas\r\n private subscriptions = new Map<string, {\r\n path?: string;\r\n query?: unknown;\r\n callback: ChangeCallback;\r\n errorCallback?: ErrorCallback;\r\n resumeToken?: string;\r\n includeMetadataChanges?: boolean;\r\n /** Flag para collectionGroup: escuchar todas las subcolecciones */\r\n allDescendants?: boolean;\r\n }>();\r\n\r\n // ID para suscripciones\r\n private subscriptionIdCounter = 0;\r\n\r\n constructor(firestore: FirestoreInstance) {\r\n this.firestore = firestore;\r\n const host = normalizeWebSocketHost(firestore._config.host, firestore._config.ssl);\r\n this.url = `${host}/v1/projects/${firestore._projectId}/databases/${firestore._databaseId}/documents:listen`;\r\n }\r\n\r\n /**\r\n * Establece el token de autenticación\r\n */\r\n setAuthToken(token: string | null): void {\r\n this.authToken = token;\r\n // Si ya está conectado, reconectar con nuevo token\r\n if (this.socket && this.state === 'connected') {\r\n this.reconnect();\r\n }\r\n }\r\n\r\n /**\r\n * Conecta al WebSocket\r\n */\r\n private async connect(): Promise<void> {\r\n if (this.state === 'connecting' || this.state === 'connected') {\r\n return;\r\n }\r\n\r\n this.state = 'connecting';\r\n\r\n return new Promise((resolve, reject) => {\r\n try {\r\n // Obtener constructor de WebSocket (navegador o Node.js)\r\n const WS = getWebSocketConstructor();\r\n if (!WS) {\r\n this.state = 'error';\r\n reject(new Error('WebSocket not available in this environment'));\r\n return;\r\n }\r\n\r\n // Conexión sin token en URL (seguridad: evita exposición en logs/proxies)\r\n this.socket = new WS(this.url) as WebSocket;\r\n\r\n this.socket.onopen = () => {\r\n this.state = 'connected';\r\n this.reconnectAttempts = 0;\r\n // Enviar autenticación como primer mensaje (antes de suscripciones)\r\n this.sendAuth();\r\n // Re-suscribirse a todas las suscripciones activas\r\n this.resubscribeAll();\r\n resolve();\r\n };\r\n\r\n this.socket.onclose = () => {\r\n this.state = 'disconnected';\r\n this.handleDisconnect();\r\n };\r\n\r\n this.socket.onerror = () => {\r\n this.state = 'error';\r\n reject(new Error('WebSocket connection failed'));\r\n };\r\n\r\n this.socket.onmessage = (event) => {\r\n this.handleMessage(event.data);\r\n };\r\n } catch (error) {\r\n this.state = 'error';\r\n reject(error);\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Maneja desconexión e intenta reconectar\r\n */\r\n private handleDisconnect(): void {\r\n if (this.reconnectAttempts < this.maxReconnectAttempts && this.subscriptions.size > 0) {\r\n this.reconnectAttempts++;\r\n const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);\r\n setTimeout(() => this.connect(), delay);\r\n }\r\n }\r\n\r\n /**\r\n * Reconecta forzadamente\r\n */\r\n private reconnect(): void {\r\n this.socket?.close();\r\n this.state = 'disconnected';\r\n this.connect();\r\n }\r\n\r\n /**\r\n * Envía el token de autenticación como mensaje (no en URL)\r\n */\r\n private sendAuth(): void {\r\n if (this.socket && this.state === 'connected' && this.authToken) {\r\n this.socket.send(JSON.stringify({ type: 'auth', token: this.authToken }));\r\n }\r\n }\r\n\r\n /**\r\n * Re-suscribe a todas las suscripciones activas\r\n */\r\n private resubscribeAll(): void {\r\n for (const [id, sub] of this.subscriptions) {\r\n this.sendSubscribe(id, sub.path, sub.query, sub.includeMetadataChanges, sub.allDescendants);\r\n if (sub.resumeToken && sub.path) {\r\n this.sendResume(id, sub.resumeToken, sub.path);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Envía mensaje de suscripción\r\n */\r\n private sendSubscribe(\r\n id: string,\r\n path?: string,\r\n query?: unknown,\r\n includeMetadataChanges?: boolean,\r\n allDescendants?: boolean\r\n ): void {\r\n if (this.socket && this.state === 'connected') {\r\n const message: WebSocketMessage = {\r\n type: 'subscribe',\r\n id,\r\n path,\r\n query,\r\n includeMetadataChanges,\r\n allDescendants,\r\n };\r\n this.socket.send(JSON.stringify(message));\r\n }\r\n }\r\n\r\n /**\r\n * Envía mensaje de reanudación con resumeToken\r\n */\r\n private sendResume(id: string, resumeToken: string, path: string): void {\r\n if (this.socket && this.state === 'connected') {\r\n const message: WebSocketMessage = {\r\n type: 'resume',\r\n id,\r\n resumeToken,\r\n path,\r\n };\r\n this.socket.send(JSON.stringify(message));\r\n }\r\n }\r\n\r\n /**\r\n * Notifica onSnapshotsInSync con debounce\r\n */\r\n private scheduleSnapshotsSync(): void {\r\n const firestoreInternal = this.firestore as unknown as {\r\n _syncCallbacks?: Set<() => void>;\r\n };\r\n\r\n if (!firestoreInternal._syncCallbacks || firestoreInternal._syncCallbacks.size === 0) {\r\n return;\r\n }\r\n\r\n if (this.syncTimer) {\r\n clearTimeout(this.syncTimer);\r\n }\r\n\r\n this.syncTimer = setTimeout(() => {\r\n firestoreInternal._syncCallbacks?.forEach(cb => cb());\r\n }, 50);\r\n }\r\n\r\n /**\r\n * Envía mensaje de cancelación de suscripción\r\n */\r\n private sendUnsubscribe(id: string): void {\r\n if (this.socket && this.state === 'connected') {\r\n const message: WebSocketMessage = {\r\n type: 'unsubscribe',\r\n id,\r\n };\r\n this.socket.send(JSON.stringify(message));\r\n }\r\n }\r\n\r\n /**\r\n * Maneja mensajes entrantes\r\n */\r\n private handleMessage(data: string): void {\r\n try {\r\n const message = JSON.parse(data) as WebSocketMessage;\r\n\r\n switch (message.type) {\r\n case 'document_change':\r\n case 'query_change':\r\n case 'metadata_change':\r\n case 'missed_event': {\r\n const sub = this.subscriptions.get(message.id || '');\r\n if (sub) {\r\n if (message.resumeToken) {\r\n sub.resumeToken = message.resumeToken;\r\n }\r\n sub.callback(message);\r\n this.scheduleSnapshotsSync();\r\n }\r\n break;\r\n }\r\n\r\n case 'snapshots_in_sync': {\r\n this.scheduleSnapshotsSync();\r\n break;\r\n }\r\n\r\n case 'error': {\r\n const sub = this.subscriptions.get(message.id || '');\r\n if (sub?.errorCallback) {\r\n sub.errorCallback(new Error(message.error || 'Unknown error'));\r\n }\r\n break;\r\n }\r\n }\r\n } catch {\r\n // Ignorar mensajes malformados\r\n }\r\n }\r\n\r\n /**\r\n * Suscribe a cambios de un documento\r\n */\r\n async subscribeToDocument(\r\n path: string,\r\n callback: ChangeCallback,\r\n errorCallback?: ErrorCallback,\r\n includeMetadataChanges?: boolean\r\n ): Promise<Unsubscribe> {\r\n // Conectar si no está conectado\r\n if (this.state !== 'connected') {\r\n await this.connect();\r\n }\r\n\r\n // Generar ID único\r\n const id = `doc_${++this.subscriptionIdCounter}`;\r\n\r\n // Guardar suscripción\r\n this.subscriptions.set(id, {\r\n path,\r\n callback,\r\n errorCallback,\r\n includeMetadataChanges,\r\n });\r\n\r\n // Enviar suscripción\r\n this.sendSubscribe(id, path, undefined, includeMetadataChanges);\r\n\r\n // Retornar función de cancelación\r\n return () => {\r\n this.subscriptions.delete(id);\r\n this.sendUnsubscribe(id);\r\n\r\n // Desconectar si no hay más suscripciones\r\n if (this.subscriptions.size === 0) {\r\n this.socket?.close();\r\n this.state = 'disconnected';\r\n }\r\n };\r\n }\r\n\r\n /**\r\n * Suscribe a cambios de una query\r\n */\r\n async subscribeToQuery(\r\n query: unknown,\r\n callback: ChangeCallback,\r\n errorCallback?: ErrorCallback,\r\n path?: string,\r\n includeMetadataChanges?: boolean,\r\n allDescendants?: boolean\r\n ): Promise<Unsubscribe> {\r\n // Conectar si no está conectado\r\n if (this.state !== 'connected') {\r\n await this.connect();\r\n }\r\n\r\n // Generar ID único\r\n const id = `query_${++this.subscriptionIdCounter}`;\r\n\r\n // Guardar suscripción\r\n this.subscriptions.set(id, {\r\n query,\r\n callback,\r\n errorCallback,\r\n path,\r\n includeMetadataChanges,\r\n allDescendants,\r\n });\r\n\r\n // Enviar suscripción\r\n this.sendSubscribe(id, path, query, includeMetadataChanges, allDescendants);\r\n\r\n // Retornar función de cancelación\r\n return () => {\r\n this.subscriptions.delete(id);\r\n this.sendUnsubscribe(id);\r\n\r\n // Desconectar si no hay más suscripciones\r\n if (this.subscriptions.size === 0) {\r\n this.socket?.close();\r\n this.state = 'disconnected';\r\n }\r\n };\r\n }\r\n\r\n /**\r\n * Cierra la conexión\r\n */\r\n close(): void {\r\n this.subscriptions.clear();\r\n this.socket?.close();\r\n this.state = 'disconnected';\r\n }\r\n\r\n /**\r\n * Fuerza desconexión sin limpiar suscripciones (para pruebas de reconexión)\r\n */\r\n simulateDisconnect(): void {\r\n if (this.socket) {\r\n this.socket.close();\r\n }\r\n }\r\n}\r\n\r\n// Cache de clientes por instancia de Firestore\r\nconst clients = new WeakMap<FirestoreInstance, WebSocketClient>();\r\n\r\n/**\r\n * Obtiene o crea un cliente WebSocket para una instancia de Firestore\r\n */\r\nexport function getWebSocketClient(firestore: FirestoreInstance): WebSocketClient {\r\n let client = clients.get(firestore);\r\n if (!client) {\r\n client = new WebSocketClient(firestore);\r\n clients.set(firestore, client);\r\n }\r\n return client;\r\n}\r\n","/**\r\n * Firestore SDK - Real-time Listeners\r\n * onSnapshot para documentos y queries\r\n */\r\n\r\nimport type {\r\n DocumentData,\r\n DocumentReference,\r\n Query,\r\n CollectionReference,\r\n DocumentSnapshot,\r\n QuerySnapshot,\r\n Unsubscribe,\r\n SnapshotListenOptions,\r\n} from './types';\r\nimport { getWebSocketClient } from '../transport/websocket';\r\nimport { getDoc } from './crud';\r\nimport { getDocsFromCache } from './query';\r\nimport { DocumentSnapshotImpl } from './snapshot';\r\nimport { registerLocalWriteListener } from './local-write-tracker';\r\nimport { getPersistenceManager } from '../persistence/persistence-manager';\r\n\r\n/**\r\n * Callback para cambios en documentos\r\n */\r\nexport type DocumentSnapshotCallback<T = DocumentData> = (\r\n snapshot: DocumentSnapshot<T>\r\n) => void;\r\n\r\n/**\r\n * Callback para cambios en queries\r\n */\r\nexport type QuerySnapshotCallback<T = DocumentData> = (\r\n snapshot: QuerySnapshot<T>\r\n) => void;\r\n\r\n/**\r\n * Callback para errores\r\n */\r\nexport type ErrorCallback = (error: Error) => void;\r\n\r\n/**\r\n * Observer para snapshots\r\n */\r\nexport interface SnapshotObserver<T> {\r\n next?: (snapshot: T) => void;\r\n error?: (error: Error) => void;\r\n complete?: () => void;\r\n}\r\n\r\n// ============================================================================\r\n// onSnapshot Implementation\r\n// ============================================================================\r\n\r\n/**\r\n * Escucha cambios en un documento en tiempo real\r\n * \r\n * @param reference Referencia al documento\r\n * @param onNext Callback llamado cuando hay cambios\r\n * @param onError Callback para errores (opcional)\r\n * @returns Función para cancelar la suscripción\r\n * \r\n * @example\r\n * ```typescript\r\n * const unsubscribe = onSnapshot(\r\n * doc(db, 'users', 'user123'),\r\n * (snapshot) => {\r\n * if (snapshot.exists()) {\r\n * console.log('Data:', snapshot.data());\r\n * }\r\n * },\r\n * (error) => {\r\n * console.error('Error:', error);\r\n * }\r\n * );\r\n * \r\n * // Cancelar suscripción\r\n * unsubscribe();\r\n * ```\r\n */\r\nexport function onSnapshot<T = DocumentData>(\r\n reference: DocumentReference<T>,\r\n options: SnapshotListenOptions,\r\n onNext: DocumentSnapshotCallback<T>,\r\n onError?: ErrorCallback\r\n): Unsubscribe;\r\n\r\n/**\r\n * Escucha cambios en un documento con opciones y observer\r\n */\r\nexport function onSnapshot<T = DocumentData>(\r\n reference: DocumentReference<T>,\r\n options: SnapshotListenOptions,\r\n observer: SnapshotObserver<DocumentSnapshot<T>>\r\n): Unsubscribe;\r\n\r\n/**\r\n * Escucha cambios en un documento en tiempo real\r\n */\r\nexport function onSnapshot<T = DocumentData>(\r\n reference: DocumentReference<T>,\r\n onNext: DocumentSnapshotCallback<T>,\r\n onError?: ErrorCallback\r\n): Unsubscribe;\r\n\r\n/**\r\n * Escucha cambios en un documento con observer\r\n */\r\nexport function onSnapshot<T = DocumentData>(\r\n reference: DocumentReference<T>,\r\n observer: SnapshotObserver<DocumentSnapshot<T>>\r\n): Unsubscribe;\r\n\r\n/**\r\n * Escucha cambios en una query en tiempo real\r\n * \r\n * @param query Query a escuchar\r\n * @param onNext Callback llamado cuando hay cambios\r\n * @param onError Callback para errores (opcional)\r\n * @returns Función para cancelar la suscripción\r\n * \r\n * @example\r\n * ```typescript\r\n * const q = query(collection(db, 'messages'), orderBy('timestamp', 'desc'), limit(50));\r\n * \r\n * const unsubscribe = onSnapshot(q, (snapshot) => {\r\n * snapshot.docChanges().forEach(change => {\r\n * if (change.type === 'added') {\r\n * console.log('Nuevo mensaje:', change.doc.data());\r\n * }\r\n * });\r\n * });\r\n * ```\r\n */\r\nexport function onSnapshot<T = DocumentData>(\r\n query: Query<T> | CollectionReference<T>,\r\n options: SnapshotListenOptions,\r\n onNext: QuerySnapshotCallback<T>,\r\n onError?: ErrorCallback\r\n): Unsubscribe;\r\n\r\n/**\r\n * Escucha cambios en una query con opciones y observer\r\n */\r\nexport function onSnapshot<T = DocumentData>(\r\n query: Query<T> | CollectionReference<T>,\r\n options: SnapshotListenOptions,\r\n observer: SnapshotObserver<QuerySnapshot<T>>\r\n): Unsubscribe;\r\n\r\n/**\r\n * Escucha cambios en una query en tiempo real\r\n */\r\nexport function onSnapshot<T = DocumentData>(\r\n query: Query<T> | CollectionReference<T>,\r\n onNext: QuerySnapshotCallback<T>,\r\n onError?: ErrorCallback\r\n): Unsubscribe;\r\n\r\n/**\r\n * Escucha cambios en una query con observer\r\n */\r\nexport function onSnapshot<T = DocumentData>(\r\n query: Query<T> | CollectionReference<T>,\r\n observer: SnapshotObserver<QuerySnapshot<T>>\r\n): Unsubscribe;\r\n\r\n/**\r\n * Implementación de onSnapshot\r\n */\r\nexport function onSnapshot<T = DocumentData>(\r\n reference: DocumentReference<T> | Query<T> | CollectionReference<T>,\r\n onNextOrObserverOrOptions:\r\n | DocumentSnapshotCallback<T>\r\n | QuerySnapshotCallback<T>\r\n | SnapshotObserver<DocumentSnapshot<T>>\r\n | SnapshotObserver<QuerySnapshot<T>>\r\n | SnapshotListenOptions,\r\n onErrorOrObserver?:\r\n | ErrorCallback\r\n | DocumentSnapshotCallback<T>\r\n | QuerySnapshotCallback<T>\r\n | SnapshotObserver<DocumentSnapshot<T>>\r\n | SnapshotObserver<QuerySnapshot<T>>,\r\n onError?: ErrorCallback\r\n): Unsubscribe {\r\n // Parsear argumentos\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n let nextCallback: ((snapshot: any) => void) | undefined;\r\n let errorCallback: ErrorCallback | undefined = onError;\r\n let options: SnapshotListenOptions | undefined;\r\n let onNextOrObserver = onNextOrObserverOrOptions as\r\n | DocumentSnapshotCallback<T>\r\n | QuerySnapshotCallback<T>\r\n | SnapshotObserver<DocumentSnapshot<T>>\r\n | SnapshotObserver<QuerySnapshot<T>>;\r\n\r\n const maybeOptions = onNextOrObserverOrOptions as SnapshotListenOptions;\r\n if (\r\n maybeOptions\r\n && typeof maybeOptions === 'object'\r\n && ('includeMetadataChanges' in maybeOptions || 'source' in maybeOptions)\r\n ) {\r\n options = maybeOptions;\r\n onNextOrObserver = onErrorOrObserver as\r\n | DocumentSnapshotCallback<T>\r\n | QuerySnapshotCallback<T>\r\n | SnapshotObserver<DocumentSnapshot<T>>\r\n | SnapshotObserver<QuerySnapshot<T>>;\r\n errorCallback = onError;\r\n } else {\r\n errorCallback = onErrorOrObserver as ErrorCallback | undefined;\r\n }\r\n\r\n if (typeof onNextOrObserver === 'function') {\r\n // onSnapshot(ref, onNext, onError?)\r\n nextCallback = onNextOrObserver;\r\n } else {\r\n // onSnapshot(ref, observer)\r\n nextCallback = onNextOrObserver?.next;\r\n errorCallback = onNextOrObserver?.error ?? errorCallback;\r\n }\r\n\r\n // Determinar tipo de referencia\r\n const isDocument = reference.type === 'document';\r\n\r\n if (isDocument) {\r\n return subscribeToDocument(\r\n reference as DocumentReference<T>,\r\n nextCallback as DocumentSnapshotCallback<T>,\r\n errorCallback,\r\n options\r\n );\r\n } else {\r\n return subscribeToQuery(\r\n reference as Query<T>,\r\n nextCallback as QuerySnapshotCallback<T>,\r\n errorCallback,\r\n options\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Suscribe a cambios en un documento\r\n */\r\nfunction subscribeToDocument<T = DocumentData>(\r\n reference: DocumentReference<T>,\r\n onNext: DocumentSnapshotCallback<T>,\r\n onError?: ErrorCallback,\r\n options?: SnapshotListenOptions\r\n): Unsubscribe {\r\n let unsubscribed = false;\r\n let wsUnsubscribe: Unsubscribe | null = null;\r\n let localUnsubscribe: Unsubscribe | null = null;\r\n let lastSnapshot: DocumentSnapshot<T> | null = null;\r\n\r\n const pm = getPersistenceManager(reference.firestore);\r\n\r\n // Si hay persistencia y datos en cache, emitir snapshot inmediato desde cache\r\n if (pm) {\r\n pm.getCachedDocument(reference.path).then(async (cached) => {\r\n if (cached && !unsubscribed) {\r\n const cachedSnapshot = new DocumentSnapshotImpl<T>(\r\n reference,\r\n (cached.exists ? cached.data : undefined) as T | undefined,\r\n cached.exists,\r\n { fromCache: true, hasPendingWrites: await pm.hasPendingWrites(reference.path) }\r\n );\r\n lastSnapshot = cachedSnapshot;\r\n if (!unsubscribed && onNext) {\r\n onNext(cachedSnapshot);\r\n }\r\n }\r\n });\r\n }\r\n\r\n // Cache-only listener (Firestore moderno)\r\n if (options?.source === 'cache') {\r\n if (!lastSnapshot) {\r\n const hasPendingPromise = pm ? Promise.resolve(pm.hasPendingWrites(reference.path)) : Promise.resolve(false);\r\n hasPendingPromise.then((hasPending) => {\r\n if (unsubscribed || lastSnapshot) return;\r\n const emptyCachedSnapshot = new DocumentSnapshotImpl<T>(\r\n reference,\r\n undefined,\r\n false,\r\n {\r\n fromCache: true,\r\n hasPendingWrites: hasPending,\r\n }\r\n );\r\n lastSnapshot = emptyCachedSnapshot;\r\n if (!unsubscribed && onNext) {\r\n onNext(emptyCachedSnapshot);\r\n }\r\n });\r\n }\r\n\r\n if (options?.includeMetadataChanges) {\r\n localUnsubscribe = registerLocalWriteListener(reference.path, () => {\r\n if (unsubscribed || !onNext) return;\r\n const data = lastSnapshot?.data();\r\n const pendingSnapshot = new DocumentSnapshotImpl<T>(\r\n reference,\r\n data,\r\n lastSnapshot?.exists() ?? false,\r\n { fromCache: true, hasPendingWrites: true }\r\n );\r\n onNext(pendingSnapshot);\r\n });\r\n }\r\n\r\n return () => {\r\n unsubscribed = true;\r\n if (localUnsubscribe) {\r\n localUnsubscribe();\r\n }\r\n };\r\n }\r\n\r\n // Obtener estado inicial del servidor\r\n getDoc(reference)\r\n .then(async (snapshot) => {\r\n if (!unsubscribed && onNext) {\r\n lastSnapshot = snapshot;\r\n // Cachear en persistencia\r\n if (pm) {\r\n await pm.cacheDocument(reference.path, snapshot.data() as DocumentData ?? null, snapshot.exists(), (snapshot as any)._updateTime);\r\n }\r\n onNext(snapshot);\r\n }\r\n })\r\n .catch(error => {\r\n if (!unsubscribed && onError) {\r\n onError(error);\r\n }\r\n });\r\n\r\n if (options?.includeMetadataChanges) {\r\n localUnsubscribe = registerLocalWriteListener(reference.path, () => {\r\n if (unsubscribed || !onNext) return;\r\n const data = lastSnapshot?.data();\r\n const pendingSnapshot = new DocumentSnapshotImpl<T>(\r\n reference,\r\n data,\r\n lastSnapshot?.exists() ?? false,\r\n { fromCache: true, hasPendingWrites: true }\r\n );\r\n onNext(pendingSnapshot);\r\n });\r\n }\r\n\r\n // Configurar WebSocket para cambios\r\n try {\r\n const wsClient = getWebSocketClient(reference.firestore);\r\n\r\n // Iniciar suscripción async y guardar el unsubscribe\r\n wsClient.subscribeToDocument(\r\n reference.path,\r\n () => {\r\n if (!unsubscribed && onNext) {\r\n // Crear snapshot desde datos de WebSocket (refrescando del servidor)\r\n getDoc(reference)\r\n .then(async (snapshot) => {\r\n if (!unsubscribed) {\r\n lastSnapshot = snapshot;\r\n // Cachear en persistencia\r\n if (pm) {\r\n await pm.cacheDocument(reference.path, snapshot.data() as DocumentData ?? null, snapshot.exists(), (snapshot as any)._updateTime);\r\n }\r\n onNext(snapshot);\r\n }\r\n })\r\n .catch(error => {\r\n if (!unsubscribed && onError) {\r\n onError(error);\r\n }\r\n });\r\n }\r\n },\r\n (error: Error) => {\r\n if (!unsubscribed && onError) {\r\n onError(error);\r\n }\r\n },\r\n options?.includeMetadataChanges\r\n ).then(unsub => {\r\n wsUnsubscribe = unsub;\r\n // Si ya se canceló antes de que se resolviera la promesa, cancelar ahora\r\n if (unsubscribed) {\r\n unsub();\r\n }\r\n }).catch((error: Error) => {\r\n if (!unsubscribed && onError) {\r\n onError(error);\r\n }\r\n });\r\n } catch (error) {\r\n if (!unsubscribed && onError) {\r\n onError(error as Error);\r\n }\r\n }\r\n\r\n // Retornar función de unsubscribe\r\n return () => {\r\n unsubscribed = true;\r\n if (wsUnsubscribe) {\r\n wsUnsubscribe();\r\n }\r\n if (localUnsubscribe) {\r\n localUnsubscribe();\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Suscribe a cambios en una query\r\n * Mantiene el estado previo para calcular docChanges() correctamente\r\n */\r\nfunction subscribeToQuery<T = DocumentData>(\r\n queryRef: Query<T>,\r\n onNext: QuerySnapshotCallback<T>,\r\n onError?: ErrorCallback,\r\n options?: SnapshotListenOptions\r\n): Unsubscribe {\r\n let unsubscribed = false;\r\n let previousSnapshot: QuerySnapshot<T> | null = null;\r\n let wsUnsubscribe: Unsubscribe | null = null;\r\n\r\n // Cache-only listener (Firestore moderno)\r\n if (options?.source === 'cache') {\r\n getDocsFromCache(queryRef)\r\n .then(snapshot => {\r\n if (!unsubscribed && onNext) {\r\n onNext(snapshot as QuerySnapshot<T>);\r\n }\r\n })\r\n .catch(error => {\r\n if (!unsubscribed && onError) {\r\n onError(error);\r\n }\r\n });\r\n\r\n return () => {\r\n unsubscribed = true;\r\n };\r\n }\r\n\r\n // Función para refrescar la query y calcular cambios\r\n const refresh = () => {\r\n getDocsWithPreviousState(queryRef, previousSnapshot)\r\n .then(snapshot => {\r\n if (!unsubscribed && onNext) {\r\n previousSnapshot = snapshot;\r\n onNext(snapshot);\r\n }\r\n })\r\n .catch(error => {\r\n if (!unsubscribed && onError) {\r\n onError(error);\r\n }\r\n });\r\n };\r\n\r\n // Obtener estado inicial\r\n refresh();\r\n\r\n // Configurar WebSocket para cambios\r\n try {\r\n const wsClient = getWebSocketClient(queryRef.firestore);\r\n const constraints = 'constraints' in queryRef\r\n ? (queryRef as { constraints: unknown[] }).constraints\r\n : [];\r\n\r\n // Detectar si es collectionGroup para notificar al backend\r\n const isCollectionGroup = (queryRef as { type?: string }).type === 'collectionGroup';\r\n\r\n wsClient.subscribeToQuery(\r\n constraints,\r\n () => {\r\n if (!unsubscribed) {\r\n refresh();\r\n }\r\n },\r\n (error: Error) => {\r\n if (!unsubscribed && onError) {\r\n onError(error);\r\n }\r\n },\r\n queryRef.path,\r\n options?.includeMetadataChanges,\r\n isCollectionGroup // allDescendants para collectionGroup\r\n ).then(unsub => {\r\n wsUnsubscribe = unsub;\r\n // Si ya se canceló antes de que se resolviera la promesa, cancelar ahora\r\n if (unsubscribed) {\r\n unsub();\r\n }\r\n }).catch((error: Error) => {\r\n if (!unsubscribed && onError) {\r\n onError(error);\r\n }\r\n });\r\n } catch (error) {\r\n if (!unsubscribed && onError) {\r\n onError(error as Error);\r\n }\r\n }\r\n\r\n // Retornar función de unsubscribe\r\n return () => {\r\n unsubscribed = true;\r\n if (wsUnsubscribe) {\r\n wsUnsubscribe();\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Obtiene documentos y calcula docChanges comparando con el estado previo\r\n */\r\nasync function getDocsWithPreviousState<T = DocumentData>(\r\n queryRef: Query<T>,\r\n previousSnapshot: QuerySnapshot<T> | null\r\n): Promise<QuerySnapshot<T>> {\r\n const { getHttpClient } = await import('../transport/http');\r\n const { createQuerySnapshot, toFirestoreValue } = await import('./snapshot');\r\n\r\n const client = getHttpClient(queryRef.firestore);\r\n const constraints = 'constraints' in queryRef\r\n ? (queryRef as { constraints: unknown[] }).constraints\r\n : [];\r\n\r\n // Detectar si es collectionGroup\r\n const isCollectionGroupQuery = (queryRef as { type?: string }).type === 'collectionGroup';\r\n\r\n // Construir StructuredQuery\r\n const structuredQuery = buildStructuredQueryForRealtime(queryRef.path, constraints, isCollectionGroupQuery, toFirestoreValue);\r\n\r\n // Ejecutar query\r\n const response = await client.post<{ document?: import('./types').FirestoreDocument }[]>(\r\n '/documents:runQuery',\r\n { structuredQuery }\r\n );\r\n\r\n // Filtrar respuestas vacías\r\n const documents = response\r\n .filter(r => r.document)\r\n .map(r => r.document!);\r\n\r\n // Pasar los docs previos para calcular cambios correctamente\r\n const previousDocs = previousSnapshot ? previousSnapshot.docs : [];\r\n\r\n return createQuerySnapshot(queryRef, documents, previousDocs);\r\n}\r\n\r\n/**\r\n * Construye un StructuredQuery (versión local para realtime)\r\n */\r\nfunction buildStructuredQueryForRealtime(\r\n collectionPath: string,\r\n constraints: unknown[],\r\n isCollectionGroup: boolean,\r\n toFirestoreValue: (v: unknown) => import('./types').FirestoreValue\r\n): import('./types').FirestoreStructuredQuery {\r\n const query: import('./types').FirestoreStructuredQuery = {\r\n from: [{\r\n collectionId: isCollectionGroup\r\n ? (collectionPath.split('/').pop() || collectionPath)\r\n : collectionPath,\r\n allDescendants: isCollectionGroup,\r\n }],\r\n };\r\n\r\n const allFilters: object[] = [];\r\n const orderBys: Array<{ field: string; direction: string }> = [];\r\n let startCursor: { values: import('./types').FirestoreValue[]; before?: boolean } | undefined;\r\n let endCursor: { values: import('./types').FirestoreValue[]; before?: boolean } | undefined;\r\n\r\n // Procesar constraints\r\n for (const raw of constraints as Array<Record<string, unknown>>) {\r\n if (raw.type === 'where') {\r\n const w = raw as unknown as { field: string; op: string; value: unknown };\r\n const filter = buildWhereFilterRealtime(w, toFirestoreValue);\r\n if (filter) {\r\n allFilters.push(filter);\r\n }\r\n } else if (raw.type === 'and' || raw.type === 'or') {\r\n allFilters.push(buildCompositeFilterRealtime(raw, toFirestoreValue));\r\n } else if (raw.type === 'orderBy') {\r\n const o = raw as unknown as { field: string; direction: string };\r\n if (!query.orderBy) query.orderBy = [];\r\n query.orderBy.push({\r\n field: { fieldPath: o.field },\r\n direction: o.direction === 'desc' ? 'DESCENDING' : 'ASCENDING',\r\n });\r\n orderBys.push(o);\r\n } else if (raw.type === 'limit') {\r\n query.limit = (raw as unknown as { limit: number }).limit;\r\n } else if (raw.type === 'limitToLast') {\r\n query.limitToLast = (raw as unknown as { limit: number }).limit;\r\n } else if (raw.type === 'startAt' || raw.type === 'startAfter') {\r\n const c = raw as unknown as { values: unknown[]; inclusive: boolean };\r\n const cursorValues = extractCursorValuesRealtime(c.values, orderBys);\r\n startCursor = {\r\n values: cursorValues.map(v => toFirestoreValue(v)),\r\n before: c.inclusive,\r\n };\r\n } else if (raw.type === 'endAt' || raw.type === 'endBefore') {\r\n const c = raw as unknown as { values: unknown[]; inclusive: boolean };\r\n const cursorValues = extractCursorValuesRealtime(c.values, orderBys);\r\n endCursor = {\r\n values: cursorValues.map(v => toFirestoreValue(v)),\r\n before: !c.inclusive,\r\n };\r\n }\r\n }\r\n\r\n if (allFilters.length === 1) {\r\n query.where = allFilters[0] as import('./types').FirestoreStructuredQuery['where'];\r\n } else if (allFilters.length > 1) {\r\n query.where = {\r\n compositeFilter: {\r\n op: 'AND',\r\n filters: allFilters,\r\n },\r\n };\r\n }\r\n\r\n if (startCursor) {\r\n query.startAt = startCursor;\r\n }\r\n if (endCursor) {\r\n query.endAt = endCursor;\r\n }\r\n\r\n return query;\r\n}\r\n\r\nfunction extractCursorValuesRealtime(\r\n values: unknown[],\r\n orderBys: Array<{ field: string; direction: string }>\r\n): unknown[] {\r\n if (!values || values.length === 0) return values;\r\n\r\n const first = values[0] as { data?: () => Record<string, unknown> | undefined; id?: string } | undefined;\r\n if (first && typeof first.data === 'function') {\r\n const data = first.data();\r\n if (!data) return values;\r\n\r\n if (orderBys.length > 0) {\r\n return orderBys.map(ob => {\r\n if (ob.field === '__name__') {\r\n return first.id ?? '';\r\n }\r\n return getNestedValue(data, ob.field);\r\n });\r\n }\r\n }\r\n\r\n return values;\r\n}\r\n\r\n/**\r\n * Obtiene un valor anidado de un objeto usando un path con puntos\r\n */\r\nfunction getNestedValue(obj: Record<string, unknown>, path: string): unknown {\r\n const parts = path.split('.');\r\n let current: unknown = obj;\r\n\r\n for (const part of parts) {\r\n if (current === null || current === undefined) return undefined;\r\n if (typeof current !== 'object') return undefined;\r\n current = (current as Record<string, unknown>)[part];\r\n }\r\n\r\n return current;\r\n}\r\n\r\nfunction buildWhereFilterRealtime(\r\n where: { field: string; op: string; value: unknown },\r\n toFirestoreValue: (v: unknown) => import('./types').FirestoreValue\r\n): import('./types').FirestoreStructuredQuery['where'] {\r\n const opMap: Record<string, string> = {\r\n '<': 'LESS_THAN',\r\n '<=': 'LESS_THAN_OR_EQUAL',\r\n '==': 'EQUAL',\r\n '!=': 'NOT_EQUAL',\r\n '>': 'GREATER_THAN',\r\n '>=': 'GREATER_THAN_OR_EQUAL',\r\n 'array-contains': 'ARRAY_CONTAINS',\r\n 'array-contains-any': 'ARRAY_CONTAINS_ANY',\r\n 'in': 'IN',\r\n 'not-in': 'NOT_IN',\r\n };\r\n\r\n return {\r\n fieldFilter: {\r\n field: { fieldPath: where.field },\r\n op: opMap[where.op] as 'EQUAL',\r\n value: toFirestoreValue(where.value),\r\n },\r\n };\r\n}\r\n\r\nfunction buildCompositeFilterRealtime(\r\n constraint: Record<string, unknown>,\r\n toFirestoreValue: (v: unknown) => import('./types').FirestoreValue\r\n): object {\r\n const op = constraint.type === 'and' ? 'AND' : 'OR';\r\n const constraints = (constraint.constraints as unknown[]) || [];\r\n\r\n const filters = constraints.map((c) => {\r\n const item = c as Record<string, unknown>;\r\n if (item.type === 'and' || item.type === 'or') {\r\n return buildCompositeFilterRealtime(item, toFirestoreValue);\r\n }\r\n return buildWhereFilterRealtime(item as { field: string; op: string; value: unknown }, toFirestoreValue);\r\n });\r\n\r\n return {\r\n compositeFilter: {\r\n op,\r\n filters,\r\n },\r\n };\r\n}\r\n","/**\r\n * Firestore SDK - WriteBatch & Transaction\r\n * Operaciones atómicas en batch y transacciones\r\n */\r\n\r\nimport type {\r\n DocumentData,\r\n DocumentReference,\r\n DocumentSnapshot,\r\n SetOptions,\r\n FirestoreInstance,\r\n FirestoreError,\r\n FirestoreErrorCode,\r\n} from './types';\r\nimport { getHttpClient } from '../transport/http';\r\nimport { toFirestoreFields, DocumentSnapshotImpl } from './snapshot';\r\nimport { getDoc } from './crud';\r\n\r\n// ============================================================================\r\n// WriteBatch\r\n// ============================================================================\r\n\r\n/**\r\n * Batch de escrituras atómicas\r\n */\r\nexport interface WriteBatch {\r\n /**\r\n * Establece un documento\r\n */\r\n set<T = DocumentData>(\r\n reference: DocumentReference<T>,\r\n data: T,\r\n options?: SetOptions\r\n ): WriteBatch;\r\n\r\n /**\r\n * Actualiza un documento\r\n */\r\n update<T = DocumentData>(\r\n reference: DocumentReference<T>,\r\n data: Partial<T>\r\n ): WriteBatch;\r\n\r\n /**\r\n * Elimina un documento\r\n */\r\n delete(reference: DocumentReference): WriteBatch;\r\n\r\n /**\r\n * Ejecuta el batch\r\n */\r\n commit(): Promise<void>;\r\n}\r\n\r\n/**\r\n * Tipo de operación en batch\r\n */\r\ntype BatchOperation =\r\n | { type: 'set'; reference: DocumentReference; data: DocumentData; options?: SetOptions }\r\n | { type: 'update'; reference: DocumentReference; data: DocumentData }\r\n | { type: 'delete'; reference: DocumentReference };\r\n\r\n/**\r\n * Implementación de WriteBatch\r\n */\r\nclass WriteBatchImpl implements WriteBatch {\r\n private operations: BatchOperation[] = [];\r\n private committed = false;\r\n\r\n constructor(private firestore: FirestoreInstance) { }\r\n\r\n set<T = DocumentData>(\r\n reference: DocumentReference<T>,\r\n data: T,\r\n options?: SetOptions\r\n ): WriteBatch {\r\n this.verifyNotCommitted();\r\n this.operations.push({\r\n type: 'set',\r\n reference,\r\n data: data as DocumentData,\r\n options\r\n });\r\n return this;\r\n }\r\n\r\n update<T = DocumentData>(\r\n reference: DocumentReference<T>,\r\n data: Partial<T>\r\n ): WriteBatch {\r\n this.verifyNotCommitted();\r\n this.operations.push({\r\n type: 'update',\r\n reference,\r\n data: data as DocumentData\r\n });\r\n return this;\r\n }\r\n\r\n delete(reference: DocumentReference): WriteBatch {\r\n this.verifyNotCommitted();\r\n this.operations.push({ type: 'delete', reference });\r\n return this;\r\n }\r\n\r\n async commit(): Promise<void> {\r\n this.verifyNotCommitted();\r\n this.committed = true;\r\n\r\n if (this.operations.length === 0) {\r\n return;\r\n }\r\n\r\n if (this.operations.length > 500) {\r\n throw createFirestoreError('invalid-argument', 'Batch write exceeds 500 operations');\r\n }\r\n\r\n const client = getHttpClient(this.firestore);\r\n\r\n // Convertir operaciones a formato Firestore\r\n const writes = this.operations.map(op => {\r\n switch (op.type) {\r\n case 'set': {\r\n const fields = toFirestoreFields(op.data);\r\n if (op.options?.merge || op.options?.mergeFields) {\r\n const fieldPaths = op.options.mergeFields || Object.keys(op.data);\r\n return {\r\n update: {\r\n name: `projects/${this.firestore.app.options.projectId}/databases/(default)/documents/${op.reference.path}`,\r\n fields,\r\n },\r\n updateMask: { fieldPaths },\r\n };\r\n }\r\n return {\r\n update: {\r\n name: `projects/${this.firestore.app.options.projectId}/databases/(default)/documents/${op.reference.path}`,\r\n fields,\r\n },\r\n };\r\n }\r\n\r\n case 'update': {\r\n const fields = toFirestoreFields(op.data);\r\n const fieldPaths = Object.keys(op.data);\r\n return {\r\n update: {\r\n name: `projects/${this.firestore.app.options.projectId}/databases/(default)/documents/${op.reference.path}`,\r\n fields,\r\n },\r\n updateMask: { fieldPaths },\r\n };\r\n }\r\n\r\n case 'delete':\r\n return {\r\n delete: `projects/${this.firestore.app.options.projectId}/databases/(default)/documents/${op.reference.path}`,\r\n };\r\n }\r\n });\r\n\r\n // Ejecutar batch commit\r\n await client.post(':batchWrite', { writes });\r\n }\r\n\r\n private verifyNotCommitted(): void {\r\n if (this.committed) {\r\n throw new Error('WriteBatch ya ha sido committed');\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Crea un nuevo WriteBatch\r\n * \r\n * @param firestore Instancia de Firestore\r\n * @returns WriteBatch\r\n * \r\n * @example\r\n * ```typescript\r\n * const batch = writeBatch(db);\r\n * \r\n * batch.set(doc(db, 'cities', 'NYC'), { name: 'New York' });\r\n * batch.update(doc(db, 'cities', 'LA'), { population: 4000000 });\r\n * batch.delete(doc(db, 'cities', 'OLD'));\r\n * \r\n * await batch.commit();\r\n * ```\r\n */\r\nexport function writeBatch(firestore: FirestoreInstance): WriteBatch {\r\n return new WriteBatchImpl(firestore);\r\n}\r\n\r\n// ============================================================================\r\n// Transaction\r\n// ============================================================================\r\n\r\n/**\r\n * Transacción de Firestore\r\n */\r\nexport interface Transaction {\r\n /**\r\n * Lee un documento dentro de la transacción\r\n */\r\n get<T = DocumentData>(reference: DocumentReference<T>): Promise<DocumentSnapshot<T>>;\r\n\r\n /**\r\n * Establece un documento\r\n */\r\n set<T = DocumentData>(\r\n reference: DocumentReference<T>,\r\n data: T,\r\n options?: SetOptions\r\n ): Transaction;\r\n\r\n /**\r\n * Actualiza un documento\r\n */\r\n update<T = DocumentData>(\r\n reference: DocumentReference<T>,\r\n data: Partial<T>\r\n ): Transaction;\r\n\r\n /**\r\n * Elimina un documento\r\n */\r\n delete(reference: DocumentReference): Transaction;\r\n}\r\n\r\n/**\r\n * Implementación de Transaction\r\n */\r\nclass TransactionImpl implements Transaction {\r\n private reads: Map<string, DocumentSnapshot> = new Map();\r\n private operations: BatchOperation[] = [];\r\n private transactionId?: string;\r\n private readonly id = createTransactionId();\r\n private readonly readVersions: Map<string, number> = new Map();\r\n private readonly preconditions: Map<string, { exists: boolean, updateTime?: string }> = new Map();\r\n\r\n constructor(\r\n private firestore: FirestoreInstance,\r\n private client: ReturnType<typeof getHttpClient>\r\n ) { }\r\n\r\n /**\r\n * Inicia la transacción\r\n */\r\n async begin(): Promise<void> {\r\n const response = await this.client.post<{ transaction: string }>(\r\n ':beginTransaction',\r\n {}\r\n );\r\n this.transactionId = response.transaction;\r\n }\r\n\r\n async get<T = DocumentData>(reference: DocumentReference<T>): Promise<DocumentSnapshot<T>> {\r\n // Verificar cache de lecturas\r\n const cached = this.reads.get(reference.path);\r\n if (cached) {\r\n return cached as DocumentSnapshot<T>;\r\n }\r\n\r\n // Leer documento con transactionId\r\n const snapshot = await getDoc(reference);\r\n\r\n // Capturar versión para OCC\r\n this.readVersions.set(reference.path, getDocumentVersion(reference.path)); // Local consistency\r\n\r\n // Capturar precondición para el backend\r\n const snapImpl = snapshot as unknown as DocumentSnapshotImpl<T>;\r\n this.preconditions.set(reference.path, {\r\n exists: snapshot.exists(),\r\n updateTime: snapImpl._updateTime\r\n });\r\n\r\n this.reads.set(reference.path, snapshot as DocumentSnapshot);\r\n\r\n return snapshot;\r\n }\r\n\r\n set<T = DocumentData>(\r\n reference: DocumentReference<T>,\r\n data: T,\r\n options?: SetOptions\r\n ): Transaction {\r\n this.operations.push({\r\n type: 'set',\r\n reference,\r\n data: data as DocumentData,\r\n options\r\n });\r\n return this;\r\n }\r\n\r\n update<T = DocumentData>(\r\n reference: DocumentReference<T>,\r\n data: Partial<T>\r\n ): Transaction {\r\n this.operations.push({\r\n type: 'update',\r\n reference,\r\n data: data as DocumentData\r\n });\r\n return this;\r\n }\r\n\r\n delete(reference: DocumentReference): Transaction {\r\n this.operations.push({ type: 'delete', reference });\r\n return this;\r\n }\r\n\r\n /**\r\n * Ejecuta la transacción\r\n */\r\n async commit(): Promise<void> {\r\n if (this.operations.length > 500) {\r\n throw createFirestoreError('invalid-argument', 'Transaction exceeds 500 operations');\r\n }\r\n\r\n if (this.operations.length > 0) {\r\n for (const [path, version] of this.readVersions.entries()) {\r\n if (getDocumentVersion(path) !== version) {\r\n throw createFirestoreError('aborted', 'Transaction read conflict');\r\n }\r\n }\r\n }\r\n\r\n const paths = this.operations.map(op => op.reference.path);\r\n if (hasWriteConflict(this.id, paths)) {\r\n throw createFirestoreError('aborted', 'Transaction conflicted with another in-flight write');\r\n }\r\n\r\n acquireWriteLocks(this.id, paths);\r\n if (this.operations.length === 0) {\r\n // Rollback si no hay operaciones\r\n if (this.transactionId) {\r\n await this.client.post(':rollback', {\r\n transaction: this.transactionId\r\n });\r\n }\r\n releaseWriteLocks(this.id);\r\n return;\r\n }\r\n\r\n // Convertir operaciones a formato Firestore\r\n const writes = this.operations.map(op => {\r\n const baseName = `projects/${this.firestore.app.options.projectId}/databases/(default)/documents/${op.reference.path}`;\r\n\r\n switch (op.type) {\r\n case 'set': {\r\n const fields = toFirestoreFields(op.data);\r\n if (op.options?.merge || op.options?.mergeFields) {\r\n const fieldPaths = op.options.mergeFields || Object.keys(op.data);\r\n return {\r\n update: { name: baseName, fields },\r\n updateMask: { fieldPaths },\r\n currentDocument: this.preconditions.get(op.reference.path),\r\n };\r\n }\r\n return {\r\n update: { name: baseName, fields },\r\n currentDocument: this.preconditions.get(op.reference.path),\r\n };\r\n }\r\n\r\n case 'update': {\r\n const fields = toFirestoreFields(op.data);\r\n const fieldPaths = Object.keys(op.data);\r\n return {\r\n update: { name: baseName, fields },\r\n updateMask: { fieldPaths },\r\n currentDocument: this.preconditions.get(op.reference.path),\r\n };\r\n }\r\n\r\n case 'delete':\r\n return {\r\n delete: baseName,\r\n currentDocument: this.preconditions.get(op.reference.path),\r\n };\r\n }\r\n });\r\n\r\n // Commit transacción\r\n try {\r\n await this.client.post(':commit', {\r\n transaction: this.transactionId,\r\n writes,\r\n });\r\n bumpDocumentVersions(paths);\r\n } finally {\r\n releaseWriteLocks(this.id);\r\n }\r\n }\r\n\r\n /**\r\n * Rollback de la transacción\r\n */\r\n async rollback(): Promise<void> {\r\n if (this.transactionId) {\r\n await this.client.post(':rollback', {\r\n transaction: this.transactionId\r\n });\r\n }\r\n releaseWriteLocks(this.id);\r\n }\r\n}\r\n\r\n/**\r\n * Opciones para runTransaction\r\n */\r\nexport interface TransactionOptions {\r\n /** Número máximo de reintentos */\r\n maxAttempts?: number;\r\n}\r\n\r\n/**\r\n * Ejecuta una función dentro de una transacción\r\n * \r\n * @param firestore Instancia de Firestore\r\n * @param updateFunction Función a ejecutar\r\n * @param options Opciones de transacción\r\n * @returns Resultado de la función\r\n * \r\n * @example\r\n * ```typescript\r\n * const newBalance = await runTransaction(db, async (transaction) => {\r\n * const accountRef = doc(db, 'accounts', 'account1');\r\n * const accountDoc = await transaction.get(accountRef);\r\n * \r\n * if (!accountDoc.exists()) {\r\n * throw new Error('Account does not exist');\r\n * }\r\n * \r\n * const currentBalance = accountDoc.data()!.balance;\r\n * const newBalance = currentBalance - 100;\r\n * \r\n * if (newBalance < 0) {\r\n * throw new Error('Insufficient funds');\r\n * }\r\n * \r\n * transaction.update(accountRef, { balance: newBalance });\r\n * return newBalance;\r\n * });\r\n * ```\r\n */\r\nexport async function runTransaction<T>(\r\n firestore: FirestoreInstance,\r\n updateFunction: (transaction: Transaction) => Promise<T>,\r\n options?: TransactionOptions\r\n): Promise<T> {\r\n const maxAttempts = options?.maxAttempts ?? 5;\r\n const client = getHttpClient(firestore);\r\n\r\n let lastError: Error | undefined;\r\n\r\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\r\n const transaction = new TransactionImpl(firestore, client);\r\n\r\n try {\r\n // Iniciar transacción\r\n await transaction.begin();\r\n\r\n // Ejecutar función del usuario\r\n const result = await updateFunction(transaction);\r\n\r\n // Commit\r\n await transaction.commit();\r\n\r\n return result;\r\n } catch (error) {\r\n lastError = error as Error;\r\n\r\n // Rollback\r\n try {\r\n await transaction.rollback();\r\n } catch {\r\n // Ignorar error de rollback\r\n }\r\n\r\n // Si es error de contención, reintentar\r\n if ((error as { code?: string }).code === 'aborted') {\r\n // Esperar antes de reintentar (backoff exponencial)\r\n await new Promise(resolve =>\r\n setTimeout(resolve, Math.pow(2, attempt) * 100)\r\n );\r\n continue;\r\n }\r\n\r\n // Otros errores, no reintentar\r\n throw error;\r\n }\r\n }\r\n\r\n throw lastError || new Error('Transaction failed after max attempts');\r\n}\r\n\r\n// =============================================================================\r\n// HELPERS\r\n// =============================================================================\r\n\r\nfunction createFirestoreError(code: FirestoreErrorCode, message: string): FirestoreError {\r\n const error = new Error(message) as FirestoreError;\r\n (error as { code: FirestoreErrorCode }).code = code;\r\n return error;\r\n}\r\n\r\nconst writeLocks = new Map<string, string>();\r\nconst documentVersions = new Map<string, number>();\r\n\r\nfunction createTransactionId(): string {\r\n return `tx_${Math.random().toString(36).slice(2)}_${Date.now()}`;\r\n}\r\n\r\nfunction hasWriteConflict(txId: string, paths: string[]): boolean {\r\n return paths.some(path => {\r\n const owner = writeLocks.get(path);\r\n return owner !== undefined && owner !== txId;\r\n });\r\n}\r\n\r\nfunction getDocumentVersion(path: string): number {\r\n return documentVersions.get(path) ?? 0;\r\n}\r\n\r\nfunction bumpDocumentVersions(paths: string[]): void {\r\n paths.forEach(path => {\r\n const current = getDocumentVersion(path);\r\n documentVersions.set(path, current + 1);\r\n });\r\n}\r\n\r\nfunction acquireWriteLocks(txId: string, paths: string[]): void {\r\n paths.forEach(path => {\r\n writeLocks.set(path, txId);\r\n });\r\n}\r\n\r\nfunction releaseWriteLocks(txId: string): void {\r\n for (const [path, owner] of writeLocks.entries()) {\r\n if (owner === txId) {\r\n writeLocks.delete(path);\r\n }\r\n }\r\n}\r\n","/**\r\n * Firestore SDK - Queries Avanzadas\r\n * and(), or(), collectionGroup()\r\n * Compatible con Firebase Firestore v9\r\n */\r\n\r\nimport type {\r\n DocumentData,\r\n Query,\r\n CollectionReference,\r\n QueryConstraint,\r\n WhereConstraint,\r\n FirestoreInstance,\r\n} from './types';\r\n\r\n// ============================================================================\r\n// Tipos para Filtros Compuestos\r\n// ============================================================================\r\n\r\n/**\r\n * Filtro compuesto AND\r\n */\r\nexport interface AndConstraint {\r\n readonly type: 'and';\r\n readonly constraints: QueryConstraint[];\r\n}\r\n\r\n/**\r\n * Filtro compuesto OR\r\n */\r\nexport interface OrConstraint {\r\n readonly type: 'or';\r\n readonly constraints: QueryConstraint[];\r\n}\r\n\r\n/**\r\n * Tipo unión para filtros compuestos\r\n */\r\nexport type CompositeFilterConstraint = AndConstraint | OrConstraint;\r\n\r\n// ============================================================================\r\n// Funciones de Filtros Compuestos\r\n// ============================================================================\r\n\r\n/**\r\n * Combina múltiples constraints con lógica AND.\r\n * \r\n * @param constraints Restricciones a combinar\r\n * @returns Constraint compuesto AND\r\n * \r\n * @example\r\n * ```typescript\r\n * const q = query(\r\n * collection(db, 'products'),\r\n * and(\r\n * where('category', '==', 'electronics'),\r\n * where('price', '<=', 1000),\r\n * where('inStock', '==', true)\r\n * )\r\n * );\r\n * ```\r\n */\r\nexport function and(...constraints: QueryConstraint[]): AndConstraint {\r\n if (constraints.length === 0) {\r\n throw new Error('and() requires at least one constraint');\r\n }\r\n return {\r\n type: 'and',\r\n constraints,\r\n };\r\n}\r\n\r\n/**\r\n * Combina múltiples constraints con lógica OR.\r\n * \r\n * @param constraints Restricciones a combinar\r\n * @returns Constraint compuesto OR\r\n * \r\n * @example\r\n * ```typescript\r\n * const q = query(\r\n * collection(db, 'products'),\r\n * or(\r\n * where('category', '==', 'electronics'),\r\n * where('category', '==', 'computers')\r\n * )\r\n * );\r\n * ```\r\n */\r\nexport function or(...constraints: QueryConstraint[]): OrConstraint {\r\n if (constraints.length === 0) {\r\n throw new Error('or() requires at least one constraint');\r\n }\r\n return {\r\n type: 'or',\r\n constraints,\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// Collection Group Query\r\n// ============================================================================\r\n\r\n/**\r\n * Referencia a un grupo de colecciones para queries.\r\n */\r\nexport interface CollectionGroupReference<T = DocumentData> extends Query<T> {\r\n readonly type: 'collectionGroup';\r\n readonly collectionId: string;\r\n}\r\n\r\n/**\r\n * Implementación interna de CollectionGroupReference\r\n */\r\nclass CollectionGroupImpl<T = DocumentData> implements CollectionGroupReference<T> {\r\n readonly type = 'collectionGroup' as const;\r\n \r\n constructor(\r\n readonly firestore: FirestoreInstance,\r\n readonly collectionId: string,\r\n readonly path: string,\r\n readonly constraints: QueryConstraint[] = []\r\n ) {}\r\n}\r\n\r\n/**\r\n * Crea una query que abarca todas las colecciones con el mismo ID.\r\n * Útil para buscar en subcolecciones anidadas en cualquier parte.\r\n * \r\n * @param firestore Instancia de Firestore\r\n * @param collectionId ID de la colección a buscar\r\n * @returns Query sobre el grupo de colecciones\r\n * \r\n * @example\r\n * ```typescript\r\n * // Buscar todos los comentarios en cualquier post\r\n * const allComments = query(\r\n * collectionGroup(db, 'comments'),\r\n * where('author', '==', 'john'),\r\n * orderBy('createdAt', 'desc'),\r\n * limit(10)\r\n * );\r\n * \r\n * const snapshot = await getDocs(allComments);\r\n * ```\r\n */\r\nexport function collectionGroup<T = DocumentData>(\r\n firestore: FirestoreInstance,\r\n collectionId: string\r\n): CollectionGroupReference<T> {\r\n // Validar collectionId\r\n if (!collectionId || typeof collectionId !== 'string') {\r\n throw new Error('collectionGroup requires a valid collection ID');\r\n }\r\n \r\n if (collectionId.includes('/')) {\r\n throw new Error('collectionGroup ID cannot contain slashes');\r\n }\r\n \r\n return new CollectionGroupImpl<T>(\r\n firestore,\r\n collectionId,\r\n collectionId, // path es solo el ID para collection groups\r\n []\r\n );\r\n}\r\n\r\n// ============================================================================\r\n// Helper para detectar tipo de constraint\r\n// ============================================================================\r\n\r\n/**\r\n * Verifica si un constraint es un filtro compuesto AND\r\n */\r\nexport function isAndConstraint(constraint: QueryConstraint): constraint is AndConstraint {\r\n return (constraint as AndConstraint).type === 'and';\r\n}\r\n\r\n/**\r\n * Verifica si un constraint es un filtro compuesto OR\r\n */\r\nexport function isOrConstraint(constraint: QueryConstraint): constraint is OrConstraint {\r\n return (constraint as OrConstraint).type === 'or';\r\n}\r\n\r\n/**\r\n * Verifica si un constraint es un filtro compuesto\r\n */\r\nexport function isCompositeFilter(constraint: QueryConstraint): constraint is CompositeFilterConstraint {\r\n return isAndConstraint(constraint) || isOrConstraint(constraint);\r\n}\r\n\r\n/**\r\n * Verifica si una query es un collection group\r\n */\r\nexport function isCollectionGroup<T>(\r\n ref: Query<T> | CollectionReference<T>\r\n): boolean {\r\n return ref.type === 'collectionGroup';\r\n}\r\n\r\n// ============================================================================\r\n// Builder de Query Estructurada con Filtros Compuestos\r\n// ============================================================================\r\n\r\n/**\r\n * Construye el filtro WHERE para filtros compuestos\r\n */\r\nexport function buildCompositeFilter(\r\n constraint: CompositeFilterConstraint,\r\n toFirestoreValue: (value: unknown) => object\r\n): object {\r\n const op = constraint.type === 'and' ? 'AND' : 'OR';\r\n \r\n const filters = constraint.constraints.map(c => {\r\n if (isCompositeFilter(c)) {\r\n // Recursivo para filtros anidados\r\n return buildCompositeFilter(c, toFirestoreValue);\r\n }\r\n \r\n // Es un WhereConstraint simple\r\n const where = c as WhereConstraint;\r\n return buildFieldFilter(where, toFirestoreValue);\r\n });\r\n \r\n return {\r\n compositeFilter: {\r\n op,\r\n filters,\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Construye un filtro de campo simple\r\n */\r\nfunction buildFieldFilter(\r\n where: WhereConstraint,\r\n toFirestoreValue: (value: unknown) => object\r\n): object {\r\n const opMap: Record<string, string> = {\r\n '<': 'LESS_THAN',\r\n '<=': 'LESS_THAN_OR_EQUAL',\r\n '==': 'EQUAL',\r\n '!=': 'NOT_EQUAL',\r\n '>': 'GREATER_THAN',\r\n '>=': 'GREATER_THAN_OR_EQUAL',\r\n 'array-contains': 'ARRAY_CONTAINS',\r\n 'array-contains-any': 'ARRAY_CONTAINS_ANY',\r\n 'in': 'IN',\r\n 'not-in': 'NOT_IN',\r\n };\r\n \r\n return {\r\n fieldFilter: {\r\n field: { fieldPath: where.field },\r\n op: opMap[where.op] || 'EQUAL',\r\n value: toFirestoreValue(where.value),\r\n },\r\n };\r\n}\r\n","/**\r\n * Firestore SDK - Agregaciones\r\n * getCountFromServer, getAggregateFromServer, count, sum, average\r\n * Compatible con Firebase Firestore v9\r\n */\r\n\r\nimport type {\r\n Query,\r\n CollectionReference,\r\n DocumentData,\r\n FirestoreInstance,\r\n} from './types';\r\nimport { getHttpClient } from '../transport/http';\r\n\r\n// ============================================================================\r\n// Tipos de Agregación\r\n// ============================================================================\r\n\r\n/**\r\n * Especificación de campo de agregación\r\n */\r\nexport interface AggregateField<T> {\r\n readonly _aggregateType: 'count' | 'sum' | 'average';\r\n readonly _field?: string;\r\n /** Tipo interno para TypeScript */\r\n readonly _valueType?: T;\r\n}\r\n\r\n/**\r\n * Especificación de agregación (objeto con campos)\r\n */\r\nexport type AggregateSpec = {\r\n [key: string]: AggregateField<unknown>;\r\n};\r\n\r\n/**\r\n * Resultado de agregación basado en el spec\r\n */\r\nexport type AggregateSpecData<T extends AggregateSpec> = {\r\n [K in keyof T]: T[K] extends AggregateField<infer U> ? U : never;\r\n};\r\n\r\n/**\r\n * Snapshot de resultado de agregación\r\n */\r\nexport interface AggregateQuerySnapshot<T extends AggregateSpec> {\r\n /** Obtiene los datos de agregación */\r\n data(): AggregateSpecData<T>;\r\n}\r\n\r\n// ============================================================================\r\n// Funciones de Agregación (Field Builders)\r\n// ============================================================================\r\n\r\n/**\r\n * Crea un campo de agregación para contar documentos.\r\n * \r\n * @example\r\n * ```typescript\r\n * const snapshot = await getAggregateFromServer(\r\n * query(collection(db, 'users')),\r\n * { userCount: count() }\r\n * );\r\n * console.log(snapshot.data().userCount);\r\n * ```\r\n */\r\nexport function count(): AggregateField<number> {\r\n return {\r\n _aggregateType: 'count',\r\n };\r\n}\r\n\r\n/**\r\n * Crea un campo de agregación para sumar valores de un campo.\r\n * \r\n * @param field Nombre del campo a sumar\r\n * \r\n * @example\r\n * ```typescript\r\n * const snapshot = await getAggregateFromServer(\r\n * query(collection(db, 'orders')),\r\n * { totalAmount: sum('amount') }\r\n * );\r\n * console.log(snapshot.data().totalAmount);\r\n * ```\r\n */\r\nexport function sum(field: string): AggregateField<number> {\r\n return {\r\n _aggregateType: 'sum',\r\n _field: field,\r\n };\r\n}\r\n\r\n/**\r\n * Crea un campo de agregación para calcular el promedio de un campo.\r\n * \r\n * @param field Nombre del campo a promediar\r\n * \r\n * @example\r\n * ```typescript\r\n * const snapshot = await getAggregateFromServer(\r\n * query(collection(db, 'products')),\r\n * { avgPrice: average('price') }\r\n * );\r\n * console.log(snapshot.data().avgPrice);\r\n * ```\r\n */\r\nexport function average(field: string): AggregateField<number | null> {\r\n return {\r\n _aggregateType: 'average',\r\n _field: field,\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// Funciones de Ejecución\r\n// ============================================================================\r\n\r\n/**\r\n * Obtiene el conteo de documentos de una query desde el servidor.\r\n * \r\n * @param query Query a contar\r\n * @returns Promise con snapshot conteniendo el conteo\r\n * \r\n * @example\r\n * ```typescript\r\n * const q = query(collection(db, 'users'), where('active', '==', true));\r\n * const snapshot = await getCountFromServer(q);\r\n * console.log('Active users:', snapshot.data().count);\r\n * ```\r\n */\r\nexport async function getCountFromServer<T = DocumentData>(\r\n query: Query<T> | CollectionReference<T>\r\n): Promise<AggregateQuerySnapshot<{ count: AggregateField<number> }>> {\r\n return getAggregateFromServer(query, { count: count() });\r\n}\r\n\r\n/**\r\n * Ejecuta múltiples agregaciones en una query desde el servidor.\r\n * \r\n * @param query Query base para la agregación\r\n * @param aggregateSpec Especificación de campos a agregar\r\n * @returns Promise con snapshot conteniendo los resultados\r\n * \r\n * @example\r\n * ```typescript\r\n * const q = query(collection(db, 'orders'), where('status', '==', 'completed'));\r\n * const snapshot = await getAggregateFromServer(q, {\r\n * totalOrders: count(),\r\n * totalRevenue: sum('amount'),\r\n * avgOrderValue: average('amount')\r\n * });\r\n * \r\n * const data = snapshot.data();\r\n * console.log('Total orders:', data.totalOrders);\r\n * console.log('Total revenue:', data.totalRevenue);\r\n * console.log('Average order:', data.avgOrderValue);\r\n * ```\r\n */\r\nexport async function getAggregateFromServer<\r\n T = DocumentData,\r\n S extends AggregateSpec = AggregateSpec\r\n>(\r\n query: Query<T> | CollectionReference<T>,\r\n aggregateSpec: S\r\n): Promise<AggregateQuerySnapshot<S>> {\r\n const firestore = query.firestore as FirestoreInstance;\r\n const client = getHttpClient(firestore);\r\n \r\n // Construir query estructurada\r\n const structuredQuery = buildAggregateQuery(query, aggregateSpec);\r\n \r\n // Ejecutar\r\n const response = await client.post<\r\n { result?: { aggregateFields?: Record<string, { integerValue?: string; doubleValue?: number }> } }[] |\r\n { result?: { aggregateFields?: Record<string, { integerValue?: string; doubleValue?: number }> } }\r\n >('/documents:runAggregationQuery', {\r\n structuredAggregationQuery: structuredQuery,\r\n });\r\n \r\n // Parsear resultados - manejar respuesta como array u objeto\r\n let aggregateFields: Record<string, { integerValue?: string; doubleValue?: number; nullValue?: null }> = {};\r\n if (Array.isArray(response)) {\r\n aggregateFields = response[0]?.result?.aggregateFields || {};\r\n } else {\r\n aggregateFields = response?.result?.aggregateFields || {};\r\n }\r\n \r\n const resultData: Record<string, number | null> = {};\r\n for (const [alias, spec] of Object.entries(aggregateSpec)) {\r\n const value = aggregateFields[alias];\r\n if (value) {\r\n if ('nullValue' in value) {\r\n resultData[alias] = null;\r\n } else if (value.integerValue !== undefined) {\r\n resultData[alias] = parseInt(value.integerValue, 10);\r\n } else if (value.doubleValue !== undefined) {\r\n resultData[alias] = value.doubleValue;\r\n } else {\r\n resultData[alias] = null;\r\n }\r\n } else {\r\n // Si no hay resultado, el valor es 0 para count/sum, null para average\r\n resultData[alias] = spec._aggregateType === 'average' ? null : 0;\r\n }\r\n }\r\n \r\n return {\r\n data: () => resultData as AggregateSpecData<S>,\r\n };\r\n}\r\n\r\n/**\r\n * Construye la query de agregación para la API\r\n */\r\nfunction buildAggregateQuery<T, S extends AggregateSpec>(\r\n query: Query<T> | CollectionReference<T>,\r\n aggregateSpec: S\r\n): object {\r\n // Obtener colección ID del path\r\n const pathParts = query.path.split('/');\r\n const collectionId = pathParts[pathParts.length - 1];\r\n \r\n // Construir structured query base\r\n const structuredQuery: Record<string, unknown> = {\r\n from: [{ collectionId }],\r\n };\r\n \r\n // Si hay constraints en la query, aplicarlos\r\n if ('constraints' in query) {\r\n const constraints = (query as unknown as { constraints: unknown[] }).constraints || [];\r\n // Aplicar filtros (simplificado - en producción usar buildStructuredQuery)\r\n const wheres = constraints.filter((c: unknown) => (c as { type: string }).type === 'where');\r\n if (wheres.length > 0) {\r\n // Construir where clause\r\n structuredQuery.where = buildWhereFromConstraints(wheres);\r\n }\r\n }\r\n \r\n // Construir agregaciones\r\n const aggregations = Object.entries(aggregateSpec).map(([alias, spec]) => {\r\n const aggregation: Record<string, unknown> = { alias };\r\n \r\n switch (spec._aggregateType) {\r\n case 'count':\r\n aggregation.count = {};\r\n break;\r\n case 'sum':\r\n aggregation.sum = { field: { fieldPath: spec._field } };\r\n break;\r\n case 'average':\r\n aggregation.average = { field: { fieldPath: spec._field } };\r\n break;\r\n }\r\n \r\n return aggregation;\r\n });\r\n \r\n return {\r\n structuredQuery,\r\n aggregations,\r\n };\r\n}\r\n\r\n/**\r\n * Construye where clause desde constraints\r\n */\r\nfunction buildWhereFromConstraints(constraints: unknown[]): object {\r\n interface WhereConstraint {\r\n type: 'where';\r\n field: string;\r\n op: string;\r\n value: unknown;\r\n }\r\n\r\n const filters = constraints.map((c) => {\r\n const constraint = c as WhereConstraint;\r\n return {\r\n fieldFilter: {\r\n field: { fieldPath: constraint.field },\r\n op: mapOperator(constraint.op),\r\n value: toFirestoreValue(constraint.value),\r\n },\r\n };\r\n });\r\n \r\n if (filters.length === 1) {\r\n return filters[0];\r\n }\r\n \r\n return {\r\n compositeFilter: {\r\n op: 'AND',\r\n filters,\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Mapea operador a formato Firestore\r\n */\r\nfunction mapOperator(op: string): string {\r\n const opMap: Record<string, string> = {\r\n '<': 'LESS_THAN',\r\n '<=': 'LESS_THAN_OR_EQUAL',\r\n '==': 'EQUAL',\r\n '!=': 'NOT_EQUAL',\r\n '>': 'GREATER_THAN',\r\n '>=': 'GREATER_THAN_OR_EQUAL',\r\n 'array-contains': 'ARRAY_CONTAINS',\r\n 'array-contains-any': 'ARRAY_CONTAINS_ANY',\r\n 'in': 'IN',\r\n 'not-in': 'NOT_IN',\r\n };\r\n return opMap[op] || 'EQUAL';\r\n}\r\n\r\n/**\r\n * Convierte valor a formato Firestore\r\n */\r\nfunction toFirestoreValue(value: unknown): object {\r\n if (value === null) return { nullValue: null };\r\n if (typeof value === 'boolean') return { booleanValue: value };\r\n if (typeof value === 'number') {\r\n return Number.isInteger(value) \r\n ? { integerValue: String(value) }\r\n : { doubleValue: value };\r\n }\r\n if (typeof value === 'string') return { stringValue: value };\r\n if (Array.isArray(value)) {\r\n return { arrayValue: { values: value.map(toFirestoreValue) } };\r\n }\r\n if (typeof value === 'object') {\r\n const fields: Record<string, object> = {};\r\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\r\n fields[k] = toFirestoreValue(v);\r\n }\r\n return { mapValue: { fields } };\r\n }\r\n return { stringValue: String(value) };\r\n}\r\n","/**\r\n * Firestore SDK - Búsqueda Avanzada\r\n * Extensiones de búsqueda que superan las capacidades de Firestore original\r\n * \r\n * Operadores disponibles:\r\n * - _search: Full-text search con normalización de acentos\r\n * - _contains: Búsqueda in-string (busca \"tel\" en \"Hotel\")\r\n * - _fuzzy: Fuzzy matching con tolerancia a errores de tipeo\r\n * - _similar: Similitud de trigramas (coincidencia parcial)\r\n * \r\n * @example\r\n * ```typescript\r\n * import { whereContains, whereFuzzy, orderByRelevance } from '@ponceca/firestore-sdk';\r\n * \r\n * // Buscar \"tel\" dentro de cualquier parte del campo (encuentra \"Hotel\")\r\n * const q1 = query(collection(db, 'places'), whereContains('name', 'tel'));\r\n * \r\n * // Buscar con tolerancia a errores de tipeo (\"algodoon\" → \"algodón\")\r\n * const q2 = query(collection(db, 'products'), whereFuzzy('description', 'algodoon'));\r\n * \r\n * // Ordenar por relevancia\r\n * const q3 = query(\r\n * collection(db, 'articles'),\r\n * whereSearch('title', 'javascript'),\r\n * orderByRelevance()\r\n * );\r\n * ```\r\n */\r\n\r\nimport type {\r\n WhereConstraint,\r\n OrderByConstraint,\r\n FirestoreInstance,\r\n DocumentData,\r\n} from './types';\r\nimport { getHttpClient } from '../transport/http';\r\n\r\n/**\r\n * Busca texto usando full-text search con normalización de acentos.\r\n * Insensible a mayúsculas/minúsculas y acentos.\r\n * \r\n * @param field Campo a buscar\r\n * @param value Texto a buscar\r\n * @returns Constraint de búsqueda\r\n * \r\n * @example\r\n * ```typescript\r\n * // Busca \"camion\" y encuentra \"Camión\", \"CAMIÓN\", \"camion\"\r\n * const q = query(collection(db, 'vehicles'), whereSearch('name', 'camion'));\r\n * ```\r\n */\r\nexport function whereSearch(field: string, value: string): WhereConstraint {\r\n return {\r\n type: 'where',\r\n field: `${field}_search`,\r\n op: '==',\r\n value,\r\n };\r\n}\r\n\r\n/**\r\n * Busca texto en cualquier posición del campo (in-string search).\r\n * Busca \"tel\" y encuentra \"Hotel\", \"telefono\", \"cartel\".\r\n * \r\n * @param field Campo a buscar\r\n * @param value Texto a buscar\r\n * @returns Constraint de búsqueda\r\n * \r\n * @example\r\n * ```typescript\r\n * // Busca \"tel\" dentro de cualquier parte (encuentra \"Hotel\", \"telefono\")\r\n * const q = query(collection(db, 'places'), whereContains('name', 'tel'));\r\n * ```\r\n */\r\nexport function whereContains(field: string, value: string): WhereConstraint {\r\n return {\r\n type: 'where',\r\n field: `${field}_contains`,\r\n op: '==',\r\n value,\r\n };\r\n}\r\n\r\n/**\r\n * Busca texto con tolerancia a errores de tipeo (fuzzy matching).\r\n * Usa distancia de Levenshtein para encontrar coincidencias aproximadas.\r\n * \r\n * @param field Campo a buscar\r\n * @param value Texto a buscar\r\n * @returns Constraint de búsqueda\r\n * \r\n * @example\r\n * ```typescript\r\n * // Busca \"algodoon\" y encuentra \"algodón\"\r\n * const q = query(collection(db, 'products'), whereFuzzy('material', 'algodoon'));\r\n * ```\r\n */\r\nexport function whereFuzzy(field: string, value: string): WhereConstraint {\r\n return {\r\n type: 'where',\r\n field: `${field}_fuzzy`,\r\n op: '==',\r\n value,\r\n // El threshold se maneja en el backend (default 0.3)\r\n };\r\n}\r\n\r\n/**\r\n * Busca texto por similitud de trigramas.\r\n * Encuentra coincidencias parciales basadas en secuencias de 3 caracteres.\r\n * \r\n * @param field Campo a buscar\r\n * @param value Texto a buscar\r\n * @returns Constraint de búsqueda\r\n * \r\n * @example\r\n * ```typescript\r\n * // Encuentra palabras similares a \"javascript\"\r\n * const q = query(collection(db, 'articles'), whereSimilar('title', 'javascript'));\r\n * ```\r\n */\r\nexport function whereSimilar(field: string, value: string): WhereConstraint {\r\n return {\r\n type: 'where',\r\n field: `${field}_similar`,\r\n op: '==',\r\n value,\r\n };\r\n}\r\n\r\n/**\r\n * Ordena los resultados por relevancia (score de búsqueda).\r\n * Debe usarse junto con operadores de búsqueda (_search, _contains, _fuzzy, _similar).\r\n * Los documentos más relevantes aparecen primero.\r\n * \r\n * @param direction Dirección del ordenamiento ('desc' = más relevantes primero)\r\n * @returns Constraint de ordenamiento\r\n * \r\n * @example\r\n * ```typescript\r\n * const q = query(\r\n * collection(db, 'products'),\r\n * whereSearch('description', 'laptop gaming'),\r\n * orderByRelevance('desc')\r\n * );\r\n * ```\r\n */\r\nexport function orderByRelevance(direction: 'asc' | 'desc' = 'desc'): OrderByConstraint {\r\n return {\r\n type: 'orderBy',\r\n field: '__relevance__',\r\n direction,\r\n };\r\n}\r\n\r\n/**\r\n * Paginación por offset directo.\r\n * Permite saltar a cualquier página sin leer documentos anteriores.\r\n * \r\n * @param count Número de documentos a saltar\r\n * @returns Constraint de offset\r\n * \r\n * @example\r\n * ```typescript\r\n * // Ir directamente a la página 5 (saltando 40 documentos)\r\n * const q = query(\r\n * collection(db, 'products'),\r\n * limit(10),\r\n * offset(40)\r\n * );\r\n * ```\r\n */\r\nexport function offset(count: number): { type: 'offset'; count: number } {\r\n return {\r\n type: 'offset',\r\n count,\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// fastSearch (endpoint ultra rápido)\r\n// ============================================================================\r\n\r\nexport interface FastSearchOptions {\r\n /** Campo específico donde buscar. Si está vacío, busca en todos los campos (global). */\r\n field?: string;\r\n /** Máximo de resultados a retornar. Default: 50 */\r\n limit?: number;\r\n /** Usar búsqueda fuzzy (tolerante a errores de tipeo). Default: false */\r\n fuzzy?: boolean;\r\n}\r\n\r\nexport interface FastSearchResult<T = DocumentData> {\r\n id: string;\r\n data: T;\r\n}\r\n\r\nexport interface FastSearchResponse<T = DocumentData> {\r\n count: number;\r\n documents: FastSearchResult<T>[];\r\n /** Indica si fue búsqueda global o por campo específico */\r\n searchMode?: string;\r\n}\r\n\r\n/**\r\n * Búsqueda ultra rápida vía endpoint dedicado.\r\n * Ideal para autocomplete y prefijos con latencia mínima.\r\n * Usa fetch directo para minimizar overhead.\r\n *\r\n * @param firestore Instancia de Firestore\r\n * @param collectionPath Ruta de colección\r\n * @param term Término de búsqueda\r\n * @param options Opciones de búsqueda (incluye field para búsqueda específica)\r\n *\r\n * @example\r\n * // Búsqueda en campo específico (recomendado)\r\n * const results = await fastSearch(db, 'articles', 'javascript', { field: 'title' });\r\n *\r\n * // Búsqueda global (todos los campos)\r\n * const results = await fastSearch(db, 'articles', 'javascript');\r\n */\r\nexport async function fastSearch<T = DocumentData>(\r\n firestore: FirestoreInstance,\r\n collectionPath: string,\r\n term: string,\r\n options: FastSearchOptions = {}\r\n): Promise<FastSearchResponse<T>> {\r\n if (!collectionPath) {\r\n throw new Error('La colección es obligatoria.');\r\n }\r\n if (!term) {\r\n throw new Error('El término es obligatorio.');\r\n }\r\n\r\n // Construir path relativo para HttpClient (maneja auth, retry, timeout)\r\n const params = new URLSearchParams({\r\n q: term,\r\n limit: String(options.limit ?? 50),\r\n fuzzy: options.fuzzy ? 'true' : 'false',\r\n });\r\n if (options.field) {\r\n params.set('field', options.field);\r\n }\r\n\r\n const searchPath = `/search/${encodeURIComponent(collectionPath)}?${params.toString()}`;\r\n const client = getHttpClient(firestore);\r\n return await client.get<FastSearchResponse<T>>(searchPath);\r\n}\r\n"],"mappings":"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","names":["doc","doc","documentId","doc","docs","createFirestoreError","where","isCollectionGroup","query","query","isCollectionGroup","getHttpClient","createQuerySnapshot","toFirestoreValue","query","getNestedValue","where","createFirestoreError","query","toFirestoreValue","count"]}
@@ -0,0 +1,65 @@
1
+ import {
2
+ __esm,
3
+ __export
4
+ } from "./chunk-NFEGQTCC.mjs";
5
+
6
+ // src/app.ts
7
+ var app_exports = {};
8
+ __export(app_exports, {
9
+ deleteApp: () => deleteApp,
10
+ getApp: () => getApp,
11
+ getApps: () => getApps,
12
+ initializeApp: () => initializeApp
13
+ });
14
+ function initializeApp(options, name = DEFAULT_APP_NAME) {
15
+ if (apps.has(name)) {
16
+ throw new Error(
17
+ `Firebase: Firebase App named '${name}' already exists with different options or config (app/duplicate-app).`
18
+ );
19
+ }
20
+ if (!options.projectId) {
21
+ throw new Error("Firebase: projectId is required");
22
+ }
23
+ const app = {
24
+ name,
25
+ options: {
26
+ ...options,
27
+ baseUrl: options.baseUrl || "http://localhost:3000",
28
+ databaseId: options.databaseId || "(default)"
29
+ }
30
+ };
31
+ apps.set(name, app);
32
+ return app;
33
+ }
34
+ function getApp(name = DEFAULT_APP_NAME) {
35
+ const app = apps.get(name);
36
+ if (!app) {
37
+ throw new Error(
38
+ `Firebase: No Firebase App '${name}' has been created - call Firebase.initializeApp()`
39
+ );
40
+ }
41
+ return app;
42
+ }
43
+ function getApps() {
44
+ return Array.from(apps.values());
45
+ }
46
+ async function deleteApp(app) {
47
+ apps.delete(app.name);
48
+ }
49
+ var apps, DEFAULT_APP_NAME;
50
+ var init_app = __esm({
51
+ "src/app.ts"() {
52
+ apps = /* @__PURE__ */ new Map();
53
+ DEFAULT_APP_NAME = "[DEFAULT]";
54
+ }
55
+ });
56
+
57
+ export {
58
+ initializeApp,
59
+ getApp,
60
+ getApps,
61
+ deleteApp,
62
+ app_exports,
63
+ init_app
64
+ };
65
+ //# sourceMappingURL=chunk-MRVKMKSO.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/app.ts"],"sourcesContent":["/**\r\n * Firestore SDK - App\r\n * Inicialización compatible con Firebase v9\r\n */\r\n\r\nimport type { FirebaseApp, FirebaseOptions } from './firestore/types';\r\n\r\n// Re-export tipos\r\nexport type { FirebaseApp, FirebaseOptions };\r\n\r\n// Almacén de apps inicializadas\r\nconst apps = new Map<string, FirebaseApp>();\r\n\r\n// Nombre por defecto de la app\r\nconst DEFAULT_APP_NAME = '[DEFAULT]';\r\n\r\n/**\r\n * Inicializa una Firebase App\r\n * @param options Opciones de configuración\r\n * @param name Nombre de la app (opcional)\r\n * @returns Instancia de FirebaseApp\r\n * \r\n * @example\r\n * ```typescript\r\n * const app = initializeApp({\r\n * projectId: 'my-project',\r\n * baseUrl: 'http://localhost:3000'\r\n * });\r\n * ```\r\n */\r\nexport function initializeApp(\r\n options: FirebaseOptions,\r\n name: string = DEFAULT_APP_NAME\r\n): FirebaseApp {\r\n // Verificar que no exista ya\r\n if (apps.has(name)) {\r\n throw new Error(\r\n `Firebase: Firebase App named '${name}' already exists with different options or config (app/duplicate-app).`\r\n );\r\n }\r\n\r\n // Validar opciones requeridas\r\n if (!options.projectId) {\r\n throw new Error('Firebase: projectId is required');\r\n }\r\n\r\n // Crear app\r\n const app: FirebaseApp = {\r\n name,\r\n options: {\r\n ...options,\r\n baseUrl: options.baseUrl || 'http://localhost:3000',\r\n databaseId: options.databaseId || '(default)',\r\n },\r\n };\r\n\r\n // Guardar y retornar\r\n apps.set(name, app);\r\n return app;\r\n}\r\n\r\n/**\r\n * Obtiene una Firebase App existente\r\n * @param name Nombre de la app (opcional)\r\n * @returns Instancia de FirebaseApp\r\n * \r\n * @example\r\n * ```typescript\r\n * const app = getApp(); // App por defecto\r\n * const namedApp = getApp('my-app');\r\n * ```\r\n */\r\nexport function getApp(name: string = DEFAULT_APP_NAME): FirebaseApp {\r\n const app = apps.get(name);\r\n if (!app) {\r\n throw new Error(\r\n `Firebase: No Firebase App '${name}' has been created - call Firebase.initializeApp()`\r\n );\r\n }\r\n return app;\r\n}\r\n\r\n/**\r\n * Obtiene todas las Firebase Apps inicializadas\r\n * @returns Array de FirebaseApp\r\n */\r\nexport function getApps(): FirebaseApp[] {\r\n return Array.from(apps.values());\r\n}\r\n\r\n/**\r\n * Elimina una Firebase App\r\n * @param app App a eliminar\r\n * \r\n * @example\r\n * ```typescript\r\n * await deleteApp(app);\r\n * ```\r\n */\r\nexport async function deleteApp(app: FirebaseApp): Promise<void> {\r\n apps.delete(app.name);\r\n}\r\n"],"mappings":";;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8BO,SAAS,cACd,SACA,OAAe,kBACF;AAEb,MAAI,KAAK,IAAI,IAAI,GAAG;AAClB,UAAM,IAAI;AAAA,MACR,iCAAiC,IAAI;AAAA,IACvC;AAAA,EACF;AAGA,MAAI,CAAC,QAAQ,WAAW;AACtB,UAAM,IAAI,MAAM,iCAAiC;AAAA,EACnD;AAGA,QAAM,MAAmB;AAAA,IACvB;AAAA,IACA,SAAS;AAAA,MACP,GAAG;AAAA,MACH,SAAS,QAAQ,WAAW;AAAA,MAC5B,YAAY,QAAQ,cAAc;AAAA,IACpC;AAAA,EACF;AAGA,OAAK,IAAI,MAAM,GAAG;AAClB,SAAO;AACT;AAaO,SAAS,OAAO,OAAe,kBAA+B;AACnE,QAAM,MAAM,KAAK,IAAI,IAAI;AACzB,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR,8BAA8B,IAAI;AAAA,IACpC;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,UAAyB;AACvC,SAAO,MAAM,KAAK,KAAK,OAAO,CAAC;AACjC;AAWA,eAAsB,UAAU,KAAiC;AAC/D,OAAK,OAAO,IAAI,IAAI;AACtB;AArGA,IAWM,MAGA;AAdN;AAAA;AAWA,IAAM,OAAO,oBAAI,IAAyB;AAG1C,IAAM,mBAAmB;AAAA;AAAA;","names":[]}
@@ -0,0 +1,27 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __esm = (fn, res) => function __init() {
6
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
7
+ };
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+
22
+ export {
23
+ __esm,
24
+ __export,
25
+ __toCommonJS
26
+ };
27
+ //# sourceMappingURL=chunk-NFEGQTCC.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}