diff --git a/packages/firestore/exp/dependencies.json b/packages/firestore/exp/dependencies.json index 31a485b2f68..e4050ab51b2 100644 --- a/packages/firestore/exp/dependencies.json +++ b/packages/firestore/exp/dependencies.json @@ -40,7 +40,7 @@ ], "variables": [] }, - "sizeInBytes": 18456 + "sizeInBytes": 18458 }, "CollectionReference": { "dependencies": { @@ -82,7 +82,7 @@ ], "variables": [] }, - "sizeInBytes": 18635 + "sizeInBytes": 18637 }, "DocumentReference": { "dependencies": { @@ -124,7 +124,7 @@ ], "variables": [] }, - "sizeInBytes": 20083 + "sizeInBytes": 20085 }, "DocumentSnapshot": { "dependencies": { @@ -208,7 +208,7 @@ ], "variables": [] }, - "sizeInBytes": 39147 + "sizeInBytes": 39149 }, "FieldPath": { "dependencies": { @@ -258,7 +258,7 @@ ], "variables": [] }, - "sizeInBytes": 23079 + "sizeInBytes": 23081 }, "FieldValue": { "dependencies": { @@ -297,7 +297,7 @@ ], "variables": [] }, - "sizeInBytes": 16709 + "sizeInBytes": 16711 }, "FirebaseFirestore": { "dependencies": { @@ -334,7 +334,7 @@ ], "variables": [] }, - "sizeInBytes": 16618 + "sizeInBytes": 16620 }, "GeoPoint": { "dependencies": { @@ -380,7 +380,7 @@ ], "variables": [] }, - "sizeInBytes": 19527 + "sizeInBytes": 19529 }, "Query": { "dependencies": { @@ -418,7 +418,7 @@ ], "variables": [] }, - "sizeInBytes": 16811 + "sizeInBytes": 16813 }, "QueryConstraint": { "dependencies": { @@ -456,7 +456,7 @@ ], "variables": [] }, - "sizeInBytes": 16624 + "sizeInBytes": 16626 }, "QueryDocumentSnapshot": { "dependencies": { @@ -540,7 +540,7 @@ ], "variables": [] }, - "sizeInBytes": 39157 + "sizeInBytes": 39159 }, "QuerySnapshot": { "dependencies": { @@ -628,7 +628,7 @@ ], "variables": [] }, - "sizeInBytes": 41788 + "sizeInBytes": 41790 }, "SnapshotMetadata": { "dependencies": { @@ -666,7 +666,7 @@ ], "variables": [] }, - "sizeInBytes": 16833 + "sizeInBytes": 16835 }, "Timestamp": { "dependencies": { @@ -704,7 +704,7 @@ ], "variables": [] }, - "sizeInBytes": 18219 + "sizeInBytes": 18221 }, "Transaction": { "dependencies": { @@ -845,7 +845,7 @@ ], "variables": [] }, - "sizeInBytes": 62687 + "sizeInBytes": 62689 }, "WriteBatch": { "dependencies": { @@ -974,14 +974,14 @@ ], "variables": [] }, - "sizeInBytes": 55275 + "sizeInBytes": 55277 }, "addDoc": { "dependencies": { "functions": [ "acknowledgeBatch", "addDoc", - "allocateTarget", + "addMutationCallback", "applyArrayRemoveTransformOperation", "applyArrayUnionTransformOperation", "applyDeleteMutationToLocalView", @@ -990,11 +990,12 @@ "applyMutationToLocalView", "applyMutationToRemoteDocument", "applyNumericIncrementTransformOperationToLocalView", + "applyOnlineStateChange", "applyPatchMutationToLocalView", "applyPatchMutationToRemoteDocument", - "applyRemoteEventToLocalCache", "applySetMutationToLocalView", "applySetMutationToRemoteDocument", + "applySuccessfulWrite", "applyTransformMutationToLocalView", "applyTransformMutationToRemoteDocument", "applyTransformOperationToLocalView", @@ -1024,7 +1025,6 @@ "coercedFieldValuesArray", "compareArrays", "compareBlobs", - "compareChangeType", "compareDocs", "compareDocumentsByField", "compareGeoPoints", @@ -1043,10 +1043,11 @@ "documentMap", "documentTargetMap", "documentVersionMap", + "emitNewSnapsAndNotifyLocalStore", "encodeBase64", "enqueueWrite", + "ensureWriteCallbacks", "errorMessage", - "executeQuery", "extractFieldMask", "extractLocalPathFromResourceName", "extractMutationBaseValue", @@ -1073,9 +1074,7 @@ "fullyQualifiedPrefixPath", "geoPointEquals", "getEncodedDatabaseId", - "getHighestUnacknowledgedBatchId", "getLastRemoteSnapshotVersion", - "getLocalTargetData", "getLocalWriteTime", "getLogLevel", "getMessageOrStack", @@ -1083,6 +1082,7 @@ "getOnlineComponentProvider", "getPostMutationVersion", "getSyncEngine", + "handleCredentialChange", "handleUserChange", "hardAssert", "ignoreIfPrimaryLeaseLoss", @@ -1152,6 +1152,7 @@ "patchObject", "preconditionIsValidForDocument", "primitiveComparator", + "processUserCallback", "queryEquals", "queryMatches", "queryMatchesBounds", @@ -1163,7 +1164,8 @@ "randomBytes", "registerFirestore", "rejectBatch", - "releaseTarget", + "rejectFailedWrite", + "rejectOutstandingPendingWritesCallbacks", "removeComponents", "removeComponents$1", "requireDocument", @@ -1171,13 +1173,13 @@ "serverTransformResults", "setOfflineComponentProvider", "setOnlineComponentProvider", - "shouldPersistTargetData", "snapshotChangesMap", "sortsBeforeDocument", "stringifyFilter", "stringifyOrderBy", "stringifyQuery", "stringifyTarget", + "syncEngineWrite", "targetEquals", "targetIdSet", "timestampEquals", @@ -1211,6 +1213,7 @@ "toVersion", "transformObject", "transformOperationEquals", + "triggerPendingWritesCallbacks", "tryGetCustomObjectType", "typeOrder", "uint8ArrayFromBinaryString", @@ -1228,7 +1231,6 @@ "wrapInUserErrorIfRecoverable" ], "classes": [ - "AddedLimboDocument", "ArrayRemoveTransformOperation", "ArrayUnionTransformOperation", "AsyncQueue", @@ -1248,11 +1250,9 @@ "DeleteMutation", "DocReference", "Document", - "DocumentChangeSet", "DocumentKey", "DocumentKeyReference", "DocumentReference", - "DocumentSet", "DocumentWatchChange", "EventManager", "ExistenceFilter", @@ -1271,7 +1271,6 @@ "JsonProtoSerializer", "LLRBEmptyNode", "LLRBNode", - "LimboResolution", "ListenSequence", "LocalClientState", "LocalDocumentsView", @@ -1314,12 +1313,10 @@ "Query", "QueryImpl", "QueryListenersInfo", - "QueryView", "ReferenceSet", "RemoteDocumentChangeBuffer", "RemoteEvent", "RemoteStore", - "RemovedLimboDocument", "ResourcePath", "SerializableFieldValue", "ServerTimestampTransform", @@ -1343,14 +1340,12 @@ "User", "UserDataReader", "VerifyMutation", - "View", - "ViewSnapshot", "WatchChangeAggregator", "WatchTargetChange" ], "variables": [] }, - "sizeInBytes": 213593 + "sizeInBytes": 190505 }, "arrayRemove": { "dependencies": { @@ -1437,7 +1432,7 @@ ], "variables": [] }, - "sizeInBytes": 35214 + "sizeInBytes": 35216 }, "arrayUnion": { "dependencies": { @@ -1524,7 +1519,7 @@ ], "variables": [] }, - "sizeInBytes": 35206 + "sizeInBytes": 35208 }, "clearIndexedDbPersistence": { "dependencies": { @@ -1573,7 +1568,7 @@ ], "variables": [] }, - "sizeInBytes": 30086 + "sizeInBytes": 30088 }, "collection": { "dependencies": { @@ -1628,7 +1623,7 @@ ], "variables": [] }, - "sizeInBytes": 24790 + "sizeInBytes": 24792 }, "collectionGroup": { "dependencies": { @@ -1679,13 +1674,13 @@ ], "variables": [] }, - "sizeInBytes": 23099 + "sizeInBytes": 23101 }, "deleteDoc": { "dependencies": { "functions": [ "acknowledgeBatch", - "allocateTarget", + "addMutationCallback", "applyArrayRemoveTransformOperation", "applyArrayUnionTransformOperation", "applyDeleteMutationToLocalView", @@ -1693,11 +1688,12 @@ "applyMutationToLocalView", "applyMutationToRemoteDocument", "applyNumericIncrementTransformOperationToLocalView", + "applyOnlineStateChange", "applyPatchMutationToLocalView", "applyPatchMutationToRemoteDocument", - "applyRemoteEventToLocalCache", "applySetMutationToLocalView", "applySetMutationToRemoteDocument", + "applySuccessfulWrite", "applyTransformMutationToLocalView", "applyTransformMutationToRemoteDocument", "applyTransformOperationToLocalView", @@ -1727,7 +1723,6 @@ "coercedFieldValuesArray", "compareArrays", "compareBlobs", - "compareChangeType", "compareDocs", "compareDocumentsByField", "compareGeoPoints", @@ -1745,9 +1740,10 @@ "documentMap", "documentTargetMap", "documentVersionMap", + "emitNewSnapsAndNotifyLocalStore", "encodeBase64", "enqueueWrite", - "executeQuery", + "ensureWriteCallbacks", "extractFieldMask", "extractLocalPathFromResourceName", "extractMutationBaseValue", @@ -1770,9 +1766,7 @@ "fullyQualifiedPrefixPath", "geoPointEquals", "getEncodedDatabaseId", - "getHighestUnacknowledgedBatchId", "getLastRemoteSnapshotVersion", - "getLocalTargetData", "getLocalWriteTime", "getLogLevel", "getMessageOrStack", @@ -1780,6 +1774,7 @@ "getOnlineComponentProvider", "getPostMutationVersion", "getSyncEngine", + "handleCredentialChange", "handleUserChange", "hardAssert", "ignoreIfPrimaryLeaseLoss", @@ -1817,7 +1812,6 @@ "newPersistentWatchStream", "newPersistentWriteStream", "newQueryComparator", - "newQueryForPath", "newSerializer", "newSyncEngine", "newTarget", @@ -1836,6 +1830,7 @@ "patchObject", "preconditionIsValidForDocument", "primitiveComparator", + "processUserCallback", "queryEquals", "queryMatches", "queryMatchesBounds", @@ -1847,7 +1842,8 @@ "randomBytes", "registerFirestore", "rejectBatch", - "releaseTarget", + "rejectFailedWrite", + "rejectOutstandingPendingWritesCallbacks", "removeComponents", "removeComponents$1", "requireDocument", @@ -1855,13 +1851,13 @@ "serverTransformResults", "setOfflineComponentProvider", "setOnlineComponentProvider", - "shouldPersistTargetData", "snapshotChangesMap", "sortsBeforeDocument", "stringifyFilter", "stringifyOrderBy", "stringifyQuery", "stringifyTarget", + "syncEngineWrite", "targetEquals", "targetIdSet", "timestampEquals", @@ -1894,6 +1890,7 @@ "toVersion", "transformObject", "transformOperationEquals", + "triggerPendingWritesCallbacks", "typeOrder", "uint8ArrayFromBinaryString", "valueCompare", @@ -1903,7 +1900,6 @@ "wrapInUserErrorIfRecoverable" ], "classes": [ - "AddedLimboDocument", "ArrayRemoveTransformOperation", "ArrayUnionTransformOperation", "AsyncQueue", @@ -1920,11 +1916,9 @@ "DeleteMutation", "DocReference", "Document", - "DocumentChangeSet", "DocumentKey", "DocumentKeyReference", "DocumentReference", - "DocumentSet", "DocumentWatchChange", "EventManager", "ExistenceFilter", @@ -1941,7 +1935,6 @@ "JsonProtoSerializer", "LLRBEmptyNode", "LLRBNode", - "LimboResolution", "ListenSequence", "LocalClientState", "LocalDocumentsView", @@ -1981,12 +1974,10 @@ "Precondition", "QueryImpl", "QueryListenersInfo", - "QueryView", "ReferenceSet", "RemoteDocumentChangeBuffer", "RemoteEvent", "RemoteStore", - "RemovedLimboDocument", "ResourcePath", "ServerTimestampTransform", "SetMutation", @@ -2008,14 +1999,12 @@ "UnknownDocument", "User", "VerifyMutation", - "View", - "ViewSnapshot", "WatchChangeAggregator", "WatchTargetChange" ], "variables": [] }, - "sizeInBytes": 196533 + "sizeInBytes": 173387 }, "deleteField": { "dependencies": { @@ -2057,13 +2046,11 @@ ], "variables": [] }, - "sizeInBytes": 17675 + "sizeInBytes": 17677 }, "disableNetwork": { "dependencies": { "functions": [ - "acknowledgeBatch", - "allocateTarget", "applyArrayRemoveTransformOperation", "applyArrayUnionTransformOperation", "applyDeleteMutationToLocalView", @@ -2071,16 +2058,15 @@ "applyMutationToLocalView", "applyMutationToRemoteDocument", "applyNumericIncrementTransformOperationToLocalView", + "applyOnlineStateChange", "applyPatchMutationToLocalView", "applyPatchMutationToRemoteDocument", - "applyRemoteEventToLocalCache", "applySetMutationToLocalView", "applySetMutationToRemoteDocument", "applyTransformMutationToLocalView", "applyTransformMutationToRemoteDocument", "applyTransformOperationToLocalView", "applyTransformOperationToRemoteDocument", - "applyWriteToRemoteDocuments", "argToString", "arrayEquals", "asNumber", @@ -2105,7 +2091,6 @@ "coercedFieldValuesArray", "compareArrays", "compareBlobs", - "compareChangeType", "compareDocs", "compareDocumentsByField", "compareGeoPoints", @@ -2123,13 +2108,10 @@ "documentMap", "documentTargetMap", "documentVersionMap", + "emitNewSnapsAndNotifyLocalStore", "encodeBase64", "enqueueNetworkEnabled", - "executeQuery", - "extractFieldMask", "extractLocalPathFromResourceName", - "extractMutationBaseValue", - "extractTransformMutationBaseValue", "fail", "fieldTransformEquals", "filterEquals", @@ -2148,9 +2130,7 @@ "fullyQualifiedPrefixPath", "geoPointEquals", "getEncodedDatabaseId", - "getHighestUnacknowledgedBatchId", "getLastRemoteSnapshotVersion", - "getLocalTargetData", "getLocalWriteTime", "getLogLevel", "getMessageOrStack", @@ -2159,9 +2139,9 @@ "getPersistence", "getPostMutationVersion", "getRemoteStore", + "handleCredentialChange", "handleUserChange", "hardAssert", - "ignoreIfPrimaryLeaseLoss", "isArray", "isCollectionGroupQuery", "isDocumentQuery", @@ -2182,7 +2162,6 @@ "isValidResourceName", "loadProtos", "localTransformResults", - "localWrite", "logDebug", "logError", "logWarn", @@ -2196,7 +2175,6 @@ "newPersistentWatchStream", "newPersistentWriteStream", "newQueryComparator", - "newQueryForPath", "newSerializer", "newSyncEngine", "newTarget", @@ -2225,8 +2203,7 @@ "queryToTarget", "randomBytes", "registerFirestore", - "rejectBatch", - "releaseTarget", + "rejectOutstandingPendingWritesCallbacks", "removeComponents", "removeComponents$1", "requireDocument", @@ -2234,7 +2211,6 @@ "serverTransformResults", "setOfflineComponentProvider", "setOnlineComponentProvider", - "shouldPersistTargetData", "snapshotChangesMap", "sortsBeforeDocument", "stringifyFilter", @@ -2282,7 +2258,6 @@ "wrapInUserErrorIfRecoverable" ], "classes": [ - "AddedLimboDocument", "ArrayRemoveTransformOperation", "ArrayUnionTransformOperation", "AsyncQueue", @@ -2299,15 +2274,12 @@ "DeleteMutation", "DocReference", "Document", - "DocumentChangeSet", "DocumentKey", - "DocumentSet", "DocumentWatchChange", "EventManager", "ExistenceFilter", "ExistenceFilterChange", "ExponentialBackoff", - "FieldMask", "FieldPath", "FirebaseCredentialsProvider", "Firestore", @@ -2318,7 +2290,6 @@ "JsonProtoSerializer", "LLRBEmptyNode", "LLRBNode", - "LimboResolution", "ListenSequence", "LocalClientState", "LocalDocumentsView", @@ -2358,12 +2329,10 @@ "Precondition", "QueryImpl", "QueryListenersInfo", - "QueryView", "ReferenceSet", "RemoteDocumentChangeBuffer", "RemoteEvent", "RemoteStore", - "RemovedLimboDocument", "ResourcePath", "ServerTimestampTransform", "SetMutation", @@ -2385,14 +2354,12 @@ "UnknownDocument", "User", "VerifyMutation", - "View", - "ViewSnapshot", "WatchChangeAggregator", "WatchTargetChange" ], "variables": [] }, - "sizeInBytes": 196151 + "sizeInBytes": 165460 }, "doc": { "dependencies": { @@ -2447,7 +2414,7 @@ ], "variables": [] }, - "sizeInBytes": 24841 + "sizeInBytes": 24843 }, "documentId": { "dependencies": { @@ -2498,7 +2465,7 @@ ], "variables": [] }, - "sizeInBytes": 23129 + "sizeInBytes": 23131 }, "enableIndexedDbPersistence": { "dependencies": { @@ -2866,7 +2833,7 @@ ], "variables": [] }, - "sizeInBytes": 200588 + "sizeInBytes": 200591 }, "enableMultiTabIndexedDbPersistence": { "dependencies": { @@ -2879,15 +2846,19 @@ "applyBatchState", "applyDeleteMutationToLocalView", "applyDeleteMutationToRemoteDocument", + "applyDocChanges", "applyMutationToLocalView", "applyMutationToRemoteDocument", "applyNumericIncrementTransformOperationToLocalView", + "applyOnlineStateChange", "applyPatchMutationToLocalView", "applyPatchMutationToRemoteDocument", "applyPrimaryState", + "applyRemoteEvent", "applyRemoteEventToLocalCache", "applySetMutationToLocalView", "applySetMutationToRemoteDocument", + "applySuccessfulWrite", "applyTargetState", "applyTransformMutationToLocalView", "applyTransformMutationToRemoteDocument", @@ -2960,17 +2931,17 @@ "documentVersionMap", "dropQueryCache", "dropRemoteDocumentChangesStore", + "emitNewSnapsAndNotifyLocalStore", "enableMultiTabIndexedDbPersistence", "encodeBase64", "encodeResourcePath", "encodeSegment", "encodeSeparator", + "ensureWatchCallbacks", + "ensureWriteCallbacks", "executeQuery", "extractDocumentKeysFromArrayValue", - "extractFieldMask", "extractLocalPathFromResourceName", - "extractMutationBaseValue", - "extractTransformMutationBaseValue", "fail", "fieldTransformEquals", "filterEquals", @@ -3017,7 +2988,6 @@ "getCachedTarget", "getDocument", "getEncodedDatabaseId", - "getHighestUnacknowledgedBatchId", "getLastRemoteSnapshotVersion", "getLocalTargetData", "getLocalWriteTime", @@ -3026,14 +2996,17 @@ "getNewDocumentChanges", "getOfflineComponentProvider", "getPostMutationVersion", + "getRemoteKeysForTarget", "getWindow", "globalTargetStore", + "handleCredentialChange", "handleUserChange", "hardAssert", "ignoreIfPrimaryLeaseLoss", "immediateSuccessor", "indexedDbClearPersistence", "indexedDbStoragePrefix", + "initializeViewAndComputeSnapshot", "isArray", "isCollectionGroupQuery", "isDocumentQuery", @@ -3056,7 +3029,6 @@ "isValidResourceName", "loadProtos", "localTransformResults", - "localWrite", "logDebug", "logError", "logWarn", @@ -3096,6 +3068,8 @@ "preconditionIsValidForDocument", "primaryClientStore", "primitiveComparator", + "processUserCallback", + "pumpEnqueuedLimboResolutions", "queryEquals", "queryMatches", "queryMatchesBounds", @@ -3107,11 +3081,16 @@ "randomBytes", "registerFirestore", "rejectBatch", + "rejectFailedWrite", + "rejectListen", + "rejectOutstandingPendingWritesCallbacks", "releaseTarget", "remoteDocumentsStore", + "removeAndCleanupTarget", "removeCachedMutationBatchMetadata", "removeComponents", "removeComponents$1", + "removeLimboTarget", "removeMutationBatch", "requireDocument", "resetLimboDocuments", @@ -3170,10 +3149,13 @@ "toTimestamp", "toUnaryOrFieldFilter", "toVersion", + "trackLimboChange", "transformObject", "transformOperationEquals", + "triggerPendingWritesCallbacks", "typeOrder", "uint8ArrayFromBinaryString", + "updateTrackedLimbos", "upgradeMutationBatchSchemaAndMigrateData", "valueCompare", "valueEquals", @@ -3345,13 +3327,11 @@ ], "variables": [] }, - "sizeInBytes": 304936 + "sizeInBytes": 302272 }, "enableNetwork": { "dependencies": { "functions": [ - "acknowledgeBatch", - "allocateTarget", "applyArrayRemoveTransformOperation", "applyArrayUnionTransformOperation", "applyDeleteMutationToLocalView", @@ -3359,16 +3339,15 @@ "applyMutationToLocalView", "applyMutationToRemoteDocument", "applyNumericIncrementTransformOperationToLocalView", + "applyOnlineStateChange", "applyPatchMutationToLocalView", "applyPatchMutationToRemoteDocument", - "applyRemoteEventToLocalCache", "applySetMutationToLocalView", "applySetMutationToRemoteDocument", "applyTransformMutationToLocalView", "applyTransformMutationToRemoteDocument", "applyTransformOperationToLocalView", "applyTransformOperationToRemoteDocument", - "applyWriteToRemoteDocuments", "argToString", "arrayEquals", "asNumber", @@ -3393,7 +3372,6 @@ "coercedFieldValuesArray", "compareArrays", "compareBlobs", - "compareChangeType", "compareDocs", "compareDocumentsByField", "compareGeoPoints", @@ -3410,14 +3388,11 @@ "documentMap", "documentTargetMap", "documentVersionMap", + "emitNewSnapsAndNotifyLocalStore", "enableNetwork", "encodeBase64", "enqueueNetworkEnabled", - "executeQuery", - "extractFieldMask", "extractLocalPathFromResourceName", - "extractMutationBaseValue", - "extractTransformMutationBaseValue", "fail", "fieldTransformEquals", "filterEquals", @@ -3436,9 +3411,7 @@ "fullyQualifiedPrefixPath", "geoPointEquals", "getEncodedDatabaseId", - "getHighestUnacknowledgedBatchId", "getLastRemoteSnapshotVersion", - "getLocalTargetData", "getLocalWriteTime", "getLogLevel", "getMessageOrStack", @@ -3447,9 +3420,9 @@ "getPersistence", "getPostMutationVersion", "getRemoteStore", + "handleCredentialChange", "handleUserChange", "hardAssert", - "ignoreIfPrimaryLeaseLoss", "isArray", "isCollectionGroupQuery", "isDocumentQuery", @@ -3470,7 +3443,6 @@ "isValidResourceName", "loadProtos", "localTransformResults", - "localWrite", "logDebug", "logError", "logWarn", @@ -3484,7 +3456,6 @@ "newPersistentWatchStream", "newPersistentWriteStream", "newQueryComparator", - "newQueryForPath", "newSerializer", "newSyncEngine", "newTarget", @@ -3513,8 +3484,7 @@ "queryToTarget", "randomBytes", "registerFirestore", - "rejectBatch", - "releaseTarget", + "rejectOutstandingPendingWritesCallbacks", "removeComponents", "removeComponents$1", "requireDocument", @@ -3522,7 +3492,6 @@ "serverTransformResults", "setOfflineComponentProvider", "setOnlineComponentProvider", - "shouldPersistTargetData", "snapshotChangesMap", "sortsBeforeDocument", "stringifyFilter", @@ -3570,7 +3539,6 @@ "wrapInUserErrorIfRecoverable" ], "classes": [ - "AddedLimboDocument", "ArrayRemoveTransformOperation", "ArrayUnionTransformOperation", "AsyncQueue", @@ -3587,15 +3555,12 @@ "DeleteMutation", "DocReference", "Document", - "DocumentChangeSet", "DocumentKey", - "DocumentSet", "DocumentWatchChange", "EventManager", "ExistenceFilter", "ExistenceFilterChange", "ExponentialBackoff", - "FieldMask", "FieldPath", "FirebaseCredentialsProvider", "Firestore", @@ -3606,7 +3571,6 @@ "JsonProtoSerializer", "LLRBEmptyNode", "LLRBNode", - "LimboResolution", "ListenSequence", "LocalClientState", "LocalDocumentsView", @@ -3646,12 +3610,10 @@ "Precondition", "QueryImpl", "QueryListenersInfo", - "QueryView", "ReferenceSet", "RemoteDocumentChangeBuffer", "RemoteEvent", "RemoteStore", - "RemovedLimboDocument", "ResourcePath", "ServerTimestampTransform", "SetMutation", @@ -3673,14 +3635,12 @@ "UnknownDocument", "User", "VerifyMutation", - "View", - "ViewSnapshot", "WatchChangeAggregator", "WatchTargetChange" ], "variables": [] }, - "sizeInBytes": 196148 + "sizeInBytes": 165457 }, "endAt": { "dependencies": { @@ -3802,7 +3762,7 @@ ], "variables": [] }, - "sizeInBytes": 52667 + "sizeInBytes": 52669 }, "endBefore": { "dependencies": { @@ -3924,22 +3884,24 @@ ], "variables": [] }, - "sizeInBytes": 52678 + "sizeInBytes": 52680 }, "getDoc": { "dependencies": { "functions": [ - "acknowledgeBatch", "allocateTarget", "applyArrayRemoveTransformOperation", "applyArrayUnionTransformOperation", "applyDeleteMutationToLocalView", "applyDeleteMutationToRemoteDocument", + "applyDocChanges", "applyMutationToLocalView", "applyMutationToRemoteDocument", "applyNumericIncrementTransformOperationToLocalView", + "applyOnlineStateChange", "applyPatchMutationToLocalView", "applyPatchMutationToRemoteDocument", + "applyRemoteEvent", "applyRemoteEventToLocalCache", "applySetMutationToLocalView", "applySetMutationToRemoteDocument", @@ -3947,7 +3909,6 @@ "applyTransformMutationToRemoteDocument", "applyTransformOperationToLocalView", "applyTransformOperationToRemoteDocument", - "applyWriteToRemoteDocuments", "argToString", "arrayEquals", "asNumber", @@ -3991,15 +3952,14 @@ "documentMap", "documentTargetMap", "documentVersionMap", + "emitNewSnapsAndNotifyLocalStore", "encodeBase64", "enqueueListen", "enqueueReadDocumentViaSnapshotListener", + "ensureWatchCallbacks", "errorMessage", "executeQuery", - "extractFieldMask", "extractLocalPathFromResourceName", - "extractMutationBaseValue", - "extractTransformMutationBaseValue", "fail", "fieldPathFromArgument$1", "fieldPathFromDotSeparatedString", @@ -4024,7 +3984,6 @@ "getDoc", "getEncodedDatabaseId", "getEventManager", - "getHighestUnacknowledgedBatchId", "getLastRemoteSnapshotVersion", "getLocalTargetData", "getLocalWriteTime", @@ -4034,9 +3993,12 @@ "getOnlineComponentProvider", "getPostMutationVersion", "getPreviousValue", + "getRemoteKeysForTarget", + "handleCredentialChange", "handleUserChange", "hardAssert", "ignoreIfPrimaryLeaseLoss", + "initializeViewAndComputeSnapshot", "invalidClassError", "isArray", "isCollectionGroupQuery", @@ -4059,7 +4021,6 @@ "isValidResourceName", "loadProtos", "localTransformResults", - "localWrite", "logDebug", "logError", "logWarn", @@ -4093,6 +4054,7 @@ "patchObject", "preconditionIsValidForDocument", "primitiveComparator", + "pumpEnqueuedLimboResolutions", "queryEquals", "queryMatches", "queryMatchesBounds", @@ -4103,10 +4065,13 @@ "queryToTarget", "randomBytes", "registerFirestore", - "rejectBatch", + "rejectListen", + "rejectOutstandingPendingWritesCallbacks", "releaseTarget", + "removeAndCleanupTarget", "removeComponents", "removeComponents$1", + "removeLimboTarget", "requireDocument", "serverTimestamp", "serverTransformResults", @@ -4119,6 +4084,8 @@ "stringifyOrderBy", "stringifyQuery", "stringifyTarget", + "syncEngineListen", + "syncEngineUnlisten", "targetEquals", "targetIdSet", "timestampEquals", @@ -4149,11 +4116,13 @@ "toTimestamp", "toUnaryOrFieldFilter", "toVersion", + "trackLimboChange", "transformObject", "transformOperationEquals", "tryGetCustomObjectType", "typeOrder", "uint8ArrayFromBinaryString", + "updateTrackedLimbos", "validateArgType", "validateExactNumberOfArgs", "validateNamedArrayAtLeastNumberOfElements", @@ -4198,7 +4167,6 @@ "ExistenceFilter", "ExistenceFilterChange", "ExponentialBackoff", - "FieldMask", "FieldPath", "FieldPath$1", "FieldPath$2", @@ -4291,7 +4259,7 @@ ], "variables": [] }, - "sizeInBytes": 212082 + "sizeInBytes": 207270 }, "getDocFromCache": { "dependencies": { @@ -4533,22 +4501,24 @@ ], "variables": [] }, - "sizeInBytes": 119449 + "sizeInBytes": 119452 }, "getDocFromServer": { "dependencies": { "functions": [ - "acknowledgeBatch", "allocateTarget", "applyArrayRemoveTransformOperation", "applyArrayUnionTransformOperation", "applyDeleteMutationToLocalView", "applyDeleteMutationToRemoteDocument", + "applyDocChanges", "applyMutationToLocalView", "applyMutationToRemoteDocument", "applyNumericIncrementTransformOperationToLocalView", + "applyOnlineStateChange", "applyPatchMutationToLocalView", "applyPatchMutationToRemoteDocument", + "applyRemoteEvent", "applyRemoteEventToLocalCache", "applySetMutationToLocalView", "applySetMutationToRemoteDocument", @@ -4556,7 +4526,6 @@ "applyTransformMutationToRemoteDocument", "applyTransformOperationToLocalView", "applyTransformOperationToRemoteDocument", - "applyWriteToRemoteDocuments", "argToString", "arrayEquals", "asNumber", @@ -4600,15 +4569,14 @@ "documentMap", "documentTargetMap", "documentVersionMap", + "emitNewSnapsAndNotifyLocalStore", "encodeBase64", "enqueueListen", "enqueueReadDocumentViaSnapshotListener", + "ensureWatchCallbacks", "errorMessage", "executeQuery", - "extractFieldMask", "extractLocalPathFromResourceName", - "extractMutationBaseValue", - "extractTransformMutationBaseValue", "fail", "fieldPathFromArgument$1", "fieldPathFromDotSeparatedString", @@ -4633,7 +4601,6 @@ "getDocFromServer", "getEncodedDatabaseId", "getEventManager", - "getHighestUnacknowledgedBatchId", "getLastRemoteSnapshotVersion", "getLocalTargetData", "getLocalWriteTime", @@ -4643,9 +4610,12 @@ "getOnlineComponentProvider", "getPostMutationVersion", "getPreviousValue", + "getRemoteKeysForTarget", + "handleCredentialChange", "handleUserChange", "hardAssert", "ignoreIfPrimaryLeaseLoss", + "initializeViewAndComputeSnapshot", "invalidClassError", "isArray", "isCollectionGroupQuery", @@ -4668,7 +4638,6 @@ "isValidResourceName", "loadProtos", "localTransformResults", - "localWrite", "logDebug", "logError", "logWarn", @@ -4702,6 +4671,7 @@ "patchObject", "preconditionIsValidForDocument", "primitiveComparator", + "pumpEnqueuedLimboResolutions", "queryEquals", "queryMatches", "queryMatchesBounds", @@ -4712,10 +4682,13 @@ "queryToTarget", "randomBytes", "registerFirestore", - "rejectBatch", + "rejectListen", + "rejectOutstandingPendingWritesCallbacks", "releaseTarget", + "removeAndCleanupTarget", "removeComponents", "removeComponents$1", + "removeLimboTarget", "requireDocument", "serverTimestamp", "serverTransformResults", @@ -4728,6 +4701,8 @@ "stringifyOrderBy", "stringifyQuery", "stringifyTarget", + "syncEngineListen", + "syncEngineUnlisten", "targetEquals", "targetIdSet", "timestampEquals", @@ -4758,11 +4733,13 @@ "toTimestamp", "toUnaryOrFieldFilter", "toVersion", + "trackLimboChange", "transformObject", "transformOperationEquals", "tryGetCustomObjectType", "typeOrder", "uint8ArrayFromBinaryString", + "updateTrackedLimbos", "validateArgType", "validateExactNumberOfArgs", "validateNamedArrayAtLeastNumberOfElements", @@ -4807,7 +4784,6 @@ "ExistenceFilter", "ExistenceFilterChange", "ExponentialBackoff", - "FieldMask", "FieldPath", "FieldPath$1", "FieldPath$2", @@ -4900,22 +4876,24 @@ ], "variables": [] }, - "sizeInBytes": 212138 + "sizeInBytes": 207326 }, "getDocs": { "dependencies": { "functions": [ - "acknowledgeBatch", "allocateTarget", "applyArrayRemoveTransformOperation", "applyArrayUnionTransformOperation", "applyDeleteMutationToLocalView", "applyDeleteMutationToRemoteDocument", + "applyDocChanges", "applyMutationToLocalView", "applyMutationToRemoteDocument", "applyNumericIncrementTransformOperationToLocalView", + "applyOnlineStateChange", "applyPatchMutationToLocalView", "applyPatchMutationToRemoteDocument", + "applyRemoteEvent", "applyRemoteEventToLocalCache", "applySetMutationToLocalView", "applySetMutationToRemoteDocument", @@ -4923,7 +4901,6 @@ "applyTransformMutationToRemoteDocument", "applyTransformOperationToLocalView", "applyTransformOperationToRemoteDocument", - "applyWriteToRemoteDocuments", "argToString", "arrayEquals", "asNumber", @@ -4967,15 +4944,14 @@ "documentMap", "documentTargetMap", "documentVersionMap", + "emitNewSnapsAndNotifyLocalStore", "encodeBase64", "enqueueExecuteQueryViaSnapshotListener", "enqueueListen", + "ensureWatchCallbacks", "errorMessage", "executeQuery", - "extractFieldMask", "extractLocalPathFromResourceName", - "extractMutationBaseValue", - "extractTransformMutationBaseValue", "fail", "fieldPathFromArgument$1", "fieldPathFromDotSeparatedString", @@ -5000,7 +4976,6 @@ "getDocs", "getEncodedDatabaseId", "getEventManager", - "getHighestUnacknowledgedBatchId", "getLastRemoteSnapshotVersion", "getLocalTargetData", "getLocalWriteTime", @@ -5010,9 +4985,12 @@ "getOnlineComponentProvider", "getPostMutationVersion", "getPreviousValue", + "getRemoteKeysForTarget", + "handleCredentialChange", "handleUserChange", "hardAssert", "ignoreIfPrimaryLeaseLoss", + "initializeViewAndComputeSnapshot", "invalidClassError", "isArray", "isCollectionGroupQuery", @@ -5035,7 +5013,6 @@ "isValidResourceName", "loadProtos", "localTransformResults", - "localWrite", "logDebug", "logError", "logWarn", @@ -5069,6 +5046,7 @@ "patchObject", "preconditionIsValidForDocument", "primitiveComparator", + "pumpEnqueuedLimboResolutions", "queryEquals", "queryMatches", "queryMatchesBounds", @@ -5079,10 +5057,13 @@ "queryToTarget", "randomBytes", "registerFirestore", - "rejectBatch", + "rejectListen", + "rejectOutstandingPendingWritesCallbacks", "releaseTarget", + "removeAndCleanupTarget", "removeComponents", "removeComponents$1", + "removeLimboTarget", "requireDocument", "resultChangeType", "serverTimestamp", @@ -5096,6 +5077,8 @@ "stringifyOrderBy", "stringifyQuery", "stringifyTarget", + "syncEngineListen", + "syncEngineUnlisten", "targetEquals", "targetIdSet", "timestampEquals", @@ -5126,11 +5109,13 @@ "toTimestamp", "toUnaryOrFieldFilter", "toVersion", + "trackLimboChange", "transformObject", "transformOperationEquals", "tryGetCustomObjectType", "typeOrder", "uint8ArrayFromBinaryString", + "updateTrackedLimbos", "validateArgType", "validateExactNumberOfArgs", "validateHasExplicitOrderByForLimitToLast", @@ -5176,7 +5161,6 @@ "ExistenceFilter", "ExistenceFilterChange", "ExponentialBackoff", - "FieldMask", "FieldPath", "FieldPath$1", "FieldPath$2", @@ -5271,7 +5255,7 @@ ], "variables": [] }, - "sizeInBytes": 214645 + "sizeInBytes": 209833 }, "getDocsFromCache": { "dependencies": { @@ -5526,22 +5510,24 @@ ], "variables": [] }, - "sizeInBytes": 132629 + "sizeInBytes": 132632 }, "getDocsFromServer": { "dependencies": { "functions": [ - "acknowledgeBatch", "allocateTarget", "applyArrayRemoveTransformOperation", "applyArrayUnionTransformOperation", "applyDeleteMutationToLocalView", "applyDeleteMutationToRemoteDocument", + "applyDocChanges", "applyMutationToLocalView", "applyMutationToRemoteDocument", "applyNumericIncrementTransformOperationToLocalView", + "applyOnlineStateChange", "applyPatchMutationToLocalView", "applyPatchMutationToRemoteDocument", + "applyRemoteEvent", "applyRemoteEventToLocalCache", "applySetMutationToLocalView", "applySetMutationToRemoteDocument", @@ -5549,7 +5535,6 @@ "applyTransformMutationToRemoteDocument", "applyTransformOperationToLocalView", "applyTransformOperationToRemoteDocument", - "applyWriteToRemoteDocuments", "argToString", "arrayEquals", "asNumber", @@ -5593,15 +5578,14 @@ "documentMap", "documentTargetMap", "documentVersionMap", + "emitNewSnapsAndNotifyLocalStore", "encodeBase64", "enqueueExecuteQueryViaSnapshotListener", "enqueueListen", + "ensureWatchCallbacks", "errorMessage", "executeQuery", - "extractFieldMask", "extractLocalPathFromResourceName", - "extractMutationBaseValue", - "extractTransformMutationBaseValue", "fail", "fieldPathFromArgument$1", "fieldPathFromDotSeparatedString", @@ -5626,7 +5610,6 @@ "getDocsFromServer", "getEncodedDatabaseId", "getEventManager", - "getHighestUnacknowledgedBatchId", "getLastRemoteSnapshotVersion", "getLocalTargetData", "getLocalWriteTime", @@ -5636,9 +5619,12 @@ "getOnlineComponentProvider", "getPostMutationVersion", "getPreviousValue", + "getRemoteKeysForTarget", + "handleCredentialChange", "handleUserChange", "hardAssert", "ignoreIfPrimaryLeaseLoss", + "initializeViewAndComputeSnapshot", "invalidClassError", "isArray", "isCollectionGroupQuery", @@ -5661,7 +5647,6 @@ "isValidResourceName", "loadProtos", "localTransformResults", - "localWrite", "logDebug", "logError", "logWarn", @@ -5695,6 +5680,7 @@ "patchObject", "preconditionIsValidForDocument", "primitiveComparator", + "pumpEnqueuedLimboResolutions", "queryEquals", "queryMatches", "queryMatchesBounds", @@ -5705,10 +5691,13 @@ "queryToTarget", "randomBytes", "registerFirestore", - "rejectBatch", + "rejectListen", + "rejectOutstandingPendingWritesCallbacks", "releaseTarget", + "removeAndCleanupTarget", "removeComponents", "removeComponents$1", + "removeLimboTarget", "requireDocument", "resultChangeType", "serverTimestamp", @@ -5722,6 +5711,8 @@ "stringifyOrderBy", "stringifyQuery", "stringifyTarget", + "syncEngineListen", + "syncEngineUnlisten", "targetEquals", "targetIdSet", "timestampEquals", @@ -5752,11 +5743,13 @@ "toTimestamp", "toUnaryOrFieldFilter", "toVersion", + "trackLimboChange", "transformObject", "transformOperationEquals", "tryGetCustomObjectType", "typeOrder", "uint8ArrayFromBinaryString", + "updateTrackedLimbos", "validateArgType", "validateExactNumberOfArgs", "validateNamedArrayAtLeastNumberOfElements", @@ -5801,7 +5794,6 @@ "ExistenceFilter", "ExistenceFilterChange", "ExponentialBackoff", - "FieldMask", "FieldPath", "FieldPath$1", "FieldPath$2", @@ -5896,7 +5888,7 @@ ], "variables": [] }, - "sizeInBytes": 214383 + "sizeInBytes": 209571 }, "getFirestore": { "dependencies": { @@ -5934,7 +5926,7 @@ ], "variables": [] }, - "sizeInBytes": 16694 + "sizeInBytes": 16696 }, "increment": { "dependencies": { @@ -5984,7 +5976,7 @@ ], "variables": [] }, - "sizeInBytes": 18540 + "sizeInBytes": 18542 }, "initializeFirestore": { "dependencies": { @@ -6023,7 +6015,7 @@ ], "variables": [] }, - "sizeInBytes": 17998 + "sizeInBytes": 18000 }, "limit": { "dependencies": { @@ -6069,7 +6061,7 @@ ], "variables": [] }, - "sizeInBytes": 19125 + "sizeInBytes": 19127 }, "limitToLast": { "dependencies": { @@ -6115,22 +6107,24 @@ ], "variables": [] }, - "sizeInBytes": 19149 + "sizeInBytes": 19151 }, "onSnapshot": { "dependencies": { "functions": [ - "acknowledgeBatch", "allocateTarget", "applyArrayRemoveTransformOperation", "applyArrayUnionTransformOperation", "applyDeleteMutationToLocalView", "applyDeleteMutationToRemoteDocument", + "applyDocChanges", "applyMutationToLocalView", "applyMutationToRemoteDocument", "applyNumericIncrementTransformOperationToLocalView", + "applyOnlineStateChange", "applyPatchMutationToLocalView", "applyPatchMutationToRemoteDocument", + "applyRemoteEvent", "applyRemoteEventToLocalCache", "applySetMutationToLocalView", "applySetMutationToRemoteDocument", @@ -6138,7 +6132,6 @@ "applyTransformMutationToRemoteDocument", "applyTransformOperationToLocalView", "applyTransformOperationToRemoteDocument", - "applyWriteToRemoteDocuments", "argToString", "arrayEquals", "asNumber", @@ -6183,14 +6176,13 @@ "documentMap", "documentTargetMap", "documentVersionMap", + "emitNewSnapsAndNotifyLocalStore", "encodeBase64", "enqueueListen", + "ensureWatchCallbacks", "errorMessage", "executeQuery", - "extractFieldMask", "extractLocalPathFromResourceName", - "extractMutationBaseValue", - "extractTransformMutationBaseValue", "fail", "fieldPathFromArgument$1", "fieldPathFromDotSeparatedString", @@ -6214,7 +6206,6 @@ "geoPointEquals", "getEncodedDatabaseId", "getEventManager", - "getHighestUnacknowledgedBatchId", "getLastRemoteSnapshotVersion", "getLocalTargetData", "getLocalWriteTime", @@ -6224,10 +6215,13 @@ "getOnlineComponentProvider", "getPostMutationVersion", "getPreviousValue", + "getRemoteKeysForTarget", + "handleCredentialChange", "handleUserChange", "hardAssert", "ignoreIfPrimaryLeaseLoss", "implementsAnyMethods", + "initializeViewAndComputeSnapshot", "invalidClassError", "isArray", "isCollectionGroupQuery", @@ -6251,7 +6245,6 @@ "isValidResourceName", "loadProtos", "localTransformResults", - "localWrite", "logDebug", "logError", "logWarn", @@ -6286,6 +6279,7 @@ "patchObject", "preconditionIsValidForDocument", "primitiveComparator", + "pumpEnqueuedLimboResolutions", "queryEquals", "queryMatches", "queryMatchesBounds", @@ -6296,10 +6290,13 @@ "queryToTarget", "randomBytes", "registerFirestore", - "rejectBatch", + "rejectListen", + "rejectOutstandingPendingWritesCallbacks", "releaseTarget", + "removeAndCleanupTarget", "removeComponents", "removeComponents$1", + "removeLimboTarget", "requireDocument", "resultChangeType", "serverTimestamp", @@ -6313,6 +6310,8 @@ "stringifyOrderBy", "stringifyQuery", "stringifyTarget", + "syncEngineListen", + "syncEngineUnlisten", "targetEquals", "targetIdSet", "timestampEquals", @@ -6343,11 +6342,13 @@ "toTimestamp", "toUnaryOrFieldFilter", "toVersion", + "trackLimboChange", "transformObject", "transformOperationEquals", "tryGetCustomObjectType", "typeOrder", "uint8ArrayFromBinaryString", + "updateTrackedLimbos", "validateArgType", "validateExactNumberOfArgs", "validateHasExplicitOrderByForLimitToLast", @@ -6393,7 +6394,6 @@ "ExistenceFilter", "ExistenceFilterChange", "ExponentialBackoff", - "FieldMask", "FieldPath", "FieldPath$1", "FieldPath$2", @@ -6488,22 +6488,24 @@ ], "variables": [] }, - "sizeInBytes": 215728 + "sizeInBytes": 210916 }, "onSnapshotsInSync": { "dependencies": { "functions": [ - "acknowledgeBatch", "allocateTarget", "applyArrayRemoveTransformOperation", "applyArrayUnionTransformOperation", "applyDeleteMutationToLocalView", "applyDeleteMutationToRemoteDocument", + "applyDocChanges", "applyMutationToLocalView", "applyMutationToRemoteDocument", "applyNumericIncrementTransformOperationToLocalView", + "applyOnlineStateChange", "applyPatchMutationToLocalView", "applyPatchMutationToRemoteDocument", + "applyRemoteEvent", "applyRemoteEventToLocalCache", "applySetMutationToLocalView", "applySetMutationToRemoteDocument", @@ -6511,7 +6513,6 @@ "applyTransformMutationToRemoteDocument", "applyTransformOperationToLocalView", "applyTransformOperationToRemoteDocument", - "applyWriteToRemoteDocuments", "argToString", "arrayEquals", "asNumber", @@ -6553,13 +6554,12 @@ "documentMap", "documentTargetMap", "documentVersionMap", + "emitNewSnapsAndNotifyLocalStore", "encodeBase64", "enqueueSnapshotsInSyncListen", + "ensureWatchCallbacks", "executeQuery", - "extractFieldMask", "extractLocalPathFromResourceName", - "extractMutationBaseValue", - "extractTransformMutationBaseValue", "fail", "fieldTransformEquals", "filterEquals", @@ -6579,7 +6579,6 @@ "geoPointEquals", "getEncodedDatabaseId", "getEventManager", - "getHighestUnacknowledgedBatchId", "getLastRemoteSnapshotVersion", "getLocalTargetData", "getLocalWriteTime", @@ -6588,10 +6587,13 @@ "getOfflineComponentProvider", "getOnlineComponentProvider", "getPostMutationVersion", + "getRemoteKeysForTarget", + "handleCredentialChange", "handleUserChange", "hardAssert", "ignoreIfPrimaryLeaseLoss", "implementsAnyMethods", + "initializeViewAndComputeSnapshot", "isArray", "isCollectionGroupQuery", "isDocumentQuery", @@ -6613,7 +6615,6 @@ "isValidResourceName", "loadProtos", "localTransformResults", - "localWrite", "logDebug", "logError", "logWarn", @@ -6647,6 +6648,7 @@ "patchObject", "preconditionIsValidForDocument", "primitiveComparator", + "pumpEnqueuedLimboResolutions", "queryEquals", "queryMatches", "queryMatchesBounds", @@ -6657,10 +6659,13 @@ "queryToTarget", "randomBytes", "registerFirestore", - "rejectBatch", + "rejectListen", + "rejectOutstandingPendingWritesCallbacks", "releaseTarget", + "removeAndCleanupTarget", "removeComponents", "removeComponents$1", + "removeLimboTarget", "requireDocument", "serverTimestamp", "serverTransformResults", @@ -6673,6 +6678,8 @@ "stringifyOrderBy", "stringifyQuery", "stringifyTarget", + "syncEngineListen", + "syncEngineUnlisten", "targetEquals", "targetIdSet", "timestampEquals", @@ -6703,10 +6710,12 @@ "toTimestamp", "toUnaryOrFieldFilter", "toVersion", + "trackLimboChange", "transformObject", "transformOperationEquals", "typeOrder", "uint8ArrayFromBinaryString", + "updateTrackedLimbos", "valueCompare", "valueEquals", "verifyNotTerminated", @@ -6740,7 +6749,6 @@ "ExistenceFilter", "ExistenceFilterChange", "ExponentialBackoff", - "FieldMask", "FieldPath", "FirebaseCredentialsProvider", "Firestore", @@ -6825,7 +6833,7 @@ ], "variables": [] }, - "sizeInBytes": 196997 + "sizeInBytes": 192185 }, "orderBy": { "dependencies": { @@ -6893,7 +6901,7 @@ ], "variables": [] }, - "sizeInBytes": 28770 + "sizeInBytes": 28772 }, "parent": { "dependencies": { @@ -6942,7 +6950,7 @@ ], "variables": [] }, - "sizeInBytes": 22851 + "sizeInBytes": 22853 }, "query": { "dependencies": { @@ -6982,7 +6990,7 @@ ], "variables": [] }, - "sizeInBytes": 17392 + "sizeInBytes": 17394 }, "queryEqual": { "dependencies": { @@ -7058,7 +7066,7 @@ ], "variables": [] }, - "sizeInBytes": 33341 + "sizeInBytes": 33343 }, "refEqual": { "dependencies": { @@ -7106,7 +7114,7 @@ ], "variables": [] }, - "sizeInBytes": 22398 + "sizeInBytes": 22400 }, "runTransaction": { "dependencies": { @@ -7292,7 +7300,7 @@ ], "variables": [] }, - "sizeInBytes": 81653 + "sizeInBytes": 81655 }, "serverTimestamp": { "dependencies": { @@ -7337,13 +7345,13 @@ ], "variables": [] }, - "sizeInBytes": 17686 + "sizeInBytes": 17688 }, "setDoc": { "dependencies": { "functions": [ "acknowledgeBatch", - "allocateTarget", + "addMutationCallback", "applyArrayRemoveTransformOperation", "applyArrayUnionTransformOperation", "applyDeleteMutationToLocalView", @@ -7352,11 +7360,12 @@ "applyMutationToLocalView", "applyMutationToRemoteDocument", "applyNumericIncrementTransformOperationToLocalView", + "applyOnlineStateChange", "applyPatchMutationToLocalView", "applyPatchMutationToRemoteDocument", - "applyRemoteEventToLocalCache", "applySetMutationToLocalView", "applySetMutationToRemoteDocument", + "applySuccessfulWrite", "applyTransformMutationToLocalView", "applyTransformMutationToRemoteDocument", "applyTransformOperationToLocalView", @@ -7386,7 +7395,6 @@ "coercedFieldValuesArray", "compareArrays", "compareBlobs", - "compareChangeType", "compareDocs", "compareDocumentsByField", "compareGeoPoints", @@ -7404,10 +7412,11 @@ "documentMap", "documentTargetMap", "documentVersionMap", + "emitNewSnapsAndNotifyLocalStore", "encodeBase64", "enqueueWrite", + "ensureWriteCallbacks", "errorMessage", - "executeQuery", "extractFieldMask", "extractLocalPathFromResourceName", "extractMutationBaseValue", @@ -7434,9 +7443,7 @@ "fullyQualifiedPrefixPath", "geoPointEquals", "getEncodedDatabaseId", - "getHighestUnacknowledgedBatchId", "getLastRemoteSnapshotVersion", - "getLocalTargetData", "getLocalWriteTime", "getLogLevel", "getMessageOrStack", @@ -7444,6 +7451,7 @@ "getOnlineComponentProvider", "getPostMutationVersion", "getSyncEngine", + "handleCredentialChange", "handleUserChange", "hardAssert", "ignoreIfPrimaryLeaseLoss", @@ -7486,7 +7494,6 @@ "newPersistentWatchStream", "newPersistentWriteStream", "newQueryComparator", - "newQueryForPath", "newSerializer", "newSyncEngine", "newTarget", @@ -7513,6 +7520,7 @@ "patchObject", "preconditionIsValidForDocument", "primitiveComparator", + "processUserCallback", "queryEquals", "queryMatches", "queryMatchesBounds", @@ -7524,7 +7532,8 @@ "randomBytes", "registerFirestore", "rejectBatch", - "releaseTarget", + "rejectFailedWrite", + "rejectOutstandingPendingWritesCallbacks", "removeComponents", "removeComponents$1", "requireDocument", @@ -7533,13 +7542,13 @@ "setDoc", "setOfflineComponentProvider", "setOnlineComponentProvider", - "shouldPersistTargetData", "snapshotChangesMap", "sortsBeforeDocument", "stringifyFilter", "stringifyOrderBy", "stringifyQuery", "stringifyTarget", + "syncEngineWrite", "targetEquals", "targetIdSet", "timestampEquals", @@ -7573,6 +7582,7 @@ "toVersion", "transformObject", "transformOperationEquals", + "triggerPendingWritesCallbacks", "tryGetCustomObjectType", "typeOrder", "uint8ArrayFromBinaryString", @@ -7589,7 +7599,6 @@ "wrapInUserErrorIfRecoverable" ], "classes": [ - "AddedLimboDocument", "ArrayRemoveTransformOperation", "ArrayUnionTransformOperation", "AsyncQueue", @@ -7608,11 +7617,9 @@ "DeleteMutation", "DocReference", "Document", - "DocumentChangeSet", "DocumentKey", "DocumentKeyReference", "DocumentReference", - "DocumentSet", "DocumentWatchChange", "EventManager", "ExistenceFilter", @@ -7631,7 +7638,6 @@ "JsonProtoSerializer", "LLRBEmptyNode", "LLRBNode", - "LimboResolution", "ListenSequence", "LocalClientState", "LocalDocumentsView", @@ -7673,12 +7679,10 @@ "Precondition", "QueryImpl", "QueryListenersInfo", - "QueryView", "ReferenceSet", "RemoteDocumentChangeBuffer", "RemoteEvent", "RemoteStore", - "RemovedLimboDocument", "ResourcePath", "SerializableFieldValue", "ServerTimestampTransform", @@ -7702,14 +7706,12 @@ "User", "UserDataReader", "VerifyMutation", - "View", - "ViewSnapshot", "WatchChangeAggregator", "WatchTargetChange" ], "variables": [] }, - "sizeInBytes": 211875 + "sizeInBytes": 188729 }, "setLogLevel": { "dependencies": { @@ -7747,7 +7749,7 @@ ], "variables": [] }, - "sizeInBytes": 16660 + "sizeInBytes": 16662 }, "snapshotEqual": { "dependencies": { @@ -7860,7 +7862,7 @@ ], "variables": [] }, - "sizeInBytes": 50432 + "sizeInBytes": 50434 }, "startAfter": { "dependencies": { @@ -7982,7 +7984,7 @@ ], "variables": [] }, - "sizeInBytes": 52688 + "sizeInBytes": 52690 }, "startAt": { "dependencies": { @@ -8104,7 +8106,7 @@ ], "variables": [] }, - "sizeInBytes": 52678 + "sizeInBytes": 52680 }, "terminate": { "dependencies": { @@ -8143,13 +8145,13 @@ ], "variables": [] }, - "sizeInBytes": 17190 + "sizeInBytes": 17193 }, "updateDoc": { "dependencies": { "functions": [ "acknowledgeBatch", - "allocateTarget", + "addMutationCallback", "applyArrayRemoveTransformOperation", "applyArrayUnionTransformOperation", "applyDeleteMutationToLocalView", @@ -8157,11 +8159,12 @@ "applyMutationToLocalView", "applyMutationToRemoteDocument", "applyNumericIncrementTransformOperationToLocalView", + "applyOnlineStateChange", "applyPatchMutationToLocalView", "applyPatchMutationToRemoteDocument", - "applyRemoteEventToLocalCache", "applySetMutationToLocalView", "applySetMutationToRemoteDocument", + "applySuccessfulWrite", "applyTransformMutationToLocalView", "applyTransformMutationToRemoteDocument", "applyTransformOperationToLocalView", @@ -8191,7 +8194,6 @@ "coercedFieldValuesArray", "compareArrays", "compareBlobs", - "compareChangeType", "compareDocs", "compareDocumentsByField", "compareGeoPoints", @@ -8209,10 +8211,11 @@ "documentMap", "documentTargetMap", "documentVersionMap", + "emitNewSnapsAndNotifyLocalStore", "encodeBase64", "enqueueWrite", + "ensureWriteCallbacks", "errorMessage", - "executeQuery", "extractFieldMask", "extractLocalPathFromResourceName", "extractMutationBaseValue", @@ -8240,9 +8243,7 @@ "fullyQualifiedPrefixPath", "geoPointEquals", "getEncodedDatabaseId", - "getHighestUnacknowledgedBatchId", "getLastRemoteSnapshotVersion", - "getLocalTargetData", "getLocalWriteTime", "getLogLevel", "getMessageOrStack", @@ -8250,6 +8251,7 @@ "getOnlineComponentProvider", "getPostMutationVersion", "getSyncEngine", + "handleCredentialChange", "handleUserChange", "hardAssert", "ignoreIfPrimaryLeaseLoss", @@ -8292,7 +8294,6 @@ "newPersistentWatchStream", "newPersistentWriteStream", "newQueryComparator", - "newQueryForPath", "newSerializer", "newSyncEngine", "newTarget", @@ -8320,6 +8321,7 @@ "patchObject", "preconditionIsValidForDocument", "primitiveComparator", + "processUserCallback", "queryEquals", "queryMatches", "queryMatchesBounds", @@ -8331,7 +8333,8 @@ "randomBytes", "registerFirestore", "rejectBatch", - "releaseTarget", + "rejectFailedWrite", + "rejectOutstandingPendingWritesCallbacks", "removeComponents", "removeComponents$1", "requireDocument", @@ -8339,13 +8342,13 @@ "serverTransformResults", "setOfflineComponentProvider", "setOnlineComponentProvider", - "shouldPersistTargetData", "snapshotChangesMap", "sortsBeforeDocument", "stringifyFilter", "stringifyOrderBy", "stringifyQuery", "stringifyTarget", + "syncEngineWrite", "targetEquals", "targetIdSet", "timestampEquals", @@ -8379,6 +8382,7 @@ "toVersion", "transformObject", "transformOperationEquals", + "triggerPendingWritesCallbacks", "tryGetCustomObjectType", "typeOrder", "uint8ArrayFromBinaryString", @@ -8396,7 +8400,6 @@ "wrapInUserErrorIfRecoverable" ], "classes": [ - "AddedLimboDocument", "ArrayRemoveTransformOperation", "ArrayUnionTransformOperation", "AsyncQueue", @@ -8416,11 +8419,9 @@ "DeleteMutation", "DocReference", "Document", - "DocumentChangeSet", "DocumentKey", "DocumentKeyReference", "DocumentReference", - "DocumentSet", "DocumentWatchChange", "EventManager", "ExistenceFilter", @@ -8440,7 +8441,6 @@ "JsonProtoSerializer", "LLRBEmptyNode", "LLRBNode", - "LimboResolution", "ListenSequence", "LocalClientState", "LocalDocumentsView", @@ -8482,12 +8482,10 @@ "Precondition", "QueryImpl", "QueryListenersInfo", - "QueryView", "ReferenceSet", "RemoteDocumentChangeBuffer", "RemoteEvent", "RemoteStore", - "RemovedLimboDocument", "ResourcePath", "SerializableFieldValue", "ServerTimestampTransform", @@ -8511,20 +8509,16 @@ "User", "UserDataReader", "VerifyMutation", - "View", - "ViewSnapshot", "WatchChangeAggregator", "WatchTargetChange" ], "variables": [] }, - "sizeInBytes": 213374 + "sizeInBytes": 190228 }, "waitForPendingWrites": { "dependencies": { "functions": [ - "acknowledgeBatch", - "allocateTarget", "applyArrayRemoveTransformOperation", "applyArrayUnionTransformOperation", "applyDeleteMutationToLocalView", @@ -8532,16 +8526,15 @@ "applyMutationToLocalView", "applyMutationToRemoteDocument", "applyNumericIncrementTransformOperationToLocalView", + "applyOnlineStateChange", "applyPatchMutationToLocalView", "applyPatchMutationToRemoteDocument", - "applyRemoteEventToLocalCache", "applySetMutationToLocalView", "applySetMutationToRemoteDocument", "applyTransformMutationToLocalView", "applyTransformMutationToRemoteDocument", "applyTransformOperationToLocalView", "applyTransformOperationToRemoteDocument", - "applyWriteToRemoteDocuments", "argToString", "arrayEquals", "asNumber", @@ -8566,7 +8559,6 @@ "coercedFieldValuesArray", "compareArrays", "compareBlobs", - "compareChangeType", "compareDocs", "compareDocumentsByField", "compareGeoPoints", @@ -8583,13 +8575,10 @@ "documentMap", "documentTargetMap", "documentVersionMap", + "emitNewSnapsAndNotifyLocalStore", "encodeBase64", "enqueueWaitForPendingWrites", - "executeQuery", - "extractFieldMask", "extractLocalPathFromResourceName", - "extractMutationBaseValue", - "extractTransformMutationBaseValue", "fail", "fieldTransformEquals", "filterEquals", @@ -8610,7 +8599,6 @@ "getEncodedDatabaseId", "getHighestUnacknowledgedBatchId", "getLastRemoteSnapshotVersion", - "getLocalTargetData", "getLocalWriteTime", "getLogLevel", "getMessageOrStack", @@ -8618,9 +8606,9 @@ "getOnlineComponentProvider", "getPostMutationVersion", "getSyncEngine", + "handleCredentialChange", "handleUserChange", "hardAssert", - "ignoreIfPrimaryLeaseLoss", "isArray", "isCollectionGroupQuery", "isDocumentQuery", @@ -8641,7 +8629,6 @@ "isValidResourceName", "loadProtos", "localTransformResults", - "localWrite", "logDebug", "logError", "logWarn", @@ -8655,7 +8642,6 @@ "newPersistentWatchStream", "newPersistentWriteStream", "newQueryComparator", - "newQueryForPath", "newSerializer", "newSyncEngine", "newTarget", @@ -8684,8 +8670,8 @@ "queryToTarget", "randomBytes", "registerFirestore", - "rejectBatch", - "releaseTarget", + "registerPendingWritesCallback", + "rejectOutstandingPendingWritesCallbacks", "removeComponents", "removeComponents$1", "requireDocument", @@ -8693,7 +8679,6 @@ "serverTransformResults", "setOfflineComponentProvider", "setOnlineComponentProvider", - "shouldPersistTargetData", "snapshotChangesMap", "sortsBeforeDocument", "stringifyFilter", @@ -8742,7 +8727,6 @@ "wrapInUserErrorIfRecoverable" ], "classes": [ - "AddedLimboDocument", "ArrayRemoveTransformOperation", "ArrayUnionTransformOperation", "AsyncQueue", @@ -8759,15 +8743,12 @@ "DeleteMutation", "DocReference", "Document", - "DocumentChangeSet", "DocumentKey", - "DocumentSet", "DocumentWatchChange", "EventManager", "ExistenceFilter", "ExistenceFilterChange", "ExponentialBackoff", - "FieldMask", "FieldPath", "FirebaseCredentialsProvider", "Firestore", @@ -8778,7 +8759,6 @@ "JsonProtoSerializer", "LLRBEmptyNode", "LLRBNode", - "LimboResolution", "ListenSequence", "LocalClientState", "LocalDocumentsView", @@ -8818,12 +8798,10 @@ "Precondition", "QueryImpl", "QueryListenersInfo", - "QueryView", "ReferenceSet", "RemoteDocumentChangeBuffer", "RemoteEvent", "RemoteStore", - "RemovedLimboDocument", "ResourcePath", "ServerTimestampTransform", "SetMutation", @@ -8845,14 +8823,12 @@ "UnknownDocument", "User", "VerifyMutation", - "View", - "ViewSnapshot", "WatchChangeAggregator", "WatchTargetChange" ], "variables": [] }, - "sizeInBytes": 195940 + "sizeInBytes": 166293 }, "where": { "dependencies": { @@ -8998,13 +8974,13 @@ ], "variables": [] }, - "sizeInBytes": 58992 + "sizeInBytes": 58994 }, "writeBatch": { "dependencies": { "functions": [ "acknowledgeBatch", - "allocateTarget", + "addMutationCallback", "applyArrayRemoveTransformOperation", "applyArrayUnionTransformOperation", "applyDeleteMutationToLocalView", @@ -9013,11 +8989,12 @@ "applyMutationToLocalView", "applyMutationToRemoteDocument", "applyNumericIncrementTransformOperationToLocalView", + "applyOnlineStateChange", "applyPatchMutationToLocalView", "applyPatchMutationToRemoteDocument", - "applyRemoteEventToLocalCache", "applySetMutationToLocalView", "applySetMutationToRemoteDocument", + "applySuccessfulWrite", "applyTransformMutationToLocalView", "applyTransformMutationToRemoteDocument", "applyTransformOperationToLocalView", @@ -9047,7 +9024,6 @@ "coercedFieldValuesArray", "compareArrays", "compareBlobs", - "compareChangeType", "compareDocs", "compareDocumentsByField", "compareGeoPoints", @@ -9065,10 +9041,11 @@ "documentMap", "documentTargetMap", "documentVersionMap", + "emitNewSnapsAndNotifyLocalStore", "encodeBase64", "enqueueWrite", + "ensureWriteCallbacks", "errorMessage", - "executeQuery", "extractFieldMask", "extractLocalPathFromResourceName", "extractMutationBaseValue", @@ -9096,9 +9073,7 @@ "fullyQualifiedPrefixPath", "geoPointEquals", "getEncodedDatabaseId", - "getHighestUnacknowledgedBatchId", "getLastRemoteSnapshotVersion", - "getLocalTargetData", "getLocalWriteTime", "getLogLevel", "getMessageOrStack", @@ -9106,6 +9081,7 @@ "getOnlineComponentProvider", "getPostMutationVersion", "getSyncEngine", + "handleCredentialChange", "handleUserChange", "hardAssert", "ignoreIfPrimaryLeaseLoss", @@ -9148,7 +9124,6 @@ "newPersistentWatchStream", "newPersistentWriteStream", "newQueryComparator", - "newQueryForPath", "newSerializer", "newSyncEngine", "newTarget", @@ -9177,6 +9152,7 @@ "patchObject", "preconditionIsValidForDocument", "primitiveComparator", + "processUserCallback", "queryEquals", "queryMatches", "queryMatchesBounds", @@ -9188,7 +9164,8 @@ "randomBytes", "registerFirestore", "rejectBatch", - "releaseTarget", + "rejectFailedWrite", + "rejectOutstandingPendingWritesCallbacks", "removeComponents", "removeComponents$1", "requireDocument", @@ -9196,13 +9173,13 @@ "serverTransformResults", "setOfflineComponentProvider", "setOnlineComponentProvider", - "shouldPersistTargetData", "snapshotChangesMap", "sortsBeforeDocument", "stringifyFilter", "stringifyOrderBy", "stringifyQuery", "stringifyTarget", + "syncEngineWrite", "targetEquals", "targetIdSet", "timestampEquals", @@ -9236,6 +9213,7 @@ "toVersion", "transformObject", "transformOperationEquals", + "triggerPendingWritesCallbacks", "tryGetCustomObjectType", "typeOrder", "uint8ArrayFromBinaryString", @@ -9254,7 +9232,6 @@ "writeBatch" ], "classes": [ - "AddedLimboDocument", "ArrayRemoveTransformOperation", "ArrayUnionTransformOperation", "AsyncQueue", @@ -9274,11 +9251,9 @@ "DeleteMutation", "DocReference", "Document", - "DocumentChangeSet", "DocumentKey", "DocumentKeyReference", "DocumentReference", - "DocumentSet", "DocumentWatchChange", "EventManager", "ExistenceFilter", @@ -9298,7 +9273,6 @@ "JsonProtoSerializer", "LLRBEmptyNode", "LLRBNode", - "LimboResolution", "ListenSequence", "LocalClientState", "LocalDocumentsView", @@ -9341,12 +9315,10 @@ "Precondition", "QueryImpl", "QueryListenersInfo", - "QueryView", "ReferenceSet", "RemoteDocumentChangeBuffer", "RemoteEvent", "RemoteStore", - "RemovedLimboDocument", "ResourcePath", "SerializableFieldValue", "ServerTimestampTransform", @@ -9370,14 +9342,12 @@ "User", "UserDataReader", "VerifyMutation", - "View", - "ViewSnapshot", "WatchChangeAggregator", "WatchTargetChange", "WriteBatch" ], "variables": [] }, - "sizeInBytes": 217041 + "sizeInBytes": 193895 } } \ No newline at end of file diff --git a/packages/firestore/exp/src/api/components.ts b/packages/firestore/exp/src/api/components.ts index fb043129cee..040b4dcc681 100644 --- a/packages/firestore/exp/src/api/components.ts +++ b/packages/firestore/exp/src/api/components.ts @@ -26,7 +26,11 @@ import { import { handleUserChange, LocalStore } from '../../../src/local/local_store'; import { Deferred } from '../../../src/util/promise'; import { logDebug } from '../../../src/util/log'; -import { SyncEngine } from '../../../src/core/sync_engine'; +import { + SyncEngine, + syncEngineListen, + syncEngineUnlisten +} from '../../../src/core/sync_engine'; import { RemoteStore } from '../../../src/remote/remote_store'; import { Persistence } from '../../../src/local/persistence'; import { EventManager } from '../../../src/core/event_manager'; @@ -140,6 +144,9 @@ function verifyNotTerminated(firestore: Firestore): void { } } +// Note: These functions cannot be `async` since we want to throw an exception +// when Firestore is terminated (via `getOnlineComponentProvider()`). + export function getSyncEngine(firestore: Firestore): Promise { return getOnlineComponentProvider(firestore).then( components => components.syncEngine @@ -153,9 +160,15 @@ export function getRemoteStore(firestore: Firestore): Promise { } export function getEventManager(firestore: Firestore): Promise { - return getOnlineComponentProvider(firestore).then( - components => components.eventManager - ); + return getOnlineComponentProvider(firestore).then(components => { + const eventManager = components.eventManager; + eventManager.onListen = syncEngineListen.bind(null, components.syncEngine); + eventManager.onUnlisten = syncEngineUnlisten.bind( + null, + components.syncEngine + ); + return eventManager; + }); } export function getPersistence(firestore: Firestore): Promise { diff --git a/packages/firestore/exp/test/shim.ts b/packages/firestore/exp/test/shim.ts index dc39db7f2d4..c8b3e6f085c 100644 --- a/packages/firestore/exp/test/shim.ts +++ b/packages/firestore/exp/test/shim.ts @@ -628,7 +628,8 @@ export class DocumentChange readonly newIndex = this._delegate.oldIndex; } -export class CollectionReference extends Query +export class CollectionReference + extends Query implements legacy.CollectionReference { constructor( firestore: FirebaseFirestore, diff --git a/packages/firestore/src/api/blob.ts b/packages/firestore/src/api/blob.ts index 32d40909e5f..119a07217e6 100644 --- a/packages/firestore/src/api/blob.ts +++ b/packages/firestore/src/api/blob.ts @@ -47,7 +47,10 @@ function assertBase64Available(): void { /** * Immutable class holding a blob (binary data). - * This class is directly exposed in the public API. + * + * This class is directly exposed in the public API. It extends the Bytes class + * of the firestore-exp API to support `instanceof Bytes` checks during user + * data conversion. * * Note that while you can't hide the constructor in JavaScript code, we are * using the hack above to make sure no-one outside this module can call it. diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index 2e39487c8f7..8b2c714165f 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -1016,7 +1016,8 @@ export class WriteBatch implements PublicWriteBatch { /** * A reference to a particular document in a collection in the database. */ -export class DocumentReference extends DocumentKeyReference +export class DocumentReference + extends DocumentKeyReference implements PublicDocumentReference { private _firestoreClient: FirestoreClient; @@ -1440,7 +1441,8 @@ export class DocumentSnapshot } } -export class QueryDocumentSnapshot extends DocumentSnapshot +export class QueryDocumentSnapshot + extends DocumentSnapshot implements PublicQueryDocumentSnapshot { data(options?: SnapshotOptions): T { const data = super.data(options); @@ -2306,7 +2308,8 @@ export class QuerySnapshot implements PublicQuerySnapshot { } } -export class CollectionReference extends Query +export class CollectionReference + extends Query implements PublicCollectionReference { constructor( readonly _path: ResourcePath, diff --git a/packages/firestore/src/api/field_value.ts b/packages/firestore/src/api/field_value.ts index b37c92a8d3f..d987bf53d8d 100644 --- a/packages/firestore/src/api/field_value.ts +++ b/packages/firestore/src/api/field_value.ts @@ -203,7 +203,8 @@ export class NumericIncrementFieldValueImpl extends SerializableFieldValue { } /** The public FieldValue class of the lite API. */ -export abstract class FieldValue extends SerializableFieldValue +export abstract class FieldValue + extends SerializableFieldValue implements PublicFieldValue { protected constructor() { super(); diff --git a/packages/firestore/src/core/component_provider.ts b/packages/firestore/src/core/component_provider.ts index 27f2676799d..2c60bc81276 100644 --- a/packages/firestore/src/core/component_provider.ts +++ b/packages/firestore/src/core/component_provider.ts @@ -29,9 +29,11 @@ import { import { applyActiveTargetsChange, applyBatchState, + applyOnlineStateChange, applyPrimaryState, applyTargetState, getActiveClients, + handleCredentialChange, newSyncEngine, SyncEngine } from './sync_engine'; @@ -332,20 +334,26 @@ export class OnlineComponentProvider { this.syncEngine = this.createSyncEngine(cfg); this.eventManager = this.createEventManager(cfg); + this.syncEngine.subscribe(this.eventManager); + this.sharedClientState.onlineStateHandler = onlineState => - this.syncEngine.applyOnlineStateChange( + applyOnlineStateChange( + this.syncEngine, onlineState, OnlineStateSource.SharedClientState ); - this.remoteStore.syncEngine = this.syncEngine; + this.remoteStore.remoteSyncer.handleCredentialChange = handleCredentialChange.bind( + null, + this.syncEngine + ); await this.remoteStore.start(); await this.remoteStore.applyPrimaryState(this.syncEngine.isPrimaryClient); } createEventManager(cfg: ComponentConfiguration): EventManager { - return new EventManager(this.syncEngine); + return new EventManager(); } createDatastore(cfg: ComponentConfiguration): Datastore { @@ -360,7 +368,8 @@ export class OnlineComponentProvider { this.datastore, cfg.asyncQueue, onlineState => - this.syncEngine.applyOnlineStateChange( + applyOnlineStateChange( + this.syncEngine, onlineState, OnlineStateSource.RemoteStore ), @@ -372,7 +381,6 @@ export class OnlineComponentProvider { return newSyncEngine( this.localStore, this.remoteStore, - this.datastore, this.sharedClientState, cfg.initialUser, cfg.maxConcurrentLimboResolutions, diff --git a/packages/firestore/src/core/event_manager.ts b/packages/firestore/src/core/event_manager.ts index c3823e2de98..ce13b658171 100644 --- a/packages/firestore/src/core/event_manager.ts +++ b/packages/firestore/src/core/event_manager.ts @@ -19,7 +19,7 @@ import { debugAssert } from '../util/assert'; import { EventHandler } from '../util/misc'; import { ObjectMap } from '../util/obj_map'; import { canonifyQuery, Query, queryEquals, stringifyQuery } from './query'; -import { SyncEngine, SyncEngineListener } from './sync_engine'; +import { SyncEngineListener } from './sync_engine'; import { OnlineState } from './types'; import { ChangeType, DocumentViewChange, ViewSnapshot } from './view_snapshot'; import { wrapInUserErrorIfRecoverable } from '../util/async_queue'; @@ -45,6 +45,10 @@ export interface Observer { * EventManager is responsible for mapping queries to query event emitters. * It handles "fan-out". -- Identical queries will re-use the same watch on the * backend. + * + * PORTING NOTE: On Web, EventManager `onListen` and `onUnlisten` need to be + * assigned to SyncEngine's `listen()` and `unlisten()` API before usage. This + * allows users to tree-shake the Watch logic. */ export class EventManager implements SyncEngineListener { private queries = new ObjectMap( @@ -56,11 +60,13 @@ export class EventManager implements SyncEngineListener { private snapshotsInSyncListeners: Set> = new Set(); - constructor(private syncEngine: SyncEngine) { - this.syncEngine.subscribe(this); - } + /** Callback invoked when a Query is first listen to. */ + onListen?: (query: Query) => Promise; + /** Callback invoked once all listeners to a Query are removed. */ + onUnlisten?: (query: Query) => Promise; async listen(listener: QueryListener): Promise { + debugAssert(!!this.onListen, 'onListen not set'); const query = listener.query; let firstListen = false; @@ -72,7 +78,7 @@ export class EventManager implements SyncEngineListener { if (firstListen) { try { - queryInfo.viewSnap = await this.syncEngine.listen(query); + queryInfo.viewSnap = await this.onListen(query); } catch (e) { const firestoreError = wrapInUserErrorIfRecoverable( e, @@ -102,6 +108,7 @@ export class EventManager implements SyncEngineListener { } async unlisten(listener: QueryListener): Promise { + debugAssert(!!this.onUnlisten, 'onUnlisten not set'); const query = listener.query; let lastListen = false; @@ -116,7 +123,7 @@ export class EventManager implements SyncEngineListener { if (lastListen) { this.queries.delete(query); - return this.syncEngine.unlisten(query); + return this.onUnlisten(query); } } diff --git a/packages/firestore/src/core/firestore_client.ts b/packages/firestore/src/core/firestore_client.ts index 4fed5afdbd0..176bc44ec8a 100644 --- a/packages/firestore/src/core/firestore_client.ts +++ b/packages/firestore/src/core/firestore_client.ts @@ -39,7 +39,13 @@ import { Observer, QueryListener } from './event_manager'; -import { SyncEngine } from './sync_engine'; +import { + registerPendingWritesCallback, + SyncEngine, + syncEngineListen, + syncEngineUnlisten, + syncEngineWrite +} from './sync_engine'; import { View } from './view'; import { SharedClientState } from '../local/shared_client_state'; import { AutoId } from '../util/misc'; @@ -277,6 +283,9 @@ export class FirestoreClient { this.syncEngine = onlineComponentProvider.syncEngine; this.eventMgr = onlineComponentProvider.eventManager; + this.eventMgr.onListen = syncEngineListen.bind(null, this.syncEngine); + this.eventMgr.onUnlisten = syncEngineUnlisten.bind(null, this.syncEngine); + // When a user calls clearPersistence() in one client, all other clients // need to be terminated to allow the delete to succeed. this.persistence.setDatabaseDeletedListener(async () => { @@ -405,9 +414,9 @@ export class FirestoreClient { this.verifyNotTerminated(); const deferred = new Deferred(); - this.asyncQueue.enqueueAndForget(() => { - return this.syncEngine.registerPendingWritesCallback(deferred); - }); + this.asyncQueue.enqueueAndForget(() => + registerPendingWritesCallback(this.syncEngine, deferred) + ); return deferred.promise; } @@ -480,7 +489,7 @@ export class FirestoreClient { this.verifyNotTerminated(); const deferred = new Deferred(); this.asyncQueue.enqueueAndForget(() => - this.syncEngine.write(mutations, deferred) + syncEngineWrite(this.syncEngine, mutations, deferred) ); return deferred.promise; } @@ -549,7 +558,9 @@ export function enqueueWrite( mutations: Mutation[] ): Promise { const deferred = new Deferred(); - asyncQueue.enqueueAndForget(() => syncEngine.write(mutations, deferred)); + asyncQueue.enqueueAndForget(() => + syncEngineWrite(syncEngine, mutations, deferred) + ); return deferred.promise; } @@ -570,9 +581,9 @@ export function enqueueWaitForPendingWrites( syncEngine: SyncEngine ): Promise { const deferred = new Deferred(); - asyncQueue.enqueueAndForget(() => { - return syncEngine.registerPendingWritesCallback(deferred); - }); + asyncQueue.enqueueAndForget(() => + registerPendingWritesCallback(syncEngine, deferred) + ); return deferred.promise; } diff --git a/packages/firestore/src/core/sync_engine.ts b/packages/firestore/src/core/sync_engine.ts index ebf50a9db38..02327e4aee9 100644 --- a/packages/firestore/src/core/sync_engine.ts +++ b/packages/firestore/src/core/sync_engine.ts @@ -17,7 +17,6 @@ import { User } from '../auth/user'; import { - applyRemoteEventToLocalCache, getNewDocumentChanges, getCachedTarget, ignoreIfPrimaryLeaseLoss, @@ -28,12 +27,13 @@ import { allocateTarget, executeQuery, releaseTarget, + applyRemoteEventToLocalCache, rejectBatch, + handleUserChange, + localWrite, acknowledgeBatch, getHighestUnacknowledgedBatchId, - localWrite, - notifyLocalViewChanges, - handleUserChange + notifyLocalViewChanges } from '../local/local_store'; import { LocalViewChanges } from '../local/local_view_changes'; import { ReferenceSet } from '../local/reference_set'; @@ -49,7 +49,6 @@ import { Mutation } from '../model/mutation'; import { BATCHID_UNKNOWN, MutationBatchResult } from '../model/mutation_batch'; import { RemoteEvent, TargetChange } from '../remote/remote_event'; import { RemoteStore } from '../remote/remote_store'; -import { RemoteSyncer } from '../remote/remote_syncer'; import { debugAssert, debugCast, fail, hardAssert } from '../util/assert'; import { Code, FirestoreError } from '../util/error'; import { logDebug } from '../util/log'; @@ -86,12 +85,10 @@ import { LimboDocumentChange, RemovedLimboDocument, View, - ViewChange, - ViewDocumentChanges + ViewChange } from './view'; import { ViewSnapshot } from './view_snapshot'; import { wrapInUserErrorIfRecoverable } from '../util/async_queue'; -import { Datastore } from '../remote/datastore'; const LOG_TAG = 'SyncEngine'; @@ -133,6 +130,16 @@ class LimboResolution { receivedDocument: boolean = false; } +/** + * A function that updates a QueryView with a set of document changes (and a + * remote event if applicable). + */ +type ApplyDocChangesHandler = ( + queryView: QueryView, + changes: MaybeDocumentMap, + remoteEvent?: RemoteEvent +) => Promise; + /** * Interface implemented by EventManager to handle notifications from * SyncEngine. @@ -162,63 +169,21 @@ export interface SyncEngineListener { * The SyncEngine’s methods should only ever be called by methods running in the * global async queue. */ -export interface SyncEngine extends RemoteSyncer { +export interface SyncEngine { isPrimaryClient: boolean; /** Subscribes to SyncEngine notifications. Has to be called exactly once. */ subscribe(syncEngineListener: SyncEngineListener): void; - - /** - * Initiates the new listen, resolves promise when listen enqueued to the - * server. All the subsequent view snapshots or errors are sent to the - * subscribed handlers. Returns the initial snapshot. - */ - listen(query: Query): Promise; - - /** Stops listening to the query. */ - unlisten(query: Query): Promise; - - /** - * Initiates the write of local mutation batch which involves adding the - * writes to the mutation queue, notifying the remote store about new - * mutations and raising events for any changes this write caused. - * - * The promise returned by this call is resolved when the above steps - * have completed, *not* when the write was acked by the backend. The - * userCallback is resolved once the write was acked/rejected by the - * backend (or failed locally for any other reason). - */ - write(batch: Mutation[], userCallback: Deferred): Promise; - - /** - * Applies an OnlineState change to the sync engine and notifies any views of - * the change. - */ - applyOnlineStateChange( - onlineState: OnlineState, - source: OnlineStateSource - ): void; - - /** - * Registers a user callback that resolves when all pending mutations at the moment of calling - * are acknowledged . - */ - registerPendingWritesCallback(callback: Deferred): Promise; - - // Visible for testing - activeLimboDocumentResolutions(): SortedMap; - - // Visible for testing - enqueuedLimboDocumentResolutions(): DocumentKey[]; - - handleCredentialChange(user: User): Promise; - - getRemoteKeysForTarget(targetId: TargetId): DocumentKeySet; } /** * An implementation of `SyncEngine` coordinating with other parts of SDK. * + * The parts of SyncEngine that act as a callback to RemoteStore need to be + * registered individually. This is done in `syncEngineWrite()` and + * `syncEngineListen()` (as well as `applyPrimaryState()`) as these methods + * serve as entry points to RemoteStore's functionality. + * * Note: some field defined in this class might have public access level, but * the class is not exported so they are only accessible from this module. * This is useful to implement optional features (like bundles) in free @@ -227,6 +192,15 @@ export interface SyncEngine extends RemoteSyncer { class SyncEngineImpl implements SyncEngine { syncEngineListener: SyncEngineListener | null = null; + /** + * A callback that updates the QueryView based on the provided change. + * + * PORTING NOTE: On other platforms, this logic lives in + * `emitNewSnapshotsAndNotifyLocalStore()`, but on Web it is extracted to + * ensure that all view logic only exists in bundles that include views. + */ + applyDocChanges?: ApplyDocChangesHandler; + queryViewsByQuery = new ObjectMap( q => canonifyQuery(q), queryEquals @@ -236,7 +210,7 @@ class SyncEngineImpl implements SyncEngine { * The keys of documents that are in limbo for which we haven't yet started a * limbo resolution query. */ - private enqueuedLimboResolutions: DocumentKey[] = []; + enqueuedLimboResolutions: DocumentKey[] = []; /** * Keeps track of the target ID for each document that is in limbo with an * active target. @@ -251,14 +225,14 @@ class SyncEngineImpl implements SyncEngine { activeLimboResolutionsByTarget = new Map(); limboDocumentRefs = new ReferenceSet(); /** Stores user completion handlers, indexed by User and BatchId. */ - private mutationUserCallbacks = {} as { + mutationUserCallbacks = {} as { [uidKey: string]: SortedMap>; }; /** Stores user callbacks waiting for all pending writes to be acknowledged. */ - private pendingWritesCallbacks = new Map>>(); - private limboTargetIdGenerator = TargetIdGenerator.forSyncEngine(); + pendingWritesCallbacks = new Map>>(); + limboTargetIdGenerator = TargetIdGenerator.forSyncEngine(); - private onlineState = OnlineState.Unknown; + onlineState = OnlineState.Unknown; // The primary state is set to `true` or `false` immediately after Firestore // startup. In the interim, a client should only be considered primary if @@ -266,13 +240,12 @@ class SyncEngineImpl implements SyncEngine { _isPrimaryClient: undefined | boolean = undefined; constructor( - public localStore: LocalStore, - public remoteStore: RemoteStore, - protected datastore: Datastore, + readonly localStore: LocalStore, + readonly remoteStore: RemoteStore, // PORTING NOTE: Manages state synchronization in multi-tab environments. - public sharedClientState: SharedClientState, - private currentUser: User, - private maxConcurrentLimboResolutions: number + readonly sharedClientState: SharedClientState, + public currentUser: User, + readonly maxConcurrentLimboResolutions: number ) {} get isPrimaryClient(): boolean { @@ -292,700 +265,862 @@ class SyncEngineImpl implements SyncEngine { this.syncEngineListener = syncEngineListener; } - async listen(query: Query): Promise { - this.assertSubscribed('listen()'); - - let targetId; - let viewSnapshot; - - const queryView = this.queryViewsByQuery.get(query); - if (queryView) { - // PORTING NOTE: With Multi-Tab Web, it is possible that a query view - // already exists when EventManager calls us for the first time. This - // happens when the primary tab is already listening to this query on - // behalf of another tab and the user of the primary also starts listening - // to the query. EventManager will not have an assigned target ID in this - // case and calls `listen` to obtain this ID. - targetId = queryView.targetId; - this.sharedClientState.addLocalQueryTarget(targetId); - viewSnapshot = queryView.view.computeInitialSnapshot(); - } else { - const targetData = await allocateTarget( - this.localStore, - queryToTarget(query) - ); - - const status = this.sharedClientState.addLocalQueryTarget( - targetData.targetId - ); - targetId = targetData.targetId; - viewSnapshot = await this.initializeViewAndComputeSnapshot( - query, - targetId, - status === 'current' - ); - if (this.isPrimaryClient) { - this.remoteStore.listen(targetData); - } - } + assertSubscribed(fnName: string): void { + debugAssert( + this.syncEngineListener !== null, + 'Trying to call ' + fnName + ' before calling subscribe().' + ); + } +} - return viewSnapshot; +export function newSyncEngine( + localStore: LocalStore, + remoteStore: RemoteStore, + // PORTING NOTE: Manages state synchronization in multi-tab environments. + sharedClientState: SharedClientState, + currentUser: User, + maxConcurrentLimboResolutions: number, + isPrimary: boolean +): SyncEngine { + const syncEngine = new SyncEngineImpl( + localStore, + remoteStore, + sharedClientState, + currentUser, + maxConcurrentLimboResolutions + ); + if (isPrimary) { + syncEngine._isPrimaryClient = true; } + return syncEngine; +} - /** - * Registers a view for a previously unknown query and computes its initial - * snapshot. - */ - async initializeViewAndComputeSnapshot( - query: Query, - targetId: TargetId, - current: boolean - ): Promise { - const queryResult = await executeQuery( - this.localStore, - query, - /* usePreviousResults= */ true +/** + * Initiates the new listen, resolves promise when listen enqueued to the + * server. All the subsequent view snapshots or errors are sent to the + * subscribed handlers. Returns the initial snapshot. + */ +export async function syncEngineListen( + syncEngine: SyncEngine, + query: Query +): Promise { + const syncEngineImpl = ensureWatchCallbacks(syncEngine); + syncEngineImpl.assertSubscribed('listen()'); + + let targetId; + let viewSnapshot; + + const queryView = syncEngineImpl.queryViewsByQuery.get(query); + if (queryView) { + // PORTING NOTE: With Multi-Tab Web, it is possible that a query view + // already exists when EventManager calls us for the first time. This + // happens when the primary tab is already listening to this query on + // behalf of another tab and the user of the primary also starts listening + // to the query. EventManager will not have an assigned target ID in this + // case and calls `listen` to obtain this ID. + targetId = queryView.targetId; + syncEngineImpl.sharedClientState.addLocalQueryTarget(targetId); + viewSnapshot = queryView.view.computeInitialSnapshot(); + } else { + const targetData = await allocateTarget( + syncEngineImpl.localStore, + queryToTarget(query) ); - const view = new View(query, queryResult.remoteKeys); - const viewDocChanges = view.computeDocChanges(queryResult.documents); - const synthesizedTargetChange = TargetChange.createSynthesizedTargetChangeForCurrentChange( - targetId, - current && this.onlineState !== OnlineState.Offline + + const status = syncEngineImpl.sharedClientState.addLocalQueryTarget( + targetData.targetId ); - const viewChange = view.applyChanges( - viewDocChanges, - /* updateLimboDocuments= */ this.isPrimaryClient, - synthesizedTargetChange + targetId = targetData.targetId; + viewSnapshot = await initializeViewAndComputeSnapshot( + syncEngineImpl, + query, + targetId, + status === 'current' ); - this.updateTrackedLimbos(targetId, viewChange.limboChanges); + if (syncEngineImpl.isPrimaryClient) { + syncEngineImpl.remoteStore.listen(targetData); + } + } - debugAssert( - !!viewChange.snapshot, - 'applyChanges for new view should always return a snapshot' - ); + return viewSnapshot; +} - const data = new QueryView(query, targetId, view); - this.queryViewsByQuery.set(query, data); - if (this.queriesByTarget.has(targetId)) { - this.queriesByTarget.get(targetId)!.push(query); - } else { - this.queriesByTarget.set(targetId, [query]); - } - return viewChange.snapshot!; +/** + * Registers a view for a previously unknown query and computes its initial + * snapshot. + */ +async function initializeViewAndComputeSnapshot( + syncEngineImpl: SyncEngineImpl, + query: Query, + targetId: TargetId, + current: boolean +): Promise { + // PORTING NOTE: On Web only, we inject the code that registers new Limbo + // targets based on view changes. This allows us to only depend on Limbo + // changes when user code includes queries. + syncEngineImpl.applyDocChanges = (queryView, changes, remoteEvent) => + applyDocChanges(syncEngineImpl, queryView, changes, remoteEvent); + + const queryResult = await executeQuery( + syncEngineImpl.localStore, + query, + /* usePreviousResults= */ true + ); + const view = new View(query, queryResult.remoteKeys); + const viewDocChanges = view.computeDocChanges(queryResult.documents); + const synthesizedTargetChange = TargetChange.createSynthesizedTargetChangeForCurrentChange( + targetId, + current && syncEngineImpl.onlineState !== OnlineState.Offline + ); + const viewChange = view.applyChanges( + viewDocChanges, + /* updateLimboDocuments= */ syncEngineImpl.isPrimaryClient, + synthesizedTargetChange + ); + updateTrackedLimbos(syncEngineImpl, targetId, viewChange.limboChanges); + + debugAssert( + !!viewChange.snapshot, + 'applyChanges for new view should always return a snapshot' + ); + + const data = new QueryView(query, targetId, view); + + syncEngineImpl.queryViewsByQuery.set(query, data); + if (syncEngineImpl.queriesByTarget.has(targetId)) { + syncEngineImpl.queriesByTarget.get(targetId)!.push(query); + } else { + syncEngineImpl.queriesByTarget.set(targetId, [query]); } - async unlisten(query: Query): Promise { - this.assertSubscribed('unlisten()'); + return viewChange.snapshot; +} - const queryView = this.queryViewsByQuery.get(query)!; - debugAssert( - !!queryView, - 'Trying to unlisten on query not found:' + stringifyQuery(query) - ); +/** Stops listening to the query. */ +export async function syncEngineUnlisten( + syncEngine: SyncEngine, + query: Query +): Promise { + const syncEngineImpl = debugCast(syncEngine, SyncEngineImpl); + syncEngineImpl.assertSubscribed('unlisten()'); - // Only clean up the query view and target if this is the only query mapped - // to the target. - const queries = this.queriesByTarget.get(queryView.targetId)!; - if (queries.length > 1) { - this.queriesByTarget.set( - queryView.targetId, - queries.filter(q => !queryEquals(q, query)) - ); - this.queryViewsByQuery.delete(query); - return; - } + const queryView = syncEngineImpl.queryViewsByQuery.get(query)!; + debugAssert( + !!queryView, + 'Trying to unlisten on query not found:' + stringifyQuery(query) + ); - // No other queries are mapped to the target, clean up the query and the target. - if (this.isPrimaryClient) { - // We need to remove the local query target first to allow us to verify - // whether any other client is still interested in this target. - this.sharedClientState.removeLocalQueryTarget(queryView.targetId); - const targetRemainsActive = this.sharedClientState.isActiveQueryTarget( - queryView.targetId - ); + // Only clean up the query view and target if this is the only query mapped + // to the target. + const queries = syncEngineImpl.queriesByTarget.get(queryView.targetId)!; + if (queries.length > 1) { + syncEngineImpl.queriesByTarget.set( + queryView.targetId, + queries.filter(q => !queryEquals(q, query)) + ); + syncEngineImpl.queryViewsByQuery.delete(query); + return; + } - if (!targetRemainsActive) { - await releaseTarget( - this.localStore, - queryView.targetId, - /*keepPersistedTargetData=*/ false - ) - .then(() => { - this.sharedClientState.clearQueryState(queryView.targetId); - this.remoteStore.unlisten(queryView.targetId); - this.removeAndCleanupTarget(queryView.targetId); - }) - .catch(ignoreIfPrimaryLeaseLoss); - } - } else { - this.removeAndCleanupTarget(queryView.targetId); + // No other queries are mapped to the target, clean up the query and the target. + if (syncEngineImpl.isPrimaryClient) { + // We need to remove the local query target first to allow us to verify + // whether any other client is still interested in this target. + syncEngineImpl.sharedClientState.removeLocalQueryTarget(queryView.targetId); + const targetRemainsActive = syncEngineImpl.sharedClientState.isActiveQueryTarget( + queryView.targetId + ); + + if (!targetRemainsActive) { await releaseTarget( - this.localStore, + syncEngineImpl.localStore, queryView.targetId, - /*keepPersistedTargetData=*/ true - ); + /*keepPersistedTargetData=*/ false + ) + .then(() => { + syncEngineImpl.sharedClientState.clearQueryState(queryView.targetId); + syncEngineImpl.remoteStore.unlisten(queryView.targetId); + removeAndCleanupTarget(syncEngineImpl, queryView.targetId); + }) + .catch(ignoreIfPrimaryLeaseLoss); } + } else { + removeAndCleanupTarget(syncEngineImpl, queryView.targetId); + await releaseTarget( + syncEngineImpl.localStore, + queryView.targetId, + /*keepPersistedTargetData=*/ true + ); } +} - async write(batch: Mutation[], userCallback: Deferred): Promise { - this.assertSubscribed('write()'); - - try { - const result = await localWrite(this.localStore, batch); - this.sharedClientState.addPendingMutation(result.batchId); - this.addMutationCallback(result.batchId, userCallback); - await this.emitNewSnapsAndNotifyLocalStore(result.changes); - await this.remoteStore.fillWritePipeline(); - } catch (e) { - // If we can't persist the mutation, we reject the user callback and - // don't send the mutation. The user can then retry the write. - const error = wrapInUserErrorIfRecoverable(e, `Failed to persist write`); - userCallback.reject(error); - } +/** + * Initiates the write of local mutation batch which involves adding the + * writes to the mutation queue, notifying the remote store about new + * mutations and raising events for any changes this write caused. + * + * The promise returned by this call is resolved when the above steps + * have completed, *not* when the write was acked by the backend. The + * userCallback is resolved once the write was acked/rejected by the + * backend (or failed locally for any other reason). + */ +export async function syncEngineWrite( + syncEngine: SyncEngine, + batch: Mutation[], + userCallback: Deferred +): Promise { + const syncEngineImpl = ensureWriteCallbacks(syncEngine); + syncEngineImpl.assertSubscribed('write()'); + + try { + const result = await localWrite(syncEngineImpl.localStore, batch); + syncEngineImpl.sharedClientState.addPendingMutation(result.batchId); + addMutationCallback(syncEngineImpl, result.batchId, userCallback); + await emitNewSnapsAndNotifyLocalStore(syncEngineImpl, result.changes); + await syncEngineImpl.remoteStore.fillWritePipeline(); + } catch (e) { + // If we can't persist the mutation, we reject the user callback and + // don't send the mutation. The user can then retry the write. + const error = wrapInUserErrorIfRecoverable(e, `Failed to persist write`); + userCallback.reject(error); } +} - async applyRemoteEvent(remoteEvent: RemoteEvent): Promise { - this.assertSubscribed('applyRemoteEvent()'); - try { - const changes = await applyRemoteEventToLocalCache( - this.localStore, - remoteEvent +/** + * Applies one remote event to the sync engine, notifying any views of the + * changes, and releasing any pending mutation batches that would become + * visible because of the snapshot version the remote event contains. + */ +export async function applyRemoteEvent( + syncEngine: SyncEngine, + remoteEvent: RemoteEvent +): Promise { + const syncEngineImpl = debugCast(syncEngine, SyncEngineImpl); + syncEngineImpl.assertSubscribed('applyRemoteEvent()'); + try { + const changes = await applyRemoteEventToLocalCache( + syncEngineImpl.localStore, + remoteEvent + ); + // Update `receivedDocument` as appropriate for any limbo targets. + remoteEvent.targetChanges.forEach((targetChange, targetId) => { + const limboResolution = syncEngineImpl.activeLimboResolutionsByTarget.get( + targetId ); - // Update `receivedDocument` as appropriate for any limbo targets. - remoteEvent.targetChanges.forEach((targetChange, targetId) => { - const limboResolution = this.activeLimboResolutionsByTarget.get( - targetId + if (limboResolution) { + // Since this is a limbo resolution lookup, it's for a single document + // and it could be added, modified, or removed, but not a combination. + hardAssert( + targetChange.addedDocuments.size + + targetChange.modifiedDocuments.size + + targetChange.removedDocuments.size <= + 1, + 'Limbo resolution for single document contains multiple changes.' ); - if (limboResolution) { - // Since this is a limbo resolution lookup, it's for a single document - // and it could be added, modified, or removed, but not a combination. + if (targetChange.addedDocuments.size > 0) { + limboResolution.receivedDocument = true; + } else if (targetChange.modifiedDocuments.size > 0) { hardAssert( - targetChange.addedDocuments.size + - targetChange.modifiedDocuments.size + - targetChange.removedDocuments.size <= - 1, - 'Limbo resolution for single document contains multiple changes.' + limboResolution.receivedDocument, + 'Received change for limbo target document without add.' ); - if (targetChange.addedDocuments.size > 0) { - limboResolution.receivedDocument = true; - } else if (targetChange.modifiedDocuments.size > 0) { - hardAssert( - limboResolution.receivedDocument, - 'Received change for limbo target document without add.' - ); - } else if (targetChange.removedDocuments.size > 0) { - hardAssert( - limboResolution.receivedDocument, - 'Received remove for limbo target document without add.' - ); - limboResolution.receivedDocument = false; - } else { - // This was probably just a CURRENT targetChange or similar. - } + } else if (targetChange.removedDocuments.size > 0) { + hardAssert( + limboResolution.receivedDocument, + 'Received remove for limbo target document without add.' + ); + limboResolution.receivedDocument = false; + } else { + // This was probably just a CURRENT targetChange or similar. } - }); - await this.emitNewSnapsAndNotifyLocalStore(changes, remoteEvent); - } catch (error) { - await ignoreIfPrimaryLeaseLoss(error); - } + } + }); + await emitNewSnapsAndNotifyLocalStore(syncEngineImpl, changes, remoteEvent); + } catch (error) { + await ignoreIfPrimaryLeaseLoss(error); } +} - applyOnlineStateChange( - onlineState: OnlineState, - source: OnlineStateSource - ): void { - // If we are the secondary client, we explicitly ignore the remote store's - // online state (the local client may go offline, even though the primary - // tab remains online) and only apply the primary tab's online state from - // SharedClientState. - if ( - (this.isPrimaryClient && source === OnlineStateSource.RemoteStore) || - (!this.isPrimaryClient && source === OnlineStateSource.SharedClientState) - ) { - this.assertSubscribed('applyOnlineStateChange()'); - const newViewSnapshots = [] as ViewSnapshot[]; - this.queryViewsByQuery.forEach((query, queryView) => { - const viewChange = queryView.view.applyOnlineStateChange(onlineState); - debugAssert( - viewChange.limboChanges.length === 0, - 'OnlineState should not affect limbo documents.' - ); - if (viewChange.snapshot) { - newViewSnapshots.push(viewChange.snapshot); - } - }); - this.syncEngineListener!.onOnlineStateChange(onlineState); - this.syncEngineListener!.onWatchChange(newViewSnapshots); - this.onlineState = onlineState; - if (this.isPrimaryClient) { - this.sharedClientState.setOnlineState(onlineState); +/** + * Applies an OnlineState change to the sync engine and notifies any views of + * the change. + */ +export function applyOnlineStateChange( + syncEngine: SyncEngine, + onlineState: OnlineState, + source: OnlineStateSource +): void { + const syncEngineImpl = debugCast(syncEngine, SyncEngineImpl); + // If we are the secondary client, we explicitly ignore the remote store's + // online state (the local client may go offline, even though the primary + // tab remains online) and only apply the primary tab's online state from + // SharedClientState. + if ( + (syncEngineImpl.isPrimaryClient && + source === OnlineStateSource.RemoteStore) || + (!syncEngineImpl.isPrimaryClient && + source === OnlineStateSource.SharedClientState) + ) { + syncEngineImpl.assertSubscribed('applyOnlineStateChange()'); + const newViewSnapshots = [] as ViewSnapshot[]; + syncEngineImpl.queryViewsByQuery.forEach((query, queryView) => { + const viewChange = queryView.view.applyOnlineStateChange(onlineState); + debugAssert( + viewChange.limboChanges.length === 0, + 'OnlineState should not affect limbo documents.' + ); + if (viewChange.snapshot) { + newViewSnapshots.push(viewChange.snapshot); } + }); + syncEngineImpl.syncEngineListener!.onOnlineStateChange(onlineState); + syncEngineImpl.syncEngineListener!.onWatchChange(newViewSnapshots); + syncEngineImpl.onlineState = onlineState; + if (syncEngineImpl.isPrimaryClient) { + syncEngineImpl.sharedClientState.setOnlineState(onlineState); } } +} - async rejectListen(targetId: TargetId, err: FirestoreError): Promise { - this.assertSubscribed('rejectListens()'); - - // PORTING NOTE: Multi-tab only. - this.sharedClientState.updateQueryState(targetId, 'rejected', err); +/** + * Rejects the listen for the given targetID. This can be triggered by the + * backend for any active target. + * + * @param syncEngine The sync engine implementation. + * @param targetId The targetID corresponds to one previously initiated by the + * user as part of TargetData passed to listen() on RemoteStore. + * @param err A description of the condition that has forced the rejection. + * Nearly always this will be an indication that the user is no longer + * authorized to see the data matching the target. + */ +export async function rejectListen( + syncEngine: SyncEngine, + targetId: TargetId, + err: FirestoreError +): Promise { + const syncEngineImpl = debugCast(syncEngine, SyncEngineImpl); + syncEngineImpl.assertSubscribed('rejectListens()'); - const limboResolution = this.activeLimboResolutionsByTarget.get(targetId); - const limboKey = limboResolution && limboResolution.key; - if (limboKey) { - // TODO(klimt): We really only should do the following on permission - // denied errors, but we don't have the cause code here. + // PORTING NOTE: Multi-tab only. + syncEngineImpl.sharedClientState.updateQueryState(targetId, 'rejected', err); - // It's a limbo doc. Create a synthetic event saying it was deleted. - // This is kind of a hack. Ideally, we would have a method in the local - // store to purge a document. However, it would be tricky to keep all of - // the local store's invariants with another method. - let documentUpdates = new SortedMap( - DocumentKey.comparator - ); - documentUpdates = documentUpdates.insert( - limboKey, - new NoDocument(limboKey, SnapshotVersion.min()) - ); - const resolvedLimboDocuments = documentKeySet().add(limboKey); - const event = new RemoteEvent( - SnapshotVersion.min(), - /* targetChanges= */ new Map(), - /* targetMismatches= */ new SortedSet(primitiveComparator), - documentUpdates, - resolvedLimboDocuments - ); + const limboResolution = syncEngineImpl.activeLimboResolutionsByTarget.get( + targetId + ); + const limboKey = limboResolution && limboResolution.key; + if (limboKey) { + // TODO(klimt): We really only should do the following on permission + // denied errors, but we don't have the cause code here. + + // It's a limbo doc. Create a synthetic event saying it was deleted. + // This is kind of a hack. Ideally, we would have a method in the local + // store to purge a document. However, it would be tricky to keep all of + // the local store's invariants with another method. + let documentUpdates = new SortedMap( + DocumentKey.comparator + ); + documentUpdates = documentUpdates.insert( + limboKey, + new NoDocument(limboKey, SnapshotVersion.min()) + ); + const resolvedLimboDocuments = documentKeySet().add(limboKey); + const event = new RemoteEvent( + SnapshotVersion.min(), + /* targetChanges= */ new Map(), + /* targetMismatches= */ new SortedSet(primitiveComparator), + documentUpdates, + resolvedLimboDocuments + ); - await this.applyRemoteEvent(event); + await applyRemoteEvent(syncEngineImpl, event); - // Since this query failed, we won't want to manually unlisten to it. - // We only remove it from bookkeeping after we successfully applied the - // RemoteEvent. If `applyRemoteEvent()` throws, we want to re-listen to - // this query when the RemoteStore restarts the Watch stream, which should - // re-trigger the target failure. - this.activeLimboTargetsByKey = this.activeLimboTargetsByKey.remove( - limboKey - ); - this.activeLimboResolutionsByTarget.delete(targetId); - this.pumpEnqueuedLimboResolutions(); - } else { - await releaseTarget( - this.localStore, - targetId, - /* keepPersistedTargetData */ false - ) - .then(() => this.removeAndCleanupTarget(targetId, err)) - .catch(ignoreIfPrimaryLeaseLoss); - } + // Since this query failed, we won't want to manually unlisten to it. + // We only remove it from bookkeeping after we successfully applied the + // RemoteEvent. If `applyRemoteEvent()` throws, we want to re-listen to + // this query when the RemoteStore restarts the Watch stream, which should + // re-trigger the target failure. + syncEngineImpl.activeLimboTargetsByKey = syncEngineImpl.activeLimboTargetsByKey.remove( + limboKey + ); + syncEngineImpl.activeLimboResolutionsByTarget.delete(targetId); + pumpEnqueuedLimboResolutions(syncEngineImpl); + } else { + await releaseTarget( + syncEngineImpl.localStore, + targetId, + /* keepPersistedTargetData */ false + ) + .then(() => removeAndCleanupTarget(syncEngineImpl, targetId, err)) + .catch(ignoreIfPrimaryLeaseLoss); } +} - async applySuccessfulWrite( - mutationBatchResult: MutationBatchResult - ): Promise { - this.assertSubscribed('applySuccessfulWrite()'); +export async function applySuccessfulWrite( + syncEngine: SyncEngine, + mutationBatchResult: MutationBatchResult +): Promise { + const syncEngineImpl = debugCast(syncEngine, SyncEngineImpl); + syncEngineImpl.assertSubscribed('applySuccessfulWrite()'); - const batchId = mutationBatchResult.batch.batchId; + const batchId = mutationBatchResult.batch.batchId; - try { - const changes = await acknowledgeBatch( - this.localStore, - mutationBatchResult - ); + try { + const changes = await acknowledgeBatch( + syncEngineImpl.localStore, + mutationBatchResult + ); - // The local store may or may not be able to apply the write result and - // raise events immediately (depending on whether the watcher is caught - // up), so we raise user callbacks first so that they consistently happen - // before listen events. - this.processUserCallback(batchId, /*error=*/ null); - this.triggerPendingWritesCallbacks(batchId); - - this.sharedClientState.updateMutationState(batchId, 'acknowledged'); - await this.emitNewSnapsAndNotifyLocalStore(changes); - } catch (error) { - await ignoreIfPrimaryLeaseLoss(error); - } - } + // The local store may or may not be able to apply the write result and + // raise events immediately (depending on whether the watcher is caught + // up), so we raise user callbacks first so that they consistently happen + // before listen events. + processUserCallback(syncEngineImpl, batchId, /*error=*/ null); + triggerPendingWritesCallbacks(syncEngineImpl, batchId); - async rejectFailedWrite( - batchId: BatchId, - error: FirestoreError - ): Promise { - this.assertSubscribed('rejectFailedWrite()'); - - try { - const changes = await rejectBatch(this.localStore, batchId); - - // The local store may or may not be able to apply the write result and - // raise events immediately (depending on whether the watcher is caught up), - // so we raise user callbacks first so that they consistently happen before - // listen events. - this.processUserCallback(batchId, error); - this.triggerPendingWritesCallbacks(batchId); - - this.sharedClientState.updateMutationState(batchId, 'rejected', error); - await this.emitNewSnapsAndNotifyLocalStore(changes); - } catch (error) { - await ignoreIfPrimaryLeaseLoss(error); - } + syncEngineImpl.sharedClientState.updateMutationState( + batchId, + 'acknowledged' + ); + await emitNewSnapsAndNotifyLocalStore(syncEngineImpl, changes); + } catch (error) { + await ignoreIfPrimaryLeaseLoss(error); } +} - async registerPendingWritesCallback(callback: Deferred): Promise { - if (!this.remoteStore.canUseNetwork()) { - logDebug( - LOG_TAG, - 'The network is disabled. The task returned by ' + - "'awaitPendingWrites()' will not complete until the network is enabled." - ); - } +export async function rejectFailedWrite( + syncEngine: SyncEngine, + batchId: BatchId, + error: FirestoreError +): Promise { + const syncEngineImpl = debugCast(syncEngine, SyncEngineImpl); + syncEngineImpl.assertSubscribed('rejectFailedWrite()'); + + try { + const changes = await rejectBatch(syncEngineImpl.localStore, batchId); + + // The local store may or may not be able to apply the write result and + // raise events immediately (depending on whether the watcher is caught up), + // so we raise user callbacks first so that they consistently happen before + // listen events. + processUserCallback(syncEngineImpl, batchId, error); + triggerPendingWritesCallbacks(syncEngineImpl, batchId); + + syncEngineImpl.sharedClientState.updateMutationState( + batchId, + 'rejected', + error + ); + await emitNewSnapsAndNotifyLocalStore(syncEngineImpl, changes); + } catch (error) { + await ignoreIfPrimaryLeaseLoss(error); + } +} - try { - const highestBatchId = await getHighestUnacknowledgedBatchId( - this.localStore - ); - if (highestBatchId === BATCHID_UNKNOWN) { - // Trigger the callback right away if there is no pending writes at the moment. - callback.resolve(); - return; - } +/** + * Registers a user callback that resolves when all pending mutations at the moment of calling + * are acknowledged . + */ +export async function registerPendingWritesCallback( + syncEngine: SyncEngine, + callback: Deferred +): Promise { + const syncEngineImpl = debugCast(syncEngine, SyncEngineImpl); + if (!syncEngineImpl.remoteStore.canUseNetwork()) { + logDebug( + LOG_TAG, + 'The network is disabled. The task returned by ' + + "'awaitPendingWrites()' will not complete until the network is enabled." + ); + } - const callbacks = this.pendingWritesCallbacks.get(highestBatchId) || []; - callbacks.push(callback); - this.pendingWritesCallbacks.set(highestBatchId, callbacks); - } catch (e) { - const firestoreError = wrapInUserErrorIfRecoverable( - e, - 'Initialization of waitForPendingWrites() operation failed' - ); - callback.reject(firestoreError); + try { + const highestBatchId = await getHighestUnacknowledgedBatchId( + syncEngineImpl.localStore + ); + if (highestBatchId === BATCHID_UNKNOWN) { + // Trigger the callback right away if there is no pending writes at the moment. + callback.resolve(); + return; } + + const callbacks = + syncEngineImpl.pendingWritesCallbacks.get(highestBatchId) || []; + callbacks.push(callback); + syncEngineImpl.pendingWritesCallbacks.set(highestBatchId, callbacks); + } catch (e) { + const firestoreError = wrapInUserErrorIfRecoverable( + e, + 'Initialization of waitForPendingWrites() operation failed' + ); + callback.reject(firestoreError); } +} - /** - * Triggers the callbacks that are waiting for this batch id to get acknowledged by server, - * if there are any. - */ - private triggerPendingWritesCallbacks(batchId: BatchId): void { - (this.pendingWritesCallbacks.get(batchId) || []).forEach(callback => { +/** + * Triggers the callbacks that are waiting for this batch id to get acknowledged by server, + * if there are any. + */ +function triggerPendingWritesCallbacks( + syncEngineImpl: SyncEngineImpl, + batchId: BatchId +): void { + (syncEngineImpl.pendingWritesCallbacks.get(batchId) || []).forEach( + callback => { callback.resolve(); - }); + } + ); - this.pendingWritesCallbacks.delete(batchId); - } + syncEngineImpl.pendingWritesCallbacks.delete(batchId); +} - /** Reject all outstanding callbacks waiting for pending writes to complete. */ - private rejectOutstandingPendingWritesCallbacks(errorMessage: string): void { - this.pendingWritesCallbacks.forEach(callbacks => { - callbacks.forEach(callback => { - callback.reject(new FirestoreError(Code.CANCELLED, errorMessage)); - }); +/** Reject all outstanding callbacks waiting for pending writes to complete. */ +function rejectOutstandingPendingWritesCallbacks( + syncEngineImpl: SyncEngineImpl, + errorMessage: string +): void { + syncEngineImpl.pendingWritesCallbacks.forEach(callbacks => { + callbacks.forEach(callback => { + callback.reject(new FirestoreError(Code.CANCELLED, errorMessage)); }); + }); - this.pendingWritesCallbacks.clear(); + syncEngineImpl.pendingWritesCallbacks.clear(); +} + +function addMutationCallback( + syncEngineImpl: SyncEngineImpl, + batchId: BatchId, + callback: Deferred +): void { + let newCallbacks = + syncEngineImpl.mutationUserCallbacks[syncEngineImpl.currentUser.toKey()]; + if (!newCallbacks) { + newCallbacks = new SortedMap>(primitiveComparator); } + newCallbacks = newCallbacks.insert(batchId, callback); + syncEngineImpl.mutationUserCallbacks[ + syncEngineImpl.currentUser.toKey() + ] = newCallbacks; +} - private addMutationCallback( - batchId: BatchId, - callback: Deferred - ): void { - let newCallbacks = this.mutationUserCallbacks[this.currentUser.toKey()]; - if (!newCallbacks) { - newCallbacks = new SortedMap>( - primitiveComparator +/** + * Resolves or rejects the user callback for the given batch and then discards + * it. + */ +export function processUserCallback( + syncEngine: SyncEngine, + batchId: BatchId, + error: Error | null +): void { + const syncEngineImpl = debugCast(syncEngine, SyncEngineImpl); + let newCallbacks = + syncEngineImpl.mutationUserCallbacks[syncEngineImpl.currentUser.toKey()]; + + // NOTE: Mutations restored from persistence won't have callbacks, so it's + // okay for there to be no callback for this ID. + if (newCallbacks) { + const callback = newCallbacks.get(batchId); + if (callback) { + debugAssert( + batchId === newCallbacks.minKey(), + 'Mutation callbacks processed out-of-order?' ); + if (error) { + callback.reject(error); + } else { + callback.resolve(); + } + newCallbacks = newCallbacks.remove(batchId); } - newCallbacks = newCallbacks.insert(batchId, callback); - this.mutationUserCallbacks[this.currentUser.toKey()] = newCallbacks; + syncEngineImpl.mutationUserCallbacks[ + syncEngineImpl.currentUser.toKey() + ] = newCallbacks; } +} - /** - * Resolves or rejects the user callback for the given batch and then discards - * it. - */ - processUserCallback(batchId: BatchId, error: Error | null): void { - let newCallbacks = this.mutationUserCallbacks[this.currentUser.toKey()]; - - // NOTE: Mutations restored from persistence won't have callbacks, so it's - // okay for there to be no callback for this ID. - if (newCallbacks) { - const callback = newCallbacks.get(batchId); - if (callback) { - debugAssert( - batchId === newCallbacks.minKey(), - 'Mutation callbacks processed out-of-order?' - ); - if (error) { - callback.reject(error); - } else { - callback.resolve(); - } - newCallbacks = newCallbacks.remove(batchId); - } - this.mutationUserCallbacks[this.currentUser.toKey()] = newCallbacks; +function removeAndCleanupTarget( + syncEngineImpl: SyncEngineImpl, + targetId: number, + error: Error | null = null +): void { + syncEngineImpl.sharedClientState.removeLocalQueryTarget(targetId); + + debugAssert( + syncEngineImpl.queriesByTarget.has(targetId) && + syncEngineImpl.queriesByTarget.get(targetId)!.length !== 0, + `There are no queries mapped to target id ${targetId}` + ); + + for (const query of syncEngineImpl.queriesByTarget.get(targetId)!) { + syncEngineImpl.queryViewsByQuery.delete(query); + if (error) { + syncEngineImpl.syncEngineListener!.onWatchError(query, error); } } - removeAndCleanupTarget(targetId: number, error: Error | null = null): void { - this.sharedClientState.removeLocalQueryTarget(targetId); + syncEngineImpl.queriesByTarget.delete(targetId); - debugAssert( - this.queriesByTarget.has(targetId) && - this.queriesByTarget.get(targetId)!.length !== 0, - `There are no queries mapped to target id ${targetId}` + if (syncEngineImpl.isPrimaryClient) { + const limboKeys = syncEngineImpl.limboDocumentRefs.removeReferencesForId( + targetId ); - - for (const query of this.queriesByTarget.get(targetId)!) { - this.queryViewsByQuery.delete(query); - if (error) { - this.syncEngineListener!.onWatchError(query, error); + limboKeys.forEach(limboKey => { + const isReferenced = syncEngineImpl.limboDocumentRefs.containsKey( + limboKey + ); + if (!isReferenced) { + // We removed the last reference for this key + removeLimboTarget(syncEngineImpl, limboKey); } - } - - this.queriesByTarget.delete(targetId); - - if (this.isPrimaryClient) { - const limboKeys = this.limboDocumentRefs.removeReferencesForId(targetId); - limboKeys.forEach(limboKey => { - const isReferenced = this.limboDocumentRefs.containsKey(limboKey); - if (!isReferenced) { - // We removed the last reference for this key - this.removeLimboTarget(limboKey); - } - }); - } + }); } +} - private removeLimboTarget(key: DocumentKey): void { - // It's possible that the target already got removed because the query failed. In that case, - // the key won't exist in `limboTargetsByKey`. Only do the cleanup if we still have the target. - const limboTargetId = this.activeLimboTargetsByKey.get(key); - if (limboTargetId === null) { - // This target already got removed, because the query failed. - return; - } - - this.remoteStore.unlisten(limboTargetId); - this.activeLimboTargetsByKey = this.activeLimboTargetsByKey.remove(key); - this.activeLimboResolutionsByTarget.delete(limboTargetId); - this.pumpEnqueuedLimboResolutions(); +function removeLimboTarget( + syncEngineImpl: SyncEngineImpl, + key: DocumentKey +): void { + // It's possible that the target already got removed because the query failed. In that case, + // the key won't exist in `limboTargetsByKey`. Only do the cleanup if we still have the target. + const limboTargetId = syncEngineImpl.activeLimboTargetsByKey.get(key); + if (limboTargetId === null) { + // This target already got removed, because the query failed. + return; } - updateTrackedLimbos( - targetId: TargetId, - limboChanges: LimboDocumentChange[] - ): void { - for (const limboChange of limboChanges) { - if (limboChange instanceof AddedLimboDocument) { - this.limboDocumentRefs.addReference(limboChange.key, targetId); - this.trackLimboChange(limboChange); - } else if (limboChange instanceof RemovedLimboDocument) { - logDebug(LOG_TAG, 'Document no longer in limbo: ' + limboChange.key); - this.limboDocumentRefs.removeReference(limboChange.key, targetId); - const isReferenced = this.limboDocumentRefs.containsKey( - limboChange.key - ); - if (!isReferenced) { - // We removed the last reference for this key - this.removeLimboTarget(limboChange.key); - } - } else { - fail('Unknown limbo change: ' + JSON.stringify(limboChange)); + syncEngineImpl.remoteStore.unlisten(limboTargetId); + syncEngineImpl.activeLimboTargetsByKey = syncEngineImpl.activeLimboTargetsByKey.remove( + key + ); + syncEngineImpl.activeLimboResolutionsByTarget.delete(limboTargetId); + pumpEnqueuedLimboResolutions(syncEngineImpl); +} + +function updateTrackedLimbos( + syncEngineImpl: SyncEngineImpl, + targetId: TargetId, + limboChanges: LimboDocumentChange[] +): void { + for (const limboChange of limboChanges) { + if (limboChange instanceof AddedLimboDocument) { + syncEngineImpl.limboDocumentRefs.addReference(limboChange.key, targetId); + trackLimboChange(syncEngineImpl, limboChange); + } else if (limboChange instanceof RemovedLimboDocument) { + logDebug(LOG_TAG, 'Document no longer in limbo: ' + limboChange.key); + syncEngineImpl.limboDocumentRefs.removeReference( + limboChange.key, + targetId + ); + const isReferenced = syncEngineImpl.limboDocumentRefs.containsKey( + limboChange.key + ); + if (!isReferenced) { + // We removed the last reference for this key + removeLimboTarget(syncEngineImpl, limboChange.key); } + } else { + fail('Unknown limbo change: ' + JSON.stringify(limboChange)); } } +} - private trackLimboChange(limboChange: AddedLimboDocument): void { - const key = limboChange.key; - if (!this.activeLimboTargetsByKey.get(key)) { - logDebug(LOG_TAG, 'New document in limbo: ' + key); - this.enqueuedLimboResolutions.push(key); - this.pumpEnqueuedLimboResolutions(); - } +function trackLimboChange( + syncEngineImpl: SyncEngineImpl, + limboChange: AddedLimboDocument +): void { + const key = limboChange.key; + if (!syncEngineImpl.activeLimboTargetsByKey.get(key)) { + logDebug(LOG_TAG, 'New document in limbo: ' + key); + syncEngineImpl.enqueuedLimboResolutions.push(key); + pumpEnqueuedLimboResolutions(syncEngineImpl); } +} - /** - * Starts listens for documents in limbo that are enqueued for resolution, - * subject to a maximum number of concurrent resolutions. - * - * Without bounding the number of concurrent resolutions, the server can fail - * with "resource exhausted" errors which can lead to pathological client - * behavior as seen in https://github.com/firebase/firebase-js-sdk/issues/2683. - */ - private pumpEnqueuedLimboResolutions(): void { - while ( - this.enqueuedLimboResolutions.length > 0 && - this.activeLimboTargetsByKey.size < this.maxConcurrentLimboResolutions - ) { - const key = this.enqueuedLimboResolutions.shift()!; - const limboTargetId = this.limboTargetIdGenerator.next(); - this.activeLimboResolutionsByTarget.set( +/** + * Starts listens for documents in limbo that are enqueued for resolution, + * subject to a maximum number of concurrent resolutions. + * + * Without bounding the number of concurrent resolutions, the server can fail + * with "resource exhausted" errors which can lead to pathological client + * behavior as seen in https://github.com/firebase/firebase-js-sdk/issues/2683. + */ +function pumpEnqueuedLimboResolutions(syncEngineImpl: SyncEngineImpl): void { + while ( + syncEngineImpl.enqueuedLimboResolutions.length > 0 && + syncEngineImpl.activeLimboTargetsByKey.size < + syncEngineImpl.maxConcurrentLimboResolutions + ) { + const key = syncEngineImpl.enqueuedLimboResolutions.shift()!; + const limboTargetId = syncEngineImpl.limboTargetIdGenerator.next(); + syncEngineImpl.activeLimboResolutionsByTarget.set( + limboTargetId, + new LimboResolution(key) + ); + syncEngineImpl.activeLimboTargetsByKey = syncEngineImpl.activeLimboTargetsByKey.insert( + key, + limboTargetId + ); + syncEngineImpl.remoteStore.listen( + new TargetData( + queryToTarget(newQueryForPath(key.path)), limboTargetId, - new LimboResolution(key) - ); - this.activeLimboTargetsByKey = this.activeLimboTargetsByKey.insert( - key, - limboTargetId - ); - this.remoteStore.listen( - new TargetData( - queryToTarget(newQueryForPath(key.path)), - limboTargetId, - TargetPurpose.LimboResolution, - ListenSequence.INVALID - ) - ); - } + TargetPurpose.LimboResolution, + ListenSequence.INVALID + ) + ); } +} - // Visible for testing - activeLimboDocumentResolutions(): SortedMap { - return this.activeLimboTargetsByKey; - } +// Visible for testing +export function activeLimboDocumentResolutions( + syncEngine: SyncEngine +): SortedMap { + const syncEngineImpl = debugCast(syncEngine, SyncEngineImpl); + return syncEngineImpl.activeLimboTargetsByKey; +} - // Visible for testing - enqueuedLimboDocumentResolutions(): DocumentKey[] { - return this.enqueuedLimboResolutions; - } +// Visible for testing +export function enqueuedLimboDocumentResolutions( + syncEngine: SyncEngine +): DocumentKey[] { + const syncEngineImpl = debugCast(syncEngine, SyncEngineImpl); + return syncEngineImpl.enqueuedLimboResolutions; +} - async emitNewSnapsAndNotifyLocalStore( - changes: MaybeDocumentMap, - remoteEvent?: RemoteEvent - ): Promise { - const newSnaps: ViewSnapshot[] = []; - const docChangesInAllViews: LocalViewChanges[] = []; - const queriesProcessed: Array> = []; - - this.queryViewsByQuery.forEach((_, queryView) => { - queriesProcessed.push( - Promise.resolve() - .then(() => { - const viewDocChanges = queryView.view.computeDocChanges(changes); - if (!viewDocChanges.needsRefill) { - return viewDocChanges; - } - // The query has a limit and some docs were removed, so we need - // to re-run the query against the local store to make sure we - // didn't lose any good docs that had been past the limit. - return executeQuery( - this.localStore, - queryView.query, - /* usePreviousResults= */ false - ).then(({ documents }) => { - return queryView.view.computeDocChanges( - documents, - viewDocChanges - ); - }); - }) - .then((viewDocChanges: ViewDocumentChanges) => { - const targetChange = - remoteEvent && remoteEvent.targetChanges.get(queryView.targetId); - const viewChange = queryView.view.applyChanges( - viewDocChanges, - /* updateLimboDocuments= */ this.isPrimaryClient, - targetChange - ); - this.updateTrackedLimbos( - queryView.targetId, - viewChange.limboChanges - ); - if (viewChange.snapshot) { - if (this.isPrimaryClient) { - this.sharedClientState.updateQueryState( - queryView.targetId, - viewChange.snapshot.fromCache ? 'not-current' : 'current' - ); - } - - newSnaps.push(viewChange.snapshot); - const docChanges = LocalViewChanges.fromSnapshot( +export async function emitNewSnapsAndNotifyLocalStore( + syncEngine: SyncEngine, + changes: MaybeDocumentMap, + remoteEvent?: RemoteEvent +): Promise { + const syncEngineImpl = debugCast(syncEngine, SyncEngineImpl); + const newSnaps: ViewSnapshot[] = []; + const docChangesInAllViews: LocalViewChanges[] = []; + const queriesProcessed: Array> = []; + + syncEngineImpl.queryViewsByQuery.forEach((_, queryView) => { + debugAssert( + !!syncEngineImpl.applyDocChanges, + 'ApplyDocChangesHandler not set' + ); + queriesProcessed.push( + syncEngineImpl + .applyDocChanges(queryView, changes, remoteEvent) + .then(viewSnapshot => { + if (viewSnapshot) { + if (syncEngineImpl.isPrimaryClient) { + syncEngineImpl.sharedClientState.updateQueryState( queryView.targetId, - viewChange.snapshot + viewSnapshot.fromCache ? 'not-current' : 'current' ); - docChangesInAllViews.push(docChanges); } - }) - ); - }); + newSnaps.push(viewSnapshot); + const docChanges = LocalViewChanges.fromSnapshot( + queryView.targetId, + viewSnapshot + ); + docChangesInAllViews.push(docChanges); + } + }) + ); + }); - await Promise.all(queriesProcessed); - this.syncEngineListener!.onWatchChange(newSnaps); - await notifyLocalViewChanges(this.localStore, docChangesInAllViews); - } + await Promise.all(queriesProcessed); + syncEngineImpl.syncEngineListener!.onWatchChange(newSnaps); + await notifyLocalViewChanges(syncEngineImpl.localStore, docChangesInAllViews); +} - assertSubscribed(fnName: string): void { - debugAssert( - this.syncEngineListener !== null, - 'Trying to call ' + fnName + ' before calling subscribe().' - ); +async function applyDocChanges( + syncEngineImpl: SyncEngineImpl, + queryView: QueryView, + changes: MaybeDocumentMap, + remoteEvent?: RemoteEvent +): Promise { + let viewDocChanges = queryView.view.computeDocChanges(changes); + if (viewDocChanges.needsRefill) { + // The query has a limit and some docs were removed, so we need + // to re-run the query against the local store to make sure we + // didn't lose any good docs that had been past the limit. + viewDocChanges = await executeQuery( + syncEngineImpl.localStore, + queryView.query, + /* usePreviousResults= */ false + ).then(({ documents }) => { + return queryView.view.computeDocChanges(documents, viewDocChanges); + }); } - async handleCredentialChange(user: User): Promise { - const userChanged = !this.currentUser.isEqual(user); + const targetChange = + remoteEvent && remoteEvent.targetChanges.get(queryView.targetId); + const viewChange = queryView.view.applyChanges( + viewDocChanges, + /* updateLimboDocuments= */ syncEngineImpl.isPrimaryClient, + targetChange + ); + updateTrackedLimbos( + syncEngineImpl, + queryView.targetId, + viewChange.limboChanges + ); + return viewChange.snapshot; +} - if (userChanged) { - logDebug(LOG_TAG, 'User change. New user:', user.toKey()); +export async function handleCredentialChange( + syncEngine: SyncEngine, + user: User +): Promise { + const syncEngineImpl = debugCast(syncEngine, SyncEngineImpl); + const userChanged = !syncEngineImpl.currentUser.isEqual(user); - const result = await handleUserChange(this.localStore, user); - this.currentUser = user; + if (userChanged) { + logDebug(LOG_TAG, 'User change. New user:', user.toKey()); - // Fails tasks waiting for pending writes requested by previous user. - this.rejectOutstandingPendingWritesCallbacks( - "'waitForPendingWrites' promise is rejected due to a user change." - ); - // TODO(b/114226417): Consider calling this only in the primary tab. - this.sharedClientState.handleUserChange( - user, - result.removedBatchIds, - result.addedBatchIds - ); - await this.emitNewSnapsAndNotifyLocalStore(result.affectedDocuments); - } - } + const result = await handleUserChange(syncEngineImpl.localStore, user); + syncEngineImpl.currentUser = user; - getRemoteKeysForTarget(targetId: TargetId): DocumentKeySet { - const limboResolution = this.activeLimboResolutionsByTarget.get(targetId); - if (limboResolution && limboResolution.receivedDocument) { - return documentKeySet().add(limboResolution.key); - } else { - let keySet = documentKeySet(); - const queries = this.queriesByTarget.get(targetId); - if (!queries) { - return keySet; - } - for (const query of queries) { - const queryView = this.queryViewsByQuery.get(query); - debugAssert( - !!queryView, - `No query view found for ${stringifyQuery(query)}` - ); - keySet = keySet.unionWith(queryView.view.syncedDocuments); - } - return keySet; - } + // Fails tasks waiting for pending writes requested by previous user. + rejectOutstandingPendingWritesCallbacks( + syncEngineImpl, + "'waitForPendingWrites' promise is rejected due to a user change." + ); + // TODO(b/114226417): Consider calling this only in the primary tab. + syncEngineImpl.sharedClientState.handleUserChange( + user, + result.removedBatchIds, + result.addedBatchIds + ); + await emitNewSnapsAndNotifyLocalStore( + syncEngineImpl, + result.affectedDocuments + ); } } -export function newSyncEngine( - localStore: LocalStore, - remoteStore: RemoteStore, - datastore: Datastore, - // PORTING NOTE: Manages state synchronization in multi-tab environments. - sharedClientState: SharedClientState, - currentUser: User, - maxConcurrentLimboResolutions: number, - isPrimary: boolean -): SyncEngine { - const syncEngine = new SyncEngineImpl( - localStore, - remoteStore, - datastore, - sharedClientState, - currentUser, - maxConcurrentLimboResolutions +export function getRemoteKeysForTarget( + syncEngine: SyncEngine, + targetId: TargetId +): DocumentKeySet { + const syncEngineImpl = debugCast(syncEngine, SyncEngineImpl); + const limboResolution = syncEngineImpl.activeLimboResolutionsByTarget.get( + targetId ); - if (isPrimary) { - syncEngine._isPrimaryClient = true; + if (limboResolution && limboResolution.receivedDocument) { + return documentKeySet().add(limboResolution.key); + } else { + let keySet = documentKeySet(); + const queries = syncEngineImpl.queriesByTarget.get(targetId); + if (!queries) { + return keySet; + } + for (const query of queries) { + const queryView = syncEngineImpl.queryViewsByQuery.get(query); + debugAssert( + !!queryView, + `No query view found for ${stringifyQuery(query)}` + ); + keySet = keySet.unionWith(queryView.view.syncedDocuments); + } + return keySet; } - return syncEngine; } /** @@ -1006,7 +1141,8 @@ async function synchronizeViewAndComputeSnapshot( queryResult ); if (syncEngineImpl.isPrimaryClient) { - syncEngineImpl.updateTrackedLimbos( + updateTrackedLimbos( + syncEngineImpl, queryView.targetId, viewSnapshot.limboChanges ); @@ -1049,13 +1185,13 @@ export async function applyBatchState( } else if (batchState === 'acknowledged' || batchState === 'rejected') { // NOTE: Both these methods are no-ops for batches that originated from // other clients. - syncEngineImpl.processUserCallback(batchId, error ? error : null); + processUserCallback(syncEngineImpl, batchId, error ? error : null); removeCachedMutationBatchMetadata(syncEngineImpl.localStore, batchId); } else { fail(`Unknown batchState: ${batchState}`); } - await syncEngineImpl.emitNewSnapsAndNotifyLocalStore(documents); + await emitNewSnapsAndNotifyLocalStore(syncEngineImpl, documents); } /** Applies a query target change from a different tab. */ @@ -1065,6 +1201,8 @@ export async function applyPrimaryState( isPrimary: boolean ): Promise { const syncEngineImpl = debugCast(syncEngine, SyncEngineImpl); + ensureWatchCallbacks(syncEngineImpl); + ensureWriteCallbacks(syncEngineImpl); if (isPrimary === true && syncEngineImpl._isPrimaryClient !== true) { // Secondary tabs only maintain Views for their local listeners and the // Views internal state may not be 100% populated (in particular @@ -1092,7 +1230,7 @@ export async function applyPrimaryState( activeTargets.push(targetId); } else { p = p.then(() => { - syncEngineImpl.removeAndCleanupTarget(targetId); + removeAndCleanupTarget(syncEngineImpl, targetId); return releaseTarget( syncEngineImpl.localStore, targetId, @@ -1136,6 +1274,7 @@ function resetLimboDocuments(syncEngine: SyncEngine): void { * persistence. Raises snapshots for any changes that affect the local * client and returns the updated state of all target's query data. * + * @param syncEngine The sync engine implementation * @param targets the list of targets with views that need to be recomputed * @param transitionToPrimary `true` iff the tab transitions from a secondary * tab to a primary tab @@ -1188,7 +1327,8 @@ async function synchronizeQueryViewsAndRaiseSnapshots( const target = await getCachedTarget(syncEngineImpl.localStore, targetId); debugAssert(!!target, `Target for id ${targetId} not found`); targetData = await allocateTarget(syncEngineImpl.localStore, target); - await syncEngineImpl.initializeViewAndComputeSnapshot( + await initializeViewAndComputeSnapshot( + syncEngineImpl, synthesizeTargetToQuery(target!), targetId, /*current=*/ false @@ -1258,7 +1398,8 @@ export async function applyTargetState( targetId, state === 'current' ); - await syncEngineImpl.emitNewSnapsAndNotifyLocalStore( + await emitNewSnapsAndNotifyLocalStore( + syncEngineImpl, changes, synthesizedRemoteEvent ); @@ -1270,7 +1411,7 @@ export async function applyTargetState( targetId, /* keepPersistedTargetData */ true ); - syncEngineImpl.removeAndCleanupTarget(targetId, error); + removeAndCleanupTarget(syncEngineImpl, targetId, error); break; } default: @@ -1285,7 +1426,7 @@ export async function applyActiveTargetsChange( added: TargetId[], removed: TargetId[] ): Promise { - const syncEngineImpl = debugCast(syncEngine, SyncEngineImpl); + const syncEngineImpl = ensureWatchCallbacks(syncEngine); if (!syncEngineImpl._isPrimaryClient) { return; } @@ -1300,7 +1441,8 @@ export async function applyActiveTargetsChange( const target = await getCachedTarget(syncEngineImpl.localStore, targetId); debugAssert(!!target, `Query data for active target ${targetId} not found`); const targetData = await allocateTarget(syncEngineImpl.localStore, target); - await syncEngineImpl.initializeViewAndComputeSnapshot( + await initializeViewAndComputeSnapshot( + syncEngineImpl, synthesizeTargetToQuery(target), targetData.targetId, /*current=*/ false @@ -1323,8 +1465,38 @@ export async function applyActiveTargetsChange( ) .then(() => { syncEngineImpl.remoteStore.unlisten(targetId); - syncEngineImpl.removeAndCleanupTarget(targetId); + removeAndCleanupTarget(syncEngineImpl, targetId); }) .catch(ignoreIfPrimaryLeaseLoss); } } + +function ensureWatchCallbacks(syncEngine: SyncEngine): SyncEngineImpl { + const syncEngineImpl = debugCast(syncEngine, SyncEngineImpl); + syncEngineImpl.remoteStore.remoteSyncer.applyRemoteEvent = applyRemoteEvent.bind( + null, + syncEngineImpl + ); + syncEngineImpl.remoteStore.remoteSyncer.getRemoteKeysForTarget = getRemoteKeysForTarget.bind( + null, + syncEngineImpl + ); + syncEngineImpl.remoteStore.remoteSyncer.rejectListen = rejectListen.bind( + null, + syncEngineImpl + ); + return syncEngineImpl; +} + +function ensureWriteCallbacks(syncEngine: SyncEngine): SyncEngineImpl { + const syncEngineImpl = debugCast(syncEngine, SyncEngineImpl); + syncEngineImpl.remoteStore.remoteSyncer.applySuccessfulWrite = applySuccessfulWrite.bind( + null, + syncEngineImpl + ); + syncEngineImpl.remoteStore.remoteSyncer.rejectFailedWrite = rejectFailedWrite.bind( + null, + syncEngineImpl + ); + return syncEngineImpl; +} diff --git a/packages/firestore/src/remote/remote_store.ts b/packages/firestore/src/remote/remote_store.ts index 1c9e03ad083..934c97484c4 100644 --- a/packages/firestore/src/remote/remote_store.ts +++ b/packages/firestore/src/remote/remote_store.ts @@ -192,10 +192,10 @@ export class RemoteStore implements TargetMetadataProvider { } /** - * SyncEngine to notify of watch and write events. This must be set - * immediately after construction. + * SyncEngine callbacks to notify of watch and write events. Individual + * callbacks must be set before use. */ - syncEngine!: RemoteSyncer; + remoteSyncer: RemoteSyncer = {}; /** * Starts up the remote store, creating streams, restoring state from @@ -316,7 +316,11 @@ export class RemoteStore implements TargetMetadataProvider { /** {@link TargetMetadataProvider.getRemoteKeysForTarget} */ getRemoteKeysForTarget(targetId: TargetId): DocumentKeySet { - return this.syncEngine.getRemoteKeysForTarget(targetId); + debugAssert( + !!this.remoteSyncer.getRemoteKeysForTarget, + 'getRemoteKeysForTarget() not set' + ); + return this.remoteSyncer.getRemoteKeysForTarget(targetId); } /** @@ -514,6 +518,10 @@ export class RemoteStore implements TargetMetadataProvider { * SyncEngine. */ private raiseWatchSnapshot(snapshotVersion: SnapshotVersion): Promise { + debugAssert( + !!this.remoteSyncer.applyRemoteEvent, + 'applyRemoteEvent() not set' + ); debugAssert( !snapshotVersion.isEqual(SnapshotVersion.min()), "Can't raise event for unknown SnapshotVersion" @@ -574,19 +582,20 @@ export class RemoteStore implements TargetMetadataProvider { }); // Finally raise remote event - return this.syncEngine.applyRemoteEvent(remoteEvent); + return this.remoteSyncer.applyRemoteEvent(remoteEvent); } /** Handles an error on a target */ private async handleTargetError( watchChange: WatchTargetChange ): Promise { + debugAssert(!!this.remoteSyncer.rejectListen, 'rejectListen() not set'); debugAssert(!!watchChange.cause, 'Handling target error without a cause'); const error = watchChange.cause!; for (const targetId of watchChange.targetIds) { // A watched target might have been removed already. if (this.listenTargets.has(targetId)) { - await this.syncEngine.rejectListen(targetId, error); + await this.remoteSyncer.rejectListen(targetId, error); this.listenTargets.delete(targetId); this.watchChangeAggregator!.removeTarget(targetId); } @@ -704,8 +713,12 @@ export class RemoteStore implements TargetMetadataProvider { const batch = this.writePipeline.shift()!; const success = MutationBatchResult.from(batch, commitVersion, results); + debugAssert( + !!this.remoteSyncer.applySuccessfulWrite, + 'applySuccessfulWrite() not set' + ); await this.executeWithRecovery(() => - this.syncEngine.applySuccessfulWrite(success) + this.remoteSyncer.applySuccessfulWrite!(success) ); // It's possible that with the completion of this mutation another @@ -750,8 +763,12 @@ export class RemoteStore implements TargetMetadataProvider { // restart. this.writeStream.inhibitBackoff(); + debugAssert( + !!this.remoteSyncer.rejectFailedWrite, + 'rejectFailedWrite() not set' + ); await this.executeWithRecovery(() => - this.syncEngine.rejectFailedWrite(batch.batchId, error) + this.remoteSyncer.rejectFailedWrite!(batch.batchId, error) ); // It's possible that with the completion of this mutation @@ -783,7 +800,11 @@ export class RemoteStore implements TargetMetadataProvider { await this.disableNetworkInternal(); this.onlineStateTracker.set(OnlineState.Unknown); - await this.syncEngine.handleCredentialChange(user); + debugAssert( + !!this.remoteSyncer.handleCredentialChange, + 'handleCredentialChange() not set' + ); + await this.remoteSyncer.handleCredentialChange(user); this.offlineCauses.delete(OfflineCause.CredentialChange); await this.enableNetworkInternal(); diff --git a/packages/firestore/src/remote/remote_syncer.ts b/packages/firestore/src/remote/remote_syncer.ts index a3b06d617fe..09cd6c9788b 100644 --- a/packages/firestore/src/remote/remote_syncer.ts +++ b/packages/firestore/src/remote/remote_syncer.ts @@ -32,7 +32,7 @@ export interface RemoteSyncer { * changes, and releasing any pending mutation batches that would become * visible because of the snapshot version the remote event contains. */ - applyRemoteEvent(remoteEvent: RemoteEvent): Promise; + applyRemoteEvent?(remoteEvent: RemoteEvent): Promise; /** * Rejects the listen for the given targetID. This can be triggered by the @@ -44,32 +44,32 @@ export interface RemoteSyncer { * Nearly always this will be an indication that the user is no longer * authorized to see the data matching the target. */ - rejectListen(targetId: TargetId, error: FirestoreError): Promise; + rejectListen?(targetId: TargetId, error: FirestoreError): Promise; /** * Applies the result of a successful write of a mutation batch to the sync * engine, emitting snapshots in any views that the mutation applies to, and * removing the batch from the mutation queue. */ - applySuccessfulWrite(result: MutationBatchResult): Promise; + applySuccessfulWrite?(result: MutationBatchResult): Promise; /** * Rejects the batch, removing the batch from the mutation queue, recomputing * the local view of any documents affected by the batch and then, emitting * snapshots with the reverted value. */ - rejectFailedWrite(batchId: BatchId, error: FirestoreError): Promise; + rejectFailedWrite?(batchId: BatchId, error: FirestoreError): Promise; /** * Returns the set of remote document keys for the given target ID. This list * includes the documents that were assigned to the target when we received * the last snapshot. */ - getRemoteKeysForTarget(targetId: TargetId): DocumentKeySet; + getRemoteKeysForTarget?(targetId: TargetId): DocumentKeySet; /** * Updates all local state to match the pending mutations for the given user. * May be called repeatedly for the same user. */ - handleCredentialChange(user: User): Promise; + handleCredentialChange?(user: User): Promise; } diff --git a/packages/firestore/test/unit/core/event_manager.test.ts b/packages/firestore/test/unit/core/event_manager.test.ts index 66ab1413ccd..54ed39a2cbd 100644 --- a/packages/firestore/test/unit/core/event_manager.test.ts +++ b/packages/firestore/test/unit/core/event_manager.test.ts @@ -50,45 +50,47 @@ describe('EventManager', () => { }; } - // mock object. + // mock objects. // eslint-disable-next-line @typescript-eslint/no-explicit-any - function makeSyncEngineSpy(): any { - const stub = { - listen: sinon.stub().returns(Promise.resolve(0)), - subscribe: sinon.spy(), - unlisten: sinon.spy() - }; - return stub; - } + let onListenSpy: any, onUnlistenSpy: any; + + beforeEach(() => { + onListenSpy = sinon.stub().returns(Promise.resolve(0)); + onUnlistenSpy = sinon.spy(); + }); it('handles many listenables per query', async () => { const query1 = query('foo/bar'); const fakeListener1 = fakeQueryListener(query1); const fakeListener2 = fakeQueryListener(query1); - const syncEngineSpy = makeSyncEngineSpy(); - const eventManager = new EventManager(syncEngineSpy); + const eventManager = new EventManager(); + eventManager.onListen = onListenSpy.bind(null); + eventManager.onUnlisten = onUnlistenSpy.bind(null); await eventManager.listen(fakeListener1); - expect(syncEngineSpy.listen.calledWith(query1)).to.be.true; + expect(onListenSpy.calledWith(query1)).to.be.true; await eventManager.listen(fakeListener2); - expect(syncEngineSpy.listen.callCount).to.equal(1); + expect(onListenSpy.callCount).to.equal(1); await eventManager.unlisten(fakeListener2); - expect(syncEngineSpy.unlisten.callCount).to.equal(0); + expect(onUnlistenSpy.callCount).to.equal(0); await eventManager.unlisten(fakeListener1); - expect(syncEngineSpy.unlisten.calledWith(query1)).to.be.true; + expect(onUnlistenSpy.calledWith(query1)).to.be.true; }); it('handles unlisten on unknown listenable gracefully', async () => { - const syncEngineSpy = makeSyncEngineSpy(); const query1 = query('foo/bar'); const fakeListener1 = fakeQueryListener(query1); - const eventManager = new EventManager(syncEngineSpy); + + const eventManager = new EventManager(); + eventManager.onListen = onListenSpy.bind(null); + eventManager.onUnlisten = onUnlistenSpy.bind(null); + await eventManager.unlisten(fakeListener1); - expect(syncEngineSpy.unlisten.callCount).to.equal(0); + expect(onUnlistenSpy.callCount).to.equal(0); }); it('notifies listenables in the right order', async () => { @@ -109,13 +111,14 @@ describe('EventManager', () => { eventOrder.push('listenable3'); }; - const syncEngineSpy = makeSyncEngineSpy(); - const eventManager = new EventManager(syncEngineSpy); + const eventManager = new EventManager(); + eventManager.onListen = onListenSpy.bind(null); + eventManager.onUnlisten = onUnlistenSpy.bind(null); await eventManager.listen(fakeListener1); await eventManager.listen(fakeListener2); await eventManager.listen(fakeListener3); - expect(syncEngineSpy.listen.callCount).to.equal(2); + expect(onListenSpy.callCount).to.equal(2); // mock ViewSnapshot. // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -140,8 +143,9 @@ describe('EventManager', () => { events.push(onlineState); }; - const syncEngineSpy = makeSyncEngineSpy(); - const eventManager = new EventManager(syncEngineSpy); + const eventManager = new EventManager(); + eventManager.onListen = onListenSpy.bind(null); + eventManager.onUnlisten = onUnlistenSpy.bind(null); await eventManager.listen(fakeListener1); expect(events).to.deep.equal([OnlineState.Unknown]); diff --git a/packages/firestore/test/unit/specs/spec_test_runner.ts b/packages/firestore/test/unit/specs/spec_test_runner.ts index 6ba65cbb3e7..374e7d6aaef 100644 --- a/packages/firestore/test/unit/specs/spec_test_runner.ts +++ b/packages/firestore/test/unit/specs/spec_test_runner.ts @@ -37,7 +37,14 @@ import { queryWithLimit } from '../../../src/core/query'; import { SnapshotVersion } from '../../../src/core/snapshot_version'; -import { SyncEngine } from '../../../src/core/sync_engine'; +import { + activeLimboDocumentResolutions, + enqueuedLimboDocumentResolutions, + SyncEngine, + syncEngineListen, + syncEngineUnlisten, + syncEngineWrite +} from '../../../src/core/sync_engine'; import { TargetId } from '../../../src/core/types'; import { ChangeType, @@ -118,12 +125,12 @@ import { EventAggregator, MockConnection, MockIndexedDbPersistence, + MockMemoryOfflineComponentProvider, MockMemoryPersistence, - QueryEvent, - SharedWriteTracker, MockMultiTabOfflineComponentProvider, - MockMemoryOfflineComponentProvider, - MockOnlineComponentProvider + MockOnlineComponentProvider, + QueryEvent, + SharedWriteTracker } from './spec_test_components'; import { IndexedDbPersistence } from '../../../src/local/indexeddb_persistence'; import { encodeBase64 } from '../../../src/platform/base64'; @@ -279,6 +286,12 @@ abstract class TestRunner { this.syncEngine = onlineComponentProvider.syncEngine; this.eventManager = onlineComponentProvider.eventManager; + this.eventManager.onListen = syncEngineListen.bind(null, this.syncEngine); + this.eventManager.onUnlisten = syncEngineUnlisten.bind( + null, + this.syncEngine + ); + await this.persistence.setDatabaseDeletedListener(async () => { await this.shutdown(); }); @@ -488,9 +501,9 @@ abstract class TestRunner { this.sharedWrites.push(mutations); } - return this.queue.enqueue(() => { - return this.syncEngine.write(mutations, syncEngineCallback); - }); + return this.queue.enqueue(() => + syncEngineWrite(this.syncEngine, mutations, syncEngineCallback) + ); } private doWatchAck(ackedTargets: SpecWatchAck): Promise { @@ -891,7 +904,7 @@ abstract class TestRunner { } private validateActiveLimboDocs(): void { - let actualLimboDocs = this.syncEngine.activeLimboDocumentResolutions(); + let actualLimboDocs = activeLimboDocumentResolutions(this.syncEngine); if (this.connection.isWatchOpen) { // Validate that each active limbo doc has an expected active target @@ -924,7 +937,7 @@ abstract class TestRunner { private validateEnqueuedLimboDocs(): void { let actualLimboDocs = new SortedSet(DocumentKey.comparator); - this.syncEngine.enqueuedLimboDocumentResolutions().forEach(key => { + enqueuedLimboDocumentResolutions(this.syncEngine).forEach(key => { actualLimboDocs = actualLimboDocs.add(key); }); let expectedLimboDocs = new SortedSet(DocumentKey.comparator); diff --git a/packages/rules-unit-testing/test/database.test.ts b/packages/rules-unit-testing/test/database.test.ts index 7b938803596..3846e31df10 100644 --- a/packages/rules-unit-testing/test/database.test.ts +++ b/packages/rules-unit-testing/test/database.test.ts @@ -90,7 +90,7 @@ describe('Testing Module Tests', function () { throw new Error('Expected otherFailure to fail.'); }) .catch(() => {}); - }) + }); it('initializeTestApp() with auth=null does not set access token', async function () { const app = firebase.initializeTestApp({