@firebase/firestore 3.4.4-canary.2dd99ae2d → 3.4.4-canary.307d019c7

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 (180) 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/platform/node/grpc_connection.d.ts +2 -2
  28. package/dist/firestore/src/platform/node/load_protos.d.ts +2 -2
  29. package/dist/firestore/src/util/sorted_set.d.ts +18 -0
  30. package/dist/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  31. package/dist/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
  32. package/dist/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  33. package/dist/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
  34. package/dist/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  35. package/dist/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  36. package/dist/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  37. package/dist/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  38. package/dist/firestore/test/util/helpers.d.ts +1 -1
  39. package/dist/index.d.ts +1 -2
  40. package/dist/index.esm2017.js +4496 -3484
  41. package/dist/index.esm2017.js.map +1 -1
  42. package/dist/index.esm5.js +4452 -3556
  43. package/dist/index.esm5.js.map +1 -1
  44. package/dist/index.node.cjs.js +1757 -295
  45. package/dist/index.node.cjs.js.map +1 -1
  46. package/dist/index.node.mjs +1737 -296
  47. package/dist/index.node.mjs.map +1 -1
  48. package/dist/index.rn.js +4508 -3496
  49. package/dist/index.rn.js.map +1 -1
  50. package/dist/internal.d.ts +253 -14
  51. package/dist/lite/firestore/src/api/credentials.d.ts +1 -0
  52. package/dist/lite/firestore/src/api/snapshot.d.ts +1 -2
  53. package/dist/lite/firestore/src/core/target.d.ts +8 -4
  54. package/dist/lite/firestore/src/index/index_byte_encoder.d.ts +18 -1
  55. package/dist/lite/firestore/src/index/index_entry.d.ts +26 -0
  56. package/dist/lite/firestore/src/lite-api/query.d.ts +2 -2
  57. package/dist/lite/firestore/src/local/document_overlay_cache.d.ts +72 -0
  58. package/dist/lite/firestore/src/local/index_manager.d.ts +52 -0
  59. package/dist/lite/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
  60. package/dist/lite/firestore/src/local/indexeddb_index_manager.d.ts +40 -2
  61. package/dist/lite/firestore/src/local/indexeddb_persistence.d.ts +6 -4
  62. package/dist/lite/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  63. package/dist/lite/firestore/src/local/indexeddb_schema.d.ts +195 -7
  64. package/dist/lite/firestore/src/local/local_documents_view.d.ts +0 -1
  65. package/dist/lite/firestore/src/local/local_serializer.d.ts +18 -2
  66. package/dist/lite/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
  67. package/dist/lite/firestore/src/local/memory_index_manager.d.ts +11 -0
  68. package/dist/lite/firestore/src/local/memory_persistence.d.ts +6 -2
  69. package/dist/lite/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  70. package/dist/lite/firestore/src/local/persistence.d.ts +8 -2
  71. package/dist/lite/firestore/src/local/remote_document_cache.d.ts +7 -9
  72. package/dist/lite/firestore/src/local/simple_db.d.ts +1 -0
  73. package/dist/lite/firestore/src/model/document_key.d.ts +5 -0
  74. package/dist/lite/firestore/src/model/field_index.d.ts +2 -2
  75. package/dist/lite/firestore/src/model/overlay.d.ts +32 -0
  76. package/dist/lite/firestore/src/model/target_index_matcher.d.ts +70 -0
  77. package/dist/lite/firestore/src/platform/node/grpc_connection.d.ts +2 -2
  78. package/dist/lite/firestore/src/platform/node/load_protos.d.ts +2 -2
  79. package/dist/lite/firestore/src/util/sorted_set.d.ts +18 -0
  80. package/dist/lite/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  81. package/dist/lite/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
  82. package/dist/lite/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  83. package/dist/lite/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
  84. package/dist/lite/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  85. package/dist/lite/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  86. package/dist/lite/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  87. package/dist/lite/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  88. package/dist/lite/firestore/test/util/helpers.d.ts +1 -1
  89. package/dist/lite/index.browser.esm2017.js +1155 -1143
  90. package/dist/lite/index.browser.esm2017.js.map +1 -1
  91. package/dist/lite/index.browser.esm5.js +355 -342
  92. package/dist/lite/index.browser.esm5.js.map +1 -1
  93. package/dist/lite/index.node.cjs.js +47 -29
  94. package/dist/lite/index.node.cjs.js.map +1 -1
  95. package/dist/lite/index.node.mjs +47 -29
  96. package/dist/lite/index.node.mjs.map +1 -1
  97. package/dist/lite/index.rn.esm2017.js +332 -320
  98. package/dist/lite/index.rn.esm2017.js.map +1 -1
  99. package/dist/lite/internal.d.ts +7 -2
  100. package/dist/lite/packages/firestore/dist/lite/index.browser.esm2017.d.ts +121 -121
  101. package/dist/lite/packages/firestore/src/api/credentials.d.ts +1 -0
  102. package/dist/lite/packages/firestore/src/api/snapshot.d.ts +1 -2
  103. package/dist/lite/packages/firestore/src/core/target.d.ts +8 -4
  104. package/dist/lite/packages/firestore/src/index/index_byte_encoder.d.ts +18 -1
  105. package/dist/lite/packages/firestore/src/index/index_entry.d.ts +26 -0
  106. package/dist/lite/packages/firestore/src/lite-api/query.d.ts +2 -2
  107. package/dist/lite/packages/firestore/src/local/document_overlay_cache.d.ts +72 -0
  108. package/dist/lite/packages/firestore/src/local/index_manager.d.ts +52 -0
  109. package/dist/lite/packages/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
  110. package/dist/lite/packages/firestore/src/local/indexeddb_index_manager.d.ts +40 -2
  111. package/dist/lite/packages/firestore/src/local/indexeddb_persistence.d.ts +6 -4
  112. package/dist/lite/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  113. package/dist/lite/packages/firestore/src/local/indexeddb_schema.d.ts +195 -7
  114. package/dist/lite/packages/firestore/src/local/local_documents_view.d.ts +0 -1
  115. package/dist/lite/packages/firestore/src/local/local_serializer.d.ts +18 -2
  116. package/dist/lite/packages/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
  117. package/dist/lite/packages/firestore/src/local/memory_index_manager.d.ts +11 -0
  118. package/dist/lite/packages/firestore/src/local/memory_persistence.d.ts +6 -2
  119. package/dist/lite/packages/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  120. package/dist/lite/packages/firestore/src/local/persistence.d.ts +8 -2
  121. package/dist/lite/packages/firestore/src/local/remote_document_cache.d.ts +7 -9
  122. package/dist/lite/packages/firestore/src/local/simple_db.d.ts +1 -0
  123. package/dist/lite/packages/firestore/src/model/document_key.d.ts +5 -0
  124. package/dist/lite/packages/firestore/src/model/field_index.d.ts +2 -2
  125. package/dist/lite/packages/firestore/src/model/overlay.d.ts +32 -0
  126. package/dist/lite/packages/firestore/src/model/target_index_matcher.d.ts +70 -0
  127. package/dist/lite/packages/firestore/src/platform/node/grpc_connection.d.ts +2 -2
  128. package/dist/lite/packages/firestore/src/platform/node/load_protos.d.ts +2 -2
  129. package/dist/lite/packages/firestore/src/util/sorted_set.d.ts +18 -0
  130. package/dist/lite/packages/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  131. package/dist/lite/packages/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
  132. package/dist/lite/packages/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  133. package/dist/lite/packages/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
  134. package/dist/lite/packages/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  135. package/dist/lite/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  136. package/dist/lite/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  137. package/dist/lite/packages/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  138. package/dist/lite/packages/firestore/test/util/helpers.d.ts +1 -1
  139. package/dist/lite/private.d.ts +2 -2
  140. package/dist/packages/firestore/dist/index.esm2017.d.ts +189 -186
  141. package/dist/packages/firestore/src/api/credentials.d.ts +1 -0
  142. package/dist/packages/firestore/src/api/snapshot.d.ts +1 -2
  143. package/dist/packages/firestore/src/core/target.d.ts +8 -4
  144. package/dist/packages/firestore/src/index/index_byte_encoder.d.ts +18 -1
  145. package/dist/packages/firestore/src/index/index_entry.d.ts +26 -0
  146. package/dist/packages/firestore/src/lite-api/query.d.ts +2 -2
  147. package/dist/packages/firestore/src/local/document_overlay_cache.d.ts +72 -0
  148. package/dist/packages/firestore/src/local/index_manager.d.ts +52 -0
  149. package/dist/packages/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
  150. package/dist/packages/firestore/src/local/indexeddb_index_manager.d.ts +40 -2
  151. package/dist/packages/firestore/src/local/indexeddb_persistence.d.ts +6 -4
  152. package/dist/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  153. package/dist/packages/firestore/src/local/indexeddb_schema.d.ts +195 -7
  154. package/dist/packages/firestore/src/local/local_documents_view.d.ts +0 -1
  155. package/dist/packages/firestore/src/local/local_serializer.d.ts +18 -2
  156. package/dist/packages/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
  157. package/dist/packages/firestore/src/local/memory_index_manager.d.ts +11 -0
  158. package/dist/packages/firestore/src/local/memory_persistence.d.ts +6 -2
  159. package/dist/packages/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  160. package/dist/packages/firestore/src/local/persistence.d.ts +8 -2
  161. package/dist/packages/firestore/src/local/remote_document_cache.d.ts +7 -9
  162. package/dist/packages/firestore/src/local/simple_db.d.ts +1 -0
  163. package/dist/packages/firestore/src/model/document_key.d.ts +5 -0
  164. package/dist/packages/firestore/src/model/field_index.d.ts +2 -2
  165. package/dist/packages/firestore/src/model/overlay.d.ts +32 -0
  166. package/dist/packages/firestore/src/model/target_index_matcher.d.ts +70 -0
  167. package/dist/packages/firestore/src/platform/node/grpc_connection.d.ts +2 -2
  168. package/dist/packages/firestore/src/platform/node/load_protos.d.ts +2 -2
  169. package/dist/packages/firestore/src/util/sorted_set.d.ts +18 -0
  170. package/dist/packages/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  171. package/dist/packages/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
  172. package/dist/packages/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  173. package/dist/packages/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
  174. package/dist/packages/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  175. package/dist/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  176. package/dist/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  177. package/dist/packages/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  178. package/dist/packages/firestore/test/util/helpers.d.ts +1 -1
  179. package/dist/private.d.ts +248 -14
  180. package/package.json +9 -9
@@ -8,11 +8,32 @@ var logger = require('@firebase/logger');
8
8
  var util = require('util');
9
9
  var util$1 = require('@firebase/util');
10
10
  var crypto = require('crypto');
11
- var grpcJs = require('@grpc/grpc-js');
11
+ var grpc = require('@grpc/grpc-js');
12
12
  var protoLoader = require('@grpc/proto-loader');
13
13
 
14
+ function _interopNamespace(e) {
15
+ if (e && e.__esModule) return e;
16
+ var n = Object.create(null);
17
+ if (e) {
18
+ Object.keys(e).forEach(function (k) {
19
+ if (k !== 'default') {
20
+ var d = Object.getOwnPropertyDescriptor(e, k);
21
+ Object.defineProperty(n, k, d.get ? d : {
22
+ enumerable: true,
23
+ get: function () { return e[k]; }
24
+ });
25
+ }
26
+ });
27
+ }
28
+ n["default"] = e;
29
+ return Object.freeze(n);
30
+ }
31
+
32
+ var grpc__namespace = /*#__PURE__*/_interopNamespace(grpc);
33
+ var protoLoader__namespace = /*#__PURE__*/_interopNamespace(protoLoader);
34
+
14
35
  const name = "@firebase/firestore";
15
- const version$1 = "3.4.4-canary.2dd99ae2d";
36
+ const version$1 = "3.4.4-canary.307d019c7";
16
37
 
17
38
  /**
18
39
  * @license
@@ -65,7 +86,7 @@ User.GOOGLE_CREDENTIALS = new User('google-credentials-uid');
65
86
  User.FIRST_PARTY = new User('first-party-uid');
66
87
  User.MOCK_USER = new User('mock-user');
67
88
 
68
- const version = "9.6.5-canary.2dd99ae2d";
89
+ const version = "9.6.6-canary.307d019c7";
69
90
 
70
91
  /**
71
92
  * @license
@@ -670,13 +691,19 @@ class FirebaseAppCheckTokenProvider {
670
691
  this.appCheckProvider = appCheckProvider;
671
692
  this.forceRefresh = false;
672
693
  this.appCheck = null;
694
+ this.latestAppCheckToken = null;
673
695
  }
674
696
  start(asyncQueue, changeListener) {
675
697
  const onTokenChanged = tokenResult => {
676
698
  if (tokenResult.error != null) {
677
699
  logDebug('FirebaseAppCheckTokenProvider', `Error getting App Check token; using placeholder token instead. Error: ${tokenResult.error.message}`);
678
700
  }
679
- return changeListener(tokenResult.token);
701
+ const tokenUpdated = tokenResult.token !== this.latestAppCheckToken;
702
+ this.latestAppCheckToken = tokenResult.token;
703
+ logDebug('FirebaseAppCheckTokenProvider', `Received ${tokenUpdated ? 'new' : 'existing'} token.`);
704
+ return tokenUpdated
705
+ ? changeListener(tokenResult.token)
706
+ : Promise.resolve();
680
707
  };
681
708
  this.tokenListener = (tokenResult) => {
682
709
  asyncQueue.enqueueRetryable(() => onTokenChanged(tokenResult));
@@ -711,6 +738,7 @@ class FirebaseAppCheckTokenProvider {
711
738
  return this.appCheck.getToken(forceRefresh).then(tokenResult => {
712
739
  if (tokenResult) {
713
740
  hardAssert(typeof tokenResult.token === 'string');
741
+ this.latestAppCheckToken = tokenResult.token;
714
742
  return new AppCheckToken(tokenResult.token);
715
743
  }
716
744
  else {
@@ -1196,23 +1224,6 @@ function decodeResourcePath(path) {
1196
1224
  }
1197
1225
  return new ResourcePath(segments);
1198
1226
  }
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
1227
  /**
1217
1228
  * Schema Version for the Web client:
1218
1229
  * 1. Initial version including Mutation Queue, Query Cache, and Remote
@@ -1231,8 +1242,10 @@ function decodeResourcePath(path) {
1231
1242
  * an auto-incrementing ID. This is required for Index-Free queries.
1232
1243
  * 10. Rewrite the canonical IDs to the explicit Protobuf-based format.
1233
1244
  * 11. Add bundles and named_queries for bundle support.
1245
+ * 12. Add document overlays.
1246
+ * 13. Add indexing support.
1234
1247
  */
1235
- const SCHEMA_VERSION = 11;
1248
+ const SCHEMA_VERSION = 12;
1236
1249
  /**
1237
1250
  * Wrapper class to store timestamps (seconds and nanos) in IndexedDb objects.
1238
1251
  */
