@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
package/esm/helpers.js CHANGED
@@ -1,5 +1,25 @@
1
1
  import { parse } from 'graphql';
2
- function isURL(str) {
2
+ const URL_REGEXP = /^(https?|wss?|file):\/\//;
3
+ /**
4
+ * Checks if the given string is a valid URL.
5
+ *
6
+ * @param str - The string to validate as a URL
7
+ * @returns A boolean indicating whether the string is a valid URL
8
+ *
9
+ * @remarks
10
+ * This function first attempts to use the `URL.canParse` method if available.
11
+ * If not, it falls back to creating a new `URL` object to validate the string.
12
+ */
13
+ export function isUrl(str) {
14
+ if (typeof str !== 'string') {
15
+ return false;
16
+ }
17
+ if (!URL_REGEXP.test(str)) {
18
+ return false;
19
+ }
20
+ if (URL.canParse) {
21
+ return URL.canParse(str);
22
+ }
3
23
  try {
4
24
  const url = new URL(str);
5
25
  return !!url;
@@ -10,6 +30,21 @@ function isURL(str) {
10
30
  }
11
31
  export const asArray = (fns) => (Array.isArray(fns) ? fns : fns ? [fns] : []);
12
32
  const invalidDocRegex = /\.[a-z0-9]+$/i;
33
+ /**
34
+ * Determines if a given input is a valid GraphQL document string.
35
+ *
36
+ * @param str - The input to validate as a GraphQL document
37
+ * @returns A boolean indicating whether the input is a valid GraphQL document string
38
+ *
39
+ * @remarks
40
+ * This function performs several validation checks:
41
+ * - Ensures the input is a string
42
+ * - Filters out strings with invalid document extensions
43
+ * - Excludes URLs
44
+ * - Attempts to parse the string as a GraphQL document
45
+ *
46
+ * @throws {Error} If the document fails to parse and is empty except GraphQL comments
47
+ */
13
48
  export function isDocumentString(str) {
14
49
  if (typeof str !== 'string') {
15
50
  return false;
@@ -19,7 +54,7 @@ export function isDocumentString(str) {
19
54
  // this why checking the extension is fast enough
20
55
  // and prevent from parsing the string in order to find out
21
56
  // if the string is a SDL
22
- if (invalidDocRegex.test(str) || isURL(str)) {
57
+ if (invalidDocRegex.test(str) || isUrl(str)) {
23
58
  return false;
24
59
  }
25
60
  try {
@@ -35,9 +70,9 @@ export function isDocumentString(str) {
35
70
  }
36
71
  return false;
37
72
  }
38
- const invalidPathRegex = /[‘“!%^<>`\n]/;
73
+ const invalidPathRegex = /[‘“!^<>`\n]/;
39
74
  /**
40
- * Checkes whether the `str` contains any path illegal characters.
75
+ * Checks whether the `str` contains any path illegal characters.
41
76
  *
42
77
  * A string may sometimes look like a path but is not (like an SDL of a simple
43
78
  * GraphQL schema). To make sure we don't yield false-positives in such cases,
package/esm/index.js CHANGED
@@ -28,8 +28,8 @@ export * from './getResponseKeyFromInfo.js';
28
28
  export * from './fields.js';
29
29
  export * from './renameType.js';
30
30
  export * from './transformInputValue.js';
31
- export * from './mapAsyncIterator.js';
32
31
  export * from './updateArgument.js';
32
+ export * from './astFromType.js';
33
33
  export * from './implementsAbstractType.js';
34
34
  export * from './errors.js';
35
35
  export * from './observableToAsyncIterable.js';
@@ -55,7 +55,6 @@ export * from './directives.js';
55
55
  export * from './mergeIncrementalResult.js';
56
56
  export * from './debugTimer.js';
57
57
  export * from './getDirectiveExtensions.js';
58
- export * from './map-maybe-promise.js';
59
- export * from './fakePromise.js';
60
- export * from './createDeferred.js';
58
+ export { mapAsyncIterator, mapMaybePromise, fakePromise, createDeferredPromise as createDeferred, } from '@whatwg-node/promise-helpers';
61
59
  export * from './registerAbortSignalListener.js';
60
+ export * from './getDirectiveValues.js';
package/esm/jsutils.js CHANGED
@@ -1,17 +1,15 @@
1
- import { mapMaybePromise } from './map-maybe-promise.js';
1
+ import { handleMaybePromise, isPromise } from '@whatwg-node/promise-helpers';
2
2
  export function isIterableObject(value) {
3
3
  return value != null && typeof value === 'object' && Symbol.iterator in value;
4
4
  }
5
5
  export function isObjectLike(value) {
6
6
  return typeof value === 'object' && value !== null;
7
7
  }
8
- export function isPromise(value) {
9
- return value?.then != null;
10
- }
8
+ export { isPromise };
11
9
  export function promiseReduce(values, callbackFn, initialValue) {
12
10
  let accumulator = initialValue;
13
11
  for (const value of values) {
14
- accumulator = mapMaybePromise(accumulator, resolved => callbackFn(resolved, value));
12
+ accumulator = handleMaybePromise(() => accumulator, resolved => callbackFn(resolved, value));
15
13
  }
16
14
  return accumulator;
17
15
  }
package/esm/mapSchema.js CHANGED
@@ -17,10 +17,11 @@ export function mapSchema(schema, schemaMapper = {}) {
17
17
  directives,
18
18
  });
19
19
  }
20
+ const builtinTypes = ['String', 'ID', 'Int', 'Float', 'Boolean'];
20
21
  function mapTypes(originalTypeMap, schema, schemaMapper, testFn = () => true) {
21
22
  const newTypeMap = {};
22
23
  for (const typeName in originalTypeMap) {
23
- if (!typeName.startsWith('__')) {
24
+ if (!typeName.startsWith('__') && !builtinTypes.includes(typeName)) {
24
25
  const originalType = originalTypeMap[typeName];
25
26
  if (originalType == null || !testFn(originalType)) {
26
27
  newTypeMap[typeName] = originalType;
package/esm/mergeDeep.js CHANGED
@@ -1,5 +1,11 @@
1
1
  import { isSome } from './helpers.js';
2
- export function mergeDeep(sources, respectPrototype = false, respectArrays = false, respectArrayLength = false) {
2
+ export function mergeDeep(sources, respectPrototype = false, respectArrays = false, respectArrayLength = false, respectNonEnumerableSymbols = false) {
3
+ if (sources.length === 0) {
4
+ return;
5
+ }
6
+ if (sources.length === 1) {
7
+ return sources[0];
8
+ }
3
9
  let expectedLength;
4
10
  let allArrays = true;
5
11
  const areArraysInTheSameLength = sources.every(source => {
@@ -18,7 +24,7 @@ export function mergeDeep(sources, respectPrototype = false, respectArrays = fal
18
24
  return false;
19
25
  });
20
26
  if (respectArrayLength && areArraysInTheSameLength) {
21
- return new Array(expectedLength).fill(null).map((_, index) => mergeDeep(sources.map(source => source[index]), respectPrototype, respectArrays, respectArrayLength));
27
+ return new Array(expectedLength).fill(null).map((_, index) => mergeDeep(sources.map(source => source[index]), respectPrototype, respectArrays, respectArrayLength, respectNonEnumerableSymbols));
22
28
  }
23
29
  if (allArrays) {
24
30
  return sources.flat(1);
@@ -27,15 +33,21 @@ export function mergeDeep(sources, respectPrototype = false, respectArrays = fal
27
33
  let firstObjectSource;
28
34
  if (respectPrototype) {
29
35
  firstObjectSource = sources.find(source => isObject(source));
30
- if (output == null) {
31
- output = {};
32
- }
33
36
  if (firstObjectSource) {
37
+ if (output == null) {
38
+ output = {};
39
+ }
34
40
  Object.setPrototypeOf(output, Object.create(Object.getPrototypeOf(firstObjectSource)));
35
41
  }
36
42
  }
37
43
  for (const source of sources) {
44
+ if (source === undefined) {
45
+ continue;
46
+ }
38
47
  if (isObject(source)) {
48
+ if (output == null) {
49
+ output = {};
50
+ }
39
51
  if (firstObjectSource) {
40
52
  const outputPrototype = Object.getPrototypeOf(output);
41
53
  const sourcePrototype = Object.getPrototypeOf(source);
@@ -49,16 +61,30 @@ export function mergeDeep(sources, respectPrototype = false, respectArrays = fal
49
61
  }
50
62
  }
51
63
  for (const key in source) {
52
- if (output == null) {
53
- output = {};
64
+ // never let a source key reach the prototype chain
65
+ if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
66
+ continue;
54
67
  }
55
- if (key in output) {
56
- output[key] = mergeDeep([output[key], source[key]], respectPrototype, respectArrays, respectArrayLength);
68
+ // An own key present with value `undefined` is an explicit override and must win,
69
+ // distinct from the key being altogether absent from `source`. Assigning directly
70
+ // (rather than recursing through mergeDeep) avoids the top-level `source === undefined`
71
+ // skip above, which would otherwise silently discard this override and keep the prior
72
+ // value.
73
+ if (Object.prototype.hasOwnProperty.call(output, key) && source[key] !== undefined) {
74
+ output[key] = mergeDeep([output[key], source[key]], respectPrototype, respectArrays, respectArrayLength, respectNonEnumerableSymbols);
57
75
  }
58
76
  else {
59
77
  output[key] = source[key];
60
78
  }
61
79
  }
80
+ if (respectNonEnumerableSymbols && output != null) {
81
+ for (const sym of Object.getOwnPropertySymbols(source)) {
82
+ const descriptor = Object.getOwnPropertyDescriptor(source, sym);
83
+ if (!descriptor.enumerable) {
84
+ Object.defineProperty(output, sym, descriptor);
85
+ }
86
+ }
87
+ }
62
88
  }
63
89
  else if (Array.isArray(source)) {
64
90
  if (!Array.isArray(output)) {
@@ -1,22 +1,22 @@
1
- import { dset } from 'dset/merge';
1
+ import { mergeDeep } from './mergeDeep.js';
2
2
  export function mergeIncrementalResult({ incrementalResult, executionResult, }) {
3
3
  const path = ['data', ...(incrementalResult.path ?? [])];
4
4
  if (incrementalResult.items) {
5
5
  for (const item of incrementalResult.items) {
6
- dset(executionResult, path, item);
6
+ setObjectKeyPath(executionResult, path, item);
7
7
  // Increment the last path segment (the array index) to merge the next item at the next index
8
8
  path[path.length - 1]++;
9
9
  }
10
10
  }
11
11
  if (incrementalResult.data) {
12
- dset(executionResult, path, incrementalResult.data);
12
+ setObjectKeyPath(executionResult, path, incrementalResult.data);
13
13
  }
14
14
  if (incrementalResult.errors) {
15
15
  executionResult.errors = executionResult.errors || [];
16
16
  executionResult.errors.push(...incrementalResult.errors);
17
17
  }
18
18
  if (incrementalResult.extensions) {
19
- dset(executionResult, 'extensions', incrementalResult.extensions);
19
+ setObjectKeyPath(executionResult, ['extensions'], incrementalResult.extensions);
20
20
  }
21
21
  if (incrementalResult.incremental) {
22
22
  incrementalResult.incremental.forEach(incrementalSubResult => {
@@ -27,3 +27,24 @@ export function mergeIncrementalResult({ incrementalResult, executionResult, })
27
27
  });
28
28
  }
29
29
  }
30
+ function setObjectKeyPath(obj, keyPath, value) {
31
+ let current = obj;
32
+ let i;
33
+ for (i = 0; i < keyPath.length - 1; i++) {
34
+ const key = keyPath[i];
35
+ if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
36
+ return;
37
+ }
38
+ if (current[key] == null) {
39
+ // Determine if the next key is a number to create an array, otherwise create an object
40
+ current[key] = typeof keyPath[i + 1] === 'number' ? [] : {};
41
+ }
42
+ current = current[key];
43
+ }
44
+ const finalKey = keyPath[i];
45
+ if (finalKey === '__proto__' || finalKey === 'constructor' || finalKey === 'prototype') {
46
+ return;
47
+ }
48
+ const existingValue = current[finalKey];
49
+ current[finalKey] = existingValue != null ? mergeDeep([existingValue, value]) : value;
50
+ }
@@ -1,8 +1,20 @@
1
- import { fakePromise } from './fakePromise.js';
1
+ import { fakePromise, fakeRejectPromise } from '@whatwg-node/promise-helpers';
2
2
  export function observableToAsyncIterable(observable) {
3
3
  const pullQueue = [];
4
4
  const pushQueue = [];
5
5
  let listening = true;
6
+ const subscriptionRef = {};
7
+ const emptyQueue = () => {
8
+ if (listening) {
9
+ listening = false;
10
+ subscriptionRef.current?.unsubscribe();
11
+ for (const resolve of pullQueue) {
12
+ resolve({ value: undefined, done: true });
13
+ }
14
+ pullQueue.length = 0;
15
+ pushQueue.length = 0;
16
+ }
17
+ };
6
18
  const pushValue = (value) => {
7
19
  if (pullQueue.length !== 0) {
8
20
  // It is safe to use the ! operator here as we check the length.
@@ -24,23 +36,29 @@ export function observableToAsyncIterable(observable) {
24
36
  const pushDone = () => {
25
37
  if (pullQueue.length !== 0) {
26
38
  // It is safe to use the ! operator here as we check the length.
27
- pullQueue.shift()({ done: true });
39
+ pullQueue.shift()({ value: undefined, done: true });
40
+ // Release queues/subscription after signaling done to the pending consumer.
41
+ emptyQueue();
28
42
  }
29
43
  else {
30
- pushQueue.push({ done: true });
44
+ pushQueue.push({ value: undefined, done: true });
31
45
  }
32
46
  };
33
47
  const pullValue = () => new Promise(resolve => {
34
48
  if (pushQueue.length !== 0) {
35
49
  const element = pushQueue.shift();
36
50
  // either {value: {errors: [...]}} or {value: ...}
51
+ if (element.done) {
52
+ // Release references before delivering the done signal.
53
+ emptyQueue();
54
+ }
37
55
  resolve(element);
38
56
  }
39
57
  else {
40
58
  pullQueue.push(resolve);
41
59
  }
42
60
  });
43
- const subscription = observable.subscribe({
61
+ subscriptionRef.current = observable.subscribe({
44
62
  next(value) {
45
63
  return pushValue(value);
46
64
  },
@@ -51,17 +69,11 @@ export function observableToAsyncIterable(observable) {
51
69
  return pushDone();
52
70
  },
53
71
  });
54
- const emptyQueue = () => {
55
- if (listening) {
56
- listening = false;
57
- subscription.unsubscribe();
58
- for (const resolve of pullQueue) {
59
- resolve({ value: undefined, done: true });
60
- }
61
- pullQueue.length = 0;
62
- pushQueue.length = 0;
63
- }
64
- };
72
+ // complete() may run synchronously inside subscribe() before it returns;
73
+ // unsubscribe the returned subscription in that case.
74
+ if (!listening) {
75
+ subscriptionRef.current.unsubscribe();
76
+ }
65
77
  return {
66
78
  next() {
67
79
  // return is a defined method, so it is safe to call it.
@@ -73,7 +85,7 @@ export function observableToAsyncIterable(observable) {
73
85
  },
74
86
  throw(error) {
75
87
  emptyQueue();
76
- return Promise.reject(error);
88
+ return fakeRejectPromise(error);
77
89
  },
78
90
  [Symbol.asyncIterator]() {
79
91
  return this;
@@ -1,7 +1,8 @@
1
- import { GraphQLDeprecatedDirective, isEnumType, isInputObjectType, isInterfaceType, isIntrospectionType, isObjectType, isScalarType, isSpecifiedDirective, isSpecifiedScalarType, isUnionType, Kind, print, } from 'graphql';
1
+ import { GraphQLDeprecatedDirective, isEnumType, isInputObjectType, isInterfaceType, isIntrospectionType, isObjectType, isScalarType, isSpecifiedDirective, isSpecifiedScalarType, isUnionType, Kind, print, specifiedDirectives, } from 'graphql';
2
2
  import { astFromType } from './astFromType.js';
3
3
  import { astFromValue } from './astFromValue.js';
4
4
  import { astFromValueUntyped } from './astFromValueUntyped.js';
5
+ import { defaultValueAstFromType } from './defaultValueAstFromType.js';
5
6
  import { getDescriptionNode } from './descriptionFromObject.js';
6
7
  import { getDirectivesInExtensions, } from './get-directives.js';
7
8
  import { isSome } from './helpers.js';
@@ -96,6 +97,10 @@ export function astFromSchema(schema, pathToDirectivesInExtensions) {
96
97
  });
97
98
  }
98
99
  }
100
+ else {
101
+ // Drop operation types that no longer exist on the schema (e.g. after pruneSchema).
102
+ operationTypeMap.set(operationTypeNode, undefined);
103
+ }
99
104
  }
100
105
  const operationTypes = [...operationTypeMap.values()].filter(isSome);
101
106
  const directives = getDirectiveNodes(schema, schema, pathToDirectivesInExtensions);
@@ -103,7 +108,7 @@ export function astFromSchema(schema, pathToDirectivesInExtensions) {
103
108
  return null;
104
109
  }
105
110
  const schemaNode = {
106
- kind: operationTypes != null ? Kind.SCHEMA_DEFINITION : Kind.SCHEMA_EXTENSION,
111
+ kind: operationTypes.length ? Kind.SCHEMA_DEFINITION : Kind.SCHEMA_EXTENSION,
107
112
  operationTypes,
108
113
  // ConstXNode has been introduced in v16 but it is not compatible with XNode so we do `as any` for backwards compatibility
109
114
  directives: directives,
@@ -131,7 +136,7 @@ export function astFromDirective(directive, schema, pathToDirectivesInExtensions
131
136
  };
132
137
  }
133
138
  export function getDirectiveNodes(entity, schema, pathToDirectivesInExtensions) {
134
- let directiveNodesBesidesDeprecatedAndSpecifiedBy = [];
139
+ let directiveNodesBesidesNativeDirectives = [];
135
140
  const directivesInExtensions = getDirectivesInExtensions(entity, pathToDirectivesInExtensions);
136
141
  let directives;
137
142
  if (directivesInExtensions != null) {
@@ -139,14 +144,12 @@ export function getDirectiveNodes(entity, schema, pathToDirectivesInExtensions)
139
144
  }
140
145
  let deprecatedDirectiveNode = null;
141
146
  let specifiedByDirectiveNode = null;
147
+ let oneOfDirectiveNode = null;
142
148
  if (directives != null) {
143
- directiveNodesBesidesDeprecatedAndSpecifiedBy = directives.filter(directive => directive.name.value !== 'deprecated' && directive.name.value !== 'specifiedBy');
144
- if (entity.deprecationReason != null) {
145
- deprecatedDirectiveNode = directives.filter(directive => directive.name.value === 'deprecated')?.[0];
146
- }
147
- if (entity.specifiedByUrl != null || entity.specifiedByURL != null) {
148
- specifiedByDirectiveNode = directives.filter(directive => directive.name.value === 'specifiedBy')?.[0];
149
- }
149
+ directiveNodesBesidesNativeDirectives = directives.filter(directive => specifiedDirectives.every(specifiedDirective => specifiedDirective.name !== directive.name.value));
150
+ deprecatedDirectiveNode = directives.find(directive => directive.name.value === 'deprecated');
151
+ specifiedByDirectiveNode = directives.find(directive => directive.name.value === 'specifiedBy');
152
+ oneOfDirectiveNode = directives.find(directive => directive.name.value === 'oneOf');
150
153
  }
151
154
  if (entity.deprecationReason != null && deprecatedDirectiveNode == null) {
152
155
  deprecatedDirectiveNode = makeDeprecatedDirective(entity.deprecationReason);
@@ -159,13 +162,19 @@ export function getDirectiveNodes(entity, schema, pathToDirectivesInExtensions)
159
162
  };
160
163
  specifiedByDirectiveNode = makeDirectiveNode('specifiedBy', specifiedByArgs);
161
164
  }
165
+ if (entity.isOneOf && oneOfDirectiveNode == null) {
166
+ oneOfDirectiveNode = makeDirectiveNode('oneOf');
167
+ }
162
168
  if (deprecatedDirectiveNode != null) {
163
- directiveNodesBesidesDeprecatedAndSpecifiedBy.push(deprecatedDirectiveNode);
169
+ directiveNodesBesidesNativeDirectives.push(deprecatedDirectiveNode);
164
170
  }
165
171
  if (specifiedByDirectiveNode != null) {
166
- directiveNodesBesidesDeprecatedAndSpecifiedBy.push(specifiedByDirectiveNode);
172
+ directiveNodesBesidesNativeDirectives.push(specifiedByDirectiveNode);
173
+ }
174
+ if (oneOfDirectiveNode != null) {
175
+ directiveNodesBesidesNativeDirectives.push(oneOfDirectiveNode);
167
176
  }
168
- return directiveNodesBesidesDeprecatedAndSpecifiedBy;
177
+ return directiveNodesBesidesNativeDirectives;
169
178
  }
170
179
  export function astFromArg(arg, schema, pathToDirectivesInExtensions) {
171
180
  return {
@@ -176,10 +185,7 @@ export function astFromArg(arg, schema, pathToDirectivesInExtensions) {
176
185
  value: arg.name,
177
186
  },
178
187
  type: astFromType(arg.type),
179
- // ConstXNode has been introduced in v16 but it is not compatible with XNode so we do `as any` for backwards compatibility
180
- defaultValue: arg.defaultValue !== undefined
181
- ? (astFromValue(arg.defaultValue, arg.type) ?? undefined)
182
- : undefined,
188
+ defaultValue: defaultValueAstFromType(arg),
183
189
  directives: getDirectiveNodes(arg, schema, pathToDirectivesInExtensions),
184
190
  };
185
191
  }
@@ -299,7 +305,7 @@ export function astFromInputField(field, schema, pathToDirectivesInExtensions) {
299
305
  type: astFromType(field.type),
300
306
  // ConstXNode has been introduced in v16 but it is not compatible with XNode so we do `as any` for backwards compatibility
301
307
  directives: getDirectiveNodes(field, schema, pathToDirectivesInExtensions),
302
- defaultValue: astFromValue(field.defaultValue, field.type) ?? undefined,
308
+ defaultValue: defaultValueAstFromType(field),
303
309
  };
304
310
  }
305
311
  export function astFromEnumValue(value, schema, pathToDirectivesInExtensions) {
@@ -1,3 +1,4 @@
1
+ import { fakeRejectPromise } from '@whatwg-node/promise-helpers';
1
2
  import { memoize1 } from './memoize.js';
2
3
  // AbortSignal handler cache to avoid the "possible EventEmitter memory leak detected"
3
4
  // on Node.js
@@ -25,8 +26,11 @@ export function registerAbortSignalListener(signal, listener) {
25
26
  getListenersOfAbortSignal(signal).add(listener);
26
27
  }
27
28
  export const getAbortPromise = memoize1(function getAbortPromise(signal) {
29
+ // If the signal is already aborted, return a rejected promise
30
+ if (signal.aborted) {
31
+ return fakeRejectPromise(signal.reason);
32
+ }
28
33
  return new Promise((_resolve, reject) => {
29
- // If the signal is already aborted, return a rejected promise
30
34
  if (signal.aborted) {
31
35
  reject(signal.reason);
32
36
  return;
package/esm/stub.js CHANGED
@@ -24,6 +24,7 @@ export function createStub(node, type) {
24
24
  case Kind.LIST_TYPE:
25
25
  return new GraphQLList(createStub(node.type, type));
26
26
  case Kind.NON_NULL_TYPE:
27
+ // FIXME: graphql@17 type issue
27
28
  return new GraphQLNonNull(createStub(node.type, type));
28
29
  default:
29
30
  if (type === 'output') {
@@ -42,7 +42,8 @@ export function createVariableNameGenerator(variableDefinitionMap) {
42
42
  return (argName) => {
43
43
  let varName;
44
44
  do {
45
- varName = `_v${(varCounter++).toString()}_${argName}`;
45
+ varName = varCounter === 0 ? argName : `_v${varCounter.toString()}_${argName}`;
46
+ varCounter++;
46
47
  } while (varName in variableDefinitionMap);
47
48
  return varName;
48
49
  };
@@ -29,7 +29,7 @@ export function visitResult(result, request, schema, resultVisitorMap, errorVisi
29
29
  }
30
30
  return acc;
31
31
  }, {});
32
- const variableValues = request.variables || {};
32
+ const variableValues = normalizeVariableValues(request.variables);
33
33
  const errorInfo = {
34
34
  segmentInfoMap: new Map(),
35
35
  unpathedErrors: new Set(),
@@ -46,6 +46,12 @@ export function visitResult(result, request, schema, resultVisitorMap, errorVisi
46
46
  }
47
47
  return result;
48
48
  }
49
+ function normalizeVariableValues(variables) {
50
+ return {
51
+ coerced: variables ?? {},
52
+ sources: {},
53
+ };
54
+ }
49
55
  function visitErrorsByType(errors, errorVisitorMap, errorInfo) {
50
56
  const segmentInfoMap = errorInfo.segmentInfoMap;
51
57
  const unpathedErrors = errorInfo.unpathedErrors;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphql-tools/utils",
3
- "version": "10.7.2",
3
+ "version": "12.0.1",
4
4
  "description": "Common package containing utils and types for GraphQL tools",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {
@@ -8,13 +8,13 @@
8
8
  },
9
9
  "dependencies": {
10
10
  "@graphql-typed-document-node/core": "^3.1.1",
11
+ "@whatwg-node/promise-helpers": "^1.0.0",
11
12
  "cross-inspect": "1.0.1",
12
- "dset": "^3.1.4",
13
13
  "tslib": "^2.4.0"
14
14
  },
15
15
  "repository": {
16
16
  "type": "git",
17
- "url": "ardatan/graphql-tools",
17
+ "url": "git+https://github.com/ardatan/graphql-tools.git",
18
18
  "directory": "packages/utils"
19
19
  },
20
20
  "author": "Dotan Simha <dotansimha@gmail.com>",
@@ -20,6 +20,37 @@ export interface ExecutionResult<TData = any, TExtensions = any> {
20
20
  }
21
21
  export interface GraphQLResolveInfo extends OrigGraphQLResolveInfo {
22
22
  signal?: AbortSignal;
23
+ /** Returns the AbortSignal supplied for this execution, if any. */
24
+ readonly getAbortSignal: () => AbortSignal | undefined;
25
+ /** Returns helper functions for tracking asynchronous resolver work. */
26
+ readonly getAsyncHelpers: () => GraphQLResolveInfoHelpers;
27
+ }
28
+ export interface GraphQLResolveInfoHelpers {
29
+ /**
30
+ * Promise.all wrapper that allows rejected branches to be tracked
31
+ * as execution async work.
32
+ *
33
+ * Intended use: return or await this promise from resolver work.
34
+ * Un-awaited async side effects are an anti-pattern:
35
+ *
36
+ * const { promiseAll } = info.getAsyncHelpers();
37
+ * promiseAll([someAsyncWork(), someOtherAsyncWork()]).catch(() => undefined);
38
+ *
39
+ * In that anti-pattern, tracking starts only after rejection (on a
40
+ * later microtask), so this work is not guaranteed to delay
41
+ * `hooks.asyncWorkFinished`.
42
+ *
43
+ * Use `track(...)` for un-awaited async side effects:
44
+ *
45
+ * const { track } = info.getAsyncHelpers();
46
+ * track([
47
+ * someAsyncWork().catch(() => undefined),
48
+ * someOtherAsyncWork().catch(() => undefined)
49
+ * ]);
50
+ */
51
+ readonly promiseAll: <T>(values: ReadonlyArray<PromiseLike<T> | T>) => Promise<Array<T>>;
52
+ /** Tracks asynchronous work that should delay execution completion hooks. */
53
+ readonly track: (maybePromises: ReadonlyArray<unknown>) => void;
23
54
  }
24
55
  export interface ExecutionRequest<TVariables extends Record<string, any> = any, TContext = any, TRootValue = any, TExtensions = Record<string, any>, TReturn = any> {
25
56
  document: TypedDocumentNode<TReturn, TVariables>;
@@ -29,8 +60,16 @@ export interface ExecutionRequest<TVariables extends Record<string, any> = any,
29
60
  extensions?: TExtensions;
30
61
  rootValue?: TRootValue;
31
62
  context?: TContext;
63
+ subgraphName?: string;
32
64
  info?: GraphQLResolveInfo;
33
65
  signal?: AbortSignal;
66
+ /**
67
+ * Enable/Disable the addition of field schema coordinate in GraphQL Errors extension
68
+ *
69
+ * Note: Schema Coordinate are exposed using Symbol.for('schemaCoordinate') so that it's not
70
+ * serialized. Exposing schema coordinate can ease the discovery of private schemas.
71
+ */
72
+ schemaCoordinateInErrors?: boolean;
34
73
  }
35
74
  export interface GraphQLParseOptions {
36
75
  noLocation?: boolean;
@@ -20,6 +20,37 @@ export interface ExecutionResult<TData = any, TExtensions = any> {
20
20
  }
21
21
  export interface GraphQLResolveInfo extends OrigGraphQLResolveInfo {
22
22
  signal?: AbortSignal;
23
+ /** Returns the AbortSignal supplied for this execution, if any. */
24
+ readonly getAbortSignal: () => AbortSignal | undefined;
25
+ /** Returns helper functions for tracking asynchronous resolver work. */
26
+ readonly getAsyncHelpers: () => GraphQLResolveInfoHelpers;
27
+ }
28
+ export interface GraphQLResolveInfoHelpers {
29
+ /**
30
+ * Promise.all wrapper that allows rejected branches to be tracked
31
+ * as execution async work.
32
+ *
33
+ * Intended use: return or await this promise from resolver work.
34
+ * Un-awaited async side effects are an anti-pattern:
35
+ *
36
+ * const { promiseAll } = info.getAsyncHelpers();
37
+ * promiseAll([someAsyncWork(), someOtherAsyncWork()]).catch(() => undefined);
38
+ *
39
+ * In that anti-pattern, tracking starts only after rejection (on a
40
+ * later microtask), so this work is not guaranteed to delay
41
+ * `hooks.asyncWorkFinished`.
42
+ *
43
+ * Use `track(...)` for un-awaited async side effects:
44
+ *
45
+ * const { track } = info.getAsyncHelpers();
46
+ * track([
47
+ * someAsyncWork().catch(() => undefined),
48
+ * someOtherAsyncWork().catch(() => undefined)
49
+ * ]);
50
+ */
51
+ readonly promiseAll: <T>(values: ReadonlyArray<PromiseLike<T> | T>) => Promise<Array<T>>;
52
+ /** Tracks asynchronous work that should delay execution completion hooks. */
53
+ readonly track: (maybePromises: ReadonlyArray<unknown>) => void;
23
54
  }
24
55
  export interface ExecutionRequest<TVariables extends Record<string, any> = any, TContext = any, TRootValue = any, TExtensions = Record<string, any>, TReturn = any> {
25
56
  document: TypedDocumentNode<TReturn, TVariables>;
@@ -29,8 +60,16 @@ export interface ExecutionRequest<TVariables extends Record<string, any> = any,
29
60
  extensions?: TExtensions;
30
61
  rootValue?: TRootValue;
31
62
  context?: TContext;
63
+ subgraphName?: string;
32
64
  info?: GraphQLResolveInfo;
33
65
  signal?: AbortSignal;
66
+ /**
67
+ * Enable/Disable the addition of field schema coordinate in GraphQL Errors extension
68
+ *
69
+ * Note: Schema Coordinate are exposed using Symbol.for('schemaCoordinate') so that it's not
70
+ * serialized. Exposing schema coordinate can ease the discovery of private schemas.
71
+ */
72
+ schemaCoordinateInErrors?: boolean;
34
73
  }
35
74
  export interface GraphQLParseOptions {
36
75
  noLocation?: boolean;