@firebase/firestore 3.4.9-canary.f84cacd70 → 3.4.10

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.
Files changed (107) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/firestore/src/core/component_provider.d.ts +11 -6
  3. package/dist/firestore/src/local/document_overlay_cache.d.ts +1 -1
  4. package/dist/firestore/src/local/index_backfiller.d.ts +44 -0
  5. package/dist/firestore/src/local/index_manager.d.ts +2 -0
  6. package/dist/firestore/src/local/indexeddb_document_overlay_cache.d.ts +1 -1
  7. package/dist/firestore/src/local/indexeddb_index_manager.d.ts +1 -0
  8. package/dist/firestore/src/local/local_documents_view.d.ts +20 -4
  9. package/dist/firestore/src/local/local_store.d.ts +9 -0
  10. package/dist/firestore/src/local/lru_garbage_collector_impl.d.ts +5 -5
  11. package/dist/firestore/src/local/memory_document_overlay_cache.d.ts +1 -1
  12. package/dist/firestore/src/local/memory_index_manager.d.ts +1 -0
  13. package/dist/firestore/src/local/persistence.d.ts +3 -5
  14. package/dist/firestore/src/local/persistence_promise.d.ts +11 -0
  15. package/dist/firestore/src/model/collections.d.ts +6 -2
  16. package/dist/firestore/src/model/mutation_batch.d.ts +2 -3
  17. package/dist/firestore/src/util/async_queue.d.ts +5 -1
  18. package/dist/firestore/test/unit/local/index_backfiller.test.d.ts +17 -0
  19. package/dist/firestore/test/unit/local/test_document_overlay_cache.d.ts +1 -1
  20. package/dist/firestore/test/unit/specs/spec_test_components.d.ts +5 -3
  21. package/dist/firestore/test/util/helpers.d.ts +1 -0
  22. package/dist/index.esm2017.js +7499 -7351
  23. package/dist/index.esm2017.js.map +1 -1
  24. package/dist/index.esm5.js +8227 -7997
  25. package/dist/index.esm5.js.map +1 -1
  26. package/dist/index.node.cjs.js +1552 -1309
  27. package/dist/index.node.cjs.js.map +1 -1
  28. package/dist/index.node.mjs +1552 -1309
  29. package/dist/index.node.mjs.map +1 -1
  30. package/dist/index.rn.js +7491 -7342
  31. package/dist/index.rn.js.map +1 -1
  32. package/dist/internal.d.ts +146 -16
  33. package/dist/lite/firestore/src/core/component_provider.d.ts +11 -6
  34. package/dist/lite/firestore/src/local/document_overlay_cache.d.ts +1 -1
  35. package/dist/lite/firestore/src/local/index_backfiller.d.ts +44 -0
  36. package/dist/lite/firestore/src/local/index_manager.d.ts +2 -0
  37. package/dist/lite/firestore/src/local/indexeddb_document_overlay_cache.d.ts +1 -1
  38. package/dist/lite/firestore/src/local/indexeddb_index_manager.d.ts +1 -0
  39. package/dist/lite/firestore/src/local/local_documents_view.d.ts +20 -4
  40. package/dist/lite/firestore/src/local/local_store.d.ts +9 -0
  41. package/dist/lite/firestore/src/local/lru_garbage_collector_impl.d.ts +5 -5
  42. package/dist/lite/firestore/src/local/memory_document_overlay_cache.d.ts +1 -1
  43. package/dist/lite/firestore/src/local/memory_index_manager.d.ts +1 -0
  44. package/dist/lite/firestore/src/local/persistence.d.ts +3 -5
  45. package/dist/lite/firestore/src/local/persistence_promise.d.ts +11 -0
  46. package/dist/lite/firestore/src/model/collections.d.ts +6 -2
  47. package/dist/lite/firestore/src/model/mutation_batch.d.ts +2 -3
  48. package/dist/lite/firestore/src/util/async_queue.d.ts +5 -1
  49. package/dist/lite/firestore/test/unit/local/index_backfiller.test.d.ts +17 -0
  50. package/dist/lite/firestore/test/unit/local/test_document_overlay_cache.d.ts +1 -1
  51. package/dist/lite/firestore/test/unit/specs/spec_test_components.d.ts +5 -3
  52. package/dist/lite/firestore/test/util/helpers.d.ts +1 -0
  53. package/dist/lite/index.browser.esm2017.js +2 -2
  54. package/dist/lite/index.browser.esm2017.js.map +1 -1
  55. package/dist/lite/index.browser.esm5.js +2 -2
  56. package/dist/lite/index.browser.esm5.js.map +1 -1
  57. package/dist/lite/index.node.cjs.js +2 -2
  58. package/dist/lite/index.node.cjs.js.map +1 -1
  59. package/dist/lite/index.node.mjs +2 -2
  60. package/dist/lite/index.node.mjs.map +1 -1
  61. package/dist/lite/index.rn.esm2017.js +2 -2
  62. package/dist/lite/index.rn.esm2017.js.map +1 -1
  63. package/dist/lite/internal.d.ts +5 -1
  64. package/dist/lite/packages/firestore/src/core/component_provider.d.ts +11 -6
  65. package/dist/lite/packages/firestore/src/local/document_overlay_cache.d.ts +1 -1
  66. package/dist/lite/packages/firestore/src/local/index_backfiller.d.ts +44 -0
  67. package/dist/lite/packages/firestore/src/local/index_manager.d.ts +2 -0
  68. package/dist/lite/packages/firestore/src/local/indexeddb_document_overlay_cache.d.ts +1 -1
  69. package/dist/lite/packages/firestore/src/local/indexeddb_index_manager.d.ts +1 -0
  70. package/dist/lite/packages/firestore/src/local/local_documents_view.d.ts +20 -4
  71. package/dist/lite/packages/firestore/src/local/local_store.d.ts +9 -0
  72. package/dist/lite/packages/firestore/src/local/lru_garbage_collector_impl.d.ts +5 -5
  73. package/dist/lite/packages/firestore/src/local/memory_document_overlay_cache.d.ts +1 -1
  74. package/dist/lite/packages/firestore/src/local/memory_index_manager.d.ts +1 -0
  75. package/dist/lite/packages/firestore/src/local/persistence.d.ts +3 -5
  76. package/dist/lite/packages/firestore/src/local/persistence_promise.d.ts +11 -0
  77. package/dist/lite/packages/firestore/src/model/collections.d.ts +6 -2
  78. package/dist/lite/packages/firestore/src/model/mutation_batch.d.ts +2 -3
  79. package/dist/lite/packages/firestore/src/util/async_queue.d.ts +5 -1
  80. package/dist/lite/packages/firestore/test/unit/local/index_backfiller.test.d.ts +17 -0
  81. package/dist/lite/packages/firestore/test/unit/local/test_document_overlay_cache.d.ts +1 -1
  82. package/dist/lite/packages/firestore/test/unit/specs/spec_test_components.d.ts +5 -3
  83. package/dist/lite/packages/firestore/test/util/helpers.d.ts +1 -0
  84. package/dist/lite/private.d.ts +5 -1
  85. package/dist/packages/firestore/dist/index.esm2017.d.ts +200 -200
  86. package/dist/packages/firestore/src/core/component_provider.d.ts +11 -6
  87. package/dist/packages/firestore/src/local/document_overlay_cache.d.ts +1 -1
  88. package/dist/packages/firestore/src/local/index_backfiller.d.ts +44 -0
  89. package/dist/packages/firestore/src/local/index_manager.d.ts +2 -0
  90. package/dist/packages/firestore/src/local/indexeddb_document_overlay_cache.d.ts +1 -1
  91. package/dist/packages/firestore/src/local/indexeddb_index_manager.d.ts +1 -0
  92. package/dist/packages/firestore/src/local/local_documents_view.d.ts +20 -4
  93. package/dist/packages/firestore/src/local/local_store.d.ts +9 -0
  94. package/dist/packages/firestore/src/local/lru_garbage_collector_impl.d.ts +5 -5
  95. package/dist/packages/firestore/src/local/memory_document_overlay_cache.d.ts +1 -1
  96. package/dist/packages/firestore/src/local/memory_index_manager.d.ts +1 -0
  97. package/dist/packages/firestore/src/local/persistence.d.ts +3 -5
  98. package/dist/packages/firestore/src/local/persistence_promise.d.ts +11 -0
  99. package/dist/packages/firestore/src/model/collections.d.ts +6 -2
  100. package/dist/packages/firestore/src/model/mutation_batch.d.ts +2 -3
  101. package/dist/packages/firestore/src/util/async_queue.d.ts +5 -1
  102. package/dist/packages/firestore/test/unit/local/index_backfiller.test.d.ts +17 -0
  103. package/dist/packages/firestore/test/unit/local/test_document_overlay_cache.d.ts +1 -1
  104. package/dist/packages/firestore/test/unit/specs/spec_test_components.d.ts +5 -3
  105. package/dist/packages/firestore/test/util/helpers.d.ts +1 -0
  106. package/dist/private.d.ts +146 -16
  107. package/package.json +9 -9
@@ -33,7 +33,7 @@ var grpc__namespace = /*#__PURE__*/_interopNamespace(grpc);
33
33
  var protoLoader__namespace = /*#__PURE__*/_interopNamespace(protoLoader);
34
34
 
35
35
  const name = "@firebase/firestore";
36
- const version$1 = "3.4.9-canary.f84cacd70";
36
+ const version$1 = "3.4.10";
37
37
 
38
38
  /**
39
39
  * @license
@@ -86,7 +86,7 @@ User.GOOGLE_CREDENTIALS = new User('google-credentials-uid');
86
86
  User.FIRST_PARTY = new User('first-party-uid');
87
87
  User.MOCK_USER = new User('mock-user');
88
88
 
89
- const version = "9.8.2-canary.f84cacd70";
89
+ const version = "9.8.3";
90
90
 
91
91
  /**
92
92
  * @license
@@ -793,7 +793,7 @@ function makeAuthCredentialsProvider(credentials) {
793
793
 
794
794
  /**
795
795
  * @license
796
- * Copyright 2018 Google LLC
796
+ * Copyright 2020 Google LLC
797
797
  *
798
798
  * Licensed under the Apache License, Version 2.0 (the "License");
799
799
  * you may not use this file except in compliance with the License.
@@ -808,32 +808,75 @@ function makeAuthCredentialsProvider(credentials) {
808
808
  * limitations under the License.
809
809
  */
810
810
  /**
811
- * `ListenSequence` is a monotonic sequence. It is initialized with a minimum value to
812
- * exceed. All subsequent calls to next will return increasing values. If provided with a
813
- * `SequenceNumberSyncer`, it will additionally bump its next value when told of a new value, as
814
- * well as write out sequence numbers that it produces via `next()`.
811
+ * Generates `nBytes` of random bytes.
812
+ *
813
+ * If `nBytes < 0` , an error will be thrown.
815
814
  */
