@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
@@ -4,11 +4,11 @@ import { Logger, LogLevel } from '@firebase/logger';
4
4
  import { inspect, TextEncoder, TextDecoder } from 'util';
5
5
  import { FirebaseError, createMockUserToken, getModularInstance, deepEqual, getUA, isIndexedDBAvailable, isSafari } from '@firebase/util';
6
6
  import { randomBytes as randomBytes$1 } from 'crypto';
7
- import { credentials, loadPackageDefinition, Metadata } from '@grpc/grpc-js';
8
- import { fromJSON } from '@grpc/proto-loader';
7
+ import * as grpc from '@grpc/grpc-js';
8
+ import * as protoLoader from '@grpc/proto-loader';
9
9
 
10
10
  const name = "@firebase/firestore";
11
- const version$1 = "3.4.4-canary.f5ac47fb1";
11
+ const version$1 = "3.4.5";
12
12
 
13
13
  /**
14
14
  * @license
@@ -61,7 +61,7 @@ User.GOOGLE_CREDENTIALS = new User('google-credentials-uid');
61
61
  User.FIRST_PARTY = new User('first-party-uid');
62
62
  User.MOCK_USER = new User('mock-user');
63
63
 
64
- const version = "9.6.6-canary.f5ac47fb1";
64
+ const version = "9.6.7";
65
65
 
66
66
  /**
67
67
  * @license
@@ -666,13 +666,19 @@ class FirebaseAppCheckTokenProvider {
666
666
  this.appCheckProvider = appCheckProvider;
667
667
  this.forceRefresh = false;
668
668
  this.appCheck = null;
669
+ this.latestAppCheckToken = null;
669
670
  }
670
671
  start(asyncQueue, changeListener) {
671
672
  const onTokenChanged = tokenResult => {
672
673
  if (tokenResult.error != null) {
673
674
  logDebug('FirebaseAppCheckTokenProvider', `Error getting App Check token; using placeholder token instead. Error: ${tokenResult.error.message}`);
674
675
  }
675
- return changeListener(tokenResult.token);
676
+ const tokenUpdated = tokenResult.token !== this.latestAppCheckToken;
677
+ this.latestAppCheckToken = tokenResult.token;
678
+ logDebug('FirebaseAppCheckTokenProvider', `Received ${tokenUpdated ? 'new' : 'existing'} token.`);
679
+ return tokenUpdated
680
+ ? changeListener(tokenResult.token)
681
+ : Promise.resolve();
676
682
  };
677
683
  this.tokenListener = (tokenResult) => {
678
684
  asyncQueue.enqueueRetryable(() => onTokenChanged(tokenResult));
@@ -707,6 +713,7 @@ class FirebaseAppCheckTokenProvider {
707
713
  return this.appCheck.getToken(forceRefresh).then(tokenResult => {
708
714
  if (tokenResult) {
709
715
  hardAssert(typeof tokenResult.token === 'string');
716
+ this.latestAppCheckToken = tokenResult.token;
710
717
  return new AppCheckToken(tokenResult.token);
711
718
  }
712
719
  else {
@@ -1210,9 +1217,10 @@ function decodeResourcePath(path) {
1210
1217
  * an auto-incrementing ID. This is required for Index-Free queries.
1211
1218
  * 10. Rewrite the canonical IDs to the explicit Protobuf-based format.
1212
1219
  * 11. Add bundles and named_queries for bundle support.
1213
- * 12. Add indexing support.
1220
+ * 12. Add document overlays.
1221
+ * 13. Add indexing support.
1214
1222
  */
1215
- const SCHEMA_VERSION = 11;
1223
+ const SCHEMA_VERSION = 12;
1216
1224
  /**
1217
1225
  * Wrapper class to store timestamps (seconds and nanos) in IndexedDb objects.
1218
1226
  */
@@ -1774,7 +1782,10 @@ DbNamedQuery.keyPath = 'name';
1774
1782
  /** An object representing the global configuration for a field index. */
