graphql 16.8.1 → 16.9.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.
Files changed (47) hide show
  1. package/index.d.ts +3 -0
  2. package/index.js +18 -0
  3. package/index.mjs +6 -3
  4. package/jsutils/instanceOf.js +6 -1
  5. package/jsutils/instanceOf.mjs +6 -1
  6. package/package.json +1 -1
  7. package/type/definition.d.ts +4 -1
  8. package/type/definition.js +29 -6
  9. package/type/definition.mjs +26 -6
  10. package/type/directives.d.ts +4 -0
  11. package/type/directives.js +15 -1
  12. package/type/directives.mjs +12 -0
  13. package/type/index.d.ts +1 -0
  14. package/type/index.js +6 -0
  15. package/type/index.mjs +2 -1
  16. package/type/introspection.js +8 -0
  17. package/type/introspection.mjs +8 -0
  18. package/type/validate.js +24 -0
  19. package/type/validate.mjs +24 -0
  20. package/utilities/buildClientSchema.js +1 -0
  21. package/utilities/buildClientSchema.mjs +1 -0
  22. package/utilities/coerceInputValue.js +25 -0
  23. package/utilities/coerceInputValue.mjs +25 -0
  24. package/utilities/extendSchema.js +10 -0
  25. package/utilities/extendSchema.mjs +9 -0
  26. package/utilities/getIntrospectionQuery.d.ts +6 -0
  27. package/utilities/getIntrospectionQuery.js +3 -0
  28. package/utilities/getIntrospectionQuery.mjs +3 -0
  29. package/utilities/introspectionFromSchema.js +1 -0
  30. package/utilities/introspectionFromSchema.mjs +1 -0
  31. package/utilities/printSchema.js +6 -1
  32. package/utilities/printSchema.mjs +6 -1
  33. package/utilities/valueFromAST.js +12 -0
  34. package/utilities/valueFromAST.mjs +12 -0
  35. package/validation/index.d.ts +2 -1
  36. package/validation/index.js +14 -0
  37. package/validation/index.mjs +3 -2
  38. package/validation/rules/MaxIntrospectionDepthRule.d.ts +5 -0
  39. package/validation/rules/MaxIntrospectionDepthRule.js +90 -0
  40. package/validation/rules/MaxIntrospectionDepthRule.mjs +77 -0
  41. package/validation/rules/ValuesOfCorrectTypeRule.js +88 -0
  42. package/validation/rules/ValuesOfCorrectTypeRule.mjs +83 -0
  43. package/validation/specifiedRules.d.ts +6 -0
  44. package/validation/specifiedRules.js +17 -1
  45. package/validation/specifiedRules.mjs +10 -1
  46. package/version.js +3 -3
  47. package/version.mjs +3 -3
@@ -26,6 +26,11 @@ export interface IntrospectionOptions {
26
26
  * Default: false
27
27
  */
28
28
  inputValueDeprecation?: boolean;
29
+ /**
30
+ * Whether target GraphQL server supports `@oneOf` input objects.
31
+ * Default: false
32
+ */
33
+ oneOf?: boolean;
29
34
  }
