@apollo/federation-internals 2.4.6 → 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.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.operationToDocument = exports.parseSelectionSet = exports.parseOperation = exports.operationFromDocument = exports.FragmentSelection = exports.FieldSelection = exports.selectionOfElement = exports.selectionSetOfElement = exports.selectionSetOf = exports.allFieldDefinitionsInSelectionSet = exports.MutableSelectionSet = exports.SelectionSetUpdates = exports.SelectionSet = exports.NamedFragments = exports.NamedFragmentDefinition = exports.Operation = exports.concatOperationPaths = exports.conditionalDirectivesInOperationPath = exports.sameOperationPaths = exports.operationPathToStringPath = exports.FragmentElement = exports.Field = void 0;
3
+ exports.operationToDocument = exports.parseSelectionSet = exports.parseOperation = exports.operationFromDocument = exports.FragmentSelection = exports.FieldSelection = exports.selectionOfElement = exports.selectionSetOfElement = exports.selectionSetOf = exports.allFieldDefinitionsInSelectionSet = exports.MutableSelectionSet = exports.SelectionSetUpdates = exports.SelectionSet = exports.ContainsResult = exports.NamedFragments = exports.NamedFragmentDefinition = exports.Operation = exports.concatOperationPaths = exports.conditionalDirectivesInOperationPath = exports.sameOperationPaths = exports.operationPathToStringPath = exports.FragmentElement = exports.Field = void 0;
4
4
  const graphql_1 = require("graphql");
5
5
  const definitions_1 = require("./definitions");
6
6
  const federation_1 = require("./federation");
@@ -442,16 +442,104 @@ function isUselessFollowupElement(first, followup, conditionals) {
442
442
  && (followup.appliedDirectives.length === 0 || (0, definitions_1.isDirectiveApplicationsSubset)(conditionals, followup.appliedDirectives))
443
443
  && (0, types_1.isSubtype)(followup.typeCondition, typeOfFirst);
444
444
  }
