Skip to content

Commit 448eaa0

Browse files
committed
simplify graph by allowing mutation
1 parent 605bd1f commit 448eaa0

File tree

3 files changed

+87
-128
lines changed

3 files changed

+87
-128
lines changed

src/execution/IncrementalGraph.ts

Lines changed: 59 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -15,32 +15,16 @@ import type {
1515
StreamRecord,
1616
SubsequentResultRecord,
1717
} from './types.js';
18-
import { isDeferredGroupedFieldSetRecord } from './types.js';
19-
20-
interface DeferredFragmentNode {
21-
deferredFragmentRecord: DeferredFragmentRecord;
22-
deferredGroupedFieldSetRecords: Set<DeferredGroupedFieldSetRecord>;
23-
reconcilableResults: Set<ReconcilableDeferredGroupedFieldSetResult>;
24-
children: Set<SubsequentResultNode>;
25-
}
26-
27-
function isDeferredFragmentNode(
28-
node: SubsequentResultNode | undefined,
29-
): node is DeferredFragmentNode {
30-
return node !== undefined && 'deferredFragmentRecord' in node;
31-
}
32-
33-
type SubsequentResultNode = DeferredFragmentNode | StreamRecord;
18+
import {
19+
isDeferredFragmentRecord,
20+
isDeferredGroupedFieldSetRecord,
21+
} from './types.js';
3422

