@graphql-hive/router-runtime
Version:
1,166 lines (1,161 loc) • 36.1 kB
JavaScript
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 };