1775
1783
  class DbIndexConfiguration {
1776
1784
  constructor(
1777
- /** The index id for this entry. */
1785
+ /**
1786
+ * The index id for this entry. Undefined for indexes that are not yet
1787
+ * persisted.
1788
+ */
1778
1789
  indexId,
1779
1790
  /** The collection group this index belongs to. */
1780
1791
  collectionGroup,
@@ -1788,6 +1799,16 @@ class DbIndexConfiguration {
1788
1799
  /** Name of the IndexedDb object store. */
1789
1800
  DbIndexConfiguration.store = 'indexConfiguration';
1790
1801
  DbIndexConfiguration.keyPath = 'indexId';
1802
+ /**
1803
+ * An index that provides access to the index configurations by collection
1804
+ * group.
1805
+ *
1806
+ * PORTING NOTE: iOS and Android maintain this index in-memory, but this is
1807
+ * not possible here as the Web client supports concurrent access to
1808
+ * persistence via multi-tab.
1809
+ */
1810
+ DbIndexConfiguration.collectionGroupIndex = 'collectionGroupIndex';
1811
+ DbIndexConfiguration.collectionGroupIndexPath = 'collectionGroup';
1791
1812
  /**
1792
1813
  * An object describing how up-to-date the index backfill is for each user and
1793
1814
  * index.
@@ -1829,8 +1850,18 @@ class DbIndexState {
1829
1850
  /** Name of the IndexedDb object store. */
1830
1851
  DbIndexState.store = 'indexState';
1831
1852
  DbIndexState.keyPath = ['indexId', 'uid'];
1853
+ /**
1854
+ * An index that provides access to documents in a collection sorted by last
1855
+ * update time. Used by the backfiller.
1856
+ *
1857
+ * PORTING NOTE: iOS and Android maintain this index in-memory, but this is
1858
+ * not possible here as the Web client supports concurrent access to
1859
+ * persistence via multi-tab.
1860
+ */
1861
+ DbIndexState.sequenceNumberIndex = 'sequenceNumberIndex';
1862
+ DbIndexState.sequenceNumberIndexPath = ['uid', 'sequenceNumber'];
1832
1863
  /** An object that stores the encoded entries for all documents and fields. */
1833
- class DbIndexEntries {
1864
+ class DbIndexEntry {
1834
1865
  constructor(
1835
1866
  /** The index id for this entry. */
1836
1867
  indexId,
@@ -1850,14 +1881,56 @@ class DbIndexEntries {
1850
1881
  }
1851
1882
  }
1852
1883
  /** Name of the IndexedDb object store. */
1853
- DbIndexEntries.store = 'indexEntries';
1854
- DbIndexEntries.keyPath = [
1884
+ DbIndexEntry.store = 'indexEntries';
1885
+ DbIndexEntry.keyPath = [
1855
1886
  'indexId',
1856
1887
  'uid',
1857
1888
  'arrayValue',
1858
1889
  'directionalValue',
1859
1890
  'documentKey'
1860
1891
  ];
1892
+ DbIndexEntry.documentKeyIndex = 'documentKeyIndex';
1893
+ DbIndexEntry.documentKeyIndexPath = ['indexId', 'uid', 'documentKey'];
1894
+ /**
1895
+ * An object representing a document overlay.
1896
+ */
1897
+ class DbDocumentOverlay {
1898
+ constructor(
1899
+ /** The user ID to whom this overlay belongs. */
1900
+ userId,
1901
+ /** The path to the collection that contains the document. */
1902
+ collectionPath,
1903
+ /** The ID (key) of the document within the collection. */
1904
+ documentId,
1905
+ /** The collection group to which the document belongs. */
1906
+ collectionGroup,
1907
+ /** The largest batch ID that's been applied for this overlay. */
1908
+ largestBatchId,
1909
+ /** The overlay mutation. */
1910
+ overlayMutation) {
1911
+ this.userId = userId;
1912
+ this.collectionPath = collectionPath;
1913
+ this.documentId = documentId;
1914
+ this.collectionGroup = collectionGroup;
1915
+ this.largestBatchId = largestBatchId;
1916
+ this.overlayMutation = overlayMutation;
1917
+ }
1918
+ }
1919
+ /** Name of the IndexedDb object store. */
1920
+ DbDocumentOverlay.store = 'documentOverlays';
1921
+ DbDocumentOverlay.keyPath = ['userId', 'collectionPath', 'documentId'];
1922
+ DbDocumentOverlay.collectionPathOverlayIndex = 'collectionPathOverlayIndex';
1923
+ DbDocumentOverlay.collectionPathOverlayIndexPath = [
1924
+ 'userId',
1925
+ 'collectionPath',
1926
+ 'largestBatchId'
1927
+ ];
1928
+ DbDocumentOverlay.collectionGroupOverlayIndex = 'collectionGroupOverlayIndex';
1929
+ DbDocumentOverlay.collectionGroupOverlayIndexPath = [
1930
+ 'userId',
1931
+ 'collectionGroup',
1932
+ 'largestBatchId'
1933
+ ];
1861
1934
  // Visible for testing
1862
1935
  const V1_STORES = [
1863
1936
  DbMutationQueue.store,
@@ -1881,18 +1954,28 @@ const V8_STORES = [...V6_STORES, DbCollectionParent.store];
1881
1954
  // V9 does not change the set of stores.
1882
1955
  // V10 does not change the set of stores.
1883
1956
  const V11_STORES = [...V8_STORES, DbBundle.store, DbNamedQuery.store];
1884
- [
1885
- ...V11_STORES,
1957
+ const V12_STORES = [...V11_STORES, DbDocumentOverlay.store];
1958
+ const V13_STORES = [
1959
+ ...V12_STORES,
1886
1960
  DbIndexConfiguration.store,
1887
1961
  DbIndexState.store,
1888
- DbIndexEntries.store
1962
+ DbIndexEntry.store
1889
1963
  ];
1890
- /**
1891
- * The list of all default IndexedDB stores used throughout the SDK. This is
1892
- * used when creating transactions so that access across all stores is done
1893
- * atomically.
1894
- */
1895
- const ALL_STORES = V11_STORES;
1964
+ /** Returns the object stores for the provided schema. */
1965
+ function getObjectStores(schemaVersion) {
1966
+ if (schemaVersion === 13) {
1967
+ return V13_STORES;
1968
+ }
1969
+ else if (schemaVersion === 12) {
1970
+ return V12_STORES;
1971
+ }
1972
+ else if (schemaVersion === 11) {
1973
+ return V11_STORES;
1974
+ }
1975
+ else {
1976
+ fail();
1977
+ }
1978
+ }
1896
1979
 
1897
1980
  /**
1898
1981
  * @license
@@ -2133,7 +2216,7 @@ class PersistencePromise {
2133
2216
  */
2134
2217
  // References to `window` are guarded by SimpleDb.isAvailable()
2135
2218
  /* eslint-disable no-restricted-globals */
2136
- const LOG_TAG$g = 'SimpleDb';
2219
+ const LOG_TAG$h = 'SimpleDb';
2137
2220
  /**
2138
2221
  * The maximum number of retry attempts for an IndexedDb transaction that fails
2139
2222
  * with a DOMException.
@@ -2184,7 +2267,7 @@ class SimpleDbTransaction {
2184
2267
  this.completionDeferred.reject(error);
2185
2268
  }
2186
2269
  if (!this.aborted) {
2187
- logDebug(LOG_TAG$g, 'Aborting transaction:', error ? error.message : 'Client-initiated abort');
2270
+ logDebug(LOG_TAG$h, 'Aborting transaction:', error ? error.message : 'Client-initiated abort');
2188
2271
  this.aborted = true;
2189
2272
  this.transaction.abort();
2190
2273
  }
@@ -2247,7 +2330,7 @@ class SimpleDb {
2247
2330
  }
2248
2331
  /** Deletes the specified database. */
2249
2332
  static delete(name) {
2250
- logDebug(LOG_TAG$g, 'Removing database:', name);
2333
+ logDebug(LOG_TAG$h, 'Removing database:', name);
2251
2334
  return wrapRequest(window.indexedDB.deleteDatabase(name)).toPromise();
2252
2335
  }
2253
2336
  /** Returns true if IndexedDB is available in the current environment. */
@@ -2326,7 +2409,7 @@ class SimpleDb {
2326
2409
  */
2327
2410
  async ensureDb(action) {
2328
2411
  if (!this.db) {
2329
- logDebug(LOG_TAG$g, 'Opening database:', this.name);
2412
+ logDebug(LOG_TAG$h, 'Opening database:', this.name);
2330
2413
  this.db = await new Promise((resolve, reject) => {
2331
2414
  // TODO(mikelehen): Investigate browser compatibility.
2332
2415
  // https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB
@@ -2362,12 +2445,12 @@ class SimpleDb {
2362
2445
  }
2363
2446
  };
2364
2447
  request.onupgradeneeded = (event) => {
2365
- logDebug(LOG_TAG$g, 'Database "' + this.name + '" requires upgrade from version:', event.oldVersion);
2448
+ logDebug(LOG_TAG$h, 'Database "' + this.name + '" requires upgrade from version:', event.oldVersion);
2366
2449
  const db = event.target.result;
2367
2450
  this.schemaConverter
2368
2451
  .createOrUpgrade(db, request.transaction, event.oldVersion, this.version)
2369
2452
  .next(() => {
2370
- logDebug(LOG_TAG$g, 'Database upgrade to version ' + this.version + ' complete');
2453
+ logDebug(LOG_TAG$h, 'Database upgrade to version ' + this.version + ' complete');
2371
2454
  });
2372
2455
  };
2373
2456
  });
@@ -2425,7 +2508,7 @@ class SimpleDb {
2425
2508
  // exception is wrapped in a generic error by our async/await handling.
2426
2509
  const retryable = error.name !== 'FirebaseError' &&
2427
2510
  attemptNumber < TRANSACTION_RETRY_COUNT;
2428
- logDebug(LOG_TAG$g, 'Transaction failed with error:', error.message, 'Retrying:', retryable);
2511
+ logDebug(LOG_TAG$h, 'Transaction failed with error:', error.message, 'Retrying:', retryable);
2429
2512
  this.close();
2430
2513
  if (!retryable) {
2431
2514
  return Promise.reject(error);
@@ -2512,11 +2595,11 @@ class SimpleDbStore {
2512
2595
  put(keyOrValue, value) {
2513
2596
  let request;
2514
2597
  if (value !== undefined) {
2515
- logDebug(LOG_TAG$g, 'PUT', this.store.name, keyOrValue, value);
2598
+ logDebug(LOG_TAG$h, 'PUT', this.store.name, keyOrValue, value);
2516
2599
  request = this.store.put(value, keyOrValue);
2517
2600
  }
2518
2601
  else {
2519
- logDebug(LOG_TAG$g, 'PUT', this.store.name, '<auto-key>', keyOrValue);
2602
+ logDebug(LOG_TAG$h, 'PUT', this.store.name, '<auto-key>', keyOrValue);
2520
2603
  request = this.store.put(keyOrValue);
2521
2604
  }
2522
2605
  return wrapRequest(request);
@@ -2529,7 +2612,7 @@ class SimpleDbStore {
2529
2612
  * @returns The key of the value to add.
2530
2613
  */
2531
2614
  add(value) {
2532
- logDebug(LOG_TAG$g, 'ADD', this.store.name, value, value);
2615
+ logDebug(LOG_TAG$h, 'ADD', this.store.name, value, value);
2533
2616
  const request = this.store.add(value);
2534
2617
  return wrapRequest(request);
2535
2618
  }
@@ -2549,12 +2632,12 @@ class SimpleDbStore {
2549
2632
  if (result === undefined) {
2550
2633
  result = null;
2551
2634
  }
2552
- logDebug(LOG_TAG$g, 'GET', this.store.name, key, result);
2635
+ logDebug(LOG_TAG$h, 'GET', this.store.name, key, result);
2553
2636
  return result;
2554
2637
  });
2555
2638
  }
2556
2639
  delete(key) {
2557
- logDebug(LOG_TAG$g, 'DELETE', this.store.name, key);
2640
+ logDebug(LOG_TAG$h, 'DELETE', this.store.name, key);
2558
2641
  const request = this.store.delete(key);
2559
2642
  return wrapRequest(request);
2560
2643
  }
@@ -2565,21 +2648,37 @@ class SimpleDbStore {
2565
2648
  * Returns the number of rows in the store.
2566
2649
  */
2567
2650
  count() {
2568
- logDebug(LOG_TAG$g, 'COUNT', this.store.name);
2651
+ logDebug(LOG_TAG$h, 'COUNT', this.store.name);
2569
2652
  const request = this.store.count();
2570
2653
  return wrapRequest(request);
2571
2654
  }
2572
2655
  loadAll(indexOrRange, range) {
2573
- const cursor = this.cursor(this.options(indexOrRange, range));
2574
- const results = [];
2575
- return this.iterateCursor(cursor, (key, value) => {
2576
- results.push(value);
2577
- }).next(() => {
2578
- return results;
2579
- });
2656
+ const iterateOptions = this.options(indexOrRange, range);
2657
+ // Use `getAll()` if the browser supports IndexedDB v3, as it is roughly
2658
+ // 20% faster. Unfortunately, getAll() does not support custom indices.
2659
+ if (!iterateOptions.index && typeof this.store.getAll === 'function') {
2660
+ const request = this.store.getAll(iterateOptions.range);
2661
+ return new PersistencePromise((resolve, reject) => {
2662
+ request.onerror = (event) => {
2663
+ reject(event.target.error);
2664
+ };
2665
+ request.onsuccess = (event) => {
2666
+ resolve(event.target.result);
2667
+ };
2668
+ });
2669
+ }
2670
+ else {
2671
+ const cursor = this.cursor(iterateOptions);
2672
+ const results = [];
2673
+ return this.iterateCursor(cursor, (key, value) => {
2674
+ results.push(value);
2675
+ }).next(() => {
2676
+ return results;
2677
+ });
2678
+ }
2580
2679
  }
2581
2680
  deleteAll(indexOrRange, range) {
2582
- logDebug(LOG_TAG$g, 'DELETE ALL', this.store.name);
2681
+ logDebug(LOG_TAG$h, 'DELETE ALL', this.store.name);
2583
2682
  const options = this.options(indexOrRange, range);
2584
2683
  options.keysOnly = false;
2585
2684
  const cursor = this.cursor(options);
@@ -3602,11 +3701,22 @@ class DocumentKey {
3602
3701
  static empty() {
3603
3702
  return new DocumentKey(ResourcePath.emptyPath());
3604
3703
  }
3704
+ get collectionGroup() {
3705
+ return this.path.popLast().lastSegment();
3706
+ }
3605
3707
  /** Returns true if the document is in the specified collectionId. */
3606
3708
  hasCollectionId(collectionId) {
3607
3709
  return (this.path.length >= 2 &&
3608
3710
  this.path.get(this.path.length - 2) === collectionId);
3609
3711
  }
3712
+ /** Returns the collection group (i.e. the name of the parent collection) for this key. */
3713
+ getCollectionGroup() {
3714
+ return this.path.get(this.path.length - 2);
3715
+ }
3716
+ /** Returns the fully qualified path to the parent collection. */
3717
+ getCollectionPath() {
3718
+ return this.path.popLast();
3719
+ }
3610
3720
  isEqual(other) {
3611
3721
  return (other !== null && ResourcePath.comparator(this.path, other.path) === 0);
3612
3722
  }
@@ -3646,6 +3756,13 @@ class DocumentKey {
3646
3756
  * See the License for the specific language governing permissions and
3647
3757
  * limitations under the License.
3648
3758
  */
3759
+ const MAX_VALUE = {
3760
+ mapValue: {
3761
+ fields: {
3762
+ '__type__': { stringValue: '__max___' }
3763
+ }
3764
+ }
3765
+ };
3649
3766
  /** Extracts the backend's type order for the provided value. */
3650
3767
  function typeOrder(value) {
3651
3768
  if ('nullValue' in value) {
@@ -4055,6 +4172,10 @@ function deepClone(source) {
4055
4172
  else {
4056
4173
  return Object.assign({}, source);
4057
4174
  }
4175
+ }
4176
+ /** Returns true if the Value represents the canonical {@link #MAX_VALUE} . */
4177
+ function isMaxValue(value) {
4178
+ return valueEquals(value, MAX_VALUE);
4058
4179
  }
4059
4180
 
4060
4181
  /**
@@ -4430,17 +4551,25 @@ class FieldIndex {
4430
4551
  /** The collection ID this index applies to. */
4431
4552
  collectionGroup,
4432
4553
  /** The field segments for this index. */
4433
- segments,
4554
+ fields,
4434
4555
  /** Shows how up-to-date the index is for the current user. */
4435
4556
  indexState) {
4436
4557
  this.indexId = indexId;
4437
4558
  this.collectionGroup = collectionGroup;
4438
- this.segments = segments;
4559
+ this.fields = fields;
4439
4560
  this.indexState = indexState;
4440
4561
  }
4441
4562
  }
4442
4563
  /** An ID for an index that has not yet been added to persistence. */
4443
4564
  FieldIndex.UNKNOWN_ID = -1;
4565
+ /** Returns the ArrayContains/ArrayContainsAny segment for this index. */
4566
+ function fieldIndexGetArraySegment(fieldIndex) {
4567
+ return fieldIndex.fields.find(s => s.kind === 2 /* CONTAINS */);
4568
+ }
4569
+ /** Returns all directional (ascending/descending) segments for this index. */
4570
+ function fieldIndexGetDirectionalSegments(fieldIndex) {
4571
+ return fieldIndex.fields.filter(s => s.kind !== 2 /* CONTAINS */);
4572
+ }
4444
4573
  /** An index component consisting of field path and index type. */
4445
4574
  class IndexSegment {
4446
4575
  constructor(
@@ -5857,6 +5986,72 @@ class SortedSetIterator {
5857
5986
  hasNext() {
5858
5987
  return this.iter.hasNext();
5859
5988
  }
5989
+ }
5990
+ /**
5991
+ * Compares two sorted sets for equality using their natural ordering. The
5992
+ * method computes the intersection and invokes `onAdd` for every element that
5993
+ * is in `after` but not `before`. `onRemove` is invoked for every element in
5994
+ * `before` but missing from `after`.
5995
+ *
5996
+ * The method creates a copy of both `before` and `after` and runs in O(n log
5997
+ * n), where n is the size of the two lists.
5998
+ *
5999
+ * @param before - The elements that exist in the original set.
6000
+ * @param after - The elements to diff against the original set.
6001
+ * @param comparator - The comparator for the elements in before and after.
6002
+ * @param onAdd - A function to invoke for every element that is part of `
6003
+ * after` but not `before`.
6004
+ * @param onRemove - A function to invoke for every element that is part of
6005
+ * `before` but not `after`.
6006
+ */
6007
+ function diffSortedSets(before, after, comparator, onAdd, onRemove) {
6008
+ const beforeIt = before.getIterator();
6009
+ const afterIt = after.getIterator();
6010
+ let beforeValue = advanceIterator(beforeIt);
6011
+ let afterValue = advanceIterator(afterIt);
6012
+ // Walk through the two sets at the same time, using the ordering defined by
6013
+ // `comparator`.
6014
+ while (beforeValue || afterValue) {
6015
+ let added = false;
6016
+ let removed = false;
6017
+ if (beforeValue && afterValue) {
6018
+ const cmp = comparator(beforeValue, afterValue);
6019
+ if (cmp < 0) {
6020
+ // The element was removed if the next element in our ordered
6021
+ // walkthrough is only in `before`.
6022
+ removed = true;
6023
+ }
6024
+ else if (cmp > 0) {
6025
+ // The element was added if the next element in our ordered walkthrough
6026
+ // is only in `after`.
6027
+ added = true;
6028
+ }
6029
+ }
6030
+ else if (beforeValue != null) {
6031
+ removed = true;
6032
+ }
6033
+ else {
6034
+ added = true;
6035
+ }
6036
+ if (added) {
6037
+ onAdd(afterValue);
6038
+ afterValue = advanceIterator(afterIt);
6039
+ }
6040
+ else if (removed) {
6041
+ onRemove(beforeValue);
6042
+ beforeValue = advanceIterator(beforeIt);
6043
+ }
6044
+ else {
6045
+ beforeValue = advanceIterator(beforeIt);
6046
+ afterValue = advanceIterator(afterIt);
6047
+ }
6048
+ }
6049
+ }
6050
+ /**
6051
+ * Returns the next element from the iterator or `undefined` if none available.
6052
+ */
6053
+ function advanceIterator(it) {
6054
+ return it.hasNext() ? it.getNext() : undefined;
5860
6055
  }
5861
6056
 
5862
6057
  /**
@@ -6695,6 +6890,47 @@ class MutationBatchResult {
6695
6890
  }
6696
6891
  }
6697
6892
 
6893
+ /**
6894
+ * @license
6895
+ * Copyright 2022 Google LLC
6896
+ *
6897
+ * Licensed under the Apache License, Version 2.0 (the "License");
6898
+ * you may not use this file except in compliance with the License.
6899
+ * You may obtain a copy of the License at
6900
+ *
6901
+ * http://www.apache.org/licenses/LICENSE-2.0
6902
+ *
6903
+ * Unless required by applicable law or agreed to in writing, software
6904
+ * distributed under the License is distributed on an "AS IS" BASIS,
6905
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
6906
+ * See the License for the specific language governing permissions and
6907
+ * limitations under the License.
6908
+ */
6909
+ /**
6910
+ * Representation of an overlay computed by Firestore.
6911
+ *
6912
+ * Holds information about a mutation and the largest batch id in Firestore when
6913
+ * the mutation was created.
6914
+ */
6915
+ class Overlay {
6916
+ constructor(largestBatchId, mutation) {
6917
+ this.largestBatchId = largestBatchId;
6918
+ this.mutation = mutation;
6919
+ }
6920
+ getKey() {
6921
+ return this.mutation.key;
6922
+ }
6923
+ isEqual(other) {
6924
+ return other !== null && this.mutation === other.mutation;
6925
+ }
6926
+ toString() {
6927
+ return `Overlay{
6928
+ largestBatchId: ${this.largestBatchId},
6929
+ mutation: ${this.mutation.toString()}
6930
+ }`;
6931
+ }
6932
+ }
6933
+
6698
6934
  /**
6699
6935
  * @license
6700
6936
  * Copyright 2017 Google LLC
@@ -7167,7 +7403,7 @@ class TargetState {
7167
7403
  this._current = true;
7168
7404
  }
7169
7405
  }
7170
- const LOG_TAG$f = 'WatchChangeAggregator';
7406
+ const LOG_TAG$g = 'WatchChangeAggregator';
7171
7407
  /**
7172
7408
  * A helper class to accumulate watch changes into a RemoteEvent.
7173
7409
  */
@@ -7462,7 +7698,7 @@ class WatchChangeAggregator {
7462
7698
  isActiveTarget(targetId) {
7463
7699
  const targetActive = this.targetDataForActiveTarget(targetId) !== null;
7464
7700
  if (!targetActive) {
7465
- logDebug(LOG_TAG$f, 'Detected inactive target', targetId);
7701
+ logDebug(LOG_TAG$g, 'Detected inactive target', targetId);
7466
7702
  }
7467
7703
  return targetActive;
7468
7704
  }
@@ -8696,6 +8932,37 @@ function fromBundleMetadata(metadata) {
8696
8932
  version: metadata.version,
8697
8933
  createTime: fromVersion(metadata.createTime)
8698
8934
  };
8935
+ }
8936
+ /** Encodes a DbDocumentOverlay object to an Overlay model object. */
8937
+ function fromDbDocumentOverlay(localSerializer, dbDocumentOverlay) {
8938
+ return new Overlay(dbDocumentOverlay.largestBatchId, fromMutation(localSerializer.remoteSerializer, dbDocumentOverlay.overlayMutation));
8939
+ }
8940
+ /** Decodes an Overlay model object into a DbDocumentOverlay object. */
8941
+ function toDbDocumentOverlay(localSerializer, userId, overlay) {
8942
+ const [_, collectionPath, documentId] = toDbDocumentOverlayKey(userId, overlay.mutation.key);
8943
+ return new DbDocumentOverlay(userId, collectionPath, documentId, overlay.mutation.key.getCollectionGroup(), overlay.largestBatchId, toMutation(localSerializer.remoteSerializer, overlay.mutation));
8944
+ }
8945
+ /**
8946
+ * Returns the DbDocumentOverlayKey corresponding to the given user and
8947
+ * document key.
8948
+ */
8949
+ function toDbDocumentOverlayKey(userId, docKey) {
8950
+ const docId = docKey.path.lastSegment();
8951
+ const collectionPath = encodeResourcePath(docKey.path.popLast());
8952
+ return [userId, collectionPath, docId];
8953
+ }
8954
+ function toDbIndexConfiguration(index) {
8955
+ return new DbIndexConfiguration(index.indexId, index.collectionGroup, index.fields.map(s => [s.fieldPath.canonicalString(), s.kind]));
8956
+ }
8957
+ function fromDbIndexConfiguration(index, state) {
8958
+ const decodedState = state
8959
+ ? new IndexState(state.sequenceNumber, new IndexOffset(fromDbTimestamp(state.readTime), new DocumentKey(decodeResourcePath(state.documentKey)), state.largestBatchId))
8960
+ : IndexState.empty();
8961
+ const decodedSegments = index.fields.map(([fieldPath, kind]) => new IndexSegment(FieldPath$1.fromServerFormat(fieldPath), kind));
8962
+ return new FieldIndex(index.indexId, index.collectionGroup, decodedSegments, decodedState);
8963
+ }
8964
+ function toDbIndexState(indexId, user, sequenceNumber, offset) {
8965
+ return new DbIndexState(indexId, user.uid || '', sequenceNumber, toDbTimestamp(offset.readTime), encodeResourcePath(offset.documentKey.path), offset.largestBatchId);
8699
8966
  }
8700
8967
 
8701
8968
  /**
@@ -8757,7 +9024,7 @@ function namedQueriesStore(txn) {
8757
9024
 
8758
9025
  /**
8759
9026
  * @license
8760
- * Copyright 2019 Google LLC
9027
+ * Copyright 2022 Google LLC
8761
9028
  *
8762
9029
  * Licensed under the Apache License, Version 2.0 (the "License");
8763
9030
  * you may not use this file except in compliance with the License.
@@ -8772,87 +9039,278 @@ function namedQueriesStore(txn) {
8772
9039
  * limitations under the License.
8773
9040
  */
8774
9041
  /**
8775
- * An in-memory implementation of IndexManager.
9042
+ * Implementation of DocumentOverlayCache using IndexedDb.
8776
9043
  */
8777
- class MemoryIndexManager {
8778
- constructor() {
8779
- this.collectionParentIndex = new MemoryCollectionParentIndex();
8780
- }
8781
- addToCollectionParentIndex(transaction, collectionPath) {
8782
- this.collectionParentIndex.add(collectionPath);
8783
- return PersistencePromise.resolve();
8784
- }
8785
- getCollectionParents(transaction, collectionId) {
8786
- return PersistencePromise.resolve(this.collectionParentIndex.getEntries(collectionId));
8787
- }
8788
- addFieldIndex(transaction, index) {
8789
- // Field indices are not supported with memory persistence.
8790
- return PersistencePromise.resolve();
9044
+ class IndexedDbDocumentOverlayCache {
9045
+ /**
9046
+ * @param serializer - The document serializer.
9047
+ * @param userId - The userId for which we are accessing overlays.
9048
+ */
9049
+ constructor(serializer, userId) {
9050
+ this.serializer = serializer;
9051
+ this.userId = userId;
8791
9052
  }
8792
- deleteFieldIndex(transaction, index) {
8793
- // Field indices are not supported with memory persistence.
8794
- return PersistencePromise.resolve();
9053
+ static forUser(serializer, user) {
9054
+ const userId = user.uid || '';
9055
+ return new IndexedDbDocumentOverlayCache(serializer, userId);
8795
9056
  }
8796
- getDocumentsMatchingTarget(transaction, fieldIndex, target) {
8797
- // Field indices are not supported with memory persistence.
8798
- return PersistencePromise.resolve(documentKeySet());
9057
+ getOverlay(transaction, key) {
9058
+ return documentOverlayStore(transaction)
9059
+ .get(toDbDocumentOverlayKey(this.userId, key))
9060
+ .next(dbOverlay => {
9061
+ if (dbOverlay) {
9062
+ return fromDbDocumentOverlay(this.serializer, dbOverlay);
9063
+ }
9064
+ return null;
9065
+ });
8799
9066
  }
8800
- getFieldIndex(transaction, target) {
8801
- // Field indices are not supported with memory persistence.
8802
- return PersistencePromise.resolve(null);
9067
+ saveOverlays(transaction, largestBatchId, overlays) {
9068
+ const promises = [];
9069
+ overlays.forEach(mutation => {
9070
+ const overlay = new Overlay(largestBatchId, mutation);
9071
+ promises.push(this.saveOverlay(transaction, overlay));
9072
+ });
9073
+ return PersistencePromise.waitFor(promises);
8803
9074
  }
8804
- getFieldIndexes(transaction, collectionGroup) {
8805
- // Field indices are not supported with memory persistence.
8806
- return PersistencePromise.resolve([]);
9075
+ removeOverlaysForBatchId(transaction, documentKeys, batchId) {
9076
+ const collectionPaths = new Set();
9077
+ // Get the set of unique collection paths.
9078
+ documentKeys.forEach(key => collectionPaths.add(encodeResourcePath(key.getCollectionPath())));
9079
+ const promises = [];
9080
+ collectionPaths.forEach(collectionPath => {
9081
+ const range = IDBKeyRange.bound([this.userId, collectionPath, batchId], [this.userId, collectionPath, batchId + 1],
9082
+ /*lowerOpen=*/ false,
9083
+ /*upperOpen=*/ true);
9084
+ promises.push(documentOverlayStore(transaction).deleteAll(DbDocumentOverlay.collectionPathOverlayIndex, range));
9085
+ });
9086
+ return PersistencePromise.waitFor(promises);
8807
9087
  }
8808
- getNextCollectionGroupToUpdate(transaction) {
8809
- // Field indices are not supported with memory persistence.
8810
- return PersistencePromise.resolve(null);
9088
+ getOverlaysForCollection(transaction, collection, sinceBatchId) {
9089
+ const result = new Map();
9090
+ const collectionPath = encodeResourcePath(collection);
9091
+ // We want batch IDs larger than `sinceBatchId`, and so the lower bound
9092
+ // is not inclusive.
9093
+ const range = IDBKeyRange.bound([this.userId, collectionPath, sinceBatchId], [this.userId, collectionPath, Number.POSITIVE_INFINITY],
9094
+ /*lowerOpen=*/ true);
9095
+ return documentOverlayStore(transaction)
9096
+ .loadAll(DbDocumentOverlay.collectionPathOverlayIndex, range)
9097
+ .next(dbOverlays => {
9098
+ for (const dbOverlay of dbOverlays) {
9099
+ const overlay = fromDbDocumentOverlay(this.serializer, dbOverlay);
9100
+ result.set(overlay.getKey(), overlay);
9101
+ }
9102
+ return result;
9103
+ });
8811
9104
  }
8812
- updateCollectionGroup(transaction, collectionGroup, offset) {
8813
- // Field indices are not supported with memory persistence.
8814
- return PersistencePromise.resolve();
9105
+ getOverlaysForCollectionGroup(transaction, collectionGroup, sinceBatchId, count) {
9106
+ const result = new Map();
9107
+ let currentBatchId = undefined;
9108
+ // We want batch IDs larger than `sinceBatchId`, and so the lower bound
9109
+ // is not inclusive.
9110
+ const range = IDBKeyRange.bound([this.userId, collectionGroup, sinceBatchId], [this.userId, collectionGroup, Number.POSITIVE_INFINITY],
9111
+ /*lowerOpen=*/ true);
9112
+ return documentOverlayStore(transaction)
9113
+ .iterate({
9114
+ index: DbDocumentOverlay.collectionGroupOverlayIndex,
9115
+ range
9116
+ }, (_, dbOverlay, control) => {
9117
+ // We do not want to return partial batch overlays, even if the size
9118
+ // of the result set exceeds the given `count` argument. Therefore, we
9119
+ // continue to aggregate results even after the result size exceeds
9120
+ // `count` if there are more overlays from the `currentBatchId`.
9121
+ const overlay = fromDbDocumentOverlay(this.serializer, dbOverlay);
9122
+ if (result.size < count ||
9123
+ overlay.largestBatchId === currentBatchId) {
9124
+ result.set(overlay.getKey(), overlay);
9125
+ currentBatchId = overlay.largestBatchId;
9126
+ }
9127
+ else {
9128
+ control.done();
9129
+ }
9130
+ })
9131
+ .next(() => result);
8815
9132
  }
8816
- updateIndexEntries(transaction, documents) {
8817
- // Field indices are not supported with memory persistence.
8818
- return PersistencePromise.resolve();
9133
+ saveOverlay(transaction, overlay) {
9134
+ return documentOverlayStore(transaction).put(toDbDocumentOverlay(this.serializer, this.userId, overlay));
8819
9135
  }
8820
9136
  }
8821
9137
  /**
8822
- * Internal implementation of the collection-parent index exposed by MemoryIndexManager.
8823
- * Also used for in-memory caching by IndexedDbIndexManager and initial index population
8824
- * in indexeddb_schema.ts
9138
+ * Helper to get a typed SimpleDbStore for the document overlay object store.
8825
9139
  */
8826
- class MemoryCollectionParentIndex {
8827
- constructor() {
8828
- this.index = {};
9140
+ function documentOverlayStore(txn) {
9141
+ return getStore(txn, DbDocumentOverlay.store);
9142
+ }
9143
+
9144
+ /**
9145
+ * @license
9146
+ * Copyright 2021 Google LLC
9147
+ *
9148
+ * Licensed under the Apache License, Version 2.0 (the "License");
9149
+ * you may not use this file except in compliance with the License.
9150
+ * You may obtain a copy of the License at
9151
+ *
9152
+ * http://www.apache.org/licenses/LICENSE-2.0
9153
+ *
9154
+ * Unless required by applicable law or agreed to in writing, software
9155
+ * distributed under the License is distributed on an "AS IS" BASIS,
9156
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9157
+ * See the License for the specific language governing permissions and
9158
+ * limitations under the License.
9159
+ */
9160
+ // Note: This code is copied from the backend. Code that is not used by
9161
+ // Firestore was removed.
9162
+ const INDEX_TYPE_NULL = 5;
9163
+ const INDEX_TYPE_BOOLEAN = 10;
9164
+ const INDEX_TYPE_NAN = 13;
9165
+ const INDEX_TYPE_NUMBER = 15;
9166
+ const INDEX_TYPE_TIMESTAMP = 20;
9167
+ const INDEX_TYPE_STRING = 25;
9168
+ const INDEX_TYPE_BLOB = 30;
9169
+ const INDEX_TYPE_REFERENCE = 37;
9170
+ const INDEX_TYPE_GEOPOINT = 45;
9171
+ const INDEX_TYPE_ARRAY = 50;
9172
+ const INDEX_TYPE_MAP = 55;
9173
+ const INDEX_TYPE_REFERENCE_SEGMENT = 60;
9174
+ // A terminator that indicates that a truncatable value was not truncated.
9175
+ // This must be smaller than all other type labels.
9176
+ const NOT_TRUNCATED = 2;
9177
+ /** Firestore index value writer. */
9178
+ class FirestoreIndexValueWriter {
9179
+ constructor() { }
9180
+ // The write methods below short-circuit writing terminators for values
9181
+ // containing a (terminating) truncated value.
9182
+ //
9183
+ // As an example, consider the resulting encoding for:
9184
+ //
9185
+ // ["bar", [2, "foo"]] -> (STRING, "bar", TERM, ARRAY, NUMBER, 2, STRING, "foo", TERM, TERM, TERM)
9186
+ // ["bar", [2, truncated("foo")]] -> (STRING, "bar", TERM, ARRAY, NUMBER, 2, STRING, "foo", TRUNC)
9187
+ // ["bar", truncated(["foo"])] -> (STRING, "bar", TERM, ARRAY. STRING, "foo", TERM, TRUNC)
9188
+ /** Writes an index value. */
9189
+ writeIndexValue(value, encoder) {
9190
+ this.writeIndexValueAux(value, encoder);
9191
+ // Write separator to split index values
9192
+ // (see go/firestore-storage-format#encodings).
9193
+ encoder.writeInfinity();
9194
+ }
9195
+ writeIndexValueAux(indexValue, encoder) {
9196
+ if ('nullValue' in indexValue) {
9197
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_NULL);
9198
+ }
9199
+ else if ('booleanValue' in indexValue) {
9200
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_BOOLEAN);
9201
+ encoder.writeNumber(indexValue.booleanValue ? 1 : 0);
9202
+ }
9203
+ else if ('integerValue' in indexValue) {
9204
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER);
9205
+ encoder.writeNumber(normalizeNumber(indexValue.integerValue));
9206
+ }
9207
+ else if ('doubleValue' in indexValue) {
9208
+ const n = normalizeNumber(indexValue.doubleValue);
9209
+ if (isNaN(n)) {
9210
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_NAN);
9211
+ }
9212
+ else {
9213
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER);
9214
+ if (isNegativeZero(n)) {
9215
+ // -0.0, 0 and 0.0 are all considered the same
9216
+ encoder.writeNumber(0.0);
9217
+ }
9218
+ else {
9219
+ encoder.writeNumber(n);
9220
+ }
9221
+ }
9222
+ }
9223
+ else if ('timestampValue' in indexValue) {
9224
+ const timestamp = indexValue.timestampValue;
9225
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_TIMESTAMP);
9226
+ if (typeof timestamp === 'string') {
9227
+ encoder.writeString(timestamp);
9228
+ }
9229
+ else {
9230
+ encoder.writeString(`${timestamp.seconds || ''}`);
9231
+ encoder.writeNumber(timestamp.nanos || 0);
9232
+ }
9233
+ }
9234
+ else if ('stringValue' in indexValue) {
9235
+ this.writeIndexString(indexValue.stringValue, encoder);
9236
+ this.writeTruncationMarker(encoder);
9237
+ }
9238
+ else if ('bytesValue' in indexValue) {
9239
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_BLOB);
9240
+ encoder.writeBytes(normalizeByteString(indexValue.bytesValue));
9241
+ this.writeTruncationMarker(encoder);
9242
+ }
9243
+ else if ('referenceValue' in indexValue) {
9244
+ this.writeIndexEntityRef(indexValue.referenceValue, encoder);
9245
+ }
9246
+ else if ('geoPointValue' in indexValue) {
9247
+ const geoPoint = indexValue.geoPointValue;
9248
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_GEOPOINT);
9249
+ encoder.writeNumber(geoPoint.latitude || 0);
9250
+ encoder.writeNumber(geoPoint.longitude || 0);
9251
+ }
9252
+ else if ('mapValue' in indexValue) {
9253
+ if (isMaxValue(indexValue)) {
9254
+ this.writeValueTypeLabel(encoder, Number.MAX_SAFE_INTEGER);
9255
+ }
9256
+ else {
9257
+ this.writeIndexMap(indexValue.mapValue, encoder);
9258
+ this.writeTruncationMarker(encoder);
9259
+ }
9260
+ }
9261
+ else if ('arrayValue' in indexValue) {
9262
+ this.writeIndexArray(indexValue.arrayValue, encoder);
9263
+ this.writeTruncationMarker(encoder);
9264
+ }
9265
+ else {
9266
+ fail();
9267
+ }
8829
9268
  }
8830
- // Returns false if the entry already existed.
8831
- add(collectionPath) {
8832
- const collectionId = collectionPath.lastSegment();
8833
- const parentPath = collectionPath.popLast();
8834
- const existingParents = this.index[collectionId] ||
8835
- new SortedSet(ResourcePath.comparator);
8836
- const added = !existingParents.has(parentPath);
8837
- this.index[collectionId] = existingParents.add(parentPath);
8838
- return added;
9269
+ writeIndexString(stringIndexValue, encoder) {
9270
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_STRING);
9271
+ this.writeUnlabeledIndexString(stringIndexValue, encoder);
8839
9272
  }
8840
- has(collectionPath) {
8841
- const collectionId = collectionPath.lastSegment();
8842
- const parentPath = collectionPath.popLast();
8843
- const existingParents = this.index[collectionId];
8844
- return existingParents && existingParents.has(parentPath);
9273
+ writeUnlabeledIndexString(stringIndexValue, encoder) {
9274
+ encoder.writeString(stringIndexValue);
8845
9275
  }
8846
- getEntries(collectionId) {
8847
- const parentPaths = this.index[collectionId] ||
8848
- new SortedSet(ResourcePath.comparator);
8849
- return parentPaths.toArray();
9276
+ writeIndexMap(mapIndexValue, encoder) {
9277
+ const map = mapIndexValue.fields || {};
9278
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_MAP);
9279
+ for (const key of Object.keys(map)) {
9280
+ this.writeIndexString(key, encoder);
9281
+ this.writeIndexValueAux(map[key], encoder);
9282
+ }
8850
9283
  }
8851
- }
9284
+ writeIndexArray(arrayIndexValue, encoder) {
9285
+ const values = arrayIndexValue.values || [];
9286
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_ARRAY);
9287
+ for (const element of values) {
9288
+ this.writeIndexValueAux(element, encoder);
9289
+ }
9290
+ }
9291
+ writeIndexEntityRef(referenceValue, encoder) {
9292
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_REFERENCE);
9293
+ const path = DocumentKey.fromName(referenceValue).path;
9294
+ path.forEach(segment => {
9295
+ this.writeValueTypeLabel(encoder, INDEX_TYPE_REFERENCE_SEGMENT);
9296
+ this.writeUnlabeledIndexString(segment, encoder);
9297
+ });
9298
+ }
9299
+ writeValueTypeLabel(encoder, typeOrder) {
9300
+ encoder.writeNumber(typeOrder);
9301
+ }
9302
+ writeTruncationMarker(encoder) {
9303
+ // While the SDK does not implement truncation, the truncation marker is
9304
+ // used to terminate all variable length values (which are strings, bytes,
9305
+ // references, arrays and maps).
9306
+ encoder.writeNumber(NOT_TRUNCATED);
9307
+ }
9308
+ }
9309
+ FirestoreIndexValueWriter.INSTANCE = new FirestoreIndexValueWriter();
8852
9310
 
8853
9311
  /**
8854
9312
  * @license
8855
- * Copyright 2019 Google LLC
9313
+ * Copyright 2021 Google LLC
8856
9314
  *
8857
9315
  * Licensed under the Apache License, Version 2.0 (the "License");
8858
9316
  * you may not use this file except in compliance with the License.
@@ -8860,25 +9318,538 @@ class MemoryCollectionParentIndex {
8860
9318
  *
8861
9319
  * http://www.apache.org/licenses/LICENSE-2.0
8862
9320
  *
8863
- * Unless required by applicable law or agreed to in writing, software
9321
+ * Unless required by applicable law | agreed to in writing, software
8864
9322
  * distributed under the License is distributed on an "AS IS" BASIS,
8865
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9323
+ * WITHOUT WARRANTIES | CONDITIONS OF ANY KIND, either express | implied.
8866
9324
  * See the License for the specific language governing permissions and
8867
9325
  * limitations under the License.
8868
9326
  */
9327
+ /** These constants are taken from the backend. */
9328
+ const MIN_SURROGATE = '\uD800';
9329
+ const MAX_SURROGATE = '\uDBFF';
9330
+ const ESCAPE1 = 0x00;
9331
+ const NULL_BYTE = 0xff; // Combined with ESCAPE1
9332
+ const SEPARATOR = 0x01; // Combined with ESCAPE1
9333
+ const ESCAPE2 = 0xff;
9334
+ const INFINITY = 0xff; // Combined with ESCAPE2
9335
+ const FF_BYTE = 0x00; // Combined with ESCAPE2
9336
+ const LONG_SIZE = 64;
9337
+ const BYTE_SIZE = 8;
8869
9338
  /**
8870
- * A persisted implementation of IndexManager.
9339
+ * The default size of the buffer. This is arbitrary, but likely larger than
9340
+ * most index values so that less copies of the underlying buffer will be made.
9341
+ * For large values, a single copy will made to double the buffer length.
8871
9342
  */
8872
- class IndexedDbIndexManager {
8873
- constructor() {
9343
+ const DEFAULT_BUFFER_SIZE = 1024;
9344
+ /** Converts a JavaScript number to a byte array (using big endian encoding). */
9345
+ function doubleToLongBits(value) {
9346
+ const dv = new DataView(new ArrayBuffer(8));
9347
+ dv.setFloat64(0, value, /* littleEndian= */ false);
9348
+ return new Uint8Array(dv.buffer);
9349
+ }
9350
+ /**
9351
+ * Counts the number of zeros in a byte.
9352
+ *
9353
+ * Visible for testing.
9354
+ */
9355
+ function numberOfLeadingZerosInByte(x) {
9356
+ if (x === 0) {
9357
+ return 8;
9358
+ }
9359
+ let zeros = 0;
9360
+ if (x >> 4 === 0) {
9361
+ // Test if the first four bits are zero.
9362
+ zeros += 4;
9363
+ x = x << 4;
9364
+ }
9365
+ if (x >> 6 === 0) {
9366
+ // Test if the first two (or next two) bits are zero.
9367
+ zeros += 2;
9368
+ x = x << 2;
9369
+ }
9370
+ if (x >> 7 === 0) {
9371
+ // Test if the remaining bit is zero.
9372
+ zeros += 1;
9373
+ }
9374
+ return zeros;
9375
+ }
9376
+ /** Counts the number of leading zeros in the given byte array. */
9377
+ function numberOfLeadingZeros(bytes) {
9378
+ let leadingZeros = 0;
9379
+ for (let i = 0; i < 8; ++i) {
9380
+ const zeros = numberOfLeadingZerosInByte(bytes[i] & 0xff);
9381
+ leadingZeros += zeros;
9382
+ if (zeros !== 8) {
9383
+ break;
9384
+ }
9385
+ }
9386
+ return leadingZeros;
9387
+ }
9388
+ /**
9389
+ * Returns the number of bytes required to store "value". Leading zero bytes
9390
+ * are skipped.
9391
+ */
9392
+ function unsignedNumLength(value) {
9393
+ // This is just the number of bytes for the unsigned representation of the number.
9394
+ const numBits = LONG_SIZE - numberOfLeadingZeros(value);
9395
+ return Math.ceil(numBits / BYTE_SIZE);
9396
+ }
9397
+ /**
9398
+ * OrderedCodeWriter is a minimal-allocation implementation of the writing
9399
+ * behavior defined by the backend.
9400
+ *
9401
+ * The code is ported from its Java counterpart.
9402
+ */
9403
+ class OrderedCodeWriter {
9404
+ constructor() {
9405
+ this.buffer = new Uint8Array(DEFAULT_BUFFER_SIZE);
9406
+ this.position = 0;
9407
+ }
9408
+ writeBytesAscending(value) {
9409
+ const it = value[Symbol.iterator]();
9410
+ let byte = it.next();
9411
+ while (!byte.done) {
9412
+ this.writeByteAscending(byte.value);
9413
+ byte = it.next();
9414
+ }
9415
+ this.writeSeparatorAscending();
9416
+ }
9417
+ writeBytesDescending(value) {
9418
+ const it = value[Symbol.iterator]();
9419
+ let byte = it.next();
9420
+ while (!byte.done) {
9421
+ this.writeByteDescending(byte.value);
9422
+ byte = it.next();
9423
+ }
9424
+ this.writeSeparatorDescending();
9425
+ }
9426
+ /** Writes utf8 bytes into this byte sequence, ascending. */
9427
+ writeUtf8Ascending(sequence) {
9428
+ for (const c of sequence) {
9429
+ const charCode = c.charCodeAt(0);
9430
+ if (charCode < 0x80) {
9431
+ this.writeByteAscending(charCode);
9432
+ }
9433
+ else if (charCode < 0x800) {
9434
+ this.writeByteAscending((0x0f << 6) | (charCode >>> 6));
9435
+ this.writeByteAscending(0x80 | (0x3f & charCode));
9436
+ }
9437
+ else if (c < MIN_SURROGATE || MAX_SURROGATE < c) {
9438
+ this.writeByteAscending((0x0f << 5) | (charCode >>> 12));
9439
+ this.writeByteAscending(0x80 | (0x3f & (charCode >>> 6)));
9440
+ this.writeByteAscending(0x80 | (0x3f & charCode));
9441
+ }
9442
+ else {
9443
+ const codePoint = c.codePointAt(0);
9444
+ this.writeByteAscending((0x0f << 4) | (codePoint >>> 18));
9445
+ this.writeByteAscending(0x80 | (0x3f & (codePoint >>> 12)));
9446
+ this.writeByteAscending(0x80 | (0x3f & (codePoint >>> 6)));
9447
+ this.writeByteAscending(0x80 | (0x3f & codePoint));
9448
+ }
9449
+ }
9450
+ this.writeSeparatorAscending();
9451
+ }
9452
+ /** Writes utf8 bytes into this byte sequence, descending */
9453
+ writeUtf8Descending(sequence) {
9454
+ for (const c of sequence) {
9455
+ const charCode = c.charCodeAt(0);
9456
+ if (charCode < 0x80) {
9457
+ this.writeByteDescending(charCode);
9458
+ }
9459
+ else if (charCode < 0x800) {
9460
+ this.writeByteDescending((0x0f << 6) | (charCode >>> 6));
9461
+ this.writeByteDescending(0x80 | (0x3f & charCode));
9462
+ }
9463
+ else if (c < MIN_SURROGATE || MAX_SURROGATE < c) {
9464
+ this.writeByteDescending((0x0f << 5) | (charCode >>> 12));
9465
+ this.writeByteDescending(0x80 | (0x3f & (charCode >>> 6)));
9466
+ this.writeByteDescending(0x80 | (0x3f & charCode));
9467
+ }
9468
+ else {
9469
+ const codePoint = c.codePointAt(0);
9470
+ this.writeByteDescending((0x0f << 4) | (codePoint >>> 18));
9471
+ this.writeByteDescending(0x80 | (0x3f & (codePoint >>> 12)));
9472
+ this.writeByteDescending(0x80 | (0x3f & (codePoint >>> 6)));
9473
+ this.writeByteDescending(0x80 | (0x3f & codePoint));
9474
+ }
9475
+ }
9476
+ this.writeSeparatorDescending();
9477
+ }
9478
+ writeNumberAscending(val) {
9479
+ // Values are encoded with a single byte length prefix, followed by the
9480
+ // actual value in big-endian format with leading 0 bytes dropped.
9481
+ const value = this.toOrderedBits(val);
9482
+ const len = unsignedNumLength(value);
9483
+ this.ensureAvailable(1 + len);
9484
+ this.buffer[this.position++] = len & 0xff; // Write the length
9485
+ for (let i = value.length - len; i < value.length; ++i) {
9486
+ this.buffer[this.position++] = value[i] & 0xff;
9487
+ }
9488
+ }
9489
+ writeNumberDescending(val) {
9490
+ // Values are encoded with a single byte length prefix, followed by the
9491
+ // inverted value in big-endian format with leading 0 bytes dropped.
9492
+ const value = this.toOrderedBits(val);
9493
+ const len = unsignedNumLength(value);
9494
+ this.ensureAvailable(1 + len);
9495
+ this.buffer[this.position++] = ~(len & 0xff); // Write the length
9496
+ for (let i = value.length - len; i < value.length; ++i) {
9497
+ this.buffer[this.position++] = ~(value[i] & 0xff);
9498
+ }
9499
+ }
9500
+ /**
9501
+ * Writes the "infinity" byte sequence that sorts after all other byte
9502
+ * sequences written in ascending order.
9503
+ */
9504
+ writeInfinityAscending() {
9505
+ this.writeEscapedByteAscending(ESCAPE2);
9506
+ this.writeEscapedByteAscending(INFINITY);
9507
+ }
9508
+ /**
9509
+ * Writes the "infinity" byte sequence that sorts before all other byte
9510
+ * sequences written in descending order.
9511
+ */
9512
+ writeInfinityDescending() {
9513
+ this.writeEscapedByteDescending(ESCAPE2);
9514
+ this.writeEscapedByteDescending(INFINITY);
9515
+ }
9516
+ /**
9517
+ * Resets the buffer such that it is the same as when it was newly
9518
+ * constructed.
9519
+ */
9520
+ reset() {
9521
+ this.position = 0;
9522
+ }
9523
+ seed(encodedBytes) {
9524
+ this.ensureAvailable(encodedBytes.length);
9525
+ this.buffer.set(encodedBytes, this.position);
9526
+ this.position += encodedBytes.length;
9527
+ }
9528
+ /** Makes a copy of the encoded bytes in this buffer. */
9529
+ encodedBytes() {
9530
+ return this.buffer.slice(0, this.position);
9531
+ }
9532
+ /**
9533
+ * Encodes `val` into an encoding so that the order matches the IEEE 754
9534
+ * floating-point comparison results with the following exceptions:
9535
+ * -0.0 < 0.0
9536
+ * all non-NaN < NaN
9537
+ * NaN = NaN
9538
+ */
9539
+ toOrderedBits(val) {
9540
+ const value = doubleToLongBits(val);
9541
+ // Check if the first bit is set. We use a bit mask since value[0] is
9542
+ // encoded as a number from 0 to 255.
9543
+ const isNegative = (value[0] & 0x80) !== 0;
9544
+ // Revert the two complement to get natural ordering
9545
+ value[0] ^= isNegative ? 0xff : 0x80;
9546
+ for (let i = 1; i < value.length; ++i) {
9547
+ value[i] ^= isNegative ? 0xff : 0x00;
9548
+ }
9549
+ return value;
9550
+ }
9551
+ /** Writes a single byte ascending to the buffer. */
9552
+ writeByteAscending(b) {
9553
+ const masked = b & 0xff;
9554
+ if (masked === ESCAPE1) {
9555
+ this.writeEscapedByteAscending(ESCAPE1);
9556
+ this.writeEscapedByteAscending(NULL_BYTE);
9557
+ }
9558
+ else if (masked === ESCAPE2) {
9559
+ this.writeEscapedByteAscending(ESCAPE2);
9560
+ this.writeEscapedByteAscending(FF_BYTE);
9561
+ }
9562
+ else {
9563
+ this.writeEscapedByteAscending(masked);
9564
+ }
9565
+ }
9566
+ /** Writes a single byte descending to the buffer. */
9567
+ writeByteDescending(b) {
9568
+ const masked = b & 0xff;
9569
+ if (masked === ESCAPE1) {
9570
+ this.writeEscapedByteDescending(ESCAPE1);
9571
+ this.writeEscapedByteDescending(NULL_BYTE);
9572
+ }
9573
+ else if (masked === ESCAPE2) {
9574
+ this.writeEscapedByteDescending(ESCAPE2);
9575
+ this.writeEscapedByteDescending(FF_BYTE);
9576
+ }
9577
+ else {
9578
+ this.writeEscapedByteDescending(b);
9579
+ }
9580
+ }
9581
+ writeSeparatorAscending() {
9582
+ this.writeEscapedByteAscending(ESCAPE1);
9583
+ this.writeEscapedByteAscending(SEPARATOR);
9584
+ }
9585
+ writeSeparatorDescending() {
9586
+ this.writeEscapedByteDescending(ESCAPE1);
9587
+ this.writeEscapedByteDescending(SEPARATOR);
9588
+ }
9589
+ writeEscapedByteAscending(b) {
9590
+ this.ensureAvailable(1);
9591
+ this.buffer[this.position++] = b;
9592
+ }
9593
+ writeEscapedByteDescending(b) {
9594
+ this.ensureAvailable(1);
9595
+ this.buffer[this.position++] = ~b;
9596
+ }
9597
+ ensureAvailable(bytes) {
9598
+ const minCapacity = bytes + this.position;
9599
+ if (minCapacity <= this.buffer.length) {
9600
+ return;
9601
+ }
9602
+ // Try doubling.
9603
+ let newLength = this.buffer.length * 2;
9604
+ // Still not big enough? Just allocate the right size.
9605
+ if (newLength < minCapacity) {
9606
+ newLength = minCapacity;
9607
+ }
9608
+ // Create the new buffer.
9609
+ const newBuffer = new Uint8Array(newLength);
9610
+ newBuffer.set(this.buffer); // copy old data
9611
+ this.buffer = newBuffer;
9612
+ }
9613
+ }
9614
+
9615
+ class AscendingIndexByteEncoder {
9616
+ constructor(orderedCode) {
9617
+ this.orderedCode = orderedCode;
9618
+ }
9619
+ writeBytes(value) {
9620
+ this.orderedCode.writeBytesAscending(value);
9621
+ }
9622
+ writeString(value) {
9623
+ this.orderedCode.writeUtf8Ascending(value);
9624
+ }
9625
+ writeNumber(value) {
9626
+ this.orderedCode.writeNumberAscending(value);
9627
+ }
9628
+ writeInfinity() {
9629
+ this.orderedCode.writeInfinityAscending();
9630
+ }
9631
+ }
9632
+ class DescendingIndexByteEncoder {
9633
+ constructor(orderedCode) {
9634
+ this.orderedCode = orderedCode;
9635
+ }
9636
+ writeBytes(value) {
9637
+ this.orderedCode.writeBytesDescending(value);
9638
+ }
9639
+ writeString(value) {
9640
+ this.orderedCode.writeUtf8Descending(value);
9641
+ }
9642
+ writeNumber(value) {
9643
+ this.orderedCode.writeNumberDescending(value);
9644
+ }
9645
+ writeInfinity() {
9646
+ this.orderedCode.writeInfinityDescending();
9647
+ }
9648
+ }
9649
+ /**
9650
+ * Implements `DirectionalIndexByteEncoder` using `OrderedCodeWriter` for the
9651
+ * actual encoding.
9652
+ */
9653
+ class IndexByteEncoder {
9654
+ constructor() {
9655
+ this.orderedCode = new OrderedCodeWriter();
9656
+ this.ascending = new AscendingIndexByteEncoder(this.orderedCode);
9657
+ this.descending = new DescendingIndexByteEncoder(this.orderedCode);
9658
+ }
9659
+ seed(encodedBytes) {
9660
+ this.orderedCode.seed(encodedBytes);
9661
+ }
9662
+ forKind(kind) {
9663
+ return kind === 0 /* ASCENDING */ ? this.ascending : this.descending;
9664
+ }
9665
+ encodedBytes() {
9666
+ return this.orderedCode.encodedBytes();
9667
+ }
9668
+ reset() {
9669
+ this.orderedCode.reset();
9670
+ }
9671
+ }
9672
+
9673
+ /**
9674
+ * @license
9675
+ * Copyright 2022 Google LLC
9676
+ *
9677
+ * Licensed under the Apache License, Version 2.0 (the "License");
9678
+ * you may not use this file except in compliance with the License.
9679
+ * You may obtain a copy of the License at
9680
+ *
9681
+ * http://www.apache.org/licenses/LICENSE-2.0
9682
+ *
9683
+ * Unless required by applicable law or agreed to in writing, software
9684
+ * distributed under the License is distributed on an "AS IS" BASIS,
9685
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9686
+ * See the License for the specific language governing permissions and
9687
+ * limitations under the License.
9688
+ */
9689
+ /** Represents an index entry saved by the SDK in persisted storage. */
9690
+ class IndexEntry {
9691
+ constructor(indexId, documentKey, arrayValue, directionalValue) {
9692
+ this.indexId = indexId;
9693
+ this.documentKey = documentKey;
9694
+ this.arrayValue = arrayValue;
9695
+ this.directionalValue = directionalValue;
9696
+ }
9697
+ }
9698
+ function indexEntryComparator(left, right) {
9699
+ let cmp = left.indexId - right.indexId;
9700
+ if (cmp !== 0) {
9701
+ return cmp;
9702
+ }
9703
+ cmp = DocumentKey.comparator(left.documentKey, right.documentKey);
9704
+ if (cmp !== 0) {
9705
+ return cmp;
9706
+ }
9707
+ cmp = compareByteArrays(left.arrayValue, right.arrayValue);
9708
+ if (cmp !== 0) {
9709
+ return cmp;
9710
+ }
9711
+ return compareByteArrays(left.directionalValue, right.directionalValue);
9712
+ }
9713
+ function compareByteArrays(left, right) {
9714
+ for (let i = 0; i < left.length && i < right.length; ++i) {
9715
+ const compare = left[i] - right[i];
9716
+ if (compare !== 0) {
9717
+ return compare;
9718
+ }
9719
+ }
9720
+ return left.length - right.length;
9721
+ }
9722
+
9723
+ /**
9724
+ * @license
9725
+ * Copyright 2019 Google LLC
9726
+ *
9727
+ * Licensed under the Apache License, Version 2.0 (the "License");
9728
+ * you may not use this file except in compliance with the License.
9729
+ * You may obtain a copy of the License at
9730
+ *
9731
+ * http://www.apache.org/licenses/LICENSE-2.0
9732
+ *
9733
+ * Unless required by applicable law or agreed to in writing, software
9734
+ * distributed under the License is distributed on an "AS IS" BASIS,
9735
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9736
+ * See the License for the specific language governing permissions and
9737
+ * limitations under the License.
9738
+ */
9739
+ /**
9740
+ * An in-memory implementation of IndexManager.
9741
+ */
9742
+ class MemoryIndexManager {
9743
+ constructor() {
9744
+ this.collectionParentIndex = new MemoryCollectionParentIndex();
9745
+ }
9746
+ addToCollectionParentIndex(transaction, collectionPath) {
9747
+ this.collectionParentIndex.add(collectionPath);
9748
+ return PersistencePromise.resolve();
9749
+ }
9750
+ getCollectionParents(transaction, collectionId) {
9751
+ return PersistencePromise.resolve(this.collectionParentIndex.getEntries(collectionId));
9752
+ }
9753
+ addFieldIndex(transaction, index) {
9754
+ // Field indices are not supported with memory persistence.
9755
+ return PersistencePromise.resolve();
9756
+ }
9757
+ deleteFieldIndex(transaction, index) {
9758
+ // Field indices are not supported with memory persistence.
9759
+ return PersistencePromise.resolve();
9760
+ }
9761
+ getDocumentsMatchingTarget(transaction, fieldIndex, target) {
9762
+ // Field indices are not supported with memory persistence.
9763
+ return PersistencePromise.resolve(documentKeySet());
9764
+ }
9765
+ getFieldIndex(transaction, target) {
9766
+ // Field indices are not supported with memory persistence.
9767
+ return PersistencePromise.resolve(null);
9768
+ }
9769
+ getFieldIndexes(transaction, collectionGroup) {
9770
+ // Field indices are not supported with memory persistence.
9771
+ return PersistencePromise.resolve([]);
9772
+ }
9773
+ getNextCollectionGroupToUpdate(transaction) {
9774
+ // Field indices are not supported with memory persistence.
9775
+ return PersistencePromise.resolve(null);
9776
+ }
9777
+ updateCollectionGroup(transaction, collectionGroup, offset) {
9778
+ // Field indices are not supported with memory persistence.
9779
+ return PersistencePromise.resolve();
9780
+ }
9781
+ updateIndexEntries(transaction, documents) {
9782
+ // Field indices are not supported with memory persistence.
9783
+ return PersistencePromise.resolve();
9784
+ }
9785
+ }
9786
+ /**
9787
+ * Internal implementation of the collection-parent index exposed by MemoryIndexManager.
9788
+ * Also used for in-memory caching by IndexedDbIndexManager and initial index population
9789
+ * in indexeddb_schema.ts
9790
+ */
9791
+ class MemoryCollectionParentIndex {
9792
+ constructor() {
9793
+ this.index = {};
9794
+ }
9795
+ // Returns false if the entry already existed.
9796
+ add(collectionPath) {
9797
+ const collectionId = collectionPath.lastSegment();
9798
+ const parentPath = collectionPath.popLast();
9799
+ const existingParents = this.index[collectionId] ||
9800
+ new SortedSet(ResourcePath.comparator);
9801
+ const added = !existingParents.has(parentPath);
9802
+ this.index[collectionId] = existingParents.add(parentPath);
9803
+ return added;
9804
+ }
9805
+ has(collectionPath) {
9806
+ const collectionId = collectionPath.lastSegment();
9807
+ const parentPath = collectionPath.popLast();
9808
+ const existingParents = this.index[collectionId];
9809
+ return existingParents && existingParents.has(parentPath);
9810
+ }
9811
+ getEntries(collectionId) {
9812
+ const parentPaths = this.index[collectionId] ||
9813
+ new SortedSet(ResourcePath.comparator);
9814
+ return parentPaths.toArray();
9815
+ }
9816
+ }
9817
+
9818
+ /**
9819
+ * @license
9820
+ * Copyright 2019 Google LLC
9821
+ *
9822
+ * Licensed under the Apache License, Version 2.0 (the "License");
9823
+ * you may not use this file except in compliance with the License.
9824
+ * You may obtain a copy of the License at
9825
+ *
9826
+ * http://www.apache.org/licenses/LICENSE-2.0
9827
+ *
9828
+ * Unless required by applicable law or agreed to in writing, software
9829
+ * distributed under the License is distributed on an "AS IS" BASIS,
9830
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9831
+ * See the License for the specific language governing permissions and
9832
+ * limitations under the License.
9833
+ */
9834
+ const LOG_TAG$f = 'IndexedDbIndexManager';
9835
+ /**
9836
+ * A persisted implementation of IndexManager.
9837
+ *
9838
+ * PORTING NOTE: Unlike iOS and Android, the Web SDK does not memoize index
9839
+ * data as it supports multi-tab access.
9840
+ */
9841
+ class IndexedDbIndexManager {
9842
+ constructor(user) {
9843
+ this.user = user;
8874
9844
  /**
8875
9845
  * An in-memory copy of the index entries we've already written since the SDK
8876
9846
  * launched. Used to avoid re-writing the same entry repeatedly.
8877
9847
  *
8878
- * This is *NOT* a complete cache of what's in persistence and so can never be used to
8879
- * satisfy reads.
9848
+ * This is *NOT* a complete cache of what's in persistence and so can never be
9849
+ * used to satisfy reads.
8880
9850
  */
8881
9851
  this.collectionParentsCache = new MemoryCollectionParentIndex();
9852
+ this.uid = user.uid || '';
8882
9853
  }
8883
9854
  /**
8884
9855
  * Adds a new entry to the collection parent index.
@@ -8926,12 +9897,25 @@ class IndexedDbIndexManager {
8926
9897
  });
8927
9898
  }
8928
9899
  addFieldIndex(transaction, index) {
8929
- // TODO(indexing): Implement
8930
- return PersistencePromise.resolve();
9900
+ // TODO(indexing): Verify that the auto-incrementing index ID works in
9901
+ // Safari & Firefox.
9902
+ const indexes = indexConfigurationStore(transaction);
9903
+ const dbIndex = toDbIndexConfiguration(index);
9904
+ delete dbIndex.indexId; // `indexId` is auto-populated by IndexedDb
9905
+ return indexes.add(dbIndex).next();
8931
9906
  }
8932
9907
  deleteFieldIndex(transaction, index) {
8933
- // TODO(indexing): Implement
8934
- return PersistencePromise.resolve();
9908
+ const indexes = indexConfigurationStore(transaction);
9909
+ const states = indexStateStore(transaction);
9910
+ const entries = indexEntriesStore(transaction);
9911
+ return indexes
9912
+ .delete(index.indexId)
9913
+ .next(() => states.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
9914
+ /*lowerOpen=*/ false,
9915
+ /*upperOpen=*/ true)))
9916
+ .next(() => entries.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
9917
+ /*lowerOpen=*/ false,
9918
+ /*upperOpen=*/ true)));
8935
9919
  }
8936
9920
  getDocumentsMatchingTarget(transaction, fieldIndex, target) {
8937
9921
  // TODO(indexing): Implement
@@ -8941,21 +9925,168 @@ class IndexedDbIndexManager {
8941
9925
  // TODO(indexing): Implement
8942
9926
  return PersistencePromise.resolve(null);
8943
9927
  }
9928
+ /**
9929
+ * Returns the byte encoded form of the directional values in the field index.
9930
+ * Returns `null` if the document does not have all fields specified in the
9931
+ * index.
9932
+ */
9933
+ encodeDirectionalElements(fieldIndex, document) {
9934
+ const encoder = new IndexByteEncoder();
9935
+ for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
9936
+ const field = document.data.field(segment.fieldPath);
9937
+ if (field == null) {
9938
+ return null;
9939
+ }
9940
+ const directionalEncoder = encoder.forKind(segment.kind);
9941
+ FirestoreIndexValueWriter.INSTANCE.writeIndexValue(field, directionalEncoder);
9942
+ }
9943
+ return encoder.encodedBytes();
9944
+ }
9945
+ /** Encodes a single value to the ascending index format. */
9946
+ encodeSingleElement(value) {
9947
+ const encoder = new IndexByteEncoder();
9948
+ FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, encoder.forKind(0 /* ASCENDING */));
9949
+ return encoder.encodedBytes();
9950
+ }
8944
9951
  getFieldIndexes(transaction, collectionGroup) {
8945
- // TODO(indexing): Implement
8946
- return PersistencePromise.resolve([]);
9952
+ const indexes = indexConfigurationStore(transaction);
9953
+ const states = indexStateStore(transaction);
9954
+ return (collectionGroup
9955
+ ? indexes.loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
9956
+ : indexes.loadAll()).next(indexConfigs => {
9957
+ const result = [];
9958
+ return PersistencePromise.forEach(indexConfigs, (indexConfig) => {
9959
+ return states
9960
+ .get([indexConfig.indexId, this.uid])
9961
+ .next(indexState => {
9962
+ result.push(fromDbIndexConfiguration(indexConfig, indexState));
9963
+ });
9964
+ }).next(() => result);
9965
+ });
8947
9966
  }
8948
9967
  getNextCollectionGroupToUpdate(transaction) {
8949
- // TODO(indexing): Implement
8950
- return PersistencePromise.resolve(null);
9968
+ return this.getFieldIndexes(transaction).next(indexes => {
9969
+ if (indexes.length === 0) {
9970
+ return null;
9971
+ }
9972
+ indexes.sort((l, r) => l.indexState.sequenceNumber - r.indexState.sequenceNumber);
9973
+ return indexes[0].collectionGroup;
9974
+ });
8951
9975
  }
8952
9976
  updateCollectionGroup(transaction, collectionGroup, offset) {
8953
- // TODO(indexing): Implement
8954
- return PersistencePromise.resolve();
9977
+ const indexes = indexConfigurationStore(transaction);
9978
+ const states = indexStateStore(transaction);
9979
+ return this.getNextSequenceNumber(transaction).next(nextSequenceNumber => indexes
9980
+ .loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
9981
+ .next(configs => PersistencePromise.forEach(configs, (config) => states.put(toDbIndexState(config.indexId, this.user, nextSequenceNumber, offset)))));
8955
9982
  }
8956
9983
  updateIndexEntries(transaction, documents) {
8957
- // TODO(indexing): Implement
8958
- return PersistencePromise.resolve();
9984
+ // Porting Note: `getFieldIndexes()` on Web does not cache index lookups as
9985
+ // it could be used across different IndexedDB transactions. As any cached
9986
+ // data might be invalidated by other multi-tab clients, we can only trust
9987
+ // data within a single IndexedDB transaction. We therefore add a cache
9988
+ // here.
9989
+ const memoizedIndexes = new Map();
9990
+ return PersistencePromise.forEach(documents, (key, doc) => {
9991
+ const memoizedCollectionIndexes = memoizedIndexes.get(key.collectionGroup);
9992
+ const fieldIndexes = memoizedCollectionIndexes
9993
+ ? PersistencePromise.resolve(memoizedCollectionIndexes)
9994
+ : this.getFieldIndexes(transaction, key.collectionGroup);
9995
+ return fieldIndexes.next(fieldIndexes => {
9996
+ memoizedIndexes.set(key.collectionGroup, fieldIndexes);
9997
+ return PersistencePromise.forEach(fieldIndexes, (fieldIndex) => {
9998
+ return this.getExistingIndexEntries(transaction, key, fieldIndex).next(existingEntries => {
9999
+ const newEntries = this.computeIndexEntries(doc, fieldIndex);
10000
+ if (!existingEntries.isEqual(newEntries)) {
10001
+ return this.updateEntries(transaction, doc, existingEntries, newEntries);
10002
+ }
10003
+ return PersistencePromise.resolve();
10004
+ });
10005
+ });
10006
+ });
10007
+ });
10008
+ }
10009
+ addIndexEntry(transaction, document, indexEntry) {
10010
+ const indexEntries = indexEntriesStore(transaction);
10011
+ return indexEntries.put(new DbIndexEntry(indexEntry.indexId, this.uid, indexEntry.arrayValue, indexEntry.directionalValue, encodeResourcePath(document.key.path)));
10012
+ }
10013
+ deleteIndexEntry(transaction, document, indexEntry) {
10014
+ const indexEntries = indexEntriesStore(transaction);
10015
+ return indexEntries.delete([
10016
+ indexEntry.indexId,
10017
+ this.uid,
10018
+ indexEntry.arrayValue,
10019
+ indexEntry.directionalValue,
10020
+ encodeResourcePath(document.key.path)
10021
+ ]);
10022
+ }
10023
+ getExistingIndexEntries(transaction, documentKey, fieldIndex) {
10024
+ const indexEntries = indexEntriesStore(transaction);
10025
+ let results = new SortedSet(indexEntryComparator);
10026
+ return indexEntries
10027
+ .iterate({
10028
+ index: DbIndexEntry.documentKeyIndex,
10029
+ range: IDBKeyRange.only([
10030
+ fieldIndex.indexId,
10031
+ this.uid,
10032
+ encodeResourcePath(documentKey.path)
10033
+ ])
10034
+ }, (_, entry) => {
10035
+ results = results.add(new IndexEntry(fieldIndex.indexId, documentKey, entry.arrayValue, entry.directionalValue));
10036
+ })
10037
+ .next(() => results);
10038
+ }
10039
+ /** Creates the index entries for the given document. */
10040
+ computeIndexEntries(document, fieldIndex) {
10041
+ let results = new SortedSet(indexEntryComparator);
10042
+ const directionalValue = this.encodeDirectionalElements(fieldIndex, document);
10043
+ if (directionalValue == null) {
10044
+ return results;
10045
+ }
10046
+ const arraySegment = fieldIndexGetArraySegment(fieldIndex);
10047
+ if (arraySegment != null) {
10048
+ const value = document.data.field(arraySegment.fieldPath);
10049
+ if (isArray(value)) {
10050
+ for (const arrayValue of value.arrayValue.values || []) {
10051
+ results = results.add(new IndexEntry(fieldIndex.indexId, document.key, this.encodeSingleElement(arrayValue), directionalValue));
10052
+ }
10053
+ }
10054
+ }
10055
+ else {
10056
+ results = results.add(new IndexEntry(fieldIndex.indexId, document.key, new Uint8Array(), directionalValue));
10057
+ }
10058
+ return results;
10059
+ }
10060
+ /**
10061
+ * Updates the index entries for the provided document by deleting entries
10062
+ * that are no longer referenced in `newEntries` and adding all newly added
10063
+ * entries.
10064
+ */
10065
+ updateEntries(transaction, document, existingEntries, newEntries) {
10066
+ logDebug(LOG_TAG$f, "Updating index entries for document '%s'", document.key);
10067
+ const promises = [];
10068
+ diffSortedSets(existingEntries, newEntries, indexEntryComparator,
10069
+ /* onAdd= */ entry => {
10070
+ promises.push(this.addIndexEntry(transaction, document, entry));
10071
+ },
10072
+ /* onRemove= */ entry => {
10073
+ promises.push(this.deleteIndexEntry(transaction, document, entry));
10074
+ });
10075
+ return PersistencePromise.waitFor(promises);
10076
+ }
10077
+ getNextSequenceNumber(transaction) {
10078
+ let nextSequenceNumber = 1;
10079
+ const states = indexStateStore(transaction);
10080
+ return states
10081
+ .iterate({
10082
+ index: DbIndexState.sequenceNumberIndex,
10083
+ reverse: true,
10084
+ range: IDBKeyRange.upperBound([this.uid, Number.MAX_SAFE_INTEGER])
10085
+ }, (_, state, controller) => {
10086
+ controller.done();
10087
+ nextSequenceNumber = state.sequenceNumber + 1;
10088
+ })
10089
+ .next(() => nextSequenceNumber);
8959
10090
  }
8960
10091
  }
8961
10092
  /**
@@ -8964,6 +10095,24 @@ class IndexedDbIndexManager {
8964
10095
  */
8965
10096
  function collectionParentsStore(txn) {
8966
10097
  return getStore(txn, DbCollectionParent.store);
10098
+ }
10099
+ /**
10100
+ * Helper to get a typed SimpleDbStore for the index entry object store.
10101
+ */
10102
+ function indexEntriesStore(txn) {
10103
+ return getStore(txn, DbIndexEntry.store);
10104
+ }
10105
+ /**
10106
+ * Helper to get a typed SimpleDbStore for the index configuration object store.
10107
+ */
10108
+ function indexConfigurationStore(txn) {
10109
+ return getStore(txn, DbIndexConfiguration.store);
10110
+ }
10111
+ /**
10112
+ * Helper to get a typed SimpleDbStore for the index state object store.
10113
+ */
10114
+ function indexStateStore(txn) {
10115
+ return getStore(txn, DbIndexState.store);
8967
10116
  }
8968
10117
 
8969
10118
  /**
@@ -10433,12 +11582,10 @@ class RemoteDocumentChangeBuffer {
10433
11582
  * `newIndexedDbRemoteDocumentCache()`.
10434
11583
  */
10435
11584
  class IndexedDbRemoteDocumentCacheImpl {
10436
- /**
10437
- * @param serializer - The document serializer.
10438
- * @param indexManager - The query indexes that need to be maintained.
10439
- */
10440
- constructor(serializer, indexManager) {
11585
+ constructor(serializer) {
10441
11586
  this.serializer = serializer;
11587
+ }
11588
+ setIndexManager(indexManager) {
10442
11589
  this.indexManager = indexManager;
10443
11590
  }
10444
11591
  /**
@@ -10560,21 +11707,21 @@ class IndexedDbRemoteDocumentCacheImpl {
10560
11707
  }
10561
11708
  });
10562
11709
  }
10563
- getDocumentsMatchingQuery(transaction, query, sinceReadTime) {
11710
+ getAll(transaction, collection, sinceReadTime) {
10564
11711
  let results = mutableDocumentMap();
10565
- const immediateChildrenPathLength = query.path.length + 1;
11712
+ const immediateChildrenPathLength = collection.length + 1;
10566
11713
  const iterationOptions = {};
10567
11714
  if (sinceReadTime.isEqual(SnapshotVersion.min())) {
10568
11715
  // Documents are ordered by key, so we can use a prefix scan to narrow
10569
11716
  // down the documents we need to match the query against.
10570
- const startKey = query.path.toArray();
11717
+ const startKey = collection.toArray();
10571
11718
  iterationOptions.range = IDBKeyRange.lowerBound(startKey);
10572
11719
  }
10573
11720
  else {
10574
11721
  // Execute an index-free query and filter by read time. This is safe
10575
11722
  // since all document changes to queries that have a
10576
11723
  // lastLimboFreeSnapshotVersion (`sinceReadTime`) have a read time set.
10577
- const collectionKey = query.path.toArray();
11724
+ const collectionKey = collection.toArray();
10578
11725
  const readTimeKey = toDbTimestampKey(sinceReadTime);
10579
11726
  iterationOptions.range = IDBKeyRange.lowerBound([collectionKey, readTimeKey],
10580
11727
  /* open= */ true);
@@ -10591,12 +11738,12 @@ class IndexedDbRemoteDocumentCacheImpl {
10591
11738
  return;
10592
11739
  }
10593
11740
  const document = this.maybeDecodeDocument(DocumentKey.fromSegments(key), dbRemoteDoc);
10594
- if (!query.path.isPrefixOf(document.key.path)) {
10595
- control.done();
10596
- }
10597
- else if (queryMatches(query, document)) {
11741
+ if (collection.isPrefixOf(document.key.path)) {
10598
11742
  results = results.insert(document.key, document);
10599
11743
  }
11744
+ else {
11745
+ control.done();
11746
+ }
10600
11747
  })
10601
11748
  .next(() => results);
10602
11749
  }
@@ -10634,14 +11781,9 @@ class IndexedDbRemoteDocumentCacheImpl {
10634
11781
  return MutableDocument.newInvalidDocument(documentKey);
10635
11782
  }
10636
11783
  }
10637
- /**
10638
- * Creates a new IndexedDbRemoteDocumentCache.
10639
- *
10640
- * @param serializer - The document serializer.
10641
- * @param indexManager - The query indexes that need to be maintained.
10642
- */
10643
- function newIndexedDbRemoteDocumentCache(serializer, indexManager) {
10644
- return new IndexedDbRemoteDocumentCacheImpl(serializer, indexManager);
11784
+ /** Creates a new IndexedDbRemoteDocumentCache. */
11785
+ function newIndexedDbRemoteDocumentCache(serializer) {
11786
+ return new IndexedDbRemoteDocumentCacheImpl(serializer);
10645
11787
  }
10646
11788
  /**
10647
11789
  * Returns the set of documents that have changed since the specified read
@@ -10875,6 +12017,11 @@ class SchemaConverter {
10875
12017
  });
10876
12018
  }
10877
12019
  if (fromVersion < 12 && toVersion >= 12) {
12020
+ p = p.next(() => {
12021
+ createDocumentOverlayStore(db);
12022
+ });
12023
+ }
12024
+ if (fromVersion < 13 && toVersion >= 13) {
10878
12025
  p = p.next(() => {
10879
12026
  createFieldIndex(db);
10880
12027
  });
@@ -11087,15 +12234,26 @@ function createNamedQueriesStore(db) {
11087
12234
  });
11088
12235
  }
11089
12236
  function createFieldIndex(db) {
11090
- db.createObjectStore(DbIndexConfiguration.store, {
11091
- keyPath: DbIndexConfiguration.keyPath
12237
+ const indexConfiguratioStore = db.createObjectStore(DbIndexConfiguration.store, {
12238
+ keyPath: DbIndexConfiguration.keyPath,
12239
+ autoIncrement: true
11092
12240
  });
11093
- db.createObjectStore(DbIndexState.store, {
12241
+ indexConfiguratioStore.createIndex(DbIndexConfiguration.collectionGroupIndex, DbIndexConfiguration.collectionGroupIndexPath, { unique: false });
12242
+ const indexStateStore = db.createObjectStore(DbIndexState.store, {
11094
12243
  keyPath: DbIndexState.keyPath
11095
12244
  });
11096
- db.createObjectStore(DbIndexEntries.store, {
11097
- keyPath: DbIndexEntries.keyPath
12245
+ indexStateStore.createIndex(DbIndexState.sequenceNumberIndex, DbIndexState.sequenceNumberIndexPath, { unique: false });
12246
+ const indexEntryStore = db.createObjectStore(DbIndexEntry.store, {
12247
+ keyPath: DbIndexEntry.keyPath
12248
+ });
12249
+ indexEntryStore.createIndex(DbIndexEntry.documentKeyIndex, DbIndexEntry.documentKeyIndexPath, { unique: false });
12250
+ }
12251
+ function createDocumentOverlayStore(db) {
12252
+ const documentOverlayStore = db.createObjectStore(DbDocumentOverlay.store, {
12253
+ keyPath: DbDocumentOverlay.keyPath
11098
12254
  });
12255
+ documentOverlayStore.createIndex(DbDocumentOverlay.collectionPathOverlayIndex, DbDocumentOverlay.collectionPathOverlayIndexPath, { unique: false });
12256
+ documentOverlayStore.createIndex(DbDocumentOverlay.collectionGroupOverlayIndex, DbDocumentOverlay.collectionGroupOverlayIndexPath, { unique: false });
11099
12257
  }
11100
12258
 
11101
12259
  /**
@@ -11207,7 +12365,7 @@ class IndexedDbPersistence {
11207
12365
  * If set to true, forcefully obtains database access. Existing tabs will
11208
12366
  * no longer be able to access IndexedDB.
11209
12367
  */
11210
- forceOwningTab) {
12368
+ forceOwningTab, schemaVersion = SCHEMA_VERSION) {
11211
12369
  this.allowTabSynchronization = allowTabSynchronization;
11212
12370
  this.persistenceKey = persistenceKey;
11213
12371
  this.clientId = clientId;
@@ -11216,6 +12374,7 @@ class IndexedDbPersistence {
11216
12374
  this.document = document;
11217
12375
  this.sequenceNumberSyncer = sequenceNumberSyncer;
11218
12376
  this.forceOwningTab = forceOwningTab;
12377
+ this.schemaVersion = schemaVersion;
11219
12378
  this.listenSequence = null;
11220
12379
  this._started = false;
11221
12380
  this.isPrimary = false;
@@ -11237,10 +12396,9 @@ class IndexedDbPersistence {
11237
12396
  this.referenceDelegate = new IndexedDbLruDelegateImpl(this, lruParams);
11238
12397
  this.dbName = persistenceKey + MAIN_DATABASE;
11239
12398
  this.serializer = new LocalSerializer(serializer);
11240
- this.simpleDb = new SimpleDb(this.dbName, SCHEMA_VERSION, new SchemaConverter(this.serializer));
12399
+ this.simpleDb = new SimpleDb(this.dbName, this.schemaVersion, new SchemaConverter(this.serializer));
11241
12400
  this.targetCache = new IndexedDbTargetCache(this.referenceDelegate, this.serializer);
11242
- this.indexManager = new IndexedDbIndexManager();
11243
- this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer, this.indexManager);
12401
+ this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer);
11244
12402
  this.bundleCache = new IndexedDbBundleCache();
11245
12403
  if (this.window && this.window.localStorage) {
11246
12404
  this.webStorage = this.window.localStorage;
@@ -11576,8 +12734,8 @@ class IndexedDbPersistence {
11576
12734
  get started() {
11577
12735
  return this._started;
11578
12736
  }
11579
- getMutationQueue(user) {
11580
- return IndexedDbMutationQueue.forUser(user, this.serializer, this.indexManager, this.referenceDelegate);
12737
+ getMutationQueue(user, indexManager) {
12738
+ return IndexedDbMutationQueue.forUser(user, this.serializer, indexManager, this.referenceDelegate);
11581
12739
  }
11582
12740
  getTargetCache() {
11583
12741
  return this.targetCache;
@@ -11585,8 +12743,11 @@ class IndexedDbPersistence {
11585
12743
  getRemoteDocumentCache() {
11586
12744
  return this.remoteDocumentCache;
11587
12745
  }
11588
- getIndexManager() {
11589
- return this.indexManager;
12746
+ getIndexManager(user) {
12747
+ return new IndexedDbIndexManager(user);
12748
+ }
12749
+ getDocumentOverlayCache(user) {
12750
+ return IndexedDbDocumentOverlayCache.forUser(this.serializer, user);
11590
12751
  }
11591
12752
  getBundleCache() {
11592
12753
  return this.bundleCache;
@@ -11594,11 +12755,12 @@ class IndexedDbPersistence {
11594
12755
  runTransaction(action, mode, transactionOperation) {
11595
12756
  logDebug(LOG_TAG$d, 'Starting transaction:', action);
11596
12757
  const simpleDbMode = mode === 'readonly' ? 'readonly' : 'readwrite';
12758
+ const objectStores = getObjectStores(this.schemaVersion);
11597
12759
  let persistenceTransaction;
11598
12760
  // Do all transactions as readwrite against all object stores, since we
11599
12761
  // are the only reader/writer.
11600
12762
  return this.simpleDb
11601
- .runTransaction(action, simpleDbMode, ALL_STORES, simpleDbTxn => {
12763
+ .runTransaction(action, simpleDbMode, objectStores, simpleDbTxn => {
11602
12764
  persistenceTransaction = new IndexedDbTransaction(simpleDbTxn, this.listenSequence
11603
12765
  ? this.listenSequence.next()
11604
12766
  : ListenSequence.INVALID);
@@ -11970,39 +13132,28 @@ class LocalDocumentsView {
11970
13132
  getDocumentsMatchingCollectionQuery(transaction, query, sinceReadTime) {
11971
13133
  // Query the remote documents and overlay mutations.
11972
13134
  let results;
11973
- let mutationBatches;
11974
13135
  return this.remoteDocumentCache
11975
- .getDocumentsMatchingQuery(transaction, query, sinceReadTime)
13136
+ .getAll(transaction, query.path, sinceReadTime)
11976
13137
  .next(queryResults => {
11977
13138
  results = queryResults;
11978
13139
  return this.mutationQueue.getAllMutationBatchesAffectingQuery(transaction, query);
11979
13140
  })
11980
- .next(matchingMutationBatches => {
11981
- mutationBatches = matchingMutationBatches;
11982
- // It is possible that a PatchMutation can make a document match a query, even if
11983
- // the version in the RemoteDocumentCache is not a match yet (waiting for server
11984
- // to ack). To handle this, we find all document keys affected by the PatchMutations
11985
- // that are not in `result` yet, and back fill them via `remoteDocumentCache.getEntries`,
11986
- // otherwise those `PatchMutations` will be ignored because no base document can be found,
11987
- // and lead to missing result for the query.
11988
- return this.addMissingBaseDocuments(transaction, mutationBatches, results).next(mergedDocuments => {
11989
- results = mergedDocuments;
11990
- for (const batch of mutationBatches) {
11991
- for (const mutation of batch.mutations) {
11992
- const key = mutation.key;
11993
- let document = results.get(key);
11994
- if (document == null) {
11995
- // Create invalid document to apply mutations on top of
11996
- document = MutableDocument.newInvalidDocument(key);
11997
- results = results.insert(key, document);
11998
- }
11999
- mutationApplyToLocalView(mutation, document, batch.localWriteTime);
12000
- if (!document.isFoundDocument()) {
12001
- results = results.remove(key);
12002
- }
13141
+ .next(mutationBatches => {
13142
+ for (const batch of mutationBatches) {
13143
+ for (const mutation of batch.mutations) {
13144
+ const key = mutation.key;
13145
+ let document = results.get(key);
13146
+ if (document == null) {
13147
+ // Create invalid document to apply mutations on top of
13148
+ document = MutableDocument.newInvalidDocument(key);
13149
+ results = results.insert(key, document);
13150
+ }
13151
+ mutationApplyToLocalView(mutation, document, batch.localWriteTime);
13152
+ if (!document.isFoundDocument()) {
13153
+ results = results.remove(key);
12003
13154
  }
12004
13155
  }
12005
- });
13156
+ }
12006
13157
  })
12007
13158
  .next(() => {
12008
13159
  // Finally, filter out any documents that don't actually match
@@ -12015,29 +13166,6 @@ class LocalDocumentsView {
12015
13166
  return results;
12016
13167
  });
12017
13168
  }
12018
- addMissingBaseDocuments(transaction, matchingMutationBatches, existingDocuments) {
12019
- let missingBaseDocEntriesForPatching = documentKeySet();
12020
- for (const batch of matchingMutationBatches) {
12021
- for (const mutation of batch.mutations) {
12022
- if (mutation instanceof PatchMutation &&
12023
- existingDocuments.get(mutation.key) === null) {
12024
- missingBaseDocEntriesForPatching =
12025
- missingBaseDocEntriesForPatching.add(mutation.key);
12026
- }
12027
- }
12028
- }
12029
- let mergedDocuments = existingDocuments;
12030
- return this.remoteDocumentCache
12031
- .getEntries(transaction, missingBaseDocEntriesForPatching)
12032
- .next(missingBaseDocs => {
12033
- missingBaseDocs.forEach((key, doc) => {
12034
- if (doc.isFoundDocument()) {
12035
- mergedDocuments = mergedDocuments.insert(key, doc);
12036
- }
12037
- });
12038
- return mergedDocuments;
12039
- });
12040
- }
12041
13169
  }
12042
13170
 
12043
13171
  /**
@@ -12096,11 +13224,18 @@ class LocalStoreImpl {
12096
13224
  * PORTING NOTE: This is only used for multi-tab synchronization.
12097
13225
  */
12098
13226
  this.lastDocumentChangeReadTime = SnapshotVersion.min();
12099
- this.mutationQueue = persistence.getMutationQueue(initialUser);
12100
13227
  this.remoteDocuments = persistence.getRemoteDocumentCache();
12101
13228
  this.targetCache = persistence.getTargetCache();
12102
- this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.persistence.getIndexManager());
12103
13229
  this.bundleCache = persistence.getBundleCache();
13230
+ this.initializeUserComponents(initialUser);
13231
+ }
13232
+ initializeUserComponents(user) {
13233
+ // TODO(indexing): Add spec tests that test these components change after a
13234
+ // user change
13235
+ this.indexManager = this.persistence.getIndexManager(user);
13236
+ this.mutationQueue = this.persistence.getMutationQueue(user, this.indexManager);
13237
+ this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.indexManager);
13238
+ this.remoteDocuments.setIndexManager(this.indexManager);
12104
13239
  this.queryEngine.setLocalDocumentsView(this.localDocuments);
12105
13240
  }
12106
13241
  collectGarbage(garbageCollector) {
@@ -12122,8 +13257,6 @@ persistence, queryEngine, initialUser, serializer) {
12122
13257
  // change.
12123
13258
  async function localStoreHandleUserChange(localStore, user) {
12124
13259
  const localStoreImpl = debugCast(localStore);
12125
- let newMutationQueue = localStoreImpl.mutationQueue;
12126
- let newLocalDocuments = localStoreImpl.localDocuments;
12127
13260
  const result = await localStoreImpl.persistence.runTransaction('Handle user change', 'readonly', txn => {
12128
13261
  // Swap out the mutation queue, grabbing the pending mutation batches
12129
13262
  // before and after.
@@ -12132,11 +13265,8 @@ async function localStoreHandleUserChange(localStore, user) {
12132
13265
  .getAllMutationBatches(txn)
12133
13266
  .next(promisedOldBatches => {
12134
13267
  oldBatches = promisedOldBatches;
12135
- newMutationQueue = localStoreImpl.persistence.getMutationQueue(user);
12136
- // Recreate our LocalDocumentsView using the new
12137
- // MutationQueue.
12138
- newLocalDocuments = new LocalDocumentsView(localStoreImpl.remoteDocuments, newMutationQueue, localStoreImpl.persistence.getIndexManager());
12139
- return newMutationQueue.getAllMutationBatches(txn);
13268
+ localStoreImpl.initializeUserComponents(user);
13269
+ return localStoreImpl.mutationQueue.getAllMutationBatches(txn);
12140
13270
  })
12141
13271
  .next(newBatches => {
12142
13272
  const removedBatchIds = [];
@@ -12157,7 +13287,7 @@ async function localStoreHandleUserChange(localStore, user) {
12157
13287
  }
12158
13288
  // Return the set of all (potentially) changed documents and the list
12159
13289
  // of mutation batch IDs that were affected by change.
12160
- return newLocalDocuments
13290
+ return localStoreImpl.localDocuments
12161
13291
  .getDocuments(txn, changedKeys)
12162
13292
  .next(affectedDocuments => {
12163
13293
  return {
@@ -12168,9 +13298,6 @@ async function localStoreHandleUserChange(localStore, user) {
12168
13298
  });
12169
13299
  });
12170
13300
  });
12171
- localStoreImpl.mutationQueue = newMutationQueue;
12172
- localStoreImpl.localDocuments = newLocalDocuments;
12173
- localStoreImpl.queryEngine.setLocalDocumentsView(localStoreImpl.localDocuments);
12174
13301
  return result;
12175
13302
  }
12176
13303
  /* Accepts locally generated Mutations and commit them to storage. */
@@ -12883,6 +14010,122 @@ class MemoryBundleCache {
12883
14010
  }
12884
14011
  }
12885
14012
 
14013
+ /**
14014
+ * @license
14015
+ * Copyright 2022 Google LLC
14016
+ *
14017
+ * Licensed under the Apache License, Version 2.0 (the "License");
14018
+ * you may not use this file except in compliance with the License.
14019
+ * You may obtain a copy of the License at
14020
+ *
14021
+ * http://www.apache.org/licenses/LICENSE-2.0
14022
+ *
14023
+ * Unless required by applicable law or agreed to in writing, software
14024
+ * distributed under the License is distributed on an "AS IS" BASIS,
14025
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14026
+ * See the License for the specific language governing permissions and
14027
+ * limitations under the License.
14028
+ */
14029
+ /**
14030
+ * An in-memory implementation of DocumentOverlayCache.
14031
+ */
14032
+ class MemoryDocumentOverlayCache {
14033
+ constructor() {
14034
+ // A map sorted by DocumentKey, whose value is a pair of the largest batch id
14035
+ // for the overlay and the overlay itself.
14036
+ this.overlays = new SortedMap(DocumentKey.comparator);
14037
+ this.overlayByBatchId = new Map();
14038
+ }
14039
+ getOverlay(transaction, key) {
14040
+ return PersistencePromise.resolve(this.overlays.get(key));
14041
+ }
14042
+ saveOverlays(transaction, largestBatchId, overlays) {
14043
+ overlays.forEach(mutation => {
14044
+ this.saveOverlay(transaction, largestBatchId, mutation);
14045
+ });
14046
+ return PersistencePromise.resolve();
14047
+ }
14048
+ removeOverlaysForBatchId(transaction, documentKeys, batchId) {
14049
+ const keys = this.overlayByBatchId.get(batchId);
14050
+ if (keys !== undefined) {
14051
+ keys.forEach(key => (this.overlays = this.overlays.remove(key)));
14052
+ this.overlayByBatchId.delete(batchId);
14053
+ }
14054
+ return PersistencePromise.resolve();
14055
+ }
14056
+ getOverlaysForCollection(transaction, collection, sinceBatchId) {
14057
+ const result = new Map();
14058
+ const immediateChildrenPathLength = collection.length + 1;
14059
+ const prefix = new DocumentKey(collection.child(''));
14060
+ const iter = this.overlays.getIteratorFrom(prefix);
14061
+ while (iter.hasNext()) {
14062
+ const entry = iter.getNext();
14063
+ const overlay = entry.value;
14064
+ const key = overlay.getKey();
14065
+ if (!collection.isPrefixOf(key.path)) {
14066
+ break;
14067
+ }
14068
+ // Documents from sub-collections
14069
+ if (key.path.length !== immediateChildrenPathLength) {
14070
+ continue;
14071
+ }
14072
+ if (overlay.largestBatchId > sinceBatchId) {
14073
+ result.set(overlay.getKey(), overlay);
14074
+ }
14075
+ }
14076
+ return PersistencePromise.resolve(result);
14077
+ }
14078
+ getOverlaysForCollectionGroup(transaction, collectionGroup, sinceBatchId, count) {
14079
+ let batchIdToOverlays = new SortedMap((key1, key2) => key1 - key2);
14080
+ const iter = this.overlays.getIterator();
14081
+ while (iter.hasNext()) {
14082
+ const entry = iter.getNext();
14083
+ const overlay = entry.value;
14084
+ const key = overlay.getKey();
14085
+ if (key.getCollectionGroup() !== collectionGroup) {
14086
+ continue;
14087
+ }
14088
+ if (overlay.largestBatchId > sinceBatchId) {
14089
+ let overlaysForBatchId = batchIdToOverlays.get(overlay.largestBatchId);
14090
+ if (overlaysForBatchId === null) {
14091
+ overlaysForBatchId = new Map();
14092
+ batchIdToOverlays = batchIdToOverlays.insert(overlay.largestBatchId, overlaysForBatchId);
14093
+ }
14094
+ overlaysForBatchId.set(overlay.getKey(), overlay);
14095
+ }
14096
+ }
14097
+ const result = new Map();
14098
+ const batchIter = batchIdToOverlays.getIterator();
14099
+ while (batchIter.hasNext()) {
14100
+ const entry = batchIter.getNext();
14101
+ const overlays = entry.value;
14102
+ overlays.forEach((overlay, key) => result.set(key, overlay));
14103
+ if (result.size >= count) {
14104
+ break;
14105
+ }
14106
+ }
14107
+ return PersistencePromise.resolve(result);
14108
+ }
14109
+ saveOverlay(transaction, largestBatchId, mutation) {
14110
+ if (mutation === null) {
14111
+ return;
14112
+ }
14113
+ // Remove the association of the overlay to its batch id.
14114
+ const existing = this.overlays.get(mutation.key);
14115
+ if (existing !== null) {
14116
+ this.overlayByBatchId.get(existing.largestBatchId).delete(mutation.key);
14117
+ }
14118
+ this.overlays = this.overlays.insert(mutation.key, new Overlay(largestBatchId, mutation));
14119
+ // Create the association of this overlay to the given largestBatchId.
14120
+ let batch = this.overlayByBatchId.get(largestBatchId);
14121
+ if (batch === undefined) {
14122
+ batch = new Set();
14123
+ this.overlayByBatchId.set(largestBatchId, batch);
14124
+ }
14125
+ batch.add(mutation.key);
14126
+ }
14127
+ }
14128
+
12886
14129
  /**
12887
14130
  * @license
12888
14131
  * Copyright 2017 Google LLC
@@ -13236,14 +14479,16 @@ class MemoryRemoteDocumentCacheImpl {
13236
14479
  * expected to just return 0 to avoid unnecessarily doing the work of
13237
14480
  * calculating the size.
13238
14481
  */
13239
- constructor(indexManager, sizer) {
13240
- this.indexManager = indexManager;
14482
+ constructor(sizer) {
13241
14483
  this.sizer = sizer;
13242
14484
  /** Underlying cache of documents and their read times. */
13243
14485
  this.docs = documentEntryMap();
13244
14486
  /** Size of all cached documents. */
13245
14487
  this.size = 0;
13246
14488
  }
14489
+ setIndexManager(indexManager) {
14490
+ this.indexManager = indexManager;
14491
+ }
13247
14492
  /**
13248
14493
  * Adds the supplied entry to the cache and updates the cache size as appropriate.
13249
14494
  *
@@ -13291,21 +14536,22 @@ class MemoryRemoteDocumentCacheImpl {
13291
14536
  });
13292
14537
  return PersistencePromise.resolve(results);
13293
14538
  }
13294
- getDocumentsMatchingQuery(transaction, query, sinceReadTime) {
14539
+ getAll(transaction, collectionPath, sinceReadTime) {
13295
14540
  let results = mutableDocumentMap();
13296
14541
  // Documents are ordered by key, so we can use a prefix scan to narrow down
13297
14542
  // the documents we need to match the query against.
13298
- const prefix = new DocumentKey(query.path.child(''));
14543
+ const prefix = new DocumentKey(collectionPath.child(''));
13299
14544
  const iterator = this.docs.getIteratorFrom(prefix);
13300
14545
  while (iterator.hasNext()) {
13301
14546
  const { key, value: { document } } = iterator.getNext();
13302
- if (!query.path.isPrefixOf(key.path)) {
14547
+ if (!collectionPath.isPrefixOf(key.path)) {
13303
14548
  break;
13304
14549
  }
13305
- if (document.readTime.compareTo(sinceReadTime) <= 0) {
14550
+ if (key.path.length > collectionPath.length + 1) {
14551
+ // Exclude entries from subcollections.
13306
14552
  continue;
13307
14553
  }
13308
- if (!queryMatches(query, document)) {
14554
+ if (document.readTime.compareTo(sinceReadTime) <= 0) {
13309
14555
  continue;
13310
14556
  }
13311
14557
  results = results.insert(document.key, document.mutableCopy());
@@ -13327,13 +14573,12 @@ class MemoryRemoteDocumentCacheImpl {
13327
14573
  /**
13328
14574
  * Creates a new memory-only RemoteDocumentCache.
13329
14575
  *
13330
- * @param indexManager - A class that manages collection group indices.
13331
14576
  * @param sizer - Used to assess the size of a document. For eager GC, this is
13332
14577
  * expected to just return 0 to avoid unnecessarily doing the work of
13333
14578
  * calculating the size.
13334
14579
  */
13335
- function newMemoryRemoteDocumentCache(indexManager, sizer) {
13336
- return new MemoryRemoteDocumentCacheImpl(indexManager, sizer);
14580
+ function newMemoryRemoteDocumentCache(sizer) {
14581
+ return new MemoryRemoteDocumentCacheImpl(sizer);
13337
14582
  }
13338
14583
  /**
13339
14584
  * Handles the details of adding and updating documents in the MemoryRemoteDocumentCache.
@@ -13527,6 +14772,7 @@ class MemoryPersistence {
13527
14772
  */
13528
14773
  constructor(referenceDelegateFactory, serializer) {
13529
14774
  this.mutationQueues = {};
14775
+ this.overlays = {};
13530
14776
  this.listenSequence = new ListenSequence(0);
13531
14777
  this._started = false;
13532
14778
  this._started = true;
@@ -13534,7 +14780,7 @@ class MemoryPersistence {
13534
14780
  this.targetCache = new MemoryTargetCache(this);
13535
14781
  const sizer = (doc) => this.referenceDelegate.documentSize(doc);
13536
14782
  this.indexManager = new MemoryIndexManager();
13537
- this.remoteDocumentCache = newMemoryRemoteDocumentCache(this.indexManager, sizer);
14783
+ this.remoteDocumentCache = newMemoryRemoteDocumentCache(sizer);
13538
14784
  this.serializer = new LocalSerializer(serializer);
13539
14785
  this.bundleCache = new MemoryBundleCache(this.serializer);
13540
14786
  }
@@ -13555,13 +14801,23 @@ class MemoryPersistence {
13555
14801
  setNetworkEnabled() {
13556
14802
  // No op.
13557
14803
  }
13558
- getIndexManager() {
14804
+ getIndexManager(user) {
14805
+ // We do not currently support indices for memory persistence, so we can
14806
+ // return the same shared instance of the memory index manager.
13559
14807
  return this.indexManager;
13560
14808
  }
13561
- getMutationQueue(user) {
14809
+ getDocumentOverlayCache(user) {
14810
+ let overlay = this.overlays[user.toKey()];
14811
+ if (!overlay) {
14812
+ overlay = new MemoryDocumentOverlayCache();
14813
+ this.overlays[user.toKey()] = overlay;
14814
+ }
14815
+ return overlay;
14816
+ }
14817
+ getMutationQueue(user, indexManager) {
13562
14818
  let queue = this.mutationQueues[user.toKey()];
13563
14819
  if (!queue) {
13564
- queue = new MemoryMutationQueue(this.indexManager, this.referenceDelegate);
14820
+ queue = new MemoryMutationQueue(indexManager, this.referenceDelegate);
13565
14821
  this.mutationQueues[user.toKey()] = queue;
13566
14822
  }
13567
14823
  return queue;
@@ -14706,7 +15962,7 @@ const LOG_TAG$9 = 'Connection';
14706
15962
  const X_GOOG_API_CLIENT_VALUE = `gl-node/${process.versions.node} fire/${SDK_VERSION} grpc/${grpcVersion}`;
14707
15963
  function createMetadata(databasePath, authToken, appCheckToken, appId) {
14708
15964
  hardAssert(authToken === null || authToken.type === 'OAuth');
14709
- const metadata = new Metadata();
15965
+ const metadata = new grpc.Metadata();
14710
15966
  if (authToken) {
14711
15967
  authToken.headers.forEach((value, key) => metadata.set(key, value));
14712
15968
  }
@@ -14737,10 +15993,10 @@ class GrpcConnection {
14737
15993
  ensureActiveStub() {
14738
15994
  if (!this.cachedStub) {
14739
15995
  logDebug(LOG_TAG$9, 'Creating Firestore stub.');
14740
- const credentials$1 = this.databaseInfo.ssl
14741
- ? credentials.createSsl()
14742
- : credentials.createInsecure();
14743
- this.cachedStub = new this.firestore.Firestore(this.databaseInfo.host, credentials$1);
15996
+ const credentials = this.databaseInfo.ssl
15997
+ ? grpc.credentials.createSsl()
15998
+ : grpc.credentials.createInsecure();
15999
+ this.cachedStub = new this.firestore.Firestore(this.databaseInfo.host, credentials);
14744
16000
  }
14745
16001
  return this.cachedStub;
14746
16002
  }
@@ -17572,8 +18828,8 @@ const protoLoaderOptions = {
17572
18828
  * @returns The GrpcObject representing our protos.
17573
18829
  */
17574
18830
  function loadProtos() {
17575
- const packageDefinition = fromJSON(protos$1, protoLoaderOptions);
17576
- return loadPackageDefinition(packageDefinition);
18831
+ const packageDefinition = protoLoader.fromJSON(protos$1, protoLoaderOptions);
18832
+ return grpc.loadPackageDefinition(packageDefinition);
17577
18833
  }
17578
18834
 
17579
18835
  /**