@firebase/firestore 3.4.4-canary.0482aa5b7 → 3.4.4-canary.3e9f3e253

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 (134) hide show
  1. package/dist/firestore/src/api/credentials.d.ts +1 -0
  2. package/dist/firestore/src/local/document_overlay_cache.d.ts +72 -0
  3. package/dist/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
  4. package/dist/firestore/src/local/indexeddb_index_manager.d.ts +10 -2
  5. package/dist/firestore/src/local/indexeddb_persistence.d.ts +6 -4
  6. package/dist/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  7. package/dist/firestore/src/local/indexeddb_schema.d.ts +81 -9
  8. package/dist/firestore/src/local/local_documents_view.d.ts +0 -1
  9. package/dist/firestore/src/local/local_serializer.d.ts +18 -2
  10. package/dist/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
  11. package/dist/firestore/src/local/memory_persistence.d.ts +6 -2
  12. package/dist/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  13. package/dist/firestore/src/local/persistence.d.ts +8 -2
  14. package/dist/firestore/src/local/remote_document_cache.d.ts +7 -9
  15. package/dist/firestore/src/local/simple_db.d.ts +1 -0
  16. package/dist/firestore/src/model/document_key.d.ts +4 -0
  17. package/dist/firestore/src/model/field_index.d.ts +2 -2
  18. package/dist/firestore/src/model/overlay.d.ts +32 -0
  19. package/dist/firestore/src/model/target_index_matcher.d.ts +70 -0
  20. package/dist/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  21. package/dist/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
  22. package/dist/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  23. package/dist/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
  24. package/dist/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  25. package/dist/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  26. package/dist/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  27. package/dist/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  28. package/dist/index.esm2017.js +3497 -3134
  29. package/dist/index.esm2017.js.map +1 -1
  30. package/dist/index.esm5.js +3448 -3123
  31. package/dist/index.esm5.js.map +1 -1
  32. package/dist/index.node.cjs.js +639 -154
  33. package/dist/index.node.cjs.js.map +1 -1
  34. package/dist/index.node.mjs +639 -154
  35. package/dist/index.node.mjs.map +1 -1
  36. package/dist/index.rn.js +3586 -3227
  37. package/dist/index.rn.js.map +1 -1
  38. package/dist/internal.d.ts +83 -12
  39. package/dist/lite/firestore/src/api/credentials.d.ts +1 -0
  40. package/dist/lite/firestore/src/local/document_overlay_cache.d.ts +72 -0
  41. package/dist/lite/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
  42. package/dist/lite/firestore/src/local/indexeddb_index_manager.d.ts +10 -2
  43. package/dist/lite/firestore/src/local/indexeddb_persistence.d.ts +6 -4
  44. package/dist/lite/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  45. package/dist/lite/firestore/src/local/indexeddb_schema.d.ts +81 -9
  46. package/dist/lite/firestore/src/local/local_documents_view.d.ts +0 -1
  47. package/dist/lite/firestore/src/local/local_serializer.d.ts +18 -2
  48. package/dist/lite/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
  49. package/dist/lite/firestore/src/local/memory_persistence.d.ts +6 -2
  50. package/dist/lite/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  51. package/dist/lite/firestore/src/local/persistence.d.ts +8 -2
  52. package/dist/lite/firestore/src/local/remote_document_cache.d.ts +7 -9
  53. package/dist/lite/firestore/src/local/simple_db.d.ts +1 -0
  54. package/dist/lite/firestore/src/model/document_key.d.ts +4 -0
  55. package/dist/lite/firestore/src/model/field_index.d.ts +2 -2
  56. package/dist/lite/firestore/src/model/overlay.d.ts +32 -0
  57. package/dist/lite/firestore/src/model/target_index_matcher.d.ts +70 -0
  58. package/dist/lite/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  59. package/dist/lite/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
  60. package/dist/lite/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  61. package/dist/lite/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
  62. package/dist/lite/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  63. package/dist/lite/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  64. package/dist/lite/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  65. package/dist/lite/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  66. package/dist/lite/index.browser.esm2017.js +1118 -1112
  67. package/dist/lite/index.browser.esm2017.js.map +1 -1
  68. package/dist/lite/index.browser.esm5.js +53 -47
  69. package/dist/lite/index.browser.esm5.js.map +1 -1
  70. package/dist/lite/index.node.cjs.js +10 -2
  71. package/dist/lite/index.node.cjs.js.map +1 -1
  72. package/dist/lite/index.node.mjs +10 -2
  73. package/dist/lite/index.node.mjs.map +1 -1
  74. package/dist/lite/index.rn.esm2017.js +1134 -1128
  75. package/dist/lite/index.rn.esm2017.js.map +1 -1
  76. package/dist/lite/internal.d.ts +4 -0
  77. package/dist/lite/packages/firestore/dist/lite/index.browser.esm2017.d.ts +112 -112
  78. package/dist/lite/packages/firestore/src/api/credentials.d.ts +1 -0
  79. package/dist/lite/packages/firestore/src/local/document_overlay_cache.d.ts +72 -0
  80. package/dist/lite/packages/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
  81. package/dist/lite/packages/firestore/src/local/indexeddb_index_manager.d.ts +10 -2
  82. package/dist/lite/packages/firestore/src/local/indexeddb_persistence.d.ts +6 -4
  83. package/dist/lite/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  84. package/dist/lite/packages/firestore/src/local/indexeddb_schema.d.ts +81 -9
  85. package/dist/lite/packages/firestore/src/local/local_documents_view.d.ts +0 -1
  86. package/dist/lite/packages/firestore/src/local/local_serializer.d.ts +18 -2
  87. package/dist/lite/packages/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
  88. package/dist/lite/packages/firestore/src/local/memory_persistence.d.ts +6 -2
  89. package/dist/lite/packages/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  90. package/dist/lite/packages/firestore/src/local/persistence.d.ts +8 -2
  91. package/dist/lite/packages/firestore/src/local/remote_document_cache.d.ts +7 -9
  92. package/dist/lite/packages/firestore/src/local/simple_db.d.ts +1 -0
  93. package/dist/lite/packages/firestore/src/model/document_key.d.ts +4 -0
  94. package/dist/lite/packages/firestore/src/model/field_index.d.ts +2 -2
  95. package/dist/lite/packages/firestore/src/model/overlay.d.ts +32 -0
  96. package/dist/lite/packages/firestore/src/model/target_index_matcher.d.ts +70 -0
  97. package/dist/lite/packages/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  98. package/dist/lite/packages/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
  99. package/dist/lite/packages/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  100. package/dist/lite/packages/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
  101. package/dist/lite/packages/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  102. package/dist/lite/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  103. package/dist/lite/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  104. package/dist/lite/packages/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  105. package/dist/packages/firestore/dist/index.esm2017.d.ts +181 -179
  106. package/dist/packages/firestore/src/api/credentials.d.ts +1 -0
  107. package/dist/packages/firestore/src/local/document_overlay_cache.d.ts +72 -0
  108. package/dist/packages/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
  109. package/dist/packages/firestore/src/local/indexeddb_index_manager.d.ts +10 -2
  110. package/dist/packages/firestore/src/local/indexeddb_persistence.d.ts +6 -4
  111. package/dist/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  112. package/dist/packages/firestore/src/local/indexeddb_schema.d.ts +81 -9
  113. package/dist/packages/firestore/src/local/local_documents_view.d.ts +0 -1
  114. package/dist/packages/firestore/src/local/local_serializer.d.ts +18 -2
  115. package/dist/packages/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
  116. package/dist/packages/firestore/src/local/memory_persistence.d.ts +6 -2
  117. package/dist/packages/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  118. package/dist/packages/firestore/src/local/persistence.d.ts +8 -2
  119. package/dist/packages/firestore/src/local/remote_document_cache.d.ts +7 -9
  120. package/dist/packages/firestore/src/local/simple_db.d.ts +1 -0
  121. package/dist/packages/firestore/src/model/document_key.d.ts +4 -0
  122. package/dist/packages/firestore/src/model/field_index.d.ts +2 -2
  123. package/dist/packages/firestore/src/model/overlay.d.ts +32 -0
  124. package/dist/packages/firestore/src/model/target_index_matcher.d.ts +70 -0
  125. package/dist/packages/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  126. package/dist/packages/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
  127. package/dist/packages/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  128. package/dist/packages/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
  129. package/dist/packages/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  130. package/dist/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  131. package/dist/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  132. package/dist/packages/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  133. package/dist/private.d.ts +79 -12
  134. package/package.json +9 -9
@@ -12,7 +12,7 @@ var grpcJs = require('@grpc/grpc-js');
12
12
  var protoLoader = require('@grpc/proto-loader');
13
13
 
14
14
  const name = "@firebase/firestore";
15
- const version$1 = "3.4.4-canary.0482aa5b7";
15
+ const version$1 = "3.4.4-canary.3e9f3e253";
16
16
 