@@ -1757,9 +1770,7 @@ class DbClientMetadata {
1757
1770
  DbClientMetadata.store = 'clientMetadata';
1758
1771
  /** Keys are automatically assigned via the clientId properties. */
1759
1772
  DbClientMetadata.keyPath = 'clientId';
1760
- /**
1761
- * A object representing a bundle loaded by the SDK.
1762
- */
1773
+ /** An object representing a bundle loaded by the SDK. */
1763
1774
  class DbBundle {
1764
1775
  constructor(
1765
1776
  /** The ID of the loaded bundle. */
@@ -1776,9 +1787,7 @@ class DbBundle {
1776
1787
  /** Name of the IndexedDb object store. */
1777
1788
  DbBundle.store = 'bundles';
1778
1789
  DbBundle.keyPath = 'bundleId';
1779
- /**
1780
- * A object representing a named query loaded by the SDK via a bundle.
1781
- */
1790
+ /** An object representing a named query loaded by the SDK via a bundle. */
1782
1791
  class DbNamedQuery {
1783
1792
  constructor(
1784
1793
  /** The name of the query. */
@@ -1795,6 +1804,158 @@ class DbNamedQuery {
1795
1804
  /** Name of the IndexedDb object store. */
1796
1805
  DbNamedQuery.store = 'namedQueries';
1797
1806
  DbNamedQuery.keyPath = 'name';
1807
+ /** An object representing the global configuration for a field index. */
1808
+ class DbIndexConfiguration {
1809
+ constructor(
1810
+ /**
1811
+ * The index id for this entry. Undefined for indexes that are not yet
1812
+ * persisted.
1813
+ */
1814
+ indexId,
1815
+ /** The collection group this index belongs to. */
1816
+ collectionGroup,
1817
+ /** The fields to index for this index. */
1818
+ fields) {
1819
+ this.indexId = indexId;
1820
+ this.collectionGroup = collectionGroup;
1821
+ this.fields = fields;
1822
+ }
1823
+ }
1824
+ /** Name of the IndexedDb object store. */
1825
+ DbIndexConfiguration.store = 'indexConfiguration';
1826
+ DbIndexConfiguration.keyPath = 'indexId';
1827
+ /**
1828
+ * An index that provides access to the index configurations by collection
1829
+ * group.
1830
+ *
1831
+ * PORTING NOTE: iOS and Android maintain this index in-memory, but this is
1832
+ * not possible here as the Web client supports concurrent access to
1833
+ * persistence via multi-tab.
1834
+ */
1835
+ DbIndexConfiguration.collectionGroupIndex = 'collectionGroupIndex';
1836
+ DbIndexConfiguration.collectionGroupIndexPath = 'collectionGroup';
1837
+ /**
1838
+ * An object describing how up-to-date the index backfill is for each user and
1839
+ * index.
1840
+ */
1841
+ class DbIndexState {
1842
+ constructor(
1843
+ /** The index id for this entry. */
1844
+ indexId,
1845
+ /** The user id for this entry. */
1846
+ uid,
1847
+ /**
1848
+ * A number that indicates when the index was last updated (relative to
1849
+ * other indexes).
1850
+ */
1851
+ sequenceNumber,
1852
+ /**
1853
+ * The latest read time that has been indexed by Firestore for this field
1854
+ * index. Set to `{seconds: 0, nanos: 0}` if no documents have been indexed.
1855
+ */
1856
+ readTime,
1857
+ /**
1858
+ * The last document that has been indexed for this field index. Empty if
1859
+ * no documents have been indexed.
1860
+ */
1861
+ documentKey,
1862
+ /**
1863
+ * The largest mutation batch id that has been processed for this index. -1
1864
+ * if no mutations have been indexed.
1865
+ */
1866
+ largestBatchId) {
1867
+ this.indexId = indexId;
1868
+ this.uid = uid;
1869
+ this.sequenceNumber = sequenceNumber;
1870
+ this.readTime = readTime;
1871
+ this.documentKey = documentKey;
1872
+ this.largestBatchId = largestBatchId;
1873
+ }
1874
+ }
1875
+ /** Name of the IndexedDb object store. */
1876
+ DbIndexState.store = 'indexState';
1877
+ DbIndexState.keyPath = ['indexId', 'uid'];
1878
+ /**
1879
+ * An index that provides access to documents in a collection sorted by last
1880
+ * update time. Used by the backfiller.
1881
+ *
1882
+ * PORTING NOTE: iOS and Android maintain this index in-memory, but this is
1883
+ * not possible here as the Web client supports concurrent access to
1884
+ * persistence via multi-tab.
1885
+ */
1886
+ DbIndexState.sequenceNumberIndex = 'sequenceNumberIndex';
1887
+ DbIndexState.sequenceNumberIndexPath = ['uid', 'sequenceNumber'];
1888
+ /** An object that stores the encoded entries for all documents and fields. */
1889
+ class DbIndexEntry {
1890
+ constructor(
1891
+ /** The index id for this entry. */
1892
+ indexId,
1893
+ /** The user id for this entry. */
1894
+ uid,
1895
+ /** The encoded array index value for this entry. */
1896
+ arrayValue,
1897
+ /** The encoded directional value for equality and inequality filters. */
1898
+ directionalValue,
1899
+ /** The document key this entry points to. */
1900
+ documentKey) {
1901
+ this.indexId = indexId;
1902
+ this.uid = uid;
1903
+ this.arrayValue = arrayValue;
1904
+ this.directionalValue = directionalValue;
1905
+ this.documentKey = documentKey;
1906
+ }
1907
+ }
1908
+ /** Name of the IndexedDb object store. */
1909
+ DbIndexEntry.store = 'indexEntries';
1910
+ DbIndexEntry.keyPath = [
1911
+ 'indexId',
1912
+ 'uid',
1913
+ 'arrayValue',
1914
+ 'directionalValue',
1915
+ 'documentKey'
1916
+ ];
1917
+ DbIndexEntry.documentKeyIndex = 'documentKeyIndex';
1918
+ DbIndexEntry.documentKeyIndexPath = ['indexId', 'uid', 'documentKey'];
1919
+ /**
1920
+ * An object representing a document overlay.
1921
+ */
1922
+ class DbDocumentOverlay {
1923
+ constructor(
1924
+ /** The user ID to whom this overlay belongs. */
1925
+ userId,
1926
+ /** The path to the collection that contains the document. */
1927
+ collectionPath,
1928
+ /** The ID (key) of the document within the collection. */
1929
+ documentId,
1930
+ /** The collection group to which the document belongs. */
1931
+ collectionGroup,
1932
+ /** The largest batch ID that's been applied for this overlay. */
1933
+ largestBatchId,
1934
+ /** The overlay mutation. */
1935
+ overlayMutation) {
1936
+ this.userId = userId;
1937
+ this.collectionPath = collectionPath;
1938
+ this.documentId = documentId;
1939
+ this.collectionGroup = collectionGroup;
1940
+ this.largestBatchId = largestBatchId;
1941
+ this.overlayMutation = overlayMutation;
1942
+ }
1943
+ }
1944
+ /** Name of the IndexedDb object store. */
1945
+ DbDocumentOverlay.store = 'documentOverlays';
1946
+ DbDocumentOverlay.keyPath = ['userId', 'collectionPath', 'documentId'];
1947
+ DbDocumentOverlay.collectionPathOverlayIndex = 'collectionPathOverlayIndex';
1948
+ DbDocumentOverlay.collectionPathOverlayIndexPath = [
1949
+ 'userId',
1950
+ 'collectionPath',
1951
+ 'largestBatchId'
1952
+ ];
1953
+ DbDocumentOverlay.collectionGroupOverlayIndex = 'collectionGroupOverlayIndex';
1954
+ DbDocumentOverlay.collectionGroupOverlayIndexPath = [
1955
+ 'userId',
1956
+ 'collectionGroup',
1957
+ 'largestBatchId'
1958
+ ];
1798
1959
  // Visible for testing
1799
1960
  const V1_STORES = [
1800
1961
  DbMutationQueue.store,
@@ -1809,7 +1970,6 @@ const V1_STORES = [
1809
1970
  // V2 is no longer usable (see comment at top of file)
1810
1971
  // Visible for testing
1811
1972
  const V3_STORES = V1_STORES;
1812
- // Visible for testing
1813
1973
  // Note: DbRemoteDocumentChanges is no longer used and dropped with v9.
1814
1974
  const V4_STORES = [...V3_STORES, DbClientMetadata.store];
1815
1975
  // V5 does not change the set of stores.
@@ -1819,12 +1979,28 @@ const V8_STORES = [...V6_STORES, DbCollectionParent.store];
1819
1979
  // V9 does not change the set of stores.
1820
1980
  // V10 does not change the set of stores.
1821
1981
  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;
1982
+ const V12_STORES = [...V11_STORES, DbDocumentOverlay.store];
1983
+ const V13_STORES = [
1984
+ ...V12_STORES,
1985
+ DbIndexConfiguration.store,
1986
+ DbIndexState.store,
1987
+ DbIndexEntry.store
1988
+ ];
1989
+ /** Returns the object stores for the provided schema. */
1990
+ function getObjectStores(schemaVersion) {
1991
+ if (schemaVersion === 13) {
1992
+ return V13_STORES;
1993
+ }
1994
+ else if (schemaVersion === 12) {
1995
+ return V12_STORES;
1996
+ }
1997
+ else if (schemaVersion === 11) {
1998
+ return V11_STORES;
1999
+ }
2000
+ else {
2001
+ fail();
2002
+ }
2003
+ }
1828
2004
 
1829
2005
  /**
1830
2006
  * @license
@@ -2065,7 +2241,7 @@ class PersistencePromise {
2065
2241
  */
2066
2242
  // References to `window` are guarded by SimpleDb.isAvailable()
2067
2243
  /* eslint-disable no-restricted-globals */
2068
- const LOG_TAG$g = 'SimpleDb';
2244
+ const LOG_TAG$h = 'SimpleDb';
2069
2245
  /**
2070
2246
  * The maximum number of retry attempts for an IndexedDb transaction that fails
2071
2247
  * with a DOMException.
@@ -2116,11 +2292,20 @@ class SimpleDbTransaction {
2116
2292
  this.completionDeferred.reject(error);
2117
2293
  }
2118
2294
  if (!this.aborted) {
2119
- logDebug(LOG_TAG$g, 'Aborting transaction:', error ? error.message : 'Client-initiated abort');
2295
+ logDebug(LOG_TAG$h, 'Aborting transaction:', error ? error.message : 'Client-initiated abort');
2120
2296
  this.aborted = true;
2121
2297
  this.transaction.abort();
2122
2298
  }
2123
2299
  }
2300
+ maybeCommit() {
2301
+ // If the browser supports V3 IndexedDB, we invoke commit() explicitly to
2302
+ // speed up index DB processing if the event loop remains blocks.
2303
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2304
+ const maybeV3IndexedDb = this.transaction;
2305
+ if (!this.aborted && typeof maybeV3IndexedDb.commit === 'function') {
2306
+ maybeV3IndexedDb.commit();
2307
+ }
2308
+ }
2124
2309
  /**
2125
2310
  * Returns a SimpleDbStore<KeyType, ValueType> for the specified store. All
2126
2311
  * operations performed on the SimpleDbStore happen within the context of this
@@ -2170,7 +2355,7 @@ class SimpleDb {
2170
2355
  }
2171
2356
  /** Deletes the specified database. */
2172
2357
  static delete(name) {
2173
- logDebug(LOG_TAG$g, 'Removing database:', name);
2358
+ logDebug(LOG_TAG$h, 'Removing database:', name);
2174
2359
  return wrapRequest(window.indexedDB.deleteDatabase(name)).toPromise();
2175
2360
  }
2176
2361
  /** Returns true if IndexedDB is available in the current environment. */
@@ -2249,7 +2434,7 @@ class SimpleDb {
2249
2434
  */
2250
2435
  async ensureDb(action) {
2251
2436
  if (!this.db) {
2252
- logDebug(LOG_TAG$g, 'Opening database:', this.name);
2437
+ logDebug(LOG_TAG$h, 'Opening database:', this.name);
2253
2438
  this.db = await new Promise((resolve, reject) => {
2254
2439
  // TODO(mikelehen): Investigate browser compatibility.
2255
2440
  // https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB
@@ -2285,12 +2470,12 @@ class SimpleDb {
2285
2470
  }
2286
2471
  };
2287
2472
  request.onupgradeneeded = (event) => {
2288
- logDebug(LOG_TAG$g, 'Database "' + this.name + '" requires upgrade from version:', event.oldVersion);
2473
+ logDebug(LOG_TAG$h, 'Database "' + this.name + '" requires upgrade from version:', event.oldVersion);
2289
2474
  const db = event.target.result;
2290
2475
  this.schemaConverter
2291
2476
  .createOrUpgrade(db, request.transaction, event.oldVersion, this.version)
2292
2477
  .next(() => {
2293
- logDebug(LOG_TAG$g, 'Database upgrade to version ' + this.version + ' complete');
2478
+ logDebug(LOG_TAG$h, 'Database upgrade to version ' + this.version + ' complete');
2294
2479
  });
2295
2480
  };
2296
2481
  });
@@ -2317,6 +2502,10 @@ class SimpleDb {
2317
2502
  this.db = await this.ensureDb(action);
2318
2503
  const transaction = SimpleDbTransaction.open(this.db, action, readonly ? 'readonly' : 'readwrite', objectStores);
2319
2504
  const transactionFnResult = transactionFn(transaction)
2505
+ .next(result => {
2506
+ transaction.maybeCommit();
2507
+ return result;
2508
+ })
2320
2509
  .catch(error => {
2321
2510
  // Abort the transaction if there was an error.
2322
2511
  transaction.abort(error);
@@ -2344,7 +2533,7 @@ class SimpleDb {
2344
2533
  // exception is wrapped in a generic error by our async/await handling.
2345
2534
  const retryable = error.name !== 'FirebaseError' &&
2346
2535
  attemptNumber < TRANSACTION_RETRY_COUNT;
2347
- logDebug(LOG_TAG$g, 'Transaction failed with error:', error.message, 'Retrying:', retryable);
2536
+ logDebug(LOG_TAG$h, 'Transaction failed with error:', error.message, 'Retrying:', retryable);
2348
2537
  this.close();
2349
2538
  if (!retryable) {
2350
2539
  return Promise.reject(error);
@@ -2431,11 +2620,11 @@ class SimpleDbStore {
2431
2620
  put(keyOrValue, value) {
2432
2621
  let request;
2433
2622
  if (value !== undefined) {
2434
- logDebug(LOG_TAG$g, 'PUT', this.store.name, keyOrValue, value);
2623
+ logDebug(LOG_TAG$h, 'PUT', this.store.name, keyOrValue, value);
2435
2624
  request = this.store.put(value, keyOrValue);
2436
2625
  }
2437
2626
  else {
2438
- logDebug(LOG_TAG$g, 'PUT', this.store.name, '<auto-key>', keyOrValue);
2627
+ logDebug(LOG_TAG$h, 'PUT', this.store.name, '<auto-key>', keyOrValue);
2439
2628
  request = this.store.put(keyOrValue);
2440
2629
  }
2441
2630
  return wrapRequest(request);
@@ -2448,7 +2637,7 @@ class SimpleDbStore {
2448
2637
  * @returns The key of the value to add.
2449
2638
  */
2450
2639
  add(value) {
2451
- logDebug(LOG_TAG$g, 'ADD', this.store.name, value, value);
2640
+ logDebug(LOG_TAG$h, 'ADD', this.store.name, value, value);
2452
2641
  const request = this.store.add(value);
2453
2642
  return wrapRequest(request);
2454
2643
  }
@@ -2468,12 +2657,12 @@ class SimpleDbStore {
2468
2657
  if (result === undefined) {
2469
2658
  result = null;
2470
2659
  }
2471
- logDebug(LOG_TAG$g, 'GET', this.store.name, key, result);
2660
+ logDebug(LOG_TAG$h, 'GET', this.store.name, key, result);
2472
2661
  return result;
2473
2662
  });
2474
2663
  }
2475
2664
  delete(key) {
2476
- logDebug(LOG_TAG$g, 'DELETE', this.store.name, key);
2665
+ logDebug(LOG_TAG$h, 'DELETE', this.store.name, key);
2477
2666
  const request = this.store.delete(key);
2478
2667
  return wrapRequest(request);
2479
2668
  }
@@ -2484,21 +2673,37 @@ class SimpleDbStore {
2484
2673
  * Returns the number of rows in the store.
2485
2674
  */
2486
2675
  count() {
2487
- logDebug(LOG_TAG$g, 'COUNT', this.store.name);
2676
+ logDebug(LOG_TAG$h, 'COUNT', this.store.name);
2488
2677
  const request = this.store.count();
2489
2678
  return wrapRequest(request);
2490
2679
  }
2491
2680
  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
- });
2681
+ const iterateOptions = this.options(indexOrRange, range);
2682
+ // Use `getAll()` if the browser supports IndexedDB v3, as it is roughly
2683
+ // 20% faster. Unfortunately, getAll() does not support custom indices.
2684
+ if (!iterateOptions.index && typeof this.store.getAll === 'function') {
2685
+ const request = this.store.getAll(iterateOptions.range);
2686
+ return new PersistencePromise((resolve, reject) => {
2687
+ request.onerror = (event) => {
2688
+ reject(event.target.error);
2689
+ };
2690
+ request.onsuccess = (event) => {
2691
+ resolve(event.target.result);
2692
+ };
2693
+ });
2694
+ }
2695
+ else {
2696
+ const cursor = this.cursor(iterateOptions);
2697
+ const results = [];
2698
+ return this.iterateCursor(cursor, (key, value) => {
2699
+ results.push(value);
2700
+ }).next(() => {
2701
+ return results;
2702
+ });
2703
+ }
2499
2704
  }
2500
2705
  deleteAll(indexOrRange, range) {
2501
- logDebug(LOG_TAG$g, 'DELETE ALL', this.store.name);
2706
+ logDebug(LOG_TAG$h, 'DELETE ALL', this.store.name);
2502
2707
  const options = this.options(indexOrRange, range);
2503
2708
  options.keysOnly = false;
2504
2709
  const cursor = this.cursor(options);
@@ -3521,11 +3726,22 @@ class DocumentKey {
3521
3726
  static empty() {
3522
3727
  return new DocumentKey(ResourcePath.emptyPath());
3523
3728
  }
3729
+ get collectionGroup() {
3730
+ return this.path.popLast().lastSegment();
3731
+ }
3524
3732
  /** Returns true if the document is in the specified collectionId. */
3525
3733
  hasCollectionId(collectionId) {
3526
3734
  return (this.path.length >= 2 &&
3527
3735
  this.path.get(this.path.length - 2) === collectionId);
3528
3736
  }
3737
+ /** Returns the collection group (i.e. the name of the parent collection) for this key. */
3738
+ getCollectionGroup() {
3739
+ return this.path.get(this.path.length - 2);
3740
+ }
3741
+ /** Returns the fully qualified path to the parent collection. */
3742
+ getCollectionPath() {
3743
+ return this.path.popLast();
3744
+ }
3529
3745
  isEqual(other) {
3530
3746
  return (other !== null && ResourcePath.comparator(this.path, other.path) === 0);
3531
3747
  }
@@ -3565,6 +3781,13 @@ class DocumentKey {
3565
3781
  * See the License for the specific language governing permissions and
3566
3782
  * limitations under the License.
3567
3783
  */
3784
+ const MAX_VALUE = {
3785
+ mapValue: {
3786
+ fields: {
3787
+ '__type__': { stringValue: '__max___' }
3788
+ }
3789
+ }
3790
+ };
3568
3791
  /** Extracts the backend's type order for the provided value. */
3569
3792
  function typeOrder(value) {
3570
3793
  if ('nullValue' in value) {
@@ -3974,6 +4197,10 @@ function deepClone(source) {
3974
4197
  else {
3975
4198
  return Object.assign({}, source);
3976
4199
  }
4200
+ }
4201
+ /** Returns true if the Value represents the canonical {@link #MAX_VALUE} . */
4202
+ function isMaxValue(value) {
4203
+ return valueEquals(value, MAX_VALUE);
3977
4204
  }
3978
4205
 
3979
4206
  /**
@@ -4349,17 +4576,25 @@ class FieldIndex {
4349
4576
  /** The collection ID this index applies to. */
4350
4577
  collectionGroup,
4351
4578
  /** The field segments for this index. */
4352
- segments,
4579
+ fields,
4353
4580
  /** Shows how up-to-date the index is for the current user. */
4354
4581
  indexState) {
4355
4582
  this.indexId = indexId;
4356
4583
  this.collectionGroup = collectionGroup;
4357
- this.segments = segments;
4584
+ this.fields = fields;
4358
4585
  this.indexState = indexState;
4359
4586
  }
4360
4587
  }
4361
4588
  /** An ID for an index that has not yet been added to persistence. */
4362
4589
  FieldIndex.UNKNOWN_ID = -1;
4590
+ /** Returns the ArrayContains/ArrayContainsAny segment for this index. */
4591
+ function fieldIndexGetArraySegment(fieldIndex) {
4592
+ return fieldIndex.fields.find(s => s.kind === 2 /* CONTAINS */);
4593
+ }
4594
+ /** Returns all directional (ascending/descending) segments for this index. */
4595
+ function fieldIndexGetDirectionalSegments(fieldIndex) {
4596
+ return fieldIndex.fields.filter(s => s.kind !== 2 /* CONTAINS */);
4597
+ }
4363
4598
  /** An index component consisting of field path and index type. */
4364
4599
  class IndexSegment {
4365
4600
  constructor(
@@ -4464,27 +4699,29 @@ function newTarget(path, collectionGroup = null, orderBy = [], filters = [], lim
4464
4699
  function canonifyTarget(target) {
4465
4700
  const targetImpl = debugCast(target);
4466
4701
  if (targetImpl.memoizedCanonicalId === null) {
4467
- let canonicalId = targetImpl.path.canonicalString();
4702
+ let str = targetImpl.path.canonicalString();
4468
4703
  if (targetImpl.collectionGroup !== null) {
4469
- canonicalId += '|cg:' + targetImpl.collectionGroup;
4704
+ str += '|cg:' + targetImpl.collectionGroup;
4470
4705
  }
4471
- canonicalId += '|f:';
4472
- canonicalId += targetImpl.filters.map(f => canonifyFilter(f)).join(',');
4473
- canonicalId += '|ob:';
4474
- canonicalId += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(',');
4706
+ str += '|f:';
4707
+ str += targetImpl.filters.map(f => canonifyFilter(f)).join(',');
4708
+ str += '|ob:';
4709
+ str += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(',');
4475
4710
  if (!isNullOrUndefined(targetImpl.limit)) {
4476
- canonicalId += '|l:';
4477
- canonicalId += targetImpl.limit;
4711
+ str += '|l:';
4712
+ str += targetImpl.limit;
4478
4713
  }
4479
4714
  if (targetImpl.startAt) {
4480
- canonicalId += '|lb:';
4481
- canonicalId += canonifyBound(targetImpl.startAt);
4715
+ str += '|lb:';
4716
+ str += targetImpl.startAt.inclusive ? 'b:' : 'a:';
4717
+ str += targetImpl.startAt.position.map(p => canonicalId(p)).join(',');
4482
4718
  }
4483
4719
  if (targetImpl.endAt) {
4484
- canonicalId += '|ub:';
4485
- canonicalId += canonifyBound(targetImpl.endAt);
4720
+ str += '|ub:';
4721
+ str += targetImpl.endAt.inclusive ? 'a:' : 'b:';
4722
+ str += targetImpl.endAt.position.map(p => canonicalId(p)).join(',');
4486
4723
  }
4487
- targetImpl.memoizedCanonicalId = canonicalId;
4724
+ targetImpl.memoizedCanonicalId = str;
4488
4725
  }
4489
4726
  return targetImpl.memoizedCanonicalId;
4490
4727
  }
@@ -4507,10 +4744,14 @@ function stringifyTarget(target) {
4507
4744
  .join(', ')}]`;
4508
4745
  }
4509
4746
  if (target.startAt) {
4510
- str += ', startAt: ' + canonifyBound(target.startAt);
4747
+ str += ', startAt: ';
4748
+ str += target.startAt.inclusive ? 'b:' : 'a:';
4749
+ str += target.startAt.position.map(p => canonicalId(p)).join(',');
4511
4750
  }
4512
4751
  if (target.endAt) {
4513
- str += ', endAt: ' + canonifyBound(target.endAt);
4752
+ str += ', endAt: ';
4753
+ str += target.endAt.inclusive ? 'a:' : 'b:';
4754
+ str += target.endAt.position.map(p => canonicalId(p)).join(',');
4514
4755
  }
4515
4756
  return `Target(${str})`;
4516
4757
  }
@@ -4733,7 +4974,6 @@ class ArrayContainsAnyFilter extends FieldFilter {
4733
4974
  return other.arrayValue.values.some(val => arrayValueContains(this.value.arrayValue, val));
4734
4975
  }
4735
4976
  }
4736
- // TODO(indexing): Change Bound.before to "inclusive"
4737
4977
  /**
4738
4978
  * Represents a bound of a query.
4739
4979
  *
@@ -4749,17 +4989,11 @@ class ArrayContainsAnyFilter extends FieldFilter {
4749
4989
  * just after the provided values.
4750
4990
  */
4751
4991
  class Bound {
4752
- constructor(position, before) {
4992
+ constructor(position, inclusive) {
4753
4993
  this.position = position;
4754
- this.before = before;
4994
+ this.inclusive = inclusive;
4755
4995
  }
4756
4996
  }
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
4997
  /**
4764
4998
  * An ordering on a field, in some Direction. Direction defaults to ASCENDING.
4765
4999
  */
@@ -4779,11 +5013,7 @@ function stringifyOrderBy(orderBy) {
4779
5013
  function orderByEquals(left, right) {
4780
5014
  return left.dir === right.dir && left.field.isEqual(right.field);
4781
5015
  }
4782
- /**
4783
- * Returns true if a document sorts before a bound using the provided sort
4784
- * order.
4785
- */
4786
- function sortsBeforeDocument(bound, orderBy, doc) {
5016
+ function boundCompareToDocument(bound, orderBy, doc) {
4787
5017
  let comparison = 0;
4788
5018
  for (let i = 0; i < bound.position.length; i++) {
4789
5019
  const orderByComponent = orderBy[i];
@@ -4802,7 +5032,23 @@ function sortsBeforeDocument(bound, orderBy, doc) {
4802
5032
  break;
4803
5033
  }
4804
5034
  }
4805
- return bound.before ? comparison <= 0 : comparison < 0;
5035
+ return comparison;
5036
+ }
5037
+ /**
5038
+ * Returns true if a document sorts after a bound using the provided sort
5039
+ * order.
5040
+ */
5041
+ function boundSortsAfterDocument(bound, orderBy, doc) {
5042
+ const comparison = boundCompareToDocument(bound, orderBy, doc);
5043
+ return bound.inclusive ? comparison >= 0 : comparison > 0;
5044
+ }
5045
+ /**
5046
+ * Returns true if a document sorts before a bound using the provided sort
5047
+ * order.
5048
+ */
5049
+ function boundSortsBeforeDocument(bound, orderBy, doc) {
5050
+ const comparison = boundCompareToDocument(bound, orderBy, doc);
5051
+ return bound.inclusive ? comparison <= 0 : comparison < 0;
4806
5052
  }
4807
5053
  function boundEquals(left, right) {
4808
5054
  if (left === null) {
@@ -4811,7 +5057,7 @@ function boundEquals(left, right) {
4811
5057
  else if (right === null) {
4812
5058
  return false;
4813
5059
  }
4814
- if (left.before !== right.before ||
5060
+ if (left.inclusive !== right.inclusive ||
4815
5061
  left.position.length !== right.position.length) {
4816
5062
  return false;
4817
5063
  }
@@ -5015,10 +5261,10 @@ function queryToTarget(query) {
5015
5261
  }
5016
5262
  // We need to swap the cursors to match the now-flipped query ordering.
5017
5263
  const startAt = queryImpl.endAt
5018
- ? new Bound(queryImpl.endAt.position, !queryImpl.endAt.before)
5264
+ ? new Bound(queryImpl.endAt.position, !queryImpl.endAt.inclusive)
5019
5265
  : null;
5020
5266
  const endAt = queryImpl.startAt
5021
- ? new Bound(queryImpl.startAt.position, !queryImpl.startAt.before)
5267
+ ? new Bound(queryImpl.startAt.position, !queryImpl.startAt.inclusive)
5022
5268
  : null;
5023
5269
  // Now return as a LimitType.First query.
5024
5270
  queryImpl.memoizedTarget = newTarget(queryImpl.path, queryImpl.collectionGroup, orderBys, queryImpl.filters, queryImpl.limit, startAt, endAt);
@@ -5106,11 +5352,11 @@ function queryMatchesFilters(query, doc) {
5106
5352
  /** Makes sure a document is within the bounds, if provided. */
5107
5353
  function queryMatchesBounds(query, doc) {
5108
5354
  if (query.startAt &&
5109
- !sortsBeforeDocument(query.startAt, queryOrderBy(query), doc)) {
5355
+ !boundSortsBeforeDocument(query.startAt, queryOrderBy(query), doc)) {
5110
5356
  return false;
5111
5357
  }
5112
5358
  if (query.endAt &&
5113
- sortsBeforeDocument(query.endAt, queryOrderBy(query), doc)) {
5359
+ !boundSortsAfterDocument(query.endAt, queryOrderBy(query), doc)) {
5114
5360
  return false;
5115
5361
  }
5116
5362
  return true;
@@ -5765,6 +6011,72 @@ class SortedSetIterator {
5765
6011
  hasNext() {
5766
6012
  return this.iter.hasNext();
5767
6013
  }
6014
+ }
6015
+ /**
6016
+ * Compares two sorted sets for equality using their natural ordering. The
6017
+ * method computes the intersection and invokes `onAdd` for every element that
6018
+ * is in `after` but not `before`. `onRemove` is invoked for every element in
6019
+ * `before` but missing from `after`.
6020
+ *
6021
+ * The method creates a copy of both `before` and `after` and runs in O(n log
6022
+ * n), where n is the size of the two lists.
6023
+ *
6024
+ * @param before - The elements that exist in the original set.
6025
+ * @param after - The elements to diff against the original set.
6026
+ * @param comparator - The comparator for the elements in before and after.
6027
+ * @param onAdd - A function to invoke for every element that is part of `
6028
+ * after` but not `before`.
6029
+ * @param onRemove - A function to invoke for every element that is part of
6030
+ * `before` but not `after`.
6031
+ */
6032
+ function diffSortedSets(before, after, comparator, onAdd, onRemove) {
6033
+ const beforeIt = before.getIterator();
6034
+ const afterIt = after.getIterator();
6035
+ let beforeValue = advanceIterator(beforeIt);
6036
+ let afterValue = advanceIterator(afterIt);
6037
+ // Walk through the two sets at the same time, using the ordering defined by
6038
+ // `comparator`.
6039
+ while (beforeValue || afterValue) {
6040
+ let added = false;
6041
+ let removed = false;
6042
+ if (beforeValue && afterValue) {
6043
+ const cmp = comparator(beforeValue, afterValue);
6044
+ if (cmp < 0) {
6045
+ // The element was removed if the next element in our ordered
6046
+ // walkthrough is only in `before`.
6047
+ removed = true;
6048
+ }
6049
+ else if (cmp > 0) {
6050
+ // The element was added if the next element in our ordered walkthrough
6051
+ // is only in `after`.
6052
+ added = true;
6053
+ }
6054
+ }
6055
+ else if (beforeValue != null) {
6056
+ removed = true;
6057
+ }
6058
+ else {
6059
+ added = true;
6060
+ }
6061
+ if (added) {
6062
+ onAdd(afterValue);
6063
+ afterValue = advanceIterator(afterIt);
6064
+ }
6065
+ else if (removed) {
6066
+ onRemove(beforeValue);
6067
+ beforeValue = advanceIterator(beforeIt);
6068
+ }
6069
+ else {
6070
+ beforeValue = advanceIterator(beforeIt);
6071
+ afterValue = advanceIterator(afterIt);
6072
+ }
6073
+ }
6074
+ }
6075
+ /**
6076
+ * Returns the next element from the iterator or `undefined` if none available.
6077
+ */
6078
+ function advanceIterator(it) {
6079
+ return it.hasNext() ? it.getNext() : undefined;
5768
6080
  }
5769
6081
 
5770
6082
  /**
@@ -6603,6 +6915,47 @@ class MutationBatchResult {
6603
6915
  }
6604
6916
  }
6605
6917
 
6918
+ /**
6919
+ * @license
6920
+ * Copyright 2022 Google LLC
6921
+ *
6922
+ * Licensed under the Apache License, Version 2.0 (the "License");
6923
+ * you may not use this file except in compliance with the License.
6924
+ * You may obtain a copy of the License at
6925
+ *
6926
+ * http://www.apache.org/licenses/LICENSE-2.0
6927
+ *
6928
+ * Unless required by applicable law or agreed to in writing, software
6929
+ * distributed under the License is distributed on an "AS IS" BASIS,
6930
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
6931
+ * See the License for the specific language governing permissions and
6932
+ * limitations under the License.
6933
+ */
6934
+ /**
6935
+ * Representation of an overlay computed by Firestore.
6936
+ *
6937
+ * Holds information about a mutation and the largest batch id in Firestore when
6938
+ * the mutation was created.
6939
+ */
6940
+ class Overlay {
6941
+ constructor(largestBatchId, mutation) {
6942
+ this.largestBatchId = largestBatchId;
6943
+ this.mutation = mutation;
6944
+ }
6945
+ getKey() {
6946
+ return this.mutation.key;
6947
+ }
6948
+ isEqual(other) {
6949
+ return other !== null && this.mutation === other.mutation;
6950
+ }
6951
+ toString() {
6952
+ return `Overlay{
6953
+ largestBatchId: ${this.largestBatchId},
6954
+ mutation: ${this.mutation.toString()}
6955
+ }`;
6956
+ }
6957
+ }
6958
+
6606
6959
  /**
6607
6960
  * @license
6608
6961
  * Copyright 2017 Google LLC
@@ -7075,7 +7428,7 @@ class TargetState {
7075
7428
  this._current = true;
7076
7429
  }
7077
7430
  }
7078
- const LOG_TAG$f = 'WatchChangeAggregator';
7431
+ const LOG_TAG$g = 'WatchChangeAggregator';
7079
7432
  /**
7080
7433
  * A helper class to accumulate watch changes into a RemoteEvent.
7081
7434
  */
@@ -7370,7 +7723,7 @@ class WatchChangeAggregator {
7370
7723
  isActiveTarget(targetId) {
7371
7724
  const targetActive = this.targetDataForActiveTarget(targetId) !== null;
7372
7725
  if (!targetActive) {
7373
- logDebug(LOG_TAG$f, 'Detected inactive target', targetId);
7726
+ logDebug(LOG_TAG$g, 'Detected inactive target', targetId);
7374
7727
  }
7375
7728
  return targetActive;
7376
7729
  }
@@ -7989,10 +8342,10 @@ function toQueryTarget(serializer, target) {
7989
8342
  result.structuredQuery.limit = limit;
7990
8343
  }
7991
8344
  if (target.startAt) {
7992
- result.structuredQuery.startAt = toCursor(target.startAt);
8345
+ result.structuredQuery.startAt = toStartAtCursor(target.startAt);
7993
8346
  }
7994
8347
  if (target.endAt) {
7995
- result.structuredQuery.endAt = toCursor(target.endAt);
8348
+ result.structuredQuery.endAt = toEndAtCursor(target.endAt);
7996
8349
  }
7997
8350
  return result;
7998
8351
  }
@@ -8025,11 +8378,11 @@ function convertQueryTargetToQuery(target) {
8025
8378
  }
8026
8379
  let startAt = null;
8027
8380
  if (query.startAt) {
8028
- startAt = fromCursor(query.startAt);
8381
+ startAt = fromStartAtCursor(query.startAt);
8029
8382
  }
8030
8383
  let endAt = null;
8031
8384
  if (query.endAt) {
8032
- endAt = fromCursor(query.endAt);
8385
+ endAt = fromEndAtCursor(query.endAt);
8033
8386
  }
8034
8387
  return newQuery(path, collectionGroup, orderBy, filterBy, limit, "F" /* First */, startAt, endAt);
8035
8388
  }
@@ -8120,16 +8473,27 @@ function toOrder(orderBys) {
8120
8473
  function fromOrder(orderBys) {
8121
8474
  return orderBys.map(order => fromPropertyOrder(order));
8122
8475
  }
8123
- function toCursor(cursor) {
8476
+ function toStartAtCursor(cursor) {
8477
+ return {
8478
+ before: cursor.inclusive,
8479
+ values: cursor.position
8480
+ };
8481
+ }
8482
+ function toEndAtCursor(cursor) {
8124
8483
  return {
8125
- before: cursor.before,
8484
+ before: !cursor.inclusive,
8126
8485
  values: cursor.position
8127
8486
  };
8128
8487
  }
8129
- function fromCursor(cursor) {
8130
- const before = !!cursor.before;
8488
+ function fromStartAtCursor(cursor) {
8489
+ const inclusive = !!cursor.before;
8490
+ const position = cursor.values || [];
8491
+ return new Bound(position, inclusive);
8492
+ }
8493
+ function fromEndAtCursor(cursor) {
8494
+ const inclusive = !cursor.before;
8131
8495
  const position = cursor.values || [];
8132
- return new Bound(position, before);
8496
+ return new Bound(position, inclusive);
8133
8497
  }
8134
8498
  // visible for testing
8135
8499
  function toDirection(dir) {
@@ -8576,28 +8940,764 @@ function fromBundledQuery(bundledQuery) {
8576
8940
  if (bundledQuery.limitType === 'LAST') {
8577
8941
  return queryWithLimit(query, query.limit, "L" /* Last */);
8578
8942
  }
8579
- return query;
8943
+ return query;
8944
+ }
8945
+ /** Encodes a NamedQuery proto object to a NamedQuery model object. */
8946
+ function fromProtoNamedQuery(namedQuery) {
8947
+ return {
8948
+ name: namedQuery.name,
8949
+ query: fromBundledQuery(namedQuery.bundledQuery),
8950
+ readTime: fromVersion(namedQuery.readTime)
8951
+ };
8952
+ }
8953
+ /** Decodes a BundleMetadata proto into a BundleMetadata object. */
8954
+ function fromBundleMetadata(metadata) {
8955
+ return {
8956
+ id: metadata.id,
8957
+ version: metadata.version,
8958
+ createTime: fromVersion(metadata.createTime)
8959
+ };
8960
+ }
8961
+ /** Encodes a DbDocumentOverlay object to an Overlay model object. */
8962
+ function fromDbDocumentOverlay(localSerializer, dbDocumentOverlay) {
8963
+ return new Overlay(dbDocumentOverlay.largestBatchId, fromMutation(localSerializer.remoteSerializer, dbDocumentOverlay.overlayMutation));
8964
+ }
8965
+ /** Decodes an Overlay model object into a DbDocumentOverlay object. */
8966
+ function toDbDocumentOverlay(localSerializer, userId, overlay) {
8967
+ const [_, collectionPath, documentId] = toDbDocumentOverlayKey(userId, overlay.mutation.key);
8968
+ return new DbDocumentOverlay(userId, collectionPath, documentId, overlay.mutation.key.getCollectionGroup(), overlay.largestBatchId, toMutation(localSerializer.remoteSerializer, overlay.mutation));
8969
+ }
8970
+ /**
8971
+ * Returns the DbDocumentOverlayKey corresponding to the given user and
8972
+ * document key.
8973
+ */
8974
+ function toDbDocumentOverlayKey(userId, docKey) {
8975
+ const docId = docKey.path.lastSegment();
8976
+ const collectionPath = encodeResourcePath(docKey.path.popLast());
8977
+ return [userId, collectionPath, docId];
8978
+ }
8979
+ function toDbIndexConfiguration(index) {
8980
+ return new DbIndexConfiguration(index.indexId, index.collectionGroup, index.fields.map(s => [s.fieldPath.canonicalString(), s.kind]));
8981
+ }
8982
+ function fromDbIndexConfiguration(index, state) {
8983
+ const decodedState = state
8984
+ ? new IndexState(state.sequenceNumber, new IndexOffset(fromDbTimestamp(state.readTime), new DocumentKey(decodeResourcePath(state.documentKey)), state.largestBatchId))
8985
+ : IndexState.empty();
8986
+ const decodedSegments = index.fields.map(([fieldPath, kind]) => new IndexSegment(FieldPath$1.fromServerFormat(fieldPath), kind));
8987
+ return new FieldIndex(index.indexId, index.collectionGroup, decodedSegments, decodedState);
8988
+ }
8989
+ function toDbIndexState(indexId, user, sequenceNumber, offset) {
8990
+ return new DbIndexState(indexId, user.uid || '', sequenceNumber, toDbTimestamp(offset.readTime), encodeResourcePath(offset.documentKey.path), offset.largestBatchId);
8991
+ }
8992
+
8993
+ /**
8994
+ * @license
8995
+ * Copyright 2020 Google LLC
8996
+ *
8997
+ * Licensed under the Apache License, Version 2.0 (the "License");
8998
+ * you may not use this file except in compliance with the License.
8999
+ * You may obtain a copy of the License at
9000
+ *
9001
+ * http://www.apache.org/licenses/LICENSE-2.0
9002
+ *
9003
+ * Unless required by applicable law or agreed to in writing, software
9004
+ * distributed under the License is distributed on an "AS IS" BASIS,
9005
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9006
+ * See the License for the specific language governing permissions and
9007
+ * limitations under the License.
9008
+ */
9009
+ class IndexedDbBundleCache {
9010
+ getBundleMetadata(transaction, bundleId) {
9011
+ return bundlesStore(transaction)
9012
+ .get(bundleId)
9013
+ .next(bundle => {
9014
+ if (bundle) {
9015
+ return fromDbBundle(bundle);
9016
+ }
9017
+ return undefined;
9018
+ });
9019
+ }
9020
+ saveBundleMetadata(transaction, bundleMetadata) {
9021
+ return bundlesStore(transaction).put(toDbBundle(bundleMetadata));
9022
+ }
9023
+ getNamedQuery(transaction, queryName) {
9024
+ return namedQueriesStore(transaction)
9025
+ .get(queryName)
9026
+ .next(query => {
9027
+ if (query) {
9028
+ return fromDbNamedQuery(query);
9029
+ }
9030
+ return undefined;
9031
+ });
9032
+ }
9033
+ saveNamedQuery(transaction, query) {
9034
+ return namedQueriesStore(transaction).put(toDbNamedQuery(query));
9035
+ }
9036
+ }
9037
+ /**
9038
+ * Helper to get a typed SimpleDbStore for the bundles object store.
9039
+ */
9040
+ function bundlesStore(txn) {
9041
+ return getStore(txn, DbBundle.store);
9042
+ }
9043
+ /**
9044
+ * Helper to get a typed SimpleDbStore for the namedQueries object store.
9045
+ */
9046
+ function namedQueriesStore(txn) {
9047
+ return getStore(txn, DbNamedQuery.store);
9048
+ }
9049
+
9050
+ /**
9051
+ * @license
9052
+ * Copyright 2022 Google LLC
9053
+ *
9054
+ * Licensed under the Apache License, Version 2.0 (the "License");
9055
+ * you may not use this file except in compliance with the License.
9056
+ * You may obtain a copy of the License at
9057
+ *
9058
+ * http://www.apache.org/licenses/LICENSE-2.0
9059
+ *
9060
+ * Unless required by applicable law or agreed to in writing, software
9061
+ * distributed under the License is distributed on an "AS IS" BASIS,
9062
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9063
+ * See the License for the specific language governing permissions and
9064
+ * limitations under the License.
9065
+ */
9066
+ /**
9067
+ * Implementation of DocumentOverlayCache using IndexedDb.
9068
+ */
9069
+ class IndexedDbDocumentOverlayCache {
9070
+ /**
9071
+ * @param serializer - The document serializer.
9072
+ * @param userId - The userId for which we are accessing overlays.
9073
+ */
9074
+ constructor(serializer, userId) {
9075
+ this.serializer = serializer;
9076
+ this.userId = userId;
9077
+ }
9078
+ static forUser(serializer, user) {
9079
+ const userId = user.uid || '';
9080
+ return new IndexedDbDocumentOverlayCache(serializer, userId);
9081
+ }
9082
+ getOverlay(transaction, key) {
9083
+ return documentOverlayStore(transaction)
9084
+ .get(toDbDocumentOverlayKey(this.userId, key))
9085
+ .next(dbOverlay => {
9086
+ if (dbOverlay) {
9087
+ return fromDbDocumentOverlay(this.serializer, dbOverlay);
9088
+ }
9089
+ return null;
9090
+ });
9091
+ }
9092
+ saveOverlays(transaction, largestBatchId, overlays) {
9093
+ const promises = [];
9094
+ overlays.forEach(mutation => {
9095
+ const overlay = new Overlay(largestBatchId, mutation);
9096
+ promises.push(this.saveOverlay(transaction, overlay));
9097
+ });
9098
+ return PersistencePromise.waitFor(promises);
9099
+ }
9100
+ removeOverlaysForBatchId(transaction, documentKeys, batchId) {
9101
+ const collectionPaths = new Set();
9102
+ // Get the set of unique collection paths.
9103
+ documentKeys.forEach(key => collectionPaths.add(encodeResourcePath(key.getCollectionPath())));
9104
+ const promises = [];
9105
+ collectionPaths.forEach(collectionPath => {
9106
+ const range = IDBKeyRange.bound([this.userId, collectionPath, batchId], [this.userId, collectionPath, batchId + 1],
9107
+ /*lowerOpen=*/ false,
9108
+ /*upperOpen=*/ true);
9109
+ promises.push(documentOverlayStore(transaction).deleteAll(DbDocumentOverlay.collectionPathOverlayIndex, range));
9110
+ });
9111
+ return PersistencePromise.waitFor(promises);
9112
+ }
9113
+ getOverlaysForCollection(transaction, collection, sinceBatchId) {
9114
+ const result = new Map();
9115
+ const collectionPath = encodeResourcePath(collection);
9116
+ // We want batch IDs larger than `sinceBatchId`, and so the lower bound
9117
+ // is not inclusive.
9118
+ const range = IDBKeyRange.bound([this.userId, collectionPath, sinceBatchId], [this.userId, collectionPath, Number.POSITIVE_INFINITY],
9119
+ /*lowerOpen=*/ true);
9120
+ return documentOverlayStore(transaction)
9121
+ .loadAll(DbDocumentOverlay.collectionPathOverlayIndex, range)
9122
+ .next(dbOverlays => {
9123
+ for (const dbOverlay of dbOverlays) {
9124
+ const overlay = fromDbDocumentOverlay(this.serializer, dbOverlay);
9125
+ result.set(overlay.getKey(), overlay);
9126
+ }
9127
+ return result;
9128
+ });
9129
+ }
9130
+ getOverlaysForCollectionGroup(transaction, collectionGroup, sinceBatchId, count) {
9131
+ const result = new Map();
9132
+ let currentBatchId = undefined;
9133
+ // We want batch IDs larger than `sinceBatchId`, and so the lower bound
9134
+ // is not inclusive.
9135
+ const range = IDBKeyRange.bound([this.userId, collectionGroup, sinceBatchId], [this.userId, collectionGroup, Number.POSITIVE_INFINITY],
9136
+ /*lowerOpen=*/ true);
9137
+ return documentOverlayStore(transaction)
9138
+ .iterate({
9139
+ index: DbDocumentOverlay.collectionGroupOverlayIndex,
9140
+ range
9141
+ }, (_, dbOverlay, control) => {
9142
+ // We do not want to return partial batch overlays, even if the size
9143
+ // of the result set exceeds the given `count` argument. Therefore, we
9144
+ // continue to aggregate results even after the result size exceeds
9145
+ // `count` if there are more overlays from the `currentBatchId`.
9146
+ const overlay = fromDbDocumentOverlay(this.serializer, dbOverlay);
9147
+ if (result.size < count ||
9148
+ overlay.largestBatchId === currentBatchId) {
9149
+ result.set(overlay.getKey(), overlay);
9150
+ currentBatchId = overlay.largestBatchId;
9151
+ }
9152
+ else {
9153
+ control.done();
9154
+ }
9155
+ })
9156
+ .next(() => result);
9157
+ }
9158
+ saveOverlay(transaction, overlay) {
9159
+ return documentOverlayStore(transaction).put(toDbDocumentOverlay(this.serializer, this.userId, overlay));
9160
+ }
9161
+ }
9162
+ /**
9163
+ * Helper to get a typed SimpleDbStore for the document overlay object store.
9164
+ */
9165
+ function documentOverlayStore(txn) {
9166
+ return getStore(txn, DbDocumentOverlay.store);
9167
+ }
9168
+
9169
+ /**
9170
+ * @license
9171
+ * Copyright 2021 Google LLC
9172
+ *
9173
+ * Licensed under the Apache License, Version 2.0 (the "License");
9174
+ * you may not use this file except in compliance with the License.
9175
+ * You may obtain a copy of the License at
9176
+ *
9177
+ * http://www.apache.org/licenses/LICENSE-2.0
9178
+ *
9179
+ * Unless required by applicable law or agreed to in writing, software
9180
+ * distributed under the License is distributed on an "AS IS" BASIS,
9181
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9182
+ * See the License for the specific language governing permissions and
9183
+ * limitations under the License.
9184
+ */
9185
+ // Note: This code is copied from the backend. Code that is not used by
9186
+ // Firestore was removed.
9187
+ const INDEX_TYPE_NULL = 5;
9188
+ const INDEX_TYPE_BOOLEAN = 10;
9189
+ const INDEX_TYPE_NAN = 13;
9190
+ const INDEX_TYPE_NUMBER = 15;
9191
+ const INDEX_TYPE_TIMESTAMP = 20;
9192
+ const INDEX_TYPE_STRING = 25;
9193
+ const INDEX_TYPE_BLOB = 30;
9194
+ const INDEX_TYPE_REFERENCE = 37;
9195
+ const INDEX_TYPE_GEOPOINT = 45;
9196
+ const INDEX_TYPE_ARRAY = 50;
9197
+ const INDEX_TYPE_MAP = 55;
9198
+ const INDEX_TYPE_REFERENCE_SEGMENT = 60;
9199
+ // A terminator that indicates that a truncatable value was not truncated.
9200
+ // This must be smaller than all other type labels.
9201
+ const NOT_TRUNCATED = 2;
9202
+ /** Firestore index value writer. */
9203
+ class FirestoreIndexValueWriter {
9204
+ constructor() { }
9205
+ // The write methods below short-circuit writing terminators for values
9206
+ // containing a (terminating) truncated value.
9207
+ //
9208
+ // As an example, consider the resulting encoding for:
9209
+ //
9210
+ // ["bar", [2, "foo"]] -> (STRING, "bar", TERM, ARRAY, NUMBER, 2, STRING, "foo", TERM, TERM, TERM)
9211
+ // ["bar", [2, truncated("foo")]] -> (STRING, "bar", TERM, ARRAY, NUMBER, 2, STRING, "foo", TRUNC)
9212
+ // ["bar", truncated(["foo"])] -> (STRING, "bar", TERM, ARRAY. STRING, "foo", TERM, TRUNC)
9213
+ /** Writes an index value. */
9214
+ writeIndexValue(value, encoder) {
9215
+ this.writeIndexValueAux(value, encoder);
9216
+ // Write separator to split index values
9217
+ // (see go/firestore-storage-format#encodings).
9218
+ encoder.writeInfinity();
9219
+ }
9220
+ writeIndexValueAux(indexValue, encoder) {
9221
+ if ('nullValue' in indexValue) {
9222
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_NULL);
9223
+ }
9224
+ else if ('booleanValue' in indexValue) {
9225
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_BOOLEAN);
9226
+ encoder.writeNumber(indexValue.booleanValue ? 1 : 0);
9227
+ }
9228
+ else if ('integerValue' in indexValue) {
9229
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER);
9230
+ encoder.writeNumber(normalizeNumber(indexValue.integerValue));
9231
+ }
9232
+ else if ('doubleValue' in indexValue) {
9233
+ const n = normalizeNumber(indexValue.doubleValue);
9234
+ if (isNaN(n)) {
9235
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_NAN);
9236
+ }
9237
+ else {
9238
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER);
9239
+ if (isNegativeZero(n)) {
9240
+ // -0.0, 0 and 0.0 are all considered the same
9241
+ encoder.writeNumber(0.0);
9242
+ }
9243
+ else {
9244
+ encoder.writeNumber(n);
9245
+ }
9246
+ }
9247
+ }
9248
+ else if ('timestampValue' in indexValue) {
9249
+ const timestamp = indexValue.timestampValue;
9250
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_TIMESTAMP);
9251
+ if (typeof timestamp === 'string') {
9252
+ encoder.writeString(timestamp);
9253
+ }
9254
+ else {
9255
+ encoder.writeString(`${timestamp.seconds || ''}`);
9256
+ encoder.writeNumber(timestamp.nanos || 0);
9257
+ }
9258
+ }
9259
+ else if ('stringValue' in indexValue) {
9260
+ this.writeIndexString(indexValue.stringValue, encoder);
9261
+ this.writeTruncationMarker(encoder);
9262
+ }
9263
+ else if ('bytesValue' in indexValue) {
9264
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_BLOB);
9265
+ encoder.writeBytes(normalizeByteString(indexValue.bytesValue));
9266
+ this.writeTruncationMarker(encoder);
9267
+ }
9268
+ else if ('referenceValue' in indexValue) {
9269
+ this.writeIndexEntityRef(indexValue.referenceValue, encoder);
9270
+ }
9271
+ else if ('geoPointValue' in indexValue) {
9272
+ const geoPoint = indexValue.geoPointValue;
9273
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_GEOPOINT);
9274
+ encoder.writeNumber(geoPoint.latitude || 0);
9275
+ encoder.writeNumber(geoPoint.longitude || 0);
9276
+ }
9277
+ else if ('mapValue' in indexValue) {
9278
+ if (isMaxValue(indexValue)) {
9279
+ this.writeValueTypeLabel(encoder, Number.MAX_SAFE_INTEGER);
9280
+ }
9281
+ else {
9282
+ this.writeIndexMap(indexValue.mapValue, encoder);
9283
+ this.writeTruncationMarker(encoder);
9284
+ }
9285
+ }
9286
+ else if ('arrayValue' in indexValue) {
9287
+ this.writeIndexArray(indexValue.arrayValue, encoder);
9288
+ this.writeTruncationMarker(encoder);
9289
+ }
9290
+ else {
9291
+ fail();
9292
+ }
9293
+ }
9294
+ writeIndexString(stringIndexValue, encoder) {
9295
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_STRING);
9296
+ this.writeUnlabeledIndexString(stringIndexValue, encoder);
9297
+ }
9298
+ writeUnlabeledIndexString(stringIndexValue, encoder) {
9299
+ encoder.writeString(stringIndexValue);
9300
+ }
9301
+ writeIndexMap(mapIndexValue, encoder) {
9302
+ const map = mapIndexValue.fields || {};
9303
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_MAP);
9304
+ for (const key of Object.keys(map)) {
9305
+ this.writeIndexString(key, encoder);
9306
+ this.writeIndexValueAux(map[key], encoder);
9307
+ }
9308
+ }
9309
+ writeIndexArray(arrayIndexValue, encoder) {
9310
+ const values = arrayIndexValue.values || [];
9311
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_ARRAY);
9312
+ for (const element of values) {
9313
+ this.writeIndexValueAux(element, encoder);
9314
+ }
9315
+ }
9316
+ writeIndexEntityRef(referenceValue, encoder) {
9317
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_REFERENCE);
9318
+ const path = DocumentKey.fromName(referenceValue).path;
9319
+ path.forEach(segment => {
9320
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_REFERENCE_SEGMENT);
9321
+ this.writeUnlabeledIndexString(segment, encoder);
9322
+ });
9323
+ }
9324
+ writeValueTypeLabel(encoder, typeOrder) {
9325
+ encoder.writeNumber(typeOrder);
9326
+ }
9327
+ writeTruncationMarker(encoder) {
9328
+ // While the SDK does not implement truncation, the truncation marker is
9329
+ // used to terminate all variable length values (which are strings, bytes,
9330
+ // references, arrays and maps).
9331
+ encoder.writeNumber(NOT_TRUNCATED);
9332
+ }
9333
+ }
9334
+ FirestoreIndexValueWriter.INSTANCE = new FirestoreIndexValueWriter();
9335
+
9336
+ /**
9337
+ * @license
9338
+ * Copyright 2021 Google LLC
9339
+ *
9340
+ * Licensed under the Apache License, Version 2.0 (the "License");
9341
+ * you may not use this file except in compliance with the License.
9342
+ * You may obtain a copy of the License at
9343
+ *
9344
+ * http://www.apache.org/licenses/LICENSE-2.0
9345
+ *
9346
+ * Unless required by applicable law | agreed to in writing, software
9347
+ * distributed under the License is distributed on an "AS IS" BASIS,
9348
+ * WITHOUT WARRANTIES | CONDITIONS OF ANY KIND, either express | implied.
9349
+ * See the License for the specific language governing permissions and
9350
+ * limitations under the License.
9351
+ */
9352
+ /** These constants are taken from the backend. */
9353
+ const MIN_SURROGATE = '\uD800';
9354
+ const MAX_SURROGATE = '\uDBFF';
9355
+ const ESCAPE1 = 0x00;
9356
+ const NULL_BYTE = 0xff; // Combined with ESCAPE1
9357
+ const SEPARATOR = 0x01; // Combined with ESCAPE1
9358
+ const ESCAPE2 = 0xff;
9359
+ const INFINITY = 0xff; // Combined with ESCAPE2
9360
+ const FF_BYTE = 0x00; // Combined with ESCAPE2
9361
+ const LONG_SIZE = 64;
9362
+ const BYTE_SIZE = 8;
9363
+ /**
9364
+ * The default size of the buffer. This is arbitrary, but likely larger than
9365
+ * most index values so that less copies of the underlying buffer will be made.
9366
+ * For large values, a single copy will made to double the buffer length.
9367
+ */
9368
+ const DEFAULT_BUFFER_SIZE = 1024;
9369
+ /** Converts a JavaScript number to a byte array (using big endian encoding). */
9370
+ function doubleToLongBits(value) {
9371
+ const dv = new DataView(new ArrayBuffer(8));
9372
+ dv.setFloat64(0, value, /* littleEndian= */ false);
9373
+ return new Uint8Array(dv.buffer);
9374
+ }
9375
+ /**
9376
+ * Counts the number of zeros in a byte.
9377
+ *
9378
+ * Visible for testing.
9379
+ */
9380
+ function numberOfLeadingZerosInByte(x) {
9381
+ if (x === 0) {
9382
+ return 8;
9383
+ }
9384
+ let zeros = 0;
9385
+ if (x >> 4 === 0) {
9386
+ // Test if the first four bits are zero.
9387
+ zeros += 4;
9388
+ x = x << 4;
9389
+ }
9390
+ if (x >> 6 === 0) {
9391
+ // Test if the first two (or next two) bits are zero.
9392
+ zeros += 2;
9393
+ x = x << 2;
9394
+ }
9395
+ if (x >> 7 === 0) {
9396
+ // Test if the remaining bit is zero.
9397
+ zeros += 1;
9398
+ }
9399
+ return zeros;
9400
+ }
9401
+ /** Counts the number of leading zeros in the given byte array. */
9402
+ function numberOfLeadingZeros(bytes) {
9403
+ let leadingZeros = 0;
9404
+ for (let i = 0; i < 8; ++i) {
9405
+ const zeros = numberOfLeadingZerosInByte(bytes[i] & 0xff);
9406
+ leadingZeros += zeros;
9407
+ if (zeros !== 8) {
9408
+ break;
9409
+ }
9410
+ }
9411
+ return leadingZeros;
9412
+ }
9413
+ /**
9414
+ * Returns the number of bytes required to store "value". Leading zero bytes
9415
+ * are skipped.
9416
+ */
9417
+ function unsignedNumLength(value) {
9418
+ // This is just the number of bytes for the unsigned representation of the number.
9419
+ const numBits = LONG_SIZE - numberOfLeadingZeros(value);
9420
+ return Math.ceil(numBits / BYTE_SIZE);
9421
+ }
9422
+ /**
9423
+ * OrderedCodeWriter is a minimal-allocation implementation of the writing
9424
+ * behavior defined by the backend.
9425
+ *
9426
+ * The code is ported from its Java counterpart.
9427
+ */
9428
+ class OrderedCodeWriter {
9429
+ constructor() {
9430
+ this.buffer = new Uint8Array(DEFAULT_BUFFER_SIZE);
9431
+ this.position = 0;
9432
+ }
9433
+ writeBytesAscending(value) {
9434
+ const it = value[Symbol.iterator]();
9435
+ let byte = it.next();
9436
+ while (!byte.done) {
9437
+ this.writeByteAscending(byte.value);
9438
+ byte = it.next();
9439
+ }
9440
+ this.writeSeparatorAscending();
9441
+ }
9442
+ writeBytesDescending(value) {
9443
+ const it = value[Symbol.iterator]();
9444
+ let byte = it.next();
9445
+ while (!byte.done) {
9446
+ this.writeByteDescending(byte.value);
9447
+ byte = it.next();
9448
+ }
9449
+ this.writeSeparatorDescending();
9450
+ }
9451
+ /** Writes utf8 bytes into this byte sequence, ascending. */
9452
+ writeUtf8Ascending(sequence) {
9453
+ for (const c of sequence) {
9454
+ const charCode = c.charCodeAt(0);
9455
+ if (charCode < 0x80) {
9456
+ this.writeByteAscending(charCode);
9457
+ }
9458
+ else if (charCode < 0x800) {
9459
+ this.writeByteAscending((0x0f << 6) | (charCode >>> 6));
9460
+ this.writeByteAscending(0x80 | (0x3f & charCode));
9461
+ }
9462
+ else if (c < MIN_SURROGATE || MAX_SURROGATE < c) {
9463
+ this.writeByteAscending((0x0f << 5) | (charCode >>> 12));
9464
+ this.writeByteAscending(0x80 | (0x3f & (charCode >>> 6)));
9465
+ this.writeByteAscending(0x80 | (0x3f & charCode));
9466
+ }
9467
+ else {
9468
+ const codePoint = c.codePointAt(0);
9469
+ this.writeByteAscending((0x0f << 4) | (codePoint >>> 18));
9470
+ this.writeByteAscending(0x80 | (0x3f & (codePoint >>> 12)));
9471
+ this.writeByteAscending(0x80 | (0x3f & (codePoint >>> 6)));
9472
+ this.writeByteAscending(0x80 | (0x3f & codePoint));
9473
+ }
9474
+ }
9475
+ this.writeSeparatorAscending();
9476
+ }
9477
+ /** Writes utf8 bytes into this byte sequence, descending */
9478
+ writeUtf8Descending(sequence) {
9479
+ for (const c of sequence) {
9480
+ const charCode = c.charCodeAt(0);
9481
+ if (charCode < 0x80) {
9482
+ this.writeByteDescending(charCode);
9483
+ }
9484
+ else if (charCode < 0x800) {
9485
+ this.writeByteDescending((0x0f << 6) | (charCode >>> 6));
9486
+ this.writeByteDescending(0x80 | (0x3f & charCode));
9487
+ }
9488
+ else if (c < MIN_SURROGATE || MAX_SURROGATE < c) {
9489
+ this.writeByteDescending((0x0f << 5) | (charCode >>> 12));
9490
+ this.writeByteDescending(0x80 | (0x3f & (charCode >>> 6)));
9491
+ this.writeByteDescending(0x80 | (0x3f & charCode));
9492
+ }
9493
+ else {
9494
+ const codePoint = c.codePointAt(0);
9495
+ this.writeByteDescending((0x0f << 4) | (codePoint >>> 18));
9496
+ this.writeByteDescending(0x80 | (0x3f & (codePoint >>> 12)));
9497
+ this.writeByteDescending(0x80 | (0x3f & (codePoint >>> 6)));
9498
+ this.writeByteDescending(0x80 | (0x3f & codePoint));
9499
+ }
9500
+ }
9501
+ this.writeSeparatorDescending();
9502
+ }
9503
+ writeNumberAscending(val) {
9504
+ // Values are encoded with a single byte length prefix, followed by the
9505
+ // actual value in big-endian format with leading 0 bytes dropped.
9506
+ const value = this.toOrderedBits(val);
9507
+ const len = unsignedNumLength(value);
9508
+ this.ensureAvailable(1 + len);
9509
+ this.buffer[this.position++] = len & 0xff; // Write the length
9510
+ for (let i = value.length - len; i < value.length; ++i) {
9511
+ this.buffer[this.position++] = value[i] & 0xff;
9512
+ }
9513
+ }
9514
+ writeNumberDescending(val) {
9515
+ // Values are encoded with a single byte length prefix, followed by the
9516
+ // inverted value in big-endian format with leading 0 bytes dropped.
9517
+ const value = this.toOrderedBits(val);
9518
+ const len = unsignedNumLength(value);
9519
+ this.ensureAvailable(1 + len);
9520
+ this.buffer[this.position++] = ~(len & 0xff); // Write the length
9521
+ for (let i = value.length - len; i < value.length; ++i) {
9522
+ this.buffer[this.position++] = ~(value[i] & 0xff);
9523
+ }
9524
+ }
9525
+ /**
9526
+ * Writes the "infinity" byte sequence that sorts after all other byte
9527
+ * sequences written in ascending order.
9528
+ */
9529
+ writeInfinityAscending() {
9530
+ this.writeEscapedByteAscending(ESCAPE2);
9531
+ this.writeEscapedByteAscending(INFINITY);
9532
+ }
9533
+ /**
9534
+ * Writes the "infinity" byte sequence that sorts before all other byte
9535
+ * sequences written in descending order.
9536
+ */
9537
+ writeInfinityDescending() {
9538
+ this.writeEscapedByteDescending(ESCAPE2);
9539
+ this.writeEscapedByteDescending(INFINITY);
9540
+ }
9541
+ /**
9542
+ * Resets the buffer such that it is the same as when it was newly
9543
+ * constructed.
9544
+ */
9545
+ reset() {
9546
+ this.position = 0;
9547
+ }
9548
+ seed(encodedBytes) {
9549
+ this.ensureAvailable(encodedBytes.length);
9550
+ this.buffer.set(encodedBytes, this.position);
9551
+ this.position += encodedBytes.length;
9552
+ }
9553
+ /** Makes a copy of the encoded bytes in this buffer. */
9554
+ encodedBytes() {
9555
+ return this.buffer.slice(0, this.position);
9556
+ }
9557
+ /**
9558
+ * Encodes `val` into an encoding so that the order matches the IEEE 754
9559
+ * floating-point comparison results with the following exceptions:
9560
+ * -0.0 < 0.0
9561
+ * all non-NaN < NaN
9562
+ * NaN = NaN
9563
+ */
9564
+ toOrderedBits(val) {
9565
+ const value = doubleToLongBits(val);
9566
+ // Check if the first bit is set. We use a bit mask since value[0] is
9567
+ // encoded as a number from 0 to 255.
9568
+ const isNegative = (value[0] & 0x80) !== 0;
9569
+ // Revert the two complement to get natural ordering
9570
+ value[0] ^= isNegative ? 0xff : 0x80;
9571
+ for (let i = 1; i < value.length; ++i) {
9572
+ value[i] ^= isNegative ? 0xff : 0x00;
9573
+ }
9574
+ return value;
9575
+ }
9576
+ /** Writes a single byte ascending to the buffer. */
9577
+ writeByteAscending(b) {
9578
+ const masked = b & 0xff;
9579
+ if (masked === ESCAPE1) {
9580
+ this.writeEscapedByteAscending(ESCAPE1);
9581
+ this.writeEscapedByteAscending(NULL_BYTE);
9582
+ }
9583
+ else if (masked === ESCAPE2) {
9584
+ this.writeEscapedByteAscending(ESCAPE2);
9585
+ this.writeEscapedByteAscending(FF_BYTE);
9586
+ }
9587
+ else {
9588
+ this.writeEscapedByteAscending(masked);
9589
+ }
9590
+ }
9591
+ /** Writes a single byte descending to the buffer. */
9592
+ writeByteDescending(b) {
9593
+ const masked = b & 0xff;
9594
+ if (masked === ESCAPE1) {
9595
+ this.writeEscapedByteDescending(ESCAPE1);
9596
+ this.writeEscapedByteDescending(NULL_BYTE);
9597
+ }
9598
+ else if (masked === ESCAPE2) {
9599
+ this.writeEscapedByteDescending(ESCAPE2);
9600
+ this.writeEscapedByteDescending(FF_BYTE);
9601
+ }
9602
+ else {
9603
+ this.writeEscapedByteDescending(b);
9604
+ }
9605
+ }
9606
+ writeSeparatorAscending() {
9607
+ this.writeEscapedByteAscending(ESCAPE1);
9608
+ this.writeEscapedByteAscending(SEPARATOR);
9609
+ }
9610
+ writeSeparatorDescending() {
9611
+ this.writeEscapedByteDescending(ESCAPE1);
9612
+ this.writeEscapedByteDescending(SEPARATOR);
9613
+ }
9614
+ writeEscapedByteAscending(b) {
9615
+ this.ensureAvailable(1);
9616
+ this.buffer[this.position++] = b;
9617
+ }
9618
+ writeEscapedByteDescending(b) {
9619
+ this.ensureAvailable(1);
9620
+ this.buffer[this.position++] = ~b;
9621
+ }
9622
+ ensureAvailable(bytes) {
9623
+ const minCapacity = bytes + this.position;
9624
+ if (minCapacity <= this.buffer.length) {
9625
+ return;
9626
+ }
9627
+ // Try doubling.
9628
+ let newLength = this.buffer.length * 2;
9629
+ // Still not big enough? Just allocate the right size.
9630
+ if (newLength < minCapacity) {
9631
+ newLength = minCapacity;
9632
+ }
9633
+ // Create the new buffer.
9634
+ const newBuffer = new Uint8Array(newLength);
9635
+ newBuffer.set(this.buffer); // copy old data
9636
+ this.buffer = newBuffer;
9637
+ }
9638
+ }
9639
+
9640
+ class AscendingIndexByteEncoder {
9641
+ constructor(orderedCode) {
9642
+ this.orderedCode = orderedCode;
9643
+ }
9644
+ writeBytes(value) {
9645
+ this.orderedCode.writeBytesAscending(value);
9646
+ }
9647
+ writeString(value) {
9648
+ this.orderedCode.writeUtf8Ascending(value);
9649
+ }
9650
+ writeNumber(value) {
9651
+ this.orderedCode.writeNumberAscending(value);
9652
+ }
9653
+ writeInfinity() {
9654
+ this.orderedCode.writeInfinityAscending();
9655
+ }
8580
9656
  }
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
- };
9657
+ class DescendingIndexByteEncoder {
9658
+ constructor(orderedCode) {
9659
+ this.orderedCode = orderedCode;
9660
+ }
9661
+ writeBytes(value) {
9662
+ this.orderedCode.writeBytesDescending(value);
9663
+ }
9664
+ writeString(value) {
9665
+ this.orderedCode.writeUtf8Descending(value);
9666
+ }
9667
+ writeNumber(value) {
9668
+ this.orderedCode.writeNumberDescending(value);
9669
+ }
9670
+ writeInfinity() {
9671
+ this.orderedCode.writeInfinityDescending();
9672
+ }
8588
9673
  }
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
- };
9674
+ /**
9675
+ * Implements `DirectionalIndexByteEncoder` using `OrderedCodeWriter` for the
9676
+ * actual encoding.
9677
+ */
9678
+ class IndexByteEncoder {
9679
+ constructor() {
9680
+ this.orderedCode = new OrderedCodeWriter();
9681
+ this.ascending = new AscendingIndexByteEncoder(this.orderedCode);
9682
+ this.descending = new DescendingIndexByteEncoder(this.orderedCode);
9683
+ }
9684
+ seed(encodedBytes) {
9685
+ this.orderedCode.seed(encodedBytes);
9686
+ }
9687
+ forKind(kind) {
9688
+ return kind === 0 /* ASCENDING */ ? this.ascending : this.descending;
9689
+ }
9690
+ encodedBytes() {
9691
+ return this.orderedCode.encodedBytes();
9692
+ }
9693
+ reset() {
9694
+ this.orderedCode.reset();
9695
+ }
8596
9696
  }
8597
9697
 
8598
9698
  /**
8599
9699
  * @license
8600
- * Copyright 2020 Google LLC
9700
+ * Copyright 2022 Google LLC
8601
9701
  *
8602
9702
  * Licensed under the Apache License, Version 2.0 (the "License");
8603
9703
  * you may not use this file except in compliance with the License.
@@ -8611,45 +9711,38 @@ function fromBundleMetadata(metadata) {
8611
9711
  * See the License for the specific language governing permissions and
8612
9712
  * limitations under the License.
8613
9713
  */
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
- });
9714
+ /** Represents an index entry saved by the SDK in persisted storage. */
9715
+ class IndexEntry {
9716
+ constructor(indexId, documentKey, arrayValue, directionalValue) {
9717
+ this.indexId = indexId;
9718
+ this.documentKey = documentKey;
9719
+ this.arrayValue = arrayValue;
9720
+ this.directionalValue = directionalValue;
8624
9721
  }
8625
- saveBundleMetadata(transaction, bundleMetadata) {
8626
- return bundlesStore(transaction).put(toDbBundle(bundleMetadata));
9722
+ }
9723
+ function indexEntryComparator(left, right) {
9724
+ let cmp = left.indexId - right.indexId;
9725
+ if (cmp !== 0) {
9726
+ return cmp;
8627
9727
  }
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
- });
9728
+ cmp = DocumentKey.comparator(left.documentKey, right.documentKey);
9729
+ if (cmp !== 0) {
9730
+ return cmp;
8637
9731
  }
8638
- saveNamedQuery(transaction, query) {
8639
- return namedQueriesStore(transaction).put(toDbNamedQuery(query));
9732
+ cmp = compareByteArrays(left.arrayValue, right.arrayValue);
9733
+ if (cmp !== 0) {
9734
+ return cmp;
8640
9735
  }
9736
+ return compareByteArrays(left.directionalValue, right.directionalValue);
8641
9737
  }
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);
9738
+ function compareByteArrays(left, right) {
9739
+ for (let i = 0; i < left.length && i < right.length; ++i) {
9740
+ const compare = left[i] - right[i];
9741
+ if (compare !== 0) {
9742
+ return compare;
9743
+ }
9744
+ }
9745
+ return left.length - right.length;
8653
9746
  }
8654
9747
 
8655
9748
  /**
@@ -8682,6 +9775,38 @@ class MemoryIndexManager {
8682
9775
  getCollectionParents(transaction, collectionId) {
8683
9776
  return PersistencePromise.resolve(this.collectionParentIndex.getEntries(collectionId));
8684
9777
  }
9778
+ addFieldIndex(transaction, index) {
9779
+ // Field indices are not supported with memory persistence.
9780
+ return PersistencePromise.resolve();
9781
+ }
9782
+ deleteFieldIndex(transaction, index) {
9783
+ // Field indices are not supported with memory persistence.
9784
+ return PersistencePromise.resolve();
9785
+ }
9786
+ getDocumentsMatchingTarget(transaction, fieldIndex, target) {
9787
+ // Field indices are not supported with memory persistence.
9788
+ return PersistencePromise.resolve(documentKeySet());
9789
+ }
9790
+ getFieldIndex(transaction, target) {
9791
+ // Field indices are not supported with memory persistence.
9792
+ return PersistencePromise.resolve(null);
9793
+ }
9794
+ getFieldIndexes(transaction, collectionGroup) {
9795
+ // Field indices are not supported with memory persistence.
9796
+ return PersistencePromise.resolve([]);
9797
+ }
9798
+ getNextCollectionGroupToUpdate(transaction) {
9799
+ // Field indices are not supported with memory persistence.
9800
+ return PersistencePromise.resolve(null);
9801
+ }
9802
+ updateCollectionGroup(transaction, collectionGroup, offset) {
9803
+ // Field indices are not supported with memory persistence.
9804
+ return PersistencePromise.resolve();
9805
+ }
9806
+ updateIndexEntries(transaction, documents) {
9807
+ // Field indices are not supported with memory persistence.
9808
+ return PersistencePromise.resolve();
9809
+ }
8685
9810
  }
8686
9811
  /**
8687
9812
  * Internal implementation of the collection-parent index exposed by MemoryIndexManager.
@@ -8731,19 +9856,25 @@ class MemoryCollectionParentIndex {
8731
9856
  * See the License for the specific language governing permissions and
8732
9857
  * limitations under the License.
8733
9858
  */
9859
+ const LOG_TAG$f = 'IndexedDbIndexManager';
8734
9860
  /**
8735
9861
  * A persisted implementation of IndexManager.
9862
+ *
9863
+ * PORTING NOTE: Unlike iOS and Android, the Web SDK does not memoize index
9864
+ * data as it supports multi-tab access.
8736
9865
  */
8737
9866
  class IndexedDbIndexManager {
8738
- constructor() {
9867
+ constructor(user) {
9868
+ this.user = user;
8739
9869
  /**
8740
9870
  * An in-memory copy of the index entries we've already written since the SDK
8741
9871
  * launched. Used to avoid re-writing the same entry repeatedly.
8742
9872
  *
8743
- * This is *NOT* a complete cache of what's in persistence and so can never be used to
8744
- * satisfy reads.
9873
+ * This is *NOT* a complete cache of what's in persistence and so can never be
9874
+ * used to satisfy reads.
8745
9875
  */
8746
9876
  this.collectionParentsCache = new MemoryCollectionParentIndex();
9877
+ this.uid = user.uid || '';
8747
9878
  }
8748
9879
  /**
8749
9880
  * Adds a new entry to the collection parent index.
@@ -8790,6 +9921,198 @@ class IndexedDbIndexManager {
8790
9921
  return parentPaths;
8791
9922
  });
8792
9923
  }
9924
+ addFieldIndex(transaction, index) {
9925
+ // TODO(indexing): Verify that the auto-incrementing index ID works in
9926
+ // Safari & Firefox.
9927
+ const indexes = indexConfigurationStore(transaction);
9928
+ const dbIndex = toDbIndexConfiguration(index);
9929
+ delete dbIndex.indexId; // `indexId` is auto-populated by IndexedDb
9930
+ return indexes.add(dbIndex).next();
9931
+ }
9932
+ deleteFieldIndex(transaction, index) {
9933
+ const indexes = indexConfigurationStore(transaction);
9934
+ const states = indexStateStore(transaction);
9935
+ const entries = indexEntriesStore(transaction);
9936
+ return indexes
9937
+ .delete(index.indexId)
9938
+ .next(() => states.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
9939
+ /*lowerOpen=*/ false,
9940
+ /*upperOpen=*/ true)))
9941
+ .next(() => entries.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
9942
+ /*lowerOpen=*/ false,
9943
+ /*upperOpen=*/ true)));
9944
+ }
9945
+ getDocumentsMatchingTarget(transaction, fieldIndex, target) {
9946
+ // TODO(indexing): Implement
9947
+ return PersistencePromise.resolve(documentKeySet());
9948
+ }
9949
+ getFieldIndex(transaction, target) {
9950
+ // TODO(indexing): Implement
9951
+ return PersistencePromise.resolve(null);
9952
+ }
9953
+ /**
9954
+ * Returns the byte encoded form of the directional values in the field index.
9955
+ * Returns `null` if the document does not have all fields specified in the
9956
+ * index.
9957
+ */
9958
+ encodeDirectionalElements(fieldIndex, document) {
9959
+ const encoder = new IndexByteEncoder();
9960
+ for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
9961
+ const field = document.data.field(segment.fieldPath);
9962
+ if (field == null) {
9963
+ return null;
9964
+ }
9965
+ const directionalEncoder = encoder.forKind(segment.kind);
9966
+ FirestoreIndexValueWriter.INSTANCE.writeIndexValue(field, directionalEncoder);
9967
+ }
9968
+ return encoder.encodedBytes();
9969
+ }
9970
+ /** Encodes a single value to the ascending index format. */
9971
+ encodeSingleElement(value) {
9972
+ const encoder = new IndexByteEncoder();
9973
+ FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, encoder.forKind(0 /* ASCENDING */));
9974
+ return encoder.encodedBytes();
9975
+ }
9976
+ getFieldIndexes(transaction, collectionGroup) {
9977
+ const indexes = indexConfigurationStore(transaction);
9978
+ const states = indexStateStore(transaction);
9979
+ return (collectionGroup
9980
+ ? indexes.loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
9981
+ : indexes.loadAll()).next(indexConfigs => {
9982
+ const result = [];
9983
+ return PersistencePromise.forEach(indexConfigs, (indexConfig) => {
9984
+ return states
9985
+ .get([indexConfig.indexId, this.uid])
9986
+ .next(indexState => {
9987
+ result.push(fromDbIndexConfiguration(indexConfig, indexState));
9988
+ });
9989
+ }).next(() => result);
9990
+ });
9991
+ }
9992
+ getNextCollectionGroupToUpdate(transaction) {
9993
+ return this.getFieldIndexes(transaction).next(indexes => {
9994
+ if (indexes.length === 0) {
9995
+ return null;
9996
+ }
9997
+ indexes.sort((l, r) => l.indexState.sequenceNumber - r.indexState.sequenceNumber);
9998
+ return indexes[0].collectionGroup;
9999
+ });
10000
+ }
10001
+ updateCollectionGroup(transaction, collectionGroup, offset) {
10002
+ const indexes = indexConfigurationStore(transaction);
10003
+ const states = indexStateStore(transaction);
10004
+ return this.getNextSequenceNumber(transaction).next(nextSequenceNumber => indexes
10005
+ .loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
10006
+ .next(configs => PersistencePromise.forEach(configs, (config) => states.put(toDbIndexState(config.indexId, this.user, nextSequenceNumber, offset)))));
10007
+ }
10008
+ updateIndexEntries(transaction, documents) {
10009
+ // Porting Note: `getFieldIndexes()` on Web does not cache index lookups as
10010
+ // it could be used across different IndexedDB transactions. As any cached
10011
+ // data might be invalidated by other multi-tab clients, we can only trust
10012
+ // data within a single IndexedDB transaction. We therefore add a cache
10013
+ // here.
10014
+ const memoizedIndexes = new Map();
10015
+ return PersistencePromise.forEach(documents, (key, doc) => {
10016
+ const memoizedCollectionIndexes = memoizedIndexes.get(key.collectionGroup);
10017
+ const fieldIndexes = memoizedCollectionIndexes
10018
+ ? PersistencePromise.resolve(memoizedCollectionIndexes)
10019
+ : this.getFieldIndexes(transaction, key.collectionGroup);
10020
+ return fieldIndexes.next(fieldIndexes => {
10021
+ memoizedIndexes.set(key.collectionGroup, fieldIndexes);
10022
+ return PersistencePromise.forEach(fieldIndexes, (fieldIndex) => {
10023
+ return this.getExistingIndexEntries(transaction, key, fieldIndex).next(existingEntries => {
10024
+ const newEntries = this.computeIndexEntries(doc, fieldIndex);
10025
+ if (!existingEntries.isEqual(newEntries)) {
10026
+ return this.updateEntries(transaction, doc, existingEntries, newEntries);
10027
+ }
10028
+ return PersistencePromise.resolve();
10029
+ });
10030
+ });
10031
+ });
10032
+ });
10033
+ }
10034
+ addIndexEntry(transaction, document, indexEntry) {
10035
+ const indexEntries = indexEntriesStore(transaction);
10036
+ return indexEntries.put(new DbIndexEntry(indexEntry.indexId, this.uid, indexEntry.arrayValue, indexEntry.directionalValue, encodeResourcePath(document.key.path)));
10037
+ }
10038
+ deleteIndexEntry(transaction, document, indexEntry) {
10039
+ const indexEntries = indexEntriesStore(transaction);
10040
+ return indexEntries.delete([
10041
+ indexEntry.indexId,
10042
+ this.uid,
10043
+ indexEntry.arrayValue,
10044
+ indexEntry.directionalValue,
10045
+ encodeResourcePath(document.key.path)
10046
+ ]);
10047
+ }
10048
+ getExistingIndexEntries(transaction, documentKey, fieldIndex) {
10049
+ const indexEntries = indexEntriesStore(transaction);
10050
+ let results = new SortedSet(indexEntryComparator);
10051
+ return indexEntries
10052
+ .iterate({
10053
+ index: DbIndexEntry.documentKeyIndex,
10054
+ range: IDBKeyRange.only([
10055
+ fieldIndex.indexId,
10056
+ this.uid,
10057
+ encodeResourcePath(documentKey.path)
10058
+ ])
10059
+ }, (_, entry) => {
10060
+ results = results.add(new IndexEntry(fieldIndex.indexId, documentKey, entry.arrayValue, entry.directionalValue));
10061
+ })
10062
+ .next(() => results);
10063
+ }
10064
+ /** Creates the index entries for the given document. */
10065
+ computeIndexEntries(document, fieldIndex) {
10066
+ let results = new SortedSet(indexEntryComparator);
10067
+ const directionalValue = this.encodeDirectionalElements(fieldIndex, document);
10068
+ if (directionalValue == null) {
10069
+ return results;
10070
+ }
10071
+ const arraySegment = fieldIndexGetArraySegment(fieldIndex);
10072
+ if (arraySegment != null) {
10073
+ const value = document.data.field(arraySegment.fieldPath);
10074
+ if (isArray(value)) {
10075
+ for (const arrayValue of value.arrayValue.values || []) {
10076
+ results = results.add(new IndexEntry(fieldIndex.indexId, document.key, this.encodeSingleElement(arrayValue), directionalValue));
10077
+ }
10078
+ }
10079
+ }
10080
+ else {
10081
+ results = results.add(new IndexEntry(fieldIndex.indexId, document.key, new Uint8Array(), directionalValue));
10082
+ }
10083
+ return results;
10084
+ }
10085
+ /**
10086
+ * Updates the index entries for the provided document by deleting entries
10087
+ * that are no longer referenced in `newEntries` and adding all newly added
10088
+ * entries.
10089
+ */
10090
+ updateEntries(transaction, document, existingEntries, newEntries) {
10091
+ logDebug(LOG_TAG$f, "Updating index entries for document '%s'", document.key);
10092
+ const promises = [];
10093
+ diffSortedSets(existingEntries, newEntries, indexEntryComparator,
10094
+ /* onAdd= */ entry => {
10095
+ promises.push(this.addIndexEntry(transaction, document, entry));
10096
+ },
10097
+ /* onRemove= */ entry => {
10098
+ promises.push(this.deleteIndexEntry(transaction, document, entry));
10099
+ });
10100
+ return PersistencePromise.waitFor(promises);
10101
+ }
10102
+ getNextSequenceNumber(transaction) {
10103
+ let nextSequenceNumber = 1;
10104
+ const states = indexStateStore(transaction);
10105
+ return states
10106
+ .iterate({
10107
+ index: DbIndexState.sequenceNumberIndex,
10108
+ reverse: true,
10109
+ range: IDBKeyRange.upperBound([this.uid, Number.MAX_SAFE_INTEGER])
10110
+ }, (_, state, controller) => {
10111
+ controller.done();
10112
+ nextSequenceNumber = state.sequenceNumber + 1;
10113
+ })
10114
+ .next(() => nextSequenceNumber);
10115
+ }
8793
10116
  }
8794
10117
  /**
8795
10118
  * Helper to get a typed SimpleDbStore for the collectionParents
@@ -8797,6 +10120,24 @@ class IndexedDbIndexManager {
8797
10120
  */
8798
10121
  function collectionParentsStore(txn) {
8799
10122
  return getStore(txn, DbCollectionParent.store);
10123
+ }
10124
+ /**
10125
+ * Helper to get a typed SimpleDbStore for the index entry object store.
10126
+ */
10127
+ function indexEntriesStore(txn) {
10128
+ return getStore(txn, DbIndexEntry.store);
10129
+ }
10130
+ /**
10131
+ * Helper to get a typed SimpleDbStore for the index configuration object store.
10132
+ */
10133
+ function indexConfigurationStore(txn) {
10134
+ return getStore(txn, DbIndexConfiguration.store);
10135
+ }
10136
+ /**
10137
+ * Helper to get a typed SimpleDbStore for the index state object store.
10138
+ */
10139
+ function indexStateStore(txn) {
10140
+ return getStore(txn, DbIndexState.store);
8800
10141
  }
8801
10142
 
8802
10143
  /**
@@ -10266,12 +11607,10 @@ class RemoteDocumentChangeBuffer {
10266
11607
  * `newIndexedDbRemoteDocumentCache()`.
10267
11608
  */
10268
11609
  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) {
11610
+ constructor(serializer) {
10274
11611
  this.serializer = serializer;
11612
+ }
11613
+ setIndexManager(indexManager) {
10275
11614
  this.indexManager = indexManager;
10276
11615
  }
10277
11616
  /**
@@ -10393,21 +11732,21 @@ class IndexedDbRemoteDocumentCacheImpl {
10393
11732
  }
10394
11733
  });
10395
11734
  }
10396
- getDocumentsMatchingQuery(transaction, query, sinceReadTime) {
11735
+ getAll(transaction, collection, sinceReadTime) {
10397
11736
  let results = mutableDocumentMap();
10398
- const immediateChildrenPathLength = query.path.length + 1;
11737
+ const immediateChildrenPathLength = collection.length + 1;
10399
11738
  const iterationOptions = {};
10400
11739
  if (sinceReadTime.isEqual(SnapshotVersion.min())) {
10401
11740
  // Documents are ordered by key, so we can use a prefix scan to narrow
10402
11741
  // down the documents we need to match the query against.
10403
- const startKey = query.path.toArray();
11742
+ const startKey = collection.toArray();
10404
11743
  iterationOptions.range = IDBKeyRange.lowerBound(startKey);
10405
11744
  }
10406
11745
  else {
10407
11746
  // Execute an index-free query and filter by read time. This is safe
10408
11747
  // since all document changes to queries that have a
10409
11748
  // lastLimboFreeSnapshotVersion (`sinceReadTime`) have a read time set.
10410
- const collectionKey = query.path.toArray();
11749
+ const collectionKey = collection.toArray();
10411
11750
  const readTimeKey = toDbTimestampKey(sinceReadTime);
10412
11751
  iterationOptions.range = IDBKeyRange.lowerBound([collectionKey, readTimeKey],
10413
11752
  /* open= */ true);
@@ -10424,12 +11763,12 @@ class IndexedDbRemoteDocumentCacheImpl {
10424
11763
  return;
10425
11764
  }
10426
11765
  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)) {
11766
+ if (collection.isPrefixOf(document.key.path)) {
10431
11767
  results = results.insert(document.key, document);
10432
11768
  }
11769
+ else {
11770
+ control.done();
11771
+ }
10433
11772
  })
10434
11773
  .next(() => results);
10435
11774
  }
@@ -10467,14 +11806,9 @@ class IndexedDbRemoteDocumentCacheImpl {
10467
11806
  return MutableDocument.newInvalidDocument(documentKey);
10468
11807
  }
10469
11808
  }
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);
11809
+ /** Creates a new IndexedDbRemoteDocumentCache. */
11810
+ function newIndexedDbRemoteDocumentCache(serializer) {
11811
+ return new IndexedDbRemoteDocumentCacheImpl(serializer);
10478
11812
  }
10479
11813
  /**
10480
11814
  * Returns the set of documents that have changed since the specified read
@@ -10641,9 +11975,6 @@ class SchemaConverter {
10641
11975
  * and local feature development.
10642
11976
  */
10643
11977
  createOrUpgrade(db, txn, fromVersion, toVersion) {
10644
- hardAssert(fromVersion < toVersion &&
10645
- fromVersion >= 0 &&
10646
- toVersion <= SCHEMA_VERSION);
10647
11978
  const simpleDbTransaction = new SimpleDbTransaction('createOrUpgrade', txn);
10648
11979
  if (fromVersion < 1 && toVersion >= 1) {
10649
11980
  createPrimaryClientStore(db);
@@ -10710,6 +12041,16 @@ class SchemaConverter {
10710
12041
  createNamedQueriesStore(db);
10711
12042
  });
10712
12043
  }
12044
+ if (fromVersion < 12 && toVersion >= 12) {
12045
+ p = p.next(() => {
12046
+ createDocumentOverlayStore(db);
12047
+ });
12048
+ }
12049
+ if (fromVersion < 13 && toVersion >= 13) {
12050
+ p = p.next(() => {
12051
+ createFieldIndex(db);
12052
+ });
12053
+ }
10713
12054
  return p;
10714
12055
  }
10715
12056
  addDocumentGlobal(txn) {
@@ -10916,6 +12257,28 @@ function createNamedQueriesStore(db) {
10916
12257
  db.createObjectStore(DbNamedQuery.store, {
10917
12258
  keyPath: DbNamedQuery.keyPath
10918
12259
  });
12260
+ }
12261
+ function createFieldIndex(db) {
12262
+ const indexConfiguratioStore = db.createObjectStore(DbIndexConfiguration.store, {
12263
+ keyPath: DbIndexConfiguration.keyPath,
12264
+ autoIncrement: true
12265
+ });
12266
+ indexConfiguratioStore.createIndex(DbIndexConfiguration.collectionGroupIndex, DbIndexConfiguration.collectionGroupIndexPath, { unique: false });
12267
+ const indexStateStore = db.createObjectStore(DbIndexState.store, {
12268
+ keyPath: DbIndexState.keyPath
12269
+ });
12270
+ indexStateStore.createIndex(DbIndexState.sequenceNumberIndex, DbIndexState.sequenceNumberIndexPath, { unique: false });
12271
+ const indexEntryStore = db.createObjectStore(DbIndexEntry.store, {
12272
+ keyPath: DbIndexEntry.keyPath
12273
+ });
12274
+ indexEntryStore.createIndex(DbIndexEntry.documentKeyIndex, DbIndexEntry.documentKeyIndexPath, { unique: false });
12275
+ }
12276
+ function createDocumentOverlayStore(db) {
12277
+ const documentOverlayStore = db.createObjectStore(DbDocumentOverlay.store, {
12278
+ keyPath: DbDocumentOverlay.keyPath
12279
+ });
12280
+ documentOverlayStore.createIndex(DbDocumentOverlay.collectionPathOverlayIndex, DbDocumentOverlay.collectionPathOverlayIndexPath, { unique: false });
12281
+ documentOverlayStore.createIndex(DbDocumentOverlay.collectionGroupOverlayIndex, DbDocumentOverlay.collectionGroupOverlayIndexPath, { unique: false });
10919
12282
  }
10920
12283
 
10921
12284
  /**
@@ -11027,7 +12390,7 @@ class IndexedDbPersistence {
11027
12390
  * If set to true, forcefully obtains database access. Existing tabs will
11028
12391
  * no longer be able to access IndexedDB.
11029
12392
  */
11030
- forceOwningTab) {
12393
+ forceOwningTab, schemaVersion = SCHEMA_VERSION) {
11031
12394
  this.allowTabSynchronization = allowTabSynchronization;
11032
12395
  this.persistenceKey = persistenceKey;
11033
12396
  this.clientId = clientId;
@@ -11036,6 +12399,7 @@ class IndexedDbPersistence {
11036
12399
  this.document = document;
11037
12400
  this.sequenceNumberSyncer = sequenceNumberSyncer;
11038
12401
  this.forceOwningTab = forceOwningTab;
12402
+ this.schemaVersion = schemaVersion;
11039
12403
  this.listenSequence = null;
11040
12404
  this._started = false;
11041
12405
  this.isPrimary = false;
@@ -11057,10 +12421,9 @@ class IndexedDbPersistence {
11057
12421
  this.referenceDelegate = new IndexedDbLruDelegateImpl(this, lruParams);
11058
12422
  this.dbName = persistenceKey + MAIN_DATABASE;
11059
12423
  this.serializer = new LocalSerializer(serializer);
11060
- this.simpleDb = new SimpleDb(this.dbName, SCHEMA_VERSION, new SchemaConverter(this.serializer));
12424
+ this.simpleDb = new SimpleDb(this.dbName, this.schemaVersion, new SchemaConverter(this.serializer));
11061
12425
  this.targetCache = new IndexedDbTargetCache(this.referenceDelegate, this.serializer);
11062
- this.indexManager = new IndexedDbIndexManager();
11063
- this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer, this.indexManager);
12426
+ this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer);
11064
12427
  this.bundleCache = new IndexedDbBundleCache();
11065
12428
  if (this.window && this.window.localStorage) {
11066
12429
  this.webStorage = this.window.localStorage;
@@ -11396,8 +12759,8 @@ class IndexedDbPersistence {
11396
12759
  get started() {
11397
12760
  return this._started;
11398
12761
  }
11399
- getMutationQueue(user) {
11400
- return IndexedDbMutationQueue.forUser(user, this.serializer, this.indexManager, this.referenceDelegate);
12762
+ getMutationQueue(user, indexManager) {
12763
+ return IndexedDbMutationQueue.forUser(user, this.serializer, indexManager, this.referenceDelegate);
11401
12764
  }
11402
12765
  getTargetCache() {
11403
12766
  return this.targetCache;
@@ -11405,8 +12768,11 @@ class IndexedDbPersistence {
11405
12768
  getRemoteDocumentCache() {
11406
12769
  return this.remoteDocumentCache;
11407
12770
  }
11408
- getIndexManager() {
11409
- return this.indexManager;
12771
+ getIndexManager(user) {
12772
+ return new IndexedDbIndexManager(user);
12773
+ }
12774
+ getDocumentOverlayCache(user) {
12775
+ return IndexedDbDocumentOverlayCache.forUser(this.serializer, user);
11410
12776
  }
11411
12777
  getBundleCache() {
11412
12778
  return this.bundleCache;
@@ -11414,11 +12780,12 @@ class IndexedDbPersistence {
11414
12780
  runTransaction(action, mode, transactionOperation) {
11415
12781
  logDebug(LOG_TAG$d, 'Starting transaction:', action);
11416
12782
  const simpleDbMode = mode === 'readonly' ? 'readonly' : 'readwrite';
12783
+ const objectStores = getObjectStores(this.schemaVersion);
11417
12784
  let persistenceTransaction;
11418
12785
  // Do all transactions as readwrite against all object stores, since we
11419
12786
  // are the only reader/writer.
11420
12787
  return this.simpleDb
11421
- .runTransaction(action, simpleDbMode, ALL_STORES, simpleDbTxn => {
12788
+ .runTransaction(action, simpleDbMode, objectStores, simpleDbTxn => {
11422
12789
  persistenceTransaction = new IndexedDbTransaction(simpleDbTxn, this.listenSequence
11423
12790
  ? this.listenSequence.next()
11424
12791
  : ListenSequence.INVALID);
@@ -11790,39 +13157,28 @@ class LocalDocumentsView {
11790
13157
  getDocumentsMatchingCollectionQuery(transaction, query, sinceReadTime) {
11791
13158
  // Query the remote documents and overlay mutations.
11792
13159
  let results;
11793
- let mutationBatches;
11794
13160
  return this.remoteDocumentCache
11795
- .getDocumentsMatchingQuery(transaction, query, sinceReadTime)
13161
+ .getAll(transaction, query.path, sinceReadTime)
11796
13162
  .next(queryResults => {
11797
13163
  results = queryResults;
11798
13164
  return this.mutationQueue.getAllMutationBatchesAffectingQuery(transaction, query);
11799
13165
  })
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
- }
13166
+ .next(mutationBatches => {
13167
+ for (const batch of mutationBatches) {
13168
+ for (const mutation of batch.mutations) {
13169
+ const key = mutation.key;
13170
+ let document = results.get(key);
13171
+ if (document == null) {
13172
+ // Create invalid document to apply mutations on top of
13173
+ document = MutableDocument.newInvalidDocument(key);
13174
+ results = results.insert(key, document);
13175
+ }
13176
+ mutationApplyToLocalView(mutation, document, batch.localWriteTime);
13177
+ if (!document.isFoundDocument()) {
13178
+ results = results.remove(key);
11823
13179
  }
11824
13180
  }
11825
- });
13181
+ }
11826
13182
  })
11827
13183
  .next(() => {
11828
13184
  // Finally, filter out any documents that don't actually match
@@ -11835,29 +13191,6 @@ class LocalDocumentsView {
11835
13191
  return results;
11836
13192
  });
11837
13193
  }
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
13194
  }
11862
13195
 
11863
13196
  /**
@@ -11916,11 +13249,18 @@ class LocalStoreImpl {
11916
13249
  * PORTING NOTE: This is only used for multi-tab synchronization.
11917
13250
  */
11918
13251
  this.lastDocumentChangeReadTime = SnapshotVersion.min();
11919
- this.mutationQueue = persistence.getMutationQueue(initialUser);
11920
13252
  this.remoteDocuments = persistence.getRemoteDocumentCache();
11921
13253
  this.targetCache = persistence.getTargetCache();
11922
- this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.persistence.getIndexManager());
11923
13254
  this.bundleCache = persistence.getBundleCache();
13255
+ this.initializeUserComponents(initialUser);
13256
+ }
13257
+ initializeUserComponents(user) {
13258
+ // TODO(indexing): Add spec tests that test these components change after a
13259
+ // user change
13260
+ this.indexManager = this.persistence.getIndexManager(user);
13261
+ this.mutationQueue = this.persistence.getMutationQueue(user, this.indexManager);
13262
+ this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.indexManager);
13263
+ this.remoteDocuments.setIndexManager(this.indexManager);
11924
13264
  this.queryEngine.setLocalDocumentsView(this.localDocuments);
11925
13265
  }
11926
13266
  collectGarbage(garbageCollector) {
@@ -11942,8 +13282,6 @@ persistence, queryEngine, initialUser, serializer) {
11942
13282
  // change.
11943
13283
  async function localStoreHandleUserChange(localStore, user) {
11944
13284
  const localStoreImpl = debugCast(localStore);
11945
- let newMutationQueue = localStoreImpl.mutationQueue;
11946
- let newLocalDocuments = localStoreImpl.localDocuments;
11947
13285
  const result = await localStoreImpl.persistence.runTransaction('Handle user change', 'readonly', txn => {
11948
13286
  // Swap out the mutation queue, grabbing the pending mutation batches
11949
13287
  // before and after.
@@ -11952,11 +13290,8 @@ async function localStoreHandleUserChange(localStore, user) {
11952
13290
  .getAllMutationBatches(txn)
11953
13291
  .next(promisedOldBatches => {
11954
13292
  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);
13293
+ localStoreImpl.initializeUserComponents(user);
13294
+ return localStoreImpl.mutationQueue.getAllMutationBatches(txn);
11960
13295
  })
11961
13296
  .next(newBatches => {
11962
13297
  const removedBatchIds = [];
@@ -11977,7 +13312,7 @@ async function localStoreHandleUserChange(localStore, user) {
11977
13312
  }
11978
13313
  // Return the set of all (potentially) changed documents and the list
11979
13314
  // of mutation batch IDs that were affected by change.
11980
- return newLocalDocuments
13315
+ return localStoreImpl.localDocuments
11981
13316
  .getDocuments(txn, changedKeys)
11982
13317
  .next(affectedDocuments => {
11983
13318
  return {
@@ -11988,9 +13323,6 @@ async function localStoreHandleUserChange(localStore, user) {
11988
13323
  });
11989
13324
  });
11990
13325
  });
11991
- localStoreImpl.mutationQueue = newMutationQueue;
11992
- localStoreImpl.localDocuments = newLocalDocuments;
11993
- localStoreImpl.queryEngine.setLocalDocumentsView(localStoreImpl.localDocuments);
11994
13326
  return result;
11995
13327
  }
11996
13328
  /* Accepts locally generated Mutations and commit them to storage. */
@@ -12703,6 +14035,122 @@ class MemoryBundleCache {
12703
14035
  }
12704
14036
  }
12705
14037
 
14038
+ /**
14039
+ * @license
14040
+ * Copyright 2022 Google LLC
14041
+ *
14042
+ * Licensed under the Apache License, Version 2.0 (the "License");
14043
+ * you may not use this file except in compliance with the License.
14044
+ * You may obtain a copy of the License at
14045
+ *
14046
+ * http://www.apache.org/licenses/LICENSE-2.0
14047
+ *
14048
+ * Unless required by applicable law or agreed to in writing, software
14049
+ * distributed under the License is distributed on an "AS IS" BASIS,
14050
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14051
+ * See the License for the specific language governing permissions and
14052
+ * limitations under the License.
14053
+ */
14054
+ /**
14055
+ * An in-memory implementation of DocumentOverlayCache.
14056
+ */
14057
+ class MemoryDocumentOverlayCache {
14058
+ constructor() {
14059
+ // A map sorted by DocumentKey, whose value is a pair of the largest batch id
14060
+ // for the overlay and the overlay itself.
14061
+ this.overlays = new SortedMap(DocumentKey.comparator);
14062
+ this.overlayByBatchId = new Map();
14063
+ }
14064
+ getOverlay(transaction, key) {
14065
+ return PersistencePromise.resolve(this.overlays.get(key));
14066
+ }
14067
+ saveOverlays(transaction, largestBatchId, overlays) {
14068
+ overlays.forEach(mutation => {
14069
+ this.saveOverlay(transaction, largestBatchId, mutation);
14070
+ });
14071
+ return PersistencePromise.resolve();
14072
+ }
14073
+ removeOverlaysForBatchId(transaction, documentKeys, batchId) {
14074
+ const keys = this.overlayByBatchId.get(batchId);
14075
+ if (keys !== undefined) {
14076
+ keys.forEach(key => (this.overlays = this.overlays.remove(key)));
14077
+ this.overlayByBatchId.delete(batchId);
14078
+ }
14079
+ return PersistencePromise.resolve();
14080
+ }
14081
+ getOverlaysForCollection(transaction, collection, sinceBatchId) {
14082
+ const result = new Map();
14083
+ const immediateChildrenPathLength = collection.length + 1;
14084
+ const prefix = new DocumentKey(collection.child(''));
14085
+ const iter = this.overlays.getIteratorFrom(prefix);
14086
+ while (iter.hasNext()) {
14087
+ const entry = iter.getNext();
14088
+ const overlay = entry.value;
14089
+ const key = overlay.getKey();
14090
+ if (!collection.isPrefixOf(key.path)) {
14091
+ break;
14092
+ }
14093
+ // Documents from sub-collections
14094
+ if (key.path.length !== immediateChildrenPathLength) {
14095
+ continue;
14096
+ }
14097
+ if (overlay.largestBatchId > sinceBatchId) {
14098
+ result.set(overlay.getKey(), overlay);
14099
+ }
14100
+ }
14101
+ return PersistencePromise.resolve(result);
14102
+ }
14103
+ getOverlaysForCollectionGroup(transaction, collectionGroup, sinceBatchId, count) {
14104
+ let batchIdToOverlays = new SortedMap((key1, key2) => key1 - key2);
14105
+ const iter = this.overlays.getIterator();
14106
+ while (iter.hasNext()) {
14107
+ const entry = iter.getNext();
14108
+ const overlay = entry.value;
14109
+ const key = overlay.getKey();
14110
+ if (key.getCollectionGroup() !== collectionGroup) {
14111
+ continue;
14112
+ }
14113
+ if (overlay.largestBatchId > sinceBatchId) {
14114
+ let overlaysForBatchId = batchIdToOverlays.get(overlay.largestBatchId);
14115
+ if (overlaysForBatchId === null) {
14116
+ overlaysForBatchId = new Map();
14117
+ batchIdToOverlays = batchIdToOverlays.insert(overlay.largestBatchId, overlaysForBatchId);
14118
+ }
14119
+ overlaysForBatchId.set(overlay.getKey(), overlay);
14120
+ }
14121
+ }
14122
+ const result = new Map();
14123
+ const batchIter = batchIdToOverlays.getIterator();
14124
+ while (batchIter.hasNext()) {
14125
+ const entry = batchIter.getNext();
14126
+ const overlays = entry.value;
14127
+ overlays.forEach((overlay, key) => result.set(key, overlay));
14128
+ if (result.size >= count) {
14129
+ break;
14130
+ }
14131
+ }
14132
+ return PersistencePromise.resolve(result);
14133
+ }
14134
+ saveOverlay(transaction, largestBatchId, mutation) {
14135
+ if (mutation === null) {
14136
+ return;
14137
+ }
14138
+ // Remove the association of the overlay to its batch id.
14139
+ const existing = this.overlays.get(mutation.key);
14140
+ if (existing !== null) {
14141
+ this.overlayByBatchId.get(existing.largestBatchId).delete(mutation.key);
14142
+ }
14143
+ this.overlays = this.overlays.insert(mutation.key, new Overlay(largestBatchId, mutation));
14144
+ // Create the association of this overlay to the given largestBatchId.
14145
+ let batch = this.overlayByBatchId.get(largestBatchId);
14146
+ if (batch === undefined) {
14147
+ batch = new Set();
14148
+ this.overlayByBatchId.set(largestBatchId, batch);
14149
+ }
14150
+ batch.add(mutation.key);
14151
+ }
14152
+ }
14153
+
12706
14154
  /**
12707
14155
  * @license
12708
14156
  * Copyright 2017 Google LLC
@@ -13056,14 +14504,16 @@ class MemoryRemoteDocumentCacheImpl {
13056
14504
  * expected to just return 0 to avoid unnecessarily doing the work of
13057
14505
  * calculating the size.
13058
14506
  */
13059
- constructor(indexManager, sizer) {
13060
- this.indexManager = indexManager;
14507
+ constructor(sizer) {
13061
14508
  this.sizer = sizer;
13062
14509
  /** Underlying cache of documents and their read times. */
13063
14510
  this.docs = documentEntryMap();
13064
14511
  /** Size of all cached documents. */
13065
14512
  this.size = 0;
13066
14513
  }
14514
+ setIndexManager(indexManager) {
14515
+ this.indexManager = indexManager;
14516
+ }
13067
14517
  /**
13068
14518
  * Adds the supplied entry to the cache and updates the cache size as appropriate.
13069
14519
  *
@@ -13111,21 +14561,22 @@ class MemoryRemoteDocumentCacheImpl {
13111
14561
  });
13112
14562
  return PersistencePromise.resolve(results);
13113
14563
  }
13114
- getDocumentsMatchingQuery(transaction, query, sinceReadTime) {
14564
+ getAll(transaction, collectionPath, sinceReadTime) {
13115
14565
  let results = mutableDocumentMap();
13116
14566
  // Documents are ordered by key, so we can use a prefix scan to narrow down
13117
14567
  // the documents we need to match the query against.
13118
- const prefix = new DocumentKey(query.path.child(''));
14568
+ const prefix = new DocumentKey(collectionPath.child(''));
13119
14569
  const iterator = this.docs.getIteratorFrom(prefix);
13120
14570
  while (iterator.hasNext()) {
13121
14571
  const { key, value: { document } } = iterator.getNext();
13122
- if (!query.path.isPrefixOf(key.path)) {
14572
+ if (!collectionPath.isPrefixOf(key.path)) {
13123
14573
  break;
13124
14574
  }
13125
- if (document.readTime.compareTo(sinceReadTime) <= 0) {
14575
+ if (key.path.length > collectionPath.length + 1) {
14576
+ // Exclude entries from subcollections.
13126
14577
  continue;
13127
14578
  }
13128
- if (!queryMatches(query, document)) {
14579
+ if (document.readTime.compareTo(sinceReadTime) <= 0) {
13129
14580
  continue;
13130
14581
  }
13131
14582
  results = results.insert(document.key, document.mutableCopy());
@@ -13147,13 +14598,12 @@ class MemoryRemoteDocumentCacheImpl {
13147
14598
  /**
13148
14599
  * Creates a new memory-only RemoteDocumentCache.
13149
14600
  *
13150
- * @param indexManager - A class that manages collection group indices.
13151
14601
  * @param sizer - Used to assess the size of a document. For eager GC, this is
13152
14602
  * expected to just return 0 to avoid unnecessarily doing the work of
13153
14603
  * calculating the size.
13154
14604
  */
13155
- function newMemoryRemoteDocumentCache(indexManager, sizer) {
13156
- return new MemoryRemoteDocumentCacheImpl(indexManager, sizer);
14605
+ function newMemoryRemoteDocumentCache(sizer) {
14606
+ return new MemoryRemoteDocumentCacheImpl(sizer);
13157
14607
  }
13158
14608
  /**
13159
14609
  * Handles the details of adding and updating documents in the MemoryRemoteDocumentCache.
@@ -13347,6 +14797,7 @@ class MemoryPersistence {
13347
14797
  */
13348
14798
  constructor(referenceDelegateFactory, serializer) {
13349
14799
  this.mutationQueues = {};
14800
+ this.overlays = {};
13350
14801
  this.listenSequence = new ListenSequence(0);
13351
14802
  this._started = false;
13352
14803
  this._started = true;
@@ -13354,7 +14805,7 @@ class MemoryPersistence {
13354
14805
  this.targetCache = new MemoryTargetCache(this);
13355
14806
  const sizer = (doc) => this.referenceDelegate.documentSize(doc);
13356
14807
  this.indexManager = new MemoryIndexManager();
13357
- this.remoteDocumentCache = newMemoryRemoteDocumentCache(this.indexManager, sizer);
14808
+ this.remoteDocumentCache = newMemoryRemoteDocumentCache(sizer);
13358
14809
  this.serializer = new LocalSerializer(serializer);
13359
14810
  this.bundleCache = new MemoryBundleCache(this.serializer);
13360
14811
  }
@@ -13375,13 +14826,23 @@ class MemoryPersistence {
13375
14826
  setNetworkEnabled() {
13376
14827
  // No op.
13377
14828
  }
13378
- getIndexManager() {
14829
+ getIndexManager(user) {
14830
+ // We do not currently support indices for memory persistence, so we can
14831
+ // return the same shared instance of the memory index manager.
13379
14832
  return this.indexManager;
13380
14833
  }
13381
- getMutationQueue(user) {
14834
+ getDocumentOverlayCache(user) {
14835
+ let overlay = this.overlays[user.toKey()];
14836
+ if (!overlay) {
14837
+ overlay = new MemoryDocumentOverlayCache();
14838
+ this.overlays[user.toKey()] = overlay;
14839
+ }
14840
+ return overlay;
14841
+ }
14842
+ getMutationQueue(user, indexManager) {
13382
14843
  let queue = this.mutationQueues[user.toKey()];
13383
14844
  if (!queue) {
13384
- queue = new MemoryMutationQueue(this.indexManager, this.referenceDelegate);
14845
+ queue = new MemoryMutationQueue(indexManager, this.referenceDelegate);
13385
14846
  this.mutationQueues[user.toKey()] = queue;
13386
14847
  }
13387
14848
  return queue;
@@ -14526,7 +15987,7 @@ const LOG_TAG$9 = 'Connection';
14526
15987
  const X_GOOG_API_CLIENT_VALUE = `gl-node/${process.versions.node} fire/${SDK_VERSION} grpc/${grpcVersion}`;
14527
15988
  function createMetadata(databasePath, authToken, appCheckToken, appId) {
14528
15989
  hardAssert(authToken === null || authToken.type === 'OAuth');
14529
- const metadata = new grpcJs.Metadata();
15990
+ const metadata = new grpc__namespace.Metadata();
14530
15991
  if (authToken) {
14531
15992
  authToken.headers.forEach((value, key) => metadata.set(key, value));
14532
15993
  }
@@ -14557,10 +16018,10 @@ class GrpcConnection {
14557
16018
  ensureActiveStub() {
14558
16019
  if (!this.cachedStub) {
14559
16020
  logDebug(LOG_TAG$9, 'Creating Firestore stub.');
14560
- const credentials$1 = this.databaseInfo.ssl
14561
- ? grpcJs.credentials.createSsl()
14562
- : grpcJs.credentials.createInsecure();
14563
- this.cachedStub = new this.firestore.Firestore(this.databaseInfo.host, credentials$1);
16021
+ const credentials = this.databaseInfo.ssl
16022
+ ? grpc__namespace.credentials.createSsl()
16023
+ : grpc__namespace.credentials.createInsecure();
16024
+ this.cachedStub = new this.firestore.Firestore(this.databaseInfo.host, credentials);
14564
16025
  }
14565
16026
  return this.cachedStub;
14566
16027
  }
@@ -17392,8 +18853,8 @@ const protoLoaderOptions = {
17392
18853
  * @returns The GrpcObject representing our protos.
17393
18854
  */
17394
18855
  function loadProtos() {
17395
- const packageDefinition = protoLoader.fromJSON(protos$1, protoLoaderOptions);
17396
- return grpcJs.loadPackageDefinition(packageDefinition);
18856
+ const packageDefinition = protoLoader__namespace.fromJSON(protos$1, protoLoaderOptions);
18857
+ return grpc__namespace.loadPackageDefinition(packageDefinition);
17397
18858
  }
17398
18859
 
17399
18860
  /**
@@ -20344,12 +21805,12 @@ async function syncEngineListen(syncEngine, query) {
20344
21805
  }
20345
21806
  else {
20346
21807
  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
21808
  if (syncEngineImpl.isPrimaryClient) {
20351
21809
  remoteStoreListen(syncEngineImpl.remoteStore, targetData);
20352
21810
  }
21811
+ const status = syncEngineImpl.sharedClientState.addLocalQueryTarget(targetData.targetId);
21812
+ targetId = targetData.targetId;
21813
+ viewSnapshot = await initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId, status === 'current');
20353
21814
  }
20354
21815
  return viewSnapshot;
20355
21816
  }
@@ -24857,8 +26318,7 @@ class DocumentSnapshot extends DocumentSnapshot$1 {
24857
26318
  this.metadata = metadata;
24858
26319
  }
24859
26320
  /**
24860
- * Property of the `DocumentSnapshot` that signals whether or not the data
24861
- * exists. True if the document exists.
26321
+ * Returns whether or not the data exists. True if the document exists.
24862
26322
  */
24863
26323
  exists() {
24864
26324
  return super.exists();
@@ -25232,51 +26692,53 @@ function limitToLast(limit) {
25232
26692
  return new QueryLimitConstraint('limitToLast', limit, "L" /* Last */);
25233
26693
  }
25234
26694
  class QueryStartAtConstraint extends QueryConstraint {
25235
- constructor(type, _docOrFields, _before) {
26695
+ constructor(type, _docOrFields, _inclusive) {
25236
26696
  super();
25237
26697
  this.type = type;
25238
26698
  this._docOrFields = _docOrFields;
25239
- this._before = _before;
26699
+ this._inclusive = _inclusive;
25240
26700
  }
25241
26701
  _apply(query) {
25242
- const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._before);
26702
+ const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
25243
26703
  return new Query(query.firestore, query.converter, queryWithStartAt(query._query, bound));
25244
26704
  }
25245
26705
  }
25246
26706
  function startAt(...docOrFields) {
25247
- return new QueryStartAtConstraint('startAt', docOrFields, /*before=*/ true);
26707
+ return new QueryStartAtConstraint('startAt', docOrFields,
26708
+ /*inclusive=*/ true);
25248
26709
  }
25249
26710
  function startAfter(...docOrFields) {
25250
26711
  return new QueryStartAtConstraint('startAfter', docOrFields,
25251
- /*before=*/ false);
26712
+ /*inclusive=*/ false);
25252
26713
  }
25253
26714
  class QueryEndAtConstraint extends QueryConstraint {
25254
- constructor(type, _docOrFields, _before) {
26715
+ constructor(type, _docOrFields, _inclusive) {
25255
26716
  super();
25256
26717
  this.type = type;
25257
26718
  this._docOrFields = _docOrFields;
25258
- this._before = _before;
26719
+ this._inclusive = _inclusive;
25259
26720
  }
25260
26721
  _apply(query) {
25261
- const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._before);
26722
+ const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
25262
26723
  return new Query(query.firestore, query.converter, queryWithEndAt(query._query, bound));
25263
26724
  }
25264
26725
  }
25265
26726
  function endBefore(...docOrFields) {
25266
- return new QueryEndAtConstraint('endBefore', docOrFields, /*before=*/ true);
26727
+ return new QueryEndAtConstraint('endBefore', docOrFields,
26728
+ /*inclusive=*/ false);
25267
26729
  }
25268
26730
  function endAt(...docOrFields) {
25269
- return new QueryEndAtConstraint('endAt', docOrFields, /*before=*/ false);
26731
+ return new QueryEndAtConstraint('endAt', docOrFields, /*inclusive=*/ true);
25270
26732
  }
25271
26733
  /** Helper function to create a bound from a document or fields */
25272
- function newQueryBoundFromDocOrFields(query, methodName, docOrFields, before) {
26734
+ function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive) {
25273
26735
  docOrFields[0] = util$1.getModularInstance(docOrFields[0]);
25274
26736
  if (docOrFields[0] instanceof DocumentSnapshot$1) {
25275
- return newQueryBoundFromDocument(query._query, query.firestore._databaseId, methodName, docOrFields[0]._document, before);
26737
+ return newQueryBoundFromDocument(query._query, query.firestore._databaseId, methodName, docOrFields[0]._document, inclusive);
25276
26738
  }
25277
26739
  else {
25278
26740
  const reader = newUserDataReader(query.firestore);
25279
- return newQueryBoundFromFields(query._query, query.firestore._databaseId, reader, methodName, docOrFields, before);
26741
+ return newQueryBoundFromFields(query._query, query.firestore._databaseId, reader, methodName, docOrFields, inclusive);
25280
26742
  }
25281
26743
  }
25282
26744
  function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op, value) {
@@ -25334,7 +26796,7 @@ function newQueryOrderBy(query, fieldPath, direction) {
25334
26796
  * of the query or if any of the fields in the order by are an uncommitted
25335
26797
  * server timestamp.
25336
26798
  */
25337
- function newQueryBoundFromDocument(query, databaseId, methodName, doc, before) {
26799
+ function newQueryBoundFromDocument(query, databaseId, methodName, doc, inclusive) {
25338
26800
  if (!doc) {
25339
26801
  throw new FirestoreError(Code.NOT_FOUND, `Can't use a DocumentSnapshot that doesn't exist for ` +
25340
26802
  `${methodName}().`);
@@ -25371,12 +26833,12 @@ function newQueryBoundFromDocument(query, databaseId, methodName, doc, before) {
25371
26833
  }
25372
26834
  }
25373
26835
  }
25374
- return new Bound(components, before);
26836
+ return new Bound(components, inclusive);
25375
26837
  }
25376
26838
  /**
25377
26839
  * Converts a list of field values to a `Bound` for the given query.
25378
26840
  */
25379
- function newQueryBoundFromFields(query, databaseId, dataReader, methodName, values, before) {
26841
+ function newQueryBoundFromFields(query, databaseId, dataReader, methodName, values, inclusive) {
25380
26842
  // Use explicit order by's because it has to match the query the user made
25381
26843
  const orderBy = query.explicitOrderBy;
25382
26844
  if (values.length > orderBy.length) {
@@ -25413,7 +26875,7 @@ function newQueryBoundFromFields(query, databaseId, dataReader, methodName, valu
25413
26875
  components.push(wrapped);
25414
26876
  }
25415
26877
  }
25416
- return new Bound(components, before);
26878
+ return new Bound(components, inclusive);
25417
26879
  }
25418
26880
  /**
25419
26881
  * Parses the given `documentIdValue` into a `ReferenceValue`, throwing