@ponceca/firestore-sdk 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -638
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @ponceca/firestore-sdk
|
|
2
2
|
|
|
3
|
-
SDK JavaScript/TypeScript 100% compatible con Firebase
|
|
3
|
+
SDK JavaScript/TypeScript 100% compatible con Firebase v9+ (API modular).
|
|
4
4
|
|
|
5
5
|
**Migrar desde Firebase es tan simple como cambiar el import.**
|
|
6
6
|
|
|
@@ -10,683 +10,107 @@ SDK JavaScript/TypeScript 100% compatible con Firebase Firestore v9+ (API modula
|
|
|
10
10
|
npm install @ponceca/firestore-sdk
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
##
|
|
14
|
-
|
|
15
|
-
Ahora puedes importar igual que Firebase:
|
|
16
|
-
|
|
17
|
-
- `@ponceca/firestore-sdk/app` (equivalente a `firebase/app`)
|
|
18
|
-
- `@ponceca/firestore-sdk/firestore` (equivalente a `firebase/firestore`)
|
|
19
|
-
|
|
20
|
-
## Configuración - Idéntica a Firebase
|
|
21
|
-
|
|
22
|
-
### Opción 1: Conectar al Emulador (Recomendado para desarrollo)
|
|
13
|
+
## Quick Start
|
|
23
14
|
|
|
24
15
|
```typescript
|
|
25
|
-
import { initializeApp } from '@ponceca/firestore-sdk
|
|
26
|
-
import {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
} from '@ponceca/firestore-sdk/firestore';
|
|
16
|
+
import { initializeApp } from '@ponceca/firestore-sdk';
|
|
17
|
+
import { getFirestore, doc, setDoc, getDoc } from '@ponceca/firestore-sdk/firestore';
|
|
18
|
+
import { getAuth, signInWithCustomToken } from '@ponceca/firestore-sdk';
|
|
19
|
+
import { getStorage, ref, uploadBytesResumable } from '@ponceca/firestore-sdk/storage';
|
|
30
20
|
|
|
31
|
-
// Inicializar
|
|
21
|
+
// 1. Inicializar
|
|
32
22
|
const app = initializeApp({
|
|
33
|
-
projectId: '
|
|
23
|
+
projectId: 'mi-proyecto',
|
|
24
|
+
baseUrl: 'http://localhost:3000', // Firestore backend
|
|
25
|
+
storageUrl: 'http://localhost:3002/api', // Storage backend
|
|
26
|
+
storageBucket: 'default',
|
|
34
27
|
});
|
|
35
28
|
|
|
36
|
-
//
|
|
29
|
+
// 2. Firestore
|
|
37
30
|
const db = getFirestore(app);
|
|
31
|
+
await setDoc(doc(db, 'users', 'u1'), { name: 'Juan', age: 28 });
|
|
32
|
+
const snap = await getDoc(doc(db, 'users', 'u1'));
|
|
33
|
+
console.log(snap.data()); // { name: 'Juan', age: 28 }
|
|
38
34
|
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
### Opción 2: Configuración en initializeFirestore
|
|
44
|
-
|
|
45
|
-
```typescript
|
|
46
|
-
import { initializeApp } from '@ponceca/firestore-sdk/app';
|
|
47
|
-
import { initializeFirestore } from '@ponceca/firestore-sdk/firestore';
|
|
48
|
-
|
|
49
|
-
const app = initializeApp({
|
|
50
|
-
projectId: 'my-project',
|
|
51
|
-
});
|
|
35
|
+
// 3. Auth
|
|
36
|
+
const auth = getAuth(app);
|
|
37
|
+
const user = await signInWithCustomToken(auth, 'eyJhbGci...');
|
|
38
|
+
console.log(user.uid);
|
|
52
39
|
|
|
53
|
-
//
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
ssl: false,
|
|
57
|
-
});
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### Perfil recomendado para producción (balanceado)
|
|
61
|
-
|
|
62
|
-
```typescript
|
|
63
|
-
import { initializeApp } from '@ponceca/firestore-sdk/app';
|
|
64
|
-
import { initializeFirestore } from '@ponceca/firestore-sdk/firestore';
|
|
65
|
-
|
|
66
|
-
const app = initializeApp({
|
|
67
|
-
projectId: 'my-project',
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
const db = initializeFirestore(app, {
|
|
71
|
-
host: 'api.my-firestore-clone.com',
|
|
72
|
-
ssl: true,
|
|
73
|
-
timeout: 12000,
|
|
74
|
-
maxRetries: 2,
|
|
75
|
-
retryInitialDelayMs: 150,
|
|
76
|
-
retryMaxDelayMs: 2000,
|
|
77
|
-
});
|
|
40
|
+
// 4. Storage
|
|
41
|
+
const storage = getStorage(app);
|
|
42
|
+
const task = uploadBytesResumable(ref(storage, 'uploads/file.pdf'), fileBlob);
|
|
78
43
|
```
|
|
79
44
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
import { initializeApp } from '@ponceca/firestore-sdk/app';
|
|
45
|
+
## Sub-entry points
|
|
84
46
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
47
|
+
| Import | Equivale a Firebase |
|
|
48
|
+
|--------|-------------------|
|
|
49
|
+
| `@ponceca/firestore-sdk` | `firebase/app` + Auth |
|
|
50
|
+
| `@ponceca/firestore-sdk/app` | `firebase/app` |
|
|
51
|
+
| `@ponceca/firestore-sdk/firestore` | `firebase/firestore` |
|
|
52
|
+
| `@ponceca/firestore-sdk/auth` | `firebase/auth` |
|
|
53
|
+
| `@ponceca/firestore-sdk/storage` | `firebase/storage` |
|
|
92
54
|
|
|
93
55
|
## Migrar desde Firebase
|
|
94
56
|
|
|
95
57
|
```diff
|
|
96
58
|
- import { initializeApp } from 'firebase/app';
|
|
97
59
|
- import { getFirestore, doc, setDoc } from 'firebase/firestore';
|
|
98
|
-
+ import { initializeApp } from '@ponceca/firestore-sdk
|
|
60
|
+
+ import { initializeApp } from '@ponceca/firestore-sdk';
|
|
99
61
|
+ import { getFirestore, doc, setDoc } from '@ponceca/firestore-sdk/firestore';
|
|
100
62
|
|
|
101
|
-
const app = initializeApp({ projectId: 'my-project' });
|
|
102
|
-
const db = getFirestore(app);
|
|
103
|
-
|
|
104
|
-
+ // Conectar a tu servidor (en desarrollo)
|
|
105
|
-
+ connectFirestoreEmulator(db, 'localhost', 3000);
|
|
106
|
-
|
|
107
|
-
// ¡El resto del código es IDÉNTICO!
|
|
108
|
-
await setDoc(doc(db, 'users', 'user1'), { name: 'Juan' });
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
## Uso Básico
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
import { initializeApp } from '@ponceca/firestore-sdk/app';
|
|
115
|
-
import {
|
|
116
|
-
getFirestore,
|
|
117
|
-
connectFirestoreEmulator,
|
|
118
|
-
collection,
|
|
119
|
-
doc,
|
|
120
|
-
getDoc,
|
|
121
|
-
setDoc,
|
|
122
|
-
updateDoc,
|
|
123
|
-
deleteDoc,
|
|
124
|
-
addDoc,
|
|
125
|
-
query,
|
|
126
|
-
where,
|
|
127
|
-
orderBy,
|
|
128
|
-
limit,
|
|
129
|
-
getDocs,
|
|
130
|
-
onSnapshot,
|
|
131
|
-
serverTimestamp,
|
|
132
|
-
} from '@ponceca/firestore-sdk/firestore';
|
|
133
|
-
|
|
134
|
-
// 1. Inicializar app
|
|
135
63
|
const app = initializeApp({
|
|
136
64
|
projectId: 'my-project',
|
|
65
|
+
+ baseUrl: 'http://localhost:3000',
|
|
137
66
|
});
|
|
138
67
|
|
|
139
|
-
//
|
|
140
|
-
const db = getFirestore(app);
|
|
141
|
-
|
|
142
|
-
// 3. Conectar al servidor (desarrollo local)
|
|
143
|
-
connectFirestoreEmulator(db, 'localhost', 3000);
|
|
144
|
-
|
|
145
|
-
// 4. Crear referencia a documento
|
|
146
|
-
const userRef = doc(db, 'users', 'user123');
|
|
147
|
-
|
|
148
|
-
// 5. Escribir documento
|
|
149
|
-
await setDoc(userRef, {
|
|
150
|
-
name: 'Juan García',
|
|
151
|
-
email: 'juan@example.com',
|
|
152
|
-
age: 25,
|
|
153
|
-
createdAt: serverTimestamp(),
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
// 6. Leer documento
|
|
157
|
-
const snapshot = await getDoc(userRef);
|
|
158
|
-
if (snapshot.exists()) {
|
|
159
|
-
console.log('Usuario:', snapshot.data());
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// 7. Actualizar documento
|
|
163
|
-
await updateDoc(userRef, {
|
|
164
|
-
age: 26,
|
|
165
|
-
'address.city': 'Madrid', // Actualización anidada
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
// 8. Eliminar documento
|
|
169
|
-
await deleteDoc(userRef);
|
|
170
|
-
|
|
171
|
-
// 9. Añadir documento con ID automático
|
|
172
|
-
const newDocRef = await addDoc(collection(db, 'posts'), {
|
|
173
|
-
title: 'Mi primer post',
|
|
174
|
-
content: 'Hola mundo!',
|
|
175
|
-
createdAt: serverTimestamp(),
|
|
176
|
-
});
|
|
177
|
-
console.log('Nuevo ID:', newDocRef.id);
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
## Autenticación
|
|
181
|
-
|
|
182
|
-
El SDK soporta múltiples formas de autenticación, idéntico a Firebase:
|
|
183
|
-
|
|
184
|
-
### Opción 1: Token Manual (Simple)
|
|
185
|
-
|
|
186
|
-
```typescript
|
|
187
|
-
import {
|
|
188
|
-
getFirestore,
|
|
189
|
-
setAuthToken,
|
|
190
|
-
getAuthToken
|
|
191
|
-
} from '@ponceca/firestore-sdk/firestore';
|
|
192
|
-
|
|
68
|
+
// ¡El resto del código es IDÉNTICO!
|
|
193
69
|
const db = getFirestore(app);
|
|
194
|
-
|
|
195
|
-
// Establecer token JWT
|
|
196
|
-
const token = await myAuthService.getToken();
|
|
197
|
-
setAuthToken(db, token);
|
|
198
|
-
|
|
199
|
-
// Verificar token actual
|
|
200
|
-
console.log('Token:', getAuthToken(db));
|
|
201
|
-
|
|
202
|
-
// Logout - eliminar token
|
|
203
|
-
setAuthToken(db, null);
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
### Opción 2: AuthTokenProvider (Automático - Recomendado)
|
|
207
|
-
|
|
208
|
-
Igual que Firebase Auth se integra automáticamente con Firestore:
|
|
209
|
-
|
|
210
|
-
```typescript
|
|
211
|
-
import {
|
|
212
|
-
initializeFirestore,
|
|
213
|
-
type AuthTokenProvider
|
|
214
|
-
} from '@ponceca/firestore-sdk/firestore';
|
|
215
|
-
|
|
216
|
-
// Crear un provider que obtiene tokens automáticamente
|
|
217
|
-
const authProvider: AuthTokenProvider = {
|
|
218
|
-
// Obtiene el token actual (llamado en cada request)
|
|
219
|
-
async getToken() {
|
|
220
|
-
const user = myAuthService.currentUser;
|
|
221
|
-
return user ? await user.getIdToken() : null;
|
|
222
|
-
},
|
|
223
|
-
|
|
224
|
-
// Opcional: escucha cambios de auth
|
|
225
|
-
onAuthStateChanged(callback) {
|
|
226
|
-
return myAuthService.onAuthStateChanged((user) => {
|
|
227
|
-
callback(user ? user.token : null);
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
// Configurar Firestore con el provider
|
|
233
|
-
const db = initializeFirestore(app, {
|
|
234
|
-
host: 'localhost:3000',
|
|
235
|
-
ssl: false,
|
|
236
|
-
authTokenProvider: authProvider
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
// Ahora cada request obtiene el token automáticamente
|
|
240
|
-
await setDoc(doc(db, 'users', 'user1'), { name: 'Juan' }); // Token incluido!
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
### Opción 3: Integración con Firebase Auth
|
|
244
|
-
|
|
245
|
-
```typescript
|
|
246
|
-
import { getAuth, onAuthStateChanged } from 'firebase/auth';
|
|
247
|
-
import { initializeFirestore, type AuthTokenProvider } from '@ponceca/firestore-sdk/firestore';
|
|
248
|
-
|
|
249
|
-
const firebaseAuth = getAuth();
|
|
250
|
-
|
|
251
|
-
// Provider que usa Firebase Auth
|
|
252
|
-
const firebaseAuthProvider: AuthTokenProvider = {
|
|
253
|
-
async getToken() {
|
|
254
|
-
const user = firebaseAuth.currentUser;
|
|
255
|
-
return user ? await user.getIdToken() : null;
|
|
256
|
-
},
|
|
257
|
-
onAuthStateChanged(callback) {
|
|
258
|
-
return onAuthStateChanged(firebaseAuth, async (user) => {
|
|
259
|
-
callback(user ? await user.getIdToken() : null);
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
const db = initializeFirestore(app, {
|
|
265
|
-
host: 'localhost:3000',
|
|
266
|
-
authTokenProvider: firebaseAuthProvider
|
|
267
|
-
});
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
### Opción 4: Mock Token para Testing
|
|
271
|
-
|
|
272
|
-
```typescript
|
|
273
|
-
connectFirestoreEmulator(db, 'localhost', 3000, {
|
|
274
|
-
mockUserToken: {
|
|
275
|
-
sub: 'user123',
|
|
276
|
-
email: 'test@example.com',
|
|
277
|
-
// Simular claims personalizados
|
|
278
|
-
admin: true
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
## Queries
|
|
284
|
-
|
|
285
|
-
```typescript
|
|
286
|
-
// Query con filtros
|
|
287
|
-
const q = query(
|
|
288
|
-
collection(db, 'users'),
|
|
289
|
-
where('age', '>=', 18),
|
|
290
|
-
where('status', '==', 'active'),
|
|
291
|
-
orderBy('age', 'desc'),
|
|
292
|
-
limit(10)
|
|
293
|
-
);
|
|
294
|
-
|
|
295
|
-
const querySnapshot = await getDocs(q);
|
|
296
|
-
querySnapshot.forEach((doc) => {
|
|
297
|
-
console.log(doc.id, '=>', doc.data());
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
// Operadores disponibles
|
|
301
|
-
where('field', '==', value); // Igual
|
|
302
|
-
where('field', '!=', value); // No igual
|
|
303
|
-
where('field', '<', value); // Menor que
|
|
304
|
-
where('field', '<=', value); // Menor o igual
|
|
305
|
-
where('field', '>', value); // Mayor que
|
|
306
|
-
where('field', '>=', value); // Mayor o igual
|
|
307
|
-
where('field', 'in', [values]); // En array
|
|
308
|
-
where('field', 'not-in', [values]); // No en array
|
|
309
|
-
where('field', 'array-contains', value); // Array contiene
|
|
310
|
-
where('field', 'array-contains-any', [values]); // Array contiene cualquiera
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
### Modo flexible (opt-in)
|
|
314
|
-
|
|
315
|
-
En Firestore original, si usas un filtro de rango (ej: `where('price', '>=', 600)`),
|
|
316
|
-
el primer `orderBy` debe ser sobre ese mismo campo. En PostgreSQL no es necesario.
|
|
317
|
-
|
|
318
|
-
Si quieres permitir queries más flexibles, activa el flag `allowFlexibleQueries`:
|
|
319
|
-
|
|
320
|
-
```typescript
|
|
321
|
-
const db = initializeFirestore(app, {
|
|
322
|
-
host: 'localhost:3000',
|
|
323
|
-
ssl: false,
|
|
324
|
-
allowFlexibleQueries: true,
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
// Ahora es válido:
|
|
328
|
-
const q = query(
|
|
329
|
-
collection(db, 'products'),
|
|
330
|
-
where('price', '>=', 600),
|
|
331
|
-
orderBy('rating', 'desc') // Ordenar por campo diferente al del filtro de rango
|
|
332
|
-
);
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
📖 Ver [documentación completa del modo flexible](docs/FLEXIBLE_QUERIES.md) con más ejemplos y casos de uso.
|
|
336
|
-
|
|
337
|
-
## Real-time Listeners
|
|
338
|
-
|
|
339
|
-
```typescript
|
|
340
|
-
// Escuchar cambios en documento
|
|
341
|
-
const unsubscribe = onSnapshot(userRef, (snapshot) => {
|
|
342
|
-
if (snapshot.exists()) {
|
|
343
|
-
console.log('Datos actualizados:', snapshot.data());
|
|
344
|
-
}
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
// Escuchar cambios en query
|
|
348
|
-
const q = query(collection(db, 'messages'), orderBy('timestamp', 'desc'), limit(50));
|
|
349
|
-
const unsubscribeQuery = onSnapshot(q, (snapshot) => {
|
|
350
|
-
snapshot.docChanges().forEach((change) => {
|
|
351
|
-
if (change.type === 'added') {
|
|
352
|
-
console.log('Nuevo mensaje:', change.doc.data());
|
|
353
|
-
}
|
|
354
|
-
if (change.type === 'modified') {
|
|
355
|
-
console.log('Mensaje modificado:', change.doc.data());
|
|
356
|
-
}
|
|
357
|
-
if (change.type === 'removed') {
|
|
358
|
-
console.log('Mensaje eliminado:', change.doc.id);
|
|
359
|
-
}
|
|
360
|
-
});
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
// Cancelar suscripción
|
|
364
|
-
unsubscribe();
|
|
365
|
-
unsubscribeQuery();
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
## Field Values Especiales
|
|
369
|
-
|
|
370
|
-
```typescript
|
|
371
|
-
import {
|
|
372
|
-
serverTimestamp,
|
|
373
|
-
increment,
|
|
374
|
-
arrayUnion,
|
|
375
|
-
arrayRemove,
|
|
376
|
-
deleteField,
|
|
377
|
-
} from '@ponceca/firestore-sdk/firestore';
|
|
378
|
-
|
|
379
|
-
// Timestamp del servidor
|
|
380
|
-
await setDoc(ref, { createdAt: serverTimestamp() });
|
|
381
|
-
|
|
382
|
-
// Incrementar valor numérico
|
|
383
|
-
await updateDoc(ref, { views: increment(1) });
|
|
384
|
-
|
|
385
|
-
// Añadir elementos a array (sin duplicados)
|
|
386
|
-
await updateDoc(ref, { tags: arrayUnion('new-tag') });
|
|
387
|
-
|
|
388
|
-
// Eliminar elementos de array
|
|
389
|
-
await updateDoc(ref, { tags: arrayRemove('old-tag') });
|
|
390
|
-
|
|
391
|
-
// Eliminar campo
|
|
392
|
-
await updateDoc(ref, { oldField: deleteField() });
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
## Batch Writes
|
|
396
|
-
|
|
397
|
-
```typescript
|
|
398
|
-
import { writeBatch } from '@ponceca/firestore-sdk/firestore';
|
|
399
|
-
|
|
400
|
-
const batch = writeBatch(db);
|
|
401
|
-
|
|
402
|
-
batch.set(doc(db, 'cities', 'NYC'), { name: 'New York' });
|
|
403
|
-
batch.update(doc(db, 'cities', 'LA'), { population: 4000000 });
|
|
404
|
-
batch.delete(doc(db, 'cities', 'OLD'));
|
|
405
|
-
|
|
406
|
-
await batch.commit();
|
|
70
|
+
await setDoc(doc(db, 'users', 'u1'), { name: 'Juan' });
|
|
407
71
|
```
|
|
408
72
|
|
|
409
|
-
##
|
|
73
|
+
## Configuración por Entorno
|
|
410
74
|
|
|
411
75
|
```typescript
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
const newBalance = await runTransaction(db, async (transaction) => {
|
|
415
|
-
const accountRef = doc(db, 'accounts', 'account1');
|
|
416
|
-
const accountDoc = await transaction.get(accountRef);
|
|
417
|
-
|
|
418
|
-
if (!accountDoc.exists()) {
|
|
419
|
-
throw new Error('Account does not exist');
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
const currentBalance = accountDoc.data()!.balance;
|
|
423
|
-
const newBalance = currentBalance - 100;
|
|
76
|
+
// Desarrollo (usa defaults: localhost:3000)
|
|
77
|
+
initializeApp({ projectId: 'dev-project' });
|
|
424
78
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
79
|
+
// Producción
|
|
80
|
+
initializeApp({
|
|
81
|
+
projectId: 'prod-project',
|
|
82
|
+
apiKey: 'mi-api-key',
|
|
83
|
+
baseUrl: 'https://firestore.midominio.com',
|
|
84
|
+
storageUrl: 'https://storage.midominio.com/api',
|
|
85
|
+
storageBucket: 'prod-bucket',
|
|
431
86
|
});
|
|
432
87
|
```
|
|
433
88
|
|
|
434
|
-
##
|
|
89
|
+
## Features
|
|
435
90
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
91
|
+
- ✅ **CRUD completo**: `getDoc`, `setDoc`, `updateDoc`, `deleteDoc`, `addDoc`
|
|
92
|
+
- ✅ **Queries**: `where`, `orderBy`, `limit`, `startAt/After`, `endAt/Before`, `and`, `or`
|
|
93
|
+
- ✅ **Real-time**: `onSnapshot` con `docChanges()`
|
|
94
|
+
- ✅ **Auth**: `signInWithCustomToken`, `signOut`, `onAuthStateChanged`, persistencia
|
|
95
|
+
- ✅ **Storage**: Upload resumable, `getDownloadURL`, `listAll`, metadata
|
|
96
|
+
- ✅ **Batch & Transactions**: Operaciones atómicas
|
|
97
|
+
- ✅ **Agregaciones**: `count()`, `sum()`, `average()`
|
|
98
|
+
- ✅ **Tipos especiales**: `Timestamp`, `GeoPoint`, `Bytes`, `VectorValue`
|
|
99
|
+
- ✅ **Búsqueda avanzada**: `fastSearch`, `whereFuzzy`, `whereSearch`
|
|
100
|
+
- ✅ **Field Values**: `serverTimestamp()`, `increment()`, `arrayUnion/Remove`
|
|
439
101
|
|
|
440
|
-
|
|
441
|
-
const userRef = doc(db, 'users', 'user123');
|
|
442
|
-
const postsRef2 = collection(userRef, 'posts');
|
|
102
|
+
## Documentación Completa
|
|
443
103
|
|
|
444
|
-
|
|
445
|
-
await addDoc(postsRef, {
|
|
446
|
-
title: 'Mi post',
|
|
447
|
-
content: 'Contenido...',
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
// Query en subcolección
|
|
451
|
-
const recentPosts = await getDocs(
|
|
452
|
-
query(postsRef, orderBy('createdAt', 'desc'), limit(5))
|
|
453
|
-
);
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
## Tipos TypeScript
|
|
457
|
-
|
|
458
|
-
El SDK incluye tipos completos para TypeScript:
|
|
459
|
-
|
|
460
|
-
```typescript
|
|
461
|
-
import type {
|
|
462
|
-
DocumentData,
|
|
463
|
-
DocumentReference,
|
|
464
|
-
CollectionReference,
|
|
465
|
-
Query,
|
|
466
|
-
DocumentSnapshot,
|
|
467
|
-
QuerySnapshot,
|
|
468
|
-
Firestore,
|
|
469
|
-
FieldValue,
|
|
470
|
-
} from '@ponceca/firestore-sdk/firestore';
|
|
104
|
+
📘 Ver [`sdk/DOCUMENTACION.md`](https://github.com/ponceca/firestore/blob/main/sdk/DOCUMENTACION.md) para la guía completa incluyendo: configuración del backend, variables de entorno, Angular SDK, seguridad JWT, y despliegue en producción.
|
|
471
105
|
|
|
472
|
-
|
|
473
|
-
interface User {
|
|
474
|
-
name: string;
|
|
475
|
-
email: string;
|
|
476
|
-
age: number;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
const userRef = doc(db, 'users', 'user123') as DocumentReference<User>;
|
|
480
|
-
const snapshot = await getDoc(userRef);
|
|
481
|
-
|
|
482
|
-
if (snapshot.exists()) {
|
|
483
|
-
const user: User = snapshot.data()!;
|
|
484
|
-
console.log(user.name); // TypeScript conoce el tipo
|
|
485
|
-
}
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
## Compatibilidad con Firebase
|
|
489
|
-
|
|
490
|
-
Este SDK es un drop-in replacement para Firebase Firestore v9. Para migrar:
|
|
491
|
-
|
|
492
|
-
```typescript
|
|
493
|
-
// Antes (Firebase)
|
|
494
|
-
import { initializeApp } from 'firebase/app';
|
|
495
|
-
import { getFirestore, doc, getDoc } from 'firebase/firestore';
|
|
496
|
-
|
|
497
|
-
// Después (Este SDK)
|
|
498
|
-
import { initializeApp } from '@ponceca/firestore-sdk/app';
|
|
499
|
-
import { getFirestore, doc, getDoc } from '@ponceca/firestore-sdk/firestore';
|
|
500
|
-
```
|
|
501
|
-
|
|
502
|
-
## Queries Avanzadas
|
|
503
|
-
|
|
504
|
-
```typescript
|
|
505
|
-
import { and, or, collectionGroup } from '@ponceca/firestore-sdk/firestore';
|
|
106
|
+
## Para Angular
|
|
506
107
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
collection(db, 'products'),
|
|
510
|
-
and(
|
|
511
|
-
where('category', '==', 'electronics'),
|
|
512
|
-
or(
|
|
513
|
-
where('price', '<', 100),
|
|
514
|
-
where('onSale', '==', true)
|
|
515
|
-
)
|
|
516
|
-
)
|
|
517
|
-
);
|
|
518
|
-
|
|
519
|
-
// Collection Group Query (buscar en todas las subcolecciones con mismo ID)
|
|
520
|
-
const allComments = query(
|
|
521
|
-
collectionGroup(db, 'comments'),
|
|
522
|
-
where('author', '==', 'john'),
|
|
523
|
-
orderBy('createdAt', 'desc'),
|
|
524
|
-
limit(10)
|
|
525
|
-
);
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
## Agregaciones
|
|
529
|
-
|
|
530
|
-
```typescript
|
|
531
|
-
import { getCountFromServer, getAggregateFromServer, count, sum, average } from '@ponceca/firestore-sdk/firestore';
|
|
532
|
-
|
|
533
|
-
// Contar documentos
|
|
534
|
-
const countSnapshot = await getCountFromServer(query(collection(db, 'users')));
|
|
535
|
-
console.log('Total users:', countSnapshot.data().count);
|
|
536
|
-
|
|
537
|
-
// Múltiples agregaciones
|
|
538
|
-
const snapshot = await getAggregateFromServer(
|
|
539
|
-
query(collection(db, 'orders'), where('status', '==', 'completed')),
|
|
540
|
-
{
|
|
541
|
-
totalOrders: count(),
|
|
542
|
-
totalRevenue: sum('amount'),
|
|
543
|
-
avgOrderValue: average('amount')
|
|
544
|
-
}
|
|
545
|
-
);
|
|
546
|
-
|
|
547
|
-
console.log('Total orders:', snapshot.data().totalOrders);
|
|
548
|
-
console.log('Total revenue:', snapshot.data().totalRevenue);
|
|
549
|
-
console.log('Average order:', snapshot.data().avgOrderValue);
|
|
550
|
-
```
|
|
551
|
-
|
|
552
|
-
## Tipos Especiales
|
|
553
|
-
|
|
554
|
-
```typescript
|
|
555
|
-
import { Timestamp, GeoPoint, Bytes, FieldPath, documentId, vector } from '@ponceca/firestore-sdk/firestore';
|
|
556
|
-
|
|
557
|
-
// Timestamp
|
|
558
|
-
const ts = Timestamp.now();
|
|
559
|
-
const tsFromDate = Timestamp.fromDate(new Date());
|
|
560
|
-
console.log(ts.toDate());
|
|
561
|
-
|
|
562
|
-
// GeoPoint
|
|
563
|
-
const location = new GeoPoint(37.7749, -122.4194);
|
|
564
|
-
console.log(location.latitude, location.longitude);
|
|
565
|
-
|
|
566
|
-
// Bytes
|
|
567
|
-
const bytes = Bytes.fromBase64String('SGVsbG8gV29ybGQ=');
|
|
568
|
-
console.log(bytes.toUint8Array());
|
|
569
|
-
|
|
570
|
-
// FieldPath para campos anidados
|
|
571
|
-
const path = new FieldPath('user', 'address', 'city');
|
|
572
|
-
|
|
573
|
-
// documentId() para filtrar por ID de documento
|
|
574
|
-
const q = query(
|
|
575
|
-
collection(db, 'users'),
|
|
576
|
-
where(documentId(), 'in', ['user1', 'user2', 'user3'])
|
|
577
|
-
);
|
|
578
|
-
|
|
579
|
-
// Vector para embeddings/ML
|
|
580
|
-
const embedding = vector([0.1, 0.2, 0.3, 0.4]);
|
|
581
|
-
await setDoc(docRef, { embedding });
|
|
582
|
-
```
|
|
583
|
-
|
|
584
|
-
## Utilidades
|
|
585
|
-
|
|
586
|
-
```typescript
|
|
587
|
-
import { refEqual, queryEqual, snapshotEqual, onSnapshotsInSync } from '@ponceca/firestore-sdk/firestore';
|
|
588
|
-
|
|
589
|
-
// Comparar referencias
|
|
590
|
-
const ref1 = doc(db, 'users', '123');
|
|
591
|
-
const ref2 = doc(db, 'users', '123');
|
|
592
|
-
console.log(refEqual(ref1, ref2)); // true
|
|
593
|
-
|
|
594
|
-
// Comparar queries
|
|
595
|
-
const q1 = query(collection(db, 'users'), where('age', '>=', 18));
|
|
596
|
-
const q2 = query(collection(db, 'users'), where('age', '>=', 18));
|
|
597
|
-
console.log(queryEqual(q1, q2)); // true
|
|
598
|
-
|
|
599
|
-
// Notificación cuando todos los listeners están sincronizados
|
|
600
|
-
const unsubSync = onSnapshotsInSync(db, () => {
|
|
601
|
-
console.log('All snapshots are in sync');
|
|
602
|
-
});
|
|
108
|
+
```bash
|
|
109
|
+
npm install @ponceca/firestore-sdk @ponceca/firestore-angular
|
|
603
110
|
```
|
|
604
111
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
### App
|
|
608
|
-
|
|
609
|
-
- `initializeApp(options, name?)` - Inicializa la app
|
|
610
|
-
- `getApp(name?)` - Obtiene app existente
|
|
611
|
-
- `getApps()` - Lista todas las apps
|
|
612
|
-
- `deleteApp(app)` - Elimina una app
|
|
613
|
-
|
|
614
|
-
### Firestore
|
|
615
|
-
|
|
616
|
-
- `getFirestore(app?)` - Obtiene instancia de Firestore
|
|
617
|
-
- `initializeFirestore(app, settings)` - Inicializa con configuración
|
|
618
|
-
- `terminate(firestore)` - Termina instancia
|
|
619
|
-
|
|
620
|
-
### Referencias
|
|
621
|
-
|
|
622
|
-
- `doc(firestore, path, ...segments)` - Referencia a documento
|
|
623
|
-
- `collection(firestore, path, ...segments)` - Referencia a colección
|
|
624
|
-
- `collectionGroup(firestore, collectionId)` - Query sobre grupo de colecciones
|
|
625
|
-
|
|
626
|
-
### CRUD
|
|
627
|
-
|
|
628
|
-
- `getDoc(ref)` - Lee documento
|
|
629
|
-
- `setDoc(ref, data, options?)` - Escribe documento
|
|
630
|
-
- `updateDoc(ref, data)` - Actualiza campos
|
|
631
|
-
- `deleteDoc(ref)` - Elimina documento
|
|
632
|
-
- `addDoc(collectionRef, data)` - Añade con ID auto
|
|
633
|
-
|
|
634
|
-
### Queries
|
|
635
|
-
|
|
636
|
-
- `query(ref, ...constraints)` - Crea query
|
|
637
|
-
- `where(field, op, value)` - Filtro
|
|
638
|
-
- `orderBy(field, direction?)` - Ordenar
|
|
639
|
-
- `limit(n)` - Limitar resultados
|
|
640
|
-
- `limitToLast(n)` - Últimos N
|
|
641
|
-
- `startAt(...values)` - Cursor inicio inclusivo
|
|
642
|
-
- `startAfter(...values)` - Cursor inicio exclusivo
|
|
643
|
-
- `endAt(...values)` - Cursor fin inclusivo
|
|
644
|
-
- `endBefore(...values)` - Cursor fin exclusivo
|
|
645
|
-
- `and(...constraints)` - Filtro AND compuesto
|
|
646
|
-
- `or(...constraints)` - Filtro OR compuesto
|
|
647
|
-
- `getDocs(query)` - Ejecuta query
|
|
648
|
-
|
|
649
|
-
### Real-time
|
|
650
|
-
|
|
651
|
-
- `onSnapshot(ref, onNext, onError?)` - Suscribe a cambios
|
|
652
|
-
- `onSnapshotsInSync(firestore, callback)` - Sincronización de listeners
|
|
653
|
-
|
|
654
|
-
### Agregaciones
|
|
655
|
-
|
|
656
|
-
- `getCountFromServer(query)` - Cuenta documentos
|
|
657
|
-
- `getAggregateFromServer(query, spec)` - Múltiples agregaciones
|
|
658
|
-
- `count()` - Campo de conteo
|
|
659
|
-
- `sum(field)` - Campo de suma
|
|
660
|
-
- `average(field)` - Campo de promedio
|
|
661
|
-
|
|
662
|
-
### Field Values
|
|
663
|
-
|
|
664
|
-
- `serverTimestamp()` - Timestamp del servidor
|
|
665
|
-
- `increment(n)` - Incrementar número
|
|
666
|
-
- `arrayUnion(...values)` - Añadir a array
|
|
667
|
-
- `arrayRemove(...values)` - Eliminar de array
|
|
668
|
-
- `deleteField()` - Eliminar campo
|
|
669
|
-
|
|
670
|
-
### Tipos Especiales
|
|
671
|
-
|
|
672
|
-
- `Timestamp` - Clase para timestamps
|
|
673
|
-
- `GeoPoint` - Clase para coordenadas
|
|
674
|
-
- `Bytes` - Clase para datos binarios
|
|
675
|
-
- `FieldPath` - Clase para paths de campos
|
|
676
|
-
- `VectorValue` / `vector()` - Para embeddings/ML
|
|
677
|
-
- `documentId()` - Referencia al ID de documento
|
|
678
|
-
|
|
679
|
-
### Batch & Transactions
|
|
680
|
-
|
|
681
|
-
- `writeBatch(firestore)` - Crea batch
|
|
682
|
-
- `runTransaction(firestore, fn, options?)` - Ejecuta transacción
|
|
683
|
-
|
|
684
|
-
### Utilidades
|
|
685
|
-
|
|
686
|
-
- `refEqual(ref1, ref2)` - Compara referencias
|
|
687
|
-
- `queryEqual(q1, q2)` - Compara queries
|
|
688
|
-
- `snapshotEqual(s1, s2)` - Compara snapshots
|
|
112
|
+
Ver [@ponceca/firestore-angular](https://www.npmjs.com/package/@ponceca/firestore-angular) para la documentación del adapter Angular con Signals, Observables, Guards y SSR.
|
|
689
113
|
|
|
690
114
|
## Licencia
|
|
691
115
|
|
|
692
|
-
MIT
|
|
116
|
+
MIT — © 2026 ponceca
|