17
17
  /**
18
18
  * @license
@@ -65,7 +65,7 @@ User.GOOGLE_CREDENTIALS = new User('google-credentials-uid');
65
65
  User.FIRST_PARTY = new User('first-party-uid');
66
66
  User.MOCK_USER = new User('mock-user');
67
67
 
68
- const version = "9.6.5-canary.0482aa5b7";
68
+ const version = "9.6.6-canary.3e9f3e253";
69
69
 
70
70
  /**
71
71
  * @license
@@ -670,13 +670,19 @@ class FirebaseAppCheckTokenProvider {
670
670
  this.appCheckProvider = appCheckProvider;
671
671
  this.forceRefresh = false;
672
672
  this.appCheck = null;
673
+ this.latestAppCheckToken = null;
673
674
  }
674
675
  start(asyncQueue, changeListener) {
675
676
  const onTokenChanged = tokenResult => {
676
677
  if (tokenResult.error != null) {
677
678
  logDebug('FirebaseAppCheckTokenProvider', `Error getting App Check token; using placeholder token instead. Error: ${tokenResult.error.message}`);
678
679
  }
679
- return changeListener(tokenResult.token);
680
+ const tokenUpdated = tokenResult.token !== this.latestAppCheckToken;
681
+ this.latestAppCheckToken = tokenResult.token;
682
+ logDebug('FirebaseAppCheckTokenProvider', `Received ${tokenUpdated ? 'new' : 'existing'} token.`);
683
+ return tokenUpdated
684
+ ? changeListener(tokenResult.token)
685
+ : Promise.resolve();
680
686
  };
681
687
  this.tokenListener = (tokenResult) => {
682
688
  asyncQueue.enqueueRetryable(() => onTokenChanged(tokenResult));
@@ -711,6 +717,7 @@ class FirebaseAppCheckTokenProvider {
711
717
  return this.appCheck.getToken(forceRefresh).then(tokenResult => {
712
718
  if (tokenResult) {
713
719
  hardAssert(typeof tokenResult.token === 'string');
720
+ this.latestAppCheckToken = tokenResult.token;
714
721
  return new AppCheckToken(tokenResult.token);
715
722
  }
716
723
  else {
@@ -1214,9 +1221,10 @@ function decodeResourcePath(path) {
1214
1221
  * an auto-incrementing ID. This is required for Index-Free queries.
1215
1222
  * 10. Rewrite the canonical IDs to the explicit Protobuf-based format.
1216
1223
  * 11. Add bundles and named_queries for bundle support.
1217
- * 12. Add indexing support.
1224
+ * 12. Add document overlays.
1225
+ * 13. Add indexing support.
1218
1226
  */
1219
- const SCHEMA_VERSION = 11;
1227
+ const SCHEMA_VERSION = 12;
1220
1228
  /**
1221
1229
  * Wrapper class to store timestamps (seconds and nanos) in IndexedDb objects.
1222
1230
  */
@@ -1778,7 +1786,10 @@ DbNamedQuery.keyPath = 'name';
1778
1786
  /** An object representing the global configuration for a field index. */
