@firebase/firestore 3.7.3 → 3.8.0-canary.06dc1364d

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 (124) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/firestore/lite/index.d.ts +1 -1
  3. package/dist/firestore/src/api/filter.d.ts +1 -1
  4. package/dist/firestore/src/api/reference_impl.d.ts +2 -1
  5. package/dist/firestore/src/api.d.ts +1 -1
  6. package/dist/firestore/src/core/bound.d.ts +49 -0
  7. package/dist/firestore/src/core/filter.d.ts +134 -0
  8. package/dist/firestore/src/core/order_by.d.ts +35 -0
  9. package/dist/firestore/src/core/query.d.ts +5 -6
  10. package/dist/firestore/src/core/target.d.ts +4 -120
  11. package/dist/firestore/src/lite-api/query.d.ts +275 -66
  12. package/dist/firestore/src/local/indexeddb_index_manager.d.ts +2 -0
  13. package/dist/firestore/src/local/local_documents_view.d.ts +0 -2
  14. package/dist/firestore/src/model/document.d.ts +8 -1
  15. package/dist/firestore/src/protos/firestore_proto_api.d.ts +1 -1
  16. package/dist/firestore/src/remote/serializer.d.ts +10 -3
  17. package/dist/firestore/src/util/logic_utils.d.ts +52 -0
  18. package/dist/firestore/src/util/types.d.ts +1 -1
  19. package/dist/firestore/test/lite/helpers.d.ts +1 -1
  20. package/dist/firestore/test/unit/core/filter.test.d.ts +17 -0
  21. package/dist/firestore/test/unit/specs/spec_test_runner.d.ts +2 -1
  22. package/dist/firestore/test/unit/util/logic_utils.test.d.ts +17 -0
  23. package/dist/firestore/test/util/helpers.d.ts +6 -2
  24. package/dist/firestore/test/util/test_platform.d.ts +2 -2
  25. package/dist/index.d.ts +140 -63
  26. package/dist/index.esm2017.js +4307 -3743
  27. package/dist/index.esm2017.js.map +1 -1
  28. package/dist/index.esm5.js +4994 -4410
  29. package/dist/index.esm5.js.map +1 -1
  30. package/dist/index.node.cjs.js +1670 -890
  31. package/dist/index.node.cjs.js.map +1 -1
  32. package/dist/index.node.mjs +1663 -891
  33. package/dist/index.node.mjs.map +1 -1
  34. package/dist/index.rn.js +4532 -3968
  35. package/dist/index.rn.js.map +1 -1
  36. package/dist/internal.d.ts +332 -69
  37. package/dist/lite/firestore/lite/index.d.ts +1 -1
  38. package/dist/lite/firestore/src/api/filter.d.ts +1 -1
  39. package/dist/lite/firestore/src/api/reference_impl.d.ts +2 -1
  40. package/dist/lite/firestore/src/api.d.ts +1 -1
  41. package/dist/lite/firestore/src/core/bound.d.ts +49 -0
  42. package/dist/lite/firestore/src/core/filter.d.ts +134 -0
  43. package/dist/lite/firestore/src/core/order_by.d.ts +35 -0
  44. package/dist/lite/firestore/src/core/query.d.ts +5 -6
  45. package/dist/lite/firestore/src/core/target.d.ts +4 -120
  46. package/dist/lite/firestore/src/lite-api/query.d.ts +275 -66
  47. package/dist/lite/firestore/src/local/indexeddb_index_manager.d.ts +2 -0
  48. package/dist/lite/firestore/src/local/local_documents_view.d.ts +0 -2
  49. package/dist/lite/firestore/src/model/document.d.ts +8 -1
  50. package/dist/lite/firestore/src/protos/firestore_proto_api.d.ts +1 -1
  51. package/dist/lite/firestore/src/remote/serializer.d.ts +10 -3
  52. package/dist/lite/firestore/src/util/logic_utils.d.ts +52 -0
  53. package/dist/lite/firestore/src/util/types.d.ts +1 -1
  54. package/dist/lite/firestore/test/lite/helpers.d.ts +1 -1
  55. package/dist/lite/firestore/test/unit/core/filter.test.d.ts +17 -0
  56. package/dist/lite/firestore/test/unit/specs/spec_test_runner.d.ts +2 -1
  57. package/dist/lite/firestore/test/unit/util/logic_utils.test.d.ts +17 -0
  58. package/dist/lite/firestore/test/util/helpers.d.ts +6 -2
  59. package/dist/lite/firestore/test/util/test_platform.d.ts +2 -2
  60. package/dist/lite/index.browser.esm2017.js +2093 -1793
  61. package/dist/lite/index.browser.esm2017.js.map +1 -1
  62. package/dist/lite/index.browser.esm5.js +2241 -2000
  63. package/dist/lite/index.browser.esm5.js.map +1 -1
  64. package/dist/lite/index.d.ts +138 -62
  65. package/dist/lite/index.node.cjs.js +2214 -1802
  66. package/dist/lite/index.node.cjs.js.map +1 -1
  67. package/dist/lite/index.node.mjs +2207 -1803
  68. package/dist/lite/index.node.mjs.map +1 -1
  69. package/dist/lite/index.rn.esm2017.js +2079 -1778
  70. package/dist/lite/index.rn.esm2017.js.map +1 -1
  71. package/dist/lite/internal.d.ts +330 -66
  72. package/dist/lite/packages/firestore/dist/lite/index.browser.esm2017.d.ts +324 -174
  73. package/dist/lite/packages/firestore/lite/index.d.ts +1 -1
  74. package/dist/lite/packages/firestore/rollup.config.debug.d.ts +1 -1
  75. package/dist/lite/packages/firestore/src/api/filter.d.ts +1 -1
  76. package/dist/lite/packages/firestore/src/api/reference_impl.d.ts +2 -1
  77. package/dist/lite/packages/firestore/src/api.d.ts +1 -1
  78. package/dist/lite/packages/firestore/src/core/bound.d.ts +49 -0
  79. package/dist/lite/packages/firestore/src/core/filter.d.ts +134 -0
  80. package/dist/lite/packages/firestore/src/core/order_by.d.ts +35 -0
  81. package/dist/lite/packages/firestore/src/core/query.d.ts +5 -6
  82. package/dist/lite/packages/firestore/src/core/target.d.ts +4 -120
  83. package/dist/lite/packages/firestore/src/lite-api/query.d.ts +275 -66
  84. package/dist/lite/packages/firestore/src/local/indexeddb_index_manager.d.ts +2 -0
  85. package/dist/lite/packages/firestore/src/local/local_documents_view.d.ts +0 -2
  86. package/dist/lite/packages/firestore/src/model/document.d.ts +8 -1
  87. package/dist/lite/packages/firestore/src/protos/firestore_proto_api.d.ts +1 -1
  88. package/dist/lite/packages/firestore/src/remote/serializer.d.ts +10 -3
  89. package/dist/lite/packages/firestore/src/util/logic_utils.d.ts +52 -0
  90. package/dist/lite/packages/firestore/src/util/types.d.ts +1 -1
  91. package/dist/lite/packages/firestore/test/lite/helpers.d.ts +1 -1
  92. package/dist/lite/packages/firestore/test/unit/core/filter.test.d.ts +17 -0
  93. package/dist/lite/packages/firestore/test/unit/specs/spec_test_runner.d.ts +2 -1
  94. package/dist/lite/packages/firestore/test/unit/util/logic_utils.test.d.ts +17 -0
  95. package/dist/lite/packages/firestore/test/util/helpers.d.ts +6 -2
  96. package/dist/lite/packages/firestore/test/util/test_platform.d.ts +2 -2
  97. package/dist/lite/private.d.ts +240 -66
  98. package/dist/packages/firestore/dist/index.esm2017.d.ts +373 -210
  99. package/dist/packages/firestore/lite/index.d.ts +1 -1
  100. package/dist/packages/firestore/rollup.config.debug.d.ts +1 -1
  101. package/dist/packages/firestore/src/api/filter.d.ts +1 -1
  102. package/dist/packages/firestore/src/api/reference_impl.d.ts +2 -1
  103. package/dist/packages/firestore/src/api.d.ts +1 -1
  104. package/dist/packages/firestore/src/core/bound.d.ts +49 -0
  105. package/dist/packages/firestore/src/core/filter.d.ts +134 -0
  106. package/dist/packages/firestore/src/core/order_by.d.ts +35 -0
  107. package/dist/packages/firestore/src/core/query.d.ts +5 -6
  108. package/dist/packages/firestore/src/core/target.d.ts +4 -120
  109. package/dist/packages/firestore/src/lite-api/query.d.ts +275 -66
  110. package/dist/packages/firestore/src/local/indexeddb_index_manager.d.ts +2 -0
  111. package/dist/packages/firestore/src/local/local_documents_view.d.ts +0 -2
  112. package/dist/packages/firestore/src/model/document.d.ts +8 -1
  113. package/dist/packages/firestore/src/protos/firestore_proto_api.d.ts +1 -1
  114. package/dist/packages/firestore/src/remote/serializer.d.ts +10 -3
  115. package/dist/packages/firestore/src/util/logic_utils.d.ts +52 -0
  116. package/dist/packages/firestore/src/util/types.d.ts +1 -1
  117. package/dist/packages/firestore/test/lite/helpers.d.ts +1 -1
  118. package/dist/packages/firestore/test/unit/core/filter.test.d.ts +17 -0
  119. package/dist/packages/firestore/test/unit/specs/spec_test_runner.d.ts +2 -1
  120. package/dist/packages/firestore/test/unit/util/logic_utils.test.d.ts +17 -0
  121. package/dist/packages/firestore/test/util/helpers.d.ts +6 -2
  122. package/dist/packages/firestore/test/util/test_platform.d.ts +2 -2
  123. package/dist/private.d.ts +242 -69
  124. package/package.json +14 -12
@@ -8,7 +8,7 @@ import * as grpc from '@grpc/grpc-js';
8
8
  import * as protoLoader from '@grpc/proto-loader';
9
9
 
10
10
  const name = "@firebase/firestore";
11
- const version$1 = "3.7.3";
11
+ const version$1 = "3.8.0-canary.06dc1364d";
12
12
 
13
13
  /**
14
14
  * @license
@@ -61,7 +61,7 @@ User.GOOGLE_CREDENTIALS = new User('google-credentials-uid');
61
61
  User.FIRST_PARTY = new User('first-party-uid');
62
62
  User.MOCK_USER = new User('mock-user');
63
63
 
64
- const version = "9.14.0";
64
+ const version = "9.15.0-canary.06dc1364d";
65
65
 
66
66
  /**
67
67
  * @license
@@ -1499,11 +1499,11 @@ class FieldIndex {
1499
1499
  FieldIndex.UNKNOWN_ID = -1;
1500
1500
  /** Returns the ArrayContains/ArrayContainsAny segment for this index. */
1501
1501
  function fieldIndexGetArraySegment(fieldIndex) {
1502
- return fieldIndex.fields.find(s => s.kind === 2 /* CONTAINS */);
1502
+ return fieldIndex.fields.find(s => s.kind === 2 /* IndexKind.CONTAINS */);
1503
1503
  }
1504
1504
  /** Returns all directional (ascending/descending) segments for this index. */
1505
1505
  function fieldIndexGetDirectionalSegments(fieldIndex) {
1506
- return fieldIndex.fields.filter(s => s.kind !== 2 /* CONTAINS */);
1506
+ return fieldIndex.fields.filter(s => s.kind !== 2 /* IndexKind.CONTAINS */);
1507
1507
  }
1508
1508
  /**
1509
1509
  * Returns the order of the document key component for the given index.
@@ -1513,7 +1513,7 @@ function fieldIndexGetDirectionalSegments(fieldIndex) {
1513
1513
  function fieldIndexGetKeyOrder(fieldIndex) {
1514
1514
  const directionalSegments = fieldIndexGetDirectionalSegments(fieldIndex);
1515
1515
  return directionalSegments.length === 0
1516
- ? 0 /* ASCENDING */
1516
+ ? 0 /* IndexKind.ASCENDING */
1517
1517
  : directionalSegments[directionalSegments.length - 1].kind;
1518
1518
  }
