@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/special-types.ts","../src/firestore/field-value.ts","../src/firestore/snapshot.ts"],"sourcesContent":["/**\r\n * Firestore SDK - Tipos Especiales\r\n * Timestamp, GeoPoint, Bytes, FieldPath, VectorValue\r\n * Compatible con Firebase Firestore v9\r\n */\r\n\r\n// ============================================================================\r\n// Timestamp\r\n// ============================================================================\r\n\r\n/**\r\n * Representa un punto en el tiempo con precisión de nanosegundos.\r\n * Compatible con Firestore Timestamp.\r\n */\r\nexport class Timestamp {\r\n /**\r\n * Crea un nuevo Timestamp.\r\n * \r\n * @param seconds Segundos desde la época Unix (1 enero 1970 UTC)\r\n * @param nanoseconds Nanosegundos adicionales (0-999999999)\r\n */\r\n constructor(\r\n readonly seconds: number,\r\n readonly nanoseconds: number\r\n ) {\r\n if (nanoseconds < 0 || nanoseconds >= 1e9) {\r\n throw new Error('Nanoseconds must be between 0 and 999999999');\r\n }\r\n }\r\n\r\n /**\r\n * Crea un Timestamp para el momento actual.\r\n */\r\n static now(): Timestamp {\r\n const now = Date.now();\r\n return Timestamp.fromMillis(now);\r\n }\r\n\r\n /**\r\n * Crea un Timestamp desde un objeto Date.\r\n */\r\n static fromDate(date: Date): Timestamp {\r\n return Timestamp.fromMillis(date.getTime());\r\n }\r\n\r\n /**\r\n * Crea un Timestamp desde milisegundos.\r\n */\r\n static fromMillis(milliseconds: number): Timestamp {\r\n const seconds = Math.floor(milliseconds / 1000);\r\n const nanoseconds = (milliseconds % 1000) * 1e6;\r\n return new Timestamp(seconds, nanoseconds);\r\n }\r\n\r\n /**\r\n * Convierte el Timestamp a un objeto Date.\r\n */\r\n toDate(): Date {\r\n return new Date(this.toMillis());\r\n }\r\n\r\n /**\r\n * Convierte el Timestamp a milisegundos.\r\n */\r\n toMillis(): number {\r\n return this.seconds * 1000 + Math.floor(this.nanoseconds / 1e6);\r\n }\r\n\r\n /**\r\n * Convierte a formato ISO 8601 para serialización.\r\n */\r\n toJSON(): string {\r\n return this.toDate().toISOString();\r\n }\r\n\r\n /**\r\n * Compara con otro Timestamp.\r\n */\r\n isEqual(other: Timestamp): boolean {\r\n return this.seconds === other.seconds && this.nanoseconds === other.nanoseconds;\r\n }\r\n\r\n /**\r\n * Representación en string.\r\n */\r\n toString(): string {\r\n return `Timestamp(seconds=${this.seconds}, nanoseconds=${this.nanoseconds})`;\r\n }\r\n\r\n /**\r\n * Compara dos Timestamps.\r\n * @returns negativo si this < other, positivo si this > other, 0 si iguales\r\n */\r\n compareTo(other: Timestamp): number {\r\n if (this.seconds !== other.seconds) {\r\n return this.seconds - other.seconds;\r\n }\r\n return this.nanoseconds - other.nanoseconds;\r\n }\r\n\r\n /**\r\n * Obtiene el valor primitivo para comparaciones.\r\n */\r\n valueOf(): number {\r\n return this.toMillis();\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// GeoPoint\r\n// ============================================================================\r\n\r\n/**\r\n * Representa un punto geográfico con latitud y longitud.\r\n * Compatible con Firestore GeoPoint.\r\n */\r\nexport class GeoPoint {\r\n /**\r\n * Crea un nuevo GeoPoint.\r\n * \r\n * @param latitude Latitud en grados (-90 a 90)\r\n * @param longitude Longitud en grados (-180 a 180)\r\n */\r\n constructor(\r\n readonly latitude: number,\r\n readonly longitude: number\r\n ) {\r\n if (latitude < -90 || latitude > 90) {\r\n throw new Error('Latitude must be between -90 and 90 degrees');\r\n }\r\n if (longitude < -180 || longitude > 180) {\r\n throw new Error('Longitude must be between -180 and 180 degrees');\r\n }\r\n }\r\n\r\n /**\r\n * Compara con otro GeoPoint.\r\n */\r\n isEqual(other: GeoPoint): boolean {\r\n return this.latitude === other.latitude && this.longitude === other.longitude;\r\n }\r\n\r\n /**\r\n * Convierte a objeto plano para serialización.\r\n */\r\n toJSON(): { latitude: number; longitude: number } {\r\n return { latitude: this.latitude, longitude: this.longitude };\r\n }\r\n\r\n /**\r\n * Representación en string.\r\n */\r\n toString(): string {\r\n return `GeoPoint(${this.latitude}, ${this.longitude})`;\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Bytes\r\n// ============================================================================\r\n\r\n/**\r\n * Representa datos binarios inmutables.\r\n * Compatible con Firestore Bytes.\r\n */\r\nexport class Bytes {\r\n private readonly _bytes: Uint8Array;\r\n\r\n private constructor(bytes: Uint8Array) {\r\n this._bytes = bytes;\r\n }\r\n\r\n /**\r\n * Crea Bytes desde una cadena Base64.\r\n */\r\n static fromBase64String(base64: string): Bytes {\r\n // Decodificar base64\r\n const binaryString = atob(base64);\r\n const bytes = new Uint8Array(binaryString.length);\r\n for (let i = 0; i < binaryString.length; i++) {\r\n bytes[i] = binaryString.charCodeAt(i);\r\n }\r\n return new Bytes(bytes);\r\n }\r\n\r\n /**\r\n * Crea Bytes desde un Uint8Array.\r\n */\r\n static fromUint8Array(array: Uint8Array): Bytes {\r\n return new Bytes(new Uint8Array(array));\r\n }\r\n\r\n /**\r\n * Convierte a cadena Base64.\r\n */\r\n toBase64(): string {\r\n let binary = '';\r\n for (let i = 0; i < this._bytes.length; i++) {\r\n binary += String.fromCharCode(this._bytes[i]);\r\n }\r\n return btoa(binary);\r\n }\r\n\r\n /**\r\n * Convierte a Uint8Array.\r\n */\r\n toUint8Array(): Uint8Array {\r\n return new Uint8Array(this._bytes);\r\n }\r\n\r\n /**\r\n * Compara con otro Bytes.\r\n */\r\n isEqual(other: Bytes): boolean {\r\n if (this._bytes.length !== other._bytes.length) {\r\n return false;\r\n }\r\n for (let i = 0; i < this._bytes.length; i++) {\r\n if (this._bytes[i] !== other._bytes[i]) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n }\r\n\r\n /**\r\n * Representación en string.\r\n */\r\n toString(): string {\r\n return `Bytes(byteLength=${this._bytes.length})`;\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// FieldPath\r\n// ============================================================================\r\n\r\n/**\r\n * Representa una ruta de campo en un documento.\r\n * Permite acceder a campos anidados de forma segura.\r\n */\r\nexport class FieldPath {\r\n private readonly _fields: string[];\r\n\r\n /**\r\n * Crea un FieldPath desde segmentos de campo.\r\n * \r\n * @param fieldNames Nombres de campos anidados\r\n * \r\n * @example\r\n * ```typescript\r\n * new FieldPath('user', 'address', 'city')\r\n * // Equivale a 'user.address.city'\r\n * ```\r\n */\r\n constructor(...fieldNames: string[]) {\r\n if (fieldNames.length === 0) {\r\n throw new Error('FieldPath must have at least one field name');\r\n }\r\n for (const field of fieldNames) {\r\n if (typeof field !== 'string' || field.length === 0) {\r\n throw new Error('Each field name must be a non-empty string');\r\n }\r\n }\r\n this._fields = fieldNames;\r\n }\r\n\r\n /**\r\n * Compara con otro FieldPath.\r\n */\r\n isEqual(other: FieldPath): boolean {\r\n if (this._fields.length !== other._fields.length) {\r\n return false;\r\n }\r\n for (let i = 0; i < this._fields.length; i++) {\r\n if (this._fields[i] !== other._fields[i]) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n }\r\n\r\n /**\r\n * Convierte a string con notación de puntos.\r\n */\r\n toString(): string {\r\n return this._fields.map(f => {\r\n // Escapar campos que contienen puntos o caracteres especiales\r\n if (f.includes('.') || f.includes('`')) {\r\n return '`' + f.replace(/`/g, '\\\\`') + '`';\r\n }\r\n return f;\r\n }).join('.');\r\n }\r\n\r\n /**\r\n * Obtiene los segmentos del path.\r\n */\r\n get segments(): string[] {\r\n return [...this._fields];\r\n }\r\n}\r\n\r\n/**\r\n * Crea un FieldPath que apunta al ID del documento.\r\n * Útil para queries que filtran por document ID.\r\n * \r\n * @example\r\n * ```typescript\r\n * query(collection(db, 'users'), where(documentId(), '==', 'user123'))\r\n * ```\r\n */\r\nexport function documentId(): FieldPath {\r\n return new FieldPath('__name__');\r\n}\r\n\r\n// ============================================================================\r\n// VectorValue\r\n// ============================================================================\r\n\r\n/**\r\n * Representa un vector de números para embeddings/ML.\r\n * Compatible con Firestore VectorValue.\r\n */\r\nexport class VectorValue {\r\n private readonly _values: number[];\r\n\r\n private constructor(values: number[]) {\r\n this._values = values;\r\n }\r\n\r\n /**\r\n * Convierte a array de números.\r\n */\r\n toArray(): number[] {\r\n return [...this._values];\r\n }\r\n\r\n /**\r\n * Compara con otro VectorValue.\r\n */\r\n isEqual(other: VectorValue): boolean {\r\n if (this._values.length !== other._values.length) {\r\n return false;\r\n }\r\n for (let i = 0; i < this._values.length; i++) {\r\n if (this._values[i] !== other._values[i]) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n }\r\n\r\n /**\r\n * Representación en string.\r\n */\r\n toString(): string {\r\n return `VectorValue(length=${this._values.length})`;\r\n }\r\n\r\n /**\r\n * Obtiene la dimensión del vector.\r\n */\r\n get length(): number {\r\n return this._values.length;\r\n }\r\n}\r\n\r\n/**\r\n * Crea un VectorValue desde un array de números.\r\n * \r\n * @param values Array de números\r\n * \r\n * @example\r\n * ```typescript\r\n * const embedding = vector([0.1, 0.2, 0.3, 0.4]);\r\n * await setDoc(docRef, { embedding });\r\n * ```\r\n */\r\nexport function vector(values: number[]): VectorValue {\r\n if (!Array.isArray(values)) {\r\n throw new Error('vector() requires an array of numbers');\r\n }\r\n for (const v of values) {\r\n if (typeof v !== 'number' || !isFinite(v)) {\r\n throw new Error('All values in vector must be finite numbers');\r\n }\r\n }\r\n // Creamos usando el constructor indirectamente\r\n return Object.assign(Object.create(VectorValue.prototype), {\r\n _values: [...values],\r\n });\r\n}\r\n","/**\r\n * Firestore SDK - Field Values\r\n * serverTimestamp, increment, arrayUnion, arrayRemove, deleteField\r\n */\r\n\r\nimport type { FieldValue } from './types';\r\n\r\n/**\r\n * Tipo interno para valores especiales de Firestore\r\n */\r\ninterface FieldValueImpl {\r\n _type: string;\r\n _value?: unknown;\r\n isEqual: (other: FieldValue) => boolean;\r\n}\r\n\r\n/**\r\n * Crea un valor que será reemplazado por el timestamp del servidor\r\n * \r\n * @returns FieldValue representando el timestamp del servidor\r\n * \r\n * @example\r\n * ```typescript\r\n * await setDoc(doc(db, 'posts', 'post1'), {\r\n * title: 'Mi post',\r\n * createdAt: serverTimestamp()\r\n * });\r\n * ```\r\n */\r\nexport function serverTimestamp(): FieldValue {\r\n return {\r\n _type: 'serverTimestamp',\r\n isEqual: (other) => (other as FieldValueImpl)?._type === 'serverTimestamp',\r\n } as FieldValueImpl;\r\n}\r\n\r\n/**\r\n * Crea un valor que incrementa el campo numérico existente\r\n * Soporta números y strings numéricos (para alta precisión).\r\n * \r\n * @param n Cantidad a incrementar (puede ser negativo)\r\n * @returns FieldValue representando el incremento\r\n * \r\n * @example\r\n * ```typescript\r\n * await updateDoc(doc(db, 'counters', 'pageViews'), {\r\n * count: increment(1)\r\n * });\r\n * // Precisión decimal\r\n * await updateDoc(doc(db, 'accounts', 'wallet'), {\r\n * balance: increment(\"100.50\") \r\n * });\r\n * ```\r\n */\r\nexport function increment(n: number | string): FieldValue {\r\n return {\r\n _type: 'increment',\r\n _value: n,\r\n isEqual: (other) =>\r\n (other as FieldValueImpl)?._type === 'increment' &&\r\n (other as FieldValueImpl)?._value === n,\r\n } as FieldValueImpl;\r\n}\r\n\r\n/**\r\n * Crea un valor que añade elementos a un array existente\r\n * Solo añade elementos que no existen ya\r\n * \r\n * @param elements Elementos a añadir\r\n * @returns FieldValue representando la unión de arrays\r\n * \r\n * @example\r\n * ```typescript\r\n * await updateDoc(doc(db, 'users', 'user1'), {\r\n * tags: arrayUnion('developer', 'javascript')\r\n * });\r\n * ```\r\n */\r\nexport function arrayUnion(...elements: unknown[]): FieldValue {\r\n return {\r\n _type: 'arrayUnion',\r\n _value: elements,\r\n isEqual: (other) =>\r\n (other as FieldValueImpl)?._type === 'arrayUnion' &&\r\n JSON.stringify((other as FieldValueImpl)?._value) === JSON.stringify(elements),\r\n } as FieldValueImpl;\r\n}\r\n\r\n/**\r\n * Crea un valor que elimina elementos de un array existente\r\n * \r\n * @param elements Elementos a eliminar\r\n * @returns FieldValue representando la eliminación de elementos\r\n * \r\n * @example\r\n * ```typescript\r\n * await updateDoc(doc(db, 'users', 'user1'), {\r\n * tags: arrayRemove('oldTag')\r\n * });\r\n * ```\r\n */\r\nexport function arrayRemove(...elements: unknown[]): FieldValue {\r\n return {\r\n _type: 'arrayRemove',\r\n _value: elements,\r\n isEqual: (other) =>\r\n (other as FieldValueImpl)?._type === 'arrayRemove' &&\r\n JSON.stringify((other as FieldValueImpl)?._value) === JSON.stringify(elements),\r\n } as FieldValueImpl;\r\n}\r\n\r\n/**\r\n * Crea un valor que elimina el campo del documento\r\n * \r\n * @returns FieldValue representando la eliminación del campo\r\n * \r\n * @example\r\n * ```typescript\r\n * await updateDoc(doc(db, 'users', 'user1'), {\r\n * tempField: deleteField()\r\n * });\r\n * ```\r\n */\r\nexport function deleteField(): FieldValue {\r\n return {\r\n _type: 'deleteField',\r\n isEqual: (other) => (other as FieldValueImpl)?._type === 'deleteField',\r\n } as FieldValueImpl;\r\n}\r\n\r\n/**\r\n * Verifica si un valor es un FieldValue especial\r\n * @internal\r\n */\r\nexport function isFieldValue(value: unknown): value is FieldValueImpl {\r\n return value !== null &&\r\n typeof value === 'object' &&\r\n '_type' in value &&\r\n typeof (value as FieldValueImpl)._type === 'string';\r\n}\r\n\r\n/**\r\n * Convierte un FieldValue a formato de la API de Firestore\r\n * @internal\r\n */\r\nexport function fieldValueToFirestore(fieldValue: FieldValueImpl): Record<string, unknown> {\r\n switch (fieldValue._type) {\r\n case 'serverTimestamp':\r\n return { fieldTransform: { setToServerValue: 'REQUEST_TIME' } };\r\n\r\n case 'increment':\r\n return {\r\n fieldTransform: {\r\n increment: typeof fieldValue._value === 'number'\r\n ? (Number.isInteger(fieldValue._value) ? { integerValue: String(fieldValue._value) } : { doubleValue: fieldValue._value })\r\n : { stringValue: String(fieldValue._value) }\r\n }\r\n };\r\n\r\n case 'arrayUnion':\r\n return {\r\n fieldTransform: {\r\n appendMissingElements: {\r\n values: (fieldValue._value as unknown[]).map(toFirestoreValueSimple)\r\n }\r\n }\r\n };\r\n\r\n case 'arrayRemove':\r\n return {\r\n fieldTransform: {\r\n removeAllFromArray: {\r\n values: (fieldValue._value as unknown[]).map(toFirestoreValueSimple)\r\n }\r\n }\r\n };\r\n\r\n case 'deleteField':\r\n return { fieldTransform: { delete: true } };\r\n\r\n default:\r\n throw new Error(`FieldValue desconocido: ${fieldValue._type}`);\r\n }\r\n}\r\n\r\n/**\r\n * Convierte un valor simple a formato Firestore\r\n * (versión simplificada para uso interno)\r\n */\r\nfunction toFirestoreValueSimple(value: unknown): Record<string, unknown> {\r\n if (value === null) {\r\n return { nullValue: null };\r\n }\r\n if (typeof value === 'boolean') {\r\n return { booleanValue: value };\r\n }\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') {\r\n return { stringValue: value };\r\n }\r\n if (value instanceof Date) {\r\n return { timestampValue: value.toISOString() };\r\n }\r\n if (Array.isArray(value)) {\r\n return {\r\n arrayValue: {\r\n values: value.map(toFirestoreValueSimple)\r\n }\r\n };\r\n }\r\n if (typeof value === 'object') {\r\n const fields: Record<string, unknown> = {};\r\n for (const [k, v] of Object.entries(value)) {\r\n fields[k] = toFirestoreValueSimple(v);\r\n }\r\n return { mapValue: { fields } };\r\n }\r\n\r\n throw new Error(`Tipo de valor no soportado: ${typeof value}`);\r\n}\r\n","/**\r\n * Firestore SDK - Snapshots\r\n * DocumentSnapshot, QuerySnapshot, conversiones\r\n */\r\n\r\nimport type {\r\n DocumentData,\r\n DocumentSnapshot,\r\n QuerySnapshot,\r\n DocumentChange,\r\n DocumentReference,\r\n Query,\r\n SnapshotMetadata,\r\n FieldPath,\r\n FirestoreDocument,\r\n FirestoreValue,\r\n} from './types';\r\nimport { Timestamp, GeoPoint, Bytes } from './special-types';\r\nimport { isFieldValue } from './field-value';\r\n\r\n// =============================================================================\r\n// CONVERSIÓN DE VALORES\r\n// =============================================================================\r\n\r\n/**\r\n * Convierte un valor de Firestore REST API a valor JS\r\n */\r\nexport function fromFirestoreValue(value: FirestoreValue): unknown {\r\n if ('nullValue' in value) return null;\r\n if ('booleanValue' in value) return value.booleanValue;\r\n if ('integerValue' in value) return parseInt(value.integerValue!, 10);\r\n if ('doubleValue' in value) return value.doubleValue;\r\n if ('stringValue' in value) return value.stringValue;\r\n if ('timestampValue' in value) {\r\n // Convertir a Timestamp preservando nanosegundos\r\n return parseTimestamp(value.timestampValue!);\r\n }\r\n if ('bytesValue' in value) {\r\n // Convertir a Bytes para compatibilidad\r\n return Bytes.fromBase64String(value.bytesValue!);\r\n }\r\n if ('referenceValue' in value) return value.referenceValue; // Path string\r\n if ('geoPointValue' in value) {\r\n // Convertir a GeoPoint para compatibilidad\r\n const gp = value.geoPointValue!;\r\n return new GeoPoint(gp.latitude, gp.longitude);\r\n }\r\n if ('arrayValue' in value) {\r\n return (value.arrayValue!.values || []).map(fromFirestoreValue);\r\n }\r\n if ('mapValue' in value) {\r\n const result: Record<string, unknown> = {};\r\n const fields = value.mapValue!.fields || {};\r\n for (const [key, val] of Object.entries(fields)) {\r\n result[key] = fromFirestoreValue(val);\r\n }\r\n return result;\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Convierte un valor JS a valor de Firestore REST API\r\n * Maneja FieldValues especiales (serverTimestamp, increment, etc.)\r\n */\r\nexport function toFirestoreValue(value: unknown): FirestoreValue {\r\n if (value === null || value === undefined) {\r\n return { nullValue: null };\r\n }\r\n if (typeof value === 'boolean') {\r\n return { booleanValue: value };\r\n }\r\n if (typeof value === 'number') {\r\n if (Number.isInteger(value)) {\r\n return { integerValue: value.toString() };\r\n }\r\n return { doubleValue: value };\r\n }\r\n if (typeof value === 'string') {\r\n return { stringValue: value };\r\n }\r\n if (value instanceof Date) {\r\n return { timestampValue: value.toISOString() };\r\n }\r\n // Timestamp class\r\n if (value instanceof Timestamp) {\r\n return { timestampValue: timestampToString(value) };\r\n }\r\n // GeoPoint class\r\n if (value instanceof GeoPoint) {\r\n return { geoPointValue: { latitude: value.latitude, longitude: value.longitude } };\r\n }\r\n // Bytes class\r\n if (value instanceof Bytes) {\r\n return { bytesValue: value.toBase64() };\r\n }\r\n if (Array.isArray(value)) {\r\n return {\r\n arrayValue: {\r\n values: value.map(toFirestoreValue),\r\n },\r\n };\r\n }\r\n // FieldValue especial (serverTimestamp, increment, etc.)\r\n if (isFieldValue(value)) {\r\n return fieldValueToBackendFormat(value);\r\n }\r\n if (typeof value === 'object') {\r\n const fields: Record<string, FirestoreValue> = {};\r\n for (const [key, val] of Object.entries(value)) {\r\n if (val === undefined) {\r\n continue;\r\n }\r\n fields[key] = toFirestoreValue(val);\r\n }\r\n return { mapValue: { fields } };\r\n }\r\n return { nullValue: null };\r\n}\r\n\r\n/**\r\n * Convierte un FieldValue al formato que espera el backend\r\n * El backend usa __type__ y __value__ / __values__\r\n */\r\nfunction fieldValueToBackendFormat(fv: unknown): FirestoreValue {\r\n const fieldValue = fv as { _type: string; _value?: unknown };\r\n const result: Record<string, unknown> = {\r\n __type__: fieldValue._type,\r\n };\r\n\r\n // Para arrayUnion/arrayRemove usar __values__\r\n if (fieldValue._type === 'arrayUnion' || fieldValue._type === 'arrayRemove') {\r\n result.__values__ = fieldValue._value;\r\n } else if (fieldValue._value !== undefined) {\r\n // Para increment usar __value__\r\n result.__value__ = fieldValue._value;\r\n }\r\n\r\n // Envolver en mapValue para que el backend lo reciba como objeto\r\n const fields: Record<string, FirestoreValue> = {};\r\n for (const [key, val] of Object.entries(result)) {\r\n fields[key] = toFirestoreValuePrimitive(val);\r\n }\r\n return { mapValue: { fields } };\r\n}\r\n\r\n/**\r\n * Convierte un valor primitivo a FirestoreValue (sin recursión de FieldValues)\r\n */\r\nfunction toFirestoreValuePrimitive(value: unknown): FirestoreValue {\r\n if (value === null || value === undefined) {\r\n return { nullValue: null };\r\n }\r\n if (typeof value === 'boolean') {\r\n return { booleanValue: value };\r\n }\r\n if (typeof value === 'number') {\r\n if (Number.isInteger(value)) {\r\n return { integerValue: value.toString() };\r\n }\r\n return { doubleValue: value };\r\n }\r\n if (typeof value === 'string') {\r\n return { stringValue: value };\r\n }\r\n if (Array.isArray(value)) {\r\n return {\r\n arrayValue: {\r\n values: value.map(toFirestoreValuePrimitive),\r\n },\r\n };\r\n }\r\n if (typeof value === 'object') {\r\n const fields: Record<string, FirestoreValue> = {};\r\n for (const [key, val] of Object.entries(value)) {\r\n fields[key] = toFirestoreValuePrimitive(val);\r\n }\r\n return { mapValue: { fields } };\r\n }\r\n return { nullValue: null };\r\n}\r\n\r\n/**\r\n * Convierte campos de Firestore a objeto JS\r\n */\r\nexport function fromFirestoreFields(fields: Record<string, FirestoreValue>): DocumentData {\r\n const result: DocumentData = {};\r\n for (const [key, value] of Object.entries(fields)) {\r\n result[key] = fromFirestoreValue(value);\r\n }\r\n return result;\r\n}\r\n\r\n/**\r\n * Convierte objeto JS a campos de Firestore\r\n */\r\nexport function toFirestoreFields(data: DocumentData): Record<string, FirestoreValue> {\r\n const fields: Record<string, FirestoreValue> = {};\r\n for (const [key, value] of Object.entries(data)) {\r\n if (value === undefined) {\r\n continue;\r\n }\r\n fields[key] = toFirestoreValue(value);\r\n }\r\n return fields;\r\n}\r\n\r\n// =============================================================================\r\n// TIMESTAMP HELPERS\r\n// =============================================================================\r\n\r\nfunction parseTimestamp(timestamp: string): Timestamp {\r\n const match = timestamp.match(/^(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2})(?:\\.(\\d{1,9}))?Z$/);\r\n if (!match) {\r\n const date = new Date(timestamp);\r\n return Timestamp.fromDate(date);\r\n }\r\n\r\n const base = match[1];\r\n const fraction = match[2] || '';\r\n const nanos = fraction.length > 0\r\n ? parseInt(fraction.padEnd(9, '0').slice(0, 9), 10)\r\n : 0;\r\n\r\n const seconds = Math.floor(new Date(`${base}Z`).getTime() / 1000);\r\n return new Timestamp(seconds, nanos);\r\n}\r\n\r\nfunction timestampToString(timestamp: Timestamp): string {\r\n const base = new Date(timestamp.seconds * 1000).toISOString().replace(/\\.\\d{3}Z$/, '');\r\n if (timestamp.nanoseconds === 0) {\r\n return `${base}Z`;\r\n }\r\n const nanos = String(timestamp.nanoseconds).padStart(9, '0');\r\n return `${base}.${nanos}Z`;\r\n}\r\n\r\n// =============================================================================\r\n// DOCUMENT SNAPSHOT\r\n// =============================================================================\r\n\r\n/**\r\n * Implementación de DocumentSnapshot\r\n */\r\nexport class DocumentSnapshotImpl<T = DocumentData> implements DocumentSnapshot<T> {\r\n readonly id: string;\r\n readonly ref: DocumentReference<T>;\r\n readonly metadata: SnapshotMetadata;\r\n private readonly _data: T | undefined;\r\n private readonly _exists: boolean;\r\n public readonly _updateTime: string | undefined;\r\n\r\n constructor(\r\n ref: DocumentReference<T>,\r\n data: T | undefined,\r\n exists: boolean,\r\n metadata?: Partial<SnapshotMetadata>,\r\n updateTime?: string\r\n ) {\r\n this.ref = ref;\r\n this.id = ref.id;\r\n this._data = data;\r\n this._exists = exists;\r\n this._updateTime = updateTime;\r\n this.metadata = {\r\n fromCache: metadata?.fromCache ?? false,\r\n hasPendingWrites: metadata?.hasPendingWrites ?? false,\r\n };\r\n }\r\n\r\n exists(): boolean {\r\n return this._exists;\r\n }\r\n\r\n data(): T | undefined {\r\n return this._exists ? this._data : undefined;\r\n }\r\n\r\n get(fieldPath: FieldPath): unknown {\r\n if (!this._exists || !this._data) return undefined;\r\n\r\n const data = this._data as Record<string, unknown>;\r\n const parts = fieldPath.split('.');\r\n\r\n let current: unknown = data;\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\r\n/**\r\n * Crea un DocumentSnapshot desde un documento de la API REST\r\n */\r\nexport function createDocumentSnapshot<T = DocumentData>(\r\n ref: DocumentReference<T>,\r\n doc: FirestoreDocument | null,\r\n metadata?: Partial<SnapshotMetadata>\r\n): DocumentSnapshot<T> {\r\n if (!doc) {\r\n return new DocumentSnapshotImpl<T>(ref, undefined, false, metadata);\r\n }\r\n\r\n const data = fromFirestoreFields(doc.fields || {}) as T;\r\n return new DocumentSnapshotImpl<T>(ref, data, true, metadata, doc.updateTime);\r\n}\r\n\r\n// =============================================================================\r\n// QUERY SNAPSHOT\r\n// =============================================================================\r\n\r\n/**\r\n * Implementación de QuerySnapshot\r\n */\r\nexport class QuerySnapshotImpl<T = DocumentData> implements QuerySnapshot<T> {\r\n readonly query: Query<T>;\r\n readonly docs: DocumentSnapshot<T>[];\r\n readonly metadata: SnapshotMetadata;\r\n private _docChanges: DocumentChange<T>[];\r\n\r\n constructor(\r\n query: Query<T>,\r\n docs: DocumentSnapshot<T>[],\r\n docChanges: DocumentChange<T>[],\r\n metadata?: Partial<SnapshotMetadata>\r\n ) {\r\n this.query = query;\r\n this.docs = docs;\r\n this._docChanges = docChanges;\r\n this.metadata = {\r\n fromCache: metadata?.fromCache ?? false,\r\n hasPendingWrites: metadata?.hasPendingWrites ?? false,\r\n };\r\n }\r\n\r\n get empty(): boolean {\r\n return this.docs.length === 0;\r\n }\r\n\r\n get size(): number {\r\n return this.docs.length;\r\n }\r\n\r\n forEach(callback: (doc: DocumentSnapshot<T>) => void): void {\r\n this.docs.forEach(callback);\r\n }\r\n\r\n docChanges(): DocumentChange<T>[] {\r\n return this._docChanges;\r\n }\r\n}\r\n\r\n/**\r\n * Crea un QuerySnapshot desde documentos de la API REST\r\n */\r\nexport function createQuerySnapshot<T = DocumentData>(\r\n query: Query<T>,\r\n docs: FirestoreDocument[],\r\n previousDocs: DocumentSnapshot<T>[] = [],\r\n metadata?: Partial<SnapshotMetadata>\r\n): QuerySnapshot<T> {\r\n // Crear snapshots para cada documento\r\n const snapshots: DocumentSnapshot<T>[] = docs.map((doc) => {\r\n // Extraer path y crear referencia\r\n const path = extractPathFromName(doc.name, query.firestore._projectId, query.firestore._databaseId);\r\n const ref = createRefFromPath<T>(query.firestore, path);\r\n return createDocumentSnapshot(ref, doc, metadata);\r\n });\r\n\r\n // Calcular cambios\r\n const changes = calculateDocChanges(snapshots, previousDocs);\r\n\r\n return new QuerySnapshotImpl(query, snapshots, changes, metadata);\r\n}\r\n\r\n/**\r\n * Extrae el path del documento desde el nombre completo\r\n */\r\nfunction extractPathFromName(name: string, projectId: string, databaseId: string): string {\r\n const prefix = `projects/${projectId}/databases/${databaseId}/documents/`;\r\n if (name.startsWith(prefix)) {\r\n return name.substring(prefix.length);\r\n }\r\n return name;\r\n}\r\n\r\n/**\r\n * Crea una referencia desde un path\r\n */\r\nfunction createRefFromPath<T>(firestore: import('./types').FirestoreInstance, path: string): DocumentReference<T> {\r\n // Importación circular evitada usando dynamic import pattern\r\n const segments = path.split('/');\r\n const parentPath = segments.slice(0, -1).join('/');\r\n\r\n // Crear referencias manualmente para evitar importación circular\r\n const parent = {\r\n type: 'collection' as const,\r\n path: parentPath,\r\n id: segments[segments.length - 2] || '',\r\n firestore,\r\n parent: null,\r\n };\r\n\r\n return {\r\n type: 'document' as const,\r\n path,\r\n id: segments[segments.length - 1] || '',\r\n firestore,\r\n parent,\r\n } as DocumentReference<T>;\r\n}\r\n\r\n/**\r\n * Calcula los cambios entre dos conjuntos de snapshots\r\n */\r\nfunction calculateDocChanges<T>(\r\n current: DocumentSnapshot<T>[],\r\n previous: DocumentSnapshot<T>[]\r\n): DocumentChange<T>[] {\r\n const changes: DocumentChange<T>[] = [];\r\n const previousMap = new Map(previous.map((doc, idx) => [doc.id, { doc, idx }]));\r\n\r\n // Documentos añadidos o modificados\r\n current.forEach((doc, newIndex) => {\r\n const prev = previousMap.get(doc.id);\r\n if (!prev) {\r\n // Nuevo documento\r\n changes.push({\r\n type: 'added',\r\n doc,\r\n oldIndex: -1,\r\n newIndex,\r\n });\r\n } else {\r\n // Documento existente - verificar si cambió\r\n const prevData = JSON.stringify(prev.doc.data());\r\n const currData = JSON.stringify(doc.data());\r\n if (prevData !== currData || prev.idx !== newIndex) {\r\n changes.push({\r\n type: 'modified',\r\n doc,\r\n oldIndex: prev.idx,\r\n newIndex,\r\n });\r\n }\r\n previousMap.delete(doc.id);\r\n }\r\n });\r\n\r\n // Documentos eliminados\r\n for (const [, { doc, idx }] of previousMap) {\r\n changes.push({\r\n type: 'removed',\r\n doc,\r\n oldIndex: idx,\r\n newIndex: -1,\r\n });\r\n }\r\n\r\n return changes;\r\n}\r\n"],"mappings":"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","names":[]}
@@ -0,0 +1,213 @@
1
+ // src/transport/http.ts
2
+ function createError(code, message) {
3
+ const error = new Error(message);
4
+ error.code = code;
5
+ return error;
6
+ }
7
+ function httpToFirestoreCode(status) {
8
+ switch (status) {
9
+ case 400:
10
+ return "invalid-argument";
11
+ case 401:
12
+ return "unauthenticated";
13
+ case 403:
14
+ return "permission-denied";
15
+ case 404:
16
+ return "not-found";
17
+ case 409:
18
+ return "already-exists";
19
+ case 429:
20
+ return "resource-exhausted";
21
+ case 499:
22
+ return "cancelled";
23
+ case 500:
24
+ return "internal";
25
+ case 501:
26
+ return "unimplemented";
27
+ case 503:
28
+ return "unavailable";
29
+ case 504:
30
+ return "deadline-exceeded";
31
+ default:
32
+ return "unknown";
33
+ }
34
+ }
35
+ var HttpClient = class {
36
+ constructor(firestore) {
37
+ const protocol = firestore._config.ssl ? "https" : "http";
38
+ const host = firestore._config.host;
39
+ this.baseUrl = host.startsWith("http") ? host : `${protocol}://${host}`;
40
+ this.projectId = firestore._projectId;
41
+ this.databaseId = firestore._databaseId;
42
+ this.timeout = firestore._config.timeout || 3e4;
43
+ this.maxRetries = Math.max(0, firestore._config.maxRetries ?? 2);
44
+ this.retryInitialDelayMs = Math.max(10, firestore._config.retryInitialDelayMs ?? 150);
45
+ this.retryMaxDelayMs = Math.max(this.retryInitialDelayMs, firestore._config.retryMaxDelayMs ?? 2e3);
46
+ this.firestore = firestore;
47
+ }
48
+ sleep(ms) {
49
+ return new Promise((resolve) => setTimeout(resolve, ms));
50
+ }
51
+ isRetryable(error) {
52
+ const code = error?.code;
53
+ return code === "deadline-exceeded" || code === "unavailable" || code === "resource-exhausted" || code === "aborted" || code === "internal";
54
+ }
55
+ async executeOnce(req) {
56
+ const url = `${this.getApiBase()}${req.path}`;
57
+ const authToken = await this.getAuthToken();
58
+ const headers = {
59
+ "Content-Type": "application/json",
60
+ ...req.headers
61
+ };
62
+ if (this.firestore._config.allowFlexibleQueries) {
63
+ headers["X-Firestore-Flexible-Queries"] = "true";
64
+ }
65
+ if (authToken) {
66
+ headers["Authorization"] = `Bearer ${authToken}`;
67
+ }
68
+ const controller = new AbortController();
69
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
70
+ try {
71
+ const response = await fetch(url, {
72
+ method: req.method,
73
+ headers,
74
+ body: req.body ? JSON.stringify(req.body) : void 0,
75
+ signal: controller.signal
76
+ });
77
+ clearTimeout(timeoutId);
78
+ const responseHeaders = {};
79
+ response.headers.forEach((value, key) => {
80
+ responseHeaders[key] = value;
81
+ });
82
+ if (!response.ok) {
83
+ let errorData = {};
84
+ try {
85
+ errorData = await response.json();
86
+ } catch {
87
+ }
88
+ let code = httpToFirestoreCode(response.status);
89
+ const message = errorData.error?.message || response.statusText;
90
+ if (response.status === 409) {
91
+ if (message.toLowerCase().includes("concurrent") || message.toLowerCase().includes("aborted") || message.toLowerCase().includes("conflict") || message.toLowerCase().includes("mismatch")) {
92
+ code = "aborted";
93
+ }
94
+ }
95
+ throw createError(code, message);
96
+ }
97
+ let data;
98
+ const contentType = response.headers.get("content-type");
99
+ if (contentType?.includes("application/json")) {
100
+ data = await response.json();
101
+ } else {
102
+ data = {};
103
+ }
104
+ return {
105
+ status: response.status,
106
+ data,
107
+ headers: responseHeaders
108
+ };
109
+ } catch (error) {
110
+ clearTimeout(timeoutId);
111
+ if (error instanceof Error && error.name === "AbortError") {
112
+ throw createError("deadline-exceeded", "Request timeout");
113
+ }
114
+ if (error.code) {
115
+ throw error;
116
+ }
117
+ throw createError("unavailable", `Network error: ${error.message}`);
118
+ }
119
+ }
120
+ /**
121
+ * Construye la URL base para la API
122
+ */
123
+ getApiBase() {
124
+ return `${this.baseUrl}/v1/projects/${this.projectId}/databases/${this.databaseId}`;
125
+ }
126
+ /**
127
+ * Obtiene el token de autenticación actual
128
+ * Soporta tanto token estático como provider dinámico
129
+ */
130
+ async getAuthToken() {
131
+ if ("getToken" in this.firestore && typeof this.firestore.getToken === "function") {
132
+ return await this.firestore.getToken();
133
+ }
134
+ return this.firestore._config.authToken ?? null;
135
+ }
136
+ /**
137
+ * Ejecuta un request HTTP
138
+ */
139
+ async request(req) {
140
+ let attempt = 0;
141
+ while (true) {
142
+ try {
143
+ return await this.executeOnce(req);
144
+ } catch (error) {
145
+ if (!this.isRetryable(error) || attempt >= this.maxRetries) {
146
+ throw error;
147
+ }
148
+ const delay = Math.min(
149
+ this.retryMaxDelayMs,
150
+ this.retryInitialDelayMs * 2 ** attempt
151
+ );
152
+ await this.sleep(delay);
153
+ attempt += 1;
154
+ }
155
+ }
156
+ }
157
+ /**
158
+ * GET request
159
+ */
160
+ async get(path) {
161
+ const response = await this.request({
162
+ method: "GET",
163
+ path
164
+ });
165
+ return response.data;
166
+ }
167
+ /**
168
+ * POST request
169
+ */
170
+ async post(path, body) {
171
+ const response = await this.request({
172
+ method: "POST",
173
+ path,
174
+ body
175
+ });
176
+ return response.data;
177
+ }
178
+ /**
179
+ * PATCH request
180
+ */
181
+ async patch(path, body) {
182
+ const response = await this.request({
183
+ method: "PATCH",
184
+ path,
185
+ body
186
+ });
187
+ return response.data;
188
+ }
189
+ /**
190
+ * DELETE request
191
+ */
192
+ async delete(path) {
193
+ await this.request({
194
+ method: "DELETE",
195
+ path
196
+ });
197
+ }
198
+ };
199
+ var clients = /* @__PURE__ */ new WeakMap();
200
+ function getHttpClient(firestore) {
201
+ let client = clients.get(firestore);
202
+ if (!client) {
203
+ client = new HttpClient(firestore);
204
+ clients.set(firestore, client);
205
+ }
206
+ return client;
207
+ }
208
+
209
+ export {
210
+ HttpClient,
211
+ getHttpClient
212
+ };
213
+ //# sourceMappingURL=chunk-C6SKWUQV.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/transport/http.ts"],"sourcesContent":["/**\r\n * Firestore SDK - Cliente HTTP\r\n * Comunicación con el servidor REST API\r\n */\r\n\r\nimport type {\r\n HttpRequest,\r\n HttpResponse,\r\n FirestoreInstance,\r\n FirestoreError,\r\n FirestoreErrorCode,\r\n} from '../firestore/types';\r\n\r\n/**\r\n * Crea un error de Firestore\r\n */\r\nfunction createError(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\n/**\r\n * Mapea códigos HTTP a códigos de Firestore\r\n */\r\nfunction httpToFirestoreCode(status: number): FirestoreErrorCode {\r\n switch (status) {\r\n case 400: return 'invalid-argument';\r\n case 401: return 'unauthenticated';\r\n case 403: return 'permission-denied';\r\n case 404: return 'not-found';\r\n case 409: return 'already-exists';\r\n case 429: return 'resource-exhausted';\r\n case 499: return 'cancelled';\r\n case 500: return 'internal';\r\n case 501: return 'unimplemented';\r\n case 503: return 'unavailable';\r\n case 504: return 'deadline-exceeded';\r\n default: return 'unknown';\r\n }\r\n}\r\n\r\n/**\r\n * Cliente HTTP para Firestore\r\n */\r\nexport class HttpClient {\r\n private readonly baseUrl: string;\r\n private readonly projectId: string;\r\n private readonly databaseId: string;\r\n private readonly timeout: number;\r\n private readonly maxRetries: number;\r\n private readonly retryInitialDelayMs: number;\r\n private readonly retryMaxDelayMs: number;\r\n private readonly firestore: FirestoreInstance;\r\n\r\n constructor(firestore: FirestoreInstance) {\r\n // Construir URL base con protocolo correcto\r\n const protocol = firestore._config.ssl ? 'https' : 'http';\r\n const host = firestore._config.host;\r\n this.baseUrl = host.startsWith('http') ? host : `${protocol}://${host}`;\r\n this.projectId = firestore._projectId;\r\n this.databaseId = firestore._databaseId;\r\n this.timeout = firestore._config.timeout || 30000;\r\n this.maxRetries = Math.max(0, firestore._config.maxRetries ?? 2);\r\n this.retryInitialDelayMs = Math.max(10, firestore._config.retryInitialDelayMs ?? 150);\r\n this.retryMaxDelayMs = Math.max(this.retryInitialDelayMs, firestore._config.retryMaxDelayMs ?? 2000);\r\n this.firestore = firestore;\r\n }\r\n\r\n private sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n }\r\n\r\n private isRetryable(error: unknown): boolean {\r\n const code = (error as FirestoreError | undefined)?.code;\r\n return code === 'deadline-exceeded'\r\n || code === 'unavailable'\r\n || code === 'resource-exhausted'\r\n || code === 'aborted'\r\n || code === 'internal';\r\n }\r\n\r\n private async executeOnce<T>(req: HttpRequest): Promise<HttpResponse<T>> {\r\n const url = `${this.getApiBase()}${req.path}`;\r\n\r\n // Obtener token de auth (puede ser async si hay provider)\r\n const authToken = await this.getAuthToken();\r\n\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n ...req.headers,\r\n };\r\n\r\n if (this.firestore._config.allowFlexibleQueries) {\r\n headers['X-Firestore-Flexible-Queries'] = 'true';\r\n }\r\n\r\n if (authToken) {\r\n headers['Authorization'] = `Bearer ${authToken}`;\r\n }\r\n\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\r\n\r\n try {\r\n const response = await fetch(url, {\r\n method: req.method,\r\n headers,\r\n body: req.body ? JSON.stringify(req.body) : undefined,\r\n signal: controller.signal,\r\n });\r\n\r\n clearTimeout(timeoutId);\r\n\r\n const responseHeaders: Record<string, string> = {};\r\n response.headers.forEach((value, key) => {\r\n responseHeaders[key] = value;\r\n });\r\n\r\n // Si es error, parsear y lanzar\r\n if (!response.ok) {\r\n let errorData: { error?: { message?: string } } = {};\r\n try {\r\n errorData = await response.json();\r\n } catch {\r\n // Ignorar si no es JSON\r\n }\r\n\r\n let code = httpToFirestoreCode(response.status);\r\n const message = errorData.error?.message || response.statusText;\r\n\r\n // Refinar código 409: Distinguir entre 'already-exists' y 'aborted' (concurrencia)\r\n if (response.status === 409) {\r\n if (message.toLowerCase().includes('concurrent') ||\r\n message.toLowerCase().includes('aborted') ||\r\n message.toLowerCase().includes('conflict') ||\r\n message.toLowerCase().includes('mismatch')) {\r\n code = 'aborted'; // Permite reintento en transacciones\r\n }\r\n }\r\n\r\n throw createError(code, message);\r\n }\r\n\r\n // Parsear respuesta\r\n let data: T;\r\n const contentType = response.headers.get('content-type');\r\n if (contentType?.includes('application/json')) {\r\n data = await response.json();\r\n } else {\r\n data = {} as T;\r\n }\r\n\r\n return {\r\n status: response.status,\r\n data,\r\n headers: responseHeaders,\r\n };\r\n } catch (error) {\r\n clearTimeout(timeoutId);\r\n\r\n if (error instanceof Error && error.name === 'AbortError') {\r\n throw createError('deadline-exceeded', 'Request timeout');\r\n }\r\n\r\n // Si ya es un FirestoreError, re-lanzar\r\n if ((error as FirestoreError).code) {\r\n throw error;\r\n }\r\n\r\n // Error de red\r\n throw createError('unavailable', `Network error: ${(error as Error).message}`);\r\n }\r\n }\r\n\r\n /**\r\n * Construye la URL base para la API\r\n */\r\n private getApiBase(): string {\r\n return `${this.baseUrl}/v1/projects/${this.projectId}/databases/${this.databaseId}`;\r\n }\r\n\r\n /**\r\n * Obtiene el token de autenticación actual\r\n * Soporta tanto token estático como provider dinámico\r\n */\r\n private async getAuthToken(): Promise<string | null> {\r\n // Si el firestore tiene método getToken, usarlo (obtiene token fresco)\r\n if ('getToken' in this.firestore && typeof this.firestore.getToken === 'function') {\r\n return await (this.firestore as { getToken: () => Promise<string | null> }).getToken();\r\n }\r\n // Fallback al token estático\r\n return this.firestore._config.authToken ?? null;\r\n }\r\n\r\n /**\r\n * Ejecuta un request HTTP\r\n */\r\n async request<T>(req: HttpRequest): Promise<HttpResponse<T>> {\r\n let attempt = 0;\r\n\r\n while (true) {\r\n try {\r\n return await this.executeOnce<T>(req);\r\n } catch (error) {\r\n if (!this.isRetryable(error) || attempt >= this.maxRetries) {\r\n throw error;\r\n }\r\n\r\n const delay = Math.min(\r\n this.retryMaxDelayMs,\r\n this.retryInitialDelayMs * (2 ** attempt)\r\n );\r\n\r\n await this.sleep(delay);\r\n attempt += 1;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * GET request\r\n */\r\n async get<T>(path: string): Promise<T> {\r\n const response = await this.request<T>({\r\n method: 'GET',\r\n path,\r\n });\r\n return response.data;\r\n }\r\n\r\n /**\r\n * POST request\r\n */\r\n async post<T>(path: string, body?: unknown): Promise<T> {\r\n const response = await this.request<T>({\r\n method: 'POST',\r\n path,\r\n body,\r\n });\r\n return response.data;\r\n }\r\n\r\n /**\r\n * PATCH request\r\n */\r\n async patch<T>(path: string, body?: unknown): Promise<T> {\r\n const response = await this.request<T>({\r\n method: 'PATCH',\r\n path,\r\n body,\r\n });\r\n return response.data;\r\n }\r\n\r\n /**\r\n * DELETE request\r\n */\r\n async delete(path: string): Promise<void> {\r\n await this.request<void>({\r\n method: 'DELETE',\r\n path,\r\n });\r\n }\r\n}\r\n\r\n// Cache de clientes por instancia de Firestore\r\nconst clients = new WeakMap<FirestoreInstance, HttpClient>();\r\n\r\n/**\r\n * Obtiene o crea un cliente HTTP para una instancia de Firestore\r\n */\r\nexport function getHttpClient(firestore: FirestoreInstance): HttpClient {\r\n let client = clients.get(firestore);\r\n if (!client) {\r\n client = new HttpClient(firestore);\r\n clients.set(firestore, client);\r\n }\r\n return client;\r\n}\r\n"],"mappings":"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","names":[]}