@apollo/federation-internals 2.4.7 → 2.4.8

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.
@@ -527,6 +527,18 @@ class Operation {
527
527
  this.fragments = fragments;
528
528
  this.name = name;
529
529
  }
530
+ withUpdatedSelectionSet(newSelectionSet) {
531
+ if (this.selectionSet === newSelectionSet) {
532
+ return this;
533
+ }
534
+ return new Operation(this.schema, this.rootKind, newSelectionSet, this.variableDefinitions, this.fragments, this.name);
535
+ }
536
+ withUpdatedSelectionSetAndFragments(newSelectionSet, newFragments) {
537
+ if (this.selectionSet === newSelectionSet && newFragments === this.fragments) {
538
+ return this;
539
+ }
540
+ return new Operation(this.schema, this.rootKind, newSelectionSet, this.variableDefinitions, newFragments, this.name);
541
+ }
530
542
  optimize(fragments, minUsagesToOptimize = 2) {
531
543
  (0, utils_1.assert)(minUsagesToOptimize >= 1, `Expected 'minUsagesToOptimize' to be at least 1, but got ${minUsagesToOptimize}`);
532
544
  if (!fragments || fragments.isEmpty()) {
@@ -536,41 +548,34 @@ class Operation {
536
548
  if (optimizedSelection === this.selectionSet) {
537
549
  return this;
538
550
  }
539
- const finalFragments = computeFragmentsToKeep(optimizedSelection, fragments, minUsagesToOptimize);
540
- if (finalFragments === null || (finalFragments === null || finalFragments === void 0 ? void 0 : finalFragments.size) === fragments.size) {
541
- return new Operation(this.schema, this.rootKind, optimizedSelection, this.variableDefinitions, finalFragments !== null && finalFragments !== void 0 ? finalFragments : undefined, this.name);
551
+ let finalFragments = computeFragmentsToKeep(optimizedSelection, fragments, minUsagesToOptimize);
552
+ if (finalFragments !== null && (finalFragments === null || finalFragments === void 0 ? void 0 : finalFragments.size) !== fragments.size) {
553
+ optimizedSelection = optimizedSelection.expandFragments(finalFragments);
554
+ optimizedSelection = optimizedSelection.normalize({ parentType: optimizedSelection.parentType });
555
+ if (finalFragments) {
556
+ const usages = new Map();
557
+ optimizedSelection.collectUsedFragmentNames(usages);
558
+ finalFragments = finalFragments.filter((f) => { var _a; return ((_a = usages.get(f.name)) !== null && _a !== void 0 ? _a : 0) > 0; });
559
+ }
542
560
  }
543
- optimizedSelection = optimizedSelection.expandFragments(finalFragments);
544
- optimizedSelection = optimizedSelection.trimUnsatisfiableBranches(optimizedSelection.parentType);
545
- return new Operation(this.schema, this.rootKind, optimizedSelection, this.variableDefinitions, finalFragments, this.name);
561
+ return this.withUpdatedSelectionSetAndFragments(optimizedSelection, finalFragments !== null && finalFragments !== void 0 ? finalFragments : undefined);
546
562
  }
547
563
  expandAllFragments() {
548
- const expandedSelections = this.selectionSet.expandFragments();
549
- if (expandedSelections === this.selectionSet) {
550
- return this;
551
- }
552
- return new Operation(this.schema, this.rootKind, expandedSelections, this.variableDefinitions, undefined, this.name);
564
+ const expanded = this.selectionSet.expandFragments();
565
+ return this.withUpdatedSelectionSetAndFragments(expanded.normalize({ parentType: expanded.parentType }), undefined);
553
566
  }
554
- trimUnsatisfiableBranches() {
555
- const trimmedSelections = this.selectionSet.trimUnsatisfiableBranches(this.selectionSet.parentType);
556
- if (trimmedSelections === this.selectionSet) {
557
- return this;
558
- }
559
- return new Operation(this.schema, this.rootKind, trimmedSelections, this.variableDefinitions, this.fragments, this.name);
567
+ normalize() {
568
+ return this.withUpdatedSelectionSet(this.selectionSet.normalize({ parentType: this.selectionSet.parentType }));
560
569
  }
561
570
  withoutDefer(labelsToRemove) {
562
- const updated = this.selectionSet.withoutDefer(labelsToRemove);
563
- return updated == this.selectionSet
564
- ? this
565
- : new Operation(this.schema, this.rootKind, updated, this.variableDefinitions, this.fragments, this.name);
571
+ return this.withUpdatedSelectionSet(this.selectionSet.withoutDefer(labelsToRemove));
566
572
  }
567
573
  withNormalizedDefer() {
568
574
  const normalizer = new DeferNormalizer();
569
575
  const { hasDefers, hasNonLabelledOrConditionalDefers } = normalizer.init(this.selectionSet);
570
576
  let updatedOperation = this;
571
577
  if (hasNonLabelledOrConditionalDefers) {
572
- const updated = this.selectionSet.withNormalizedDefer(normalizer);
573
- updatedOperation = new Operation(this.schema, this.rootKind, updated, this.variableDefinitions, this.fragments, this.name);
578
+ updatedOperation = this.withUpdatedSelectionSet(this.selectionSet.withNormalizedDefer(normalizer));
574
579
  }
575
580
  return {
576
581
  operation: updatedOperation,
@@ -612,7 +617,7 @@ class NamedFragmentDefinition extends definitions_1.DirectiveTargetElement {
612
617
  }
613
618
  expandedSelectionSet() {
614
619
  if (!this._expandedSelectionSet) {
615
- this._expandedSelectionSet = this.selectionSet.expandFragments().trimUnsatisfiableBranches(this.typeCondition);
620
+ this._expandedSelectionSet = this.selectionSet.expandFragments().normalize({ parentType: this.typeCondition });
616
621
  }
617
622
  return this._expandedSelectionSet;
618
623
  }
@@ -650,7 +655,7 @@ class NamedFragmentDefinition extends definitions_1.DirectiveTargetElement {
650
655
  selectionSet: this.selectionSet.toSelectionSetNode()
651
656
  };
652
657
  }
653
- canApplyAtType(type) {
658
+ canApplyDirectlyAtType(type) {
654
659
  if ((0, types_1.sameType)(type, this.typeCondition)) {
655
660
  return true;
656
661
  }
@@ -659,23 +664,29 @@ class NamedFragmentDefinition extends definitions_1.DirectiveTargetElement {
659
664
  }
660
665
  const conditionRuntimes = (0, definitions_1.possibleRuntimeTypes)(this.typeCondition);
661
666
  const typeRuntimes = (0, definitions_1.possibleRuntimeTypes)(type);
662
- return conditionRuntimes.length >= typeRuntimes.length
663
- && typeRuntimes.every((t1) => conditionRuntimes.some((t2) => (0, types_1.sameType)(t1, t2)));
667
+ if (conditionRuntimes.length < typeRuntimes.length
668
+ || !typeRuntimes.every((t1) => conditionRuntimes.some((t2) => (0, types_1.sameType)(t1, t2)))) {
669
+ return false;
670
+ }
671
+ return (0, definitions_1.isObjectType)(type) || (0, definitions_1.isUnionType)(this.typeCondition);
664
672
  }
665
673
  expandedSelectionSetAtType(type) {
666
- const expandedSelectionSet = this.expandedSelectionSet();
667
674
  if ((0, types_1.sameType)(type, this.typeCondition) || (0, definitions_1.isObjectType)(this.typeCondition)) {
668
- return expandedSelectionSet;
669
- }
670
- if (!(0, definitions_1.isObjectType)(type)) {
671
- return expandedSelectionSet;
675
+ return { selectionSet: this.expandedSelectionSet() };
672
676
  }
673
- let selectionAtType = this.expandedSelectionSetsAtTypesCache.get(type.name);
674
- if (!selectionAtType) {
675
- selectionAtType = expandedSelectionSet.trimUnsatisfiableBranches(type, { recursive: false });
676
- this.expandedSelectionSetsAtTypesCache.set(type.name, selectionAtType);
677
+ let cached = this.expandedSelectionSetsAtTypesCache.get(type.name);
678
+ if (!cached) {
679
+ cached = this.computeExpandedSelectionSetAtType(type);
680
+ this.expandedSelectionSetsAtTypesCache.set(type.name, cached);
677
681
  }
678
- return selectionAtType;
682
+ return cached;
683
+ }
684
+ computeExpandedSelectionSetAtType(type) {
685
+ const expandedSelectionSet = this.expandedSelectionSet();
686
+ const selectionSet = expandedSelectionSet.normalize({ parentType: type, recursive: false });
687
+ const trimmed = expandedSelectionSet.minus(selectionSet);
688
+ const validator = trimmed.isEmpty() ? undefined : FieldsConflictValidator.build(trimmed);
689
+ return { selectionSet, validator };
679
690
  }
680
691
  includes(otherFragment) {
681
692
  if (this.name === otherFragment) {
@@ -724,8 +735,8 @@ class NamedFragments {
724
735
  this.fragments.set(fragment.name, fragment);
725
736
  }
726
737
  }
727
- maybeApplyingAtType(type) {
728
- return this.fragments.values().filter(f => f.canApplyAtType(type));
738
+ maybeApplyingDirectlyAtType(type) {
739
+ return this.fragments.values().filter(f => f.canApplyDirectlyAtType(type));
729
740
  }
730
741
  get(name) {
731
742
  return this.fragments.get(name);
@@ -772,7 +783,7 @@ class NamedFragments {
772
783
  }
773
784
  mapToExpandedSelectionSets(mapper) {
774
785
  return this.mapInDependencyOrder((fragment, newFragments) => {
775
- const mappedSelectionSet = mapper(fragment.selectionSet.expandFragments().trimUnsatisfiableBranches(fragment.typeCondition));
786
+ const mappedSelectionSet = mapper(fragment.selectionSet.expandFragments().normalize({ parentType: fragment.typeCondition }));
776
787
  if (!mappedSelectionSet) {
777
788
  return undefined;
778
789
  }
@@ -872,7 +883,7 @@ var ContainsResult;
872
883
  ContainsResult[ContainsResult["NOT_CONTAINED"] = 0] = "NOT_CONTAINED";
873
884
  ContainsResult[ContainsResult["STRICTLY_CONTAINED"] = 1] = "STRICTLY_CONTAINED";
874
885
  ContainsResult[ContainsResult["EQUAL"] = 2] = "EQUAL";
875
- })(ContainsResult = exports.ContainsResult || (exports.ContainsResult = {}));
886
+ })(ContainsResult || (exports.ContainsResult = ContainsResult = {}));
876
887
  class SelectionSet {
877
888
  constructor(parentType, keyedSelections = new Map()) {
878
889
  this.parentType = parentType;
@@ -909,6 +920,21 @@ class SelectionSet {
909
920
  }
910
921
  return fields;
911
922
  }
923
+ fieldsByResponseName() {
924
+ const byResponseName = new utils_1.MultiMap();
925
+ this.collectFieldsByResponseName(byResponseName);
926
+ return byResponseName;
927
+ }
928
+ collectFieldsByResponseName(collector) {
929
+ for (const selection of this.selections()) {
930
+ if (selection.kind === 'FieldSelection') {
931
+ collector.add(selection.element.responseName(), selection);
932
+ }
933
+ else {
934
+ selection.selectionSet.collectFieldsByResponseName(collector);
935
+ }
936
+ }
937
+ }
912
938
  usedVariables() {
913
939
  const collector = new definitions_1.VariableCollector();
914
940
  this.collectVariables(collector);
@@ -929,19 +955,20 @@ class SelectionSet {
929
955
  return this;
930
956
  }
931
957
  const wrapped = new InlineFragmentSelection(new FragmentElement(this.parentType, this.parentType), this);
932
- const optimized = wrapped.optimize(fragments);
958
+ const validator = FieldsConflictValidator.build(this);
959
+ const optimized = wrapped.optimize(fragments, validator);
933
960
  return optimized instanceof FragmentSpreadSelection
934
961
  ? selectionSetOf(this.parentType, optimized)
935
962
  : optimized.selectionSet;
936
963
  }
937
- optimizeSelections(fragments) {
938
- return this.lazyMap((selection) => selection.optimize(fragments));
964
+ optimizeSelections(fragments, validator) {
965
+ return this.lazyMap((selection) => selection.optimize(fragments, validator));
939
966
  }
940
967
  expandFragments(updatedFragments) {
941
968
  return this.lazyMap((selection) => selection.expandFragments(updatedFragments));
942
969
  }
943
- trimUnsatisfiableBranches(parentType, options) {
944
- return this.lazyMap((selection) => selection.trimUnsatisfiableBranches(parentType, options), { parentType });
970
+ normalize({ parentType, recursive }) {
971
+ return this.lazyMap((selection) => selection.normalize({ parentType, recursive }), { parentType });
945
972
  }
946
973
  lazyMap(mapper, options) {
947
974
  var _a;
@@ -975,10 +1002,13 @@ class SelectionSet {
975
1002
  return this.selections().some((s) => s.hasDefer());
976
1003
  }
977
1004
  filter(predicate) {
978
- return this.lazyMap((selection) => selection.filter(predicate));
1005
+ return this.lazyMap((selection) => predicate(selection) ? selection : undefined);
1006
+ }
1007
+ filterRecursiveDepthFirst(predicate) {
1008
+ return this.lazyMap((selection) => selection.filterRecursiveDepthFirst(predicate));
979
1009
  }
980
1010
  withoutEmptyBranches() {
981
- const updated = this.filter((selection) => { var _a; return ((_a = selection.selectionSet) === null || _a === void 0 ? void 0 : _a.isEmpty()) !== true; });
1011
+ const updated = this.filterRecursiveDepthFirst((selection) => { var _a; return ((_a = selection.selectionSet) === null || _a === void 0 ? void 0 : _a.isEmpty()) !== true; });
982
1012
  return updated.isEmpty() ? undefined : updated;
983
1013
  }
984
1014
  rebaseOn(parentType, fragments) {
@@ -1023,21 +1053,6 @@ class SelectionSet {
1023
1053
  ? ContainsResult.EQUAL
1024
1054
  : ContainsResult.STRICTLY_CONTAINED;
1025
1055
  }
1026
- diffWithNamedFragmentIfContained(candidate, parentType, fragments) {
1027
- const that = candidate.expandedSelectionSetAtType(parentType);
1028
- if (that.isEmpty() || (that.selections().length === 1 && that.selections()[0].isTypenameField())) {
1029
- return { contains: false };
1030
- }
1031
- if (this.contains(that)) {
1032
- let updatedThis = this.expandFragments(fragments.filter((f) => f.name !== candidate.name));
1033
- if (updatedThis !== this) {
1034
- updatedThis = updatedThis.trimUnsatisfiableBranches(parentType);
1035
- }
1036
- const diff = updatedThis.minus(that);
1037
- return { contains: true, diff: diff.isEmpty() ? undefined : diff };
1038
- }
1039
- return { contains: false };
1040
- }
1041
1056
  minus(that) {
1042
1057
  const updated = new SelectionSetUpdates();
1043
1058
  for (const [key, thisSelection] of this._keyedSelections) {
@@ -1054,6 +1069,25 @@ class SelectionSet {
1054
1069
  }
1055
1070
  return updated.toSelectionSet(this.parentType);
1056
1071
  }
1072
+ intersectionWith(that) {
1073
+ if (this.isEmpty()) {
1074
+ return this;
1075
+ }
1076
+ if (that.isEmpty()) {
1077
+ return that;
1078
+ }
1079
+ const intersection = new SelectionSetUpdates();
1080
+ for (const [key, thisSelection] of this._keyedSelections) {
1081
+ const thatSelection = that._keyedSelections.get(key);
1082
+ if (thatSelection) {
1083
+ const selection = thisSelection.intersectionWith(thatSelection);
1084
+ if (selection) {
1085
+ intersection.add(selection);
1086
+ }
1087
+ }
1088
+ }
1089
+ return intersection.toSelectionSet(this.parentType);
1090
+ }
1057
1091
  canRebaseOn(parentTypeToTest) {
1058
1092
  return this.selections().every((selection) => selection.canAddTo(parentTypeToTest));
1059
1093
  }
@@ -1196,6 +1230,16 @@ class SelectionSetUpdates {
1196
1230
  toSelectionSet(parentType, fragments) {
1197
1231
  return makeSelectionSet(parentType, this.keyedUpdates, fragments);
1198
1232
  }
1233
+ toString() {
1234
+ return '{\n'
1235
+ + [...this.keyedUpdates.entries()].map(([k, updates]) => {
1236
+ const updStr = updates.map((upd) => upd instanceof AbstractSelection
1237
+ ? upd.toString()
1238
+ : `${upd.path} -> ${upd.selections}`);
1239
+ return ` - ${k}: ${updStr}`;
1240
+ }).join('\n')
1241
+ + '\n\}';
1242
+ }
1199
1243
  }
1200
1244
  exports.SelectionSetUpdates = SelectionSetUpdates;
1201
1245
  function addToKeyedUpdates(keyedUpdates, selections) {
@@ -1435,40 +1479,187 @@ class AbstractSelection {
1435
1479
  }
1436
1480
  return undefined;
1437
1481
  }
1438
- tryOptimizeSubselectionWithFragments({ parentType, subSelection, fragments, canUseFullMatchingFragment, }) {
1439
- let candidates = fragments.maybeApplyingAtType(parentType);
1482
+ intersectionWith(that) {
1483
+ if (this.selectionSet && that.selectionSet) {
1484
+ const subSelectionSetIntersection = this.selectionSet.intersectionWith(that.selectionSet);
1485
+ if (subSelectionSetIntersection.isEmpty()) {
1486
+ return undefined;
1487
+ }
1488
+ else {
1489
+ return this.withUpdatedSelectionSet(subSelectionSetIntersection);
1490
+ }
1491
+ }
1492
+ else {
1493
+ return this.us();
1494
+ }
1495
+ }
1496
+ tryOptimizeSubselectionWithFragments({ parentType, subSelection, fragments, validator, canUseFullMatchingFragment, }) {
1497
+ let candidates = fragments.maybeApplyingDirectlyAtType(parentType);
1498
+ if (candidates.length === 0) {
1499
+ return subSelection;
1500
+ }
1440
1501
  const applyingFragments = [];
1441
1502
  for (const candidate of candidates) {
1442
- const fragmentSSet = candidate.expandedSelectionSetAtType(parentType);
1443
- if (fragmentSSet.isEmpty() || (fragmentSSet.selections().length === 1 && fragmentSSet.selections()[0].isTypenameField())) {
1503
+ const atType = candidate.expandedSelectionSetAtType(parentType);
1504
+ const selectionSetAtType = atType.selectionSet;
1505
+ if (selectionSetAtType.isEmpty() || (selectionSetAtType.selections().length === 1 && selectionSetAtType.selections()[0].isTypenameField())) {
1444
1506
  continue;
1445
1507
  }
1446
- const res = subSelection.contains(fragmentSSet);
1508
+ const res = subSelection.contains(selectionSetAtType);
1447
1509
  if (res === ContainsResult.EQUAL) {
1448
1510
  if (canUseFullMatchingFragment(candidate)) {
1511
+ if (!validator.checkCanReuseFragmentAndTrackIt(atType)) {
1512
+ continue;
1513
+ }
1449
1514
  return candidate;
1450
1515
  }
1451
1516
  if (candidate.appliedDirectives.length === 0) {
1452
- applyingFragments.push(candidate);
1517
+ applyingFragments.push({ fragment: candidate, atType });
1453
1518
  }
1454
1519
  }
1455
1520
  else if (res === ContainsResult.STRICTLY_CONTAINED && candidate.appliedDirectives.length === 0) {
1456
- applyingFragments.push(candidate);
1521
+ applyingFragments.push({ fragment: candidate, atType });
1457
1522
  }
1458
1523
  }
1459
1524
  if (applyingFragments.length === 0) {
1460
1525
  return subSelection;
1461
1526
  }
1462
- const filteredApplyingFragments = applyingFragments.filter((f) => !applyingFragments.some((o) => o.includes(f.name)));
1527
+ const filteredApplyingFragments = applyingFragments.filter(({ fragment }) => !applyingFragments.some((o) => o.fragment.includes(fragment.name)));
1463
1528
  let notCoveredByFragments = subSelection;
1464
1529
  const optimized = new SelectionSetUpdates();
1465
- for (const fragment of filteredApplyingFragments) {
1466
- notCoveredByFragments = notCoveredByFragments.minus(fragment.expandedSelectionSetAtType(parentType));
1530
+ for (const { fragment, atType } of filteredApplyingFragments) {
1531
+ if (!validator.checkCanReuseFragmentAndTrackIt(atType)) {
1532
+ continue;
1533
+ }
1534
+ const notCovered = subSelection.minus(atType.selectionSet);
1535
+ notCoveredByFragments = notCoveredByFragments.intersectionWith(notCovered);
1467
1536
  optimized.add(new FragmentSpreadSelection(parentType, fragments, fragment, []));
1468
1537
  }
1469
1538
  return optimized.add(notCoveredByFragments).toSelectionSet(parentType, fragments);
1470
1539
  }
1471
1540
  }
1541
+ class FieldsConflictValidator {
1542
+ constructor(byResponseName) {
1543
+ this.byResponseName = byResponseName;
1544
+ }
1545
+ static build(s) {
1546
+ return FieldsConflictValidator.forLevel(s.fieldsInSet());
1547
+ }
1548
+ static forLevel(level) {
1549
+ var _a;
1550
+ const atLevel = new Map();
1551
+ for (const { field } of level) {
1552
+ const responseName = field.element.responseName();
1553
+ let atResponseName = atLevel.get(responseName);
1554
+ if (!atResponseName) {
1555
+ atResponseName = new Map();
1556
+ atLevel.set(responseName, atResponseName);
1557
+ }
1558
+ if (field.selectionSet) {
1559
+ let forField = (_a = atResponseName.get(field.element)) !== null && _a !== void 0 ? _a : [];
1560
+ atResponseName.set(field.element, forField.concat(field.selectionSet.fieldsInSet()));
1561
+ }
1562
+ else {
1563
+ atResponseName.set(field.element, null);
1564
+ }
1565
+ }
1566
+ const byResponseName = new Map();
1567
+ for (const [name, level] of atLevel.entries()) {
1568
+ const atResponseName = new Map();
1569
+ for (const [field, collectedFields] of level) {
1570
+ const validator = collectedFields ? FieldsConflictValidator.forLevel(collectedFields) : null;
1571
+ atResponseName.set(field, validator);
1572
+ }
1573
+ byResponseName.set(name, atResponseName);
1574
+ }
1575
+ return new FieldsConflictValidator(byResponseName);
1576
+ }
1577
+ forField(field) {
1578
+ var _a;
1579
+ const validator = (_a = this.byResponseName.get(field.responseName())) === null || _a === void 0 ? void 0 : _a.get(field);
1580
+ (0, utils_1.assert)(validator, () => `Should have found validator for ${field}`);
1581
+ return validator;
1582
+ }
1583
+ checkCanReuseFragmentAndTrackIt(fragment) {
1584
+ const validator = fragment.validator;
1585
+ if (!validator) {
1586
+ return true;
1587
+ }
1588
+ if (!this.doMergeWith(validator)) {
1589
+ return false;
1590
+ }
1591
+ if (this.usedSpreadTrimmedPartAtLevel) {
1592
+ if (!this.usedSpreadTrimmedPartAtLevel.every((t) => validator.doMergeWith(t))) {
1593
+ return false;
1594
+ }
1595
+ }
1596
+ else {
1597
+ this.usedSpreadTrimmedPartAtLevel = [];
1598
+ }
1599
+ this.usedSpreadTrimmedPartAtLevel.push(validator);
1600
+ return true;
1601
+ }
1602
+ doMergeWith(that) {
1603
+ var _a, _b;
1604
+ for (const [responseName, thisFields] of this.byResponseName.entries()) {
1605
+ const thatFields = that.byResponseName.get(responseName);
1606
+ if (!thatFields) {
1607
+ continue;
1608
+ }
1609
+ for (const [thisField, thisValidator] of thisFields.entries()) {
1610
+ for (const [thatField, thatValidator] of thatFields.entries()) {
1611
+ if (!(0, types_1.typesCanBeMerged)(thisField.definition.type, thatField.definition.type)) {
1612
+ return false;
1613
+ }
1614
+ const p1 = thisField.parentType;
1615
+ const p2 = thatField.parentType;
1616
+ if ((0, types_1.sameType)(p1, p2) || !(0, definitions_1.isObjectType)(p1) || !(0, definitions_1.isObjectType)(p2)) {
1617
+ if (thisField.name !== thatField.name
1618
+ || !(0, values_1.argumentsEquals)((_a = thisField.args) !== null && _a !== void 0 ? _a : {}, (_b = thatField.args) !== null && _b !== void 0 ? _b : {})
1619
+ || (thisValidator && thatValidator && !thisValidator.doMergeWith(thatValidator))) {
1620
+ return false;
1621
+ }
1622
+ }
1623
+ else {
1624
+ if (thisValidator && thatValidator && !thisValidator.hasSameResponseShapeThan(thatValidator)) {
1625
+ return false;
1626
+ }
1627
+ }
1628
+ }
1629
+ }
1630
+ }
1631
+ return true;
1632
+ }
1633
+ hasSameResponseShapeThan(that) {
1634
+ for (const [responseName, thisFields] of this.byResponseName.entries()) {
1635
+ const thatFields = that.byResponseName.get(responseName);
1636
+ if (!thatFields) {
1637
+ continue;
1638
+ }
1639
+ for (const [thisField, thisValidator] of thisFields.entries()) {
1640
+ for (const [thatField, thatValidator] of thatFields.entries()) {
1641
+ if (!(0, types_1.typesCanBeMerged)(thisField.definition.type, thatField.definition.type)
1642
+ || (thisValidator && thatValidator && !thisValidator.hasSameResponseShapeThan(thatValidator))) {
1643
+ return false;
1644
+ }
1645
+ }
1646
+ }
1647
+ }
1648
+ return true;
1649
+ }
1650
+ toString(indent = '') {
1651
+ return '{\n'
1652
+ + [...this.byResponseName.entries()].map(([name, byFields]) => {
1653
+ const innerIndent = indent + ' ';
1654
+ return `${innerIndent}${name}: [\n`
1655
+ + [...byFields.entries()]
1656
+ .map(([field, next]) => `${innerIndent} ${field.parentType}.${field}${next ? next.toString(innerIndent + ' ') : ''}`)
1657
+ .join('\n')
1658
+ + `\n${innerIndent}]`;
1659
+ }).join('\n')
1660
+ + `\n${indent}}`;
1661
+ }
1662
+ }
1472
1663
  class FieldSelection extends AbstractSelection {
1473
1664
  constructor(field, _selectionSet) {
1474
1665
  super(field);
@@ -1485,22 +1676,27 @@ class FieldSelection extends AbstractSelection {
1485
1676
  return this.element.definition.name === definitions_1.typenameFieldName;
1486
1677
  }
1487
1678
  withUpdatedComponents(field, selectionSet) {
1679
+ if (this.element === field && this.selectionSet === selectionSet) {
1680
+ return this;
1681
+ }
1488
1682
  return new FieldSelection(field, selectionSet);
1489
1683
  }
1490
1684
  key() {
1491
1685
  return this.element.key();
1492
1686
  }
1493
- optimize(fragments) {
1687
+ optimize(fragments, validator) {
1494
1688
  const fieldBaseType = (0, definitions_1.baseType)(this.element.definition.type);
1495
1689
  if (!(0, definitions_1.isCompositeType)(fieldBaseType) || !this.selectionSet) {
1496
1690
  return this;
1497
1691
  }
1692
+ const fieldValidator = validator.forField(this.element);
1498
1693
  let optimizedSelection = this.selectionSet;
1499
1694
  if ((0, definitions_1.isCompositeType)(fieldBaseType) && this.selectionSet) {
1500
1695
  const optimized = this.tryOptimizeSubselectionWithFragments({
1501
1696
  parentType: fieldBaseType,
1502
1697
  subSelection: this.selectionSet,
1503
1698
  fragments,
1699
+ validator: fieldValidator,
1504
1700
  canUseFullMatchingFragment: (fragment) => fragment.appliedDirectives.length === 0,
1505
1701
  });
1506
1702
  if (optimized instanceof NamedFragmentDefinition) {
@@ -1510,16 +1706,16 @@ class FieldSelection extends AbstractSelection {
1510
1706
  optimizedSelection = optimized;
1511
1707
  }
1512
1708
  }
1513
- optimizedSelection = optimizedSelection.optimize(fragments);
1709
+ optimizedSelection = optimizedSelection.optimizeSelections(fragments, fieldValidator);
1514
1710
  return this.selectionSet === optimizedSelection
1515
1711
  ? this
1516
1712
  : this.withUpdatedSelectionSet(optimizedSelection);
1517
1713
  }
1518
- filter(predicate) {
1714
+ filterRecursiveDepthFirst(predicate) {
1519
1715
  if (!this.selectionSet) {
1520
1716
  return predicate(this) ? this : undefined;
1521
1717
  }
1522
- const updatedSelectionSet = this.selectionSet.filter(predicate);
1718
+ const updatedSelectionSet = this.selectionSet.filterRecursiveDepthFirst(predicate);
1523
1719
  const thisWithFilteredSelectionSet = this.selectionSet === updatedSelectionSet
1524
1720
  ? this
1525
1721
  : new FieldSelection(this.element, updatedSelectionSet);
@@ -1586,19 +1782,23 @@ class FieldSelection extends AbstractSelection {
1586
1782
  var _a;
1587
1783
  return !!((_a = this.selectionSet) === null || _a === void 0 ? void 0 : _a.hasDefer());
1588
1784
  }
1589
- trimUnsatisfiableBranches(_, options) {
1590
- var _a, _b;
1785
+ normalize({ parentType, recursive }) {
1786
+ const definition = parentType === this.parentType
1787
+ ? this.element.definition
1788
+ : parentType.field(this.element.name);
1789
+ (0, utils_1.assert)(definition, `Cannot normalize ${this.element} at ${parentType} which does not have that field`);
1790
+ const element = this.element.definition === definition ? this.element : this.element.withUpdatedDefinition(definition);
1591
1791
  if (!this.selectionSet) {
1592
- return this;
1792
+ return this.withUpdatedElement(element);
1593
1793
  }
1594
- const base = this.element.baseType();
1595
- (0, utils_1.assert)((0, definitions_1.isCompositeType)(base), () => `Field ${this.element} should not have a sub-selection`);
1596
- const trimmed = ((_a = options === null || options === void 0 ? void 0 : options.recursive) !== null && _a !== void 0 ? _a : true) ? this.mapToSelectionSet((s) => s.trimUnsatisfiableBranches(base)) : this;
1597
- if ((_b = trimmed.selectionSet) === null || _b === void 0 ? void 0 : _b.isEmpty()) {
1598
- return trimmed.withUpdatedSelectionSet(selectionSetOfElement(new Field(base.typenameField(), undefined, [new definitions_1.Directive('include', { 'if': false })])));
1794
+ const base = element.baseType();
1795
+ (0, utils_1.assert)((0, definitions_1.isCompositeType)(base), () => `Field ${element} should not have a sub-selection`);
1796
+ const normalizedSubSelection = (recursive !== null && recursive !== void 0 ? recursive : true) ? this.selectionSet.normalize({ parentType: base }) : this.selectionSet;
1797
+ if (normalizedSubSelection === null || normalizedSubSelection === void 0 ? void 0 : normalizedSubSelection.isEmpty()) {
1798
+ return this.withUpdatedComponents(element, selectionSetOfElement(new Field(base.typenameField(), undefined, [new definitions_1.Directive('include', { 'if': false })])));
1599
1799
  }
1600
1800
  else {
1601
- return trimmed;
1801
+ return this.withUpdatedComponents(element, normalizedSubSelection);
1602
1802
  }
1603
1803
  }
1604
1804
  expandFragments(updatedFragments) {
@@ -1647,10 +1847,9 @@ class FragmentSelection extends AbstractSelection {
1647
1847
  validate(schemaDef.rootType('mutation') !== parentType && schemaDef.rootType('subscription') !== parentType, () => { var _a; return `The @defer and @stream directives cannot be used on ${(_a = schemaDef.roots().filter((t) => t.type === parentType).pop()) === null || _a === void 0 ? void 0 : _a.rootKind} root type "${parentType}"`; });
1648
1848
  }
1649
1849
  }
1650
- filter(predicate) {
1651
- const selectionSet = this.selectionSet;
1652
- const updatedSelectionSet = selectionSet.filter(predicate);
1653
- const thisWithFilteredSelectionSet = updatedSelectionSet === selectionSet
1850
+ filterRecursiveDepthFirst(predicate) {
1851
+ const updatedSelectionSet = this.selectionSet.filterRecursiveDepthFirst(predicate);
1852
+ const thisWithFilteredSelectionSet = updatedSelectionSet === this.selectionSet
1654
1853
  ? this
1655
1854
  : new InlineFragmentSelection(this.element, updatedSelectionSet);
1656
1855
  return predicate(thisWithFilteredSelectionSet) ? thisWithFilteredSelectionSet : undefined;
@@ -1672,6 +1871,9 @@ class InlineFragmentSelection extends FragmentSelection {
1672
1871
  return this.element.key();
1673
1872
  }
1674
1873
  withUpdatedComponents(fragment, selectionSet) {
1874
+ if (fragment === this.element && selectionSet === this.selectionSet) {
1875
+ return this;
1876
+ }
1675
1877
  return new InlineFragmentSelection(fragment, selectionSet);
1676
1878
  }
1677
1879
  validate(variableDefinitions) {
@@ -1720,7 +1922,7 @@ class InlineFragmentSelection extends FragmentSelection {
1720
1922
  selectionSet: this.selectionSet.toSelectionSetNode()
1721
1923
  };
1722
1924
  }
1723
- optimize(fragments) {
1925
+ optimize(fragments, validator) {
1724
1926
  let optimizedSelection = this.selectionSet;
1725
1927
  const typeCondition = this.element.typeCondition;
1726
1928
  if (typeCondition) {
@@ -1728,6 +1930,7 @@ class InlineFragmentSelection extends FragmentSelection {
1728
1930
  parentType: typeCondition,
1729
1931
  subSelection: optimizedSelection,
1730
1932
  fragments,
1933
+ validator,
1731
1934
  canUseFullMatchingFragment: (fragment) => {
1732
1935
  return fragment.appliedDirectives.length === 0
1733
1936
  || ((0, types_1.sameType)(typeCondition, fragment.typeCondition)
@@ -1750,7 +1953,7 @@ class InlineFragmentSelection extends FragmentSelection {
1750
1953
  optimizedSelection = optimized;
1751
1954
  }
1752
1955
  }
1753
- optimizedSelection = optimizedSelection.optimizeSelections(fragments);
1956
+ optimizedSelection = optimizedSelection.optimizeSelections(fragments, validator);
1754
1957
  return this.selectionSet === optimizedSelection
1755
1958
  ? this
1756
1959
  : new InlineFragmentSelection(this.element, optimizedSelection);
@@ -1778,42 +1981,42 @@ class InlineFragmentSelection extends FragmentSelection {
1778
1981
  ? this
1779
1982
  : this.withUpdatedComponents(newElement, newSelection);
1780
1983
  }
1781
- trimUnsatisfiableBranches(currentType, options) {
1782
- var _a, _b, _c;
1783
- const recursive = (_a = options === null || options === void 0 ? void 0 : options.recursive) !== null && _a !== void 0 ? _a : true;
1984
+ normalize({ parentType, recursive }) {
1985
+ var _a;
1784
1986
  const thisCondition = this.element.typeCondition;
1987
+ if (thisCondition && parentType !== this.parentType) {
1988
+ const conditionRuntimes = (0, definitions_1.possibleRuntimeTypes)(thisCondition);
1989
+ const typeRuntimes = (0, definitions_1.possibleRuntimeTypes)(parentType);
1990
+ if (!conditionRuntimes.some((t) => typeRuntimes.includes(t))) {
1991
+ return undefined;
1992
+ }
1993
+ }
1785
1994
  if (this.element.appliedDirectives.length === 0) {
1786
- if (!thisCondition || currentType === this.element.typeCondition) {
1787
- const trimmed = this.selectionSet.trimUnsatisfiableBranches(currentType, options);
1788
- return trimmed.isEmpty() ? undefined : trimmed;
1995
+ if (!thisCondition || parentType === this.element.typeCondition || (0, definitions_1.isObjectType)(parentType)) {
1996
+ const normalized = this.selectionSet.normalize({ parentType, recursive });
1997
+ return normalized.isEmpty() ? undefined : normalized;
1789
1998
  }
1790
- if ((0, definitions_1.isObjectType)(currentType)) {
1791
- if ((0, definitions_1.isObjectType)(thisCondition) || !(0, definitions_1.possibleRuntimeTypes)(thisCondition).includes(currentType)) {
1999
+ }
2000
+ let normalizedSelectionSet;
2001
+ if (recursive !== null && recursive !== void 0 ? recursive : true) {
2002
+ normalizedSelectionSet = this.selectionSet.normalize({ parentType: thisCondition !== null && thisCondition !== void 0 ? thisCondition : parentType });
2003
+ if (normalizedSelectionSet.isEmpty()) {
2004
+ if (this.element.appliedDirectives.length === 0) {
1792
2005
  return undefined;
1793
2006
  }
1794
2007
  else {
1795
- const trimmed = this.selectionSet.trimUnsatisfiableBranches(currentType, options);
1796
- return trimmed.isEmpty() ? undefined : trimmed;
2008
+ return this.withUpdatedComponents(this.element.rebaseOn(parentType), selectionSetOfElement(new Field(((_a = this.element.typeCondition) !== null && _a !== void 0 ? _a : parentType).typenameField(), undefined, [new definitions_1.Directive('include', { 'if': false })])));
1797
2009
  }
1798
2010
  }
1799
2011
  }
1800
- if (!recursive) {
1801
- return this;
1802
- }
1803
- const trimmedSelectionSet = this.selectionSet.trimUnsatisfiableBranches((_b = this.element.typeCondition) !== null && _b !== void 0 ? _b : this.parentType);
1804
- if (trimmedSelectionSet.isEmpty()) {
1805
- if (this.element.appliedDirectives.length === 0) {
1806
- return undefined;
1807
- }
1808
- else {
1809
- return this.withUpdatedSelectionSet(selectionSetOfElement(new Field(((_c = this.element.typeCondition) !== null && _c !== void 0 ? _c : this.parentType).typenameField(), undefined, [new definitions_1.Directive('include', { 'if': false })])));
1810
- }
2012
+ else {
2013
+ normalizedSelectionSet = this.selectionSet;
1811
2014
  }
1812
2015
  if (this.element.appliedDirectives.length === 0 && (0, definitions_1.isAbstractType)(thisCondition)) {
1813
- (0, utils_1.assert)(!(0, definitions_1.isObjectType)(currentType), () => `Should not have got here if ${currentType} is an object type`);
1814
- const currentRuntimes = (0, definitions_1.possibleRuntimeTypes)(currentType);
2016
+ (0, utils_1.assert)(!(0, definitions_1.isObjectType)(parentType), () => `Should not have got here if ${parentType} is an object type`);
2017
+ const currentRuntimes = (0, definitions_1.possibleRuntimeTypes)(parentType);
1815
2018
  const liftableSelections = [];
1816
- for (const selection of trimmedSelectionSet.selections()) {
2019
+ for (const selection of normalizedSelectionSet.selections()) {
1817
2020
  if (selection.kind === 'FragmentSelection'
1818
2021
  && selection.element.typeCondition
1819
2022
  && (0, definitions_1.isObjectType)(selection.element.typeCondition)
@@ -1821,17 +2024,19 @@ class InlineFragmentSelection extends FragmentSelection {
1821
2024
  liftableSelections.push(selection);
1822
2025
  }
1823
2026
  }
1824
- if (liftableSelections.length === trimmedSelectionSet.selections().length) {
1825
- return trimmedSelectionSet;
2027
+ if (liftableSelections.length === normalizedSelectionSet.selections().length) {
2028
+ return normalizedSelectionSet;
1826
2029
  }
1827
2030
  if (liftableSelections.length > 0) {
1828
2031
  const newSet = new SelectionSetUpdates();
1829
2032
  newSet.add(liftableSelections);
1830
- newSet.add(this.withUpdatedSelectionSet(trimmedSelectionSet.filter((s) => !liftableSelections.includes(s))));
1831
- return newSet.toSelectionSet(this.parentType);
2033
+ newSet.add(this.withUpdatedSelectionSet(normalizedSelectionSet.filter((s) => !liftableSelections.includes(s))));
2034
+ return newSet.toSelectionSet(parentType);
1832
2035
  }
1833
2036
  }
1834
- return this.selectionSet === trimmedSelectionSet ? this : this.withUpdatedSelectionSet(trimmedSelectionSet);
2037
+ return this.parentType === parentType && this.selectionSet === normalizedSelectionSet
2038
+ ? this
2039
+ : this.withUpdatedComponents(this.element.rebaseOn(parentType), normalizedSelectionSet);
1835
2040
  }
1836
2041
  expandFragments(updatedFragments) {
1837
2042
  return this.mapToSelectionSet((s) => s.expandFragments(updatedFragments));
@@ -1876,12 +2081,13 @@ class FragmentSpreadSelection extends FragmentSelection {
1876
2081
  withUpdatedComponents(_fragment, _selectionSet) {
1877
2082
  (0, utils_1.assert)(false, `Unsupported`);
1878
2083
  }
1879
- trimUnsatisfiableBranches(parentType) {
1880
- (0, utils_1.assert)(parentType.schema() === this.parentType.schema(), 'Should not try to trim using a type from another schema');
2084
+ normalize({ parentType }) {
2085
+ (0, utils_1.assert)(parentType.schema() === this.parentType.schema(), 'Should not try to normalize using a type from another schema');
1881
2086
  return this.rebaseOn(parentType, this.fragments);
1882
2087
  }
1883
2088
  validate() {
1884
2089
  this.validateDeferAndStream();
2090
+ validate((0, definitions_1.runtimeTypesIntersects)(this.parentType, this.namedFragment.typeCondition), () => `Fragment "${this.namedFragment.name}" cannot be spread inside type ${this.parentType} as the runtime types do not intersect ${this.namedFragment.typeCondition}`);
1885
2091
  }
1886
2092
  toSelectionNode() {
1887
2093
  const directiveNodes = this.spreadDirectives.length === 0
@@ -1902,7 +2108,7 @@ class FragmentSpreadSelection extends FragmentSelection {
1902
2108
  directives: directiveNodes,
1903
2109
  };
1904
2110
  }
1905
- optimize(_) {
2111
+ optimize(_1, _2) {
1906
2112
  return this;
1907
2113
  }
1908
2114
  rebaseOn(parentType, fragments) {