@graphql-tools/utils 10.7.2 → 12.0.1

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 (89) hide show
  1. package/cjs/astFromValue.js +21 -14
  2. package/cjs/build-operation-for-field.js +13 -3
  3. package/cjs/collectFields.js +4 -3
  4. package/cjs/comments.js +1 -1
  5. package/cjs/defaultValueAstFromType.js +32 -0
  6. package/cjs/descriptionFromObject.js +7 -5
  7. package/cjs/errors.js +30 -4
  8. package/cjs/getArgumentValues.js +23 -15
  9. package/cjs/getDirectiveValues.js +15 -0
  10. package/cjs/heal.js +4 -1
  11. package/cjs/helpers.js +40 -4
  12. package/cjs/index.js +8 -5
  13. package/cjs/jsutils.js +4 -6
  14. package/cjs/mapSchema.js +2 -1
  15. package/cjs/mergeDeep.js +35 -9
  16. package/cjs/mergeIncrementalResult.js +25 -4
  17. package/cjs/observableToAsyncIterable.js +29 -17
  18. package/cjs/print-schema-with-directives.js +23 -17
  19. package/cjs/registerAbortSignalListener.js +5 -1
  20. package/cjs/stub.js +1 -0
  21. package/cjs/updateArgument.js +2 -1
  22. package/cjs/visitResult.js +7 -1
  23. package/esm/astFromValue.js +21 -14
  24. package/esm/build-operation-for-field.js +13 -3
  25. package/esm/collectFields.js +2 -1
  26. package/esm/comments.js +1 -1
  27. package/esm/defaultValueAstFromType.js +28 -0
  28. package/esm/descriptionFromObject.js +7 -5
  29. package/esm/errors.js +29 -6
  30. package/esm/getArgumentValues.js +24 -16
  31. package/esm/getDirectiveValues.js +12 -0
  32. package/esm/heal.js +4 -1
  33. package/esm/helpers.js +39 -4
  34. package/esm/index.js +3 -4
  35. package/esm/jsutils.js +3 -5
  36. package/esm/mapSchema.js +2 -1
  37. package/esm/mergeDeep.js +35 -9
  38. package/esm/mergeIncrementalResult.js +25 -4
  39. package/esm/observableToAsyncIterable.js +28 -16
  40. package/esm/print-schema-with-directives.js +24 -18
  41. package/esm/registerAbortSignalListener.js +5 -1
  42. package/esm/stub.js +1 -0
  43. package/esm/updateArgument.js +2 -1
  44. package/esm/visitResult.js +7 -1
  45. package/package.json +3 -3
  46. package/typings/Interfaces.d.cts +39 -0
  47. package/typings/Interfaces.d.ts +39 -0
  48. package/typings/collectFields.d.cts +5 -6
  49. package/typings/collectFields.d.ts +5 -6
  50. package/typings/defaultValueAstFromType.d.cts +9 -0
  51. package/typings/defaultValueAstFromType.d.ts +9 -0
  52. package/typings/errors.d.cts +19 -1
  53. package/typings/errors.d.ts +19 -1
  54. package/typings/executor.d.cts +4 -7
  55. package/typings/executor.d.ts +4 -7
  56. package/typings/getDirectiveExtensions.d.cts +1 -3
  57. package/typings/getDirectiveExtensions.d.ts +1 -3
  58. package/typings/getDirectiveValues.d.cts +10 -0
  59. package/typings/getDirectiveValues.d.ts +10 -0
  60. package/typings/helpers.d.cts +28 -2
  61. package/typings/helpers.d.ts +28 -2
  62. package/typings/index.d.cts +3 -4
  63. package/typings/index.d.ts +3 -4
  64. package/typings/jsutils.d.cts +2 -2
  65. package/typings/jsutils.d.ts +2 -2
  66. package/typings/mergeDeep.d.cts +1 -1
  67. package/typings/mergeDeep.d.ts +1 -1
  68. package/typings/print-schema-with-directives.d.cts +1 -0
  69. package/typings/print-schema-with-directives.d.ts +1 -0
  70. package/typings/registerAbortSignalListener.d.cts +1 -1
  71. package/typings/registerAbortSignalListener.d.ts +1 -1
  72. package/typings/types.d.cts +25 -1
  73. package/typings/types.d.ts +25 -1
  74. package/cjs/createDeferred.js +0 -15
  75. package/cjs/fakePromise.js +0 -63
  76. package/cjs/map-maybe-promise.js +0 -26
  77. package/cjs/mapAsyncIterator.js +0 -75
  78. package/esm/createDeferred.js +0 -12
  79. package/esm/fakePromise.js +0 -59
  80. package/esm/map-maybe-promise.js +0 -23
  81. package/esm/mapAsyncIterator.js +0 -72
  82. package/typings/createDeferred.d.cts +0 -6
  83. package/typings/createDeferred.d.ts +0 -6
  84. package/typings/fakePromise.d.cts +0 -4
  85. package/typings/fakePromise.d.ts +0 -4
  86. package/typings/map-maybe-promise.d.cts +0 -2
  87. package/typings/map-maybe-promise.d.ts +0 -2
  88. package/typings/mapAsyncIterator.d.cts +0 -6
  89. package/typings/mapAsyncIterator.d.ts +0 -6
