@@ -287,6 +287,35 @@ export function execute<TData = any, TVariables = any, TContext = any>(
287287 return executeImpl ( exeContext ) ;
288288}
289289
290+ // AbortSignal handler cache to avoid the "possible EventEmitter memory leak detected"
291+ // on Node.js
292+ const abortSignalHandlers = new WeakMap < AbortSignal , Set < VoidFunction > > ( ) ;
293+
294+ /**
295+ * Register an AbortSignal handler for a signal.
296+ * This helper function mainly exists to work around the
297+ * "possible EventEmitter memory leak detected. 11 listeners added. Use emitter.setMaxListeners() to increase limit."
298+ * warning occuring on Node.js
299+ */
300+ function registerAbortSignalHandler ( signal : AbortSignal , handler : VoidFunction ) : void {
301+ let handlers = abortSignalHandlers . get ( signal ) ;
302+
303+ if ( ! Array . isArray ( handlers ) ) {
304+ handlers = new Set ( ) ;
305+ abortSignalHandlers . set ( signal , handlers ) ;
306+
307+ function abortSignalHandler ( ) {
308+ for ( const handler of handlers ! ) {
309+ handler ( ) ;
310+ }
311+ }
312+
313+ signal . addEventListener ( 'abort' , abortSignalHandler , { once : true } ) ;
314+ }
315+
316+ handlers . add ( handler ) ;
317+ }
318+
290319function executeImpl < TData = any , TVariables = any , TContext = any > (
291320 exeContext : ExecutionContext < TVariables , TContext > ,
292321) : MaybePromise < SingularExecutionResult < TData > | IncrementalExecutionResults < TData > > {
@@ -958,13 +987,12 @@ async function completeAsyncIteratorValue(
958987 iterator : AsyncIterator < unknown > ,
959988 asyncPayloadRecord ?: AsyncPayloadRecord ,
960989) : Promise < ReadonlyArray < unknown > > {
961- exeContext . signal ?. addEventListener (
962- 'abort' ,
963- ( ) => {
990+ if ( exeContext . signal ) {
991+ registerAbortSignalHandler ( exeContext . signal , ( ) => {
964992 iterator . return ?.( ) ;
965- } ,
966- { once : true } ,
967- ) ;
993+ } ) ;
994+ }
995+
968996 const errors = asyncPayloadRecord ?. errors ?? exeContext . errors ;
969997 const stream = getStreamValues ( exeContext , fieldNodes , path ) ;
970998 let containsPromise = false ;
@@ -1761,9 +1789,12 @@ function assertEventStream(result: unknown, signal?: AbortSignal): AsyncIterable
17611789 return {
17621790 [ Symbol . asyncIterator ] ( ) {
17631791 const asyncIterator = result [ Symbol . asyncIterator ] ( ) ;
1764- signal ?. addEventListener ( 'abort' , ( ) => {
1765- asyncIterator . return ?.( ) ;
1766- } ) ;
1792+ if ( signal ) {
1793+ registerAbortSignalHandler ( signal , ( ) => {
1794+ asyncIterator . return ?.( ) ;
1795+ } ) ;
1796+ }
1797+
17671798 return asyncIterator ;
17681799 } ,
17691800 } ;
@@ -2085,14 +2116,12 @@ function yieldSubsequentPayloads(
20852116 let isDone = false ;
20862117
20872118 const abortPromise = new Promise < void > ( ( _ , reject ) => {
2088- exeContext . signal ?. addEventListener (
2089- 'abort' ,
2090- ( ) => {
2119+ if ( exeContext . signal ) {
2120+ registerAbortSignalHandler ( exeContext . signal , ( ) => {
20912121 isDone = true ;
20922122 reject ( exeContext . signal ?. reason ) ;
2093- } ,
2094- { once : true } ,
2095- ) ;
2123+ } ) ;
2124+ }
20962125 } ) ;
20972126
20982127 async function next ( ) : Promise < IteratorResult < SubsequentIncrementalExecutionResult , void > > {
0 commit comments