445
+ function computeFragmentsDependents(fragments) {
446
+ const reverseDeps = new utils_1.SetMultiMap();
447
+ for (const fragment of fragments.definitions()) {
448
+ for (const dependency of fragment.fragmentUsages().keys()) {
449
+ reverseDeps.add(dependency, fragment.name);
450
+ }
451
+ }
452
+ return reverseDeps;
453
+ }
454
+ function clearKeptFragments(usages, fragments, minUsagesToOptimize) {
455
+ let toCheck = Array.from(usages.entries()).filter(([_, count]) => count >= minUsagesToOptimize).map(([name, _]) => name);
456
+ while (toCheck.length > 0) {
457
+ const newToCheck = [];
458
+ for (const name of toCheck) {
459
+ usages.delete(name);
460
+ const ownUsages = fragments.get(name).fragmentUsages();
461
+ for (const [otherName, otherCount] of ownUsages.entries()) {
462
+ const prevCount = usages.get(otherName);
463
+ if (prevCount !== undefined) {
464
+ const newCount = prevCount + otherCount;
465
+ usages.set(otherName, newCount);
466
+ if (prevCount < minUsagesToOptimize && newCount >= minUsagesToOptimize) {
467
+ newToCheck.push(otherName);
468
+ }
469
+ }
470
+ }
471
+ }
472
+ toCheck = newToCheck;
473
+ }
474
+ }
475
+ function computeFragmentsToKeep(selectionSet, fragments, minUsagesToOptimize) {
476
+ const usages = new Map();
477
+ selectionSet.collectUsedFragmentNames(usages);
478
+ if (usages.size === 0) {
479
+ return null;
480
+ }
481
+ for (const fragment of fragments.definitions()) {
482
+ if (usages.get(fragment.name) === undefined) {
483
+ usages.set(fragment.name, 0);
484
+ }
485
+ }
486
+ const reverseDependencies = computeFragmentsDependents(fragments);
487
+ const toExpand = new Set;
488
+ let shouldContinue = true;
489
+ while (shouldContinue) {
490
+ shouldContinue = false;
491
+ clearKeptFragments(usages, fragments, minUsagesToOptimize);
492
+ for (const name of (0, utils_1.mapKeys)(usages)) {
493
+ const count = usages.get(name);
494
+ if (count === 0) {
495
+ continue;
496
+ }
497
+ if (count >= minUsagesToOptimize) {
498
+ shouldContinue = true;
499
+ break;
500
+ }
501
+ const fragmentsUsingName = reverseDependencies.get(name);
502
+ if (!fragmentsUsingName || [...fragmentsUsingName].every((fragName) => toExpand.has(fragName) || !usages.get(fragName))) {
503
+ toExpand.add(name);
504
+ usages.delete(name);
505
+ shouldContinue = true;
506
+ const nameUsages = fragments.get(name).fragmentUsages();
507
+ for (const [otherName, otherCount] of nameUsages.entries()) {
508
+ const prev = usages.get(otherName);
509
+ if (prev !== undefined) {
510
+ usages.set(otherName, prev + count * otherCount);
511
+ }
512
+ }
513
+ }
514
+ }
515
+ }
516
+ for (const name of usages.keys()) {
517
+ toExpand.add(name);
518
+ }
519
+ return toExpand.size === 0 ? fragments : fragments.filter((f) => !toExpand.has(f.name));
520
+ }
445
521
  class Operation {
446
- constructor(schema, rootKind, selectionSet, variableDefinitions, name) {
522
+ constructor(schema, rootKind, selectionSet, variableDefinitions, fragments, name) {
447
523
  this.schema = schema;
448
524
  this.rootKind = rootKind;
449
525
  this.selectionSet = selectionSet;
450
526
  this.variableDefinitions = variableDefinitions;
527
+ this.fragments = fragments;
451
528
  this.name = name;
452
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
+ }
453
542
  optimize(fragments, minUsagesToOptimize = 2) {
454
- var _a;
455
543
  (0, utils_1.assert)(minUsagesToOptimize >= 1, `Expected 'minUsagesToOptimize' to be at least 1, but got ${minUsagesToOptimize}`);
456
544
  if (!fragments || fragments.isEmpty()) {
457
545
  return this;
@@ -460,50 +548,34 @@ class Operation {
460
548
  if (optimizedSelection === this.selectionSet) {
461
549
  return this;
462
550
  }
463
- const usages = new Map();
464
- optimizedSelection.collectUsedFragmentNames(usages);
465
- for (const fragment of fragments.names()) {
466
- if (!usages.has(fragment)) {
467
- usages.set(fragment, 0);
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; });
468
559
  }
469
560
  }
470
- const toDeoptimize = (0, utils_1.mapEntries)(usages).filter(([_, count]) => count < minUsagesToOptimize).map(([name]) => name);
471
- if (toDeoptimize.length > 0) {
472
- const newFragments = (_a = optimizedSelection.fragments) === null || _a === void 0 ? void 0 : _a.without(toDeoptimize);
473
- optimizedSelection = optimizedSelection.expandFragments(toDeoptimize, newFragments);
474
- optimizedSelection = optimizedSelection.trimUnsatisfiableBranches(optimizedSelection.parentType);
475
- }
476
- return new Operation(this.schema, this.rootKind, optimizedSelection, this.variableDefinitions, this.name);
561
+ return this.withUpdatedSelectionSetAndFragments(optimizedSelection, finalFragments !== null && finalFragments !== void 0 ? finalFragments : undefined);
477
562
  }
478
563
  expandAllFragments() {
479
- const expandedSelections = this.selectionSet.expandAllFragments();
480
- if (expandedSelections === this.selectionSet) {
481
- return this;
482
- }
483
- return new Operation(this.schema, this.rootKind, expandedSelections, this.variableDefinitions, this.name);
564
+ const expanded = this.selectionSet.expandFragments();
565
+ return this.withUpdatedSelectionSetAndFragments(expanded.normalize({ parentType: expanded.parentType }), undefined);
484
566
  }
485
- trimUnsatisfiableBranches() {
486
- const trimmedSelections = this.selectionSet.trimUnsatisfiableBranches(this.selectionSet.parentType);
487
- if (trimmedSelections === this.selectionSet) {
488
- return this;
489
- }
490
- return new Operation(this.schema, this.rootKind, trimmedSelections, this.variableDefinitions, this.name);
567
+ normalize() {
568
+ return this.withUpdatedSelectionSet(this.selectionSet.normalize({ parentType: this.selectionSet.parentType }));
491
569
  }
492
570
  withoutDefer(labelsToRemove) {
493
- (0, utils_1.assert)(!this.selectionSet.fragments || this.selectionSet.fragments.isEmpty(), 'Removing @defer currently only work on "expanded" selections (no named fragments)');
494
- const updated = this.selectionSet.withoutDefer(labelsToRemove);
495
- return updated == this.selectionSet
496
- ? this
497
- : new Operation(this.schema, this.rootKind, updated, this.variableDefinitions, this.name);
571
+ return this.withUpdatedSelectionSet(this.selectionSet.withoutDefer(labelsToRemove));
498
572
  }
499
573
  withNormalizedDefer() {
500
- (0, utils_1.assert)(!this.selectionSet.fragments || this.selectionSet.fragments.isEmpty(), 'Assigning @defer lables currently only work on "expanded" selections (no named fragments)');
501
574
  const normalizer = new DeferNormalizer();
502
575
  const { hasDefers, hasNonLabelledOrConditionalDefers } = normalizer.init(this.selectionSet);
503
576
  let updatedOperation = this;
504
577
  if (hasNonLabelledOrConditionalDefers) {
505
- const updated = this.selectionSet.withNormalizedDefer(normalizer);
506
- updatedOperation = new Operation(this.schema, this.rootKind, updated, this.variableDefinitions, this.name);
578
+ updatedOperation = this.withUpdatedSelectionSet(this.selectionSet.withNormalizedDefer(normalizer));
507
579
  }
508
580
  return {
509
581
  operation: updatedOperation,
@@ -522,7 +594,7 @@ class Operation {
522
594
  return defaultedVariableValues;
523
595
  }
524
596
  toString(expandFragments = false, prettyPrint = true) {
525
- return this.selectionSet.toOperationString(this.rootKind, this.variableDefinitions, this.name, expandFragments, prettyPrint);
597
+ return this.selectionSet.toOperationString(this.rootKind, this.variableDefinitions, this.fragments, this.name, expandFragments, prettyPrint);
526
598
  }
527
599
  }
528
600
  exports.Operation = Operation;
@@ -531,7 +603,7 @@ class NamedFragmentDefinition extends definitions_1.DirectiveTargetElement {
531
603
  super(schema, directives);
532
604
  this.name = name;
533
605
  this.typeCondition = typeCondition;
534
- this.selectionSetsAtTypesCache = new Map();
606
+ this.expandedSelectionSetsAtTypesCache = new Map();
535
607
  }
536
608
  setSelectionSet(selectionSet) {
537
609
  (0, utils_1.assert)(!this._selectionSet, 'Attempting to set the selection set of a fragment definition already built');
@@ -543,11 +615,28 @@ class NamedFragmentDefinition extends definitions_1.DirectiveTargetElement {
543
615
  (0, utils_1.assert)(this._selectionSet, () => `Trying to access fragment definition ${this.name} before it is fully built`);
544
616
  return this._selectionSet;
545
617
  }
618
+ expandedSelectionSet() {
619
+ if (!this._expandedSelectionSet) {
620
+ this._expandedSelectionSet = this.selectionSet.expandFragments().normalize({ parentType: this.typeCondition });
621
+ }
622
+ return this._expandedSelectionSet;
623
+ }
546
624
  withUpdatedSelectionSet(newSelectionSet) {
547
625
  return new NamedFragmentDefinition(this.schema(), this.name, this.typeCondition).setSelectionSet(newSelectionSet);
548
626
  }
627
+ fragmentUsages() {
628
+ if (!this._fragmentUsages) {
629
+ this._fragmentUsages = new Map();
630
+ this.selectionSet.collectUsedFragmentNames(this._fragmentUsages);
631
+ }
632
+ return this._fragmentUsages;
633
+ }
549
634
  collectUsedFragmentNames(collector) {
550
- this.selectionSet.collectUsedFragmentNames(collector);
635
+ const usages = this.fragmentUsages();
636
+ for (const [name, count] of usages.entries()) {
637
+ const prevCount = collector.get(name);
638
+ collector.set(name, prevCount ? prevCount + count : count);
639
+ }
551
640
  }
552
641
  toFragmentDefinitionNode() {
553
642
  return {
@@ -566,22 +655,56 @@ class NamedFragmentDefinition extends definitions_1.DirectiveTargetElement {
566
655
  selectionSet: this.selectionSet.toSelectionSetNode()
567
656
  };
568
657
  }
569
- canApplyAtType(type) {
570
- return (0, types_1.sameType)(type, this.typeCondition) || (0, definitions_1.runtimeTypesIntersects)(type, this.typeCondition);
658
+ canApplyDirectlyAtType(type) {
659
+ if ((0, types_1.sameType)(type, this.typeCondition)) {
660
+ return true;
661
+ }
662
+ if (!(0, definitions_1.isAbstractType)(this.typeCondition)) {
663
+ return false;
664
+ }
665
+ const conditionRuntimes = (0, definitions_1.possibleRuntimeTypes)(this.typeCondition);
666
+ const typeRuntimes = (0, definitions_1.possibleRuntimeTypes)(type);
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);
571
672
  }
572
- selectionSetAtType(type) {
673
+ expandedSelectionSetAtType(type) {
573
674
  if ((0, types_1.sameType)(type, this.typeCondition) || (0, definitions_1.isObjectType)(this.typeCondition)) {
574
- return this.selectionSet;
675
+ return { selectionSet: this.expandedSelectionSet() };
575
676
  }
576
- if (!(0, definitions_1.isObjectType)(type)) {
577
- return this.selectionSet;
677
+ let cached = this.expandedSelectionSetsAtTypesCache.get(type.name);
678
+ if (!cached) {
679
+ cached = this.computeExpandedSelectionSetAtType(type);
680
+ this.expandedSelectionSetsAtTypesCache.set(type.name, cached);
578
681
  }
579
- let selectionSet = this.selectionSetsAtTypesCache.get(type.name);
580
- if (!selectionSet) {
581
- selectionSet = this.selectionSet.trimUnsatisfiableBranches(type, { recursive: false });
582
- this.selectionSetsAtTypesCache.set(type.name, selectionSet);
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 };
690
+ }
691
+ includes(otherFragment) {
692
+ if (this.name === otherFragment) {
693
+ return false;
583
694
  }
584
- return selectionSet;
695
+ if (!this._includedFragmentNames) {
696
+ this._includedFragmentNames = this.computeIncludedFragmentNames();
697
+ }
698
+ return this._includedFragmentNames.has(otherFragment);
699
+ }
700
+ computeIncludedFragmentNames() {
701
+ const included = new Set();
702
+ for (const selection of this.selectionSet.selections()) {
703
+ if (selection instanceof FragmentSpreadSelection) {
704
+ included.add(selection.namedFragment.name);
705
+ }
706
+ }
707
+ return included;
585
708
  }
586
709
  toString(indent) {
587
710
  return (indent !== null && indent !== void 0 ? indent : '') + `fragment ${this.name} on ${this.typeCondition}${this.appliedDirectivesToString()} ${this.selectionSet.toString(false, true, indent)}`;
@@ -593,7 +716,10 @@ class NamedFragments {
593
716
  this.fragments = new utils_1.MapWithCachedArrays();
594
717
  }
595
718
  isEmpty() {
596
- return this.fragments.size === 0;
719
+ return this.size === 0;
720
+ }
721
+ get size() {
722
+ return this.fragments.size;
597
723
  }
598
724
  names() {
599
725
  return this.fragments.keys();
@@ -609,24 +735,8 @@ class NamedFragments {
609
735
  this.fragments.set(fragment.name, fragment);
610
736
  }
611
737
  }
612
- maybeApplyingAtType(type) {
613
- return this.fragments.values().filter(f => f.canApplyAtType(type));
614
- }
615
- without(names) {
616
- if (!names.some(n => this.fragments.has(n))) {
617
- return this;
618
- }
619
- const newFragments = new NamedFragments();
620
- for (const fragment of this.fragments.values()) {
621
- if (!names.includes(fragment.name)) {
622
- const updatedSelectionSet = fragment.selectionSet.expandFragments(names, newFragments);
623
- const newFragment = updatedSelectionSet === fragment.selectionSet
624
- ? fragment
625
- : fragment.withUpdatedSelectionSet(updatedSelectionSet);
626
- newFragments.add(newFragment);
627
- }
628
- }
629
- return newFragments.isEmpty() ? undefined : newFragments;
738
+ maybeApplyingDirectlyAtType(type) {
739
+ return this.fragments.values().filter(f => f.canApplyDirectlyAtType(type));
630
740
  }
631
741
  get(name) {
632
742
  return this.fragments.get(name);
@@ -644,45 +754,70 @@ class NamedFragments {
644
754
  }
645
755
  return mapped;
646
756
  }
647
- mapToExpandedSelectionSets(mapper, recreateFct = (f, s) => f.withUpdatedSelectionSet(s)) {
757
+ mapInDependencyOrder(mapper) {
648
758
  const fragmentsMap = new Map();
649
- const removedFragments = new Set();
650
759
  for (const fragment of this.definitions()) {
651
- const mappedSelectionSet = mapper(fragment.selectionSet.expandAllFragments().trimUnsatisfiableBranches(fragment.typeCondition));
652
- if (!mappedSelectionSet) {
653
- removedFragments.add(fragment.name);
654
- continue;
655
- }
656
- const otherFragmentsUsages = new Map();
657
- fragment.collectUsedFragmentNames(otherFragmentsUsages);
658
760
  fragmentsMap.set(fragment.name, {
659
- original: fragment,
660
- mappedSelectionSet,
661
- dependsOn: Array.from(otherFragmentsUsages.keys()),
761
+ fragment,
762
+ dependsOn: Array.from(fragment.fragmentUsages().keys()),
662
763
  });
663
764
  }
765
+ const removedFragments = new Set();
664
766
  const mappedFragments = new NamedFragments();
665
767
  while (fragmentsMap.size > 0) {
666
768
  for (const [name, info] of fragmentsMap) {
667
769
  if (info.dependsOn.every((n) => mappedFragments.has(n) || removedFragments.has(n))) {
668
- const reoptimizedSelectionSet = info.mappedSelectionSet.optimize(mappedFragments);
669
- mappedFragments.add(recreateFct(info.original, reoptimizedSelectionSet));
770
+ const mapped = mapper(info.fragment, mappedFragments);
670
771
  fragmentsMap.delete(name);
772
+ if (!mapped) {
773
+ removedFragments.add(name);
774
+ }
775
+ else {
776
+ mappedFragments.add(mapped);
777
+ }
778
+ break;
671
779
  }
672
780
  }
673
781
  }
674
782
  return mappedFragments.isEmpty() ? undefined : mappedFragments;
675
783
  }
784
+ mapToExpandedSelectionSets(mapper) {
785
+ return this.mapInDependencyOrder((fragment, newFragments) => {
786
+ const mappedSelectionSet = mapper(fragment.selectionSet.expandFragments().normalize({ parentType: fragment.typeCondition }));
787
+ if (!mappedSelectionSet) {
788
+ return undefined;
789
+ }
790
+ const reoptimizedSelectionSet = mappedSelectionSet.optimize(newFragments);
791
+ return fragment.withUpdatedSelectionSet(reoptimizedSelectionSet);
792
+ });
793
+ }
676
794
  rebaseOn(schema) {
677
- return this.mapToExpandedSelectionSets((s) => {
678
- const rebasedType = schema.type(s.parentType.name);
795
+ return this.mapInDependencyOrder((fragment, newFragments) => {
796
+ const rebasedType = schema.type(fragment.selectionSet.parentType.name);
679
797
  try {
680
- return rebasedType && (0, definitions_1.isCompositeType)(rebasedType) ? s.rebaseOn(rebasedType) : undefined;
798
+ if (!rebasedType || !(0, definitions_1.isCompositeType)(rebasedType)) {
799
+ return undefined;
800
+ }
801
+ const rebasedSelection = fragment.selectionSet.rebaseOn(rebasedType, newFragments);
802
+ return new NamedFragmentDefinition(schema, fragment.name, rebasedType).setSelectionSet(rebasedSelection);
681
803
  }
682
804
  catch (e) {
683
805
  return undefined;
684
806
  }
685
- }, (orig, newSelection) => new NamedFragmentDefinition(schema, orig.name, newSelection.parentType).setSelectionSet(newSelection));
807
+ });
808
+ }
809
+ filter(predicate) {
810
+ return this.mapInDependencyOrder((fragment, newFragments) => {
811
+ if (predicate(fragment)) {
812
+ const updatedSelectionSet = fragment.selectionSet.expandFragments(newFragments);
813
+ return updatedSelectionSet === fragment.selectionSet
814
+ ? fragment
815
+ : fragment.withUpdatedSelectionSet(updatedSelectionSet);
816
+ }
817
+ else {
818
+ return undefined;
819
+ }
820
+ });
686
821
  }
687
822
  validate(variableDefinitions) {
688
823
  for (const fragment of this.fragments.values()) {
@@ -743,10 +878,15 @@ class DeferNormalizer {
743
878
  this.deferConditions.add(condition.name, label);
744
879
  }
745
880
  }
881
+ var ContainsResult;
882
+ (function (ContainsResult) {
883
+ ContainsResult[ContainsResult["NOT_CONTAINED"] = 0] = "NOT_CONTAINED";
884
+ ContainsResult[ContainsResult["STRICTLY_CONTAINED"] = 1] = "STRICTLY_CONTAINED";
885
+ ContainsResult[ContainsResult["EQUAL"] = 2] = "EQUAL";
886
+ })(ContainsResult || (exports.ContainsResult = ContainsResult = {}));
746
887
  class SelectionSet {
747
- constructor(parentType, keyedSelections = new Map(), fragments) {
888
+ constructor(parentType, keyedSelections = new Map()) {
748
889
  this.parentType = parentType;
749
- this.fragments = fragments;
750
890
  this._keyedSelections = keyedSelections;
751
891
  this._selections = (0, utils_1.mapValues)(keyedSelections);
752
892
  }
@@ -780,6 +920,21 @@ class SelectionSet {
780
920
  }
781
921
  return fields;
782
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
+ }
783
938
  usedVariables() {
784
939
  const collector = new definitions_1.VariableCollector();
785
940
  this.collectVariables(collector);
@@ -800,32 +955,24 @@ class SelectionSet {
800
955
  return this;
801
956
  }
802
957
  const wrapped = new InlineFragmentSelection(new FragmentElement(this.parentType, this.parentType), this);
803
- const optimized = wrapped.optimize(fragments);
958
+ const validator = FieldsConflictValidator.build(this);
959
+ const optimized = wrapped.optimize(fragments, validator);
804
960
  return optimized instanceof FragmentSpreadSelection
805
- ? selectionSetOf(this.parentType, optimized, fragments)
961
+ ? selectionSetOf(this.parentType, optimized)
806
962
  : optimized.selectionSet;
807
963
  }
808
- optimizeSelections(fragments) {
809
- (0, utils_1.assert)(!this.fragments || this.fragments.isEmpty(), `Should not be called on selection that already has named fragments, but got ${this.fragments}`);
810
- return this.lazyMap((selection) => selection.optimize(fragments), { fragments });
964
+ optimizeSelections(fragments, validator) {
965
+ return this.lazyMap((selection) => selection.optimize(fragments, validator));
811
966
  }
812
- expandAllFragments() {
813
- return this.lazyMap((selection) => selection.expandAllFragments(), { fragments: null });
967
+ expandFragments(updatedFragments) {
968
+ return this.lazyMap((selection) => selection.expandFragments(updatedFragments));
814
969
  }
815
- expandFragments(names, updatedFragments) {
816
- if (names.length === 0) {
817
- return this;
818
- }
819
- return this.lazyMap((selection) => selection.expandFragments(names, updatedFragments), { fragments: updatedFragments !== null && updatedFragments !== void 0 ? updatedFragments : null });
820
- }
821
- trimUnsatisfiableBranches(parentType, options) {
822
- return this.lazyMap((selection) => selection.trimUnsatisfiableBranches(parentType, options), { parentType });
970
+ normalize({ parentType, recursive }) {
971
+ return this.lazyMap((selection) => selection.normalize({ parentType, recursive }), { parentType });
823
972
  }
824
973
  lazyMap(mapper, options) {
825
974
  var _a;
826
975
  const selections = this.selections();
827
- const updatedFragments = options === null || options === void 0 ? void 0 : options.fragments;
828
- const newFragments = updatedFragments === undefined ? this.fragments : (updatedFragments !== null && updatedFragments !== void 0 ? updatedFragments : undefined);
829
976
  let updatedSelections = undefined;
830
977
  for (let i = 0; i < selections.length; i++) {
831
978
  const selection = selections[i];
@@ -841,40 +988,38 @@ class SelectionSet {
841
988
  }
842
989
  }
843
990
  if (!updatedSelections) {
844
- return this.withUpdatedFragments(newFragments);
991
+ return this;
845
992
  }
846
- return updatedSelections.toSelectionSet((_a = options === null || options === void 0 ? void 0 : options.parentType) !== null && _a !== void 0 ? _a : this.parentType, newFragments);
847
- }
848
- withUpdatedFragments(newFragments) {
849
- return this.fragments === newFragments ? this : new SelectionSet(this.parentType, this._keyedSelections, newFragments);
993
+ return updatedSelections.toSelectionSet((_a = options === null || options === void 0 ? void 0 : options.parentType) !== null && _a !== void 0 ? _a : this.parentType);
850
994
  }
851
995
  withoutDefer(labelsToRemove) {
852
- (0, utils_1.assert)(!this.fragments, 'Not yet supported');
853
996
  return this.lazyMap((selection) => selection.withoutDefer(labelsToRemove));
854
997
  }
855
998
  withNormalizedDefer(normalizer) {
856
- (0, utils_1.assert)(!this.fragments, 'Not yet supported');
857
999
  return this.lazyMap((selection) => selection.withNormalizedDefer(normalizer));
858
1000
  }
859
1001
  hasDefer() {
860
1002
  return this.selections().some((s) => s.hasDefer());
861
1003
  }
862
1004
  filter(predicate) {
863
- 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));
864
1009
  }
865
1010
  withoutEmptyBranches() {
866
- 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; });
867
1012
  return updated.isEmpty() ? undefined : updated;
868
1013
  }
869
- rebaseOn(parentType) {
1014
+ rebaseOn(parentType, fragments) {
870
1015
  if (this.parentType === parentType) {
871
1016
  return this;
872
1017
  }
873
1018
  const newSelections = new Map();
874
1019
  for (const selection of this.selections()) {
875
- newSelections.set(selection.key(), selection.rebaseOn(parentType));
1020
+ newSelections.set(selection.key(), selection.rebaseOn(parentType, fragments));
876
1021
  }
877
- return new SelectionSet(parentType, newSelections, this.fragments);
1022
+ return new SelectionSet(parentType, newSelections);
878
1023
  }
879
1024
  equals(that) {
880
1025
  if (this === that) {
@@ -891,92 +1036,57 @@ class SelectionSet {
891
1036
  }
892
1037
  return true;
893
1038
  }
894
- triviallyNestedSelectionsForKey(parentType, key) {
895
- const found = [];
896
- for (const selection of this.selections()) {
897
- if (selection.isUnecessaryInlineFragment(parentType)) {
898
- const selectionForKey = selection.selectionSet._keyedSelections.get(key);
899
- if (selectionForKey) {
900
- found.push(selectionForKey);
901
- }
902
- for (const nestedSelection of selection.selectionSet.triviallyNestedSelectionsForKey(parentType, key)) {
903
- found.push(nestedSelection);
904
- }
905
- }
906
- }
907
- return found;
908
- }
909
- mergeSameKeySelections(selections) {
910
- if (selections.length === 0) {
911
- return undefined;
912
- }
913
- const first = selections[0];
914
- if (!first.selectionSet || (first instanceof FragmentSpreadSelection) || selections.length === 1) {
915
- return first;
916
- }
917
- const mergedSubselections = new SelectionSetUpdates();
918
- for (const selection of selections) {
919
- mergedSubselections.add(selection.selectionSet);
920
- }
921
- if (first.kind === 'FieldSelection') {
922
- const rebasedField = first.element.rebaseOn(this.parentType);
923
- return new FieldSelection(rebasedField, mergedSubselections.toSelectionSet(rebasedField.baseType()));
924
- }
925
- else {
926
- const rebasedFragment = first.element.rebaseOn(this.parentType);
927
- return new InlineFragmentSelection(rebasedFragment, mergedSubselections.toSelectionSet(rebasedFragment.castedType()));
928
- }
929
- }
930
1039
  contains(that) {
1040
+ if (that._selections.length > this._selections.length) {
1041
+ return ContainsResult.NOT_CONTAINED;
1042
+ }
1043
+ let isEqual = true;
931
1044
  for (const [key, thatSelection] of that._keyedSelections) {
932
1045
  const thisSelection = this._keyedSelections.get(key);
933
- const otherSelections = this.triviallyNestedSelectionsForKey(this.parentType, key);
934
- const mergedSelection = this.mergeSameKeySelections([thisSelection].concat(otherSelections).filter(utils_1.isDefined));
935
- if (!(mergedSelection && mergedSelection.contains(thatSelection))
936
- && !(thatSelection.isUnecessaryInlineFragment(this.parentType) && this.contains(thatSelection.selectionSet))) {
937
- return false;
1046
+ const selectionResult = thisSelection === null || thisSelection === void 0 ? void 0 : thisSelection.contains(thatSelection);
1047
+ if (selectionResult === undefined || selectionResult === ContainsResult.NOT_CONTAINED) {
1048
+ return ContainsResult.NOT_CONTAINED;
938
1049
  }
1050
+ isEqual && (isEqual = selectionResult === ContainsResult.EQUAL);
939
1051
  }
940
- return true;
941
- }
942
- diffWithNamedFragmentIfContained(candidate, parentType) {
943
- const that = candidate.selectionSetAtType(parentType);
944
- if (that.isEmpty() || (that.selections().length === 1 && that.selections()[0].isTypenameField())) {
945
- return { contains: false };
946
- }
947
- if (this.contains(that)) {
948
- let updatedThis = this.expandFragments([candidate.name], this.fragments);
949
- if (updatedThis !== this) {
950
- updatedThis = updatedThis.trimUnsatisfiableBranches(parentType);
951
- }
952
- const diff = updatedThis.minus(that);
953
- return { contains: true, diff: diff.isEmpty() ? undefined : diff };
954
- }
955
- return { contains: false };
1052
+ return isEqual && that._selections.length === this._selections.length
1053
+ ? ContainsResult.EQUAL
1054
+ : ContainsResult.STRICTLY_CONTAINED;
956
1055
  }
957
1056
  minus(that) {
958
1057
  const updated = new SelectionSetUpdates();
959
1058
  for (const [key, thisSelection] of this._keyedSelections) {
960
1059
  const thatSelection = that._keyedSelections.get(key);
961
- const otherSelections = that.triviallyNestedSelectionsForKey(this.parentType, key);
962
- const allSelections = thatSelection ? [thatSelection].concat(otherSelections) : otherSelections;
963
- if (allSelections.length === 0) {
964
- if (thisSelection instanceof FragmentSpreadSelection) {
965
- const expanded = thisSelection.selectionSet.expandAllFragments().trimUnsatisfiableBranches(this.parentType);
966
- if (expanded.minus(that).isEmpty()) {
967
- continue;
968
- }
1060
+ if (thatSelection) {
1061
+ const remainder = thisSelection.minus(thatSelection);
1062
+ if (remainder) {
1063
+ updated.add(remainder);
969
1064
  }
970
- updated.add(thisSelection);
971
1065
  }
972
1066
  else {
973
- const selectionDiff = allSelections.reduce((prev, val) => prev === null || prev === void 0 ? void 0 : prev.minus(val), thisSelection);
974
- if (selectionDiff) {
975
- updated.add(selectionDiff);
1067
+ updated.add(thisSelection);
1068
+ }
1069
+ }
1070
+ return updated.toSelectionSet(this.parentType);
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);
976
1086
  }
977
1087
  }
978
1088
  }
979
- return updated.toSelectionSet(this.parentType, this.fragments);
1089
+ return intersection.toSelectionSet(this.parentType);
980
1090
  }
981
1091
  canRebaseOn(parentTypeToTest) {
982
1092
  return this.selections().every((selection) => selection.canAddTo(parentTypeToTest));
@@ -1046,10 +1156,10 @@ class SelectionSet {
1046
1156
  }
1047
1157
  return false;
1048
1158
  }
1049
- toOperationString(rootKind, variableDefinitions, operationName, expandFragments = false, prettyPrint = true) {
1159
+ toOperationString(rootKind, variableDefinitions, fragments, operationName, expandFragments = false, prettyPrint = true) {
1050
1160
  const indent = prettyPrint ? '' : undefined;
1051
- const fragmentsDefinitions = !expandFragments && this.fragments && !this.fragments.isEmpty()
1052
- ? this.fragments.toString(indent) + "\n\n"
1161
+ const fragmentsDefinitions = !expandFragments && fragments && !fragments.isEmpty()
1162
+ ? fragments.toString(indent) + "\n\n"
1053
1163
  : "";
1054
1164
  if (rootKind == "query" && !operationName && variableDefinitions.isEmpty()) {
1055
1165
  return fragmentsDefinitions + this.toString(expandFragments, true, indent);
@@ -1120,6 +1230,16 @@ class SelectionSetUpdates {
1120
1230
  toSelectionSet(parentType, fragments) {
1121
1231
  return makeSelectionSet(parentType, this.keyedUpdates, fragments);
1122
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
+ }
1123
1243
  }
1124
1244
  exports.SelectionSetUpdates = SelectionSetUpdates;
1125
1245
  function addToKeyedUpdates(keyedUpdates, selections) {
@@ -1184,7 +1304,7 @@ function makeSelection(parentType, updates, fragments) {
1184
1304
  (0, utils_1.assert)(updates.length > 0, 'Should not be called without any updates');
1185
1305
  const first = updates[0];
1186
1306
  if (updates.length === 1 && first instanceof AbstractSelection) {
1187
- return first.rebaseOn(parentType);
1307
+ return first.rebaseOn(parentType, fragments);
1188
1308
  }
1189
1309
  const element = updateElement(first).rebaseOn(parentType);
1190
1310
  const subSelectionParentType = element.kind === 'Field' ? element.baseType() : element.castedType();
@@ -1223,7 +1343,7 @@ function makeSelectionSet(parentType, keyedUpdates, fragments) {
1223
1343
  for (const [key, updates] of keyedUpdates.entries()) {
1224
1344
  selections.set(key, makeSelection(parentType, updates, fragments));
1225
1345
  }
1226
- return new SelectionSet(parentType, selections, fragments);
1346
+ return new SelectionSet(parentType, selections);
1227
1347
  }
1228
1348
  class MutableSelectionSet {
1229
1349
  constructor(parentType, _updates, memoizer) {
@@ -1299,14 +1419,14 @@ function allFieldDefinitionsInSelectionSet(selection) {
1299
1419
  return allFields;
1300
1420
  }
1301
1421
  exports.allFieldDefinitionsInSelectionSet = allFieldDefinitionsInSelectionSet;
1302
- function selectionSetOf(parentType, selection, fragments) {
1422
+ function selectionSetOf(parentType, selection) {
1303
1423
  const map = new Map();
1304
1424
  map.set(selection.key(), selection);
1305
- return new SelectionSet(parentType, map, fragments);
1425
+ return new SelectionSet(parentType, map);
1306
1426
  }
1307
1427
  exports.selectionSetOf = selectionSetOf;
1308
- function selectionSetOfElement(element, subSelection, fragments) {
1309
- return selectionSetOf(element.parentType, selectionOfElement(element, subSelection), fragments);
1428
+ function selectionSetOfElement(element, subSelection) {
1429
+ return selectionSetOf(element.parentType, selectionOfElement(element, subSelection));
1310
1430
  }
1311
1431
  exports.selectionSetOfElement = selectionSetOfElement;
1312
1432
  function selectionOfElement(element, subSelection) {
@@ -1332,10 +1452,6 @@ class AbstractSelection {
1332
1452
  var _a;
1333
1453
  (_a = this.selectionSet) === null || _a === void 0 ? void 0 : _a.collectUsedFragmentNames(collector);
1334
1454
  }
1335
- namedFragments() {
1336
- var _a;
1337
- return (_a = this.selectionSet) === null || _a === void 0 ? void 0 : _a.fragments;
1338
- }
1339
1455
  withUpdatedSelectionSet(selectionSet) {
1340
1456
  return this.withUpdatedComponents(this.element, selectionSet);
1341
1457
  }
@@ -1351,6 +1467,9 @@ class AbstractSelection {
1351
1467
  ? this.us()
1352
1468
  : this.withUpdatedSelectionSet(updatedSelectionSet);
1353
1469
  }
1470
+ isFragmentSpread() {
1471
+ return false;
1472
+ }
1354
1473
  minus(that) {
1355
1474
  if (this.selectionSet && that.selectionSet) {
1356
1475
  const updatedSubSelectionSet = this.selectionSet.minus(that.selectionSet);
@@ -1360,29 +1479,185 @@ class AbstractSelection {
1360
1479
  }
1361
1480
  return undefined;
1362
1481
  }
1363
- tryOptimizeSubselectionOnce(_) {
1364
- (0, utils_1.assert)(false, `UNSUPPORTED`);
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
+ }
1501
+ const applyingFragments = [];
1502
+ for (const candidate of candidates) {
1503
+ const atType = candidate.expandedSelectionSetAtType(parentType);
1504
+ const selectionSetAtType = atType.selectionSet;
1505
+ if (selectionSetAtType.isEmpty() || (selectionSetAtType.selections().length === 1 && selectionSetAtType.selections()[0].isTypenameField())) {
1506
+ continue;
1507
+ }
1508
+ const res = subSelection.contains(selectionSetAtType);
1509
+ if (res === ContainsResult.EQUAL) {
1510
+ if (canUseFullMatchingFragment(candidate)) {
1511
+ if (!validator.checkCanReuseFragmentAndTrackIt(atType)) {
1512
+ continue;
1513
+ }
1514
+ return candidate;
1515
+ }
1516
+ if (candidate.appliedDirectives.length === 0) {
1517
+ applyingFragments.push({ fragment: candidate, atType });
1518
+ }
1519
+ }
1520
+ else if (res === ContainsResult.STRICTLY_CONTAINED && candidate.appliedDirectives.length === 0) {
1521
+ applyingFragments.push({ fragment: candidate, atType });
1522
+ }
1523
+ }
1524
+ if (applyingFragments.length === 0) {
1525
+ return subSelection;
1526
+ }
1527
+ const filteredApplyingFragments = applyingFragments.filter(({ fragment }) => !applyingFragments.some((o) => o.fragment.includes(fragment.name)));
1528
+ let notCoveredByFragments = subSelection;
1529
+ const optimized = new SelectionSetUpdates();
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);
1536
+ optimized.add(new FragmentSpreadSelection(parentType, fragments, fragment, []));
1537
+ }
1538
+ return optimized.add(notCoveredByFragments).toSelectionSet(parentType, fragments);
1365
1539
  }
1366
- tryOptimizeSubselectionWithFragments({ parentType, subSelection, fragments, fragmentFilter, }) {
1367
- let candidates = fragments.maybeApplyingAtType(parentType);
1368
- if (fragmentFilter) {
1369
- candidates = candidates.filter(fragmentFilter);
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
+ }
1370
1565
  }
1371
- let shouldTryAgain;
1372
- do {
1373
- const { spread, optimizedSelection, hasDiff } = this.tryOptimizeSubselectionOnce({ parentType, subSelection, candidates, fragments });
1374
- if (optimizedSelection) {
1375
- subSelection = optimizedSelection;
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);
1376
1572
  }
1377
- else if (spread) {
1378
- return spread;
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
+ }
1379
1629
  }
1380
- shouldTryAgain = !!spread && !!hasDiff;
1381
- if (shouldTryAgain) {
1382
- candidates = candidates.filter((c) => c !== (spread === null || spread === void 0 ? void 0 : spread.namedFragment));
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;
1383
1638
  }
1384
- } while (shouldTryAgain);
1385
- return subSelection;
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}}`;
1386
1661
  }
1387
1662
  }
1388
1663
  class FieldSelection extends AbstractSelection {
@@ -1401,47 +1676,46 @@ class FieldSelection extends AbstractSelection {
1401
1676
  return this.element.definition.name === definitions_1.typenameFieldName;
1402
1677
  }
1403
1678
  withUpdatedComponents(field, selectionSet) {
1679
+ if (this.element === field && this.selectionSet === selectionSet) {
1680
+ return this;
1681
+ }
1404
1682
  return new FieldSelection(field, selectionSet);
1405
1683
  }
1406
1684
  key() {
1407
1685
  return this.element.key();
1408
1686
  }
1409
- optimize(fragments) {
1410
- let optimizedSelection = this.selectionSet ? this.selectionSet.optimizeSelections(fragments) : undefined;
1411
- const fieldBaseType = this.element.baseType();
1412
- if ((0, definitions_1.isCompositeType)(fieldBaseType) && optimizedSelection) {
1687
+ optimize(fragments, validator) {
1688
+ const fieldBaseType = (0, definitions_1.baseType)(this.element.definition.type);
1689
+ if (!(0, definitions_1.isCompositeType)(fieldBaseType) || !this.selectionSet) {
1690
+ return this;
1691
+ }
1692
+ const fieldValidator = validator.forField(this.element);
1693
+ let optimizedSelection = this.selectionSet;
1694
+ if ((0, definitions_1.isCompositeType)(fieldBaseType) && this.selectionSet) {
1413
1695
  const optimized = this.tryOptimizeSubselectionWithFragments({
1414
1696
  parentType: fieldBaseType,
1415
- subSelection: optimizedSelection,
1697
+ subSelection: this.selectionSet,
1416
1698
  fragments,
1417
- fragmentFilter: (f) => f.appliedDirectives.length === 0,
1699
+ validator: fieldValidator,
1700
+ canUseFullMatchingFragment: (fragment) => fragment.appliedDirectives.length === 0,
1418
1701
  });
1419
- (0, utils_1.assert)(!(optimized instanceof FragmentSpreadSelection), 'tryOptimizeSubselectionOnce should never return only a spread');
1420
- optimizedSelection = optimized;
1702
+ if (optimized instanceof NamedFragmentDefinition) {
1703
+ optimizedSelection = selectionSetOf(fieldBaseType, new FragmentSpreadSelection(fieldBaseType, fragments, optimized, []));
1704
+ }
1705
+ else {
1706
+ optimizedSelection = optimized;
1707
+ }
1421
1708
  }
1709
+ optimizedSelection = optimizedSelection.optimizeSelections(fragments, fieldValidator);
1422
1710
  return this.selectionSet === optimizedSelection
1423
1711
  ? this
1424
- : new FieldSelection(this.element, optimizedSelection);
1425
- }
1426
- tryOptimizeSubselectionOnce({ parentType, subSelection, candidates, fragments, }) {
1427
- let optimizedSelection = subSelection;
1428
- for (const candidate of candidates) {
1429
- const { contains, diff } = optimizedSelection.diffWithNamedFragmentIfContained(candidate, parentType);
1430
- if (contains) {
1431
- const spread = new FragmentSpreadSelection(parentType, fragments, candidate, []);
1432
- optimizedSelection = diff
1433
- ? new SelectionSetUpdates().add(spread).add(diff).toSelectionSet(parentType, fragments)
1434
- : selectionSetOf(parentType, spread);
1435
- return { spread, optimizedSelection, hasDiff: !!diff };
1436
- }
1437
- }
1438
- return {};
1712
+ : this.withUpdatedSelectionSet(optimizedSelection);
1439
1713
  }
1440
- filter(predicate) {
1714
+ filterRecursiveDepthFirst(predicate) {
1441
1715
  if (!this.selectionSet) {
1442
1716
  return predicate(this) ? this : undefined;
1443
1717
  }
1444
- const updatedSelectionSet = this.selectionSet.filter(predicate);
1718
+ const updatedSelectionSet = this.selectionSet.filterRecursiveDepthFirst(predicate);
1445
1719
  const thisWithFilteredSelectionSet = this.selectionSet === updatedSelectionSet
1446
1720
  ? this
1447
1721
  : new FieldSelection(this.element, updatedSelectionSet);
@@ -1453,7 +1727,7 @@ class FieldSelection extends AbstractSelection {
1453
1727
  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);
1454
1728
  (_a = this.selectionSet) === null || _a === void 0 ? void 0 : _a.validate(variableDefinitions);
1455
1729
  }
1456
- rebaseOn(parentType) {
1730
+ rebaseOn(parentType, fragments) {
1457
1731
  if (this.element.parentType === parentType) {
1458
1732
  return this;
1459
1733
  }
@@ -1466,7 +1740,7 @@ class FieldSelection extends AbstractSelection {
1466
1740
  return this.withUpdatedElement(rebasedElement);
1467
1741
  }
1468
1742
  validate((0, definitions_1.isCompositeType)(rebasedBase), () => `Cannot rebase field selection ${this} on ${parentType}: rebased field base return type ${rebasedBase} is not composite`);
1469
- return this.withUpdatedComponents(rebasedElement, this.selectionSet.rebaseOn(rebasedBase));
1743
+ return this.withUpdatedComponents(rebasedElement, this.selectionSet.rebaseOn(rebasedBase, fragments));
1470
1744
  }
1471
1745
  canAddTo(parentType) {
1472
1746
  if (this.element.parentType === parentType) {
@@ -1508,26 +1782,27 @@ class FieldSelection extends AbstractSelection {
1508
1782
  var _a;
1509
1783
  return !!((_a = this.selectionSet) === null || _a === void 0 ? void 0 : _a.hasDefer());
1510
1784
  }
1511
- expandAllFragments() {
1512
- return this.mapToSelectionSet((s) => s.expandAllFragments());
1513
- }
1514
- trimUnsatisfiableBranches(_, options) {
1515
- 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);
1516
1791
  if (!this.selectionSet) {
1517
- return this;
1792
+ return this.withUpdatedElement(element);
1518
1793
  }
1519
- const base = this.element.baseType();
1520
- (0, utils_1.assert)((0, definitions_1.isCompositeType)(base), () => `Field ${this.element} should not have a sub-selection`);
1521
- 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;
1522
- if ((_b = trimmed.selectionSet) === null || _b === void 0 ? void 0 : _b.isEmpty()) {
1523
- 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 })])));
1524
1799
  }
1525
1800
  else {
1526
- return trimmed;
1801
+ return this.withUpdatedComponents(element, normalizedSubSelection);
1527
1802
  }
1528
1803
  }
1529
- expandFragments(names, updatedFragments) {
1530
- return this.mapToSelectionSet((s) => s.expandFragments(names, updatedFragments));
1804
+ expandFragments(updatedFragments) {
1805
+ return this.mapToSelectionSet((s) => s.expandFragments(updatedFragments));
1531
1806
  }
1532
1807
  equals(that) {
1533
1808
  if (this === that) {
@@ -1543,15 +1818,14 @@ class FieldSelection extends AbstractSelection {
1543
1818
  }
1544
1819
  contains(that) {
1545
1820
  if (!(that instanceof FieldSelection) || !this.element.equals(that.element)) {
1546
- return false;
1821
+ return ContainsResult.NOT_CONTAINED;
1547
1822
  }
1548
- if (!that.selectionSet) {
1549
- return true;
1823
+ if (!this.selectionSet) {
1824
+ (0, utils_1.assert)(!that.selectionSet, '`this` and `that` have the same element, so if one does not have a sub-selection, neither should the other one');
1825
+ return ContainsResult.EQUAL;
1550
1826
  }
1551
- return !!this.selectionSet && this.selectionSet.contains(that.selectionSet);
1552
- }
1553
- isUnecessaryInlineFragment(_) {
1554
- return false;
1827
+ (0, utils_1.assert)(that.selectionSet, '`this` and `that` have the same element, so if one has sub-selection, the other one should too');
1828
+ return this.selectionSet.contains(that.selectionSet);
1555
1829
  }
1556
1830
  toString(expandFragments = true, indent) {
1557
1831
  return (indent !== null && indent !== void 0 ? indent : '') + this.element + (this.selectionSet ? ' ' + this.selectionSet.toString(expandFragments, true, indent) : '');
@@ -1573,10 +1847,9 @@ class FragmentSelection extends AbstractSelection {
1573
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}"`; });
1574
1848
  }
1575
1849
  }
1576
- filter(predicate) {
1577
- const selectionSet = this.selectionSet;
1578
- const updatedSelectionSet = selectionSet.filter(predicate);
1579
- const thisWithFilteredSelectionSet = updatedSelectionSet === selectionSet
1850
+ filterRecursiveDepthFirst(predicate) {
1851
+ const updatedSelectionSet = this.selectionSet.filterRecursiveDepthFirst(predicate);
1852
+ const thisWithFilteredSelectionSet = updatedSelectionSet === this.selectionSet
1580
1853
  ? this
1581
1854
  : new InlineFragmentSelection(this.element, updatedSelectionSet);
1582
1855
  return predicate(thisWithFilteredSelectionSet) ? thisWithFilteredSelectionSet : undefined;
@@ -1584,12 +1857,6 @@ class FragmentSelection extends AbstractSelection {
1584
1857
  hasDefer() {
1585
1858
  return this.element.hasDefer() || this.selectionSet.hasDefer();
1586
1859
  }
1587
- isUnecessaryInlineFragment(parentType) {
1588
- return this.element.appliedDirectives.length === 0
1589
- && !!this.element.typeCondition
1590
- && (this.element.typeCondition.name === parentType.name
1591
- || ((0, definitions_1.isObjectType)(parentType) && (0, definitions_1.possibleRuntimeTypes)(this.element.typeCondition).some((t) => t.name === parentType.name)));
1592
- }
1593
1860
  }
1594
1861
  exports.FragmentSelection = FragmentSelection;
1595
1862
  class InlineFragmentSelection extends FragmentSelection {
@@ -1604,6 +1871,9 @@ class InlineFragmentSelection extends FragmentSelection {
1604
1871
  return this.element.key();
1605
1872
  }
1606
1873
  withUpdatedComponents(fragment, selectionSet) {
1874
+ if (fragment === this.element && selectionSet === this.selectionSet) {
1875
+ return this;
1876
+ }
1607
1877
  return new InlineFragmentSelection(fragment, selectionSet);
1608
1878
  }
1609
1879
  validate(variableDefinitions) {
@@ -1611,7 +1881,7 @@ class InlineFragmentSelection extends FragmentSelection {
1611
1881
  validate(!this.selectionSet.isEmpty(), () => `Invalid empty selection set for fragment "${this.element}"`);
1612
1882
  this.selectionSet.validate(variableDefinitions);
1613
1883
  }
1614
- rebaseOn(parentType) {
1884
+ rebaseOn(parentType, fragments) {
1615
1885
  if (this.parentType === parentType) {
1616
1886
  return this;
1617
1887
  }
@@ -1620,7 +1890,7 @@ class InlineFragmentSelection extends FragmentSelection {
1620
1890
  if (rebasedCastedType === this.selectionSet.parentType) {
1621
1891
  return this.withUpdatedElement(rebasedFragment);
1622
1892
  }
1623
- return this.withUpdatedComponents(rebasedFragment, this.selectionSet.rebaseOn(rebasedCastedType));
1893
+ return this.withUpdatedComponents(rebasedFragment, this.selectionSet.rebaseOn(rebasedCastedType, fragments));
1624
1894
  }
1625
1895
  canAddTo(parentType) {
1626
1896
  if (this.element.parentType === parentType) {
@@ -1652,53 +1922,41 @@ class InlineFragmentSelection extends FragmentSelection {
1652
1922
  selectionSet: this.selectionSet.toSelectionSetNode()
1653
1923
  };
1654
1924
  }
1655
- optimize(fragments) {
1656
- let optimizedSelection = this.selectionSet.optimizeSelections(fragments);
1925
+ optimize(fragments, validator) {
1926
+ let optimizedSelection = this.selectionSet;
1657
1927
  const typeCondition = this.element.typeCondition;
1658
1928
  if (typeCondition) {
1659
1929
  const optimized = this.tryOptimizeSubselectionWithFragments({
1660
1930
  parentType: typeCondition,
1661
1931
  subSelection: optimizedSelection,
1662
1932
  fragments,
1933
+ validator,
1934
+ canUseFullMatchingFragment: (fragment) => {
1935
+ return fragment.appliedDirectives.length === 0
1936
+ || ((0, types_1.sameType)(typeCondition, fragment.typeCondition)
1937
+ && fragment.appliedDirectives.every((d) => this.element.appliedDirectives.some((s) => (0, definitions_1.sameDirectiveApplication)(d, s))));
1938
+ },
1663
1939
  });
1664
- if (optimized instanceof FragmentSpreadSelection) {
1665
- return optimized;
1666
- }
1667
- optimizedSelection = optimized;
1668
- }
1669
- return this.selectionSet === optimizedSelection
1670
- ? this
1671
- : new InlineFragmentSelection(this.element, optimizedSelection);
1672
- }
1673
- tryOptimizeSubselectionOnce({ parentType, subSelection, candidates, fragments, }) {
1674
- let optimizedSelection = subSelection;
1675
- for (const candidate of candidates) {
1676
- const { contains, diff } = optimizedSelection.diffWithNamedFragmentIfContained(candidate, parentType);
1677
- if (contains) {
1678
- if (!diff && (0, types_1.sameType)(this.element.typeCondition, candidate.typeCondition)) {
1940
+ if (optimized instanceof NamedFragmentDefinition) {
1941
+ if ((0, types_1.sameType)(typeCondition, optimized.typeCondition)) {
1679
1942
  let spreadDirectives = this.element.appliedDirectives;
1680
- if (candidate.appliedDirectives.length > 0) {
1681
- const { isSubset, difference } = diffDirectives(this.element.appliedDirectives, candidate.appliedDirectives);
1682
- if (!isSubset) {
1683
- continue;
1684
- }
1685
- spreadDirectives = difference;
1943
+ if (optimized.appliedDirectives) {
1944
+ spreadDirectives = spreadDirectives.filter((s) => !optimized.appliedDirectives.some((d) => (0, definitions_1.sameDirectiveApplication)(d, s)));
1686
1945
  }
1687
- return {
1688
- spread: new FragmentSpreadSelection(this.parentType, fragments, candidate, spreadDirectives),
1689
- };
1946
+ return new FragmentSpreadSelection(this.parentType, fragments, optimized, spreadDirectives);
1690
1947
  }
1691
- if (candidate.appliedDirectives.length > 0) {
1692
- continue;
1948
+ else {
1949
+ optimizedSelection = selectionSetOf(typeCondition, new FragmentSpreadSelection(typeCondition, fragments, optimized, []));
1693
1950
  }
1694
- const spread = new FragmentSpreadSelection(parentType, fragments, candidate, []);
1695
- optimizedSelection = diff
1696
- ? new SelectionSetUpdates().add(spread).add(diff).toSelectionSet(parentType, fragments)
1697
- : selectionSetOf(parentType, spread);
1698
- return { spread, optimizedSelection, hasDiff: !!diff };
1951
+ }
1952
+ else {
1953
+ optimizedSelection = optimized;
1699
1954
  }
1700
1955
  }
1701
- return {};
1956
+ optimizedSelection = optimizedSelection.optimizeSelections(fragments, validator);
1957
+ return this.selectionSet === optimizedSelection
1958
+ ? this
1959
+ : new InlineFragmentSelection(this.element, optimizedSelection);
1702
1960
  }
1703
1961
  withoutDefer(labelsToRemove) {
1704
1962
  const newSelection = this.selectionSet.withoutDefer(labelsToRemove);
@@ -1723,42 +1981,42 @@ class InlineFragmentSelection extends FragmentSelection {
1723
1981
  ? this
1724
1982
  : this.withUpdatedComponents(newElement, newSelection);
1725
1983
  }
1726
- trimUnsatisfiableBranches(currentType, options) {
1727
- var _a, _b, _c;
1728
- 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;
1729
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
+ }
1730
1994
  if (this.element.appliedDirectives.length === 0) {
1731
- if (!thisCondition || currentType === this.element.typeCondition) {
1732
- const trimmed = this.selectionSet.trimUnsatisfiableBranches(currentType, options);
1733
- 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;
1734
1998
  }
1735
- if ((0, definitions_1.isObjectType)(currentType)) {
1736
- 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) {
1737
2005
  return undefined;
1738
2006
  }
1739
2007
  else {
1740
- const trimmed = this.selectionSet.trimUnsatisfiableBranches(currentType, options);
1741
- 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 })])));
1742
2009
  }
1743
2010
  }
1744
2011
  }
1745
- if (!recursive) {
1746
- return this;
1747
- }
1748
- const trimmedSelectionSet = this.selectionSet.trimUnsatisfiableBranches((_b = this.element.typeCondition) !== null && _b !== void 0 ? _b : this.parentType);
1749
- if (trimmedSelectionSet.isEmpty()) {
1750
- if (this.element.appliedDirectives.length === 0) {
1751
- return undefined;
1752
- }
1753
- else {
1754
- 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 })])));
1755
- }
2012
+ else {
2013
+ normalizedSelectionSet = this.selectionSet;
1756
2014
  }
1757
2015
  if (this.element.appliedDirectives.length === 0 && (0, definitions_1.isAbstractType)(thisCondition)) {
1758
- (0, utils_1.assert)(!(0, definitions_1.isObjectType)(currentType), () => `Should not have got here if ${currentType} is an object type`);
1759
- 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);
1760
2018
  const liftableSelections = [];
1761
- for (const selection of trimmedSelectionSet.selections()) {
2019
+ for (const selection of normalizedSelectionSet.selections()) {
1762
2020
  if (selection.kind === 'FragmentSelection'
1763
2021
  && selection.element.typeCondition
1764
2022
  && (0, definitions_1.isObjectType)(selection.element.typeCondition)
@@ -1766,23 +2024,22 @@ class InlineFragmentSelection extends FragmentSelection {
1766
2024
  liftableSelections.push(selection);
1767
2025
  }
1768
2026
  }
1769
- if (liftableSelections.length === trimmedSelectionSet.selections().length) {
1770
- return trimmedSelectionSet;
2027
+ if (liftableSelections.length === normalizedSelectionSet.selections().length) {
2028
+ return normalizedSelectionSet;
1771
2029
  }
1772
2030
  if (liftableSelections.length > 0) {
1773
2031
  const newSet = new SelectionSetUpdates();
1774
2032
  newSet.add(liftableSelections);
1775
- newSet.add(this.withUpdatedSelectionSet(trimmedSelectionSet.filter((s) => !liftableSelections.includes(s))));
1776
- return newSet.toSelectionSet(this.parentType);
2033
+ newSet.add(this.withUpdatedSelectionSet(normalizedSelectionSet.filter((s) => !liftableSelections.includes(s))));
2034
+ return newSet.toSelectionSet(parentType);
1777
2035
  }
1778
2036
  }
1779
- return this.selectionSet === trimmedSelectionSet ? this : this.withUpdatedSelectionSet(trimmedSelectionSet);
1780
- }
1781
- expandAllFragments() {
1782
- return this.mapToSelectionSet((s) => s.expandAllFragments());
2037
+ return this.parentType === parentType && this.selectionSet === normalizedSelectionSet
2038
+ ? this
2039
+ : this.withUpdatedComponents(this.element.rebaseOn(parentType), normalizedSelectionSet);
1783
2040
  }
1784
- expandFragments(names, updatedFragments) {
1785
- return this.mapToSelectionSet((s) => s.expandFragments(names, updatedFragments));
2041
+ expandFragments(updatedFragments) {
2042
+ return this.mapToSelectionSet((s) => s.expandFragments(updatedFragments));
1786
2043
  }
1787
2044
  equals(that) {
1788
2045
  if (this === that) {
@@ -1793,22 +2050,15 @@ class InlineFragmentSelection extends FragmentSelection {
1793
2050
  && this.selectionSet.equals(that.selectionSet);
1794
2051
  }
1795
2052
  contains(that) {
1796
- return (that instanceof FragmentSelection)
1797
- && this.element.equals(that.element)
1798
- && this.selectionSet.contains(that.selectionSet);
2053
+ if (!(that instanceof FragmentSelection) || !this.element.equals(that.element)) {
2054
+ return ContainsResult.NOT_CONTAINED;
2055
+ }
2056
+ return this.selectionSet.contains(that.selectionSet);
1799
2057
  }
1800
2058
  toString(expandFragments = true, indent) {
1801
2059
  return (indent !== null && indent !== void 0 ? indent : '') + this.element + ' ' + this.selectionSet.toString(expandFragments, true, indent);
1802
2060
  }
1803
2061
  }
1804
- function diffDirectives(superset, maybeSubset) {
1805
- if (maybeSubset.every((d) => superset.some((s) => (0, definitions_1.sameDirectiveApplication)(d, s)))) {
1806
- return { isSubset: true, difference: superset.filter((s) => !maybeSubset.some((d) => (0, definitions_1.sameDirectiveApplication)(d, s))) };
1807
- }
1808
- else {
1809
- return { isSubset: false, difference: [] };
1810
- }
1811
- }
1812
2062
  class FragmentSpreadSelection extends FragmentSelection {
1813
2063
  constructor(sourceType, fragments, namedFragment, spreadDirectives) {
1814
2064
  super(new FragmentElement(sourceType, namedFragment.typeCondition, namedFragment.appliedDirectives.concat(spreadDirectives)));
@@ -1816,6 +2066,9 @@ class FragmentSpreadSelection extends FragmentSelection {
1816
2066
  this.namedFragment = namedFragment;
1817
2067
  this.spreadDirectives = spreadDirectives;
1818
2068
  }
2069
+ isFragmentSpread() {
2070
+ return true;
2071
+ }
1819
2072
  get selectionSet() {
1820
2073
  return this.namedFragment.selectionSet;
1821
2074
  }
@@ -1828,14 +2081,13 @@ class FragmentSpreadSelection extends FragmentSelection {
1828
2081
  withUpdatedComponents(_fragment, _selectionSet) {
1829
2082
  (0, utils_1.assert)(false, `Unsupported`);
1830
2083
  }
1831
- trimUnsatisfiableBranches(parentType) {
1832
- return this.rebaseOn(parentType);
1833
- }
1834
- namedFragments() {
1835
- return this.fragments;
2084
+ normalize({ parentType }) {
2085
+ (0, utils_1.assert)(parentType.schema() === this.parentType.schema(), 'Should not try to normalize using a type from another schema');
2086
+ return this.rebaseOn(parentType, this.fragments);
1836
2087
  }
1837
2088
  validate() {
1838
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}`);
1839
2091
  }
1840
2092
  toSelectionNode() {
1841
2093
  const directiveNodes = this.spreadDirectives.length === 0
@@ -1856,35 +2108,32 @@ class FragmentSpreadSelection extends FragmentSelection {
1856
2108
  directives: directiveNodes,
1857
2109
  };
1858
2110
  }
1859
- optimize(_) {
2111
+ optimize(_1, _2) {
1860
2112
  return this;
1861
2113
  }
1862
- rebaseOn(parentType) {
2114
+ rebaseOn(parentType, fragments) {
1863
2115
  if (this.parentType === parentType) {
1864
2116
  return this;
1865
2117
  }
1866
- return new FragmentSpreadSelection(parentType, this.fragments, this.namedFragment, this.spreadDirectives);
2118
+ (0, utils_1.assert)(fragments || this.parentType.schema() === parentType.schema(), `Must provide fragments is rebasing on other schema`);
2119
+ const newFragments = fragments !== null && fragments !== void 0 ? fragments : this.fragments;
2120
+ const namedFragment = newFragments.get(this.namedFragment.name);
2121
+ (0, utils_1.assert)(namedFragment, () => `Cannot rebase ${this} if it isn't part of the provided fragments`);
2122
+ return new FragmentSpreadSelection(parentType, newFragments, namedFragment, this.spreadDirectives);
1867
2123
  }
1868
2124
  canAddTo(_) {
1869
2125
  return true;
1870
2126
  }
1871
- expandAllFragments() {
1872
- const expandedSubSelections = this.selectionSet.expandAllFragments();
1873
- return (0, types_1.sameType)(this.parentType, this.namedFragment.typeCondition) && this.element.appliedDirectives.length === 0
1874
- ? expandedSubSelections.selections()
1875
- : new InlineFragmentSelection(this.element, expandedSubSelections);
1876
- }
1877
- expandFragments(names, updatedFragments) {
1878
- if (!names.includes(this.namedFragment.name)) {
2127
+ expandFragments(updatedFragments) {
2128
+ if (updatedFragments === null || updatedFragments === void 0 ? void 0 : updatedFragments.has(this.namedFragment.name)) {
1879
2129
  return this;
1880
2130
  }
1881
- const expandedSubSelections = this.selectionSet.expandFragments(names, updatedFragments);
2131
+ const expandedSubSelections = this.selectionSet.expandFragments(updatedFragments);
1882
2132
  return (0, types_1.sameType)(this.parentType, this.namedFragment.typeCondition) && this.element.appliedDirectives.length === 0
1883
2133
  ? expandedSubSelections.selections()
1884
2134
  : new InlineFragmentSelection(this.element, expandedSubSelections);
1885
2135
  }
1886
2136
  collectUsedFragmentNames(collector) {
1887
- this.selectionSet.collectUsedFragmentNames(collector);
1888
2137
  const usageCount = collector.get(this.namedFragment.name);
1889
2138
  collector.set(this.namedFragment.name, usageCount === undefined ? 1 : usageCount + 1);
1890
2139
  }
@@ -1908,11 +2157,12 @@ class FragmentSpreadSelection extends FragmentSelection {
1908
2157
  }
1909
2158
  contains(that) {
1910
2159
  if (this.equals(that)) {
1911
- return true;
2160
+ return ContainsResult.EQUAL;
1912
2161
  }
1913
- return (that instanceof FragmentSelection)
1914
- && this.element.equals(that.element)
1915
- && this.selectionSet.contains(that.selectionSet);
2162
+ if (!(that instanceof FragmentSelection) || !this.element.equals(that.element)) {
2163
+ return ContainsResult.NOT_CONTAINED;
2164
+ }
2165
+ return this.selectionSet.contains(that.selectionSet);
1916
2166
  }
1917
2167
  toString(expandFragments = true, indent) {
1918
2168
  if (expandFragments) {
@@ -1927,7 +2177,7 @@ class FragmentSpreadSelection extends FragmentSelection {
1927
2177
  }
1928
2178
  function selectionSetOfNode(parentType, node, variableDefinitions, fragments, fieldAccessor = (type, name) => type.field(name)) {
1929
2179
  if (node.selections.length === 1) {
1930
- return selectionSetOf(parentType, selectionOfNode(parentType, node.selections[0], variableDefinitions, fragments, fieldAccessor), fragments);
2180
+ return selectionSetOf(parentType, selectionOfNode(parentType, node.selections[0], variableDefinitions, fragments, fieldAccessor));
1931
2181
  }
1932
2182
  const selections = new SelectionSetUpdates();
1933
2183
  for (const selectionNode of node.selections) {
@@ -2018,13 +2268,14 @@ function operationFromAST({ schema, operation, variableDefinitions, fragments, v
2018
2268
  var _a;
2019
2269
  const rootType = schema.schemaDefinition.root(operation.operation);
2020
2270
  validate(rootType, () => `The schema has no "${operation.operation}" root type defined`);
2271
+ const fragmentsIfAny = fragments.isEmpty() ? undefined : fragments;
2021
2272
  return new Operation(schema, operation.operation, parseSelectionSet({
2022
2273
  parentType: rootType.type,
2023
2274
  source: operation.selectionSet,
2024
2275
  variableDefinitions,
2025
- fragments: fragments.isEmpty() ? undefined : fragments,
2276
+ fragments: fragmentsIfAny,
2026
2277
  validate: validateInput,
2027
- }), variableDefinitions, (_a = operation.name) === null || _a === void 0 ? void 0 : _a.value);
2278
+ }), variableDefinitions, fragmentsIfAny, (_a = operation.name) === null || _a === void 0 ? void 0 : _a.value);
2028
2279
  }
2029
2280
  function parseOperation(schema, operation, options) {
2030
2281
  return operationFromDocument(schema, (0, graphql_1.parse)(operation), options);
@@ -2056,8 +2307,8 @@ function operationToDocument(operation) {
2056
2307
  selectionSet: operation.selectionSet.toSelectionSetNode(),
2057
2308
  variableDefinitions: operation.variableDefinitions.toVariableDefinitionNodes(),
2058
2309
  };
2059
- const fragmentASTs = operation.selectionSet.fragments
2060
- ? (_a = operation.selectionSet.fragments) === null || _a === void 0 ? void 0 : _a.toFragmentDefinitionNodes()
2310
+ const fragmentASTs = operation.fragments
2311
+ ? (_a = operation.fragments) === null || _a === void 0 ? void 0 : _a.toFragmentDefinitionNodes()
2061
2312
  : [];
2062
2313
  return {
2063
2314
  kind: graphql_1.Kind.DOCUMENT,