@apollo/federation-internals 2.4.7 → 2.4.9

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.
@@ -25,6 +25,9 @@ class AbstractOperationElement extends definitions_1.DirectiveTargetElement {
25
25
  this.collectVariablesInElement(collector);
26
26
  this.collectVariablesInAppliedDirectives(collector);
27
27
  }
28
+ rebaseOnOrError(parentType) {
29
+ return this.rebaseOn({ parentType, errorIfCannotRebase: true });
30
+ }
28
31
  addAttachement(key, value) {
29
32
  if (!this.attachements) {
30
33
  this.attachements = new Map();
@@ -162,7 +165,7 @@ class Field extends AbstractOperationElement {
162
165
  }
163
166
  }
164
167
  }
165
- rebaseOn(parentType) {
168
+ rebaseOn({ parentType, errorIfCannotRebase }) {
166
169
  const fieldParent = this.definition.parent;
167
170
  if (parentType === fieldParent) {
168
171
  return this;
@@ -170,9 +173,12 @@ class Field extends AbstractOperationElement {
170
173
  if (this.name === definitions_1.typenameFieldName) {
171
174
  return this.withUpdatedDefinition(parentType.typenameField());
172
175
  }
173
- validate(this.canRebaseOn(parentType), () => `Cannot add selection of field "${this.definition.coordinate}" to selection set of parent type "${parentType}"`);
174
176
  const fieldDef = parentType.field(this.name);
175
- validate(fieldDef, () => `Cannot add selection of field "${this.definition.coordinate}" to selection set of parent type "${parentType}" (that does not declare that field)`);
177
+ const canRebase = this.canRebaseOn(parentType) && fieldDef;
178
+ if (!canRebase) {
179
+ validate(!errorIfCannotRebase, () => `Cannot add selection of field "${this.definition.coordinate}" to selection set of parent type "${parentType}"`);
180
+ return undefined;
181
+ }
176
182
  return this.withUpdatedDefinition(fieldDef);
177
183
  }
178
184
  canRebaseOn(parentType) {
@@ -276,14 +282,17 @@ class FragmentElement extends AbstractOperationElement {
276
282
  this.copyAttachementsTo(newFragment);
277
283
  return newFragment;
278
284
  }
279
- rebaseOn(parentType) {
285
+ rebaseOn({ parentType, errorIfCannotRebase }) {
280
286
  const fragmentParent = this.parentType;
281
287
  const typeCondition = this.typeCondition;
282
288
  if (parentType === fragmentParent) {
283
289
  return this;
284
290
  }
285
291
  const { canRebase, rebasedCondition } = this.canRebaseOn(parentType);
286
- validate(canRebase, () => `Cannot add fragment of condition "${typeCondition}" (runtimes: [${(0, definitions_1.possibleRuntimeTypes)(typeCondition)}]) to parent type "${parentType}" (runtimes: ${(0, definitions_1.possibleRuntimeTypes)(parentType)})`);
292
+ if (!canRebase) {
293
+ validate(!errorIfCannotRebase, () => `Cannot add fragment of condition "${typeCondition}" (runtimes: [${(0, definitions_1.possibleRuntimeTypes)(typeCondition)}]) to parent type "${parentType}" (runtimes: ${(0, definitions_1.possibleRuntimeTypes)(parentType)})`);
294
+ return undefined;
295
+ }
287
296
  return this.withUpdatedTypes(parentType, rebasedCondition);
288
297
  }
289
298
  canRebaseOn(parentType) {
@@ -527,6 +536,18 @@ class Operation {
527
536
  this.fragments = fragments;
528
537
  this.name = name;
529
538
  }
539
+ withUpdatedSelectionSet(newSelectionSet) {
540
+ if (this.selectionSet === newSelectionSet) {
541
+ return this;
542
+ }
543
+ return new Operation(this.schema, this.rootKind, newSelectionSet, this.variableDefinitions, this.fragments, this.name);
544
+ }
545
+ withUpdatedSelectionSetAndFragments(newSelectionSet, newFragments) {
546
+ if (this.selectionSet === newSelectionSet && newFragments === this.fragments) {
547
+ return this;
548
+ }
549
+ return new Operation(this.schema, this.rootKind, newSelectionSet, this.variableDefinitions, newFragments, this.name);
550
+ }
530
551
  optimize(fragments, minUsagesToOptimize = 2) {
531
552
  (0, utils_1.assert)(minUsagesToOptimize >= 1, `Expected 'minUsagesToOptimize' to be at least 1, but got ${minUsagesToOptimize}`);
532
553
  if (!fragments || fragments.isEmpty()) {
@@ -536,41 +557,39 @@ class Operation {
536
557
  if (optimizedSelection === this.selectionSet) {
537
558
  return this;
538
559
  }
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);
560
+ let finalFragments = computeFragmentsToKeep(optimizedSelection, fragments, minUsagesToOptimize);
561
+ if (finalFragments !== null && (finalFragments === null || finalFragments === void 0 ? void 0 : finalFragments.size) !== fragments.size) {
562
+ optimizedSelection = optimizedSelection.expandFragments(finalFragments);
563
+ optimizedSelection = optimizedSelection.normalize({ parentType: optimizedSelection.parentType });
564
+ if (finalFragments) {
565
+ let beforeRemoval;
566
+ do {
567
+ beforeRemoval = finalFragments;
568
+ const usages = new Map();
569
+ optimizedSelection.collectUsedFragmentNames(usages);
570
+ finalFragments.collectUsedFragmentNames(usages);
571
+ finalFragments = finalFragments.filter((f) => { var _a; return ((_a = usages.get(f.name)) !== null && _a !== void 0 ? _a : 0) > 0; });
572
+ } while (finalFragments && finalFragments.size < beforeRemoval.size);
573
+ }
542
574
  }
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);
575
+ return this.withUpdatedSelectionSetAndFragments(optimizedSelection, finalFragments !== null && finalFragments !== void 0 ? finalFragments : undefined);
546
576
  }
547
577
  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);
578
+ const expanded = this.selectionSet.expandFragments();
579
+ return this.withUpdatedSelectionSetAndFragments(expanded.normalize({ parentType: expanded.parentType }), undefined);
553
580
  }
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);
581
+ normalize() {
582
+ return this.withUpdatedSelectionSet(this.selectionSet.normalize({ parentType: this.selectionSet.parentType }));
560
583
  }
561
584
  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);
585
+ return this.withUpdatedSelectionSet(this.selectionSet.withoutDefer(labelsToRemove));
566
586
  }
567
587
  withNormalizedDefer() {
568
588
  const normalizer = new DeferNormalizer();
569
589
  const { hasDefers, hasNonLabelledOrConditionalDefers } = normalizer.init(this.selectionSet);
570
590
  let updatedOperation = this;
571
591
  if (hasNonLabelledOrConditionalDefers) {
572
- const updated = this.selectionSet.withNormalizedDefer(normalizer);
573
- updatedOperation = new Operation(this.schema, this.rootKind, updated, this.variableDefinitions, this.fragments, this.name);
592
+ updatedOperation = this.withUpdatedSelectionSet(this.selectionSet.withNormalizedDefer(normalizer));
574
593
  }
575
594
  return {
576
595
  operation: updatedOperation,
@@ -612,7 +631,7 @@ class NamedFragmentDefinition extends definitions_1.DirectiveTargetElement {
612
631
  }
613
632
  expandedSelectionSet() {
614
633
  if (!this._expandedSelectionSet) {
615
- this._expandedSelectionSet = this.selectionSet.expandFragments().trimUnsatisfiableBranches(this.typeCondition);
634
+ this._expandedSelectionSet = this.selectionSet.expandFragments().normalize({ parentType: this.typeCondition });
616
635
  }
617
636
  return this._expandedSelectionSet;
618
637
  }
@@ -650,7 +669,7 @@ class NamedFragmentDefinition extends definitions_1.DirectiveTargetElement {
650
669
  selectionSet: this.selectionSet.toSelectionSetNode()
651
670
  };
652
671
  }
653
- canApplyAtType(type) {
672
+ canApplyDirectlyAtType(type) {
654
673
  if ((0, types_1.sameType)(type, this.typeCondition)) {
655
674
  return true;
656
675
  }
@@ -659,23 +678,29 @@ class NamedFragmentDefinition extends definitions_1.DirectiveTargetElement {
659
678
  }
660
679
  const conditionRuntimes = (0, definitions_1.possibleRuntimeTypes)(this.typeCondition);
661
680
  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)));
681
+ if (conditionRuntimes.length < typeRuntimes.length
682
+ || !typeRuntimes.every((t1) => conditionRuntimes.some((t2) => (0, types_1.sameType)(t1, t2)))) {
683
+ return false;
684
+ }
685
+ return (0, definitions_1.isObjectType)(type) || (0, definitions_1.isUnionType)(this.typeCondition);
664
686
  }
