UNPKG

@graphql-hive/router-runtime

Version:
1,620 lines (1,618 loc) • 64.3 kB
import { handleFederationSupergraph } from '@graphql-mesh/fusion-runtime'; import { createDefaultExecutor } from '@graphql-mesh/transport-common'; import { defaultPrintFn } from '@graphql-tools/executor-common'; import { filterInternalFieldsAndTypes, getRngFromEnv } from '@graphql-tools/federation'; import { isAsyncIterable, getOperationASTFromRequest, memoize1, getOperationASTFromDocument, relocatedError, mergeDeep, getDirective } from '@graphql-tools/utils'; import { handleMaybePromise, mapAsyncIterator, isPromise } from '@whatwg-node/promise-helpers'; import { parse, isObjectType, isAbstractType, Kind, isInterfaceType, isOutputType, TypeNameMetaFieldDef, getNamedType, isEnumType, isNonNullType, visit, BREAK } from 'graphql'; import { documentStringMap } from '@envelop/core'; import { getVariableValues, getFragmentsFromDocument } from '@graphql-tools/executor'; function isEntityRepresentation(obj) { return obj?.__typename != null; } function executeQueryPlan({ queryPlan, executionRequest, onSubgraphExecute, supergraphSchema }) { const node = queryPlan.node; if (!node) { throw new Error("Query plan has no root node."); } const executionContext = createQueryPlanExecutionContext({ supergraphSchema, executionRequest, onSubgraphExecute }); function handleResp() { const executionResult = {}; if (hasOwnProperties(executionContext.data)) { executionResult.data = projectDataByOperation(executionContext); } if (executionContext.errors.length > 0) { executionResult.errors = executionContext.errors; } return executionResult; } return handleMaybePromise( () => executePlanNode(node, executionContext), (res) => { if (isAsyncIterable(res)) { return mapAsyncIterator(res, handleResp); } return handleResp(); } ); } const globalEmpty = {}; const projectionArtifactsByDocument = /* @__PURE__ */ new WeakMap(); const compiledRequiresCache = /* @__PURE__ */ new WeakMap(); function createQueryPlanExecutionContext({ supergraphSchema, executionRequest, onSubgraphExecute }) { const { operation, fragments, compiledProjection } = getOrCreateCompiledProjectionArtifacts(executionRequest); let variableValues = executionRequest.variables; if (operation.variableDefinitions) { const variableValuesResult = getVariableValues( supergraphSchema, operation.variableDefinitions, variableValues || globalEmpty ); if (variableValuesResult.errors?.length) { if (variableValuesResult.errors.length === 1) { throw variableValuesResult.errors[0]; } if (variableValuesResult.errors.length > 1) { throw new AggregateError( variableValuesResult.errors, "Variable parsing error" ); } } if (variableValuesResult.coerced) { variableValues = variableValuesResult.coerced; } } return { supergraphSchema, operation, fragments, variableValues, data: {}, errors: [], onSubgraphExecute, executionRequest, compiledProjection, projectionRuntimeCache: { fieldMetaByParentType: /* @__PURE__ */ new WeakMap(), inaccessibleByObjectType: /* @__PURE__ */ new WeakMap(), enumProjectionValueByType: /* @__PURE__ */ new WeakMap(), entityTypeConditionResult: /* @__PURE__ */ new Map() } }; } function normalizeFlattenNodePath(path) { const normalized = []; for (const segment of path) { if (segment === "@") { normalized.push({ kind: "List" }); continue; } else if ("Field" in segment) { normalized.push({ kind: "Field", name: segment.Field }); } else if ("TypeCondition" in segment) { normalized.push({ kind: "Cast", typeCondition: segment.TypeCondition }); } else { throw new Error( `Unsupported flatten path segment received from query planner: ${JSON.stringify(segment)}` ); } } return normalized; } function collectFlattenEntities(source, pathSegments, supergraphSchema) { const entities = []; const activePath = []; traverseFlattenPath( source, pathSegments, 0, supergraphSchema, activePath, (value, path) => { if (Array.isArray(value)) { for (let index = 0; index < value.length; index++) { const item = value[index]; if (item && typeof item === "object") { path.push(index); entities.push({ entity: item, path: path.slice() }); path.pop(); } } return; } if (value && typeof value === "object") { entities.push({ entity: value, path: path.slice() }); } } ); return entities; } function traverseFlattenPath(current, pathSegments, pathIndex, supergraphSchema, path, callback) { if (current == null) { return; } const segment = pathSegments[pathIndex]; if (!segment) { callback(current, path); return; } switch (segment.kind) { case "Field": { if (Array.isArray(current)) { for (const item of current) { traverseFlattenPath( item, pathSegments, pathIndex, supergraphSchema, path, callback ); } return; } if (typeof current === "object") { path.push(segment.name); const next = current[segment.name]; traverseFlattenPath( next, pathSegments, pathIndex + 1, supergraphSchema, path, callback ); path.pop(); } return; } case "List": { if (Array.isArray(current)) { for (let index = 0; index < current.length; index++) { path.push(index); traverseFlattenPath( current[index], pathSegments, pathIndex + 1, supergraphSchema, path, callback ); path.pop(); } } return; } case "Cast": { if (Array.isArray(current)) { for (const item of current) { traverseFlattenPath( item, pathSegments, pathIndex, supergraphSchema, path, callback ); } return; } if (typeof current === "object") { const value = current; const candidateTypenames = typeof value.__typename === "string" ? [value.__typename] : segment.typeCondition; if (candidateTypenames.some( (typeName) => entitySatisfiesTypeCondition( supergraphSchema, typeName, segment.typeCondition ) )) { traverseFlattenPath( current, pathSegments, pathIndex + 1, supergraphSchema, path, callback ); } } } } } function prepareFlattenContext(flattenNode, executionContext) { if (!flattenNode.node || flattenNode.node.kind !== "Fetch") { return null; } const fetchNode = flattenNode.node; const requires = fetchNode.requires; if (!requires || requires.length === 0) { return null; } const pathSegments = normalizeFlattenNodePath(flattenNode.path); const entityLocations = collectFlattenEntities( executionContext.data, pathSegments, executionContext.supergraphSchema ); if (entityLocations.length === 0) { return null; } const entityRefs = []; const entityPaths = []; const representationOrder = []; const dedupedRepresentations = []; const hashToEntry = /* @__PURE__ */ new Map(); for (const location of entityLocations) { const entityRef = location.entity; if (!isEntityRepresentation(entityRef)) { continue; } let representation = projectRequires( requires, entityRef, executionContext.supergraphSchema ); if (!representation || Array.isArray(representation)) { continue; } representation.__typename ??= entityRef.__typename; if (fetchNode.inputRewrites?.length) { representation = applyInputRewrites( representation, fetchNode.inputRewrites, executionContext.supergraphSchema ); } const hashKey = stableStringify(representation); let bucket = hashToEntry.get(hashKey); let dedupIndex; if (!bucket) { dedupIndex = dedupedRepresentations.length; const canonical = canonicalEncodeEntity(representation); hashToEntry.set(hashKey, [[canonical, dedupIndex]]); dedupedRepresentations.push(representation); } else { const canonical = canonicalEncodeEntity(representation); const existing = findInBucket(bucket, canonical); if (existing !== void 0) { dedupIndex = existing; } else { dedupIndex = dedupedRepresentations.length; bucket.push([canonical, dedupIndex]); dedupedRepresentations.push(representation); } } entityRefs.push(entityRef); entityPaths.push(location.path); representationOrder.push(dedupIndex); } if (dedupedRepresentations.length === 0) { return null; } let errorPath; for (const segment of pathSegments) { if (segment.kind === "Field") { (errorPath ??= []).push(segment.name); } } return { entityRefs, dedupedRepresentations, entityPaths, representationOrder, errorPath }; } function prepareBatchFetchContext(batchFetchNode, executionContext) { const byAlias = /* @__PURE__ */ new Map(); const representationsByVariableName = /* @__PURE__ */ new Map(); for (const alias of batchFetchNode.entityBatch.aliases) { const requires = alias.requires; if (!requires || requires.length === 0) { continue; } const pathStates = []; const entityPathsByRepresentationIndex = /* @__PURE__ */ new Map(); const representationsVariableName = alias.representationsVariableName; let variableBatchState = representationsByVariableName.get( representationsVariableName ); if (!variableBatchState) { variableBatchState = { representations: [], identityToEntityIndex: /* @__PURE__ */ new Map() }; representationsByVariableName.set( representationsVariableName, variableBatchState ); } const ensuredVariableBatchState = variableBatchState; for (const path of alias.paths) { const normalizedPath = normalizeFlattenNodePath(path); const entityLocations = collectFlattenEntities( executionContext.data, normalizedPath, executionContext.supergraphSchema ); const entityRefs = []; const entityPaths = []; const representationIndexByTarget = []; for (const location of entityLocations) { const entityRef = location.entity; if (!isEntityRepresentation(entityRef)) { continue; } let representation = projectRequires( requires, entityRef, executionContext.supergraphSchema ); if (!representation || Array.isArray(representation)) { continue; } representation.__typename ??= entityRef.__typename; if (alias.inputRewrites?.length) { representation = applyInputRewrites( representation, alias.inputRewrites, executionContext.supergraphSchema ); } const hashKey = stableStringify(representation); let bucket = ensuredVariableBatchState.identityToEntityIndex.get(hashKey); let dedupIndex; if (!bucket) { dedupIndex = ensuredVariableBatchState.representations.length; const canonical = canonicalEncodeEntity(representation); ensuredVariableBatchState.identityToEntityIndex.set(hashKey, [ [canonical, dedupIndex] ]); ensuredVariableBatchState.representations.push(representation); } else { const canonical = canonicalEncodeEntity(representation); const existing = findInBucket(bucket, canonical); if (existing !== void 0) { dedupIndex = existing; } else { dedupIndex = ensuredVariableBatchState.representations.length; bucket.push([canonical, dedupIndex]); ensuredVariableBatchState.representations.push(representation); } } entityRefs.push(entityRef); entityPaths.push(location.path); representationIndexByTarget.push(dedupIndex); let pathsForRepresentation = entityPathsByRepresentationIndex.get(dedupIndex); if (!pathsForRepresentation) { pathsForRepresentation = []; entityPathsByRepresentationIndex.set( dedupIndex, pathsForRepresentation ); } pathsForRepresentation.push(location.path); } pathStates.push({ entityRefs, entityPaths, representationIndexByTarget }); } byAlias.set(alias.alias, { alias: alias.alias, pathStates, entityPathsByRepresentationIndex, outputRewrites: alias.outputRewrites }); } let hasRepresentations = false; for (const state of representationsByVariableName.values()) { if (state.representations.length > 0) { hasRepresentations = true; break; } } if (!hasRepresentations) { return null; } return { byAlias, representationsByVariableName }; } function buildBatchFetchVariables(batchContext, selectedVariables) { let variablesForFetch; if (selectedVariables) { variablesForFetch = { ...selectedVariables }; } for (const [ variableName, state ] of batchContext.representationsByVariableName.entries()) { if (!state.representations.length) { continue; } const targetVariables = variablesForFetch ?? /* @__PURE__ */ Object.create(null); if (!variablesForFetch) { variablesForFetch = targetVariables; } targetVariables[variableName] = state.representations; } return variablesForFetch; } function applyBatchAliasEntities(returnedEntities, aliasContext) { for (const pathContext of aliasContext.pathStates) { const { entityRefs, representationIndexByTarget } = pathContext; for (let index = 0; index < entityRefs.length; index++) { const target = entityRefs[index]; const dedupIndex = representationIndexByTarget[index]; if (dedupIndex == null) { continue; } const entity = returnedEntities[dedupIndex]; if (!target || !entity) { continue; } mergeEntityPayload(target, entity); } } } function normalizeBatchFetchErrors(errors, batchContext) { if (!errors.length) { return []; } const relocated = []; for (const error of errors) { const errorPath = error.path; if (!errorPath || errorPath.length === 0) { relocated.push(error); continue; } const aliasName = errorPath[0]; if (typeof aliasName !== "string") { relocated.push(error); continue; } const aliasContext = batchContext.byAlias.get(aliasName); if (!aliasContext) { relocated.push(error); continue; } const entityIndex = errorPath[1]; if (typeof entityIndex !== "number") { relocated.push(error); continue; } const mappedPaths = aliasContext.entityPathsByRepresentationIndex.get(entityIndex); if (!mappedPaths?.length) { relocated.push(error); continue; } const tailStart = 2; const tailLen = errorPath.length - tailStart; for (const mappedPath of mappedPaths) { const newPath = new Array(mappedPath.length + tailLen); let i = 0; for (let j = 0; j < mappedPath.length; j++) { newPath[i++] = mappedPath[j]; } for (let j = tailStart; j < errorPath.length; j++) { newPath[i++] = errorPath[j]; } relocated.push(relocatedError(error, newPath)); } } return relocated; } function executeBatchFetchPlanNode(batchFetchNode, executionContext, batchContext) { const selectedVariables = selectFetchVariables( executionContext.variableValues, batchFetchNode.variableUsages ); const variablesForFetch = buildBatchFetchVariables( batchContext, selectedVariables ); const handleBatchResult = (fetchResult) => { if (isAsyncIterable(fetchResult)) { return mapAsyncIterator(fetchResult, handleBatchResult); } if (fetchResult.errors?.length) { executionContext.errors.push( ...normalizeBatchFetchErrors(fetchResult.errors, batchContext) ); } const responseData = fetchResult.data; if (!responseData || typeof responseData !== "object") { return; } for (const [aliasName, aliasContext] of batchContext.byAlias.entries()) { let aliasData = responseData[aliasName]; if (!aliasData) { continue; } if (aliasContext.outputRewrites?.length) { aliasData = applyOutputRewrites( aliasData, aliasContext.outputRewrites, executionContext.supergraphSchema ); } if (Array.isArray(aliasData)) { applyBatchAliasEntities( aliasData, aliasContext ); } } return; }; return handleMaybePromise( () => executionContext.onSubgraphExecute(batchFetchNode.serviceName, { document: getDocumentNodeOfFetchingNode(batchFetchNode), variables: variablesForFetch, operationType: batchFetchNode.operationKind ?? executionContext.operation.operation, operationName: batchFetchNode.operationName, extensions: executionContext.executionRequest.extensions, rootValue: executionContext.executionRequest.rootValue, context: executionContext.executionRequest.context, subgraphName: batchFetchNode.serviceName, info: executionContext.executionRequest.info, signal: executionContext.executionRequest.signal }), handleBatchResult ); } function executeFetchPlanNode(fetchNode, executionContext, state) { const flattenState = state?.flatten; let representationTargets = flattenState?.entityRefs ?? state?.representations; let preparedRepresentations; if (flattenState) { if (!flattenState.dedupedRepresentations.length) { return; } preparedRepresentations = flattenState.dedupedRepresentations; } else if (representationTargets?.length) { const requires = fetchNode.requires; const nextTargets = []; const payloads = []; for (const entity of representationTargets) { if (requires) { let satisfies = false; for (const requiresNode of requires) { if (entity && entitySatisfiesTypeCondition( executionContext.supergraphSchema, entity.__typename, requiresNode.kind === "InlineFragment" ? requiresNode.typeCondition : void 0 )) { satisfies = true; break; } } if (!satisfies) { continue; } } let projection = requires ? projectRequires( requires, entity, executionContext.supergraphSchema ) : entity; if (!projection || Array.isArray(projection)) { continue; } projection.__typename ??= entity.__typename; if (fetchNode.inputRewrites?.length) { projection = applyInputRewrites( projection, fetchNode.inputRewrites, executionContext.supergraphSchema ); } payloads.push(projection); nextTargets.push(entity); } if (!payloads.length) { return; } representationTargets = nextTargets; preparedRepresentations = payloads; } const selectedVariables = selectFetchVariables( executionContext.variableValues, fetchNode.variableUsages ); let variablesForFetch; if (preparedRepresentations?.length) { variablesForFetch = { representations: preparedRepresentations }; } if (selectedVariables) { variablesForFetch = variablesForFetch ? { ...selectedVariables, ...variablesForFetch } : { ...selectedVariables }; } const defaultErrorPath = state?.errorPath ?? state?.flatten?.errorPath ?? getDefaultErrorPath(fetchNode); const handleFetchResult = (fetchResult) => { if (isAsyncIterable(fetchResult)) { return mapAsyncIterator(fetchResult, handleFetchResult); } if (fetchResult.errors?.length) { const normalizedErrors = normalizeFetchErrors(fetchResult.errors, { fetchNode, state, defaultPath: defaultErrorPath }); if (normalizedErrors.length) { executionContext.errors.push(...normalizedErrors); } } const responseData = fetchNode.outputRewrites ? applyOutputRewrites( fetchResult.data, fetchNode.outputRewrites, executionContext.supergraphSchema ) : fetchResult.data; if (!responseData) { return; } if (flattenState && flattenState.entityRefs.length) { const returnedEntities = responseData._entities; if (Array.isArray(returnedEntities)) { mergeFlattenEntities(returnedEntities, flattenState); } return; } if (representationTargets?.length && responseData._entities) { const returnedEntities = responseData._entities; for (let index = 0; index < returnedEntities.length; index++) { const entity = returnedEntities[index]; const target = representationTargets[index]; if (target && entity) { mergeEntityPayload(target, entity); } } return; } mergeEntityPayload(executionContext.data, responseData); return; }; return handleMaybePromise( () => executionContext.onSubgraphExecute(fetchNode.serviceName, { document: getDocumentNodeOfFetchingNode(fetchNode), variables: variablesForFetch, operationType: fetchNode.operationKind ?? executionContext.operation.operation, operationName: fetchNode.operationName, extensions: executionContext.executionRequest.extensions, rootValue: executionContext.executionRequest.rootValue, context: executionContext.executionRequest.context, subgraphName: fetchNode.serviceName, info: executionContext.executionRequest.info, signal: executionContext.executionRequest.signal }), handleFetchResult ); } function selectFetchVariables(variableValues, variableUsages) { if (!variableValues || !variableUsages || variableUsages.length === 0) { return void 0; } const selected = /* @__PURE__ */ Object.create(null); let hasValue = false; for (const variableName of variableUsages) { if (Object.prototype.hasOwnProperty.call(variableValues, variableName)) { selected[variableName] = variableValues[variableName]; hasValue = true; } } return hasValue ? selected : void 0; } function normalizeFetchErrors(errors, options) { if (!errors.length) { return []; } const { fetchNode, state } = options; const flattenState = state?.flatten; const fallbackPath = options.defaultPath ?? getDefaultErrorPath(fetchNode); if (!flattenState) { if (!fallbackPath) { return [...errors]; } return errors.map((error) => relocatedError(error, fallbackPath)); } const entityPathMap = buildFlattenEntityPathMap(flattenState); const relocated = []; for (const error of errors) { const errorPath = error.path; if (errorPath) { const entityIndexPosition = errorPath.indexOf("_entities"); if (entityIndexPosition !== -1) { const dedupIndex = errorPath[entityIndexPosition + 1]; if (typeof dedupIndex === "number") { const mappedPaths = entityPathMap.get(dedupIndex); if (mappedPaths && mappedPaths.length) { const tailStart = entityIndexPosition + 2; const tailLen = errorPath.length - tailStart; for (const mappedPath of mappedPaths) { const newPath = new Array( mappedPath.length + tailLen ); let i = 0; for (let j = 0; j < mappedPath.length; j++) { newPath[i++] = mappedPath[j]; } for (let j = tailStart; j < errorPath.length; j++) { newPath[i++] = errorPath[j]; } relocated.push(relocatedError(error, newPath)); } continue; } } } } if (fallbackPath) { relocated.push(relocatedError(error, fallbackPath)); } else { relocated.push(error); } } return relocated; } function buildFlattenEntityPathMap(flattenState) { const map = /* @__PURE__ */ new Map(); flattenState.representationOrder.forEach((dedupIndex, index) => { const existing = map.get(dedupIndex); if (existing) { existing.push(flattenState.entityPaths[index]); } else { map.set(dedupIndex, [flattenState.entityPaths[index]]); } }); return map; } function mergeFlattenEntities(returnedEntities, flattenState) { const { entityRefs, representationOrder } = flattenState; for (let index = 0; index < entityRefs.length; index++) { const target = entityRefs[index]; const dedupIndex = representationOrder[index]; const entity = returnedEntities[dedupIndex]; if (!target || !entity) { continue; } mergeEntityPayload(target, entity); } } function applyInputRewrites(representation, rewrites, supergraphSchema) { let current = representation; for (const rewrite of rewrites) { const normalizedRewrite = normalizeRewrite(rewrite); if (!normalizedRewrite) { continue; } switch (normalizedRewrite.kind) { case "ValueSetter": current = applyValueSetter( current, normalizedRewrite.path, normalizedRewrite.setValueTo, supergraphSchema ); break; case "KeyRenamer": applyKeyRenamer( current, normalizedRewrite.path, normalizedRewrite.renameKeyTo, supergraphSchema ); break; } } return current; } function applyOutputRewrites(data, rewrites, supergraphSchema) { let current = data; if (!current) { return current; } for (const rewrite of rewrites) { const normalizedRewrite = normalizeRewrite(rewrite); if (!normalizedRewrite) { continue; } switch (normalizedRewrite.kind) { case "KeyRenamer": applyKeyRenamer( current, normalizedRewrite.path, normalizedRewrite.renameKeyTo, supergraphSchema ); break; case "ValueSetter": current = applyValueSetter( current, normalizedRewrite.path, normalizedRewrite.setValueTo, supergraphSchema ); break; } } return current; } const getDocumentNodeOfFetchingNode = memoize1( function getDocumentNodeOfFetchNode(fetchingNode) { const doc = parse(fetchingNode.operation, { noLocation: true }); documentStringMap.set(doc, fetchingNode.operation); return doc; } ); const getDefaultErrorPath = memoize1(function getDefaultErrorPath2(fetchNode) { const document = getDocumentNodeOfFetchingNode(fetchNode); const operationAst = getOperationASTFromDocument( document, fetchNode.operationName ); if (!operationAst) { return []; } const rootSelection = operationAst.selectionSet.selections.find( (selection) => selection.kind === Kind.FIELD ); if (!rootSelection) { return []; } const responseKey = rootSelection.alias?.value ?? rootSelection.name.value; return responseKey ? [responseKey] : []; }); function stableStringify(value) { return hashValueOrderIndependent32Ultra(value); } function hasOwnProperties(obj) { for (const _key in obj) { if (Object.prototype.hasOwnProperty.call(obj, _key)) { return true; } } return false; } function findInBucket(bucket, canonical) { for (let i = 0; i < bucket.length; i++) { if (bucket[i][0] === canonical) { return bucket[i][1]; } } return void 0; } function canonicalEncodeEntity(value) { if (value === null) { return "N"; } const t = typeof value; if (t === "string") { const v = value; return `s${v.length}:${v}`; } if (t === "number") { const n = value; if (!Number.isFinite(n)) { return `n~${n}`; } return `n${Object.is(n, -0) ? 0 : n}`; } if (t === "boolean") { return value ? "b1" : "b0"; } if (Array.isArray(value)) { const arr = value; let result = `A${arr.length}[`; for (let i = 0; i < arr.length; i++) { if (i > 0) result += ","; result += canonicalEncodeEntity(arr[i]); } return result + "]"; } if (t === "object") { const obj = value; const sortedKeys = Object.keys(obj).sort(); const definedKeys = []; for (let i = 0; i < sortedKeys.length; i++) { const k = sortedKeys[i]; if (obj[k] !== void 0) { definedKeys.push(k); } } let result = `O${definedKeys.length}{`; for (let i = 0; i < definedKeys.length; i++) { const k = definedKeys[i]; result += `k${k.length}:${k}=${canonicalEncodeEntity(obj[k])}`; } return result + "}"; } try { return `X${JSON.stringify(value)}`; } catch { return `X${String(value)}`; } } function executePlanNode(planNode, executionContext, state) { switch (planNode.kind) { case "Sequence": { let pending = null; let nextState = state; for (const node of planNode.nodes) { const currentState = nextState; nextState = void 0; pending = handleMaybePromise( () => pending, () => executePlanNode(node, executionContext, currentState) ); } return pending; } case "Parallel": { const promises = []; planNode.nodes.forEach((node, index) => { const maybePromise = executePlanNode( node, executionContext, index === 0 ? state : void 0 ); if (isPromise(maybePromise)) { promises.push(maybePromise); } }); if (promises.length === 1) { return promises[0]; } if (promises.length === 0) { return; } return Promise.all(promises); } case "Flatten": { const flattenContext = prepareFlattenContext(planNode, executionContext); if (!flattenContext) { return; } const errorPath = flattenContext.errorPath && flattenContext.errorPath.length ? [...flattenContext.errorPath] : void 0; return executePlanNode(planNode.node, executionContext, { representations: flattenContext.entityRefs, errorPath, flatten: flattenContext }); } case "Fetch": { return executeFetchPlanNode(planNode, executionContext, state); } case "BatchFetch": { const batchContext = prepareBatchFetchContext(planNode, executionContext); if (!batchContext) { return; } return executeBatchFetchPlanNode( planNode, executionContext, batchContext ); } case "Condition": { const conditionValue = executionContext.variableValues?.[planNode.condition]; if (conditionValue === true) { if (planNode.ifClause) { return executePlanNode(planNode.ifClause, executionContext); } } else if (planNode.elseClause) { return executePlanNode(planNode.elseClause, executionContext); } break; } case "Subscription": { return executePlanNode(planNode.primary, executionContext); } default: throw new Error(`Invalid plan node: ${JSON.stringify(planNode)}`); } } function normalizeRewrite(rewrite) { if ("kind" in rewrite && rewrite.kind === "ValueSetter") { return { ...rewrite, path: normalizeRewritePath(rewrite.path) }; } if ("ValueSetter" in rewrite) { return { kind: "ValueSetter", path: normalizeRewritePath(rewrite.ValueSetter?.path), setValueTo: rewrite.ValueSetter?.setValueTo }; } if ("KeyRenamer" in rewrite) { return { kind: "KeyRenamer", path: normalizeRewritePath(rewrite.KeyRenamer?.path), renameKeyTo: rewrite.KeyRenamer?.renameKeyTo }; } throw new Error(`Unsupported fetch node rewrite: ${JSON.stringify(rewrite)}`); } function normalizeRewritePath(path) { const normalized = []; for (const segment of path) { if ("TypenameEquals" in segment) { normalized.push(`... on ${segment.TypenameEquals}`); } else if ("Key" in segment) { normalized.push(segment.Key); } else { throw new Error( `Unsupported fetch node path segment: ${JSON.stringify(segment)}` ); } } return normalized; } function applyKeyRenamer(entityRepresentation, path, renameKeyTo, supergraphSchema) { const keyProp = path[0]; if (!keyProp) { throw new Error("Invalid key prop"); } const nextPath = path.slice(1); if (keyProp.startsWith("... on")) { const typeCondition = keyProp.split("... on ")[1]; if (isEntityRepresentation(entityRepresentation) && typeCondition && !entitySatisfiesTypeCondition( supergraphSchema, entityRepresentation.__typename, typeCondition )) { return; } return applyKeyRenamer( entityRepresentation, nextPath, renameKeyTo, supergraphSchema ); } if (path.length === 1) { entityRepresentation[renameKeyTo] = entityRepresentation[keyProp]; delete entityRepresentation[keyProp]; return; } const nextData = entityRepresentation[keyProp]; if (nextData == null) { return; } if (Array.isArray(nextData)) { return nextData.map( (item) => applyKeyRenamer(item, nextPath, renameKeyTo, supergraphSchema) ); } return applyKeyRenamer(nextData, nextPath, renameKeyTo, supergraphSchema); } function applyValueSetter(data, path, setValueTo, supergraphSchema) { if (Array.isArray(data)) { return data.map( (item) => applyValueSetter(item, path, setValueTo, supergraphSchema) ); } const keyProp = path[0]; if (!keyProp) { return setValueTo; } const nextPath = path.slice(1); if (keyProp.startsWith("... on")) { const typeCondition = keyProp.split("... on ")[1]; if (!typeCondition) { throw new Error("Invalid type condition"); } if (isEntityRepresentation(data) && !entitySatisfiesTypeCondition( supergraphSchema, data.__typename, typeCondition )) { return data; } return applyValueSetter(data, nextPath, setValueTo, supergraphSchema); } if (path.length === 1) { const existingValue = data[keyProp]; if (existingValue === setValueTo) { return data; } return { ...data, [keyProp]: setValueTo }; } const nextData = data[keyProp]; if (nextData == null) { return data; } return { ...data, [keyProp]: applyValueSetter( nextData, nextPath, setValueTo, supergraphSchema ) }; } function entitySatisfiesTypeCondition(supergraphSchema, typeNameInEntity, typeConditionInInlineFragment) { if (!typeConditionInInlineFragment) { return false; } const normalizedTypeConditions = Array.isArray(typeConditionInInlineFragment) ? typeConditionInInlineFragment : [typeConditionInInlineFragment]; if (normalizedTypeConditions.includes(typeNameInEntity)) { return true; } const entityType = supergraphSchema.getType(typeNameInEntity); if (!isObjectType(entityType)) { return false; } for (const typeCondition of normalizedTypeConditions) { const conditionType = supergraphSchema.getType(typeCondition); if (isAbstractType(conditionType) && supergraphSchema.isSubType(conditionType, entityType)) { return true; } } return false; } function projectSelectionSet(data, selectionSet, type, executionContext) { if (data == null) { return data; } if (Array.isArray(data)) { return data.map( (item) => projectSelectionSet(item, selectionSet, type, executionContext) ); } const parentType = isEntityRepresentation(data) ? executionContext.supergraphSchema.getType(data.__typename) : type; if (!isObjectType(parentType) && !isInterfaceType(parentType)) { return null; } if (isObjectType(parentType) && isInaccessibleObjectType(parentType, executionContext)) { return null; } const result = {}; for (const selection of selectionSet.selections) { if (!shouldIncludeCompiledSelection( selection.directiveConditions, executionContext.variableValues )) { continue; } if (selection.kind === "Field") { const fieldMeta = getProjectionFieldMeta( parentType, selection.fieldName, executionContext ); if (!fieldMeta) { throw new Error( `Field not found: ${selection.fieldName} on ${parentType.name}` ); } const responseKey = selection.responseKey; let projectedValue = selection.selectionSet ? projectSelectionSet( data[responseKey], selection.selectionSet, fieldMeta.namedType, executionContext ) : data[responseKey]; if (projectedValue !== void 0) { if (fieldMeta.enumType) { projectedValue = projectEnumValue( projectedValue, fieldMeta.enumType, executionContext ); } if (result[responseKey] == null) { result[responseKey] = projectedValue; } else if (typeof result[responseKey] === "object" && projectedValue != null) { result[responseKey] = mergeProjectedFieldValue( result[responseKey], projectedValue ); } else { result[responseKey] = projectedValue; } } else if (fieldMeta.field.name === "__typename") { result[responseKey] = type.name; } else if (fieldMeta.isNonNull) { return null; } else { result[responseKey] = null; } } else if (selection.kind === "InlineFragment") { const typeCondition = selection.typeCondition; if (isEntityRepresentation(data)) { if (typeCondition && !entitySatisfiesTypeConditionCached( executionContext, data.__typename, typeCondition )) { continue; } const typeByTypename = executionContext.supergraphSchema.getType( data.__typename ); if (!isOutputType(typeByTypename)) { throw new Error("Invalid type"); } const projectedValue = projectSelectionSet( data, selection.selectionSet, typeByTypename, executionContext ); mergeProjectedSelectionObject(result, projectedValue); } else { if (typeCondition && !entitySatisfiesTypeConditionCached( executionContext, parentType.name, typeCondition )) { continue; } const projectedValue = projectSelectionSet( data, selection.selectionSet, typeCondition ? executionContext.supergraphSchema.getType(typeCondition) : parentType, executionContext ); mergeProjectedSelectionObject(result, projectedValue); } } else if (selection.kind === "FragmentSpread") { const fragment = executionContext.compiledProjection.fragments[selection.fragmentName]; if (!fragment) { throw new Error(`Fragment "${selection.fragmentName}" not found`); } const typeCondition = fragment.typeCondition; if (isEntityRepresentation(data) && typeCondition && !entitySatisfiesTypeConditionCached( executionContext, data.__typename, typeCondition )) { continue; } const typeByTypename = executionContext.supergraphSchema.getType( data.__typename || typeCondition ); if (!isOutputType(typeByTypename)) { throw new Error("Invalid type"); } const projectedValue = projectSelectionSet( data, fragment.selectionSet, typeByTypename, executionContext ); mergeProjectedSelectionObject(result, projectedValue); } } return result; } function projectDataByOperation(executionContext) { const rootType = executionContext.supergraphSchema.getRootType( executionContext.operation.operation ); if (!rootType) { throw new Error("Root type not found"); } return projectSelectionSet( executionContext.data, executionContext.compiledProjection.rootSelectionSet, rootType, executionContext ); } function projectRequires(requiresSelections, entity, supergraphSchema) { if (!entity) { return entity; } const runtime = { supergraphSchema, entityTypeConditionResult: /* @__PURE__ */ new Map() }; const compiledRequiresSelections = getOrCompileRequiresSelections(requiresSelections); return projectRequiresCompiled(compiledRequiresSelections, entity, runtime); } function projectRequiresCompiled(requiresSelections, entity, runtime) { if (!entity) { return entity; } if (Array.isArray(entity)) { return entity.map( (item) => projectRequiresCompiled(requiresSelections, item, runtime) ); } let result = null; for (const requiresSelection of requiresSelections) { switch (requiresSelection.kind) { case "Field": { let original = entity[requiresSelection.fieldName]; if (original === void 0) { original = entity[requiresSelection.responseKey]; } const projectedValue = requiresSelection.selections ? projectRequiresCompiled( requiresSelection.selections, original, runtime ) : original; if (projectedValue != null) { result ??= {}; result[requiresSelection.responseKey] = projectedValue; } break; } case "InlineFragment": { if (entitySatisfiesTypeConditionForRequires( runtime, entity.__typename, requiresSelection.typeCondition )) { const projected = projectRequiresCompiled( requiresSelection.selections, entity, runtime ); if (projected) { result ??= {}; mergeEntityPayload(result, projected); } } break; } } } if (!result) { return null; } for (const key in result) { if (key !== "__typename") { return result; } } return null; } function entitySatisfiesTypeConditionForRequires(runtime, typeNameInEntity, typeConditionInInlineFragment) { if (!typeConditionInInlineFragment) { return false; } const typeConditionCacheKey = Array.isArray(typeConditionInInlineFragment) ? typeConditionInInlineFragment.join(",") : typeConditionInInlineFragment; const cacheKey = `${typeNameInEntity}::${typeConditionCacheKey}`; const cachedResult = runtime.entityTypeConditionResult.get(cacheKey); if (cachedResult != null) { return cachedResult; } const result = entitySatisfiesTypeCondition( runtime.supergraphSchema, typeNameInEntity, typeConditionInInlineFragment ); runtime.entityTypeConditionResult.set(cacheKey, result); return result; } function getOperationProjectionCacheKey(executionRequest) { return executionRequest.operationName ?? null; } function getOrCreateCompiledProjectionArtifacts(executionRequest) { let artifactsByOperation = projectionArtifactsByDocument.get( executionRequest.document ); if (!artifactsByOperation) { artifactsByOperation = /* @__PURE__ */ new Map(); projectionArtifactsByDocument.set( executionRequest.document, artifactsByOperation ); } const cacheKey = getOperationProjectionCacheKey(executionRequest); const cachedArtifacts = artifactsByOperation.get(cacheKey); if (cachedArtifacts) { return cachedArtifacts; } const fragments = getFragmentsFromDocument(executionRequest.document); const operation = getOperationASTFromRequest(executionRequest); const artifacts = { operation, fragments, compiledProjection: compileProjectionPlan(operation, fragments) }; artifactsByOperation.set(cacheKey, artifacts); return artifacts; } function getOrCompileRequiresSelections(requiresSelections) { const cached = compiledRequiresCache.get(requiresSelections); if (cached) { return cached; } const compiled = compileRequiresSelections(requiresSelections); compiledRequiresCache.set(requiresSelections, compiled); return compiled; } function compileRequiresSelections(requiresSelections) { const compiled = []; for (const requiresSelection of requiresSelections) { switch (requiresSelection.kind) { case "Field": { compiled.push({ kind: "Field", fieldName: requiresSelection.name, responseKey: requiresSelection.alias ?? requiresSelection.name, selections: requiresSelection.selections ? compileRequiresSelections(requiresSelection.selections) : void 0 }); break; } case "InlineFragment": { compiled.push({ kind: "InlineFragment", typeCondition: requiresSelection.typeCondition, selections: compileRequiresSelections(requiresSelection.selections) }); break; } default: throw new Error( `Unsupported requires selection kind: ${requiresSelection.kind}` ); } } return compiled; } function compileProjectionPlan(operation, fragments) { const compiledFragments = {}; for (const fragmentName in fragments) { const fragment = fragments[fragmentName]; if (!fragment) { continue; } compiledFragments[fragmentName] = { typeCondition: fragment.typeCondition?.name.value, selectionSet: compileProjectionSelectionSet(fragment.selectionSet) }; } return { rootSelectionSet: compileProjectionSelectionSet(operation.selectionSet), fragments: compiledFragments }; } function compileProjectionSelectionSet(selectionSet) { const selections = []; for (const selection of selectionSet.selections) { const directiveConditions = compileDirectiveConditions( selection.directives ); if (selection.kind === Kind.FIELD) { selections.push({ kind: "Field", fieldName: selection.name.value, responseKey: selection.alias?.value || selection.name.value, directiveConditions, selectionSet: selection.selectionSet ? compileProjectionSelectionSet(selection.selectionSet) : void 0 }); continue; } if (selection.kind === Kind.INLINE_FRAGMENT) { selections.push({ kind: "InlineFragment", typeCondition: selection.typeCondition?.name.value, directiveConditions, selectionSet: compileProjectionSelectionSet(selection.selectionSet) }); continue; } if (selection.kind === Kind.FRAGMENT_SPREAD) { selections.push({ kind: "FragmentSpread", fragmentName: selection.name.value, directiveConditions }); continue; } assertNever(); } return { selections }; } function compileDirectiveConditions(directives) { if (!directives?.length) { return void 0; } const conditions = []; for (const directiveNode of directives) { const directiveName = directiveNode.name.value; if (directiveName !== "skip" && directiveName !== "include") { continue; } const ifArg = directiveNode.arguments?.find( (arg) => arg.name.value === "if" ); if (!ifArg) { conditions.push({ kind: "AlwaysExclude" }); continue; } const ifValueNode = ifArg.value; if (ifValueNode.kind === Kind.VARIABLE) { conditions.push( directiveName === "skip" ? { kind: "SkipIf", variableName: ifValueNode.name.value } : { kind: "IncludeIf", variableName: ifValueNode.name.value } ); continue; } if (ifValueNode.kind === Kind.BOOLEAN) { conditions.push( directiveName === "skip" ? { kind: "SkipIf", value: ifValueNode.value } : { kind: "IncludeIf", value: ifValueNode.value } ); } } return conditions.length ? conditions : void 0; } function shouldIncludeCompiledSelection(directiveConditions, variableValues) { if (!directiveConditions?.length) { return true; } for (const condition of directiveConditions) { switch (condition.kind) { case "AlwaysExclude": return false; case "SkipIf": { const ifValue = "variableName" in condition ? variableValues?.[condition.variableName] : condition.value; if (ifValue) { return false; } break; } case "IncludeIf": { const ifValue = "variableName" in condition ? variableValues?.[condition.variableName] : condition.value; if (!ifValue) { return false; } break; } default: assertNever(); } } return true; } function getProjectionFieldMeta(parentType, fieldName, executionContext) { let byFieldName = executionContext.projectionRuntimeCache.fieldMetaByParentType.get( parentType ); if (!byFieldName) { byFieldName = /* @__PURE__ */ new Map(); executionContext.projectionRuntimeCache.fieldMetaByParentType.set( parentType, byFieldName ); } if (byFieldName.has(fieldName)) { return byFieldName.get(fieldName) || null; } const field = fieldName === "__typename" ? TypeNameMetaFieldDef : parentType.getFields()[fieldName]; if (!field) { byFieldName.set(fieldName, null); return null;