UNPKG

@graphql-hive/router-runtime

Version:
1,166 lines (1,161 loc) 36.1 kB
import { handleFederationSupergraph } from '@graphql-mesh/fusion-runtime'; import { createDefaultExecutor } from '@graphql-mesh/transport-common'; import { defaultPrintFn } from '@graphql-tools/executor-common'; import { filterInternalFieldsAndTypes } from '@graphql-tools/federation'; import { handleMaybePromise, mapAsyncIterator, isPromise } from '@whatwg-node/promise-helpers'; import { isObjectType, isAbstractType, Kind, parse, isInterfaceType, TypeNameMetaFieldDef, getNamedType, isEnumType, isOutputType, visit, BREAK } from 'graphql'; import { documentStringMap } from '@envelop/core'; import { getFragmentsFromDocument, getVariableValues } from '@graphql-tools/executor'; import { isAsyncIterable, getOperationASTFromRequest, memoize1, mergeDeep, getOperationASTFromDocument, relocatedError } from '@graphql-tools/utils'; 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 (Object.keys(executionContext.data).length > 0) { 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 = {}; function createQueryPlanExecutionContext({ supergraphSchema, executionRequest, onSubgraphExecute }) { const fragments = getFragmentsFromDocument(executionRequest.document); const operation = getOperationASTFromRequest(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 }; } 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 ("Cast" in segment) { normalized.push({ kind: "Cast", typeCondition: segment.Cast }); } 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, 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, remainingPath, supergraphSchema, path, callback) { if (current == null) { return; } const [segment, ...rest] = remainingPath; if (!segment) { callback(current, path); return; } switch (segment.kind) { case "Field": { if (Array.isArray(current)) { for (const item of current) { traverseFlattenPath( item, remainingPath, supergraphSchema, path, callback ); } return; } if (typeof current === "object" && current !== null) { path.push(segment.name); const next = current[segment.name]; traverseFlattenPath(next, rest, 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], rest, supergraphSchema, path, callback ); path.pop(); } } return; } case "Cast": { if (Array.isArray(current)) { for (const item of current) { traverseFlattenPath( item, remainingPath, supergraphSchema, path, callback ); } return; } if (typeof current === "object" && current !== null) { const candidate = current; const typename = typeof candidate.__typename === "string" ? candidate.__typename : segment.typeCondition; if (entitySatisfiesTypeCondition( supergraphSchema, typename, segment.typeCondition )) { traverseFlattenPath(current, rest, 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 representationKeyToIndex = /* @__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 dedupeKey = stableStringify(representation); let dedupIndex = representationKeyToIndex.get(dedupeKey); if (dedupIndex === void 0) { dedupIndex = dedupedRepresentations.length; representationKeyToIndex.set(dedupeKey, dedupIndex); dedupedRepresentations.push(representation); } entityRefs.push(entityRef); entityPaths.push(location.path); representationOrder.push(dedupIndex); } if (dedupedRepresentations.length === 0) { return null; } const errorPath = pathSegments.filter( (segment) => segment.kind === "Field" ).map((segment) => segment.name); return { entityRefs, dedupedRepresentations, entityPaths, representationOrder, errorPath: errorPath.length ? errorPath : void 0 }; } 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; if (requires && representationTargets.length) { representationTargets = representationTargets.filter( (entity) => requires.some( (requiresNode) => entity && entitySatisfiesTypeCondition( executionContext.supergraphSchema, entity.__typename, requiresNode.kind === "InlineFragment" ? requiresNode.typeCondition : void 0 ) ) ); } if (!representationTargets || representationTargets.length === 0) { return; } const nextTargets = []; const payloads = []; for (const entity of representationTargets) { let projection = fetchNode.requires ? projectRequires( fetchNode.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) { Object.assign(target, mergeDeep([target, entity], false, true, true)); } } return; } Object.assign( executionContext.data, mergeDeep([executionContext.data, responseData], false, true, true) ); return; }; return handleMaybePromise( () => executionContext.onSubgraphExecute(fetchNode.serviceName, { document: getDocumentNodeOfFetchNode(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 tail = errorPath.slice(entityIndexPosition + 2); for (const mappedPath of mappedPaths) { relocated.push(relocatedError(error, [...mappedPath, ...tail])); } 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; } Object.assign(target, mergeDeep([target, entity], false, true, true)); } } 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 getDocumentNodeOfFetchNode = memoize1(function getDocumentNodeOfFetchNode2(fetchNode) { const doc = parse(fetchNode.operation, { noLocation: true }); documentStringMap.set(doc, fetchNode.operation); return doc; }); const getDefaultErrorPath = memoize1(function getDefaultErrorPath2(fetchNode) { const document = getDocumentNodeOfFetchNode(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; const path = responseKey ? [responseKey] : void 0; return path ?? []; }); function stableStringify(value) { if (value === null) { return "null"; } const type = typeof value; if (type === "number" || type === "boolean") { return JSON.stringify(value); } if (type === "string") { return JSON.stringify(value); } if (Array.isArray(value)) { return `[${value.map((item) => stableStringify(item)).join(",")}]`; } if (type === "object") { const entries = Object.entries(value).filter(([, entryValue]) => entryValue !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map( ([key, entryValue]) => `${JSON.stringify(key)}:${stableStringify(entryValue)}` ); return `{${entries.join(",")}}`; } return JSON.stringify(null); } 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 "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 nextData; } return { ...data, [keyProp]: applyValueSetter( nextData, nextPath, setValueTo, supergraphSchema ) }; } function entitySatisfiesTypeCondition(supergraphSchema, typeNameInEntity, typeConditionInInlineFragment) { if (!typeConditionInInlineFragment) { return false; } if (typeNameInEntity === typeConditionInInlineFragment) { return true; } const conditionType = supergraphSchema.getType(typeConditionInInlineFragment); const entityType = supergraphSchema.getType(typeNameInEntity); return isObjectType(entityType) && isAbstractType(conditionType) && supergraphSchema.isSubType(conditionType, entityType); } 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)) { const inaccessibleDirective = parentType.astNode?.directives?.find( (directive) => directive.name.value === "inaccessible" ); if (inaccessibleDirective) { return null; } } const result = {}; selectionLoop: for (const selection of selectionSet.selections) { if (selection.directives?.length) { for (const directiveNode of selection.directives) { const ifArg = directiveNode.arguments?.find( (arg) => arg.name.value === "if" ); if (directiveNode.name.value === "skip") { if (ifArg) { const ifValueNode = ifArg.value; if (ifValueNode.kind === Kind.VARIABLE) { const variableName = ifValueNode.name.value; if (executionContext.variableValues?.[variableName]) { continue selectionLoop; } } else if (ifValueNode.kind === Kind.BOOLEAN) { if (ifValueNode.value) { continue selectionLoop; } } } else { continue selectionLoop; } } if (directiveNode.name.value === "include") { if (ifArg) { const ifValueNode = ifArg.value; if (ifValueNode.kind === Kind.VARIABLE) { const variableName = ifValueNode.name.value; if (!executionContext.variableValues?.[variableName]) { continue selectionLoop; } } else if (ifValueNode.kind === Kind.BOOLEAN) { if (!ifValueNode.value) { continue selectionLoop; } } } else { continue selectionLoop; } } } } if (selection.kind === "Field") { const field = selection.name.value === "__typename" ? TypeNameMetaFieldDef : parentType.getFields()[selection.name.value]; if (!field) { throw new Error( `Field not found: ${selection.name.value} on ${parentType.name}` ); } const fieldType = getNamedType(field.type); const responseKey = selection.alias?.value || selection.name.value; let projectedValue = selection.selectionSet ? projectSelectionSet( data[responseKey], selection.selectionSet, fieldType, executionContext ) : data[responseKey]; if (projectedValue !== void 0) { if (isEnumType(fieldType)) { const enumValue = fieldType.getValue(projectedValue); if (!enumValue) { projectedValue = null; } else { const inaccessibleDirective = enumValue.astNode?.directives?.find( (directive) => directive.name.value === "inaccessible" ); if (inaccessibleDirective) { projectedValue = null; } } } if (result[responseKey] == null) { result[responseKey] = projectedValue; } else if (typeof result[responseKey] === "object" && projectedValue != null) { result[responseKey] = Object.assign( result[responseKey], mergeDeep(result[responseKey], projectedValue) ); } else { result[responseKey] = projectedValue; } } else if (field.name === "__typename") { result[responseKey] = type.name; } else { result[responseKey] = null; } } else if (selection.kind === "InlineFragment") { const typeCondition = selection.typeCondition?.name.value; if (isEntityRepresentation(data)) { if (typeCondition && !entitySatisfiesTypeCondition( executionContext.supergraphSchema, 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 ); if (projectedValue != null) { Object.assign( result, mergeDeep([result, projectedValue], false, true, true) ); } } else { if (typeCondition && !entitySatisfiesTypeCondition( executionContext.supergraphSchema, parentType.name, typeCondition )) { continue; } const projectedValue = projectSelectionSet( data, selection.selectionSet, typeCondition ? executionContext.supergraphSchema.getType(typeCondition) : parentType, executionContext ); if (projectedValue != null) { Object.assign( result, mergeDeep([result, projectedValue], false, true, true) ); } } } else if (selection.kind === "FragmentSpread") { const fragment = executionContext.fragments[selection.name.value]; if (!fragment) { throw new Error(`Fragment "${selection.name.value}" not found`); } const typeCondition = fragment.typeCondition?.name.value; if (isEntityRepresentation(data) && typeCondition && !entitySatisfiesTypeCondition( executionContext.supergraphSchema, 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 ); if (projectedValue != null) { Object.assign( result, mergeDeep([result, projectedValue], false, true, true) ); } } } 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.operation.selectionSet, rootType, executionContext ); } function projectRequires(requiresSelections, entity, supergraphSchema) { if (!entity) { return entity; } if (Array.isArray(entity)) { return entity.map( (item) => projectRequires(requiresSelections, item, supergraphSchema) ); } const result = {}; for (const requiresSelection of requiresSelections) { switch (requiresSelection.kind) { case "Field": { const fieldName = requiresSelection.name; const responseKey = requiresSelection.alias ?? fieldName; let original = entity[fieldName]; if (original === void 0) { original = entity[responseKey]; } const projectedValue = requiresSelection.selections ? projectRequires( requiresSelection.selections, original, supergraphSchema ) : original; if (projectedValue != null) { result[responseKey] = projectedValue; } break; } case "InlineFragment": if (entitySatisfiesTypeCondition( supergraphSchema, entity.__typename, requiresSelection.typeCondition )) { const projected = projectRequires( requiresSelection.selections, entity, supergraphSchema ); if (projected) { Object.assign( result, mergeDeep([result, projected], false, true, true) ); } } break; } } if (Object.keys(result).length === 1 && result.__typename || Object.keys(result).length === 0) { return null; } return result; } function getLazyValue(factory) { let _value; return function() { if (_value == null) { _value = factory(); } return _value; }; } function getLazyFactory(factory) { let _value; return function(...args) { if (!_value) { _value = factory(); } return _value(...args); }; } function unifiedGraphHandler(opts) { const getSubgraphSchema = getLazyFactory( () => handleFederationSupergraph(opts).getSubgraphSchema ); const getQueryPlanner = getLazyValue(() => { const moduleName = "@graphql-hive/router-query-planner"; const supergraphSdl = opts.getUnifiedGraphSDL(); return import(moduleName).then( ({ QueryPlanner }) => new QueryPlanner(supergraphSdl) ); }); const supergraphSchema = filterInternalFieldsAndTypes(opts.unifiedGraph); const defaultExecutor = getLazyFactory( () => createDefaultExecutor(supergraphSchema) ); const documentOperationPlanCache = /* @__PURE__ */ new WeakMap(); function planDocument(document, operationName) { let operationCache = documentOperationPlanCache.get(document); if (operationCache) { const plan2 = operationCache.get(operationName); if (plan2) { return plan2; } } else { operationCache = /* @__PURE__ */ new Map(); documentOperationPlanCache.set(document, operationCache); } const plan = handleMaybePromise( getQueryPlanner, (qp) => qp.plan(defaultPrintFn(document), operationName).then((queryPlan) => { operationCache.set(operationName, queryPlan); return queryPlan; }) ); operationCache.set(operationName, plan); return plan; } return { unifiedGraph: supergraphSchema, getSubgraphSchema, executor(executionRequest) { if (isIntrospection(executionRequest.document)) { return defaultExecutor(executionRequest); } return handleMaybePromise( () => planDocument( executionRequest.document, executionRequest.operationName || null ), (queryPlan) => executeQueryPlan({ supergraphSchema, executionRequest, onSubgraphExecute: opts.onSubgraphExecute, queryPlan }) ); } }; } function isIntrospection(document) { let onlyQueryTypenameFields = false; let containsIntrospectionField = false; visit(document, { // @ts-expect-error we dont have to return anything aside from break OperationDefinition(node) { for (const sel of node.selectionSet.selections) { if (sel.kind !== "Field") return BREAK; if (sel.name.value === "__schema" || sel.name.value === "__type") { containsIntrospectionField = true; return BREAK; } if (sel.name.value === "__typename") { onlyQueryTypenameFields = true; } else { onlyQueryTypenameFields = false; return BREAK; } } } }); return containsIntrospectionField || onlyQueryTypenameFields; } export { unifiedGraphHandler };