UNPKG

@firebase/firestore

Version:

The Cloud Firestore component of the Firebase JS SDK.

319 lines (318 loc) • 13 kB
/** * @license * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { DatabaseId } from '../core/database_info'; import { SnapshotVersion } from '../core/snapshot_version'; import { RemoteTargetId } from '../core/types'; import { ChangeType } from '../core/view_snapshot'; import { TargetData } from '../local/target_data'; import { DocumentKeySet } from '../model/collections'; import { MutableDocument } from '../model/document'; import { DocumentKey } from '../model/document_key'; import { ByteString } from '../util/byte_string'; import { FirestoreError } from '../util/error'; import { ExistenceFilter } from './existence_filter'; import { RemoteEvent, TargetChange } from './remote_event'; /** * Internal representation of the watcher API protocol buffers. */ export type WatchChange = DocumentWatchChange | WatchTargetChange | ExistenceFilterChange; /** * Represents a changed document and a list of target ids to which this change * applies. * * If document has been deleted NoDocument will be provided. */ export declare class DocumentWatchChange { /** The new document applies to all of these targets. */ updatedTargetIds: RemoteTargetId[]; /** The new document is removed from all of these targets. */ removedTargetIds: RemoteTargetId[]; /** The key of the document for this change. */ key: DocumentKey; /** * The new document or NoDocument if it was deleted. Is null if the * document went out of view without the server sending a new document. */ newDoc: MutableDocument | null; constructor( /** The new document applies to all of these targets. */ updatedTargetIds: RemoteTargetId[], /** The new document is removed from all of these targets. */ removedTargetIds: RemoteTargetId[], /** The key of the document for this change. */ key: DocumentKey, /** * The new document or NoDocument if it was deleted. Is null if the * document went out of view without the server sending a new document. */ newDoc: MutableDocument | null); } export declare class ExistenceFilterChange { targetId: RemoteTargetId; existenceFilter: ExistenceFilter; constructor(targetId: RemoteTargetId, existenceFilter: ExistenceFilter); } export declare const enum WatchTargetChangeState { NoChange = 0, Added = 1, Removed = 2, Current = 3, Reset = 4 } export declare class WatchTargetChange { /** What kind of change occurred to the watch target. */ state: WatchTargetChangeState; /** The target IDs that were added/removed/set. */ targetIds: RemoteTargetId[]; /** * An opaque, server-assigned token that allows watching a target to be * resumed after disconnecting without retransmitting all the data that * matches the target. The resume token essentially identifies a point in * time from which the server should resume sending results. */ resumeToken: ByteString; /** An RPC error indicating why the watch failed. */ cause: FirestoreError | null; constructor( /** What kind of change occurred to the watch target. */ state: WatchTargetChangeState, /** The target IDs that were added/removed/set. */ targetIds: RemoteTargetId[], /** * An opaque, server-assigned token that allows watching a target to be * resumed after disconnecting without retransmitting all the data that * matches the target. The resume token essentially identifies a point in * time from which the server should resume sending results. */ resumeToken?: ByteString, /** An RPC error indicating why the watch failed. */ cause?: FirestoreError | null); } /** Tracks the internal state of a Watch target. */ export declare class TargetState { private targetId; /** * Track the targetId for logging. */ constructor(targetId: RemoteTargetId); /** * The number of pending responses (adds or removes) that we are waiting on. * We only consider targets active that have no pending responses. */ private pendingResponses; /** * Keeps track of the document changes since the last raised snapshot. * * These changes are continuously updated as we receive document updates and * always reflect the current set of changes against the last issued snapshot. */ private documentChanges; /** See public getters for explanations of these fields. */ private _resumeToken; private _current; /** * Whether this target state should be included in the next snapshot. We * initialize to true so that newly-added targets are included in the next * RemoteEvent. */ private _hasPendingChanges; /** * Whether this target has been marked 'current'. * * 'Current' has special meaning in the RPC protocol: It implies that the * Watch backend has sent us all changes up to the point at which the target * was added and that the target is consistent with the rest of the watch * stream. */ get current(): boolean; /** The last resume token sent to us for this target. */ get resumeToken(): ByteString; /** Whether this target has pending target adds or target removes. */ get isPending(): boolean; /** Whether we have modified any state that should trigger a snapshot. */ get hasPendingChanges(): boolean; /** * Applies the resume token to the TargetChange, but only when it has a new * value. Empty resumeTokens are discarded. */ updateResumeToken(resumeToken: ByteString): void; /** * Creates a target change from the current set of changes. * * To reset the document changes after raising this snapshot, call * `clearPendingChanges()`. */ toTargetChange(): TargetChange; /** * Resets the document changes and sets `hasPendingChanges` to false. */ clearPendingChanges(): void; addDocumentChange(key: DocumentKey, changeType: ChangeType): void; removeDocumentChange(key: DocumentKey): void; recordPendingTargetRequest(): void; recordTargetResponse(): void; markCurrent(): void; } /** * Interface implemented by RemoteStore to expose target metadata to the * WatchChangeAggregator. */ export interface TargetMetadataProvider { /** * Returns the set of remote document keys for the given target ID as of the * last raised snapshot. */ getRemoteKeysForTarget(targetId: RemoteTargetId): DocumentKeySet; /** * Returns the TargetData for an active target ID or 'null' if this target * has become inactive */ getTargetDataForTarget(targetId: RemoteTargetId): TargetData<RemoteTargetId> | null; /** * Returns the database ID of the Firestore instance. */ getDatabaseId(): DatabaseId; } /** * A helper class to accumulate watch changes into a RemoteEvent. */ export declare class WatchChangeAggregator { private metadataProvider; constructor(metadataProvider: TargetMetadataProvider); /** * The internal state of all tracked targets. * * Targets have the following lifecycle of [states] within the WatchChangeAggregator: * [unknown] -> recordPendingTargetRequest(t) * -> [pending] * -> handleTargetChange(t, Added) * -> [added / !pending] * -> recordPendingTargetRequest(t) * -> [pending] * -> handleTargetChange(t, Removed) * -> [unknown] * * A reset on an [added] target leaves the target in an [added] state. * [added / !pending] -> handleTargetChange(t, Reset) * -> [added / !pending] * * [active]: is a substate of [added], where also `remoteStore.listenTargets.has(t) === true`. * Generally it is expected that when a target is [active / !pending] * then it is also [active], but the implementation does not guarantee * this will always be true. * */ private targetStates; /** Keeps track of the documents to update since the last raised snapshot. */ private pendingDocumentUpdates; private pendingDocumentUpdatesByTarget; /** A mapping of document keys to their set of target IDs. */ private pendingDocumentTargetMapping; /** * A map of targets with existence filter mismatches. These targets are * known to be inconsistent and their listens needs to be re-established by * RemoteStore. */ private pendingTargetResets; /** * Processes and adds the DocumentWatchChange to the current set of changes. */ handleDocumentChange(docChange: DocumentWatchChange): void; /** Processes and adds the WatchTargetChange to the current set of changes. */ handleTargetChange(targetChange: WatchTargetChange): void; /** * Iterates over all targetIds that the watch change applies to: either the * targetIds explicitly listed in the change or the targetIds of all currently * active targets. */ forEachTarget(targetChange: WatchTargetChange, fn: (targetId: RemoteTargetId) => void): void; /** * Handles existence filters and synthesizes deletes for filter mismatches. * Targets that are invalidated by filter mismatches are added to * `pendingTargetResets`. */ handleExistenceFilter(watchChange: ExistenceFilterChange): void; /** * Parse the bloom filter from the "unchanged_names" field of an existence * filter. */ private parseBloomFilter; /** * Apply bloom filter to remove the deleted documents, and return the * application status. */ private applyBloomFilter; /** * Filter out removed documents based on bloom filter membership result and * return number of documents removed. */ private filterRemovedDocuments; /** * Converts the currently accumulated state into a remote event at the * provided snapshot version. Resets the accumulated changes before returning. */ createRemoteEvent(snapshotVersion: SnapshotVersion): RemoteEvent<RemoteTargetId>; /** * Adds the provided document to the internal list of document updates and * its document key to the given target's mapping. */ addDocumentToTarget(targetId: RemoteTargetId, document: MutableDocument): void; /** * Removes the provided document from the target mapping. If the * document no longer matches the target, but the document's state is still * known (e.g. we know that the document was deleted or we received the change * that caused the filter mismatch), the new document can be provided * to update the remote document cache. */ removeDocumentFromTarget(targetId: RemoteTargetId, key: DocumentKey, updatedDocument: MutableDocument | null): void; removeTarget(targetId: RemoteTargetId): void; /** * Returns the current count of documents in the target. This includes both * the number of documents that the LocalStore considers to be part of the * target as well as any accumulated changes. */ private getCurrentDocumentCountForTarget; /** * Increment the number of acks needed from watch before we can consider the * server to be 'in-sync' with the client's active targets. */ recordPendingTargetRequest(targetId: RemoteTargetId): void; private ensureDocumentTargetMapping; private ensureDocumentUpdateByTarget; /** * Verifies that the user is still interested in this target (by calling * `getTargetDataForTarget()`) and that we are not waiting for pending ADDs * from watch. */ protected isActiveTarget(targetId: RemoteTargetId): boolean; /** * Returns the TargetData for an active target (i.e. a target that the user * is still interested in that has no outstanding target change requests). */ protected targetDataForActiveTarget(targetId: RemoteTargetId): TargetData<RemoteTargetId> | null; /** * Resets the state of a Watch target to its initial state (e.g. sets * 'current' to false, clears the resume token and removes its target mapping * from all documents). */ private resetTarget; /** * Returns whether the LocalStore considers the document to be part of the * specified target. */ private targetContainsDocument; }