@angular/fire
Version:
Angular + Firebase = ❤️
717 lines (707 loc) • 29.5 kB
JavaScript
import { isPlatformServer } from '@angular/common';
import * as i0 from '@angular/core';
import { inject, EnvironmentInjector, InjectionToken, NgZone, PLATFORM_ID, Injectable, Inject, Optional, NgModule } from '@angular/core';
import * as i1 from '@angular/fire';
import { VERSION } from '@angular/fire';
import * as i3 from '@angular/fire/app-check';
import { ɵfirebaseAppFactory as _firebaseAppFactory, ɵcacheInstance as _cacheInstance, FIREBASE_OPTIONS, FIREBASE_APP_NAME } from '@angular/fire/compat';
import * as i2 from '@angular/fire/compat/auth';
import { ɵauthFactory as _authFactory, USE_EMULATOR as USE_EMULATOR$1, SETTINGS as SETTINGS$1, TENANT_ID, LANGUAGE_CODE, USE_DEVICE_LANGUAGE, PERSISTENCE } from '@angular/fire/compat/auth';
import { Observable, asyncScheduler, from, of } from 'rxjs';
import { pendingUntilEvent } from '@angular/core/rxjs-interop';
import { startWith, pairwise, map, scan, distinctUntilChanged, filter } from 'rxjs/operators';
import 'firebase/compat/auth';
import 'firebase/compat/firestore';
import firebase from 'firebase/compat/app';
function _fromRef(ref, scheduler = asyncScheduler) {
return new Observable(subscriber => {
let unsubscribe;
if (scheduler != null) {
scheduler.schedule(() => {
unsubscribe = ref.onSnapshot({ includeMetadataChanges: true }, subscriber);
});
}
else {
unsubscribe = ref.onSnapshot({ includeMetadataChanges: true }, subscriber);
}
return () => {
if (unsubscribe != null) {
unsubscribe();
}
};
});
}
function fromRef(ref, scheduler) {
return _fromRef(ref, scheduler);
}
function fromDocRef(ref, scheduler) {
return fromRef(ref, scheduler)
.pipe(startWith(undefined), pairwise(), map((snapshots) => {
const [priorPayload, payload] = snapshots;
if (!payload.exists) {
return { payload, type: 'removed' };
}
if (!priorPayload?.exists) {
return { payload, type: 'added' };
}
return { payload, type: 'modified' };
}));
}
function fromCollectionRef(ref, scheduler) {
return fromRef(ref, scheduler).pipe(map(payload => ({ payload, type: 'query' })));
}
/**
* AngularFirestoreDocument service
*
* This class creates a reference to a Firestore Document. A reference is provided in
* in the constructor. The class is generic which gives you type safety for data update
* methods and data streaming.
*
* This class uses Symbol.observable to transform into Observable using Observable.from().
*
* This class is rarely used directly and should be created from the AngularFirestore service.
*
* Example:
*
* const fakeStock = new AngularFirestoreDocument<Stock>(doc('stocks/FAKE'));
* await fakeStock.set({ name: 'FAKE', price: 0.01 });
* fakeStock.valueChanges().map(snap => {
* if(snap.exists) return snap.data();
* return null;
* }).subscribe(value => console.log(value));
* // OR! Transform using Observable.from() and the data is unwrapped for you
* Observable.from(fakeStock).subscribe(value => console.log(value));
*/
class AngularFirestoreDocument {
ref;
afs;
injector = inject(EnvironmentInjector);
/**
* The constructor takes in a DocumentReference to provide wrapper methods
* for data operations, data streaming, and Symbol.observable.
*/
constructor(ref, afs) {
this.ref = ref;
this.afs = afs;
}
/**
* Create or overwrite a single document.
*/
set(data, options) {
return this.ref.set(data, options);
}
/**
* Update some fields of a document without overwriting the entire document.
*/
update(data) {
return this.ref.update(data);
}
/**
* Delete a document.
*/
delete() {
return this.ref.delete();
}
/**
* Create a reference to a sub-collection given a path and an optional query
* function.
*/
collection(path, queryFn) {
const collectionRef = this.ref.collection(path);
const { ref, query } = associateQuery(collectionRef, queryFn);
return new AngularFirestoreCollection(ref, query, this.afs);
}
/**
* Listen to snapshot updates from the document.
*/
snapshotChanges() {
const scheduledFromDocRef$ = fromDocRef(this.ref, this.afs.schedulers.outsideAngular);
return scheduledFromDocRef$.pipe(pendingUntilEvent(this.injector));
}
valueChanges(options = {}) {
return this.snapshotChanges().pipe(map(({ payload }) => options.idField ? {
...payload.data(),
...{ [options.idField]: payload.id }
} : payload.data()));
}
/**
* Retrieve the document once.
*/
get(options) {
return from(this.ref.get(options)).pipe(pendingUntilEvent(this.injector));
}
}
/**
* Return a stream of document changes on a query. These results are not in sort order but in
* order of occurence.
*/
function docChanges(query, scheduler) {
return fromCollectionRef(query, scheduler)
.pipe(startWith(undefined), pairwise(), map((actionTuple) => {
const [priorAction, action] = actionTuple;
const docChanges = action.payload.docChanges();
const actions = docChanges.map(change => ({ type: change.type, payload: change }));
// the metadata has changed from the prior emission
if (priorAction && JSON.stringify(priorAction.payload.metadata) !== JSON.stringify(action.payload.metadata)) {
// go through all the docs in payload and figure out which ones changed
action.payload.docs.forEach((currentDoc, currentIndex) => {
const docChange = docChanges.find(d => d.doc.ref.isEqual(currentDoc.ref));
const priorDoc = priorAction?.payload.docs.find(d => d.ref.isEqual(currentDoc.ref));
if (docChange && JSON.stringify(docChange.doc.metadata) === JSON.stringify(currentDoc.metadata) ||
!docChange && priorDoc && JSON.stringify(priorDoc.metadata) === JSON.stringify(currentDoc.metadata)) {
// document doesn't appear to have changed, don't log another action
}
else {
// since the actions are processed in order just push onto the array
actions.push({
type: 'modified',
payload: {
oldIndex: currentIndex,
newIndex: currentIndex,
type: 'modified',
doc: currentDoc
}
});
}
});
}
return actions;
}));
}
/**
* Return a stream of document changes on a query. These results are in sort order.
*/
function sortedChanges(query, events, scheduler) {
return docChanges(query, scheduler)
.pipe(scan((current, changes) => combineChanges(current, changes.map(it => it.payload), events), []), distinctUntilChanged(), // cut down on unneed change cycles
map(changes => changes.map(c => ({ type: c.type, payload: c }))));
}
/**
* Combines the total result set from the current set of changes from an incoming set
* of changes.
*/
function combineChanges(current, changes, events) {
changes.forEach(change => {
// skip unwanted change types
if (events.indexOf(change.type) > -1) {
current = combineChange(current, change);
}
});
return current;
}
/**
* Splice arguments on top of a sliced array, to break top-level ===
* this is useful for change-detection
*/
function sliceAndSplice(original, start, deleteCount, ...args) {
const returnArray = original.slice();
returnArray.splice(start, deleteCount, ...args);
return returnArray;
}
/**
* Creates a new sorted array from a new change.
* Build our own because we allow filtering of action types ('added', 'removed', 'modified') before scanning
* and so we have greater control over change detection (by breaking ===)
*/
function combineChange(combined, change) {
switch (change.type) {
case 'added':
if (combined[change.newIndex]?.doc.ref.isEqual(change.doc.ref)) {
// Not sure why the duplicates are getting fired
}
else {
return sliceAndSplice(combined, change.newIndex, 0, change);
}
break;
case 'modified':
if (combined[change.oldIndex] == null || combined[change.oldIndex].doc.ref.isEqual(change.doc.ref)) {
// When an item changes position we first remove it
// and then add it's new position
if (change.oldIndex !== change.newIndex) {
const copiedArray = combined.slice();
copiedArray.splice(change.oldIndex, 1);
copiedArray.splice(change.newIndex, 0, change);
return copiedArray;
}
else {
return sliceAndSplice(combined, change.newIndex, 1, change);
}
}
break;
case 'removed':
if (combined[change.oldIndex]?.doc.ref.isEqual(change.doc.ref)) {
return sliceAndSplice(combined, change.oldIndex, 1);
}
break;
}
return combined;
}
function validateEventsArray(events) {
if (!events || events.length === 0) {
events = ['added', 'removed', 'modified'];
}
return events;
}
/**
* AngularFirestoreCollection service
*
* This class creates a reference to a Firestore Collection. A reference and a query are provided in
* in the constructor. The query can be the unqueried reference if no query is desired.The class
* is generic which gives you type safety for data update methods and data streaming.
*
* This class uses Symbol.observable to transform into Observable using Observable.from().
*
* This class is rarely used directly and should be created from the AngularFirestore service.
*
* Example:
*
* const collectionRef = firebase.firestore.collection('stocks');
* const query = collectionRef.where('price', '>', '0.01');
* const fakeStock = new AngularFirestoreCollection<Stock>(collectionRef, query);
*
* // NOTE!: the updates are performed on the reference not the query
* await fakeStock.add({ name: 'FAKE', price: 0.01 });
*
* // Subscribe to changes as snapshots. This provides you data updates as well as delta updates.
* fakeStock.valueChanges().subscribe(value => console.log(value));
*/
class AngularFirestoreCollection {
ref;
query;
afs;
injector = inject(EnvironmentInjector);
/**
* The constructor takes in a CollectionReference and Query to provide wrapper methods
* for data operations and data streaming.
*
* Note: Data operation methods are done on the reference not the query. This means
* when you update data it is not updating data to the window of your query unless
* the data fits the criteria of the query. See the AssociatedRefence type for details
* on this implication.
*/
constructor(ref, query, afs) {
this.ref = ref;
this.query = query;
this.afs = afs;
}
/**
* Listen to the latest change in the stream. This method returns changes
* as they occur and they are not sorted by query order. This allows you to construct
* your own data structure.
*/
stateChanges(events) {
let source = docChanges(this.query, this.afs.schedulers.outsideAngular);
if (events && events.length > 0) {
source = source.pipe(map(actions => actions.filter(change => events.indexOf(change.type) > -1)));
}
return source.pipe(
// We want to filter out empty arrays, but always emit at first, so the developer knows
// that the collection has been resolve; even if it's empty
startWith(undefined), pairwise(), filter(([prior, current]) => current.length > 0 || !prior), map(([, current]) => current), pendingUntilEvent(this.injector));
}
/**
* Create a stream of changes as they occur it time. This method is similar to stateChanges()
* but it collects each event in an array over time.
*/
auditTrail(events) {
return this.stateChanges(events).pipe(scan((current, action) => [...current, ...action], []));
}
/**
* Create a stream of synchronized changes. This method keeps the local array in sorted
* query order.
*/
snapshotChanges(events) {
const validatedEvents = validateEventsArray(events);
const scheduledSortedChanges$ = sortedChanges(this.query, validatedEvents, this.afs.schedulers.outsideAngular);
return scheduledSortedChanges$.pipe(pendingUntilEvent(this.injector));
}
valueChanges(options = {}) {
return fromCollectionRef(this.query, this.afs.schedulers.outsideAngular)
.pipe(map(actions => actions.payload.docs.map(a => {
if (options.idField) {
return {
...a.data(),
...{ [options.idField]: a.id }
};
}
else {
return a.data();
}
})), pendingUntilEvent(this.injector));
}
/**
* Retrieve the results of the query once.
*/
get(options) {
return from(this.query.get(options)).pipe(pendingUntilEvent(this.injector));
}
/**
* Add data to a collection reference.
*
* Note: Data operation methods are done on the reference not the query. This means
* when you update data it is not updating data to the window of your query unless
* the data fits the criteria of the query.
*/
add(data) {
return this.ref.add(data);
}
/**
* Create a reference to a single document in a collection.
*/
doc(path) {
// TODO is there a better way to solve this type issue
return new AngularFirestoreDocument(this.ref.doc(path), this.afs);
}
}
/**
* AngularFirestoreCollectionGroup service
*
* This class holds a reference to a Firestore Collection Group Query.
*
* This class uses Symbol.observable to transform into Observable using Observable.from().
*
* This class is rarely used directly and should be created from the AngularFirestore service.
*
* Example:
*
* const collectionGroup = firebase.firestore.collectionGroup('stocks');
* const query = collectionRef.where('price', '>', '0.01');
* const fakeStock = new AngularFirestoreCollectionGroup<Stock>(query, afs);
*
* // Subscribe to changes as snapshots. This provides you data updates as well as delta updates.
* fakeStock.valueChanges().subscribe(value => console.log(value));
*/
class AngularFirestoreCollectionGroup {
query;
afs;
injector = inject(EnvironmentInjector);
/**
* The constructor takes in a CollectionGroupQuery to provide wrapper methods
* for data operations and data streaming.
*/
constructor(query, afs) {
this.query = query;
this.afs = afs;
}
/**
* Listen to the latest change in the stream. This method returns changes
* as they occur and they are not sorted by query order. This allows you to construct
* your own data structure.
*/
stateChanges(events) {
if (!events || events.length === 0) {
return docChanges(this.query, this.afs.schedulers.outsideAngular).pipe(pendingUntilEvent(this.injector));
}
return docChanges(this.query, this.afs.schedulers.outsideAngular)
.pipe(map(actions => actions.filter(change => events.indexOf(change.type) > -1)), filter(changes => changes.length > 0), pendingUntilEvent(this.injector));
}
/**
* Create a stream of changes as they occur it time. This method is similar to stateChanges()
* but it collects each event in an array over time.
*/
auditTrail(events) {
return this.stateChanges(events).pipe(scan((current, action) => [...current, ...action], []));
}
/**
* Create a stream of synchronized changes. This method keeps the local array in sorted
* query order.
*/
snapshotChanges(events) {
const validatedEvents = validateEventsArray(events);
const scheduledSortedChanges$ = sortedChanges(this.query, validatedEvents, this.afs.schedulers.outsideAngular);
return scheduledSortedChanges$.pipe(pendingUntilEvent(this.injector));
}
valueChanges(options = {}) {
const fromCollectionRefScheduled$ = fromCollectionRef(this.query, this.afs.schedulers.outsideAngular);
return fromCollectionRefScheduled$
.pipe(map(actions => actions.payload.docs.map(a => {
if (options.idField) {
return {
[options.idField]: a.id,
...a.data()
};
}
else {
return a.data();
}
})), pendingUntilEvent(this.injector));
}
/**
* Retrieve the results of the query once.
*/
get(options) {
return from(this.query.get(options)).pipe(pendingUntilEvent(this.injector));
}
}
/**
* The value of this token determines whether or not the firestore will have persistance enabled
*/
const ENABLE_PERSISTENCE = new InjectionToken('angularfire2.enableFirestorePersistence');
const PERSISTENCE_SETTINGS = new InjectionToken('angularfire2.firestore.persistenceSettings');
const SETTINGS = new InjectionToken('angularfire2.firestore.settings');
const USE_EMULATOR = new InjectionToken('angularfire2.firestore.use-emulator');
/**
* A utility methods for associating a collection reference with
* a query.
*
* @param collectionRef - A collection reference to query
* @param queryFn - The callback to create a query
*
* Example:
* const { query, ref } = associateQuery(docRef.collection('items'), ref => {
* return ref.where('age', '<', 200);
* });
*/
function associateQuery(collectionRef, queryFn = ref => ref) {
const query = queryFn(collectionRef);
const ref = collectionRef;
return { query, ref };
}
/**
* AngularFirestore Service
*
* This service is the main entry point for this feature module. It provides
* an API for creating Collection and Reference services. These services can
* then be used to do data updates and observable streams of the data.
*
* Example:
*
* import { Component } from '@angular/core';
* import { AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument } from '@angular/fire/firestore';
* import { Observable } from 'rxjs/Observable';
* import { from } from 'rxjs/observable';
*
* @Component({
* selector: 'app-my-component',
* template: `
* <h2>Items for {{ (profile | async)?.name }}
* <ul>
* <li *ngFor="let item of items | async">{{ item.name }}</li>
* </ul>
* <div class="control-input">
* <input type="text" #itemname />
* <button (click)="addItem(itemname.value)">Add Item</button>
* </div>
* `
* })
* export class MyComponent implements OnInit {
*
* // services for data operations and data streaming
* private readonly itemsRef: AngularFirestoreCollection<Item>;
* private readonly profileRef: AngularFirestoreDocument<Profile>;
*
* // observables for template
* items: Observable<Item[]>;
* profile: Observable<Profile>;
*
* // inject main service
* constructor(private readonly afs: AngularFirestore) {}
*
* ngOnInit() {
* this.itemsRef = afs.collection('items', ref => ref.where('user', '==', 'davideast').limit(10));
* this.items = this.itemsRef.valueChanges().map(snap => snap.docs.map(data => doc.data()));
* // this.items = from(this.itemsRef); // you can also do this with no mapping
*
* this.profileRef = afs.doc('users/davideast');
* this.profile = this.profileRef.valueChanges();
* }
*
* addItem(name: string) {
* const user = 'davideast';
* this.itemsRef.add({ name, user });
* }
* }
*/
class AngularFirestore {
schedulers;
firestore;
persistenceEnabled$;
ngZone = inject(NgZone);
/**
* Each Feature of AngularFire has a FirebaseApp injected. This way we
* don't rely on the main Firebase App instance and we can create named
* apps and use multiple apps.
*/
constructor(options, name, shouldEnablePersistence, settings,
// eslint-disable-next-line @typescript-eslint/ban-types
platformId, zone, schedulers, persistenceSettings, _useEmulator, auth, useAuthEmulator, authSettings, // can't use firebase.auth.AuthSettings here
tenantId, languageCode, useDeviceLanguage, persistence, _appCheckInstances) {
this.schedulers = schedulers;
const app = _firebaseAppFactory(options, zone, name);
const useEmulator = _useEmulator;
if (auth) {
_authFactory(app, zone, useAuthEmulator, tenantId, languageCode, useDeviceLanguage, authSettings, persistence);
}
[this.firestore, this.persistenceEnabled$] = _cacheInstance(`${app.name}.firestore`, 'AngularFirestore', app.name, () => {
const firestore = zone.runOutsideAngular(() => app.firestore());
if (settings) {
firestore.settings(settings);
}
if (useEmulator) {
firestore.useEmulator(...useEmulator);
}
if (shouldEnablePersistence && !isPlatformServer(platformId)) {
// We need to try/catch here because not all enablePersistence() failures are caught
// https://github.com/firebase/firebase-js-sdk/issues/608
const enablePersistence = () => {
try {
return from(firestore.enablePersistence(persistenceSettings || undefined).then(() => true, () => false));
}
catch (e) {
if (typeof console !== 'undefined') {
console.warn(e);
}
return of(false);
}
};
return [firestore, zone.runOutsideAngular(enablePersistence)];
}
else {
return [firestore, of(false)];
}
}, [settings, useEmulator, shouldEnablePersistence]);
}
collection(pathOrRef, queryFn) {
let collectionRef;
if (typeof pathOrRef === 'string') {
collectionRef = this.firestore.collection(pathOrRef);
}
else {
collectionRef = pathOrRef;
}
const { ref, query } = associateQuery(collectionRef, queryFn);
const refInZone = this.ngZone.run(() => ref);
return new AngularFirestoreCollection(refInZone, query, this);
}
/**
* Create a reference to a Firestore Collection Group based on a collectionId
* and an optional query function to narrow the result
* set.
*/
collectionGroup(collectionId, queryGroupFn) {
const queryFn = queryGroupFn || (ref => ref);
const collectionGroup = this.firestore.collectionGroup(collectionId);
return new AngularFirestoreCollectionGroup(queryFn(collectionGroup), this);
}
doc(pathOrRef) {
let ref;
if (typeof pathOrRef === 'string') {
ref = this.firestore.doc(pathOrRef);
}
else {
ref = pathOrRef;
}
const refInZone = this.ngZone.run(() => ref);
return new AngularFirestoreDocument(refInZone, this);
}
/**
* Returns a generated Firestore Document Id.
*/
createId() {
return this.firestore.collection('_').doc().id;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: AngularFirestore, deps: [{ token: FIREBASE_OPTIONS }, { token: FIREBASE_APP_NAME, optional: true }, { token: ENABLE_PERSISTENCE, optional: true }, { token: SETTINGS, optional: true }, { token: PLATFORM_ID }, { token: i0.NgZone }, { token: i1.ɵAngularFireSchedulers }, { token: PERSISTENCE_SETTINGS, optional: true }, { token: USE_EMULATOR, optional: true }, { token: i2.AngularFireAuth, optional: true }, { token: USE_EMULATOR$1, optional: true }, { token: SETTINGS$1, optional: true }, { token: TENANT_ID, optional: true }, { token: LANGUAGE_CODE, optional: true }, { token: USE_DEVICE_LANGUAGE, optional: true }, { token: PERSISTENCE, optional: true }, { token: i3.AppCheckInstances, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: AngularFirestore, providedIn: 'any' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: AngularFirestore, decorators: [{
type: Injectable,
args: [{
providedIn: 'any'
}]
}], ctorParameters: () => [{ type: undefined, decorators: [{
type: Inject,
args: [FIREBASE_OPTIONS]
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [FIREBASE_APP_NAME]
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [ENABLE_PERSISTENCE]
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [SETTINGS]
}] }, { type: Object, decorators: [{
type: Inject,
args: [PLATFORM_ID]
}] }, { type: i0.NgZone }, { type: i1.ɵAngularFireSchedulers }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [PERSISTENCE_SETTINGS]
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [USE_EMULATOR]
}] }, { type: i2.AngularFireAuth, decorators: [{
type: Optional
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [USE_EMULATOR$1]
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [SETTINGS$1]
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [TENANT_ID]
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [LANGUAGE_CODE]
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [USE_DEVICE_LANGUAGE]
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [PERSISTENCE]
}] }, { type: i3.AppCheckInstances, decorators: [{
type: Optional
}] }] });
class AngularFirestoreModule {
constructor() {
firebase.registerVersion('angularfire', VERSION.full, 'fst-compat');
}
/**
* Attempt to enable persistent storage, if possible
*/
static enablePersistence(persistenceSettings) {
return {
ngModule: AngularFirestoreModule,
providers: [
{ provide: ENABLE_PERSISTENCE, useValue: true },
{ provide: PERSISTENCE_SETTINGS, useValue: persistenceSettings },
]
};
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: AngularFirestoreModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.0.0", ngImport: i0, type: AngularFirestoreModule });
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: AngularFirestoreModule, providers: [AngularFirestore] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: AngularFirestoreModule, decorators: [{
type: NgModule,
args: [{
providers: [AngularFirestore]
}]
}], ctorParameters: () => [] });
/**
* Generated bundle index. Do not edit.
*/
export { AngularFirestore, AngularFirestoreCollection, AngularFirestoreCollectionGroup, AngularFirestoreDocument, AngularFirestoreModule, ENABLE_PERSISTENCE, PERSISTENCE_SETTINGS, SETTINGS, USE_EMULATOR, associateQuery, combineChange, combineChanges, docChanges, fromCollectionRef, fromDocRef, fromRef, sortedChanges, validateEventsArray };
//# sourceMappingURL=angular-fire-compat-firestore.mjs.map