@firebase/firestore 3.7.3 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/firestore/lite/index.d.ts +1 -1
  3. package/dist/firestore/src/api/filter.d.ts +1 -1
  4. package/dist/firestore/src/api.d.ts +1 -1
  5. package/dist/firestore/src/core/bound.d.ts +49 -0
  6. package/dist/firestore/src/core/filter.d.ts +134 -0
  7. package/dist/firestore/src/core/order_by.d.ts +35 -0
  8. package/dist/firestore/src/core/query.d.ts +5 -6
  9. package/dist/firestore/src/core/target.d.ts +4 -120
  10. package/dist/firestore/src/lite-api/query.d.ts +273 -66
  11. package/dist/firestore/src/local/indexeddb_index_manager.d.ts +2 -0
  12. package/dist/firestore/src/local/local_documents_view.d.ts +0 -2
  13. package/dist/firestore/src/model/document.d.ts +8 -1
  14. package/dist/firestore/src/protos/firestore_proto_api.d.ts +1 -1
  15. package/dist/firestore/src/remote/serializer.d.ts +10 -3
  16. package/dist/firestore/src/util/logic_utils.d.ts +52 -0
  17. package/dist/firestore/src/util/types.d.ts +1 -1
  18. package/dist/firestore/test/lite/helpers.d.ts +1 -1
  19. package/dist/firestore/test/unit/core/filter.test.d.ts +17 -0
  20. package/dist/firestore/test/unit/specs/spec_test_runner.d.ts +2 -1
  21. package/dist/firestore/test/unit/util/logic_utils.test.d.ts +17 -0
  22. package/dist/firestore/test/util/helpers.d.ts +6 -2
  23. package/dist/firestore/test/util/test_platform.d.ts +2 -2
  24. package/dist/index.d.ts +138 -62
  25. package/dist/index.esm2017.js +4295 -3742
  26. package/dist/index.esm2017.js.map +1 -1
  27. package/dist/index.esm5.js +4981 -4410
  28. package/dist/index.esm5.js.map +1 -1
  29. package/dist/index.node.cjs.js +1657 -889
  30. package/dist/index.node.cjs.js.map +1 -1
  31. package/dist/index.node.mjs +1650 -890
  32. package/dist/index.node.mjs.map +1 -1
  33. package/dist/index.rn.js +4520 -3967
  34. package/dist/index.rn.js.map +1 -1
  35. package/dist/internal.d.ts +328 -68
  36. package/dist/lite/firestore/lite/index.d.ts +1 -1
  37. package/dist/lite/firestore/src/api/filter.d.ts +1 -1
  38. package/dist/lite/firestore/src/api.d.ts +1 -1
  39. package/dist/lite/firestore/src/core/bound.d.ts +49 -0
  40. package/dist/lite/firestore/src/core/filter.d.ts +134 -0
  41. package/dist/lite/firestore/src/core/order_by.d.ts +35 -0
  42. package/dist/lite/firestore/src/core/query.d.ts +5 -6
  43. package/dist/lite/firestore/src/core/target.d.ts +4 -120
  44. package/dist/lite/firestore/src/lite-api/query.d.ts +273 -66
  45. package/dist/lite/firestore/src/local/indexeddb_index_manager.d.ts +2 -0
  46. package/dist/lite/firestore/src/local/local_documents_view.d.ts +0 -2
  47. package/dist/lite/firestore/src/model/document.d.ts +8 -1
  48. package/dist/lite/firestore/src/protos/firestore_proto_api.d.ts +1 -1
  49. package/dist/lite/firestore/src/remote/serializer.d.ts +10 -3
  50. package/dist/lite/firestore/src/util/logic_utils.d.ts +52 -0
  51. package/dist/lite/firestore/src/util/types.d.ts +1 -1
  52. package/dist/lite/firestore/test/lite/helpers.d.ts +1 -1
  53. package/dist/lite/firestore/test/unit/core/filter.test.d.ts +17 -0
  54. package/dist/lite/firestore/test/unit/specs/spec_test_runner.d.ts +2 -1
  55. package/dist/lite/firestore/test/unit/util/logic_utils.test.d.ts +17 -0
  56. package/dist/lite/firestore/test/util/helpers.d.ts +6 -2
  57. package/dist/lite/firestore/test/util/test_platform.d.ts +2 -2
  58. package/dist/lite/index.browser.esm2017.js +2091 -1793
  59. package/dist/lite/index.browser.esm2017.js.map +1 -1
  60. package/dist/lite/index.browser.esm5.js +2239 -2000
  61. package/dist/lite/index.browser.esm5.js.map +1 -1
  62. package/dist/lite/index.d.ts +138 -62
  63. package/dist/lite/index.node.cjs.js +2212 -1802
  64. package/dist/lite/index.node.cjs.js.map +1 -1
  65. package/dist/lite/index.node.mjs +2205 -1803
  66. package/dist/lite/index.node.mjs.map +1 -1
  67. package/dist/lite/index.rn.esm2017.js +2077 -1778
  68. package/dist/lite/index.rn.esm2017.js.map +1 -1
  69. package/dist/lite/internal.d.ts +328 -66
  70. package/dist/lite/packages/firestore/dist/lite/index.browser.esm2017.d.ts +322 -174
  71. package/dist/lite/packages/firestore/lite/index.d.ts +1 -1
  72. package/dist/lite/packages/firestore/rollup.config.debug.d.ts +1 -1
  73. package/dist/lite/packages/firestore/src/api/filter.d.ts +1 -1
  74. package/dist/lite/packages/firestore/src/api.d.ts +1 -1
  75. package/dist/lite/packages/firestore/src/core/bound.d.ts +49 -0
  76. package/dist/lite/packages/firestore/src/core/filter.d.ts +134 -0
  77. package/dist/lite/packages/firestore/src/core/order_by.d.ts +35 -0
  78. package/dist/lite/packages/firestore/src/core/query.d.ts +5 -6
  79. package/dist/lite/packages/firestore/src/core/target.d.ts +4 -120
  80. package/dist/lite/packages/firestore/src/lite-api/query.d.ts +273 -66
  81. package/dist/lite/packages/firestore/src/local/indexeddb_index_manager.d.ts +2 -0
  82. package/dist/lite/packages/firestore/src/local/local_documents_view.d.ts +0 -2
  83. package/dist/lite/packages/firestore/src/model/document.d.ts +8 -1
  84. package/dist/lite/packages/firestore/src/protos/firestore_proto_api.d.ts +1 -1
  85. package/dist/lite/packages/firestore/src/remote/serializer.d.ts +10 -3
  86. package/dist/lite/packages/firestore/src/util/logic_utils.d.ts +52 -0
  87. package/dist/lite/packages/firestore/src/util/types.d.ts +1 -1
  88. package/dist/lite/packages/firestore/test/lite/helpers.d.ts +1 -1
  89. package/dist/lite/packages/firestore/test/unit/core/filter.test.d.ts +17 -0
  90. package/dist/lite/packages/firestore/test/unit/specs/spec_test_runner.d.ts +2 -1
  91. package/dist/lite/packages/firestore/test/unit/util/logic_utils.test.d.ts +17 -0
  92. package/dist/lite/packages/firestore/test/util/helpers.d.ts +6 -2
  93. package/dist/lite/packages/firestore/test/util/test_platform.d.ts +2 -2
  94. package/dist/lite/private.d.ts +240 -66
  95. package/dist/packages/firestore/dist/index.esm2017.d.ts +368 -208
  96. package/dist/packages/firestore/lite/index.d.ts +1 -1
  97. package/dist/packages/firestore/rollup.config.debug.d.ts +1 -1
  98. package/dist/packages/firestore/src/api/filter.d.ts +1 -1
  99. package/dist/packages/firestore/src/api.d.ts +1 -1
  100. package/dist/packages/firestore/src/core/bound.d.ts +49 -0
  101. package/dist/packages/firestore/src/core/filter.d.ts +134 -0
  102. package/dist/packages/firestore/src/core/order_by.d.ts +35 -0
  103. package/dist/packages/firestore/src/core/query.d.ts +5 -6
  104. package/dist/packages/firestore/src/core/target.d.ts +4 -120
  105. package/dist/packages/firestore/src/lite-api/query.d.ts +273 -66
  106. package/dist/packages/firestore/src/local/indexeddb_index_manager.d.ts +2 -0
  107. package/dist/packages/firestore/src/local/local_documents_view.d.ts +0 -2
  108. package/dist/packages/firestore/src/model/document.d.ts +8 -1
  109. package/dist/packages/firestore/src/protos/firestore_proto_api.d.ts +1 -1
  110. package/dist/packages/firestore/src/remote/serializer.d.ts +10 -3
  111. package/dist/packages/firestore/src/util/logic_utils.d.ts +52 -0
  112. package/dist/packages/firestore/src/util/types.d.ts +1 -1
  113. package/dist/packages/firestore/test/lite/helpers.d.ts +1 -1
  114. package/dist/packages/firestore/test/unit/core/filter.test.d.ts +17 -0
  115. package/dist/packages/firestore/test/unit/specs/spec_test_runner.d.ts +2 -1
  116. package/dist/packages/firestore/test/unit/util/logic_utils.test.d.ts +17 -0
  117. package/dist/packages/firestore/test/util/helpers.d.ts +6 -2
  118. package/dist/packages/firestore/test/util/test_platform.d.ts +2 -2
  119. package/dist/private.d.ts +240 -68
  120. package/package.json +13 -11
@@ -14,7 +14,7 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
14
14
 
15
15
  var nodeFetch__default = /*#__PURE__*/_interopDefaultLegacy(nodeFetch);
16
16
 
17
- const version$1 = "3.7.3";
17
+ const version$1 = "3.8.0";
18
18
 
19
19
  /**
20
20
  * @license
@@ -67,7 +67,7 @@ User.GOOGLE_CREDENTIALS = new User('google-credentials-uid');
67
67
  User.FIRST_PARTY = new User('first-party-uid');
68
68
  User.MOCK_USER = new User('mock-user');
69
69
 
70
- const version = "9.14.0";
70
+ const version = "9.15.0";
71
71
 
72
72
  /**
73
73
  * @license
@@ -1478,7 +1478,8 @@ class FetchConnection extends RestConnection {
1478
1478
  body: requestJson
1479
1479
  });
1480
1480
  }
1481
- catch (err) {
1481
+ catch (e) {
1482
+ const err = e;
1482
1483
  throw new FirestoreError(mapCodeFromHttpStatus(err.status), 'Request failed with error: ' + err.statusText);
1483
1484
  }
1484
1485
  if (!response.ok) {
@@ -1612,159 +1613,29 @@ function arrayEquals(left, right, comparator) {
1612
1613
  * See the License for the specific language governing permissions and
1613
1614
  * limitations under the License.
1614
1615
  */
