@graphql-tools/executor 1.2.6 → 2.0.0-alpha-20240606144658-8963c8b8f661638eaee0e101a55f3b6e46cc03ff
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/execution/AccumulatorMap.js +21 -0
- package/cjs/execution/BoxedPromiseOrValue.js +25 -0
- package/cjs/execution/IncrementalGraph.js +271 -0
- package/cjs/execution/IncrementalPublisher.js +274 -0
- package/cjs/execution/buildFieldPlan.js +62 -0
- package/cjs/execution/collectFields.js +174 -0
- package/cjs/execution/execute.js +548 -513
- package/cjs/execution/getBySet.js +13 -0
- package/cjs/execution/isSameSet.js +15 -0
- package/cjs/execution/promiseWithResolvers.js +18 -0
- package/cjs/execution/types.js +19 -0
- package/esm/execution/AccumulatorMap.js +17 -0
- package/esm/execution/BoxedPromiseOrValue.js +21 -0
- package/esm/execution/IncrementalGraph.js +267 -0
- package/esm/execution/IncrementalPublisher.js +270 -0
- package/esm/execution/buildFieldPlan.js +58 -0
- package/esm/execution/collectFields.js +169 -0
- package/esm/execution/execute.js +549 -514
- package/esm/execution/getBySet.js +9 -0
- package/esm/execution/isSameSet.js +11 -0
- package/esm/execution/promiseWithResolvers.js +14 -0
- package/esm/execution/types.js +12 -0
- package/package.json +2 -2
- package/typings/execution/AccumulatorMap.d.cts +7 -0
- package/typings/execution/AccumulatorMap.d.ts +7 -0
- package/typings/execution/BoxedPromiseOrValue.d.cts +15 -0
- package/typings/execution/BoxedPromiseOrValue.d.ts +15 -0
- package/typings/execution/IncrementalGraph.d.cts +32 -0
- package/typings/execution/IncrementalGraph.d.ts +32 -0
- package/typings/execution/IncrementalPublisher.d.cts +8 -0
- package/typings/execution/IncrementalPublisher.d.ts +8 -0
- package/typings/execution/buildFieldPlan.d.cts +7 -0
- package/typings/execution/buildFieldPlan.d.ts +7 -0
- package/typings/execution/collectFields.d.cts +40 -0
- package/typings/execution/collectFields.d.ts +40 -0
- package/typings/execution/execute.d.cts +8 -106
- package/typings/execution/execute.d.ts +8 -106
- package/typings/execution/getBySet.d.cts +1 -0
- package/typings/execution/getBySet.d.ts +1 -0
- package/typings/execution/isSameSet.d.cts +1 -0
- package/typings/execution/isSameSet.d.ts +1 -0
- package/typings/execution/promiseWithResolvers.d.cts +10 -0
- package/typings/execution/promiseWithResolvers.d.ts +10 -0
- package/typings/execution/types.d.cts +155 -0
- package/typings/execution/types.d.ts +155 -0
- package/cjs/execution/flattenAsyncIterable.js +0 -89
- package/esm/execution/flattenAsyncIterable.js +0 -85
- package/typings/execution/flattenAsyncIterable.d.cts +0 -7
- package/typings/execution/flattenAsyncIterable.d.ts +0 -7
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { getBySet } from './getBySet.js';
|
|
2
|
+
import { isSameSet } from './isSameSet.js';
|
|
3
|
+
export function buildFieldPlan(originalGroupedFieldSet, parentDeferUsages = new Set()) {
|
|
4
|
+
const groupedFieldSet = new Map();
|
|
5
|
+
const newGroupedFieldSets = new Map();
|
|
6
|
+
const map = new Map();
|
|
7
|
+
for (const [responseKey, fieldGroup] of originalGroupedFieldSet) {
|
|
8
|
+
const deferUsageSet = new Set();
|
|
9
|
+
let inOriginalResult = false;
|
|
10
|
+
for (const fieldDetails of fieldGroup) {
|
|
11
|
+
const deferUsage = fieldDetails.deferUsage;
|
|
12
|
+
if (deferUsage === undefined) {
|
|
13
|
+
inOriginalResult = true;
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
deferUsageSet.add(deferUsage);
|
|
17
|
+
}
|
|
18
|
+
if (inOriginalResult) {
|
|
19
|
+
deferUsageSet.clear();
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
deferUsageSet.forEach(deferUsage => {
|
|
23
|
+
const ancestors = getAncestors(deferUsage);
|
|
24
|
+
for (const ancestor of ancestors) {
|
|
25
|
+
if (deferUsageSet.has(ancestor)) {
|
|
26
|
+
deferUsageSet.delete(deferUsage);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
map.set(responseKey, { deferUsageSet, fieldGroup });
|
|
32
|
+
}
|
|
33
|
+
for (const [responseKey, { deferUsageSet, fieldGroup }] of map) {
|
|
34
|
+
if (isSameSet(deferUsageSet, parentDeferUsages)) {
|
|
35
|
+
groupedFieldSet.set(responseKey, fieldGroup);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
let newGroupedFieldSet = getBySet(newGroupedFieldSets, deferUsageSet);
|
|
39
|
+
if (newGroupedFieldSet === undefined) {
|
|
40
|
+
newGroupedFieldSet = new Map();
|
|
41
|
+
newGroupedFieldSets.set(deferUsageSet, newGroupedFieldSet);
|
|
42
|
+
}
|
|
43
|
+
newGroupedFieldSet.set(responseKey, fieldGroup);
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
groupedFieldSet,
|
|
47
|
+
newGroupedFieldSets,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function getAncestors(deferUsage) {
|
|
51
|
+
const ancestors = [];
|
|
52
|
+
let parentDeferUsage = deferUsage.parentDeferUsage;
|
|
53
|
+
while (parentDeferUsage !== undefined) {
|
|
54
|
+
ancestors.unshift(parentDeferUsage);
|
|
55
|
+
parentDeferUsage = parentDeferUsage.parentDeferUsage;
|
|
56
|
+
}
|
|
57
|
+
return ancestors;
|
|
58
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { getDirectiveValues, GraphQLIncludeDirective, GraphQLSkipDirective, isAbstractType, Kind, typeFromAST, } from 'graphql';
|
|
2
|
+
import { GraphQLDeferDirective } from '@graphql-tools/utils';
|
|
3
|
+
import { AccumulatorMap } from './AccumulatorMap.js';
|
|
4
|
+
import { invariant } from './invariant.js';
|
|
5
|
+
/**
|
|
6
|
+
* Given a selectionSet, collects all of the fields and returns them.
|
|
7
|
+
*
|
|
8
|
+
* CollectFields requires the "runtime type" of an object. For a field that
|
|
9
|
+
* returns an Interface or Union type, the "runtime type" will be the actual
|
|
10
|
+
* object type returned by that field.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
export function collectFields(schema, fragments, variableValues, runtimeType, operation) {
|
|
15
|
+
const groupedFieldSet = new AccumulatorMap();
|
|
16
|
+
const newDeferUsages = [];
|
|
17
|
+
const context = {
|
|
18
|
+
schema,
|
|
19
|
+
fragments,
|
|
20
|
+
variableValues,
|
|
21
|
+
runtimeType,
|
|
22
|
+
operation,
|
|
23
|
+
visitedFragmentNames: new Set(),
|
|
24
|
+
};
|
|
25
|
+
collectFieldsImpl(context, operation.selectionSet, groupedFieldSet, newDeferUsages);
|
|
26
|
+
return { groupedFieldSet, newDeferUsages };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Given an array of field nodes, collects all of the subfields of the passed
|
|
30
|
+
* in fields, and returns them at the end.
|
|
31
|
+
*
|
|
32
|
+
* CollectSubFields requires the "return type" of an object. For a field that
|
|
33
|
+
* returns an Interface or Union type, the "return type" will be the actual
|
|
34
|
+
* object type returned by that field.
|
|
35
|
+
*
|
|
36
|
+
* @internal
|
|
37
|
+
*/
|
|
38
|
+
export function collectSubfields(schema, fragments, variableValues, operation, returnType, fieldGroup) {
|
|
39
|
+
const context = {
|
|
40
|
+
schema,
|
|
41
|
+
fragments,
|
|
42
|
+
variableValues,
|
|
43
|
+
runtimeType: returnType,
|
|
44
|
+
operation,
|
|
45
|
+
visitedFragmentNames: new Set(),
|
|
46
|
+
};
|
|
47
|
+
const subGroupedFieldSet = new AccumulatorMap();
|
|
48
|
+
const newDeferUsages = [];
|
|
49
|
+
for (const fieldDetail of fieldGroup) {
|
|
50
|
+
const node = fieldDetail.node;
|
|
51
|
+
if (node.selectionSet) {
|
|
52
|
+
collectFieldsImpl(context, node.selectionSet, subGroupedFieldSet, newDeferUsages, fieldDetail.deferUsage);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
groupedFieldSet: subGroupedFieldSet,
|
|
57
|
+
newDeferUsages,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function collectFieldsImpl(context, selectionSet, groupedFieldSet, newDeferUsages, deferUsage) {
|
|
61
|
+
const { schema, fragments, variableValues, runtimeType, operation, visitedFragmentNames } = context;
|
|
62
|
+
for (const selection of selectionSet.selections) {
|
|
63
|
+
switch (selection.kind) {
|
|
64
|
+
case Kind.FIELD: {
|
|
65
|
+
if (!shouldIncludeNode(variableValues, selection)) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
groupedFieldSet.add(getFieldEntryKey(selection), {
|
|
69
|
+
node: selection,
|
|
70
|
+
deferUsage,
|
|
71
|
+
});
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
case Kind.INLINE_FRAGMENT: {
|
|
75
|
+
if (!shouldIncludeNode(variableValues, selection) ||
|
|
76
|
+
!doesFragmentConditionMatch(schema, selection, runtimeType)) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const newDeferUsage = getDeferUsage(operation, variableValues, selection, deferUsage);
|
|
80
|
+
if (!newDeferUsage) {
|
|
81
|
+
collectFieldsImpl(context, selection.selectionSet, groupedFieldSet, newDeferUsages, deferUsage);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
newDeferUsages.push(newDeferUsage);
|
|
85
|
+
collectFieldsImpl(context, selection.selectionSet, groupedFieldSet, newDeferUsages, newDeferUsage);
|
|
86
|
+
}
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
case Kind.FRAGMENT_SPREAD: {
|
|
90
|
+
const fragName = selection.name.value;
|
|
91
|
+
const newDeferUsage = getDeferUsage(operation, variableValues, selection, deferUsage);
|
|
92
|
+
if (!newDeferUsage &&
|
|
93
|
+
(visitedFragmentNames.has(fragName) || !shouldIncludeNode(variableValues, selection))) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const fragment = fragments[fragName];
|
|
97
|
+
if (fragment == null || !doesFragmentConditionMatch(schema, fragment, runtimeType)) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (!newDeferUsage) {
|
|
101
|
+
visitedFragmentNames.add(fragName);
|
|
102
|
+
collectFieldsImpl(context, fragment.selectionSet, groupedFieldSet, newDeferUsages, deferUsage);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
newDeferUsages.push(newDeferUsage);
|
|
106
|
+
collectFieldsImpl(context, fragment.selectionSet, groupedFieldSet, newDeferUsages, newDeferUsage);
|
|
107
|
+
}
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Returns an object containing the `@defer` arguments if a field should be
|
|
115
|
+
* deferred based on the experimental flag, defer directive present and
|
|
116
|
+
* not disabled by the "if" argument.
|
|
117
|
+
*/
|
|
118
|
+
function getDeferUsage(operation, variableValues, node, parentDeferUsage) {
|
|
119
|
+
const defer = getDirectiveValues(GraphQLDeferDirective, node, variableValues);
|
|
120
|
+
if (!defer) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (defer['if'] === false) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
invariant(operation.operation !== 'subscription', '`@defer` directive not supported on subscription operations. Disable `@defer` by setting the `if` argument to `false`.');
|
|
127
|
+
return {
|
|
128
|
+
label: typeof defer['label'] === 'string' ? defer['label'] : undefined,
|
|
129
|
+
parentDeferUsage,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Determines if a field should be included based on the `@include` and `@skip`
|
|
134
|
+
* directives, where `@skip` has higher precedence than `@include`.
|
|
135
|
+
*/
|
|
136
|
+
function shouldIncludeNode(variableValues, node) {
|
|
137
|
+
const skip = getDirectiveValues(GraphQLSkipDirective, node, variableValues);
|
|
138
|
+
if (skip?.['if'] === true) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
const include = getDirectiveValues(GraphQLIncludeDirective, node, variableValues);
|
|
142
|
+
if (include?.['if'] === false) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Determines if a fragment is applicable to the given type.
|
|
149
|
+
*/
|
|
150
|
+
function doesFragmentConditionMatch(schema, fragment, type) {
|
|
151
|
+
const typeConditionNode = fragment.typeCondition;
|
|
152
|
+
if (!typeConditionNode) {
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
const conditionalType = typeFromAST(schema, typeConditionNode);
|
|
156
|
+
if (conditionalType === type) {
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
if (isAbstractType(conditionalType)) {
|
|
160
|
+
return schema.isSubType(conditionalType, type);
|
|
161
|
+
}
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Implements the logic to compute the key of a given field's entry
|
|
166
|
+
*/
|
|
167
|
+
function getFieldEntryKey(node) {
|
|
168
|
+
return node.alias ? node.alias.value : node.name.value;
|
|
169
|
+
}
|