@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.
- package/cjs/astFromValue.js +21 -14
- package/cjs/build-operation-for-field.js +13 -3
- package/cjs/collectFields.js +4 -3
- package/cjs/comments.js +1 -1
- package/cjs/defaultValueAstFromType.js +32 -0
- package/cjs/descriptionFromObject.js +7 -5
- package/cjs/errors.js +30 -4
- package/cjs/getArgumentValues.js +23 -15
- package/cjs/getDirectiveValues.js +15 -0
- package/cjs/heal.js +4 -1
- package/cjs/helpers.js +40 -4
- package/cjs/index.js +8 -5
- package/cjs/jsutils.js +4 -6
- package/cjs/mapSchema.js +2 -1
- package/cjs/mergeDeep.js +35 -9
- package/cjs/mergeIncrementalResult.js +25 -4
- package/cjs/observableToAsyncIterable.js +29 -17
- package/cjs/print-schema-with-directives.js +23 -17
- package/cjs/registerAbortSignalListener.js +5 -1
- package/cjs/stub.js +1 -0
- package/cjs/updateArgument.js +2 -1
- package/cjs/visitResult.js +7 -1
- package/esm/astFromValue.js +21 -14
- package/esm/build-operation-for-field.js +13 -3
- package/esm/collectFields.js +2 -1
- package/esm/comments.js +1 -1
- package/esm/defaultValueAstFromType.js +28 -0
- package/esm/descriptionFromObject.js +7 -5
- package/esm/errors.js +29 -6
- package/esm/getArgumentValues.js +24 -16
- package/esm/getDirectiveValues.js +12 -0
- package/esm/heal.js +4 -1
- package/esm/helpers.js +39 -4
- package/esm/index.js +3 -4
- package/esm/jsutils.js +3 -5
- package/esm/mapSchema.js +2 -1
- package/esm/mergeDeep.js +35 -9
- package/esm/mergeIncrementalResult.js +25 -4
- package/esm/observableToAsyncIterable.js +28 -16
- package/esm/print-schema-with-directives.js +24 -18
- package/esm/registerAbortSignalListener.js +5 -1
- package/esm/stub.js +1 -0
- package/esm/updateArgument.js +2 -1
- package/esm/visitResult.js +7 -1
- package/package.json +3 -3
- package/typings/Interfaces.d.cts +39 -0
- package/typings/Interfaces.d.ts +39 -0
- package/typings/collectFields.d.cts +5 -6
- package/typings/collectFields.d.ts +5 -6
- package/typings/defaultValueAstFromType.d.cts +9 -0
- package/typings/defaultValueAstFromType.d.ts +9 -0
- package/typings/errors.d.cts +19 -1
- package/typings/errors.d.ts +19 -1
- package/typings/executor.d.cts +4 -7
- package/typings/executor.d.ts +4 -7
- package/typings/getDirectiveExtensions.d.cts +1 -3
- package/typings/getDirectiveExtensions.d.ts +1 -3
- package/typings/getDirectiveValues.d.cts +10 -0
- package/typings/getDirectiveValues.d.ts +10 -0
- package/typings/helpers.d.cts +28 -2
- package/typings/helpers.d.ts +28 -2
- package/typings/index.d.cts +3 -4
- package/typings/index.d.ts +3 -4
- package/typings/jsutils.d.cts +2 -2
- package/typings/jsutils.d.ts +2 -2
- package/typings/mergeDeep.d.cts +1 -1
- package/typings/mergeDeep.d.ts +1 -1
- package/typings/print-schema-with-directives.d.cts +1 -0
- package/typings/print-schema-with-directives.d.ts +1 -0
- package/typings/registerAbortSignalListener.d.cts +1 -1
- package/typings/registerAbortSignalListener.d.ts +1 -1
- package/typings/types.d.cts +25 -1
- package/typings/types.d.ts +25 -1
- package/cjs/createDeferred.js +0 -15
- package/cjs/fakePromise.js +0 -63
- package/cjs/map-maybe-promise.js +0 -26
- package/cjs/mapAsyncIterator.js +0 -75
- package/esm/createDeferred.js +0 -12
- package/esm/fakePromise.js +0 -59
- package/esm/map-maybe-promise.js +0 -23
- package/esm/mapAsyncIterator.js +0 -72
- package/typings/createDeferred.d.cts +0 -6
- package/typings/createDeferred.d.ts +0 -6
- package/typings/fakePromise.d.cts +0 -4
- package/typings/fakePromise.d.ts +0 -4
- package/typings/map-maybe-promise.d.cts +0 -2
- package/typings/map-maybe-promise.d.ts +0 -2
- package/typings/mapAsyncIterator.d.cts +0 -6
- package/typings/mapAsyncIterator.d.ts +0 -6
package/esm/helpers.js
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
import { parse } from 'graphql';
|
|
2
|
-
|
|
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) ||
|
|
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 = /[
|
|
73
|
+
const invalidPathRegex = /[‘“!^<>`\n]/;
|
|
39
74
|
/**
|
|
40
|
-
*
|
|
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
|
|
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 {
|
|
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
|
|
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 =
|
|
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
|
-
|
|
53
|
-
|
|
64
|
+
// never let a source key reach the prototype chain
|
|
65
|
+
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
|
|
66
|
+
continue;
|
|
54
67
|
}
|
|
55
|
-
|
|
56
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 '
|
|
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
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
169
|
+
directiveNodesBesidesNativeDirectives.push(deprecatedDirectiveNode);
|
|
164
170
|
}
|
|
165
171
|
if (specifiedByDirectiveNode != null) {
|
|
166
|
-
|
|
172
|
+
directiveNodesBesidesNativeDirectives.push(specifiedByDirectiveNode);
|
|
173
|
+
}
|
|
174
|
+
if (oneOfDirectiveNode != null) {
|
|
175
|
+
directiveNodesBesidesNativeDirectives.push(oneOfDirectiveNode);
|
|
167
176
|
}
|
|
168
|
-
return
|
|
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
|
-
|
|
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:
|
|
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') {
|
package/esm/updateArgument.js
CHANGED
|
@@ -42,7 +42,8 @@ export function createVariableNameGenerator(variableDefinitionMap) {
|
|
|
42
42
|
return (argName) => {
|
|
43
43
|
let varName;
|
|
44
44
|
do {
|
|
45
|
-
varName = `_v${
|
|
45
|
+
varName = varCounter === 0 ? argName : `_v${varCounter.toString()}_${argName}`;
|
|
46
|
+
varCounter++;
|
|
46
47
|
} while (varName in variableDefinitionMap);
|
|
47
48
|
return varName;
|
|
48
49
|
};
|
package/esm/visitResult.js
CHANGED
|
@@ -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": "
|
|
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>",
|
package/typings/Interfaces.d.cts
CHANGED
|
@@ -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;
|
package/typings/Interfaces.d.ts
CHANGED
|
@@ -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;
|