@graphql-tools/executor 2.0.0-alpha-20240702193427-1ce4b1efdef732d2635b657df6273a975a19d217 → 2.0.0-alpha-20240708182941-ed27153e85ecdb79083b1d124d470573a65893fd

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.
@@ -33,6 +33,20 @@ class IncrementalGraph {
33
33
  this._addIncrementalDataRecords(incrementalDataRecords, reconcilableResult.deferredGroupedFieldSetRecord.deferredFragmentRecords);
34
34
  }
35
35
  }
36
+ currentCompletedIncrementalData() {
37
+ return {
38
+ [Symbol.iterator]() {
39
+ return this;
40
+ },
41
+ next: () => {
42
+ const value = this._completedQueue.shift();
43
+ if (value !== undefined) {
44
+ return { value, done: false };
45
+ }
46
+ return { value: undefined, done: true };
47
+ },
48
+ };
49
+ }
36
50
  completedIncrementalData() {
37
51
  return {
38
52
  [Symbol.asyncIterator]() {
@@ -70,13 +70,11 @@ class IncrementalPublisher {
70
70
  incremental: [],
71
71
  completed: [],
72
72
  };
73
+ let currentCompletedIncrementalData = this._incrementalGraph.currentCompletedIncrementalData();
73
74
  const completedIncrementalData = this._incrementalGraph.completedIncrementalData();
74
- // use the raw iterator rather than 'for await ... of' so as not to trigger the
75
- // '.return()' method on the iterator when exiting the loop with the next value
76
75
  const asyncIterator = completedIncrementalData[Symbol.asyncIterator]();
77
- let iteration = await asyncIterator.next();
78
- while (!iteration.done) {
79
- for (const completedResult of iteration.value) {
76
+ do {
77
+ for (const completedResult of currentCompletedIncrementalData) {
80
78
  this._handleCompletedIncrementalData(completedResult, context);
81
79
  }
82
80
  const { incremental, completed } = context;
@@ -100,8 +98,9 @@ class IncrementalPublisher {
100
98
  }
101
99
  return { value: subsequentIncrementalExecutionResult, done: false };
102
100
  }
103
- iteration = await asyncIterator.next();
104
- }
101
+ const iteration = await asyncIterator.next();
102
+ currentCompletedIncrementalData = iteration.value;
103
+ } while (currentCompletedIncrementalData !== undefined);
105
104
  if (this._context.signal?.aborted) {
106
105
  throw this._context.signal.reason;
107
106
  }
@@ -145,10 +144,28 @@ class IncrementalPublisher {
145
144
  continue;
146
145
  }
147
146
  (0, invariant_js_1.invariant)(id !== undefined);
148
- context.completed.push({
149
- id,
150
- errors: deferredGroupedFieldSetResult.errors,
151
- });
147
+ if (this._context.sendIncrementalErrorsAsNull) {
148
+ const incrementalEntry = {
149
+ id,
150
+ data: null,
151
+ errors: deferredGroupedFieldSetResult.errors,
152
+ };
153
+ if (this._context.sendPathAndLabelOnIncremental) {
154
+ const { path, label } = deferredFragmentRecord;
155
+ incrementalEntry.path = (0, utils_1.pathToArray)(path);
156
+ if (label !== undefined) {
157
+ incrementalEntry.label = label;
158
+ }
159
+ }
160
+ context.incremental.push(incrementalEntry);
161
+ context.completed.push({ id });
162
+ }
163
+ else {
164
+ context.completed.push({
165
+ id,
166
+ errors: deferredGroupedFieldSetResult.errors,
167
+ });
168
+ }
152
169
  }
153
170
  return;
154
171
  }
@@ -170,6 +187,13 @@ class IncrementalPublisher {
170
187
  ...reconcilableResult.result,
171
188
  id: bestId,
172
189
  };
190
+ if (this._context.sendPathAndLabelOnIncremental) {
191
+ const { path, label } = deferredFragmentRecord;
192
+ incrementalEntry.path = (0, utils_1.pathToArray)(path);
193
+ if (label !== undefined) {
194
+ incrementalEntry.label = label;
195
+ }
196
+ }
173
197
  if (subPath !== undefined) {
174
198
  incrementalEntry.subPath = subPath;
175
199
  }
@@ -183,10 +207,28 @@ class IncrementalPublisher {
183
207
  const id = streamRecord.id;
184
208
  (0, invariant_js_1.invariant)(id !== undefined);
185
209
  if (streamItemsResult.errors !== undefined) {
186
- context.completed.push({
187
- id,
188
- errors: streamItemsResult.errors,
189
- });
210
+ if (this._context.sendIncrementalErrorsAsNull) {
211
+ const incrementalEntry = {
212
+ items: null,
213
+ id,
214
+ errors: streamItemsResult.errors,
215
+ };
216
+ if (this._context.sendPathAndLabelOnIncremental) {
217
+ const { path, label, index } = streamRecord;
218
+ incrementalEntry.path = (0, utils_1.pathToArray)((0, utils_1.addPath)(path, index, undefined));
219
+ if (label !== undefined) {
220
+ incrementalEntry.label = label;
221
+ }
222
+ }
223
+ context.incremental.push(incrementalEntry);
224
+ context.completed.push({ id });
225
+ }
226
+ else {
227
+ context.completed.push({
228
+ id,
229
+ errors: streamItemsResult.errors,
230
+ });
231
+ }
190
232
  this._incrementalGraph.removeStream(streamRecord);
191
233
  if ((0, types_js_1.isCancellableStreamRecord)(streamRecord)) {
192
234
  (0, invariant_js_1.invariant)(this._context.cancellableStreams !== undefined);
@@ -206,10 +248,19 @@ class IncrementalPublisher {
206
248
  }
207
249
  }
208
250
  else {
251
+ const bareResult = streamItemsResult.result;
209
252
  const incrementalEntry = {
210
253
  id,
211
- ...streamItemsResult.result,
254
+ ...bareResult,
212
255
  };
256
+ if (this._context.sendPathAndLabelOnIncremental) {
257
+ const { path, label, index } = streamRecord;
258
+ incrementalEntry.path = (0, utils_1.pathToArray)((0, utils_1.addPath)(path, index, undefined));
259
+ streamRecord.index += bareResult.items.length;
260
+ if (label !== undefined) {
261
+ incrementalEntry.label = label;
262
+ }
263
+ }
213
264
  context.incremental.push(incrementalEntry);
214
265
  const incrementalDataRecords = streamItemsResult.incrementalDataRecords;
215
266
  if (incrementalDataRecords !== undefined) {
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildFieldPlan = void 0;
3
+ exports.buildBranchingFieldPlan = exports.buildFieldPlan = void 0;
4
+ const AccumulatorMap_js_1 = require("./AccumulatorMap.js");
4
5
  const getBySet_js_1 = require("./getBySet.js");
5
6
  const isSameSet_js_1 = require("./isSameSet.js");
6
7
  function buildFieldPlan(originalGroupedFieldSet, parentDeferUsages = new Set()) {
@@ -60,3 +61,29 @@ function getAncestors(deferUsage) {
60
61
  }
61
62
  return ancestors;
62
63
  }
64
+ function buildBranchingFieldPlan(originalGroupedFieldSet, parentDeferUsages = new Set()) {
65
+ const groupedFieldSet = new AccumulatorMap_js_1.AccumulatorMap();
66
+ const newGroupedFieldSets = new Map();
67
+ for (const [responseKey, fieldGroup] of originalGroupedFieldSet) {
68
+ for (const fieldDetails of fieldGroup) {
69
+ const deferUsage = fieldDetails.deferUsage;
70
+ const deferUsageSet = deferUsage === undefined ? new Set() : new Set([deferUsage]);
71
+ if ((0, isSameSet_js_1.isSameSet)(parentDeferUsages, deferUsageSet)) {
72
+ groupedFieldSet.add(responseKey, fieldDetails);
73
+ }
74
+ else {
75
+ let newGroupedFieldSet = (0, getBySet_js_1.getBySet)(newGroupedFieldSets, deferUsageSet);
76
+ if (newGroupedFieldSet === undefined) {
77
+ newGroupedFieldSet = new AccumulatorMap_js_1.AccumulatorMap();
78
+ newGroupedFieldSets.set(deferUsageSet, newGroupedFieldSet);
79
+ }
80
+ newGroupedFieldSet.add(responseKey, fieldDetails);
81
+ }
82
+ }
83
+ }
84
+ return {
85
+ groupedFieldSet,
86
+ newGroupedFieldSets,
87
+ };
88
+ }
89
+ exports.buildBranchingFieldPlan = buildBranchingFieldPlan;
@@ -150,7 +150,7 @@ exports.getFragmentsFromDocument = (0, utils_1.memoize1)(function getFragmentsFr
150
150
  * @internal
151
151
  */
152
152
  function buildExecutionContext(args) {
153
- const { schema, document, rootValue, contextValue, variableValues: rawVariableValues, operationName, fieldResolver, typeResolver, subscribeFieldResolver, enableEarlyExecution, errorWithIncrementalSubscription, signal, } = args;
153
+ const { schema, document, rootValue, contextValue, variableValues: rawVariableValues, operationName, fieldResolver, typeResolver, subscribeFieldResolver, enableEarlyExecution, deduplicateDefers, sendIncrementalErrorsAsNull, sendPathAndLabelOnIncremental, errorWithIncrementalSubscription, signal, } = args;
154
154
  // If the schema used for execution is invalid, throw an error.
155
155
  (0, graphql_1.assertValidSchema)(schema);
156
156
  const fragments = (0, exports.getFragmentsFromDocument)(document);
@@ -200,6 +200,9 @@ function buildExecutionContext(args) {
200
200
  typeResolver: typeResolver ?? exports.defaultTypeResolver,
201
201
  subscribeFieldResolver: subscribeFieldResolver ?? exports.defaultFieldResolver,
202
202
  enableEarlyExecution: enableEarlyExecution !== false,
203
+ deduplicateDefers: deduplicateDefers !== false,
204
+ sendIncrementalErrorsAsNull: sendIncrementalErrorsAsNull === true,
205
+ sendPathAndLabelOnIncremental: sendPathAndLabelOnIncremental === true,
203
206
  errorWithIncrementalSubscription: operation.operation === 'subscription' && errorWithIncrementalSubscription !== false,
204
207
  signal,
205
208
  errors: undefined,
@@ -223,7 +226,7 @@ function executeOperation(exeContext) {
223
226
  throw exeContext.signal.reason;
224
227
  }
225
228
  try {
226
- const { operation, schema, fragments, variableValues, rootValue, errorWithIncrementalSubscription, } = exeContext;
229
+ const { operation, schema, fragments, variableValues, rootValue, deduplicateDefers, errorWithIncrementalSubscription, } = exeContext;
227
230
  const rootType = (0, utils_1.getDefinedRootType)(schema, operation.operation, [operation]);
228
231
  if (rootType == null) {
229
232
  (0, utils_1.createGraphQLError)(`Schema is not configured to execute ${operation.operation} operation.`, {
@@ -238,7 +241,9 @@ function executeOperation(exeContext) {
238
241
  data = executeRootGroupedFieldSet(exeContext, operation.operation, rootType, rootValue, groupedFieldSet, undefined);
239
242
  }
240
243
  else {
241
- const fieldPLan = (0, buildFieldPlan_js_1.buildFieldPlan)(groupedFieldSet);
244
+ const fieldPLan = deduplicateDefers
245
+ ? (0, buildFieldPlan_js_1.buildFieldPlan)(groupedFieldSet)
246
+ : (0, buildFieldPlan_js_1.buildBranchingFieldPlan)(groupedFieldSet);
242
247
  groupedFieldSet = fieldPLan.groupedFieldSet;
243
248
  const newGroupedFieldSets = fieldPLan.newGroupedFieldSets;
244
249
  const newDeferMap = addNewDeferredFragments(newDeferUsages, new Map());
@@ -573,6 +578,7 @@ async function completeAsyncIteratorValue(exeContext, itemType, fieldGroup, info
573
578
  streamRecord = {
574
579
  label: streamUsage.label,
575
580
  path,
581
+ index,
576
582
  streamItemQueue,
577
583
  };
578
584
  }
@@ -580,6 +586,7 @@ async function completeAsyncIteratorValue(exeContext, itemType, fieldGroup, info
580
586
  streamRecord = {
581
587
  label: streamUsage.label,
582
588
  path,
589
+ index,
583
590
  streamItemQueue,
584
591
  earlyReturn: returnFn.bind(asyncIterator),
585
592
  };
@@ -657,6 +664,7 @@ function completeIterableValue(exeContext, itemType, fieldGroup, info, path, ite
657
664
  const streamRecord = {
658
665
  label: streamUsage.label,
659
666
  path,
667
+ index,
660
668
  streamItemQueue: buildSyncStreamItemQueue(item, index, path, iterator, exeContext, streamUsage.fieldGroup, info, itemType),
661
669
  };
662
670
  const context = incrementalContext ?? exeContext;
@@ -834,7 +842,7 @@ function collectAndExecuteSubfields(exeContext, returnType, fieldGroup, path, re
834
842
  if (deferMap === undefined && newDeferUsages.length === 0) {
835
843
  return executeFields(exeContext, returnType, result, path, groupedFieldSet, incrementalContext, undefined);
836
844
  }
837
- const subFieldPlan = buildSubFieldPlan(groupedFieldSet, incrementalContext?.deferUsageSet);
845
+ const subFieldPlan = buildSubFieldPlan(groupedFieldSet, incrementalContext?.deferUsageSet, exeContext.deduplicateDefers);
838
846
  groupedFieldSet = subFieldPlan.groupedFieldSet;
839
847
  const newGroupedFieldSets = subFieldPlan.newGroupedFieldSets;
840
848
  const newDeferMap = addNewDeferredFragments(newDeferUsages, new Map(deferMap), path);
@@ -846,12 +854,14 @@ function collectAndExecuteSubfields(exeContext, returnType, fieldGroup, path, re
846
854
  }
847
855
  return subFields;
848
856
  }
849
- function buildSubFieldPlan(originalGroupedFieldSet, deferUsageSet) {
857
+ function buildSubFieldPlan(originalGroupedFieldSet, deferUsageSet, deduplicateDefers) {
850
858
  let fieldPlan = originalGroupedFieldSet._fieldPlan;
851
859
  if (fieldPlan !== undefined) {
852
860
  return fieldPlan;
853
861
  }
854
- fieldPlan = (0, buildFieldPlan_js_1.buildFieldPlan)(originalGroupedFieldSet, deferUsageSet);
862
+ fieldPlan = deduplicateDefers
863
+ ? (0, buildFieldPlan_js_1.buildFieldPlan)(originalGroupedFieldSet, deferUsageSet)
864
+ : (0, buildFieldPlan_js_1.buildBranchingFieldPlan)(originalGroupedFieldSet, deferUsageSet);
855
865
  originalGroupedFieldSet._fieldPlan = fieldPlan;
856
866
  return fieldPlan;
857
867
  }
@@ -30,6 +30,20 @@ export class IncrementalGraph {
30
30
  this._addIncrementalDataRecords(incrementalDataRecords, reconcilableResult.deferredGroupedFieldSetRecord.deferredFragmentRecords);
31
31
  }
32
32
  }
33
+ currentCompletedIncrementalData() {
34
+ return {
35
+ [Symbol.iterator]() {
36
+ return this;
37
+ },
38
+ next: () => {
39
+ const value = this._completedQueue.shift();
40
+ if (value !== undefined) {
41
+ return { value, done: false };
42
+ }
43
+ return { value: undefined, done: true };
44
+ },
45
+ };
46
+ }
33
47
  completedIncrementalData() {
34
48
  return {
35
49
  [Symbol.asyncIterator]() {
@@ -1,4 +1,4 @@
1
- import { pathToArray } from '@graphql-tools/utils';
1
+ import { addPath, pathToArray } from '@graphql-tools/utils';
2
2
  import { IncrementalGraph } from './IncrementalGraph.js';
3
3
  import { invariant } from './invariant.js';
4
4
  import { isCancellableStreamRecord, isDeferredGroupedFieldSetResult, isNonReconcilableDeferredGroupedFieldSetResult, } from './types.js';
@@ -66,13 +66,11 @@ class IncrementalPublisher {
66
66
  incremental: [],
67
67
  completed: [],
68
68
  };
69
+ let currentCompletedIncrementalData = this._incrementalGraph.currentCompletedIncrementalData();
69
70
  const completedIncrementalData = this._incrementalGraph.completedIncrementalData();
70
- // use the raw iterator rather than 'for await ... of' so as not to trigger the
71
- // '.return()' method on the iterator when exiting the loop with the next value
72
71
  const asyncIterator = completedIncrementalData[Symbol.asyncIterator]();
73
- let iteration = await asyncIterator.next();
74
- while (!iteration.done) {
75
- for (const completedResult of iteration.value) {
72
+ do {
73
+ for (const completedResult of currentCompletedIncrementalData) {
76
74
  this._handleCompletedIncrementalData(completedResult, context);
77
75
  }
78
76
  const { incremental, completed } = context;
@@ -96,8 +94,9 @@ class IncrementalPublisher {
96
94
  }
97
95
  return { value: subsequentIncrementalExecutionResult, done: false };
98
96
  }
99
- iteration = await asyncIterator.next();
100
- }
97
+ const iteration = await asyncIterator.next();
98
+ currentCompletedIncrementalData = iteration.value;
99
+ } while (currentCompletedIncrementalData !== undefined);
101
100
  if (this._context.signal?.aborted) {
102
101
  throw this._context.signal.reason;
103
102
  }
@@ -141,10 +140,28 @@ class IncrementalPublisher {
141
140
  continue;
142
141
  }
143
142
  invariant(id !== undefined);
144
- context.completed.push({
145
- id,
146
- errors: deferredGroupedFieldSetResult.errors,
147
- });
143
+ if (this._context.sendIncrementalErrorsAsNull) {
144
+ const incrementalEntry = {
145
+ id,
146
+ data: null,
147
+ errors: deferredGroupedFieldSetResult.errors,
148
+ };
149
+ if (this._context.sendPathAndLabelOnIncremental) {
150
+ const { path, label } = deferredFragmentRecord;
151
+ incrementalEntry.path = pathToArray(path);
152
+ if (label !== undefined) {
153
+ incrementalEntry.label = label;
154
+ }
155
+ }
156
+ context.incremental.push(incrementalEntry);
157
+ context.completed.push({ id });
158
+ }
159
+ else {
160
+ context.completed.push({
161
+ id,
162
+ errors: deferredGroupedFieldSetResult.errors,
163
+ });
164
+ }
148
165
  }
149
166
  return;
150
167
  }
@@ -166,6 +183,13 @@ class IncrementalPublisher {
166
183
  ...reconcilableResult.result,
167
184
  id: bestId,
168
185
  };
186
+ if (this._context.sendPathAndLabelOnIncremental) {
187
+ const { path, label } = deferredFragmentRecord;
188
+ incrementalEntry.path = pathToArray(path);
189
+ if (label !== undefined) {
190
+ incrementalEntry.label = label;
191
+ }
192
+ }
169
193
  if (subPath !== undefined) {
170
194
  incrementalEntry.subPath = subPath;
171
195
  }
@@ -179,10 +203,28 @@ class IncrementalPublisher {
179
203
  const id = streamRecord.id;
180
204
  invariant(id !== undefined);
181
205
  if (streamItemsResult.errors !== undefined) {
182
- context.completed.push({
183
- id,
184
- errors: streamItemsResult.errors,
185
- });
206
+ if (this._context.sendIncrementalErrorsAsNull) {
207
+ const incrementalEntry = {
208
+ items: null,
209
+ id,
210
+ errors: streamItemsResult.errors,
211
+ };
212
+ if (this._context.sendPathAndLabelOnIncremental) {
213
+ const { path, label, index } = streamRecord;
214
+ incrementalEntry.path = pathToArray(addPath(path, index, undefined));
215
+ if (label !== undefined) {
216
+ incrementalEntry.label = label;
217
+ }
218
+ }
219
+ context.incremental.push(incrementalEntry);
220
+ context.completed.push({ id });
221
+ }
222
+ else {
223
+ context.completed.push({
224
+ id,
225
+ errors: streamItemsResult.errors,
226
+ });
227
+ }
186
228
  this._incrementalGraph.removeStream(streamRecord);
187
229
  if (isCancellableStreamRecord(streamRecord)) {
188
230
  invariant(this._context.cancellableStreams !== undefined);
@@ -202,10 +244,19 @@ class IncrementalPublisher {
202
244
  }
203
245
  }
204
246
  else {
247
+ const bareResult = streamItemsResult.result;
205
248
  const incrementalEntry = {
206
249
  id,
207
- ...streamItemsResult.result,
250
+ ...bareResult,
208
251
  };
252
+ if (this._context.sendPathAndLabelOnIncremental) {
253
+ const { path, label, index } = streamRecord;
254
+ incrementalEntry.path = pathToArray(addPath(path, index, undefined));
255
+ streamRecord.index += bareResult.items.length;
256
+ if (label !== undefined) {
257
+ incrementalEntry.label = label;
258
+ }
259
+ }
209
260
  context.incremental.push(incrementalEntry);
210
261
  const incrementalDataRecords = streamItemsResult.incrementalDataRecords;
211
262
  if (incrementalDataRecords !== undefined) {
@@ -1,3 +1,4 @@
1
+ import { AccumulatorMap } from './AccumulatorMap.js';
1
2
  import { getBySet } from './getBySet.js';
2
3
  import { isSameSet } from './isSameSet.js';
3
4
  export function buildFieldPlan(originalGroupedFieldSet, parentDeferUsages = new Set()) {
@@ -56,3 +57,28 @@ function getAncestors(deferUsage) {
56
57
  }
57
58
  return ancestors;
58
59
  }
60
+ export function buildBranchingFieldPlan(originalGroupedFieldSet, parentDeferUsages = new Set()) {
61
+ const groupedFieldSet = new AccumulatorMap();
62
+ const newGroupedFieldSets = new Map();
63
+ for (const [responseKey, fieldGroup] of originalGroupedFieldSet) {
64
+ for (const fieldDetails of fieldGroup) {
65
+ const deferUsage = fieldDetails.deferUsage;
66
+ const deferUsageSet = deferUsage === undefined ? new Set() : new Set([deferUsage]);
67
+ if (isSameSet(parentDeferUsages, deferUsageSet)) {
68
+ groupedFieldSet.add(responseKey, fieldDetails);
69
+ }
70
+ else {
71
+ let newGroupedFieldSet = getBySet(newGroupedFieldSets, deferUsageSet);
72
+ if (newGroupedFieldSet === undefined) {
73
+ newGroupedFieldSet = new AccumulatorMap();
74
+ newGroupedFieldSets.set(deferUsageSet, newGroupedFieldSet);
75
+ }
76
+ newGroupedFieldSet.add(responseKey, fieldDetails);
77
+ }
78
+ }
79
+ }
80
+ return {
81
+ groupedFieldSet,
82
+ newGroupedFieldSets,
83
+ };
84
+ }
@@ -1,7 +1,7 @@
1
1
  import { assertValidSchema, getDirectiveValues, GraphQLError, isAbstractType, isLeafType, isListType, isNonNullType, isObjectType, Kind, locatedError, SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef, versionInfo, } from 'graphql';
2
2
  import { addPath, createGraphQLError, getArgumentValues, getDefinedRootType, GraphQLStreamDirective, inspect, isAsyncIterable, isIterableObject, isObjectLike, isPromise, mapAsyncIterator, memoize1, memoize3, pathToArray, promiseReduce, } from '@graphql-tools/utils';
3
3
  import { BoxedPromiseOrValue } from './BoxedPromiseOrValue.js';
4
- import { buildFieldPlan } from './buildFieldPlan.js';
4
+ import { buildBranchingFieldPlan, buildFieldPlan, } from './buildFieldPlan.js';
5
5
  import { coerceError } from './coerceError.js';
6
6
  import { collectSubfields as _collectSubfields, collectFields, } from './collectFields.js';
7
7
  import { flattenAsyncIterable } from './flattenAsyncIterable.js';
@@ -144,7 +144,7 @@ export const getFragmentsFromDocument = memoize1(function getFragmentsFromDocume
144
144
  * @internal
145
145
  */
146
146
  export function buildExecutionContext(args) {
147
- const { schema, document, rootValue, contextValue, variableValues: rawVariableValues, operationName, fieldResolver, typeResolver, subscribeFieldResolver, enableEarlyExecution, errorWithIncrementalSubscription, signal, } = args;
147
+ const { schema, document, rootValue, contextValue, variableValues: rawVariableValues, operationName, fieldResolver, typeResolver, subscribeFieldResolver, enableEarlyExecution, deduplicateDefers, sendIncrementalErrorsAsNull, sendPathAndLabelOnIncremental, errorWithIncrementalSubscription, signal, } = args;
148
148
  // If the schema used for execution is invalid, throw an error.
149
149
  assertValidSchema(schema);
150
150
  const fragments = getFragmentsFromDocument(document);
@@ -194,6 +194,9 @@ export function buildExecutionContext(args) {
194
194
  typeResolver: typeResolver ?? defaultTypeResolver,
195
195
  subscribeFieldResolver: subscribeFieldResolver ?? defaultFieldResolver,
196
196
  enableEarlyExecution: enableEarlyExecution !== false,
197
+ deduplicateDefers: deduplicateDefers !== false,
198
+ sendIncrementalErrorsAsNull: sendIncrementalErrorsAsNull === true,
199
+ sendPathAndLabelOnIncremental: sendPathAndLabelOnIncremental === true,
197
200
  errorWithIncrementalSubscription: operation.operation === 'subscription' && errorWithIncrementalSubscription !== false,
198
201
  signal,
199
202
  errors: undefined,
@@ -216,7 +219,7 @@ function executeOperation(exeContext) {
216
219
  throw exeContext.signal.reason;
217
220
  }
218
221
  try {
219
- const { operation, schema, fragments, variableValues, rootValue, errorWithIncrementalSubscription, } = exeContext;
222
+ const { operation, schema, fragments, variableValues, rootValue, deduplicateDefers, errorWithIncrementalSubscription, } = exeContext;
220
223
  const rootType = getDefinedRootType(schema, operation.operation, [operation]);
221
224
  if (rootType == null) {
222
225
  createGraphQLError(`Schema is not configured to execute ${operation.operation} operation.`, {
@@ -231,7 +234,9 @@ function executeOperation(exeContext) {
231
234
  data = executeRootGroupedFieldSet(exeContext, operation.operation, rootType, rootValue, groupedFieldSet, undefined);
232
235
  }
233
236
  else {
234
- const fieldPLan = buildFieldPlan(groupedFieldSet);
237
+ const fieldPLan = deduplicateDefers
238
+ ? buildFieldPlan(groupedFieldSet)
239
+ : buildBranchingFieldPlan(groupedFieldSet);
235
240
  groupedFieldSet = fieldPLan.groupedFieldSet;
236
241
  const newGroupedFieldSets = fieldPLan.newGroupedFieldSets;
237
242
  const newDeferMap = addNewDeferredFragments(newDeferUsages, new Map());
@@ -565,6 +570,7 @@ async function completeAsyncIteratorValue(exeContext, itemType, fieldGroup, info
565
570
  streamRecord = {
566
571
  label: streamUsage.label,
567
572
  path,
573
+ index,
568
574
  streamItemQueue,
569
575
  };
570
576
  }
@@ -572,6 +578,7 @@ async function completeAsyncIteratorValue(exeContext, itemType, fieldGroup, info
572
578
  streamRecord = {
573
579
  label: streamUsage.label,
574
580
  path,
581
+ index,
575
582
  streamItemQueue,
576
583
  earlyReturn: returnFn.bind(asyncIterator),
577
584
  };
@@ -649,6 +656,7 @@ function completeIterableValue(exeContext, itemType, fieldGroup, info, path, ite
649
656
  const streamRecord = {
650
657
  label: streamUsage.label,
651
658
  path,
659
+ index,
652
660
  streamItemQueue: buildSyncStreamItemQueue(item, index, path, iterator, exeContext, streamUsage.fieldGroup, info, itemType),
653
661
  };
654
662
  const context = incrementalContext ?? exeContext;
@@ -826,7 +834,7 @@ function collectAndExecuteSubfields(exeContext, returnType, fieldGroup, path, re
826
834
  if (deferMap === undefined && newDeferUsages.length === 0) {
827
835
  return executeFields(exeContext, returnType, result, path, groupedFieldSet, incrementalContext, undefined);
828
836
  }
829
- const subFieldPlan = buildSubFieldPlan(groupedFieldSet, incrementalContext?.deferUsageSet);
837
+ const subFieldPlan = buildSubFieldPlan(groupedFieldSet, incrementalContext?.deferUsageSet, exeContext.deduplicateDefers);
830
838
  groupedFieldSet = subFieldPlan.groupedFieldSet;
831
839
  const newGroupedFieldSets = subFieldPlan.newGroupedFieldSets;
832
840
  const newDeferMap = addNewDeferredFragments(newDeferUsages, new Map(deferMap), path);
@@ -838,12 +846,14 @@ function collectAndExecuteSubfields(exeContext, returnType, fieldGroup, path, re
838
846
  }
839
847
  return subFields;
840
848
  }
841
- function buildSubFieldPlan(originalGroupedFieldSet, deferUsageSet) {
849
+ function buildSubFieldPlan(originalGroupedFieldSet, deferUsageSet, deduplicateDefers) {
842
850
  let fieldPlan = originalGroupedFieldSet._fieldPlan;
843
851
  if (fieldPlan !== undefined) {
844
852
  return fieldPlan;
845
853
  }
846
- fieldPlan = buildFieldPlan(originalGroupedFieldSet, deferUsageSet);
854
+ fieldPlan = deduplicateDefers
855
+ ? buildFieldPlan(originalGroupedFieldSet, deferUsageSet)
856
+ : buildBranchingFieldPlan(originalGroupedFieldSet, deferUsageSet);
847
857
  originalGroupedFieldSet._fieldPlan = fieldPlan;
848
858
  return fieldPlan;
849
859
  }
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@graphql-tools/executor",
3
- "version": "2.0.0-alpha-20240702193427-1ce4b1efdef732d2635b657df6273a975a19d217",
3
+ "version": "2.0.0-alpha-20240708182941-ed27153e85ecdb79083b1d124d470573a65893fd",
4
4
  "sideEffects": false,
5
5
  "peerDependencies": {
6
6
  "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
7
7
  },
8
8
  "dependencies": {
9
- "@graphql-tools/utils": "10.3.0-alpha-20240702193427-1ce4b1efdef732d2635b657df6273a975a19d217",
9
+ "@graphql-tools/utils": "10.3.0-alpha-20240708182941-ed27153e85ecdb79083b1d124d470573a65893fd",
10
10
  "@graphql-typed-document-node/core": "3.2.0",
11
11
  "@repeaterjs/repeater": "^3.0.4",
12
12
  "tslib": "^2.4.0",
@@ -9,6 +9,10 @@ export declare class IncrementalGraph {
9
9
  constructor();
10
10
  getNewPending(incrementalDataRecords: ReadonlyArray<IncrementalDataRecord>): ReadonlyArray<SubsequentResultRecord>;
11
11
  addCompletedReconcilableDeferredGroupedFieldSet(reconcilableResult: ReconcilableDeferredGroupedFieldSetResult): void;
12
+ currentCompletedIncrementalData(): {
13
+ [Symbol.iterator](): any;
14
+ next: () => IteratorResult<IncrementalDataRecordResult>;
15
+ };
12
16
  completedIncrementalData(): {
13
17
  [Symbol.asyncIterator](): any;
14
18
  next: () => Promise<IteratorResult<Iterable<IncrementalDataRecordResult>>>;
@@ -9,6 +9,10 @@ export declare class IncrementalGraph {
9
9
  constructor();
10
10
  getNewPending(incrementalDataRecords: ReadonlyArray<IncrementalDataRecord>): ReadonlyArray<SubsequentResultRecord>;
11
11
  addCompletedReconcilableDeferredGroupedFieldSet(reconcilableResult: ReconcilableDeferredGroupedFieldSetResult): void;
12
+ currentCompletedIncrementalData(): {
13
+ [Symbol.iterator](): any;
14
+ next: () => IteratorResult<IncrementalDataRecordResult>;
15
+ };
12
16
  completedIncrementalData(): {
13
17
  [Symbol.asyncIterator](): any;
14
18
  next: () => Promise<IteratorResult<Iterable<IncrementalDataRecordResult>>>;
@@ -2,6 +2,8 @@ import type { GraphQLError } from 'graphql';
2
2
  import type { CancellableStreamRecord, IncrementalDataRecord, IncrementalExecutionResults } from './types.cjs';
3
3
  export declare function buildIncrementalResponse<TData = any>(context: IncrementalPublisherContext, result: TData, errors: ReadonlyArray<GraphQLError> | undefined, incrementalDataRecords: ReadonlyArray<IncrementalDataRecord>): IncrementalExecutionResults<TData>;
4
4
  interface IncrementalPublisherContext {
5
+ sendIncrementalErrorsAsNull: boolean;
6
+ sendPathAndLabelOnIncremental: boolean;
5
7
  signal: AbortSignal | undefined;
6
8
  cancellableStreams: Set<CancellableStreamRecord> | undefined;
7
9
  }
@@ -2,6 +2,8 @@ import type { GraphQLError } from 'graphql';
2
2
  import type { CancellableStreamRecord, IncrementalDataRecord, IncrementalExecutionResults } from './types.js';
3
3
  export declare function buildIncrementalResponse<TData = any>(context: IncrementalPublisherContext, result: TData, errors: ReadonlyArray<GraphQLError> | undefined, incrementalDataRecords: ReadonlyArray<IncrementalDataRecord>): IncrementalExecutionResults<TData>;
4
4
  interface IncrementalPublisherContext {
5
+ sendIncrementalErrorsAsNull: boolean;
6
+ sendPathAndLabelOnIncremental: boolean;
5
7
  signal: AbortSignal | undefined;
6
8
  cancellableStreams: Set<CancellableStreamRecord> | undefined;
7
9
  }
@@ -5,3 +5,4 @@ export interface FieldPlan {
5
5
  newGroupedFieldSets: Map<DeferUsageSet, GroupedFieldSet>;
6
6
  }
7
7
  export declare function buildFieldPlan(originalGroupedFieldSet: GroupedFieldSet, parentDeferUsages?: DeferUsageSet): FieldPlan;
8
+ export declare function buildBranchingFieldPlan(originalGroupedFieldSet: GroupedFieldSet, parentDeferUsages?: DeferUsageSet): FieldPlan;
@@ -5,3 +5,4 @@ export interface FieldPlan {
5
5
  newGroupedFieldSets: Map<DeferUsageSet, GroupedFieldSet>;
6
6
  }
7
7
  export declare function buildFieldPlan(originalGroupedFieldSet: GroupedFieldSet, parentDeferUsages?: DeferUsageSet): FieldPlan;
8
+ export declare function buildBranchingFieldPlan(originalGroupedFieldSet: GroupedFieldSet, parentDeferUsages?: DeferUsageSet): FieldPlan;
@@ -38,6 +38,9 @@ export interface ExecutionContext<TVariables = any, TContext = any> {
38
38
  typeResolver: GraphQLTypeResolver<any, TContext>;
39
39
  subscribeFieldResolver: GraphQLFieldResolver<any, TContext>;
40
40
  enableEarlyExecution: boolean;
41
+ deduplicateDefers: boolean;
42
+ sendIncrementalErrorsAsNull: boolean;
43
+ sendPathAndLabelOnIncremental: boolean;
41
44
  errorWithIncrementalSubscription: boolean;
42
45
  signal: AbortSignal | undefined;
43
46
  errors: Map<Path | undefined, GraphQLError> | undefined;
@@ -55,6 +58,9 @@ export interface ExecutionArgs<TData = any, TVariables = any, TContext = any> {
55
58
  typeResolver?: Maybe<GraphQLTypeResolver<any, TContext>>;
56
59
  subscribeFieldResolver?: Maybe<GraphQLFieldResolver<any, TContext>>;
57
60
  enableEarlyExecution?: Maybe<boolean>;
61
+ deduplicateDefers?: Maybe<boolean>;
62
+ sendIncrementalErrorsAsNull?: Maybe<boolean>;
63
+ sendPathAndLabelOnIncremental?: Maybe<boolean>;
58
64
  errorWithIncrementalSubscription?: Maybe<boolean>;
59
65
  signal?: AbortSignal;
60
66
  }
@@ -38,6 +38,9 @@ export interface ExecutionContext<TVariables = any, TContext = any> {
38
38
  typeResolver: GraphQLTypeResolver<any, TContext>;
39
39
  subscribeFieldResolver: GraphQLFieldResolver<any, TContext>;
40
40
  enableEarlyExecution: boolean;
41
+ deduplicateDefers: boolean;
42
+ sendIncrementalErrorsAsNull: boolean;
43
+ sendPathAndLabelOnIncremental: boolean;
41
44
  errorWithIncrementalSubscription: boolean;
42
45
  signal: AbortSignal | undefined;
43
46
  errors: Map<Path | undefined, GraphQLError> | undefined;
@@ -55,6 +58,9 @@ export interface ExecutionArgs<TData = any, TVariables = any, TContext = any> {
55
58
  typeResolver?: Maybe<GraphQLTypeResolver<any, TContext>>;
56
59
  subscribeFieldResolver?: Maybe<GraphQLFieldResolver<any, TContext>>;
57
60
  enableEarlyExecution?: Maybe<boolean>;
61
+ deduplicateDefers?: Maybe<boolean>;
62
+ sendIncrementalErrorsAsNull?: Maybe<boolean>;
63
+ sendPathAndLabelOnIncremental?: Maybe<boolean>;
58
64
  errorWithIncrementalSubscription?: Maybe<boolean>;
59
65
  signal?: AbortSignal;
60
66
  }
@@ -54,15 +54,21 @@ interface BareDeferredGroupedFieldSetResult<TData = Record<string, unknown>> {
54
54
  errors?: ReadonlyArray<GraphQLError>;
55
55
  data: TData;
56
56
  }
57
- export interface IncrementalDeferResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> extends BareDeferredGroupedFieldSetResult<TData> {
57
+ export interface IncrementalDeferResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> {
58
+ errors?: ReadonlyArray<GraphQLError>;
59
+ data: TData | null;
58
60
  id: string;
61
+ path?: ReadonlyArray<string | number>;
62
+ label?: string;
59
63
  subPath?: ReadonlyArray<string | number>;
60
64
  extensions?: TExtensions;
61
65
  }
62
66
  export interface FormattedIncrementalDeferResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> {
63
67
  errors?: ReadonlyArray<GraphQLFormattedError>;
64
- data: TData;
68
+ data: TData | null;
65
69
  id: string;
70
+ path?: ReadonlyArray<string | number>;
71
+ label?: string;
66
72
  subPath?: ReadonlyArray<string | number>;
67
73
  extensions?: TExtensions;
68
74
  }
@@ -70,16 +76,20 @@ interface BareStreamItemsResult<TData = ReadonlyArray<unknown>> {
70
76
  errors?: ReadonlyArray<GraphQLError>;
71
77
  items: TData;
72
78
  }
73
- export interface IncrementalStreamResult<TData = ReadonlyArray<unknown>, TExtensions = Record<string, unknown>> extends BareStreamItemsResult<TData> {
79
+ export interface IncrementalStreamResult<TData = ReadonlyArray<unknown>, TExtensions = Record<string, unknown>> {
80
+ errors?: ReadonlyArray<GraphQLError>;
81
+ items: TData | null;
74
82
  id: string;
75
- subPath?: ReadonlyArray<string | number>;
83
+ path?: ReadonlyArray<string | number>;
84
+ label?: string;
76
85
  extensions?: TExtensions;
77
86
  }
78
87
  export interface FormattedIncrementalStreamResult<TData = Array<unknown>, TExtensions = Record<string, unknown>> {
79
88
  errors?: ReadonlyArray<GraphQLFormattedError>;
80
- items: TData;
89
+ items: TData | null;
81
90
  id: string;
82
- subPath?: ReadonlyArray<string | number>;
91
+ path?: ReadonlyArray<string | number>;
92
+ label?: string;
83
93
  extensions?: TExtensions;
84
94
  }
85
95
  export type IncrementalResult<TData = unknown, TExtensions = Record<string, unknown>> = IncrementalDeferResult<TData, TExtensions> | IncrementalStreamResult<TData, TExtensions>;
@@ -147,6 +157,7 @@ export type StreamItemRecord = BoxedPromiseOrValue<StreamItemResult> | (() => Bo
147
157
  export interface StreamRecord {
148
158
  path: Path;
149
159
  label: string | undefined;
160
+ index: number;
150
161
  id?: string | undefined;
151
162
  streamItemQueue: Array<StreamItemRecord>;
152
163
  }
@@ -54,15 +54,21 @@ interface BareDeferredGroupedFieldSetResult<TData = Record<string, unknown>> {
54
54
  errors?: ReadonlyArray<GraphQLError>;
55
55
  data: TData;
56
56
  }
57
- export interface IncrementalDeferResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> extends BareDeferredGroupedFieldSetResult<TData> {
57
+ export interface IncrementalDeferResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> {
58
+ errors?: ReadonlyArray<GraphQLError>;
59
+ data: TData | null;
58
60
  id: string;
61
+ path?: ReadonlyArray<string | number>;
62
+ label?: string;
59
63
  subPath?: ReadonlyArray<string | number>;
60
64
  extensions?: TExtensions;
61
65
  }
62
66
  export interface FormattedIncrementalDeferResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> {
63
67
  errors?: ReadonlyArray<GraphQLFormattedError>;
64
- data: TData;
68
+ data: TData | null;
65
69
  id: string;
70
+ path?: ReadonlyArray<string | number>;
71
+ label?: string;
66
72
  subPath?: ReadonlyArray<string | number>;
67
73
  extensions?: TExtensions;
68
74
  }
@@ -70,16 +76,20 @@ interface BareStreamItemsResult<TData = ReadonlyArray<unknown>> {
70
76
  errors?: ReadonlyArray<GraphQLError>;
71
77
  items: TData;
72
78
  }
73
- export interface IncrementalStreamResult<TData = ReadonlyArray<unknown>, TExtensions = Record<string, unknown>> extends BareStreamItemsResult<TData> {
79
+ export interface IncrementalStreamResult<TData = ReadonlyArray<unknown>, TExtensions = Record<string, unknown>> {
80
+ errors?: ReadonlyArray<GraphQLError>;
81
+ items: TData | null;
74
82
  id: string;
75
- subPath?: ReadonlyArray<string | number>;
83
+ path?: ReadonlyArray<string | number>;
84
+ label?: string;
76
85
  extensions?: TExtensions;
77
86
  }
78
87
  export interface FormattedIncrementalStreamResult<TData = Array<unknown>, TExtensions = Record<string, unknown>> {
79
88
  errors?: ReadonlyArray<GraphQLFormattedError>;
80
- items: TData;
89
+ items: TData | null;
81
90
  id: string;
82
- subPath?: ReadonlyArray<string | number>;
91
+ path?: ReadonlyArray<string | number>;
92
+ label?: string;
83
93
  extensions?: TExtensions;
84
94
  }
85
95
  export type IncrementalResult<TData = unknown, TExtensions = Record<string, unknown>> = IncrementalDeferResult<TData, TExtensions> | IncrementalStreamResult<TData, TExtensions>;
@@ -147,6 +157,7 @@ export type StreamItemRecord = BoxedPromiseOrValue<StreamItemResult> | (() => Bo
147
157
  export interface StreamRecord {
148
158
  path: Path;
149
159
  label: string | undefined;
160
+ index: number;
150
161
  id?: string | undefined;
151
162
  streamItemQueue: Array<StreamItemRecord>;
152
163
  }