30
35
  /**
31
36
  * Produce the GraphQL query recommended for a full schema introspection.
@@ -112,6 +117,7 @@ export interface IntrospectionInputObjectType {
112
117
  readonly name: string;
113
118
  readonly description?: Maybe<string>;
114
119
  readonly inputFields: ReadonlyArray<IntrospectionInputValue>;
120
+ readonly isOneOf: boolean;
115
121
  }
116
122
  export interface IntrospectionListTypeRef<
117
123
  T extends IntrospectionTypeRef = IntrospectionTypeRef,
@@ -16,6 +16,7 @@ function getIntrospectionQuery(options) {
16
16
  directiveIsRepeatable: false,
17
17
  schemaDescription: false,
18
18
  inputValueDeprecation: false,
19
+ oneOf: false,
19
20
  ...options,
20
21
  };
21
22
  const descriptions = optionsWithDefault.descriptions ? 'description' : '';
@@ -33,6 +34,7 @@ function getIntrospectionQuery(options) {
33
34
  return optionsWithDefault.inputValueDeprecation ? str : '';
34
35
  }
35
36
 
37
+ const oneOf = optionsWithDefault.oneOf ? 'isOneOf' : '';
36
38
  return `
37
39
  query IntrospectionQuery {
38
40
  __schema {
@@ -60,6 +62,7 @@ function getIntrospectionQuery(options) {
60
62
  name
61
63
  ${descriptions}
62
64
  ${specifiedByUrl}
65
+ ${oneOf}
63
66
  fields(includeDeprecated: true) {
64
67
  name
65
68
  ${descriptions}
@@ -9,6 +9,7 @@ export function getIntrospectionQuery(options) {
9
9
  directiveIsRepeatable: false,
10
10
  schemaDescription: false,
11
11
  inputValueDeprecation: false,
12
+ oneOf: false,
12
13
  ...options,
13
14
  };
14
15
  const descriptions = optionsWithDefault.descriptions ? 'description' : '';
@@ -26,6 +27,7 @@ export function getIntrospectionQuery(options) {
26
27
  return optionsWithDefault.inputValueDeprecation ? str : '';
27
28
  }
28
29
 
30
+ const oneOf = optionsWithDefault.oneOf ? 'isOneOf' : '';
29
31
  return `
30
32
  query IntrospectionQuery {
31
33
  __schema {
@@ -53,6 +55,7 @@ export function getIntrospectionQuery(options) {
53
55
  name
54
56
  ${descriptions}
55
57
  ${specifiedByUrl}
58
+ ${oneOf}
56
59
  fields(includeDeprecated: true) {
57
60
  name
58
61
  ${descriptions}
@@ -28,6 +28,7 @@ function introspectionFromSchema(schema, options) {
28
28
  directiveIsRepeatable: true,
29
29
  schemaDescription: true,
30
30
  inputValueDeprecation: true,
31
+ oneOf: true,
31
32
  ...options,
32
33
  };
33
34
  const document = (0, _parser.parse)(
@@ -18,6 +18,7 @@ export function introspectionFromSchema(schema, options) {
18
18
  directiveIsRepeatable: true,
19
19
  schemaDescription: true,
20
20
  inputValueDeprecation: true,
21
+ oneOf: true,
21
22
  ...options,
22
23
  };
23
24
  const document = parse(getIntrospectionQuery(optionsWithDefaults));
@@ -214,7 +214,12 @@ function printInputObject(type) {
214
214
  const fields = Object.values(type.getFields()).map(
215
215
  (f, i) => printDescription(f, ' ', !i) + ' ' + printInputValue(f),
216
216
  );
217
- return printDescription(type) + `input ${type.name}` + printBlock(fields);
217
+ return (
218
+ printDescription(type) +
219
+ `input ${type.name}` +
220
+ (type.isOneOf ? ' @oneOf' : '') +
221
+ printBlock(fields)
222
+ );
218
223
  }
219
224
 
220
225
  function printFields(type) {
@@ -193,7 +193,12 @@ function printInputObject(type) {
193
193
  const fields = Object.values(type.getFields()).map(
194
194
  (f, i) => printDescription(f, ' ', !i) + ' ' + printInputValue(f),
195
195
  );
196
- return printDescription(type) + `input ${type.name}` + printBlock(fields);
196
+ return (
197
+ printDescription(type) +
198
+ `input ${type.name}` +
199
+ (type.isOneOf ? ' @oneOf' : '') +
200
+ printBlock(fields)
201
+ );
197
202
  }
198
203
 
199
204
  function printFields(type) {
@@ -145,6 +145,18 @@ function valueFromAST(valueNode, type, variables) {
145
145
  coercedObj[field.name] = fieldValue;
146
146
  }
147
147
 
148
+ if (type.isOneOf) {
149
+ const keys = Object.keys(coercedObj);
150
+
151
+ if (keys.length !== 1) {
152
+ return; // Invalid: not exactly one key, intentionally return no value.
153
+ }
154
+
155
+ if (coercedObj[keys[0]] === null) {
156
+ return; // Invalid: value not non-null, intentionally return no value.
157
+ }
158
+ }
159
+
148
160
  return coercedObj;
149
161
  }
150
162
 
@@ -136,6 +136,18 @@ export function valueFromAST(valueNode, type, variables) {
136
136
  coercedObj[field.name] = fieldValue;
137
137
  }
138
138
 
139
+ if (type.isOneOf) {
140
+ const keys = Object.keys(coercedObj);
141
+
142
+ if (keys.length !== 1) {
143
+ return; // Invalid: not exactly one key, intentionally return no value.
144
+ }
145
+
146
+ if (coercedObj[keys[0]] === null) {
147
+ return; // Invalid: value not non-null, intentionally return no value.
148
+ }
149
+ }
150
+
139
151
  return coercedObj;
140
152
  }
141
153
 
@@ -1,7 +1,7 @@
1
1
  export { validate } from './validate';
2
2
  export { ValidationContext } from './ValidationContext';
3
3
  export type { ValidationRule } from './ValidationContext';
4
- export { specifiedRules } from './specifiedRules';
4
+ export { specifiedRules, recommendedRules } from './specifiedRules';
5
5
  export { ExecutableDefinitionsRule } from './rules/ExecutableDefinitionsRule';
6
6
  export { FieldsOnCorrectTypeRule } from './rules/FieldsOnCorrectTypeRule';
7
7
  export { FragmentsOnCompositeTypesRule } from './rules/FragmentsOnCompositeTypesRule';
@@ -28,6 +28,7 @@ export { UniqueVariableNamesRule } from './rules/UniqueVariableNamesRule';
28
28
  export { ValuesOfCorrectTypeRule } from './rules/ValuesOfCorrectTypeRule';
29
29
  export { VariablesAreInputTypesRule } from './rules/VariablesAreInputTypesRule';
30
30
  export { VariablesInAllowedPositionRule } from './rules/VariablesInAllowedPositionRule';
31
+ export { MaxIntrospectionDepthRule } from './rules/MaxIntrospectionDepthRule';
31
32
  export { LoneSchemaDefinitionRule } from './rules/LoneSchemaDefinitionRule';
32
33
  export { UniqueOperationTypesRule } from './rules/UniqueOperationTypesRule';
33
34
  export { UniqueTypeNamesRule } from './rules/UniqueTypeNamesRule';
@@ -57,6 +57,12 @@ Object.defineProperty(exports, 'LoneSchemaDefinitionRule', {
57
57
  return _LoneSchemaDefinitionRule.LoneSchemaDefinitionRule;
58
58
  },
59
59
  });
60
+ Object.defineProperty(exports, 'MaxIntrospectionDepthRule', {
61
+ enumerable: true,
62
+ get: function () {
63
+ return _MaxIntrospectionDepthRule.MaxIntrospectionDepthRule;
64
+ },
65
+ });
60
66
  Object.defineProperty(exports, 'NoDeprecatedCustomRule', {
61
67
  enumerable: true,
62
68
  get: function () {
@@ -225,6 +231,12 @@ Object.defineProperty(exports, 'VariablesInAllowedPositionRule', {
225
231
  return _VariablesInAllowedPositionRule.VariablesInAllowedPositionRule;
226
232
  },
227
233
  });
234
+ Object.defineProperty(exports, 'recommendedRules', {
235
+ enumerable: true,
236
+ get: function () {
237
+ return _specifiedRules.recommendedRules;
238
+ },
239
+ });
228
240
  Object.defineProperty(exports, 'specifiedRules', {
229
241
  enumerable: true,
230
242
  get: function () {
@@ -296,6 +308,8 @@ var _VariablesAreInputTypesRule = require('./rules/VariablesAreInputTypesRule.js
296
308
 
297
309
  var _VariablesInAllowedPositionRule = require('./rules/VariablesInAllowedPositionRule.js');
298
310
 
311
+ var _MaxIntrospectionDepthRule = require('./rules/MaxIntrospectionDepthRule.js');
312
+
299
313
  var _LoneSchemaDefinitionRule = require('./rules/LoneSchemaDefinitionRule.js');
300
314
 
301
315
  var _UniqueOperationTypesRule = require('./rules/UniqueOperationTypesRule.js');
@@ -1,7 +1,7 @@
1
1
  export { validate } from './validate.mjs';
2
2
  export { ValidationContext } from './ValidationContext.mjs';
3
3
  // All validation rules in the GraphQL Specification.
4
- export { specifiedRules } from './specifiedRules.mjs'; // Spec Section: "Executable Definitions"
4
+ export { specifiedRules, recommendedRules } from './specifiedRules.mjs'; // Spec Section: "Executable Definitions"
5
5
 
6
6
  export { ExecutableDefinitionsRule } from './rules/ExecutableDefinitionsRule.mjs'; // Spec Section: "Field Selections on Objects, Interfaces, and Unions Types"
7
7
 
@@ -53,7 +53,8 @@ export { ValuesOfCorrectTypeRule } from './rules/ValuesOfCorrectTypeRule.mjs'; /
53
53
 
54
54
  export { VariablesAreInputTypesRule } from './rules/VariablesAreInputTypesRule.mjs'; // Spec Section: "All Variable Usages Are Allowed"
55
55
 
56
- export { VariablesInAllowedPositionRule } from './rules/VariablesInAllowedPositionRule.mjs'; // SDL-specific validation rules
56
+ export { VariablesInAllowedPositionRule } from './rules/VariablesInAllowedPositionRule.mjs';
57
+ export { MaxIntrospectionDepthRule } from './rules/MaxIntrospectionDepthRule.mjs'; // SDL-specific validation rules
57
58
 
58
59
  export { LoneSchemaDefinitionRule } from './rules/LoneSchemaDefinitionRule.mjs';
59
60
  export { UniqueOperationTypesRule } from './rules/UniqueOperationTypesRule.mjs';
@@ -0,0 +1,5 @@
1
+ import type { ASTVisitor } from '../../language/visitor';
2
+ import type { ASTValidationContext } from '../ValidationContext';
3
+ export declare function MaxIntrospectionDepthRule(
4
+ context: ASTValidationContext,
5
+ ): ASTVisitor;
@@ -0,0 +1,90 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', {
4
+ value: true,
5
+ });
6
+ exports.MaxIntrospectionDepthRule = MaxIntrospectionDepthRule;
7
+
8
+ var _GraphQLError = require('../../error/GraphQLError.js');
9
+
10
+ var _kinds = require('../../language/kinds.js');
11
+
12
+ const MAX_LISTS_DEPTH = 3;
13
+
14
+ function MaxIntrospectionDepthRule(context) {
15
+ /**
16
+ * Counts the depth of list fields in "__Type" recursively and
17
+ * returns `true` if the limit has been reached.
18
+ */
19
+ function checkDepth(node, visitedFragments = Object.create(null), depth = 0) {
20
+ if (node.kind === _kinds.Kind.FRAGMENT_SPREAD) {
21
+ const fragmentName = node.name.value;
22
+
23
+ if (visitedFragments[fragmentName] === true) {
24
+ // Fragment cycles are handled by `NoFragmentCyclesRule`.
25
+ return false;
26
+ }
27
+
28
+ const fragment = context.getFragment(fragmentName);
29
+
30
+ if (!fragment) {
31
+ // Missing fragments checks are handled by `KnownFragmentNamesRule`.
32
+ return false;
33
+ } // Rather than following an immutable programming pattern which has
34
+ // significant memory and garbage collection overhead, we've opted to
35
+ // take a mutable approach for efficiency's sake. Importantly visiting a
36
+ // fragment twice is fine, so long as you don't do one visit inside the
37
+ // other.
38
+
39
+ try {
40
+ visitedFragments[fragmentName] = true;
41
+ return checkDepth(fragment, visitedFragments, depth);
42
+ } finally {
43
+ visitedFragments[fragmentName] = undefined;
44
+ }
45
+ }
46
+
47
+ if (
48
+ node.kind === _kinds.Kind.FIELD && // check all introspection lists
49
+ (node.name.value === 'fields' ||
50
+ node.name.value === 'interfaces' ||
51
+ node.name.value === 'possibleTypes' ||
52
+ node.name.value === 'inputFields')
53
+ ) {
54
+ // eslint-disable-next-line no-param-reassign
55
+ depth++;
56
+
57
+ if (depth >= MAX_LISTS_DEPTH) {
58
+ return true;
59
+ }
60
+ } // handles fields and inline fragments
61
+
62
+ if ('selectionSet' in node && node.selectionSet) {
63
+ for (const child of node.selectionSet.selections) {
64
+ if (checkDepth(child, visitedFragments, depth)) {
65
+ return true;
66
+ }
67
+ }
68
+ }
69
+
70
+ return false;
71
+ }
72
+
73
+ return {
74
+ Field(node) {
75
+ if (node.name.value === '__schema' || node.name.value === '__type') {
76
+ if (checkDepth(node)) {
77
+ context.reportError(
78
+ new _GraphQLError.GraphQLError(
79
+ 'Maximum introspection depth exceeded',
80
+ {
81
+ nodes: [node],
82
+ },
83
+ ),
84
+ );
85
+ return false;
86
+ }
87
+ }
88
+ },
89
+ };
90
+ }
@@ -0,0 +1,77 @@
1
+ import { GraphQLError } from '../../error/GraphQLError.mjs';
2
+ import { Kind } from '../../language/kinds.mjs';
3
+ const MAX_LISTS_DEPTH = 3;
4
+ export function MaxIntrospectionDepthRule(context) {
5
+ /**
6
+ * Counts the depth of list fields in "__Type" recursively and
7
+ * returns `true` if the limit has been reached.
8
+ */
9
+ function checkDepth(node, visitedFragments = Object.create(null), depth = 0) {
10
+ if (node.kind === Kind.FRAGMENT_SPREAD) {
11
+ const fragmentName = node.name.value;
12
+
13
+ if (visitedFragments[fragmentName] === true) {
14
+ // Fragment cycles are handled by `NoFragmentCyclesRule`.
15
+ return false;
16
+ }
17
+
18
+ const fragment = context.getFragment(fragmentName);
19
+
20
+ if (!fragment) {
21
+ // Missing fragments checks are handled by `KnownFragmentNamesRule`.
22
+ return false;
23
+ } // Rather than following an immutable programming pattern which has
24
+ // significant memory and garbage collection overhead, we've opted to
25
+ // take a mutable approach for efficiency's sake. Importantly visiting a
26
+ // fragment twice is fine, so long as you don't do one visit inside the
27
+ // other.
28
+
29
+ try {
30
+ visitedFragments[fragmentName] = true;
31
+ return checkDepth(fragment, visitedFragments, depth);
32
+ } finally {
33
+ visitedFragments[fragmentName] = undefined;
34
+ }
35
+ }
36
+
37
+ if (
38
+ node.kind === Kind.FIELD && // check all introspection lists
39
+ (node.name.value === 'fields' ||
40
+ node.name.value === 'interfaces' ||
41
+ node.name.value === 'possibleTypes' ||
42
+ node.name.value === 'inputFields')
43
+ ) {
44
+ // eslint-disable-next-line no-param-reassign
45
+ depth++;
46
+
47
+ if (depth >= MAX_LISTS_DEPTH) {
48
+ return true;
49
+ }
50
+ } // handles fields and inline fragments
51
+
52
+ if ('selectionSet' in node && node.selectionSet) {
53
+ for (const child of node.selectionSet.selections) {
54
+ if (checkDepth(child, visitedFragments, depth)) {
55
+ return true;
56
+ }
57
+ }
58
+ }
59
+
60
+ return false;
61
+ }
62
+
63
+ return {
64
+ Field(node) {
65
+ if (node.name.value === '__schema' || node.name.value === '__type') {
66
+ if (checkDepth(node)) {
67
+ context.reportError(
68
+ new GraphQLError('Maximum introspection depth exceeded', {
69
+ nodes: [node],
70
+ }),
71
+ );
72
+ return false;
73
+ }
74
+ }
75
+ },
76
+ };
77
+ }
@@ -15,6 +15,8 @@ var _suggestionList = require('../../jsutils/suggestionList.js');
15
15
 
