@firebase/firestore
Version:
The Cloud Firestore component of the Firebase JS SDK.
1,276 lines (1,217 loc) • 995 kB
JavaScript
import { _isFirebaseServerApp as e, _getProvider, getApp as t, _removeServiceInstance as n, _registerComponent as r, registerVersion as i, SDK_VERSION as s } from "@firebase/app";
import { Component as o } from "@firebase/component";
import { Logger as _, LogLevel as a } from "@firebase/logger";
import { FirebaseError as u, getGlobal as c, isIndexedDBAvailable as l, getUA as h, base64 as P, DecodeBase64StringError as T, isSafariOrWebkit as I, isSafari as E, isCloudWorkstation as d, pingServer as A, updateEmulatorBanner as R, deepEqual as V, createMockUserToken as m, getModularInstance as f, getDefaultEmulatorHostnameAndPort as g } from "@firebase/util";
import { Integer as p, Md5 as y } from "@firebase/webchannel-wrapper/bloom-blob";
import { XhrIo as w, EventType as S, ErrorCode as b, createWebChannelTransport as D, getStatEventTarget as C, WebChannel as v, Event as F, Stat as M } from "@firebase/webchannel-wrapper/webchannel-blob";
const x = "@firebase/firestore", O = "4.9.0";
/**
* @license
* Copyright 2017 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.
*/
/**
* Simple wrapper around a nullable UID. Mostly exists to make code more
* readable.
*/
class User {
constructor(e) {
this.uid = e;
}
isAuthenticated() {
return null != this.uid;
}
/**
* Returns a key representing this user, suitable for inclusion in a
* dictionary.
*/ toKey() {
return this.isAuthenticated() ? "uid:" + this.uid : "anonymous-user";
}
isEqual(e) {
return e.uid === this.uid;
}
}
/** A user with a null UID. */ User.UNAUTHENTICATED = new User(null),
// TODO(mikelehen): Look into getting a proper uid-equivalent for
// non-FirebaseAuth providers.
User.GOOGLE_CREDENTIALS = new User("google-credentials-uid"), User.FIRST_PARTY = new User("first-party-uid"),
User.MOCK_USER = new User("mock-user");
/**
* @license
* Copyright 2017 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.
*/
let N = "12.0.0";
/**
* @license
* Copyright 2017 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.
*/
const B = new _("@firebase/firestore");
// Helper methods are needed because variables can't be exported as read/write
function __PRIVATE_getLogLevel() {
return B.logLevel;
}
/**
* Sets the verbosity of Cloud Firestore logs (debug, error, or silent).
*
* @param logLevel - The verbosity you set for activity and error logging. Can
* be any of the following values:
*
* <ul>
* <li>`debug` for the most verbose logging level, primarily for
* debugging.</li>
* <li>`error` to log errors only.</li>
* <li><code>`silent` to turn off logging.</li>
* </ul>
*/ function setLogLevel(e) {
B.setLogLevel(e);
}
function __PRIVATE_logDebug(e, ...t) {
if (B.logLevel <= a.DEBUG) {
const n = t.map(__PRIVATE_argToString);
B.debug(`Firestore (${N}): ${e}`, ...n);
}
}
function __PRIVATE_logError(e, ...t) {
if (B.logLevel <= a.ERROR) {
const n = t.map(__PRIVATE_argToString);
B.error(`Firestore (${N}): ${e}`, ...n);
}
}
/**
* @internal
*/ function __PRIVATE_logWarn(e, ...t) {
if (B.logLevel <= a.WARN) {
const n = t.map(__PRIVATE_argToString);
B.warn(`Firestore (${N}): ${e}`, ...n);
}
}
/**
* Converts an additional log parameter to a string representation.
*/ function __PRIVATE_argToString(e) {
if ("string" == typeof e) return e;
try {
/**
* @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.
*/
/** Formats an object as a JSON string, suitable for logging. */
return function __PRIVATE_formatJSON(e) {
return JSON.stringify(e);
}(e);
} catch (t) {
// Converting to JSON failed, just log the object directly
return e;
}
}
/**
* @license
* Copyright 2017 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 fail(e, t, n) {
let r = "Unexpected state";
"string" == typeof t ? r = t : n = t, __PRIVATE__fail(e, r, n);
}
function __PRIVATE__fail(e, t, n) {
// Log the failure in addition to throw an exception, just in case the
// exception is swallowed.
let r = `FIRESTORE (${N}) INTERNAL ASSERTION FAILED: ${t} (ID: ${e.toString(16)})`;
if (void 0 !== n) try {
r += " CONTEXT: " + JSON.stringify(n);
} catch (e) {
r += " CONTEXT: " + n;
}
// NOTE: We don't use FirestoreError here because these are internal failures
// that cannot be handled by the user. (Also it would create a circular
// dependency between the error and assert modules which doesn't work.)
throw __PRIVATE_logError(r), new Error(r);
}
function __PRIVATE_hardAssert(e, t, n, r) {
let i = "Unexpected state";
"string" == typeof n ? i = n : r = n, e || __PRIVATE__fail(t, i, r);
}
/**
* Fails if the given assertion condition is false, throwing an Error with the
* given message if it did.
*
* The code of callsites invoking this function are stripped out in production
* builds. Any side-effects of code within the debugAssert() invocation will not
* happen in this case.
*
* @internal
*/ function __PRIVATE_debugAssert(e, t) {
e || fail(57014, t);
}
/**
* Casts `obj` to `T`. In non-production builds, verifies that `obj` is an
* instance of `T` before casting.
*/ function __PRIVATE_debugCast(e,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
t) {
return e;
}
/**
* @license
* Copyright 2017 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.
*/ const L = {
// Causes are copied from:
// https://github.com/grpc/grpc/blob/bceec94ea4fc5f0085d81235d8e1c06798dc341a/include/grpc%2B%2B/impl/codegen/status_code_enum.h
/** Not an error; returned on success. */
OK: "ok",
/** The operation was cancelled (typically by the caller). */
CANCELLED: "cancelled",
/** Unknown error or an error from a different error domain. */
UNKNOWN: "unknown",
/**
* Client specified an invalid argument. Note that this differs from
* FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments that are
* problematic regardless of the state of the system (e.g., a malformed file
* name).
*/
INVALID_ARGUMENT: "invalid-argument",
/**
* Deadline expired before operation could complete. For operations that
* change the state of the system, this error may be returned even if the
* operation has completed successfully. For example, a successful response
* from a server could have been delayed long enough for the deadline to
* expire.
*/
DEADLINE_EXCEEDED: "deadline-exceeded",
/** Some requested entity (e.g., file or directory) was not found. */
NOT_FOUND: "not-found",
/**
* Some entity that we attempted to create (e.g., file or directory) already
* exists.
*/
ALREADY_EXISTS: "already-exists",
/**
* The caller does not have permission to execute the specified operation.
* PERMISSION_DENIED must not be used for rejections caused by exhausting
* some resource (use RESOURCE_EXHAUSTED instead for those errors).
* PERMISSION_DENIED must not be used if the caller cannot be identified
* (use UNAUTHENTICATED instead for those errors).
*/
PERMISSION_DENIED: "permission-denied",
/**
* The request does not have valid authentication credentials for the
* operation.
*/
UNAUTHENTICATED: "unauthenticated",
/**
* Some resource has been exhausted, perhaps a per-user quota, or perhaps the
* entire file system is out of space.
*/
RESOURCE_EXHAUSTED: "resource-exhausted",
/**
* Operation was rejected because the system is not in a state required for
* the operation's execution. For example, directory to be deleted may be
* non-empty, an rmdir operation is applied to a non-directory, etc.
*
* A litmus test that may help a service implementor in deciding
* between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE:
* (a) Use UNAVAILABLE if the client can retry just the failing call.
* (b) Use ABORTED if the client should retry at a higher-level
* (e.g., restarting a read-modify-write sequence).
* (c) Use FAILED_PRECONDITION if the client should not retry until
* the system state has been explicitly fixed. E.g., if an "rmdir"
* fails because the directory is non-empty, FAILED_PRECONDITION
* should be returned since the client should not retry unless
* they have first fixed up the directory by deleting files from it.
* (d) Use FAILED_PRECONDITION if the client performs conditional
* REST Get/Update/Delete on a resource and the resource on the
* server does not match the condition. E.g., conflicting
* read-modify-write on the same resource.
*/
FAILED_PRECONDITION: "failed-precondition",
/**
* The operation was aborted, typically due to a concurrency issue like
* sequencer check failures, transaction aborts, etc.
*
* See litmus test above for deciding between FAILED_PRECONDITION, ABORTED,
* and UNAVAILABLE.
*/
ABORTED: "aborted",
/**
* Operation was attempted past the valid range. E.g., seeking or reading
* past end of file.
*
* Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed
* if the system state changes. For example, a 32-bit file system will
* generate INVALID_ARGUMENT if asked to read at an offset that is not in the
* range [0,2^32-1], but it will generate OUT_OF_RANGE if asked to read from
* an offset past the current file size.
*
* There is a fair bit of overlap between FAILED_PRECONDITION and
* OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific error)
* when it applies so that callers who are iterating through a space can
* easily look for an OUT_OF_RANGE error to detect when they are done.
*/
OUT_OF_RANGE: "out-of-range",
/** Operation is not implemented or not supported/enabled in this service. */
UNIMPLEMENTED: "unimplemented",
/**
* Internal errors. Means some invariants expected by underlying System has
* been broken. If you see one of these errors, Something is very broken.
*/
INTERNAL: "internal",
/**
* The service is currently unavailable. This is a most likely a transient
* condition and may be corrected by retrying with a backoff.
*
* See litmus test above for deciding between FAILED_PRECONDITION, ABORTED,
* and UNAVAILABLE.
*/
UNAVAILABLE: "unavailable",
/** Unrecoverable data loss or corruption. */
DATA_LOSS: "data-loss"
};
/** An error returned by a Firestore operation. */ class FirestoreError extends u {
/** @hideconstructor */
constructor(
/**
* The backend error code associated with this error.
*/
e,
/**
* A custom error description.
*/
t) {
super(e, t), this.code = e, this.message = t,
// HACK: We write a toString property directly because Error is not a real
// class and so inheritance does not work correctly. We could alternatively
// do the same "back-door inheritance" trick that FirebaseError does.
this.toString = () => `${this.name}: [code=${this.code}]: ${this.message}`;
}
}
/**
* @license
* Copyright 2017 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 __PRIVATE_Deferred {
constructor() {
this.promise = new Promise(((e, t) => {
this.resolve = e, this.reject = t;
}));
}
}
/**
* @license
* Copyright 2017 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 __PRIVATE_OAuthToken {
constructor(e, t) {
this.user = t, this.type = "OAuth", this.headers = new Map, this.headers.set("Authorization", `Bearer ${e}`);
}
}
/**
* A CredentialsProvider that always yields an empty token.
* @internal
*/ class __PRIVATE_EmptyAuthCredentialsProvider {
getToken() {
return Promise.resolve(null);
}
invalidateToken() {}
start(e, t) {
// Fire with initial user.
e.enqueueRetryable((() => t(User.UNAUTHENTICATED)));
}
shutdown() {}
}
/**
* A CredentialsProvider that always returns a constant token. Used for
* emulator token mocking.
*/ class __PRIVATE_EmulatorAuthCredentialsProvider {
constructor(e) {
this.token = e,
/**
* Stores the listener registered with setChangeListener()
* This isn't actually necessary since the UID never changes, but we use this
* to verify the listen contract is adhered to in tests.
*/
this.changeListener = null;
}
getToken() {
return Promise.resolve(this.token);
}
invalidateToken() {}
start(e, t) {
this.changeListener = t,
// Fire with initial user.
e.enqueueRetryable((() => t(this.token.user)));
}
shutdown() {
this.changeListener = null;
}
}
class __PRIVATE_FirebaseAuthCredentialsProvider {
constructor(e) {
this.t = e,
/** Tracks the current User. */
this.currentUser = User.UNAUTHENTICATED,
/**
* Counter used to detect if the token changed while a getToken request was
* outstanding.
*/
this.i = 0, this.forceRefresh = !1, this.auth = null;
}
start(e, t) {
__PRIVATE_hardAssert(void 0 === this.o, 42304);
let n = this.i;
// A change listener that prevents double-firing for the same token change.
const __PRIVATE_guardedChangeListener = e => this.i !== n ? (n = this.i,
t(e)) : Promise.resolve();
// A promise that can be waited on to block on the next token change.
// This promise is re-created after each change.
let r = new __PRIVATE_Deferred;
this.o = () => {
this.i++, this.currentUser = this.u(), r.resolve(), r = new __PRIVATE_Deferred,
e.enqueueRetryable((() => __PRIVATE_guardedChangeListener(this.currentUser)));
};
const __PRIVATE_awaitNextToken = () => {
const t = r;
e.enqueueRetryable((async () => {
await t.promise, await __PRIVATE_guardedChangeListener(this.currentUser);
}));
}, __PRIVATE_registerAuth = e => {
__PRIVATE_logDebug("FirebaseAuthCredentialsProvider", "Auth detected"), this.auth = e,
this.o && (this.auth.addAuthTokenListener(this.o), __PRIVATE_awaitNextToken());
};
this.t.onInit((e => __PRIVATE_registerAuth(e))),
// Our users can initialize Auth right after Firestore, so we give it
// a chance to register itself with the component framework before we
// determine whether to start up in unauthenticated mode.
setTimeout((() => {
if (!this.auth) {
const e = this.t.getImmediate({
optional: !0
});
e ? __PRIVATE_registerAuth(e) : (
// If auth is still not available, proceed with `null` user
__PRIVATE_logDebug("FirebaseAuthCredentialsProvider", "Auth not yet detected"),
r.resolve(), r = new __PRIVATE_Deferred);
}
}), 0), __PRIVATE_awaitNextToken();
}
getToken() {
// Take note of the current value of the tokenCounter so that this method
// can fail (with an ABORTED error) if there is a token change while the
// request is outstanding.
const e = this.i, t = this.forceRefresh;
return this.forceRefresh = !1, this.auth ? this.auth.getToken(t).then((t =>
// Cancel the request since the token changed while the request was
// outstanding so the response is potentially for a previous user (which
// user, we can't be sure).
this.i !== e ? (__PRIVATE_logDebug("FirebaseAuthCredentialsProvider", "getToken aborted due to token change."),
this.getToken()) : t ? (__PRIVATE_hardAssert("string" == typeof t.accessToken, 31837, {
l: t
}), new __PRIVATE_OAuthToken(t.accessToken, this.currentUser)) : null)) : Promise.resolve(null);
}
invalidateToken() {
this.forceRefresh = !0;
}
shutdown() {
this.auth && this.o && this.auth.removeAuthTokenListener(this.o), this.o = void 0;
}
// Auth.getUid() can return null even with a user logged in. It is because
// getUid() is synchronous, but the auth code populating Uid is asynchronous.
// This method should only be called in the AuthTokenListener callback
// to guarantee to get the actual user.
u() {
const e = this.auth && this.auth.getUid();
return __PRIVATE_hardAssert(null === e || "string" == typeof e, 2055, {
h: e
}), new User(e);
}
}
/*
* FirstPartyToken provides a fresh token each time its value
* is requested, because if the token is too old, requests will be rejected.
* Technically this may no longer be necessary since the SDK should gracefully
* recover from unauthenticated errors (see b/33147818 for context), but it's
* safer to keep the implementation as-is.
*/ class __PRIVATE_FirstPartyToken {
constructor(e, t, n) {
this.P = e, this.T = t, this.I = n, this.type = "FirstParty", this.user = User.FIRST_PARTY,
this.A = new Map;
}
/**
* Gets an authorization token, using a provided factory function, or return
* null.
*/ R() {
return this.I ? this.I() : null;
}
get headers() {
this.A.set("X-Goog-AuthUser", this.P);
// Use array notation to prevent minification
const e = this.R();
return e && this.A.set("Authorization", e), this.T && this.A.set("X-Goog-Iam-Authorization-Token", this.T),
this.A;
}
}
/*
* Provides user credentials required for the Firestore JavaScript SDK
* to authenticate the user, using technique that is only available
* to applications hosted by Google.
*/ class __PRIVATE_FirstPartyAuthCredentialsProvider {
constructor(e, t, n) {
this.P = e, this.T = t, this.I = n;
}
getToken() {
return Promise.resolve(new __PRIVATE_FirstPartyToken(this.P, this.T, this.I));
}
start(e, t) {
// Fire with initial uid.
e.enqueueRetryable((() => t(User.FIRST_PARTY)));
}
shutdown() {}
invalidateToken() {}
}
class AppCheckToken {
constructor(e) {
this.value = e, this.type = "AppCheck", this.headers = new Map, e && e.length > 0 && this.headers.set("x-firebase-appcheck", this.value);
}
}
class __PRIVATE_FirebaseAppCheckTokenProvider {
constructor(t, n) {
this.V = n, this.forceRefresh = !1, this.appCheck = null, this.m = null, this.p = null,
e(t) && t.settings.appCheckToken && (this.p = t.settings.appCheckToken);
}
start(e, t) {
__PRIVATE_hardAssert(void 0 === this.o, 3512);
const onTokenChanged = e => {
null != e.error && __PRIVATE_logDebug("FirebaseAppCheckTokenProvider", `Error getting App Check token; using placeholder token instead. Error: ${e.error.message}`);
const n = e.token !== this.m;
return this.m = e.token, __PRIVATE_logDebug("FirebaseAppCheckTokenProvider", `Received ${n ? "new" : "existing"} token.`),
n ? t(e.token) : Promise.resolve();
};
this.o = t => {
e.enqueueRetryable((() => onTokenChanged(t)));
};
const __PRIVATE_registerAppCheck = e => {
__PRIVATE_logDebug("FirebaseAppCheckTokenProvider", "AppCheck detected"), this.appCheck = e,
this.o && this.appCheck.addTokenListener(this.o);
};
this.V.onInit((e => __PRIVATE_registerAppCheck(e))),
// Our users can initialize AppCheck after Firestore, so we give it
// a chance to register itself with the component framework.
setTimeout((() => {
if (!this.appCheck) {
const e = this.V.getImmediate({
optional: !0
});
e ? __PRIVATE_registerAppCheck(e) :
// If AppCheck is still not available, proceed without it.
__PRIVATE_logDebug("FirebaseAppCheckTokenProvider", "AppCheck not yet detected");
}
}), 0);
}
getToken() {
if (this.p) return Promise.resolve(new AppCheckToken(this.p));
const e = this.forceRefresh;
return this.forceRefresh = !1, this.appCheck ? this.appCheck.getToken(e).then((e => e ? (__PRIVATE_hardAssert("string" == typeof e.token, 44558, {
tokenResult: e
}), this.m = e.token, new AppCheckToken(e.token)) : null)) : Promise.resolve(null);
}
invalidateToken() {
this.forceRefresh = !0;
}
shutdown() {
this.appCheck && this.o && this.appCheck.removeTokenListener(this.o), this.o = void 0;
}
}
/**
* An AppCheck token provider that always yields an empty token.
* @internal
*/ class __PRIVATE_EmptyAppCheckTokenProvider {
getToken() {
return Promise.resolve(new AppCheckToken(""));
}
invalidateToken() {}
start(e, t) {}
shutdown() {}
}
/**
* Builds a CredentialsProvider depending on the type of
* the credentials passed in.
*/
/**
* @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.
*/
/**
* Generates `nBytes` of random bytes.
*
* If `nBytes < 0` , an error will be thrown.
*/
function __PRIVATE_randomBytes(e) {
// Polyfills for IE and WebWorker by using `self` and `msCrypto` when `crypto` is not available.
const t =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
"undefined" != typeof self && (self.crypto || self.msCrypto), n = new Uint8Array(e);
if (t && "function" == typeof t.getRandomValues) t.getRandomValues(n); else
// Falls back to Math.random
for (let t = 0; t < e; t++) n[t] = Math.floor(256 * Math.random());
return n;
}
/**
* @license
* Copyright 2017 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 utility class for generating unique alphanumeric IDs of a specified length.
*
* @internal
* Exported internally for testing purposes.
*/ class __PRIVATE_AutoId {
static newId() {
// Alphanumeric characters
const e = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", t = 62 * Math.floor(256 / 62);
// The largest byte value that is a multiple of `char.length`.
let n = "";
for (;n.length < 20; ) {
const r = __PRIVATE_randomBytes(40);
for (let i = 0; i < r.length; ++i)
// Only accept values that are [0, maxMultiple), this ensures they can
// be evenly mapped to indices of `chars` via a modulo operation.
n.length < 20 && r[i] < t && (n += e.charAt(r[i] % 62));
}
return n;
}
}
function __PRIVATE_primitiveComparator(e, t) {
return e < t ? -1 : e > t ? 1 : 0;
}
/** Compare strings in UTF-8 encoded byte order */ function __PRIVATE_compareUtf8Strings(e, t) {
// Find the first differing character (a.k.a. "UTF-16 code unit") in the two strings and,
// if found, use that character to determine the relative ordering of the two strings as a
// whole. Comparing UTF-16 strings in UTF-8 byte order can be done simply and efficiently by
// comparing the UTF-16 code units (chars). This serendipitously works because of the way UTF-8
// and UTF-16 happen to represent Unicode code points.
// After finding the first pair of differing characters, there are two cases:
// Case 1: Both characters are non-surrogates (code points less than or equal to 0xFFFF) or
// both are surrogates from a surrogate pair (that collectively represent code points greater
// than 0xFFFF). In this case their numeric order as UTF-16 code units is the same as the
// lexicographical order of their corresponding UTF-8 byte sequences. A direct comparison is
// sufficient.
// Case 2: One character is a surrogate and the other is not. In this case the surrogate-
// containing string is always ordered after the non-surrogate. This is because surrogates are
// used to represent code points greater than 0xFFFF which have 4-byte UTF-8 representations
// and are lexicographically greater than the 1, 2, or 3-byte representations of code points
// less than or equal to 0xFFFF.
// An example of why Case 2 is required is comparing the following two Unicode code points:
// |-----------------------|------------|---------------------|-----------------|
// | Name | Code Point | UTF-8 Encoding | UTF-16 Encoding |
// |-----------------------|------------|---------------------|-----------------|
// | Replacement Character | U+FFFD | 0xEF 0xBF 0xBD | 0xFFFD |
// | Grinning Face | U+1F600 | 0xF0 0x9F 0x98 0x80 | 0xD83D 0xDE00 |
// |-----------------------|------------|---------------------|-----------------|
// A lexicographical comparison of the UTF-8 encodings of these code points would order
// "Replacement Character" _before_ "Grinning Face" because 0xEF is less than 0xF0. However, a
// direct comparison of the UTF-16 code units, as would be done in case 1, would erroneously
// produce the _opposite_ ordering, because 0xFFFD is _greater than_ 0xD83D. As it turns out,
// this relative ordering holds for all comparisons of UTF-16 code points requiring a surrogate
// pair with those that do not.
const n = Math.min(e.length, t.length);
for (let r = 0; r < n; r++) {
const n = e.charAt(r), i = t.charAt(r);
if (n !== i) return __PRIVATE_isSurrogate(n) === __PRIVATE_isSurrogate(i) ? __PRIVATE_primitiveComparator(n, i) : __PRIVATE_isSurrogate(n) ? 1 : -1;
}
// Use the lengths of the strings to determine the overall comparison result since either the
// strings were equal or one is a prefix of the other.
return __PRIVATE_primitiveComparator(e.length, t.length);
}
const k = 55296, q = 57343;
function __PRIVATE_isSurrogate(e) {
const t = e.charCodeAt(0);
return t >= k && t <= q;
}
/** Helper to compare arrays using isEqual(). */ function __PRIVATE_arrayEquals(e, t, n) {
return e.length === t.length && e.every(((e, r) => n(e, t[r])));
}
/**
* Returns the immediate lexicographically-following string. This is useful to
* construct an inclusive range for indexeddb iterators.
*/ function __PRIVATE_immediateSuccessor(e) {
// Return the input string, with an additional NUL byte appended.
return e + "\0";
}
/**
* @license
* Copyright 2017 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.
*/ const Q = "__name__";
/**
* Path represents an ordered sequence of string segments.
*/ class BasePath {
constructor(e, t, n) {
void 0 === t ? t = 0 : t > e.length && fail(637, {
offset: t,
range: e.length
}), void 0 === n ? n = e.length - t : n > e.length - t && fail(1746, {
length: n,
range: e.length - t
}), this.segments = e, this.offset = t, this.len = n;
}
get length() {
return this.len;
}
isEqual(e) {
return 0 === BasePath.comparator(this, e);
}
child(e) {
const t = this.segments.slice(this.offset, this.limit());
return e instanceof BasePath ? e.forEach((e => {
t.push(e);
})) : t.push(e), this.construct(t);
}
/** The index of one past the last segment of the path. */ limit() {
return this.offset + this.length;
}
popFirst(e) {
return e = void 0 === e ? 1 : e, this.construct(this.segments, this.offset + e, this.length - e);
}
popLast() {
return this.construct(this.segments, this.offset, this.length - 1);
}
firstSegment() {
return this.segments[this.offset];
}
lastSegment() {
return this.get(this.length - 1);
}
get(e) {
return this.segments[this.offset + e];
}
isEmpty() {
return 0 === this.length;
}
isPrefixOf(e) {
if (e.length < this.length) return !1;
for (let t = 0; t < this.length; t++) if (this.get(t) !== e.get(t)) return !1;
return !0;
}
isImmediateParentOf(e) {
if (this.length + 1 !== e.length) return !1;
for (let t = 0; t < this.length; t++) if (this.get(t) !== e.get(t)) return !1;
return !0;
}
forEach(e) {
for (let t = this.offset, n = this.limit(); t < n; t++) e(this.segments[t]);
}
toArray() {
return this.segments.slice(this.offset, this.limit());
}
/**
* Compare 2 paths segment by segment, prioritizing numeric IDs
* (e.g., "__id123__") in numeric ascending order, followed by string
* segments in lexicographical order.
*/ static comparator(e, t) {
const n = Math.min(e.length, t.length);
for (let r = 0; r < n; r++) {
const n = BasePath.compareSegments(e.get(r), t.get(r));
if (0 !== n) return n;
}
return __PRIVATE_primitiveComparator(e.length, t.length);
}
static compareSegments(e, t) {
const n = BasePath.isNumericId(e), r = BasePath.isNumericId(t);
return n && !r ? -1 : !n && r ? 1 : n && r ? BasePath.extractNumericId(e).compare(BasePath.extractNumericId(t)) : __PRIVATE_compareUtf8Strings(e, t);
}
// Checks if a segment is a numeric ID (starts with "__id" and ends with "__").
static isNumericId(e) {
return e.startsWith("__id") && e.endsWith("__");
}
static extractNumericId(e) {
return p.fromString(e.substring(4, e.length - 2));
}
}
/**
* A slash-separated path for navigating resources (documents and collections)
* within Firestore.
*
* @internal
*/ class ResourcePath extends BasePath {
construct(e, t, n) {
return new ResourcePath(e, t, n);
}
canonicalString() {
// NOTE: The client is ignorant of any path segments containing escape
// sequences (e.g. __id123__) and just passes them through raw (they exist
// for legacy reasons and should not be used frequently).
return this.toArray().join("/");
}
toString() {
return this.canonicalString();
}
/**
* Returns a string representation of this path
* where each path segment has been encoded with
* `encodeURIComponent`.
*/ toUriEncodedString() {
return this.toArray().map(encodeURIComponent).join("/");
}
/**
* Creates a resource path from the given slash-delimited string. If multiple
* arguments are provided, all components are combined. Leading and trailing
* slashes from all components are ignored.
*/ static fromString(...e) {
// NOTE: The client is ignorant of any path segments containing escape
// sequences (e.g. __id123__) and just passes them through raw (they exist
// for legacy reasons and should not be used frequently).
const t = [];
for (const n of e) {
if (n.indexOf("//") >= 0) throw new FirestoreError(L.INVALID_ARGUMENT, `Invalid segment (${n}). Paths must not contain // in them.`);
// Strip leading and trailing slashed.
t.push(...n.split("/").filter((e => e.length > 0)));
}
return new ResourcePath(t);
}
static emptyPath() {
return new ResourcePath([]);
}
}
const $ = /^[_a-zA-Z][_a-zA-Z0-9]*$/;
/**
* A dot-separated path for navigating sub-objects within a document.
* @internal
*/ class FieldPath$1 extends BasePath {
construct(e, t, n) {
return new FieldPath$1(e, t, n);
}
/**
* Returns true if the string could be used as a segment in a field path
* without escaping.
*/ static isValidIdentifier(e) {
return $.test(e);
}
canonicalString() {
return this.toArray().map((e => (e = e.replace(/\\/g, "\\\\").replace(/`/g, "\\`"),
FieldPath$1.isValidIdentifier(e) || (e = "`" + e + "`"), e))).join(".");
}
toString() {
return this.canonicalString();
}
/**
* Returns true if this field references the key of a document.
*/ isKeyField() {
return 1 === this.length && this.get(0) === Q;
}
/**
* The field designating the key of a document.
*/ static keyField() {
return new FieldPath$1([ Q ]);
}
/**
* Parses a field string from the given server-formatted string.
*
* - Splitting the empty string is not allowed (for now at least).
* - Empty segments within the string (e.g. if there are two consecutive
* separators) are not allowed.
*
* TODO(b/37244157): we should make this more strict. Right now, it allows
* non-identifier path components, even if they aren't escaped.
*/ static fromServerFormat(e) {
const t = [];
let n = "", r = 0;
const __PRIVATE_addCurrentSegment = () => {
if (0 === n.length) throw new FirestoreError(L.INVALID_ARGUMENT, `Invalid field path (${e}). Paths must not be empty, begin with '.', end with '.', or contain '..'`);
t.push(n), n = "";
};
let i = !1;
for (;r < e.length; ) {
const t = e[r];
if ("\\" === t) {
if (r + 1 === e.length) throw new FirestoreError(L.INVALID_ARGUMENT, "Path has trailing escape character: " + e);
const t = e[r + 1];
if ("\\" !== t && "." !== t && "`" !== t) throw new FirestoreError(L.INVALID_ARGUMENT, "Path has invalid escape sequence: " + e);
n += t, r += 2;
} else "`" === t ? (i = !i, r++) : "." !== t || i ? (n += t, r++) : (__PRIVATE_addCurrentSegment(),
r++);
}
if (__PRIVATE_addCurrentSegment(), i) throw new FirestoreError(L.INVALID_ARGUMENT, "Unterminated ` in path: " + e);
return new FieldPath$1(t);
}
static emptyPath() {
return new FieldPath$1([]);
}
}
/**
* @license
* Copyright 2017 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.
*/
/**
* @internal
*/ class DocumentKey {
constructor(e) {
this.path = e;
}
static fromPath(e) {
return new DocumentKey(ResourcePath.fromString(e));
}
static fromName(e) {
return new DocumentKey(ResourcePath.fromString(e).popFirst(5));
}
static empty() {
return new DocumentKey(ResourcePath.emptyPath());
}
get collectionGroup() {
return this.path.popLast().lastSegment();
}
/** Returns true if the document is in the specified collectionId. */ hasCollectionId(e) {
return this.path.length >= 2 && this.path.get(this.path.length - 2) === e;
}
/** Returns the collection group (i.e. the name of the parent collection) for this key. */ getCollectionGroup() {
return this.path.get(this.path.length - 2);
}
/** Returns the fully qualified path to the parent collection. */ getCollectionPath() {
return this.path.popLast();
}
isEqual(e) {
return null !== e && 0 === ResourcePath.comparator(this.path, e.path);
}
toString() {
return this.path.toString();
}
static comparator(e, t) {
return ResourcePath.comparator(e.path, t.path);
}
static isDocumentKey(e) {
return e.length % 2 == 0;
}
/**
* Creates and returns a new document key with the given segments.
*
* @param segments - The segments of the path to the document
* @returns A new instance of DocumentKey
*/ static fromSegments(e) {
return new DocumentKey(new ResourcePath(e.slice()));
}
}
/**
* @license
* Copyright 2017 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 __PRIVATE_validateNonEmptyArgument(e, t, n) {
if (!n) throw new FirestoreError(L.INVALID_ARGUMENT, `Function ${e}() cannot be called with an empty ${t}.`);
}
/**
* Validates that two boolean options are not set at the same time.
* @internal
*/ function __PRIVATE_validateIsNotUsedTogether(e, t, n, r) {
if (!0 === t && !0 === r) throw new FirestoreError(L.INVALID_ARGUMENT, `${e} and ${n} cannot be used together.`);
}
/**
* Validates that `path` refers to a document (indicated by the fact it contains
* an even numbers of segments).
*/ function __PRIVATE_validateDocumentPath(e) {
if (!DocumentKey.isDocumentKey(e)) throw new FirestoreError(L.INVALID_ARGUMENT, `Invalid document reference. Document references must have an even number of segments, but ${e} has ${e.length}.`);
}
/**
* Validates that `path` refers to a collection (indicated by the fact it
* contains an odd numbers of segments).
*/ function __PRIVATE_validateCollectionPath(e) {
if (DocumentKey.isDocumentKey(e)) throw new FirestoreError(L.INVALID_ARGUMENT, `Invalid collection reference. Collection references must have an odd number of segments, but ${e} has ${e.length}.`);
}
/**
* Returns true if it's a non-null object without a custom prototype
* (i.e. excludes Array, Date, etc.).
*/ function __PRIVATE_isPlainObject(e) {
return "object" == typeof e && null !== e && (Object.getPrototypeOf(e) === Object.prototype || null === Object.getPrototypeOf(e));
}
/** Returns a string describing the type / value of the provided input. */ function __PRIVATE_valueDescription(e) {
if (void 0 === e) return "undefined";
if (null === e) return "null";
if ("string" == typeof e) return e.length > 20 && (e = `${e.substring(0, 20)}...`),
JSON.stringify(e);
if ("number" == typeof e || "boolean" == typeof e) return "" + e;
if ("object" == typeof e) {
if (e instanceof Array) return "an array";
{
const t =
/** try to get the constructor name for an object. */
function __PRIVATE_tryGetCustomObjectType(e) {
if (e.constructor) return e.constructor.name;
return null;
}
/**
* Casts `obj` to `T`, optionally unwrapping Compat types to expose the
* underlying instance. Throws if `obj` is not an instance of `T`.
*
* This cast is used in the Lite and Full SDK to verify instance types for
* arguments passed to the public API.
* @internal
*/ (e);
return t ? `a custom ${t} object` : "an object";
}
}
return "function" == typeof e ? "a function" : fail(12329, {
type: typeof e
});
}
function __PRIVATE_cast(e,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
t) {
if ("_delegate" in e && (
// Unwrap Compat types
// eslint-disable-next-line @typescript-eslint/no-explicit-any
e = e._delegate), !(e instanceof t)) {
if (t.name === e.constructor.name) throw new FirestoreError(L.INVALID_ARGUMENT, "Type does not match the expected instance. Did you pass a reference from a different Firestore SDK?");
{
const n = __PRIVATE_valueDescription(e);
throw new FirestoreError(L.INVALID_ARGUMENT, `Expected type '${t.name}', but it was: ${n}`);
}
}
return e;
}
function __PRIVATE_validatePositiveNumber(e, t) {
if (t <= 0) throw new FirestoreError(L.INVALID_ARGUMENT, `Function ${e}() requires a positive number, but it was: ${t}.`);
}
/**
* @license
* Copyright 2025 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.
*/
/**
* Helper function to define a JSON schema {@link Property}.
* @private
* @internal
*/ function property(e, t) {
const n = {
typeString: e
};
return t && (n.value = t), n;
}
/**
* Validates the JSON object based on the provided schema, and narrows the type to the provided
* JSON schema.
* @private
* @internal
*
* @param json A JSON object to validate.
* @param scheme a {@link JsonSchema} that defines the properties to validate.
* @returns true if the JSON schema exists within the object. Throws a FirestoreError otherwise.
*/ function __PRIVATE_validateJSON(e, t) {
if (!__PRIVATE_isPlainObject(e)) throw new FirestoreError(L.INVALID_ARGUMENT, "JSON must be an object");
let n;
for (const r in t) if (t[r]) {
const i = t[r].typeString, s = "value" in t[r] ? {
value: t[r].value
} : void 0;
if (!(r in e)) {
n = `JSON missing required field: '${r}'`;
break;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const o = e[r];
if (i && typeof o !== i) {
n = `JSON field '${r}' must be a ${i}.`;
break;
}
if (void 0 !== s && o !== s.value) {
n = `Expected '${r}' field to equal '${s.value}'`;
break;
}
}
if (n) throw new FirestoreError(L.INVALID_ARGUMENT, n);
return !0;
}
/**
* @license
* Copyright 2017 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.
*/
// The earliest date supported by Firestore timestamps (0001-01-01T00:00:00Z).
const U = -62135596800, K = 1e6;
// Number of nanoseconds in a millisecond.
/**
* A `Timestamp` represents a point in time independent of any time zone or
* calendar, represented as seconds and fractions of seconds at nanosecond
* resolution in UTC Epoch time.
*
* It is encoded using the Proleptic Gregorian Calendar which extends the
* Gregorian calendar backwards to year one. It is encoded assuming all minutes
* are 60 seconds long, i.e. leap seconds are "smeared" so that no leap second
* table is needed for interpretation. Range is from 0001-01-01T00:00:00Z to
* 9999-12-31T23:59:59.999999999Z.
*
* For examples and further specifications, refer to the
* {@link https://github.com/google/protobuf/blob/master/src/google/protobuf/timestamp.proto | Timestamp definition}.
*/
class Timestamp {
/**
* Creates a new timestamp with the current date, with millisecond precision.
*
* @returns a new timestamp representing the current date.
*/
static now()