1615
- // The earliest date supported by Firestore timestamps (0001-01-01T00:00:00Z).
1616
- const MIN_SECONDS = -62135596800;
1617
- // Number of nanoseconds in a millisecond.
1618
- const MS_TO_NANOS = 1e6;
1619
- /**
1620
- * A `Timestamp` represents a point in time independent of any time zone or
1621
- * calendar, represented as seconds and fractions of seconds at nanosecond
1622
- * resolution in UTC Epoch time.
1623
- *
1624
- * It is encoded using the Proleptic Gregorian Calendar which extends the
1625
- * Gregorian calendar backwards to year one. It is encoded assuming all minutes
1626
- * are 60 seconds long, i.e. leap seconds are "smeared" so that no leap second
1627
- * table is needed for interpretation. Range is from 0001-01-01T00:00:00Z to
1628
- * 9999-12-31T23:59:59.999999999Z.
1629
- *
1630
- * For examples and further specifications, refer to the
1631
- * {@link https://github.com/google/protobuf/blob/master/src/google/protobuf/timestamp.proto | Timestamp definition}.
1632
- */
1633
- class Timestamp {
1634
- /**
1635
- * Creates a new timestamp.
1636
- *
1637
- * @param seconds - The number of seconds of UTC time since Unix epoch
1638
- * 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
1639
- * 9999-12-31T23:59:59Z inclusive.
1640
- * @param nanoseconds - The non-negative fractions of a second at nanosecond
1641
- * resolution. Negative second values with fractions must still have
1642
- * non-negative nanoseconds values that count forward in time. Must be
1643
- * from 0 to 999,999,999 inclusive.
1644
- */
1645
- constructor(
1646
- /**
1647
- * The number of seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z.
1648
- */
1649
- seconds,
1650
- /**
1651
- * The fractions of a second at nanosecond resolution.*
1652
- */
1653
- nanoseconds) {
1654
- this.seconds = seconds;
1655
- this.nanoseconds = nanoseconds;
1656
- if (nanoseconds < 0) {
1657
- throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp nanoseconds out of range: ' + nanoseconds);
1658
- }
1659
- if (nanoseconds >= 1e9) {
1660
- throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp nanoseconds out of range: ' + nanoseconds);
1661
- }
1662
- if (seconds < MIN_SECONDS) {
1663
- throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp seconds out of range: ' + seconds);
1664
- }
1665
- // This will break in the year 10,000.
1666
- if (seconds >= 253402300800) {
1667
- throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp seconds out of range: ' + seconds);
1616
+ function objectSize(obj) {
1617
+ let count = 0;
1618
+ for (const key in obj) {
1619
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
1620
+ count++;
1668
1621
  }
1669
1622
  }
1670
- /**
1671
- * Creates a new timestamp with the current date, with millisecond precision.
1672
- *
1673
- * @returns a new timestamp representing the current date.
1674
- */
1675
- static now() {
1676
- return Timestamp.fromMillis(Date.now());
1677
- }
1678
- /**
1679
- * Creates a new timestamp from the given date.
1680
- *
1681
- * @param date - The date to initialize the `Timestamp` from.
1682
- * @returns A new `Timestamp` representing the same point in time as the given
1683
- * date.
1684
- */
1685
- static fromDate(date) {
1686
- return Timestamp.fromMillis(date.getTime());
1687
- }
1688
- /**
1689
- * Creates a new timestamp from the given number of milliseconds.
1690
- *
1691
- * @param milliseconds - Number of milliseconds since Unix epoch
1692
- * 1970-01-01T00:00:00Z.
1693
- * @returns A new `Timestamp` representing the same point in time as the given
1694
- * number of milliseconds.
1695
- */
1696
- static fromMillis(milliseconds) {
1697
- const seconds = Math.floor(milliseconds / 1000);
1698
- const nanos = Math.floor((milliseconds - seconds * 1000) * MS_TO_NANOS);
1699
- return new Timestamp(seconds, nanos);
1700
- }
1701
- /**
1702
- * Converts a `Timestamp` to a JavaScript `Date` object. This conversion
1703
- * causes a loss of precision since `Date` objects only support millisecond
1704
- * precision.
1705
- *
1706
- * @returns JavaScript `Date` object representing the same point in time as
1707
- * this `Timestamp`, with millisecond precision.
1708
- */
1709
- toDate() {
1710
- return new Date(this.toMillis());
1711
- }
1712
- /**
1713
- * Converts a `Timestamp` to a numeric timestamp (in milliseconds since
1714
- * epoch). This operation causes a loss of precision.
1715
- *
1716
- * @returns The point in time corresponding to this timestamp, represented as
1717
- * the number of milliseconds since Unix epoch 1970-01-01T00:00:00Z.
1718
- */
1719
- toMillis() {
1720
- return this.seconds * 1000 + this.nanoseconds / MS_TO_NANOS;
1721
- }
1722
- _compareTo(other) {
1723
- if (this.seconds === other.seconds) {
1724
- return primitiveComparator(this.nanoseconds, other.nanoseconds);
1623
+ return count;
1624
+ }
1625
+ function forEach(obj, fn) {
1626
+ for (const key in obj) {
1627
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
1628
+ fn(key, obj[key]);
1725
1629
  }
1726
- return primitiveComparator(this.seconds, other.seconds);
1727
- }
1728
- /**
1729
- * Returns true if this `Timestamp` is equal to the provided one.
1730
- *
1731
- * @param other - The `Timestamp` to compare against.
1732
- * @returns true if this `Timestamp` is equal to the provided one.
1733
- */
1734
- isEqual(other) {
1735
- return (other.seconds === this.seconds && other.nanoseconds === this.nanoseconds);
1736
- }
1737
- /** Returns a textual representation of this `Timestamp`. */
1738
- toString() {
1739
- return ('Timestamp(seconds=' +
1740
- this.seconds +
1741
- ', nanoseconds=' +
1742
- this.nanoseconds +
1743
- ')');
1744
- }
1745
- /** Returns a JSON-serializable representation of this `Timestamp`. */
1746
- toJSON() {
1747
- return { seconds: this.seconds, nanoseconds: this.nanoseconds };
1748
1630
  }
1749
- /**
1750
- * Converts this object to a primitive string, which allows `Timestamp` objects
1751
- * to be compared using the `>`, `<=`, `>=` and `>` operators.
1752
- */
1753
- valueOf() {
1754
- // This method returns a string of the form <seconds>.<nanoseconds> where
1755
- // <seconds> is translated to have a non-negative value and both <seconds>
1756
- // and <nanoseconds> are left-padded with zeroes to be a consistent length.
1757
- // Strings with this format then have a lexiographical ordering that matches
1758
- // the expected ordering. The <seconds> translation is done to avoid having
1759
- // a leading negative sign (i.e. a leading '-' character) in its string
1760
- // representation, which would affect its lexiographical ordering.
1761
- const adjustedSeconds = this.seconds - MIN_SECONDS;
1762
- // Note: Up to 12 decimal digits are required to represent all valid
1763
- // 'seconds' values.
1764
- const formattedSeconds = String(adjustedSeconds).padStart(12, '0');
1765
- const formattedNanoseconds = String(this.nanoseconds).padStart(9, '0');
1766
- return formattedSeconds + '.' + formattedNanoseconds;
1631
+ }
1632
+ function isEmpty(obj) {
1633
+ for (const key in obj) {
1634
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
1635
+ return false;
1636
+ }
1767
1637
  }
1638
+ return true;
1768
1639
  }
1769
1640
 
1770
1641
  /**
@@ -1784,44 +1655,32 @@ class Timestamp {
1784
1655
  * limitations under the License.
1785
1656
  */
1786
1657
  /**
1787
- * A version of a document in Firestore. This corresponds to the version
1788
- * timestamp, such as update_time or read_time.
1658
+ * Returns whether a variable is either undefined or null.
1789
1659
  */
1790
- class SnapshotVersion {
1791
- constructor(timestamp) {
1792
- this.timestamp = timestamp;
1793
- }
1794
- static fromTimestamp(value) {
1795
- return new SnapshotVersion(value);
1796
- }
1797
- static min() {
1798
- return new SnapshotVersion(new Timestamp(0, 0));
1799
- }
1800
- static max() {
1801
- return new SnapshotVersion(new Timestamp(253402300799, 1e9 - 1));
1802
- }
1803
- compareTo(other) {
1804
- return this.timestamp._compareTo(other.timestamp);
1805
- }
1806
- isEqual(other) {
1807
- return this.timestamp.isEqual(other.timestamp);
1808
- }
1809
- /** Returns a number representation of the version for use in spec tests. */
1810
- toMicroseconds() {
1811
- // Convert to microseconds.
1812
- return this.timestamp.seconds * 1e6 + this.timestamp.nanoseconds / 1000;
1813
- }
1814
- toString() {
1815
- return 'SnapshotVersion(' + this.timestamp.toString() + ')';
1816
- }
1817
- toTimestamp() {
1818
- return this.timestamp;
1819
- }
1660
+ function isNullOrUndefined(value) {
1661
+ return value === null || value === undefined;
1662
+ }
1663
+ /** Returns whether the value represents -0. */
1664
+ function isNegativeZero(value) {
1665
+ // Detect if the value is -0.0. Based on polyfill from
1666
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
1667
+ return value === 0 && 1 / value === 1 / -0;
1668
+ }
1669
+ /**
1670
+ * Returns whether a value is an integer and in the safe integer range
1671
+ * @param value - The value to test for being an integer and in the safe range
1672
+ */
1673
+ function isSafeInteger(value) {
1674
+ return (typeof value === 'number' &&
1675
+ Number.isInteger(value) &&
1676
+ !isNegativeZero(value) &&
1677
+ value <= Number.MAX_SAFE_INTEGER &&
1678
+ value >= Number.MIN_SAFE_INTEGER);
1820
1679
  }
1821
1680
 
1822
1681
  /**
1823
1682
  * @license
1824
- * Copyright 2017 Google LLC
1683
+ * Copyright 2020 Google LLC
1825
1684
  *
1826
1685
  * Licensed under the Apache License, Version 2.0 (the "License");
1827
1686
  * you may not use this file except in compliance with the License.
@@ -1835,34 +1694,20 @@ class SnapshotVersion {
1835
1694
  * See the License for the specific language governing permissions and
1836
1695
  * limitations under the License.
1837
1696
  */
1838
- function objectSize(obj) {
1839
- let count = 0;
1840
- for (const key in obj) {
1841
- if (Object.prototype.hasOwnProperty.call(obj, key)) {
1842
- count++;
1843
- }
1844
- }
1845
- return count;
1846
- }
1847
- function forEach(obj, fn) {
1848
- for (const key in obj) {
1849
- if (Object.prototype.hasOwnProperty.call(obj, key)) {
1850
- fn(key, obj[key]);
1851
- }
1852
- }
1697
+ /** Converts a Base64 encoded string to a binary string. */
1698
+ function decodeBase64(encoded) {
1699
+ // Note: We used to validate the base64 string here via a regular expression.
1700
+ // This was removed to improve the performance of indexing.
1701
+ return Buffer.from(encoded, 'base64').toString('binary');
1853
1702
  }
1854
- function isEmpty(obj) {
1855
- for (const key in obj) {
1856
- if (Object.prototype.hasOwnProperty.call(obj, key)) {
1857
- return false;
1858
- }
1859
- }
1860
- return true;
1703
+ /** Converts a binary string to a Base64 encoded string. */
1704
+ function encodeBase64(raw) {
1705
+ return Buffer.from(raw, 'binary').toString('base64');
1861
1706
  }
1862
1707
 
1863
1708
  /**
1864
1709
  * @license
1865
- * Copyright 2017 Google LLC
1710
+ * Copyright 2020 Google LLC
1866
1711
  *
1867
1712
  * Licensed under the Apache License, Version 2.0 (the "License");
1868
1713
  * you may not use this file except in compliance with the License.
@@ -1876,445 +1721,1165 @@ function isEmpty(obj) {
1876
1721
  * See the License for the specific language governing permissions and
1877
1722
  * limitations under the License.
1878
1723
  */
1879
- // An immutable sorted map implementation, based on a Left-leaning Red-Black
1880
- // tree.
1881
- class SortedMap {
1882
- constructor(comparator, root) {
1883
- this.comparator = comparator;
1884
- this.root = root ? root : LLRBNode.EMPTY;
1724
+ /**
1725
+ * Immutable class that represents a "proto" byte string.
1726
+ *
1727
+ * Proto byte strings can either be Base64-encoded strings or Uint8Arrays when
1728
+ * sent on the wire. This class abstracts away this differentiation by holding
1729
+ * the proto byte string in a common class that must be converted into a string
1730
+ * before being sent as a proto.
1731
+ * @internal
1732
+ */
1733
+ class ByteString {
1734
+ constructor(binaryString) {
1735
+ this.binaryString = binaryString;
1885
1736
  }
1886
- // Returns a copy of the map, with the specified key/value added or replaced.
1887
- insert(key, value) {
1888
- return new SortedMap(this.comparator, this.root
1889
- .insert(key, value, this.comparator)
1890
- .copy(null, null, LLRBNode.BLACK, null, null));
1737
+ static fromBase64String(base64) {
1738
+ const binaryString = decodeBase64(base64);
1739
+ return new ByteString(binaryString);
1891
1740
  }
1892
- // Returns a copy of the map, with the specified key removed.
1893
- remove(key) {
1894
- return new SortedMap(this.comparator, this.root
1895
- .remove(key, this.comparator)
1896
- .copy(null, null, LLRBNode.BLACK, null, null));
1741
+ static fromUint8Array(array) {
1742
+ // TODO(indexing); Remove the copy of the byte string here as this method
1743
+ // is frequently called during indexing.
1744
+ const binaryString = binaryStringFromUint8Array(array);
1745
+ return new ByteString(binaryString);
1897
1746
  }
1898
- // Returns the value of the node with the given key, or null.
1899
- get(key) {
1900
- let node = this.root;
1901
- while (!node.isEmpty()) {
1902
- const cmp = this.comparator(key, node.key);
1903
- if (cmp === 0) {
1904
- return node.value;
1905
- }
1906
- else if (cmp < 0) {
1907
- node = node.left;
1908
- }
1909
- else if (cmp > 0) {
1910
- node = node.right;
1747
+ [Symbol.iterator]() {
1748
+ let i = 0;
1749
+ return {
1750
+ next: () => {
1751
+ if (i < this.binaryString.length) {
1752
+ return { value: this.binaryString.charCodeAt(i++), done: false };
1753
+ }
1754
+ else {
1755
+ return { value: undefined, done: true };
1756
+ }
1911
1757
  }
1912
- }
1913
- return null;
1758
+ };
1914
1759
  }
1915
- // Returns the index of the element in this sorted map, or -1 if it doesn't
1916
- // exist.
1917
- indexOf(key) {
1918
- // Number of nodes that were pruned when descending right
1919
- let prunedNodes = 0;
1920
- let node = this.root;
1921
- while (!node.isEmpty()) {
1922
- const cmp = this.comparator(key, node.key);
1923
- if (cmp === 0) {
1924
- return prunedNodes + node.left.size;
1925
- }
1926
- else if (cmp < 0) {
1927
- node = node.left;
1928
- }
1929
- else {
1930
- // Count all nodes left of the node plus the node itself
1931
- prunedNodes += node.left.size + 1;
1932
- node = node.right;
1933
- }
1934
- }
1935
- // Node not found
1936
- return -1;
1760
+ toBase64() {
1761
+ return encodeBase64(this.binaryString);
1937
1762
  }
1938
- isEmpty() {
1939
- return this.root.isEmpty();
1763
+ toUint8Array() {
1764
+ return uint8ArrayFromBinaryString(this.binaryString);
1940
1765
  }
1941
- // Returns the total number of nodes in the map.
1942
- get size() {
1943
- return this.root.size;
1766
+ approximateByteSize() {
1767
+ return this.binaryString.length * 2;
1944
1768
  }
1945
- // Returns the minimum key in the map.
1946
- minKey() {
1947
- return this.root.minKey();
1769
+ compareTo(other) {
1770
+ return primitiveComparator(this.binaryString, other.binaryString);
1948
1771
  }
1949
- // Returns the maximum key in the map.
1950
- maxKey() {
1951
- return this.root.maxKey();
1772
+ isEqual(other) {
1773
+ return this.binaryString === other.binaryString;
1952
1774
  }
1953
- // Traverses the map in key order and calls the specified action function
1954
- // for each key/value pair. If action returns true, traversal is aborted.
1955
- // Returns the first truthy value returned by action, or the last falsey
1956
- // value returned by action.
1957
- inorderTraversal(action) {
1958
- return this.root.inorderTraversal(action);
1775
+ }
1776
+ ByteString.EMPTY_BYTE_STRING = new ByteString('');
1777
+ /**
1778
+ * Helper function to convert an Uint8array to a binary string.
1779
+ */
1780
+ function binaryStringFromUint8Array(array) {
1781
+ let binaryString = '';
1782
+ for (let i = 0; i < array.length; ++i) {
1783
+ binaryString += String.fromCharCode(array[i]);
1959
1784
  }
1960
- forEach(fn) {
1961
- this.inorderTraversal((k, v) => {
1962
- fn(k, v);
1963
- return false;
1964
- });
1785
+ return binaryString;
1786
+ }
1787
+ /**
1788
+ * Helper function to convert a binary string to an Uint8Array.
1789
+ */
1790
+ function uint8ArrayFromBinaryString(binaryString) {
1791
+ const buffer = new Uint8Array(binaryString.length);
1792
+ for (let i = 0; i < binaryString.length; i++) {
1793
+ buffer[i] = binaryString.charCodeAt(i);
1965
1794
  }
1966
- toString() {
1967
- const descriptions = [];
1968
- this.inorderTraversal((k, v) => {
1969
- descriptions.push(`${k}:${v}`);
1970
- return false;
1971
- });
1972
- return `{${descriptions.join(', ')}}`;
1795
+ return buffer;
1796
+ }
1797
+
1798
+ /**
1799
+ * @license
1800
+ * Copyright 2020 Google LLC
1801
+ *
1802
+ * Licensed under the Apache License, Version 2.0 (the "License");
1803
+ * you may not use this file except in compliance with the License.
1804
+ * You may obtain a copy of the License at
1805
+ *
1806
+ * http://www.apache.org/licenses/LICENSE-2.0
1807
+ *
1808
+ * Unless required by applicable law or agreed to in writing, software
1809
+ * distributed under the License is distributed on an "AS IS" BASIS,
1810
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1811
+ * See the License for the specific language governing permissions and
1812
+ * limitations under the License.
1813
+ */
1814
+ // A RegExp matching ISO 8601 UTC timestamps with optional fraction.
1815
+ const ISO_TIMESTAMP_REG_EXP = new RegExp(/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.(\d+))?Z$/);
1816
+ /**
1817
+ * Converts the possible Proto values for a timestamp value into a "seconds and
1818
+ * nanos" representation.
1819
+ */
1820
+ function normalizeTimestamp(date) {
1821
+ hardAssert(!!date);
1822
+ // The json interface (for the browser) will return an iso timestamp string,
1823
+ // while the proto js library (for node) will return a
1824
+ // google.protobuf.Timestamp instance.
1825
+ if (typeof date === 'string') {
1826
+ // The date string can have higher precision (nanos) than the Date class
1827
+ // (millis), so we do some custom parsing here.
1828
+ // Parse the nanos right out of the string.
1829
+ let nanos = 0;
1830
+ const fraction = ISO_TIMESTAMP_REG_EXP.exec(date);
1831
+ hardAssert(!!fraction);
1832
+ if (fraction[1]) {
1833
+ // Pad the fraction out to 9 digits (nanos).
1834
+ let nanoStr = fraction[1];
1835
+ nanoStr = (nanoStr + '000000000').substr(0, 9);
1836
+ nanos = Number(nanoStr);
1837
+ }
1838
+ // Parse the date to get the seconds.
1839
+ const parsedDate = new Date(date);
1840
+ const seconds = Math.floor(parsedDate.getTime() / 1000);
1841
+ return { seconds, nanos };
1973
1842
  }
1974
- // Traverses the map in reverse key order and calls the specified action
1975
- // function for each key/value pair. If action returns true, traversal is
1976
- // aborted.
1977
- // Returns the first truthy value returned by action, or the last falsey
1978
- // value returned by action.
1979
- reverseTraversal(action) {
1980
- return this.root.reverseTraversal(action);
1843
+ else {
1844
+ // TODO(b/37282237): Use strings for Proto3 timestamps
1845
+ // assert(!this.options.useProto3Json,
1846
+ // 'The timestamp instance format requires Proto JS.');
1847
+ const seconds = normalizeNumber(date.seconds);
1848
+ const nanos = normalizeNumber(date.nanos);
1849
+ return { seconds, nanos };
1981
1850
  }
1982
- // Returns an iterator over the SortedMap.
1983
- getIterator() {
1984
- return new SortedMapIterator(this.root, null, this.comparator, false);
1851
+ }
1852
+ /**
1853
+ * Converts the possible Proto types for numbers into a JavaScript number.
1854
+ * Returns 0 if the value is not numeric.
1855
+ */
1856
+ function normalizeNumber(value) {
1857
+ // TODO(bjornick): Handle int64 greater than 53 bits.
1858
+ if (typeof value === 'number') {
1859
+ return value;
1985
1860
  }
1986
- getIteratorFrom(key) {
1987
- return new SortedMapIterator(this.root, key, this.comparator, false);
1861
+ else if (typeof value === 'string') {
1862
+ return Number(value);
1988
1863
  }
1989
- getReverseIterator() {
1990
- return new SortedMapIterator(this.root, null, this.comparator, true);
1864
+ else {
1865
+ return 0;
1991
1866
  }
1992
- getReverseIteratorFrom(key) {
1993
- return new SortedMapIterator(this.root, key, this.comparator, true);
1867
+ }
1868
+ /** Converts the possible Proto types for Blobs into a ByteString. */
1869
+ function normalizeByteString(blob) {
1870
+ if (typeof blob === 'string') {
1871
+ return ByteString.fromBase64String(blob);
1994
1872
  }
1995
- } // end SortedMap
1996
- // An iterator over an LLRBNode.
1997
- class SortedMapIterator {
1998
- constructor(node, startKey, comparator, isReverse) {
1999
- this.isReverse = isReverse;
2000
- this.nodeStack = [];
2001
- let cmp = 1;
2002
- while (!node.isEmpty()) {
2003
- cmp = startKey ? comparator(node.key, startKey) : 1;
2004
- // flip the comparison if we're going in reverse
2005
- if (startKey && isReverse) {
2006
- cmp *= -1;
2007
- }
2008
- if (cmp < 0) {
2009
- // This node is less than our start key. ignore it
2010
- if (this.isReverse) {
2011
- node = node.left;
2012
- }
2013
- else {
2014
- node = node.right;
2015
- }
2016
- }
2017
- else if (cmp === 0) {
2018
- // This node is exactly equal to our start key. Push it on the stack,
2019
- // but stop iterating;
2020
- this.nodeStack.push(node);
2021
- break;
2022
- }
2023
- else {
2024
- // This node is greater than our start key, add it to the stack and move
2025
- // to the next one
2026
- this.nodeStack.push(node);
2027
- if (this.isReverse) {
2028
- node = node.right;
2029
- }
2030
- else {
2031
- node = node.left;
2032
- }
2033
- }
1873
+ else {
1874
+ return ByteString.fromUint8Array(blob);
1875
+ }
1876
+ }
1877
+
1878
+ /**
1879
+ * @license
1880
+ * Copyright 2017 Google LLC
1881
+ *
1882
+ * Licensed under the Apache License, Version 2.0 (the "License");
1883
+ * you may not use this file except in compliance with the License.
1884
+ * You may obtain a copy of the License at
1885
+ *
1886
+ * http://www.apache.org/licenses/LICENSE-2.0
1887
+ *
1888
+ * Unless required by applicable law or agreed to in writing, software
1889
+ * distributed under the License is distributed on an "AS IS" BASIS,
1890
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1891
+ * See the License for the specific language governing permissions and
1892
+ * limitations under the License.
1893
+ */
1894
+ // The earliest date supported by Firestore timestamps (0001-01-01T00:00:00Z).
1895
+ const MIN_SECONDS = -62135596800;
1896
+ // Number of nanoseconds in a millisecond.
1897
+ const MS_TO_NANOS = 1e6;
1898
+ /**
1899
+ * A `Timestamp` represents a point in time independent of any time zone or
1900
+ * calendar, represented as seconds and fractions of seconds at nanosecond
1901
+ * resolution in UTC Epoch time.
1902
+ *
1903
+ * It is encoded using the Proleptic Gregorian Calendar which extends the
1904
+ * Gregorian calendar backwards to year one. It is encoded assuming all minutes
1905
+ * are 60 seconds long, i.e. leap seconds are "smeared" so that no leap second
1906
+ * table is needed for interpretation. Range is from 0001-01-01T00:00:00Z to
1907
+ * 9999-12-31T23:59:59.999999999Z.
1908
+ *
1909
+ * For examples and further specifications, refer to the
1910
+ * {@link https://github.com/google/protobuf/blob/master/src/google/protobuf/timestamp.proto | Timestamp definition}.
1911
+ */
1912
+ class Timestamp {
1913
+ /**
1914
+ * Creates a new timestamp.
1915
+ *
1916
+ * @param seconds - The number of seconds of UTC time since Unix epoch
1917
+ * 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
1918
+ * 9999-12-31T23:59:59Z inclusive.
1919
+ * @param nanoseconds - The non-negative fractions of a second at nanosecond
1920
+ * resolution. Negative second values with fractions must still have
1921
+ * non-negative nanoseconds values that count forward in time. Must be
1922
+ * from 0 to 999,999,999 inclusive.
1923
+ */
1924
+ constructor(
1925
+ /**
1926
+ * The number of seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z.
1927
+ */
1928
+ seconds,
1929
+ /**
1930
+ * The fractions of a second at nanosecond resolution.*
1931
+ */
1932
+ nanoseconds) {
1933
+ this.seconds = seconds;
1934
+ this.nanoseconds = nanoseconds;
1935
+ if (nanoseconds < 0) {
1936
+ throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp nanoseconds out of range: ' + nanoseconds);
1937
+ }
1938
+ if (nanoseconds >= 1e9) {
1939
+ throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp nanoseconds out of range: ' + nanoseconds);
1940
+ }
1941
+ if (seconds < MIN_SECONDS) {
1942
+ throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp seconds out of range: ' + seconds);
1943
+ }
1944
+ // This will break in the year 10,000.
1945
+ if (seconds >= 253402300800) {
1946
+ throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp seconds out of range: ' + seconds);
2034
1947
  }
2035
1948
  }
2036
- getNext() {
2037
- let node = this.nodeStack.pop();
2038
- const result = { key: node.key, value: node.value };
2039
- if (this.isReverse) {
2040
- node = node.left;
2041
- while (!node.isEmpty()) {
2042
- this.nodeStack.push(node);
2043
- node = node.right;
1949
+ /**
1950
+ * Creates a new timestamp with the current date, with millisecond precision.
1951
+ *
1952
+ * @returns a new timestamp representing the current date.
1953
+ */
1954
+ static now() {
1955
+ return Timestamp.fromMillis(Date.now());
1956
+ }
1957
+ /**
1958
+ * Creates a new timestamp from the given date.
1959
+ *
1960
+ * @param date - The date to initialize the `Timestamp` from.
1961
+ * @returns A new `Timestamp` representing the same point in time as the given
1962
+ * date.
1963
+ */
1964
+ static fromDate(date) {
1965
+ return Timestamp.fromMillis(date.getTime());
1966
+ }
1967
+ /**
1968
+ * Creates a new timestamp from the given number of milliseconds.
1969
+ *
1970
+ * @param milliseconds - Number of milliseconds since Unix epoch
1971
+ * 1970-01-01T00:00:00Z.
1972
+ * @returns A new `Timestamp` representing the same point in time as the given
1973
+ * number of milliseconds.
1974
+ */
1975
+ static fromMillis(milliseconds) {
1976
+ const seconds = Math.floor(milliseconds / 1000);
1977
+ const nanos = Math.floor((milliseconds - seconds * 1000) * MS_TO_NANOS);
1978
+ return new Timestamp(seconds, nanos);
1979
+ }
1980
+ /**
1981
+ * Converts a `Timestamp` to a JavaScript `Date` object. This conversion
1982
+ * causes a loss of precision since `Date` objects only support millisecond
1983
+ * precision.
1984
+ *
1985
+ * @returns JavaScript `Date` object representing the same point in time as
1986
+ * this `Timestamp`, with millisecond precision.
1987
+ */
1988
+ toDate() {
1989
+ return new Date(this.toMillis());
1990
+ }
1991
+ /**
1992
+ * Converts a `Timestamp` to a numeric timestamp (in milliseconds since
1993
+ * epoch). This operation causes a loss of precision.
1994
+ *
1995
+ * @returns The point in time corresponding to this timestamp, represented as
1996
+ * the number of milliseconds since Unix epoch 1970-01-01T00:00:00Z.
1997
+ */
1998
+ toMillis() {
1999
+ return this.seconds * 1000 + this.nanoseconds / MS_TO_NANOS;
2000
+ }
2001
+ _compareTo(other) {
2002
+ if (this.seconds === other.seconds) {
2003
+ return primitiveComparator(this.nanoseconds, other.nanoseconds);
2004
+ }
2005
+ return primitiveComparator(this.seconds, other.seconds);
2006
+ }
2007
+ /**
2008
+ * Returns true if this `Timestamp` is equal to the provided one.
2009
+ *
2010
+ * @param other - The `Timestamp` to compare against.
2011
+ * @returns true if this `Timestamp` is equal to the provided one.
2012
+ */
2013
+ isEqual(other) {
2014
+ return (other.seconds === this.seconds && other.nanoseconds === this.nanoseconds);
2015
+ }
2016
+ /** Returns a textual representation of this `Timestamp`. */
2017
+ toString() {
2018
+ return ('Timestamp(seconds=' +
2019
+ this.seconds +
2020
+ ', nanoseconds=' +
2021
+ this.nanoseconds +
2022
+ ')');
2023
+ }
2024
+ /** Returns a JSON-serializable representation of this `Timestamp`. */
2025
+ toJSON() {
2026
+ return { seconds: this.seconds, nanoseconds: this.nanoseconds };
2027
+ }
2028
+ /**
2029
+ * Converts this object to a primitive string, which allows `Timestamp` objects
2030
+ * to be compared using the `>`, `<=`, `>=` and `>` operators.
2031
+ */
2032
+ valueOf() {
2033
+ // This method returns a string of the form <seconds>.<nanoseconds> where
2034
+ // <seconds> is translated to have a non-negative value and both <seconds>
2035
+ // and <nanoseconds> are left-padded with zeroes to be a consistent length.
2036
+ // Strings with this format then have a lexiographical ordering that matches
2037
+ // the expected ordering. The <seconds> translation is done to avoid having
2038
+ // a leading negative sign (i.e. a leading '-' character) in its string
2039
+ // representation, which would affect its lexiographical ordering.
2040
+ const adjustedSeconds = this.seconds - MIN_SECONDS;
2041
+ // Note: Up to 12 decimal digits are required to represent all valid
2042
+ // 'seconds' values.
2043
+ const formattedSeconds = String(adjustedSeconds).padStart(12, '0');
2044
+ const formattedNanoseconds = String(this.nanoseconds).padStart(9, '0');
2045
+ return formattedSeconds + '.' + formattedNanoseconds;
2046
+ }
2047
+ }
2048
+
2049
+ /**
2050
+ * @license
2051
+ * Copyright 2020 Google LLC
2052
+ *
2053
+ * Licensed under the Apache License, Version 2.0 (the "License");
2054
+ * you may not use this file except in compliance with the License.
2055
+ * You may obtain a copy of the License at
2056
+ *
2057
+ * http://www.apache.org/licenses/LICENSE-2.0
2058
+ *
2059
+ * Unless required by applicable law or agreed to in writing, software
2060
+ * distributed under the License is distributed on an "AS IS" BASIS,
2061
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2062
+ * See the License for the specific language governing permissions and
2063
+ * limitations under the License.
2064
+ */
2065
+ /**
2066
+ * Represents a locally-applied ServerTimestamp.
2067
+ *
2068
+ * Server Timestamps are backed by MapValues that contain an internal field
2069
+ * `__type__` with a value of `server_timestamp`. The previous value and local
2070
+ * write time are stored in its `__previous_value__` and `__local_write_time__`
2071
+ * fields respectively.
2072
+ *
2073
+ * Notes:
2074
+ * - ServerTimestampValue instances are created as the result of applying a
2075
+ * transform. They can only exist in the local view of a document. Therefore
2076
+ * they do not need to be parsed or serialized.
2077
+ * - When evaluated locally (e.g. for snapshot.data()), they by default
2078
+ * evaluate to `null`. This behavior can be configured by passing custom
2079
+ * FieldValueOptions to value().
2080
+ * - With respect to other ServerTimestampValues, they sort by their
2081
+ * localWriteTime.
2082
+ */
2083
+ const SERVER_TIMESTAMP_SENTINEL = 'server_timestamp';
2084
+ const TYPE_KEY = '__type__';
2085
+ const PREVIOUS_VALUE_KEY = '__previous_value__';
2086
+ const LOCAL_WRITE_TIME_KEY = '__local_write_time__';
2087
+ function isServerTimestamp(value) {
2088
+ var _a, _b;
2089
+ const type = (_b = (((_a = value === null || value === void 0 ? void 0 : value.mapValue) === null || _a === void 0 ? void 0 : _a.fields) || {})[TYPE_KEY]) === null || _b === void 0 ? void 0 : _b.stringValue;
2090
+ return type === SERVER_TIMESTAMP_SENTINEL;
2091
+ }
2092
+ /**
2093
+ * Returns the value of the field before this ServerTimestamp was set.
2094
+ *
2095
+ * Preserving the previous values allows the user to display the last resoled
2096
+ * value until the backend responds with the timestamp.
2097
+ */
2098
+ function getPreviousValue(value) {
2099
+ const previousValue = value.mapValue.fields[PREVIOUS_VALUE_KEY];
2100
+ if (isServerTimestamp(previousValue)) {
2101
+ return getPreviousValue(previousValue);
2102
+ }
2103
+ return previousValue;
2104
+ }
2105
+ /**
2106
+ * Returns the local time at which this timestamp was first set.
2107
+ */
2108
+ function getLocalWriteTime(value) {
2109
+ const localWriteTime = normalizeTimestamp(value.mapValue.fields[LOCAL_WRITE_TIME_KEY].timestampValue);
2110
+ return new Timestamp(localWriteTime.seconds, localWriteTime.nanos);
2111
+ }
2112
+
2113
+ /**
2114
+ * @license
2115
+ * Copyright 2020 Google LLC
2116
+ *
2117
+ * Licensed under the Apache License, Version 2.0 (the "License");
2118
+ * you may not use this file except in compliance with the License.
2119
+ * You may obtain a copy of the License at
2120
+ *
2121
+ * http://www.apache.org/licenses/LICENSE-2.0
2122
+ *
2123
+ * Unless required by applicable law or agreed to in writing, software
2124
+ * distributed under the License is distributed on an "AS IS" BASIS,
2125
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2126
+ * See the License for the specific language governing permissions and
2127
+ * limitations under the License.
2128
+ */
2129
+ const MAX_VALUE_TYPE = '__max__';
2130
+ const MAX_VALUE = {
2131
+ mapValue: {
2132
+ fields: {
2133
+ '__type__': { stringValue: MAX_VALUE_TYPE }
2134
+ }
2135
+ }
2136
+ };
2137
+ /** Extracts the backend's type order for the provided value. */
2138
+ function typeOrder(value) {
2139
+ if ('nullValue' in value) {
2140
+ return 0 /* TypeOrder.NullValue */;
2141
+ }
2142
+ else if ('booleanValue' in value) {
2143
+ return 1 /* TypeOrder.BooleanValue */;
2144
+ }
2145
+ else if ('integerValue' in value || 'doubleValue' in value) {
2146
+ return 2 /* TypeOrder.NumberValue */;
2147
+ }
2148
+ else if ('timestampValue' in value) {
2149
+ return 3 /* TypeOrder.TimestampValue */;
2150
+ }
2151
+ else if ('stringValue' in value) {
2152
+ return 5 /* TypeOrder.StringValue */;
2153
+ }
2154
+ else if ('bytesValue' in value) {
2155
+ return 6 /* TypeOrder.BlobValue */;
2156
+ }
2157
+ else if ('referenceValue' in value) {
2158
+ return 7 /* TypeOrder.RefValue */;
2159
+ }
2160
+ else if ('geoPointValue' in value) {
2161
+ return 8 /* TypeOrder.GeoPointValue */;
2162
+ }
2163
+ else if ('arrayValue' in value) {
2164
+ return 9 /* TypeOrder.ArrayValue */;
2165
+ }
2166
+ else if ('mapValue' in value) {
2167
+ if (isServerTimestamp(value)) {
2168
+ return 4 /* TypeOrder.ServerTimestampValue */;
2169
+ }
2170
+ else if (isMaxValue(value)) {
2171
+ return 9007199254740991 /* TypeOrder.MaxValue */;
2172
+ }
2173
+ return 10 /* TypeOrder.ObjectValue */;
2174
+ }
2175
+ else {
2176
+ return fail();
2177
+ }
2178
+ }
2179
+ /** Tests `left` and `right` for equality based on the backend semantics. */
2180
+ function valueEquals(left, right) {
2181
+ if (left === right) {
2182
+ return true;
2183
+ }
2184
+ const leftType = typeOrder(left);
2185
+ const rightType = typeOrder(right);
2186
+ if (leftType !== rightType) {
2187
+ return false;
2188
+ }
2189
+ switch (leftType) {
2190
+ case 0 /* TypeOrder.NullValue */:
2191
+ return true;
2192
+ case 1 /* TypeOrder.BooleanValue */:
2193
+ return left.booleanValue === right.booleanValue;
2194
+ case 4 /* TypeOrder.ServerTimestampValue */:
2195
+ return getLocalWriteTime(left).isEqual(getLocalWriteTime(right));
2196
+ case 3 /* TypeOrder.TimestampValue */:
2197
+ return timestampEquals(left, right);
2198
+ case 5 /* TypeOrder.StringValue */:
2199
+ return left.stringValue === right.stringValue;
2200
+ case 6 /* TypeOrder.BlobValue */:
2201
+ return blobEquals(left, right);
2202
+ case 7 /* TypeOrder.RefValue */:
2203
+ return left.referenceValue === right.referenceValue;
2204
+ case 8 /* TypeOrder.GeoPointValue */:
2205
+ return geoPointEquals(left, right);
2206
+ case 2 /* TypeOrder.NumberValue */:
2207
+ return numberEquals(left, right);
2208
+ case 9 /* TypeOrder.ArrayValue */:
2209
+ return arrayEquals(left.arrayValue.values || [], right.arrayValue.values || [], valueEquals);
2210
+ case 10 /* TypeOrder.ObjectValue */:
2211
+ return objectEquals(left, right);
2212
+ case 9007199254740991 /* TypeOrder.MaxValue */:
2213
+ return true;
2214
+ default:
2215
+ return fail();
2216
+ }
2217
+ }
2218
+ function timestampEquals(left, right) {
2219
+ if (typeof left.timestampValue === 'string' &&
2220
+ typeof right.timestampValue === 'string' &&
2221
+ left.timestampValue.length === right.timestampValue.length) {
2222
+ // Use string equality for ISO 8601 timestamps
2223
+ return left.timestampValue === right.timestampValue;
2224
+ }
2225
+ const leftTimestamp = normalizeTimestamp(left.timestampValue);
2226
+ const rightTimestamp = normalizeTimestamp(right.timestampValue);
2227
+ return (leftTimestamp.seconds === rightTimestamp.seconds &&
2228
+ leftTimestamp.nanos === rightTimestamp.nanos);
2229
+ }
2230
+ function geoPointEquals(left, right) {
2231
+ return (normalizeNumber(left.geoPointValue.latitude) ===
2232
+ normalizeNumber(right.geoPointValue.latitude) &&
2233
+ normalizeNumber(left.geoPointValue.longitude) ===
2234
+ normalizeNumber(right.geoPointValue.longitude));
2235
+ }
2236
+ function blobEquals(left, right) {
2237
+ return normalizeByteString(left.bytesValue).isEqual(normalizeByteString(right.bytesValue));
2238
+ }
2239
+ function numberEquals(left, right) {
2240
+ if ('integerValue' in left && 'integerValue' in right) {
2241
+ return (normalizeNumber(left.integerValue) === normalizeNumber(right.integerValue));
2242
+ }
2243
+ else if ('doubleValue' in left && 'doubleValue' in right) {
2244
+ const n1 = normalizeNumber(left.doubleValue);
2245
+ const n2 = normalizeNumber(right.doubleValue);
2246
+ if (n1 === n2) {
2247
+ return isNegativeZero(n1) === isNegativeZero(n2);
2248
+ }
2249
+ else {
2250
+ return isNaN(n1) && isNaN(n2);
2251
+ }
2252
+ }
2253
+ return false;
2254
+ }
2255
+ function objectEquals(left, right) {
2256
+ const leftMap = left.mapValue.fields || {};
2257
+ const rightMap = right.mapValue.fields || {};
2258
+ if (objectSize(leftMap) !== objectSize(rightMap)) {
2259
+ return false;
2260
+ }
2261
+ for (const key in leftMap) {
2262
+ if (leftMap.hasOwnProperty(key)) {
2263
+ if (rightMap[key] === undefined ||
2264
+ !valueEquals(leftMap[key], rightMap[key])) {
2265
+ return false;
2044
2266
  }
2045
2267
  }
2046
- else {
2047
- node = node.right;
2048
- while (!node.isEmpty()) {
2049
- this.nodeStack.push(node);
2050
- node = node.left;
2051
- }
2268
+ }
2269
+ return true;
2270
+ }
2271
+ /** Returns true if the ArrayValue contains the specified element. */
2272
+ function arrayValueContains(haystack, needle) {
2273
+ return ((haystack.values || []).find(v => valueEquals(v, needle)) !== undefined);
2274
+ }
2275
+ function valueCompare(left, right) {
2276
+ if (left === right) {
2277
+ return 0;
2278
+ }
2279
+ const leftType = typeOrder(left);
2280
+ const rightType = typeOrder(right);
2281
+ if (leftType !== rightType) {
2282
+ return primitiveComparator(leftType, rightType);
2283
+ }
2284
+ switch (leftType) {
2285
+ case 0 /* TypeOrder.NullValue */:
2286
+ case 9007199254740991 /* TypeOrder.MaxValue */:
2287
+ return 0;
2288
+ case 1 /* TypeOrder.BooleanValue */:
2289
+ return primitiveComparator(left.booleanValue, right.booleanValue);
2290
+ case 2 /* TypeOrder.NumberValue */:
2291
+ return compareNumbers(left, right);
2292
+ case 3 /* TypeOrder.TimestampValue */:
2293
+ return compareTimestamps(left.timestampValue, right.timestampValue);
2294
+ case 4 /* TypeOrder.ServerTimestampValue */:
2295
+ return compareTimestamps(getLocalWriteTime(left), getLocalWriteTime(right));
2296
+ case 5 /* TypeOrder.StringValue */:
2297
+ return primitiveComparator(left.stringValue, right.stringValue);
2298
+ case 6 /* TypeOrder.BlobValue */:
2299
+ return compareBlobs(left.bytesValue, right.bytesValue);
2300
+ case 7 /* TypeOrder.RefValue */:
2301
+ return compareReferences(left.referenceValue, right.referenceValue);
2302
+ case 8 /* TypeOrder.GeoPointValue */:
2303
+ return compareGeoPoints(left.geoPointValue, right.geoPointValue);
2304
+ case 9 /* TypeOrder.ArrayValue */:
2305
+ return compareArrays(left.arrayValue, right.arrayValue);
2306
+ case 10 /* TypeOrder.ObjectValue */:
2307
+ return compareMaps(left.mapValue, right.mapValue);
2308
+ default:
2309
+ throw fail();
2310
+ }
2311
+ }
2312
+ function compareNumbers(left, right) {
2313
+ const leftNumber = normalizeNumber(left.integerValue || left.doubleValue);
2314
+ const rightNumber = normalizeNumber(right.integerValue || right.doubleValue);
2315
+ if (leftNumber < rightNumber) {
2316
+ return -1;
2317
+ }
2318
+ else if (leftNumber > rightNumber) {
2319
+ return 1;
2320
+ }
2321
+ else if (leftNumber === rightNumber) {
2322
+ return 0;
2323
+ }
2324
+ else {
2325
+ // one or both are NaN.
2326
+ if (isNaN(leftNumber)) {
2327
+ return isNaN(rightNumber) ? 0 : -1;
2328
+ }
2329
+ else {
2330
+ return 1;
2331
+ }
2332
+ }
2333
+ }
2334
+ function compareTimestamps(left, right) {
2335
+ if (typeof left === 'string' &&
2336
+ typeof right === 'string' &&
2337
+ left.length === right.length) {
2338
+ return primitiveComparator(left, right);
2339
+ }
2340
+ const leftTimestamp = normalizeTimestamp(left);
2341
+ const rightTimestamp = normalizeTimestamp(right);
2342
+ const comparison = primitiveComparator(leftTimestamp.seconds, rightTimestamp.seconds);
2343
+ if (comparison !== 0) {
2344
+ return comparison;
2345
+ }
2346
+ return primitiveComparator(leftTimestamp.nanos, rightTimestamp.nanos);
2347
+ }
2348
+ function compareReferences(leftPath, rightPath) {
2349
+ const leftSegments = leftPath.split('/');
2350
+ const rightSegments = rightPath.split('/');
2351
+ for (let i = 0; i < leftSegments.length && i < rightSegments.length; i++) {
2352
+ const comparison = primitiveComparator(leftSegments[i], rightSegments[i]);
2353
+ if (comparison !== 0) {
2354
+ return comparison;
2355
+ }
2356
+ }
2357
+ return primitiveComparator(leftSegments.length, rightSegments.length);
2358
+ }
2359
+ function compareGeoPoints(left, right) {
2360
+ const comparison = primitiveComparator(normalizeNumber(left.latitude), normalizeNumber(right.latitude));
2361
+ if (comparison !== 0) {
2362
+ return comparison;
2363
+ }
2364
+ return primitiveComparator(normalizeNumber(left.longitude), normalizeNumber(right.longitude));
2365
+ }
2366
+ function compareBlobs(left, right) {
2367
+ const leftBytes = normalizeByteString(left);
2368
+ const rightBytes = normalizeByteString(right);
2369
+ return leftBytes.compareTo(rightBytes);
2370
+ }
2371
+ function compareArrays(left, right) {
2372
+ const leftArray = left.values || [];
2373
+ const rightArray = right.values || [];
2374
+ for (let i = 0; i < leftArray.length && i < rightArray.length; ++i) {
2375
+ const compare = valueCompare(leftArray[i], rightArray[i]);
2376
+ if (compare) {
2377
+ return compare;
2378
+ }
2379
+ }
2380
+ return primitiveComparator(leftArray.length, rightArray.length);
2381
+ }
2382
+ function compareMaps(left, right) {
2383
+ if (left === MAX_VALUE.mapValue && right === MAX_VALUE.mapValue) {
2384
+ return 0;
2385
+ }
2386
+ else if (left === MAX_VALUE.mapValue) {
2387
+ return 1;
2388
+ }
2389
+ else if (right === MAX_VALUE.mapValue) {
2390
+ return -1;
2391
+ }
2392
+ const leftMap = left.fields || {};
2393
+ const leftKeys = Object.keys(leftMap);
2394
+ const rightMap = right.fields || {};
2395
+ const rightKeys = Object.keys(rightMap);
2396
+ // Even though MapValues are likely sorted correctly based on their insertion
2397
+ // order (e.g. when received from the backend), local modifications can bring
2398
+ // elements out of order. We need to re-sort the elements to ensure that
2399
+ // canonical IDs are independent of insertion order.
2400
+ leftKeys.sort();
2401
+ rightKeys.sort();
2402
+ for (let i = 0; i < leftKeys.length && i < rightKeys.length; ++i) {
2403
+ const keyCompare = primitiveComparator(leftKeys[i], rightKeys[i]);
2404
+ if (keyCompare !== 0) {
2405
+ return keyCompare;
2406
+ }
2407
+ const compare = valueCompare(leftMap[leftKeys[i]], rightMap[rightKeys[i]]);
2408
+ if (compare !== 0) {
2409
+ return compare;
2052
2410
  }
2053
- return result;
2054
- }
2055
- hasNext() {
2056
- return this.nodeStack.length > 0;
2057
2411
  }
2058
- peek() {
2059
- if (this.nodeStack.length === 0) {
2060
- return null;
2061
- }
2062
- const node = this.nodeStack[this.nodeStack.length - 1];
2063
- return { key: node.key, value: node.value };
2412
+ return primitiveComparator(leftKeys.length, rightKeys.length);
2413
+ }
2414
+ /** Returns a reference value for the provided database and key. */
2415
+ function refValue(databaseId, key) {
2416
+ return {
2417
+ referenceValue: `projects/${databaseId.projectId}/databases/${databaseId.database}/documents/${key.path.canonicalString()}`
2418
+ };
2419
+ }
2420
+ /** Returns true if `value` is an ArrayValue. */
2421
+ function isArray(value) {
2422
+ return !!value && 'arrayValue' in value;
2423
+ }
2424
+ /** Returns true if `value` is a NullValue. */
2425
+ function isNullValue(value) {
2426
+ return !!value && 'nullValue' in value;
2427
+ }
2428
+ /** Returns true if `value` is NaN. */
2429
+ function isNanValue(value) {
2430
+ return !!value && 'doubleValue' in value && isNaN(Number(value.doubleValue));
2431
+ }
2432
+ /** Returns true if `value` is a MapValue. */
2433
+ function isMapValue(value) {
2434
+ return !!value && 'mapValue' in value;
2435
+ }
2436
+ /** Creates a deep copy of `source`. */
2437
+ function deepClone(source) {
2438
+ if (source.geoPointValue) {
2439
+ return { geoPointValue: Object.assign({}, source.geoPointValue) };
2064
2440
  }
2065
- } // end SortedMapIterator
2066
- // Represents a node in a Left-leaning Red-Black tree.
2067
- class LLRBNode {
2068
- constructor(key, value, color, left, right) {
2069
- this.key = key;
2070
- this.value = value;
2071
- this.color = color != null ? color : LLRBNode.RED;
2072
- this.left = left != null ? left : LLRBNode.EMPTY;
2073
- this.right = right != null ? right : LLRBNode.EMPTY;
2074
- this.size = this.left.size + 1 + this.right.size;
2441
+ else if (source.timestampValue &&
2442
+ typeof source.timestampValue === 'object') {
2443
+ return { timestampValue: Object.assign({}, source.timestampValue) };
2075
2444
  }
2076
- // Returns a copy of the current node, optionally replacing pieces of it.
2077
- copy(key, value, color, left, right) {
2078
- return new LLRBNode(key != null ? key : this.key, value != null ? value : this.value, color != null ? color : this.color, left != null ? left : this.left, right != null ? right : this.right);
2445
+ else if (source.mapValue) {
2446
+ const target = { mapValue: { fields: {} } };
2447
+ forEach(source.mapValue.fields, (key, val) => (target.mapValue.fields[key] = deepClone(val)));
2448
+ return target;
2079
2449
  }
2080
- isEmpty() {
2081
- return false;
2450
+ else if (source.arrayValue) {
2451
+ const target = { arrayValue: { values: [] } };
2452
+ for (let i = 0; i < (source.arrayValue.values || []).length; ++i) {
2453
+ target.arrayValue.values[i] = deepClone(source.arrayValue.values[i]);
2454
+ }
2455
+ return target;
2082
2456
  }
2083
- // Traverses the tree in key order and calls the specified action function
2084
- // for each node. If action returns true, traversal is aborted.
2085
- // Returns the first truthy value returned by action, or the last falsey
2086
- // value returned by action.
2087
- inorderTraversal(action) {
2088
- return (this.left.inorderTraversal(action) ||
2089
- action(this.key, this.value) ||
2090
- this.right.inorderTraversal(action));
2457
+ else {
2458
+ return Object.assign({}, source);
2091
2459
  }
2092
- // Traverses the tree in reverse key order and calls the specified action
2093
- // function for each node. If action returns true, traversal is aborted.
2094
- // Returns the first truthy value returned by action, or the last falsey
2095
- // value returned by action.
2096
- reverseTraversal(action) {
2097
- return (this.right.reverseTraversal(action) ||
2098
- action(this.key, this.value) ||
2099
- this.left.reverseTraversal(action));
2460
+ }
2461
+ /** Returns true if the Value represents the canonical {@link #MAX_VALUE} . */
2462
+ function isMaxValue(value) {
2463
+ return ((((value.mapValue || {}).fields || {})['__type__'] || {}).stringValue ===
2464
+ MAX_VALUE_TYPE);
2465
+ }
2466
+
2467
+ /**
2468
+ * @license
2469
+ * Copyright 2022 Google LLC
2470
+ *
2471
+ * Licensed under the Apache License, Version 2.0 (the "License");
2472
+ * you may not use this file except in compliance with the License.
2473
+ * You may obtain a copy of the License at
2474
+ *
2475
+ * http://www.apache.org/licenses/LICENSE-2.0
2476
+ *
2477
+ * Unless required by applicable law or agreed to in writing, software
2478
+ * distributed under the License is distributed on an "AS IS" BASIS,
2479
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2480
+ * See the License for the specific language governing permissions and
2481
+ * limitations under the License.
2482
+ */
2483
+ /**
2484
+ * Represents a bound of a query.
2485
+ *
2486
+ * The bound is specified with the given components representing a position and
2487
+ * whether it's just before or just after the position (relative to whatever the
2488
+ * query order is).
2489
+ *
2490
+ * The position represents a logical index position for a query. It's a prefix
2491
+ * of values for the (potentially implicit) order by clauses of a query.
2492
+ *
2493
+ * Bound provides a function to determine whether a document comes before or
2494
+ * after a bound. This is influenced by whether the position is just before or
2495
+ * just after the provided values.
2496
+ */
2497
+ class Bound {
2498
+ constructor(position, inclusive) {
2499
+ this.position = position;
2500
+ this.inclusive = inclusive;
2100
2501
  }
2101
- // Returns the minimum node in the tree.
2102
- min() {
2103
- if (this.left.isEmpty()) {
2104
- return this;
2105
- }
2106
- else {
2107
- return this.left.min();
2108
- }
2502
+ }
2503
+ function boundEquals(left, right) {
2504
+ if (left === null) {
2505
+ return right === null;
2109
2506
  }
2110
- // Returns the maximum key in the tree.
2111
- minKey() {
2112
- return this.min().key;
2507
+ else if (right === null) {
2508
+ return false;
2113
2509
  }
2114
- // Returns the maximum key in the tree.
2115
- maxKey() {
2116
- if (this.right.isEmpty()) {
2117
- return this.key;
2118
- }
2119
- else {
2120
- return this.right.maxKey();
2121
- }
2510
+ if (left.inclusive !== right.inclusive ||
2511
+ left.position.length !== right.position.length) {
2512
+ return false;
2122
2513
  }
2123
- // Returns new tree, with the key/value added.
2124
- insert(key, value, comparator) {
2125
- let n = this;
2126
- const cmp = comparator(key, n.key);
2127
- if (cmp < 0) {
2128
- n = n.copy(null, null, null, n.left.insert(key, value, comparator), null);
2129
- }
2130
- else if (cmp === 0) {
2131
- n = n.copy(null, value, null, null, null);
2132
- }
2133
- else {
2134
- n = n.copy(null, null, null, null, n.right.insert(key, value, comparator));
2514
+ for (let i = 0; i < left.position.length; i++) {
2515
+ const leftPosition = left.position[i];
2516
+ const rightPosition = right.position[i];
2517
+ if (!valueEquals(leftPosition, rightPosition)) {
2518
+ return false;
2135
2519
  }
2136
- return n.fixUp();
2137
2520
  }
2138
- removeMin() {
2139
- if (this.left.isEmpty()) {
2140
- return LLRBNode.EMPTY;
2141
- }
2142
- let n = this;
2143
- if (!n.left.isRed() && !n.left.left.isRed()) {
2144
- n = n.moveRedLeft();
2145
- }
2146
- n = n.copy(null, null, null, n.left.removeMin(), null);
2147
- return n.fixUp();
2521
+ return true;
2522
+ }
2523
+
2524
+ /**
2525
+ * @license
2526
+ * Copyright 2022 Google LLC
2527
+ *
2528
+ * Licensed under the Apache License, Version 2.0 (the "License");
2529
+ * you may not use this file except in compliance with the License.
2530
+ * You may obtain a copy of the License at
2531
+ *
2532
+ * http://www.apache.org/licenses/LICENSE-2.0
2533
+ *
2534
+ * Unless required by applicable law or agreed to in writing, software
2535
+ * distributed under the License is distributed on an "AS IS" BASIS,
2536
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2537
+ * See the License for the specific language governing permissions and
2538
+ * limitations under the License.
2539
+ */
2540
+ class Filter {
2541
+ }
2542
+ class FieldFilter extends Filter {
2543
+ constructor(field, op, value) {
2544
+ super();
2545
+ this.field = field;
2546
+ this.op = op;
2547
+ this.value = value;
2148
2548
  }
2149
- // Returns new tree, with the specified item removed.
2150
- remove(key, comparator) {
2151
- let smallest;
2152
- let n = this;
2153
- if (comparator(key, n.key) < 0) {
2154
- if (!n.left.isEmpty() && !n.left.isRed() && !n.left.left.isRed()) {
2155
- n = n.moveRedLeft();
2156
- }
2157
- n = n.copy(null, null, null, n.left.remove(key, comparator), null);
2158
- }
2159
- else {
2160
- if (n.left.isRed()) {
2161
- n = n.rotateRight();
2162
- }
2163
- if (!n.right.isEmpty() && !n.right.isRed() && !n.right.left.isRed()) {
2164
- n = n.moveRedRight();
2549
+ /**
2550
+ * Creates a filter based on the provided arguments.
2551
+ */
2552
+ static create(field, op, value) {
2553
+ if (field.isKeyField()) {
2554
+ if (op === "in" /* Operator.IN */ || op === "not-in" /* Operator.NOT_IN */) {
2555
+ return this.createKeyFieldInFilter(field, op, value);
2165
2556
  }
2166
- if (comparator(key, n.key) === 0) {
2167
- if (n.right.isEmpty()) {
2168
- return LLRBNode.EMPTY;
2169
- }
2170
- else {
2171
- smallest = n.right.min();
2172
- n = n.copy(smallest.key, smallest.value, null, null, n.right.removeMin());
2173
- }
2557
+ else {
2558
+ return new KeyFieldFilter(field, op, value);
2174
2559
  }
2175
- n = n.copy(null, null, null, null, n.right.remove(key, comparator));
2176
2560
  }
2177
- return n.fixUp();
2178
- }
2179
- isRed() {
2180
- return this.color;
2181
- }
2182
- // Returns new tree after performing any needed rotations.
2183
- fixUp() {
2184
- let n = this;
2185
- if (n.right.isRed() && !n.left.isRed()) {
2186
- n = n.rotateLeft();
2561
+ else if (op === "array-contains" /* Operator.ARRAY_CONTAINS */) {
2562
+ return new ArrayContainsFilter(field, value);
2187
2563
  }
2188
- if (n.left.isRed() && n.left.left.isRed()) {
2189
- n = n.rotateRight();
2564
+ else if (op === "in" /* Operator.IN */) {
2565
+ return new InFilter(field, value);
2190
2566
  }
2191
- if (n.left.isRed() && n.right.isRed()) {
2192
- n = n.colorFlip();
2567
+ else if (op === "not-in" /* Operator.NOT_IN */) {
2568
+ return new NotInFilter(field, value);
2569
+ }
2570
+ else if (op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
2571
+ return new ArrayContainsAnyFilter(field, value);
2572
+ }
2573
+ else {
2574
+ return new FieldFilter(field, op, value);
2193
2575
  }
2194
- return n;
2195
2576
  }
2196
- moveRedLeft() {
2197
- let n = this.colorFlip();
2198
- if (n.right.left.isRed()) {
2199
- n = n.copy(null, null, null, null, n.right.rotateRight());
2200
- n = n.rotateLeft();
2201
- n = n.colorFlip();
2577
+ static createKeyFieldInFilter(field, op, value) {
2578
+ return op === "in" /* Operator.IN */
2579
+ ? new KeyFieldInFilter(field, value)
2580
+ : new KeyFieldNotInFilter(field, value);
2581
+ }
2582
+ matches(doc) {
2583
+ const other = doc.data.field(this.field);
2584
+ // Types do not have to match in NOT_EQUAL filters.
2585
+ if (this.op === "!=" /* Operator.NOT_EQUAL */) {
2586
+ return (other !== null &&
2587
+ this.matchesComparison(valueCompare(other, this.value)));
2202
2588
  }
2203
- return n;
2589
+ // Only compare types with matching backend order (such as double and int).
2590
+ return (other !== null &&
2591
+ typeOrder(this.value) === typeOrder(other) &&
2592
+ this.matchesComparison(valueCompare(other, this.value)));
2204
2593
  }
2205
- moveRedRight() {
2206
- let n = this.colorFlip();
2207
- if (n.left.left.isRed()) {
2208
- n = n.rotateRight();
2209
- n = n.colorFlip();
2594
+ matchesComparison(comparison) {
2595
+ switch (this.op) {
2596
+ case "<" /* Operator.LESS_THAN */:
2597
+ return comparison < 0;
2598
+ case "<=" /* Operator.LESS_THAN_OR_EQUAL */:
2599
+ return comparison <= 0;
2600
+ case "==" /* Operator.EQUAL */:
2601
+ return comparison === 0;
2602
+ case "!=" /* Operator.NOT_EQUAL */:
2603
+ return comparison !== 0;
2604
+ case ">" /* Operator.GREATER_THAN */:
2605
+ return comparison > 0;
2606
+ case ">=" /* Operator.GREATER_THAN_OR_EQUAL */:
2607
+ return comparison >= 0;
2608
+ default:
2609
+ return fail();
2210
2610
  }
2211
- return n;
2212
2611
  }
2213
- rotateLeft() {
2214
- const nl = this.copy(null, null, LLRBNode.RED, null, this.right.left);
2215
- return this.right.copy(null, null, this.color, nl, null);
2612
+ isInequality() {
2613
+ return ([
2614
+ "<" /* Operator.LESS_THAN */,
2615
+ "<=" /* Operator.LESS_THAN_OR_EQUAL */,
2616
+ ">" /* Operator.GREATER_THAN */,
2617
+ ">=" /* Operator.GREATER_THAN_OR_EQUAL */,
2618
+ "!=" /* Operator.NOT_EQUAL */,
2619
+ "not-in" /* Operator.NOT_IN */
2620
+ ].indexOf(this.op) >= 0);
2216
2621
  }
2217
- rotateRight() {
2218
- const nr = this.copy(null, null, LLRBNode.RED, this.left.right, null);
2219
- return this.left.copy(null, null, this.color, null, nr);
2622
+ getFlattenedFilters() {
2623
+ return [this];
2220
2624
  }
2221
- colorFlip() {
2222
- const left = this.left.copy(null, null, !this.left.color, null, null);
2223
- const right = this.right.copy(null, null, !this.right.color, null, null);
2224
- return this.copy(null, null, !this.color, left, right);
2625
+ getFilters() {
2626
+ return [this];
2225
2627
  }
2226
- // For testing.
2227
- checkMaxDepth() {
2228
- const blackDepth = this.check();
2229
- if (Math.pow(2.0, blackDepth) <= this.size + 1) {
2230
- return true;
2628
+ getFirstInequalityField() {
2629
+ if (this.isInequality()) {
2630
+ return this.field;
2631
+ }
2632
+ return null;
2633
+ }
2634
+ }
2635
+ class CompositeFilter extends Filter {
2636
+ constructor(filters, op) {
2637
+ super();
2638
+ this.filters = filters;
2639
+ this.op = op;
2640
+ this.memoizedFlattenedFilters = null;
2641
+ }
2642
+ /**
2643
+ * Creates a filter based on the provided arguments.
2644
+ */
2645
+ static create(filters, op) {
2646
+ return new CompositeFilter(filters, op);
2647
+ }
2648
+ matches(doc) {
2649
+ if (compositeFilterIsConjunction(this)) {
2650
+ // For conjunctions, all filters must match, so return false if any filter doesn't match.
2651
+ return this.filters.find(filter => !filter.matches(doc)) === undefined;
2231
2652
  }
2232
2653
  else {
2233
- return false;
2654
+ // For disjunctions, at least one filter should match.
2655
+ return this.filters.find(filter => filter.matches(doc)) !== undefined;
2234
2656
  }
2235
2657
  }
2236
- // In a balanced RB tree, the black-depth (number of black nodes) from root to
2237
- // leaves is equal on both sides. This function verifies that or asserts.
2238
- check() {
2239
- if (this.isRed() && this.left.isRed()) {
2240
- throw fail();
2241
- }
2242
- if (this.right.isRed()) {
2243
- throw fail();
2658
+ getFlattenedFilters() {
2659
+ if (this.memoizedFlattenedFilters !== null) {
2660
+ return this.memoizedFlattenedFilters;
2244
2661
  }
2245
- const blackDepth = this.left.check();
2246
- if (blackDepth !== this.right.check()) {
2247
- throw fail();
2662
+ this.memoizedFlattenedFilters = this.filters.reduce((result, subfilter) => {
2663
+ return result.concat(subfilter.getFlattenedFilters());
2664
+ }, []);
2665
+ return this.memoizedFlattenedFilters;
2666
+ }
2667
+ // Returns a mutable copy of `this.filters`
2668
+ getFilters() {
2669
+ return Object.assign([], this.filters);
2670
+ }
2671
+ getFirstInequalityField() {
2672
+ const found = this.findFirstMatchingFilter(filter => filter.isInequality());
2673
+ if (found !== null) {
2674
+ return found.field;
2248
2675
  }
2249
- else {
2250
- return blackDepth + (this.isRed() ? 0 : 1);
2676
+ return null;
2677
+ }
2678
+ // Performs a depth-first search to find and return the first FieldFilter in the composite filter
2679
+ // that satisfies the predicate. Returns `null` if none of the FieldFilters satisfy the
2680
+ // predicate.
2681
+ findFirstMatchingFilter(predicate) {
2682
+ for (const fieldFilter of this.getFlattenedFilters()) {
2683
+ if (predicate(fieldFilter)) {
2684
+ return fieldFilter;
2685
+ }
2251
2686
  }
2687
+ return null;
2252
2688
  }
2253
- } // end LLRBNode
2254
- // Empty node is shared between all LLRB trees.
2255
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2256
- LLRBNode.EMPTY = null;
2257
- LLRBNode.RED = true;
2258
- LLRBNode.BLACK = false;
2259
- // Represents an empty node (a leaf node in the Red-Black Tree).
2260
- class LLRBEmptyNode {
2261
- constructor() {
2262
- this.size = 0;
2689
+ }
2690
+ function compositeFilterIsConjunction(compositeFilter) {
2691
+ return compositeFilter.op === "and" /* CompositeOperator.AND */;
2692
+ }
2693
+ function filterEquals(f1, f2) {
2694
+ if (f1 instanceof FieldFilter) {
2695
+ return fieldFilterEquals(f1, f2);
2263
2696
  }
2264
- get key() {
2265
- throw fail();
2697
+ else if (f1 instanceof CompositeFilter) {
2698
+ return compositeFilterEquals(f1, f2);
2266
2699
  }
2267
- get value() {
2268
- throw fail();
2700
+ else {
2701
+ fail();
2269
2702
  }
2270
- get color() {
2271
- throw fail();
2703
+ }
2704
+ function fieldFilterEquals(f1, f2) {
2705
+ return (f2 instanceof FieldFilter &&
2706
+ f1.op === f2.op &&
2707
+ f1.field.isEqual(f2.field) &&
2708
+ valueEquals(f1.value, f2.value));
2709
+ }
2710
+ function compositeFilterEquals(f1, f2) {
2711
+ if (f2 instanceof CompositeFilter &&
2712
+ f1.op === f2.op &&
2713
+ f1.filters.length === f2.filters.length) {
2714
+ const subFiltersMatch = f1.filters.reduce((result, f1Filter, index) => result && filterEquals(f1Filter, f2.filters[index]), true);
2715
+ return subFiltersMatch;
2272
2716
  }
2273
- get left() {
2274
- throw fail();
2717
+ return false;
2718
+ }
2719
+ /** Filter that matches on key fields (i.e. '__name__'). */
2720
+ class KeyFieldFilter extends FieldFilter {
2721
+ constructor(field, op, value) {
2722
+ super(field, op, value);
2723
+ this.key = DocumentKey.fromName(value.referenceValue);
2275
2724
  }
2276
- get right() {
2277
- throw fail();
2725
+ matches(doc) {
2726
+ const comparison = DocumentKey.comparator(doc.key, this.key);
2727
+ return this.matchesComparison(comparison);
2278
2728
  }
2279
- // Returns a copy of the current node.
2280
- copy(key, value, color, left, right) {
2281
- return this;
2729
+ }
2730
+ /** Filter that matches on key fields within an array. */
2731
+ class KeyFieldInFilter extends FieldFilter {
2732
+ constructor(field, value) {
2733
+ super(field, "in" /* Operator.IN */, value);
2734
+ this.keys = extractDocumentKeysFromArrayValue("in" /* Operator.IN */, value);
2282
2735
  }
2283
- // Returns a copy of the tree, with the specified key/value added.
2284
- insert(key, value, comparator) {
2285
- return new LLRBNode(key, value);
2736
+ matches(doc) {
2737
+ return this.keys.some(key => key.isEqual(doc.key));
2286
2738
  }
2287
- // Returns a copy of the tree, with the specified key removed.
2288
- remove(key, comparator) {
2289
- return this;
2739
+ }
2740
+ /** Filter that matches on key fields not present within an array. */
2741
+ class KeyFieldNotInFilter extends FieldFilter {
2742
+ constructor(field, value) {
2743
+ super(field, "not-in" /* Operator.NOT_IN */, value);
2744
+ this.keys = extractDocumentKeysFromArrayValue("not-in" /* Operator.NOT_IN */, value);
2745
+ }
2746
+ matches(doc) {
2747
+ return !this.keys.some(key => key.isEqual(doc.key));
2748
+ }
2749
+ }
2750
+ function extractDocumentKeysFromArrayValue(op, value) {
2751
+ var _a;
2752
+ return (((_a = value.arrayValue) === null || _a === void 0 ? void 0 : _a.values) || []).map(v => {
2753
+ return DocumentKey.fromName(v.referenceValue);
2754
+ });
2755
+ }
2756
+ /** A Filter that implements the array-contains operator. */
2757
+ class ArrayContainsFilter extends FieldFilter {
2758
+ constructor(field, value) {
2759
+ super(field, "array-contains" /* Operator.ARRAY_CONTAINS */, value);
2760
+ }
2761
+ matches(doc) {
2762
+ const other = doc.data.field(this.field);
2763
+ return isArray(other) && arrayValueContains(other.arrayValue, this.value);
2764
+ }
2765
+ }
2766
+ /** A Filter that implements the IN operator. */
2767
+ class InFilter extends FieldFilter {
2768
+ constructor(field, value) {
2769
+ super(field, "in" /* Operator.IN */, value);
2770
+ }
2771
+ matches(doc) {
2772
+ const other = doc.data.field(this.field);
2773
+ return other !== null && arrayValueContains(this.value.arrayValue, other);
2774
+ }
2775
+ }
2776
+ /** A Filter that implements the not-in operator. */
2777
+ class NotInFilter extends FieldFilter {
2778
+ constructor(field, value) {
2779
+ super(field, "not-in" /* Operator.NOT_IN */, value);
2780
+ }
2781
+ matches(doc) {
2782
+ if (arrayValueContains(this.value.arrayValue, { nullValue: 'NULL_VALUE' })) {
2783
+ return false;
2784
+ }
2785
+ const other = doc.data.field(this.field);
2786
+ return other !== null && !arrayValueContains(this.value.arrayValue, other);
2787
+ }
2788
+ }
2789
+ /** A Filter that implements the array-contains-any operator. */
2790
+ class ArrayContainsAnyFilter extends FieldFilter {
2791
+ constructor(field, value) {
2792
+ super(field, "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */, value);
2793
+ }
2794
+ matches(doc) {
2795
+ const other = doc.data.field(this.field);
2796
+ if (!isArray(other) || !other.arrayValue.values) {
2797
+ return false;
2798
+ }
2799
+ return other.arrayValue.values.some(val => arrayValueContains(this.value.arrayValue, val));
2800
+ }
2801
+ }
2802
+
2803
+ /**
2804
+ * @license
2805
+ * Copyright 2022 Google LLC
2806
+ *
2807
+ * Licensed under the Apache License, Version 2.0 (the "License");
2808
+ * you may not use this file except in compliance with the License.
2809
+ * You may obtain a copy of the License at
2810
+ *
2811
+ * http://www.apache.org/licenses/LICENSE-2.0
2812
+ *
2813
+ * Unless required by applicable law or agreed to in writing, software
2814
+ * distributed under the License is distributed on an "AS IS" BASIS,
2815
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2816
+ * See the License for the specific language governing permissions and
2817
+ * limitations under the License.
2818
+ */
2819
+ /**
2820
+ * An ordering on a field, in some Direction. Direction defaults to ASCENDING.
2821
+ */
2822
+ class OrderBy {
2823
+ constructor(field, dir = "asc" /* Direction.ASCENDING */) {
2824
+ this.field = field;
2825
+ this.dir = dir;
2826
+ }
2827
+ }
2828
+ function orderByEquals(left, right) {
2829
+ return left.dir === right.dir && left.field.isEqual(right.field);
2830
+ }
2831
+
2832
+ /**
2833
+ * @license
2834
+ * Copyright 2017 Google LLC
2835
+ *
2836
+ * Licensed under the Apache License, Version 2.0 (the "License");
2837
+ * you may not use this file except in compliance with the License.
2838
+ * You may obtain a copy of the License at
2839
+ *
2840
+ * http://www.apache.org/licenses/LICENSE-2.0
2841
+ *
2842
+ * Unless required by applicable law or agreed to in writing, software
2843
+ * distributed under the License is distributed on an "AS IS" BASIS,
2844
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2845
+ * See the License for the specific language governing permissions and
2846
+ * limitations under the License.
2847
+ */
2848
+ /**
2849
+ * A version of a document in Firestore. This corresponds to the version
2850
+ * timestamp, such as update_time or read_time.
2851
+ */
2852
+ class SnapshotVersion {
2853
+ constructor(timestamp) {
2854
+ this.timestamp = timestamp;
2290
2855
  }
2291
- isEmpty() {
2292
- return true;
2856
+ static fromTimestamp(value) {
2857
+ return new SnapshotVersion(value);
2293
2858
  }
2294
- inorderTraversal(action) {
2295
- return false;
2859
+ static min() {
2860
+ return new SnapshotVersion(new Timestamp(0, 0));
2296
2861
  }
2297
- reverseTraversal(action) {
2298
- return false;
2862
+ static max() {
2863
+ return new SnapshotVersion(new Timestamp(253402300799, 1e9 - 1));
2299
2864
  }
2300
- minKey() {
2301
- return null;
2865
+ compareTo(other) {
2866
+ return this.timestamp._compareTo(other.timestamp);
2302
2867
  }
2303
- maxKey() {
2304
- return null;
2868
+ isEqual(other) {
2869
+ return this.timestamp.isEqual(other.timestamp);
2305
2870
  }
2306
- isRed() {
2307
- return false;
2871
+ /** Returns a number representation of the version for use in spec tests. */
2872
+ toMicroseconds() {
2873
+ // Convert to microseconds.
2874
+ return this.timestamp.seconds * 1e6 + this.timestamp.nanoseconds / 1000;
2308
2875
  }
2309
- // For testing.
2310
- checkMaxDepth() {
2311
- return true;
2876
+ toString() {
2877
+ return 'SnapshotVersion(' + this.timestamp.toString() + ')';
2312
2878
  }
2313
- check() {
2314
- return 0;
2879
+ toTimestamp() {
2880
+ return this.timestamp;
2315
2881
  }
2316
- } // end LLRBEmptyNode
2317
- LLRBNode.EMPTY = new LLRBEmptyNode();
2882
+ }
2318
2883
 
2319
2884
  /**
2320
2885
  * @license
@@ -2332,484 +2897,445 @@ LLRBNode.EMPTY = new LLRBEmptyNode();
2332
2897
  * See the License for the specific language governing permissions and
2333
2898
  * limitations under the License.
2334
2899
  */
2335
- /**
2336
- * SortedSet is an immutable (copy-on-write) collection that holds elements
2337
- * in order specified by the provided comparator.
2338
- *
2339
- * NOTE: if provided comparator returns 0 for two elements, we consider them to
2340
- * be equal!
2341
- */
2342
- class SortedSet {
2343
- constructor(comparator) {
2900
+ // An immutable sorted map implementation, based on a Left-leaning Red-Black
2901
+ // tree.
2902
+ class SortedMap {
2903
+ constructor(comparator, root) {
2344
2904
  this.comparator = comparator;
2345
- this.data = new SortedMap(this.comparator);
2905
+ this.root = root ? root : LLRBNode.EMPTY;
2346
2906
  }
2347
- has(elem) {
2348
- return this.data.get(elem) !== null;
2907
+ // Returns a copy of the map, with the specified key/value added or replaced.
2908
+ insert(key, value) {
2909
+ return new SortedMap(this.comparator, this.root
2910
+ .insert(key, value, this.comparator)
2911
+ .copy(null, null, LLRBNode.BLACK, null, null));
2349
2912
  }
2350
- first() {
2351
- return this.data.minKey();
2913
+ // Returns a copy of the map, with the specified key removed.
2914
+ remove(key) {
2915
+ return new SortedMap(this.comparator, this.root
2916
+ .remove(key, this.comparator)
2917
+ .copy(null, null, LLRBNode.BLACK, null, null));
2352
2918
  }
2353
- last() {
2354
- return this.data.maxKey();
2919
+ // Returns the value of the node with the given key, or null.
2920
+ get(key) {
2921
+ let node = this.root;
2922
+ while (!node.isEmpty()) {
2923
+ const cmp = this.comparator(key, node.key);
2924
+ if (cmp === 0) {
2925
+ return node.value;
2926
+ }
2927
+ else if (cmp < 0) {
2928
+ node = node.left;
2929
+ }
2930
+ else if (cmp > 0) {
2931
+ node = node.right;
2932
+ }
2933
+ }
2934
+ return null;
2935
+ }
2936
+ // Returns the index of the element in this sorted map, or -1 if it doesn't
2937
+ // exist.
2938
+ indexOf(key) {
2939
+ // Number of nodes that were pruned when descending right
2940
+ let prunedNodes = 0;
2941
+ let node = this.root;
2942
+ while (!node.isEmpty()) {
2943
+ const cmp = this.comparator(key, node.key);
2944
+ if (cmp === 0) {
2945
+ return prunedNodes + node.left.size;
2946
+ }
2947
+ else if (cmp < 0) {
2948
+ node = node.left;
2949
+ }
2950
+ else {
2951
+ // Count all nodes left of the node plus the node itself
2952
+ prunedNodes += node.left.size + 1;
2953
+ node = node.right;
2954
+ }
2955
+ }
2956
+ // Node not found
2957
+ return -1;
2958
+ }
2959
+ isEmpty() {
2960
+ return this.root.isEmpty();
2355
2961
  }
2962
+ // Returns the total number of nodes in the map.
2356
2963
  get size() {
2357
- return this.data.size;
2964
+ return this.root.size;
2358
2965
  }
2359
- indexOf(elem) {
2360
- return this.data.indexOf(elem);
2966
+ // Returns the minimum key in the map.
2967
+ minKey() {
2968
+ return this.root.minKey();
2361
2969
  }
2362
- /** Iterates elements in order defined by "comparator" */
2363
- forEach(cb) {
2364
- this.data.inorderTraversal((k, v) => {
2365
- cb(k);
2970
+ // Returns the maximum key in the map.
2971
+ maxKey() {
2972
+ return this.root.maxKey();
2973
+ }
2974
+ // Traverses the map in key order and calls the specified action function
2975
+ // for each key/value pair. If action returns true, traversal is aborted.
2976
+ // Returns the first truthy value returned by action, or the last falsey
2977
+ // value returned by action.
2978
+ inorderTraversal(action) {
2979
+ return this.root.inorderTraversal(action);
2980
+ }
2981
+ forEach(fn) {
2982
+ this.inorderTraversal((k, v) => {
2983
+ fn(k, v);
2366
2984
  return false;
2367
2985
  });
2368
2986
  }
2369
- /** Iterates over `elem`s such that: range[0] &lt;= elem &lt; range[1]. */
2370
- forEachInRange(range, cb) {
2371
- const iter = this.data.getIteratorFrom(range[0]);
2372
- while (iter.hasNext()) {
2373
- const elem = iter.getNext();
2374
- if (this.comparator(elem.key, range[1]) >= 0) {
2375
- return;
2376
- }
2377
- cb(elem.key);
2378
- }
2379
- }
2380
- /**
2381
- * Iterates over `elem`s such that: start &lt;= elem until false is returned.
2382
- */
2383
- forEachWhile(cb, start) {
2384
- let iter;
2385
- if (start !== undefined) {
2386
- iter = this.data.getIteratorFrom(start);
2387
- }
2388
- else {
2389
- iter = this.data.getIterator();
2390
- }
2391
- while (iter.hasNext()) {
2392
- const elem = iter.getNext();
2393
- const result = cb(elem.key);
2394
- if (!result) {
2395
- return;
2396
- }
2397
- }
2987
+ toString() {
2988
+ const descriptions = [];
2989
+ this.inorderTraversal((k, v) => {
2990
+ descriptions.push(`${k}:${v}`);
2991
+ return false;
2992
+ });
2993
+ return `{${descriptions.join(', ')}}`;
2398
2994
  }
2399
- /** Finds the least element greater than or equal to `elem`. */
2400
- firstAfterOrEqual(elem) {
2401
- const iter = this.data.getIteratorFrom(elem);
2402
- return iter.hasNext() ? iter.getNext().key : null;
2995
+ // Traverses the map in reverse key order and calls the specified action
2996
+ // function for each key/value pair. If action returns true, traversal is
2997
+ // aborted.
2998
+ // Returns the first truthy value returned by action, or the last falsey
2999
+ // value returned by action.
3000
+ reverseTraversal(action) {
3001
+ return this.root.reverseTraversal(action);
2403
3002
  }
3003
+ // Returns an iterator over the SortedMap.
2404
3004
  getIterator() {
2405
- return new SortedSetIterator(this.data.getIterator());
3005
+ return new SortedMapIterator(this.root, null, this.comparator, false);
2406
3006
  }
2407
3007
  getIteratorFrom(key) {
2408
- return new SortedSetIterator(this.data.getIteratorFrom(key));
2409
- }
2410
- /** Inserts or updates an element */
2411
- add(elem) {
2412
- return this.copy(this.data.remove(elem).insert(elem, true));
3008
+ return new SortedMapIterator(this.root, key, this.comparator, false);
2413
3009
  }
2414
- /** Deletes an element */
2415
- delete(elem) {
2416
- if (!this.has(elem)) {
2417
- return this;
2418
- }
2419
- return this.copy(this.data.remove(elem));
3010
+ getReverseIterator() {
3011
+ return new SortedMapIterator(this.root, null, this.comparator, true);
2420
3012
  }
2421
- isEmpty() {
2422
- return this.data.isEmpty();
3013
+ getReverseIteratorFrom(key) {
3014
+ return new SortedMapIterator(this.root, key, this.comparator, true);
2423
3015
  }
2424
- unionWith(other) {
2425
- let result = this;
2426
- // Make sure `result` always refers to the larger one of the two sets.
2427
- if (result.size < other.size) {
2428
- result = other;
2429
- other = this;
3016
+ } // end SortedMap
3017
+ // An iterator over an LLRBNode.
3018
+ class SortedMapIterator {
3019
+ constructor(node, startKey, comparator, isReverse) {
3020
+ this.isReverse = isReverse;
3021
+ this.nodeStack = [];
3022
+ let cmp = 1;
3023
+ while (!node.isEmpty()) {
3024
+ cmp = startKey ? comparator(node.key, startKey) : 1;
3025
+ // flip the comparison if we're going in reverse
3026
+ if (startKey && isReverse) {
3027
+ cmp *= -1;
3028
+ }
3029
+ if (cmp < 0) {
3030
+ // This node is less than our start key. ignore it
3031
+ if (this.isReverse) {
3032
+ node = node.left;
3033
+ }
3034
+ else {
3035
+ node = node.right;
3036
+ }
3037
+ }
3038
+ else if (cmp === 0) {
3039
+ // This node is exactly equal to our start key. Push it on the stack,
3040
+ // but stop iterating;
3041
+ this.nodeStack.push(node);
3042
+ break;
3043
+ }
3044
+ else {
3045
+ // This node is greater than our start key, add it to the stack and move
3046
+ // to the next one
3047
+ this.nodeStack.push(node);
3048
+ if (this.isReverse) {
3049
+ node = node.right;
3050
+ }
3051
+ else {
3052
+ node = node.left;
3053
+ }
3054
+ }
2430
3055
  }
2431
- other.forEach(elem => {
2432
- result = result.add(elem);
2433
- });
2434
- return result;
2435
3056
  }
2436
- isEqual(other) {
2437
- if (!(other instanceof SortedSet)) {
2438
- return false;
2439
- }
2440
- if (this.size !== other.size) {
2441
- return false;
3057
+ getNext() {
3058
+ let node = this.nodeStack.pop();
3059
+ const result = { key: node.key, value: node.value };
3060
+ if (this.isReverse) {
3061
+ node = node.left;
3062
+ while (!node.isEmpty()) {
3063
+ this.nodeStack.push(node);
3064
+ node = node.right;
3065
+ }
2442
3066
  }
2443
- const thisIt = this.data.getIterator();
2444
- const otherIt = other.data.getIterator();
2445
- while (thisIt.hasNext()) {
2446
- const thisElem = thisIt.getNext().key;
2447
- const otherElem = otherIt.getNext().key;
2448
- if (this.comparator(thisElem, otherElem) !== 0) {
2449
- return false;
3067
+ else {
3068
+ node = node.right;
3069
+ while (!node.isEmpty()) {
3070
+ this.nodeStack.push(node);
3071
+ node = node.left;
2450
3072
  }
2451
3073
  }
2452
- return true;
2453
- }
2454
- toArray() {
2455
- const res = [];
2456
- this.forEach(targetId => {
2457
- res.push(targetId);
2458
- });
2459
- return res;
3074
+ return result;
2460
3075
  }
2461
- toString() {
2462
- const result = [];
2463
- this.forEach(elem => result.push(elem));
2464
- return 'SortedSet(' + result.toString() + ')';
3076
+ hasNext() {
3077
+ return this.nodeStack.length > 0;
2465
3078
  }
2466
- copy(data) {
2467
- const result = new SortedSet(this.comparator);
2468
- result.data = data;
2469
- return result;
3079
+ peek() {
3080
+ if (this.nodeStack.length === 0) {
3081
+ return null;
3082
+ }
3083
+ const node = this.nodeStack[this.nodeStack.length - 1];
3084
+ return { key: node.key, value: node.value };
2470
3085
  }
2471
- }
2472
- class SortedSetIterator {
2473
- constructor(iter) {
2474
- this.iter = iter;
3086
+ } // end SortedMapIterator
3087
+ // Represents a node in a Left-leaning Red-Black tree.
3088
+ class LLRBNode {
3089
+ constructor(key, value, color, left, right) {
3090
+ this.key = key;
3091
+ this.value = value;
3092
+ this.color = color != null ? color : LLRBNode.RED;
3093
+ this.left = left != null ? left : LLRBNode.EMPTY;
3094
+ this.right = right != null ? right : LLRBNode.EMPTY;
3095
+ this.size = this.left.size + 1 + this.right.size;
2475
3096
  }
2476
- getNext() {
2477
- return this.iter.getNext().key;
3097
+ // Returns a copy of the current node, optionally replacing pieces of it.
3098
+ copy(key, value, color, left, right) {
3099
+ return new LLRBNode(key != null ? key : this.key, value != null ? value : this.value, color != null ? color : this.color, left != null ? left : this.left, right != null ? right : this.right);
2478
3100
  }
2479
- hasNext() {
2480
- return this.iter.hasNext();
3101
+ isEmpty() {
3102
+ return false;
2481
3103
  }
2482
- }
2483
-
2484
- /**
2485
- * @license
2486
- * Copyright 2020 Google LLC
2487
- *
2488
- * Licensed under the Apache License, Version 2.0 (the "License");
2489
- * you may not use this file except in compliance with the License.
2490
- * You may obtain a copy of the License at
2491
- *
2492
- * http://www.apache.org/licenses/LICENSE-2.0
2493
- *
2494
- * Unless required by applicable law or agreed to in writing, software
2495
- * distributed under the License is distributed on an "AS IS" BASIS,
2496
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2497
- * See the License for the specific language governing permissions and
2498
- * limitations under the License.
2499
- */
2500
- /**
2501
- * Provides a set of fields that can be used to partially patch a document.
2502
- * FieldMask is used in conjunction with ObjectValue.
2503
- * Examples:
2504
- * foo - Overwrites foo entirely with the provided value. If foo is not
2505
- * present in the companion ObjectValue, the field is deleted.
2506
- * foo.bar - Overwrites only the field bar of the object foo.
2507
- * If foo is not an object, foo is replaced with an object
2508
- * containing foo
2509
- */
2510
- class FieldMask {
2511
- constructor(fields) {
2512
- this.fields = fields;
2513
- // TODO(dimond): validation of FieldMask
2514
- // Sort the field mask to support `FieldMask.isEqual()` and assert below.
2515
- fields.sort(FieldPath$1.comparator);
3104
+ // Traverses the tree in key order and calls the specified action function
3105
+ // for each node. If action returns true, traversal is aborted.
3106
+ // Returns the first truthy value returned by action, or the last falsey
3107
+ // value returned by action.
3108
+ inorderTraversal(action) {
3109
+ return (this.left.inorderTraversal(action) ||
3110
+ action(this.key, this.value) ||
3111
+ this.right.inorderTraversal(action));
2516
3112
  }
2517
- static empty() {
2518
- return new FieldMask([]);
3113
+ // Traverses the tree in reverse key order and calls the specified action
3114
+ // function for each node. If action returns true, traversal is aborted.
3115
+ // Returns the first truthy value returned by action, or the last falsey
3116
+ // value returned by action.
3117
+ reverseTraversal(action) {
3118
+ return (this.right.reverseTraversal(action) ||
3119
+ action(this.key, this.value) ||
3120
+ this.left.reverseTraversal(action));
2519
3121
  }
2520
- /**
2521
- * Returns a new FieldMask object that is the result of adding all the given
2522
- * fields paths to this field mask.
2523
- */
2524
- unionWith(extraFields) {
2525
- let mergedMaskSet = new SortedSet(FieldPath$1.comparator);
2526
- for (const fieldPath of this.fields) {
2527
- mergedMaskSet = mergedMaskSet.add(fieldPath);
2528
- }
2529
- for (const fieldPath of extraFields) {
2530
- mergedMaskSet = mergedMaskSet.add(fieldPath);
3122
+ // Returns the minimum node in the tree.
3123
+ min() {
3124
+ if (this.left.isEmpty()) {
3125
+ return this;
2531
3126
  }
2532
- return new FieldMask(mergedMaskSet.toArray());
2533
- }
2534
- /**
2535
- * Verifies that `fieldPath` is included by at least one field in this field
2536
- * mask.
2537
- *
2538
- * This is an O(n) operation, where `n` is the size of the field mask.
2539
- */
2540
- covers(fieldPath) {
2541
- for (const fieldMaskPath of this.fields) {
2542
- if (fieldMaskPath.isPrefixOf(fieldPath)) {
2543
- return true;
2544
- }
3127
+ else {
3128
+ return this.left.min();
2545
3129
  }
2546
- return false;
2547
3130
  }
2548
- isEqual(other) {
2549
- return arrayEquals(this.fields, other.fields, (l, r) => l.isEqual(r));
3131
+ // Returns the maximum key in the tree.
3132
+ minKey() {
3133
+ return this.min().key;
2550
3134
  }
2551
- }
2552
-
2553
- /**
2554
- * @license
2555
- * Copyright 2020 Google LLC
2556
- *
2557
- * Licensed under the Apache License, Version 2.0 (the "License");
2558
- * you may not use this file except in compliance with the License.
2559
- * You may obtain a copy of the License at
2560
- *
2561
- * http://www.apache.org/licenses/LICENSE-2.0
2562
- *
2563
- * Unless required by applicable law or agreed to in writing, software
2564
- * distributed under the License is distributed on an "AS IS" BASIS,
2565
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2566
- * See the License for the specific language governing permissions and
2567
- * limitations under the License.
2568
- */
2569
- /** Converts a Base64 encoded string to a binary string. */
2570
- function decodeBase64(encoded) {
2571
- // Note: We used to validate the base64 string here via a regular expression.
2572
- // This was removed to improve the performance of indexing.
2573
- return Buffer.from(encoded, 'base64').toString('binary');
2574
- }
2575
- /** Converts a binary string to a Base64 encoded string. */
2576
- function encodeBase64(raw) {
2577
- return Buffer.from(raw, 'binary').toString('base64');
2578
- }
2579
-
2580
- /**
2581
- * @license
2582
- * Copyright 2020 Google LLC
2583
- *
2584
- * Licensed under the Apache License, Version 2.0 (the "License");
2585
- * you may not use this file except in compliance with the License.
2586
- * You may obtain a copy of the License at
2587
- *
2588
- * http://www.apache.org/licenses/LICENSE-2.0
2589
- *
2590
- * Unless required by applicable law or agreed to in writing, software
2591
- * distributed under the License is distributed on an "AS IS" BASIS,
2592
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2593
- * See the License for the specific language governing permissions and
2594
- * limitations under the License.
2595
- */
2596
- /**
2597
- * Immutable class that represents a "proto" byte string.
2598
- *
2599
- * Proto byte strings can either be Base64-encoded strings or Uint8Arrays when
2600
- * sent on the wire. This class abstracts away this differentiation by holding
2601
- * the proto byte string in a common class that must be converted into a string
2602
- * before being sent as a proto.
2603
- * @internal
2604
- */
2605
- class ByteString {
2606
- constructor(binaryString) {
2607
- this.binaryString = binaryString;
3135
+ // Returns the maximum key in the tree.
3136
+ maxKey() {
3137
+ if (this.right.isEmpty()) {
3138
+ return this.key;
3139
+ }
3140
+ else {
3141
+ return this.right.maxKey();
3142
+ }
2608
3143
  }
2609
- static fromBase64String(base64) {
2610
- const binaryString = decodeBase64(base64);
2611
- return new ByteString(binaryString);
3144
+ // Returns new tree, with the key/value added.
3145
+ insert(key, value, comparator) {
3146
+ let n = this;
3147
+ const cmp = comparator(key, n.key);
3148
+ if (cmp < 0) {
3149
+ n = n.copy(null, null, null, n.left.insert(key, value, comparator), null);
3150
+ }
3151
+ else if (cmp === 0) {
3152
+ n = n.copy(null, value, null, null, null);
3153
+ }
3154
+ else {
3155
+ n = n.copy(null, null, null, null, n.right.insert(key, value, comparator));
3156
+ }
3157
+ return n.fixUp();
2612
3158
  }
2613
- static fromUint8Array(array) {
2614
- // TODO(indexing); Remove the copy of the byte string here as this method
2615
- // is frequently called during indexing.
2616
- const binaryString = binaryStringFromUint8Array(array);
2617
- return new ByteString(binaryString);
3159
+ removeMin() {
3160
+ if (this.left.isEmpty()) {
3161
+ return LLRBNode.EMPTY;
3162
+ }
3163
+ let n = this;
3164
+ if (!n.left.isRed() && !n.left.left.isRed()) {
3165
+ n = n.moveRedLeft();
3166
+ }
3167
+ n = n.copy(null, null, null, n.left.removeMin(), null);
3168
+ return n.fixUp();
2618
3169
  }
2619
- [Symbol.iterator]() {
2620
- let i = 0;
2621
- return {
2622
- next: () => {
2623
- if (i < this.binaryString.length) {
2624
- return { value: this.binaryString.charCodeAt(i++), done: false };
3170
+ // Returns new tree, with the specified item removed.
3171
+ remove(key, comparator) {
3172
+ let smallest;
3173
+ let n = this;
3174
+ if (comparator(key, n.key) < 0) {
3175
+ if (!n.left.isEmpty() && !n.left.isRed() && !n.left.left.isRed()) {
3176
+ n = n.moveRedLeft();
3177
+ }
3178
+ n = n.copy(null, null, null, n.left.remove(key, comparator), null);
3179
+ }
3180
+ else {
3181
+ if (n.left.isRed()) {
3182
+ n = n.rotateRight();
3183
+ }
3184
+ if (!n.right.isEmpty() && !n.right.isRed() && !n.right.left.isRed()) {
3185
+ n = n.moveRedRight();
3186
+ }
3187
+ if (comparator(key, n.key) === 0) {
3188
+ if (n.right.isEmpty()) {
3189
+ return LLRBNode.EMPTY;
2625
3190
  }
2626
3191
  else {
2627
- return { value: undefined, done: true };
3192
+ smallest = n.right.min();
3193
+ n = n.copy(smallest.key, smallest.value, null, null, n.right.removeMin());
2628
3194
  }
2629
3195
  }
2630
- };
3196
+ n = n.copy(null, null, null, null, n.right.remove(key, comparator));
3197
+ }
3198
+ return n.fixUp();
2631
3199
  }
2632
- toBase64() {
2633
- return encodeBase64(this.binaryString);
3200
+ isRed() {
3201
+ return this.color;
2634
3202
  }
2635
- toUint8Array() {
2636
- return uint8ArrayFromBinaryString(this.binaryString);
3203
+ // Returns new tree after performing any needed rotations.
3204
+ fixUp() {
3205
+ let n = this;
3206
+ if (n.right.isRed() && !n.left.isRed()) {
3207
+ n = n.rotateLeft();
3208
+ }
3209
+ if (n.left.isRed() && n.left.left.isRed()) {
3210
+ n = n.rotateRight();
3211
+ }
3212
+ if (n.left.isRed() && n.right.isRed()) {
3213
+ n = n.colorFlip();
3214
+ }
3215
+ return n;
2637
3216
  }
2638
- approximateByteSize() {
2639
- return this.binaryString.length * 2;
3217
+ moveRedLeft() {
3218
+ let n = this.colorFlip();
3219
+ if (n.right.left.isRed()) {
3220
+ n = n.copy(null, null, null, null, n.right.rotateRight());
3221
+ n = n.rotateLeft();
3222
+ n = n.colorFlip();
3223
+ }
3224
+ return n;
2640
3225
  }
2641
- compareTo(other) {
2642
- return primitiveComparator(this.binaryString, other.binaryString);
3226
+ moveRedRight() {
3227
+ let n = this.colorFlip();
3228
+ if (n.left.left.isRed()) {
3229
+ n = n.rotateRight();
3230
+ n = n.colorFlip();
3231
+ }
3232
+ return n;
2643
3233
  }
2644
- isEqual(other) {
2645
- return this.binaryString === other.binaryString;
3234
+ rotateLeft() {
3235
+ const nl = this.copy(null, null, LLRBNode.RED, null, this.right.left);
3236
+ return this.right.copy(null, null, this.color, nl, null);
2646
3237
  }
2647
- }
2648
- ByteString.EMPTY_BYTE_STRING = new ByteString('');
2649
- /**
2650
- * Helper function to convert an Uint8array to a binary string.
2651
- */
2652
- function binaryStringFromUint8Array(array) {
2653
- let binaryString = '';
2654
- for (let i = 0; i < array.length; ++i) {
2655
- binaryString += String.fromCharCode(array[i]);
3238
+ rotateRight() {
3239
+ const nr = this.copy(null, null, LLRBNode.RED, this.left.right, null);
3240
+ return this.left.copy(null, null, this.color, null, nr);
2656
3241
  }
2657
- return binaryString;
2658
- }
2659
- /**
2660
- * Helper function to convert a binary string to an Uint8Array.
2661
- */
2662
- function uint8ArrayFromBinaryString(binaryString) {
2663
- const buffer = new Uint8Array(binaryString.length);
2664
- for (let i = 0; i < binaryString.length; i++) {
2665
- buffer[i] = binaryString.charCodeAt(i);
3242
+ colorFlip() {
3243
+ const left = this.left.copy(null, null, !this.left.color, null, null);
3244
+ const right = this.right.copy(null, null, !this.right.color, null, null);
3245
+ return this.copy(null, null, !this.color, left, right);
2666
3246
  }
2667
- return buffer;
2668
- }
2669
-
2670
- /**
2671
- * @license
2672
- * Copyright 2020 Google LLC
2673
- *
2674
- * Licensed under the Apache License, Version 2.0 (the "License");
2675
- * you may not use this file except in compliance with the License.
2676
- * You may obtain a copy of the License at
2677
- *
2678
- * http://www.apache.org/licenses/LICENSE-2.0
2679
- *
2680
- * Unless required by applicable law or agreed to in writing, software
2681
- * distributed under the License is distributed on an "AS IS" BASIS,
2682
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2683
- * See the License for the specific language governing permissions and
2684
- * limitations under the License.
2685
- */
2686
- // A RegExp matching ISO 8601 UTC timestamps with optional fraction.
2687
- const ISO_TIMESTAMP_REG_EXP = new RegExp(/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.(\d+))?Z$/);
2688
- /**
2689
- * Converts the possible Proto values for a timestamp value into a "seconds and
2690
- * nanos" representation.
2691
- */
2692
- function normalizeTimestamp(date) {
2693
- hardAssert(!!date);
2694
- // The json interface (for the browser) will return an iso timestamp string,
2695
- // while the proto js library (for node) will return a
2696
- // google.protobuf.Timestamp instance.
2697
- if (typeof date === 'string') {
2698
- // The date string can have higher precision (nanos) than the Date class
2699
- // (millis), so we do some custom parsing here.
2700
- // Parse the nanos right out of the string.
2701
- let nanos = 0;
2702
- const fraction = ISO_TIMESTAMP_REG_EXP.exec(date);
2703
- hardAssert(!!fraction);
2704
- if (fraction[1]) {
2705
- // Pad the fraction out to 9 digits (nanos).
2706
- let nanoStr = fraction[1];
2707
- nanoStr = (nanoStr + '000000000').substr(0, 9);
2708
- nanos = Number(nanoStr);
3247
+ // For testing.
3248
+ checkMaxDepth() {
3249
+ const blackDepth = this.check();
3250
+ if (Math.pow(2.0, blackDepth) <= this.size + 1) {
3251
+ return true;
3252
+ }
3253
+ else {
3254
+ return false;
2709
3255
  }
2710
- // Parse the date to get the seconds.
2711
- const parsedDate = new Date(date);
2712
- const seconds = Math.floor(parsedDate.getTime() / 1000);
2713
- return { seconds, nanos };
2714
3256
  }
2715
- else {
2716
- // TODO(b/37282237): Use strings for Proto3 timestamps
2717
- // assert(!this.options.useProto3Json,
2718
- // 'The timestamp instance format requires Proto JS.');
2719
- const seconds = normalizeNumber(date.seconds);
2720
- const nanos = normalizeNumber(date.nanos);
2721
- return { seconds, nanos };
3257
+ // In a balanced RB tree, the black-depth (number of black nodes) from root to
3258
+ // leaves is equal on both sides. This function verifies that or asserts.
3259
+ check() {
3260
+ if (this.isRed() && this.left.isRed()) {
3261
+ throw fail();
3262
+ }
3263
+ if (this.right.isRed()) {
3264
+ throw fail();
3265
+ }
3266
+ const blackDepth = this.left.check();
3267
+ if (blackDepth !== this.right.check()) {
3268
+ throw fail();
3269
+ }
3270
+ else {
3271
+ return blackDepth + (this.isRed() ? 0 : 1);
3272
+ }
2722
3273
  }
2723
- }
2724
- /**
2725
- * Converts the possible Proto types for numbers into a JavaScript number.
2726
- * Returns 0 if the value is not numeric.
2727
- */
2728
- function normalizeNumber(value) {
2729
- // TODO(bjornick): Handle int64 greater than 53 bits.
2730
- if (typeof value === 'number') {
2731
- return value;
3274
+ } // end LLRBNode
3275
+ // Empty node is shared between all LLRB trees.
3276
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3277
+ LLRBNode.EMPTY = null;
3278
+ LLRBNode.RED = true;
3279
+ LLRBNode.BLACK = false;
3280
+ // Represents an empty node (a leaf node in the Red-Black Tree).
3281
+ class LLRBEmptyNode {
3282
+ constructor() {
3283
+ this.size = 0;
2732
3284
  }
2733
- else if (typeof value === 'string') {
2734
- return Number(value);
3285
+ get key() {
3286
+ throw fail();
3287
+ }
3288
+ get value() {
3289
+ throw fail();
3290
+ }
3291
+ get color() {
3292
+ throw fail();
3293
+ }
3294
+ get left() {
3295
+ throw fail();
3296
+ }
3297
+ get right() {
3298
+ throw fail();
3299
+ }
3300
+ // Returns a copy of the current node.
3301
+ copy(key, value, color, left, right) {
3302
+ return this;
3303
+ }
3304
+ // Returns a copy of the tree, with the specified key/value added.
3305
+ insert(key, value, comparator) {
3306
+ return new LLRBNode(key, value);
3307
+ }
3308
+ // Returns a copy of the tree, with the specified key removed.
3309
+ remove(key, comparator) {
3310
+ return this;
3311
+ }
3312
+ isEmpty() {
3313
+ return true;
3314
+ }
3315
+ inorderTraversal(action) {
3316
+ return false;
3317
+ }
3318
+ reverseTraversal(action) {
3319
+ return false;
3320
+ }
3321
+ minKey() {
3322
+ return null;
2735
3323
  }
2736
- else {
2737
- return 0;
3324
+ maxKey() {
3325
+ return null;
2738
3326
  }
2739
- }
2740
- /** Converts the possible Proto types for Blobs into a ByteString. */
2741
- function normalizeByteString(blob) {
2742
- if (typeof blob === 'string') {
2743
- return ByteString.fromBase64String(blob);
3327
+ isRed() {
3328
+ return false;
2744
3329
  }
2745
- else {
2746
- return ByteString.fromUint8Array(blob);
3330
+ // For testing.
3331
+ checkMaxDepth() {
3332
+ return true;
2747
3333
  }
2748
- }
2749
-
2750
- /**
2751
- * @license
2752
- * Copyright 2020 Google LLC
2753
- *
2754
- * Licensed under the Apache License, Version 2.0 (the "License");
2755
- * you may not use this file except in compliance with the License.
2756
- * You may obtain a copy of the License at
2757
- *
2758
- * http://www.apache.org/licenses/LICENSE-2.0
2759
- *
2760
- * Unless required by applicable law or agreed to in writing, software
2761
- * distributed under the License is distributed on an "AS IS" BASIS,
2762
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2763
- * See the License for the specific language governing permissions and
2764
- * limitations under the License.
2765
- */
2766
- /**
2767
- * Represents a locally-applied ServerTimestamp.
2768
- *
2769
- * Server Timestamps are backed by MapValues that contain an internal field
2770
- * `__type__` with a value of `server_timestamp`. The previous value and local
2771
- * write time are stored in its `__previous_value__` and `__local_write_time__`
2772
- * fields respectively.
2773
- *
2774
- * Notes:
2775
- * - ServerTimestampValue instances are created as the result of applying a
2776
- * transform. They can only exist in the local view of a document. Therefore
2777
- * they do not need to be parsed or serialized.
2778
- * - When evaluated locally (e.g. for snapshot.data()), they by default
2779
- * evaluate to `null`. This behavior can be configured by passing custom
2780
- * FieldValueOptions to value().
2781
- * - With respect to other ServerTimestampValues, they sort by their
2782
- * localWriteTime.
2783
- */
2784
- const SERVER_TIMESTAMP_SENTINEL = 'server_timestamp';
2785
- const TYPE_KEY = '__type__';
2786
- const PREVIOUS_VALUE_KEY = '__previous_value__';
2787
- const LOCAL_WRITE_TIME_KEY = '__local_write_time__';
2788
- function isServerTimestamp(value) {
2789
- var _a, _b;
2790
- const type = (_b = (((_a = value === null || value === void 0 ? void 0 : value.mapValue) === null || _a === void 0 ? void 0 : _a.fields) || {})[TYPE_KEY]) === null || _b === void 0 ? void 0 : _b.stringValue;
2791
- return type === SERVER_TIMESTAMP_SENTINEL;
2792
- }
2793
- /**
2794
- * Returns the value of the field before this ServerTimestamp was set.
2795
- *
2796
- * Preserving the previous values allows the user to display the last resoled
2797
- * value until the backend responds with the timestamp.
2798
- */
2799
- function getPreviousValue(value) {
2800
- const previousValue = value.mapValue.fields[PREVIOUS_VALUE_KEY];
2801
- if (isServerTimestamp(previousValue)) {
2802
- return getPreviousValue(previousValue);
3334
+ check() {
3335
+ return 0;
2803
3336
  }
2804
- return previousValue;
2805
- }
2806
- /**
2807
- * Returns the local time at which this timestamp was first set.
2808
- */
2809
- function getLocalWriteTime(value) {
2810
- const localWriteTime = normalizeTimestamp(value.mapValue.fields[LOCAL_WRITE_TIME_KEY].timestampValue);
2811
- return new Timestamp(localWriteTime.seconds, localWriteTime.nanos);
2812
- }
3337
+ } // end LLRBEmptyNode
3338
+ LLRBNode.EMPTY = new LLRBEmptyNode();
2813
3339
 
2814
3340
  /**
2815
3341
  * @license
@@ -2828,381 +3354,221 @@ function getLocalWriteTime(value) {
2828
3354
  * limitations under the License.
2829
3355
  */
2830
3356
  /**
2831
- * Returns whether a variable is either undefined or null.
2832
- */
2833
- function isNullOrUndefined(value) {
2834
- return value === null || value === undefined;
2835
- }
2836
- /** Returns whether the value represents -0. */
2837
- function isNegativeZero(value) {
2838
- // Detect if the value is -0.0. Based on polyfill from
2839
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
2840
- return value === 0 && 1 / value === 1 / -0;
2841
- }
2842
- /**
2843
- * Returns whether a value is an integer and in the safe integer range
2844
- * @param value - The value to test for being an integer and in the safe range
2845
- */
2846
- function isSafeInteger(value) {
2847
- return (typeof value === 'number' &&
2848
- Number.isInteger(value) &&
2849
- !isNegativeZero(value) &&
2850
- value <= Number.MAX_SAFE_INTEGER &&
2851
- value >= Number.MIN_SAFE_INTEGER);
2852
- }
2853
-
2854
- /**
2855
- * @license
2856
- * Copyright 2020 Google LLC
2857
- *
2858
- * Licensed under the Apache License, Version 2.0 (the "License");
2859
- * you may not use this file except in compliance with the License.
2860
- * You may obtain a copy of the License at
2861
- *
2862
- * http://www.apache.org/licenses/LICENSE-2.0
3357
+ * SortedSet is an immutable (copy-on-write) collection that holds elements
3358
+ * in order specified by the provided comparator.
2863
3359
  *
2864
- * Unless required by applicable law or agreed to in writing, software
2865
- * distributed under the License is distributed on an "AS IS" BASIS,
2866
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2867
- * See the License for the specific language governing permissions and
2868
- * limitations under the License.
3360
+ * NOTE: if provided comparator returns 0 for two elements, we consider them to
3361
+ * be equal!
2869
3362
  */
2870
- const MAX_VALUE_TYPE = '__max__';
2871
- const MAX_VALUE = {
2872
- mapValue: {
2873
- fields: {
2874
- '__type__': { stringValue: MAX_VALUE_TYPE }
2875
- }
2876
- }
2877
- };
2878
- /** Extracts the backend's type order for the provided value. */
2879
- function typeOrder(value) {
2880
- if ('nullValue' in value) {
2881
- return 0 /* NullValue */;
2882
- }
2883
- else if ('booleanValue' in value) {
2884
- return 1 /* BooleanValue */;
2885
- }
2886
- else if ('integerValue' in value || 'doubleValue' in value) {
2887
- return 2 /* NumberValue */;
2888
- }
2889
- else if ('timestampValue' in value) {
2890
- return 3 /* TimestampValue */;
2891
- }
2892
- else if ('stringValue' in value) {
2893
- return 5 /* StringValue */;
2894
- }
2895
- else if ('bytesValue' in value) {
2896
- return 6 /* BlobValue */;
2897
- }
2898
- else if ('referenceValue' in value) {
2899
- return 7 /* RefValue */;
2900
- }
2901
- else if ('geoPointValue' in value) {
2902
- return 8 /* GeoPointValue */;
2903
- }
2904
- else if ('arrayValue' in value) {
2905
- return 9 /* ArrayValue */;
2906
- }
2907
- else if ('mapValue' in value) {
2908
- if (isServerTimestamp(value)) {
2909
- return 4 /* ServerTimestampValue */;
2910
- }
2911
- else if (isMaxValue(value)) {
2912
- return 9007199254740991 /* MaxValue */;
2913
- }
2914
- return 10 /* ObjectValue */;
2915
- }
2916
- else {
2917
- return fail();
2918
- }
2919
- }
2920
- /** Tests `left` and `right` for equality based on the backend semantics. */
2921
- function valueEquals(left, right) {
2922
- if (left === right) {
2923
- return true;
2924
- }
2925
- const leftType = typeOrder(left);
2926
- const rightType = typeOrder(right);
2927
- if (leftType !== rightType) {
2928
- return false;
2929
- }
2930
- switch (leftType) {
2931
- case 0 /* NullValue */:
2932
- return true;
2933
- case 1 /* BooleanValue */:
2934
- return left.booleanValue === right.booleanValue;
2935
- case 4 /* ServerTimestampValue */:
2936
- return getLocalWriteTime(left).isEqual(getLocalWriteTime(right));
2937
- case 3 /* TimestampValue */:
2938
- return timestampEquals(left, right);
2939
- case 5 /* StringValue */:
2940
- return left.stringValue === right.stringValue;
2941
- case 6 /* BlobValue */:
2942
- return blobEquals(left, right);
2943
- case 7 /* RefValue */:
2944
- return left.referenceValue === right.referenceValue;
2945
- case 8 /* GeoPointValue */:
2946
- return geoPointEquals(left, right);
2947
- case 2 /* NumberValue */:
2948
- return numberEquals(left, right);
2949
- case 9 /* ArrayValue */:
2950
- return arrayEquals(left.arrayValue.values || [], right.arrayValue.values || [], valueEquals);
2951
- case 10 /* ObjectValue */:
2952
- return objectEquals(left, right);
2953
- case 9007199254740991 /* MaxValue */:
2954
- return true;
2955
- default:
2956
- return fail();
2957
- }
2958
- }
2959
- function timestampEquals(left, right) {
2960
- if (typeof left.timestampValue === 'string' &&
2961
- typeof right.timestampValue === 'string' &&
2962
- left.timestampValue.length === right.timestampValue.length) {
2963
- // Use string equality for ISO 8601 timestamps
2964
- return left.timestampValue === right.timestampValue;
2965
- }
2966
- const leftTimestamp = normalizeTimestamp(left.timestampValue);
2967
- const rightTimestamp = normalizeTimestamp(right.timestampValue);
2968
- return (leftTimestamp.seconds === rightTimestamp.seconds &&
2969
- leftTimestamp.nanos === rightTimestamp.nanos);
2970
- }
2971
- function geoPointEquals(left, right) {
2972
- return (normalizeNumber(left.geoPointValue.latitude) ===
2973
- normalizeNumber(right.geoPointValue.latitude) &&
2974
- normalizeNumber(left.geoPointValue.longitude) ===
2975
- normalizeNumber(right.geoPointValue.longitude));
2976
- }
2977
- function blobEquals(left, right) {
2978
- return normalizeByteString(left.bytesValue).isEqual(normalizeByteString(right.bytesValue));
2979
- }
2980
- function numberEquals(left, right) {
2981
- if ('integerValue' in left && 'integerValue' in right) {
2982
- return (normalizeNumber(left.integerValue) === normalizeNumber(right.integerValue));
2983
- }
2984
- else if ('doubleValue' in left && 'doubleValue' in right) {
2985
- const n1 = normalizeNumber(left.doubleValue);
2986
- const n2 = normalizeNumber(right.doubleValue);
2987
- if (n1 === n2) {
2988
- return isNegativeZero(n1) === isNegativeZero(n2);
2989
- }
2990
- else {
2991
- return isNaN(n1) && isNaN(n2);
2992
- }
2993
- }
2994
- return false;
2995
- }
2996
- function objectEquals(left, right) {
2997
- const leftMap = left.mapValue.fields || {};
2998
- const rightMap = right.mapValue.fields || {};
2999
- if (objectSize(leftMap) !== objectSize(rightMap)) {
3000
- return false;
3363
+ class SortedSet {
3364
+ constructor(comparator) {
3365
+ this.comparator = comparator;
3366
+ this.data = new SortedMap(this.comparator);
3001
3367
  }
3002
- for (const key in leftMap) {
3003
- if (leftMap.hasOwnProperty(key)) {
3004
- if (rightMap[key] === undefined ||
3005
- !valueEquals(leftMap[key], rightMap[key])) {
3006
- return false;
3007
- }
3008
- }
3368
+ has(elem) {
3369
+ return this.data.get(elem) !== null;
3009
3370
  }
3010
- return true;
3011
- }
3012
- /** Returns true if the ArrayValue contains the specified element. */
3013
- function arrayValueContains(haystack, needle) {
3014
- return ((haystack.values || []).find(v => valueEquals(v, needle)) !== undefined);
3015
- }
3016
- function valueCompare(left, right) {
3017
- if (left === right) {
3018
- return 0;
3371
+ first() {
3372
+ return this.data.minKey();
3019
3373
  }
3020
- const leftType = typeOrder(left);
3021
- const rightType = typeOrder(right);
3022
- if (leftType !== rightType) {
3023
- return primitiveComparator(leftType, rightType);
3374
+ last() {
3375
+ return this.data.maxKey();
3024
3376
  }
3025
- switch (leftType) {
3026
- case 0 /* NullValue */:
3027
- case 9007199254740991 /* MaxValue */:
3028
- return 0;
3029
- case 1 /* BooleanValue */:
3030
- return primitiveComparator(left.booleanValue, right.booleanValue);
3031
- case 2 /* NumberValue */:
3032
- return compareNumbers(left, right);
3033
- case 3 /* TimestampValue */:
3034
- return compareTimestamps(left.timestampValue, right.timestampValue);
3035
- case 4 /* ServerTimestampValue */:
3036
- return compareTimestamps(getLocalWriteTime(left), getLocalWriteTime(right));
3037
- case 5 /* StringValue */:
3038
- return primitiveComparator(left.stringValue, right.stringValue);
3039
- case 6 /* BlobValue */:
3040
- return compareBlobs(left.bytesValue, right.bytesValue);
3041
- case 7 /* RefValue */:
3042
- return compareReferences(left.referenceValue, right.referenceValue);
3043
- case 8 /* GeoPointValue */:
3044
- return compareGeoPoints(left.geoPointValue, right.geoPointValue);
3045
- case 9 /* ArrayValue */:
3046
- return compareArrays(left.arrayValue, right.arrayValue);
3047
- case 10 /* ObjectValue */:
3048
- return compareMaps(left.mapValue, right.mapValue);
3049
- default:
3050
- throw fail();
3377
+ get size() {
3378
+ return this.data.size;
3051
3379
  }
3052
- }
3053
- function compareNumbers(left, right) {
3054
- const leftNumber = normalizeNumber(left.integerValue || left.doubleValue);
3055
- const rightNumber = normalizeNumber(right.integerValue || right.doubleValue);
3056
- if (leftNumber < rightNumber) {
3057
- return -1;
3380
+ indexOf(elem) {
3381
+ return this.data.indexOf(elem);
3058
3382
  }
3059
- else if (leftNumber > rightNumber) {
3060
- return 1;
3383
+ /** Iterates elements in order defined by "comparator" */
3384
+ forEach(cb) {
3385
+ this.data.inorderTraversal((k, v) => {
3386
+ cb(k);
3387
+ return false;
3388
+ });
3061
3389
  }
3062
- else if (leftNumber === rightNumber) {
3063
- return 0;
3390
+ /** Iterates over `elem`s such that: range[0] &lt;= elem &lt; range[1]. */
3391
+ forEachInRange(range, cb) {
3392
+ const iter = this.data.getIteratorFrom(range[0]);
3393
+ while (iter.hasNext()) {
3394
+ const elem = iter.getNext();
3395
+ if (this.comparator(elem.key, range[1]) >= 0) {
3396
+ return;
3397
+ }
3398
+ cb(elem.key);
3399
+ }
3064
3400
  }
3065
- else {
3066
- // one or both are NaN.
3067
- if (isNaN(leftNumber)) {
3068
- return isNaN(rightNumber) ? 0 : -1;
3401
+ /**
3402
+ * Iterates over `elem`s such that: start &lt;= elem until false is returned.
3403
+ */
3404
+ forEachWhile(cb, start) {
3405
+ let iter;
3406
+ if (start !== undefined) {
3407
+ iter = this.data.getIteratorFrom(start);
3069
3408
  }
3070
3409
  else {
3071
- return 1;
3410
+ iter = this.data.getIterator();
3411
+ }
3412
+ while (iter.hasNext()) {
3413
+ const elem = iter.getNext();
3414
+ const result = cb(elem.key);
3415
+ if (!result) {
3416
+ return;
3417
+ }
3072
3418
  }
3073
3419
  }
3074
- }
3075
- function compareTimestamps(left, right) {
3076
- if (typeof left === 'string' &&
3077
- typeof right === 'string' &&
3078
- left.length === right.length) {
3079
- return primitiveComparator(left, right);
3420
+ /** Finds the least element greater than or equal to `elem`. */
3421
+ firstAfterOrEqual(elem) {
3422
+ const iter = this.data.getIteratorFrom(elem);
3423
+ return iter.hasNext() ? iter.getNext().key : null;
3080
3424
  }
3081
- const leftTimestamp = normalizeTimestamp(left);
3082
- const rightTimestamp = normalizeTimestamp(right);
3083
- const comparison = primitiveComparator(leftTimestamp.seconds, rightTimestamp.seconds);
3084
- if (comparison !== 0) {
3085
- return comparison;
3425
+ getIterator() {
3426
+ return new SortedSetIterator(this.data.getIterator());
3086
3427
  }
3087
- return primitiveComparator(leftTimestamp.nanos, rightTimestamp.nanos);
3088
- }
3089
- function compareReferences(leftPath, rightPath) {
3090
- const leftSegments = leftPath.split('/');
3091
- const rightSegments = rightPath.split('/');
3092
- for (let i = 0; i < leftSegments.length && i < rightSegments.length; i++) {
3093
- const comparison = primitiveComparator(leftSegments[i], rightSegments[i]);
3094
- if (comparison !== 0) {
3095
- return comparison;
3096
- }
3428
+ getIteratorFrom(key) {
3429
+ return new SortedSetIterator(this.data.getIteratorFrom(key));
3097
3430
  }
3098
- return primitiveComparator(leftSegments.length, rightSegments.length);
3099
- }
3100
- function compareGeoPoints(left, right) {
3101
- const comparison = primitiveComparator(normalizeNumber(left.latitude), normalizeNumber(right.latitude));
3102
- if (comparison !== 0) {
3103
- return comparison;
3431
+ /** Inserts or updates an element */
3432
+ add(elem) {
3433
+ return this.copy(this.data.remove(elem).insert(elem, true));
3104
3434
  }
3105
- return primitiveComparator(normalizeNumber(left.longitude), normalizeNumber(right.longitude));
3106
- }
3107
- function compareBlobs(left, right) {
3108
- const leftBytes = normalizeByteString(left);
3109
- const rightBytes = normalizeByteString(right);
3110
- return leftBytes.compareTo(rightBytes);
3111
- }
3112
- function compareArrays(left, right) {
3113
- const leftArray = left.values || [];
3114
- const rightArray = right.values || [];
3115
- for (let i = 0; i < leftArray.length && i < rightArray.length; ++i) {
3116
- const compare = valueCompare(leftArray[i], rightArray[i]);
3117
- if (compare) {
3118
- return compare;
3435
+ /** Deletes an element */
3436
+ delete(elem) {
3437
+ if (!this.has(elem)) {
3438
+ return this;
3119
3439
  }
3440
+ return this.copy(this.data.remove(elem));
3120
3441
  }
3121
- return primitiveComparator(leftArray.length, rightArray.length);
3122
- }
3123
- function compareMaps(left, right) {
3124
- if (left === MAX_VALUE.mapValue && right === MAX_VALUE.mapValue) {
3125
- return 0;
3126
- }
3127
- else if (left === MAX_VALUE.mapValue) {
3128
- return 1;
3442
+ isEmpty() {
3443
+ return this.data.isEmpty();
3129
3444
  }
3130
- else if (right === MAX_VALUE.mapValue) {
3131
- return -1;
3445
+ unionWith(other) {
3446
+ let result = this;
3447
+ // Make sure `result` always refers to the larger one of the two sets.
3448
+ if (result.size < other.size) {
3449
+ result = other;
3450
+ other = this;
3451
+ }
3452
+ other.forEach(elem => {
3453
+ result = result.add(elem);
3454
+ });
3455
+ return result;
3132
3456
  }
3133
- const leftMap = left.fields || {};
3134
- const leftKeys = Object.keys(leftMap);
3135
- const rightMap = right.fields || {};
3136
- const rightKeys = Object.keys(rightMap);
3137
- // Even though MapValues are likely sorted correctly based on their insertion
3138
- // order (e.g. when received from the backend), local modifications can bring
3139
- // elements out of order. We need to re-sort the elements to ensure that
3140
- // canonical IDs are independent of insertion order.
3141
- leftKeys.sort();
3142
- rightKeys.sort();
3143
- for (let i = 0; i < leftKeys.length && i < rightKeys.length; ++i) {
3144
- const keyCompare = primitiveComparator(leftKeys[i], rightKeys[i]);
3145
- if (keyCompare !== 0) {
3146
- return keyCompare;
3457
+ isEqual(other) {
3458
+ if (!(other instanceof SortedSet)) {
3459
+ return false;
3147
3460
  }
3148
- const compare = valueCompare(leftMap[leftKeys[i]], rightMap[rightKeys[i]]);
3149
- if (compare !== 0) {
3150
- return compare;
3461
+ if (this.size !== other.size) {
3462
+ return false;
3463
+ }
3464
+ const thisIt = this.data.getIterator();
3465
+ const otherIt = other.data.getIterator();
3466
+ while (thisIt.hasNext()) {
3467
+ const thisElem = thisIt.getNext().key;
3468
+ const otherElem = otherIt.getNext().key;
3469
+ if (this.comparator(thisElem, otherElem) !== 0) {
3470
+ return false;
3471
+ }
3151
3472
  }
3473
+ return true;
3474
+ }
3475
+ toArray() {
3476
+ const res = [];
3477
+ this.forEach(targetId => {
3478
+ res.push(targetId);
3479
+ });
3480
+ return res;
3481
+ }
3482
+ toString() {
3483
+ const result = [];
3484
+ this.forEach(elem => result.push(elem));
3485
+ return 'SortedSet(' + result.toString() + ')';
3486
+ }
3487
+ copy(data) {
3488
+ const result = new SortedSet(this.comparator);
3489
+ result.data = data;
3490
+ return result;
3152
3491
  }
3153
- return primitiveComparator(leftKeys.length, rightKeys.length);
3154
- }
3155
- /** Returns a reference value for the provided database and key. */
3156
- function refValue(databaseId, key) {
3157
- return {
3158
- referenceValue: `projects/${databaseId.projectId}/databases/${databaseId.database}/documents/${key.path.canonicalString()}`
3159
- };
3160
- }
3161
- /** Returns true if `value` is an ArrayValue. */
3162
- function isArray(value) {
3163
- return !!value && 'arrayValue' in value;
3164
- }
3165
- /** Returns true if `value` is a NullValue. */
3166
- function isNullValue(value) {
3167
- return !!value && 'nullValue' in value;
3168
- }
3169
- /** Returns true if `value` is NaN. */
3170
- function isNanValue(value) {
3171
- return !!value && 'doubleValue' in value && isNaN(Number(value.doubleValue));
3172
- }
3173
- /** Returns true if `value` is a MapValue. */
3174
- function isMapValue(value) {
3175
- return !!value && 'mapValue' in value;
3176
3492
  }
3177
- /** Creates a deep copy of `source`. */
3178
- function deepClone(source) {
3179
- if (source.geoPointValue) {
3180
- return { geoPointValue: Object.assign({}, source.geoPointValue) };
3493
+ class SortedSetIterator {
3494
+ constructor(iter) {
3495
+ this.iter = iter;
3496
+ }
3497
+ getNext() {
3498
+ return this.iter.getNext().key;
3499
+ }
3500
+ hasNext() {
3501
+ return this.iter.hasNext();
3502
+ }
3503
+ }
3504
+
3505
+ /**
3506
+ * @license
3507
+ * Copyright 2020 Google LLC
3508
+ *
3509
+ * Licensed under the Apache License, Version 2.0 (the "License");
3510
+ * you may not use this file except in compliance with the License.
3511
+ * You may obtain a copy of the License at
3512
+ *
3513
+ * http://www.apache.org/licenses/LICENSE-2.0
3514
+ *
3515
+ * Unless required by applicable law or agreed to in writing, software
3516
+ * distributed under the License is distributed on an "AS IS" BASIS,
3517
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3518
+ * See the License for the specific language governing permissions and
3519
+ * limitations under the License.
3520
+ */
3521
+ /**
3522
+ * Provides a set of fields that can be used to partially patch a document.
3523
+ * FieldMask is used in conjunction with ObjectValue.
3524
+ * Examples:
3525
+ * foo - Overwrites foo entirely with the provided value. If foo is not
3526
+ * present in the companion ObjectValue, the field is deleted.
3527
+ * foo.bar - Overwrites only the field bar of the object foo.
3528
+ * If foo is not an object, foo is replaced with an object
3529
+ * containing foo
3530
+ */
3531
+ class FieldMask {
3532
+ constructor(fields) {
3533
+ this.fields = fields;
3534
+ // TODO(dimond): validation of FieldMask
3535
+ // Sort the field mask to support `FieldMask.isEqual()` and assert below.
3536
+ fields.sort(FieldPath$1.comparator);
3181
3537
  }
3182
- else if (source.timestampValue &&
3183
- typeof source.timestampValue === 'object') {
3184
- return { timestampValue: Object.assign({}, source.timestampValue) };
3538
+ static empty() {
3539
+ return new FieldMask([]);
3185
3540
  }
3186
- else if (source.mapValue) {
3187
- const target = { mapValue: { fields: {} } };
3188
- forEach(source.mapValue.fields, (key, val) => (target.mapValue.fields[key] = deepClone(val)));
3189
- return target;
3541
+ /**
3542
+ * Returns a new FieldMask object that is the result of adding all the given
3543
+ * fields paths to this field mask.
3544
+ */
3545
+ unionWith(extraFields) {
3546
+ let mergedMaskSet = new SortedSet(FieldPath$1.comparator);
3547
+ for (const fieldPath of this.fields) {
3548
+ mergedMaskSet = mergedMaskSet.add(fieldPath);
3549
+ }
3550
+ for (const fieldPath of extraFields) {
3551
+ mergedMaskSet = mergedMaskSet.add(fieldPath);
3552
+ }
3553
+ return new FieldMask(mergedMaskSet.toArray());
3190
3554
  }
3191
- else if (source.arrayValue) {
3192
- const target = { arrayValue: { values: [] } };
3193
- for (let i = 0; i < (source.arrayValue.values || []).length; ++i) {
3194
- target.arrayValue.values[i] = deepClone(source.arrayValue.values[i]);
3555
+ /**
3556
+ * Verifies that `fieldPath` is included by at least one field in this field
3557
+ * mask.
3558
+ *
3559
+ * This is an O(n) operation, where `n` is the size of the field mask.
3560
+ */
3561
+ covers(fieldPath) {
3562
+ for (const fieldMaskPath of this.fields) {
3563
+ if (fieldMaskPath.isPrefixOf(fieldPath)) {
3564
+ return true;
3565
+ }
3195
3566
  }
3196
- return target;
3567
+ return false;
3197
3568
  }
3198
- else {
3199
- return Object.assign({}, source);
3569
+ isEqual(other) {
3570
+ return arrayEquals(this.fields, other.fields, (l, r) => l.isEqual(r));
3200
3571
  }
3201
- }
3202
- /** Returns true if the Value represents the canonical {@link #MAX_VALUE} . */
3203
- function isMaxValue(value) {
3204
- return ((((value.mapValue || {}).fields || {})['__type__'] || {}).stringValue ===
3205
- MAX_VALUE_TYPE);
3206
3572
  }
3207
3573
 
3208
3574
  /**
@@ -3368,11 +3734,12 @@ class ObjectValue {
3368
3734
  * from all views.
3369
3735
  */
3370
3736
  class MutableDocument {
3371
- constructor(key, documentType, version, readTime, data, documentState) {
3737
+ constructor(key, documentType, version, readTime, createTime, data, documentState) {
3372
3738
  this.key = key;
3373
3739
  this.documentType = documentType;
3374
3740
  this.version = version;
3375
3741
  this.readTime = readTime;
3742
+ this.createTime = createTime;
3376
3743
  this.data = data;
3377
3744
  this.documentState = documentState;
3378
3745
  }
@@ -3381,18 +3748,27 @@ class MutableDocument {
3381
3748
  * base document for mutations.
3382
3749
  */
3383
3750
  static newInvalidDocument(documentKey) {
3384
- return new MutableDocument(documentKey, 0 /* INVALID */, SnapshotVersion.min(), SnapshotVersion.min(), ObjectValue.empty(), 0 /* SYNCED */);
3751
+ return new MutableDocument(documentKey, 0 /* DocumentType.INVALID */,
3752
+ /* version */ SnapshotVersion.min(),
3753
+ /* readTime */ SnapshotVersion.min(),
3754
+ /* createTime */ SnapshotVersion.min(), ObjectValue.empty(), 0 /* DocumentState.SYNCED */);
3385
3755
  }
3386
3756
  /**
3387
3757
  * Creates a new document that is known to exist with the given data at the
3388
3758
  * given version.
3389
3759
  */
3390
- static newFoundDocument(documentKey, version, value) {
3391
- return new MutableDocument(documentKey, 1 /* FOUND_DOCUMENT */, version, SnapshotVersion.min(), value, 0 /* SYNCED */);
3760
+ static newFoundDocument(documentKey, version, createTime, value) {
3761
+ return new MutableDocument(documentKey, 1 /* DocumentType.FOUND_DOCUMENT */,
3762
+ /* version */ version,
3763
+ /* readTime */ SnapshotVersion.min(),
3764
+ /* createTime */ createTime, value, 0 /* DocumentState.SYNCED */);
3392
3765
  }
3393
3766
  /** Creates a new document that is known to not exist at the given version. */
3394
3767
  static newNoDocument(documentKey, version) {
3395
- return new MutableDocument(documentKey, 2 /* NO_DOCUMENT */, version, SnapshotVersion.min(), ObjectValue.empty(), 0 /* SYNCED */);
3768
+ return new MutableDocument(documentKey, 2 /* DocumentType.NO_DOCUMENT */,
3769
+ /* version */ version,
3770
+ /* readTime */ SnapshotVersion.min(),
3771
+ /* createTime */ SnapshotVersion.min(), ObjectValue.empty(), 0 /* DocumentState.SYNCED */);
3396
3772
  }
3397
3773
  /**
3398
3774
  * Creates a new document that is known to exist at the given version but
@@ -3400,17 +3776,30 @@ class MutableDocument {
3400
3776
  * base document).
3401
3777
  */
3402
3778
  static newUnknownDocument(documentKey, version) {
3403
- return new MutableDocument(documentKey, 3 /* UNKNOWN_DOCUMENT */, version, SnapshotVersion.min(), ObjectValue.empty(), 2 /* HAS_COMMITTED_MUTATIONS */);
3779
+ return new MutableDocument(documentKey, 3 /* DocumentType.UNKNOWN_DOCUMENT */,
3780
+ /* version */ version,
3781
+ /* readTime */ SnapshotVersion.min(),
3782
+ /* createTime */ SnapshotVersion.min(), ObjectValue.empty(), 2 /* DocumentState.HAS_COMMITTED_MUTATIONS */);
3404
3783
  }
3405
3784
  /**
3406
3785
  * Changes the document type to indicate that it exists and that its version
3407
3786
  * and data are known.
3408
3787
  */
3409
3788
  convertToFoundDocument(version, value) {
3789
+ // If a document is switching state from being an invalid or deleted
3790
+ // document to a valid (FOUND_DOCUMENT) document, either due to receiving an
3791
+ // update from Watch or due to applying a local set mutation on top
3792
+ // of a deleted document, our best guess about its createTime would be the
3793
+ // version at which the document transitioned to a FOUND_DOCUMENT.
3794
+ if (this.createTime.isEqual(SnapshotVersion.min()) &&
3795
+ (this.documentType === 2 /* DocumentType.NO_DOCUMENT */ ||
3796
+ this.documentType === 0 /* DocumentType.INVALID */)) {
3797
+ this.createTime = version;
3798
+ }
3410
3799
  this.version = version;
3411
- this.documentType = 1 /* FOUND_DOCUMENT */;
3800
+ this.documentType = 1 /* DocumentType.FOUND_DOCUMENT */;
3412
3801
  this.data = value;
3413
- this.documentState = 0 /* SYNCED */;
3802
+ this.documentState = 0 /* DocumentState.SYNCED */;
3414
3803
  return this;
3415
3804
  }
3416
3805
  /**
@@ -3419,9 +3808,9 @@ class MutableDocument {
3419
3808
  */
3420
3809
  convertToNoDocument(version) {
3421
3810
  this.version = version;
3422
- this.documentType = 2 /* NO_DOCUMENT */;
3811
+ this.documentType = 2 /* DocumentType.NO_DOCUMENT */;
3423
3812
  this.data = ObjectValue.empty();
3424
- this.documentState = 0 /* SYNCED */;
3813
+ this.documentState = 0 /* DocumentState.SYNCED */;
3425
3814
  return this;
3426
3815
  }
3427
3816
  /**
@@ -3431,17 +3820,17 @@ class MutableDocument {
3431
3820
  */
3432
3821
  convertToUnknownDocument(version) {
3433
3822
  this.version = version;
3434
- this.documentType = 3 /* UNKNOWN_DOCUMENT */;
3823
+ this.documentType = 3 /* DocumentType.UNKNOWN_DOCUMENT */;
3435
3824
  this.data = ObjectValue.empty();
3436
- this.documentState = 2 /* HAS_COMMITTED_MUTATIONS */;
3825
+ this.documentState = 2 /* DocumentState.HAS_COMMITTED_MUTATIONS */;
3437
3826
  return this;
3438
3827
  }
3439
3828
  setHasCommittedMutations() {
3440
- this.documentState = 2 /* HAS_COMMITTED_MUTATIONS */;
3829
+ this.documentState = 2 /* DocumentState.HAS_COMMITTED_MUTATIONS */;
3441
3830
  return this;
3442
3831
  }
3443
3832
  setHasLocalMutations() {
3444
- this.documentState = 1 /* HAS_LOCAL_MUTATIONS */;
3833
+ this.documentState = 1 /* DocumentState.HAS_LOCAL_MUTATIONS */;
3445
3834
  this.version = SnapshotVersion.min();
3446
3835
  return this;
3447
3836
  }
@@ -3450,337 +3839,115 @@ class MutableDocument {
3450
3839
  return this;
3451
3840
  }
3452
3841
  get hasLocalMutations() {
3453
- return this.documentState === 1 /* HAS_LOCAL_MUTATIONS */;
3454
- }
3455
- get hasCommittedMutations() {
3456
- return this.documentState === 2 /* HAS_COMMITTED_MUTATIONS */;
3457
- }
3458
- get hasPendingWrites() {
3459
- return this.hasLocalMutations || this.hasCommittedMutations;
3460
- }
3461
- isValidDocument() {
3462
- return this.documentType !== 0 /* INVALID */;
3463
- }
3464
- isFoundDocument() {
3465
- return this.documentType === 1 /* FOUND_DOCUMENT */;
3466
- }
3467
- isNoDocument() {
3468
- return this.documentType === 2 /* NO_DOCUMENT */;
3469
- }
3470
- isUnknownDocument() {
3471
- return this.documentType === 3 /* UNKNOWN_DOCUMENT */;
3472
- }
3473
- isEqual(other) {
3474
- return (other instanceof MutableDocument &&
3475
- this.key.isEqual(other.key) &&
3476
- this.version.isEqual(other.version) &&
3477
- this.documentType === other.documentType &&
3478
- this.documentState === other.documentState &&
3479
- this.data.isEqual(other.data));
3480
- }
3481
- mutableCopy() {
3482
- return new MutableDocument(this.key, this.documentType, this.version, this.readTime, this.data.clone(), this.documentState);
3483
- }
3484
- toString() {
3485
- return (`Document(${this.key}, ${this.version}, ${JSON.stringify(this.data.value)}, ` +
3486
- `{documentType: ${this.documentType}}), ` +
3487
- `{documentState: ${this.documentState}})`);
3488
- }
3489
- }
3490
-
3491
- /**
3492
- * @license
3493
- * Copyright 2019 Google LLC
3494
- *
3495
- * Licensed under the Apache License, Version 2.0 (the "License");
3496
- * you may not use this file except in compliance with the License.
3497
- * You may obtain a copy of the License at
3498
- *
3499
- * http://www.apache.org/licenses/LICENSE-2.0
3500
- *
3501
- * Unless required by applicable law or agreed to in writing, software
3502
- * distributed under the License is distributed on an "AS IS" BASIS,
3503
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3504
- * See the License for the specific language governing permissions and
3505
- * limitations under the License.
3506
- */
3507
- // Visible for testing
3508
- class TargetImpl {
3509
- constructor(path, collectionGroup = null, orderBy = [], filters = [], limit = null, startAt = null, endAt = null) {
3510
- this.path = path;
3511
- this.collectionGroup = collectionGroup;
3512
- this.orderBy = orderBy;
3513
- this.filters = filters;
3514
- this.limit = limit;
3515
- this.startAt = startAt;
3516
- this.endAt = endAt;
3517
- this.memoizedCanonicalId = null;
3518
- }
3519
- }
3520
- /**
3521
- * Initializes a Target with a path and optional additional query constraints.
3522
- * Path must currently be empty if this is a collection group query.
3523
- *
3524
- * NOTE: you should always construct `Target` from `Query.toTarget` instead of
3525
- * using this factory method, because `Query` provides an implicit `orderBy`
3526
- * property.
3527
- */
3528
- function newTarget(path, collectionGroup = null, orderBy = [], filters = [], limit = null, startAt = null, endAt = null) {
3529
- return new TargetImpl(path, collectionGroup, orderBy, filters, limit, startAt, endAt);
3530
- }
3531
- function targetEquals(left, right) {
3532
- if (left.limit !== right.limit) {
3533
- return false;
3534
- }
3535
- if (left.orderBy.length !== right.orderBy.length) {
3536
- return false;
3537
- }
3538
- for (let i = 0; i < left.orderBy.length; i++) {
3539
- if (!orderByEquals(left.orderBy[i], right.orderBy[i])) {
3540
- return false;
3541
- }
3542
- }
3543
- if (left.filters.length !== right.filters.length) {
3544
- return false;
3545
- }
3546
- for (let i = 0; i < left.filters.length; i++) {
3547
- if (!filterEquals(left.filters[i], right.filters[i])) {
3548
- return false;
3549
- }
3550
- }
3551
- if (left.collectionGroup !== right.collectionGroup) {
3552
- return false;
3553
- }
3554
- if (!left.path.isEqual(right.path)) {
3555
- return false;
3556
- }
3557
- if (!boundEquals(left.startAt, right.startAt)) {
3558
- return false;
3559
- }
3560
- return boundEquals(left.endAt, right.endAt);
3561
- }
3562
- class Filter {
3563
- }
3564
- class FieldFilter extends Filter {
3565
- constructor(field, op, value) {
3566
- super();
3567
- this.field = field;
3568
- this.op = op;
3569
- this.value = value;
3570
- }
3571
- /**
3572
- * Creates a filter based on the provided arguments.
3573
- */
3574
- static create(field, op, value) {
3575
- if (field.isKeyField()) {
3576
- if (op === "in" /* IN */ || op === "not-in" /* NOT_IN */) {
3577
- return this.createKeyFieldInFilter(field, op, value);
3578
- }
3579
- else {
3580
- return new KeyFieldFilter(field, op, value);
3581
- }
3582
- }
3583
- else if (op === "array-contains" /* ARRAY_CONTAINS */) {
3584
- return new ArrayContainsFilter(field, value);
3585
- }
3586
- else if (op === "in" /* IN */) {
3587
- return new InFilter(field, value);
3588
- }
3589
- else if (op === "not-in" /* NOT_IN */) {
3590
- return new NotInFilter(field, value);
3591
- }
3592
- else if (op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
3593
- return new ArrayContainsAnyFilter(field, value);
3594
- }
3595
- else {
3596
- return new FieldFilter(field, op, value);
3597
- }
3598
- }
3599
- static createKeyFieldInFilter(field, op, value) {
3600
- return op === "in" /* IN */
3601
- ? new KeyFieldInFilter(field, value)
3602
- : new KeyFieldNotInFilter(field, value);
3603
- }
3604
- matches(doc) {
3605
- const other = doc.data.field(this.field);
3606
- // Types do not have to match in NOT_EQUAL filters.
3607
- if (this.op === "!=" /* NOT_EQUAL */) {
3608
- return (other !== null &&
3609
- this.matchesComparison(valueCompare(other, this.value)));
3610
- }
3611
- // Only compare types with matching backend order (such as double and int).
3612
- return (other !== null &&
3613
- typeOrder(this.value) === typeOrder(other) &&
3614
- this.matchesComparison(valueCompare(other, this.value)));
3615
- }
3616
- matchesComparison(comparison) {
3617
- switch (this.op) {
3618
- case "<" /* LESS_THAN */:
3619
- return comparison < 0;
3620
- case "<=" /* LESS_THAN_OR_EQUAL */:
3621
- return comparison <= 0;
3622
- case "==" /* EQUAL */:
3623
- return comparison === 0;
3624
- case "!=" /* NOT_EQUAL */:
3625
- return comparison !== 0;
3626
- case ">" /* GREATER_THAN */:
3627
- return comparison > 0;
3628
- case ">=" /* GREATER_THAN_OR_EQUAL */:
3629
- return comparison >= 0;
3630
- default:
3631
- return fail();
3632
- }
3633
- }
3634
- isInequality() {
3635
- return ([
3636
- "<" /* LESS_THAN */,
3637
- "<=" /* LESS_THAN_OR_EQUAL */,
3638
- ">" /* GREATER_THAN */,
3639
- ">=" /* GREATER_THAN_OR_EQUAL */,
3640
- "!=" /* NOT_EQUAL */,
3641
- "not-in" /* NOT_IN */
3642
- ].indexOf(this.op) >= 0);
3643
- }
3644
- }
3645
- function filterEquals(f1, f2) {
3646
- return (f1.op === f2.op &&
3647
- f1.field.isEqual(f2.field) &&
3648
- valueEquals(f1.value, f2.value));
3649
- }
3650
- /** Filter that matches on key fields (i.e. '__name__'). */
3651
- class KeyFieldFilter extends FieldFilter {
3652
- constructor(field, op, value) {
3653
- super(field, op, value);
3654
- this.key = DocumentKey.fromName(value.referenceValue);
3655
- }
3656
- matches(doc) {
3657
- const comparison = DocumentKey.comparator(doc.key, this.key);
3658
- return this.matchesComparison(comparison);
3659
- }
3660
- }
3661
- /** Filter that matches on key fields within an array. */
3662
- class KeyFieldInFilter extends FieldFilter {
3663
- constructor(field, value) {
3664
- super(field, "in" /* IN */, value);
3665
- this.keys = extractDocumentKeysFromArrayValue("in" /* IN */, value);
3666
- }
3667
- matches(doc) {
3668
- return this.keys.some(key => key.isEqual(doc.key));
3669
- }
3670
- }
3671
- /** Filter that matches on key fields not present within an array. */
3672
- class KeyFieldNotInFilter extends FieldFilter {
3673
- constructor(field, value) {
3674
- super(field, "not-in" /* NOT_IN */, value);
3675
- this.keys = extractDocumentKeysFromArrayValue("not-in" /* NOT_IN */, value);
3842
+ return this.documentState === 1 /* DocumentState.HAS_LOCAL_MUTATIONS */;
3676
3843
  }
3677
- matches(doc) {
3678
- return !this.keys.some(key => key.isEqual(doc.key));
3844
+ get hasCommittedMutations() {
3845
+ return this.documentState === 2 /* DocumentState.HAS_COMMITTED_MUTATIONS */;
3679
3846
  }
3680
- }
3681
- function extractDocumentKeysFromArrayValue(op, value) {
3682
- var _a;
3683
- return (((_a = value.arrayValue) === null || _a === void 0 ? void 0 : _a.values) || []).map(v => {
3684
- return DocumentKey.fromName(v.referenceValue);
3685
- });
3686
- }
3687
- /** A Filter that implements the array-contains operator. */
3688
- class ArrayContainsFilter extends FieldFilter {
3689
- constructor(field, value) {
3690
- super(field, "array-contains" /* ARRAY_CONTAINS */, value);
3847
+ get hasPendingWrites() {
3848
+ return this.hasLocalMutations || this.hasCommittedMutations;
3691
3849
  }
3692
- matches(doc) {
3693
- const other = doc.data.field(this.field);
3694
- return isArray(other) && arrayValueContains(other.arrayValue, this.value);
3850
+ isValidDocument() {
3851
+ return this.documentType !== 0 /* DocumentType.INVALID */;
3695
3852
  }
3696
- }
3697
- /** A Filter that implements the IN operator. */
3698
- class InFilter extends FieldFilter {
3699
- constructor(field, value) {
3700
- super(field, "in" /* IN */, value);
3853
+ isFoundDocument() {
3854
+ return this.documentType === 1 /* DocumentType.FOUND_DOCUMENT */;
3701
3855
  }
3702
- matches(doc) {
3703
- const other = doc.data.field(this.field);
3704
- return other !== null && arrayValueContains(this.value.arrayValue, other);
3856
+ isNoDocument() {
3857
+ return this.documentType === 2 /* DocumentType.NO_DOCUMENT */;
3705
3858
  }
3706
- }
3707
- /** A Filter that implements the not-in operator. */
3708
- class NotInFilter extends FieldFilter {
3709
- constructor(field, value) {
3710
- super(field, "not-in" /* NOT_IN */, value);
3859
+ isUnknownDocument() {
3860
+ return this.documentType === 3 /* DocumentType.UNKNOWN_DOCUMENT */;
3711
3861
  }
3712
- matches(doc) {
3713
- if (arrayValueContains(this.value.arrayValue, { nullValue: 'NULL_VALUE' })) {
3714
- return false;
3715
- }
3716
- const other = doc.data.field(this.field);
3717
- return other !== null && !arrayValueContains(this.value.arrayValue, other);
3862
+ isEqual(other) {
3863
+ return (other instanceof MutableDocument &&
3864
+ this.key.isEqual(other.key) &&
3865
+ this.version.isEqual(other.version) &&
3866
+ this.documentType === other.documentType &&
3867
+ this.documentState === other.documentState &&
3868
+ this.data.isEqual(other.data));
3718
3869
  }
3719
- }
3720
- /** A Filter that implements the array-contains-any operator. */
3721
- class ArrayContainsAnyFilter extends FieldFilter {
3722
- constructor(field, value) {
3723
- super(field, "array-contains-any" /* ARRAY_CONTAINS_ANY */, value);
3870
+ mutableCopy() {
3871
+ return new MutableDocument(this.key, this.documentType, this.version, this.readTime, this.createTime, this.data.clone(), this.documentState);
3724
3872
  }
3725
- matches(doc) {
3726
- const other = doc.data.field(this.field);
3727
- if (!isArray(other) || !other.arrayValue.values) {
3728
- return false;
3729
- }
3730
- return other.arrayValue.values.some(val => arrayValueContains(this.value.arrayValue, val));
3873
+ toString() {
3874
+ return (`Document(${this.key}, ${this.version}, ${JSON.stringify(this.data.value)}, ` +
3875
+ `{createTime: ${this.createTime}}), ` +
3876
+ `{documentType: ${this.documentType}}), ` +
3877
+ `{documentState: ${this.documentState}})`);
3731
3878
  }
3732
- }
3879
+ }
3880
+
3733
3881
  /**
3734
- * Represents a bound of a query.
3882
+ * @license
3883
+ * Copyright 2019 Google LLC
3735
3884
  *
3736
- * The bound is specified with the given components representing a position and
3737
- * whether it's just before or just after the position (relative to whatever the
3738
- * query order is).
3885
+ * Licensed under the Apache License, Version 2.0 (the "License");
3886
+ * you may not use this file except in compliance with the License.
3887
+ * You may obtain a copy of the License at
3739
3888
  *
3740
- * The position represents a logical index position for a query. It's a prefix
3741
- * of values for the (potentially implicit) order by clauses of a query.
3889
+ * http://www.apache.org/licenses/LICENSE-2.0
3742
3890
  *
3743
- * Bound provides a function to determine whether a document comes before or
3744
- * after a bound. This is influenced by whether the position is just before or
3745
- * just after the provided values.
3891
+ * Unless required by applicable law or agreed to in writing, software
3892
+ * distributed under the License is distributed on an "AS IS" BASIS,
3893
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3894
+ * See the License for the specific language governing permissions and
3895
+ * limitations under the License.
3746
3896
  */
3747
- class Bound {
3748
- constructor(position, inclusive) {
3749
- this.position = position;
3750
- this.inclusive = inclusive;
3897
+ // Visible for testing
3898
+ class TargetImpl {
3899
+ constructor(path, collectionGroup = null, orderBy = [], filters = [], limit = null, startAt = null, endAt = null) {
3900
+ this.path = path;
3901
+ this.collectionGroup = collectionGroup;
3902
+ this.orderBy = orderBy;
3903
+ this.filters = filters;
3904
+ this.limit = limit;
3905
+ this.startAt = startAt;
3906
+ this.endAt = endAt;
3907
+ this.memoizedCanonicalId = null;
3751
3908
  }
3752
3909
  }
3753
3910
  /**
3754
- * An ordering on a field, in some Direction. Direction defaults to ASCENDING.
3911
+ * Initializes a Target with a path and optional additional query constraints.
3912
+ * Path must currently be empty if this is a collection group query.
3913
+ *
3914
+ * NOTE: you should always construct `Target` from `Query.toTarget` instead of
3915
+ * using this factory method, because `Query` provides an implicit `orderBy`
3916
+ * property.
3755
3917
  */
3756
- class OrderBy {
3757
- constructor(field, dir = "asc" /* ASCENDING */) {
3758
- this.field = field;
3759
- this.dir = dir;
3760
- }
3761
- }
3762
- function orderByEquals(left, right) {
3763
- return left.dir === right.dir && left.field.isEqual(right.field);
3918
+ function newTarget(path, collectionGroup = null, orderBy = [], filters = [], limit = null, startAt = null, endAt = null) {
3919
+ return new TargetImpl(path, collectionGroup, orderBy, filters, limit, startAt, endAt);
3764
3920
  }
3765
- function boundEquals(left, right) {
3766
- if (left === null) {
3767
- return right === null;
3921
+ function targetEquals(left, right) {
3922
+ if (left.limit !== right.limit) {
3923
+ return false;
3768
3924
  }
3769
- else if (right === null) {
3925
+ if (left.orderBy.length !== right.orderBy.length) {
3770
3926
  return false;
3771
3927
  }
3772
- if (left.inclusive !== right.inclusive ||
3773
- left.position.length !== right.position.length) {
3928
+ for (let i = 0; i < left.orderBy.length; i++) {
3929
+ if (!orderByEquals(left.orderBy[i], right.orderBy[i])) {
3930
+ return false;
3931
+ }
3932
+ }
3933
+ if (left.filters.length !== right.filters.length) {
3774
3934
  return false;
3775
3935
  }
3776
- for (let i = 0; i < left.position.length; i++) {
3777
- const leftPosition = left.position[i];
3778
- const rightPosition = right.position[i];
3779
- if (!valueEquals(leftPosition, rightPosition)) {
3936
+ for (let i = 0; i < left.filters.length; i++) {
3937
+ if (!filterEquals(left.filters[i], right.filters[i])) {
3780
3938
  return false;
3781
3939
  }
3782
3940
  }
3783
- return true;
3941
+ if (left.collectionGroup !== right.collectionGroup) {
3942
+ return false;
3943
+ }
3944
+ if (!left.path.isEqual(right.path)) {
3945
+ return false;
3946
+ }
3947
+ if (!boundEquals(left.startAt, right.startAt)) {
3948
+ return false;
3949
+ }
3950
+ return boundEquals(left.endAt, right.endAt);
3784
3951
  }
3785
3952
 
3786
3953
  /**
@@ -3811,7 +3978,7 @@ class QueryImpl {
3811
3978
  * Initializes a Query with a path and optional additional query constraints.
3812
3979
  * Path must currently be empty if this is a collection group query.
3813
3980
  */
3814
- constructor(path, collectionGroup = null, explicitOrderBy = [], filters = [], limit = null, limitType = "F" /* First */, startAt = null, endAt = null) {
3981
+ constructor(path, collectionGroup = null, explicitOrderBy = [], filters = [], limit = null, limitType = "F" /* LimitType.First */, startAt = null, endAt = null) {
3815
3982
  this.path = path;
3816
3983
  this.collectionGroup = collectionGroup;
3817
3984
  this.explicitOrderBy = explicitOrderBy;
@@ -3838,20 +4005,9 @@ function getFirstOrderByField(query) {
3838
4005
  }
3839
4006
  function getInequalityFilterField(query) {
3840
4007
  for (const filter of query.filters) {
3841
- if (filter.isInequality()) {
3842
- return filter.field;
3843
- }
3844
- }
3845
- return null;
3846
- }
3847
- /**
3848
- * Checks if any of the provided Operators are included in the query and
3849
- * returns the first one that is, or null if none are.
3850
- */
3851
- function findFilterOperator(query, operators) {
3852
- for (const filter of query.filters) {
3853
- if (operators.indexOf(filter.op) >= 0) {
3854
- return filter.op;
4008
+ const result = filter.getFirstInequalityField();
4009
+ if (result !== null) {
4010
+ return result;
3855
4011
  }
3856
4012
  }
3857
4013
  return null;
@@ -3888,7 +4044,7 @@ function queryOrderBy(query) {
3888
4044
  if (!inequalityField.isKeyField()) {
3889
4045
  queryImpl.memoizedOrderBy.push(new OrderBy(inequalityField));
3890
4046
  }
3891
- queryImpl.memoizedOrderBy.push(new OrderBy(FieldPath$1.keyField(), "asc" /* ASCENDING */));
4047
+ queryImpl.memoizedOrderBy.push(new OrderBy(FieldPath$1.keyField(), "asc" /* Direction.ASCENDING */));
3892
4048
  }
3893
4049
  else {
3894
4050
  let foundKeyOrdering = false;
@@ -3904,7 +4060,7 @@ function queryOrderBy(query) {
3904
4060
  const lastDirection = queryImpl.explicitOrderBy.length > 0
3905
4061
  ? queryImpl.explicitOrderBy[queryImpl.explicitOrderBy.length - 1]
3906
4062
  .dir
3907
- : "asc" /* ASCENDING */;
4063
+ : "asc" /* Direction.ASCENDING */;
3908
4064
  queryImpl.memoizedOrderBy.push(new OrderBy(FieldPath$1.keyField(), lastDirection));
3909
4065
  }
3910
4066
  }
@@ -3917,16 +4073,16 @@ function queryOrderBy(query) {
3917
4073
  function queryToTarget(query) {
3918
4074
  const queryImpl = debugCast(query);
3919
4075
  if (!queryImpl.memoizedTarget) {
3920
- if (queryImpl.limitType === "F" /* First */) {
4076
+ if (queryImpl.limitType === "F" /* LimitType.First */) {
3921
4077
  queryImpl.memoizedTarget = newTarget(queryImpl.path, queryImpl.collectionGroup, queryOrderBy(queryImpl), queryImpl.filters, queryImpl.limit, queryImpl.startAt, queryImpl.endAt);
3922
4078
  }
3923
4079
  else {
3924
4080
  // Flip the orderBy directions since we want the last results
3925
4081
  const orderBys = [];
3926
4082
  for (const orderBy of queryOrderBy(queryImpl)) {
3927
- const dir = orderBy.dir === "desc" /* DESCENDING */
3928
- ? "asc" /* ASCENDING */
3929
- : "desc" /* DESCENDING */;
4083
+ const dir = orderBy.dir === "desc" /* Direction.DESCENDING */
4084
+ ? "asc" /* Direction.ASCENDING */
4085
+ : "desc" /* Direction.DESCENDING */;
3930
4086
  orderBys.push(new OrderBy(orderBy.field, dir));
3931
4087
  }
3932
4088
  // We need to swap the cursors to match the now-flipped query ordering.
@@ -3943,6 +4099,8 @@ function queryToTarget(query) {
3943
4099
  return queryImpl.memoizedTarget;
3944
4100
  }
3945
4101
  function queryWithAddedFilter(query, filter) {
4102
+ filter.getFirstInequalityField();
4103
+ getInequalityFilterField(query);
3946
4104
  const newFilters = query.filters.concat([filter]);
3947
4105
  return new QueryImpl(query.path, query.collectionGroup, query.explicitOrderBy.slice(), newFilters, query.limit, query.limitType, query.startAt, query.endAt);
3948
4106
  }
@@ -4182,7 +4340,7 @@ class SetMutation extends Mutation {
4182
4340
  this.value = value;
4183
4341
  this.precondition = precondition;
4184
4342
  this.fieldTransforms = fieldTransforms;
4185
- this.type = 0 /* Set */;
4343
+ this.type = 0 /* MutationType.Set */;
4186
4344
  }
4187
4345
  getFieldMask() {
4188
4346
  return null;
@@ -4209,7 +4367,7 @@ class PatchMutation extends Mutation {
4209
4367
  this.fieldMask = fieldMask;
4210
4368
  this.precondition = precondition;
4211
4369
  this.fieldTransforms = fieldTransforms;
4212
- this.type = 1 /* Patch */;
4370
+ this.type = 1 /* MutationType.Patch */;
4213
4371
  }
4214
4372
  getFieldMask() {
4215
4373
  return this.fieldMask;
@@ -4221,7 +4379,7 @@ class DeleteMutation extends Mutation {
4221
4379
  super();
4222
4380
  this.key = key;
4223
4381
  this.precondition = precondition;
4224
- this.type = 2 /* Delete */;
4382
+ this.type = 2 /* MutationType.Delete */;
4225
4383
  this.fieldTransforms = [];
4226
4384
  }
4227
4385
  getFieldMask() {
@@ -4240,7 +4398,7 @@ class VerifyMutation extends Mutation {
4240
4398
  super();
4241
4399
  this.key = key;
4242
4400
  this.precondition = precondition;
4243
- this.type = 3 /* Verify */;
4401
+ this.type = 3 /* MutationType.Verify */;
4244
4402
  this.fieldTransforms = [];
4245
4403
  }
4246
4404
  getFieldMask() {
@@ -4266,22 +4424,28 @@ class VerifyMutation extends Mutation {
4266
4424
  */
4267
4425
  const DIRECTIONS = (() => {
4268
4426
  const dirs = {};
4269
- dirs["asc" /* ASCENDING */] = 'ASCENDING';
4270
- dirs["desc" /* DESCENDING */] = 'DESCENDING';
4427
+ dirs["asc" /* Direction.ASCENDING */] = 'ASCENDING';
4428
+ dirs["desc" /* Direction.DESCENDING */] = 'DESCENDING';
4271
4429
  return dirs;
4272
4430
  })();
4273
4431
  const OPERATORS = (() => {
4274
4432
  const ops = {};
4275
- ops["<" /* LESS_THAN */] = 'LESS_THAN';
4276
- ops["<=" /* LESS_THAN_OR_EQUAL */] = 'LESS_THAN_OR_EQUAL';
4277
- ops[">" /* GREATER_THAN */] = 'GREATER_THAN';
4278
- ops[">=" /* GREATER_THAN_OR_EQUAL */] = 'GREATER_THAN_OR_EQUAL';
4279
- ops["==" /* EQUAL */] = 'EQUAL';
4280
- ops["!=" /* NOT_EQUAL */] = 'NOT_EQUAL';
4281
- ops["array-contains" /* ARRAY_CONTAINS */] = 'ARRAY_CONTAINS';
4282
- ops["in" /* IN */] = 'IN';
4283
- ops["not-in" /* NOT_IN */] = 'NOT_IN';
4284
- ops["array-contains-any" /* ARRAY_CONTAINS_ANY */] = 'ARRAY_CONTAINS_ANY';
4433
+ ops["<" /* Operator.LESS_THAN */] = 'LESS_THAN';
4434
+ ops["<=" /* Operator.LESS_THAN_OR_EQUAL */] = 'LESS_THAN_OR_EQUAL';
4435
+ ops[">" /* Operator.GREATER_THAN */] = 'GREATER_THAN';
4436
+ ops[">=" /* Operator.GREATER_THAN_OR_EQUAL */] = 'GREATER_THAN_OR_EQUAL';
4437
+ ops["==" /* Operator.EQUAL */] = 'EQUAL';
4438
+ ops["!=" /* Operator.NOT_EQUAL */] = 'NOT_EQUAL';
4439
+ ops["array-contains" /* Operator.ARRAY_CONTAINS */] = 'ARRAY_CONTAINS';
4440
+ ops["in" /* Operator.IN */] = 'IN';
4441
+ ops["not-in" /* Operator.NOT_IN */] = 'NOT_IN';
4442
+ ops["array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */] = 'ARRAY_CONTAINS_ANY';
4443
+ return ops;
4444
+ })();
4445
+ const COMPOSITE_OPERATORS = (() => {
4446
+ const ops = {};
4447
+ ops["and" /* CompositeOperator.AND */] = 'AND';
4448
+ ops["or" /* CompositeOperator.OR */] = 'OR';
4285
4449
  return ops;
4286
4450
  })();
4287
4451
  function assertPresent(value, description) {
@@ -4433,8 +4597,14 @@ function toMutationDocument(serializer, key, fields) {
4433
4597
  function fromDocument(serializer, document, hasCommittedMutations) {
4434
4598
  const key = fromName(serializer, document.name);
4435
4599
  const version = fromVersion(document.updateTime);
4600
+ // If we read a document from persistence that is missing createTime, it's due
4601
+ // to older SDK versions not storing this information. In such cases, we'll
4602
+ // set the createTime to zero. This can be removed in the long term.
4603
+ const createTime = document.createTime
4604
+ ? fromVersion(document.createTime)
4605
+ : SnapshotVersion.min();
4436
4606
  const data = new ObjectValue({ mapValue: { fields: document.fields } });
4437
- const result = MutableDocument.newFoundDocument(key, version, data);
4607
+ const result = MutableDocument.newFoundDocument(key, version, createTime, data);
4438
4608
  if (hasCommittedMutations) {
4439
4609
  result.setHasCommittedMutations();
4440
4610
  }
@@ -4446,8 +4616,11 @@ function fromFound(serializer, doc) {
4446
4616
  assertPresent(doc.found.updateTime);
4447
4617
  const key = fromName(serializer, doc.found.name);
4448
4618
  const version = fromVersion(doc.found.updateTime);
4619
+ const createTime = doc.found.createTime
4620
+ ? fromVersion(doc.found.createTime)
4621
+ : SnapshotVersion.min();
4449
4622
  const data = new ObjectValue({ mapValue: { fields: doc.found.fields } });
4450
- return MutableDocument.newFoundDocument(key, version, data);
4623
+ return MutableDocument.newFoundDocument(key, version, createTime, data);
4451
4624
  }
4452
4625
  function fromMissing(serializer, result) {
4453
4626
  hardAssert(!!result.missing);
@@ -4561,7 +4734,7 @@ function toQueryTarget(serializer, target) {
4561
4734
  result.parent = toQueryPath(serializer, path.popLast());
4562
4735
  result.structuredQuery.from = [{ collectionId: path.lastSegment() }];
4563
4736
  }
4564
- const where = toFilter(target.filters);
4737
+ const where = toFilters(target.filters);
4565
4738
  if (where) {
4566
4739
  result.structuredQuery.where = where;
4567
4740
  }
@@ -4596,17 +4769,11 @@ function toRunAggregationQueryRequest(serializer, target) {
4596
4769
  parent: queryTarget.parent
4597
4770
  };
4598
4771
  }
4599
- function toFilter(filters) {
4772
+ function toFilters(filters) {
4600
4773
  if (filters.length === 0) {
4601
4774
  return;
4602
4775
  }
4603
- const protos = filters.map(filter => {
4604
- return toUnaryOrFieldFilter(filter);
4605
- });
4606
- if (protos.length === 1) {
4607
- return protos[0];
4608
- }
4609
- return { compositeFilter: { op: 'AND', filters: protos } };
4776
+ return toFilter(CompositeFilter.create(filters, "and" /* CompositeOperator.AND */));
4610
4777
  }
4611
4778
  function toOrder(orderBys) {
4612
4779
  if (orderBys.length === 0) {
@@ -4634,6 +4801,9 @@ function toDirection(dir) {
4634
4801
  function toOperatorName(op) {
4635
4802
  return OPERATORS[op];
4636
4803
  }
4804
+ function toCompositeOperatorName(op) {
4805
+ return COMPOSITE_OPERATORS[op];
4806
+ }
4637
4807
  function toFieldPathReference(path) {
4638
4808
  return { fieldPath: path.canonicalString() };
4639
4809
  }
@@ -4645,8 +4815,31 @@ function toPropertyOrder(orderBy) {
4645
4815
  };
4646
4816
  }
4647
4817
  // visible for testing
4818
+ function toFilter(filter) {
4819
+ if (filter instanceof FieldFilter) {
4820
+ return toUnaryOrFieldFilter(filter);
4821
+ }
4822
+ else if (filter instanceof CompositeFilter) {
4823
+ return toCompositeFilter(filter);
4824
+ }
4825
+ else {
4826
+ return fail();
4827
+ }
4828
+ }
4829
+ function toCompositeFilter(filter) {
4830
+ const protos = filter.getFilters().map(filter => toFilter(filter));
4831
+ if (protos.length === 1) {
4832
+ return protos[0];
4833
+ }
4834
+ return {
4835
+ compositeFilter: {
4836
+ op: toCompositeOperatorName(filter.op),
4837
+ filters: protos
4838
+ }
4839
+ };
4840
+ }
4648
4841
  function toUnaryOrFieldFilter(filter) {
4649
- if (filter.op === "==" /* EQUAL */) {
4842
+ if (filter.op === "==" /* Operator.EQUAL */) {
4650
4843
  if (isNanValue(filter.value)) {
4651
4844
  return {
4652
4845
  unaryFilter: {
@@ -4664,7 +4857,7 @@ function toUnaryOrFieldFilter(filter) {
4664
4857
  };
4665
4858
  }
4666
4859
  }
4667
- else if (filter.op === "!=" /* NOT_EQUAL */) {
4860
+ else if (filter.op === "!=" /* Operator.NOT_EQUAL */) {
4668
4861
  if (isNanValue(filter.value)) {
4669
4862
  return {
4670
4863
  unaryFilter: {
@@ -6044,12 +6237,12 @@ class ParsedUpdateData {
6044
6237
  }
6045
6238
  function isWrite(dataSource) {
6046
6239
  switch (dataSource) {
6047
- case 0 /* Set */: // fall through
6048
- case 2 /* MergeSet */: // fall through
6049
- case 1 /* Update */:
6240
+ case 0 /* UserDataSource.Set */: // fall through
6241
+ case 2 /* UserDataSource.MergeSet */: // fall through
6242
+ case 1 /* UserDataSource.Update */:
6050
6243
  return true;
6051
- case 3 /* Argument */:
6052
- case 4 /* ArrayArgument */:
6244
+ case 3 /* UserDataSource.Argument */:
6245
+ case 4 /* UserDataSource.ArrayArgument */:
6053
6246
  return false;
6054
6247
  default:
6055
6248
  throw fail();
@@ -6174,8 +6367,8 @@ function newUserDataReader(firestore) {
6174
6367
  /** Parse document data from a set() call. */
6175
6368
  function parseSetData(userDataReader, methodName, targetDoc, input, hasConverter, options = {}) {
6176
6369
  const context = userDataReader.createContext(options.merge || options.mergeFields
6177
- ? 2 /* MergeSet */
6178
- : 0 /* Set */, methodName, targetDoc, hasConverter);
6370
+ ? 2 /* UserDataSource.MergeSet */
6371
+ : 0 /* UserDataSource.Set */, methodName, targetDoc, hasConverter);
6179
6372
  validatePlainObject('Data must be an object, but it was:', context, input);
6180
6373
  const updateData = parseObject(input, context);
6181
6374
  let fieldMask;
@@ -6206,12 +6399,12 @@ function parseSetData(userDataReader, methodName, targetDoc, input, hasConverter
6206
6399
  }
6207
6400
  class DeleteFieldValueImpl extends FieldValue {
6208
6401
  _toFieldTransform(context) {
6209
- if (context.dataSource === 2 /* MergeSet */) {
6402
+ if (context.dataSource === 2 /* UserDataSource.MergeSet */) {
6210
6403
  // No transform to add for a delete, but we need to add it to our
6211
6404
  // fieldMask so it gets deleted.
6212
6405
  context.fieldMask.push(context.path);
6213
6406
  }
6214
- else if (context.dataSource === 1 /* Update */) {
6407
+ else if (context.dataSource === 1 /* UserDataSource.Update */) {
6215
6408
  throw context.createError(`${this._methodName}() can only appear at the top level ` +
6216
6409
  'of your update data');
6217
6410
  }
@@ -6244,7 +6437,7 @@ class DeleteFieldValueImpl extends FieldValue {
6244
6437
  */
6245
6438
  function createSentinelChildContext(fieldValue, context, arrayElement) {
6246
6439
  return new ParseContextImpl({
6247
- dataSource: 3 /* Argument */,
6440
+ dataSource: 3 /* UserDataSource.Argument */,
6248
6441
  targetDoc: context.settings.targetDoc,
6249
6442
  methodName: fieldValue._methodName,
6250
6443
  arrayElement
@@ -6308,7 +6501,7 @@ class NumericIncrementFieldValueImpl extends FieldValue {
6308
6501
  }
6309
6502
  /** Parse update data from an update() call. */
6310
6503
  function parseUpdateData(userDataReader, methodName, targetDoc, input) {
6311
- const context = userDataReader.createContext(1 /* Update */, methodName, targetDoc);
6504
+ const context = userDataReader.createContext(1 /* UserDataSource.Update */, methodName, targetDoc);
6312
6505
  validatePlainObject('Data must be an object, but it was:', context, input);
6313
6506
  const fieldMaskPaths = [];
6314
6507
  const updateData = ObjectValue.empty();
@@ -6335,7 +6528,7 @@ function parseUpdateData(userDataReader, methodName, targetDoc, input) {
6335
6528
  }
6336
6529
  /** Parse update data from a list of field/value arguments. */
6337
6530
  function parseUpdateVarargs(userDataReader, methodName, targetDoc, field, value, moreFieldsAndValues) {
6338
- const context = userDataReader.createContext(1 /* Update */, methodName, targetDoc);
6531
+ const context = userDataReader.createContext(1 /* UserDataSource.Update */, methodName, targetDoc);
6339
6532
  const keys = [fieldPathFromArgument$1(methodName, field, targetDoc)];
6340
6533
  const values = [value];
6341
6534
  if (moreFieldsAndValues.length % 2 !== 0) {
@@ -6382,7 +6575,7 @@ function parseUpdateVarargs(userDataReader, methodName, targetDoc, field, value,
6382
6575
  * contain additional arrays (e.g. the operand of an `in` query).
6383
6576
  */
6384
6577
  function parseQueryValue(userDataReader, methodName, input, allowArrays = false) {
6385
- const context = userDataReader.createContext(allowArrays ? 4 /* ArrayArgument */ : 3 /* Argument */, methodName);
6578
+ const context = userDataReader.createContext(allowArrays ? 4 /* UserDataSource.ArrayArgument */ : 3 /* UserDataSource.Argument */, methodName);
6386
6579
  const parsed = parseData(input, context);
6387
6580
  return parsed;
6388
6581
  }
@@ -6432,7 +6625,7 @@ function parseData(input, context) {
6432
6625
  // contain additional arrays (each representing an individual field
6433
6626
  // value), so we disable this validation.
6434
6627
  if (context.settings.arrayElement &&
6435
- context.dataSource !== 4 /* ArrayArgument */) {
6628
+ context.dataSource !== 4 /* UserDataSource.ArrayArgument */) {
6436
6629
  throw context.createError('Nested arrays are not supported');
6437
6630
  }
6438
6631
  return parseArray(input, context);
@@ -6874,164 +7067,334 @@ function fieldPathFromArgument(methodName, arg) {
6874
7067
  * limitations under the License.
6875
7068
  */
6876
7069
  function validateHasExplicitOrderByForLimitToLast(query) {
6877
- if (query.limitType === "L" /* Last */ &&
7070
+ if (query.limitType === "L" /* LimitType.Last */ &&
6878
7071
  query.explicitOrderBy.length === 0) {
6879
7072
  throw new FirestoreError(Code.UNIMPLEMENTED, 'limitToLast() queries require specifying at least one orderBy() clause');
6880
7073
  }
6881
7074
  }
7075
+ /**
7076
+ * An `AppliableConstraint` is an abstraction of a constraint that can be applied
7077
+ * to a Firestore query.
7078
+ */
7079
+ class AppliableConstraint {
7080
+ }
6882
7081
  /**
6883
7082
  * A `QueryConstraint` is used to narrow the set of documents returned by a
6884
7083
  * Firestore query. `QueryConstraint`s are created by invoking {@link where},
6885
- * {@link orderBy}, {@link (startAt:1)}, {@link (startAfter:1)}, {@link
6886
- * endBefore:1}, {@link (endAt:1)}, {@link limit} or {@link limitToLast} and
7084
+ * {@link orderBy}, {@link startAt}, {@link startAfter}, {@link
7085
+ * endBefore}, {@link endAt}, {@link limit}, {@link limitToLast} and
6887
7086
  * can then be passed to {@link query} to create a new query instance that
6888
7087
  * also contains this `QueryConstraint`.
6889
7088
  */
6890
- class QueryConstraint {
7089
+ class QueryConstraint extends AppliableConstraint {
6891
7090
  }
6892
- /**
6893
- * Creates a new immutable instance of {@link Query} that is extended to also include
6894
- * additional query constraints.
6895
- *
6896
- * @param query - The {@link Query} instance to use as a base for the new constraints.
6897
- * @param queryConstraints - The list of {@link QueryConstraint}s to apply.
6898
- * @throws if any of the provided query constraints cannot be combined with the
6899
- * existing or new constraints.
6900
- */
6901
- function query(query, ...queryConstraints) {
7091
+ function query(query, queryConstraint, ...additionalQueryConstraints) {
7092
+ let queryConstraints = [];
7093
+ if (queryConstraint instanceof AppliableConstraint) {
7094
+ queryConstraints.push(queryConstraint);
7095
+ }
7096
+ queryConstraints = queryConstraints.concat(additionalQueryConstraints);
7097
+ validateQueryConstraintArray(queryConstraints);
6902
7098
  for (const constraint of queryConstraints) {
6903
7099
  query = constraint._apply(query);
6904
7100
  }
6905
7101
  return query;
6906
7102
  }
6907
- class QueryFilterConstraint extends QueryConstraint {
7103
+ /**
7104
+ * A `QueryFieldFilterConstraint` is used to narrow the set of documents returned by
7105
+ * a Firestore query by filtering on one or more document fields.
7106
+ * `QueryFieldFilterConstraint`s are created by invoking {@link where} and can then
7107
+ * be passed to {@link query} to create a new query instance that also contains
7108
+ * this `QueryFieldFilterConstraint`.
7109
+ */
7110
+ class QueryFieldFilterConstraint extends QueryConstraint {
7111
+ /**
7112
+ * @internal
7113
+ */
6908
7114
  constructor(_field, _op, _value) {
6909
7115
  super();
6910
7116
  this._field = _field;
6911
7117
  this._op = _op;
6912
7118
  this._value = _value;
7119
+ /** The type of this query constraint */
6913
7120
  this.type = 'where';
6914
7121
  }
7122
+ static _create(_field, _op, _value) {
7123
+ return new QueryFieldFilterConstraint(_field, _op, _value);
7124
+ }
6915
7125
  _apply(query) {
7126
+ const filter = this._parse(query);
7127
+ validateNewFieldFilter(query._query, filter);
7128
+ return new Query(query.firestore, query.converter, queryWithAddedFilter(query._query, filter));
7129
+ }
7130
+ _parse(query) {
6916
7131
  const reader = newUserDataReader(query.firestore);
6917
7132
  const filter = newQueryFilter(query._query, 'where', reader, query.firestore._databaseId, this._field, this._op, this._value);
6918
- return new Query(query.firestore, query.converter, queryWithAddedFilter(query._query, filter));
7133
+ return filter;
6919
7134
  }
6920
7135
  }
6921
7136
  /**
6922
- * Creates a {@link QueryConstraint} that enforces that documents must contain the
6923
- * specified field and that the value should satisfy the relation constraint
6924
- * provided.
7137
+ * Creates a {@link QueryFieldFilterConstraint} that enforces that documents
7138
+ * must contain the specified field and that the value should satisfy the
7139
+ * relation constraint provided.
6925
7140
  *
6926
7141
  * @param fieldPath - The path to compare
6927
7142
  * @param opStr - The operation string (e.g "&lt;", "&lt;=", "==", "&lt;",
6928
7143
  * "&lt;=", "!=").
6929
7144
  * @param value - The value for comparison
6930
- * @returns The created {@link Query}.
7145
+ * @returns The created {@link QueryFieldFilterConstraint}.
6931
7146
  */
6932
7147
  function where(fieldPath, opStr, value) {
6933
7148
  const op = opStr;
6934
7149
  const field = fieldPathFromArgument('where', fieldPath);
6935
- return new QueryFilterConstraint(field, op, value);
7150
+ return QueryFieldFilterConstraint._create(field, op, value);
7151
+ }
7152
+ /**
7153
+ * A `QueryCompositeFilterConstraint` is used to narrow the set of documents
7154
+ * returned by a Firestore query by performing the logical OR or AND of multiple
7155
+ * {@link QueryFieldFilterConstraint}s or {@link QueryCompositeFilterConstraint}s.
7156
+ * `QueryCompositeFilterConstraint`s are created by invoking {@link or} or
7157
+ * {@link and} and can then be passed to {@link query} to create a new query
7158
+ * instance that also contains the `QueryCompositeFilterConstraint`.
7159
+ * @internal TODO remove this internal tag with OR Query support in the server
7160
+ */
7161
+ class QueryCompositeFilterConstraint extends AppliableConstraint {
7162
+ /**
7163
+ * @internal
7164
+ */
7165
+ constructor(
7166
+ /** The type of this query constraint */
7167
+ type, _queryConstraints) {
7168
+ super();
7169
+ this.type = type;
7170
+ this._queryConstraints = _queryConstraints;
7171
+ }
7172
+ static _create(type, _queryConstraints) {
7173
+ return new QueryCompositeFilterConstraint(type, _queryConstraints);
7174
+ }
7175
+ _parse(query) {
7176
+ const parsedFilters = this._queryConstraints
7177
+ .map(queryConstraint => {
7178
+ return queryConstraint._parse(query);
7179
+ })
7180
+ .filter(parsedFilter => parsedFilter.getFilters().length > 0);
7181
+ if (parsedFilters.length === 1) {
7182
+ return parsedFilters[0];
7183
+ }
7184
+ return CompositeFilter.create(parsedFilters, this._getOperator());
7185
+ }
7186
+ _apply(query) {
7187
+ const parsedFilter = this._parse(query);
7188
+ if (parsedFilter.getFilters().length === 0) {
7189
+ // Return the existing query if not adding any more filters (e.g. an empty
7190
+ // composite filter).
7191
+ return query;
7192
+ }
7193
+ validateNewFilter(query._query, parsedFilter);
7194
+ return new Query(query.firestore, query.converter, queryWithAddedFilter(query._query, parsedFilter));
7195
+ }
7196
+ _getQueryConstraints() {
7197
+ return this._queryConstraints;
7198
+ }
7199
+ _getOperator() {
7200
+ return this.type === 'and' ? "and" /* CompositeOperator.AND */ : "or" /* CompositeOperator.OR */;
7201
+ }
7202
+ }
7203
+ /**
7204
+ * Creates a {@link QueryCompositeFilterConstraint} that performs a logical OR
7205
+ * of all the provided {@link QueryFilterConstraint}s.
7206
+ *
7207
+ * @param queryConstraints - Optional. The {@link QueryFilterConstraint}s
7208
+ * for OR operation. These must be created with calls to {@link where},
7209
+ * {@link or}, or {@link and}.
7210
+ * @returns The created {@link QueryCompositeFilterConstraint}.
7211
+ * @internal TODO remove this internal tag with OR Query support in the server
7212
+ */
7213
+ function or(...queryConstraints) {
7214
+ // Only support QueryFilterConstraints
7215
+ queryConstraints.forEach(queryConstraint => validateQueryFilterConstraint('or', queryConstraint));
7216
+ return QueryCompositeFilterConstraint._create("or" /* CompositeOperator.OR */, queryConstraints);
7217
+ }
7218
+ /**
7219
+ * Creates a {@link QueryCompositeFilterConstraint} that performs a logical AND
7220
+ * of all the provided {@link QueryFilterConstraint}s.
7221
+ *
7222
+ * @param queryConstraints - Optional. The {@link QueryFilterConstraint}s
7223
+ * for AND operation. These must be created with calls to {@link where},
7224
+ * {@link or}, or {@link and}.
7225
+ * @returns The created {@link QueryCompositeFilterConstraint}.
7226
+ * @internal TODO remove this internal tag with OR Query support in the server
7227
+ */
7228
+ function and(...queryConstraints) {
7229
+ // Only support QueryFilterConstraints
7230
+ queryConstraints.forEach(queryConstraint => validateQueryFilterConstraint('and', queryConstraint));
7231
+ return QueryCompositeFilterConstraint._create("and" /* CompositeOperator.AND */, queryConstraints);
6936
7232
  }
7233
+ /**
7234
+ * A `QueryOrderByConstraint` is used to sort the set of documents returned by a
7235
+ * Firestore query. `QueryOrderByConstraint`s are created by invoking
7236
+ * {@link orderBy} and can then be passed to {@link query} to create a new query
7237
+ * instance that also contains this `QueryOrderByConstraint`.
7238
+ *
7239
+ * Note: Documents that do not contain the orderBy field will not be present in
7240
+ * the query result.
7241
+ */
6937
7242
  class QueryOrderByConstraint extends QueryConstraint {
7243
+ /**
7244
+ * @internal
7245
+ */
6938
7246
  constructor(_field, _direction) {
6939
7247
  super();
6940
7248
  this._field = _field;
6941
7249
  this._direction = _direction;
7250
+ /** The type of this query constraint */
6942
7251
  this.type = 'orderBy';
6943
7252
  }
7253
+ static _create(_field, _direction) {
7254
+ return new QueryOrderByConstraint(_field, _direction);
7255
+ }
6944
7256
  _apply(query) {
6945
7257
  const orderBy = newQueryOrderBy(query._query, this._field, this._direction);
6946
7258
  return new Query(query.firestore, query.converter, queryWithAddedOrderBy(query._query, orderBy));
6947
7259
  }
6948
7260
  }
6949
7261
  /**
6950
- * Creates a {@link QueryConstraint} that sorts the query result by the
7262
+ * Creates a {@link QueryOrderByConstraint} that sorts the query result by the
6951
7263
  * specified field, optionally in descending order instead of ascending.
6952
7264
  *
7265
+ * Note: Documents that do not contain the specified field will not be present
7266
+ * in the query result.
7267
+ *
6953
7268
  * @param fieldPath - The field to sort by.
6954
7269
  * @param directionStr - Optional direction to sort by ('asc' or 'desc'). If
6955
7270
  * not specified, order will be ascending.
6956
- * @returns The created {@link Query}.
7271
+ * @returns The created {@link QueryOrderByConstraint}.
6957
7272
  */
6958
7273
  function orderBy(fieldPath, directionStr = 'asc') {
6959
7274
  const direction = directionStr;
6960
7275
  const path = fieldPathFromArgument('orderBy', fieldPath);
6961
- return new QueryOrderByConstraint(path, direction);
7276
+ return QueryOrderByConstraint._create(path, direction);
6962
7277
  }
7278
+ /**
7279
+ * A `QueryLimitConstraint` is used to limit the number of documents returned by
7280
+ * a Firestore query.
7281
+ * `QueryLimitConstraint`s are created by invoking {@link limit} or
7282
+ * {@link limitToLast} and can then be passed to {@link query} to create a new
7283
+ * query instance that also contains this `QueryLimitConstraint`.
7284
+ */
6963
7285
  class QueryLimitConstraint extends QueryConstraint {
6964
- constructor(type, _limit, _limitType) {
7286
+ /**
7287
+ * @internal
7288
+ */
7289
+ constructor(
7290
+ /** The type of this query constraint */
7291
+ type, _limit, _limitType) {
6965
7292
  super();
6966
7293
  this.type = type;
6967
7294
  this._limit = _limit;
6968
7295
  this._limitType = _limitType;
6969
7296
  }
7297
+ static _create(type, _limit, _limitType) {
7298
+ return new QueryLimitConstraint(type, _limit, _limitType);
7299
+ }
6970
7300
  _apply(query) {
6971
7301
  return new Query(query.firestore, query.converter, queryWithLimit(query._query, this._limit, this._limitType));
6972
7302
  }
6973
7303
  }
6974
7304
  /**
6975
- * Creates a {@link QueryConstraint} that only returns the first matching documents.
7305
+ * Creates a {@link QueryLimitConstraint} that only returns the first matching
7306
+ * documents.
6976
7307
  *
6977
7308
  * @param limit - The maximum number of items to return.
6978
- * @returns The created {@link Query}.
7309
+ * @returns The created {@link QueryLimitConstraint}.
6979
7310
  */
6980
7311
  function limit(limit) {
6981
7312
  validatePositiveNumber('limit', limit);
6982
- return new QueryLimitConstraint('limit', limit, "F" /* First */);
7313
+ return QueryLimitConstraint._create('limit', limit, "F" /* LimitType.First */);
6983
7314
  }
6984
7315
  /**
6985
- * Creates a {@link QueryConstraint} that only returns the last matching documents.
7316
+ * Creates a {@link QueryLimitConstraint} that only returns the last matching
7317
+ * documents.
6986
7318
  *
6987
7319
  * You must specify at least one `orderBy` clause for `limitToLast` queries,
6988
7320
  * otherwise an exception will be thrown during execution.
6989
7321
  *
6990
7322
  * @param limit - The maximum number of items to return.
6991
- * @returns The created {@link Query}.
7323
+ * @returns The created {@link QueryLimitConstraint}.
6992
7324
  */
6993
7325
  function limitToLast(limit) {
6994
7326
  validatePositiveNumber('limitToLast', limit);
6995
- return new QueryLimitConstraint('limitToLast', limit, "L" /* Last */);
7327
+ return QueryLimitConstraint._create('limitToLast', limit, "L" /* LimitType.Last */);
6996
7328
  }
7329
+ /**
7330
+ * A `QueryStartAtConstraint` is used to exclude documents from the start of a
7331
+ * result set returned by a Firestore query.
7332
+ * `QueryStartAtConstraint`s are created by invoking {@link (startAt:1)} or
7333
+ * {@link (startAfter:1)} and can then be passed to {@link query} to create a
7334
+ * new query instance that also contains this `QueryStartAtConstraint`.
7335
+ */
6997
7336
  class QueryStartAtConstraint extends QueryConstraint {
6998
- constructor(type, _docOrFields, _inclusive) {
7337
+ /**
7338
+ * @internal
7339
+ */
7340
+ constructor(
7341
+ /** The type of this query constraint */
7342
+ type, _docOrFields, _inclusive) {
6999
7343
  super();
7000
7344
  this.type = type;
7001
7345
  this._docOrFields = _docOrFields;
7002
7346
  this._inclusive = _inclusive;
7003
7347
  }
7348
+ static _create(type, _docOrFields, _inclusive) {
7349
+ return new QueryStartAtConstraint(type, _docOrFields, _inclusive);
7350
+ }
7004
7351
  _apply(query) {
7005
7352
  const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
7006
7353
  return new Query(query.firestore, query.converter, queryWithStartAt(query._query, bound));
7007
7354
  }
7008
7355
  }
7009
7356
  function startAt(...docOrFields) {
7010
- return new QueryStartAtConstraint('startAt', docOrFields,
7357
+ return QueryStartAtConstraint._create('startAt', docOrFields,
7011
7358
  /*inclusive=*/ true);
7012
7359
  }
7013
7360
  function startAfter(...docOrFields) {
7014
- return new QueryStartAtConstraint('startAfter', docOrFields,
7361
+ return QueryStartAtConstraint._create('startAfter', docOrFields,
7015
7362
  /*inclusive=*/ false);
7016
7363
  }
7364
+ /**
7365
+ * A `QueryEndAtConstraint` is used to exclude documents from the end of a
7366
+ * result set returned by a Firestore query.
7367
+ * `QueryEndAtConstraint`s are created by invoking {@link (endAt:1)} or
7368
+ * {@link (endBefore:1)} and can then be passed to {@link query} to create a new
7369
+ * query instance that also contains this `QueryEndAtConstraint`.
7370
+ */
7017
7371
  class QueryEndAtConstraint extends QueryConstraint {
7018
- constructor(type, _docOrFields, _inclusive) {
7372
+ /**
7373
+ * @internal
7374
+ */
7375
+ constructor(
7376
+ /** The type of this query constraint */
7377
+ type, _docOrFields, _inclusive) {
7019
7378
  super();
7020
7379
  this.type = type;
7021
7380
  this._docOrFields = _docOrFields;
7022
7381
  this._inclusive = _inclusive;
7023
7382
  }
7383
+ static _create(type, _docOrFields, _inclusive) {
7384
+ return new QueryEndAtConstraint(type, _docOrFields, _inclusive);
7385
+ }
7024
7386
  _apply(query) {
7025
7387
  const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
7026
7388
  return new Query(query.firestore, query.converter, queryWithEndAt(query._query, bound));
7027
7389
  }
7028
7390
  }
7029
7391
  function endBefore(...docOrFields) {
7030
- return new QueryEndAtConstraint('endBefore', docOrFields,
7392
+ return QueryEndAtConstraint._create('endBefore', docOrFields,
7031
7393
  /*inclusive=*/ false);
7032
7394
  }
7033
7395
  function endAt(...docOrFields) {
7034
- return new QueryEndAtConstraint('endAt', docOrFields, /*inclusive=*/ true);
7396
+ return QueryEndAtConstraint._create('endAt', docOrFields,
7397
+ /*inclusive=*/ true);
7035
7398
  }
7036
7399
  /** Helper function to create a bound from a document or fields */
7037
7400
  function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive) {
@@ -7047,10 +7410,10 @@ function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive)
7047
7410
  function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op, value) {
7048
7411
  let fieldValue;
7049
7412
  if (fieldPath.isKeyField()) {
7050
- if (op === "array-contains" /* ARRAY_CONTAINS */ || op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
7413
+ if (op === "array-contains" /* Operator.ARRAY_CONTAINS */ || op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
7051
7414
  throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid Query. You can't perform '${op}' queries on documentId().`);
7052
7415
  }
7053
- else if (op === "in" /* IN */ || op === "not-in" /* NOT_IN */) {
7416
+ else if (op === "in" /* Operator.IN */ || op === "not-in" /* Operator.NOT_IN */) {
7054
7417
  validateDisjunctiveFilterElements(value, op);
7055
7418
  const referenceList = [];
7056
7419
  for (const arrayValue of value) {
@@ -7063,16 +7426,15 @@ function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op
7063
7426
  }
7064
7427
  }
7065
7428
  else {
7066
- if (op === "in" /* IN */ ||
7067
- op === "not-in" /* NOT_IN */ ||
7068
- op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
7429
+ if (op === "in" /* Operator.IN */ ||
7430
+ op === "not-in" /* Operator.NOT_IN */ ||
7431
+ op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
7069
7432
  validateDisjunctiveFilterElements(value, op);
7070
7433
  }
7071
7434
  fieldValue = parseQueryValue(dataReader, methodName, value,
7072
- /* allowArrays= */ op === "in" /* IN */ || op === "not-in" /* NOT_IN */);
7435
+ /* allowArrays= */ op === "in" /* Operator.IN */ || op === "not-in" /* Operator.NOT_IN */);
7073
7436
  }
7074
7437
  const filter = FieldFilter.create(fieldPath, op, fieldValue);
7075
- validateNewFilter(query, filter);
7076
7438
  return filter;
7077
7439
  }
7078
7440
  function newQueryOrderBy(query, fieldPath, direction) {
@@ -7242,62 +7604,84 @@ function validateDisjunctiveFilterElements(value, operator) {
7242
7604
  */
7243
7605
  function conflictingOps(op) {
7244
7606
  switch (op) {
7245
- case "!=" /* NOT_EQUAL */:
7246
- return ["!=" /* NOT_EQUAL */, "not-in" /* NOT_IN */];
7247
- case "array-contains" /* ARRAY_CONTAINS */:
7607
+ case "!=" /* Operator.NOT_EQUAL */:
7608
+ return ["!=" /* Operator.NOT_EQUAL */, "not-in" /* Operator.NOT_IN */];
7609
+ case "array-contains" /* Operator.ARRAY_CONTAINS */:
7248
7610
  return [
7249
- "array-contains" /* ARRAY_CONTAINS */,
7250
- "array-contains-any" /* ARRAY_CONTAINS_ANY */,
7251
- "not-in" /* NOT_IN */
7611
+ "array-contains" /* Operator.ARRAY_CONTAINS */,
7612
+ "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
7613
+ "not-in" /* Operator.NOT_IN */
7252
7614
  ];
7253
- case "in" /* IN */:
7254
- return ["array-contains-any" /* ARRAY_CONTAINS_ANY */, "in" /* IN */, "not-in" /* NOT_IN */];
7255
- case "array-contains-any" /* ARRAY_CONTAINS_ANY */:
7615
+ case "in" /* Operator.IN */:
7616
+ return ["array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */, "in" /* Operator.IN */, "not-in" /* Operator.NOT_IN */];
7617
+ case "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */:
7256
7618
  return [
7257
- "array-contains" /* ARRAY_CONTAINS */,
7258
- "array-contains-any" /* ARRAY_CONTAINS_ANY */,
7259
- "in" /* IN */,
7260
- "not-in" /* NOT_IN */
7619
+ "array-contains" /* Operator.ARRAY_CONTAINS */,
7620
+ "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
7621
+ "in" /* Operator.IN */,
7622
+ "not-in" /* Operator.NOT_IN */
7261
7623
  ];
7262
- case "not-in" /* NOT_IN */:
7624
+ case "not-in" /* Operator.NOT_IN */:
7263
7625
  return [
7264
- "array-contains" /* ARRAY_CONTAINS */,
7265
- "array-contains-any" /* ARRAY_CONTAINS_ANY */,
7266
- "in" /* IN */,
7267
- "not-in" /* NOT_IN */,
7268
- "!=" /* NOT_EQUAL */
7626
+ "array-contains" /* Operator.ARRAY_CONTAINS */,
7627
+ "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
7628
+ "in" /* Operator.IN */,
7629
+ "not-in" /* Operator.NOT_IN */,
7630
+ "!=" /* Operator.NOT_EQUAL */
7269
7631
  ];
7270
7632
  default:
7271
7633
  return [];
7272
7634
  }
7273
7635
  }
7274
- function validateNewFilter(query, filter) {
7275
- if (filter.isInequality()) {
7276
- const existingField = getInequalityFilterField(query);
7277
- if (existingField !== null && !existingField.isEqual(filter.field)) {
7636
+ function validateNewFieldFilter(query, fieldFilter) {
7637
+ if (fieldFilter.isInequality()) {
7638
+ const existingInequality = getInequalityFilterField(query);
7639
+ const newInequality = fieldFilter.field;
7640
+ if (existingInequality !== null &&
7641
+ !existingInequality.isEqual(newInequality)) {
7278
7642
  throw new FirestoreError(Code.INVALID_ARGUMENT, 'Invalid query. All where filters with an inequality' +
7279
7643
  ' (<, <=, !=, not-in, >, or >=) must be on the same field. But you have' +
7280
- ` inequality filters on '${existingField.toString()}'` +
7281
- ` and '${filter.field.toString()}'`);
7644
+ ` inequality filters on '${existingInequality.toString()}'` +
7645
+ ` and '${newInequality.toString()}'`);
7282
7646
  }
7283
7647
  const firstOrderByField = getFirstOrderByField(query);
7284
7648
  if (firstOrderByField !== null) {
7285
- validateOrderByAndInequalityMatch(query, filter.field, firstOrderByField);
7649
+ validateOrderByAndInequalityMatch(query, newInequality, firstOrderByField);
7286
7650
  }
7287
7651
  }
7288
- const conflictingOp = findFilterOperator(query, conflictingOps(filter.op));
7652
+ const conflictingOp = findOpInsideFilters(query.filters, conflictingOps(fieldFilter.op));
7289
7653
  if (conflictingOp !== null) {
7290
7654
  // Special case when it's a duplicate op to give a slightly clearer error message.
7291
- if (conflictingOp === filter.op) {
7655
+ if (conflictingOp === fieldFilter.op) {
7292
7656
  throw new FirestoreError(Code.INVALID_ARGUMENT, 'Invalid query. You cannot use more than one ' +
7293
- `'${filter.op.toString()}' filter.`);
7657
+ `'${fieldFilter.op.toString()}' filter.`);
7294
7658
  }
7295
7659
  else {
7296
- throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. You cannot use '${filter.op.toString()}' filters ` +
7660
+ throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. You cannot use '${fieldFilter.op.toString()}' filters ` +
7297
7661
  `with '${conflictingOp.toString()}' filters.`);
7298
7662
  }
7299
7663
  }
7300
7664
  }
7665
+ function validateNewFilter(query, filter) {
7666
+ let testQuery = query;
7667
+ const subFilters = filter.getFlattenedFilters();
7668
+ for (const subFilter of subFilters) {
7669
+ validateNewFieldFilter(testQuery, subFilter);
7670
+ testQuery = queryWithAddedFilter(testQuery, subFilter);
7671
+ }
7672
+ }
7673
+ // Checks if any of the provided filter operators are included in the given list of filters and
7674
+ // returns the first one that is, or null if none are.
7675
+ function findOpInsideFilters(filters, operators) {
7676
+ for (const filter of filters) {
7677
+ for (const fieldFilter of filter.getFlattenedFilters()) {
7678
+ if (operators.indexOf(fieldFilter.op) >= 0) {
7679
+ return fieldFilter.op;
7680
+ }
7681
+ }
7682
+ }
7683
+ return null;
7684
+ }
7301
7685
  function validateNewOrderBy(query, orderBy) {
7302
7686
  if (getFirstOrderByField(query) === null) {
7303
7687
  // This is the first order by. It must match any inequality.
@@ -7315,6 +7699,24 @@ function validateOrderByAndInequalityMatch(baseQuery, inequality, orderBy) {
7315
7699
  `as your first argument to orderBy(), but your first orderBy() ` +
7316
7700
  `is on field '${orderBy.toString()}' instead.`);
7317
7701
  }
7702
+ }
7703
+ function validateQueryFilterConstraint(functionName, queryConstraint) {
7704
+ if (!(queryConstraint instanceof QueryFieldFilterConstraint) &&
7705
+ !(queryConstraint instanceof QueryCompositeFilterConstraint)) {
7706
+ throw new FirestoreError(Code.INVALID_ARGUMENT, `Function ${functionName}() requires AppliableConstraints created with a call to 'where(...)', 'or(...)', or 'and(...)'.`);
7707
+ }
7708
+ }
7709
+ function validateQueryConstraintArray(queryConstraint) {
7710
+ const compositeFilterCount = queryConstraint.filter(filter => filter instanceof QueryCompositeFilterConstraint).length;
7711
+ const fieldFilterCount = queryConstraint.filter(filter => filter instanceof QueryFieldFilterConstraint).length;
7712
+ if (compositeFilterCount > 1 ||
7713
+ (compositeFilterCount > 0 && fieldFilterCount > 0)) {
7714
+ throw new FirestoreError(Code.INVALID_ARGUMENT, 'InvalidQuery. When using composite filters, you cannot use ' +
7715
+ 'more than one filter at the top level. Consider nesting the multiple ' +
7716
+ 'filters within an `and(...)` statement. For example: ' +
7717
+ 'change `query(query, where(...), or(...))` to ' +
7718
+ '`query(query, and(where(...), or(...)))`.');
7719
+ }
7318
7720
  }
7319
7721
 
7320
7722
  /**
@@ -7342,27 +7744,27 @@ function validateOrderByAndInequalityMatch(baseQuery, inequality, orderBy) {
7342
7744
  class AbstractUserDataWriter {
7343
7745
  convertValue(value, serverTimestampBehavior = 'none') {
7344
7746
  switch (typeOrder(value)) {
7345
- case 0 /* NullValue */:
7747
+ case 0 /* TypeOrder.NullValue */:
7346
7748
  return null;
7347
- case 1 /* BooleanValue */:
7749
+ case 1 /* TypeOrder.BooleanValue */:
7348
7750
  return value.booleanValue;
7349
- case 2 /* NumberValue */:
7751
+ case 2 /* TypeOrder.NumberValue */:
7350
7752
  return normalizeNumber(value.integerValue || value.doubleValue);
7351
- case 3 /* TimestampValue */:
7753
+ case 3 /* TypeOrder.TimestampValue */:
7352
7754
  return this.convertTimestamp(value.timestampValue);
7353
- case 4 /* ServerTimestampValue */:
7755
+ case 4 /* TypeOrder.ServerTimestampValue */:
7354
7756
  return this.convertServerTimestamp(value, serverTimestampBehavior);
7355
- case 5 /* StringValue */:
7757
+ case 5 /* TypeOrder.StringValue */:
7356
7758
  return value.stringValue;
7357
- case 6 /* BlobValue */:
7759
+ case 6 /* TypeOrder.BlobValue */:
7358
7760
  return this.convertBytes(normalizeByteString(value.bytesValue));
7359
- case 7 /* RefValue */:
7761
+ case 7 /* TypeOrder.RefValue */:
7360
7762
  return this.convertReference(value.referenceValue);
7361
- case 8 /* GeoPointValue */:
7763
+ case 8 /* TypeOrder.GeoPointValue */:
7362
7764
  return this.convertGeoPoint(value.geoPointValue);
7363
- case 9 /* ArrayValue */:
7765
+ case 9 /* TypeOrder.ArrayValue */:
7364
7766
  return this.convertArray(value.arrayValue, serverTimestampBehavior);
7365
- case 10 /* ObjectValue */:
7767
+ case 10 /* TypeOrder.ObjectValue */:
7366
7768
  return this.convertObject(value.mapValue, serverTimestampBehavior);
7367
7769
  default:
7368
7770
  throw fail();
@@ -7515,7 +7917,7 @@ function getDocs(query) {
7515
7917
  const userDataWriter = new LiteUserDataWriter(query.firestore);
7516
7918
  return invokeRunQueryRpc(datastore, query._query).then(result => {
7517
7919
  const docs = result.map(doc => new QueryDocumentSnapshot(query.firestore, userDataWriter, doc.key, doc, query.converter));
7518
- if (query._query.limitType === "L" /* Last */) {
7920
+ if (query._query.limitType === "L" /* LimitType.Last */) {
7519
7921
  // Limit to last queries reverse the orderBy constraint that was
7520
7922
  // specified by the user. As such, we need to reverse the order of the
7521
7923
  // results to return the documents in the expected order.
@@ -8078,7 +8480,7 @@ class TransactionRunner {
8078
8480
  this.updateFunction = updateFunction;
8079
8481
  this.deferred = deferred;
8080
8482
  this.attemptsRemaining = options.maxAttempts;
8081
- this.backoff = new ExponentialBackoff(this.asyncQueue, "transaction_retry" /* TransactionRetry */);
8483
+ this.backoff = new ExponentialBackoff(this.asyncQueue, "transaction_retry" /* TimerId.TransactionRetry */);
8082
8484
  }
8083
8485
  /** Runs the transaction and sets the result on deferred. */
8084
8486
  run() {
@@ -8302,7 +8704,7 @@ class AsyncQueueImpl {
8302
8704
  // List of TimerIds to fast-forward delays for.
8303
8705
  this.timerIdsToSkip = [];
8304
8706
  // Backoff timer used to schedule retries for retryable operations
8305
- this.backoff = new ExponentialBackoff(this, "async_queue_retry" /* AsyncQueueRetry */);
8707
+ this.backoff = new ExponentialBackoff(this, "async_queue_retry" /* TimerId.AsyncQueueRetry */);
8306
8708
  // Visibility handler that triggers an immediate retry of all retryable
8307
8709
  // operations. Meant to speed up recovery when we regain file system access
8308
8710
  // after page comes into foreground.
@@ -8472,7 +8874,7 @@ class AsyncQueueImpl {
8472
8874
  this.delayedOperations.sort((a, b) => a.targetTimeMs - b.targetTimeMs);
8473
8875
  for (const op of this.delayedOperations) {
8474
8876
  op.skipDelay();
8475
- if (lastTimerId !== "all" /* All */ && op.timerId === lastTimerId) {
8877
+ if (lastTimerId !== "all" /* TimerId.All */ && op.timerId === lastTimerId) {
8476
8878
  break;
8477
8879
  }
8478
8880
  }
@@ -8656,14 +9058,21 @@ exports.Firestore = Firestore;
8656
9058
  exports.FirestoreError = FirestoreError;
8657
9059
  exports.GeoPoint = GeoPoint;
8658
9060
  exports.Query = Query;
9061
+ exports.QueryCompositeFilterConstraint = QueryCompositeFilterConstraint;
8659
9062
  exports.QueryConstraint = QueryConstraint;
8660
9063
  exports.QueryDocumentSnapshot = QueryDocumentSnapshot;
9064
+ exports.QueryEndAtConstraint = QueryEndAtConstraint;
9065
+ exports.QueryFieldFilterConstraint = QueryFieldFilterConstraint;
9066
+ exports.QueryLimitConstraint = QueryLimitConstraint;
9067
+ exports.QueryOrderByConstraint = QueryOrderByConstraint;
8661
9068
  exports.QuerySnapshot = QuerySnapshot;
9069
+ exports.QueryStartAtConstraint = QueryStartAtConstraint;
8662
9070
  exports.Timestamp = Timestamp;
8663
9071
  exports.Transaction = Transaction;
8664
9072
  exports.WriteBatch = WriteBatch;
8665
9073
  exports.addDoc = addDoc;
8666
9074
  exports.aggregateQuerySnapshotEqual = aggregateQuerySnapshotEqual;
9075
+ exports.and = and;
8667
9076
  exports.arrayRemove = arrayRemove;
8668
9077
  exports.arrayUnion = arrayUnion;
8669
9078
  exports.collection = collection;
@@ -8683,6 +9092,7 @@ exports.increment = increment;
8683
9092
  exports.initializeFirestore = initializeFirestore;
8684
9093
  exports.limit = limit;
8685
9094
  exports.limitToLast = limitToLast;
9095
+ exports.or = or;
8686
9096
  exports.orderBy = orderBy;
8687
9097
  exports.query = query;
8688
9098
  exports.queryEqual = queryEqual;