16
16
  var _GraphQLError = require('../../error/GraphQLError.js');
17
17
 
18
+ var _kinds = require('../../language/kinds.js');
19
+
18
20
  var _printer = require('../../language/printer.js');
19
21
 
20
22
  var _definition = require('../../type/definition.js');
@@ -28,7 +30,18 @@ var _definition = require('../../type/definition.js');
28
30
  * See https://spec.graphql.org/draft/#sec-Values-of-Correct-Type
29
31
  */
30
32
  function ValuesOfCorrectTypeRule(context) {
33
+ let variableDefinitions = {};
31
34
  return {
35
+ OperationDefinition: {
36
+ enter() {
37
+ variableDefinitions = {};
38
+ },
39
+ },
40
+
41
+ VariableDefinition(definition) {
42
+ variableDefinitions[definition.variable.name.value] = definition;
43
+ },
44
+
32
45
  ListValue(node) {
33
46
  // Note: TypeInfo will traverse into a list's item type, so look to the
34
47
  // parent input type to check if it is a list.
@@ -70,6 +83,16 @@ function ValuesOfCorrectTypeRule(context) {
70
83
  );
71
84
  }
72
85
  }
86
+
87
+ if (type.isOneOf) {
88
+ validateOneOfInputObject(
89
+ context,
90
+ node,
91
+ type,
92
+ fieldNodeMap,
93
+ variableDefinitions,
94
+ );
95
+ }
73
96
  },