665
687
  expandedSelectionSetAtType(type) {
666
- const expandedSelectionSet = this.expandedSelectionSet();
667
688
  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;
689
+ return { selectionSet: this.expandedSelectionSet() };
672
690
  }
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);
691
+ let cached = this.expandedSelectionSetsAtTypesCache.get(type.name);
692
+ if (!cached) {
693
+ cached = this.computeExpandedSelectionSetAtType(type);
694
+ this.expandedSelectionSetsAtTypesCache.set(type.name, cached);
677
695
  }
678
- return selectionAtType;
696
+ return cached;
697
+ }
698
+ computeExpandedSelectionSetAtType(type) {
699
+ const expandedSelectionSet = this.expandedSelectionSet();
700
+ const selectionSet = expandedSelectionSet.normalize({ parentType: type, recursive: false });
701
+ const trimmed = expandedSelectionSet.minus(selectionSet);
702
+ const validator = trimmed.isEmpty() ? undefined : FieldsConflictValidator.build(trimmed);
703
+ return { selectionSet, validator };
679
704
  }
680
705
  includes(otherFragment) {
681
706
  if (this.name === otherFragment) {
@@ -696,7 +721,7 @@ class NamedFragmentDefinition extends definitions_1.DirectiveTargetElement {
696
721
  return included;
697
722
  }
698
723
  toString(indent) {
699
- return (indent !== null && indent !== void 0 ? indent : '') + `fragment ${this.name} on ${this.typeCondition}${this.appliedDirectivesToString()} ${this.selectionSet.toString(false, true, indent)}`;
724
+ return `fragment ${this.name} on ${this.typeCondition}${this.appliedDirectivesToString()} ${this.selectionSet.toString(false, true, indent)}`;
700
725
  }
701
726
  }
702
727
  exports.NamedFragmentDefinition = NamedFragmentDefinition;
@@ -724,8 +749,8 @@ class NamedFragments {
724
749
  this.fragments.set(fragment.name, fragment);
725
750
  }
726
751
  }
