@firebase/firestore 3.7.3 → 3.8.0

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 (120) 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.d.ts +1 -1
  5. package/dist/firestore/src/core/bound.d.ts +49 -0
  6. package/dist/firestore/src/core/filter.d.ts +134 -0
  7. package/dist/firestore/src/core/order_by.d.ts +35 -0
  8. package/dist/firestore/src/core/query.d.ts +5 -6
  9. package/dist/firestore/src/core/target.d.ts +4 -120
  10. package/dist/firestore/src/lite-api/query.d.ts +273 -66
  11. package/dist/firestore/src/local/indexeddb_index_manager.d.ts +2 -0
  12. package/dist/firestore/src/local/local_documents_view.d.ts +0 -2
  13. package/dist/firestore/src/model/document.d.ts +8 -1
  14. package/dist/firestore/src/protos/firestore_proto_api.d.ts +1 -1
  15. package/dist/firestore/src/remote/serializer.d.ts +10 -3
  16. package/dist/firestore/src/util/logic_utils.d.ts +52 -0
  17. package/dist/firestore/src/util/types.d.ts +1 -1
  18. package/dist/firestore/test/lite/helpers.d.ts +1 -1
  19. package/dist/firestore/test/unit/core/filter.test.d.ts +17 -0
  20. package/dist/firestore/test/unit/specs/spec_test_runner.d.ts +2 -1
  21. package/dist/firestore/test/unit/util/logic_utils.test.d.ts +17 -0
  22. package/dist/firestore/test/util/helpers.d.ts +6 -2
  23. package/dist/firestore/test/util/test_platform.d.ts +2 -2
  24. package/dist/index.d.ts +138 -62
  25. package/dist/index.esm2017.js +4295 -3742
  26. package/dist/index.esm2017.js.map +1 -1
  27. package/dist/index.esm5.js +4981 -4410
  28. package/dist/index.esm5.js.map +1 -1
  29. package/dist/index.node.cjs.js +1657 -889
  30. package/dist/index.node.cjs.js.map +1 -1
  31. package/dist/index.node.mjs +1650 -890
  32. package/dist/index.node.mjs.map +1 -1
  33. package/dist/index.rn.js +4520 -3967
  34. package/dist/index.rn.js.map +1 -1
  35. package/dist/internal.d.ts +328 -68
  36. package/dist/lite/firestore/lite/index.d.ts +1 -1
  37. package/dist/lite/firestore/src/api/filter.d.ts +1 -1
  38. package/dist/lite/firestore/src/api.d.ts +1 -1
  39. package/dist/lite/firestore/src/core/bound.d.ts +49 -0
  40. package/dist/lite/firestore/src/core/filter.d.ts +134 -0
  41. package/dist/lite/firestore/src/core/order_by.d.ts +35 -0
  42. package/dist/lite/firestore/src/core/query.d.ts +5 -6
  43. package/dist/lite/firestore/src/core/target.d.ts +4 -120
  44. package/dist/lite/firestore/src/lite-api/query.d.ts +273 -66
  45. package/dist/lite/firestore/src/local/indexeddb_index_manager.d.ts +2 -0
  46. package/dist/lite/firestore/src/local/local_documents_view.d.ts +0 -2
  47. package/dist/lite/firestore/src/model/document.d.ts +8 -1
  48. package/dist/lite/firestore/src/protos/firestore_proto_api.d.ts +1 -1
  49. package/dist/lite/firestore/src/remote/serializer.d.ts +10 -3
  50. package/dist/lite/firestore/src/util/logic_utils.d.ts +52 -0
  51. package/dist/lite/firestore/src/util/types.d.ts +1 -1
  52. package/dist/lite/firestore/test/lite/helpers.d.ts +1 -1
  53. package/dist/lite/firestore/test/unit/core/filter.test.d.ts +17 -0
  54. package/dist/lite/firestore/test/unit/specs/spec_test_runner.d.ts +2 -1
  55. package/dist/lite/firestore/test/unit/util/logic_utils.test.d.ts +17 -0
  56. package/dist/lite/firestore/test/util/helpers.d.ts +6 -2
  57. package/dist/lite/firestore/test/util/test_platform.d.ts +2 -2
  58. package/dist/lite/index.browser.esm2017.js +2091 -1793
  59. package/dist/lite/index.browser.esm2017.js.map +1 -1
  60. package/dist/lite/index.browser.esm5.js +2239 -2000
  61. package/dist/lite/index.browser.esm5.js.map +1 -1
  62. package/dist/lite/index.d.ts +138 -62
  63. package/dist/lite/index.node.cjs.js +2212 -1802
  64. package/dist/lite/index.node.cjs.js.map +1 -1
  65. package/dist/lite/index.node.mjs +2205 -1803
  66. package/dist/lite/index.node.mjs.map +1 -1
  67. package/dist/lite/index.rn.esm2017.js +2077 -1778
  68. package/dist/lite/index.rn.esm2017.js.map +1 -1
  69. package/dist/lite/internal.d.ts +328 -66
  70. package/dist/lite/packages/firestore/dist/lite/index.browser.esm2017.d.ts +322 -174
  71. package/dist/lite/packages/firestore/lite/index.d.ts +1 -1
  72. package/dist/lite/packages/firestore/rollup.config.debug.d.ts +1 -1
  73. package/dist/lite/packages/firestore/src/api/filter.d.ts +1 -1
  74. package/dist/lite/packages/firestore/src/api.d.ts +1 -1
  75. package/dist/lite/packages/firestore/src/core/bound.d.ts +49 -0
  76. package/dist/lite/packages/firestore/src/core/filter.d.ts +134 -0
  77. package/dist/lite/packages/firestore/src/core/order_by.d.ts +35 -0
  78. package/dist/lite/packages/firestore/src/core/query.d.ts +5 -6
  79. package/dist/lite/packages/firestore/src/core/target.d.ts +4 -120
  80. package/dist/lite/packages/firestore/src/lite-api/query.d.ts +273 -66
  81. package/dist/lite/packages/firestore/src/local/indexeddb_index_manager.d.ts +2 -0
  82. package/dist/lite/packages/firestore/src/local/local_documents_view.d.ts +0 -2
  83. package/dist/lite/packages/firestore/src/model/document.d.ts +8 -1
  84. package/dist/lite/packages/firestore/src/protos/firestore_proto_api.d.ts +1 -1
  85. package/dist/lite/packages/firestore/src/remote/serializer.d.ts +10 -3
  86. package/dist/lite/packages/firestore/src/util/logic_utils.d.ts +52 -0
  87. package/dist/lite/packages/firestore/src/util/types.d.ts +1 -1
  88. package/dist/lite/packages/firestore/test/lite/helpers.d.ts +1 -1
  89. package/dist/lite/packages/firestore/test/unit/core/filter.test.d.ts +17 -0
  90. package/dist/lite/packages/firestore/test/unit/specs/spec_test_runner.d.ts +2 -1
  91. package/dist/lite/packages/firestore/test/unit/util/logic_utils.test.d.ts +17 -0
  92. package/dist/lite/packages/firestore/test/util/helpers.d.ts +6 -2
  93. package/dist/lite/packages/firestore/test/util/test_platform.d.ts +2 -2
  94. package/dist/lite/private.d.ts +240 -66
  95. package/dist/packages/firestore/dist/index.esm2017.d.ts +368 -208
  96. package/dist/packages/firestore/lite/index.d.ts +1 -1
  97. package/dist/packages/firestore/rollup.config.debug.d.ts +1 -1
  98. package/dist/packages/firestore/src/api/filter.d.ts +1 -1
  99. package/dist/packages/firestore/src/api.d.ts +1 -1
  100. package/dist/packages/firestore/src/core/bound.d.ts +49 -0
  101. package/dist/packages/firestore/src/core/filter.d.ts +134 -0
  102. package/dist/packages/firestore/src/core/order_by.d.ts +35 -0
  103. package/dist/packages/firestore/src/core/query.d.ts +5 -6
  104. package/dist/packages/firestore/src/core/target.d.ts +4 -120
  105. package/dist/packages/firestore/src/lite-api/query.d.ts +273 -66
  106. package/dist/packages/firestore/src/local/indexeddb_index_manager.d.ts +2 -0
  107. package/dist/packages/firestore/src/local/local_documents_view.d.ts +0 -2
  108. package/dist/packages/firestore/src/model/document.d.ts +8 -1
  109. package/dist/packages/firestore/src/protos/firestore_proto_api.d.ts +1 -1
  110. package/dist/packages/firestore/src/remote/serializer.d.ts +10 -3
  111. package/dist/packages/firestore/src/util/logic_utils.d.ts +52 -0
  112. package/dist/packages/firestore/src/util/types.d.ts +1 -1
  113. package/dist/packages/firestore/test/lite/helpers.d.ts +1 -1
  114. package/dist/packages/firestore/test/unit/core/filter.test.d.ts +17 -0
  115. package/dist/packages/firestore/test/unit/specs/spec_test_runner.d.ts +2 -1
  116. package/dist/packages/firestore/test/unit/util/logic_utils.test.d.ts +17 -0
  117. package/dist/packages/firestore/test/util/helpers.d.ts +6 -2
  118. package/dist/packages/firestore/test/util/test_platform.d.ts +2 -2
  119. package/dist/private.d.ts +240 -68
  120. package/package.json +13 -11
@@ -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";
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";
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,344 @@ 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));
5482
5595
  }
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);
5596
+ else {
5597
+ // filter instanceof CompositeFilter
5598
+ const canonicalIdsString = filter.filters
5599
+ .map(filter => canonifyFilter(filter))
5600
+ .join(',');
5601
+ return `${filter.op}(${canonicalIdsString})`;
5501
5602
  }
5502
- return new Bound(values, inclusive);
5503
5603
  }
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
- }
5604
+ function filterEquals(f1, f2) {
5605
+ if (f1 instanceof FieldFilter) {
5606
+ return fieldFilterEquals(f1, f2);
5554
5607
  }
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
- }
5608
+ else if (f1 instanceof CompositeFilter) {
5609
+ return compositeFilterEquals(f1, f2);
5593
5610
  }
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
- }
5611
+ else {
5612
+ fail();
5608
5613
  }
5609
- return { value, inclusive };
5610
5614
  }
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
- }
5615
+ function fieldFilterEquals(f1, f2) {
5616
+ return (f2 instanceof FieldFilter &&
5617
+ f1.op === f2.op &&
5618
+ f1.field.isEqual(f2.field) &&
5619
+ valueEquals(f1.value, f2.value));
5620
+ }
5621
+ function compositeFilterEquals(f1, f2) {
5622
+ if (f2 instanceof CompositeFilter &&
5623
+ f1.op === f2.op &&
5624
+ f1.filters.length === f2.filters.length) {
5625
+ const subFiltersMatch = f1.filters.reduce((result, f1Filter, index) => result && filterEquals(f1Filter, f2.filters[index]), true);
5626
+ return subFiltersMatch;
5641
5627
  }
5642
- return fields.size + (hasArraySegment ? 1 : 0);
5628
+ return false;
5643
5629
  }
5644
- class Filter {
5630
+ /**
5631
+ * Returns a new composite filter that contains all filter from
5632
+ * `compositeFilter` plus all the given filters in `otherFilters`.
5633
+ */
5634
+ function compositeFilterWithAddedFilters(compositeFilter, otherFilters) {
5635
+ const mergedFilters = compositeFilter.filters.concat(otherFilters);
5636
+ return CompositeFilter.create(mergedFilters, compositeFilter.op);
5645
5637
  }
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)));
5638
+ /** Returns a debug description for `filter`. */
5639
+ function stringifyFilter(filter) {
5640
+ if (filter instanceof FieldFilter) {
5641
+ return stringifyFieldFilter(filter);
5697
5642
  }
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
- }
5643
+ else if (filter instanceof CompositeFilter) {
5644
+ return stringifyCompositeFilter(filter);
5715
5645
  }
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);
5646
+ else {
5647
+ return 'Filter';
5725
5648
  }
5726
5649
  }
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));
5650
+ function stringifyCompositeFilter(filter) {
5651
+ return (filter.op.toString() +
5652
+ ` {` +
5653
+ filter.getFilters().map(stringifyFilter).join(' ,') +
5654
+ '}');
5739
5655
  }
