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