UNPKG

@graphql-tools/executor

Version:

Fork of GraphQL.js' execute function

262 lines (261 loc) • 11 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.IncrementalGraph = void 0; const utils_1 = require("@graphql-tools/utils"); const BoxedPromiseOrValue_js_1 = require("./BoxedPromiseOrValue.js"); const DeferredFragments_js_1 = require("./DeferredFragments.js"); const invariant_js_1 = require("./invariant.js"); const types_js_1 = require("./types.js"); /** * @internal */ class IncrementalGraph { _rootNodes; _deferredFragmentFactory; _completedQueue; _nextQueue; constructor(deferredFragmentFactory) { this._rootNodes = new Set(); this._deferredFragmentFactory = deferredFragmentFactory; this._completedQueue = []; this._nextQueue = []; } getNewRootNodes(incrementalDataRecords) { const initialResultChildren = new Set(); this._addIncrementalDataRecords(incrementalDataRecords, undefined, initialResultChildren); return this._promoteNonEmptyToRoot(initialResultChildren); } addCompletedSuccessfulExecutionGroup(successfulExecutionGroup) { const { pendingExecutionGroup, incrementalDataRecords } = successfulExecutionGroup; const { deferUsages, path } = pendingExecutionGroup; const deferredFragmentRecords = []; for (const deferUsage of deferUsages) { const deferredFragmentRecord = this._deferredFragmentFactory.get(deferUsage, path); deferredFragmentRecords.push(deferredFragmentRecord); const { pendingExecutionGroups, successfulExecutionGroups } = deferredFragmentRecord; pendingExecutionGroups.delete(pendingExecutionGroup); successfulExecutionGroups.add(successfulExecutionGroup); } if (incrementalDataRecords !== undefined) { this._addIncrementalDataRecords(incrementalDataRecords, deferredFragmentRecords); } } getDeepestDeferredFragmentAtRoot(initialDeferUsage, deferUsages, path) { let bestDeferUsage = initialDeferUsage; let maxDepth = initialDeferUsage.depth; for (const deferUsage of deferUsages) { if (deferUsage === initialDeferUsage) { continue; } const depth = deferUsage.depth; if (depth > maxDepth) { maxDepth = depth; bestDeferUsage = deferUsage; } } return this._deferredFragmentFactory.get(bestDeferUsage, path); } *currentCompletedBatch() { let completed; while ((completed = this._completedQueue.shift()) !== undefined) { yield completed; } if (this._rootNodes.size === 0) { for (const resolve of this._nextQueue) { resolve(undefined); } } } nextCompletedBatch() { const { promise, resolve } = (0, utils_1.createDeferred)(); this._nextQueue.push(resolve); return promise; } abort() { for (const resolve of this._nextQueue) { resolve(undefined); } } hasNext() { return this._rootNodes.size > 0; } completeDeferredFragment(deferUsage, path) { const deferredFragmentRecord = this._deferredFragmentFactory.get(deferUsage, path); if (!this._rootNodes.has(deferredFragmentRecord) || deferredFragmentRecord.pendingExecutionGroups.size > 0) { return; } const successfulExecutionGroups = Array.from(deferredFragmentRecord.successfulExecutionGroups); this._rootNodes.delete(deferredFragmentRecord); for (const successfulExecutionGroup of successfulExecutionGroups) { const { deferUsages, path: resultPath } = successfulExecutionGroup.pendingExecutionGroup; for (const otherDeferUsage of deferUsages) { const otherDeferredFragmentRecord = this._deferredFragmentFactory.get(otherDeferUsage, resultPath); otherDeferredFragmentRecord.successfulExecutionGroups.delete(successfulExecutionGroup); } } const newRootNodes = this._promoteNonEmptyToRoot(deferredFragmentRecord.children); return { deferredFragmentRecord, newRootNodes, successfulExecutionGroups }; } removeDeferredFragment(deferUsage, path) { const deferredFragmentRecord = this._deferredFragmentFactory.get(deferUsage, path); if (!this._rootNodes.has(deferredFragmentRecord)) { return; } this._rootNodes.delete(deferredFragmentRecord); return deferredFragmentRecord; } removeStream(streamRecord) { this._rootNodes.delete(streamRecord); } _addIncrementalDataRecords(incrementalDataRecords, parents, initialResultChildren) { for (const incrementalDataRecord of incrementalDataRecords) { if ((0, types_js_1.isPendingExecutionGroup)(incrementalDataRecord)) { const { deferUsages, path } = incrementalDataRecord; for (const deferUsage of deferUsages) { const deferredFragmentRecord = this._deferredFragmentFactory.get(deferUsage, path); this._addDeferredFragment(deferredFragmentRecord, initialResultChildren); deferredFragmentRecord.pendingExecutionGroups.add(incrementalDataRecord); } if (this._completesRootNode(incrementalDataRecord)) { this._onExecutionGroup(incrementalDataRecord); } } else if (parents === undefined) { (0, invariant_js_1.invariant)(initialResultChildren !== undefined); initialResultChildren.add(incrementalDataRecord); } else { for (const parent of parents) { this._addDeferredFragment(parent, initialResultChildren); parent.children.add(incrementalDataRecord); } } } } _promoteNonEmptyToRoot(maybeEmptyNewRootNodes) { const newRootNodes = []; for (const node of maybeEmptyNewRootNodes) { if ((0, DeferredFragments_js_1.isDeferredFragmentRecord)(node)) { if (node.pendingExecutionGroups.size > 0) { node.setAsPending(); for (const pendingExecutionGroup of node.pendingExecutionGroups) { if (!this._completesRootNode(pendingExecutionGroup)) { this._onExecutionGroup(pendingExecutionGroup); } } this._rootNodes.add(node); newRootNodes.push(node); continue; } for (const child of node.children) { maybeEmptyNewRootNodes.add(child); } } else { this._rootNodes.add(node); newRootNodes.push(node); this._onStreamItems(node); } } return newRootNodes; } _completesRootNode(pendingExecutionGroup) { const { deferUsages, path } = pendingExecutionGroup; for (const deferUsage of deferUsages) { const deferredFragmentRecord = this._deferredFragmentFactory.get(deferUsage, path); if (this._rootNodes.has(deferredFragmentRecord)) { return true; } } return false; } _addDeferredFragment(deferredFragmentRecord, initialResultChildren) { if (this._rootNodes.has(deferredFragmentRecord)) { return; } const parentDeferUsage = deferredFragmentRecord.parentDeferUsage; if (parentDeferUsage === undefined) { (0, invariant_js_1.invariant)(initialResultChildren !== undefined); initialResultChildren.add(deferredFragmentRecord); return; } const parent = this._deferredFragmentFactory.get(parentDeferUsage, deferredFragmentRecord.path); parent.children.add(deferredFragmentRecord); this._addDeferredFragment(parent, initialResultChildren); } _onExecutionGroup(pendingExecutionGroup) { const value = pendingExecutionGroup.result .value; if ((0, utils_1.isPromise)(value)) { value.then(resolved => this._enqueue(resolved)); } else { this._enqueue(value); } } async _onStreamItems(streamRecord) { let items = []; let errors = []; let incrementalDataRecords = []; const streamItemQueue = streamRecord.streamItemQueue; let streamItemRecord; while ((streamItemRecord = streamItemQueue.shift()) !== undefined) { let result = streamItemRecord instanceof BoxedPromiseOrValue_js_1.BoxedPromiseOrValue ? streamItemRecord.value : streamItemRecord().value; if ((0, utils_1.isPromise)(result)) { if (items.length > 0) { this._enqueue({ streamRecord, result: // TODO add additional test case or rework for coverage errors.length > 0 /* c8 ignore start */ ? { items, errors } /* c8 ignore stop */ : { items }, incrementalDataRecords, }); items = []; errors = []; incrementalDataRecords = []; } result = await result; // wait an additional tick to coalesce resolving additional promises // within the queue await Promise.resolve(); } if (result.item === undefined) { if (items.length > 0) { this._enqueue({ streamRecord, result: errors.length > 0 ? { items, errors } : { items }, incrementalDataRecords, }); } this._enqueue(result.errors === undefined ? { streamRecord } : { streamRecord, errors: result.errors, }); return; } items.push(result.item); if (result.errors !== undefined) { errors.push(...result.errors); } if (result.incrementalDataRecords !== undefined) { incrementalDataRecords.push(...result.incrementalDataRecords); } } } _enqueue(completed) { this._completedQueue.push(completed); const next = this._nextQueue.shift(); if (next === undefined) { return; } next(this.currentCompletedBatch()); } } exports.IncrementalGraph = IncrementalGraph;