@firebase/firestore 3.4.4 → 3.4.5-2022116201138

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 (213) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/firestore/src/api/credentials.d.ts +1 -0
  3. package/dist/firestore/src/api/{query.d.ts → filter.d.ts} +0 -0
  4. package/dist/firestore/src/api/snapshot.d.ts +1 -2
  5. package/dist/firestore/src/api.d.ts +1 -1
  6. package/dist/firestore/src/core/database_info.d.ts +1 -0
  7. package/dist/firestore/src/core/target.d.ts +34 -5
  8. package/dist/firestore/src/index/index_byte_encoder.d.ts +18 -1
  9. package/dist/firestore/src/index/index_entry.d.ts +26 -0
  10. package/dist/firestore/src/lite-api/query.d.ts +2 -2
  11. package/dist/firestore/src/local/document_overlay_cache.d.ts +72 -0
  12. package/dist/firestore/src/local/index_manager.d.ts +52 -0
  13. package/dist/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
  14. package/dist/firestore/src/local/indexeddb_index_manager.d.ts +40 -2
  15. package/dist/firestore/src/local/indexeddb_persistence.d.ts +6 -4
  16. package/dist/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  17. package/dist/firestore/src/local/indexeddb_schema.d.ts +195 -7
  18. package/dist/firestore/src/local/local_documents_view.d.ts +0 -1
  19. package/dist/firestore/src/local/local_serializer.d.ts +19 -3
  20. package/dist/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
  21. package/dist/firestore/src/local/memory_index_manager.d.ts +11 -0
  22. package/dist/firestore/src/local/memory_persistence.d.ts +6 -2
  23. package/dist/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  24. package/dist/firestore/src/local/persistence.d.ts +8 -2
  25. package/dist/firestore/src/local/remote_document_cache.d.ts +7 -9
  26. package/dist/firestore/src/local/remote_document_change_buffer.d.ts +3 -12
  27. package/dist/firestore/src/local/simple_db.d.ts +1 -0
  28. package/dist/firestore/src/model/document.d.ts +7 -0
  29. package/dist/firestore/src/model/document_key.d.ts +6 -0
  30. package/dist/firestore/src/model/field_index.d.ts +87 -7
  31. package/dist/firestore/src/model/overlay.d.ts +32 -0
  32. package/dist/firestore/src/model/target_index_matcher.d.ts +70 -0
  33. package/dist/firestore/src/model/values.d.ts +7 -0
  34. package/dist/firestore/src/platform/node/grpc_connection.d.ts +2 -2
  35. package/dist/firestore/src/platform/node/load_protos.d.ts +2 -2
  36. package/dist/firestore/src/util/sorted_set.d.ts +18 -0
  37. package/dist/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  38. package/dist/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
  39. package/dist/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  40. package/dist/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
  41. package/dist/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  42. package/dist/firestore/test/unit/local/test_remote_document_cache.d.ts +5 -3
  43. package/dist/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  44. package/dist/firestore/test/unit/model/field_index.test.d.ts +17 -0
  45. package/dist/firestore/test/unit/model/target.test.d.ts +17 -0
  46. package/dist/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  47. package/dist/firestore/test/util/helpers.d.ts +8 -2
  48. package/dist/index.d.ts +1 -2
  49. package/dist/index.esm2017.js +4815 -3708
  50. package/dist/index.esm2017.js.map +1 -1
  51. package/dist/index.esm5.js +4500 -3545
  52. package/dist/index.esm5.js.map +1 -1
  53. package/dist/index.node.cjs.js +2001 -389
  54. package/dist/index.node.cjs.js.map +1 -1
  55. package/dist/index.node.mjs +1981 -390
  56. package/dist/index.node.mjs.map +1 -1
  57. package/dist/index.rn.js +4798 -3733
  58. package/dist/index.rn.js.map +1 -1
  59. package/dist/internal.d.ts +265 -26
  60. package/dist/lite/firestore/src/api/credentials.d.ts +1 -0
  61. package/dist/lite/firestore/src/api/{query.d.ts → filter.d.ts} +0 -0
  62. package/dist/lite/firestore/src/api/snapshot.d.ts +1 -2
  63. package/dist/lite/firestore/src/api.d.ts +1 -1
  64. package/dist/lite/firestore/src/core/database_info.d.ts +1 -0
  65. package/dist/lite/firestore/src/core/target.d.ts +34 -5
  66. package/dist/lite/firestore/src/index/index_byte_encoder.d.ts +18 -1
  67. package/dist/lite/firestore/src/index/index_entry.d.ts +26 -0
  68. package/dist/lite/firestore/src/lite-api/query.d.ts +2 -2
  69. package/dist/lite/firestore/src/local/document_overlay_cache.d.ts +72 -0
  70. package/dist/lite/firestore/src/local/index_manager.d.ts +52 -0
  71. package/dist/lite/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
  72. package/dist/lite/firestore/src/local/indexeddb_index_manager.d.ts +40 -2
  73. package/dist/lite/firestore/src/local/indexeddb_persistence.d.ts +6 -4
  74. package/dist/lite/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  75. package/dist/lite/firestore/src/local/indexeddb_schema.d.ts +195 -7
  76. package/dist/lite/firestore/src/local/local_documents_view.d.ts +0 -1
  77. package/dist/lite/firestore/src/local/local_serializer.d.ts +19 -3
  78. package/dist/lite/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
  79. package/dist/lite/firestore/src/local/memory_index_manager.d.ts +11 -0
  80. package/dist/lite/firestore/src/local/memory_persistence.d.ts +6 -2
  81. package/dist/lite/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  82. package/dist/lite/firestore/src/local/persistence.d.ts +8 -2
  83. package/dist/lite/firestore/src/local/remote_document_cache.d.ts +7 -9
  84. package/dist/lite/firestore/src/local/remote_document_change_buffer.d.ts +3 -12
  85. package/dist/lite/firestore/src/local/simple_db.d.ts +1 -0
  86. package/dist/lite/firestore/src/model/document.d.ts +7 -0
  87. package/dist/lite/firestore/src/model/document_key.d.ts +6 -0
  88. package/dist/lite/firestore/src/model/field_index.d.ts +87 -7
  89. package/dist/lite/firestore/src/model/overlay.d.ts +32 -0
  90. package/dist/lite/firestore/src/model/target_index_matcher.d.ts +70 -0
  91. package/dist/lite/firestore/src/model/values.d.ts +7 -0
  92. package/dist/lite/firestore/src/platform/node/grpc_connection.d.ts +2 -2
  93. package/dist/lite/firestore/src/platform/node/load_protos.d.ts +2 -2
  94. package/dist/lite/firestore/src/util/sorted_set.d.ts +18 -0
  95. package/dist/lite/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  96. package/dist/lite/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
  97. package/dist/lite/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  98. package/dist/lite/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
  99. package/dist/lite/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  100. package/dist/lite/firestore/test/unit/local/test_remote_document_cache.d.ts +5 -3
  101. package/dist/lite/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  102. package/dist/lite/firestore/test/unit/model/field_index.test.d.ts +17 -0
  103. package/dist/lite/firestore/test/unit/model/target.test.d.ts +17 -0
  104. package/dist/lite/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  105. package/dist/lite/firestore/test/util/helpers.d.ts +8 -2
  106. package/dist/lite/index.browser.esm2017.js +1166 -1143
  107. package/dist/lite/index.browser.esm2017.js.map +1 -1
  108. package/dist/lite/index.browser.esm5.js +357 -337
  109. package/dist/lite/index.browser.esm5.js.map +1 -1
  110. package/dist/lite/index.node.cjs.js +64 -34
  111. package/dist/lite/index.node.cjs.js.map +1 -1
  112. package/dist/lite/index.node.mjs +64 -34
  113. package/dist/lite/index.node.mjs.map +1 -1
  114. package/dist/lite/index.rn.esm2017.js +344 -321
  115. package/dist/lite/index.rn.esm2017.js.map +1 -1
  116. package/dist/lite/internal.d.ts +16 -2
  117. package/dist/lite/packages/firestore/dist/lite/index.browser.esm2017.d.ts +122 -121
  118. package/dist/lite/packages/firestore/src/api/credentials.d.ts +1 -0
  119. package/dist/lite/packages/firestore/src/api/{query.d.ts → filter.d.ts} +0 -0
  120. package/dist/lite/packages/firestore/src/api/snapshot.d.ts +1 -2
  121. package/dist/lite/packages/firestore/src/api.d.ts +1 -1
  122. package/dist/lite/packages/firestore/src/core/database_info.d.ts +1 -0
  123. package/dist/lite/packages/firestore/src/core/target.d.ts +34 -5
  124. package/dist/lite/packages/firestore/src/index/index_byte_encoder.d.ts +18 -1
  125. package/dist/lite/packages/firestore/src/index/index_entry.d.ts +26 -0
  126. package/dist/lite/packages/firestore/src/lite-api/query.d.ts +2 -2
  127. package/dist/lite/packages/firestore/src/local/document_overlay_cache.d.ts +72 -0
  128. package/dist/lite/packages/firestore/src/local/index_manager.d.ts +52 -0
  129. package/dist/lite/packages/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
  130. package/dist/lite/packages/firestore/src/local/indexeddb_index_manager.d.ts +40 -2
  131. package/dist/lite/packages/firestore/src/local/indexeddb_persistence.d.ts +6 -4
  132. package/dist/lite/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  133. package/dist/lite/packages/firestore/src/local/indexeddb_schema.d.ts +195 -7
  134. package/dist/lite/packages/firestore/src/local/local_documents_view.d.ts +0 -1
  135. package/dist/lite/packages/firestore/src/local/local_serializer.d.ts +19 -3
  136. package/dist/lite/packages/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
  137. package/dist/lite/packages/firestore/src/local/memory_index_manager.d.ts +11 -0
  138. package/dist/lite/packages/firestore/src/local/memory_persistence.d.ts +6 -2
  139. package/dist/lite/packages/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  140. package/dist/lite/packages/firestore/src/local/persistence.d.ts +8 -2
  141. package/dist/lite/packages/firestore/src/local/remote_document_cache.d.ts +7 -9
  142. package/dist/lite/packages/firestore/src/local/remote_document_change_buffer.d.ts +3 -12
  143. package/dist/lite/packages/firestore/src/local/simple_db.d.ts +1 -0
  144. package/dist/lite/packages/firestore/src/model/document.d.ts +7 -0
  145. package/dist/lite/packages/firestore/src/model/document_key.d.ts +6 -0
  146. package/dist/lite/packages/firestore/src/model/field_index.d.ts +87 -7
  147. package/dist/lite/packages/firestore/src/model/overlay.d.ts +32 -0
  148. package/dist/lite/packages/firestore/src/model/target_index_matcher.d.ts +70 -0
  149. package/dist/lite/packages/firestore/src/model/values.d.ts +7 -0
  150. package/dist/lite/packages/firestore/src/platform/node/grpc_connection.d.ts +2 -2
  151. package/dist/lite/packages/firestore/src/platform/node/load_protos.d.ts +2 -2
  152. package/dist/lite/packages/firestore/src/util/sorted_set.d.ts +18 -0
  153. package/dist/lite/packages/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  154. package/dist/lite/packages/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
  155. package/dist/lite/packages/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  156. package/dist/lite/packages/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
  157. package/dist/lite/packages/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  158. package/dist/lite/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +5 -3
  159. package/dist/lite/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  160. package/dist/lite/packages/firestore/test/unit/model/field_index.test.d.ts +17 -0
  161. package/dist/lite/packages/firestore/test/unit/model/target.test.d.ts +17 -0
  162. package/dist/lite/packages/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  163. package/dist/lite/packages/firestore/test/util/helpers.d.ts +8 -2
  164. package/dist/lite/private.d.ts +9 -2
  165. package/dist/packages/firestore/dist/index.esm2017.d.ts +199 -194
  166. package/dist/packages/firestore/src/api/credentials.d.ts +1 -0
  167. package/dist/packages/firestore/src/api/{query.d.ts → filter.d.ts} +0 -0
  168. package/dist/packages/firestore/src/api/snapshot.d.ts +1 -2
  169. package/dist/packages/firestore/src/api.d.ts +1 -1
  170. package/dist/packages/firestore/src/core/database_info.d.ts +1 -0
  171. package/dist/packages/firestore/src/core/target.d.ts +34 -5
  172. package/dist/packages/firestore/src/index/index_byte_encoder.d.ts +18 -1
  173. package/dist/packages/firestore/src/index/index_entry.d.ts +26 -0
  174. package/dist/packages/firestore/src/lite-api/query.d.ts +2 -2
  175. package/dist/packages/firestore/src/local/document_overlay_cache.d.ts +72 -0
  176. package/dist/packages/firestore/src/local/index_manager.d.ts +52 -0
  177. package/dist/packages/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
  178. package/dist/packages/firestore/src/local/indexeddb_index_manager.d.ts +40 -2
  179. package/dist/packages/firestore/src/local/indexeddb_persistence.d.ts +6 -4
  180. package/dist/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  181. package/dist/packages/firestore/src/local/indexeddb_schema.d.ts +195 -7
  182. package/dist/packages/firestore/src/local/local_documents_view.d.ts +0 -1
  183. package/dist/packages/firestore/src/local/local_serializer.d.ts +19 -3
  184. package/dist/packages/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
  185. package/dist/packages/firestore/src/local/memory_index_manager.d.ts +11 -0
  186. package/dist/packages/firestore/src/local/memory_persistence.d.ts +6 -2
  187. package/dist/packages/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  188. package/dist/packages/firestore/src/local/persistence.d.ts +8 -2
  189. package/dist/packages/firestore/src/local/remote_document_cache.d.ts +7 -9
  190. package/dist/packages/firestore/src/local/remote_document_change_buffer.d.ts +3 -12
  191. package/dist/packages/firestore/src/local/simple_db.d.ts +1 -0
  192. package/dist/packages/firestore/src/model/document.d.ts +7 -0
  193. package/dist/packages/firestore/src/model/document_key.d.ts +6 -0
  194. package/dist/packages/firestore/src/model/field_index.d.ts +87 -7
  195. package/dist/packages/firestore/src/model/overlay.d.ts +32 -0
  196. package/dist/packages/firestore/src/model/target_index_matcher.d.ts +70 -0
  197. package/dist/packages/firestore/src/model/values.d.ts +7 -0
  198. package/dist/packages/firestore/src/platform/node/grpc_connection.d.ts +2 -2
  199. package/dist/packages/firestore/src/platform/node/load_protos.d.ts +2 -2
  200. package/dist/packages/firestore/src/util/sorted_set.d.ts +18 -0
  201. package/dist/packages/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  202. package/dist/packages/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
  203. package/dist/packages/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  204. package/dist/packages/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
  205. package/dist/packages/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  206. package/dist/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +5 -3
  207. package/dist/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  208. package/dist/packages/firestore/test/unit/model/field_index.test.d.ts +17 -0
  209. package/dist/packages/firestore/test/unit/model/target.test.d.ts +17 -0
  210. package/dist/packages/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  211. package/dist/packages/firestore/test/util/helpers.d.ts +8 -2
  212. package/dist/private.d.ts +258 -26
  213. package/package.json +4 -4