1779
1787
  class DbIndexConfiguration {
1780
1788
  constructor(
1781
- /** The index id for this entry. */
1789
+ /**
1790
+ * The index id for this entry. Undefined for indexes that are not yet
1791
+ * persisted.
1792
+ */
1782
1793
  indexId,
1783
1794
  /** The collection group this index belongs to. */
1784
1795
  collectionGroup,
@@ -1792,6 +1803,16 @@ class DbIndexConfiguration {
1792
1803
  /** Name of the IndexedDb object store. */
1793
1804
  DbIndexConfiguration.store = 'indexConfiguration';
1794
1805
  DbIndexConfiguration.keyPath = 'indexId';
1806
+ /**
1807
+ * An index that provides access to the index configurations by collection
1808
+ * group.
1809
+ *
1810
+ * PORTING NOTE: iOS and Android maintain this index in-memory, but this is
1811
+ * not possible here as the Web client supports concurrent access to
1812
+ * persistence via multi-tab.
1813
+ */
1814
+ DbIndexConfiguration.collectionGroupIndex = 'collectionGroupIndex';
1815
+ DbIndexConfiguration.collectionGroupIndexPath = 'collectionGroup';
1795
1816
  /**
1796
1817
  * An object describing how up-to-date the index backfill is for each user and
1797
1818
  * index.
@@ -1833,8 +1854,18 @@ class DbIndexState {
1833
1854
  /** Name of the IndexedDb object store. */
1834
1855
  DbIndexState.store = 'indexState';
1835
1856
  DbIndexState.keyPath = ['indexId', 'uid'];
1857
+ /**
1858
+ * An index that provides access to documents in a collection sorted by last
1859
+ * update time. Used by the backfiller.
1860
+ *
1861
+ * PORTING NOTE: iOS and Android maintain this index in-memory, but this is
1862
+ * not possible here as the Web client supports concurrent access to
1863
+ * persistence via multi-tab.
1864
+ */
1865
+ DbIndexState.sequenceNumberIndex = 'sequenceNumberIndex';
1866
+ DbIndexState.sequenceNumberIndexPath = ['uid', 'sequenceNumber'];
1836
1867
  /** An object that stores the encoded entries for all documents and fields. */
1837
- class DbIndexEntries {
1868
+ class DbIndexEntry {
1838
1869
  constructor(
1839
1870
  /** The index id for this entry. */
1840
1871
  indexId,
@@ -1854,14 +1885,54 @@ class DbIndexEntries {
1854
1885
  }
1855
1886
  }
1856
1887
  /** Name of the IndexedDb object store. */
1857
- DbIndexEntries.store = 'indexEntries';
1858
- DbIndexEntries.keyPath = [
1888
+ DbIndexEntry.store = 'indexEntries';
1889
+ DbIndexEntry.keyPath = [
1859
1890
  'indexId',
1860
1891
  'uid',
1861
1892
  'arrayValue',
1862
1893
  'directionalValue',
1863
1894
  'documentKey'
1864
1895
  ];
1896
+ /**
1897
+ * An object representing a document overlay.
1898
+ */
1899
+ class DbDocumentOverlay {
1900
+ constructor(
1901
+ /** The user ID to whom this overlay belongs. */
1902
+ userId,
1903
+ /** The path to the collection that contains the document. */
1904
+ collectionPath,
1905
+ /** The ID (key) of the document within the collection. */
1906
+ documentId,
1907
+ /** The collection group to which the document belongs. */
1908
+ collectionGroup,
1909
+ /** The largest batch ID that's been applied for this overlay. */
1910
+ largestBatchId,
1911
+ /** The overlay mutation. */
1912
+ overlayMutation) {
1913
+ this.userId = userId;
1914
+ this.collectionPath = collectionPath;
1915
+ this.documentId = documentId;
1916
+ this.collectionGroup = collectionGroup;
1917
+ this.largestBatchId = largestBatchId;
1918
+ this.overlayMutation = overlayMutation;
1919
+ }
1920
+ }
1921
+ /** Name of the IndexedDb object store. */
1922
+ DbDocumentOverlay.store = 'documentOverlays';
1923
+ DbDocumentOverlay.keyPath = ['userId', 'collectionPath', 'documentId'];
1924
+ DbDocumentOverlay.collectionPathOverlayIndex = 'collectionPathOverlayIndex';
1925
+ DbDocumentOverlay.collectionPathOverlayIndexPath = [
1926
+ 'userId',
1927
+ 'collectionPath',
1928
+ 'largestBatchId'
1929
+ ];
1930
+ DbDocumentOverlay.collectionGroupOverlayIndex = 'collectionGroupOverlayIndex';
1931
+ DbDocumentOverlay.collectionGroupOverlayIndexPath = [
1932
+ 'userId',
1933
+ 'collectionGroup',
1934
+ 'largestBatchId'
1935
+ ];
1865
1936
  // Visible for testing
1866
1937
  const V1_STORES = [
1867
1938
  DbMutationQueue.store,
@@ -1885,18 +1956,28 @@ const V8_STORES = [...V6_STORES, DbCollectionParent.store];
1885
1956
  // V9 does not change the set of stores.
1886
1957
  // V10 does not change the set of stores.
1887
1958
  const V11_STORES = [...V8_STORES, DbBundle.store, DbNamedQuery.store];
1888
- [
1889
- ...V11_STORES,
1959
+ const V12_STORES = [...V11_STORES, DbDocumentOverlay.store];
1960
+ const V13_STORES = [
1961
+ ...V12_STORES,
1890
1962
  DbIndexConfiguration.store,
1891
1963
  DbIndexState.store,
1892
- DbIndexEntries.store
1964
+ DbIndexEntry.store
1893
1965
  ];
1894
- /**
1895
- * The list of all default IndexedDB stores used throughout the SDK. This is
1896
- * used when creating transactions so that access across all stores is done
1897
- * atomically.
1898
- */
1899
- const ALL_STORES = V11_STORES;
1966
+ /** Returns the object stores for the provided schema. */
1967
+ function getObjectStores(schemaVersion) {
1968
+ if (schemaVersion === 13) {
1969
+ return V13_STORES;
1970
+ }
1971
+ else if (schemaVersion === 12) {
1972
+ return V12_STORES;
1973
+ }
1974
+ else if (schemaVersion === 11) {
1975
+ return V11_STORES;
1976
+ }
1977
+ else {
1978
+ fail();
1979
+ }
1980
+ }
1900
1981
 
1901
1982
  /**
1902
1983
  * @license
@@ -2193,6 +2274,15 @@ class SimpleDbTransaction {
2193
2274
  this.transaction.abort();
2194
2275
  }
2195
2276
  }
2277
+ maybeCommit() {
2278
+ // If the browser supports V3 IndexedDB, we invoke commit() explicitly to
2279
+ // speed up index DB processing if the event loop remains blocks.
2280
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2281
+ const maybeV3IndexedDb = this.transaction;
2282
+ if (!this.aborted && typeof maybeV3IndexedDb.commit === 'function') {
2283
+ maybeV3IndexedDb.commit();
2284
+ }
2285
+ }
2196
2286
  /**
2197
2287
  * Returns a SimpleDbStore<KeyType, ValueType> for the specified store. All
2198
2288
  * operations performed on the SimpleDbStore happen within the context of this
@@ -2389,6 +2479,10 @@ class SimpleDb {
2389
2479
  this.db = await this.ensureDb(action);
2390
2480
  const transaction = SimpleDbTransaction.open(this.db, action, readonly ? 'readonly' : 'readwrite', objectStores);
2391
2481
  const transactionFnResult = transactionFn(transaction)
2482
+ .next(result => {
2483
+ transaction.maybeCommit();
2484
+ return result;
2485
+ })
2392
2486
  .catch(error => {
2393
2487
  // Abort the transaction if there was an error.
2394
2488
  transaction.abort(error);
@@ -2561,13 +2655,29 @@ class SimpleDbStore {
2561
2655
  return wrapRequest(request);
2562
2656
  }
2563
2657
  loadAll(indexOrRange, range) {
2564
- const cursor = this.cursor(this.options(indexOrRange, range));
2565
- const results = [];
2566
- return this.iterateCursor(cursor, (key, value) => {
2567
- results.push(value);
2568
- }).next(() => {
2569
- return results;
2570
- });
2658
+ const iterateOptions = this.options(indexOrRange, range);
2659
+ // Use `getAll()` if the browser supports IndexedDB v3, as it is roughly
2660
+ // 20% faster. Unfortunately, getAll() does not support custom indices.
2661
+ if (!iterateOptions.index && typeof this.store.getAll === 'function') {
2662
+ const request = this.store.getAll(iterateOptions.range);
2663
+ return new PersistencePromise((resolve, reject) => {
2664
+ request.onerror = (event) => {
2665
+ reject(event.target.error);
2666
+ };
2667
+ request.onsuccess = (event) => {
2668
+ resolve(event.target.result);
2669
+ };
2670
+ });
2671
+ }
2672
+ else {
2673
+ const cursor = this.cursor(iterateOptions);
2674
+ const results = [];
2675
+ return this.iterateCursor(cursor, (key, value) => {
2676
+ results.push(value);
2677
+ }).next(() => {
2678
+ return results;
2679
+ });
2680
+ }
2571
2681
  }
2572
2682
  deleteAll(indexOrRange, range) {
2573
2683
  logDebug(LOG_TAG$g, 'DELETE ALL', this.store.name);
@@ -3598,6 +3708,14 @@ class DocumentKey {
3598
3708
  return (this.path.length >= 2 &&
3599
3709
  this.path.get(this.path.length - 2) === collectionId);
3600
3710
  }
3711
+ /** Returns the collection group (i.e. the name of the parent collection) for this key. */
3712
+ getCollectionGroup() {
3713
+ return this.path.get(this.path.length - 2);
3714
+ }
3715
+ /** Returns the fully qualified path to the parent collection. */
3716
+ getCollectionPath() {
3717
+ return this.path.popLast();
3718
+ }
3601
3719
  isEqual(other) {
3602
3720
  return (other !== null && ResourcePath.comparator(this.path, other.path) === 0);
3603
3721
  }
@@ -4421,12 +4539,12 @@ class FieldIndex {
4421
4539
  /** The collection ID this index applies to. */
4422
4540
  collectionGroup,
4423
4541
  /** The field segments for this index. */
4424
- segments,
4542
+ fields,
4425
4543
  /** Shows how up-to-date the index is for the current user. */
4426
4544
  indexState) {
4427
4545
  this.indexId = indexId;
4428
4546
  this.collectionGroup = collectionGroup;
4429
- this.segments = segments;
4547
+ this.fields = fields;
4430
4548
  this.indexState = indexState;
4431
4549
  }
4432
4550
  }
@@ -6686,6 +6804,47 @@ class MutationBatchResult {
6686
6804
  }
6687
6805
  }
6688
6806
 
6807
+ /**
6808
+ * @license
6809
+ * Copyright 2022 Google LLC
6810
+ *
6811
+ * Licensed under the Apache License, Version 2.0 (the "License");
6812
+ * you may not use this file except in compliance with the License.
6813
+ * You may obtain a copy of the License at
6814
+ *
6815
+ * http://www.apache.org/licenses/LICENSE-2.0
6816
+ *
6817
+ * Unless required by applicable law or agreed to in writing, software
6818
+ * distributed under the License is distributed on an "AS IS" BASIS,
6819
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
6820
+ * See the License for the specific language governing permissions and
6821
+ * limitations under the License.
6822
+ */
6823
+ /**
6824
+ * Representation of an overlay computed by Firestore.
6825
+ *
6826
+ * Holds information about a mutation and the largest batch id in Firestore when
6827
+ * the mutation was created.
6828
+ */
6829
+ class Overlay {
6830
+ constructor(largestBatchId, mutation) {
6831
+ this.largestBatchId = largestBatchId;
6832
+ this.mutation = mutation;
6833
+ }
6834
+ getKey() {
6835
+ return this.mutation.key;
6836
+ }
6837
+ isEqual(other) {
6838
+ return other !== null && this.mutation === other.mutation;
6839
+ }
6840
+ toString() {
6841
+ return `Overlay{
6842
+ largestBatchId: ${this.largestBatchId},
6843
+ mutation: ${this.mutation.toString()}
6844
+ }`;
6845
+ }
6846
+ }
6847
+
6689
6848
  /**
6690
6849
  * @license
6691
6850
  * Copyright 2017 Google LLC
@@ -8687,6 +8846,37 @@ function fromBundleMetadata(metadata) {
8687
8846
  version: metadata.version,
8688
8847
  createTime: fromVersion(metadata.createTime)
8689
8848
  };
8849
+ }
8850
+ /** Encodes a DbDocumentOverlay object to an Overlay model object. */
8851
+ function fromDbDocumentOverlay(localSerializer, dbDocumentOverlay) {
8852
+ return new Overlay(dbDocumentOverlay.largestBatchId, fromMutation(localSerializer.remoteSerializer, dbDocumentOverlay.overlayMutation));
8853
+ }
8854
+ /** Decodes an Overlay model object into a DbDocumentOverlay object. */
8855
+ function toDbDocumentOverlay(localSerializer, userId, overlay) {
8856
+ const [_, collectionPath, documentId] = toDbDocumentOverlayKey(userId, overlay.mutation.key);
8857
+ return new DbDocumentOverlay(userId, collectionPath, documentId, overlay.mutation.key.getCollectionGroup(), overlay.largestBatchId, toMutation(localSerializer.remoteSerializer, overlay.mutation));
8858
+ }
8859
+ /**
8860
+ * Returns the DbDocumentOverlayKey corresponding to the given user and
8861
+ * document key.
8862
+ */
8863
+ function toDbDocumentOverlayKey(userId, docKey) {
8864
+ const docId = docKey.path.lastSegment();
8865
+ const collectionPath = encodeResourcePath(docKey.path.popLast());
8866
+ return [userId, collectionPath, docId];
8867
+ }
8868
+ function toDbIndexConfiguration(index) {
8869
+ return new DbIndexConfiguration(index.indexId, index.collectionGroup, index.fields.map(s => [s.fieldPath.canonicalString(), s.kind]));
8870
+ }
8871
+ function fromDbIndexConfiguration(index, state) {
8872
+ const decodedState = state
8873
+ ? new IndexState(state.sequenceNumber, new IndexOffset(fromDbTimestamp(state.readTime), new DocumentKey(decodeResourcePath(state.documentKey)), state.largestBatchId))
8874
+ : IndexState.empty();
8875
+ const decodedSegments = index.fields.map(([fieldPath, kind]) => new IndexSegment(FieldPath$1.fromServerFormat(fieldPath), kind));
8876
+ return new FieldIndex(index.indexId, index.collectionGroup, decodedSegments, decodedState);
8877
+ }
8878
+ function toDbIndexState(indexId, user, sequenceNumber, offset) {
8879
+ return new DbIndexState(indexId, user.uid || '', sequenceNumber, toDbTimestamp(offset.readTime), encodeResourcePath(offset.documentKey.path), offset.largestBatchId);
8690
8880
  }
8691
8881
 
8692
8882
  /**
@@ -8746,6 +8936,125 @@ function namedQueriesStore(txn) {
8746
8936
  return getStore(txn, DbNamedQuery.store);
8747
8937
  }
8748
8938
 
8939
+ /**
8940
+ * @license
8941
+ * Copyright 2022 Google LLC
8942
+ *
8943
+ * Licensed under the Apache License, Version 2.0 (the "License");
8944
+ * you may not use this file except in compliance with the License.
8945
+ * You may obtain a copy of the License at
8946
+ *
8947
+ * http://www.apache.org/licenses/LICENSE-2.0
8948
+ *
8949
+ * Unless required by applicable law or agreed to in writing, software
8950
+ * distributed under the License is distributed on an "AS IS" BASIS,
8951
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8952
+ * See the License for the specific language governing permissions and
8953
+ * limitations under the License.
8954
+ */
8955
+ /**
8956
+ * Implementation of DocumentOverlayCache using IndexedDb.
8957
+ */
8958
+ class IndexedDbDocumentOverlayCache {
8959
+ /**
8960
+ * @param serializer - The document serializer.
8961
+ * @param userId - The userId for which we are accessing overlays.
8962
+ */
8963
+ constructor(serializer, userId) {
8964
+ this.serializer = serializer;
8965
+ this.userId = userId;
8966
+ }
8967
+ static forUser(serializer, user) {
8968
+ const userId = user.uid || '';
8969
+ return new IndexedDbDocumentOverlayCache(serializer, userId);
8970
+ }
8971
+ getOverlay(transaction, key) {
8972
+ return documentOverlayStore(transaction)
8973
+ .get(toDbDocumentOverlayKey(this.userId, key))
8974
+ .next(dbOverlay => {
8975
+ if (dbOverlay) {
8976
+ return fromDbDocumentOverlay(this.serializer, dbOverlay);
8977
+ }
8978
+ return null;
8979
+ });
8980
+ }
8981
+ saveOverlays(transaction, largestBatchId, overlays) {
8982
+ const promises = [];
8983
+ overlays.forEach(mutation => {
8984
+ const overlay = new Overlay(largestBatchId, mutation);
8985
+ promises.push(this.saveOverlay(transaction, overlay));
8986
+ });
8987
+ return PersistencePromise.waitFor(promises);
8988
+ }
8989
+ removeOverlaysForBatchId(transaction, documentKeys, batchId) {
8990
+ const collectionPaths = new Set();
8991
+ // Get the set of unique collection paths.
8992
+ documentKeys.forEach(key => collectionPaths.add(encodeResourcePath(key.getCollectionPath())));
8993
+ const promises = [];
8994
+ collectionPaths.forEach(collectionPath => {
8995
+ const range = IDBKeyRange.bound([this.userId, collectionPath, batchId], [this.userId, collectionPath, batchId + 1],
8996
+ /*lowerOpen=*/ false,
8997
+ /*upperOpen=*/ true);
8998
+ promises.push(documentOverlayStore(transaction).deleteAll(DbDocumentOverlay.collectionPathOverlayIndex, range));
8999
+ });
9000
+ return PersistencePromise.waitFor(promises);
9001
+ }
9002
+ getOverlaysForCollection(transaction, collection, sinceBatchId) {
9003
+ const result = new Map();
9004
+ const collectionPath = encodeResourcePath(collection);
9005
+ // We want batch IDs larger than `sinceBatchId`, and so the lower bound
9006
+ // is not inclusive.
9007
+ const range = IDBKeyRange.bound([this.userId, collectionPath, sinceBatchId], [this.userId, collectionPath, Number.POSITIVE_INFINITY],
9008
+ /*lowerOpen=*/ true);
9009
+ return documentOverlayStore(transaction)
9010
+ .loadAll(DbDocumentOverlay.collectionPathOverlayIndex, range)
9011
+ .next(dbOverlays => {
9012
+ for (const dbOverlay of dbOverlays) {
9013
+ const overlay = fromDbDocumentOverlay(this.serializer, dbOverlay);
9014
+ result.set(overlay.getKey(), overlay);
9015
+ }
9016
+ return result;
9017
+ });
9018
+ }
9019
+ getOverlaysForCollectionGroup(transaction, collectionGroup, sinceBatchId, count) {
9020
+ const result = new Map();
9021
+ let currentBatchId = undefined;
9022
+ // We want batch IDs larger than `sinceBatchId`, and so the lower bound
9023
+ // is not inclusive.
9024
+ const range = IDBKeyRange.bound([this.userId, collectionGroup, sinceBatchId], [this.userId, collectionGroup, Number.POSITIVE_INFINITY],
9025
+ /*lowerOpen=*/ true);
9026
+ return documentOverlayStore(transaction)
9027
+ .iterate({
9028
+ index: DbDocumentOverlay.collectionGroupOverlayIndex,
9029
+ range
9030
+ }, (_, dbOverlay, control) => {
9031
+ // We do not want to return partial batch overlays, even if the size
9032
+ // of the result set exceeds the given `count` argument. Therefore, we
9033
+ // continue to aggregate results even after the result size exceeds
9034
+ // `count` if there are more overlays from the `currentBatchId`.
9035
+ const overlay = fromDbDocumentOverlay(this.serializer, dbOverlay);
9036
+ if (result.size < count ||
9037
+ overlay.largestBatchId === currentBatchId) {
9038
+ result.set(overlay.getKey(), overlay);
9039
+ currentBatchId = overlay.largestBatchId;
9040
+ }
9041
+ else {
9042
+ control.done();
9043
+ }
9044
+ })
9045
+ .next(() => result);
9046
+ }
9047
+ saveOverlay(transaction, overlay) {
9048
+ return documentOverlayStore(transaction).put(toDbDocumentOverlay(this.serializer, this.userId, overlay));
9049
+ }
9050
+ }
9051
+ /**
9052
+ * Helper to get a typed SimpleDbStore for the document overlay object store.
9053
+ */
9054
+ function documentOverlayStore(txn) {
9055
+ return getStore(txn, DbDocumentOverlay.store);
9056
+ }
9057
+
8749
9058
  /**
8750
9059
  * @license
8751
9060
  * Copyright 2019 Google LLC
@@ -8859,17 +9168,22 @@ class MemoryCollectionParentIndex {
8859
9168
  */
8860
9169
  /**
8861
9170
  * A persisted implementation of IndexManager.
9171
+ *
9172
+ * PORTING NOTE: Unlike iOS and Android, the Web SDK does not memoize index
9173
+ * data as it supports multi-tab access.
8862
9174
  */
8863
9175
  class IndexedDbIndexManager {
8864
- constructor() {
9176
+ constructor(user) {
9177
+ this.user = user;
8865
9178
  /**
8866
9179
  * An in-memory copy of the index entries we've already written since the SDK
8867
9180
  * launched. Used to avoid re-writing the same entry repeatedly.
8868
9181
  *
8869
- * This is *NOT* a complete cache of what's in persistence and so can never be used to
8870
- * satisfy reads.
9182
+ * This is *NOT* a complete cache of what's in persistence and so can never be
9183
+ * used to satisfy reads.
8871
9184
  */
8872
9185
  this.collectionParentsCache = new MemoryCollectionParentIndex();
9186
+ this.uid = user.uid || '';
8873
9187
  }
8874
9188
  /**
8875
9189
  * Adds a new entry to the collection parent index.
@@ -8917,12 +9231,25 @@ class IndexedDbIndexManager {
8917
9231
  });
8918
9232
  }
8919
9233
  addFieldIndex(transaction, index) {
8920
- // TODO(indexing): Implement
8921
- return PersistencePromise.resolve();
9234
+ // TODO(indexing): Verify that the auto-incrementing index ID works in
9235
+ // Safari & Firefox.
9236
+ const indexes = indexConfigurationStore(transaction);
9237
+ const dbIndex = toDbIndexConfiguration(index);
9238
+ delete dbIndex.indexId; // `indexId` is auto-populated by IndexedDb
9239
+ return indexes.add(dbIndex).next();
8922
9240
  }
8923
9241
  deleteFieldIndex(transaction, index) {
8924
- // TODO(indexing): Implement
8925
- return PersistencePromise.resolve();
9242
+ const indexes = indexConfigurationStore(transaction);
9243
+ const states = indexStateStore(transaction);
9244
+ const entries = indexEntriesStore(transaction);
9245
+ return indexes
9246
+ .delete(index.indexId)
9247
+ .next(() => states.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
9248
+ /*lowerOpen=*/ false,
9249
+ /*upperOpen=*/ true)))
9250
+ .next(() => entries.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
9251
+ /*lowerOpen=*/ false,
9252
+ /*upperOpen=*/ true)));
8926
9253
  }
8927
9254
  getDocumentsMatchingTarget(transaction, fieldIndex, target) {
8928
9255
  // TODO(indexing): Implement
@@ -8933,21 +9260,55 @@ class IndexedDbIndexManager {
8933
9260
  return PersistencePromise.resolve(null);
8934
9261
  }
8935
9262
  getFieldIndexes(transaction, collectionGroup) {
8936
- // TODO(indexing): Implement
8937
- return PersistencePromise.resolve([]);
9263
+ const indexes = indexConfigurationStore(transaction);
9264
+ const states = indexStateStore(transaction);
9265
+ return (collectionGroup
9266
+ ? indexes.loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
9267
+ : indexes.loadAll()).next(indexConfigs => {
9268
+ const result = [];
9269
+ return PersistencePromise.forEach(indexConfigs, (indexConfig) => {
9270
+ return states
9271
+ .get([indexConfig.indexId, this.uid])
9272
+ .next(indexState => {
9273
+ result.push(fromDbIndexConfiguration(indexConfig, indexState));
9274
+ });
9275
+ }).next(() => result);
9276
+ });
8938
9277
  }
8939
9278
  getNextCollectionGroupToUpdate(transaction) {
8940
- // TODO(indexing): Implement
8941
- return PersistencePromise.resolve(null);
9279
+ return this.getFieldIndexes(transaction).next(indexes => {
9280
+ if (indexes.length === 0) {
9281
+ return null;
9282
+ }
9283
+ indexes.sort((l, r) => l.indexState.sequenceNumber - r.indexState.sequenceNumber);
9284
+ return indexes[0].collectionGroup;
9285
+ });
8942
9286
  }
8943
9287
  updateCollectionGroup(transaction, collectionGroup, offset) {
8944
- // TODO(indexing): Implement
8945
- return PersistencePromise.resolve();
9288
+ const indexes = indexConfigurationStore(transaction);
9289
+ const states = indexStateStore(transaction);
9290
+ return this.getNextSequenceNumber(transaction).next(nextSequenceNumber => indexes
9291
+ .loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
9292
+ .next(configs => PersistencePromise.forEach(configs, (config) => states.put(toDbIndexState(config.indexId, this.user, nextSequenceNumber, offset)))));
8946
9293
  }
8947
9294
  updateIndexEntries(transaction, documents) {
8948
9295
  // TODO(indexing): Implement
8949
9296
  return PersistencePromise.resolve();
8950
9297
  }
9298
+ getNextSequenceNumber(transaction) {
9299
+ let nextSequenceNumber = 1;
9300
+ const states = indexStateStore(transaction);
9301
+ return states
9302
+ .iterate({
9303
+ index: DbIndexState.sequenceNumberIndex,
9304
+ reverse: true,
9305
+ range: IDBKeyRange.upperBound([this.uid, Number.MAX_SAFE_INTEGER])
9306
+ }, (_, state, controller) => {
9307
+ controller.done();
9308
+ nextSequenceNumber = state.sequenceNumber + 1;
9309
+ })
9310
+ .next(() => nextSequenceNumber);
9311
+ }
8951
9312
  }
8952
9313
  /**
8953
9314
  * Helper to get a typed SimpleDbStore for the collectionParents
@@ -8955,6 +9316,24 @@ class IndexedDbIndexManager {
8955
9316
  */
8956
9317
  function collectionParentsStore(txn) {
8957
9318
  return getStore(txn, DbCollectionParent.store);
9319
+ }
9320
+ /**
9321
+ * Helper to get a typed SimpleDbStore for the index entry object store.
9322
+ */
9323
+ function indexEntriesStore(txn) {
9324
+ return getStore(txn, DbIndexEntry.store);
9325
+ }
9326
+ /**
9327
+ * Helper to get a typed SimpleDbStore for the index configuration object store.
9328
+ */
9329
+ function indexConfigurationStore(txn) {
9330
+ return getStore(txn, DbIndexConfiguration.store);
9331
+ }
9332
+ /**
9333
+ * Helper to get a typed SimpleDbStore for the index state object store.
9334
+ */
9335
+ function indexStateStore(txn) {
9336
+ return getStore(txn, DbIndexState.store);
8958
9337
  }
8959
9338
 
8960
9339
  /**
@@ -10424,12 +10803,10 @@ class RemoteDocumentChangeBuffer {
10424
10803
  * `newIndexedDbRemoteDocumentCache()`.
10425
10804
  */
10426
10805
  class IndexedDbRemoteDocumentCacheImpl {
10427
- /**
10428
- * @param serializer - The document serializer.
10429
- * @param indexManager - The query indexes that need to be maintained.
10430
- */
10431
- constructor(serializer, indexManager) {
10806
+ constructor(serializer) {
10432
10807
  this.serializer = serializer;
10808
+ }
10809
+ setIndexManager(indexManager) {
10433
10810
  this.indexManager = indexManager;
10434
10811
  }
10435
10812
  /**
@@ -10551,21 +10928,21 @@ class IndexedDbRemoteDocumentCacheImpl {
10551
10928
  }
10552
10929
  });
10553
10930
  }
10554
- getDocumentsMatchingQuery(transaction, query, sinceReadTime) {
10931
+ getAll(transaction, collection, sinceReadTime) {
10555
10932
  let results = mutableDocumentMap();
10556
- const immediateChildrenPathLength = query.path.length + 1;
10933
+ const immediateChildrenPathLength = collection.length + 1;
10557
10934
  const iterationOptions = {};
10558
10935
  if (sinceReadTime.isEqual(SnapshotVersion.min())) {
10559
10936
  // Documents are ordered by key, so we can use a prefix scan to narrow
10560
10937
  // down the documents we need to match the query against.
10561
- const startKey = query.path.toArray();
10938
+ const startKey = collection.toArray();
10562
10939
  iterationOptions.range = IDBKeyRange.lowerBound(startKey);
10563
10940
  }
10564
10941
  else {
10565
10942
  // Execute an index-free query and filter by read time. This is safe
10566
10943
  // since all document changes to queries that have a
10567
10944
  // lastLimboFreeSnapshotVersion (`sinceReadTime`) have a read time set.
10568
- const collectionKey = query.path.toArray();
10945
+ const collectionKey = collection.toArray();
10569
10946
  const readTimeKey = toDbTimestampKey(sinceReadTime);
10570
10947
  iterationOptions.range = IDBKeyRange.lowerBound([collectionKey, readTimeKey],
10571
10948
  /* open= */ true);
@@ -10582,12 +10959,12 @@ class IndexedDbRemoteDocumentCacheImpl {
10582
10959
  return;
10583
10960
  }
10584
10961
  const document = this.maybeDecodeDocument(DocumentKey.fromSegments(key), dbRemoteDoc);
10585
- if (!query.path.isPrefixOf(document.key.path)) {
10586
- control.done();
10587
- }
10588
- else if (queryMatches(query, document)) {
10962
+ if (collection.isPrefixOf(document.key.path)) {
10589
10963
  results = results.insert(document.key, document);
10590
10964
  }
10965
+ else {
10966
+ control.done();
10967
+ }
10591
10968
  })
10592
10969
  .next(() => results);
10593
10970
  }
@@ -10625,14 +11002,9 @@ class IndexedDbRemoteDocumentCacheImpl {
10625
11002
  return MutableDocument.newInvalidDocument(documentKey);
10626
11003
  }
10627
11004
  }
