@firebase/firestore 3.4.4-canary.2dd99ae2d → 3.4.4-canary.3a8d4c1d1

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 (172) hide show
  1. package/dist/firestore/src/api/credentials.d.ts +1 -0
  2. package/dist/firestore/src/api/snapshot.d.ts +1 -2
  3. package/dist/firestore/src/core/target.d.ts +8 -4
  4. package/dist/firestore/src/index/index_byte_encoder.d.ts +18 -1
  5. package/dist/firestore/src/index/index_entry.d.ts +26 -0
  6. package/dist/firestore/src/lite-api/query.d.ts +2 -2
  7. package/dist/firestore/src/local/document_overlay_cache.d.ts +72 -0
  8. package/dist/firestore/src/local/index_manager.d.ts +52 -0
  9. package/dist/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
  10. package/dist/firestore/src/local/indexeddb_index_manager.d.ts +40 -2
  11. package/dist/firestore/src/local/indexeddb_persistence.d.ts +6 -4
  12. package/dist/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  13. package/dist/firestore/src/local/indexeddb_schema.d.ts +195 -7
  14. package/dist/firestore/src/local/local_documents_view.d.ts +0 -1
  15. package/dist/firestore/src/local/local_serializer.d.ts +18 -2
  16. package/dist/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
  17. package/dist/firestore/src/local/memory_index_manager.d.ts +11 -0
  18. package/dist/firestore/src/local/memory_persistence.d.ts +6 -2
  19. package/dist/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  20. package/dist/firestore/src/local/persistence.d.ts +8 -2
  21. package/dist/firestore/src/local/remote_document_cache.d.ts +7 -9
  22. package/dist/firestore/src/local/simple_db.d.ts +1 -0
  23. package/dist/firestore/src/model/document_key.d.ts +5 -0
  24. package/dist/firestore/src/model/field_index.d.ts +2 -2
  25. package/dist/firestore/src/model/overlay.d.ts +32 -0
  26. package/dist/firestore/src/model/target_index_matcher.d.ts +70 -0
  27. package/dist/firestore/src/util/sorted_set.d.ts +18 -0
  28. package/dist/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  29. package/dist/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
  30. package/dist/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  31. package/dist/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
  32. package/dist/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  33. package/dist/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  34. package/dist/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  35. package/dist/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  36. package/dist/firestore/test/util/helpers.d.ts +1 -1
  37. package/dist/index.d.ts +1 -2
  38. package/dist/index.esm2017.js +4496 -3484
  39. package/dist/index.esm2017.js.map +1 -1
  40. package/dist/index.esm5.js +4452 -3556
  41. package/dist/index.esm5.js.map +1 -1
  42. package/dist/index.node.cjs.js +1728 -287
  43. package/dist/index.node.cjs.js.map +1 -1
  44. package/dist/index.node.mjs +1728 -287
  45. package/dist/index.node.mjs.map +1 -1
  46. package/dist/index.rn.js +4508 -3496
  47. package/dist/index.rn.js.map +1 -1
  48. package/dist/internal.d.ts +253 -14
  49. package/dist/lite/firestore/src/api/credentials.d.ts +1 -0
  50. package/dist/lite/firestore/src/api/snapshot.d.ts +1 -2
  51. package/dist/lite/firestore/src/core/target.d.ts +8 -4
  52. package/dist/lite/firestore/src/index/index_byte_encoder.d.ts +18 -1
  53. package/dist/lite/firestore/src/index/index_entry.d.ts +26 -0
  54. package/dist/lite/firestore/src/lite-api/query.d.ts +2 -2
  55. package/dist/lite/firestore/src/local/document_overlay_cache.d.ts +72 -0
  56. package/dist/lite/firestore/src/local/index_manager.d.ts +52 -0
  57. package/dist/lite/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
  58. package/dist/lite/firestore/src/local/indexeddb_index_manager.d.ts +40 -2
  59. package/dist/lite/firestore/src/local/indexeddb_persistence.d.ts +6 -4
  60. package/dist/lite/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  61. package/dist/lite/firestore/src/local/indexeddb_schema.d.ts +195 -7
  62. package/dist/lite/firestore/src/local/local_documents_view.d.ts +0 -1
  63. package/dist/lite/firestore/src/local/local_serializer.d.ts +18 -2
  64. package/dist/lite/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
  65. package/dist/lite/firestore/src/local/memory_index_manager.d.ts +11 -0
  66. package/dist/lite/firestore/src/local/memory_persistence.d.ts +6 -2
  67. package/dist/lite/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  68. package/dist/lite/firestore/src/local/persistence.d.ts +8 -2
  69. package/dist/lite/firestore/src/local/remote_document_cache.d.ts +7 -9
  70. package/dist/lite/firestore/src/local/simple_db.d.ts +1 -0
  71. package/dist/lite/firestore/src/model/document_key.d.ts +5 -0
  72. package/dist/lite/firestore/src/model/field_index.d.ts +2 -2
  73. package/dist/lite/firestore/src/model/overlay.d.ts +32 -0
  74. package/dist/lite/firestore/src/model/target_index_matcher.d.ts +70 -0
  75. package/dist/lite/firestore/src/util/sorted_set.d.ts +18 -0
  76. package/dist/lite/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  77. package/dist/lite/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
  78. package/dist/lite/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  79. package/dist/lite/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
  80. package/dist/lite/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  81. package/dist/lite/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  82. package/dist/lite/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  83. package/dist/lite/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  84. package/dist/lite/firestore/test/util/helpers.d.ts +1 -1
  85. package/dist/lite/index.browser.esm2017.js +1155 -1143
  86. package/dist/lite/index.browser.esm2017.js.map +1 -1
  87. package/dist/lite/index.browser.esm5.js +355 -342
  88. package/dist/lite/index.browser.esm5.js.map +1 -1
  89. package/dist/lite/index.node.cjs.js +47 -29
  90. package/dist/lite/index.node.cjs.js.map +1 -1
  91. package/dist/lite/index.node.mjs +47 -29
  92. package/dist/lite/index.node.mjs.map +1 -1
  93. package/dist/lite/index.rn.esm2017.js +332 -320
  94. package/dist/lite/index.rn.esm2017.js.map +1 -1
  95. package/dist/lite/internal.d.ts +7 -2
  96. package/dist/lite/packages/firestore/dist/lite/index.browser.esm2017.d.ts +121 -121
  97. package/dist/lite/packages/firestore/src/api/credentials.d.ts +1 -0
  98. package/dist/lite/packages/firestore/src/api/snapshot.d.ts +1 -2
  99. package/dist/lite/packages/firestore/src/core/target.d.ts +8 -4
  100. package/dist/lite/packages/firestore/src/index/index_byte_encoder.d.ts +18 -1
  101. package/dist/lite/packages/firestore/src/index/index_entry.d.ts +26 -0
  102. package/dist/lite/packages/firestore/src/lite-api/query.d.ts +2 -2
  103. package/dist/lite/packages/firestore/src/local/document_overlay_cache.d.ts +72 -0
  104. package/dist/lite/packages/firestore/src/local/index_manager.d.ts +52 -0
  105. package/dist/lite/packages/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
  106. package/dist/lite/packages/firestore/src/local/indexeddb_index_manager.d.ts +40 -2
  107. package/dist/lite/packages/firestore/src/local/indexeddb_persistence.d.ts +6 -4
  108. package/dist/lite/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  109. package/dist/lite/packages/firestore/src/local/indexeddb_schema.d.ts +195 -7
  110. package/dist/lite/packages/firestore/src/local/local_documents_view.d.ts +0 -1
  111. package/dist/lite/packages/firestore/src/local/local_serializer.d.ts +18 -2
  112. package/dist/lite/packages/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
  113. package/dist/lite/packages/firestore/src/local/memory_index_manager.d.ts +11 -0
  114. package/dist/lite/packages/firestore/src/local/memory_persistence.d.ts +6 -2
  115. package/dist/lite/packages/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  116. package/dist/lite/packages/firestore/src/local/persistence.d.ts +8 -2
  117. package/dist/lite/packages/firestore/src/local/remote_document_cache.d.ts +7 -9
  118. package/dist/lite/packages/firestore/src/local/simple_db.d.ts +1 -0
  119. package/dist/lite/packages/firestore/src/model/document_key.d.ts +5 -0
  120. package/dist/lite/packages/firestore/src/model/field_index.d.ts +2 -2
  121. package/dist/lite/packages/firestore/src/model/overlay.d.ts +32 -0
  122. package/dist/lite/packages/firestore/src/model/target_index_matcher.d.ts +70 -0
  123. package/dist/lite/packages/firestore/src/util/sorted_set.d.ts +18 -0
  124. package/dist/lite/packages/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  125. package/dist/lite/packages/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
  126. package/dist/lite/packages/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  127. package/dist/lite/packages/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
  128. package/dist/lite/packages/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  129. package/dist/lite/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  130. package/dist/lite/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  131. package/dist/lite/packages/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  132. package/dist/lite/packages/firestore/test/util/helpers.d.ts +1 -1
  133. package/dist/lite/private.d.ts +2 -2
  134. package/dist/packages/firestore/dist/index.esm2017.d.ts +189 -186
  135. package/dist/packages/firestore/src/api/credentials.d.ts +1 -0
  136. package/dist/packages/firestore/src/api/snapshot.d.ts +1 -2
  137. package/dist/packages/firestore/src/core/target.d.ts +8 -4
  138. package/dist/packages/firestore/src/index/index_byte_encoder.d.ts +18 -1
  139. package/dist/packages/firestore/src/index/index_entry.d.ts +26 -0
  140. package/dist/packages/firestore/src/lite-api/query.d.ts +2 -2
  141. package/dist/packages/firestore/src/local/document_overlay_cache.d.ts +72 -0
  142. package/dist/packages/firestore/src/local/index_manager.d.ts +52 -0
  143. package/dist/packages/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
  144. package/dist/packages/firestore/src/local/indexeddb_index_manager.d.ts +40 -2
  145. package/dist/packages/firestore/src/local/indexeddb_persistence.d.ts +6 -4
  146. package/dist/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  147. package/dist/packages/firestore/src/local/indexeddb_schema.d.ts +195 -7
  148. package/dist/packages/firestore/src/local/local_documents_view.d.ts +0 -1
  149. package/dist/packages/firestore/src/local/local_serializer.d.ts +18 -2
  150. package/dist/packages/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
  151. package/dist/packages/firestore/src/local/memory_index_manager.d.ts +11 -0
  152. package/dist/packages/firestore/src/local/memory_persistence.d.ts +6 -2
  153. package/dist/packages/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  154. package/dist/packages/firestore/src/local/persistence.d.ts +8 -2
  155. package/dist/packages/firestore/src/local/remote_document_cache.d.ts +7 -9
  156. package/dist/packages/firestore/src/local/simple_db.d.ts +1 -0
  157. package/dist/packages/firestore/src/model/document_key.d.ts +5 -0
  158. package/dist/packages/firestore/src/model/field_index.d.ts +2 -2
  159. package/dist/packages/firestore/src/model/overlay.d.ts +32 -0
  160. package/dist/packages/firestore/src/model/target_index_matcher.d.ts +70 -0
  161. package/dist/packages/firestore/src/util/sorted_set.d.ts +18 -0
  162. package/dist/packages/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  163. package/dist/packages/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
  164. package/dist/packages/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  165. package/dist/packages/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
  166. package/dist/packages/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  167. package/dist/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  168. package/dist/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  169. package/dist/packages/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  170. package/dist/packages/firestore/test/util/helpers.d.ts +1 -1
  171. package/dist/private.d.ts +248 -14
  172. package/package.json +9 -9
@@ -12,7 +12,7 @@ var grpcJs = require('@grpc/grpc-js');
12
12
  var protoLoader = require('@grpc/proto-loader');
13
13
 
14
14
  const name = "@firebase/firestore";
15
- const version$1 = "3.4.4-canary.2dd99ae2d";
15
+ const version$1 = "3.4.4-canary.3a8d4c1d1";
16
16
 
17
17
  /**
18
18
  * @license
@@ -65,7 +65,7 @@ User.GOOGLE_CREDENTIALS = new User('google-credentials-uid');
65
65
  User.FIRST_PARTY = new User('first-party-uid');
66
66
  User.MOCK_USER = new User('mock-user');
67
67
 
68
- const version = "9.6.5-canary.2dd99ae2d";
68
+ const version = "9.6.6-canary.3a8d4c1d1";
69
69
 
70
70
  /**
71
71
  * @license
@@ -670,13 +670,19 @@ class FirebaseAppCheckTokenProvider {
670
670
  this.appCheckProvider = appCheckProvider;
671
671
  this.forceRefresh = false;
672
672
  this.appCheck = null;
673
+ this.latestAppCheckToken = null;
673
674
  }
674
675
  start(asyncQueue, changeListener) {
675
676
  const onTokenChanged = tokenResult => {
676
677
  if (tokenResult.error != null) {
677
678
  logDebug('FirebaseAppCheckTokenProvider', `Error getting App Check token; using placeholder token instead. Error: ${tokenResult.error.message}`);
678
679
  }
679
- return changeListener(tokenResult.token);
680
+ const tokenUpdated = tokenResult.token !== this.latestAppCheckToken;
681
+ this.latestAppCheckToken = tokenResult.token;
682
+ logDebug('FirebaseAppCheckTokenProvider', `Received ${tokenUpdated ? 'new' : 'existing'} token.`);
683
+ return tokenUpdated
684
+ ? changeListener(tokenResult.token)
685
+ : Promise.resolve();
680
686
  };
681
687
  this.tokenListener = (tokenResult) => {
682
688
  asyncQueue.enqueueRetryable(() => onTokenChanged(tokenResult));
@@ -711,6 +717,7 @@ class FirebaseAppCheckTokenProvider {
711
717
  return this.appCheck.getToken(forceRefresh).then(tokenResult => {
712
718
  if (tokenResult) {
713
719
  hardAssert(typeof tokenResult.token === 'string');
720
+ this.latestAppCheckToken = tokenResult.token;
714
721
  return new AppCheckToken(tokenResult.token);
715
722
  }
716
723
  else {
@@ -1196,23 +1203,6 @@ function decodeResourcePath(path) {
1196
1203
  }
1197
1204
  return new ResourcePath(segments);
1198
1205
  }
1199
-
1200
- /**
1201
- * @license
1202
- * Copyright 2017 Google LLC
1203
- *
1204
- * Licensed under the Apache License, Version 2.0 (the "License");
1205
- * you may not use this file except in compliance with the License.
1206
- * You may obtain a copy of the License at
1207
- *
1208
- * http://www.apache.org/licenses/LICENSE-2.0
1209
- *
1210
- * Unless required by applicable law or agreed to in writing, software
1211
- * distributed under the License is distributed on an "AS IS" BASIS,
1212
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1213
- * See the License for the specific language governing permissions and
1214
- * limitations under the License.
1215
- */
1216
1206
  /**
1217
1207
  * Schema Version for the Web client:
1218
1208
  * 1. Initial version including Mutation Queue, Query Cache, and Remote
@@ -1231,8 +1221,10 @@ function decodeResourcePath(path) {
1231
1221
  * an auto-incrementing ID. This is required for Index-Free queries.
1232
1222
  * 10. Rewrite the canonical IDs to the explicit Protobuf-based format.
1233
1223
  * 11. Add bundles and named_queries for bundle support.
1224
+ * 12. Add document overlays.
1225
+ * 13. Add indexing support.
1234
1226
  */
1235
- const SCHEMA_VERSION = 11;
1227
+ const SCHEMA_VERSION = 12;
1236
1228
  /**
1237
1229
  * Wrapper class to store timestamps (seconds and nanos) in IndexedDb objects.
1238
1230
  */