@@ -1,11 +1,23 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.observableToAsyncIterable = observableToAsyncIterable;
4
- const fakePromise_js_1 = require("./fakePromise.js");
4
+ const promise_helpers_1 = require("@whatwg-node/promise-helpers");
5
5
  function observableToAsyncIterable(observable) {
6
6
  const pullQueue = [];
7
7
  const pushQueue = [];
8
8
  let listening = true;
9
+ const subscriptionRef = {};
10
+ const emptyQueue = () => {
11
+ if (listening) {
12
+ listening = false;
13
+ subscriptionRef.current?.unsubscribe();
14
+ for (const resolve of pullQueue) {
15
+ resolve({ value: undefined, done: true });
16
+ }
17
+ pullQueue.length = 0;
18
+ pushQueue.length = 0;
19
+ }
20
+ };
9
21
  const pushValue = (value) => {
10
22
  if (pullQueue.length !== 0) {
11
23
  // It is safe to use the ! operator here as we check the length.
@@ -27,23 +39,29 @@ function observableToAsyncIterable(observable) {
27
39
  const pushDone = () => {
28
40
  if (pullQueue.length !== 0) {
29
41
  // It is safe to use the ! operator here as we check the length.
30
- pullQueue.shift()({ done: true });
42
+ pullQueue.shift()({ value: undefined, done: true });
43
+ // Release queues/subscription after signaling done to the pending consumer.
44
+ emptyQueue();
31
45
  }
32
46
  else {
33
- pushQueue.push({ done: true });
47
+ pushQueue.push({ value: undefined, done: true });
34
48
  }
35
49
  };
36
50
  const pullValue = () => new Promise(resolve => {
37
51
  if (pushQueue.length !== 0) {
38
52
  const element = pushQueue.shift();
39
53
  // either {value: {errors: [...]}} or {value: ...}
54
+ if (element.done) {
55
+ // Release references before delivering the done signal.
56
+ emptyQueue();
57
+ }
40
58
  resolve(element);
41
59
  }
42
60
  else {
43
61
  pullQueue.push(resolve);
44
62
  }
45
63
  });
46
- const subscription = observable.subscribe({
64
+ subscriptionRef.current = observable.subscribe({
47
65
  next(value) {
48
66
  return pushValue(value);
49
67
  },
@@ -54,17 +72,11 @@ function observableToAsyncIterable(observable) {
54
72
  return pushDone();
55
73
  },
56
74
  });
57
- const emptyQueue = () => {
58
- if (listening) {
59
- listening = false;
60
- subscription.unsubscribe();
61
- for (const resolve of pullQueue) {
62
- resolve({ value: undefined, done: true });
63
- }
64
- pullQueue.length = 0;
65
- pushQueue.length = 0;
66
- }
67
- };
75
+ // complete() may run synchronously inside subscribe() before it returns;
76
+ // unsubscribe the returned subscription in that case.
77
+ if (!listening) {
78
+ subscriptionRef.current.unsubscribe();
79
+ }
68
80
  return {
69
81
  next() {
70
82
  // return is a defined method, so it is safe to call it.
@@ -72,11 +84,11 @@ function observableToAsyncIterable(observable) {
72
84
  },
73
85
  return() {
74
86
  emptyQueue();
75
- return (0, fakePromise_js_1.fakePromise)({ value: undefined, done: true });
87
+ return (0, promise_helpers_1.fakePromise)({ value: undefined, done: true });
76
88
  },
77
89
  throw(error) {
78
90
  emptyQueue();
79
- return Promise.reject(error);
91
+ return (0, promise_helpers_1.fakeRejectPromise)(error);
80
92
  },
81
93
  [Symbol.asyncIterator]() {
82
94
  return this;
@@ -22,6 +22,7 @@ const graphql_1 = require("graphql");
22
22
  const astFromType_js_1 = require("./astFromType.js");
23
23
  const astFromValue_js_1 = require("./astFromValue.js");
24
24
  const astFromValueUntyped_js_1 = require("./astFromValueUntyped.js");
25
+ const defaultValueAstFromType_js_1 = require("./defaultValueAstFromType.js");
25
26
  const descriptionFromObject_js_1 = require("./descriptionFromObject.js");
26
27
  const get_directives_js_1 = require("./get-directives.js");
27
28
  const helpers_js_1 = require("./helpers.js");
@@ -116,6 +117,10 @@ function astFromSchema(schema, pathToDirectivesInExtensions) {
116
117
  });
117
118
  }
118
119
  }
120
+ else {
121
+ // Drop operation types that no longer exist on the schema (e.g. after pruneSchema).
122
+ operationTypeMap.set(operationTypeNode, undefined);
123
+ }
119
124
  }
120
125
  const operationTypes = [...operationTypeMap.values()].filter(helpers_js_1.isSome);
121
126
  const directives = getDirectiveNodes(schema, schema, pathToDirectivesInExtensions);
@@ -123,7 +128,7 @@ function astFromSchema(schema, pathToDirectivesInExtensions) {
123
128
  return null;
124
129
  }
125
130
  const schemaNode = {
126
- kind: operationTypes != null ? graphql_1.Kind.SCHEMA_DEFINITION : graphql_1.Kind.SCHEMA_EXTENSION,
131
+ kind: operationTypes.length ? graphql_1.Kind.SCHEMA_DEFINITION : graphql_1.Kind.SCHEMA_EXTENSION,
127
132
  operationTypes,
128
133
  // ConstXNode has been introduced in v16 but it is not compatible with XNode so we do `as any` for backwards compatibility
129
134
  directives: directives,
@@ -151,7 +156,7 @@ function astFromDirective(directive, schema, pathToDirectivesInExtensions) {
151
156
  };
152
157
  }
153
158
  function getDirectiveNodes(entity, schema, pathToDirectivesInExtensions) {
154
- let directiveNodesBesidesDeprecatedAndSpecifiedBy = [];
159
+ let directiveNodesBesidesNativeDirectives = [];
155
160
  const directivesInExtensions = (0, get_directives_js_1.getDirectivesInExtensions)(entity, pathToDirectivesInExtensions);
156
161
  let directives;
157
162
  if (directivesInExtensions != null) {
@@ -159,14 +164,12 @@ function getDirectiveNodes(entity, schema, pathToDirectivesInExtensions) {
159
164
  }
160
165
  let deprecatedDirectiveNode = null;
161
166
  let specifiedByDirectiveNode = null;
167
+ let oneOfDirectiveNode = null;
162
168
  if (directives != null) {
163
- directiveNodesBesidesDeprecatedAndSpecifiedBy = directives.filter(directive => directive.name.value !== 'deprecated' && directive.name.value !== 'specifiedBy');
164
- if (entity.deprecationReason != null) {
165
- deprecatedDirectiveNode = directives.filter(directive => directive.name.value === 'deprecated')?.[0];
166
- }
167
- if (entity.specifiedByUrl != null || entity.specifiedByURL != null) {
168
- specifiedByDirectiveNode = directives.filter(directive => directive.name.value === 'specifiedBy')?.[0];
169
- }
169
+ directiveNodesBesidesNativeDirectives = directives.filter(directive => graphql_1.specifiedDirectives.every(specifiedDirective => specifiedDirective.name !== directive.name.value));
170
+ deprecatedDirectiveNode = directives.find(directive => directive.name.value === 'deprecated');
171
+ specifiedByDirectiveNode = directives.find(directive => directive.name.value === 'specifiedBy');
172
+ oneOfDirectiveNode = directives.find(directive => directive.name.value === 'oneOf');
170
173
  }
171
174
  if (entity.deprecationReason != null && deprecatedDirectiveNode == null) {
172
175
  deprecatedDirectiveNode = makeDeprecatedDirective(entity.deprecationReason);
@@ -179,13 +182,19 @@ function getDirectiveNodes(entity, schema, pathToDirectivesInExtensions) {
179
182
  };
180
183
  specifiedByDirectiveNode = makeDirectiveNode('specifiedBy', specifiedByArgs);
181
184
  }
185
+ if (entity.isOneOf && oneOfDirectiveNode == null) {
186
+ oneOfDirectiveNode = makeDirectiveNode('oneOf');
187
+ }
182
188
  if (deprecatedDirectiveNode != null) {
183
- directiveNodesBesidesDeprecatedAndSpecifiedBy.push(deprecatedDirectiveNode);
189
+ directiveNodesBesidesNativeDirectives.push(deprecatedDirectiveNode);
184
190
  }
185
191
  if (specifiedByDirectiveNode != null) {
186
- directiveNodesBesidesDeprecatedAndSpecifiedBy.push(specifiedByDirectiveNode);
192
+ directiveNodesBesidesNativeDirectives.push(specifiedByDirectiveNode);
193
+ }
194
+ if (oneOfDirectiveNode != null) {
195
+ directiveNodesBesidesNativeDirectives.push(oneOfDirectiveNode);
187
196
  }
188
- return directiveNodesBesidesDeprecatedAndSpecifiedBy;
197
+ return directiveNodesBesidesNativeDirectives;
189
198
  }
190
199
  function astFromArg(arg, schema, pathToDirectivesInExtensions) {
191
200
  return {
@@ -196,10 +205,7 @@ function astFromArg(arg, schema, pathToDirectivesInExtensions) {
196
205
  value: arg.name,
197
206
  },
198
207
  type: (0, astFromType_js_1.astFromType)(arg.type),
199
- // ConstXNode has been introduced in v16 but it is not compatible with XNode so we do `as any` for backwards compatibility
200
- defaultValue: arg.defaultValue !== undefined
201
- ? ((0, astFromValue_js_1.astFromValue)(arg.defaultValue, arg.type) ?? undefined)
202
- : undefined,
208
+ defaultValue: (0, defaultValueAstFromType_js_1.defaultValueAstFromType)(arg),
203
209
  directives: getDirectiveNodes(arg, schema, pathToDirectivesInExtensions),
204
210
  };
205
211
  }
@@ -319,7 +325,7 @@ function astFromInputField(field, schema, pathToDirectivesInExtensions) {
319
325
  type: (0, astFromType_js_1.astFromType)(field.type),
320
326
  // ConstXNode has been introduced in v16 but it is not compatible with XNode so we do `as any` for backwards compatibility
321
327
  directives: getDirectiveNodes(field, schema, pathToDirectivesInExtensions),
322
- defaultValue: (0, astFromValue_js_1.astFromValue)(field.defaultValue, field.type) ?? undefined,
328
+ defaultValue: (0, defaultValueAstFromType_js_1.defaultValueAstFromType)(field),
323
329
  };
324
330
  }
325
331
  function astFromEnumValue(value, schema, pathToDirectivesInExtensions) {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getAbortPromise = void 0;
4
4
  exports.registerAbortSignalListener = registerAbortSignalListener;
5
+ const promise_helpers_1 = require("@whatwg-node/promise-helpers");
5
6
  const memoize_js_1 = require("./memoize.js");
6
7
  // AbortSignal handler cache to avoid the "possible EventEmitter memory leak detected"
7
8
  // on Node.js
@@ -29,8 +30,11 @@ function registerAbortSignalListener(signal, listener) {
29
30
  getListenersOfAbortSignal(signal).add(listener);
30
31
  }
31
32
  exports.getAbortPromise = (0, memoize_js_1.memoize1)(function getAbortPromise(signal) {
33
+ // If the signal is already aborted, return a rejected promise
34
+ if (signal.aborted) {
35
+ return (0, promise_helpers_1.fakeRejectPromise)(signal.reason);
36
+ }
32
37
  return new Promise((_resolve, reject) => {
33
- // If the signal is already aborted, return a rejected promise
34
38
  if (signal.aborted) {
35
39
  reject(signal.reason);
36
40
  return;
package/cjs/stub.js CHANGED
@@ -30,6 +30,7 @@ function createStub(node, type) {
30
30
  case graphql_1.Kind.LIST_TYPE:
31
31
  return new graphql_1.GraphQLList(createStub(node.type, type));
32
32
  case graphql_1.Kind.NON_NULL_TYPE:
33
+ // FIXME: graphql@17 type issue
33
34
  return new graphql_1.GraphQLNonNull(createStub(node.type, type));
34
35
  default:
35
36
  if (type === 'output') {
@@ -46,7 +46,8 @@ function createVariableNameGenerator(variableDefinitionMap) {
46
46
  return (argName) => {
47
47
  let varName;
48
48
  do {
49
- varName = `_v${(varCounter++).toString()}_${argName}`;
49
+ varName = varCounter === 0 ? argName : `_v${varCounter.toString()}_${argName}`;
50
+ varCounter++;
50
51
  } while (varName in variableDefinitionMap);
51
52
  return varName;
52
53
  };
@@ -34,7 +34,7 @@ function visitResult(result, request, schema, resultVisitorMap, errorVisitorMap)
34
34
  }
35
35
  return acc;
36
36
  }, {});
37
- const variableValues = request.variables || {};
37
+ const variableValues = normalizeVariableValues(request.variables);
38
38
  const errorInfo = {
39
39
  segmentInfoMap: new Map(),
40
40
  unpathedErrors: new Set(),
@@ -51,6 +51,12 @@ function visitResult(result, request, schema, resultVisitorMap, errorVisitorMap)
51
51
  }
52
52
  return result;
53
53
  }
54
+ function normalizeVariableValues(variables) {
55
+ return {
56
+ coerced: variables ?? {},
57
+ sources: {},
58
+ };
59
+ }
54
60
  function visitErrorsByType(errors, errorVisitorMap, errorInfo) {
55
61
  const segmentInfoMap = errorInfo.segmentInfoMap;
56
62
  const unpathedErrors = errorInfo.unpathedErrors;
@@ -1,4 +1,3 @@
1
- import { inspect } from 'cross-inspect';
2
1
  import { isEnumType, isInputObjectType, isLeafType, isListType, isNonNullType, Kind, } from 'graphql';
3
2
  import { astFromValueUntyped } from './astFromValueUntyped.js';
4
3
  import { isIterableObject, isObjectLike } from './jsutils.js';
@@ -78,24 +77,32 @@ export function astFromValue(value, type) {
78
77
  if (isLeafType(type)) {
79
78
  // Since value is an internally represented value, it must be serialized
80
79
  // to an externally represented value before converting into an AST.
81
- const serialized = type.serialize(value);
82
- if (serialized == null) {
83
- return null;
84
- }
85
80
  if (isEnumType(type)) {
86
- return { kind: Kind.ENUM, value: serialized };
81
+ const values = type.getValues();
82
+ for (const enumValue of values) {
83
+ if (enumValue.value === value) {
84
+ value = enumValue.name;
85
+ break;
86
+ }
87
+ if (enumValue.name === value) {
88
+ value = enumValue.name;
89
+ break;
90
+ }
91
+ }
92
+ return { kind: Kind.ENUM, value: value };
93
+ }
94
+ else {
95
+ value = type.serialize(value);
96
+ }
97
+ if (value == null) {
98
+ return null;
87
99
  }
88
100
  // ID types can use Int literals.
89
- if (type.name === 'ID' &&
90
- typeof serialized === 'string' &&
91
- integerStringRegExp.test(serialized)) {
92
- return { kind: Kind.INT, value: serialized };
101
+ if (type.name === 'ID' && typeof value === 'string' && integerStringRegExp.test(value)) {
102
+ return { kind: Kind.INT, value };
93
103
  }
94
- return astFromValueUntyped(serialized);
95
104
  }
96
- /* c8 ignore next 3 */
97
- // Not reachable, all possible types have been considered.
98
- console.assert(false, 'Unexpected input type: ' + inspect(type));
105
+ return astFromValueUntyped(value);
99
106
  }
100
107
  /**
101
108
  * IntValue:
@@ -1,4 +1,6 @@
1
1
  import { getNamedType, isEnumType, isInterfaceType, isListType, isNonNullType, isObjectType, isScalarType, isUnionType, Kind, } from 'graphql';
2
+ import { astFromValueUntyped } from './astFromValueUntyped.js';
3
+ import { defaultValueAstFromType } from './defaultValueAstFromType.js';
2
4
  import { getDefinedRootType, getRootTypeNames } from './rootTypes.js';
3
5
  let operationVariables = [];
4
6
  let fieldTypeMap = new Map();
@@ -238,6 +240,13 @@ function resolveVariable(arg, name) {
238
240
  },
239
241
  };
240
242
  }
243
+ let defaultValue;
244
+ try {
245
+ defaultValue = defaultValueAstFromType(arg);
246
+ }
247
+ catch (e) {
248
+ defaultValue = astFromValueUntyped(arg.defaultValue) ?? undefined;
249
+ }
241
250
  return {
242
251
  kind: Kind.VARIABLE_DEFINITION,
243
252
  variable: {
@@ -248,6 +257,7 @@ function resolveVariable(arg, name) {
248
257
  },
249
258
  },
250
259
  type: resolveVariableType(arg.type),
260
+ defaultValue,
251
261
  };
252
262
  }
253
263
  function getArgumentName(name, path) {
@@ -296,9 +306,9 @@ function resolveField({ type, field, models, firstCall, path, ancestors, ignore,
296
306
  if (fieldTypeMap.has(fieldPathStr) && fieldTypeMap.get(fieldPathStr) !== field.type.toString()) {
297
307
  fieldName += field.type
298
308
  .toString()
299
- .replace('!', 'NonNull')
300
- .replace('[', 'List')
301
- .replace(']', '');
309
+ .replace(/!/g, 'NonNull')
310
+ .replace(/\[/g, 'List')
311
+ .replace(/\]/g, '');
302
312
  }
303
313
  fieldTypeMap.set(fieldPathStr, field.type.toString());
304
314
  if (!isScalarType(namedType) && !isEnumType(namedType)) {
@@ -1,6 +1,7 @@
1
- import { getDirectiveValues, GraphQLIncludeDirective, GraphQLSkipDirective, isAbstractType, Kind, typeFromAST, } from 'graphql';
1
+ import { GraphQLIncludeDirective, GraphQLSkipDirective, isAbstractType, Kind, typeFromAST, } from 'graphql';
2
2
  import { AccumulatorMap } from './AccumulatorMap.js';
3
3
  import { GraphQLDeferDirective } from './directives.js';
4
+ import { getDirectiveValues } from './getDirectiveValues.js';
4
5
  import { memoize5 } from './memoize.js';
5
6
  function collectFieldsImpl(schema, fragments, variableValues, runtimeType, selectionSet, fields, patches, visitedFragmentNames) {
6
7
  for (const selection of selectionSet.selections) {
package/esm/comments.js CHANGED
@@ -115,7 +115,7 @@ function wrap(start, maybeString, end) {
115
115
  * a single-line, adding a leading blank line would strip that whitespace.
116
116
  */
117
117
  function printBlockString(value, isDescription = false) {
118
- const escaped = value.replace(/"""/g, '\\"""');
118
+ const escaped = value.replace(/\\/g, '\\\\').replace(/"""/g, '\\"""');
119
119
  return (value[0] === ' ' || value[0] === '\t') && value.indexOf('\n') === -1
120
120
  ? `"""${escaped.replace(/"$/, '"\n')}"""`
121
121
  : `"""\n${isDescription ? escaped : indent(escaped)}\n"""`;
@@ -0,0 +1,28 @@
1
+ import { valueFromASTUntyped, } from 'graphql';
2
+ import { astFromValue } from './astFromValue.js';
3
+ import { astFromValueUntyped } from './astFromValueUntyped.js';
4
+ /**
5
+ * `defaultValueAstFromType` extracts default value from `GraphQLArgument` or `GraphQLInputField`, if available
6
+ * This is compatible with all `graphql` versions
7
+ *
8
+ * The return type is `ConstValueNode` in graphql@16+,
9
+ * but it is not available in graphql@15 so `ValueNode` is used as return type here and `as any` is often required at callsites for backwards compatibility,
10
+ */
11
+ export const defaultValueAstFromType = (arg) => {
12
+ // graphql >= v17 has `default` instead of `defaultValue`
13
+ // So for backward compatibility with v16, we are using `arg.default as any`, otherwise, TypeScript report type error
14
+ if ('default' in arg) {
15
+ if (!arg.default) {
16
+ return undefined;
17
+ }
18
+ if ('value' in arg.default) {
19
+ return astFromValueUntyped(arg.default.value) ?? undefined;
20
+ }
21
+ const value = valueFromASTUntyped(arg.default.literal);
22
+ return astFromValue(value, arg.type) ?? undefined;
23
+ }
24
+ // graphql < v17 has `defaultValue` instead of `default`
25
+ return arg.defaultValue !== undefined
26
+ ? (astFromValue(arg.defaultValue, arg.type) ?? undefined)
27
+ : undefined;
28
+ };
@@ -1,15 +1,17 @@
1
1
  import { Kind } from 'graphql';
2
2
  export function getDescriptionNode(obj) {
3
- if (obj.astNode?.description) {
3
+ // Prefer the runtime `description` over a stale `astNode` description.
4
+ // Schema transforms (e.g. mapSchema) update config descriptions without rewriting astNode (#5508).
5
+ if (typeof obj.description === 'string') {
4
6
  return {
5
- ...obj.astNode.description,
7
+ kind: Kind.STRING,
8
+ value: obj.description,
6
9
  block: true,
7
10
  };
8
11
  }
9
- if (obj.description) {
12
+ if (obj.astNode?.description) {
10
13
  return {
11
- kind: Kind.STRING,
12
- value: obj.description,
14
+ ...obj.astNode.description,
13
15
  block: true,
14
16
  };
15
17
  }
package/esm/errors.js CHANGED
@@ -1,4 +1,4 @@
1
- import { GraphQLError, versionInfo } from 'graphql';
1
+ import { locatedError as _locatedError, GraphQLError, versionInfo } from 'graphql';
2
2
  const possibleGraphQLErrorProperties = [
3
3
  'message',
4
4
  'locations',
@@ -10,8 +10,9 @@ const possibleGraphQLErrorProperties = [
10
10
  'name',
11
11
  'stack',
12
12
  'extensions',
13
+ 'coordinate',
13
14
  ];
14
- function isGraphQLErrorLike(error) {
15
+ export function isGraphQLErrorLike(error) {
15
16
  return (error != null &&
16
17
  typeof error === 'object' &&
17
18
  Object.keys(error).every(key => possibleGraphQLErrorProperties.includes(key)));
@@ -22,12 +23,33 @@ export function createGraphQLError(message, options) {
22
23
  isGraphQLErrorLike(options.originalError)) {
23
24
  options.originalError = createGraphQLError(options.originalError.message, options.originalError);
24
25
  }
25
- if (versionInfo.major >= 17) {
26
- return new GraphQLError(message, options);
26
+ // To avoid type error on graphql <16, we have to use an any type here
27
+ const Constructor = GraphQLError;
28
+ const error = versionInfo.major >= 16
29
+ ? new Constructor(message, options)
30
+ : new Constructor(message, options?.nodes, options?.source, options?.positions, options?.path, options?.originalError, options?.extensions);
31
+ if (options?.coordinate && error.coordinate == null) {
32
+ Object.defineProperties(error, {
33
+ coordinate: { value: options.coordinate, enumerable: true, configurable: true },
34
+ });
27
35
  }
28
- return new GraphQLError(message, options?.nodes, options?.source, options?.positions, options?.path, options?.originalError, options?.extensions);
36
+ return error;
29
37
  }
30
- export function relocatedError(originalError, path) {
38
+ export function getSchemaCoordinate(error) {
39
+ return error.coordinate;
40
+ }
41
+ export function locatedError(rawError, nodes, path, info) {
42
+ const error = _locatedError(rawError, nodes, path);
43
+ // `graphql` locatedError is only changing path and nodes if it is not already defined
44
+ if (!error.coordinate && info && error.coordinate == null) {
45
+ const coordinate = `${info.parentType.name}.${info.fieldName}`;
46
+ Object.defineProperties(error, {
47
+ coordinate: { value: coordinate, enumerable: true, configurable: true },
48
+ });
49
+ }
50
+ return error;
51
+ }
52
+ export function relocatedError(originalError, path, info) {
31
53
  return createGraphQLError(originalError.message, {
32
54
  nodes: originalError.nodes,
33
55
  source: originalError.source,
@@ -35,5 +57,6 @@ export function relocatedError(originalError, path) {
35
57
  path: path == null ? originalError.path : path,
36
58
  originalError,
37
59
  extensions: originalError.extensions,
60
+ coordinate: info ? `${info.parentType.name}.${info.fieldName}` : undefined,
38
61
  });
39
62
  }
@@ -1,5 +1,5 @@
1
1
  import { inspect } from 'cross-inspect';
2
- import { isNonNullType, Kind, print, valueFromAST, } from 'graphql';
2
+ import { isNonNullType, Kind, print, valueFromAST, valueFromASTUntyped, } from 'graphql';
3
3
  import { createGraphQLError } from './errors.js';
4
4
  import { hasOwnProperty } from './jsutils.js';
5
5
  /**
@@ -17,14 +17,22 @@ export function getArgumentValues(def, node, variableValues = {}) {
17
17
  ...prev,
18
18
  [arg.name.value]: arg,
19
19
  }), {});
20
- for (const { name, type: argType, defaultValue } of def.args) {
21
- const argumentNode = argNodeMap[name];
20
+ for (const arg of def.args) {
21
+ const argumentNode = argNodeMap[arg.name];
22
22
  if (!argumentNode) {
23
- if (defaultValue !== undefined) {
24
- coercedValues[name] = defaultValue;
23
+ if ('default' in arg && arg.default) {
24
+ // graphql v17
25
+ coercedValues[arg.name] =
26
+ 'value' in arg.default
27
+ ? arg.default.value
28
+ : valueFromASTUntyped(arg.default.literal);
25
29
  }
26
- else if (isNonNullType(argType)) {
27
- throw createGraphQLError(`Argument "${name}" of required type "${inspect(argType)}" ` + 'was not provided.', {
30
+ else if (arg.defaultValue !== undefined) {
31
+ // graphql < v17
32
+ coercedValues[arg.name] = arg.defaultValue;
33
+ }
34
+ else if (isNonNullType(arg.type)) {
35
+ throw createGraphQLError(`Argument "${arg.name}" of required type "${inspect(arg.type)}" ` + 'was not provided.', {
28
36
  nodes: [node],
29
37
  });
30
38
  }
@@ -35,11 +43,11 @@ export function getArgumentValues(def, node, variableValues = {}) {
35
43
  if (valueNode.kind === Kind.VARIABLE) {
36
44
  const variableName = valueNode.name.value;
37
45
  if (variableValues == null || !hasOwnProperty(variableValues, variableName)) {
38
- if (defaultValue !== undefined) {
39
- coercedValues[name] = defaultValue;
46
+ if (arg.defaultValue !== undefined) {
47
+ coercedValues[arg.name] = arg.defaultValue;
40
48
  }
41
- else if (isNonNullType(argType)) {
42
- throw createGraphQLError(`Argument "${name}" of required type "${inspect(argType)}" ` +
49
+ else if (isNonNullType(arg.type)) {
50
+ throw createGraphQLError(`Argument "${arg.name}" of required type "${inspect(arg.type)}" ` +
43
51
  `was provided the variable "$${variableName}" which was not provided a runtime value.`, {
44
52
  nodes: [valueNode],
45
53
  });
@@ -48,21 +56,21 @@ export function getArgumentValues(def, node, variableValues = {}) {
48
56
  }
49
57
  isNull = variableValues[variableName] == null;
50
58
  }
51
- if (isNull && isNonNullType(argType)) {
52
- throw createGraphQLError(`Argument "${name}" of non-null type "${inspect(argType)}" ` + 'must not be null.', {
59
+ if (isNull && isNonNullType(arg.type)) {
60
+ throw createGraphQLError(`Argument "${arg.name}" of non-null type "${inspect(arg.type)}" ` + 'must not be null.', {
53
61
  nodes: [valueNode],
54
62
  });
55
63
  }
56
- const coercedValue = valueFromAST(valueNode, argType, variableValues);
64
+ const coercedValue = valueFromAST(valueNode, arg.type, variableValues);
57
65
  if (coercedValue === undefined) {
58
66
  // Note: ValuesOfCorrectTypeRule validation should catch this before
59
67
  // execution. This is a runtime check to ensure execution does not
60
68
  // continue with an invalid argument value.
61
- throw createGraphQLError(`Argument "${name}" has invalid value ${print(valueNode)}.`, {
69
+ throw createGraphQLError(`Argument "${arg.name}" has invalid value ${print(valueNode)}.`, {
62
70
  nodes: [valueNode],
63
71
  });
64
72
  }
65
- coercedValues[name] = coercedValue;
73
+ coercedValues[arg.name] = coercedValue;
66
74
  }
67
75
  return coercedValues;
68
76
  }
@@ -0,0 +1,12 @@
1
+ import { getDirectiveValues as getDirectiveValuesOrig, versionInfo, } from 'graphql';
2
+ /**
3
+ * GraphQL v17's getDirectiveValues function accepts `VariableValues`
4
+ * while older versions accept `Record<string, any>`. This function wraps the original
5
+ * getDirectiveValues function to provide compatibility across versions.
6
+ */
7
+ export function getDirectiveValues(directiveDef, node, variableValues) {
8
+ if (versionInfo.major >= 17) {
9
+ return getDirectiveValuesOrig(directiveDef, node, variableValues);
10
+ }
11
+ return getDirectiveValuesOrig(directiveDef, node, variableValues?.coerced);
12
+ }
package/esm/heal.js CHANGED
@@ -154,7 +154,10 @@ export function healTypes(originalTypeMap, directives) {
154
154
  }
155
155
  else if (isNonNullType(type)) {
156
156
  const healedType = healType(type.ofType);
157
- return healedType != null ? new GraphQLNonNull(healedType) : null;
157
+ return healedType != null
158
+ ? // FIXME: `healedType` needs `as any` as graphql@17 changes the type for `GraphQLType`
159
+ new GraphQLNonNull(healedType)
160
+ : null;
158
161
  }
159
162
  else if (isNamedType(type)) {
160
163
  // If a type annotation on a field or an argument or a union member is