@@ -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";
36
+ const version$1 = "3.4.5-2022116201138";
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";
89
+ const version = "9.6.7-2022116201138";
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);
@@ -3378,6 +3583,75 @@ function getLocalWriteTime(value) {
3378
3583
  return new Timestamp(localWriteTime.seconds, localWriteTime.nanos);
3379
3584
  }
3380
3585
 
3586
+ /**
3587
+ * @license
3588
+ * Copyright 2017 Google LLC
3589
+ *
3590
+ * Licensed under the Apache License, Version 2.0 (the "License");
3591
+ * you may not use this file except in compliance with the License.
3592
+ * You may obtain a copy of the License at
3593
+ *
3594
+ * http://www.apache.org/licenses/LICENSE-2.0
3595
+ *
3596
+ * Unless required by applicable law or agreed to in writing, software
3597
+ * distributed under the License is distributed on an "AS IS" BASIS,
3598
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3599
+ * See the License for the specific language governing permissions and
3600
+ * limitations under the License.
3601
+ */
3602
+ class DatabaseInfo {
3603
+ /**
3604
+ * Constructs a DatabaseInfo using the provided host, databaseId and
3605
+ * persistenceKey.
3606
+ *
3607
+ * @param databaseId - The database to use.
3608
+ * @param appId - The Firebase App Id.
3609
+ * @param persistenceKey - A unique identifier for this Firestore's local
3610
+ * storage (used in conjunction with the databaseId).
3611
+ * @param host - The Firestore backend host to connect to.
3612
+ * @param ssl - Whether to use SSL when connecting.
3613
+ * @param forceLongPolling - Whether to use the forceLongPolling option
3614
+ * when using WebChannel as the network transport.
3615
+ * @param autoDetectLongPolling - Whether to use the detectBufferingProxy
3616
+ * option when using WebChannel as the network transport.
3617
+ * @param useFetchStreams Whether to use the Fetch API instead of
3618
+ * XMLHTTPRequest
3619
+ */
3620
+ constructor(databaseId, appId, persistenceKey, host, ssl, forceLongPolling, autoDetectLongPolling, useFetchStreams) {
3621
+ this.databaseId = databaseId;
3622
+ this.appId = appId;
3623
+ this.persistenceKey = persistenceKey;
3624
+ this.host = host;
3625
+ this.ssl = ssl;
3626
+ this.forceLongPolling = forceLongPolling;
3627
+ this.autoDetectLongPolling = autoDetectLongPolling;
3628
+ this.useFetchStreams = useFetchStreams;
3629
+ }
3630
+ }
3631
+ /** The default database name for a project. */
3632
+ const DEFAULT_DATABASE_NAME = '(default)';
3633
+ /**
3634
+ * Represents the database ID a Firestore client is associated with.
3635
+ * @internal
3636
+ */
3637
+ class DatabaseId {
3638
+ constructor(projectId, database) {
3639
+ this.projectId = projectId;
3640
+ this.database = database ? database : DEFAULT_DATABASE_NAME;
3641
+ }
3642
+ static empty() {
3643
+ return new DatabaseId('', '');
3644
+ }
3645
+ get isDefaultDatabase() {
3646
+ return this.database === DEFAULT_DATABASE_NAME;
3647
+ }
3648
+ isEqual(other) {
3649
+ return (other instanceof DatabaseId &&
3650
+ other.projectId === this.projectId &&
3651
+ other.database === this.database);
3652
+ }
3653
+ }
3654
+
3381
3655
  /**
3382
3656
  * @license
3383
3657
  * Copyright 2017 Google LLC
@@ -3449,11 +3723,25 @@ class DocumentKey {
3449
3723
  static fromName(name) {
3450
3724
  return new DocumentKey(ResourcePath.fromString(name).popFirst(5));
3451
3725
  }
3726
+ static empty() {
3727
+ return new DocumentKey(ResourcePath.emptyPath());
3728
+ }
3729
+ get collectionGroup() {
3730
+ return this.path.popLast().lastSegment();
3731
+ }
3452
3732
  /** Returns true if the document is in the specified collectionId. */
3453
3733
  hasCollectionId(collectionId) {
3454
3734
  return (this.path.length >= 2 &&
3455
3735
  this.path.get(this.path.length - 2) === collectionId);
3456
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
+ }
3457
3745
  isEqual(other) {
3458
3746
  return (other !== null && ResourcePath.comparator(this.path, other.path) === 0);
3459
3747
  }
