@graphql-tools/executor 2.0.0-alpha-20240708191055-6aa94b70be0a68ccac6339230c2f5ff436909dc4 → 2.0.0-alpha-20240709211328-82fe341cc6dfe08711abc33047b33107f5df5b85

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -33,44 +33,26 @@ class IncrementalGraph {
33
33
  this._addIncrementalDataRecords(incrementalDataRecords, reconcilableResult.deferredGroupedFieldSetRecord.deferredFragmentRecords);
34
34
  }
35
35
  }
36
- currentCompletedIncrementalData() {
37
- return {
38
- [Symbol.iterator]() {
39
- return this;
40
- },
41
- next: () => {
42
- const value = this._completedQueue.shift();
43
- if (value !== undefined) {
44
- return { value, done: false };
45
- }
46
- return { value: undefined, done: true };
47
- },
48
- };
36
+ *currentCompletedBatch() {
37
+ let completed;
38
+ while ((completed = this._completedQueue.shift()) !== undefined) {
39
+ yield completed;
40
+ }
41
+ if (this._rootNodes.size === 0) {
42
+ for (const resolve of this._nextQueue) {
43
+ resolve(undefined);
44
+ }
45
+ }
49
46
  }
50
- completedIncrementalData() {
51
- return {
52
- [Symbol.asyncIterator]() {
53
- return this;
54
- },
55
- next: () => {
56
- const firstResult = this._completedQueue.shift();
57
- if (firstResult !== undefined) {
58
- return Promise.resolve({
59
- value: this._yieldCurrentCompletedIncrementalData(firstResult),
60
- done: false,
61
- });
62
- }
63
- const { promise, resolve } = (0, utils_1.createDeferred)();
64
- this._nextQueue.push(resolve);
65
- return promise;
66
- },
67
- return: () => {
68
- for (const resolve of this._nextQueue) {
69
- resolve({ value: undefined, done: true });
70
- }
71
- return Promise.resolve({ value: undefined, done: true });
72
- },
73
- };
47
+ nextCompletedBatch() {
48
+ const { promise, resolve } = (0, utils_1.createDeferred)();
49
+ this._nextQueue.push(resolve);
50
+ return promise;
51
+ }
52
+ abort() {
53
+ for (const resolve of this._nextQueue) {
54
+ resolve(undefined);
55
+ }
74
56
  }
75
57
  hasNext() {
76
58
  return this._rootNodes.size > 0;
@@ -103,11 +85,6 @@ class IncrementalGraph {
103
85
  }
104
86
  _removePending(subsequentResultRecord) {
105
87
  this._rootNodes.delete(subsequentResultRecord);
106
- if (this._rootNodes.size === 0) {
107
- for (const resolve of this._nextQueue) {
108
- resolve({ value: undefined, done: true });
109
- }
110
- }
111
88
  }
112
89
  _addIncrementalDataRecords(incrementalDataRecords, parents, initialResultChildren) {
113
90
  for (const incrementalDataRecord of incrementalDataRecords) {
@@ -245,14 +222,16 @@ class IncrementalGraph {
245
222
  while ((completed = this._completedQueue.shift()) !== undefined) {
246
223
  yield completed;
247
224
  }
225
+ if (this._rootNodes.size === 0) {
226
+ for (const resolve of this._nextQueue) {
227
+ resolve(undefined);
228
+ }
229
+ }
248
230
  }
249
231
  _enqueue(completed) {
250
232
  const next = this._nextQueue.shift();
251
233
  if (next !== undefined) {
252
- next({
253
- value: this._yieldCurrentCompletedIncrementalData(completed),
254
- done: false,
255
- });
234
+ next(this._yieldCurrentCompletedIncrementalData(completed));
256
235
  return;
257
236
  }
258
237
  this._completedQueue.push(completed);
@@ -10,6 +10,19 @@ function buildIncrementalResponse(context, result, errors, incrementalDataRecord
10
10
  return incrementalPublisher.buildResponse(result, errors, incrementalDataRecords);
11
11
  }
12
12
  exports.buildIncrementalResponse = buildIncrementalResponse;
13
+ /**
14
+ * The IncrementalPublisherState Enum tracks the state of the IncrementalPublisher, which is initialized to
15
+ * "Started". When there are no more incremental results to publish, the state is set to "Completed". On the
16
+ * next call to next, clean-up is potentially performed and the state is set to "Finished".
17
+ *
18
+ * If the IncrementalPublisher is ended early, it may be advanced directly from "Started" to "Finished".
19
+ */
20
+ var IncrementalPublisherState;
21
+ (function (IncrementalPublisherState) {
22
+ IncrementalPublisherState[IncrementalPublisherState["Started"] = 1] = "Started";
23
+ IncrementalPublisherState[IncrementalPublisherState["Completed"] = 2] = "Completed";
24
+ IncrementalPublisherState[IncrementalPublisherState["Finished"] = 3] = "Finished";
25
+ })(IncrementalPublisherState || (IncrementalPublisherState = {}));
13
26
  /**
14
27
  * This class is used to publish incremental results to the client, enabling semi-concurrent
15
28
  * execution while preserving result order.
@@ -56,32 +69,43 @@ class IncrementalPublisher {
56
69
  return String(this._nextId++);
57
70
  }
58
71
  _subscribe() {
59
- let isDone = false;
72
+ let incrementalPublisherState = IncrementalPublisherState.Started;
73
+ const _finish = async () => {
74
+ incrementalPublisherState = IncrementalPublisherState.Finished;
75
+ this._incrementalGraph.abort();
76
+ await this._returnAsyncIterators();
77
+ };
60
78
  this._context.signal?.addEventListener('abort', () => {
61
- this._incrementalGraph.completedIncrementalData().return();
79
+ this._incrementalGraph.abort();
62
80
  });
63
81
  const _next = async () => {
64
- if (isDone) {
65
- await this._returnAsyncIteratorsIgnoringErrors();
66
- return { value: undefined, done: true };
82
+ switch (incrementalPublisherState) {
83
+ case IncrementalPublisherState.Finished: {
84
+ return { value: undefined, done: true };
85
+ }
86
+ case IncrementalPublisherState.Completed: {
87
+ await _finish();
88
+ return { value: undefined, done: true };
89
+ }
90
+ case IncrementalPublisherState.Started: {
91
+ // continue
92
+ }
67
93
  }
68
94
  const context = {
69
95
  pending: [],
70
96
  incremental: [],
71
97
  completed: [],
72
98
  };
73
- let currentCompletedIncrementalData = this._incrementalGraph.currentCompletedIncrementalData();
74
- const completedIncrementalData = this._incrementalGraph.completedIncrementalData();
75
- const asyncIterator = completedIncrementalData[Symbol.asyncIterator]();
99
+ let batch = this._incrementalGraph.currentCompletedBatch();
76
100
  do {
77
- for (const completedResult of currentCompletedIncrementalData) {
101
+ for (const completedResult of batch) {
78
102
  this._handleCompletedIncrementalData(completedResult, context);
79
103
  }
80
104
  const { incremental, completed } = context;
81
105
  if (incremental.length > 0 || completed.length > 0) {
82
106
  const hasNext = this._incrementalGraph.hasNext();
83
107
  if (!hasNext) {
84
- isDone = true;
108
+ incrementalPublisherState = IncrementalPublisherState.Completed;
85
109
  }
86
110
  const subsequentIncrementalExecutionResult = {
87
111
  hasNext,
@@ -98,23 +122,19 @@ class IncrementalPublisher {
98
122
  }
99
123
  return { value: subsequentIncrementalExecutionResult, done: false };
100
124
  }
101
- const iteration = await asyncIterator.next();
102
- currentCompletedIncrementalData = iteration.value;
103
- } while (currentCompletedIncrementalData !== undefined);
125
+ batch = await this._incrementalGraph.nextCompletedBatch();
126
+ } while (batch !== undefined);
104
127
  if (this._context.signal?.aborted) {
105
128
  throw this._context.signal.reason;
106
129
  }
107
- await this._returnAsyncIteratorsIgnoringErrors();
108
130
  return { value: undefined, done: true };
109
131
  };
110
132
  const _return = async () => {
111
- isDone = true;
112
- await this._returnAsyncIterators();
133
+ await _finish();
113
134
  return { value: undefined, done: true };
114
135
  };
115
136
  const _throw = async (error) => {
116
- isDone = true;
117
- await this._returnAsyncIterators();
137
+ await _finish();
118
138
  return Promise.reject(error);
119
139
  };
120
140
  return {
@@ -297,7 +317,7 @@ class IncrementalPublisher {
297
317
  };
298
318
  }
299
319
  async _returnAsyncIterators() {
300
- await this._incrementalGraph.completedIncrementalData().return();
320
+ await this._incrementalGraph.abort();
301
321
  const cancellableStreams = this._context.cancellableStreams;
302
322
  if (cancellableStreams === undefined) {
303
323
  return;
@@ -310,9 +330,4 @@ class IncrementalPublisher {
310
330
  }
311
331
  await Promise.all(promises);
312
332
  }
313
- async _returnAsyncIteratorsIgnoringErrors() {
314
- await this._returnAsyncIterators().catch(() => {
315
- // Ignore errors
316
- });
317
- }
318
333
  }
@@ -30,44 +30,26 @@ export class IncrementalGraph {
30
30
  this._addIncrementalDataRecords(incrementalDataRecords, reconcilableResult.deferredGroupedFieldSetRecord.deferredFragmentRecords);
31
31
  }
32
32
  }
33
- currentCompletedIncrementalData() {
34
- return {
35
- [Symbol.iterator]() {
36
- return this;
37
- },
38
- next: () => {
39
- const value = this._completedQueue.shift();
40
- if (value !== undefined) {
41
- return { value, done: false };
42
- }
43
- return { value: undefined, done: true };
44
- },
45
- };
33
+ *currentCompletedBatch() {
34
+ let completed;
35
+ while ((completed = this._completedQueue.shift()) !== undefined) {
36
+ yield completed;
37
+ }
38
+ if (this._rootNodes.size === 0) {
39
+ for (const resolve of this._nextQueue) {
40
+ resolve(undefined);
41
+ }
42
+ }
46
43
  }
47
- completedIncrementalData() {
48
- return {
49
- [Symbol.asyncIterator]() {
50
- return this;
51
- },
52
- next: () => {
53
- const firstResult = this._completedQueue.shift();
54
- if (firstResult !== undefined) {
55
- return Promise.resolve({
56
- value: this._yieldCurrentCompletedIncrementalData(firstResult),
57
- done: false,
58
- });
59
- }
60
- const { promise, resolve } = createDeferred();
61
- this._nextQueue.push(resolve);
62
- return promise;
63
- },
64
- return: () => {
65
- for (const resolve of this._nextQueue) {
66
- resolve({ value: undefined, done: true });
67
- }
68
- return Promise.resolve({ value: undefined, done: true });
69
- },
70
- };
44
+ nextCompletedBatch() {
45
+ const { promise, resolve } = createDeferred();
46
+ this._nextQueue.push(resolve);
47
+ return promise;
48
+ }
49
+ abort() {
50
+ for (const resolve of this._nextQueue) {
51
+ resolve(undefined);
52
+ }
71
53
  }
72
54
  hasNext() {
73
55
  return this._rootNodes.size > 0;
@@ -100,11 +82,6 @@ export class IncrementalGraph {
100
82
  }
101
83
  _removePending(subsequentResultRecord) {
102
84
  this._rootNodes.delete(subsequentResultRecord);
103
- if (this._rootNodes.size === 0) {
104
- for (const resolve of this._nextQueue) {
105
- resolve({ value: undefined, done: true });
106
- }
107
- }
108
85
  }
109
86
  _addIncrementalDataRecords(incrementalDataRecords, parents, initialResultChildren) {
110
87
  for (const incrementalDataRecord of incrementalDataRecords) {
@@ -242,14 +219,16 @@ export class IncrementalGraph {
242
219
  while ((completed = this._completedQueue.shift()) !== undefined) {
243
220
  yield completed;
244
221
  }
222
+ if (this._rootNodes.size === 0) {
223
+ for (const resolve of this._nextQueue) {
224
+ resolve(undefined);
225
+ }
226
+ }
245
227
  }
246
228
  _enqueue(completed) {
247
229
  const next = this._nextQueue.shift();
248
230
  if (next !== undefined) {
249
- next({
250
- value: this._yieldCurrentCompletedIncrementalData(completed),
251
- done: false,
252
- });
231
+ next(this._yieldCurrentCompletedIncrementalData(completed));
253
232
  return;
254
233
  }
255
234
  this._completedQueue.push(completed);
@@ -6,6 +6,19 @@ export function buildIncrementalResponse(context, result, errors, incrementalDat
6
6
  const incrementalPublisher = new IncrementalPublisher(context);
7
7
  return incrementalPublisher.buildResponse(result, errors, incrementalDataRecords);
8
8
  }
9
+ /**
10
+ * The IncrementalPublisherState Enum tracks the state of the IncrementalPublisher, which is initialized to
11
+ * "Started". When there are no more incremental results to publish, the state is set to "Completed". On the
12
+ * next call to next, clean-up is potentially performed and the state is set to "Finished".
13
+ *
14
+ * If the IncrementalPublisher is ended early, it may be advanced directly from "Started" to "Finished".
15
+ */
16
+ var IncrementalPublisherState;
17
+ (function (IncrementalPublisherState) {
18
+ IncrementalPublisherState[IncrementalPublisherState["Started"] = 1] = "Started";
19
+ IncrementalPublisherState[IncrementalPublisherState["Completed"] = 2] = "Completed";
20
+ IncrementalPublisherState[IncrementalPublisherState["Finished"] = 3] = "Finished";
21
+ })(IncrementalPublisherState || (IncrementalPublisherState = {}));
9
22
  /**
10
23
  * This class is used to publish incremental results to the client, enabling semi-concurrent
11
24
  * execution while preserving result order.
@@ -52,32 +65,43 @@ class IncrementalPublisher {
52
65
  return String(this._nextId++);
53
66
  }
54
67
  _subscribe() {
55
- let isDone = false;
68
+ let incrementalPublisherState = IncrementalPublisherState.Started;
69
+ const _finish = async () => {
70
+ incrementalPublisherState = IncrementalPublisherState.Finished;
71
+ this._incrementalGraph.abort();
72
+ await this._returnAsyncIterators();
73
+ };
56
74
  this._context.signal?.addEventListener('abort', () => {
57
- this._incrementalGraph.completedIncrementalData().return();
75
+ this._incrementalGraph.abort();
58
76
  });
59
77
  const _next = async () => {
60
- if (isDone) {
61
- await this._returnAsyncIteratorsIgnoringErrors();
62
- return { value: undefined, done: true };
78
+ switch (incrementalPublisherState) {
79
+ case IncrementalPublisherState.Finished: {
80
+ return { value: undefined, done: true };
81
+ }
82
+ case IncrementalPublisherState.Completed: {
83
+ await _finish();
84
+ return { value: undefined, done: true };
85
+ }
86
+ case IncrementalPublisherState.Started: {
87
+ // continue
88
+ }
63
89
  }
64
90
  const context = {
65
91
  pending: [],
66
92
  incremental: [],
67
93
  completed: [],
68
94
  };
69
- let currentCompletedIncrementalData = this._incrementalGraph.currentCompletedIncrementalData();
70
- const completedIncrementalData = this._incrementalGraph.completedIncrementalData();
71
- const asyncIterator = completedIncrementalData[Symbol.asyncIterator]();
95
+ let batch = this._incrementalGraph.currentCompletedBatch();
72
96
  do {
73
- for (const completedResult of currentCompletedIncrementalData) {
97
+ for (const completedResult of batch) {
74
98
  this._handleCompletedIncrementalData(completedResult, context);
75
99
  }
76
100
  const { incremental, completed } = context;
77
101
  if (incremental.length > 0 || completed.length > 0) {
78
102
  const hasNext = this._incrementalGraph.hasNext();
79
103
  if (!hasNext) {
80
- isDone = true;
104
+ incrementalPublisherState = IncrementalPublisherState.Completed;
81
105
  }
82
106
  const subsequentIncrementalExecutionResult = {
83
107
  hasNext,
@@ -94,23 +118,19 @@ class IncrementalPublisher {
94
118
  }
95
119
  return { value: subsequentIncrementalExecutionResult, done: false };
96
120
  }
97
- const iteration = await asyncIterator.next();
98
- currentCompletedIncrementalData = iteration.value;
99
- } while (currentCompletedIncrementalData !== undefined);
121
+ batch = await this._incrementalGraph.nextCompletedBatch();
122
+ } while (batch !== undefined);
100
123
  if (this._context.signal?.aborted) {
101
124
  throw this._context.signal.reason;
102
125
  }
103
- await this._returnAsyncIteratorsIgnoringErrors();
104
126
  return { value: undefined, done: true };
105
127
  };
106
128
  const _return = async () => {
107
- isDone = true;
108
- await this._returnAsyncIterators();
129
+ await _finish();
109
130
  return { value: undefined, done: true };
110
131
  };
111
132
  const _throw = async (error) => {
112
- isDone = true;
113
- await this._returnAsyncIterators();
133
+ await _finish();
114
134
  return Promise.reject(error);
115
135
  };
116
136
  return {
@@ -293,7 +313,7 @@ class IncrementalPublisher {
293
313
  };
294
314
  }
295
315
  async _returnAsyncIterators() {
296
- await this._incrementalGraph.completedIncrementalData().return();
316
+ await this._incrementalGraph.abort();
297
317
  const cancellableStreams = this._context.cancellableStreams;
298
318
  if (cancellableStreams === undefined) {
299
319
  return;
@@ -306,9 +326,4 @@ class IncrementalPublisher {
306
326
  }
307
327
  await Promise.all(promises);
308
328
  }
309
- async _returnAsyncIteratorsIgnoringErrors() {
310
- await this._returnAsyncIterators().catch(() => {
311
- // Ignore errors
312
- });
313
- }
314
329
  }
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@graphql-tools/executor",
3
- "version": "2.0.0-alpha-20240708191055-6aa94b70be0a68ccac6339230c2f5ff436909dc4",
3
+ "version": "2.0.0-alpha-20240709211328-82fe341cc6dfe08711abc33047b33107f5df5b85",
4
4
  "sideEffects": false,
5
5
  "peerDependencies": {
6
6
  "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
7
7
  },
8
8
  "dependencies": {
9
- "@graphql-tools/utils": "10.4.0-alpha-20240708191055-6aa94b70be0a68ccac6339230c2f5ff436909dc4",
9
+ "@graphql-tools/utils": "10.4.0-alpha-20240709211328-82fe341cc6dfe08711abc33047b33107f5df5b85",
10
10
  "@graphql-typed-document-node/core": "3.2.0",
11
11
  "@repeaterjs/repeater": "^3.0.4",
12
12
  "tslib": "^2.4.0",
@@ -9,15 +9,9 @@ export declare class IncrementalGraph {
9
9
  constructor();
10
10
  getNewPending(incrementalDataRecords: ReadonlyArray<IncrementalDataRecord>): ReadonlyArray<SubsequentResultRecord>;
11
11
  addCompletedReconcilableDeferredGroupedFieldSet(reconcilableResult: ReconcilableDeferredGroupedFieldSetResult): void;
12
- currentCompletedIncrementalData(): {
13
- [Symbol.iterator](): any;
14
- next: () => IteratorResult<IncrementalDataRecordResult>;
15
- };
16
- completedIncrementalData(): {
17
- [Symbol.asyncIterator](): any;
18
- next: () => Promise<IteratorResult<Iterable<IncrementalDataRecordResult>>>;
19
- return: () => Promise<IteratorResult<Iterable<IncrementalDataRecordResult>>>;
20
- };
12
+ currentCompletedBatch(): Generator<IncrementalDataRecordResult>;
13
+ nextCompletedBatch(): Promise<Iterable<IncrementalDataRecordResult> | undefined>;
14
+ abort(): void;
21
15
  hasNext(): boolean;
22
16
  completeDeferredFragment(deferredFragmentRecord: DeferredFragmentRecord): {
23
17
  newPending: ReadonlyArray<SubsequentResultRecord>;
@@ -9,15 +9,9 @@ export declare class IncrementalGraph {
9
9
  constructor();
10
10
  getNewPending(incrementalDataRecords: ReadonlyArray<IncrementalDataRecord>): ReadonlyArray<SubsequentResultRecord>;
11
11
  addCompletedReconcilableDeferredGroupedFieldSet(reconcilableResult: ReconcilableDeferredGroupedFieldSetResult): void;
12
- currentCompletedIncrementalData(): {
13
- [Symbol.iterator](): any;
14
- next: () => IteratorResult<IncrementalDataRecordResult>;
15
- };
16
- completedIncrementalData(): {
17
- [Symbol.asyncIterator](): any;
18
- next: () => Promise<IteratorResult<Iterable<IncrementalDataRecordResult>>>;
19
- return: () => Promise<IteratorResult<Iterable<IncrementalDataRecordResult>>>;
20
- };
12
+ currentCompletedBatch(): Generator<IncrementalDataRecordResult>;
13
+ nextCompletedBatch(): Promise<Iterable<IncrementalDataRecordResult> | undefined>;
14
+ abort(): void;
21
15
  hasNext(): boolean;
22
16
  completeDeferredFragment(deferredFragmentRecord: DeferredFragmentRecord): {
23
17
  newPending: ReadonlyArray<SubsequentResultRecord>;