@graphql-tools/executor 4.0.2 → 4.1.0-alpha-20261020141228-0b45c008c3eb39b6b634293732537f794bfeef17

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.
@@ -18,6 +18,7 @@ const disposablestack_1 = require("@whatwg-node/disposablestack");
18
18
  const promise_helpers_1 = require("@whatwg-node/promise-helpers");
19
19
  const coerceError_js_1 = require("./coerceError.js");
20
20
  const flattenAsyncIterable_js_1 = require("./flattenAsyncIterable.js");
21
+ const incremental_js_1 = require("./incremental.js");
21
22
  const invariant_js_1 = require("./invariant.js");
22
23
  const promiseForObject_js_1 = require("./promiseForObject.js");
23
24
  const EMPTY_ARGS = Object.freeze(Object.create(null));
@@ -113,12 +114,16 @@ function executeImpl(exeContext) {
113
114
  return (0, promise_helpers_1.handleMaybePromise)(() => executeOperation(exeContext), data => {
114
115
  const initialResult = buildResponse(data, exeContext.errors);
115
116
  if (exeContext.subsequentPayloads.size > 0) {
117
+ const incrementalInitial = {
118
+ ...initialResult,
119
+ hasNext: true,
120
+ };
121
+ if (exeContext.incrementalSpec === 'v0.2') {
122
+ incrementalInitial.pending = (0, incremental_js_1.collectInitialPending)(exeContext);
123
+ }
116
124
  return {
117
- initialResult: {
118
- ...initialResult,
119
- hasNext: true,
120
- },
121
- subsequentResults: yieldSubsequentPayloads(exeContext),
125
+ initialResult: incrementalInitial,
126
+ subsequentResults: (0, incremental_js_1.yieldSubsequentPayloads)(exeContext),
122
127
  };
123
128
  }
124
129
  return initialResult;
@@ -190,7 +195,7 @@ function getFragmentsFromDocument(document) {
190
195
  * @internal
191
196
  */
192
197
  function buildExecutionContext(args) {
193
- const { schema, document, rootValue, contextValue, variableValues: rawVariableValues, operationName, fieldResolver, typeResolver, subscribeFieldResolver, signal, schemaCoordinateInErrors, hideSuggestions, } = args;
198
+ const { schema, document, rootValue, contextValue, variableValues: rawVariableValues, operationName, fieldResolver, typeResolver, subscribeFieldResolver, signal, schemaCoordinateInErrors, hideSuggestions, incrementalSpec = 'v0.1', } = args;
194
199
  signal?.throwIfAborted();
195
200
  // If the schema used for execution is invalid, throw an error.
196
201
  (0, graphql_1.assertValidSchema)(schema);
@@ -328,6 +333,8 @@ function buildExecutionContext(args) {
328
333
  typeResolver: typeResolver ?? exports.defaultTypeResolver,
329
334
  subscribeFieldResolver: subscribeFieldResolver ?? exports.defaultFieldResolver,
330
335
  subsequentPayloads: new Set(),
336
+ incrementalSpec,
337
+ publisher: incrementalSpec === 'v0.2' ? (0, incremental_js_1.createIncrementalPublisherState)() : undefined,
331
338
  errors: [],
332
339
  signal,
333
340
  getAbortSignal,
@@ -362,6 +369,8 @@ function buildPerEventExecutionContext(exeContext, payload) {
362
369
  typeResolver: exeContext.typeResolver,
363
370
  subscribeFieldResolver: exeContext.subscribeFieldResolver,
364
371
  subsequentPayloads: new Set(),
372
+ incrementalSpec: exeContext.incrementalSpec,
373
+ publisher: exeContext.incrementalSpec === 'v0.2' ? (0, incremental_js_1.createIncrementalPublisherState)() : undefined,
365
374
  errors: [],
366
375
  signal: exeContext.signal,
367
376
  getAbortSignal: exeContext.getAbortSignal,
@@ -399,8 +408,8 @@ function executeOperation(exeContext) {
399
408
  result = executeFields(exeContext, rootType, rootValue, path, rootFields);
400
409
  }
401
410
  for (const patch of patches) {
402
- const { label, fields: patchFields } = patch;
403
- executeDeferredFragment(exeContext, rootType, rootValue, patchFields, label, path);
411
+ const { label, fields: patchFields, patches: nestedPatches } = patch;
412
+ executeDeferredFragment(exeContext, rootType, rootValue, patchFields, label, path, undefined, nestedPatches);
404
413
  }
405
414
  return result;
406
415
  }
@@ -676,7 +685,12 @@ async function completeAsyncIteratorValue(exeContext, itemType, fieldNodes, info
676
685
  let index = 0;
677
686
  while (true) {
678
687
  if (stream && typeof stream.initialCount === 'number' && index >= stream.initialCount) {
679
- executeStreamIterator(index, iterator, exeContext, fieldNodes, info, itemType, path, stream.label, asyncPayloadRecord);
688
+ executeStreamIterator(index, iterator, exeContext, fieldNodes, info, itemType, path, stream.label, asyncPayloadRecord, (0, incremental_js_1.createStreamGroupIfV02)(exeContext, {
689
+ path,
690
+ label: stream.label,
691
+ parentContext: asyncPayloadRecord,
692
+ hasIterator: true,
693
+ }));
680
694
  break;
681
695
  }
682
696
  const itemPath = (0, utils_1.addPath)(path, index, undefined);
@@ -715,6 +729,7 @@ function completeListValue(exeContext, returnType, fieldNodes, info, path, resul
715
729
  throw new graphql_1.GraphQLError(`Expected Iterable, but did not find one for field "${info.parentType.name}.${info.fieldName}".`);
716
730
  }
717
731
  const stream = getStreamValues(exeContext, fieldNodes, path);
732
+ let streamGroup;
718
733
  // This is specified as a simple map, however we're optimizing the path
719
734
  // where the list contains no Promises by avoiding creating another Promise.
720
735
  let containsPromise = false;
@@ -726,7 +741,12 @@ function completeListValue(exeContext, returnType, fieldNodes, info, path, resul
726
741
  // since from here on it is not ever accessed by resolver functions.
727
742
  const itemPath = (0, utils_1.addPath)(path, index, undefined);
728
743
  if (stream && typeof stream.initialCount === 'number' && index >= stream.initialCount) {
729
- previousAsyncPayloadRecord = executeStreamField(path, itemPath, item, exeContext, fieldNodes, info, itemType, stream.label, previousAsyncPayloadRecord);
744
+ streamGroup ??= (0, incremental_js_1.createStreamGroupIfV02)(exeContext, {
745
+ path,
746
+ label: stream.label,
747
+ parentContext: asyncPayloadRecord,
748
+ });
749
+ previousAsyncPayloadRecord = executeStreamField(path, itemPath, item, exeContext, fieldNodes, info, itemType, stream.label, previousAsyncPayloadRecord, streamGroup);
730
750
  index++;
731
751
  continue;
732
752
  }
@@ -868,8 +888,8 @@ function collectAndExecuteSubfields(exeContext, returnType, fieldNodes, path, re
868
888
  const { fields: subFieldNodes, patches: subPatches } = collectSubfields(exeContext, returnType, fieldNodes);
869
889
  const subFields = executeFields(exeContext, returnType, result, path, subFieldNodes, asyncPayloadRecord);
870
890
  for (const subPatch of subPatches) {
871
- const { label, fields: subPatchFieldNodes } = subPatch;
872
- executeDeferredFragment(exeContext, returnType, result, subPatchFieldNodes, label, path, asyncPayloadRecord);
891
+ const { label, fields: subPatchFieldNodes, patches: nestedPatches } = subPatch;
892
+ executeDeferredFragment(exeContext, returnType, result, subPatchFieldNodes, label, path, asyncPayloadRecord, nestedPatches);
873
893
  }
874
894
  return subFields;
875
895
  }
@@ -1138,11 +1158,12 @@ function assertEventStream(result, signal, onSignalAbort) {
1138
1158
  }
1139
1159
  return result;
1140
1160
  }
1141
- function executeDeferredFragment(exeContext, parentType, sourceValue, fields, label, path, parentContext) {
1142
- const asyncPayloadRecord = new DeferredFragmentRecord({
1161
+ function executeDeferredFragment(exeContext, parentType, sourceValue, fields, label, path, parentContext, nestedPatches, deliveryParent) {
1162
+ const asyncPayloadRecord = new incremental_js_1.DeferredFragmentRecord({
1143
1163
  label,
1144
1164
  path,
1145
1165
  parentContext,
1166
+ deliveryParent: deliveryParent ?? parentContext,
1146
1167
  exeContext,
1147
1168
  });
1148
1169
  let promiseOrData;
@@ -1160,13 +1181,21 @@ function executeDeferredFragment(exeContext, parentType, sourceValue, fields, la
1160
1181
  promiseOrData = null;
1161
1182
  }
1162
1183
  asyncPayloadRecord.addData(promiseOrData);
1184
+ if (nestedPatches) {
1185
+ for (const nested of nestedPatches) {
1186
+ executeDeferredFragment(exeContext, parentType, sourceValue, nested.fields, nested.label, path,
1187
+ // Keep v0.1 sibling completion; v0.2 pending still nests via deliveryParent.
1188
+ parentContext, nested.patches, asyncPayloadRecord);
1189
+ }
1190
+ }
1163
1191
  }
1164
- function executeStreamField(path, itemPath, item, exeContext, fieldNodes, info, itemType, label, parentContext) {
1165
- const asyncPayloadRecord = new StreamRecord({
1192
+ function executeStreamField(path, itemPath, item, exeContext, fieldNodes, info, itemType, label, parentContext, streamGroup) {
1193
+ const asyncPayloadRecord = new incremental_js_1.StreamRecord({
1166
1194
  label,
1167
1195
  path: itemPath,
1168
1196
  parentContext,
1169
1197
  exeContext,
1198
+ streamGroup,
1170
1199
  });
1171
1200
  let completedItem;
1172
1201
  try {
@@ -1230,7 +1259,11 @@ async function executeStreamIteratorItem(iterator, exeContext, fieldNodes, info,
1230
1259
  const coercedError = (0, coerceError_js_1.coerceError)(rawError);
1231
1260
  const error = (0, utils_1.locatedError)(coercedError, fieldNodes, (0, utils_1.pathToArray)(itemPath), exeContext.schemaCoordinateInErrors && info);
1232
1261
  const value = handleFieldError(error, itemType, asyncPayloadRecord.errors);
1233
- // don't continue if iterator throws
1262
+ // Mark the group complete without isCompletedIterator, so the error item
1263
+ // is still published. Don't continue if iterator throws.
1264
+ if (asyncPayloadRecord.streamGroup) {
1265
+ asyncPayloadRecord.streamGroup.iteratorCompleted = true;
1266
+ }
1234
1267
  return { done: true, value };
1235
1268
  }
1236
1269
  let completedItem;
@@ -1253,17 +1286,18 @@ async function executeStreamIteratorItem(iterator, exeContext, fieldNodes, info,
1253
1286
  return { done: false, value };
1254
1287
  }
1255
1288
  }
1256
- async function executeStreamIterator(initialIndex, iterator, exeContext, fieldNodes, info, itemType, path, label, parentContext) {
1289
+ async function executeStreamIterator(initialIndex, iterator, exeContext, fieldNodes, info, itemType, path, label, parentContext, streamGroup) {
1257
1290
  let index = initialIndex;
1258
1291
  let previousAsyncPayloadRecord = parentContext ?? undefined;
1259
1292
  while (true) {
1260
1293
  const itemPath = (0, utils_1.addPath)(path, index, undefined);
1261
- const asyncPayloadRecord = new StreamRecord({
1294
+ const asyncPayloadRecord = new incremental_js_1.StreamRecord({
1262
1295
  label,
1263
1296
  path: itemPath,
1264
1297
  parentContext: previousAsyncPayloadRecord,
1265
1298
  iterator,
1266
1299
  exeContext,
1300
+ streamGroup,
1267
1301
  });
1268
1302
  let iteration;
1269
1303
  try {
@@ -1271,6 +1305,9 @@ async function executeStreamIterator(initialIndex, iterator, exeContext, fieldNo
1271
1305
  }
1272
1306
  catch (error) {
1273
1307
  asyncPayloadRecord.errors.push(error);
1308
+ if (streamGroup) {
1309
+ streamGroup.iteratorCompleted = true;
1310
+ }
1274
1311
  filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
1275
1312
  asyncPayloadRecord.addItems(null);
1276
1313
  // entire stream has errored and bubbled upwards
@@ -1315,193 +1352,14 @@ function filterSubsequentPayloads(exeContext, nullPath, currentAsyncRecord) {
1315
1352
  }
1316
1353
  }
1317
1354
  // asyncRecord path points to nulled error field
1318
- if (isStreamPayload(asyncRecord) && asyncRecord.iterator?.return) {
1355
+ if ((0, incremental_js_1.isStreamPayload)(asyncRecord) && asyncRecord.iterator?.return) {
1319
1356
  asyncRecord.iterator.return().catch(() => {
1320
1357
  // ignore error
1321
1358
  });
1322
1359
  }
1323
- exeContext.subsequentPayloads.delete(asyncRecord);
1360
+ (0, incremental_js_1.deleteAsyncPayload)(exeContext, asyncRecord);
1324
1361
  });
1325
1362
  }
1326
- function getCompletedIncrementalResults(exeContext) {
1327
- const incrementalResults = [];
1328
- for (const asyncPayloadRecord of exeContext.subsequentPayloads) {
1329
- const incrementalResult = {};
1330
- if (!asyncPayloadRecord.isCompleted) {
1331
- continue;
1332
- }
1333
- exeContext.subsequentPayloads.delete(asyncPayloadRecord);
1334
- if (isStreamPayload(asyncPayloadRecord)) {
1335
- const items = asyncPayloadRecord.items;
1336
- if (asyncPayloadRecord.isCompletedIterator) {
1337
- // async iterable resolver just finished but there may be pending payloads
1338
- continue;
1339
- }
1340
- incrementalResult.items = items;
1341
- }
1342
- else {
1343
- const data = asyncPayloadRecord.data;
1344
- incrementalResult.data = data ?? null;
1345
- }
1346
- incrementalResult.path = asyncPayloadRecord.path;
1347
- if (asyncPayloadRecord.label) {
1348
- incrementalResult.label = asyncPayloadRecord.label;
1349
- }
1350
- if (asyncPayloadRecord.errors.length > 0) {
1351
- incrementalResult.errors = asyncPayloadRecord.errors;
1352
- }
1353
- incrementalResults.push(incrementalResult);
1354
- }
1355
- return incrementalResults;
1356
- }
1357
- function yieldSubsequentPayloads(exeContext) {
1358
- let isDone = false;
1359
- async function next() {
1360
- if (isDone) {
1361
- return { value: undefined, done: true };
1362
- }
1363
- const subSequentPayloadPromises = Array.from(exeContext.subsequentPayloads).map(record => record.promise);
1364
- if (exeContext.signalPromise) {
1365
- await Promise.race([exeContext.signalPromise, ...subSequentPayloadPromises]);
1366
- }
1367
- else {
1368
- await Promise.race(subSequentPayloadPromises);
1369
- }
1370
- if (isDone) {
1371
- // a different call to next has exhausted all payloads
1372
- return { value: undefined, done: true };
1373
- }
1374
- const incremental = getCompletedIncrementalResults(exeContext);
1375
- const hasNext = exeContext.subsequentPayloads.size > 0;
1376
- if (!incremental.length && hasNext) {
1377
- return next();
1378
- }
1379
- if (!hasNext) {
1380
- isDone = true;
1381
- }
1382
- return {
1383
- value: incremental.length ? { incremental, hasNext } : { hasNext },
1384
- done: false,
1385
- };
1386
- }
1387
- function returnStreamIterators() {
1388
- const promises = [];
1389
- exeContext.subsequentPayloads.forEach(asyncPayloadRecord => {
1390
- if (isStreamPayload(asyncPayloadRecord) && asyncPayloadRecord.iterator?.return) {
1391
- promises.push(asyncPayloadRecord.iterator.return());
1392
- }
1393
- });
1394
- return Promise.all(promises);
1395
- }
1396
- return {
1397
- [Symbol.asyncIterator]() {
1398
- return this;
1399
- },
1400
- next,
1401
- async return() {
1402
- await returnStreamIterators();
1403
- isDone = true;
1404
- return { value: undefined, done: true };
1405
- },
1406
- async throw(error) {
1407
- await returnStreamIterators();
1408
- isDone = true;
1409
- throw error;
1410
- },
1411
- async [disposablestack_1.DisposableSymbols.asyncDispose]() {
1412
- await returnStreamIterators();
1413
- isDone = true;
1414
- },
1415
- };
1416
- }
1417
- class DeferredFragmentRecord {
1418
- type;
1419
- errors;
1420
- label;
1421
- path;
1422
- promise;
1423
- data;
1424
- parentContext;
1425
- isCompleted;
1426
- _exeContext;
1427
- _resolve;
1428
- constructor(opts) {
1429
- this.type = 'defer';
1430
- this.label = opts.label;
1431
- this.path = (0, utils_1.pathToArray)(opts.path);
1432
- this.parentContext = opts.parentContext;
1433
- this.errors = [];
1434
- this._exeContext = opts.exeContext;
1435
- this._exeContext.subsequentPayloads.add(this);
1436
- this.isCompleted = false;
1437
- this.data = null;
1438
- this.promise = new Promise(resolve => {
1439
- this._resolve = MaybePromise => {
1440
- resolve(MaybePromise);
1441
- };
1442
- }).then(data => {
1443
- this.data = data;
1444
- this.isCompleted = true;
1445
- });
1446
- }
1447
- addData(data) {
1448
- const parentData = this.parentContext?.promise;
1449
- if (parentData) {
1450
- this._resolve?.(parentData.then(() => data));
1451
- return;
1452
- }
1453
- this._resolve?.(data);
1454
- }
1455
- }
1456
- class StreamRecord {
1457
- type;
1458
- errors;
1459
- label;
1460
- path;
1461
- items;
1462
- promise;
1463
- parentContext;
1464
- iterator;
1465
- isCompletedIterator;
1466
- isCompleted;
1467
- _exeContext;
1468
- _resolve;
1469
- constructor(opts) {
1470
- this.type = 'stream';
1471
- this.items = null;
1472
- this.label = opts.label;
1473
- this.path = (0, utils_1.pathToArray)(opts.path);
1474
- this.parentContext = opts.parentContext;
1475
- this.iterator = opts.iterator;
1476
- this.errors = [];
1477
- this._exeContext = opts.exeContext;
1478
- this._exeContext.subsequentPayloads.add(this);
1479
- this.isCompleted = false;
1480
- this.items = null;
1481
- this.promise = new Promise(resolve => {
1482
- this._resolve = MaybePromise => {
1483
- resolve(MaybePromise);
1484
- };
1485
- }).then(items => {
1486
- this.items = items;
1487
- this.isCompleted = true;
1488
- });
1489
- }
1490
- addItems(items) {
1491
- const parentData = this.parentContext?.promise;
1492
- if (parentData) {
1493
- this._resolve?.(parentData.then(() => items));
1494
- return;
1495
- }
1496
- this._resolve?.(items);
1497
- }
1498
- setIsCompletedIterator() {
1499
- this.isCompletedIterator = true;
1500
- }
1501
- }
1502
- function isStreamPayload(asyncPayload) {
1503
- return asyncPayload.type === 'stream';
1504
- }
1505
1363
  /**
1506
1364
  * This method looks up the field on the given type definition.
1507
1365
  * It has special casing for the three introspection fields,