@@ -3493,6 +3781,13 @@ class DocumentKey {
3493
3781
  * See the License for the specific language governing permissions and
3494
3782
  * limitations under the License.
3495
3783
  */
3784
+ const MAX_VALUE = {
3785
+ mapValue: {
3786
+ fields: {
3787
+ '__type__': { stringValue: '__max___' }
3788
+ }
3789
+ }
3790
+ };
3496
3791
  /** Extracts the backend's type order for the provided value. */
3497
3792
  function typeOrder(value) {
3498
3793
  if ('nullValue' in value) {
@@ -3902,6 +4197,10 @@ function deepClone(source) {
3902
4197
  else {
3903
4198
  return Object.assign({}, source);
3904
4199
  }
4200
+ }
4201
+ /** Returns true if the Value represents the canonical {@link #MAX_VALUE} . */
4202
+ function isMaxValue(value) {
4203
+ return valueEquals(value, MAX_VALUE);
3905
4204
  }
3906
4205
 
3907
4206
  /**
@@ -4097,10 +4396,11 @@ function extractFieldMask(value) {
4097
4396
  * from all views.
4098
4397
  */
4099
4398
  class MutableDocument {
4100
- constructor(key, documentType, version, data, documentState) {
4399
+ constructor(key, documentType, version, readTime, data, documentState) {
4101
4400
  this.key = key;
4102
4401
  this.documentType = documentType;
4103
4402
  this.version = version;
4403
+ this.readTime = readTime;
4104
4404
  this.data = data;
4105
4405
  this.documentState = documentState;
4106
4406
  }
@@ -4109,18 +4409,18 @@ class MutableDocument {
4109
4409
  * base document for mutations.
4110
4410
  */
4111
4411
  static newInvalidDocument(documentKey) {
4112
- return new MutableDocument(documentKey, 0 /* INVALID */, SnapshotVersion.min(), ObjectValue.empty(), 0 /* SYNCED */);
4412
+ return new MutableDocument(documentKey, 0 /* INVALID */, SnapshotVersion.min(), SnapshotVersion.min(), ObjectValue.empty(), 0 /* SYNCED */);
4113
4413
  }
4114
4414
  /**
4115
4415
  * Creates a new document that is known to exist with the given data at the
4116
4416
  * given version.
4117
4417
  */
4118
4418
  static newFoundDocument(documentKey, version, value) {
4119
- return new MutableDocument(documentKey, 1 /* FOUND_DOCUMENT */, version, value, 0 /* SYNCED */);
4419
+ return new MutableDocument(documentKey, 1 /* FOUND_DOCUMENT */, version, SnapshotVersion.min(), value, 0 /* SYNCED */);
4120
4420
  }
4121
4421
  /** Creates a new document that is known to not exist at the given version. */
4122
4422
  static newNoDocument(documentKey, version) {
4123
- return new MutableDocument(documentKey, 2 /* NO_DOCUMENT */, version, ObjectValue.empty(), 0 /* SYNCED */);
4423
+ return new MutableDocument(documentKey, 2 /* NO_DOCUMENT */, version, SnapshotVersion.min(), ObjectValue.empty(), 0 /* SYNCED */);
4124
4424
  }
4125
4425
  /**
4126
4426
  * Creates a new document that is known to exist at the given version but
@@ -4128,7 +4428,7 @@ class MutableDocument {
4128
4428
  * base document).
4129
4429
  */
4130
4430
  static newUnknownDocument(documentKey, version) {
4131
- return new MutableDocument(documentKey, 3 /* UNKNOWN_DOCUMENT */, version, ObjectValue.empty(), 2 /* HAS_COMMITTED_MUTATIONS */);
4431
+ return new MutableDocument(documentKey, 3 /* UNKNOWN_DOCUMENT */, version, SnapshotVersion.min(), ObjectValue.empty(), 2 /* HAS_COMMITTED_MUTATIONS */);
4132
4432
  }
4133
4433
  /**
4134
4434
  * Changes the document type to indicate that it exists and that its version
@@ -4172,6 +4472,10 @@ class MutableDocument {
4172
4472
  this.documentState = 1 /* HAS_LOCAL_MUTATIONS */;
4173
4473
  return this;
4174
4474
  }
4475
+ setReadTime(readTime) {
4476
+ this.readTime = readTime;
4477
+ return this;
4478
+ }
4175
4479
  get hasLocalMutations() {
4176
4480
  return this.documentState === 1 /* HAS_LOCAL_MUTATIONS */;
4177
4481
  }
@@ -4202,7 +4506,7 @@ class MutableDocument {
4202
4506
  this.data.isEqual(other.data));
4203
4507
  }
4204
4508
  mutableCopy() {
4205
- return new MutableDocument(this.key, this.documentType, this.version, this.data.clone(), this.documentState);
4509
+ return new MutableDocument(this.key, this.documentType, this.version, this.readTime, this.data.clone(), this.documentState);
4206
4510
  }
4207
4511
  toString() {
4208
4512
  return (`Document(${this.key}, ${this.version}, ${JSON.stringify(this.data.value)}, ` +
@@ -4225,6 +4529,133 @@ function compareDocumentsByField(field, d1, d2) {
4225
4529
  }
4226
4530
  }
4227
4531
 
4532
+ /**
4533
+ * @license
4534
+ * Copyright 2021 Google LLC
4535
+ *
4536
+ * Licensed under the Apache License, Version 2.0 (the "License");
4537
+ * you may not use this file except in compliance with the License.
4538
+ * You may obtain a copy of the License at
4539
+ *
4540
+ * http://www.apache.org/licenses/LICENSE-2.0
4541
+ *
4542
+ * Unless required by applicable law or agreed to in writing, software
4543
+ * distributed under the License is distributed on an "AS IS" BASIS,
4544
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4545
+ * See the License for the specific language governing permissions and
4546
+ * limitations under the License.
4547
+ */
4548
+ /**
4549
+ * The initial mutation batch id for each index. Gets updated during index
4550
+ * backfill.
4551
+ */
4552
+ const INITIAL_LARGEST_BATCH_ID = -1;
4553
+ /**
4554
+ * The initial sequence number for each index. Gets updated during index
4555
+ * backfill.
4556
+ */
4557
+ const INITIAL_SEQUENCE_NUMBER = 0;
4558
+ /**
4559
+ * An index definition for field indexes in Firestore.
4560
+ *
4561
+ * Every index is associated with a collection. The definition contains a list
4562
+ * of fields and their index kind (which can be `ASCENDING`, `DESCENDING` or
4563
+ * `CONTAINS` for ArrayContains/ArrayContainsAny queries).
4564
+ *
4565
+ * Unlike the backend, the SDK does not differentiate between collection or
4566
+ * collection group-scoped indices. Every index can be used for both single
4567
+ * collection and collection group queries.
4568
+ */
4569
+ class FieldIndex {
4570
+ constructor(
4571
+ /**
4572
+ * The index ID. Returns -1 if the index ID is not available (e.g. the index
4573
+ * has not yet been persisted).
4574
+ */
4575
+ indexId,
4576
+ /** The collection ID this index applies to. */
4577
+ collectionGroup,
4578
+ /** The field segments for this index. */
4579
+ fields,
4580
+ /** Shows how up-to-date the index is for the current user. */
4581
+ indexState) {
4582
+ this.indexId = indexId;
4583
+ this.collectionGroup = collectionGroup;
4584
+ this.fields = fields;
4585
+ this.indexState = indexState;
4586
+ }
4587
+ }
4588
+ /** An ID for an index that has not yet been added to persistence. */
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
+ }
4598
+ /** An index component consisting of field path and index type. */
4599
+ class IndexSegment {
4600
+ constructor(
4601
+ /** The field path of the component. */
4602
+ fieldPath,
4603
+ /** The fields sorting order. */
4604
+ kind) {
4605
+ this.fieldPath = fieldPath;
4606
+ this.kind = kind;
4607
+ }
4608
+ }
4609
+ /**
4610
+ * Stores the "high water mark" that indicates how updated the Index is for the
4611
+ * current user.
4612
+ */
4613
+ class IndexState {
4614
+ constructor(
4615
+ /**
4616
+ * Indicates when the index was last updated (relative to other indexes).
4617
+ */
4618
+ sequenceNumber,
4619
+ /** The the latest indexed read time, document and batch id. */
4620
+ offset) {
4621
+ this.sequenceNumber = sequenceNumber;
4622
+ this.offset = offset;
4623
+ }
4624
+ /** The state of an index that has not yet been backfilled. */
4625
+ static empty() {
4626
+ return new IndexState(INITIAL_SEQUENCE_NUMBER, IndexOffset.min());
4627
+ }
4628
+ }
4629
+ /**
4630
+ * Stores the latest read time, document and batch ID that were processed for an
4631
+ * index.
4632
+ */
4633
+ class IndexOffset {
4634
+ constructor(
4635
+ /**
4636
+ * The latest read time version that has been indexed by Firestore for this
4637
+ * field index.
4638
+ */
4639
+ readTime,
4640
+ /**
4641
+ * The key of the last document that was indexed for this query. Use
4642
+ * `DocumentKey.empty()` if no document has been indexed.
4643
+ */
4644
+ documentKey,
4645
+ /*
4646
+ * The largest mutation batch id that's been processed by Firestore.
4647
+ */
4648
+ largestBatchId) {
4649
+ this.readTime = readTime;
4650
+ this.documentKey = documentKey;
4651
+ this.largestBatchId = largestBatchId;
4652
+ }
4653
+ /** The state of an index that has not yet been backfilled. */
4654
+ static min() {
4655
+ return new IndexOffset(SnapshotVersion.min(), DocumentKey.empty(), INITIAL_LARGEST_BATCH_ID);
4656
+ }
4657
+ }
4658
+
4228
4659
  /**
4229
4660
  * @license
4230
4661
  * Copyright 2019 Google LLC
@@ -4268,27 +4699,29 @@ function newTarget(path, collectionGroup = null, orderBy = [], filters = [], lim
4268
4699
  function canonifyTarget(target) {
4269
4700
  const targetImpl = debugCast(target);
4270
4701
  if (targetImpl.memoizedCanonicalId === null) {
4271
- let canonicalId = targetImpl.path.canonicalString();
4702
+ let str = targetImpl.path.canonicalString();
4272
4703
  if (targetImpl.collectionGroup !== null) {
4273
- canonicalId += '|cg:' + targetImpl.collectionGroup;
4704
+ str += '|cg:' + targetImpl.collectionGroup;
4274
4705
  }
4275
- canonicalId += '|f:';
4276
- canonicalId += targetImpl.filters.map(f => canonifyFilter(f)).join(',');
4277
- canonicalId += '|ob:';
4278
- 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(',');
4279
4710
  if (!isNullOrUndefined(targetImpl.limit)) {
4280
- canonicalId += '|l:';
4281
- canonicalId += targetImpl.limit;
4711
+ str += '|l:';
4712
+ str += targetImpl.limit;
4282
4713
  }
4283
4714
  if (targetImpl.startAt) {
4284
- canonicalId += '|lb:';
4285
- canonicalId += canonifyBound(targetImpl.startAt);
4715
+ str += '|lb:';
4716
+ str += targetImpl.startAt.inclusive ? 'b:' : 'a:';
4717
+ str += targetImpl.startAt.position.map(p => canonicalId(p)).join(',');
4286
4718
  }
4287
4719
  if (targetImpl.endAt) {
4288
- canonicalId += '|ub:';
4289
- canonicalId += canonifyBound(targetImpl.endAt);
4720
+ str += '|ub:';
4721
+ str += targetImpl.endAt.inclusive ? 'a:' : 'b:';
4722
+ str += targetImpl.endAt.position.map(p => canonicalId(p)).join(',');
4290
4723
  }
4291
- targetImpl.memoizedCanonicalId = canonicalId;
4724
+ targetImpl.memoizedCanonicalId = str;
4292
4725
  }
4293
4726
  return targetImpl.memoizedCanonicalId;
4294
4727
  }
@@ -4311,10 +4744,14 @@ function stringifyTarget(target) {
4311
4744
  .join(', ')}]`;
4312
4745
  }
4313
4746
  if (target.startAt) {
4314
- 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(',');
4315
4750
  }
4316
4751
  if (target.endAt) {
4317
- 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(',');
4318
4755
  }
4319
4756
  return `Target(${str})`;
4320
4757
  }
@@ -4349,7 +4786,7 @@ function targetEquals(left, right) {
4349
4786
  }
4350
4787
  return boundEquals(left.endAt, right.endAt);
4351
4788
  }
4352
- function isDocumentTarget(target) {
4789
+ function targetIsDocumentTarget(target) {
4353
4790
  return (DocumentKey.isDocumentKey(target.path) &&
4354
4791
  target.collectionGroup === null &&
4355
4792
  target.filters.length === 0);
@@ -4552,17 +4989,11 @@ class ArrayContainsAnyFilter extends FieldFilter {
4552
4989
  * just after the provided values.
4553
4990
  */
4554
4991
  class Bound {
4555
- constructor(position, before) {
4992
+ constructor(position, inclusive) {
4556
4993
  this.position = position;
4557
- this.before = before;
4994
+ this.inclusive = inclusive;
4558
4995
  }
4559
4996
  }
4560
- function canonifyBound(bound) {
4561
- // TODO(b/29183165): Make this collision robust.
4562
- return `${bound.before ? 'b' : 'a'}:${bound.position
4563
- .map(p => canonicalId(p))
4564
- .join(',')}`;
4565
- }
4566
4997
  /**
4567
4998
  * An ordering on a field, in some Direction. Direction defaults to ASCENDING.
4568
4999
  */
@@ -4582,11 +5013,7 @@ function stringifyOrderBy(orderBy) {
4582
5013
  function orderByEquals(left, right) {
4583
5014
  return left.dir === right.dir && left.field.isEqual(right.field);
4584
5015
  }
4585
- /**
4586
- * Returns true if a document sorts before a bound using the provided sort
4587
- * order.
4588
- */
4589
- function sortsBeforeDocument(bound, orderBy, doc) {
5016
+ function boundCompareToDocument(bound, orderBy, doc) {
4590
5017
  let comparison = 0;
4591
5018
  for (let i = 0; i < bound.position.length; i++) {
4592
5019
  const orderByComponent = orderBy[i];
@@ -4605,7 +5032,23 @@ function sortsBeforeDocument(bound, orderBy, doc) {
4605
5032
  break;
4606
5033
  }
4607
5034
  }
4608
- 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;
4609
5052
  }
4610
5053
  function boundEquals(left, right) {
4611
5054
  if (left === null) {
@@ -4614,7 +5057,7 @@ function boundEquals(left, right) {
4614
5057
  else if (right === null) {
4615
5058
  return false;
4616
5059
  }
4617
- if (left.before !== right.before ||
5060
+ if (left.inclusive !== right.inclusive ||
4618
5061
  left.position.length !== right.position.length) {
4619
5062
  return false;
4620
5063
  }
@@ -4818,10 +5261,10 @@ function queryToTarget(query) {
4818
5261
  }
4819
5262
  // We need to swap the cursors to match the now-flipped query ordering.
4820
5263
  const startAt = queryImpl.endAt
4821
- ? new Bound(queryImpl.endAt.position, !queryImpl.endAt.before)
5264
+ ? new Bound(queryImpl.endAt.position, !queryImpl.endAt.inclusive)
4822
5265
  : null;
4823
5266
  const endAt = queryImpl.startAt
4824
- ? new Bound(queryImpl.startAt.position, !queryImpl.startAt.before)
5267
+ ? new Bound(queryImpl.startAt.position, !queryImpl.startAt.inclusive)
4825
5268
  : null;
4826
5269
  // Now return as a LimitType.First query.
4827
5270
  queryImpl.memoizedTarget = newTarget(queryImpl.path, queryImpl.collectionGroup, orderBys, queryImpl.filters, queryImpl.limit, startAt, endAt);
@@ -4909,11 +5352,11 @@ function queryMatchesFilters(query, doc) {
4909
5352
  /** Makes sure a document is within the bounds, if provided. */
4910
5353
  function queryMatchesBounds(query, doc) {
4911
5354
  if (query.startAt &&
4912
- !sortsBeforeDocument(query.startAt, queryOrderBy(query), doc)) {
5355
+ !boundSortsBeforeDocument(query.startAt, queryOrderBy(query), doc)) {
4913
5356
  return false;
4914
5357
  }
4915
5358
  if (query.endAt &&
4916
- sortsBeforeDocument(query.endAt, queryOrderBy(query), doc)) {
5359
+ !boundSortsAfterDocument(query.endAt, queryOrderBy(query), doc)) {
4917
5360
  return false;
4918
5361
  }
4919
5362
  return true;
@@ -5568,6 +6011,72 @@ class SortedSetIterator {
5568
6011
  hasNext() {
5569
6012
  return this.iter.hasNext();
5570
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;
5571
6080
  }
5572
6081
 
5573
6082
  /**
@@ -6406,6 +6915,47 @@ class MutationBatchResult {
6406
6915
  }
6407
6916
  }
6408
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
+
6409
6959
  /**
6410
6960
  * @license
6411
6961
  * Copyright 2017 Google LLC
@@ -6878,7 +7428,7 @@ class TargetState {
6878
7428
  this._current = true;
6879
7429
  }
6880
7430
  }
6881
- const LOG_TAG$f = 'WatchChangeAggregator';
7431
+ const LOG_TAG$g = 'WatchChangeAggregator';
6882
7432
  /**
6883
7433
  * A helper class to accumulate watch changes into a RemoteEvent.
6884
7434
  */
@@ -6994,7 +7544,7 @@ class WatchChangeAggregator {
6994
7544
  const targetData = this.targetDataForActiveTarget(targetId);
6995
7545
  if (targetData) {
6996
7546
  const target = targetData.target;
6997
- if (isDocumentTarget(target)) {
7547
+ if (targetIsDocumentTarget(target)) {
6998
7548
  if (expectedCount === 0) {
6999
7549
  // The existence filter told us the document does not exist. We deduce
7000
7550
  // that this document does not exist and apply a deleted document to
@@ -7029,7 +7579,7 @@ class WatchChangeAggregator {
7029
7579
  this.targetStates.forEach((targetState, targetId) => {
7030
7580
  const targetData = this.targetDataForActiveTarget(targetId);
7031
7581
  if (targetData) {
7032
- if (targetState.current && isDocumentTarget(targetData.target)) {
7582
+ if (targetState.current && targetIsDocumentTarget(targetData.target)) {
7033
7583
  // Document queries for document that don't exist can produce an empty
7034
7584
  // result set. To update our local cache, we synthesize a document
7035
7585
  // delete if we have not previously received the document. This
@@ -7072,6 +7622,7 @@ class WatchChangeAggregator {
7072
7622
  resolvedLimboDocuments = resolvedLimboDocuments.add(key);
7073
7623
  }
7074
7624
  });
7625
+ this.pendingDocumentUpdates.forEach((_, doc) => doc.setReadTime(snapshotVersion));
7075
7626
  const remoteEvent = new RemoteEvent(snapshotVersion, targetChanges, this.pendingTargetResets, this.pendingDocumentUpdates, resolvedLimboDocuments);
7076
7627
  this.pendingDocumentUpdates = mutableDocumentMap();
7077
7628
  this.pendingDocumentTargetMapping = documentTargetMap();
@@ -7172,7 +7723,7 @@ class WatchChangeAggregator {
7172
7723
  isActiveTarget(targetId) {
7173
7724
  const targetActive = this.targetDataForActiveTarget(targetId) !== null;
7174
7725
  if (!targetActive) {
7175
- logDebug(LOG_TAG$f, 'Detected inactive target', targetId);
7726
+ logDebug(LOG_TAG$g, 'Detected inactive target', targetId);
7176
7727
  }
7177
7728
  return targetActive;
7178
7729
  }
@@ -7791,10 +8342,10 @@ function toQueryTarget(serializer, target) {
7791
8342
  result.structuredQuery.limit = limit;
7792
8343
  }
7793
8344
  if (target.startAt) {
7794
- result.structuredQuery.startAt = toCursor(target.startAt);
8345
+ result.structuredQuery.startAt = toStartAtCursor(target.startAt);
7795
8346
  }
7796
8347
  if (target.endAt) {
7797
- result.structuredQuery.endAt = toCursor(target.endAt);
8348
+ result.structuredQuery.endAt = toEndAtCursor(target.endAt);
7798
8349
  }
7799
8350
  return result;
7800
8351
  }
@@ -7827,11 +8378,11 @@ function convertQueryTargetToQuery(target) {
7827
8378
  }
7828
8379
  let startAt = null;
7829
8380
  if (query.startAt) {
7830
- startAt = fromCursor(query.startAt);
8381
+ startAt = fromStartAtCursor(query.startAt);
7831
8382
  }
7832
8383
  let endAt = null;
7833
8384
  if (query.endAt) {
7834
- endAt = fromCursor(query.endAt);
8385
+ endAt = fromEndAtCursor(query.endAt);
7835
8386
  }
7836
8387
  return newQuery(path, collectionGroup, orderBy, filterBy, limit, "F" /* First */, startAt, endAt);
7837
8388
  }
@@ -7864,7 +8415,7 @@ function toLabel(serializer, purpose) {
7864
8415
  function toTarget(serializer, targetData) {
7865
8416
  let result;
7866
8417
  const target = targetData.target;
7867
- if (isDocumentTarget(target)) {
8418
+ if (targetIsDocumentTarget(target)) {
7868
8419
  result = { documents: toDocumentsTarget(serializer, target) };
7869
8420
  }
7870
8421
  else {
@@ -7922,16 +8473,27 @@ function toOrder(orderBys) {
7922
8473
  function fromOrder(orderBys) {
7923
8474
  return orderBys.map(order => fromPropertyOrder(order));
7924
8475
  }
7925
- function toCursor(cursor) {
8476
+ function toStartAtCursor(cursor) {
7926
8477
  return {
7927
- before: cursor.before,
8478
+ before: cursor.inclusive,
7928
8479
  values: cursor.position
7929
8480
  };
7930
8481
  }
7931
- function fromCursor(cursor) {
7932
- const before = !!cursor.before;
8482
+ function toEndAtCursor(cursor) {
8483
+ return {
8484
+ before: !cursor.inclusive,
8485
+ values: cursor.position
8486
+ };
8487
+ }
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;
7933
8495
  const position = cursor.values || [];
7934
- return new Bound(position, before);
8496
+ return new Bound(position, inclusive);
7935
8497
  }
7936
8498
  // visible for testing
7937
8499
  function toDirection(dir) {
@@ -8192,29 +8754,34 @@ class LocalSerializer {
8192
8754
  }
8193
8755
  /** Decodes a remote document from storage locally to a Document. */
8194
8756
  function fromDbRemoteDocument(localSerializer, remoteDoc) {
8757
+ let doc;
8195
8758
  if (remoteDoc.document) {
8196
- return fromDocument(localSerializer.remoteSerializer, remoteDoc.document, !!remoteDoc.hasCommittedMutations);
8759
+ doc = fromDocument(localSerializer.remoteSerializer, remoteDoc.document, !!remoteDoc.hasCommittedMutations);
8197
8760
  }
8198
8761
  else if (remoteDoc.noDocument) {
8199
8762
  const key = DocumentKey.fromSegments(remoteDoc.noDocument.path);
8200
8763
  const version = fromDbTimestamp(remoteDoc.noDocument.readTime);
8201
- const document = MutableDocument.newNoDocument(key, version);
8202
- return remoteDoc.hasCommittedMutations
8203
- ? document.setHasCommittedMutations()
8204
- : document;
8764
+ doc = MutableDocument.newNoDocument(key, version);
8765
+ if (remoteDoc.hasCommittedMutations) {
8766
+ doc.setHasCommittedMutations();
8767
+ }
8205
8768
  }
8206
8769
  else if (remoteDoc.unknownDocument) {
8207
8770
  const key = DocumentKey.fromSegments(remoteDoc.unknownDocument.path);
8208
8771
  const version = fromDbTimestamp(remoteDoc.unknownDocument.version);
8209
- return MutableDocument.newUnknownDocument(key, version);
8772
+ doc = MutableDocument.newUnknownDocument(key, version);
8210
8773
  }
8211
8774
  else {
8212
8775
  return fail();
8213
8776
  }
8777
+ if (remoteDoc.readTime) {
8778
+ doc.setReadTime(fromDbTimestampKey(remoteDoc.readTime));
8779
+ }
8780
+ return doc;
8214
8781
  }
8215
8782
  /** Encodes a document for storage locally. */
8216
- function toDbRemoteDocument(localSerializer, document, readTime) {
8217
- const dbReadTime = toDbTimestampKey(readTime);
8783
+ function toDbRemoteDocument(localSerializer, document) {
8784
+ const dbReadTime = toDbTimestampKey(document.readTime);
8218
8785
  const parentPath = document.key.path.popLast().toArray();
8219
8786
  if (document.isFoundDocument()) {
8220
8787
  const doc = toDocument(localSerializer.remoteSerializer, document);
@@ -8225,10 +8792,10 @@ function toDbRemoteDocument(localSerializer, document, readTime) {
8225
8792
  }
8226
8793
  else if (document.isNoDocument()) {
8227
8794
  const path = document.key.path.toArray();
8228
- const readTime = toDbTimestamp(document.version);
8795
+ const version = toDbTimestamp(document.version);
8229
8796
  const hasCommittedMutations = document.hasCommittedMutations;
8230
8797
  return new DbRemoteDocument(
8231
- /* unknownDocument= */ null, new DbNoDocument(path, readTime),
8798
+ /* unknownDocument= */ null, new DbNoDocument(path, version),
8232
8799
  /* document= */ null, hasCommittedMutations, dbReadTime, parentPath);
8233
8800
  }
8234
8801
  else if (document.isUnknownDocument()) {
@@ -8309,7 +8876,7 @@ function toDbTarget(localSerializer, targetData) {
8309
8876
  const dbTimestamp = toDbTimestamp(targetData.snapshotVersion);
8310
8877
  const dbLastLimboFreeTimestamp = toDbTimestamp(targetData.lastLimboFreeSnapshotVersion);
8311
8878
  let queryProto;
8312
- if (isDocumentTarget(targetData.target)) {
8879
+ if (targetIsDocumentTarget(targetData.target)) {
8313
8880
  queryProto = toDocumentsTarget(localSerializer.remoteSerializer, targetData.target);
8314
8881
  }
8315
8882
  else {
@@ -8390,6 +8957,37 @@ function fromBundleMetadata(metadata) {
8390
8957
  version: metadata.version,
8391
8958
  createTime: fromVersion(metadata.createTime)
8392
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);
8393
8991
  }
8394
8992
 
8395
8993
  /**
@@ -8449,6 +9047,704 @@ function namedQueriesStore(txn) {
8449
9047
  return getStore(txn, DbNamedQuery.store);
8450
9048
  }
8451
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
+ }
9656
+ }
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
+ }
9673
+ }
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
+ }
9696
+ }
9697
+
9698
+ /**
9699
+ * @license
9700
+ * Copyright 2022 Google LLC
9701
+ *
9702
+ * Licensed under the Apache License, Version 2.0 (the "License");
9703
+ * you may not use this file except in compliance with the License.
9704
+ * You may obtain a copy of the License at
9705
+ *
9706
+ * http://www.apache.org/licenses/LICENSE-2.0
9707
+ *
9708
+ * Unless required by applicable law or agreed to in writing, software
9709
+ * distributed under the License is distributed on an "AS IS" BASIS,
9710
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9711
+ * See the License for the specific language governing permissions and
9712
+ * limitations under the License.
9713
+ */
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;
9721
+ }
9722
+ }
9723
+ function indexEntryComparator(left, right) {
9724
+ let cmp = left.indexId - right.indexId;
9725
+ if (cmp !== 0) {
9726
+ return cmp;
9727
+ }
9728
+ cmp = DocumentKey.comparator(left.documentKey, right.documentKey);
9729
+ if (cmp !== 0) {
9730
+ return cmp;
9731
+ }
9732
+ cmp = compareByteArrays(left.arrayValue, right.arrayValue);
9733
+ if (cmp !== 0) {
9734
+ return cmp;
9735
+ }
9736
+ return compareByteArrays(left.directionalValue, right.directionalValue);
9737
+ }
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;
9746
+ }
9747
+
8452
9748
  /**
8453
9749
  * @license
8454
9750
  * Copyright 2019 Google LLC
@@ -8479,6 +9775,38 @@ class MemoryIndexManager {
8479
9775
  getCollectionParents(transaction, collectionId) {
8480
9776
  return PersistencePromise.resolve(this.collectionParentIndex.getEntries(collectionId));
8481
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
+ }
8482
9810
  }
8483
9811
  /**
8484
9812
  * Internal implementation of the collection-parent index exposed by MemoryIndexManager.
@@ -8528,19 +9856,25 @@ class MemoryCollectionParentIndex {
8528
9856
  * See the License for the specific language governing permissions and
8529
9857
  * limitations under the License.
8530
9858
  */
9859
+ const LOG_TAG$f = 'IndexedDbIndexManager';
8531
9860
  /**
8532
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.
8533
9865
  */
8534
9866
  class IndexedDbIndexManager {
8535
- constructor() {
9867
+ constructor(user) {
9868
+ this.user = user;
8536
9869
  /**
8537
9870
  * An in-memory copy of the index entries we've already written since the SDK
8538
9871
  * launched. Used to avoid re-writing the same entry repeatedly.
8539
9872
  *
8540
- * This is *NOT* a complete cache of what's in persistence and so can never be used to
8541
- * satisfy reads.
9873
+ * This is *NOT* a complete cache of what's in persistence and so can never be
9874
+ * used to satisfy reads.
8542
9875
  */
8543
9876
  this.collectionParentsCache = new MemoryCollectionParentIndex();
9877
+ this.uid = user.uid || '';
8544
9878
  }
8545
9879
  /**
8546
9880
  * Adds a new entry to the collection parent index.
@@ -8587,6 +9921,198 @@ class IndexedDbIndexManager {
8587
9921
  return parentPaths;
8588
9922
  });
8589
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
+ }
8590
10116
  }
8591
10117
  /**
8592
10118
  * Helper to get a typed SimpleDbStore for the collectionParents
@@ -8594,6 +10120,24 @@ class IndexedDbIndexManager {
8594
10120
  */
8595
10121
  function collectionParentsStore(txn) {
8596
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);
8597
10141
  }
8598
10142
 
8599
10143
  /**
@@ -9757,7 +11301,7 @@ class IndexedDbLruDelegateImpl {
9757
11301
  // Our size accounting requires us to read all documents before
9758
11302
  // removing them.
9759
11303
  return changeBuffer.getEntry(txn, docKey).next(() => {
9760
- changeBuffer.removeEntry(docKey);
11304
+ changeBuffer.removeEntry(docKey, SnapshotVersion.min());
9761
11305
  return documentTargetStore(txn).delete(sentinelKey$1(docKey));
9762
11306
  });
9763
11307
  }
@@ -9970,27 +11514,19 @@ class ObjectMap {
9970
11514
  */
9971
11515
  class RemoteDocumentChangeBuffer {
9972
11516
  constructor() {
9973
- // A mapping of document key to the new cache entry that should be written (or null if any
9974
- // existing cache entry should be removed).
11517
+ // A mapping of document key to the new cache entry that should be written.
9975
11518
  this.changes = new ObjectMap(key => key.toString(), (l, r) => l.isEqual(r));
9976
11519
  this.changesApplied = false;
9977
11520
  }
9978
- getReadTime(key) {
9979
- const change = this.changes.get(key);
9980
- if (change) {
9981
- return change.readTime;
9982
- }
9983
- return SnapshotVersion.min();
9984
- }
9985
11521
  /**
9986
11522
  * Buffers a `RemoteDocumentCache.addEntry()` call.
9987
11523
  *
9988
11524
  * You can only modify documents that have already been retrieved via
9989
11525
  * `getEntry()/getEntries()` (enforced via IndexedDbs `apply()`).
9990
11526
  */
9991
- addEntry(document, readTime) {
11527
+ addEntry(document) {
9992
11528
  this.assertNotApplied();
9993
- this.changes.set(document.key, { document, readTime });
11529
+ this.changes.set(document.key, document);
9994
11530
  }
9995
11531
  /**
9996
11532
  * Buffers a `RemoteDocumentCache.removeEntry()` call.
@@ -9998,12 +11534,9 @@ class RemoteDocumentChangeBuffer {
9998
11534
  * You can only remove documents that have already been retrieved via
9999
11535
  * `getEntry()/getEntries()` (enforced via IndexedDbs `apply()`).
10000
11536
  */
10001
- removeEntry(key, readTime = null) {
11537
+ removeEntry(key, readTime) {
10002
11538
  this.assertNotApplied();
10003
- this.changes.set(key, {
10004
- document: MutableDocument.newInvalidDocument(key),
10005
- readTime
10006
- });
11539
+ this.changes.set(key, MutableDocument.newInvalidDocument(key).setReadTime(readTime));
10007
11540
  }
10008
11541
  /**
10009
11542
  * Looks up an entry in the cache. The buffered changes will first be checked,
@@ -10020,7 +11553,7 @@ class RemoteDocumentChangeBuffer {
10020
11553
  this.assertNotApplied();
10021
11554
  const bufferedEntry = this.changes.get(documentKey);
10022
11555
  if (bufferedEntry !== undefined) {
10023
- return PersistencePromise.resolve(bufferedEntry.document);
11556
+ return PersistencePromise.resolve(bufferedEntry);
10024
11557
  }
10025
11558
  else {
10026
11559
  return this.getFromCache(transaction, documentKey);
@@ -10074,12 +11607,10 @@ class RemoteDocumentChangeBuffer {
10074
11607
  * `newIndexedDbRemoteDocumentCache()`.
10075
11608
  */
10076
11609
  class IndexedDbRemoteDocumentCacheImpl {
10077
- /**
10078
- * @param serializer - The document serializer.
10079
- * @param indexManager - The query indexes that need to be maintained.
10080
- */
10081
- constructor(serializer, indexManager) {
11610
+ constructor(serializer) {
10082
11611
  this.serializer = serializer;
11612
+ }
11613
+ setIndexManager(indexManager) {
10083
11614
  this.indexManager = indexManager;
10084
11615
  }
10085
11616
  /**
@@ -10201,21 +11732,21 @@ class IndexedDbRemoteDocumentCacheImpl {
10201
11732
  }
10202
11733
  });
10203
11734
  }
10204
- getDocumentsMatchingQuery(transaction, query, sinceReadTime) {
11735
+ getAll(transaction, collection, sinceReadTime) {
10205
11736
  let results = mutableDocumentMap();
10206
- const immediateChildrenPathLength = query.path.length + 1;
11737
+ const immediateChildrenPathLength = collection.length + 1;
10207
11738
  const iterationOptions = {};
10208
11739
  if (sinceReadTime.isEqual(SnapshotVersion.min())) {
10209
11740
  // Documents are ordered by key, so we can use a prefix scan to narrow
10210
11741
  // down the documents we need to match the query against.
10211
- const startKey = query.path.toArray();
11742
+ const startKey = collection.toArray();
10212
11743
  iterationOptions.range = IDBKeyRange.lowerBound(startKey);
10213
11744
  }
10214
11745
  else {
10215
11746
  // Execute an index-free query and filter by read time. This is safe
10216
11747
  // since all document changes to queries that have a
10217
11748
  // lastLimboFreeSnapshotVersion (`sinceReadTime`) have a read time set.
10218
- const collectionKey = query.path.toArray();
11749
+ const collectionKey = collection.toArray();
10219
11750
  const readTimeKey = toDbTimestampKey(sinceReadTime);
10220
11751
  iterationOptions.range = IDBKeyRange.lowerBound([collectionKey, readTimeKey],
10221
11752
  /* open= */ true);
@@ -10231,13 +11762,13 @@ class IndexedDbRemoteDocumentCacheImpl {
10231
11762
  if (key.length !== immediateChildrenPathLength) {
10232
11763
  return;
10233
11764
  }
10234
- const document = fromDbRemoteDocument(this.serializer, dbRemoteDoc);
10235
- if (!query.path.isPrefixOf(document.key.path)) {
10236
- control.done();
10237
- }
10238
- else if (queryMatches(query, document)) {
11765
+ const document = this.maybeDecodeDocument(DocumentKey.fromSegments(key), dbRemoteDoc);
11766
+ if (collection.isPrefixOf(document.key.path)) {
10239
11767
  results = results.insert(document.key, document);
10240
11768
  }
11769
+ else {
11770
+ control.done();
11771
+ }
10241
11772
  })
10242
11773
  .next(() => results);
10243
11774
  }
@@ -10259,8 +11790,8 @@ class IndexedDbRemoteDocumentCacheImpl {
10259
11790
  return documentGlobalStore(txn).put(DbRemoteDocumentGlobal.key, metadata);
10260
11791
  }
10261
11792
  /**
10262
- * Decodes `remoteDoc` and returns the document (or null, if the document
10263
- * corresponds to the format used for sentinel deletes).
11793
+ * Decodes `dbRemoteDoc` and returns the document (or an invalid document if
11794
+ * the document corresponds to the format used for sentinel deletes).
10264
11795
  */
10265
11796
  maybeDecodeDocument(documentKey, dbRemoteDoc) {
10266
11797
  if (dbRemoteDoc) {
@@ -10275,14 +11806,9 @@ class IndexedDbRemoteDocumentCacheImpl {
10275
11806
  return MutableDocument.newInvalidDocument(documentKey);
10276
11807
  }
10277
11808
  }
10278
- /**
10279
- * Creates a new IndexedDbRemoteDocumentCache.
10280
- *
10281
- * @param serializer - The document serializer.
10282
- * @param indexManager - The query indexes that need to be maintained.
10283
- */
10284
- function newIndexedDbRemoteDocumentCache(serializer, indexManager) {
10285
- return new IndexedDbRemoteDocumentCacheImpl(serializer, indexManager);
11809
+ /** Creates a new IndexedDbRemoteDocumentCache. */
11810
+ function newIndexedDbRemoteDocumentCache(serializer) {
11811
+ return new IndexedDbRemoteDocumentCacheImpl(serializer);
10286
11812
  }
10287
11813
  /**
10288
11814
  * Returns the set of documents that have changed since the specified read
@@ -10354,8 +11880,8 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
10354
11880
  let collectionParents = new SortedSet((l, r) => primitiveComparator(l.canonicalString(), r.canonicalString()));
10355
11881
  this.changes.forEach((key, documentChange) => {
10356
11882
  const previousSize = this.documentSizes.get(key);
10357
- if (documentChange.document.isValidDocument()) {
10358
- const doc = toDbRemoteDocument(this.documentCache.serializer, documentChange.document, this.getReadTime(key));
11883
+ if (documentChange.isValidDocument()) {
11884
+ const doc = toDbRemoteDocument(this.documentCache.serializer, documentChange);
10359
11885
  collectionParents = collectionParents.add(key.path.popLast());
10360
11886
  const size = dbDocumentSize(doc);
10361
11887
  sizeDelta += size - previousSize;
@@ -10368,7 +11894,7 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
10368
11894
  // RemoteDocumentCache. This entry is represented by a NoDocument
10369
11895
  // with a version of 0 and ignored by `maybeDecodeDocument()` but
10370
11896
  // preserved in `getNewDocumentChanges()`.
10371
- const deletedDoc = toDbRemoteDocument(this.documentCache.serializer, MutableDocument.newNoDocument(key, SnapshotVersion.min()), this.getReadTime(key));
11897
+ const deletedDoc = toDbRemoteDocument(this.documentCache.serializer, documentChange.convertToNoDocument(SnapshotVersion.min()));
10372
11898
  promises.push(this.documentCache.addEntry(transaction, key, deletedDoc));
10373
11899
  }
10374
11900
  else {
@@ -10449,9 +11975,6 @@ class SchemaConverter {
10449
11975
  * and local feature development.
10450
11976
  */
10451
11977
  createOrUpgrade(db, txn, fromVersion, toVersion) {
10452
- hardAssert(fromVersion < toVersion &&
10453
- fromVersion >= 0 &&
10454
- toVersion <= SCHEMA_VERSION);
10455
11978
  const simpleDbTransaction = new SimpleDbTransaction('createOrUpgrade', txn);
10456
11979
  if (fromVersion < 1 && toVersion >= 1) {
10457
11980
  createPrimaryClientStore(db);
@@ -10518,6 +12041,16 @@ class SchemaConverter {
10518
12041
  createNamedQueriesStore(db);
10519
12042
  });
10520
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
+ }
10521
12054
  return p;
10522
12055
  }
10523
12056
  addDocumentGlobal(txn) {
@@ -10724,6 +12257,28 @@ function createNamedQueriesStore(db) {
10724
12257
  db.createObjectStore(DbNamedQuery.store, {
10725
12258
  keyPath: DbNamedQuery.keyPath
10726
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 });
10727
12282
  }
10728
12283
 
10729
12284
  /**
@@ -10835,7 +12390,7 @@ class IndexedDbPersistence {
10835
12390
  * If set to true, forcefully obtains database access. Existing tabs will
10836
12391
  * no longer be able to access IndexedDB.
10837
12392
  */
10838
- forceOwningTab) {
12393
+ forceOwningTab, schemaVersion = SCHEMA_VERSION) {
10839
12394
  this.allowTabSynchronization = allowTabSynchronization;
10840
12395
  this.persistenceKey = persistenceKey;
10841
12396
  this.clientId = clientId;
@@ -10844,6 +12399,7 @@ class IndexedDbPersistence {
10844
12399
  this.document = document;
10845
12400
  this.sequenceNumberSyncer = sequenceNumberSyncer;
10846
12401
  this.forceOwningTab = forceOwningTab;
12402
+ this.schemaVersion = schemaVersion;
10847
12403
  this.listenSequence = null;
10848
12404
  this._started = false;
10849
12405
  this.isPrimary = false;
@@ -10865,10 +12421,9 @@ class IndexedDbPersistence {
10865
12421
  this.referenceDelegate = new IndexedDbLruDelegateImpl(this, lruParams);
10866
12422
  this.dbName = persistenceKey + MAIN_DATABASE;
10867
12423
  this.serializer = new LocalSerializer(serializer);
10868
- 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));
10869
12425
  this.targetCache = new IndexedDbTargetCache(this.referenceDelegate, this.serializer);
10870
- this.indexManager = new IndexedDbIndexManager();
10871
- this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer, this.indexManager);
12426
+ this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer);
10872
12427
  this.bundleCache = new IndexedDbBundleCache();
10873
12428
  if (this.window && this.window.localStorage) {
10874
12429
  this.webStorage = this.window.localStorage;
@@ -11204,8 +12759,8 @@ class IndexedDbPersistence {
11204
12759
  get started() {
11205
12760
  return this._started;
11206
12761
  }
11207
- getMutationQueue(user) {
11208
- return IndexedDbMutationQueue.forUser(user, this.serializer, this.indexManager, this.referenceDelegate);
12762
+ getMutationQueue(user, indexManager) {
12763
+ return IndexedDbMutationQueue.forUser(user, this.serializer, indexManager, this.referenceDelegate);
11209
12764
  }
11210
12765
  getTargetCache() {
11211
12766
  return this.targetCache;
@@ -11213,8 +12768,11 @@ class IndexedDbPersistence {
11213
12768
  getRemoteDocumentCache() {
11214
12769
  return this.remoteDocumentCache;
11215
12770
  }
11216
- getIndexManager() {
11217
- return this.indexManager;
12771
+ getIndexManager(user) {
12772
+ return new IndexedDbIndexManager(user);
12773
+ }
12774
+ getDocumentOverlayCache(user) {
12775
+ return IndexedDbDocumentOverlayCache.forUser(this.serializer, user);
11218
12776
  }
11219
12777
  getBundleCache() {
11220
12778
  return this.bundleCache;
@@ -11222,11 +12780,12 @@ class IndexedDbPersistence {
11222
12780
  runTransaction(action, mode, transactionOperation) {
11223
12781
  logDebug(LOG_TAG$d, 'Starting transaction:', action);
11224
12782
  const simpleDbMode = mode === 'readonly' ? 'readonly' : 'readwrite';
12783
+ const objectStores = getObjectStores(this.schemaVersion);
11225
12784
  let persistenceTransaction;
11226
12785
  // Do all transactions as readwrite against all object stores, since we
11227
12786
  // are the only reader/writer.
11228
12787
  return this.simpleDb
11229
- .runTransaction(action, simpleDbMode, ALL_STORES, simpleDbTxn => {
12788
+ .runTransaction(action, simpleDbMode, objectStores, simpleDbTxn => {
11230
12789
  persistenceTransaction = new IndexedDbTransaction(simpleDbTxn, this.listenSequence
11231
12790
  ? this.listenSequence.next()
11232
12791
  : ListenSequence.INVALID);
@@ -11598,39 +13157,28 @@ class LocalDocumentsView {
11598
13157
  getDocumentsMatchingCollectionQuery(transaction, query, sinceReadTime) {
11599
13158
  // Query the remote documents and overlay mutations.
11600
13159
  let results;
11601
- let mutationBatches;
11602
13160
  return this.remoteDocumentCache
11603
- .getDocumentsMatchingQuery(transaction, query, sinceReadTime)
13161
+ .getAll(transaction, query.path, sinceReadTime)
11604
13162
  .next(queryResults => {
11605
13163
  results = queryResults;
11606
13164
  return this.mutationQueue.getAllMutationBatchesAffectingQuery(transaction, query);
11607
13165
  })
11608
- .next(matchingMutationBatches => {
11609
- mutationBatches = matchingMutationBatches;
11610
- // It is possible that a PatchMutation can make a document match a query, even if
11611
- // the version in the RemoteDocumentCache is not a match yet (waiting for server
11612
- // to ack). To handle this, we find all document keys affected by the PatchMutations
11613
- // that are not in `result` yet, and back fill them via `remoteDocumentCache.getEntries`,
11614
- // otherwise those `PatchMutations` will be ignored because no base document can be found,
11615
- // and lead to missing result for the query.
11616
- return this.addMissingBaseDocuments(transaction, mutationBatches, results).next(mergedDocuments => {
11617
- results = mergedDocuments;
11618
- for (const batch of mutationBatches) {
11619
- for (const mutation of batch.mutations) {
11620
- const key = mutation.key;
11621
- let document = results.get(key);
11622
- if (document == null) {
11623
- // Create invalid document to apply mutations on top of
11624
- document = MutableDocument.newInvalidDocument(key);
11625
- results = results.insert(key, document);
11626
- }
11627
- mutationApplyToLocalView(mutation, document, batch.localWriteTime);
11628
- if (!document.isFoundDocument()) {
11629
- results = results.remove(key);
11630
- }
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);
11631
13179
  }
11632
13180
  }
11633
- });
13181
+ }
11634
13182
  })
11635
13183
  .next(() => {
11636
13184
  // Finally, filter out any documents that don't actually match
@@ -11643,29 +13191,6 @@ class LocalDocumentsView {
11643
13191
  return results;
11644
13192
  });
11645
13193
  }
11646
- addMissingBaseDocuments(transaction, matchingMutationBatches, existingDocuments) {
11647
- let missingBaseDocEntriesForPatching = documentKeySet();
11648
- for (const batch of matchingMutationBatches) {
11649
- for (const mutation of batch.mutations) {
11650
- if (mutation instanceof PatchMutation &&
11651
- existingDocuments.get(mutation.key) === null) {
11652
- missingBaseDocEntriesForPatching =
11653
- missingBaseDocEntriesForPatching.add(mutation.key);
11654
- }
11655
- }
11656
- }
11657
- let mergedDocuments = existingDocuments;
11658
- return this.remoteDocumentCache
11659
- .getEntries(transaction, missingBaseDocEntriesForPatching)
11660
- .next(missingBaseDocs => {
11661
- missingBaseDocs.forEach((key, doc) => {
11662
- if (doc.isFoundDocument()) {
11663
- mergedDocuments = mergedDocuments.insert(key, doc);
11664
- }
11665
- });
11666
- return mergedDocuments;
11667
- });
11668
- }
11669
13194
  }
11670
13195
 
11671
13196
  /**
@@ -11724,11 +13249,18 @@ class LocalStoreImpl {
11724
13249
  * PORTING NOTE: This is only used for multi-tab synchronization.
11725
13250
  */
11726
13251
  this.lastDocumentChangeReadTime = SnapshotVersion.min();
11727
- this.mutationQueue = persistence.getMutationQueue(initialUser);
11728
13252
  this.remoteDocuments = persistence.getRemoteDocumentCache();
11729
13253
  this.targetCache = persistence.getTargetCache();
11730
- this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.persistence.getIndexManager());
11731
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);
11732
13264
  this.queryEngine.setLocalDocumentsView(this.localDocuments);
11733
13265
  }
11734
13266
  collectGarbage(garbageCollector) {
@@ -11750,8 +13282,6 @@ persistence, queryEngine, initialUser, serializer) {
11750
13282
  // change.
11751
13283
  async function localStoreHandleUserChange(localStore, user) {
11752
13284
  const localStoreImpl = debugCast(localStore);
11753
- let newMutationQueue = localStoreImpl.mutationQueue;
11754
- let newLocalDocuments = localStoreImpl.localDocuments;
11755
13285
  const result = await localStoreImpl.persistence.runTransaction('Handle user change', 'readonly', txn => {
11756
13286
  // Swap out the mutation queue, grabbing the pending mutation batches
11757
13287
  // before and after.
@@ -11760,11 +13290,8 @@ async function localStoreHandleUserChange(localStore, user) {
11760
13290
  .getAllMutationBatches(txn)
11761
13291
  .next(promisedOldBatches => {
11762
13292
  oldBatches = promisedOldBatches;
11763
- newMutationQueue = localStoreImpl.persistence.getMutationQueue(user);
11764
- // Recreate our LocalDocumentsView using the new
11765
- // MutationQueue.
11766
- newLocalDocuments = new LocalDocumentsView(localStoreImpl.remoteDocuments, newMutationQueue, localStoreImpl.persistence.getIndexManager());
11767
- return newMutationQueue.getAllMutationBatches(txn);
13293
+ localStoreImpl.initializeUserComponents(user);
13294
+ return localStoreImpl.mutationQueue.getAllMutationBatches(txn);
11768
13295
  })
11769
13296
  .next(newBatches => {
11770
13297
  const removedBatchIds = [];
@@ -11785,7 +13312,7 @@ async function localStoreHandleUserChange(localStore, user) {
11785
13312
  }
11786
13313
  // Return the set of all (potentially) changed documents and the list
11787
13314
  // of mutation batch IDs that were affected by change.
11788
- return newLocalDocuments
13315
+ return localStoreImpl.localDocuments
11789
13316
  .getDocuments(txn, changedKeys)
11790
13317
  .next(affectedDocuments => {
11791
13318
  return {
@@ -11796,9 +13323,6 @@ async function localStoreHandleUserChange(localStore, user) {
11796
13323
  });
11797
13324
  });
11798
13325
  });
11799
- localStoreImpl.mutationQueue = newMutationQueue;
11800
- localStoreImpl.localDocuments = newLocalDocuments;
11801
- localStoreImpl.queryEngine.setLocalDocumentsView(localStoreImpl.localDocuments);
11802
13326
  return result;
11803
13327
  }
11804
13328
  /* Accepts locally generated Mutations and commit them to storage. */
@@ -11955,14 +13479,14 @@ function localStoreApplyRemoteEventToLocalCache(localStore, remoteEvent) {
11955
13479
  }
11956
13480
  });
11957
13481
  let changedDocs = mutableDocumentMap();
11958
- remoteEvent.documentUpdates.forEach((key, doc) => {
13482
+ remoteEvent.documentUpdates.forEach(key => {
11959
13483
  if (remoteEvent.resolvedLimboDocuments.has(key)) {
11960
13484
  promises.push(localStoreImpl.persistence.referenceDelegate.updateLimboDocument(txn, key));
11961
13485
  }
11962
13486
  });
11963
13487
  // Each loop iteration only affects its "own" doc, so it's safe to get all the remote
11964
13488
  // documents in advance in a single call.
11965
- promises.push(populateDocumentChangeBuffer(txn, documentBuffer, remoteEvent.documentUpdates, remoteVersion, undefined).next(result => {
13489
+ promises.push(populateDocumentChangeBuffer(txn, documentBuffer, remoteEvent.documentUpdates).next(result => {
11966
13490
  changedDocs = result;
11967
13491
  }));
11968
13492
  // HACK: The only reason we allow a null snapshot version is so that we
@@ -12003,17 +13527,13 @@ function localStoreApplyRemoteEventToLocalCache(localStore, remoteEvent) {
12003
13527
  * Note: this function will use `documentVersions` if it is defined;
12004
13528
  * when it is not defined, resorts to `globalVersion`.
12005
13529
  */
12006
- function populateDocumentChangeBuffer(txn, documentBuffer, documents, globalVersion,
12007
- // TODO(wuandy): We could add `readTime` to MaybeDocument instead to remove
12008
- // this parameter.
12009
- documentVersions) {
13530
+ function populateDocumentChangeBuffer(txn, documentBuffer, documents) {
12010
13531
  let updatedKeys = documentKeySet();
12011
13532
  documents.forEach(k => (updatedKeys = updatedKeys.add(k)));
12012
13533
  return documentBuffer.getEntries(txn, updatedKeys).next(existingDocs => {
12013
13534
  let changedDocs = mutableDocumentMap();
12014
13535
  documents.forEach((key, doc) => {
12015
13536
  const existingDoc = existingDocs.get(key);
12016
- const docReadTime = (documentVersions === null || documentVersions === void 0 ? void 0 : documentVersions.get(key)) || globalVersion;
12017
13537
  // Note: The order of the steps below is important, since we want
12018
13538
  // to ensure that rejected limbo resolutions (which fabricate
12019
13539
  // NoDocuments with SnapshotVersion.min()) never add documents to
@@ -12022,14 +13542,14 @@ documentVersions) {
12022
13542
  // NoDocuments with SnapshotVersion.min() are used in manufactured
12023
13543
  // events. We remove these documents from cache since we lost
12024
13544
  // access.
12025
- documentBuffer.removeEntry(key, docReadTime);
13545
+ documentBuffer.removeEntry(key, doc.readTime);
12026
13546
  changedDocs = changedDocs.insert(key, doc);
12027
13547
  }
12028
13548
  else if (!existingDoc.isValidDocument() ||
12029
13549
  doc.version.compareTo(existingDoc.version) > 0 ||
12030
13550
  (doc.version.compareTo(existingDoc.version) === 0 &&
12031
13551
  existingDoc.hasPendingWrites)) {
12032
- documentBuffer.addEntry(doc, docReadTime);
13552
+ documentBuffer.addEntry(doc);
12033
13553
  changedDocs = changedDocs.insert(key, doc);
12034
13554
  }
12035
13555
  else {
@@ -12283,7 +13803,8 @@ function applyWriteToRemoteDocuments(localStoreImpl, txn, batchResult, documentB
12283
13803
  // We use the commitVersion as the readTime rather than the
12284
13804
  // document's updateTime since the updateTime is not advanced
12285
13805
  // for updates that do not modify the underlying document.
12286
- documentBuffer.addEntry(doc, batchResult.commitVersion);
13806
+ doc.setReadTime(batchResult.commitVersion);
13807
+ documentBuffer.addEntry(doc);
12287
13808
  }
12288
13809
  }
12289
13810
  });
@@ -12384,14 +13905,14 @@ async function localStoreApplyBundledDocuments(localStore, bundleConverter, docu
12384
13905
  const localStoreImpl = debugCast(localStore);
12385
13906
  let documentKeys = documentKeySet();
12386
13907
  let documentMap = mutableDocumentMap();
12387
- let versionMap = documentVersionMap();
12388
13908
  for (const bundleDoc of documents) {
12389
13909
  const documentKey = bundleConverter.toDocumentKey(bundleDoc.metadata.name);
12390
13910
  if (bundleDoc.document) {
12391
13911
  documentKeys = documentKeys.add(documentKey);
12392
13912
  }
12393
- documentMap = documentMap.insert(documentKey, bundleConverter.toMutableDocument(bundleDoc));
12394
- versionMap = versionMap.insert(documentKey, bundleConverter.toSnapshotVersion(bundleDoc.metadata.readTime));
13913
+ const doc = bundleConverter.toMutableDocument(bundleDoc);
13914
+ doc.setReadTime(bundleConverter.toSnapshotVersion(bundleDoc.metadata.readTime));
13915
+ documentMap = documentMap.insert(documentKey, doc);
12395
13916
  }
12396
13917
  const documentBuffer = localStoreImpl.remoteDocuments.newChangeBuffer({
12397
13918
  trackRemovals: true // Make sure document removals show up in `getNewDocumentChanges()`
@@ -12400,7 +13921,7 @@ async function localStoreApplyBundledDocuments(localStore, bundleConverter, docu
12400
13921
  // they will not get garbage collected right away.
12401
13922
  const umbrellaTargetData = await localStoreAllocateTarget(localStoreImpl, umbrellaTarget(bundleName));
12402
13923
  return localStoreImpl.persistence.runTransaction('Apply bundle documents', 'readwrite', txn => {
12403
- return populateDocumentChangeBuffer(txn, documentBuffer, documentMap, SnapshotVersion.min(), versionMap)
13924
+ return populateDocumentChangeBuffer(txn, documentBuffer, documentMap)
12404
13925
  .next(changedDocs => {
12405
13926
  documentBuffer.apply(txn);
12406
13927
  return changedDocs;
@@ -12514,6 +14035,122 @@ class MemoryBundleCache {
12514
14035
  }
12515
14036
  }
12516
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
+
12517
14154
  /**
12518
14155
  * @license
12519
14156
  * Copyright 2017 Google LLC
@@ -12867,29 +14504,30 @@ class MemoryRemoteDocumentCacheImpl {
12867
14504
  * expected to just return 0 to avoid unnecessarily doing the work of
12868
14505
  * calculating the size.
12869
14506
  */
12870
- constructor(indexManager, sizer) {
12871
- this.indexManager = indexManager;
14507
+ constructor(sizer) {
12872
14508
  this.sizer = sizer;
12873
14509
  /** Underlying cache of documents and their read times. */
12874
14510
  this.docs = documentEntryMap();
12875
14511
  /** Size of all cached documents. */
12876
14512
  this.size = 0;
12877
14513
  }
14514
+ setIndexManager(indexManager) {
14515
+ this.indexManager = indexManager;
14516
+ }
12878
14517
  /**
12879
14518
  * Adds the supplied entry to the cache and updates the cache size as appropriate.
12880
14519
  *
12881
14520
  * All calls of `addEntry` are required to go through the RemoteDocumentChangeBuffer
12882
14521
  * returned by `newChangeBuffer()`.
12883
14522
  */
12884
- addEntry(transaction, doc, readTime) {
14523
+ addEntry(transaction, doc) {
12885
14524
  const key = doc.key;
12886
14525
  const entry = this.docs.get(key);
12887
14526
  const previousSize = entry ? entry.size : 0;
12888
14527
  const currentSize = this.sizer(doc);
12889
14528
  this.docs = this.docs.insert(key, {
12890
14529
  document: doc.mutableCopy(),
12891
- size: currentSize,
12892
- readTime
14530
+ size: currentSize
12893
14531
  });
12894
14532
  this.size += currentSize - previousSize;
12895
14533
  return this.indexManager.addToCollectionParentIndex(transaction, key.path.popLast());
@@ -12923,21 +14561,22 @@ class MemoryRemoteDocumentCacheImpl {
12923
14561
  });
12924
14562
  return PersistencePromise.resolve(results);
12925
14563
  }
12926
- getDocumentsMatchingQuery(transaction, query, sinceReadTime) {
14564
+ getAll(transaction, collectionPath, sinceReadTime) {
12927
14565
  let results = mutableDocumentMap();
12928
14566
  // Documents are ordered by key, so we can use a prefix scan to narrow down
12929
14567
  // the documents we need to match the query against.
12930
- const prefix = new DocumentKey(query.path.child(''));
14568
+ const prefix = new DocumentKey(collectionPath.child(''));
12931
14569
  const iterator = this.docs.getIteratorFrom(prefix);
12932
14570
  while (iterator.hasNext()) {
12933
- const { key, value: { document, readTime } } = iterator.getNext();
12934
- if (!query.path.isPrefixOf(key.path)) {
14571
+ const { key, value: { document } } = iterator.getNext();
14572
+ if (!collectionPath.isPrefixOf(key.path)) {
12935
14573
  break;
12936
14574
  }
12937
- if (readTime.compareTo(sinceReadTime) <= 0) {
14575
+ if (key.path.length > collectionPath.length + 1) {
14576
+ // Exclude entries from subcollections.
12938
14577
  continue;
12939
14578
  }
12940
- if (!queryMatches(query, document)) {
14579
+ if (document.readTime.compareTo(sinceReadTime) <= 0) {
12941
14580
  continue;
12942
14581
  }
12943
14582
  results = results.insert(document.key, document.mutableCopy());
@@ -12959,13 +14598,12 @@ class MemoryRemoteDocumentCacheImpl {
12959
14598
  /**
12960
14599
  * Creates a new memory-only RemoteDocumentCache.
12961
14600
  *
12962
- * @param indexManager - A class that manages collection group indices.
12963
14601
  * @param sizer - Used to assess the size of a document. For eager GC, this is
12964
14602
  * expected to just return 0 to avoid unnecessarily doing the work of
12965
14603
  * calculating the size.
12966
14604
  */
12967
- function newMemoryRemoteDocumentCache(indexManager, sizer) {
12968
- return new MemoryRemoteDocumentCacheImpl(indexManager, sizer);
14605
+ function newMemoryRemoteDocumentCache(sizer) {
14606
+ return new MemoryRemoteDocumentCacheImpl(sizer);
12969
14607
  }
12970
14608
  /**
12971
14609
  * Handles the details of adding and updating documents in the MemoryRemoteDocumentCache.
@@ -12978,8 +14616,8 @@ class MemoryRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
12978
14616
  applyChanges(transaction) {
12979
14617
  const promises = [];
12980
14618
  this.changes.forEach((key, doc) => {
12981
- if (doc.document.isValidDocument()) {
12982
- promises.push(this.documentCache.addEntry(transaction, doc.document, this.getReadTime(key)));
14619
+ if (doc.isValidDocument()) {
14620
+ promises.push(this.documentCache.addEntry(transaction, doc));
12983
14621
  }
12984
14622
  else {
12985
14623
  this.documentCache.removeEntry(key);
@@ -13159,6 +14797,7 @@ class MemoryPersistence {
13159
14797
  */
13160
14798
  constructor(referenceDelegateFactory, serializer) {
13161
14799
  this.mutationQueues = {};
14800
+ this.overlays = {};
13162
14801
  this.listenSequence = new ListenSequence(0);
13163
14802
  this._started = false;
13164
14803
  this._started = true;
@@ -13166,7 +14805,7 @@ class MemoryPersistence {
13166
14805
  this.targetCache = new MemoryTargetCache(this);
13167
14806
  const sizer = (doc) => this.referenceDelegate.documentSize(doc);
13168
14807
  this.indexManager = new MemoryIndexManager();
13169
- this.remoteDocumentCache = newMemoryRemoteDocumentCache(this.indexManager, sizer);
14808
+ this.remoteDocumentCache = newMemoryRemoteDocumentCache(sizer);
13170
14809
  this.serializer = new LocalSerializer(serializer);
13171
14810
  this.bundleCache = new MemoryBundleCache(this.serializer);
13172
14811
  }
@@ -13187,13 +14826,23 @@ class MemoryPersistence {
13187
14826
  setNetworkEnabled() {
13188
14827
  // No op.
13189
14828
  }
13190
- 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.
13191
14832
  return this.indexManager;
13192
14833
  }
13193
- 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) {
13194
14843
  let queue = this.mutationQueues[user.toKey()];
13195
14844
  if (!queue) {
13196
- queue = new MemoryMutationQueue(this.indexManager, this.referenceDelegate);
14845
+ queue = new MemoryMutationQueue(indexManager, this.referenceDelegate);
13197
14846
  this.mutationQueues[user.toKey()] = queue;
13198
14847
  }
13199
14848
  return queue;
@@ -13292,7 +14941,7 @@ class MemoryEagerDelegate {
13292
14941
  const key = DocumentKey.fromPath(path);
13293
14942
  return this.isReferenced(txn, key).next(isReferenced => {
13294
14943
  if (!isReferenced) {
13295
- changeBuffer.removeEntry(key);
14944
+ changeBuffer.removeEntry(key, SnapshotVersion.min());
13296
14945
  }
13297
14946
  });
13298
14947
  }).next(() => {
@@ -14338,7 +15987,7 @@ const LOG_TAG$9 = 'Connection';
14338
15987
  const X_GOOG_API_CLIENT_VALUE = `gl-node/${process.versions.node} fire/${SDK_VERSION} grpc/${grpcVersion}`;
14339
15988
  function createMetadata(databasePath, authToken, appCheckToken, appId) {
14340
15989
  hardAssert(authToken === null || authToken.type === 'OAuth');
14341
- const metadata = new grpcJs.Metadata();
15990
+ const metadata = new grpc__namespace.Metadata();
14342
15991
  if (authToken) {
14343
15992
  authToken.headers.forEach((value, key) => metadata.set(key, value));
14344
15993
  }
@@ -14369,10 +16018,10 @@ class GrpcConnection {
14369
16018
  ensureActiveStub() {
14370
16019
  if (!this.cachedStub) {
14371
16020
  logDebug(LOG_TAG$9, 'Creating Firestore stub.');
14372
- const credentials$1 = this.databaseInfo.ssl
14373
- ? grpcJs.credentials.createSsl()
14374
- : grpcJs.credentials.createInsecure();
14375
- 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);
14376
16025
  }
14377
16026
  return this.cachedStub;
14378
16027
  }
@@ -17072,6 +18721,40 @@ const nested = {
17072
18721
  id: 2
17073
18722
  }
17074
18723
  }
18724
+ },
18725
+ methodSignature: {
18726
+ rule: "repeated",
18727
+ type: "string",
18728
+ id: 1051,
18729
+ extend: "google.protobuf.MethodOptions"
18730
+ },
18731
+ defaultHost: {
18732
+ type: "string",
18733
+ id: 1049,
18734
+ extend: "google.protobuf.ServiceOptions"
18735
+ },
18736
+ oauthScopes: {
18737
+ type: "string",
18738
+ id: 1050,
18739
+ extend: "google.protobuf.ServiceOptions"
18740
+ },
18741
+ fieldBehavior: {
18742
+ rule: "repeated",
18743
+ type: "google.api.FieldBehavior",
18744
+ id: 1052,
18745
+ extend: "google.protobuf.FieldOptions"
18746
+ },
18747
+ FieldBehavior: {
18748
+ values: {
18749
+ FIELD_BEHAVIOR_UNSPECIFIED: 0,
18750
+ OPTIONAL: 1,
18751
+ REQUIRED: 2,
18752
+ OUTPUT_ONLY: 3,
18753
+ INPUT_ONLY: 4,
18754
+ IMMUTABLE: 5,
18755
+ UNORDERED_LIST: 6,
18756
+ NON_EMPTY_DEFAULT: 7
18757
+ }
17075
18758
  }
17076
18759
  }
17077
18760
  },
@@ -17170,8 +18853,8 @@ const protoLoaderOptions = {
17170
18853
  * @returns The GrpcObject representing our protos.
17171
18854
  */
17172
18855
  function loadProtos() {
17173
- const packageDefinition = protoLoader.fromJSON(protos$1, protoLoaderOptions);
17174
- return grpcJs.loadPackageDefinition(packageDefinition);
18856
+ const packageDefinition = protoLoader__namespace.fromJSON(protos$1, protoLoaderOptions);
18857
+ return grpc__namespace.loadPackageDefinition(packageDefinition);
17175
18858
  }
17176
18859
 
17177
18860
  /**
@@ -20122,12 +21805,12 @@ async function syncEngineListen(syncEngine, query) {
20122
21805
  }
20123
21806
  else {
20124
21807
  const targetData = await localStoreAllocateTarget(syncEngineImpl.localStore, queryToTarget(query));
20125
- const status = syncEngineImpl.sharedClientState.addLocalQueryTarget(targetData.targetId);
20126
- targetId = targetData.targetId;
20127
- viewSnapshot = await initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId, status === 'current');
20128
21808
  if (syncEngineImpl.isPrimaryClient) {
20129
21809
  remoteStoreListen(syncEngineImpl.remoteStore, targetData);
20130
21810
  }
21811
+ const status = syncEngineImpl.sharedClientState.addLocalQueryTarget(targetData.targetId);
21812
+ targetId = targetData.targetId;
21813
+ viewSnapshot = await initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId, status === 'current');
20131
21814
  }
20132
21815
  return viewSnapshot;
20133
21816
  }
@@ -20315,6 +21998,9 @@ async function syncEngineRejectListen(syncEngine, targetId, err) {
20315
21998
  // store to purge a document. However, it would be tricky to keep all of
20316
21999
  // the local store's invariants with another method.
20317
22000
  let documentUpdates = new SortedMap(DocumentKey.comparator);
22001
+ // TODO(b/217189216): This limbo document should ideally have a read time,
22002
+ // so that it is picked up by any read-time based scans. The backend,
22003
+ // however, does not send a read time for target removals.
20318
22004
  documentUpdates = documentUpdates.insert(limboKey, MutableDocument.newNoDocument(limboKey, SnapshotVersion.min()));
20319
22005
  const resolvedLimboDocuments = documentKeySet().add(limboKey);
20320
22006
  const event = new RemoteEvent(SnapshotVersion.min(),
@@ -22259,72 +23945,6 @@ function createBundleReader(data, serializer) {
22259
23945
  return newBundleReader(toByteStreamReader(content), serializer);
22260
23946
  }
22261
23947
 
22262
- /**
22263
- * @license
22264
- * Copyright 2017 Google LLC
22265
- *
22266
- * Licensed under the Apache License, Version 2.0 (the "License");
22267
- * you may not use this file except in compliance with the License.
22268
- * You may obtain a copy of the License at
22269
- *
22270
- * http://www.apache.org/licenses/LICENSE-2.0
22271
- *
22272
- * Unless required by applicable law or agreed to in writing, software
22273
- * distributed under the License is distributed on an "AS IS" BASIS,
22274
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22275
- * See the License for the specific language governing permissions and
22276
- * limitations under the License.
22277
- */
22278
- class DatabaseInfo {
22279
- /**
22280
- * Constructs a DatabaseInfo using the provided host, databaseId and
22281
- * persistenceKey.
22282
- *
22283
- * @param databaseId - The database to use.
22284
- * @param appId - The Firebase App Id.
22285
- * @param persistenceKey - A unique identifier for this Firestore's local
22286
- * storage (used in conjunction with the databaseId).
22287
- * @param host - The Firestore backend host to connect to.
22288
- * @param ssl - Whether to use SSL when connecting.
22289
- * @param forceLongPolling - Whether to use the forceLongPolling option
22290
- * when using WebChannel as the network transport.
22291
- * @param autoDetectLongPolling - Whether to use the detectBufferingProxy
22292
- * option when using WebChannel as the network transport.
22293
- * @param useFetchStreams Whether to use the Fetch API instead of
22294
- * XMLHTTPRequest
22295
- */
22296
- constructor(databaseId, appId, persistenceKey, host, ssl, forceLongPolling, autoDetectLongPolling, useFetchStreams) {
22297
- this.databaseId = databaseId;
22298
- this.appId = appId;
22299
- this.persistenceKey = persistenceKey;
22300
- this.host = host;
22301
- this.ssl = ssl;
22302
- this.forceLongPolling = forceLongPolling;
22303
- this.autoDetectLongPolling = autoDetectLongPolling;
22304
- this.useFetchStreams = useFetchStreams;
22305
- }
22306
- }
22307
- /** The default database name for a project. */
22308
- const DEFAULT_DATABASE_NAME = '(default)';
22309
- /**
22310
- * Represents the database ID a Firestore client is associated with.
22311
- * @internal
22312
- */
22313
- class DatabaseId {
22314
- constructor(projectId, database) {
22315
- this.projectId = projectId;
22316
- this.database = database ? database : DEFAULT_DATABASE_NAME;
22317
- }
22318
- get isDefaultDatabase() {
22319
- return this.database === DEFAULT_DATABASE_NAME;
22320
- }
22321
- isEqual(other) {
22322
- return (other instanceof DatabaseId &&
22323
- other.projectId === this.projectId &&
22324
- other.database === this.database);
22325
- }
22326
- }
22327
-
22328
23948
  /**
22329
23949
  * @license
22330
23950
  * Copyright 2020 Google LLC
@@ -24698,8 +26318,7 @@ class DocumentSnapshot extends DocumentSnapshot$1 {
24698
26318
  this.metadata = metadata;
24699
26319
  }
24700
26320
  /**
24701
- * Property of the `DocumentSnapshot` that signals whether or not the data
24702
- * exists. True if the document exists.
26321
+ * Returns whether or not the data exists. True if the document exists.
24703
26322
  */
24704
26323
  exists() {
24705
26324
  return super.exists();
@@ -25073,51 +26692,53 @@ function limitToLast(limit) {
25073
26692
  return new QueryLimitConstraint('limitToLast', limit, "L" /* Last */);
25074
26693
  }
25075
26694
  class QueryStartAtConstraint extends QueryConstraint {
25076
- constructor(type, _docOrFields, _before) {
26695
+ constructor(type, _docOrFields, _inclusive) {
25077
26696
  super();
25078
26697
  this.type = type;
25079
26698
  this._docOrFields = _docOrFields;
25080
- this._before = _before;
26699
+ this._inclusive = _inclusive;
25081
26700
  }
25082
26701
  _apply(query) {
25083
- const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._before);
26702
+ const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
25084
26703
  return new Query(query.firestore, query.converter, queryWithStartAt(query._query, bound));
25085
26704
  }
25086
26705
  }
25087
26706
  function startAt(...docOrFields) {
25088
- return new QueryStartAtConstraint('startAt', docOrFields, /*before=*/ true);
26707
+ return new QueryStartAtConstraint('startAt', docOrFields,
26708
+ /*inclusive=*/ true);
25089
26709
  }
25090
26710
  function startAfter(...docOrFields) {
25091
26711
  return new QueryStartAtConstraint('startAfter', docOrFields,
25092
- /*before=*/ false);
26712
+ /*inclusive=*/ false);
25093
26713
  }
25094
26714
  class QueryEndAtConstraint extends QueryConstraint {
25095
- constructor(type, _docOrFields, _before) {
26715
+ constructor(type, _docOrFields, _inclusive) {
25096
26716
  super();
25097
26717
  this.type = type;
25098
26718
  this._docOrFields = _docOrFields;
25099
- this._before = _before;
26719
+ this._inclusive = _inclusive;
25100
26720
  }
25101
26721
  _apply(query) {
25102
- const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._before);
26722
+ const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
25103
26723
  return new Query(query.firestore, query.converter, queryWithEndAt(query._query, bound));
25104
26724
  }
25105
26725
  }
25106
26726
  function endBefore(...docOrFields) {
25107
- return new QueryEndAtConstraint('endBefore', docOrFields, /*before=*/ true);
26727
+ return new QueryEndAtConstraint('endBefore', docOrFields,
26728
+ /*inclusive=*/ false);
25108
26729
  }
25109
26730
  function endAt(...docOrFields) {
25110
- return new QueryEndAtConstraint('endAt', docOrFields, /*before=*/ false);
26731
+ return new QueryEndAtConstraint('endAt', docOrFields, /*inclusive=*/ true);
25111
26732
  }
25112
26733
  /** Helper function to create a bound from a document or fields */
25113
- function newQueryBoundFromDocOrFields(query, methodName, docOrFields, before) {
26734
+ function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive) {
25114
26735
  docOrFields[0] = util$1.getModularInstance(docOrFields[0]);
25115
26736
  if (docOrFields[0] instanceof DocumentSnapshot$1) {
25116
- 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);
25117
26738
  }
25118
26739
  else {
25119
26740
  const reader = newUserDataReader(query.firestore);
25120
- return newQueryBoundFromFields(query._query, query.firestore._databaseId, reader, methodName, docOrFields, before);
26741
+ return newQueryBoundFromFields(query._query, query.firestore._databaseId, reader, methodName, docOrFields, inclusive);
25121
26742
  }
25122
26743
  }
25123
26744
  function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op, value) {
@@ -25175,7 +26796,7 @@ function newQueryOrderBy(query, fieldPath, direction) {
25175
26796
  * of the query or if any of the fields in the order by are an uncommitted
25176
26797
  * server timestamp.
25177
26798
  */
25178
- function newQueryBoundFromDocument(query, databaseId, methodName, doc, before) {
26799
+ function newQueryBoundFromDocument(query, databaseId, methodName, doc, inclusive) {
25179
26800
  if (!doc) {
25180
26801
  throw new FirestoreError(Code.NOT_FOUND, `Can't use a DocumentSnapshot that doesn't exist for ` +
25181
26802
  `${methodName}().`);
@@ -25212,12 +26833,12 @@ function newQueryBoundFromDocument(query, databaseId, methodName, doc, before) {
25212
26833
  }
25213
26834
  }
25214
26835
  }
25215
- return new Bound(components, before);
26836
+ return new Bound(components, inclusive);
25216
26837
  }
25217
26838
  /**
25218
26839
  * Converts a list of field values to a `Bound` for the given query.
25219
26840
  */
25220
- function newQueryBoundFromFields(query, databaseId, dataReader, methodName, values, before) {
26841
+ function newQueryBoundFromFields(query, databaseId, dataReader, methodName, values, inclusive) {
25221
26842
  // Use explicit order by's because it has to match the query the user made
25222
26843
  const orderBy = query.explicitOrderBy;
25223
26844
  if (values.length > orderBy.length) {
@@ -25254,7 +26875,7 @@ function newQueryBoundFromFields(query, databaseId, dataReader, methodName, valu
25254
26875
  components.push(wrapped);
25255
26876
  }
25256
26877
  }
25257
- return new Bound(components, before);
26878
+ return new Bound(components, inclusive);
25258
26879
  }
25259
26880
  /**
25260
26881
  * Parses the given `documentIdValue` into a `ReferenceValue`, throwing
@@ -26238,17 +27859,6 @@ function writeBatch(firestore) {
26238
27859
  ensureFirestoreConfigured(firestore);
26239
27860
  return new WriteBatch(firestore, mutations => executeWrite(firestore, mutations));
26240
27861
  }
26241
- /** An index component consisting of field path and index type. */
26242
- class Segment {
26243
- constructor(
26244
- /** The field path of the component. */
26245
- fieldPath,
26246
- /** The fields sorting order. */
26247
- kind) {
26248
- this.fieldPath = fieldPath;
26249
- this.kind = kind;
26250
- }
26251
- }
26252
27862
 
26253
27863
  /**
26254
27864
  * @license
@@ -26272,27 +27882,29 @@ function setIndexConfiguration(firestore, jsonOrConfiguration) {
26272
27882
  const indexConfiguration = typeof jsonOrConfiguration === 'string'
26273
27883
  ? tryParseJson(jsonOrConfiguration)
26274
27884
  : jsonOrConfiguration;
27885
+ const parsedIndexes = [];
26275
27886
  // PORTING NOTE: We don't return an error if the user has not enabled
26276
27887
  // persistence since `enableIndexeddbPersistence()` can fail on the Web.
26277
27888
  if (Array.isArray(indexConfiguration.indexes)) {
26278
27889
  for (const index of indexConfiguration.indexes) {
26279
- tryGetString(index, 'collectionGroup');
27890
+ const collectionGroup = tryGetString(index, 'collectionGroup');
26280
27891
  const segments = [];
26281
27892
  if (Array.isArray(index.fields)) {
26282
27893
  for (const field of index.fields) {
26283
27894
  const fieldPathString = tryGetString(field, 'fieldPath');
26284
27895
  const fieldPath = fieldPathFromDotSeparatedString('setIndexConfiguration', fieldPathString);
26285
27896
  if (field.arrayConfig === 'CONTAINS') {
26286
- segments.push(new Segment(fieldPath, 2 /* CONTAINS */));
27897
+ segments.push(new IndexSegment(fieldPath, 2 /* CONTAINS */));
26287
27898
  }
26288
27899
  else if (field.order === 'ASCENDING') {
26289
- segments.push(new Segment(fieldPath, 0 /* ASCENDING */));
27900
+ segments.push(new IndexSegment(fieldPath, 0 /* ASCENDING */));
26290
27901
  }
26291
27902
  else if (field.order === 'DESCENDING') {
26292
- segments.push(new Segment(fieldPath, 1 /* DESCENDING */));
27903
+ segments.push(new IndexSegment(fieldPath, 1 /* DESCENDING */));
26293
27904
  }
26294
27905
  }
26295
27906
  }
27907
+ parsedIndexes.push(new FieldIndex(FieldIndex.UNKNOWN_ID, collectionGroup, segments, IndexState.empty()));
26296
27908
  }
26297
27909
  }
26298
27910
  // TODO(indexing): Configure indexes