@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.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.defaultFieldResolver = exports.defaultTypeResolver = exports.CRITICAL_ERROR = void 0;
3
+ exports.CRITICAL_ERROR = exports.defaultTypeResolver = exports.defaultFieldResolver = void 0;
4
4
  exports.execute = execute;
5
5
  exports.executeSync = executeSync;
6
6
  exports.assertValidExecutionArguments = assertValidExecutionArguments;
@@ -13,12 +13,15 @@ exports.flattenIncrementalResults = flattenIncrementalResults;
13
13
  exports.getFieldDef = getFieldDef;
14
14
  exports.isIncrementalResult = isIncrementalResult;
15
15
  const graphql_1 = require("graphql");
16
+ Object.defineProperty(exports, "defaultFieldResolver", { enumerable: true, get: function () { return graphql_1.defaultFieldResolver; } });
17
+ Object.defineProperty(exports, "defaultTypeResolver", { enumerable: true, get: function () { return graphql_1.defaultTypeResolver; } });
16
18
  const utils_1 = require("@graphql-tools/utils");
17
19
  const disposablestack_1 = require("@whatwg-node/disposablestack");
18
20
  const promise_helpers_1 = require("@whatwg-node/promise-helpers");
19
21
  const coerceError_js_1 = require("./coerceError.js");
20
22
  const flattenAsyncIterable_js_1 = require("./flattenAsyncIterable.js");
21
23
  const incremental_js_1 = require("./incremental.js");
24
+ const incrementalCollect_js_1 = require("./incrementalCollect.js");
22
25
  const invariant_js_1 = require("./invariant.js");
23
26
  const promiseForObject_js_1 = require("./promiseForObject.js");
24
27
  const EMPTY_ARGS = Object.freeze(Object.create(null));
