UNPKG

@firebase/firestore

Version:

The Cloud Firestore component of the Firebase JS SDK.

1,344 lines (1,299 loc) • 1.14 MB
import { _isFirebaseServerApp, _getProvider, getApp, _removeServiceInstance } from '@firebase/app'; import { FirebaseError, getModularInstance, isCloudWorkstation, pingServer, deepEqual, createMockUserToken, getDefaultEmulatorHostnameAndPort, getGlobal, isIndexedDBAvailable, getUA, isSafari, isSafariOrWebkit } from '@firebase/util'; import { Integer, Md5 } from '@firebase/webchannel-wrapper/bloom-blob'; import { Logger, LogLevel } from '@firebase/logger'; import { getStatEventTarget, Event, XhrIo, EventType, ErrorCode, WebChannel, createWebChannelTransport, Stat } from '@firebase/webchannel-wrapper/webchannel-blob'; import { RE2JS } from 're2js'; /** * @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 semver (www.semver.org) version of the SDK. */ let ve = "12.17.0"; function __PRIVATE_setSDKVersion(e) { ve = e; } /** * @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. */ /** * @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 Se = new Logger("@firebase/firestore"); // Helper methods are needed because variables can't be exported as read/write function __PRIVATE_getLogLevel() { return Se.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) { Se.setLogLevel(e); } function __PRIVATE_logDebug(e, ...t) { if (Se.logLevel <= LogLevel.DEBUG) { const n = t.map(__PRIVATE_argToString); Se.debug(`Firestore (${ve}): ${e}`, ...n); } } function __PRIVATE_logError(e, ...t) { if (Se.logLevel <= LogLevel.ERROR) { const n = t.map(__PRIVATE_argToString); Se.error(`Firestore (${ve}): ${e}`, ...n); } } /** * @internal */ function __PRIVATE_logWarn(e, ...t) { if (Se.logLevel <= LogLevel.WARN) { const n = t.map(__PRIVATE_argToString); Se.warn(`Firestore (${ve}): ${e}`, ...n); } } /** * Converts an additional log parameter to a string representation. */ function __PRIVATE_argToString(e) { if ("string" == typeof e) return e; try { 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 l(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 (${ve}) 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 || l(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 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 De = 55296, xe = 57343; function __PRIVATE_isSurrogate(e) { const t = e.charCodeAt(0); return t >= De && t <= xe; } /** 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]))); } /** * Verifies equality for an optional value. */ function na(e, t, n) { return void 0 === e && void 0 === t || void 0 !== e && void 0 !== t && n(e, t); } /** * 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. */ // An immutable sorted map implementation, based on a Left-leaning Red-Black // tree. class SortedMap { constructor(e, t) { this.comparator = e, this.root = t || LLRBNode.EMPTY; } // Returns a copy of the map, with the specified key/value added or replaced. insert(e, t) { return new SortedMap(this.comparator, this.root.insert(e, t, this.comparator).copy(null, null, LLRBNode.BLACK, null, null)); } // Returns a copy of the map, with the specified key removed. remove(e) { return new SortedMap(this.comparator, this.root.remove(e, this.comparator).copy(null, null, LLRBNode.BLACK, null, null)); } // Returns the value of the node with the given key, or null. get(e) { let t = this.root; for (;!t.isEmpty(); ) { const n = this.comparator(e, t.key); if (0 === n) return t.value; n < 0 ? t = t.left : n > 0 && (t = t.right); } return null; } // Returns the index of the element in this sorted map, or -1 if it doesn't // exist. indexOf(e) { // Number of nodes that were pruned when descending right let t = 0, n = this.root; for (;!n.isEmpty(); ) { const r = this.comparator(e, n.key); if (0 === r) return t + n.left.size; r < 0 ? n = n.left : ( // Count all nodes left of the node plus the node itself t += n.left.size + 1, n = n.right); } // Node not found return -1; } isEmpty() { return this.root.isEmpty(); } // Returns the total number of nodes in the map. get size() { return this.root.size; } // Returns the minimum key in the map. minKey() { return this.root.minKey(); } // Returns the maximum key in the map. maxKey() { return this.root.maxKey(); } // Traverses the map in key order and calls the specified action function // for each key/value pair. If action returns true, traversal is aborted. // Returns the first truthy value returned by action, or the last falsey // value returned by action. inorderTraversal(e) { return this.root.inorderTraversal(e); } forEach(e) { this.inorderTraversal(((t, n) => (e(t, n), false))); } toString() { const e = []; return this.inorderTraversal(((t, n) => (e.push(`${t}:${n}`), false))), `{${e.join(", ")}}`; } // Traverses the map in reverse key order and calls the specified action // function for each key/value pair. If action returns true, traversal is // aborted. // Returns the first truthy value returned by action, or the last falsey // value returned by action. reverseTraversal(e) { return this.root.reverseTraversal(e); } // Returns an iterator over the SortedMap. getIterator() { return new SortedMapIterator(this.root, null, this.comparator, false); } getIteratorFrom(e) { return new SortedMapIterator(this.root, e, this.comparator, false); } getReverseIterator() { return new SortedMapIterator(this.root, null, this.comparator, true); } getReverseIteratorFrom(e) { return new SortedMapIterator(this.root, e, this.comparator, true); } } // end SortedMap // An iterator over an LLRBNode. class SortedMapIterator { constructor(e, t, n, r) { this.isReverse = r, this.nodeStack = []; let i = 1; for (;!e.isEmpty(); ) if (i = t ? n(e.key, t) : 1, // flip the comparison if we're going in reverse t && r && (i *= -1), i < 0) // This node is less than our start key. ignore it e = this.isReverse ? e.left : e.right; else { if (0 === i) { // This node is exactly equal to our start key. Push it on the stack, // but stop iterating; this.nodeStack.push(e); break; } // This node is greater than our start key, add it to the stack and move // to the next one this.nodeStack.push(e), e = this.isReverse ? e.right : e.left; } } getNext() { let e = this.nodeStack.pop(); const t = { key: e.key, value: e.value }; if (this.isReverse) for (e = e.left; !e.isEmpty(); ) this.nodeStack.push(e), e = e.right; else for (e = e.right; !e.isEmpty(); ) this.nodeStack.push(e), e = e.left; return t; } hasNext() { return this.nodeStack.length > 0; } peek() { if (0 === this.nodeStack.length) return null; const e = this.nodeStack[this.nodeStack.length - 1]; return { key: e.key, value: e.value }; } } // end SortedMapIterator // Represents a node in a Left-leaning Red-Black tree. class LLRBNode { constructor(e, t, n, r, i) { this.key = e, this.value = t, this.color = null != n ? n : LLRBNode.RED, this.left = null != r ? r : LLRBNode.EMPTY, this.right = null != i ? i : LLRBNode.EMPTY, this.size = this.left.size + 1 + this.right.size; } // Returns a copy of the current node, optionally replacing pieces of it. copy(e, t, n, r, i) { return new LLRBNode(null != e ? e : this.key, null != t ? t : this.value, null != n ? n : this.color, null != r ? r : this.left, null != i ? i : this.right); } isEmpty() { return false; } // Traverses the tree in key order and calls the specified action function // for each node. If action returns true, traversal is aborted. // Returns the first truthy value returned by action, or the last falsey // value returned by action. inorderTraversal(e) { return this.left.inorderTraversal(e) || e(this.key, this.value) || this.right.inorderTraversal(e); } // Traverses the tree in reverse key order and calls the specified action // function for each node. If action returns true, traversal is aborted. // Returns the first truthy value returned by action, or the last falsey // value returned by action. reverseTraversal(e) { return this.right.reverseTraversal(e) || e(this.key, this.value) || this.left.reverseTraversal(e); } // Returns the minimum node in the tree. min() { return this.left.isEmpty() ? this : this.left.min(); } // Returns the maximum key in the tree. minKey() { return this.min().key; } // Returns the maximum key in the tree. maxKey() { return this.right.isEmpty() ? this.key : this.right.maxKey(); } // Returns new tree, with the key/value added. insert(e, t, n) { let r = this; const i = n(e, r.key); return r = i < 0 ? r.copy(null, null, null, r.left.insert(e, t, n), null) : 0 === i ? r.copy(null, t, null, null, null) : r.copy(null, null, null, null, r.right.insert(e, t, n)), r.fixUp(); } removeMin() { if (this.left.isEmpty()) return LLRBNode.EMPTY; let e = this; return e.left.isRed() || e.left.left.isRed() || (e = e.moveRedLeft()), e = e.copy(null, null, null, e.left.removeMin(), null), e.fixUp(); } // Returns new tree, with the specified item removed. remove(e, t) { let n, r = this; if (t(e, r.key) < 0) r.left.isEmpty() || r.left.isRed() || r.left.left.isRed() || (r = r.moveRedLeft()), r = r.copy(null, null, null, r.left.remove(e, t), null); else { if (r.left.isRed() && (r = r.rotateRight()), r.right.isEmpty() || r.right.isRed() || r.right.left.isRed() || (r = r.moveRedRight()), 0 === t(e, r.key)) { if (r.right.isEmpty()) return LLRBNode.EMPTY; n = r.right.min(), r = r.copy(n.key, n.value, null, null, r.right.removeMin()); } r = r.copy(null, null, null, null, r.right.remove(e, t)); } return r.fixUp(); } isRed() { return this.color; } // Returns new tree after performing any needed rotations. fixUp() { let e = this; return e.right.isRed() && !e.left.isRed() && (e = e.rotateLeft()), e.left.isRed() && e.left.left.isRed() && (e = e.rotateRight()), e.left.isRed() && e.right.isRed() && (e = e.colorFlip()), e; } moveRedLeft() { let e = this.colorFlip(); return e.right.left.isRed() && (e = e.copy(null, null, null, null, e.right.rotateRight()), e = e.rotateLeft(), e = e.colorFlip()), e; } moveRedRight() { let e = this.colorFlip(); return e.left.left.isRed() && (e = e.rotateRight(), e = e.colorFlip()), e; } rotateLeft() { const e = this.copy(null, null, LLRBNode.RED, null, this.right.left); return this.right.copy(null, null, this.color, e, null); } rotateRight() { const e = this.copy(null, null, LLRBNode.RED, this.left.right, null); return this.left.copy(null, null, this.color, null, e); } colorFlip() { const e = this.left.copy(null, null, !this.left.color, null, null), t = this.right.copy(null, null, !this.right.color, null, null); return this.copy(null, null, !this.color, e, t); } // For testing. checkMaxDepth() { const e = this.check(); return Math.pow(2, e) <= this.size + 1; } // In a balanced RB tree, the black-depth (number of black nodes) from root to // leaves is equal on both sides. This function verifies that or asserts. check() { if (this.isRed() && this.left.isRed()) throw l(43730, { key: this.key, value: this.value }); if (this.right.isRed()) throw l(14113, { key: this.key, value: this.value }); const e = this.left.check(); if (e !== this.right.check()) throw l(27949); return e + (this.isRed() ? 0 : 1); } } // end LLRBNode // Empty node is shared between all LLRB trees. // eslint-disable-next-line @typescript-eslint/no-explicit-any LLRBNode.EMPTY = null, LLRBNode.RED = true, LLRBNode.BLACK = false; // end LLRBEmptyNode LLRBNode.EMPTY = new // Represents an empty node (a leaf node in the Red-Black Tree). class LLRBEmptyNode { constructor() { this.size = 0; } get key() { throw l(57766); } get value() { throw l(16141); } get color() { throw l(16727); } get left() { throw l(29726); } get right() { throw l(36894); } // Returns a copy of the current node. copy(e, t, n, r, i) { return this; } // Returns a copy of the tree, with the specified key/value added. insert(e, t, n) { return new LLRBNode(e, t); } // Returns a copy of the tree, with the specified key removed. remove(e, t) { return this; } isEmpty() { return true; } inorderTraversal(e) { return false; } reverseTraversal(e) { return false; } minKey() { return null; } maxKey() { return null; } isRed() { return false; } // For testing. checkMaxDepth() { return true; } check() { 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. */ /** * SortedSet is an immutable (copy-on-write) collection that holds elements * in order specified by the provided comparator. * * NOTE: if provided comparator returns 0 for two elements, we consider them to * be equal! */ class SortedSet { constructor(e) { this.comparator = e, this.data = new SortedMap(this.comparator); } has(e) { return null !== this.data.get(e); } first() { return this.data.minKey(); } last() { return this.data.maxKey(); } get size() { return this.data.size; } indexOf(e) { return this.data.indexOf(e); } /** Iterates elements in order defined by "comparator" */ forEach(e) { this.data.inorderTraversal(((t, n) => (e(t), false))); } /** Iterates over `elem`s such that: range[0] &lt;= elem &lt; range[1]. */ forEachInRange(e, t) { const n = this.data.getIteratorFrom(e[0]); for (;n.hasNext(); ) { const r = n.getNext(); if (this.comparator(r.key, e[1]) >= 0) return; t(r.key); } } /** * Iterates over `elem`s such that: start &lt;= elem until false is returned. */ forEachWhile(e, t) { let n; for (n = void 0 !== t ? this.data.getIteratorFrom(t) : this.data.getIterator(); n.hasNext(); ) { if (!e(n.getNext().key)) return; } } /** Finds the least element greater than or equal to `elem`. */ firstAfterOrEqual(e) { const t = this.data.getIteratorFrom(e); return t.hasNext() ? t.getNext().key : null; } getIterator() { return new SortedSetIterator(this.data.getIterator()); } getIteratorFrom(e) { return new SortedSetIterator(this.data.getIteratorFrom(e)); } /** Inserts or updates an element */ add(e) { return this.copy(this.data.remove(e).insert(e, true)); } /** Deletes an element */ delete(e) { return this.has(e) ? this.copy(this.data.remove(e)) : this; } isEmpty() { return this.data.isEmpty(); } unionWith(e) { let t = this; // Make sure `result` always refers to the larger one of the two sets. return t.size < e.size && (t = e, e = this), e.forEach((e => { t = t.add(e); })), t; } isEqual(e) { if (!(e instanceof SortedSet)) return false; if (this.size !== e.size) return false; const t = this.data.getIterator(), n = e.data.getIterator(); for (;t.hasNext(); ) { const e = t.getNext().key, r = n.getNext().key; if (0 !== this.comparator(e, r)) return false; } return true; } toArray() { const e = []; return this.forEach((t => { e.push(t); })), e; } toString() { const e = []; return this.forEach((t => e.push(t))), "SortedSet(" + e.toString() + ")"; } copy(e) { const t = new SortedSet(this.comparator); return t.data = e, t; } } class SortedSetIterator { constructor(e) { this.iter = e; } getNext() { return this.iter.getNext().key; } hasNext() { return this.iter.hasNext(); } } /** * Compares two sorted sets for equality using their natural ordering. The * method computes the intersection and invokes `onAdd` for every element that * is in `after` but not `before`. `onRemove` is invoked for every element in * `before` but missing from `after`. * * The method creates a copy of both `before` and `after` and runs in O(n log * n), where n is the size of the two lists. * * @param before - The elements that exist in the original set. * @param after - The elements to diff against the original set. * @param comparator - The comparator for the elements in before and after. * @param onAdd - A function to invoke for every element that is part of ` * after` but not `before`. * @param onRemove - A function to invoke for every element that is part of * `before` but not `after`. */ /** * Returns the next element from the iterator or `undefined` if none available. */ function __PRIVATE_advanceIterator(e) { return e.hasNext() ? e.getNext() : void 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 aa = { // 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 s extends FirebaseError { /** @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. */ const Ce = "__name__"; /** * Path represents an ordered sequence of string segments. */ class BasePath { constructor(e, t, n) { void 0 === t ? t = 0 : t > e.length && l(637, { offset: t, range: e.length }), void 0 === n ? n = e.length - t : n > e.length - t && l(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 false; for (let t = 0; t < this.length; t++) if (this.get(t) !== e.get(t)) return false; return true; } isImmediateParentOf(e) { if (this.length + 1 !== e.length) return false; for (let t = 0; t < this.length; t++) if (this.get(t) !== e.get(t)) return false; return true; } 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 Integer.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(); } toStringWithLeadingSlash() { 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 s(aa.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 Fe = /^[_a-zA-Z][_a-zA-Z0-9]*$/; /** * A dot-separated path for navigating sub-objects within a document. * @internal */ let Oe = class FieldPath extends BasePath { construct(e, t, n) { return new FieldPath(e, t, n); } /** * Returns true if the string could be used as a segment in a field path * without escaping. */ static isValidIdentifier(e) { return Fe.test(e); } canonicalString() { return this.toArray().map((e => (e = e.replace(/\\/g, "\\\\").replace(/`/g, "\\`"), FieldPath.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) === Ce; } /** * The field designating the key of a document. */ static keyField() { return new FieldPath([ Ce ]); } /** * 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 s(aa.INVALID_ARGUMENT, `Invalid field path (${e}). Paths must not be empty, begin with '.', end with '.', or contain '..'`); t.push(n), n = ""; }; let i = false; for (;r < e.length; ) { const t = e[r]; if ("\\" === t) { if (r + 1 === e.length) throw new s(aa.INVALID_ARGUMENT, "Path has trailing escape character: " + e); const t = e[r + 1]; if ("\\" !== t && "." !== t && "`" !== t) throw new s(aa.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 s(aa.INVALID_ARGUMENT, "Unterminated ` in path: " + e); return new FieldPath(t); } static emptyPath() { return new FieldPath([]); } }; /** * @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. */ /** * Provides a set of fields that can be used to partially patch a document. * FieldMask is used in conjunction with ObjectValue. * Examples: * foo - Overwrites foo entirely with the provided value. If foo is not * present in the companion ObjectValue, the field is deleted. * foo.bar - Overwrites only the field bar of the object foo. * If foo is not an object, foo is replaced with an object * containing foo */ class FieldMask { constructor(e) { this.fields = e, // TODO(dimond): validation of FieldMask // Sort the field mask to support `FieldMask.isEqual()` and assert below. e.sort(Oe.comparator); } static empty() { return new FieldMask([]); } /** * Returns a new FieldMask object that is the result of adding all the given * fields paths to this field mask. */ unionWith(e) { let t = new SortedSet(Oe.comparator); for (const e of this.fields) t = t.add(e); for (const n of e) t = t.add(n); return new FieldMask(t.toArray()); } /** * Verifies that `fieldPath` is included by at least one field in this field * mask. * * This is an O(n) operation, where `n` is the size of the field mask. */ covers(e) { for (const t of this.fields) if (t.isPrefixOf(e)) return true; return false; } isEqual(e) { return __PRIVATE_arrayEquals(this.fields, e.fields, ((e, t) => e.isEqual(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. */ function __PRIVATE_objectSize(e) { let t = 0; for (const n in e) Object.prototype.hasOwnProperty.call(e, n) && t++; return t; } function forEach(e, t) { for (const n in e) Object.prototype.hasOwnProperty.call(e, n) && t(n, e[n]); } function __PRIVATE_mapToArray(e, t) { const n = []; for (const r in e) Object.prototype.hasOwnProperty.call(e, r) && n.push(t(e[r], r, e)); return n; } function isEmpty(e) { for (const t in e) if (Object.prototype.hasOwnProperty.call(e, t)) return false; return true; } /** * @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 * *