UNPKG

@graphql-hive/router-runtime

Version:
1,591 lines (1,588 loc) • 64.6 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 { handleMaybePromise, mapAsyncIterator, isPromise } from '@whatwg-node/promise-helpers'; import { parse, Kind, isObjectType, isInterfaceType, TypeNameMetaFieldDef, getNamedType, isEnumType, isNonNullType, isOutputType, isAbstractType, GraphQLError, visit, visitWithTypeInfo, BREAK, valueFromASTUntyped } from 'graphql'; import { documentStringMap } from '@envelop/core'; import { getFragmentsFromDocument, getVariableValues } from '@graphql-tools/executor'; import { isAsyncIterable, getOperationASTFromRequest, memoize1, getOperationASTFromDocument, mergeDeep, createGraphQLError, relocatedError, getDirective, getDirectiveExtensions, parseSelectionSet, getDirectiveInExtensions, memoize2, asArray, visitResult } from '@graphql-tools/utils'; import { getResolverForPubSubOperation } from '@graphql-mesh/utils'; import { getTypeInfo } from '@graphql-tools/delegate'; const queryPlanForExecutionRequestContext = /* @__PURE__ */ new WeakMap(); 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 onSubgraphExecuteWithTransforms(subgraphName, executionRequest, onSubgraphExecute, getSubschema) { const subschema = getSubschema(subgraphName); if (subschema.transforms?.length) { const transforms = subschema.transforms; const transformationContext = /* @__PURE__ */ Object.create(null); const delegationContext = void 0; for (const transform of transforms) { if (transform.transformRequest) { executionRequest = transform.transformRequest( executionRequest, delegationContext, transformationContext ); } } return handleMaybePromiseMaybeAsyncIterable( () => onSubgraphExecute(subgraphName, executionRequest), (executionResult) => { for (const transform of transforms.toReversed()) { if (transform.transformResult) { executionResult = transform.transformResult( executionResult, delegationContext, transformationContext ); } } return executionResult; } ); } return onSubgraphExecute(subgraphName, executionRequest); } function handleMaybePromiseMaybeAsyncIterable(executor, mapper, errorMapper) { return handleMaybePromise( executor, (result$) => { if (isAsyncIterable(result$)) { return mapAsyncIterator(result$, mapper, errorMapper); } return mapper(result$); }, errorMapper ); } 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 }); return handleMaybePromiseMaybeAsyncIterable( () => executePlanNode(node, executionContext), () => { const executionResult = {}; if (Object.keys(executionContext.data).length > 0) { executionResult.data = projectDataByOperation(executionContext); } if (executionContext.errors.length > 0) { executionResult.errors = executionContext.errors; } return executionResult; } ); } 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 ("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, 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") { 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") { const value = current; const candidateTypenames = typeof value.__typename === "string" ? [value.__typename] : segment.typeCondition; if (candidateTypenames.some( (typeName) => 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 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 ); } 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 identity = stableStringify(representation); let dedupIndex = variableBatchState.identityToEntityIndex.get(identity); if (dedupIndex == null) { dedupIndex = variableBatchState.representations.length; variableBatchState.identityToEntityIndex.set(identity, dedupIndex); variableBatchState.representations.push(representation); } entityRefs.push(entityRef); entityPaths.push(location.path); representationIndexByTarget.push(dedupIndex); const pathsForRepresentation = entityPathsByRepresentationIndex.get(dedupIndex) ?? []; pathsForRepresentation.push(location.path); entityPathsByRepresentationIndex.set( dedupIndex, pathsForRepresentation ); } pathStates.push({ entityRefs, entityPaths, representationIndexByTarget }); } byAlias.set(alias.alias, { alias: alias.alias, pathStates, entityPathsByRepresentationIndex, outputRewrites: alias.outputRewrites }); } const hasRepresentations = Array.from( representationsByVariableName.values() ).some((state) => state.representations.length > 0); 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; } Object.assign(target, mergeDeep([target, entity], false, true, true)); } } } 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 tail = errorPath.slice(2); for (const mappedPath of mappedPaths) { relocated.push(relocatedError(error, [...mappedPath, ...tail])); } } return relocated; } function executeBatchFetchPlanNode(batchFetchNode, executionContext, batchContext) { const selectedVariables = selectFetchVariables( executionContext.variableValues, batchFetchNode.variableUsages ); const variablesForFetch = buildBatchFetchVariables( batchContext, selectedVariables ); return handleMaybePromiseMaybeAsyncIterable( () => 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 }), (fetchResult) => { 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; }, (error) => handleSubgraphExecutionError( error, executionContext, batchFetchNode.serviceName ) ); } 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); return handleMaybePromiseMaybeAsyncIterable( () => 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 }), (fetchResult) => { 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; } if (executionContext.operation.operation === "subscription") { executionContext.data = responseData; } else { Object.assign( executionContext.data, mergeDeep([executionContext.data, responseData], false, true, true) ); } return; }, (error) => handleSubgraphExecutionError( error, executionContext, fetchNode.serviceName, defaultErrorPath ) ); } function handleSubgraphExecutionError(error, executionContext, serviceName, path) { let graphQLError; if (error instanceof GraphQLError) { graphQLError = error; } else { graphQLError = createGraphQLError(error.message, { originalError: error, extensions: { code: "DOWNSTREAM_SERVICE_ERROR", serviceName }, path }); } executionContext.errors.push(graphQLError); } 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 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) { if (value == null) { return "null"; } if (value === true) { return "true"; } if (value === false) { return "false"; } const type = typeof value; if (type === "number") { return value.toString(); } if (type === "bigint") { return value.toString(); } 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]) => `${stableStringify(key)}:${stableStringify(entryValue)}` ); return `{${entries.join(",")}}`; } return "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 = handleMaybePromiseMaybeAsyncIterable( () => 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 executeFetchPlanNode(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)) { 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)) { let projectEnumValue2 = function(value) { if (Array.isArray(value)) { return value.map((item) => projectEnumValue2(item)); } const enumValue = enumType.getValue(value); if (enumValue == null) { return null; } else if (getDirective( executionContext.supergraphSchema, enumValue, "inaccessible" )?.length) { return null; } return value; }; const enumType = fieldType; projectedValue = projectEnumValue2(projectedValue); } 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 if (isNonNullType(field.type)) { return null; } 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; } const REPRESENTATIONS_VAR_DEF = Object.freeze({ kind: Kind.VARIABLE_DEFINITION, variable: { kind: Kind.VARIABLE, name: { kind: Kind.NAME, value: "representations" } }, type: { kind: Kind.NON_NULL_TYPE, type: { kind: Kind.LIST_TYPE, type: { kind: Kind.NON_NULL_TYPE, type: { kind: Kind.NAMED_TYPE, name: { kind: Kind.NAME, value: "_Any" } } } } } }); function getEntityResolutionMap(supergraphSchema) { const entityResolutionMap = /* @__PURE__ */ new Map(); const realSubgraphNames = /* @__PURE__ */ new Map(); const joinGraph = supergraphSchema.getType("join__Graph"); if (isEnumType(joinGraph)) { for (const value of joinGraph.getValues()) { const valueDirectives = getDirectiveExtensions(value, supergraphSchema); const graphName = valueDirectives?.["join__graph"]?.[0]?.["name"]; if (graphName) { realSubgraphNames.set(value.name, graphName); } } } for (const typeName in supergraphSchema.getTypeMap()) { const type = supergraphSchema.getType(typeName); if (type) { const directives = getDirectiveExtensions(type, supergraphSchema); const joinTypeDirective = directives?.["join__type"]; if (joinTypeDirective) { for (const joinTypeDirectiveArgs of joinTypeDirective) { const subgraphName = joinTypeDirectiveArgs["graph"]; const keySelectionSetStr = joinTypeDirectiveArgs["key"]; const resolvable = joinTypeDirectiveArgs["resolvable"] !== false; if (subgraphName && keySelectionSetStr && resolvable) { const realSubgraphName = realSubgraphNames.get(subgraphName) || subgraphName; entityResolutionMap.set(typeName, { [realSubgraphName]: parseSelectionSet( `{ ${keySelectionSetStr} }` ) }); } } } } } return entityResolutionMap; } function getPubsubOperationRootFields(schema, entityResolutionMap) { const pubsubOperationFields = /* @__PURE__ */ new Map(); const subscriptionType = schema.getSubscriptionType(); if (subscriptionType) { const subscriptionFields = subscriptionType.getFields(); for (const fieldName in subscriptionFields) { const fieldDef = subscriptionFields[fieldName]; if (fieldDef) { const pubsubOperations = getDirectiveInExtensions( fieldDef, "pubsubOperation" ); if (pubsubOperations) { for (const operationDef of pubsubOperations) { const returnType = getNamedType(fieldDef.type); const entityResolution = entityResolutionMap.has(returnType.name); pubsubOperationFields.set(fieldDef.name, { pubsubTopic: operationDef["pubsubTopic"], filterBy: operationDef["filterBy"], result: operationDef["result"], entityTypeName: entityResolution ? returnType.name : void 0 }); } } } } } return pubsubOperationFields; } function resolvePubsubOperationRootField(opts, responseKey, resolverOpts) { const pubsubOperationResolver = getResolverForPubSubOperation( opts, (payload) => ({ data: { [responseKey]: payload } }) ); return handleMaybePromiseMaybeAsyncIterable( () => pubsubOperationResolver.subscribe( resolverOpts.root, resolverOpts.args, resolverOpts.context, void 0 ), (root) => pubsubOperationResolver.resolve( root, resolverOpts.args, resolverOpts.context, void 0 ) ); } const getSubscriptionInformationFromDocument = memoize2( function getSubscriptionInformationFromDocument2(supergraphSchema, document) { const typeInfo = getTypeInfo(supergraphSchema); let responseKey; let fieldName; let selectionSet; let argNodes; let variableDefinitions; visit( document, visitWithTypeInfo(typeInfo, { OperationDefinition(node) { variableDefinitions = node.variableDefinitions; }, Field(node) { const parentType = typeInfo.getParentType(); if (parentType === supergraphSchema.getSubscriptionType()) { responseKey = node.alias?.value || node.name.value; fieldName = node.name.value; selectionSet = node.selectionSet; argNodes = node.arguments; return BREAK; } return node; } }) ); return { responseKey, fieldName, selectionSet, argNodes, variableDefinitions }; } ); function getArgsFromArgumentNodes(argNodes, variables) { const args = {}; if (argNodes) { for (const argNode of argNodes) { const argName = argNode.name.value; const argValueNode = argNode.value; args[argName] = valueFromASTUntyped(argValueNode, variables); } } return args; } function resolvePubsubOperationResult(executionRequest, executionResult, pubsubOperationMetadata, responseKey, executeSubgraph, variableDefinitions, selectionSet) { if (pubsubOperationMetadata.entityTypeName && selectionSet && executionResult.data?.[responseKey] != null) { const representations = asArray( executionResult.data[responseKey] ).filter(Boolean); if (representations.length) { for (const representation of representations) { representation.__typename ||= pubsubOperationMeta