@@ -329,9 +332,9 @@ function buildExecutionContext(args) {
329
332
  operation,
330
333
  variableValues,
331
334
  resolveInfoVariableValues,
332
- fieldResolver: fieldResolver ?? exports.defaultFieldResolver,
333
- typeResolver: typeResolver ?? exports.defaultTypeResolver,
334
- subscribeFieldResolver: subscribeFieldResolver ?? exports.defaultFieldResolver,
335
+ fieldResolver: fieldResolver ?? graphql_1.defaultFieldResolver,
336
+ typeResolver: typeResolver ?? graphql_1.defaultTypeResolver,
337
+ subscribeFieldResolver: subscribeFieldResolver ?? graphql_1.defaultFieldResolver,
335
338
  subsequentPayloads: new Set(),
336
339
  incrementalSpec,
337
340
  publisher: incrementalSpec === 'v0.2' ? (0, incremental_js_1.createIncrementalPublisherState)() : undefined,
@@ -401,18 +404,52 @@ function executeOperation(exeContext) {
401
404
  const { fields: rootFields, patches } = (0, utils_1.collectFields)(schema, fragments, variableValues, rootType, operation.selectionSet);
402
405
  const path = undefined;
403
406
  let result;
407
+ if (patches.length === 0) {
408
+ if (operation.operation === 'mutation') {
409
+ result = executeFieldsSerially(exeContext, rootType, rootValue, path, rootFields);
410
+ }
411
+ else {
412
+ result = executeFields(exeContext, rootType, rootValue, path, rootFields);
413
+ }
414
+ return result;
415
+ }
416
+ exeContext.nulledPaths ??= [];
417
+ const { groupedFieldSet } = (0, incrementalCollect_js_1.collectIncrementalFields)(schema, fragments, variableValues, rootType, operation.selectionSet);
418
+ const { groupedFieldSet: initialFields, newGroupedFieldSets } = (0, incrementalCollect_js_1.buildExecutionPlan)(groupedFieldSet);
419
+ const plannedRootFields = (0, incrementalCollect_js_1.groupedFieldSetToFieldMap)(initialFields);
404
420
  if (operation.operation === 'mutation') {
405
- result = executeFieldsSerially(exeContext, rootType, rootValue, path, rootFields);
421
+ result = executeFieldsSerially(exeContext, rootType, rootValue, path, plannedRootFields);
406
422
  }
407
423
  else {
408
- result = executeFields(exeContext, rootType, rootValue, path, rootFields);
409
- }
410
- for (const patch of patches) {
411
- const { label, fields: patchFields, patches: nestedPatches } = patch;
412
- executeDeferredFragment(exeContext, rootType, rootValue, patchFields, label, path, undefined, nestedPatches);
424
+ result = executeFields(exeContext, rootType, rootValue, path, plannedRootFields);
413
425
  }
426
+ executeDeferredGroups(exeContext, rootType, rootValue, path, newGroupedFieldSets, undefined);
414
427
  return result;
415
428
  }
429
+ function executeDeferredGroups(exeContext, parentType, sourceValue, path, newGroupedFieldSets, parentContext) {
430
+ if ((0, incremental_js_1.isAtOrUnderNulledPath)((0, utils_1.pathToArray)(path), exeContext.nulledPaths)) {
431
+ return;
432
+ }
433
+ const records = new Map();
434
+ const groups = [...newGroupedFieldSets.entries()].sort(([a], [b]) => deferUsageDepth((0, incrementalCollect_js_1.rootDeferUsage)(a)) - deferUsageDepth((0, incrementalCollect_js_1.rootDeferUsage)(b)));
435
+ for (const [deferUsageSet, groupedFieldSet] of groups) {
436
+ const usage = (0, incrementalCollect_js_1.rootDeferUsage)(deferUsageSet);
437
+ const deliveryParent = (usage?.parentDeferUsage && records.get(usage.parentDeferUsage)) ?? parentContext;
438
+ const record = executeDeferredFragment(exeContext, parentType, sourceValue, (0, incrementalCollect_js_1.groupedFieldSetToFieldMap)(groupedFieldSet), (0, incrementalCollect_js_1.labelForDeferUsageSet)(deferUsageSet), path, parentContext, undefined, deliveryParent, usage);
439
+ if (usage) {
440
+ records.set(usage, record);
441
+ }
442
+ }
443
+ }
444
+ function deferUsageDepth(usage) {
445
+ let depth = 0;
446
+ let current = usage;
447
+ while (current?.parentDeferUsage) {
448
+ depth++;
449
+ current = current.parentDeferUsage;
450
+ }
451
+ return depth;
452
+ }
416
453
  /**
417
454
  * Implements the "Executing selection sets" section of the spec
418
455
  * for fields that must be executed serially.
@@ -886,71 +923,29 @@ function invalidReturnTypeError(returnType, result, fieldNodes) {
886
923
  function collectAndExecuteSubfields(exeContext, returnType, fieldNodes, path, result, asyncPayloadRecord) {
887
924
  // Collect sub-fields to execute to complete this value.
888
925
  const { fields: subFieldNodes, patches: subPatches } = collectSubfields(exeContext, returnType, fieldNodes);
889
- const subFields = executeFields(exeContext, returnType, result, path, subFieldNodes, asyncPayloadRecord);
890
- for (const subPatch of subPatches) {
891
- const { label, fields: subPatchFieldNodes, patches: nestedPatches } = subPatch;
892
- executeDeferredFragment(exeContext, returnType, result, subPatchFieldNodes, label, path, asyncPayloadRecord, nestedPatches);
893
- }
894
- return subFields;
895
- }
896
- /**
897
- * If a resolveType function is not given, then a default resolve behavior is
898
- * used which attempts two strategies:
899
- *
900
- * First, See if the provided value has a `__typename` field defined, if so, use
901
- * that value as name of the resolved type.
902
- *
903
- * Otherwise, test each possible type for the abstract type by calling
904
- * isTypeOf for the object being coerced, returning the first type that matches.
905
- */
906
- const defaultTypeResolver = function (value, contextValue, info, abstractType) {
907
- // First, look for `__typename`.
908
- if ((0, utils_1.isObjectLike)(value) && typeof value['__typename'] === 'string') {
909
- return value['__typename'];
910
- }
911
- // Otherwise, test each possible type.
912
- const possibleTypes = info.schema.getPossibleTypes(abstractType);
913
- const promisedIsTypeOfResults = [];
914
- for (let i = 0; i < possibleTypes.length; i++) {
915
- const type = possibleTypes[i];
916
- if (type.isTypeOf) {
917
- const isTypeOfResult = type.isTypeOf(value, contextValue, info);
918
- if ((0, promise_helpers_1.isPromise)(isTypeOfResult)) {
919
- promisedIsTypeOfResults[i] = isTypeOfResult;
920
- }
921
- else if (isTypeOfResult) {
922
- return type.name;
923
- }
924
- }
925
- }
926
- if (promisedIsTypeOfResults.length) {
927
- return Promise.all(promisedIsTypeOfResults).then(isTypeOfResults => {
928
- for (let i = 0; i < isTypeOfResults.length; i++) {
929
- if (isTypeOfResults[i]) {
930
- return possibleTypes[i].name;
931
- }
926
+ if (subPatches.length === 0 && exeContext.nulledPaths == null) {
927
+ return executeFields(exeContext, returnType, result, path, subFieldNodes, asyncPayloadRecord);
928
+ }
929
+ exeContext.nulledPaths ??= [];
930
+ const parentDeferUsage = asyncPayloadRecord?.type === 'defer' ? asyncPayloadRecord.deferUsage : undefined;
931
+ const { groupedFieldSet } = (0, incrementalCollect_js_1.collectIncrementalSubfields)(exeContext.schema, exeContext.fragments, exeContext.variableValues, returnType, fieldNodes, parentDeferUsage);
932
+ const parentDeferUsages = new Set();
933
+ if (parentDeferUsage) {
934
+ parentDeferUsages.add(parentDeferUsage);
935
+ }
936
+ else if (asyncPayloadRecord?.type === 'stream') {
937
+ for (const node of fieldNodes) {
938
+ const usage = (0, incrementalCollect_js_1.deferUsageForFieldNode)(node);
939
+ if (usage) {
940
+ parentDeferUsages.add(usage);
932
941
  }
933
- });
934
- }
935
- };
936
- exports.defaultTypeResolver = defaultTypeResolver;
937
- /**
938
- * If a resolve function is not given, then a default resolve behavior is used
939
- * which takes the property of the source object of the same name as the field
940
- * and returns it as the result, or if it's a function, returns the result
941
- * of calling that function while passing along args and context value.
942
- */
943
- const defaultFieldResolver = function (source, args, contextValue, info) {
944
- // ensure source is a value for which property access is acceptable.
945
- if ((0, utils_1.isObjectLike)(source) || typeof source === 'function') {
946
- const property = source[info.fieldName];
947
- if (typeof property === 'function') {
948
- return source[info.fieldName](args, contextValue, info);
949
942
  }
950
- return property;
951
943
  }
952
- };
953
- exports.defaultFieldResolver = defaultFieldResolver;
944
+ const { groupedFieldSet: initialFields, newGroupedFieldSets } = (0, incrementalCollect_js_1.buildExecutionPlan)(groupedFieldSet, parentDeferUsages);
945
+ const subFields = executeFields(exeContext, returnType, result, path, (0, incrementalCollect_js_1.groupedFieldSetToFieldMap)(initialFields), asyncPayloadRecord);
946
+ executeDeferredGroups(exeContext, returnType, result, path, newGroupedFieldSets, asyncPayloadRecord);
947
+ return subFields;
948
+ }
954
949
  /**
955
950
  * Implements the "Subscribe" algorithm described in the GraphQL specification,
956
951
  * including `@defer` and `@stream` as proposed in
@@ -1158,13 +1153,14 @@ function assertEventStream(result, signal, onSignalAbort) {
1158
1153
  }
1159
1154
  return result;
1160
1155
  }
1161
- function executeDeferredFragment(exeContext, parentType, sourceValue, fields, label, path, parentContext, nestedPatches, deliveryParent) {
1156
+ function executeDeferredFragment(exeContext, parentType, sourceValue, fields, label, path, parentContext, nestedPatches, deliveryParent, deferUsage) {
1162
1157
  const asyncPayloadRecord = new incremental_js_1.DeferredFragmentRecord({
1163
1158
  label,
1164
1159
  path,
1165
1160
  parentContext,
1166
1161
  deliveryParent: deliveryParent ?? parentContext,
1167
1162
  exeContext,
1163
+ deferUsage,
1168
1164
  });
1169
1165
  let promiseOrData;
1170
1166
  try {
@@ -1188,6 +1184,7 @@ function executeDeferredFragment(exeContext, parentType, sourceValue, fields, la
1188
1184
  parentContext, nested.patches, asyncPayloadRecord);
1189
1185
  }
1190
1186
  }
1187
+ return asyncPayloadRecord;
1191
1188
  }
1192
1189
  function executeStreamField(path, itemPath, item, exeContext, fieldNodes, info, itemType, label, parentContext, streamGroup) {
1193
1190
  const asyncPayloadRecord = new incremental_js_1.StreamRecord({
@@ -1197,6 +1194,10 @@ function executeStreamField(path, itemPath, item, exeContext, fieldNodes, info,
1197
1194
  exeContext,
1198
1195
  streamGroup,
1199
1196
  });
1197
+ if ((0, incremental_js_1.isAtOrUnderNulledPath)(asyncPayloadRecord.path, exeContext.nulledPaths)) {
1198
+ (0, incremental_js_1.cancelAsyncPayload)(exeContext, asyncPayloadRecord);
1199
+ return asyncPayloadRecord;
1200
+ }
1200
1201
  let completedItem;
1201
1202
  try {
1202
1203
  try {
@@ -1299,6 +1300,15 @@ async function executeStreamIterator(initialIndex, iterator, exeContext, fieldNo
1299
1300
  exeContext,
1300
1301
  streamGroup,
1301
1302
  });
1303
+ if ((0, incremental_js_1.isAtOrUnderNulledPath)(asyncPayloadRecord.path, exeContext.nulledPaths)) {
1304
+ (0, incremental_js_1.cancelAsyncPayload)(exeContext, asyncPayloadRecord);
1305
+ if (iterator.return) {
1306
+ iterator.return().catch(() => {
1307
+ // ignore errors
1308
+ });
1309
+ }
1310
+ break;
1311
+ }
1302
1312
  let iteration;
1303
1313
  try {
1304
1314
  iteration = await executeStreamIteratorItem(iterator, exeContext, fieldNodes, info, itemType, asyncPayloadRecord, itemPath);
@@ -1340,24 +1350,22 @@ async function executeStreamIterator(initialIndex, iterator, exeContext, fieldNo
1340
1350
  }
1341
1351
  function filterSubsequentPayloads(exeContext, nullPath, currentAsyncRecord) {
1342
1352
  const nullPathArray = (0, utils_1.pathToArray)(nullPath);
1353
+ if (exeContext.nulledPaths) {
1354
+ exeContext.nulledPaths.push(nullPathArray);
1355
+ }
1356
+ else if (exeContext.subsequentPayloads.size === 0) {
1357
+ return;
1358
+ }
1343
1359
  exeContext.subsequentPayloads.forEach(asyncRecord => {
1344
- if (asyncRecord === currentAsyncRecord) {
1360
+ if (asyncRecord === currentAsyncRecord || asyncRecord.cancelled) {
1345
1361
  // don't remove payload from where error originates
1346
1362
  return;
1347
1363
  }
1348
- for (let i = 0; i < nullPathArray.length; i++) {
1349
- if (asyncRecord.path[i] !== nullPathArray[i]) {
1350
- // asyncRecord points to a path unaffected by this payload
1351
- return;
1352
- }
1353
- }
1354
- // asyncRecord path points to nulled error field
1355
- if ((0, incremental_js_1.isStreamPayload)(asyncRecord) && asyncRecord.iterator?.return) {
1356
- asyncRecord.iterator.return().catch(() => {
1357
- // ignore error
1358
- });
1364
+ if (!(0, incremental_js_1.pathStartsWith)(asyncRecord.path, nullPathArray)) {
1365
+ // ancestor or a different branch: ancestor patches are pruned at yield
1366
+ return;
1359
1367
  }
1360
- (0, incremental_js_1.deleteAsyncPayload)(exeContext, asyncRecord);
1368
+ (0, incremental_js_1.cancelAsyncPayload)(exeContext, asyncRecord);
1361
1369
  });
1362
1370
  }
1363
1371
  /**
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getBySet = getBySet;
4
+ const isSameSet_js_1 = require("./isSameSet.js");
5
+ /** @internal */
6
+ function getBySet(map, setToMatch) {
7
+ for (const set of map.keys()) {
8
+ if ((0, isSameSet_js_1.isSameSet)(set, setToMatch)) {
9
+ return map.get(set);
10
+ }
11
+ }
12
+ return undefined;
13
+ }
@@ -4,6 +4,10 @@ exports.StreamRecord = exports.DeferredFragmentRecord = exports.StreamGroup = vo
4
4
  exports.createIncrementalPublisherState = createIncrementalPublisherState;
5
5
  exports.createStreamGroupIfV02 = createStreamGroupIfV02;
6
6
  exports.deleteAsyncPayload = deleteAsyncPayload;
7
+ exports.pathStartsWith = pathStartsWith;
8
+ exports.isAtOrUnderNulledPath = isAtOrUnderNulledPath;
9
+ exports.pruneUnmergeableIncrementalData = pruneUnmergeableIncrementalData;
10
+ exports.cancelAsyncPayload = cancelAsyncPayload;
7
11
  exports.isStreamPayload = isStreamPayload;
8
12
  exports.collectInitialPending = collectInitialPending;
9
13
  exports.yieldSubsequentPayloads = yieldSubsequentPayloads;
@@ -54,6 +58,130 @@ function deleteAsyncPayload(exeContext, record) {
54
58
  record.streamGroup?.release();
55
59
  }
56
60
  }
61
+ function pathStartsWith(path, prefix) {
62
+ if (prefix.length > path.length) {
63
+ return false;
64
+ }
65
+ for (let i = 0; i < prefix.length; i++) {
66
+ if (path[i] !== prefix[i]) {
67
+ return false;
68
+ }
69
+ }
70
+ return true;
71
+ }
72
+ function isAtOrUnderNulledPath(path, nulledPaths) {
73
+ if (nulledPaths == null || nulledPaths.length === 0) {
74
+ return false;
75
+ }
76
+ for (const nulledPath of nulledPaths) {
77
+ if (pathStartsWith(path, nulledPath)) {
78
+ return true;
79
+ }
80
+ }
81
+ return false;
82
+ }
83
+ function isStrictlyUnderNulledPath(path, nulledPaths) {
84
+ for (const nulledPath of nulledPaths) {
85
+ if (path.length > nulledPath.length && pathStartsWith(path, nulledPath)) {
86
+ return true;
87
+ }
88
+ }
89
+ return false;
90
+ }
91
+ function isExactNulledPath(path, nulledPaths) {
92
+ for (const nulledPath of nulledPaths) {
93
+ if (path.length === nulledPath.length && pathStartsWith(path, nulledPath)) {
94
+ return true;
95
+ }
96
+ }
97
+ return false;
98
+ }
99
+ function isEmptyIncrementalData(data) {
100
+ if (data == null) {
101
+ return true;
102
+ }
103
+ if (typeof data !== 'object') {
104
+ return false;
105
+ }
106
+ if (Array.isArray(data)) {
107
+ return data.length === 0;
108
+ }
109
+ const entries = Object.entries(data);
110
+ if (entries.length === 0) {
111
+ return true;
112
+ }
113
+ return entries.every(([, value]) => value != null && typeof value === 'object' && isEmptyIncrementalData(value));
114
+ }
115
+ /**
116
+ * Drop keys that would write into a subtree already nulled by error bubbling.
117
+ * Ancestor deferred records can still deliver sibling fields (e.g. `someField`
118
+ * next to a nulled `b.c`).
119
+ */
120
+ function pruneUnmergeableIncrementalData(data, basePath, nulledPaths) {
121
+ if (nulledPaths == null || nulledPaths.length === 0 || data == null || typeof data !== 'object') {
122
+ return data;
123
+ }
124
+ if (Array.isArray(data)) {
125
+ return data.map((item, index) => {
126
+ const itemPath = [...basePath, index];
127
+ if (isAtOrUnderNulledPath(itemPath, nulledPaths)) {
128
+ return null;
129
+ }
130
+ return pruneUnmergeableIncrementalData(item, itemPath, nulledPaths);
131
+ });
132
+ }
133
+ const result = {};
134
+ for (const [key, value] of Object.entries(data)) {
135
+ const childPath = [...basePath, key];
136
+ if (isStrictlyUnderNulledPath(childPath, nulledPaths)) {
137
+ continue;
138
+ }
139
+ if (isExactNulledPath(childPath, nulledPaths)) {
140
+ // Keep a null delivered by this payload; drop a non-null write into an
141
+ // already-nulled object (Yaacov: unmergeable incremental).
142
+ if (value != null) {
143
+ continue;
144
+ }
145
+ result[key] = null;
146
+ continue;
147
+ }
148
+ result[key] = pruneUnmergeableIncrementalData(value, childPath, nulledPaths);
149
+ }
150
+ return result;
151
+ }
152
+ function isAnnounced(exeContext, record) {
153
+ const state = exeContext.publisher;
154
+ if (state == null) {
155
+ return false;
156
+ }
157
+ return state.announced.has(getPublishNode(record));
158
+ }
159
+ /**
160
+ * Cancel a subsequent record whose path is at or under a nulled position
161
+ * (graphql-js 17 `hasNulledPosition`). v0.2 records that were already
162
+ * announced stay in the set so the publisher can emit `completed` without
163
+ * incremental data.
164
+ */
165
+ function cancelAsyncPayload(exeContext, record) {
166
+ record.cancelled = true;
167
+ if (isStreamPayload(record) && record.iterator?.return) {
168
+ record.iterator.return().catch(() => {
169
+ // ignore error
170
+ });
171
+ }
172
+ if (!record.isCompleted) {
173
+ if (isStreamPayload(record)) {
174
+ record.addItems(null);
175
+ }
176
+ else {
177
+ record.addData(null);
178
+ }
179
+ }
180
+ if (exeContext.incrementalSpec === 'v0.2' && isAnnounced(exeContext, record)) {
181
+ return;
182
+ }
183
+ deleteAsyncPayload(exeContext, record);
184
+ }
57
185
  class DeferredFragmentRecord {
58
186
  type;
59
187
  errors;
@@ -63,7 +191,9 @@ class DeferredFragmentRecord {
63
191
  data;
64
192
  parentContext;
65
193
  deliveryParent;
194
+ deferUsage;
66
195
  isCompleted;
196
+ cancelled;
67
197
  _exeContext;
68
198
  _resolve;
69
199
  constructor(opts) {
@@ -72,10 +202,12 @@ class DeferredFragmentRecord {
72
202
  this.path = (0, utils_1.pathToArray)(opts.path);
73
203
  this.parentContext = opts.parentContext;
74
204
  this.deliveryParent = opts.deliveryParent ?? opts.parentContext;
205
+ this.deferUsage = opts.deferUsage;
75
206
  this.errors = [];
76
207
  this._exeContext = opts.exeContext;
77
208
  this._exeContext.subsequentPayloads.add(this);
78
209
  this.isCompleted = false;
210
+ this.cancelled = false;
79
211
  this.data = null;
80
212
  this.promise = new Promise(resolve => {
81
213
  this._resolve = MaybePromise => {
@@ -107,6 +239,7 @@ class StreamRecord {
107
239
  iterator;
108
240
  isCompletedIterator;
109
241
  isCompleted;
242
+ cancelled;
110
243
  streamGroup;
111
244
  _exeContext;
112
245
  _resolve;
@@ -123,6 +256,7 @@ class StreamRecord {
123
256
  this._exeContext.subsequentPayloads.add(this);
124
257
  this.streamGroup?.retain();
125
258
  this.isCompleted = false;
259
+ this.cancelled = false;
126
260
  this.items = null;
127
261
  this.promise = new Promise(resolve => {
128
262
  this._resolve = MaybePromise => {
@@ -190,6 +324,9 @@ function toPendingResult(state, record) {
190
324
  return pending;
191
325
  }
192
326
  function announceIfNeeded(record, state, pending) {
327
+ if (record.cancelled) {
328
+ return;
329
+ }
193
330
  const node = getPublishNode(record);
194
331
  if (state.announced.has(node)) {
195
332
  return;
@@ -222,6 +359,9 @@ function getCompletedIncrementalResults(exeContext) {
222
359
  continue;
223
360
  }
224
361
  deleteAsyncPayload(exeContext, asyncPayloadRecord);
362
+ if (asyncPayloadRecord.cancelled) {
363
+ continue;
364
+ }
225
365
  if (isStreamPayload(asyncPayloadRecord)) {
226
366
  const items = asyncPayloadRecord.items;
227
367
  if (asyncPayloadRecord.isCompletedIterator) {
@@ -231,8 +371,12 @@ function getCompletedIncrementalResults(exeContext) {
231
371
  incrementalResult.items = items;
232
372
  }
233
373
  else {
234
- const data = asyncPayloadRecord.data;
235
- incrementalResult.data = data ?? null;
374
+ const data = pruneUnmergeableIncrementalData(asyncPayloadRecord.data, asyncPayloadRecord.path, exeContext.nulledPaths);
375
+ if (isEmptyIncrementalData(data) && asyncPayloadRecord.errors.length === 0) {
376
+ continue;
377
+ }
378
+ incrementalResult.data =
379
+ data ?? null;
236
380
  }
237
381
  incrementalResult.path = asyncPayloadRecord.path;
238
382
  if (asyncPayloadRecord.label) {
@@ -272,13 +416,22 @@ function buildSubsequentV02(exeContext, completedRecords) {
272
416
  continue;
273
417
  }
274
418
  const id = ensureId(state, record);
275
- if (record.data == null && record.errors.length > 0) {
419
+ if (record.cancelled) {
420
+ completed.push({ id });
421
+ continue;
422
+ }
423
+ const data = pruneUnmergeableIncrementalData(record.data, record.path, exeContext.nulledPaths);
424
+ if ((record.data == null && record.errors.length > 0) ||
425
+ (isEmptyIncrementalData(data) && record.errors.length > 0)) {
276
426
  completed.push({ id, errors: record.errors });
277
427
  }
428
+ else if (isEmptyIncrementalData(data) && record.errors.length === 0) {
429
+ completed.push({ id });
430
+ }
278
431
  else {
279
432
  const incrementalResult = {
280
433
  id,
281
- data: record.data ?? null,
434
+ data: data ?? null,
282
435
  };
283
436
  if (record.errors.length > 0) {
284
437
  incrementalResult.errors = record.errors;