816
- class ListenSequence {
817
- constructor(previousValue, sequenceNumberSyncer) {
818
- this.previousValue = previousValue;
819
- if (sequenceNumberSyncer) {
820
- sequenceNumberSyncer.sequenceNumberHandler = sequenceNumber => this.setPreviousValue(sequenceNumber);
821
- this.writeNewSequenceNumber = sequenceNumber => sequenceNumberSyncer.writeSequenceNumber(sequenceNumber);
815
+ function randomBytes(nBytes) {
816
+ return crypto.randomBytes(nBytes);
817
+ }
818
+
819
+ /**
820
+ * @license
821
+ * Copyright 2017 Google LLC
822
+ *
823
+ * Licensed under the Apache License, Version 2.0 (the "License");
824
+ * you may not use this file except in compliance with the License.
825
+ * You may obtain a copy of the License at
826
+ *
827
+ * http://www.apache.org/licenses/LICENSE-2.0
828
+ *
829
+ * Unless required by applicable law or agreed to in writing, software
830
+ * distributed under the License is distributed on an "AS IS" BASIS,
831
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
832
+ * See the License for the specific language governing permissions and
833
+ * limitations under the License.
834
+ */
835
+ class AutoId {
836
+ static newId() {
837
+ // Alphanumeric characters
838
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
839
+ // The largest byte value that is a multiple of `char.length`.
840
+ const maxMultiple = Math.floor(256 / chars.length) * chars.length;
841
+ let autoId = '';
842
+ const targetLength = 20;
843
+ while (autoId.length < targetLength) {
844
+ const bytes = randomBytes(40);
845
+ for (let i = 0; i < bytes.length; ++i) {
846
+ // Only accept values that are [0, maxMultiple), this ensures they can
847
+ // be evenly mapped to indices of `chars` via a modulo operation.
848
+ if (autoId.length < targetLength && bytes[i] < maxMultiple) {
849
+ autoId += chars.charAt(bytes[i] % chars.length);
850
+ }
851
+ }
822
852
  }
853
+ return autoId;
823
854
  }
824
- setPreviousValue(externalPreviousValue) {
825
- this.previousValue = Math.max(externalPreviousValue, this.previousValue);
826
- return this.previousValue;
855
+ }
856
+ function primitiveComparator(left, right) {
857
+ if (left < right) {
858
+ return -1;
827
859
  }
828
- next() {
829
- const nextValue = ++this.previousValue;
830
- if (this.writeNewSequenceNumber) {
831
- this.writeNewSequenceNumber(nextValue);
832
- }
833
- return nextValue;
860
+ if (left > right) {
861
+ return 1;
834
862
  }
863
+ return 0;
835
864
  }
836
- ListenSequence.INVALID = -1;
865
+ /** Helper to compare arrays using isEqual(). */
866
+ function arrayEquals(left, right, comparator) {
867
+ if (left.length !== right.length) {
868
+ return false;
869
+ }
870
+ return left.every((value, index) => comparator(value, right[index]));
871
+ }
872
+ /**
873
+ * Returns the immediate lexicographically-following string. This is useful to
874
+ * construct an inclusive range for indexeddb iterators.
875
+ */
876
+ function immediateSuccessor(s) {
877
+ // Return the input string, with an additional NUL byte appended.
878
+ return s + '\0';
879
+ }
837
880
 
838
881
  /**
839
882
  * @license
@@ -851,75 +894,298 @@ ListenSequence.INVALID = -1;
851
894
  * See the License for the specific language governing permissions and
852
895
  * limitations under the License.
853
896
  */
854
- const DOCUMENT_KEY_NAME = '__name__';
897
+ // The earliest date supported by Firestore timestamps (0001-01-01T00:00:00Z).
898
+ const MIN_SECONDS = -62135596800;
899
+ // Number of nanoseconds in a millisecond.
900
+ const MS_TO_NANOS = 1e6;
855
901
  /**
856
- * Path represents an ordered sequence of string segments.
902
+ * A `Timestamp` represents a point in time independent of any time zone or
903
+ * calendar, represented as seconds and fractions of seconds at nanosecond
904
+ * resolution in UTC Epoch time.
905
+ *
906
+ * It is encoded using the Proleptic Gregorian Calendar which extends the
907
+ * Gregorian calendar backwards to year one. It is encoded assuming all minutes
908
+ * are 60 seconds long, i.e. leap seconds are "smeared" so that no leap second
909
+ * table is needed for interpretation. Range is from 0001-01-01T00:00:00Z to
910
+ * 9999-12-31T23:59:59.999999999Z.
911
+ *
912
+ * For examples and further specifications, refer to the
913
+ * {@link https://github.com/google/protobuf/blob/master/src/google/protobuf/timestamp.proto | Timestamp definition}.
857
914
  */
858
- class BasePath {
859
- constructor(segments, offset, length) {
860
- if (offset === undefined) {
861
- offset = 0;
915
+ class Timestamp {
916
+ /**
917
+ * Creates a new timestamp.
918
+ *
919
+ * @param seconds - The number of seconds of UTC time since Unix epoch
920
+ * 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
921
+ * 9999-12-31T23:59:59Z inclusive.
922
+ * @param nanoseconds - The non-negative fractions of a second at nanosecond
923
+ * resolution. Negative second values with fractions must still have
924
+ * non-negative nanoseconds values that count forward in time. Must be
925
+ * from 0 to 999,999,999 inclusive.
926
+ */
927
+ constructor(
928
+ /**
929
+ * The number of seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z.
930
+ */
931
+ seconds,
932
+ /**
933
+ * The fractions of a second at nanosecond resolution.*
934
+ */
935
+ nanoseconds) {
936
+ this.seconds = seconds;
937
+ this.nanoseconds = nanoseconds;
938
+ if (nanoseconds < 0) {
939
+ throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp nanoseconds out of range: ' + nanoseconds);
862
940
  }
863
- else if (offset > segments.length) {
864
- fail();
941
+ if (nanoseconds >= 1e9) {
942
+ throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp nanoseconds out of range: ' + nanoseconds);
865
943
  }
866
- if (length === undefined) {
867
- length = segments.length - offset;
944
+ if (seconds < MIN_SECONDS) {
945
+ throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp seconds out of range: ' + seconds);
868
946
  }
869
- else if (length > segments.length - offset) {
870
- fail();
947
+ // This will break in the year 10,000.
948
+ if (seconds >= 253402300800) {
949
+ throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp seconds out of range: ' + seconds);
871
950
  }
872
- this.segments = segments;
873
- this.offset = offset;
874
- this.len = length;
875
951
  }
876
- get length() {
877
- return this.len;
878
- }
879
- isEqual(other) {
880
- return BasePath.comparator(this, other) === 0;
952
+ /**
953
+ * Creates a new timestamp with the current date, with millisecond precision.
954
+ *
955
+ * @returns a new timestamp representing the current date.
956
+ */
957
+ static now() {
958
+ return Timestamp.fromMillis(Date.now());
881
959
  }
882
- child(nameOrPath) {
883
- const segments = this.segments.slice(this.offset, this.limit());
884
- if (nameOrPath instanceof BasePath) {
885
- nameOrPath.forEach(segment => {
886
- segments.push(segment);
887
- });
888
- }
889
- else {
890
- segments.push(nameOrPath);
891
- }
892
- return this.construct(segments);
960
+ /**
961
+ * Creates a new timestamp from the given date.
962
+ *
963
+ * @param date - The date to initialize the `Timestamp` from.
964
+ * @returns A new `Timestamp` representing the same point in time as the given
965
+ * date.
966
+ */
967
+ static fromDate(date) {
968
+ return Timestamp.fromMillis(date.getTime());
893
969
  }
894
- /** The index of one past the last segment of the path. */
895
- limit() {
896
- return this.offset + this.length;
970
+ /**
971
+ * Creates a new timestamp from the given number of milliseconds.
972
+ *
973
+ * @param milliseconds - Number of milliseconds since Unix epoch
974
+ * 1970-01-01T00:00:00Z.
975
+ * @returns A new `Timestamp` representing the same point in time as the given
976
+ * number of milliseconds.
977
+ */
978
+ static fromMillis(milliseconds) {
979
+ const seconds = Math.floor(milliseconds / 1000);
980
+ const nanos = Math.floor((milliseconds - seconds * 1000) * MS_TO_NANOS);
981
+ return new Timestamp(seconds, nanos);
897
982
  }
898
- popFirst(size) {
899
- size = size === undefined ? 1 : size;
900
- return this.construct(this.segments, this.offset + size, this.length - size);
983
+ /**
984
+ * Converts a `Timestamp` to a JavaScript `Date` object. This conversion
985
+ * causes a loss of precision since `Date` objects only support millisecond
986
+ * precision.
987
+ *
988
+ * @returns JavaScript `Date` object representing the same point in time as
989
+ * this `Timestamp`, with millisecond precision.
990
+ */
991
+ toDate() {
992
+ return new Date(this.toMillis());
901
993
  }
902
- popLast() {
903
- return this.construct(this.segments, this.offset, this.length - 1);
994
+ /**
995
+ * Converts a `Timestamp` to a numeric timestamp (in milliseconds since
996
+ * epoch). This operation causes a loss of precision.
997
+ *
998
+ * @returns The point in time corresponding to this timestamp, represented as
999
+ * the number of milliseconds since Unix epoch 1970-01-01T00:00:00Z.
1000
+ */
1001
+ toMillis() {
1002
+ return this.seconds * 1000 + this.nanoseconds / MS_TO_NANOS;
904
1003
  }
905
- firstSegment() {
906
- return this.segments[this.offset];
1004
+ _compareTo(other) {
1005
+ if (this.seconds === other.seconds) {
1006
+ return primitiveComparator(this.nanoseconds, other.nanoseconds);
1007
+ }
1008
+ return primitiveComparator(this.seconds, other.seconds);
907
1009
  }
908
- lastSegment() {
909
- return this.get(this.length - 1);
1010
+ /**
1011
+ * Returns true if this `Timestamp` is equal to the provided one.
1012
+ *
1013
+ * @param other - The `Timestamp` to compare against.
1014
+ * @returns true if this `Timestamp` is equal to the provided one.
1015
+ */
1016
+ isEqual(other) {
1017
+ return (other.seconds === this.seconds && other.nanoseconds === this.nanoseconds);
910
1018
  }
911
- get(index) {
912
- return this.segments[this.offset + index];
1019
+ /** Returns a textual representation of this `Timestamp`. */
1020
+ toString() {
1021
+ return ('Timestamp(seconds=' +
1022
+ this.seconds +
1023
+ ', nanoseconds=' +
1024
+ this.nanoseconds +
1025
+ ')');
913
1026
  }
914
- isEmpty() {
915
- return this.length === 0;
1027
+ /** Returns a JSON-serializable representation of this `Timestamp`. */
1028
+ toJSON() {
1029
+ return { seconds: this.seconds, nanoseconds: this.nanoseconds };
916
1030
  }
917
- isPrefixOf(other) {
918
- if (other.length < this.length) {
919
- return false;
920
- }
921
- for (let i = 0; i < this.length; i++) {
922
- if (this.get(i) !== other.get(i)) {
1031
+ /**
1032
+ * Converts this object to a primitive string, which allows `Timestamp` objects
1033
+ * to be compared using the `>`, `<=`, `>=` and `>` operators.
1034
+ */
1035
+ valueOf() {
1036
+ // This method returns a string of the form <seconds>.<nanoseconds> where
1037
+ // <seconds> is translated to have a non-negative value and both <seconds>
1038
+ // and <nanoseconds> are left-padded with zeroes to be a consistent length.
1039
+ // Strings with this format then have a lexiographical ordering that matches
1040
+ // the expected ordering. The <seconds> translation is done to avoid having
1041
+ // a leading negative sign (i.e. a leading '-' character) in its string
1042
+ // representation, which would affect its lexiographical ordering.
1043
+ const adjustedSeconds = this.seconds - MIN_SECONDS;
1044
+ // Note: Up to 12 decimal digits are required to represent all valid
1045
+ // 'seconds' values.
1046
+ const formattedSeconds = String(adjustedSeconds).padStart(12, '0');
1047
+ const formattedNanoseconds = String(this.nanoseconds).padStart(9, '0');
1048
+ return formattedSeconds + '.' + formattedNanoseconds;
1049
+ }
1050
+ }
1051
+
1052
+ /**
1053
+ * @license
1054
+ * Copyright 2017 Google LLC
1055
+ *
1056
+ * Licensed under the Apache License, Version 2.0 (the "License");
1057
+ * you may not use this file except in compliance with the License.
1058
+ * You may obtain a copy of the License at
1059
+ *
1060
+ * http://www.apache.org/licenses/LICENSE-2.0
1061
+ *
1062
+ * Unless required by applicable law or agreed to in writing, software
1063
+ * distributed under the License is distributed on an "AS IS" BASIS,
1064
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1065
+ * See the License for the specific language governing permissions and
1066
+ * limitations under the License.
1067
+ */
1068
+ /**
1069
+ * A version of a document in Firestore. This corresponds to the version
1070
+ * timestamp, such as update_time or read_time.
1071
+ */
1072
+ class SnapshotVersion {
1073
+ constructor(timestamp) {
1074
+ this.timestamp = timestamp;
1075
+ }
1076
+ static fromTimestamp(value) {
1077
+ return new SnapshotVersion(value);
1078
+ }
1079
+ static min() {
1080
+ return new SnapshotVersion(new Timestamp(0, 0));
1081
+ }
1082
+ static max() {
1083
+ return new SnapshotVersion(new Timestamp(253402300799, 1e9 - 1));
1084
+ }
1085
+ compareTo(other) {
1086
+ return this.timestamp._compareTo(other.timestamp);
1087
+ }
1088
+ isEqual(other) {
1089
+ return this.timestamp.isEqual(other.timestamp);
1090
+ }
1091
+ /** Returns a number representation of the version for use in spec tests. */
1092
+ toMicroseconds() {
1093
+ // Convert to microseconds.
1094
+ return this.timestamp.seconds * 1e6 + this.timestamp.nanoseconds / 1000;
1095
+ }
1096
+ toString() {
1097
+ return 'SnapshotVersion(' + this.timestamp.toString() + ')';
1098
+ }
1099
+ toTimestamp() {
1100
+ return this.timestamp;
1101
+ }
1102
+ }
1103
+
1104
+ /**
1105
+ * @license
1106
+ * Copyright 2017 Google LLC
1107
+ *
1108
+ * Licensed under the Apache License, Version 2.0 (the "License");
1109
+ * you may not use this file except in compliance with the License.
1110
+ * You may obtain a copy of the License at
1111
+ *
1112
+ * http://www.apache.org/licenses/LICENSE-2.0
1113
+ *
1114
+ * Unless required by applicable law or agreed to in writing, software
1115
+ * distributed under the License is distributed on an "AS IS" BASIS,
1116
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1117
+ * See the License for the specific language governing permissions and
1118
+ * limitations under the License.
1119
+ */
1120
+ const DOCUMENT_KEY_NAME = '__name__';
1121
+ /**
1122
+ * Path represents an ordered sequence of string segments.
1123
+ */
1124
+ class BasePath {
1125
+ constructor(segments, offset, length) {
1126
+ if (offset === undefined) {
1127
+ offset = 0;
1128
+ }
1129
+ else if (offset > segments.length) {
1130
+ fail();
1131
+ }
1132
+ if (length === undefined) {
1133
+ length = segments.length - offset;
1134
+ }
1135
+ else if (length > segments.length - offset) {
1136
+ fail();
1137
+ }
1138
+ this.segments = segments;
1139
+ this.offset = offset;
1140
+ this.len = length;
1141
+ }
1142
+ get length() {
1143
+ return this.len;
1144
+ }
1145
+ isEqual(other) {
1146
+ return BasePath.comparator(this, other) === 0;
1147
+ }
1148
+ child(nameOrPath) {
1149
+ const segments = this.segments.slice(this.offset, this.limit());
1150
+ if (nameOrPath instanceof BasePath) {
1151
+ nameOrPath.forEach(segment => {
1152
+ segments.push(segment);
1153
+ });
1154
+ }
1155
+ else {
1156
+ segments.push(nameOrPath);
1157
+ }
1158
+ return this.construct(segments);
1159
+ }
1160
+ /** The index of one past the last segment of the path. */
1161
+ limit() {
1162
+ return this.offset + this.length;
1163
+ }
1164
+ popFirst(size) {
1165
+ size = size === undefined ? 1 : size;
1166
+ return this.construct(this.segments, this.offset + size, this.length - size);
1167
+ }
1168
+ popLast() {
1169
+ return this.construct(this.segments, this.offset, this.length - 1);
1170
+ }
1171
+ firstSegment() {
1172
+ return this.segments[this.offset];
1173
+ }
1174
+ lastSegment() {
1175
+ return this.get(this.length - 1);
1176
+ }
1177
+ get(index) {
1178
+ return this.segments[this.offset + index];
1179
+ }
1180
+ isEmpty() {
1181
+ return this.length === 0;
1182
+ }
1183
+ isPrefixOf(other) {
1184
+ if (other.length < this.length) {
1185
+ return false;
1186
+ }
1187
+ for (let i = 0; i < this.length; i++) {
1188
+ if (this.get(i) !== other.get(i)) {
923
1189
  return false;
924
1190
  }
925
1191
  }
@@ -1125,109 +1391,64 @@ class FieldPath$1 extends BasePath {
1125
1391
  * See the License for the specific language governing permissions and
1126
1392
  * limitations under the License.
1127
1393
  */
1128
- const escapeChar = '\u0001';
1129
- const encodedSeparatorChar = '\u0001';
1130
- const encodedNul = '\u0010';
1131
- const encodedEscape = '\u0011';
1132
1394
  /**
1133
- * Encodes a resource path into a IndexedDb-compatible string form.
1395
+ * @internal
1134
1396
  */
1135
- function encodeResourcePath(path) {
1136
- let result = '';
1137
- for (let i = 0; i < path.length; i++) {
1138
- if (result.length > 0) {
1139
- result = encodeSeparator(result);
1140
- }
1141
- result = encodeSegment(path.get(i), result);
1397
+ class DocumentKey {
1398
+ constructor(path) {
1399
+ this.path = path;
1142
1400
  }
1143
- return encodeSeparator(result);
1144
- }
1145
- /** Encodes a single segment of a resource path into the given result */
1146
- function encodeSegment(segment, resultBuf) {
1147
- let result = resultBuf;
1148
- const length = segment.length;
1149
- for (let i = 0; i < length; i++) {
1150
- const c = segment.charAt(i);
1151
- switch (c) {
1152
- case '\0':
1153
- result += escapeChar + encodedNul;
1154
- break;
1155
- case escapeChar:
1156
- result += escapeChar + encodedEscape;
1157
- break;
1158
- default:
1159
- result += c;
1160
- }
1401
+ static fromPath(path) {
1402
+ return new DocumentKey(ResourcePath.fromString(path));
1161
1403
  }
1162
- return result;
1163
- }
1164
- /** Encodes a path separator into the given result */
1165
- function encodeSeparator(result) {
1166
- return result + escapeChar + encodedSeparatorChar;
1167
- }
1168
- /**
1169
- * Decodes the given IndexedDb-compatible string form of a resource path into
1170
- * a ResourcePath instance. Note that this method is not suitable for use with
1171
- * decoding resource names from the server; those are One Platform format
1172
- * strings.
1173
- */
1174
- function decodeResourcePath(path) {
1175
- // Event the empty path must encode as a path of at least length 2. A path
1176
- // with exactly 2 must be the empty path.
1177
- const length = path.length;
1178
- hardAssert(length >= 2);
1179
- if (length === 2) {
1180
- hardAssert(path.charAt(0) === escapeChar && path.charAt(1) === encodedSeparatorChar);
1181
- return ResourcePath.emptyPath();
1404
+ static fromName(name) {
1405
+ return new DocumentKey(ResourcePath.fromString(name).popFirst(5));
1182
1406
  }
1183
- // Escape characters cannot exist past the second-to-last position in the
1184
- // source value.
1185
- const lastReasonableEscapeIndex = length - 2;
1186
- const segments = [];
1187
- let segmentBuilder = '';
1188
- for (let start = 0; start < length;) {
1189
- // The last two characters of a valid encoded path must be a separator, so
1190
- // there must be an end to this segment.
1191
- const end = path.indexOf(escapeChar, start);
1192
- if (end < 0 || end > lastReasonableEscapeIndex) {
1193
- fail();
1194
- }
1195
- const next = path.charAt(end + 1);
1196
- switch (next) {
1197
- case encodedSeparatorChar:
1198
- const currentPiece = path.substring(start, end);
1199
- let segment;
1200
- if (segmentBuilder.length === 0) {
1201
- // Avoid copying for the common case of a segment that excludes \0
1202
- // and \001
1203
- segment = currentPiece;
1204
- }
1205
- else {
1206
- segmentBuilder += currentPiece;
1207
- segment = segmentBuilder;
1208
- segmentBuilder = '';
1209
- }
1210
- segments.push(segment);
1211
- break;
1212
- case encodedNul:
1213
- segmentBuilder += path.substring(start, end);
1214
- segmentBuilder += '\0';
1215
- break;
1216
- case encodedEscape:
1217
- // The escape character can be used in the output to encode itself.
1218
- segmentBuilder += path.substring(start, end + 1);
1219
- break;
1220
- default:
1221
- fail();
1222
- }
1223
- start = end + 2;
1407
+ static empty() {
1408
+ return new DocumentKey(ResourcePath.emptyPath());
1409
+ }
1410
+ get collectionGroup() {
1411
+ return this.path.popLast().lastSegment();
1412
+ }
1413
+ /** Returns true if the document is in the specified collectionId. */
1414
+ hasCollectionId(collectionId) {
1415
+ return (this.path.length >= 2 &&
1416
+ this.path.get(this.path.length - 2) === collectionId);
1417
+ }
1418
+ /** Returns the collection group (i.e. the name of the parent collection) for this key. */
1419
+ getCollectionGroup() {
1420
+ return this.path.get(this.path.length - 2);
1421
+ }
1422
+ /** Returns the fully qualified path to the parent collection. */
1423
+ getCollectionPath() {
1424
+ return this.path.popLast();
1425
+ }
1426
+ isEqual(other) {
1427
+ return (other !== null && ResourcePath.comparator(this.path, other.path) === 0);
1428
+ }
1429
+ toString() {
1430
+ return this.path.toString();
1431
+ }
1432
+ static comparator(k1, k2) {
1433
+ return ResourcePath.comparator(k1.path, k2.path);
1434
+ }
1435
+ static isDocumentKey(path) {
1436
+ return path.length % 2 === 0;
1437
+ }
1438
+ /**
1439
+ * Creates and returns a new document key with the given segments.
1440
+ *
1441
+ * @param segments - The segments of the path to the document
1442
+ * @returns A new instance of DocumentKey
1443
+ */
1444
+ static fromSegments(segments) {
1445
+ return new DocumentKey(new ResourcePath(segments.slice()));
1224
1446
  }
1225
- return new ResourcePath(segments);
1226
1447
  }
1227
1448
 
1228
1449
  /**
1229
1450
  * @license
1230
- * Copyright 2022 Google LLC
1451
+ * Copyright 2021 Google LLC
1231
1452
  *
1232
1453
  * Licensed under the Apache License, Version 2.0 (the "License");
1233
1454
  * you may not use this file except in compliance with the License.
@@ -1241,11 +1462,197 @@ function decodeResourcePath(path) {
1241
1462
  * See the License for the specific language governing permissions and
1242
1463
  * limitations under the License.
1243
1464
  */
1244
- const DbRemoteDocumentStore$1 = 'remoteDocuments';
1465
+ /**
1466
+ * The initial mutation batch id for each index. Gets updated during index
1467
+ * backfill.
1468
+ */
1469
+ const INITIAL_LARGEST_BATCH_ID = -1;
1470
+ /**
1471
+ * The initial sequence number for each index. Gets updated during index
1472
+ * backfill.
1473
+ */
1474
+ const INITIAL_SEQUENCE_NUMBER = 0;
1475
+ /**
1476
+ * An index definition for field indexes in Firestore.
1477
+ *
1478
+ * Every index is associated with a collection. The definition contains a list
1479
+ * of fields and their index kind (which can be `ASCENDING`, `DESCENDING` or
1480
+ * `CONTAINS` for ArrayContains/ArrayContainsAny queries).
1481
+ *
1482
+ * Unlike the backend, the SDK does not differentiate between collection or
1483
+ * collection group-scoped indices. Every index can be used for both single
1484
+ * collection and collection group queries.
1485
+ */
1486
+ class FieldIndex {
1487
+ constructor(
1488
+ /**
1489
+ * The index ID. Returns -1 if the index ID is not available (e.g. the index
1490
+ * has not yet been persisted).
1491
+ */
1492
+ indexId,
1493
+ /** The collection ID this index applies to. */
1494
+ collectionGroup,
1495
+ /** The field segments for this index. */
1496
+ fields,
1497
+ /** Shows how up-to-date the index is for the current user. */
1498
+ indexState) {
1499
+ this.indexId = indexId;
1500
+ this.collectionGroup = collectionGroup;
1501
+ this.fields = fields;
1502
+ this.indexState = indexState;
1503
+ }
1504
+ }
1505
+ /** An ID for an index that has not yet been added to persistence. */
1506
+ FieldIndex.UNKNOWN_ID = -1;
1507
+ /** Returns the ArrayContains/ArrayContainsAny segment for this index. */
1508
+ function fieldIndexGetArraySegment(fieldIndex) {
1509
+ return fieldIndex.fields.find(s => s.kind === 2 /* CONTAINS */);
1510
+ }
1511
+ /** Returns all directional (ascending/descending) segments for this index. */
1512
+ function fieldIndexGetDirectionalSegments(fieldIndex) {
1513
+ return fieldIndex.fields.filter(s => s.kind !== 2 /* CONTAINS */);
1514
+ }
1515
+ /**
1516
+ * Returns the order of the document key component for the given index.
1517
+ *
1518
+ * PORTING NOTE: This is only used in the Web IndexedDb implementation.
1519
+ */
1520
+ function fieldIndexGetKeyOrder(fieldIndex) {
1521
+ const directionalSegments = fieldIndexGetDirectionalSegments(fieldIndex);
1522
+ return directionalSegments.length === 0
1523
+ ? 0 /* ASCENDING */
1524
+ : directionalSegments[directionalSegments.length - 1].kind;
1525
+ }
1526
+ /** Returns a debug representation of the field index */
1527
+ function fieldIndexToString(fieldIndex) {
1528
+ return `id=${fieldIndex.indexId}|cg=${fieldIndex.collectionGroup}|f=${fieldIndex.fields.map(f => `${f.fieldPath}:${f.kind}`).join(',')}`;
1529
+ }
1530
+ /** An index component consisting of field path and index type. */
1531
+ class IndexSegment {
1532
+ constructor(
1533
+ /** The field path of the component. */
1534
+ fieldPath,
1535
+ /** The fields sorting order. */
1536
+ kind) {
1537
+ this.fieldPath = fieldPath;
1538
+ this.kind = kind;
1539
+ }
1540
+ }
1541
+ /**
1542
+ * Stores the "high water mark" that indicates how updated the Index is for the
1543
+ * current user.
1544
+ */
1545
+ class IndexState {
1546
+ constructor(
1547
+ /**
1548
+ * Indicates when the index was last updated (relative to other indexes).
1549
+ */
1550
+ sequenceNumber,
1551
+ /** The the latest indexed read time, document and batch id. */
1552
+ offset) {
1553
+ this.sequenceNumber = sequenceNumber;
1554
+ this.offset = offset;
1555
+ }
1556
+ /** The state of an index that has not yet been backfilled. */
1557
+ static empty() {
1558
+ return new IndexState(INITIAL_SEQUENCE_NUMBER, IndexOffset.min());
1559
+ }
1560
+ }
1561
+ /**
1562
+ * Creates an offset that matches all documents with a read time higher than
1563
+ * `readTime`.
1564
+ */
1565
+ function newIndexOffsetSuccessorFromReadTime(readTime, largestBatchId) {
1566
+ // We want to create an offset that matches all documents with a read time
1567
+ // greater than the provided read time. To do so, we technically need to
1568
+ // create an offset for `(readTime, MAX_DOCUMENT_KEY)`. While we could use
1569
+ // Unicode codepoints to generate MAX_DOCUMENT_KEY, it is much easier to use
1570
+ // `(readTime + 1, DocumentKey.empty())` since `> DocumentKey.empty()` matches
1571
+ // all valid document IDs.
1572
+ const successorSeconds = readTime.toTimestamp().seconds;
1573
+ const successorNanos = readTime.toTimestamp().nanoseconds + 1;
1574
+ const successor = SnapshotVersion.fromTimestamp(successorNanos === 1e9
1575
+ ? new Timestamp(successorSeconds + 1, 0)
1576
+ : new Timestamp(successorSeconds, successorNanos));
1577
+ return new IndexOffset(successor, DocumentKey.empty(), largestBatchId);
1578
+ }
1579
+ /** Creates a new offset based on the provided document. */
1580
+ function newIndexOffsetFromDocument(document) {
1581
+ return new IndexOffset(document.readTime, document.key, INITIAL_LARGEST_BATCH_ID);
1582
+ }
1583
+ /**
1584
+ * Stores the latest read time, document and batch ID that were processed for an
1585
+ * index.
1586
+ */
1587
+ class IndexOffset {
1588
+ constructor(
1589
+ /**
1590
+ * The latest read time version that has been indexed by Firestore for this
1591
+ * field index.
1592
+ */
1593
+ readTime,
1594
+ /**
1595
+ * The key of the last document that was indexed for this query. Use
1596
+ * `DocumentKey.empty()` if no document has been indexed.
1597
+ */
1598
+ documentKey,
1599
+ /*
1600
+ * The largest mutation batch id that's been processed by Firestore.
1601
+ */
1602
+ largestBatchId) {
1603
+ this.readTime = readTime;
1604
+ this.documentKey = documentKey;
1605
+ this.largestBatchId = largestBatchId;
1606
+ }
1607
+ /** Returns an offset that sorts before all regular offsets. */
1608
+ static min() {
1609
+ return new IndexOffset(SnapshotVersion.min(), DocumentKey.empty(), INITIAL_LARGEST_BATCH_ID);
1610
+ }
1611
+ /** Returns an offset that sorts after all regular offsets. */
1612
+ static max() {
1613
+ return new IndexOffset(SnapshotVersion.max(), DocumentKey.empty(), INITIAL_LARGEST_BATCH_ID);
1614
+ }
1615
+ }
1616
+ function indexOffsetComparator(left, right) {
1617
+ let cmp = left.readTime.compareTo(right.readTime);
1618
+ if (cmp !== 0) {
1619
+ return cmp;
1620
+ }
1621
+ cmp = DocumentKey.comparator(left.documentKey, right.documentKey);
1622
+ if (cmp !== 0) {
1623
+ return cmp;
1624
+ }
1625
+ return primitiveComparator(left.largestBatchId, right.largestBatchId);
1626
+ }
1627
+ /**
1628
+ * Schema Version for the Web client:
1629
+ * 1. Initial version including Mutation Queue, Query Cache, and Remote
1630
+ * Document Cache
1631
+ * 2. Used to ensure a targetGlobal object exists and add targetCount to it. No
1632
+ * longer required because migration 3 unconditionally clears it.
1633
+ * 3. Dropped and re-created Query Cache to deal with cache corruption related
1634
+ * to limbo resolution. Addresses
1635
+ * https://github.com/firebase/firebase-ios-sdk/issues/1548
1636
+ * 4. Multi-Tab Support.
1637
+ * 5. Removal of held write acks.
1638
+ * 6. Create document global for tracking document cache size.
1639
+ * 7. Ensure every cached document has a sentinel row with a sequence number.
1640
+ * 8. Add collection-parent index for Collection Group queries.
1641
+ * 9. Change RemoteDocumentChanges store to be keyed by readTime rather than
1642
+ * an auto-incrementing ID. This is required for Index-Free queries.
1643
+ * 10. Rewrite the canonical IDs to the explicit Protobuf-based format.
1644
+ * 11. Add bundles and named_queries for bundle support.
1645
+ * 12. Add document overlays.
1646
+ * 13. Rewrite the keys of the remote document cache to allow for efficient
1647
+ * document lookup via `getAll()`.
1648
+ * 14. Add overlays.
1649
+ * 15. Add indexing support.
1650
+ */
1651
+ const SCHEMA_VERSION = 14;
1245
1652
 
1246
1653
  /**
1247
1654
  * @license
1248
- * Copyright 2022 Google LLC
1655
+ * Copyright 2020 Google LLC
1249
1656
  *
1250
1657
  * Licensed under the Apache License, Version 2.0 (the "License");
1251
1658
  * you may not use this file except in compliance with the License.
@@ -1259,260 +1666,31 @@ const DbRemoteDocumentStore$1 = 'remoteDocuments';
1259
1666
  * See the License for the specific language governing permissions and
1260
1667
  * limitations under the License.
1261
1668
  */
1669
+ const PRIMARY_LEASE_LOST_ERROR_MSG = 'The current tab is not in the required state to perform this operation. ' +
1670
+ 'It might be necessary to refresh the browser tab.';
1262
1671
  /**
1263
- * Name of the IndexedDb object store.
1672
+ * A base class representing a persistence transaction, encapsulating both the
1673
+ * transaction's sequence numbers as well as a list of onCommitted listeners.
1264
1674
  *
1265
- * Note that the name 'owner' is chosen to ensure backwards compatibility with
1266
- * older clients that only supported single locked access to the persistence
1267
- * layer.
1268
- */
1269
- const DbPrimaryClientStore = 'owner';
1270
- /**
1271
- * The key string used for the single object that exists in the
1272
- * DbPrimaryClient store.
1675
+ * When you call Persistence.runTransaction(), it will create a transaction and
1676
+ * pass it to your callback. You then pass it to any method that operates
1677
+ * on persistence.
1273
1678
  */
1274
- const DbPrimaryClientKey = 'owner';
1275
- /** Name of the IndexedDb object store. */
1276
- const DbMutationQueueStore = 'mutationQueues';
1277
- /** Keys are automatically assigned via the userId property. */
1278
- const DbMutationQueueKeyPath = 'userId';
1279
- /** Name of the IndexedDb object store. */
1280
- const DbMutationBatchStore = 'mutations';
1281
- /** Keys are automatically assigned via the userId, batchId properties. */
1282
- const DbMutationBatchKeyPath = 'batchId';
1283
- /** The index name for lookup of mutations by user. */
1284
- const DbMutationBatchUserMutationsIndex = 'userMutationsIndex';
1285
- /** The user mutations index is keyed by [userId, batchId] pairs. */
1286
- const DbMutationBatchUserMutationsKeyPath = ['userId', 'batchId'];
1287
- /**
1288
- * Creates a [userId] key for use in the DbDocumentMutations index to iterate
1289
- * over all of a user's document mutations.
1290
- */
1291
- function newDbDocumentMutationPrefixForUser(userId) {
1292
- return [userId];
1293
- }
1294
- /**
1295
- * Creates a [userId, encodedPath] key for use in the DbDocumentMutations
1296
- * index to iterate over all at document mutations for a given path or lower.
1297
- */
1298
- function newDbDocumentMutationPrefixForPath(userId, path) {
1299
- return [userId, encodeResourcePath(path)];
1300
- }
1301
- /**
1302
- * Creates a full index key of [userId, encodedPath, batchId] for inserting
1303
- * and deleting into the DbDocumentMutations index.
1304
- */
1305
- function newDbDocumentMutationKey(userId, path, batchId) {
1306
- return [userId, encodeResourcePath(path), batchId];
1307
- }
1308
- /**
1309
- * Because we store all the useful information for this store in the key,
1310
- * there is no useful information to store as the value. The raw (unencoded)
1311
- * path cannot be stored because IndexedDb doesn't store prototype
1312
- * information.
1313
- */
1314
- const DbDocumentMutationPlaceholder = {};
1315
- const DbDocumentMutationStore = 'documentMutations';
1316
- const DbRemoteDocumentStore = 'remoteDocumentsV14';
1317
- /**
1318
- * The primary key of the remote documents store, which allows for efficient
1319
- * access by collection path and read time.
1320
- */
1321
- const DbRemoteDocumentKeyPath = [
1322
- 'prefixPath',
1323
- 'collectionGroup',
1324
- 'readTime',
1325
- 'documentId'
1326
- ];
1327
- /** An index that provides access to documents by key. */
1328
- const DbRemoteDocumentDocumentKeyIndex = 'documentKeyIndex';
1329
- const DbRemoteDocumentDocumentKeyIndexPath = [
1330
- 'prefixPath',
1331
- 'collectionGroup',
1332
- 'documentId'
1333
- ];
1334
- /**
1335
- * An index that provides access to documents by collection group and read
1336
- * time.
1337
- *
1338
- * This index is used by the index backfiller.
1339
- */
1340
- const DbRemoteDocumentCollectionGroupIndex = 'collectionGroupIndex';
1341
- const DbRemoteDocumentCollectionGroupIndexPath = [
1342
- 'collectionGroup',
1343
- 'readTime',
1344
- 'prefixPath',
1345
- 'documentId'
1346
- ];
1347
- const DbRemoteDocumentGlobalStore = 'remoteDocumentGlobal';
1348
- const DbRemoteDocumentGlobalKey = 'remoteDocumentGlobalKey';
1349
- const DbTargetStore = 'targets';
1350
- /** Keys are automatically assigned via the targetId property. */
1351
- const DbTargetKeyPath = 'targetId';
1352
- /** The name of the queryTargets index. */
1353
- const DbTargetQueryTargetsIndexName = 'queryTargetsIndex';
1354
- /**
1355
- * The index of all canonicalIds to the targets that they match. This is not
1356
- * a unique mapping because canonicalId does not promise a unique name for all
1357
- * possible queries, so we append the targetId to make the mapping unique.
1358
- */
1359
- const DbTargetQueryTargetsKeyPath = ['canonicalId', 'targetId'];
1360
- /** Name of the IndexedDb object store. */
1361
- const DbTargetDocumentStore = 'targetDocuments';
1362
- /** Keys are automatically assigned via the targetId, path properties. */
1363
- const DbTargetDocumentKeyPath = ['targetId', 'path'];
1364
- /** The index name for the reverse index. */
1365
- const DbTargetDocumentDocumentTargetsIndex = 'documentTargetsIndex';
1366
- /** We also need to create the reverse index for these properties. */
1367
- const DbTargetDocumentDocumentTargetsKeyPath = ['path', 'targetId'];
1368
- /**
1369
- * The key string used for the single object that exists in the
1370
- * DbTargetGlobal store.
1371
- */
1372
- const DbTargetGlobalKey = 'targetGlobalKey';
1373
- const DbTargetGlobalStore = 'targetGlobal';
1374
- /** Name of the IndexedDb object store. */
1375
- const DbCollectionParentStore = 'collectionParents';
1376
- /** Keys are automatically assigned via the collectionId, parent properties. */
1377
- const DbCollectionParentKeyPath = ['collectionId', 'parent'];
1378
- /** Name of the IndexedDb object store. */
1379
- const DbClientMetadataStore = 'clientMetadata';
1380
- /** Keys are automatically assigned via the clientId properties. */
1381
- const DbClientMetadataKeyPath = 'clientId';
1382
- /** Name of the IndexedDb object store. */
1383
- const DbBundleStore = 'bundles';
1384
- const DbBundleKeyPath = 'bundleId';
1385
- /** Name of the IndexedDb object store. */
1386
- const DbNamedQueryStore = 'namedQueries';
1387
- const DbNamedQueryKeyPath = 'name';
1388
- /** Name of the IndexedDb object store. */
1389
- const DbIndexConfigurationStore = 'indexConfiguration';
1390
- const DbIndexConfigurationKeyPath = 'indexId';
1391
- /**
1392
- * An index that provides access to the index configurations by collection
1393
- * group.
1394
- *
1395
- * PORTING NOTE: iOS and Android maintain this index in-memory, but this is
1396
- * not possible here as the Web client supports concurrent access to
1397
- * persistence via multi-tab.
1398
- */
1399
- const DbIndexConfigurationCollectionGroupIndex = 'collectionGroupIndex';
1400
- const DbIndexConfigurationCollectionGroupIndexPath = 'collectionGroup';
1401
- /** Name of the IndexedDb object store. */
1402
- const DbIndexStateStore = 'indexState';
1403
- const DbIndexStateKeyPath = ['indexId', 'uid'];
1404
- /**
1405
- * An index that provides access to documents in a collection sorted by last
1406
- * update time. Used by the backfiller.
1407
- *
1408
- * PORTING NOTE: iOS and Android maintain this index in-memory, but this is
1409
- * not possible here as the Web client supports concurrent access to
1410
- * persistence via multi-tab.
1411
- */
1412
- const DbIndexStateSequenceNumberIndex = 'sequenceNumberIndex';
1413
- const DbIndexStateSequenceNumberIndexPath = ['uid', 'sequenceNumber'];
1414
- /** Name of the IndexedDb object store. */
1415
- const DbIndexEntryStore = 'indexEntries';
1416
- const DbIndexEntryKeyPath = [
1417
- 'indexId',
1418
- 'uid',
1419
- 'arrayValue',
1420
- 'directionalValue',
1421
- 'orderedDocumentKey',
1422
- 'documentKey'
1423
- ];
1424
- const DbIndexEntryDocumentKeyIndex = 'documentKeyIndex';
1425
- const DbIndexEntryDocumentKeyIndexPath = [
1426
- 'indexId',
1427
- 'uid',
1428
- 'orderedDocumentKey'
1429
- ];
1430
- /** Name of the IndexedDb object store. */
1431
- const DbDocumentOverlayStore = 'documentOverlays';
1432
- const DbDocumentOverlayKeyPath = [
1433
- 'userId',
1434
- 'collectionPath',
1435
- 'documentId'
1436
- ];
1437
- const DbDocumentOverlayCollectionPathOverlayIndex = 'collectionPathOverlayIndex';
1438
- const DbDocumentOverlayCollectionPathOverlayIndexPath = [
1439
- 'userId',
1440
- 'collectionPath',
1441
- 'largestBatchId'
1442
- ];
1443
- const DbDocumentOverlayCollectionGroupOverlayIndex = 'collectionGroupOverlayIndex';
1444
- const DbDocumentOverlayCollectionGroupOverlayIndexPath = [
1445
- 'userId',
1446
- 'collectionGroup',
1447
- 'largestBatchId'
1448
- ];
1449
- // Visible for testing
1450
- const V1_STORES = [
1451
- DbMutationQueueStore,
1452
- DbMutationBatchStore,
1453
- DbDocumentMutationStore,
1454
- DbRemoteDocumentStore$1,
1455
- DbTargetStore,
1456
- DbPrimaryClientStore,
1457
- DbTargetGlobalStore,
1458
- DbTargetDocumentStore
1459
- ];
1460
- // Visible for testing
1461
- const V3_STORES = V1_STORES;
1462
- // Note: DbRemoteDocumentChanges is no longer used and dropped with v9.
1463
- const V4_STORES = [...V3_STORES, DbClientMetadataStore];
1464
- const V6_STORES = [...V4_STORES, DbRemoteDocumentGlobalStore];
1465
- const V8_STORES = [...V6_STORES, DbCollectionParentStore];
1466
- const V11_STORES = [...V8_STORES, DbBundleStore, DbNamedQueryStore];
1467
- const V12_STORES = [...V11_STORES, DbDocumentOverlayStore];
1468
- const V13_STORES = [
1469
- DbMutationQueueStore,
1470
- DbMutationBatchStore,
1471
- DbDocumentMutationStore,
1472
- DbRemoteDocumentStore,
1473
- DbTargetStore,
1474
- DbPrimaryClientStore,
1475
- DbTargetGlobalStore,
1476
- DbTargetDocumentStore,
1477
- DbClientMetadataStore,
1478
- DbRemoteDocumentGlobalStore,
1479
- DbCollectionParentStore,
1480
- DbBundleStore,
1481
- DbNamedQueryStore,
1482
- DbDocumentOverlayStore
1483
- ];
1484
- const V14_STORES = V13_STORES;
1485
- const V15_STORES = [
1486
- ...V14_STORES,
1487
- DbIndexConfigurationStore,
1488
- DbIndexStateStore,
1489
- DbIndexEntryStore
1490
- ];
1491
- /** Returns the object stores for the provided schema. */
1492
- function getObjectStores(schemaVersion) {
1493
- if (schemaVersion === 15) {
1494
- return V15_STORES;
1495
- }
1496
- else if (schemaVersion === 14) {
1497
- return V14_STORES;
1498
- }
1499
- else if (schemaVersion === 13) {
1500
- return V13_STORES;
1501
- }
1502
- else if (schemaVersion === 12) {
1503
- return V12_STORES;
1679
+ class PersistenceTransaction {
1680
+ constructor() {
1681
+ this.onCommittedListeners = [];
1504
1682
  }
1505
- else if (schemaVersion === 11) {
1506
- return V11_STORES;
1683
+ addOnCommittedListener(listener) {
1684
+ this.onCommittedListeners.push(listener);
1507
1685
  }
1508
- else {
1509
- fail();
1686
+ raiseOnCommittedEvent() {
1687
+ this.onCommittedListeners.forEach(listener => listener());
1510
1688
  }
1511
1689
  }
1512
1690
 
1513
1691
  /**
1514
1692
  * @license
1515
- * Copyright 2020 Google LLC
1693
+ * Copyright 2017 Google LLC
1516
1694
  *
1517
1695
  * Licensed under the Apache License, Version 2.0 (the "License");
1518
1696
  * you may not use this file except in compliance with the License.
@@ -1526,25 +1704,23 @@ function getObjectStores(schemaVersion) {
1526
1704
  * See the License for the specific language governing permissions and
1527
1705
  * limitations under the License.
1528
1706
  */
1529
- const PRIMARY_LEASE_LOST_ERROR_MSG = 'The current tab is not in the required state to perform this operation. ' +
1530
- 'It might be necessary to refresh the browser tab.';
1531
1707
  /**
1532
- * A base class representing a persistence transaction, encapsulating both the
1533
- * transaction's sequence numbers as well as a list of onCommitted listeners.
1708
+ * Verifies the error thrown by a LocalStore operation. If a LocalStore
1709
+ * operation fails because the primary lease has been taken by another client,
1710
+ * we ignore the error (the persistence layer will immediately call
1711
+ * `applyPrimaryLease` to propagate the primary state change). All other errors
1712
+ * are re-thrown.
1534
1713
  *
1535
- * When you call Persistence.runTransaction(), it will create a transaction and
1536
- * pass it to your callback. You then pass it to any method that operates
1537
- * on persistence.
1714
+ * @param err - An error returned by a LocalStore operation.
1715
+ * @returns A Promise that resolves after we recovered, or the original error.
1538
1716
  */
1539
- class PersistenceTransaction {
1540
- constructor() {
1541
- this.onCommittedListeners = [];
1542
- }
1543
- addOnCommittedListener(listener) {
1544
- this.onCommittedListeners.push(listener);
1717
+ async function ignoreIfPrimaryLeaseLoss(err) {
1718
+ if (err.code === Code.FAILED_PRECONDITION &&
1719
+ err.message === PRIMARY_LEASE_LOST_ERROR_MSG) {
1720
+ logDebug('LocalStore', 'Unexpectedly lost primary lease');
1545
1721
  }
1546
- raiseOnCommittedEvent() {
1547
- this.onCommittedListeners.forEach(listener => listener());
1722
+ else {
1723
+ throw err;
1548
1724
  }
1549
1725
  }
1550
1726
 
@@ -1729,6 +1905,47 @@ class PersistencePromise {
1729
1905
  });
1730
1906
  return this.waitFor(promises);
1731
1907
  }
1908
+ /**
1909
+ * Concurrently map all array elements through asynchronous function.
1910
+ */
1911
+ static mapArray(array, f) {
1912
+ return new PersistencePromise((resolve, reject) => {
1913
+ const expectedCount = array.length;
1914
+ const results = new Array(expectedCount);
1915
+ let resolvedCount = 0;
1916
+ for (let i = 0; i < expectedCount; i++) {
1917
+ const current = i;
1918
+ f(array[current]).next(result => {
1919
+ results[current] = result;
1920
+ ++resolvedCount;
1921
+ if (resolvedCount === expectedCount) {
1922
+ resolve(results);
1923
+ }
1924
+ }, err => reject(err));
1925
+ }
1926
+ });
1927
+ }
1928
+ /**
1929
+ * An alternative to recursive PersistencePromise calls, that avoids
1930
+ * potential memory problems from unbounded chains of promises.
1931
+ *
1932
+ * The `action` will be called repeatedly while `condition` is true.
1933
+ */
1934
+ static doWhile(condition, action) {
1935
+ return new PersistencePromise((resolve, reject) => {
1936
+ const process = () => {
1937
+ if (condition() === true) {
1938
+ action().next(() => {
1939
+ process();
1940
+ }, reject);
1941
+ }
1942
+ else {
1943
+ resolve();
1944
+ }
1945
+ };
1946
+ process();
1947
+ });
1948
+ }
1732
1949
  }
1733
1950
 
1734
1951
  /**
@@ -1749,7 +1966,7 @@ class PersistencePromise {
1749
1966
  */
1750
1967
  // References to `window` are guarded by SimpleDb.isAvailable()
1751
1968
  /* eslint-disable no-restricted-globals */
1752
- const LOG_TAG$h = 'SimpleDb';
1969
+ const LOG_TAG$i = 'SimpleDb';
1753
1970
  /**
1754
1971
  * The maximum number of retry attempts for an IndexedDb transaction that fails
1755
1972
  * with a DOMException.
@@ -1800,7 +2017,7 @@ class SimpleDbTransaction {
1800
2017
  this.completionDeferred.reject(error);
1801
2018
  }
1802
2019
  if (!this.aborted) {
1803
- logDebug(LOG_TAG$h, 'Aborting transaction:', error ? error.message : 'Client-initiated abort');
2020
+ logDebug(LOG_TAG$i, 'Aborting transaction:', error ? error.message : 'Client-initiated abort');
1804
2021
  this.aborted = true;
1805
2022
  this.transaction.abort();
1806
2023
  }
@@ -1863,7 +2080,7 @@ class SimpleDb {
1863
2080
  }
1864
2081
  /** Deletes the specified database. */
1865
2082
  static delete(name) {
1866
- logDebug(LOG_TAG$h, 'Removing database:', name);
2083
+ logDebug(LOG_TAG$i, 'Removing database:', name);
1867
2084
  return wrapRequest(window.indexedDB.deleteDatabase(name)).toPromise();
1868
2085
  }
1869
2086
  /** Returns true if IndexedDB is available in the current environment. */
@@ -1942,7 +2159,7 @@ class SimpleDb {
1942
2159
  */
1943
2160
  async ensureDb(action) {
1944
2161
  if (!this.db) {
1945
- logDebug(LOG_TAG$h, 'Opening database:', this.name);
2162
+ logDebug(LOG_TAG$i, 'Opening database:', this.name);
1946
2163
  this.db = await new Promise((resolve, reject) => {
1947
2164
  // TODO(mikelehen): Investigate browser compatibility.
1948
2165
  // https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB
@@ -1978,12 +2195,12 @@ class SimpleDb {
1978
2195
  }
1979
2196
  };
1980
2197
  request.onupgradeneeded = (event) => {
1981
- logDebug(LOG_TAG$h, 'Database "' + this.name + '" requires upgrade from version:', event.oldVersion);
2198
+ logDebug(LOG_TAG$i, 'Database "' + this.name + '" requires upgrade from version:', event.oldVersion);
1982
2199
  const db = event.target.result;
1983
2200
  this.schemaConverter
1984
2201
  .createOrUpgrade(db, request.transaction, event.oldVersion, this.version)
1985
2202
  .next(() => {
1986
- logDebug(LOG_TAG$h, 'Database upgrade to version ' + this.version + ' complete');
2203
+ logDebug(LOG_TAG$i, 'Database upgrade to version ' + this.version + ' complete');
1987
2204
  });
1988
2205
  };
1989
2206
  });
@@ -2041,7 +2258,7 @@ class SimpleDb {
2041
2258
  // exception is wrapped in a generic error by our async/await handling.
2042
2259
  const retryable = error.name !== 'FirebaseError' &&
2043
2260
  attemptNumber < TRANSACTION_RETRY_COUNT;
2044
- logDebug(LOG_TAG$h, 'Transaction failed with error:', error.message, 'Retrying:', retryable);
2261
+ logDebug(LOG_TAG$i, 'Transaction failed with error:', error.message, 'Retrying:', retryable);
2045
2262
  this.close();
2046
2263
  if (!retryable) {
2047
2264
  return Promise.reject(error);
@@ -2128,11 +2345,11 @@ class SimpleDbStore {
2128
2345
  put(keyOrValue, value) {
2129
2346
  let request;
2130
2347
  if (value !== undefined) {
2131
- logDebug(LOG_TAG$h, 'PUT', this.store.name, keyOrValue, value);
2348
+ logDebug(LOG_TAG$i, 'PUT', this.store.name, keyOrValue, value);
2132
2349
  request = this.store.put(value, keyOrValue);
2133
2350
  }
2134
2351
  else {
2135
- logDebug(LOG_TAG$h, 'PUT', this.store.name, '<auto-key>', keyOrValue);
2352
+ logDebug(LOG_TAG$i, 'PUT', this.store.name, '<auto-key>', keyOrValue);
2136
2353
  request = this.store.put(keyOrValue);
2137
2354
  }
2138
2355
  return wrapRequest(request);
@@ -2145,7 +2362,7 @@ class SimpleDbStore {
2145
2362
  * @returns The key of the value to add.
2146
2363
  */
2147
2364
  add(value) {
2148
- logDebug(LOG_TAG$h, 'ADD', this.store.name, value, value);
2365
+ logDebug(LOG_TAG$i, 'ADD', this.store.name, value, value);
2149
2366
  const request = this.store.add(value);
2150
2367
  return wrapRequest(request);
2151
2368
  }
@@ -2165,12 +2382,12 @@ class SimpleDbStore {
2165
2382
  if (result === undefined) {
2166
2383
  result = null;
2167
2384
  }
2168
- logDebug(LOG_TAG$h, 'GET', this.store.name, key, result);
2385
+ logDebug(LOG_TAG$i, 'GET', this.store.name, key, result);
2169
2386
  return result;
2170
2387
  });
2171
2388
  }
2172
2389
  delete(key) {
2173
- logDebug(LOG_TAG$h, 'DELETE', this.store.name, key);
2390
+ logDebug(LOG_TAG$i, 'DELETE', this.store.name, key);
2174
2391
  const request = this.store.delete(key);
2175
2392
  return wrapRequest(request);
2176
2393
  }
@@ -2181,7 +2398,7 @@ class SimpleDbStore {
2181
2398
  * Returns the number of rows in the store.
2182
2399
  */
2183
2400
  count() {
2184
- logDebug(LOG_TAG$h, 'COUNT', this.store.name);
2401
+ logDebug(LOG_TAG$i, 'COUNT', this.store.name);
2185
2402
  const request = this.store.count();
2186
2403
  return wrapRequest(request);
2187
2404
  }
@@ -2226,7 +2443,7 @@ class SimpleDbStore {
2226
2443
  });
2227
2444
  }
2228
2445
  deleteAll(indexOrRange, range) {
2229
- logDebug(LOG_TAG$h, 'DELETE ALL', this.store.name);
2446
+ logDebug(LOG_TAG$i, 'DELETE ALL', this.store.name);
2230
2447
  const options = this.options(indexOrRange, range);
2231
2448
  options.keysOnly = false;
2232
2449
  const cursor = this.cursor(options);
@@ -2389,37 +2606,126 @@ function checkForAndReportiOSError(error) {
2389
2606
  return error;
2390
2607
  }
2391
2608
 
2392
- /**
2393
- * @license
2394
- * Copyright 2020 Google LLC
2395
- *
2396
- * Licensed under the Apache License, Version 2.0 (the "License");
2397
- * you may not use this file except in compliance with the License.
2398
- * You may obtain a copy of the License at
2399
- *
2400
- * http://www.apache.org/licenses/LICENSE-2.0
2401
- *
2402
- * Unless required by applicable law or agreed to in writing, software
2403
- * distributed under the License is distributed on an "AS IS" BASIS,
2404
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2405
- * See the License for the specific language governing permissions and
2406
- * limitations under the License.
2407
- */
2408
- class IndexedDbTransaction extends PersistenceTransaction {
2409
- constructor(simpleDbTransaction, currentSequenceNumber) {
2410
- super();
2411
- this.simpleDbTransaction = simpleDbTransaction;
2412
- this.currentSequenceNumber = currentSequenceNumber;
2609
+ const LOG_TAG$h = 'IndexBackiller';
2610
+ /** Minimum amount of time between backfill checks, after the first one. */
2611
+ const REGULAR_BACKFILL_DELAY_MS = 1;
2612
+ /** The maximum number of documents to process each time backfill() is called. */
2613
+ const MAX_DOCUMENTS_TO_PROCESS = 50;
2614
+ /** This class is responsible for the scheduling of Index Backfiller. */
2615
+ class IndexBackfillerScheduler {
2616
+ constructor(asyncQueue, backfiller) {
2617
+ this.asyncQueue = asyncQueue;
2618
+ this.backfiller = backfiller;
2619
+ this.task = null;
2620
+ }
2621
+ start() {
2622
+ }
2623
+ stop() {
2624
+ if (this.task) {
2625
+ this.task.cancel();
2626
+ this.task = null;
2627
+ }
2628
+ }
2629
+ get started() {
2630
+ return this.task !== null;
2631
+ }
2632
+ schedule(delay) {
2633
+ logDebug(LOG_TAG$h, `Scheduled in ${delay}ms`);
2634
+ this.task = this.asyncQueue.enqueueAfterDelay("index_backfill" /* IndexBackfill */, delay, async () => {
2635
+ this.task = null;
2636
+ try {
2637
+ const documentsProcessed = await this.backfiller.backfill();
2638
+ logDebug(LOG_TAG$h, `Documents written: ${documentsProcessed}`);
2639
+ }
2640
+ catch (e) {
2641
+ if (isIndexedDbTransactionError(e)) {
2642
+ logDebug(LOG_TAG$h, 'Ignoring IndexedDB error during index backfill: ', e);
2643
+ }
2644
+ else {
2645
+ await ignoreIfPrimaryLeaseLoss(e);
2646
+ }
2647
+ }
2648
+ await this.schedule(REGULAR_BACKFILL_DELAY_MS);
2649
+ });
2413
2650
  }
2414
2651
  }
2415
- function getStore(txn, store) {
2416
- const indexedDbTransaction = debugCast(txn);
2417
- return SimpleDb.getStore(indexedDbTransaction.simpleDbTransaction, store);
2652
+ /** Implements the steps for backfilling indexes. */
2653
+ class IndexBackfiller {
2654
+ constructor(
2655
+ /**
2656
+ * LocalStore provides access to IndexManager and LocalDocumentView.
2657
+ * These properties will update when the user changes. Consequently,
2658
+ * making a local copy of IndexManager and LocalDocumentView will require
2659
+ * updates over time. The simpler solution is to rely on LocalStore to have
2660
+ * an up-to-date references to IndexManager and LocalDocumentStore.
2661
+ */
2662
+ localStore, persistence) {
2663
+ this.localStore = localStore;
2664
+ this.persistence = persistence;
2665
+ }
2666
+ async backfill(maxDocumentsToProcess = MAX_DOCUMENTS_TO_PROCESS) {
2667
+ return this.persistence.runTransaction('Backfill Indexes', 'readwrite-primary', txn => this.writeIndexEntries(txn, maxDocumentsToProcess));
2668
+ }
2669
+ /** Writes index entries until the cap is reached. Returns the number of documents processed. */
2670
+ writeIndexEntries(transation, maxDocumentsToProcess) {
2671
+ const processedCollectionGroups = new Set();
2672
+ let documentsRemaining = maxDocumentsToProcess;
2673
+ let continueLoop = true;
2674
+ return PersistencePromise.doWhile(() => continueLoop === true && documentsRemaining > 0, () => {
2675
+ return this.localStore.indexManager
2676
+ .getNextCollectionGroupToUpdate(transation)
2677
+ .next((collectionGroup) => {
2678
+ if (collectionGroup === null ||
2679
+ processedCollectionGroups.has(collectionGroup)) {
2680
+ continueLoop = false;
2681
+ }
2682
+ else {
2683
+ logDebug(LOG_TAG$h, `Processing collection: ${collectionGroup}`);
2684
+ return this.writeEntriesForCollectionGroup(transation, collectionGroup, documentsRemaining).next(documentsProcessed => {
2685
+ documentsRemaining -= documentsProcessed;
2686
+ processedCollectionGroups.add(collectionGroup);
2687
+ });
2688
+ }
2689
+ });
2690
+ }).next(() => maxDocumentsToProcess - documentsRemaining);
2691
+ }
2692
+ /**
2693
+ * Writes entries for the provided collection group. Returns the number of documents processed.
2694
+ */
2695
+ writeEntriesForCollectionGroup(transaction, collectionGroup, documentsRemainingUnderCap) {
2696
+ // Use the earliest offset of all field indexes to query the local cache.
2697
+ return this.localStore.indexManager
2698
+ .getMinOffsetFromCollectionGroup(transaction, collectionGroup)
2699
+ .next(existingOffset => this.localStore.localDocuments
2700
+ .getNextDocuments(transaction, collectionGroup, existingOffset, documentsRemainingUnderCap)
2701
+ .next(nextBatch => {
2702
+ const docs = nextBatch.changes;
2703
+ return this.localStore.indexManager
2704
+ .updateIndexEntries(transaction, docs)
2705
+ .next(() => this.getNewOffset(existingOffset, nextBatch))
2706
+ .next(newOffset => {
2707
+ logDebug(LOG_TAG$h, `Updating offset: ${newOffset}`);
2708
+ return this.localStore.indexManager.updateCollectionGroup(transaction, collectionGroup, newOffset);
2709
+ })
2710
+ .next(() => docs.size);
2711
+ }));
2712
+ }
2713
+ /** Returns the next offset based on the provided documents. */
2714
+ getNewOffset(existingOffset, lookupResult) {
2715
+ let maxOffset = existingOffset;
2716
+ lookupResult.changes.forEach((key, document) => {
2717
+ const newOffset = newIndexOffsetFromDocument(document);
2718
+ if (indexOffsetComparator(newOffset, maxOffset) > 0) {
2719
+ maxOffset = newOffset;
2720
+ }
2721
+ });
2722
+ return new IndexOffset(maxOffset.readTime, maxOffset.documentKey, Math.max(lookupResult.batchId, existingOffset.largestBatchId));
2723
+ }
2418
2724
  }
2419
2725
 
2420
2726
  /**
2421
2727
  * @license
2422
- * Copyright 2020 Google LLC
2728
+ * Copyright 2018 Google LLC
2423
2729
  *
2424
2730
  * Licensed under the Apache License, Version 2.0 (the "License");
2425
2731
  * you may not use this file except in compliance with the License.
@@ -2434,13 +2740,32 @@ function getStore(txn, store) {
2434
2740
  * limitations under the License.
2435
2741
  */
2436
2742
  /**
2437
- * Generates `nBytes` of random bytes.
2438
- *
2439
- * If `nBytes < 0` , an error will be thrown.
2743
+ * `ListenSequence` is a monotonic sequence. It is initialized with a minimum value to
2744
+ * exceed. All subsequent calls to next will return increasing values. If provided with a
2745
+ * `SequenceNumberSyncer`, it will additionally bump its next value when told of a new value, as
2746
+ * well as write out sequence numbers that it produces via `next()`.
2440
2747
  */
2441
- function randomBytes(nBytes) {
2442
- return crypto.randomBytes(nBytes);
2443
- }
2748
+ class ListenSequence {
2749
+ constructor(previousValue, sequenceNumberSyncer) {
2750
+ this.previousValue = previousValue;
2751
+ if (sequenceNumberSyncer) {
2752
+ sequenceNumberSyncer.sequenceNumberHandler = sequenceNumber => this.setPreviousValue(sequenceNumber);
2753
+ this.writeNewSequenceNumber = sequenceNumber => sequenceNumberSyncer.writeSequenceNumber(sequenceNumber);
2754
+ }
2755
+ }
2756
+ setPreviousValue(externalPreviousValue) {
2757
+ this.previousValue = Math.max(externalPreviousValue, this.previousValue);
2758
+ return this.previousValue;
2759
+ }
2760
+ next() {
2761
+ const nextValue = ++this.previousValue;
2762
+ if (this.writeNewSequenceNumber) {
2763
+ this.writeNewSequenceNumber(nextValue);
2764
+ }
2765
+ return nextValue;
2766
+ }
2767
+ }
2768
+ ListenSequence.INVALID = -1;
2444
2769
 
2445
2770
  /**
2446
2771
  * @license
@@ -2458,55 +2783,109 @@ function randomBytes(nBytes) {
2458
2783
  * See the License for the specific language governing permissions and
2459
2784
  * limitations under the License.
2460
2785
  */
2461
- class AutoId {
2462
- static newId() {
2463
- // Alphanumeric characters
2464
- const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
2465
- // The largest byte value that is a multiple of `char.length`.
2466
- const maxMultiple = Math.floor(256 / chars.length) * chars.length;
2467
- let autoId = '';
2468
- const targetLength = 20;
2469
- while (autoId.length < targetLength) {
2470
- const bytes = randomBytes(40);
2471
- for (let i = 0; i < bytes.length; ++i) {
2472
- // Only accept values that are [0, maxMultiple), this ensures they can
2473
- // be evenly mapped to indices of `chars` via a modulo operation.
2474
- if (autoId.length < targetLength && bytes[i] < maxMultiple) {
2475
- autoId += chars.charAt(bytes[i] % chars.length);
2476
- }
2477
- }
2786
+ const escapeChar = '\u0001';
2787
+ const encodedSeparatorChar = '\u0001';
2788
+ const encodedNul = '\u0010';
2789
+ const encodedEscape = '\u0011';
2790
+ /**
2791
+ * Encodes a resource path into a IndexedDb-compatible string form.
2792
+ */
2793
+ function encodeResourcePath(path) {
2794
+ let result = '';
2795
+ for (let i = 0; i < path.length; i++) {
2796
+ if (result.length > 0) {
2797
+ result = encodeSeparator(result);
2478
2798
  }
2479
- return autoId;
2799
+ result = encodeSegment(path.get(i), result);
2480
2800
  }
2801
+ return encodeSeparator(result);
2481
2802
  }
2482
- function primitiveComparator(left, right) {
2483
- if (left < right) {
2484
- return -1;
2485
- }
2486
- if (left > right) {
2487
- return 1;
2803
+ /** Encodes a single segment of a resource path into the given result */
2804
+ function encodeSegment(segment, resultBuf) {
2805
+ let result = resultBuf;
2806
+ const length = segment.length;
2807
+ for (let i = 0; i < length; i++) {
2808
+ const c = segment.charAt(i);
2809
+ switch (c) {
2810
+ case '\0':
2811
+ result += escapeChar + encodedNul;
2812
+ break;
2813
+ case escapeChar:
2814
+ result += escapeChar + encodedEscape;
2815
+ break;
2816
+ default:
2817
+ result += c;
2818
+ }
2488
2819
  }
2489
- return 0;
2820
+ return result;
2490
2821
  }
2491
- /** Helper to compare arrays using isEqual(). */
2492
- function arrayEquals(left, right, comparator) {
2493
- if (left.length !== right.length) {
2494
- return false;
2495
- }
2496
- return left.every((value, index) => comparator(value, right[index]));
2822
+ /** Encodes a path separator into the given result */
2823
+ function encodeSeparator(result) {
2824
+ return result + escapeChar + encodedSeparatorChar;
2497
2825
  }
2498
2826
  /**
2499
- * Returns the immediate lexicographically-following string. This is useful to
2500
- * construct an inclusive range for indexeddb iterators.
2827
+ * Decodes the given IndexedDb-compatible string form of a resource path into
2828
+ * a ResourcePath instance. Note that this method is not suitable for use with
2829
+ * decoding resource names from the server; those are One Platform format
2830
+ * strings.
2501
2831
  */
2502
- function immediateSuccessor(s) {
2503
- // Return the input string, with an additional NUL byte appended.
2504
- return s + '\0';
2832
+ function decodeResourcePath(path) {
2833
+ // Event the empty path must encode as a path of at least length 2. A path
2834
+ // with exactly 2 must be the empty path.
2835
+ const length = path.length;
2836
+ hardAssert(length >= 2);
2837
+ if (length === 2) {
2838
+ hardAssert(path.charAt(0) === escapeChar && path.charAt(1) === encodedSeparatorChar);
2839
+ return ResourcePath.emptyPath();
2840
+ }
2841
+ // Escape characters cannot exist past the second-to-last position in the
2842
+ // source value.
2843
+ const lastReasonableEscapeIndex = length - 2;
2844
+ const segments = [];
2845
+ let segmentBuilder = '';
2846
+ for (let start = 0; start < length;) {
2847
+ // The last two characters of a valid encoded path must be a separator, so
2848
+ // there must be an end to this segment.
2849
+ const end = path.indexOf(escapeChar, start);
2850
+ if (end < 0 || end > lastReasonableEscapeIndex) {
2851
+ fail();
2852
+ }
2853
+ const next = path.charAt(end + 1);
2854
+ switch (next) {
2855
+ case encodedSeparatorChar:
2856
+ const currentPiece = path.substring(start, end);
2857
+ let segment;
2858
+ if (segmentBuilder.length === 0) {
2859
+ // Avoid copying for the common case of a segment that excludes \0
2860
+ // and \001
2861
+ segment = currentPiece;
2862
+ }
2863
+ else {
2864
+ segmentBuilder += currentPiece;
2865
+ segment = segmentBuilder;
2866
+ segmentBuilder = '';
2867
+ }
2868
+ segments.push(segment);
2869
+ break;
2870
+ case encodedNul:
2871
+ segmentBuilder += path.substring(start, end);
2872
+ segmentBuilder += '\0';
2873
+ break;
2874
+ case encodedEscape:
2875
+ // The escape character can be used in the output to encode itself.
2876
+ segmentBuilder += path.substring(start, end + 1);
2877
+ break;
2878
+ default:
2879
+ fail();
2880
+ }
2881
+ start = end + 2;
2882
+ }
2883
+ return new ResourcePath(segments);
2505
2884
  }
2506
2885
 
2507
2886
  /**
2508
2887
  * @license
2509
- * Copyright 2017 Google LLC
2888
+ * Copyright 2022 Google LLC
2510
2889
  *
2511
2890
  * Licensed under the Apache License, Version 2.0 (the "License");
2512
2891
  * you may not use this file except in compliance with the License.
@@ -2520,164 +2899,278 @@ function immediateSuccessor(s) {
2520
2899
  * See the License for the specific language governing permissions and
2521
2900
  * limitations under the License.
2522
2901
  */
2523
- // The earliest date supported by Firestore timestamps (0001-01-01T00:00:00Z).
2524
- const MIN_SECONDS = -62135596800;
2525
- // Number of nanoseconds in a millisecond.
2526
- const MS_TO_NANOS = 1e6;
2902
+ const DbRemoteDocumentStore$1 = 'remoteDocuments';
2903
+
2527
2904
  /**
2528
- * A `Timestamp` represents a point in time independent of any time zone or
2529
- * calendar, represented as seconds and fractions of seconds at nanosecond
2530
- * resolution in UTC Epoch time.
2905
+ * @license
2906
+ * Copyright 2022 Google LLC
2531
2907
  *
2532
- * It is encoded using the Proleptic Gregorian Calendar which extends the
2533
- * Gregorian calendar backwards to year one. It is encoded assuming all minutes
2534
- * are 60 seconds long, i.e. leap seconds are "smeared" so that no leap second
2535
- * table is needed for interpretation. Range is from 0001-01-01T00:00:00Z to
2536
- * 9999-12-31T23:59:59.999999999Z.
2908
+ * Licensed under the Apache License, Version 2.0 (the "License");
2909
+ * you may not use this file except in compliance with the License.
2910
+ * You may obtain a copy of the License at
2537
2911
  *
2538
- * For examples and further specifications, refer to the
2539
- * {@link https://github.com/google/protobuf/blob/master/src/google/protobuf/timestamp.proto | Timestamp definition}.
2912
+ * http://www.apache.org/licenses/LICENSE-2.0
2913
+ *
2914
+ * Unless required by applicable law or agreed to in writing, software
2915
+ * distributed under the License is distributed on an "AS IS" BASIS,
2916
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2917
+ * See the License for the specific language governing permissions and
2918
+ * limitations under the License.
2540
2919
  */
2541
- class Timestamp {
2542
- /**
2543
- * Creates a new timestamp.
2544
- *
2545
- * @param seconds - The number of seconds of UTC time since Unix epoch
2546
- * 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
2547
- * 9999-12-31T23:59:59Z inclusive.
2548
- * @param nanoseconds - The non-negative fractions of a second at nanosecond
2549
- * resolution. Negative second values with fractions must still have
2550
- * non-negative nanoseconds values that count forward in time. Must be
2551
- * from 0 to 999,999,999 inclusive.
2552
- */
2553
- constructor(
2554
- /**
2555
- * The number of seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z.
2556
- */
2557
- seconds,
2558
- /**
2559
- * The fractions of a second at nanosecond resolution.*
2560
- */
2561
- nanoseconds) {
2562
- this.seconds = seconds;
2563
- this.nanoseconds = nanoseconds;
2564
- if (nanoseconds < 0) {
2565
- throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp nanoseconds out of range: ' + nanoseconds);
2566
- }
2567
- if (nanoseconds >= 1e9) {
2568
- throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp nanoseconds out of range: ' + nanoseconds);
2569
- }
2570
- if (seconds < MIN_SECONDS) {
2571
- throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp seconds out of range: ' + seconds);
2572
- }
2573
- // This will break in the year 10,000.
2574
- if (seconds >= 253402300800) {
2575
- throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp seconds out of range: ' + seconds);
2576
- }
2577
- }
2578
- /**
2579
- * Creates a new timestamp with the current date, with millisecond precision.
2580
- *
2581
- * @returns a new timestamp representing the current date.
2582
- */
2583
- static now() {
2584
- return Timestamp.fromMillis(Date.now());
2585
- }
2586
- /**
2587
- * Creates a new timestamp from the given date.
2588
- *
2589
- * @param date - The date to initialize the `Timestamp` from.
2590
- * @returns A new `Timestamp` representing the same point in time as the given
2591
- * date.
2592
- */
2593
- static fromDate(date) {
2594
- return Timestamp.fromMillis(date.getTime());
2595
- }
2596
- /**
2597
- * Creates a new timestamp from the given number of milliseconds.
2598
- *
2599
- * @param milliseconds - Number of milliseconds since Unix epoch
2600
- * 1970-01-01T00:00:00Z.
2601
- * @returns A new `Timestamp` representing the same point in time as the given
2602
- * number of milliseconds.
2603
- */
2604
- static fromMillis(milliseconds) {
2605
- const seconds = Math.floor(milliseconds / 1000);
2606
- const nanos = Math.floor((milliseconds - seconds * 1000) * MS_TO_NANOS);
2607
- return new Timestamp(seconds, nanos);
2608
- }
2609
- /**
2610
- * Converts a `Timestamp` to a JavaScript `Date` object. This conversion
2611
- * causes a loss of precision since `Date` objects only support millisecond
2612
- * precision.
2613
- *
2614
- * @returns JavaScript `Date` object representing the same point in time as
2615
- * this `Timestamp`, with millisecond precision.
2616
- */
2617
- toDate() {
2618
- return new Date(this.toMillis());
2619
- }
2620
- /**
2621
- * Converts a `Timestamp` to a numeric timestamp (in milliseconds since
2622
- * epoch). This operation causes a loss of precision.
2623
- *
2624
- * @returns The point in time corresponding to this timestamp, represented as
2625
- * the number of milliseconds since Unix epoch 1970-01-01T00:00:00Z.
2626
- */
2627
- toMillis() {
2628
- return this.seconds * 1000 + this.nanoseconds / MS_TO_NANOS;
2920
+ /**
2921
+ * Name of the IndexedDb object store.
2922
+ *
2923
+ * Note that the name 'owner' is chosen to ensure backwards compatibility with
2924
+ * older clients that only supported single locked access to the persistence
2925
+ * layer.
2926
+ */
2927
+ const DbPrimaryClientStore = 'owner';
2928
+ /**
2929
+ * The key string used for the single object that exists in the
2930
+ * DbPrimaryClient store.
2931
+ */
2932
+ const DbPrimaryClientKey = 'owner';
2933
+ /** Name of the IndexedDb object store. */
2934
+ const DbMutationQueueStore = 'mutationQueues';
2935
+ /** Keys are automatically assigned via the userId property. */
2936
+ const DbMutationQueueKeyPath = 'userId';
2937
+ /** Name of the IndexedDb object store. */
2938
+ const DbMutationBatchStore = 'mutations';
2939
+ /** Keys are automatically assigned via the userId, batchId properties. */
2940
+ const DbMutationBatchKeyPath = 'batchId';
2941
+ /** The index name for lookup of mutations by user. */
2942
+ const DbMutationBatchUserMutationsIndex = 'userMutationsIndex';
2943
+ /** The user mutations index is keyed by [userId, batchId] pairs. */
2944
+ const DbMutationBatchUserMutationsKeyPath = ['userId', 'batchId'];
2945
+ /**
2946
+ * Creates a [userId] key for use in the DbDocumentMutations index to iterate
2947
+ * over all of a user's document mutations.
2948
+ */
2949
+ function newDbDocumentMutationPrefixForUser(userId) {
2950
+ return [userId];
2951
+ }
2952
+ /**
2953
+ * Creates a [userId, encodedPath] key for use in the DbDocumentMutations
2954
+ * index to iterate over all at document mutations for a given path or lower.
2955
+ */
2956
+ function newDbDocumentMutationPrefixForPath(userId, path) {
2957
+ return [userId, encodeResourcePath(path)];
2958
+ }
2959
+ /**
2960
+ * Creates a full index key of [userId, encodedPath, batchId] for inserting
2961
+ * and deleting into the DbDocumentMutations index.
2962
+ */
2963
+ function newDbDocumentMutationKey(userId, path, batchId) {
2964
+ return [userId, encodeResourcePath(path), batchId];
2965
+ }
2966
+ /**
2967
+ * Because we store all the useful information for this store in the key,
2968
+ * there is no useful information to store as the value. The raw (unencoded)
2969
+ * path cannot be stored because IndexedDb doesn't store prototype
2970
+ * information.
2971
+ */
2972
+ const DbDocumentMutationPlaceholder = {};
2973
+ const DbDocumentMutationStore = 'documentMutations';
2974
+ const DbRemoteDocumentStore = 'remoteDocumentsV14';
2975
+ /**
2976
+ * The primary key of the remote documents store, which allows for efficient
2977
+ * access by collection path and read time.
2978
+ */
2979
+ const DbRemoteDocumentKeyPath = [
2980
+ 'prefixPath',
2981
+ 'collectionGroup',
2982
+ 'readTime',
2983
+ 'documentId'
2984
+ ];
2985
+ /** An index that provides access to documents by key. */
2986
+ const DbRemoteDocumentDocumentKeyIndex = 'documentKeyIndex';
2987
+ const DbRemoteDocumentDocumentKeyIndexPath = [
2988
+ 'prefixPath',
2989
+ 'collectionGroup',
2990
+ 'documentId'
2991
+ ];
2992
+ /**
2993
+ * An index that provides access to documents by collection group and read
2994
+ * time.
2995
+ *
2996
+ * This index is used by the index backfiller.
2997
+ */
2998
+ const DbRemoteDocumentCollectionGroupIndex = 'collectionGroupIndex';
2999
+ const DbRemoteDocumentCollectionGroupIndexPath = [
3000
+ 'collectionGroup',
3001
+ 'readTime',
3002
+ 'prefixPath',
3003
+ 'documentId'
3004
+ ];
3005
+ const DbRemoteDocumentGlobalStore = 'remoteDocumentGlobal';
3006
+ const DbRemoteDocumentGlobalKey = 'remoteDocumentGlobalKey';
3007
+ const DbTargetStore = 'targets';
3008
+ /** Keys are automatically assigned via the targetId property. */
3009
+ const DbTargetKeyPath = 'targetId';
3010
+ /** The name of the queryTargets index. */
3011
+ const DbTargetQueryTargetsIndexName = 'queryTargetsIndex';
3012
+ /**
3013
+ * The index of all canonicalIds to the targets that they match. This is not
3014
+ * a unique mapping because canonicalId does not promise a unique name for all
3015
+ * possible queries, so we append the targetId to make the mapping unique.
3016
+ */
3017
+ const DbTargetQueryTargetsKeyPath = ['canonicalId', 'targetId'];
3018
+ /** Name of the IndexedDb object store. */
3019
+ const DbTargetDocumentStore = 'targetDocuments';
3020
+ /** Keys are automatically assigned via the targetId, path properties. */
3021
+ const DbTargetDocumentKeyPath = ['targetId', 'path'];
3022
+ /** The index name for the reverse index. */
3023
+ const DbTargetDocumentDocumentTargetsIndex = 'documentTargetsIndex';
3024
+ /** We also need to create the reverse index for these properties. */
3025
+ const DbTargetDocumentDocumentTargetsKeyPath = ['path', 'targetId'];
3026
+ /**
3027
+ * The key string used for the single object that exists in the
3028
+ * DbTargetGlobal store.
3029
+ */
3030
+ const DbTargetGlobalKey = 'targetGlobalKey';
3031
+ const DbTargetGlobalStore = 'targetGlobal';
3032
+ /** Name of the IndexedDb object store. */
3033
+ const DbCollectionParentStore = 'collectionParents';
3034
+ /** Keys are automatically assigned via the collectionId, parent properties. */
3035
+ const DbCollectionParentKeyPath = ['collectionId', 'parent'];
3036
+ /** Name of the IndexedDb object store. */
3037
+ const DbClientMetadataStore = 'clientMetadata';
3038
+ /** Keys are automatically assigned via the clientId properties. */
3039
+ const DbClientMetadataKeyPath = 'clientId';
3040
+ /** Name of the IndexedDb object store. */
3041
+ const DbBundleStore = 'bundles';
3042
+ const DbBundleKeyPath = 'bundleId';
3043
+ /** Name of the IndexedDb object store. */
3044
+ const DbNamedQueryStore = 'namedQueries';
3045
+ const DbNamedQueryKeyPath = 'name';
3046
+ /** Name of the IndexedDb object store. */
3047
+ const DbIndexConfigurationStore = 'indexConfiguration';
3048
+ const DbIndexConfigurationKeyPath = 'indexId';
3049
+ /**
3050
+ * An index that provides access to the index configurations by collection
3051
+ * group.
3052
+ *
3053
+ * PORTING NOTE: iOS and Android maintain this index in-memory, but this is
3054
+ * not possible here as the Web client supports concurrent access to
3055
+ * persistence via multi-tab.
3056
+ */
3057
+ const DbIndexConfigurationCollectionGroupIndex = 'collectionGroupIndex';
3058
+ const DbIndexConfigurationCollectionGroupIndexPath = 'collectionGroup';
3059
+ /** Name of the IndexedDb object store. */
3060
+ const DbIndexStateStore = 'indexState';
3061
+ const DbIndexStateKeyPath = ['indexId', 'uid'];
3062
+ /**
3063
+ * An index that provides access to documents in a collection sorted by last
3064
+ * update time. Used by the backfiller.
3065
+ *
3066
+ * PORTING NOTE: iOS and Android maintain this index in-memory, but this is
3067
+ * not possible here as the Web client supports concurrent access to
3068
+ * persistence via multi-tab.
3069
+ */
3070
+ const DbIndexStateSequenceNumberIndex = 'sequenceNumberIndex';
3071
+ const DbIndexStateSequenceNumberIndexPath = ['uid', 'sequenceNumber'];
3072
+ /** Name of the IndexedDb object store. */
3073
+ const DbIndexEntryStore = 'indexEntries';
3074
+ const DbIndexEntryKeyPath = [
3075
+ 'indexId',
3076
+ 'uid',
3077
+ 'arrayValue',
3078
+ 'directionalValue',
3079
+ 'orderedDocumentKey',
3080
+ 'documentKey'
3081
+ ];
3082
+ const DbIndexEntryDocumentKeyIndex = 'documentKeyIndex';
3083
+ const DbIndexEntryDocumentKeyIndexPath = [
3084
+ 'indexId',
3085
+ 'uid',
3086
+ 'orderedDocumentKey'
3087
+ ];
3088
+ /** Name of the IndexedDb object store. */
3089
+ const DbDocumentOverlayStore = 'documentOverlays';
3090
+ const DbDocumentOverlayKeyPath = [
3091
+ 'userId',
3092
+ 'collectionPath',
3093
+ 'documentId'
3094
+ ];
3095
+ const DbDocumentOverlayCollectionPathOverlayIndex = 'collectionPathOverlayIndex';
3096
+ const DbDocumentOverlayCollectionPathOverlayIndexPath = [
3097
+ 'userId',
3098
+ 'collectionPath',
3099
+ 'largestBatchId'
3100
+ ];
3101
+ const DbDocumentOverlayCollectionGroupOverlayIndex = 'collectionGroupOverlayIndex';
3102
+ const DbDocumentOverlayCollectionGroupOverlayIndexPath = [
3103
+ 'userId',
3104
+ 'collectionGroup',
3105
+ 'largestBatchId'
3106
+ ];
3107
+ // Visible for testing
3108
+ const V1_STORES = [
3109
+ DbMutationQueueStore,
3110
+ DbMutationBatchStore,
3111
+ DbDocumentMutationStore,
3112
+ DbRemoteDocumentStore$1,
3113
+ DbTargetStore,
3114
+ DbPrimaryClientStore,
3115
+ DbTargetGlobalStore,
3116
+ DbTargetDocumentStore
3117
+ ];
3118
+ // Visible for testing
3119
+ const V3_STORES = V1_STORES;
3120
+ // Note: DbRemoteDocumentChanges is no longer used and dropped with v9.
3121
+ const V4_STORES = [...V3_STORES, DbClientMetadataStore];
3122
+ const V6_STORES = [...V4_STORES, DbRemoteDocumentGlobalStore];
3123
+ const V8_STORES = [...V6_STORES, DbCollectionParentStore];
3124
+ const V11_STORES = [...V8_STORES, DbBundleStore, DbNamedQueryStore];
3125
+ const V12_STORES = [...V11_STORES, DbDocumentOverlayStore];
3126
+ const V13_STORES = [
3127
+ DbMutationQueueStore,
3128
+ DbMutationBatchStore,
3129
+ DbDocumentMutationStore,
3130
+ DbRemoteDocumentStore,
3131
+ DbTargetStore,
3132
+ DbPrimaryClientStore,
3133
+ DbTargetGlobalStore,
3134
+ DbTargetDocumentStore,
3135
+ DbClientMetadataStore,
3136
+ DbRemoteDocumentGlobalStore,
3137
+ DbCollectionParentStore,
3138
+ DbBundleStore,
3139
+ DbNamedQueryStore,
3140
+ DbDocumentOverlayStore
3141
+ ];
3142
+ const V14_STORES = V13_STORES;
3143
+ const V15_STORES = [
3144
+ ...V14_STORES,
3145
+ DbIndexConfigurationStore,
3146
+ DbIndexStateStore,
3147
+ DbIndexEntryStore
3148
+ ];
3149
+ /** Returns the object stores for the provided schema. */
3150
+ function getObjectStores(schemaVersion) {
3151
+ if (schemaVersion === 15) {
3152
+ return V15_STORES;
2629
3153
  }
2630
- _compareTo(other) {
2631
- if (this.seconds === other.seconds) {
2632
- return primitiveComparator(this.nanoseconds, other.nanoseconds);
2633
- }
2634
- return primitiveComparator(this.seconds, other.seconds);
3154
+ else if (schemaVersion === 14) {
3155
+ return V14_STORES;
2635
3156
  }
2636
- /**
2637
- * Returns true if this `Timestamp` is equal to the provided one.
2638
- *
2639
- * @param other - The `Timestamp` to compare against.
2640
- * @returns true if this `Timestamp` is equal to the provided one.
2641
- */
2642
- isEqual(other) {
2643
- return (other.seconds === this.seconds && other.nanoseconds === this.nanoseconds);
3157
+ else if (schemaVersion === 13) {
3158
+ return V13_STORES;
2644
3159
  }
2645
- /** Returns a textual representation of this `Timestamp`. */
2646
- toString() {
2647
- return ('Timestamp(seconds=' +
2648
- this.seconds +
2649
- ', nanoseconds=' +
2650
- this.nanoseconds +
2651
- ')');
3160
+ else if (schemaVersion === 12) {
3161
+ return V12_STORES;
2652
3162
  }
2653
- /** Returns a JSON-serializable representation of this `Timestamp`. */
2654
- toJSON() {
2655
- return { seconds: this.seconds, nanoseconds: this.nanoseconds };
3163
+ else if (schemaVersion === 11) {
3164
+ return V11_STORES;
2656
3165
  }
2657
- /**
2658
- * Converts this object to a primitive string, which allows `Timestamp` objects
2659
- * to be compared using the `>`, `<=`, `>=` and `>` operators.
2660
- */
2661
- valueOf() {
2662
- // This method returns a string of the form <seconds>.<nanoseconds> where
2663
- // <seconds> is translated to have a non-negative value and both <seconds>
2664
- // and <nanoseconds> are left-padded with zeroes to be a consistent length.
2665
- // Strings with this format then have a lexiographical ordering that matches
2666
- // the expected ordering. The <seconds> translation is done to avoid having
2667
- // a leading negative sign (i.e. a leading '-' character) in its string
2668
- // representation, which would affect its lexiographical ordering.
2669
- const adjustedSeconds = this.seconds - MIN_SECONDS;
2670
- // Note: Up to 12 decimal digits are required to represent all valid
2671
- // 'seconds' values.
2672
- const formattedSeconds = String(adjustedSeconds).padStart(12, '0');
2673
- const formattedNanoseconds = String(this.nanoseconds).padStart(9, '0');
2674
- return formattedSeconds + '.' + formattedNanoseconds;
3166
+ else {
3167
+ fail();
2675
3168
  }
2676
3169
  }
2677
3170
 
2678
3171
  /**
2679
3172
  * @license
2680
- * Copyright 2017 Google LLC
3173
+ * Copyright 2020 Google LLC
2681
3174
  *
2682
3175
  * Licensed under the Apache License, Version 2.0 (the "License");
2683
3176
  * you may not use this file except in compliance with the License.
@@ -2691,40 +3184,16 @@ class Timestamp {
2691
3184
  * See the License for the specific language governing permissions and
2692
3185
  * limitations under the License.
2693
3186
  */
2694
- /**
2695
- * A version of a document in Firestore. This corresponds to the version
2696
- * timestamp, such as update_time or read_time.
2697
- */
2698
- class SnapshotVersion {
2699
- constructor(timestamp) {
2700
- this.timestamp = timestamp;
2701
- }
2702
- static fromTimestamp(value) {
2703
- return new SnapshotVersion(value);
2704
- }
2705
- static min() {
2706
- return new SnapshotVersion(new Timestamp(0, 0));
2707
- }
2708
- static max() {
2709
- return new SnapshotVersion(new Timestamp(253402300799, 1e9 - 1));
2710
- }
2711
- compareTo(other) {
2712
- return this.timestamp._compareTo(other.timestamp);
2713
- }
2714
- isEqual(other) {
2715
- return this.timestamp.isEqual(other.timestamp);
2716
- }
2717
- /** Returns a number representation of the version for use in spec tests. */
2718
- toMicroseconds() {
2719
- // Convert to microseconds.
2720
- return this.timestamp.seconds * 1e6 + this.timestamp.nanoseconds / 1000;
2721
- }
2722
- toString() {
2723
- return 'SnapshotVersion(' + this.timestamp.toString() + ')';
2724
- }
2725
- toTimestamp() {
2726
- return this.timestamp;
3187
+ class IndexedDbTransaction extends PersistenceTransaction {
3188
+ constructor(simpleDbTransaction, currentSequenceNumber) {
3189
+ super();
3190
+ this.simpleDbTransaction = simpleDbTransaction;
3191
+ this.currentSequenceNumber = currentSequenceNumber;
2727
3192
  }
3193
+ }
3194
+ function getStore(txn, store) {
3195
+ const indexedDbTransaction = debugCast(txn);
3196
+ return SimpleDb.getStore(indexedDbTransaction.simpleDbTransaction, store);
2728
3197
  }
2729
3198
 
2730
3199
  /**
@@ -3893,104 +4362,33 @@ class DatabaseId {
3893
4362
  * Unless required by applicable law or agreed to in writing, software
3894
4363
  * distributed under the License is distributed on an "AS IS" BASIS,
3895
4364
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3896
- * See the License for the specific language governing permissions and
3897
- * limitations under the License.
3898
- */
3899
- /** Sentinel value that sorts before any Mutation Batch ID. */
3900
- const BATCHID_UNKNOWN = -1;
3901
- /**
3902
- * Returns whether a variable is either undefined or null.
3903
- */
3904
- function isNullOrUndefined(value) {
3905
- return value === null || value === undefined;
3906
- }
3907
- /** Returns whether the value represents -0. */
3908
- function isNegativeZero(value) {
3909
- // Detect if the value is -0.0. Based on polyfill from
3910
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
3911
- return value === 0 && 1 / value === 1 / -0;
3912
- }
3913
- /**
3914
- * Returns whether a value is an integer and in the safe integer range
3915
- * @param value - The value to test for being an integer and in the safe range
3916
- */
3917
- function isSafeInteger(value) {
3918
- return (typeof value === 'number' &&
3919
- Number.isInteger(value) &&
3920
- !isNegativeZero(value) &&
3921
- value <= Number.MAX_SAFE_INTEGER &&
3922
- value >= Number.MIN_SAFE_INTEGER);
3923
- }
3924
-
3925
- /**
3926
- * @license
3927
- * Copyright 2017 Google LLC
3928
- *
3929
- * Licensed under the Apache License, Version 2.0 (the "License");
3930
- * you may not use this file except in compliance with the License.
3931
- * You may obtain a copy of the License at
3932
- *
3933
- * http://www.apache.org/licenses/LICENSE-2.0
3934
- *
3935
- * Unless required by applicable law or agreed to in writing, software
3936
- * distributed under the License is distributed on an "AS IS" BASIS,
3937
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3938
- * See the License for the specific language governing permissions and
3939
- * limitations under the License.
3940
- */
3941
- /**
3942
- * @internal
3943
- */
3944
- class DocumentKey {
3945
- constructor(path) {
3946
- this.path = path;
3947
- }
3948
- static fromPath(path) {
3949
- return new DocumentKey(ResourcePath.fromString(path));
3950
- }
3951
- static fromName(name) {
3952
- return new DocumentKey(ResourcePath.fromString(name).popFirst(5));
3953
- }
3954
- static empty() {
3955
- return new DocumentKey(ResourcePath.emptyPath());
3956
- }
3957
- get collectionGroup() {
3958
- return this.path.popLast().lastSegment();
3959
- }
3960
- /** Returns true if the document is in the specified collectionId. */
3961
- hasCollectionId(collectionId) {
3962
- return (this.path.length >= 2 &&
3963
- this.path.get(this.path.length - 2) === collectionId);
3964
- }
3965
- /** Returns the collection group (i.e. the name of the parent collection) for this key. */
3966
- getCollectionGroup() {
3967
- return this.path.get(this.path.length - 2);
3968
- }
3969
- /** Returns the fully qualified path to the parent collection. */
3970
- getCollectionPath() {
3971
- return this.path.popLast();
3972
- }
3973
- isEqual(other) {
3974
- return (other !== null && ResourcePath.comparator(this.path, other.path) === 0);
3975
- }
3976
- toString() {
3977
- return this.path.toString();
3978
- }
3979
- static comparator(k1, k2) {
3980
- return ResourcePath.comparator(k1.path, k2.path);
3981
- }
3982
- static isDocumentKey(path) {
3983
- return path.length % 2 === 0;
3984
- }
3985
- /**
3986
- * Creates and returns a new document key with the given segments.
3987
- *
3988
- * @param segments - The segments of the path to the document
3989
- * @returns A new instance of DocumentKey
3990
- */
3991
- static fromSegments(segments) {
3992
- return new DocumentKey(new ResourcePath(segments.slice()));
3993
- }
4365
+ * See the License for the specific language governing permissions and
4366
+ * limitations under the License.
4367
+ */
4368
+ /** Sentinel value that sorts before any Mutation Batch ID. */
4369
+ const BATCHID_UNKNOWN = -1;
4370
+ /**
4371
+ * Returns whether a variable is either undefined or null.
4372
+ */
4373
+ function isNullOrUndefined(value) {
4374
+ return value === null || value === undefined;
4375
+ }
4376
+ /** Returns whether the value represents -0. */
4377
+ function isNegativeZero(value) {
4378
+ // Detect if the value is -0.0. Based on polyfill from
4379
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
4380
+ return value === 0 && 1 / value === 1 / -0;
4381
+ }
4382
+ /**
4383
+ * Returns whether a value is an integer and in the safe integer range
4384
+ * @param value - The value to test for being an integer and in the safe range
4385
+ */
4386
+ function isSafeInteger(value) {
4387
+ return (typeof value === 'number' &&
4388
+ Number.isInteger(value) &&
4389
+ !isNegativeZero(value) &&
4390
+ value <= Number.MAX_SAFE_INTEGER &&
4391
+ value >= Number.MIN_SAFE_INTEGER);
3994
4392
  }
3995
4393
 
3996
4394
  /**
@@ -4611,274 +5009,113 @@ class ObjectValue {
4611
5009
  /**
4612
5010
  * Sets the provided fields to the provided values.
4613
5011
  *
4614
- * @param data - A map of fields to values (or null for deletes).
4615
- */
4616
- setAll(data) {
4617
- let parent = FieldPath$1.emptyPath();
4618
- let upserts = {};
4619
- let deletes = [];
4620
- data.forEach((value, path) => {
4621
- if (!parent.isImmediateParentOf(path)) {
4622
- // Insert the accumulated changes at this parent location
4623
- const fieldsMap = this.getFieldsMap(parent);
4624
- this.applyChanges(fieldsMap, upserts, deletes);
4625
- upserts = {};
4626
- deletes = [];
4627
- parent = path.popLast();
4628
- }
4629
- if (value) {
4630
- upserts[path.lastSegment()] = deepClone(value);
4631
- }
4632
- else {
4633
- deletes.push(path.lastSegment());
4634
- }
4635
- });
4636
- const fieldsMap = this.getFieldsMap(parent);
4637
- this.applyChanges(fieldsMap, upserts, deletes);
4638
- }
4639
- /**
4640
- * Removes the field at the specified path. If there is no field at the
4641
- * specified path, nothing is changed.
4642
- *
4643
- * @param path - The field path to remove.
4644
- */
4645
- delete(path) {
4646
- const nestedValue = this.field(path.popLast());
4647
- if (isMapValue(nestedValue) && nestedValue.mapValue.fields) {
4648
- delete nestedValue.mapValue.fields[path.lastSegment()];
4649
- }
4650
- }
4651
- isEqual(other) {
4652
- return valueEquals(this.value, other.value);
4653
- }
4654
- /**
4655
- * Returns the map that contains the leaf element of `path`. If the parent
4656
- * entry does not yet exist, or if it is not a map, a new map will be created.
4657
- */
4658
- getFieldsMap(path) {
4659
- let current = this.value;
4660
- if (!current.mapValue.fields) {
4661
- current.mapValue = { fields: {} };
4662
- }
4663
- for (let i = 0; i < path.length; ++i) {
4664
- let next = current.mapValue.fields[path.get(i)];
4665
- if (!isMapValue(next) || !next.mapValue.fields) {
4666
- next = { mapValue: { fields: {} } };
4667
- current.mapValue.fields[path.get(i)] = next;
4668
- }
4669
- current = next;
4670
- }
4671
- return current.mapValue.fields;
4672
- }
4673
- /**
4674
- * Modifies `fieldsMap` by adding, replacing or deleting the specified
4675
- * entries.
4676
- */
4677
- applyChanges(fieldsMap, inserts, deletes) {
4678
- forEach(inserts, (key, val) => (fieldsMap[key] = val));
4679
- for (const field of deletes) {
4680
- delete fieldsMap[field];
4681
- }
4682
- }
4683
- clone() {
4684
- return new ObjectValue(deepClone(this.value));
4685
- }
4686
- }
4687
- /**
4688
- * Returns a FieldMask built from all fields in a MapValue.
4689
- */
4690
- function extractFieldMask(value) {
4691
- const fields = [];
4692
- forEach(value.fields, (key, value) => {
4693
- const currentPath = new FieldPath$1([key]);
4694
- if (isMapValue(value)) {
4695
- const nestedMask = extractFieldMask(value.mapValue);
4696
- const nestedFields = nestedMask.fields;
4697
- if (nestedFields.length === 0) {
4698
- // Preserve the empty map by adding it to the FieldMask.
4699
- fields.push(currentPath);
4700
- }
4701
- else {
4702
- // For nested and non-empty ObjectValues, add the FieldPath of the
4703
- // leaf nodes.
4704
- for (const nestedPath of nestedFields) {
4705
- fields.push(currentPath.child(nestedPath));
4706
- }
4707
- }
4708
- }
4709
- else {
4710
- // For nested and non-empty ObjectValues, add the FieldPath of the leaf
4711
- // nodes.
4712
- fields.push(currentPath);
4713
- }
4714
- });
4715
- return new FieldMask(fields);
4716
- }
4717
-
4718
- /**
4719
- * @license
4720
- * Copyright 2017 Google LLC
4721
- *
4722
- * Licensed under the Apache License, Version 2.0 (the "License");
4723
- * you may not use this file except in compliance with the License.
4724
- * You may obtain a copy of the License at
4725
- *
4726
- * http://www.apache.org/licenses/LICENSE-2.0
4727
- *
4728
- * Unless required by applicable law or agreed to in writing, software
4729
- * distributed under the License is distributed on an "AS IS" BASIS,
4730
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4731
- * See the License for the specific language governing permissions and
4732
- * limitations under the License.
4733
- */
4734
- /**
4735
- * Represents a document in Firestore with a key, version, data and whether it
4736
- * has local mutations applied to it.
4737
- *
4738
- * Documents can transition between states via `convertToFoundDocument()`,
4739
- * `convertToNoDocument()` and `convertToUnknownDocument()`. If a document does
4740
- * not transition to one of these states even after all mutations have been
4741
- * applied, `isValidDocument()` returns false and the document should be removed
4742
- * from all views.
4743
- */
4744
- class MutableDocument {
4745
- constructor(key, documentType, version, readTime, data, documentState) {
4746
- this.key = key;
4747
- this.documentType = documentType;
4748
- this.version = version;
4749
- this.readTime = readTime;
4750
- this.data = data;
4751
- this.documentState = documentState;
4752
- }
4753
- /**
4754
- * Creates a document with no known version or data, but which can serve as
4755
- * base document for mutations.
4756
- */
4757
- static newInvalidDocument(documentKey) {
4758
- return new MutableDocument(documentKey, 0 /* INVALID */, SnapshotVersion.min(), SnapshotVersion.min(), ObjectValue.empty(), 0 /* SYNCED */);
4759
- }
4760
- /**
4761
- * Creates a new document that is known to exist with the given data at the
4762
- * given version.
4763
- */
4764
- static newFoundDocument(documentKey, version, value) {
4765
- return new MutableDocument(documentKey, 1 /* FOUND_DOCUMENT */, version, SnapshotVersion.min(), value, 0 /* SYNCED */);
4766
- }
4767
- /** Creates a new document that is known to not exist at the given version. */
4768
- static newNoDocument(documentKey, version) {
4769
- return new MutableDocument(documentKey, 2 /* NO_DOCUMENT */, version, SnapshotVersion.min(), ObjectValue.empty(), 0 /* SYNCED */);
4770
- }
4771
- /**
4772
- * Creates a new document that is known to exist at the given version but
4773
- * whose data is not known (e.g. a document that was updated without a known
4774
- * base document).
4775
- */
4776
- static newUnknownDocument(documentKey, version) {
4777
- return new MutableDocument(documentKey, 3 /* UNKNOWN_DOCUMENT */, version, SnapshotVersion.min(), ObjectValue.empty(), 2 /* HAS_COMMITTED_MUTATIONS */);
4778
- }
4779
- /**
4780
- * Changes the document type to indicate that it exists and that its version
4781
- * and data are known.
4782
- */
4783
- convertToFoundDocument(version, value) {
4784
- this.version = version;
4785
- this.documentType = 1 /* FOUND_DOCUMENT */;
4786
- this.data = value;
4787
- this.documentState = 0 /* SYNCED */;
4788
- return this;
4789
- }
4790
- /**
4791
- * Changes the document type to indicate that it doesn't exist at the given
4792
- * version.
4793
- */
4794
- convertToNoDocument(version) {
4795
- this.version = version;
4796
- this.documentType = 2 /* NO_DOCUMENT */;
4797
- this.data = ObjectValue.empty();
4798
- this.documentState = 0 /* SYNCED */;
4799
- return this;
4800
- }
4801
- /**
4802
- * Changes the document type to indicate that it exists at a given version but
4803
- * that its data is not known (e.g. a document that was updated without a known
4804
- * base document).
4805
- */
4806
- convertToUnknownDocument(version) {
4807
- this.version = version;
4808
- this.documentType = 3 /* UNKNOWN_DOCUMENT */;
4809
- this.data = ObjectValue.empty();
4810
- this.documentState = 2 /* HAS_COMMITTED_MUTATIONS */;
4811
- return this;
4812
- }
4813
- setHasCommittedMutations() {
4814
- this.documentState = 2 /* HAS_COMMITTED_MUTATIONS */;
4815
- return this;
4816
- }
4817
- setHasLocalMutations() {
4818
- this.documentState = 1 /* HAS_LOCAL_MUTATIONS */;
4819
- this.version = SnapshotVersion.min();
4820
- return this;
4821
- }
4822
- setReadTime(readTime) {
4823
- this.readTime = readTime;
4824
- return this;
4825
- }
4826
- get hasLocalMutations() {
4827
- return this.documentState === 1 /* HAS_LOCAL_MUTATIONS */;
4828
- }
4829
- get hasCommittedMutations() {
4830
- return this.documentState === 2 /* HAS_COMMITTED_MUTATIONS */;
4831
- }
4832
- get hasPendingWrites() {
4833
- return this.hasLocalMutations || this.hasCommittedMutations;
4834
- }
4835
- isValidDocument() {
4836
- return this.documentType !== 0 /* INVALID */;
4837
- }
4838
- isFoundDocument() {
4839
- return this.documentType === 1 /* FOUND_DOCUMENT */;
4840
- }
4841
- isNoDocument() {
4842
- return this.documentType === 2 /* NO_DOCUMENT */;
5012
+ * @param data - A map of fields to values (or null for deletes).
5013
+ */
5014
+ setAll(data) {
5015
+ let parent = FieldPath$1.emptyPath();
5016
+ let upserts = {};
5017
+ let deletes = [];
5018
+ data.forEach((value, path) => {
5019
+ if (!parent.isImmediateParentOf(path)) {
5020
+ // Insert the accumulated changes at this parent location
5021
+ const fieldsMap = this.getFieldsMap(parent);
5022
+ this.applyChanges(fieldsMap, upserts, deletes);
5023
+ upserts = {};
5024
+ deletes = [];
5025
+ parent = path.popLast();
5026
+ }
5027
+ if (value) {
5028
+ upserts[path.lastSegment()] = deepClone(value);
5029
+ }
5030
+ else {
5031
+ deletes.push(path.lastSegment());
5032
+ }
5033
+ });
5034
+ const fieldsMap = this.getFieldsMap(parent);
5035
+ this.applyChanges(fieldsMap, upserts, deletes);
4843
5036
  }
4844
- isUnknownDocument() {
4845
- return this.documentType === 3 /* UNKNOWN_DOCUMENT */;
5037
+ /**
5038
+ * Removes the field at the specified path. If there is no field at the
5039
+ * specified path, nothing is changed.
5040
+ *
5041
+ * @param path - The field path to remove.
5042
+ */
5043
+ delete(path) {
5044
+ const nestedValue = this.field(path.popLast());
5045
+ if (isMapValue(nestedValue) && nestedValue.mapValue.fields) {
5046
+ delete nestedValue.mapValue.fields[path.lastSegment()];
5047
+ }
4846
5048
  }
4847
5049
  isEqual(other) {
4848
- return (other instanceof MutableDocument &&
4849
- this.key.isEqual(other.key) &&
4850
- this.version.isEqual(other.version) &&
4851
- this.documentType === other.documentType &&
4852
- this.documentState === other.documentState &&
4853
- this.data.isEqual(other.data));
5050
+ return valueEquals(this.value, other.value);
4854
5051
  }
4855
- mutableCopy() {
4856
- return new MutableDocument(this.key, this.documentType, this.version, this.readTime, this.data.clone(), this.documentState);
5052
+ /**
5053
+ * Returns the map that contains the leaf element of `path`. If the parent
5054
+ * entry does not yet exist, or if it is not a map, a new map will be created.
5055
+ */
5056
+ getFieldsMap(path) {
5057
+ let current = this.value;
5058
+ if (!current.mapValue.fields) {
5059
+ current.mapValue = { fields: {} };
5060
+ }
5061
+ for (let i = 0; i < path.length; ++i) {
5062
+ let next = current.mapValue.fields[path.get(i)];
5063
+ if (!isMapValue(next) || !next.mapValue.fields) {
5064
+ next = { mapValue: { fields: {} } };
5065
+ current.mapValue.fields[path.get(i)] = next;
5066
+ }
5067
+ current = next;
5068
+ }
5069
+ return current.mapValue.fields;
4857
5070
  }
4858
- toString() {
4859
- return (`Document(${this.key}, ${this.version}, ${JSON.stringify(this.data.value)}, ` +
4860
- `{documentType: ${this.documentType}}), ` +
4861
- `{documentState: ${this.documentState}})`);
5071
+ /**
5072
+ * Modifies `fieldsMap` by adding, replacing or deleting the specified
5073
+ * entries.
5074
+ */
5075
+ applyChanges(fieldsMap, inserts, deletes) {
5076
+ forEach(inserts, (key, val) => (fieldsMap[key] = val));
5077
+ for (const field of deletes) {
5078
+ delete fieldsMap[field];
5079
+ }
5080
+ }
5081
+ clone() {
5082
+ return new ObjectValue(deepClone(this.value));
4862
5083
  }
4863
5084
  }
4864
5085
  /**
4865
- * Compares the value for field `field` in the provided documents. Throws if
4866
- * the field does not exist in both documents.
5086
+ * Returns a FieldMask built from all fields in a MapValue.
4867
5087
  */
4868
- function compareDocumentsByField(field, d1, d2) {
4869
- const v1 = d1.data.field(field);
4870
- const v2 = d2.data.field(field);
4871
- if (v1 !== null && v2 !== null) {
4872
- return valueCompare(v1, v2);
4873
- }
4874
- else {
4875
- return fail();
4876
- }
5088
+ function extractFieldMask(value) {
5089
+ const fields = [];
5090
+ forEach(value.fields, (key, value) => {
5091
+ const currentPath = new FieldPath$1([key]);
5092
+ if (isMapValue(value)) {
5093
+ const nestedMask = extractFieldMask(value.mapValue);
5094
+ const nestedFields = nestedMask.fields;
5095
+ if (nestedFields.length === 0) {
5096
+ // Preserve the empty map by adding it to the FieldMask.
5097
+ fields.push(currentPath);
5098
+ }
5099
+ else {
5100
+ // For nested and non-empty ObjectValues, add the FieldPath of the
5101
+ // leaf nodes.
5102
+ for (const nestedPath of nestedFields) {
5103
+ fields.push(currentPath.child(nestedPath));
5104
+ }
5105
+ }
5106
+ }
5107
+ else {
5108
+ // For nested and non-empty ObjectValues, add the FieldPath of the leaf
5109
+ // nodes.
5110
+ fields.push(currentPath);
5111
+ }
5112
+ });
5113
+ return new FieldMask(fields);
4877
5114
  }
4878
5115
 
4879
5116
  /**
4880
5117
  * @license
4881
- * Copyright 2021 Google LLC
5118
+ * Copyright 2017 Google LLC
4882
5119
  *
4883
5120
  * Licensed under the Apache License, Version 2.0 (the "License");
4884
5121
  * you may not use this file except in compliance with the License.
@@ -4893,166 +5130,148 @@ function compareDocumentsByField(field, d1, d2) {
4893
5130
  * limitations under the License.
4894
5131
  */
4895
5132
  /**
4896
- * The initial mutation batch id for each index. Gets updated during index
4897
- * backfill.
4898
- */
4899
- const INITIAL_LARGEST_BATCH_ID = -1;
4900
- /**
4901
- * The initial sequence number for each index. Gets updated during index
4902
- * backfill.
4903
- */
4904
- const INITIAL_SEQUENCE_NUMBER = 0;
4905
- /**
4906
- * An index definition for field indexes in Firestore.
4907
- *
4908
- * Every index is associated with a collection. The definition contains a list
4909
- * of fields and their index kind (which can be `ASCENDING`, `DESCENDING` or
4910
- * `CONTAINS` for ArrayContains/ArrayContainsAny queries).
4911
- *
4912
- * Unlike the backend, the SDK does not differentiate between collection or
4913
- * collection group-scoped indices. Every index can be used for both single
4914
- * collection and collection group queries.
4915
- */
4916
- class FieldIndex {
4917
- constructor(
4918
- /**
4919
- * The index ID. Returns -1 if the index ID is not available (e.g. the index
4920
- * has not yet been persisted).
4921
- */
4922
- indexId,
4923
- /** The collection ID this index applies to. */
4924
- collectionGroup,
4925
- /** The field segments for this index. */
4926
- fields,
4927
- /** Shows how up-to-date the index is for the current user. */
4928
- indexState) {
4929
- this.indexId = indexId;
4930
- this.collectionGroup = collectionGroup;
4931
- this.fields = fields;
4932
- this.indexState = indexState;
4933
- }
4934
- }
4935
- /** An ID for an index that has not yet been added to persistence. */
4936
- FieldIndex.UNKNOWN_ID = -1;
4937
- /** Returns the ArrayContains/ArrayContainsAny segment for this index. */
4938
- function fieldIndexGetArraySegment(fieldIndex) {
4939
- return fieldIndex.fields.find(s => s.kind === 2 /* CONTAINS */);
4940
- }
4941
- /** Returns all directional (ascending/descending) segments for this index. */
4942
- function fieldIndexGetDirectionalSegments(fieldIndex) {
4943
- return fieldIndex.fields.filter(s => s.kind !== 2 /* CONTAINS */);
4944
- }
4945
- /**
4946
- * Returns the order of the document key component for the given index.
5133
+ * Represents a document in Firestore with a key, version, data and whether it
5134
+ * has local mutations applied to it.
4947
5135
  *
4948
- * PORTING NOTE: This is only used in the Web IndexedDb implementation.
5136
+ * Documents can transition between states via `convertToFoundDocument()`,
5137
+ * `convertToNoDocument()` and `convertToUnknownDocument()`. If a document does
5138
+ * not transition to one of these states even after all mutations have been
5139
+ * applied, `isValidDocument()` returns false and the document should be removed
5140
+ * from all views.
4949
5141
  */
4950
- function fieldIndexGetKeyOrder(fieldIndex) {
4951
- const directionalSegments = fieldIndexGetDirectionalSegments(fieldIndex);
4952
- return directionalSegments.length === 0
4953
- ? 0 /* ASCENDING */
4954
- : directionalSegments[directionalSegments.length - 1].kind;
4955
- }
4956
- /** Returns a debug representation of the field index */
4957
- function fieldIndexToString(fieldIndex) {
4958
- return `id=${fieldIndex.indexId}|cg=${fieldIndex.collectionGroup}|f=${fieldIndex.fields.map(f => `${f.fieldPath}:${f.kind}`).join(',')}`;
4959
- }
4960
- /** An index component consisting of field path and index type. */
4961
- class IndexSegment {
4962
- constructor(
4963
- /** The field path of the component. */
4964
- fieldPath,
4965
- /** The fields sorting order. */
4966
- kind) {
4967
- this.fieldPath = fieldPath;
4968
- this.kind = kind;
5142
+ class MutableDocument {
5143
+ constructor(key, documentType, version, readTime, data, documentState) {
5144
+ this.key = key;
5145
+ this.documentType = documentType;
5146
+ this.version = version;
5147
+ this.readTime = readTime;
5148
+ this.data = data;
5149
+ this.documentState = documentState;
4969
5150
  }
4970
- }
4971
- /**
4972
- * Stores the "high water mark" that indicates how updated the Index is for the
4973
- * current user.
4974
- */
4975
- class IndexState {
4976
- constructor(
4977
5151
  /**
4978
- * Indicates when the index was last updated (relative to other indexes).
5152
+ * Creates a document with no known version or data, but which can serve as
5153
+ * base document for mutations.
4979
5154
  */
4980
- sequenceNumber,
4981
- /** The the latest indexed read time, document and batch id. */
4982
- offset) {
4983
- this.sequenceNumber = sequenceNumber;
4984
- this.offset = offset;
4985
- }
4986
- /** The state of an index that has not yet been backfilled. */
4987
- static empty() {
4988
- return new IndexState(INITIAL_SEQUENCE_NUMBER, IndexOffset.min());
5155
+ static newInvalidDocument(documentKey) {
5156
+ return new MutableDocument(documentKey, 0 /* INVALID */, SnapshotVersion.min(), SnapshotVersion.min(), ObjectValue.empty(), 0 /* SYNCED */);
4989
5157
  }
4990
- }
4991
- /**
4992
- * Creates an offset that matches all documents with a read time higher than
4993
- * `readTime`.
4994
- */
4995
- function newIndexOffsetSuccessorFromReadTime(readTime, largestBatchId) {
4996
- // We want to create an offset that matches all documents with a read time
4997
- // greater than the provided read time. To do so, we technically need to
4998
- // create an offset for `(readTime, MAX_DOCUMENT_KEY)`. While we could use
4999
- // Unicode codepoints to generate MAX_DOCUMENT_KEY, it is much easier to use
5000
- // `(readTime + 1, DocumentKey.empty())` since `> DocumentKey.empty()` matches
5001
- // all valid document IDs.
5002
- const successorSeconds = readTime.toTimestamp().seconds;
5003
- const successorNanos = readTime.toTimestamp().nanoseconds + 1;
5004
- const successor = SnapshotVersion.fromTimestamp(successorNanos === 1e9
5005
- ? new Timestamp(successorSeconds + 1, 0)
5006
- : new Timestamp(successorSeconds, successorNanos));
5007
- return new IndexOffset(successor, DocumentKey.empty(), largestBatchId);
5008
- }
5009
- /** Creates a new offset based on the provided document. */
5010
- function newIndexOffsetFromDocument(document) {
5011
- return new IndexOffset(document.readTime, document.key, INITIAL_LARGEST_BATCH_ID);
5012
- }
5013
- /**
5014
- * Stores the latest read time, document and batch ID that were processed for an
5015
- * index.
5016
- */
5017
- class IndexOffset {
5018
- constructor(
5019
5158
  /**
5020
- * The latest read time version that has been indexed by Firestore for this
5021
- * field index.
5159
+ * Creates a new document that is known to exist with the given data at the
5160
+ * given version.
5022
5161
  */
5023
- readTime,
5162
+ static newFoundDocument(documentKey, version, value) {
5163
+ return new MutableDocument(documentKey, 1 /* FOUND_DOCUMENT */, version, SnapshotVersion.min(), value, 0 /* SYNCED */);
5164
+ }
5165
+ /** Creates a new document that is known to not exist at the given version. */
5166
+ static newNoDocument(documentKey, version) {
5167
+ return new MutableDocument(documentKey, 2 /* NO_DOCUMENT */, version, SnapshotVersion.min(), ObjectValue.empty(), 0 /* SYNCED */);
5168
+ }
5024
5169
  /**
5025
- * The key of the last document that was indexed for this query. Use
5026
- * `DocumentKey.empty()` if no document has been indexed.
5170
+ * Creates a new document that is known to exist at the given version but
5171
+ * whose data is not known (e.g. a document that was updated without a known
5172
+ * base document).
5027
5173
  */
5028
- documentKey,
5029
- /*
5030
- * The largest mutation batch id that's been processed by Firestore.
5174
+ static newUnknownDocument(documentKey, version) {
5175
+ return new MutableDocument(documentKey, 3 /* UNKNOWN_DOCUMENT */, version, SnapshotVersion.min(), ObjectValue.empty(), 2 /* HAS_COMMITTED_MUTATIONS */);
5176
+ }
5177
+ /**
5178
+ * Changes the document type to indicate that it exists and that its version
5179
+ * and data are known.
5031
5180
  */
5032
- largestBatchId) {
5181
+ convertToFoundDocument(version, value) {
5182
+ this.version = version;
5183
+ this.documentType = 1 /* FOUND_DOCUMENT */;
5184
+ this.data = value;
5185
+ this.documentState = 0 /* SYNCED */;
5186
+ return this;
5187
+ }
5188
+ /**
5189
+ * Changes the document type to indicate that it doesn't exist at the given
5190
+ * version.
5191
+ */
5192
+ convertToNoDocument(version) {
5193
+ this.version = version;
5194
+ this.documentType = 2 /* NO_DOCUMENT */;
5195
+ this.data = ObjectValue.empty();
5196
+ this.documentState = 0 /* SYNCED */;
5197
+ return this;
5198
+ }
5199
+ /**
5200
+ * Changes the document type to indicate that it exists at a given version but
5201
+ * that its data is not known (e.g. a document that was updated without a known
5202
+ * base document).
5203
+ */
5204
+ convertToUnknownDocument(version) {
5205
+ this.version = version;
5206
+ this.documentType = 3 /* UNKNOWN_DOCUMENT */;
5207
+ this.data = ObjectValue.empty();
5208
+ this.documentState = 2 /* HAS_COMMITTED_MUTATIONS */;
5209
+ return this;
5210
+ }
5211
+ setHasCommittedMutations() {
5212
+ this.documentState = 2 /* HAS_COMMITTED_MUTATIONS */;
5213
+ return this;
5214
+ }
5215
+ setHasLocalMutations() {
5216
+ this.documentState = 1 /* HAS_LOCAL_MUTATIONS */;
5217
+ this.version = SnapshotVersion.min();
5218
+ return this;
5219
+ }
5220
+ setReadTime(readTime) {
5033
5221
  this.readTime = readTime;
5034
- this.documentKey = documentKey;
5035
- this.largestBatchId = largestBatchId;
5222
+ return this;
5036
5223
  }
5037
- /** Returns an offset that sorts before all regular offsets. */
5038
- static min() {
5039
- return new IndexOffset(SnapshotVersion.min(), DocumentKey.empty(), INITIAL_LARGEST_BATCH_ID);
5224
+ get hasLocalMutations() {
5225
+ return this.documentState === 1 /* HAS_LOCAL_MUTATIONS */;
5040
5226
  }
5041
- /** Returns an offset that sorts after all regular offsets. */
5042
- static max() {
5043
- return new IndexOffset(SnapshotVersion.max(), DocumentKey.empty(), INITIAL_LARGEST_BATCH_ID);
5227
+ get hasCommittedMutations() {
5228
+ return this.documentState === 2 /* HAS_COMMITTED_MUTATIONS */;
5229
+ }
5230
+ get hasPendingWrites() {
5231
+ return this.hasLocalMutations || this.hasCommittedMutations;
5232
+ }
5233
+ isValidDocument() {
5234
+ return this.documentType !== 0 /* INVALID */;
5235
+ }
5236
+ isFoundDocument() {
5237
+ return this.documentType === 1 /* FOUND_DOCUMENT */;
5238
+ }
5239
+ isNoDocument() {
5240
+ return this.documentType === 2 /* NO_DOCUMENT */;
5241
+ }
5242
+ isUnknownDocument() {
5243
+ return this.documentType === 3 /* UNKNOWN_DOCUMENT */;
5244
+ }
5245
+ isEqual(other) {
5246
+ return (other instanceof MutableDocument &&
5247
+ this.key.isEqual(other.key) &&
5248
+ this.version.isEqual(other.version) &&
5249
+ this.documentType === other.documentType &&
5250
+ this.documentState === other.documentState &&
5251
+ this.data.isEqual(other.data));
5252
+ }
5253
+ mutableCopy() {
5254
+ return new MutableDocument(this.key, this.documentType, this.version, this.readTime, this.data.clone(), this.documentState);
5255
+ }
5256
+ toString() {
5257
+ return (`Document(${this.key}, ${this.version}, ${JSON.stringify(this.data.value)}, ` +
5258
+ `{documentType: ${this.documentType}}), ` +
5259
+ `{documentState: ${this.documentState}})`);
5044
5260
  }
5045
5261
  }
5046
- function indexOffsetComparator(left, right) {
5047
- let cmp = left.readTime.compareTo(right.readTime);
5048
- if (cmp !== 0) {
5049
- return cmp;
5262
+ /**
5263
+ * Compares the value for field `field` in the provided documents. Throws if
5264
+ * the field does not exist in both documents.
5265
+ */
5266
+ function compareDocumentsByField(field, d1, d2) {
5267
+ const v1 = d1.data.field(field);
5268
+ const v2 = d2.data.field(field);
5269
+ if (v1 !== null && v2 !== null) {
5270
+ return valueCompare(v1, v2);
5050
5271
  }
5051
- cmp = DocumentKey.comparator(left.documentKey, right.documentKey);
5052
- if (cmp !== 0) {
5053
- return cmp;
5272
+ else {
5273
+ return fail();
5054
5274
  }
5055
- return primitiveComparator(left.largestBatchId, right.largestBatchId);
5056
5275
  }
5057
5276
 
5058
5277
  /**
@@ -6165,11 +6384,19 @@ function documentMap(...docs) {
6165
6384
  }
6166
6385
  return map;
6167
6386
  }
6387
+ function newOverlayedDocumentMap() {
6388
+ return newDocumentKeyMap();
6389
+ }
6390
+ function convertOverlayedDocumentMapToDocumentMap(collection) {
6391
+ let documents = EMPTY_DOCUMENT_MAP;
6392
+ collection.forEach((k, v) => (documents = documents.insert(k, v.overlayedDocument)));
6393
+ return documents;
6394
+ }
6168
6395
  function newOverlayMap() {
6169
- return new ObjectMap(key => key.toString(), (l, r) => l.isEqual(r));
6396
+ return newDocumentKeyMap();
6170
6397
  }
6171
6398
  function newMutationMap() {
6172
- return new ObjectMap(key => key.toString(), (l, r) => l.isEqual(r));
6399
+ return newDocumentKeyMap();
6173
6400
  }
6174
6401
  function newDocumentKeyMap() {
6175
6402
  return new ObjectMap(key => key.toString(), (l, r) => l.isEqual(r));
@@ -9270,15 +9497,13 @@ class IndexedDbDocumentOverlayCache {
9270
9497
  }
9271
9498
  getOverlays(transaction, keys) {
9272
9499
  const result = newOverlayMap();
9273
- const promises = [];
9274
- keys.forEach(key => {
9275
- promises.push(this.getOverlay(transaction, key).next(overlay => {
9500
+ return PersistencePromise.forEach(keys, (key) => {
9501
+ return this.getOverlay(transaction, key).next(overlay => {
9276
9502
  if (overlay !== null) {
9277
9503
  result.set(key, overlay);
9278
9504
  }
9279
- }));
9280
- });
9281
- return PersistencePromise.waitFor(promises).next(() => result);
9505
+ });
9506
+ }).next(() => result);
9282
9507
  }
9283
9508
  saveOverlays(transaction, largestBatchId, overlays) {
9284
9509
  const promises = [];
@@ -10166,6 +10391,9 @@ class MemoryIndexManager {
10166
10391
  getMinOffset(transaction, target) {
10167
10392
  return PersistencePromise.resolve(IndexOffset.min());
10168
10393
  }
10394
+ getMinOffsetFromCollectionGroup(transaction, collectionGroup) {
10395
+ return PersistencePromise.resolve(IndexOffset.min());
10396
+ }
10169
10397
  updateCollectionGroup(transaction, collectionGroup, offset) {
10170
10398
  // Field indices are not supported with memory persistence.
10171
10399
  return PersistencePromise.resolve();
@@ -10301,7 +10529,16 @@ class IndexedDbIndexManager {
10301
10529
  const indexes = indexConfigurationStore(transaction);
10302
10530
  const dbIndex = toDbIndexConfiguration(index);
10303
10531
  delete dbIndex.indexId; // `indexId` is auto-populated by IndexedDb
10304
- return indexes.add(dbIndex).next();
10532
+ const result = indexes.add(dbIndex);
10533
+ if (index.indexState) {
10534
+ const states = indexStateStore(transaction);
10535
+ return result.next(indexId => {
10536
+ states.put(toDbIndexState(indexId, this.user, index.indexState.sequenceNumber, index.indexState.offset));
10537
+ });
10538
+ }
10539
+ else {
10540
+ return result.next();
10541
+ }
10305
10542
  }
10306
10543
  deleteFieldIndex(transaction, index) {
10307
10544
  const indexes = indexConfigurationStore(transaction);
@@ -10738,19 +10975,11 @@ class IndexedDbIndexManager {
10738
10975
  }
10739
10976
  return ranges;
10740
10977
  }
10978
+ getMinOffsetFromCollectionGroup(transaction, collectionGroup) {
10979
+ return this.getFieldIndexes(transaction, collectionGroup).next(getMinOffsetFromFieldIndexes);
10980
+ }
10741
10981
  getMinOffset(transaction, target) {
10742
- let offset;
10743
- return PersistencePromise.forEach(this.getSubTargets(target), (target) => {
10744
- return this.getFieldIndex(transaction, target).next(index => {
10745
- if (!index) {
10746
- offset = IndexOffset.min();
10747
- }
10748
- else if (!offset ||
10749
- indexOffsetComparator(index.indexState.offset, offset) < 0) {
10750
- offset = index.indexState.offset;
10751
- }
10752
- });
10753
- }).next(() => offset);
10982
+ return PersistencePromise.mapArray(this.getSubTargets(target), (subTarget) => this.getFieldIndex(transaction, subTarget).next(index => index ? index : fail())).next(getMinOffsetFromFieldIndexes);
10754
10983
  }
10755
10984
  }
10756
10985
  /**
@@ -10777,6 +11006,21 @@ function indexConfigurationStore(txn) {
10777
11006
  */
10778
11007
  function indexStateStore(txn) {
10779
11008
  return getStore(txn, DbIndexStateStore);
11009
+ }
11010
+ function getMinOffsetFromFieldIndexes(fieldIndexes) {
11011
+ hardAssert(fieldIndexes.length !== 0);
11012
+ let minOffset = fieldIndexes[0].indexState.offset;
11013
+ let maxBatchId = minOffset.largestBatchId;
11014
+ for (let i = 1; i < fieldIndexes.length; i++) {
11015
+ const newOffset = fieldIndexes[i].indexState.offset;
11016
+ if (indexOffsetComparator(newOffset, minOffset) < 0) {
11017
+ minOffset = newOffset;
11018
+ }
11019
+ if (maxBatchId < newOffset.largestBatchId) {
11020
+ maxBatchId = newOffset.largestBatchId;
11021
+ }
11022
+ }
11023
+ return new IndexOffset(minOffset.readTime, minOffset.documentKey, maxBatchId);
10780
11024
  }
10781
11025
 
10782
11026
  /**
@@ -11563,42 +11807,6 @@ function documentTargetStore(txn) {
11563
11807
  return getStore(txn, DbTargetDocumentStore);
11564
11808
  }
11565
11809
 
11566
- /**
11567
- * @license
11568
- * Copyright 2017 Google LLC
11569
- *
11570
- * Licensed under the Apache License, Version 2.0 (the "License");
11571
- * you may not use this file except in compliance with the License.
11572
- * You may obtain a copy of the License at
11573
- *
11574
- * http://www.apache.org/licenses/LICENSE-2.0
11575
- *
11576
- * Unless required by applicable law or agreed to in writing, software
11577
- * distributed under the License is distributed on an "AS IS" BASIS,
11578
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11579
- * See the License for the specific language governing permissions and
11580
- * limitations under the License.
11581
- */
11582
- /**
11583
- * Verifies the error thrown by a LocalStore operation. If a LocalStore
11584
- * operation fails because the primary lease has been taken by another client,
11585
- * we ignore the error (the persistence layer will immediately call
11586
- * `applyPrimaryLease` to propagate the primary state change). All other errors
11587
- * are re-thrown.
11588
- *
11589
- * @param err - An error returned by a LocalStore operation.
11590
- * @returns A Promise that resolves after we recovered, or the original error.
11591
- */
11592
- async function ignoreIfPrimaryLeaseLoss(err) {
11593
- if (err.code === Code.FAILED_PRECONDITION &&
11594
- err.message === PRIMARY_LEASE_LOST_ERROR_MSG) {
11595
- logDebug('LocalStore', 'Unexpectedly lost primary lease');
11596
- }
11597
- else {
11598
- throw err;
11599
- }
11600
- }
11601
-
11602
11810
  /**
11603
11811
  * @license
11604
11812
  * Copyright 2018 Google LLC
@@ -11720,16 +11928,16 @@ class RollingSequenceNumberBuffer {
11720
11928
  * whether or not GC is enabled, as well as which delay to use before the next run.
11721
11929
  */
11722
11930
  class LruScheduler {
11723
- constructor(garbageCollector, asyncQueue) {
11931
+ constructor(garbageCollector, asyncQueue, localStore) {
11724
11932
  this.garbageCollector = garbageCollector;
11725
11933
  this.asyncQueue = asyncQueue;
11726
- this.hasRun = false;
11934
+ this.localStore = localStore;
11727
11935
  this.gcTask = null;
11728
11936
  }
11729
- start(localStore) {
11937
+ start() {
11730
11938
  if (this.garbageCollector.params.cacheSizeCollectionThreshold !==
11731
11939
  LRU_COLLECTION_DISABLED) {
11732
- this.scheduleGC(localStore);
11940
+ this.scheduleGC(INITIAL_GC_DELAY_MS);
11733
11941
  }
11734
11942
  }
11735
11943
  stop() {
@@ -11741,14 +11949,12 @@ class LruScheduler {
11741
11949
  get started() {
11742
11950
  return this.gcTask !== null;
11743
11951
  }
11744
- scheduleGC(localStore) {
11745
- const delay = this.hasRun ? REGULAR_GC_DELAY_MS : INITIAL_GC_DELAY_MS;
11746
- logDebug('LruGarbageCollector', `Garbage collection scheduled in ${delay}ms`);
11952
+ scheduleGC(delay) {
11953
+ logDebug(LOG_TAG$e, `Garbage collection scheduled in ${delay}ms`);
11747
11954
  this.gcTask = this.asyncQueue.enqueueAfterDelay("lru_garbage_collection" /* LruGarbageCollection */, delay, async () => {
11748
11955
  this.gcTask = null;
11749
- this.hasRun = true;
11750
11956
  try {
11751
- await localStore.collectGarbage(this.garbageCollector);
11957
+ await this.localStore.collectGarbage(this.garbageCollector);
11752
11958
  }
11753
11959
  catch (e) {
11754
11960
  if (isIndexedDbTransactionError(e)) {
@@ -11758,7 +11964,7 @@ class LruScheduler {
11758
11964
  await ignoreIfPrimaryLeaseLoss(e);
11759
11965
  }
11760
11966
  }
11761
- await this.scheduleGC(localStore);
11967
+ await this.scheduleGC(REGULAR_GC_DELAY_MS);
11762
11968
  });
11763
11969
  }
11764
11970
  }
@@ -12527,31 +12733,6 @@ function dbKeyComparator(l, r) {
12527
12733
  }
12528
12734
  return primitiveComparator(left[left.length - 1], right[right.length - 1]);
12529
12735
  }
12530
- /**
12531
- * Schema Version for the Web client:
12532
- * 1. Initial version including Mutation Queue, Query Cache, and Remote
12533
- * Document Cache
12534
- * 2. Used to ensure a targetGlobal object exists and add targetCount to it. No
12535
- * longer required because migration 3 unconditionally clears it.
12536
- * 3. Dropped and re-created Query Cache to deal with cache corruption related
12537
- * to limbo resolution. Addresses
12538
- * https://github.com/firebase/firebase-ios-sdk/issues/1548
12539
- * 4. Multi-Tab Support.
12540
- * 5. Removal of held write acks.
12541
- * 6. Create document global for tracking document cache size.
12542
- * 7. Ensure every cached document has a sentinel row with a sequence number.
12543
- * 8. Add collection-parent index for Collection Group queries.
12544
- * 9. Change RemoteDocumentChanges store to be keyed by readTime rather than
12545
- * an auto-incrementing ID. This is required for Index-Free queries.
12546
- * 10. Rewrite the canonical IDs to the explicit Protobuf-based format.
12547
- * 11. Add bundles and named_queries for bundle support.
12548
- * 12. Add document overlays.
12549
- * 13. Rewrite the keys of the remote document cache to allow for efficient
12550
- * document lookup via `getAll()`.
12551
- * 14. Add overlays.
12552
- * 15. Add indexing support.
12553
- */
12554
- const SCHEMA_VERSION = 14;
12555
12736
 
12556
12737
  /**
12557
12738
  * @license
@@ -12682,10 +12863,10 @@ class LocalDocumentsView {
12682
12863
  * if the map does not already contain an entry for the given document key.
12683
12864
  */
12684
12865
  populateOverlays(transaction, overlays, docs) {
12685
- let missingOverlays = documentKeySet();
12866
+ const missingOverlays = [];
12686
12867
  docs.forEach(key => {
12687
12868
  if (!overlays.has(key)) {
12688
- missingOverlays = missingOverlays.add(key);
12869
+ missingOverlays.push(key);
12689
12870
  }
12690
12871
  });
12691
12872
  return this.documentOverlayCache
@@ -12711,7 +12892,7 @@ class LocalDocumentsView {
12711
12892
  computeViews(transaction, docs, overlays, existenceStateChanged) {
12712
12893
  let recalculateDocuments = mutableDocumentMap();
12713
12894
  const mutatedFields = newDocumentKeyMap();
12714
- const results = newDocumentKeyMap();
12895
+ const results = newOverlayedDocumentMap();
12715
12896
  docs.forEach((_, doc) => {
12716
12897
  const overlay = overlays.get(doc.key);
12717
12898
  // Recalculate an overlay if the document's existence state changed due to
@@ -12813,6 +12994,55 @@ class LocalDocumentsView {
12813
12994
  return this.getDocumentsMatchingCollectionQuery(transaction, query, offset);
12814
12995
  }
12815
12996
  }
12997
+ /**
12998
+ * Given a collection group, returns the next documents that follow the provided offset, along
12999
+ * with an updated batch ID.
13000
+ *
13001
+ * <p>The documents returned by this method are ordered by remote version from the provided
13002
+ * offset. If there are no more remote documents after the provided offset, documents with
13003
+ * mutations in order of batch id from the offset are returned. Since all documents in a batch are
13004
+ * returned together, the total number of documents returned can exceed {@code count}.
13005
+ *
13006
+ * @param transaction
13007
+ * @param collectionGroup The collection group for the documents.
13008
+ * @param offset The offset to index into.
13009
+ * @param count The number of documents to return
13010
+ * @return A LocalWriteResult with the documents that follow the provided offset and the last processed batch id.
13011
+ */
13012
+ getNextDocuments(transaction, collectionGroup, offset, count) {
13013
+ return this.remoteDocumentCache
13014
+ .getAllFromCollectionGroup(transaction, collectionGroup, offset, count)
13015
+ .next((originalDocs) => {
13016
+ const overlaysPromise = count - originalDocs.size > 0
13017
+ ? this.documentOverlayCache.getOverlaysForCollectionGroup(transaction, collectionGroup, offset.largestBatchId, count - originalDocs.size)
13018
+ : PersistencePromise.resolve(newOverlayMap());
13019
+ // The callsite will use the largest batch ID together with the latest read time to create
13020
+ // a new index offset. Since we only process batch IDs if all remote documents have been read,
13021
+ // no overlay will increase the overall read time. This is why we only need to special case
13022
+ // the batch id.
13023
+ let largestBatchId = INITIAL_LARGEST_BATCH_ID;
13024
+ let modifiedDocs = originalDocs;
13025
+ return overlaysPromise.next(overlays => {
13026
+ return PersistencePromise.forEach(overlays, (key, overlay) => {
13027
+ if (largestBatchId < overlay.largestBatchId) {
13028
+ largestBatchId = overlay.largestBatchId;
13029
+ }
13030
+ if (originalDocs.get(key)) {
13031
+ return PersistencePromise.resolve();
13032
+ }
13033
+ return this.getBaseDocument(transaction, key, overlay).next(doc => {
13034
+ modifiedDocs = modifiedDocs.insert(key, doc);
13035
+ });
13036
+ })
13037
+ .next(() => this.populateOverlays(transaction, overlays, originalDocs))
13038
+ .next(() => this.computeViews(transaction, modifiedDocs, overlays, documentKeySet()))
13039
+ .next(localDocs => ({
13040
+ batchId: largestBatchId,
13041
+ changes: convertOverlayedDocumentMapToDocumentMap(localDocs)
13042
+ }));
13043
+ });
13044
+ });
13045
+ }
12816
13046
  getDocumentsMatchingDocumentQuery(transaction, docPath) {
12817
13047
  // Just do a simple document lookup.
12818
13048
  return this.getDocument(transaction, new DocumentKey(docPath)).next(document => {
@@ -12876,7 +13106,7 @@ class LocalDocumentsView {
12876
13106
  }
12877
13107
  /** Returns a base document that can be used to apply `overlay`. */
12878
13108
  getBaseDocument(transaction, key, overlay) {
12879
- return overlay === null || overlay.mutation instanceof PatchMutation
13109
+ return overlay === null || overlay.mutation.type === 1 /* Patch */
12880
13110
  ? this.remoteDocumentCache.getEntry(transaction, key)
12881
13111
  : PersistencePromise.resolve(MutableDocument.newInvalidDocument(key));
12882
13112
  }
@@ -12951,15 +13181,13 @@ class MemoryDocumentOverlayCache {
12951
13181
  }
12952
13182
  getOverlays(transaction, keys) {
12953
13183
  const result = newOverlayMap();
12954
- const promises = [];
12955
- keys.forEach(key => {
12956
- promises.push(this.getOverlay(transaction, key).next(overlay => {
13184
+ return PersistencePromise.forEach(keys, (key) => {
13185
+ return this.getOverlay(transaction, key).next(overlay => {
12957
13186
  if (overlay !== null) {
12958
13187
  result.set(key, overlay);
12959
13188
  }
12960
- }));
12961
- });
12962
- return PersistencePromise.waitFor(promises).next(() => result);
13189
+ });
13190
+ }).next(() => result);
12963
13191
  }
12964
13192
  saveOverlays(transaction, largestBatchId, overlays) {
12965
13193
  overlays.forEach((_, mutation) => {
@@ -14123,11 +14351,8 @@ class SchemaConverter {
14123
14351
  const mutationsStore = transaction.store(DbMutationBatchStore);
14124
14352
  const remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer);
14125
14353
  const memoryPersistence = new MemoryPersistence(MemoryEagerDelegate.factory, this.serializer.remoteSerializer);
14126
- const promises = [];
14127
- const userToDocumentSet = new Map();
14128
- return mutationsStore
14129
- .loadAll()
14130
- .next(dbBatches => {
14354
+ return mutationsStore.loadAll().next(dbBatches => {
14355
+ const userToDocumentSet = new Map();
14131
14356
  dbBatches.forEach(dbBatch => {
14132
14357
  var _a;
14133
14358
  let documentSet = (_a = userToDocumentSet.get(dbBatch.userId)) !== null && _a !== void 0 ? _a : documentKeySet();
@@ -14135,9 +14360,7 @@ class SchemaConverter {
14135
14360
  batch.keys().forEach(key => (documentSet = documentSet.add(key)));
14136
14361
  userToDocumentSet.set(dbBatch.userId, documentSet);
14137
14362
  });
14138
- })
14139
- .next(() => {
14140
- userToDocumentSet.forEach((allDocumentKeysForUser, userId) => {
14363
+ return PersistencePromise.forEach(userToDocumentSet, (allDocumentKeysForUser, userId) => {
14141
14364
  const user = new User(userId);
14142
14365
  const documentOverlayCache = IndexedDbDocumentOverlayCache.forUser(this.serializer, user);
14143
14366
  // NOTE: The index manager and the reference delegate are
@@ -14147,10 +14370,11 @@ class SchemaConverter {
14147
14370
  const indexManager = memoryPersistence.getIndexManager(user);
14148
14371
  const mutationQueue = IndexedDbMutationQueue.forUser(user, this.serializer, indexManager, memoryPersistence.referenceDelegate);
14149
14372
  const localDocumentsView = new LocalDocumentsView(remoteDocumentCache, mutationQueue, documentOverlayCache, indexManager);
14150
- promises.push(localDocumentsView.recalculateAndSaveOverlaysForDocumentKeys(new IndexedDbTransaction(transaction, ListenSequence.INVALID), allDocumentKeysForUser));
14373
+ return localDocumentsView
14374
+ .recalculateAndSaveOverlaysForDocumentKeys(new IndexedDbTransaction(transaction, ListenSequence.INVALID), allDocumentKeysForUser)
14375
+ .next();
14151
14376
  });
14152
- })
14153
- .next(() => PersistencePromise.waitFor(promises));
14377
+ });
14154
14378
  }
14155
14379
  }
14156
14380
  function sentinelKey(path) {
@@ -15215,7 +15439,7 @@ function localStoreWriteLocally(localStore, mutations) {
15215
15439
  // any additional user-provided writes.
15216
15440
  return localStoreImpl.localDocuments.getOverlayedDocuments(txn, remoteDocs);
15217
15441
  })
15218
- .next(docs => {
15442
+ .next((docs) => {
15219
15443
  overlayedDocuments = docs;
15220
15444
  // For non-idempotent mutations (such as `FieldValue.increment()`),
15221
15445
  // we record the base state in a separate patch mutation. This is
@@ -15240,11 +15464,10 @@ function localStoreWriteLocally(localStore, mutations) {
15240
15464
  return localStoreImpl.documentOverlayCache.saveOverlays(txn, batch.batchId, overlays);
15241
15465
  });
15242
15466
  })
15243
- .then(() => {
15244
- let documents = documentMap();
15245
- overlayedDocuments.forEach((key, val) => (documents = documents.insert(key, val.overlayedDocument)));
15246
- return { batchId: mutationBatch.batchId, changes: documents };
15247
- });
15467
+ .then(() => ({
15468
+ batchId: mutationBatch.batchId,
15469
+ changes: convertOverlayedDocumentMapToDocumentMap(overlayedDocuments)
15470
+ }));
15248
15471
  }
15249
15472
  /**
15250
15473
  * Acknowledges the given batch.
@@ -23628,10 +23851,14 @@ class MemoryOfflineComponentProvider {
23628
23851
  this.sharedClientState = this.createSharedClientState(cfg);
23629
23852
  this.persistence = this.createPersistence(cfg);
23630
23853
  await this.persistence.start();
23631
- this.gcScheduler = this.createGarbageCollectionScheduler(cfg);
23632
23854
  this.localStore = this.createLocalStore(cfg);
23855
+ this.gcScheduler = this.createGarbageCollectionScheduler(cfg, this.localStore);
23856
+ this.indexBackfillerScheduler = this.createIndexBackfillerScheduler(cfg, this.localStore);
23633
23857
  }
23634
- createGarbageCollectionScheduler(cfg) {
23858
+ createGarbageCollectionScheduler(cfg, localStore) {
23859
+ return null;
23860
+ }
23861
+ createIndexBackfillerScheduler(cfg, localStore) {
23635
23862
  return null;
23636
23863
  }
23637
23864
  createLocalStore(cfg) {
@@ -23672,7 +23899,11 @@ class IndexedDbOfflineComponentProvider extends MemoryOfflineComponentProvider {
23672
23899
  // set it after localStore / remoteStore are started.
23673
23900
  await this.persistence.setPrimaryStateListener(() => {
23674
23901
  if (this.gcScheduler && !this.gcScheduler.started) {
23675
- this.gcScheduler.start(this.localStore);
23902
+ this.gcScheduler.start();
23903
+ }
23904
+ if (this.indexBackfillerScheduler &&
23905
+ !this.indexBackfillerScheduler.started) {
23906
+ this.indexBackfillerScheduler.start();
23676
23907
  }
23677
23908
  return Promise.resolve();
23678
23909
  });
@@ -23680,9 +23911,13 @@ class IndexedDbOfflineComponentProvider extends MemoryOfflineComponentProvider {
23680
23911
  createLocalStore(cfg) {
23681
23912
  return newLocalStore(this.persistence, new QueryEngine(), cfg.initialUser, this.serializer);
23682
23913
  }
23683
- createGarbageCollectionScheduler(cfg) {
23914
+ createGarbageCollectionScheduler(cfg, localStore) {
23684
23915
  const garbageCollector = this.persistence.referenceDelegate.garbageCollector;
23685
- return new LruScheduler(garbageCollector, cfg.asyncQueue);
23916
+ return new LruScheduler(garbageCollector, cfg.asyncQueue, localStore);
23917
+ }
23918
+ createIndexBackfillerScheduler(cfg, localStore) {
23919
+ const indexBackfiller = new IndexBackfiller(localStore, this.persistence);
23920
+ return new IndexBackfillerScheduler(cfg.asyncQueue, indexBackfiller);
23686
23921
  }
23687
23922
  createPersistence(cfg) {
23688
23923
  const persistenceKey = indexedDbStoragePrefix(cfg.databaseInfo.databaseId, cfg.databaseInfo.persistenceKey);
@@ -23729,12 +23964,20 @@ class MultiTabOfflineComponentProvider extends IndexedDbOfflineComponentProvider
23729
23964
  await syncEngineApplyPrimaryState(this.onlineComponentProvider.syncEngine, isPrimary);
23730
23965
  if (this.gcScheduler) {
23731
23966
  if (isPrimary && !this.gcScheduler.started) {
23732
- this.gcScheduler.start(this.localStore);
23967
+ this.gcScheduler.start();
23733
23968
  }
23734
23969
  else if (!isPrimary) {
23735
23970
  this.gcScheduler.stop();
23736
23971
  }
23737
23972
  }
23973
+ if (this.indexBackfillerScheduler) {
23974
+ if (isPrimary && !this.indexBackfillerScheduler.started) {
23975
+ this.indexBackfillerScheduler.start();
23976
+ }
23977
+ else if (!isPrimary) {
23978
+ this.indexBackfillerScheduler.stop();
23979
+ }
23980
+ }
23738
23981
  });
23739
23982
  }
23740
23983
  createSharedClientState(cfg) {