@graphql-tools/executor 1.1.0 → 1.2.0-alpha-20230809163713-4e6faf66

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.
@@ -2,12 +2,12 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isIncrementalResult = exports.getFieldDef = exports.flattenIncrementalResults = exports.subscribe = exports.defaultFieldResolver = exports.defaultTypeResolver = exports.buildResolveInfo = exports.buildExecutionContext = exports.getFragmentsFromDocument = exports.assertValidExecutionArguments = exports.executeSync = exports.execute = void 0;
4
4
  const graphql_1 = require("graphql");
5
+ const value_or_promise_1 = require("value-or-promise");
5
6
  const utils_1 = require("@graphql-tools/utils");
6
- const values_js_1 = require("./values.js");
7
- const promiseForObject_js_1 = require("./promiseForObject.js");
8
7
  const flattenAsyncIterable_js_1 = require("./flattenAsyncIterable.js");
9
8
  const invariant_js_1 = require("./invariant.js");
10
- const value_or_promise_1 = require("value-or-promise");
9
+ const promiseForObject_js_1 = require("./promiseForObject.js");
10
+ const values_js_1 = require("./values.js");
11
11
  /**
12
12
  * A memoized collection of relevant subfields with regard to the return
13
13
  * type. Memoizing ensures the subfields are not repeatedly calculated, which
@@ -135,7 +135,7 @@ exports.getFragmentsFromDocument = (0, utils_1.memoize1)(function getFragmentsFr
135
135
  * @internal
136
136
  */
