UNPKG

owltech

Version:
320 lines (317 loc) 11.7 kB
/** * @license * Copyright 2017 Google Inc. * * 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 { Path } from './util/Path'; import { Query } from '../api/Query'; import { Node } from './snap/Node'; import { Event } from './view/Event'; import { EventRegistration } from './view/EventRegistration'; /** * @typedef {{ * startListening: function( * !Query, * ?number, * function():string, * function(!string, *):!Array.<!Event> * ):!Array.<!Event>, * * stopListening: function(!Query, ?number) * }} */ export interface ListenProvider { startListening(query: Query, tag: number | null, hashFn: () => string, onComplete: (a: string, b?: any) => Event[]): Event[]; stopListening(a: Query, b: number | null): void; } /** * SyncTree is the central class for managing event callback registration, data caching, views * (query processing), and event generation. There are typically two SyncTree instances for * each Repo, one for the normal Firebase data, and one for the .info data. * * It has a number of responsibilities, including: * - Tracking all user event callbacks (registered via addEventRegistration() and removeEventRegistration()). * - Applying and caching data changes for user set(), transaction(), and update() calls * (applyUserOverwrite(), applyUserMerge()). * - Applying and caching data changes for server data changes (applyServerOverwrite(), * applyServerMerge()). * - Generating user-facing events for server and user changes (all of the apply* methods * return the set of events that need to be raised as a result). * - Maintaining the appropriate set of server listens to ensure we are always subscribed * to the correct set of paths and queries to satisfy the current set of user event * callbacks (listens are started/stopped using the provided listenProvider). * * NOTE: Although SyncTree tracks event callbacks and calculates events to raise, the actual * events are returned to the caller rather than raised synchronously. * * @constructor */ export declare class SyncTree { private listenProvider_; /** * Tree of SyncPoints. There's a SyncPoint at any location that has 1 or more views. * @type {!ImmutableTree.<!SyncPoint>} * @private */ private syncPointTree_; /** * A tree of all pending user writes (user-initiated set()'s, transaction()'s, update()'s, etc.). * @type {!WriteTree} * @private */ private pendingWriteTree_; private tagToQueryMap_; private queryToTagMap_; /** * @param {!ListenProvider} listenProvider_ Used by SyncTree to start / stop listening * to server data. */ constructor(listenProvider_: ListenProvider); /** * Apply the data changes for a user-generated set() or transaction() call. * * @param {!Path} path * @param {!Node} newData * @param {number} writeId * @param {boolean=} visible * @return {!Array.<!Event>} Events to raise. */ applyUserOverwrite(path: Path, newData: Node, writeId: number, visible?: boolean): Event[]; /** * Apply the data from a user-generated update() call * * @param {!Path} path * @param {!Object.<string, !Node>} changedChildren * @param {!number} writeId * @return {!Array.<!Event>} Events to raise. */ applyUserMerge(path: Path, changedChildren: { [k: string]: Node; }, writeId: number): Event[]; /** * Acknowledge a pending user write that was previously registered with applyUserOverwrite() or applyUserMerge(). * * @param {!number} writeId * @param {boolean=} revert True if the given write failed and needs to be reverted * @return {!Array.<!Event>} Events to raise. */ ackUserWrite(writeId: number, revert?: boolean): Event[]; /** * Apply new server data for the specified path.. * * @param {!Path} path * @param {!Node} newData * @return {!Array.<!Event>} Events to raise. */ applyServerOverwrite(path: Path, newData: Node): Event[]; /** * Apply new server data to be merged in at the specified path. * * @param {!Path} path * @param {!Object.<string, !Node>} changedChildren * @return {!Array.<!Event>} Events to raise. */ applyServerMerge(path: Path, changedChildren: { [k: string]: Node; }): Event[]; /** * Apply a listen complete for a query * * @param {!Path} path * @return {!Array.<!Event>} Events to raise. */ applyListenComplete(path: Path): Event[]; /** * Apply new server data for the specified tagged query. * * @param {!Path} path * @param {!Node} snap * @param {!number} tag * @return {!Array.<!Event>} Events to raise. */ applyTaggedQueryOverwrite(path: Path, snap: Node, tag: number): Event[]; /** * Apply server data to be merged in for the specified tagged query. * * @param {!Path} path * @param {!Object.<string, !Node>} changedChildren * @param {!number} tag * @return {!Array.<!Event>} Events to raise. */ applyTaggedQueryMerge(path: Path, changedChildren: { [k: string]: Node; }, tag: number): Event[]; /** * Apply a listen complete for a tagged query * * @param {!Path} path * @param {!number} tag * @return {!Array.<!Event>} Events to raise. */ applyTaggedListenComplete(path: Path, tag: number): Event[]; /** * Add an event callback for the specified query. * * @param {!Query} query * @param {!EventRegistration} eventRegistration * @return {!Array.<!Event>} Events to raise. */ addEventRegistration(query: Query, eventRegistration: EventRegistration): Event[]; /** * Remove event callback(s). * * If query is the default query, we'll check all queries for the specified eventRegistration. * If eventRegistration is null, we'll remove all callbacks for the specified query/queries. * * @param {!Query} query * @param {?EventRegistration} eventRegistration If null, all callbacks are removed. * @param {Error=} cancelError If a cancelError is provided, appropriate cancel events will be returned. * @return {!Array.<!Event>} Cancel events, if cancelError was provided. */ removeEventRegistration(query: Query, eventRegistration: EventRegistration | null, cancelError?: Error): Event[]; /** * Returns a complete cache, if we have one, of the data at a particular path. The location must have a listener above * it, but as this is only used by transaction code, that should always be the case anyways. * * Note: this method will *include* hidden writes from transaction with applyLocally set to false. * @param {!Path} path The path to the data we want * @param {Array.<number>=} writeIdsToExclude A specific set to be excluded * @return {?Node} */ calcCompleteEventCache(path: Path, writeIdsToExclude?: number[]): Node | null; /** * This collapses multiple unfiltered views into a single view, since we only need a single * listener for them. * * @param {!ImmutableTree.<!SyncPoint>} subtree * @return {!Array.<!View>} * @private */ private collectDistinctViewsForSubTree_; /** * @param {!Array.<!Query>} queries * @private */ private removeTags_; /** * Normalizes a query to a query we send the server for listening * @param {!Query} query * @return {!Query} The normalized query * @private */ private static queryForListening_; /** * For a given new listen, manage the de-duplication of outstanding subscriptions. * * @param {!Query} query * @param {!View} view * @return {!Array.<!Event>} This method can return events to support synchronous data sources * @private */ private setupListener_; /** * * @param {!View} view * @return {{hashFn: function(), onComplete: function(!string, *)}} * @private */ private createListenerForView_; /** * Given a query, computes a "queryKey" suitable for use in our queryToTagMap_. * @private * @param {!Query} query * @return {string} */ private static makeQueryKey_; /** * Given a queryKey (created by makeQueryKey), parse it back into a path and queryId. * @private * @param {!string} queryKey * @return {{queryId: !string, path: !Path}} */ private static parseQueryKey_; /** * Return the query associated with the given tag, if we have one * @param {!number} tag * @return {?string} * @private */ private queryKeyForTag_; /** * Return the tag associated with the given query. * @param {!Query} query * @return {?number} * @private */ private tagForQuery_; /** * Static tracker for next query tag. * @type {number} * @private */ private static nextQueryTag_; /** * Static accessor for query tags. * @return {number} * @private */ private static getNextQueryTag_; /** * A helper method to apply tagged operations * * @param {!Path} queryPath * @param {!Operation} operation * @return {!Array.<!Event>} * @private */ private applyTaggedOperation_; /** * A helper method that visits all descendant and ancestor SyncPoints, applying the operation. * * NOTES: * - Descendant SyncPoints will be visited first (since we raise events depth-first). * - We call applyOperation() on each SyncPoint passing three things: * 1. A version of the Operation that has been made relative to the SyncPoint location. * 2. A WriteTreeRef of any writes we have cached at the SyncPoint location. * 3. A snapshot Node with cached server data, if we have it. * - We concatenate all of the events returned by each SyncPoint and return the result. * * @param {!Operation} operation * @return {!Array.<!Event>} * @private */ private applyOperationToSyncPoints_; /** * Recursive helper for applyOperationToSyncPoints_ * * @private * @param {!Operation} operation * @param {ImmutableTree.<!SyncPoint>} syncPointTree * @param {?Node} serverCache * @param {!WriteTreeRef} writesCache * @return {!Array.<!Event>} */ private applyOperationHelper_; /** * Recursive helper for applyOperationToSyncPoints_ * * @private * @param {!Operation} operation * @param {ImmutableTree.<!SyncPoint>} syncPointTree * @param {?Node} serverCache * @param {!WriteTreeRef} writesCache * @return {!Array.<!Event>} */ private applyOperationDescendantsHelper_; }