1519
1519
  /**
@@ -2627,7 +2627,7 @@ class IndexBackfillerScheduler {
2627
2627
  }
2628
2628
  schedule(delay) {
2629
2629
  logDebug(LOG_TAG$h, `Scheduled in ${delay}ms`);
2630
- this.task = this.asyncQueue.enqueueAfterDelay("index_backfill" /* IndexBackfill */, delay, async () => {
2630
+ this.task = this.asyncQueue.enqueueAfterDelay("index_backfill" /* TimerId.IndexBackfill */, delay, async () => {
2631
2631
  this.task = null;
2632
2632
  try {
2633
2633
  const documentsProcessed = await this.backfiller.backfill();
@@ -4423,40 +4423,40 @@ const MIN_VALUE = {
4423
4423
  /** Extracts the backend's type order for the provided value. */
4424
4424
  function typeOrder(value) {
4425
4425
  if ('nullValue' in value) {
4426
- return 0 /* NullValue */;
4426
+ return 0 /* TypeOrder.NullValue */;
4427
4427
  }
4428
4428
  else if ('booleanValue' in value) {
4429
- return 1 /* BooleanValue */;
4429
+ return 1 /* TypeOrder.BooleanValue */;
4430
4430
  }
4431
4431
  else if ('integerValue' in value || 'doubleValue' in value) {
4432
- return 2 /* NumberValue */;
4432
+ return 2 /* TypeOrder.NumberValue */;
4433
4433
  }
4434
4434
  else if ('timestampValue' in value) {
4435
- return 3 /* TimestampValue */;
4435
+ return 3 /* TypeOrder.TimestampValue */;
4436
4436
  }
4437
4437
  else if ('stringValue' in value) {
4438
- return 5 /* StringValue */;
4438
+ return 5 /* TypeOrder.StringValue */;
4439
4439
  }
4440
4440
  else if ('bytesValue' in value) {
4441
- return 6 /* BlobValue */;
4441
+ return 6 /* TypeOrder.BlobValue */;
4442
4442
  }
4443
4443
  else if ('referenceValue' in value) {
4444
- return 7 /* RefValue */;
4444
+ return 7 /* TypeOrder.RefValue */;
4445
4445
  }
4446
4446
  else if ('geoPointValue' in value) {
4447
- return 8 /* GeoPointValue */;
4447
+ return 8 /* TypeOrder.GeoPointValue */;
4448
4448
  }
4449
4449
  else if ('arrayValue' in value) {
4450
- return 9 /* ArrayValue */;
4450
+ return 9 /* TypeOrder.ArrayValue */;
4451
4451
  }
4452
4452
  else if ('mapValue' in value) {
4453
4453
  if (isServerTimestamp(value)) {
4454
- return 4 /* ServerTimestampValue */;
4454
+ return 4 /* TypeOrder.ServerTimestampValue */;
4455
4455
  }
4456
4456
  else if (isMaxValue(value)) {
4457
- return 9007199254740991 /* MaxValue */;
4457
+ return 9007199254740991 /* TypeOrder.MaxValue */;
4458
4458
  }
4459
- return 10 /* ObjectValue */;
4459
+ return 10 /* TypeOrder.ObjectValue */;
4460
4460
  }
4461
4461
  else {
4462
4462
  return fail();
@@ -4473,29 +4473,29 @@ function valueEquals(left, right) {
4473
4473
  return false;
4474
4474
  }
4475
4475
  switch (leftType) {
4476
- case 0 /* NullValue */:
4476
+ case 0 /* TypeOrder.NullValue */:
4477
4477
  return true;
4478
- case 1 /* BooleanValue */:
4478
+ case 1 /* TypeOrder.BooleanValue */:
4479
4479
  return left.booleanValue === right.booleanValue;
4480
- case 4 /* ServerTimestampValue */:
4480
+ case 4 /* TypeOrder.ServerTimestampValue */:
4481
4481
  return getLocalWriteTime(left).isEqual(getLocalWriteTime(right));
4482
- case 3 /* TimestampValue */:
4482
+ case 3 /* TypeOrder.TimestampValue */:
4483
4483
  return timestampEquals(left, right);
4484
- case 5 /* StringValue */:
4484
+ case 5 /* TypeOrder.StringValue */:
4485
4485
  return left.stringValue === right.stringValue;
4486
- case 6 /* BlobValue */:
4486
+ case 6 /* TypeOrder.BlobValue */:
4487
4487
  return blobEquals(left, right);
4488
- case 7 /* RefValue */:
4488
+ case 7 /* TypeOrder.RefValue */:
4489
4489
  return left.referenceValue === right.referenceValue;
4490
- case 8 /* GeoPointValue */:
4490
+ case 8 /* TypeOrder.GeoPointValue */:
4491
4491
  return geoPointEquals(left, right);
4492
- case 2 /* NumberValue */:
4492
+ case 2 /* TypeOrder.NumberValue */:
4493
4493
  return numberEquals(left, right);
4494
- case 9 /* ArrayValue */:
4494
+ case 9 /* TypeOrder.ArrayValue */:
4495
4495
  return arrayEquals(left.arrayValue.values || [], right.arrayValue.values || [], valueEquals);
4496
- case 10 /* ObjectValue */:
4496
+ case 10 /* TypeOrder.ObjectValue */:
4497
4497
  return objectEquals(left, right);
4498
- case 9007199254740991 /* MaxValue */:
4498
+ case 9007199254740991 /* TypeOrder.MaxValue */:
4499
4499
  return true;
4500
4500
  default:
4501
4501
  return fail();
@@ -4568,28 +4568,28 @@ function valueCompare(left, right) {
4568
4568
  return primitiveComparator(leftType, rightType);
4569
4569
  }
4570
4570
  switch (leftType) {
4571
- case 0 /* NullValue */:
4572
- case 9007199254740991 /* MaxValue */:
4571
+ case 0 /* TypeOrder.NullValue */:
4572
+ case 9007199254740991 /* TypeOrder.MaxValue */:
4573
4573
  return 0;
4574
- case 1 /* BooleanValue */:
4574
+ case 1 /* TypeOrder.BooleanValue */:
4575
4575
  return primitiveComparator(left.booleanValue, right.booleanValue);
4576
- case 2 /* NumberValue */:
4576
+ case 2 /* TypeOrder.NumberValue */:
4577
4577
  return compareNumbers(left, right);
4578
- case 3 /* TimestampValue */:
4578
+ case 3 /* TypeOrder.TimestampValue */:
4579
4579
  return compareTimestamps(left.timestampValue, right.timestampValue);
4580
- case 4 /* ServerTimestampValue */:
4580
+ case 4 /* TypeOrder.ServerTimestampValue */:
4581
4581
  return compareTimestamps(getLocalWriteTime(left), getLocalWriteTime(right));
4582
- case 5 /* StringValue */:
4582
+ case 5 /* TypeOrder.StringValue */:
4583
4583
  return primitiveComparator(left.stringValue, right.stringValue);
4584
- case 6 /* BlobValue */:
4584
+ case 6 /* TypeOrder.BlobValue */:
4585
4585
  return compareBlobs(left.bytesValue, right.bytesValue);
4586
- case 7 /* RefValue */:
4586
+ case 7 /* TypeOrder.RefValue */:
4587
4587
  return compareReferences(left.referenceValue, right.referenceValue);
4588
- case 8 /* GeoPointValue */:
4588
+ case 8 /* TypeOrder.GeoPointValue */:
4589
4589
  return compareGeoPoints(left.geoPointValue, right.geoPointValue);
4590
- case 9 /* ArrayValue */:
4590
+ case 9 /* TypeOrder.ArrayValue */:
4591
4591
  return compareArrays(left.arrayValue, right.arrayValue);
4592
- case 10 /* ObjectValue */:
4592
+ case 10 /* TypeOrder.ObjectValue */:
4593
4593
  return compareMaps(left.mapValue, right.mapValue);
4594
4594
  default:
4595
4595
  throw fail();
@@ -5142,11 +5142,12 @@ function extractFieldMask(value) {
5142
5142
  * from all views.
5143
5143
  */
5144
5144
  class MutableDocument {
5145
- constructor(key, documentType, version, readTime, data, documentState) {
5145
+ constructor(key, documentType, version, readTime, createTime, data, documentState) {
5146
5146
  this.key = key;
5147
5147
  this.documentType = documentType;
5148
5148
  this.version = version;
5149
5149
  this.readTime = readTime;
5150
+ this.createTime = createTime;
5150
5151
  this.data = data;
5151
5152
  this.documentState = documentState;
5152
5153
  }
@@ -5155,18 +5156,27 @@ class MutableDocument {
5155
5156
  * base document for mutations.
5156
5157
  */
5157
5158
  static newInvalidDocument(documentKey) {
5158
- return new MutableDocument(documentKey, 0 /* INVALID */, SnapshotVersion.min(), SnapshotVersion.min(), ObjectValue.empty(), 0 /* SYNCED */);
5159
+ return new MutableDocument(documentKey, 0 /* DocumentType.INVALID */,
5160
+ /* version */ SnapshotVersion.min(),
5161
+ /* readTime */ SnapshotVersion.min(),
5162
+ /* createTime */ SnapshotVersion.min(), ObjectValue.empty(), 0 /* DocumentState.SYNCED */);
5159
5163
  }
5160
5164
  /**
5161
5165
  * Creates a new document that is known to exist with the given data at the
5162
5166
  * given version.
5163
5167
  */
5164
- static newFoundDocument(documentKey, version, value) {
5165
- return new MutableDocument(documentKey, 1 /* FOUND_DOCUMENT */, version, SnapshotVersion.min(), value, 0 /* SYNCED */);
5168
+ static newFoundDocument(documentKey, version, createTime, value) {
5169
+ return new MutableDocument(documentKey, 1 /* DocumentType.FOUND_DOCUMENT */,
5170
+ /* version */ version,
5171
+ /* readTime */ SnapshotVersion.min(),
5172
+ /* createTime */ createTime, value, 0 /* DocumentState.SYNCED */);
5166
5173
  }
5167
5174
  /** Creates a new document that is known to not exist at the given version. */
5168
5175
  static newNoDocument(documentKey, version) {
5169
- return new MutableDocument(documentKey, 2 /* NO_DOCUMENT */, version, SnapshotVersion.min(), ObjectValue.empty(), 0 /* SYNCED */);
5176
+ return new MutableDocument(documentKey, 2 /* DocumentType.NO_DOCUMENT */,
5177
+ /* version */ version,
5178
+ /* readTime */ SnapshotVersion.min(),
5179
+ /* createTime */ SnapshotVersion.min(), ObjectValue.empty(), 0 /* DocumentState.SYNCED */);
5170
5180
  }
5171
5181
  /**
5172
5182
  * Creates a new document that is known to exist at the given version but
@@ -5174,17 +5184,30 @@ class MutableDocument {
5174
5184
  * base document).
5175
5185
  */
5176
5186
  static newUnknownDocument(documentKey, version) {
5177
- return new MutableDocument(documentKey, 3 /* UNKNOWN_DOCUMENT */, version, SnapshotVersion.min(), ObjectValue.empty(), 2 /* HAS_COMMITTED_MUTATIONS */);
5187
+ return new MutableDocument(documentKey, 3 /* DocumentType.UNKNOWN_DOCUMENT */,
5188
+ /* version */ version,
5189
+ /* readTime */ SnapshotVersion.min(),
5190
+ /* createTime */ SnapshotVersion.min(), ObjectValue.empty(), 2 /* DocumentState.HAS_COMMITTED_MUTATIONS */);
5178
5191
  }
5179
5192
  /**
5180
5193
  * Changes the document type to indicate that it exists and that its version
5181
5194
  * and data are known.
5182
5195
  */
5183
5196
  convertToFoundDocument(version, value) {
5197
+ // If a document is switching state from being an invalid or deleted
5198
+ // document to a valid (FOUND_DOCUMENT) document, either due to receiving an
5199
+ // update from Watch or due to applying a local set mutation on top
5200
+ // of a deleted document, our best guess about its createTime would be the
5201
+ // version at which the document transitioned to a FOUND_DOCUMENT.
5202
+ if (this.createTime.isEqual(SnapshotVersion.min()) &&
5203
+ (this.documentType === 2 /* DocumentType.NO_DOCUMENT */ ||
5204
+ this.documentType === 0 /* DocumentType.INVALID */)) {
5205
+ this.createTime = version;
5206
+ }
5184
5207
  this.version = version;
5185
- this.documentType = 1 /* FOUND_DOCUMENT */;
5208
+ this.documentType = 1 /* DocumentType.FOUND_DOCUMENT */;
5186
5209
  this.data = value;
5187
- this.documentState = 0 /* SYNCED */;
5210
+ this.documentState = 0 /* DocumentState.SYNCED */;
5188
5211
  return this;
5189
5212
  }
5190
5213
  /**
@@ -5193,9 +5216,9 @@ class MutableDocument {
5193
5216
  */
5194
5217
  convertToNoDocument(version) {
5195
5218
  this.version = version;
5196
- this.documentType = 2 /* NO_DOCUMENT */;
5219
+ this.documentType = 2 /* DocumentType.NO_DOCUMENT */;
5197
5220
  this.data = ObjectValue.empty();
5198
- this.documentState = 0 /* SYNCED */;
5221
+ this.documentState = 0 /* DocumentState.SYNCED */;
5199
5222
  return this;
5200
5223
  }
5201
5224
  /**
@@ -5205,17 +5228,17 @@ class MutableDocument {
5205
5228
  */
5206
5229
  convertToUnknownDocument(version) {
5207
5230
  this.version = version;
5208
- this.documentType = 3 /* UNKNOWN_DOCUMENT */;
5231
+ this.documentType = 3 /* DocumentType.UNKNOWN_DOCUMENT */;
5209
5232
  this.data = ObjectValue.empty();
5210
- this.documentState = 2 /* HAS_COMMITTED_MUTATIONS */;
5233
+ this.documentState = 2 /* DocumentState.HAS_COMMITTED_MUTATIONS */;
5211
5234
  return this;
5212
5235
  }
5213
5236
  setHasCommittedMutations() {
5214
- this.documentState = 2 /* HAS_COMMITTED_MUTATIONS */;
5237
+ this.documentState = 2 /* DocumentState.HAS_COMMITTED_MUTATIONS */;
5215
5238
  return this;
5216
5239
  }
5217
5240
  setHasLocalMutations() {
5218
- this.documentState = 1 /* HAS_LOCAL_MUTATIONS */;
5241
+ this.documentState = 1 /* DocumentState.HAS_LOCAL_MUTATIONS */;
5219
5242
  this.version = SnapshotVersion.min();
5220
5243
  return this;
5221
5244
  }
@@ -5224,25 +5247,25 @@ class MutableDocument {
5224
5247
  return this;
5225
5248
  }
5226
5249
  get hasLocalMutations() {
5227
- return this.documentState === 1 /* HAS_LOCAL_MUTATIONS */;
5250
+ return this.documentState === 1 /* DocumentState.HAS_LOCAL_MUTATIONS */;
5228
5251
  }
5229
5252
  get hasCommittedMutations() {
5230
- return this.documentState === 2 /* HAS_COMMITTED_MUTATIONS */;
5253
+ return this.documentState === 2 /* DocumentState.HAS_COMMITTED_MUTATIONS */;
5231
5254
  }
5232
5255
  get hasPendingWrites() {
5233
5256
  return this.hasLocalMutations || this.hasCommittedMutations;
5234
5257
  }
5235
5258
  isValidDocument() {
5236
- return this.documentType !== 0 /* INVALID */;
5259
+ return this.documentType !== 0 /* DocumentType.INVALID */;
5237
5260
  }
5238
5261
  isFoundDocument() {
5239
- return this.documentType === 1 /* FOUND_DOCUMENT */;
5262
+ return this.documentType === 1 /* DocumentType.FOUND_DOCUMENT */;
5240
5263
  }
5241
5264
  isNoDocument() {
5242
- return this.documentType === 2 /* NO_DOCUMENT */;
5265
+ return this.documentType === 2 /* DocumentType.NO_DOCUMENT */;
5243
5266
  }
5244
5267
  isUnknownDocument() {
5245
- return this.documentType === 3 /* UNKNOWN_DOCUMENT */;
5268
+ return this.documentType === 3 /* DocumentType.UNKNOWN_DOCUMENT */;
5246
5269
  }
5247
5270
  isEqual(other) {
5248
5271
  return (other instanceof MutableDocument &&
@@ -5253,10 +5276,11 @@ class MutableDocument {
5253
5276
  this.data.isEqual(other.data));
5254
5277
  }
5255
5278
  mutableCopy() {
5256
- return new MutableDocument(this.key, this.documentType, this.version, this.readTime, this.data.clone(), this.documentState);
5279
+ return new MutableDocument(this.key, this.documentType, this.version, this.readTime, this.createTime, this.data.clone(), this.documentState);
5257
5280
  }
5258
5281
  toString() {
5259
5282
  return (`Document(${this.key}, ${this.version}, ${JSON.stringify(this.data.value)}, ` +
5283
+ `{createTime: ${this.createTime}}), ` +
5260
5284
  `{documentType: ${this.documentType}}), ` +
5261
5285
  `{documentState: ${this.documentState}})`);
5262
5286
  }
@@ -5278,7 +5302,7 @@ function compareDocumentsByField(field, d1, d2) {
5278
5302
 
5279
5303
  /**
5280
5304
  * @license
5281
- * Copyright 2019 Google LLC
5305
+ * Copyright 2022 Google LLC
5282
5306
  *
5283
5307
  * Licensed under the Apache License, Version 2.0 (the "License");
5284
5308
  * you may not use this file except in compliance with the License.
@@ -5292,453 +5316,353 @@ function compareDocumentsByField(field, d1, d2) {
5292
5316
  * See the License for the specific language governing permissions and
5293
5317
  * limitations under the License.
5294
5318
  */
5295
- // Visible for testing
5296
- class TargetImpl {
5297
- constructor(path, collectionGroup = null, orderBy = [], filters = [], limit = null, startAt = null, endAt = null) {
5298
- this.path = path;
5299
- this.collectionGroup = collectionGroup;
5300
- this.orderBy = orderBy;
5301
- this.filters = filters;
5302
- this.limit = limit;
5303
- this.startAt = startAt;
5304
- this.endAt = endAt;
5305
- this.memoizedCanonicalId = null;
5319
+ /**
5320
+ * Represents a bound of a query.
5321
+ *
5322
+ * The bound is specified with the given components representing a position and
5323
+ * whether it's just before or just after the position (relative to whatever the
5324
+ * query order is).
5325
+ *
5326
+ * The position represents a logical index position for a query. It's a prefix
5327
+ * of values for the (potentially implicit) order by clauses of a query.
5328
+ *
5329
+ * Bound provides a function to determine whether a document comes before or
5330
+ * after a bound. This is influenced by whether the position is just before or
5331
+ * just after the provided values.
5332
+ */
5333
+ class Bound {
5334
+ constructor(position, inclusive) {
5335
+ this.position = position;
5336
+ this.inclusive = inclusive;
5306
5337
  }
5307
5338
  }
5339
+ function boundCompareToDocument(bound, orderBy, doc) {
5340
+ let comparison = 0;
5341
+ for (let i = 0; i < bound.position.length; i++) {
5342
+ const orderByComponent = orderBy[i];
5343
+ const component = bound.position[i];
5344
+ if (orderByComponent.field.isKeyField()) {
5345
+ comparison = DocumentKey.comparator(DocumentKey.fromName(component.referenceValue), doc.key);
5346
+ }
5347
+ else {
5348
+ const docValue = doc.data.field(orderByComponent.field);
5349
+ comparison = valueCompare(component, docValue);
5350
+ }
5351
+ if (orderByComponent.dir === "desc" /* Direction.DESCENDING */) {
5352
+ comparison = comparison * -1;
5353
+ }
5354
+ if (comparison !== 0) {
5355
+ break;
5356
+ }
5357
+ }
5358
+ return comparison;
5359
+ }
5308
5360
  /**
5309
- * Initializes a Target with a path and optional additional query constraints.
5310
- * Path must currently be empty if this is a collection group query.
5361
+ * Returns true if a document sorts after a bound using the provided sort
5362
+ * order.
5363
+ */
5364
+ function boundSortsAfterDocument(bound, orderBy, doc) {
5365
+ const comparison = boundCompareToDocument(bound, orderBy, doc);
5366
+ return bound.inclusive ? comparison >= 0 : comparison > 0;
5367
+ }
5368
+ /**
5369
+ * Returns true if a document sorts before a bound using the provided sort
5370
+ * order.
5371
+ */
5372
+ function boundSortsBeforeDocument(bound, orderBy, doc) {
5373
+ const comparison = boundCompareToDocument(bound, orderBy, doc);
5374
+ return bound.inclusive ? comparison <= 0 : comparison < 0;
5375
+ }
5376
+ function boundEquals(left, right) {
5377
+ if (left === null) {
5378
+ return right === null;
5379
+ }
5380
+ else if (right === null) {
5381
+ return false;
5382
+ }
5383
+ if (left.inclusive !== right.inclusive ||
5384
+ left.position.length !== right.position.length) {
5385
+ return false;
5386
+ }
5387
+ for (let i = 0; i < left.position.length; i++) {
5388
+ const leftPosition = left.position[i];
5389
+ const rightPosition = right.position[i];
5390
+ if (!valueEquals(leftPosition, rightPosition)) {
5391
+ return false;
5392
+ }
5393
+ }
5394
+ return true;
5395
+ }
5396
+
5397
+ /**
5398
+ * @license
5399
+ * Copyright 2022 Google LLC
5311
5400
  *
5312
- * NOTE: you should always construct `Target` from `Query.toTarget` instead of
5313
- * using this factory method, because `Query` provides an implicit `orderBy`
5314
- * property.
5401
+ * Licensed under the Apache License, Version 2.0 (the "License");
5402
+ * you may not use this file except in compliance with the License.
5403
+ * You may obtain a copy of the License at
5404
+ *
5405
+ * http://www.apache.org/licenses/LICENSE-2.0
5406
+ *
5407
+ * Unless required by applicable law or agreed to in writing, software
5408
+ * distributed under the License is distributed on an "AS IS" BASIS,
5409
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5410
+ * See the License for the specific language governing permissions and
5411
+ * limitations under the License.
5315
5412
  */
5316
- function newTarget(path, collectionGroup = null, orderBy = [], filters = [], limit = null, startAt = null, endAt = null) {
5317
- return new TargetImpl(path, collectionGroup, orderBy, filters, limit, startAt, endAt);
5413
+ class Filter {
5318
5414
  }
5319
- function canonifyTarget(target) {
5320
- const targetImpl = debugCast(target);
5321
- if (targetImpl.memoizedCanonicalId === null) {
5322
- let str = targetImpl.path.canonicalString();
5323
- if (targetImpl.collectionGroup !== null) {
5324
- str += '|cg:' + targetImpl.collectionGroup;
5415
+ class FieldFilter extends Filter {
5416
+ constructor(field, op, value) {
5417
+ super();
5418
+ this.field = field;
5419
+ this.op = op;
5420
+ this.value = value;
5421
+ }
5422
+ /**
5423
+ * Creates a filter based on the provided arguments.
5424
+ */
5425
+ static create(field, op, value) {
5426
+ if (field.isKeyField()) {
5427
+ if (op === "in" /* Operator.IN */ || op === "not-in" /* Operator.NOT_IN */) {
5428
+ return this.createKeyFieldInFilter(field, op, value);
5429
+ }
5430
+ else {
5431
+ return new KeyFieldFilter(field, op, value);
5432
+ }
5325
5433
  }
5326
- str += '|f:';
5327
- str += targetImpl.filters.map(f => canonifyFilter(f)).join(',');
5328
- str += '|ob:';
5329
- str += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(',');
5330
- if (!isNullOrUndefined(targetImpl.limit)) {
5331
- str += '|l:';
5332
- str += targetImpl.limit;
5434
+ else if (op === "array-contains" /* Operator.ARRAY_CONTAINS */) {
5435
+ return new ArrayContainsFilter(field, value);
5333
5436
  }
5334
- if (targetImpl.startAt) {
5335
- str += '|lb:';
5336
- str += targetImpl.startAt.inclusive ? 'b:' : 'a:';
5337
- str += targetImpl.startAt.position.map(p => canonicalId(p)).join(',');
5437
+ else if (op === "in" /* Operator.IN */) {
5438
+ return new InFilter(field, value);
5338
5439
  }
5339
- if (targetImpl.endAt) {
5340
- str += '|ub:';
5341
- str += targetImpl.endAt.inclusive ? 'a:' : 'b:';
5342
- str += targetImpl.endAt.position.map(p => canonicalId(p)).join(',');
5440
+ else if (op === "not-in" /* Operator.NOT_IN */) {
5441
+ return new NotInFilter(field, value);
5442
+ }
5443
+ else if (op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
5444
+ return new ArrayContainsAnyFilter(field, value);
5445
+ }
5446
+ else {
5447
+ return new FieldFilter(field, op, value);
5343
5448
  }
5344
- targetImpl.memoizedCanonicalId = str;
5345
5449
  }
5346
- return targetImpl.memoizedCanonicalId;
5347
- }
5348
- function stringifyTarget(target) {
5349
- let str = target.path.canonicalString();
5350
- if (target.collectionGroup !== null) {
5351
- str += ' collectionGroup=' + target.collectionGroup;
5450
+ static createKeyFieldInFilter(field, op, value) {
5451
+ return op === "in" /* Operator.IN */
5452
+ ? new KeyFieldInFilter(field, value)
5453
+ : new KeyFieldNotInFilter(field, value);
5352
5454
  }
5353
- if (target.filters.length > 0) {
5354
- str += `, filters: [${target.filters
5355
- .map(f => stringifyFilter(f))
5356
- .join(', ')}]`;
5455
+ matches(doc) {
5456
+ const other = doc.data.field(this.field);
5457
+ // Types do not have to match in NOT_EQUAL filters.
5458
+ if (this.op === "!=" /* Operator.NOT_EQUAL */) {
5459
+ return (other !== null &&
5460
+ this.matchesComparison(valueCompare(other, this.value)));
5461
+ }
5462
+ // Only compare types with matching backend order (such as double and int).
5463
+ return (other !== null &&
5464
+ typeOrder(this.value) === typeOrder(other) &&
5465
+ this.matchesComparison(valueCompare(other, this.value)));
5357
5466
  }
5358
- if (!isNullOrUndefined(target.limit)) {
5359
- str += ', limit: ' + target.limit;
5467
+ matchesComparison(comparison) {
5468
+ switch (this.op) {
5469
+ case "<" /* Operator.LESS_THAN */:
5470
+ return comparison < 0;
5471
+ case "<=" /* Operator.LESS_THAN_OR_EQUAL */:
5472
+ return comparison <= 0;
5473
+ case "==" /* Operator.EQUAL */:
5474
+ return comparison === 0;
5475
+ case "!=" /* Operator.NOT_EQUAL */:
5476
+ return comparison !== 0;
5477
+ case ">" /* Operator.GREATER_THAN */:
5478
+ return comparison > 0;
5479
+ case ">=" /* Operator.GREATER_THAN_OR_EQUAL */:
5480
+ return comparison >= 0;
5481
+ default:
5482
+ return fail();
5483
+ }
5360
5484
  }
5361
- if (target.orderBy.length > 0) {
5362
- str += `, orderBy: [${target.orderBy
5363
- .map(o => stringifyOrderBy(o))
5364
- .join(', ')}]`;
5485
+ isInequality() {
5486
+ return ([
5487
+ "<" /* Operator.LESS_THAN */,
5488
+ "<=" /* Operator.LESS_THAN_OR_EQUAL */,
5489
+ ">" /* Operator.GREATER_THAN */,
5490
+ ">=" /* Operator.GREATER_THAN_OR_EQUAL */,
5491
+ "!=" /* Operator.NOT_EQUAL */,
5492
+ "not-in" /* Operator.NOT_IN */
5493
+ ].indexOf(this.op) >= 0);
5365
5494
  }
5366
- if (target.startAt) {
5367
- str += ', startAt: ';
5368
- str += target.startAt.inclusive ? 'b:' : 'a:';
5369
- str += target.startAt.position.map(p => canonicalId(p)).join(',');
5495
+ getFlattenedFilters() {
5496
+ return [this];
5370
5497
  }
5371
- if (target.endAt) {
5372
- str += ', endAt: ';
5373
- str += target.endAt.inclusive ? 'a:' : 'b:';
5374
- str += target.endAt.position.map(p => canonicalId(p)).join(',');
5498
+ getFilters() {
5499
+ return [this];
5500
+ }
5501
+ getFirstInequalityField() {
5502
+ if (this.isInequality()) {
5503
+ return this.field;
5504
+ }
5505
+ return null;
5375
5506
  }
5376
- return `Target(${str})`;
5377
5507
  }
5378
- function targetEquals(left, right) {
5379
- if (left.limit !== right.limit) {
5380
- return false;
5508
+ class CompositeFilter extends Filter {
5509
+ constructor(filters, op) {
5510
+ super();
5511
+ this.filters = filters;
5512
+ this.op = op;
5513
+ this.memoizedFlattenedFilters = null;
5381
5514
  }
5382
- if (left.orderBy.length !== right.orderBy.length) {
5383
- return false;
5515
+ /**
5516
+ * Creates a filter based on the provided arguments.
5517
+ */
5518
+ static create(filters, op) {
5519
+ return new CompositeFilter(filters, op);
5384
5520
  }
5385
- for (let i = 0; i < left.orderBy.length; i++) {
5386
- if (!orderByEquals(left.orderBy[i], right.orderBy[i])) {
5387
- return false;
5521
+ matches(doc) {
5522
+ if (compositeFilterIsConjunction(this)) {
5523
+ // For conjunctions, all filters must match, so return false if any filter doesn't match.
5524
+ return this.filters.find(filter => !filter.matches(doc)) === undefined;
5525
+ }
5526
+ else {
5527
+ // For disjunctions, at least one filter should match.
5528
+ return this.filters.find(filter => filter.matches(doc)) !== undefined;
5388
5529
  }
5389
5530
  }
5390
- if (left.filters.length !== right.filters.length) {
5391
- return false;
5392
- }
5393
- for (let i = 0; i < left.filters.length; i++) {
5394
- if (!filterEquals(left.filters[i], right.filters[i])) {
5395
- return false;
5531
+ getFlattenedFilters() {
5532
+ if (this.memoizedFlattenedFilters !== null) {
5533
+ return this.memoizedFlattenedFilters;
5396
5534
  }
5535
+ this.memoizedFlattenedFilters = this.filters.reduce((result, subfilter) => {
5536
+ return result.concat(subfilter.getFlattenedFilters());
5537
+ }, []);
5538
+ return this.memoizedFlattenedFilters;
5397
5539
  }
5398
- if (left.collectionGroup !== right.collectionGroup) {
5399
- return false;
5540
+ // Returns a mutable copy of `this.filters`
5541
+ getFilters() {
5542
+ return Object.assign([], this.filters);
5400
5543
  }
5401
- if (!left.path.isEqual(right.path)) {
5402
- return false;
5544
+ getFirstInequalityField() {
5545
+ const found = this.findFirstMatchingFilter(filter => filter.isInequality());
5546
+ if (found !== null) {
5547
+ return found.field;
5548
+ }
5549
+ return null;
5403
5550
  }
5404
- if (!boundEquals(left.startAt, right.startAt)) {
5405
- return false;
5551
+ // Performs a depth-first search to find and return the first FieldFilter in the composite filter
5552
+ // that satisfies the predicate. Returns `null` if none of the FieldFilters satisfy the
5553
+ // predicate.
5554
+ findFirstMatchingFilter(predicate) {
5555
+ for (const fieldFilter of this.getFlattenedFilters()) {
5556
+ if (predicate(fieldFilter)) {
5557
+ return fieldFilter;
5558
+ }
5559
+ }
5560
+ return null;
5406
5561
  }
5407
- return boundEquals(left.endAt, right.endAt);
5408
5562
  }
5409
- function targetIsDocumentTarget(target) {
5410
- return (DocumentKey.isDocumentKey(target.path) &&
5411
- target.collectionGroup === null &&
5412
- target.filters.length === 0);
5563
+ function compositeFilterIsConjunction(compositeFilter) {
5564
+ return compositeFilter.op === "and" /* CompositeOperator.AND */;
5413
5565
  }
5414
- /** Returns the field filters that target the given field path. */
5415
- function targetGetFieldFiltersForPath(target, path) {
5416
- return target.filters.filter(f => f instanceof FieldFilter && f.field.isEqual(path));
5566
+ function compositeFilterIsDisjunction(compositeFilter) {
5567
+ return compositeFilter.op === "or" /* CompositeOperator.OR */;
5417
5568
  }
5418
5569
  /**
5419
- * Returns the values that are used in ARRAY_CONTAINS or ARRAY_CONTAINS_ANY
5420
- * filters. Returns `null` if there are no such filters.
5570
+ * Returns true if this filter is a conjunction of field filters only. Returns false otherwise.
5421
5571
  */
5422
- function targetGetArrayValues(target, fieldIndex) {
5423
- const segment = fieldIndexGetArraySegment(fieldIndex);
5424
- if (segment === undefined) {
5425
- return null;
5426
- }
5427
- for (const fieldFilter of targetGetFieldFiltersForPath(target, segment.fieldPath)) {
5428
- switch (fieldFilter.op) {
5429
- case "array-contains-any" /* ARRAY_CONTAINS_ANY */:
5430
- return fieldFilter.value.arrayValue.values || [];
5431
- case "array-contains" /* ARRAY_CONTAINS */:
5432
- return [fieldFilter.value];
5433
- // Remaining filters are not array filters.
5434
- }
5435
- }
5436
- return null;
5572
+ function compositeFilterIsFlatConjunction(compositeFilter) {
5573
+ return (compositeFilterIsFlat(compositeFilter) &&
5574
+ compositeFilterIsConjunction(compositeFilter));
5437
5575
  }
5438
5576
  /**
5439
- * Returns the list of values that are used in != or NOT_IN filters. Returns
5440
- * `null` if there are no such filters.
5577
+ * Returns true if this filter does not contain any composite filters. Returns false otherwise.
5441
5578
  */
5442
- function targetGetNotInValues(target, fieldIndex) {
5443
- const values = new Map();
5444
- for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
5445
- for (const fieldFilter of targetGetFieldFiltersForPath(target, segment.fieldPath)) {
5446
- switch (fieldFilter.op) {
5447
- case "==" /* EQUAL */:
5448
- case "in" /* IN */:
5449
- // Encode equality prefix, which is encoded in the index value before
5450
- // the inequality (e.g. `a == 'a' && b != 'b'` is encoded to
5451
- // `value != 'ab'`).
5452
- values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
5453
- break;
5454
- case "not-in" /* NOT_IN */:
5455
- case "!=" /* NOT_EQUAL */:
5456
- // NotIn/NotEqual is always a suffix. There cannot be any remaining
5457
- // segments and hence we can return early here.
5458
- values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
5459
- return Array.from(values.values());
5460
- // Remaining filters cannot be used as notIn bounds.
5461
- }
5579
+ function compositeFilterIsFlat(compositeFilter) {
5580
+ for (const filter of compositeFilter.filters) {
5581
+ if (filter instanceof CompositeFilter) {
5582
+ return false;
5462
5583
  }
5463
5584
  }
5464
- return null;
5585
+ return true;
5465
5586
  }
5466
- /**
5467
- * Returns a lower bound of field values that can be used as a starting point to
5468
- * scan the index defined by `fieldIndex`. Returns `MIN_VALUE` if no lower bound
5469
- * exists.
5470
- */
5471
- function targetGetLowerBound(target, fieldIndex) {
5472
- const values = [];
5473
- let inclusive = true;
5474
- // For each segment, retrieve a lower bound if there is a suitable filter or
5475
- // startAt.
5476
- for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
5477
- const segmentBound = segment.kind === 0 /* ASCENDING */
5478
- ? targetGetAscendingBound(target, segment.fieldPath, target.startAt)
5479
- : targetGetDescendingBound(target, segment.fieldPath, target.startAt);
5480
- values.push(segmentBound.value);
5481
- inclusive && (inclusive = segmentBound.inclusive);
5587
+ function canonifyFilter(filter) {
5588
+ if (filter instanceof FieldFilter) {
5589
+ // TODO(b/29183165): Technically, this won't be unique if two values have
5590
+ // the same description, such as the int 3 and the string "3". So we should
5591
+ // add the types in here somehow, too.
5592
+ return (filter.field.canonicalString() +
5593
+ filter.op.toString() +
5594
+ canonicalId(filter.value));
5595
+ }
5596
+ else if (compositeFilterIsFlatConjunction(filter)) {
5597
+ // Older SDK versions use an implicit AND operation between their filters.
5598
+ // In the new SDK versions, the developer may use an explicit AND filter.
5599
+ // To stay consistent with the old usages, we add a special case to ensure
5600
+ // the canonical ID for these two are the same. For example:
5601
+ // `col.whereEquals("a", 1).whereEquals("b", 2)` should have the same
5602
+ // canonical ID as `col.where(and(equals("a",1), equals("b",2)))`.
5603
+ return filter.filters.map(filter => canonifyFilter(filter)).join(',');
5482
5604
  }
5483
- return new Bound(values, inclusive);
5484
- }
5485
- /**
5486
- * Returns an upper bound of field values that can be used as an ending point
5487
- * when scanning the index defined by `fieldIndex`. Returns `MAX_VALUE` if no
5488
- * upper bound exists.
5489
- */
5490
- function targetGetUpperBound(target, fieldIndex) {
5491
- const values = [];
5492
- let inclusive = true;
5493
- // For each segment, retrieve an upper bound if there is a suitable filter or
5494
- // endAt.
5495
- for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
5496
- const segmentBound = segment.kind === 0 /* ASCENDING */
5497
- ? targetGetDescendingBound(target, segment.fieldPath, target.endAt)
5498
- : targetGetAscendingBound(target, segment.fieldPath, target.endAt);
5499
- values.push(segmentBound.value);
5500
- inclusive && (inclusive = segmentBound.inclusive);
5605
+ else {
5606
+ // filter instanceof CompositeFilter
5607
+ const canonicalIdsString = filter.filters
5608
+ .map(filter => canonifyFilter(filter))
5609
+ .join(',');
5610
+ return `${filter.op}(${canonicalIdsString})`;
5501
5611
  }
5502
- return new Bound(values, inclusive);
5503
5612
  }
5504
- /**
5505
- * Returns the value to use as the lower bound for ascending index segment at
5506
- * the provided `fieldPath` (or the upper bound for an descending segment).
5507
- */
5508
- function targetGetAscendingBound(target, fieldPath, bound) {
5509
- let value = MIN_VALUE;
5510
- let inclusive = true;
5511
- // Process all filters to find a value for the current field segment
5512
- for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) {
5513
- let filterValue = MIN_VALUE;
5514
- let filterInclusive = true;
5515
- switch (fieldFilter.op) {
5516
- case "<" /* LESS_THAN */:
5517
- case "<=" /* LESS_THAN_OR_EQUAL */:
5518
- filterValue = valuesGetLowerBound(fieldFilter.value);
5519
- break;
5520
- case "==" /* EQUAL */:
5521
- case "in" /* IN */:
5522
- case ">=" /* GREATER_THAN_OR_EQUAL */:
5523
- filterValue = fieldFilter.value;
5524
- break;
5525
- case ">" /* GREATER_THAN */:
5526
- filterValue = fieldFilter.value;
5527
- filterInclusive = false;
5528
- break;
5529
- case "!=" /* NOT_EQUAL */:
5530
- case "not-in" /* NOT_IN */:
5531
- filterValue = MIN_VALUE;
5532
- break;
5533
- // Remaining filters cannot be used as lower bounds.
5534
- }
5535
- if (lowerBoundCompare({ value, inclusive }, { value: filterValue, inclusive: filterInclusive }) < 0) {
5536
- value = filterValue;
5537
- inclusive = filterInclusive;
5538
- }
5539
- }
5540
- // If there is an additional bound, compare the values against the existing
5541
- // range to see if we can narrow the scope.
5542
- if (bound !== null) {
5543
- for (let i = 0; i < target.orderBy.length; ++i) {
5544
- const orderBy = target.orderBy[i];
5545
- if (orderBy.field.isEqual(fieldPath)) {
5546
- const cursorValue = bound.position[i];
5547
- if (lowerBoundCompare({ value, inclusive }, { value: cursorValue, inclusive: bound.inclusive }) < 0) {
5548
- value = cursorValue;
5549
- inclusive = bound.inclusive;
5550
- }
5551
- break;
5552
- }
5553
- }
5613
+ function filterEquals(f1, f2) {
5614
+ if (f1 instanceof FieldFilter) {
5615
+ return fieldFilterEquals(f1, f2);
5554
5616
  }
5555
- return { value, inclusive };
5556
- }
5557
- /**
5558
- * Returns the value to use as the upper bound for ascending index segment at
5559
- * the provided `fieldPath` (or the lower bound for a descending segment).
5560
- */
5561
- function targetGetDescendingBound(target, fieldPath, bound) {
5562
- let value = MAX_VALUE;
5563
- let inclusive = true;
5564
- // Process all filters to find a value for the current field segment
5565
- for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) {
5566
- let filterValue = MAX_VALUE;
5567
- let filterInclusive = true;
5568
- switch (fieldFilter.op) {
5569
- case ">=" /* GREATER_THAN_OR_EQUAL */:
5570
- case ">" /* GREATER_THAN */:
5571
- filterValue = valuesGetUpperBound(fieldFilter.value);
5572
- filterInclusive = false;
5573
- break;
5574
- case "==" /* EQUAL */:
5575
- case "in" /* IN */:
5576
- case "<=" /* LESS_THAN_OR_EQUAL */:
5577
- filterValue = fieldFilter.value;
5578
- break;
5579
- case "<" /* LESS_THAN */:
5580
- filterValue = fieldFilter.value;
5581
- filterInclusive = false;
5582
- break;
5583
- case "!=" /* NOT_EQUAL */:
5584
- case "not-in" /* NOT_IN */:
5585
- filterValue = MAX_VALUE;
5586
- break;
5587
- // Remaining filters cannot be used as upper bounds.
5588
- }
5589
- if (upperBoundCompare({ value, inclusive }, { value: filterValue, inclusive: filterInclusive }) > 0) {
5590
- value = filterValue;
5591
- inclusive = filterInclusive;
5592
- }
5617
+ else if (f1 instanceof CompositeFilter) {
5618
+ return compositeFilterEquals(f1, f2);
5593
5619
  }
5594
- // If there is an additional bound, compare the values against the existing
5595
- // range to see if we can narrow the scope.
5596
- if (bound !== null) {
5597
- for (let i = 0; i < target.orderBy.length; ++i) {
5598
- const orderBy = target.orderBy[i];
5599
- if (orderBy.field.isEqual(fieldPath)) {
5600
- const cursorValue = bound.position[i];
5601
- if (upperBoundCompare({ value, inclusive }, { value: cursorValue, inclusive: bound.inclusive }) > 0) {
5602
- value = cursorValue;
5603
- inclusive = bound.inclusive;
5604
- }
5605
- break;
5606
- }
5607
- }
5620
+ else {
5621
+ fail();
5608
5622
  }
5609
- return { value, inclusive };
5610
5623
  }
5611
- /** Returns the number of segments of a perfect index for this target. */
5612
- function targetGetSegmentCount(target) {
5613
- let fields = new SortedSet(FieldPath$1.comparator);
5614
- let hasArraySegment = false;
5615
- for (const filter of target.filters) {
5616
- // TODO(orquery): Use the flattened filters here
5617
- const fieldFilter = filter;
5618
- // __name__ is not an explicit segment of any index, so we don't need to
5619
- // count it.
5620
- if (fieldFilter.field.isKeyField()) {
5621
- continue;
5622
- }
5623
- // ARRAY_CONTAINS or ARRAY_CONTAINS_ANY filters must be counted separately.
5624
- // For instance, it is possible to have an index for "a ARRAY a ASC". Even
5625
- // though these are on the same field, they should be counted as two
5626
- // separate segments in an index.
5627
- if (fieldFilter.op === "array-contains" /* ARRAY_CONTAINS */ ||
5628
- fieldFilter.op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
5629
- hasArraySegment = true;
5630
- }
5631
- else {
5632
- fields = fields.add(fieldFilter.field);
5633
- }
5634
- }
5635
- for (const orderBy of target.orderBy) {
5636
- // __name__ is not an explicit segment of any index, so we don't need to
5637
- // count it.
5638
- if (!orderBy.field.isKeyField()) {
5639
- fields = fields.add(orderBy.field);
5640
- }
5624
+ function fieldFilterEquals(f1, f2) {
5625
+ return (f2 instanceof FieldFilter &&
5626
+ f1.op === f2.op &&
5627
+ f1.field.isEqual(f2.field) &&
5628
+ valueEquals(f1.value, f2.value));
5629
+ }
5630
+ function compositeFilterEquals(f1, f2) {
5631
+ if (f2 instanceof CompositeFilter &&
5632
+ f1.op === f2.op &&
5633
+ f1.filters.length === f2.filters.length) {
5634
+ const subFiltersMatch = f1.filters.reduce((result, f1Filter, index) => result && filterEquals(f1Filter, f2.filters[index]), true);
5635
+ return subFiltersMatch;
5641
5636
  }
5642
- return fields.size + (hasArraySegment ? 1 : 0);
5637
+ return false;
5643
5638
  }
5644
- class Filter {
5639
+ /**
5640
+ * Returns a new composite filter that contains all filter from
5641
+ * `compositeFilter` plus all the given filters in `otherFilters`.
5642
+ */
5643
+ function compositeFilterWithAddedFilters(compositeFilter, otherFilters) {
5644
+ const mergedFilters = compositeFilter.filters.concat(otherFilters);
5645
+ return CompositeFilter.create(mergedFilters, compositeFilter.op);
5645
5646
  }
5646
- class FieldFilter extends Filter {
5647
- constructor(field, op, value) {
5648
- super();
5649
- this.field = field;
5650
- this.op = op;
5651
- this.value = value;
5652
- }
5653
- /**
5654
- * Creates a filter based on the provided arguments.
5655
- */
5656
- static create(field, op, value) {
5657
- if (field.isKeyField()) {
5658
- if (op === "in" /* IN */ || op === "not-in" /* NOT_IN */) {
5659
- return this.createKeyFieldInFilter(field, op, value);
5660
- }
5661
- else {
5662
- return new KeyFieldFilter(field, op, value);
5663
- }
5664
- }
5665
- else if (op === "array-contains" /* ARRAY_CONTAINS */) {
5666
- return new ArrayContainsFilter(field, value);
5667
- }
5668
- else if (op === "in" /* IN */) {
5669
- return new InFilter(field, value);
5670
- }
5671
- else if (op === "not-in" /* NOT_IN */) {
5672
- return new NotInFilter(field, value);
5673
- }
5674
- else if (op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
5675
- return new ArrayContainsAnyFilter(field, value);
5676
- }
5677
- else {
5678
- return new FieldFilter(field, op, value);
5679
- }
5680
- }
5681
- static createKeyFieldInFilter(field, op, value) {
5682
- return op === "in" /* IN */
5683
- ? new KeyFieldInFilter(field, value)
5684
- : new KeyFieldNotInFilter(field, value);
5685
- }
5686
- matches(doc) {
5687
- const other = doc.data.field(this.field);
5688
- // Types do not have to match in NOT_EQUAL filters.
5689
- if (this.op === "!=" /* NOT_EQUAL */) {
5690
- return (other !== null &&
5691
- this.matchesComparison(valueCompare(other, this.value)));
5692
- }
5693
- // Only compare types with matching backend order (such as double and int).
5694
- return (other !== null &&
5695
- typeOrder(this.value) === typeOrder(other) &&
5696
- this.matchesComparison(valueCompare(other, this.value)));
5647
+ /** Returns a debug description for `filter`. */
5648
+ function stringifyFilter(filter) {
5649
+ if (filter instanceof FieldFilter) {
5650
+ return stringifyFieldFilter(filter);
5697
5651
  }
5698
- matchesComparison(comparison) {
5699
- switch (this.op) {
5700
- case "<" /* LESS_THAN */:
5701
- return comparison < 0;
5702
- case "<=" /* LESS_THAN_OR_EQUAL */:
5703
- return comparison <= 0;
5704
- case "==" /* EQUAL */:
5705
- return comparison === 0;
5706
- case "!=" /* NOT_EQUAL */:
5707
- return comparison !== 0;
5708
- case ">" /* GREATER_THAN */:
5709
- return comparison > 0;
5710
- case ">=" /* GREATER_THAN_OR_EQUAL */:
5711
- return comparison >= 0;
5712
- default:
5713
- return fail();
5714
- }
5652
+ else if (filter instanceof CompositeFilter) {
5653
+ return stringifyCompositeFilter(filter);
5715
5654
  }
5716
- isInequality() {
5717
- return ([
5718
- "<" /* LESS_THAN */,
5719
- "<=" /* LESS_THAN_OR_EQUAL */,
5720
- ">" /* GREATER_THAN */,
5721
- ">=" /* GREATER_THAN_OR_EQUAL */,
5722
- "!=" /* NOT_EQUAL */,
5723
- "not-in" /* NOT_IN */
5724
- ].indexOf(this.op) >= 0);
5655
+ else {
5656
+ return 'Filter';
5725
5657
  }
5726
5658
  }
5727
- function canonifyFilter(filter) {
5728
- // TODO(b/29183165): Technically, this won't be unique if two values have
5729
- // the same description, such as the int 3 and the string "3". So we should
5730
- // add the types in here somehow, too.
5731
- return (filter.field.canonicalString() +
5732
- filter.op.toString() +
5733
- canonicalId(filter.value));
5734
- }
5735
- function filterEquals(f1, f2) {
5736
- return (f1.op === f2.op &&
5737
- f1.field.isEqual(f2.field) &&
5738
- valueEquals(f1.value, f2.value));
5659
+ function stringifyCompositeFilter(filter) {
5660
+ return (filter.op.toString() +
5661
+ ` {` +
5662
+ filter.getFilters().map(stringifyFilter).join(' ,') +
5663
+ '}');
5739
5664
  }
5740
- /** Returns a debug description for `filter`. */
5741
- function stringifyFilter(filter) {
5665
+ function stringifyFieldFilter(filter) {
5742
5666
  return `${filter.field.canonicalString()} ${filter.op} ${canonicalId(filter.value)}`;
5743
5667
  }
5744
5668
  /** Filter that matches on key fields (i.e. '__name__'). */
@@ -5755,8 +5679,8 @@ class KeyFieldFilter extends FieldFilter {
5755
5679
  /** Filter that matches on key fields within an array. */
5756
5680
  class KeyFieldInFilter extends FieldFilter {
5757
5681
  constructor(field, value) {
5758
- super(field, "in" /* IN */, value);
5759
- this.keys = extractDocumentKeysFromArrayValue("in" /* IN */, value);
5682
+ super(field, "in" /* Operator.IN */, value);
5683
+ this.keys = extractDocumentKeysFromArrayValue("in" /* Operator.IN */, value);
5760
5684
  }
5761
5685
  matches(doc) {
5762
5686
  return this.keys.some(key => key.isEqual(doc.key));
@@ -5765,8 +5689,8 @@ class KeyFieldInFilter extends FieldFilter {
5765
5689
  /** Filter that matches on key fields not present within an array. */
5766
5690
  class KeyFieldNotInFilter extends FieldFilter {
5767
5691
  constructor(field, value) {
5768
- super(field, "not-in" /* NOT_IN */, value);
5769
- this.keys = extractDocumentKeysFromArrayValue("not-in" /* NOT_IN */, value);
5692
+ super(field, "not-in" /* Operator.NOT_IN */, value);
5693
+ this.keys = extractDocumentKeysFromArrayValue("not-in" /* Operator.NOT_IN */, value);
5770
5694
  }
5771
5695
  matches(doc) {
5772
5696
  return !this.keys.some(key => key.isEqual(doc.key));
@@ -5781,7 +5705,7 @@ function extractDocumentKeysFromArrayValue(op, value) {
5781
5705
  /** A Filter that implements the array-contains operator. */
5782
5706
  class ArrayContainsFilter extends FieldFilter {
5783
5707
  constructor(field, value) {
5784
- super(field, "array-contains" /* ARRAY_CONTAINS */, value);
5708
+ super(field, "array-contains" /* Operator.ARRAY_CONTAINS */, value);
5785
5709
  }
5786
5710
  matches(doc) {
5787
5711
  const other = doc.data.field(this.field);
@@ -5791,7 +5715,7 @@ class ArrayContainsFilter extends FieldFilter {
5791
5715
  /** A Filter that implements the IN operator. */
5792
5716
  class InFilter extends FieldFilter {
5793
5717
  constructor(field, value) {
5794
- super(field, "in" /* IN */, value);
5718
+ super(field, "in" /* Operator.IN */, value);
5795
5719
  }
5796
5720
  matches(doc) {
5797
5721
  const other = doc.data.field(this.field);
@@ -5801,7 +5725,7 @@ class InFilter extends FieldFilter {
5801
5725
  /** A Filter that implements the not-in operator. */
5802
5726
  class NotInFilter extends FieldFilter {
5803
5727
  constructor(field, value) {
5804
- super(field, "not-in" /* NOT_IN */, value);
5728
+ super(field, "not-in" /* Operator.NOT_IN */, value);
5805
5729
  }
5806
5730
  matches(doc) {
5807
5731
  if (arrayValueContains(this.value.arrayValue, { nullValue: 'NULL_VALUE' })) {
@@ -5814,7 +5738,7 @@ class NotInFilter extends FieldFilter {
5814
5738
  /** A Filter that implements the array-contains-any operator. */
5815
5739
  class ArrayContainsAnyFilter extends FieldFilter {
5816
5740
  constructor(field, value) {
5817
- super(field, "array-contains-any" /* ARRAY_CONTAINS_ANY */, value);
5741
+ super(field, "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */, value);
5818
5742
  }
5819
5743
  matches(doc) {
5820
5744
  const other = doc.data.field(this.field);
@@ -5823,32 +5747,29 @@ class ArrayContainsAnyFilter extends FieldFilter {
5823
5747
  }
5824
5748
  return other.arrayValue.values.some(val => arrayValueContains(this.value.arrayValue, val));
5825
5749
  }
5826
- }
5827
- /**
5828
- * Represents a bound of a query.
5750
+ }
5751
+
5752
+ /**
5753
+ * @license
5754
+ * Copyright 2022 Google LLC
5829
5755
  *
5830
- * The bound is specified with the given components representing a position and
5831
- * whether it's just before or just after the position (relative to whatever the
5832
- * query order is).
5756
+ * Licensed under the Apache License, Version 2.0 (the "License");
5757
+ * you may not use this file except in compliance with the License.
5758
+ * You may obtain a copy of the License at
5833
5759
  *
5834
- * The position represents a logical index position for a query. It's a prefix
5835
- * of values for the (potentially implicit) order by clauses of a query.
5760
+ * http://www.apache.org/licenses/LICENSE-2.0
5836
5761
  *
5837
- * Bound provides a function to determine whether a document comes before or
5838
- * after a bound. This is influenced by whether the position is just before or
5839
- * just after the provided values.
5762
+ * Unless required by applicable law or agreed to in writing, software
5763
+ * distributed under the License is distributed on an "AS IS" BASIS,
5764
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5765
+ * See the License for the specific language governing permissions and
5766
+ * limitations under the License.
5840
5767
  */
5841
- class Bound {
5842
- constructor(position, inclusive) {
5843
- this.position = position;
5844
- this.inclusive = inclusive;
5845
- }
5846
- }
5847
5768
  /**
5848
5769
  * An ordering on a field, in some Direction. Direction defaults to ASCENDING.
5849
5770
  */
5850
5771
  class OrderBy {
5851
- constructor(field, dir = "asc" /* ASCENDING */) {
5772
+ constructor(field, dir = "asc" /* Direction.ASCENDING */) {
5852
5773
  this.field = field;
5853
5774
  this.dir = dir;
5854
5775
  }
@@ -5862,63 +5783,375 @@ function stringifyOrderBy(orderBy) {
5862
5783
  }
5863
5784
  function orderByEquals(left, right) {
5864
5785
  return left.dir === right.dir && left.field.isEqual(right.field);
5786
+ }
5787
+
5788
+ /**
5789
+ * @license
5790
+ * Copyright 2019 Google LLC
5791
+ *
5792
+ * Licensed under the Apache License, Version 2.0 (the "License");
5793
+ * you may not use this file except in compliance with the License.
5794
+ * You may obtain a copy of the License at
5795
+ *
5796
+ * http://www.apache.org/licenses/LICENSE-2.0
5797
+ *
5798
+ * Unless required by applicable law or agreed to in writing, software
5799
+ * distributed under the License is distributed on an "AS IS" BASIS,
5800
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5801
+ * See the License for the specific language governing permissions and
5802
+ * limitations under the License.
5803
+ */
5804
+ // Visible for testing
5805
+ class TargetImpl {
5806
+ constructor(path, collectionGroup = null, orderBy = [], filters = [], limit = null, startAt = null, endAt = null) {
5807
+ this.path = path;
5808
+ this.collectionGroup = collectionGroup;
5809
+ this.orderBy = orderBy;
5810
+ this.filters = filters;
5811
+ this.limit = limit;
5812
+ this.startAt = startAt;
5813
+ this.endAt = endAt;
5814
+ this.memoizedCanonicalId = null;
5815
+ }
5865
5816
  }
5866
- function boundCompareToDocument(bound, orderBy, doc) {
5867
- let comparison = 0;
5868
- for (let i = 0; i < bound.position.length; i++) {
5869
- const orderByComponent = orderBy[i];
5870
- const component = bound.position[i];
5871
- if (orderByComponent.field.isKeyField()) {
5872
- comparison = DocumentKey.comparator(DocumentKey.fromName(component.referenceValue), doc.key);
5817
+ /**
5818
+ * Initializes a Target with a path and optional additional query constraints.
5819
+ * Path must currently be empty if this is a collection group query.
5820
+ *
5821
+ * NOTE: you should always construct `Target` from `Query.toTarget` instead of
5822
+ * using this factory method, because `Query` provides an implicit `orderBy`
5823
+ * property.
5824
+ */
5825
+ function newTarget(path, collectionGroup = null, orderBy = [], filters = [], limit = null, startAt = null, endAt = null) {
5826
+ return new TargetImpl(path, collectionGroup, orderBy, filters, limit, startAt, endAt);
5827
+ }
5828
+ function canonifyTarget(target) {
5829
+ const targetImpl = debugCast(target);
5830
+ if (targetImpl.memoizedCanonicalId === null) {
5831
+ let str = targetImpl.path.canonicalString();
5832
+ if (targetImpl.collectionGroup !== null) {
5833
+ str += '|cg:' + targetImpl.collectionGroup;
5873
5834
  }
5874
- else {
5875
- const docValue = doc.data.field(orderByComponent.field);
5876
- comparison = valueCompare(component, docValue);
5835
+ str += '|f:';
5836
+ str += targetImpl.filters.map(f => canonifyFilter(f)).join(',');
5837
+ str += '|ob:';
5838
+ str += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(',');
5839
+ if (!isNullOrUndefined(targetImpl.limit)) {
5840
+ str += '|l:';
5841
+ str += targetImpl.limit;
5877
5842
  }
5878
- if (orderByComponent.dir === "desc" /* DESCENDING */) {
5879
- comparison = comparison * -1;
5843
+ if (targetImpl.startAt) {
5844
+ str += '|lb:';
5845
+ str += targetImpl.startAt.inclusive ? 'b:' : 'a:';
5846
+ str += targetImpl.startAt.position.map(p => canonicalId(p)).join(',');
5880
5847
  }
5881
- if (comparison !== 0) {
5882
- break;
5848
+ if (targetImpl.endAt) {
5849
+ str += '|ub:';
5850
+ str += targetImpl.endAt.inclusive ? 'a:' : 'b:';
5851
+ str += targetImpl.endAt.position.map(p => canonicalId(p)).join(',');
5883
5852
  }
5853
+ targetImpl.memoizedCanonicalId = str;
5884
5854
  }
5885
- return comparison;
5855
+ return targetImpl.memoizedCanonicalId;
5856
+ }
5857
+ function stringifyTarget(target) {
5858
+ let str = target.path.canonicalString();
5859
+ if (target.collectionGroup !== null) {
5860
+ str += ' collectionGroup=' + target.collectionGroup;
5861
+ }
5862
+ if (target.filters.length > 0) {
5863
+ str += `, filters: [${target.filters
5864
+ .map(f => stringifyFilter(f))
5865
+ .join(', ')}]`;
5866
+ }
5867
+ if (!isNullOrUndefined(target.limit)) {
5868
+ str += ', limit: ' + target.limit;
5869
+ }
5870
+ if (target.orderBy.length > 0) {
5871
+ str += `, orderBy: [${target.orderBy
5872
+ .map(o => stringifyOrderBy(o))
5873
+ .join(', ')}]`;
5874
+ }
5875
+ if (target.startAt) {
5876
+ str += ', startAt: ';
5877
+ str += target.startAt.inclusive ? 'b:' : 'a:';
5878
+ str += target.startAt.position.map(p => canonicalId(p)).join(',');
5879
+ }
5880
+ if (target.endAt) {
5881
+ str += ', endAt: ';
5882
+ str += target.endAt.inclusive ? 'a:' : 'b:';
5883
+ str += target.endAt.position.map(p => canonicalId(p)).join(',');
5884
+ }
5885
+ return `Target(${str})`;
5886
+ }
5887
+ function targetEquals(left, right) {
5888
+ if (left.limit !== right.limit) {
5889
+ return false;
5890
+ }
5891
+ if (left.orderBy.length !== right.orderBy.length) {
5892
+ return false;
5893
+ }
5894
+ for (let i = 0; i < left.orderBy.length; i++) {
5895
+ if (!orderByEquals(left.orderBy[i], right.orderBy[i])) {
5896
+ return false;
5897
+ }
5898
+ }
5899
+ if (left.filters.length !== right.filters.length) {
5900
+ return false;
5901
+ }
5902
+ for (let i = 0; i < left.filters.length; i++) {
5903
+ if (!filterEquals(left.filters[i], right.filters[i])) {
5904
+ return false;
5905
+ }
5906
+ }
5907
+ if (left.collectionGroup !== right.collectionGroup) {
5908
+ return false;
5909
+ }
5910
+ if (!left.path.isEqual(right.path)) {
5911
+ return false;
5912
+ }
5913
+ if (!boundEquals(left.startAt, right.startAt)) {
5914
+ return false;
5915
+ }
5916
+ return boundEquals(left.endAt, right.endAt);
5917
+ }
5918
+ function targetIsDocumentTarget(target) {
5919
+ return (DocumentKey.isDocumentKey(target.path) &&
5920
+ target.collectionGroup === null &&
5921
+ target.filters.length === 0);
5922
+ }
5923
+ /** Returns the field filters that target the given field path. */
5924
+ function targetGetFieldFiltersForPath(target, path) {
5925
+ return target.filters.filter(f => f instanceof FieldFilter && f.field.isEqual(path));
5886
5926
  }
5887
5927
  /**
5888
- * Returns true if a document sorts after a bound using the provided sort
5889
- * order.
5928
+ * Returns the values that are used in ARRAY_CONTAINS or ARRAY_CONTAINS_ANY
5929
+ * filters. Returns `null` if there are no such filters.
5890
5930
  */
5891
- function boundSortsAfterDocument(bound, orderBy, doc) {
5892
- const comparison = boundCompareToDocument(bound, orderBy, doc);
5893
- return bound.inclusive ? comparison >= 0 : comparison > 0;
5931
+ function targetGetArrayValues(target, fieldIndex) {
5932
+ const segment = fieldIndexGetArraySegment(fieldIndex);
5933
+ if (segment === undefined) {
5934
+ return null;
5935
+ }
5936
+ for (const fieldFilter of targetGetFieldFiltersForPath(target, segment.fieldPath)) {
5937
+ switch (fieldFilter.op) {
5938
+ case "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */:
5939
+ return fieldFilter.value.arrayValue.values || [];
5940
+ case "array-contains" /* Operator.ARRAY_CONTAINS */:
5941
+ return [fieldFilter.value];
5942
+ // Remaining filters are not array filters.
5943
+ }
5944
+ }
5945
+ return null;
5894
5946
  }
5895
5947
  /**
5896
- * Returns true if a document sorts before a bound using the provided sort
5897
- * order.
5948
+ * Returns the list of values that are used in != or NOT_IN filters. Returns
5949
+ * `null` if there are no such filters.
5898
5950
  */
5899
- function boundSortsBeforeDocument(bound, orderBy, doc) {
5900
- const comparison = boundCompareToDocument(bound, orderBy, doc);
5901
- return bound.inclusive ? comparison <= 0 : comparison < 0;
5951
+ function targetGetNotInValues(target, fieldIndex) {
5952
+ const values = new Map();
5953
+ for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
5954
+ for (const fieldFilter of targetGetFieldFiltersForPath(target, segment.fieldPath)) {
5955
+ switch (fieldFilter.op) {
5956
+ case "==" /* Operator.EQUAL */:
5957
+ case "in" /* Operator.IN */:
5958
+ // Encode equality prefix, which is encoded in the index value before
5959
+ // the inequality (e.g. `a == 'a' && b != 'b'` is encoded to
5960
+ // `value != 'ab'`).
5961
+ values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
5962
+ break;
5963
+ case "not-in" /* Operator.NOT_IN */:
5964
+ case "!=" /* Operator.NOT_EQUAL */:
5965
+ // NotIn/NotEqual is always a suffix. There cannot be any remaining
5966
+ // segments and hence we can return early here.
5967
+ values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
5968
+ return Array.from(values.values());
5969
+ // Remaining filters cannot be used as notIn bounds.
5970
+ }
5971
+ }
5972
+ }
5973
+ return null;
5902
5974
  }
5903
- function boundEquals(left, right) {
5904
- if (left === null) {
5905
- return right === null;
5975
+ /**
5976
+ * Returns a lower bound of field values that can be used as a starting point to
5977
+ * scan the index defined by `fieldIndex`. Returns `MIN_VALUE` if no lower bound
5978
+ * exists.
5979
+ */
5980
+ function targetGetLowerBound(target, fieldIndex) {
5981
+ const values = [];
5982
+ let inclusive = true;
5983
+ // For each segment, retrieve a lower bound if there is a suitable filter or
5984
+ // startAt.
5985
+ for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
5986
+ const segmentBound = segment.kind === 0 /* IndexKind.ASCENDING */
5987
+ ? targetGetAscendingBound(target, segment.fieldPath, target.startAt)
5988
+ : targetGetDescendingBound(target, segment.fieldPath, target.startAt);
5989
+ values.push(segmentBound.value);
5990
+ inclusive && (inclusive = segmentBound.inclusive);
5906
5991
  }
5907
- else if (right === null) {
5908
- return false;
5992
+ return new Bound(values, inclusive);
5993
+ }
5994
+ /**
5995
+ * Returns an upper bound of field values that can be used as an ending point
5996
+ * when scanning the index defined by `fieldIndex`. Returns `MAX_VALUE` if no
5997
+ * upper bound exists.
5998
+ */
5999
+ function targetGetUpperBound(target, fieldIndex) {
6000
+ const values = [];
6001
+ let inclusive = true;
6002
+ // For each segment, retrieve an upper bound if there is a suitable filter or
6003
+ // endAt.
6004
+ for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
6005
+ const segmentBound = segment.kind === 0 /* IndexKind.ASCENDING */
6006
+ ? targetGetDescendingBound(target, segment.fieldPath, target.endAt)
6007
+ : targetGetAscendingBound(target, segment.fieldPath, target.endAt);
6008
+ values.push(segmentBound.value);
6009
+ inclusive && (inclusive = segmentBound.inclusive);
6010
+ }
6011
+ return new Bound(values, inclusive);
6012
+ }
6013
+ /**
6014
+ * Returns the value to use as the lower bound for ascending index segment at
6015
+ * the provided `fieldPath` (or the upper bound for an descending segment).
6016
+ */
6017
+ function targetGetAscendingBound(target, fieldPath, bound) {
6018
+ let value = MIN_VALUE;
6019
+ let inclusive = true;
6020
+ // Process all filters to find a value for the current field segment
6021
+ for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) {
6022
+ let filterValue = MIN_VALUE;
6023
+ let filterInclusive = true;
6024
+ switch (fieldFilter.op) {
6025
+ case "<" /* Operator.LESS_THAN */:
6026
+ case "<=" /* Operator.LESS_THAN_OR_EQUAL */:
6027
+ filterValue = valuesGetLowerBound(fieldFilter.value);
6028
+ break;
6029
+ case "==" /* Operator.EQUAL */:
6030
+ case "in" /* Operator.IN */:
6031
+ case ">=" /* Operator.GREATER_THAN_OR_EQUAL */:
6032
+ filterValue = fieldFilter.value;
6033
+ break;
6034
+ case ">" /* Operator.GREATER_THAN */:
6035
+ filterValue = fieldFilter.value;
6036
+ filterInclusive = false;
6037
+ break;
6038
+ case "!=" /* Operator.NOT_EQUAL */:
6039
+ case "not-in" /* Operator.NOT_IN */:
6040
+ filterValue = MIN_VALUE;
6041
+ break;
6042
+ // Remaining filters cannot be used as lower bounds.
6043
+ }
6044
+ if (lowerBoundCompare({ value, inclusive }, { value: filterValue, inclusive: filterInclusive }) < 0) {
6045
+ value = filterValue;
6046
+ inclusive = filterInclusive;
6047
+ }
6048
+ }
6049
+ // If there is an additional bound, compare the values against the existing
6050
+ // range to see if we can narrow the scope.
6051
+ if (bound !== null) {
6052
+ for (let i = 0; i < target.orderBy.length; ++i) {
6053
+ const orderBy = target.orderBy[i];
6054
+ if (orderBy.field.isEqual(fieldPath)) {
6055
+ const cursorValue = bound.position[i];
6056
+ if (lowerBoundCompare({ value, inclusive }, { value: cursorValue, inclusive: bound.inclusive }) < 0) {
6057
+ value = cursorValue;
6058
+ inclusive = bound.inclusive;
6059
+ }
6060
+ break;
6061
+ }
6062
+ }
6063
+ }
6064
+ return { value, inclusive };
6065
+ }
6066
+ /**
6067
+ * Returns the value to use as the upper bound for ascending index segment at
6068
+ * the provided `fieldPath` (or the lower bound for a descending segment).
6069
+ */
6070
+ function targetGetDescendingBound(target, fieldPath, bound) {
6071
+ let value = MAX_VALUE;
6072
+ let inclusive = true;
6073
+ // Process all filters to find a value for the current field segment
6074
+ for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) {
6075
+ let filterValue = MAX_VALUE;
6076
+ let filterInclusive = true;
6077
+ switch (fieldFilter.op) {
6078
+ case ">=" /* Operator.GREATER_THAN_OR_EQUAL */:
6079
+ case ">" /* Operator.GREATER_THAN */:
6080
+ filterValue = valuesGetUpperBound(fieldFilter.value);
6081
+ filterInclusive = false;
6082
+ break;
6083
+ case "==" /* Operator.EQUAL */:
6084
+ case "in" /* Operator.IN */:
6085
+ case "<=" /* Operator.LESS_THAN_OR_EQUAL */:
6086
+ filterValue = fieldFilter.value;
6087
+ break;
6088
+ case "<" /* Operator.LESS_THAN */:
6089
+ filterValue = fieldFilter.value;
6090
+ filterInclusive = false;
6091
+ break;
6092
+ case "!=" /* Operator.NOT_EQUAL */:
6093
+ case "not-in" /* Operator.NOT_IN */:
6094
+ filterValue = MAX_VALUE;
6095
+ break;
6096
+ // Remaining filters cannot be used as upper bounds.
6097
+ }
6098
+ if (upperBoundCompare({ value, inclusive }, { value: filterValue, inclusive: filterInclusive }) > 0) {
6099
+ value = filterValue;
6100
+ inclusive = filterInclusive;
6101
+ }
6102
+ }
6103
+ // If there is an additional bound, compare the values against the existing
6104
+ // range to see if we can narrow the scope.
6105
+ if (bound !== null) {
6106
+ for (let i = 0; i < target.orderBy.length; ++i) {
6107
+ const orderBy = target.orderBy[i];
6108
+ if (orderBy.field.isEqual(fieldPath)) {
6109
+ const cursorValue = bound.position[i];
6110
+ if (upperBoundCompare({ value, inclusive }, { value: cursorValue, inclusive: bound.inclusive }) > 0) {
6111
+ value = cursorValue;
6112
+ inclusive = bound.inclusive;
6113
+ }
6114
+ break;
6115
+ }
6116
+ }
5909
6117
  }
5910
- if (left.inclusive !== right.inclusive ||
5911
- left.position.length !== right.position.length) {
5912
- return false;
6118
+ return { value, inclusive };
6119
+ }
6120
+ /** Returns the number of segments of a perfect index for this target. */
6121
+ function targetGetSegmentCount(target) {
6122
+ let fields = new SortedSet(FieldPath$1.comparator);
6123
+ let hasArraySegment = false;
6124
+ for (const filter of target.filters) {
6125
+ for (const subFilter of filter.getFlattenedFilters()) {
6126
+ // __name__ is not an explicit segment of any index, so we don't need to
6127
+ // count it.
6128
+ if (subFilter.field.isKeyField()) {
6129
+ continue;
6130
+ }
6131
+ // ARRAY_CONTAINS or ARRAY_CONTAINS_ANY filters must be counted separately.
6132
+ // For instance, it is possible to have an index for "a ARRAY a ASC". Even
6133
+ // though these are on the same field, they should be counted as two
6134
+ // separate segments in an index.
6135
+ if (subFilter.op === "array-contains" /* Operator.ARRAY_CONTAINS */ ||
6136
+ subFilter.op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
6137
+ hasArraySegment = true;
6138
+ }
6139
+ else {
6140
+ fields = fields.add(subFilter.field);
6141
+ }
6142
+ }
5913
6143
  }
5914
- for (let i = 0; i < left.position.length; i++) {
5915
- const leftPosition = left.position[i];
5916
- const rightPosition = right.position[i];
5917
- if (!valueEquals(leftPosition, rightPosition)) {
5918
- return false;
6144
+ for (const orderBy of target.orderBy) {
6145
+ // __name__ is not an explicit segment of any index, so we don't need to
6146
+ // count it.
6147
+ if (!orderBy.field.isKeyField()) {
6148
+ fields = fields.add(orderBy.field);
5919
6149
  }
5920
6150
  }
5921
- return true;
6151
+ return fields.size + (hasArraySegment ? 1 : 0);
6152
+ }
6153
+ function targetHasLimit(target) {
6154
+ return target.limit !== null;
5922
6155
  }
5923
6156
 
5924
6157
  /**
@@ -5949,7 +6182,7 @@ class QueryImpl {
5949
6182
  * Initializes a Query with a path and optional additional query constraints.
5950
6183
  * Path must currently be empty if this is a collection group query.
5951
6184
  */
5952
- constructor(path, collectionGroup = null, explicitOrderBy = [], filters = [], limit = null, limitType = "F" /* First */, startAt = null, endAt = null) {
6185
+ constructor(path, collectionGroup = null, explicitOrderBy = [], filters = [], limit = null, limitType = "F" /* LimitType.First */, startAt = null, endAt = null) {
5953
6186
  this.path = path;
5954
6187
  this.collectionGroup = collectionGroup;
5955
6188
  this.explicitOrderBy = explicitOrderBy;
@@ -6003,20 +6236,9 @@ function getFirstOrderByField(query) {
6003
6236
  }
6004
6237
  function getInequalityFilterField(query) {
6005
6238
  for (const filter of query.filters) {
6006
- if (filter.isInequality()) {
6007
- return filter.field;
6008
- }
6009
- }
6010
- return null;
6011
- }
6012
- /**
6013
- * Checks if any of the provided Operators are included in the query and
6014
- * returns the first one that is, or null if none are.
6015
- */
6016
- function findFilterOperator(query, operators) {
6017
- for (const filter of query.filters) {
6018
- if (operators.indexOf(filter.op) >= 0) {
6019
- return filter.op;
6239
+ const result = filter.getFirstInequalityField();
6240
+ if (result !== null) {
6241
+ return result;
6020
6242
  }
6021
6243
  }
6022
6244
  return null;
@@ -6062,7 +6284,7 @@ function queryOrderBy(query) {
6062
6284
  if (!inequalityField.isKeyField()) {
6063
6285
  queryImpl.memoizedOrderBy.push(new OrderBy(inequalityField));
6064
6286
  }
6065
- queryImpl.memoizedOrderBy.push(new OrderBy(FieldPath$1.keyField(), "asc" /* ASCENDING */));
6287
+ queryImpl.memoizedOrderBy.push(new OrderBy(FieldPath$1.keyField(), "asc" /* Direction.ASCENDING */));
6066
6288
  }
6067
6289
  else {
6068
6290
  let foundKeyOrdering = false;
@@ -6078,7 +6300,7 @@ function queryOrderBy(query) {
6078
6300
  const lastDirection = queryImpl.explicitOrderBy.length > 0
6079
6301
  ? queryImpl.explicitOrderBy[queryImpl.explicitOrderBy.length - 1]
6080
6302
  .dir
6081
- : "asc" /* ASCENDING */;
6303
+ : "asc" /* Direction.ASCENDING */;
6082
6304
  queryImpl.memoizedOrderBy.push(new OrderBy(FieldPath$1.keyField(), lastDirection));
6083
6305
  }
6084
6306
  }
@@ -6091,16 +6313,16 @@ function queryOrderBy(query) {
6091
6313
  function queryToTarget(query) {
6092
6314
  const queryImpl = debugCast(query);
6093
6315
  if (!queryImpl.memoizedTarget) {
6094
- if (queryImpl.limitType === "F" /* First */) {
6316
+ if (queryImpl.limitType === "F" /* LimitType.First */) {
6095
6317
  queryImpl.memoizedTarget = newTarget(queryImpl.path, queryImpl.collectionGroup, queryOrderBy(queryImpl), queryImpl.filters, queryImpl.limit, queryImpl.startAt, queryImpl.endAt);
6096
6318
  }
6097
6319
  else {
6098
6320
  // Flip the orderBy directions since we want the last results
6099
6321
  const orderBys = [];
6100
6322
  for (const orderBy of queryOrderBy(queryImpl)) {
6101
- const dir = orderBy.dir === "desc" /* DESCENDING */
6102
- ? "asc" /* ASCENDING */
6103
- : "desc" /* DESCENDING */;
6323
+ const dir = orderBy.dir === "desc" /* Direction.DESCENDING */
6324
+ ? "asc" /* Direction.ASCENDING */
6325
+ : "desc" /* Direction.DESCENDING */;
6104
6326
  orderBys.push(new OrderBy(orderBy.field, dir));
6105
6327
  }
6106
6328
  // We need to swap the cursors to match the now-flipped query ordering.
@@ -6117,6 +6339,8 @@ function queryToTarget(query) {
6117
6339
  return queryImpl.memoizedTarget;
6118
6340
  }
6119
6341
  function queryWithAddedFilter(query, filter) {
6342
+ filter.getFirstInequalityField();
6343
+ getInequalityFilterField(query);
6120
6344
  const newFilters = query.filters.concat([filter]);
6121
6345
  return new QueryImpl(query.path, query.collectionGroup, query.explicitOrderBy.slice(), newFilters, query.limit, query.limitType, query.startAt, query.endAt);
6122
6346
  }
@@ -6177,7 +6401,13 @@ function queryMatchesPathAndCollectionGroup(query, doc) {
6177
6401
  * in the results.
6178
6402
  */
6179
6403
  function queryMatchesOrderBy(query, doc) {
6180
- for (const orderBy of query.explicitOrderBy) {
6404
+ // We must use `queryOrderBy()` to get the list of all orderBys (both implicit and explicit).
6405
+ // Note that for OR queries, orderBy applies to all disjunction terms and implicit orderBys must
6406
+ // be taken into account. For example, the query "a > 1 || b==1" has an implicit "orderBy a" due
6407
+ // to the inequality, and is evaluated as "a > 1 orderBy a || b==1 orderBy a".
6408
+ // A document with content of {b:1} matches the filters, but does not match the orderBy because
6409
+ // it's missing the field 'a'.
6410
+ for (const orderBy of queryOrderBy(query)) {
6181
6411
  // order by key always matches
6182
6412
  if (!orderBy.field.isKeyField() && doc.data.field(orderBy.field) === null) {
6183
6413
  return false;
@@ -6239,9 +6469,9 @@ function compareDocs(orderBy, d1, d2) {
6239
6469
  ? DocumentKey.comparator(d1.key, d2.key)
6240
6470
  : compareDocumentsByField(orderBy.field, d1, d2);
6241
6471
  switch (orderBy.dir) {
6242
- case "asc" /* ASCENDING */:
6472
+ case "asc" /* Direction.ASCENDING */:
6243
6473
  return comparison;
6244
- case "desc" /* DESCENDING */:
6474
+ case "desc" /* Direction.DESCENDING */:
6245
6475
  return -1 * comparison;
6246
6476
  default:
6247
6477
  return fail();
@@ -6927,10 +7157,10 @@ function mutationEquals(left, right) {
6927
7157
  if (!fieldTransformsAreEqual(left.fieldTransforms, right.fieldTransforms)) {
6928
7158
  return false;
6929
7159
  }
6930
- if (left.type === 0 /* Set */) {
7160
+ if (left.type === 0 /* MutationType.Set */) {
6931
7161
  return left.value.isEqual(right.value);
6932
7162
  }
6933
- if (left.type === 1 /* Patch */) {
7163
+ if (left.type === 1 /* MutationType.Patch */) {
6934
7164
  return (left.data.isEqual(right.data) &&
6935
7165
  left.fieldMask.isEqual(right.fieldMask));
6936
7166
  }
@@ -6947,7 +7177,7 @@ class SetMutation extends Mutation {
6947
7177
  this.value = value;
6948
7178
  this.precondition = precondition;
6949
7179
  this.fieldTransforms = fieldTransforms;
6950
- this.type = 0 /* Set */;
7180
+ this.type = 0 /* MutationType.Set */;
6951
7181
  }
6952
7182
  getFieldMask() {
6953
7183
  return null;
@@ -6999,7 +7229,7 @@ class PatchMutation extends Mutation {
6999
7229
  this.fieldMask = fieldMask;
7000
7230
  this.precondition = precondition;
7001
7231
  this.fieldTransforms = fieldTransforms;
7002
- this.type = 1 /* Patch */;
7232
+ this.type = 1 /* MutationType.Patch */;
7003
7233
  }
7004
7234
  getFieldMask() {
7005
7235
  return this.fieldMask;
@@ -7101,7 +7331,7 @@ class DeleteMutation extends Mutation {
7101
7331
  super();
7102
7332
  this.key = key;
7103
7333
  this.precondition = precondition;
7104
- this.type = 2 /* Delete */;
7334
+ this.type = 2 /* MutationType.Delete */;
7105
7335
  this.fieldTransforms = [];
7106
7336
  }
7107
7337
  getFieldMask() {
@@ -7135,7 +7365,7 @@ class VerifyMutation extends Mutation {
7135
7365
  super();
7136
7366
  this.key = key;
7137
7367
  this.precondition = precondition;
7138
- this.type = 3 /* Verify */;
7368
+ this.type = 3 /* MutationType.Verify */;
7139
7369
  this.fieldTransforms = [];
7140
7370
  }
7141
7371
  getFieldMask() {
@@ -7764,13 +7994,13 @@ class TargetState {
7764
7994
  let removedDocuments = documentKeySet();
7765
7995
  this.documentChanges.forEach((key, changeType) => {
7766
7996
  switch (changeType) {
7767
- case 0 /* Added */:
7997
+ case 0 /* ChangeType.Added */:
7768
7998
  addedDocuments = addedDocuments.add(key);
7769
7999
  break;
7770
- case 2 /* Modified */:
8000
+ case 2 /* ChangeType.Modified */:
7771
8001
  modifiedDocuments = modifiedDocuments.add(key);
7772
8002
  break;
7773
- case 1 /* Removed */:
8003
+ case 1 /* ChangeType.Removed */:
7774
8004
  removedDocuments = removedDocuments.add(key);
7775
8005
  break;
7776
8006
  default:
@@ -7846,12 +8076,12 @@ class WatchChangeAggregator {
7846
8076
  this.forEachTarget(targetChange, targetId => {
7847
8077
  const targetState = this.ensureTargetState(targetId);
7848
8078
  switch (targetChange.state) {
7849
- case 0 /* NoChange */:
8079
+ case 0 /* WatchTargetChangeState.NoChange */:
7850
8080
  if (this.isActiveTarget(targetId)) {
7851
8081
  targetState.updateResumeToken(targetChange.resumeToken);
7852
8082
  }
7853
8083
  break;
7854
- case 1 /* Added */:
8084
+ case 1 /* WatchTargetChangeState.Added */:
7855
8085
  // We need to decrement the number of pending acks needed from watch
7856
8086
  // for this targetId.
7857
8087
  targetState.recordTargetResponse();
@@ -7863,7 +8093,7 @@ class WatchChangeAggregator {
7863
8093
  }
7864
8094
  targetState.updateResumeToken(targetChange.resumeToken);
7865
8095
  break;
7866
- case 2 /* Removed */:
8096
+ case 2 /* WatchTargetChangeState.Removed */:
7867
8097
  // We need to keep track of removed targets to we can post-filter and
7868
8098
  // remove any target changes.
7869
8099
  // We need to decrement the number of pending acks needed from watch
@@ -7873,13 +8103,13 @@ class WatchChangeAggregator {
7873
8103
  this.removeTarget(targetId);
7874
8104
  }
7875
8105
  break;
7876
- case 3 /* Current */:
8106
+ case 3 /* WatchTargetChangeState.Current */:
7877
8107
  if (this.isActiveTarget(targetId)) {
7878
8108
  targetState.markCurrent();
7879
8109
  targetState.updateResumeToken(targetChange.resumeToken);
7880
8110
  }
7881
8111
  break;
7882
- case 4 /* Reset */:
8112
+ case 4 /* WatchTargetChangeState.Reset */:
7883
8113
  if (this.isActiveTarget(targetId)) {
7884
8114
  // Reset the target and synthesizes removes for all existing
7885
8115
  // documents. The backend will re-add any documents that still
@@ -7989,7 +8219,7 @@ class WatchChangeAggregator {
7989
8219
  targets.forEachWhile(targetId => {
7990
8220
  const targetData = this.targetDataForActiveTarget(targetId);
7991
8221
  if (targetData &&
7992
- targetData.purpose !== 2 /* LimboResolution */) {
8222
+ targetData.purpose !== 2 /* TargetPurpose.LimboResolution */) {
7993
8223
  isOnlyLimboTarget = false;
7994
8224
  return false;
7995
8225
  }
@@ -8016,8 +8246,8 @@ class WatchChangeAggregator {
8016
8246
  return;
8017
8247
  }
8018
8248
  const changeType = this.targetContainsDocument(targetId, document.key)
8019
- ? 2 /* Modified */
8020
- : 0 /* Added */;
8249
+ ? 2 /* ChangeType.Modified */
8250
+ : 0 /* ChangeType.Added */;
8021
8251
  const targetState = this.ensureTargetState(targetId);
8022
8252
  targetState.addDocumentChange(document.key, changeType);
8023
8253
  this.pendingDocumentUpdates = this.pendingDocumentUpdates.insert(document.key, document);
@@ -8038,7 +8268,7 @@ class WatchChangeAggregator {
8038
8268
  }
8039
8269
  const targetState = this.ensureTargetState(targetId);
8040
8270
  if (this.targetContainsDocument(targetId, key)) {
8041
- targetState.addDocumentChange(key, 1 /* Removed */);
8271
+ targetState.addDocumentChange(key, 1 /* ChangeType.Removed */);
8042
8272
  }
8043
8273
  else {
8044
8274
  // The document may have entered and left the target before we raised a
@@ -8163,22 +8393,28 @@ function snapshotChangesMap() {
8163
8393
  */
8164
8394
  const DIRECTIONS = (() => {
8165
8395
  const dirs = {};
8166
- dirs["asc" /* ASCENDING */] = 'ASCENDING';
8167
- dirs["desc" /* DESCENDING */] = 'DESCENDING';
8396
+ dirs["asc" /* Direction.ASCENDING */] = 'ASCENDING';
8397
+ dirs["desc" /* Direction.DESCENDING */] = 'DESCENDING';
8168
8398
  return dirs;
8169
8399
  })();
8170
8400
  const OPERATORS = (() => {
8171
8401
  const ops = {};
8172
- ops["<" /* LESS_THAN */] = 'LESS_THAN';
8173
- ops["<=" /* LESS_THAN_OR_EQUAL */] = 'LESS_THAN_OR_EQUAL';
8174
- ops[">" /* GREATER_THAN */] = 'GREATER_THAN';
8175
- ops[">=" /* GREATER_THAN_OR_EQUAL */] = 'GREATER_THAN_OR_EQUAL';
8176
- ops["==" /* EQUAL */] = 'EQUAL';
8177
- ops["!=" /* NOT_EQUAL */] = 'NOT_EQUAL';
8178
- ops["array-contains" /* ARRAY_CONTAINS */] = 'ARRAY_CONTAINS';
8179
- ops["in" /* IN */] = 'IN';
8180
- ops["not-in" /* NOT_IN */] = 'NOT_IN';
8181
- ops["array-contains-any" /* ARRAY_CONTAINS_ANY */] = 'ARRAY_CONTAINS_ANY';
8402
+ ops["<" /* Operator.LESS_THAN */] = 'LESS_THAN';
8403
+ ops["<=" /* Operator.LESS_THAN_OR_EQUAL */] = 'LESS_THAN_OR_EQUAL';
8404
+ ops[">" /* Operator.GREATER_THAN */] = 'GREATER_THAN';
8405
+ ops[">=" /* Operator.GREATER_THAN_OR_EQUAL */] = 'GREATER_THAN_OR_EQUAL';
8406
+ ops["==" /* Operator.EQUAL */] = 'EQUAL';
8407
+ ops["!=" /* Operator.NOT_EQUAL */] = 'NOT_EQUAL';
8408
+ ops["array-contains" /* Operator.ARRAY_CONTAINS */] = 'ARRAY_CONTAINS';
8409
+ ops["in" /* Operator.IN */] = 'IN';
8410
+ ops["not-in" /* Operator.NOT_IN */] = 'NOT_IN';
8411
+ ops["array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */] = 'ARRAY_CONTAINS_ANY';
8412
+ return ops;
8413
+ })();
8414
+ const COMPOSITE_OPERATORS = (() => {
8415
+ const ops = {};
8416
+ ops["and" /* CompositeOperator.AND */] = 'AND';
8417
+ ops["or" /* CompositeOperator.OR */] = 'OR';
8182
8418
  return ops;
8183
8419
  })();
8184
8420
  function assertPresent(value, description) {
@@ -8372,14 +8608,21 @@ function toDocument(serializer, document) {
8372
8608
  return {
8373
8609
  name: toName(serializer, document.key),
8374
8610
  fields: document.data.value.mapValue.fields,
8375
- updateTime: toTimestamp(serializer, document.version.toTimestamp())
8611
+ updateTime: toTimestamp(serializer, document.version.toTimestamp()),
8612
+ createTime: toTimestamp(serializer, document.createTime.toTimestamp())
8376
8613
  };
8377
8614
  }
8378
8615
  function fromDocument(serializer, document, hasCommittedMutations) {
8379
8616
  const key = fromName(serializer, document.name);
8380
8617
  const version = fromVersion(document.updateTime);
8618
+ // If we read a document from persistence that is missing createTime, it's due
8619
+ // to older SDK versions not storing this information. In such cases, we'll
8620
+ // set the createTime to zero. This can be removed in the long term.
8621
+ const createTime = document.createTime
8622
+ ? fromVersion(document.createTime)
8623
+ : SnapshotVersion.min();
8381
8624
  const data = new ObjectValue({ mapValue: { fields: document.fields } });
8382
- const result = MutableDocument.newFoundDocument(key, version, data);
8625
+ const result = MutableDocument.newFoundDocument(key, version, createTime, data);
8383
8626
  if (hasCommittedMutations) {
8384
8627
  result.setHasCommittedMutations();
8385
8628
  }
@@ -8391,8 +8634,11 @@ function fromFound(serializer, doc) {
8391
8634
  assertPresent(doc.found.updateTime);
8392
8635
  const key = fromName(serializer, doc.found.name);
8393
8636
  const version = fromVersion(doc.found.updateTime);
8637
+ const createTime = doc.found.createTime
8638
+ ? fromVersion(doc.found.createTime)
8639
+ : SnapshotVersion.min();
8394
8640
  const data = new ObjectValue({ mapValue: { fields: doc.found.fields } });
8395
- return MutableDocument.newFoundDocument(key, version, data);
8641
+ return MutableDocument.newFoundDocument(key, version, createTime, data);
8396
8642
  }
8397
8643
  function fromMissing(serializer, result) {
8398
8644
  hardAssert(!!result.missing);
@@ -8431,10 +8677,13 @@ function fromWatchChange(serializer, change) {
8431
8677
  assertPresent(entityChange.document.updateTime);
8432
8678
  const key = fromName(serializer, entityChange.document.name);
8433
8679
  const version = fromVersion(entityChange.document.updateTime);
8680
+ const createTime = entityChange.document.createTime
8681
+ ? fromVersion(entityChange.document.createTime)
8682
+ : SnapshotVersion.min();
8434
8683
  const data = new ObjectValue({
8435
8684
  mapValue: { fields: entityChange.document.fields }
8436
8685
  });
8437
- const doc = MutableDocument.newFoundDocument(key, version, data);
8686
+ const doc = MutableDocument.newFoundDocument(key, version, createTime, data);
8438
8687
  const updatedTargetIds = entityChange.targetIds || [];
8439
8688
  const removedTargetIds = entityChange.removedTargetIds || [];
8440
8689
  watchChange = new DocumentWatchChange(updatedTargetIds, removedTargetIds, doc.key, doc);
@@ -8476,19 +8725,19 @@ function fromWatchChange(serializer, change) {
8476
8725
  }
8477
8726
  function fromWatchTargetChangeState(state) {
8478
8727
  if (state === 'NO_CHANGE') {
8479
- return 0 /* NoChange */;
8728
+ return 0 /* WatchTargetChangeState.NoChange */;
8480
8729
  }
8481
8730
  else if (state === 'ADD') {
8482
- return 1 /* Added */;
8731
+ return 1 /* WatchTargetChangeState.Added */;
8483
8732
  }
8484
8733
  else if (state === 'REMOVE') {
8485
- return 2 /* Removed */;
8734
+ return 2 /* WatchTargetChangeState.Removed */;
8486
8735
  }
8487
8736
  else if (state === 'CURRENT') {
8488
- return 3 /* Current */;
8737
+ return 3 /* WatchTargetChangeState.Current */;
8489
8738
  }
8490
8739
  else if (state === 'RESET') {
8491
- return 4 /* Reset */;
8740
+ return 4 /* WatchTargetChangeState.Reset */;
8492
8741
  }
8493
8742
  else {
8494
8743
  return fail();
@@ -8706,7 +8955,7 @@ function toQueryTarget(serializer, target) {
8706
8955
  result.parent = toQueryPath(serializer, path.popLast());
8707
8956
  result.structuredQuery.from = [{ collectionId: path.lastSegment() }];
8708
8957
  }
8709
- const where = toFilter(target.filters);
8958
+ const where = toFilters(target.filters);
8710
8959
  if (where) {
8711
8960
  result.structuredQuery.where = where;
8712
8961
  }
@@ -8758,7 +9007,7 @@ function convertQueryTargetToQuery(target) {
8758
9007
  }
8759
9008
  let filterBy = [];
8760
9009
  if (query.where) {
8761
- filterBy = fromFilter(query.where);
9010
+ filterBy = fromFilters(query.where);
8762
9011
  }
8763
9012
  let orderBy = [];
8764
9013
  if (query.orderBy) {
@@ -8776,7 +9025,7 @@ function convertQueryTargetToQuery(target) {
8776
9025
  if (query.endAt) {
8777
9026
  endAt = fromEndAtCursor(query.endAt);
8778
9027
  }
8779
- return newQuery(path, collectionGroup, orderBy, filterBy, limit, "F" /* First */, startAt, endAt);
9028
+ return newQuery(path, collectionGroup, orderBy, filterBy, limit, "F" /* LimitType.First */, startAt, endAt);
8780
9029
  }
8781
9030
  function fromQueryTarget(target) {
8782
9031
  return queryToTarget(convertQueryTargetToQuery(target));
@@ -8794,11 +9043,11 @@ function toListenRequestLabels(serializer, targetData) {
8794
9043
  }
8795
9044
  function toLabel(serializer, purpose) {
8796
9045
  switch (purpose) {
8797
- case 0 /* Listen */:
9046
+ case 0 /* TargetPurpose.Listen */:
8798
9047
  return null;
8799
- case 1 /* ExistenceFilterMismatch */:
9048
+ case 1 /* TargetPurpose.ExistenceFilterMismatch */:
8800
9049
  return 'existence-filter-mismatch';
8801
- case 2 /* LimboResolution */:
9050
+ case 2 /* TargetPurpose.LimboResolution */:
8802
9051
  return 'limbo-document';
8803
9052
  default:
8804
9053
  return fail();
@@ -8825,32 +9074,29 @@ function toTarget(serializer, targetData) {
8825
9074
  }
8826
9075
  return result;
8827
9076
  }
8828
- function toFilter(filters) {
9077
+ function toFilters(filters) {
8829
9078
  if (filters.length === 0) {
8830
9079
  return;
8831
9080
  }
8832
- const protos = filters.map(filter => {
8833
- return toUnaryOrFieldFilter(filter);
8834
- });
8835
- if (protos.length === 1) {
8836
- return protos[0];
9081
+ return toFilter(CompositeFilter.create(filters, "and" /* CompositeOperator.AND */));
9082
+ }
9083
+ function fromFilters(filter) {
9084
+ const result = fromFilter(filter);
9085
+ if (result instanceof CompositeFilter &&
9086
+ compositeFilterIsFlatConjunction(result)) {
9087
+ return result.getFilters();
8837
9088
  }
8838
- return { compositeFilter: { op: 'AND', filters: protos } };
9089
+ return [result];
8839
9090
  }
8840
9091
  function fromFilter(filter) {
8841
- if (!filter) {
8842
- return [];
8843
- }
8844
- else if (filter.unaryFilter !== undefined) {
8845
- return [fromUnaryFilter(filter)];
9092
+ if (filter.unaryFilter !== undefined) {
9093
+ return fromUnaryFilter(filter);
8846
9094
  }
8847
9095
  else if (filter.fieldFilter !== undefined) {
8848
- return [fromFieldFilter(filter)];
9096
+ return fromFieldFilter(filter);
8849
9097
  }
8850
9098
  else if (filter.compositeFilter !== undefined) {
8851
- return filter.compositeFilter
8852
- .filters.map(f => fromFilter(f))
8853
- .reduce((accum, current) => accum.concat(current));
9099
+ return fromCompositeFilter(filter);
8854
9100
  }
8855
9101
  else {
8856
9102
  return fail();
@@ -8895,9 +9141,9 @@ function toDirection(dir) {
8895
9141
  function fromDirection(dir) {
8896
9142
  switch (dir) {
8897
9143
  case 'ASCENDING':
8898
- return "asc" /* ASCENDING */;
9144
+ return "asc" /* Direction.ASCENDING */;
8899
9145
  case 'DESCENDING':
8900
- return "desc" /* DESCENDING */;
9146
+ return "desc" /* Direction.DESCENDING */;
8901
9147
  default:
8902
9148
  return undefined;
8903
9149
  }
@@ -8906,34 +9152,47 @@ function fromDirection(dir) {
8906
9152
  function toOperatorName(op) {
8907
9153
  return OPERATORS[op];
8908
9154
  }
9155
+ function toCompositeOperatorName(op) {
9156
+ return COMPOSITE_OPERATORS[op];
9157
+ }
8909
9158
  function fromOperatorName(op) {
8910
9159
  switch (op) {
8911
9160
  case 'EQUAL':
8912
- return "==" /* EQUAL */;
9161
+ return "==" /* Operator.EQUAL */;
8913
9162
  case 'NOT_EQUAL':
8914
- return "!=" /* NOT_EQUAL */;
9163
+ return "!=" /* Operator.NOT_EQUAL */;
8915
9164
  case 'GREATER_THAN':
8916
- return ">" /* GREATER_THAN */;
9165
+ return ">" /* Operator.GREATER_THAN */;
8917
9166
  case 'GREATER_THAN_OR_EQUAL':
8918
- return ">=" /* GREATER_THAN_OR_EQUAL */;
9167
+ return ">=" /* Operator.GREATER_THAN_OR_EQUAL */;
8919
9168
  case 'LESS_THAN':
8920
- return "<" /* LESS_THAN */;
9169
+ return "<" /* Operator.LESS_THAN */;
8921
9170
  case 'LESS_THAN_OR_EQUAL':
8922
- return "<=" /* LESS_THAN_OR_EQUAL */;
9171
+ return "<=" /* Operator.LESS_THAN_OR_EQUAL */;
8923
9172
  case 'ARRAY_CONTAINS':
8924
- return "array-contains" /* ARRAY_CONTAINS */;
9173
+ return "array-contains" /* Operator.ARRAY_CONTAINS */;
8925
9174
  case 'IN':
8926
- return "in" /* IN */;
9175
+ return "in" /* Operator.IN */;
8927
9176
  case 'NOT_IN':
8928
- return "not-in" /* NOT_IN */;
9177
+ return "not-in" /* Operator.NOT_IN */;
8929
9178
  case 'ARRAY_CONTAINS_ANY':
8930
- return "array-contains-any" /* ARRAY_CONTAINS_ANY */;
9179
+ return "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */;
8931
9180
  case 'OPERATOR_UNSPECIFIED':
8932
9181
  return fail();
8933
9182
  default:
8934
9183
  return fail();
8935
9184
  }
8936
9185
  }
9186
+ function fromCompositeOperatorName(op) {
9187
+ switch (op) {
9188
+ case 'AND':
9189
+ return "and" /* CompositeOperator.AND */;
9190
+ case 'OR':
9191
+ return "or" /* CompositeOperator.OR */;
9192
+ default:
9193
+ return fail();
9194
+ }
9195
+ }
8937
9196
  function toFieldPathReference(path) {
8938
9197
  return { fieldPath: path.canonicalString() };
8939
9198
  }
@@ -8950,12 +9209,32 @@ function toPropertyOrder(orderBy) {
8950
9209
  function fromPropertyOrder(orderBy) {
8951
9210
  return new OrderBy(fromFieldPathReference(orderBy.field), fromDirection(orderBy.direction));
8952
9211
  }
8953
- function fromFieldFilter(filter) {
8954
- return FieldFilter.create(fromFieldPathReference(filter.fieldFilter.field), fromOperatorName(filter.fieldFilter.op), filter.fieldFilter.value);
8955
- }
8956
9212
  // visible for testing
9213
+ function toFilter(filter) {
9214
+ if (filter instanceof FieldFilter) {
9215
+ return toUnaryOrFieldFilter(filter);
9216
+ }
9217
+ else if (filter instanceof CompositeFilter) {
9218
+ return toCompositeFilter(filter);
9219
+ }
9220
+ else {
9221
+ return fail();
9222
+ }
9223
+ }
9224
+ function toCompositeFilter(filter) {
9225
+ const protos = filter.getFilters().map(filter => toFilter(filter));
9226
+ if (protos.length === 1) {
9227
+ return protos[0];
9228
+ }
9229
+ return {
9230
+ compositeFilter: {
9231
+ op: toCompositeOperatorName(filter.op),
9232
+ filters: protos
9233
+ }
9234
+ };
9235
+ }
8957
9236
  function toUnaryOrFieldFilter(filter) {
8958
- if (filter.op === "==" /* EQUAL */) {
9237
+ if (filter.op === "==" /* Operator.EQUAL */) {
8959
9238
  if (isNanValue(filter.value)) {
8960
9239
  return {
8961
9240
  unaryFilter: {
@@ -8973,7 +9252,7 @@ function toUnaryOrFieldFilter(filter) {
8973
9252
  };
8974
9253
  }
8975
9254
  }
8976
- else if (filter.op === "!=" /* NOT_EQUAL */) {
9255
+ else if (filter.op === "!=" /* Operator.NOT_EQUAL */) {
8977
9256
  if (isNanValue(filter.value)) {
8978
9257
  return {
8979
9258
  unaryFilter: {
@@ -9003,22 +9282,22 @@ function fromUnaryFilter(filter) {
9003
9282
  switch (filter.unaryFilter.op) {
9004
9283
  case 'IS_NAN':
9005
9284
  const nanField = fromFieldPathReference(filter.unaryFilter.field);
9006
- return FieldFilter.create(nanField, "==" /* EQUAL */, {
9285
+ return FieldFilter.create(nanField, "==" /* Operator.EQUAL */, {
9007
9286
  doubleValue: NaN
9008
9287
  });
9009
9288
  case 'IS_NULL':
9010
9289
  const nullField = fromFieldPathReference(filter.unaryFilter.field);
9011
- return FieldFilter.create(nullField, "==" /* EQUAL */, {
9290
+ return FieldFilter.create(nullField, "==" /* Operator.EQUAL */, {
9012
9291
  nullValue: 'NULL_VALUE'
9013
9292
  });
9014
9293
  case 'IS_NOT_NAN':
9015
9294
  const notNanField = fromFieldPathReference(filter.unaryFilter.field);
9016
- return FieldFilter.create(notNanField, "!=" /* NOT_EQUAL */, {
9295
+ return FieldFilter.create(notNanField, "!=" /* Operator.NOT_EQUAL */, {
9017
9296
  doubleValue: NaN
9018
9297
  });
9019
9298
  case 'IS_NOT_NULL':
9020
9299
  const notNullField = fromFieldPathReference(filter.unaryFilter.field);
9021
- return FieldFilter.create(notNullField, "!=" /* NOT_EQUAL */, {
9300
+ return FieldFilter.create(notNullField, "!=" /* Operator.NOT_EQUAL */, {
9022
9301
  nullValue: 'NULL_VALUE'
9023
9302
  });
9024
9303
  case 'OPERATOR_UNSPECIFIED':
@@ -9027,6 +9306,12 @@ function fromUnaryFilter(filter) {
9027
9306
  return fail();
9028
9307
  }
9029
9308
  }
9309
+ function fromFieldFilter(filter) {
9310
+ return FieldFilter.create(fromFieldPathReference(filter.fieldFilter.field), fromOperatorName(filter.fieldFilter.op), filter.fieldFilter.value);
9311
+ }
9312
+ function fromCompositeFilter(filter) {
9313
+ return CompositeFilter.create(filter.compositeFilter.filters.map(filter => fromFilter(filter)), fromCompositeOperatorName(filter.compositeFilter.op));
9314
+ }
9030
9315
  function toDocumentMask(fieldMask) {
9031
9316
  const canonicalFields = [];
9032
9317
  fieldMask.fields.forEach(field => canonicalFields.push(field.canonicalString()));
@@ -9266,7 +9551,7 @@ function fromDbTarget(dbTarget) {
9266
9551
  else {
9267
9552
  target = fromQueryTarget(dbTarget.query);
9268
9553
  }
9269
- return new TargetData(target, dbTarget.targetId, 0 /* Listen */, dbTarget.lastListenSequenceNumber, version, lastLimboFreeSnapshotVersion, ByteString.fromBase64String(dbTarget.resumeToken));
9554
+ return new TargetData(target, dbTarget.targetId, 0 /* TargetPurpose.Listen */, dbTarget.lastListenSequenceNumber, version, lastLimboFreeSnapshotVersion, ByteString.fromBase64String(dbTarget.resumeToken));
9270
9555
  }
9271
9556
  /** Encodes TargetData into a DbTarget for storage locally. */
9272
9557
  function toDbTarget(localSerializer, targetData) {
@@ -9343,7 +9628,7 @@ function fromBundledQuery(bundledQuery) {
9343
9628
  structuredQuery: bundledQuery.structuredQuery
9344
9629
  });
9345
9630
  if (bundledQuery.limitType === 'LAST') {
9346
- return queryWithLimit(query, query.limit, "L" /* Last */);
9631
+ return queryWithLimit(query, query.limit, "L" /* LimitType.Last */);
9347
9632
  }
9348
9633
  return query;
9349
9634
  }
@@ -10118,7 +10403,7 @@ class IndexByteEncoder {
10118
10403
  this.orderedCode.seed(encodedBytes);
10119
10404
  }
10120
10405
  forKind(kind) {
10121
- return kind === 0 /* ASCENDING */ ? this.ascending : this.descending;
10406
+ return kind === 0 /* IndexKind.ASCENDING */ ? this.ascending : this.descending;
10122
10407
  }
10123
10408
  encodedBytes() {
10124
10409
  return this.orderedCode.encodedBytes();
@@ -10273,6 +10558,7 @@ class TargetIndexMatcher {
10273
10558
  * omitted.
10274
10559
  */
10275
10560
  servedByIndex(index) {
10561
+ hardAssert(index.collectionGroup === this.collectionId);
10276
10562
  // If there is an array element, find a matching filter.
10277
10563
  const arraySegment = fieldIndexGetArraySegment(index);
10278
10564
  if (arraySegment !== undefined &&
@@ -10336,19 +10622,268 @@ class TargetIndexMatcher {
10336
10622
  if (filter === undefined || !filter.field.isEqual(segment.fieldPath)) {
10337
10623
  return false;
10338
10624
  }
10339
- const isArrayOperator = filter.op === "array-contains" /* ARRAY_CONTAINS */ ||
10340
- filter.op === "array-contains-any" /* ARRAY_CONTAINS_ANY */;
10341
- return (segment.kind === 2 /* CONTAINS */) === isArrayOperator;
10625
+ const isArrayOperator = filter.op === "array-contains" /* Operator.ARRAY_CONTAINS */ ||
10626
+ filter.op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */;
10627
+ return (segment.kind === 2 /* IndexKind.CONTAINS */) === isArrayOperator;
10342
10628
  }
10343
10629
  matchesOrderBy(orderBy, segment) {
10344
10630
  if (!orderBy.field.isEqual(segment.fieldPath)) {
10345
10631
  return false;
10346
10632
  }
10347
- return ((segment.kind === 0 /* ASCENDING */ &&
10348
- orderBy.dir === "asc" /* ASCENDING */) ||
10349
- (segment.kind === 1 /* DESCENDING */ &&
10350
- orderBy.dir === "desc" /* DESCENDING */));
10633
+ return ((segment.kind === 0 /* IndexKind.ASCENDING */ &&
10634
+ orderBy.dir === "asc" /* Direction.ASCENDING */) ||
10635
+ (segment.kind === 1 /* IndexKind.DESCENDING */ &&
10636
+ orderBy.dir === "desc" /* Direction.DESCENDING */));
10637
+ }
10638
+ }
10639
+
10640
+ /**
10641
+ * @license
10642
+ * Copyright 2022 Google LLC
10643
+ *
10644
+ * Licensed under the Apache License, Version 2.0 (the "License");
10645
+ * you may not use this file except in compliance with the License.
10646
+ * You may obtain a copy of the License at
10647
+ *
10648
+ * http://www.apache.org/licenses/LICENSE-2.0
10649
+ *
10650
+ * Unless required by applicable law or agreed to in writing, software
10651
+ * distributed under the License is distributed on an "AS IS" BASIS,
10652
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10653
+ * See the License for the specific language governing permissions and
10654
+ * limitations under the License.
10655
+ */
10656
+ /**
10657
+ * Provides utility functions that help with boolean logic transformations needed for handling
10658
+ * complex filters used in queries.
10659
+ */
10660
+ /**
10661
+ * The `in` filter is only a syntactic sugar over a disjunction of equalities. For instance: `a in
10662
+ * [1,2,3]` is in fact `a==1 || a==2 || a==3`. This method expands any `in` filter in the given
10663
+ * input into a disjunction of equality filters and returns the expanded filter.
10664
+ */
10665
+ function computeInExpansion(filter) {
10666
+ var _a, _b;
10667
+ hardAssert(filter instanceof FieldFilter || filter instanceof CompositeFilter);
10668
+ if (filter instanceof FieldFilter) {
10669
+ if (filter instanceof InFilter) {
10670
+ const expandedFilters = ((_b = (_a = filter.value.arrayValue) === null || _a === void 0 ? void 0 : _a.values) === null || _b === void 0 ? void 0 : _b.map(value => FieldFilter.create(filter.field, "==" /* Operator.EQUAL */, value))) || [];
10671
+ return CompositeFilter.create(expandedFilters, "or" /* CompositeOperator.OR */);
10672
+ }
10673
+ else {
10674
+ // We have reached other kinds of field filters.
10675
+ return filter;
10676
+ }
10677
+ }
10678
+ // We have a composite filter.
10679
+ const expandedFilters = filter.filters.map(subfilter => computeInExpansion(subfilter));
10680
+ return CompositeFilter.create(expandedFilters, filter.op);
10681
+ }
10682
+ /**
10683
+ * Given a composite filter, returns the list of terms in its disjunctive normal form.
10684
+ *
10685
+ * <p>Each element in the return value is one term of the resulting DNF. For instance: For the
10686
+ * input: (A || B) && C, the DNF form is: (A && C) || (B && C), and the return value is a list
10687
+ * with two elements: a composite filter that performs (A && C), and a composite filter that
10688
+ * performs (B && C).
10689
+ *
10690
+ * @param filter the composite filter to calculate DNF transform for.
10691
+ * @return the terms in the DNF transform.
10692
+ */
10693
+ function getDnfTerms(filter) {
10694
+ if (filter.getFilters().length === 0) {
10695
+ return [];
10696
+ }
10697
+ const result = computeDistributedNormalForm(computeInExpansion(filter));
10698
+ hardAssert(isDisjunctiveNormalForm(result));
10699
+ if (isSingleFieldFilter(result) || isFlatConjunction(result)) {
10700
+ return [result];
10701
+ }
10702
+ return result.getFilters();
10703
+ }
10704
+ /** Returns true if the given filter is a single field filter. e.g. (a == 10). */
10705
+ function isSingleFieldFilter(filter) {
10706
+ return filter instanceof FieldFilter;
10707
+ }
10708
+ /**
10709
+ * Returns true if the given filter is the conjunction of one or more field filters. e.g. (a == 10
10710
+ * && b == 20)
10711
+ */
10712
+ function isFlatConjunction(filter) {
10713
+ return (filter instanceof CompositeFilter &&
10714
+ compositeFilterIsFlatConjunction(filter));
10715
+ }
10716
+ /**
10717
+ * Returns whether or not the given filter is in disjunctive normal form (DNF).
10718
+ *
10719
+ * <p>In boolean logic, a disjunctive normal form (DNF) is a canonical normal form of a logical
10720
+ * formula consisting of a disjunction of conjunctions; it can also be described as an OR of ANDs.
10721
+ *
10722
+ * <p>For more info, visit: https://en.wikipedia.org/wiki/Disjunctive_normal_form
10723
+ */
10724
+ function isDisjunctiveNormalForm(filter) {
10725
+ return (isSingleFieldFilter(filter) ||
10726
+ isFlatConjunction(filter) ||
10727
+ isDisjunctionOfFieldFiltersAndFlatConjunctions(filter));
10728
+ }
10729
+ /**
10730
+ * Returns true if the given filter is the disjunction of one or more "flat conjunctions" and
10731
+ * field filters. e.g. (a == 10) || (b==20 && c==30)
10732
+ */
10733
+ function isDisjunctionOfFieldFiltersAndFlatConjunctions(filter) {
10734
+ if (filter instanceof CompositeFilter) {
10735
+ if (compositeFilterIsDisjunction(filter)) {
10736
+ for (const subFilter of filter.getFilters()) {
10737
+ if (!isSingleFieldFilter(subFilter) && !isFlatConjunction(subFilter)) {
10738
+ return false;
10739
+ }
10740
+ }
10741
+ return true;
10742
+ }
10743
+ }
10744
+ return false;
10745
+ }
10746
+ function computeDistributedNormalForm(filter) {
10747
+ hardAssert(filter instanceof FieldFilter || filter instanceof CompositeFilter);
10748
+ if (filter instanceof FieldFilter) {
10749
+ return filter;
10750
+ }
10751
+ if (filter.filters.length === 1) {
10752
+ return computeDistributedNormalForm(filter.filters[0]);
10753
+ }
10754
+ // Compute DNF for each of the subfilters first
10755
+ const result = filter.filters.map(subfilter => computeDistributedNormalForm(subfilter));
10756
+ let newFilter = CompositeFilter.create(result, filter.op);
10757
+ newFilter = applyAssociation(newFilter);
10758
+ if (isDisjunctiveNormalForm(newFilter)) {
10759
+ return newFilter;
10760
+ }
10761
+ hardAssert(newFilter instanceof CompositeFilter);
10762
+ hardAssert(compositeFilterIsConjunction(newFilter));
10763
+ hardAssert(newFilter.filters.length > 1);
10764
+ return newFilter.filters.reduce((runningResult, filter) => applyDistribution(runningResult, filter));
10765
+ }
10766
+ function applyDistribution(lhs, rhs) {
10767
+ hardAssert(lhs instanceof FieldFilter || lhs instanceof CompositeFilter);
10768
+ hardAssert(rhs instanceof FieldFilter || rhs instanceof CompositeFilter);
10769
+ let result;
10770
+ if (lhs instanceof FieldFilter) {
10771
+ if (rhs instanceof FieldFilter) {
10772
+ // FieldFilter FieldFilter
10773
+ result = applyDistributionFieldFilters(lhs, rhs);
10774
+ }
10775
+ else {
10776
+ // FieldFilter CompositeFilter
10777
+ result = applyDistributionFieldAndCompositeFilters(lhs, rhs);
10778
+ }
10779
+ }
10780
+ else {
10781
+ if (rhs instanceof FieldFilter) {
10782
+ // CompositeFilter FieldFilter
10783
+ result = applyDistributionFieldAndCompositeFilters(rhs, lhs);
10784
+ }
10785
+ else {
10786
+ // CompositeFilter CompositeFilter
10787
+ result = applyDistributionCompositeFilters(lhs, rhs);
10788
+ }
10789
+ }
10790
+ return applyAssociation(result);
10791
+ }
10792
+ function applyDistributionFieldFilters(lhs, rhs) {
10793
+ // Conjunction distribution for two field filters is the conjunction of them.
10794
+ return CompositeFilter.create([lhs, rhs], "and" /* CompositeOperator.AND */);
10795
+ }
10796
+ function applyDistributionCompositeFilters(lhs, rhs) {
10797
+ hardAssert(lhs.filters.length > 0 && rhs.filters.length > 0);
10798
+ // There are four cases:
10799
+ // (A & B) & (C & D) --> (A & B & C & D)
10800
+ // (A & B) & (C | D) --> (A & B & C) | (A & B & D)
10801
+ // (A | B) & (C & D) --> (C & D & A) | (C & D & B)
10802
+ // (A | B) & (C | D) --> (A & C) | (A & D) | (B & C) | (B & D)
10803
+ // Case 1 is a merge.
10804
+ if (compositeFilterIsConjunction(lhs) && compositeFilterIsConjunction(rhs)) {
10805
+ return compositeFilterWithAddedFilters(lhs, rhs.getFilters());
10806
+ }
10807
+ // Case 2,3,4 all have at least one side (lhs or rhs) that is a disjunction. In all three cases
10808
+ // we should take each element of the disjunction and distribute it over the other side, and
10809
+ // return the disjunction of the distribution results.
10810
+ const disjunctionSide = compositeFilterIsDisjunction(lhs) ? lhs : rhs;
10811
+ const otherSide = compositeFilterIsDisjunction(lhs) ? rhs : lhs;
10812
+ const results = disjunctionSide.filters.map(subfilter => applyDistribution(subfilter, otherSide));
10813
+ return CompositeFilter.create(results, "or" /* CompositeOperator.OR */);
10814
+ }
10815
+ function applyDistributionFieldAndCompositeFilters(fieldFilter, compositeFilter) {
10816
+ // There are two cases:
10817
+ // A & (B & C) --> (A & B & C)
10818
+ // A & (B | C) --> (A & B) | (A & C)
10819
+ if (compositeFilterIsConjunction(compositeFilter)) {
10820
+ // Case 1
10821
+ return compositeFilterWithAddedFilters(compositeFilter, fieldFilter.getFilters());
10822
+ }
10823
+ else {
10824
+ // Case 2
10825
+ const newFilters = compositeFilter.filters.map(subfilter => applyDistribution(fieldFilter, subfilter));
10826
+ return CompositeFilter.create(newFilters, "or" /* CompositeOperator.OR */);
10827
+ }
10828
+ }
10829
+ /**
10830
+ * Applies the associativity property to the given filter and returns the resulting filter.
10831
+ *
10832
+ * <ul>
10833
+ * <li>A | (B | C) == (A | B) | C == (A | B | C)
10834
+ * <li>A & (B & C) == (A & B) & C == (A & B & C)
10835
+ * </ul>
10836
+ *
10837
+ * <p>For more info, visit: https://en.wikipedia.org/wiki/Associative_property#Propositional_logic
10838
+ */
10839
+ function applyAssociation(filter) {
10840
+ hardAssert(filter instanceof FieldFilter || filter instanceof CompositeFilter);
10841
+ if (filter instanceof FieldFilter) {
10842
+ return filter;
10843
+ }
10844
+ const filters = filter.getFilters();
10845
+ // If the composite filter only contains 1 filter, apply associativity to it.
10846
+ if (filters.length === 1) {
10847
+ return applyAssociation(filters[0]);
10848
+ }
10849
+ // Associativity applied to a flat composite filter results is itself.
10850
+ if (compositeFilterIsFlat(filter)) {
10851
+ return filter;
10852
+ }
10853
+ // First apply associativity to all subfilters. This will in turn recursively apply
10854
+ // associativity to all nested composite filters and field filters.
10855
+ const updatedFilters = filters.map(subfilter => applyAssociation(subfilter));
10856
+ // For composite subfilters that perform the same kind of logical operation as `compositeFilter`
10857
+ // take out their filters and add them to `compositeFilter`. For example:
10858
+ // compositeFilter = (A | (B | C | D))
10859
+ // compositeSubfilter = (B | C | D)
10860
+ // Result: (A | B | C | D)
10861
+ // Note that the `compositeSubfilter` has been eliminated, and its filters (B, C, D) have been
10862
+ // added to the top-level "compositeFilter".
10863
+ const newSubfilters = [];
10864
+ updatedFilters.forEach(subfilter => {
10865
+ if (subfilter instanceof FieldFilter) {
10866
+ newSubfilters.push(subfilter);
10867
+ }
10868
+ else if (subfilter instanceof CompositeFilter) {
10869
+ if (subfilter.op === filter.op) {
10870
+ // compositeFilter: (A | (B | C))
10871
+ // compositeSubfilter: (B | C)
10872
+ // Result: (A | B | C)
10873
+ newSubfilters.push(...subfilter.filters);
10874
+ }
10875
+ else {
10876
+ // compositeFilter: (A | (B & C))
10877
+ // compositeSubfilter: (B & C)
10878
+ // Result: (A | (B & C))
10879
+ newSubfilters.push(subfilter);
10880
+ }
10881
+ }
10882
+ });
10883
+ if (newSubfilters.length === 1) {
10884
+ return newSubfilters[0];
10351
10885
  }
10886
+ return CompositeFilter.create(newSubfilters, filter.op);
10352
10887
  }
10353
10888
 
10354
10889
  /**
@@ -10395,7 +10930,7 @@ class MemoryIndexManager {
10395
10930
  }
10396
10931
  getIndexType(transaction, target) {
10397
10932
  // Field indices are not supported with memory persistence.
10398
- return PersistencePromise.resolve(0 /* NONE */);
10933
+ return PersistencePromise.resolve(0 /* IndexType.NONE */);
10399
10934
  }
10400
10935
  getFieldIndexes(transaction, collectionGroup) {
10401
10936
  // Field indices are not supported with memory persistence.
@@ -10618,8 +11153,14 @@ class IndexedDbIndexManager {
10618
11153
  if (subTargets) {
10619
11154
  return subTargets;
10620
11155
  }
10621
- // TODO(orquery): Implement DNF transform
10622
- subTargets = [target];
11156
+ if (target.filters.length === 0) {
11157
+ subTargets = [target];
11158
+ }
11159
+ else {
11160
+ // There is an implicit AND operation between all the filters stored in the target
11161
+ const dnf = getDnfTerms(CompositeFilter.create(target.filters, "and" /* CompositeOperator.AND */));
11162
+ subTargets = dnf.map(term => newTarget(target.path, target.collectionGroup, target.orderBy, term.getFilters(), target.limit, target.startAt, target.endAt));
11163
+ }
10623
11164
  this.targetToDnfSubTargets.set(target, subTargets);
10624
11165
  return subTargets;
10625
11166
  }
@@ -10677,18 +11218,29 @@ class IndexedDbIndexManager {
10677
11218
  });
10678
11219
  }
10679
11220
  getIndexType(transaction, target) {
10680
- let indexType = 2 /* FULL */;
10681
- return PersistencePromise.forEach(this.getSubTargets(target), (target) => {
11221
+ let indexType = 2 /* IndexType.FULL */;
11222
+ const subTargets = this.getSubTargets(target);
11223
+ return PersistencePromise.forEach(subTargets, (target) => {
10682
11224
  return this.getFieldIndex(transaction, target).next(index => {
10683
11225
  if (!index) {
10684
- indexType = 0 /* NONE */;
11226
+ indexType = 0 /* IndexType.NONE */;
10685
11227
  }
10686
- else if (indexType !== 0 /* NONE */ &&
11228
+ else if (indexType !== 0 /* IndexType.NONE */ &&
10687
11229
  index.fields.length < targetGetSegmentCount(target)) {
10688
- indexType = 1 /* PARTIAL */;
11230
+ indexType = 1 /* IndexType.PARTIAL */;
10689
11231
  }
10690
11232
  });
10691
- }).next(() => indexType);
11233
+ }).next(() => {
11234
+ // OR queries have more than one sub-target (one sub-target per DNF term). We currently consider
11235
+ // OR queries that have a `limit` to have a partial index. For such queries we perform sorting
11236
+ // and apply the limit in memory as a post-processing step.
11237
+ if (targetHasLimit(target) &&
11238
+ subTargets.length > 1 &&
11239
+ indexType === 2 /* IndexType.FULL */) {
11240
+ return 1 /* IndexType.PARTIAL */;
11241
+ }
11242
+ return indexType;
11243
+ });
10692
11244
  }
10693
11245
  /**
10694
11246
  * Returns the byte encoded form of the directional values in the field index.
@@ -10710,7 +11262,7 @@ class IndexedDbIndexManager {
10710
11262
  /** Encodes a single value to the ascending index format. */
10711
11263
  encodeSingleElement(value) {
10712
11264
  const encoder = new IndexByteEncoder();
10713
- FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, encoder.forKind(0 /* ASCENDING */));
11265
+ FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, encoder.forKind(0 /* IndexKind.ASCENDING */));
10714
11266
  return encoder.encodedBytes();
10715
11267
  }
10716
11268
  /**
@@ -10726,15 +11278,15 @@ class IndexedDbIndexManager {
10726
11278
  * Encodes the given field values according to the specification in `target`.
10727
11279
  * For IN queries, a list of possible values is returned.
10728
11280
  */
10729
- encodeValues(fieldIndex, target, bound) {
10730
- if (bound === null) {
11281
+ encodeValues(fieldIndex, target, values) {
11282
+ if (values === null) {
10731
11283
  return [];
10732
11284
  }
10733
11285
  let encoders = [];
10734
11286
  encoders.push(new IndexByteEncoder());
10735
- let boundIdx = 0;
11287
+ let valueIdx = 0;
10736
11288
  for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
10737
- const value = bound[boundIdx++];
11289
+ const value = values[valueIdx++];
10738
11290
  for (const encoder of encoders) {
10739
11291
  if (this.isInFilter(target, segment.fieldPath) && isArray(value)) {
10740
11292
  encoders = this.expandIndexValues(encoders, segment, value);
@@ -10785,7 +11337,7 @@ class IndexedDbIndexManager {
10785
11337
  isInFilter(target, fieldPath) {
10786
11338
  return !!target.filters.find(f => f instanceof FieldFilter &&
10787
11339
  f.field.isEqual(fieldPath) &&
10788
- (f.op === "in" /* IN */ || f.op === "not-in" /* NOT_IN */));
11340
+ (f.op === "in" /* Operator.IN */ || f.op === "not-in" /* Operator.NOT_IN */));
10789
11341
  }
10790
11342
  getFieldIndexes(transaction, collectionGroup) {
10791
11343
  const indexes = indexConfigurationStore(transaction);
@@ -10974,24 +11526,36 @@ class IndexedDbIndexManager {
10974
11526
  bounds.push(upper);
10975
11527
  const ranges = [];
10976
11528
  for (let i = 0; i < bounds.length; i += 2) {
10977
- ranges.push(IDBKeyRange.bound([
11529
+ // If we encounter two bounds that will create an unmatchable key range,
11530
+ // then we return an empty set of key ranges.
11531
+ if (this.isRangeMatchable(bounds[i], bounds[i + 1])) {
11532
+ return [];
11533
+ }
11534
+ const lowerBound = [
10978
11535
  bounds[i].indexId,
10979
11536
  this.uid,
10980
11537
  bounds[i].arrayValue,
10981
11538
  bounds[i].directionalValue,
10982
11539
  EMPTY_VALUE,
10983
11540
  []
10984
- ], [
11541
+ ];
11542
+ const upperBound = [
10985
11543
  bounds[i + 1].indexId,
10986
11544
  this.uid,
10987
11545
  bounds[i + 1].arrayValue,
10988
11546
  bounds[i + 1].directionalValue,
10989
11547
  EMPTY_VALUE,
10990
11548
  []
10991
- ]));
11549
+ ];
11550
+ ranges.push(IDBKeyRange.bound(lowerBound, upperBound));
10992
11551
  }
10993
11552
  return ranges;
10994
11553
  }
11554
+ isRangeMatchable(lowerBound, upperBound) {
11555
+ // If lower bound is greater than the upper bound, then the key
11556
+ // range can never be matched.
11557
+ return indexEntryComparator(lowerBound, upperBound) > 0;
11558
+ }
10995
11559
  getMinOffsetFromCollectionGroup(transaction, collectionGroup) {
10996
11560
  return this.getFieldIndexes(transaction, collectionGroup).next(getMinOffsetFromFieldIndexes);
10997
11561
  }
@@ -11968,7 +12532,7 @@ class LruScheduler {
11968
12532
  }
11969
12533
  scheduleGC(delay) {
11970
12534
  logDebug(LOG_TAG$e, `Garbage collection scheduled in ${delay}ms`);
11971
- this.gcTask = this.asyncQueue.enqueueAfterDelay("lru_garbage_collection" /* LruGarbageCollection */, delay, async () => {
12535
+ this.gcTask = this.asyncQueue.enqueueAfterDelay("lru_garbage_collection" /* TimerId.LruGarbageCollection */, delay, async () => {
11972
12536
  this.gcTask = null;
11973
12537
  try {
11974
12538
  await this.localStore.collectGarbage(this.garbageCollector);
@@ -12866,7 +13430,7 @@ class LocalDocumentsView {
12866
13430
  .getOverlay(transaction, key)
12867
13431
  .next(value => {
12868
13432
  overlay = value;
12869
- return this.getBaseDocument(transaction, key, overlay);
13433
+ return this.remoteDocumentCache.getEntry(transaction, key);
12870
13434
  })
12871
13435
  .next(document => {
12872
13436
  if (overlay !== null) {
@@ -13089,7 +13653,9 @@ class LocalDocumentsView {
13089
13653
  if (originalDocs.get(key)) {
13090
13654
  return PersistencePromise.resolve();
13091
13655
  }
13092
- return this.getBaseDocument(transaction, key, overlay).next(doc => {
13656
+ return this.remoteDocumentCache
13657
+ .getEntry(transaction, key)
13658
+ .next(doc => {
13093
13659
  modifiedDocs = modifiedDocs.insert(key, doc);
13094
13660
  });
13095
13661
  })
@@ -13163,12 +13729,6 @@ class LocalDocumentsView {
13163
13729
  return results;
13164
13730
  });
13165
13731
  }
13166
- /** Returns a base document that can be used to apply `overlay`. */
13167
- getBaseDocument(transaction, key, overlay) {
13168
- return overlay === null || overlay.mutation.type === 1 /* Patch */
13169
- ? this.remoteDocumentCache.getEntry(transaction, key)
13170
- : PersistencePromise.resolve(MutableDocument.newInvalidDocument(key));
13171
- }
13172
13732
  }
13173
13733
 
13174
13734
  /**
@@ -14918,7 +15478,7 @@ class IndexedDbPersistence {
14918
15478
  * extend or acquire the primary lease if the client is eligible.
14919
15479
  */
14920
15480
  scheduleClientMetadataAndPrimaryLeaseRefreshes() {
14921
- this.clientMetadataRefresher = this.queue.enqueueAfterDelay("client_metadata_refresh" /* ClientMetadataRefresh */, CLIENT_METADATA_REFRESH_INTERVAL_MS, () => {
15481
+ this.clientMetadataRefresher = this.queue.enqueueAfterDelay("client_metadata_refresh" /* TimerId.ClientMetadataRefresh */, CLIENT_METADATA_REFRESH_INTERVAL_MS, () => {
14922
15482
  return this.updateClientMetadataAndTryBecomePrimary()
14923
15483
  .then(() => this.maybeGarbageCollectMultiClientState())
14924
15484
  .then(() => this.scheduleClientMetadataAndPrimaryLeaseRefreshes());
@@ -15928,7 +16488,7 @@ function localStoreAllocateTarget(localStore, target) {
15928
16488
  return localStoreImpl.targetCache
15929
16489
  .allocateTargetId(txn)
15930
16490
  .next(targetId => {
15931
- targetData = new TargetData(target, targetId, 0 /* Listen */, txn.currentSequenceNumber);
16491
+ targetData = new TargetData(target, targetId, 0 /* TargetPurpose.Listen */, txn.currentSequenceNumber);
15932
16492
  return localStoreImpl.targetCache
15933
16493
  .addTargetData(txn, targetData)
15934
16494
  .next(() => targetData);
@@ -16350,11 +16910,11 @@ class QueryEngine {
16350
16910
  return this.indexManager
16351
16911
  .getIndexType(transaction, target)
16352
16912
  .next(indexType => {
16353
- if (indexType === 0 /* NONE */) {
16913
+ if (indexType === 0 /* IndexType.NONE */) {
16354
16914
  // The target cannot be served from any index.
16355
16915
  return null;
16356
16916
  }
16357
- if (query.limit !== null && indexType === 1 /* PARTIAL */) {
16917
+ if (query.limit !== null && indexType === 1 /* IndexType.PARTIAL */) {
16358
16918
  // We cannot apply a limit for targets that are served using a partial
16359
16919
  // index. If a partial index will be used to serve the target, the
16360
16920
  // query may return a superset of documents that match the target
@@ -16362,7 +16922,7 @@ class QueryEngine {
16362
16922
  // may return the correct set of documents in the wrong order (e.g. if
16363
16923
  // the index doesn't include a segment for one of the orderBys).
16364
16924
  // Therefore, a limit should not be applied in such cases.
16365
- query = queryWithLimit(query, null, "F" /* First */);
16925
+ query = queryWithLimit(query, null, "F" /* LimitType.First */);
16366
16926
  target = queryToTarget(query);
16367
16927
  }
16368
16928
  return this.indexManager
@@ -16383,7 +16943,7 @@ class QueryEngine {
16383
16943
  // query's filters are included in the result set. The SDK
16384
16944
  // can then apply the limit once all local edits are
16385
16945
  // incorporated.
16386
- return this.performQueryUsingIndex(transaction, queryWithLimit(query, null, "F" /* First */));
16946
+ return this.performQueryUsingIndex(transaction, queryWithLimit(query, null, "F" /* LimitType.First */));
16387
16947
  }
16388
16948
  return this.appendRemainingResults(transaction, previousResults, query, offset);
16389
16949
  });
@@ -16462,7 +17022,7 @@ class QueryEngine {
16462
17022
  // the boundary of the limit itself did not change and documents from cache
16463
17023
  // will continue to be "rejected" by this boundary. Therefore, we can ignore
16464
17024
  // any modifications that don't affect the last document.
16465
- const docAtLimitEdge = query.limitType === "F" /* First */
17025
+ const docAtLimitEdge = query.limitType === "F" /* LimitType.First */
16466
17026
  ? sortedPreviousResults.last()
16467
17027
  : sortedPreviousResults.first();
16468
17028
  if (!docAtLimitEdge) {
@@ -17383,7 +17943,7 @@ function nodePromise(action) {
17383
17943
  */
17384
17944
  // TODO: Fetch runtime version from grpc-js/package.json instead
17385
17945
  // when there's a cleaner way to dynamic require JSON in both Node ESM and CJS
17386
- const grpcVersion = '1.3.7';
17946
+ const grpcVersion = '1.7.3';
17387
17947
  const LOG_TAG$9 = 'Connection';
17388
17948
  const X_GOOG_API_CLIENT_VALUE = `gl-node/${process.versions.node} fire/${SDK_VERSION} grpc/${grpcVersion}`;
17389
17949
  function createMetadata(databasePath, authToken, appCheckToken, appId) {
@@ -19831,7 +20391,8 @@ const nested = {
19831
20391
  Operator: {
19832
20392
  values: {
19833
20393
  OPERATOR_UNSPECIFIED: 0,
19834
- AND: 1
20394
+ AND: 1,
20395
+ OR: 2
19835
20396
  }
19836
20397
  }
19837
20398
  }
@@ -20707,7 +21268,7 @@ class PersistentStream {
20707
21268
  this.authCredentialsProvider = authCredentialsProvider;
20708
21269
  this.appCheckCredentialsProvider = appCheckCredentialsProvider;
20709
21270
  this.listener = listener;
20710
- this.state = 0 /* Initial */;
21271
+ this.state = 0 /* PersistentStreamState.Initial */;
20711
21272
  /**
20712
21273
  * A close count that's incremented every time the stream is closed; used by
20713
21274
  * getCloseGuardedDispatcher() to invalidate callbacks that happen after
@@ -20727,8 +21288,8 @@ class PersistentStream {
20727
21288
  * outbound requests.
20728
21289
  */
20729
21290
  isStarted() {
20730
- return (this.state === 1 /* Starting */ ||
20731
- this.state === 5 /* Backoff */ ||
21291
+ return (this.state === 1 /* PersistentStreamState.Starting */ ||
21292
+ this.state === 5 /* PersistentStreamState.Backoff */ ||
20732
21293
  this.isOpen());
20733
21294
  }
20734
21295
  /**
@@ -20736,8 +21297,8 @@ class PersistentStream {
20736
21297
  * called) and the stream is ready for outbound requests.
20737
21298
  */
20738
21299
  isOpen() {
20739
- return (this.state === 2 /* Open */ ||
20740
- this.state === 3 /* Healthy */);
21300
+ return (this.state === 2 /* PersistentStreamState.Open */ ||
21301
+ this.state === 3 /* PersistentStreamState.Healthy */);
20741
21302
  }
20742
21303
  /**
20743
21304
  * Starts the RPC. Only allowed if isStarted() returns false. The stream is
@@ -20747,7 +21308,7 @@ class PersistentStream {
20747
21308
  * When start returns, isStarted() will return true.
20748
21309
  */
20749
21310
  start() {
20750
- if (this.state === 4 /* Error */) {
21311
+ if (this.state === 4 /* PersistentStreamState.Error */) {
20751
21312
  this.performBackoff();
20752
21313
  return;
20753
21314
  }
@@ -20761,7 +21322,7 @@ class PersistentStream {
20761
21322
  */
20762
21323
  async stop() {
20763
21324
  if (this.isStarted()) {
20764
- await this.close(0 /* Initial */);
21325
+ await this.close(0 /* PersistentStreamState.Initial */);
20765
21326
  }
20766
21327
  }
20767
21328
  /**
@@ -20773,7 +21334,7 @@ class PersistentStream {
20773
21334
  * inhibit backoff if required.
20774
21335
  */
20775
21336
  inhibitBackoff() {
20776
- this.state = 0 /* Initial */;
21337
+ this.state = 0 /* PersistentStreamState.Initial */;
20777
21338
  this.backoff.reset();
20778
21339
  }
20779
21340
  /**
@@ -20803,7 +21364,7 @@ class PersistentStream {
20803
21364
  if (this.isOpen()) {
20804
21365
  // When timing out an idle stream there's no reason to force the stream into backoff when
20805
21366
  // it restarts so set the stream state to Initial instead of Error.
20806
- return this.close(0 /* Initial */);
21367
+ return this.close(0 /* PersistentStreamState.Initial */);
20807
21368
  }
20808
21369
  }
20809
21370
  /** Marks the stream as active again. */
@@ -20841,7 +21402,7 @@ class PersistentStream {
20841
21402
  // Invalidates any stream-related callbacks (e.g. from auth or the
20842
21403
  // underlying stream), guaranteeing they won't execute.
20843
21404
  this.closeCount++;
20844
- if (finalState !== 4 /* Error */) {
21405
+ if (finalState !== 4 /* PersistentStreamState.Error */) {
20845
21406
  // If this is an intentional close ensure we don't delay our next connection attempt.
20846
21407
  this.backoff.reset();
20847
21408
  }
@@ -20853,7 +21414,7 @@ class PersistentStream {
20853
21414
  }
20854
21415
  else if (error &&
20855
21416
  error.code === Code.UNAUTHENTICATED &&
20856
- this.state !== 3 /* Healthy */) {
21417
+ this.state !== 3 /* PersistentStreamState.Healthy */) {
20857
21418
  // "unauthenticated" error means the token was rejected. This should rarely
20858
21419
  // happen since both Auth and AppCheck ensure a sufficient TTL when we
20859
21420
  // request a token. If a user manually resets their system clock this can
@@ -20881,7 +21442,7 @@ class PersistentStream {
20881
21442
  */
20882
21443
  tearDown() { }
20883
21444
  auth() {
20884
- this.state = 1 /* Starting */;
21445
+ this.state = 1 /* PersistentStreamState.Starting */;
20885
21446
  const dispatchIfNotClosed = this.getCloseGuardedDispatcher(this.closeCount);
20886
21447
  // TODO(mikelehen): Just use dispatchIfNotClosed, but see TODO below.
20887
21448
  const closeCount = this.closeCount;
@@ -20911,10 +21472,10 @@ class PersistentStream {
20911
21472
  this.stream = this.startRpc(authToken, appCheckToken);
20912
21473
  this.stream.onOpen(() => {
20913
21474
  dispatchIfNotClosed(() => {
20914
- this.state = 2 /* Open */;
21475
+ this.state = 2 /* PersistentStreamState.Open */;
20915
21476
  this.healthCheck = this.queue.enqueueAfterDelay(this.healthTimerId, HEALTHY_TIMEOUT_MS, () => {
20916
21477
  if (this.isOpen()) {
20917
- this.state = 3 /* Healthy */;
21478
+ this.state = 3 /* PersistentStreamState.Healthy */;
20918
21479
  }
20919
21480
  return Promise.resolve();
20920
21481
  });
@@ -20933,9 +21494,9 @@ class PersistentStream {
20933
21494
  });
20934
21495
  }
20935
21496
  performBackoff() {
20936
- this.state = 5 /* Backoff */;
21497
+ this.state = 5 /* PersistentStreamState.Backoff */;
20937
21498
  this.backoff.backoffAndRun(async () => {
20938
- this.state = 0 /* Initial */;
21499
+ this.state = 0 /* PersistentStreamState.Initial */;
20939
21500
  this.start();
20940
21501
  });
20941
21502
  }
@@ -20947,7 +21508,7 @@ class PersistentStream {
20947
21508
  // we never expect this to happen because if we stop a stream ourselves,
20948
21509
  // this callback will never be called. To prevent cases where we retry
20949
21510
  // without a backoff accidentally, we set the stream to error in all cases.
20950
- return this.close(4 /* Error */, error);
21511
+ return this.close(4 /* PersistentStreamState.Error */, error);
20951
21512
  }
20952
21513
  /**
20953
21514
  * Returns a "dispatcher" function that dispatches operations onto the
@@ -20978,7 +21539,7 @@ class PersistentStream {
20978
21539
  */
20979
21540
  class PersistentListenStream extends PersistentStream {
20980
21541
  constructor(queue, connection, authCredentials, appCheckCredentials, serializer, listener) {
20981
- super(queue, "listen_stream_connection_backoff" /* ListenStreamConnectionBackoff */, "listen_stream_idle" /* ListenStreamIdle */, "health_check_timeout" /* HealthCheckTimeout */, connection, authCredentials, appCheckCredentials, listener);
21542
+ super(queue, "listen_stream_connection_backoff" /* TimerId.ListenStreamConnectionBackoff */, "listen_stream_idle" /* TimerId.ListenStreamIdle */, "health_check_timeout" /* TimerId.HealthCheckTimeout */, connection, authCredentials, appCheckCredentials, listener);
20982
21543
  this.serializer = serializer;
20983
21544
  }
20984
21545
  startRpc(authToken, appCheckToken) {
@@ -21037,7 +21598,7 @@ class PersistentListenStream extends PersistentStream {
21037
21598
  */
21038
21599
  class PersistentWriteStream extends PersistentStream {
21039
21600
  constructor(queue, connection, authCredentials, appCheckCredentials, serializer, listener) {
21040
- super(queue, "write_stream_connection_backoff" /* WriteStreamConnectionBackoff */, "write_stream_idle" /* WriteStreamIdle */, "health_check_timeout" /* HealthCheckTimeout */, connection, authCredentials, appCheckCredentials, listener);
21601
+ super(queue, "write_stream_connection_backoff" /* TimerId.WriteStreamConnectionBackoff */, "write_stream_idle" /* TimerId.WriteStreamIdle */, "health_check_timeout" /* TimerId.HealthCheckTimeout */, connection, authCredentials, appCheckCredentials, listener);
21041
21602
  this.serializer = serializer;
21042
21603
  this.handshakeComplete_ = false;
21043
21604
  }
@@ -21296,7 +21857,7 @@ class OnlineStateTracker {
21296
21857
  this.asyncQueue = asyncQueue;
21297
21858
  this.onlineStateHandler = onlineStateHandler;
21298
21859
  /** The current OnlineState. */
21299
- this.state = "Unknown" /* Unknown */;
21860
+ this.state = "Unknown" /* OnlineState.Unknown */;
21300
21861
  /**
21301
21862
  * A count of consecutive failures to open the stream. If it reaches the
21302
21863
  * maximum defined by MAX_WATCH_STREAM_FAILURES, we'll set the OnlineState to
@@ -21325,12 +21886,12 @@ class OnlineStateTracker {
21325
21886
  */
21326
21887
  handleWatchStreamStart() {
21327
21888
  if (this.watchStreamFailures === 0) {
21328
- this.setAndBroadcast("Unknown" /* Unknown */);
21329
- this.onlineStateTimer = this.asyncQueue.enqueueAfterDelay("online_state_timeout" /* OnlineStateTimeout */, ONLINE_STATE_TIMEOUT_MS, () => {
21889
+ this.setAndBroadcast("Unknown" /* OnlineState.Unknown */);
21890
+ this.onlineStateTimer = this.asyncQueue.enqueueAfterDelay("online_state_timeout" /* TimerId.OnlineStateTimeout */, ONLINE_STATE_TIMEOUT_MS, () => {
21330
21891
  this.onlineStateTimer = null;
21331
21892
  this.logClientOfflineWarningIfNecessary(`Backend didn't respond within ${ONLINE_STATE_TIMEOUT_MS / 1000} ` +
21332
21893
  `seconds.`);
21333
- this.setAndBroadcast("Offline" /* Offline */);
21894
+ this.setAndBroadcast("Offline" /* OnlineState.Offline */);
21334
21895
  // NOTE: handleWatchStreamFailure() will continue to increment
21335
21896
  // watchStreamFailures even though we are already marked Offline,
21336
21897
  // but this is non-harmful.
@@ -21345,8 +21906,8 @@ class OnlineStateTracker {
21345
21906
  * actually transition to the 'Offline' state.
21346
21907
  */
21347
21908
  handleWatchStreamFailure(error) {
21348
- if (this.state === "Online" /* Online */) {
21349
- this.setAndBroadcast("Unknown" /* Unknown */);
21909
+ if (this.state === "Online" /* OnlineState.Online */) {
21910
+ this.setAndBroadcast("Unknown" /* OnlineState.Unknown */);
21350
21911
  }
21351
21912
  else {
21352
21913
  this.watchStreamFailures++;
@@ -21354,7 +21915,7 @@ class OnlineStateTracker {
21354
21915
  this.clearOnlineStateTimer();
21355
21916
  this.logClientOfflineWarningIfNecessary(`Connection failed ${MAX_WATCH_STREAM_FAILURES} ` +
21356
21917
  `times. Most recent error: ${error.toString()}`);
21357
- this.setAndBroadcast("Offline" /* Offline */);
21918
+ this.setAndBroadcast("Offline" /* OnlineState.Offline */);
21358
21919
  }
21359
21920
  }
21360
21921
  }
@@ -21368,7 +21929,7 @@ class OnlineStateTracker {
21368
21929
  set(newState) {
21369
21930
  this.clearOnlineStateTimer();
21370
21931
  this.watchStreamFailures = 0;
21371
- if (newState === "Online" /* Online */) {
21932
+ if (newState === "Online" /* OnlineState.Online */) {
21372
21933
  // We've connected to watch at least once. Don't warn the developer
21373
21934
  // about being offline going forward.
21374
21935
  this.shouldWarnClientIsOffline = false;
@@ -21495,7 +22056,7 @@ function newRemoteStore(localStore, datastore, asyncQueue, onlineStateHandler, c
21495
22056
  /** Re-enables the network. Idempotent. */
21496
22057
  function remoteStoreEnableNetwork(remoteStore) {
21497
22058
  const remoteStoreImpl = debugCast(remoteStore);
21498
- remoteStoreImpl.offlineCauses.delete(0 /* UserDisabled */);
22059
+ remoteStoreImpl.offlineCauses.delete(0 /* OfflineCause.UserDisabled */);
21499
22060
  return enableNetworkInternal(remoteStoreImpl);
21500
22061
  }
21501
22062
  async function enableNetworkInternal(remoteStoreImpl) {
@@ -21511,10 +22072,10 @@ async function enableNetworkInternal(remoteStoreImpl) {
21511
22072
  */
21512
22073
  async function remoteStoreDisableNetwork(remoteStore) {
21513
22074
  const remoteStoreImpl = debugCast(remoteStore);
21514
- remoteStoreImpl.offlineCauses.add(0 /* UserDisabled */);
22075
+ remoteStoreImpl.offlineCauses.add(0 /* OfflineCause.UserDisabled */);
21515
22076
  await disableNetworkInternal(remoteStoreImpl);
21516
22077
  // Set the OnlineState to Offline so get()s return from cache, etc.
21517
- remoteStoreImpl.onlineStateTracker.set("Offline" /* Offline */);
22078
+ remoteStoreImpl.onlineStateTracker.set("Offline" /* OnlineState.Offline */);
21518
22079
  }
21519
22080
  async function disableNetworkInternal(remoteStoreImpl) {
21520
22081
  for (const networkStatusHandler of remoteStoreImpl.onNetworkStatusChange) {
@@ -21524,12 +22085,12 @@ async function disableNetworkInternal(remoteStoreImpl) {
21524
22085
  async function remoteStoreShutdown(remoteStore) {
21525
22086
  const remoteStoreImpl = debugCast(remoteStore);
21526
22087
  logDebug(LOG_TAG$5, 'RemoteStore shutting down.');
21527
- remoteStoreImpl.offlineCauses.add(5 /* Shutdown */);
22088
+ remoteStoreImpl.offlineCauses.add(5 /* OfflineCause.Shutdown */);
21528
22089
  await disableNetworkInternal(remoteStoreImpl);
21529
22090
  remoteStoreImpl.connectivityMonitor.shutdown();
21530
22091
  // Set the OnlineState to Unknown (rather than Offline) to avoid potentially
21531
22092
  // triggering spurious listener events with cached data, etc.
21532
- remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
22093
+ remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
21533
22094
  }
21534
22095
  /**
21535
22096
  * Starts new listen for the given target. Uses resume token if provided. It
@@ -21569,7 +22130,7 @@ function remoteStoreUnlisten(remoteStore, targetId) {
21569
22130
  // Revert to OnlineState.Unknown if the watch stream is not open and we
21570
22131
  // have no listeners, since without any listens to send we cannot
21571
22132
  // confirm if the stream is healthy and upgrade to OnlineState.Online.
21572
- remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
22133
+ remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
21573
22134
  }
21574
22135
  }
21575
22136
  }
@@ -21630,14 +22191,14 @@ async function onWatchStreamClose(remoteStoreImpl, error) {
21630
22191
  // No need to restart watch stream because there are no active targets.
21631
22192
  // The online state is set to unknown because there is no active attempt
21632
22193
  // at establishing a connection
21633
- remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
22194
+ remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
21634
22195
  }
21635
22196
  }
21636
22197
  async function onWatchStreamChange(remoteStoreImpl, watchChange, snapshotVersion) {
21637
22198
  // Mark the client as online since we got a message from the server
21638
- remoteStoreImpl.onlineStateTracker.set("Online" /* Online */);
22199
+ remoteStoreImpl.onlineStateTracker.set("Online" /* OnlineState.Online */);
21639
22200
  if (watchChange instanceof WatchTargetChange &&
21640
- watchChange.state === 2 /* Removed */ &&
22201
+ watchChange.state === 2 /* WatchTargetChangeState.Removed */ &&
21641
22202
  watchChange.cause) {
21642
22203
  // There was an error on a target, don't wait for a consistent snapshot
21643
22204
  // to raise events
@@ -21685,10 +22246,10 @@ async function onWatchStreamChange(remoteStoreImpl, watchChange, snapshotVersion
21685
22246
  */
21686
22247
  async function disableNetworkUntilRecovery(remoteStoreImpl, e, op) {
21687
22248
  if (isIndexedDbTransactionError(e)) {
21688
- remoteStoreImpl.offlineCauses.add(1 /* IndexedDbFailed */);
22249
+ remoteStoreImpl.offlineCauses.add(1 /* OfflineCause.IndexedDbFailed */);
21689
22250
  // Disable network and raise offline snapshots
21690
22251
  await disableNetworkInternal(remoteStoreImpl);
21691
- remoteStoreImpl.onlineStateTracker.set("Offline" /* Offline */);
22252
+ remoteStoreImpl.onlineStateTracker.set("Offline" /* OnlineState.Offline */);
21692
22253
  if (!op) {
21693
22254
  // Use a simple read operation to determine if IndexedDB recovered.
21694
22255
  // Ideally, we would expose a health check directly on SimpleDb, but
@@ -21699,7 +22260,7 @@ async function disableNetworkUntilRecovery(remoteStoreImpl, e, op) {
21699
22260
  remoteStoreImpl.asyncQueue.enqueueRetryable(async () => {
21700
22261
  logDebug(LOG_TAG$5, 'Retrying IndexedDB access');
21701
22262
  await op();
21702
- remoteStoreImpl.offlineCauses.delete(1 /* IndexedDbFailed */);
22263
+ remoteStoreImpl.offlineCauses.delete(1 /* OfflineCause.IndexedDbFailed */);
21703
22264
  await enableNetworkInternal(remoteStoreImpl);
21704
22265
  });
21705
22266
  }
@@ -21750,7 +22311,7 @@ function raiseWatchSnapshot(remoteStoreImpl, snapshotVersion) {
21750
22311
  // mismatch, but don't actually retain that in listenTargets. This ensures
21751
22312
  // that we flag the first re-listen this way without impacting future
21752
22313
  // listens of this target (that might happen e.g. on reconnect).
21753
- const requestTargetData = new TargetData(targetData.target, targetId, 1 /* ExistenceFilterMismatch */, targetData.sequenceNumber);
22314
+ const requestTargetData = new TargetData(targetData.target, targetId, 1 /* TargetPurpose.ExistenceFilterMismatch */, targetData.sequenceNumber);
21754
22315
  sendWatchRequest(remoteStoreImpl, requestTargetData);
21755
22316
  });
21756
22317
  return remoteStoreImpl.remoteSyncer.applyRemoteEvent(remoteEvent);
@@ -21881,10 +22442,10 @@ async function handleWriteError(remoteStoreImpl, error) {
21881
22442
  }
21882
22443
  async function restartNetwork(remoteStore) {
21883
22444
  const remoteStoreImpl = debugCast(remoteStore);
21884
- remoteStoreImpl.offlineCauses.add(4 /* ConnectivityChange */);
22445
+ remoteStoreImpl.offlineCauses.add(4 /* OfflineCause.ConnectivityChange */);
21885
22446
  await disableNetworkInternal(remoteStoreImpl);
21886
- remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
21887
- remoteStoreImpl.offlineCauses.delete(4 /* ConnectivityChange */);
22447
+ remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
22448
+ remoteStoreImpl.offlineCauses.delete(4 /* OfflineCause.ConnectivityChange */);
21888
22449
  await enableNetworkInternal(remoteStoreImpl);
21889
22450
  }
21890
22451
  async function remoteStoreHandleCredentialChange(remoteStore, user) {
@@ -21895,14 +22456,14 @@ async function remoteStoreHandleCredentialChange(remoteStore, user) {
21895
22456
  // Tear down and re-create our network streams. This will ensure we get a
21896
22457
  // fresh auth token for the new user and re-fill the write pipeline with
21897
22458
  // new mutations from the LocalStore (since mutations are per-user).
21898
- remoteStoreImpl.offlineCauses.add(3 /* CredentialChange */);
22459
+ remoteStoreImpl.offlineCauses.add(3 /* OfflineCause.CredentialChange */);
21899
22460
  await disableNetworkInternal(remoteStoreImpl);
21900
22461
  if (usesNetwork) {
21901
22462
  // Don't set the network status to Unknown if we are offline.
21902
- remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
22463
+ remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
21903
22464
  }
21904
22465
  await remoteStoreImpl.remoteSyncer.handleCredentialChange(user);
21905
- remoteStoreImpl.offlineCauses.delete(3 /* CredentialChange */);
22466
+ remoteStoreImpl.offlineCauses.delete(3 /* OfflineCause.CredentialChange */);
21906
22467
  await enableNetworkInternal(remoteStoreImpl);
21907
22468
  }
21908
22469
  /**
@@ -21911,13 +22472,13 @@ async function remoteStoreHandleCredentialChange(remoteStore, user) {
21911
22472
  async function remoteStoreApplyPrimaryState(remoteStore, isPrimary) {
21912
22473
  const remoteStoreImpl = debugCast(remoteStore);
21913
22474
  if (isPrimary) {
21914
- remoteStoreImpl.offlineCauses.delete(2 /* IsSecondary */);
22475
+ remoteStoreImpl.offlineCauses.delete(2 /* OfflineCause.IsSecondary */);
21915
22476
  await enableNetworkInternal(remoteStoreImpl);
21916
22477
  }
21917
22478
  else if (!isPrimary) {
21918
- remoteStoreImpl.offlineCauses.add(2 /* IsSecondary */);
22479
+ remoteStoreImpl.offlineCauses.add(2 /* OfflineCause.IsSecondary */);
21919
22480
  await disableNetworkInternal(remoteStoreImpl);
21920
- remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
22481
+ remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
21921
22482
  }
21922
22483
  }
21923
22484
  /**
@@ -21943,7 +22504,7 @@ function ensureWatchStream(remoteStoreImpl) {
21943
22504
  startWatchStream(remoteStoreImpl);
21944
22505
  }
21945
22506
  else {
21946
- remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
22507
+ remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
21947
22508
  }
21948
22509
  }
21949
22510
  else {
@@ -22274,46 +22835,46 @@ class DocumentChangeSet {
22274
22835
  return;
22275
22836
  }
22276
22837
  // Merge the new change with the existing change.
22277
- if (change.type !== 0 /* Added */ &&
22278
- oldChange.type === 3 /* Metadata */) {
22838
+ if (change.type !== 0 /* ChangeType.Added */ &&
22839
+ oldChange.type === 3 /* ChangeType.Metadata */) {
22279
22840
  this.changeMap = this.changeMap.insert(key, change);
22280
22841
  }
22281
- else if (change.type === 3 /* Metadata */ &&
22282
- oldChange.type !== 1 /* Removed */) {
22842
+ else if (change.type === 3 /* ChangeType.Metadata */ &&
22843
+ oldChange.type !== 1 /* ChangeType.Removed */) {
22283
22844
  this.changeMap = this.changeMap.insert(key, {
22284
22845
  type: oldChange.type,
22285
22846
  doc: change.doc
22286
22847
  });
22287
22848
  }
22288
- else if (change.type === 2 /* Modified */ &&
22289
- oldChange.type === 2 /* Modified */) {
22849
+ else if (change.type === 2 /* ChangeType.Modified */ &&
22850
+ oldChange.type === 2 /* ChangeType.Modified */) {
22290
22851
  this.changeMap = this.changeMap.insert(key, {
22291
- type: 2 /* Modified */,
22852
+ type: 2 /* ChangeType.Modified */,
22292
22853
  doc: change.doc
22293
22854
  });
22294
22855
  }
22295
- else if (change.type === 2 /* Modified */ &&
22296
- oldChange.type === 0 /* Added */) {
22856
+ else if (change.type === 2 /* ChangeType.Modified */ &&
22857
+ oldChange.type === 0 /* ChangeType.Added */) {
22297
22858
  this.changeMap = this.changeMap.insert(key, {
22298
- type: 0 /* Added */,
22859
+ type: 0 /* ChangeType.Added */,
22299
22860
  doc: change.doc
22300
22861
  });
22301
22862
  }
22302
- else if (change.type === 1 /* Removed */ &&
22303
- oldChange.type === 0 /* Added */) {
22863
+ else if (change.type === 1 /* ChangeType.Removed */ &&
22864
+ oldChange.type === 0 /* ChangeType.Added */) {
22304
22865
  this.changeMap = this.changeMap.remove(key);
22305
22866
  }
22306
- else if (change.type === 1 /* Removed */ &&
22307
- oldChange.type === 2 /* Modified */) {
22867
+ else if (change.type === 1 /* ChangeType.Removed */ &&
22868
+ oldChange.type === 2 /* ChangeType.Modified */) {
22308
22869
  this.changeMap = this.changeMap.insert(key, {
22309
- type: 1 /* Removed */,
22870
+ type: 1 /* ChangeType.Removed */,
22310
22871
  doc: oldChange.doc
22311
22872
  });
22312
22873
  }
22313
- else if (change.type === 0 /* Added */ &&
22314
- oldChange.type === 1 /* Removed */) {
22874
+ else if (change.type === 0 /* ChangeType.Added */ &&
22875
+ oldChange.type === 1 /* ChangeType.Removed */) {
22315
22876
  this.changeMap = this.changeMap.insert(key, {
22316
- type: 2 /* Modified */,
22877
+ type: 2 /* ChangeType.Modified */,
22317
22878
  doc: change.doc
22318
22879
  });
22319
22880
  }
@@ -22352,7 +22913,7 @@ class ViewSnapshot {
22352
22913
  static fromInitialDocuments(query, documents, mutatedKeys, fromCache, hasCachedResults) {
22353
22914
  const changes = [];
22354
22915
  documents.forEach(doc => {
22355
- changes.push({ type: 0 /* Added */, doc });
22916
+ changes.push({ type: 0 /* ChangeType.Added */, doc });
22356
22917
  });
22357
22918
  return new ViewSnapshot(query, documents, DocumentSet.emptySet(documents), changes, mutatedKeys, fromCache,
22358
22919
  /* syncStateChanged= */ true,
@@ -22418,7 +22979,7 @@ function newEventManager() {
22418
22979
  class EventManagerImpl {
22419
22980
  constructor() {
22420
22981
  this.queries = new ObjectMap(q => canonifyQuery(q), queryEquals);
22421
- this.onlineState = "Unknown" /* Unknown */;
22982
+ this.onlineState = "Unknown" /* OnlineState.Unknown */;
22422
22983
  this.snapshotsInSyncListeners = new Set();
22423
22984
  }
22424
22985
  }
@@ -22549,7 +23110,7 @@ class QueryListener {
22549
23110
  */
22550
23111
  this.raisedInitialEvent = false;
22551
23112
  this.snap = null;
22552
- this.onlineState = "Unknown" /* Unknown */;
23113
+ this.onlineState = "Unknown" /* OnlineState.Unknown */;
22553
23114
  this.options = options || {};
22554
23115
  }
22555
23116
  /**
@@ -22563,7 +23124,7 @@ class QueryListener {
22563
23124
  // Remove the metadata only changes.
22564
23125
  const docChanges = [];
22565
23126
  for (const docChange of snap.docChanges) {
22566
- if (docChange.type !== 3 /* Metadata */) {
23127
+ if (docChange.type !== 3 /* ChangeType.Metadata */) {
22567
23128
  docChanges.push(docChange);
22568
23129
  }
22569
23130
  }
@@ -22606,7 +23167,7 @@ class QueryListener {
22606
23167
  }
22607
23168
  // NOTE: We consider OnlineState.Unknown as online (it should become Offline
22608
23169
  // or Online if we wait long enough).
22609
- const maybeOnline = onlineState !== "Offline" /* Offline */;
23170
+ const maybeOnline = onlineState !== "Offline" /* OnlineState.Offline */;
22610
23171
  // Don't raise the event if we're online, aren't synced yet (checked
22611
23172
  // above) and are waiting for a sync.
22612
23173
  if (this.options.waitForSyncWhenOnline && maybeOnline) {
@@ -22616,7 +23177,7 @@ class QueryListener {
22616
23177
  // or we are offline.
22617
23178
  return (!snap.docs.isEmpty() ||
22618
23179
  snap.hasCachedResults ||
22619
- onlineState === "Offline" /* Offline */);
23180
+ onlineState === "Offline" /* OnlineState.Offline */);
22620
23181
  }
22621
23182
  shouldRaiseEvent(snap) {
22622
23183
  // We don't need to handle includeDocumentMetadataChanges here because
@@ -22675,10 +23236,10 @@ class LocalViewChanges {
22675
23236
  let removedKeys = documentKeySet();
22676
23237
  for (const docChange of viewSnapshot.docChanges) {
22677
23238
  switch (docChange.type) {
22678
- case 0 /* Added */:
23239
+ case 0 /* ChangeType.Added */:
22679
23240
  addedKeys = addedKeys.add(docChange.doc.key);
22680
23241
  break;
22681
- case 1 /* Removed */:
23242
+ case 1 /* ChangeType.Removed */:
22682
23243
  removedKeys = removedKeys.add(docChange.doc.key);
22683
23244
  break;
22684
23245
  // do nothing
@@ -22926,11 +23487,11 @@ class View {
22926
23487
  //
22927
23488
  // Note that this should never get used in a refill (when previousChanges is
22928
23489
  // set), because there will only be adds -- no deletes or updates.
22929
- const lastDocInLimit = this.query.limitType === "F" /* First */ &&
23490
+ const lastDocInLimit = this.query.limitType === "F" /* LimitType.First */ &&
22930
23491
  oldDocumentSet.size === this.query.limit
22931
23492
  ? oldDocumentSet.last()
22932
23493
  : null;
22933
- const firstDocInLimit = this.query.limitType === "L" /* Last */ &&
23494
+ const firstDocInLimit = this.query.limitType === "L" /* LimitType.Last */ &&
22934
23495
  oldDocumentSet.size === this.query.limit
22935
23496
  ? oldDocumentSet.first()
22936
23497
  : null;
@@ -22953,7 +23514,7 @@ class View {
22953
23514
  if (!docsEqual) {
22954
23515
  if (!this.shouldWaitForSyncedDocument(oldDoc, newDoc)) {
22955
23516
  changeSet.track({
22956
- type: 2 /* Modified */,
23517
+ type: 2 /* ChangeType.Modified */,
22957
23518
  doc: newDoc
22958
23519
  });
22959
23520
  changeApplied = true;
@@ -22969,16 +23530,16 @@ class View {
22969
23530
  }
22970
23531
  }
22971
23532
  else if (oldDocHadPendingMutations !== newDocHasPendingMutations) {
22972
- changeSet.track({ type: 3 /* Metadata */, doc: newDoc });
23533
+ changeSet.track({ type: 3 /* ChangeType.Metadata */, doc: newDoc });
22973
23534
  changeApplied = true;
22974
23535
  }
22975
23536
  }
22976
23537
  else if (!oldDoc && newDoc) {
22977
- changeSet.track({ type: 0 /* Added */, doc: newDoc });
23538
+ changeSet.track({ type: 0 /* ChangeType.Added */, doc: newDoc });
22978
23539
  changeApplied = true;
22979
23540
  }
22980
23541
  else if (oldDoc && !newDoc) {
22981
- changeSet.track({ type: 1 /* Removed */, doc: oldDoc });
23542
+ changeSet.track({ type: 1 /* ChangeType.Removed */, doc: oldDoc });
22982
23543
  changeApplied = true;
22983
23544
  if (lastDocInLimit || firstDocInLimit) {
22984
23545
  // A doc was removed from a full limit query. We'll need to
@@ -23006,12 +23567,12 @@ class View {
23006
23567
  // Drop documents out to meet limit/limitToLast requirement.
23007
23568
  if (this.query.limit !== null) {
23008
23569
  while (newDocumentSet.size > this.query.limit) {
23009
- const oldDoc = this.query.limitType === "F" /* First */
23570
+ const oldDoc = this.query.limitType === "F" /* LimitType.First */
23010
23571
  ? newDocumentSet.last()
23011
23572
  : newDocumentSet.first();
23012
23573
  newDocumentSet = newDocumentSet.delete(oldDoc.key);
23013
23574
  newMutatedKeys = newMutatedKeys.delete(oldDoc.key);
23014
- changeSet.track({ type: 1 /* Removed */, doc: oldDoc });
23575
+ changeSet.track({ type: 1 /* ChangeType.Removed */, doc: oldDoc });
23015
23576
  }
23016
23577
  }
23017
23578
  return {
@@ -23059,7 +23620,7 @@ class View {
23059
23620
  ? this.updateLimboDocuments()
23060
23621
  : [];
23061
23622
  const synced = this.limboDocuments.size === 0 && this.current;
23062
- const newSyncState = synced ? 1 /* Synced */ : 0 /* Local */;
23623
+ const newSyncState = synced ? 1 /* SyncState.Synced */ : 0 /* SyncState.Local */;
23063
23624
  const syncStateChanged = newSyncState !== this.syncState;
23064
23625
  this.syncState = newSyncState;
23065
23626
  if (changes.length === 0 && !syncStateChanged) {
@@ -23067,7 +23628,7 @@ class View {
23067
23628
  return { limboChanges };
23068
23629
  }
23069
23630
  else {
23070
- const snap = new ViewSnapshot(this.query, docChanges.documentSet, oldDocs, changes, docChanges.mutatedKeys, newSyncState === 0 /* Local */, syncStateChanged,
23631
+ const snap = new ViewSnapshot(this.query, docChanges.documentSet, oldDocs, changes, docChanges.mutatedKeys, newSyncState === 0 /* SyncState.Local */, syncStateChanged,
23071
23632
  /* excludesMetadataChanges= */ false, targetChange
23072
23633
  ? targetChange.resumeToken.approximateByteSize() > 0
23073
23634
  : false);
@@ -23082,7 +23643,7 @@ class View {
23082
23643
  * ViewChange if the view's syncState changes as a result.
23083
23644
  */
23084
23645
  applyOnlineStateChange(onlineState) {
23085
- if (this.current && onlineState === "Offline" /* Offline */) {
23646
+ if (this.current && onlineState === "Offline" /* OnlineState.Offline */) {
23086
23647
  // If we're offline, set `current` to false and then call applyChanges()
23087
23648
  // to refresh our syncState and generate a ViewChange as appropriate. We
23088
23649
  // are guaranteed to get a new TargetChange that sets `current` back to
@@ -23197,22 +23758,22 @@ class View {
23197
23758
  */
23198
23759
  // PORTING NOTE: Multi-tab only.
23199
23760
  computeInitialSnapshot() {
23200
- return ViewSnapshot.fromInitialDocuments(this.query, this.documentSet, this.mutatedKeys, this.syncState === 0 /* Local */, this.hasCachedResults);
23761
+ return ViewSnapshot.fromInitialDocuments(this.query, this.documentSet, this.mutatedKeys, this.syncState === 0 /* SyncState.Local */, this.hasCachedResults);
23201
23762
  }
23202
23763
  }
23203
23764
  function compareChangeType(c1, c2) {
23204
23765
  const order = (change) => {
23205
23766
  switch (change) {
23206
- case 0 /* Added */:
23767
+ case 0 /* ChangeType.Added */:
23207
23768
  return 1;
23208
- case 2 /* Modified */:
23769
+ case 2 /* ChangeType.Modified */:
23209
23770
  return 2;
23210
- case 3 /* Metadata */:
23771
+ case 3 /* ChangeType.Metadata */:
23211
23772
  // A metadata change is converted to a modified change at the public
23212
23773
  // api layer. Since we sort by document key and then change type,
23213
23774
  // metadata and modified changes must be sorted equivalently.
23214
23775
  return 2;
23215
- case 1 /* Removed */:
23776
+ case 1 /* ChangeType.Removed */:
23216
23777
  return 0;
23217
23778
  default:
23218
23779
  return fail();
@@ -23330,7 +23891,7 @@ class SyncEngineImpl {
23330
23891
  /** Stores user callbacks waiting for all pending writes to be acknowledged. */
23331
23892
  this.pendingWritesCallbacks = new Map();
23332
23893
  this.limboTargetIdGenerator = TargetIdGenerator.forSyncEngine();
23333
- this.onlineState = "Unknown" /* Unknown */;
23894
+ this.onlineState = "Unknown" /* OnlineState.Unknown */;
23334
23895
  // The primary state is set to `true` or `false` immediately after Firestore
23335
23896
  // startup. In the interim, a client should only be considered primary if
23336
23897
  // `isPrimary` is true.
@@ -23394,7 +23955,7 @@ async function initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId,
23394
23955
  /* usePreviousResults= */ true);
23395
23956
  const view = new View(query, queryResult.remoteKeys);
23396
23957
  const viewDocChanges = view.computeDocChanges(queryResult.documents);
23397
- const synthesizedTargetChange = TargetChange.createSynthesizedTargetChangeForCurrentChange(targetId, current && syncEngineImpl.onlineState !== "Offline" /* Offline */, resumeToken);
23958
+ const synthesizedTargetChange = TargetChange.createSynthesizedTargetChangeForCurrentChange(targetId, current && syncEngineImpl.onlineState !== "Offline" /* OnlineState.Offline */, resumeToken);
23398
23959
  const viewChange = view.applyChanges(viewDocChanges,
23399
23960
  /* updateLimboDocuments= */ syncEngineImpl.isPrimaryClient, synthesizedTargetChange);
23400
23961
  updateTrackedLimbos(syncEngineImpl, targetId, viewChange.limboChanges);
@@ -23520,9 +24081,9 @@ function syncEngineApplyOnlineStateChange(syncEngine, onlineState, source) {
23520
24081
  // tab remains online) and only apply the primary tab's online state from
23521
24082
  // SharedClientState.
23522
24083
  if ((syncEngineImpl.isPrimaryClient &&
23523
- source === 0 /* RemoteStore */) ||
24084
+ source === 0 /* OnlineStateSource.RemoteStore */) ||
23524
24085
  (!syncEngineImpl.isPrimaryClient &&
23525
- source === 1 /* SharedClientState */)) {
24086
+ source === 1 /* OnlineStateSource.SharedClientState */)) {
23526
24087
  const newViewSnapshots = [];
23527
24088
  syncEngineImpl.queryViewsByQuery.forEach((query, queryView) => {
23528
24089
  const viewChange = queryView.view.applyOnlineStateChange(onlineState);
@@ -23790,7 +24351,7 @@ function pumpEnqueuedLimboResolutions(syncEngineImpl) {
23790
24351
  syncEngineImpl.activeLimboResolutionsByTarget.set(limboTargetId, new LimboResolution(key));
23791
24352
  syncEngineImpl.activeLimboTargetsByKey =
23792
24353
  syncEngineImpl.activeLimboTargetsByKey.insert(key, limboTargetId);
23793
- remoteStoreListen(syncEngineImpl.remoteStore, new TargetData(queryToTarget(newQueryForPath(key.path)), limboTargetId, 2 /* LimboResolution */, ListenSequence.INVALID));
24354
+ remoteStoreListen(syncEngineImpl.remoteStore, new TargetData(queryToTarget(newQueryForPath(key.path)), limboTargetId, 2 /* TargetPurpose.LimboResolution */, ListenSequence.INVALID));
23794
24355
  }
23795
24356
  }
23796
24357
  async function syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngine, changes, remoteEvent) {
@@ -24043,7 +24604,7 @@ async function synchronizeQueryViewsAndRaiseSnapshots(syncEngine, targets, trans
24043
24604
  */
24044
24605
  // PORTING NOTE: Multi-Tab only.
24045
24606
  function synthesizeTargetToQuery(target) {
24046
- return newQuery(target.path, target.collectionGroup, target.orderBy, target.filters, target.limit, "F" /* First */, target.startAt, target.endAt);
24607
+ return newQuery(target.path, target.collectionGroup, target.orderBy, target.filters, target.limit, "F" /* LimitType.First */, target.startAt, target.endAt);
24047
24608
  }
24048
24609
  /** Returns the IDs of the clients that are currently active. */
24049
24610
  // PORTING NOTE: Multi-Tab only.
@@ -24374,7 +24935,7 @@ class OnlineComponentProvider {
24374
24935
  this.eventManager = this.createEventManager(cfg);
24375
24936
  this.syncEngine = this.createSyncEngine(cfg,
24376
24937
  /* startAsPrimary=*/ !offlineComponentProvider.synchronizeTabs);
24377
- this.sharedClientState.onlineStateHandler = onlineState => syncEngineApplyOnlineStateChange(this.syncEngine, onlineState, 1 /* SharedClientState */);
24938
+ this.sharedClientState.onlineStateHandler = onlineState => syncEngineApplyOnlineStateChange(this.syncEngine, onlineState, 1 /* OnlineStateSource.SharedClientState */);
24378
24939
  this.remoteStore.remoteSyncer.handleCredentialChange =
24379
24940
  syncEngineHandleCredentialChange.bind(null, this.syncEngine);
24380
24941
  await remoteStoreApplyPrimaryState(this.remoteStore, this.syncEngine.isPrimaryClient);
@@ -24388,7 +24949,7 @@ class OnlineComponentProvider {
24388
24949
  return newDatastore(cfg.authCredentials, cfg.appCheckCredentials, connection, serializer);
24389
24950
  }
24390
24951
  createRemoteStore(cfg) {
24391
- return newRemoteStore(this.localStore, this.datastore, cfg.asyncQueue, onlineState => syncEngineApplyOnlineStateChange(this.syncEngine, onlineState, 0 /* RemoteStore */), newConnectivityMonitor());
24952
+ return newRemoteStore(this.localStore, this.datastore, cfg.asyncQueue, onlineState => syncEngineApplyOnlineStateChange(this.syncEngine, onlineState, 0 /* OnlineStateSource.RemoteStore */), newConnectivityMonitor());
24392
24953
  }
24393
24954
  createSyncEngine(cfg, startAsPrimary) {
24394
24955
  return newSyncEngine(this.localStore, this.remoteStore, this.eventManager, this.sharedClientState, cfg.initialUser, cfg.maxConcurrentLimboResolutions, startAsPrimary);
@@ -25145,7 +25706,7 @@ class TransactionRunner {
25145
25706
  this.updateFunction = updateFunction;
25146
25707
  this.deferred = deferred;
25147
25708
  this.attemptsRemaining = options.maxAttempts;
25148
- this.backoff = new ExponentialBackoff(this.asyncQueue, "transaction_retry" /* TransactionRetry */);
25709
+ this.backoff = new ExponentialBackoff(this.asyncQueue, "transaction_retry" /* TimerId.TransactionRetry */);
25149
25710
  }
25150
25711
  /** Runs the transaction and sets the result on deferred. */
25151
25712
  run() {
@@ -26178,7 +26739,7 @@ class AsyncQueueImpl {
26178
26739
  // List of TimerIds to fast-forward delays for.
26179
26740
  this.timerIdsToSkip = [];
26180
26741
  // Backoff timer used to schedule retries for retryable operations
26181
- this.backoff = new ExponentialBackoff(this, "async_queue_retry" /* AsyncQueueRetry */);
26742
+ this.backoff = new ExponentialBackoff(this, "async_queue_retry" /* TimerId.AsyncQueueRetry */);
26182
26743
  // Visibility handler that triggers an immediate retry of all retryable
26183
26744
  // operations. Meant to speed up recovery when we regain file system access
26184
26745
  // after page comes into foreground.
@@ -26348,7 +26909,7 @@ class AsyncQueueImpl {
26348
26909
  this.delayedOperations.sort((a, b) => a.targetTimeMs - b.targetTimeMs);
26349
26910
  for (const op of this.delayedOperations) {
26350
26911
  op.skipDelay();
26351
- if (lastTimerId !== "all" /* All */ && op.timerId === lastTimerId) {
26912
+ if (lastTimerId !== "all" /* TimerId.All */ && op.timerId === lastTimerId) {
26352
26913
  break;
26353
26914
  }
26354
26915
  }
@@ -27261,12 +27822,12 @@ class ParsedUpdateData {
27261
27822
  }
27262
27823
  function isWrite(dataSource) {
27263
27824
  switch (dataSource) {
27264
- case 0 /* Set */: // fall through
27265
- case 2 /* MergeSet */: // fall through
27266
- case 1 /* Update */:
27825
+ case 0 /* UserDataSource.Set */: // fall through
27826
+ case 2 /* UserDataSource.MergeSet */: // fall through
27827
+ case 1 /* UserDataSource.Update */:
27267
27828
  return true;
27268
- case 3 /* Argument */:
27269
- case 4 /* ArrayArgument */:
27829
+ case 3 /* UserDataSource.Argument */:
27830
+ case 4 /* UserDataSource.ArrayArgument */:
27270
27831
  return false;
27271
27832
  default:
27272
27833
  throw fail();
@@ -27391,8 +27952,8 @@ function newUserDataReader(firestore) {
27391
27952
  /** Parse document data from a set() call. */
27392
27953
  function parseSetData(userDataReader, methodName, targetDoc, input, hasConverter, options = {}) {
27393
27954
  const context = userDataReader.createContext(options.merge || options.mergeFields
27394
- ? 2 /* MergeSet */
27395
- : 0 /* Set */, methodName, targetDoc, hasConverter);
27955
+ ? 2 /* UserDataSource.MergeSet */
27956
+ : 0 /* UserDataSource.Set */, methodName, targetDoc, hasConverter);
27396
27957
  validatePlainObject('Data must be an object, but it was:', context, input);
27397
27958
  const updateData = parseObject(input, context);
27398
27959
  let fieldMask;
@@ -27423,12 +27984,12 @@ function parseSetData(userDataReader, methodName, targetDoc, input, hasConverter
27423
27984
  }
27424
27985
  class DeleteFieldValueImpl extends FieldValue {
27425
27986
  _toFieldTransform(context) {
27426
- if (context.dataSource === 2 /* MergeSet */) {
27987
+ if (context.dataSource === 2 /* UserDataSource.MergeSet */) {
27427
27988
  // No transform to add for a delete, but we need to add it to our
27428
27989
  // fieldMask so it gets deleted.
27429
27990
  context.fieldMask.push(context.path);
27430
27991
  }
27431
- else if (context.dataSource === 1 /* Update */) {
27992
+ else if (context.dataSource === 1 /* UserDataSource.Update */) {
27432
27993
  throw context.createError(`${this._methodName}() can only appear at the top level ` +
27433
27994
  'of your update data');
27434
27995
  }
@@ -27461,7 +28022,7 @@ class DeleteFieldValueImpl extends FieldValue {
27461
28022
  */
27462
28023
  function createSentinelChildContext(fieldValue, context, arrayElement) {
27463
28024
  return new ParseContextImpl({
27464
- dataSource: 3 /* Argument */,
28025
+ dataSource: 3 /* UserDataSource.Argument */,
27465
28026
  targetDoc: context.settings.targetDoc,
27466
28027
  methodName: fieldValue._methodName,
27467
28028
  arrayElement
@@ -27525,7 +28086,7 @@ class NumericIncrementFieldValueImpl extends FieldValue {
27525
28086
  }
27526
28087
  /** Parse update data from an update() call. */
27527
28088
  function parseUpdateData(userDataReader, methodName, targetDoc, input) {
27528
- const context = userDataReader.createContext(1 /* Update */, methodName, targetDoc);
28089
+ const context = userDataReader.createContext(1 /* UserDataSource.Update */, methodName, targetDoc);
27529
28090
  validatePlainObject('Data must be an object, but it was:', context, input);
27530
28091
  const fieldMaskPaths = [];
27531
28092
  const updateData = ObjectValue.empty();
@@ -27552,7 +28113,7 @@ function parseUpdateData(userDataReader, methodName, targetDoc, input) {
27552
28113
  }
27553
28114
  /** Parse update data from a list of field/value arguments. */
27554
28115
  function parseUpdateVarargs(userDataReader, methodName, targetDoc, field, value, moreFieldsAndValues) {
27555
- const context = userDataReader.createContext(1 /* Update */, methodName, targetDoc);
28116
+ const context = userDataReader.createContext(1 /* UserDataSource.Update */, methodName, targetDoc);
27556
28117
  const keys = [fieldPathFromArgument$1(methodName, field, targetDoc)];
27557
28118
  const values = [value];
27558
28119
  if (moreFieldsAndValues.length % 2 !== 0) {
@@ -27599,7 +28160,7 @@ function parseUpdateVarargs(userDataReader, methodName, targetDoc, field, value,
27599
28160
  * contain additional arrays (e.g. the operand of an `in` query).
27600
28161
  */
27601
28162
  function parseQueryValue(userDataReader, methodName, input, allowArrays = false) {
27602
- const context = userDataReader.createContext(allowArrays ? 4 /* ArrayArgument */ : 3 /* Argument */, methodName);
28163
+ const context = userDataReader.createContext(allowArrays ? 4 /* UserDataSource.ArrayArgument */ : 3 /* UserDataSource.Argument */, methodName);
27603
28164
  const parsed = parseData(input, context);
27604
28165
  return parsed;
27605
28166
  }
@@ -27649,7 +28210,7 @@ function parseData(input, context) {
27649
28210
  // contain additional arrays (each representing an individual field
27650
28211
  // value), so we disable this validation.
27651
28212
  if (context.settings.arrayElement &&
27652
- context.dataSource !== 4 /* ArrayArgument */) {
28213
+ context.dataSource !== 4 /* UserDataSource.ArrayArgument */) {
27653
28214
  throw context.createError('Nested arrays are not supported');
27654
28215
  }
27655
28216
  return parseArray(input, context);
@@ -28031,164 +28592,336 @@ function fieldPathFromArgument(methodName, arg) {
28031
28592
  * limitations under the License.
28032
28593
  */
28033
28594
  function validateHasExplicitOrderByForLimitToLast(query) {
28034
- if (query.limitType === "L" /* Last */ &&
28595
+ if (query.limitType === "L" /* LimitType.Last */ &&
28035
28596
  query.explicitOrderBy.length === 0) {
28036
28597
  throw new FirestoreError(Code.UNIMPLEMENTED, 'limitToLast() queries require specifying at least one orderBy() clause');
28037
28598
  }
28038
28599
  }
28600
+ /**
28601
+ * An `AppliableConstraint` is an abstraction of a constraint that can be applied
28602
+ * to a Firestore query.
28603
+ */
28604
+ class AppliableConstraint {
28605
+ }
28039
28606
  /**
28040
28607
  * A `QueryConstraint` is used to narrow the set of documents returned by a
28041
28608
  * Firestore query. `QueryConstraint`s are created by invoking {@link where},
28042
- * {@link orderBy}, {@link (startAt:1)}, {@link (startAfter:1)}, {@link
28043
- * endBefore:1}, {@link (endAt:1)}, {@link limit} or {@link limitToLast} and
28609
+ * {@link orderBy}, {@link startAt}, {@link startAfter}, {@link
28610
+ * endBefore}, {@link endAt}, {@link limit}, {@link limitToLast} and
28044
28611
  * can then be passed to {@link query} to create a new query instance that
28045
28612
  * also contains this `QueryConstraint`.
28046
28613
  */
28047
- class QueryConstraint {
28614
+ class QueryConstraint extends AppliableConstraint {
28048
28615
  }
28049
- /**
28050
- * Creates a new immutable instance of {@link Query} that is extended to also include
28051
- * additional query constraints.
28052
- *
28053
- * @param query - The {@link Query} instance to use as a base for the new constraints.
28054
- * @param queryConstraints - The list of {@link QueryConstraint}s to apply.
28055
- * @throws if any of the provided query constraints cannot be combined with the
28056
- * existing or new constraints.
28057
- */
28058
- function query(query, ...queryConstraints) {
28616
+ function query(query, queryConstraint, ...additionalQueryConstraints) {
28617
+ let queryConstraints = [];
28618
+ if (queryConstraint instanceof AppliableConstraint) {
28619
+ queryConstraints.push(queryConstraint);
28620
+ }
28621
+ queryConstraints = queryConstraints.concat(additionalQueryConstraints);
28622
+ validateQueryConstraintArray(queryConstraints);
28059
28623
  for (const constraint of queryConstraints) {
28060
28624
  query = constraint._apply(query);
28061
28625
  }
28062
28626
  return query;
28063
28627
  }
28064
- class QueryFilterConstraint extends QueryConstraint {
28628
+ /**
28629
+ * A `QueryFieldFilterConstraint` is used to narrow the set of documents returned by
28630
+ * a Firestore query by filtering on one or more document fields.
28631
+ * `QueryFieldFilterConstraint`s are created by invoking {@link where} and can then
28632
+ * be passed to {@link query} to create a new query instance that also contains
28633
+ * this `QueryFieldFilterConstraint`.
28634
+ */
28635
+ class QueryFieldFilterConstraint extends QueryConstraint {
28636
+ /**
28637
+ * @internal
28638
+ */
28065
28639
  constructor(_field, _op, _value) {
28066
28640
  super();
28067
28641
  this._field = _field;
28068
28642
  this._op = _op;
28069
28643
  this._value = _value;
28644
+ /** The type of this query constraint */
28070
28645
  this.type = 'where';
28071
28646
  }
28647
+ static _create(_field, _op, _value) {
28648
+ return new QueryFieldFilterConstraint(_field, _op, _value);
28649
+ }
28072
28650
  _apply(query) {
28651
+ const filter = this._parse(query);
28652
+ validateNewFieldFilter(query._query, filter);
28653
+ return new Query(query.firestore, query.converter, queryWithAddedFilter(query._query, filter));
28654
+ }
28655
+ _parse(query) {
28073
28656
  const reader = newUserDataReader(query.firestore);
28074
28657
  const filter = newQueryFilter(query._query, 'where', reader, query.firestore._databaseId, this._field, this._op, this._value);
28075
- return new Query(query.firestore, query.converter, queryWithAddedFilter(query._query, filter));
28658
+ return filter;
28076
28659
  }
28077
28660
  }
28078
28661
  /**
28079
- * Creates a {@link QueryConstraint} that enforces that documents must contain the
28080
- * specified field and that the value should satisfy the relation constraint
28081
- * provided.
28662
+ * Creates a {@link QueryFieldFilterConstraint} that enforces that documents
28663
+ * must contain the specified field and that the value should satisfy the
28664
+ * relation constraint provided.
28082
28665
  *
28083
28666
  * @param fieldPath - The path to compare
28084
28667
  * @param opStr - The operation string (e.g "&lt;", "&lt;=", "==", "&lt;",
28085
28668
  * "&lt;=", "!=").
28086
28669
  * @param value - The value for comparison
28087
- * @returns The created {@link Query}.
28670
+ * @returns The created {@link QueryFieldFilterConstraint}.
28088
28671
  */
28089
28672
  function where(fieldPath, opStr, value) {
28090
28673
  const op = opStr;
28091
28674
  const field = fieldPathFromArgument('where', fieldPath);
28092
- return new QueryFilterConstraint(field, op, value);
28675
+ return QueryFieldFilterConstraint._create(field, op, value);
28676
+ }
28677
+ /**
28678
+ * A `QueryCompositeFilterConstraint` is used to narrow the set of documents
28679
+ * returned by a Firestore query by performing the logical OR or AND of multiple
28680
+ * {@link QueryFieldFilterConstraint}s or {@link QueryCompositeFilterConstraint}s.
28681
+ * `QueryCompositeFilterConstraint`s are created by invoking {@link or} or
28682
+ * {@link and} and can then be passed to {@link query} to create a new query
28683
+ * instance that also contains the `QueryCompositeFilterConstraint`.
28684
+ * @internal TODO remove this internal tag with OR Query support in the server
28685
+ */
28686
+ class QueryCompositeFilterConstraint extends AppliableConstraint {
28687
+ /**
28688
+ * @internal
28689
+ */
28690
+ constructor(
28691
+ /** The type of this query constraint */
28692
+ type, _queryConstraints) {
28693
+ super();
28694
+ this.type = type;
28695
+ this._queryConstraints = _queryConstraints;
28696
+ }
28697
+ static _create(type, _queryConstraints) {
28698
+ return new QueryCompositeFilterConstraint(type, _queryConstraints);
28699
+ }
28700
+ _parse(query) {
28701
+ const parsedFilters = this._queryConstraints
28702
+ .map(queryConstraint => {
28703
+ return queryConstraint._parse(query);
28704
+ })
28705
+ .filter(parsedFilter => parsedFilter.getFilters().length > 0);
28706
+ if (parsedFilters.length === 1) {
28707
+ return parsedFilters[0];
28708
+ }
28709
+ return CompositeFilter.create(parsedFilters, this._getOperator());
28710
+ }
28711
+ _apply(query) {
28712
+ const parsedFilter = this._parse(query);
28713
+ if (parsedFilter.getFilters().length === 0) {
28714
+ // Return the existing query if not adding any more filters (e.g. an empty
28715
+ // composite filter).
28716
+ return query;
28717
+ }
28718
+ validateNewFilter(query._query, parsedFilter);
28719
+ return new Query(query.firestore, query.converter, queryWithAddedFilter(query._query, parsedFilter));
28720
+ }
28721
+ _getQueryConstraints() {
28722
+ return this._queryConstraints;
28723
+ }
28724
+ _getOperator() {
28725
+ return this.type === 'and' ? "and" /* CompositeOperator.AND */ : "or" /* CompositeOperator.OR */;
28726
+ }
28727
+ }
28728
+ /**
28729
+ * Creates a new {@link QueryCompositeFilterConstraint} that is a disjunction of
28730
+ * the given filter constraints. A disjunction filter includes a document if it
28731
+ * satisfies any of the given filters.
28732
+ *
28733
+ * @param queryConstraints - Optional. The list of
28734
+ * {@link QueryFilterConstraint}s to perform a disjunction for. These must be
28735
+ * created with calls to {@link where}, {@link or}, or {@link and}.
28736
+ * @returns The newly created {@link QueryCompositeFilterConstraint}.
28737
+ * @internal TODO remove this internal tag with OR Query support in the server
28738
+ */
28739
+ function or(...queryConstraints) {
28740
+ // Only support QueryFilterConstraints
28741
+ queryConstraints.forEach(queryConstraint => validateQueryFilterConstraint('or', queryConstraint));
28742
+ return QueryCompositeFilterConstraint._create("or" /* CompositeOperator.OR */, queryConstraints);
28743
+ }
28744
+ /**
28745
+ * Creates a new {@link QueryCompositeFilterConstraint} that is a conjunction of
28746
+ * the given filter constraints. A conjunction filter includes a document if it
28747
+ * satisfies all of the given filters.
28748
+ *
28749
+ * @param queryConstraints - Optional. The list of
28750
+ * {@link QueryFilterConstraint}s to perform a conjunction for. These must be
28751
+ * created with calls to {@link where}, {@link or}, or {@link and}.
28752
+ * @returns The newly created {@link QueryCompositeFilterConstraint}.
28753
+ * @internal TODO remove this internal tag with OR Query support in the server
28754
+ */
28755
+ function and(...queryConstraints) {
28756
+ // Only support QueryFilterConstraints
28757
+ queryConstraints.forEach(queryConstraint => validateQueryFilterConstraint('and', queryConstraint));
28758
+ return QueryCompositeFilterConstraint._create("and" /* CompositeOperator.AND */, queryConstraints);
28093
28759
  }
28760
+ /**
28761
+ * A `QueryOrderByConstraint` is used to sort the set of documents returned by a
28762
+ * Firestore query. `QueryOrderByConstraint`s are created by invoking
28763
+ * {@link orderBy} and can then be passed to {@link query} to create a new query
28764
+ * instance that also contains this `QueryOrderByConstraint`.
28765
+ *
28766
+ * Note: Documents that do not contain the orderBy field will not be present in
28767
+ * the query result.
28768
+ */
28094
28769
  class QueryOrderByConstraint extends QueryConstraint {
28770
+ /**
28771
+ * @internal
28772
+ */
28095
28773
  constructor(_field, _direction) {
28096
28774
  super();
28097
28775
  this._field = _field;
28098
28776
  this._direction = _direction;
28777
+ /** The type of this query constraint */
28099
28778
  this.type = 'orderBy';
28100
28779
  }
28780
+ static _create(_field, _direction) {
28781
+ return new QueryOrderByConstraint(_field, _direction);
28782
+ }
28101
28783
  _apply(query) {
28102
28784
  const orderBy = newQueryOrderBy(query._query, this._field, this._direction);
28103
28785
  return new Query(query.firestore, query.converter, queryWithAddedOrderBy(query._query, orderBy));
28104
28786
  }
28105
28787
  }
28106
28788
  /**
28107
- * Creates a {@link QueryConstraint} that sorts the query result by the
28789
+ * Creates a {@link QueryOrderByConstraint} that sorts the query result by the
28108
28790
  * specified field, optionally in descending order instead of ascending.
28109
28791
  *
28792
+ * Note: Documents that do not contain the specified field will not be present
28793
+ * in the query result.
28794
+ *
28110
28795
  * @param fieldPath - The field to sort by.
28111
28796
  * @param directionStr - Optional direction to sort by ('asc' or 'desc'). If
28112
28797
  * not specified, order will be ascending.
28113
- * @returns The created {@link Query}.
28798
+ * @returns The created {@link QueryOrderByConstraint}.
28114
28799
  */
28115
28800
  function orderBy(fieldPath, directionStr = 'asc') {
28116
28801
  const direction = directionStr;
28117
28802
  const path = fieldPathFromArgument('orderBy', fieldPath);
28118
- return new QueryOrderByConstraint(path, direction);
28803
+ return QueryOrderByConstraint._create(path, direction);
28119
28804
  }
28805
+ /**
28806
+ * A `QueryLimitConstraint` is used to limit the number of documents returned by
28807
+ * a Firestore query.
28808
+ * `QueryLimitConstraint`s are created by invoking {@link limit} or
28809
+ * {@link limitToLast} and can then be passed to {@link query} to create a new
28810
+ * query instance that also contains this `QueryLimitConstraint`.
28811
+ */
28120
28812
  class QueryLimitConstraint extends QueryConstraint {
28121
- constructor(type, _limit, _limitType) {
28813
+ /**
28814
+ * @internal
28815
+ */
28816
+ constructor(
28817
+ /** The type of this query constraint */
28818
+ type, _limit, _limitType) {
28122
28819
  super();
28123
28820
  this.type = type;
28124
28821
  this._limit = _limit;
28125
28822
  this._limitType = _limitType;
28126
28823
  }
28824
+ static _create(type, _limit, _limitType) {
28825
+ return new QueryLimitConstraint(type, _limit, _limitType);
28826
+ }
28127
28827
  _apply(query) {
28128
28828
  return new Query(query.firestore, query.converter, queryWithLimit(query._query, this._limit, this._limitType));
28129
28829
  }
28130
28830
  }
28131
28831
  /**
28132
- * Creates a {@link QueryConstraint} that only returns the first matching documents.
28832
+ * Creates a {@link QueryLimitConstraint} that only returns the first matching
28833
+ * documents.
28133
28834
  *
28134
28835
  * @param limit - The maximum number of items to return.
28135
- * @returns The created {@link Query}.
28836
+ * @returns The created {@link QueryLimitConstraint}.
28136
28837
  */
28137
28838
  function limit(limit) {
28138
28839
  validatePositiveNumber('limit', limit);
28139
- return new QueryLimitConstraint('limit', limit, "F" /* First */);
28840
+ return QueryLimitConstraint._create('limit', limit, "F" /* LimitType.First */);
28140
28841
  }
28141
28842
  /**
28142
- * Creates a {@link QueryConstraint} that only returns the last matching documents.
28843
+ * Creates a {@link QueryLimitConstraint} that only returns the last matching
28844
+ * documents.
28143
28845
  *
28144
28846
  * You must specify at least one `orderBy` clause for `limitToLast` queries,
28145
28847
  * otherwise an exception will be thrown during execution.
28146
28848
  *
28147
28849
  * @param limit - The maximum number of items to return.
28148
- * @returns The created {@link Query}.
28850
+ * @returns The created {@link QueryLimitConstraint}.
28149
28851
  */
28150
28852
  function limitToLast(limit) {
28151
28853
  validatePositiveNumber('limitToLast', limit);
28152
- return new QueryLimitConstraint('limitToLast', limit, "L" /* Last */);
28854
+ return QueryLimitConstraint._create('limitToLast', limit, "L" /* LimitType.Last */);
28153
28855
  }
28856
+ /**
28857
+ * A `QueryStartAtConstraint` is used to exclude documents from the start of a
28858
+ * result set returned by a Firestore query.
28859
+ * `QueryStartAtConstraint`s are created by invoking {@link (startAt:1)} or
28860
+ * {@link (startAfter:1)} and can then be passed to {@link query} to create a
28861
+ * new query instance that also contains this `QueryStartAtConstraint`.
28862
+ */
28154
28863
  class QueryStartAtConstraint extends QueryConstraint {
28155
- constructor(type, _docOrFields, _inclusive) {
28864
+ /**
28865
+ * @internal
28866
+ */
28867
+ constructor(
28868
+ /** The type of this query constraint */
28869
+ type, _docOrFields, _inclusive) {
28156
28870
  super();
28157
28871
  this.type = type;
28158
28872
  this._docOrFields = _docOrFields;
28159
28873
  this._inclusive = _inclusive;
28160
28874
  }
28875
+ static _create(type, _docOrFields, _inclusive) {
28876
+ return new QueryStartAtConstraint(type, _docOrFields, _inclusive);
28877
+ }
28161
28878
  _apply(query) {
28162
28879
  const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
28163
28880
  return new Query(query.firestore, query.converter, queryWithStartAt(query._query, bound));
28164
28881
  }
28165
28882
  }
28166
28883
  function startAt(...docOrFields) {
28167
- return new QueryStartAtConstraint('startAt', docOrFields,
28884
+ return QueryStartAtConstraint._create('startAt', docOrFields,
28168
28885
  /*inclusive=*/ true);
28169
28886
  }
28170
28887
  function startAfter(...docOrFields) {
28171
- return new QueryStartAtConstraint('startAfter', docOrFields,
28888
+ return QueryStartAtConstraint._create('startAfter', docOrFields,
28172
28889
  /*inclusive=*/ false);
28173
28890
  }
28891
+ /**
28892
+ * A `QueryEndAtConstraint` is used to exclude documents from the end of a
28893
+ * result set returned by a Firestore query.
28894
+ * `QueryEndAtConstraint`s are created by invoking {@link (endAt:1)} or
28895
+ * {@link (endBefore:1)} and can then be passed to {@link query} to create a new
28896
+ * query instance that also contains this `QueryEndAtConstraint`.
28897
+ */
28174
28898
  class QueryEndAtConstraint extends QueryConstraint {
28175
- constructor(type, _docOrFields, _inclusive) {
28899
+ /**
28900
+ * @internal
28901
+ */
28902
+ constructor(
28903
+ /** The type of this query constraint */
28904
+ type, _docOrFields, _inclusive) {
28176
28905
  super();
28177
28906
  this.type = type;
28178
28907
  this._docOrFields = _docOrFields;
28179
28908
  this._inclusive = _inclusive;
28180
28909
  }
28910
+ static _create(type, _docOrFields, _inclusive) {
28911
+ return new QueryEndAtConstraint(type, _docOrFields, _inclusive);
28912
+ }
28181
28913
  _apply(query) {
28182
28914
  const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
28183
28915
  return new Query(query.firestore, query.converter, queryWithEndAt(query._query, bound));
28184
28916
  }
28185
28917
  }
28186
28918
  function endBefore(...docOrFields) {
28187
- return new QueryEndAtConstraint('endBefore', docOrFields,
28919
+ return QueryEndAtConstraint._create('endBefore', docOrFields,
28188
28920
  /*inclusive=*/ false);
28189
28921
  }
28190
28922
  function endAt(...docOrFields) {
28191
- return new QueryEndAtConstraint('endAt', docOrFields, /*inclusive=*/ true);
28923
+ return QueryEndAtConstraint._create('endAt', docOrFields,
28924
+ /*inclusive=*/ true);
28192
28925
  }
28193
28926
  /** Helper function to create a bound from a document or fields */
28194
28927
  function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive) {
@@ -28204,10 +28937,10 @@ function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive)
28204
28937
  function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op, value) {
28205
28938
  let fieldValue;
28206
28939
  if (fieldPath.isKeyField()) {
28207
- if (op === "array-contains" /* ARRAY_CONTAINS */ || op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
28940
+ if (op === "array-contains" /* Operator.ARRAY_CONTAINS */ || op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
28208
28941
  throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid Query. You can't perform '${op}' queries on documentId().`);
28209
28942
  }
28210
- else if (op === "in" /* IN */ || op === "not-in" /* NOT_IN */) {
28943
+ else if (op === "in" /* Operator.IN */ || op === "not-in" /* Operator.NOT_IN */) {
28211
28944
  validateDisjunctiveFilterElements(value, op);
28212
28945
  const referenceList = [];
28213
28946
  for (const arrayValue of value) {
@@ -28220,16 +28953,15 @@ function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op
28220
28953
  }
28221
28954
  }
28222
28955
  else {
28223
- if (op === "in" /* IN */ ||
28224
- op === "not-in" /* NOT_IN */ ||
28225
- op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
28956
+ if (op === "in" /* Operator.IN */ ||
28957
+ op === "not-in" /* Operator.NOT_IN */ ||
28958
+ op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
28226
28959
  validateDisjunctiveFilterElements(value, op);
28227
28960
  }
28228
28961
  fieldValue = parseQueryValue(dataReader, methodName, value,
28229
- /* allowArrays= */ op === "in" /* IN */ || op === "not-in" /* NOT_IN */);
28962
+ /* allowArrays= */ op === "in" /* Operator.IN */ || op === "not-in" /* Operator.NOT_IN */);
28230
28963
  }
28231
28964
  const filter = FieldFilter.create(fieldPath, op, fieldValue);
28232
- validateNewFilter(query, filter);
28233
28965
  return filter;
28234
28966
  }
28235
28967
  function newQueryOrderBy(query, fieldPath, direction) {
@@ -28399,62 +29131,84 @@ function validateDisjunctiveFilterElements(value, operator) {
28399
29131
  */
28400
29132
  function conflictingOps(op) {
28401
29133
  switch (op) {
28402
- case "!=" /* NOT_EQUAL */:
28403
- return ["!=" /* NOT_EQUAL */, "not-in" /* NOT_IN */];
28404
- case "array-contains" /* ARRAY_CONTAINS */:
29134
+ case "!=" /* Operator.NOT_EQUAL */:
29135
+ return ["!=" /* Operator.NOT_EQUAL */, "not-in" /* Operator.NOT_IN */];
29136
+ case "array-contains" /* Operator.ARRAY_CONTAINS */:
28405
29137
  return [
28406
- "array-contains" /* ARRAY_CONTAINS */,
28407
- "array-contains-any" /* ARRAY_CONTAINS_ANY */,
28408
- "not-in" /* NOT_IN */
29138
+ "array-contains" /* Operator.ARRAY_CONTAINS */,
29139
+ "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
29140
+ "not-in" /* Operator.NOT_IN */
28409
29141
  ];
28410
- case "in" /* IN */:
28411
- return ["array-contains-any" /* ARRAY_CONTAINS_ANY */, "in" /* IN */, "not-in" /* NOT_IN */];
28412
- case "array-contains-any" /* ARRAY_CONTAINS_ANY */:
29142
+ case "in" /* Operator.IN */:
29143
+ return ["array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */, "in" /* Operator.IN */, "not-in" /* Operator.NOT_IN */];
29144
+ case "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */:
28413
29145
  return [
28414
- "array-contains" /* ARRAY_CONTAINS */,
28415
- "array-contains-any" /* ARRAY_CONTAINS_ANY */,
28416
- "in" /* IN */,
28417
- "not-in" /* NOT_IN */
29146
+ "array-contains" /* Operator.ARRAY_CONTAINS */,
29147
+ "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
29148
+ "in" /* Operator.IN */,
29149
+ "not-in" /* Operator.NOT_IN */
28418
29150
  ];
28419
- case "not-in" /* NOT_IN */:
29151
+ case "not-in" /* Operator.NOT_IN */:
28420
29152
  return [
28421
- "array-contains" /* ARRAY_CONTAINS */,
28422
- "array-contains-any" /* ARRAY_CONTAINS_ANY */,
28423
- "in" /* IN */,
28424
- "not-in" /* NOT_IN */,
28425
- "!=" /* NOT_EQUAL */
29153
+ "array-contains" /* Operator.ARRAY_CONTAINS */,
29154
+ "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
29155
+ "in" /* Operator.IN */,
29156
+ "not-in" /* Operator.NOT_IN */,
29157
+ "!=" /* Operator.NOT_EQUAL */
28426
29158
  ];
28427
29159
  default:
28428
29160
  return [];
28429
29161
  }
28430
29162
  }
28431
- function validateNewFilter(query, filter) {
28432
- if (filter.isInequality()) {
28433
- const existingField = getInequalityFilterField(query);
28434
- if (existingField !== null && !existingField.isEqual(filter.field)) {
29163
+ function validateNewFieldFilter(query, fieldFilter) {
29164
+ if (fieldFilter.isInequality()) {
29165
+ const existingInequality = getInequalityFilterField(query);
29166
+ const newInequality = fieldFilter.field;
29167
+ if (existingInequality !== null &&
29168
+ !existingInequality.isEqual(newInequality)) {
28435
29169
  throw new FirestoreError(Code.INVALID_ARGUMENT, 'Invalid query. All where filters with an inequality' +
28436
29170
  ' (<, <=, !=, not-in, >, or >=) must be on the same field. But you have' +
28437
- ` inequality filters on '${existingField.toString()}'` +
28438
- ` and '${filter.field.toString()}'`);
29171
+ ` inequality filters on '${existingInequality.toString()}'` +
29172
+ ` and '${newInequality.toString()}'`);
28439
29173
  }
28440
29174
  const firstOrderByField = getFirstOrderByField(query);
28441
29175
  if (firstOrderByField !== null) {
28442
- validateOrderByAndInequalityMatch(query, filter.field, firstOrderByField);
29176
+ validateOrderByAndInequalityMatch(query, newInequality, firstOrderByField);
28443
29177
  }
28444
29178
  }
28445
- const conflictingOp = findFilterOperator(query, conflictingOps(filter.op));
29179
+ const conflictingOp = findOpInsideFilters(query.filters, conflictingOps(fieldFilter.op));
28446
29180
  if (conflictingOp !== null) {
28447
29181
  // Special case when it's a duplicate op to give a slightly clearer error message.
28448
- if (conflictingOp === filter.op) {
29182
+ if (conflictingOp === fieldFilter.op) {
28449
29183
  throw new FirestoreError(Code.INVALID_ARGUMENT, 'Invalid query. You cannot use more than one ' +
28450
- `'${filter.op.toString()}' filter.`);
29184
+ `'${fieldFilter.op.toString()}' filter.`);
28451
29185
  }
28452
29186
  else {
28453
- throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. You cannot use '${filter.op.toString()}' filters ` +
29187
+ throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. You cannot use '${fieldFilter.op.toString()}' filters ` +
28454
29188
  `with '${conflictingOp.toString()}' filters.`);
28455
29189
  }
28456
29190
  }
28457
29191
  }
29192
+ function validateNewFilter(query, filter) {
29193
+ let testQuery = query;
29194
+ const subFilters = filter.getFlattenedFilters();
29195
+ for (const subFilter of subFilters) {
29196
+ validateNewFieldFilter(testQuery, subFilter);
29197
+ testQuery = queryWithAddedFilter(testQuery, subFilter);
29198
+ }
29199
+ }
29200
+ // Checks if any of the provided filter operators are included in the given list of filters and
29201
+ // returns the first one that is, or null if none are.
29202
+ function findOpInsideFilters(filters, operators) {
29203
+ for (const filter of filters) {
29204
+ for (const fieldFilter of filter.getFlattenedFilters()) {
29205
+ if (operators.indexOf(fieldFilter.op) >= 0) {
29206
+ return fieldFilter.op;
29207
+ }
29208
+ }
29209
+ }
29210
+ return null;
29211
+ }
28458
29212
  function validateNewOrderBy(query, orderBy) {
28459
29213
  if (getFirstOrderByField(query) === null) {
28460
29214
  // This is the first order by. It must match any inequality.
@@ -28472,6 +29226,24 @@ function validateOrderByAndInequalityMatch(baseQuery, inequality, orderBy) {
28472
29226
  `as your first argument to orderBy(), but your first orderBy() ` +
28473
29227
  `is on field '${orderBy.toString()}' instead.`);
28474
29228
  }
29229
+ }
29230
+ function validateQueryFilterConstraint(functionName, queryConstraint) {
29231
+ if (!(queryConstraint instanceof QueryFieldFilterConstraint) &&
29232
+ !(queryConstraint instanceof QueryCompositeFilterConstraint)) {
29233
+ throw new FirestoreError(Code.INVALID_ARGUMENT, `Function ${functionName}() requires AppliableConstraints created with a call to 'where(...)', 'or(...)', or 'and(...)'.`);
29234
+ }
29235
+ }
29236
+ function validateQueryConstraintArray(queryConstraint) {
29237
+ const compositeFilterCount = queryConstraint.filter(filter => filter instanceof QueryCompositeFilterConstraint).length;
29238
+ const fieldFilterCount = queryConstraint.filter(filter => filter instanceof QueryFieldFilterConstraint).length;
29239
+ if (compositeFilterCount > 1 ||
29240
+ (compositeFilterCount > 0 && fieldFilterCount > 0)) {
29241
+ throw new FirestoreError(Code.INVALID_ARGUMENT, 'InvalidQuery. When using composite filters, you cannot use ' +
29242
+ 'more than one filter at the top level. Consider nesting the multiple ' +
29243
+ 'filters within an `and(...)` statement. For example: ' +
29244
+ 'change `query(query, where(...), or(...))` to ' +
29245
+ '`query(query, and(where(...), or(...)))`.');
29246
+ }
28475
29247
  }
28476
29248
 
28477
29249
  /**
@@ -28499,27 +29271,27 @@ function validateOrderByAndInequalityMatch(baseQuery, inequality, orderBy) {
28499
29271
  class AbstractUserDataWriter {
28500
29272
  convertValue(value, serverTimestampBehavior = 'none') {
28501
29273
  switch (typeOrder(value)) {
28502
- case 0 /* NullValue */:
29274
+ case 0 /* TypeOrder.NullValue */:
28503
29275
  return null;
28504
- case 1 /* BooleanValue */:
29276
+ case 1 /* TypeOrder.BooleanValue */:
28505
29277
  return value.booleanValue;
28506
- case 2 /* NumberValue */:
29278
+ case 2 /* TypeOrder.NumberValue */:
28507
29279
  return normalizeNumber(value.integerValue || value.doubleValue);
28508
- case 3 /* TimestampValue */:
29280
+ case 3 /* TypeOrder.TimestampValue */:
28509
29281
  return this.convertTimestamp(value.timestampValue);
28510
- case 4 /* ServerTimestampValue */:
29282
+ case 4 /* TypeOrder.ServerTimestampValue */:
28511
29283
  return this.convertServerTimestamp(value, serverTimestampBehavior);
28512
- case 5 /* StringValue */:
29284
+ case 5 /* TypeOrder.StringValue */:
28513
29285
  return value.stringValue;
28514
- case 6 /* BlobValue */:
29286
+ case 6 /* TypeOrder.BlobValue */:
28515
29287
  return this.convertBytes(normalizeByteString(value.bytesValue));
28516
- case 7 /* RefValue */:
29288
+ case 7 /* TypeOrder.RefValue */:
28517
29289
  return this.convertReference(value.referenceValue);
28518
- case 8 /* GeoPointValue */:
29290
+ case 8 /* TypeOrder.GeoPointValue */:
28519
29291
  return this.convertGeoPoint(value.geoPointValue);
28520
- case 9 /* ArrayValue */:
29292
+ case 9 /* TypeOrder.ArrayValue */:
28521
29293
  return this.convertArray(value.arrayValue, serverTimestampBehavior);
28522
- case 10 /* ObjectValue */:
29294
+ case 10 /* TypeOrder.ObjectValue */:
28523
29295
  return this.convertObject(value.mapValue, serverTimestampBehavior);
28524
29296
  default:
28525
29297
  throw fail();
@@ -28861,16 +29633,16 @@ function changesFromSnapshot(querySnapshot, includeMetadataChanges) {
28861
29633
  // to lookup the index of a document.
28862
29634
  let indexTracker = querySnapshot._snapshot.oldDocs;
28863
29635
  return querySnapshot._snapshot.docChanges
28864
- .filter(change => includeMetadataChanges || change.type !== 3 /* Metadata */)
29636
+ .filter(change => includeMetadataChanges || change.type !== 3 /* ChangeType.Metadata */)
28865
29637
  .map(change => {
28866
29638
  const doc = new QueryDocumentSnapshot(querySnapshot._firestore, querySnapshot._userDataWriter, change.doc.key, change.doc, new SnapshotMetadata(querySnapshot._snapshot.mutatedKeys.has(change.doc.key), querySnapshot._snapshot.fromCache), querySnapshot.query.converter);
28867
29639
  let oldIndex = -1;
28868
29640
  let newIndex = -1;
28869
- if (change.type !== 0 /* Added */) {
29641
+ if (change.type !== 0 /* ChangeType.Added */) {
28870
29642
  oldIndex = indexTracker.indexOf(change.doc.key);
28871
29643
  indexTracker = indexTracker.delete(change.doc.key);
28872
29644
  }
28873
- if (change.type !== 1 /* Removed */) {
29645
+ if (change.type !== 1 /* ChangeType.Removed */) {
28874
29646
  indexTracker = indexTracker.add(change.doc);
28875
29647
  newIndex = indexTracker.indexOf(change.doc.key);
28876
29648
  }
@@ -28885,12 +29657,12 @@ function changesFromSnapshot(querySnapshot, includeMetadataChanges) {
28885
29657
  }
28886
29658
  function resultChangeType(type) {
28887
29659
  switch (type) {
28888
- case 0 /* Added */:
29660
+ case 0 /* ChangeType.Added */:
28889
29661
  return 'added';
28890
- case 2 /* Modified */:
28891
- case 3 /* Metadata */:
29662
+ case 2 /* ChangeType.Modified */:
29663
+ case 3 /* ChangeType.Metadata */:
28892
29664
  return 'modified';
28893
- case 1 /* Removed */:
29665
+ case 1 /* ChangeType.Removed */:
28894
29666
  return 'removed';
28895
29667
  default:
28896
29668
  return fail();
@@ -29020,7 +29792,8 @@ function getDocs(query) {
29020
29792
  }
29021
29793
  /**
29022
29794
  * Executes the query and returns the results as a `QuerySnapshot` from cache.
29023
- * Returns an error if the document is not currently cached.
29795
+ * Returns an empty result set if no documents matching the query are currently
29796
+ * cached.
29024
29797
  *
29025
29798
  * @returns A `Promise` that will be resolved with the results of the query.
29026
29799
  */
@@ -29724,13 +30497,13 @@ function parseIndexes(jsonOrConfiguration) {
29724
30497
  const fieldPathString = tryGetString(field, 'fieldPath');
29725
30498
  const fieldPath = fieldPathFromDotSeparatedString('setIndexConfiguration', fieldPathString);
29726
30499
  if (field.arrayConfig === 'CONTAINS') {
29727
- segments.push(new IndexSegment(fieldPath, 2 /* CONTAINS */));
30500
+ segments.push(new IndexSegment(fieldPath, 2 /* IndexKind.CONTAINS */));
29728
30501
  }
29729
30502
  else if (field.order === 'ASCENDING') {
29730
- segments.push(new IndexSegment(fieldPath, 0 /* ASCENDING */));
30503
+ segments.push(new IndexSegment(fieldPath, 0 /* IndexKind.ASCENDING */));
29731
30504
  }
29732
30505
  else if (field.order === 'DESCENDING') {
29733
- segments.push(new IndexSegment(fieldPath, 1 /* DESCENDING */));
30506
+ segments.push(new IndexSegment(fieldPath, 1 /* IndexKind.DESCENDING */));
29734
30507
  }
29735
30508
  }
29736
30509
  }
@@ -29740,12 +30513,11 @@ function parseIndexes(jsonOrConfiguration) {
29740
30513
  return parsedIndexes;
29741
30514
  }
29742
30515
  function tryParseJson(json) {
29743
- var _a;
29744
30516
  try {
29745
30517
  return JSON.parse(json);
29746
30518
  }
29747
30519
  catch (e) {
29748
- throw new FirestoreError(Code.INVALID_ARGUMENT, 'Failed to parse JSON: ' + ((_a = e) === null || _a === void 0 ? void 0 : _a.message));
30520
+ throw new FirestoreError(Code.INVALID_ARGUMENT, 'Failed to parse JSON: ' + (e === null || e === void 0 ? void 0 : e.message));
29749
30521
  }
29750
30522
  }
29751
30523
  function tryGetString(data, property) {
@@ -29773,5 +30545,5 @@ function tryGetString(data, property) {
29773
30545
  */
29774
30546
  registerFirestore('node');
29775
30547
 
29776
- export { AbstractUserDataWriter, AggregateField, AggregateQuerySnapshot, Bytes, CACHE_SIZE_UNLIMITED, CollectionReference, DocumentReference, DocumentSnapshot, FieldPath, FieldValue, Firestore, FirestoreError, GeoPoint, LoadBundleTask, Query, QueryConstraint, QueryDocumentSnapshot, QuerySnapshot, SnapshotMetadata, Timestamp, Transaction, WriteBatch, DatabaseId as _DatabaseId, DocumentKey as _DocumentKey, EmptyAppCheckTokenProvider as _EmptyAppCheckTokenProvider, EmptyAuthCredentialsProvider as _EmptyAuthCredentialsProvider, FieldPath$1 as _FieldPath, cast as _cast, debugAssert as _debugAssert, isBase64Available as _isBase64Available, logWarn as _logWarn, validateIsNotUsedTogether as _validateIsNotUsedTogether, addDoc, aggregateQuerySnapshotEqual, arrayRemove, arrayUnion, clearIndexedDbPersistence, collection, collectionGroup, connectFirestoreEmulator, deleteDoc, deleteField, disableNetwork, doc, documentId, enableIndexedDbPersistence, enableMultiTabIndexedDbPersistence, enableNetwork, endAt, endBefore, ensureFirestoreConfigured, executeWrite, getCountFromServer, getDoc, getDocFromCache, getDocFromServer, getDocs, getDocsFromCache, getDocsFromServer, getFirestore, increment, initializeFirestore, limit, limitToLast, loadBundle, namedQuery, onSnapshot, onSnapshotsInSync, orderBy, query, queryEqual, refEqual, runTransaction, serverTimestamp, setDoc, setIndexConfiguration, setLogLevel, snapshotEqual, startAfter, startAt, terminate, updateDoc, waitForPendingWrites, where, writeBatch };
30548
+ export { AbstractUserDataWriter, AggregateField, AggregateQuerySnapshot, Bytes, CACHE_SIZE_UNLIMITED, CollectionReference, DocumentReference, DocumentSnapshot, FieldPath, FieldValue, Firestore, FirestoreError, GeoPoint, LoadBundleTask, Query, QueryCompositeFilterConstraint, QueryConstraint, QueryDocumentSnapshot, QueryEndAtConstraint, QueryFieldFilterConstraint, QueryLimitConstraint, QueryOrderByConstraint, QuerySnapshot, QueryStartAtConstraint, SnapshotMetadata, Timestamp, Transaction, WriteBatch, DatabaseId as _DatabaseId, DocumentKey as _DocumentKey, EmptyAppCheckTokenProvider as _EmptyAppCheckTokenProvider, EmptyAuthCredentialsProvider as _EmptyAuthCredentialsProvider, FieldPath$1 as _FieldPath, cast as _cast, debugAssert as _debugAssert, isBase64Available as _isBase64Available, logWarn as _logWarn, validateIsNotUsedTogether as _validateIsNotUsedTogether, addDoc, aggregateQuerySnapshotEqual, and, arrayRemove, arrayUnion, clearIndexedDbPersistence, collection, collectionGroup, connectFirestoreEmulator, deleteDoc, deleteField, disableNetwork, doc, documentId, enableIndexedDbPersistence, enableMultiTabIndexedDbPersistence, enableNetwork, endAt, endBefore, ensureFirestoreConfigured, executeWrite, getCountFromServer, getDoc, getDocFromCache, getDocFromServer, getDocs, getDocsFromCache, getDocsFromServer, getFirestore, increment, initializeFirestore, limit, limitToLast, loadBundle, namedQuery, onSnapshot, onSnapshotsInSync, or, orderBy, query, queryEqual, refEqual, runTransaction, serverTimestamp, setDoc, setIndexConfiguration, setLogLevel, snapshotEqual, startAfter, startAt, terminate, updateDoc, waitForPendingWrites, where, writeBatch };
29777
30549
  //# sourceMappingURL=index.node.mjs.map