3523
/**
3624
* @internal
3725
*/
3826
export class IncrementalGraph {
39-
private _pending: Set<SubsequentResultNode>;
40-
private _deferredFragmentNodes: Map<
41-
DeferredFragmentRecord,
42-
DeferredFragmentNode
43-
>;
27+
private _pending: Set<SubsequentResultRecord>;
4428

4529
private _completedQueue: Array<IncrementalDataRecordResult>;
4630
private _nextQueue: Array<
@@ -49,33 +33,31 @@ export class IncrementalGraph {
4933

5034
constructor() {
5135
this._pending = new Set();
52-
this._deferredFragmentNodes = new Map();
5336
this._completedQueue = [];
5437
this._nextQueue = [];
5538
}
5639

5740
getNewPending(
5841
incrementalDataRecords: ReadonlyArray<IncrementalDataRecord>,
5942
): ReadonlyArray<SubsequentResultRecord> {
60-
const newPending = new Set<SubsequentResultNode>();
43+
const newPending = new Set<SubsequentResultRecord>();
6144
this._addIncrementalDataRecords(
6245
incrementalDataRecords,
6346
undefined,
6447
newPending,
6548
);
66-
return this._pendingNodesToResults(newPending);
49+
return this._getNewPending(newPending);
6750
}
6851

6952
addCompletedReconcilableDeferredGroupedFieldSet(
7053
reconcilableResult: ReconcilableDeferredGroupedFieldSetResult,
7154
): void {
72-
for (const deferredFragmentNode of this._fragmentsToNodes(
73-
reconcilableResult.deferredGroupedFieldSetRecord.deferredFragmentRecords,
74-
)) {
75-
deferredFragmentNode.deferredGroupedFieldSetRecords.delete(
55+
for (const deferredFragmentRecord of reconcilableResult
56+
.deferredGroupedFieldSetRecord.deferredFragmentRecords) {
57+
deferredFragmentRecord.deferredGroupedFieldSetRecords.delete(
7658
reconcilableResult.deferredGroupedFieldSetRecord,
7759
);
78-
deferredFragmentNode.reconcilableResults.add(reconcilableResult);
60+
deferredFragmentRecord.reconcilableResults.add(reconcilableResult);
7961
}
8062

8163
const incrementalDataRecords = reconcilableResult.incrementalDataRecords;
@@ -131,64 +113,46 @@ export class IncrementalGraph {
131113
reconcilableResults: ReadonlyArray<ReconcilableDeferredGroupedFieldSetResult>;
132114
}
133115
| undefined {
134-
const deferredFragmentNode = this._deferredFragmentNodes.get(
135-
deferredFragmentRecord,
136-
);
137116
// TODO: add test case?
138117
/* c8 ignore next 3 */
139-
if (deferredFragmentNode === undefined) {
118+
if (!this._pending.has(deferredFragmentRecord)) {
140119
return undefined;
141120
}
142-
if (deferredFragmentNode.deferredGroupedFieldSetRecords.size > 0) {
121+
if (deferredFragmentRecord.deferredGroupedFieldSetRecords.size > 0) {
143122
return;
144123
}
145124
const reconcilableResults = Array.from(
146-
deferredFragmentNode.reconcilableResults,
125+
deferredFragmentRecord.reconcilableResults,
147126
);
148-
this._removePending(deferredFragmentNode);
127+
this._removePending(deferredFragmentRecord);
149128
for (const reconcilableResult of reconcilableResults) {
150-
for (const otherDeferredFragmentNode of this._fragmentsToNodes(
151-
reconcilableResult.deferredGroupedFieldSetRecord
152-
.deferredFragmentRecords,
153-
)) {
154-
otherDeferredFragmentNode.reconcilableResults.delete(
129+
for (const otherDeferredFragmentRecord of reconcilableResult
130+
.deferredGroupedFieldSetRecord.deferredFragmentRecords) {
131+
otherDeferredFragmentRecord.reconcilableResults.delete(
155132
reconcilableResult,
156133
);
157134
}
158135
}
159-
const newPending = this._pendingNodesToResults(
160-
deferredFragmentNode.children,
161-
);
136+
const newPending = this._getNewPending(deferredFragmentRecord.children);
162137
return { newPending, reconcilableResults };
163138
}
164139

165140
removeDeferredFragment(
166141
deferredFragmentRecord: DeferredFragmentRecord,
167142
): boolean {
168-
const deferredFragmentNode = this._deferredFragmentNodes.get(
169-
deferredFragmentRecord,
170-
);
171-
if (deferredFragmentNode === undefined) {
143+
if (!this._pending.has(deferredFragmentRecord)) {
172144
return false;
173145
}
174-
this._removePending(deferredFragmentNode);
175-
this._deferredFragmentNodes.delete(deferredFragmentRecord);
176-
// TODO: add test case for an erroring deferred fragment with child defers
177-
/* c8 ignore next 5 */
178-
for (const child of deferredFragmentNode.children) {
179-
if (isDeferredFragmentNode(child)) {
180-
this.removeDeferredFragment(child.deferredFragmentRecord);
181-
}
182-
}
146+
this._removePending(deferredFragmentRecord);
183147
return true;
184148
}
185149

186150
removeStream(streamRecord: StreamRecord): void {
187151
this._removePending(streamRecord);
188152
}
189153

190-
private _removePending(subsequentResultNode: SubsequentResultNode): void {
191-
this._pending.delete(subsequentResultNode);
154+
private _removePending(subsequentResultRecord: SubsequentResultRecord): void {
155+
this._pending.delete(subsequentResultRecord);
192156
if (this._pending.size === 0) {
193157
for (const resolve of this._nextQueue) {
194158
resolve({ value: undefined, done: true });
@@ -199,115 +163,87 @@ export class IncrementalGraph {
199163
private _addIncrementalDataRecords(
200164
incrementalDataRecords: ReadonlyArray<IncrementalDataRecord>,
201165
parents: ReadonlyArray<DeferredFragmentRecord> | undefined,
202-
newPending?: Set<SubsequentResultNode> | undefined,
166+
subsequentResultRecords?: Set<SubsequentResultRecord> | undefined,
203167
): void {
204168
for (const incrementalDataRecord of incrementalDataRecords) {
205169
if (isDeferredGroupedFieldSetRecord(incrementalDataRecord)) {
206170
for (const deferredFragmentRecord of incrementalDataRecord.deferredFragmentRecords) {
207-
const deferredFragmentNode = this._addDeferredFragmentNode(
171+
this._addDeferredFragment(
208172
deferredFragmentRecord,
209-
newPending,
173+
subsequentResultRecords,
210174
);
211-
deferredFragmentNode.deferredGroupedFieldSetRecords.add(
175+
deferredFragmentRecord.deferredGroupedFieldSetRecords.add(
212176
incrementalDataRecord,
213177
);
214178
}
215179
if (this._hasPendingFragment(incrementalDataRecord)) {
216180
this._onDeferredGroupedFieldSet(incrementalDataRecord);
217181
}
218182
} else if (parents === undefined) {
219-
invariant(newPending !== undefined);
220-
newPending.add(incrementalDataRecord);
183+
invariant(subsequentResultRecords !== undefined);
184+
subsequentResultRecords.add(incrementalDataRecord);
221185
} else {
222186
for (const parent of parents) {
223-
const deferredFragmentNode = this._addDeferredFragmentNode(
224-
parent,
225-
newPending,
226-
);
227-
deferredFragmentNode.children.add(incrementalDataRecord);
187+
this._addDeferredFragment(parent, subsequentResultRecords);
188+
parent.children.add(incrementalDataRecord);
228189
}
229190
}
230191
}
231192
}
232193

233-
private _pendingNodesToResults(
234-
newPendingNodes: Set<SubsequentResultNode>,
194+
private _getNewPending(
195+
subsequentResultRecords: Set<SubsequentResultRecord>,
235196
): ReadonlyArray<SubsequentResultRecord> {
236-
const newPendingResults: Array<SubsequentResultRecord> = [];
237-
for (const node of newPendingNodes) {
238-
if (isDeferredFragmentNode(node)) {
239-
if (node.deferredGroupedFieldSetRecords.size > 0) {
240-
for (const deferredGroupedFieldSetRecord of node.deferredGroupedFieldSetRecords) {
197+
const newPending: Array<SubsequentResultRecord> = [];
198+
for (const subsequentResultRecord of subsequentResultRecords) {
199+
if (isDeferredFragmentRecord(subsequentResultRecord)) {
200+
if (subsequentResultRecord.deferredGroupedFieldSetRecords.size > 0) {
201+
for (const deferredGroupedFieldSetRecord of subsequentResultRecord.deferredGroupedFieldSetRecords) {
241202
if (!this._hasPendingFragment(deferredGroupedFieldSetRecord)) {
242203
this._onDeferredGroupedFieldSet(deferredGroupedFieldSetRecord);
243204
}
244205
}
245-
this._pending.add(node);
246-
newPendingResults.push(node.deferredFragmentRecord);
206+
this._pending.add(subsequentResultRecord);
207+
newPending.push(subsequentResultRecord);
247208
continue;
248209
}
249-
this._deferredFragmentNodes.delete(node.deferredFragmentRecord);
250-
for (const child of node.children) {
251-
newPendingNodes.add(child);
210+
for (const child of subsequentResultRecord.children) {
211+
subsequentResultRecords.add(child);
252212
}
253213
} else {
254-
this._pending.add(node);
255-
newPendingResults.push(node);
214+
this._pending.add(subsequentResultRecord);
215+
newPending.push(subsequentResultRecord);
256216

257217
// eslint-disable-next-line @typescript-eslint/no-floating-promises
258-
this._onStreamItems(node);
218+
this._onStreamItems(subsequentResultRecord);
259219
}
260220
}
261-
return newPendingResults;
221+
return newPending;
262222
}
263223

264224
private _hasPendingFragment(
265225
deferredGroupedFieldSetRecord: DeferredGroupedFieldSetRecord,
266226
): boolean {
267-
return this._fragmentsToNodes(
268-
deferredGroupedFieldSetRecord.deferredFragmentRecords,
269-
).some((node) => this._pending.has(node));
270-
}
271-
272-
private _fragmentsToNodes(
273-
deferredFragmentRecords: ReadonlyArray<DeferredFragmentRecord>,
274-
): Array<DeferredFragmentNode> {
275-
return deferredFragmentRecords
276-
.map((deferredFragmentRecord) =>
277-
this._deferredFragmentNodes.get(deferredFragmentRecord),
278-
)
279-
.filter<DeferredFragmentNode>(isDeferredFragmentNode);
227+
return deferredGroupedFieldSetRecord.deferredFragmentRecords.some(
228+
(deferredFragmentRecord) => this._pending.has(deferredFragmentRecord),
229+
);
280230
}
281231

282-
private _addDeferredFragmentNode(
232+
private _addDeferredFragment(
283233
deferredFragmentRecord: DeferredFragmentRecord,
284-
newPending: Set<SubsequentResultNode> | undefined,
285-
): DeferredFragmentNode {
286-
let deferredFragmentNode = this._deferredFragmentNodes.get(
287-
deferredFragmentRecord,
288-
);
289-
if (deferredFragmentNode !== undefined) {
290-
return deferredFragmentNode;
234+
subsequentResultRecords: Set<SubsequentResultRecord> | undefined,
235+
): void {
236+
if (this._pending.has(deferredFragmentRecord)) {
237+
return;
291238
}
292-
deferredFragmentNode = {
293-
deferredFragmentRecord,
294-
deferredGroupedFieldSetRecords: new Set(),
295-
reconcilableResults: new Set(),
296-
children: new Set(),
297-
};
298-
this._deferredFragmentNodes.set(
299-
deferredFragmentRecord,
300-
deferredFragmentNode,
301-
);
302239
const parent = deferredFragmentRecord.parent;
303240
if (parent === undefined) {
304-
invariant(newPending !== undefined);
305-
newPending.add(deferredFragmentNode);
306-
return deferredFragmentNode;
241+
invariant(subsequentResultRecords !== undefined);
242+
subsequentResultRecords.add(deferredFragmentRecord);
243+
return;
307244
}
308-
const parentNode = this._addDeferredFragmentNode(parent, newPending);
309-
parentNode.children.add(deferredFragmentNode);
310-
return deferredFragmentNode;
245+
parent.children.add(deferredFragmentRecord);
246+
this._addDeferredFragment(parent, subsequentResultRecords);
311247
}
312248

313249
private _onDeferredGroupedFieldSet(

src/execution/execute.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ import { buildIncrementalResponse } from './IncrementalPublisher.js';
6363
import { mapAsyncIterable } from './mapAsyncIterable.js';
6464
import type {
6565
CancellableStreamRecord,
66-
DeferredFragmentRecord,
6766
DeferredGroupedFieldSetRecord,
6867
DeferredGroupedFieldSetResult,
6968
ExecutionResult,
@@ -73,6 +72,7 @@ import type {
7372
StreamItemResult,
7473
StreamRecord,
7574
} from './types.js';
75+
import { DeferredFragmentRecord } from './types.js';
7676
import {
7777
getArgumentValues,
7878
getDirectiveValues,
@@ -1676,11 +1676,11 @@ function addNewDeferredFragments(
16761676
: deferredFragmentRecordFromDeferUsage(parentDeferUsage, newDeferMap);
16771677

16781678
// Instantiate the new record.
1679-
const deferredFragmentRecord: DeferredFragmentRecord = {
1679+
const deferredFragmentRecord = new DeferredFragmentRecord(
16801680
path,
1681-
label: newDeferUsage.label,
1681+
newDeferUsage.label,
16821682
parent,
1683-
};
1683+
);
16841684

16851685
// Update the map.
16861686
newDeferMap.set(newDeferUsage, deferredFragmentRecord);

src/execution/types.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,10 +214,33 @@ export interface DeferredGroupedFieldSetRecord {
214214

215215
export type SubsequentResultRecord = DeferredFragmentRecord | StreamRecord;
216216

217-
export interface DeferredFragmentRecord {
217+
/** @internal */
218+
export class DeferredFragmentRecord {
218219
path: Path | undefined;
219220
label: string | undefined;
220221
parent: DeferredFragmentRecord | undefined;
222+
deferredGroupedFieldSetRecords: Set<DeferredGroupedFieldSetRecord>;
223+
reconcilableResults: Set<ReconcilableDeferredGroupedFieldSetResult>;
224+
children: Set<SubsequentResultRecord>;
225+
226+
constructor(
227+
path: Path | undefined,
228+
label: string | undefined,
229+
parent: DeferredFragmentRecord | undefined,
230+
) {
231+
this.path = path;
232+
this.label = label;
233+
this.parent = parent;
234+
this.deferredGroupedFieldSetRecords = new Set();
235+
this.reconcilableResults = new Set();
236+
this.children = new Set();
237+
}
238+
}
239+
240+
export function isDeferredFragmentRecord(
241+
subsequentResultRecord: SubsequentResultRecord,
242+
): subsequentResultRecord is DeferredFragmentRecord {
243+
return subsequentResultRecord instanceof DeferredFragmentRecord;
221244
}
222245

223246
export interface StreamItemResult {

0 commit comments

Comments
 (0)