@firebase/firestore
Version:
The Cloud Firestore component of the Firebase JS SDK.
1,123 lines (1,114 loc) • 131 kB
JavaScript
import { _registerComponent, registerVersion, SDK_VERSION } from '@firebase/app';
import { Component } from '@firebase/component';
import { s as setSDKVersion, F as Firestore, a as FirebaseAuthCredentialsProvider, b as FirebaseAppCheckTokenProvider, d as databaseIdFromApp, O as ObjectValue, D as DocumentReference, f as fieldPathFromArgument, Q as Query, q as queryWithAddedFilter, n as newUserDataReader, C as CompositeFilter, c as queryWithAddedOrderBy, e as queryWithLimit, v as validatePositiveNumber, g as queryWithStartAt, h as queryWithEndAt, i as FirestoreError, j as Code, p as parseQueryValue, k as FieldFilter, l as OrderBy, m as queryNormalizedOrderBy, r as refValue, o as isServerTimestamp, B as Bound, t as isCollectionGroupQuery, R as ResourcePath, u as DocumentKey, w as valueDescription, A as AbstractUserDataWriter, x as Bytes, y as queryEqual, z as cast, E as ensureFirestoreConfigured, G as mapToArray, H as firestoreClientRunAggregateQuery, I as ExpUserDataWriter, J as AggregateImpl, K as OnlineComponentProvider, L as LruGcMemoryOfflineComponentProvider, M as MemoryOfflineComponentProvider, N as IndexedDbOfflineComponentProvider, P as MultiTabOfflineComponentProvider, S as newTextEncoder, T as Timestamp, U as JsonProtoSerializer, V as UserDataReader, W as fromTimestamp, X as toName, Y as toTimestamp, Z as toQueryTarget, _ as queryToTarget, $ as parseObject, a0 as AutoId, a1 as property, a2 as validateJSON, a3 as newSerializer, a4 as createBundleReaderSync, a5 as BundleLoader, a6 as fromDocument, a7 as fromBundledQuery, a8 as DocumentSet, a9 as ViewSnapshot, aa as documentKeySet, ab as fail, ac as parseSetData, ad as Precondition, ae as FieldPath, af as parseUpdateVarargs, ag as parseUpdateData, ah as DeleteMutation, ai as firestoreClientTransaction, aj as firestoreClientGetDocumentViaSnapshotListener, ak as firestoreClientGetDocumentFromLocalCache, al as firestoreClientGetDocumentsViaSnapshotListener, am as firestoreClientGetDocumentsFromLocalCache, an as doc, ao as newQueryForPath, ap as firestoreClientListen, aq as firestoreClientAddSnapshotsInSyncListener, ar as firestoreClientWrite, as as loadBundle, at as namedQuery, au as logWarn, av as firestoreClientSetIndexConfiguration, aw as fieldPathFromDotSeparatedString, ax as IndexSegment, ay as FieldIndex, az as IndexState, aA as firestoreClientDeleteAllFieldIndexes, aB as logDebug, aC as firestoreClientSetPersistentCacheIndexAutoCreationEnabled, aD as setTestingHooksSpi } from './common-948f0202.node.mjs';
export { A as AbstractUserDataWriter, x as Bytes, b2 as CACHE_SIZE_UNLIMITED, aS as CollectionReference, D as DocumentReference, ae as FieldPath, aU as FieldValue, F as Firestore, i as FirestoreError, b1 as GeoPoint, aP as LoadBundleTask, Q as Query, T as Timestamp, a$ as VectorValue, a0 as _AutoId, ba as _ByteString, b4 as _DatabaseId, u as _DocumentKey, bc as _EmptyAppCheckTokenProvider, bb as _EmptyAuthCredentialsProvider, b9 as _FieldPath, z as _cast, b8 as _debugAssert, b6 as _internalAggregationQueryToProtoRunAggregationQueryRequest, b5 as _internalQueryToProtoQueryTarget, b3 as _isBase64Available, au as _logWarn, b7 as _validateIsNotUsedTogether, aV as arrayRemove, aW as arrayUnion, aF as clearIndexedDbPersistence, aQ as collection, aR as collectionGroup, aG as connectFirestoreEmulator, aX as deleteField, aH as disableNetwork, an as doc, aE as documentId, aI as enableIndexedDbPersistence, aJ as enableMultiTabIndexedDbPersistence, aK as enableNetwork, E as ensureFirestoreConfigured, aL as getFirestore, aY as increment, aM as initializeFirestore, as as loadBundle, at as namedQuery, y as queryEqual, aT as refEqual, aZ as serverTimestamp, b0 as setLogLevel, aN as terminate, a_ as vector, aO as waitForPendingWrites } from './common-948f0202.node.mjs';
import { getModularInstance, deepEqual } from '@firebase/util';
import '@firebase/webchannel-wrapper/bloom-blob';
import '@firebase/logger';
import 'util';
import 'crypto';
import '@grpc/grpc-js';
import '@grpc/proto-loader';
const name$1 = "@firebase/firestore";
const version = "4.10.0";
/**
* @license
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function registerFirestore(variant, useFetchStreams = true) {
setSDKVersion(SDK_VERSION);
_registerComponent(new Component('firestore', (container, { instanceIdentifier: databaseId, options: settings }) => {
const app = container.getProvider('app').getImmediate();
const firestoreInstance = new Firestore(new FirebaseAuthCredentialsProvider(container.getProvider('auth-internal')), new FirebaseAppCheckTokenProvider(app, container.getProvider('app-check-internal')), databaseIdFromApp(app, databaseId), app);
settings = { useFetchStreams, ...settings };
firestoreInstance._setSettings(settings);
return firestoreInstance;
}, 'PUBLIC').setMultipleInstances(true));
registerVersion(name$1, version, variant);
// BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
registerVersion(name$1, version, '__BUILD_TARGET__');
}
/**
* @license
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Represents an aggregation that can be performed by Firestore.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
class AggregateField {
/**
* Create a new AggregateField<T>
* @param aggregateType - Specifies the type of aggregation operation to perform.
* @param _internalFieldPath - Optionally specifies the field that is aggregated.
* @internal
*/
constructor(aggregateType = 'count', _internalFieldPath) {
this._internalFieldPath = _internalFieldPath;
/** A type string to uniquely identify instances of this class. */
this.type = 'AggregateField';
this.aggregateType = aggregateType;
}
}
/**
* The results of executing an aggregation query.
*/
class AggregateQuerySnapshot {
/** @hideconstructor */
constructor(query, _userDataWriter, _data) {
this._userDataWriter = _userDataWriter;
this._data = _data;
/** A type string to uniquely identify instances of this class. */
this.type = 'AggregateQuerySnapshot';
this.query = query;
}
/**
* Returns the results of the aggregations performed over the underlying
* query.
*
* The keys of the returned object will be the same as those of the
* `AggregateSpec` object specified to the aggregation method, and the values
* will be the corresponding aggregation result.
*
* @returns The results of the aggregations performed over the underlying
* query.
*/
data() {
return this._userDataWriter.convertObjectMap(this._data);
}
/**
* @internal
* @private
*
* Retrieves all fields in the snapshot as a proto value.
*
* @returns An `Object` containing all fields in the snapshot.
*/
_fieldsProto() {
// Wrap data in an ObjectValue to clone it.
const dataClone = new ObjectValue({
mapValue: { fields: this._data }
}).clone();
// Return the cloned value to prevent manipulation of the Snapshot's data
return dataClone.value.mapValue.fields;
}
}
/**
* @license
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* A `DocumentSnapshot` contains data read from a document in your Firestore
* database. The data can be extracted with `.data()` or `.get(<field>)` to
* get a specific field.
*
* For a `DocumentSnapshot` that points to a non-existing document, any data
* access will return 'undefined'. You can use the `exists()` method to
* explicitly verify a document's existence.
*/
class DocumentSnapshot$1 {
// Note: This class is stripped down version of the DocumentSnapshot in
// the legacy SDK. The changes are:
// - No support for SnapshotMetadata.
// - No support for SnapshotOptions.
/** @hideconstructor protected */
constructor(_firestore, _userDataWriter, _key, _document, _converter) {
this._firestore = _firestore;
this._userDataWriter = _userDataWriter;
this._key = _key;
this._document = _document;
this._converter = _converter;
}
/** Property of the `DocumentSnapshot` that provides the document's ID. */
get id() {
return this._key.path.lastSegment();
}
/**
* The `DocumentReference` for the document included in the `DocumentSnapshot`.
*/
get ref() {
return new DocumentReference(this._firestore, this._converter, this._key);
}
/**
* Signals whether or not the document at the snapshot's location exists.
*
* @returns true if the document exists.
*/
exists() {
return this._document !== null;
}
/**
* Retrieves all fields in the document as an `Object`. Returns `undefined` if
* the document doesn't exist.
*
* @returns An `Object` containing all fields in the document or `undefined`
* if the document doesn't exist.
*/
data() {
if (!this._document) {
return undefined;
}
else if (this._converter) {
// We only want to use the converter and create a new DocumentSnapshot
// if a converter has been provided.
const snapshot = new QueryDocumentSnapshot$1(this._firestore, this._userDataWriter, this._key, this._document,
/* converter= */ null);
return this._converter.fromFirestore(snapshot);
}
else {
return this._userDataWriter.convertValue(this._document.data.value);
}
}
/**
* @internal
* @private
*
* Retrieves all fields in the document as a proto Value. Returns `undefined` if
* the document doesn't exist.
*
* @returns An `Object` containing all fields in the document or `undefined`
* if the document doesn't exist.
*/
_fieldsProto() {
// Return a cloned value to prevent manipulation of the Snapshot's data
return this._document?.data.clone().value.mapValue.fields ?? undefined;
}
/**
* Retrieves the field specified by `fieldPath`. Returns `undefined` if the
* document or field doesn't exist.
*
* @param fieldPath - The path (for example 'foo' or 'foo.bar') to a specific
* field.
* @returns The data at the specified field location or undefined if no such
* field exists in the document.
*/
// We are using `any` here to avoid an explicit cast by our users.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
get(fieldPath) {
if (this._document) {
const value = this._document.data.field(fieldPathFromArgument('DocumentSnapshot.get', fieldPath));
if (value !== null) {
return this._userDataWriter.convertValue(value);
}
}
return undefined;
}
}
/**
* A `QueryDocumentSnapshot` contains data read from a document in your
* Firestore database as part of a query. The document is guaranteed to exist
* and its data can be extracted with `.data()` or `.get(<field>)` to get a
* specific field.
*
* A `QueryDocumentSnapshot` offers the same API surface as a
* `DocumentSnapshot`. Since query results contain only existing documents, the
* `exists` property will always be true and `data()` will never return
* 'undefined'.
*/
class QueryDocumentSnapshot$1 extends DocumentSnapshot$1 {
/**
* Retrieves all fields in the document as an `Object`.
*
* @override
* @returns An `Object` containing all fields in the document.
*/
data() {
return super.data();
}
}
/**
* @license
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function validateHasExplicitOrderByForLimitToLast(query) {
if (query.limitType === "L" /* LimitType.Last */ &&
query.explicitOrderBy.length === 0) {
throw new FirestoreError(Code.UNIMPLEMENTED, 'limitToLast() queries require specifying at least one orderBy() clause');
}
}
/**
* An `AppliableConstraint` is an abstraction of a constraint that can be applied
* to a Firestore query.
*/
class AppliableConstraint {
}
/**
* A `QueryConstraint` is used to narrow the set of documents returned by a
* Firestore query. `QueryConstraint`s are created by invoking {@link where},
* {@link orderBy}, {@link (startAt:1)}, {@link (startAfter:1)}, {@link
* (endBefore:1)}, {@link (endAt:1)}, {@link limit}, {@link limitToLast} and
* can then be passed to {@link (query:1)} to create a new query instance that
* also contains this `QueryConstraint`.
*/
class QueryConstraint extends AppliableConstraint {
}
function query(query, queryConstraint, ...additionalQueryConstraints) {
let queryConstraints = [];
if (queryConstraint instanceof AppliableConstraint) {
queryConstraints.push(queryConstraint);
}
queryConstraints = queryConstraints.concat(additionalQueryConstraints);
validateQueryConstraintArray(queryConstraints);
for (const constraint of queryConstraints) {
query = constraint._apply(query);
}
return query;
}
/**
* A `QueryFieldFilterConstraint` is used to narrow the set of documents returned by
* a Firestore query by filtering on one or more document fields.
* `QueryFieldFilterConstraint`s are created by invoking {@link where} and can then
* be passed to {@link (query:1)} to create a new query instance that also contains
* this `QueryFieldFilterConstraint`.
*/
class QueryFieldFilterConstraint extends QueryConstraint {
/**
* @internal
*/
constructor(_field, _op, _value) {
super();
this._field = _field;
this._op = _op;
this._value = _value;
/** The type of this query constraint */
this.type = 'where';
}
static _create(_field, _op, _value) {
return new QueryFieldFilterConstraint(_field, _op, _value);
}
_apply(query) {
const filter = this._parse(query);
validateNewFieldFilter(query._query, filter);
return new Query(query.firestore, query.converter, queryWithAddedFilter(query._query, filter));
}
_parse(query) {
const reader = newUserDataReader(query.firestore);
const filter = newQueryFilter(query._query, 'where', reader, query.firestore._databaseId, this._field, this._op, this._value);
return filter;
}
}
/**
* Creates a {@link QueryFieldFilterConstraint} that enforces that documents
* must contain the specified field and that the value should satisfy the
* relation constraint provided.
*
* @param fieldPath - The path to compare
* @param opStr - The operation string (e.g "<", "<=", "==", "<",
* "<=", "!=").
* @param value - The value for comparison
* @returns The created {@link QueryFieldFilterConstraint}.
*/
function where(fieldPath, opStr, value) {
const op = opStr;
const field = fieldPathFromArgument('where', fieldPath);
return QueryFieldFilterConstraint._create(field, op, value);
}
/**
* A `QueryCompositeFilterConstraint` is used to narrow the set of documents
* returned by a Firestore query by performing the logical OR or AND of multiple
* {@link QueryFieldFilterConstraint}s or {@link QueryCompositeFilterConstraint}s.
* `QueryCompositeFilterConstraint`s are created by invoking {@link or} or
* {@link and} and can then be passed to {@link (query:1)} to create a new query
* instance that also contains the `QueryCompositeFilterConstraint`.
*/
class QueryCompositeFilterConstraint extends AppliableConstraint {
/**
* @internal
*/
constructor(
/** The type of this query constraint */
type, _queryConstraints) {
super();
this.type = type;
this._queryConstraints = _queryConstraints;
}
static _create(type, _queryConstraints) {
return new QueryCompositeFilterConstraint(type, _queryConstraints);
}
_parse(query) {
const parsedFilters = this._queryConstraints
.map(queryConstraint => {
return queryConstraint._parse(query);
})
.filter(parsedFilter => parsedFilter.getFilters().length > 0);
if (parsedFilters.length === 1) {
return parsedFilters[0];
}
return CompositeFilter.create(parsedFilters, this._getOperator());
}
_apply(query) {
const parsedFilter = this._parse(query);
if (parsedFilter.getFilters().length === 0) {
// Return the existing query if not adding any more filters (e.g. an empty
// composite filter).
return query;
}
validateNewFilter(query._query, parsedFilter);
return new Query(query.firestore, query.converter, queryWithAddedFilter(query._query, parsedFilter));
}
_getQueryConstraints() {
return this._queryConstraints;
}
_getOperator() {
return this.type === 'and' ? "and" /* CompositeOperator.AND */ : "or" /* CompositeOperator.OR */;
}
}
/**
* Creates a new {@link QueryCompositeFilterConstraint} that is a disjunction of
* the given filter constraints. A disjunction filter includes a document if it
* satisfies any of the given filters.
*
* @param queryConstraints - Optional. The list of
* {@link QueryFilterConstraint}s to perform a disjunction for. These must be
* created with calls to {@link where}, {@link or}, or {@link and}.
* @returns The newly created {@link QueryCompositeFilterConstraint}.
*/
function or(...queryConstraints) {
// Only support QueryFilterConstraints
queryConstraints.forEach(queryConstraint => validateQueryFilterConstraint('or', queryConstraint));
return QueryCompositeFilterConstraint._create("or" /* CompositeOperator.OR */, queryConstraints);
}
/**
* Creates a new {@link QueryCompositeFilterConstraint} that is a conjunction of
* the given filter constraints. A conjunction filter includes a document if it
* satisfies all of the given filters.
*
* @param queryConstraints - Optional. The list of
* {@link QueryFilterConstraint}s to perform a conjunction for. These must be
* created with calls to {@link where}, {@link or}, or {@link and}.
* @returns The newly created {@link QueryCompositeFilterConstraint}.
*/
function and(...queryConstraints) {
// Only support QueryFilterConstraints
queryConstraints.forEach(queryConstraint => validateQueryFilterConstraint('and', queryConstraint));
return QueryCompositeFilterConstraint._create("and" /* CompositeOperator.AND */, queryConstraints);
}
/**
* A `QueryOrderByConstraint` is used to sort the set of documents returned by a
* Firestore query. `QueryOrderByConstraint`s are created by invoking
* {@link orderBy} and can then be passed to {@link (query:1)} to create a new query
* instance that also contains this `QueryOrderByConstraint`.
*
* Note: Documents that do not contain the orderBy field will not be present in
* the query result.
*/
class QueryOrderByConstraint extends QueryConstraint {
/**
* @internal
*/
constructor(_field, _direction) {
super();
this._field = _field;
this._direction = _direction;
/** The type of this query constraint */
this.type = 'orderBy';
}
static _create(_field, _direction) {
return new QueryOrderByConstraint(_field, _direction);
}
_apply(query) {
const orderBy = newQueryOrderBy(query._query, this._field, this._direction);
return new Query(query.firestore, query.converter, queryWithAddedOrderBy(query._query, orderBy));
}
}
/**
* Creates a {@link QueryOrderByConstraint} that sorts the query result by the
* specified field, optionally in descending order instead of ascending.
*
* Note: Documents that do not contain the specified field will not be present
* in the query result.
*
* @param fieldPath - The field to sort by.
* @param directionStr - Optional direction to sort by ('asc' or 'desc'). If
* not specified, order will be ascending.
* @returns The created {@link QueryOrderByConstraint}.
*/
function orderBy(fieldPath, directionStr = 'asc') {
const direction = directionStr;
const path = fieldPathFromArgument('orderBy', fieldPath);
return QueryOrderByConstraint._create(path, direction);
}
/**
* A `QueryLimitConstraint` is used to limit the number of documents returned by
* a Firestore query.
* `QueryLimitConstraint`s are created by invoking {@link limit} or
* {@link limitToLast} and can then be passed to {@link (query:1)} to create a new
* query instance that also contains this `QueryLimitConstraint`.
*/
class QueryLimitConstraint extends QueryConstraint {
/**
* @internal
*/
constructor(
/** The type of this query constraint */
type, _limit, _limitType) {
super();
this.type = type;
this._limit = _limit;
this._limitType = _limitType;
}
static _create(type, _limit, _limitType) {
return new QueryLimitConstraint(type, _limit, _limitType);
}
_apply(query) {
return new Query(query.firestore, query.converter, queryWithLimit(query._query, this._limit, this._limitType));
}
}
/**
* Creates a {@link QueryLimitConstraint} that only returns the first matching
* documents.
*
* @param limit - The maximum number of items to return.
* @returns The created {@link QueryLimitConstraint}.
*/
function limit(limit) {
validatePositiveNumber('limit', limit);
return QueryLimitConstraint._create('limit', limit, "F" /* LimitType.First */);
}
/**
* Creates a {@link QueryLimitConstraint} that only returns the last matching
* documents.
*
* You must specify at least one `orderBy` clause for `limitToLast` queries,
* otherwise an exception will be thrown during execution.
*
* @param limit - The maximum number of items to return.
* @returns The created {@link QueryLimitConstraint}.
*/
function limitToLast(limit) {
validatePositiveNumber('limitToLast', limit);
return QueryLimitConstraint._create('limitToLast', limit, "L" /* LimitType.Last */);
}
/**
* A `QueryStartAtConstraint` is used to exclude documents from the start of a
* result set returned by a Firestore query.
* `QueryStartAtConstraint`s are created by invoking {@link (startAt:1)} or
* {@link (startAfter:1)} and can then be passed to {@link (query:1)} to create a
* new query instance that also contains this `QueryStartAtConstraint`.
*/
class QueryStartAtConstraint extends QueryConstraint {
/**
* @internal
*/
constructor(
/** The type of this query constraint */
type, _docOrFields, _inclusive) {
super();
this.type = type;
this._docOrFields = _docOrFields;
this._inclusive = _inclusive;
}
static _create(type, _docOrFields, _inclusive) {
return new QueryStartAtConstraint(type, _docOrFields, _inclusive);
}
_apply(query) {
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
return new Query(query.firestore, query.converter, queryWithStartAt(query._query, bound));
}
}
function startAt(...docOrFields) {
return QueryStartAtConstraint._create('startAt', docOrFields,
/*inclusive=*/ true);
}
function startAfter(...docOrFields) {
return QueryStartAtConstraint._create('startAfter', docOrFields,
/*inclusive=*/ false);
}
/**
* A `QueryEndAtConstraint` is used to exclude documents from the end of a
* result set returned by a Firestore query.
* `QueryEndAtConstraint`s are created by invoking {@link (endAt:1)} or
* {@link (endBefore:1)} and can then be passed to {@link (query:1)} to create a new
* query instance that also contains this `QueryEndAtConstraint`.
*/
class QueryEndAtConstraint extends QueryConstraint {
/**
* @internal
*/
constructor(
/** The type of this query constraint */
type, _docOrFields, _inclusive) {
super();
this.type = type;
this._docOrFields = _docOrFields;
this._inclusive = _inclusive;
}
static _create(type, _docOrFields, _inclusive) {
return new QueryEndAtConstraint(type, _docOrFields, _inclusive);
}
_apply(query) {
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
return new Query(query.firestore, query.converter, queryWithEndAt(query._query, bound));
}
}
function endBefore(...docOrFields) {
return QueryEndAtConstraint._create('endBefore', docOrFields,
/*inclusive=*/ false);
}
function endAt(...docOrFields) {
return QueryEndAtConstraint._create('endAt', docOrFields,
/*inclusive=*/ true);
}
/** Helper function to create a bound from a document or fields */
function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive) {
docOrFields[0] = getModularInstance(docOrFields[0]);
if (docOrFields[0] instanceof DocumentSnapshot$1) {
return newQueryBoundFromDocument(query._query, query.firestore._databaseId, methodName, docOrFields[0]._document, inclusive);
}
else {
const reader = newUserDataReader(query.firestore);
return newQueryBoundFromFields(query._query, query.firestore._databaseId, reader, methodName, docOrFields, inclusive);
}
}
function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op, value) {
let fieldValue;
if (fieldPath.isKeyField()) {
if (op === "array-contains" /* Operator.ARRAY_CONTAINS */ || op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid Query. You can't perform '${op}' queries on documentId().`);
}
else if (op === "in" /* Operator.IN */ || op === "not-in" /* Operator.NOT_IN */) {
validateDisjunctiveFilterElements(value, op);
const referenceList = [];
for (const arrayValue of value) {
referenceList.push(parseDocumentIdValue(databaseId, query, arrayValue));
}
fieldValue = { arrayValue: { values: referenceList } };
}
else {
fieldValue = parseDocumentIdValue(databaseId, query, value);
}
}
else {
if (op === "in" /* Operator.IN */ ||
op === "not-in" /* Operator.NOT_IN */ ||
op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
validateDisjunctiveFilterElements(value, op);
}
fieldValue = parseQueryValue(dataReader, methodName, value,
/* allowArrays= */ op === "in" /* Operator.IN */ || op === "not-in" /* Operator.NOT_IN */);
}
const filter = FieldFilter.create(fieldPath, op, fieldValue);
return filter;
}
function newQueryOrderBy(query, fieldPath, direction) {
if (query.startAt !== null) {
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Invalid query. You must not call startAt() or startAfter() before ' +
'calling orderBy().');
}
if (query.endAt !== null) {
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Invalid query. You must not call endAt() or endBefore() before ' +
'calling orderBy().');
}
const orderBy = new OrderBy(fieldPath, direction);
return orderBy;
}
/**
* Create a `Bound` from a query and a document.
*
* Note that the `Bound` will always include the key of the document
* and so only the provided document will compare equal to the returned
* position.
*
* Will throw if the document does not contain all fields of the order by
* of the query or if any of the fields in the order by are an uncommitted
* server timestamp.
*/
function newQueryBoundFromDocument(query, databaseId, methodName, doc, inclusive) {
if (!doc) {
throw new FirestoreError(Code.NOT_FOUND, `Can't use a DocumentSnapshot that doesn't exist for ` +
`${methodName}().`);
}
const components = [];
// Because people expect to continue/end a query at the exact document
// provided, we need to use the implicit sort order rather than the explicit
// sort order, because it's guaranteed to contain the document key. That way
// the position becomes unambiguous and the query continues/ends exactly at
// the provided document. Without the key (by using the explicit sort
// orders), multiple documents could match the position, yielding duplicate
// results.
for (const orderBy of queryNormalizedOrderBy(query)) {
if (orderBy.field.isKeyField()) {
components.push(refValue(databaseId, doc.key));
}
else {
const value = doc.data.field(orderBy.field);
if (isServerTimestamp(value)) {
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Invalid query. You are trying to start or end a query using a ' +
'document for which the field "' +
orderBy.field +
'" is an uncommitted server timestamp. (Since the value of ' +
'this field is unknown, you cannot start/end a query with it.)');
}
else if (value !== null) {
components.push(value);
}
else {
const field = orderBy.field.canonicalString();
throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. You are trying to start or end a query using a ` +
`document for which the field '${field}' (used as the ` +
`orderBy) does not exist.`);
}
}
}
return new Bound(components, inclusive);
}
/**
* Converts a list of field values to a `Bound` for the given query.
*/
function newQueryBoundFromFields(query, databaseId, dataReader, methodName, values, inclusive) {
// Use explicit order by's because it has to match the query the user made
const orderBy = query.explicitOrderBy;
if (values.length > orderBy.length) {
throw new FirestoreError(Code.INVALID_ARGUMENT, `Too many arguments provided to ${methodName}(). ` +
`The number of arguments must be less than or equal to the ` +
`number of orderBy() clauses`);
}
const components = [];
for (let i = 0; i < values.length; i++) {
const rawValue = values[i];
const orderByComponent = orderBy[i];
if (orderByComponent.field.isKeyField()) {
if (typeof rawValue !== 'string') {
throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. Expected a string for document ID in ` +
`${methodName}(), but got a ${typeof rawValue}`);
}
if (!isCollectionGroupQuery(query) && rawValue.indexOf('/') !== -1) {
throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. When querying a collection and ordering by documentId(), ` +
`the value passed to ${methodName}() must be a plain document ID, but ` +
`'${rawValue}' contains a slash.`);
}
const path = query.path.child(ResourcePath.fromString(rawValue));
if (!DocumentKey.isDocumentKey(path)) {
throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. When querying a collection group and ordering by ` +
`documentId(), the value passed to ${methodName}() must result in a ` +
`valid document path, but '${path}' is not because it contains an odd number ` +
`of segments.`);
}
const key = new DocumentKey(path);
components.push(refValue(databaseId, key));
}
else {
const wrapped = parseQueryValue(dataReader, methodName, rawValue);
components.push(wrapped);
}
}
return new Bound(components, inclusive);
}
/**
* Parses the given `documentIdValue` into a `ReferenceValue`, throwing
* appropriate errors if the value is anything other than a `DocumentReference`
* or `string`, or if the string is malformed.
*/
function parseDocumentIdValue(databaseId, query, documentIdValue) {
documentIdValue = getModularInstance(documentIdValue);
if (typeof documentIdValue === 'string') {
if (documentIdValue === '') {
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Invalid query. When querying with documentId(), you ' +
'must provide a valid document ID, but it was an empty string.');
}
if (!isCollectionGroupQuery(query) && documentIdValue.indexOf('/') !== -1) {
throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. When querying a collection by ` +
`documentId(), you must provide a plain document ID, but ` +
`'${documentIdValue}' contains a '/' character.`);
}
const path = query.path.child(ResourcePath.fromString(documentIdValue));
if (!DocumentKey.isDocumentKey(path)) {
throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. When querying a collection group by ` +
`documentId(), the value provided must result in a valid document path, ` +
`but '${path}' is not because it has an odd number of segments (${path.length}).`);
}
return refValue(databaseId, new DocumentKey(path));
}
else if (documentIdValue instanceof DocumentReference) {
return refValue(databaseId, documentIdValue._key);
}
else {
throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. When querying with documentId(), you must provide a valid ` +
`string or a DocumentReference, but it was: ` +
`${valueDescription(documentIdValue)}.`);
}
}
/**
* Validates that the value passed into a disjunctive filter satisfies all
* array requirements.
*/
function validateDisjunctiveFilterElements(value, operator) {
if (!Array.isArray(value) || value.length === 0) {
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Invalid Query. A non-empty array is required for ' +
`'${operator.toString()}' filters.`);
}
}
/**
* Given an operator, returns the set of operators that cannot be used with it.
*
* This is not a comprehensive check, and this function should be removed in the
* long term. Validations should occur in the Firestore backend.
*
* Operators in a query must adhere to the following set of rules:
* 1. Only one inequality per query.
* 2. `NOT_IN` cannot be used with array, disjunctive, or `NOT_EQUAL` operators.
*/
function conflictingOps(op) {
switch (op) {
case "!=" /* Operator.NOT_EQUAL */:
return ["!=" /* Operator.NOT_EQUAL */, "not-in" /* Operator.NOT_IN */];
case "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */:
case "in" /* Operator.IN */:
return ["not-in" /* Operator.NOT_IN */];
case "not-in" /* Operator.NOT_IN */:
return [
"array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
"in" /* Operator.IN */,
"not-in" /* Operator.NOT_IN */,
"!=" /* Operator.NOT_EQUAL */
];
default:
return [];
}
}
function validateNewFieldFilter(query, fieldFilter) {
const conflictingOp = findOpInsideFilters(query.filters, conflictingOps(fieldFilter.op));
if (conflictingOp !== null) {
// Special case when it's a duplicate op to give a slightly clearer error message.
if (conflictingOp === fieldFilter.op) {
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Invalid query. You cannot use more than one ' +
`'${fieldFilter.op.toString()}' filter.`);
}
else {
throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. You cannot use '${fieldFilter.op.toString()}' filters ` +
`with '${conflictingOp.toString()}' filters.`);
}
}
}
function validateNewFilter(query, filter) {
let testQuery = query;
const subFilters = filter.getFlattenedFilters();
for (const subFilter of subFilters) {
validateNewFieldFilter(testQuery, subFilter);
testQuery = queryWithAddedFilter(testQuery, subFilter);
}
}
// Checks if any of the provided filter operators are included in the given list of filters and
// returns the first one that is, or null if none are.
function findOpInsideFilters(filters, operators) {
for (const filter of filters) {
for (const fieldFilter of filter.getFlattenedFilters()) {
if (operators.indexOf(fieldFilter.op) >= 0) {
return fieldFilter.op;
}
}
}
return null;
}
function validateQueryFilterConstraint(functionName, queryConstraint) {
if (!(queryConstraint instanceof QueryFieldFilterConstraint) &&
!(queryConstraint instanceof QueryCompositeFilterConstraint)) {
throw new FirestoreError(Code.INVALID_ARGUMENT, `Function ${functionName}() requires AppliableConstraints created with a call to 'where(...)', 'or(...)', or 'and(...)'.`);
}
}
function validateQueryConstraintArray(queryConstraint) {
const compositeFilterCount = queryConstraint.filter(filter => filter instanceof QueryCompositeFilterConstraint).length;
const fieldFilterCount = queryConstraint.filter(filter => filter instanceof QueryFieldFilterConstraint).length;
if (compositeFilterCount > 1 ||
(compositeFilterCount > 0 && fieldFilterCount > 0)) {
throw new FirestoreError(Code.INVALID_ARGUMENT, 'InvalidQuery. When using composite filters, you cannot use ' +
'more than one filter at the top level. Consider nesting the multiple ' +
'filters within an `and(...)` statement. For example: ' +
'change `query(query, where(...), or(...))` to ' +
'`query(query, and(where(...), or(...)))`.');
}
}
/**
* @license
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Converts custom model object of type T into `DocumentData` by applying the
* converter if it exists.
*
* This function is used when converting user objects to `DocumentData`
* because we want to provide the user with a more specific error message if
* their `set()` or fails due to invalid data originating from a `toFirestore()`
* call.
*/
function applyFirestoreDataConverter(converter, value, options) {
let convertedValue;
if (converter) {
if (options && (options.merge || options.mergeFields)) {
// Cast to `any` in order to satisfy the union type constraint on
// toFirestore().
// eslint-disable-next-line @typescript-eslint/no-explicit-any
convertedValue = converter.toFirestore(value, options);
}
else {
convertedValue = converter.toFirestore(value);
}
}
else {
convertedValue = value;
}
return convertedValue;
}
class LiteUserDataWriter extends AbstractUserDataWriter {
constructor(firestore) {
super();
this.firestore = firestore;
}
convertBytes(bytes) {
return new Bytes(bytes);
}
convertReference(name) {
const key = this.convertDocumentKey(name, this.firestore._databaseId);
return new DocumentReference(this.firestore, /* converter= */ null, key);
}
}
/**
* @license
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Create an AggregateField object that can be used to compute the sum of
* a specified field over a range of documents in the result set of a query.
* @param field - Specifies the field to sum across the result set.
*/
function sum(field) {
return new AggregateField('sum', fieldPathFromArgument('sum', field));
}
/**
* Create an AggregateField object that can be used to compute the average of
* a specified field over a range of documents in the result set of a query.
* @param field - Specifies the field to average across the result set.
*/
function average(field) {
return new AggregateField('avg', fieldPathFromArgument('average', field));
}
/**
* Create an AggregateField object that can be used to compute the count of
* documents in the result set of a query.
*/
function count() {
return new AggregateField('count');
}
/**
* Compares two 'AggregateField` instances for equality.
*
* @param left - Compare this AggregateField to the `right`.
* @param right - Compare this AggregateField to the `left`.
*/
function aggregateFieldEqual(left, right) {
return (left instanceof AggregateField &&
right instanceof AggregateField &&
left.aggregateType === right.aggregateType &&
left._internalFieldPath?.canonicalString() ===
right._internalFieldPath?.canonicalString());
}
/**
* Compares two `AggregateQuerySnapshot` instances for equality.
*
* Two `AggregateQuerySnapshot` instances are considered "equal" if they have
* underlying queries that compare equal, and the same data.
*
* @param left - The first `AggregateQuerySnapshot` to compare.
* @param right - The second `AggregateQuerySnapshot` to compare.
*
* @returns `true` if the objects are "equal", as defined above, or `false`
* otherwise.
*/
function aggregateQuerySnapshotEqual(left, right) {
return (queryEqual(left.query, right.query) && deepEqual(left.data(), right.data()));
}
/**
* @license
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Calculates the number of documents in the result set of the given query
* without actually downloading the documents.
*
* Using this function to count the documents is efficient because only the
* final count, not the documents' data, is downloaded. This function can
* count the documents in cases where the result set is prohibitively large to
* download entirely (thousands of documents).
*
* The result received from the server is presented, unaltered, without
* considering any local state. That is, documents in the local cache are not
* taken into consideration, neither are local modifications not yet
* synchronized with the server. Previously-downloaded results, if any, are not
* used. Every invocation of this function necessarily involves a round trip to
* the server.
*
* @param query - The query whose result set size is calculated.
* @returns A Promise that will be resolved with the count; the count can be
* retrieved from `snapshot.data().count`, where `snapshot` is the
* `AggregateQuerySnapshot` to which the returned Promise resolves.
*/
function getCountFromServer(query) {
const countQuerySpec = {
count: count()
};
return getAggregateFromServer(query, countQuerySpec);
}
/**
* Calculates the specified aggregations over the documents in the result
* set of the given query without actually downloading the documents.
*
* Using this function to perform aggregations is efficient because only the
* final aggregation values, not the documents' data, are downloaded. This
* function can perform aggregations of the documents in cases where the result
* set is prohibitively large to download entirely (thousands of documents).
*
* The result received from the server is presented, unaltered, without
* considering any local state. That is, documents in the local cache are not
* taken into consideration, neither are local modifications not yet
* synchronized with the server. Previously-downloaded results, if any, are not
* used. Every invocation of this function necessarily involves a round trip to
* the server.
*
* @param query - The query whose result set is aggregated over.
* @param aggregateSpec - An `AggregateSpec` object that specifies the aggregates
* to perform over the result set. The AggregateSpec specifies aliases for each
* aggregate, which can be used to retrieve the aggregate result.
* @example
* ```typescript
* const aggregateSnapshot = await getAggregateFromServer(query, {
* countOfDocs: count(),
* totalHours: sum('hours'),
* averageScore: average('score')
* });
*
* const countOfDocs: number = aggregateSnapshot.data().countOfDocs;
* const totalHours: number = aggregateSnapshot.data().totalHours;
* const averageScore: number | null = aggregateSnapshot.data().averageScore;
* ```
*/
function getAggregateFromServer(query, aggregateSpec) {
const firestore = cast(query.firestore, Firestore);
const client = ensureFirestoreConfigured(firestore);
const internalAggregates = mapToArray(aggregateSpec, (aggregate, alias) => {
return new AggregateImpl(alias, aggregate.aggregateType, aggregate._internalFieldPath);
});
// Run the aggregation and convert the results
return firestoreClientRunAggregateQuery(client, query._query, internalAggregates).then(aggregateResult => convertToAggregateQuerySnapshot(firestore, query, aggregateResult));
}
/**
* Converts the core aggregation result to an `AggregateQuerySnapshot`
* that can be returned to the consumer.
* @param query
* @param aggregateResult - Core aggregation result
* @internal
*/
function convertToAggregateQuerySnapshot(firestore, query, aggregateResult) {
const userDataWriter = new ExpUserDataWriter(firestore);
const querySnapshot = new AggregateQuerySnapshot(query, userDataWriter, aggregateResult);
return querySnapshot;
}
/**
* @license
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class MemoryLocalCacheImpl {
constructor(settings) {
this.kind = 'memory';
this._onlineComponentProvider = OnlineComponentProvider.provider;
if (settings?.garbageCollector) {
this._offlineComponentProvider =
settings.garbageCollector._offlineComponentProvider;
}
else {
this._offlineComponentProvider