@@ -1757,9 +1749,7 @@ class DbClientMetadata {
1757
1749
  DbClientMetadata.store = 'clientMetadata';
1758
1750
  /** Keys are automatically assigned via the clientId properties. */
1759
1751
  DbClientMetadata.keyPath = 'clientId';
1760
- /**
1761
- * A object representing a bundle loaded by the SDK.
1762
- */
1752
+ /** An object representing a bundle loaded by the SDK. */
1763
1753
  class DbBundle {
1764
1754
  constructor(
1765
1755
  /** The ID of the loaded bundle. */
@@ -1776,9 +1766,7 @@ class DbBundle {
1776
1766
  /** Name of the IndexedDb object store. */
1777
1767
  DbBundle.store = 'bundles';
1778
1768
  DbBundle.keyPath = 'bundleId';
1779
- /**
1780
- * A object representing a named query loaded by the SDK via a bundle.
1781
- */
1769
+ /** An object representing a named query loaded by the SDK via a bundle. */
1782
1770
  class DbNamedQuery {
1783
1771
  constructor(
1784
1772
  /** The name of the query. */
@@ -1795,6 +1783,158 @@ class DbNamedQuery {
1795
1783
  /** Name of the IndexedDb object store. */
1796
1784
  DbNamedQuery.store = 'namedQueries';
1797
1785
  DbNamedQuery.keyPath = 'name';
1786
+ /** An object representing the global configuration for a field index. */
1787
+ class DbIndexConfiguration {
1788
+ constructor(
1789
+ /**
1790
+ * The index id for this entry. Undefined for indexes that are not yet
1791
+ * persisted.
1792
+ */
1793
+ indexId,
1794
+ /** The collection group this index belongs to. */
1795
+ collectionGroup,
1796
+ /** The fields to index for this index. */
1797
+ fields) {
1798
+ this.indexId = indexId;
1799
+ this.collectionGroup = collectionGroup;
1800
+ this.fields = fields;
1801
+ }
1802
+ }
1803
+ /** Name of the IndexedDb object store. */
1804
+ DbIndexConfiguration.store = 'indexConfiguration';
1805
+ DbIndexConfiguration.keyPath = 'indexId';
1806
+ /**
1807
+ * An index that provides access to the index configurations by collection
1808
+ * group.
1809
+ *
1810
+ * PORTING NOTE: iOS and Android maintain this index in-memory, but this is
1811
+ * not possible here as the Web client supports concurrent access to
1812
+ * persistence via multi-tab.
1813
+ */
1814
+ DbIndexConfiguration.collectionGroupIndex = 'collectionGroupIndex';
1815
+ DbIndexConfiguration.collectionGroupIndexPath = 'collectionGroup';
1816
+ /**
1817
+ * An object describing how up-to-date the index backfill is for each user and
1818
+ * index.
1819
+ */
1820
+ class DbIndexState {
1821
+ constructor(
1822
+ /** The index id for this entry. */
1823
+ indexId,
1824
+ /** The user id for this entry. */
1825
+ uid,
1826
+ /**
1827
+ * A number that indicates when the index was last updated (relative to
1828
+ * other indexes).
1829
+ */
1830
+ sequenceNumber,
1831
+ /**
1832
+ * The latest read time that has been indexed by Firestore for this field
1833
+ * index. Set to `{seconds: 0, nanos: 0}` if no documents have been indexed.
1834
+ */
1835
+ readTime,
1836
+ /**
1837
+ * The last document that has been indexed for this field index. Empty if
1838
+ * no documents have been indexed.
1839
+ */
1840
+ documentKey,
1841
+ /**
1842
+ * The largest mutation batch id that has been processed for this index. -1
1843
+ * if no mutations have been indexed.
1844
+ */
1845
+ largestBatchId) {
1846
+ this.indexId = indexId;
1847
+ this.uid = uid;
1848
+ this.sequenceNumber = sequenceNumber;
1849
+ this.readTime = readTime;
1850
+ this.documentKey = documentKey;
1851
+ this.largestBatchId = largestBatchId;
1852
+ }
1853
+ }
1854
+ /** Name of the IndexedDb object store. */
1855
+ DbIndexState.store = 'indexState';
1856
+ DbIndexState.keyPath = ['indexId', 'uid'];
1857
+ /**
1858
+ * An index that provides access to documents in a collection sorted by last
1859
+ * update time. Used by the backfiller.
1860
+ *
1861
+ * PORTING NOTE: iOS and Android maintain this index in-memory, but this is
1862
+ * not possible here as the Web client supports concurrent access to
1863
+ * persistence via multi-tab.
1864
+ */
1865
+ DbIndexState.sequenceNumberIndex = 'sequenceNumberIndex';
1866
+ DbIndexState.sequenceNumberIndexPath = ['uid', 'sequenceNumber'];
1867
+ /** An object that stores the encoded entries for all documents and fields. */
1868
+ class DbIndexEntry {
1869
+ constructor(
1870
+ /** The index id for this entry. */
1871
+ indexId,
1872
+ /** The user id for this entry. */
1873
+ uid,
1874
+ /** The encoded array index value for this entry. */
1875
+ arrayValue,
1876
+ /** The encoded directional value for equality and inequality filters. */
1877
+ directionalValue,
1878
+ /** The document key this entry points to. */
1879
+ documentKey) {
1880
+ this.indexId = indexId;
1881
+ this.uid = uid;
1882
+ this.arrayValue = arrayValue;
1883
+ this.directionalValue = directionalValue;
1884
+ this.documentKey = documentKey;
1885
+ }
1886
+ }
1887
+ /** Name of the IndexedDb object store. */
1888
+ DbIndexEntry.store = 'indexEntries';
1889
+ DbIndexEntry.keyPath = [
1890
+ 'indexId',
1891
+ 'uid',
1892
+ 'arrayValue',
1893
+ 'directionalValue',
1894
+ 'documentKey'
1895
+ ];
1896
+ DbIndexEntry.documentKeyIndex = 'documentKeyIndex';
1897
+ DbIndexEntry.documentKeyIndexPath = ['indexId', 'uid', 'documentKey'];
1898
+ /**
1899
+ * An object representing a document overlay.
1900
+ */
1901
+ class DbDocumentOverlay {
1902
+ constructor(
1903
+ /** The user ID to whom this overlay belongs. */
1904
+ userId,
1905
+ /** The path to the collection that contains the document. */
1906
+ collectionPath,
1907
+ /** The ID (key) of the document within the collection. */
1908
+ documentId,
1909
+ /** The collection group to which the document belongs. */
1910
+ collectionGroup,
1911
+ /** The largest batch ID that's been applied for this overlay. */
1912
+ largestBatchId,
1913
+ /** The overlay mutation. */
1914
+ overlayMutation) {
1915
+ this.userId = userId;
1916
+ this.collectionPath = collectionPath;
1917
+ this.documentId = documentId;
1918
+ this.collectionGroup = collectionGroup;
1919
+ this.largestBatchId = largestBatchId;
1920
+ this.overlayMutation = overlayMutation;
1921
+ }
1922
+ }
1923
+ /** Name of the IndexedDb object store. */
1924
+ DbDocumentOverlay.store = 'documentOverlays';
1925
+ DbDocumentOverlay.keyPath = ['userId', 'collectionPath', 'documentId'];
1926
+ DbDocumentOverlay.collectionPathOverlayIndex = 'collectionPathOverlayIndex';
1927
+ DbDocumentOverlay.collectionPathOverlayIndexPath = [
1928
+ 'userId',
1929
+ 'collectionPath',
1930
+ 'largestBatchId'
1931
+ ];
1932
+ DbDocumentOverlay.collectionGroupOverlayIndex = 'collectionGroupOverlayIndex';
1933
+ DbDocumentOverlay.collectionGroupOverlayIndexPath = [
1934
+ 'userId',
1935
+ 'collectionGroup',
1936
+ 'largestBatchId'
1937
+ ];
1798
1938
  // Visible for testing
1799
1939
  const V1_STORES = [
1800
1940
  DbMutationQueue.store,
@@ -1809,7 +1949,6 @@ const V1_STORES = [
1809
1949
  // V2 is no longer usable (see comment at top of file)
1810
1950
  // Visible for testing
1811
1951
  const V3_STORES = V1_STORES;
1812
- // Visible for testing
1813
1952
  // Note: DbRemoteDocumentChanges is no longer used and dropped with v9.
1814
1953
  const V4_STORES = [...V3_STORES, DbClientMetadata.store];
1815
1954
  // V5 does not change the set of stores.
@@ -1819,12 +1958,28 @@ const V8_STORES = [...V6_STORES, DbCollectionParent.store];
1819
1958
  // V9 does not change the set of stores.
1820
1959
  // V10 does not change the set of stores.
1821
1960
  const V11_STORES = [...V8_STORES, DbBundle.store, DbNamedQuery.store];
1822
- /**
1823
- * The list of all default IndexedDB stores used throughout the SDK. This is
1824
- * used when creating transactions so that access across all stores is done
1825
- * atomically.
1826
- */
1827
- const ALL_STORES = V11_STORES;
1961
+ const V12_STORES = [...V11_STORES, DbDocumentOverlay.store];
1962
+ const V13_STORES = [
1963
+ ...V12_STORES,
1964
+ DbIndexConfiguration.store,
1965
+ DbIndexState.store,
1966
+ DbIndexEntry.store
1967
+ ];
1968
+ /** Returns the object stores for the provided schema. */
1969
+ function getObjectStores(schemaVersion) {
1970
+ if (schemaVersion === 13) {
1971
+ return V13_STORES;
1972
+ }
1973
+ else if (schemaVersion === 12) {
1974
+ return V12_STORES;
1975
+ }
1976
+ else if (schemaVersion === 11) {
1977
+ return V11_STORES;
1978
+ }
1979
+ else {
1980
+ fail();
1981
+ }
1982
+ }
1828
1983
 
1829
1984
  /**
1830
1985
  * @license
@@ -2065,7 +2220,7 @@ class PersistencePromise {
2065
2220
  */
2066
2221
  // References to `window` are guarded by SimpleDb.isAvailable()
2067
2222
  /* eslint-disable no-restricted-globals */
2068
- const LOG_TAG$g = 'SimpleDb';
2223
+ const LOG_TAG$h = 'SimpleDb';
2069
2224
  /**
2070
2225
  * The maximum number of retry attempts for an IndexedDb transaction that fails
2071
2226
  * with a DOMException.
@@ -2116,11 +2271,20 @@ class SimpleDbTransaction {
2116
2271
  this.completionDeferred.reject(error);
2117
2272
  }
2118
2273
  if (!this.aborted) {
2119
- logDebug(LOG_TAG$g, 'Aborting transaction:', error ? error.message : 'Client-initiated abort');
2274
+ logDebug(LOG_TAG$h, 'Aborting transaction:', error ? error.message : 'Client-initiated abort');
2120
2275
  this.aborted = true;
2121
2276
  this.transaction.abort();
2122
2277
  }
2123
2278
  }
2279
+ maybeCommit() {
2280
+ // If the browser supports V3 IndexedDB, we invoke commit() explicitly to
2281
+ // speed up index DB processing if the event loop remains blocks.
2282
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2283
+ const maybeV3IndexedDb = this.transaction;
2284
+ if (!this.aborted && typeof maybeV3IndexedDb.commit === 'function') {
2285
+ maybeV3IndexedDb.commit();
2286
+ }
2287
+ }
2124
2288
  /**
2125
2289
  * Returns a SimpleDbStore<KeyType, ValueType> for the specified store. All
2126
2290
  * operations performed on the SimpleDbStore happen within the context of this
@@ -2170,7 +2334,7 @@ class SimpleDb {
2170
2334
  }
2171
2335
  /** Deletes the specified database. */
2172
2336
  static delete(name) {
2173
- logDebug(LOG_TAG$g, 'Removing database:', name);
2337
+ logDebug(LOG_TAG$h, 'Removing database:', name);
2174
2338
  return wrapRequest(window.indexedDB.deleteDatabase(name)).toPromise();
2175
2339
  }
2176
2340
  /** Returns true if IndexedDB is available in the current environment. */
@@ -2249,7 +2413,7 @@ class SimpleDb {
2249
2413
  */
2250
2414
  async ensureDb(action) {
2251
2415
  if (!this.db) {
2252
- logDebug(LOG_TAG$g, 'Opening database:', this.name);
2416
+ logDebug(LOG_TAG$h, 'Opening database:', this.name);
2253
2417
  this.db = await new Promise((resolve, reject) => {
2254
2418
  // TODO(mikelehen): Investigate browser compatibility.
2255
2419
  // https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB
@@ -2285,12 +2449,12 @@ class SimpleDb {
2285
2449
  }
2286
2450
  };
2287
2451
  request.onupgradeneeded = (event) => {
2288
- logDebug(LOG_TAG$g, 'Database "' + this.name + '" requires upgrade from version:', event.oldVersion);
2452
+ logDebug(LOG_TAG$h, 'Database "' + this.name + '" requires upgrade from version:', event.oldVersion);
2289
2453
  const db = event.target.result;
2290
2454
  this.schemaConverter
2291
2455
  .createOrUpgrade(db, request.transaction, event.oldVersion, this.version)
2292
2456
  .next(() => {
2293
- logDebug(LOG_TAG$g, 'Database upgrade to version ' + this.version + ' complete');
2457
+ logDebug(LOG_TAG$h, 'Database upgrade to version ' + this.version + ' complete');
2294
2458
  });
2295
2459
  };
2296
2460
  });
@@ -2317,6 +2481,10 @@ class SimpleDb {
2317
2481
  this.db = await this.ensureDb(action);
2318
2482
  const transaction = SimpleDbTransaction.open(this.db, action, readonly ? 'readonly' : 'readwrite', objectStores);
2319
2483
  const transactionFnResult = transactionFn(transaction)
2484
+ .next(result => {
2485
+ transaction.maybeCommit();
2486
+ return result;
2487
+ })
2320
2488
  .catch(error => {
2321
2489
  // Abort the transaction if there was an error.
2322
2490
  transaction.abort(error);
@@ -2344,7 +2512,7 @@ class SimpleDb {
2344
2512
  // exception is wrapped in a generic error by our async/await handling.
2345
2513
  const retryable = error.name !== 'FirebaseError' &&
2346
2514
  attemptNumber < TRANSACTION_RETRY_COUNT;
2347
- logDebug(LOG_TAG$g, 'Transaction failed with error:', error.message, 'Retrying:', retryable);
2515
+ logDebug(LOG_TAG$h, 'Transaction failed with error:', error.message, 'Retrying:', retryable);
2348
2516
  this.close();
2349
2517
  if (!retryable) {
2350
2518
  return Promise.reject(error);
@@ -2431,11 +2599,11 @@ class SimpleDbStore {
2431
2599
  put(keyOrValue, value) {
2432
2600
  let request;
2433
2601
  if (value !== undefined) {
2434
- logDebug(LOG_TAG$g, 'PUT', this.store.name, keyOrValue, value);
2602
+ logDebug(LOG_TAG$h, 'PUT', this.store.name, keyOrValue, value);
2435
2603
  request = this.store.put(value, keyOrValue);
2436
2604
  }
2437
2605
  else {
2438
- logDebug(LOG_TAG$g, 'PUT', this.store.name, '<auto-key>', keyOrValue);
2606
+ logDebug(LOG_TAG$h, 'PUT', this.store.name, '<auto-key>', keyOrValue);
2439
2607
  request = this.store.put(keyOrValue);
2440
2608
  }
2441
2609
  return wrapRequest(request);
@@ -2448,7 +2616,7 @@ class SimpleDbStore {
2448
2616
  * @returns The key of the value to add.
2449
2617
  */
2450
2618
  add(value) {
2451
- logDebug(LOG_TAG$g, 'ADD', this.store.name, value, value);
2619
+ logDebug(LOG_TAG$h, 'ADD', this.store.name, value, value);
2452
2620
  const request = this.store.add(value);
2453
2621
  return wrapRequest(request);
2454
2622
  }
@@ -2468,12 +2636,12 @@ class SimpleDbStore {
2468
2636
  if (result === undefined) {
2469
2637
  result = null;
2470
2638
  }
2471
- logDebug(LOG_TAG$g, 'GET', this.store.name, key, result);
2639
+ logDebug(LOG_TAG$h, 'GET', this.store.name, key, result);
2472
2640
  return result;
2473
2641
  });
2474
2642
  }
2475
2643
  delete(key) {
2476
- logDebug(LOG_TAG$g, 'DELETE', this.store.name, key);
2644
+ logDebug(LOG_TAG$h, 'DELETE', this.store.name, key);
2477
2645
  const request = this.store.delete(key);
2478
2646
  return wrapRequest(request);
2479
2647
  }
@@ -2484,21 +2652,37 @@ class SimpleDbStore {
2484
2652
  * Returns the number of rows in the store.
2485
2653
  */
2486
2654
  count() {
2487
- logDebug(LOG_TAG$g, 'COUNT', this.store.name);
2655
+ logDebug(LOG_TAG$h, 'COUNT', this.store.name);
2488
2656
  const request = this.store.count();
2489
2657
  return wrapRequest(request);
2490
2658
  }
2491
2659
  loadAll(indexOrRange, range) {
2492
- const cursor = this.cursor(this.options(indexOrRange, range));
2493
- const results = [];
2494
- return this.iterateCursor(cursor, (key, value) => {
2495
- results.push(value);
2496
- }).next(() => {
2497
- return results;
2498
- });
2660
+ const iterateOptions = this.options(indexOrRange, range);
2661
+ // Use `getAll()` if the browser supports IndexedDB v3, as it is roughly
2662
+ // 20% faster. Unfortunately, getAll() does not support custom indices.
2663
+ if (!iterateOptions.index && typeof this.store.getAll === 'function') {
2664
+ const request = this.store.getAll(iterateOptions.range);
2665
+ return new PersistencePromise((resolve, reject) => {
2666
+ request.onerror = (event) => {
2667
+ reject(event.target.error);
2668
+ };
2669
+ request.onsuccess = (event) => {
2670
+ resolve(event.target.result);
2671
+ };
2672
+ });
2673
+ }
2674
+ else {
2675
+ const cursor = this.cursor(iterateOptions);
2676
+ const results = [];
2677
+ return this.iterateCursor(cursor, (key, value) => {
2678
+ results.push(value);
2679
+ }).next(() => {
2680
+ return results;
2681
+ });
2682
+ }
2499
2683
  }
2500
2684
  deleteAll(indexOrRange, range) {
2501
- logDebug(LOG_TAG$g, 'DELETE ALL', this.store.name);
2685
+ logDebug(LOG_TAG$h, 'DELETE ALL', this.store.name);
2502
2686
  const options = this.options(indexOrRange, range);
2503
2687
  options.keysOnly = false;
2504
2688
  const cursor = this.cursor(options);
@@ -3521,11 +3705,22 @@ class DocumentKey {
3521
3705
  static empty() {
3522
3706
  return new DocumentKey(ResourcePath.emptyPath());
3523
3707
  }
3708
+ get collectionGroup() {
3709
+ return this.path.popLast().lastSegment();
3710
+ }
3524
3711
  /** Returns true if the document is in the specified collectionId. */
3525
3712
  hasCollectionId(collectionId) {
3526
3713
  return (this.path.length >= 2 &&
3527
3714
  this.path.get(this.path.length - 2) === collectionId);
3528
3715
  }
3716
+ /** Returns the collection group (i.e. the name of the parent collection) for this key. */
3717
+ getCollectionGroup() {
3718
+ return this.path.get(this.path.length - 2);
3719
+ }
3720
+ /** Returns the fully qualified path to the parent collection. */
3721
+ getCollectionPath() {
3722
+ return this.path.popLast();
3723
+ }
3529
3724
  isEqual(other) {
3530
3725
  return (other !== null && ResourcePath.comparator(this.path, other.path) === 0);
3531
3726
  }
@@ -3565,6 +3760,13 @@ class DocumentKey {
3565
3760
  * See the License for the specific language governing permissions and
3566
3761
  * limitations under the License.
3567
3762
  */
3763
+ const MAX_VALUE = {
3764
+ mapValue: {
3765
+ fields: {
3766
+ '__type__': { stringValue: '__max___' }
3767
+ }
3768
+ }
3769
+ };
3568
3770
  /** Extracts the backend's type order for the provided value. */
3569
3771
  function typeOrder(value) {
3570
3772
  if ('nullValue' in value) {
@@ -3974,6 +4176,10 @@ function deepClone(source) {
3974
4176
  else {
3975
4177
  return Object.assign({}, source);
3976
4178
  }
4179
+ }
4180
+ /** Returns true if the Value represents the canonical {@link #MAX_VALUE} . */
4181
+ function isMaxValue(value) {
4182
+ return valueEquals(value, MAX_VALUE);
3977
4183
  }
3978
4184
 
3979
4185
  /**
@@ -4349,17 +4555,25 @@ class FieldIndex {
4349
4555
  /** The collection ID this index applies to. */
4350
4556
  collectionGroup,
4351
4557
  /** The field segments for this index. */
4352
- segments,
4558
+ fields,
4353
4559
  /** Shows how up-to-date the index is for the current user. */
4354
4560
  indexState) {
4355
4561
  this.indexId = indexId;
4356
4562
  this.collectionGroup = collectionGroup;
4357
- this.segments = segments;
4563
+ this.fields = fields;
4358
4564
  this.indexState = indexState;
4359
4565
  }
4360
4566
  }
4361
4567
  /** An ID for an index that has not yet been added to persistence. */
4362
4568
  FieldIndex.UNKNOWN_ID = -1;
4569
+ /** Returns the ArrayContains/ArrayContainsAny segment for this index. */
4570
+ function fieldIndexGetArraySegment(fieldIndex) {
4571
+ return fieldIndex.fields.find(s => s.kind === 2 /* CONTAINS */);
4572
+ }
4573
+ /** Returns all directional (ascending/descending) segments for this index. */
4574
+ function fieldIndexGetDirectionalSegments(fieldIndex) {
4575
+ return fieldIndex.fields.filter(s => s.kind !== 2 /* CONTAINS */);
4576
+ }
4363
4577
  /** An index component consisting of field path and index type. */
4364
4578
  class IndexSegment {
4365
4579
  constructor(
@@ -4464,27 +4678,29 @@ function newTarget(path, collectionGroup = null, orderBy = [], filters = [], lim
4464
4678
  function canonifyTarget(target) {
4465
4679
  const targetImpl = debugCast(target);
4466
4680
  if (targetImpl.memoizedCanonicalId === null) {
4467
- let canonicalId = targetImpl.path.canonicalString();
4681
+ let str = targetImpl.path.canonicalString();
4468
4682
  if (targetImpl.collectionGroup !== null) {
4469
- canonicalId += '|cg:' + targetImpl.collectionGroup;
4683
+ str += '|cg:' + targetImpl.collectionGroup;
4470
4684
  }
4471
- canonicalId += '|f:';
4472
- canonicalId += targetImpl.filters.map(f => canonifyFilter(f)).join(',');
4473
- canonicalId += '|ob:';
4474
- canonicalId += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(',');
4685
+ str += '|f:';
4686
+ str += targetImpl.filters.map(f => canonifyFilter(f)).join(',');
4687
+ str += '|ob:';
4688
+ str += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(',');
4475
4689
  if (!isNullOrUndefined(targetImpl.limit)) {
4476
- canonicalId += '|l:';
4477
- canonicalId += targetImpl.limit;
4690
+ str += '|l:';
4691
+ str += targetImpl.limit;
4478
4692
  }
4479
4693
  if (targetImpl.startAt) {
4480
- canonicalId += '|lb:';
4481
- canonicalId += canonifyBound(targetImpl.startAt);
4694
+ str += '|lb:';
4695
+ str += targetImpl.startAt.inclusive ? 'b:' : 'a:';
4696
+ str += targetImpl.startAt.position.map(p => canonicalId(p)).join(',');
4482
4697
  }
4483
4698
  if (targetImpl.endAt) {
4484
- canonicalId += '|ub:';
4485
- canonicalId += canonifyBound(targetImpl.endAt);
4699
+ str += '|ub:';
4700
+ str += targetImpl.endAt.inclusive ? 'a:' : 'b:';
4701
+ str += targetImpl.endAt.position.map(p => canonicalId(p)).join(',');
4486
4702
  }
4487
- targetImpl.memoizedCanonicalId = canonicalId;
4703
+ targetImpl.memoizedCanonicalId = str;
4488
4704
  }
4489
4705
  return targetImpl.memoizedCanonicalId;
4490
4706
  }
@@ -4507,10 +4723,14 @@ function stringifyTarget(target) {
4507
4723
  .join(', ')}]`;
4508
4724
  }
4509
4725
  if (target.startAt) {
4510
- str += ', startAt: ' + canonifyBound(target.startAt);
4726
+ str += ', startAt: ';
4727
+ str += target.startAt.inclusive ? 'b:' : 'a:';
4728
+ str += target.startAt.position.map(p => canonicalId(p)).join(',');
4511
4729
  }
4512
4730
  if (target.endAt) {
4513
- str += ', endAt: ' + canonifyBound(target.endAt);
4731
+ str += ', endAt: ';
4732
+ str += target.endAt.inclusive ? 'a:' : 'b:';
4733
+ str += target.endAt.position.map(p => canonicalId(p)).join(',');
4514
4734
  }
4515
4735
  return `Target(${str})`;
4516
4736
  }
@@ -4733,7 +4953,6 @@ class ArrayContainsAnyFilter extends FieldFilter {
4733
4953
  return other.arrayValue.values.some(val => arrayValueContains(this.value.arrayValue, val));
4734
4954
  }
4735
4955
  }
4736
- // TODO(indexing): Change Bound.before to "inclusive"
4737
4956
  /**
4738
4957
  * Represents a bound of a query.
4739
4958
  *
@@ -4749,17 +4968,11 @@ class ArrayContainsAnyFilter extends FieldFilter {
4749
4968
  * just after the provided values.
4750
4969
  */
4751
4970
  class Bound {
4752
- constructor(position, before) {
4971
+ constructor(position, inclusive) {
4753
4972
  this.position = position;
4754
- this.before = before;
4973
+ this.inclusive = inclusive;
4755
4974
  }
4756
4975
  }
4757
- function canonifyBound(bound) {
4758
- // TODO(b/29183165): Make this collision robust.
4759
- return `${bound.before ? 'b' : 'a'}:${bound.position
4760
- .map(p => canonicalId(p))
4761
- .join(',')}`;
4762
- }
4763
4976
  /**
4764
4977
  * An ordering on a field, in some Direction. Direction defaults to ASCENDING.
4765
4978
  */
@@ -4779,11 +4992,7 @@ function stringifyOrderBy(orderBy) {
4779
4992
  function orderByEquals(left, right) {
4780
4993
  return left.dir === right.dir && left.field.isEqual(right.field);
4781
4994
  }
4782
- /**
4783
- * Returns true if a document sorts before a bound using the provided sort
4784
- * order.
4785
- */
4786
- function sortsBeforeDocument(bound, orderBy, doc) {
4995
+ function boundCompareToDocument(bound, orderBy, doc) {
4787
4996
  let comparison = 0;
4788
4997
  for (let i = 0; i < bound.position.length; i++) {
4789
4998
  const orderByComponent = orderBy[i];
@@ -4802,7 +5011,23 @@ function sortsBeforeDocument(bound, orderBy, doc) {
4802
5011
  break;
4803
5012
  }
4804
5013
  }
4805
- return bound.before ? comparison <= 0 : comparison < 0;
5014
+ return comparison;
5015
+ }
5016
+ /**
5017
+ * Returns true if a document sorts after a bound using the provided sort
5018
+ * order.
5019
+ */
5020
+ function boundSortsAfterDocument(bound, orderBy, doc) {
5021
+ const comparison = boundCompareToDocument(bound, orderBy, doc);
5022
+ return bound.inclusive ? comparison >= 0 : comparison > 0;
5023
+ }
5024
+ /**
5025
+ * Returns true if a document sorts before a bound using the provided sort
5026
+ * order.
5027
+ */
5028
+ function boundSortsBeforeDocument(bound, orderBy, doc) {
5029
+ const comparison = boundCompareToDocument(bound, orderBy, doc);
5030
+ return bound.inclusive ? comparison <= 0 : comparison < 0;
4806
5031
  }
4807
5032
  function boundEquals(left, right) {
4808
5033
  if (left === null) {
@@ -4811,7 +5036,7 @@ function boundEquals(left, right) {
4811
5036
  else if (right === null) {
4812
5037
  return false;
4813
5038
  }
4814
- if (left.before !== right.before ||
5039
+ if (left.inclusive !== right.inclusive ||
4815
5040
  left.position.length !== right.position.length) {
4816
5041
  return false;
4817
5042
  }
@@ -5015,10 +5240,10 @@ function queryToTarget(query) {
5015
5240
  }
5016
5241
  // We need to swap the cursors to match the now-flipped query ordering.
5017
5242
  const startAt = queryImpl.endAt
5018
- ? new Bound(queryImpl.endAt.position, !queryImpl.endAt.before)
5243
+ ? new Bound(queryImpl.endAt.position, !queryImpl.endAt.inclusive)
5019
5244
  : null;
5020
5245
  const endAt = queryImpl.startAt
5021
- ? new Bound(queryImpl.startAt.position, !queryImpl.startAt.before)
5246
+ ? new Bound(queryImpl.startAt.position, !queryImpl.startAt.inclusive)
5022
5247
  : null;
5023
5248
  // Now return as a LimitType.First query.
5024
5249
  queryImpl.memoizedTarget = newTarget(queryImpl.path, queryImpl.collectionGroup, orderBys, queryImpl.filters, queryImpl.limit, startAt, endAt);
@@ -5106,11 +5331,11 @@ function queryMatchesFilters(query, doc) {
5106
5331
  /** Makes sure a document is within the bounds, if provided. */
5107
5332
  function queryMatchesBounds(query, doc) {
5108
5333
  if (query.startAt &&
5109
- !sortsBeforeDocument(query.startAt, queryOrderBy(query), doc)) {
5334
+ !boundSortsBeforeDocument(query.startAt, queryOrderBy(query), doc)) {
5110
5335
  return false;
5111
5336
  }
5112
5337
  if (query.endAt &&
5113
- sortsBeforeDocument(query.endAt, queryOrderBy(query), doc)) {
5338
+ !boundSortsAfterDocument(query.endAt, queryOrderBy(query), doc)) {
5114
5339
  return false;
5115
5340
  }
5116
5341
  return true;
@@ -5765,6 +5990,72 @@ class SortedSetIterator {
5765
5990
  hasNext() {
5766
5991
  return this.iter.hasNext();
5767
5992
  }
5993
+ }
5994
+ /**
5995
+ * Compares two sorted sets for equality using their natural ordering. The
5996
+ * method computes the intersection and invokes `onAdd` for every element that
5997
+ * is in `after` but not `before`. `onRemove` is invoked for every element in
5998
+ * `before` but missing from `after`.
5999
+ *
6000
+ * The method creates a copy of both `before` and `after` and runs in O(n log
6001
+ * n), where n is the size of the two lists.
6002
+ *
6003
+ * @param before - The elements that exist in the original set.
6004
+ * @param after - The elements to diff against the original set.
6005
+ * @param comparator - The comparator for the elements in before and after.
6006
+ * @param onAdd - A function to invoke for every element that is part of `
6007
+ * after` but not `before`.
6008
+ * @param onRemove - A function to invoke for every element that is part of
6009
+ * `before` but not `after`.
6010
+ */
6011
+ function diffSortedSets(before, after, comparator, onAdd, onRemove) {
6012
+ const beforeIt = before.getIterator();
6013
+ const afterIt = after.getIterator();
6014
+ let beforeValue = advanceIterator(beforeIt);
6015
+ let afterValue = advanceIterator(afterIt);
6016
+ // Walk through the two sets at the same time, using the ordering defined by
6017
+ // `comparator`.
6018
+ while (beforeValue || afterValue) {
6019
+ let added = false;
6020
+ let removed = false;
6021
+ if (beforeValue && afterValue) {
6022
+ const cmp = comparator(beforeValue, afterValue);
6023
+ if (cmp < 0) {
6024
+ // The element was removed if the next element in our ordered
6025
+ // walkthrough is only in `before`.
6026
+ removed = true;
6027
+ }
6028
+ else if (cmp > 0) {
6029
+ // The element was added if the next element in our ordered walkthrough
6030
+ // is only in `after`.
6031
+ added = true;
6032
+ }
6033
+ }
6034
+ else if (beforeValue != null) {
6035
+ removed = true;
6036
+ }
6037
+ else {
6038
+ added = true;
6039
+ }
6040
+ if (added) {
6041
+ onAdd(afterValue);
6042
+ afterValue = advanceIterator(afterIt);
6043
+ }
6044
+ else if (removed) {
6045
+ onRemove(beforeValue);
6046
+ beforeValue = advanceIterator(beforeIt);
6047
+ }
6048
+ else {
6049
+ beforeValue = advanceIterator(beforeIt);
6050
+ afterValue = advanceIterator(afterIt);
6051
+ }
6052
+ }
6053
+ }
6054
+ /**
6055
+ * Returns the next element from the iterator or `undefined` if none available.
6056
+ */
6057
+ function advanceIterator(it) {
6058
+ return it.hasNext() ? it.getNext() : undefined;
5768
6059
  }
5769
6060
 
5770
6061
  /**
@@ -6603,6 +6894,47 @@ class MutationBatchResult {
6603
6894
  }
6604
6895
  }
6605
6896
 
6897
+ /**
6898
+ * @license
6899
+ * Copyright 2022 Google LLC
6900
+ *
6901
+ * Licensed under the Apache License, Version 2.0 (the "License");
6902
+ * you may not use this file except in compliance with the License.
6903
+ * You may obtain a copy of the License at
6904
+ *
6905
+ * http://www.apache.org/licenses/LICENSE-2.0
6906
+ *
6907
+ * Unless required by applicable law or agreed to in writing, software
6908
+ * distributed under the License is distributed on an "AS IS" BASIS,
6909
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
6910
+ * See the License for the specific language governing permissions and
6911
+ * limitations under the License.
6912
+ */
6913
+ /**
6914
+ * Representation of an overlay computed by Firestore.
6915
+ *
6916
+ * Holds information about a mutation and the largest batch id in Firestore when
6917
+ * the mutation was created.
6918
+ */
6919
+ class Overlay {
6920
+ constructor(largestBatchId, mutation) {
6921
+ this.largestBatchId = largestBatchId;
6922
+ this.mutation = mutation;
6923
+ }
6924
+ getKey() {
6925
+ return this.mutation.key;
6926
+ }
6927
+ isEqual(other) {
6928
+ return other !== null && this.mutation === other.mutation;
6929
+ }
6930
+ toString() {
6931
+ return `Overlay{
6932
+ largestBatchId: ${this.largestBatchId},
6933
+ mutation: ${this.mutation.toString()}
6934
+ }`;
6935
+ }
6936
+ }
6937
+
6606
6938
  /**
6607
6939
  * @license
6608
6940
  * Copyright 2017 Google LLC
@@ -7075,7 +7407,7 @@ class TargetState {
7075
7407
  this._current = true;
7076
7408
  }
7077
7409
  }
7078
- const LOG_TAG$f = 'WatchChangeAggregator';
7410
+ const LOG_TAG$g = 'WatchChangeAggregator';
7079
7411
  /**
7080
7412
  * A helper class to accumulate watch changes into a RemoteEvent.
7081
7413
  */
@@ -7370,7 +7702,7 @@ class WatchChangeAggregator {
7370
7702
  isActiveTarget(targetId) {
7371
7703
  const targetActive = this.targetDataForActiveTarget(targetId) !== null;
7372
7704
  if (!targetActive) {
7373
- logDebug(LOG_TAG$f, 'Detected inactive target', targetId);
7705
+ logDebug(LOG_TAG$g, 'Detected inactive target', targetId);
7374
7706
  }
7375
7707
  return targetActive;
7376
7708
  }
@@ -7989,10 +8321,10 @@ function toQueryTarget(serializer, target) {
7989
8321
  result.structuredQuery.limit = limit;
7990
8322
  }
7991
8323
  if (target.startAt) {
7992
- result.structuredQuery.startAt = toCursor(target.startAt);
8324
+ result.structuredQuery.startAt = toStartAtCursor(target.startAt);
7993
8325
  }
7994
8326
  if (target.endAt) {
7995
- result.structuredQuery.endAt = toCursor(target.endAt);
8327
+ result.structuredQuery.endAt = toEndAtCursor(target.endAt);
7996
8328
  }
7997
8329
  return result;
7998
8330
  }
@@ -8025,11 +8357,11 @@ function convertQueryTargetToQuery(target) {
8025
8357
  }
8026
8358
  let startAt = null;
8027
8359
  if (query.startAt) {
8028
- startAt = fromCursor(query.startAt);
8360
+ startAt = fromStartAtCursor(query.startAt);
8029
8361
  }
8030
8362
  let endAt = null;
8031
8363
  if (query.endAt) {
8032
- endAt = fromCursor(query.endAt);
8364
+ endAt = fromEndAtCursor(query.endAt);
8033
8365
  }
8034
8366
  return newQuery(path, collectionGroup, orderBy, filterBy, limit, "F" /* First */, startAt, endAt);
8035
8367
  }
@@ -8120,16 +8452,27 @@ function toOrder(orderBys) {
8120
8452
  function fromOrder(orderBys) {
8121
8453
  return orderBys.map(order => fromPropertyOrder(order));
8122
8454
  }
8123
- function toCursor(cursor) {
8455
+ function toStartAtCursor(cursor) {
8456
+ return {
8457
+ before: cursor.inclusive,
8458
+ values: cursor.position
8459
+ };
8460
+ }
8461
+ function toEndAtCursor(cursor) {
8124
8462
  return {
8125
- before: cursor.before,
8463
+ before: !cursor.inclusive,
8126
8464
  values: cursor.position
8127
8465
  };
8128
8466
  }
8129
- function fromCursor(cursor) {
8130
- const before = !!cursor.before;
8467
+ function fromStartAtCursor(cursor) {
8468
+ const inclusive = !!cursor.before;
8131
8469
  const position = cursor.values || [];
8132
- return new Bound(position, before);
8470
+ return new Bound(position, inclusive);
8471
+ }
8472
+ function fromEndAtCursor(cursor) {
8473
+ const inclusive = !cursor.before;
8474
+ const position = cursor.values || [];
8475
+ return new Bound(position, inclusive);
8133
8476
  }
8134
8477
  // visible for testing
8135
8478
  function toDirection(dir) {
@@ -8576,28 +8919,764 @@ function fromBundledQuery(bundledQuery) {
8576
8919
  if (bundledQuery.limitType === 'LAST') {
8577
8920
  return queryWithLimit(query, query.limit, "L" /* Last */);
8578
8921
  }
8579
- return query;
8922
+ return query;
8923
+ }
8924
+ /** Encodes a NamedQuery proto object to a NamedQuery model object. */
8925
+ function fromProtoNamedQuery(namedQuery) {
8926
+ return {
8927
+ name: namedQuery.name,
8928
+ query: fromBundledQuery(namedQuery.bundledQuery),
8929
+ readTime: fromVersion(namedQuery.readTime)
8930
+ };
8931
+ }
8932
+ /** Decodes a BundleMetadata proto into a BundleMetadata object. */
8933
+ function fromBundleMetadata(metadata) {
8934
+ return {
8935
+ id: metadata.id,
8936
+ version: metadata.version,
8937
+ createTime: fromVersion(metadata.createTime)
8938
+ };
8939
+ }
8940
+ /** Encodes a DbDocumentOverlay object to an Overlay model object. */
8941
+ function fromDbDocumentOverlay(localSerializer, dbDocumentOverlay) {
8942
+ return new Overlay(dbDocumentOverlay.largestBatchId, fromMutation(localSerializer.remoteSerializer, dbDocumentOverlay.overlayMutation));
8943
+ }
8944
+ /** Decodes an Overlay model object into a DbDocumentOverlay object. */
8945
+ function toDbDocumentOverlay(localSerializer, userId, overlay) {
8946
+ const [_, collectionPath, documentId] = toDbDocumentOverlayKey(userId, overlay.mutation.key);
8947
+ return new DbDocumentOverlay(userId, collectionPath, documentId, overlay.mutation.key.getCollectionGroup(), overlay.largestBatchId, toMutation(localSerializer.remoteSerializer, overlay.mutation));
8948
+ }
8949
+ /**
8950
+ * Returns the DbDocumentOverlayKey corresponding to the given user and
8951
+ * document key.
8952
+ */
8953
+ function toDbDocumentOverlayKey(userId, docKey) {
8954
+ const docId = docKey.path.lastSegment();
8955
+ const collectionPath = encodeResourcePath(docKey.path.popLast());
8956
+ return [userId, collectionPath, docId];
8957
+ }
8958
+ function toDbIndexConfiguration(index) {
8959
+ return new DbIndexConfiguration(index.indexId, index.collectionGroup, index.fields.map(s => [s.fieldPath.canonicalString(), s.kind]));
8960
+ }
8961
+ function fromDbIndexConfiguration(index, state) {
8962
+ const decodedState = state
8963
+ ? new IndexState(state.sequenceNumber, new IndexOffset(fromDbTimestamp(state.readTime), new DocumentKey(decodeResourcePath(state.documentKey)), state.largestBatchId))
8964
+ : IndexState.empty();
8965
+ const decodedSegments = index.fields.map(([fieldPath, kind]) => new IndexSegment(FieldPath$1.fromServerFormat(fieldPath), kind));
8966
+ return new FieldIndex(index.indexId, index.collectionGroup, decodedSegments, decodedState);
8967
+ }
8968
+ function toDbIndexState(indexId, user, sequenceNumber, offset) {
8969
+ return new DbIndexState(indexId, user.uid || '', sequenceNumber, toDbTimestamp(offset.readTime), encodeResourcePath(offset.documentKey.path), offset.largestBatchId);
8970
+ }
8971
+
8972
+ /**
8973
+ * @license
8974
+ * Copyright 2020 Google LLC
8975
+ *
8976
+ * Licensed under the Apache License, Version 2.0 (the "License");
8977
+ * you may not use this file except in compliance with the License.
8978
+ * You may obtain a copy of the License at
8979
+ *
8980
+ * http://www.apache.org/licenses/LICENSE-2.0
8981
+ *
8982
+ * Unless required by applicable law or agreed to in writing, software
8983
+ * distributed under the License is distributed on an "AS IS" BASIS,
8984
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8985
+ * See the License for the specific language governing permissions and
8986
+ * limitations under the License.
8987
+ */
8988
+ class IndexedDbBundleCache {
8989
+ getBundleMetadata(transaction, bundleId) {
8990
+ return bundlesStore(transaction)
8991
+ .get(bundleId)
8992
+ .next(bundle => {
8993
+ if (bundle) {
8994
+ return fromDbBundle(bundle);
8995
+ }
8996
+ return undefined;
8997
+ });
8998
+ }
8999
+ saveBundleMetadata(transaction, bundleMetadata) {
9000
+ return bundlesStore(transaction).put(toDbBundle(bundleMetadata));
9001
+ }
9002
+ getNamedQuery(transaction, queryName) {
9003
+ return namedQueriesStore(transaction)
9004
+ .get(queryName)
9005
+ .next(query => {
9006
+ if (query) {
9007
+ return fromDbNamedQuery(query);
9008
+ }
9009
+ return undefined;
9010
+ });
9011
+ }
9012
+ saveNamedQuery(transaction, query) {
9013
+ return namedQueriesStore(transaction).put(toDbNamedQuery(query));
9014
+ }
9015
+ }
9016
+ /**
9017
+ * Helper to get a typed SimpleDbStore for the bundles object store.
9018
+ */
9019
+ function bundlesStore(txn) {
9020
+ return getStore(txn, DbBundle.store);
9021
+ }
9022
+ /**
9023
+ * Helper to get a typed SimpleDbStore for the namedQueries object store.
9024
+ */
9025
+ function namedQueriesStore(txn) {
9026
+ return getStore(txn, DbNamedQuery.store);
9027
+ }
9028
+
9029
+ /**
9030
+ * @license
9031
+ * Copyright 2022 Google LLC
9032
+ *
9033
+ * Licensed under the Apache License, Version 2.0 (the "License");
9034
+ * you may not use this file except in compliance with the License.
9035
+ * You may obtain a copy of the License at
9036
+ *
9037
+ * http://www.apache.org/licenses/LICENSE-2.0
9038
+ *
9039
+ * Unless required by applicable law or agreed to in writing, software
9040
+ * distributed under the License is distributed on an "AS IS" BASIS,
9041
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9042
+ * See the License for the specific language governing permissions and
9043
+ * limitations under the License.
9044
+ */
9045
+ /**
9046
+ * Implementation of DocumentOverlayCache using IndexedDb.
9047
+ */
9048
+ class IndexedDbDocumentOverlayCache {
9049
+ /**
9050
+ * @param serializer - The document serializer.
9051
+ * @param userId - The userId for which we are accessing overlays.
9052
+ */
9053
+ constructor(serializer, userId) {
9054
+ this.serializer = serializer;
9055
+ this.userId = userId;
9056
+ }
9057
+ static forUser(serializer, user) {
9058
+ const userId = user.uid || '';
9059
+ return new IndexedDbDocumentOverlayCache(serializer, userId);
9060
+ }
9061
+ getOverlay(transaction, key) {
9062
+ return documentOverlayStore(transaction)
9063
+ .get(toDbDocumentOverlayKey(this.userId, key))
9064
+ .next(dbOverlay => {
9065
+ if (dbOverlay) {
9066
+ return fromDbDocumentOverlay(this.serializer, dbOverlay);
9067
+ }
9068
+ return null;
9069
+ });
9070
+ }
9071
+ saveOverlays(transaction, largestBatchId, overlays) {
9072
+ const promises = [];
9073
+ overlays.forEach(mutation => {
9074
+ const overlay = new Overlay(largestBatchId, mutation);
9075
+ promises.push(this.saveOverlay(transaction, overlay));
9076
+ });
9077
+ return PersistencePromise.waitFor(promises);
9078
+ }
9079
+ removeOverlaysForBatchId(transaction, documentKeys, batchId) {
9080
+ const collectionPaths = new Set();
9081
+ // Get the set of unique collection paths.
9082
+ documentKeys.forEach(key => collectionPaths.add(encodeResourcePath(key.getCollectionPath())));
9083
+ const promises = [];
9084
+ collectionPaths.forEach(collectionPath => {
9085
+ const range = IDBKeyRange.bound([this.userId, collectionPath, batchId], [this.userId, collectionPath, batchId + 1],
9086
+ /*lowerOpen=*/ false,
9087
+ /*upperOpen=*/ true);
9088
+ promises.push(documentOverlayStore(transaction).deleteAll(DbDocumentOverlay.collectionPathOverlayIndex, range));
9089
+ });
9090
+ return PersistencePromise.waitFor(promises);
9091
+ }
9092
+ getOverlaysForCollection(transaction, collection, sinceBatchId) {
9093
+ const result = new Map();
9094
+ const collectionPath = encodeResourcePath(collection);
9095
+ // We want batch IDs larger than `sinceBatchId`, and so the lower bound
9096
+ // is not inclusive.
9097
+ const range = IDBKeyRange.bound([this.userId, collectionPath, sinceBatchId], [this.userId, collectionPath, Number.POSITIVE_INFINITY],
9098
+ /*lowerOpen=*/ true);
9099
+ return documentOverlayStore(transaction)
9100
+ .loadAll(DbDocumentOverlay.collectionPathOverlayIndex, range)
9101
+ .next(dbOverlays => {
9102
+ for (const dbOverlay of dbOverlays) {
9103
+ const overlay = fromDbDocumentOverlay(this.serializer, dbOverlay);
9104
+ result.set(overlay.getKey(), overlay);
9105
+ }
9106
+ return result;
9107
+ });
9108
+ }
9109
+ getOverlaysForCollectionGroup(transaction, collectionGroup, sinceBatchId, count) {
9110
+ const result = new Map();
9111
+ let currentBatchId = undefined;
9112
+ // We want batch IDs larger than `sinceBatchId`, and so the lower bound
9113
+ // is not inclusive.
9114
+ const range = IDBKeyRange.bound([this.userId, collectionGroup, sinceBatchId], [this.userId, collectionGroup, Number.POSITIVE_INFINITY],
9115
+ /*lowerOpen=*/ true);
9116
+ return documentOverlayStore(transaction)
9117
+ .iterate({
9118
+ index: DbDocumentOverlay.collectionGroupOverlayIndex,
9119
+ range
9120
+ }, (_, dbOverlay, control) => {
9121
+ // We do not want to return partial batch overlays, even if the size
9122
+ // of the result set exceeds the given `count` argument. Therefore, we
9123
+ // continue to aggregate results even after the result size exceeds
9124
+ // `count` if there are more overlays from the `currentBatchId`.
9125
+ const overlay = fromDbDocumentOverlay(this.serializer, dbOverlay);
9126
+ if (result.size < count ||
9127
+ overlay.largestBatchId === currentBatchId) {
9128
+ result.set(overlay.getKey(), overlay);
9129
+ currentBatchId = overlay.largestBatchId;
9130
+ }
9131
+ else {
9132
+ control.done();
9133
+ }
9134
+ })
9135
+ .next(() => result);
9136
+ }
9137
+ saveOverlay(transaction, overlay) {
9138
+ return documentOverlayStore(transaction).put(toDbDocumentOverlay(this.serializer, this.userId, overlay));
9139
+ }
9140
+ }
9141
+ /**
9142
+ * Helper to get a typed SimpleDbStore for the document overlay object store.
9143
+ */
9144
+ function documentOverlayStore(txn) {
9145
+ return getStore(txn, DbDocumentOverlay.store);
9146
+ }
9147
+
9148
+ /**
9149
+ * @license
9150
+ * Copyright 2021 Google LLC
9151
+ *
9152
+ * Licensed under the Apache License, Version 2.0 (the "License");
9153
+ * you may not use this file except in compliance with the License.
9154
+ * You may obtain a copy of the License at
9155
+ *
9156
+ * http://www.apache.org/licenses/LICENSE-2.0
9157
+ *
9158
+ * Unless required by applicable law or agreed to in writing, software
9159
+ * distributed under the License is distributed on an "AS IS" BASIS,
9160
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9161
+ * See the License for the specific language governing permissions and
9162
+ * limitations under the License.
9163
+ */
9164
+ // Note: This code is copied from the backend. Code that is not used by
9165
+ // Firestore was removed.
9166
+ const INDEX_TYPE_NULL = 5;
9167
+ const INDEX_TYPE_BOOLEAN = 10;
9168
+ const INDEX_TYPE_NAN = 13;
9169
+ const INDEX_TYPE_NUMBER = 15;
9170
+ const INDEX_TYPE_TIMESTAMP = 20;
9171
+ const INDEX_TYPE_STRING = 25;
9172
+ const INDEX_TYPE_BLOB = 30;
9173
+ const INDEX_TYPE_REFERENCE = 37;
9174
+ const INDEX_TYPE_GEOPOINT = 45;
9175
+ const INDEX_TYPE_ARRAY = 50;
9176
+ const INDEX_TYPE_MAP = 55;
9177
+ const INDEX_TYPE_REFERENCE_SEGMENT = 60;
9178
+ // A terminator that indicates that a truncatable value was not truncated.
9179
+ // This must be smaller than all other type labels.
9180
+ const NOT_TRUNCATED = 2;
9181
+ /** Firestore index value writer. */
9182
+ class FirestoreIndexValueWriter {
9183
+ constructor() { }
9184
+ // The write methods below short-circuit writing terminators for values
9185
+ // containing a (terminating) truncated value.
9186
+ //
9187
+ // As an example, consider the resulting encoding for:
9188
+ //
9189
+ // ["bar", [2, "foo"]] -> (STRING, "bar", TERM, ARRAY, NUMBER, 2, STRING, "foo", TERM, TERM, TERM)
9190
+ // ["bar", [2, truncated("foo")]] -> (STRING, "bar", TERM, ARRAY, NUMBER, 2, STRING, "foo", TRUNC)
9191
+ // ["bar", truncated(["foo"])] -> (STRING, "bar", TERM, ARRAY. STRING, "foo", TERM, TRUNC)
9192
+ /** Writes an index value. */
9193
+ writeIndexValue(value, encoder) {
9194
+ this.writeIndexValueAux(value, encoder);
9195
+ // Write separator to split index values
9196
+ // (see go/firestore-storage-format#encodings).
9197
+ encoder.writeInfinity();
9198
+ }
9199
+ writeIndexValueAux(indexValue, encoder) {
9200
+ if ('nullValue' in indexValue) {
9201
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_NULL);
9202
+ }
9203
+ else if ('booleanValue' in indexValue) {
9204
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_BOOLEAN);
9205
+ encoder.writeNumber(indexValue.booleanValue ? 1 : 0);
9206
+ }
9207
+ else if ('integerValue' in indexValue) {
9208
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER);
9209
+ encoder.writeNumber(normalizeNumber(indexValue.integerValue));
9210
+ }
9211
+ else if ('doubleValue' in indexValue) {
9212
+ const n = normalizeNumber(indexValue.doubleValue);
9213
+ if (isNaN(n)) {
9214
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_NAN);
9215
+ }
9216
+ else {
9217
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER);
9218
+ if (isNegativeZero(n)) {
9219
+ // -0.0, 0 and 0.0 are all considered the same
9220
+ encoder.writeNumber(0.0);
9221
+ }
9222
+ else {
9223
+ encoder.writeNumber(n);
9224
+ }
9225
+ }
9226
+ }
9227
+ else if ('timestampValue' in indexValue) {
9228
+ const timestamp = indexValue.timestampValue;
9229
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_TIMESTAMP);
9230
+ if (typeof timestamp === 'string') {
9231
+ encoder.writeString(timestamp);
9232
+ }
9233
+ else {
9234
+ encoder.writeString(`${timestamp.seconds || ''}`);
9235
+ encoder.writeNumber(timestamp.nanos || 0);
9236
+ }
9237
+ }
9238
+ else if ('stringValue' in indexValue) {
9239
+ this.writeIndexString(indexValue.stringValue, encoder);
9240
+ this.writeTruncationMarker(encoder);
9241
+ }
9242
+ else if ('bytesValue' in indexValue) {
9243
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_BLOB);
9244
+ encoder.writeBytes(normalizeByteString(indexValue.bytesValue));
9245
+ this.writeTruncationMarker(encoder);
9246
+ }
9247
+ else if ('referenceValue' in indexValue) {
9248
+ this.writeIndexEntityRef(indexValue.referenceValue, encoder);
9249
+ }
9250
+ else if ('geoPointValue' in indexValue) {
9251
+ const geoPoint = indexValue.geoPointValue;
9252
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_GEOPOINT);
9253
+ encoder.writeNumber(geoPoint.latitude || 0);
9254
+ encoder.writeNumber(geoPoint.longitude || 0);
9255
+ }
9256
+ else if ('mapValue' in indexValue) {
9257
+ if (isMaxValue(indexValue)) {
9258
+ this.writeValueTypeLabel(encoder, Number.MAX_SAFE_INTEGER);
9259
+ }
9260
+ else {
9261
+ this.writeIndexMap(indexValue.mapValue, encoder);
9262
+ this.writeTruncationMarker(encoder);
9263
+ }
9264
+ }
9265
+ else if ('arrayValue' in indexValue) {
9266
+ this.writeIndexArray(indexValue.arrayValue, encoder);
9267
+ this.writeTruncationMarker(encoder);
9268
+ }
9269
+ else {
9270
+ fail();
9271
+ }
9272
+ }
9273
+ writeIndexString(stringIndexValue, encoder) {
9274
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_STRING);
9275
+ this.writeUnlabeledIndexString(stringIndexValue, encoder);
9276
+ }
9277
+ writeUnlabeledIndexString(stringIndexValue, encoder) {
9278
+ encoder.writeString(stringIndexValue);
9279
+ }
9280
+ writeIndexMap(mapIndexValue, encoder) {
9281
+ const map = mapIndexValue.fields || {};
9282
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_MAP);
9283
+ for (const key of Object.keys(map)) {
9284
+ this.writeIndexString(key, encoder);
9285
+ this.writeIndexValueAux(map[key], encoder);
9286
+ }
9287
+ }
9288
+ writeIndexArray(arrayIndexValue, encoder) {
9289
+ const values = arrayIndexValue.values || [];
9290
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_ARRAY);
9291
+ for (const element of values) {
9292
+ this.writeIndexValueAux(element, encoder);
9293
+ }
9294
+ }
9295
+ writeIndexEntityRef(referenceValue, encoder) {
9296
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_REFERENCE);
9297
+ const path = DocumentKey.fromName(referenceValue).path;
9298
+ path.forEach(segment => {
9299
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_REFERENCE_SEGMENT);
9300
+ this.writeUnlabeledIndexString(segment, encoder);
9301
+ });
9302
+ }
9303
+ writeValueTypeLabel(encoder, typeOrder) {
9304
+ encoder.writeNumber(typeOrder);
9305
+ }
9306
+ writeTruncationMarker(encoder) {
9307
+ // While the SDK does not implement truncation, the truncation marker is
9308
+ // used to terminate all variable length values (which are strings, bytes,
9309
+ // references, arrays and maps).
9310
+ encoder.writeNumber(NOT_TRUNCATED);
9311
+ }
9312
+ }
9313
+ FirestoreIndexValueWriter.INSTANCE = new FirestoreIndexValueWriter();
9314
+
9315
+ /**
9316
+ * @license
9317
+ * Copyright 2021 Google LLC
9318
+ *
9319
+ * Licensed under the Apache License, Version 2.0 (the "License");
9320
+ * you may not use this file except in compliance with the License.
9321
+ * You may obtain a copy of the License at
9322
+ *
9323
+ * http://www.apache.org/licenses/LICENSE-2.0
9324
+ *
9325
+ * Unless required by applicable law | agreed to in writing, software
9326
+ * distributed under the License is distributed on an "AS IS" BASIS,
9327
+ * WITHOUT WARRANTIES | CONDITIONS OF ANY KIND, either express | implied.
9328
+ * See the License for the specific language governing permissions and
9329
+ * limitations under the License.
9330
+ */
9331
+ /** These constants are taken from the backend. */
9332
+ const MIN_SURROGATE = '\uD800';
9333
+ const MAX_SURROGATE = '\uDBFF';
9334
+ const ESCAPE1 = 0x00;
9335
+ const NULL_BYTE = 0xff; // Combined with ESCAPE1
9336
+ const SEPARATOR = 0x01; // Combined with ESCAPE1
9337
+ const ESCAPE2 = 0xff;
9338
+ const INFINITY = 0xff; // Combined with ESCAPE2
9339
+ const FF_BYTE = 0x00; // Combined with ESCAPE2
9340
+ const LONG_SIZE = 64;
9341
+ const BYTE_SIZE = 8;
9342
+ /**
9343
+ * The default size of the buffer. This is arbitrary, but likely larger than
9344
+ * most index values so that less copies of the underlying buffer will be made.
9345
+ * For large values, a single copy will made to double the buffer length.
9346
+ */
9347
+ const DEFAULT_BUFFER_SIZE = 1024;
9348
+ /** Converts a JavaScript number to a byte array (using big endian encoding). */
9349
+ function doubleToLongBits(value) {
9350
+ const dv = new DataView(new ArrayBuffer(8));
9351
+ dv.setFloat64(0, value, /* littleEndian= */ false);
9352
+ return new Uint8Array(dv.buffer);
9353
+ }
9354
+ /**
9355
+ * Counts the number of zeros in a byte.
9356
+ *
9357
+ * Visible for testing.
9358
+ */
9359
+ function numberOfLeadingZerosInByte(x) {
9360
+ if (x === 0) {
9361
+ return 8;
9362
+ }
9363
+ let zeros = 0;
9364
+ if (x >> 4 === 0) {
9365
+ // Test if the first four bits are zero.
9366
+ zeros += 4;
9367
+ x = x << 4;
9368
+ }
9369
+ if (x >> 6 === 0) {
9370
+ // Test if the first two (or next two) bits are zero.
9371
+ zeros += 2;
9372
+ x = x << 2;
9373
+ }
9374
+ if (x >> 7 === 0) {
9375
+ // Test if the remaining bit is zero.
9376
+ zeros += 1;
9377
+ }
9378
+ return zeros;
9379
+ }
9380
+ /** Counts the number of leading zeros in the given byte array. */
9381
+ function numberOfLeadingZeros(bytes) {
9382
+ let leadingZeros = 0;
9383
+ for (let i = 0; i < 8; ++i) {
9384
+ const zeros = numberOfLeadingZerosInByte(bytes[i] & 0xff);
9385
+ leadingZeros += zeros;
9386
+ if (zeros !== 8) {
9387
+ break;
9388
+ }
9389
+ }
9390
+ return leadingZeros;
9391
+ }
9392
+ /**
9393
+ * Returns the number of bytes required to store "value". Leading zero bytes
9394
+ * are skipped.
9395
+ */
9396
+ function unsignedNumLength(value) {
9397
+ // This is just the number of bytes for the unsigned representation of the number.
9398
+ const numBits = LONG_SIZE - numberOfLeadingZeros(value);
9399
+ return Math.ceil(numBits / BYTE_SIZE);
9400
+ }
9401
+ /**
9402
+ * OrderedCodeWriter is a minimal-allocation implementation of the writing
9403
+ * behavior defined by the backend.
9404
+ *
9405
+ * The code is ported from its Java counterpart.
9406
+ */
9407
+ class OrderedCodeWriter {
9408
+ constructor() {
9409
+ this.buffer = new Uint8Array(DEFAULT_BUFFER_SIZE);
9410
+ this.position = 0;
9411
+ }
9412
+ writeBytesAscending(value) {
9413
+ const it = value[Symbol.iterator]();
9414
+ let byte = it.next();
9415
+ while (!byte.done) {
9416
+ this.writeByteAscending(byte.value);
9417
+ byte = it.next();
9418
+ }
9419
+ this.writeSeparatorAscending();
9420
+ }
9421
+ writeBytesDescending(value) {
9422
+ const it = value[Symbol.iterator]();
9423
+ let byte = it.next();
9424
+ while (!byte.done) {
9425
+ this.writeByteDescending(byte.value);
9426
+ byte = it.next();
9427
+ }
9428
+ this.writeSeparatorDescending();
9429
+ }
9430
+ /** Writes utf8 bytes into this byte sequence, ascending. */
9431
+ writeUtf8Ascending(sequence) {
9432
+ for (const c of sequence) {
9433
+ const charCode = c.charCodeAt(0);
9434
+ if (charCode < 0x80) {
9435
+ this.writeByteAscending(charCode);
9436
+ }
9437
+ else if (charCode < 0x800) {
9438
+ this.writeByteAscending((0x0f << 6) | (charCode >>> 6));
9439
+ this.writeByteAscending(0x80 | (0x3f & charCode));
9440
+ }
9441
+ else if (c < MIN_SURROGATE || MAX_SURROGATE < c) {
9442
+ this.writeByteAscending((0x0f << 5) | (charCode >>> 12));
9443
+ this.writeByteAscending(0x80 | (0x3f & (charCode >>> 6)));
9444
+ this.writeByteAscending(0x80 | (0x3f & charCode));
9445
+ }
9446
+ else {
9447
+ const codePoint = c.codePointAt(0);
9448
+ this.writeByteAscending((0x0f << 4) | (codePoint >>> 18));
9449
+ this.writeByteAscending(0x80 | (0x3f & (codePoint >>> 12)));
9450
+ this.writeByteAscending(0x80 | (0x3f & (codePoint >>> 6)));
9451
+ this.writeByteAscending(0x80 | (0x3f & codePoint));
9452
+ }
9453
+ }
9454
+ this.writeSeparatorAscending();
9455
+ }
9456
+ /** Writes utf8 bytes into this byte sequence, descending */
9457
+ writeUtf8Descending(sequence) {
9458
+ for (const c of sequence) {
9459
+ const charCode = c.charCodeAt(0);
9460
+ if (charCode < 0x80) {
9461
+ this.writeByteDescending(charCode);
9462
+ }
9463
+ else if (charCode < 0x800) {
9464
+ this.writeByteDescending((0x0f << 6) | (charCode >>> 6));
9465
+ this.writeByteDescending(0x80 | (0x3f & charCode));
9466
+ }
9467
+ else if (c < MIN_SURROGATE || MAX_SURROGATE < c) {
9468
+ this.writeByteDescending((0x0f << 5) | (charCode >>> 12));
9469
+ this.writeByteDescending(0x80 | (0x3f & (charCode >>> 6)));
9470
+ this.writeByteDescending(0x80 | (0x3f & charCode));
9471
+ }
9472
+ else {
9473
+ const codePoint = c.codePointAt(0);
9474
+ this.writeByteDescending((0x0f << 4) | (codePoint >>> 18));
9475
+ this.writeByteDescending(0x80 | (0x3f & (codePoint >>> 12)));
9476
+ this.writeByteDescending(0x80 | (0x3f & (codePoint >>> 6)));
9477
+ this.writeByteDescending(0x80 | (0x3f & codePoint));
9478
+ }
9479
+ }
9480
+ this.writeSeparatorDescending();
9481
+ }
9482
+ writeNumberAscending(val) {
9483
+ // Values are encoded with a single byte length prefix, followed by the
9484
+ // actual value in big-endian format with leading 0 bytes dropped.
9485
+ const value = this.toOrderedBits(val);
9486
+ const len = unsignedNumLength(value);
9487
+ this.ensureAvailable(1 + len);
9488
+ this.buffer[this.position++] = len & 0xff; // Write the length
9489
+ for (let i = value.length - len; i < value.length; ++i) {
9490
+ this.buffer[this.position++] = value[i] & 0xff;
9491
+ }
9492
+ }
9493
+ writeNumberDescending(val) {
9494
+ // Values are encoded with a single byte length prefix, followed by the
9495
+ // inverted value in big-endian format with leading 0 bytes dropped.
9496
+ const value = this.toOrderedBits(val);
9497
+ const len = unsignedNumLength(value);
9498
+ this.ensureAvailable(1 + len);
9499
+ this.buffer[this.position++] = ~(len & 0xff); // Write the length
9500
+ for (let i = value.length - len; i < value.length; ++i) {
9501
+ this.buffer[this.position++] = ~(value[i] & 0xff);
9502
+ }
9503
+ }
9504
+ /**
9505
+ * Writes the "infinity" byte sequence that sorts after all other byte
9506
+ * sequences written in ascending order.
9507
+ */
9508
+ writeInfinityAscending() {
9509
+ this.writeEscapedByteAscending(ESCAPE2);
9510
+ this.writeEscapedByteAscending(INFINITY);
9511
+ }
9512
+ /**
9513
+ * Writes the "infinity" byte sequence that sorts before all other byte
9514
+ * sequences written in descending order.
9515
+ */
9516
+ writeInfinityDescending() {
9517
+ this.writeEscapedByteDescending(ESCAPE2);
9518
+ this.writeEscapedByteDescending(INFINITY);
9519
+ }
9520
+ /**
9521
+ * Resets the buffer such that it is the same as when it was newly
9522
+ * constructed.
9523
+ */
9524
+ reset() {
9525
+ this.position = 0;
9526
+ }
9527
+ seed(encodedBytes) {
9528
+ this.ensureAvailable(encodedBytes.length);
9529
+ this.buffer.set(encodedBytes, this.position);
9530
+ this.position += encodedBytes.length;
9531
+ }
9532
+ /** Makes a copy of the encoded bytes in this buffer. */
9533
+ encodedBytes() {
9534
+ return this.buffer.slice(0, this.position);
9535
+ }
9536
+ /**
9537
+ * Encodes `val` into an encoding so that the order matches the IEEE 754
9538
+ * floating-point comparison results with the following exceptions:
9539
+ * -0.0 < 0.0
9540
+ * all non-NaN < NaN
9541
+ * NaN = NaN
9542
+ */
9543
+ toOrderedBits(val) {
9544
+ const value = doubleToLongBits(val);
9545
+ // Check if the first bit is set. We use a bit mask since value[0] is
9546
+ // encoded as a number from 0 to 255.
9547
+ const isNegative = (value[0] & 0x80) !== 0;
9548
+ // Revert the two complement to get natural ordering
9549
+ value[0] ^= isNegative ? 0xff : 0x80;
9550
+ for (let i = 1; i < value.length; ++i) {
9551
+ value[i] ^= isNegative ? 0xff : 0x00;
9552
+ }
9553
+ return value;
9554
+ }
9555
+ /** Writes a single byte ascending to the buffer. */
9556
+ writeByteAscending(b) {
9557
+ const masked = b & 0xff;
9558
+ if (masked === ESCAPE1) {
9559
+ this.writeEscapedByteAscending(ESCAPE1);
9560
+ this.writeEscapedByteAscending(NULL_BYTE);
9561
+ }
9562
+ else if (masked === ESCAPE2) {
9563
+ this.writeEscapedByteAscending(ESCAPE2);
9564
+ this.writeEscapedByteAscending(FF_BYTE);
9565
+ }
9566
+ else {
9567
+ this.writeEscapedByteAscending(masked);
9568
+ }
9569
+ }
9570
+ /** Writes a single byte descending to the buffer. */
9571
+ writeByteDescending(b) {
9572
+ const masked = b & 0xff;
9573
+ if (masked === ESCAPE1) {
9574
+ this.writeEscapedByteDescending(ESCAPE1);
9575
+ this.writeEscapedByteDescending(NULL_BYTE);
9576
+ }
9577
+ else if (masked === ESCAPE2) {
9578
+ this.writeEscapedByteDescending(ESCAPE2);
9579
+ this.writeEscapedByteDescending(FF_BYTE);
9580
+ }
9581
+ else {
9582
+ this.writeEscapedByteDescending(b);
9583
+ }
9584
+ }
9585
+ writeSeparatorAscending() {
9586
+ this.writeEscapedByteAscending(ESCAPE1);
9587
+ this.writeEscapedByteAscending(SEPARATOR);
9588
+ }
9589
+ writeSeparatorDescending() {
9590
+ this.writeEscapedByteDescending(ESCAPE1);
9591
+ this.writeEscapedByteDescending(SEPARATOR);
9592
+ }
9593
+ writeEscapedByteAscending(b) {
9594
+ this.ensureAvailable(1);
9595
+ this.buffer[this.position++] = b;
9596
+ }
9597
+ writeEscapedByteDescending(b) {
9598
+ this.ensureAvailable(1);
9599
+ this.buffer[this.position++] = ~b;
9600
+ }
9601
+ ensureAvailable(bytes) {
9602
+ const minCapacity = bytes + this.position;
9603
+ if (minCapacity <= this.buffer.length) {
9604
+ return;
9605
+ }
9606
+ // Try doubling.
9607
+ let newLength = this.buffer.length * 2;
9608
+ // Still not big enough? Just allocate the right size.
9609
+ if (newLength < minCapacity) {
9610
+ newLength = minCapacity;
9611
+ }
9612
+ // Create the new buffer.
9613
+ const newBuffer = new Uint8Array(newLength);
9614
+ newBuffer.set(this.buffer); // copy old data
9615
+ this.buffer = newBuffer;
9616
+ }
9617
+ }
9618
+
9619
+ class AscendingIndexByteEncoder {
9620
+ constructor(orderedCode) {
9621
+ this.orderedCode = orderedCode;
9622
+ }
9623
+ writeBytes(value) {
9624
+ this.orderedCode.writeBytesAscending(value);
9625
+ }
9626
+ writeString(value) {
9627
+ this.orderedCode.writeUtf8Ascending(value);
9628
+ }
9629
+ writeNumber(value) {
9630
+ this.orderedCode.writeNumberAscending(value);
9631
+ }
9632
+ writeInfinity() {
9633
+ this.orderedCode.writeInfinityAscending();
9634
+ }
8580
9635
  }
8581
- /** Encodes a NamedQuery proto object to a NamedQuery model object. */
8582
- function fromProtoNamedQuery(namedQuery) {
8583
- return {
8584
- name: namedQuery.name,
8585
- query: fromBundledQuery(namedQuery.bundledQuery),
8586
- readTime: fromVersion(namedQuery.readTime)
8587
- };
9636
+ class DescendingIndexByteEncoder {
9637
+ constructor(orderedCode) {
9638
+ this.orderedCode = orderedCode;
9639
+ }
9640
+ writeBytes(value) {
9641
+ this.orderedCode.writeBytesDescending(value);
9642
+ }
9643
+ writeString(value) {
9644
+ this.orderedCode.writeUtf8Descending(value);
9645
+ }
9646
+ writeNumber(value) {
9647
+ this.orderedCode.writeNumberDescending(value);
9648
+ }
9649
+ writeInfinity() {
9650
+ this.orderedCode.writeInfinityDescending();
9651
+ }
8588
9652
  }
8589
- /** Decodes a BundleMetadata proto into a BundleMetadata object. */
8590
- function fromBundleMetadata(metadata) {
8591
- return {
8592
- id: metadata.id,
8593
- version: metadata.version,
8594
- createTime: fromVersion(metadata.createTime)
8595
- };
9653
+ /**
9654
+ * Implements `DirectionalIndexByteEncoder` using `OrderedCodeWriter` for the
9655
+ * actual encoding.
9656
+ */
9657
+ class IndexByteEncoder {
9658
+ constructor() {
9659
+ this.orderedCode = new OrderedCodeWriter();
9660
+ this.ascending = new AscendingIndexByteEncoder(this.orderedCode);
9661
+ this.descending = new DescendingIndexByteEncoder(this.orderedCode);
9662
+ }
9663
+ seed(encodedBytes) {
9664
+ this.orderedCode.seed(encodedBytes);
9665
+ }
9666
+ forKind(kind) {
9667
+ return kind === 0 /* ASCENDING */ ? this.ascending : this.descending;
9668
+ }
9669
+ encodedBytes() {
9670
+ return this.orderedCode.encodedBytes();
9671
+ }
9672
+ reset() {
9673
+ this.orderedCode.reset();
9674
+ }
8596
9675
  }
8597
9676
 
8598
9677
  /**
8599
9678
  * @license
8600
- * Copyright 2020 Google LLC
9679
+ * Copyright 2022 Google LLC
8601
9680
  *
8602
9681
  * Licensed under the Apache License, Version 2.0 (the "License");
8603
9682
  * you may not use this file except in compliance with the License.
@@ -8611,45 +9690,38 @@ function fromBundleMetadata(metadata) {
8611
9690
  * See the License for the specific language governing permissions and
8612
9691
  * limitations under the License.
8613
9692
  */
8614
- class IndexedDbBundleCache {
8615
- getBundleMetadata(transaction, bundleId) {
8616
- return bundlesStore(transaction)
8617
- .get(bundleId)
8618
- .next(bundle => {
8619
- if (bundle) {
8620
- return fromDbBundle(bundle);
8621
- }
8622
- return undefined;
8623
- });
9693
+ /** Represents an index entry saved by the SDK in persisted storage. */
9694
+ class IndexEntry {
9695
+ constructor(indexId, documentKey, arrayValue, directionalValue) {
9696
+ this.indexId = indexId;
9697
+ this.documentKey = documentKey;
9698
+ this.arrayValue = arrayValue;
9699
+ this.directionalValue = directionalValue;
8624
9700
  }
8625
- saveBundleMetadata(transaction, bundleMetadata) {
8626
- return bundlesStore(transaction).put(toDbBundle(bundleMetadata));
9701
+ }
9702
+ function indexEntryComparator(left, right) {
9703
+ let cmp = left.indexId - right.indexId;
9704
+ if (cmp !== 0) {
9705
+ return cmp;
8627
9706
  }
8628
- getNamedQuery(transaction, queryName) {
8629
- return namedQueriesStore(transaction)
8630
- .get(queryName)
8631
- .next(query => {
8632
- if (query) {
8633
- return fromDbNamedQuery(query);
8634
- }
8635
- return undefined;
8636
- });
9707
+ cmp = DocumentKey.comparator(left.documentKey, right.documentKey);
9708
+ if (cmp !== 0) {
9709
+ return cmp;
8637
9710
  }
8638
- saveNamedQuery(transaction, query) {
8639
- return namedQueriesStore(transaction).put(toDbNamedQuery(query));
9711
+ cmp = compareByteArrays(left.arrayValue, right.arrayValue);
9712
+ if (cmp !== 0) {
9713
+ return cmp;
8640
9714
  }
9715
+ return compareByteArrays(left.directionalValue, right.directionalValue);
8641
9716
  }
8642
- /**
8643
- * Helper to get a typed SimpleDbStore for the bundles object store.
8644
- */
8645
- function bundlesStore(txn) {
8646
- return getStore(txn, DbBundle.store);
8647
- }
8648
- /**
8649
- * Helper to get a typed SimpleDbStore for the namedQueries object store.
8650
- */
8651
- function namedQueriesStore(txn) {
8652
- return getStore(txn, DbNamedQuery.store);
9717
+ function compareByteArrays(left, right) {
9718
+ for (let i = 0; i < left.length && i < right.length; ++i) {
9719
+ const compare = left[i] - right[i];
9720
+ if (compare !== 0) {
9721
+ return compare;
9722
+ }
9723
+ }
9724
+ return left.length - right.length;
8653
9725
  }
8654
9726
 
8655
9727
  /**
@@ -8682,6 +9754,38 @@ class MemoryIndexManager {
8682
9754
  getCollectionParents(transaction, collectionId) {
8683
9755
  return PersistencePromise.resolve(this.collectionParentIndex.getEntries(collectionId));
8684
9756
  }
9757
+ addFieldIndex(transaction, index) {
9758
+ // Field indices are not supported with memory persistence.
9759
+ return PersistencePromise.resolve();
9760
+ }
9761
+ deleteFieldIndex(transaction, index) {
9762
+ // Field indices are not supported with memory persistence.
9763
+ return PersistencePromise.resolve();
9764
+ }
9765
+ getDocumentsMatchingTarget(transaction, fieldIndex, target) {
9766
+ // Field indices are not supported with memory persistence.
9767
+ return PersistencePromise.resolve(documentKeySet());
9768
+ }
9769
+ getFieldIndex(transaction, target) {
9770
+ // Field indices are not supported with memory persistence.
9771
+ return PersistencePromise.resolve(null);
9772
+ }
9773
+ getFieldIndexes(transaction, collectionGroup) {
9774
+ // Field indices are not supported with memory persistence.
9775
+ return PersistencePromise.resolve([]);
9776
+ }
9777
+ getNextCollectionGroupToUpdate(transaction) {
9778
+ // Field indices are not supported with memory persistence.
9779
+ return PersistencePromise.resolve(null);
9780
+ }
9781
+ updateCollectionGroup(transaction, collectionGroup, offset) {
9782
+ // Field indices are not supported with memory persistence.
9783
+ return PersistencePromise.resolve();
9784
+ }
9785
+ updateIndexEntries(transaction, documents) {
9786
+ // Field indices are not supported with memory persistence.
9787
+ return PersistencePromise.resolve();
9788
+ }
8685
9789
  }
8686
9790
  /**
8687
9791
  * Internal implementation of the collection-parent index exposed by MemoryIndexManager.
@@ -8731,19 +9835,25 @@ class MemoryCollectionParentIndex {
8731
9835
  * See the License for the specific language governing permissions and
8732
9836
  * limitations under the License.
8733
9837
  */
9838
+ const LOG_TAG$f = 'IndexedDbIndexManager';
8734
9839
  /**
8735
9840
  * A persisted implementation of IndexManager.
9841
+ *
9842
+ * PORTING NOTE: Unlike iOS and Android, the Web SDK does not memoize index
9843
+ * data as it supports multi-tab access.
8736
9844
  */
8737
9845
  class IndexedDbIndexManager {
8738
- constructor() {
9846
+ constructor(user) {
9847
+ this.user = user;
8739
9848
  /**
8740
9849
  * An in-memory copy of the index entries we've already written since the SDK
8741
9850
  * launched. Used to avoid re-writing the same entry repeatedly.
8742
9851
  *
8743
- * This is *NOT* a complete cache of what's in persistence and so can never be used to
8744
- * satisfy reads.
9852
+ * This is *NOT* a complete cache of what's in persistence and so can never be
9853
+ * used to satisfy reads.
8745
9854
  */
8746
9855
  this.collectionParentsCache = new MemoryCollectionParentIndex();
9856
+ this.uid = user.uid || '';
8747
9857
  }
8748
9858
  /**
8749
9859
  * Adds a new entry to the collection parent index.
@@ -8790,6 +9900,198 @@ class IndexedDbIndexManager {
8790
9900
  return parentPaths;
8791
9901
  });
8792
9902
  }
9903
+ addFieldIndex(transaction, index) {
9904
+ // TODO(indexing): Verify that the auto-incrementing index ID works in
9905
+ // Safari & Firefox.
9906
+ const indexes = indexConfigurationStore(transaction);
9907
+ const dbIndex = toDbIndexConfiguration(index);
9908
+ delete dbIndex.indexId; // `indexId` is auto-populated by IndexedDb
9909
+ return indexes.add(dbIndex).next();
9910
+ }
9911
+ deleteFieldIndex(transaction, index) {
9912
+ const indexes = indexConfigurationStore(transaction);
9913
+ const states = indexStateStore(transaction);
9914
+ const entries = indexEntriesStore(transaction);
9915
+ return indexes
9916
+ .delete(index.indexId)
9917
+ .next(() => states.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
9918
+ /*lowerOpen=*/ false,
9919
+ /*upperOpen=*/ true)))
9920
+ .next(() => entries.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
9921
+ /*lowerOpen=*/ false,
9922
+ /*upperOpen=*/ true)));
9923
+ }
9924
+ getDocumentsMatchingTarget(transaction, fieldIndex, target) {
9925
+ // TODO(indexing): Implement
9926
+ return PersistencePromise.resolve(documentKeySet());
9927
+ }
9928
+ getFieldIndex(transaction, target) {
9929
+ // TODO(indexing): Implement
9930
+ return PersistencePromise.resolve(null);
9931
+ }
9932
+ /**
9933
+ * Returns the byte encoded form of the directional values in the field index.
9934
+ * Returns `null` if the document does not have all fields specified in the
9935
+ * index.
9936
+ */
9937
+ encodeDirectionalElements(fieldIndex, document) {
9938
+ const encoder = new IndexByteEncoder();
9939
+ for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
9940
+ const field = document.data.field(segment.fieldPath);
9941
+ if (field == null) {
9942
+ return null;
9943
+ }
9944
+ const directionalEncoder = encoder.forKind(segment.kind);
9945
+ FirestoreIndexValueWriter.INSTANCE.writeIndexValue(field, directionalEncoder);
9946
+ }
9947
+ return encoder.encodedBytes();
9948
+ }
9949
+ /** Encodes a single value to the ascending index format. */
9950
+ encodeSingleElement(value) {
9951
+ const encoder = new IndexByteEncoder();
9952
+ FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, encoder.forKind(0 /* ASCENDING */));
9953
+ return encoder.encodedBytes();
9954
+ }
9955
+ getFieldIndexes(transaction, collectionGroup) {
9956
+ const indexes = indexConfigurationStore(transaction);
9957
+ const states = indexStateStore(transaction);
9958
+ return (collectionGroup
9959
+ ? indexes.loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
9960
+ : indexes.loadAll()).next(indexConfigs => {
9961
+ const result = [];
9962
+ return PersistencePromise.forEach(indexConfigs, (indexConfig) => {
9963
+ return states
9964
+ .get([indexConfig.indexId, this.uid])
9965
+ .next(indexState => {
9966
+ result.push(fromDbIndexConfiguration(indexConfig, indexState));
9967
+ });
9968
+ }).next(() => result);
9969
+ });
9970
+ }
9971
+ getNextCollectionGroupToUpdate(transaction) {
9972
+ return this.getFieldIndexes(transaction).next(indexes => {
9973
+ if (indexes.length === 0) {
9974
+ return null;
9975
+ }
9976
+ indexes.sort((l, r) => l.indexState.sequenceNumber - r.indexState.sequenceNumber);
9977
+ return indexes[0].collectionGroup;
9978
+ });
9979
+ }
9980
+ updateCollectionGroup(transaction, collectionGroup, offset) {
9981
+ const indexes = indexConfigurationStore(transaction);
9982
+ const states = indexStateStore(transaction);
9983
+ return this.getNextSequenceNumber(transaction).next(nextSequenceNumber => indexes
9984
+ .loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
9985
+ .next(configs => PersistencePromise.forEach(configs, (config) => states.put(toDbIndexState(config.indexId, this.user, nextSequenceNumber, offset)))));
9986
+ }
9987
+ updateIndexEntries(transaction, documents) {
9988
+ // Porting Note: `getFieldIndexes()` on Web does not cache index lookups as
9989
+ // it could be used across different IndexedDB transactions. As any cached
9990
+ // data might be invalidated by other multi-tab clients, we can only trust
9991
+ // data within a single IndexedDB transaction. We therefore add a cache
9992
+ // here.
9993
+ const memoizedIndexes = new Map();
9994
+ return PersistencePromise.forEach(documents, (key, doc) => {
9995
+ const memoizedCollectionIndexes = memoizedIndexes.get(key.collectionGroup);
9996
+ const fieldIndexes = memoizedCollectionIndexes
9997
+ ? PersistencePromise.resolve(memoizedCollectionIndexes)
9998
+ : this.getFieldIndexes(transaction, key.collectionGroup);
9999
+ return fieldIndexes.next(fieldIndexes => {
10000
+ memoizedIndexes.set(key.collectionGroup, fieldIndexes);
10001
+ return PersistencePromise.forEach(fieldIndexes, (fieldIndex) => {
10002
+ return this.getExistingIndexEntries(transaction, key, fieldIndex).next(existingEntries => {
10003
+ const newEntries = this.computeIndexEntries(doc, fieldIndex);
10004
+ if (!existingEntries.isEqual(newEntries)) {
10005
+ return this.updateEntries(transaction, doc, existingEntries, newEntries);
10006
+ }
10007
+ return PersistencePromise.resolve();
10008
+ });
10009
+ });
10010
+ });
10011
+ });
10012
+ }
10013
+ addIndexEntry(transaction, document, indexEntry) {
10014
+ const indexEntries = indexEntriesStore(transaction);
10015
+ return indexEntries.put(new DbIndexEntry(indexEntry.indexId, this.uid, indexEntry.arrayValue, indexEntry.directionalValue, encodeResourcePath(document.key.path)));
10016
+ }
10017
+ deleteIndexEntry(transaction, document, indexEntry) {
10018
+ const indexEntries = indexEntriesStore(transaction);
10019
+ return indexEntries.delete([
10020
+ indexEntry.indexId,
10021
+ this.uid,
10022
+ indexEntry.arrayValue,
10023
+ indexEntry.directionalValue,
10024
+ encodeResourcePath(document.key.path)
10025
+ ]);
10026
+ }
10027
+ getExistingIndexEntries(transaction, documentKey, fieldIndex) {
10028
+ const indexEntries = indexEntriesStore(transaction);
10029
+ let results = new SortedSet(indexEntryComparator);
10030
+ return indexEntries
10031
+ .iterate({
10032
+ index: DbIndexEntry.documentKeyIndex,
10033
+ range: IDBKeyRange.only([
10034
+ fieldIndex.indexId,
10035
+ this.uid,
10036
+ encodeResourcePath(documentKey.path)
10037
+ ])
10038
+ }, (_, entry) => {
10039
+ results = results.add(new IndexEntry(fieldIndex.indexId, documentKey, entry.arrayValue, entry.directionalValue));
10040
+ })
10041
+ .next(() => results);
10042
+ }
10043
+ /** Creates the index entries for the given document. */
10044
+ computeIndexEntries(document, fieldIndex) {
10045
+ let results = new SortedSet(indexEntryComparator);
10046
+ const directionalValue = this.encodeDirectionalElements(fieldIndex, document);
10047
+ if (directionalValue == null) {
10048
+ return results;
10049
+ }
10050
+ const arraySegment = fieldIndexGetArraySegment(fieldIndex);
10051
+ if (arraySegment != null) {
10052
+ const value = document.data.field(arraySegment.fieldPath);
10053
+ if (isArray(value)) {
10054
+ for (const arrayValue of value.arrayValue.values || []) {
10055
+ results = results.add(new IndexEntry(fieldIndex.indexId, document.key, this.encodeSingleElement(arrayValue), directionalValue));
10056
+ }
10057
+ }
10058
+ }
10059
+ else {
10060
+ results = results.add(new IndexEntry(fieldIndex.indexId, document.key, new Uint8Array(), directionalValue));
10061
+ }
10062
+ return results;
10063
+ }
10064
+ /**
10065
+ * Updates the index entries for the provided document by deleting entries
10066
+ * that are no longer referenced in `newEntries` and adding all newly added
10067
+ * entries.
10068
+ */
10069
+ updateEntries(transaction, document, existingEntries, newEntries) {
10070
+ logDebug(LOG_TAG$f, "Updating index entries for document '%s'", document.key);
10071
+ const promises = [];
10072
+ diffSortedSets(existingEntries, newEntries, indexEntryComparator,
10073
+ /* onAdd= */ entry => {
10074
+ promises.push(this.addIndexEntry(transaction, document, entry));
10075
+ },
10076
+ /* onRemove= */ entry => {
10077
+ promises.push(this.deleteIndexEntry(transaction, document, entry));
10078
+ });
10079
+ return PersistencePromise.waitFor(promises);
10080
+ }
10081
+ getNextSequenceNumber(transaction) {
10082
+ let nextSequenceNumber = 1;
10083
+ const states = indexStateStore(transaction);
10084
+ return states
10085
+ .iterate({
10086
+ index: DbIndexState.sequenceNumberIndex,
10087
+ reverse: true,
10088
+ range: IDBKeyRange.upperBound([this.uid, Number.MAX_SAFE_INTEGER])
10089
+ }, (_, state, controller) => {
10090
+ controller.done();
10091
+ nextSequenceNumber = state.sequenceNumber + 1;
10092
+ })
10093
+ .next(() => nextSequenceNumber);
10094
+ }
8793
10095
  }
8794
10096
  /**
8795
10097
  * Helper to get a typed SimpleDbStore for the collectionParents
@@ -8797,6 +10099,24 @@ class IndexedDbIndexManager {
8797
10099
  */
8798
10100
  function collectionParentsStore(txn) {
8799
10101
  return getStore(txn, DbCollectionParent.store);
10102
+ }
10103
+ /**
10104
+ * Helper to get a typed SimpleDbStore for the index entry object store.
10105
+ */
10106
+ function indexEntriesStore(txn) {
10107
+ return getStore(txn, DbIndexEntry.store);
10108
+ }
10109
+ /**
10110
+ * Helper to get a typed SimpleDbStore for the index configuration object store.
10111
+ */
10112
+ function indexConfigurationStore(txn) {
10113
+ return getStore(txn, DbIndexConfiguration.store);
10114
+ }
10115
+ /**
10116
+ * Helper to get a typed SimpleDbStore for the index state object store.
10117
+ */
10118
+ function indexStateStore(txn) {
10119
+ return getStore(txn, DbIndexState.store);
8800
10120
  }
8801
10121
 
8802
10122
  /**
@@ -10266,12 +11586,10 @@ class RemoteDocumentChangeBuffer {
10266
11586
  * `newIndexedDbRemoteDocumentCache()`.
10267
11587
  */
10268
11588
  class IndexedDbRemoteDocumentCacheImpl {
10269
- /**
10270
- * @param serializer - The document serializer.
10271
- * @param indexManager - The query indexes that need to be maintained.
10272
- */
10273
- constructor(serializer, indexManager) {
11589
+ constructor(serializer) {
10274
11590
  this.serializer = serializer;
11591
+ }
11592
+ setIndexManager(indexManager) {
10275
11593
  this.indexManager = indexManager;
10276
11594
  }
10277
11595
  /**
@@ -10393,21 +11711,21 @@ class IndexedDbRemoteDocumentCacheImpl {
10393
11711
  }
10394
11712
  });
10395
11713
  }
10396
- getDocumentsMatchingQuery(transaction, query, sinceReadTime) {
11714
+ getAll(transaction, collection, sinceReadTime) {
10397
11715
  let results = mutableDocumentMap();
10398
- const immediateChildrenPathLength = query.path.length + 1;
11716
+ const immediateChildrenPathLength = collection.length + 1;
10399
11717
  const iterationOptions = {};
10400
11718
  if (sinceReadTime.isEqual(SnapshotVersion.min())) {
10401
11719
  // Documents are ordered by key, so we can use a prefix scan to narrow
10402
11720
  // down the documents we need to match the query against.
10403
- const startKey = query.path.toArray();
11721
+ const startKey = collection.toArray();
10404
11722
  iterationOptions.range = IDBKeyRange.lowerBound(startKey);
10405
11723
  }
10406
11724
  else {
10407
11725
  // Execute an index-free query and filter by read time. This is safe
10408
11726
  // since all document changes to queries that have a
10409
11727
  // lastLimboFreeSnapshotVersion (`sinceReadTime`) have a read time set.
10410
- const collectionKey = query.path.toArray();
11728
+ const collectionKey = collection.toArray();
10411
11729
  const readTimeKey = toDbTimestampKey(sinceReadTime);
10412
11730
  iterationOptions.range = IDBKeyRange.lowerBound([collectionKey, readTimeKey],
10413
11731
  /* open= */ true);
@@ -10424,12 +11742,12 @@ class IndexedDbRemoteDocumentCacheImpl {
10424
11742
  return;
10425
11743
  }
10426
11744
  const document = this.maybeDecodeDocument(DocumentKey.fromSegments(key), dbRemoteDoc);
10427
- if (!query.path.isPrefixOf(document.key.path)) {
10428
- control.done();
10429
- }
10430
- else if (queryMatches(query, document)) {
11745
+ if (collection.isPrefixOf(document.key.path)) {
10431
11746
  results = results.insert(document.key, document);
10432
11747
  }
11748
+ else {
11749
+ control.done();
11750
+ }
10433
11751
  })
10434
11752
  .next(() => results);
10435
11753
  }
@@ -10467,14 +11785,9 @@ class IndexedDbRemoteDocumentCacheImpl {
10467
11785
  return MutableDocument.newInvalidDocument(documentKey);
10468
11786
  }
10469
11787
  }
10470
- /**
10471
- * Creates a new IndexedDbRemoteDocumentCache.
10472
- *
10473
- * @param serializer - The document serializer.
10474
- * @param indexManager - The query indexes that need to be maintained.
10475
- */
10476
- function newIndexedDbRemoteDocumentCache(serializer, indexManager) {
10477
- return new IndexedDbRemoteDocumentCacheImpl(serializer, indexManager);
11788
+ /** Creates a new IndexedDbRemoteDocumentCache. */
11789
+ function newIndexedDbRemoteDocumentCache(serializer) {
11790
+ return new IndexedDbRemoteDocumentCacheImpl(serializer);
10478
11791
  }
10479
11792
  /**
10480
11793
  * Returns the set of documents that have changed since the specified read
@@ -10641,9 +11954,6 @@ class SchemaConverter {
10641
11954
  * and local feature development.
10642
11955
  */
10643
11956
  createOrUpgrade(db, txn, fromVersion, toVersion) {
10644
- hardAssert(fromVersion < toVersion &&
10645
- fromVersion >= 0 &&
10646
- toVersion <= SCHEMA_VERSION);
10647
11957
  const simpleDbTransaction = new SimpleDbTransaction('createOrUpgrade', txn);
10648
11958
  if (fromVersion < 1 && toVersion >= 1) {
10649
11959
  createPrimaryClientStore(db);
@@ -10710,6 +12020,16 @@ class SchemaConverter {
10710
12020
  createNamedQueriesStore(db);
10711
12021
  });
10712
12022
  }
12023
+ if (fromVersion < 12 && toVersion >= 12) {
12024
+ p = p.next(() => {
12025
+ createDocumentOverlayStore(db);
12026
+ });
12027
+ }
12028
+ if (fromVersion < 13 && toVersion >= 13) {
12029
+ p = p.next(() => {
12030
+ createFieldIndex(db);
12031
+ });
12032
+ }
10713
12033
  return p;
10714
12034
  }
10715
12035
  addDocumentGlobal(txn) {
@@ -10916,6 +12236,28 @@ function createNamedQueriesStore(db) {
10916
12236
  db.createObjectStore(DbNamedQuery.store, {
10917
12237
  keyPath: DbNamedQuery.keyPath
10918
12238
  });
12239
+ }
12240
+ function createFieldIndex(db) {
12241
+ const indexConfiguratioStore = db.createObjectStore(DbIndexConfiguration.store, {
12242
+ keyPath: DbIndexConfiguration.keyPath,
12243
+ autoIncrement: true
12244
+ });
12245
+ indexConfiguratioStore.createIndex(DbIndexConfiguration.collectionGroupIndex, DbIndexConfiguration.collectionGroupIndexPath, { unique: false });
12246
+ const indexStateStore = db.createObjectStore(DbIndexState.store, {
12247
+ keyPath: DbIndexState.keyPath
12248
+ });
12249
+ indexStateStore.createIndex(DbIndexState.sequenceNumberIndex, DbIndexState.sequenceNumberIndexPath, { unique: false });
12250
+ const indexEntryStore = db.createObjectStore(DbIndexEntry.store, {
12251
+ keyPath: DbIndexEntry.keyPath
12252
+ });
12253
+ indexEntryStore.createIndex(DbIndexEntry.documentKeyIndex, DbIndexEntry.documentKeyIndexPath, { unique: false });
12254
+ }
12255
+ function createDocumentOverlayStore(db) {
12256
+ const documentOverlayStore = db.createObjectStore(DbDocumentOverlay.store, {
12257
+ keyPath: DbDocumentOverlay.keyPath
12258
+ });
12259
+ documentOverlayStore.createIndex(DbDocumentOverlay.collectionPathOverlayIndex, DbDocumentOverlay.collectionPathOverlayIndexPath, { unique: false });
12260
+ documentOverlayStore.createIndex(DbDocumentOverlay.collectionGroupOverlayIndex, DbDocumentOverlay.collectionGroupOverlayIndexPath, { unique: false });
10919
12261
  }
10920
12262
 
10921
12263
  /**
@@ -11027,7 +12369,7 @@ class IndexedDbPersistence {
11027
12369
  * If set to true, forcefully obtains database access. Existing tabs will
11028
12370
  * no longer be able to access IndexedDB.
11029
12371
  */
11030
- forceOwningTab) {
12372
+ forceOwningTab, schemaVersion = SCHEMA_VERSION) {
11031
12373
  this.allowTabSynchronization = allowTabSynchronization;
11032
12374
  this.persistenceKey = persistenceKey;
11033
12375
  this.clientId = clientId;
@@ -11036,6 +12378,7 @@ class IndexedDbPersistence {
11036
12378
  this.document = document;
11037
12379
  this.sequenceNumberSyncer = sequenceNumberSyncer;
11038
12380
  this.forceOwningTab = forceOwningTab;
12381
+ this.schemaVersion = schemaVersion;
11039
12382
  this.listenSequence = null;
11040
12383
  this._started = false;
11041
12384
  this.isPrimary = false;
@@ -11057,10 +12400,9 @@ class IndexedDbPersistence {
11057
12400
  this.referenceDelegate = new IndexedDbLruDelegateImpl(this, lruParams);
11058
12401
  this.dbName = persistenceKey + MAIN_DATABASE;
11059
12402
  this.serializer = new LocalSerializer(serializer);
11060
- this.simpleDb = new SimpleDb(this.dbName, SCHEMA_VERSION, new SchemaConverter(this.serializer));
12403
+ this.simpleDb = new SimpleDb(this.dbName, this.schemaVersion, new SchemaConverter(this.serializer));
11061
12404
  this.targetCache = new IndexedDbTargetCache(this.referenceDelegate, this.serializer);
11062
- this.indexManager = new IndexedDbIndexManager();
11063
- this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer, this.indexManager);
12405
+ this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer);
11064
12406
  this.bundleCache = new IndexedDbBundleCache();
11065
12407
  if (this.window && this.window.localStorage) {
11066
12408
  this.webStorage = this.window.localStorage;
@@ -11396,8 +12738,8 @@ class IndexedDbPersistence {
11396
12738
  get started() {
11397
12739
  return this._started;
11398
12740
  }
11399
- getMutationQueue(user) {
11400
- return IndexedDbMutationQueue.forUser(user, this.serializer, this.indexManager, this.referenceDelegate);
12741
+ getMutationQueue(user, indexManager) {
12742
+ return IndexedDbMutationQueue.forUser(user, this.serializer, indexManager, this.referenceDelegate);
11401
12743
  }
11402
12744
  getTargetCache() {
11403
12745
  return this.targetCache;
@@ -11405,8 +12747,11 @@ class IndexedDbPersistence {
11405
12747
  getRemoteDocumentCache() {
11406
12748
  return this.remoteDocumentCache;
11407
12749
  }
11408
- getIndexManager() {
11409
- return this.indexManager;
12750
+ getIndexManager(user) {
12751
+ return new IndexedDbIndexManager(user);
12752
+ }
12753
+ getDocumentOverlayCache(user) {
12754
+ return IndexedDbDocumentOverlayCache.forUser(this.serializer, user);
11410
12755
  }
11411
12756
  getBundleCache() {
11412
12757
  return this.bundleCache;
@@ -11414,11 +12759,12 @@ class IndexedDbPersistence {
11414
12759
  runTransaction(action, mode, transactionOperation) {
11415
12760
  logDebug(LOG_TAG$d, 'Starting transaction:', action);
11416
12761
  const simpleDbMode = mode === 'readonly' ? 'readonly' : 'readwrite';
12762
+ const objectStores = getObjectStores(this.schemaVersion);
11417
12763
  let persistenceTransaction;
11418
12764
  // Do all transactions as readwrite against all object stores, since we
11419
12765
  // are the only reader/writer.
11420
12766
  return this.simpleDb
11421
- .runTransaction(action, simpleDbMode, ALL_STORES, simpleDbTxn => {
12767
+ .runTransaction(action, simpleDbMode, objectStores, simpleDbTxn => {
11422
12768
  persistenceTransaction = new IndexedDbTransaction(simpleDbTxn, this.listenSequence
11423
12769
  ? this.listenSequence.next()
11424
12770
  : ListenSequence.INVALID);
@@ -11790,39 +13136,28 @@ class LocalDocumentsView {
11790
13136
  getDocumentsMatchingCollectionQuery(transaction, query, sinceReadTime) {
11791
13137
  // Query the remote documents and overlay mutations.
11792
13138
  let results;
11793
- let mutationBatches;
11794
13139
  return this.remoteDocumentCache
11795
- .getDocumentsMatchingQuery(transaction, query, sinceReadTime)
13140
+ .getAll(transaction, query.path, sinceReadTime)
11796
13141
  .next(queryResults => {
11797
13142
  results = queryResults;
11798
13143
  return this.mutationQueue.getAllMutationBatchesAffectingQuery(transaction, query);
11799
13144
  })
11800
- .next(matchingMutationBatches => {
11801
- mutationBatches = matchingMutationBatches;
11802
- // It is possible that a PatchMutation can make a document match a query, even if
11803
- // the version in the RemoteDocumentCache is not a match yet (waiting for server
11804
- // to ack). To handle this, we find all document keys affected by the PatchMutations
11805
- // that are not in `result` yet, and back fill them via `remoteDocumentCache.getEntries`,
11806
- // otherwise those `PatchMutations` will be ignored because no base document can be found,
11807
- // and lead to missing result for the query.
11808
- return this.addMissingBaseDocuments(transaction, mutationBatches, results).next(mergedDocuments => {
11809
- results = mergedDocuments;
11810
- for (const batch of mutationBatches) {
11811
- for (const mutation of batch.mutations) {
11812
- const key = mutation.key;
11813
- let document = results.get(key);
11814
- if (document == null) {
11815
- // Create invalid document to apply mutations on top of
11816
- document = MutableDocument.newInvalidDocument(key);
11817
- results = results.insert(key, document);
11818
- }
11819
- mutationApplyToLocalView(mutation, document, batch.localWriteTime);
11820
- if (!document.isFoundDocument()) {
11821
- results = results.remove(key);
11822
- }
13145
+ .next(mutationBatches => {
13146
+ for (const batch of mutationBatches) {
13147
+ for (const mutation of batch.mutations) {
13148
+ const key = mutation.key;
13149
+ let document = results.get(key);
13150
+ if (document == null) {
13151
+ // Create invalid document to apply mutations on top of
13152
+ document = MutableDocument.newInvalidDocument(key);
13153
+ results = results.insert(key, document);
13154
+ }
13155
+ mutationApplyToLocalView(mutation, document, batch.localWriteTime);
13156
+ if (!document.isFoundDocument()) {
13157
+ results = results.remove(key);
11823
13158
  }
11824
13159
  }
11825
- });
13160
+ }
11826
13161
  })
11827
13162
  .next(() => {
11828
13163
  // Finally, filter out any documents that don't actually match
@@ -11835,29 +13170,6 @@ class LocalDocumentsView {
11835
13170
  return results;
11836
13171
  });
11837
13172
  }
11838
- addMissingBaseDocuments(transaction, matchingMutationBatches, existingDocuments) {
11839
- let missingBaseDocEntriesForPatching = documentKeySet();
11840
- for (const batch of matchingMutationBatches) {
11841
- for (const mutation of batch.mutations) {
11842
- if (mutation instanceof PatchMutation &&
11843
- existingDocuments.get(mutation.key) === null) {
11844
- missingBaseDocEntriesForPatching =
11845
- missingBaseDocEntriesForPatching.add(mutation.key);
11846
- }
11847
- }
11848
- }
11849
- let mergedDocuments = existingDocuments;
11850
- return this.remoteDocumentCache
11851
- .getEntries(transaction, missingBaseDocEntriesForPatching)
11852
- .next(missingBaseDocs => {
11853
- missingBaseDocs.forEach((key, doc) => {
11854
- if (doc.isFoundDocument()) {
11855
- mergedDocuments = mergedDocuments.insert(key, doc);
11856
- }
11857
- });
11858
- return mergedDocuments;
11859
- });
11860
- }
11861
13173
  }
11862
13174
 
11863
13175
  /**
@@ -11916,11 +13228,18 @@ class LocalStoreImpl {
11916
13228
  * PORTING NOTE: This is only used for multi-tab synchronization.
11917
13229
  */
11918
13230
  this.lastDocumentChangeReadTime = SnapshotVersion.min();
11919
- this.mutationQueue = persistence.getMutationQueue(initialUser);
11920
13231
  this.remoteDocuments = persistence.getRemoteDocumentCache();
11921
13232
  this.targetCache = persistence.getTargetCache();
11922
- this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.persistence.getIndexManager());
11923
13233
  this.bundleCache = persistence.getBundleCache();
13234
+ this.initializeUserComponents(initialUser);
13235
+ }
13236
+ initializeUserComponents(user) {
13237
+ // TODO(indexing): Add spec tests that test these components change after a
13238
+ // user change
13239
+ this.indexManager = this.persistence.getIndexManager(user);
13240
+ this.mutationQueue = this.persistence.getMutationQueue(user, this.indexManager);
13241
+ this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.indexManager);
13242
+ this.remoteDocuments.setIndexManager(this.indexManager);
11924
13243
  this.queryEngine.setLocalDocumentsView(this.localDocuments);
11925
13244
  }
11926
13245
  collectGarbage(garbageCollector) {
@@ -11942,8 +13261,6 @@ persistence, queryEngine, initialUser, serializer) {
11942
13261
  // change.
11943
13262
  async function localStoreHandleUserChange(localStore, user) {
11944
13263
  const localStoreImpl = debugCast(localStore);
11945
- let newMutationQueue = localStoreImpl.mutationQueue;
11946
- let newLocalDocuments = localStoreImpl.localDocuments;
11947
13264
  const result = await localStoreImpl.persistence.runTransaction('Handle user change', 'readonly', txn => {
11948
13265
  // Swap out the mutation queue, grabbing the pending mutation batches
11949
13266
  // before and after.
@@ -11952,11 +13269,8 @@ async function localStoreHandleUserChange(localStore, user) {
11952
13269
  .getAllMutationBatches(txn)
11953
13270
  .next(promisedOldBatches => {
11954
13271
  oldBatches = promisedOldBatches;
11955
- newMutationQueue = localStoreImpl.persistence.getMutationQueue(user);
11956
- // Recreate our LocalDocumentsView using the new
11957
- // MutationQueue.
11958
- newLocalDocuments = new LocalDocumentsView(localStoreImpl.remoteDocuments, newMutationQueue, localStoreImpl.persistence.getIndexManager());
11959
- return newMutationQueue.getAllMutationBatches(txn);
13272
+ localStoreImpl.initializeUserComponents(user);
13273
+ return localStoreImpl.mutationQueue.getAllMutationBatches(txn);
11960
13274
  })
11961
13275
  .next(newBatches => {
11962
13276
  const removedBatchIds = [];
@@ -11977,7 +13291,7 @@ async function localStoreHandleUserChange(localStore, user) {
11977
13291
  }
11978
13292
  // Return the set of all (potentially) changed documents and the list
11979
13293
  // of mutation batch IDs that were affected by change.
11980
- return newLocalDocuments
13294
+ return localStoreImpl.localDocuments
11981
13295
  .getDocuments(txn, changedKeys)
11982
13296
  .next(affectedDocuments => {
11983
13297
  return {
@@ -11988,9 +13302,6 @@ async function localStoreHandleUserChange(localStore, user) {
11988
13302
  });
11989
13303
  });
11990
13304
  });
11991
- localStoreImpl.mutationQueue = newMutationQueue;
11992
- localStoreImpl.localDocuments = newLocalDocuments;
11993
- localStoreImpl.queryEngine.setLocalDocumentsView(localStoreImpl.localDocuments);
11994
13305
  return result;
11995
13306
  }
11996
13307
  /* Accepts locally generated Mutations and commit them to storage. */
@@ -12703,6 +14014,122 @@ class MemoryBundleCache {
12703
14014
  }
12704
14015
  }
12705
14016
 
14017
+ /**
14018
+ * @license
14019
+ * Copyright 2022 Google LLC
14020
+ *
14021
+ * Licensed under the Apache License, Version 2.0 (the "License");
14022
+ * you may not use this file except in compliance with the License.
14023
+ * You may obtain a copy of the License at
14024
+ *
14025
+ * http://www.apache.org/licenses/LICENSE-2.0
14026
+ *
14027
+ * Unless required by applicable law or agreed to in writing, software
14028
+ * distributed under the License is distributed on an "AS IS" BASIS,
14029
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14030
+ * See the License for the specific language governing permissions and
14031
+ * limitations under the License.
14032
+ */
14033
+ /**
14034
+ * An in-memory implementation of DocumentOverlayCache.
14035
+ */
14036
+ class MemoryDocumentOverlayCache {
14037
+ constructor() {
14038
+ // A map sorted by DocumentKey, whose value is a pair of the largest batch id
14039
+ // for the overlay and the overlay itself.
14040
+ this.overlays = new SortedMap(DocumentKey.comparator);
14041
+ this.overlayByBatchId = new Map();
14042
+ }
14043
+ getOverlay(transaction, key) {
14044
+ return PersistencePromise.resolve(this.overlays.get(key));
14045
+ }
14046
+ saveOverlays(transaction, largestBatchId, overlays) {
14047
+ overlays.forEach(mutation => {
14048
+ this.saveOverlay(transaction, largestBatchId, mutation);
14049
+ });
14050
+ return PersistencePromise.resolve();
14051
+ }
14052
+ removeOverlaysForBatchId(transaction, documentKeys, batchId) {
14053
+ const keys = this.overlayByBatchId.get(batchId);
14054
+ if (keys !== undefined) {
14055
+ keys.forEach(key => (this.overlays = this.overlays.remove(key)));
14056
+ this.overlayByBatchId.delete(batchId);
14057
+ }
14058
+ return PersistencePromise.resolve();
14059
+ }
14060
+ getOverlaysForCollection(transaction, collection, sinceBatchId) {
14061
+ const result = new Map();
14062
+ const immediateChildrenPathLength = collection.length + 1;
14063
+ const prefix = new DocumentKey(collection.child(''));
14064
+ const iter = this.overlays.getIteratorFrom(prefix);
14065
+ while (iter.hasNext()) {
14066
+ const entry = iter.getNext();
14067
+ const overlay = entry.value;
14068
+ const key = overlay.getKey();
14069
+ if (!collection.isPrefixOf(key.path)) {
14070
+ break;
14071
+ }
14072
+ // Documents from sub-collections
14073
+ if (key.path.length !== immediateChildrenPathLength) {
14074
+ continue;
14075
+ }
14076
+ if (overlay.largestBatchId > sinceBatchId) {
14077
+ result.set(overlay.getKey(), overlay);
14078
+ }
14079
+ }
14080
+ return PersistencePromise.resolve(result);
14081
+ }
14082
+ getOverlaysForCollectionGroup(transaction, collectionGroup, sinceBatchId, count) {
14083
+ let batchIdToOverlays = new SortedMap((key1, key2) => key1 - key2);
14084
+ const iter = this.overlays.getIterator();
14085
+ while (iter.hasNext()) {
14086
+ const entry = iter.getNext();
14087
+ const overlay = entry.value;
14088
+ const key = overlay.getKey();
14089
+ if (key.getCollectionGroup() !== collectionGroup) {
14090
+ continue;
14091
+ }
14092
+ if (overlay.largestBatchId > sinceBatchId) {
14093
+ let overlaysForBatchId = batchIdToOverlays.get(overlay.largestBatchId);
14094
+ if (overlaysForBatchId === null) {
14095
+ overlaysForBatchId = new Map();
14096
+ batchIdToOverlays = batchIdToOverlays.insert(overlay.largestBatchId, overlaysForBatchId);
14097
+ }
14098
+ overlaysForBatchId.set(overlay.getKey(), overlay);
14099
+ }
14100
+ }
14101
+ const result = new Map();
14102
+ const batchIter = batchIdToOverlays.getIterator();
14103
+ while (batchIter.hasNext()) {
14104
+ const entry = batchIter.getNext();
14105
+ const overlays = entry.value;
14106
+ overlays.forEach((overlay, key) => result.set(key, overlay));
14107
+ if (result.size >= count) {
14108
+ break;
14109
+ }
14110
+ }
14111
+ return PersistencePromise.resolve(result);
14112
+ }
14113
+ saveOverlay(transaction, largestBatchId, mutation) {
14114
+ if (mutation === null) {
14115
+ return;
14116
+ }
14117
+ // Remove the association of the overlay to its batch id.
14118
+ const existing = this.overlays.get(mutation.key);
14119
+ if (existing !== null) {
14120
+ this.overlayByBatchId.get(existing.largestBatchId).delete(mutation.key);
14121
+ }
14122
+ this.overlays = this.overlays.insert(mutation.key, new Overlay(largestBatchId, mutation));
14123
+ // Create the association of this overlay to the given largestBatchId.
14124
+ let batch = this.overlayByBatchId.get(largestBatchId);
14125
+ if (batch === undefined) {
14126
+ batch = new Set();
14127
+ this.overlayByBatchId.set(largestBatchId, batch);
14128
+ }
14129
+ batch.add(mutation.key);
14130
+ }
14131
+ }
14132
+
12706
14133
  /**
12707
14134
  * @license
12708
14135
  * Copyright 2017 Google LLC
@@ -13056,14 +14483,16 @@ class MemoryRemoteDocumentCacheImpl {
13056
14483
  * expected to just return 0 to avoid unnecessarily doing the work of
13057
14484
  * calculating the size.
13058
14485
  */
13059
- constructor(indexManager, sizer) {
13060
- this.indexManager = indexManager;
14486
+ constructor(sizer) {
13061
14487
  this.sizer = sizer;
13062
14488
  /** Underlying cache of documents and their read times. */
13063
14489
  this.docs = documentEntryMap();
13064
14490
  /** Size of all cached documents. */
13065
14491
  this.size = 0;
13066
14492
  }
14493
+ setIndexManager(indexManager) {
14494
+ this.indexManager = indexManager;
14495
+ }
13067
14496
  /**
13068
14497
  * Adds the supplied entry to the cache and updates the cache size as appropriate.
13069
14498
  *
@@ -13111,21 +14540,22 @@ class MemoryRemoteDocumentCacheImpl {
13111
14540
  });
13112
14541
  return PersistencePromise.resolve(results);
13113
14542
  }
13114
- getDocumentsMatchingQuery(transaction, query, sinceReadTime) {
14543
+ getAll(transaction, collectionPath, sinceReadTime) {
13115
14544
  let results = mutableDocumentMap();
13116
14545
  // Documents are ordered by key, so we can use a prefix scan to narrow down
13117
14546
  // the documents we need to match the query against.
13118
- const prefix = new DocumentKey(query.path.child(''));
14547
+ const prefix = new DocumentKey(collectionPath.child(''));
13119
14548
  const iterator = this.docs.getIteratorFrom(prefix);
13120
14549
  while (iterator.hasNext()) {
13121
14550
  const { key, value: { document } } = iterator.getNext();
13122
- if (!query.path.isPrefixOf(key.path)) {
14551
+ if (!collectionPath.isPrefixOf(key.path)) {
13123
14552
  break;
13124
14553
  }
13125
- if (document.readTime.compareTo(sinceReadTime) <= 0) {
14554
+ if (key.path.length > collectionPath.length + 1) {
14555
+ // Exclude entries from subcollections.
13126
14556
  continue;
13127
14557
  }
13128
- if (!queryMatches(query, document)) {
14558
+ if (document.readTime.compareTo(sinceReadTime) <= 0) {
13129
14559
  continue;
13130
14560
  }
13131
14561
  results = results.insert(document.key, document.mutableCopy());
@@ -13147,13 +14577,12 @@ class MemoryRemoteDocumentCacheImpl {
13147
14577
  /**
13148
14578
  * Creates a new memory-only RemoteDocumentCache.
13149
14579
  *
13150
- * @param indexManager - A class that manages collection group indices.
13151
14580
  * @param sizer - Used to assess the size of a document. For eager GC, this is
13152
14581
  * expected to just return 0 to avoid unnecessarily doing the work of
13153
14582
  * calculating the size.
13154
14583
  */
13155
- function newMemoryRemoteDocumentCache(indexManager, sizer) {
13156
- return new MemoryRemoteDocumentCacheImpl(indexManager, sizer);
14584
+ function newMemoryRemoteDocumentCache(sizer) {
14585
+ return new MemoryRemoteDocumentCacheImpl(sizer);
13157
14586
  }
13158
14587
  /**
13159
14588
  * Handles the details of adding and updating documents in the MemoryRemoteDocumentCache.
@@ -13347,6 +14776,7 @@ class MemoryPersistence {
13347
14776
  */
13348
14777
  constructor(referenceDelegateFactory, serializer) {
13349
14778
  this.mutationQueues = {};
14779
+ this.overlays = {};
13350
14780
  this.listenSequence = new ListenSequence(0);
13351
14781
  this._started = false;
13352
14782
  this._started = true;
@@ -13354,7 +14784,7 @@ class MemoryPersistence {
13354
14784
  this.targetCache = new MemoryTargetCache(this);
13355
14785
  const sizer = (doc) => this.referenceDelegate.documentSize(doc);
13356
14786
  this.indexManager = new MemoryIndexManager();
13357
- this.remoteDocumentCache = newMemoryRemoteDocumentCache(this.indexManager, sizer);
14787
+ this.remoteDocumentCache = newMemoryRemoteDocumentCache(sizer);
13358
14788
  this.serializer = new LocalSerializer(serializer);
13359
14789
  this.bundleCache = new MemoryBundleCache(this.serializer);
13360
14790
  }
@@ -13375,13 +14805,23 @@ class MemoryPersistence {
13375
14805
  setNetworkEnabled() {
13376
14806
  // No op.
13377
14807
  }
13378
- getIndexManager() {
14808
+ getIndexManager(user) {
14809
+ // We do not currently support indices for memory persistence, so we can
14810
+ // return the same shared instance of the memory index manager.
13379
14811
  return this.indexManager;
13380
14812
  }
13381
- getMutationQueue(user) {
14813
+ getDocumentOverlayCache(user) {
14814
+ let overlay = this.overlays[user.toKey()];
14815
+ if (!overlay) {
14816
+ overlay = new MemoryDocumentOverlayCache();
14817
+ this.overlays[user.toKey()] = overlay;
14818
+ }
14819
+ return overlay;
14820
+ }
14821
+ getMutationQueue(user, indexManager) {
13382
14822
  let queue = this.mutationQueues[user.toKey()];
13383
14823
  if (!queue) {
13384
- queue = new MemoryMutationQueue(this.indexManager, this.referenceDelegate);
14824
+ queue = new MemoryMutationQueue(indexManager, this.referenceDelegate);
13385
14825
  this.mutationQueues[user.toKey()] = queue;
13386
14826
  }
13387
14827
  return queue;
@@ -20344,12 +21784,12 @@ async function syncEngineListen(syncEngine, query) {
20344
21784
  }
20345
21785
  else {
20346
21786
  const targetData = await localStoreAllocateTarget(syncEngineImpl.localStore, queryToTarget(query));
20347
- const status = syncEngineImpl.sharedClientState.addLocalQueryTarget(targetData.targetId);
20348
- targetId = targetData.targetId;
20349
- viewSnapshot = await initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId, status === 'current');
20350
21787
  if (syncEngineImpl.isPrimaryClient) {
20351
21788
  remoteStoreListen(syncEngineImpl.remoteStore, targetData);
20352
21789
  }
21790
+ const status = syncEngineImpl.sharedClientState.addLocalQueryTarget(targetData.targetId);
21791
+ targetId = targetData.targetId;
21792
+ viewSnapshot = await initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId, status === 'current');
20353
21793
  }
20354
21794
  return viewSnapshot;
20355
21795
  }
@@ -24857,8 +26297,7 @@ class DocumentSnapshot extends DocumentSnapshot$1 {
24857
26297
  this.metadata = metadata;
24858
26298
  }
24859
26299
  /**
24860
- * Property of the `DocumentSnapshot` that signals whether or not the data
24861
- * exists. True if the document exists.
26300
+ * Returns whether or not the data exists. True if the document exists.
24862
26301
  */
24863
26302
  exists() {
24864
26303
  return super.exists();
@@ -25232,51 +26671,53 @@ function limitToLast(limit) {
25232
26671
  return new QueryLimitConstraint('limitToLast', limit, "L" /* Last */);
25233
26672
  }
25234
26673
  class QueryStartAtConstraint extends QueryConstraint {
25235
- constructor(type, _docOrFields, _before) {
26674
+ constructor(type, _docOrFields, _inclusive) {
25236
26675
  super();
25237
26676
  this.type = type;
25238
26677
  this._docOrFields = _docOrFields;
25239
- this._before = _before;
26678
+ this._inclusive = _inclusive;
25240
26679
  }
25241
26680
  _apply(query) {
25242
- const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._before);
26681
+ const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
25243
26682
  return new Query(query.firestore, query.converter, queryWithStartAt(query._query, bound));
25244
26683
  }
25245
26684
  }
25246
26685
  function startAt(...docOrFields) {
25247
- return new QueryStartAtConstraint('startAt', docOrFields, /*before=*/ true);
26686
+ return new QueryStartAtConstraint('startAt', docOrFields,
26687
+ /*inclusive=*/ true);
25248
26688
  }
25249
26689
  function startAfter(...docOrFields) {
25250
26690
  return new QueryStartAtConstraint('startAfter', docOrFields,
25251
- /*before=*/ false);
26691
+ /*inclusive=*/ false);
25252
26692
  }
25253
26693
  class QueryEndAtConstraint extends QueryConstraint {
25254
- constructor(type, _docOrFields, _before) {
26694
+ constructor(type, _docOrFields, _inclusive) {
25255
26695
  super();
25256
26696
  this.type = type;
25257
26697
  this._docOrFields = _docOrFields;
25258
- this._before = _before;
26698
+ this._inclusive = _inclusive;
25259
26699
  }
25260
26700
  _apply(query) {
25261
- const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._before);
26701
+ const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
25262
26702
  return new Query(query.firestore, query.converter, queryWithEndAt(query._query, bound));
25263
26703
  }
25264
26704
  }
25265
26705
  function endBefore(...docOrFields) {
25266
- return new QueryEndAtConstraint('endBefore', docOrFields, /*before=*/ true);
26706
+ return new QueryEndAtConstraint('endBefore', docOrFields,
26707
+ /*inclusive=*/ false);
25267
26708
  }
25268
26709
  function endAt(...docOrFields) {
25269
- return new QueryEndAtConstraint('endAt', docOrFields, /*before=*/ false);
26710
+ return new QueryEndAtConstraint('endAt', docOrFields, /*inclusive=*/ true);
25270
26711
  }
25271
26712
  /** Helper function to create a bound from a document or fields */
25272
- function newQueryBoundFromDocOrFields(query, methodName, docOrFields, before) {
26713
+ function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive) {
25273
26714
  docOrFields[0] = util$1.getModularInstance(docOrFields[0]);
25274
26715
  if (docOrFields[0] instanceof DocumentSnapshot$1) {
25275
- return newQueryBoundFromDocument(query._query, query.firestore._databaseId, methodName, docOrFields[0]._document, before);
26716
+ return newQueryBoundFromDocument(query._query, query.firestore._databaseId, methodName, docOrFields[0]._document, inclusive);
25276
26717
  }
25277
26718
  else {
25278
26719
  const reader = newUserDataReader(query.firestore);
25279
- return newQueryBoundFromFields(query._query, query.firestore._databaseId, reader, methodName, docOrFields, before);
26720
+ return newQueryBoundFromFields(query._query, query.firestore._databaseId, reader, methodName, docOrFields, inclusive);
25280
26721
  }
25281
26722
  }
25282
26723
  function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op, value) {
@@ -25334,7 +26775,7 @@ function newQueryOrderBy(query, fieldPath, direction) {
25334
26775
  * of the query or if any of the fields in the order by are an uncommitted
25335
26776
  * server timestamp.
25336
26777
  */
25337
- function newQueryBoundFromDocument(query, databaseId, methodName, doc, before) {
26778
+ function newQueryBoundFromDocument(query, databaseId, methodName, doc, inclusive) {
25338
26779
  if (!doc) {
25339
26780
  throw new FirestoreError(Code.NOT_FOUND, `Can't use a DocumentSnapshot that doesn't exist for ` +
25340
26781
  `${methodName}().`);
@@ -25371,12 +26812,12 @@ function newQueryBoundFromDocument(query, databaseId, methodName, doc, before) {
25371
26812
  }
25372
26813
  }
25373
26814
  }
25374
- return new Bound(components, before);
26815
+ return new Bound(components, inclusive);
25375
26816
  }
25376
26817
  /**
25377
26818
  * Converts a list of field values to a `Bound` for the given query.
25378
26819
  */
25379
- function newQueryBoundFromFields(query, databaseId, dataReader, methodName, values, before) {
26820
+ function newQueryBoundFromFields(query, databaseId, dataReader, methodName, values, inclusive) {
25380
26821
  // Use explicit order by's because it has to match the query the user made
25381
26822
  const orderBy = query.explicitOrderBy;
25382
26823
  if (values.length > orderBy.length) {
@@ -25413,7 +26854,7 @@ function newQueryBoundFromFields(query, databaseId, dataReader, methodName, valu
25413
26854
  components.push(wrapped);
25414
26855
  }
25415
26856
  }
25416
- return new Bound(components, before);
26857
+ return new Bound(components, inclusive);
25417
26858
  }
25418
26859
  /**
25419
26860
  * Parses the given `documentIdValue` into a `ReferenceValue`, throwing