137
137
  function buildExecutionContext(args) {
138
- const { schema, document, rootValue, contextValue, variableValues: rawVariableValues, operationName, fieldResolver, typeResolver, subscribeFieldResolver, } = args;
138
+ const { schema, document, rootValue, contextValue, variableValues: rawVariableValues, operationName, fieldResolver, typeResolver, subscribeFieldResolver, signal, } = args;
139
139
  // If the schema used for execution is invalid, throw an error.
140
140
  (0, graphql_1.assertValidSchema)(schema);
141
141
  const fragments = (0, exports.getFragmentsFromDocument)(document);
@@ -145,7 +145,9 @@ function buildExecutionContext(args) {
145
145
  case graphql_1.Kind.OPERATION_DEFINITION:
146
146
  if (operationName == null) {
147
147
  if (operation !== undefined) {
148
- return [(0, utils_1.createGraphQLError)('Must provide operation name if query contains multiple operations.')];
148
+ return [
149
+ (0, utils_1.createGraphQLError)('Must provide operation name if query contains multiple operations.'),
150
+ ];
149
151
  }
150
152
  operation = definition;
151
153
  }
@@ -184,6 +186,7 @@ function buildExecutionContext(args) {
184
186
  subscribeFieldResolver: subscribeFieldResolver ?? exports.defaultFieldResolver,
185
187
  subsequentPayloads: new Set(),
186
188
  errors: [],
189
+ signal,
187
190
  };
188
191
  }
189
192
  exports.buildExecutionContext = buildExecutionContext;
@@ -226,13 +229,26 @@ function executeOperation(exeContext) {
226
229
  * for fields that must be executed serially.
227
230
  */
228
231
  function executeFieldsSerially(exeContext, parentType, sourceValue, path, fields) {
232
+ let abortErrorThrown = false;
229
233
  return (0, utils_1.promiseReduce)(fields, (results, [responseName, fieldNodes]) => {
230
234
  const fieldPath = (0, utils_1.addPath)(path, responseName, parentType.name);
235
+ if (exeContext.signal?.aborted) {
236
+ results[responseName] = null;
237
+ return results;
238
+ }
231
239
  return new value_or_promise_1.ValueOrPromise(() => executeField(exeContext, parentType, sourceValue, fieldNodes, fieldPath)).then(result => {
232
240
  if (result === undefined) {
233
241
  return results;
234
242
  }
235
243
  results[responseName] = result;
244
+ if (exeContext.signal?.aborted && !abortErrorThrown) {
245
+ exeContext.errors.push((0, utils_1.createGraphQLError)('Execution aborted', {
246
+ nodes: fieldNodes,
247
+ path: (0, utils_1.pathToArray)(fieldPath),
248
+ originalError: exeContext.signal?.reason,
249
+ }));
250
+ abortErrorThrown = true;
251
+ }
236
252
  return results;
237
253
  });
238
254
  }, Object.create(null)).resolve();
@@ -244,8 +260,13 @@ function executeFieldsSerially(exeContext, parentType, sourceValue, path, fields
244
260
  function executeFields(exeContext, parentType, sourceValue, path, fields, asyncPayloadRecord) {
245
261
  const results = Object.create(null);
246
262
  let containsPromise = false;
263
+ let abortErrorThrown = false;
247
264
  try {
248
265
  for (const [responseName, fieldNodes] of fields) {
266
+ if (exeContext.signal?.aborted) {
267
+ results[responseName] = null;
268
+ continue;
269
+ }
249
270
  const fieldPath = (0, utils_1.addPath)(path, responseName, parentType.name);
250
271
  const result = executeField(exeContext, parentType, sourceValue, fieldNodes, fieldPath, asyncPayloadRecord);
251
272
  if (result !== undefined) {
@@ -254,12 +275,20 @@ function executeFields(exeContext, parentType, sourceValue, path, fields, asyncP
254
275
  containsPromise = true;
255
276
  }
256
277
  }
278
+ if (exeContext.signal?.aborted && !abortErrorThrown) {
279
+ exeContext.errors.push((0, utils_1.createGraphQLError)('Execution aborted', {
280
+ nodes: fieldNodes,
281
+ path: (0, utils_1.pathToArray)(fieldPath),
282
+ originalError: exeContext.signal?.reason,
283
+ }));
284
+ abortErrorThrown = true;
285
+ }
257
286
  }
258
287
  }
259
288
  catch (error) {
260
289
  if (containsPromise) {
261
290
  // Ensure that any promises returned by other fields are handled, as they may also reject.
262
- return (0, promiseForObject_js_1.promiseForObject)(results).finally(() => {
291
+ return (0, promiseForObject_js_1.promiseForObject)(results, exeContext.signal).finally(() => {
263
292
  throw error;
264
293
  });
265
294
  }
@@ -272,7 +301,7 @@ function executeFields(exeContext, parentType, sourceValue, path, fields, asyncP
272
301
  // Otherwise, results is a map from field name to the result of resolving that
273
302
  // field, which is possibly a promise. Return a promise that will return this
274
303
  // same map, but with any promises replaced with the values they resolved to.
275
- return (0, promiseForObject_js_1.promiseForObject)(results);
304
+ return (0, promiseForObject_js_1.promiseForObject)(results, exeContext.signal);
276
305
  }
277
306
  /**
278
307
  * Implements the "Executing fields" section of the spec
@@ -450,6 +479,14 @@ function getStreamValues(exeContext, fieldNodes, path) {
450
479
  * recursively until all the results are completed.
451
480
  */
452
481
  async function completeAsyncIteratorValue(exeContext, itemType, fieldNodes, info, path, iterator, asyncPayloadRecord) {
482
+ exeContext.signal?.addEventListener('abort', () => {
483
+ iterator.return?.();
484
+ exeContext.errors.push((0, utils_1.createGraphQLError)('Execution aborted', {
485
+ nodes: fieldNodes,
486
+ path: (0, utils_1.pathToArray)(path),
487
+ originalError: exeContext.signal?.reason,
488
+ }));
489
+ });
453
490
  const errors = asyncPayloadRecord?.errors ?? exeContext.errors;
454
491
  const stream = getStreamValues(exeContext, fieldNodes, path);
455
492
  let containsPromise = false;
@@ -772,20 +809,24 @@ function subscribe(args) {
772
809
  return mapSourceToResponse(exeContext, resultOrStream);
773
810
  }
774
811
  exports.subscribe = subscribe;
775
- function flattenIncrementalResults(incrementalResults) {
812
+ function flattenIncrementalResults(incrementalResults, signal) {
776
813
  const subsequentIterator = incrementalResults.subsequentResults;
777
814
  let initialResultSent = false;
778
815
  let done = false;
816
+ signal?.addEventListener('abort', () => {
817
+ done = true;
818
+ subsequentIterator.throw?.(signal?.reason);
819
+ });
779
820
  return {
780
821
  [Symbol.asyncIterator]() {
781
822
  return this;
782
823
  },
783
- async next() {
824
+ next() {
784
825
  if (done) {
785
- return {
826
+ return Promise.resolve({
786
827
  value: undefined,
787
828
  done,
788
- };
829
+ });
789
830
  }
790
831
  if (initialResultSent) {
791
832
  return subsequentIterator.next();
@@ -807,9 +848,9 @@ function flattenIncrementalResults(incrementalResults) {
807
848
  };
808
849
  }
809
850
  exports.flattenIncrementalResults = flattenIncrementalResults;
810
- async function* ensureAsyncIterable(someExecutionResult) {
851
+ async function* ensureAsyncIterable(someExecutionResult, signal) {
811
852
  if ('initialResult' in someExecutionResult) {
812
- yield* flattenIncrementalResults(someExecutionResult);
853
+ yield* flattenIncrementalResults(someExecutionResult, signal);
813
854
  }
814
855
  else {
815
856
  yield someExecutionResult;
@@ -825,7 +866,7 @@ function mapSourceToResponse(exeContext, resultOrStream) {
825
866
  // the GraphQL specification. The `execute` function provides the
826
867
  // "ExecuteSubscriptionEvent" algorithm, as it is nearly identical to the
827
868
  // "ExecuteQuery" algorithm, for which `execute` is also used.
828
- return (0, flattenAsyncIterable_js_1.flattenAsyncIterable)((0, utils_1.mapAsyncIterator)(resultOrStream[Symbol.asyncIterator](), async (payload) => ensureAsyncIterable(await executeImpl(buildPerEventExecutionContext(exeContext, payload))), async function* (error) {
869
+ return (0, flattenAsyncIterable_js_1.flattenAsyncIterable)((0, utils_1.mapAsyncIterator)(resultOrStream[Symbol.asyncIterator](), async (payload) => ensureAsyncIterable(await executeImpl(buildPerEventExecutionContext(exeContext, payload)), exeContext.signal), async function* (error) {
829
870
  const wrappedError = (0, utils_1.createGraphQLError)(error.message, {
830
871
  originalError: error,
831
872
  nodes: [exeContext.operation],
@@ -851,14 +892,18 @@ function executeSubscription(exeContext) {
851
892
  const { schema, fragments, operation, variableValues, rootValue } = exeContext;
852
893
  const rootType = schema.getSubscriptionType();
853
894
  if (rootType == null) {
854
- throw (0, utils_1.createGraphQLError)('Schema is not configured to execute subscription operation.', { nodes: operation });
895
+ throw (0, utils_1.createGraphQLError)('Schema is not configured to execute subscription operation.', {
896
+ nodes: operation,
897
+ });
855
898
  }
856
899
  const { fields: rootFields } = (0, utils_1.collectFields)(schema, fragments, variableValues, rootType, operation.selectionSet);
857
900
  const [responseName, fieldNodes] = [...rootFields.entries()][0];
858
901
  const fieldName = fieldNodes[0].name.value;
859
902
  const fieldDef = getFieldDef(schema, rootType, fieldNodes[0]);
860
903
  if (!fieldDef) {
861
- throw (0, utils_1.createGraphQLError)(`The subscription field "${fieldName}" is not defined.`, { nodes: fieldNodes });
904
+ throw (0, utils_1.createGraphQLError)(`The subscription field "${fieldName}" is not defined.`, {
905
+ nodes: fieldNodes,
906
+ });
862
907
  }
863
908
  const path = (0, utils_1.addPath)(undefined, responseName, rootType.name);
864
909
  const info = buildResolveInfo(exeContext, fieldDef, fieldNodes, rootType, path);
@@ -881,13 +926,13 @@ function executeSubscription(exeContext) {
881
926
  throw (0, graphql_1.locatedError)(error, fieldNodes, (0, utils_1.pathToArray)(path));
882
927
  });
883
928
  }
884
- return assertEventStream(result);
929
+ return assertEventStream(result, exeContext.signal);
885
930
  }
886
931
  catch (error) {
887
932
  throw (0, graphql_1.locatedError)(error, fieldNodes, (0, utils_1.pathToArray)(path));
888
933
  }
889
934
  }
890
- function assertEventStream(result) {
935
+ function assertEventStream(result, signal) {
891
936
  if (result instanceof Error) {
892
937
  throw result;
893
938
  }
@@ -895,7 +940,15 @@ function assertEventStream(result) {
895
940
  if (!(0, utils_1.isAsyncIterable)(result)) {
896
941
  throw (0, utils_1.createGraphQLError)('Subscription field must return Async Iterable. ' + `Received: ${(0, utils_1.inspect)(result)}.`);
897
942
  }
898
- return result;
943
+ return {
944
+ [Symbol.asyncIterator]() {
945
+ const asyncIterator = result[Symbol.asyncIterator]();
946
+ signal?.addEventListener('abort', () => {
947
+ asyncIterator.return?.();
948
+ });
949
+ return asyncIterator;
950
+ },
951
+ };
899
952
  }
900
953
  function executeDeferredFragment(exeContext, parentType, sourceValue, fields, label, path, parentContext) {
901
954
  const asyncPayloadRecord = new DeferredFragmentRecord({
@@ -2,8 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.normalizedExecutor = void 0;
4
4
  const graphql_1 = require("graphql");
5
- const execute_js_1 = require("./execute.js");
6
5
  const value_or_promise_1 = require("value-or-promise");
6
+ const execute_js_1 = require("./execute.js");
7
7
  function normalizedExecutor(args) {
8
8
  const operationAST = (0, graphql_1.getOperationAST)(args.document, args.operationName);
9
9
  if (operationAST == null) {
@@ -15,7 +15,7 @@ function normalizedExecutor(args) {
15
15
  return new value_or_promise_1.ValueOrPromise(() => (0, execute_js_1.execute)(args))
16
16
  .then((result) => {
17
17
  if ('initialResult' in result) {
18
- return (0, execute_js_1.flattenIncrementalResults)(result);
18
+ return (0, execute_js_1.flattenIncrementalResults)(result, args.signal);
19
19
  }
20
20
  return result;
21
21
  })
@@ -8,11 +8,16 @@ exports.promiseForObject = void 0;
8
8
  * This is akin to bluebird's `Promise.props`, but implemented only using
9
9
  * `Promise.all` so it will work with any implementation of ES6 promises.
10
10
  */
11
- async function promiseForObject(object) {
11
+ async function promiseForObject(object, signal) {
12
12
  const resolvedObject = Object.create(null);
13
- await Promise.all(Object.entries(object).map(async ([key, value]) => {
14
- resolvedObject[key] = await value;
15
- }));
13
+ await new Promise((resolve, reject) => {
14
+ signal?.addEventListener('abort', () => {
15
+ resolve();
16
+ });
17
+ Promise.all(Object.entries(object).map(async ([key, value]) => {
18
+ resolvedObject[key] = await value;
19
+ })).then(() => resolve(), reject);
20
+ });
16
21
  return resolvedObject;
17
22
  }
18
23
  exports.promiseForObject = promiseForObject;
@@ -1,10 +1,10 @@
1
- import { locatedError, Kind, isAbstractType, isLeafType, isListType, isNonNullType, isObjectType, assertValidSchema, getDirectiveValues, SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef, GraphQLError, } from 'graphql';
2
- import { createGraphQLError, inspect, isAsyncIterable, isIterableObject, isObjectLike, isPromise, pathToArray, addPath, getArgumentValues, promiseReduce, memoize3, getDefinedRootType, mapAsyncIterator, GraphQLStreamDirective, collectFields, collectSubFields as _collectSubfields, memoize1, } from '@graphql-tools/utils';
3
- import { getVariableValues } from './values.js';
4
- import { promiseForObject } from './promiseForObject.js';
1
+ import { assertValidSchema, getDirectiveValues, GraphQLError, isAbstractType, isLeafType, isListType, isNonNullType, isObjectType, Kind, locatedError, SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef, } from 'graphql';
2
+ import { ValueOrPromise } from 'value-or-promise';
3
+ import { collectSubFields as _collectSubfields, addPath, collectFields, createGraphQLError, getArgumentValues, getDefinedRootType, GraphQLStreamDirective, inspect, isAsyncIterable, isIterableObject, isObjectLike, isPromise, mapAsyncIterator, memoize1, memoize3, pathToArray, promiseReduce, } from '@graphql-tools/utils';
5
4
  import { flattenAsyncIterable } from './flattenAsyncIterable.js';
6
5
  import { invariant } from './invariant.js';
7
- import { ValueOrPromise } from 'value-or-promise';
6
+ import { promiseForObject } from './promiseForObject.js';
7
+ import { getVariableValues } from './values.js';
8
8
  /**
9
9
  * A memoized collection of relevant subfields with regard to the return
10
10
  * type. Memoizing ensures the subfields are not repeatedly calculated, which
@@ -129,7 +129,7 @@ export const getFragmentsFromDocument = memoize1(function getFragmentsFromDocume
129
129
  * @internal
130
130
  */
131
131
  export function buildExecutionContext(args) {
132
- const { schema, document, rootValue, contextValue, variableValues: rawVariableValues, operationName, fieldResolver, typeResolver, subscribeFieldResolver, } = args;
132
+ const { schema, document, rootValue, contextValue, variableValues: rawVariableValues, operationName, fieldResolver, typeResolver, subscribeFieldResolver, signal, } = args;
133
133
  // If the schema used for execution is invalid, throw an error.
134
134
  assertValidSchema(schema);
135
135
  const fragments = getFragmentsFromDocument(document);
@@ -139,7 +139,9 @@ export function buildExecutionContext(args) {
139
139
  case Kind.OPERATION_DEFINITION:
140
140
  if (operationName == null) {
141
141
  if (operation !== undefined) {
142
- return [createGraphQLError('Must provide operation name if query contains multiple operations.')];
142
+ return [
143
+ createGraphQLError('Must provide operation name if query contains multiple operations.'),
144
+ ];
143
145
  }
144
146
  operation = definition;
145
147
  }
@@ -178,6 +180,7 @@ export function buildExecutionContext(args) {
178
180
  subscribeFieldResolver: subscribeFieldResolver ?? defaultFieldResolver,
179
181
  subsequentPayloads: new Set(),
180
182
  errors: [],
183
+ signal,
181
184
  };
182
185
  }
183
186
  function buildPerEventExecutionContext(exeContext, payload) {
@@ -219,13 +222,26 @@ function executeOperation(exeContext) {
219
222
  * for fields that must be executed serially.
220
223
  */
221
224
  function executeFieldsSerially(exeContext, parentType, sourceValue, path, fields) {
225
+ let abortErrorThrown = false;
222
226
  return promiseReduce(fields, (results, [responseName, fieldNodes]) => {
223
227
  const fieldPath = addPath(path, responseName, parentType.name);
228
+ if (exeContext.signal?.aborted) {
229
+ results[responseName] = null;
230
+ return results;
231
+ }
224
232
  return new ValueOrPromise(() => executeField(exeContext, parentType, sourceValue, fieldNodes, fieldPath)).then(result => {
225
233
  if (result === undefined) {
226
234
  return results;
227
235
  }
228
236
  results[responseName] = result;
237
+ if (exeContext.signal?.aborted && !abortErrorThrown) {
238
+ exeContext.errors.push(createGraphQLError('Execution aborted', {
239
+ nodes: fieldNodes,
240
+ path: pathToArray(fieldPath),
241
+ originalError: exeContext.signal?.reason,
242
+ }));
243
+ abortErrorThrown = true;
244
+ }
229
245
  return results;
230
246
  });
231
247
  }, Object.create(null)).resolve();
@@ -237,8 +253,13 @@ function executeFieldsSerially(exeContext, parentType, sourceValue, path, fields
237
253
  function executeFields(exeContext, parentType, sourceValue, path, fields, asyncPayloadRecord) {
238
254
  const results = Object.create(null);
239
255
  let containsPromise = false;
256
+ let abortErrorThrown = false;
240
257
  try {
241
258
  for (const [responseName, fieldNodes] of fields) {
259
+ if (exeContext.signal?.aborted) {
260
+ results[responseName] = null;
261
+ continue;
262
+ }
242
263
  const fieldPath = addPath(path, responseName, parentType.name);
243
264
  const result = executeField(exeContext, parentType, sourceValue, fieldNodes, fieldPath, asyncPayloadRecord);
244
265
  if (result !== undefined) {
@@ -247,12 +268,20 @@ function executeFields(exeContext, parentType, sourceValue, path, fields, asyncP
247
268
  containsPromise = true;
248
269
  }
249
270
  }
271
+ if (exeContext.signal?.aborted && !abortErrorThrown) {
272
+ exeContext.errors.push(createGraphQLError('Execution aborted', {
273
+ nodes: fieldNodes,
274
+ path: pathToArray(fieldPath),
275
+ originalError: exeContext.signal?.reason,
276
+ }));
277
+ abortErrorThrown = true;
278
+ }
250
279
  }
251
280
  }
252
281
  catch (error) {
253
282
  if (containsPromise) {
254
283
  // Ensure that any promises returned by other fields are handled, as they may also reject.
255
- return promiseForObject(results).finally(() => {
284
+ return promiseForObject(results, exeContext.signal).finally(() => {
256
285
  throw error;
257
286
  });
258
287
  }
@@ -265,7 +294,7 @@ function executeFields(exeContext, parentType, sourceValue, path, fields, asyncP
265
294
  // Otherwise, results is a map from field name to the result of resolving that
266
295
  // field, which is possibly a promise. Return a promise that will return this
267
296
  // same map, but with any promises replaced with the values they resolved to.
268
- return promiseForObject(results);
297
+ return promiseForObject(results, exeContext.signal);
269
298
  }
270
299
  /**
271
300
  * Implements the "Executing fields" section of the spec
@@ -442,6 +471,14 @@ function getStreamValues(exeContext, fieldNodes, path) {
442
471
  * recursively until all the results are completed.
443
472
  */
444
473
  async function completeAsyncIteratorValue(exeContext, itemType, fieldNodes, info, path, iterator, asyncPayloadRecord) {
474
+ exeContext.signal?.addEventListener('abort', () => {
475
+ iterator.return?.();
476
+ exeContext.errors.push(createGraphQLError('Execution aborted', {
477
+ nodes: fieldNodes,
478
+ path: pathToArray(path),
479
+ originalError: exeContext.signal?.reason,
480
+ }));
481
+ });
445
482
  const errors = asyncPayloadRecord?.errors ?? exeContext.errors;
446
483
  const stream = getStreamValues(exeContext, fieldNodes, path);
447
484
  let containsPromise = false;
@@ -761,20 +798,24 @@ export function subscribe(args) {
761
798
  }
762
799
  return mapSourceToResponse(exeContext, resultOrStream);
763
800
  }
764
- export function flattenIncrementalResults(incrementalResults) {
801
+ export function flattenIncrementalResults(incrementalResults, signal) {
765
802
  const subsequentIterator = incrementalResults.subsequentResults;
766
803
  let initialResultSent = false;
767
804
  let done = false;
805
+ signal?.addEventListener('abort', () => {
806
+ done = true;
807
+ subsequentIterator.throw?.(signal?.reason);
808
+ });
768
809
  return {
769
810
  [Symbol.asyncIterator]() {
770
811
  return this;
771
812
  },
772
- async next() {
813
+ next() {
773
814
  if (done) {
774
- return {
815
+ return Promise.resolve({
775
816
  value: undefined,
776
817
  done,
777
- };
818
+ });
778
819
  }
779
820
  if (initialResultSent) {
780
821
  return subsequentIterator.next();
@@ -795,9 +836,9 @@ export function flattenIncrementalResults(incrementalResults) {
795
836
  },
796
837
  };
797
838
  }
798
- async function* ensureAsyncIterable(someExecutionResult) {
839
+ async function* ensureAsyncIterable(someExecutionResult, signal) {
799
840
  if ('initialResult' in someExecutionResult) {
800
- yield* flattenIncrementalResults(someExecutionResult);
841
+ yield* flattenIncrementalResults(someExecutionResult, signal);
801
842
  }
802
843
  else {
803
844
  yield someExecutionResult;
@@ -813,7 +854,7 @@ function mapSourceToResponse(exeContext, resultOrStream) {
813
854
  // the GraphQL specification. The `execute` function provides the
814
855
  // "ExecuteSubscriptionEvent" algorithm, as it is nearly identical to the
815
856
  // "ExecuteQuery" algorithm, for which `execute` is also used.
816
- return flattenAsyncIterable(mapAsyncIterator(resultOrStream[Symbol.asyncIterator](), async (payload) => ensureAsyncIterable(await executeImpl(buildPerEventExecutionContext(exeContext, payload))), async function* (error) {
857
+ return flattenAsyncIterable(mapAsyncIterator(resultOrStream[Symbol.asyncIterator](), async (payload) => ensureAsyncIterable(await executeImpl(buildPerEventExecutionContext(exeContext, payload)), exeContext.signal), async function* (error) {
817
858
  const wrappedError = createGraphQLError(error.message, {
818
859
  originalError: error,
819
860
  nodes: [exeContext.operation],
@@ -839,14 +880,18 @@ function executeSubscription(exeContext) {
839
880
  const { schema, fragments, operation, variableValues, rootValue } = exeContext;
840
881
  const rootType = schema.getSubscriptionType();
841
882
  if (rootType == null) {
842
- throw createGraphQLError('Schema is not configured to execute subscription operation.', { nodes: operation });
883
+ throw createGraphQLError('Schema is not configured to execute subscription operation.', {
884
+ nodes: operation,
885
+ });
843
886
  }
844
887
  const { fields: rootFields } = collectFields(schema, fragments, variableValues, rootType, operation.selectionSet);
845
888
  const [responseName, fieldNodes] = [...rootFields.entries()][0];
846
889
  const fieldName = fieldNodes[0].name.value;
847
890
  const fieldDef = getFieldDef(schema, rootType, fieldNodes[0]);
848
891
  if (!fieldDef) {
849
- throw createGraphQLError(`The subscription field "${fieldName}" is not defined.`, { nodes: fieldNodes });
892
+ throw createGraphQLError(`The subscription field "${fieldName}" is not defined.`, {
893
+ nodes: fieldNodes,
894
+ });
850
895
  }
851
896
  const path = addPath(undefined, responseName, rootType.name);
852
897
  const info = buildResolveInfo(exeContext, fieldDef, fieldNodes, rootType, path);
@@ -869,13 +914,13 @@ function executeSubscription(exeContext) {
869
914
  throw locatedError(error, fieldNodes, pathToArray(path));
870
915
  });
871
916
  }
872
- return assertEventStream(result);
917
+ return assertEventStream(result, exeContext.signal);
873
918
  }
874
919
  catch (error) {
875
920
  throw locatedError(error, fieldNodes, pathToArray(path));
876
921
  }
877
922
  }
878
- function assertEventStream(result) {
923
+ function assertEventStream(result, signal) {
879
924
  if (result instanceof Error) {
880
925
  throw result;
881
926
  }
@@ -883,7 +928,15 @@ function assertEventStream(result) {
883
928
  if (!isAsyncIterable(result)) {
884
929
  throw createGraphQLError('Subscription field must return Async Iterable. ' + `Received: ${inspect(result)}.`);
885
930
  }
886
- return result;
931
+ return {
932
+ [Symbol.asyncIterator]() {
933
+ const asyncIterator = result[Symbol.asyncIterator]();
934
+ signal?.addEventListener('abort', () => {
935
+ asyncIterator.return?.();
936
+ });
937
+ return asyncIterator;
938
+ },
939
+ };
887
940
  }
888
941
  function executeDeferredFragment(exeContext, parentType, sourceValue, fields, label, path, parentContext) {
889
942
  const asyncPayloadRecord = new DeferredFragmentRecord({
@@ -1,6 +1,6 @@
1
1
  import { getOperationAST } from 'graphql';
2
- import { execute, flattenIncrementalResults, subscribe } from './execute.js';
3
2
  import { ValueOrPromise } from 'value-or-promise';
3
+ import { execute, flattenIncrementalResults, subscribe } from './execute.js';
4
4
  export function normalizedExecutor(args) {
5
5
  const operationAST = getOperationAST(args.document, args.operationName);
6
6
  if (operationAST == null) {
@@ -12,7 +12,7 @@ export function normalizedExecutor(args) {
12
12
  return new ValueOrPromise(() => execute(args))
13
13
  .then((result) => {
14
14
  if ('initialResult' in result) {
15
- return flattenIncrementalResults(result);
15
+ return flattenIncrementalResults(result, args.signal);
16
16
  }
17
17
  return result;
18
18
  })
@@ -5,10 +5,15 @@
5
5
  * This is akin to bluebird's `Promise.props`, but implemented only using
6
6
  * `Promise.all` so it will work with any implementation of ES6 promises.
7
7
  */
8
- export async function promiseForObject(object) {
8
+ export async function promiseForObject(object, signal) {
9
9
  const resolvedObject = Object.create(null);
10
- await Promise.all(Object.entries(object).map(async ([key, value]) => {
11
- resolvedObject[key] = await value;
12
- }));
10
+ await new Promise((resolve, reject) => {
11
+ signal?.addEventListener('abort', () => {
12
+ resolve();
13
+ });
14
+ Promise.all(Object.entries(object).map(async ([key, value]) => {
15
+ resolvedObject[key] = await value;
16
+ })).then(() => resolve(), reject);
17
+ });
13
18
  return resolvedObject;
14
19
  }
@@ -1,4 +1,4 @@
1
- import { print, isInputType, isNonNullType, coerceInputValue, typeFromAST, valueFromAST, } from 'graphql';
1
+ import { coerceInputValue, isInputType, isNonNullType, print, typeFromAST, valueFromAST, } from 'graphql';
2
2
  import { createGraphQLError, hasOwnProperty, inspect, printPathArray } from '@graphql-tools/utils';
3
3
  /**
4
4
  * Prepares an object map of variableValues of the correct type based on the
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@graphql-tools/executor",
3
- "version": "1.1.0",
3
+ "version": "1.2.0-alpha-20230809163713-4e6faf66",
4
4
  "sideEffects": false,
5
5
  "peerDependencies": {
6
6
  "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
7
7
  },
8
8
  "dependencies": {
9
- "@repeaterjs/repeater": "^3.0.4",
10
9
  "@graphql-tools/utils": "^10.0.0",
11
10
  "@graphql-typed-document-node/core": "3.2.0",
11
+ "@repeaterjs/repeater": "^3.0.4",
12
12
  "tslib": "^2.4.0",
13
13
  "value-or-promise": "^1.0.12"
14
14
  },
@@ -1,5 +1,5 @@
1
- import { GraphQLFormattedError, FieldNode, FragmentDefinitionNode, OperationDefinitionNode, GraphQLField, GraphQLFieldResolver, GraphQLObjectType, GraphQLResolveInfo, GraphQLTypeResolver, GraphQLSchema, DocumentNode, GraphQLError } from 'graphql';
2
- import { Path, Maybe, MaybePromise } from '@graphql-tools/utils';
1
+ import { DocumentNode, FieldNode, FragmentDefinitionNode, GraphQLError, GraphQLField, GraphQLFieldResolver, GraphQLFormattedError, GraphQLObjectType, GraphQLResolveInfo, GraphQLSchema, GraphQLTypeResolver, OperationDefinitionNode } from 'graphql';
2
+ import { Maybe, MaybePromise, Path } from '@graphql-tools/utils';
3
3
  import { TypedDocumentNode } from '@graphql-typed-document-node/core';
4
4
  export interface SingularExecutionResult<TData = any, TExtensions = any> {
5
5
  errors?: ReadonlyArray<GraphQLError>;
@@ -43,6 +43,7 @@ export interface ExecutionContext<TVariables = any, TContext = any> {
43
43
  subscribeFieldResolver: GraphQLFieldResolver<any, TContext>;
44
44
  errors: Array<GraphQLError>;
45
45
  subsequentPayloads: Set<AsyncPayloadRecord>;
46
+ signal?: AbortSignal;
46
47
  }
47
48
  export interface FormattedExecutionResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> {
48
49
  errors?: ReadonlyArray<GraphQLFormattedError>;
@@ -107,6 +108,7 @@ export interface ExecutionArgs<TData = any, TVariables = any, TContext = any> {
107
108
  fieldResolver?: Maybe<GraphQLFieldResolver<any, TContext>>;
108
109
  typeResolver?: Maybe<GraphQLTypeResolver<any, TContext>>;
109
110
  subscribeFieldResolver?: Maybe<GraphQLFieldResolver<any, TContext>>;
111
+ signal?: AbortSignal;
110
112
  }
111
113
  /**
112
114
  * Implements the "Executing requests" section of the GraphQL specification,
@@ -202,7 +204,7 @@ export declare const defaultFieldResolver: GraphQLFieldResolver<unknown, unknown
202
204
  * Accepts an object with named arguments.
203
205
  */
204
206
  export declare function subscribe<TData = any, TVariables = any, TContext = any>(args: ExecutionArgs<TData, TVariables, TContext>): MaybePromise<AsyncGenerator<SingularExecutionResult<TData> | InitialIncrementalExecutionResult<TData> | SubsequentIncrementalExecutionResult<TData>, void, void> | SingularExecutionResult<TData>>;
205
- export declare function flattenIncrementalResults<TData>(incrementalResults: IncrementalExecutionResults<TData>): AsyncGenerator<SubsequentIncrementalExecutionResult<TData, Record<string, unknown>>, void, void>;
207
+ export declare function flattenIncrementalResults<TData>(incrementalResults: IncrementalExecutionResults<TData>, signal?: AbortSignal): AsyncGenerator<SubsequentIncrementalExecutionResult<TData, Record<string, unknown>>, void, void>;
206
208
  declare class DeferredFragmentRecord {
207
209
  type: 'defer';
208
210
  errors: Array<GraphQLError>;
@@ -1,5 +1,5 @@
1
- import { GraphQLFormattedError, FieldNode, FragmentDefinitionNode, OperationDefinitionNode, GraphQLField, GraphQLFieldResolver, GraphQLObjectType, GraphQLResolveInfo, GraphQLTypeResolver, GraphQLSchema, DocumentNode, GraphQLError } from 'graphql';
2
- import { Path, Maybe, MaybePromise } from '@graphql-tools/utils';
1
+ import { DocumentNode, FieldNode, FragmentDefinitionNode, GraphQLError, GraphQLField, GraphQLFieldResolver, GraphQLFormattedError, GraphQLObjectType, GraphQLResolveInfo, GraphQLSchema, GraphQLTypeResolver, OperationDefinitionNode } from 'graphql';
2
+ import { Maybe, MaybePromise, Path } from '@graphql-tools/utils';
3
3
  import { TypedDocumentNode } from '@graphql-typed-document-node/core';
4
4
  export interface SingularExecutionResult<TData = any, TExtensions = any> {
5
5
  errors?: ReadonlyArray<GraphQLError>;
@@ -43,6 +43,7 @@ export interface ExecutionContext<TVariables = any, TContext = any> {
43
43
  subscribeFieldResolver: GraphQLFieldResolver<any, TContext>;
44
44
  errors: Array<GraphQLError>;
45
45
  subsequentPayloads: Set<AsyncPayloadRecord>;
46
+ signal?: AbortSignal;
46
47
  }
47
48
  export interface FormattedExecutionResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> {
48
49
  errors?: ReadonlyArray<GraphQLFormattedError>;
@@ -107,6 +108,7 @@ export interface ExecutionArgs<TData = any, TVariables = any, TContext = any> {
107
108
  fieldResolver?: Maybe<GraphQLFieldResolver<any, TContext>>;
108
109
  typeResolver?: Maybe<GraphQLTypeResolver<any, TContext>>;
109
110
  subscribeFieldResolver?: Maybe<GraphQLFieldResolver<any, TContext>>;
111
+ signal?: AbortSignal;
110
112
  }
111
113
  /**
112
114
  * Implements the "Executing requests" section of the GraphQL specification,
@@ -202,7 +204,7 @@ export declare const defaultFieldResolver: GraphQLFieldResolver<unknown, unknown
202
204
  * Accepts an object with named arguments.
203
205
  */
204
206
  export declare function subscribe<TData = any, TVariables = any, TContext = any>(args: ExecutionArgs<TData, TVariables, TContext>): MaybePromise<AsyncGenerator<SingularExecutionResult<TData> | InitialIncrementalExecutionResult<TData> | SubsequentIncrementalExecutionResult<TData>, void, void> | SingularExecutionResult<TData>>;
205
- export declare function flattenIncrementalResults<TData>(incrementalResults: IncrementalExecutionResults<TData>): AsyncGenerator<SubsequentIncrementalExecutionResult<TData, Record<string, unknown>>, void, void>;
207
+ export declare function flattenIncrementalResults<TData>(incrementalResults: IncrementalExecutionResults<TData>, signal?: AbortSignal): AsyncGenerator<SubsequentIncrementalExecutionResult<TData, Record<string, unknown>>, void, void>;
206
208
  declare class DeferredFragmentRecord {
207
209
  type: 'defer';
208
210
  errors: Array<GraphQLError>;
@@ -1,3 +1,3 @@
1
- import { MaybeAsyncIterable, ExecutionResult, MaybePromise } from '@graphql-tools/utils';
1
+ import { ExecutionResult, MaybeAsyncIterable, MaybePromise } from '@graphql-tools/utils';
2
2
  import { ExecutionArgs } from './execute.cjs';
3
3
  export declare function normalizedExecutor<TData = any, TVariables = any, TContext = any>(args: ExecutionArgs<TData, TVariables, TContext>): MaybePromise<MaybeAsyncIterable<ExecutionResult<TData>>>;
@@ -1,3 +1,3 @@
1
- import { MaybeAsyncIterable, ExecutionResult, MaybePromise } from '@graphql-tools/utils';
1
+ import { ExecutionResult, MaybeAsyncIterable, MaybePromise } from '@graphql-tools/utils';
2
2
  import { ExecutionArgs } from './execute.js';
3
3
  export declare function normalizedExecutor<TData = any, TVariables = any, TContext = any>(args: ExecutionArgs<TData, TVariables, TContext>): MaybePromise<MaybeAsyncIterable<ExecutionResult<TData>>>;
@@ -8,5 +8,5 @@ type ResolvedObject<TData> = {
8
8
  * This is akin to bluebird's `Promise.props`, but implemented only using
9
9
  * `Promise.all` so it will work with any implementation of ES6 promises.
10
10
  */
11
- export declare function promiseForObject<TData>(object: TData): Promise<ResolvedObject<TData>>;
11
+ export declare function promiseForObject<TData>(object: TData, signal?: AbortSignal): Promise<ResolvedObject<TData>>;
12
12
  export {};
@@ -8,5 +8,5 @@ type ResolvedObject<TData> = {
8
8
  * This is akin to bluebird's `Promise.props`, but implemented only using
9
9
  * `Promise.all` so it will work with any implementation of ES6 promises.
10
10
  */
11
- export declare function promiseForObject<TData>(object: TData): Promise<ResolvedObject<TData>>;
11
+ export declare function promiseForObject<TData>(object: TData, signal?: AbortSignal): Promise<ResolvedObject<TData>>;
12
12
  export {};
@@ -1,4 +1,4 @@
1
- import { GraphQLError, VariableDefinitionNode, GraphQLSchema } from 'graphql';
1
+ import { GraphQLError, GraphQLSchema, VariableDefinitionNode } from 'graphql';
2
2
  type CoercedVariableValues = {
3
3
  errors: ReadonlyArray<GraphQLError>;
4
4
  coerced?: never;
@@ -1,4 +1,4 @@
1
- import { GraphQLError, VariableDefinitionNode, GraphQLSchema } from 'graphql';
1
+ import { GraphQLError, GraphQLSchema, VariableDefinitionNode } from 'graphql';
2
2
  type CoercedVariableValues = {
3
3
  errors: ReadonlyArray<GraphQLError>;
4
4
  coerced?: never;