@graphql-tools/executor 4.1.1 → 4.1.2-alpha-20261020172239-6aecb886558928f54033e77a926c32d55e3e81cc
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 +174 -114
- package/cjs/execution/getBySet.js +13 -0
- package/cjs/execution/incremental.js +149 -4
- package/cjs/execution/incrementalCollect.js +189 -0
- package/cjs/execution/isSameSet.js +15 -0
- package/esm/execution/execute.js +171 -110
- package/esm/execution/getBySet.js +10 -0
- package/esm/execution/incremental.js +145 -4
- package/esm/execution/incrementalCollect.js +181 -0
- package/esm/execution/isSameSet.js +12 -0
- package/package.json +1 -1
- package/typings/execution/execute.d.cts +24 -19
- package/typings/execution/execute.d.ts +24 -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 +34 -0
- package/typings/execution/incrementalCollect.d.ts +34 -0
- package/typings/execution/isSameSet.d.cts +2 -0
- package/typings/execution/isSameSet.d.ts +2 -0
package/cjs/execution/execute.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
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 ??
|
|
333
|
-
typeResolver: typeResolver ??
|
|
334
|
-
subscribeFieldResolver: subscribeFieldResolver ??
|
|
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,125 @@ 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
|
+
ensureIncrementalCollectState(exeContext);
|
|
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, exeContext.deferUsageByFieldNode);
|
|
404
420
|
if (operation.operation === 'mutation') {
|
|
405
|
-
result = executeFieldsSerially(exeContext, rootType, rootValue, path,
|
|
421
|
+
result = executeFieldsSerially(exeContext, rootType, rootValue, path, plannedRootFields);
|
|
406
422
|
}
|
|
407
423
|
else {
|
|
408
|
-
result = executeFields(exeContext, rootType, rootValue, path,
|
|
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 remainingGroups = new Map();
|
|
435
|
+
const parts = new Map();
|
|
436
|
+
const groups = [...newGroupedFieldSets.entries()].sort(([a], [b]) => deferUsageDepth((0, incrementalCollect_js_1.rootDeferUsage)(a)) - deferUsageDepth((0, incrementalCollect_js_1.rootDeferUsage)(b)));
|
|
437
|
+
for (const [deferUsageSet] of groups) {
|
|
438
|
+
for (const usage of deferUsageSet) {
|
|
439
|
+
remainingGroups.set(usage, (remainingGroups.get(usage) ?? 0) + 1);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
const usages = [...remainingGroups.keys()].sort((a, b) => deferUsageDepth(a) - deferUsageDepth(b));
|
|
443
|
+
for (const usage of usages) {
|
|
444
|
+
const deliveryParent = (usage.parentDeferUsage && records.get(usage.parentDeferUsage)) ?? parentContext;
|
|
445
|
+
records.set(usage, new incremental_js_1.DeferredFragmentRecord({
|
|
446
|
+
label: usage.label,
|
|
447
|
+
path,
|
|
448
|
+
parentContext,
|
|
449
|
+
deliveryParent,
|
|
450
|
+
exeContext,
|
|
451
|
+
deferUsage: usage,
|
|
452
|
+
}));
|
|
453
|
+
}
|
|
454
|
+
for (const [deferUsageSet, groupedFieldSet] of groups) {
|
|
455
|
+
const fields = (0, incrementalCollect_js_1.groupedFieldSetToFieldMap)(groupedFieldSet, exeContext.deferUsageByFieldNode);
|
|
456
|
+
const fieldUsageStore = (exeContext.deferUsagesForFieldNodes ??= new WeakMap());
|
|
457
|
+
for (const fieldNodes of fields.values()) {
|
|
458
|
+
fieldUsageStore.set(fieldNodes, deferUsageSet);
|
|
459
|
+
}
|
|
460
|
+
const primaryUsage = (0, incrementalCollect_js_1.rootDeferUsage)(deferUsageSet);
|
|
461
|
+
const primaryRecord = primaryUsage ? records.get(primaryUsage) : undefined;
|
|
462
|
+
const data = executeDeferredGroupedFields(exeContext, parentType, sourceValue, path, fields, primaryRecord, [...deferUsageSet].map(usage => records.get(usage)).filter(record => record != null));
|
|
463
|
+
for (const usage of deferUsageSet) {
|
|
464
|
+
const list = parts.get(usage) ?? [];
|
|
465
|
+
list.push(data);
|
|
466
|
+
parts.set(usage, list);
|
|
467
|
+
const left = (remainingGroups.get(usage) ?? 1) - 1;
|
|
468
|
+
remainingGroups.set(usage, left);
|
|
469
|
+
if (left === 0) {
|
|
470
|
+
records.get(usage)?.addData(mergeDeferredParts(list));
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
function executeDeferredGroupedFields(exeContext, parentType, sourceValue, path, fields, primaryRecord, records) {
|
|
476
|
+
try {
|
|
477
|
+
const data = executeFields(exeContext, parentType, sourceValue, path, fields, primaryRecord);
|
|
478
|
+
if ((0, promise_helpers_1.isPromise)(data)) {
|
|
479
|
+
return data.then(null, e => {
|
|
480
|
+
for (const record of records) {
|
|
481
|
+
record.errors.push(e);
|
|
482
|
+
}
|
|
483
|
+
return null;
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
return data;
|
|
487
|
+
}
|
|
488
|
+
catch (e) {
|
|
489
|
+
for (const record of records) {
|
|
490
|
+
record.errors.push(e);
|
|
491
|
+
}
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
function mergeDeferredParts(parts) {
|
|
496
|
+
if (parts.some(promise_helpers_1.isPromise)) {
|
|
497
|
+
return Promise.all(parts).then(mergeDeferredObjects);
|
|
498
|
+
}
|
|
499
|
+
return mergeDeferredObjects(parts);
|
|
500
|
+
}
|
|
501
|
+
function mergeDeferredObjects(parts) {
|
|
502
|
+
const result = Object.create(null);
|
|
503
|
+
let sawObject = false;
|
|
504
|
+
for (const part of parts) {
|
|
505
|
+
if (part == null) {
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
sawObject = true;
|
|
509
|
+
Object.assign(result, part);
|
|
510
|
+
}
|
|
511
|
+
return sawObject ? result : null;
|
|
512
|
+
}
|
|
513
|
+
function ensureIncrementalCollectState(exeContext) {
|
|
514
|
+
exeContext.nulledPaths ??= [];
|
|
515
|
+
exeContext.deferUsageByFieldNode ??= new WeakMap();
|
|
516
|
+
}
|
|
517
|
+
function deferUsageDepth(usage) {
|
|
518
|
+
let depth = 0;
|
|
519
|
+
let current = usage;
|
|
520
|
+
while (current?.parentDeferUsage) {
|
|
521
|
+
depth++;
|
|
522
|
+
current = current.parentDeferUsage;
|
|
523
|
+
}
|
|
524
|
+
return depth;
|
|
525
|
+
}
|
|
416
526
|
/**
|
|
417
527
|
* Implements the "Executing selection sets" section of the spec
|
|
418
528
|
* for fields that must be executed serially.
|
|
@@ -886,71 +996,41 @@ function invalidReturnTypeError(returnType, result, fieldNodes) {
|
|
|
886
996
|
function collectAndExecuteSubfields(exeContext, returnType, fieldNodes, path, result, asyncPayloadRecord) {
|
|
887
997
|
// Collect sub-fields to execute to complete this value.
|
|
888
998
|
const { fields: subFieldNodes, patches: subPatches } = collectSubfields(exeContext, returnType, fieldNodes);
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
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
|
-
}
|
|
999
|
+
if (subPatches.length === 0 && exeContext.nulledPaths == null) {
|
|
1000
|
+
return executeFields(exeContext, returnType, result, path, subFieldNodes, asyncPayloadRecord);
|
|
1001
|
+
}
|
|
1002
|
+
ensureIncrementalCollectState(exeContext);
|
|
1003
|
+
const parentDeferUsage = asyncPayloadRecord?.type === 'defer' ? asyncPayloadRecord.deferUsage : undefined;
|
|
1004
|
+
const subfieldCache = (exeContext.incrementalSubfieldCache ??= new WeakMap());
|
|
1005
|
+
let collected = subfieldCache.get(fieldNodes);
|
|
1006
|
+
if (collected == null) {
|
|
1007
|
+
collected = (0, incrementalCollect_js_1.collectIncrementalSubfields)(exeContext.schema, exeContext.fragments, exeContext.variableValues, returnType, fieldNodes, parentDeferUsage, exeContext.deferUsageByFieldNode);
|
|
1008
|
+
subfieldCache.set(fieldNodes, collected);
|
|
1009
|
+
}
|
|
1010
|
+
const { groupedFieldSet } = collected;
|
|
1011
|
+
const parentDeferUsages = new Set();
|
|
1012
|
+
const plannedUsages = exeContext.deferUsagesForFieldNodes?.get(fieldNodes);
|
|
1013
|
+
if (plannedUsages) {
|
|
1014
|
+
for (const usage of plannedUsages) {
|
|
1015
|
+
parentDeferUsages.add(usage);
|
|
924
1016
|
}
|
|
925
1017
|
}
|
|
926
|
-
if (
|
|
927
|
-
|
|
928
|
-
for (let i = 0; i < isTypeOfResults.length; i++) {
|
|
929
|
-
if (isTypeOfResults[i]) {
|
|
930
|
-
return possibleTypes[i].name;
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
});
|
|
1018
|
+
else if (parentDeferUsage) {
|
|
1019
|
+
parentDeferUsages.add(parentDeferUsage);
|
|
934
1020
|
}
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
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);
|
|
1021
|
+
else if (asyncPayloadRecord?.type === 'stream') {
|
|
1022
|
+
for (const node of fieldNodes) {
|
|
1023
|
+
const usage = exeContext.deferUsageByFieldNode?.get(node);
|
|
1024
|
+
if (usage) {
|
|
1025
|
+
parentDeferUsages.add(usage);
|
|
1026
|
+
}
|
|
949
1027
|
}
|
|
950
|
-
return property;
|
|
951
1028
|
}
|
|
952
|
-
};
|
|
953
|
-
|
|
1029
|
+
const { groupedFieldSet: initialFields, newGroupedFieldSets } = (0, incrementalCollect_js_1.buildExecutionPlan)(groupedFieldSet, parentDeferUsages);
|
|
1030
|
+
const subFields = executeFields(exeContext, returnType, result, path, (0, incrementalCollect_js_1.groupedFieldSetToFieldMap)(initialFields, exeContext.deferUsageByFieldNode), asyncPayloadRecord);
|
|
1031
|
+
executeDeferredGroups(exeContext, returnType, result, path, newGroupedFieldSets, asyncPayloadRecord);
|
|
1032
|
+
return subFields;
|
|
1033
|
+
}
|
|
954
1034
|
/**
|
|
955
1035
|
* Implements the "Subscribe" algorithm described in the GraphQL specification,
|
|
956
1036
|
* including `@defer` and `@stream` as proposed in
|
|
@@ -1158,37 +1238,6 @@ function assertEventStream(result, signal, onSignalAbort) {
|
|
|
1158
1238
|
}
|
|
1159
1239
|
return result;
|
|
1160
1240
|
}
|
|
1161
|
-
function executeDeferredFragment(exeContext, parentType, sourceValue, fields, label, path, parentContext, nestedPatches, deliveryParent) {
|
|
1162
|
-
const asyncPayloadRecord = new incremental_js_1.DeferredFragmentRecord({
|
|
1163
|
-
label,
|
|
1164
|
-
path,
|
|
1165
|
-
parentContext,
|
|
1166
|
-
deliveryParent: deliveryParent ?? parentContext,
|
|
1167
|
-
exeContext,
|
|
1168
|
-
});
|
|
1169
|
-
let promiseOrData;
|
|
1170
|
-
try {
|
|
1171
|
-
promiseOrData = executeFields(exeContext, parentType, sourceValue, path, fields, asyncPayloadRecord);
|
|
1172
|
-
if ((0, promise_helpers_1.isPromise)(promiseOrData)) {
|
|
1173
|
-
promiseOrData = promiseOrData.then(null, e => {
|
|
1174
|
-
asyncPayloadRecord.errors.push(e);
|
|
1175
|
-
return null;
|
|
1176
|
-
});
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
catch (e) {
|
|
1180
|
-
asyncPayloadRecord.errors.push(e);
|
|
1181
|
-
promiseOrData = null;
|
|
1182
|
-
}
|
|
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
|
-
}
|
|
1191
|
-
}
|
|
1192
1241
|
function executeStreamField(path, itemPath, item, exeContext, fieldNodes, info, itemType, label, parentContext, streamGroup) {
|
|
1193
1242
|
const asyncPayloadRecord = new incremental_js_1.StreamRecord({
|
|
1194
1243
|
label,
|
|
@@ -1197,6 +1246,10 @@ function executeStreamField(path, itemPath, item, exeContext, fieldNodes, info,
|
|
|
1197
1246
|
exeContext,
|
|
1198
1247
|
streamGroup,
|
|
1199
1248
|
});
|
|
1249
|
+
if ((0, incremental_js_1.isAtOrUnderNulledPath)(asyncPayloadRecord.path, exeContext.nulledPaths)) {
|
|
1250
|
+
(0, incremental_js_1.cancelAsyncPayload)(exeContext, asyncPayloadRecord);
|
|
1251
|
+
return asyncPayloadRecord;
|
|
1252
|
+
}
|
|
1200
1253
|
let completedItem;
|
|
1201
1254
|
try {
|
|
1202
1255
|
try {
|
|
@@ -1299,6 +1352,15 @@ async function executeStreamIterator(initialIndex, iterator, exeContext, fieldNo
|
|
|
1299
1352
|
exeContext,
|
|
1300
1353
|
streamGroup,
|
|
1301
1354
|
});
|
|
1355
|
+
if ((0, incremental_js_1.isAtOrUnderNulledPath)(asyncPayloadRecord.path, exeContext.nulledPaths)) {
|
|
1356
|
+
(0, incremental_js_1.cancelAsyncPayload)(exeContext, asyncPayloadRecord);
|
|
1357
|
+
if (iterator.return) {
|
|
1358
|
+
iterator.return().catch(() => {
|
|
1359
|
+
// ignore errors
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
break;
|
|
1363
|
+
}
|
|
1302
1364
|
let iteration;
|
|
1303
1365
|
try {
|
|
1304
1366
|
iteration = await executeStreamIteratorItem(iterator, exeContext, fieldNodes, info, itemType, asyncPayloadRecord, itemPath);
|
|
@@ -1340,24 +1402,22 @@ async function executeStreamIterator(initialIndex, iterator, exeContext, fieldNo
|
|
|
1340
1402
|
}
|
|
1341
1403
|
function filterSubsequentPayloads(exeContext, nullPath, currentAsyncRecord) {
|
|
1342
1404
|
const nullPathArray = (0, utils_1.pathToArray)(nullPath);
|
|
1405
|
+
if (exeContext.nulledPaths) {
|
|
1406
|
+
exeContext.nulledPaths.push(nullPathArray);
|
|
1407
|
+
}
|
|
1408
|
+
else if (exeContext.subsequentPayloads.size === 0) {
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1343
1411
|
exeContext.subsequentPayloads.forEach(asyncRecord => {
|
|
1344
|
-
if (asyncRecord === currentAsyncRecord) {
|
|
1412
|
+
if (asyncRecord === currentAsyncRecord || asyncRecord.cancelled) {
|
|
1345
1413
|
// don't remove payload from where error originates
|
|
1346
1414
|
return;
|
|
1347
1415
|
}
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
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
|
-
});
|
|
1416
|
+
if (!(0, incremental_js_1.pathStartsWith)(asyncRecord.path, nullPathArray)) {
|
|
1417
|
+
// ancestor or a different branch: ancestor patches are pruned at yield
|
|
1418
|
+
return;
|
|
1359
1419
|
}
|
|
1360
|
-
(0, incremental_js_1.
|
|
1420
|
+
(0, incremental_js_1.cancelAsyncPayload)(exeContext, asyncRecord);
|
|
1361
1421
|
});
|
|
1362
1422
|
}
|
|
1363
1423
|
/**
|
|
@@ -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,122 @@ 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
|
+
return (typeof data === 'object' &&
|
|
104
|
+
!Array.isArray(data) &&
|
|
105
|
+
Object.keys(data).length === 0);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Drop keys that would write into a subtree already nulled by error bubbling.
|
|
109
|
+
* Ancestor deferred records can still deliver sibling fields (e.g. `someField`
|
|
110
|
+
* next to a nulled `b.c`).
|
|
111
|
+
*/
|
|
112
|
+
function pruneUnmergeableIncrementalData(data, basePath, nulledPaths) {
|
|
113
|
+
if (nulledPaths == null || nulledPaths.length === 0 || data == null || typeof data !== 'object') {
|
|
114
|
+
return data;
|
|
115
|
+
}
|
|
116
|
+
if (Array.isArray(data)) {
|
|
117
|
+
return data.map((item, index) => {
|
|
118
|
+
const itemPath = [...basePath, index];
|
|
119
|
+
if (isAtOrUnderNulledPath(itemPath, nulledPaths)) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
return pruneUnmergeableIncrementalData(item, itemPath, nulledPaths);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
const result = {};
|
|
126
|
+
for (const [key, value] of Object.entries(data)) {
|
|
127
|
+
const childPath = [...basePath, key];
|
|
128
|
+
if (isStrictlyUnderNulledPath(childPath, nulledPaths)) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (isExactNulledPath(childPath, nulledPaths)) {
|
|
132
|
+
// Keep a null delivered by this payload; drop a non-null write into an
|
|
133
|
+
// already-nulled object (Yaacov: unmergeable incremental).
|
|
134
|
+
if (value != null) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
result[key] = null;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
result[key] = pruneUnmergeableIncrementalData(value, childPath, nulledPaths);
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
function isAnnounced(exeContext, record) {
|
|
145
|
+
const state = exeContext.publisher;
|
|
146
|
+
if (state == null) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
return state.announced.has(getPublishNode(record));
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Cancel a subsequent record whose path is at or under a nulled position
|
|
153
|
+
* (graphql-js 17 `hasNulledPosition`). v0.2 records that were already
|
|
154
|
+
* announced stay in the set so the publisher can emit `completed` without
|
|
155
|
+
* incremental data.
|
|
156
|
+
*/
|
|
157
|
+
function cancelAsyncPayload(exeContext, record) {
|
|
158
|
+
record.cancelled = true;
|
|
159
|
+
if (isStreamPayload(record) && record.iterator?.return) {
|
|
160
|
+
record.iterator.return().catch(() => {
|
|
161
|
+
// ignore error
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
if (!record.isCompleted) {
|
|
165
|
+
if (isStreamPayload(record)) {
|
|
166
|
+
record.addItems(null);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
record.addData(null);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (exeContext.incrementalSpec === 'v0.2' && isAnnounced(exeContext, record)) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
deleteAsyncPayload(exeContext, record);
|
|
176
|
+
}
|
|
57
177
|
class DeferredFragmentRecord {
|
|
58
178
|
type;
|
|
59
179
|
errors;
|
|
@@ -63,7 +183,9 @@ class DeferredFragmentRecord {
|
|
|
63
183
|
data;
|
|
64
184
|
parentContext;
|
|
65
185
|
deliveryParent;
|
|
186
|
+
deferUsage;
|
|
66
187
|
isCompleted;
|
|
188
|
+
cancelled;
|
|
67
189
|
_exeContext;
|
|
68
190
|
_resolve;
|
|
69
191
|
constructor(opts) {
|
|
@@ -72,10 +194,12 @@ class DeferredFragmentRecord {
|
|
|
72
194
|
this.path = (0, utils_1.pathToArray)(opts.path);
|
|
73
195
|
this.parentContext = opts.parentContext;
|
|
74
196
|
this.deliveryParent = opts.deliveryParent ?? opts.parentContext;
|
|
197
|
+
this.deferUsage = opts.deferUsage;
|
|
75
198
|
this.errors = [];
|
|
76
199
|
this._exeContext = opts.exeContext;
|
|
77
200
|
this._exeContext.subsequentPayloads.add(this);
|
|
78
201
|
this.isCompleted = false;
|
|
202
|
+
this.cancelled = false;
|
|
79
203
|
this.data = null;
|
|
80
204
|
this.promise = new Promise(resolve => {
|
|
81
205
|
this._resolve = MaybePromise => {
|
|
@@ -107,6 +231,7 @@ class StreamRecord {
|
|
|
107
231
|
iterator;
|
|
108
232
|
isCompletedIterator;
|
|
109
233
|
isCompleted;
|
|
234
|
+
cancelled;
|
|
110
235
|
streamGroup;
|
|
111
236
|
_exeContext;
|
|
112
237
|
_resolve;
|
|
@@ -123,6 +248,7 @@ class StreamRecord {
|
|
|
123
248
|
this._exeContext.subsequentPayloads.add(this);
|
|
124
249
|
this.streamGroup?.retain();
|
|
125
250
|
this.isCompleted = false;
|
|
251
|
+
this.cancelled = false;
|
|
126
252
|
this.items = null;
|
|
127
253
|
this.promise = new Promise(resolve => {
|
|
128
254
|
this._resolve = MaybePromise => {
|
|
@@ -190,6 +316,9 @@ function toPendingResult(state, record) {
|
|
|
190
316
|
return pending;
|
|
191
317
|
}
|
|
192
318
|
function announceIfNeeded(record, state, pending) {
|
|
319
|
+
if (record.cancelled) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
193
322
|
const node = getPublishNode(record);
|
|
194
323
|
if (state.announced.has(node)) {
|
|
195
324
|
return;
|
|
@@ -222,6 +351,9 @@ function getCompletedIncrementalResults(exeContext) {
|
|
|
222
351
|
continue;
|
|
223
352
|
}
|
|
224
353
|
deleteAsyncPayload(exeContext, asyncPayloadRecord);
|
|
354
|
+
if (asyncPayloadRecord.cancelled) {
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
225
357
|
if (isStreamPayload(asyncPayloadRecord)) {
|
|
226
358
|
const items = asyncPayloadRecord.items;
|
|
227
359
|
if (asyncPayloadRecord.isCompletedIterator) {
|
|
@@ -231,8 +363,12 @@ function getCompletedIncrementalResults(exeContext) {
|
|
|
231
363
|
incrementalResult.items = items;
|
|
232
364
|
}
|
|
233
365
|
else {
|
|
234
|
-
const data = asyncPayloadRecord.data;
|
|
235
|
-
|
|
366
|
+
const data = pruneUnmergeableIncrementalData(asyncPayloadRecord.data, asyncPayloadRecord.path, exeContext.nulledPaths);
|
|
367
|
+
if (isEmptyIncrementalData(data) && asyncPayloadRecord.errors.length === 0) {
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
incrementalResult.data =
|
|
371
|
+
data ?? null;
|
|
236
372
|
}
|
|
237
373
|
incrementalResult.path = asyncPayloadRecord.path;
|
|
238
374
|
if (asyncPayloadRecord.label) {
|
|
@@ -272,13 +408,22 @@ function buildSubsequentV02(exeContext, completedRecords) {
|
|
|
272
408
|
continue;
|
|
273
409
|
}
|
|
274
410
|
const id = ensureId(state, record);
|
|
275
|
-
if (record.
|
|
411
|
+
if (record.cancelled) {
|
|
412
|
+
completed.push({ id });
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
const data = pruneUnmergeableIncrementalData(record.data, record.path, exeContext.nulledPaths);
|
|
416
|
+
if ((record.data == null && record.errors.length > 0) ||
|
|
417
|
+
(isEmptyIncrementalData(data) && record.errors.length > 0)) {
|
|
276
418
|
completed.push({ id, errors: record.errors });
|
|
277
419
|
}
|
|
420
|
+
else if (isEmptyIncrementalData(data) && record.errors.length === 0) {
|
|
421
|
+
completed.push({ id });
|
|
422
|
+
}
|
|
278
423
|
else {
|
|
279
424
|
const incrementalResult = {
|
|
280
425
|
id,
|
|
281
|
-
data:
|
|
426
|
+
data: data ?? null,
|
|
282
427
|
};
|
|
283
428
|
if (record.errors.length > 0) {
|
|
284
429
|
incrementalResult.errors = record.errors;
|