74
97
 
75
98
  ObjectField(node) {
@@ -190,3 +213,68 @@ function isValidValueNode(context, node) {
190
213
  }
191
214
  }
192
215
  }
216
+
217
+ function validateOneOfInputObject(
218
+ context,
219
+ node,
220
+ type,
221
+ fieldNodeMap,
222
+ variableDefinitions,
223
+ ) {
224
+ var _fieldNodeMap$keys$;
225
+
226
+ const keys = Object.keys(fieldNodeMap);
227
+ const isNotExactlyOneField = keys.length !== 1;
228
+
229
+ if (isNotExactlyOneField) {
230
+ context.reportError(
231
+ new _GraphQLError.GraphQLError(
232
+ `OneOf Input Object "${type.name}" must specify exactly one key.`,
233
+ {
234
+ nodes: [node],
235
+ },
236
+ ),
237
+ );
238
+ return;
239
+ }
240
+
241
+ const value =
242
+ (_fieldNodeMap$keys$ = fieldNodeMap[keys[0]]) === null ||
243
+ _fieldNodeMap$keys$ === void 0
244
+ ? void 0
245
+ : _fieldNodeMap$keys$.value;
246
+ const isNullLiteral = !value || value.kind === _kinds.Kind.NULL;
247
+ const isVariable =
248
+ (value === null || value === void 0 ? void 0 : value.kind) ===
249
+ _kinds.Kind.VARIABLE;
250
+
251
+ if (isNullLiteral) {
252
+ context.reportError(
253
+ new _GraphQLError.GraphQLError(
254
+ `Field "${type.name}.${keys[0]}" must be non-null.`,
255
+ {
256
+ nodes: [node],
257
+ },
258
+ ),
259
+ );
260
+ return;
261
+ }
262
+
263
+ if (isVariable) {
264
+ const variableName = value.name.value;
265
+ const definition = variableDefinitions[variableName];
266
+ const isNullableVariable =
267
+ definition.type.kind !== _kinds.Kind.NON_NULL_TYPE;
268
+
269
+ if (isNullableVariable) {
270
+ context.reportError(
271
+ new _GraphQLError.GraphQLError(
272
+ `Variable "${variableName}" must be non-nullable to be used for OneOf Input Object "${type.name}".`,
273
+ {
274
+ nodes: [node],
275
+ },
276
+ ),
277
+ );
278
+ }
279
+ }
280
+ }
@@ -3,6 +3,7 @@ import { inspect } from '../../jsutils/inspect.mjs';
3
3
  import { keyMap } from '../../jsutils/keyMap.mjs';