727
- maybeApplyingAtType(type) {
728
- return this.fragments.values().filter(f => f.canApplyAtType(type));
752
+ maybeApplyingDirectlyAtType(type) {
753
+ return this.fragments.values().filter(f => f.canApplyDirectlyAtType(type));
729
754
  }
730
755
  get(name) {
731
756
  return this.fragments.get(name);
@@ -736,6 +761,11 @@ class NamedFragments {
736
761
  definitions() {
737
762
  return this.fragments.values();
738
763
  }
764
+ collectUsedFragmentNames(collector) {
765
+ for (const fragment of this.definitions()) {
766
+ fragment.collectUsedFragmentNames(collector);
767
+ }
768
+ }
739
769
  map(mapper) {
740
770
  const mapped = new NamedFragments();
741
771
  for (const def of this.fragments.values()) {
@@ -772,7 +802,7 @@ class NamedFragments {
772
802
  }
773
803
  mapToExpandedSelectionSets(mapper) {
774
804
  return this.mapInDependencyOrder((fragment, newFragments) => {
775
- const mappedSelectionSet = mapper(fragment.selectionSet.expandFragments().trimUnsatisfiableBranches(fragment.typeCondition));
805
+ const mappedSelectionSet = mapper(fragment.selectionSet.expandFragments().normalize({ parentType: fragment.typeCondition }));
776
806
  if (!mappedSelectionSet) {
777
807
  return undefined;
778
808
  }
@@ -780,19 +810,28 @@ class NamedFragments {
780
810
  return fragment.withUpdatedSelectionSet(reoptimizedSelectionSet);
781
811
  });
782
812
  }
813
+ selectionSetIsWorthUsing(selectionSet) {
814
+ const selections = selectionSet.selections();
815
+ if (selections.length === 0) {
816
+ return false;
817
+ }
818
+ if (selections.length === 1) {
819
+ const s = selections[0];
820
+ return !(s.kind === 'FieldSelection' && s.element.isLeafField());
821
+ }
822
+ return true;
823
+ }
783
824
  rebaseOn(schema) {
784
825
  return this.mapInDependencyOrder((fragment, newFragments) => {
785
826
  const rebasedType = schema.type(fragment.selectionSet.parentType.name);
786
- try {
787
- if (!rebasedType || !(0, definitions_1.isCompositeType)(rebasedType)) {
788
- return undefined;
789
- }
790
- const rebasedSelection = fragment.selectionSet.rebaseOn(rebasedType, newFragments);
791
- return new NamedFragmentDefinition(schema, fragment.name, rebasedType).setSelectionSet(rebasedSelection);
792
- }
793
- catch (e) {
827
+ if (!rebasedType || !(0, definitions_1.isCompositeType)(rebasedType)) {
794
828
  return undefined;
795
829
  }
830
+ const rebasedSelection = fragment.selectionSet.rebaseOn({ parentType: rebasedType, fragments: newFragments, errorIfCannotRebase: false });
831
+ return this.selectionSetIsWorthUsing(rebasedSelection)
832
+ ? new NamedFragmentDefinition(schema, fragment.name, rebasedType).setSelectionSet(rebasedSelection)
833
+ : undefined;
834
+ ;
796
835
  });
797
836
  }
798
837
  filter(predicate) {
@@ -872,7 +911,7 @@ var ContainsResult;
872
911
  ContainsResult[ContainsResult["NOT_CONTAINED"] = 0] = "NOT_CONTAINED";
873
912
  ContainsResult[ContainsResult["STRICTLY_CONTAINED"] = 1] = "STRICTLY_CONTAINED";
874
913
  ContainsResult[ContainsResult["EQUAL"] = 2] = "EQUAL";
875
- })(ContainsResult = exports.ContainsResult || (exports.ContainsResult = {}));
914
+ })(ContainsResult || (exports.ContainsResult = ContainsResult = {}));
876
915
  class SelectionSet {
877
916
  constructor(parentType, keyedSelections = new Map()) {
878
917
  this.parentType = parentType;
@@ -893,6 +932,18 @@ class SelectionSet {
893
932
  hasTopLevelTypenameField() {
894
933
  return this._keyedSelections.has(definitions_1.typenameFieldName);
895
934
  }
935
+ withoutTopLevelTypenameField() {
936
+ if (!this.hasTopLevelTypenameField) {
937
+ return this;
938
+ }
939
+ const newKeyedSelections = new Map();
940
+ for (const [key, selection] of this._keyedSelections) {
941
+ if (key !== definitions_1.typenameFieldName) {
942
+ newKeyedSelections.set(key, selection);
943
+ }
944
+ }
945
+ return new SelectionSet(this.parentType, newKeyedSelections);
946
+ }
896
947
  fieldsInSet() {
897
948
  const fields = new Array();
898
949
  for (const selection of this.selections()) {
@@ -909,6 +960,21 @@ class SelectionSet {
909
960
  }
910
961
  return fields;
911
962
  }
963
+ fieldsByResponseName() {
964
+ const byResponseName = new utils_1.MultiMap();
965
+ this.collectFieldsByResponseName(byResponseName);
966
+ return byResponseName;
967
+ }
968
+ collectFieldsByResponseName(collector) {
969
+ for (const selection of this.selections()) {
970
+ if (selection.kind === 'FieldSelection') {
971
+ collector.add(selection.element.responseName(), selection);
972
+ }
973
+ else {
974
+ selection.selectionSet.collectFieldsByResponseName(collector);
975
+ }
976
+ }
977
+ }
912
978
  usedVariables() {
913
979
  const collector = new definitions_1.VariableCollector();
914
980
  this.collectVariables(collector);
@@ -929,19 +995,20 @@ class SelectionSet {
929
995
  return this;
930
996
  }
931
997
  const wrapped = new InlineFragmentSelection(new FragmentElement(this.parentType, this.parentType), this);
932
- const optimized = wrapped.optimize(fragments);
998
+ const validator = FieldsConflictMultiBranchValidator.ofInitial(FieldsConflictValidator.build(this));
999
+ const optimized = wrapped.optimize(fragments, validator);
933
1000
  return optimized instanceof FragmentSpreadSelection
934
1001
  ? selectionSetOf(this.parentType, optimized)
935
1002
  : optimized.selectionSet;
936
1003
  }
937
- optimizeSelections(fragments) {
938
- return this.lazyMap((selection) => selection.optimize(fragments));
1004
+ optimizeSelections(fragments, validator) {
1005
+ return this.lazyMap((selection) => selection.optimize(fragments, validator));
939
1006
  }
940
1007
  expandFragments(updatedFragments) {
941
1008
  return this.lazyMap((selection) => selection.expandFragments(updatedFragments));
942
1009
  }
943
- trimUnsatisfiableBranches(parentType, options) {
944
- return this.lazyMap((selection) => selection.trimUnsatisfiableBranches(parentType, options), { parentType });
1010
+ normalize({ parentType, recursive }) {
1011
+ return this.lazyMap((selection) => selection.normalize({ parentType, recursive }), { parentType });
945
1012
  }
946
1013
  lazyMap(mapper, options) {
947
1014
  var _a;
@@ -975,19 +1042,25 @@ class SelectionSet {
975
1042
  return this.selections().some((s) => s.hasDefer());
976
1043
  }
977
1044
  filter(predicate) {
978
- return this.lazyMap((selection) => selection.filter(predicate));
1045
+ return this.lazyMap((selection) => predicate(selection) ? selection : undefined);
1046
+ }
1047
+ filterRecursiveDepthFirst(predicate) {
1048
+ return this.lazyMap((selection) => selection.filterRecursiveDepthFirst(predicate));
979
1049
  }
980
1050
  withoutEmptyBranches() {
981
- const updated = this.filter((selection) => { var _a; return ((_a = selection.selectionSet) === null || _a === void 0 ? void 0 : _a.isEmpty()) !== true; });
1051
+ const updated = this.filterRecursiveDepthFirst((selection) => { var _a; return ((_a = selection.selectionSet) === null || _a === void 0 ? void 0 : _a.isEmpty()) !== true; });
982
1052
  return updated.isEmpty() ? undefined : updated;
983
1053
  }
984
- rebaseOn(parentType, fragments) {
1054
+ rebaseOn({ parentType, fragments, errorIfCannotRebase, }) {
985
1055
  if (this.parentType === parentType) {
986
1056
  return this;
987
1057
  }
988
1058
  const newSelections = new Map();
989
1059
  for (const selection of this.selections()) {
990
- newSelections.set(selection.key(), selection.rebaseOn(parentType, fragments));
1060
+ const rebasedSelection = selection.rebaseOn({ parentType, fragments, errorIfCannotRebase });
1061
+ if (rebasedSelection) {
1062
+ newSelections.set(selection.key(), rebasedSelection);
1063
+ }
991
1064
  }
992
1065
  return new SelectionSet(parentType, newSelections);
993
1066
  }
@@ -1006,14 +1079,21 @@ class SelectionSet {
1006
1079
  }
1007
1080
  return true;
1008
1081
  }
1009
- contains(that) {
1082
+ contains(that, options) {
1083
+ var _a;
1084
+ const ignoreMissingTypename = (_a = options === null || options === void 0 ? void 0 : options.ignoreMissingTypename) !== null && _a !== void 0 ? _a : false;
1010
1085
  if (that._selections.length > this._selections.length) {
1011
- return ContainsResult.NOT_CONTAINED;
1086
+ if (!ignoreMissingTypename || that._selections.length > this._selections.length + 1 || this.hasTopLevelTypenameField() || !that.hasTopLevelTypenameField()) {
1087
+ return ContainsResult.NOT_CONTAINED;
1088
+ }
1012
1089
  }
1013
1090
  let isEqual = true;
1014
1091
  for (const [key, thatSelection] of that._keyedSelections) {
1092
+ if (key === definitions_1.typenameFieldName && ignoreMissingTypename) {
1093
+ continue;
1094
+ }
1015
1095
  const thisSelection = this._keyedSelections.get(key);
1016
- const selectionResult = thisSelection === null || thisSelection === void 0 ? void 0 : thisSelection.contains(thatSelection);
1096
+ const selectionResult = thisSelection === null || thisSelection === void 0 ? void 0 : thisSelection.contains(thatSelection, options);
1017
1097
  if (selectionResult === undefined || selectionResult === ContainsResult.NOT_CONTAINED) {
1018
1098
  return ContainsResult.NOT_CONTAINED;
1019
1099
  }
@@ -1023,20 +1103,9 @@ class SelectionSet {
1023
1103
  ? ContainsResult.EQUAL
1024
1104
  : ContainsResult.STRICTLY_CONTAINED;
1025
1105
  }
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 };
1106
+ containsTopLevelField(field) {
1107
+ const selection = this._keyedSelections.get(field.key());
1108
+ return !!selection && selection.element.equals(field);
1040
1109
  }
1041
1110
  minus(that) {
1042
1111
  const updated = new SelectionSetUpdates();
@@ -1054,6 +1123,25 @@ class SelectionSet {
1054
1123
  }
1055
1124
  return updated.toSelectionSet(this.parentType);
1056
1125
  }
1126
+ intersectionWith(that) {
1127
+ if (this.isEmpty()) {
1128
+ return this;
1129
+ }
1130
+ if (that.isEmpty()) {
1131
+ return that;
1132
+ }
1133
+ const intersection = new SelectionSetUpdates();
1134
+ for (const [key, thisSelection] of this._keyedSelections) {
1135
+ const thatSelection = that._keyedSelections.get(key);
1136
+ if (thatSelection) {
1137
+ const selection = thisSelection.intersectionWith(thatSelection);
1138
+ if (selection) {
1139
+ intersection.add(selection);
1140
+ }
1141
+ }
1142
+ }
1143
+ return intersection.toSelectionSet(this.parentType);
1144
+ }
1057
1145
  canRebaseOn(parentTypeToTest) {
1058
1146
  return this.selections().every((selection) => selection.canAddTo(parentTypeToTest));
1059
1147
  }
@@ -1196,6 +1284,16 @@ class SelectionSetUpdates {
1196
1284
  toSelectionSet(parentType, fragments) {
1197
1285
  return makeSelectionSet(parentType, this.keyedUpdates, fragments);
1198
1286
  }
1287
+ toString() {
1288
+ return '{\n'
1289
+ + [...this.keyedUpdates.entries()].map(([k, updates]) => {
1290
+ const updStr = updates.map((upd) => upd instanceof AbstractSelection
1291
+ ? upd.toString()
1292
+ : `${upd.path} -> ${upd.selections}`);
1293
+ return ` - ${k}: ${updStr}`;
1294
+ }).join('\n')
1295
+ + '\n\}';
1296
+ }
1199
1297
  }
1200
1298
  exports.SelectionSetUpdates = SelectionSetUpdates;
1201
1299
  function addToKeyedUpdates(keyedUpdates, selections) {
@@ -1260,9 +1358,9 @@ function makeSelection(parentType, updates, fragments) {
1260
1358
  (0, utils_1.assert)(updates.length > 0, 'Should not be called without any updates');
1261
1359
  const first = updates[0];
1262
1360
  if (updates.length === 1 && first instanceof AbstractSelection) {
1263
- return first.rebaseOn(parentType, fragments);
1361
+ return first.rebaseOnOrError({ parentType, fragments });
1264
1362
  }
1265
- const element = updateElement(first).rebaseOn(parentType);
1363
+ const element = updateElement(first).rebaseOnOrError(parentType);
1266
1364
  const subSelectionParentType = element.kind === 'Field' ? element.baseType() : element.castedType();
1267
1365
  if (!(0, definitions_1.isCompositeType)(subSelectionParentType)) {
1268
1366
  return selectionOfElement(element);
@@ -1393,6 +1491,9 @@ class AbstractSelection {
1393
1491
  constructor(element) {
1394
1492
  this.element = element;
1395
1493
  }
1494
+ rebaseOnOrError({ parentType, fragments }) {
1495
+ return this.rebaseOn({ parentType, fragments, errorIfCannotRebase: true });
1496
+ }
1396
1497
  get parentType() {
1397
1498
  return this.element.parentType;
1398
1499
  }
@@ -1435,40 +1536,200 @@ class AbstractSelection {
1435
1536
  }
1436
1537
  return undefined;
1437
1538
  }
1438
- tryOptimizeSubselectionWithFragments({ parentType, subSelection, fragments, canUseFullMatchingFragment, }) {
1439
- let candidates = fragments.maybeApplyingAtType(parentType);
1539
+ intersectionWith(that) {
1540
+ if (this.selectionSet && that.selectionSet) {
1541
+ const subSelectionSetIntersection = this.selectionSet.intersectionWith(that.selectionSet);
1542
+ if (subSelectionSetIntersection.isEmpty()) {
1543
+ return undefined;
1544
+ }
1545
+ else {
1546
+ return this.withUpdatedSelectionSet(subSelectionSetIntersection);
1547
+ }
1548
+ }
1549
+ else {
1550
+ return this.us();
1551
+ }
1552
+ }
1553
+ tryOptimizeSubselectionWithFragments({ parentType, subSelection, fragments, validator, canUseFullMatchingFragment, }) {
1554
+ let candidates = fragments.maybeApplyingDirectlyAtType(parentType);
1555
+ if (candidates.length === 0) {
1556
+ return subSelection;
1557
+ }
1440
1558
  const applyingFragments = [];
1441
1559
  for (const candidate of candidates) {
1442
- const fragmentSSet = candidate.expandedSelectionSetAtType(parentType);
1443
- if (fragmentSSet.isEmpty() || (fragmentSSet.selections().length === 1 && fragmentSSet.selections()[0].isTypenameField())) {
1560
+ let atType = candidate.expandedSelectionSetAtType(parentType);
1561
+ if (atType.selectionSet.isEmpty() || (atType.selectionSet.selections().length === 1 && atType.selectionSet.selections()[0].isTypenameField())) {
1444
1562
  continue;
1445
1563
  }
1446
- const res = subSelection.contains(fragmentSSet);
1564
+ const res = subSelection.contains(atType.selectionSet, { ignoreMissingTypename: true });
1447
1565
  if (res === ContainsResult.EQUAL) {
1448
1566
  if (canUseFullMatchingFragment(candidate)) {
1567
+ if (!validator.checkCanReuseFragmentAndTrackIt(atType)) {
1568
+ continue;
1569
+ }
1449
1570
  return candidate;
1450
1571
  }
1451
1572
  if (candidate.appliedDirectives.length === 0) {
1452
- applyingFragments.push(candidate);
1573
+ applyingFragments.push({ fragment: candidate, atType });
1453
1574
  }
1454
1575
  }
1455
1576
  else if (res === ContainsResult.STRICTLY_CONTAINED && candidate.appliedDirectives.length === 0) {
1456
- applyingFragments.push(candidate);
1577
+ applyingFragments.push({ fragment: candidate, atType });
1457
1578
  }
1458
1579
  }
1459
1580
  if (applyingFragments.length === 0) {
1460
1581
  return subSelection;
1461
1582
  }
1462
- const filteredApplyingFragments = applyingFragments.filter((f) => !applyingFragments.some((o) => o.includes(f.name)));
1583
+ const filteredApplyingFragments = applyingFragments.filter(({ fragment }) => !applyingFragments.some((o) => o.fragment.includes(fragment.name)));
1463
1584
  let notCoveredByFragments = subSelection;
1464
1585
  const optimized = new SelectionSetUpdates();
1465
- for (const fragment of filteredApplyingFragments) {
1466
- notCoveredByFragments = notCoveredByFragments.minus(fragment.expandedSelectionSetAtType(parentType));
1586
+ for (const { fragment, atType } of filteredApplyingFragments) {
1587
+ if (!validator.checkCanReuseFragmentAndTrackIt(atType)) {
1588
+ continue;
1589
+ }
1590
+ const notCovered = subSelection.minus(atType.selectionSet);
1591
+ notCoveredByFragments = notCoveredByFragments.intersectionWith(notCovered);
1467
1592
  optimized.add(new FragmentSpreadSelection(parentType, fragments, fragment, []));
1468
1593
  }
1469
1594
  return optimized.add(notCoveredByFragments).toSelectionSet(parentType, fragments);
1470
1595
  }
1471
1596
  }
1597
+ class FieldsConflictMultiBranchValidator {
1598
+ constructor(validators) {
1599
+ this.validators = validators;
1600
+ }
1601
+ static ofInitial(validator) {
1602
+ return new FieldsConflictMultiBranchValidator([validator]);
1603
+ }
1604
+ forField(field) {
1605
+ const forAllBranches = this.validators.flatMap((vs) => vs.forField(field));
1606
+ (0, utils_1.assert)(forAllBranches.length > 0, `Shoud have found at least one validator for ${field}`);
1607
+ return new FieldsConflictMultiBranchValidator(forAllBranches);
1608
+ }
1609
+ checkCanReuseFragmentAndTrackIt(fragment) {
1610
+ const validator = fragment.validator;
1611
+ if (!validator) {
1612
+ return true;
1613
+ }
1614
+ if (!this.validators.every((v) => v.doMergeWith(validator))) {
1615
+ return false;
1616
+ }
1617
+ if (this.usedSpreadTrimmedPartAtLevel) {
1618
+ if (!this.usedSpreadTrimmedPartAtLevel.every((t) => validator.doMergeWith(t))) {
1619
+ return false;
1620
+ }
1621
+ }
1622
+ else {
1623
+ this.usedSpreadTrimmedPartAtLevel = [];
1624
+ }
1625
+ this.usedSpreadTrimmedPartAtLevel.push(validator);
1626
+ return true;
1627
+ }
1628
+ }
1629
+ class FieldsConflictValidator {
1630
+ constructor(byResponseName) {
1631
+ this.byResponseName = byResponseName;
1632
+ }
1633
+ static build(s) {
1634
+ return FieldsConflictValidator.forLevel(s.fieldsInSet());
1635
+ }
1636
+ static forLevel(level) {
1637
+ var _a;
1638
+ const atLevel = new Map();
1639
+ for (const { field } of level) {
1640
+ const responseName = field.element.responseName();
1641
+ let atResponseName = atLevel.get(responseName);
1642
+ if (!atResponseName) {
1643
+ atResponseName = new Map();
1644
+ atLevel.set(responseName, atResponseName);
1645
+ }
1646
+ if (field.selectionSet) {
1647
+ let forField = (_a = atResponseName.get(field.element)) !== null && _a !== void 0 ? _a : [];
1648
+ atResponseName.set(field.element, forField.concat(field.selectionSet.fieldsInSet()));
1649
+ }
1650
+ else {
1651
+ atResponseName.set(field.element, null);
1652
+ }
1653
+ }
1654
+ const byResponseName = new Map();
1655
+ for (const [name, level] of atLevel.entries()) {
1656
+ const atResponseName = new Map();
1657
+ for (const [field, collectedFields] of level) {
1658
+ const validator = collectedFields ? FieldsConflictValidator.forLevel(collectedFields) : null;
1659
+ atResponseName.set(field, validator);
1660
+ }
1661
+ byResponseName.set(name, atResponseName);
1662
+ }
1663
+ return new FieldsConflictValidator(byResponseName);
1664
+ }
1665
+ forField(field) {
1666
+ const byResponseName = this.byResponseName.get(field.responseName());
1667
+ if (!byResponseName) {
1668
+ return [];
1669
+ }
1670
+ return (0, utils_1.mapValues)(byResponseName).filter((v) => !!v);
1671
+ }
1672
+ doMergeWith(that) {
1673
+ var _a, _b;
1674
+ for (const [responseName, thisFields] of this.byResponseName.entries()) {
1675
+ const thatFields = that.byResponseName.get(responseName);
1676
+ if (!thatFields) {
1677
+ continue;
1678
+ }
1679
+ for (const [thisField, thisValidator] of thisFields.entries()) {
1680
+ for (const [thatField, thatValidator] of thatFields.entries()) {
1681
+ if (!(0, types_1.typesCanBeMerged)(thisField.definition.type, thatField.definition.type)) {
1682
+ return false;
1683
+ }
1684
+ const p1 = thisField.parentType;
1685
+ const p2 = thatField.parentType;
1686
+ if ((0, types_1.sameType)(p1, p2) || !(0, definitions_1.isObjectType)(p1) || !(0, definitions_1.isObjectType)(p2)) {
1687
+ if (thisField.name !== thatField.name
1688
+ || !(0, values_1.argumentsEquals)((_a = thisField.args) !== null && _a !== void 0 ? _a : {}, (_b = thatField.args) !== null && _b !== void 0 ? _b : {})
1689
+ || (thisValidator && thatValidator && !thisValidator.doMergeWith(thatValidator))) {
1690
+ return false;
1691
+ }
1692
+ }
1693
+ else {
1694
+ if (thisValidator && thatValidator && !thisValidator.hasSameResponseShapeThan(thatValidator)) {
1695
+ return false;
1696
+ }
1697
+ }
1698
+ }
1699
+ }
1700
+ }
1701
+ return true;
1702
+ }
1703
+ hasSameResponseShapeThan(that) {
1704
+ for (const [responseName, thisFields] of this.byResponseName.entries()) {
1705
+ const thatFields = that.byResponseName.get(responseName);
1706
+ if (!thatFields) {
1707
+ continue;
1708
+ }
1709
+ for (const [thisField, thisValidator] of thisFields.entries()) {
1710
+ for (const [thatField, thatValidator] of thatFields.entries()) {
1711
+ if (!(0, types_1.typesCanBeMerged)(thisField.definition.type, thatField.definition.type)
1712
+ || (thisValidator && thatValidator && !thisValidator.hasSameResponseShapeThan(thatValidator))) {
1713
+ return false;
1714
+ }
1715
+ }
1716
+ }
1717
+ }
1718
+ return true;
1719
+ }
1720
+ toString(indent = '') {
1721
+ return '{\n'
1722
+ + [...this.byResponseName.entries()].map(([name, byFields]) => {
1723
+ const innerIndent = indent + ' ';
1724
+ return `${innerIndent}${name}: [\n`
1725
+ + [...byFields.entries()]
1726
+ .map(([field, next]) => `${innerIndent} ${field.parentType}.${field}${next ? next.toString(innerIndent + ' ') : ''}`)
1727
+ .join('\n')
1728
+ + `\n${innerIndent}]`;
1729
+ }).join('\n')
1730
+ + `\n${indent}}`;
1731
+ }
1732
+ }
1472
1733
  class FieldSelection extends AbstractSelection {
1473
1734
  constructor(field, _selectionSet) {
1474
1735
  super(field);
@@ -1485,41 +1746,44 @@ class FieldSelection extends AbstractSelection {
1485
1746
  return this.element.definition.name === definitions_1.typenameFieldName;
1486
1747
  }
1487
1748
  withUpdatedComponents(field, selectionSet) {
1749
+ if (this.element === field && this.selectionSet === selectionSet) {
1750
+ return this;
1751
+ }
1488
1752
  return new FieldSelection(field, selectionSet);
1489
1753
  }
1490
1754
  key() {
1491
1755
  return this.element.key();
1492
1756
  }
1493
- optimize(fragments) {
1757
+ optimize(fragments, validator) {
1494
1758
  const fieldBaseType = (0, definitions_1.baseType)(this.element.definition.type);
1495
1759
  if (!(0, definitions_1.isCompositeType)(fieldBaseType) || !this.selectionSet) {
1496
1760
  return this;
1497
1761
  }
1498
- let optimizedSelection = this.selectionSet;
1499
- if ((0, definitions_1.isCompositeType)(fieldBaseType) && this.selectionSet) {
1500
- const optimized = this.tryOptimizeSubselectionWithFragments({
1501
- parentType: fieldBaseType,
1502
- subSelection: this.selectionSet,
1503
- fragments,
1504
- canUseFullMatchingFragment: (fragment) => fragment.appliedDirectives.length === 0,
1505
- });
1506
- if (optimized instanceof NamedFragmentDefinition) {
1507
- optimizedSelection = selectionSetOf(fieldBaseType, new FragmentSpreadSelection(fieldBaseType, fragments, optimized, []));
1508
- }
1509
- else {
1510
- optimizedSelection = optimized;
1511
- }
1762
+ const fieldValidator = validator.forField(this.element);
1763
+ const optimized = this.tryOptimizeSubselectionWithFragments({
1764
+ parentType: fieldBaseType,
1765
+ subSelection: this.selectionSet,
1766
+ fragments,
1767
+ validator: fieldValidator,
1768
+ canUseFullMatchingFragment: (fragment) => fragment.appliedDirectives.length === 0,
1769
+ });
1770
+ let optimizedSelection;
1771
+ if (optimized instanceof NamedFragmentDefinition) {
1772
+ optimizedSelection = selectionSetOf(fieldBaseType, new FragmentSpreadSelection(fieldBaseType, fragments, optimized, []));
1773
+ }
1774
+ else {
1775
+ optimizedSelection = optimized;
1512
1776
  }
1513
- optimizedSelection = optimizedSelection.optimize(fragments);
1777
+ optimizedSelection = optimizedSelection.optimizeSelections(fragments, fieldValidator);
1514
1778
  return this.selectionSet === optimizedSelection
1515
1779
  ? this
1516
1780
  : this.withUpdatedSelectionSet(optimizedSelection);
1517
1781
  }
1518
- filter(predicate) {
1782
+ filterRecursiveDepthFirst(predicate) {
1519
1783
  if (!this.selectionSet) {
1520
1784
  return predicate(this) ? this : undefined;
1521
1785
  }
1522
- const updatedSelectionSet = this.selectionSet.filter(predicate);
1786
+ const updatedSelectionSet = this.selectionSet.filterRecursiveDepthFirst(predicate);
1523
1787
  const thisWithFilteredSelectionSet = this.selectionSet === updatedSelectionSet
1524
1788
  ? this
1525
1789
  : new FieldSelection(this.element, updatedSelectionSet);
@@ -1531,11 +1795,14 @@ class FieldSelection extends AbstractSelection {
1531
1795
  validate(this.element.isLeafField() || (this.selectionSet && !this.selectionSet.isEmpty()), () => `Invalid empty selection set for field "${this.element.definition.coordinate}" of non-leaf type ${this.element.definition.type}`, this.element.definition.sourceAST);
1532
1796
  (_a = this.selectionSet) === null || _a === void 0 ? void 0 : _a.validate(variableDefinitions);
1533
1797
  }
1534
- rebaseOn(parentType, fragments) {
1798
+ rebaseOn({ parentType, fragments, errorIfCannotRebase, }) {
1535
1799
  if (this.element.parentType === parentType) {
1536
1800
  return this;
1537
1801
  }
1538
- const rebasedElement = this.element.rebaseOn(parentType);
1802
+ const rebasedElement = this.element.rebaseOn({ parentType, errorIfCannotRebase });
1803
+ if (!rebasedElement) {
1804
+ return undefined;
1805
+ }
1539
1806
  if (!this.selectionSet) {
1540
1807
  return this.withUpdatedElement(rebasedElement);
1541
1808
  }
@@ -1544,7 +1811,8 @@ class FieldSelection extends AbstractSelection {
1544
1811
  return this.withUpdatedElement(rebasedElement);
1545
1812
  }
1546
1813
  validate((0, definitions_1.isCompositeType)(rebasedBase), () => `Cannot rebase field selection ${this} on ${parentType}: rebased field base return type ${rebasedBase} is not composite`);
1547
- return this.withUpdatedComponents(rebasedElement, this.selectionSet.rebaseOn(rebasedBase, fragments));
1814
+ const rebasedSelectionSet = this.selectionSet.rebaseOn({ parentType: rebasedBase, fragments, errorIfCannotRebase });
1815
+ return rebasedSelectionSet.isEmpty() ? undefined : this.withUpdatedComponents(rebasedElement, rebasedSelectionSet);
1548
1816
  }
1549
1817
  canAddTo(parentType) {
1550
1818
  if (this.element.parentType === parentType) {
@@ -1586,19 +1854,23 @@ class FieldSelection extends AbstractSelection {
1586
1854
  var _a;
1587
1855
  return !!((_a = this.selectionSet) === null || _a === void 0 ? void 0 : _a.hasDefer());
1588
1856
  }
1589
- trimUnsatisfiableBranches(_, options) {
1590
- var _a, _b;
1857
+ normalize({ parentType, recursive }) {
1858
+ const definition = parentType === this.parentType
1859
+ ? this.element.definition
1860
+ : parentType.field(this.element.name);
1861
+ (0, utils_1.assert)(definition, `Cannot normalize ${this.element} at ${parentType} which does not have that field`);
1862
+ const element = this.element.definition === definition ? this.element : this.element.withUpdatedDefinition(definition);
1591
1863
  if (!this.selectionSet) {
1592
- return this;
1864
+ return this.withUpdatedElement(element);
1593
1865
  }
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 })])));
1866
+ const base = element.baseType();
1867
+ (0, utils_1.assert)((0, definitions_1.isCompositeType)(base), () => `Field ${element} should not have a sub-selection`);
1868
+ const normalizedSubSelection = (recursive !== null && recursive !== void 0 ? recursive : true) ? this.selectionSet.normalize({ parentType: base }) : this.selectionSet;
1869
+ if (normalizedSubSelection === null || normalizedSubSelection === void 0 ? void 0 : normalizedSubSelection.isEmpty()) {
1870
+ return this.withUpdatedComponents(element, selectionSetOfElement(new Field(base.typenameField(), undefined, [new definitions_1.Directive('include', { 'if': false })])));
1599
1871
  }
1600
1872
  else {
1601
- return trimmed;
1873
+ return this.withUpdatedComponents(element, normalizedSubSelection);
1602
1874
  }
1603
1875
  }
1604
1876
  expandFragments(updatedFragments) {
@@ -1616,7 +1888,7 @@ class FieldSelection extends AbstractSelection {
1616
1888
  }
1617
1889
  return !!that.selectionSet && this.selectionSet.equals(that.selectionSet);
1618
1890
  }
1619
- contains(that) {
1891
+ contains(that, options) {
1620
1892
  if (!(that instanceof FieldSelection) || !this.element.equals(that.element)) {
1621
1893
  return ContainsResult.NOT_CONTAINED;
1622
1894
  }
@@ -1625,7 +1897,7 @@ class FieldSelection extends AbstractSelection {
1625
1897
  return ContainsResult.EQUAL;
1626
1898
  }
1627
1899
  (0, utils_1.assert)(that.selectionSet, '`this` and `that` have the same element, so if one has sub-selection, the other one should too');
1628
- return this.selectionSet.contains(that.selectionSet);
1900
+ return this.selectionSet.contains(that.selectionSet, options);
1629
1901
  }
1630
1902
  toString(expandFragments = true, indent) {
1631
1903
  return (indent !== null && indent !== void 0 ? indent : '') + this.element + (this.selectionSet ? ' ' + this.selectionSet.toString(expandFragments, true, indent) : '');
@@ -1647,10 +1919,9 @@ class FragmentSelection extends AbstractSelection {
1647
1919
  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
1920
  }
1649
1921
  }
1650
- filter(predicate) {
1651
- const selectionSet = this.selectionSet;
1652
- const updatedSelectionSet = selectionSet.filter(predicate);
1653
- const thisWithFilteredSelectionSet = updatedSelectionSet === selectionSet
1922
+ filterRecursiveDepthFirst(predicate) {
1923
+ const updatedSelectionSet = this.selectionSet.filterRecursiveDepthFirst(predicate);
1924
+ const thisWithFilteredSelectionSet = updatedSelectionSet === this.selectionSet
1654
1925
  ? this
1655
1926
  : new InlineFragmentSelection(this.element, updatedSelectionSet);
1656
1927
  return predicate(thisWithFilteredSelectionSet) ? thisWithFilteredSelectionSet : undefined;
@@ -1658,6 +1929,17 @@ class FragmentSelection extends AbstractSelection {
1658
1929
  hasDefer() {
1659
1930
  return this.element.hasDefer() || this.selectionSet.hasDefer();
1660
1931
  }
1932
+ normalize({ parentType, recursive }) {
1933
+ const thisCondition = this.element.typeCondition;
1934
+ if (thisCondition && parentType !== this.parentType) {
1935
+ const conditionRuntimes = (0, definitions_1.possibleRuntimeTypes)(thisCondition);
1936
+ const typeRuntimes = (0, definitions_1.possibleRuntimeTypes)(parentType);
1937
+ if (!conditionRuntimes.some((t) => typeRuntimes.includes(t))) {
1938
+ return undefined;
1939
+ }
1940
+ }
1941
+ return this.normalizeKnowingItIntersects({ parentType, recursive });
1942
+ }
1661
1943
  }
1662
1944
  exports.FragmentSelection = FragmentSelection;
1663
1945
  class InlineFragmentSelection extends FragmentSelection {
@@ -1672,6 +1954,9 @@ class InlineFragmentSelection extends FragmentSelection {
1672
1954
  return this.element.key();
1673
1955
  }
1674
1956
  withUpdatedComponents(fragment, selectionSet) {
1957
+ if (fragment === this.element && selectionSet === this.selectionSet) {
1958
+ return this;
1959
+ }
1675
1960
  return new InlineFragmentSelection(fragment, selectionSet);
1676
1961
  }
1677
1962
  validate(variableDefinitions) {
@@ -1679,16 +1964,20 @@ class InlineFragmentSelection extends FragmentSelection {
1679
1964
  validate(!this.selectionSet.isEmpty(), () => `Invalid empty selection set for fragment "${this.element}"`);
1680
1965
  this.selectionSet.validate(variableDefinitions);
1681
1966
  }
1682
- rebaseOn(parentType, fragments) {
1967
+ rebaseOn({ parentType, fragments, errorIfCannotRebase, }) {
1683
1968
  if (this.parentType === parentType) {
1684
1969
  return this;
1685
1970
  }
1686
- const rebasedFragment = this.element.rebaseOn(parentType);
1971
+ const rebasedFragment = this.element.rebaseOn({ parentType, errorIfCannotRebase });
1972
+ if (!rebasedFragment) {
1973
+ return undefined;
1974
+ }
1687
1975
  const rebasedCastedType = rebasedFragment.castedType();
1688
1976
  if (rebasedCastedType === this.selectionSet.parentType) {
1689
1977
  return this.withUpdatedElement(rebasedFragment);
1690
1978
  }
1691
- return this.withUpdatedComponents(rebasedFragment, this.selectionSet.rebaseOn(rebasedCastedType, fragments));
1979
+ const rebasedSelectionSet = this.selectionSet.rebaseOn({ parentType: rebasedCastedType, fragments, errorIfCannotRebase });
1980
+ return rebasedSelectionSet.isEmpty() ? undefined : this.withUpdatedComponents(rebasedFragment, rebasedSelectionSet);
1692
1981
  }
1693
1982
  canAddTo(parentType) {
1694
1983
  if (this.element.parentType === parentType) {
@@ -1720,7 +2009,7 @@ class InlineFragmentSelection extends FragmentSelection {
1720
2009
  selectionSet: this.selectionSet.toSelectionSetNode()
1721
2010
  };
1722
2011
  }
1723
- optimize(fragments) {
2012
+ optimize(fragments, validator) {
1724
2013
  let optimizedSelection = this.selectionSet;
1725
2014
  const typeCondition = this.element.typeCondition;
1726
2015
  if (typeCondition) {
@@ -1728,6 +2017,7 @@ class InlineFragmentSelection extends FragmentSelection {
1728
2017
  parentType: typeCondition,
1729
2018
  subSelection: optimizedSelection,
1730
2019
  fragments,
2020
+ validator,
1731
2021
  canUseFullMatchingFragment: (fragment) => {
1732
2022
  return fragment.appliedDirectives.length === 0
1733
2023
  || ((0, types_1.sameType)(typeCondition, fragment.typeCondition)
@@ -1750,7 +2040,7 @@ class InlineFragmentSelection extends FragmentSelection {
1750
2040
  optimizedSelection = optimized;
1751
2041
  }
1752
2042
  }
1753
- optimizedSelection = optimizedSelection.optimizeSelections(fragments);
2043
+ optimizedSelection = optimizedSelection.optimizeSelections(fragments, validator);
1754
2044
  return this.selectionSet === optimizedSelection
1755
2045
  ? this
1756
2046
  : new InlineFragmentSelection(this.element, optimizedSelection);
@@ -1778,42 +2068,35 @@ class InlineFragmentSelection extends FragmentSelection {
1778
2068
  ? this
1779
2069
  : this.withUpdatedComponents(newElement, newSelection);
1780
2070
  }
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;
2071
+ normalizeKnowingItIntersects({ parentType, recursive }) {
2072
+ var _a;
1784
2073
  const thisCondition = this.element.typeCondition;
1785
2074
  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;
2075
+ if (!thisCondition || parentType === this.element.typeCondition || (0, definitions_1.isObjectType)(parentType)) {
2076
+ const normalized = this.selectionSet.normalize({ parentType, recursive });
2077
+ return normalized.isEmpty() ? undefined : normalized;
1789
2078
  }
1790
- if ((0, definitions_1.isObjectType)(currentType)) {
1791
- if ((0, definitions_1.isObjectType)(thisCondition) || !(0, definitions_1.possibleRuntimeTypes)(thisCondition).includes(currentType)) {
2079
+ }
2080
+ let normalizedSelectionSet;
2081
+ if (recursive !== null && recursive !== void 0 ? recursive : true) {
2082
+ normalizedSelectionSet = this.selectionSet.normalize({ parentType: thisCondition !== null && thisCondition !== void 0 ? thisCondition : parentType });
2083
+ if (normalizedSelectionSet.isEmpty()) {
2084
+ if (this.element.appliedDirectives.length === 0) {
1792
2085
  return undefined;
1793
2086
  }
1794
2087
  else {
1795
- const trimmed = this.selectionSet.trimUnsatisfiableBranches(currentType, options);
1796
- return trimmed.isEmpty() ? undefined : trimmed;
2088
+ return this.withUpdatedComponents(this.element.rebaseOnOrError(parentType), selectionSetOfElement(new Field(((_a = this.element.typeCondition) !== null && _a !== void 0 ? _a : parentType).typenameField(), undefined, [new definitions_1.Directive('include', { 'if': false })])));
1797
2089
  }
1798
2090
  }
1799
2091
  }
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
- }
2092
+ else {
2093
+ normalizedSelectionSet = this.selectionSet;
1811
2094
  }
1812
2095
  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);
2096
+ (0, utils_1.assert)(!(0, definitions_1.isObjectType)(parentType), () => `Should not have got here if ${parentType} is an object type`);
2097
+ const currentRuntimes = (0, definitions_1.possibleRuntimeTypes)(parentType);
1815
2098
  const liftableSelections = [];
1816
- for (const selection of trimmedSelectionSet.selections()) {
2099
+ for (const selection of normalizedSelectionSet.selections()) {
1817
2100
  if (selection.kind === 'FragmentSelection'
1818
2101
  && selection.element.typeCondition
1819
2102
  && (0, definitions_1.isObjectType)(selection.element.typeCondition)
@@ -1821,17 +2104,19 @@ class InlineFragmentSelection extends FragmentSelection {
1821
2104
  liftableSelections.push(selection);
1822
2105
  }
1823
2106
  }
1824
- if (liftableSelections.length === trimmedSelectionSet.selections().length) {
1825
- return trimmedSelectionSet;
2107
+ if (liftableSelections.length === normalizedSelectionSet.selections().length) {
2108
+ return normalizedSelectionSet;
1826
2109
  }
1827
2110
  if (liftableSelections.length > 0) {
1828
2111
  const newSet = new SelectionSetUpdates();
1829
2112
  newSet.add(liftableSelections);
1830
- newSet.add(this.withUpdatedSelectionSet(trimmedSelectionSet.filter((s) => !liftableSelections.includes(s))));
1831
- return newSet.toSelectionSet(this.parentType);
2113
+ newSet.add(this.withUpdatedSelectionSet(normalizedSelectionSet.filter((s) => !liftableSelections.includes(s))));
2114
+ return newSet.toSelectionSet(parentType);
1832
2115
  }
1833
2116
  }
1834
- return this.selectionSet === trimmedSelectionSet ? this : this.withUpdatedSelectionSet(trimmedSelectionSet);
2117
+ return this.parentType === parentType && this.selectionSet === normalizedSelectionSet
2118
+ ? this
2119
+ : this.withUpdatedComponents(this.element.rebaseOnOrError(parentType), normalizedSelectionSet);
1835
2120
  }
1836
2121
  expandFragments(updatedFragments) {
1837
2122
  return this.mapToSelectionSet((s) => s.expandFragments(updatedFragments));
@@ -1844,11 +2129,11 @@ class InlineFragmentSelection extends FragmentSelection {
1844
2129
  && this.element.equals(that.element)
1845
2130
  && this.selectionSet.equals(that.selectionSet);
1846
2131
  }
1847
- contains(that) {
2132
+ contains(that, options) {
1848
2133
  if (!(that instanceof FragmentSelection) || !this.element.equals(that.element)) {
1849
2134
  return ContainsResult.NOT_CONTAINED;
1850
2135
  }
1851
- return this.selectionSet.contains(that.selectionSet);
2136
+ return this.selectionSet.contains(that.selectionSet, options);
1852
2137
  }
1853
2138
  toString(expandFragments = true, indent) {
1854
2139
  return (indent !== null && indent !== void 0 ? indent : '') + this.element + ' ' + this.selectionSet.toString(expandFragments, true, indent);
@@ -1876,12 +2161,13 @@ class FragmentSpreadSelection extends FragmentSelection {
1876
2161
  withUpdatedComponents(_fragment, _selectionSet) {
1877
2162
  (0, utils_1.assert)(false, `Unsupported`);
1878
2163
  }
1879
- trimUnsatisfiableBranches(parentType) {
1880
- (0, utils_1.assert)(parentType.schema() === this.parentType.schema(), 'Should not try to trim using a type from another schema');
1881
- return this.rebaseOn(parentType, this.fragments);
2164
+ normalizeKnowingItIntersects({ parentType }) {
2165
+ (0, utils_1.assert)(parentType.schema() === this.parentType.schema(), 'Should not try to normalize using a type from another schema');
2166
+ return this.rebaseOnOrError({ parentType, fragments: this.fragments });
1882
2167
  }
1883
2168
  validate() {
1884
2169
  this.validateDeferAndStream();
2170
+ 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
2171
  }
1886
2172
  toSelectionNode() {
1887
2173
  const directiveNodes = this.spreadDirectives.length === 0
@@ -1902,17 +2188,20 @@ class FragmentSpreadSelection extends FragmentSelection {
1902
2188
  directives: directiveNodes,
1903
2189
  };
1904
2190
  }
1905
- optimize(_) {
2191
+ optimize(_1, _2) {
1906
2192
  return this;
1907
2193
  }
1908
- rebaseOn(parentType, fragments) {
2194
+ rebaseOn({ parentType, fragments, errorIfCannotRebase, }) {
1909
2195
  if (this.parentType === parentType) {
1910
2196
  return this;
1911
2197
  }
1912
2198
  (0, utils_1.assert)(fragments || this.parentType.schema() === parentType.schema(), `Must provide fragments is rebasing on other schema`);
1913
2199
  const newFragments = fragments !== null && fragments !== void 0 ? fragments : this.fragments;
1914
2200
  const namedFragment = newFragments.get(this.namedFragment.name);
1915
- (0, utils_1.assert)(namedFragment, () => `Cannot rebase ${this} if it isn't part of the provided fragments`);
2201
+ if (!namedFragment) {
2202
+ validate(!errorIfCannotRebase, () => `Cannot rebase ${this.toString(false)} if it isn't part of the provided fragments`);
2203
+ return undefined;
2204
+ }
1916
2205
  return new FragmentSpreadSelection(parentType, newFragments, namedFragment, this.spreadDirectives);
1917
2206
  }
1918
2207
  canAddTo(_) {
@@ -1949,14 +2238,14 @@ class FragmentSpreadSelection extends FragmentSelection {
1949
2238
  && this.namedFragment.name === that.namedFragment.name
1950
2239
  && (0, definitions_1.sameDirectiveApplications)(this.spreadDirectives, that.spreadDirectives);
1951
2240
  }
1952
- contains(that) {
2241
+ contains(that, options) {
1953
2242
  if (this.equals(that)) {
1954
2243
  return ContainsResult.EQUAL;
1955
2244
  }
1956
2245
  if (!(that instanceof FragmentSelection) || !this.element.equals(that.element)) {
1957
2246
  return ContainsResult.NOT_CONTAINED;
1958
2247
  }
1959
- return this.selectionSet.contains(that.selectionSet);
2248
+ return this.selectionSet.contains(that.selectionSet, options);
1960
2249
  }
1961
2250
  toString(expandFragments = true, indent) {
1962
2251
  if (expandFragments) {