@apollo/federation-internals 2.4.9 → 2.4.10
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.
- package/dist/operations.d.ts +9 -43
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +44 -97
- package/dist/operations.js.map +1 -1
- package/package.json +1 -1
- package/src/operations.ts +62 -182
package/src/operations.ts
CHANGED
|
@@ -85,11 +85,7 @@ abstract class AbstractOperationElement<T extends AbstractOperationElement<T>> e
|
|
|
85
85
|
|
|
86
86
|
abstract asPathElement(): string | undefined;
|
|
87
87
|
|
|
88
|
-
abstract rebaseOn(
|
|
89
|
-
|
|
90
|
-
rebaseOnOrError(parentType: CompositeType): T {
|
|
91
|
-
return this.rebaseOn({ parentType, errorIfCannotRebase: true })!;
|
|
92
|
-
}
|
|
88
|
+
abstract rebaseOn(parentType: CompositeType): T;
|
|
93
89
|
|
|
94
90
|
abstract withUpdatedDirectives(newDirectives: readonly Directive<any>[]): T;
|
|
95
91
|
|
|
@@ -294,7 +290,7 @@ export class Field<TArgs extends {[key: string]: any} = {[key: string]: any}> ex
|
|
|
294
290
|
}
|
|
295
291
|
}
|
|
296
292
|
|
|
297
|
-
rebaseOn(
|
|
293
|
+
rebaseOn(parentType: CompositeType): Field<TArgs> {
|
|
298
294
|
const fieldParent = this.definition.parent;
|
|
299
295
|
if (parentType === fieldParent) {
|
|
300
296
|
return this;
|
|
@@ -304,16 +300,12 @@ export class Field<TArgs extends {[key: string]: any} = {[key: string]: any}> ex
|
|
|
304
300
|
return this.withUpdatedDefinition(parentType.typenameField()!);
|
|
305
301
|
}
|
|
306
302
|
|
|
303
|
+
validate(
|
|
304
|
+
this.canRebaseOn(parentType),
|
|
305
|
+
() => `Cannot add selection of field "${this.definition.coordinate}" to selection set of parent type "${parentType}"`
|
|
306
|
+
);
|
|
307
307
|
const fieldDef = parentType.field(this.name);
|
|
308
|
-
|
|
309
|
-
if (!canRebase) {
|
|
310
|
-
validate(
|
|
311
|
-
!errorIfCannotRebase,
|
|
312
|
-
() => `Cannot add selection of field "${this.definition.coordinate}" to selection set of parent type "${parentType}"`
|
|
313
|
-
);
|
|
314
|
-
return undefined;
|
|
315
|
-
}
|
|
316
|
-
|
|
308
|
+
validate(fieldDef, () => `Cannot add selection of field "${this.definition.coordinate}" to selection set of parent type "${parentType}" (that does not declare that field)`);
|
|
317
309
|
return this.withUpdatedDefinition(fieldDef);
|
|
318
310
|
}
|
|
319
311
|
|
|
@@ -474,7 +466,7 @@ export class FragmentElement extends AbstractOperationElement<FragmentElement> {
|
|
|
474
466
|
return newFragment;
|
|
475
467
|
}
|
|
476
468
|
|
|
477
|
-
rebaseOn(
|
|
469
|
+
rebaseOn(parentType: CompositeType): FragmentElement {
|
|
478
470
|
const fragmentParent = this.parentType;
|
|
479
471
|
const typeCondition = this.typeCondition;
|
|
480
472
|
if (parentType === fragmentParent) {
|
|
@@ -485,13 +477,10 @@ export class FragmentElement extends AbstractOperationElement<FragmentElement> {
|
|
|
485
477
|
// to update the source type of the fragment, but also "rebase" the condition to the selection set
|
|
486
478
|
// schema.
|
|
487
479
|
const { canRebase, rebasedCondition } = this.canRebaseOn(parentType);
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
);
|
|
493
|
-
return undefined;
|
|
494
|
-
}
|
|
480
|
+
validate(
|
|
481
|
+
canRebase,
|
|
482
|
+
() => `Cannot add fragment of condition "${typeCondition}" (runtimes: [${possibleRuntimeTypes(typeCondition!)}]) to parent type "${parentType}" (runtimes: ${possibleRuntimeTypes(parentType)})`
|
|
483
|
+
);
|
|
495
484
|
return this.withUpdatedTypes(parentType, rebasedCondition);
|
|
496
485
|
}
|
|
497
486
|
|
|
@@ -708,7 +697,7 @@ export type RootOperationPath = {
|
|
|
708
697
|
path: OperationPath
|
|
709
698
|
}
|
|
710
699
|
|
|
711
|
-
// Computes for every fragment, which other fragments use it (so the reverse of it's dependencies, the other fragment it uses).
|
|
700
|
+
// Computes for every fragment, which other fragments use it (so the reverse of it's dependencies, the other fragment it uses).
|
|
712
701
|
function computeFragmentsDependents(fragments: NamedFragments): SetMultiMap<string, string> {
|
|
713
702
|
const reverseDeps = new SetMultiMap<string, string>();
|
|
714
703
|
for (const fragment of fragments.definitions()) {
|
|
@@ -1243,7 +1232,7 @@ export class NamedFragmentDefinition extends DirectiveTargetElement<NamedFragmen
|
|
|
1243
1232
|
}
|
|
1244
1233
|
|
|
1245
1234
|
toString(indent?: string): string {
|
|
1246
|
-
return `fragment ${this.name} on ${this.typeCondition}${this.appliedDirectivesToString()} ${this.selectionSet.toString(false, true, indent)}`;
|
|
1235
|
+
return (indent ?? '') + `fragment ${this.name} on ${this.typeCondition}${this.appliedDirectivesToString()} ${this.selectionSet.toString(false, true, indent)}`;
|
|
1247
1236
|
}
|
|
1248
1237
|
}
|
|
1249
1238
|
|
|
@@ -1374,39 +1363,21 @@ export class NamedFragments {
|
|
|
1374
1363
|
});
|
|
1375
1364
|
}
|
|
1376
1365
|
|
|
1377
|
-
// When we rebase named fragments on a subgraph schema, only a subset of what the fragment handles may belong
|
|
1378
|
-
// to that particular subgraph. And there are a few sub-cases where that subset is such that we basically need or
|
|
1379
|
-
// want to consider to ignore the fragment for that subgraph, and that is when:
|
|
1380
|
-
// 1. the subset that apply is actually empty. The fragment wouldn't be valid in this case anyway.
|
|
1381
|
-
// 2. the subset is a single leaf field: in that case, using the one field directly is just shorter than using
|
|
1382
|
-
// the fragment, so we consider the fragment don't really apply to that subgraph. Technically, using the
|
|
1383
|
-
// fragment could still be of value if the fragment name is a lot smaller than the one field name, but it's
|
|
1384
|
-
// enough of a niche case that we ignore it. Note in particular that one sub-case of this rule that is likely
|
|
1385
|
-
// to be common is when the subset ends up being just `__typename`: this would basically mean the fragment
|
|
1386
|
-
// don't really apply to the subgraph, and that this will ensure this is the case.
|
|
1387
|
-
private selectionSetIsWorthUsing(selectionSet: SelectionSet): boolean {
|
|
1388
|
-
const selections = selectionSet.selections();
|
|
1389
|
-
if (selections.length === 0) {
|
|
1390
|
-
return false;
|
|
1391
|
-
}
|
|
1392
|
-
if (selections.length === 1) {
|
|
1393
|
-
const s = selections[0];
|
|
1394
|
-
return !(s.kind === 'FieldSelection' && s.element.isLeafField());
|
|
1395
|
-
}
|
|
1396
|
-
return true;
|
|
1397
|
-
}
|
|
1398
|
-
|
|
1399
1366
|
rebaseOn(schema: Schema): NamedFragments | undefined {
|
|
1400
1367
|
return this.mapInDependencyOrder((fragment, newFragments) => {
|
|
1401
1368
|
const rebasedType = schema.type(fragment.selectionSet.parentType.name);
|
|
1402
|
-
|
|
1369
|
+
try {
|
|
1370
|
+
if (!rebasedType || !isCompositeType(rebasedType)) {
|
|
1371
|
+
return undefined;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
const rebasedSelection = fragment.selectionSet.rebaseOn(rebasedType, newFragments);
|
|
1375
|
+
return new NamedFragmentDefinition(schema, fragment.name, rebasedType).setSelectionSet(rebasedSelection);
|
|
1376
|
+
} catch (e) {
|
|
1377
|
+
// This means we cannot rebase this selection on the schema and thus cannot reuse that fragment on that
|
|
1378
|
+
// particular schema.
|
|
1403
1379
|
return undefined;
|
|
1404
1380
|
}
|
|
1405
|
-
|
|
1406
|
-
const rebasedSelection = fragment.selectionSet.rebaseOn({ parentType: rebasedType, fragments: newFragments, errorIfCannotRebase: false });
|
|
1407
|
-
return this.selectionSetIsWorthUsing(rebasedSelection)
|
|
1408
|
-
? new NamedFragmentDefinition(schema, fragment.name, rebasedType).setSelectionSet(rebasedSelection)
|
|
1409
|
-
: undefined;;
|
|
1410
1381
|
});
|
|
1411
1382
|
}
|
|
1412
1383
|
|
|
@@ -1505,7 +1476,7 @@ class DeferNormalizer {
|
|
|
1505
1476
|
}
|
|
1506
1477
|
|
|
1507
1478
|
export enum ContainsResult {
|
|
1508
|
-
// Note: enum values are numbers in the end, and 0 means false in JS, so we should keep `NOT_CONTAINED` first
|
|
1479
|
+
// Note: enum values are numbers in the end, and 0 means false in JS, so we should keep `NOT_CONTAINED` first
|
|
1509
1480
|
// so that using the result of `contains` as a boolean works.
|
|
1510
1481
|
NOT_CONTAINED,
|
|
1511
1482
|
STRICTLY_CONTAINED,
|
|
@@ -1544,20 +1515,6 @@ export class SelectionSet {
|
|
|
1544
1515
|
return this._keyedSelections.has(typenameFieldName);
|
|
1545
1516
|
}
|
|
1546
1517
|
|
|
1547
|
-
withoutTopLevelTypenameField(): SelectionSet {
|
|
1548
|
-
if (!this.hasTopLevelTypenameField) {
|
|
1549
|
-
return this;
|
|
1550
|
-
}
|
|
1551
|
-
|
|
1552
|
-
const newKeyedSelections = new Map<string, Selection>();
|
|
1553
|
-
for (const [key, selection] of this._keyedSelections) {
|
|
1554
|
-
if (key !== typenameFieldName) {
|
|
1555
|
-
newKeyedSelections.set(key, selection);
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
return new SelectionSet(this.parentType, newKeyedSelections);
|
|
1559
|
-
}
|
|
1560
|
-
|
|
1561
1518
|
fieldsInSet(): CollectedFieldsInSet {
|
|
1562
1519
|
const fields = new Array<{ path: string[], field: FieldSelection }>();
|
|
1563
1520
|
for (const selection of this.selections()) {
|
|
@@ -1652,7 +1609,7 @@ export class SelectionSet {
|
|
|
1652
1609
|
}
|
|
1653
1610
|
|
|
1654
1611
|
/**
|
|
1655
|
-
* Applies some normalization rules to this selection set in the context of the provided `parentType`.
|
|
1612
|
+
* Applies some normalization rules to this selection set in the context of the provided `parentType`.
|
|
1656
1613
|
*
|
|
1657
1614
|
* Normalization mostly removes unecessary/redundant inline fragments, so that for instance, with
|
|
1658
1615
|
* schema:
|
|
@@ -1802,25 +1759,14 @@ export class SelectionSet {
|
|
|
1802
1759
|
return updated.isEmpty() ? undefined : updated;
|
|
1803
1760
|
}
|
|
1804
1761
|
|
|
1805
|
-
rebaseOn({
|
|
1806
|
-
parentType,
|
|
1807
|
-
fragments,
|
|
1808
|
-
errorIfCannotRebase,
|
|
1809
|
-
}: {
|
|
1810
|
-
parentType: CompositeType,
|
|
1811
|
-
fragments: NamedFragments | undefined
|
|
1812
|
-
errorIfCannotRebase: boolean,
|
|
1813
|
-
}): SelectionSet {
|
|
1762
|
+
rebaseOn(parentType: CompositeType, fragments: NamedFragments | undefined): SelectionSet {
|
|
1814
1763
|
if (this.parentType === parentType) {
|
|
1815
1764
|
return this;
|
|
1816
1765
|
}
|
|
1817
1766
|
|
|
1818
1767
|
const newSelections = new Map<string, Selection>();
|
|
1819
1768
|
for (const selection of this.selections()) {
|
|
1820
|
-
|
|
1821
|
-
if (rebasedSelection) {
|
|
1822
|
-
newSelections.set(selection.key(), rebasedSelection);
|
|
1823
|
-
}
|
|
1769
|
+
newSelections.set(selection.key(), selection.rebaseOn(parentType, fragments));
|
|
1824
1770
|
}
|
|
1825
1771
|
|
|
1826
1772
|
return new SelectionSet(parentType, newSelections);
|
|
@@ -1844,25 +1790,15 @@ export class SelectionSet {
|
|
|
1844
1790
|
return true;
|
|
1845
1791
|
}
|
|
1846
1792
|
|
|
1847
|
-
contains(that: SelectionSet
|
|
1848
|
-
const ignoreMissingTypename = options?.ignoreMissingTypename ?? false;
|
|
1793
|
+
contains(that: SelectionSet): ContainsResult {
|
|
1849
1794
|
if (that._selections.length > this._selections.length) {
|
|
1850
|
-
|
|
1851
|
-
// `that` has a __typename but `this` does not, then we need the length of `that` to be at
|
|
1852
|
-
// least 2 more than that of `this` to be able to conclude there is no contains.
|
|
1853
|
-
if (!ignoreMissingTypename || that._selections.length > this._selections.length + 1 || this.hasTopLevelTypenameField() || !that.hasTopLevelTypenameField()) {
|
|
1854
|
-
return ContainsResult.NOT_CONTAINED;
|
|
1855
|
-
}
|
|
1795
|
+
return ContainsResult.NOT_CONTAINED;
|
|
1856
1796
|
}
|
|
1857
1797
|
|
|
1858
1798
|
let isEqual = true;
|
|
1859
1799
|
for (const [key, thatSelection] of that._keyedSelections) {
|
|
1860
|
-
if (key === typenameFieldName && ignoreMissingTypename) {
|
|
1861
|
-
continue;
|
|
1862
|
-
}
|
|
1863
|
-
|
|
1864
1800
|
const thisSelection = this._keyedSelections.get(key);
|
|
1865
|
-
const selectionResult = thisSelection?.contains(thatSelection
|
|
1801
|
+
const selectionResult = thisSelection?.contains(thatSelection);
|
|
1866
1802
|
if (selectionResult === undefined || selectionResult === ContainsResult.NOT_CONTAINED) {
|
|
1867
1803
|
return ContainsResult.NOT_CONTAINED;
|
|
1868
1804
|
}
|
|
@@ -2228,10 +2164,10 @@ function makeSelection(parentType: CompositeType, updates: SelectionUpdate[], fr
|
|
|
2228
2164
|
|
|
2229
2165
|
// Optimize for the simple case of a single selection, as we don't have to do anything complex to merge the sub-selections.
|
|
2230
2166
|
if (updates.length === 1 && first instanceof AbstractSelection) {
|
|
2231
|
-
return first.
|
|
2167
|
+
return first.rebaseOn(parentType, fragments);
|
|
2232
2168
|
}
|
|
2233
2169
|
|
|
2234
|
-
const element = updateElement(first).
|
|
2170
|
+
const element = updateElement(first).rebaseOn(parentType);
|
|
2235
2171
|
const subSelectionParentType = element.kind === 'Field' ? element.baseType() : element.castedType();
|
|
2236
2172
|
if (!isCompositeType(subSelectionParentType)) {
|
|
2237
2173
|
// This is a leaf, so all updates should correspond ot the same field and we just use the first.
|
|
@@ -2279,7 +2215,7 @@ function makeSelectionSet(parentType: CompositeType, keyedUpdates: MultiMap<stri
|
|
|
2279
2215
|
}
|
|
2280
2216
|
|
|
2281
2217
|
/**
|
|
2282
|
-
* A simple wrapper over a `SelectionSetUpdates` that allows to conveniently build a selection set, then add some more updates and build it again, etc...
|
|
2218
|
+
* A simple wrapper over a `SelectionSetUpdates` that allows to conveniently build a selection set, then add some more updates and build it again, etc...
|
|
2283
2219
|
*/
|
|
2284
2220
|
export class MutableSelectionSet<TMemoizedValue extends { [key: string]: any } = {}> {
|
|
2285
2221
|
private computed: SelectionSet | undefined;
|
|
@@ -2420,11 +2356,7 @@ abstract class AbstractSelection<TElement extends OperationElement, TIsLeaf exte
|
|
|
2420
2356
|
|
|
2421
2357
|
abstract validate(variableDefinitions: VariableDefinitions): void;
|
|
2422
2358
|
|
|
2423
|
-
abstract rebaseOn(
|
|
2424
|
-
|
|
2425
|
-
rebaseOnOrError({ parentType, fragments }: { parentType: CompositeType, fragments: NamedFragments | undefined }): TOwnType {
|
|
2426
|
-
return this.rebaseOn({ parentType, fragments, errorIfCannotRebase: true})!;
|
|
2427
|
-
}
|
|
2359
|
+
abstract rebaseOn(parentType: CompositeType, fragments: NamedFragments | undefined): TOwnType;
|
|
2428
2360
|
|
|
2429
2361
|
get parentType(): CompositeType {
|
|
2430
2362
|
return this.element.parentType;
|
|
@@ -2524,7 +2456,7 @@ abstract class AbstractSelection<TElement extends OperationElement, TIsLeaf exte
|
|
|
2524
2456
|
// have been "normalized away" and so we want for this very call to be called on the fragment whose type _is_ the fragment condition (at
|
|
2525
2457
|
// which point, this `maybeApplyingDirectlyAtType` method will apply.
|
|
2526
2458
|
// Also note that this is because we have this restriction that calling `expandedSelectionSetAtType` is ok.
|
|
2527
|
-
|
|
2459
|
+
const candidates = fragments.maybeApplyingDirectlyAtType(parentType);
|
|
2528
2460
|
if (candidates.length === 0) {
|
|
2529
2461
|
return subSelection;
|
|
2530
2462
|
}
|
|
@@ -2535,7 +2467,8 @@ abstract class AbstractSelection<TElement extends OperationElement, TIsLeaf exte
|
|
|
2535
2467
|
// applies to a subset of `subSelection`.
|
|
2536
2468
|
const applyingFragments: { fragment: NamedFragmentDefinition, atType: FragmentRestrictionAtType }[] = [];
|
|
2537
2469
|
for (const candidate of candidates) {
|
|
2538
|
-
|
|
2470
|
+
const atType = candidate.expandedSelectionSetAtType(parentType);
|
|
2471
|
+
const selectionSetAtType = atType.selectionSet;
|
|
2539
2472
|
// It's possible that while the fragment technically applies at `parentType`, it's "rebasing" on
|
|
2540
2473
|
// `parentType` is empty, or contains only `__typename`. For instance, suppose we have
|
|
2541
2474
|
// a union `U = A | B | C`, and then a fragment:
|
|
@@ -2554,22 +2487,11 @@ abstract class AbstractSelection<TElement extends OperationElement, TIsLeaf exte
|
|
|
2554
2487
|
//
|
|
2555
2488
|
// Using `F` in those cases is, while not 100% incorrect, at least not productive, and so we
|
|
2556
2489
|
// skip it that case. This is essentially an optimisation.
|
|
2557
|
-
if (
|
|
2490
|
+
if (selectionSetAtType.isEmpty() || (selectionSetAtType.selections().length === 1 && selectionSetAtType.selections()[0].isTypenameField())) {
|
|
2558
2491
|
continue;
|
|
2559
2492
|
}
|
|
2560
2493
|
|
|
2561
|
-
|
|
2562
|
-
// The rational is that querying `__typename` unecessarily is mostly harmless (it always works and it's super cheap)
|
|
2563
|
-
// so we don't want to not use a fragment just to save querying a `__typename` in a few cases. But the underlying
|
|
2564
|
-
// context of why this matters is that the query planner always requests __typename for abstract type, and will do
|
|
2565
|
-
// so in fragments too, but we can have a field that _does_ return an abstract type within a fragment, but that
|
|
2566
|
-
// _does not_ end up returning an abstract type when applied in a "more specific" context (think a fragment on
|
|
2567
|
-
// an interface I1 where a inside field returns another interface I2, but applied in the context of a implementation
|
|
2568
|
-
// type of I1 where that particular field returns an implementation of I2 rather than I2 directly; we would have
|
|
2569
|
-
// added __typename to the fragment (because it's all interfaces), but the selection itself, which only deals
|
|
2570
|
-
// with object type, may not have __typename requested; using the fragment might still be a good idea, and
|
|
2571
|
-
// querying __typename needlessly is a very small price to pay for that).
|
|
2572
|
-
const res = subSelection.contains(atType.selectionSet, { ignoreMissingTypename: true });
|
|
2494
|
+
const res = subSelection.contains(selectionSetAtType);
|
|
2573
2495
|
|
|
2574
2496
|
if (res === ContainsResult.EQUAL) {
|
|
2575
2497
|
if (canUseFullMatchingFragment(candidate)) {
|
|
@@ -2751,7 +2673,7 @@ class FieldsConflictValidator {
|
|
|
2751
2673
|
// It's unlikely that we've seen the same `field.element` as we don't particularly "intern" `Field` object (so even if the exact same field
|
|
2752
2674
|
// is used in 2 parts of a selection set, it will probably be a different `Field` object), so the `get` below will probably mostly return `undefined`,
|
|
2753
2675
|
// but it wouldn't be incorrect to re-use a `Field` object multiple side, so no reason not to handle that correctly.
|
|
2754
|
-
|
|
2676
|
+
const forField = atResponseName.get(field.element) ?? [];
|
|
2755
2677
|
atResponseName.set(field.element, forField.concat(field.selectionSet.fieldsInSet()));
|
|
2756
2678
|
} else {
|
|
2757
2679
|
// Note that whether a `FieldSelection` has `selectionSet` or not is entirely determined by whether the field type is a composite type
|
|
@@ -2946,30 +2868,18 @@ export class FieldSelection extends AbstractSelection<Field<any>, undefined, Fie
|
|
|
2946
2868
|
}
|
|
2947
2869
|
|
|
2948
2870
|
/**
|
|
2949
|
-
* Returns a field selection "equivalent" to the one represented by this object, but such that its parent type
|
|
2871
|
+
* Returns a field selection "equivalent" to the one represented by this object, but such that its parent type
|
|
2950
2872
|
* is the one provided as argument.
|
|
2951
2873
|
*
|
|
2952
2874
|
* Obviously, this operation will only succeed if this selection (both the field itself and its subselections)
|
|
2953
2875
|
* make sense from the provided parent type. If this is not the case, this method will throw.
|
|
2954
2876
|
*/
|
|
2955
|
-
rebaseOn({
|
|
2956
|
-
parentType,
|
|
2957
|
-
fragments,
|
|
2958
|
-
errorIfCannotRebase,
|
|
2959
|
-
}: {
|
|
2960
|
-
parentType: CompositeType,
|
|
2961
|
-
fragments: NamedFragments | undefined,
|
|
2962
|
-
errorIfCannotRebase: boolean,
|
|
2963
|
-
}): FieldSelection | undefined {
|
|
2877
|
+
rebaseOn(parentType: CompositeType, fragments: NamedFragments | undefined): FieldSelection {
|
|
2964
2878
|
if (this.element.parentType === parentType) {
|
|
2965
2879
|
return this;
|
|
2966
2880
|
}
|
|
2967
2881
|
|
|
2968
|
-
const rebasedElement = this.element.rebaseOn(
|
|
2969
|
-
if (!rebasedElement) {
|
|
2970
|
-
return undefined;
|
|
2971
|
-
}
|
|
2972
|
-
|
|
2882
|
+
const rebasedElement = this.element.rebaseOn(parentType);
|
|
2973
2883
|
if (!this.selectionSet) {
|
|
2974
2884
|
return this.withUpdatedElement(rebasedElement);
|
|
2975
2885
|
}
|
|
@@ -2980,8 +2890,7 @@ export class FieldSelection extends AbstractSelection<Field<any>, undefined, Fie
|
|
|
2980
2890
|
}
|
|
2981
2891
|
|
|
2982
2892
|
validate(isCompositeType(rebasedBase), () => `Cannot rebase field selection ${this} on ${parentType}: rebased field base return type ${rebasedBase} is not composite`);
|
|
2983
|
-
|
|
2984
|
-
return rebasedSelectionSet.isEmpty() ? undefined : this.withUpdatedComponents(rebasedElement, rebasedSelectionSet);
|
|
2893
|
+
return this.withUpdatedComponents(rebasedElement, this.selectionSet.rebaseOn(rebasedBase, fragments));
|
|
2985
2894
|
}
|
|
2986
2895
|
|
|
2987
2896
|
/**
|
|
@@ -3088,7 +2997,7 @@ export class FieldSelection extends AbstractSelection<Field<any>, undefined, Fie
|
|
|
3088
2997
|
return !!that.selectionSet && this.selectionSet.equals(that.selectionSet);
|
|
3089
2998
|
}
|
|
3090
2999
|
|
|
3091
|
-
contains(that: Selection
|
|
3000
|
+
contains(that: Selection): ContainsResult {
|
|
3092
3001
|
if (!(that instanceof FieldSelection) || !this.element.equals(that.element)) {
|
|
3093
3002
|
return ContainsResult.NOT_CONTAINED;
|
|
3094
3003
|
}
|
|
@@ -3098,7 +3007,7 @@ export class FieldSelection extends AbstractSelection<Field<any>, undefined, Fie
|
|
|
3098
3007
|
return ContainsResult.EQUAL;
|
|
3099
3008
|
}
|
|
3100
3009
|
assert(that.selectionSet, '`this` and `that` have the same element, so if one has sub-selection, the other one should too')
|
|
3101
|
-
return this.selectionSet.contains(that.selectionSet
|
|
3010
|
+
return this.selectionSet.contains(that.selectionSet);
|
|
3102
3011
|
}
|
|
3103
3012
|
|
|
3104
3013
|
toString(expandFragments: boolean = true, indent?: string): string {
|
|
@@ -3125,7 +3034,7 @@ export abstract class FragmentSelection extends AbstractSelection<FragmentElemen
|
|
|
3125
3034
|
);
|
|
3126
3035
|
}
|
|
3127
3036
|
}
|
|
3128
|
-
|
|
3037
|
+
|
|
3129
3038
|
filterRecursiveDepthFirst(predicate: (selection: Selection) => boolean): FragmentSelection | undefined {
|
|
3130
3039
|
// Note that we essentially expand all fragments as part of this.
|
|
3131
3040
|
const updatedSelectionSet = this.selectionSet.filterRecursiveDepthFirst(predicate);
|
|
@@ -3135,14 +3044,14 @@ export abstract class FragmentSelection extends AbstractSelection<FragmentElemen
|
|
|
3135
3044
|
|
|
3136
3045
|
return predicate(thisWithFilteredSelectionSet) ? thisWithFilteredSelectionSet : undefined;
|
|
3137
3046
|
}
|
|
3138
|
-
|
|
3047
|
+
|
|
3139
3048
|
hasDefer(): boolean {
|
|
3140
3049
|
return this.element.hasDefer() || this.selectionSet.hasDefer();
|
|
3141
3050
|
}
|
|
3142
3051
|
|
|
3143
3052
|
abstract equals(that: Selection): boolean;
|
|
3144
3053
|
|
|
3145
|
-
abstract contains(that: Selection
|
|
3054
|
+
abstract contains(that: Selection): ContainsResult;
|
|
3146
3055
|
|
|
3147
3056
|
normalize({ parentType, recursive }: { parentType: CompositeType, recursive? : boolean }): FragmentSelection | SelectionSet | undefined {
|
|
3148
3057
|
const thisCondition = this.element.typeCondition;
|
|
@@ -3199,31 +3108,18 @@ class InlineFragmentSelection extends FragmentSelection {
|
|
|
3199
3108
|
this.selectionSet.validate(variableDefinitions);
|
|
3200
3109
|
}
|
|
3201
3110
|
|
|
3202
|
-
rebaseOn({
|
|
3203
|
-
parentType,
|
|
3204
|
-
fragments,
|
|
3205
|
-
errorIfCannotRebase,
|
|
3206
|
-
}: {
|
|
3207
|
-
parentType: CompositeType,
|
|
3208
|
-
fragments: NamedFragments | undefined,
|
|
3209
|
-
errorIfCannotRebase: boolean,
|
|
3210
|
-
}): FragmentSelection | undefined {
|
|
3111
|
+
rebaseOn(parentType: CompositeType, fragments: NamedFragments | undefined): FragmentSelection {
|
|
3211
3112
|
if (this.parentType === parentType) {
|
|
3212
3113
|
return this;
|
|
3213
3114
|
}
|
|
3214
3115
|
|
|
3215
|
-
const rebasedFragment = this.element.rebaseOn(
|
|
3216
|
-
if (!rebasedFragment) {
|
|
3217
|
-
return undefined;
|
|
3218
|
-
}
|
|
3219
|
-
|
|
3116
|
+
const rebasedFragment = this.element.rebaseOn(parentType);
|
|
3220
3117
|
const rebasedCastedType = rebasedFragment.castedType();
|
|
3221
3118
|
if (rebasedCastedType === this.selectionSet.parentType) {
|
|
3222
3119
|
return this.withUpdatedElement(rebasedFragment);
|
|
3223
3120
|
}
|
|
3224
3121
|
|
|
3225
|
-
|
|
3226
|
-
return rebasedSelectionSet.isEmpty() ? undefined : this.withUpdatedComponents(rebasedFragment, rebasedSelectionSet);
|
|
3122
|
+
return this.withUpdatedComponents(rebasedFragment, this.selectionSet.rebaseOn(rebasedCastedType, fragments));
|
|
3227
3123
|
}
|
|
3228
3124
|
|
|
3229
3125
|
canAddTo(parentType: CompositeType): boolean {
|
|
@@ -3372,8 +3268,7 @@ class InlineFragmentSelection extends FragmentSelection {
|
|
|
3372
3268
|
return undefined;
|
|
3373
3269
|
} else {
|
|
3374
3270
|
return this.withUpdatedComponents(
|
|
3375
|
-
|
|
3376
|
-
this.element.rebaseOnOrError(parentType),
|
|
3271
|
+
this.element.rebaseOn(parentType),
|
|
3377
3272
|
selectionSetOfElement(
|
|
3378
3273
|
new Field(
|
|
3379
3274
|
(this.element.typeCondition ?? parentType).typenameField()!,
|
|
@@ -3425,7 +3320,7 @@ class InlineFragmentSelection extends FragmentSelection {
|
|
|
3425
3320
|
|
|
3426
3321
|
return this.parentType === parentType && this.selectionSet === normalizedSelectionSet
|
|
3427
3322
|
? this
|
|
3428
|
-
: this.withUpdatedComponents(this.element.
|
|
3323
|
+
: this.withUpdatedComponents(this.element.rebaseOn(parentType), normalizedSelectionSet);
|
|
3429
3324
|
}
|
|
3430
3325
|
|
|
3431
3326
|
expandFragments(updatedFragments: NamedFragments | undefined): FragmentSelection {
|
|
@@ -3442,12 +3337,12 @@ class InlineFragmentSelection extends FragmentSelection {
|
|
|
3442
3337
|
&& this.selectionSet.equals(that.selectionSet);
|
|
3443
3338
|
}
|
|
3444
3339
|
|
|
3445
|
-
contains(that: Selection
|
|
3340
|
+
contains(that: Selection): ContainsResult {
|
|
3446
3341
|
if (!(that instanceof FragmentSelection) || !this.element.equals(that.element)) {
|
|
3447
3342
|
return ContainsResult.NOT_CONTAINED;
|
|
3448
3343
|
}
|
|
3449
3344
|
|
|
3450
|
-
return this.selectionSet.contains(that.selectionSet
|
|
3345
|
+
return this.selectionSet.contains(that.selectionSet);
|
|
3451
3346
|
}
|
|
3452
3347
|
|
|
3453
3348
|
toString(expandFragments: boolean = true, indent?: string): string {
|
|
@@ -3490,7 +3385,7 @@ class FragmentSpreadSelection extends FragmentSelection {
|
|
|
3490
3385
|
// We must update the spread parent type if necessary since we're not going deeper,
|
|
3491
3386
|
// or we'll be fundamentally losing context.
|
|
3492
3387
|
assert(parentType.schema() === this.parentType.schema(), 'Should not try to normalize using a type from another schema');
|
|
3493
|
-
return this.
|
|
3388
|
+
return this.rebaseOn(parentType, this.fragments);
|
|
3494
3389
|
}
|
|
3495
3390
|
|
|
3496
3391
|
validate(): void {
|
|
@@ -3526,15 +3421,7 @@ class FragmentSpreadSelection extends FragmentSelection {
|
|
|
3526
3421
|
return this;
|
|
3527
3422
|
}
|
|
3528
3423
|
|
|
3529
|
-
rebaseOn({
|
|
3530
|
-
parentType,
|
|
3531
|
-
fragments,
|
|
3532
|
-
errorIfCannotRebase,
|
|
3533
|
-
}: {
|
|
3534
|
-
parentType: CompositeType,
|
|
3535
|
-
fragments: NamedFragments | undefined,
|
|
3536
|
-
errorIfCannotRebase: boolean,
|
|
3537
|
-
}): FragmentSelection | undefined {
|
|
3424
|
+
rebaseOn(parentType: CompositeType, fragments: NamedFragments | undefined): FragmentSelection {
|
|
3538
3425
|
// We preserve the parent type here, to make sure we don't lose context, but we actually don't
|
|
3539
3426
|
// want to expand the spread as that would compromise the code that optimize subgraph fetches to re-use named
|
|
3540
3427
|
// fragments.
|
|
@@ -3554,14 +3441,7 @@ class FragmentSpreadSelection extends FragmentSelection {
|
|
|
3554
3441
|
assert(fragments || this.parentType.schema() === parentType.schema(), `Must provide fragments is rebasing on other schema`);
|
|
3555
3442
|
const newFragments = fragments ?? this.fragments;
|
|
3556
3443
|
const namedFragment = newFragments.get(this.namedFragment.name);
|
|
3557
|
-
|
|
3558
|
-
// of them may not contain anything that is on that subgraph, in which case they will not have been included at all.
|
|
3559
|
-
// If so, then as long as we're not ask to error if we cannot rebase, then we're happy to skip that spread (since again,
|
|
3560
|
-
// it expands to nothing that apply on the schema).
|
|
3561
|
-
if (!namedFragment) {
|
|
3562
|
-
validate(!errorIfCannotRebase, () => `Cannot rebase ${this.toString(false)} if it isn't part of the provided fragments`);
|
|
3563
|
-
return undefined;
|
|
3564
|
-
}
|
|
3444
|
+
assert(namedFragment, () => `Cannot rebase ${this} if it isn't part of the provided fragments`);
|
|
3565
3445
|
return new FragmentSpreadSelection(
|
|
3566
3446
|
parentType,
|
|
3567
3447
|
newFragments,
|
|
@@ -3617,7 +3497,7 @@ class FragmentSpreadSelection extends FragmentSelection {
|
|
|
3617
3497
|
&& sameDirectiveApplications(this.spreadDirectives, that.spreadDirectives);
|
|
3618
3498
|
}
|
|
3619
3499
|
|
|
3620
|
-
contains(that: Selection
|
|
3500
|
+
contains(that: Selection): ContainsResult {
|
|
3621
3501
|
if (this.equals(that)) {
|
|
3622
3502
|
return ContainsResult.EQUAL;
|
|
3623
3503
|
}
|
|
@@ -3626,7 +3506,7 @@ class FragmentSpreadSelection extends FragmentSelection {
|
|
|
3626
3506
|
return ContainsResult.NOT_CONTAINED;
|
|
3627
3507
|
}
|
|
3628
3508
|
|
|
3629
|
-
return
|
|
3509
|
+
return this.selectionSet.contains(that.selectionSet);
|
|
3630
3510
|
}
|
|
3631
3511
|
|
|
3632
3512
|
toString(expandFragments: boolean = true, indent?: string): string {
|