10628
- /**
10629
- * Creates a new IndexedDbRemoteDocumentCache.
10630
- *
10631
- * @param serializer - The document serializer.
10632
- * @param indexManager - The query indexes that need to be maintained.
10633
- */
10634
- function newIndexedDbRemoteDocumentCache(serializer, indexManager) {
10635
- return new IndexedDbRemoteDocumentCacheImpl(serializer, indexManager);
11005
+ /** Creates a new IndexedDbRemoteDocumentCache. */
11006
+ function newIndexedDbRemoteDocumentCache(serializer) {
11007
+ return new IndexedDbRemoteDocumentCacheImpl(serializer);
10636
11008
  }
10637
11009
  /**
10638
11010
  * Returns the set of documents that have changed since the specified read
@@ -10866,6 +11238,11 @@ class SchemaConverter {
10866
11238
  });
10867
11239
  }
10868
11240
  if (fromVersion < 12 && toVersion >= 12) {
11241
+ p = p.next(() => {
11242
+ createDocumentOverlayStore(db);
11243
+ });
11244
+ }
11245
+ if (fromVersion < 13 && toVersion >= 13) {
10869
11246
  p = p.next(() => {
10870
11247
  createFieldIndex(db);
10871
11248
  });
@@ -11078,15 +11455,25 @@ function createNamedQueriesStore(db) {
11078
11455
  });
11079
11456
  }
11080
11457
  function createFieldIndex(db) {
11081
- db.createObjectStore(DbIndexConfiguration.store, {
11082
- keyPath: DbIndexConfiguration.keyPath
11458
+ const indexConfiguratioStore = db.createObjectStore(DbIndexConfiguration.store, {
11459
+ keyPath: DbIndexConfiguration.keyPath,
11460
+ autoIncrement: true
11083
11461
  });
11084
- db.createObjectStore(DbIndexState.store, {
11462
+ indexConfiguratioStore.createIndex(DbIndexConfiguration.collectionGroupIndex, DbIndexConfiguration.collectionGroupIndexPath, { unique: false });
11463
+ const indexStateStore = db.createObjectStore(DbIndexState.store, {
11085
11464
  keyPath: DbIndexState.keyPath
11086
11465
  });
11087
- db.createObjectStore(DbIndexEntries.store, {
11088
- keyPath: DbIndexEntries.keyPath
11466
+ indexStateStore.createIndex(DbIndexState.sequenceNumberIndex, DbIndexState.sequenceNumberIndexPath, { unique: false });
11467
+ db.createObjectStore(DbIndexEntry.store, {
11468
+ keyPath: DbIndexEntry.keyPath
11469
+ });
11470
+ }
11471
+ function createDocumentOverlayStore(db) {
11472
+ const documentOverlayStore = db.createObjectStore(DbDocumentOverlay.store, {
11473
+ keyPath: DbDocumentOverlay.keyPath
11089
11474
  });
11475
+ documentOverlayStore.createIndex(DbDocumentOverlay.collectionPathOverlayIndex, DbDocumentOverlay.collectionPathOverlayIndexPath, { unique: false });
11476
+ documentOverlayStore.createIndex(DbDocumentOverlay.collectionGroupOverlayIndex, DbDocumentOverlay.collectionGroupOverlayIndexPath, { unique: false });
11090
11477
  }
11091
11478
 
11092
11479
  /**
@@ -11198,7 +11585,7 @@ class IndexedDbPersistence {
11198
11585
  * If set to true, forcefully obtains database access. Existing tabs will
11199
11586
  * no longer be able to access IndexedDB.
11200
11587
  */
11201
- forceOwningTab) {
11588
+ forceOwningTab, schemaVersion = SCHEMA_VERSION) {
11202
11589
  this.allowTabSynchronization = allowTabSynchronization;
11203
11590
  this.persistenceKey = persistenceKey;
11204
11591
  this.clientId = clientId;
@@ -11207,6 +11594,7 @@ class IndexedDbPersistence {
11207
11594
  this.document = document;
11208
11595
  this.sequenceNumberSyncer = sequenceNumberSyncer;
11209
11596
  this.forceOwningTab = forceOwningTab;
11597
+ this.schemaVersion = schemaVersion;
11210
11598
  this.listenSequence = null;
11211
11599
  this._started = false;
11212
11600
  this.isPrimary = false;
@@ -11228,10 +11616,9 @@ class IndexedDbPersistence {
11228
11616
  this.referenceDelegate = new IndexedDbLruDelegateImpl(this, lruParams);
11229
11617
  this.dbName = persistenceKey + MAIN_DATABASE;
11230
11618
  this.serializer = new LocalSerializer(serializer);
11231
- this.simpleDb = new SimpleDb(this.dbName, SCHEMA_VERSION, new SchemaConverter(this.serializer));
11619
+ this.simpleDb = new SimpleDb(this.dbName, this.schemaVersion, new SchemaConverter(this.serializer));
11232
11620
  this.targetCache = new IndexedDbTargetCache(this.referenceDelegate, this.serializer);
11233
- this.indexManager = new IndexedDbIndexManager();
11234
- this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer, this.indexManager);
11621
+ this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer);
11235
11622
  this.bundleCache = new IndexedDbBundleCache();
11236
11623
  if (this.window && this.window.localStorage) {
11237
11624
  this.webStorage = this.window.localStorage;
@@ -11567,8 +11954,8 @@ class IndexedDbPersistence {
11567
11954
  get started() {
11568
11955
  return this._started;
11569
11956
  }
11570
- getMutationQueue(user) {
11571
- return IndexedDbMutationQueue.forUser(user, this.serializer, this.indexManager, this.referenceDelegate);
11957
+ getMutationQueue(user, indexManager) {
11958
+ return IndexedDbMutationQueue.forUser(user, this.serializer, indexManager, this.referenceDelegate);
11572
11959
  }
11573
11960
  getTargetCache() {
11574
11961
  return this.targetCache;
@@ -11576,8 +11963,11 @@ class IndexedDbPersistence {
11576
11963
  getRemoteDocumentCache() {
11577
11964
  return this.remoteDocumentCache;
11578
11965
  }
11579
- getIndexManager() {
11580
- return this.indexManager;
11966
+ getIndexManager(user) {
11967
+ return new IndexedDbIndexManager(user);
11968
+ }
11969
+ getDocumentOverlayCache(user) {
11970
+ return IndexedDbDocumentOverlayCache.forUser(this.serializer, user);
11581
11971
  }
11582
11972
  getBundleCache() {
11583
11973
  return this.bundleCache;
@@ -11585,11 +11975,12 @@ class IndexedDbPersistence {
11585
11975
  runTransaction(action, mode, transactionOperation) {
11586
11976
  logDebug(LOG_TAG$d, 'Starting transaction:', action);
11587
11977
  const simpleDbMode = mode === 'readonly' ? 'readonly' : 'readwrite';
11978
+ const objectStores = getObjectStores(this.schemaVersion);
11588
11979
  let persistenceTransaction;
11589
11980
  // Do all transactions as readwrite against all object stores, since we
11590
11981
  // are the only reader/writer.
11591
11982
  return this.simpleDb
11592
- .runTransaction(action, simpleDbMode, ALL_STORES, simpleDbTxn => {
11983
+ .runTransaction(action, simpleDbMode, objectStores, simpleDbTxn => {
11593
11984
  persistenceTransaction = new IndexedDbTransaction(simpleDbTxn, this.listenSequence
11594
11985
  ? this.listenSequence.next()
11595
11986
  : ListenSequence.INVALID);
@@ -11961,39 +12352,28 @@ class LocalDocumentsView {
11961
12352
  getDocumentsMatchingCollectionQuery(transaction, query, sinceReadTime) {
11962
12353
  // Query the remote documents and overlay mutations.
11963
12354
  let results;
11964
- let mutationBatches;
11965
12355
  return this.remoteDocumentCache
11966
- .getDocumentsMatchingQuery(transaction, query, sinceReadTime)
12356
+ .getAll(transaction, query.path, sinceReadTime)
11967
12357
  .next(queryResults => {
11968
12358
  results = queryResults;
11969
12359
  return this.mutationQueue.getAllMutationBatchesAffectingQuery(transaction, query);
11970
12360
  })
11971
- .next(matchingMutationBatches => {
11972
- mutationBatches = matchingMutationBatches;
11973
- // It is possible that a PatchMutation can make a document match a query, even if
11974
- // the version in the RemoteDocumentCache is not a match yet (waiting for server
11975
- // to ack). To handle this, we find all document keys affected by the PatchMutations
11976
- // that are not in `result` yet, and back fill them via `remoteDocumentCache.getEntries`,
11977
- // otherwise those `PatchMutations` will be ignored because no base document can be found,
11978
- // and lead to missing result for the query.
11979
- return this.addMissingBaseDocuments(transaction, mutationBatches, results).next(mergedDocuments => {
11980
- results = mergedDocuments;
11981
- for (const batch of mutationBatches) {
11982
- for (const mutation of batch.mutations) {
11983
- const key = mutation.key;
11984
- let document = results.get(key);
11985
- if (document == null) {
11986
- // Create invalid document to apply mutations on top of
11987
- document = MutableDocument.newInvalidDocument(key);
11988
- results = results.insert(key, document);
11989
- }
11990
- mutationApplyToLocalView(mutation, document, batch.localWriteTime);
11991
- if (!document.isFoundDocument()) {
11992
- results = results.remove(key);
11993
- }
12361
+ .next(mutationBatches => {
12362
+ for (const batch of mutationBatches) {
12363
+ for (const mutation of batch.mutations) {
12364
+ const key = mutation.key;
12365
+ let document = results.get(key);
12366
+ if (document == null) {
12367
+ // Create invalid document to apply mutations on top of
12368
+ document = MutableDocument.newInvalidDocument(key);
12369
+ results = results.insert(key, document);
12370
+ }
12371
+ mutationApplyToLocalView(mutation, document, batch.localWriteTime);
12372
+ if (!document.isFoundDocument()) {
12373
+ results = results.remove(key);
11994
12374
  }
11995
12375
  }
11996
- });
12376
+ }
11997
12377
  })
11998
12378
  .next(() => {
11999
12379
  // Finally, filter out any documents that don't actually match
@@ -12006,29 +12386,6 @@ class LocalDocumentsView {
12006
12386
  return results;
12007
12387
  });
12008
12388
  }
12009
- addMissingBaseDocuments(transaction, matchingMutationBatches, existingDocuments) {
12010
- let missingBaseDocEntriesForPatching = documentKeySet();
12011
- for (const batch of matchingMutationBatches) {
12012
- for (const mutation of batch.mutations) {
12013
- if (mutation instanceof PatchMutation &&
12014
- existingDocuments.get(mutation.key) === null) {
12015
- missingBaseDocEntriesForPatching =
12016
- missingBaseDocEntriesForPatching.add(mutation.key);
12017
- }
12018
- }
12019
- }
12020
- let mergedDocuments = existingDocuments;
12021
- return this.remoteDocumentCache
12022
- .getEntries(transaction, missingBaseDocEntriesForPatching)
12023
- .next(missingBaseDocs => {
12024
- missingBaseDocs.forEach((key, doc) => {
12025
- if (doc.isFoundDocument()) {
12026
- mergedDocuments = mergedDocuments.insert(key, doc);
12027
- }
12028
- });
12029
- return mergedDocuments;
12030
- });
12031
- }
12032
12389
  }
12033
12390
 
12034
12391
  /**
@@ -12087,11 +12444,18 @@ class LocalStoreImpl {
12087
12444
  * PORTING NOTE: This is only used for multi-tab synchronization.
12088
12445
  */
12089
12446
  this.lastDocumentChangeReadTime = SnapshotVersion.min();
12090
- this.mutationQueue = persistence.getMutationQueue(initialUser);
12091
12447
  this.remoteDocuments = persistence.getRemoteDocumentCache();
12092
12448
  this.targetCache = persistence.getTargetCache();
12093
- this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.persistence.getIndexManager());
12094
12449
  this.bundleCache = persistence.getBundleCache();
12450
+ this.initializeUserComponents(initialUser);
12451
+ }
12452
+ initializeUserComponents(user) {
12453
+ // TODO(indexing): Add spec tests that test these components change after a
12454
+ // user change
12455
+ this.indexManager = this.persistence.getIndexManager(user);
12456
+ this.mutationQueue = this.persistence.getMutationQueue(user, this.indexManager);
12457
+ this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.indexManager);
12458
+ this.remoteDocuments.setIndexManager(this.indexManager);
12095
12459
  this.queryEngine.setLocalDocumentsView(this.localDocuments);
12096
12460
  }
12097
12461
  collectGarbage(garbageCollector) {
@@ -12113,8 +12477,6 @@ persistence, queryEngine, initialUser, serializer) {
12113
12477
  // change.
12114
12478
  async function localStoreHandleUserChange(localStore, user) {
12115
12479
  const localStoreImpl = debugCast(localStore);
12116
- let newMutationQueue = localStoreImpl.mutationQueue;
12117
- let newLocalDocuments = localStoreImpl.localDocuments;
12118
12480
  const result = await localStoreImpl.persistence.runTransaction('Handle user change', 'readonly', txn => {
12119
12481
  // Swap out the mutation queue, grabbing the pending mutation batches
12120
12482
  // before and after.
@@ -12123,11 +12485,8 @@ async function localStoreHandleUserChange(localStore, user) {
12123
12485
  .getAllMutationBatches(txn)
12124
12486
  .next(promisedOldBatches => {
12125
12487
  oldBatches = promisedOldBatches;
12126
- newMutationQueue = localStoreImpl.persistence.getMutationQueue(user);
12127
- // Recreate our LocalDocumentsView using the new
12128
- // MutationQueue.
12129
- newLocalDocuments = new LocalDocumentsView(localStoreImpl.remoteDocuments, newMutationQueue, localStoreImpl.persistence.getIndexManager());
12130
- return newMutationQueue.getAllMutationBatches(txn);
12488
+ localStoreImpl.initializeUserComponents(user);
12489
+ return localStoreImpl.mutationQueue.getAllMutationBatches(txn);
12131
12490
  })
12132
12491
  .next(newBatches => {
12133
12492
  const removedBatchIds = [];
@@ -12148,7 +12507,7 @@ async function localStoreHandleUserChange(localStore, user) {
12148
12507
  }
12149
12508
  // Return the set of all (potentially) changed documents and the list
12150
12509
  // of mutation batch IDs that were affected by change.
12151
- return newLocalDocuments
12510
+ return localStoreImpl.localDocuments
12152
12511
  .getDocuments(txn, changedKeys)
12153
12512
  .next(affectedDocuments => {
12154
12513
  return {
@@ -12159,9 +12518,6 @@ async function localStoreHandleUserChange(localStore, user) {
12159
12518
  });
12160
12519
  });
12161
12520
  });
12162
- localStoreImpl.mutationQueue = newMutationQueue;
12163
- localStoreImpl.localDocuments = newLocalDocuments;
12164
- localStoreImpl.queryEngine.setLocalDocumentsView(localStoreImpl.localDocuments);
12165
12521
  return result;
12166
12522
  }
12167
12523
  /* Accepts locally generated Mutations and commit them to storage. */
@@ -12874,6 +13230,122 @@ class MemoryBundleCache {
12874
13230
  }
12875
13231
  }
12876
13232
 
13233
+ /**
13234
+ * @license
13235
+ * Copyright 2022 Google LLC
13236
+ *
13237
+ * Licensed under the Apache License, Version 2.0 (the "License");
13238
+ * you may not use this file except in compliance with the License.
13239
+ * You may obtain a copy of the License at
13240
+ *
13241
+ * http://www.apache.org/licenses/LICENSE-2.0
13242
+ *
13243
+ * Unless required by applicable law or agreed to in writing, software
13244
+ * distributed under the License is distributed on an "AS IS" BASIS,
13245
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13246
+ * See the License for the specific language governing permissions and
13247
+ * limitations under the License.
13248
+ */
13249
+ /**
13250
+ * An in-memory implementation of DocumentOverlayCache.
13251
+ */
13252
+ class MemoryDocumentOverlayCache {
13253
+ constructor() {
13254
+ // A map sorted by DocumentKey, whose value is a pair of the largest batch id
13255
+ // for the overlay and the overlay itself.
13256
+ this.overlays = new SortedMap(DocumentKey.comparator);
13257
+ this.overlayByBatchId = new Map();
13258
+ }
13259
+ getOverlay(transaction, key) {
13260
+ return PersistencePromise.resolve(this.overlays.get(key));
13261
+ }
13262
+ saveOverlays(transaction, largestBatchId, overlays) {
13263
+ overlays.forEach(mutation => {
13264
+ this.saveOverlay(transaction, largestBatchId, mutation);
13265
+ });
13266
+ return PersistencePromise.resolve();
13267
+ }
13268
+ removeOverlaysForBatchId(transaction, documentKeys, batchId) {
13269
+ const keys = this.overlayByBatchId.get(batchId);
13270
+ if (keys !== undefined) {
13271
+ keys.forEach(key => (this.overlays = this.overlays.remove(key)));
13272
+ this.overlayByBatchId.delete(batchId);
13273
+ }
13274
+ return PersistencePromise.resolve();
13275
+ }
13276
+ getOverlaysForCollection(transaction, collection, sinceBatchId) {
13277
+ const result = new Map();
13278
+ const immediateChildrenPathLength = collection.length + 1;
13279
+ const prefix = new DocumentKey(collection.child(''));
13280
+ const iter = this.overlays.getIteratorFrom(prefix);
13281
+ while (iter.hasNext()) {
13282
+ const entry = iter.getNext();
13283
+ const overlay = entry.value;
13284
+ const key = overlay.getKey();
13285
+ if (!collection.isPrefixOf(key.path)) {
13286
+ break;
13287
+ }
13288
+ // Documents from sub-collections
13289
+ if (key.path.length !== immediateChildrenPathLength) {
13290
+ continue;
13291
+ }
13292
+ if (overlay.largestBatchId > sinceBatchId) {
13293
+ result.set(overlay.getKey(), overlay);
13294
+ }
13295
+ }
13296
+ return PersistencePromise.resolve(result);
13297
+ }
13298
+ getOverlaysForCollectionGroup(transaction, collectionGroup, sinceBatchId, count) {
13299
+ let batchIdToOverlays = new SortedMap((key1, key2) => key1 - key2);
13300
+ const iter = this.overlays.getIterator();
13301
+ while (iter.hasNext()) {
13302
+ const entry = iter.getNext();
13303
+ const overlay = entry.value;
13304
+ const key = overlay.getKey();
13305
+ if (key.getCollectionGroup() !== collectionGroup) {
13306
+ continue;
13307
+ }
13308
+ if (overlay.largestBatchId > sinceBatchId) {
13309
+ let overlaysForBatchId = batchIdToOverlays.get(overlay.largestBatchId);
13310
+ if (overlaysForBatchId === null) {
13311
+ overlaysForBatchId = new Map();
13312
+ batchIdToOverlays = batchIdToOverlays.insert(overlay.largestBatchId, overlaysForBatchId);
13313
+ }
13314
+ overlaysForBatchId.set(overlay.getKey(), overlay);
13315
+ }
13316
+ }
13317
+ const result = new Map();
13318
+ const batchIter = batchIdToOverlays.getIterator();
13319
+ while (batchIter.hasNext()) {
13320
+ const entry = batchIter.getNext();
13321
+ const overlays = entry.value;
13322
+ overlays.forEach((overlay, key) => result.set(key, overlay));
13323
+ if (result.size >= count) {
13324
+ break;
13325
+ }
13326
+ }
13327
+ return PersistencePromise.resolve(result);
13328
+ }
13329
+ saveOverlay(transaction, largestBatchId, mutation) {
13330
+ if (mutation === null) {
13331
+ return;
13332
+ }
13333
+ // Remove the association of the overlay to its batch id.
13334
+ const existing = this.overlays.get(mutation.key);
13335
+ if (existing !== null) {
13336
+ this.overlayByBatchId.get(existing.largestBatchId).delete(mutation.key);
13337
+ }
13338
+ this.overlays = this.overlays.insert(mutation.key, new Overlay(largestBatchId, mutation));
13339
+ // Create the association of this overlay to the given largestBatchId.
13340
+ let batch = this.overlayByBatchId.get(largestBatchId);
13341
+ if (batch === undefined) {
13342
+ batch = new Set();
13343
+ this.overlayByBatchId.set(largestBatchId, batch);
13344
+ }
13345
+ batch.add(mutation.key);
13346
+ }
13347
+ }
13348
+
12877
13349
  /**
12878
13350
  * @license
12879
13351
  * Copyright 2017 Google LLC
@@ -13227,14 +13699,16 @@ class MemoryRemoteDocumentCacheImpl {
13227
13699
  * expected to just return 0 to avoid unnecessarily doing the work of
13228
13700
  * calculating the size.
13229
13701
  */
13230
- constructor(indexManager, sizer) {
13231
- this.indexManager = indexManager;
13702
+ constructor(sizer) {
13232
13703
  this.sizer = sizer;
13233
13704
  /** Underlying cache of documents and their read times. */
13234
13705
  this.docs = documentEntryMap();
13235
13706
  /** Size of all cached documents. */
13236
13707
  this.size = 0;
13237
13708
  }
13709
+ setIndexManager(indexManager) {
13710
+ this.indexManager = indexManager;
13711
+ }
13238
13712
  /**
13239
13713
  * Adds the supplied entry to the cache and updates the cache size as appropriate.
13240
13714
  *
@@ -13282,21 +13756,22 @@ class MemoryRemoteDocumentCacheImpl {
13282
13756
  });
13283
13757
  return PersistencePromise.resolve(results);
13284
13758
  }
13285
- getDocumentsMatchingQuery(transaction, query, sinceReadTime) {
13759
+ getAll(transaction, collectionPath, sinceReadTime) {
13286
13760
  let results = mutableDocumentMap();
13287
13761
  // Documents are ordered by key, so we can use a prefix scan to narrow down
13288
13762
  // the documents we need to match the query against.
13289
- const prefix = new DocumentKey(query.path.child(''));
13763
+ const prefix = new DocumentKey(collectionPath.child(''));
13290
13764
  const iterator = this.docs.getIteratorFrom(prefix);
13291
13765
  while (iterator.hasNext()) {
13292
13766
  const { key, value: { document } } = iterator.getNext();
13293
- if (!query.path.isPrefixOf(key.path)) {
13767
+ if (!collectionPath.isPrefixOf(key.path)) {
13294
13768
  break;
13295
13769
  }
13296
- if (document.readTime.compareTo(sinceReadTime) <= 0) {
13770
+ if (key.path.length > collectionPath.length + 1) {
13771
+ // Exclude entries from subcollections.
13297
13772
  continue;
13298
13773
  }
13299
- if (!queryMatches(query, document)) {
13774
+ if (document.readTime.compareTo(sinceReadTime) <= 0) {
13300
13775
  continue;
13301
13776
  }
13302
13777
  results = results.insert(document.key, document.mutableCopy());
@@ -13318,13 +13793,12 @@ class MemoryRemoteDocumentCacheImpl {
13318
13793
  /**
13319
13794
  * Creates a new memory-only RemoteDocumentCache.
13320
13795
  *
13321
- * @param indexManager - A class that manages collection group indices.
13322
13796
  * @param sizer - Used to assess the size of a document. For eager GC, this is
13323
13797
  * expected to just return 0 to avoid unnecessarily doing the work of
13324
13798
  * calculating the size.
13325
13799
  */
13326
- function newMemoryRemoteDocumentCache(indexManager, sizer) {
13327
- return new MemoryRemoteDocumentCacheImpl(indexManager, sizer);
13800
+ function newMemoryRemoteDocumentCache(sizer) {
13801
+ return new MemoryRemoteDocumentCacheImpl(sizer);
13328
13802
  }
13329
13803
  /**
13330
13804
  * Handles the details of adding and updating documents in the MemoryRemoteDocumentCache.
@@ -13518,6 +13992,7 @@ class MemoryPersistence {
13518
13992
  */
13519
13993
  constructor(referenceDelegateFactory, serializer) {
13520
13994
  this.mutationQueues = {};
13995
+ this.overlays = {};
13521
13996
  this.listenSequence = new ListenSequence(0);
13522
13997
  this._started = false;
13523
13998
  this._started = true;
@@ -13525,7 +14000,7 @@ class MemoryPersistence {
13525
14000
  this.targetCache = new MemoryTargetCache(this);
13526
14001
  const sizer = (doc) => this.referenceDelegate.documentSize(doc);
13527
14002
  this.indexManager = new MemoryIndexManager();
13528
- this.remoteDocumentCache = newMemoryRemoteDocumentCache(this.indexManager, sizer);
14003
+ this.remoteDocumentCache = newMemoryRemoteDocumentCache(sizer);
13529
14004
  this.serializer = new LocalSerializer(serializer);
13530
14005
  this.bundleCache = new MemoryBundleCache(this.serializer);
13531
14006
  }
@@ -13546,13 +14021,23 @@ class MemoryPersistence {
13546
14021
  setNetworkEnabled() {
13547
14022
  // No op.
13548
14023
  }
13549
- getIndexManager() {
14024
+ getIndexManager(user) {
14025
+ // We do not currently support indices for memory persistence, so we can
14026
+ // return the same shared instance of the memory index manager.
13550
14027
  return this.indexManager;
13551
14028
  }
13552
- getMutationQueue(user) {
14029
+ getDocumentOverlayCache(user) {
14030
+ let overlay = this.overlays[user.toKey()];
14031
+ if (!overlay) {
14032
+ overlay = new MemoryDocumentOverlayCache();
14033
+ this.overlays[user.toKey()] = overlay;
14034
+ }
14035
+ return overlay;
14036
+ }
14037
+ getMutationQueue(user, indexManager) {
13553
14038
  let queue = this.mutationQueues[user.toKey()];
13554
14039
  if (!queue) {
13555
- queue = new MemoryMutationQueue(this.indexManager, this.referenceDelegate);
14040
+ queue = new MemoryMutationQueue(indexManager, this.referenceDelegate);
13556
14041
  this.mutationQueues[user.toKey()] = queue;
13557
14042
  }
13558
14043
  return queue;
@@ -20515,12 +21000,12 @@ async function syncEngineListen(syncEngine, query) {
20515
21000
  }
20516
21001
  else {
20517
21002
  const targetData = await localStoreAllocateTarget(syncEngineImpl.localStore, queryToTarget(query));
20518
- const status = syncEngineImpl.sharedClientState.addLocalQueryTarget(targetData.targetId);
20519
- targetId = targetData.targetId;
20520
- viewSnapshot = await initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId, status === 'current');
20521
21003
  if (syncEngineImpl.isPrimaryClient) {
20522
21004
  remoteStoreListen(syncEngineImpl.remoteStore, targetData);
20523
21005
  }
21006
+ const status = syncEngineImpl.sharedClientState.addLocalQueryTarget(targetData.targetId);
21007
+ targetId = targetData.targetId;
21008
+ viewSnapshot = await initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId, status === 'current');
20524
21009
  }
20525
21010
  return viewSnapshot;
20526
21011
  }