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