@apollo/federation-internals 2.4.5 → 2.4.7

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");
@@ -75,7 +75,10 @@ class Field extends AbstractOperationElement {
75
75
  return this.definition.parent;
76
76
  }
77
77
  isLeafField() {
78
- return (0, definitions_1.isLeafType)((0, definitions_1.baseType)(this.definition.type));
78
+ return (0, definitions_1.isLeafType)(this.baseType());
79
+ }
80
+ baseType() {
81
+ return (0, definitions_1.baseType)(this.definition.type);
79
82
  }
80
83
  withUpdatedDefinition(newDefinition) {
81
84
  const newField = new Field(newDefinition, this.args, this.appliedDirectives, this.alias);
@@ -431,7 +434,7 @@ function concatOperationPaths(head, tail) {
431
434
  exports.concatOperationPaths = concatOperationPaths;
432
435
  function isUselessFollowupElement(first, followup, conditionals) {
433
436
  const typeOfFirst = first.kind === 'Field'
434
- ? (0, definitions_1.baseType)(first.definition.type)
437
+ ? first.baseType()
435
438
  : first.typeCondition;
436
439
  return !!typeOfFirst
437
440
  && followup.kind === 'FragmentElement'
@@ -439,16 +442,92 @@ function isUselessFollowupElement(first, followup, conditionals) {
439
442
  && (followup.appliedDirectives.length === 0 || (0, definitions_1.isDirectiveApplicationsSubset)(conditionals, followup.appliedDirectives))
440
443
  && (0, types_1.isSubtype)(followup.typeCondition, typeOfFirst);
441
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
+ }
442
521
  class Operation {
443
- constructor(schema, rootKind, selectionSet, variableDefinitions, name) {
522
+ constructor(schema, rootKind, selectionSet, variableDefinitions, fragments, name) {
444
523
  this.schema = schema;
445
524
  this.rootKind = rootKind;
446
525
  this.selectionSet = selectionSet;
447
526
  this.variableDefinitions = variableDefinitions;
527
+ this.fragments = fragments;
448
528
  this.name = name;
449
529
  }
450
530
  optimize(fragments, minUsagesToOptimize = 2) {
451
- var _a;
452
531
  (0, utils_1.assert)(minUsagesToOptimize >= 1, `Expected 'minUsagesToOptimize' to be at least 1, but got ${minUsagesToOptimize}`);
453
532
  if (!fragments || fragments.isEmpty()) {
454
533
  return this;
@@ -457,50 +536,41 @@ class Operation {
457
536
  if (optimizedSelection === this.selectionSet) {
458
537
  return this;
459
538
  }
460
- const usages = new Map();
461
- optimizedSelection.collectUsedFragmentNames(usages);
462
- for (const fragment of fragments.names()) {
463
- if (!usages.has(fragment)) {
464
- usages.set(fragment, 0);
465
- }
466
- }
467
- const toDeoptimize = (0, utils_1.mapEntries)(usages).filter(([_, count]) => count < minUsagesToOptimize).map(([name]) => name);
468
- if (toDeoptimize.length > 0) {
469
- const newFragments = (_a = optimizedSelection.fragments) === null || _a === void 0 ? void 0 : _a.without(toDeoptimize);
470
- optimizedSelection = optimizedSelection.expandFragments(toDeoptimize, newFragments);
471
- optimizedSelection = optimizedSelection.trimUnsatisfiableBranches(optimizedSelection.parentType);
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);
472
542
  }
473
- return new Operation(this.schema, this.rootKind, optimizedSelection, this.variableDefinitions, this.name);
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);
474
546
  }
475
547
  expandAllFragments() {
476
- const expandedSelections = this.selectionSet.expandAllFragments();
548
+ const expandedSelections = this.selectionSet.expandFragments();
477
549
  if (expandedSelections === this.selectionSet) {
478
550
  return this;
479
551
  }
480
- return new Operation(this.schema, this.rootKind, expandedSelections, this.variableDefinitions, this.name);
552
+ return new Operation(this.schema, this.rootKind, expandedSelections, this.variableDefinitions, undefined, this.name);
481
553
  }
482
554
  trimUnsatisfiableBranches() {
483
555
  const trimmedSelections = this.selectionSet.trimUnsatisfiableBranches(this.selectionSet.parentType);
484
556
  if (trimmedSelections === this.selectionSet) {
485
557
  return this;
486
558
  }
487
- return new Operation(this.schema, this.rootKind, trimmedSelections, this.variableDefinitions, this.name);
559
+ return new Operation(this.schema, this.rootKind, trimmedSelections, this.variableDefinitions, this.fragments, this.name);
488
560
  }
489
561
  withoutDefer(labelsToRemove) {
490
- (0, utils_1.assert)(!this.selectionSet.fragments || this.selectionSet.fragments.isEmpty(), 'Removing @defer currently only work on "expanded" selections (no named fragments)');
491
562
  const updated = this.selectionSet.withoutDefer(labelsToRemove);
492
563
  return updated == this.selectionSet
493
564
  ? this
494
- : new Operation(this.schema, this.rootKind, updated, this.variableDefinitions, this.name);
565
+ : new Operation(this.schema, this.rootKind, updated, this.variableDefinitions, this.fragments, this.name);
495
566
  }
496
567
  withNormalizedDefer() {
497
- (0, utils_1.assert)(!this.selectionSet.fragments || this.selectionSet.fragments.isEmpty(), 'Assigning @defer lables currently only work on "expanded" selections (no named fragments)');
498
568
  const normalizer = new DeferNormalizer();
499
569
  const { hasDefers, hasNonLabelledOrConditionalDefers } = normalizer.init(this.selectionSet);
500
570
  let updatedOperation = this;
501
571
  if (hasNonLabelledOrConditionalDefers) {
502
572
  const updated = this.selectionSet.withNormalizedDefer(normalizer);
503
- updatedOperation = new Operation(this.schema, this.rootKind, updated, this.variableDefinitions, this.name);
573
+ updatedOperation = new Operation(this.schema, this.rootKind, updated, this.variableDefinitions, this.fragments, this.name);
504
574
  }
505
575
  return {
506
576
  operation: updatedOperation,
@@ -519,7 +589,7 @@ class Operation {
519
589
  return defaultedVariableValues;
520
590
  }
521
591
  toString(expandFragments = false, prettyPrint = true) {
522
- return this.selectionSet.toOperationString(this.rootKind, this.variableDefinitions, this.name, expandFragments, prettyPrint);
592
+ return this.selectionSet.toOperationString(this.rootKind, this.variableDefinitions, this.fragments, this.name, expandFragments, prettyPrint);
523
593
  }
524
594
  }
525
595
  exports.Operation = Operation;
@@ -528,7 +598,7 @@ class NamedFragmentDefinition extends definitions_1.DirectiveTargetElement {
528
598
  super(schema, directives);
529
599
  this.name = name;
530
600
  this.typeCondition = typeCondition;
531
- this.selectionSetsAtTypesCache = new Map();
601
+ this.expandedSelectionSetsAtTypesCache = new Map();
532
602
  }
533
603
  setSelectionSet(selectionSet) {
534
604
  (0, utils_1.assert)(!this._selectionSet, 'Attempting to set the selection set of a fragment definition already built');
@@ -540,11 +610,28 @@ class NamedFragmentDefinition extends definitions_1.DirectiveTargetElement {
540
610
  (0, utils_1.assert)(this._selectionSet, () => `Trying to access fragment definition ${this.name} before it is fully built`);
541
611
  return this._selectionSet;
542
612
  }
613
+ expandedSelectionSet() {
614
+ if (!this._expandedSelectionSet) {
615
+ this._expandedSelectionSet = this.selectionSet.expandFragments().trimUnsatisfiableBranches(this.typeCondition);
616
+ }
617
+ return this._expandedSelectionSet;
618
+ }
543
619
  withUpdatedSelectionSet(newSelectionSet) {
544
620
  return new NamedFragmentDefinition(this.schema(), this.name, this.typeCondition).setSelectionSet(newSelectionSet);
545
621
  }
622
+ fragmentUsages() {
623
+ if (!this._fragmentUsages) {
624
+ this._fragmentUsages = new Map();
625
+ this.selectionSet.collectUsedFragmentNames(this._fragmentUsages);
626
+ }
627
+ return this._fragmentUsages;
628
+ }
546
629
  collectUsedFragmentNames(collector) {
547
- this.selectionSet.collectUsedFragmentNames(collector);
630
+ const usages = this.fragmentUsages();
631
+ for (const [name, count] of usages.entries()) {
632
+ const prevCount = collector.get(name);
633
+ collector.set(name, prevCount ? prevCount + count : count);
634
+ }
548
635
  }
549
636
  toFragmentDefinitionNode() {
550
637
  return {
@@ -564,21 +651,49 @@ class NamedFragmentDefinition extends definitions_1.DirectiveTargetElement {
564
651
  };
565
652
  }
566
653
  canApplyAtType(type) {
567
- return (0, types_1.sameType)(type, this.typeCondition) || (0, definitions_1.runtimeTypesIntersects)(type, this.typeCondition);
654
+ if ((0, types_1.sameType)(type, this.typeCondition)) {
655
+ return true;
656
+ }
657
+ if (!(0, definitions_1.isAbstractType)(this.typeCondition)) {
658
+ return false;
659
+ }
660
+ const conditionRuntimes = (0, definitions_1.possibleRuntimeTypes)(this.typeCondition);
661
+ 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)));
568
664
  }
569
- selectionSetAtType(type) {
665
+ expandedSelectionSetAtType(type) {
666
+ const expandedSelectionSet = this.expandedSelectionSet();
570
667
  if ((0, types_1.sameType)(type, this.typeCondition) || (0, definitions_1.isObjectType)(this.typeCondition)) {
571
- return this.selectionSet;
668
+ return expandedSelectionSet;
572
669
  }
573
670
  if (!(0, definitions_1.isObjectType)(type)) {
574
- return this.selectionSet;
671
+ return expandedSelectionSet;
672
+ }
673
+ let selectionAtType = this.expandedSelectionSetsAtTypesCache.get(type.name);
674
+ if (!selectionAtType) {
675
+ selectionAtType = expandedSelectionSet.trimUnsatisfiableBranches(type, { recursive: false });
676
+ this.expandedSelectionSetsAtTypesCache.set(type.name, selectionAtType);
677
+ }
678
+ return selectionAtType;
679
+ }
680
+ includes(otherFragment) {
681
+ if (this.name === otherFragment) {
682
+ return false;
575
683
  }
576
- let selectionSet = this.selectionSetsAtTypesCache.get(type.name);
577
- if (!selectionSet) {
578
- selectionSet = this.selectionSet.trimUnsatisfiableBranches(type, { recursive: false });
579
- this.selectionSetsAtTypesCache.set(type.name, selectionSet);
684
+ if (!this._includedFragmentNames) {
685
+ this._includedFragmentNames = this.computeIncludedFragmentNames();
580
686
  }
581
- return selectionSet;
687
+ return this._includedFragmentNames.has(otherFragment);
688
+ }
689
+ computeIncludedFragmentNames() {
690
+ const included = new Set();
691
+ for (const selection of this.selectionSet.selections()) {
692
+ if (selection instanceof FragmentSpreadSelection) {
693
+ included.add(selection.namedFragment.name);
694
+ }
695
+ }
696
+ return included;
582
697
  }
583
698
  toString(indent) {
584
699
  return (indent !== null && indent !== void 0 ? indent : '') + `fragment ${this.name} on ${this.typeCondition}${this.appliedDirectivesToString()} ${this.selectionSet.toString(false, true, indent)}`;
@@ -590,7 +705,10 @@ class NamedFragments {
590
705
  this.fragments = new utils_1.MapWithCachedArrays();
591
706
  }
592
707
  isEmpty() {
593
- return this.fragments.size === 0;
708
+ return this.size === 0;
709
+ }
710
+ get size() {
711
+ return this.fragments.size;
594
712
  }
595
713
  names() {
596
714
  return this.fragments.keys();
@@ -609,22 +727,6 @@ class NamedFragments {
609
727
  maybeApplyingAtType(type) {
610
728
  return this.fragments.values().filter(f => f.canApplyAtType(type));
611
729
  }
612
- without(names) {
613
- if (!names.some(n => this.fragments.has(n))) {
614
- return this;
615
- }
616
- const newFragments = new NamedFragments();
617
- for (const fragment of this.fragments.values()) {
618
- if (!names.includes(fragment.name)) {
619
- const updatedSelectionSet = fragment.selectionSet.expandFragments(names, newFragments);
620
- const newFragment = updatedSelectionSet === fragment.selectionSet
621
- ? fragment
622
- : fragment.withUpdatedSelectionSet(updatedSelectionSet);
623
- newFragments.add(newFragment);
624
- }
625
- }
626
- return newFragments.isEmpty() ? undefined : newFragments;
627
- }
628
730
  get(name) {
629
731
  return this.fragments.get(name);
630
732
  }
@@ -641,45 +743,70 @@ class NamedFragments {
641
743
  }
642
744
  return mapped;
643
745
  }
644
- mapToExpandedSelectionSets(mapper, recreateFct = (f, s) => f.withUpdatedSelectionSet(s)) {
746
+ mapInDependencyOrder(mapper) {
645
747
  const fragmentsMap = new Map();
646
- const removedFragments = new Set();
647
748
  for (const fragment of this.definitions()) {
648
- const mappedSelectionSet = mapper(fragment.selectionSet.expandAllFragments().trimUnsatisfiableBranches(fragment.typeCondition));
649
- if (!mappedSelectionSet) {
650
- removedFragments.add(fragment.name);
651
- continue;
652
- }
653
- const otherFragmentsUsages = new Map();
654
- fragment.collectUsedFragmentNames(otherFragmentsUsages);
655
749
  fragmentsMap.set(fragment.name, {
656
- original: fragment,
657
- mappedSelectionSet,
658
- dependsOn: Array.from(otherFragmentsUsages.keys()),
750
+ fragment,
751
+ dependsOn: Array.from(fragment.fragmentUsages().keys()),
659
752
  });
660
753
  }
754
+ const removedFragments = new Set();
661
755
  const mappedFragments = new NamedFragments();
662
756
  while (fragmentsMap.size > 0) {
663
757
  for (const [name, info] of fragmentsMap) {
664
758
  if (info.dependsOn.every((n) => mappedFragments.has(n) || removedFragments.has(n))) {
665
- const reoptimizedSelectionSet = info.mappedSelectionSet.optimize(mappedFragments);
666
- mappedFragments.add(recreateFct(info.original, reoptimizedSelectionSet));
759
+ const mapped = mapper(info.fragment, mappedFragments);
667
760
  fragmentsMap.delete(name);
761
+ if (!mapped) {
762
+ removedFragments.add(name);
763
+ }
764
+ else {
765
+ mappedFragments.add(mapped);
766
+ }
767
+ break;
668
768
  }
669
769
  }
670
770
  }
671
771
  return mappedFragments.isEmpty() ? undefined : mappedFragments;
672
772
  }
773
+ mapToExpandedSelectionSets(mapper) {
774
+ return this.mapInDependencyOrder((fragment, newFragments) => {
775
+ const mappedSelectionSet = mapper(fragment.selectionSet.expandFragments().trimUnsatisfiableBranches(fragment.typeCondition));
776
+ if (!mappedSelectionSet) {
777
+ return undefined;
778
+ }
779
+ const reoptimizedSelectionSet = mappedSelectionSet.optimize(newFragments);
780
+ return fragment.withUpdatedSelectionSet(reoptimizedSelectionSet);
781
+ });
782
+ }
673
783
  rebaseOn(schema) {
674
- return this.mapToExpandedSelectionSets((s) => {
675
- const rebasedType = schema.type(s.parentType.name);
784
+ return this.mapInDependencyOrder((fragment, newFragments) => {
785
+ const rebasedType = schema.type(fragment.selectionSet.parentType.name);
676
786
  try {
677
- return rebasedType && (0, definitions_1.isCompositeType)(rebasedType) ? s.rebaseOn(rebasedType) : undefined;
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);
678
792
  }
679
793
  catch (e) {
680
794
  return undefined;
681
795
  }
682
- }, (orig, newSelection) => new NamedFragmentDefinition(schema, orig.name, newSelection.parentType).setSelectionSet(newSelection));
796
+ });
797
+ }
798
+ filter(predicate) {
799
+ return this.mapInDependencyOrder((fragment, newFragments) => {
800
+ if (predicate(fragment)) {
801
+ const updatedSelectionSet = fragment.selectionSet.expandFragments(newFragments);
802
+ return updatedSelectionSet === fragment.selectionSet
803
+ ? fragment
804
+ : fragment.withUpdatedSelectionSet(updatedSelectionSet);
805
+ }
806
+ else {
807
+ return undefined;
808
+ }
809
+ });
683
810
  }
684
811
  validate(variableDefinitions) {
685
812
  for (const fragment of this.fragments.values()) {
@@ -740,10 +867,15 @@ class DeferNormalizer {
740
867
  this.deferConditions.add(condition.name, label);
741
868
  }
742
869
  }
870
+ var ContainsResult;
871
+ (function (ContainsResult) {
872
+ ContainsResult[ContainsResult["NOT_CONTAINED"] = 0] = "NOT_CONTAINED";
873
+ ContainsResult[ContainsResult["STRICTLY_CONTAINED"] = 1] = "STRICTLY_CONTAINED";
874
+ ContainsResult[ContainsResult["EQUAL"] = 2] = "EQUAL";
875
+ })(ContainsResult = exports.ContainsResult || (exports.ContainsResult = {}));
743
876
  class SelectionSet {
744
- constructor(parentType, keyedSelections = new Map(), fragments) {
877
+ constructor(parentType, keyedSelections = new Map()) {
745
878
  this.parentType = parentType;
746
- this.fragments = fragments;
747
879
  this._keyedSelections = keyedSelections;
748
880
  this._selections = (0, utils_1.mapValues)(keyedSelections);
749
881
  }
@@ -799,21 +931,14 @@ class SelectionSet {
799
931
  const wrapped = new InlineFragmentSelection(new FragmentElement(this.parentType, this.parentType), this);
800
932
  const optimized = wrapped.optimize(fragments);
801
933
  return optimized instanceof FragmentSpreadSelection
802
- ? selectionSetOf(this.parentType, optimized, fragments)
934
+ ? selectionSetOf(this.parentType, optimized)
803
935
  : optimized.selectionSet;
804
936
  }
805
937
  optimizeSelections(fragments) {
806
- (0, utils_1.assert)(!this.fragments || this.fragments.isEmpty(), `Should not be called on selection that already has named fragments, but got ${this.fragments}`);
807
- return this.lazyMap((selection) => selection.optimize(fragments), { fragments });
808
- }
809
- expandAllFragments() {
810
- return this.lazyMap((selection) => selection.expandAllFragments(), { fragments: null });
938
+ return this.lazyMap((selection) => selection.optimize(fragments));
811
939
  }
812
- expandFragments(names, updatedFragments) {
813
- if (names.length === 0) {
814
- return this;
815
- }
816
- return this.lazyMap((selection) => selection.expandFragments(names, updatedFragments), { fragments: updatedFragments !== null && updatedFragments !== void 0 ? updatedFragments : null });
940
+ expandFragments(updatedFragments) {
941
+ return this.lazyMap((selection) => selection.expandFragments(updatedFragments));
817
942
  }
818
943
  trimUnsatisfiableBranches(parentType, options) {
819
944
  return this.lazyMap((selection) => selection.trimUnsatisfiableBranches(parentType, options), { parentType });
@@ -821,8 +946,6 @@ class SelectionSet {
821
946
  lazyMap(mapper, options) {
822
947
  var _a;
823
948
  const selections = this.selections();
824
- const updatedFragments = options === null || options === void 0 ? void 0 : options.fragments;
825
- const newFragments = updatedFragments === undefined ? this.fragments : (updatedFragments !== null && updatedFragments !== void 0 ? updatedFragments : undefined);
826
949
  let updatedSelections = undefined;
827
950
  for (let i = 0; i < selections.length; i++) {
828
951
  const selection = selections[i];
@@ -838,19 +961,14 @@ class SelectionSet {
838
961
  }
839
962
  }
840
963
  if (!updatedSelections) {
841
- return this.withUpdatedFragments(newFragments);
964
+ return this;
842
965
  }
843
- return updatedSelections.toSelectionSet((_a = options === null || options === void 0 ? void 0 : options.parentType) !== null && _a !== void 0 ? _a : this.parentType, newFragments);
844
- }
845
- withUpdatedFragments(newFragments) {
846
- return this.fragments === newFragments ? this : new SelectionSet(this.parentType, this._keyedSelections, newFragments);
966
+ return updatedSelections.toSelectionSet((_a = options === null || options === void 0 ? void 0 : options.parentType) !== null && _a !== void 0 ? _a : this.parentType);
847
967
  }
848
968
  withoutDefer(labelsToRemove) {
849
- (0, utils_1.assert)(!this.fragments, 'Not yet supported');
850
969
  return this.lazyMap((selection) => selection.withoutDefer(labelsToRemove));
851
970
  }
852
971
  withNormalizedDefer(normalizer) {
853
- (0, utils_1.assert)(!this.fragments, 'Not yet supported');
854
972
  return this.lazyMap((selection) => selection.withNormalizedDefer(normalizer));
855
973
  }
856
974
  hasDefer() {
@@ -863,15 +981,15 @@ class SelectionSet {
863
981
  const updated = this.filter((selection) => { var _a; return ((_a = selection.selectionSet) === null || _a === void 0 ? void 0 : _a.isEmpty()) !== true; });
864
982
  return updated.isEmpty() ? undefined : updated;
865
983
  }
866
- rebaseOn(parentType) {
984
+ rebaseOn(parentType, fragments) {
867
985
  if (this.parentType === parentType) {
868
986
  return this;
869
987
  }
870
988
  const newSelections = new Map();
871
989
  for (const selection of this.selections()) {
872
- newSelections.set(selection.key(), selection.rebaseOn(parentType));
990
+ newSelections.set(selection.key(), selection.rebaseOn(parentType, fragments));
873
991
  }
874
- return new SelectionSet(parentType, newSelections, this.fragments);
992
+ return new SelectionSet(parentType, newSelections);
875
993
  }
876
994
  equals(that) {
877
995
  if (this === that) {
@@ -888,51 +1006,30 @@ class SelectionSet {
888
1006
  }
889
1007
  return true;
890
1008
  }
891
- triviallyNestedSelectionsForKey(parentType, key) {
892
- const found = [];
893
- for (const selection of this.selections()) {
894
- if (selection.isUnecessaryInlineFragment(parentType)) {
895
- const selectionForKey = selection.selectionSet._keyedSelections.get(key);
896
- if (selectionForKey) {
897
- found.push(selectionForKey);
898
- }
899
- for (const nestedSelection of selection.selectionSet.triviallyNestedSelectionsForKey(parentType, key)) {
900
- found.push(nestedSelection);
901
- }
902
- }
903
- }
904
- return found;
905
- }
906
- mergeSameKeySelections(selections) {
907
- if (selections.length === 0) {
908
- return undefined;
909
- }
910
- const first = selections[0];
911
- if (!first.selectionSet || (first instanceof FragmentSpreadSelection) || selections.length === 1) {
912
- return first;
913
- }
914
- const mergedSubselections = new SelectionSetUpdates();
915
- for (const selection of selections) {
916
- mergedSubselections.add(selection.selectionSet);
917
- }
918
- return first.withUpdatedSelectionSet(mergedSubselections.toSelectionSet(first.selectionSet.parentType));
919
- }
920
1009
  contains(that) {
1010
+ if (that._selections.length > this._selections.length) {
1011
+ return ContainsResult.NOT_CONTAINED;
1012
+ }
1013
+ let isEqual = true;
921
1014
  for (const [key, thatSelection] of that._keyedSelections) {
922
1015
  const thisSelection = this._keyedSelections.get(key);
923
- const otherSelections = this.triviallyNestedSelectionsForKey(this.parentType, key);
924
- const mergedSelection = this.mergeSameKeySelections([thisSelection].concat(otherSelections).filter(utils_1.isDefined));
925
- if (!(mergedSelection && mergedSelection.contains(thatSelection))
926
- && !(thatSelection.isUnecessaryInlineFragment(this.parentType) && this.contains(thatSelection.selectionSet))) {
927
- return false;
1016
+ const selectionResult = thisSelection === null || thisSelection === void 0 ? void 0 : thisSelection.contains(thatSelection);
1017
+ if (selectionResult === undefined || selectionResult === ContainsResult.NOT_CONTAINED) {
1018
+ return ContainsResult.NOT_CONTAINED;
928
1019
  }
1020
+ isEqual && (isEqual = selectionResult === ContainsResult.EQUAL);
929
1021
  }
930
- return true;
1022
+ return isEqual && that._selections.length === this._selections.length
1023
+ ? ContainsResult.EQUAL
1024
+ : ContainsResult.STRICTLY_CONTAINED;
931
1025
  }
932
- diffWithNamedFragmentIfContained(candidate, parentType) {
933
- const that = candidate.selectionSetAtType(parentType);
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
+ }
934
1031
  if (this.contains(that)) {
935
- let updatedThis = this.expandFragments([candidate.name], this.fragments);
1032
+ let updatedThis = this.expandFragments(fragments.filter((f) => f.name !== candidate.name));
936
1033
  if (updatedThis !== this) {
937
1034
  updatedThis = updatedThis.trimUnsatisfiableBranches(parentType);
938
1035
  }
@@ -945,19 +1042,17 @@ class SelectionSet {
945
1042
  const updated = new SelectionSetUpdates();
946
1043
  for (const [key, thisSelection] of this._keyedSelections) {
947
1044
  const thatSelection = that._keyedSelections.get(key);
948
- const otherSelections = that.triviallyNestedSelectionsForKey(this.parentType, key);
949
- const allSelections = thatSelection ? [thatSelection].concat(otherSelections) : otherSelections;
950
- if (allSelections.length === 0) {
951
- updated.add(thisSelection);
1045
+ if (thatSelection) {
1046
+ const remainder = thisSelection.minus(thatSelection);
1047
+ if (remainder) {
1048
+ updated.add(remainder);
1049
+ }
952
1050
  }
953
1051
  else {
954
- const selectionDiff = allSelections.reduce((prev, val) => prev === null || prev === void 0 ? void 0 : prev.minus(val), thisSelection);
955
- if (selectionDiff) {
956
- updated.add(selectionDiff);
957
- }
1052
+ updated.add(thisSelection);
958
1053
  }
959
1054
  }
960
- return updated.toSelectionSet(this.parentType, this.fragments);
1055
+ return updated.toSelectionSet(this.parentType);
961
1056
  }
962
1057
  canRebaseOn(parentTypeToTest) {
963
1058
  return this.selections().every((selection) => selection.canAddTo(parentTypeToTest));
@@ -1027,10 +1122,10 @@ class SelectionSet {
1027
1122
  }
1028
1123
  return false;
1029
1124
  }
1030
- toOperationString(rootKind, variableDefinitions, operationName, expandFragments = false, prettyPrint = true) {
1125
+ toOperationString(rootKind, variableDefinitions, fragments, operationName, expandFragments = false, prettyPrint = true) {
1031
1126
  const indent = prettyPrint ? '' : undefined;
1032
- const fragmentsDefinitions = !expandFragments && this.fragments && !this.fragments.isEmpty()
1033
- ? this.fragments.toString(indent) + "\n\n"
1127
+ const fragmentsDefinitions = !expandFragments && fragments && !fragments.isEmpty()
1128
+ ? fragments.toString(indent) + "\n\n"
1034
1129
  : "";
1035
1130
  if (rootKind == "query" && !operationName && variableDefinitions.isEmpty()) {
1036
1131
  return fragmentsDefinitions + this.toString(expandFragments, true, indent);
@@ -1165,10 +1260,10 @@ function makeSelection(parentType, updates, fragments) {
1165
1260
  (0, utils_1.assert)(updates.length > 0, 'Should not be called without any updates');
1166
1261
  const first = updates[0];
1167
1262
  if (updates.length === 1 && first instanceof AbstractSelection) {
1168
- return first.rebaseOn(parentType);
1263
+ return first.rebaseOn(parentType, fragments);
1169
1264
  }
1170
1265
  const element = updateElement(first).rebaseOn(parentType);
1171
- const subSelectionParentType = element.kind === 'Field' ? (0, definitions_1.baseType)(element.definition.type) : element.castedType();
1266
+ const subSelectionParentType = element.kind === 'Field' ? element.baseType() : element.castedType();
1172
1267
  if (!(0, definitions_1.isCompositeType)(subSelectionParentType)) {
1173
1268
  return selectionOfElement(element);
1174
1269
  }
@@ -1204,7 +1299,7 @@ function makeSelectionSet(parentType, keyedUpdates, fragments) {
1204
1299
  for (const [key, updates] of keyedUpdates.entries()) {
1205
1300
  selections.set(key, makeSelection(parentType, updates, fragments));
1206
1301
  }
1207
- return new SelectionSet(parentType, selections, fragments);
1302
+ return new SelectionSet(parentType, selections);
1208
1303
  }
1209
1304
  class MutableSelectionSet {
1210
1305
  constructor(parentType, _updates, memoizer) {
@@ -1280,14 +1375,14 @@ function allFieldDefinitionsInSelectionSet(selection) {
1280
1375
  return allFields;
1281
1376
  }
1282
1377
  exports.allFieldDefinitionsInSelectionSet = allFieldDefinitionsInSelectionSet;
1283
- function selectionSetOf(parentType, selection, fragments) {
1378
+ function selectionSetOf(parentType, selection) {
1284
1379
  const map = new Map();
1285
1380
  map.set(selection.key(), selection);
1286
- return new SelectionSet(parentType, map, fragments);
1381
+ return new SelectionSet(parentType, map);
1287
1382
  }
1288
1383
  exports.selectionSetOf = selectionSetOf;
1289
- function selectionSetOfElement(element, subSelection, fragments) {
1290
- return selectionSetOf(element.parentType, selectionOfElement(element, subSelection), fragments);
1384
+ function selectionSetOfElement(element, subSelection) {
1385
+ return selectionSetOf(element.parentType, selectionOfElement(element, subSelection));
1291
1386
  }
1292
1387
  exports.selectionSetOfElement = selectionSetOfElement;
1293
1388
  function selectionOfElement(element, subSelection) {
@@ -1301,6 +1396,9 @@ class AbstractSelection {
1301
1396
  get parentType() {
1302
1397
  return this.element.parentType;
1303
1398
  }
1399
+ isTypenameField() {
1400
+ return false;
1401
+ }
1304
1402
  collectVariables(collector) {
1305
1403
  var _a;
1306
1404
  this.element.collectVariables(collector);
@@ -1310,10 +1408,6 @@ class AbstractSelection {
1310
1408
  var _a;
1311
1409
  (_a = this.selectionSet) === null || _a === void 0 ? void 0 : _a.collectUsedFragmentNames(collector);
1312
1410
  }
1313
- namedFragments() {
1314
- var _a;
1315
- return (_a = this.selectionSet) === null || _a === void 0 ? void 0 : _a.fragments;
1316
- }
1317
1411
  withUpdatedSelectionSet(selectionSet) {
1318
1412
  return this.withUpdatedComponents(this.element, selectionSet);
1319
1413
  }
@@ -1329,6 +1423,9 @@ class AbstractSelection {
1329
1423
  ? this.us()
1330
1424
  : this.withUpdatedSelectionSet(updatedSelectionSet);
1331
1425
  }
1426
+ isFragmentSpread() {
1427
+ return false;
1428
+ }
1332
1429
  minus(that) {
1333
1430
  if (this.selectionSet && that.selectionSet) {
1334
1431
  const updatedSubSelectionSet = this.selectionSet.minus(that.selectionSet);
@@ -1338,29 +1435,38 @@ class AbstractSelection {
1338
1435
  }
1339
1436
  return undefined;
1340
1437
  }
1341
- tryOptimizeSubselectionOnce(_) {
1342
- (0, utils_1.assert)(false, `UNSUPPORTED`);
1343
- }
1344
- tryOptimizeSubselectionWithFragments({ parentType, subSelection, fragments, fragmentFilter, }) {
1438
+ tryOptimizeSubselectionWithFragments({ parentType, subSelection, fragments, canUseFullMatchingFragment, }) {
1345
1439
  let candidates = fragments.maybeApplyingAtType(parentType);
1346
- if (fragmentFilter) {
1347
- candidates = candidates.filter(fragmentFilter);
1348
- }
1349
- let shouldTryAgain;
1350
- do {
1351
- const { spread, optimizedSelection, hasDiff } = this.tryOptimizeSubselectionOnce({ parentType, subSelection, candidates, fragments });
1352
- if (optimizedSelection) {
1353
- subSelection = optimizedSelection;
1440
+ const applyingFragments = [];
1441
+ for (const candidate of candidates) {
1442
+ const fragmentSSet = candidate.expandedSelectionSetAtType(parentType);
1443
+ if (fragmentSSet.isEmpty() || (fragmentSSet.selections().length === 1 && fragmentSSet.selections()[0].isTypenameField())) {
1444
+ continue;
1354
1445
  }
1355
- else if (spread) {
1356
- return spread;
1446
+ const res = subSelection.contains(fragmentSSet);
1447
+ if (res === ContainsResult.EQUAL) {
1448
+ if (canUseFullMatchingFragment(candidate)) {
1449
+ return candidate;
1450
+ }
1451
+ if (candidate.appliedDirectives.length === 0) {
1452
+ applyingFragments.push(candidate);
1453
+ }
1357
1454
  }
1358
- shouldTryAgain = !!spread && !!hasDiff;
1359
- if (shouldTryAgain) {
1360
- candidates = candidates.filter((c) => c !== (spread === null || spread === void 0 ? void 0 : spread.namedFragment));
1455
+ else if (res === ContainsResult.STRICTLY_CONTAINED && candidate.appliedDirectives.length === 0) {
1456
+ applyingFragments.push(candidate);
1361
1457
  }
1362
- } while (shouldTryAgain);
1363
- return subSelection;
1458
+ }
1459
+ if (applyingFragments.length === 0) {
1460
+ return subSelection;
1461
+ }
1462
+ const filteredApplyingFragments = applyingFragments.filter((f) => !applyingFragments.some((o) => o.includes(f.name)));
1463
+ let notCoveredByFragments = subSelection;
1464
+ const optimized = new SelectionSetUpdates();
1465
+ for (const fragment of filteredApplyingFragments) {
1466
+ notCoveredByFragments = notCoveredByFragments.minus(fragment.expandedSelectionSetAtType(parentType));
1467
+ optimized.add(new FragmentSpreadSelection(parentType, fragments, fragment, []));
1468
+ }
1469
+ return optimized.add(notCoveredByFragments).toSelectionSet(parentType, fragments);
1364
1470
  }
1365
1471
  }
1366
1472
  class FieldSelection extends AbstractSelection {
@@ -1375,6 +1481,9 @@ class FieldSelection extends AbstractSelection {
1375
1481
  us() {
1376
1482
  return this;
1377
1483
  }
1484
+ isTypenameField() {
1485
+ return this.element.definition.name === definitions_1.typenameFieldName;
1486
+ }
1378
1487
  withUpdatedComponents(field, selectionSet) {
1379
1488
  return new FieldSelection(field, selectionSet);
1380
1489
  }
@@ -1382,35 +1491,29 @@ class FieldSelection extends AbstractSelection {
1382
1491
  return this.element.key();
1383
1492
  }
1384
1493
  optimize(fragments) {
1385
- let optimizedSelection = this.selectionSet ? this.selectionSet.optimizeSelections(fragments) : undefined;
1386
1494
  const fieldBaseType = (0, definitions_1.baseType)(this.element.definition.type);
1387
- if ((0, definitions_1.isCompositeType)(fieldBaseType) && optimizedSelection) {
1495
+ if (!(0, definitions_1.isCompositeType)(fieldBaseType) || !this.selectionSet) {
1496
+ return this;
1497
+ }
1498
+ let optimizedSelection = this.selectionSet;
1499
+ if ((0, definitions_1.isCompositeType)(fieldBaseType) && this.selectionSet) {
1388
1500
  const optimized = this.tryOptimizeSubselectionWithFragments({
1389
1501
  parentType: fieldBaseType,
1390
- subSelection: optimizedSelection,
1502
+ subSelection: this.selectionSet,
1391
1503
  fragments,
1392
- fragmentFilter: (f) => f.appliedDirectives.length === 0,
1504
+ canUseFullMatchingFragment: (fragment) => fragment.appliedDirectives.length === 0,
1393
1505
  });
1394
- (0, utils_1.assert)(!(optimized instanceof FragmentSpreadSelection), 'tryOptimizeSubselectionOnce should never return only a spread');
1395
- optimizedSelection = optimized;
1506
+ if (optimized instanceof NamedFragmentDefinition) {
1507
+ optimizedSelection = selectionSetOf(fieldBaseType, new FragmentSpreadSelection(fieldBaseType, fragments, optimized, []));
1508
+ }
1509
+ else {
1510
+ optimizedSelection = optimized;
1511
+ }
1396
1512
  }
1513
+ optimizedSelection = optimizedSelection.optimize(fragments);
1397
1514
  return this.selectionSet === optimizedSelection
1398
1515
  ? this
1399
- : new FieldSelection(this.element, optimizedSelection);
1400
- }
1401
- tryOptimizeSubselectionOnce({ parentType, subSelection, candidates, fragments, }) {
1402
- let optimizedSelection = subSelection;
1403
- for (const candidate of candidates) {
1404
- const { contains, diff } = optimizedSelection.diffWithNamedFragmentIfContained(candidate, parentType);
1405
- if (contains) {
1406
- const spread = new FragmentSpreadSelection(parentType, fragments, candidate, []);
1407
- optimizedSelection = diff
1408
- ? new SelectionSetUpdates().add(spread).add(diff).toSelectionSet(parentType, fragments)
1409
- : selectionSetOf(parentType, spread);
1410
- return { spread, optimizedSelection, hasDiff: !!diff };
1411
- }
1412
- }
1413
- return {};
1516
+ : this.withUpdatedSelectionSet(optimizedSelection);
1414
1517
  }
1415
1518
  filter(predicate) {
1416
1519
  if (!this.selectionSet) {
@@ -1428,7 +1531,7 @@ class FieldSelection extends AbstractSelection {
1428
1531
  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);
1429
1532
  (_a = this.selectionSet) === null || _a === void 0 ? void 0 : _a.validate(variableDefinitions);
1430
1533
  }
1431
- rebaseOn(parentType) {
1534
+ rebaseOn(parentType, fragments) {
1432
1535
  if (this.element.parentType === parentType) {
1433
1536
  return this;
1434
1537
  }
@@ -1436,12 +1539,12 @@ class FieldSelection extends AbstractSelection {
1436
1539
  if (!this.selectionSet) {
1437
1540
  return this.withUpdatedElement(rebasedElement);
1438
1541
  }
1439
- const rebasedBase = (0, definitions_1.baseType)(rebasedElement.definition.type);
1542
+ const rebasedBase = rebasedElement.baseType();
1440
1543
  if (rebasedBase === this.selectionSet.parentType) {
1441
1544
  return this.withUpdatedElement(rebasedElement);
1442
1545
  }
1443
1546
  validate((0, definitions_1.isCompositeType)(rebasedBase), () => `Cannot rebase field selection ${this} on ${parentType}: rebased field base return type ${rebasedBase} is not composite`);
1444
- return this.withUpdatedComponents(rebasedElement, this.selectionSet.rebaseOn(rebasedBase));
1547
+ return this.withUpdatedComponents(rebasedElement, this.selectionSet.rebaseOn(rebasedBase, fragments));
1445
1548
  }
1446
1549
  canAddTo(parentType) {
1447
1550
  if (this.element.parentType === parentType) {
@@ -1483,15 +1586,12 @@ class FieldSelection extends AbstractSelection {
1483
1586
  var _a;
1484
1587
  return !!((_a = this.selectionSet) === null || _a === void 0 ? void 0 : _a.hasDefer());
1485
1588
  }
1486
- expandAllFragments() {
1487
- return this.mapToSelectionSet((s) => s.expandAllFragments());
1488
- }
1489
1589
  trimUnsatisfiableBranches(_, options) {
1490
1590
  var _a, _b;
1491
1591
  if (!this.selectionSet) {
1492
1592
  return this;
1493
1593
  }
1494
- const base = (0, definitions_1.baseType)(this.element.definition.type);
1594
+ const base = this.element.baseType();
1495
1595
  (0, utils_1.assert)((0, definitions_1.isCompositeType)(base), () => `Field ${this.element} should not have a sub-selection`);
1496
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;
1497
1597
  if ((_b = trimmed.selectionSet) === null || _b === void 0 ? void 0 : _b.isEmpty()) {
@@ -1501,8 +1601,8 @@ class FieldSelection extends AbstractSelection {
1501
1601
  return trimmed;
1502
1602
  }
1503
1603
  }
1504
- expandFragments(names, updatedFragments) {
1505
- return this.mapToSelectionSet((s) => s.expandFragments(names, updatedFragments));
1604
+ expandFragments(updatedFragments) {
1605
+ return this.mapToSelectionSet((s) => s.expandFragments(updatedFragments));
1506
1606
  }
1507
1607
  equals(that) {
1508
1608
  if (this === that) {
@@ -1518,15 +1618,14 @@ class FieldSelection extends AbstractSelection {
1518
1618
  }
1519
1619
  contains(that) {
1520
1620
  if (!(that instanceof FieldSelection) || !this.element.equals(that.element)) {
1521
- return false;
1621
+ return ContainsResult.NOT_CONTAINED;
1522
1622
  }
1523
- if (!that.selectionSet) {
1524
- return true;
1623
+ if (!this.selectionSet) {
1624
+ (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');
1625
+ return ContainsResult.EQUAL;
1525
1626
  }
1526
- return !!this.selectionSet && this.selectionSet.contains(that.selectionSet);
1527
- }
1528
- isUnecessaryInlineFragment(_) {
1529
- return false;
1627
+ (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);
1530
1629
  }
1531
1630
  toString(expandFragments = true, indent) {
1532
1631
  return (indent !== null && indent !== void 0 ? indent : '') + this.element + (this.selectionSet ? ' ' + this.selectionSet.toString(expandFragments, true, indent) : '');
@@ -1559,12 +1658,6 @@ class FragmentSelection extends AbstractSelection {
1559
1658
  hasDefer() {
1560
1659
  return this.element.hasDefer() || this.selectionSet.hasDefer();
1561
1660
  }
1562
- isUnecessaryInlineFragment(parentType) {
1563
- return this.element.appliedDirectives.length === 0
1564
- && !!this.element.typeCondition
1565
- && (this.element.typeCondition.name === parentType.name
1566
- || ((0, definitions_1.isObjectType)(parentType) && (0, definitions_1.possibleRuntimeTypes)(this.element.typeCondition).some((t) => t.name === parentType.name)));
1567
- }
1568
1661
  }
1569
1662
  exports.FragmentSelection = FragmentSelection;
1570
1663
  class InlineFragmentSelection extends FragmentSelection {
@@ -1586,7 +1679,7 @@ class InlineFragmentSelection extends FragmentSelection {
1586
1679
  validate(!this.selectionSet.isEmpty(), () => `Invalid empty selection set for fragment "${this.element}"`);
1587
1680
  this.selectionSet.validate(variableDefinitions);
1588
1681
  }
1589
- rebaseOn(parentType) {
1682
+ rebaseOn(parentType, fragments) {
1590
1683
  if (this.parentType === parentType) {
1591
1684
  return this;
1592
1685
  }
@@ -1595,7 +1688,7 @@ class InlineFragmentSelection extends FragmentSelection {
1595
1688
  if (rebasedCastedType === this.selectionSet.parentType) {
1596
1689
  return this.withUpdatedElement(rebasedFragment);
1597
1690
  }
1598
- return this.withUpdatedComponents(rebasedFragment, this.selectionSet.rebaseOn(rebasedCastedType));
1691
+ return this.withUpdatedComponents(rebasedFragment, this.selectionSet.rebaseOn(rebasedCastedType, fragments));
1599
1692
  }
1600
1693
  canAddTo(parentType) {
1601
1694
  if (this.element.parentType === parentType) {
@@ -1628,52 +1721,39 @@ class InlineFragmentSelection extends FragmentSelection {
1628
1721
  };
1629
1722
  }
1630
1723
  optimize(fragments) {
1631
- let optimizedSelection = this.selectionSet.optimizeSelections(fragments);
1724
+ let optimizedSelection = this.selectionSet;
1632
1725
  const typeCondition = this.element.typeCondition;
1633
1726
  if (typeCondition) {
1634
1727
  const optimized = this.tryOptimizeSubselectionWithFragments({
1635
1728
  parentType: typeCondition,
1636
1729
  subSelection: optimizedSelection,
1637
1730
  fragments,
1731
+ canUseFullMatchingFragment: (fragment) => {
1732
+ return fragment.appliedDirectives.length === 0
1733
+ || ((0, types_1.sameType)(typeCondition, fragment.typeCondition)
1734
+ && fragment.appliedDirectives.every((d) => this.element.appliedDirectives.some((s) => (0, definitions_1.sameDirectiveApplication)(d, s))));
1735
+ },
1638
1736
  });
1639
- if (optimized instanceof FragmentSpreadSelection) {
1640
- return optimized;
1641
- }
1642
- optimizedSelection = optimized;
1643
- }
1644
- return this.selectionSet === optimizedSelection
1645
- ? this
1646
- : new InlineFragmentSelection(this.element, optimizedSelection);
1647
- }
1648
- tryOptimizeSubselectionOnce({ parentType, subSelection, candidates, fragments, }) {
1649
- let optimizedSelection = subSelection;
1650
- for (const candidate of candidates) {
1651
- const { contains, diff } = optimizedSelection.diffWithNamedFragmentIfContained(candidate, parentType);
1652
- if (contains) {
1653
- if (!diff && (0, types_1.sameType)(this.element.typeCondition, candidate.typeCondition)) {
1737
+ if (optimized instanceof NamedFragmentDefinition) {
1738
+ if ((0, types_1.sameType)(typeCondition, optimized.typeCondition)) {
1654
1739
  let spreadDirectives = this.element.appliedDirectives;
1655
- if (candidate.appliedDirectives.length > 0) {
1656
- const { isSubset, difference } = diffDirectives(this.element.appliedDirectives, candidate.appliedDirectives);
1657
- if (!isSubset) {
1658
- continue;
1659
- }
1660
- spreadDirectives = difference;
1740
+ if (optimized.appliedDirectives) {
1741
+ spreadDirectives = spreadDirectives.filter((s) => !optimized.appliedDirectives.some((d) => (0, definitions_1.sameDirectiveApplication)(d, s)));
1661
1742
  }
1662
- return {
1663
- spread: new FragmentSpreadSelection(this.parentType, fragments, candidate, spreadDirectives),
1664
- };
1743
+ return new FragmentSpreadSelection(this.parentType, fragments, optimized, spreadDirectives);
1665
1744
  }
1666
- if (candidate.appliedDirectives.length > 0) {
1667
- continue;
1745
+ else {
1746
+ optimizedSelection = selectionSetOf(typeCondition, new FragmentSpreadSelection(typeCondition, fragments, optimized, []));
1668
1747
  }
1669
- const spread = new FragmentSpreadSelection(parentType, fragments, candidate, []);
1670
- optimizedSelection = diff
1671
- ? new SelectionSetUpdates().add(spread).add(diff).toSelectionSet(parentType, fragments)
1672
- : selectionSetOf(parentType, spread);
1673
- return { spread, optimizedSelection, hasDiff: !!diff };
1748
+ }
1749
+ else {
1750
+ optimizedSelection = optimized;
1674
1751
  }
1675
1752
  }
1676
- return {};
1753
+ optimizedSelection = optimizedSelection.optimizeSelections(fragments);
1754
+ return this.selectionSet === optimizedSelection
1755
+ ? this
1756
+ : new InlineFragmentSelection(this.element, optimizedSelection);
1677
1757
  }
1678
1758
  withoutDefer(labelsToRemove) {
1679
1759
  const newSelection = this.selectionSet.withoutDefer(labelsToRemove);
@@ -1753,11 +1833,8 @@ class InlineFragmentSelection extends FragmentSelection {
1753
1833
  }
1754
1834
  return this.selectionSet === trimmedSelectionSet ? this : this.withUpdatedSelectionSet(trimmedSelectionSet);
1755
1835
  }
1756
- expandAllFragments() {
1757
- return this.mapToSelectionSet((s) => s.expandAllFragments());
1758
- }
1759
- expandFragments(names, updatedFragments) {
1760
- return this.mapToSelectionSet((s) => s.expandFragments(names, updatedFragments));
1836
+ expandFragments(updatedFragments) {
1837
+ return this.mapToSelectionSet((s) => s.expandFragments(updatedFragments));
1761
1838
  }
1762
1839
  equals(that) {
1763
1840
  if (this === that) {
@@ -1768,22 +1845,15 @@ class InlineFragmentSelection extends FragmentSelection {
1768
1845
  && this.selectionSet.equals(that.selectionSet);
1769
1846
  }
1770
1847
  contains(that) {
1771
- return (that instanceof FragmentSelection)
1772
- && this.element.equals(that.element)
1773
- && this.selectionSet.contains(that.selectionSet);
1848
+ if (!(that instanceof FragmentSelection) || !this.element.equals(that.element)) {
1849
+ return ContainsResult.NOT_CONTAINED;
1850
+ }
1851
+ return this.selectionSet.contains(that.selectionSet);
1774
1852
  }
1775
1853
  toString(expandFragments = true, indent) {
1776
1854
  return (indent !== null && indent !== void 0 ? indent : '') + this.element + ' ' + this.selectionSet.toString(expandFragments, true, indent);
1777
1855
  }
1778
1856
  }
1779
- function diffDirectives(superset, maybeSubset) {
1780
- if (maybeSubset.every((d) => superset.some((s) => (0, definitions_1.sameDirectiveApplication)(d, s)))) {
1781
- return { isSubset: true, difference: superset.filter((s) => !maybeSubset.some((d) => (0, definitions_1.sameDirectiveApplication)(d, s))) };
1782
- }
1783
- else {
1784
- return { isSubset: false, difference: [] };
1785
- }
1786
- }
1787
1857
  class FragmentSpreadSelection extends FragmentSelection {
1788
1858
  constructor(sourceType, fragments, namedFragment, spreadDirectives) {
1789
1859
  super(new FragmentElement(sourceType, namedFragment.typeCondition, namedFragment.appliedDirectives.concat(spreadDirectives)));
@@ -1791,6 +1861,9 @@ class FragmentSpreadSelection extends FragmentSelection {
1791
1861
  this.namedFragment = namedFragment;
1792
1862
  this.spreadDirectives = spreadDirectives;
1793
1863
  }
1864
+ isFragmentSpread() {
1865
+ return true;
1866
+ }
1794
1867
  get selectionSet() {
1795
1868
  return this.namedFragment.selectionSet;
1796
1869
  }
@@ -1803,11 +1876,9 @@ class FragmentSpreadSelection extends FragmentSelection {
1803
1876
  withUpdatedComponents(_fragment, _selectionSet) {
1804
1877
  (0, utils_1.assert)(false, `Unsupported`);
1805
1878
  }
1806
- trimUnsatisfiableBranches(_) {
1807
- return this;
1808
- }
1809
- namedFragments() {
1810
- return this.fragments;
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);
1811
1882
  }
1812
1883
  validate() {
1813
1884
  this.validateDeferAndStream();
@@ -1834,29 +1905,29 @@ class FragmentSpreadSelection extends FragmentSelection {
1834
1905
  optimize(_) {
1835
1906
  return this;
1836
1907
  }
1837
- rebaseOn(_parentType) {
1838
- return this;
1908
+ rebaseOn(parentType, fragments) {
1909
+ if (this.parentType === parentType) {
1910
+ return this;
1911
+ }
1912
+ (0, utils_1.assert)(fragments || this.parentType.schema() === parentType.schema(), `Must provide fragments is rebasing on other schema`);
1913
+ const newFragments = fragments !== null && fragments !== void 0 ? fragments : this.fragments;
1914
+ 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`);
1916
+ return new FragmentSpreadSelection(parentType, newFragments, namedFragment, this.spreadDirectives);
1839
1917
  }
1840
1918
  canAddTo(_) {
1841
1919
  return true;
1842
1920
  }
1843
- expandAllFragments() {
1844
- const expandedSubSelections = this.selectionSet.expandAllFragments();
1845
- return (0, types_1.sameType)(this.parentType, this.namedFragment.typeCondition) && this.element.appliedDirectives.length === 0
1846
- ? expandedSubSelections.selections()
1847
- : new InlineFragmentSelection(this.element, expandedSubSelections);
1848
- }
1849
- expandFragments(names, updatedFragments) {
1850
- if (!names.includes(this.namedFragment.name)) {
1921
+ expandFragments(updatedFragments) {
1922
+ if (updatedFragments === null || updatedFragments === void 0 ? void 0 : updatedFragments.has(this.namedFragment.name)) {
1851
1923
  return this;
1852
1924
  }
1853
- const expandedSubSelections = this.selectionSet.expandFragments(names, updatedFragments);
1925
+ const expandedSubSelections = this.selectionSet.expandFragments(updatedFragments);
1854
1926
  return (0, types_1.sameType)(this.parentType, this.namedFragment.typeCondition) && this.element.appliedDirectives.length === 0
1855
1927
  ? expandedSubSelections.selections()
1856
1928
  : new InlineFragmentSelection(this.element, expandedSubSelections);
1857
1929
  }
1858
1930
  collectUsedFragmentNames(collector) {
1859
- this.selectionSet.collectUsedFragmentNames(collector);
1860
1931
  const usageCount = collector.get(this.namedFragment.name);
1861
1932
  collector.set(this.namedFragment.name, usageCount === undefined ? 1 : usageCount + 1);
1862
1933
  }
@@ -1880,11 +1951,12 @@ class FragmentSpreadSelection extends FragmentSelection {
1880
1951
  }
1881
1952
  contains(that) {
1882
1953
  if (this.equals(that)) {
1883
- return true;
1954
+ return ContainsResult.EQUAL;
1884
1955
  }
1885
- return (that instanceof FragmentSelection)
1886
- && this.element.equals(that.element)
1887
- && this.selectionSet.contains(that.selectionSet);
1956
+ if (!(that instanceof FragmentSelection) || !this.element.equals(that.element)) {
1957
+ return ContainsResult.NOT_CONTAINED;
1958
+ }
1959
+ return this.selectionSet.contains(that.selectionSet);
1888
1960
  }
1889
1961
  toString(expandFragments = true, indent) {
1890
1962
  if (expandFragments) {
@@ -1899,7 +1971,7 @@ class FragmentSpreadSelection extends FragmentSelection {
1899
1971
  }
1900
1972
  function selectionSetOfNode(parentType, node, variableDefinitions, fragments, fieldAccessor = (type, name) => type.field(name)) {
1901
1973
  if (node.selections.length === 1) {
1902
- return selectionSetOf(parentType, selectionOfNode(parentType, node.selections[0], variableDefinitions, fragments, fieldAccessor), fragments);
1974
+ return selectionSetOf(parentType, selectionOfNode(parentType, node.selections[0], variableDefinitions, fragments, fieldAccessor));
1903
1975
  }
1904
1976
  const selections = new SelectionSetUpdates();
1905
1977
  for (const selectionNode of node.selections) {
@@ -1990,13 +2062,14 @@ function operationFromAST({ schema, operation, variableDefinitions, fragments, v
1990
2062
  var _a;
1991
2063
  const rootType = schema.schemaDefinition.root(operation.operation);
1992
2064
  validate(rootType, () => `The schema has no "${operation.operation}" root type defined`);
2065
+ const fragmentsIfAny = fragments.isEmpty() ? undefined : fragments;
1993
2066
  return new Operation(schema, operation.operation, parseSelectionSet({
1994
2067
  parentType: rootType.type,
1995
2068
  source: operation.selectionSet,
1996
2069
  variableDefinitions,
1997
- fragments: fragments.isEmpty() ? undefined : fragments,
2070
+ fragments: fragmentsIfAny,
1998
2071
  validate: validateInput,
1999
- }), variableDefinitions, (_a = operation.name) === null || _a === void 0 ? void 0 : _a.value);
2072
+ }), variableDefinitions, fragmentsIfAny, (_a = operation.name) === null || _a === void 0 ? void 0 : _a.value);
2000
2073
  }
2001
2074
  function parseOperation(schema, operation, options) {
2002
2075
  return operationFromDocument(schema, (0, graphql_1.parse)(operation), options);
@@ -2028,8 +2101,8 @@ function operationToDocument(operation) {
2028
2101
  selectionSet: operation.selectionSet.toSelectionSetNode(),
2029
2102
  variableDefinitions: operation.variableDefinitions.toVariableDefinitionNodes(),
2030
2103
  };
2031
- const fragmentASTs = operation.selectionSet.fragments
2032
- ? (_a = operation.selectionSet.fragments) === null || _a === void 0 ? void 0 : _a.toFragmentDefinitionNodes()
2104
+ const fragmentASTs = operation.fragments
2105
+ ? (_a = operation.fragments) === null || _a === void 0 ? void 0 : _a.toFragmentDefinitionNodes()
2033
2106
  : [];
2034
2107
  return {
2035
2108
  kind: graphql_1.Kind.DOCUMENT,