@graphql-inspector/action 3.1.2 → 3.2.0
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/index.js +4528 -83
- package/index.mjs +4528 -83
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1971,6 +1971,18 @@ function readName(lexer, start) {
|
|
|
1971
1971
|
);
|
|
1972
1972
|
}
|
|
1973
1973
|
|
|
1974
|
+
/**
|
|
1975
|
+
* Configuration options to control parser behavior
|
|
1976
|
+
*/
|
|
1977
|
+
|
|
1978
|
+
/**
|
|
1979
|
+
* Given a GraphQL source, parses it into a Document.
|
|
1980
|
+
* Throws GraphQLError if a syntax error is encountered.
|
|
1981
|
+
*/
|
|
1982
|
+
function parse(source, options) {
|
|
1983
|
+
const parser = new Parser(source, options);
|
|
1984
|
+
return parser.parseDocument();
|
|
1985
|
+
}
|
|
1974
1986
|
/**
|
|
1975
1987
|
* Given a string containing a GraphQL value (ex. `[42]`), parse the AST for
|
|
1976
1988
|
* that value.
|
|
@@ -3690,6 +3702,85 @@ function visit(root, visitor, visitorKeys = QueryDocumentKeys) {
|
|
|
3690
3702
|
|
|
3691
3703
|
return newRoot;
|
|
3692
3704
|
}
|
|
3705
|
+
/**
|
|
3706
|
+
* Creates a new visitor instance which delegates to many visitors to run in
|
|
3707
|
+
* parallel. Each visitor will be visited for each node before moving on.
|
|
3708
|
+
*
|
|
3709
|
+
* If a prior visitor edits a node, no following visitors will see that node.
|
|
3710
|
+
*/
|
|
3711
|
+
|
|
3712
|
+
function visitInParallel(visitors) {
|
|
3713
|
+
const skipping = new Array(visitors.length).fill(null);
|
|
3714
|
+
const mergedVisitor = Object.create(null);
|
|
3715
|
+
|
|
3716
|
+
for (const kind of Object.values(Kind)) {
|
|
3717
|
+
let hasVisitor = false;
|
|
3718
|
+
const enterList = new Array(visitors.length).fill(undefined);
|
|
3719
|
+
const leaveList = new Array(visitors.length).fill(undefined);
|
|
3720
|
+
|
|
3721
|
+
for (let i = 0; i < visitors.length; ++i) {
|
|
3722
|
+
const { enter, leave } = getEnterLeaveForKind(visitors[i], kind);
|
|
3723
|
+
hasVisitor || (hasVisitor = enter != null || leave != null);
|
|
3724
|
+
enterList[i] = enter;
|
|
3725
|
+
leaveList[i] = leave;
|
|
3726
|
+
}
|
|
3727
|
+
|
|
3728
|
+
if (!hasVisitor) {
|
|
3729
|
+
continue;
|
|
3730
|
+
}
|
|
3731
|
+
|
|
3732
|
+
const mergedEnterLeave = {
|
|
3733
|
+
enter(...args) {
|
|
3734
|
+
const node = args[0];
|
|
3735
|
+
|
|
3736
|
+
for (let i = 0; i < visitors.length; i++) {
|
|
3737
|
+
if (skipping[i] === null) {
|
|
3738
|
+
var _enterList$i;
|
|
3739
|
+
|
|
3740
|
+
const result =
|
|
3741
|
+
(_enterList$i = enterList[i]) === null || _enterList$i === void 0
|
|
3742
|
+
? void 0
|
|
3743
|
+
: _enterList$i.apply(visitors[i], args);
|
|
3744
|
+
|
|
3745
|
+
if (result === false) {
|
|
3746
|
+
skipping[i] = node;
|
|
3747
|
+
} else if (result === BREAK) {
|
|
3748
|
+
skipping[i] = BREAK;
|
|
3749
|
+
} else if (result !== undefined) {
|
|
3750
|
+
return result;
|
|
3751
|
+
}
|
|
3752
|
+
}
|
|
3753
|
+
}
|
|
3754
|
+
},
|
|
3755
|
+
|
|
3756
|
+
leave(...args) {
|
|
3757
|
+
const node = args[0];
|
|
3758
|
+
|
|
3759
|
+
for (let i = 0; i < visitors.length; i++) {
|
|
3760
|
+
if (skipping[i] === null) {
|
|
3761
|
+
var _leaveList$i;
|
|
3762
|
+
|
|
3763
|
+
const result =
|
|
3764
|
+
(_leaveList$i = leaveList[i]) === null || _leaveList$i === void 0
|
|
3765
|
+
? void 0
|
|
3766
|
+
: _leaveList$i.apply(visitors[i], args);
|
|
3767
|
+
|
|
3768
|
+
if (result === BREAK) {
|
|
3769
|
+
skipping[i] = BREAK;
|
|
3770
|
+
} else if (result !== undefined && result !== false) {
|
|
3771
|
+
return result;
|
|
3772
|
+
}
|
|
3773
|
+
} else if (skipping[i] === node) {
|
|
3774
|
+
skipping[i] = null;
|
|
3775
|
+
}
|
|
3776
|
+
}
|
|
3777
|
+
},
|
|
3778
|
+
};
|
|
3779
|
+
mergedVisitor[kind] = mergedEnterLeave;
|
|
3780
|
+
}
|
|
3781
|
+
|
|
3782
|
+
return mergedVisitor;
|
|
3783
|
+
}
|
|
3693
3784
|
/**
|
|
3694
3785
|
* Given a visitor instance and a node kind, return EnterLeaveVisitor for that kind.
|
|
3695
3786
|
*/
|
|
@@ -4746,6 +4837,13 @@ function isLeafType(type) {
|
|
|
4746
4837
|
* These types may describe the parent context of a selection set.
|
|
4747
4838
|
*/
|
|
4748
4839
|
|
|
4840
|
+
function isCompositeType(type) {
|
|
4841
|
+
return isObjectType(type) || isInterfaceType(type) || isUnionType(type);
|
|
4842
|
+
}
|
|
4843
|
+
/**
|
|
4844
|
+
* These types may describe the parent context of a selection set.
|
|
4845
|
+
*/
|
|
4846
|
+
|
|
4749
4847
|
function isAbstractType(type) {
|
|
4750
4848
|
return isInterfaceType(type) || isUnionType(type);
|
|
4751
4849
|
}
|
|
@@ -4853,6 +4951,11 @@ function assertNullableType(type) {
|
|
|
4853
4951
|
|
|
4854
4952
|
return type;
|
|
4855
4953
|
}
|
|
4954
|
+
function getNullableType(type) {
|
|
4955
|
+
if (type) {
|
|
4956
|
+
return isNonNullType(type) ? type.ofType : type;
|
|
4957
|
+
}
|
|
4958
|
+
}
|
|
4856
4959
|
function getNamedType(type) {
|
|
4857
4960
|
if (type) {
|
|
4858
4961
|
let unwrappedType = type;
|
|
@@ -5214,6 +5317,9 @@ function argsToArgsConfig(args) {
|
|
|
5214
5317
|
}),
|
|
5215
5318
|
);
|
|
5216
5319
|
}
|
|
5320
|
+
function isRequiredArgument(arg) {
|
|
5321
|
+
return isNonNullType(arg.type) && arg.defaultValue === undefined;
|
|
5322
|
+
}
|
|
5217
5323
|
|
|
5218
5324
|
/**
|
|
5219
5325
|
* Interface Type Definition
|
|
@@ -5671,6 +5777,90 @@ function defineInputFieldMap(config) {
|
|
|
5671
5777
|
});
|
|
5672
5778
|
}
|
|
5673
5779
|
|
|
5780
|
+
function isRequiredInputField(field) {
|
|
5781
|
+
return isNonNullType(field.type) && field.defaultValue === undefined;
|
|
5782
|
+
}
|
|
5783
|
+
|
|
5784
|
+
/**
|
|
5785
|
+
* Provided a type and a super type, return true if the first type is either
|
|
5786
|
+
* equal or a subset of the second super type (covariant).
|
|
5787
|
+
*/
|
|
5788
|
+
|
|
5789
|
+
function isTypeSubTypeOf(schema, maybeSubType, superType) {
|
|
5790
|
+
// Equivalent type is a valid subtype
|
|
5791
|
+
if (maybeSubType === superType) {
|
|
5792
|
+
return true;
|
|
5793
|
+
} // If superType is non-null, maybeSubType must also be non-null.
|
|
5794
|
+
|
|
5795
|
+
if (isNonNullType(superType)) {
|
|
5796
|
+
if (isNonNullType(maybeSubType)) {
|
|
5797
|
+
return isTypeSubTypeOf(schema, maybeSubType.ofType, superType.ofType);
|
|
5798
|
+
}
|
|
5799
|
+
|
|
5800
|
+
return false;
|
|
5801
|
+
}
|
|
5802
|
+
|
|
5803
|
+
if (isNonNullType(maybeSubType)) {
|
|
5804
|
+
// If superType is nullable, maybeSubType may be non-null or nullable.
|
|
5805
|
+
return isTypeSubTypeOf(schema, maybeSubType.ofType, superType);
|
|
5806
|
+
} // If superType type is a list, maybeSubType type must also be a list.
|
|
5807
|
+
|
|
5808
|
+
if (isListType(superType)) {
|
|
5809
|
+
if (isListType(maybeSubType)) {
|
|
5810
|
+
return isTypeSubTypeOf(schema, maybeSubType.ofType, superType.ofType);
|
|
5811
|
+
}
|
|
5812
|
+
|
|
5813
|
+
return false;
|
|
5814
|
+
}
|
|
5815
|
+
|
|
5816
|
+
if (isListType(maybeSubType)) {
|
|
5817
|
+
// If superType is not a list, maybeSubType must also be not a list.
|
|
5818
|
+
return false;
|
|
5819
|
+
} // If superType type is an abstract type, check if it is super type of maybeSubType.
|
|
5820
|
+
// Otherwise, the child type is not a valid subtype of the parent type.
|
|
5821
|
+
|
|
5822
|
+
return (
|
|
5823
|
+
isAbstractType(superType) &&
|
|
5824
|
+
(isInterfaceType(maybeSubType) || isObjectType(maybeSubType)) &&
|
|
5825
|
+
schema.isSubType(superType, maybeSubType)
|
|
5826
|
+
);
|
|
5827
|
+
}
|
|
5828
|
+
/**
|
|
5829
|
+
* Provided two composite types, determine if they "overlap". Two composite
|
|
5830
|
+
* types overlap when the Sets of possible concrete types for each intersect.
|
|
5831
|
+
*
|
|
5832
|
+
* This is often used to determine if a fragment of a given type could possibly
|
|
5833
|
+
* be visited in a context of another type.
|
|
5834
|
+
*
|
|
5835
|
+
* This function is commutative.
|
|
5836
|
+
*/
|
|
5837
|
+
|
|
5838
|
+
function doTypesOverlap(schema, typeA, typeB) {
|
|
5839
|
+
// Equivalent types overlap
|
|
5840
|
+
if (typeA === typeB) {
|
|
5841
|
+
return true;
|
|
5842
|
+
}
|
|
5843
|
+
|
|
5844
|
+
if (isAbstractType(typeA)) {
|
|
5845
|
+
if (isAbstractType(typeB)) {
|
|
5846
|
+
// If both types are abstract, then determine if there is any intersection
|
|
5847
|
+
// between possible concrete types of each.
|
|
5848
|
+
return schema
|
|
5849
|
+
.getPossibleTypes(typeA)
|
|
5850
|
+
.some((type) => schema.isSubType(typeB, type));
|
|
5851
|
+
} // Determine if the latter type is a possible concrete type of the former.
|
|
5852
|
+
|
|
5853
|
+
return schema.isSubType(typeA, typeB);
|
|
5854
|
+
}
|
|
5855
|
+
|
|
5856
|
+
if (isAbstractType(typeB)) {
|
|
5857
|
+
// Determine if the former type is a possible concrete type of the latter.
|
|
5858
|
+
return schema.isSubType(typeB, typeA);
|
|
5859
|
+
} // Otherwise the types do not overlap.
|
|
5860
|
+
|
|
5861
|
+
return false;
|
|
5862
|
+
}
|
|
5863
|
+
|
|
5674
5864
|
/**
|
|
5675
5865
|
* Returns true if the provided object is an Object (i.e. not a string literal)
|
|
5676
5866
|
* and implements the Iterator protocol.
|
|
@@ -7197,6 +7387,166 @@ function collectReferencedTypes(type, typeSet) {
|
|
|
7197
7387
|
return typeSet;
|
|
7198
7388
|
}
|
|
7199
7389
|
|
|
7390
|
+
/**
|
|
7391
|
+
* Given a Schema and an AST node describing a type, return a GraphQLType
|
|
7392
|
+
* definition which applies to that type. For example, if provided the parsed
|
|
7393
|
+
* AST node for `[User]`, a GraphQLList instance will be returned, containing
|
|
7394
|
+
* the type called "User" found in the schema. If a type called "User" is not
|
|
7395
|
+
* found in the schema, then undefined will be returned.
|
|
7396
|
+
*/
|
|
7397
|
+
|
|
7398
|
+
function typeFromAST(schema, typeNode) {
|
|
7399
|
+
let innerType;
|
|
7400
|
+
|
|
7401
|
+
if (typeNode.kind === Kind.LIST_TYPE) {
|
|
7402
|
+
innerType = typeFromAST(schema, typeNode.type);
|
|
7403
|
+
return innerType && new GraphQLList(innerType);
|
|
7404
|
+
}
|
|
7405
|
+
|
|
7406
|
+
if (typeNode.kind === Kind.NON_NULL_TYPE) {
|
|
7407
|
+
innerType = typeFromAST(schema, typeNode.type);
|
|
7408
|
+
return innerType && new GraphQLNonNull(innerType);
|
|
7409
|
+
} // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618')
|
|
7410
|
+
|
|
7411
|
+
if (typeNode.kind === Kind.NAMED_TYPE) {
|
|
7412
|
+
return schema.getType(typeNode.name.value);
|
|
7413
|
+
} // istanbul ignore next (Not reachable. All possible type nodes have been considered)
|
|
7414
|
+
|
|
7415
|
+
invariant(false, 'Unexpected type node: ' + inspect(typeNode));
|
|
7416
|
+
}
|
|
7417
|
+
|
|
7418
|
+
function isExecutableDefinitionNode(node) {
|
|
7419
|
+
return (
|
|
7420
|
+
node.kind === Kind.OPERATION_DEFINITION ||
|
|
7421
|
+
node.kind === Kind.FRAGMENT_DEFINITION
|
|
7422
|
+
);
|
|
7423
|
+
}
|
|
7424
|
+
function isTypeSystemDefinitionNode(node) {
|
|
7425
|
+
return (
|
|
7426
|
+
node.kind === Kind.SCHEMA_DEFINITION ||
|
|
7427
|
+
isTypeDefinitionNode(node) ||
|
|
7428
|
+
node.kind === Kind.DIRECTIVE_DEFINITION
|
|
7429
|
+
);
|
|
7430
|
+
}
|
|
7431
|
+
function isTypeDefinitionNode(node) {
|
|
7432
|
+
return (
|
|
7433
|
+
node.kind === Kind.SCALAR_TYPE_DEFINITION ||
|
|
7434
|
+
node.kind === Kind.OBJECT_TYPE_DEFINITION ||
|
|
7435
|
+
node.kind === Kind.INTERFACE_TYPE_DEFINITION ||
|
|
7436
|
+
node.kind === Kind.UNION_TYPE_DEFINITION ||
|
|
7437
|
+
node.kind === Kind.ENUM_TYPE_DEFINITION ||
|
|
7438
|
+
node.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION
|
|
7439
|
+
);
|
|
7440
|
+
}
|
|
7441
|
+
function isTypeSystemExtensionNode(node) {
|
|
7442
|
+
return node.kind === Kind.SCHEMA_EXTENSION || isTypeExtensionNode(node);
|
|
7443
|
+
}
|
|
7444
|
+
function isTypeExtensionNode(node) {
|
|
7445
|
+
return (
|
|
7446
|
+
node.kind === Kind.SCALAR_TYPE_EXTENSION ||
|
|
7447
|
+
node.kind === Kind.OBJECT_TYPE_EXTENSION ||
|
|
7448
|
+
node.kind === Kind.INTERFACE_TYPE_EXTENSION ||
|
|
7449
|
+
node.kind === Kind.UNION_TYPE_EXTENSION ||
|
|
7450
|
+
node.kind === Kind.ENUM_TYPE_EXTENSION ||
|
|
7451
|
+
node.kind === Kind.INPUT_OBJECT_TYPE_EXTENSION
|
|
7452
|
+
);
|
|
7453
|
+
}
|
|
7454
|
+
|
|
7455
|
+
/**
|
|
7456
|
+
* Executable definitions
|
|
7457
|
+
*
|
|
7458
|
+
* A GraphQL document is only valid for execution if all definitions are either
|
|
7459
|
+
* operation or fragment definitions.
|
|
7460
|
+
*
|
|
7461
|
+
* See https://spec.graphql.org/draft/#sec-Executable-Definitions
|
|
7462
|
+
*/
|
|
7463
|
+
function ExecutableDefinitionsRule(context) {
|
|
7464
|
+
return {
|
|
7465
|
+
Document(node) {
|
|
7466
|
+
for (const definition of node.definitions) {
|
|
7467
|
+
if (!isExecutableDefinitionNode(definition)) {
|
|
7468
|
+
const defName =
|
|
7469
|
+
definition.kind === Kind.SCHEMA_DEFINITION ||
|
|
7470
|
+
definition.kind === Kind.SCHEMA_EXTENSION
|
|
7471
|
+
? 'schema'
|
|
7472
|
+
: '"' + definition.name.value + '"';
|
|
7473
|
+
context.reportError(
|
|
7474
|
+
new GraphQLError(
|
|
7475
|
+
`The ${defName} definition is not executable.`,
|
|
7476
|
+
definition,
|
|
7477
|
+
),
|
|
7478
|
+
);
|
|
7479
|
+
}
|
|
7480
|
+
}
|
|
7481
|
+
|
|
7482
|
+
return false;
|
|
7483
|
+
},
|
|
7484
|
+
};
|
|
7485
|
+
}
|
|
7486
|
+
|
|
7487
|
+
/**
|
|
7488
|
+
* Unique operation names
|
|
7489
|
+
*
|
|
7490
|
+
* A GraphQL document is only valid if all defined operations have unique names.
|
|
7491
|
+
*
|
|
7492
|
+
* See https://spec.graphql.org/draft/#sec-Operation-Name-Uniqueness
|
|
7493
|
+
*/
|
|
7494
|
+
function UniqueOperationNamesRule(context) {
|
|
7495
|
+
const knownOperationNames = Object.create(null);
|
|
7496
|
+
return {
|
|
7497
|
+
OperationDefinition(node) {
|
|
7498
|
+
const operationName = node.name;
|
|
7499
|
+
|
|
7500
|
+
if (operationName) {
|
|
7501
|
+
if (knownOperationNames[operationName.value]) {
|
|
7502
|
+
context.reportError(
|
|
7503
|
+
new GraphQLError(
|
|
7504
|
+
`There can be only one operation named "${operationName.value}".`,
|
|
7505
|
+
[knownOperationNames[operationName.value], operationName],
|
|
7506
|
+
),
|
|
7507
|
+
);
|
|
7508
|
+
} else {
|
|
7509
|
+
knownOperationNames[operationName.value] = operationName;
|
|
7510
|
+
}
|
|
7511
|
+
}
|
|
7512
|
+
|
|
7513
|
+
return false;
|
|
7514
|
+
},
|
|
7515
|
+
|
|
7516
|
+
FragmentDefinition: () => false,
|
|
7517
|
+
};
|
|
7518
|
+
}
|
|
7519
|
+
|
|
7520
|
+
/**
|
|
7521
|
+
* Lone anonymous operation
|
|
7522
|
+
*
|
|
7523
|
+
* A GraphQL document is only valid if when it contains an anonymous operation
|
|
7524
|
+
* (the query short-hand) that it contains only that one operation definition.
|
|
7525
|
+
*
|
|
7526
|
+
* See https://spec.graphql.org/draft/#sec-Lone-Anonymous-Operation
|
|
7527
|
+
*/
|
|
7528
|
+
function LoneAnonymousOperationRule(context) {
|
|
7529
|
+
let operationCount = 0;
|
|
7530
|
+
return {
|
|
7531
|
+
Document(node) {
|
|
7532
|
+
operationCount = node.definitions.filter(
|
|
7533
|
+
(definition) => definition.kind === Kind.OPERATION_DEFINITION,
|
|
7534
|
+
).length;
|
|
7535
|
+
},
|
|
7536
|
+
|
|
7537
|
+
OperationDefinition(node) {
|
|
7538
|
+
if (!node.name && operationCount > 1) {
|
|
7539
|
+
context.reportError(
|
|
7540
|
+
new GraphQLError(
|
|
7541
|
+
'This anonymous operation must be the only defined operation.',
|
|
7542
|
+
node,
|
|
7543
|
+
),
|
|
7544
|
+
);
|
|
7545
|
+
}
|
|
7546
|
+
},
|
|
7547
|
+
};
|
|
7548
|
+
}
|
|
7549
|
+
|
|
7200
7550
|
/**
|
|
7201
7551
|
* Produces a JavaScript value given a GraphQL Value AST.
|
|
7202
7552
|
*
|
|
@@ -7358,109 +7708,3397 @@ function isMissingVariable(valueNode, variables) {
|
|
|
7358
7708
|
}
|
|
7359
7709
|
|
|
7360
7710
|
/**
|
|
7361
|
-
*
|
|
7711
|
+
* Prepares an object map of argument values given a list of argument
|
|
7712
|
+
* definitions and list of argument AST nodes.
|
|
7362
7713
|
*
|
|
7363
|
-
*
|
|
7364
|
-
*
|
|
7365
|
-
*
|
|
7366
|
-
* represent the "resolver", "parse" or "serialize" functions or any other
|
|
7367
|
-
* server-internal mechanisms.
|
|
7714
|
+
* Note: The returned value is a plain Object with a prototype, since it is
|
|
7715
|
+
* exposed to user code. Care should be taken to not pull values from the
|
|
7716
|
+
* Object prototype.
|
|
7368
7717
|
*
|
|
7369
|
-
*
|
|
7370
|
-
* the "errors" field of a server response before calling this function.
|
|
7718
|
+
* @internal
|
|
7371
7719
|
*/
|
|
7372
7720
|
|
|
7373
|
-
function
|
|
7374
|
-
|
|
7375
|
-
devAssert(
|
|
7376
|
-
false,
|
|
7377
|
-
`Invalid or incomplete introspection result. Ensure that you are passing "data" property of introspection response and no "errors" was returned alongside: ${inspect(
|
|
7378
|
-
introspection,
|
|
7379
|
-
)}.`,
|
|
7380
|
-
); // Get the schema from the introspection result.
|
|
7721
|
+
function getArgumentValues(def, node, variableValues) {
|
|
7722
|
+
var _node$arguments;
|
|
7381
7723
|
|
|
7382
|
-
const
|
|
7724
|
+
const coercedValues = {}; // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
7383
7725
|
|
|
7384
|
-
const
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7726
|
+
const argumentNodes =
|
|
7727
|
+
(_node$arguments = node.arguments) !== null && _node$arguments !== void 0
|
|
7728
|
+
? _node$arguments
|
|
7729
|
+
: [];
|
|
7730
|
+
const argNodeMap = keyMap(argumentNodes, (arg) => arg.name.value);
|
|
7731
|
+
|
|
7732
|
+
for (const argDef of def.args) {
|
|
7733
|
+
const name = argDef.name;
|
|
7734
|
+
const argType = argDef.type;
|
|
7735
|
+
const argumentNode = argNodeMap[name];
|
|
7736
|
+
|
|
7737
|
+
if (!argumentNode) {
|
|
7738
|
+
if (argDef.defaultValue !== undefined) {
|
|
7739
|
+
coercedValues[name] = argDef.defaultValue;
|
|
7740
|
+
} else if (isNonNullType(argType)) {
|
|
7741
|
+
throw new GraphQLError(
|
|
7742
|
+
`Argument "${name}" of required type "${inspect(argType)}" ` +
|
|
7743
|
+
'was not provided.',
|
|
7744
|
+
node,
|
|
7745
|
+
);
|
|
7746
|
+
}
|
|
7389
7747
|
|
|
7390
|
-
|
|
7391
|
-
if (typeMap[stdType.name]) {
|
|
7392
|
-
typeMap[stdType.name] = stdType;
|
|
7748
|
+
continue;
|
|
7393
7749
|
}
|
|
7394
|
-
} // Get the root Query, Mutation, and Subscription types.
|
|
7395
|
-
|
|
7396
|
-
const queryType = schemaIntrospection.queryType
|
|
7397
|
-
? getObjectType(schemaIntrospection.queryType)
|
|
7398
|
-
: null;
|
|
7399
|
-
const mutationType = schemaIntrospection.mutationType
|
|
7400
|
-
? getObjectType(schemaIntrospection.mutationType)
|
|
7401
|
-
: null;
|
|
7402
|
-
const subscriptionType = schemaIntrospection.subscriptionType
|
|
7403
|
-
? getObjectType(schemaIntrospection.subscriptionType)
|
|
7404
|
-
: null; // Get the directives supported by Introspection, assuming empty-set if
|
|
7405
|
-
// directives were not queried for.
|
|
7406
7750
|
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
: []; // Then produce and return a Schema with these types.
|
|
7751
|
+
const valueNode = argumentNode.value;
|
|
7752
|
+
let isNull = valueNode.kind === Kind.NULL;
|
|
7410
7753
|
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
query: queryType,
|
|
7414
|
-
mutation: mutationType,
|
|
7415
|
-
subscription: subscriptionType,
|
|
7416
|
-
types: Object.values(typeMap),
|
|
7417
|
-
directives,
|
|
7418
|
-
assumeValid:
|
|
7419
|
-
options === null || options === void 0 ? void 0 : options.assumeValid,
|
|
7420
|
-
}); // Given a type reference in introspection, return the GraphQLType instance.
|
|
7421
|
-
// preferring cached instances before building new instances.
|
|
7754
|
+
if (valueNode.kind === Kind.VARIABLE) {
|
|
7755
|
+
const variableName = valueNode.name.value;
|
|
7422
7756
|
|
|
7423
|
-
|
|
7424
|
-
|
|
7425
|
-
|
|
7757
|
+
if (
|
|
7758
|
+
variableValues == null ||
|
|
7759
|
+
!hasOwnProperty(variableValues, variableName)
|
|
7760
|
+
) {
|
|
7761
|
+
if (argDef.defaultValue !== undefined) {
|
|
7762
|
+
coercedValues[name] = argDef.defaultValue;
|
|
7763
|
+
} else if (isNonNullType(argType)) {
|
|
7764
|
+
throw new GraphQLError(
|
|
7765
|
+
`Argument "${name}" of required type "${inspect(argType)}" ` +
|
|
7766
|
+
`was provided the variable "$${variableName}" which was not provided a runtime value.`,
|
|
7767
|
+
valueNode,
|
|
7768
|
+
);
|
|
7769
|
+
}
|
|
7426
7770
|
|
|
7427
|
-
|
|
7428
|
-
throw new Error('Decorated type deeper than introspection query.');
|
|
7771
|
+
continue;
|
|
7429
7772
|
}
|
|
7430
7773
|
|
|
7431
|
-
|
|
7774
|
+
isNull = variableValues[variableName] == null;
|
|
7432
7775
|
}
|
|
7433
7776
|
|
|
7434
|
-
if (
|
|
7435
|
-
|
|
7777
|
+
if (isNull && isNonNullType(argType)) {
|
|
7778
|
+
throw new GraphQLError(
|
|
7779
|
+
`Argument "${name}" of non-null type "${inspect(argType)}" ` +
|
|
7780
|
+
'must not be null.',
|
|
7781
|
+
valueNode,
|
|
7782
|
+
);
|
|
7783
|
+
}
|
|
7436
7784
|
|
|
7437
|
-
|
|
7438
|
-
throw new Error('Decorated type deeper than introspection query.');
|
|
7439
|
-
}
|
|
7785
|
+
const coercedValue = valueFromAST(valueNode, argType, variableValues);
|
|
7440
7786
|
|
|
7441
|
-
|
|
7442
|
-
|
|
7787
|
+
if (coercedValue === undefined) {
|
|
7788
|
+
// Note: ValuesOfCorrectTypeRule validation should catch this before
|
|
7789
|
+
// execution. This is a runtime check to ensure execution does not
|
|
7790
|
+
// continue with an invalid argument value.
|
|
7791
|
+
throw new GraphQLError(
|
|
7792
|
+
`Argument "${name}" has invalid value ${print(valueNode)}.`,
|
|
7793
|
+
valueNode,
|
|
7794
|
+
);
|
|
7443
7795
|
}
|
|
7444
7796
|
|
|
7445
|
-
|
|
7797
|
+
coercedValues[name] = coercedValue;
|
|
7446
7798
|
}
|
|
7447
7799
|
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7800
|
+
return coercedValues;
|
|
7801
|
+
}
|
|
7802
|
+
/**
|
|
7803
|
+
* Prepares an object map of argument values given a directive definition
|
|
7804
|
+
* and a AST node which may contain directives. Optionally also accepts a map
|
|
7805
|
+
* of variable values.
|
|
7806
|
+
*
|
|
7807
|
+
* If the directive does not exist on the node, returns undefined.
|
|
7808
|
+
*
|
|
7809
|
+
* Note: The returned value is a plain Object with a prototype, since it is
|
|
7810
|
+
* exposed to user code. Care should be taken to not pull values from the
|
|
7811
|
+
* Object prototype.
|
|
7812
|
+
*/
|
|
7454
7813
|
|
|
7455
|
-
|
|
7814
|
+
function getDirectiveValues(directiveDef, node, variableValues) {
|
|
7815
|
+
var _node$directives;
|
|
7456
7816
|
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7817
|
+
// istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
7818
|
+
const directiveNode =
|
|
7819
|
+
(_node$directives = node.directives) === null || _node$directives === void 0
|
|
7820
|
+
? void 0
|
|
7821
|
+
: _node$directives.find(
|
|
7822
|
+
(directive) => directive.name.value === directiveDef.name,
|
|
7823
|
+
);
|
|
7462
7824
|
|
|
7463
|
-
|
|
7825
|
+
if (directiveNode) {
|
|
7826
|
+
return getArgumentValues(directiveDef, directiveNode, variableValues);
|
|
7827
|
+
}
|
|
7828
|
+
}
|
|
7829
|
+
|
|
7830
|
+
function hasOwnProperty(obj, prop) {
|
|
7831
|
+
return Object.prototype.hasOwnProperty.call(obj, prop);
|
|
7832
|
+
}
|
|
7833
|
+
|
|
7834
|
+
/**
|
|
7835
|
+
* Given a selectionSet, collect all of the fields and returns it at the end.
|
|
7836
|
+
*
|
|
7837
|
+
* CollectFields requires the "runtime type" of an object. For a field which
|
|
7838
|
+
* returns an Interface or Union type, the "runtime type" will be the actual
|
|
7839
|
+
* Object type returned by that field.
|
|
7840
|
+
*
|
|
7841
|
+
* @internal
|
|
7842
|
+
*/
|
|
7843
|
+
|
|
7844
|
+
function collectFields(
|
|
7845
|
+
schema,
|
|
7846
|
+
fragments,
|
|
7847
|
+
variableValues,
|
|
7848
|
+
runtimeType,
|
|
7849
|
+
selectionSet,
|
|
7850
|
+
) {
|
|
7851
|
+
const fields = new Map();
|
|
7852
|
+
collectFieldsImpl(
|
|
7853
|
+
schema,
|
|
7854
|
+
fragments,
|
|
7855
|
+
variableValues,
|
|
7856
|
+
runtimeType,
|
|
7857
|
+
selectionSet,
|
|
7858
|
+
fields,
|
|
7859
|
+
new Set(),
|
|
7860
|
+
);
|
|
7861
|
+
return fields;
|
|
7862
|
+
}
|
|
7863
|
+
|
|
7864
|
+
function collectFieldsImpl(
|
|
7865
|
+
schema,
|
|
7866
|
+
fragments,
|
|
7867
|
+
variableValues,
|
|
7868
|
+
runtimeType,
|
|
7869
|
+
selectionSet,
|
|
7870
|
+
fields,
|
|
7871
|
+
visitedFragmentNames,
|
|
7872
|
+
) {
|
|
7873
|
+
for (const selection of selectionSet.selections) {
|
|
7874
|
+
switch (selection.kind) {
|
|
7875
|
+
case Kind.FIELD: {
|
|
7876
|
+
if (!shouldIncludeNode(variableValues, selection)) {
|
|
7877
|
+
continue;
|
|
7878
|
+
}
|
|
7879
|
+
|
|
7880
|
+
const name = getFieldEntryKey(selection);
|
|
7881
|
+
const fieldList = fields.get(name);
|
|
7882
|
+
|
|
7883
|
+
if (fieldList !== undefined) {
|
|
7884
|
+
fieldList.push(selection);
|
|
7885
|
+
} else {
|
|
7886
|
+
fields.set(name, [selection]);
|
|
7887
|
+
}
|
|
7888
|
+
|
|
7889
|
+
break;
|
|
7890
|
+
}
|
|
7891
|
+
|
|
7892
|
+
case Kind.INLINE_FRAGMENT: {
|
|
7893
|
+
if (
|
|
7894
|
+
!shouldIncludeNode(variableValues, selection) ||
|
|
7895
|
+
!doesFragmentConditionMatch(schema, selection, runtimeType)
|
|
7896
|
+
) {
|
|
7897
|
+
continue;
|
|
7898
|
+
}
|
|
7899
|
+
|
|
7900
|
+
collectFieldsImpl(
|
|
7901
|
+
schema,
|
|
7902
|
+
fragments,
|
|
7903
|
+
variableValues,
|
|
7904
|
+
runtimeType,
|
|
7905
|
+
selection.selectionSet,
|
|
7906
|
+
fields,
|
|
7907
|
+
visitedFragmentNames,
|
|
7908
|
+
);
|
|
7909
|
+
break;
|
|
7910
|
+
}
|
|
7911
|
+
|
|
7912
|
+
case Kind.FRAGMENT_SPREAD: {
|
|
7913
|
+
const fragName = selection.name.value;
|
|
7914
|
+
|
|
7915
|
+
if (
|
|
7916
|
+
visitedFragmentNames.has(fragName) ||
|
|
7917
|
+
!shouldIncludeNode(variableValues, selection)
|
|
7918
|
+
) {
|
|
7919
|
+
continue;
|
|
7920
|
+
}
|
|
7921
|
+
|
|
7922
|
+
visitedFragmentNames.add(fragName);
|
|
7923
|
+
const fragment = fragments[fragName];
|
|
7924
|
+
|
|
7925
|
+
if (
|
|
7926
|
+
!fragment ||
|
|
7927
|
+
!doesFragmentConditionMatch(schema, fragment, runtimeType)
|
|
7928
|
+
) {
|
|
7929
|
+
continue;
|
|
7930
|
+
}
|
|
7931
|
+
|
|
7932
|
+
collectFieldsImpl(
|
|
7933
|
+
schema,
|
|
7934
|
+
fragments,
|
|
7935
|
+
variableValues,
|
|
7936
|
+
runtimeType,
|
|
7937
|
+
fragment.selectionSet,
|
|
7938
|
+
fields,
|
|
7939
|
+
visitedFragmentNames,
|
|
7940
|
+
);
|
|
7941
|
+
break;
|
|
7942
|
+
}
|
|
7943
|
+
}
|
|
7944
|
+
}
|
|
7945
|
+
}
|
|
7946
|
+
/**
|
|
7947
|
+
* Determines if a field should be included based on the `@include` and `@skip`
|
|
7948
|
+
* directives, where `@skip` has higher precedence than `@include`.
|
|
7949
|
+
*/
|
|
7950
|
+
|
|
7951
|
+
function shouldIncludeNode(variableValues, node) {
|
|
7952
|
+
const skip = getDirectiveValues(GraphQLSkipDirective, node, variableValues);
|
|
7953
|
+
|
|
7954
|
+
if ((skip === null || skip === void 0 ? void 0 : skip.if) === true) {
|
|
7955
|
+
return false;
|
|
7956
|
+
}
|
|
7957
|
+
|
|
7958
|
+
const include = getDirectiveValues(
|
|
7959
|
+
GraphQLIncludeDirective,
|
|
7960
|
+
node,
|
|
7961
|
+
variableValues,
|
|
7962
|
+
);
|
|
7963
|
+
|
|
7964
|
+
if (
|
|
7965
|
+
(include === null || include === void 0 ? void 0 : include.if) === false
|
|
7966
|
+
) {
|
|
7967
|
+
return false;
|
|
7968
|
+
}
|
|
7969
|
+
|
|
7970
|
+
return true;
|
|
7971
|
+
}
|
|
7972
|
+
/**
|
|
7973
|
+
* Determines if a fragment is applicable to the given type.
|
|
7974
|
+
*/
|
|
7975
|
+
|
|
7976
|
+
function doesFragmentConditionMatch(schema, fragment, type) {
|
|
7977
|
+
const typeConditionNode = fragment.typeCondition;
|
|
7978
|
+
|
|
7979
|
+
if (!typeConditionNode) {
|
|
7980
|
+
return true;
|
|
7981
|
+
}
|
|
7982
|
+
|
|
7983
|
+
const conditionalType = typeFromAST(schema, typeConditionNode);
|
|
7984
|
+
|
|
7985
|
+
if (conditionalType === type) {
|
|
7986
|
+
return true;
|
|
7987
|
+
}
|
|
7988
|
+
|
|
7989
|
+
if (isAbstractType(conditionalType)) {
|
|
7990
|
+
return schema.isSubType(conditionalType, type);
|
|
7991
|
+
}
|
|
7992
|
+
|
|
7993
|
+
return false;
|
|
7994
|
+
}
|
|
7995
|
+
/**
|
|
7996
|
+
* Implements the logic to compute the key of a given field's entry
|
|
7997
|
+
*/
|
|
7998
|
+
|
|
7999
|
+
function getFieldEntryKey(node) {
|
|
8000
|
+
return node.alias ? node.alias.value : node.name.value;
|
|
8001
|
+
}
|
|
8002
|
+
|
|
8003
|
+
/**
|
|
8004
|
+
* Subscriptions must only include a non-introspection field.
|
|
8005
|
+
*
|
|
8006
|
+
* A GraphQL subscription is valid only if it contains a single root field and
|
|
8007
|
+
* that root field is not an introspection field.
|
|
8008
|
+
*
|
|
8009
|
+
* See https://spec.graphql.org/draft/#sec-Single-root-field
|
|
8010
|
+
*/
|
|
8011
|
+
function SingleFieldSubscriptionsRule(context) {
|
|
8012
|
+
return {
|
|
8013
|
+
OperationDefinition(node) {
|
|
8014
|
+
if (node.operation === 'subscription') {
|
|
8015
|
+
const schema = context.getSchema();
|
|
8016
|
+
const subscriptionType = schema.getSubscriptionType();
|
|
8017
|
+
|
|
8018
|
+
if (subscriptionType) {
|
|
8019
|
+
const operationName = node.name ? node.name.value : null;
|
|
8020
|
+
const variableValues = Object.create(null);
|
|
8021
|
+
const document = context.getDocument();
|
|
8022
|
+
const fragments = Object.create(null);
|
|
8023
|
+
|
|
8024
|
+
for (const definition of document.definitions) {
|
|
8025
|
+
if (definition.kind === Kind.FRAGMENT_DEFINITION) {
|
|
8026
|
+
fragments[definition.name.value] = definition;
|
|
8027
|
+
}
|
|
8028
|
+
}
|
|
8029
|
+
|
|
8030
|
+
const fields = collectFields(
|
|
8031
|
+
schema,
|
|
8032
|
+
fragments,
|
|
8033
|
+
variableValues,
|
|
8034
|
+
subscriptionType,
|
|
8035
|
+
node.selectionSet,
|
|
8036
|
+
);
|
|
8037
|
+
|
|
8038
|
+
if (fields.size > 1) {
|
|
8039
|
+
const fieldSelectionLists = [...fields.values()];
|
|
8040
|
+
const extraFieldSelectionLists = fieldSelectionLists.slice(1);
|
|
8041
|
+
const extraFieldSelections = extraFieldSelectionLists.flat();
|
|
8042
|
+
context.reportError(
|
|
8043
|
+
new GraphQLError(
|
|
8044
|
+
operationName != null
|
|
8045
|
+
? `Subscription "${operationName}" must select only one top level field.`
|
|
8046
|
+
: 'Anonymous Subscription must select only one top level field.',
|
|
8047
|
+
extraFieldSelections,
|
|
8048
|
+
),
|
|
8049
|
+
);
|
|
8050
|
+
}
|
|
8051
|
+
|
|
8052
|
+
for (const fieldNodes of fields.values()) {
|
|
8053
|
+
const field = fieldNodes[0];
|
|
8054
|
+
const fieldName = field.name.value;
|
|
8055
|
+
|
|
8056
|
+
if (fieldName.startsWith('__')) {
|
|
8057
|
+
context.reportError(
|
|
8058
|
+
new GraphQLError(
|
|
8059
|
+
operationName != null
|
|
8060
|
+
? `Subscription "${operationName}" must not select an introspection top level field.`
|
|
8061
|
+
: 'Anonymous Subscription must not select an introspection top level field.',
|
|
8062
|
+
fieldNodes,
|
|
8063
|
+
),
|
|
8064
|
+
);
|
|
8065
|
+
}
|
|
8066
|
+
}
|
|
8067
|
+
}
|
|
8068
|
+
}
|
|
8069
|
+
},
|
|
8070
|
+
};
|
|
8071
|
+
}
|
|
8072
|
+
|
|
8073
|
+
/**
|
|
8074
|
+
* Known type names
|
|
8075
|
+
*
|
|
8076
|
+
* A GraphQL document is only valid if referenced types (specifically
|
|
8077
|
+
* variable definitions and fragment conditions) are defined by the type schema.
|
|
8078
|
+
*
|
|
8079
|
+
* See https://spec.graphql.org/draft/#sec-Fragment-Spread-Type-Existence
|
|
8080
|
+
*/
|
|
8081
|
+
function KnownTypeNamesRule(context) {
|
|
8082
|
+
const schema = context.getSchema();
|
|
8083
|
+
const existingTypesMap = schema ? schema.getTypeMap() : Object.create(null);
|
|
8084
|
+
const definedTypes = Object.create(null);
|
|
8085
|
+
|
|
8086
|
+
for (const def of context.getDocument().definitions) {
|
|
8087
|
+
if (isTypeDefinitionNode(def)) {
|
|
8088
|
+
definedTypes[def.name.value] = true;
|
|
8089
|
+
}
|
|
8090
|
+
}
|
|
8091
|
+
|
|
8092
|
+
const typeNames = [
|
|
8093
|
+
...Object.keys(existingTypesMap),
|
|
8094
|
+
...Object.keys(definedTypes),
|
|
8095
|
+
];
|
|
8096
|
+
return {
|
|
8097
|
+
NamedType(node, _1, parent, _2, ancestors) {
|
|
8098
|
+
const typeName = node.name.value;
|
|
8099
|
+
|
|
8100
|
+
if (!existingTypesMap[typeName] && !definedTypes[typeName]) {
|
|
8101
|
+
var _ancestors$;
|
|
8102
|
+
|
|
8103
|
+
const definitionNode =
|
|
8104
|
+
(_ancestors$ = ancestors[2]) !== null && _ancestors$ !== void 0
|
|
8105
|
+
? _ancestors$
|
|
8106
|
+
: parent;
|
|
8107
|
+
const isSDL = definitionNode != null && isSDLNode(definitionNode);
|
|
8108
|
+
|
|
8109
|
+
if (isSDL && standardTypeNames.includes(typeName)) {
|
|
8110
|
+
return;
|
|
8111
|
+
}
|
|
8112
|
+
|
|
8113
|
+
const suggestedTypes = suggestionList(
|
|
8114
|
+
typeName,
|
|
8115
|
+
isSDL ? standardTypeNames.concat(typeNames) : typeNames,
|
|
8116
|
+
);
|
|
8117
|
+
context.reportError(
|
|
8118
|
+
new GraphQLError(
|
|
8119
|
+
`Unknown type "${typeName}".` + didYouMean(suggestedTypes),
|
|
8120
|
+
node,
|
|
8121
|
+
),
|
|
8122
|
+
);
|
|
8123
|
+
}
|
|
8124
|
+
},
|
|
8125
|
+
};
|
|
8126
|
+
}
|
|
8127
|
+
const standardTypeNames = [...specifiedScalarTypes, ...introspectionTypes].map(
|
|
8128
|
+
(type) => type.name,
|
|
8129
|
+
);
|
|
8130
|
+
|
|
8131
|
+
function isSDLNode(value) {
|
|
8132
|
+
return (
|
|
8133
|
+
'kind' in value &&
|
|
8134
|
+
(isTypeSystemDefinitionNode(value) || isTypeSystemExtensionNode(value))
|
|
8135
|
+
);
|
|
8136
|
+
}
|
|
8137
|
+
|
|
8138
|
+
/**
|
|
8139
|
+
* Fragments on composite type
|
|
8140
|
+
*
|
|
8141
|
+
* Fragments use a type condition to determine if they apply, since fragments
|
|
8142
|
+
* can only be spread into a composite type (object, interface, or union), the
|
|
8143
|
+
* type condition must also be a composite type.
|
|
8144
|
+
*
|
|
8145
|
+
* See https://spec.graphql.org/draft/#sec-Fragments-On-Composite-Types
|
|
8146
|
+
*/
|
|
8147
|
+
function FragmentsOnCompositeTypesRule(context) {
|
|
8148
|
+
return {
|
|
8149
|
+
InlineFragment(node) {
|
|
8150
|
+
const typeCondition = node.typeCondition;
|
|
8151
|
+
|
|
8152
|
+
if (typeCondition) {
|
|
8153
|
+
const type = typeFromAST(context.getSchema(), typeCondition);
|
|
8154
|
+
|
|
8155
|
+
if (type && !isCompositeType(type)) {
|
|
8156
|
+
const typeStr = print(typeCondition);
|
|
8157
|
+
context.reportError(
|
|
8158
|
+
new GraphQLError(
|
|
8159
|
+
`Fragment cannot condition on non composite type "${typeStr}".`,
|
|
8160
|
+
typeCondition,
|
|
8161
|
+
),
|
|
8162
|
+
);
|
|
8163
|
+
}
|
|
8164
|
+
}
|
|
8165
|
+
},
|
|
8166
|
+
|
|
8167
|
+
FragmentDefinition(node) {
|
|
8168
|
+
const type = typeFromAST(context.getSchema(), node.typeCondition);
|
|
8169
|
+
|
|
8170
|
+
if (type && !isCompositeType(type)) {
|
|
8171
|
+
const typeStr = print(node.typeCondition);
|
|
8172
|
+
context.reportError(
|
|
8173
|
+
new GraphQLError(
|
|
8174
|
+
`Fragment "${node.name.value}" cannot condition on non composite type "${typeStr}".`,
|
|
8175
|
+
node.typeCondition,
|
|
8176
|
+
),
|
|
8177
|
+
);
|
|
8178
|
+
}
|
|
8179
|
+
},
|
|
8180
|
+
};
|
|
8181
|
+
}
|
|
8182
|
+
|
|
8183
|
+
/**
|
|
8184
|
+
* Variables are input types
|
|
8185
|
+
*
|
|
8186
|
+
* A GraphQL operation is only valid if all the variables it defines are of
|
|
8187
|
+
* input types (scalar, enum, or input object).
|
|
8188
|
+
*
|
|
8189
|
+
* See https://spec.graphql.org/draft/#sec-Variables-Are-Input-Types
|
|
8190
|
+
*/
|
|
8191
|
+
function VariablesAreInputTypesRule(context) {
|
|
8192
|
+
return {
|
|
8193
|
+
VariableDefinition(node) {
|
|
8194
|
+
const type = typeFromAST(context.getSchema(), node.type);
|
|
8195
|
+
|
|
8196
|
+
if (type !== undefined && !isInputType(type)) {
|
|
8197
|
+
const variableName = node.variable.name.value;
|
|
8198
|
+
const typeName = print(node.type);
|
|
8199
|
+
context.reportError(
|
|
8200
|
+
new GraphQLError(
|
|
8201
|
+
`Variable "$${variableName}" cannot be non-input type "${typeName}".`,
|
|
8202
|
+
node.type,
|
|
8203
|
+
),
|
|
8204
|
+
);
|
|
8205
|
+
}
|
|
8206
|
+
},
|
|
8207
|
+
};
|
|
8208
|
+
}
|
|
8209
|
+
|
|
8210
|
+
/**
|
|
8211
|
+
* Scalar leafs
|
|
8212
|
+
*
|
|
8213
|
+
* A GraphQL document is valid only if all leaf fields (fields without
|
|
8214
|
+
* sub selections) are of scalar or enum types.
|
|
8215
|
+
*/
|
|
8216
|
+
function ScalarLeafsRule(context) {
|
|
8217
|
+
return {
|
|
8218
|
+
Field(node) {
|
|
8219
|
+
const type = context.getType();
|
|
8220
|
+
const selectionSet = node.selectionSet;
|
|
8221
|
+
|
|
8222
|
+
if (type) {
|
|
8223
|
+
if (isLeafType(getNamedType(type))) {
|
|
8224
|
+
if (selectionSet) {
|
|
8225
|
+
const fieldName = node.name.value;
|
|
8226
|
+
const typeStr = inspect(type);
|
|
8227
|
+
context.reportError(
|
|
8228
|
+
new GraphQLError(
|
|
8229
|
+
`Field "${fieldName}" must not have a selection since type "${typeStr}" has no subfields.`,
|
|
8230
|
+
selectionSet,
|
|
8231
|
+
),
|
|
8232
|
+
);
|
|
8233
|
+
}
|
|
8234
|
+
} else if (!selectionSet) {
|
|
8235
|
+
const fieldName = node.name.value;
|
|
8236
|
+
const typeStr = inspect(type);
|
|
8237
|
+
context.reportError(
|
|
8238
|
+
new GraphQLError(
|
|
8239
|
+
`Field "${fieldName}" of type "${typeStr}" must have a selection of subfields. Did you mean "${fieldName} { ... }"?`,
|
|
8240
|
+
node,
|
|
8241
|
+
),
|
|
8242
|
+
);
|
|
8243
|
+
}
|
|
8244
|
+
}
|
|
8245
|
+
},
|
|
8246
|
+
};
|
|
8247
|
+
}
|
|
8248
|
+
|
|
8249
|
+
/**
|
|
8250
|
+
* Fields on correct type
|
|
8251
|
+
*
|
|
8252
|
+
* A GraphQL document is only valid if all fields selected are defined by the
|
|
8253
|
+
* parent type, or are an allowed meta field such as __typename.
|
|
8254
|
+
*
|
|
8255
|
+
* See https://spec.graphql.org/draft/#sec-Field-Selections
|
|
8256
|
+
*/
|
|
8257
|
+
function FieldsOnCorrectTypeRule(context) {
|
|
8258
|
+
return {
|
|
8259
|
+
Field(node) {
|
|
8260
|
+
const type = context.getParentType();
|
|
8261
|
+
|
|
8262
|
+
if (type) {
|
|
8263
|
+
const fieldDef = context.getFieldDef();
|
|
8264
|
+
|
|
8265
|
+
if (!fieldDef) {
|
|
8266
|
+
// This field doesn't exist, lets look for suggestions.
|
|
8267
|
+
const schema = context.getSchema();
|
|
8268
|
+
const fieldName = node.name.value; // First determine if there are any suggested types to condition on.
|
|
8269
|
+
|
|
8270
|
+
let suggestion = didYouMean(
|
|
8271
|
+
'to use an inline fragment on',
|
|
8272
|
+
getSuggestedTypeNames(schema, type, fieldName),
|
|
8273
|
+
); // If there are no suggested types, then perhaps this was a typo?
|
|
8274
|
+
|
|
8275
|
+
if (suggestion === '') {
|
|
8276
|
+
suggestion = didYouMean(getSuggestedFieldNames(type, fieldName));
|
|
8277
|
+
} // Report an error, including helpful suggestions.
|
|
8278
|
+
|
|
8279
|
+
context.reportError(
|
|
8280
|
+
new GraphQLError(
|
|
8281
|
+
`Cannot query field "${fieldName}" on type "${type.name}".` +
|
|
8282
|
+
suggestion,
|
|
8283
|
+
node,
|
|
8284
|
+
),
|
|
8285
|
+
);
|
|
8286
|
+
}
|
|
8287
|
+
}
|
|
8288
|
+
},
|
|
8289
|
+
};
|
|
8290
|
+
}
|
|
8291
|
+
/**
|
|
8292
|
+
* Go through all of the implementations of type, as well as the interfaces that
|
|
8293
|
+
* they implement. If any of those types include the provided field, suggest them,
|
|
8294
|
+
* sorted by how often the type is referenced.
|
|
8295
|
+
*/
|
|
8296
|
+
|
|
8297
|
+
function getSuggestedTypeNames(schema, type, fieldName) {
|
|
8298
|
+
if (!isAbstractType(type)) {
|
|
8299
|
+
// Must be an Object type, which does not have possible fields.
|
|
8300
|
+
return [];
|
|
8301
|
+
}
|
|
8302
|
+
|
|
8303
|
+
const suggestedTypes = new Set();
|
|
8304
|
+
const usageCount = Object.create(null);
|
|
8305
|
+
|
|
8306
|
+
for (const possibleType of schema.getPossibleTypes(type)) {
|
|
8307
|
+
if (!possibleType.getFields()[fieldName]) {
|
|
8308
|
+
continue;
|
|
8309
|
+
} // This object type defines this field.
|
|
8310
|
+
|
|
8311
|
+
suggestedTypes.add(possibleType);
|
|
8312
|
+
usageCount[possibleType.name] = 1;
|
|
8313
|
+
|
|
8314
|
+
for (const possibleInterface of possibleType.getInterfaces()) {
|
|
8315
|
+
var _usageCount$possibleI;
|
|
8316
|
+
|
|
8317
|
+
if (!possibleInterface.getFields()[fieldName]) {
|
|
8318
|
+
continue;
|
|
8319
|
+
} // This interface type defines this field.
|
|
8320
|
+
|
|
8321
|
+
suggestedTypes.add(possibleInterface);
|
|
8322
|
+
usageCount[possibleInterface.name] =
|
|
8323
|
+
((_usageCount$possibleI = usageCount[possibleInterface.name]) !==
|
|
8324
|
+
null && _usageCount$possibleI !== void 0
|
|
8325
|
+
? _usageCount$possibleI
|
|
8326
|
+
: 0) + 1;
|
|
8327
|
+
}
|
|
8328
|
+
}
|
|
8329
|
+
|
|
8330
|
+
return [...suggestedTypes]
|
|
8331
|
+
.sort((typeA, typeB) => {
|
|
8332
|
+
// Suggest both interface and object types based on how common they are.
|
|
8333
|
+
const usageCountDiff = usageCount[typeB.name] - usageCount[typeA.name];
|
|
8334
|
+
|
|
8335
|
+
if (usageCountDiff !== 0) {
|
|
8336
|
+
return usageCountDiff;
|
|
8337
|
+
} // Suggest super types first followed by subtypes
|
|
8338
|
+
|
|
8339
|
+
if (isInterfaceType(typeA) && schema.isSubType(typeA, typeB)) {
|
|
8340
|
+
return -1;
|
|
8341
|
+
}
|
|
8342
|
+
|
|
8343
|
+
if (isInterfaceType(typeB) && schema.isSubType(typeB, typeA)) {
|
|
8344
|
+
return 1;
|
|
8345
|
+
}
|
|
8346
|
+
|
|
8347
|
+
return naturalCompare(typeA.name, typeB.name);
|
|
8348
|
+
})
|
|
8349
|
+
.map((x) => x.name);
|
|
8350
|
+
}
|
|
8351
|
+
/**
|
|
8352
|
+
* For the field name provided, determine if there are any similar field names
|
|
8353
|
+
* that may be the result of a typo.
|
|
8354
|
+
*/
|
|
8355
|
+
|
|
8356
|
+
function getSuggestedFieldNames(type, fieldName) {
|
|
8357
|
+
if (isObjectType(type) || isInterfaceType(type)) {
|
|
8358
|
+
const possibleFieldNames = Object.keys(type.getFields());
|
|
8359
|
+
return suggestionList(fieldName, possibleFieldNames);
|
|
8360
|
+
} // Otherwise, must be a Union type, which does not define fields.
|
|
8361
|
+
|
|
8362
|
+
return [];
|
|
8363
|
+
}
|
|
8364
|
+
|
|
8365
|
+
/**
|
|
8366
|
+
* Unique fragment names
|
|
8367
|
+
*
|
|
8368
|
+
* A GraphQL document is only valid if all defined fragments have unique names.
|
|
8369
|
+
*
|
|
8370
|
+
* See https://spec.graphql.org/draft/#sec-Fragment-Name-Uniqueness
|
|
8371
|
+
*/
|
|
8372
|
+
function UniqueFragmentNamesRule(context) {
|
|
8373
|
+
const knownFragmentNames = Object.create(null);
|
|
8374
|
+
return {
|
|
8375
|
+
OperationDefinition: () => false,
|
|
8376
|
+
|
|
8377
|
+
FragmentDefinition(node) {
|
|
8378
|
+
const fragmentName = node.name.value;
|
|
8379
|
+
|
|
8380
|
+
if (knownFragmentNames[fragmentName]) {
|
|
8381
|
+
context.reportError(
|
|
8382
|
+
new GraphQLError(
|
|
8383
|
+
`There can be only one fragment named "${fragmentName}".`,
|
|
8384
|
+
[knownFragmentNames[fragmentName], node.name],
|
|
8385
|
+
),
|
|
8386
|
+
);
|
|
8387
|
+
} else {
|
|
8388
|
+
knownFragmentNames[fragmentName] = node.name;
|
|
8389
|
+
}
|
|
8390
|
+
|
|
8391
|
+
return false;
|
|
8392
|
+
},
|
|
8393
|
+
};
|
|
8394
|
+
}
|
|
8395
|
+
|
|
8396
|
+
/**
|
|
8397
|
+
* Known fragment names
|
|
8398
|
+
*
|
|
8399
|
+
* A GraphQL document is only valid if all `...Fragment` fragment spreads refer
|
|
8400
|
+
* to fragments defined in the same document.
|
|
8401
|
+
*
|
|
8402
|
+
* See https://spec.graphql.org/draft/#sec-Fragment-spread-target-defined
|
|
8403
|
+
*/
|
|
8404
|
+
function KnownFragmentNamesRule(context) {
|
|
8405
|
+
return {
|
|
8406
|
+
FragmentSpread(node) {
|
|
8407
|
+
const fragmentName = node.name.value;
|
|
8408
|
+
const fragment = context.getFragment(fragmentName);
|
|
8409
|
+
|
|
8410
|
+
if (!fragment) {
|
|
8411
|
+
context.reportError(
|
|
8412
|
+
new GraphQLError(`Unknown fragment "${fragmentName}".`, node.name),
|
|
8413
|
+
);
|
|
8414
|
+
}
|
|
8415
|
+
},
|
|
8416
|
+
};
|
|
8417
|
+
}
|
|
8418
|
+
|
|
8419
|
+
/**
|
|
8420
|
+
* No unused fragments
|
|
8421
|
+
*
|
|
8422
|
+
* A GraphQL document is only valid if all fragment definitions are spread
|
|
8423
|
+
* within operations, or spread within other fragments spread within operations.
|
|
8424
|
+
*
|
|
8425
|
+
* See https://spec.graphql.org/draft/#sec-Fragments-Must-Be-Used
|
|
8426
|
+
*/
|
|
8427
|
+
function NoUnusedFragmentsRule(context) {
|
|
8428
|
+
const operationDefs = [];
|
|
8429
|
+
const fragmentDefs = [];
|
|
8430
|
+
return {
|
|
8431
|
+
OperationDefinition(node) {
|
|
8432
|
+
operationDefs.push(node);
|
|
8433
|
+
return false;
|
|
8434
|
+
},
|
|
8435
|
+
|
|
8436
|
+
FragmentDefinition(node) {
|
|
8437
|
+
fragmentDefs.push(node);
|
|
8438
|
+
return false;
|
|
8439
|
+
},
|
|
8440
|
+
|
|
8441
|
+
Document: {
|
|
8442
|
+
leave() {
|
|
8443
|
+
const fragmentNameUsed = Object.create(null);
|
|
8444
|
+
|
|
8445
|
+
for (const operation of operationDefs) {
|
|
8446
|
+
for (const fragment of context.getRecursivelyReferencedFragments(
|
|
8447
|
+
operation,
|
|
8448
|
+
)) {
|
|
8449
|
+
fragmentNameUsed[fragment.name.value] = true;
|
|
8450
|
+
}
|
|
8451
|
+
}
|
|
8452
|
+
|
|
8453
|
+
for (const fragmentDef of fragmentDefs) {
|
|
8454
|
+
const fragName = fragmentDef.name.value;
|
|
8455
|
+
|
|
8456
|
+
if (fragmentNameUsed[fragName] !== true) {
|
|
8457
|
+
context.reportError(
|
|
8458
|
+
new GraphQLError(
|
|
8459
|
+
`Fragment "${fragName}" is never used.`,
|
|
8460
|
+
fragmentDef,
|
|
8461
|
+
),
|
|
8462
|
+
);
|
|
8463
|
+
}
|
|
8464
|
+
}
|
|
8465
|
+
},
|
|
8466
|
+
},
|
|
8467
|
+
};
|
|
8468
|
+
}
|
|
8469
|
+
|
|
8470
|
+
/**
|
|
8471
|
+
* Possible fragment spread
|
|
8472
|
+
*
|
|
8473
|
+
* A fragment spread is only valid if the type condition could ever possibly
|
|
8474
|
+
* be true: if there is a non-empty intersection of the possible parent types,
|
|
8475
|
+
* and possible types which pass the type condition.
|
|
8476
|
+
*/
|
|
8477
|
+
function PossibleFragmentSpreadsRule(context) {
|
|
8478
|
+
return {
|
|
8479
|
+
InlineFragment(node) {
|
|
8480
|
+
const fragType = context.getType();
|
|
8481
|
+
const parentType = context.getParentType();
|
|
8482
|
+
|
|
8483
|
+
if (
|
|
8484
|
+
isCompositeType(fragType) &&
|
|
8485
|
+
isCompositeType(parentType) &&
|
|
8486
|
+
!doTypesOverlap(context.getSchema(), fragType, parentType)
|
|
8487
|
+
) {
|
|
8488
|
+
const parentTypeStr = inspect(parentType);
|
|
8489
|
+
const fragTypeStr = inspect(fragType);
|
|
8490
|
+
context.reportError(
|
|
8491
|
+
new GraphQLError(
|
|
8492
|
+
`Fragment cannot be spread here as objects of type "${parentTypeStr}" can never be of type "${fragTypeStr}".`,
|
|
8493
|
+
node,
|
|
8494
|
+
),
|
|
8495
|
+
);
|
|
8496
|
+
}
|
|
8497
|
+
},
|
|
8498
|
+
|
|
8499
|
+
FragmentSpread(node) {
|
|
8500
|
+
const fragName = node.name.value;
|
|
8501
|
+
const fragType = getFragmentType(context, fragName);
|
|
8502
|
+
const parentType = context.getParentType();
|
|
8503
|
+
|
|
8504
|
+
if (
|
|
8505
|
+
fragType &&
|
|
8506
|
+
parentType &&
|
|
8507
|
+
!doTypesOverlap(context.getSchema(), fragType, parentType)
|
|
8508
|
+
) {
|
|
8509
|
+
const parentTypeStr = inspect(parentType);
|
|
8510
|
+
const fragTypeStr = inspect(fragType);
|
|
8511
|
+
context.reportError(
|
|
8512
|
+
new GraphQLError(
|
|
8513
|
+
`Fragment "${fragName}" cannot be spread here as objects of type "${parentTypeStr}" can never be of type "${fragTypeStr}".`,
|
|
8514
|
+
node,
|
|
8515
|
+
),
|
|
8516
|
+
);
|
|
8517
|
+
}
|
|
8518
|
+
},
|
|
8519
|
+
};
|
|
8520
|
+
}
|
|
8521
|
+
|
|
8522
|
+
function getFragmentType(context, name) {
|
|
8523
|
+
const frag = context.getFragment(name);
|
|
8524
|
+
|
|
8525
|
+
if (frag) {
|
|
8526
|
+
const type = typeFromAST(context.getSchema(), frag.typeCondition);
|
|
8527
|
+
|
|
8528
|
+
if (isCompositeType(type)) {
|
|
8529
|
+
return type;
|
|
8530
|
+
}
|
|
8531
|
+
}
|
|
8532
|
+
}
|
|
8533
|
+
|
|
8534
|
+
/**
|
|
8535
|
+
* No fragment cycles
|
|
8536
|
+
*
|
|
8537
|
+
* The graph of fragment spreads must not form any cycles including spreading itself.
|
|
8538
|
+
* Otherwise an operation could infinitely spread or infinitely execute on cycles in the underlying data.
|
|
8539
|
+
*
|
|
8540
|
+
* See https://spec.graphql.org/draft/#sec-Fragment-spreads-must-not-form-cycles
|
|
8541
|
+
*/
|
|
8542
|
+
function NoFragmentCyclesRule(context) {
|
|
8543
|
+
// Tracks already visited fragments to maintain O(N) and to ensure that cycles
|
|
8544
|
+
// are not redundantly reported.
|
|
8545
|
+
const visitedFrags = Object.create(null); // Array of AST nodes used to produce meaningful errors
|
|
8546
|
+
|
|
8547
|
+
const spreadPath = []; // Position in the spread path
|
|
8548
|
+
|
|
8549
|
+
const spreadPathIndexByName = Object.create(null);
|
|
8550
|
+
return {
|
|
8551
|
+
OperationDefinition: () => false,
|
|
8552
|
+
|
|
8553
|
+
FragmentDefinition(node) {
|
|
8554
|
+
detectCycleRecursive(node);
|
|
8555
|
+
return false;
|
|
8556
|
+
},
|
|
8557
|
+
}; // This does a straight-forward DFS to find cycles.
|
|
8558
|
+
// It does not terminate when a cycle was found but continues to explore
|
|
8559
|
+
// the graph to find all possible cycles.
|
|
8560
|
+
|
|
8561
|
+
function detectCycleRecursive(fragment) {
|
|
8562
|
+
if (visitedFrags[fragment.name.value]) {
|
|
8563
|
+
return;
|
|
8564
|
+
}
|
|
8565
|
+
|
|
8566
|
+
const fragmentName = fragment.name.value;
|
|
8567
|
+
visitedFrags[fragmentName] = true;
|
|
8568
|
+
const spreadNodes = context.getFragmentSpreads(fragment.selectionSet);
|
|
8569
|
+
|
|
8570
|
+
if (spreadNodes.length === 0) {
|
|
8571
|
+
return;
|
|
8572
|
+
}
|
|
8573
|
+
|
|
8574
|
+
spreadPathIndexByName[fragmentName] = spreadPath.length;
|
|
8575
|
+
|
|
8576
|
+
for (const spreadNode of spreadNodes) {
|
|
8577
|
+
const spreadName = spreadNode.name.value;
|
|
8578
|
+
const cycleIndex = spreadPathIndexByName[spreadName];
|
|
8579
|
+
spreadPath.push(spreadNode);
|
|
8580
|
+
|
|
8581
|
+
if (cycleIndex === undefined) {
|
|
8582
|
+
const spreadFragment = context.getFragment(spreadName);
|
|
8583
|
+
|
|
8584
|
+
if (spreadFragment) {
|
|
8585
|
+
detectCycleRecursive(spreadFragment);
|
|
8586
|
+
}
|
|
8587
|
+
} else {
|
|
8588
|
+
const cyclePath = spreadPath.slice(cycleIndex);
|
|
8589
|
+
const viaPath = cyclePath
|
|
8590
|
+
.slice(0, -1)
|
|
8591
|
+
.map((s) => '"' + s.name.value + '"')
|
|
8592
|
+
.join(', ');
|
|
8593
|
+
context.reportError(
|
|
8594
|
+
new GraphQLError(
|
|
8595
|
+
`Cannot spread fragment "${spreadName}" within itself` +
|
|
8596
|
+
(viaPath !== '' ? ` via ${viaPath}.` : '.'),
|
|
8597
|
+
cyclePath,
|
|
8598
|
+
),
|
|
8599
|
+
);
|
|
8600
|
+
}
|
|
8601
|
+
|
|
8602
|
+
spreadPath.pop();
|
|
8603
|
+
}
|
|
8604
|
+
|
|
8605
|
+
spreadPathIndexByName[fragmentName] = undefined;
|
|
8606
|
+
}
|
|
8607
|
+
}
|
|
8608
|
+
|
|
8609
|
+
/**
|
|
8610
|
+
* Groups array items into a Map, given a function to produce grouping key.
|
|
8611
|
+
*/
|
|
8612
|
+
function groupBy(list, keyFn) {
|
|
8613
|
+
const result = new Map();
|
|
8614
|
+
|
|
8615
|
+
for (const item of list) {
|
|
8616
|
+
const key = keyFn(item);
|
|
8617
|
+
const group = result.get(key);
|
|
8618
|
+
|
|
8619
|
+
if (group === undefined) {
|
|
8620
|
+
result.set(key, [item]);
|
|
8621
|
+
} else {
|
|
8622
|
+
group.push(item);
|
|
8623
|
+
}
|
|
8624
|
+
}
|
|
8625
|
+
|
|
8626
|
+
return result;
|
|
8627
|
+
}
|
|
8628
|
+
|
|
8629
|
+
/**
|
|
8630
|
+
* Unique variable names
|
|
8631
|
+
*
|
|
8632
|
+
* A GraphQL operation is only valid if all its variables are uniquely named.
|
|
8633
|
+
*/
|
|
8634
|
+
function UniqueVariableNamesRule(context) {
|
|
8635
|
+
return {
|
|
8636
|
+
OperationDefinition(operationNode) {
|
|
8637
|
+
var _operationNode$variab;
|
|
8638
|
+
|
|
8639
|
+
// istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
8640
|
+
const variableDefinitions =
|
|
8641
|
+
(_operationNode$variab = operationNode.variableDefinitions) !== null &&
|
|
8642
|
+
_operationNode$variab !== void 0
|
|
8643
|
+
? _operationNode$variab
|
|
8644
|
+
: [];
|
|
8645
|
+
const seenVariableDefinitions = groupBy(
|
|
8646
|
+
variableDefinitions,
|
|
8647
|
+
(node) => node.variable.name.value,
|
|
8648
|
+
);
|
|
8649
|
+
|
|
8650
|
+
for (const [variableName, variableNodes] of seenVariableDefinitions) {
|
|
8651
|
+
if (variableNodes.length > 1) {
|
|
8652
|
+
context.reportError(
|
|
8653
|
+
new GraphQLError(
|
|
8654
|
+
`There can be only one variable named "$${variableName}".`,
|
|
8655
|
+
variableNodes.map((node) => node.variable.name),
|
|
8656
|
+
),
|
|
8657
|
+
);
|
|
8658
|
+
}
|
|
8659
|
+
}
|
|
8660
|
+
},
|
|
8661
|
+
};
|
|
8662
|
+
}
|
|
8663
|
+
|
|
8664
|
+
/**
|
|
8665
|
+
* No undefined variables
|
|
8666
|
+
*
|
|
8667
|
+
* A GraphQL operation is only valid if all variables encountered, both directly
|
|
8668
|
+
* and via fragment spreads, are defined by that operation.
|
|
8669
|
+
*
|
|
8670
|
+
* See https://spec.graphql.org/draft/#sec-All-Variable-Uses-Defined
|
|
8671
|
+
*/
|
|
8672
|
+
function NoUndefinedVariablesRule(context) {
|
|
8673
|
+
let variableNameDefined = Object.create(null);
|
|
8674
|
+
return {
|
|
8675
|
+
OperationDefinition: {
|
|
8676
|
+
enter() {
|
|
8677
|
+
variableNameDefined = Object.create(null);
|
|
8678
|
+
},
|
|
8679
|
+
|
|
8680
|
+
leave(operation) {
|
|
8681
|
+
const usages = context.getRecursiveVariableUsages(operation);
|
|
8682
|
+
|
|
8683
|
+
for (const { node } of usages) {
|
|
8684
|
+
const varName = node.name.value;
|
|
8685
|
+
|
|
8686
|
+
if (variableNameDefined[varName] !== true) {
|
|
8687
|
+
context.reportError(
|
|
8688
|
+
new GraphQLError(
|
|
8689
|
+
operation.name
|
|
8690
|
+
? `Variable "$${varName}" is not defined by operation "${operation.name.value}".`
|
|
8691
|
+
: `Variable "$${varName}" is not defined.`,
|
|
8692
|
+
[node, operation],
|
|
8693
|
+
),
|
|
8694
|
+
);
|
|
8695
|
+
}
|
|
8696
|
+
}
|
|
8697
|
+
},
|
|
8698
|
+
},
|
|
8699
|
+
|
|
8700
|
+
VariableDefinition(node) {
|
|
8701
|
+
variableNameDefined[node.variable.name.value] = true;
|
|
8702
|
+
},
|
|
8703
|
+
};
|
|
8704
|
+
}
|
|
8705
|
+
|
|
8706
|
+
/**
|
|
8707
|
+
* No unused variables
|
|
8708
|
+
*
|
|
8709
|
+
* A GraphQL operation is only valid if all variables defined by an operation
|
|
8710
|
+
* are used, either directly or within a spread fragment.
|
|
8711
|
+
*
|
|
8712
|
+
* See https://spec.graphql.org/draft/#sec-All-Variables-Used
|
|
8713
|
+
*/
|
|
8714
|
+
function NoUnusedVariablesRule(context) {
|
|
8715
|
+
let variableDefs = [];
|
|
8716
|
+
return {
|
|
8717
|
+
OperationDefinition: {
|
|
8718
|
+
enter() {
|
|
8719
|
+
variableDefs = [];
|
|
8720
|
+
},
|
|
8721
|
+
|
|
8722
|
+
leave(operation) {
|
|
8723
|
+
const variableNameUsed = Object.create(null);
|
|
8724
|
+
const usages = context.getRecursiveVariableUsages(operation);
|
|
8725
|
+
|
|
8726
|
+
for (const { node } of usages) {
|
|
8727
|
+
variableNameUsed[node.name.value] = true;
|
|
8728
|
+
}
|
|
8729
|
+
|
|
8730
|
+
for (const variableDef of variableDefs) {
|
|
8731
|
+
const variableName = variableDef.variable.name.value;
|
|
8732
|
+
|
|
8733
|
+
if (variableNameUsed[variableName] !== true) {
|
|
8734
|
+
context.reportError(
|
|
8735
|
+
new GraphQLError(
|
|
8736
|
+
operation.name
|
|
8737
|
+
? `Variable "$${variableName}" is never used in operation "${operation.name.value}".`
|
|
8738
|
+
: `Variable "$${variableName}" is never used.`,
|
|
8739
|
+
variableDef,
|
|
8740
|
+
),
|
|
8741
|
+
);
|
|
8742
|
+
}
|
|
8743
|
+
}
|
|
8744
|
+
},
|
|
8745
|
+
},
|
|
8746
|
+
|
|
8747
|
+
VariableDefinition(def) {
|
|
8748
|
+
variableDefs.push(def);
|
|
8749
|
+
},
|
|
8750
|
+
};
|
|
8751
|
+
}
|
|
8752
|
+
|
|
8753
|
+
/**
|
|
8754
|
+
* Known directives
|
|
8755
|
+
*
|
|
8756
|
+
* A GraphQL document is only valid if all `@directives` are known by the
|
|
8757
|
+
* schema and legally positioned.
|
|
8758
|
+
*
|
|
8759
|
+
* See https://spec.graphql.org/draft/#sec-Directives-Are-Defined
|
|
8760
|
+
*/
|
|
8761
|
+
function KnownDirectivesRule(context) {
|
|
8762
|
+
const locationsMap = Object.create(null);
|
|
8763
|
+
const schema = context.getSchema();
|
|
8764
|
+
const definedDirectives = schema
|
|
8765
|
+
? schema.getDirectives()
|
|
8766
|
+
: specifiedDirectives;
|
|
8767
|
+
|
|
8768
|
+
for (const directive of definedDirectives) {
|
|
8769
|
+
locationsMap[directive.name] = directive.locations;
|
|
8770
|
+
}
|
|
8771
|
+
|
|
8772
|
+
const astDefinitions = context.getDocument().definitions;
|
|
8773
|
+
|
|
8774
|
+
for (const def of astDefinitions) {
|
|
8775
|
+
if (def.kind === Kind.DIRECTIVE_DEFINITION) {
|
|
8776
|
+
locationsMap[def.name.value] = def.locations.map((name) => name.value);
|
|
8777
|
+
}
|
|
8778
|
+
}
|
|
8779
|
+
|
|
8780
|
+
return {
|
|
8781
|
+
Directive(node, _key, _parent, _path, ancestors) {
|
|
8782
|
+
const name = node.name.value;
|
|
8783
|
+
const locations = locationsMap[name];
|
|
8784
|
+
|
|
8785
|
+
if (!locations) {
|
|
8786
|
+
context.reportError(
|
|
8787
|
+
new GraphQLError(`Unknown directive "@${name}".`, node),
|
|
8788
|
+
);
|
|
8789
|
+
return;
|
|
8790
|
+
}
|
|
8791
|
+
|
|
8792
|
+
const candidateLocation = getDirectiveLocationForASTPath(ancestors);
|
|
8793
|
+
|
|
8794
|
+
if (candidateLocation && !locations.includes(candidateLocation)) {
|
|
8795
|
+
context.reportError(
|
|
8796
|
+
new GraphQLError(
|
|
8797
|
+
`Directive "@${name}" may not be used on ${candidateLocation}.`,
|
|
8798
|
+
node,
|
|
8799
|
+
),
|
|
8800
|
+
);
|
|
8801
|
+
}
|
|
8802
|
+
},
|
|
8803
|
+
};
|
|
8804
|
+
}
|
|
8805
|
+
|
|
8806
|
+
function getDirectiveLocationForASTPath(ancestors) {
|
|
8807
|
+
const appliedTo = ancestors[ancestors.length - 1];
|
|
8808
|
+
'kind' in appliedTo || invariant(false);
|
|
8809
|
+
|
|
8810
|
+
switch (appliedTo.kind) {
|
|
8811
|
+
case Kind.OPERATION_DEFINITION:
|
|
8812
|
+
return getDirectiveLocationForOperation(appliedTo.operation);
|
|
8813
|
+
|
|
8814
|
+
case Kind.FIELD:
|
|
8815
|
+
return DirectiveLocation.FIELD;
|
|
8816
|
+
|
|
8817
|
+
case Kind.FRAGMENT_SPREAD:
|
|
8818
|
+
return DirectiveLocation.FRAGMENT_SPREAD;
|
|
8819
|
+
|
|
8820
|
+
case Kind.INLINE_FRAGMENT:
|
|
8821
|
+
return DirectiveLocation.INLINE_FRAGMENT;
|
|
8822
|
+
|
|
8823
|
+
case Kind.FRAGMENT_DEFINITION:
|
|
8824
|
+
return DirectiveLocation.FRAGMENT_DEFINITION;
|
|
8825
|
+
|
|
8826
|
+
case Kind.VARIABLE_DEFINITION:
|
|
8827
|
+
return DirectiveLocation.VARIABLE_DEFINITION;
|
|
8828
|
+
|
|
8829
|
+
case Kind.SCHEMA_DEFINITION:
|
|
8830
|
+
case Kind.SCHEMA_EXTENSION:
|
|
8831
|
+
return DirectiveLocation.SCHEMA;
|
|
8832
|
+
|
|
8833
|
+
case Kind.SCALAR_TYPE_DEFINITION:
|
|
8834
|
+
case Kind.SCALAR_TYPE_EXTENSION:
|
|
8835
|
+
return DirectiveLocation.SCALAR;
|
|
8836
|
+
|
|
8837
|
+
case Kind.OBJECT_TYPE_DEFINITION:
|
|
8838
|
+
case Kind.OBJECT_TYPE_EXTENSION:
|
|
8839
|
+
return DirectiveLocation.OBJECT;
|
|
8840
|
+
|
|
8841
|
+
case Kind.FIELD_DEFINITION:
|
|
8842
|
+
return DirectiveLocation.FIELD_DEFINITION;
|
|
8843
|
+
|
|
8844
|
+
case Kind.INTERFACE_TYPE_DEFINITION:
|
|
8845
|
+
case Kind.INTERFACE_TYPE_EXTENSION:
|
|
8846
|
+
return DirectiveLocation.INTERFACE;
|
|
8847
|
+
|
|
8848
|
+
case Kind.UNION_TYPE_DEFINITION:
|
|
8849
|
+
case Kind.UNION_TYPE_EXTENSION:
|
|
8850
|
+
return DirectiveLocation.UNION;
|
|
8851
|
+
|
|
8852
|
+
case Kind.ENUM_TYPE_DEFINITION:
|
|
8853
|
+
case Kind.ENUM_TYPE_EXTENSION:
|
|
8854
|
+
return DirectiveLocation.ENUM;
|
|
8855
|
+
|
|
8856
|
+
case Kind.ENUM_VALUE_DEFINITION:
|
|
8857
|
+
return DirectiveLocation.ENUM_VALUE;
|
|
8858
|
+
|
|
8859
|
+
case Kind.INPUT_OBJECT_TYPE_DEFINITION:
|
|
8860
|
+
case Kind.INPUT_OBJECT_TYPE_EXTENSION:
|
|
8861
|
+
return DirectiveLocation.INPUT_OBJECT;
|
|
8862
|
+
|
|
8863
|
+
case Kind.INPUT_VALUE_DEFINITION: {
|
|
8864
|
+
const parentNode = ancestors[ancestors.length - 3];
|
|
8865
|
+
'kind' in parentNode || invariant(false);
|
|
8866
|
+
return parentNode.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION
|
|
8867
|
+
? DirectiveLocation.INPUT_FIELD_DEFINITION
|
|
8868
|
+
: DirectiveLocation.ARGUMENT_DEFINITION;
|
|
8869
|
+
}
|
|
8870
|
+
}
|
|
8871
|
+
}
|
|
8872
|
+
|
|
8873
|
+
function getDirectiveLocationForOperation(operation) {
|
|
8874
|
+
switch (operation) {
|
|
8875
|
+
case OperationTypeNode.QUERY:
|
|
8876
|
+
return DirectiveLocation.QUERY;
|
|
8877
|
+
|
|
8878
|
+
case OperationTypeNode.MUTATION:
|
|
8879
|
+
return DirectiveLocation.MUTATION;
|
|
8880
|
+
|
|
8881
|
+
case OperationTypeNode.SUBSCRIPTION:
|
|
8882
|
+
return DirectiveLocation.SUBSCRIPTION;
|
|
8883
|
+
} // istanbul ignore next (Not reachable. All possible types have been considered)
|
|
8884
|
+
|
|
8885
|
+
invariant(false, 'Unexpected operation: ' + inspect(operation));
|
|
8886
|
+
}
|
|
8887
|
+
|
|
8888
|
+
/**
|
|
8889
|
+
* Unique directive names per location
|
|
8890
|
+
*
|
|
8891
|
+
* A GraphQL document is only valid if all non-repeatable directives at
|
|
8892
|
+
* a given location are uniquely named.
|
|
8893
|
+
*
|
|
8894
|
+
* See https://spec.graphql.org/draft/#sec-Directives-Are-Unique-Per-Location
|
|
8895
|
+
*/
|
|
8896
|
+
function UniqueDirectivesPerLocationRule(context) {
|
|
8897
|
+
const uniqueDirectiveMap = Object.create(null);
|
|
8898
|
+
const schema = context.getSchema();
|
|
8899
|
+
const definedDirectives = schema
|
|
8900
|
+
? schema.getDirectives()
|
|
8901
|
+
: specifiedDirectives;
|
|
8902
|
+
|
|
8903
|
+
for (const directive of definedDirectives) {
|
|
8904
|
+
uniqueDirectiveMap[directive.name] = !directive.isRepeatable;
|
|
8905
|
+
}
|
|
8906
|
+
|
|
8907
|
+
const astDefinitions = context.getDocument().definitions;
|
|
8908
|
+
|
|
8909
|
+
for (const def of astDefinitions) {
|
|
8910
|
+
if (def.kind === Kind.DIRECTIVE_DEFINITION) {
|
|
8911
|
+
uniqueDirectiveMap[def.name.value] = !def.repeatable;
|
|
8912
|
+
}
|
|
8913
|
+
}
|
|
8914
|
+
|
|
8915
|
+
const schemaDirectives = Object.create(null);
|
|
8916
|
+
const typeDirectivesMap = Object.create(null);
|
|
8917
|
+
return {
|
|
8918
|
+
// Many different AST nodes may contain directives. Rather than listing
|
|
8919
|
+
// them all, just listen for entering any node, and check to see if it
|
|
8920
|
+
// defines any directives.
|
|
8921
|
+
enter(node) {
|
|
8922
|
+
if (!('directives' in node) || !node.directives) {
|
|
8923
|
+
return;
|
|
8924
|
+
}
|
|
8925
|
+
|
|
8926
|
+
let seenDirectives;
|
|
8927
|
+
|
|
8928
|
+
if (
|
|
8929
|
+
node.kind === Kind.SCHEMA_DEFINITION ||
|
|
8930
|
+
node.kind === Kind.SCHEMA_EXTENSION
|
|
8931
|
+
) {
|
|
8932
|
+
seenDirectives = schemaDirectives;
|
|
8933
|
+
} else if (isTypeDefinitionNode(node) || isTypeExtensionNode(node)) {
|
|
8934
|
+
const typeName = node.name.value;
|
|
8935
|
+
seenDirectives = typeDirectivesMap[typeName];
|
|
8936
|
+
|
|
8937
|
+
if (seenDirectives === undefined) {
|
|
8938
|
+
typeDirectivesMap[typeName] = seenDirectives = Object.create(null);
|
|
8939
|
+
}
|
|
8940
|
+
} else {
|
|
8941
|
+
seenDirectives = Object.create(null);
|
|
8942
|
+
}
|
|
8943
|
+
|
|
8944
|
+
for (const directive of node.directives) {
|
|
8945
|
+
const directiveName = directive.name.value;
|
|
8946
|
+
|
|
8947
|
+
if (uniqueDirectiveMap[directiveName]) {
|
|
8948
|
+
if (seenDirectives[directiveName]) {
|
|
8949
|
+
context.reportError(
|
|
8950
|
+
new GraphQLError(
|
|
8951
|
+
`The directive "@${directiveName}" can only be used once at this location.`,
|
|
8952
|
+
[seenDirectives[directiveName], directive],
|
|
8953
|
+
),
|
|
8954
|
+
);
|
|
8955
|
+
} else {
|
|
8956
|
+
seenDirectives[directiveName] = directive;
|
|
8957
|
+
}
|
|
8958
|
+
}
|
|
8959
|
+
}
|
|
8960
|
+
},
|
|
8961
|
+
};
|
|
8962
|
+
}
|
|
8963
|
+
|
|
8964
|
+
/**
|
|
8965
|
+
* Known argument names
|
|
8966
|
+
*
|
|
8967
|
+
* A GraphQL field is only valid if all supplied arguments are defined by
|
|
8968
|
+
* that field.
|
|
8969
|
+
*
|
|
8970
|
+
* See https://spec.graphql.org/draft/#sec-Argument-Names
|
|
8971
|
+
* See https://spec.graphql.org/draft/#sec-Directives-Are-In-Valid-Locations
|
|
8972
|
+
*/
|
|
8973
|
+
function KnownArgumentNamesRule(context) {
|
|
8974
|
+
return {
|
|
8975
|
+
// eslint-disable-next-line new-cap
|
|
8976
|
+
...KnownArgumentNamesOnDirectivesRule(context),
|
|
8977
|
+
|
|
8978
|
+
Argument(argNode) {
|
|
8979
|
+
const argDef = context.getArgument();
|
|
8980
|
+
const fieldDef = context.getFieldDef();
|
|
8981
|
+
const parentType = context.getParentType();
|
|
8982
|
+
|
|
8983
|
+
if (!argDef && fieldDef && parentType) {
|
|
8984
|
+
const argName = argNode.name.value;
|
|
8985
|
+
const knownArgsNames = fieldDef.args.map((arg) => arg.name);
|
|
8986
|
+
const suggestions = suggestionList(argName, knownArgsNames);
|
|
8987
|
+
context.reportError(
|
|
8988
|
+
new GraphQLError(
|
|
8989
|
+
`Unknown argument "${argName}" on field "${parentType.name}.${fieldDef.name}".` +
|
|
8990
|
+
didYouMean(suggestions),
|
|
8991
|
+
argNode,
|
|
8992
|
+
),
|
|
8993
|
+
);
|
|
8994
|
+
}
|
|
8995
|
+
},
|
|
8996
|
+
};
|
|
8997
|
+
}
|
|
8998
|
+
/**
|
|
8999
|
+
* @internal
|
|
9000
|
+
*/
|
|
9001
|
+
|
|
9002
|
+
function KnownArgumentNamesOnDirectivesRule(context) {
|
|
9003
|
+
const directiveArgs = Object.create(null);
|
|
9004
|
+
const schema = context.getSchema();
|
|
9005
|
+
const definedDirectives = schema
|
|
9006
|
+
? schema.getDirectives()
|
|
9007
|
+
: specifiedDirectives;
|
|
9008
|
+
|
|
9009
|
+
for (const directive of definedDirectives) {
|
|
9010
|
+
directiveArgs[directive.name] = directive.args.map((arg) => arg.name);
|
|
9011
|
+
}
|
|
9012
|
+
|
|
9013
|
+
const astDefinitions = context.getDocument().definitions;
|
|
9014
|
+
|
|
9015
|
+
for (const def of astDefinitions) {
|
|
9016
|
+
if (def.kind === Kind.DIRECTIVE_DEFINITION) {
|
|
9017
|
+
var _def$arguments;
|
|
9018
|
+
|
|
9019
|
+
// istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
9020
|
+
const argsNodes =
|
|
9021
|
+
(_def$arguments = def.arguments) !== null && _def$arguments !== void 0
|
|
9022
|
+
? _def$arguments
|
|
9023
|
+
: [];
|
|
9024
|
+
directiveArgs[def.name.value] = argsNodes.map((arg) => arg.name.value);
|
|
9025
|
+
}
|
|
9026
|
+
}
|
|
9027
|
+
|
|
9028
|
+
return {
|
|
9029
|
+
Directive(directiveNode) {
|
|
9030
|
+
const directiveName = directiveNode.name.value;
|
|
9031
|
+
const knownArgs = directiveArgs[directiveName];
|
|
9032
|
+
|
|
9033
|
+
if (directiveNode.arguments && knownArgs) {
|
|
9034
|
+
for (const argNode of directiveNode.arguments) {
|
|
9035
|
+
const argName = argNode.name.value;
|
|
9036
|
+
|
|
9037
|
+
if (!knownArgs.includes(argName)) {
|
|
9038
|
+
const suggestions = suggestionList(argName, knownArgs);
|
|
9039
|
+
context.reportError(
|
|
9040
|
+
new GraphQLError(
|
|
9041
|
+
`Unknown argument "${argName}" on directive "@${directiveName}".` +
|
|
9042
|
+
didYouMean(suggestions),
|
|
9043
|
+
argNode,
|
|
9044
|
+
),
|
|
9045
|
+
);
|
|
9046
|
+
}
|
|
9047
|
+
}
|
|
9048
|
+
}
|
|
9049
|
+
|
|
9050
|
+
return false;
|
|
9051
|
+
},
|
|
9052
|
+
};
|
|
9053
|
+
}
|
|
9054
|
+
|
|
9055
|
+
/**
|
|
9056
|
+
* Unique argument names
|
|
9057
|
+
*
|
|
9058
|
+
* A GraphQL field or directive is only valid if all supplied arguments are
|
|
9059
|
+
* uniquely named.
|
|
9060
|
+
*
|
|
9061
|
+
* See https://spec.graphql.org/draft/#sec-Argument-Names
|
|
9062
|
+
*/
|
|
9063
|
+
function UniqueArgumentNamesRule(context) {
|
|
9064
|
+
return {
|
|
9065
|
+
Field: checkArgUniqueness,
|
|
9066
|
+
Directive: checkArgUniqueness,
|
|
9067
|
+
};
|
|
9068
|
+
|
|
9069
|
+
function checkArgUniqueness(parentNode) {
|
|
9070
|
+
var _parentNode$arguments;
|
|
9071
|
+
|
|
9072
|
+
// istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
9073
|
+
const argumentNodes =
|
|
9074
|
+
(_parentNode$arguments = parentNode.arguments) !== null &&
|
|
9075
|
+
_parentNode$arguments !== void 0
|
|
9076
|
+
? _parentNode$arguments
|
|
9077
|
+
: [];
|
|
9078
|
+
const seenArgs = groupBy(argumentNodes, (arg) => arg.name.value);
|
|
9079
|
+
|
|
9080
|
+
for (const [argName, argNodes] of seenArgs) {
|
|
9081
|
+
if (argNodes.length > 1) {
|
|
9082
|
+
context.reportError(
|
|
9083
|
+
new GraphQLError(
|
|
9084
|
+
`There can be only one argument named "${argName}".`,
|
|
9085
|
+
argNodes.map((node) => node.name),
|
|
9086
|
+
),
|
|
9087
|
+
);
|
|
9088
|
+
}
|
|
9089
|
+
}
|
|
9090
|
+
}
|
|
9091
|
+
}
|
|
9092
|
+
|
|
9093
|
+
/**
|
|
9094
|
+
* Value literals of correct type
|
|
9095
|
+
*
|
|
9096
|
+
* A GraphQL document is only valid if all value literals are of the type
|
|
9097
|
+
* expected at their position.
|
|
9098
|
+
*
|
|
9099
|
+
* See https://spec.graphql.org/draft/#sec-Values-of-Correct-Type
|
|
9100
|
+
*/
|
|
9101
|
+
function ValuesOfCorrectTypeRule(context) {
|
|
9102
|
+
return {
|
|
9103
|
+
ListValue(node) {
|
|
9104
|
+
// Note: TypeInfo will traverse into a list's item type, so look to the
|
|
9105
|
+
// parent input type to check if it is a list.
|
|
9106
|
+
const type = getNullableType(context.getParentInputType());
|
|
9107
|
+
|
|
9108
|
+
if (!isListType(type)) {
|
|
9109
|
+
isValidValueNode(context, node);
|
|
9110
|
+
return false; // Don't traverse further.
|
|
9111
|
+
}
|
|
9112
|
+
},
|
|
9113
|
+
|
|
9114
|
+
ObjectValue(node) {
|
|
9115
|
+
const type = getNamedType(context.getInputType());
|
|
9116
|
+
|
|
9117
|
+
if (!isInputObjectType(type)) {
|
|
9118
|
+
isValidValueNode(context, node);
|
|
9119
|
+
return false; // Don't traverse further.
|
|
9120
|
+
} // Ensure every required field exists.
|
|
9121
|
+
|
|
9122
|
+
const fieldNodeMap = keyMap(node.fields, (field) => field.name.value);
|
|
9123
|
+
|
|
9124
|
+
for (const fieldDef of Object.values(type.getFields())) {
|
|
9125
|
+
const fieldNode = fieldNodeMap[fieldDef.name];
|
|
9126
|
+
|
|
9127
|
+
if (!fieldNode && isRequiredInputField(fieldDef)) {
|
|
9128
|
+
const typeStr = inspect(fieldDef.type);
|
|
9129
|
+
context.reportError(
|
|
9130
|
+
new GraphQLError(
|
|
9131
|
+
`Field "${type.name}.${fieldDef.name}" of required type "${typeStr}" was not provided.`,
|
|
9132
|
+
node,
|
|
9133
|
+
),
|
|
9134
|
+
);
|
|
9135
|
+
}
|
|
9136
|
+
}
|
|
9137
|
+
},
|
|
9138
|
+
|
|
9139
|
+
ObjectField(node) {
|
|
9140
|
+
const parentType = getNamedType(context.getParentInputType());
|
|
9141
|
+
const fieldType = context.getInputType();
|
|
9142
|
+
|
|
9143
|
+
if (!fieldType && isInputObjectType(parentType)) {
|
|
9144
|
+
const suggestions = suggestionList(
|
|
9145
|
+
node.name.value,
|
|
9146
|
+
Object.keys(parentType.getFields()),
|
|
9147
|
+
);
|
|
9148
|
+
context.reportError(
|
|
9149
|
+
new GraphQLError(
|
|
9150
|
+
`Field "${node.name.value}" is not defined by type "${parentType.name}".` +
|
|
9151
|
+
didYouMean(suggestions),
|
|
9152
|
+
node,
|
|
9153
|
+
),
|
|
9154
|
+
);
|
|
9155
|
+
}
|
|
9156
|
+
},
|
|
9157
|
+
|
|
9158
|
+
NullValue(node) {
|
|
9159
|
+
const type = context.getInputType();
|
|
9160
|
+
|
|
9161
|
+
if (isNonNullType(type)) {
|
|
9162
|
+
context.reportError(
|
|
9163
|
+
new GraphQLError(
|
|
9164
|
+
`Expected value of type "${inspect(type)}", found ${print(node)}.`,
|
|
9165
|
+
node,
|
|
9166
|
+
),
|
|
9167
|
+
);
|
|
9168
|
+
}
|
|
9169
|
+
},
|
|
9170
|
+
|
|
9171
|
+
EnumValue: (node) => isValidValueNode(context, node),
|
|
9172
|
+
IntValue: (node) => isValidValueNode(context, node),
|
|
9173
|
+
FloatValue: (node) => isValidValueNode(context, node),
|
|
9174
|
+
StringValue: (node) => isValidValueNode(context, node),
|
|
9175
|
+
BooleanValue: (node) => isValidValueNode(context, node),
|
|
9176
|
+
};
|
|
9177
|
+
}
|
|
9178
|
+
/**
|
|
9179
|
+
* Any value literal may be a valid representation of a Scalar, depending on
|
|
9180
|
+
* that scalar type.
|
|
9181
|
+
*/
|
|
9182
|
+
|
|
9183
|
+
function isValidValueNode(context, node) {
|
|
9184
|
+
// Report any error at the full type expected by the location.
|
|
9185
|
+
const locationType = context.getInputType();
|
|
9186
|
+
|
|
9187
|
+
if (!locationType) {
|
|
9188
|
+
return;
|
|
9189
|
+
}
|
|
9190
|
+
|
|
9191
|
+
const type = getNamedType(locationType);
|
|
9192
|
+
|
|
9193
|
+
if (!isLeafType(type)) {
|
|
9194
|
+
const typeStr = inspect(locationType);
|
|
9195
|
+
context.reportError(
|
|
9196
|
+
new GraphQLError(
|
|
9197
|
+
`Expected value of type "${typeStr}", found ${print(node)}.`,
|
|
9198
|
+
node,
|
|
9199
|
+
),
|
|
9200
|
+
);
|
|
9201
|
+
return;
|
|
9202
|
+
} // Scalars and Enums determine if a literal value is valid via parseLiteral(),
|
|
9203
|
+
// which may throw or return an invalid value to indicate failure.
|
|
9204
|
+
|
|
9205
|
+
try {
|
|
9206
|
+
const parseResult = type.parseLiteral(
|
|
9207
|
+
node,
|
|
9208
|
+
undefined,
|
|
9209
|
+
/* variables */
|
|
9210
|
+
);
|
|
9211
|
+
|
|
9212
|
+
if (parseResult === undefined) {
|
|
9213
|
+
const typeStr = inspect(locationType);
|
|
9214
|
+
context.reportError(
|
|
9215
|
+
new GraphQLError(
|
|
9216
|
+
`Expected value of type "${typeStr}", found ${print(node)}.`,
|
|
9217
|
+
node,
|
|
9218
|
+
),
|
|
9219
|
+
);
|
|
9220
|
+
}
|
|
9221
|
+
} catch (error) {
|
|
9222
|
+
const typeStr = inspect(locationType);
|
|
9223
|
+
|
|
9224
|
+
if (error instanceof GraphQLError) {
|
|
9225
|
+
context.reportError(error);
|
|
9226
|
+
} else {
|
|
9227
|
+
context.reportError(
|
|
9228
|
+
new GraphQLError(
|
|
9229
|
+
`Expected value of type "${typeStr}", found ${print(node)}; ` +
|
|
9230
|
+
error.message,
|
|
9231
|
+
node,
|
|
9232
|
+
undefined,
|
|
9233
|
+
undefined,
|
|
9234
|
+
undefined,
|
|
9235
|
+
error,
|
|
9236
|
+
),
|
|
9237
|
+
);
|
|
9238
|
+
}
|
|
9239
|
+
}
|
|
9240
|
+
}
|
|
9241
|
+
|
|
9242
|
+
/**
|
|
9243
|
+
* Provided required arguments
|
|
9244
|
+
*
|
|
9245
|
+
* A field or directive is only valid if all required (non-null without a
|
|
9246
|
+
* default value) field arguments have been provided.
|
|
9247
|
+
*/
|
|
9248
|
+
function ProvidedRequiredArgumentsRule(context) {
|
|
9249
|
+
return {
|
|
9250
|
+
// eslint-disable-next-line new-cap
|
|
9251
|
+
...ProvidedRequiredArgumentsOnDirectivesRule(context),
|
|
9252
|
+
Field: {
|
|
9253
|
+
// Validate on leave to allow for deeper errors to appear first.
|
|
9254
|
+
leave(fieldNode) {
|
|
9255
|
+
var _fieldNode$arguments;
|
|
9256
|
+
|
|
9257
|
+
const fieldDef = context.getFieldDef();
|
|
9258
|
+
|
|
9259
|
+
if (!fieldDef) {
|
|
9260
|
+
return false;
|
|
9261
|
+
}
|
|
9262
|
+
|
|
9263
|
+
const providedArgs = new Set( // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
9264
|
+
(_fieldNode$arguments = fieldNode.arguments) === null ||
|
|
9265
|
+
_fieldNode$arguments === void 0
|
|
9266
|
+
? void 0
|
|
9267
|
+
: _fieldNode$arguments.map((arg) => arg.name.value),
|
|
9268
|
+
);
|
|
9269
|
+
|
|
9270
|
+
for (const argDef of fieldDef.args) {
|
|
9271
|
+
if (!providedArgs.has(argDef.name) && isRequiredArgument(argDef)) {
|
|
9272
|
+
const argTypeStr = inspect(argDef.type);
|
|
9273
|
+
context.reportError(
|
|
9274
|
+
new GraphQLError(
|
|
9275
|
+
`Field "${fieldDef.name}" argument "${argDef.name}" of type "${argTypeStr}" is required, but it was not provided.`,
|
|
9276
|
+
fieldNode,
|
|
9277
|
+
),
|
|
9278
|
+
);
|
|
9279
|
+
}
|
|
9280
|
+
}
|
|
9281
|
+
},
|
|
9282
|
+
},
|
|
9283
|
+
};
|
|
9284
|
+
}
|
|
9285
|
+
/**
|
|
9286
|
+
* @internal
|
|
9287
|
+
*/
|
|
9288
|
+
|
|
9289
|
+
function ProvidedRequiredArgumentsOnDirectivesRule(context) {
|
|
9290
|
+
var _schema$getDirectives;
|
|
9291
|
+
|
|
9292
|
+
const requiredArgsMap = Object.create(null);
|
|
9293
|
+
const schema = context.getSchema();
|
|
9294
|
+
const definedDirectives =
|
|
9295
|
+
(_schema$getDirectives =
|
|
9296
|
+
schema === null || schema === void 0
|
|
9297
|
+
? void 0
|
|
9298
|
+
: schema.getDirectives()) !== null && _schema$getDirectives !== void 0
|
|
9299
|
+
? _schema$getDirectives
|
|
9300
|
+
: specifiedDirectives;
|
|
9301
|
+
|
|
9302
|
+
for (const directive of definedDirectives) {
|
|
9303
|
+
requiredArgsMap[directive.name] = keyMap(
|
|
9304
|
+
directive.args.filter(isRequiredArgument),
|
|
9305
|
+
(arg) => arg.name,
|
|
9306
|
+
);
|
|
9307
|
+
}
|
|
9308
|
+
|
|
9309
|
+
const astDefinitions = context.getDocument().definitions;
|
|
9310
|
+
|
|
9311
|
+
for (const def of astDefinitions) {
|
|
9312
|
+
if (def.kind === Kind.DIRECTIVE_DEFINITION) {
|
|
9313
|
+
var _def$arguments;
|
|
9314
|
+
|
|
9315
|
+
// istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
9316
|
+
const argNodes =
|
|
9317
|
+
(_def$arguments = def.arguments) !== null && _def$arguments !== void 0
|
|
9318
|
+
? _def$arguments
|
|
9319
|
+
: [];
|
|
9320
|
+
requiredArgsMap[def.name.value] = keyMap(
|
|
9321
|
+
argNodes.filter(isRequiredArgumentNode),
|
|
9322
|
+
(arg) => arg.name.value,
|
|
9323
|
+
);
|
|
9324
|
+
}
|
|
9325
|
+
}
|
|
9326
|
+
|
|
9327
|
+
return {
|
|
9328
|
+
Directive: {
|
|
9329
|
+
// Validate on leave to allow for deeper errors to appear first.
|
|
9330
|
+
leave(directiveNode) {
|
|
9331
|
+
const directiveName = directiveNode.name.value;
|
|
9332
|
+
const requiredArgs = requiredArgsMap[directiveName];
|
|
9333
|
+
|
|
9334
|
+
if (requiredArgs) {
|
|
9335
|
+
var _directiveNode$argume;
|
|
9336
|
+
|
|
9337
|
+
// istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
9338
|
+
const argNodes =
|
|
9339
|
+
(_directiveNode$argume = directiveNode.arguments) !== null &&
|
|
9340
|
+
_directiveNode$argume !== void 0
|
|
9341
|
+
? _directiveNode$argume
|
|
9342
|
+
: [];
|
|
9343
|
+
const argNodeMap = new Set(argNodes.map((arg) => arg.name.value));
|
|
9344
|
+
|
|
9345
|
+
for (const [argName, argDef] of Object.entries(requiredArgs)) {
|
|
9346
|
+
if (!argNodeMap.has(argName)) {
|
|
9347
|
+
const argType = isType(argDef.type)
|
|
9348
|
+
? inspect(argDef.type)
|
|
9349
|
+
: print(argDef.type);
|
|
9350
|
+
context.reportError(
|
|
9351
|
+
new GraphQLError(
|
|
9352
|
+
`Directive "@${directiveName}" argument "${argName}" of type "${argType}" is required, but it was not provided.`,
|
|
9353
|
+
directiveNode,
|
|
9354
|
+
),
|
|
9355
|
+
);
|
|
9356
|
+
}
|
|
9357
|
+
}
|
|
9358
|
+
}
|
|
9359
|
+
},
|
|
9360
|
+
},
|
|
9361
|
+
};
|
|
9362
|
+
}
|
|
9363
|
+
|
|
9364
|
+
function isRequiredArgumentNode(arg) {
|
|
9365
|
+
return arg.type.kind === Kind.NON_NULL_TYPE && arg.defaultValue == null;
|
|
9366
|
+
}
|
|
9367
|
+
|
|
9368
|
+
/**
|
|
9369
|
+
* Variables in allowed position
|
|
9370
|
+
*
|
|
9371
|
+
* Variable usages must be compatible with the arguments they are passed to.
|
|
9372
|
+
*
|
|
9373
|
+
* See https://spec.graphql.org/draft/#sec-All-Variable-Usages-are-Allowed
|
|
9374
|
+
*/
|
|
9375
|
+
function VariablesInAllowedPositionRule(context) {
|
|
9376
|
+
let varDefMap = Object.create(null);
|
|
9377
|
+
return {
|
|
9378
|
+
OperationDefinition: {
|
|
9379
|
+
enter() {
|
|
9380
|
+
varDefMap = Object.create(null);
|
|
9381
|
+
},
|
|
9382
|
+
|
|
9383
|
+
leave(operation) {
|
|
9384
|
+
const usages = context.getRecursiveVariableUsages(operation);
|
|
9385
|
+
|
|
9386
|
+
for (const { node, type, defaultValue } of usages) {
|
|
9387
|
+
const varName = node.name.value;
|
|
9388
|
+
const varDef = varDefMap[varName];
|
|
9389
|
+
|
|
9390
|
+
if (varDef && type) {
|
|
9391
|
+
// A var type is allowed if it is the same or more strict (e.g. is
|
|
9392
|
+
// a subtype of) than the expected type. It can be more strict if
|
|
9393
|
+
// the variable type is non-null when the expected type is nullable.
|
|
9394
|
+
// If both are list types, the variable item type can be more strict
|
|
9395
|
+
// than the expected item type (contravariant).
|
|
9396
|
+
const schema = context.getSchema();
|
|
9397
|
+
const varType = typeFromAST(schema, varDef.type);
|
|
9398
|
+
|
|
9399
|
+
if (
|
|
9400
|
+
varType &&
|
|
9401
|
+
!allowedVariableUsage(
|
|
9402
|
+
schema,
|
|
9403
|
+
varType,
|
|
9404
|
+
varDef.defaultValue,
|
|
9405
|
+
type,
|
|
9406
|
+
defaultValue,
|
|
9407
|
+
)
|
|
9408
|
+
) {
|
|
9409
|
+
const varTypeStr = inspect(varType);
|
|
9410
|
+
const typeStr = inspect(type);
|
|
9411
|
+
context.reportError(
|
|
9412
|
+
new GraphQLError(
|
|
9413
|
+
`Variable "$${varName}" of type "${varTypeStr}" used in position expecting type "${typeStr}".`,
|
|
9414
|
+
[varDef, node],
|
|
9415
|
+
),
|
|
9416
|
+
);
|
|
9417
|
+
}
|
|
9418
|
+
}
|
|
9419
|
+
}
|
|
9420
|
+
},
|
|
9421
|
+
},
|
|
9422
|
+
|
|
9423
|
+
VariableDefinition(node) {
|
|
9424
|
+
varDefMap[node.variable.name.value] = node;
|
|
9425
|
+
},
|
|
9426
|
+
};
|
|
9427
|
+
}
|
|
9428
|
+
/**
|
|
9429
|
+
* Returns true if the variable is allowed in the location it was found,
|
|
9430
|
+
* which includes considering if default values exist for either the variable
|
|
9431
|
+
* or the location at which it is located.
|
|
9432
|
+
*/
|
|
9433
|
+
|
|
9434
|
+
function allowedVariableUsage(
|
|
9435
|
+
schema,
|
|
9436
|
+
varType,
|
|
9437
|
+
varDefaultValue,
|
|
9438
|
+
locationType,
|
|
9439
|
+
locationDefaultValue,
|
|
9440
|
+
) {
|
|
9441
|
+
if (isNonNullType(locationType) && !isNonNullType(varType)) {
|
|
9442
|
+
const hasNonNullVariableDefaultValue =
|
|
9443
|
+
varDefaultValue != null && varDefaultValue.kind !== Kind.NULL;
|
|
9444
|
+
const hasLocationDefaultValue = locationDefaultValue !== undefined;
|
|
9445
|
+
|
|
9446
|
+
if (!hasNonNullVariableDefaultValue && !hasLocationDefaultValue) {
|
|
9447
|
+
return false;
|
|
9448
|
+
}
|
|
9449
|
+
|
|
9450
|
+
const nullableLocationType = locationType.ofType;
|
|
9451
|
+
return isTypeSubTypeOf(schema, varType, nullableLocationType);
|
|
9452
|
+
}
|
|
9453
|
+
|
|
9454
|
+
return isTypeSubTypeOf(schema, varType, locationType);
|
|
9455
|
+
}
|
|
9456
|
+
|
|
9457
|
+
function reasonMessage(reason) {
|
|
9458
|
+
if (Array.isArray(reason)) {
|
|
9459
|
+
return reason
|
|
9460
|
+
.map(
|
|
9461
|
+
([responseName, subReason]) =>
|
|
9462
|
+
`subfields "${responseName}" conflict because ` +
|
|
9463
|
+
reasonMessage(subReason),
|
|
9464
|
+
)
|
|
9465
|
+
.join(' and ');
|
|
9466
|
+
}
|
|
9467
|
+
|
|
9468
|
+
return reason;
|
|
9469
|
+
}
|
|
9470
|
+
/**
|
|
9471
|
+
* Overlapping fields can be merged
|
|
9472
|
+
*
|
|
9473
|
+
* A selection set is only valid if all fields (including spreading any
|
|
9474
|
+
* fragments) either correspond to distinct response names or can be merged
|
|
9475
|
+
* without ambiguity.
|
|
9476
|
+
*
|
|
9477
|
+
* See https://spec.graphql.org/draft/#sec-Field-Selection-Merging
|
|
9478
|
+
*/
|
|
9479
|
+
|
|
9480
|
+
function OverlappingFieldsCanBeMergedRule(context) {
|
|
9481
|
+
// A memoization for when two fragments are compared "between" each other for
|
|
9482
|
+
// conflicts. Two fragments may be compared many times, so memoizing this can
|
|
9483
|
+
// dramatically improve the performance of this validator.
|
|
9484
|
+
const comparedFragmentPairs = new PairSet(); // A cache for the "field map" and list of fragment names found in any given
|
|
9485
|
+
// selection set. Selection sets may be asked for this information multiple
|
|
9486
|
+
// times, so this improves the performance of this validator.
|
|
9487
|
+
|
|
9488
|
+
const cachedFieldsAndFragmentNames = new Map();
|
|
9489
|
+
return {
|
|
9490
|
+
SelectionSet(selectionSet) {
|
|
9491
|
+
const conflicts = findConflictsWithinSelectionSet(
|
|
9492
|
+
context,
|
|
9493
|
+
cachedFieldsAndFragmentNames,
|
|
9494
|
+
comparedFragmentPairs,
|
|
9495
|
+
context.getParentType(),
|
|
9496
|
+
selectionSet,
|
|
9497
|
+
);
|
|
9498
|
+
|
|
9499
|
+
for (const [[responseName, reason], fields1, fields2] of conflicts) {
|
|
9500
|
+
const reasonMsg = reasonMessage(reason);
|
|
9501
|
+
context.reportError(
|
|
9502
|
+
new GraphQLError(
|
|
9503
|
+
`Fields "${responseName}" conflict because ${reasonMsg}. Use different aliases on the fields to fetch both if this was intentional.`,
|
|
9504
|
+
fields1.concat(fields2),
|
|
9505
|
+
),
|
|
9506
|
+
);
|
|
9507
|
+
}
|
|
9508
|
+
},
|
|
9509
|
+
};
|
|
9510
|
+
}
|
|
9511
|
+
|
|
9512
|
+
/**
|
|
9513
|
+
* Algorithm:
|
|
9514
|
+
*
|
|
9515
|
+
* Conflicts occur when two fields exist in a query which will produce the same
|
|
9516
|
+
* response name, but represent differing values, thus creating a conflict.
|
|
9517
|
+
* The algorithm below finds all conflicts via making a series of comparisons
|
|
9518
|
+
* between fields. In order to compare as few fields as possible, this makes
|
|
9519
|
+
* a series of comparisons "within" sets of fields and "between" sets of fields.
|
|
9520
|
+
*
|
|
9521
|
+
* Given any selection set, a collection produces both a set of fields by
|
|
9522
|
+
* also including all inline fragments, as well as a list of fragments
|
|
9523
|
+
* referenced by fragment spreads.
|
|
9524
|
+
*
|
|
9525
|
+
* A) Each selection set represented in the document first compares "within" its
|
|
9526
|
+
* collected set of fields, finding any conflicts between every pair of
|
|
9527
|
+
* overlapping fields.
|
|
9528
|
+
* Note: This is the *only time* that a the fields "within" a set are compared
|
|
9529
|
+
* to each other. After this only fields "between" sets are compared.
|
|
9530
|
+
*
|
|
9531
|
+
* B) Also, if any fragment is referenced in a selection set, then a
|
|
9532
|
+
* comparison is made "between" the original set of fields and the
|
|
9533
|
+
* referenced fragment.
|
|
9534
|
+
*
|
|
9535
|
+
* C) Also, if multiple fragments are referenced, then comparisons
|
|
9536
|
+
* are made "between" each referenced fragment.
|
|
9537
|
+
*
|
|
9538
|
+
* D) When comparing "between" a set of fields and a referenced fragment, first
|
|
9539
|
+
* a comparison is made between each field in the original set of fields and
|
|
9540
|
+
* each field in the the referenced set of fields.
|
|
9541
|
+
*
|
|
9542
|
+
* E) Also, if any fragment is referenced in the referenced selection set,
|
|
9543
|
+
* then a comparison is made "between" the original set of fields and the
|
|
9544
|
+
* referenced fragment (recursively referring to step D).
|
|
9545
|
+
*
|
|
9546
|
+
* F) When comparing "between" two fragments, first a comparison is made between
|
|
9547
|
+
* each field in the first referenced set of fields and each field in the the
|
|
9548
|
+
* second referenced set of fields.
|
|
9549
|
+
*
|
|
9550
|
+
* G) Also, any fragments referenced by the first must be compared to the
|
|
9551
|
+
* second, and any fragments referenced by the second must be compared to the
|
|
9552
|
+
* first (recursively referring to step F).
|
|
9553
|
+
*
|
|
9554
|
+
* H) When comparing two fields, if both have selection sets, then a comparison
|
|
9555
|
+
* is made "between" both selection sets, first comparing the set of fields in
|
|
9556
|
+
* the first selection set with the set of fields in the second.
|
|
9557
|
+
*
|
|
9558
|
+
* I) Also, if any fragment is referenced in either selection set, then a
|
|
9559
|
+
* comparison is made "between" the other set of fields and the
|
|
9560
|
+
* referenced fragment.
|
|
9561
|
+
*
|
|
9562
|
+
* J) Also, if two fragments are referenced in both selection sets, then a
|
|
9563
|
+
* comparison is made "between" the two fragments.
|
|
9564
|
+
*
|
|
9565
|
+
*/
|
|
9566
|
+
// Find all conflicts found "within" a selection set, including those found
|
|
9567
|
+
// via spreading in fragments. Called when visiting each SelectionSet in the
|
|
9568
|
+
// GraphQL Document.
|
|
9569
|
+
function findConflictsWithinSelectionSet(
|
|
9570
|
+
context,
|
|
9571
|
+
cachedFieldsAndFragmentNames,
|
|
9572
|
+
comparedFragmentPairs,
|
|
9573
|
+
parentType,
|
|
9574
|
+
selectionSet,
|
|
9575
|
+
) {
|
|
9576
|
+
const conflicts = [];
|
|
9577
|
+
const [fieldMap, fragmentNames] = getFieldsAndFragmentNames(
|
|
9578
|
+
context,
|
|
9579
|
+
cachedFieldsAndFragmentNames,
|
|
9580
|
+
parentType,
|
|
9581
|
+
selectionSet,
|
|
9582
|
+
); // (A) Find find all conflicts "within" the fields of this selection set.
|
|
9583
|
+
// Note: this is the *only place* `collectConflictsWithin` is called.
|
|
9584
|
+
|
|
9585
|
+
collectConflictsWithin(
|
|
9586
|
+
context,
|
|
9587
|
+
conflicts,
|
|
9588
|
+
cachedFieldsAndFragmentNames,
|
|
9589
|
+
comparedFragmentPairs,
|
|
9590
|
+
fieldMap,
|
|
9591
|
+
);
|
|
9592
|
+
|
|
9593
|
+
if (fragmentNames.length !== 0) {
|
|
9594
|
+
// (B) Then collect conflicts between these fields and those represented by
|
|
9595
|
+
// each spread fragment name found.
|
|
9596
|
+
for (let i = 0; i < fragmentNames.length; i++) {
|
|
9597
|
+
collectConflictsBetweenFieldsAndFragment(
|
|
9598
|
+
context,
|
|
9599
|
+
conflicts,
|
|
9600
|
+
cachedFieldsAndFragmentNames,
|
|
9601
|
+
comparedFragmentPairs,
|
|
9602
|
+
false,
|
|
9603
|
+
fieldMap,
|
|
9604
|
+
fragmentNames[i],
|
|
9605
|
+
); // (C) Then compare this fragment with all other fragments found in this
|
|
9606
|
+
// selection set to collect conflicts between fragments spread together.
|
|
9607
|
+
// This compares each item in the list of fragment names to every other
|
|
9608
|
+
// item in that same list (except for itself).
|
|
9609
|
+
|
|
9610
|
+
for (let j = i + 1; j < fragmentNames.length; j++) {
|
|
9611
|
+
collectConflictsBetweenFragments(
|
|
9612
|
+
context,
|
|
9613
|
+
conflicts,
|
|
9614
|
+
cachedFieldsAndFragmentNames,
|
|
9615
|
+
comparedFragmentPairs,
|
|
9616
|
+
false,
|
|
9617
|
+
fragmentNames[i],
|
|
9618
|
+
fragmentNames[j],
|
|
9619
|
+
);
|
|
9620
|
+
}
|
|
9621
|
+
}
|
|
9622
|
+
}
|
|
9623
|
+
|
|
9624
|
+
return conflicts;
|
|
9625
|
+
} // Collect all conflicts found between a set of fields and a fragment reference
|
|
9626
|
+
// including via spreading in any nested fragments.
|
|
9627
|
+
|
|
9628
|
+
function collectConflictsBetweenFieldsAndFragment(
|
|
9629
|
+
context,
|
|
9630
|
+
conflicts,
|
|
9631
|
+
cachedFieldsAndFragmentNames,
|
|
9632
|
+
comparedFragmentPairs,
|
|
9633
|
+
areMutuallyExclusive,
|
|
9634
|
+
fieldMap,
|
|
9635
|
+
fragmentName,
|
|
9636
|
+
) {
|
|
9637
|
+
const fragment = context.getFragment(fragmentName);
|
|
9638
|
+
|
|
9639
|
+
if (!fragment) {
|
|
9640
|
+
return;
|
|
9641
|
+
}
|
|
9642
|
+
|
|
9643
|
+
const [fieldMap2, referencedFragmentNames] =
|
|
9644
|
+
getReferencedFieldsAndFragmentNames(
|
|
9645
|
+
context,
|
|
9646
|
+
cachedFieldsAndFragmentNames,
|
|
9647
|
+
fragment,
|
|
9648
|
+
); // Do not compare a fragment's fieldMap to itself.
|
|
9649
|
+
|
|
9650
|
+
if (fieldMap === fieldMap2) {
|
|
9651
|
+
return;
|
|
9652
|
+
} // (D) First collect any conflicts between the provided collection of fields
|
|
9653
|
+
// and the collection of fields represented by the given fragment.
|
|
9654
|
+
|
|
9655
|
+
collectConflictsBetween(
|
|
9656
|
+
context,
|
|
9657
|
+
conflicts,
|
|
9658
|
+
cachedFieldsAndFragmentNames,
|
|
9659
|
+
comparedFragmentPairs,
|
|
9660
|
+
areMutuallyExclusive,
|
|
9661
|
+
fieldMap,
|
|
9662
|
+
fieldMap2,
|
|
9663
|
+
); // (E) Then collect any conflicts between the provided collection of fields
|
|
9664
|
+
// and any fragment names found in the given fragment.
|
|
9665
|
+
|
|
9666
|
+
for (const referencedFragmentName of referencedFragmentNames) {
|
|
9667
|
+
collectConflictsBetweenFieldsAndFragment(
|
|
9668
|
+
context,
|
|
9669
|
+
conflicts,
|
|
9670
|
+
cachedFieldsAndFragmentNames,
|
|
9671
|
+
comparedFragmentPairs,
|
|
9672
|
+
areMutuallyExclusive,
|
|
9673
|
+
fieldMap,
|
|
9674
|
+
referencedFragmentName,
|
|
9675
|
+
);
|
|
9676
|
+
}
|
|
9677
|
+
} // Collect all conflicts found between two fragments, including via spreading in
|
|
9678
|
+
// any nested fragments.
|
|
9679
|
+
|
|
9680
|
+
function collectConflictsBetweenFragments(
|
|
9681
|
+
context,
|
|
9682
|
+
conflicts,
|
|
9683
|
+
cachedFieldsAndFragmentNames,
|
|
9684
|
+
comparedFragmentPairs,
|
|
9685
|
+
areMutuallyExclusive,
|
|
9686
|
+
fragmentName1,
|
|
9687
|
+
fragmentName2,
|
|
9688
|
+
) {
|
|
9689
|
+
// No need to compare a fragment to itself.
|
|
9690
|
+
if (fragmentName1 === fragmentName2) {
|
|
9691
|
+
return;
|
|
9692
|
+
} // Memoize so two fragments are not compared for conflicts more than once.
|
|
9693
|
+
|
|
9694
|
+
if (
|
|
9695
|
+
comparedFragmentPairs.has(
|
|
9696
|
+
fragmentName1,
|
|
9697
|
+
fragmentName2,
|
|
9698
|
+
areMutuallyExclusive,
|
|
9699
|
+
)
|
|
9700
|
+
) {
|
|
9701
|
+
return;
|
|
9702
|
+
}
|
|
9703
|
+
|
|
9704
|
+
comparedFragmentPairs.add(fragmentName1, fragmentName2, areMutuallyExclusive);
|
|
9705
|
+
const fragment1 = context.getFragment(fragmentName1);
|
|
9706
|
+
const fragment2 = context.getFragment(fragmentName2);
|
|
9707
|
+
|
|
9708
|
+
if (!fragment1 || !fragment2) {
|
|
9709
|
+
return;
|
|
9710
|
+
}
|
|
9711
|
+
|
|
9712
|
+
const [fieldMap1, referencedFragmentNames1] =
|
|
9713
|
+
getReferencedFieldsAndFragmentNames(
|
|
9714
|
+
context,
|
|
9715
|
+
cachedFieldsAndFragmentNames,
|
|
9716
|
+
fragment1,
|
|
9717
|
+
);
|
|
9718
|
+
const [fieldMap2, referencedFragmentNames2] =
|
|
9719
|
+
getReferencedFieldsAndFragmentNames(
|
|
9720
|
+
context,
|
|
9721
|
+
cachedFieldsAndFragmentNames,
|
|
9722
|
+
fragment2,
|
|
9723
|
+
); // (F) First, collect all conflicts between these two collections of fields
|
|
9724
|
+
// (not including any nested fragments).
|
|
9725
|
+
|
|
9726
|
+
collectConflictsBetween(
|
|
9727
|
+
context,
|
|
9728
|
+
conflicts,
|
|
9729
|
+
cachedFieldsAndFragmentNames,
|
|
9730
|
+
comparedFragmentPairs,
|
|
9731
|
+
areMutuallyExclusive,
|
|
9732
|
+
fieldMap1,
|
|
9733
|
+
fieldMap2,
|
|
9734
|
+
); // (G) Then collect conflicts between the first fragment and any nested
|
|
9735
|
+
// fragments spread in the second fragment.
|
|
9736
|
+
|
|
9737
|
+
for (const referencedFragmentName2 of referencedFragmentNames2) {
|
|
9738
|
+
collectConflictsBetweenFragments(
|
|
9739
|
+
context,
|
|
9740
|
+
conflicts,
|
|
9741
|
+
cachedFieldsAndFragmentNames,
|
|
9742
|
+
comparedFragmentPairs,
|
|
9743
|
+
areMutuallyExclusive,
|
|
9744
|
+
fragmentName1,
|
|
9745
|
+
referencedFragmentName2,
|
|
9746
|
+
);
|
|
9747
|
+
} // (G) Then collect conflicts between the second fragment and any nested
|
|
9748
|
+
// fragments spread in the first fragment.
|
|
9749
|
+
|
|
9750
|
+
for (const referencedFragmentName1 of referencedFragmentNames1) {
|
|
9751
|
+
collectConflictsBetweenFragments(
|
|
9752
|
+
context,
|
|
9753
|
+
conflicts,
|
|
9754
|
+
cachedFieldsAndFragmentNames,
|
|
9755
|
+
comparedFragmentPairs,
|
|
9756
|
+
areMutuallyExclusive,
|
|
9757
|
+
referencedFragmentName1,
|
|
9758
|
+
fragmentName2,
|
|
9759
|
+
);
|
|
9760
|
+
}
|
|
9761
|
+
} // Find all conflicts found between two selection sets, including those found
|
|
9762
|
+
// via spreading in fragments. Called when determining if conflicts exist
|
|
9763
|
+
// between the sub-fields of two overlapping fields.
|
|
9764
|
+
|
|
9765
|
+
function findConflictsBetweenSubSelectionSets(
|
|
9766
|
+
context,
|
|
9767
|
+
cachedFieldsAndFragmentNames,
|
|
9768
|
+
comparedFragmentPairs,
|
|
9769
|
+
areMutuallyExclusive,
|
|
9770
|
+
parentType1,
|
|
9771
|
+
selectionSet1,
|
|
9772
|
+
parentType2,
|
|
9773
|
+
selectionSet2,
|
|
9774
|
+
) {
|
|
9775
|
+
const conflicts = [];
|
|
9776
|
+
const [fieldMap1, fragmentNames1] = getFieldsAndFragmentNames(
|
|
9777
|
+
context,
|
|
9778
|
+
cachedFieldsAndFragmentNames,
|
|
9779
|
+
parentType1,
|
|
9780
|
+
selectionSet1,
|
|
9781
|
+
);
|
|
9782
|
+
const [fieldMap2, fragmentNames2] = getFieldsAndFragmentNames(
|
|
9783
|
+
context,
|
|
9784
|
+
cachedFieldsAndFragmentNames,
|
|
9785
|
+
parentType2,
|
|
9786
|
+
selectionSet2,
|
|
9787
|
+
); // (H) First, collect all conflicts between these two collections of field.
|
|
9788
|
+
|
|
9789
|
+
collectConflictsBetween(
|
|
9790
|
+
context,
|
|
9791
|
+
conflicts,
|
|
9792
|
+
cachedFieldsAndFragmentNames,
|
|
9793
|
+
comparedFragmentPairs,
|
|
9794
|
+
areMutuallyExclusive,
|
|
9795
|
+
fieldMap1,
|
|
9796
|
+
fieldMap2,
|
|
9797
|
+
); // (I) Then collect conflicts between the first collection of fields and
|
|
9798
|
+
// those referenced by each fragment name associated with the second.
|
|
9799
|
+
|
|
9800
|
+
for (const fragmentName2 of fragmentNames2) {
|
|
9801
|
+
collectConflictsBetweenFieldsAndFragment(
|
|
9802
|
+
context,
|
|
9803
|
+
conflicts,
|
|
9804
|
+
cachedFieldsAndFragmentNames,
|
|
9805
|
+
comparedFragmentPairs,
|
|
9806
|
+
areMutuallyExclusive,
|
|
9807
|
+
fieldMap1,
|
|
9808
|
+
fragmentName2,
|
|
9809
|
+
);
|
|
9810
|
+
} // (I) Then collect conflicts between the second collection of fields and
|
|
9811
|
+
// those referenced by each fragment name associated with the first.
|
|
9812
|
+
|
|
9813
|
+
for (const fragmentName1 of fragmentNames1) {
|
|
9814
|
+
collectConflictsBetweenFieldsAndFragment(
|
|
9815
|
+
context,
|
|
9816
|
+
conflicts,
|
|
9817
|
+
cachedFieldsAndFragmentNames,
|
|
9818
|
+
comparedFragmentPairs,
|
|
9819
|
+
areMutuallyExclusive,
|
|
9820
|
+
fieldMap2,
|
|
9821
|
+
fragmentName1,
|
|
9822
|
+
);
|
|
9823
|
+
} // (J) Also collect conflicts between any fragment names by the first and
|
|
9824
|
+
// fragment names by the second. This compares each item in the first set of
|
|
9825
|
+
// names to each item in the second set of names.
|
|
9826
|
+
|
|
9827
|
+
for (const fragmentName1 of fragmentNames1) {
|
|
9828
|
+
for (const fragmentName2 of fragmentNames2) {
|
|
9829
|
+
collectConflictsBetweenFragments(
|
|
9830
|
+
context,
|
|
9831
|
+
conflicts,
|
|
9832
|
+
cachedFieldsAndFragmentNames,
|
|
9833
|
+
comparedFragmentPairs,
|
|
9834
|
+
areMutuallyExclusive,
|
|
9835
|
+
fragmentName1,
|
|
9836
|
+
fragmentName2,
|
|
9837
|
+
);
|
|
9838
|
+
}
|
|
9839
|
+
}
|
|
9840
|
+
|
|
9841
|
+
return conflicts;
|
|
9842
|
+
} // Collect all Conflicts "within" one collection of fields.
|
|
9843
|
+
|
|
9844
|
+
function collectConflictsWithin(
|
|
9845
|
+
context,
|
|
9846
|
+
conflicts,
|
|
9847
|
+
cachedFieldsAndFragmentNames,
|
|
9848
|
+
comparedFragmentPairs,
|
|
9849
|
+
fieldMap,
|
|
9850
|
+
) {
|
|
9851
|
+
// A field map is a keyed collection, where each key represents a response
|
|
9852
|
+
// name and the value at that key is a list of all fields which provide that
|
|
9853
|
+
// response name. For every response name, if there are multiple fields, they
|
|
9854
|
+
// must be compared to find a potential conflict.
|
|
9855
|
+
for (const [responseName, fields] of Object.entries(fieldMap)) {
|
|
9856
|
+
// This compares every field in the list to every other field in this list
|
|
9857
|
+
// (except to itself). If the list only has one item, nothing needs to
|
|
9858
|
+
// be compared.
|
|
9859
|
+
if (fields.length > 1) {
|
|
9860
|
+
for (let i = 0; i < fields.length; i++) {
|
|
9861
|
+
for (let j = i + 1; j < fields.length; j++) {
|
|
9862
|
+
const conflict = findConflict(
|
|
9863
|
+
context,
|
|
9864
|
+
cachedFieldsAndFragmentNames,
|
|
9865
|
+
comparedFragmentPairs,
|
|
9866
|
+
false, // within one collection is never mutually exclusive
|
|
9867
|
+
responseName,
|
|
9868
|
+
fields[i],
|
|
9869
|
+
fields[j],
|
|
9870
|
+
);
|
|
9871
|
+
|
|
9872
|
+
if (conflict) {
|
|
9873
|
+
conflicts.push(conflict);
|
|
9874
|
+
}
|
|
9875
|
+
}
|
|
9876
|
+
}
|
|
9877
|
+
}
|
|
9878
|
+
}
|
|
9879
|
+
} // Collect all Conflicts between two collections of fields. This is similar to,
|
|
9880
|
+
// but different from the `collectConflictsWithin` function above. This check
|
|
9881
|
+
// assumes that `collectConflictsWithin` has already been called on each
|
|
9882
|
+
// provided collection of fields. This is true because this validator traverses
|
|
9883
|
+
// each individual selection set.
|
|
9884
|
+
|
|
9885
|
+
function collectConflictsBetween(
|
|
9886
|
+
context,
|
|
9887
|
+
conflicts,
|
|
9888
|
+
cachedFieldsAndFragmentNames,
|
|
9889
|
+
comparedFragmentPairs,
|
|
9890
|
+
parentFieldsAreMutuallyExclusive,
|
|
9891
|
+
fieldMap1,
|
|
9892
|
+
fieldMap2,
|
|
9893
|
+
) {
|
|
9894
|
+
// A field map is a keyed collection, where each key represents a response
|
|
9895
|
+
// name and the value at that key is a list of all fields which provide that
|
|
9896
|
+
// response name. For any response name which appears in both provided field
|
|
9897
|
+
// maps, each field from the first field map must be compared to every field
|
|
9898
|
+
// in the second field map to find potential conflicts.
|
|
9899
|
+
for (const [responseName, fields1] of Object.entries(fieldMap1)) {
|
|
9900
|
+
const fields2 = fieldMap2[responseName];
|
|
9901
|
+
|
|
9902
|
+
if (fields2) {
|
|
9903
|
+
for (const field1 of fields1) {
|
|
9904
|
+
for (const field2 of fields2) {
|
|
9905
|
+
const conflict = findConflict(
|
|
9906
|
+
context,
|
|
9907
|
+
cachedFieldsAndFragmentNames,
|
|
9908
|
+
comparedFragmentPairs,
|
|
9909
|
+
parentFieldsAreMutuallyExclusive,
|
|
9910
|
+
responseName,
|
|
9911
|
+
field1,
|
|
9912
|
+
field2,
|
|
9913
|
+
);
|
|
9914
|
+
|
|
9915
|
+
if (conflict) {
|
|
9916
|
+
conflicts.push(conflict);
|
|
9917
|
+
}
|
|
9918
|
+
}
|
|
9919
|
+
}
|
|
9920
|
+
}
|
|
9921
|
+
}
|
|
9922
|
+
} // Determines if there is a conflict between two particular fields, including
|
|
9923
|
+
// comparing their sub-fields.
|
|
9924
|
+
|
|
9925
|
+
function findConflict(
|
|
9926
|
+
context,
|
|
9927
|
+
cachedFieldsAndFragmentNames,
|
|
9928
|
+
comparedFragmentPairs,
|
|
9929
|
+
parentFieldsAreMutuallyExclusive,
|
|
9930
|
+
responseName,
|
|
9931
|
+
field1,
|
|
9932
|
+
field2,
|
|
9933
|
+
) {
|
|
9934
|
+
const [parentType1, node1, def1] = field1;
|
|
9935
|
+
const [parentType2, node2, def2] = field2; // If it is known that two fields could not possibly apply at the same
|
|
9936
|
+
// time, due to the parent types, then it is safe to permit them to diverge
|
|
9937
|
+
// in aliased field or arguments used as they will not present any ambiguity
|
|
9938
|
+
// by differing.
|
|
9939
|
+
// It is known that two parent types could never overlap if they are
|
|
9940
|
+
// different Object types. Interface or Union types might overlap - if not
|
|
9941
|
+
// in the current state of the schema, then perhaps in some future version,
|
|
9942
|
+
// thus may not safely diverge.
|
|
9943
|
+
|
|
9944
|
+
const areMutuallyExclusive =
|
|
9945
|
+
parentFieldsAreMutuallyExclusive ||
|
|
9946
|
+
(parentType1 !== parentType2 &&
|
|
9947
|
+
isObjectType(parentType1) &&
|
|
9948
|
+
isObjectType(parentType2));
|
|
9949
|
+
|
|
9950
|
+
if (!areMutuallyExclusive) {
|
|
9951
|
+
var _node1$arguments, _node2$arguments;
|
|
9952
|
+
|
|
9953
|
+
// Two aliases must refer to the same field.
|
|
9954
|
+
const name1 = node1.name.value;
|
|
9955
|
+
const name2 = node2.name.value;
|
|
9956
|
+
|
|
9957
|
+
if (name1 !== name2) {
|
|
9958
|
+
return [
|
|
9959
|
+
[responseName, `"${name1}" and "${name2}" are different fields`],
|
|
9960
|
+
[node1],
|
|
9961
|
+
[node2],
|
|
9962
|
+
];
|
|
9963
|
+
} // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
9964
|
+
|
|
9965
|
+
const args1 =
|
|
9966
|
+
(_node1$arguments = node1.arguments) !== null &&
|
|
9967
|
+
_node1$arguments !== void 0
|
|
9968
|
+
? _node1$arguments
|
|
9969
|
+
: []; // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
9970
|
+
|
|
9971
|
+
const args2 =
|
|
9972
|
+
(_node2$arguments = node2.arguments) !== null &&
|
|
9973
|
+
_node2$arguments !== void 0
|
|
9974
|
+
? _node2$arguments
|
|
9975
|
+
: []; // Two field calls must have the same arguments.
|
|
9976
|
+
|
|
9977
|
+
if (!sameArguments(args1, args2)) {
|
|
9978
|
+
return [
|
|
9979
|
+
[responseName, 'they have differing arguments'],
|
|
9980
|
+
[node1],
|
|
9981
|
+
[node2],
|
|
9982
|
+
];
|
|
9983
|
+
}
|
|
9984
|
+
} // The return type for each field.
|
|
9985
|
+
|
|
9986
|
+
const type1 = def1 === null || def1 === void 0 ? void 0 : def1.type;
|
|
9987
|
+
const type2 = def2 === null || def2 === void 0 ? void 0 : def2.type;
|
|
9988
|
+
|
|
9989
|
+
if (type1 && type2 && doTypesConflict(type1, type2)) {
|
|
9990
|
+
return [
|
|
9991
|
+
[
|
|
9992
|
+
responseName,
|
|
9993
|
+
`they return conflicting types "${inspect(type1)}" and "${inspect(
|
|
9994
|
+
type2,
|
|
9995
|
+
)}"`,
|
|
9996
|
+
],
|
|
9997
|
+
[node1],
|
|
9998
|
+
[node2],
|
|
9999
|
+
];
|
|
10000
|
+
} // Collect and compare sub-fields. Use the same "visited fragment names" list
|
|
10001
|
+
// for both collections so fields in a fragment reference are never
|
|
10002
|
+
// compared to themselves.
|
|
10003
|
+
|
|
10004
|
+
const selectionSet1 = node1.selectionSet;
|
|
10005
|
+
const selectionSet2 = node2.selectionSet;
|
|
10006
|
+
|
|
10007
|
+
if (selectionSet1 && selectionSet2) {
|
|
10008
|
+
const conflicts = findConflictsBetweenSubSelectionSets(
|
|
10009
|
+
context,
|
|
10010
|
+
cachedFieldsAndFragmentNames,
|
|
10011
|
+
comparedFragmentPairs,
|
|
10012
|
+
areMutuallyExclusive,
|
|
10013
|
+
getNamedType(type1),
|
|
10014
|
+
selectionSet1,
|
|
10015
|
+
getNamedType(type2),
|
|
10016
|
+
selectionSet2,
|
|
10017
|
+
);
|
|
10018
|
+
return subfieldConflicts(conflicts, responseName, node1, node2);
|
|
10019
|
+
}
|
|
10020
|
+
}
|
|
10021
|
+
|
|
10022
|
+
function sameArguments(arguments1, arguments2) {
|
|
10023
|
+
if (arguments1.length !== arguments2.length) {
|
|
10024
|
+
return false;
|
|
10025
|
+
}
|
|
10026
|
+
|
|
10027
|
+
return arguments1.every((argument1) => {
|
|
10028
|
+
const argument2 = arguments2.find(
|
|
10029
|
+
(argument) => argument.name.value === argument1.name.value,
|
|
10030
|
+
);
|
|
10031
|
+
|
|
10032
|
+
if (!argument2) {
|
|
10033
|
+
return false;
|
|
10034
|
+
}
|
|
10035
|
+
|
|
10036
|
+
return sameValue(argument1.value, argument2.value);
|
|
10037
|
+
});
|
|
10038
|
+
}
|
|
10039
|
+
|
|
10040
|
+
function sameValue(value1, value2) {
|
|
10041
|
+
return print(value1) === print(value2);
|
|
10042
|
+
} // Two types conflict if both types could not apply to a value simultaneously.
|
|
10043
|
+
// Composite types are ignored as their individual field types will be compared
|
|
10044
|
+
// later recursively. However List and Non-Null types must match.
|
|
10045
|
+
|
|
10046
|
+
function doTypesConflict(type1, type2) {
|
|
10047
|
+
if (isListType(type1)) {
|
|
10048
|
+
return isListType(type2)
|
|
10049
|
+
? doTypesConflict(type1.ofType, type2.ofType)
|
|
10050
|
+
: true;
|
|
10051
|
+
}
|
|
10052
|
+
|
|
10053
|
+
if (isListType(type2)) {
|
|
10054
|
+
return true;
|
|
10055
|
+
}
|
|
10056
|
+
|
|
10057
|
+
if (isNonNullType(type1)) {
|
|
10058
|
+
return isNonNullType(type2)
|
|
10059
|
+
? doTypesConflict(type1.ofType, type2.ofType)
|
|
10060
|
+
: true;
|
|
10061
|
+
}
|
|
10062
|
+
|
|
10063
|
+
if (isNonNullType(type2)) {
|
|
10064
|
+
return true;
|
|
10065
|
+
}
|
|
10066
|
+
|
|
10067
|
+
if (isLeafType(type1) || isLeafType(type2)) {
|
|
10068
|
+
return type1 !== type2;
|
|
10069
|
+
}
|
|
10070
|
+
|
|
10071
|
+
return false;
|
|
10072
|
+
} // Given a selection set, return the collection of fields (a mapping of response
|
|
10073
|
+
// name to field nodes and definitions) as well as a list of fragment names
|
|
10074
|
+
// referenced via fragment spreads.
|
|
10075
|
+
|
|
10076
|
+
function getFieldsAndFragmentNames(
|
|
10077
|
+
context,
|
|
10078
|
+
cachedFieldsAndFragmentNames,
|
|
10079
|
+
parentType,
|
|
10080
|
+
selectionSet,
|
|
10081
|
+
) {
|
|
10082
|
+
const cached = cachedFieldsAndFragmentNames.get(selectionSet);
|
|
10083
|
+
|
|
10084
|
+
if (cached) {
|
|
10085
|
+
return cached;
|
|
10086
|
+
}
|
|
10087
|
+
|
|
10088
|
+
const nodeAndDefs = Object.create(null);
|
|
10089
|
+
const fragmentNames = Object.create(null);
|
|
10090
|
+
|
|
10091
|
+
_collectFieldsAndFragmentNames(
|
|
10092
|
+
context,
|
|
10093
|
+
parentType,
|
|
10094
|
+
selectionSet,
|
|
10095
|
+
nodeAndDefs,
|
|
10096
|
+
fragmentNames,
|
|
10097
|
+
);
|
|
10098
|
+
|
|
10099
|
+
const result = [nodeAndDefs, Object.keys(fragmentNames)];
|
|
10100
|
+
cachedFieldsAndFragmentNames.set(selectionSet, result);
|
|
10101
|
+
return result;
|
|
10102
|
+
} // Given a reference to a fragment, return the represented collection of fields
|
|
10103
|
+
// as well as a list of nested fragment names referenced via fragment spreads.
|
|
10104
|
+
|
|
10105
|
+
function getReferencedFieldsAndFragmentNames(
|
|
10106
|
+
context,
|
|
10107
|
+
cachedFieldsAndFragmentNames,
|
|
10108
|
+
fragment,
|
|
10109
|
+
) {
|
|
10110
|
+
// Short-circuit building a type from the node if possible.
|
|
10111
|
+
const cached = cachedFieldsAndFragmentNames.get(fragment.selectionSet);
|
|
10112
|
+
|
|
10113
|
+
if (cached) {
|
|
10114
|
+
return cached;
|
|
10115
|
+
}
|
|
10116
|
+
|
|
10117
|
+
const fragmentType = typeFromAST(context.getSchema(), fragment.typeCondition);
|
|
10118
|
+
return getFieldsAndFragmentNames(
|
|
10119
|
+
context,
|
|
10120
|
+
cachedFieldsAndFragmentNames,
|
|
10121
|
+
fragmentType,
|
|
10122
|
+
fragment.selectionSet,
|
|
10123
|
+
);
|
|
10124
|
+
}
|
|
10125
|
+
|
|
10126
|
+
function _collectFieldsAndFragmentNames(
|
|
10127
|
+
context,
|
|
10128
|
+
parentType,
|
|
10129
|
+
selectionSet,
|
|
10130
|
+
nodeAndDefs,
|
|
10131
|
+
fragmentNames,
|
|
10132
|
+
) {
|
|
10133
|
+
for (const selection of selectionSet.selections) {
|
|
10134
|
+
switch (selection.kind) {
|
|
10135
|
+
case Kind.FIELD: {
|
|
10136
|
+
const fieldName = selection.name.value;
|
|
10137
|
+
let fieldDef;
|
|
10138
|
+
|
|
10139
|
+
if (isObjectType(parentType) || isInterfaceType(parentType)) {
|
|
10140
|
+
fieldDef = parentType.getFields()[fieldName];
|
|
10141
|
+
}
|
|
10142
|
+
|
|
10143
|
+
const responseName = selection.alias
|
|
10144
|
+
? selection.alias.value
|
|
10145
|
+
: fieldName;
|
|
10146
|
+
|
|
10147
|
+
if (!nodeAndDefs[responseName]) {
|
|
10148
|
+
nodeAndDefs[responseName] = [];
|
|
10149
|
+
}
|
|
10150
|
+
|
|
10151
|
+
nodeAndDefs[responseName].push([parentType, selection, fieldDef]);
|
|
10152
|
+
break;
|
|
10153
|
+
}
|
|
10154
|
+
|
|
10155
|
+
case Kind.FRAGMENT_SPREAD:
|
|
10156
|
+
fragmentNames[selection.name.value] = true;
|
|
10157
|
+
break;
|
|
10158
|
+
|
|
10159
|
+
case Kind.INLINE_FRAGMENT: {
|
|
10160
|
+
const typeCondition = selection.typeCondition;
|
|
10161
|
+
const inlineFragmentType = typeCondition
|
|
10162
|
+
? typeFromAST(context.getSchema(), typeCondition)
|
|
10163
|
+
: parentType;
|
|
10164
|
+
|
|
10165
|
+
_collectFieldsAndFragmentNames(
|
|
10166
|
+
context,
|
|
10167
|
+
inlineFragmentType,
|
|
10168
|
+
selection.selectionSet,
|
|
10169
|
+
nodeAndDefs,
|
|
10170
|
+
fragmentNames,
|
|
10171
|
+
);
|
|
10172
|
+
|
|
10173
|
+
break;
|
|
10174
|
+
}
|
|
10175
|
+
}
|
|
10176
|
+
}
|
|
10177
|
+
} // Given a series of Conflicts which occurred between two sub-fields, generate
|
|
10178
|
+
// a single Conflict.
|
|
10179
|
+
|
|
10180
|
+
function subfieldConflicts(conflicts, responseName, node1, node2) {
|
|
10181
|
+
if (conflicts.length > 0) {
|
|
10182
|
+
return [
|
|
10183
|
+
[responseName, conflicts.map(([reason]) => reason)],
|
|
10184
|
+
[node1, ...conflicts.map(([, fields1]) => fields1).flat()],
|
|
10185
|
+
[node2, ...conflicts.map(([, , fields2]) => fields2).flat()],
|
|
10186
|
+
];
|
|
10187
|
+
}
|
|
10188
|
+
}
|
|
10189
|
+
/**
|
|
10190
|
+
* A way to keep track of pairs of things when the ordering of the pair does not matter.
|
|
10191
|
+
*/
|
|
10192
|
+
|
|
10193
|
+
class PairSet {
|
|
10194
|
+
constructor() {
|
|
10195
|
+
this._data = new Map();
|
|
10196
|
+
}
|
|
10197
|
+
|
|
10198
|
+
has(a, b, areMutuallyExclusive) {
|
|
10199
|
+
var _this$_data$get;
|
|
10200
|
+
|
|
10201
|
+
const [key1, key2] = a < b ? [a, b] : [b, a];
|
|
10202
|
+
const result =
|
|
10203
|
+
(_this$_data$get = this._data.get(key1)) === null ||
|
|
10204
|
+
_this$_data$get === void 0
|
|
10205
|
+
? void 0
|
|
10206
|
+
: _this$_data$get.get(key2);
|
|
10207
|
+
|
|
10208
|
+
if (result === undefined) {
|
|
10209
|
+
return false;
|
|
10210
|
+
} // areMutuallyExclusive being false is a superset of being true, hence if
|
|
10211
|
+
// we want to know if this PairSet "has" these two with no exclusivity,
|
|
10212
|
+
// we have to ensure it was added as such.
|
|
10213
|
+
|
|
10214
|
+
return areMutuallyExclusive ? true : areMutuallyExclusive === result;
|
|
10215
|
+
}
|
|
10216
|
+
|
|
10217
|
+
add(a, b, areMutuallyExclusive) {
|
|
10218
|
+
const [key1, key2] = a < b ? [a, b] : [b, a];
|
|
10219
|
+
|
|
10220
|
+
const map = this._data.get(key1);
|
|
10221
|
+
|
|
10222
|
+
if (map === undefined) {
|
|
10223
|
+
this._data.set(key1, new Map([[key2, areMutuallyExclusive]]));
|
|
10224
|
+
} else {
|
|
10225
|
+
map.set(key2, areMutuallyExclusive);
|
|
10226
|
+
}
|
|
10227
|
+
}
|
|
10228
|
+
}
|
|
10229
|
+
|
|
10230
|
+
/**
|
|
10231
|
+
* Unique input field names
|
|
10232
|
+
*
|
|
10233
|
+
* A GraphQL input object value is only valid if all supplied fields are
|
|
10234
|
+
* uniquely named.
|
|
10235
|
+
*
|
|
10236
|
+
* See https://spec.graphql.org/draft/#sec-Input-Object-Field-Uniqueness
|
|
10237
|
+
*/
|
|
10238
|
+
function UniqueInputFieldNamesRule(context) {
|
|
10239
|
+
const knownNameStack = [];
|
|
10240
|
+
let knownNames = Object.create(null);
|
|
10241
|
+
return {
|
|
10242
|
+
ObjectValue: {
|
|
10243
|
+
enter() {
|
|
10244
|
+
knownNameStack.push(knownNames);
|
|
10245
|
+
knownNames = Object.create(null);
|
|
10246
|
+
},
|
|
10247
|
+
|
|
10248
|
+
leave() {
|
|
10249
|
+
const prevKnownNames = knownNameStack.pop();
|
|
10250
|
+
prevKnownNames || invariant(false);
|
|
10251
|
+
knownNames = prevKnownNames;
|
|
10252
|
+
},
|
|
10253
|
+
},
|
|
10254
|
+
|
|
10255
|
+
ObjectField(node) {
|
|
10256
|
+
const fieldName = node.name.value;
|
|
10257
|
+
|
|
10258
|
+
if (knownNames[fieldName]) {
|
|
10259
|
+
context.reportError(
|
|
10260
|
+
new GraphQLError(
|
|
10261
|
+
`There can be only one input field named "${fieldName}".`,
|
|
10262
|
+
[knownNames[fieldName], node.name],
|
|
10263
|
+
),
|
|
10264
|
+
);
|
|
10265
|
+
} else {
|
|
10266
|
+
knownNames[fieldName] = node.name;
|
|
10267
|
+
}
|
|
10268
|
+
},
|
|
10269
|
+
};
|
|
10270
|
+
}
|
|
10271
|
+
|
|
10272
|
+
/**
|
|
10273
|
+
* Lone Schema definition
|
|
10274
|
+
*
|
|
10275
|
+
* A GraphQL document is only valid if it contains only one schema definition.
|
|
10276
|
+
*/
|
|
10277
|
+
function LoneSchemaDefinitionRule(context) {
|
|
10278
|
+
var _ref, _ref2, _oldSchema$astNode;
|
|
10279
|
+
|
|
10280
|
+
const oldSchema = context.getSchema();
|
|
10281
|
+
const alreadyDefined =
|
|
10282
|
+
(_ref =
|
|
10283
|
+
(_ref2 =
|
|
10284
|
+
(_oldSchema$astNode =
|
|
10285
|
+
oldSchema === null || oldSchema === void 0
|
|
10286
|
+
? void 0
|
|
10287
|
+
: oldSchema.astNode) !== null && _oldSchema$astNode !== void 0
|
|
10288
|
+
? _oldSchema$astNode
|
|
10289
|
+
: oldSchema === null || oldSchema === void 0
|
|
10290
|
+
? void 0
|
|
10291
|
+
: oldSchema.getQueryType()) !== null && _ref2 !== void 0
|
|
10292
|
+
? _ref2
|
|
10293
|
+
: oldSchema === null || oldSchema === void 0
|
|
10294
|
+
? void 0
|
|
10295
|
+
: oldSchema.getMutationType()) !== null && _ref !== void 0
|
|
10296
|
+
? _ref
|
|
10297
|
+
: oldSchema === null || oldSchema === void 0
|
|
10298
|
+
? void 0
|
|
10299
|
+
: oldSchema.getSubscriptionType();
|
|
10300
|
+
let schemaDefinitionsCount = 0;
|
|
10301
|
+
return {
|
|
10302
|
+
SchemaDefinition(node) {
|
|
10303
|
+
if (alreadyDefined) {
|
|
10304
|
+
context.reportError(
|
|
10305
|
+
new GraphQLError(
|
|
10306
|
+
'Cannot define a new schema within a schema extension.',
|
|
10307
|
+
node,
|
|
10308
|
+
),
|
|
10309
|
+
);
|
|
10310
|
+
return;
|
|
10311
|
+
}
|
|
10312
|
+
|
|
10313
|
+
if (schemaDefinitionsCount > 0) {
|
|
10314
|
+
context.reportError(
|
|
10315
|
+
new GraphQLError('Must provide only one schema definition.', node),
|
|
10316
|
+
);
|
|
10317
|
+
}
|
|
10318
|
+
|
|
10319
|
+
++schemaDefinitionsCount;
|
|
10320
|
+
},
|
|
10321
|
+
};
|
|
10322
|
+
}
|
|
10323
|
+
|
|
10324
|
+
/**
|
|
10325
|
+
* Unique operation types
|
|
10326
|
+
*
|
|
10327
|
+
* A GraphQL document is only valid if it has only one type per operation.
|
|
10328
|
+
*/
|
|
10329
|
+
function UniqueOperationTypesRule(context) {
|
|
10330
|
+
const schema = context.getSchema();
|
|
10331
|
+
const definedOperationTypes = Object.create(null);
|
|
10332
|
+
const existingOperationTypes = schema
|
|
10333
|
+
? {
|
|
10334
|
+
query: schema.getQueryType(),
|
|
10335
|
+
mutation: schema.getMutationType(),
|
|
10336
|
+
subscription: schema.getSubscriptionType(),
|
|
10337
|
+
}
|
|
10338
|
+
: {};
|
|
10339
|
+
return {
|
|
10340
|
+
SchemaDefinition: checkOperationTypes,
|
|
10341
|
+
SchemaExtension: checkOperationTypes,
|
|
10342
|
+
};
|
|
10343
|
+
|
|
10344
|
+
function checkOperationTypes(node) {
|
|
10345
|
+
var _node$operationTypes;
|
|
10346
|
+
|
|
10347
|
+
// istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
10348
|
+
const operationTypesNodes =
|
|
10349
|
+
(_node$operationTypes = node.operationTypes) !== null &&
|
|
10350
|
+
_node$operationTypes !== void 0
|
|
10351
|
+
? _node$operationTypes
|
|
10352
|
+
: [];
|
|
10353
|
+
|
|
10354
|
+
for (const operationType of operationTypesNodes) {
|
|
10355
|
+
const operation = operationType.operation;
|
|
10356
|
+
const alreadyDefinedOperationType = definedOperationTypes[operation];
|
|
10357
|
+
|
|
10358
|
+
if (existingOperationTypes[operation]) {
|
|
10359
|
+
context.reportError(
|
|
10360
|
+
new GraphQLError(
|
|
10361
|
+
`Type for ${operation} already defined in the schema. It cannot be redefined.`,
|
|
10362
|
+
operationType,
|
|
10363
|
+
),
|
|
10364
|
+
);
|
|
10365
|
+
} else if (alreadyDefinedOperationType) {
|
|
10366
|
+
context.reportError(
|
|
10367
|
+
new GraphQLError(
|
|
10368
|
+
`There can be only one ${operation} type in schema.`,
|
|
10369
|
+
[alreadyDefinedOperationType, operationType],
|
|
10370
|
+
),
|
|
10371
|
+
);
|
|
10372
|
+
} else {
|
|
10373
|
+
definedOperationTypes[operation] = operationType;
|
|
10374
|
+
}
|
|
10375
|
+
}
|
|
10376
|
+
|
|
10377
|
+
return false;
|
|
10378
|
+
}
|
|
10379
|
+
}
|
|
10380
|
+
|
|
10381
|
+
/**
|
|
10382
|
+
* Unique type names
|
|
10383
|
+
*
|
|
10384
|
+
* A GraphQL document is only valid if all defined types have unique names.
|
|
10385
|
+
*/
|
|
10386
|
+
function UniqueTypeNamesRule(context) {
|
|
10387
|
+
const knownTypeNames = Object.create(null);
|
|
10388
|
+
const schema = context.getSchema();
|
|
10389
|
+
return {
|
|
10390
|
+
ScalarTypeDefinition: checkTypeName,
|
|
10391
|
+
ObjectTypeDefinition: checkTypeName,
|
|
10392
|
+
InterfaceTypeDefinition: checkTypeName,
|
|
10393
|
+
UnionTypeDefinition: checkTypeName,
|
|
10394
|
+
EnumTypeDefinition: checkTypeName,
|
|
10395
|
+
InputObjectTypeDefinition: checkTypeName,
|
|
10396
|
+
};
|
|
10397
|
+
|
|
10398
|
+
function checkTypeName(node) {
|
|
10399
|
+
const typeName = node.name.value;
|
|
10400
|
+
|
|
10401
|
+
if (schema !== null && schema !== void 0 && schema.getType(typeName)) {
|
|
10402
|
+
context.reportError(
|
|
10403
|
+
new GraphQLError(
|
|
10404
|
+
`Type "${typeName}" already exists in the schema. It cannot also be defined in this type definition.`,
|
|
10405
|
+
node.name,
|
|
10406
|
+
),
|
|
10407
|
+
);
|
|
10408
|
+
return;
|
|
10409
|
+
}
|
|
10410
|
+
|
|
10411
|
+
if (knownTypeNames[typeName]) {
|
|
10412
|
+
context.reportError(
|
|
10413
|
+
new GraphQLError(`There can be only one type named "${typeName}".`, [
|
|
10414
|
+
knownTypeNames[typeName],
|
|
10415
|
+
node.name,
|
|
10416
|
+
]),
|
|
10417
|
+
);
|
|
10418
|
+
} else {
|
|
10419
|
+
knownTypeNames[typeName] = node.name;
|
|
10420
|
+
}
|
|
10421
|
+
|
|
10422
|
+
return false;
|
|
10423
|
+
}
|
|
10424
|
+
}
|
|
10425
|
+
|
|
10426
|
+
/**
|
|
10427
|
+
* Unique enum value names
|
|
10428
|
+
*
|
|
10429
|
+
* A GraphQL enum type is only valid if all its values are uniquely named.
|
|
10430
|
+
*/
|
|
10431
|
+
function UniqueEnumValueNamesRule(context) {
|
|
10432
|
+
const schema = context.getSchema();
|
|
10433
|
+
const existingTypeMap = schema ? schema.getTypeMap() : Object.create(null);
|
|
10434
|
+
const knownValueNames = Object.create(null);
|
|
10435
|
+
return {
|
|
10436
|
+
EnumTypeDefinition: checkValueUniqueness,
|
|
10437
|
+
EnumTypeExtension: checkValueUniqueness,
|
|
10438
|
+
};
|
|
10439
|
+
|
|
10440
|
+
function checkValueUniqueness(node) {
|
|
10441
|
+
var _node$values;
|
|
10442
|
+
|
|
10443
|
+
const typeName = node.name.value;
|
|
10444
|
+
|
|
10445
|
+
if (!knownValueNames[typeName]) {
|
|
10446
|
+
knownValueNames[typeName] = Object.create(null);
|
|
10447
|
+
} // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
10448
|
+
|
|
10449
|
+
const valueNodes =
|
|
10450
|
+
(_node$values = node.values) !== null && _node$values !== void 0
|
|
10451
|
+
? _node$values
|
|
10452
|
+
: [];
|
|
10453
|
+
const valueNames = knownValueNames[typeName];
|
|
10454
|
+
|
|
10455
|
+
for (const valueDef of valueNodes) {
|
|
10456
|
+
const valueName = valueDef.name.value;
|
|
10457
|
+
const existingType = existingTypeMap[typeName];
|
|
10458
|
+
|
|
10459
|
+
if (isEnumType(existingType) && existingType.getValue(valueName)) {
|
|
10460
|
+
context.reportError(
|
|
10461
|
+
new GraphQLError(
|
|
10462
|
+
`Enum value "${typeName}.${valueName}" already exists in the schema. It cannot also be defined in this type extension.`,
|
|
10463
|
+
valueDef.name,
|
|
10464
|
+
),
|
|
10465
|
+
);
|
|
10466
|
+
} else if (valueNames[valueName]) {
|
|
10467
|
+
context.reportError(
|
|
10468
|
+
new GraphQLError(
|
|
10469
|
+
`Enum value "${typeName}.${valueName}" can only be defined once.`,
|
|
10470
|
+
[valueNames[valueName], valueDef.name],
|
|
10471
|
+
),
|
|
10472
|
+
);
|
|
10473
|
+
} else {
|
|
10474
|
+
valueNames[valueName] = valueDef.name;
|
|
10475
|
+
}
|
|
10476
|
+
}
|
|
10477
|
+
|
|
10478
|
+
return false;
|
|
10479
|
+
}
|
|
10480
|
+
}
|
|
10481
|
+
|
|
10482
|
+
/**
|
|
10483
|
+
* Unique field definition names
|
|
10484
|
+
*
|
|
10485
|
+
* A GraphQL complex type is only valid if all its fields are uniquely named.
|
|
10486
|
+
*/
|
|
10487
|
+
function UniqueFieldDefinitionNamesRule(context) {
|
|
10488
|
+
const schema = context.getSchema();
|
|
10489
|
+
const existingTypeMap = schema ? schema.getTypeMap() : Object.create(null);
|
|
10490
|
+
const knownFieldNames = Object.create(null);
|
|
10491
|
+
return {
|
|
10492
|
+
InputObjectTypeDefinition: checkFieldUniqueness,
|
|
10493
|
+
InputObjectTypeExtension: checkFieldUniqueness,
|
|
10494
|
+
InterfaceTypeDefinition: checkFieldUniqueness,
|
|
10495
|
+
InterfaceTypeExtension: checkFieldUniqueness,
|
|
10496
|
+
ObjectTypeDefinition: checkFieldUniqueness,
|
|
10497
|
+
ObjectTypeExtension: checkFieldUniqueness,
|
|
10498
|
+
};
|
|
10499
|
+
|
|
10500
|
+
function checkFieldUniqueness(node) {
|
|
10501
|
+
var _node$fields;
|
|
10502
|
+
|
|
10503
|
+
const typeName = node.name.value;
|
|
10504
|
+
|
|
10505
|
+
if (!knownFieldNames[typeName]) {
|
|
10506
|
+
knownFieldNames[typeName] = Object.create(null);
|
|
10507
|
+
} // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
10508
|
+
|
|
10509
|
+
const fieldNodes =
|
|
10510
|
+
(_node$fields = node.fields) !== null && _node$fields !== void 0
|
|
10511
|
+
? _node$fields
|
|
10512
|
+
: [];
|
|
10513
|
+
const fieldNames = knownFieldNames[typeName];
|
|
10514
|
+
|
|
10515
|
+
for (const fieldDef of fieldNodes) {
|
|
10516
|
+
const fieldName = fieldDef.name.value;
|
|
10517
|
+
|
|
10518
|
+
if (hasField(existingTypeMap[typeName], fieldName)) {
|
|
10519
|
+
context.reportError(
|
|
10520
|
+
new GraphQLError(
|
|
10521
|
+
`Field "${typeName}.${fieldName}" already exists in the schema. It cannot also be defined in this type extension.`,
|
|
10522
|
+
fieldDef.name,
|
|
10523
|
+
),
|
|
10524
|
+
);
|
|
10525
|
+
} else if (fieldNames[fieldName]) {
|
|
10526
|
+
context.reportError(
|
|
10527
|
+
new GraphQLError(
|
|
10528
|
+
`Field "${typeName}.${fieldName}" can only be defined once.`,
|
|
10529
|
+
[fieldNames[fieldName], fieldDef.name],
|
|
10530
|
+
),
|
|
10531
|
+
);
|
|
10532
|
+
} else {
|
|
10533
|
+
fieldNames[fieldName] = fieldDef.name;
|
|
10534
|
+
}
|
|
10535
|
+
}
|
|
10536
|
+
|
|
10537
|
+
return false;
|
|
10538
|
+
}
|
|
10539
|
+
}
|
|
10540
|
+
|
|
10541
|
+
function hasField(type, fieldName) {
|
|
10542
|
+
if (isObjectType(type) || isInterfaceType(type) || isInputObjectType(type)) {
|
|
10543
|
+
return type.getFields()[fieldName] != null;
|
|
10544
|
+
}
|
|
10545
|
+
|
|
10546
|
+
return false;
|
|
10547
|
+
}
|
|
10548
|
+
|
|
10549
|
+
/**
|
|
10550
|
+
* Unique argument definition names
|
|
10551
|
+
*
|
|
10552
|
+
* A GraphQL Object or Interface type is only valid if all its fields have uniquely named arguments.
|
|
10553
|
+
* A GraphQL Directive is only valid if all its arguments are uniquely named.
|
|
10554
|
+
*/
|
|
10555
|
+
function UniqueArgumentDefinitionNamesRule(context) {
|
|
10556
|
+
return {
|
|
10557
|
+
DirectiveDefinition(directiveNode) {
|
|
10558
|
+
var _directiveNode$argume;
|
|
10559
|
+
|
|
10560
|
+
// istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
10561
|
+
const argumentNodes =
|
|
10562
|
+
(_directiveNode$argume = directiveNode.arguments) !== null &&
|
|
10563
|
+
_directiveNode$argume !== void 0
|
|
10564
|
+
? _directiveNode$argume
|
|
10565
|
+
: [];
|
|
10566
|
+
return checkArgUniqueness(`@${directiveNode.name.value}`, argumentNodes);
|
|
10567
|
+
},
|
|
10568
|
+
|
|
10569
|
+
InterfaceTypeDefinition: checkArgUniquenessPerField,
|
|
10570
|
+
InterfaceTypeExtension: checkArgUniquenessPerField,
|
|
10571
|
+
ObjectTypeDefinition: checkArgUniquenessPerField,
|
|
10572
|
+
ObjectTypeExtension: checkArgUniquenessPerField,
|
|
10573
|
+
};
|
|
10574
|
+
|
|
10575
|
+
function checkArgUniquenessPerField(typeNode) {
|
|
10576
|
+
var _typeNode$fields;
|
|
10577
|
+
|
|
10578
|
+
const typeName = typeNode.name.value; // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
10579
|
+
|
|
10580
|
+
const fieldNodes =
|
|
10581
|
+
(_typeNode$fields = typeNode.fields) !== null &&
|
|
10582
|
+
_typeNode$fields !== void 0
|
|
10583
|
+
? _typeNode$fields
|
|
10584
|
+
: [];
|
|
10585
|
+
|
|
10586
|
+
for (const fieldDef of fieldNodes) {
|
|
10587
|
+
var _fieldDef$arguments;
|
|
10588
|
+
|
|
10589
|
+
const fieldName = fieldDef.name.value; // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
10590
|
+
|
|
10591
|
+
const argumentNodes =
|
|
10592
|
+
(_fieldDef$arguments = fieldDef.arguments) !== null &&
|
|
10593
|
+
_fieldDef$arguments !== void 0
|
|
10594
|
+
? _fieldDef$arguments
|
|
10595
|
+
: [];
|
|
10596
|
+
checkArgUniqueness(`${typeName}.${fieldName}`, argumentNodes);
|
|
10597
|
+
}
|
|
10598
|
+
|
|
10599
|
+
return false;
|
|
10600
|
+
}
|
|
10601
|
+
|
|
10602
|
+
function checkArgUniqueness(parentName, argumentNodes) {
|
|
10603
|
+
const seenArgs = groupBy(argumentNodes, (arg) => arg.name.value);
|
|
10604
|
+
|
|
10605
|
+
for (const [argName, argNodes] of seenArgs) {
|
|
10606
|
+
if (argNodes.length > 1) {
|
|
10607
|
+
context.reportError(
|
|
10608
|
+
new GraphQLError(
|
|
10609
|
+
`Argument "${parentName}(${argName}:)" can only be defined once.`,
|
|
10610
|
+
argNodes.map((node) => node.name),
|
|
10611
|
+
),
|
|
10612
|
+
);
|
|
10613
|
+
}
|
|
10614
|
+
}
|
|
10615
|
+
|
|
10616
|
+
return false;
|
|
10617
|
+
}
|
|
10618
|
+
}
|
|
10619
|
+
|
|
10620
|
+
/**
|
|
10621
|
+
* Unique directive names
|
|
10622
|
+
*
|
|
10623
|
+
* A GraphQL document is only valid if all defined directives have unique names.
|
|
10624
|
+
*/
|
|
10625
|
+
function UniqueDirectiveNamesRule(context) {
|
|
10626
|
+
const knownDirectiveNames = Object.create(null);
|
|
10627
|
+
const schema = context.getSchema();
|
|
10628
|
+
return {
|
|
10629
|
+
DirectiveDefinition(node) {
|
|
10630
|
+
const directiveName = node.name.value;
|
|
10631
|
+
|
|
10632
|
+
if (
|
|
10633
|
+
schema !== null &&
|
|
10634
|
+
schema !== void 0 &&
|
|
10635
|
+
schema.getDirective(directiveName)
|
|
10636
|
+
) {
|
|
10637
|
+
context.reportError(
|
|
10638
|
+
new GraphQLError(
|
|
10639
|
+
`Directive "@${directiveName}" already exists in the schema. It cannot be redefined.`,
|
|
10640
|
+
node.name,
|
|
10641
|
+
),
|
|
10642
|
+
);
|
|
10643
|
+
return;
|
|
10644
|
+
}
|
|
10645
|
+
|
|
10646
|
+
if (knownDirectiveNames[directiveName]) {
|
|
10647
|
+
context.reportError(
|
|
10648
|
+
new GraphQLError(
|
|
10649
|
+
`There can be only one directive named "@${directiveName}".`,
|
|
10650
|
+
[knownDirectiveNames[directiveName], node.name],
|
|
10651
|
+
),
|
|
10652
|
+
);
|
|
10653
|
+
} else {
|
|
10654
|
+
knownDirectiveNames[directiveName] = node.name;
|
|
10655
|
+
}
|
|
10656
|
+
|
|
10657
|
+
return false;
|
|
10658
|
+
},
|
|
10659
|
+
};
|
|
10660
|
+
}
|
|
10661
|
+
|
|
10662
|
+
/**
|
|
10663
|
+
* Possible type extension
|
|
10664
|
+
*
|
|
10665
|
+
* A type extension is only valid if the type is defined and has the same kind.
|
|
10666
|
+
*/
|
|
10667
|
+
function PossibleTypeExtensionsRule(context) {
|
|
10668
|
+
const schema = context.getSchema();
|
|
10669
|
+
const definedTypes = Object.create(null);
|
|
10670
|
+
|
|
10671
|
+
for (const def of context.getDocument().definitions) {
|
|
10672
|
+
if (isTypeDefinitionNode(def)) {
|
|
10673
|
+
definedTypes[def.name.value] = def;
|
|
10674
|
+
}
|
|
10675
|
+
}
|
|
10676
|
+
|
|
10677
|
+
return {
|
|
10678
|
+
ScalarTypeExtension: checkExtension,
|
|
10679
|
+
ObjectTypeExtension: checkExtension,
|
|
10680
|
+
InterfaceTypeExtension: checkExtension,
|
|
10681
|
+
UnionTypeExtension: checkExtension,
|
|
10682
|
+
EnumTypeExtension: checkExtension,
|
|
10683
|
+
InputObjectTypeExtension: checkExtension,
|
|
10684
|
+
};
|
|
10685
|
+
|
|
10686
|
+
function checkExtension(node) {
|
|
10687
|
+
const typeName = node.name.value;
|
|
10688
|
+
const defNode = definedTypes[typeName];
|
|
10689
|
+
const existingType =
|
|
10690
|
+
schema === null || schema === void 0 ? void 0 : schema.getType(typeName);
|
|
10691
|
+
let expectedKind;
|
|
10692
|
+
|
|
10693
|
+
if (defNode) {
|
|
10694
|
+
expectedKind = defKindToExtKind[defNode.kind];
|
|
10695
|
+
} else if (existingType) {
|
|
10696
|
+
expectedKind = typeToExtKind(existingType);
|
|
10697
|
+
}
|
|
10698
|
+
|
|
10699
|
+
if (expectedKind) {
|
|
10700
|
+
if (expectedKind !== node.kind) {
|
|
10701
|
+
const kindStr = extensionKindToTypeName(node.kind);
|
|
10702
|
+
context.reportError(
|
|
10703
|
+
new GraphQLError(
|
|
10704
|
+
`Cannot extend non-${kindStr} type "${typeName}".`,
|
|
10705
|
+
defNode ? [defNode, node] : node,
|
|
10706
|
+
),
|
|
10707
|
+
);
|
|
10708
|
+
}
|
|
10709
|
+
} else {
|
|
10710
|
+
const allTypeNames = Object.keys({
|
|
10711
|
+
...definedTypes,
|
|
10712
|
+
...(schema === null || schema === void 0
|
|
10713
|
+
? void 0
|
|
10714
|
+
: schema.getTypeMap()),
|
|
10715
|
+
});
|
|
10716
|
+
const suggestedTypes = suggestionList(typeName, allTypeNames);
|
|
10717
|
+
context.reportError(
|
|
10718
|
+
new GraphQLError(
|
|
10719
|
+
`Cannot extend type "${typeName}" because it is not defined.` +
|
|
10720
|
+
didYouMean(suggestedTypes),
|
|
10721
|
+
node.name,
|
|
10722
|
+
),
|
|
10723
|
+
);
|
|
10724
|
+
}
|
|
10725
|
+
}
|
|
10726
|
+
}
|
|
10727
|
+
const defKindToExtKind = {
|
|
10728
|
+
[Kind.SCALAR_TYPE_DEFINITION]: Kind.SCALAR_TYPE_EXTENSION,
|
|
10729
|
+
[Kind.OBJECT_TYPE_DEFINITION]: Kind.OBJECT_TYPE_EXTENSION,
|
|
10730
|
+
[Kind.INTERFACE_TYPE_DEFINITION]: Kind.INTERFACE_TYPE_EXTENSION,
|
|
10731
|
+
[Kind.UNION_TYPE_DEFINITION]: Kind.UNION_TYPE_EXTENSION,
|
|
10732
|
+
[Kind.ENUM_TYPE_DEFINITION]: Kind.ENUM_TYPE_EXTENSION,
|
|
10733
|
+
[Kind.INPUT_OBJECT_TYPE_DEFINITION]: Kind.INPUT_OBJECT_TYPE_EXTENSION,
|
|
10734
|
+
};
|
|
10735
|
+
|
|
10736
|
+
function typeToExtKind(type) {
|
|
10737
|
+
if (isScalarType(type)) {
|
|
10738
|
+
return Kind.SCALAR_TYPE_EXTENSION;
|
|
10739
|
+
}
|
|
10740
|
+
|
|
10741
|
+
if (isObjectType(type)) {
|
|
10742
|
+
return Kind.OBJECT_TYPE_EXTENSION;
|
|
10743
|
+
}
|
|
10744
|
+
|
|
10745
|
+
if (isInterfaceType(type)) {
|
|
10746
|
+
return Kind.INTERFACE_TYPE_EXTENSION;
|
|
10747
|
+
}
|
|
10748
|
+
|
|
10749
|
+
if (isUnionType(type)) {
|
|
10750
|
+
return Kind.UNION_TYPE_EXTENSION;
|
|
10751
|
+
}
|
|
10752
|
+
|
|
10753
|
+
if (isEnumType(type)) {
|
|
10754
|
+
return Kind.ENUM_TYPE_EXTENSION;
|
|
10755
|
+
} // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618')
|
|
10756
|
+
|
|
10757
|
+
if (isInputObjectType(type)) {
|
|
10758
|
+
return Kind.INPUT_OBJECT_TYPE_EXTENSION;
|
|
10759
|
+
} // istanbul ignore next (Not reachable. All possible types have been considered)
|
|
10760
|
+
|
|
10761
|
+
invariant(false, 'Unexpected type: ' + inspect(type));
|
|
10762
|
+
}
|
|
10763
|
+
|
|
10764
|
+
function extensionKindToTypeName(kind) {
|
|
10765
|
+
switch (kind) {
|
|
10766
|
+
case Kind.SCALAR_TYPE_EXTENSION:
|
|
10767
|
+
return 'scalar';
|
|
10768
|
+
|
|
10769
|
+
case Kind.OBJECT_TYPE_EXTENSION:
|
|
10770
|
+
return 'object';
|
|
10771
|
+
|
|
10772
|
+
case Kind.INTERFACE_TYPE_EXTENSION:
|
|
10773
|
+
return 'interface';
|
|
10774
|
+
|
|
10775
|
+
case Kind.UNION_TYPE_EXTENSION:
|
|
10776
|
+
return 'union';
|
|
10777
|
+
|
|
10778
|
+
case Kind.ENUM_TYPE_EXTENSION:
|
|
10779
|
+
return 'enum';
|
|
10780
|
+
|
|
10781
|
+
case Kind.INPUT_OBJECT_TYPE_EXTENSION:
|
|
10782
|
+
return 'input object';
|
|
10783
|
+
} // istanbul ignore next (Not reachable. All possible types have been considered)
|
|
10784
|
+
|
|
10785
|
+
invariant(false, 'Unexpected kind: ' + inspect(kind));
|
|
10786
|
+
}
|
|
10787
|
+
|
|
10788
|
+
// Spec Section: "Executable Definitions"
|
|
10789
|
+
/**
|
|
10790
|
+
* This set includes all validation rules defined by the GraphQL spec.
|
|
10791
|
+
*
|
|
10792
|
+
* The order of the rules in this list has been adjusted to lead to the
|
|
10793
|
+
* most clear output when encountering multiple validation errors.
|
|
10794
|
+
*/
|
|
10795
|
+
|
|
10796
|
+
const specifiedRules = Object.freeze([
|
|
10797
|
+
ExecutableDefinitionsRule,
|
|
10798
|
+
UniqueOperationNamesRule,
|
|
10799
|
+
LoneAnonymousOperationRule,
|
|
10800
|
+
SingleFieldSubscriptionsRule,
|
|
10801
|
+
KnownTypeNamesRule,
|
|
10802
|
+
FragmentsOnCompositeTypesRule,
|
|
10803
|
+
VariablesAreInputTypesRule,
|
|
10804
|
+
ScalarLeafsRule,
|
|
10805
|
+
FieldsOnCorrectTypeRule,
|
|
10806
|
+
UniqueFragmentNamesRule,
|
|
10807
|
+
KnownFragmentNamesRule,
|
|
10808
|
+
NoUnusedFragmentsRule,
|
|
10809
|
+
PossibleFragmentSpreadsRule,
|
|
10810
|
+
NoFragmentCyclesRule,
|
|
10811
|
+
UniqueVariableNamesRule,
|
|
10812
|
+
NoUndefinedVariablesRule,
|
|
10813
|
+
NoUnusedVariablesRule,
|
|
10814
|
+
KnownDirectivesRule,
|
|
10815
|
+
UniqueDirectivesPerLocationRule,
|
|
10816
|
+
KnownArgumentNamesRule,
|
|
10817
|
+
UniqueArgumentNamesRule,
|
|
10818
|
+
ValuesOfCorrectTypeRule,
|
|
10819
|
+
ProvidedRequiredArgumentsRule,
|
|
10820
|
+
VariablesInAllowedPositionRule,
|
|
10821
|
+
OverlappingFieldsCanBeMergedRule,
|
|
10822
|
+
UniqueInputFieldNamesRule,
|
|
10823
|
+
]);
|
|
10824
|
+
/**
|
|
10825
|
+
* @internal
|
|
10826
|
+
*/
|
|
10827
|
+
|
|
10828
|
+
const specifiedSDLRules = Object.freeze([
|
|
10829
|
+
LoneSchemaDefinitionRule,
|
|
10830
|
+
UniqueOperationTypesRule,
|
|
10831
|
+
UniqueTypeNamesRule,
|
|
10832
|
+
UniqueEnumValueNamesRule,
|
|
10833
|
+
UniqueFieldDefinitionNamesRule,
|
|
10834
|
+
UniqueArgumentDefinitionNamesRule,
|
|
10835
|
+
UniqueDirectiveNamesRule,
|
|
10836
|
+
KnownTypeNamesRule,
|
|
10837
|
+
KnownDirectivesRule,
|
|
10838
|
+
UniqueDirectivesPerLocationRule,
|
|
10839
|
+
PossibleTypeExtensionsRule,
|
|
10840
|
+
KnownArgumentNamesOnDirectivesRule,
|
|
10841
|
+
UniqueArgumentNamesRule,
|
|
10842
|
+
UniqueInputFieldNamesRule,
|
|
10843
|
+
ProvidedRequiredArgumentsOnDirectivesRule,
|
|
10844
|
+
]);
|
|
10845
|
+
|
|
10846
|
+
/**
|
|
10847
|
+
* An instance of this class is passed as the "this" context to all validators,
|
|
10848
|
+
* allowing access to commonly useful contextual information from within a
|
|
10849
|
+
* validation rule.
|
|
10850
|
+
*/
|
|
10851
|
+
class ASTValidationContext {
|
|
10852
|
+
constructor(ast, onError) {
|
|
10853
|
+
this._ast = ast;
|
|
10854
|
+
this._fragments = undefined;
|
|
10855
|
+
this._fragmentSpreads = new Map();
|
|
10856
|
+
this._recursivelyReferencedFragments = new Map();
|
|
10857
|
+
this._onError = onError;
|
|
10858
|
+
}
|
|
10859
|
+
|
|
10860
|
+
get [Symbol.toStringTag]() {
|
|
10861
|
+
return 'ASTValidationContext';
|
|
10862
|
+
}
|
|
10863
|
+
|
|
10864
|
+
reportError(error) {
|
|
10865
|
+
this._onError(error);
|
|
10866
|
+
}
|
|
10867
|
+
|
|
10868
|
+
getDocument() {
|
|
10869
|
+
return this._ast;
|
|
10870
|
+
}
|
|
10871
|
+
|
|
10872
|
+
getFragment(name) {
|
|
10873
|
+
let fragments;
|
|
10874
|
+
|
|
10875
|
+
if (this._fragments) {
|
|
10876
|
+
fragments = this._fragments;
|
|
10877
|
+
} else {
|
|
10878
|
+
fragments = Object.create(null);
|
|
10879
|
+
|
|
10880
|
+
for (const defNode of this.getDocument().definitions) {
|
|
10881
|
+
if (defNode.kind === Kind.FRAGMENT_DEFINITION) {
|
|
10882
|
+
fragments[defNode.name.value] = defNode;
|
|
10883
|
+
}
|
|
10884
|
+
}
|
|
10885
|
+
|
|
10886
|
+
this._fragments = fragments;
|
|
10887
|
+
}
|
|
10888
|
+
|
|
10889
|
+
return fragments[name];
|
|
10890
|
+
}
|
|
10891
|
+
|
|
10892
|
+
getFragmentSpreads(node) {
|
|
10893
|
+
let spreads = this._fragmentSpreads.get(node);
|
|
10894
|
+
|
|
10895
|
+
if (!spreads) {
|
|
10896
|
+
spreads = [];
|
|
10897
|
+
const setsToVisit = [node];
|
|
10898
|
+
let set;
|
|
10899
|
+
|
|
10900
|
+
while ((set = setsToVisit.pop())) {
|
|
10901
|
+
for (const selection of set.selections) {
|
|
10902
|
+
if (selection.kind === Kind.FRAGMENT_SPREAD) {
|
|
10903
|
+
spreads.push(selection);
|
|
10904
|
+
} else if (selection.selectionSet) {
|
|
10905
|
+
setsToVisit.push(selection.selectionSet);
|
|
10906
|
+
}
|
|
10907
|
+
}
|
|
10908
|
+
}
|
|
10909
|
+
|
|
10910
|
+
this._fragmentSpreads.set(node, spreads);
|
|
10911
|
+
}
|
|
10912
|
+
|
|
10913
|
+
return spreads;
|
|
10914
|
+
}
|
|
10915
|
+
|
|
10916
|
+
getRecursivelyReferencedFragments(operation) {
|
|
10917
|
+
let fragments = this._recursivelyReferencedFragments.get(operation);
|
|
10918
|
+
|
|
10919
|
+
if (!fragments) {
|
|
10920
|
+
fragments = [];
|
|
10921
|
+
const collectedNames = Object.create(null);
|
|
10922
|
+
const nodesToVisit = [operation.selectionSet];
|
|
10923
|
+
let node;
|
|
10924
|
+
|
|
10925
|
+
while ((node = nodesToVisit.pop())) {
|
|
10926
|
+
for (const spread of this.getFragmentSpreads(node)) {
|
|
10927
|
+
const fragName = spread.name.value;
|
|
10928
|
+
|
|
10929
|
+
if (collectedNames[fragName] !== true) {
|
|
10930
|
+
collectedNames[fragName] = true;
|
|
10931
|
+
const fragment = this.getFragment(fragName);
|
|
10932
|
+
|
|
10933
|
+
if (fragment) {
|
|
10934
|
+
fragments.push(fragment);
|
|
10935
|
+
nodesToVisit.push(fragment.selectionSet);
|
|
10936
|
+
}
|
|
10937
|
+
}
|
|
10938
|
+
}
|
|
10939
|
+
}
|
|
10940
|
+
|
|
10941
|
+
this._recursivelyReferencedFragments.set(operation, fragments);
|
|
10942
|
+
}
|
|
10943
|
+
|
|
10944
|
+
return fragments;
|
|
10945
|
+
}
|
|
10946
|
+
}
|
|
10947
|
+
class SDLValidationContext extends ASTValidationContext {
|
|
10948
|
+
constructor(ast, schema, onError) {
|
|
10949
|
+
super(ast, onError);
|
|
10950
|
+
this._schema = schema;
|
|
10951
|
+
}
|
|
10952
|
+
|
|
10953
|
+
get [Symbol.toStringTag]() {
|
|
10954
|
+
return 'SDLValidationContext';
|
|
10955
|
+
}
|
|
10956
|
+
|
|
10957
|
+
getSchema() {
|
|
10958
|
+
return this._schema;
|
|
10959
|
+
}
|
|
10960
|
+
}
|
|
10961
|
+
|
|
10962
|
+
/**
|
|
10963
|
+
* @internal
|
|
10964
|
+
*/
|
|
10965
|
+
|
|
10966
|
+
function validateSDL(
|
|
10967
|
+
documentAST,
|
|
10968
|
+
schemaToExtend,
|
|
10969
|
+
rules = specifiedSDLRules,
|
|
10970
|
+
) {
|
|
10971
|
+
const errors = [];
|
|
10972
|
+
const context = new SDLValidationContext(
|
|
10973
|
+
documentAST,
|
|
10974
|
+
schemaToExtend,
|
|
10975
|
+
(error) => {
|
|
10976
|
+
errors.push(error);
|
|
10977
|
+
},
|
|
10978
|
+
);
|
|
10979
|
+
const visitors = rules.map((rule) => rule(context));
|
|
10980
|
+
visit(documentAST, visitInParallel(visitors));
|
|
10981
|
+
return errors;
|
|
10982
|
+
}
|
|
10983
|
+
/**
|
|
10984
|
+
* Utility function which asserts a SDL document is valid by throwing an error
|
|
10985
|
+
* if it is invalid.
|
|
10986
|
+
*
|
|
10987
|
+
* @internal
|
|
10988
|
+
*/
|
|
10989
|
+
|
|
10990
|
+
function assertValidSDL(documentAST) {
|
|
10991
|
+
const errors = validateSDL(documentAST);
|
|
10992
|
+
|
|
10993
|
+
if (errors.length !== 0) {
|
|
10994
|
+
throw new Error(errors.map((error) => error.message).join('\n\n'));
|
|
10995
|
+
}
|
|
10996
|
+
}
|
|
10997
|
+
|
|
10998
|
+
/**
|
|
10999
|
+
* Build a GraphQLSchema for use by client tools.
|
|
11000
|
+
*
|
|
11001
|
+
* Given the result of a client running the introspection query, creates and
|
|
11002
|
+
* returns a GraphQLSchema instance which can be then used with all graphql-js
|
|
11003
|
+
* tools, but cannot be used to execute a query, as introspection does not
|
|
11004
|
+
* represent the "resolver", "parse" or "serialize" functions or any other
|
|
11005
|
+
* server-internal mechanisms.
|
|
11006
|
+
*
|
|
11007
|
+
* This function expects a complete introspection result. Don't forget to check
|
|
11008
|
+
* the "errors" field of a server response before calling this function.
|
|
11009
|
+
*/
|
|
11010
|
+
|
|
11011
|
+
function buildClientSchema(introspection, options) {
|
|
11012
|
+
(isObjectLike(introspection) && isObjectLike(introspection.__schema)) ||
|
|
11013
|
+
devAssert(
|
|
11014
|
+
false,
|
|
11015
|
+
`Invalid or incomplete introspection result. Ensure that you are passing "data" property of introspection response and no "errors" was returned alongside: ${inspect(
|
|
11016
|
+
introspection,
|
|
11017
|
+
)}.`,
|
|
11018
|
+
); // Get the schema from the introspection result.
|
|
11019
|
+
|
|
11020
|
+
const schemaIntrospection = introspection.__schema; // Iterate through all types, getting the type definition for each.
|
|
11021
|
+
|
|
11022
|
+
const typeMap = keyValMap(
|
|
11023
|
+
schemaIntrospection.types,
|
|
11024
|
+
(typeIntrospection) => typeIntrospection.name,
|
|
11025
|
+
(typeIntrospection) => buildType(typeIntrospection),
|
|
11026
|
+
); // Include standard types only if they are used.
|
|
11027
|
+
|
|
11028
|
+
for (const stdType of [...specifiedScalarTypes, ...introspectionTypes]) {
|
|
11029
|
+
if (typeMap[stdType.name]) {
|
|
11030
|
+
typeMap[stdType.name] = stdType;
|
|
11031
|
+
}
|
|
11032
|
+
} // Get the root Query, Mutation, and Subscription types.
|
|
11033
|
+
|
|
11034
|
+
const queryType = schemaIntrospection.queryType
|
|
11035
|
+
? getObjectType(schemaIntrospection.queryType)
|
|
11036
|
+
: null;
|
|
11037
|
+
const mutationType = schemaIntrospection.mutationType
|
|
11038
|
+
? getObjectType(schemaIntrospection.mutationType)
|
|
11039
|
+
: null;
|
|
11040
|
+
const subscriptionType = schemaIntrospection.subscriptionType
|
|
11041
|
+
? getObjectType(schemaIntrospection.subscriptionType)
|
|
11042
|
+
: null; // Get the directives supported by Introspection, assuming empty-set if
|
|
11043
|
+
// directives were not queried for.
|
|
11044
|
+
|
|
11045
|
+
const directives = schemaIntrospection.directives
|
|
11046
|
+
? schemaIntrospection.directives.map(buildDirective)
|
|
11047
|
+
: []; // Then produce and return a Schema with these types.
|
|
11048
|
+
|
|
11049
|
+
return new GraphQLSchema({
|
|
11050
|
+
description: schemaIntrospection.description,
|
|
11051
|
+
query: queryType,
|
|
11052
|
+
mutation: mutationType,
|
|
11053
|
+
subscription: subscriptionType,
|
|
11054
|
+
types: Object.values(typeMap),
|
|
11055
|
+
directives,
|
|
11056
|
+
assumeValid:
|
|
11057
|
+
options === null || options === void 0 ? void 0 : options.assumeValid,
|
|
11058
|
+
}); // Given a type reference in introspection, return the GraphQLType instance.
|
|
11059
|
+
// preferring cached instances before building new instances.
|
|
11060
|
+
|
|
11061
|
+
function getType(typeRef) {
|
|
11062
|
+
if (typeRef.kind === TypeKind.LIST) {
|
|
11063
|
+
const itemRef = typeRef.ofType;
|
|
11064
|
+
|
|
11065
|
+
if (!itemRef) {
|
|
11066
|
+
throw new Error('Decorated type deeper than introspection query.');
|
|
11067
|
+
}
|
|
11068
|
+
|
|
11069
|
+
return new GraphQLList(getType(itemRef));
|
|
11070
|
+
}
|
|
11071
|
+
|
|
11072
|
+
if (typeRef.kind === TypeKind.NON_NULL) {
|
|
11073
|
+
const nullableRef = typeRef.ofType;
|
|
11074
|
+
|
|
11075
|
+
if (!nullableRef) {
|
|
11076
|
+
throw new Error('Decorated type deeper than introspection query.');
|
|
11077
|
+
}
|
|
11078
|
+
|
|
11079
|
+
const nullableType = getType(nullableRef);
|
|
11080
|
+
return new GraphQLNonNull(assertNullableType(nullableType));
|
|
11081
|
+
}
|
|
11082
|
+
|
|
11083
|
+
return getNamedType(typeRef);
|
|
11084
|
+
}
|
|
11085
|
+
|
|
11086
|
+
function getNamedType(typeRef) {
|
|
11087
|
+
const typeName = typeRef.name;
|
|
11088
|
+
|
|
11089
|
+
if (!typeName) {
|
|
11090
|
+
throw new Error(`Unknown type reference: ${inspect(typeRef)}.`);
|
|
11091
|
+
}
|
|
11092
|
+
|
|
11093
|
+
const type = typeMap[typeName];
|
|
11094
|
+
|
|
11095
|
+
if (!type) {
|
|
11096
|
+
throw new Error(
|
|
11097
|
+
`Invalid or incomplete schema, unknown type: ${typeName}. Ensure that a full introspection query is used in order to build a client schema.`,
|
|
11098
|
+
);
|
|
11099
|
+
}
|
|
11100
|
+
|
|
11101
|
+
return type;
|
|
7464
11102
|
}
|
|
7465
11103
|
|
|
7466
11104
|
function getObjectType(typeRef) {
|
|
@@ -7694,6 +11332,796 @@ function buildClientSchema(introspection, options) {
|
|
|
7694
11332
|
}
|
|
7695
11333
|
}
|
|
7696
11334
|
|
|
11335
|
+
/**
|
|
11336
|
+
* @internal
|
|
11337
|
+
*/
|
|
11338
|
+
|
|
11339
|
+
function extendSchemaImpl(schemaConfig, documentAST, options) {
|
|
11340
|
+
var _schemaDef, _schemaDef$descriptio, _schemaDef2, _options$assumeValid;
|
|
11341
|
+
|
|
11342
|
+
// Collect the type definitions and extensions found in the document.
|
|
11343
|
+
const typeDefs = [];
|
|
11344
|
+
const typeExtensionsMap = Object.create(null); // New directives and types are separate because a directives and types can
|
|
11345
|
+
// have the same name. For example, a type named "skip".
|
|
11346
|
+
|
|
11347
|
+
const directiveDefs = [];
|
|
11348
|
+
let schemaDef; // Schema extensions are collected which may add additional operation types.
|
|
11349
|
+
|
|
11350
|
+
const schemaExtensions = [];
|
|
11351
|
+
|
|
11352
|
+
for (const def of documentAST.definitions) {
|
|
11353
|
+
if (def.kind === Kind.SCHEMA_DEFINITION) {
|
|
11354
|
+
schemaDef = def;
|
|
11355
|
+
} else if (def.kind === Kind.SCHEMA_EXTENSION) {
|
|
11356
|
+
schemaExtensions.push(def);
|
|
11357
|
+
} else if (isTypeDefinitionNode(def)) {
|
|
11358
|
+
typeDefs.push(def);
|
|
11359
|
+
} else if (isTypeExtensionNode(def)) {
|
|
11360
|
+
const extendedTypeName = def.name.value;
|
|
11361
|
+
const existingTypeExtensions = typeExtensionsMap[extendedTypeName];
|
|
11362
|
+
typeExtensionsMap[extendedTypeName] = existingTypeExtensions
|
|
11363
|
+
? existingTypeExtensions.concat([def])
|
|
11364
|
+
: [def];
|
|
11365
|
+
} else if (def.kind === Kind.DIRECTIVE_DEFINITION) {
|
|
11366
|
+
directiveDefs.push(def);
|
|
11367
|
+
}
|
|
11368
|
+
} // If this document contains no new types, extensions, or directives then
|
|
11369
|
+
// return the same unmodified GraphQLSchema instance.
|
|
11370
|
+
|
|
11371
|
+
if (
|
|
11372
|
+
Object.keys(typeExtensionsMap).length === 0 &&
|
|
11373
|
+
typeDefs.length === 0 &&
|
|
11374
|
+
directiveDefs.length === 0 &&
|
|
11375
|
+
schemaExtensions.length === 0 &&
|
|
11376
|
+
schemaDef == null
|
|
11377
|
+
) {
|
|
11378
|
+
return schemaConfig;
|
|
11379
|
+
}
|
|
11380
|
+
|
|
11381
|
+
const typeMap = Object.create(null);
|
|
11382
|
+
|
|
11383
|
+
for (const existingType of schemaConfig.types) {
|
|
11384
|
+
typeMap[existingType.name] = extendNamedType(existingType);
|
|
11385
|
+
}
|
|
11386
|
+
|
|
11387
|
+
for (const typeNode of typeDefs) {
|
|
11388
|
+
var _stdTypeMap$name;
|
|
11389
|
+
|
|
11390
|
+
const name = typeNode.name.value;
|
|
11391
|
+
typeMap[name] =
|
|
11392
|
+
(_stdTypeMap$name = stdTypeMap[name]) !== null &&
|
|
11393
|
+
_stdTypeMap$name !== void 0
|
|
11394
|
+
? _stdTypeMap$name
|
|
11395
|
+
: buildType(typeNode);
|
|
11396
|
+
}
|
|
11397
|
+
|
|
11398
|
+
const operationTypes = {
|
|
11399
|
+
// Get the extended root operation types.
|
|
11400
|
+
query: schemaConfig.query && replaceNamedType(schemaConfig.query),
|
|
11401
|
+
mutation: schemaConfig.mutation && replaceNamedType(schemaConfig.mutation),
|
|
11402
|
+
subscription:
|
|
11403
|
+
schemaConfig.subscription && replaceNamedType(schemaConfig.subscription),
|
|
11404
|
+
// Then, incorporate schema definition and all schema extensions.
|
|
11405
|
+
...(schemaDef && getOperationTypes([schemaDef])),
|
|
11406
|
+
...getOperationTypes(schemaExtensions),
|
|
11407
|
+
}; // Then produce and return a Schema config with these types.
|
|
11408
|
+
|
|
11409
|
+
return {
|
|
11410
|
+
description:
|
|
11411
|
+
(_schemaDef = schemaDef) === null || _schemaDef === void 0
|
|
11412
|
+
? void 0
|
|
11413
|
+
: (_schemaDef$descriptio = _schemaDef.description) === null ||
|
|
11414
|
+
_schemaDef$descriptio === void 0
|
|
11415
|
+
? void 0
|
|
11416
|
+
: _schemaDef$descriptio.value,
|
|
11417
|
+
...operationTypes,
|
|
11418
|
+
types: Object.values(typeMap),
|
|
11419
|
+
directives: [
|
|
11420
|
+
...schemaConfig.directives.map(replaceDirective),
|
|
11421
|
+
...directiveDefs.map(buildDirective),
|
|
11422
|
+
],
|
|
11423
|
+
extensions: Object.create(null),
|
|
11424
|
+
astNode:
|
|
11425
|
+
(_schemaDef2 = schemaDef) !== null && _schemaDef2 !== void 0
|
|
11426
|
+
? _schemaDef2
|
|
11427
|
+
: schemaConfig.astNode,
|
|
11428
|
+
extensionASTNodes: schemaConfig.extensionASTNodes.concat(schemaExtensions),
|
|
11429
|
+
assumeValid:
|
|
11430
|
+
(_options$assumeValid =
|
|
11431
|
+
options === null || options === void 0
|
|
11432
|
+
? void 0
|
|
11433
|
+
: options.assumeValid) !== null && _options$assumeValid !== void 0
|
|
11434
|
+
? _options$assumeValid
|
|
11435
|
+
: false,
|
|
11436
|
+
}; // Below are functions used for producing this schema that have closed over
|
|
11437
|
+
// this scope and have access to the schema, cache, and newly defined types.
|
|
11438
|
+
|
|
11439
|
+
function replaceType(type) {
|
|
11440
|
+
if (isListType(type)) {
|
|
11441
|
+
// @ts-expect-error
|
|
11442
|
+
return new GraphQLList(replaceType(type.ofType));
|
|
11443
|
+
}
|
|
11444
|
+
|
|
11445
|
+
if (isNonNullType(type)) {
|
|
11446
|
+
// @ts-expect-error
|
|
11447
|
+
return new GraphQLNonNull(replaceType(type.ofType));
|
|
11448
|
+
} // @ts-expect-error FIXME
|
|
11449
|
+
|
|
11450
|
+
return replaceNamedType(type);
|
|
11451
|
+
}
|
|
11452
|
+
|
|
11453
|
+
function replaceNamedType(type) {
|
|
11454
|
+
// Note: While this could make early assertions to get the correctly
|
|
11455
|
+
// typed values, that would throw immediately while type system
|
|
11456
|
+
// validation with validateSchema() will produce more actionable results.
|
|
11457
|
+
return typeMap[type.name];
|
|
11458
|
+
}
|
|
11459
|
+
|
|
11460
|
+
function replaceDirective(directive) {
|
|
11461
|
+
const config = directive.toConfig();
|
|
11462
|
+
return new GraphQLDirective({
|
|
11463
|
+
...config,
|
|
11464
|
+
args: mapValue(config.args, extendArg),
|
|
11465
|
+
});
|
|
11466
|
+
}
|
|
11467
|
+
|
|
11468
|
+
function extendNamedType(type) {
|
|
11469
|
+
if (isIntrospectionType(type) || isSpecifiedScalarType(type)) {
|
|
11470
|
+
// Builtin types are not extended.
|
|
11471
|
+
return type;
|
|
11472
|
+
}
|
|
11473
|
+
|
|
11474
|
+
if (isScalarType(type)) {
|
|
11475
|
+
return extendScalarType(type);
|
|
11476
|
+
}
|
|
11477
|
+
|
|
11478
|
+
if (isObjectType(type)) {
|
|
11479
|
+
return extendObjectType(type);
|
|
11480
|
+
}
|
|
11481
|
+
|
|
11482
|
+
if (isInterfaceType(type)) {
|
|
11483
|
+
return extendInterfaceType(type);
|
|
11484
|
+
}
|
|
11485
|
+
|
|
11486
|
+
if (isUnionType(type)) {
|
|
11487
|
+
return extendUnionType(type);
|
|
11488
|
+
}
|
|
11489
|
+
|
|
11490
|
+
if (isEnumType(type)) {
|
|
11491
|
+
return extendEnumType(type);
|
|
11492
|
+
} // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618')
|
|
11493
|
+
|
|
11494
|
+
if (isInputObjectType(type)) {
|
|
11495
|
+
return extendInputObjectType(type);
|
|
11496
|
+
} // istanbul ignore next (Not reachable. All possible types have been considered)
|
|
11497
|
+
|
|
11498
|
+
invariant(false, 'Unexpected type: ' + inspect(type));
|
|
11499
|
+
}
|
|
11500
|
+
|
|
11501
|
+
function extendInputObjectType(type) {
|
|
11502
|
+
var _typeExtensionsMap$co;
|
|
11503
|
+
|
|
11504
|
+
const config = type.toConfig();
|
|
11505
|
+
const extensions =
|
|
11506
|
+
(_typeExtensionsMap$co = typeExtensionsMap[config.name]) !== null &&
|
|
11507
|
+
_typeExtensionsMap$co !== void 0
|
|
11508
|
+
? _typeExtensionsMap$co
|
|
11509
|
+
: [];
|
|
11510
|
+
return new GraphQLInputObjectType({
|
|
11511
|
+
...config,
|
|
11512
|
+
fields: () => ({
|
|
11513
|
+
...mapValue(config.fields, (field) => ({
|
|
11514
|
+
...field,
|
|
11515
|
+
type: replaceType(field.type),
|
|
11516
|
+
})),
|
|
11517
|
+
...buildInputFieldMap(extensions),
|
|
11518
|
+
}),
|
|
11519
|
+
extensionASTNodes: config.extensionASTNodes.concat(extensions),
|
|
11520
|
+
});
|
|
11521
|
+
}
|
|
11522
|
+
|
|
11523
|
+
function extendEnumType(type) {
|
|
11524
|
+
var _typeExtensionsMap$ty;
|
|
11525
|
+
|
|
11526
|
+
const config = type.toConfig();
|
|
11527
|
+
const extensions =
|
|
11528
|
+
(_typeExtensionsMap$ty = typeExtensionsMap[type.name]) !== null &&
|
|
11529
|
+
_typeExtensionsMap$ty !== void 0
|
|
11530
|
+
? _typeExtensionsMap$ty
|
|
11531
|
+
: [];
|
|
11532
|
+
return new GraphQLEnumType({
|
|
11533
|
+
...config,
|
|
11534
|
+
values: { ...config.values, ...buildEnumValueMap(extensions) },
|
|
11535
|
+
extensionASTNodes: config.extensionASTNodes.concat(extensions),
|
|
11536
|
+
});
|
|
11537
|
+
}
|
|
11538
|
+
|
|
11539
|
+
function extendScalarType(type) {
|
|
11540
|
+
var _typeExtensionsMap$co2;
|
|
11541
|
+
|
|
11542
|
+
const config = type.toConfig();
|
|
11543
|
+
const extensions =
|
|
11544
|
+
(_typeExtensionsMap$co2 = typeExtensionsMap[config.name]) !== null &&
|
|
11545
|
+
_typeExtensionsMap$co2 !== void 0
|
|
11546
|
+
? _typeExtensionsMap$co2
|
|
11547
|
+
: [];
|
|
11548
|
+
let specifiedByURL = config.specifiedByURL;
|
|
11549
|
+
|
|
11550
|
+
for (const extensionNode of extensions) {
|
|
11551
|
+
var _getSpecifiedByURL;
|
|
11552
|
+
|
|
11553
|
+
specifiedByURL =
|
|
11554
|
+
(_getSpecifiedByURL = getSpecifiedByURL(extensionNode)) !== null &&
|
|
11555
|
+
_getSpecifiedByURL !== void 0
|
|
11556
|
+
? _getSpecifiedByURL
|
|
11557
|
+
: specifiedByURL;
|
|
11558
|
+
}
|
|
11559
|
+
|
|
11560
|
+
return new GraphQLScalarType({
|
|
11561
|
+
...config,
|
|
11562
|
+
specifiedByURL,
|
|
11563
|
+
extensionASTNodes: config.extensionASTNodes.concat(extensions),
|
|
11564
|
+
});
|
|
11565
|
+
}
|
|
11566
|
+
|
|
11567
|
+
function extendObjectType(type) {
|
|
11568
|
+
var _typeExtensionsMap$co3;
|
|
11569
|
+
|
|
11570
|
+
const config = type.toConfig();
|
|
11571
|
+
const extensions =
|
|
11572
|
+
(_typeExtensionsMap$co3 = typeExtensionsMap[config.name]) !== null &&
|
|
11573
|
+
_typeExtensionsMap$co3 !== void 0
|
|
11574
|
+
? _typeExtensionsMap$co3
|
|
11575
|
+
: [];
|
|
11576
|
+
return new GraphQLObjectType({
|
|
11577
|
+
...config,
|
|
11578
|
+
interfaces: () => [
|
|
11579
|
+
...type.getInterfaces().map(replaceNamedType),
|
|
11580
|
+
...buildInterfaces(extensions),
|
|
11581
|
+
],
|
|
11582
|
+
fields: () => ({
|
|
11583
|
+
...mapValue(config.fields, extendField),
|
|
11584
|
+
...buildFieldMap(extensions),
|
|
11585
|
+
}),
|
|
11586
|
+
extensionASTNodes: config.extensionASTNodes.concat(extensions),
|
|
11587
|
+
});
|
|
11588
|
+
}
|
|
11589
|
+
|
|
11590
|
+
function extendInterfaceType(type) {
|
|
11591
|
+
var _typeExtensionsMap$co4;
|
|
11592
|
+
|
|
11593
|
+
const config = type.toConfig();
|
|
11594
|
+
const extensions =
|
|
11595
|
+
(_typeExtensionsMap$co4 = typeExtensionsMap[config.name]) !== null &&
|
|
11596
|
+
_typeExtensionsMap$co4 !== void 0
|
|
11597
|
+
? _typeExtensionsMap$co4
|
|
11598
|
+
: [];
|
|
11599
|
+
return new GraphQLInterfaceType({
|
|
11600
|
+
...config,
|
|
11601
|
+
interfaces: () => [
|
|
11602
|
+
...type.getInterfaces().map(replaceNamedType),
|
|
11603
|
+
...buildInterfaces(extensions),
|
|
11604
|
+
],
|
|
11605
|
+
fields: () => ({
|
|
11606
|
+
...mapValue(config.fields, extendField),
|
|
11607
|
+
...buildFieldMap(extensions),
|
|
11608
|
+
}),
|
|
11609
|
+
extensionASTNodes: config.extensionASTNodes.concat(extensions),
|
|
11610
|
+
});
|
|
11611
|
+
}
|
|
11612
|
+
|
|
11613
|
+
function extendUnionType(type) {
|
|
11614
|
+
var _typeExtensionsMap$co5;
|
|
11615
|
+
|
|
11616
|
+
const config = type.toConfig();
|
|
11617
|
+
const extensions =
|
|
11618
|
+
(_typeExtensionsMap$co5 = typeExtensionsMap[config.name]) !== null &&
|
|
11619
|
+
_typeExtensionsMap$co5 !== void 0
|
|
11620
|
+
? _typeExtensionsMap$co5
|
|
11621
|
+
: [];
|
|
11622
|
+
return new GraphQLUnionType({
|
|
11623
|
+
...config,
|
|
11624
|
+
types: () => [
|
|
11625
|
+
...type.getTypes().map(replaceNamedType),
|
|
11626
|
+
...buildUnionTypes(extensions),
|
|
11627
|
+
],
|
|
11628
|
+
extensionASTNodes: config.extensionASTNodes.concat(extensions),
|
|
11629
|
+
});
|
|
11630
|
+
}
|
|
11631
|
+
|
|
11632
|
+
function extendField(field) {
|
|
11633
|
+
return {
|
|
11634
|
+
...field,
|
|
11635
|
+
type: replaceType(field.type),
|
|
11636
|
+
args: field.args && mapValue(field.args, extendArg),
|
|
11637
|
+
};
|
|
11638
|
+
}
|
|
11639
|
+
|
|
11640
|
+
function extendArg(arg) {
|
|
11641
|
+
return { ...arg, type: replaceType(arg.type) };
|
|
11642
|
+
}
|
|
11643
|
+
|
|
11644
|
+
function getOperationTypes(nodes) {
|
|
11645
|
+
const opTypes = {};
|
|
11646
|
+
|
|
11647
|
+
for (const node of nodes) {
|
|
11648
|
+
var _node$operationTypes;
|
|
11649
|
+
|
|
11650
|
+
// istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
11651
|
+
const operationTypesNodes =
|
|
11652
|
+
(_node$operationTypes = node.operationTypes) !== null &&
|
|
11653
|
+
_node$operationTypes !== void 0
|
|
11654
|
+
? _node$operationTypes
|
|
11655
|
+
: [];
|
|
11656
|
+
|
|
11657
|
+
for (const operationType of operationTypesNodes) {
|
|
11658
|
+
// Note: While this could make early assertions to get the correctly
|
|
11659
|
+
// typed values below, that would throw immediately while type system
|
|
11660
|
+
// validation with validateSchema() will produce more actionable results.
|
|
11661
|
+
// @ts-expect-error
|
|
11662
|
+
opTypes[operationType.operation] = getNamedType(operationType.type);
|
|
11663
|
+
}
|
|
11664
|
+
}
|
|
11665
|
+
|
|
11666
|
+
return opTypes;
|
|
11667
|
+
}
|
|
11668
|
+
|
|
11669
|
+
function getNamedType(node) {
|
|
11670
|
+
var _stdTypeMap$name2;
|
|
11671
|
+
|
|
11672
|
+
const name = node.name.value;
|
|
11673
|
+
const type =
|
|
11674
|
+
(_stdTypeMap$name2 = stdTypeMap[name]) !== null &&
|
|
11675
|
+
_stdTypeMap$name2 !== void 0
|
|
11676
|
+
? _stdTypeMap$name2
|
|
11677
|
+
: typeMap[name];
|
|
11678
|
+
|
|
11679
|
+
if (type === undefined) {
|
|
11680
|
+
throw new Error(`Unknown type: "${name}".`);
|
|
11681
|
+
}
|
|
11682
|
+
|
|
11683
|
+
return type;
|
|
11684
|
+
}
|
|
11685
|
+
|
|
11686
|
+
function getWrappedType(node) {
|
|
11687
|
+
if (node.kind === Kind.LIST_TYPE) {
|
|
11688
|
+
return new GraphQLList(getWrappedType(node.type));
|
|
11689
|
+
}
|
|
11690
|
+
|
|
11691
|
+
if (node.kind === Kind.NON_NULL_TYPE) {
|
|
11692
|
+
return new GraphQLNonNull(getWrappedType(node.type));
|
|
11693
|
+
}
|
|
11694
|
+
|
|
11695
|
+
return getNamedType(node);
|
|
11696
|
+
}
|
|
11697
|
+
|
|
11698
|
+
function buildDirective(node) {
|
|
11699
|
+
var _node$description;
|
|
11700
|
+
|
|
11701
|
+
return new GraphQLDirective({
|
|
11702
|
+
name: node.name.value,
|
|
11703
|
+
description:
|
|
11704
|
+
(_node$description = node.description) === null ||
|
|
11705
|
+
_node$description === void 0
|
|
11706
|
+
? void 0
|
|
11707
|
+
: _node$description.value,
|
|
11708
|
+
// @ts-expect-error
|
|
11709
|
+
locations: node.locations.map(({ value }) => value),
|
|
11710
|
+
isRepeatable: node.repeatable,
|
|
11711
|
+
args: buildArgumentMap(node.arguments),
|
|
11712
|
+
astNode: node,
|
|
11713
|
+
});
|
|
11714
|
+
}
|
|
11715
|
+
|
|
11716
|
+
function buildFieldMap(nodes) {
|
|
11717
|
+
const fieldConfigMap = Object.create(null);
|
|
11718
|
+
|
|
11719
|
+
for (const node of nodes) {
|
|
11720
|
+
var _node$fields;
|
|
11721
|
+
|
|
11722
|
+
// istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
11723
|
+
const nodeFields =
|
|
11724
|
+
(_node$fields = node.fields) !== null && _node$fields !== void 0
|
|
11725
|
+
? _node$fields
|
|
11726
|
+
: [];
|
|
11727
|
+
|
|
11728
|
+
for (const field of nodeFields) {
|
|
11729
|
+
var _field$description;
|
|
11730
|
+
|
|
11731
|
+
fieldConfigMap[field.name.value] = {
|
|
11732
|
+
// Note: While this could make assertions to get the correctly typed
|
|
11733
|
+
// value, that would throw immediately while type system validation
|
|
11734
|
+
// with validateSchema() will produce more actionable results.
|
|
11735
|
+
type: getWrappedType(field.type),
|
|
11736
|
+
description:
|
|
11737
|
+
(_field$description = field.description) === null ||
|
|
11738
|
+
_field$description === void 0
|
|
11739
|
+
? void 0
|
|
11740
|
+
: _field$description.value,
|
|
11741
|
+
args: buildArgumentMap(field.arguments),
|
|
11742
|
+
deprecationReason: getDeprecationReason(field),
|
|
11743
|
+
astNode: field,
|
|
11744
|
+
};
|
|
11745
|
+
}
|
|
11746
|
+
}
|
|
11747
|
+
|
|
11748
|
+
return fieldConfigMap;
|
|
11749
|
+
}
|
|
11750
|
+
|
|
11751
|
+
function buildArgumentMap(args) {
|
|
11752
|
+
// istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
11753
|
+
const argsNodes = args !== null && args !== void 0 ? args : [];
|
|
11754
|
+
const argConfigMap = Object.create(null);
|
|
11755
|
+
|
|
11756
|
+
for (const arg of argsNodes) {
|
|
11757
|
+
var _arg$description;
|
|
11758
|
+
|
|
11759
|
+
// Note: While this could make assertions to get the correctly typed
|
|
11760
|
+
// value, that would throw immediately while type system validation
|
|
11761
|
+
// with validateSchema() will produce more actionable results.
|
|
11762
|
+
const type = getWrappedType(arg.type);
|
|
11763
|
+
argConfigMap[arg.name.value] = {
|
|
11764
|
+
type,
|
|
11765
|
+
description:
|
|
11766
|
+
(_arg$description = arg.description) === null ||
|
|
11767
|
+
_arg$description === void 0
|
|
11768
|
+
? void 0
|
|
11769
|
+
: _arg$description.value,
|
|
11770
|
+
defaultValue: valueFromAST(arg.defaultValue, type),
|
|
11771
|
+
deprecationReason: getDeprecationReason(arg),
|
|
11772
|
+
astNode: arg,
|
|
11773
|
+
};
|
|
11774
|
+
}
|
|
11775
|
+
|
|
11776
|
+
return argConfigMap;
|
|
11777
|
+
}
|
|
11778
|
+
|
|
11779
|
+
function buildInputFieldMap(nodes) {
|
|
11780
|
+
const inputFieldMap = Object.create(null);
|
|
11781
|
+
|
|
11782
|
+
for (const node of nodes) {
|
|
11783
|
+
var _node$fields2;
|
|
11784
|
+
|
|
11785
|
+
// istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
11786
|
+
const fieldsNodes =
|
|
11787
|
+
(_node$fields2 = node.fields) !== null && _node$fields2 !== void 0
|
|
11788
|
+
? _node$fields2
|
|
11789
|
+
: [];
|
|
11790
|
+
|
|
11791
|
+
for (const field of fieldsNodes) {
|
|
11792
|
+
var _field$description2;
|
|
11793
|
+
|
|
11794
|
+
// Note: While this could make assertions to get the correctly typed
|
|
11795
|
+
// value, that would throw immediately while type system validation
|
|
11796
|
+
// with validateSchema() will produce more actionable results.
|
|
11797
|
+
const type = getWrappedType(field.type);
|
|
11798
|
+
inputFieldMap[field.name.value] = {
|
|
11799
|
+
type,
|
|
11800
|
+
description:
|
|
11801
|
+
(_field$description2 = field.description) === null ||
|
|
11802
|
+
_field$description2 === void 0
|
|
11803
|
+
? void 0
|
|
11804
|
+
: _field$description2.value,
|
|
11805
|
+
defaultValue: valueFromAST(field.defaultValue, type),
|
|
11806
|
+
deprecationReason: getDeprecationReason(field),
|
|
11807
|
+
astNode: field,
|
|
11808
|
+
};
|
|
11809
|
+
}
|
|
11810
|
+
}
|
|
11811
|
+
|
|
11812
|
+
return inputFieldMap;
|
|
11813
|
+
}
|
|
11814
|
+
|
|
11815
|
+
function buildEnumValueMap(nodes) {
|
|
11816
|
+
const enumValueMap = Object.create(null);
|
|
11817
|
+
|
|
11818
|
+
for (const node of nodes) {
|
|
11819
|
+
var _node$values;
|
|
11820
|
+
|
|
11821
|
+
// istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
11822
|
+
const valuesNodes =
|
|
11823
|
+
(_node$values = node.values) !== null && _node$values !== void 0
|
|
11824
|
+
? _node$values
|
|
11825
|
+
: [];
|
|
11826
|
+
|
|
11827
|
+
for (const value of valuesNodes) {
|
|
11828
|
+
var _value$description;
|
|
11829
|
+
|
|
11830
|
+
enumValueMap[value.name.value] = {
|
|
11831
|
+
description:
|
|
11832
|
+
(_value$description = value.description) === null ||
|
|
11833
|
+
_value$description === void 0
|
|
11834
|
+
? void 0
|
|
11835
|
+
: _value$description.value,
|
|
11836
|
+
deprecationReason: getDeprecationReason(value),
|
|
11837
|
+
astNode: value,
|
|
11838
|
+
};
|
|
11839
|
+
}
|
|
11840
|
+
}
|
|
11841
|
+
|
|
11842
|
+
return enumValueMap;
|
|
11843
|
+
}
|
|
11844
|
+
|
|
11845
|
+
function buildInterfaces(nodes) {
|
|
11846
|
+
// Note: While this could make assertions to get the correctly typed
|
|
11847
|
+
// values below, that would throw immediately while type system
|
|
11848
|
+
// validation with validateSchema() will produce more actionable results.
|
|
11849
|
+
// @ts-expect-error
|
|
11850
|
+
return nodes.flatMap(
|
|
11851
|
+
// istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
11852
|
+
(node) => {
|
|
11853
|
+
var _node$interfaces$map, _node$interfaces;
|
|
11854
|
+
|
|
11855
|
+
return (_node$interfaces$map =
|
|
11856
|
+
(_node$interfaces = node.interfaces) === null ||
|
|
11857
|
+
_node$interfaces === void 0
|
|
11858
|
+
? void 0
|
|
11859
|
+
: _node$interfaces.map(getNamedType)) !== null &&
|
|
11860
|
+
_node$interfaces$map !== void 0
|
|
11861
|
+
? _node$interfaces$map
|
|
11862
|
+
: [];
|
|
11863
|
+
},
|
|
11864
|
+
);
|
|
11865
|
+
}
|
|
11866
|
+
|
|
11867
|
+
function buildUnionTypes(nodes) {
|
|
11868
|
+
// Note: While this could make assertions to get the correctly typed
|
|
11869
|
+
// values below, that would throw immediately while type system
|
|
11870
|
+
// validation with validateSchema() will produce more actionable results.
|
|
11871
|
+
// @ts-expect-error
|
|
11872
|
+
return nodes.flatMap(
|
|
11873
|
+
// istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
|
|
11874
|
+
(node) => {
|
|
11875
|
+
var _node$types$map, _node$types;
|
|
11876
|
+
|
|
11877
|
+
return (_node$types$map =
|
|
11878
|
+
(_node$types = node.types) === null || _node$types === void 0
|
|
11879
|
+
? void 0
|
|
11880
|
+
: _node$types.map(getNamedType)) !== null &&
|
|
11881
|
+
_node$types$map !== void 0
|
|
11882
|
+
? _node$types$map
|
|
11883
|
+
: [];
|
|
11884
|
+
},
|
|
11885
|
+
);
|
|
11886
|
+
}
|
|
11887
|
+
|
|
11888
|
+
function buildType(astNode) {
|
|
11889
|
+
var _typeExtensionsMap$na;
|
|
11890
|
+
|
|
11891
|
+
const name = astNode.name.value;
|
|
11892
|
+
const extensionASTNodes =
|
|
11893
|
+
(_typeExtensionsMap$na = typeExtensionsMap[name]) !== null &&
|
|
11894
|
+
_typeExtensionsMap$na !== void 0
|
|
11895
|
+
? _typeExtensionsMap$na
|
|
11896
|
+
: [];
|
|
11897
|
+
|
|
11898
|
+
switch (astNode.kind) {
|
|
11899
|
+
case Kind.OBJECT_TYPE_DEFINITION: {
|
|
11900
|
+
var _astNode$description;
|
|
11901
|
+
|
|
11902
|
+
const allNodes = [astNode, ...extensionASTNodes];
|
|
11903
|
+
return new GraphQLObjectType({
|
|
11904
|
+
name,
|
|
11905
|
+
description:
|
|
11906
|
+
(_astNode$description = astNode.description) === null ||
|
|
11907
|
+
_astNode$description === void 0
|
|
11908
|
+
? void 0
|
|
11909
|
+
: _astNode$description.value,
|
|
11910
|
+
interfaces: () => buildInterfaces(allNodes),
|
|
11911
|
+
fields: () => buildFieldMap(allNodes),
|
|
11912
|
+
astNode,
|
|
11913
|
+
extensionASTNodes,
|
|
11914
|
+
});
|
|
11915
|
+
}
|
|
11916
|
+
|
|
11917
|
+
case Kind.INTERFACE_TYPE_DEFINITION: {
|
|
11918
|
+
var _astNode$description2;
|
|
11919
|
+
|
|
11920
|
+
const allNodes = [astNode, ...extensionASTNodes];
|
|
11921
|
+
return new GraphQLInterfaceType({
|
|
11922
|
+
name,
|
|
11923
|
+
description:
|
|
11924
|
+
(_astNode$description2 = astNode.description) === null ||
|
|
11925
|
+
_astNode$description2 === void 0
|
|
11926
|
+
? void 0
|
|
11927
|
+
: _astNode$description2.value,
|
|
11928
|
+
interfaces: () => buildInterfaces(allNodes),
|
|
11929
|
+
fields: () => buildFieldMap(allNodes),
|
|
11930
|
+
astNode,
|
|
11931
|
+
extensionASTNodes,
|
|
11932
|
+
});
|
|
11933
|
+
}
|
|
11934
|
+
|
|
11935
|
+
case Kind.ENUM_TYPE_DEFINITION: {
|
|
11936
|
+
var _astNode$description3;
|
|
11937
|
+
|
|
11938
|
+
const allNodes = [astNode, ...extensionASTNodes];
|
|
11939
|
+
return new GraphQLEnumType({
|
|
11940
|
+
name,
|
|
11941
|
+
description:
|
|
11942
|
+
(_astNode$description3 = astNode.description) === null ||
|
|
11943
|
+
_astNode$description3 === void 0
|
|
11944
|
+
? void 0
|
|
11945
|
+
: _astNode$description3.value,
|
|
11946
|
+
values: buildEnumValueMap(allNodes),
|
|
11947
|
+
astNode,
|
|
11948
|
+
extensionASTNodes,
|
|
11949
|
+
});
|
|
11950
|
+
}
|
|
11951
|
+
|
|
11952
|
+
case Kind.UNION_TYPE_DEFINITION: {
|
|
11953
|
+
var _astNode$description4;
|
|
11954
|
+
|
|
11955
|
+
const allNodes = [astNode, ...extensionASTNodes];
|
|
11956
|
+
return new GraphQLUnionType({
|
|
11957
|
+
name,
|
|
11958
|
+
description:
|
|
11959
|
+
(_astNode$description4 = astNode.description) === null ||
|
|
11960
|
+
_astNode$description4 === void 0
|
|
11961
|
+
? void 0
|
|
11962
|
+
: _astNode$description4.value,
|
|
11963
|
+
types: () => buildUnionTypes(allNodes),
|
|
11964
|
+
astNode,
|
|
11965
|
+
extensionASTNodes,
|
|
11966
|
+
});
|
|
11967
|
+
}
|
|
11968
|
+
|
|
11969
|
+
case Kind.SCALAR_TYPE_DEFINITION: {
|
|
11970
|
+
var _astNode$description5;
|
|
11971
|
+
|
|
11972
|
+
return new GraphQLScalarType({
|
|
11973
|
+
name,
|
|
11974
|
+
description:
|
|
11975
|
+
(_astNode$description5 = astNode.description) === null ||
|
|
11976
|
+
_astNode$description5 === void 0
|
|
11977
|
+
? void 0
|
|
11978
|
+
: _astNode$description5.value,
|
|
11979
|
+
specifiedByURL: getSpecifiedByURL(astNode),
|
|
11980
|
+
astNode,
|
|
11981
|
+
extensionASTNodes,
|
|
11982
|
+
});
|
|
11983
|
+
}
|
|
11984
|
+
|
|
11985
|
+
case Kind.INPUT_OBJECT_TYPE_DEFINITION: {
|
|
11986
|
+
var _astNode$description6;
|
|
11987
|
+
|
|
11988
|
+
const allNodes = [astNode, ...extensionASTNodes];
|
|
11989
|
+
return new GraphQLInputObjectType({
|
|
11990
|
+
name,
|
|
11991
|
+
description:
|
|
11992
|
+
(_astNode$description6 = astNode.description) === null ||
|
|
11993
|
+
_astNode$description6 === void 0
|
|
11994
|
+
? void 0
|
|
11995
|
+
: _astNode$description6.value,
|
|
11996
|
+
fields: () => buildInputFieldMap(allNodes),
|
|
11997
|
+
astNode,
|
|
11998
|
+
extensionASTNodes,
|
|
11999
|
+
});
|
|
12000
|
+
}
|
|
12001
|
+
} // istanbul ignore next (Not reachable. All possible type definition nodes have been considered)
|
|
12002
|
+
|
|
12003
|
+
|
|
12004
|
+
invariant(false, 'Unexpected type definition node: ' + inspect(astNode));
|
|
12005
|
+
}
|
|
12006
|
+
}
|
|
12007
|
+
const stdTypeMap = keyMap(
|
|
12008
|
+
[...specifiedScalarTypes, ...introspectionTypes],
|
|
12009
|
+
(type) => type.name,
|
|
12010
|
+
);
|
|
12011
|
+
/**
|
|
12012
|
+
* Given a field or enum value node, returns the string value for the
|
|
12013
|
+
* deprecation reason.
|
|
12014
|
+
*/
|
|
12015
|
+
|
|
12016
|
+
function getDeprecationReason(node) {
|
|
12017
|
+
const deprecated = getDirectiveValues(GraphQLDeprecatedDirective, node); // @ts-expect-error validated by `getDirectiveValues`
|
|
12018
|
+
|
|
12019
|
+
return deprecated === null || deprecated === void 0
|
|
12020
|
+
? void 0
|
|
12021
|
+
: deprecated.reason;
|
|
12022
|
+
}
|
|
12023
|
+
/**
|
|
12024
|
+
* Given a scalar node, returns the string value for the specifiedByURL.
|
|
12025
|
+
*/
|
|
12026
|
+
|
|
12027
|
+
function getSpecifiedByURL(node) {
|
|
12028
|
+
const specifiedBy = getDirectiveValues(GraphQLSpecifiedByDirective, node); // @ts-expect-error validated by `getDirectiveValues`
|
|
12029
|
+
|
|
12030
|
+
return specifiedBy === null || specifiedBy === void 0
|
|
12031
|
+
? void 0
|
|
12032
|
+
: specifiedBy.url;
|
|
12033
|
+
}
|
|
12034
|
+
|
|
12035
|
+
/**
|
|
12036
|
+
* This takes the ast of a schema document produced by the parse function in
|
|
12037
|
+
* src/language/parser.js.
|
|
12038
|
+
*
|
|
12039
|
+
* If no schema definition is provided, then it will look for types named Query,
|
|
12040
|
+
* Mutation and Subscription.
|
|
12041
|
+
*
|
|
12042
|
+
* Given that AST it constructs a GraphQLSchema. The resulting schema
|
|
12043
|
+
* has no resolve methods, so execution will use default resolvers.
|
|
12044
|
+
*/
|
|
12045
|
+
function buildASTSchema(documentAST, options) {
|
|
12046
|
+
(documentAST != null && documentAST.kind === Kind.DOCUMENT) ||
|
|
12047
|
+
devAssert(false, 'Must provide valid Document AST.');
|
|
12048
|
+
|
|
12049
|
+
if (
|
|
12050
|
+
(options === null || options === void 0 ? void 0 : options.assumeValid) !==
|
|
12051
|
+
true &&
|
|
12052
|
+
(options === null || options === void 0
|
|
12053
|
+
? void 0
|
|
12054
|
+
: options.assumeValidSDL) !== true
|
|
12055
|
+
) {
|
|
12056
|
+
assertValidSDL(documentAST);
|
|
12057
|
+
}
|
|
12058
|
+
|
|
12059
|
+
const emptySchemaConfig = {
|
|
12060
|
+
description: undefined,
|
|
12061
|
+
types: [],
|
|
12062
|
+
directives: [],
|
|
12063
|
+
extensions: Object.create(null),
|
|
12064
|
+
extensionASTNodes: [],
|
|
12065
|
+
assumeValid: false,
|
|
12066
|
+
};
|
|
12067
|
+
const config = extendSchemaImpl(emptySchemaConfig, documentAST, options);
|
|
12068
|
+
|
|
12069
|
+
if (config.astNode == null) {
|
|
12070
|
+
for (const type of config.types) {
|
|
12071
|
+
switch (type.name) {
|
|
12072
|
+
// Note: While this could make early assertions to get the correctly
|
|
12073
|
+
// typed values below, that would throw immediately while type system
|
|
12074
|
+
// validation with validateSchema() will produce more actionable results.
|
|
12075
|
+
case 'Query':
|
|
12076
|
+
// @ts-expect-error validated in `validateSchema`
|
|
12077
|
+
config.query = type;
|
|
12078
|
+
break;
|
|
12079
|
+
|
|
12080
|
+
case 'Mutation':
|
|
12081
|
+
// @ts-expect-error validated in `validateSchema`
|
|
12082
|
+
config.mutation = type;
|
|
12083
|
+
break;
|
|
12084
|
+
|
|
12085
|
+
case 'Subscription':
|
|
12086
|
+
// @ts-expect-error validated in `validateSchema`
|
|
12087
|
+
config.subscription = type;
|
|
12088
|
+
break;
|
|
12089
|
+
}
|
|
12090
|
+
}
|
|
12091
|
+
}
|
|
12092
|
+
|
|
12093
|
+
const directives = [
|
|
12094
|
+
...config.directives, // If specified directives were not explicitly declared, add them.
|
|
12095
|
+
...specifiedDirectives.filter((stdDirective) =>
|
|
12096
|
+
config.directives.every(
|
|
12097
|
+
(directive) => directive.name !== stdDirective.name,
|
|
12098
|
+
),
|
|
12099
|
+
),
|
|
12100
|
+
];
|
|
12101
|
+
return new GraphQLSchema({ ...config, directives });
|
|
12102
|
+
}
|
|
12103
|
+
/**
|
|
12104
|
+
* A helper function to build a GraphQLSchema directly from a source
|
|
12105
|
+
* document.
|
|
12106
|
+
*/
|
|
12107
|
+
|
|
12108
|
+
function buildSchema(source, options) {
|
|
12109
|
+
const document = parse(source, {
|
|
12110
|
+
noLocation:
|
|
12111
|
+
options === null || options === void 0 ? void 0 : options.noLocation,
|
|
12112
|
+
allowLegacyFragmentVariables:
|
|
12113
|
+
options === null || options === void 0
|
|
12114
|
+
? void 0
|
|
12115
|
+
: options.allowLegacyFragmentVariables,
|
|
12116
|
+
});
|
|
12117
|
+
return buildASTSchema(document, {
|
|
12118
|
+
assumeValidSDL:
|
|
12119
|
+
options === null || options === void 0 ? void 0 : options.assumeValidSDL,
|
|
12120
|
+
assumeValid:
|
|
12121
|
+
options === null || options === void 0 ? void 0 : options.assumeValid,
|
|
12122
|
+
});
|
|
12123
|
+
}
|
|
12124
|
+
|
|
7697
12125
|
function printSchema(schema) {
|
|
7698
12126
|
return printFilteredSchema(
|
|
7699
12127
|
schema,
|
|
@@ -8086,8 +12514,8 @@ function run() {
|
|
|
8086
12514
|
let oldSchema;
|
|
8087
12515
|
let newSchema;
|
|
8088
12516
|
let sources;
|
|
8089
|
-
if (path.extname(schemaPath.toLowerCase()) ===
|
|
8090
|
-
oldSchema = buildClientSchema(JSON.parse(oldFile));
|
|
12517
|
+
if (path.extname(schemaPath.toLowerCase()) === ".json") {
|
|
12518
|
+
oldSchema = endpoint ? buildSchema(oldFile) : buildClientSchema(JSON.parse(oldFile));
|
|
8091
12519
|
newSchema = buildClientSchema(JSON.parse(newFile));
|
|
8092
12520
|
sources = {
|
|
8093
12521
|
old: new Source(printSchema(oldSchema), endpoint || `${schemaRef}:${schemaPath}`),
|
|
@@ -8160,6 +12588,8 @@ function fileLoader({ octokit, owner, repo, }) {
|
|
|
8160
12588
|
repository(name: $repo, owner: $owner) {
|
|
8161
12589
|
object(expression: $expression) {
|
|
8162
12590
|
... on Blob {
|
|
12591
|
+
isTruncated
|
|
12592
|
+
oid
|
|
8163
12593
|
text
|
|
8164
12594
|
}
|
|
8165
12595
|
}
|
|
@@ -8167,7 +12597,7 @@ function fileLoader({ octokit, owner, repo, }) {
|
|
|
8167
12597
|
}
|
|
8168
12598
|
`;
|
|
8169
12599
|
return function loadFile(file) {
|
|
8170
|
-
var _a, _b;
|
|
12600
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
8171
12601
|
return tslib.__awaiter(this, void 0, void 0, function* () {
|
|
8172
12602
|
if (file.workspace) {
|
|
8173
12603
|
return fs.readFileSync(path.resolve(file.workspace, file.path), {
|
|
@@ -8181,8 +12611,23 @@ function fileLoader({ octokit, owner, repo, }) {
|
|
|
8181
12611
|
});
|
|
8182
12612
|
core.info(`Query ${file.ref}:${file.path} from ${owner}/${repo}`);
|
|
8183
12613
|
try {
|
|
8184
|
-
if ((_b = (_a = result === null || result === void 0 ? void 0 : result.repository) === null || _a === void 0 ? void 0 : _a.object) === null || _b === void 0 ? void 0 : _b.
|
|
8185
|
-
|
|
12614
|
+
if (((_b = (_a = result === null || result === void 0 ? void 0 : result.repository) === null || _a === void 0 ? void 0 : _a.object) === null || _b === void 0 ? void 0 : _b.oid) && ((_d = (_c = result === null || result === void 0 ? void 0 : result.repository) === null || _c === void 0 ? void 0 : _c.object) === null || _d === void 0 ? void 0 : _d.isTruncated)) {
|
|
12615
|
+
const oid = (_f = (_e = result === null || result === void 0 ? void 0 : result.repository) === null || _e === void 0 ? void 0 : _e.object) === null || _f === void 0 ? void 0 : _f.oid;
|
|
12616
|
+
const getBlobResponse = yield octokit.git.getBlob({
|
|
12617
|
+
owner,
|
|
12618
|
+
repo,
|
|
12619
|
+
file_sha: oid,
|
|
12620
|
+
});
|
|
12621
|
+
if ((_g = getBlobResponse === null || getBlobResponse === void 0 ? void 0 : getBlobResponse.data) === null || _g === void 0 ? void 0 : _g.content) {
|
|
12622
|
+
return Buffer.from((_h = getBlobResponse === null || getBlobResponse === void 0 ? void 0 : getBlobResponse.data) === null || _h === void 0 ? void 0 : _h.content, 'base64').toString('utf-8');
|
|
12623
|
+
}
|
|
12624
|
+
throw new Error('getBlobResponse.data.content is null');
|
|
12625
|
+
}
|
|
12626
|
+
if ((_k = (_j = result === null || result === void 0 ? void 0 : result.repository) === null || _j === void 0 ? void 0 : _j.object) === null || _k === void 0 ? void 0 : _k.text) {
|
|
12627
|
+
if (((_m = (_l = result === null || result === void 0 ? void 0 : result.repository) === null || _l === void 0 ? void 0 : _l.object) === null || _m === void 0 ? void 0 : _m.isTruncated) === false) {
|
|
12628
|
+
return result.repository.object.text;
|
|
12629
|
+
}
|
|
12630
|
+
throw new Error('result.repository.object.text is truncated and oid is null');
|
|
8186
12631
|
}
|
|
8187
12632
|
throw new Error('result.repository.object.text is null');
|
|
8188
12633
|
}
|