5740
- /** Returns a debug description for `filter`. */
5741
- function stringifyFilter(filter) {
5656
+ function stringifyFieldFilter(filter) {
5742
5657
  return `${filter.field.canonicalString()} ${filter.op} ${canonicalId(filter.value)}`;
5743
5658
  }
5744
5659
  /** Filter that matches on key fields (i.e. '__name__'). */
@@ -5755,8 +5670,8 @@ class KeyFieldFilter extends FieldFilter {
5755
5670
  /** Filter that matches on key fields within an array. */
5756
5671
  class KeyFieldInFilter extends FieldFilter {
5757
5672
  constructor(field, value) {
5758
- super(field, "in" /* IN */, value);
5759
- this.keys = extractDocumentKeysFromArrayValue("in" /* IN */, value);
5673
+ super(field, "in" /* Operator.IN */, value);
5674
+ this.keys = extractDocumentKeysFromArrayValue("in" /* Operator.IN */, value);
5760
5675
  }
5761
5676
  matches(doc) {
5762
5677
  return this.keys.some(key => key.isEqual(doc.key));
@@ -5765,8 +5680,8 @@ class KeyFieldInFilter extends FieldFilter {
5765
5680
  /** Filter that matches on key fields not present within an array. */
5766
5681
  class KeyFieldNotInFilter extends FieldFilter {
5767
5682
  constructor(field, value) {
5768
- super(field, "not-in" /* NOT_IN */, value);
5769
- this.keys = extractDocumentKeysFromArrayValue("not-in" /* NOT_IN */, value);
5683
+ super(field, "not-in" /* Operator.NOT_IN */, value);
5684
+ this.keys = extractDocumentKeysFromArrayValue("not-in" /* Operator.NOT_IN */, value);
5770
5685
  }
5771
5686
  matches(doc) {
5772
5687
  return !this.keys.some(key => key.isEqual(doc.key));
@@ -5781,7 +5696,7 @@ function extractDocumentKeysFromArrayValue(op, value) {
5781
5696
  /** A Filter that implements the array-contains operator. */
5782
5697
  class ArrayContainsFilter extends FieldFilter {
5783
5698
  constructor(field, value) {
5784
- super(field, "array-contains" /* ARRAY_CONTAINS */, value);
5699
+ super(field, "array-contains" /* Operator.ARRAY_CONTAINS */, value);
5785
5700
  }
5786
5701
  matches(doc) {
5787
5702
  const other = doc.data.field(this.field);
@@ -5791,7 +5706,7 @@ class ArrayContainsFilter extends FieldFilter {
5791
5706
  /** A Filter that implements the IN operator. */
5792
5707
  class InFilter extends FieldFilter {
5793
5708
  constructor(field, value) {
5794
- super(field, "in" /* IN */, value);
5709
+ super(field, "in" /* Operator.IN */, value);
5795
5710
  }
5796
5711
  matches(doc) {
5797
5712
  const other = doc.data.field(this.field);
@@ -5801,7 +5716,7 @@ class InFilter extends FieldFilter {
5801
5716
  /** A Filter that implements the not-in operator. */
5802
5717
  class NotInFilter extends FieldFilter {
5803
5718
  constructor(field, value) {
5804
- super(field, "not-in" /* NOT_IN */, value);
5719
+ super(field, "not-in" /* Operator.NOT_IN */, value);
5805
5720
  }
5806
5721
  matches(doc) {
5807
5722
  if (arrayValueContains(this.value.arrayValue, { nullValue: 'NULL_VALUE' })) {
@@ -5814,7 +5729,7 @@ class NotInFilter extends FieldFilter {
5814
5729
  /** A Filter that implements the array-contains-any operator. */
5815
5730
  class ArrayContainsAnyFilter extends FieldFilter {
5816
5731
  constructor(field, value) {
5817
- super(field, "array-contains-any" /* ARRAY_CONTAINS_ANY */, value);
5732
+ super(field, "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */, value);
5818
5733
  }
5819
5734
  matches(doc) {
5820
5735
  const other = doc.data.field(this.field);
@@ -5823,32 +5738,29 @@ class ArrayContainsAnyFilter extends FieldFilter {
5823
5738
  }
5824
5739
  return other.arrayValue.values.some(val => arrayValueContains(this.value.arrayValue, val));
5825
5740
  }
5826
- }
5827
- /**
5828
- * Represents a bound of a query.
5741
+ }
5742
+
5743
+ /**
5744
+ * @license
5745
+ * Copyright 2022 Google LLC
5829
5746
  *
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).
5747
+ * Licensed under the Apache License, Version 2.0 (the "License");
5748
+ * you may not use this file except in compliance with the License.
5749
+ * You may obtain a copy of the License at
5833
5750
  *
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.
5751
+ * http://www.apache.org/licenses/LICENSE-2.0
5836
5752
  *
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.
5753
+ * Unless required by applicable law or agreed to in writing, software
5754
+ * distributed under the License is distributed on an "AS IS" BASIS,
5755
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5756
+ * See the License for the specific language governing permissions and
5757
+ * limitations under the License.
5840
5758
  */
5841
- class Bound {
5842
- constructor(position, inclusive) {
5843
- this.position = position;
5844
- this.inclusive = inclusive;
5845
- }
5846
- }
5847
5759
  /**
5848
5760
  * An ordering on a field, in some Direction. Direction defaults to ASCENDING.
5849
5761
  */
5850
5762
  class OrderBy {
5851
- constructor(field, dir = "asc" /* ASCENDING */) {
5763
+ constructor(field, dir = "asc" /* Direction.ASCENDING */) {
5852
5764
  this.field = field;
5853
5765
  this.dir = dir;
5854
5766
  }
@@ -5862,63 +5774,375 @@ function stringifyOrderBy(orderBy) {
5862
5774
  }
5863
5775
  function orderByEquals(left, right) {
5864
5776
  return left.dir === right.dir && left.field.isEqual(right.field);
5777
+ }
5778
+
5779
+ /**
5780
+ * @license
5781
+ * Copyright 2019 Google LLC
5782
+ *
5783
+ * Licensed under the Apache License, Version 2.0 (the "License");
5784
+ * you may not use this file except in compliance with the License.
5785
+ * You may obtain a copy of the License at
5786
+ *
5787
+ * http://www.apache.org/licenses/LICENSE-2.0
5788
+ *
5789
+ * Unless required by applicable law or agreed to in writing, software
5790
+ * distributed under the License is distributed on an "AS IS" BASIS,
5791
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5792
+ * See the License for the specific language governing permissions and
5793
+ * limitations under the License.
5794
+ */
5795
+ // Visible for testing
5796
+ class TargetImpl {
5797
+ constructor(path, collectionGroup = null, orderBy = [], filters = [], limit = null, startAt = null, endAt = null) {
5798
+ this.path = path;
5799
+ this.collectionGroup = collectionGroup;
5800
+ this.orderBy = orderBy;
5801
+ this.filters = filters;
5802
+ this.limit = limit;
5803
+ this.startAt = startAt;
5804
+ this.endAt = endAt;
5805
+ this.memoizedCanonicalId = null;
5806
+ }
5865
5807
  }
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);
5808
+ /**
5809
+ * Initializes a Target with a path and optional additional query constraints.
5810
+ * Path must currently be empty if this is a collection group query.
5811
+ *
5812
+ * NOTE: you should always construct `Target` from `Query.toTarget` instead of
5813
+ * using this factory method, because `Query` provides an implicit `orderBy`
5814
+ * property.
5815
+ */
5816
+ function newTarget(path, collectionGroup = null, orderBy = [], filters = [], limit = null, startAt = null, endAt = null) {
5817
+ return new TargetImpl(path, collectionGroup, orderBy, filters, limit, startAt, endAt);
5818
+ }
5819
+ function canonifyTarget(target) {
5820
+ const targetImpl = debugCast(target);
5821
+ if (targetImpl.memoizedCanonicalId === null) {
5822
+ let str = targetImpl.path.canonicalString();
5823
+ if (targetImpl.collectionGroup !== null) {
5824
+ str += '|cg:' + targetImpl.collectionGroup;
5873
5825
  }
5874
- else {
5875
- const docValue = doc.data.field(orderByComponent.field);
5876
- comparison = valueCompare(component, docValue);
5826
+ str += '|f:';
5827
+ str += targetImpl.filters.map(f => canonifyFilter(f)).join(',');
5828
+ str += '|ob:';
5829
+ str += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(',');
5830
+ if (!isNullOrUndefined(targetImpl.limit)) {
5831
+ str += '|l:';
5832
+ str += targetImpl.limit;
5877
5833
  }
5878
- if (orderByComponent.dir === "desc" /* DESCENDING */) {
5879
- comparison = comparison * -1;
5834
+ if (targetImpl.startAt) {
5835
+ str += '|lb:';
5836
+ str += targetImpl.startAt.inclusive ? 'b:' : 'a:';
5837
+ str += targetImpl.startAt.position.map(p => canonicalId(p)).join(',');
5880
5838
  }
5881
- if (comparison !== 0) {
5882
- break;
5839
+ if (targetImpl.endAt) {
5840
+ str += '|ub:';
5841
+ str += targetImpl.endAt.inclusive ? 'a:' : 'b:';
5842
+ str += targetImpl.endAt.position.map(p => canonicalId(p)).join(',');
5883
5843
  }
5844
+ targetImpl.memoizedCanonicalId = str;
5884
5845
  }
5885
- return comparison;
5846
+ return targetImpl.memoizedCanonicalId;
5847
+ }
5848
+ function stringifyTarget(target) {
5849
+ let str = target.path.canonicalString();
5850
+ if (target.collectionGroup !== null) {
5851
+ str += ' collectionGroup=' + target.collectionGroup;
5852
+ }
5853
+ if (target.filters.length > 0) {
5854
+ str += `, filters: [${target.filters
5855
+ .map(f => stringifyFilter(f))
5856
+ .join(', ')}]`;
5857
+ }
5858
+ if (!isNullOrUndefined(target.limit)) {
5859
+ str += ', limit: ' + target.limit;
5860
+ }
5861
+ if (target.orderBy.length > 0) {
5862
+ str += `, orderBy: [${target.orderBy
5863
+ .map(o => stringifyOrderBy(o))
5864
+ .join(', ')}]`;
5865
+ }
5866
+ if (target.startAt) {
5867
+ str += ', startAt: ';
5868
+ str += target.startAt.inclusive ? 'b:' : 'a:';
5869
+ str += target.startAt.position.map(p => canonicalId(p)).join(',');
5870
+ }
5871
+ if (target.endAt) {
5872
+ str += ', endAt: ';
5873
+ str += target.endAt.inclusive ? 'a:' : 'b:';
5874
+ str += target.endAt.position.map(p => canonicalId(p)).join(',');
5875
+ }
5876
+ return `Target(${str})`;
5877
+ }
5878
+ function targetEquals(left, right) {
5879
+ if (left.limit !== right.limit) {
5880
+ return false;
5881
+ }
5882
+ if (left.orderBy.length !== right.orderBy.length) {
5883
+ return false;
5884
+ }
5885
+ for (let i = 0; i < left.orderBy.length; i++) {
5886
+ if (!orderByEquals(left.orderBy[i], right.orderBy[i])) {
5887
+ return false;
5888
+ }
5889
+ }
5890
+ if (left.filters.length !== right.filters.length) {
5891
+ return false;
5892
+ }
5893
+ for (let i = 0; i < left.filters.length; i++) {
5894
+ if (!filterEquals(left.filters[i], right.filters[i])) {
5895
+ return false;
5896
+ }
5897
+ }
5898
+ if (left.collectionGroup !== right.collectionGroup) {
5899
+ return false;
5900
+ }
5901
+ if (!left.path.isEqual(right.path)) {
5902
+ return false;
5903
+ }
5904
+ if (!boundEquals(left.startAt, right.startAt)) {
5905
+ return false;
5906
+ }
5907
+ return boundEquals(left.endAt, right.endAt);
5908
+ }
5909
+ function targetIsDocumentTarget(target) {
5910
+ return (DocumentKey.isDocumentKey(target.path) &&
5911
+ target.collectionGroup === null &&
5912
+ target.filters.length === 0);
5913
+ }
5914
+ /** Returns the field filters that target the given field path. */
5915
+ function targetGetFieldFiltersForPath(target, path) {
5916
+ return target.filters.filter(f => f instanceof FieldFilter && f.field.isEqual(path));
5886
5917
  }
5887
5918
  /**
5888
- * Returns true if a document sorts after a bound using the provided sort
5889
- * order.
5919
+ * Returns the values that are used in ARRAY_CONTAINS or ARRAY_CONTAINS_ANY
5920
+ * filters. Returns `null` if there are no such filters.
5890
5921
  */
5891
- function boundSortsAfterDocument(bound, orderBy, doc) {
5892
- const comparison = boundCompareToDocument(bound, orderBy, doc);
5893
- return bound.inclusive ? comparison >= 0 : comparison > 0;
5922
+ function targetGetArrayValues(target, fieldIndex) {
5923
+ const segment = fieldIndexGetArraySegment(fieldIndex);
5924
+ if (segment === undefined) {
5925
+ return null;
5926
+ }
5927
+ for (const fieldFilter of targetGetFieldFiltersForPath(target, segment.fieldPath)) {
5928
+ switch (fieldFilter.op) {
5929
+ case "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */:
5930
+ return fieldFilter.value.arrayValue.values || [];
5931
+ case "array-contains" /* Operator.ARRAY_CONTAINS */:
5932
+ return [fieldFilter.value];
5933
+ // Remaining filters are not array filters.
5934
+ }
5935
+ }
5936
+ return null;
5894
5937
  }
5895
5938
  /**
5896
- * Returns true if a document sorts before a bound using the provided sort
5897
- * order.
5939
+ * Returns the list of values that are used in != or NOT_IN filters. Returns
5940
+ * `null` if there are no such filters.
5898
5941
  */
5899
- function boundSortsBeforeDocument(bound, orderBy, doc) {
5900
- const comparison = boundCompareToDocument(bound, orderBy, doc);
5901
- return bound.inclusive ? comparison <= 0 : comparison < 0;
5942
+ function targetGetNotInValues(target, fieldIndex) {
5943
+ const values = new Map();
5944
+ for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
5945
+ for (const fieldFilter of targetGetFieldFiltersForPath(target, segment.fieldPath)) {
5946
+ switch (fieldFilter.op) {
5947
+ case "==" /* Operator.EQUAL */:
5948
+ case "in" /* Operator.IN */:
5949
+ // Encode equality prefix, which is encoded in the index value before
5950
+ // the inequality (e.g. `a == 'a' && b != 'b'` is encoded to
5951
+ // `value != 'ab'`).
5952
+ values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
5953
+ break;
5954
+ case "not-in" /* Operator.NOT_IN */:
5955
+ case "!=" /* Operator.NOT_EQUAL */:
5956
+ // NotIn/NotEqual is always a suffix. There cannot be any remaining
5957
+ // segments and hence we can return early here.
5958
+ values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
5959
+ return Array.from(values.values());
5960
+ // Remaining filters cannot be used as notIn bounds.
5961
+ }
5962
+ }
5963
+ }
5964
+ return null;
5902
5965
  }
5903
- function boundEquals(left, right) {
5904
- if (left === null) {
5905
- return right === null;
5966
+ /**
5967
+ * Returns a lower bound of field values that can be used as a starting point to
5968
+ * scan the index defined by `fieldIndex`. Returns `MIN_VALUE` if no lower bound
5969
+ * exists.
5970
+ */
5971
+ function targetGetLowerBound(target, fieldIndex) {
5972
+ const values = [];
5973
+ let inclusive = true;
5974
+ // For each segment, retrieve a lower bound if there is a suitable filter or
5975
+ // startAt.
5976
+ for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
5977
+ const segmentBound = segment.kind === 0 /* IndexKind.ASCENDING */
5978
+ ? targetGetAscendingBound(target, segment.fieldPath, target.startAt)
5979
+ : targetGetDescendingBound(target, segment.fieldPath, target.startAt);
5980
+ values.push(segmentBound.value);
5981
+ inclusive && (inclusive = segmentBound.inclusive);
5906
5982
  }
5907
- else if (right === null) {
5908
- return false;
5983
+ return new Bound(values, inclusive);
5984
+ }
5985
+ /**
5986
+ * Returns an upper bound of field values that can be used as an ending point
5987
+ * when scanning the index defined by `fieldIndex`. Returns `MAX_VALUE` if no
5988
+ * upper bound exists.
5989
+ */
5990
+ function targetGetUpperBound(target, fieldIndex) {
5991
+ const values = [];
5992
+ let inclusive = true;
5993
+ // For each segment, retrieve an upper bound if there is a suitable filter or
5994
+ // endAt.
5995
+ for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
5996
+ const segmentBound = segment.kind === 0 /* IndexKind.ASCENDING */
5997
+ ? targetGetDescendingBound(target, segment.fieldPath, target.endAt)
5998
+ : targetGetAscendingBound(target, segment.fieldPath, target.endAt);
5999
+ values.push(segmentBound.value);
6000
+ inclusive && (inclusive = segmentBound.inclusive);
6001
+ }
6002
+ return new Bound(values, inclusive);
6003
+ }
6004
+ /**
6005
+ * Returns the value to use as the lower bound for ascending index segment at
6006
+ * the provided `fieldPath` (or the upper bound for an descending segment).
6007
+ */
6008
+ function targetGetAscendingBound(target, fieldPath, bound) {
6009
+ let value = MIN_VALUE;
6010
+ let inclusive = true;
6011
+ // Process all filters to find a value for the current field segment
6012
+ for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) {
6013
+ let filterValue = MIN_VALUE;
6014
+ let filterInclusive = true;
6015
+ switch (fieldFilter.op) {
6016
+ case "<" /* Operator.LESS_THAN */:
6017
+ case "<=" /* Operator.LESS_THAN_OR_EQUAL */:
6018
+ filterValue = valuesGetLowerBound(fieldFilter.value);
6019
+ break;
6020
+ case "==" /* Operator.EQUAL */:
6021
+ case "in" /* Operator.IN */:
6022
+ case ">=" /* Operator.GREATER_THAN_OR_EQUAL */:
6023
+ filterValue = fieldFilter.value;
6024
+ break;
6025
+ case ">" /* Operator.GREATER_THAN */:
6026
+ filterValue = fieldFilter.value;
6027
+ filterInclusive = false;
6028
+ break;
6029
+ case "!=" /* Operator.NOT_EQUAL */:
6030
+ case "not-in" /* Operator.NOT_IN */:
6031
+ filterValue = MIN_VALUE;
6032
+ break;
6033
+ // Remaining filters cannot be used as lower bounds.
6034
+ }
6035
+ if (lowerBoundCompare({ value, inclusive }, { value: filterValue, inclusive: filterInclusive }) < 0) {
6036
+ value = filterValue;
6037
+ inclusive = filterInclusive;
6038
+ }
6039
+ }
6040
+ // If there is an additional bound, compare the values against the existing
6041
+ // range to see if we can narrow the scope.
6042
+ if (bound !== null) {
6043
+ for (let i = 0; i < target.orderBy.length; ++i) {
6044
+ const orderBy = target.orderBy[i];
6045
+ if (orderBy.field.isEqual(fieldPath)) {
6046
+ const cursorValue = bound.position[i];
6047
+ if (lowerBoundCompare({ value, inclusive }, { value: cursorValue, inclusive: bound.inclusive }) < 0) {
6048
+ value = cursorValue;
6049
+ inclusive = bound.inclusive;
6050
+ }
6051
+ break;
6052
+ }
6053
+ }
6054
+ }
6055
+ return { value, inclusive };
6056
+ }
6057
+ /**
6058
+ * Returns the value to use as the upper bound for ascending index segment at
6059
+ * the provided `fieldPath` (or the lower bound for a descending segment).
6060
+ */
6061
+ function targetGetDescendingBound(target, fieldPath, bound) {
6062
+ let value = MAX_VALUE;
6063
+ let inclusive = true;
6064
+ // Process all filters to find a value for the current field segment
6065
+ for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) {
6066
+ let filterValue = MAX_VALUE;
6067
+ let filterInclusive = true;
6068
+ switch (fieldFilter.op) {
6069
+ case ">=" /* Operator.GREATER_THAN_OR_EQUAL */:
6070
+ case ">" /* Operator.GREATER_THAN */:
6071
+ filterValue = valuesGetUpperBound(fieldFilter.value);
6072
+ filterInclusive = false;
6073
+ break;
6074
+ case "==" /* Operator.EQUAL */:
6075
+ case "in" /* Operator.IN */:
6076
+ case "<=" /* Operator.LESS_THAN_OR_EQUAL */:
6077
+ filterValue = fieldFilter.value;
6078
+ break;
6079
+ case "<" /* Operator.LESS_THAN */:
6080
+ filterValue = fieldFilter.value;
6081
+ filterInclusive = false;
6082
+ break;
6083
+ case "!=" /* Operator.NOT_EQUAL */:
6084
+ case "not-in" /* Operator.NOT_IN */:
6085
+ filterValue = MAX_VALUE;
6086
+ break;
6087
+ // Remaining filters cannot be used as upper bounds.
6088
+ }
6089
+ if (upperBoundCompare({ value, inclusive }, { value: filterValue, inclusive: filterInclusive }) > 0) {
6090
+ value = filterValue;
6091
+ inclusive = filterInclusive;
6092
+ }
6093
+ }
6094
+ // If there is an additional bound, compare the values against the existing
6095
+ // range to see if we can narrow the scope.
6096
+ if (bound !== null) {
6097
+ for (let i = 0; i < target.orderBy.length; ++i) {
6098
+ const orderBy = target.orderBy[i];
6099
+ if (orderBy.field.isEqual(fieldPath)) {
6100
+ const cursorValue = bound.position[i];
6101
+ if (upperBoundCompare({ value, inclusive }, { value: cursorValue, inclusive: bound.inclusive }) > 0) {
6102
+ value = cursorValue;
6103
+ inclusive = bound.inclusive;
6104
+ }
6105
+ break;
6106
+ }
6107
+ }
5909
6108
  }
5910
- if (left.inclusive !== right.inclusive ||
5911
- left.position.length !== right.position.length) {
5912
- return false;
6109
+ return { value, inclusive };
6110
+ }
6111
+ /** Returns the number of segments of a perfect index for this target. */
6112
+ function targetGetSegmentCount(target) {
6113
+ let fields = new SortedSet(FieldPath$1.comparator);
6114
+ let hasArraySegment = false;
6115
+ for (const filter of target.filters) {
6116
+ for (const subFilter of filter.getFlattenedFilters()) {
6117
+ // __name__ is not an explicit segment of any index, so we don't need to
6118
+ // count it.
6119
+ if (subFilter.field.isKeyField()) {
6120
+ continue;
6121
+ }
6122
+ // ARRAY_CONTAINS or ARRAY_CONTAINS_ANY filters must be counted separately.
6123
+ // For instance, it is possible to have an index for "a ARRAY a ASC". Even
6124
+ // though these are on the same field, they should be counted as two
6125
+ // separate segments in an index.
6126
+ if (subFilter.op === "array-contains" /* Operator.ARRAY_CONTAINS */ ||
6127
+ subFilter.op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
6128
+ hasArraySegment = true;
6129
+ }
6130
+ else {
6131
+ fields = fields.add(subFilter.field);
6132
+ }
6133
+ }
5913
6134
  }
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;
6135
+ for (const orderBy of target.orderBy) {
6136
+ // __name__ is not an explicit segment of any index, so we don't need to
6137
+ // count it.
6138
+ if (!orderBy.field.isKeyField()) {
6139
+ fields = fields.add(orderBy.field);
5919
6140
  }
5920
6141
  }
5921
- return true;
6142
+ return fields.size + (hasArraySegment ? 1 : 0);
6143
+ }
6144
+ function targetHasLimit(target) {
6145
+ return target.limit !== null;
5922
6146
  }
5923
6147
 
5924
6148
  /**
@@ -5949,7 +6173,7 @@ class QueryImpl {
5949
6173
  * Initializes a Query with a path and optional additional query constraints.
5950
6174
  * Path must currently be empty if this is a collection group query.
5951
6175
  */
5952
- constructor(path, collectionGroup = null, explicitOrderBy = [], filters = [], limit = null, limitType = "F" /* First */, startAt = null, endAt = null) {
6176
+ constructor(path, collectionGroup = null, explicitOrderBy = [], filters = [], limit = null, limitType = "F" /* LimitType.First */, startAt = null, endAt = null) {
5953
6177
  this.path = path;
5954
6178
  this.collectionGroup = collectionGroup;
5955
6179
  this.explicitOrderBy = explicitOrderBy;
@@ -6003,20 +6227,9 @@ function getFirstOrderByField(query) {
6003
6227
  }
6004
6228
  function getInequalityFilterField(query) {
6005
6229
  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;
6230
+ const result = filter.getFirstInequalityField();
6231
+ if (result !== null) {
6232
+ return result;
6020
6233
  }
6021
6234
  }
6022
6235
  return null;
@@ -6062,7 +6275,7 @@ function queryOrderBy(query) {
6062
6275
  if (!inequalityField.isKeyField()) {
6063
6276
  queryImpl.memoizedOrderBy.push(new OrderBy(inequalityField));
6064
6277
  }
6065
- queryImpl.memoizedOrderBy.push(new OrderBy(FieldPath$1.keyField(), "asc" /* ASCENDING */));
6278
+ queryImpl.memoizedOrderBy.push(new OrderBy(FieldPath$1.keyField(), "asc" /* Direction.ASCENDING */));
6066
6279
  }
6067
6280
  else {
6068
6281
  let foundKeyOrdering = false;
@@ -6078,7 +6291,7 @@ function queryOrderBy(query) {
6078
6291
  const lastDirection = queryImpl.explicitOrderBy.length > 0
6079
6292
  ? queryImpl.explicitOrderBy[queryImpl.explicitOrderBy.length - 1]
6080
6293
  .dir
6081
- : "asc" /* ASCENDING */;
6294
+ : "asc" /* Direction.ASCENDING */;
6082
6295
  queryImpl.memoizedOrderBy.push(new OrderBy(FieldPath$1.keyField(), lastDirection));
6083
6296
  }
6084
6297
  }
@@ -6091,16 +6304,16 @@ function queryOrderBy(query) {
6091
6304
  function queryToTarget(query) {
6092
6305
  const queryImpl = debugCast(query);
6093
6306
  if (!queryImpl.memoizedTarget) {
6094
- if (queryImpl.limitType === "F" /* First */) {
6307
+ if (queryImpl.limitType === "F" /* LimitType.First */) {
6095
6308
  queryImpl.memoizedTarget = newTarget(queryImpl.path, queryImpl.collectionGroup, queryOrderBy(queryImpl), queryImpl.filters, queryImpl.limit, queryImpl.startAt, queryImpl.endAt);
6096
6309
  }
6097
6310
  else {
6098
6311
  // Flip the orderBy directions since we want the last results
6099
6312
  const orderBys = [];
6100
6313
  for (const orderBy of queryOrderBy(queryImpl)) {
6101
- const dir = orderBy.dir === "desc" /* DESCENDING */
6102
- ? "asc" /* ASCENDING */
6103
- : "desc" /* DESCENDING */;
6314
+ const dir = orderBy.dir === "desc" /* Direction.DESCENDING */
6315
+ ? "asc" /* Direction.ASCENDING */
6316
+ : "desc" /* Direction.DESCENDING */;
6104
6317
  orderBys.push(new OrderBy(orderBy.field, dir));
6105
6318
  }
6106
6319
  // We need to swap the cursors to match the now-flipped query ordering.
@@ -6117,6 +6330,8 @@ function queryToTarget(query) {
6117
6330
  return queryImpl.memoizedTarget;
6118
6331
  }
6119
6332
  function queryWithAddedFilter(query, filter) {
6333
+ filter.getFirstInequalityField();
6334
+ getInequalityFilterField(query);
6120
6335
  const newFilters = query.filters.concat([filter]);
6121
6336
  return new QueryImpl(query.path, query.collectionGroup, query.explicitOrderBy.slice(), newFilters, query.limit, query.limitType, query.startAt, query.endAt);
6122
6337
  }
@@ -6177,7 +6392,13 @@ function queryMatchesPathAndCollectionGroup(query, doc) {
6177
6392
  * in the results.
6178
6393
  */
6179
6394
  function queryMatchesOrderBy(query, doc) {
6180
- for (const orderBy of query.explicitOrderBy) {
6395
+ // We must use `queryOrderBy()` to get the list of all orderBys (both implicit and explicit).
6396
+ // Note that for OR queries, orderBy applies to all disjunction terms and implicit orderBys must
6397
+ // be taken into account. For example, the query "a > 1 || b==1" has an implicit "orderBy a" due
6398
+ // to the inequality, and is evaluated as "a > 1 orderBy a || b==1 orderBy a".
6399
+ // A document with content of {b:1} matches the filters, but does not match the orderBy because
6400
+ // it's missing the field 'a'.
6401
+ for (const orderBy of queryOrderBy(query)) {
6181
6402
  // order by key always matches
6182
6403
  if (!orderBy.field.isKeyField() && doc.data.field(orderBy.field) === null) {
6183
6404
  return false;
@@ -6239,9 +6460,9 @@ function compareDocs(orderBy, d1, d2) {
6239
6460
  ? DocumentKey.comparator(d1.key, d2.key)
6240
6461
  : compareDocumentsByField(orderBy.field, d1, d2);
6241
6462
  switch (orderBy.dir) {
6242
- case "asc" /* ASCENDING */:
6463
+ case "asc" /* Direction.ASCENDING */:
6243
6464
  return comparison;
6244
- case "desc" /* DESCENDING */:
6465
+ case "desc" /* Direction.DESCENDING */:
6245
6466
  return -1 * comparison;
6246
6467
  default:
6247
6468
  return fail();
@@ -6927,10 +7148,10 @@ function mutationEquals(left, right) {
6927
7148
  if (!fieldTransformsAreEqual(left.fieldTransforms, right.fieldTransforms)) {
6928
7149
  return false;
6929
7150
  }
6930
- if (left.type === 0 /* Set */) {
7151
+ if (left.type === 0 /* MutationType.Set */) {
6931
7152
  return left.value.isEqual(right.value);
6932
7153
  }
6933
- if (left.type === 1 /* Patch */) {
7154
+ if (left.type === 1 /* MutationType.Patch */) {
6934
7155
  return (left.data.isEqual(right.data) &&
6935
7156
  left.fieldMask.isEqual(right.fieldMask));
6936
7157
  }
@@ -6947,7 +7168,7 @@ class SetMutation extends Mutation {
6947
7168
  this.value = value;
6948
7169
  this.precondition = precondition;
6949
7170
  this.fieldTransforms = fieldTransforms;
6950
- this.type = 0 /* Set */;
7171
+ this.type = 0 /* MutationType.Set */;
6951
7172
  }
6952
7173
  getFieldMask() {
6953
7174
  return null;
@@ -6999,7 +7220,7 @@ class PatchMutation extends Mutation {
6999
7220
  this.fieldMask = fieldMask;
7000
7221
  this.precondition = precondition;
7001
7222
  this.fieldTransforms = fieldTransforms;
7002
- this.type = 1 /* Patch */;
7223
+ this.type = 1 /* MutationType.Patch */;
7003
7224
  }
7004
7225
  getFieldMask() {
7005
7226
  return this.fieldMask;
@@ -7101,7 +7322,7 @@ class DeleteMutation extends Mutation {
7101
7322
  super();
7102
7323
  this.key = key;
7103
7324
  this.precondition = precondition;
7104
- this.type = 2 /* Delete */;
7325
+ this.type = 2 /* MutationType.Delete */;
7105
7326
  this.fieldTransforms = [];
7106
7327
  }
7107
7328
  getFieldMask() {
@@ -7135,7 +7356,7 @@ class VerifyMutation extends Mutation {
7135
7356
  super();
7136
7357
  this.key = key;
7137
7358
  this.precondition = precondition;
7138
- this.type = 3 /* Verify */;
7359
+ this.type = 3 /* MutationType.Verify */;
7139
7360
  this.fieldTransforms = [];
7140
7361
  }
7141
7362
  getFieldMask() {
@@ -7764,13 +7985,13 @@ class TargetState {
7764
7985
  let removedDocuments = documentKeySet();
7765
7986
  this.documentChanges.forEach((key, changeType) => {
7766
7987
  switch (changeType) {
7767
- case 0 /* Added */:
7988
+ case 0 /* ChangeType.Added */:
7768
7989
  addedDocuments = addedDocuments.add(key);
7769
7990
  break;
7770
- case 2 /* Modified */:
7991
+ case 2 /* ChangeType.Modified */:
7771
7992
  modifiedDocuments = modifiedDocuments.add(key);
7772
7993
  break;
7773
- case 1 /* Removed */:
7994
+ case 1 /* ChangeType.Removed */:
7774
7995
  removedDocuments = removedDocuments.add(key);
7775
7996
  break;
7776
7997
  default:
@@ -7846,12 +8067,12 @@ class WatchChangeAggregator {
7846
8067
  this.forEachTarget(targetChange, targetId => {
7847
8068
  const targetState = this.ensureTargetState(targetId);
7848
8069
  switch (targetChange.state) {
7849
- case 0 /* NoChange */:
8070
+ case 0 /* WatchTargetChangeState.NoChange */:
7850
8071
  if (this.isActiveTarget(targetId)) {
7851
8072
  targetState.updateResumeToken(targetChange.resumeToken);
7852
8073
  }
7853
8074
  break;
7854
- case 1 /* Added */:
8075
+ case 1 /* WatchTargetChangeState.Added */:
7855
8076
  // We need to decrement the number of pending acks needed from watch
7856
8077
  // for this targetId.
7857
8078
  targetState.recordTargetResponse();
@@ -7863,7 +8084,7 @@ class WatchChangeAggregator {
7863
8084
  }
7864
8085
  targetState.updateResumeToken(targetChange.resumeToken);
7865
8086
  break;
7866
- case 2 /* Removed */:
8087
+ case 2 /* WatchTargetChangeState.Removed */:
7867
8088
  // We need to keep track of removed targets to we can post-filter and
7868
8089
  // remove any target changes.
7869
8090
  // We need to decrement the number of pending acks needed from watch
@@ -7873,13 +8094,13 @@ class WatchChangeAggregator {
7873
8094
  this.removeTarget(targetId);
7874
8095
  }
7875
8096
  break;
7876
- case 3 /* Current */:
8097
+ case 3 /* WatchTargetChangeState.Current */:
7877
8098
  if (this.isActiveTarget(targetId)) {
7878
8099
  targetState.markCurrent();
7879
8100
  targetState.updateResumeToken(targetChange.resumeToken);
7880
8101
  }
7881
8102
  break;
7882
- case 4 /* Reset */:
8103
+ case 4 /* WatchTargetChangeState.Reset */:
7883
8104
  if (this.isActiveTarget(targetId)) {
7884
8105
  // Reset the target and synthesizes removes for all existing
7885
8106
  // documents. The backend will re-add any documents that still
@@ -7989,7 +8210,7 @@ class WatchChangeAggregator {
7989
8210
  targets.forEachWhile(targetId => {
7990
8211
  const targetData = this.targetDataForActiveTarget(targetId);
7991
8212
  if (targetData &&
7992
- targetData.purpose !== 2 /* LimboResolution */) {
8213
+ targetData.purpose !== 2 /* TargetPurpose.LimboResolution */) {
7993
8214
  isOnlyLimboTarget = false;
7994
8215
  return false;
7995
8216
  }
@@ -8016,8 +8237,8 @@ class WatchChangeAggregator {
8016
8237
  return;
8017
8238
  }
8018
8239
  const changeType = this.targetContainsDocument(targetId, document.key)
8019
- ? 2 /* Modified */
8020
- : 0 /* Added */;
8240
+ ? 2 /* ChangeType.Modified */
8241
+ : 0 /* ChangeType.Added */;
8021
8242
  const targetState = this.ensureTargetState(targetId);
8022
8243
  targetState.addDocumentChange(document.key, changeType);
8023
8244
  this.pendingDocumentUpdates = this.pendingDocumentUpdates.insert(document.key, document);
@@ -8038,7 +8259,7 @@ class WatchChangeAggregator {
8038
8259
  }
8039
8260
  const targetState = this.ensureTargetState(targetId);
8040
8261
  if (this.targetContainsDocument(targetId, key)) {
8041
- targetState.addDocumentChange(key, 1 /* Removed */);
8262
+ targetState.addDocumentChange(key, 1 /* ChangeType.Removed */);
8042
8263
  }
8043
8264
  else {
8044
8265
  // The document may have entered and left the target before we raised a
@@ -8163,22 +8384,28 @@ function snapshotChangesMap() {
8163
8384
  */
8164
8385
  const DIRECTIONS = (() => {
8165
8386
  const dirs = {};
8166
- dirs["asc" /* ASCENDING */] = 'ASCENDING';
8167
- dirs["desc" /* DESCENDING */] = 'DESCENDING';
8387
+ dirs["asc" /* Direction.ASCENDING */] = 'ASCENDING';
8388
+ dirs["desc" /* Direction.DESCENDING */] = 'DESCENDING';
8168
8389
  return dirs;
8169
8390
  })();
8170
8391
  const OPERATORS = (() => {
8171
8392
  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';
8393
+ ops["<" /* Operator.LESS_THAN */] = 'LESS_THAN';
8394
+ ops["<=" /* Operator.LESS_THAN_OR_EQUAL */] = 'LESS_THAN_OR_EQUAL';
8395
+ ops[">" /* Operator.GREATER_THAN */] = 'GREATER_THAN';
8396
+ ops[">=" /* Operator.GREATER_THAN_OR_EQUAL */] = 'GREATER_THAN_OR_EQUAL';
8397
+ ops["==" /* Operator.EQUAL */] = 'EQUAL';
8398
+ ops["!=" /* Operator.NOT_EQUAL */] = 'NOT_EQUAL';
8399
+ ops["array-contains" /* Operator.ARRAY_CONTAINS */] = 'ARRAY_CONTAINS';
8400
+ ops["in" /* Operator.IN */] = 'IN';
8401
+ ops["not-in" /* Operator.NOT_IN */] = 'NOT_IN';
8402
+ ops["array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */] = 'ARRAY_CONTAINS_ANY';
8403
+ return ops;
8404
+ })();
8405
+ const COMPOSITE_OPERATORS = (() => {
8406
+ const ops = {};
8407
+ ops["and" /* CompositeOperator.AND */] = 'AND';
8408
+ ops["or" /* CompositeOperator.OR */] = 'OR';
8182
8409
  return ops;
8183
8410
  })();
8184
8411
  function assertPresent(value, description) {
@@ -8372,14 +8599,21 @@ function toDocument(serializer, document) {
8372
8599
  return {
8373
8600
  name: toName(serializer, document.key),
8374
8601
  fields: document.data.value.mapValue.fields,
8375
- updateTime: toTimestamp(serializer, document.version.toTimestamp())
8602
+ updateTime: toTimestamp(serializer, document.version.toTimestamp()),
8603
+ createTime: toTimestamp(serializer, document.createTime.toTimestamp())
8376
8604
  };
8377
8605
  }
8378
8606
  function fromDocument(serializer, document, hasCommittedMutations) {
8379
8607
  const key = fromName(serializer, document.name);
8380
8608
  const version = fromVersion(document.updateTime);
8609
+ // If we read a document from persistence that is missing createTime, it's due
8610
+ // to older SDK versions not storing this information. In such cases, we'll
8611
+ // set the createTime to zero. This can be removed in the long term.
8612
+ const createTime = document.createTime
8613
+ ? fromVersion(document.createTime)
8614
+ : SnapshotVersion.min();
8381
8615
  const data = new ObjectValue({ mapValue: { fields: document.fields } });
8382
- const result = MutableDocument.newFoundDocument(key, version, data);
8616
+ const result = MutableDocument.newFoundDocument(key, version, createTime, data);
8383
8617
  if (hasCommittedMutations) {
8384
8618
  result.setHasCommittedMutations();
8385
8619
  }
@@ -8391,8 +8625,11 @@ function fromFound(serializer, doc) {
8391
8625
  assertPresent(doc.found.updateTime);
8392
8626
  const key = fromName(serializer, doc.found.name);
8393
8627
  const version = fromVersion(doc.found.updateTime);
8628
+ const createTime = doc.found.createTime
8629
+ ? fromVersion(doc.found.createTime)
8630
+ : SnapshotVersion.min();
8394
8631
  const data = new ObjectValue({ mapValue: { fields: doc.found.fields } });
8395
- return MutableDocument.newFoundDocument(key, version, data);
8632
+ return MutableDocument.newFoundDocument(key, version, createTime, data);
8396
8633
  }
8397
8634
  function fromMissing(serializer, result) {
8398
8635
  hardAssert(!!result.missing);
@@ -8431,10 +8668,13 @@ function fromWatchChange(serializer, change) {
8431
8668
  assertPresent(entityChange.document.updateTime);
8432
8669
  const key = fromName(serializer, entityChange.document.name);
8433
8670
  const version = fromVersion(entityChange.document.updateTime);
8671
+ const createTime = entityChange.document.createTime
8672
+ ? fromVersion(entityChange.document.createTime)
8673
+ : SnapshotVersion.min();
8434
8674
  const data = new ObjectValue({
8435
8675
  mapValue: { fields: entityChange.document.fields }
8436
8676
  });
8437
- const doc = MutableDocument.newFoundDocument(key, version, data);
8677
+ const doc = MutableDocument.newFoundDocument(key, version, createTime, data);
8438
8678
  const updatedTargetIds = entityChange.targetIds || [];
8439
8679
  const removedTargetIds = entityChange.removedTargetIds || [];
8440
8680
  watchChange = new DocumentWatchChange(updatedTargetIds, removedTargetIds, doc.key, doc);
@@ -8476,19 +8716,19 @@ function fromWatchChange(serializer, change) {
8476
8716
  }
8477
8717
  function fromWatchTargetChangeState(state) {
8478
8718
  if (state === 'NO_CHANGE') {
8479
- return 0 /* NoChange */;
8719
+ return 0 /* WatchTargetChangeState.NoChange */;
8480
8720
  }
8481
8721
  else if (state === 'ADD') {
8482
- return 1 /* Added */;
8722
+ return 1 /* WatchTargetChangeState.Added */;
8483
8723
  }
8484
8724
  else if (state === 'REMOVE') {
8485
- return 2 /* Removed */;
8725
+ return 2 /* WatchTargetChangeState.Removed */;
8486
8726
  }
8487
8727
  else if (state === 'CURRENT') {
8488
- return 3 /* Current */;
8728
+ return 3 /* WatchTargetChangeState.Current */;
8489
8729
  }
8490
8730
  else if (state === 'RESET') {
8491
- return 4 /* Reset */;
8731
+ return 4 /* WatchTargetChangeState.Reset */;
8492
8732
  }
8493
8733
  else {
8494
8734
  return fail();
@@ -8706,7 +8946,7 @@ function toQueryTarget(serializer, target) {
8706
8946
  result.parent = toQueryPath(serializer, path.popLast());
8707
8947
  result.structuredQuery.from = [{ collectionId: path.lastSegment() }];
8708
8948
  }
8709
- const where = toFilter(target.filters);
8949
+ const where = toFilters(target.filters);
8710
8950
  if (where) {
8711
8951
  result.structuredQuery.where = where;
8712
8952
  }
@@ -8758,7 +8998,7 @@ function convertQueryTargetToQuery(target) {
8758
8998
  }
8759
8999
  let filterBy = [];
8760
9000
  if (query.where) {
8761
- filterBy = fromFilter(query.where);
9001
+ filterBy = fromFilters(query.where);
8762
9002
  }
8763
9003
  let orderBy = [];
8764
9004
  if (query.orderBy) {
@@ -8776,7 +9016,7 @@ function convertQueryTargetToQuery(target) {
8776
9016
  if (query.endAt) {
8777
9017
  endAt = fromEndAtCursor(query.endAt);
8778
9018
  }
8779
- return newQuery(path, collectionGroup, orderBy, filterBy, limit, "F" /* First */, startAt, endAt);
9019
+ return newQuery(path, collectionGroup, orderBy, filterBy, limit, "F" /* LimitType.First */, startAt, endAt);
8780
9020
  }
8781
9021
  function fromQueryTarget(target) {
8782
9022
  return queryToTarget(convertQueryTargetToQuery(target));
@@ -8794,11 +9034,11 @@ function toListenRequestLabels(serializer, targetData) {
8794
9034
  }
8795
9035
  function toLabel(serializer, purpose) {
8796
9036
  switch (purpose) {
8797
- case 0 /* Listen */:
9037
+ case 0 /* TargetPurpose.Listen */:
8798
9038
  return null;
8799
- case 1 /* ExistenceFilterMismatch */:
9039
+ case 1 /* TargetPurpose.ExistenceFilterMismatch */:
8800
9040
  return 'existence-filter-mismatch';
8801
- case 2 /* LimboResolution */:
9041
+ case 2 /* TargetPurpose.LimboResolution */:
8802
9042
  return 'limbo-document';
8803
9043
  default:
8804
9044
  return fail();
@@ -8825,32 +9065,29 @@ function toTarget(serializer, targetData) {
8825
9065
  }
8826
9066
  return result;
8827
9067
  }
8828
- function toFilter(filters) {
9068
+ function toFilters(filters) {
8829
9069
  if (filters.length === 0) {
8830
9070
  return;
8831
9071
  }
8832
- const protos = filters.map(filter => {
8833
- return toUnaryOrFieldFilter(filter);
8834
- });
8835
- if (protos.length === 1) {
8836
- return protos[0];
9072
+ return toFilter(CompositeFilter.create(filters, "and" /* CompositeOperator.AND */));
9073
+ }
9074
+ function fromFilters(filter) {
9075
+ const result = fromFilter(filter);
9076
+ if (result instanceof CompositeFilter &&
9077
+ compositeFilterIsFlatConjunction(result)) {
9078
+ return result.getFilters();
8837
9079
  }
8838
- return { compositeFilter: { op: 'AND', filters: protos } };
9080
+ return [result];
8839
9081
  }
8840
9082
  function fromFilter(filter) {
8841
- if (!filter) {
8842
- return [];
8843
- }
8844
- else if (filter.unaryFilter !== undefined) {
8845
- return [fromUnaryFilter(filter)];
9083
+ if (filter.unaryFilter !== undefined) {
9084
+ return fromUnaryFilter(filter);
8846
9085
  }
8847
9086
  else if (filter.fieldFilter !== undefined) {
8848
- return [fromFieldFilter(filter)];
9087
+ return fromFieldFilter(filter);
8849
9088
  }
8850
9089
  else if (filter.compositeFilter !== undefined) {
8851
- return filter.compositeFilter
8852
- .filters.map(f => fromFilter(f))
8853
- .reduce((accum, current) => accum.concat(current));
9090
+ return fromCompositeFilter(filter);
8854
9091
  }
8855
9092
  else {
8856
9093
  return fail();
@@ -8895,9 +9132,9 @@ function toDirection(dir) {
8895
9132
  function fromDirection(dir) {
8896
9133
  switch (dir) {
8897
9134
  case 'ASCENDING':
8898
- return "asc" /* ASCENDING */;
9135
+ return "asc" /* Direction.ASCENDING */;
8899
9136
  case 'DESCENDING':
8900
- return "desc" /* DESCENDING */;
9137
+ return "desc" /* Direction.DESCENDING */;
8901
9138
  default:
8902
9139
  return undefined;
8903
9140
  }
@@ -8906,34 +9143,47 @@ function fromDirection(dir) {
8906
9143
  function toOperatorName(op) {
8907
9144
  return OPERATORS[op];
8908
9145
  }
9146
+ function toCompositeOperatorName(op) {
9147
+ return COMPOSITE_OPERATORS[op];
9148
+ }
8909
9149
  function fromOperatorName(op) {
8910
9150
  switch (op) {
8911
9151
  case 'EQUAL':
8912
- return "==" /* EQUAL */;
9152
+ return "==" /* Operator.EQUAL */;
8913
9153
  case 'NOT_EQUAL':
8914
- return "!=" /* NOT_EQUAL */;
9154
+ return "!=" /* Operator.NOT_EQUAL */;
8915
9155
  case 'GREATER_THAN':
8916
- return ">" /* GREATER_THAN */;
9156
+ return ">" /* Operator.GREATER_THAN */;
8917
9157
  case 'GREATER_THAN_OR_EQUAL':
8918
- return ">=" /* GREATER_THAN_OR_EQUAL */;
9158
+ return ">=" /* Operator.GREATER_THAN_OR_EQUAL */;
8919
9159
  case 'LESS_THAN':
8920
- return "<" /* LESS_THAN */;
9160
+ return "<" /* Operator.LESS_THAN */;
8921
9161
  case 'LESS_THAN_OR_EQUAL':
8922
- return "<=" /* LESS_THAN_OR_EQUAL */;
9162
+ return "<=" /* Operator.LESS_THAN_OR_EQUAL */;
8923
9163
  case 'ARRAY_CONTAINS':
8924
- return "array-contains" /* ARRAY_CONTAINS */;
9164
+ return "array-contains" /* Operator.ARRAY_CONTAINS */;
8925
9165
  case 'IN':
8926
- return "in" /* IN */;
9166
+ return "in" /* Operator.IN */;
8927
9167
  case 'NOT_IN':
8928
- return "not-in" /* NOT_IN */;
9168
+ return "not-in" /* Operator.NOT_IN */;
8929
9169
  case 'ARRAY_CONTAINS_ANY':
8930
- return "array-contains-any" /* ARRAY_CONTAINS_ANY */;
9170
+ return "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */;
8931
9171
  case 'OPERATOR_UNSPECIFIED':
8932
9172
  return fail();
8933
9173
  default:
8934
9174
  return fail();
8935
9175
  }
8936
9176
  }
9177
+ function fromCompositeOperatorName(op) {
9178
+ switch (op) {
9179
+ case 'AND':
9180
+ return "and" /* CompositeOperator.AND */;
9181
+ case 'OR':
9182
+ return "or" /* CompositeOperator.OR */;
9183
+ default:
9184
+ return fail();
9185
+ }
9186
+ }
8937
9187
  function toFieldPathReference(path) {
8938
9188
  return { fieldPath: path.canonicalString() };
8939
9189
  }
@@ -8950,12 +9200,32 @@ function toPropertyOrder(orderBy) {
8950
9200
  function fromPropertyOrder(orderBy) {
8951
9201
  return new OrderBy(fromFieldPathReference(orderBy.field), fromDirection(orderBy.direction));
8952
9202
  }
8953
- function fromFieldFilter(filter) {
8954
- return FieldFilter.create(fromFieldPathReference(filter.fieldFilter.field), fromOperatorName(filter.fieldFilter.op), filter.fieldFilter.value);
8955
- }
8956
9203
  // visible for testing
9204
+ function toFilter(filter) {
9205
+ if (filter instanceof FieldFilter) {
9206
+ return toUnaryOrFieldFilter(filter);
9207
+ }
9208
+ else if (filter instanceof CompositeFilter) {
9209
+ return toCompositeFilter(filter);
9210
+ }
9211
+ else {
9212
+ return fail();
9213
+ }
9214
+ }
9215
+ function toCompositeFilter(filter) {
9216
+ const protos = filter.getFilters().map(filter => toFilter(filter));
9217
+ if (protos.length === 1) {
9218
+ return protos[0];
9219
+ }
9220
+ return {
9221
+ compositeFilter: {
9222
+ op: toCompositeOperatorName(filter.op),
9223
+ filters: protos
9224
+ }
9225
+ };
9226
+ }
8957
9227
  function toUnaryOrFieldFilter(filter) {
8958
- if (filter.op === "==" /* EQUAL */) {
9228
+ if (filter.op === "==" /* Operator.EQUAL */) {
8959
9229
  if (isNanValue(filter.value)) {
8960
9230
  return {
8961
9231
  unaryFilter: {
@@ -8973,7 +9243,7 @@ function toUnaryOrFieldFilter(filter) {
8973
9243
  };
8974
9244
  }
8975
9245
  }
8976
- else if (filter.op === "!=" /* NOT_EQUAL */) {
9246
+ else if (filter.op === "!=" /* Operator.NOT_EQUAL */) {
8977
9247
  if (isNanValue(filter.value)) {
8978
9248
  return {
8979
9249
  unaryFilter: {
@@ -9003,22 +9273,22 @@ function fromUnaryFilter(filter) {
9003
9273
  switch (filter.unaryFilter.op) {
9004
9274
  case 'IS_NAN':
9005
9275
  const nanField = fromFieldPathReference(filter.unaryFilter.field);
9006
- return FieldFilter.create(nanField, "==" /* EQUAL */, {
9276
+ return FieldFilter.create(nanField, "==" /* Operator.EQUAL */, {
9007
9277
  doubleValue: NaN
9008
9278
  });
9009
9279
  case 'IS_NULL':
9010
9280
  const nullField = fromFieldPathReference(filter.unaryFilter.field);
9011
- return FieldFilter.create(nullField, "==" /* EQUAL */, {
9281
+ return FieldFilter.create(nullField, "==" /* Operator.EQUAL */, {
9012
9282
  nullValue: 'NULL_VALUE'
9013
9283
  });
9014
9284
  case 'IS_NOT_NAN':
9015
9285
  const notNanField = fromFieldPathReference(filter.unaryFilter.field);
9016
- return FieldFilter.create(notNanField, "!=" /* NOT_EQUAL */, {
9286
+ return FieldFilter.create(notNanField, "!=" /* Operator.NOT_EQUAL */, {
9017
9287
  doubleValue: NaN
9018
9288
  });
9019
9289
  case 'IS_NOT_NULL':
9020
9290
  const notNullField = fromFieldPathReference(filter.unaryFilter.field);
9021
- return FieldFilter.create(notNullField, "!=" /* NOT_EQUAL */, {
9291
+ return FieldFilter.create(notNullField, "!=" /* Operator.NOT_EQUAL */, {
9022
9292
  nullValue: 'NULL_VALUE'
9023
9293
  });
9024
9294
  case 'OPERATOR_UNSPECIFIED':
@@ -9027,6 +9297,12 @@ function fromUnaryFilter(filter) {
9027
9297
  return fail();
9028
9298
  }
9029
9299
  }
9300
+ function fromFieldFilter(filter) {
9301
+ return FieldFilter.create(fromFieldPathReference(filter.fieldFilter.field), fromOperatorName(filter.fieldFilter.op), filter.fieldFilter.value);
9302
+ }
9303
+ function fromCompositeFilter(filter) {
9304
+ return CompositeFilter.create(filter.compositeFilter.filters.map(filter => fromFilter(filter)), fromCompositeOperatorName(filter.compositeFilter.op));
9305
+ }
9030
9306
  function toDocumentMask(fieldMask) {
9031
9307
  const canonicalFields = [];
9032
9308
  fieldMask.fields.forEach(field => canonicalFields.push(field.canonicalString()));
@@ -9266,7 +9542,7 @@ function fromDbTarget(dbTarget) {
9266
9542
  else {
9267
9543
  target = fromQueryTarget(dbTarget.query);
9268
9544
  }
9269
- return new TargetData(target, dbTarget.targetId, 0 /* Listen */, dbTarget.lastListenSequenceNumber, version, lastLimboFreeSnapshotVersion, ByteString.fromBase64String(dbTarget.resumeToken));
9545
+ return new TargetData(target, dbTarget.targetId, 0 /* TargetPurpose.Listen */, dbTarget.lastListenSequenceNumber, version, lastLimboFreeSnapshotVersion, ByteString.fromBase64String(dbTarget.resumeToken));
9270
9546
  }
9271
9547
  /** Encodes TargetData into a DbTarget for storage locally. */
9272
9548
  function toDbTarget(localSerializer, targetData) {
@@ -9343,7 +9619,7 @@ function fromBundledQuery(bundledQuery) {
9343
9619
  structuredQuery: bundledQuery.structuredQuery
9344
9620
  });
9345
9621
  if (bundledQuery.limitType === 'LAST') {
9346
- return queryWithLimit(query, query.limit, "L" /* Last */);
9622
+ return queryWithLimit(query, query.limit, "L" /* LimitType.Last */);
9347
9623
  }
9348
9624
  return query;
9349
9625
  }
@@ -10118,7 +10394,7 @@ class IndexByteEncoder {
10118
10394
  this.orderedCode.seed(encodedBytes);
10119
10395
  }
10120
10396
  forKind(kind) {
10121
- return kind === 0 /* ASCENDING */ ? this.ascending : this.descending;
10397
+ return kind === 0 /* IndexKind.ASCENDING */ ? this.ascending : this.descending;
10122
10398
  }
10123
10399
  encodedBytes() {
10124
10400
  return this.orderedCode.encodedBytes();
@@ -10273,6 +10549,7 @@ class TargetIndexMatcher {
10273
10549
  * omitted.
10274
10550
  */
10275
10551
  servedByIndex(index) {
10552
+ hardAssert(index.collectionGroup === this.collectionId);
10276
10553
  // If there is an array element, find a matching filter.
10277
10554
  const arraySegment = fieldIndexGetArraySegment(index);
10278
10555
  if (arraySegment !== undefined &&
@@ -10336,19 +10613,268 @@ class TargetIndexMatcher {
10336
10613
  if (filter === undefined || !filter.field.isEqual(segment.fieldPath)) {
10337
10614
  return false;
10338
10615
  }
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;
10616
+ const isArrayOperator = filter.op === "array-contains" /* Operator.ARRAY_CONTAINS */ ||
10617
+ filter.op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */;
10618
+ return (segment.kind === 2 /* IndexKind.CONTAINS */) === isArrayOperator;
10342
10619
  }
10343
10620
  matchesOrderBy(orderBy, segment) {
10344
10621
  if (!orderBy.field.isEqual(segment.fieldPath)) {
10345
10622
  return false;
10346
10623
  }
10347
- return ((segment.kind === 0 /* ASCENDING */ &&
10348
- orderBy.dir === "asc" /* ASCENDING */) ||
10349
- (segment.kind === 1 /* DESCENDING */ &&
10350
- orderBy.dir === "desc" /* DESCENDING */));
10624
+ return ((segment.kind === 0 /* IndexKind.ASCENDING */ &&
10625
+ orderBy.dir === "asc" /* Direction.ASCENDING */) ||
10626
+ (segment.kind === 1 /* IndexKind.DESCENDING */ &&
10627
+ orderBy.dir === "desc" /* Direction.DESCENDING */));
10628
+ }
10629
+ }
10630
+
10631
+ /**
10632
+ * @license
10633
+ * Copyright 2022 Google LLC
10634
+ *
10635
+ * Licensed under the Apache License, Version 2.0 (the "License");
10636
+ * you may not use this file except in compliance with the License.
10637
+ * You may obtain a copy of the License at
10638
+ *
10639
+ * http://www.apache.org/licenses/LICENSE-2.0
10640
+ *
10641
+ * Unless required by applicable law or agreed to in writing, software
10642
+ * distributed under the License is distributed on an "AS IS" BASIS,
10643
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10644
+ * See the License for the specific language governing permissions and
10645
+ * limitations under the License.
10646
+ */
10647
+ /**
10648
+ * Provides utility functions that help with boolean logic transformations needed for handling
10649
+ * complex filters used in queries.
10650
+ */
10651
+ /**
10652
+ * The `in` filter is only a syntactic sugar over a disjunction of equalities. For instance: `a in
10653
+ * [1,2,3]` is in fact `a==1 || a==2 || a==3`. This method expands any `in` filter in the given
10654
+ * input into a disjunction of equality filters and returns the expanded filter.
10655
+ */
10656
+ function computeInExpansion(filter) {
10657
+ var _a, _b;
10658
+ hardAssert(filter instanceof FieldFilter || filter instanceof CompositeFilter);
10659
+ if (filter instanceof FieldFilter) {
10660
+ if (filter instanceof InFilter) {
10661
+ 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))) || [];
10662
+ return CompositeFilter.create(expandedFilters, "or" /* CompositeOperator.OR */);
10663
+ }
10664
+ else {
10665
+ // We have reached other kinds of field filters.
10666
+ return filter;
10667
+ }
10668
+ }
10669
+ // We have a composite filter.
10670
+ const expandedFilters = filter.filters.map(subfilter => computeInExpansion(subfilter));
10671
+ return CompositeFilter.create(expandedFilters, filter.op);
10672
+ }
10673
+ /**
10674
+ * Given a composite filter, returns the list of terms in its disjunctive normal form.
10675
+ *
10676
+ * <p>Each element in the return value is one term of the resulting DNF. For instance: For the
10677
+ * input: (A || B) && C, the DNF form is: (A && C) || (B && C), and the return value is a list
10678
+ * with two elements: a composite filter that performs (A && C), and a composite filter that
10679
+ * performs (B && C).
10680
+ *
10681
+ * @param filter the composite filter to calculate DNF transform for.
10682
+ * @return the terms in the DNF transform.
10683
+ */
10684
+ function getDnfTerms(filter) {
10685
+ if (filter.getFilters().length === 0) {
10686
+ return [];
10687
+ }
10688
+ const result = computeDistributedNormalForm(computeInExpansion(filter));
10689
+ hardAssert(isDisjunctiveNormalForm(result));
10690
+ if (isSingleFieldFilter(result) || isFlatConjunction(result)) {
10691
+ return [result];
10692
+ }
10693
+ return result.getFilters();
10694
+ }
10695
+ /** Returns true if the given filter is a single field filter. e.g. (a == 10). */
10696
+ function isSingleFieldFilter(filter) {
10697
+ return filter instanceof FieldFilter;
10698
+ }
10699
+ /**
10700
+ * Returns true if the given filter is the conjunction of one or more field filters. e.g. (a == 10
10701
+ * && b == 20)
10702
+ */
10703
+ function isFlatConjunction(filter) {
10704
+ return (filter instanceof CompositeFilter &&
10705
+ compositeFilterIsFlatConjunction(filter));
10706
+ }
10707
+ /**
10708
+ * Returns whether or not the given filter is in disjunctive normal form (DNF).
10709
+ *
10710
+ * <p>In boolean logic, a disjunctive normal form (DNF) is a canonical normal form of a logical
10711
+ * formula consisting of a disjunction of conjunctions; it can also be described as an OR of ANDs.
10712
+ *
10713
+ * <p>For more info, visit: https://en.wikipedia.org/wiki/Disjunctive_normal_form
10714
+ */
10715
+ function isDisjunctiveNormalForm(filter) {
10716
+ return (isSingleFieldFilter(filter) ||
10717
+ isFlatConjunction(filter) ||
10718
+ isDisjunctionOfFieldFiltersAndFlatConjunctions(filter));
10719
+ }
10720
+ /**
10721
+ * Returns true if the given filter is the disjunction of one or more "flat conjunctions" and
10722
+ * field filters. e.g. (a == 10) || (b==20 && c==30)
10723
+ */
10724
+ function isDisjunctionOfFieldFiltersAndFlatConjunctions(filter) {
10725
+ if (filter instanceof CompositeFilter) {
10726
+ if (compositeFilterIsDisjunction(filter)) {
10727
+ for (const subFilter of filter.getFilters()) {
10728
+ if (!isSingleFieldFilter(subFilter) && !isFlatConjunction(subFilter)) {
10729
+ return false;
10730
+ }
10731
+ }
10732
+ return true;
10733
+ }
10734
+ }
10735
+ return false;
10736
+ }
10737
+ function computeDistributedNormalForm(filter) {
10738
+ hardAssert(filter instanceof FieldFilter || filter instanceof CompositeFilter);
10739
+ if (filter instanceof FieldFilter) {
10740
+ return filter;
10741
+ }
10742
+ if (filter.filters.length === 1) {
10743
+ return computeDistributedNormalForm(filter.filters[0]);
10744
+ }
10745
+ // Compute DNF for each of the subfilters first
10746
+ const result = filter.filters.map(subfilter => computeDistributedNormalForm(subfilter));
10747
+ let newFilter = CompositeFilter.create(result, filter.op);
10748
+ newFilter = applyAssociation(newFilter);
10749
+ if (isDisjunctiveNormalForm(newFilter)) {
10750
+ return newFilter;
10751
+ }
10752
+ hardAssert(newFilter instanceof CompositeFilter);
10753
+ hardAssert(compositeFilterIsConjunction(newFilter));
10754
+ hardAssert(newFilter.filters.length > 1);
10755
+ return newFilter.filters.reduce((runningResult, filter) => applyDistribution(runningResult, filter));
10756
+ }
10757
+ function applyDistribution(lhs, rhs) {
10758
+ hardAssert(lhs instanceof FieldFilter || lhs instanceof CompositeFilter);
10759
+ hardAssert(rhs instanceof FieldFilter || rhs instanceof CompositeFilter);
10760
+ let result;
10761
+ if (lhs instanceof FieldFilter) {
10762
+ if (rhs instanceof FieldFilter) {
10763
+ // FieldFilter FieldFilter
10764
+ result = applyDistributionFieldFilters(lhs, rhs);
10765
+ }
10766
+ else {
10767
+ // FieldFilter CompositeFilter
10768
+ result = applyDistributionFieldAndCompositeFilters(lhs, rhs);
10769
+ }
10770
+ }
10771
+ else {
10772
+ if (rhs instanceof FieldFilter) {
10773
+ // CompositeFilter FieldFilter
10774
+ result = applyDistributionFieldAndCompositeFilters(rhs, lhs);
10775
+ }
10776
+ else {
10777
+ // CompositeFilter CompositeFilter
10778
+ result = applyDistributionCompositeFilters(lhs, rhs);
10779
+ }
10780
+ }
10781
+ return applyAssociation(result);
10782
+ }
10783
+ function applyDistributionFieldFilters(lhs, rhs) {
10784
+ // Conjunction distribution for two field filters is the conjunction of them.
10785
+ return CompositeFilter.create([lhs, rhs], "and" /* CompositeOperator.AND */);
10786
+ }
10787
+ function applyDistributionCompositeFilters(lhs, rhs) {
10788
+ hardAssert(lhs.filters.length > 0 && rhs.filters.length > 0);
10789
+ // There are four cases:
10790
+ // (A & B) & (C & D) --> (A & B & C & D)
10791
+ // (A & B) & (C | D) --> (A & B & C) | (A & B & D)
10792
+ // (A | B) & (C & D) --> (C & D & A) | (C & D & B)
10793
+ // (A | B) & (C | D) --> (A & C) | (A & D) | (B & C) | (B & D)
10794
+ // Case 1 is a merge.
10795
+ if (compositeFilterIsConjunction(lhs) && compositeFilterIsConjunction(rhs)) {
10796
+ return compositeFilterWithAddedFilters(lhs, rhs.getFilters());
10797
+ }
10798
+ // Case 2,3,4 all have at least one side (lhs or rhs) that is a disjunction. In all three cases
10799
+ // we should take each element of the disjunction and distribute it over the other side, and
10800
+ // return the disjunction of the distribution results.
10801
+ const disjunctionSide = compositeFilterIsDisjunction(lhs) ? lhs : rhs;
10802
+ const otherSide = compositeFilterIsDisjunction(lhs) ? rhs : lhs;
10803
+ const results = disjunctionSide.filters.map(subfilter => applyDistribution(subfilter, otherSide));
10804
+ return CompositeFilter.create(results, "or" /* CompositeOperator.OR */);
10805
+ }
10806
+ function applyDistributionFieldAndCompositeFilters(fieldFilter, compositeFilter) {
10807
+ // There are two cases:
10808
+ // A & (B & C) --> (A & B & C)
10809
+ // A & (B | C) --> (A & B) | (A & C)
10810
+ if (compositeFilterIsConjunction(compositeFilter)) {
10811
+ // Case 1
10812
+ return compositeFilterWithAddedFilters(compositeFilter, fieldFilter.getFilters());
10813
+ }
10814
+ else {
10815
+ // Case 2
10816
+ const newFilters = compositeFilter.filters.map(subfilter => applyDistribution(fieldFilter, subfilter));
10817
+ return CompositeFilter.create(newFilters, "or" /* CompositeOperator.OR */);
10818
+ }
10819
+ }
10820
+ /**
10821
+ * Applies the associativity property to the given filter and returns the resulting filter.
10822
+ *
10823
+ * <ul>
10824
+ * <li>A | (B | C) == (A | B) | C == (A | B | C)
10825
+ * <li>A & (B & C) == (A & B) & C == (A & B & C)
10826
+ * </ul>
10827
+ *
10828
+ * <p>For more info, visit: https://en.wikipedia.org/wiki/Associative_property#Propositional_logic
10829
+ */
10830
+ function applyAssociation(filter) {
10831
+ hardAssert(filter instanceof FieldFilter || filter instanceof CompositeFilter);
10832
+ if (filter instanceof FieldFilter) {
10833
+ return filter;
10834
+ }
10835
+ const filters = filter.getFilters();
10836
+ // If the composite filter only contains 1 filter, apply associativity to it.
10837
+ if (filters.length === 1) {
10838
+ return applyAssociation(filters[0]);
10839
+ }
10840
+ // Associativity applied to a flat composite filter results is itself.
10841
+ if (compositeFilterIsFlat(filter)) {
10842
+ return filter;
10843
+ }
10844
+ // First apply associativity to all subfilters. This will in turn recursively apply
10845
+ // associativity to all nested composite filters and field filters.
10846
+ const updatedFilters = filters.map(subfilter => applyAssociation(subfilter));
10847
+ // For composite subfilters that perform the same kind of logical operation as `compositeFilter`
10848
+ // take out their filters and add them to `compositeFilter`. For example:
10849
+ // compositeFilter = (A | (B | C | D))
10850
+ // compositeSubfilter = (B | C | D)
10851
+ // Result: (A | B | C | D)
10852
+ // Note that the `compositeSubfilter` has been eliminated, and its filters (B, C, D) have been
10853
+ // added to the top-level "compositeFilter".
10854
+ const newSubfilters = [];
10855
+ updatedFilters.forEach(subfilter => {
10856
+ if (subfilter instanceof FieldFilter) {
10857
+ newSubfilters.push(subfilter);
10858
+ }
10859
+ else if (subfilter instanceof CompositeFilter) {
10860
+ if (subfilter.op === filter.op) {
10861
+ // compositeFilter: (A | (B | C))
10862
+ // compositeSubfilter: (B | C)
10863
+ // Result: (A | B | C)
10864
+ newSubfilters.push(...subfilter.filters);
10865
+ }
10866
+ else {
10867
+ // compositeFilter: (A | (B & C))
10868
+ // compositeSubfilter: (B & C)
10869
+ // Result: (A | (B & C))
10870
+ newSubfilters.push(subfilter);
10871
+ }
10872
+ }
10873
+ });
10874
+ if (newSubfilters.length === 1) {
10875
+ return newSubfilters[0];
10351
10876
  }
10877
+ return CompositeFilter.create(newSubfilters, filter.op);
10352
10878
  }
10353
10879
 
10354
10880
  /**
@@ -10395,7 +10921,7 @@ class MemoryIndexManager {
10395
10921
  }
10396
10922
  getIndexType(transaction, target) {
10397
10923
  // Field indices are not supported with memory persistence.
10398
- return PersistencePromise.resolve(0 /* NONE */);
10924
+ return PersistencePromise.resolve(0 /* IndexType.NONE */);
10399
10925
  }
10400
10926
  getFieldIndexes(transaction, collectionGroup) {
10401
10927
  // Field indices are not supported with memory persistence.
@@ -10618,8 +11144,14 @@ class IndexedDbIndexManager {
10618
11144
  if (subTargets) {
10619
11145
  return subTargets;
10620
11146
  }
10621
- // TODO(orquery): Implement DNF transform
10622
- subTargets = [target];
11147
+ if (target.filters.length === 0) {
11148
+ subTargets = [target];
11149
+ }
11150
+ else {
11151
+ // There is an implicit AND operation between all the filters stored in the target
11152
+ const dnf = getDnfTerms(CompositeFilter.create(target.filters, "and" /* CompositeOperator.AND */));
11153
+ subTargets = dnf.map(term => newTarget(target.path, target.collectionGroup, target.orderBy, term.getFilters(), target.limit, target.startAt, target.endAt));
11154
+ }
10623
11155
  this.targetToDnfSubTargets.set(target, subTargets);
10624
11156
  return subTargets;
10625
11157
  }
@@ -10677,18 +11209,29 @@ class IndexedDbIndexManager {
10677
11209
  });
10678
11210
  }
10679
11211
  getIndexType(transaction, target) {
10680
- let indexType = 2 /* FULL */;
10681
- return PersistencePromise.forEach(this.getSubTargets(target), (target) => {
11212
+ let indexType = 2 /* IndexType.FULL */;
11213
+ const subTargets = this.getSubTargets(target);
11214
+ return PersistencePromise.forEach(subTargets, (target) => {
10682
11215
  return this.getFieldIndex(transaction, target).next(index => {
10683
11216
  if (!index) {
10684
- indexType = 0 /* NONE */;
11217
+ indexType = 0 /* IndexType.NONE */;
10685
11218
  }
10686
- else if (indexType !== 0 /* NONE */ &&
11219
+ else if (indexType !== 0 /* IndexType.NONE */ &&
10687
11220
  index.fields.length < targetGetSegmentCount(target)) {
10688
- indexType = 1 /* PARTIAL */;
11221
+ indexType = 1 /* IndexType.PARTIAL */;
10689
11222
  }
10690
11223
  });
10691
- }).next(() => indexType);
11224
+ }).next(() => {
11225
+ // OR queries have more than one sub-target (one sub-target per DNF term). We currently consider
11226
+ // OR queries that have a `limit` to have a partial index. For such queries we perform sorting
11227
+ // and apply the limit in memory as a post-processing step.
11228
+ if (targetHasLimit(target) &&
11229
+ subTargets.length > 1 &&
11230
+ indexType === 2 /* IndexType.FULL */) {
11231
+ return 1 /* IndexType.PARTIAL */;
11232
+ }
11233
+ return indexType;
11234
+ });
10692
11235
  }
10693
11236
  /**
10694
11237
  * Returns the byte encoded form of the directional values in the field index.
@@ -10710,7 +11253,7 @@ class IndexedDbIndexManager {
10710
11253
  /** Encodes a single value to the ascending index format. */
10711
11254
  encodeSingleElement(value) {
10712
11255
  const encoder = new IndexByteEncoder();
10713
- FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, encoder.forKind(0 /* ASCENDING */));
11256
+ FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, encoder.forKind(0 /* IndexKind.ASCENDING */));
10714
11257
  return encoder.encodedBytes();
10715
11258
  }
10716
11259
  /**
@@ -10726,15 +11269,15 @@ class IndexedDbIndexManager {
10726
11269
  * Encodes the given field values according to the specification in `target`.
10727
11270
  * For IN queries, a list of possible values is returned.
10728
11271
  */
10729
- encodeValues(fieldIndex, target, bound) {
10730
- if (bound === null) {
11272
+ encodeValues(fieldIndex, target, values) {
11273
+ if (values === null) {
10731
11274
  return [];
10732
11275
  }
10733
11276
  let encoders = [];
10734
11277
  encoders.push(new IndexByteEncoder());
10735
- let boundIdx = 0;
11278
+ let valueIdx = 0;
10736
11279
  for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
10737
- const value = bound[boundIdx++];
11280
+ const value = values[valueIdx++];
10738
11281
  for (const encoder of encoders) {
10739
11282
  if (this.isInFilter(target, segment.fieldPath) && isArray(value)) {
10740
11283
  encoders = this.expandIndexValues(encoders, segment, value);
@@ -10785,7 +11328,7 @@ class IndexedDbIndexManager {
10785
11328
  isInFilter(target, fieldPath) {
10786
11329
  return !!target.filters.find(f => f instanceof FieldFilter &&
10787
11330
  f.field.isEqual(fieldPath) &&
10788
- (f.op === "in" /* IN */ || f.op === "not-in" /* NOT_IN */));
11331
+ (f.op === "in" /* Operator.IN */ || f.op === "not-in" /* Operator.NOT_IN */));
10789
11332
  }
10790
11333
  getFieldIndexes(transaction, collectionGroup) {
10791
11334
  const indexes = indexConfigurationStore(transaction);
@@ -10974,24 +11517,36 @@ class IndexedDbIndexManager {
10974
11517
  bounds.push(upper);
10975
11518
  const ranges = [];
10976
11519
  for (let i = 0; i < bounds.length; i += 2) {
10977
- ranges.push(IDBKeyRange.bound([
11520
+ // If we encounter two bounds that will create an unmatchable key range,
11521
+ // then we return an empty set of key ranges.
11522
+ if (this.isRangeMatchable(bounds[i], bounds[i + 1])) {
11523
+ return [];
11524
+ }
11525
+ const lowerBound = [
10978
11526
  bounds[i].indexId,
10979
11527
  this.uid,
10980
11528
  bounds[i].arrayValue,
10981
11529
  bounds[i].directionalValue,
10982
11530
  EMPTY_VALUE,
10983
11531
  []
10984
- ], [
11532
+ ];
11533
+ const upperBound = [
10985
11534
  bounds[i + 1].indexId,
10986
11535
  this.uid,
10987
11536
  bounds[i + 1].arrayValue,
10988
11537
  bounds[i + 1].directionalValue,
10989
11538
  EMPTY_VALUE,
10990
11539
  []
10991
- ]));
11540
+ ];
11541
+ ranges.push(IDBKeyRange.bound(lowerBound, upperBound));
10992
11542
  }
10993
11543
  return ranges;
10994
11544
  }
11545
+ isRangeMatchable(lowerBound, upperBound) {
11546
+ // If lower bound is greater than the upper bound, then the key
11547
+ // range can never be matched.
11548
+ return indexEntryComparator(lowerBound, upperBound) > 0;
11549
+ }
10995
11550
  getMinOffsetFromCollectionGroup(transaction, collectionGroup) {
10996
11551
  return this.getFieldIndexes(transaction, collectionGroup).next(getMinOffsetFromFieldIndexes);
10997
11552
  }
@@ -11968,7 +12523,7 @@ class LruScheduler {
11968
12523
  }
11969
12524
  scheduleGC(delay) {
11970
12525
  logDebug(LOG_TAG$e, `Garbage collection scheduled in ${delay}ms`);
11971
- this.gcTask = this.asyncQueue.enqueueAfterDelay("lru_garbage_collection" /* LruGarbageCollection */, delay, async () => {
12526
+ this.gcTask = this.asyncQueue.enqueueAfterDelay("lru_garbage_collection" /* TimerId.LruGarbageCollection */, delay, async () => {
11972
12527
  this.gcTask = null;
11973
12528
  try {
11974
12529
  await this.localStore.collectGarbage(this.garbageCollector);
@@ -12866,7 +13421,7 @@ class LocalDocumentsView {
12866
13421
  .getOverlay(transaction, key)
12867
13422
  .next(value => {
12868
13423
  overlay = value;
12869
- return this.getBaseDocument(transaction, key, overlay);
13424
+ return this.remoteDocumentCache.getEntry(transaction, key);
12870
13425
  })
12871
13426
  .next(document => {
12872
13427
  if (overlay !== null) {
@@ -13089,7 +13644,9 @@ class LocalDocumentsView {
13089
13644
  if (originalDocs.get(key)) {
13090
13645
  return PersistencePromise.resolve();
13091
13646
  }
13092
- return this.getBaseDocument(transaction, key, overlay).next(doc => {
13647
+ return this.remoteDocumentCache
13648
+ .getEntry(transaction, key)
13649
+ .next(doc => {
13093
13650
  modifiedDocs = modifiedDocs.insert(key, doc);
13094
13651
  });
13095
13652
  })
@@ -13163,12 +13720,6 @@ class LocalDocumentsView {
13163
13720
  return results;
13164
13721
  });
13165
13722
  }
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
13723
  }
13173
13724
 
13174
13725
  /**
@@ -14918,7 +15469,7 @@ class IndexedDbPersistence {
14918
15469
  * extend or acquire the primary lease if the client is eligible.
14919
15470
  */
14920
15471
  scheduleClientMetadataAndPrimaryLeaseRefreshes() {
14921
- this.clientMetadataRefresher = this.queue.enqueueAfterDelay("client_metadata_refresh" /* ClientMetadataRefresh */, CLIENT_METADATA_REFRESH_INTERVAL_MS, () => {
15472
+ this.clientMetadataRefresher = this.queue.enqueueAfterDelay("client_metadata_refresh" /* TimerId.ClientMetadataRefresh */, CLIENT_METADATA_REFRESH_INTERVAL_MS, () => {
14922
15473
  return this.updateClientMetadataAndTryBecomePrimary()
14923
15474
  .then(() => this.maybeGarbageCollectMultiClientState())
14924
15475
  .then(() => this.scheduleClientMetadataAndPrimaryLeaseRefreshes());
@@ -15928,7 +16479,7 @@ function localStoreAllocateTarget(localStore, target) {
15928
16479
  return localStoreImpl.targetCache
15929
16480
  .allocateTargetId(txn)
15930
16481
  .next(targetId => {
15931
- targetData = new TargetData(target, targetId, 0 /* Listen */, txn.currentSequenceNumber);
16482
+ targetData = new TargetData(target, targetId, 0 /* TargetPurpose.Listen */, txn.currentSequenceNumber);
15932
16483
  return localStoreImpl.targetCache
15933
16484
  .addTargetData(txn, targetData)
15934
16485
  .next(() => targetData);
@@ -16350,11 +16901,11 @@ class QueryEngine {
16350
16901
  return this.indexManager
16351
16902
  .getIndexType(transaction, target)
16352
16903
  .next(indexType => {
16353
- if (indexType === 0 /* NONE */) {
16904
+ if (indexType === 0 /* IndexType.NONE */) {
16354
16905
  // The target cannot be served from any index.
16355
16906
  return null;
16356
16907
  }
16357
- if (query.limit !== null && indexType === 1 /* PARTIAL */) {
16908
+ if (query.limit !== null && indexType === 1 /* IndexType.PARTIAL */) {
16358
16909
  // We cannot apply a limit for targets that are served using a partial
16359
16910
  // index. If a partial index will be used to serve the target, the
16360
16911
  // query may return a superset of documents that match the target
@@ -16362,7 +16913,7 @@ class QueryEngine {
16362
16913
  // may return the correct set of documents in the wrong order (e.g. if
16363
16914
  // the index doesn't include a segment for one of the orderBys).
16364
16915
  // Therefore, a limit should not be applied in such cases.
16365
- query = queryWithLimit(query, null, "F" /* First */);
16916
+ query = queryWithLimit(query, null, "F" /* LimitType.First */);
16366
16917
  target = queryToTarget(query);
16367
16918
  }
16368
16919
  return this.indexManager
@@ -16383,7 +16934,7 @@ class QueryEngine {
16383
16934
  // query's filters are included in the result set. The SDK
16384
16935
  // can then apply the limit once all local edits are
16385
16936
  // incorporated.
16386
- return this.performQueryUsingIndex(transaction, queryWithLimit(query, null, "F" /* First */));
16937
+ return this.performQueryUsingIndex(transaction, queryWithLimit(query, null, "F" /* LimitType.First */));
16387
16938
  }
16388
16939
  return this.appendRemainingResults(transaction, previousResults, query, offset);
16389
16940
  });
@@ -16462,7 +17013,7 @@ class QueryEngine {
16462
17013
  // the boundary of the limit itself did not change and documents from cache
16463
17014
  // will continue to be "rejected" by this boundary. Therefore, we can ignore
16464
17015
  // any modifications that don't affect the last document.
16465
- const docAtLimitEdge = query.limitType === "F" /* First */
17016
+ const docAtLimitEdge = query.limitType === "F" /* LimitType.First */
16466
17017
  ? sortedPreviousResults.last()
16467
17018
  : sortedPreviousResults.first();
16468
17019
  if (!docAtLimitEdge) {
@@ -17383,7 +17934,7 @@ function nodePromise(action) {
17383
17934
  */
17384
17935
  // TODO: Fetch runtime version from grpc-js/package.json instead
17385
17936
  // when there's a cleaner way to dynamic require JSON in both Node ESM and CJS
17386
- const grpcVersion = '1.3.7';
17937
+ const grpcVersion = '1.7.3';
17387
17938
  const LOG_TAG$9 = 'Connection';
17388
17939
  const X_GOOG_API_CLIENT_VALUE = `gl-node/${process.versions.node} fire/${SDK_VERSION} grpc/${grpcVersion}`;
17389
17940
  function createMetadata(databasePath, authToken, appCheckToken, appId) {
@@ -19831,7 +20382,8 @@ const nested = {
19831
20382
  Operator: {
19832
20383
  values: {
19833
20384
  OPERATOR_UNSPECIFIED: 0,
19834
- AND: 1
20385
+ AND: 1,
20386
+ OR: 2
19835
20387
  }
19836
20388
  }
19837
20389
  }
@@ -20707,7 +21259,7 @@ class PersistentStream {
20707
21259
  this.authCredentialsProvider = authCredentialsProvider;
20708
21260
  this.appCheckCredentialsProvider = appCheckCredentialsProvider;
20709
21261
  this.listener = listener;
20710
- this.state = 0 /* Initial */;
21262
+ this.state = 0 /* PersistentStreamState.Initial */;
20711
21263
  /**
20712
21264
  * A close count that's incremented every time the stream is closed; used by
20713
21265
  * getCloseGuardedDispatcher() to invalidate callbacks that happen after
@@ -20727,8 +21279,8 @@ class PersistentStream {
20727
21279
  * outbound requests.
20728
21280
  */
20729
21281
  isStarted() {
20730
- return (this.state === 1 /* Starting */ ||
20731
- this.state === 5 /* Backoff */ ||
21282
+ return (this.state === 1 /* PersistentStreamState.Starting */ ||
21283
+ this.state === 5 /* PersistentStreamState.Backoff */ ||
20732
21284
  this.isOpen());
20733
21285
  }
20734
21286
  /**
@@ -20736,8 +21288,8 @@ class PersistentStream {
20736
21288
  * called) and the stream is ready for outbound requests.
20737
21289
  */
20738
21290
  isOpen() {
20739
- return (this.state === 2 /* Open */ ||
20740
- this.state === 3 /* Healthy */);
21291
+ return (this.state === 2 /* PersistentStreamState.Open */ ||
21292
+ this.state === 3 /* PersistentStreamState.Healthy */);
20741
21293
  }
20742
21294
  /**
20743
21295
  * Starts the RPC. Only allowed if isStarted() returns false. The stream is
@@ -20747,7 +21299,7 @@ class PersistentStream {
20747
21299
  * When start returns, isStarted() will return true.
20748
21300
  */
20749
21301
  start() {
20750
- if (this.state === 4 /* Error */) {
21302
+ if (this.state === 4 /* PersistentStreamState.Error */) {
20751
21303
  this.performBackoff();
20752
21304
  return;
20753
21305
  }
@@ -20761,7 +21313,7 @@ class PersistentStream {
20761
21313
  */
20762
21314
  async stop() {
20763
21315
  if (this.isStarted()) {
20764
- await this.close(0 /* Initial */);
21316
+ await this.close(0 /* PersistentStreamState.Initial */);
20765
21317
  }
20766
21318
  }
20767
21319
  /**
@@ -20773,7 +21325,7 @@ class PersistentStream {
20773
21325
  * inhibit backoff if required.
20774
21326
  */
20775
21327
  inhibitBackoff() {
20776
- this.state = 0 /* Initial */;
21328
+ this.state = 0 /* PersistentStreamState.Initial */;
20777
21329
  this.backoff.reset();
20778
21330
  }
20779
21331
  /**
@@ -20803,7 +21355,7 @@ class PersistentStream {
20803
21355
  if (this.isOpen()) {
20804
21356
  // When timing out an idle stream there's no reason to force the stream into backoff when
20805
21357
  // it restarts so set the stream state to Initial instead of Error.
20806
- return this.close(0 /* Initial */);
21358
+ return this.close(0 /* PersistentStreamState.Initial */);
20807
21359
  }
20808
21360
  }
20809
21361
  /** Marks the stream as active again. */
@@ -20841,7 +21393,7 @@ class PersistentStream {
20841
21393
  // Invalidates any stream-related callbacks (e.g. from auth or the
20842
21394
  // underlying stream), guaranteeing they won't execute.
20843
21395
  this.closeCount++;
20844
- if (finalState !== 4 /* Error */) {
21396
+ if (finalState !== 4 /* PersistentStreamState.Error */) {
20845
21397
  // If this is an intentional close ensure we don't delay our next connection attempt.
20846
21398
  this.backoff.reset();
20847
21399
  }
@@ -20853,7 +21405,7 @@ class PersistentStream {
20853
21405
  }
20854
21406
  else if (error &&
20855
21407
  error.code === Code.UNAUTHENTICATED &&
20856
- this.state !== 3 /* Healthy */) {
21408
+ this.state !== 3 /* PersistentStreamState.Healthy */) {
20857
21409
  // "unauthenticated" error means the token was rejected. This should rarely
20858
21410
  // happen since both Auth and AppCheck ensure a sufficient TTL when we
20859
21411
  // request a token. If a user manually resets their system clock this can
@@ -20881,7 +21433,7 @@ class PersistentStream {
20881
21433
  */
20882
21434
  tearDown() { }
20883
21435
  auth() {
20884
- this.state = 1 /* Starting */;
21436
+ this.state = 1 /* PersistentStreamState.Starting */;
20885
21437
  const dispatchIfNotClosed = this.getCloseGuardedDispatcher(this.closeCount);
20886
21438
  // TODO(mikelehen): Just use dispatchIfNotClosed, but see TODO below.
20887
21439
  const closeCount = this.closeCount;
@@ -20911,10 +21463,10 @@ class PersistentStream {
20911
21463
  this.stream = this.startRpc(authToken, appCheckToken);
20912
21464
  this.stream.onOpen(() => {
20913
21465
  dispatchIfNotClosed(() => {
20914
- this.state = 2 /* Open */;
21466
+ this.state = 2 /* PersistentStreamState.Open */;
20915
21467
  this.healthCheck = this.queue.enqueueAfterDelay(this.healthTimerId, HEALTHY_TIMEOUT_MS, () => {
20916
21468
  if (this.isOpen()) {
20917
- this.state = 3 /* Healthy */;
21469
+ this.state = 3 /* PersistentStreamState.Healthy */;
20918
21470
  }
20919
21471
  return Promise.resolve();
20920
21472
  });
@@ -20933,9 +21485,9 @@ class PersistentStream {
20933
21485
  });
20934
21486
  }
20935
21487
  performBackoff() {
20936
- this.state = 5 /* Backoff */;
21488
+ this.state = 5 /* PersistentStreamState.Backoff */;
20937
21489
  this.backoff.backoffAndRun(async () => {
20938
- this.state = 0 /* Initial */;
21490
+ this.state = 0 /* PersistentStreamState.Initial */;
20939
21491
  this.start();
20940
21492
  });
20941
21493
  }
@@ -20947,7 +21499,7 @@ class PersistentStream {
20947
21499
  // we never expect this to happen because if we stop a stream ourselves,
20948
21500
  // this callback will never be called. To prevent cases where we retry
20949
21501
  // without a backoff accidentally, we set the stream to error in all cases.
20950
- return this.close(4 /* Error */, error);
21502
+ return this.close(4 /* PersistentStreamState.Error */, error);
20951
21503
  }
20952
21504
  /**
20953
21505
  * Returns a "dispatcher" function that dispatches operations onto the
@@ -20978,7 +21530,7 @@ class PersistentStream {
20978
21530
  */
20979
21531
  class PersistentListenStream extends PersistentStream {
20980
21532
  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);
21533
+ super(queue, "listen_stream_connection_backoff" /* TimerId.ListenStreamConnectionBackoff */, "listen_stream_idle" /* TimerId.ListenStreamIdle */, "health_check_timeout" /* TimerId.HealthCheckTimeout */, connection, authCredentials, appCheckCredentials, listener);
20982
21534
  this.serializer = serializer;
20983
21535
  }
20984
21536
  startRpc(authToken, appCheckToken) {
@@ -21037,7 +21589,7 @@ class PersistentListenStream extends PersistentStream {
21037
21589
  */
21038
21590
  class PersistentWriteStream extends PersistentStream {
21039
21591
  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);
21592
+ super(queue, "write_stream_connection_backoff" /* TimerId.WriteStreamConnectionBackoff */, "write_stream_idle" /* TimerId.WriteStreamIdle */, "health_check_timeout" /* TimerId.HealthCheckTimeout */, connection, authCredentials, appCheckCredentials, listener);
21041
21593
  this.serializer = serializer;
21042
21594
  this.handshakeComplete_ = false;
21043
21595
  }
@@ -21296,7 +21848,7 @@ class OnlineStateTracker {
21296
21848
  this.asyncQueue = asyncQueue;
21297
21849
  this.onlineStateHandler = onlineStateHandler;
21298
21850
  /** The current OnlineState. */
21299
- this.state = "Unknown" /* Unknown */;
21851
+ this.state = "Unknown" /* OnlineState.Unknown */;
21300
21852
  /**
21301
21853
  * A count of consecutive failures to open the stream. If it reaches the
21302
21854
  * maximum defined by MAX_WATCH_STREAM_FAILURES, we'll set the OnlineState to
@@ -21325,12 +21877,12 @@ class OnlineStateTracker {
21325
21877
  */
21326
21878
  handleWatchStreamStart() {
21327
21879
  if (this.watchStreamFailures === 0) {
21328
- this.setAndBroadcast("Unknown" /* Unknown */);
21329
- this.onlineStateTimer = this.asyncQueue.enqueueAfterDelay("online_state_timeout" /* OnlineStateTimeout */, ONLINE_STATE_TIMEOUT_MS, () => {
21880
+ this.setAndBroadcast("Unknown" /* OnlineState.Unknown */);
21881
+ this.onlineStateTimer = this.asyncQueue.enqueueAfterDelay("online_state_timeout" /* TimerId.OnlineStateTimeout */, ONLINE_STATE_TIMEOUT_MS, () => {
21330
21882
  this.onlineStateTimer = null;
21331
21883
  this.logClientOfflineWarningIfNecessary(`Backend didn't respond within ${ONLINE_STATE_TIMEOUT_MS / 1000} ` +
21332
21884
  `seconds.`);
21333
- this.setAndBroadcast("Offline" /* Offline */);
21885
+ this.setAndBroadcast("Offline" /* OnlineState.Offline */);
21334
21886
  // NOTE: handleWatchStreamFailure() will continue to increment
21335
21887
  // watchStreamFailures even though we are already marked Offline,
21336
21888
  // but this is non-harmful.
@@ -21345,8 +21897,8 @@ class OnlineStateTracker {
21345
21897
  * actually transition to the 'Offline' state.
21346
21898
  */
21347
21899
  handleWatchStreamFailure(error) {
21348
- if (this.state === "Online" /* Online */) {
21349
- this.setAndBroadcast("Unknown" /* Unknown */);
21900
+ if (this.state === "Online" /* OnlineState.Online */) {
21901
+ this.setAndBroadcast("Unknown" /* OnlineState.Unknown */);
21350
21902
  }
21351
21903
  else {
21352
21904
  this.watchStreamFailures++;
@@ -21354,7 +21906,7 @@ class OnlineStateTracker {
21354
21906
  this.clearOnlineStateTimer();
21355
21907
  this.logClientOfflineWarningIfNecessary(`Connection failed ${MAX_WATCH_STREAM_FAILURES} ` +
21356
21908
  `times. Most recent error: ${error.toString()}`);
21357
- this.setAndBroadcast("Offline" /* Offline */);
21909
+ this.setAndBroadcast("Offline" /* OnlineState.Offline */);
21358
21910
  }
21359
21911
  }
21360
21912
  }
@@ -21368,7 +21920,7 @@ class OnlineStateTracker {
21368
21920
  set(newState) {
21369
21921
  this.clearOnlineStateTimer();
21370
21922
  this.watchStreamFailures = 0;
21371
- if (newState === "Online" /* Online */) {
21923
+ if (newState === "Online" /* OnlineState.Online */) {
21372
21924
  // We've connected to watch at least once. Don't warn the developer
21373
21925
  // about being offline going forward.
21374
21926
  this.shouldWarnClientIsOffline = false;
@@ -21495,7 +22047,7 @@ function newRemoteStore(localStore, datastore, asyncQueue, onlineStateHandler, c
21495
22047
  /** Re-enables the network. Idempotent. */
21496
22048
  function remoteStoreEnableNetwork(remoteStore) {
21497
22049
  const remoteStoreImpl = debugCast(remoteStore);
21498
- remoteStoreImpl.offlineCauses.delete(0 /* UserDisabled */);
22050
+ remoteStoreImpl.offlineCauses.delete(0 /* OfflineCause.UserDisabled */);
21499
22051
  return enableNetworkInternal(remoteStoreImpl);
21500
22052
  }
21501
22053
  async function enableNetworkInternal(remoteStoreImpl) {
@@ -21511,10 +22063,10 @@ async function enableNetworkInternal(remoteStoreImpl) {
21511
22063
  */
21512
22064
  async function remoteStoreDisableNetwork(remoteStore) {
21513
22065
  const remoteStoreImpl = debugCast(remoteStore);
21514
- remoteStoreImpl.offlineCauses.add(0 /* UserDisabled */);
22066
+ remoteStoreImpl.offlineCauses.add(0 /* OfflineCause.UserDisabled */);
21515
22067
  await disableNetworkInternal(remoteStoreImpl);
21516
22068
  // Set the OnlineState to Offline so get()s return from cache, etc.
21517
- remoteStoreImpl.onlineStateTracker.set("Offline" /* Offline */);
22069
+ remoteStoreImpl.onlineStateTracker.set("Offline" /* OnlineState.Offline */);
21518
22070
  }
21519
22071
  async function disableNetworkInternal(remoteStoreImpl) {
21520
22072
  for (const networkStatusHandler of remoteStoreImpl.onNetworkStatusChange) {
@@ -21524,12 +22076,12 @@ async function disableNetworkInternal(remoteStoreImpl) {
21524
22076
  async function remoteStoreShutdown(remoteStore) {
21525
22077
  const remoteStoreImpl = debugCast(remoteStore);
21526
22078
  logDebug(LOG_TAG$5, 'RemoteStore shutting down.');
21527
- remoteStoreImpl.offlineCauses.add(5 /* Shutdown */);
22079
+ remoteStoreImpl.offlineCauses.add(5 /* OfflineCause.Shutdown */);
21528
22080
  await disableNetworkInternal(remoteStoreImpl);
21529
22081
  remoteStoreImpl.connectivityMonitor.shutdown();
21530
22082
  // Set the OnlineState to Unknown (rather than Offline) to avoid potentially
21531
22083
  // triggering spurious listener events with cached data, etc.
21532
- remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
22084
+ remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
21533
22085
  }
21534
22086
  /**
21535
22087
  * Starts new listen for the given target. Uses resume token if provided. It
@@ -21569,7 +22121,7 @@ function remoteStoreUnlisten(remoteStore, targetId) {
21569
22121
  // Revert to OnlineState.Unknown if the watch stream is not open and we
21570
22122
  // have no listeners, since without any listens to send we cannot
21571
22123
  // confirm if the stream is healthy and upgrade to OnlineState.Online.
21572
- remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
22124
+ remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
21573
22125
  }
21574
22126
  }
21575
22127
  }
@@ -21630,14 +22182,14 @@ async function onWatchStreamClose(remoteStoreImpl, error) {
21630
22182
  // No need to restart watch stream because there are no active targets.
21631
22183
  // The online state is set to unknown because there is no active attempt
21632
22184
  // at establishing a connection
21633
- remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
22185
+ remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
21634
22186
  }
21635
22187
  }
21636
22188
  async function onWatchStreamChange(remoteStoreImpl, watchChange, snapshotVersion) {
21637
22189
  // Mark the client as online since we got a message from the server
21638
- remoteStoreImpl.onlineStateTracker.set("Online" /* Online */);
22190
+ remoteStoreImpl.onlineStateTracker.set("Online" /* OnlineState.Online */);
21639
22191
  if (watchChange instanceof WatchTargetChange &&
21640
- watchChange.state === 2 /* Removed */ &&
22192
+ watchChange.state === 2 /* WatchTargetChangeState.Removed */ &&
21641
22193
  watchChange.cause) {
21642
22194
  // There was an error on a target, don't wait for a consistent snapshot
21643
22195
  // to raise events
@@ -21685,10 +22237,10 @@ async function onWatchStreamChange(remoteStoreImpl, watchChange, snapshotVersion
21685
22237
  */
21686
22238
  async function disableNetworkUntilRecovery(remoteStoreImpl, e, op) {
21687
22239
  if (isIndexedDbTransactionError(e)) {
21688
- remoteStoreImpl.offlineCauses.add(1 /* IndexedDbFailed */);
22240
+ remoteStoreImpl.offlineCauses.add(1 /* OfflineCause.IndexedDbFailed */);
21689
22241
  // Disable network and raise offline snapshots
21690
22242
  await disableNetworkInternal(remoteStoreImpl);
21691
- remoteStoreImpl.onlineStateTracker.set("Offline" /* Offline */);
22243
+ remoteStoreImpl.onlineStateTracker.set("Offline" /* OnlineState.Offline */);
21692
22244
  if (!op) {
21693
22245
  // Use a simple read operation to determine if IndexedDB recovered.
21694
22246
  // Ideally, we would expose a health check directly on SimpleDb, but
@@ -21699,7 +22251,7 @@ async function disableNetworkUntilRecovery(remoteStoreImpl, e, op) {
21699
22251
  remoteStoreImpl.asyncQueue.enqueueRetryable(async () => {
21700
22252
  logDebug(LOG_TAG$5, 'Retrying IndexedDB access');
21701
22253
  await op();
21702
- remoteStoreImpl.offlineCauses.delete(1 /* IndexedDbFailed */);
22254
+ remoteStoreImpl.offlineCauses.delete(1 /* OfflineCause.IndexedDbFailed */);
21703
22255
  await enableNetworkInternal(remoteStoreImpl);
21704
22256
  });
21705
22257
  }
@@ -21750,7 +22302,7 @@ function raiseWatchSnapshot(remoteStoreImpl, snapshotVersion) {
21750
22302
  // mismatch, but don't actually retain that in listenTargets. This ensures
21751
22303
  // that we flag the first re-listen this way without impacting future
21752
22304
  // listens of this target (that might happen e.g. on reconnect).
21753
- const requestTargetData = new TargetData(targetData.target, targetId, 1 /* ExistenceFilterMismatch */, targetData.sequenceNumber);
22305
+ const requestTargetData = new TargetData(targetData.target, targetId, 1 /* TargetPurpose.ExistenceFilterMismatch */, targetData.sequenceNumber);
21754
22306
  sendWatchRequest(remoteStoreImpl, requestTargetData);
21755
22307
  });
21756
22308
  return remoteStoreImpl.remoteSyncer.applyRemoteEvent(remoteEvent);
@@ -21881,10 +22433,10 @@ async function handleWriteError(remoteStoreImpl, error) {
21881
22433
  }
21882
22434
  async function restartNetwork(remoteStore) {
21883
22435
  const remoteStoreImpl = debugCast(remoteStore);
21884
- remoteStoreImpl.offlineCauses.add(4 /* ConnectivityChange */);
22436
+ remoteStoreImpl.offlineCauses.add(4 /* OfflineCause.ConnectivityChange */);
21885
22437
  await disableNetworkInternal(remoteStoreImpl);
21886
- remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
21887
- remoteStoreImpl.offlineCauses.delete(4 /* ConnectivityChange */);
22438
+ remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
22439
+ remoteStoreImpl.offlineCauses.delete(4 /* OfflineCause.ConnectivityChange */);
21888
22440
  await enableNetworkInternal(remoteStoreImpl);
21889
22441
  }
21890
22442
  async function remoteStoreHandleCredentialChange(remoteStore, user) {
@@ -21895,14 +22447,14 @@ async function remoteStoreHandleCredentialChange(remoteStore, user) {
21895
22447
  // Tear down and re-create our network streams. This will ensure we get a
21896
22448
  // fresh auth token for the new user and re-fill the write pipeline with
21897
22449
  // new mutations from the LocalStore (since mutations are per-user).
21898
- remoteStoreImpl.offlineCauses.add(3 /* CredentialChange */);
22450
+ remoteStoreImpl.offlineCauses.add(3 /* OfflineCause.CredentialChange */);
21899
22451
  await disableNetworkInternal(remoteStoreImpl);
21900
22452
  if (usesNetwork) {
21901
22453
  // Don't set the network status to Unknown if we are offline.
21902
- remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
22454
+ remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
21903
22455
  }
21904
22456
  await remoteStoreImpl.remoteSyncer.handleCredentialChange(user);
21905
- remoteStoreImpl.offlineCauses.delete(3 /* CredentialChange */);
22457
+ remoteStoreImpl.offlineCauses.delete(3 /* OfflineCause.CredentialChange */);
21906
22458
  await enableNetworkInternal(remoteStoreImpl);
21907
22459
  }
21908
22460
  /**
@@ -21911,13 +22463,13 @@ async function remoteStoreHandleCredentialChange(remoteStore, user) {
21911
22463
  async function remoteStoreApplyPrimaryState(remoteStore, isPrimary) {
21912
22464
  const remoteStoreImpl = debugCast(remoteStore);
21913
22465
  if (isPrimary) {
21914
- remoteStoreImpl.offlineCauses.delete(2 /* IsSecondary */);
22466
+ remoteStoreImpl.offlineCauses.delete(2 /* OfflineCause.IsSecondary */);
21915
22467
  await enableNetworkInternal(remoteStoreImpl);
21916
22468
  }
21917
22469
  else if (!isPrimary) {
21918
- remoteStoreImpl.offlineCauses.add(2 /* IsSecondary */);
22470
+ remoteStoreImpl.offlineCauses.add(2 /* OfflineCause.IsSecondary */);
21919
22471
  await disableNetworkInternal(remoteStoreImpl);
21920
- remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
22472
+ remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
21921
22473
  }
21922
22474
  }
21923
22475
  /**
@@ -21943,7 +22495,7 @@ function ensureWatchStream(remoteStoreImpl) {
21943
22495
  startWatchStream(remoteStoreImpl);
21944
22496
  }
21945
22497
  else {
21946
- remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
22498
+ remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
21947
22499
  }
21948
22500
  }
21949
22501
  else {
@@ -22274,46 +22826,46 @@ class DocumentChangeSet {
22274
22826
  return;
22275
22827
  }
22276
22828
  // Merge the new change with the existing change.
22277
- if (change.type !== 0 /* Added */ &&
22278
- oldChange.type === 3 /* Metadata */) {
22829
+ if (change.type !== 0 /* ChangeType.Added */ &&
22830
+ oldChange.type === 3 /* ChangeType.Metadata */) {
22279
22831
  this.changeMap = this.changeMap.insert(key, change);
22280
22832
  }
22281
- else if (change.type === 3 /* Metadata */ &&
22282
- oldChange.type !== 1 /* Removed */) {
22833
+ else if (change.type === 3 /* ChangeType.Metadata */ &&
22834
+ oldChange.type !== 1 /* ChangeType.Removed */) {
22283
22835
  this.changeMap = this.changeMap.insert(key, {
22284
22836
  type: oldChange.type,
22285
22837
  doc: change.doc
22286
22838
  });
22287
22839
  }
22288
- else if (change.type === 2 /* Modified */ &&
22289
- oldChange.type === 2 /* Modified */) {
22840
+ else if (change.type === 2 /* ChangeType.Modified */ &&
22841
+ oldChange.type === 2 /* ChangeType.Modified */) {
22290
22842
  this.changeMap = this.changeMap.insert(key, {
22291
- type: 2 /* Modified */,
22843
+ type: 2 /* ChangeType.Modified */,
22292
22844
  doc: change.doc
22293
22845
  });
22294
22846
  }
22295
- else if (change.type === 2 /* Modified */ &&
22296
- oldChange.type === 0 /* Added */) {
22847
+ else if (change.type === 2 /* ChangeType.Modified */ &&
22848
+ oldChange.type === 0 /* ChangeType.Added */) {
22297
22849
  this.changeMap = this.changeMap.insert(key, {
22298
- type: 0 /* Added */,
22850
+ type: 0 /* ChangeType.Added */,
22299
22851
  doc: change.doc
22300
22852
  });
22301
22853
  }
22302
- else if (change.type === 1 /* Removed */ &&
22303
- oldChange.type === 0 /* Added */) {
22854
+ else if (change.type === 1 /* ChangeType.Removed */ &&
22855
+ oldChange.type === 0 /* ChangeType.Added */) {
22304
22856
  this.changeMap = this.changeMap.remove(key);
22305
22857
  }
22306
- else if (change.type === 1 /* Removed */ &&
22307
- oldChange.type === 2 /* Modified */) {
22858
+ else if (change.type === 1 /* ChangeType.Removed */ &&
22859
+ oldChange.type === 2 /* ChangeType.Modified */) {
22308
22860
  this.changeMap = this.changeMap.insert(key, {
22309
- type: 1 /* Removed */,
22861
+ type: 1 /* ChangeType.Removed */,
22310
22862
  doc: oldChange.doc
22311
22863
  });
22312
22864
  }
22313
- else if (change.type === 0 /* Added */ &&
22314
- oldChange.type === 1 /* Removed */) {
22865
+ else if (change.type === 0 /* ChangeType.Added */ &&
22866
+ oldChange.type === 1 /* ChangeType.Removed */) {
22315
22867
  this.changeMap = this.changeMap.insert(key, {
22316
- type: 2 /* Modified */,
22868
+ type: 2 /* ChangeType.Modified */,
22317
22869
  doc: change.doc
22318
22870
  });
22319
22871
  }
@@ -22352,7 +22904,7 @@ class ViewSnapshot {
22352
22904
  static fromInitialDocuments(query, documents, mutatedKeys, fromCache, hasCachedResults) {
22353
22905
  const changes = [];
22354
22906
  documents.forEach(doc => {
22355
- changes.push({ type: 0 /* Added */, doc });
22907
+ changes.push({ type: 0 /* ChangeType.Added */, doc });
22356
22908
  });
22357
22909
  return new ViewSnapshot(query, documents, DocumentSet.emptySet(documents), changes, mutatedKeys, fromCache,
22358
22910
  /* syncStateChanged= */ true,
@@ -22418,7 +22970,7 @@ function newEventManager() {
22418
22970
  class EventManagerImpl {
22419
22971
  constructor() {
22420
22972
  this.queries = new ObjectMap(q => canonifyQuery(q), queryEquals);
22421
- this.onlineState = "Unknown" /* Unknown */;
22973
+ this.onlineState = "Unknown" /* OnlineState.Unknown */;
22422
22974
  this.snapshotsInSyncListeners = new Set();
22423
22975
  }
22424
22976
  }
@@ -22549,7 +23101,7 @@ class QueryListener {
22549
23101
  */
22550
23102
  this.raisedInitialEvent = false;
22551
23103
  this.snap = null;
22552
- this.onlineState = "Unknown" /* Unknown */;
23104
+ this.onlineState = "Unknown" /* OnlineState.Unknown */;
22553
23105
  this.options = options || {};
22554
23106
  }
22555
23107
  /**
@@ -22563,7 +23115,7 @@ class QueryListener {
22563
23115
  // Remove the metadata only changes.
22564
23116
  const docChanges = [];
22565
23117
  for (const docChange of snap.docChanges) {
22566
- if (docChange.type !== 3 /* Metadata */) {
23118
+ if (docChange.type !== 3 /* ChangeType.Metadata */) {
22567
23119
  docChanges.push(docChange);
22568
23120
  }
22569
23121
  }
@@ -22606,7 +23158,7 @@ class QueryListener {
22606
23158
  }
22607
23159
  // NOTE: We consider OnlineState.Unknown as online (it should become Offline
22608
23160
  // or Online if we wait long enough).
22609
- const maybeOnline = onlineState !== "Offline" /* Offline */;
23161
+ const maybeOnline = onlineState !== "Offline" /* OnlineState.Offline */;
22610
23162
  // Don't raise the event if we're online, aren't synced yet (checked
22611
23163
  // above) and are waiting for a sync.
22612
23164
  if (this.options.waitForSyncWhenOnline && maybeOnline) {
@@ -22616,7 +23168,7 @@ class QueryListener {
22616
23168
  // or we are offline.
22617
23169
  return (!snap.docs.isEmpty() ||
22618
23170
  snap.hasCachedResults ||
22619
- onlineState === "Offline" /* Offline */);
23171
+ onlineState === "Offline" /* OnlineState.Offline */);
22620
23172
  }
22621
23173
  shouldRaiseEvent(snap) {
22622
23174
  // We don't need to handle includeDocumentMetadataChanges here because
@@ -22675,10 +23227,10 @@ class LocalViewChanges {
22675
23227
  let removedKeys = documentKeySet();
22676
23228
  for (const docChange of viewSnapshot.docChanges) {
22677
23229
  switch (docChange.type) {
22678
- case 0 /* Added */:
23230
+ case 0 /* ChangeType.Added */:
22679
23231
  addedKeys = addedKeys.add(docChange.doc.key);
22680
23232
  break;
22681
- case 1 /* Removed */:
23233
+ case 1 /* ChangeType.Removed */:
22682
23234
  removedKeys = removedKeys.add(docChange.doc.key);
22683
23235
  break;
22684
23236
  // do nothing
@@ -22926,11 +23478,11 @@ class View {
22926
23478
  //
22927
23479
  // Note that this should never get used in a refill (when previousChanges is
22928
23480
  // set), because there will only be adds -- no deletes or updates.
22929
- const lastDocInLimit = this.query.limitType === "F" /* First */ &&
23481
+ const lastDocInLimit = this.query.limitType === "F" /* LimitType.First */ &&
22930
23482
  oldDocumentSet.size === this.query.limit
22931
23483
  ? oldDocumentSet.last()
22932
23484
  : null;
22933
- const firstDocInLimit = this.query.limitType === "L" /* Last */ &&
23485
+ const firstDocInLimit = this.query.limitType === "L" /* LimitType.Last */ &&
22934
23486
  oldDocumentSet.size === this.query.limit
22935
23487
  ? oldDocumentSet.first()
22936
23488
  : null;
@@ -22953,7 +23505,7 @@ class View {
22953
23505
  if (!docsEqual) {
22954
23506
  if (!this.shouldWaitForSyncedDocument(oldDoc, newDoc)) {
22955
23507
  changeSet.track({
22956
- type: 2 /* Modified */,
23508
+ type: 2 /* ChangeType.Modified */,
22957
23509
  doc: newDoc
22958
23510
  });
22959
23511
  changeApplied = true;
@@ -22969,16 +23521,16 @@ class View {
22969
23521
  }
22970
23522
  }
22971
23523
  else if (oldDocHadPendingMutations !== newDocHasPendingMutations) {
22972
- changeSet.track({ type: 3 /* Metadata */, doc: newDoc });
23524
+ changeSet.track({ type: 3 /* ChangeType.Metadata */, doc: newDoc });
22973
23525
  changeApplied = true;
22974
23526
  }
22975
23527
  }
22976
23528
  else if (!oldDoc && newDoc) {
22977
- changeSet.track({ type: 0 /* Added */, doc: newDoc });
23529
+ changeSet.track({ type: 0 /* ChangeType.Added */, doc: newDoc });
22978
23530
  changeApplied = true;
22979
23531
  }
22980
23532
  else if (oldDoc && !newDoc) {
22981
- changeSet.track({ type: 1 /* Removed */, doc: oldDoc });
23533
+ changeSet.track({ type: 1 /* ChangeType.Removed */, doc: oldDoc });
22982
23534
  changeApplied = true;
22983
23535
  if (lastDocInLimit || firstDocInLimit) {
22984
23536
  // A doc was removed from a full limit query. We'll need to
@@ -23006,12 +23558,12 @@ class View {
23006
23558
  // Drop documents out to meet limit/limitToLast requirement.
23007
23559
  if (this.query.limit !== null) {
23008
23560
  while (newDocumentSet.size > this.query.limit) {
23009
- const oldDoc = this.query.limitType === "F" /* First */
23561
+ const oldDoc = this.query.limitType === "F" /* LimitType.First */
23010
23562
  ? newDocumentSet.last()
23011
23563
  : newDocumentSet.first();
23012
23564
  newDocumentSet = newDocumentSet.delete(oldDoc.key);
23013
23565
  newMutatedKeys = newMutatedKeys.delete(oldDoc.key);
23014
- changeSet.track({ type: 1 /* Removed */, doc: oldDoc });
23566
+ changeSet.track({ type: 1 /* ChangeType.Removed */, doc: oldDoc });
23015
23567
  }
23016
23568
  }
23017
23569
  return {
@@ -23059,7 +23611,7 @@ class View {
23059
23611
  ? this.updateLimboDocuments()
23060
23612
  : [];
23061
23613
  const synced = this.limboDocuments.size === 0 && this.current;
23062
- const newSyncState = synced ? 1 /* Synced */ : 0 /* Local */;
23614
+ const newSyncState = synced ? 1 /* SyncState.Synced */ : 0 /* SyncState.Local */;
23063
23615
  const syncStateChanged = newSyncState !== this.syncState;
23064
23616
  this.syncState = newSyncState;
23065
23617
  if (changes.length === 0 && !syncStateChanged) {
@@ -23067,7 +23619,7 @@ class View {
23067
23619
  return { limboChanges };
23068
23620
  }
23069
23621
  else {
23070
- const snap = new ViewSnapshot(this.query, docChanges.documentSet, oldDocs, changes, docChanges.mutatedKeys, newSyncState === 0 /* Local */, syncStateChanged,
23622
+ const snap = new ViewSnapshot(this.query, docChanges.documentSet, oldDocs, changes, docChanges.mutatedKeys, newSyncState === 0 /* SyncState.Local */, syncStateChanged,
23071
23623
  /* excludesMetadataChanges= */ false, targetChange
23072
23624
  ? targetChange.resumeToken.approximateByteSize() > 0
23073
23625
  : false);
@@ -23082,7 +23634,7 @@ class View {
23082
23634
  * ViewChange if the view's syncState changes as a result.
23083
23635
  */
23084
23636
  applyOnlineStateChange(onlineState) {
23085
- if (this.current && onlineState === "Offline" /* Offline */) {
23637
+ if (this.current && onlineState === "Offline" /* OnlineState.Offline */) {
23086
23638
  // If we're offline, set `current` to false and then call applyChanges()
23087
23639
  // to refresh our syncState and generate a ViewChange as appropriate. We
23088
23640
  // are guaranteed to get a new TargetChange that sets `current` back to
@@ -23197,22 +23749,22 @@ class View {
23197
23749
  */
23198
23750
  // PORTING NOTE: Multi-tab only.
23199
23751
  computeInitialSnapshot() {
23200
- return ViewSnapshot.fromInitialDocuments(this.query, this.documentSet, this.mutatedKeys, this.syncState === 0 /* Local */, this.hasCachedResults);
23752
+ return ViewSnapshot.fromInitialDocuments(this.query, this.documentSet, this.mutatedKeys, this.syncState === 0 /* SyncState.Local */, this.hasCachedResults);
23201
23753
  }
23202
23754
  }
23203
23755
  function compareChangeType(c1, c2) {
23204
23756
  const order = (change) => {
23205
23757
  switch (change) {
23206
- case 0 /* Added */:
23758
+ case 0 /* ChangeType.Added */:
23207
23759
  return 1;
23208
- case 2 /* Modified */:
23760
+ case 2 /* ChangeType.Modified */:
23209
23761
  return 2;
23210
- case 3 /* Metadata */:
23762
+ case 3 /* ChangeType.Metadata */:
23211
23763
  // A metadata change is converted to a modified change at the public
23212
23764
  // api layer. Since we sort by document key and then change type,
23213
23765
  // metadata and modified changes must be sorted equivalently.
23214
23766
  return 2;
23215
- case 1 /* Removed */:
23767
+ case 1 /* ChangeType.Removed */:
23216
23768
  return 0;
23217
23769
  default:
23218
23770
  return fail();
@@ -23330,7 +23882,7 @@ class SyncEngineImpl {
23330
23882
  /** Stores user callbacks waiting for all pending writes to be acknowledged. */
23331
23883
  this.pendingWritesCallbacks = new Map();
23332
23884
  this.limboTargetIdGenerator = TargetIdGenerator.forSyncEngine();
23333
- this.onlineState = "Unknown" /* Unknown */;
23885
+ this.onlineState = "Unknown" /* OnlineState.Unknown */;
23334
23886
  // The primary state is set to `true` or `false` immediately after Firestore
23335
23887
  // startup. In the interim, a client should only be considered primary if
23336
23888
  // `isPrimary` is true.
@@ -23394,7 +23946,7 @@ async function initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId,
23394
23946
  /* usePreviousResults= */ true);
23395
23947
  const view = new View(query, queryResult.remoteKeys);
23396
23948
  const viewDocChanges = view.computeDocChanges(queryResult.documents);
23397
- const synthesizedTargetChange = TargetChange.createSynthesizedTargetChangeForCurrentChange(targetId, current && syncEngineImpl.onlineState !== "Offline" /* Offline */, resumeToken);
23949
+ const synthesizedTargetChange = TargetChange.createSynthesizedTargetChangeForCurrentChange(targetId, current && syncEngineImpl.onlineState !== "Offline" /* OnlineState.Offline */, resumeToken);
23398
23950
  const viewChange = view.applyChanges(viewDocChanges,
23399
23951
  /* updateLimboDocuments= */ syncEngineImpl.isPrimaryClient, synthesizedTargetChange);
23400
23952
  updateTrackedLimbos(syncEngineImpl, targetId, viewChange.limboChanges);
@@ -23520,9 +24072,9 @@ function syncEngineApplyOnlineStateChange(syncEngine, onlineState, source) {
23520
24072
  // tab remains online) and only apply the primary tab's online state from
23521
24073
  // SharedClientState.
23522
24074
  if ((syncEngineImpl.isPrimaryClient &&
23523
- source === 0 /* RemoteStore */) ||
24075
+ source === 0 /* OnlineStateSource.RemoteStore */) ||
23524
24076
  (!syncEngineImpl.isPrimaryClient &&
23525
- source === 1 /* SharedClientState */)) {
24077
+ source === 1 /* OnlineStateSource.SharedClientState */)) {
23526
24078
  const newViewSnapshots = [];
23527
24079
  syncEngineImpl.queryViewsByQuery.forEach((query, queryView) => {
23528
24080
  const viewChange = queryView.view.applyOnlineStateChange(onlineState);
@@ -23790,7 +24342,7 @@ function pumpEnqueuedLimboResolutions(syncEngineImpl) {
23790
24342
  syncEngineImpl.activeLimboResolutionsByTarget.set(limboTargetId, new LimboResolution(key));
23791
24343
  syncEngineImpl.activeLimboTargetsByKey =
23792
24344
  syncEngineImpl.activeLimboTargetsByKey.insert(key, limboTargetId);
23793
- remoteStoreListen(syncEngineImpl.remoteStore, new TargetData(queryToTarget(newQueryForPath(key.path)), limboTargetId, 2 /* LimboResolution */, ListenSequence.INVALID));
24345
+ remoteStoreListen(syncEngineImpl.remoteStore, new TargetData(queryToTarget(newQueryForPath(key.path)), limboTargetId, 2 /* TargetPurpose.LimboResolution */, ListenSequence.INVALID));
23794
24346
  }
23795
24347
  }
23796
24348
  async function syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngine, changes, remoteEvent) {
@@ -24043,7 +24595,7 @@ async function synchronizeQueryViewsAndRaiseSnapshots(syncEngine, targets, trans
24043
24595
  */
24044
24596
  // PORTING NOTE: Multi-Tab only.
24045
24597
  function synthesizeTargetToQuery(target) {
24046
- return newQuery(target.path, target.collectionGroup, target.orderBy, target.filters, target.limit, "F" /* First */, target.startAt, target.endAt);
24598
+ return newQuery(target.path, target.collectionGroup, target.orderBy, target.filters, target.limit, "F" /* LimitType.First */, target.startAt, target.endAt);
24047
24599
  }
24048
24600
  /** Returns the IDs of the clients that are currently active. */
24049
24601
  // PORTING NOTE: Multi-Tab only.
@@ -24374,7 +24926,7 @@ class OnlineComponentProvider {
24374
24926
  this.eventManager = this.createEventManager(cfg);
24375
24927
  this.syncEngine = this.createSyncEngine(cfg,
24376
24928
  /* startAsPrimary=*/ !offlineComponentProvider.synchronizeTabs);
24377
- this.sharedClientState.onlineStateHandler = onlineState => syncEngineApplyOnlineStateChange(this.syncEngine, onlineState, 1 /* SharedClientState */);
24929
+ this.sharedClientState.onlineStateHandler = onlineState => syncEngineApplyOnlineStateChange(this.syncEngine, onlineState, 1 /* OnlineStateSource.SharedClientState */);
24378
24930
  this.remoteStore.remoteSyncer.handleCredentialChange =
24379
24931
  syncEngineHandleCredentialChange.bind(null, this.syncEngine);
24380
24932
  await remoteStoreApplyPrimaryState(this.remoteStore, this.syncEngine.isPrimaryClient);
@@ -24388,7 +24940,7 @@ class OnlineComponentProvider {
24388
24940
  return newDatastore(cfg.authCredentials, cfg.appCheckCredentials, connection, serializer);
24389
24941
  }
24390
24942
  createRemoteStore(cfg) {
24391
- return newRemoteStore(this.localStore, this.datastore, cfg.asyncQueue, onlineState => syncEngineApplyOnlineStateChange(this.syncEngine, onlineState, 0 /* RemoteStore */), newConnectivityMonitor());
24943
+ return newRemoteStore(this.localStore, this.datastore, cfg.asyncQueue, onlineState => syncEngineApplyOnlineStateChange(this.syncEngine, onlineState, 0 /* OnlineStateSource.RemoteStore */), newConnectivityMonitor());
24392
24944
  }
24393
24945
  createSyncEngine(cfg, startAsPrimary) {
24394
24946
  return newSyncEngine(this.localStore, this.remoteStore, this.eventManager, this.sharedClientState, cfg.initialUser, cfg.maxConcurrentLimboResolutions, startAsPrimary);
@@ -25145,7 +25697,7 @@ class TransactionRunner {
25145
25697
  this.updateFunction = updateFunction;
25146
25698
  this.deferred = deferred;
25147
25699
  this.attemptsRemaining = options.maxAttempts;
25148
- this.backoff = new ExponentialBackoff(this.asyncQueue, "transaction_retry" /* TransactionRetry */);
25700
+ this.backoff = new ExponentialBackoff(this.asyncQueue, "transaction_retry" /* TimerId.TransactionRetry */);
25149
25701
  }
25150
25702
  /** Runs the transaction and sets the result on deferred. */
25151
25703
  run() {
@@ -26178,7 +26730,7 @@ class AsyncQueueImpl {
26178
26730
  // List of TimerIds to fast-forward delays for.
26179
26731
  this.timerIdsToSkip = [];
26180
26732
  // Backoff timer used to schedule retries for retryable operations
26181
- this.backoff = new ExponentialBackoff(this, "async_queue_retry" /* AsyncQueueRetry */);
26733
+ this.backoff = new ExponentialBackoff(this, "async_queue_retry" /* TimerId.AsyncQueueRetry */);
26182
26734
  // Visibility handler that triggers an immediate retry of all retryable
26183
26735
  // operations. Meant to speed up recovery when we regain file system access
26184
26736
  // after page comes into foreground.
@@ -26348,7 +26900,7 @@ class AsyncQueueImpl {
26348
26900
  this.delayedOperations.sort((a, b) => a.targetTimeMs - b.targetTimeMs);
26349
26901
  for (const op of this.delayedOperations) {
26350
26902
  op.skipDelay();
26351
- if (lastTimerId !== "all" /* All */ && op.timerId === lastTimerId) {
26903
+ if (lastTimerId !== "all" /* TimerId.All */ && op.timerId === lastTimerId) {
26352
26904
  break;
26353
26905
  }
26354
26906
  }
@@ -27261,12 +27813,12 @@ class ParsedUpdateData {
27261
27813
  }
27262
27814
  function isWrite(dataSource) {
27263
27815
  switch (dataSource) {
27264
- case 0 /* Set */: // fall through
27265
- case 2 /* MergeSet */: // fall through
27266
- case 1 /* Update */:
27816
+ case 0 /* UserDataSource.Set */: // fall through
27817
+ case 2 /* UserDataSource.MergeSet */: // fall through
27818
+ case 1 /* UserDataSource.Update */:
27267
27819
  return true;
27268
- case 3 /* Argument */:
27269
- case 4 /* ArrayArgument */:
27820
+ case 3 /* UserDataSource.Argument */:
27821
+ case 4 /* UserDataSource.ArrayArgument */:
27270
27822
  return false;
27271
27823
  default:
27272
27824
  throw fail();
@@ -27391,8 +27943,8 @@ function newUserDataReader(firestore) {
27391
27943
  /** Parse document data from a set() call. */
27392
27944
  function parseSetData(userDataReader, methodName, targetDoc, input, hasConverter, options = {}) {
27393
27945
  const context = userDataReader.createContext(options.merge || options.mergeFields
27394
- ? 2 /* MergeSet */
27395
- : 0 /* Set */, methodName, targetDoc, hasConverter);
27946
+ ? 2 /* UserDataSource.MergeSet */
27947
+ : 0 /* UserDataSource.Set */, methodName, targetDoc, hasConverter);
27396
27948
  validatePlainObject('Data must be an object, but it was:', context, input);
27397
27949
  const updateData = parseObject(input, context);
27398
27950
  let fieldMask;
@@ -27423,12 +27975,12 @@ function parseSetData(userDataReader, methodName, targetDoc, input, hasConverter
27423
27975
  }
27424
27976
  class DeleteFieldValueImpl extends FieldValue {
27425
27977
  _toFieldTransform(context) {
27426
- if (context.dataSource === 2 /* MergeSet */) {
27978
+ if (context.dataSource === 2 /* UserDataSource.MergeSet */) {
27427
27979
  // No transform to add for a delete, but we need to add it to our
27428
27980
  // fieldMask so it gets deleted.
27429
27981
  context.fieldMask.push(context.path);
27430
27982
  }
27431
- else if (context.dataSource === 1 /* Update */) {
27983
+ else if (context.dataSource === 1 /* UserDataSource.Update */) {
27432
27984
  throw context.createError(`${this._methodName}() can only appear at the top level ` +
27433
27985
  'of your update data');
27434
27986
  }
@@ -27461,7 +28013,7 @@ class DeleteFieldValueImpl extends FieldValue {
27461
28013
  */
27462
28014
  function createSentinelChildContext(fieldValue, context, arrayElement) {
27463
28015
  return new ParseContextImpl({
27464
- dataSource: 3 /* Argument */,
28016
+ dataSource: 3 /* UserDataSource.Argument */,
27465
28017
  targetDoc: context.settings.targetDoc,
27466
28018
  methodName: fieldValue._methodName,
27467
28019
  arrayElement
@@ -27525,7 +28077,7 @@ class NumericIncrementFieldValueImpl extends FieldValue {
27525
28077
  }
27526
28078
  /** Parse update data from an update() call. */
27527
28079
  function parseUpdateData(userDataReader, methodName, targetDoc, input) {
27528
- const context = userDataReader.createContext(1 /* Update */, methodName, targetDoc);
28080
+ const context = userDataReader.createContext(1 /* UserDataSource.Update */, methodName, targetDoc);
27529
28081
  validatePlainObject('Data must be an object, but it was:', context, input);
27530
28082
  const fieldMaskPaths = [];
27531
28083
  const updateData = ObjectValue.empty();
@@ -27552,7 +28104,7 @@ function parseUpdateData(userDataReader, methodName, targetDoc, input) {
27552
28104
  }
27553
28105
  /** Parse update data from a list of field/value arguments. */
27554
28106
  function parseUpdateVarargs(userDataReader, methodName, targetDoc, field, value, moreFieldsAndValues) {
27555
- const context = userDataReader.createContext(1 /* Update */, methodName, targetDoc);
28107
+ const context = userDataReader.createContext(1 /* UserDataSource.Update */, methodName, targetDoc);
27556
28108
  const keys = [fieldPathFromArgument$1(methodName, field, targetDoc)];
27557
28109
  const values = [value];
27558
28110
  if (moreFieldsAndValues.length % 2 !== 0) {
@@ -27599,7 +28151,7 @@ function parseUpdateVarargs(userDataReader, methodName, targetDoc, field, value,
27599
28151
  * contain additional arrays (e.g. the operand of an `in` query).
27600
28152
  */
27601
28153
  function parseQueryValue(userDataReader, methodName, input, allowArrays = false) {
27602
- const context = userDataReader.createContext(allowArrays ? 4 /* ArrayArgument */ : 3 /* Argument */, methodName);
28154
+ const context = userDataReader.createContext(allowArrays ? 4 /* UserDataSource.ArrayArgument */ : 3 /* UserDataSource.Argument */, methodName);
27603
28155
  const parsed = parseData(input, context);
27604
28156
  return parsed;
27605
28157
  }
@@ -27649,7 +28201,7 @@ function parseData(input, context) {
27649
28201
  // contain additional arrays (each representing an individual field
27650
28202
  // value), so we disable this validation.
27651
28203
  if (context.settings.arrayElement &&
27652
- context.dataSource !== 4 /* ArrayArgument */) {
28204
+ context.dataSource !== 4 /* UserDataSource.ArrayArgument */) {
27653
28205
  throw context.createError('Nested arrays are not supported');
27654
28206
  }
27655
28207
  return parseArray(input, context);
@@ -28031,164 +28583,334 @@ function fieldPathFromArgument(methodName, arg) {
28031
28583
  * limitations under the License.
28032
28584
  */
28033
28585
  function validateHasExplicitOrderByForLimitToLast(query) {
28034
- if (query.limitType === "L" /* Last */ &&
28586
+ if (query.limitType === "L" /* LimitType.Last */ &&
28035
28587
  query.explicitOrderBy.length === 0) {
28036
28588
  throw new FirestoreError(Code.UNIMPLEMENTED, 'limitToLast() queries require specifying at least one orderBy() clause');
28037
28589
  }
28038
28590
  }
28591
+ /**
28592
+ * An `AppliableConstraint` is an abstraction of a constraint that can be applied
28593
+ * to a Firestore query.
28594
+ */
28595
+ class AppliableConstraint {
28596
+ }
28039
28597
  /**
28040
28598
  * A `QueryConstraint` is used to narrow the set of documents returned by a
28041
28599
  * 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
28600
+ * {@link orderBy}, {@link startAt}, {@link startAfter}, {@link
28601
+ * endBefore}, {@link endAt}, {@link limit}, {@link limitToLast} and
28044
28602
  * can then be passed to {@link query} to create a new query instance that
28045
28603
  * also contains this `QueryConstraint`.
28046
28604
  */
28047
- class QueryConstraint {
28605
+ class QueryConstraint extends AppliableConstraint {
28048
28606
  }
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) {
28607
+ function query(query, queryConstraint, ...additionalQueryConstraints) {
28608
+ let queryConstraints = [];
28609
+ if (queryConstraint instanceof AppliableConstraint) {
28610
+ queryConstraints.push(queryConstraint);
28611
+ }
28612
+ queryConstraints = queryConstraints.concat(additionalQueryConstraints);
28613
+ validateQueryConstraintArray(queryConstraints);
28059
28614
  for (const constraint of queryConstraints) {
28060
28615
  query = constraint._apply(query);
28061
28616
  }
28062
28617
  return query;
28063
28618
  }
28064
- class QueryFilterConstraint extends QueryConstraint {
28619
+ /**
28620
+ * A `QueryFieldFilterConstraint` is used to narrow the set of documents returned by
28621
+ * a Firestore query by filtering on one or more document fields.
28622
+ * `QueryFieldFilterConstraint`s are created by invoking {@link where} and can then
28623
+ * be passed to {@link query} to create a new query instance that also contains
28624
+ * this `QueryFieldFilterConstraint`.
28625
+ */
28626
+ class QueryFieldFilterConstraint extends QueryConstraint {
28627
+ /**
28628
+ * @internal
28629
+ */
28065
28630
  constructor(_field, _op, _value) {
28066
28631
  super();
28067
28632
  this._field = _field;
28068
28633
  this._op = _op;
28069
28634
  this._value = _value;
28635
+ /** The type of this query constraint */
28070
28636
  this.type = 'where';
28071
28637
  }
28638
+ static _create(_field, _op, _value) {
28639
+ return new QueryFieldFilterConstraint(_field, _op, _value);
28640
+ }
28072
28641
  _apply(query) {
28642
+ const filter = this._parse(query);
28643
+ validateNewFieldFilter(query._query, filter);
28644
+ return new Query(query.firestore, query.converter, queryWithAddedFilter(query._query, filter));
28645
+ }
28646
+ _parse(query) {
28073
28647
  const reader = newUserDataReader(query.firestore);
28074
28648
  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));
28649
+ return filter;
28076
28650
  }
28077
28651
  }
28078
28652
  /**
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.
28653
+ * Creates a {@link QueryFieldFilterConstraint} that enforces that documents
28654
+ * must contain the specified field and that the value should satisfy the
28655
+ * relation constraint provided.
28082
28656
  *
28083
28657
  * @param fieldPath - The path to compare
28084
28658
  * @param opStr - The operation string (e.g "&lt;", "&lt;=", "==", "&lt;",
28085
28659
  * "&lt;=", "!=").
28086
28660
  * @param value - The value for comparison
28087
- * @returns The created {@link Query}.
28661
+ * @returns The created {@link QueryFieldFilterConstraint}.
28088
28662
  */
28089
28663
  function where(fieldPath, opStr, value) {
28090
28664
  const op = opStr;
28091
28665
  const field = fieldPathFromArgument('where', fieldPath);
28092
- return new QueryFilterConstraint(field, op, value);
28666
+ return QueryFieldFilterConstraint._create(field, op, value);
28667
+ }
28668
+ /**
28669
+ * A `QueryCompositeFilterConstraint` is used to narrow the set of documents
28670
+ * returned by a Firestore query by performing the logical OR or AND of multiple
28671
+ * {@link QueryFieldFilterConstraint}s or {@link QueryCompositeFilterConstraint}s.
28672
+ * `QueryCompositeFilterConstraint`s are created by invoking {@link or} or
28673
+ * {@link and} and can then be passed to {@link query} to create a new query
28674
+ * instance that also contains the `QueryCompositeFilterConstraint`.
28675
+ * @internal TODO remove this internal tag with OR Query support in the server
28676
+ */
28677
+ class QueryCompositeFilterConstraint extends AppliableConstraint {
28678
+ /**
28679
+ * @internal
28680
+ */
28681
+ constructor(
28682
+ /** The type of this query constraint */
28683
+ type, _queryConstraints) {
28684
+ super();
28685
+ this.type = type;
28686
+ this._queryConstraints = _queryConstraints;
28687
+ }
28688
+ static _create(type, _queryConstraints) {
28689
+ return new QueryCompositeFilterConstraint(type, _queryConstraints);
28690
+ }
28691
+ _parse(query) {
28692
+ const parsedFilters = this._queryConstraints
28693
+ .map(queryConstraint => {
28694
+ return queryConstraint._parse(query);
28695
+ })
28696
+ .filter(parsedFilter => parsedFilter.getFilters().length > 0);
28697
+ if (parsedFilters.length === 1) {
28698
+ return parsedFilters[0];
28699
+ }
28700
+ return CompositeFilter.create(parsedFilters, this._getOperator());
28701
+ }
28702
+ _apply(query) {
28703
+ const parsedFilter = this._parse(query);
28704
+ if (parsedFilter.getFilters().length === 0) {
28705
+ // Return the existing query if not adding any more filters (e.g. an empty
28706
+ // composite filter).
28707
+ return query;
28708
+ }
28709
+ validateNewFilter(query._query, parsedFilter);
28710
+ return new Query(query.firestore, query.converter, queryWithAddedFilter(query._query, parsedFilter));
28711
+ }
28712
+ _getQueryConstraints() {
28713
+ return this._queryConstraints;
28714
+ }
28715
+ _getOperator() {
28716
+ return this.type === 'and' ? "and" /* CompositeOperator.AND */ : "or" /* CompositeOperator.OR */;
28717
+ }
28718
+ }
28719
+ /**
28720
+ * Creates a {@link QueryCompositeFilterConstraint} that performs a logical OR
28721
+ * of all the provided {@link QueryFilterConstraint}s.
28722
+ *
28723
+ * @param queryConstraints - Optional. The {@link QueryFilterConstraint}s
28724
+ * for OR operation. These must be created with calls to {@link where},
28725
+ * {@link or}, or {@link and}.
28726
+ * @returns The created {@link QueryCompositeFilterConstraint}.
28727
+ * @internal TODO remove this internal tag with OR Query support in the server
28728
+ */
28729
+ function or(...queryConstraints) {
28730
+ // Only support QueryFilterConstraints
28731
+ queryConstraints.forEach(queryConstraint => validateQueryFilterConstraint('or', queryConstraint));
28732
+ return QueryCompositeFilterConstraint._create("or" /* CompositeOperator.OR */, queryConstraints);
28733
+ }
28734
+ /**
28735
+ * Creates a {@link QueryCompositeFilterConstraint} that performs a logical AND
28736
+ * of all the provided {@link QueryFilterConstraint}s.
28737
+ *
28738
+ * @param queryConstraints - Optional. The {@link QueryFilterConstraint}s
28739
+ * for AND operation. These must be created with calls to {@link where},
28740
+ * {@link or}, or {@link and}.
28741
+ * @returns The created {@link QueryCompositeFilterConstraint}.
28742
+ * @internal TODO remove this internal tag with OR Query support in the server
28743
+ */
28744
+ function and(...queryConstraints) {
28745
+ // Only support QueryFilterConstraints
28746
+ queryConstraints.forEach(queryConstraint => validateQueryFilterConstraint('and', queryConstraint));
28747
+ return QueryCompositeFilterConstraint._create("and" /* CompositeOperator.AND */, queryConstraints);
28093
28748
  }
28749
+ /**
28750
+ * A `QueryOrderByConstraint` is used to sort the set of documents returned by a
28751
+ * Firestore query. `QueryOrderByConstraint`s are created by invoking
28752
+ * {@link orderBy} and can then be passed to {@link query} to create a new query
28753
+ * instance that also contains this `QueryOrderByConstraint`.
28754
+ *
28755
+ * Note: Documents that do not contain the orderBy field will not be present in
28756
+ * the query result.
28757
+ */
28094
28758
  class QueryOrderByConstraint extends QueryConstraint {
28759
+ /**
28760
+ * @internal
28761
+ */
28095
28762
  constructor(_field, _direction) {
28096
28763
  super();
28097
28764
  this._field = _field;
28098
28765
  this._direction = _direction;
28766
+ /** The type of this query constraint */
28099
28767
  this.type = 'orderBy';
28100
28768
  }
28769
+ static _create(_field, _direction) {
28770
+ return new QueryOrderByConstraint(_field, _direction);
28771
+ }
28101
28772
  _apply(query) {
28102
28773
  const orderBy = newQueryOrderBy(query._query, this._field, this._direction);
28103
28774
  return new Query(query.firestore, query.converter, queryWithAddedOrderBy(query._query, orderBy));
28104
28775
  }
28105
28776
  }
28106
28777
  /**
28107
- * Creates a {@link QueryConstraint} that sorts the query result by the
28778
+ * Creates a {@link QueryOrderByConstraint} that sorts the query result by the
28108
28779
  * specified field, optionally in descending order instead of ascending.
28109
28780
  *
28781
+ * Note: Documents that do not contain the specified field will not be present
28782
+ * in the query result.
28783
+ *
28110
28784
  * @param fieldPath - The field to sort by.
28111
28785
  * @param directionStr - Optional direction to sort by ('asc' or 'desc'). If
28112
28786
  * not specified, order will be ascending.
28113
- * @returns The created {@link Query}.
28787
+ * @returns The created {@link QueryOrderByConstraint}.
28114
28788
  */
28115
28789
  function orderBy(fieldPath, directionStr = 'asc') {
28116
28790
  const direction = directionStr;
28117
28791
  const path = fieldPathFromArgument('orderBy', fieldPath);
28118
- return new QueryOrderByConstraint(path, direction);
28792
+ return QueryOrderByConstraint._create(path, direction);
28119
28793
  }
28794
+ /**
28795
+ * A `QueryLimitConstraint` is used to limit the number of documents returned by
28796
+ * a Firestore query.
28797
+ * `QueryLimitConstraint`s are created by invoking {@link limit} or
28798
+ * {@link limitToLast} and can then be passed to {@link query} to create a new
28799
+ * query instance that also contains this `QueryLimitConstraint`.
28800
+ */
28120
28801
  class QueryLimitConstraint extends QueryConstraint {
28121
- constructor(type, _limit, _limitType) {
28802
+ /**
28803
+ * @internal
28804
+ */
28805
+ constructor(
28806
+ /** The type of this query constraint */
28807
+ type, _limit, _limitType) {
28122
28808
  super();
28123
28809
  this.type = type;
28124
28810
  this._limit = _limit;
28125
28811
  this._limitType = _limitType;
28126
28812
  }
28813
+ static _create(type, _limit, _limitType) {
28814
+ return new QueryLimitConstraint(type, _limit, _limitType);
28815
+ }
28127
28816
  _apply(query) {
28128
28817
  return new Query(query.firestore, query.converter, queryWithLimit(query._query, this._limit, this._limitType));
28129
28818
  }
28130
28819
  }
28131
28820
  /**
28132
- * Creates a {@link QueryConstraint} that only returns the first matching documents.
28821
+ * Creates a {@link QueryLimitConstraint} that only returns the first matching
28822
+ * documents.
28133
28823
  *
28134
28824
  * @param limit - The maximum number of items to return.
28135
- * @returns The created {@link Query}.
28825
+ * @returns The created {@link QueryLimitConstraint}.
28136
28826
  */
28137
28827
  function limit(limit) {
28138
28828
  validatePositiveNumber('limit', limit);
28139
- return new QueryLimitConstraint('limit', limit, "F" /* First */);
28829
+ return QueryLimitConstraint._create('limit', limit, "F" /* LimitType.First */);
28140
28830
  }
28141
28831
  /**
28142
- * Creates a {@link QueryConstraint} that only returns the last matching documents.
28832
+ * Creates a {@link QueryLimitConstraint} that only returns the last matching
28833
+ * documents.
28143
28834
  *
28144
28835
  * You must specify at least one `orderBy` clause for `limitToLast` queries,
28145
28836
  * otherwise an exception will be thrown during execution.
28146
28837
  *
28147
28838
  * @param limit - The maximum number of items to return.
28148
- * @returns The created {@link Query}.
28839
+ * @returns The created {@link QueryLimitConstraint}.
28149
28840
  */
28150
28841
  function limitToLast(limit) {
28151
28842
  validatePositiveNumber('limitToLast', limit);
28152
- return new QueryLimitConstraint('limitToLast', limit, "L" /* Last */);
28843
+ return QueryLimitConstraint._create('limitToLast', limit, "L" /* LimitType.Last */);
28153
28844
  }
28845
+ /**
28846
+ * A `QueryStartAtConstraint` is used to exclude documents from the start of a
28847
+ * result set returned by a Firestore query.
28848
+ * `QueryStartAtConstraint`s are created by invoking {@link (startAt:1)} or
28849
+ * {@link (startAfter:1)} and can then be passed to {@link query} to create a
28850
+ * new query instance that also contains this `QueryStartAtConstraint`.
28851
+ */
28154
28852
  class QueryStartAtConstraint extends QueryConstraint {
28155
- constructor(type, _docOrFields, _inclusive) {
28853
+ /**
28854
+ * @internal
28855
+ */
28856
+ constructor(
28857
+ /** The type of this query constraint */
28858
+ type, _docOrFields, _inclusive) {
28156
28859
  super();
28157
28860
  this.type = type;
28158
28861
  this._docOrFields = _docOrFields;
28159
28862
  this._inclusive = _inclusive;
28160
28863
  }
28864
+ static _create(type, _docOrFields, _inclusive) {
28865
+ return new QueryStartAtConstraint(type, _docOrFields, _inclusive);
28866
+ }
28161
28867
  _apply(query) {
28162
28868
  const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
28163
28869
  return new Query(query.firestore, query.converter, queryWithStartAt(query._query, bound));
28164
28870
  }
28165
28871
  }
28166
28872
  function startAt(...docOrFields) {
28167
- return new QueryStartAtConstraint('startAt', docOrFields,
28873
+ return QueryStartAtConstraint._create('startAt', docOrFields,
28168
28874
  /*inclusive=*/ true);
28169
28875
  }
28170
28876
  function startAfter(...docOrFields) {
28171
- return new QueryStartAtConstraint('startAfter', docOrFields,
28877
+ return QueryStartAtConstraint._create('startAfter', docOrFields,
28172
28878
  /*inclusive=*/ false);
28173
28879
  }
28880
+ /**
28881
+ * A `QueryEndAtConstraint` is used to exclude documents from the end of a
28882
+ * result set returned by a Firestore query.
28883
+ * `QueryEndAtConstraint`s are created by invoking {@link (endAt:1)} or
28884
+ * {@link (endBefore:1)} and can then be passed to {@link query} to create a new
28885
+ * query instance that also contains this `QueryEndAtConstraint`.
28886
+ */
28174
28887
  class QueryEndAtConstraint extends QueryConstraint {
28175
- constructor(type, _docOrFields, _inclusive) {
28888
+ /**
28889
+ * @internal
28890
+ */
28891
+ constructor(
28892
+ /** The type of this query constraint */
28893
+ type, _docOrFields, _inclusive) {
28176
28894
  super();
28177
28895
  this.type = type;
28178
28896
  this._docOrFields = _docOrFields;
28179
28897
  this._inclusive = _inclusive;
28180
28898
  }
28899
+ static _create(type, _docOrFields, _inclusive) {
28900
+ return new QueryEndAtConstraint(type, _docOrFields, _inclusive);
28901
+ }
28181
28902
  _apply(query) {
28182
28903
  const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
28183
28904
  return new Query(query.firestore, query.converter, queryWithEndAt(query._query, bound));
28184
28905
  }
28185
28906
  }
28186
28907
  function endBefore(...docOrFields) {
28187
- return new QueryEndAtConstraint('endBefore', docOrFields,
28908
+ return QueryEndAtConstraint._create('endBefore', docOrFields,
28188
28909
  /*inclusive=*/ false);
28189
28910
  }
28190
28911
  function endAt(...docOrFields) {
28191
- return new QueryEndAtConstraint('endAt', docOrFields, /*inclusive=*/ true);
28912
+ return QueryEndAtConstraint._create('endAt', docOrFields,
28913
+ /*inclusive=*/ true);
28192
28914
  }
28193
28915
  /** Helper function to create a bound from a document or fields */
28194
28916
  function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive) {
@@ -28204,10 +28926,10 @@ function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive)
28204
28926
  function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op, value) {
28205
28927
  let fieldValue;
28206
28928
  if (fieldPath.isKeyField()) {
28207
- if (op === "array-contains" /* ARRAY_CONTAINS */ || op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
28929
+ if (op === "array-contains" /* Operator.ARRAY_CONTAINS */ || op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
28208
28930
  throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid Query. You can't perform '${op}' queries on documentId().`);
28209
28931
  }
28210
- else if (op === "in" /* IN */ || op === "not-in" /* NOT_IN */) {
28932
+ else if (op === "in" /* Operator.IN */ || op === "not-in" /* Operator.NOT_IN */) {
28211
28933
  validateDisjunctiveFilterElements(value, op);
28212
28934
  const referenceList = [];
28213
28935
  for (const arrayValue of value) {
@@ -28220,16 +28942,15 @@ function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op
28220
28942
  }
28221
28943
  }
28222
28944
  else {
28223
- if (op === "in" /* IN */ ||
28224
- op === "not-in" /* NOT_IN */ ||
28225
- op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
28945
+ if (op === "in" /* Operator.IN */ ||
28946
+ op === "not-in" /* Operator.NOT_IN */ ||
28947
+ op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
28226
28948
  validateDisjunctiveFilterElements(value, op);
28227
28949
  }
28228
28950
  fieldValue = parseQueryValue(dataReader, methodName, value,
28229
- /* allowArrays= */ op === "in" /* IN */ || op === "not-in" /* NOT_IN */);
28951
+ /* allowArrays= */ op === "in" /* Operator.IN */ || op === "not-in" /* Operator.NOT_IN */);
28230
28952
  }
28231
28953
  const filter = FieldFilter.create(fieldPath, op, fieldValue);
28232
- validateNewFilter(query, filter);
28233
28954
  return filter;
28234
28955
  }
28235
28956
  function newQueryOrderBy(query, fieldPath, direction) {
@@ -28399,62 +29120,84 @@ function validateDisjunctiveFilterElements(value, operator) {
28399
29120
  */
28400
29121
  function conflictingOps(op) {
28401
29122
  switch (op) {
28402
- case "!=" /* NOT_EQUAL */:
28403
- return ["!=" /* NOT_EQUAL */, "not-in" /* NOT_IN */];
28404
- case "array-contains" /* ARRAY_CONTAINS */:
29123
+ case "!=" /* Operator.NOT_EQUAL */:
29124
+ return ["!=" /* Operator.NOT_EQUAL */, "not-in" /* Operator.NOT_IN */];
29125
+ case "array-contains" /* Operator.ARRAY_CONTAINS */:
28405
29126
  return [
28406
- "array-contains" /* ARRAY_CONTAINS */,
28407
- "array-contains-any" /* ARRAY_CONTAINS_ANY */,
28408
- "not-in" /* NOT_IN */
29127
+ "array-contains" /* Operator.ARRAY_CONTAINS */,
29128
+ "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
29129
+ "not-in" /* Operator.NOT_IN */
28409
29130
  ];
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 */:
29131
+ case "in" /* Operator.IN */:
29132
+ return ["array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */, "in" /* Operator.IN */, "not-in" /* Operator.NOT_IN */];
29133
+ case "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */:
28413
29134
  return [
28414
- "array-contains" /* ARRAY_CONTAINS */,
28415
- "array-contains-any" /* ARRAY_CONTAINS_ANY */,
28416
- "in" /* IN */,
28417
- "not-in" /* NOT_IN */
29135
+ "array-contains" /* Operator.ARRAY_CONTAINS */,
29136
+ "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
29137
+ "in" /* Operator.IN */,
29138
+ "not-in" /* Operator.NOT_IN */
28418
29139
  ];
28419
- case "not-in" /* NOT_IN */:
29140
+ case "not-in" /* Operator.NOT_IN */:
28420
29141
  return [
28421
- "array-contains" /* ARRAY_CONTAINS */,
28422
- "array-contains-any" /* ARRAY_CONTAINS_ANY */,
28423
- "in" /* IN */,
28424
- "not-in" /* NOT_IN */,
28425
- "!=" /* NOT_EQUAL */
29142
+ "array-contains" /* Operator.ARRAY_CONTAINS */,
29143
+ "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
29144
+ "in" /* Operator.IN */,
29145
+ "not-in" /* Operator.NOT_IN */,
29146
+ "!=" /* Operator.NOT_EQUAL */
28426
29147
  ];
28427
29148
  default:
28428
29149
  return [];
28429
29150
  }
28430
29151
  }
28431
- function validateNewFilter(query, filter) {
28432
- if (filter.isInequality()) {
28433
- const existingField = getInequalityFilterField(query);
28434
- if (existingField !== null && !existingField.isEqual(filter.field)) {
29152
+ function validateNewFieldFilter(query, fieldFilter) {
29153
+ if (fieldFilter.isInequality()) {
29154
+ const existingInequality = getInequalityFilterField(query);
29155
+ const newInequality = fieldFilter.field;
29156
+ if (existingInequality !== null &&
29157
+ !existingInequality.isEqual(newInequality)) {
28435
29158
  throw new FirestoreError(Code.INVALID_ARGUMENT, 'Invalid query. All where filters with an inequality' +
28436
29159
  ' (<, <=, !=, not-in, >, or >=) must be on the same field. But you have' +
28437
- ` inequality filters on '${existingField.toString()}'` +
28438
- ` and '${filter.field.toString()}'`);
29160
+ ` inequality filters on '${existingInequality.toString()}'` +
29161
+ ` and '${newInequality.toString()}'`);
28439
29162
  }
28440
29163
  const firstOrderByField = getFirstOrderByField(query);
28441
29164
  if (firstOrderByField !== null) {
28442
- validateOrderByAndInequalityMatch(query, filter.field, firstOrderByField);
29165
+ validateOrderByAndInequalityMatch(query, newInequality, firstOrderByField);
28443
29166
  }
28444
29167
  }
28445
- const conflictingOp = findFilterOperator(query, conflictingOps(filter.op));
29168
+ const conflictingOp = findOpInsideFilters(query.filters, conflictingOps(fieldFilter.op));
28446
29169
  if (conflictingOp !== null) {
28447
29170
  // Special case when it's a duplicate op to give a slightly clearer error message.
28448
- if (conflictingOp === filter.op) {
29171
+ if (conflictingOp === fieldFilter.op) {
28449
29172
  throw new FirestoreError(Code.INVALID_ARGUMENT, 'Invalid query. You cannot use more than one ' +
28450
- `'${filter.op.toString()}' filter.`);
29173
+ `'${fieldFilter.op.toString()}' filter.`);
28451
29174
  }
28452
29175
  else {
28453
- throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. You cannot use '${filter.op.toString()}' filters ` +
29176
+ throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. You cannot use '${fieldFilter.op.toString()}' filters ` +
28454
29177
  `with '${conflictingOp.toString()}' filters.`);
28455
29178
  }
28456
29179
  }
28457
29180
  }
29181
+ function validateNewFilter(query, filter) {
29182
+ let testQuery = query;
29183
+ const subFilters = filter.getFlattenedFilters();
29184
+ for (const subFilter of subFilters) {
29185
+ validateNewFieldFilter(testQuery, subFilter);
29186
+ testQuery = queryWithAddedFilter(testQuery, subFilter);
29187
+ }
29188
+ }
29189
+ // Checks if any of the provided filter operators are included in the given list of filters and
29190
+ // returns the first one that is, or null if none are.
29191
+ function findOpInsideFilters(filters, operators) {
29192
+ for (const filter of filters) {
29193
+ for (const fieldFilter of filter.getFlattenedFilters()) {
29194
+ if (operators.indexOf(fieldFilter.op) >= 0) {
29195
+ return fieldFilter.op;
29196
+ }
29197
+ }
29198
+ }
29199
+ return null;
29200
+ }
28458
29201
  function validateNewOrderBy(query, orderBy) {
28459
29202
  if (getFirstOrderByField(query) === null) {
28460
29203
  // This is the first order by. It must match any inequality.
@@ -28472,6 +29215,24 @@ function validateOrderByAndInequalityMatch(baseQuery, inequality, orderBy) {
28472
29215
  `as your first argument to orderBy(), but your first orderBy() ` +
28473
29216
  `is on field '${orderBy.toString()}' instead.`);
28474
29217
  }
29218
+ }
29219
+ function validateQueryFilterConstraint(functionName, queryConstraint) {
29220
+ if (!(queryConstraint instanceof QueryFieldFilterConstraint) &&
29221
+ !(queryConstraint instanceof QueryCompositeFilterConstraint)) {
29222
+ throw new FirestoreError(Code.INVALID_ARGUMENT, `Function ${functionName}() requires AppliableConstraints created with a call to 'where(...)', 'or(...)', or 'and(...)'.`);
29223
+ }
29224
+ }
29225
+ function validateQueryConstraintArray(queryConstraint) {
29226
+ const compositeFilterCount = queryConstraint.filter(filter => filter instanceof QueryCompositeFilterConstraint).length;
29227
+ const fieldFilterCount = queryConstraint.filter(filter => filter instanceof QueryFieldFilterConstraint).length;
29228
+ if (compositeFilterCount > 1 ||
29229
+ (compositeFilterCount > 0 && fieldFilterCount > 0)) {
29230
+ throw new FirestoreError(Code.INVALID_ARGUMENT, 'InvalidQuery. When using composite filters, you cannot use ' +
29231
+ 'more than one filter at the top level. Consider nesting the multiple ' +
29232
+ 'filters within an `and(...)` statement. For example: ' +
29233
+ 'change `query(query, where(...), or(...))` to ' +
29234
+ '`query(query, and(where(...), or(...)))`.');
29235
+ }
28475
29236
  }
28476
29237
 
28477
29238
  /**
@@ -28499,27 +29260,27 @@ function validateOrderByAndInequalityMatch(baseQuery, inequality, orderBy) {
28499
29260
  class AbstractUserDataWriter {
28500
29261
  convertValue(value, serverTimestampBehavior = 'none') {
28501
29262
  switch (typeOrder(value)) {
28502
- case 0 /* NullValue */:
29263
+ case 0 /* TypeOrder.NullValue */:
28503
29264
  return null;
28504
- case 1 /* BooleanValue */:
29265
+ case 1 /* TypeOrder.BooleanValue */:
28505
29266
  return value.booleanValue;
28506
- case 2 /* NumberValue */:
29267
+ case 2 /* TypeOrder.NumberValue */:
28507
29268
  return normalizeNumber(value.integerValue || value.doubleValue);
28508
- case 3 /* TimestampValue */:
29269
+ case 3 /* TypeOrder.TimestampValue */:
28509
29270
  return this.convertTimestamp(value.timestampValue);
28510
- case 4 /* ServerTimestampValue */:
29271
+ case 4 /* TypeOrder.ServerTimestampValue */:
28511
29272
  return this.convertServerTimestamp(value, serverTimestampBehavior);
28512
- case 5 /* StringValue */:
29273
+ case 5 /* TypeOrder.StringValue */:
28513
29274
  return value.stringValue;
28514
- case 6 /* BlobValue */:
29275
+ case 6 /* TypeOrder.BlobValue */:
28515
29276
  return this.convertBytes(normalizeByteString(value.bytesValue));
28516
- case 7 /* RefValue */:
29277
+ case 7 /* TypeOrder.RefValue */:
28517
29278
  return this.convertReference(value.referenceValue);
28518
- case 8 /* GeoPointValue */:
29279
+ case 8 /* TypeOrder.GeoPointValue */:
28519
29280
  return this.convertGeoPoint(value.geoPointValue);
28520
- case 9 /* ArrayValue */:
29281
+ case 9 /* TypeOrder.ArrayValue */:
28521
29282
  return this.convertArray(value.arrayValue, serverTimestampBehavior);
28522
- case 10 /* ObjectValue */:
29283
+ case 10 /* TypeOrder.ObjectValue */:
28523
29284
  return this.convertObject(value.mapValue, serverTimestampBehavior);
28524
29285
  default:
28525
29286
  throw fail();
@@ -28861,16 +29622,16 @@ function changesFromSnapshot(querySnapshot, includeMetadataChanges) {
28861
29622
  // to lookup the index of a document.
28862
29623
  let indexTracker = querySnapshot._snapshot.oldDocs;
28863
29624
  return querySnapshot._snapshot.docChanges
28864
- .filter(change => includeMetadataChanges || change.type !== 3 /* Metadata */)
29625
+ .filter(change => includeMetadataChanges || change.type !== 3 /* ChangeType.Metadata */)
28865
29626
  .map(change => {
28866
29627
  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
29628
  let oldIndex = -1;
28868
29629
  let newIndex = -1;
28869
- if (change.type !== 0 /* Added */) {
29630
+ if (change.type !== 0 /* ChangeType.Added */) {
28870
29631
  oldIndex = indexTracker.indexOf(change.doc.key);
28871
29632
  indexTracker = indexTracker.delete(change.doc.key);
28872
29633
  }
28873
- if (change.type !== 1 /* Removed */) {
29634
+ if (change.type !== 1 /* ChangeType.Removed */) {
28874
29635
  indexTracker = indexTracker.add(change.doc);
28875
29636
  newIndex = indexTracker.indexOf(change.doc.key);
28876
29637
  }
@@ -28885,12 +29646,12 @@ function changesFromSnapshot(querySnapshot, includeMetadataChanges) {
28885
29646
  }
28886
29647
  function resultChangeType(type) {
28887
29648
  switch (type) {
28888
- case 0 /* Added */:
29649
+ case 0 /* ChangeType.Added */:
28889
29650
  return 'added';
28890
- case 2 /* Modified */:
28891
- case 3 /* Metadata */:
29651
+ case 2 /* ChangeType.Modified */:
29652
+ case 3 /* ChangeType.Metadata */:
28892
29653
  return 'modified';
28893
- case 1 /* Removed */:
29654
+ case 1 /* ChangeType.Removed */:
28894
29655
  return 'removed';
28895
29656
  default:
28896
29657
  return fail();
@@ -29724,13 +30485,13 @@ function parseIndexes(jsonOrConfiguration) {
29724
30485
  const fieldPathString = tryGetString(field, 'fieldPath');
29725
30486
  const fieldPath = fieldPathFromDotSeparatedString('setIndexConfiguration', fieldPathString);
29726
30487
  if (field.arrayConfig === 'CONTAINS') {
29727
- segments.push(new IndexSegment(fieldPath, 2 /* CONTAINS */));
30488
+ segments.push(new IndexSegment(fieldPath, 2 /* IndexKind.CONTAINS */));
29728
30489
  }
29729
30490
  else if (field.order === 'ASCENDING') {
29730
- segments.push(new IndexSegment(fieldPath, 0 /* ASCENDING */));
30491
+ segments.push(new IndexSegment(fieldPath, 0 /* IndexKind.ASCENDING */));
29731
30492
  }
29732
30493
  else if (field.order === 'DESCENDING') {
29733
- segments.push(new IndexSegment(fieldPath, 1 /* DESCENDING */));
30494
+ segments.push(new IndexSegment(fieldPath, 1 /* IndexKind.DESCENDING */));
29734
30495
  }
29735
30496
  }
29736
30497
  }
@@ -29740,12 +30501,11 @@ function parseIndexes(jsonOrConfiguration) {
29740
30501
  return parsedIndexes;
29741
30502
  }
29742
30503
  function tryParseJson(json) {
29743
- var _a;
29744
30504
  try {
29745
30505
  return JSON.parse(json);
29746
30506
  }
29747
30507
  catch (e) {
29748
- throw new FirestoreError(Code.INVALID_ARGUMENT, 'Failed to parse JSON: ' + ((_a = e) === null || _a === void 0 ? void 0 : _a.message));
30508
+ throw new FirestoreError(Code.INVALID_ARGUMENT, 'Failed to parse JSON: ' + (e === null || e === void 0 ? void 0 : e.message));
29749
30509
  }
29750
30510
  }
29751
30511
  function tryGetString(data, property) {
@@ -29773,5 +30533,5 @@ function tryGetString(data, property) {
29773
30533
  */
29774
30534
  registerFirestore('node');
29775
30535
 
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 };
30536
+ 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
30537
  //# sourceMappingURL=index.node.mjs.map