@firebase/database 0.13.4-canary.ac578e98f → 0.13.4-canary.dcfebe8dc

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -338,9 +338,7 @@ export declare class DataSnapshot {
338
338
  * @returns true if enumeration was canceled due to your callback returning
339
339
  * true.
340
340
  */
341
- forEach(action: (child: DataSnapshot & {
342
- key: string;
343
- }) => boolean | void): boolean;
341
+ forEach(action: (child: DataSnapshot) => boolean | void): boolean;
344
342
  /**
345
343
  * Returns true if the specified child path has (non-null) data.
346
344
  *
@@ -1241,7 +1241,7 @@ WebSocketConnection.responsesRequiredToBeHealthy = 2;
1241
1241
  WebSocketConnection.healthyTimeout = 30000;
1242
1242
 
1243
1243
  const name = "@firebase/database";
1244
- const version = "0.13.4-canary.ac578e98f";
1244
+ const version = "0.13.4-canary.dcfebe8dc";
1245
1245
 
1246
1246
  /**
1247
1247
  * @license
@@ -9139,7 +9139,7 @@ function viewProcessorApplyServerMerge(viewProcessor, viewCache, path, changedCh
9139
9139
  });
9140
9140
  viewMergeTree.children.inorderTraversal((childKey, childMergeTree) => {
9141
9141
  const isUnknownDeepMerge = !viewCache.serverCache.isCompleteForChild(childKey) &&
9142
- childMergeTree.value === undefined;
9142
+ childMergeTree.value === null;
9143
9143
  if (!serverNode.hasChild(childKey) && !isUnknownDeepMerge) {
9144
9144
  const serverChild = viewCache.serverCache
9145
9145
  .getNode()
@@ -9778,9 +9778,11 @@ function syncTreeApplyTaggedListenComplete(syncTree, path, tag) {
9778
9778
  *
9779
9779
  * @param eventRegistration - If null, all callbacks are removed.
9780
9780
  * @param cancelError - If a cancelError is provided, appropriate cancel events will be returned.
9781
+ * @param skipListenerDedup - When performing a `get()`, we don't add any new listeners, so no
9782
+ * deduping needs to take place. This flag allows toggling of that behavior
9781
9783
  * @returns Cancel events, if cancelError was provided.
9782
9784
  */
9783
- function syncTreeRemoveEventRegistration(syncTree, query, eventRegistration, cancelError) {
9785
+ function syncTreeRemoveEventRegistration(syncTree, query, eventRegistration, cancelError, skipListenerDedup = false) {
9784
9786
  // Find the syncPoint first. Then deal with whether or not it has matching listeners
9785
9787
  const path = query._path;
9786
9788
  const maybeSyncPoint = syncTree.syncPointTree_.get(path);
@@ -9797,48 +9799,52 @@ function syncTreeRemoveEventRegistration(syncTree, query, eventRegistration, can
9797
9799
  }
9798
9800
  const removed = removedAndEvents.removed;
9799
9801
  cancelEvents = removedAndEvents.events;
9800
- // We may have just removed one of many listeners and can short-circuit this whole process
9801
- // We may also not have removed a default listener, in which case all of the descendant listeners should already be
9802
- // properly set up.
9803
- //
9804
- // Since indexed queries can shadow if they don't have other query constraints, check for loadsAllData(), instead of
9805
- // queryId === 'default'
9806
- const removingDefault = -1 !==
9807
- removed.findIndex(query => {
9808
- return query._queryParams.loadsAllData();
9809
- });
9810
- const covered = syncTree.syncPointTree_.findOnPath(path, (relativePath, parentSyncPoint) => syncPointHasCompleteView(parentSyncPoint));
9811
- if (removingDefault && !covered) {
9812
- const subtree = syncTree.syncPointTree_.subtree(path);
9813
- // There are potentially child listeners. Determine what if any listens we need to send before executing the
9814
- // removal
9815
- if (!subtree.isEmpty()) {
9816
- // We need to fold over our subtree and collect the listeners to send
9817
- const newViews = syncTreeCollectDistinctViewsForSubTree_(subtree);
9818
- // Ok, we've collected all the listens we need. Set them up.
9819
- for (let i = 0; i < newViews.length; ++i) {
9820
- const view = newViews[i], newQuery = view.query;
9821
- const listener = syncTreeCreateListenerForView_(syncTree, view);
9822
- syncTree.listenProvider_.startListening(syncTreeQueryForListening_(newQuery), syncTreeTagForQuery_(syncTree, newQuery), listener.hashFn, listener.onComplete);
9823
- }
9824
- }
9825
- }
9826
- // If we removed anything and we're not covered by a higher up listen, we need to stop listening on this query
9827
- // The above block has us covered in terms of making sure we're set up on listens lower in the tree.
9828
- // Also, note that if we have a cancelError, it's already been removed at the provider level.
9829
- if (!covered && removed.length > 0 && !cancelError) {
9830
- // If we removed a default, then we weren't listening on any of the other queries here. Just cancel the one
9831
- // default. Otherwise, we need to iterate through and cancel each individual query
9832
- if (removingDefault) {
9833
- // We don't tag default listeners
9834
- const defaultTag = null;
9835
- syncTree.listenProvider_.stopListening(syncTreeQueryForListening_(query), defaultTag);
9836
- }
9837
- else {
9838
- removed.forEach((queryToRemove) => {
9839
- const tagToRemove = syncTree.queryToTagMap.get(syncTreeMakeQueryKey_(queryToRemove));
9840
- syncTree.listenProvider_.stopListening(syncTreeQueryForListening_(queryToRemove), tagToRemove);
9802
+ if (!skipListenerDedup) {
9803
+ /**
9804
+ * We may have just removed one of many listeners and can short-circuit this whole process
9805
+ * We may also not have removed a default listener, in which case all of the descendant listeners should already be
9806
+ * properly set up.
9807
+ */
9808
+ // Since indexed queries can shadow if they don't have other query constraints, check for loadsAllData(), instead of
9809
+ // queryId === 'default'
9810
+ const removingDefault = -1 !==
9811
+ removed.findIndex(query => {
9812
+ return query._queryParams.loadsAllData();
9841
9813
  });
9814
+ const covered = syncTree.syncPointTree_.findOnPath(path, (relativePath, parentSyncPoint) => syncPointHasCompleteView(parentSyncPoint));
9815
+ if (removingDefault && !covered) {
9816
+ const subtree = syncTree.syncPointTree_.subtree(path);
9817
+ // There are potentially child listeners. Determine what if any listens we need to send before executing the
9818
+ // removal
9819
+ if (!subtree.isEmpty()) {
9820
+ // We need to fold over our subtree and collect the listeners to send
9821
+ const newViews = syncTreeCollectDistinctViewsForSubTree_(subtree);
9822
+ // Ok, we've collected all the listens we need. Set them up.
9823
+ for (let i = 0; i < newViews.length; ++i) {
9824
+ const view = newViews[i], newQuery = view.query;
9825
+ const listener = syncTreeCreateListenerForView_(syncTree, view);
9826
+ syncTree.listenProvider_.startListening(syncTreeQueryForListening_(newQuery), syncTreeTagForQuery(syncTree, newQuery), listener.hashFn, listener.onComplete);
9827
+ }
9828
+ }
9829
+ // Otherwise there's nothing below us, so nothing we need to start listening on
9830
+ }
9831
+ // If we removed anything and we're not covered by a higher up listen, we need to stop listening on this query
9832
+ // The above block has us covered in terms of making sure we're set up on listens lower in the tree.
9833
+ // Also, note that if we have a cancelError, it's already been removed at the provider level.
9834
+ if (!covered && removed.length > 0 && !cancelError) {
9835
+ // If we removed a default, then we weren't listening on any of the other queries here. Just cancel the one
9836
+ // default. Otherwise, we need to iterate through and cancel each individual query
9837
+ if (removingDefault) {
9838
+ // We don't tag default listeners
9839
+ const defaultTag = null;
9840
+ syncTree.listenProvider_.stopListening(syncTreeQueryForListening_(query), defaultTag);
9841
+ }
9842
+ else {
9843
+ removed.forEach((queryToRemove) => {
9844
+ const tagToRemove = syncTree.queryToTagMap.get(syncTreeMakeQueryKey_(queryToRemove));
9845
+ syncTree.listenProvider_.stopListening(syncTreeQueryForListening_(queryToRemove), tagToRemove);
9846
+ });
9847
+ }
9842
9848
  }
9843
9849
  }
9844
9850
  // Now, clear all of the tags we're tracking for the removed listens
@@ -9846,30 +9852,6 @@ function syncTreeRemoveEventRegistration(syncTree, query, eventRegistration, can
9846
9852
  }
9847
9853
  return cancelEvents;
9848
9854
  }
9849
- /**
9850
- * This function was added to support non-listener queries,
9851
- * specifically for use in repoGetValue. It sets up all the same
9852
- * local cache data-structures (SyncPoint + View) that are
9853
- * needed for listeners without installing an event registration.
9854
- * If `query` is not `loadsAllData`, it will also provision a tag for
9855
- * the query so that query results can be merged into the sync
9856
- * tree using existing logic for tagged listener queries.
9857
- *
9858
- * @param syncTree - Synctree to add the query to.
9859
- * @param query - Query to register
9860
- * @returns tag as a string if query is not a default query, null if query is not.
9861
- */
9862
- function syncTreeRegisterQuery(syncTree, query) {
9863
- const { syncPoint, serverCache, writesCache, serverCacheComplete } = syncTreeRegisterSyncPoint(query, syncTree);
9864
- const view = syncPointGetView(syncPoint, query, writesCache, serverCache, serverCacheComplete);
9865
- if (!syncPoint.views.has(query._queryIdentifier)) {
9866
- syncPoint.views.set(query._queryIdentifier, view);
9867
- }
9868
- if (!query._queryParams.loadsAllData()) {
9869
- return syncTreeTagForQuery_(syncTree, query);
9870
- }
9871
- return null;
9872
- }
9873
9855
  /**
9874
9856
  * Apply new server data for the specified tagged query.
9875
9857
  *
@@ -9910,11 +9892,11 @@ function syncTreeApplyTaggedQueryMerge(syncTree, path, changedChildren, tag) {
9910
9892
  }
9911
9893
  }
9912
9894
  /**
9913
- * Creates a new syncpoint for a query and creates a tag if the view doesn't exist.
9914
- * Extracted from addEventRegistration to allow `repoGetValue` to properly set up the SyncTree
9915
- * without actually listening on a query.
9895
+ * Add an event callback for the specified query.
9896
+ *
9897
+ * @returns Events to raise.
9916
9898
  */
9917
- function syncTreeRegisterSyncPoint(query, syncTree) {
9899
+ function syncTreeAddEventRegistration(syncTree, query, eventRegistration, skipSetupListener = false) {
9918
9900
  const path = query._path;
9919
9901
  let serverCache = null;
9920
9902
  let foundAncestorDefaultView = false;
@@ -9963,24 +9945,8 @@ function syncTreeRegisterSyncPoint(query, syncTree) {
9963
9945
  syncTree.tagToQueryMap.set(tag, queryKey);
9964
9946
  }
9965
9947
  const writesCache = writeTreeChildWrites(syncTree.pendingWriteTree_, path);
9966
- return {
9967
- syncPoint,
9968
- writesCache,
9969
- serverCache,
9970
- serverCacheComplete,
9971
- foundAncestorDefaultView,
9972
- viewAlreadyExists
9973
- };
9974
- }
9975
- /**
9976
- * Add an event callback for the specified query.
9977
- *
9978
- * @returns Events to raise.
9979
- */
9980
- function syncTreeAddEventRegistration(syncTree, query, eventRegistration) {
9981
- const { syncPoint, serverCache, writesCache, serverCacheComplete, viewAlreadyExists, foundAncestorDefaultView } = syncTreeRegisterSyncPoint(query, syncTree);
9982
9948
  let events = syncPointAddEventRegistration(syncPoint, query, eventRegistration, writesCache, serverCache, serverCacheComplete);
9983
- if (!viewAlreadyExists && !foundAncestorDefaultView) {
9949
+ if (!viewAlreadyExists && !foundAncestorDefaultView && !skipSetupListener) {
9984
9950
  const view = syncPointViewForQuery(syncPoint, query);
9985
9951
  events = events.concat(syncTreeSetupListener_(syncTree, query, view));
9986
9952
  }
@@ -10110,7 +10076,7 @@ function syncTreeApplyOperationDescendantsHelper_(operation, syncPointTree, serv
10110
10076
  }
10111
10077
  function syncTreeCreateListenerForView_(syncTree, view) {
10112
10078
  const query = view.query;
10113
- const tag = syncTreeTagForQuery_(syncTree, query);
10079
+ const tag = syncTreeTagForQuery(syncTree, query);
10114
10080
  return {
10115
10081
  hashFn: () => {
10116
10082
  const cache = viewGetServerCache(view) || ChildrenNode.EMPTY_NODE;
@@ -10138,7 +10104,7 @@ function syncTreeCreateListenerForView_(syncTree, view) {
10138
10104
  /**
10139
10105
  * Return the tag associated with the given query.
10140
10106
  */
10141
- function syncTreeTagForQuery_(syncTree, query) {
10107
+ function syncTreeTagForQuery(syncTree, query) {
10142
10108
  const queryKey = syncTreeMakeQueryKey_(query);
10143
10109
  return syncTree.queryToTagMap.get(queryKey);
10144
10110
  }
@@ -10238,7 +10204,7 @@ function syncTreeGetNextQueryTag_() {
10238
10204
  */
10239
10205
  function syncTreeSetupListener_(syncTree, query, view) {
10240
10206
  const path = query._path;
10241
- const tag = syncTreeTagForQuery_(syncTree, query);
10207
+ const tag = syncTreeTagForQuery(syncTree, query);
10242
10208
  const listener = syncTreeCreateListenerForView_(syncTree, view);
10243
10209
  const events = syncTree.listenProvider_.startListening(syncTreeQueryForListening_(query), tag, listener.hashFn, listener.onComplete);
10244
10210
  const subtree = syncTree.syncPointTree_.subtree(path);
@@ -10269,7 +10235,7 @@ function syncTreeSetupListener_(syncTree, query, view) {
10269
10235
  });
10270
10236
  for (let i = 0; i < queriesToStop.length; ++i) {
10271
10237
  const queryToStop = queriesToStop[i];
10272
- syncTree.listenProvider_.stopListening(syncTreeQueryForListening_(queryToStop), syncTreeTagForQuery_(syncTree, queryToStop));
10238
+ syncTree.listenProvider_.stopListening(syncTreeQueryForListening_(queryToStop), syncTreeTagForQuery(syncTree, queryToStop));
10273
10239
  }
10274
10240
  }
10275
10241
  return events;
@@ -11194,14 +11160,14 @@ function repoGetNextWriteId(repo) {
11194
11160
  * belonging to active listeners. If they are found, such values
11195
11161
  * are considered to be the most up-to-date.
11196
11162
  *
11197
- * If the client is not connected, this method will try to
11198
- * establish a connection and request the value for `query`. If
11199
- * the client is not able to retrieve the query result, it reports
11200
- * an error.
11163
+ * If the client is not connected, this method will wait until the
11164
+ * repo has established a connection and then request the value for `query`.
11165
+ * If the client is not able to retrieve the query result for another reason,
11166
+ * it reports an error.
11201
11167
  *
11202
11168
  * @param query - The query to surface a value for.
11203
11169
  */
11204
- function repoGetValue(repo, query) {
11170
+ function repoGetValue(repo, query, eventRegistration) {
11205
11171
  // Only active queries are cached. There is no persisted cache.
11206
11172
  const cached = syncTreeGetServerValue(repo.serverSyncTree_, query);
11207
11173
  if (cached != null) {
@@ -11209,24 +11175,34 @@ function repoGetValue(repo, query) {
11209
11175
  }
11210
11176
  return repo.server_.get(query).then(payload => {
11211
11177
  const node = nodeFromJSON(payload).withIndex(query._queryParams.getIndex());
11212
- // if this is a filtered query, then overwrite at path
11178
+ /**
11179
+ * Below we simulate the actions of an `onlyOnce` `onValue()` event where:
11180
+ * Add an event registration,
11181
+ * Update data at the path,
11182
+ * Raise any events,
11183
+ * Cleanup the SyncTree
11184
+ */
11185
+ syncTreeAddEventRegistration(repo.serverSyncTree_, query, eventRegistration, true);
11186
+ let events;
11213
11187
  if (query._queryParams.loadsAllData()) {
11214
- syncTreeApplyServerOverwrite(repo.serverSyncTree_, query._path, node);
11188
+ events = syncTreeApplyServerOverwrite(repo.serverSyncTree_, query._path, node);
11215
11189
  }
11216
11190
  else {
11217
- // Simulate `syncTreeAddEventRegistration` without events/listener setup.
11218
- // We do this (along with the syncTreeRemoveEventRegistration` below) so that
11219
- // `repoGetValue` results have the same cache effects as initial listener(s)
11220
- // updates.
11221
- const tag = syncTreeRegisterQuery(repo.serverSyncTree_, query);
11222
- syncTreeApplyTaggedQueryOverwrite(repo.serverSyncTree_, query._path, node, tag);
11223
- // Call `syncTreeRemoveEventRegistration` with a null event registration, since there is none.
11224
- // Note: The below code essentially unregisters the query and cleans up any views/syncpoints temporarily created above.
11225
- }
11226
- const cancels = syncTreeRemoveEventRegistration(repo.serverSyncTree_, query, null);
11227
- if (cancels.length > 0) {
11228
- repoLog(repo, 'unexpected cancel events in repoGetValue');
11191
+ const tag = syncTreeTagForQuery(repo.serverSyncTree_, query);
11192
+ events = syncTreeApplyTaggedQueryOverwrite(repo.serverSyncTree_, query._path, node, tag);
11229
11193
  }
11194
+ /*
11195
+ * We need to raise events in the scenario where `get()` is called at a parent path, and
11196
+ * while the `get()` is pending, `onValue` is called at a child location. While get() is waiting
11197
+ * for the data, `onValue` will register a new event. Then, get() will come back, and update the syncTree
11198
+ * and its corresponding serverCache, including the child location where `onValue` is called. Then,
11199
+ * `onValue` will receive the event from the server, but look at the syncTree and see that the data received
11200
+ * from the server is already at the SyncPoint, and so the `onValue` callback will never get fired.
11201
+ * Calling `eventQueueRaiseEventsForChangedPath()` is the correct way to propagate the events and
11202
+ * ensure the corresponding child events will get fired.
11203
+ */
11204
+ eventQueueRaiseEventsForChangedPath(repo.eventQueue_, query._path, events);
11205
+ syncTreeRemoveEventRegistration(repo.serverSyncTree_, query, eventRegistration, null, true);
11230
11206
  return node;
11231
11207
  }, err => {
11232
11208
  repoLog(repo, 'get for query ' + stringify(query) + ' failed: ' + err);
@@ -12909,7 +12885,9 @@ function update(ref, values) {
12909
12885
  */
12910
12886
  function get(query) {
12911
12887
  query = getModularInstance(query);
12912
- return repoGetValue(query._repo, query).then(node => {
12888
+ const callbackContext = new CallbackContext(() => { });
12889
+ const container = new ValueEventRegistration(callbackContext);
12890
+ return repoGetValue(query._repo, query, container).then(node => {
12913
12891
  return new DataSnapshot(node, new ReferenceImpl(query._repo, query._path), query._queryParams.getIndex());
12914
12892
  });
12915
12893
  }