@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.
- package/CHANGELOG.md +12 -0
- package/dist/firestore/src/api/credentials.d.ts +1 -0
- package/dist/firestore/src/index/index_byte_encoder.d.ts +18 -1
- package/dist/firestore/src/index/index_entry.d.ts +26 -0
- package/dist/firestore/src/local/document_overlay_cache.d.ts +72 -0
- package/dist/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
- package/dist/firestore/src/local/indexeddb_index_manager.d.ts +29 -2
- package/dist/firestore/src/local/indexeddb_persistence.d.ts +6 -4
- package/dist/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
- package/dist/firestore/src/local/indexeddb_schema.d.ts +83 -9
- package/dist/firestore/src/local/local_documents_view.d.ts +0 -1
- package/dist/firestore/src/local/local_serializer.d.ts +18 -2
- package/dist/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
- package/dist/firestore/src/local/memory_persistence.d.ts +6 -2
- package/dist/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
- package/dist/firestore/src/local/persistence.d.ts +8 -2
- package/dist/firestore/src/local/remote_document_cache.d.ts +7 -9
- package/dist/firestore/src/model/document_key.d.ts +5 -0
- package/dist/firestore/src/model/field_index.d.ts +2 -2
- package/dist/firestore/src/model/overlay.d.ts +32 -0
- package/dist/firestore/src/model/target_index_matcher.d.ts +70 -0
- package/dist/firestore/src/platform/node/grpc_connection.d.ts +2 -2
- package/dist/firestore/src/platform/node/load_protos.d.ts +2 -2
- package/dist/firestore/src/util/sorted_set.d.ts +18 -0
- package/dist/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
- package/dist/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
- package/dist/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
- package/dist/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
- package/dist/firestore/test/unit/local/test_index_manager.d.ts +11 -0
- package/dist/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
- package/dist/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
- package/dist/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
- package/dist/index.esm2017.js +4367 -3517
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm5.js +4226 -3437
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +1519 -242
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/index.node.mjs +1501 -245
- package/dist/index.node.mjs.map +1 -1
- package/dist/index.rn.js +4355 -3509
- package/dist/index.rn.js.map +1 -1
- package/dist/internal.d.ts +84 -12
- package/dist/lite/firestore/src/api/credentials.d.ts +1 -0
- package/dist/lite/firestore/src/index/index_byte_encoder.d.ts +18 -1
- package/dist/lite/firestore/src/index/index_entry.d.ts +26 -0
- package/dist/lite/firestore/src/local/document_overlay_cache.d.ts +72 -0
- package/dist/lite/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
- package/dist/lite/firestore/src/local/indexeddb_index_manager.d.ts +29 -2
- package/dist/lite/firestore/src/local/indexeddb_persistence.d.ts +6 -4
- package/dist/lite/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
- package/dist/lite/firestore/src/local/indexeddb_schema.d.ts +83 -9
- package/dist/lite/firestore/src/local/local_documents_view.d.ts +0 -1
- package/dist/lite/firestore/src/local/local_serializer.d.ts +18 -2
- package/dist/lite/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
- package/dist/lite/firestore/src/local/memory_persistence.d.ts +6 -2
- package/dist/lite/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
- package/dist/lite/firestore/src/local/persistence.d.ts +8 -2
- package/dist/lite/firestore/src/local/remote_document_cache.d.ts +7 -9
- package/dist/lite/firestore/src/model/document_key.d.ts +5 -0
- package/dist/lite/firestore/src/model/field_index.d.ts +2 -2
- package/dist/lite/firestore/src/model/overlay.d.ts +32 -0
- package/dist/lite/firestore/src/model/target_index_matcher.d.ts +70 -0
- package/dist/lite/firestore/src/platform/node/grpc_connection.d.ts +2 -2
- package/dist/lite/firestore/src/platform/node/load_protos.d.ts +2 -2
- package/dist/lite/firestore/src/util/sorted_set.d.ts +18 -0
- package/dist/lite/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
- package/dist/lite/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
- package/dist/lite/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
- package/dist/lite/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
- package/dist/lite/firestore/test/unit/local/test_index_manager.d.ts +11 -0
- package/dist/lite/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
- package/dist/lite/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
- package/dist/lite/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
- package/dist/lite/index.browser.esm2017.js +1121 -1112
- package/dist/lite/index.browser.esm2017.js.map +1 -1
- package/dist/lite/index.browser.esm5.js +60 -48
- package/dist/lite/index.browser.esm5.js.map +1 -1
- package/dist/lite/index.node.cjs.js +13 -2
- package/dist/lite/index.node.cjs.js.map +1 -1
- package/dist/lite/index.node.mjs +13 -2
- package/dist/lite/index.node.mjs.map +1 -1
- package/dist/lite/index.rn.esm2017.js +46 -37
- package/dist/lite/index.rn.esm2017.js.map +1 -1
- package/dist/lite/internal.d.ts +5 -0
- package/dist/lite/packages/firestore/dist/lite/index.browser.esm2017.d.ts +112 -112
- package/dist/lite/packages/firestore/src/api/credentials.d.ts +1 -0
- package/dist/lite/packages/firestore/src/index/index_byte_encoder.d.ts +18 -1
- package/dist/lite/packages/firestore/src/index/index_entry.d.ts +26 -0
- package/dist/lite/packages/firestore/src/local/document_overlay_cache.d.ts +72 -0
- package/dist/lite/packages/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
- package/dist/lite/packages/firestore/src/local/indexeddb_index_manager.d.ts +29 -2
- package/dist/lite/packages/firestore/src/local/indexeddb_persistence.d.ts +6 -4
- package/dist/lite/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
- package/dist/lite/packages/firestore/src/local/indexeddb_schema.d.ts +83 -9
- package/dist/lite/packages/firestore/src/local/local_documents_view.d.ts +0 -1
- package/dist/lite/packages/firestore/src/local/local_serializer.d.ts +18 -2
- package/dist/lite/packages/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
- package/dist/lite/packages/firestore/src/local/memory_persistence.d.ts +6 -2
- package/dist/lite/packages/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
- package/dist/lite/packages/firestore/src/local/persistence.d.ts +8 -2
- package/dist/lite/packages/firestore/src/local/remote_document_cache.d.ts +7 -9
- package/dist/lite/packages/firestore/src/model/document_key.d.ts +5 -0
- package/dist/lite/packages/firestore/src/model/field_index.d.ts +2 -2
- package/dist/lite/packages/firestore/src/model/overlay.d.ts +32 -0
- package/dist/lite/packages/firestore/src/model/target_index_matcher.d.ts +70 -0
- package/dist/lite/packages/firestore/src/platform/node/grpc_connection.d.ts +2 -2
- package/dist/lite/packages/firestore/src/platform/node/load_protos.d.ts +2 -2
- package/dist/lite/packages/firestore/src/util/sorted_set.d.ts +18 -0
- package/dist/lite/packages/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
- package/dist/lite/packages/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
- package/dist/lite/packages/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
- package/dist/lite/packages/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
- package/dist/lite/packages/firestore/test/unit/local/test_index_manager.d.ts +11 -0
- package/dist/lite/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
- package/dist/lite/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
- package/dist/lite/packages/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
- package/dist/packages/firestore/dist/index.esm2017.d.ts +187 -184
- package/dist/packages/firestore/src/api/credentials.d.ts +1 -0
- package/dist/packages/firestore/src/index/index_byte_encoder.d.ts +18 -1
- package/dist/packages/firestore/src/index/index_entry.d.ts +26 -0
- package/dist/packages/firestore/src/local/document_overlay_cache.d.ts +72 -0
- package/dist/packages/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
- package/dist/packages/firestore/src/local/indexeddb_index_manager.d.ts +29 -2
- package/dist/packages/firestore/src/local/indexeddb_persistence.d.ts +6 -4
- package/dist/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
- package/dist/packages/firestore/src/local/indexeddb_schema.d.ts +83 -9
- package/dist/packages/firestore/src/local/local_documents_view.d.ts +0 -1
- package/dist/packages/firestore/src/local/local_serializer.d.ts +18 -2
- package/dist/packages/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
- package/dist/packages/firestore/src/local/memory_persistence.d.ts +6 -2
- package/dist/packages/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
- package/dist/packages/firestore/src/local/persistence.d.ts +8 -2
- package/dist/packages/firestore/src/local/remote_document_cache.d.ts +7 -9
- package/dist/packages/firestore/src/model/document_key.d.ts +5 -0
- package/dist/packages/firestore/src/model/field_index.d.ts +2 -2
- package/dist/packages/firestore/src/model/overlay.d.ts +32 -0
- package/dist/packages/firestore/src/model/target_index_matcher.d.ts +70 -0
- package/dist/packages/firestore/src/platform/node/grpc_connection.d.ts +2 -2
- package/dist/packages/firestore/src/platform/node/load_protos.d.ts +2 -2
- package/dist/packages/firestore/src/util/sorted_set.d.ts +18 -0
- package/dist/packages/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
- package/dist/packages/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
- package/dist/packages/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
- package/dist/packages/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
- package/dist/packages/firestore/test/unit/local/test_index_manager.d.ts +11 -0
- package/dist/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
- package/dist/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
- package/dist/packages/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
- package/dist/private.d.ts +79 -12
- package/package.json +9 -9
package/dist/index.node.mjs
CHANGED
|
@@ -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
|
|
8
|
-
import
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
|
1220
|
+
* 12. Add document overlays.
|
|
1221
|
+
* 13. Add indexing support.
|
|
1214
1222
|
*/
|
|
1215
|
-
const SCHEMA_VERSION =
|
|
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
|
-
/**
|
|
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
|
|
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
|
-
|
|
1854
|
-
|
|
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
|
-
|
|
1957
|
+
const V12_STORES = [...V11_STORES, DbDocumentOverlay.store];
|
|
1958
|
+
const V13_STORES = [
|
|
1959
|
+
...V12_STORES,
|
|
1886
1960
|
DbIndexConfiguration.store,
|
|
1887
1961
|
DbIndexState.store,
|
|
1888
|
-
|
|
1962
|
+
DbIndexEntry.store
|
|
1889
1963
|
];
|
|
1890
|
-
/**
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
return
|
|
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$
|
|
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
|
-
|
|
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.
|
|
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$
|
|
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$
|
|
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
|
|
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
|
-
*
|
|
9042
|
+
* Implementation of DocumentOverlayCache using IndexedDb.
|
|
8776
9043
|
*/
|
|
8777
|
-
class
|
|
8778
|
-
|
|
8779
|
-
|
|
8780
|
-
|
|
8781
|
-
|
|
8782
|
-
|
|
8783
|
-
|
|
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
|
-
|
|
8793
|
-
|
|
8794
|
-
return
|
|
9053
|
+
static forUser(serializer, user) {
|
|
9054
|
+
const userId = user.uid || '';
|
|
9055
|
+
return new IndexedDbDocumentOverlayCache(serializer, userId);
|
|
8795
9056
|
}
|
|
8796
|
-
|
|
8797
|
-
|
|
8798
|
-
|
|
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
|
-
|
|
8801
|
-
|
|
8802
|
-
|
|
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
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
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
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
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
|
-
|
|
8813
|
-
|
|
8814
|
-
|
|
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
|
-
|
|
8817
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
8827
|
-
|
|
8828
|
-
|
|
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
|
-
|
|
8831
|
-
|
|
8832
|
-
|
|
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
|
-
|
|
8841
|
-
|
|
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
|
-
|
|
8847
|
-
const
|
|
8848
|
-
|
|
8849
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
*
|
|
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
|
-
|
|
8873
|
-
|
|
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
|
|
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):
|
|
8930
|
-
|
|
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
|
-
|
|
8934
|
-
|
|
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
|
-
|
|
8946
|
-
|
|
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
|
-
|
|
8950
|
-
|
|
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
|
-
|
|
8954
|
-
|
|
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
|
-
//
|
|
8958
|
-
|
|
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
|
-
|
|
11710
|
+
getAll(transaction, collection, sinceReadTime) {
|
|
10564
11711
|
let results = mutableDocumentMap();
|
|
10565
|
-
const immediateChildrenPathLength =
|
|
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 =
|
|
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 =
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
12241
|
+
indexConfiguratioStore.createIndex(DbIndexConfiguration.collectionGroupIndex, DbIndexConfiguration.collectionGroupIndexPath, { unique: false });
|
|
12242
|
+
const indexStateStore = db.createObjectStore(DbIndexState.store, {
|
|
11094
12243
|
keyPath: DbIndexState.keyPath
|
|
11095
12244
|
});
|
|
11096
|
-
|
|
11097
|
-
|
|
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,
|
|
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.
|
|
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,
|
|
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
|
|
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,
|
|
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
|
-
.
|
|
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(
|
|
11981
|
-
mutationBatches
|
|
11982
|
-
|
|
11983
|
-
|
|
11984
|
-
|
|
11985
|
-
|
|
11986
|
-
|
|
11987
|
-
|
|
11988
|
-
|
|
11989
|
-
|
|
11990
|
-
|
|
11991
|
-
|
|
11992
|
-
|
|
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
|
-
|
|
12136
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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 (!
|
|
14547
|
+
if (!collectionPath.isPrefixOf(key.path)) {
|
|
13303
14548
|
break;
|
|
13304
14549
|
}
|
|
13305
|
-
if (
|
|
14550
|
+
if (key.path.length > collectionPath.length + 1) {
|
|
14551
|
+
// Exclude entries from subcollections.
|
|
13306
14552
|
continue;
|
|
13307
14553
|
}
|
|
13308
|
-
if (
|
|
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(
|
|
13336
|
-
return new MemoryRemoteDocumentCacheImpl(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
|
14741
|
-
? credentials.createSsl()
|
|
14742
|
-
: credentials.createInsecure();
|
|
14743
|
-
this.cachedStub = new this.firestore.Firestore(this.databaseInfo.host, credentials
|
|
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
|
/**
|