@firebase/firestore 3.4.4-canary.f5ac47fb1 → 3.4.5

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 (151) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/firestore/src/api/credentials.d.ts +1 -0
  3. package/dist/firestore/src/index/index_byte_encoder.d.ts +18 -1
  4. package/dist/firestore/src/index/index_entry.d.ts +26 -0
  5. package/dist/firestore/src/local/document_overlay_cache.d.ts +72 -0
  6. package/dist/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
  7. package/dist/firestore/src/local/indexeddb_index_manager.d.ts +29 -2
  8. package/dist/firestore/src/local/indexeddb_persistence.d.ts +6 -4
  9. package/dist/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  10. package/dist/firestore/src/local/indexeddb_schema.d.ts +83 -9
  11. package/dist/firestore/src/local/local_documents_view.d.ts +0 -1
  12. package/dist/firestore/src/local/local_serializer.d.ts +18 -2
  13. package/dist/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
  14. package/dist/firestore/src/local/memory_persistence.d.ts +6 -2
  15. package/dist/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  16. package/dist/firestore/src/local/persistence.d.ts +8 -2
  17. package/dist/firestore/src/local/remote_document_cache.d.ts +7 -9
  18. package/dist/firestore/src/model/document_key.d.ts +5 -0
  19. package/dist/firestore/src/model/field_index.d.ts +2 -2
  20. package/dist/firestore/src/model/overlay.d.ts +32 -0
  21. package/dist/firestore/src/model/target_index_matcher.d.ts +70 -0
  22. package/dist/firestore/src/platform/node/grpc_connection.d.ts +2 -2
  23. package/dist/firestore/src/platform/node/load_protos.d.ts +2 -2
  24. package/dist/firestore/src/util/sorted_set.d.ts +18 -0
  25. package/dist/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  26. package/dist/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
  27. package/dist/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  28. package/dist/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
  29. package/dist/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  30. package/dist/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  31. package/dist/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  32. package/dist/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  33. package/dist/index.esm2017.js +4367 -3517
  34. package/dist/index.esm2017.js.map +1 -1
  35. package/dist/index.esm5.js +4226 -3437
  36. package/dist/index.esm5.js.map +1 -1
  37. package/dist/index.node.cjs.js +1519 -242
  38. package/dist/index.node.cjs.js.map +1 -1
  39. package/dist/index.node.mjs +1501 -245
  40. package/dist/index.node.mjs.map +1 -1
  41. package/dist/index.rn.js +4355 -3509
  42. package/dist/index.rn.js.map +1 -1
  43. package/dist/internal.d.ts +84 -12
  44. package/dist/lite/firestore/src/api/credentials.d.ts +1 -0
  45. package/dist/lite/firestore/src/index/index_byte_encoder.d.ts +18 -1
  46. package/dist/lite/firestore/src/index/index_entry.d.ts +26 -0
  47. package/dist/lite/firestore/src/local/document_overlay_cache.d.ts +72 -0
  48. package/dist/lite/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
  49. package/dist/lite/firestore/src/local/indexeddb_index_manager.d.ts +29 -2
  50. package/dist/lite/firestore/src/local/indexeddb_persistence.d.ts +6 -4
  51. package/dist/lite/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  52. package/dist/lite/firestore/src/local/indexeddb_schema.d.ts +83 -9
  53. package/dist/lite/firestore/src/local/local_documents_view.d.ts +0 -1
  54. package/dist/lite/firestore/src/local/local_serializer.d.ts +18 -2
  55. package/dist/lite/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
  56. package/dist/lite/firestore/src/local/memory_persistence.d.ts +6 -2
  57. package/dist/lite/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  58. package/dist/lite/firestore/src/local/persistence.d.ts +8 -2
  59. package/dist/lite/firestore/src/local/remote_document_cache.d.ts +7 -9
  60. package/dist/lite/firestore/src/model/document_key.d.ts +5 -0
  61. package/dist/lite/firestore/src/model/field_index.d.ts +2 -2
  62. package/dist/lite/firestore/src/model/overlay.d.ts +32 -0
  63. package/dist/lite/firestore/src/model/target_index_matcher.d.ts +70 -0
  64. package/dist/lite/firestore/src/platform/node/grpc_connection.d.ts +2 -2
  65. package/dist/lite/firestore/src/platform/node/load_protos.d.ts +2 -2
  66. package/dist/lite/firestore/src/util/sorted_set.d.ts +18 -0
  67. package/dist/lite/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  68. package/dist/lite/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
  69. package/dist/lite/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  70. package/dist/lite/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
  71. package/dist/lite/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  72. package/dist/lite/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  73. package/dist/lite/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  74. package/dist/lite/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  75. package/dist/lite/index.browser.esm2017.js +1121 -1112
  76. package/dist/lite/index.browser.esm2017.js.map +1 -1
  77. package/dist/lite/index.browser.esm5.js +60 -48
  78. package/dist/lite/index.browser.esm5.js.map +1 -1
  79. package/dist/lite/index.node.cjs.js +13 -2
  80. package/dist/lite/index.node.cjs.js.map +1 -1
  81. package/dist/lite/index.node.mjs +13 -2
  82. package/dist/lite/index.node.mjs.map +1 -1
  83. package/dist/lite/index.rn.esm2017.js +46 -37
  84. package/dist/lite/index.rn.esm2017.js.map +1 -1
  85. package/dist/lite/internal.d.ts +5 -0
  86. package/dist/lite/packages/firestore/dist/lite/index.browser.esm2017.d.ts +112 -112
  87. package/dist/lite/packages/firestore/src/api/credentials.d.ts +1 -0
  88. package/dist/lite/packages/firestore/src/index/index_byte_encoder.d.ts +18 -1
  89. package/dist/lite/packages/firestore/src/index/index_entry.d.ts +26 -0
  90. package/dist/lite/packages/firestore/src/local/document_overlay_cache.d.ts +72 -0
  91. package/dist/lite/packages/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
  92. package/dist/lite/packages/firestore/src/local/indexeddb_index_manager.d.ts +29 -2
  93. package/dist/lite/packages/firestore/src/local/indexeddb_persistence.d.ts +6 -4
  94. package/dist/lite/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  95. package/dist/lite/packages/firestore/src/local/indexeddb_schema.d.ts +83 -9
  96. package/dist/lite/packages/firestore/src/local/local_documents_view.d.ts +0 -1
  97. package/dist/lite/packages/firestore/src/local/local_serializer.d.ts +18 -2
  98. package/dist/lite/packages/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
  99. package/dist/lite/packages/firestore/src/local/memory_persistence.d.ts +6 -2
  100. package/dist/lite/packages/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  101. package/dist/lite/packages/firestore/src/local/persistence.d.ts +8 -2
  102. package/dist/lite/packages/firestore/src/local/remote_document_cache.d.ts +7 -9
  103. package/dist/lite/packages/firestore/src/model/document_key.d.ts +5 -0
  104. package/dist/lite/packages/firestore/src/model/field_index.d.ts +2 -2
  105. package/dist/lite/packages/firestore/src/model/overlay.d.ts +32 -0
  106. package/dist/lite/packages/firestore/src/model/target_index_matcher.d.ts +70 -0
  107. package/dist/lite/packages/firestore/src/platform/node/grpc_connection.d.ts +2 -2
  108. package/dist/lite/packages/firestore/src/platform/node/load_protos.d.ts +2 -2
  109. package/dist/lite/packages/firestore/src/util/sorted_set.d.ts +18 -0
  110. package/dist/lite/packages/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  111. package/dist/lite/packages/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
  112. package/dist/lite/packages/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  113. package/dist/lite/packages/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
  114. package/dist/lite/packages/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  115. package/dist/lite/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  116. package/dist/lite/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  117. package/dist/lite/packages/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  118. package/dist/packages/firestore/dist/index.esm2017.d.ts +187 -184
  119. package/dist/packages/firestore/src/api/credentials.d.ts +1 -0
  120. package/dist/packages/firestore/src/index/index_byte_encoder.d.ts +18 -1
  121. package/dist/packages/firestore/src/index/index_entry.d.ts +26 -0
  122. package/dist/packages/firestore/src/local/document_overlay_cache.d.ts +72 -0
  123. package/dist/packages/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
  124. package/dist/packages/firestore/src/local/indexeddb_index_manager.d.ts +29 -2
  125. package/dist/packages/firestore/src/local/indexeddb_persistence.d.ts +6 -4
  126. package/dist/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  127. package/dist/packages/firestore/src/local/indexeddb_schema.d.ts +83 -9
  128. package/dist/packages/firestore/src/local/local_documents_view.d.ts +0 -1
  129. package/dist/packages/firestore/src/local/local_serializer.d.ts +18 -2
  130. package/dist/packages/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
  131. package/dist/packages/firestore/src/local/memory_persistence.d.ts +6 -2
  132. package/dist/packages/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  133. package/dist/packages/firestore/src/local/persistence.d.ts +8 -2
  134. package/dist/packages/firestore/src/local/remote_document_cache.d.ts +7 -9
  135. package/dist/packages/firestore/src/model/document_key.d.ts +5 -0
  136. package/dist/packages/firestore/src/model/field_index.d.ts +2 -2
  137. package/dist/packages/firestore/src/model/overlay.d.ts +32 -0
  138. package/dist/packages/firestore/src/model/target_index_matcher.d.ts +70 -0
  139. package/dist/packages/firestore/src/platform/node/grpc_connection.d.ts +2 -2
  140. package/dist/packages/firestore/src/platform/node/load_protos.d.ts +2 -2
  141. package/dist/packages/firestore/src/util/sorted_set.d.ts +18 -0
  142. package/dist/packages/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  143. package/dist/packages/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
  144. package/dist/packages/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  145. package/dist/packages/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
  146. package/dist/packages/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  147. package/dist/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  148. package/dist/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  149. package/dist/packages/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  150. package/dist/private.d.ts +79 -12
  151. package/package.json +9 -9
