Skip to content

Commit 526d0ad

Browse files
committed
use undefined for empty
1 parent 75d6844 commit 526d0ad

File tree

2 files changed

+63
-46
lines changed

2 files changed

+63
-46
lines changed

src/execution/IncrementalPublisher.ts

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ export interface FormattedCompletedResult {
174174
export function buildIncrementalResponse(
175175
context: IncrementalPublisherContext,
176176
result: ObjMap<unknown>,
177-
errors: ReadonlyArray<GraphQLError>,
177+
errors: ReadonlyArray<GraphQLError> | undefined,
178178
incrementalDataRecords: ReadonlyArray<IncrementalDataRecord>,
179179
): ExperimentalIncrementalExecutionResults {
180180
const incrementalPublisher = new IncrementalPublisher(context);
@@ -186,7 +186,7 @@ export function buildIncrementalResponse(
186186
}
187187

188188
interface IncrementalPublisherContext {
189-
cancellableStreams: Set<StreamRecord>;
189+
cancellableStreams?: Set<StreamRecord> | undefined;
190190
}
191191

192192
/**
@@ -220,7 +220,7 @@ class IncrementalPublisher {
220220

221221
buildResponse(
222222
data: ObjMap<unknown>,
223-
errors: ReadonlyArray<GraphQLError>,
223+
errors: ReadonlyArray<GraphQLError> | undefined,
224224
incrementalDataRecords: ReadonlyArray<IncrementalDataRecord>,
225225
): ExperimentalIncrementalExecutionResults {
226226
this._addIncrementalDataRecords(incrementalDataRecords);
@@ -229,7 +229,7 @@ class IncrementalPublisher {
229229
const pending = this._pendingSourcesToResults();
230230

231231
const initialResult: InitialIncrementalExecutionResult =
232-
errors.length === 0
232+
errors === undefined
233233
? { data, pending, hasNext: true }
234234
: { errors, data, pending, hasNext: true };
235235

@@ -441,8 +441,12 @@ class IncrementalPublisher {
441441
};
442442

443443
const returnStreamIterators = async (): Promise<void> => {
444+
const cancellableStreams = this._context.cancellableStreams;
445+
if (cancellableStreams === undefined) {
446+
return;
447+
}
444448
const promises: Array<Promise<unknown>> = [];
445-
for (const streamRecord of this._context.cancellableStreams) {
449+
for (const streamRecord of cancellableStreams) {
446450
if (streamRecord.earlyReturn !== undefined) {
447451
promises.push(streamRecord.earlyReturn());
448452
}
@@ -516,7 +520,7 @@ class IncrementalPublisher {
516520
);
517521
}
518522

519-
if (deferredGroupedFieldSetResult.incrementalDataRecords.length > 0) {
523+
if (deferredGroupedFieldSetResult.incrementalDataRecords !== undefined) {
520524
this._addIncrementalDataRecords(
521525
deferredGroupedFieldSetResult.incrementalDataRecords,
522526
);
@@ -586,14 +590,20 @@ class IncrementalPublisher {
586590
if (streamItemsResult.result === undefined) {
587591
this._completed.push({ id });
588592
this._pending.delete(streamRecord);
589-
this._context.cancellableStreams.delete(streamRecord);
593+
const cancellableStreams = this._context.cancellableStreams;
594+
if (cancellableStreams !== undefined) {
595+
cancellableStreams.delete(streamRecord);
596+
}
590597
} else if (streamItemsResult.result === null) {
591598
this._completed.push({
592599
id,
593600
errors: streamItemsResult.errors,
594601
});
595602
this._pending.delete(streamRecord);
596-
this._context.cancellableStreams.delete(streamRecord);
603+
const cancellableStreams = this._context.cancellableStreams;
604+
if (cancellableStreams !== undefined) {
605+
cancellableStreams.delete(streamRecord);
606+
}
597607
streamRecord.earlyReturn?.().catch(() => {
598608
/* c8 ignore next 1 */
599609
// ignore error
@@ -606,7 +616,7 @@ class IncrementalPublisher {
606616

607617
this._incremental.push(incrementalEntry);
608618

609-
if (streamItemsResult.incrementalDataRecords.length > 0) {
619+
if (streamItemsResult.incrementalDataRecords !== undefined) {
610620
this._addIncrementalDataRecords(
611621
streamItemsResult.incrementalDataRecords,
612622
);
@@ -663,7 +673,7 @@ function isDeferredGroupedFieldSetRecord(
663673
export interface IncrementalContext {
664674
deferUsageSet: DeferUsageSet | undefined;
665675
path: Path | undefined;
666-
errors: Array<GraphQLError>;
676+
errors?: Array<GraphQLError> | undefined;
667677
}
668678

669679
export type DeferredGroupedFieldSetResult =
@@ -680,7 +690,7 @@ interface ReconcilableDeferredGroupedFieldSetResult {
680690
deferredFragmentRecords: ReadonlyArray<DeferredFragmentRecord>;
681691
path: Array<string | number>;
682692
result: BareDeferredGroupedFieldSetResult;
683-
incrementalDataRecords: ReadonlyArray<IncrementalDataRecord>;
693+
incrementalDataRecords: ReadonlyArray<IncrementalDataRecord> | undefined;
684694
sent?: true | undefined;
685695
}
686696

@@ -718,7 +728,6 @@ export class DeferredGroupedFieldSetRecord {
718728
const incrementalContext: IncrementalContext = {
719729
deferUsageSet,
720730
path,
721-
errors: [],
722731
};
723732

724733
for (const deferredFragmentRecord of deferredFragmentRecords) {
@@ -786,7 +795,7 @@ interface NonReconcilableStreamItemsResult {
786795
interface NonTerminatingStreamItemsResult {
787796
streamRecord: StreamRecord;
788797
result: BareStreamItemsResult;
789-
incrementalDataRecords: ReadonlyArray<IncrementalDataRecord>;
798+
incrementalDataRecords: ReadonlyArray<IncrementalDataRecord> | undefined;
790799
}
791800

792801
interface TerminatingStreamItemsResult {
@@ -826,7 +835,6 @@ export class StreamItemsRecord {
826835
const incrementalContext: IncrementalContext = {
827836
deferUsageSet: undefined,
828837
path: itemPath,
829-
errors: [],
830838
};
831839

832840
this._result = executor(incrementalContext);
@@ -850,7 +858,7 @@ export class StreamItemsRecord {
850858
? {
851859
...result,
852860
incrementalDataRecords:
853-
result.incrementalDataRecords.length === 0
861+
result.incrementalDataRecords === undefined
854862
? [this.nextStreamItems]
855863
: [this.nextStreamItems, ...result.incrementalDataRecords],
856864
}

src/execution/execute.ts

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,8 @@ export interface ExecutionContext {
140140
fieldResolver: GraphQLFieldResolver<any, any>;
141141
typeResolver: GraphQLTypeResolver<any, any>;
142142
subscribeFieldResolver: GraphQLFieldResolver<any, any>;
143-
errors: Array<GraphQLError>;
144-
cancellableStreams: Set<StreamRecord>;
143+
errors: Array<GraphQLError> | undefined;
144+
cancellableStreams: Set<StreamRecord> | undefined;
145145
}
146146

147147
export interface ExecutionArgs {
@@ -162,7 +162,7 @@ export interface StreamUsage {
162162
fieldGroup: FieldGroup;
163163
}
164164

165-
type GraphQLResult<T> = [T, ReadonlyArray<IncrementalDataRecord>];
165+
type GraphQLResult<T> = [T, ReadonlyArray<IncrementalDataRecord> | undefined];
166166

167167
const UNEXPECTED_EXPERIMENTAL_DIRECTIVES =
168168
'The provided schema unexpectedly contains experimental directives (@defer or @stream). These directives may only be utilized if experimental execution features are explicitly enabled.';
@@ -328,20 +328,20 @@ function executeOperation(
328328
}
329329

330330
function withError(
331-
errors: Array<GraphQLError>,
331+
errors: Array<GraphQLError> | undefined,
332332
error: GraphQLError,
333333
): ReadonlyArray<GraphQLError> {
334-
return errors.length === 0 ? [error] : [...errors, error];
334+
return errors === undefined ? [error] : [...errors, error];
335335
}
336336

337337
function buildDataResponse(
338338
exeContext: ExecutionContext,
339339
data: ObjMap<unknown>,
340-
incrementalDataRecords: ReadonlyArray<IncrementalDataRecord>,
340+
incrementalDataRecords: ReadonlyArray<IncrementalDataRecord> | undefined,
341341
): ExecutionResult | ExperimentalIncrementalExecutionResults {
342342
const { errors } = exeContext;
343-
if (incrementalDataRecords.length === 0) {
344-
return errors.length > 0 ? { errors, data } : { data };
343+
if (incrementalDataRecords === undefined) {
344+
return errors !== undefined ? { errors, data } : { data };
345345
}
346346

347347
return buildIncrementalResponse(
@@ -453,8 +453,8 @@ export function buildExecutionContext(
453453
fieldResolver: fieldResolver ?? defaultFieldResolver,
454454
typeResolver: typeResolver ?? defaultTypeResolver,
455455
subscribeFieldResolver: subscribeFieldResolver ?? defaultFieldResolver,
456-
errors: [],
457-
cancellableStreams: new Set(),
456+
errors: undefined,
457+
cancellableStreams: undefined,
458458
};
459459
}
460460

@@ -465,7 +465,7 @@ function buildPerEventExecutionContext(
465465
return {
466466
...exeContext,
467467
rootValue: payload,
468-
errors: [],
468+
errors: undefined,
469469
};
470470
}
471471

@@ -551,16 +551,16 @@ function executeFieldsSerially(
551551
appendNewIncrementalDataRecords(acc, result[1]);
552552
return acc;
553553
},
554-
[Object.create(null), []] as GraphQLResult<ObjMap<unknown>>,
554+
[Object.create(null), undefined] as GraphQLResult<ObjMap<unknown>>,
555555
);
556556
}
557557

558558
function appendNewIncrementalDataRecords(
559559
acc: GraphQLResult<unknown>,
560-
newRecords: ReadonlyArray<IncrementalDataRecord>,
560+
newRecords: ReadonlyArray<IncrementalDataRecord> | undefined,
561561
): void {
562-
if (newRecords.length > 0) {
563-
acc[1] = acc[1].length === 0 ? newRecords : [...acc[1], ...newRecords];
562+
if (newRecords !== undefined) {
563+
acc[1] = acc[1] === undefined ? newRecords : [...acc[1], ...newRecords];
564564
}
565565
}
566566

@@ -577,7 +577,7 @@ function executeFields(
577577
incrementalContext: IncrementalContext | undefined,
578578
deferMap: ReadonlyMap<DeferUsage, DeferredFragmentRecord> | undefined,
579579
): PromiseOrValue<GraphQLResult<ObjMap<unknown>>> {
580-
const acc: GraphQLResult<ObjMap<unknown>> = [Object.create(null), []];
580+
const acc: GraphQLResult<ObjMap<unknown>> = [Object.create(null), undefined];
581581
const promises: Array<Promise<void>> = [];
582582

583583
try {
@@ -719,7 +719,7 @@ function executeField(
719719
path,
720720
incrementalContext,
721721
);
722-
return [null, []];
722+
return [null, undefined];
723723
});
724724
}
725725
return completed;
@@ -732,7 +732,7 @@ function executeField(
732732
path,
733733
incrementalContext,
734734
);
735-
return [null, []];
735+
return [null, undefined];
736736
}
737737
}
738738

@@ -781,7 +781,13 @@ function handleFieldError(
781781

782782
// Otherwise, error protection is applied, logging the error and resolving
783783
// a null value for this field if one is encountered.
784-
(incrementalContext ?? exeContext).errors.push(error);
784+
const context = incrementalContext ?? exeContext;
785+
let errors = context.errors;
786+
if (errors === undefined) {
787+
errors = [];
788+
context.errors = errors;
789+
}
790+
errors.push(error);
785791
}
786792

787793
/**
@@ -843,7 +849,7 @@ function completeValue(
843849

844850
// If result value is null or undefined then return null.
845851
if (result == null) {
846-
return [null, []];
852+
return [null, undefined];
847853
}
848854

849855
// If field type is List, complete each item in the list with the inner type
@@ -863,7 +869,7 @@ function completeValue(
863869
// If field type is a leaf type, Scalar or Enum, serialize to a valid value,
864870
// returning null if serialization is not possible.
865871
if (isLeafType(returnType)) {
866-
return [completeLeafValue(returnType, result), []];
872+
return [completeLeafValue(returnType, result), undefined];
867873
}
868874

869875
// If field type is an abstract type, Interface or Union, determine the
@@ -938,7 +944,7 @@ async function completePromisedValue(
938944
path,
939945
incrementalContext,
940946
);
941-
return [null, []];
947+
return [null, undefined];
942948
}
943949
}
944950

@@ -1028,7 +1034,7 @@ async function completeAsyncIteratorValue(
10281034
): Promise<GraphQLResult<ReadonlyArray<unknown>>> {
10291035
let containsPromise = false;
10301036
const completedResults: Array<unknown> = [];
1031-
const acc: GraphQLResult<Array<unknown>> = [completedResults, []];
1037+
const acc: GraphQLResult<Array<unknown>> = [completedResults, undefined];
10321038
let index = 0;
10331039
// eslint-disable-next-line no-constant-condition
10341040
while (true) {
@@ -1105,7 +1111,7 @@ async function completeAsyncIteratorValueWithPossibleStream(
11051111
): Promise<GraphQLResult<ReadonlyArray<unknown>>> {
11061112
let containsPromise = false;
11071113
const completedResults: Array<unknown> = [];
1108-
const acc: GraphQLResult<Array<unknown>> = [completedResults, []];
1114+
const acc: GraphQLResult<Array<unknown>> = [completedResults, undefined];
11091115
let index = 0;
11101116
const initialCount = streamUsage.initialCount;
11111117
// eslint-disable-next-line no-constant-condition
@@ -1117,6 +1123,9 @@ async function completeAsyncIteratorValueWithPossibleStream(
11171123
earlyReturn: asyncIterator.return?.bind(asyncIterator),
11181124
});
11191125

1126+
if (exeContext.cancellableStreams === undefined) {
1127+
exeContext.cancellableStreams = new Set();
1128+
}
11201129
exeContext.cancellableStreams.add(streamRecord);
11211130

11221131
const firstStreamItems = firstAsyncStreamItems(
@@ -1298,7 +1307,7 @@ function completeIterableValue(
12981307
// where the list contains no Promises by avoiding creating another Promise.
12991308
let containsPromise = false;
13001309
const completedResults: Array<unknown> = [];
1301-
const acc: GraphQLResult<Array<unknown>> = [completedResults, []];
1310+
const acc: GraphQLResult<Array<unknown>> = [completedResults, undefined];
13021311
let index = 0;
13031312
for (const item of items) {
13041313
// No need to modify the info object containing the path,
@@ -1359,7 +1368,7 @@ function completeIterableValueWithPossibleStream(
13591368
// where the list contains no Promises by avoiding creating another Promise.
13601369
let containsPromise = false;
13611370
const completedResults: Array<unknown> = [];
1362-
const acc: GraphQLResult<Array<unknown>> = [completedResults, []];
1371+
const acc: GraphQLResult<Array<unknown>> = [completedResults, undefined];
13631372
let index = 0;
13641373
const initialCount = streamUsage.initialCount;
13651374
const iterator = items[Symbol.iterator]();
@@ -2330,7 +2339,7 @@ function buildDeferredGroupedFieldSetResult(
23302339
deferredFragmentRecords,
23312340
path: pathToArray(path),
23322341
result:
2333-
errors.length === 0 ? { data: result[0] } : { data: result[0], errors },
2342+
errors === undefined ? { data: result[0] } : { data: result[0], errors },
23342343
incrementalDataRecords: result[1],
23352344
};
23362345
}
@@ -2546,7 +2555,7 @@ function completeStreamItems(
25462555
itemPath,
25472556
incrementalContext,
25482557
);
2549-
return [null, []] as GraphQLResult<unknown>;
2558+
return [null, undefined] as GraphQLResult<unknown>;
25502559
})
25512560
.then(
25522561
(resolvedItem) =>
@@ -2585,7 +2594,7 @@ function completeStreamItems(
25852594
itemPath,
25862595
incrementalContext,
25872596
);
2588-
result = [null, []];
2597+
result = [null, undefined];
25892598
}
25902599
} catch (error) {
25912600
return {
@@ -2606,7 +2615,7 @@ function completeStreamItems(
26062615
itemPath,
26072616
incrementalContext,
26082617
);
2609-
return [null, []] as GraphQLResult<unknown>;
2618+
return [null, undefined] as GraphQLResult<unknown>;
26102619
})
26112620
.then(
26122621
(resolvedItem) =>
@@ -2635,7 +2644,7 @@ function buildStreamItemsResult(
26352644
return {
26362645
streamRecord,
26372646
result:
2638-
errors.length === 0
2647+
errors === undefined
26392648
? { items: [result[0]] }
26402649
: {
26412650
items: [result[0]],

0 commit comments

Comments
 (0)