@firebase/database 0.13.9 → 0.13.10-canary.09dfc3aac

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.
@@ -2109,8 +2109,6 @@ export declare class _QueryParams {
2109
2109
  indexEndName_: string;
2110
2110
  index_: PriorityIndex;
2111
2111
  hasStart(): boolean;
2112
- hasStartAfter(): boolean;
2113
- hasEndBefore(): boolean;
2114
2112
  /**
2115
2113
  * @returns True if it would return from left.
2116
2114
  */
@@ -1,7 +1,7 @@
1
1
  import Websocket from 'faye-websocket';
2
- import { stringify, jsonEval, contains, assert, isNodeSdk, base64, stringToByteArray, Sha1, deepCopy, base64Encode, isMobileCordova, stringLength, Deferred, safeGet, isAdmin, isValidFormat, isEmpty, isReactNative, assertionError, map, querystring, errorPrefix, getModularInstance, getDefaultEmulatorHostnameAndPort, createMockUserToken } from '@firebase/util';
2
+ import { stringify, jsonEval, contains, assert, isNodeSdk, stringToByteArray, Sha1, base64, deepCopy, base64Encode, isMobileCordova, stringLength, Deferred, safeGet, isAdmin, isValidFormat, isEmpty, isReactNative, assertionError, map, querystring, errorPrefix, getModularInstance, getDefaultEmulatorHostnameAndPort, createMockUserToken } from '@firebase/util';
3
3
  import { Logger, LogLevel } from '@firebase/logger';
4
- import { getApp, _getProvider, SDK_VERSION as SDK_VERSION$1, _registerComponent, registerVersion } from '@firebase/app';
4
+ import { _getProvider, getApp, SDK_VERSION as SDK_VERSION$1, _registerComponent, registerVersion } from '@firebase/app';
5
5
  import { Component } from '@firebase/component';
6
6
 
7
7
  /**
@@ -1250,7 +1250,7 @@ WebSocketConnection.responsesRequiredToBeHealthy = 2;
1250
1250
  WebSocketConnection.healthyTimeout = 30000;
1251
1251
 
1252
1252
  const name = "@firebase/database";
1253
- const version = "0.13.9";
1253
+ const version = "0.13.10-canary.09dfc3aac";
1254
1254
 
1255
1255
  /**
1256
1256
  * @license
@@ -5955,153 +5955,6 @@ class ValueIndex extends Index {
5955
5955
  }
5956
5956
  const VALUE_INDEX = new ValueIndex();
5957
5957
 
5958
- /**
5959
- * @license
5960
- * Copyright 2017 Google LLC
5961
- *
5962
- * Licensed under the Apache License, Version 2.0 (the "License");
5963
- * you may not use this file except in compliance with the License.
5964
- * You may obtain a copy of the License at
5965
- *
5966
- * http://www.apache.org/licenses/LICENSE-2.0
5967
- *
5968
- * Unless required by applicable law or agreed to in writing, software
5969
- * distributed under the License is distributed on an "AS IS" BASIS,
5970
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5971
- * See the License for the specific language governing permissions and
5972
- * limitations under the License.
5973
- */
5974
- // Modeled after base64 web-safe chars, but ordered by ASCII.
5975
- const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
5976
- const MIN_PUSH_CHAR = '-';
5977
- const MAX_PUSH_CHAR = 'z';
5978
- const MAX_KEY_LEN = 786;
5979
- /**
5980
- * Fancy ID generator that creates 20-character string identifiers with the
5981
- * following properties:
5982
- *
5983
- * 1. They're based on timestamp so that they sort *after* any existing ids.
5984
- * 2. They contain 72-bits of random data after the timestamp so that IDs won't
5985
- * collide with other clients' IDs.
5986
- * 3. They sort *lexicographically* (so the timestamp is converted to characters
5987
- * that will sort properly).
5988
- * 4. They're monotonically increasing. Even if you generate more than one in
5989
- * the same timestamp, the latter ones will sort after the former ones. We do
5990
- * this by using the previous random bits but "incrementing" them by 1 (only
5991
- * in the case of a timestamp collision).
5992
- */
5993
- const nextPushId = (function () {
5994
- // Timestamp of last push, used to prevent local collisions if you push twice
5995
- // in one ms.
5996
- let lastPushTime = 0;
5997
- // We generate 72-bits of randomness which get turned into 12 characters and
5998
- // appended to the timestamp to prevent collisions with other clients. We
5999
- // store the last characters we generated because in the event of a collision,
6000
- // we'll use those same characters except "incremented" by one.
6001
- const lastRandChars = [];
6002
- return function (now) {
6003
- const duplicateTime = now === lastPushTime;
6004
- lastPushTime = now;
6005
- let i;
6006
- const timeStampChars = new Array(8);
6007
- for (i = 7; i >= 0; i--) {
6008
- timeStampChars[i] = PUSH_CHARS.charAt(now % 64);
6009
- // NOTE: Can't use << here because javascript will convert to int and lose
6010
- // the upper bits.
6011
- now = Math.floor(now / 64);
6012
- }
6013
- assert(now === 0, 'Cannot push at time == 0');
6014
- let id = timeStampChars.join('');
6015
- if (!duplicateTime) {
6016
- for (i = 0; i < 12; i++) {
6017
- lastRandChars[i] = Math.floor(Math.random() * 64);
6018
- }
6019
- }
6020
- else {
6021
- // If the timestamp hasn't changed since last push, use the same random
6022
- // number, except incremented by 1.
6023
- for (i = 11; i >= 0 && lastRandChars[i] === 63; i--) {
6024
- lastRandChars[i] = 0;
6025
- }
6026
- lastRandChars[i]++;
6027
- }
6028
- for (i = 0; i < 12; i++) {
6029
- id += PUSH_CHARS.charAt(lastRandChars[i]);
6030
- }
6031
- assert(id.length === 20, 'nextPushId: Length should be 20.');
6032
- return id;
6033
- };
6034
- })();
6035
- const successor = function (key) {
6036
- if (key === '' + INTEGER_32_MAX) {
6037
- // See https://firebase.google.com/docs/database/web/lists-of-data#data-order
6038
- return MIN_PUSH_CHAR;
6039
- }
6040
- const keyAsInt = tryParseInt(key);
6041
- if (keyAsInt != null) {
6042
- return '' + (keyAsInt + 1);
6043
- }
6044
- const next = new Array(key.length);
6045
- for (let i = 0; i < next.length; i++) {
6046
- next[i] = key.charAt(i);
6047
- }
6048
- if (next.length < MAX_KEY_LEN) {
6049
- next.push(MIN_PUSH_CHAR);
6050
- return next.join('');
6051
- }
6052
- let i = next.length - 1;
6053
- while (i >= 0 && next[i] === MAX_PUSH_CHAR) {
6054
- i--;
6055
- }
6056
- // `successor` was called on the largest possible key, so return the
6057
- // MAX_NAME, which sorts larger than all keys.
6058
- if (i === -1) {
6059
- return MAX_NAME;
6060
- }
6061
- const source = next[i];
6062
- const sourcePlusOne = PUSH_CHARS.charAt(PUSH_CHARS.indexOf(source) + 1);
6063
- next[i] = sourcePlusOne;
6064
- return next.slice(0, i + 1).join('');
6065
- };
6066
- // `key` is assumed to be non-empty.
6067
- const predecessor = function (key) {
6068
- if (key === '' + INTEGER_32_MIN) {
6069
- return MIN_NAME;
6070
- }
6071
- const keyAsInt = tryParseInt(key);
6072
- if (keyAsInt != null) {
6073
- return '' + (keyAsInt - 1);
6074
- }
6075
- const next = new Array(key.length);
6076
- for (let i = 0; i < next.length; i++) {
6077
- next[i] = key.charAt(i);
6078
- }
6079
- // If `key` ends in `MIN_PUSH_CHAR`, the largest key lexicographically
6080
- // smaller than `key`, is `key[0:key.length - 1]`. The next key smaller
6081
- // than that, `predecessor(predecessor(key))`, is
6082
- //
6083
- // `key[0:key.length - 2] + (key[key.length - 1] - 1) + \
6084
- // { MAX_PUSH_CHAR repeated MAX_KEY_LEN - (key.length - 1) times }
6085
- //
6086
- // analogous to increment/decrement for base-10 integers.
6087
- //
6088
- // This works because lexigographic comparison works character-by-character,
6089
- // using length as a tie-breaker if one key is a prefix of the other.
6090
- if (next[next.length - 1] === MIN_PUSH_CHAR) {
6091
- if (next.length === 1) {
6092
- // See https://firebase.google.com/docs/database/web/lists-of-data#orderbykey
6093
- return '' + INTEGER_32_MAX;
6094
- }
6095
- delete next[next.length - 1];
6096
- return next.join('');
6097
- }
6098
- // Replace the last character with it's immediate predecessor, and
6099
- // fill the suffix of the key with MAX_PUSH_CHAR. This is the
6100
- // lexicographically largest possible key smaller than `key`.
6101
- next[next.length - 1] = PUSH_CHARS.charAt(PUSH_CHARS.indexOf(next[next.length - 1]) - 1);
6102
- return next.join('') + MAX_PUSH_CHAR.repeat(MAX_KEY_LEN - next.length);
6103
- };
6104
-
6105
5958
  /**
6106
5959
  * @license
6107
5960
  * Copyright 2017 Google LLC
@@ -6270,6 +6123,8 @@ class RangedFilter {
6270
6123
  this.index_ = params.getIndex();
6271
6124
  this.startPost_ = RangedFilter.getStartPost_(params);
6272
6125
  this.endPost_ = RangedFilter.getEndPost_(params);
6126
+ this.startIsInclusive_ = !params.startAfterSet_;
6127
+ this.endIsInclusive_ = !params.endBeforeSet_;
6273
6128
  }
6274
6129
  getStartPost() {
6275
6130
  return this.startPost_;
@@ -6278,8 +6133,13 @@ class RangedFilter {
6278
6133
  return this.endPost_;
6279
6134
  }
6280
6135
  matches(node) {
6281
- return (this.index_.compare(this.getStartPost(), node) <= 0 &&
6282
- this.index_.compare(node, this.getEndPost()) <= 0);
6136
+ const isWithinStart = this.startIsInclusive_
6137
+ ? this.index_.compare(this.getStartPost(), node) <= 0
6138
+ : this.index_.compare(this.getStartPost(), node) < 0;
6139
+ const isWithinEnd = this.endIsInclusive_
6140
+ ? this.index_.compare(node, this.getEndPost()) <= 0
6141
+ : this.index_.compare(node, this.getEndPost()) < 0;
6142
+ return isWithinStart && isWithinEnd;
6283
6143
  }
6284
6144
  updateChild(snap, key, newChild, affectedPath, source, optChangeAccumulator) {
6285
6145
  if (!this.matches(new NamedNode(key, newChild))) {
@@ -6357,10 +6217,22 @@ class RangedFilter {
6357
6217
  */
6358
6218
  class LimitedFilter {
6359
6219
  constructor(params) {
6220
+ this.withinDirectionalStart = (node) => this.reverse_ ? this.withinEndPost(node) : this.withinStartPost(node);
6221
+ this.withinDirectionalEnd = (node) => this.reverse_ ? this.withinStartPost(node) : this.withinEndPost(node);
6222
+ this.withinStartPost = (node) => {
6223
+ const compareRes = this.index_.compare(this.rangedFilter_.getStartPost(), node);
6224
+ return this.startIsInclusive_ ? compareRes <= 0 : compareRes < 0;
6225
+ };
6226
+ this.withinEndPost = (node) => {
6227
+ const compareRes = this.index_.compare(node, this.rangedFilter_.getEndPost());
6228
+ return this.endIsInclusive_ ? compareRes <= 0 : compareRes < 0;
6229
+ };
6360
6230
  this.rangedFilter_ = new RangedFilter(params);
6361
6231
  this.index_ = params.getIndex();
6362
6232
  this.limit_ = params.getLimit();
6363
6233
  this.reverse_ = !params.isViewFromLeft();
6234
+ this.startIsInclusive_ = !params.startAfterSet_;
6235
+ this.endIsInclusive_ = !params.endBeforeSet_;
6364
6236
  }
6365
6237
  updateChild(snap, key, newChild, affectedPath, source, optChangeAccumulator) {
6366
6238
  if (!this.rangedFilter_.matches(new NamedNode(key, newChild))) {
@@ -6401,23 +6273,18 @@ class LimitedFilter {
6401
6273
  let count = 0;
6402
6274
  while (iterator.hasNext() && count < this.limit_) {
6403
6275
  const next = iterator.getNext();
6404
- let inRange;
6405
- if (this.reverse_) {
6406
- inRange =
6407
- this.index_.compare(this.rangedFilter_.getStartPost(), next) <= 0;
6276
+ if (!this.withinDirectionalStart(next)) {
6277
+ // if we have not reached the start, skip to the next element
6278
+ continue;
6408
6279
  }
6409
- else {
6410
- inRange =
6411
- this.index_.compare(next, this.rangedFilter_.getEndPost()) <= 0;
6280
+ else if (!this.withinDirectionalEnd(next)) {
6281
+ // if we have reached the end, stop adding elements
6282
+ break;
6412
6283
  }
6413
- if (inRange) {
6284
+ else {
6414
6285
  filtered = filtered.updateImmediateChild(next.name, next.node);
6415
6286
  count++;
6416
6287
  }
6417
- else {
6418
- // if we have reached the end post, we cannot keep adding elemments
6419
- break;
6420
- }
6421
6288
  }
6422
6289
  }
6423
6290
  else {
@@ -6425,32 +6292,19 @@ class LimitedFilter {
6425
6292
  filtered = newSnap.withIndex(this.index_);
6426
6293
  // Don't support priorities on queries
6427
6294
  filtered = filtered.updatePriority(ChildrenNode.EMPTY_NODE);
6428
- let startPost;
6429
- let endPost;
6430
- let cmp;
6431
6295
  let iterator;
6432
6296
  if (this.reverse_) {
6433
6297
  iterator = filtered.getReverseIterator(this.index_);
6434
- startPost = this.rangedFilter_.getEndPost();
6435
- endPost = this.rangedFilter_.getStartPost();
6436
- const indexCompare = this.index_.getCompare();
6437
- cmp = (a, b) => indexCompare(b, a);
6438
6298
  }
6439
6299
  else {
6440
6300
  iterator = filtered.getIterator(this.index_);
6441
- startPost = this.rangedFilter_.getStartPost();
6442
- endPost = this.rangedFilter_.getEndPost();
6443
- cmp = this.index_.getCompare();
6444
6301
  }
6445
6302
  let count = 0;
6446
- let foundStartPost = false;
6447
6303
  while (iterator.hasNext()) {
6448
6304
  const next = iterator.getNext();
6449
- if (!foundStartPost && cmp(startPost, next) <= 0) {
6450
- // start adding
6451
- foundStartPost = true;
6452
- }
6453
- const inRange = foundStartPost && count < this.limit_ && cmp(next, endPost) <= 0;
6305
+ const inRange = count < this.limit_ &&
6306
+ this.withinDirectionalStart(next) &&
6307
+ this.withinDirectionalEnd(next);
6454
6308
  if (inRange) {
6455
6309
  count++;
6456
6310
  }
@@ -6581,10 +6435,10 @@ class QueryParams {
6581
6435
  this.limitSet_ = false;
6582
6436
  this.startSet_ = false;
6583
6437
  this.startNameSet_ = false;
6584
- this.startAfterSet_ = false;
6438
+ this.startAfterSet_ = false; // can only be true if startSet_ is true
6585
6439
  this.endSet_ = false;
6586
6440
  this.endNameSet_ = false;
6587
- this.endBeforeSet_ = false;
6441
+ this.endBeforeSet_ = false; // can only be true if endSet_ is true
6588
6442
  this.limit_ = 0;
6589
6443
  this.viewFrom_ = '';
6590
6444
  this.indexStartValue_ = null;
@@ -6596,12 +6450,6 @@ class QueryParams {
6596
6450
  hasStart() {
6597
6451
  return this.startSet_;
6598
6452
  }
6599
- hasStartAfter() {
6600
- return this.startAfterSet_;
6601
- }
6602
- hasEndBefore() {
6603
- return this.endBeforeSet_;
6604
- }
6605
6453
  /**
6606
6454
  * @returns True if it would return from left.
6607
6455
  */
@@ -6690,10 +6538,12 @@ class QueryParams {
6690
6538
  copy.limitSet_ = this.limitSet_;
6691
6539
  copy.limit_ = this.limit_;
6692
6540
  copy.startSet_ = this.startSet_;
6541
+ copy.startAfterSet_ = this.startAfterSet_;
6693
6542
  copy.indexStartValue_ = this.indexStartValue_;
6694
6543
  copy.startNameSet_ = this.startNameSet_;
6695
6544
  copy.indexStartName_ = this.indexStartName_;
6696
6545
  copy.endSet_ = this.endSet_;
6546
+ copy.endBeforeSet_ = this.endBeforeSet_;
6697
6547
  copy.indexEndValue_ = this.indexEndValue_;
6698
6548
  copy.endNameSet_ = this.endNameSet_;
6699
6549
  copy.indexEndName_ = this.indexEndName_;
@@ -6746,21 +6596,11 @@ function queryParamsStartAt(queryParams, indexValue, key) {
6746
6596
  }
6747
6597
  function queryParamsStartAfter(queryParams, indexValue, key) {
6748
6598
  let params;
6749
- if (queryParams.index_ === KEY_INDEX) {
6750
- if (typeof indexValue === 'string') {
6751
- indexValue = successor(indexValue);
6752
- }
6599
+ if (queryParams.index_ === KEY_INDEX || !!key) {
6753
6600
  params = queryParamsStartAt(queryParams, indexValue, key);
6754
6601
  }
6755
6602
  else {
6756
- let childKey;
6757
- if (key == null) {
6758
- childKey = MAX_NAME;
6759
- }
6760
- else {
6761
- childKey = successor(key);
6762
- }
6763
- params = queryParamsStartAt(queryParams, indexValue, childKey);
6603
+ params = queryParamsStartAt(queryParams, indexValue, MAX_NAME);
6764
6604
  }
6765
6605
  params.startAfterSet_ = true;
6766
6606
  return params;
@@ -6783,22 +6623,12 @@ function queryParamsEndAt(queryParams, indexValue, key) {
6783
6623
  return newParams;
6784
6624
  }
6785
6625
  function queryParamsEndBefore(queryParams, indexValue, key) {
6786
- let childKey;
6787
6626
  let params;
6788
- if (queryParams.index_ === KEY_INDEX) {
6789
- if (typeof indexValue === 'string') {
6790
- indexValue = predecessor(indexValue);
6791
- }
6627
+ if (queryParams.index_ === KEY_INDEX || !!key) {
6792
6628
  params = queryParamsEndAt(queryParams, indexValue, key);
6793
6629
  }
6794
6630
  else {
6795
- if (key == null) {
6796
- childKey = MIN_NAME;
6797
- }
6798
- else {
6799
- childKey = predecessor(key);
6800
- }
6801
- params = queryParamsEndAt(queryParams, indexValue, childKey);
6631
+ params = queryParamsEndAt(queryParams, indexValue, MIN_NAME);
6802
6632
  }
6803
6633
  params.endBeforeSet_ = true;
6804
6634
  return params;
@@ -6834,17 +6664,21 @@ function queryParamsToRestQueryStringParameters(queryParams) {
6834
6664
  }
6835
6665
  qs["orderBy" /* ORDER_BY */] = stringify(orderBy);
6836
6666
  if (queryParams.startSet_) {
6837
- qs["startAt" /* START_AT */] = stringify(queryParams.indexStartValue_);
6667
+ const startParam = queryParams.startAfterSet_
6668
+ ? "startAfter" /* START_AFTER */
6669
+ : "startAt" /* START_AT */;
6670
+ qs[startParam] = stringify(queryParams.indexStartValue_);
6838
6671
  if (queryParams.startNameSet_) {
6839
- qs["startAt" /* START_AT */] +=
6840
- ',' + stringify(queryParams.indexStartName_);
6672
+ qs[startParam] += ',' + stringify(queryParams.indexStartName_);
6841
6673
  }
6842
6674
  }
6843
6675
  if (queryParams.endSet_) {
6844
- qs["endAt" /* END_AT */] = stringify(queryParams.indexEndValue_);
6676
+ const endParam = queryParams.endBeforeSet_
6677
+ ? "endBefore" /* END_BEFORE */
6678
+ : "endAt" /* END_AT */;
6679
+ qs[endParam] = stringify(queryParams.indexEndValue_);
6845
6680
  if (queryParams.endNameSet_) {
6846
- qs["endAt" /* END_AT */] +=
6847
- ',' + stringify(queryParams.indexEndName_);
6681
+ qs[endParam] += ',' + stringify(queryParams.indexEndName_);
6848
6682
  }
6849
6683
  }
6850
6684
  if (queryParams.limitSet_) {
@@ -6866,12 +6700,16 @@ function queryParamsGetQueryObject(queryParams) {
6866
6700
  obj["sn" /* INDEX_START_NAME */] =
6867
6701
  queryParams.indexStartName_;
6868
6702
  }
6703
+ obj["sin" /* INDEX_START_IS_INCLUSIVE */] =
6704
+ !queryParams.startAfterSet_;
6869
6705
  }
6870
6706
  if (queryParams.endSet_) {
6871
6707
  obj["ep" /* INDEX_END_VALUE */] = queryParams.indexEndValue_;
6872
6708
  if (queryParams.endNameSet_) {
6873
6709
  obj["en" /* INDEX_END_NAME */] = queryParams.indexEndName_;
6874
6710
  }
6711
+ obj["ein" /* INDEX_END_IS_INCLUSIVE */] =
6712
+ !queryParams.endBeforeSet_;
6875
6713
  }
6876
6714
  if (queryParams.limitSet_) {
6877
6715
  obj["l" /* LIMIT */] = queryParams.limit_;
@@ -12009,6 +11847,81 @@ const parseDatabaseURL = function (dataURL) {
12009
11847
  };
12010
11848
  };
12011
11849
 
11850
+ /**
11851
+ * @license
11852
+ * Copyright 2017 Google LLC
11853
+ *
11854
+ * Licensed under the Apache License, Version 2.0 (the "License");
11855
+ * you may not use this file except in compliance with the License.
11856
+ * You may obtain a copy of the License at
11857
+ *
11858
+ * http://www.apache.org/licenses/LICENSE-2.0
11859
+ *
11860
+ * Unless required by applicable law or agreed to in writing, software
11861
+ * distributed under the License is distributed on an "AS IS" BASIS,
11862
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11863
+ * See the License for the specific language governing permissions and
11864
+ * limitations under the License.
11865
+ */
11866
+ // Modeled after base64 web-safe chars, but ordered by ASCII.
11867
+ const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
11868
+ /**
11869
+ * Fancy ID generator that creates 20-character string identifiers with the
11870
+ * following properties:
11871
+ *
11872
+ * 1. They're based on timestamp so that they sort *after* any existing ids.
11873
+ * 2. They contain 72-bits of random data after the timestamp so that IDs won't
11874
+ * collide with other clients' IDs.
11875
+ * 3. They sort *lexicographically* (so the timestamp is converted to characters
11876
+ * that will sort properly).
11877
+ * 4. They're monotonically increasing. Even if you generate more than one in
11878
+ * the same timestamp, the latter ones will sort after the former ones. We do
11879
+ * this by using the previous random bits but "incrementing" them by 1 (only
11880
+ * in the case of a timestamp collision).
11881
+ */
11882
+ const nextPushId = (function () {
11883
+ // Timestamp of last push, used to prevent local collisions if you push twice
11884
+ // in one ms.
11885
+ let lastPushTime = 0;
11886
+ // We generate 72-bits of randomness which get turned into 12 characters and
11887
+ // appended to the timestamp to prevent collisions with other clients. We
11888
+ // store the last characters we generated because in the event of a collision,
11889
+ // we'll use those same characters except "incremented" by one.
11890
+ const lastRandChars = [];
11891
+ return function (now) {
11892
+ const duplicateTime = now === lastPushTime;
11893
+ lastPushTime = now;
11894
+ let i;
11895
+ const timeStampChars = new Array(8);
11896
+ for (i = 7; i >= 0; i--) {
11897
+ timeStampChars[i] = PUSH_CHARS.charAt(now % 64);
11898
+ // NOTE: Can't use << here because javascript will convert to int and lose
11899
+ // the upper bits.
11900
+ now = Math.floor(now / 64);
11901
+ }
11902
+ assert(now === 0, 'Cannot push at time == 0');
11903
+ let id = timeStampChars.join('');
11904
+ if (!duplicateTime) {
11905
+ for (i = 0; i < 12; i++) {
11906
+ lastRandChars[i] = Math.floor(Math.random() * 64);
11907
+ }
11908
+ }
11909
+ else {
11910
+ // If the timestamp hasn't changed since last push, use the same random
11911
+ // number, except incremented by 1.
11912
+ for (i = 11; i >= 0 && lastRandChars[i] === 63; i--) {
11913
+ lastRandChars[i] = 0;
11914
+ }
11915
+ lastRandChars[i]++;
11916
+ }
11917
+ for (i = 0; i < 12; i++) {
11918
+ id += PUSH_CHARS.charAt(lastRandChars[i]);
11919
+ }
11920
+ assert(id.length === 20, 'nextPushId: Length should be 20.');
11921
+ return id;
11922
+ };
11923
+ })();
11924
+
12012
11925
  /**
12013
11926
  * @license
12014
11927
  * Copyright 2017 Google LLC