@graphql-tools/executor 4.1.1 → 4.1.2-alpha-20261020170708-72d61a1bbfbf64a0a9d61e801c80001b7ea79dc3
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/execute.js +94 -86
- package/cjs/execution/getBySet.js +13 -0
- package/cjs/execution/incremental.js +157 -4
- package/cjs/execution/incrementalCollect.js +194 -0
- package/cjs/execution/isSameSet.js +15 -0
- package/esm/execution/execute.js +91 -82
- package/esm/execution/getBySet.js +10 -0
- package/esm/execution/incremental.js +153 -4
- package/esm/execution/incrementalCollect.js +185 -0
- package/esm/execution/isSameSet.js +12 -0
- package/package.json +1 -1
- package/typings/execution/execute.d.cts +7 -19
- package/typings/execution/execute.d.ts +7 -19
- package/typings/execution/getBySet.d.cts +2 -0
- package/typings/execution/getBySet.d.ts +2 -0
- package/typings/execution/incremental.d.cts +25 -0
- package/typings/execution/incremental.d.ts +25 -0
- package/typings/execution/incrementalCollect.d.cts +35 -0
- package/typings/execution/incrementalCollect.d.ts +35 -0
- package/typings/execution/isSameSet.d.cts +2 -0
- package/typings/execution/isSameSet.d.ts +2 -0
|
@@ -44,6 +44,130 @@ export function deleteAsyncPayload(exeContext, record) {
|
|
|
44
44
|
record.streamGroup?.release();
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
+
export function pathStartsWith(path, prefix) {
|
|
48
|
+
if (prefix.length > path.length) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
52
|
+
if (path[i] !== prefix[i]) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
export function isAtOrUnderNulledPath(path, nulledPaths) {
|
|
59
|
+
if (nulledPaths == null || nulledPaths.length === 0) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
for (const nulledPath of nulledPaths) {
|
|
63
|
+
if (pathStartsWith(path, nulledPath)) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
function isStrictlyUnderNulledPath(path, nulledPaths) {
|
|
70
|
+
for (const nulledPath of nulledPaths) {
|
|
71
|
+
if (path.length > nulledPath.length && pathStartsWith(path, nulledPath)) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
function isExactNulledPath(path, nulledPaths) {
|
|
78
|
+
for (const nulledPath of nulledPaths) {
|
|
79
|
+
if (path.length === nulledPath.length && pathStartsWith(path, nulledPath)) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
function isEmptyIncrementalData(data) {
|
|
86
|
+
if (data == null) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
if (typeof data !== 'object') {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
if (Array.isArray(data)) {
|
|
93
|
+
return data.length === 0;
|
|
94
|
+
}
|
|
95
|
+
const entries = Object.entries(data);
|
|
96
|
+
if (entries.length === 0) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
return entries.every(([, value]) => value != null && typeof value === 'object' && isEmptyIncrementalData(value));
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Drop keys that would write into a subtree already nulled by error bubbling.
|
|
103
|
+
* Ancestor deferred records can still deliver sibling fields (e.g. `someField`
|
|
104
|
+
* next to a nulled `b.c`).
|
|
105
|
+
*/
|
|
106
|
+
export function pruneUnmergeableIncrementalData(data, basePath, nulledPaths) {
|
|
107
|
+
if (nulledPaths == null || nulledPaths.length === 0 || data == null || typeof data !== 'object') {
|
|
108
|
+
return data;
|
|
109
|
+
}
|
|
110
|
+
if (Array.isArray(data)) {
|
|
111
|
+
return data.map((item, index) => {
|
|
112
|
+
const itemPath = [...basePath, index];
|
|
113
|
+
if (isAtOrUnderNulledPath(itemPath, nulledPaths)) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
return pruneUnmergeableIncrementalData(item, itemPath, nulledPaths);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
const result = {};
|
|
120
|
+
for (const [key, value] of Object.entries(data)) {
|
|
121
|
+
const childPath = [...basePath, key];
|
|
122
|
+
if (isStrictlyUnderNulledPath(childPath, nulledPaths)) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (isExactNulledPath(childPath, nulledPaths)) {
|
|
126
|
+
// Keep a null delivered by this payload; drop a non-null write into an
|
|
127
|
+
// already-nulled object (Yaacov: unmergeable incremental).
|
|
128
|
+
if (value != null) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
result[key] = null;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
result[key] = pruneUnmergeableIncrementalData(value, childPath, nulledPaths);
|
|
135
|
+
}
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
function isAnnounced(exeContext, record) {
|
|
139
|
+
const state = exeContext.publisher;
|
|
140
|
+
if (state == null) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
return state.announced.has(getPublishNode(record));
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Cancel a subsequent record whose path is at or under a nulled position
|
|
147
|
+
* (graphql-js 17 `hasNulledPosition`). v0.2 records that were already
|
|
148
|
+
* announced stay in the set so the publisher can emit `completed` without
|
|
149
|
+
* incremental data.
|
|
150
|
+
*/
|
|
151
|
+
export function cancelAsyncPayload(exeContext, record) {
|
|
152
|
+
record.cancelled = true;
|
|
153
|
+
if (isStreamPayload(record) && record.iterator?.return) {
|
|
154
|
+
record.iterator.return().catch(() => {
|
|
155
|
+
// ignore error
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
if (!record.isCompleted) {
|
|
159
|
+
if (isStreamPayload(record)) {
|
|
160
|
+
record.addItems(null);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
record.addData(null);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (exeContext.incrementalSpec === 'v0.2' && isAnnounced(exeContext, record)) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
deleteAsyncPayload(exeContext, record);
|
|
170
|
+
}
|
|
47
171
|
export class DeferredFragmentRecord {
|
|
48
172
|
type;
|
|
49
173
|
errors;
|
|
@@ -53,7 +177,9 @@ export class DeferredFragmentRecord {
|
|
|
53
177
|
data;
|
|
54
178
|
parentContext;
|
|
55
179
|
deliveryParent;
|
|
180
|
+
deferUsage;
|
|
56
181
|
isCompleted;
|
|
182
|
+
cancelled;
|
|
57
183
|
_exeContext;
|
|
58
184
|
_resolve;
|
|
59
185
|
constructor(opts) {
|
|
@@ -62,10 +188,12 @@ export class DeferredFragmentRecord {
|
|
|
62
188
|
this.path = pathToArray(opts.path);
|
|
63
189
|
this.parentContext = opts.parentContext;
|
|
64
190
|
this.deliveryParent = opts.deliveryParent ?? opts.parentContext;
|
|
191
|
+
this.deferUsage = opts.deferUsage;
|
|
65
192
|
this.errors = [];
|
|
66
193
|
this._exeContext = opts.exeContext;
|
|
67
194
|
this._exeContext.subsequentPayloads.add(this);
|
|
68
195
|
this.isCompleted = false;
|
|
196
|
+
this.cancelled = false;
|
|
69
197
|
this.data = null;
|
|
70
198
|
this.promise = new Promise(resolve => {
|
|
71
199
|
this._resolve = MaybePromise => {
|
|
@@ -96,6 +224,7 @@ export class StreamRecord {
|
|
|
96
224
|
iterator;
|
|
97
225
|
isCompletedIterator;
|
|
98
226
|
isCompleted;
|
|
227
|
+
cancelled;
|
|
99
228
|
streamGroup;
|
|
100
229
|
_exeContext;
|
|
101
230
|
_resolve;
|
|
@@ -112,6 +241,7 @@ export class StreamRecord {
|
|
|
112
241
|
this._exeContext.subsequentPayloads.add(this);
|
|
113
242
|
this.streamGroup?.retain();
|
|
114
243
|
this.isCompleted = false;
|
|
244
|
+
this.cancelled = false;
|
|
115
245
|
this.items = null;
|
|
116
246
|
this.promise = new Promise(resolve => {
|
|
117
247
|
this._resolve = MaybePromise => {
|
|
@@ -178,6 +308,9 @@ function toPendingResult(state, record) {
|
|
|
178
308
|
return pending;
|
|
179
309
|
}
|
|
180
310
|
function announceIfNeeded(record, state, pending) {
|
|
311
|
+
if (record.cancelled) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
181
314
|
const node = getPublishNode(record);
|
|
182
315
|
if (state.announced.has(node)) {
|
|
183
316
|
return;
|
|
@@ -210,6 +343,9 @@ function getCompletedIncrementalResults(exeContext) {
|
|
|
210
343
|
continue;
|
|
211
344
|
}
|
|
212
345
|
deleteAsyncPayload(exeContext, asyncPayloadRecord);
|
|
346
|
+
if (asyncPayloadRecord.cancelled) {
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
213
349
|
if (isStreamPayload(asyncPayloadRecord)) {
|
|
214
350
|
const items = asyncPayloadRecord.items;
|
|
215
351
|
if (asyncPayloadRecord.isCompletedIterator) {
|
|
@@ -219,8 +355,12 @@ function getCompletedIncrementalResults(exeContext) {
|
|
|
219
355
|
incrementalResult.items = items;
|
|
220
356
|
}
|
|
221
357
|
else {
|
|
222
|
-
const data = asyncPayloadRecord.data;
|
|
223
|
-
|
|
358
|
+
const data = pruneUnmergeableIncrementalData(asyncPayloadRecord.data, asyncPayloadRecord.path, exeContext.nulledPaths);
|
|
359
|
+
if (isEmptyIncrementalData(data) && asyncPayloadRecord.errors.length === 0) {
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
incrementalResult.data =
|
|
363
|
+
data ?? null;
|
|
224
364
|
}
|
|
225
365
|
incrementalResult.path = asyncPayloadRecord.path;
|
|
226
366
|
if (asyncPayloadRecord.label) {
|
|
@@ -260,13 +400,22 @@ function buildSubsequentV02(exeContext, completedRecords) {
|
|
|
260
400
|
continue;
|
|
261
401
|
}
|
|
262
402
|
const id = ensureId(state, record);
|
|
263
|
-
if (record.
|
|
403
|
+
if (record.cancelled) {
|
|
404
|
+
completed.push({ id });
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
const data = pruneUnmergeableIncrementalData(record.data, record.path, exeContext.nulledPaths);
|
|
408
|
+
if ((record.data == null && record.errors.length > 0) ||
|
|
409
|
+
(isEmptyIncrementalData(data) && record.errors.length > 0)) {
|
|
264
410
|
completed.push({ id, errors: record.errors });
|
|
265
411
|
}
|
|
412
|
+
else if (isEmptyIncrementalData(data) && record.errors.length === 0) {
|
|
413
|
+
completed.push({ id });
|
|
414
|
+
}
|
|
266
415
|
else {
|
|
267
416
|
const incrementalResult = {
|
|
268
417
|
id,
|
|
269
|
-
data:
|
|
418
|
+
data: data ?? null,
|
|
270
419
|
};
|
|
271
420
|
if (record.errors.length > 0) {
|
|
272
421
|
incrementalResult.errors = record.errors;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { Kind, } from 'graphql';
|
|
2
|
+
import { doesFragmentConditionMatch, getDirectiveValues, getFieldEntryKey, GraphQLDeferDirective, shouldIncludeNode, } from '@graphql-tools/utils';
|
|
3
|
+
import { getBySet } from './getBySet.js';
|
|
4
|
+
import { isSameSet } from './isSameSet.js';
|
|
5
|
+
class FieldDetailsMap extends Map {
|
|
6
|
+
add(key, item) {
|
|
7
|
+
const group = this.get(key);
|
|
8
|
+
if (group === undefined) {
|
|
9
|
+
this.set(key, [item]);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
group.push(item);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function collectIncrementalFields(schema, fragments, variableValues, runtimeType, selectionSet, parentDeferUsage) {
|
|
17
|
+
const groupedFieldSet = new FieldDetailsMap();
|
|
18
|
+
const newDeferUsages = [];
|
|
19
|
+
const context = {
|
|
20
|
+
schema,
|
|
21
|
+
fragments,
|
|
22
|
+
variableValues,
|
|
23
|
+
runtimeType,
|
|
24
|
+
visitedFragmentNames: new Map(),
|
|
25
|
+
};
|
|
26
|
+
collectFieldsImpl(context, selectionSet, groupedFieldSet, newDeferUsages, parentDeferUsage);
|
|
27
|
+
return { groupedFieldSet, newDeferUsages };
|
|
28
|
+
}
|
|
29
|
+
const deferUsageByFieldNode = new WeakMap();
|
|
30
|
+
export function collectIncrementalSubfields(schema, fragments, variableValues, returnType, fieldNodes, parentDeferUsage) {
|
|
31
|
+
const groupedFieldSet = new FieldDetailsMap();
|
|
32
|
+
const newDeferUsages = [];
|
|
33
|
+
const context = {
|
|
34
|
+
schema,
|
|
35
|
+
fragments,
|
|
36
|
+
variableValues,
|
|
37
|
+
runtimeType: returnType,
|
|
38
|
+
visitedFragmentNames: new Map(),
|
|
39
|
+
};
|
|
40
|
+
for (const node of fieldNodes) {
|
|
41
|
+
if (node.selectionSet) {
|
|
42
|
+
const deferUsage = deferUsageByFieldNode.has(node)
|
|
43
|
+
? deferUsageByFieldNode.get(node)
|
|
44
|
+
: parentDeferUsage;
|
|
45
|
+
collectFieldsImpl(context, node.selectionSet, groupedFieldSet, newDeferUsages, deferUsage);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { groupedFieldSet, newDeferUsages };
|
|
49
|
+
}
|
|
50
|
+
function collectFieldsImpl(context, selectionSet, groupedFieldSet, newDeferUsages, deferUsage) {
|
|
51
|
+
const { schema, fragments, variableValues, runtimeType, visitedFragmentNames } = context;
|
|
52
|
+
for (const selection of selectionSet.selections) {
|
|
53
|
+
switch (selection.kind) {
|
|
54
|
+
case Kind.FIELD: {
|
|
55
|
+
if (!shouldIncludeNode(variableValues, selection)) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
groupedFieldSet.add(getFieldEntryKey(selection), {
|
|
59
|
+
node: selection,
|
|
60
|
+
deferUsage,
|
|
61
|
+
});
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
case Kind.INLINE_FRAGMENT: {
|
|
65
|
+
if (!shouldIncludeNode(variableValues, selection) ||
|
|
66
|
+
!doesFragmentConditionMatch(schema, selection, runtimeType)) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const newDeferUsage = getDeferUsage(variableValues, selection, deferUsage);
|
|
70
|
+
if (!newDeferUsage) {
|
|
71
|
+
collectFieldsImpl(context, selection.selectionSet, groupedFieldSet, newDeferUsages, deferUsage);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
newDeferUsages.push(newDeferUsage);
|
|
75
|
+
collectFieldsImpl(context, selection.selectionSet, groupedFieldSet, newDeferUsages, newDeferUsage);
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
case Kind.FRAGMENT_SPREAD: {
|
|
80
|
+
const fragName = selection.name.value;
|
|
81
|
+
if (!shouldIncludeNode(variableValues, selection)) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const fragment = fragments[fragName];
|
|
85
|
+
if (!fragment || !doesFragmentConditionMatch(schema, fragment, runtimeType)) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const newDeferUsage = getDeferUsage(variableValues, selection, deferUsage);
|
|
89
|
+
const visitedAsDeferred = visitedFragmentNames.get(fragName);
|
|
90
|
+
let maybeNewDeferUsage;
|
|
91
|
+
if (!newDeferUsage) {
|
|
92
|
+
if (visitedAsDeferred === false) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
visitedFragmentNames.set(fragName, false);
|
|
96
|
+
maybeNewDeferUsage = deferUsage;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
if (visitedAsDeferred !== undefined) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
visitedFragmentNames.set(fragName, true);
|
|
103
|
+
newDeferUsages.push(newDeferUsage);
|
|
104
|
+
maybeNewDeferUsage = newDeferUsage;
|
|
105
|
+
}
|
|
106
|
+
collectFieldsImpl(context, fragment.selectionSet, groupedFieldSet, newDeferUsages, maybeNewDeferUsage);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function getDeferUsage(variableValues, node, parentDeferUsage) {
|
|
113
|
+
const defer = getDirectiveValues(GraphQLDeferDirective, node, variableValues);
|
|
114
|
+
if (!defer || defer['if'] === false) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
label: typeof defer['label'] === 'string' ? defer['label'] : undefined,
|
|
119
|
+
parentDeferUsage,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
export function buildExecutionPlan(originalGroupedFieldSet, parentDeferUsages = new Set()) {
|
|
123
|
+
const groupedFieldSet = new Map();
|
|
124
|
+
const newGroupedFieldSets = new Map();
|
|
125
|
+
for (const [responseKey, fieldDetailsList] of originalGroupedFieldSet) {
|
|
126
|
+
const filteredDeferUsageSet = getFilteredDeferUsageSet(fieldDetailsList);
|
|
127
|
+
if (isSameSet(filteredDeferUsageSet, parentDeferUsages)) {
|
|
128
|
+
groupedFieldSet.set(responseKey, fieldDetailsList);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
let newGroupedFieldSet = getBySet(newGroupedFieldSets, filteredDeferUsageSet);
|
|
132
|
+
if (newGroupedFieldSet === undefined) {
|
|
133
|
+
newGroupedFieldSet = new Map();
|
|
134
|
+
newGroupedFieldSets.set(filteredDeferUsageSet, newGroupedFieldSet);
|
|
135
|
+
}
|
|
136
|
+
newGroupedFieldSet.set(responseKey, fieldDetailsList);
|
|
137
|
+
}
|
|
138
|
+
return { groupedFieldSet, newGroupedFieldSets };
|
|
139
|
+
}
|
|
140
|
+
function getFilteredDeferUsageSet(fieldDetailsList) {
|
|
141
|
+
const filteredDeferUsageSet = new Set();
|
|
142
|
+
for (const fieldDetails of fieldDetailsList) {
|
|
143
|
+
const deferUsage = fieldDetails.deferUsage;
|
|
144
|
+
if (deferUsage === undefined) {
|
|
145
|
+
filteredDeferUsageSet.clear();
|
|
146
|
+
return filteredDeferUsageSet;
|
|
147
|
+
}
|
|
148
|
+
filteredDeferUsageSet.add(deferUsage);
|
|
149
|
+
}
|
|
150
|
+
for (const deferUsage of filteredDeferUsageSet) {
|
|
151
|
+
let parentDeferUsage = deferUsage.parentDeferUsage;
|
|
152
|
+
while (parentDeferUsage !== undefined) {
|
|
153
|
+
if (filteredDeferUsageSet.has(parentDeferUsage)) {
|
|
154
|
+
filteredDeferUsageSet.delete(deferUsage);
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
parentDeferUsage = parentDeferUsage.parentDeferUsage;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return filteredDeferUsageSet;
|
|
161
|
+
}
|
|
162
|
+
export function deferUsageForFieldNode(node) {
|
|
163
|
+
return deferUsageByFieldNode.get(node);
|
|
164
|
+
}
|
|
165
|
+
export function groupedFieldSetToFieldMap(groupedFieldSet) {
|
|
166
|
+
const fields = new Map();
|
|
167
|
+
for (const [responseKey, fieldDetailsList] of groupedFieldSet) {
|
|
168
|
+
fields.set(responseKey, fieldDetailsList.map(details => {
|
|
169
|
+
deferUsageByFieldNode.set(details.node, details.deferUsage);
|
|
170
|
+
return details.node;
|
|
171
|
+
}));
|
|
172
|
+
}
|
|
173
|
+
return fields;
|
|
174
|
+
}
|
|
175
|
+
export function rootDeferUsage(deferUsageSet) {
|
|
176
|
+
for (const usage of deferUsageSet) {
|
|
177
|
+
if (usage.parentDeferUsage == null || !deferUsageSet.has(usage.parentDeferUsage)) {
|
|
178
|
+
return usage;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
export function labelForDeferUsageSet(deferUsageSet) {
|
|
184
|
+
return rootDeferUsage(deferUsageSet)?.label;
|
|
185
|
+
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { DocumentNode, FieldNode, FragmentDefinitionNode, GraphQLError, GraphQLField, GraphQLFieldResolver, GraphQLFormattedError, GraphQLObjectType, GraphQLSchema, GraphQLTypeResolver, OperationDefinitionNode } from 'graphql';
|
|
1
|
+
import { defaultFieldResolver, defaultTypeResolver, DocumentNode, FieldNode, FragmentDefinitionNode, GraphQLError, GraphQLField, GraphQLFieldResolver, GraphQLFormattedError, GraphQLObjectType, GraphQLSchema, GraphQLTypeResolver, OperationDefinitionNode } from 'graphql';
|
|
2
2
|
import { GraphQLResolveInfo, Maybe, Path, VariableValues, type IncrementalSpec } from '@graphql-tools/utils';
|
|
3
3
|
import { TypedDocumentNode } from '@graphql-typed-document-node/core';
|
|
4
4
|
import { type MaybePromise } from '@whatwg-node/promise-helpers';
|
|
5
5
|
import { type AsyncPayloadRecord, type CompletedResult, type IncrementalPublisherState, type PendingResult } from './incremental.cjs';
|
|
6
|
+
export { defaultFieldResolver, defaultTypeResolver };
|
|
6
7
|
export type { CompletedResult, PendingResult } from './incremental.cjs';
|
|
7
8
|
export interface SingularExecutionResult<TData = any, TExtensions = any> {
|
|
8
9
|
errors?: ReadonlyArray<GraphQLError>;
|
|
@@ -50,6 +51,11 @@ export interface ExecutionContext<TVariables = any, TContext = any> {
|
|
|
50
51
|
subsequentPayloads: Set<AsyncPayloadRecord>;
|
|
51
52
|
incrementalSpec: IncrementalSpec;
|
|
52
53
|
publisher?: IncrementalPublisherState;
|
|
54
|
+
/**
|
|
55
|
+
* Allocated only when the operation actually uses @defer/@stream so
|
|
56
|
+
* default execution does not pay for null-bubble bookkeeping.
|
|
57
|
+
*/
|
|
58
|
+
nulledPaths?: Array<ReadonlyArray<string | number>>;
|
|
53
59
|
signal?: AbortSignal;
|
|
54
60
|
getAbortSignal: () => AbortSignal | undefined;
|
|
55
61
|
getAsyncHelpers: () => {
|
|
@@ -196,24 +202,6 @@ export declare function buildExecutionContext<TData = any, TVariables = any, TCo
|
|
|
196
202
|
*/
|
|
197
203
|
export declare function buildResolveInfo(exeContext: ExecutionContext, fieldDef: GraphQLField<unknown, unknown>, fieldNodes: Array<FieldNode>, parentType: GraphQLObjectType, path: Path): GraphQLResolveInfo;
|
|
198
204
|
export declare const CRITICAL_ERROR: 'CRITICAL_ERROR';
|
|
199
|
-
/**
|
|
200
|
-
* If a resolveType function is not given, then a default resolve behavior is
|
|
201
|
-
* used which attempts two strategies:
|
|
202
|
-
*
|
|
203
|
-
* First, See if the provided value has a `__typename` field defined, if so, use
|
|
204
|
-
* that value as name of the resolved type.
|
|
205
|
-
*
|
|
206
|
-
* Otherwise, test each possible type for the abstract type by calling
|
|
207
|
-
* isTypeOf for the object being coerced, returning the first type that matches.
|
|
208
|
-
*/
|
|
209
|
-
export declare const defaultTypeResolver: GraphQLTypeResolver<unknown, unknown>;
|
|
210
|
-
/**
|
|
211
|
-
* If a resolve function is not given, then a default resolve behavior is used
|
|
212
|
-
* which takes the property of the source object of the same name as the field
|
|
213
|
-
* and returns it as the result, or if it's a function, returns the result
|
|
214
|
-
* of calling that function while passing along args and context value.
|
|
215
|
-
*/
|
|
216
|
-
export declare const defaultFieldResolver: GraphQLFieldResolver<unknown, unknown>;
|
|
217
205
|
/**
|
|
218
206
|
* Implements the "Subscribe" algorithm described in the GraphQL specification,
|
|
219
207
|
* including `@defer` and `@stream` as proposed in
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { DocumentNode, FieldNode, FragmentDefinitionNode, GraphQLError, GraphQLField, GraphQLFieldResolver, GraphQLFormattedError, GraphQLObjectType, GraphQLSchema, GraphQLTypeResolver, OperationDefinitionNode } from 'graphql';
|
|
1
|
+
import { defaultFieldResolver, defaultTypeResolver, DocumentNode, FieldNode, FragmentDefinitionNode, GraphQLError, GraphQLField, GraphQLFieldResolver, GraphQLFormattedError, GraphQLObjectType, GraphQLSchema, GraphQLTypeResolver, OperationDefinitionNode } from 'graphql';
|
|
2
2
|
import { GraphQLResolveInfo, Maybe, Path, VariableValues, type IncrementalSpec } from '@graphql-tools/utils';
|
|
3
3
|
import { TypedDocumentNode } from '@graphql-typed-document-node/core';
|
|
4
4
|
import { type MaybePromise } from '@whatwg-node/promise-helpers';
|
|
5
5
|
import { type AsyncPayloadRecord, type CompletedResult, type IncrementalPublisherState, type PendingResult } from './incremental.js';
|
|
6
|
+
export { defaultFieldResolver, defaultTypeResolver };
|
|
6
7
|
export type { CompletedResult, PendingResult } from './incremental.js';
|
|
7
8
|
export interface SingularExecutionResult<TData = any, TExtensions = any> {
|
|
8
9
|
errors?: ReadonlyArray<GraphQLError>;
|
|
@@ -50,6 +51,11 @@ export interface ExecutionContext<TVariables = any, TContext = any> {
|
|
|
50
51
|
subsequentPayloads: Set<AsyncPayloadRecord>;
|
|
51
52
|
incrementalSpec: IncrementalSpec;
|
|
52
53
|
publisher?: IncrementalPublisherState;
|
|
54
|
+
/**
|
|
55
|
+
* Allocated only when the operation actually uses @defer/@stream so
|
|
56
|
+
* default execution does not pay for null-bubble bookkeeping.
|
|
57
|
+
*/
|
|
58
|
+
nulledPaths?: Array<ReadonlyArray<string | number>>;
|
|
53
59
|
signal?: AbortSignal;
|
|
54
60
|
getAbortSignal: () => AbortSignal | undefined;
|
|
55
61
|
getAsyncHelpers: () => {
|
|
@@ -196,24 +202,6 @@ export declare function buildExecutionContext<TData = any, TVariables = any, TCo
|
|
|
196
202
|
*/
|
|
197
203
|
export declare function buildResolveInfo(exeContext: ExecutionContext, fieldDef: GraphQLField<unknown, unknown>, fieldNodes: Array<FieldNode>, parentType: GraphQLObjectType, path: Path): GraphQLResolveInfo;
|
|
198
204
|
export declare const CRITICAL_ERROR: 'CRITICAL_ERROR';
|
|
199
|
-
/**
|
|
200
|
-
* If a resolveType function is not given, then a default resolve behavior is
|
|
201
|
-
* used which attempts two strategies:
|
|
202
|
-
*
|
|
203
|
-
* First, See if the provided value has a `__typename` field defined, if so, use
|
|
204
|
-
* that value as name of the resolved type.
|
|
205
|
-
*
|
|
206
|
-
* Otherwise, test each possible type for the abstract type by calling
|
|
207
|
-
* isTypeOf for the object being coerced, returning the first type that matches.
|
|
208
|
-
*/
|
|
209
|
-
export declare const defaultTypeResolver: GraphQLTypeResolver<unknown, unknown>;
|
|
210
|
-
/**
|
|
211
|
-
* If a resolve function is not given, then a default resolve behavior is used
|
|
212
|
-
* which takes the property of the source object of the same name as the field
|
|
213
|
-
* and returns it as the result, or if it's a function, returns the result
|
|
214
|
-
* of calling that function while passing along args and context value.
|
|
215
|
-
*/
|
|
216
|
-
export declare const defaultFieldResolver: GraphQLFieldResolver<unknown, unknown>;
|
|
217
205
|
/**
|
|
218
206
|
* Implements the "Subscribe" algorithm described in the GraphQL specification,
|
|
219
207
|
* including `@defer` and `@stream` as proposed in
|
|
@@ -2,6 +2,7 @@ import { GraphQLError } from 'graphql';
|
|
|
2
2
|
import { Path, type IncrementalSpec } from '@graphql-tools/utils';
|
|
3
3
|
import { type MaybePromise } from '@whatwg-node/promise-helpers';
|
|
4
4
|
import type { SubsequentIncrementalExecutionResult } from './execute.cjs';
|
|
5
|
+
import type { DeferUsage } from './incrementalCollect.cjs';
|
|
5
6
|
export interface PendingResult {
|
|
6
7
|
id: string;
|
|
7
8
|
path: ReadonlyArray<string | number>;
|
|
@@ -23,6 +24,11 @@ export interface IncrementalPublisherContext {
|
|
|
23
24
|
incrementalSpec: IncrementalSpec;
|
|
24
25
|
publisher?: IncrementalPublisherState;
|
|
25
26
|
signalPromise?: Promise<never>;
|
|
27
|
+
/**
|
|
28
|
+
* Paths that were set to null by error bubbling. Present only on the
|
|
29
|
+
* incremental collect path so default execution does not allocate.
|
|
30
|
+
*/
|
|
31
|
+
nulledPaths?: Array<ReadonlyArray<string | number>>;
|
|
26
32
|
}
|
|
27
33
|
export declare class StreamGroup {
|
|
28
34
|
path: Array<string | number>;
|
|
@@ -48,6 +54,21 @@ export declare function createStreamGroupIfV02(exeContext: IncrementalPublisherC
|
|
|
48
54
|
hasIterator?: boolean;
|
|
49
55
|
}): StreamGroup | undefined;
|
|
50
56
|
export declare function deleteAsyncPayload(exeContext: IncrementalPublisherContext, record: AsyncPayloadRecord): void;
|
|
57
|
+
export declare function pathStartsWith(path: ReadonlyArray<string | number>, prefix: ReadonlyArray<string | number>): boolean;
|
|
58
|
+
export declare function isAtOrUnderNulledPath(path: ReadonlyArray<string | number>, nulledPaths: ReadonlyArray<ReadonlyArray<string | number>> | undefined): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Drop keys that would write into a subtree already nulled by error bubbling.
|
|
61
|
+
* Ancestor deferred records can still deliver sibling fields (e.g. `someField`
|
|
62
|
+
* next to a nulled `b.c`).
|
|
63
|
+
*/
|
|
64
|
+
export declare function pruneUnmergeableIncrementalData(data: unknown, basePath: ReadonlyArray<string | number>, nulledPaths: ReadonlyArray<ReadonlyArray<string | number>> | undefined): unknown;
|
|
65
|
+
/**
|
|
66
|
+
* Cancel a subsequent record whose path is at or under a nulled position
|
|
67
|
+
* (graphql-js 17 `hasNulledPosition`). v0.2 records that were already
|
|
68
|
+
* announced stay in the set so the publisher can emit `completed` without
|
|
69
|
+
* incremental data.
|
|
70
|
+
*/
|
|
71
|
+
export declare function cancelAsyncPayload(exeContext: IncrementalPublisherContext, record: AsyncPayloadRecord): void;
|
|
51
72
|
export declare class DeferredFragmentRecord {
|
|
52
73
|
type: 'defer';
|
|
53
74
|
errors: Array<GraphQLError>;
|
|
@@ -57,7 +78,9 @@ export declare class DeferredFragmentRecord {
|
|
|
57
78
|
data: Record<string, unknown> | null;
|
|
58
79
|
parentContext: AsyncPayloadRecord | undefined;
|
|
59
80
|
deliveryParent: AsyncPayloadRecord | undefined;
|
|
81
|
+
deferUsage: DeferUsage | undefined;
|
|
60
82
|
isCompleted: boolean;
|
|
83
|
+
cancelled: boolean;
|
|
61
84
|
_exeContext: IncrementalPublisherContext;
|
|
62
85
|
_resolve?: (arg: MaybePromise<Record<string, unknown> | null>) => void;
|
|
63
86
|
constructor(opts: {
|
|
@@ -66,6 +89,7 @@ export declare class DeferredFragmentRecord {
|
|
|
66
89
|
parentContext: AsyncPayloadRecord | undefined;
|
|
67
90
|
deliveryParent?: AsyncPayloadRecord | undefined;
|
|
68
91
|
exeContext: IncrementalPublisherContext;
|
|
92
|
+
deferUsage?: DeferUsage;
|
|
69
93
|
});
|
|
70
94
|
addData(data: MaybePromise<Record<string, unknown> | null>): void;
|
|
71
95
|
}
|
|
@@ -80,6 +104,7 @@ export declare class StreamRecord {
|
|
|
80
104
|
iterator: AsyncIterator<unknown> | undefined;
|
|
81
105
|
isCompletedIterator?: boolean;
|
|
82
106
|
isCompleted: boolean;
|
|
107
|
+
cancelled: boolean;
|
|
83
108
|
streamGroup: StreamGroup | undefined;
|
|
84
109
|
_exeContext: IncrementalPublisherContext;
|
|
85
110
|
_resolve?: (arg: MaybePromise<Array<unknown> | null>) => void;
|
|
@@ -2,6 +2,7 @@ import { GraphQLError } from 'graphql';
|
|
|
2
2
|
import { Path, type IncrementalSpec } from '@graphql-tools/utils';
|
|
3
3
|
import { type MaybePromise } from '@whatwg-node/promise-helpers';
|
|
4
4
|
import type { SubsequentIncrementalExecutionResult } from './execute.js';
|
|
5
|
+
import type { DeferUsage } from './incrementalCollect.js';
|
|
5
6
|
export interface PendingResult {
|
|
6
7
|
id: string;
|
|
7
8
|
path: ReadonlyArray<string | number>;
|
|
@@ -23,6 +24,11 @@ export interface IncrementalPublisherContext {
|
|
|
23
24
|
incrementalSpec: IncrementalSpec;
|
|
24
25
|
publisher?: IncrementalPublisherState;
|
|
25
26
|
signalPromise?: Promise<never>;
|
|
27
|
+
/**
|
|
28
|
+
* Paths that were set to null by error bubbling. Present only on the
|
|
29
|
+
* incremental collect path so default execution does not allocate.
|
|
30
|
+
*/
|
|
31
|
+
nulledPaths?: Array<ReadonlyArray<string | number>>;
|
|
26
32
|
}
|
|
27
33
|
export declare class StreamGroup {
|
|
28
34
|
path: Array<string | number>;
|
|
@@ -48,6 +54,21 @@ export declare function createStreamGroupIfV02(exeContext: IncrementalPublisherC
|
|
|
48
54
|
hasIterator?: boolean;
|
|
49
55
|
}): StreamGroup | undefined;
|
|
50
56
|
export declare function deleteAsyncPayload(exeContext: IncrementalPublisherContext, record: AsyncPayloadRecord): void;
|
|
57
|
+
export declare function pathStartsWith(path: ReadonlyArray<string | number>, prefix: ReadonlyArray<string | number>): boolean;
|
|
58
|
+
export declare function isAtOrUnderNulledPath(path: ReadonlyArray<string | number>, nulledPaths: ReadonlyArray<ReadonlyArray<string | number>> | undefined): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Drop keys that would write into a subtree already nulled by error bubbling.
|
|
61
|
+
* Ancestor deferred records can still deliver sibling fields (e.g. `someField`
|
|
62
|
+
* next to a nulled `b.c`).
|
|
63
|
+
*/
|
|
64
|
+
export declare function pruneUnmergeableIncrementalData(data: unknown, basePath: ReadonlyArray<string | number>, nulledPaths: ReadonlyArray<ReadonlyArray<string | number>> | undefined): unknown;
|
|
65
|
+
/**
|
|
66
|
+
* Cancel a subsequent record whose path is at or under a nulled position
|
|
67
|
+
* (graphql-js 17 `hasNulledPosition`). v0.2 records that were already
|
|
68
|
+
* announced stay in the set so the publisher can emit `completed` without
|
|
69
|
+
* incremental data.
|
|
70
|
+
*/
|
|
71
|
+
export declare function cancelAsyncPayload(exeContext: IncrementalPublisherContext, record: AsyncPayloadRecord): void;
|
|
51
72
|
export declare class DeferredFragmentRecord {
|
|
52
73
|
type: 'defer';
|
|
53
74
|
errors: Array<GraphQLError>;
|
|
@@ -57,7 +78,9 @@ export declare class DeferredFragmentRecord {
|
|
|
57
78
|
data: Record<string, unknown> | null;
|
|
58
79
|
parentContext: AsyncPayloadRecord | undefined;
|
|
59
80
|
deliveryParent: AsyncPayloadRecord | undefined;
|
|
81
|
+
deferUsage: DeferUsage | undefined;
|
|
60
82
|
isCompleted: boolean;
|
|
83
|
+
cancelled: boolean;
|
|
61
84
|
_exeContext: IncrementalPublisherContext;
|
|
62
85
|
_resolve?: (arg: MaybePromise<Record<string, unknown> | null>) => void;
|
|
63
86
|
constructor(opts: {
|
|
@@ -66,6 +89,7 @@ export declare class DeferredFragmentRecord {
|
|
|
66
89
|
parentContext: AsyncPayloadRecord | undefined;
|
|
67
90
|
deliveryParent?: AsyncPayloadRecord | undefined;
|
|
68
91
|
exeContext: IncrementalPublisherContext;
|
|
92
|
+
deferUsage?: DeferUsage;
|
|
69
93
|
});
|
|
70
94
|
addData(data: MaybePromise<Record<string, unknown> | null>): void;
|
|
71
95
|
}
|
|
@@ -80,6 +104,7 @@ export declare class StreamRecord {
|
|
|
80
104
|
iterator: AsyncIterator<unknown> | undefined;
|
|
81
105
|
isCompletedIterator?: boolean;
|
|
82
106
|
isCompleted: boolean;
|
|
107
|
+
cancelled: boolean;
|
|
83
108
|
streamGroup: StreamGroup | undefined;
|
|
84
109
|
_exeContext: IncrementalPublisherContext;
|
|
85
110
|
_resolve?: (arg: MaybePromise<Array<unknown> | null>) => void;
|