From 8e4af17d11cc93fca88f709bc9bdeb1bf4f571be Mon Sep 17 00:00:00 2001 From: Yaacov Rydzinski Date: Thu, 30 Apr 2026 22:25:53 +0300 Subject: [PATCH] refactor: add executionMode/serially argument to executeRootSelectionSet Motivation: more closely matches spec. allows executeSubscriptionEvent to set without re-checking operation drops comment/handling regarding subscribe/execute integration which may never happen. For now, the argument is only within the Executor, so that non-spec compliant combinations of operations and execution mode are disallowed. --- src/execution/Executor.ts | 35 ++++++------------- .../ExecutorThrowingOnIncremental.ts | 4 +-- src/execution/execute.ts | 4 ++- .../incremental/IncrementalExecutor.ts | 6 ++-- 4 files changed, 19 insertions(+), 30 deletions(-) diff --git a/src/execution/Executor.ts b/src/execution/Executor.ts index c2312167d2..4d7145d050 100644 --- a/src/execution/Executor.ts +++ b/src/execution/Executor.ts @@ -272,9 +272,9 @@ export class Executor< this.promiseAll = promiseAll; } - executeRootSelectionSet(): PromiseOrValue< - ExecutionResult | TAlternativeInitialResponse - > { + executeRootSelectionSet( + serially?: boolean, + ): PromiseOrValue { const externalAbortSignal = this.validatedExecutionArgs.externalAbortSignal; let removeExternalAbortListener: (() => void) | undefined; if (externalAbortSignal) { @@ -322,10 +322,10 @@ export class Executor< ); result = this.executeCollectedRootFields( - operation.operation, rootType, rootValue, groupedFieldSet, + serially ?? operationType === OperationTypeNode.MUTATION, newDeferUsages, ); @@ -420,56 +420,43 @@ export class Executor< } executeCollectedRootFields( - operation: OperationTypeNode, rootType: GraphQLObjectType, rootValue: unknown, originalGroupedFieldSet: GroupedFieldSet, + serially: boolean, _newDeferUsages: ReadonlyArray, ): PromiseOrValue> { return this.executeRootGroupedFieldSet( - operation, rootType, rootValue, originalGroupedFieldSet, + serially, undefined, ); } executeRootGroupedFieldSet( - operation: OperationTypeNode, rootType: GraphQLObjectType, rootValue: unknown, groupedFieldSet: GroupedFieldSet, + serially: boolean, positionContext?: TPositionContext, ): PromiseOrValue> { - switch (operation) { - case OperationTypeNode.QUERY: - return this.executeFields( + return serially + ? this.executeFieldsSerially( rootType, rootValue, undefined, groupedFieldSet, positionContext, - ); - case OperationTypeNode.MUTATION: - return this.executeFieldsSerially( - rootType, - rootValue, - undefined, - groupedFieldSet, - positionContext, - ); - case OperationTypeNode.SUBSCRIPTION: - // TODO: deprecate `subscribe` and move all logic here - // Temporary solution until we finish merging execute and subscribe together - return this.executeFields( + ) + : this.executeFields( rootType, rootValue, undefined, groupedFieldSet, positionContext, ); - } } /** diff --git a/src/execution/ExecutorThrowingOnIncremental.ts b/src/execution/ExecutorThrowingOnIncremental.ts index 4983e61c97..990f0e68f3 100644 --- a/src/execution/ExecutorThrowingOnIncremental.ts +++ b/src/execution/ExecutorThrowingOnIncremental.ts @@ -25,10 +25,10 @@ const UNEXPECTED_MULTIPLE_PAYLOADS = /** @internal */ export class ExecutorThrowingOnIncremental extends Executor { override executeCollectedRootFields( - operation: OperationTypeNode, rootType: GraphQLObjectType, rootValue: unknown, originalGroupedFieldSet: GroupedFieldSet, + serially: boolean, newDeferUsages: ReadonlyArray, ): PromiseOrValue> { if (newDeferUsages.length > 0) { @@ -42,10 +42,10 @@ export class ExecutorThrowingOnIncremental extends Executor { throw reason; } return this.executeRootGroupedFieldSet( - operation, rootType, rootValue, originalGroupedFieldSet, + serially, undefined, ); } diff --git a/src/execution/execute.ts b/src/execution/execute.ts index c82e2e0bc2..988142cb3d 100644 --- a/src/execution/execute.ts +++ b/src/execution/execute.ts @@ -183,7 +183,9 @@ export function executeSync(args: ExecutionArgs): ExecutionResult { export function executeSubscriptionEvent( validatedExecutionArgs: ValidatedSubscriptionArgs, ): PromiseOrValue { - return executeRootSelectionSet(validatedExecutionArgs); + return new ExecutorThrowingOnIncremental( + validatedExecutionArgs, + ).executeRootSelectionSet(false); } /** diff --git a/src/execution/incremental/IncrementalExecutor.ts b/src/execution/incremental/IncrementalExecutor.ts index 00b4264624..0622818f82 100644 --- a/src/execution/incremental/IncrementalExecutor.ts +++ b/src/execution/incremental/IncrementalExecutor.ts @@ -362,18 +362,18 @@ export class IncrementalExecutor< } override executeCollectedRootFields( - operation: OperationTypeNode, rootType: GraphQLObjectType, rootValue: unknown, originalGroupedFieldSet: GroupedFieldSet, + serially: boolean, newDeferUsages: ReadonlyArray, ): PromiseOrValue> { if (newDeferUsages.length === 0) { return this.executeRootGroupedFieldSet( - operation, rootType, rootValue, originalGroupedFieldSet, + serially, undefined, ); } @@ -391,10 +391,10 @@ export class IncrementalExecutor< this.buildRootExecutionPlan(originalGroupedFieldSet); const data = this.executeRootGroupedFieldSet( - operation, rootType, rootValue, groupedFieldSet, + serially, newDeliveryGroupMap, );