4
4
  import { suggestionList } from '../../jsutils/suggestionList.mjs';
5
5
  import { GraphQLError } from '../../error/GraphQLError.mjs';
6
+ import { Kind } from '../../language/kinds.mjs';
6
7
  import { print } from '../../language/printer.mjs';
7
8
  import {
8
9
  getNamedType,
@@ -23,7 +24,18 @@ import {
23
24
  * See https://spec.graphql.org/draft/#sec-Values-of-Correct-Type
24
25
  */
25
26
  export function ValuesOfCorrectTypeRule(context) {
27
+ let variableDefinitions = {};
26
28
  return {
29
+ OperationDefinition: {
30
+ enter() {
31
+ variableDefinitions = {};
32
+ },
33
+ },
34
+
35
+ VariableDefinition(definition) {
36
+ variableDefinitions[definition.variable.name.value] = definition;
37
+ },
38
+
27
39
  ListValue(node) {
28
40
  // Note: TypeInfo will traverse into a list's item type, so look to the
29
41
  // parent input type to check if it is a list.
@@ -60,6 +72,16 @@ export function ValuesOfCorrectTypeRule(context) {
60
72
  );
61
73
  }
62
74
  }
75
+
76
+ if (type.isOneOf) {
77
+ validateOneOfInputObject(
78
+ context,
79
+ node,
80
+ type,
81
+ fieldNodeMap,
82
+ variableDefinitions,
83
+ );
84
+ }
63
85
  },
64
86
 
65
87
  ObjectField(node) {
@@ -171,3 +193,64 @@ function isValidValueNode(context, node) {
171
193
  }
172
194
  }
173
195
  }
196
+
197
+ function validateOneOfInputObject(
198
+ context,
199
+ node,
200
+ type,
201
+ fieldNodeMap,
202
+ variableDefinitions,
203
+ ) {
204
+ var _fieldNodeMap$keys$;
205
+
206
+ const keys = Object.keys(fieldNodeMap);
207
+ const isNotExactlyOneField = keys.length !== 1;
208
+
209
+ if (isNotExactlyOneField) {
210
+ context.reportError(
211
+ new GraphQLError(
212
+ `OneOf Input Object "${type.name}" must specify exactly one key.`,
213
+ {
214
+ nodes: [node],
215
+ },
216
+ ),
217
+ );
218
+ return;
219
+ }
220
+
221
+ const value =
222
+ (_fieldNodeMap$keys$ = fieldNodeMap[keys[0]]) === null ||
223
+ _fieldNodeMap$keys$ === void 0
224
+ ? void 0
225
+ : _fieldNodeMap$keys$.value;
226
+ const isNullLiteral = !value || value.kind === Kind.NULL;
227
+ const isVariable =
228
+ (value === null || value === void 0 ? void 0 : value.kind) ===
229
+ Kind.VARIABLE;
230
+
231
+ if (isNullLiteral) {
232
+ context.reportError(
233
+ new GraphQLError(`Field "${type.name}.${keys[0]}" must be non-null.`, {
234
+ nodes: [node],
235
+ }),
236
+ );
237
+ return;
238
+ }
239
+
240
+ if (isVariable) {
241
+ const variableName = value.name.value;
242
+ const definition = variableDefinitions[variableName];
243
+ const isNullableVariable = definition.type.kind !== Kind.NON_NULL_TYPE;
244
+
245
+ if (isNullableVariable) {
246
+ context.reportError(
247
+ new GraphQLError(
248
+ `Variable "${variableName}" must be non-nullable to be used for OneOf Input Object "${type.name}".`,
249
+ {
250
+ nodes: [node],
251
+ },
252
+ ),
253
+ );
254
+ }
255
+ }
256
+ }
@@ -1,4 +1,10 @@
1
+ import { MaxIntrospectionDepthRule } from './rules/MaxIntrospectionDepthRule';
1
2
  import type { SDLValidationRule, ValidationRule } from './ValidationContext';
3
+ /**
4
+ * Technically these aren't part of the spec but they are strongly encouraged
5
+ * validation rules.
6
+ */
7
+ export declare const recommendedRules: readonly typeof MaxIntrospectionDepthRule[];
2
8
  /**
3
9
  * This set includes all validation rules defined by the GraphQL spec.
4
10
  *