UNPKG

25.7 kBJavaScriptView Raw
1import * as i0 from '@angular/core';
2import { InjectionToken, Injectable, Inject, Optional, PLATFORM_ID, NgZone, NgModule } from '@angular/core';
3import { asyncScheduler, Observable, from, of } from 'rxjs';
4import { startWith, pairwise, map, scan, distinctUntilChanged, filter, observeOn } from 'rxjs/operators';
5import * as i1 from '@angular/fire';
6import { ɵAngularFireSchedulers, ɵkeepUnstableUntilFirstFactory, ɵfirebaseAppFactory, ɵlogAuthEmulatorError, ɵfetchInstance, FIREBASE_OPTIONS, FIREBASE_APP_NAME } from '@angular/fire';
7import { isPlatformServer } from '@angular/common';
8import firebase from 'firebase/app';
9import 'firebase/firestore';
10import * as i2 from '@angular/fire/auth';
11import { USE_EMULATOR as USE_EMULATOR$1 } from '@angular/fire/auth';
12
13function _fromRef(ref, scheduler = asyncScheduler) {
14 return new Observable(subscriber => {
15 let unsubscribe;
16 if (scheduler != null) {
17 scheduler.schedule(() => {
18 unsubscribe = ref.onSnapshot({ includeMetadataChanges: true }, subscriber);
19 });
20 }
21 else {
22 unsubscribe = ref.onSnapshot({ includeMetadataChanges: true }, subscriber);
23 }
24 return () => {
25 if (unsubscribe != null) {
26 unsubscribe();
27 }
28 };
29 });
30}
31function fromRef(ref, scheduler) {
32 return _fromRef(ref, scheduler);
33}
34function fromDocRef(ref, scheduler) {
35 return fromRef(ref, scheduler)
36 .pipe(startWith(undefined), pairwise(), map(([priorPayload, payload]) => {
37 if (!payload.exists) {
38 return { payload, type: 'removed' };
39 }
40 if (!(priorPayload === null || priorPayload === void 0 ? void 0 : priorPayload.exists)) {
41 return { payload, type: 'added' };
42 }
43 return { payload, type: 'modified' };
44 }));
45}
46function fromCollectionRef(ref, scheduler) {
47 return fromRef(ref, scheduler).pipe(map(payload => ({ payload, type: 'query' })));
48}
49
50/**
51 * Return a stream of document changes on a query. These results are not in sort order but in
52 * order of occurence.
53 */
54function docChanges(query, scheduler) {
55 return fromCollectionRef(query, scheduler)
56 .pipe(startWith(undefined), pairwise(), map(([priorAction, action]) => {
57 const docChanges = action.payload.docChanges();
58 const actions = docChanges.map(change => ({ type: change.type, payload: change }));
59 // the metadata has changed from the prior emission
60 if (priorAction && JSON.stringify(priorAction.payload.metadata) !== JSON.stringify(action.payload.metadata)) {
61 // go through all the docs in payload and figure out which ones changed
62 action.payload.docs.forEach((currentDoc, currentIndex) => {
63 const docChange = docChanges.find(d => d.doc.ref.isEqual(currentDoc.ref));
64 const priorDoc = priorAction === null || priorAction === void 0 ? void 0 : priorAction.payload.docs.find(d => d.ref.isEqual(currentDoc.ref));
65 if (docChange && JSON.stringify(docChange.doc.metadata) === JSON.stringify(currentDoc.metadata) ||
66 !docChange && priorDoc && JSON.stringify(priorDoc.metadata) === JSON.stringify(currentDoc.metadata)) {
67 // document doesn't appear to have changed, don't log another action
68 }
69 else {
70 // since the actions are processed in order just push onto the array
71 actions.push({
72 type: 'modified',
73 payload: {
74 oldIndex: currentIndex,
75 newIndex: currentIndex,
76 type: 'modified',
77 doc: currentDoc
78 }
79 });
80 }
81 });
82 }
83 return actions;
84 }));
85}
86/**
87 * Return a stream of document changes on a query. These results are in sort order.
88 */
89function sortedChanges(query, events, scheduler) {
90 return docChanges(query, scheduler)
91 .pipe(scan((current, changes) => combineChanges(current, changes.map(it => it.payload), events), []), distinctUntilChanged(), // cut down on unneed change cycles
92 map(changes => changes.map(c => ({ type: c.type, payload: c }))));
93}
94/**
95 * Combines the total result set from the current set of changes from an incoming set
96 * of changes.
97 */
98function combineChanges(current, changes, events) {
99 changes.forEach(change => {
100 // skip unwanted change types
101 if (events.indexOf(change.type) > -1) {
102 current = combineChange(current, change);
103 }
104 });
105 return current;
106}
107/**
108 * Splice arguments on top of a sliced array, to break top-level ===
109 * this is useful for change-detection
110 */
111function sliceAndSplice(original, start, deleteCount, ...args) {
112 const returnArray = original.slice();
113 returnArray.splice(start, deleteCount, ...args);
114 return returnArray;
115}
116/**
117 * Creates a new sorted array from a new change.
118 * Build our own because we allow filtering of action types ('added', 'removed', 'modified') before scanning
119 * and so we have greater control over change detection (by breaking ===)
120 */
121function combineChange(combined, change) {
122 switch (change.type) {
123 case 'added':
124 if (combined[change.newIndex] && combined[change.newIndex].doc.ref.isEqual(change.doc.ref)) {
125 // Not sure why the duplicates are getting fired
126 }
127 else {
128 return sliceAndSplice(combined, change.newIndex, 0, change);
129 }
130 break;
131 case 'modified':
132 if (combined[change.oldIndex] == null || combined[change.oldIndex].doc.ref.isEqual(change.doc.ref)) {
133 // When an item changes position we first remove it
134 // and then add it's new position
135 if (change.oldIndex !== change.newIndex) {
136 const copiedArray = combined.slice();
137 copiedArray.splice(change.oldIndex, 1);
138 copiedArray.splice(change.newIndex, 0, change);
139 return copiedArray;
140 }
141 else {
142 return sliceAndSplice(combined, change.newIndex, 1, change);
143 }
144 }
145 break;
146 case 'removed':
147 if (combined[change.oldIndex] && combined[change.oldIndex].doc.ref.isEqual(change.doc.ref)) {
148 return sliceAndSplice(combined, change.oldIndex, 1);
149 }
150 break;
151 }
152 return combined;
153}
154
155function validateEventsArray(events) {
156 if (!events || events.length === 0) {
157 events = ['added', 'removed', 'modified'];
158 }
159 return events;
160}
161/**
162 * AngularFirestoreCollection service
163 *
164 * This class creates a reference to a Firestore Collection. A reference and a query are provided in
165 * in the constructor. The query can be the unqueried reference if no query is desired.The class
166 * is generic which gives you type safety for data update methods and data streaming.
167 *
168 * This class uses Symbol.observable to transform into Observable using Observable.from().
169 *
170 * This class is rarely used directly and should be created from the AngularFirestore service.
171 *
172 * Example:
173 *
174 * const collectionRef = firebase.firestore.collection('stocks');
175 * const query = collectionRef.where('price', '>', '0.01');
176 * const fakeStock = new AngularFirestoreCollection<Stock>(collectionRef, query);
177 *
178 * // NOTE!: the updates are performed on the reference not the query
179 * await fakeStock.add({ name: 'FAKE', price: 0.01 });
180 *
181 * // Subscribe to changes as snapshots. This provides you data updates as well as delta updates.
182 * fakeStock.valueChanges().subscribe(value => console.log(value));
183 */
184class AngularFirestoreCollection {
185 /**
186 * The constructor takes in a CollectionReference and Query to provide wrapper methods
187 * for data operations and data streaming.
188 *
189 * Note: Data operation methods are done on the reference not the query. This means
190 * when you update data it is not updating data to the window of your query unless
191 * the data fits the criteria of the query. See the AssociatedRefence type for details
192 * on this implication.
193 */
194 constructor(ref, query, afs) {
195 this.ref = ref;
196 this.query = query;
197 this.afs = afs;
198 }
199 /**
200 * Listen to the latest change in the stream. This method returns changes
201 * as they occur and they are not sorted by query order. This allows you to construct
202 * your own data structure.
203 */
204 stateChanges(events) {
205 let source = docChanges(this.query, this.afs.schedulers.outsideAngular);
206 if (events && events.length > 0) {
207 source = source.pipe(map(actions => actions.filter(change => events.indexOf(change.type) > -1)));
208 }
209 return source.pipe(
210 // We want to filter out empty arrays, but always emit at first, so the developer knows
211 // that the collection has been resolve; even if it's empty
212 startWith(undefined), pairwise(), filter(([prior, current]) => current.length > 0 || !prior), map(([prior, current]) => current), this.afs.keepUnstableUntilFirst);
213 }
214 /**
215 * Create a stream of changes as they occur it time. This method is similar to stateChanges()
216 * but it collects each event in an array over time.
217 */
218 auditTrail(events) {
219 return this.stateChanges(events).pipe(scan((current, action) => [...current, ...action], []));
220 }
221 /**
222 * Create a stream of synchronized changes. This method keeps the local array in sorted
223 * query order.
224 */
225 snapshotChanges(events) {
226 const validatedEvents = validateEventsArray(events);
227 const scheduledSortedChanges$ = sortedChanges(this.query, validatedEvents, this.afs.schedulers.outsideAngular);
228 return scheduledSortedChanges$.pipe(this.afs.keepUnstableUntilFirst);
229 }
230 valueChanges(options = {}) {
231 return fromCollectionRef(this.query, this.afs.schedulers.outsideAngular)
232 .pipe(map(actions => actions.payload.docs.map(a => {
233 if (options.idField) {
234 return Object.assign(Object.assign({}, a.data()), { [options.idField]: a.id });
235 }
236 else {
237 return a.data();
238 }
239 })), this.afs.keepUnstableUntilFirst);
240 }
241 /**
242 * Retrieve the results of the query once.
243 */
244 get(options) {
245 return from(this.query.get(options)).pipe(observeOn(this.afs.schedulers.insideAngular));
246 }
247 /**
248 * Add data to a collection reference.
249 *
250 * Note: Data operation methods are done on the reference not the query. This means
251 * when you update data it is not updating data to the window of your query unless
252 * the data fits the criteria of the query.
253 */
254 add(data) {
255 return this.ref.add(data);
256 }
257 /**
258 * Create a reference to a single document in a collection.
259 */
260 doc(path) {
261 // TODO is there a better way to solve this type issue
262 return new AngularFirestoreDocument(this.ref.doc(path), this.afs);
263 }
264}
265
266/**
267 * AngularFirestoreDocument service
268 *
269 * This class creates a reference to a Firestore Document. A reference is provided in
270 * in the constructor. The class is generic which gives you type safety for data update
271 * methods and data streaming.
272 *
273 * This class uses Symbol.observable to transform into Observable using Observable.from().
274 *
275 * This class is rarely used directly and should be created from the AngularFirestore service.
276 *
277 * Example:
278 *
279 * const fakeStock = new AngularFirestoreDocument<Stock>(doc('stocks/FAKE'));
280 * await fakeStock.set({ name: 'FAKE', price: 0.01 });
281 * fakeStock.valueChanges().map(snap => {
282 * if(snap.exists) return snap.data();
283 * return null;
284 * }).subscribe(value => console.log(value));
285 * // OR! Transform using Observable.from() and the data is unwrapped for you
286 * Observable.from(fakeStock).subscribe(value => console.log(value));
287 */
288class AngularFirestoreDocument {
289 /**
290 * The constructor takes in a DocumentReference to provide wrapper methods
291 * for data operations, data streaming, and Symbol.observable.
292 */
293 constructor(ref, afs) {
294 this.ref = ref;
295 this.afs = afs;
296 }
297 /**
298 * Create or overwrite a single document.
299 */
300 set(data, options) {
301 return this.ref.set(data, options);
302 }
303 /**
304 * Update some fields of a document without overwriting the entire document.
305 */
306 update(data) {
307 return this.ref.update(data);
308 }
309 /**
310 * Delete a document.
311 */
312 delete() {
313 return this.ref.delete();
314 }
315 /**
316 * Create a reference to a sub-collection given a path and an optional query
317 * function.
318 */
319 collection(path, queryFn) {
320 const collectionRef = this.ref.collection(path);
321 const { ref, query } = associateQuery(collectionRef, queryFn);
322 return new AngularFirestoreCollection(ref, query, this.afs);
323 }
324 /**
325 * Listen to snapshot updates from the document.
326 */
327 snapshotChanges() {
328 const scheduledFromDocRef$ = fromDocRef(this.ref, this.afs.schedulers.outsideAngular);
329 return scheduledFromDocRef$.pipe(this.afs.keepUnstableUntilFirst);
330 }
331 valueChanges(options = {}) {
332 return this.snapshotChanges().pipe(map(({ payload }) => options.idField ? Object.assign(Object.assign({}, payload.data()), { [options.idField]: payload.id }) : payload.data()));
333 }
334 /**
335 * Retrieve the document once.
336 */
337 get(options) {
338 return from(this.ref.get(options)).pipe(observeOn(this.afs.schedulers.insideAngular));
339 }
340}
341
342/**
343 * AngularFirestoreCollectionGroup service
344 *
345 * This class holds a reference to a Firestore Collection Group Query.
346 *
347 * This class uses Symbol.observable to transform into Observable using Observable.from().
348 *
349 * This class is rarely used directly and should be created from the AngularFirestore service.
350 *
351 * Example:
352 *
353 * const collectionGroup = firebase.firestore.collectionGroup('stocks');
354 * const query = collectionRef.where('price', '>', '0.01');
355 * const fakeStock = new AngularFirestoreCollectionGroup<Stock>(query, afs);
356 *
357 * // Subscribe to changes as snapshots. This provides you data updates as well as delta updates.
358 * fakeStock.valueChanges().subscribe(value => console.log(value));
359 */
360class AngularFirestoreCollectionGroup {
361 /**
362 * The constructor takes in a CollectionGroupQuery to provide wrapper methods
363 * for data operations and data streaming.
364 */
365 constructor(query, afs) {
366 this.query = query;
367 this.afs = afs;
368 }
369 /**
370 * Listen to the latest change in the stream. This method returns changes
371 * as they occur and they are not sorted by query order. This allows you to construct
372 * your own data structure.
373 */
374 stateChanges(events) {
375 if (!events || events.length === 0) {
376 return docChanges(this.query, this.afs.schedulers.outsideAngular).pipe(this.afs.keepUnstableUntilFirst);
377 }
378 return docChanges(this.query, this.afs.schedulers.outsideAngular)
379 .pipe(map(actions => actions.filter(change => events.indexOf(change.type) > -1)), filter(changes => changes.length > 0), this.afs.keepUnstableUntilFirst);
380 }
381 /**
382 * Create a stream of changes as they occur it time. This method is similar to stateChanges()
383 * but it collects each event in an array over time.
384 */
385 auditTrail(events) {
386 return this.stateChanges(events).pipe(scan((current, action) => [...current, ...action], []));
387 }
388 /**
389 * Create a stream of synchronized changes. This method keeps the local array in sorted
390 * query order.
391 */
392 snapshotChanges(events) {
393 const validatedEvents = validateEventsArray(events);
394 const scheduledSortedChanges$ = sortedChanges(this.query, validatedEvents, this.afs.schedulers.outsideAngular);
395 return scheduledSortedChanges$.pipe(this.afs.keepUnstableUntilFirst);
396 }
397 valueChanges(options = {}) {
398 const fromCollectionRefScheduled$ = fromCollectionRef(this.query, this.afs.schedulers.outsideAngular);
399 return fromCollectionRefScheduled$
400 .pipe(map(actions => actions.payload.docs.map(a => {
401 if (options.idField) {
402 return Object.assign({ [options.idField]: a.id }, a.data());
403 }
404 else {
405 return a.data();
406 }
407 })), this.afs.keepUnstableUntilFirst);
408 }
409 /**
410 * Retrieve the results of the query once.
411 */
412 get(options) {
413 return from(this.query.get(options)).pipe(observeOn(this.afs.schedulers.insideAngular));
414 }
415}
416
417/**
418 * The value of this token determines whether or not the firestore will have persistance enabled
419 */
420const ENABLE_PERSISTENCE = new InjectionToken('angularfire2.enableFirestorePersistence');
421const PERSISTENCE_SETTINGS = new InjectionToken('angularfire2.firestore.persistenceSettings');
422const SETTINGS = new InjectionToken('angularfire2.firestore.settings');
423const USE_EMULATOR = new InjectionToken('angularfire2.firestore.use-emulator');
424/**
425 * A utility methods for associating a collection reference with
426 * a query.
427 *
428 * @param collectionRef - A collection reference to query
429 * @param queryFn - The callback to create a query
430 *
431 * Example:
432 * const { query, ref } = associateQuery(docRef.collection('items'), ref => {
433 * return ref.where('age', '<', 200);
434 * });
435 */
436function associateQuery(collectionRef, queryFn = ref => ref) {
437 const query = queryFn(collectionRef);
438 const ref = collectionRef;
439 return { query, ref };
440}
441/**
442 * AngularFirestore Service
443 *
444 * This service is the main entry point for this feature module. It provides
445 * an API for creating Collection and Reference services. These services can
446 * then be used to do data updates and observable streams of the data.
447 *
448 * Example:
449 *
450 * import { Component } from '@angular/core';
451 * import { AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument } from '@angular/fire/firestore';
452 * import { Observable } from 'rxjs/Observable';
453 * import { from } from 'rxjs/observable';
454 *
455 * @Component({
456 * selector: 'app-my-component',
457 * template: `
458 * <h2>Items for {{ (profile | async)?.name }}
459 * <ul>
460 * <li *ngFor="let item of items | async">{{ item.name }}</li>
461 * </ul>
462 * <div class="control-input">
463 * <input type="text" #itemname />
464 * <button (click)="addItem(itemname.value)">Add Item</button>
465 * </div>
466 * `
467 * })
468 * export class MyComponent implements OnInit {
469 *
470 * // services for data operations and data streaming
471 * private readonly itemsRef: AngularFirestoreCollection<Item>;
472 * private readonly profileRef: AngularFirestoreDocument<Profile>;
473 *
474 * // observables for template
475 * items: Observable<Item[]>;
476 * profile: Observable<Profile>;
477 *
478 * // inject main service
479 * constructor(private readonly afs: AngularFirestore) {}
480 *
481 * ngOnInit() {
482 * this.itemsRef = afs.collection('items', ref => ref.where('user', '==', 'davideast').limit(10));
483 * this.items = this.itemsRef.valueChanges().map(snap => snap.docs.map(data => doc.data()));
484 * // this.items = from(this.itemsRef); // you can also do this with no mapping
485 *
486 * this.profileRef = afs.doc('users/davideast');
487 * this.profile = this.profileRef.valueChanges();
488 * }
489 *
490 * addItem(name: string) {
491 * const user = 'davideast';
492 * this.itemsRef.add({ name, user });
493 * }
494 * }
495 */
496class AngularFirestore {
497 /**
498 * Each Feature of AngularFire has a FirebaseApp injected. This way we
499 * don't rely on the main Firebase App instance and we can create named
500 * apps and use multiple apps.
501 */
502 constructor(options, nameOrConfig, shouldEnablePersistence, settings,
503 // tslint:disable-next-line:ban-types
504 platformId, zone, persistenceSettings, _useEmulator, useAuthEmulator) {
505 this.schedulers = new ɵAngularFireSchedulers(zone);
506 this.keepUnstableUntilFirst = ɵkeepUnstableUntilFirstFactory(this.schedulers);
507 const app = ɵfirebaseAppFactory(options, zone, nameOrConfig);
508 if (!firebase.auth && useAuthEmulator) {
509 ɵlogAuthEmulatorError();
510 }
511 const useEmulator = _useEmulator;
512 [this.firestore, this.persistenceEnabled$] = ɵfetchInstance(`${app.name}.firestore`, 'AngularFirestore', app, () => {
513 const firestore = zone.runOutsideAngular(() => app.firestore());
514 if (settings) {
515 firestore.settings(settings);
516 }
517 if (useEmulator) {
518 firestore.useEmulator(...useEmulator);
519 }
520 if (shouldEnablePersistence && !isPlatformServer(platformId)) {
521 // We need to try/catch here because not all enablePersistence() failures are caught
522 // https://github.com/firebase/firebase-js-sdk/issues/608
523 const enablePersistence = () => {
524 try {
525 return from(firestore.enablePersistence(persistenceSettings || undefined).then(() => true, () => false));
526 }
527 catch (e) {
528 if (typeof console !== 'undefined') {
529 console.warn(e);
530 }
531 return of(false);
532 }
533 };
534 return [firestore, zone.runOutsideAngular(enablePersistence)];
535 }
536 else {
537 return [firestore, of(false)];
538 }
539 }, [settings, useEmulator, shouldEnablePersistence]);
540 }
541 collection(pathOrRef, queryFn) {
542 let collectionRef;
543 if (typeof pathOrRef === 'string') {
544 collectionRef = this.firestore.collection(pathOrRef);
545 }
546 else {
547 collectionRef = pathOrRef;
548 }
549 const { ref, query } = associateQuery(collectionRef, queryFn);
550 const refInZone = this.schedulers.ngZone.run(() => ref);
551 return new AngularFirestoreCollection(refInZone, query, this);
552 }
553 /**
554 * Create a reference to a Firestore Collection Group based on a collectionId
555 * and an optional query function to narrow the result
556 * set.
557 */
558 collectionGroup(collectionId, queryGroupFn) {
559 const queryFn = queryGroupFn || (ref => ref);
560 const collectionGroup = this.firestore.collectionGroup(collectionId);
561 return new AngularFirestoreCollectionGroup(queryFn(collectionGroup), this);
562 }
563 doc(pathOrRef) {
564 let ref;
565 if (typeof pathOrRef === 'string') {
566 ref = this.firestore.doc(pathOrRef);
567 }
568 else {
569 ref = pathOrRef;
570 }
571 const refInZone = this.schedulers.ngZone.run(() => ref);
572 return new AngularFirestoreDocument(refInZone, this);
573 }
574 /**
575 * Returns a generated Firestore Document Id.
576 */
577 createId() {
578 return this.firestore.collection('_').doc().id;
579 }
580}
581/** @nocollapse */ AngularFirestore.ɵprov = i0.ɵɵdefineInjectable({ factory: function AngularFirestore_Factory() { return new AngularFirestore(i0.ɵɵinject(i1.FIREBASE_OPTIONS), i0.ɵɵinject(i1.FIREBASE_APP_NAME, 8), i0.ɵɵinject(ENABLE_PERSISTENCE, 8), i0.ɵɵinject(SETTINGS, 8), i0.ɵɵinject(i0.PLATFORM_ID), i0.ɵɵinject(i0.NgZone), i0.ɵɵinject(PERSISTENCE_SETTINGS, 8), i0.ɵɵinject(USE_EMULATOR, 8), i0.ɵɵinject(i2.USE_EMULATOR, 8)); }, token: AngularFirestore, providedIn: "any" });
582AngularFirestore.decorators = [
583 { type: Injectable, args: [{
584 providedIn: 'any'
585 },] }
586];
587/** @nocollapse */
588AngularFirestore.ctorParameters = () => [
589 { type: undefined, decorators: [{ type: Inject, args: [FIREBASE_OPTIONS,] }] },
590 { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [FIREBASE_APP_NAME,] }] },
591 { type: Boolean, decorators: [{ type: Optional }, { type: Inject, args: [ENABLE_PERSISTENCE,] }] },
592 { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [SETTINGS,] }] },
593 { type: Object, decorators: [{ type: Inject, args: [PLATFORM_ID,] }] },
594 { type: NgZone },
595 { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [PERSISTENCE_SETTINGS,] }] },
596 { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [USE_EMULATOR,] }] },
597 { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [USE_EMULATOR$1,] }] }
598];
599
600class AngularFirestoreModule {
601 /**
602 * Attempt to enable persistent storage, if possible
603 */
604 static enablePersistence(persistenceSettings) {
605 return {
606 ngModule: AngularFirestoreModule,
607 providers: [
608 { provide: ENABLE_PERSISTENCE, useValue: true },
609 { provide: PERSISTENCE_SETTINGS, useValue: persistenceSettings },
610 ]
611 };
612 }
613}
614AngularFirestoreModule.decorators = [
615 { type: NgModule, args: [{
616 providers: [AngularFirestore]
617 },] }
618];
619
620/**
621 * Generated bundle index. Do not edit.
622 */
623
624export { AngularFirestore, AngularFirestoreCollection, AngularFirestoreCollectionGroup, AngularFirestoreDocument, AngularFirestoreModule, ENABLE_PERSISTENCE, PERSISTENCE_SETTINGS, SETTINGS, USE_EMULATOR, associateQuery, combineChange, combineChanges, docChanges, fromCollectionRef, fromDocRef, fromRef, sortedChanges, validateEventsArray };
625//# sourceMappingURL=angular-fire-firestore.js.map