Skip to content

Commit bc532a5

Browse files
committed
fix last edge case
1 parent 0857b7e commit bc532a5

File tree

3 files changed

+159
-100
lines changed

3 files changed

+159
-100
lines changed

src/transform/buildTransformationContext.ts

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { invariant } from '../jsutils/invariant.js';
22
import { mapValue } from '../jsutils/mapValue.js';
33
import type { ObjMap } from '../jsutils/ObjMap.js';
4+
import type { Path } from '../jsutils/Path.js';
45

56
import type {
67
ArgumentNode,
@@ -14,12 +15,15 @@ import {
1415
GraphQLDeferDirective,
1516
GraphQLStreamDirective,
1617
} from '../type/directives.js';
18+
import type { GraphQLOutputType } from '../type/index.js';
1719
import { TypeNameMetaFieldDef } from '../type/introspection.js';
1820

1921
import { collectSubfields as _collectSubfields } from '../execution/collectFields.js';
2022
import type { ValidatedExecutionArgs } from '../execution/execute.js';
2123
import type { PendingResult } from '../execution/types.js';
2224

25+
import type { FieldDetails } from './collectFields.js';
26+
2327
type SelectionSetNodeOrFragmentName =
2428
| { node: SelectionSetNode; fragmentName?: never }
2529
| { node?: never; fragmentName: string };
@@ -29,9 +33,16 @@ interface DeferUsageContext {
2933
selectionSet: SelectionSetNodeOrFragmentName;
3034
}
3135

36+
export interface Stream {
37+
path: Path;
38+
itemType: GraphQLOutputType;
39+
fieldDetailsList: ReadonlyArray<FieldDetails>;
40+
}
41+
3242
interface StreamUsageContext {
3343
originalLabel: string | undefined;
34-
selectionSet: SelectionSetNode | undefined;
44+
streams: Set<Stream>;
45+
nextIndex: number;
3546
}
3647

3748
export interface TransformationContext {
@@ -145,26 +156,18 @@ function transformSelection(
145156
if (selection.kind === Kind.FIELD) {
146157
const selectionSet = selection.selectionSet;
147158
if (selectionSet) {
148-
const transformedSelectionSet = transformNestedSelectionSet(
149-
context,
150-
selectionSet,
151-
);
152159
return {
153160
...selection,
154-
selectionSet: transformedSelectionSet,
161+
selectionSet: transformNestedSelectionSet(context, selectionSet),
155162
directives: selection.directives?.map((directive) =>
156-
transformMaybeStreamDirective(
157-
context,
158-
directive,
159-
transformedSelectionSet,
160-
),
163+
transformMaybeStreamDirective(context, directive),
161164
),
162165
};
163166
}
164167
return {
165168
...selection,
166169
directives: selection.directives?.map((directive) =>
167-
transformMaybeStreamDirective(context, directive, undefined),
170+
transformMaybeStreamDirective(context, directive),
168171
),
169172
};
170173
} else if (selection.kind === Kind.INLINE_FRAGMENT) {
@@ -263,7 +266,6 @@ function transformMaybeDeferDirective(
263266
function transformMaybeStreamDirective(
264267
context: RequestTransformationContext,
265268
directive: DirectiveNode,
266-
selectionSet: SelectionSetNode | undefined,
267269
): DirectiveNode {
268270
const name = directive.name.value;
269271

@@ -286,7 +288,8 @@ function transformMaybeStreamDirective(
286288
const prefixedLabel = `${context.prefix}stream${context.incrementalCounter++}__${originalLabel}`;
287289
context.streamUsageMap.set(prefixedLabel, {
288290
originalLabel,
289-
selectionSet,
291+
streams: new Set(),
292+
nextIndex: 0,
290293
});
291294
newArgs.push({
292295
...arg,
@@ -305,7 +308,8 @@ function transformMaybeStreamDirective(
305308
const newLabel = `${context.prefix}stream${context.incrementalCounter++}`;
306309
context.streamUsageMap.set(newLabel, {
307310
originalLabel: undefined,
308-
selectionSet,
311+
streams: new Set(),
312+
nextIndex: 0,
309313
});
310314
newArgs.push({
311315
kind: Kind.ARGUMENT,

src/transform/completeValue.ts

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import { invariant } from '../jsutils/invariant.js';
22
import { isObjectLike } from '../jsutils/isObjectLike.js';
33
import type { ObjMap } from '../jsutils/ObjMap.js';
44
import type { Path } from '../jsutils/Path.js';
5-
import { addPath } from '../jsutils/Path.js';
5+
import { addPath, pathToArray } from '../jsutils/Path.js';
66

77
import type { GraphQLError } from '../error/GraphQLError.js';
88

9+
import { Kind } from '../language/kinds.js';
10+
911
import type {
1012
GraphQLObjectType,
1113
GraphQLOutputType,
@@ -16,17 +18,18 @@ import {
1618
isNonNullType,
1719
isObjectType,
1820
} from '../type/definition.js';
21+
import { GraphQLStreamDirective } from '../type/directives.js';
1922

2023
import type { TransformationContext } from './buildTransformationContext.js';
21-
import type { FieldDetailsList, GroupedFieldSet } from './collectFields.js';
24+
import type { FieldDetails, GroupedFieldSet } from './collectFields.js';
2225
import { collectSubfields as _collectSubfields } from './collectFields.js';
2326
import { memoize3of4 } from './memoize3of4.js';
2427

2528
const collectSubfields = memoize3of4(
2629
(
2730
context: TransformationContext,
2831
returnType: GraphQLObjectType,
29-
fieldDetailsList: FieldDetailsList,
32+
fieldDetailsList: ReadonlyArray<FieldDetails>,
3033
path: Path | undefined,
3134
) => _collectSubfields(context, returnType, fieldDetailsList, path),
3235
);
@@ -67,13 +70,14 @@ export function completeValue(
6770
}
6871

6972
// eslint-disable-next-line @typescript-eslint/max-params
70-
function completeSubValue(
73+
export function completeSubValue(
7174
context: TransformationContext,
7275
errors: Array<GraphQLError>,
7376
returnType: GraphQLOutputType,
74-
fieldDetailsList: FieldDetailsList,
77+
fieldDetailsList: ReadonlyArray<FieldDetails>,
7578
result: unknown,
7679
path: Path,
80+
listDepth = 0,
7781
): unknown {
7882
if (isNonNullType(returnType)) {
7983
return completeSubValue(
@@ -110,6 +114,7 @@ function completeSubValue(
110114
fieldDetailsList,
111115
result,
112116
path,
117+
listDepth,
113118
);
114119
}
115120

@@ -120,7 +125,7 @@ function completeSubValue(
120125
function completeObjectType(
121126
context: TransformationContext,
122127
errors: Array<GraphQLError>,
123-
fieldDetailsList: FieldDetailsList,
128+
fieldDetailsList: ReadonlyArray<FieldDetails>,
124129
result: ObjMap<unknown>,
125130
path: Path,
126131
): ObjMap<unknown> {
@@ -172,22 +177,87 @@ function completeObjectType(
172177
function completeListValue(
173178
context: TransformationContext,
174179
errors: Array<GraphQLError>,
175-
returnType: GraphQLOutputType,
176-
fieldDetailsList: FieldDetailsList,
180+
itemType: GraphQLOutputType,
181+
fieldDetailsList: ReadonlyArray<FieldDetails>,
177182
result: Array<unknown>,
178183
path: Path,
184+
listDepth: number,
179185
): Array<unknown> {
180186
const completedItems = [];
187+
181188
for (let index = 0; index < result.length; index++) {
182189
const completed = completeSubValue(
183190
context,
184191
errors,
185-
returnType,
192+
itemType,
186193
fieldDetailsList,
187194
result[index],
188195
addPath(path, index, undefined),
196+
listDepth + 1,
189197
);
190198
completedItems.push(completed);
191199
}
200+
201+
maybeAddStream(
202+
context,
203+
itemType,
204+
fieldDetailsList,
205+
listDepth,
206+
path,
207+
result.length,
208+
);
209+
192210
return completedItems;
193211
}
212+
213+
// eslint-disable-next-line @typescript-eslint/max-params
214+
function maybeAddStream(
215+
context: TransformationContext,
216+
itemType: GraphQLOutputType,
217+
fieldDetailsList: ReadonlyArray<FieldDetails>,
218+
listDepth: number,
219+
path: Path,
220+
nextIndex: number,
221+
): void {
222+
if (listDepth > 0) {
223+
return;
224+
}
225+
226+
let stream;
227+
for (const fieldDetails of fieldDetailsList) {
228+
const directives = fieldDetails.node.directives;
229+
if (!directives) {
230+
continue;
231+
}
232+
stream = directives.find(
233+
(directive) => directive.name.value === GraphQLStreamDirective.name,
234+
);
235+
if (stream != null) {
236+
break;
237+
}
238+
}
239+
240+
if (stream == null) {
241+
return;
242+
}
243+
244+
const labelArg = stream.arguments?.find((arg) => arg.name.value === 'label');
245+
invariant(labelArg != null);
246+
const labelValue = labelArg.value;
247+
invariant(labelValue.kind === Kind.STRING);
248+
const label = labelValue.value;
249+
invariant(label != null);
250+
const pendingLabels = context.pendingLabelsByPath.get(
251+
pathToArray(path).join('.'),
252+
);
253+
if (pendingLabels?.has(label)) {
254+
const streamUsage = context.streamUsageMap.get(label);
255+
invariant(streamUsage != null);
256+
streamUsage.nextIndex = nextIndex;
257+
streamUsage.streams.add({
258+
path,
259+
itemType,
260+
fieldDetailsList,
261+
});
262+
}
263+
}

0 commit comments

Comments
 (0)