@@ -8,11 +8,32 @@ var logger = require('@firebase/logger');
8
8
  var util = require('util');
9
9
  var util$1 = require('@firebase/util');
10
10
  var crypto = require('crypto');
11
- var grpcJs = require('@grpc/grpc-js');
11
+ var grpc = require('@grpc/grpc-js');
12
12
  var protoLoader = require('@grpc/proto-loader');
13
13
 
14
+ function _interopNamespace(e) {
15
+ if (e && e.__esModule) return e;
16
+ var n = Object.create(null);
17
+ if (e) {
18
+ Object.keys(e).forEach(function (k) {
19
+ if (k !== 'default') {
20
+ var d = Object.getOwnPropertyDescriptor(e, k);
21
+ Object.defineProperty(n, k, d.get ? d : {
22
+ enumerable: true,
23
+ get: function () { return e[k]; }
24
+ });
25
+ }
26
+ });
27
+ }
28
+ n["default"] = e;
29
+ return Object.freeze(n);
30
+ }
31
+
32
+ var grpc__namespace = /*#__PURE__*/_interopNamespace(grpc);
33
+ var protoLoader__namespace = /*#__PURE__*/_interopNamespace(protoLoader);
34
+
14
35
  const name = "@firebase/firestore";
15
- const version$1 = "3.4.4-canary.f5ac47fb1";
36
+ const version$1 = "3.4.5";
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.6-canary.f5ac47fb1";
89
+ const version = "9.6.7";
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 {
@@ -1214,9 +1242,10 @@ function decodeResourcePath(path) {
1214
1242
  * an auto-incrementing ID. This is required for Index-Free queries.
1215
1243
  * 10. Rewrite the canonical IDs to the explicit Protobuf-based format.
1216
1244
  * 11. Add bundles and named_queries for bundle support.
1217
- * 12. Add indexing support.
1245
+ * 12. Add document overlays.
1246
+ * 13. Add indexing support.
1218
1247
  */
1219
- const SCHEMA_VERSION = 11;
1248
+ const SCHEMA_VERSION = 12;
1220
1249
  /**
1221
1250
  * Wrapper class to store timestamps (seconds and nanos) in IndexedDb objects.
1222
1251
  */
@@ -1778,7 +1807,10 @@ DbNamedQuery.keyPath = 'name';
1778
1807
  /** An object representing the global configuration for a field index. */
1779
1808
  class DbIndexConfiguration {
1780
1809
  constructor(
1781
- /** The index id for this entry. */
1810
+ /**
1811
+ * The index id for this entry. Undefined for indexes that are not yet
1812
+ * persisted.
1813
+ */
1782
1814
  indexId,
1783
1815
  /** The collection group this index belongs to. */
1784
1816
  collectionGroup,
@@ -1792,6 +1824,16 @@ class DbIndexConfiguration {
1792
1824
  /** Name of the IndexedDb object store. */
1793
1825
  DbIndexConfiguration.store = 'indexConfiguration';
1794
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';
1795
1837
  /**
1796
1838
  * An object describing how up-to-date the index backfill is for each user and
1797
1839
  * index.
@@ -1833,8 +1875,18 @@ class DbIndexState {
1833
1875
  /** Name of the IndexedDb object store. */
1834
1876
  DbIndexState.store = 'indexState';
1835
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'];
1836
1888
  /** An object that stores the encoded entries for all documents and fields. */
1837
- class DbIndexEntries {
1889
+ class DbIndexEntry {
1838
1890
  constructor(
1839
1891
  /** The index id for this entry. */
1840
1892
  indexId,
@@ -1854,14 +1906,56 @@ class DbIndexEntries {
1854
1906
  }
1855
1907
  }
1856
1908
  /** Name of the IndexedDb object store. */
1857
- DbIndexEntries.store = 'indexEntries';
1858
- DbIndexEntries.keyPath = [
1909
+ DbIndexEntry.store = 'indexEntries';
1910
+ DbIndexEntry.keyPath = [
1859
1911
  'indexId',
1860
1912
  'uid',
1861
1913
  'arrayValue',
1862
1914
  'directionalValue',
1863
1915
  'documentKey'
1864
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
+ ];
1865
1959
  // Visible for testing
1866
1960
  const V1_STORES = [
1867
1961
  DbMutationQueue.store,
@@ -1885,18 +1979,28 @@ const V8_STORES = [...V6_STORES, DbCollectionParent.store];
1885
1979
  // V9 does not change the set of stores.
1886
1980
  // V10 does not change the set of stores.
1887
1981
  const V11_STORES = [...V8_STORES, DbBundle.store, DbNamedQuery.store];
1888
- [
1889
- ...V11_STORES,
1982
+ const V12_STORES = [...V11_STORES, DbDocumentOverlay.store];
1983
+ const V13_STORES = [
1984
+ ...V12_STORES,
1890
1985
  DbIndexConfiguration.store,
1891
1986
  DbIndexState.store,
1892
- DbIndexEntries.store
1987
+ DbIndexEntry.store
1893
1988
  ];
1894
- /**
1895
- * The list of all default IndexedDB stores used throughout the SDK. This is
1896
- * used when creating transactions so that access across all stores is done
1897
- * atomically.
1898
- */
1899
- const ALL_STORES = V11_STORES;
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
+ }
1900
2004
 
1901
2005
  /**
1902
2006
  * @license
@@ -2137,7 +2241,7 @@ class PersistencePromise {
2137
2241
  */
2138
2242
  // References to `window` are guarded by SimpleDb.isAvailable()
2139
2243
  /* eslint-disable no-restricted-globals */
2140
- const LOG_TAG$g = 'SimpleDb';
2244
+ const LOG_TAG$h = 'SimpleDb';
2141
2245
  /**
2142
2246
  * The maximum number of retry attempts for an IndexedDb transaction that fails
2143
2247
  * with a DOMException.
@@ -2188,7 +2292,7 @@ class SimpleDbTransaction {
2188
2292
  this.completionDeferred.reject(error);
2189
2293
  }
2190
2294
  if (!this.aborted) {
2191
- 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');
2192
2296
  this.aborted = true;
2193
2297
  this.transaction.abort();
2194
2298
  }
@@ -2251,7 +2355,7 @@ class SimpleDb {
2251
2355
  }
2252
2356
  /** Deletes the specified database. */
2253
2357
  static delete(name) {
2254
- logDebug(LOG_TAG$g, 'Removing database:', name);
2358
+ logDebug(LOG_TAG$h, 'Removing database:', name);
2255
2359
  return wrapRequest(window.indexedDB.deleteDatabase(name)).toPromise();
2256
2360
  }
2257
2361
  /** Returns true if IndexedDB is available in the current environment. */
@@ -2330,7 +2434,7 @@ class SimpleDb {
2330
2434
  */
2331
2435
  async ensureDb(action) {
2332
2436
  if (!this.db) {
2333
- logDebug(LOG_TAG$g, 'Opening database:', this.name);
2437
+ logDebug(LOG_TAG$h, 'Opening database:', this.name);
2334
2438
  this.db = await new Promise((resolve, reject) => {
2335
2439
  // TODO(mikelehen): Investigate browser compatibility.
2336
2440
  // https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB
@@ -2366,12 +2470,12 @@ class SimpleDb {
2366
2470
  }
2367
2471
  };
2368
2472
  request.onupgradeneeded = (event) => {
2369
- 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);
2370
2474
  const db = event.target.result;
2371
2475
  this.schemaConverter
2372
2476
  .createOrUpgrade(db, request.transaction, event.oldVersion, this.version)
2373
2477
  .next(() => {
2374
- logDebug(LOG_TAG$g, 'Database upgrade to version ' + this.version + ' complete');
2478
+ logDebug(LOG_TAG$h, 'Database upgrade to version ' + this.version + ' complete');
2375
2479
  });
2376
2480
  };
2377
2481
  });
@@ -2429,7 +2533,7 @@ class SimpleDb {
2429
2533
  // exception is wrapped in a generic error by our async/await handling.
2430
2534
  const retryable = error.name !== 'FirebaseError' &&
2431
2535
  attemptNumber < TRANSACTION_RETRY_COUNT;
2432
- 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);
2433
2537
  this.close();
2434
2538
  if (!retryable) {
2435
2539
  return Promise.reject(error);
@@ -2516,11 +2620,11 @@ class SimpleDbStore {
2516
2620
  put(keyOrValue, value) {
2517
2621
  let request;
2518
2622
  if (value !== undefined) {
2519
- logDebug(LOG_TAG$g, 'PUT', this.store.name, keyOrValue, value);
2623
+ logDebug(LOG_TAG$h, 'PUT', this.store.name, keyOrValue, value);
2520
2624
  request = this.store.put(value, keyOrValue);
2521
2625
  }
2522
2626
  else {
2523
- logDebug(LOG_TAG$g, 'PUT', this.store.name, '<auto-key>', keyOrValue);
2627
+ logDebug(LOG_TAG$h, 'PUT', this.store.name, '<auto-key>', keyOrValue);
2524
2628
  request = this.store.put(keyOrValue);
2525
2629
  }
2526
2630
  return wrapRequest(request);
@@ -2533,7 +2637,7 @@ class SimpleDbStore {
2533
2637
  * @returns The key of the value to add.
2534
2638
  */
2535
2639
  add(value) {
2536
- logDebug(LOG_TAG$g, 'ADD', this.store.name, value, value);
2640
+ logDebug(LOG_TAG$h, 'ADD', this.store.name, value, value);
2537
2641
  const request = this.store.add(value);
2538
2642
  return wrapRequest(request);
2539
2643
  }
@@ -2553,12 +2657,12 @@ class SimpleDbStore {
2553
2657
  if (result === undefined) {
2554
2658
  result = null;
2555
2659
  }
2556
- logDebug(LOG_TAG$g, 'GET', this.store.name, key, result);
2660
+ logDebug(LOG_TAG$h, 'GET', this.store.name, key, result);
2557
2661
  return result;
2558
2662
  });
2559
2663
  }
2560
2664
  delete(key) {
2561
- logDebug(LOG_TAG$g, 'DELETE', this.store.name, key);
2665
+ logDebug(LOG_TAG$h, 'DELETE', this.store.name, key);
2562
2666
  const request = this.store.delete(key);
2563
2667
  return wrapRequest(request);
2564
2668
  }
@@ -2569,21 +2673,37 @@ class SimpleDbStore {
2569
2673
  * Returns the number of rows in the store.
2570
2674
  */
2571
2675
  count() {
2572
- logDebug(LOG_TAG$g, 'COUNT', this.store.name);
2676
+ logDebug(LOG_TAG$h, 'COUNT', this.store.name);
2573
2677
  const request = this.store.count();
2574
2678
  return wrapRequest(request);
2575
2679
  }
2576
2680
  loadAll(indexOrRange, range) {
2577
- const cursor = this.cursor(this.options(indexOrRange, range));
2578
- const results = [];
2579
- return this.iterateCursor(cursor, (key, value) => {
2580
- results.push(value);
2581
- }).next(() => {
2582
- return results;
2583
- });
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
+ }
2584
2704
  }
2585
2705
  deleteAll(indexOrRange, range) {
2586
- logDebug(LOG_TAG$g, 'DELETE ALL', this.store.name);
2706
+ logDebug(LOG_TAG$h, 'DELETE ALL', this.store.name);
2587
2707
  const options = this.options(indexOrRange, range);
2588
2708
  options.keysOnly = false;
2589
2709
  const cursor = this.cursor(options);
@@ -3606,11 +3726,22 @@ class DocumentKey {
3606
3726
  static empty() {
3607
3727
  return new DocumentKey(ResourcePath.emptyPath());
3608
3728
  }
3729
+ get collectionGroup() {
3730
+ return this.path.popLast().lastSegment();
3731
+ }
3609
3732
  /** Returns true if the document is in the specified collectionId. */
3610
3733
  hasCollectionId(collectionId) {
3611
3734
  return (this.path.length >= 2 &&
3612
3735
  this.path.get(this.path.length - 2) === collectionId);
3613
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
+ }
3614
3745
  isEqual(other) {
3615
3746
  return (other !== null && ResourcePath.comparator(this.path, other.path) === 0);
3616
3747
  }
@@ -3650,6 +3781,13 @@ class DocumentKey {
3650
3781
  * See the License for the specific language governing permissions and
3651
3782
  * limitations under the License.
3652
3783
  */
3784
+ const MAX_VALUE = {
3785
+ mapValue: {
3786
+ fields: {
3787
+ '__type__': { stringValue: '__max___' }
3788
+ }
3789
+ }
3790
+ };
3653
3791
  /** Extracts the backend's type order for the provided value. */
3654
3792
  function typeOrder(value) {
3655
3793
  if ('nullValue' in value) {
@@ -4059,6 +4197,10 @@ function deepClone(source) {
4059
4197
  else {
4060
4198
  return Object.assign({}, source);
4061
4199
  }
4200
+ }
4201
+ /** Returns true if the Value represents the canonical {@link #MAX_VALUE} . */
4202
+ function isMaxValue(value) {
4203
+ return valueEquals(value, MAX_VALUE);
4062
4204
  }
4063
4205
 
4064
4206
  /**
@@ -4434,17 +4576,25 @@ class FieldIndex {
4434
4576
  /** The collection ID this index applies to. */
4435
4577
  collectionGroup,
4436
4578
  /** The field segments for this index. */
4437
- segments,
4579
+ fields,
4438
4580
  /** Shows how up-to-date the index is for the current user. */
4439
4581
  indexState) {
4440
4582
  this.indexId = indexId;
4441
4583
  this.collectionGroup = collectionGroup;
4442
- this.segments = segments;
4584
+ this.fields = fields;
4443
4585
  this.indexState = indexState;
4444
4586
  }
4445
4587
  }
4446
4588
  /** An ID for an index that has not yet been added to persistence. */
4447
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
+ }
4448
4598
  /** An index component consisting of field path and index type. */
4449
4599
  class IndexSegment {
4450
4600
  constructor(
@@ -5861,6 +6011,72 @@ class SortedSetIterator {
5861
6011
  hasNext() {
5862
6012
  return this.iter.hasNext();
5863
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;
5864
6080
  }
5865
6081
 
5866
6082
  /**
@@ -6699,6 +6915,47 @@ class MutationBatchResult {
6699
6915
  }
6700
6916
  }
6701
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
+
6702
6959
  /**
6703
6960
  * @license
6704
6961
  * Copyright 2017 Google LLC
@@ -7171,7 +7428,7 @@ class TargetState {
7171
7428
  this._current = true;
7172
7429
  }
7173
7430
  }
7174
- const LOG_TAG$f = 'WatchChangeAggregator';
7431
+ const LOG_TAG$g = 'WatchChangeAggregator';
7175
7432
  /**
7176
7433
  * A helper class to accumulate watch changes into a RemoteEvent.
7177
7434
  */
@@ -7466,7 +7723,7 @@ class WatchChangeAggregator {
7466
7723
  isActiveTarget(targetId) {
7467
7724
  const targetActive = this.targetDataForActiveTarget(targetId) !== null;
7468
7725
  if (!targetActive) {
7469
- logDebug(LOG_TAG$f, 'Detected inactive target', targetId);
7726
+ logDebug(LOG_TAG$g, 'Detected inactive target', targetId);
7470
7727
  }
7471
7728
  return targetActive;
7472
7729
  }
@@ -8700,6 +8957,37 @@ function fromBundleMetadata(metadata) {
8700
8957
  version: metadata.version,
8701
8958
  createTime: fromVersion(metadata.createTime)
8702
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);
8703
8991
  }
8704
8992
 
8705
8993
  /**
@@ -8761,7 +9049,7 @@ function namedQueriesStore(txn) {
8761
9049
 
8762
9050
  /**
8763
9051
  * @license
8764
- * Copyright 2019 Google LLC
9052
+ * Copyright 2022 Google LLC
8765
9053
  *
8766
9054
  * Licensed under the Apache License, Version 2.0 (the "License");
8767
9055
  * you may not use this file except in compliance with the License.
@@ -8776,87 +9064,278 @@ function namedQueriesStore(txn) {
8776
9064
  * limitations under the License.
8777
9065
  */
8778
9066
  /**
8779
- * An in-memory implementation of IndexManager.
9067
+ * Implementation of DocumentOverlayCache using IndexedDb.
8780
9068
  */
8781
- class MemoryIndexManager {
8782
- constructor() {
8783
- this.collectionParentIndex = new MemoryCollectionParentIndex();
8784
- }
8785
- addToCollectionParentIndex(transaction, collectionPath) {
8786
- this.collectionParentIndex.add(collectionPath);
8787
- return PersistencePromise.resolve();
8788
- }
8789
- getCollectionParents(transaction, collectionId) {
8790
- return PersistencePromise.resolve(this.collectionParentIndex.getEntries(collectionId));
8791
- }
8792
- addFieldIndex(transaction, index) {
8793
- // Field indices are not supported with memory persistence.
8794
- return PersistencePromise.resolve();
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;
8795
9077
  }
8796
- deleteFieldIndex(transaction, index) {
8797
- // Field indices are not supported with memory persistence.
8798
- return PersistencePromise.resolve();
9078
+ static forUser(serializer, user) {
9079
+ const userId = user.uid || '';
9080
+ return new IndexedDbDocumentOverlayCache(serializer, userId);
8799
9081
  }
8800
- getDocumentsMatchingTarget(transaction, fieldIndex, target) {
8801
- // Field indices are not supported with memory persistence.
8802
- return PersistencePromise.resolve(documentKeySet());
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
+ });
8803
9091
  }
8804
- getFieldIndex(transaction, target) {
8805
- // Field indices are not supported with memory persistence.
8806
- return PersistencePromise.resolve(null);
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);
8807
9099
  }
8808
- getFieldIndexes(transaction, collectionGroup) {
8809
- // Field indices are not supported with memory persistence.
8810
- return PersistencePromise.resolve([]);
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);
8811
9112
  }
8812
- getNextCollectionGroupToUpdate(transaction) {
8813
- // Field indices are not supported with memory persistence.
8814
- return PersistencePromise.resolve(null);
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
+ });
8815
9129
  }
8816
- updateCollectionGroup(transaction, collectionGroup, offset) {
8817
- // Field indices are not supported with memory persistence.
8818
- return PersistencePromise.resolve();
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);
8819
9157
  }
8820
- updateIndexEntries(transaction, documents) {
8821
- // Field indices are not supported with memory persistence.
8822
- return PersistencePromise.resolve();
9158
+ saveOverlay(transaction, overlay) {
9159
+ return documentOverlayStore(transaction).put(toDbDocumentOverlay(this.serializer, this.userId, overlay));
8823
9160
  }
8824
9161
  }
8825
9162
  /**
8826
- * Internal implementation of the collection-parent index exposed by MemoryIndexManager.
8827
- * Also used for in-memory caching by IndexedDbIndexManager and initial index population
8828
- * in indexeddb_schema.ts
9163
+ * Helper to get a typed SimpleDbStore for the document overlay object store.
8829
9164
  */
8830
- class MemoryCollectionParentIndex {
8831
- constructor() {
8832
- this.index = {};
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
+ }
8833
9293
  }
8834
- // Returns false if the entry already existed.
8835
- add(collectionPath) {
8836
- const collectionId = collectionPath.lastSegment();
8837
- const parentPath = collectionPath.popLast();
8838
- const existingParents = this.index[collectionId] ||
8839
- new SortedSet(ResourcePath.comparator);
8840
- const added = !existingParents.has(parentPath);
8841
- this.index[collectionId] = existingParents.add(parentPath);
8842
- return added;
9294
+ writeIndexString(stringIndexValue, encoder) {
9295
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_STRING);
9296
+ this.writeUnlabeledIndexString(stringIndexValue, encoder);
8843
9297
  }
8844
- has(collectionPath) {
8845
- const collectionId = collectionPath.lastSegment();
8846
- const parentPath = collectionPath.popLast();
8847
- const existingParents = this.index[collectionId];
8848
- return existingParents && existingParents.has(parentPath);
9298
+ writeUnlabeledIndexString(stringIndexValue, encoder) {
9299
+ encoder.writeString(stringIndexValue);
8849
9300
  }
8850
- getEntries(collectionId) {
8851
- const parentPaths = this.index[collectionId] ||
8852
- new SortedSet(ResourcePath.comparator);
8853
- return parentPaths.toArray();
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
+ }
8854
9308
  }
8855
- }
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();
8856
9335
 
8857
9336
  /**
8858
9337
  * @license
8859
- * Copyright 2019 Google LLC
9338
+ * Copyright 2021 Google LLC
8860
9339
  *
8861
9340
  * Licensed under the Apache License, Version 2.0 (the "License");
8862
9341
  * you may not use this file except in compliance with the License.
@@ -8864,25 +9343,538 @@ class MemoryCollectionParentIndex {
8864
9343
  *
8865
9344
  * http://www.apache.org/licenses/LICENSE-2.0
8866
9345
  *
8867
- * Unless required by applicable law or agreed to in writing, software
9346
+ * Unless required by applicable law | agreed to in writing, software
8868
9347
  * distributed under the License is distributed on an "AS IS" BASIS,
8869
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9348
+ * WITHOUT WARRANTIES | CONDITIONS OF ANY KIND, either express | implied.
8870
9349
  * See the License for the specific language governing permissions and
8871
9350
  * limitations under the License.
8872
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
+
9748
+ /**
9749
+ * @license
9750
+ * Copyright 2019 Google LLC
9751
+ *
9752
+ * Licensed under the Apache License, Version 2.0 (the "License");
9753
+ * you may not use this file except in compliance with the License.
9754
+ * You may obtain a copy of the License at
9755
+ *
9756
+ * http://www.apache.org/licenses/LICENSE-2.0
9757
+ *
9758
+ * Unless required by applicable law or agreed to in writing, software
9759
+ * distributed under the License is distributed on an "AS IS" BASIS,
9760
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9761
+ * See the License for the specific language governing permissions and
9762
+ * limitations under the License.
9763
+ */
9764
+ /**
9765
+ * An in-memory implementation of IndexManager.
9766
+ */
9767
+ class MemoryIndexManager {
9768
+ constructor() {
9769
+ this.collectionParentIndex = new MemoryCollectionParentIndex();
9770
+ }
9771
+ addToCollectionParentIndex(transaction, collectionPath) {
9772
+ this.collectionParentIndex.add(collectionPath);
9773
+ return PersistencePromise.resolve();
9774
+ }
9775
+ getCollectionParents(transaction, collectionId) {
9776
+ return PersistencePromise.resolve(this.collectionParentIndex.getEntries(collectionId));
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
+ }
9810
+ }
9811
+ /**
9812
+ * Internal implementation of the collection-parent index exposed by MemoryIndexManager.
9813
+ * Also used for in-memory caching by IndexedDbIndexManager and initial index population
9814
+ * in indexeddb_schema.ts
9815
+ */
9816
+ class MemoryCollectionParentIndex {
9817
+ constructor() {
9818
+ this.index = {};
9819
+ }
9820
+ // Returns false if the entry already existed.
9821
+ add(collectionPath) {
9822
+ const collectionId = collectionPath.lastSegment();
9823
+ const parentPath = collectionPath.popLast();
9824
+ const existingParents = this.index[collectionId] ||
9825
+ new SortedSet(ResourcePath.comparator);
9826
+ const added = !existingParents.has(parentPath);
9827
+ this.index[collectionId] = existingParents.add(parentPath);
9828
+ return added;
9829
+ }
9830
+ has(collectionPath) {
9831
+ const collectionId = collectionPath.lastSegment();
9832
+ const parentPath = collectionPath.popLast();
9833
+ const existingParents = this.index[collectionId];
9834
+ return existingParents && existingParents.has(parentPath);
9835
+ }
9836
+ getEntries(collectionId) {
9837
+ const parentPaths = this.index[collectionId] ||
9838
+ new SortedSet(ResourcePath.comparator);
9839
+ return parentPaths.toArray();
9840
+ }
9841
+ }
9842
+
9843
+ /**
9844
+ * @license
9845
+ * Copyright 2019 Google LLC
9846
+ *
9847
+ * Licensed under the Apache License, Version 2.0 (the "License");
9848
+ * you may not use this file except in compliance with the License.
9849
+ * You may obtain a copy of the License at
9850
+ *
9851
+ * http://www.apache.org/licenses/LICENSE-2.0
9852
+ *
9853
+ * Unless required by applicable law or agreed to in writing, software
9854
+ * distributed under the License is distributed on an "AS IS" BASIS,
9855
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9856
+ * See the License for the specific language governing permissions and
9857
+ * limitations under the License.
9858
+ */
9859
+ const LOG_TAG$f = 'IndexedDbIndexManager';
8873
9860
  /**
8874
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.
8875
9865
  */
8876
9866
  class IndexedDbIndexManager {
8877
- constructor() {
9867
+ constructor(user) {
9868
+ this.user = user;
8878
9869
  /**
8879
9870
  * An in-memory copy of the index entries we've already written since the SDK
8880
9871
  * launched. Used to avoid re-writing the same entry repeatedly.
8881
9872
  *
8882
- * This is *NOT* a complete cache of what's in persistence and so can never be used to
8883
- * satisfy reads.
9873
+ * This is *NOT* a complete cache of what's in persistence and so can never be
9874
+ * used to satisfy reads.
8884
9875
  */
8885
9876
  this.collectionParentsCache = new MemoryCollectionParentIndex();
9877
+ this.uid = user.uid || '';
8886
9878
  }
8887
9879
  /**
8888
9880
  * Adds a new entry to the collection parent index.
@@ -8930,12 +9922,25 @@ class IndexedDbIndexManager {
8930
9922
  });
8931
9923
  }
8932
9924
  addFieldIndex(transaction, index) {
8933
- // TODO(indexing): Implement
8934
- return PersistencePromise.resolve();
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();
8935
9931
  }
8936
9932
  deleteFieldIndex(transaction, index) {
8937
- // TODO(indexing): Implement
8938
- return PersistencePromise.resolve();
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)));
8939
9944
  }
8940
9945
  getDocumentsMatchingTarget(transaction, fieldIndex, target) {
8941
9946
  // TODO(indexing): Implement
@@ -8945,21 +9950,168 @@ class IndexedDbIndexManager {
8945
9950
  // TODO(indexing): Implement
8946
9951
  return PersistencePromise.resolve(null);
8947
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
+ }
8948
9976
  getFieldIndexes(transaction, collectionGroup) {
8949
- // TODO(indexing): Implement
8950
- return PersistencePromise.resolve([]);
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
+ });
8951
9991
  }
8952
9992
  getNextCollectionGroupToUpdate(transaction) {
8953
- // TODO(indexing): Implement
8954
- return PersistencePromise.resolve(null);
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
+ });
8955
10000
  }
8956
10001
  updateCollectionGroup(transaction, collectionGroup, offset) {
8957
- // TODO(indexing): Implement
8958
- return PersistencePromise.resolve();
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)))));
8959
10007
  }
8960
10008
  updateIndexEntries(transaction, documents) {
8961
- // TODO(indexing): Implement
8962
- return PersistencePromise.resolve();
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);
8963
10115
  }
8964
10116
  }
8965
10117
  /**
@@ -8968,6 +10120,24 @@ class IndexedDbIndexManager {
8968
10120
  */
8969
10121
  function collectionParentsStore(txn) {
8970
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);
8971
10141
  }
8972
10142
 
8973
10143
  /**
@@ -10437,12 +11607,10 @@ class RemoteDocumentChangeBuffer {
10437
11607
  * `newIndexedDbRemoteDocumentCache()`.
10438
11608
  */
10439
11609
  class IndexedDbRemoteDocumentCacheImpl {
10440
- /**
10441
- * @param serializer - The document serializer.
10442
- * @param indexManager - The query indexes that need to be maintained.
10443
- */
10444
- constructor(serializer, indexManager) {
11610
+ constructor(serializer) {
10445
11611
  this.serializer = serializer;
11612
+ }
11613
+ setIndexManager(indexManager) {
10446
11614
  this.indexManager = indexManager;
10447
11615
  }
10448
11616
  /**
@@ -10564,21 +11732,21 @@ class IndexedDbRemoteDocumentCacheImpl {
10564
11732
  }
10565
11733
  });
10566
11734
  }
10567
- getDocumentsMatchingQuery(transaction, query, sinceReadTime) {
11735
+ getAll(transaction, collection, sinceReadTime) {
10568
11736
  let results = mutableDocumentMap();
10569
- const immediateChildrenPathLength = query.path.length + 1;
11737
+ const immediateChildrenPathLength = collection.length + 1;
10570
11738
  const iterationOptions = {};
10571
11739
  if (sinceReadTime.isEqual(SnapshotVersion.min())) {
10572
11740
  // Documents are ordered by key, so we can use a prefix scan to narrow
10573
11741
  // down the documents we need to match the query against.
10574
- const startKey = query.path.toArray();
11742
+ const startKey = collection.toArray();
10575
11743
  iterationOptions.range = IDBKeyRange.lowerBound(startKey);
10576
11744
  }
10577
11745
  else {
10578
11746
  // Execute an index-free query and filter by read time. This is safe
10579
11747
  // since all document changes to queries that have a
10580
11748
  // lastLimboFreeSnapshotVersion (`sinceReadTime`) have a read time set.
10581
- const collectionKey = query.path.toArray();
11749
+ const collectionKey = collection.toArray();
10582
11750
  const readTimeKey = toDbTimestampKey(sinceReadTime);
10583
11751
  iterationOptions.range = IDBKeyRange.lowerBound([collectionKey, readTimeKey],
10584
11752
  /* open= */ true);
@@ -10595,12 +11763,12 @@ class IndexedDbRemoteDocumentCacheImpl {
10595
11763
  return;
10596
11764
  }
10597
11765
  const document = this.maybeDecodeDocument(DocumentKey.fromSegments(key), dbRemoteDoc);
10598
- if (!query.path.isPrefixOf(document.key.path)) {
10599
- control.done();
10600
- }
10601
- else if (queryMatches(query, document)) {
11766
+ if (collection.isPrefixOf(document.key.path)) {
10602
11767
  results = results.insert(document.key, document);
10603
11768
  }
11769
+ else {
11770
+ control.done();
11771
+ }
10604
11772
  })
10605
11773
  .next(() => results);
10606
11774
  }
@@ -10638,14 +11806,9 @@ class IndexedDbRemoteDocumentCacheImpl {
10638
11806
  return MutableDocument.newInvalidDocument(documentKey);
10639
11807
  }
10640
11808
  }
10641
- /**
10642
- * Creates a new IndexedDbRemoteDocumentCache.
10643
- *
10644
- * @param serializer - The document serializer.
10645
- * @param indexManager - The query indexes that need to be maintained.
10646
- */
10647
- function newIndexedDbRemoteDocumentCache(serializer, indexManager) {
10648
- return new IndexedDbRemoteDocumentCacheImpl(serializer, indexManager);
11809
+ /** Creates a new IndexedDbRemoteDocumentCache. */
11810
+ function newIndexedDbRemoteDocumentCache(serializer) {
11811
+ return new IndexedDbRemoteDocumentCacheImpl(serializer);
10649
11812
  }
10650
11813
  /**
10651
11814
  * Returns the set of documents that have changed since the specified read
@@ -10879,6 +12042,11 @@ class SchemaConverter {
10879
12042
  });
10880
12043
  }
10881
12044
  if (fromVersion < 12 && toVersion >= 12) {
12045
+ p = p.next(() => {
12046
+ createDocumentOverlayStore(db);
12047
+ });
12048
+ }
12049
+ if (fromVersion < 13 && toVersion >= 13) {
10882
12050
  p = p.next(() => {
10883
12051
  createFieldIndex(db);
10884
12052
  });
@@ -11091,15 +12259,26 @@ function createNamedQueriesStore(db) {
11091
12259
  });
11092
12260
  }
11093
12261
  function createFieldIndex(db) {
11094
- db.createObjectStore(DbIndexConfiguration.store, {
11095
- keyPath: DbIndexConfiguration.keyPath
12262
+ const indexConfiguratioStore = db.createObjectStore(DbIndexConfiguration.store, {
12263
+ keyPath: DbIndexConfiguration.keyPath,
12264
+ autoIncrement: true
11096
12265
  });
11097
- db.createObjectStore(DbIndexState.store, {
12266
+ indexConfiguratioStore.createIndex(DbIndexConfiguration.collectionGroupIndex, DbIndexConfiguration.collectionGroupIndexPath, { unique: false });
12267
+ const indexStateStore = db.createObjectStore(DbIndexState.store, {
11098
12268
  keyPath: DbIndexState.keyPath
11099
12269
  });
11100
- db.createObjectStore(DbIndexEntries.store, {
11101
- keyPath: DbIndexEntries.keyPath
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
11102
12279
  });
12280
+ documentOverlayStore.createIndex(DbDocumentOverlay.collectionPathOverlayIndex, DbDocumentOverlay.collectionPathOverlayIndexPath, { unique: false });
12281
+ documentOverlayStore.createIndex(DbDocumentOverlay.collectionGroupOverlayIndex, DbDocumentOverlay.collectionGroupOverlayIndexPath, { unique: false });
11103
12282
  }
11104
12283
 
11105
12284
  /**
@@ -11211,7 +12390,7 @@ class IndexedDbPersistence {
11211
12390
  * If set to true, forcefully obtains database access. Existing tabs will
11212
12391
  * no longer be able to access IndexedDB.
11213
12392
  */
11214
- forceOwningTab) {
12393
+ forceOwningTab, schemaVersion = SCHEMA_VERSION) {
11215
12394
  this.allowTabSynchronization = allowTabSynchronization;
11216
12395
  this.persistenceKey = persistenceKey;
11217
12396
  this.clientId = clientId;
@@ -11220,6 +12399,7 @@ class IndexedDbPersistence {
11220
12399
  this.document = document;
11221
12400
  this.sequenceNumberSyncer = sequenceNumberSyncer;
11222
12401
  this.forceOwningTab = forceOwningTab;
12402
+ this.schemaVersion = schemaVersion;
11223
12403
  this.listenSequence = null;
11224
12404
  this._started = false;
11225
12405
  this.isPrimary = false;
@@ -11241,10 +12421,9 @@ class IndexedDbPersistence {
11241
12421
  this.referenceDelegate = new IndexedDbLruDelegateImpl(this, lruParams);
11242
12422
  this.dbName = persistenceKey + MAIN_DATABASE;
11243
12423
  this.serializer = new LocalSerializer(serializer);
11244
- 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));
11245
12425
  this.targetCache = new IndexedDbTargetCache(this.referenceDelegate, this.serializer);
11246
- this.indexManager = new IndexedDbIndexManager();
11247
- this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer, this.indexManager);
12426
+ this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer);
11248
12427
  this.bundleCache = new IndexedDbBundleCache();
11249
12428
  if (this.window && this.window.localStorage) {
11250
12429
  this.webStorage = this.window.localStorage;
@@ -11580,8 +12759,8 @@ class IndexedDbPersistence {
11580
12759
  get started() {
11581
12760
  return this._started;
11582
12761
  }
11583
- getMutationQueue(user) {
11584
- return IndexedDbMutationQueue.forUser(user, this.serializer, this.indexManager, this.referenceDelegate);
12762
+ getMutationQueue(user, indexManager) {
12763
+ return IndexedDbMutationQueue.forUser(user, this.serializer, indexManager, this.referenceDelegate);
11585
12764
  }
11586
12765
  getTargetCache() {
11587
12766
  return this.targetCache;
@@ -11589,8 +12768,11 @@ class IndexedDbPersistence {
11589
12768
  getRemoteDocumentCache() {
11590
12769
  return this.remoteDocumentCache;
11591
12770
  }
11592
- getIndexManager() {
11593
- return this.indexManager;
12771
+ getIndexManager(user) {
12772
+ return new IndexedDbIndexManager(user);
12773
+ }
12774
+ getDocumentOverlayCache(user) {
12775
+ return IndexedDbDocumentOverlayCache.forUser(this.serializer, user);
11594
12776
  }
11595
12777
  getBundleCache() {
11596
12778
  return this.bundleCache;
@@ -11598,11 +12780,12 @@ class IndexedDbPersistence {
11598
12780
  runTransaction(action, mode, transactionOperation) {
11599
12781
  logDebug(LOG_TAG$d, 'Starting transaction:', action);
11600
12782
  const simpleDbMode = mode === 'readonly' ? 'readonly' : 'readwrite';
12783
+ const objectStores = getObjectStores(this.schemaVersion);
11601
12784
  let persistenceTransaction;
11602
12785
  // Do all transactions as readwrite against all object stores, since we
11603
12786
  // are the only reader/writer.
11604
12787
  return this.simpleDb
11605
- .runTransaction(action, simpleDbMode, ALL_STORES, simpleDbTxn => {
12788
+ .runTransaction(action, simpleDbMode, objectStores, simpleDbTxn => {
11606
12789
  persistenceTransaction = new IndexedDbTransaction(simpleDbTxn, this.listenSequence
11607
12790
  ? this.listenSequence.next()
11608
12791
  : ListenSequence.INVALID);
@@ -11974,39 +13157,28 @@ class LocalDocumentsView {
11974
13157
  getDocumentsMatchingCollectionQuery(transaction, query, sinceReadTime) {
11975
13158
  // Query the remote documents and overlay mutations.
11976
13159
  let results;
11977
- let mutationBatches;
11978
13160
  return this.remoteDocumentCache
11979
- .getDocumentsMatchingQuery(transaction, query, sinceReadTime)
13161
+ .getAll(transaction, query.path, sinceReadTime)
11980
13162
  .next(queryResults => {
11981
13163
  results = queryResults;
11982
13164
  return this.mutationQueue.getAllMutationBatchesAffectingQuery(transaction, query);
11983
13165
  })
11984
- .next(matchingMutationBatches => {
11985
- mutationBatches = matchingMutationBatches;
11986
- // It is possible that a PatchMutation can make a document match a query, even if
11987
- // the version in the RemoteDocumentCache is not a match yet (waiting for server
11988
- // to ack). To handle this, we find all document keys affected by the PatchMutations
11989
- // that are not in `result` yet, and back fill them via `remoteDocumentCache.getEntries`,
11990
- // otherwise those `PatchMutations` will be ignored because no base document can be found,
11991
- // and lead to missing result for the query.
11992
- return this.addMissingBaseDocuments(transaction, mutationBatches, results).next(mergedDocuments => {
11993
- results = mergedDocuments;
11994
- for (const batch of mutationBatches) {
11995
- for (const mutation of batch.mutations) {
11996
- const key = mutation.key;
11997
- let document = results.get(key);
11998
- if (document == null) {
11999
- // Create invalid document to apply mutations on top of
12000
- document = MutableDocument.newInvalidDocument(key);
12001
- results = results.insert(key, document);
12002
- }
12003
- mutationApplyToLocalView(mutation, document, batch.localWriteTime);
12004
- if (!document.isFoundDocument()) {
12005
- results = results.remove(key);
12006
- }
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);
12007
13179
  }
12008
13180
  }
12009
- });
13181
+ }
12010
13182
  })
12011
13183
  .next(() => {
12012
13184
  // Finally, filter out any documents that don't actually match
@@ -12019,29 +13191,6 @@ class LocalDocumentsView {
12019
13191
  return results;
12020
13192
  });
12021
13193
  }
12022
- addMissingBaseDocuments(transaction, matchingMutationBatches, existingDocuments) {
12023
- let missingBaseDocEntriesForPatching = documentKeySet();
12024
- for (const batch of matchingMutationBatches) {
12025
- for (const mutation of batch.mutations) {
12026
- if (mutation instanceof PatchMutation &&
12027
- existingDocuments.get(mutation.key) === null) {
12028
- missingBaseDocEntriesForPatching =
12029
- missingBaseDocEntriesForPatching.add(mutation.key);
12030
- }
12031
- }
12032
- }
12033
- let mergedDocuments = existingDocuments;
12034
- return this.remoteDocumentCache
12035
- .getEntries(transaction, missingBaseDocEntriesForPatching)
12036
- .next(missingBaseDocs => {
12037
- missingBaseDocs.forEach((key, doc) => {
12038
- if (doc.isFoundDocument()) {
12039
- mergedDocuments = mergedDocuments.insert(key, doc);
12040
- }
12041
- });
12042
- return mergedDocuments;
12043
- });
12044
- }
12045
13194
  }
12046
13195
 
12047
13196
  /**
@@ -12100,11 +13249,18 @@ class LocalStoreImpl {
12100
13249
  * PORTING NOTE: This is only used for multi-tab synchronization.
12101
13250
  */
12102
13251
  this.lastDocumentChangeReadTime = SnapshotVersion.min();
12103
- this.mutationQueue = persistence.getMutationQueue(initialUser);
12104
13252
  this.remoteDocuments = persistence.getRemoteDocumentCache();
12105
13253
  this.targetCache = persistence.getTargetCache();
12106
- this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.persistence.getIndexManager());
12107
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);
12108
13264
  this.queryEngine.setLocalDocumentsView(this.localDocuments);
12109
13265
  }
12110
13266
  collectGarbage(garbageCollector) {
@@ -12126,8 +13282,6 @@ persistence, queryEngine, initialUser, serializer) {
12126
13282
  // change.
12127
13283
  async function localStoreHandleUserChange(localStore, user) {
12128
13284
  const localStoreImpl = debugCast(localStore);
12129
- let newMutationQueue = localStoreImpl.mutationQueue;
12130
- let newLocalDocuments = localStoreImpl.localDocuments;
12131
13285
  const result = await localStoreImpl.persistence.runTransaction('Handle user change', 'readonly', txn => {
12132
13286
  // Swap out the mutation queue, grabbing the pending mutation batches
12133
13287
  // before and after.
@@ -12136,11 +13290,8 @@ async function localStoreHandleUserChange(localStore, user) {
12136
13290
  .getAllMutationBatches(txn)
12137
13291
  .next(promisedOldBatches => {
12138
13292
  oldBatches = promisedOldBatches;
12139
- newMutationQueue = localStoreImpl.persistence.getMutationQueue(user);
12140
- // Recreate our LocalDocumentsView using the new
12141
- // MutationQueue.
12142
- newLocalDocuments = new LocalDocumentsView(localStoreImpl.remoteDocuments, newMutationQueue, localStoreImpl.persistence.getIndexManager());
12143
- return newMutationQueue.getAllMutationBatches(txn);
13293
+ localStoreImpl.initializeUserComponents(user);
13294
+ return localStoreImpl.mutationQueue.getAllMutationBatches(txn);
12144
13295
  })
12145
13296
  .next(newBatches => {
12146
13297
  const removedBatchIds = [];
@@ -12161,7 +13312,7 @@ async function localStoreHandleUserChange(localStore, user) {
12161
13312
  }
12162
13313
  // Return the set of all (potentially) changed documents and the list
12163
13314
  // of mutation batch IDs that were affected by change.
12164
- return newLocalDocuments
13315
+ return localStoreImpl.localDocuments
12165
13316
  .getDocuments(txn, changedKeys)
12166
13317
  .next(affectedDocuments => {
12167
13318
  return {
@@ -12172,9 +13323,6 @@ async function localStoreHandleUserChange(localStore, user) {
12172
13323
  });
12173
13324
  });
12174
13325
  });
12175
- localStoreImpl.mutationQueue = newMutationQueue;
12176
- localStoreImpl.localDocuments = newLocalDocuments;
12177
- localStoreImpl.queryEngine.setLocalDocumentsView(localStoreImpl.localDocuments);
12178
13326
  return result;
12179
13327
  }
12180
13328
  /* Accepts locally generated Mutations and commit them to storage. */
@@ -12887,6 +14035,122 @@ class MemoryBundleCache {
12887
14035
  }
12888
14036
  }
12889
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
+
12890
14154
  /**
12891
14155
  * @license
12892
14156
  * Copyright 2017 Google LLC
@@ -13240,14 +14504,16 @@ class MemoryRemoteDocumentCacheImpl {
13240
14504
  * expected to just return 0 to avoid unnecessarily doing the work of
13241
14505
  * calculating the size.
13242
14506
  */
13243
- constructor(indexManager, sizer) {
13244
- this.indexManager = indexManager;
14507
+ constructor(sizer) {
13245
14508
  this.sizer = sizer;
13246
14509
  /** Underlying cache of documents and their read times. */
13247
14510
  this.docs = documentEntryMap();
13248
14511
  /** Size of all cached documents. */
13249
14512
  this.size = 0;
13250
14513
  }
14514
+ setIndexManager(indexManager) {
14515
+ this.indexManager = indexManager;
14516
+ }
13251
14517
  /**
13252
14518
  * Adds the supplied entry to the cache and updates the cache size as appropriate.
13253
14519
  *
@@ -13295,21 +14561,22 @@ class MemoryRemoteDocumentCacheImpl {
13295
14561
  });
13296
14562
  return PersistencePromise.resolve(results);
13297
14563
  }
13298
- getDocumentsMatchingQuery(transaction, query, sinceReadTime) {
14564
+ getAll(transaction, collectionPath, sinceReadTime) {
13299
14565
  let results = mutableDocumentMap();
13300
14566
  // Documents are ordered by key, so we can use a prefix scan to narrow down
13301
14567
  // the documents we need to match the query against.
13302
- const prefix = new DocumentKey(query.path.child(''));
14568
+ const prefix = new DocumentKey(collectionPath.child(''));
13303
14569
  const iterator = this.docs.getIteratorFrom(prefix);
13304
14570
  while (iterator.hasNext()) {
13305
14571
  const { key, value: { document } } = iterator.getNext();
13306
- if (!query.path.isPrefixOf(key.path)) {
14572
+ if (!collectionPath.isPrefixOf(key.path)) {
13307
14573
  break;
13308
14574
  }
13309
- if (document.readTime.compareTo(sinceReadTime) <= 0) {
14575
+ if (key.path.length > collectionPath.length + 1) {
14576
+ // Exclude entries from subcollections.
13310
14577
  continue;
13311
14578
  }
13312
- if (!queryMatches(query, document)) {
14579
+ if (document.readTime.compareTo(sinceReadTime) <= 0) {
13313
14580
  continue;
13314
14581
  }
13315
14582
  results = results.insert(document.key, document.mutableCopy());
@@ -13331,13 +14598,12 @@ class MemoryRemoteDocumentCacheImpl {
13331
14598
  /**
13332
14599
  * Creates a new memory-only RemoteDocumentCache.
13333
14600
  *
13334
- * @param indexManager - A class that manages collection group indices.
13335
14601
  * @param sizer - Used to assess the size of a document. For eager GC, this is
13336
14602
  * expected to just return 0 to avoid unnecessarily doing the work of
13337
14603
  * calculating the size.
13338
14604
  */
13339
- function newMemoryRemoteDocumentCache(indexManager, sizer) {
13340
- return new MemoryRemoteDocumentCacheImpl(indexManager, sizer);
14605
+ function newMemoryRemoteDocumentCache(sizer) {
14606
+ return new MemoryRemoteDocumentCacheImpl(sizer);
13341
14607
  }
13342
14608
  /**
13343
14609
  * Handles the details of adding and updating documents in the MemoryRemoteDocumentCache.
@@ -13531,6 +14797,7 @@ class MemoryPersistence {
13531
14797
  */
13532
14798
  constructor(referenceDelegateFactory, serializer) {
13533
14799
  this.mutationQueues = {};
14800
+ this.overlays = {};
13534
14801
  this.listenSequence = new ListenSequence(0);
13535
14802
  this._started = false;
13536
14803
  this._started = true;
@@ -13538,7 +14805,7 @@ class MemoryPersistence {
13538
14805
  this.targetCache = new MemoryTargetCache(this);
13539
14806
  const sizer = (doc) => this.referenceDelegate.documentSize(doc);
13540
14807
  this.indexManager = new MemoryIndexManager();
13541
- this.remoteDocumentCache = newMemoryRemoteDocumentCache(this.indexManager, sizer);
14808
+ this.remoteDocumentCache = newMemoryRemoteDocumentCache(sizer);
13542
14809
  this.serializer = new LocalSerializer(serializer);
13543
14810
  this.bundleCache = new MemoryBundleCache(this.serializer);
13544
14811
  }
@@ -13559,13 +14826,23 @@ class MemoryPersistence {
13559
14826
  setNetworkEnabled() {
13560
14827
  // No op.
13561
14828
  }
13562
- 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.
13563
14832
  return this.indexManager;
13564
14833
  }
13565
- 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) {
13566
14843
  let queue = this.mutationQueues[user.toKey()];
13567
14844
  if (!queue) {
13568
- queue = new MemoryMutationQueue(this.indexManager, this.referenceDelegate);
14845
+ queue = new MemoryMutationQueue(indexManager, this.referenceDelegate);
13569
14846
  this.mutationQueues[user.toKey()] = queue;
13570
14847
  }
13571
14848
  return queue;
@@ -14710,7 +15987,7 @@ const LOG_TAG$9 = 'Connection';
14710
15987
  const X_GOOG_API_CLIENT_VALUE = `gl-node/${process.versions.node} fire/${SDK_VERSION} grpc/${grpcVersion}`;
14711
15988
  function createMetadata(databasePath, authToken, appCheckToken, appId) {
14712
15989
  hardAssert(authToken === null || authToken.type === 'OAuth');
14713
- const metadata = new grpcJs.Metadata();
15990
+ const metadata = new grpc__namespace.Metadata();
14714
15991
  if (authToken) {
14715
15992
  authToken.headers.forEach((value, key) => metadata.set(key, value));
14716
15993
  }
@@ -14741,10 +16018,10 @@ class GrpcConnection {
14741
16018
  ensureActiveStub() {
14742
16019
  if (!this.cachedStub) {
14743
16020
  logDebug(LOG_TAG$9, 'Creating Firestore stub.');
14744
- const credentials$1 = this.databaseInfo.ssl
14745
- ? grpcJs.credentials.createSsl()
14746
- : grpcJs.credentials.createInsecure();
14747
- 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);
14748
16025
  }
14749
16026
  return this.cachedStub;
14750
16027
  }
@@ -17576,8 +18853,8 @@ const protoLoaderOptions = {
17576
18853
  * @returns The GrpcObject representing our protos.
17577
18854
  */
17578
18855
  function loadProtos() {
17579
- const packageDefinition = protoLoader.fromJSON(protos$1, protoLoaderOptions);
17580
- return grpcJs.loadPackageDefinition(packageDefinition);
18856
+ const packageDefinition = protoLoader__namespace.fromJSON(protos$1, protoLoaderOptions);
18857
+ return grpc__namespace.loadPackageDefinition(packageDefinition);
17581
18858
  }
17582
18859
 
17583
18860
  /**