@graphql-tools/stitch
Version:
A set of utils for faster development of GraphQL tools
3,156 lines • 112 kB
JavaScript
import { subtractSelectionSets, extractUnavailableFields, leftOverByDelegationPlan, extractUnavailableFieldsFromSelectionSet, delegateToSchema, cloneSubschemaConfig, isSubschemaConfig, defaultMergedResolver, Subschema } from '@graphql-tools/delegate';
import { mergeType, mergeInputType, mergeInterface, mergeUnion, mergeEnum, mergeScalar, mergeTypeDefs, mergeResolvers, mergeExtensions, applyExtensions } from '@graphql-tools/merge';
import { extendResolversFromInterfaces, addResolversToSchema, assertResolversPresent, makeExecutableSchema } from '@graphql-tools/schema';
import { collectSubFields, memoize2, memoize1, memoize3, parseSelectionSet, collectFields, isSome, getImplementingTypes, getRootTypeNames, filterSchema, mergeDeep, getDescription, createNamedStub, createStub, getRootTypeMap, getRootTypes, inspect, rewireTypes, getOperationASTFromRequest, getDefinedRootType } from '@graphql-tools/utils';
import { isAbstractType, Kind, TypeNameMetaFieldDef, getNamedType, GraphQLList, isLeafType, isInputObjectType, isUnionType, print, isObjectType, isInterfaceType, visit, isScalarType, isCompositeType, isNonNullType, isEnumType, isListType, getNullableType, isNullableType, GraphQLObjectType, GraphQLInputObjectType, GraphQLInterfaceType, GraphQLUnionType, GraphQLEnumType, GraphQLScalarType, GraphQLDirective, valueFromASTUntyped, getDirectiveValues, GraphQLDeprecatedDirective, DirectiveLocation, isNamedType, isIntrospectionType, isDirective, isSpecifiedScalarType, specifiedDirectives, GraphQLSchema, extendSchema } from 'graphql';
import { handleMaybePromise } from '@whatwg-node/promise-helpers';
import { batchDelegateToSchema } from '@graphql-tools/batch-delegate';
import { FilterTypes, TransformCompositeFields, wrapSchema } from '@graphql-tools/wrap';
import { getFragmentsFromDocument } from '@graphql-tools/executor';
function getFieldsNotInSubschema(schema, stitchingInfo, gatewayType, subschemaType, fieldNodes, fragments, variableValues, subschema, providedSelectionNode) {
const sourceSchema = subschema.transformedSchema;
let { fields: subFieldNodesByResponseKey, patches } = collectSubFields(
schema,
fragments,
variableValues,
gatewayType,
fieldNodes
);
let mapChanged = false;
if (patches.length) {
subFieldNodesByResponseKey = new Map(subFieldNodesByResponseKey);
for (const patch of patches) {
for (const [responseKey, fields2] of patch.fields) {
if (!mapChanged) {
subFieldNodesByResponseKey = new Map(subFieldNodesByResponseKey);
mapChanged = true;
}
const existingSubFieldNodes = subFieldNodesByResponseKey.get(responseKey);
if (existingSubFieldNodes) {
existingSubFieldNodes.push(...fields2);
} else {
subFieldNodesByResponseKey.set(responseKey, fields2);
}
}
}
}
const fieldsNotInSchema = /* @__PURE__ */ new Set();
if (isAbstractType(gatewayType)) {
fieldsNotInSchema.add({
kind: Kind.FIELD,
name: {
kind: Kind.NAME,
value: "__typename"
}
});
for (const possibleType of schema.getPossibleTypes(gatewayType)) {
const { fields: subFieldNodesOfPossibleType, patches: patches2 } = collectSubFields(
schema,
fragments,
variableValues,
possibleType,
fieldNodes
);
for (const patch of patches2) {
for (const [responseKey, fields2] of patch.fields) {
if (!mapChanged) {
subFieldNodesByResponseKey = new Map(subFieldNodesByResponseKey);
mapChanged = true;
}
const existingSubFieldNodes = subFieldNodesByResponseKey.get(responseKey);
if (existingSubFieldNodes) {
existingSubFieldNodes.push(...fields2);
} else {
subFieldNodesByResponseKey.set(responseKey, fields2);
}
}
}
for (const [responseKey, subFieldNodes] of subFieldNodesOfPossibleType) {
if (!mapChanged) {
subFieldNodesByResponseKey = new Map(subFieldNodesByResponseKey);
mapChanged = true;
}
const existingSubFieldNodes = subFieldNodesByResponseKey.get(responseKey);
if (existingSubFieldNodes) {
existingSubFieldNodes.push(...subFieldNodes);
} else {
subFieldNodesByResponseKey.set(responseKey, subFieldNodes);
}
}
}
}
const fieldNodesByField = stitchingInfo?.fieldNodesByField;
const fields = subschemaType.getFields();
const fieldNodesByFieldForType = fieldNodesByField?.[gatewayType.name];
for (const [, subFieldNodes] of subFieldNodesByResponseKey) {
let fieldNotInSchema = false;
const fieldName = subFieldNodes[0]?.name.value;
const field = fieldName === "__typename" ? TypeNameMetaFieldDef : fields[fieldName];
if (!field) {
if (providedSelectionNode) {
const subFieldSelection = {
kind: Kind.SELECTION_SET,
selections: subFieldNodes
};
const subtracted = subtractSelectionSets(
subFieldSelection,
providedSelectionNode
);
if (subtracted?.selections?.length) {
fieldNotInSchema = true;
for (const subFieldNode of subtracted.selections) {
fieldsNotInSchema.add(subFieldNode);
}
}
} else {
fieldNotInSchema = true;
for (const subFieldNode of subFieldNodes) {
fieldsNotInSchema.add(subFieldNode);
}
}
} else {
for (const subFieldNode of subFieldNodes) {
const unavailableFields = extractUnavailableFields(
sourceSchema,
field,
subFieldNode,
(fieldType) => {
if (stitchingInfo.mergedTypes[fieldType.name]?.resolvers.get(
subschema
)) {
return false;
}
return true;
},
fragments
);
if (unavailableFields.length) {
fieldNotInSchema = true;
fieldsNotInSchema.add({
...subFieldNode,
selectionSet: {
kind: Kind.SELECTION_SET,
selections: unavailableFields
}
});
}
}
}
const isComputedField = subschema.merge?.[gatewayType.name]?.fields?.[fieldName]?.computed;
let addedSubFieldNodes = false;
if ((isComputedField || fieldNotInSchema) && fieldNodesByFieldForType) {
const visitedFieldNames = /* @__PURE__ */ new Set();
addMissingRequiredFields({
fieldName,
fields,
fieldsNotInSchema,
visitedFieldNames,
onAdd: () => {
if (!addedSubFieldNodes) {
for (const subFieldNode of subFieldNodes) {
fieldsNotInSchema.add(subFieldNode);
}
addedSubFieldNodes = true;
}
},
fieldNodesByField: fieldNodesByFieldForType
});
}
}
return Array.from(fieldsNotInSchema);
}
function addMissingRequiredFields({
fieldName,
fields,
fieldsNotInSchema,
onAdd,
fieldNodesByField,
visitedFieldNames
}) {
if (visitedFieldNames.has(fieldName)) {
return;
}
visitedFieldNames.add(fieldName);
const fieldNodesForField = fieldNodesByField?.[fieldName];
if (fieldNodesForField) {
for (const fieldNode of fieldNodesForField) {
if (fieldNode.name.value !== "__typename" && !fields[fieldNode.name.value]) {
onAdd();
fieldsNotInSchema.add(fieldNode);
addMissingRequiredFields({
fieldName: fieldNode.name.value,
fields,
fieldsNotInSchema,
onAdd,
fieldNodesByField,
visitedFieldNames
});
}
}
}
}
function memoize5of7(fn) {
const memoize5Cache = /* @__PURE__ */ new WeakMap();
return function memoized(a1, a2, a3, a4, a5, a6, a7) {
let cache2 = memoize5Cache.get(a1);
if (!cache2) {
cache2 = /* @__PURE__ */ new WeakMap();
memoize5Cache.set(a1, cache2);
const cache32 = /* @__PURE__ */ new WeakMap();
cache2.set(a2, cache32);
const cache42 = /* @__PURE__ */ new WeakMap();
cache32.set(a3, cache42);
const cache52 = /* @__PURE__ */ new WeakMap();
cache42.set(a4, cache52);
const newValue = fn(a1, a2, a3, a4, a5, a6, a7);
cache52.set(a5, newValue);
return newValue;
}
let cache3 = cache2.get(a2);
if (!cache3) {
cache3 = /* @__PURE__ */ new WeakMap();
cache2.set(a2, cache3);
const cache42 = /* @__PURE__ */ new WeakMap();
cache3.set(a3, cache42);
const cache52 = /* @__PURE__ */ new WeakMap();
cache42.set(a4, cache52);
const newValue = fn(a1, a2, a3, a4, a5, a6, a7);
cache52.set(a5, newValue);
return newValue;
}
let cache4 = cache3.get(a3);
if (!cache4) {
cache4 = /* @__PURE__ */ new WeakMap();
cache3.set(a3, cache4);
const cache52 = /* @__PURE__ */ new WeakMap();
cache4.set(a4, cache52);
const newValue = fn(a1, a2, a3, a4, a5, a6, a7);
cache52.set(a5, newValue);
return newValue;
}
let cache5 = cache4.get(a4);
if (!cache5) {
cache5 = /* @__PURE__ */ new WeakMap();
cache4.set(a4, cache5);
const newValue = fn(a1, a2, a3, a4, a5, a6, a7);
cache5.set(a5, newValue);
return newValue;
}
const cachedValue = cache5.get(a5);
if (cachedValue === void 0) {
const newValue = fn(a1, a2, a3, a4, a5, a6, a7);
cache5.set(a5, newValue);
return newValue;
}
return cachedValue;
};
}
function calculateDelegationStage(mergedTypeInfo, sourceSubschemas, targetSubschemas, fieldNodes, fragments) {
const { selectionSets, fieldSelectionSets, uniqueFields, nonUniqueFields } = mergedTypeInfo;
const proxiableSubschemas = [];
const nonProxiableSubschemas = [];
for (const t of targetSubschemas) {
const selectionSet = selectionSets.get(t);
const fieldSelectionSetsMap = fieldSelectionSets.get(t);
if (selectionSet != null && !subschemaTypesContainSelectionSet(
mergedTypeInfo,
sourceSubschemas,
selectionSet
)) {
nonProxiableSubschemas.push(t);
} else {
if (fieldSelectionSetsMap == null || fieldNodes.every((fieldNode) => {
const fieldName = fieldNode.name.value;
const fieldSelectionSet = fieldSelectionSetsMap[fieldName];
return fieldSelectionSet == null || subschemaTypesContainSelectionSet(
mergedTypeInfo,
sourceSubschemas,
fieldSelectionSet
);
})) {
proxiableSubschemas.push(t);
} else {
nonProxiableSubschemas.push(t);
}
}
}
const unproxiableFieldNodes = [];
const delegationMap = /* @__PURE__ */ new Map();
for (const fieldNode of fieldNodes) {
const fieldName = fieldNode.name.value;
if (fieldName === "__typename") {
continue;
}
const sourcesWithUnsatisfiedDependencies = sourceSubschemas.filter(
(s) => fieldSelectionSets.get(s) != null && fieldSelectionSets.get(s)[fieldName] != null && !subschemaTypesContainSelectionSet(
mergedTypeInfo,
sourceSubschemas,
fieldSelectionSets.get(s)[fieldName]
)
);
if (sourcesWithUnsatisfiedDependencies.length === sourceSubschemas.length) {
unproxiableFieldNodes.push(fieldNode);
for (const source of sourcesWithUnsatisfiedDependencies) {
if (!nonProxiableSubschemas.includes(source)) {
nonProxiableSubschemas.push(source);
}
}
continue;
}
const uniqueSubschema = uniqueFields[fieldName];
if (uniqueSubschema != null) {
if (!proxiableSubschemas.includes(uniqueSubschema)) {
unproxiableFieldNodes.push(fieldNode);
continue;
}
const existingSubschema2 = delegationMap.get(uniqueSubschema)?.selections;
if (existingSubschema2 != null) {
existingSubschema2.push(fieldNode);
} else {
delegationMap.set(uniqueSubschema, {
kind: Kind.SELECTION_SET,
selections: [fieldNode]
});
}
continue;
}
let nonUniqueSubschemas = nonUniqueFields[fieldNode.name.value];
if (nonUniqueSubschemas == null) {
unproxiableFieldNodes.push(fieldNode);
continue;
}
nonUniqueSubschemas = nonUniqueSubschemas.filter(
(s) => proxiableSubschemas.includes(s)
);
if (!nonUniqueSubschemas.length) {
unproxiableFieldNodes.push(fieldNode);
continue;
}
const existingSubschema = nonUniqueSubschemas.find(
(s) => delegationMap.has(s)
);
if (existingSubschema != null) {
delegationMap.get(existingSubschema).selections.push(fieldNode);
} else {
let bestUniqueSubschema = nonUniqueSubschemas[0];
let bestScore = Infinity;
for (const nonUniqueSubschema of nonUniqueSubschemas) {
const typeInSubschema = nonUniqueSubschema.transformedSchema.getType(
mergedTypeInfo.typeName
);
const fields = typeInSubschema.getFields();
const field = fields[fieldNode.name.value];
if (field != null) {
const unavailableFields = extractUnavailableFields(
nonUniqueSubschema.transformedSchema,
field,
fieldNode,
(fieldType) => {
if (!nonUniqueSubschema.merge?.[fieldType.name]) {
let nonUniqueSubschemaSelections = (
// We have to cast it to `SelectionNode[]` because it is Readonly<SelectionNode[]> and it doesn't allow us to push new elements.
delegationMap.get(nonUniqueSubschema)?.selections
);
if (nonUniqueSubschemaSelections == null) {
nonUniqueSubschemaSelections = [];
delegationMap.set(nonUniqueSubschema, {
kind: Kind.SELECTION_SET,
selections: nonUniqueSubschemaSelections
});
}
nonUniqueSubschemaSelections.push(fieldNode);
return false;
}
return true;
}
);
const currentScore = calculateSelectionScore(
unavailableFields,
fragments
);
if (currentScore < bestScore) {
bestScore = currentScore;
bestUniqueSubschema = nonUniqueSubschema;
}
}
}
delegationMap.set(bestUniqueSubschema, {
kind: Kind.SELECTION_SET,
selections: [fieldNode]
});
}
}
if (delegationMap.size > 1) {
optimizeDelegationMap(delegationMap, mergedTypeInfo.typeName, fragments);
}
return {
delegationMap,
proxiableSubschemas,
nonProxiableSubschemas,
unproxiableFieldNodes
};
}
const calculateSelectionScore = memoize2(
function calculateSelectionScore2(selections, fragments) {
let score = 0;
for (const selectionNode of selections) {
switch (selectionNode.kind) {
case Kind.FIELD:
score++;
if (selectionNode.selectionSet?.selections) {
score += calculateSelectionScore2(
selectionNode.selectionSet.selections,
fragments
);
}
break;
case Kind.INLINE_FRAGMENT:
score += calculateSelectionScore2(
selectionNode.selectionSet.selections,
fragments
);
break;
case Kind.FRAGMENT_SPREAD:
const fragment = fragments?.[selectionNode.name.value];
if (fragment) {
score += calculateSelectionScore2(
fragment.selectionSet.selections,
fragments
);
}
break;
}
}
return score;
}
);
function getStitchingInfo(schema) {
const stitchingInfo = schema.extensions?.["stitchingInfo"];
if (!stitchingInfo) {
throw new Error(`Schema is not a stitched schema.`);
}
return stitchingInfo;
}
function createDelegationPlanBuilder(mergedTypeInfo) {
mergedTypeInfo.nonMemoizedDelegationPlanBuilder = function delegationPlanBuilder(schema, sourceSubschema, variableValues, fragments, fieldNodes, _context, info) {
const stitchingInfo = getStitchingInfo(schema);
const targetSubschemas = mergedTypeInfo?.targetSubschemas.get(sourceSubschema);
if (!targetSubschemas || !targetSubschemas.length) {
return [];
}
const typeName = mergedTypeInfo.typeName;
const typeInSubschema = sourceSubschema.transformedSchema.getType(
typeName
);
let providedSelectionNode;
const parentFieldName = fieldNodes[0]?.name.value;
if (info?.parentType && parentFieldName) {
const providedSelectionsByField = stitchingInfo.mergedTypes[info.parentType.name]?.providedSelectionsByField?.get(sourceSubschema);
providedSelectionNode = providedSelectionsByField?.[parentFieldName];
}
const fieldsNotInSubschema = getFieldsNotInSubschema(
schema,
stitchingInfo,
schema.getType(typeName),
mergedTypeInfo.typeMaps.get(sourceSubschema)?.[typeName],
fieldNodes,
fragments,
variableValues,
sourceSubschema,
providedSelectionNode
);
if (!fieldsNotInSubschema.length) {
return [];
}
const delegationMaps = [];
let sourceSubschemas = createSubschemas(sourceSubschema);
let delegationStage = calculateDelegationStage(
mergedTypeInfo,
sourceSubschemas,
targetSubschemas,
fieldsNotInSubschema,
fragments
);
let { delegationMap } = delegationStage;
while (delegationMap.size) {
delegationMaps.push(delegationMap);
const {
proxiableSubschemas,
nonProxiableSubschemas,
unproxiableFieldNodes
} = delegationStage;
sourceSubschemas = combineSubschemas(
sourceSubschemas,
proxiableSubschemas
);
delegationStage = calculateDelegationStage(
mergedTypeInfo,
sourceSubschemas,
nonProxiableSubschemas,
unproxiableFieldNodes,
fragments
);
delegationMap = delegationStage.delegationMap;
}
if (isAbstractType(typeInSubschema) && fieldsNotInSubschema.some(
(fieldNode) => fieldNode.name.value === "__typename"
)) {
const inlineFragments = [];
for (const fieldNode of fieldNodes) {
if (fieldNode.selectionSet) {
for (const selection of fieldNode.selectionSet.selections) {
if (selection.kind === Kind.INLINE_FRAGMENT) {
inlineFragments.push(selection);
}
}
}
}
const implementedSubschemas = targetSubschemas.filter((subschema) => {
const typeInTargetSubschema = mergedTypeInfo.typeMaps.get(subschema)?.[typeName];
return isAbstractType(typeInTargetSubschema) && subschema.transformedSchema.getPossibleTypes(typeInTargetSubschema).length;
});
let added = false;
for (const implementedSubgraphs of implementedSubschemas) {
for (const delegationMap2 of delegationMaps) {
const existingSelections = delegationMap2.get(implementedSubgraphs)?.selections;
if (existingSelections) {
existingSelections.push({
kind: Kind.FIELD,
name: {
kind: Kind.NAME,
value: "__typename"
}
});
existingSelections.push(...inlineFragments);
added = true;
break;
}
if (added) {
break;
}
}
}
if (!added) {
const subschemaWithTypeName = implementedSubschemas[0];
if (subschemaWithTypeName) {
const delegationStageToFetchTypeName = /* @__PURE__ */ new Map();
delegationStageToFetchTypeName.set(subschemaWithTypeName, {
kind: Kind.SELECTION_SET,
selections: [
{
kind: Kind.FIELD,
name: {
kind: Kind.NAME,
value: "__typename"
}
},
...inlineFragments
]
});
delegationMaps.push(delegationStageToFetchTypeName);
}
}
}
if (delegationStage.unproxiableFieldNodes.length && delegationStage.nonProxiableSubschemas.length) {
leftOverByDelegationPlan.set(delegationMaps, {
unproxiableFieldNodes: delegationStage.unproxiableFieldNodes,
nonProxiableSubschemas: delegationStage.nonProxiableSubschemas,
missingFieldsParentMap: /* @__PURE__ */ new Map(),
missingFieldsParentDeferredMap: /* @__PURE__ */ new Map()
});
}
return delegationMaps;
};
return memoize5of7(function wrappedDelegationPlanBuilder(schema, sourceSubschema, variableValues, fragments, fieldNodes, context, info) {
return mergedTypeInfo.nonMemoizedDelegationPlanBuilder(
schema,
sourceSubschema,
variableValues,
fragments,
fieldNodes,
context,
info
);
});
}
function optimizeDelegationMap(delegationMap, typeName, fragments) {
for (const [subschema, selectionSet] of delegationMap) {
for (const [subschema2, selectionSet2] of delegationMap) {
if (subschema === subschema2) {
continue;
}
const unavailableFields = extractUnavailableFieldsFromSelectionSet(
subschema2.transformedSchema,
// Unfortunately, getType returns GraphQLNamedType, but we already know the type is a GraphQLObjectType, so we can cast it.
subschema2.transformedSchema.getType(typeName),
selectionSet,
() => true,
fragments
);
if (!unavailableFields.length) {
delegationMap.set(subschema2, {
kind: Kind.SELECTION_SET,
selections: [...selectionSet2.selections, ...selectionSet.selections]
});
delegationMap.delete(subschema);
}
}
}
return delegationMap;
}
const createSubschemas = memoize1(function createSubschemas2(sourceSubschema) {
return [sourceSubschema];
});
const combineSubschemas = memoize2(function combineSubschemas2(sourceSubschemas, additionalSubschemas) {
return sourceSubschemas.concat(additionalSubschemas);
});
const subschemaTypesContainSelectionSet = memoize3(
function subschemaTypesContainSelectionSet2(mergedTypeInfo, sourceSubchemas, selectionSet) {
return typesContainSelectionSet(
sourceSubchemas.map(
(sourceSubschema) => sourceSubschema.transformedSchema.getType(
mergedTypeInfo.typeName
)
),
selectionSet
);
}
);
function typesContainSelectionSet(types, selectionSet) {
const fieldMaps = types.map((type) => type.getFields());
for (const selection of selectionSet.selections) {
if (selection.kind === Kind.FIELD) {
const fields = fieldMaps.map((fieldMap) => fieldMap[selection.name.value]).filter((field) => field != null);
if (!fields.length) {
return false;
}
if (selection.selectionSet != null) {
return typesContainSelectionSet(
fields.map(
(field) => getNamedType(field.type)
),
selection.selectionSet
);
}
} else if (selection.kind === Kind.INLINE_FRAGMENT && selection.typeCondition?.name.value === types[0]?.name) {
return typesContainSelectionSet(types, selection.selectionSet);
}
}
return true;
}
function createMergedTypeResolver(mergedTypeResolverOptions, mergedType) {
const { fieldName, argsFromKeys, valuesFromResults, args } = mergedTypeResolverOptions;
function getType(info) {
if (!mergedType) {
return getNamedType(info.returnType);
}
if (typeof mergedType === "string") {
return info.schema.getType(mergedType);
}
return mergedType;
}
if (argsFromKeys != null) {
return function mergedBatchedTypeResolver(_originalResult, context, info, subschema, selectionSet, key, type = getType(info)) {
return batchDelegateToSchema({
schema: subschema,
operation: "query",
fieldName,
returnType: new GraphQLList(type),
key,
argsFromKeys,
valuesFromResults,
selectionSet,
context,
info,
skipTypeMerging: true,
dataLoaderOptions: mergedTypeResolverOptions.dataLoaderOptions
});
};
}
if (args != null) {
return function mergedTypeResolver(originalResult, context, info, subschema, selectionSet, _key, type = getType(info)) {
return delegateToSchema({
schema: subschema,
operation: "query",
fieldName,
returnType: type,
args: args(originalResult),
selectionSet,
context,
info,
skipTypeMerging: true
});
};
}
return void 0;
}
function createStitchingInfo(subschemaMap, typeCandidates, mergeTypes) {
const mergedTypes = createMergedTypes(typeCandidates, mergeTypes);
return {
subschemaMap,
fieldNodesByType: /* @__PURE__ */ Object.create(null),
fieldNodesByField: /* @__PURE__ */ Object.create(null),
dynamicSelectionSetsByField: /* @__PURE__ */ Object.create(null),
mergedTypes
};
}
function createMergedTypes(typeCandidates, mergeTypes) {
const mergedTypes = /* @__PURE__ */ Object.create(
null
);
const typeInterfacesMap = /* @__PURE__ */ Object.create(null);
for (const typeName in typeCandidates) {
if (typeCandidates[typeName]) {
for (const { type } of typeCandidates[typeName]) {
if ("getInterfaces" in type) {
const interfaces = type.getInterfaces();
for (const iface of interfaces) {
const interfaceName = iface.name;
let implementingTypes = typeInterfacesMap[typeName];
if (implementingTypes == null) {
implementingTypes = /* @__PURE__ */ new Set();
typeInterfacesMap[typeName] = implementingTypes;
}
implementingTypes.add(interfaceName);
}
}
}
}
}
for (const typeName in typeCandidates) {
const typeCandidatesOfTypeName = typeCandidates[typeName];
if (!typeCandidatesOfTypeName) {
throw new Error(`Invalid type candidates for type name ${typeName}`);
}
const typeCandidate = typeCandidatesOfTypeName[0];
if (isObjectType(typeCandidate?.type) || isInterfaceType(typeCandidate?.type)) {
const typeCandidatesWithMergedTypeConfig = typeCandidatesOfTypeName.filter(
(typeCandidate2) => typeCandidate2.transformedSubschema != null && typeCandidate2.transformedSubschema.merge != null && typeName in typeCandidate2.transformedSubschema.merge
);
if (mergeTypes === true || typeof mergeTypes === "function" && mergeTypes(typeCandidatesOfTypeName, typeName) || Array.isArray(mergeTypes) && mergeTypes.includes(typeName) || typeCandidatesWithMergedTypeConfig.length) {
const targetSubschemas = [];
const typeMaps = /* @__PURE__ */ new Map();
const supportedBySubschemas = /* @__PURE__ */ Object.create({});
const selectionSets = /* @__PURE__ */ new Map();
const fieldSelectionSets = /* @__PURE__ */ new Map();
const resolvers = /* @__PURE__ */ new Map();
const providedSelectionsByField = /* @__PURE__ */ new Map();
for (const typeCandidate2 of typeCandidatesOfTypeName) {
const subschema = typeCandidate2.transformedSubschema;
if (subschema == null) {
continue;
}
typeMaps.set(subschema, subschema.transformedSchema.getTypeMap());
let mergedTypeConfig2 = subschema?.merge?.[typeName];
if (!mergedTypeConfig2) {
for (const interfaceName of typeInterfacesMap[typeName] ?? []) {
mergedTypeConfig2 = subschema?.merge?.[interfaceName];
if (mergedTypeConfig2) {
break;
}
}
}
if (mergedTypeConfig2 == null) {
continue;
}
if (mergedTypeConfig2.selectionSet) {
const selectionSet2 = parseSelectionSet(
mergedTypeConfig2.selectionSet,
{
noLocation: true
}
);
selectionSets.set(subschema, selectionSet2);
}
if (mergedTypeConfig2.fields) {
const parsedFieldSelectionSets = /* @__PURE__ */ Object.create(null);
for (const fieldName in mergedTypeConfig2.fields) {
if (mergedTypeConfig2.fields[fieldName]?.selectionSet) {
const rawFieldSelectionSet = mergedTypeConfig2.fields[fieldName].selectionSet;
parsedFieldSelectionSets[fieldName] = rawFieldSelectionSet ? parseSelectionSet(rawFieldSelectionSet, {
noLocation: true
}) : void 0;
}
let providedSelectionSet = mergedTypeConfig2.fields[fieldName]?.provides;
if (providedSelectionSet) {
providedSelectionSet = visit(providedSelectionSet, {
[Kind.SELECTION_SET](node) {
const typeNameField = node.selections.find(
(selection) => selection.kind === Kind.FIELD && selection.name.value === "__typename"
);
if (typeNameField) {
return node;
}
return {
...node,
selections: [
...node.selections,
{
kind: Kind.FIELD,
name: {
kind: Kind.NAME,
value: "__typename"
}
}
]
};
}
});
let providedSelectionsForSubschema = providedSelectionsByField.get(subschema);
if (providedSelectionsForSubschema == null) {
providedSelectionsForSubschema = /* @__PURE__ */ Object.create({});
providedSelectionsByField.set(
subschema,
providedSelectionsForSubschema
);
}
providedSelectionsForSubschema[fieldName] = providedSelectionSet;
}
}
fieldSelectionSets.set(subschema, parsedFieldSelectionSets);
}
const type = subschema.transformedSchema.getType(typeName);
const resolver = mergedTypeConfig2.resolve ?? createMergedTypeResolver(mergedTypeConfig2, type);
if (resolver == null) {
continue;
}
const keyFn = mergedTypeConfig2.key;
resolvers.set(
subschema,
keyFn ? function batchMergedTypeResolverWrapper(originalResult, context, info, subschema2, selectionSet2, type2) {
return handleMaybePromise(
() => keyFn(originalResult),
(key) => resolver(
originalResult,
context,
info,
subschema2,
selectionSet2,
key,
type2
)
);
} : resolver
);
targetSubschemas.push(subschema);
const fieldMap = type.getFields();
const selectionSet = selectionSets.get(subschema);
for (const fieldName in fieldMap) {
const field = fieldMap[fieldName];
const fieldType = getNamedType(field?.type);
if (selectionSet && isLeafType(fieldType) && selectionSetContainsTopLevelField(selectionSet, fieldName)) {
continue;
}
if (!supportedBySubschemas[fieldName]) {
supportedBySubschemas[fieldName] = [];
}
supportedBySubschemas[fieldName]?.push(subschema);
}
}
const sourceSubschemas = typeCandidates[typeName]?.map((typeCandidate2) => typeCandidate2?.transformedSubschema).filter(isSome);
const targetSubschemasBySubschema = /* @__PURE__ */ new Map();
if (sourceSubschemas) {
for (const subschema of sourceSubschemas) {
const filteredSubschemas = targetSubschemas.filter(
(s) => s !== subschema
);
if (filteredSubschemas.length) {
targetSubschemasBySubschema.set(subschema, filteredSubschemas);
}
}
}
const mergedTypeConfig = {
typeName,
targetSubschemas: targetSubschemasBySubschema,
typeMaps,
selectionSets,
fieldSelectionSets,
uniqueFields: /* @__PURE__ */ Object.create({}),
nonUniqueFields: /* @__PURE__ */ Object.create({}),
resolvers,
providedSelectionsByField
};
mergedTypes[typeName] = mergedTypeConfig;
mergedTypeConfig.delegationPlanBuilder = createDelegationPlanBuilder(
mergedTypeConfig
);
for (const fieldName in supportedBySubschemas) {
if (supportedBySubschemas[fieldName]?.length === 1 && supportedBySubschemas[fieldName][0]) {
mergedTypeConfig.uniqueFields[fieldName] = supportedBySubschemas[fieldName][0];
} else if (supportedBySubschemas[fieldName]) {
mergedTypeConfig.nonUniqueFields[fieldName] = supportedBySubschemas[fieldName];
}
}
}
}
}
return mergedTypes;
}
function completeStitchingInfo(stitchingInfo, resolvers, schema) {
const {
fieldNodesByType,
fieldNodesByField,
dynamicSelectionSetsByField,
mergedTypes
} = stitchingInfo;
const rootTypes = [schema.getQueryType(), schema.getMutationType()];
for (const rootType of rootTypes) {
if (rootType) {
fieldNodesByType[rootType.name] = [
parseSelectionSet("{ __typename }", { noLocation: true }).selections[0]
];
}
}
const selectionSetsByField = /* @__PURE__ */ Object.create(null);
for (const typeName in mergedTypes) {
const mergedTypeInfo = mergedTypes[typeName];
if (mergedTypeInfo?.selectionSets == null && mergedTypeInfo?.fieldSelectionSets == null) {
continue;
}
for (const [
subschemaConfig,
selectionSet
] of mergedTypeInfo.selectionSets) {
const schema2 = subschemaConfig.transformedSchema;
const type = schema2.getType(typeName);
const fields = type.getFields();
for (const fieldName in fields) {
const field = fields[fieldName];
const fieldType = getNamedType(field?.type);
if (selectionSet && isLeafType(fieldType) && selectionSetContainsTopLevelField(selectionSet, fieldName)) {
continue;
}
updateSelectionSetMap(
selectionSetsByField,
typeName,
fieldName,
selectionSet,
true
);
}
if (isAbstractType(type)) {
updateSelectionSetMap(
selectionSetsByField,
typeName,
"__typename",
selectionSet
);
}
}
for (const [, selectionSetFieldMap] of mergedTypeInfo.fieldSelectionSets) {
for (const fieldName in selectionSetFieldMap) {
const selectionSet = selectionSetFieldMap[fieldName];
if (selectionSet) {
updateSelectionSetMap(
selectionSetsByField,
typeName,
fieldName,
selectionSet,
true
);
}
}
}
}
for (const typeName in resolvers) {
const type = schema.getType(typeName);
if (type === void 0 || isLeafType(type) || isInputObjectType(type) || isUnionType(type)) {
continue;
}
const resolver = resolvers[typeName];
for (const fieldName in resolver) {
const field = resolver[fieldName];
if (typeof field.selectionSet === "function") {
if (!dynamicSelectionSetsByField[typeName]) {
dynamicSelectionSetsByField[typeName] = /* @__PURE__ */ Object.create(null);
}
if (!dynamicSelectionSetsByField[typeName][fieldName]) {
dynamicSelectionSetsByField[typeName][fieldName] = [];
}
dynamicSelectionSetsByField[typeName][fieldName].push(
field.selectionSet
);
} else if (field.selectionSet) {
const selectionSet = parseSelectionSet(field.selectionSet, {
noLocation: true
});
updateSelectionSetMap(
selectionSetsByField,
typeName,
fieldName,
selectionSet
);
}
}
}
const variableValues = /* @__PURE__ */ Object.create(null);
const fragments = /* @__PURE__ */ Object.create(null);
const fieldNodeMap = /* @__PURE__ */ Object.create(null);
for (const typeName in selectionSetsByField) {
const type = schema.getType(typeName);
for (const fieldName in selectionSetsByField[typeName]) {
for (const selectionSet of selectionSetsByField[typeName][fieldName]) {
const { fields } = collectFields(
schema,
fragments,
variableValues,
type,
selectionSet
);
for (const [, fieldNodes] of fields) {
for (const fieldNode of fieldNodes) {
const key = print(fieldNode);
if (fieldNodeMap[key] == null) {
fieldNodeMap[key] = fieldNode;
updateArrayMap(fieldNodesByField, typeName, fieldName, fieldNode);
} else {
updateArrayMap(
fieldNodesByField,
typeName,
fieldName,
fieldNodeMap[key]
);
}
}
}
}
}
}
return stitchingInfo;
}
function updateSelectionSetMap(map, typeName, fieldName, selectionSet, includeTypename) {
if (includeTypename) {
const typenameSelectionSet = parseSelectionSet("{ __typename }", {
noLocation: true
});
updateArrayMap(
map,
typeName,
fieldName,
selectionSet,
typenameSelectionSet
);
return;
}
updateArrayMap(map, typeName, fieldName, selectionSet);
}
function updateArrayMap(map, typeName, fieldName, value, initialValue) {
if (map[typeName] == null) {
const initialItems = initialValue === void 0 ? [value] : [initialValue, value];
map[typeName] = {
[fieldName]: initialItems
};
} else if (map[typeName][fieldName] == null) {
const initialItems = initialValue === void 0 ? [value] : [initialValue, value];
map[typeName][fieldName] = initialItems;
} else {
map[typeName][fieldName].push(value);
}
}
function addStitchingInfo(stitchedSchema, stitchingInfo) {
stitchedSchema.extensions = {
...stitchedSchema.extensions,
stitchingInfo
};
}
function selectionSetContainsTopLevelField(selectionSet, fieldName) {
return selectionSet.selections.some(
(selection) => selection.kind === Kind.FIELD && selection.name.value === fieldName
);
}
function isolateComputedFieldsTransformer(subschemaConfig) {
if (subschemaConfig.merge == null) {
return [subschemaConfig];
}
const baseSchemaTypes = /* @__PURE__ */ Object.create(null);
const isolatedSchemaTypes = /* @__PURE__ */ Object.create(null);
for (const typeName in subschemaConfig.merge) {
const mergedTypeConfig = subschemaConfig.merge[typeName];
const objectType = subschemaConfig.schema.getType(
typeName
);
baseSchemaTypes[typeName] = mergedTypeConfig;
if (mergedTypeConfig.fields) {
const baseFields = /* @__PURE__ */ Object.create(null);
const isolatedFields = /* @__PURE__ */ Object.create(null);
for (const fieldName in mergedTypeConfig.fields) {
const mergedFieldConfig = mergedTypeConfig.fields[fieldName];
if (mergedFieldConfig?.computed && mergedFieldConfig?.selectionSet) {
isolatedFields[fieldName] = mergedFieldConfig;
} else if (mergedFieldConfig?.computed) {
throw new Error(
`A selectionSet is required for computed field "${typeName}.${fieldName}"`
);
} else {
baseFields[fieldName] = mergedFieldConfig;
}
}
const isolatedFieldCount = Object.keys(isolatedFields).length;
if (isolatedFieldCount && isolatedFieldCount !== Object.keys(objectType.getFields()).length) {
baseSchemaTypes[typeName] = {
...mergedTypeConfig,
fields: baseFields
};
const keyFieldNames = isolatedSchemaTypes[typeName]?.keyFieldNames ?? [];
if (keyFieldNames.length === 0) {
if (mergedTypeConfig.selectionSet) {
const parsedSelectionSet = parseSelectionSet(
mergedTypeConfig.selectionSet,
{ noLocation: true }
);
const keyFields = collectFields(
subschemaConfig.schema,
{},
{},
objectType,
parsedSelectionSet
);
keyFieldNames.push(...Array.from(keyFields.fields.keys()));
}
for (const entryPoint of mergedTypeConfig.entryPoints ?? []) {
if (entryPoint.selectionSet) {
const parsedSelectionSet = parseSelectionSet(
entryPoint.selectionSet,
{ noLocation: true }
);
const keyFields = collectFields(
subschemaConfig.schema,
{},
{},
objectType,
parsedSelectionSet
);
keyFieldNames.push(...Array.from(keyFields.fields.keys()));
}
}
}
isolatedSchemaTypes[typeName] = {
...mergedTypeConfig,
// there might already be key fields
keyFieldNames,
fields: {
...isolatedSchemaTypes[typeName]?.fields ?? {},
...isolatedFields
},
canonical: void 0
};
for (const fieldName in isolatedFields) {
const returnType = getNamedType(
objectType.getFields()[fieldName]?.type
);
const returnTypes = [returnType];
if (isInterfaceType(returnType)) {
returnTypes.push(
...getImplementingTypes(
returnType.name,
subschemaConfig.schema
).map(
(name) => subschemaConfig.schema.getType(
name
)
)
);
} else if (isUnionType(returnType)) {
returnTypes.push(...returnType.getTypes());
}
for (const type of returnTypes) {
const returnTypeMergeConfig = subschemaConfig.merge[type.name];
if (Object.values(subschemaConfig.schema.getTypeMap()).filter(isObjectType).filter((t) => t !== type).filter((t) => !isolatedSchemaTypes[t.name]).find(
(t) => Object.values(t.getFields()).find(
(f) => getNamedType(f.type) === type
)
)) {
continue;
}
if (isObjectType(type)) {
const returnTypeSelectionSet = returnTypeMergeConfig?.selectionSet;
if (returnTypeSelectionSet) {
const keyFieldNames2 = [];
const parsedSelectionSet = parseSelectionSet(
returnTypeSelectionSet,
{ noLocation: true }
);
const keyFields = collectFields(
subschemaConfig.schema,
{},
{},
type,
parsedSelectionSet
);
keyFieldNames2.push(...Array.from(keyFields.fields.keys()));
for (const entryPoint of returnTypeMergeConfig.entryPoints ?? []) {
if (entryPoint.selectionSet) {
const parsedSelectionSet2 = parseSelectionSet(
entryPoint.selectionSet,
{ noLocation: true }
);
const keyFields2 = collectFields(
subschemaConfig.schema,
{},
{},
type,
parsedSelectionSet2
);
keyFieldNames2.push(...Array.from(keyFields2.fields.keys()));
}
}
isolatedSchemaTypes[type.name] = {
...returnTypeMergeConfig,
keyFieldNames: keyFieldNames2,
fields: {
...isolatedSchemaTypes[type.name]?.fields ?? {}
}
};
} else if (!returnTypeMergeConfig) {
const fields = {
...isolatedSchemaTypes[type.name]?.fields
};
if (isAbstractType(type)) {
for (const implementingType of getImplementingTypes(
type.name,
subschemaConfig.schema
)) {
const implementingTypeFields = isolatedSchemaTypes[implementingType]?.fields;
if (implementingTypeFields) {
for (const fieldName2 in implementingTypeFields) {
if (implementingTypeFields[fieldName2]) {
fields[fieldName2] = {
...implementingTypeFields[fieldName2],
...fields[fieldName2]
};
}
}
}
}
}
if (isInterfaceType(type) || isObjectType(type)) {
for (const fieldName2 in type.getFields()) {
fields[fieldName2] ||= {};
}
}
isolatedSchemaTypes[type.name] = {
keyFieldNames: [],
fields,
canonical: true
};
}
}
}
}
}
}
}
if (Object.keys(isolatedSchemaTypes).length) {
return [
filterIsolatedSubschema(subschemaConfig, isolatedSchemaTypes),
filterBaseSubschema(
{ ...subschemaConfig, merge: baseSchemaTypes },
isolatedSchemaTypes
)
];
}
return [subschemaConfig];
}
function _createCompositeFieldFilter(schema) {
const filteredFields = {};
for (const typeName in schema.getTypeMap()) {
const type = schema.getType(typeName);
if (isObjectType(type) || isInterfaceType(type)) {
const filteredFieldsOfType = {
__typename: true
};
let hasField = false;
const fieldMap = type.getFields();
for (const fieldName in fieldMap) {
filteredFieldsOfType[fieldName] = true;
hasField = true;
}
if (hasField) {
filteredFields[typeName] = filteredFieldsOfType;
}
}
}
return new TransformCompositeFields(
(typeName, fieldName) => filteredFields[typeName]?.[fieldName] ? void 0 : null,
(typeName, fieldName) => filteredFields[typeName]?.[fieldName] ? void 0 : null
);
}
function isIsolatedField(typeName, fieldName, isolatedSchemaTypes) {
const fieldConfig = isolatedSchemaTypes[typeName]?.fields?.[fieldName];
if (fieldConfig) {
return true;
}
return false;
}
function filterBaseSubschema(subschemaConfig, isolatedSchemaTypes) {
const schema = subschemaConfig.schema;
const typesForInterface = {};
const iFacesForTypes = {};
const filteredSchema = filterSchema({
schema,
objectFieldFilter: (typeName, fieldName) => {
const iFacesForType = iFacesForTypes[typeName] ||= [];
if (!iFacesForType) {
let addIface2 = function(iFace) {
if (!iFacesForType.includes(iFace.name)) {
iFacesForType.push(iFace.name);
iFace.getInterfaces().forEach(addIface2);
}
};
const type = schema.getType(typeName);
let iFaces = type.getInterfaces();
for (const iface of iFaces) {
addIface2(iface);
}
}
const allTypes = [typeName, ...iFacesForType];
const isIsolatedFieldName = allTypes.every(
(implementingTypeName) => isIsolatedField(implementingTypeName, fieldName, isolatedSchemaTypes)
);
const isKeyFieldName = allTypes.some(
(implementingTypeName) => (isolatedSchemaTypes[implementingTypeName]?.keyFieldNames ?? []).includes(fieldName)
);
return !isIsolatedFieldName || isKeyFieldName;
},
interfaceFieldFilter: (typeName, fieldName) => {
if (!typesForInterface[typeName]) {
typesForInterface[typeName] = getImplementingTypes(typeName, schema);
}
const iFacesForType = iFacesForTypes[typeName] ||= [];
if (!iFacesForType) {
let addIface2 = function(iFace) {
if (!iFacesForType.includes(iFace.name)) {
iFacesForType.push(iFace.name);
iFace.getInterfaces().forEach(addIface2);
}
};
const type = schema.getType(typeName);
let iFaces = type.getInterfaces();
for (const iface of iFaces) {
addIface2(iface);
}
}
const allTypes = [
typeName,
...iFacesForType,
...typesForInterface[typeName]
];
const isIsolatedFieldName = allTypes.every(
(implementingTypeName) => isIsolatedField(implementingTypeName, fieldName, isolatedSchemaTypes)
);
const isKeyFieldName = allTypes.some(
(implementingTypeName) => (isolatedSchemaTypes[implementingTypeName]?.keyFieldNames ?? []).includes(fieldName)
);
return !isIsolatedFieldName || isKeyFieldName;
}
});
const filteredSubschema = {
...subschemaConfig,
merge: subschemaConfig.merge ? {
...subschemaConfig.merge
} : void 0,
transforms: (subschemaConfig.transforms ?? []).concat([
_createCompositeFieldFilter(filteredSchema),
new FilterTypes((type) => {
const typeName = type.name;
const typeInFiltered = filteredSchema.getType(typeName);
if (!typeInFiltered) {
return false;
}
if (isObjectType(type) || isInterfaceType(type)) {
return Object.keys(type.getFields()).length > 0;
}
return true;
})
])
};
const remainingTypes = filteredSchema.getTypeMap();
const mergeConfig = filteredSubschema.merge;
if (mergeConfig) {
for (const mergeType in mergeConfig) {
if (!remainingTypes[mergeType]) {
delete mergeConfig[mergeType];
}
}
if (!Object.keys(mergeConfig).length) {
delete filteredSubschema.merge;
}
}
return filteredSubschema;
}
function filterIsolatedSubschema(subschemaConfig, isolatedSchemaTypes) {
const computedFieldTypes = {};
const queryRootFields = {};
function listReachableTypesToIsolate(subschemaConfig2, type, typeNames = /* @__PURE__ */ new Set()) {
if (isScalarType(type)) {
return typeNames;
} else if ((isObjectType(type) || isInterfaceType(type)) && subschemaConfig2.merge?.[type.name]) {
typeNames.add(type.name);
return typeNames;
} else if (isCompositeType(type)) {
typeNames.add(type.name);
const types = /* @__PURE__ */ new Set();
if (isObjectType(type)) {
types.add(type);
} else if (isInterfaceType(type)) {
getImplementingTypes(type.name, subschemaConfig2.schema).forEach(
(name) => types.add(
subschemaConfig2.schema.getType(name)
)
);
} else if (isUnionType(type)) {
type.getTypes().forEach((t) => types.add(t));
}
for (const type2 of types) {
typeNames.add(type2.name);
for (const f of Object.values(type2.getFields())) {
const fieldType = getNamedType(f.type);
if (!typeNames.has(fieldType.name) && isCompositeType(fieldType)) {
listReachableTypesToIsolate(subschemaConfig2, fieldType, typeNames);
}
}
}
return typeNames;
} else if (isUnionType(type)) {
typeNames.add(type.name);
type.getTypes().forEach(
(t) => listReachableTypesToIsolate(subschemaConfig2, t, typeNames)
);
return typeNames;
} else {
return typeNames;
}
}
const queryType = subschemaConfig.schema.getQueryType();
for (const typeName in subschemaConfig.merge) {
const mergedTypeConfig = subschemaConfig.merge[typeName];
const entryPoints = mergedTypeConfig?.entryPoints ?? [mergedTypeConfig];
const queryTypeFields = queryType?.getFields();
for (const entryPoint of entryPoints) {
if (entryPoint?.fieldName != null) {
queryRootFields[entryPoint.fieldName] = true;
const rootField = queryTypeFields?.[entryPoint.fieldName];
if (rootField) {
const rootFieldType = getNamedType(rootField.type);
computedFieldTypes[rootFieldType.name] = true;
if (isInterfaceType(rootFieldType)) {
getImplementingTypes(
rootFieldType.name,
subschemaConfig.schema
).forEach((tn) => {
computedFieldTypes[tn] = true;
});
}
}
}
}
const computedFields = [
...Object.entries(mergedTypeConfig?.fields || {}).map(([k, v]) => v.computed ? k : null).filter((fn) => fn !== null)
].filter((fn) => !queryRootFields[fn]);
const type = subschemaConfig.schema.getType(typeName);
for (const fieldName of computedFields) {
const fieldType = getNamedType(type.getFields()[fieldName].type);
computedFieldTypes[fieldType.name] = true;
listReachableTypesToIsolate(subschemaConfig, fieldType).forEach((tn) => {
computedFieldTypes[tn] = true;
});
}
}
const rootTypeNames = getRootTypeNames(subschemaConfig.schema);
const typesForInterface = {};
const iFaceForTypes = {};
const filteredSchema = filterSchema({
schema: subschemaConfig.schema,
rootFieldFilter: (typeName, fieldName, config) => {
if (rootTypeNames.has(typeName)) {
if (queryType?.name === typeName) {
if (queryRootFields[fieldName]) {
return true;
}
} else {
return true;
}
}
const returnType = getNamedType(config.type);
if (isAbstractType(returnType)) {
const typesForInterface2 = [
returnType.name,
...getImplementingTypes(returnType.name, subschemaConfig.schema)
];
return typesForInterface2.some((t) => computedFieldTypes[t] != null);
}
return computedFieldTypes[returnType.name] != null;
},
objectFieldFilter: (typeName, fieldName, config) => {
if (computedFieldTypes[typeName]) {
return true;
}
if (!iFaceForTypes[typeName]) {
iFaceForTypes[typeName] = subschemaConfig.schema.getType(typeName).getInterfaces().map((iFace) => iFace.name);
}
if (iFaceForTypes[typeName].some((iFace) => computedFieldTypes[iFace])) {
return true;
}
const fieldType = getNamedType(config.type);
if (computedFieldTypes[fieldType.name]) {
return true;
}
return subschemaConfig.merge?.[typeName] == null || subschemaConfig.merge[typeName]?.fields?.[fieldName] != null || (isolatedSchemaTypes[typeName]?.keyFieldNames ?? []).includes(fieldName);
},
interfaceFieldFilter: (typeName, fieldName, config) => {
if (computedFieldTypes[typeName]) {
return true;
}
const fieldType = getNamedType(config.type);
if (computedFieldTypes[fieldType.name]) {
return true;
}
if (!typesForInterface[typeName]) {
typesForInterface[typeName] = getImplementingTypes(
typeName,
subschemaConfig.schema
);
}
if (typesForInterface[typeName].some((t) => computedFieldTypes[t])) {
return true;
}
const isIsolatedFieldName = typesForInterface[typeName].some(
(implementingTypeName) => isIsolatedField(implementingTypeName, fieldName, isolatedSchemaTypes)
) || subschemaConfig.merge?.[typeName]?.fields?.[fieldName] != null;
const isComputedFieldType = typesForInterface[typeName].some(
(implementingTypeName) => {
if (computedFieldTypes[implementingTypeName]) {
return true;
}
const type = subschemaConfig.schema.getType(
implementingTypeName
);
const field = type.getFields()[fieldName];
if (field == null) {
return false;
}
const fieldType2 = getNamedType(field.type);
return computedFieldTypes[fieldType2.name] != null;
}
);
return isIsolatedFieldName || isComputedFieldType || typesForInterface[typeName].some(
(implementingTypeName) => (isolatedSchemaTypes?.[implementingTypeName]?.keyFieldNames ?? []).includes(fieldName)
) || (isolatedSchemaTypes[typeName]?.keyFieldNames ?? []).includes(fieldName);
}
});
const merge = Object.fromEntries(
// get rid of keyFieldNames again
Object.entries(isolatedSchemaTypes).map(
([typeName, { keyFieldNames, ...config }]) => [typeName, config]
)
);
const filteredSubschema = {
...subschemaConfig,
merge,
transforms: (subschemaConfig.transforms ?? []).concat([
_createCompositeFieldFilter(filteredSchema)
])
};
return filteredSubschema;
}
function splitMergedTypeEntryPointsTransformer(subschemaConfig) {
if (!subschemaConfig.merge) return [subschemaConfig];
const maxEntryPoints = Object.values(subschemaConfig.merge).reduce(
(max, mergedTypeConfig) => {
return Math.max(max, mergedTypeConfig?.entryPoints?.length ?? 0);
},
0
);
if (maxEntryPoints === 0) return [subschemaConfig];
const subschemaPermutations = [];
for (let i = 0; i < maxEntryPoints; i += 1) {
const subschemaPermutation = cloneSubschemaConfig(subschemaConfig);
const mergedTypesCopy = subschemaPermutation.merge ?? /* @__PURE__ */ Object.create(null);
let currentMerge = mergedTypesCopy;
if (i > 0) {
subschemaPermutation.merge = currentMerge = /* @__PURE__ */ Object.create(null);
}
for (const typeName in mergedTypesCopy) {
const mergedTypeConfig = mergedTypesCopy[typeName];
const mergedTypeEntryPoint = mergedTypeConfig?.entryPoints?.[i];
if (mergedTypeEntryPoint) {
if (mergedTypeConfig.selectionSet ?? mergedTypeConfig.fieldName ?? mergedTypeConfig.resolve) {
throw new Error(
`Merged type ${typeName} may not define entryPoints in addition to selectionSet, fieldName, or resolve`
);
}
Object.assign(mergedTypeConfig, mergedTypeEntryPoint);
delete mergedTypeConfig.entryPoints;
if (i > 0) {
delete mergedTypeConfig.canonical;
if (mergedTypeConfig.fields != null) {
for (const mergedFieldName in mergedTypeConfig.fields) {
const mergedFieldConfig = mergedTypeConfig.fields[mergedFieldName];
delete mergedFieldConfig?.canonical;
}
}
}
currentMerge[typeName] = mergedTypeConfig;
}
}
subschemaPermutations.push(subschemaPermutation);
}
return subschemaPermutations;
}
function extractDefinitions(ast) {
const typeDefinitions = [];
const directiveDefs = [];
const schemaDefs = [];
const schemaExtensions = [];
const extensionDefs = [];
for (const def of ast.definitions) {
switch (def.kind) {
case Kind.OBJECT_TYPE_DEFINITION:
case Kind.INTERFACE_TYPE_DEFINITION:
case Kind.INPUT_OBJECT_TYPE_DEFINITION:
case Kind.UNION_TYPE_DEFINITION:
case Kind.ENUM_TYPE_DEFINITION:
case Kind.SCALAR_TYPE_DEFINITION:
typeDefinitions.push(def);
break;
case Kind.DIRECTIVE_DEFINITION:
directiveDefs.push(def);
break;
case Kind.SCHEMA_DEFINITION:
schemaDefs.push(def);
break;
case Kind.SCHEMA_EXTENSION:
schemaExtensions.push(def);
break;
case Kind.OBJECT_TYPE_EXTENSION:
case Kind.INTERFACE_TYPE_EXTENSION:
case Kind.INPUT_OBJECT_TYPE_EXTENSION:
case Kind.UNION_TYPE_EXTENSION:
case Kind.ENUM_TYPE_EXTENSION:
case Kind.SCALAR_TYPE_EXTENSION:
extensionDefs.push(def);
break;
}
}
return {
typeDefinitions,
directiveDefs,
schemaDefs,
schemaExtensions,
extensionDefs
};
}
var ValidationLevel = /* @__PURE__ */ ((ValidationLevel2) => {
ValidationLevel2["Error"] = "error";
ValidationLevel2["Warn"] = "warn";
ValidationLevel2["Off"] = "off";
return ValidationLevel2;
})(ValidationLevel || {});
function validateFieldConsistency(finalFieldConfig, candidates, typeMergingOptions) {
const firstCandidate = candidates[0];
if (!firstCandidate) {
throw new Error("First candidate is null");
}
const fieldNamespace = `${firstCandidate.type.name}.${firstCandidate.fieldName}`;
const finalFieldNull = isNonNullType(finalFieldConfig.type);
validateTypeConsistency(
finalFieldConfig,
candidates.map((c) => c.fieldConfig),
"field",
fieldNamespace,
typeMergingOptions
);
if (getValidationSettings(fieldNamespace, typeMergingOptions).strictNullComparison && candidates.some((c) => finalFieldNull !== isNonNullType(c.fieldConfig.type))) {
validationMessage(
`Nullability of field "${fieldNamespace}" does not match across subschemas. Disable typeMergingOptions.validationSettings.strictNullComparison to permit safe divergences.`,
fieldNamespace,
typeMergingOptions
);
} else if (finalFieldNull && candidates.some((c) => !isNonNullType(c.fieldConfig.type))) {
validationMessage(
`Canonical definition of field "${fieldNamespace}" is not-null while some subschemas permit null. This will be an automatic error in future versions.`,
fieldNamespace,
typeMergingOptions
);
}
const argCandidatesMap = /* @__PURE__ */ Object.create(null);
for (const { fieldConfig } of candidates) {
if (fieldConfig.args == null) {
continue;
}
for (const argName in fieldConfig.args) {
const arg = fieldConfig.args[argName];
if (arg) {
argCandidatesMap[argName] ||= [];
argCandidatesMap[argName].push(arg);
}
}
}
if (Object.values(argCandidatesMap).some(
(argCandidates) => candidates.length !== argCandidates.length
)) {
validationMessage(
`Canonical definition of field "${fieldNamespace}" implements inconsistent argument names across subschemas. Input may be filtered from some requests.`,
fieldNamespace,
typeMergingOptions
);
}
for (const argName in argCandidatesMap) {
if (finalFieldConfig.args == null) {
continue;
}
const argCandidates = argCandidatesMap[argName];
if (!argCandidates) {
throw new Error("argCandidates is null");
}
const argNamespace = `${fieldNamespace}.${argName}`;
const finalArgConfig = finalFieldConfig.args[argName] || argCandidates[argCandidates.length - 1];
if (!finalArgConfig) {
throw new Error("finalArgConfig is null");
}
const finalArgType = getNamedType(finalArgConfig.type);
const finalArgNull = isNonNullType(finalArgConfig.type);
validateTypeConsistency(
finalArgConfig,
argCandidates,
"argument",
argNamespace,
typeMergingOptions
);
if (getValidationSettings(argNamespace, typeMergingOptions).strictNullComparison && argCandidates.some((c) => finalArgNull !== isNonNullType(c.type))) {
validationMessage(
`Nullability of argument "${argNamespace}" does not match across subschemas. Disable typeMergingOptions.validationSettings.strictNullComparison to permit safe divergences.`,
argNamespace,
typeMergingOptions
);
} else if (!finalArgNull && argCandidates.some((c) => isNonNullType(c.type))) {
validationMessage(
`Canonical definition of argument "${argNamespace}" permits null while some subschemas require not-null. This will be an automatic error in future versions.`,
argNamespace,
typeMergingOptions
);
}
if (isEnumType(finalArgType)) {
validateInputEnumConsistency(
finalArgType,
argCandidates,
typeMergingOptions
);
}
}
}
function validateInputObjectConsistency(fieldInclusionMap, candidates, typeMergingOptions) {
for (const fieldName in fieldInclusionMap) {
const count = fieldInclusionMap[fieldName];
if (candidates.length !== count && candidates[0]) {
const namespace = `${candidates[0].type.name}.${fieldName}`;
validationMessage(
`Definition of input field "${namespace}" is not implemented by all subschemas. Input may be filtered from some requests.`,
namespace,
typeMergingOptions
);
}
}
}
function validateInputFieldConsistency(finalInputFieldConfig, candidates, typeMergingOptions) {
const inputFieldNamespace = `${candidates[0]?.type.name}.${candidates[0]?.fieldName}`;
const inputFieldConfigs = candidates.map((c) => c.inputFieldConfig);
const finalInputFieldType = getNamedType(finalInputFieldConfig.type);
const finalInputFieldNull = isNonNullType(finalInputFieldConfig.type);
validateTypeConsistency(
finalInputFieldConfig,
inputFieldConfigs,
"input field",
inputFieldNamespace,
typeMergingOptions
);
if (getValidationSettings(inputFieldNamespace, typeMergingOptions).strictNullComparison && candidates.some(
(c) => finalInputFieldNull !== isNonNullType(c.inputFieldConfig.type)
)) {
validationMessage(
`Nullability of input field "${inputFieldNamespace}" does not match across subschemas. Disable typeMergingOptions.validationSettings.strictNullComparison to permit safe divergences.`,
inputFieldNamespace,
typeMergingOptions
);
} else if (!finalInputFieldNull && candidates.some((c) => isNonNullType(c.inputFieldConfig.type))) {
validationMessage(
`Canonical definition of input field "${inputFieldNamespace}" permits null while some subschemas require not-null. This will be an automatic error in future versions.`,
inputFieldNamespace,
typeMergingOptions
);
}
if (isEnumType(finalInputFieldType)) {
validateInputEnumConsistency(
finalInputFieldType,
inputFieldConfigs,
typeMergingOptions
);
}
}
function validateTypeConsistency(finalElementConfig, candidates, definitionType, settingNamespace, typeMergingOptions) {
const finalNamedType = getNamedType(finalElementConfig.type);
const finalIsScalar = isScalarType(finalNamedType);
const finalIsList = hasListType(finalElementConfig.type);
for (const c of candidates) {
if (finalIsList !== hasListType(c.type)) {
throw new Error(
`Definitions of ${definitionType} "${settingNamespace}" implement inconsistent list types across subschemas and cannot be merged.`
);
}
const currentNamedType = getNamedType(c.type);
if (finalNamedType.toString() !== currentNamedType.toString()) {
const proxiableScalar = !!typeMergingOptions?.validationSettings?.proxiableScalars?.[finalNamedType.toString()]?.includes(currentNamedType.toString());
const bothScalars = finalIsScalar && isScalarType(currentNamedType);
const permitScalar = proxiableScalar && bothScalars;
if (proxiableScalar && !bothScalars) {
throw new Error(
`Types ${finalNamedType} and ${currentNamedType} are not proxiable scalars.`
);
}
if (!permitScalar) {
validationMessage(
`Definitions of ${definitionType} "${settingNamespace}" implement inconsistent named types across subschemas. This will be an automatic error in future versions.`,
settingNamespace,
typeMergingOptions
);
}
}
}
}
function hasListType(type) {
return isListType(getNullableType(type));
}
function validateInputEnumConsistency(inputEnumType, candidates, typeMergingOptions) {
const enumValueInclusionMap = /* @__PURE__ */ Object.create(null);
for (const candidate of candidates) {
const enumType = getNamedType(candidate.type);
if (isEnumType(enumType)) {
for (const { value } of enumType.getValues()) {
enumValueInclusionMap[value] = enumValueInclusionMap[value] || 0;
enumValueInclusionMap[value] += 1;
}
}
}
if (Object.values(enumValueInclusionMap).some(
(count) => candidates.length !== count
)) {
validationMessage(
`Enum "${inputEnumType.name}" is used as an input with inconsistent values across subschemas. This will be an automatic error in future versions.`,
inputEnumType.name,
typeMergingOptions
);
}
}
function validationMessage(message, settingNamespace, typeMergingOptions) {
const override = `typeMergingOptions.validationScopes['${settingNamespace}'].validationLevel`;
const settings = getValidationSettings(settingNamespace, typeMergingOptions);
switch (settings.validationLevel ?? ValidationLevel.Warn) {
case ValidationLevel.Off:
return;
case ValidationLevel.Error:
throw new Error(
`${message} If this is intentional, you may disable this error by setting ${override} = "warn|off"`
);
default:
console.warn(
`${message} To disable this warning or elevate it to an error, set ${override} = "error|off"`
);
}
}
function getValidationSettings(settingNamespace, typeMergingOptions) {
return {
...typeMergingOptions?.validationSettings ?? {},
...typeMergingOptions?.validationScopes?.[settingNamespace] ?? {}
};
}
function mergeCandidates(typeName, candidates, typeMergingOptions) {
const initialCandidateType = candidates[0]?.type;
if (candidates.some(
(candidate) => candidate.type.constructor !== initialCandidateType?.constructor
)) {
throw new Error(
`Cannot merge different type categories into common type ${typeName}.`
);
}
if (isObjectType(initialCandidateType)) {
return mergeObjectTypeCandidates(typeName, candidates, typeMergingOptions);
} else if (isInputObjectType(initialCandidateType)) {
return mergeInputObjectTypeCandidates(
typeName,
candidates,
typeMergingOptions
);
} else if (isInterfaceType(initialCandidateType)) {
return mergeInterfaceTypeCandidates(
typeName,
candidates,
typeMergingOptions
);
} else if (isUnionType(initialCandidateType)) {
return mergeUnionTypeCandidates(typeName, candidates, typeMergingOptions);
} else if (isEnumType(initialCandidateType)) {
return mergeEnumTypeCandidates(typeName, candidates, typeMergingOptions);
} else if (isScalarType(initialCandidateType)) {
return mergeScalarTypeCandidates(typeName, candidates, typeMergingOptions);
} else {
throw new Error(`Type ${typeName} has unknown GraphQL type.`);
}
}
function mergeObjectTypeCandidates(typeName, candidates, typeMergingOptions) {
candidates = orderedTypeCandidates(candidates, typeMergingOptions);
const description = mergeTypeDescriptions(candidates, typeMergingOptions);
const fields = fieldConfigMapFromTypeCandidates(
candidates,
typeMergingOptions
);
const typeConfigs = candidates.map(
(candidate) => candidate.type.toConfig()
);
const interfaceMap = typeConfigs.map((typeConfig2) => typeConfig2.interfaces).reduce((acc, interfaces2) => {
if (interfaces2 != null) {
for (const iface of interfaces2) {
acc[iface.name] = iface;
}
}
return acc;
}, /* @__PURE__ */ Object.create(null));
const interfaces = Object.values(interfaceMap);
const astNodes = pluck("astNode", candidates);
const fieldAstNodes = canonicalFieldNamesForType(candidates).map((fieldName) => fields[fieldName]?.astNode).filter((n) => n != null);
if (astNodes.length > 1 && fieldAstNodes.length) {
astNodes.push({
...astNodes[astNodes.length - 1],
fields: JSON.parse(JSON.stringify(fieldAstNodes))
});
}
const astNode = astNodes.slice(1).reduce(
(acc, astNode2) => mergeType(astNode2, acc, {
ignoreFieldConflicts: true
}),
astNodes[0]
);
const extensionASTNodes = pluck(
"extensionASTNodes",
candidates
);
const extensions = Object.assign(
{},
...pluck("extensions", candidates)
);
const typeConfig = {
name: typeName,
description,
fields,
interfaces,
astNode,
extensionASTNodes,
extensions
};
return new GraphQLObjectType(typeConfig);
}
function mergeInputObjectTypeCandidates(typeName, candidates, typeMergingOptions) {
candidates = orderedTypeCandidates(candidates, typeMergingOptions);
const description = mergeTypeDescriptions(candidates, typeMergingOptions);
const fields = inputFieldConfigMapFromTypeCandidates(
candidates,
typeMergingOptions
);
const astNodes = pluck("astNode", candidates);
const fieldAstNodes = canonicalFieldNamesForType(candidates).map((fieldName) => fields[fieldName]?.astNode).filter((n) => n != null);
if (astNodes.length > 1 && fieldAstNodes.length) {
astNodes.push({
...astNodes[astNodes.length - 1],
fields: JSON.parse(JSON.stringify(fieldAstNodes))
});
}
const astNode = astNodes.slice(1).reduce(
(acc, astNode2) => mergeInputType(astNode2, acc, {
ignoreFieldConflicts: true
}),
astNodes[0]
);
const extensionASTNodes = pluck(
"extensionASTNodes",
candidates
);
const extensions = Object.assign(
{},
...pluck("extensions", candidates)
);
const typeConfig = {
name: typeName,
description,
fields,
astNode,
extensionASTNodes,
extensions
};
return new GraphQLInputObjectType(typeConfig);
}
function pluck(typeProperty, candidates) {
return candidates.map((candidate) => candidate.type[typeProperty]).filter((value) => value != null);
}
function mergeInterfaceTypeCandidates(typeName, candidates, typeMergingOptions) {
candidates = orderedTypeCandidates(candidates, typeMergingOptions);
const description = mergeTypeDescriptions(candidates, typeMergingOptions);
const fields = fieldConfigMapFromTypeCandidates(
candidates,
typeMergingOptions
);
const typeConfigs = candidates.map((candidate) => candidate.type.toConfig());
const interfaceMap = typeConfigs.map(
(typeConfig2) => "interfaces" in typeConfig2 ? typeConfig2.interfaces : []
).reduce((acc, interfaces2) => {
if (interfaces2 != null) {
for (const iface of interfaces2) {
acc[iface.name] = iface;
}
}
return acc;
}, /* @__PURE__ */ Object.create(null));
const interfaces = Object.values(interfaceMap);
const astNodes = pluck("astNode", candidates);
const fieldAstNodes = canonicalFieldNamesForType(candidates).map((fieldName) => fields[fieldName]?.astNode).filter((n) => n != null);
if (astNodes.length > 1 && fieldAstNodes.length) {
astNodes.push({
...astNodes[astNodes.length - 1],
fields: JSON.parse(JSON.stringify(fieldAstNodes))
});
}
const astNode = astNodes.slice(1).reduce(
(acc, astNode2) => mergeInterface(astNode2, acc, {
ignoreFieldConflicts: true
}),
astNodes[0]
);
const extensionASTNodes = pluck(
"extensionASTNodes",
candidates
);
const extensions = Object.assign(
{},
...pluck("extensions", candidates)
);
const typeConfig = {
name: typeName,
description,
fields,
interfaces,
astNode,
extensionASTNodes,
extensions
};
return new GraphQLInterfaceType(typeConfig);
}
function mergeUnionTypeCandidates(typeName, candidates, typeMergingOptions) {
candidates = orderedTypeCandidates(candidates, typeMergingOptions);
const description = mergeTypeDescriptions(candidates, typeMergingOptions);
const typeConfigs = candidates.map((candidate) => {
if (!isUnionType(candidate.type)) {
throw new Error(`Expected ${candidate.type} to be a union type!`);
}
return candidate.type.toConfig();
});
const typeMap = typeConfigs.reduce(
(acc, typeConfig2) => {
for (const type of typeConfig2.types) {
acc[type.name] = type;
}
return acc;
},
/* @__PURE__ */ Object.create(null)
);
const types = Object.values(typeMap);
const astNodes = pluck("astNode", candidates);
const astNode = astNodes.slice(1).reduce(
(acc, astNode2) => mergeUnion(
astNode2,
acc
),
astNodes[0]
);
const extensionASTNodes = pluck(
"extensionASTNodes",
candidates
);
const extensions = Object.assign(
{},
...pluck("extensions", candidates)
);
const typeConfig = {
name: typeName,
description,
types,
astNode,
extensionASTNodes,
extensions
};
return new GraphQLUnionType(typeConfig);
}
function mergeEnumTypeCandidates(typeName, candidates, typeMergingOptions) {
candidates = orderedTypeCandidates(candidates, typeMergingOptions);
const description = mergeTypeDescriptions(candidates, typeMergingOptions);
const values = enumValueConfigMapFromTypeCandidates(
candidates,
typeMergingOptions
);
const astNodes = pluck("astNode", candidates);
const astNode = astNodes.slice(1).reduce(
(acc, astNode2) => mergeEnum(astNode2, acc, {
consistentEnumMerge: true
}),
astNodes[0]
);
const extensionASTNodes = pluck(
"extensionASTNodes",
candidates
);
const extensions = Object.assign(
{},
...pluck("extensions", candidates)
);
const typeConfig = {
name: typeName,
description,
values,
astNode,
extensionASTNodes,
extensions
};
return new GraphQLEnumType(typeConfig);
}
function enumValueConfigMapFromTypeCandidates(candidates, typeMergingOptions) {
const enumValueConfigCandidatesMap = /* @__PURE__ */ Object.create(null);
for (const candidate of candidates) {
const valueMap = candidate.type.toConfig().values;
for (const enumValue in valueMap) {
const enumValueConfig = valueMap[enumValue];
if (enumValueConfig) {
const enumValueConfigCandidate = {
enumValueConfig,
enumValue,
type: candidate.type,
subschema: candidate.subschema,
transformedSubschema: candidate.transformedSubschema
};
if (enumValueConfigCandidatesMap[enumValue]) {
enumValueConfigCandidatesMap[enumValue].push(
enumValueConfigCandidate
);
} else {
enumValueConfigCandidatesMap[enumValue] = [enumValueConfigCandidate];
}
}
}
}
const enumValueConfigMap = /* @__PURE__ */ Object.create(null);
for (const enumValue in enumValueConfigCandidatesMap) {
const enumValueConfigCandidates = enumValueConfigCandidatesMap[enumValue];
if (enumValueConfigCandidates) {
const enumValueConfigMerger = typeMergingOptions?.enumValueConfigMerger ?? defaultEnumValueConfigMerger;
enumValueConfigMap[enumValue] = enumValueConfigMerger(
enumValueConfigCandidates
);
}
}
return enumValueConfigMap;
}
function defaultEnumValueConfigMerger(candidates) {
const preferred = candidates.find(
({ type, transformedSubschema }) => isSubschemaConfig(transformedSubschema) && transformedSubschema.merge?.[type.name]?.canonical
);
const lastCanonical = candidates[candidates.length - 1];
if (!lastCanonical) {
throw new Error("Last canonical is required");
}
return (preferred || lastCanonical).enumValueConfig;
}
function mergeScalarTypeCandidates(typeName, candidates, typeMergingOptions) {
candidates = orderedTypeCandidates(candidates, typeMergingOptions);
const description = mergeTypeDescriptions(candidates, typeMergingOptions);
const serializeFns = pluck(
"serialize",
candidates
);
const serialize = serializeFns[serializeFns.length - 1];
const parseValueFns = pluck(
"parseValue",
candidates
);
const parseValue = parseValueFns[parseValueFns.length - 1];
const parseLiteralFns = pluck(
"parseLiteral",
candidates
);
const parseLiteral = parseLiteralFns[parseLiteralFns.length - 1];
const astNodes = pluck("astNode", candidates);
const astNode = astNodes.slice(1).reduce(
(acc, astNode2) => mergeScalar(
astNode2,
acc
),
astNodes[0]
);
const extensionASTNodes = pluck(
"extensionASTNodes",
candidates
);
const extensions = Object.assign(
{},
...pluck("extensions", candidates)
);
let specifiedByURL;
for (const candidate of candidates) {
if ("specifiedByURL" in candidate.type && candidate.type.specifiedByURL) {
specifiedByURL = candidate.type.specifiedByURL;
break;
}
}
const typeConfig = {
name: typeName,
description,
serialize,
parseValue,
parseLiteral,
astNode,
extensionASTNodes,
extensions,
specifiedByURL
};
return new GraphQLScalarType(typeConfig);
}
function orderedTypeCandidates(candidates, typeMergingOptions) {
const typeCandidateMerger = typeMergingOptions?.typeCandidateMerger ?? defaultTypeCandidateMerger;
const candidate = typeCandidateMerger(candidates);
return candidates.filter((c) => c !== candidate).concat([candidate]);
}
function defaultTypeCandidateMerger(candidates) {
const canonical = candidates.filter(
({ type, transformedSubschema }) => isSubschemaConfig(transformedSubschema) ? transformedSubschema.merge?.[type.name]?.canonical : false
);
if (canonical.length > 1) {
if (!canonical[0]) {
throw new Error(`First canonical is required`);
}
throw new Error(
`Multiple canonical definitions for "${canonical[0].type.name}"`
);
} else if (canonical.length) {
if (!canonical[0]) {
throw new Error(`First canonical is required`);
}
return canonical[0];
}
const lastCanonical = candidates[candidates.length - 1];
if (!lastCanonical) {
throw new Error(`Last canonical is required`);
}
return lastCanonical;
}
function mergeTypeDescriptions(candidates, typeMergingOptions) {
const typeDescriptionsMerger = typeMergingOptions?.typeDescriptionsMerger ?? defaultTypeDescriptionMerger;
return typeDescriptionsMerger(candidates);
}
function defaultTypeDescriptionMerger(candidates) {
const lastCandidate = candidates[candidates.length - 1];
return lastCandidate?.type.description;
}
function fieldConfigMapFromTypeCandidates(candidates, typeMergingOptions) {
const fieldConfigCandidatesMap = /* @__PURE__ */ Object.create(null);
for (const candidate of candidates) {
const typeConfig = candidate.type.toConfig();
const fieldConfigMap2 = typeConfig.fields;
for (const fieldName in fieldConfigMap2) {
const fieldConfig = fieldConfigMap2[fieldName];
if (fieldConfig) {
const fieldConfigCandidate = {
fieldConfig,
fieldName,
type: candidate.type,
subschema: candidate.subschema,
transformedSubschema: candidate.transformedSubschema
};
if (fieldConfigCandidatesMap[fieldName]) {
fieldConfigCandidatesMap[fieldName].push(fieldConfigCandidate);
} else {
fieldConfigCandidatesMap[fieldName] = [fieldConfigCandidate];
}
}
}
}
const fieldConfigMap = /* @__PURE__ */ Object.create(null);
for (const fieldName in fieldConfigCandidatesMap) {
const fieldConfigCandidates = fieldConfigCandidatesMap[fieldName];
if (fieldConfigCandidates) {
fieldConfigMap[fieldName] = mergeFieldConfigs(
fieldConfigCandidates,
typeMergingOptions
);
}
}
return fieldConfigMap;
}
function mergeFieldConfigs(candidates, typeMergingOptions) {
const fieldConfigMerger = typeMergingOptions?.fieldConfigMerger ?? getDefaultFieldConfigMerger(
typeMergingOptions?.useNonNullableFieldOnConflict
);
const finalFieldConfig = fieldConfigMerger(candidates);
validateFieldConsistency(finalFieldConfig, candidates, typeMergingOptions);
return finalFieldConfig;
}
function getDefaultFieldConfigMerger(useNonNullableFieldOnConflict = false) {
return function defaultFieldConfigMerger(candidates) {
const nullables = [];
const nonNullables = [];
const canonicalByField = [];
const canonicalByType = [];
for (const {
type,
fieldName,
fieldConfig,
transformedSubschema
} of candidates) {
if (!isSubschemaConfig(transformedSubschema)) continue;
if (transformedSubschema.merge?.[type.name]?.fields?.[fieldName]?.canonical) {
canonicalByField.push(fieldConfig);
} else if (transformedSubschema.merge?.[type.name]?.canonical) {
canonicalByType.push(fieldConfig);
}
if (isNullableType(fieldConfig.type)) {
nullables.push(fieldConfig);
} else {
nonNullables.push(fieldConfig);
}
}
const nonNullableFinalField = nonNullables.length > 0 && nullables.length > 0 && useNonNullableFieldOnConflict;
if (canonicalByField.length > 1 && candidates[0]) {
throw new Error(
`Multiple canonical definitions for "${candidates[0].type.name}.${candidates[0].fieldName}"`
);
} else if (canonicalByField.length) {
const finalField2 = canonicalByField[0];
if (!finalField2) {
throw new Error("Final field is required");
}
if (nonNullableFinalField) {
return {
...finalField2,
type: getNullableType(finalField2.type)
};
}
return finalField2;
} else if (canonicalByType.length) {
const finalField2 = canonicalByType[0];
if (!finalField2) {
throw new Error("Final field is required");
}
if (nonNullableFinalField) {
return {
...finalField2,
type: getNullableType(finalField2.type)
};
}
return finalField2;
}
const finalField = candidates[candidates.length - 1]?.fieldConfig;
if (!finalField) {
throw new Error("Field config is required");
}
if (nonNullableFinalField) {
return {
...finalField,
type: getNullableType(finalField.type)
};
}
return finalField;
};
}
function inputFieldConfigMapFromTypeCandidates(candidates, typeMergingOptions) {
const inputFieldConfigCandidatesMap = /* @__PURE__ */ Object.create(null);
const fieldInclusionMap = /* @__PURE__ */ Object.create(null);
for (const candidate of candidates) {
const typeConfig = candidate.type.toConfig();
const inputFieldConfigMap2 = typeConfig.fields;
for (const fieldName in inputFieldConfigMap2) {
const inputFieldConfig = inputFieldConfigMap2[fieldName];
if (inputFieldConfig == null) {
throw new Error(`'inputFieldConfig' is required`);
}
fieldInclusionMap[fieldName] = fieldInclusionMap[fieldName] || 0;
fieldInclusionMap[fieldName] += 1;
const inputFieldConfigCandidate = {
inputFieldConfig,
fieldName,
type: candidate.type,
subschema: candidate.subschema,
transformedSubschema: candidate.transformedSubschema
};
if (inputFieldConfigCandidatesMap[fieldName]) {
inputFieldConfigCandidatesMap[fieldName].push(
inputFieldConfigCandidate
);
} else {
inputFieldConfigCandidatesMap[fieldName] = [inputFieldConfigCandidate];
}
}
}
validateInputObjectConsistency(
fieldInclusionMap,
candidates,
typeMergingOptions
);
const inputFieldConfigMap = /* @__PURE__ */ Object.create(null);
for (const fieldName in inputFieldConfigCandidatesMap) {
const inputFieldConfigMerger = typeMergingOptions?.inputFieldConfigMerger ?? defaultInputFieldConfigMerger;
const inputFieldConfigCandidate = inputFieldConfigCandidatesMap[fieldName];
if (!inputFieldConfigCandidate) {
throw new Error("Input field config candidate is required");
}
inputFieldConfigMap[fieldName] = inputFieldConfigMerger(
inputFieldConfigCandidate
);
validateInputFieldConsistency(
inputFieldConfigMap[fieldName],
inputFieldConfigCandidate,
typeMergingOptions
);
}
return inputFieldConfigMap;
}
function defaultInputFieldConfigMerger(candidates) {
const canonicalByField = [];
const canonicalByType = [];
for (const {
type,
fieldName,
inputFieldConfig,
transformedSubschema
} of candidates) {
if (!isSubschemaConfig(transformedSubschema)) continue;
if (transformedSubschema.merge?.[type.name]?.fields?.[fieldName]?.canonical) {
canonicalByField.push(inputFieldConfig);
} else if (transformedSubschema.merge?.[type.name]?.canonical) {
canonicalByType.push(inputFieldConfig);
}
}
if (canonicalByField.length > 1 && candidates[0]) {
throw new Error(
`Multiple canonical definitions for "${candidates[0].type.name}.${candidates[0].fieldName}"`
);
} else if (canonicalByField.length) {
return canonicalByField[0];
} else if (canonicalByType.length) {
return canonicalByType[0];
}
const lastCandidate = candidates[candidates.length - 1];
if (!lastCandidate) {
throw new Error("Last candidate is required");
}
return lastCandidate.inputFieldConfig;
}
function canonicalFieldNamesForType(candidates) {
const canonicalFieldNames = /* @__PURE__ */ Object.create(null);
for (const { type, transformedSubschema } of candidates) {
if (!isSubschemaConfig(transformedSubschema)) continue;
const mergeConfig = transformedSubschema.merge?.[type.name];
if (mergeConfig != null && mergeConfig.fields != null && !mergeConfig.canonical) {
for (const fieldName in mergeConfig.fields) {
const mergedFieldConfig = mergeConfig.fields[fieldName];
if (mergedFieldConfig?.canonical) {
canonicalFieldNames[fieldName] = true;
}
}
}
}
return Object.keys(canonicalFieldNames);
}
function mergeDirectives(directives) {
if (directives.size === 0) {
return void 0;
}
if (directives.size === 1) {
const directive = directives.values().next().value;
return directive;
}
let name;
let description;
const locations = /* @__PURE__ */ new Set();
const args = {};
const extensionsSet = /* @__PURE__ */ new Set();
let isRepeatable = false;
for (const directive of directives) {
name = directive.name;
if (directive.description) {
description = directive.description;
}
for (const location of directive.locations) {
locations.add(location);
}
for (const arg of directive.args) {
args[arg.name] = arg;
}
isRepeatable = isRepeatable || directive.isRepeatable;
if (directive.extensions) {
extensionsSet.add(directive.extensions);
}
}
return new GraphQLDirective({
name,
description,
locations: Array.from(locations),
args,
isRepeatable,
extensions: extensionsSet.size > 0 ? mergeDeep([...extensionsSet]) : void 0
});
}
const backcompatOptions = { commentDescriptions: true };
function typeFromAST(node) {
switch (node.kind) {
case Kind.OBJECT_TYPE_DEFINITION:
return makeObjectType(node);
case Kind.INTERFACE_TYPE_DEFINITION:
return makeInterfaceType(node);
case Kind.ENUM_TYPE_DEFINITION:
return makeEnumType(node);
case Kind.UNION_TYPE_DEFINITION:
return makeUnionType(node);
case Kind.SCALAR_TYPE_DEFINITION:
return makeScalarType(node);
case Kind.INPUT_OBJECT_TYPE_DEFINITION:
return makeInputObjectType(node);
case Kind.DIRECTIVE_DEFINITION:
return makeDirective(node);
default:
return null;
}
}
function makeObjectType(node) {
const config = {
name: node.name.value,
description: getDescription(node, backcompatOptions),
interfaces: () => node.interfaces?.map(
(iface) => createNamedStub(iface.name.value, "interface")
) || [],
fields: () => node.fields != null ? makeFields(node.fields) : {},
astNode: node
};
return new GraphQLObjectType(config);
}
function makeInterfaceType(node) {
const config = {
name: node.name.value,
description: getDescription(node, backcompatOptions),
interfaces: () => node.interfaces?.map(
(iface) => createNamedStub(iface.name.value, "interface")
),
fields: () => node.fields != null ? makeFields(node.fields) : {},
astNode: node
};
return new GraphQLInterfaceType(config);
}
function makeEnumType(node) {
const values = node.values?.reduce(
(prev, value) => ({
...prev,
[value.name.value]: {
description: getDescription(value, backcompatOptions),
deprecationReason: getDeprecationReason(value),
astNode: value
}
}),
{}
) ?? {};
return new GraphQLEnumType({
name: node.name.value,
description: getDescription(node, backcompatOptions),
values,
astNode: node
});
}
function makeUnionType(node) {
return new GraphQLUnionType({
name: node.name.value,
description: getDescription(node, backcompatOptions),
types: () => node.types?.map((type) => createNamedStub(type.name.value, "object")) ?? [],
astNode: node
});
}
function makeScalarType(node) {
return new GraphQLScalarType({
name: node.name.value,
description: getDescription(node, backcompatOptions),
astNode: node,
// TODO: serialize default property setting can be dropped once
// upstream graphql-js TypeScript typings are updated, likely in v16
serialize: (value) => value
});
}
function makeInputObjectType(node) {
return new GraphQLInputObjectType({
name: node.name.value,
description: getDescription(node, backcompatOptions),
fields: () => node.fields ? makeValues(node.fields) : {},
astNode: node
});
}
function makeFields(nodes) {
return nodes.reduce(
(prev, node) => ({
...prev,
[node.name.value]: {
type: createStub(node.type, "output"),
description: getDescription(node, backcompatOptions),
args: makeValues(node.arguments ?? []),
deprecationReason: getDeprecationReason(node),
astNode: node
}
}),
{}
);
}
function makeValues(nodes) {
return nodes.reduce(
(prev, node) => ({
...prev,
[node.name.value]: {
type: createStub(node.type, "input"),
defaultValue: node.defaultValue !== void 0 ? valueFromASTUntyped(node.defaultValue) : void 0,
description: getDescription(node, backcompatOptions),
astNode: node
}
}),
{}
);
}
function isLocationValue(value) {
return value in DirectiveLocation;
}
function makeDirective(node) {
const locations = [];
for (const location of node.locations) {
const locationValue = location.value;
if (isLocationValue(locationValue)) {
locations.push(locationValue);
}
}
return new GraphQLDirective({
name: node.name.value,
description: node.description != null ? node.description.value : null,
locations,
isRepeatable: node.repeatable,
args: makeValues(node.arguments ?? []),
astNode: node
});
}
function getDeprecationReason(node) {
const deprecated = getDirectiveValues(GraphQLDeprecatedDirective, node);
return deprecated?.["reason"];
}
function buildTypeCandidates({
subschemas,
originalSubschemaMap,
types,
typeDefs,
parseOptions,
directiveMap,
schemaDefs,
mergeDirectives: isMergeDirectives
}) {
const directiveCandidates = new Map(
Object.entries(directiveMap).map(([name, directive]) => [
name,
/* @__PURE__ */ new Set([directive])
])
);
const extensions = [];
const typeCandidates = /* @__PURE__ */ Object.create(null);
let schemaDef;
let schemaExtensions = [];
let document;
let extraction;
if (typeDefs && !Array.isArray(typeDefs) || Array.isArray(typeDefs) && typeDefs.length) {
document = mergeTypeDefs(typeDefs, parseOptions);
extraction = extractDefinitions(document);
schemaDef = extraction.schemaDefs[0];
schemaExtensions = schemaExtensions.concat(extraction.schemaExtensions);
}
schemaDefs.schemaDef = schemaDef ?? schemaDefs.schemaDef;
schemaDefs.schemaExtensions = schemaExtensions;
const rootTypeNameMap = getRootTypeNameMap(schemaDefs);
for (const subschema of subschemas) {
const schema = subschema.transformedSchema = wrapSchema(subschema);
const rootTypeMap = getRootTypeMap(schema);
const rootTypes = getRootTypes(schema);
for (const [operation, rootType] of rootTypeMap.entries()) {
addTypeCandidate(typeCandidates, rootTypeNameMap[operation], {
type: rootType,
subschema: originalSubschemaMap.get(subschema),
transformedSubschema: subschema
});
}
if (isMergeDirectives === true) {
for (const directive of schema.getDirectives()) {
let directiveCandidatesForName = directiveCandidates.get(
directive.name
);
if (directiveCandidatesForName == null) {
directiveCandidatesForName = /* @__PURE__ */ new Set();
directiveCandidates.set(directive.name, directiveCandidatesForName);
}
directiveCandidatesForName.add(directive);
}
}
const originalTypeMap = schema.getTypeMap();
for (const typeName in originalTypeMap) {
const type = originalTypeMap[typeName];
if (isNamedType(type) && !isIntrospectionType(type) && !rootTypes.has(type)) {
addTypeCandidate(typeCandidates, type.name, {
type,
subschema: originalSubschemaMap.get(subschema),
transformedSubschema: subschema
});
}
}
}
if (document != null && extraction != null) {
for (const def of extraction.typeDefinitions) {
const type = typeFromAST(def);
if (!isNamedType(type)) {
throw new Error(`Expected to get named typed but got ${inspect(def)}`);
}
if (type != null) {
if (isInterfaceType(type)) {
try {
type.getInterfaces();
} catch {
Object.defineProperty(type, "_interfaces", {
value: []
});
}
}
addTypeCandidate(typeCandidates, type.name, { type });
}
}
for (const def of extraction.directiveDefs) {
const directive = typeFromAST(def);
if (!isDirective(directive)) {
throw new Error(
`Expected to get directive type but got ${inspect(def)}`
);
}
let directiveCandidatesForName = directiveCandidates.get(directive.name);
if (directiveCandidatesForName == null) {
directiveCandidatesForName = /* @__PURE__ */ new Set();
directiveCandidates.set(directive.name, directiveCandidatesForName);
}
directiveCandidatesForName.add(directive);
}
if (extraction.extensionDefs.length > 0) {
extensions.push({
...document,
definitions: extraction.extensionDefs
});
}
}
for (const type of types) {
addTypeCandidate(typeCandidates, type.name, { type });
}
for (const [
directiveName,
directiveCandidatesForName
] of directiveCandidates) {
directiveMap[directiveName] = mergeDirectives(directiveCandidatesForName);
}
return [typeCandidates, rootTypeNameMap, extensions];
}
function getRootTypeNameMap({
schemaDef,
schemaExtensions
}) {
const rootTypeNameMap = {
query: "Query",
mutation: "Mutation",
subscription: "Subscription"
};
const allNodes = schemaExtensions.slice();
if (schemaDef != null) {
allNodes.unshift(schemaDef);
}
for (const node of allNodes) {
if (node.operationTypes != null) {
for (const operationType of node.operationTypes) {
rootTypeNameMap[operationType.operation] = operationType.type.name.value;
}
}
}
return rootTypeNameMap;
}
function addTypeCandidate(typeCandidates, name, typeCandidate) {
if (!typeCandidates[name]) {
typeCandidates[name] = [];
}
typeCandidates[name].push(typeCandidate);
}
function buildTypes({
typeCandidates,
directives,
stitchingInfo,
rootTypeNames,
onTypeConflict,
mergeTypes,
typeMergingOptions
}) {
const typeMap = /* @__PURE__ */ Object.create(null);
for (const typeName in typeCandidates) {
if (rootTypeNames.includes(typeName) || mergeTypes === true && !typeCandidates[typeName]?.some(
(candidate) => isSpecifiedScalarType(candidate.type)
) || typeof mergeTypes === "function" && mergeTypes(typeCandidates[typeName], typeName) || Array.isArray(mergeTypes) && mergeTypes.includes(typeName) || stitchingInfo != null && typeName in stitchingInfo.mergedTypes) {
typeMap[typeName] = mergeCandidates(
typeName,
typeCandidates[typeName],
typeMergingOptions
);
} else {
const candidateSelector = onTypeConflict != null ? onTypeConflictToCandidateSelector(onTypeConflict) : (cands) => cands[cands.length - 1];
typeMap[typeName] = candidateSelector(typeCandidates[typeName]).type;
}
}
return rewireTypes(typeMap, directives);
}
function onTypeConflictToCandidateSelector(onTypeConflict) {
return (cands) => cands.reduce((prev, next) => {
const type = onTypeConflict(prev.type, next.type, {
left: {
subschema: prev.subschema,
transformedSubschema: prev.transformedSubschema
},
right: {
subschema: next.subschema,
transformedSubschema: next.transformedSubschema
}
});
if (prev.type === type) {
return prev;
} else if (next.type === type) {
return next;
}
return {
schemaName: "unknown",
type
};
});
}
function stitchSchemas({
subschemas = [],
types = [],
typeDefs = [],
onTypeConflict,
mergeDirectives,
mergeTypes = true,
typeMergingOptions,
subschemaConfigTransforms = [],
resolvers = {},
inheritResolversFromInterfaces = false,
resolverValidationOptions = {},
updateResolversInPlace = true,
schemaExtensions,
...rest
}) {
const transformedSubschemas = [];
const subschemaMap = /* @__PURE__ */ new Map();
const originalSubschemaMap = /* @__PURE__ */ new Map();
for (const subschema of subschemas) {
for (const transformedSubschemaConfig of applySubschemaConfigTransforms(
subschemaConfigTransforms,
subschema,
subschemaMap,
originalSubschemaMap
)) {
transformedSubschemas.push(transformedSubschemaConfig);
}
}
const directiveMap = /* @__PURE__ */ Object.create(null);
for (const directive of specifiedDirectives) {
directiveMap[directive.name] = directive;
}
const schemaDefs = /* @__PURE__ */ Object.create(null);
const [typeCandidates, rootTypeNameMap, extensions] = buildTypeCandidates({
subschemas: transformedSubschemas,
originalSubschemaMap,
types,
typeDefs: typeDefs || [],
parseOptions: rest,
directiveMap,
schemaDefs,
mergeDirectives
});
let stitchingInfo = createStitchingInfo(
subschemaMap,
typeCandidates,
mergeTypes
);
const { typeMap: newTypeMap, directives: newDirectives } = buildTypes({
typeCandidates,
directives: Object.values(directiveMap),
stitchingInfo,
rootTypeNames: Object.values(rootTypeNameMap),
onTypeConflict,
mergeTypes,
typeMergingOptions
});
let schema = new GraphQLSchema({
query: newTypeMap[rootTypeNameMap.query],
mutation: newTypeMap[rootTypeNameMap.mutation],
subscription: newTypeMap[rootTypeNameMap.subscription],
types: Object.values(newTypeMap),
directives: newDirectives,
astNode: schemaDefs.schemaDef,
extensionASTNodes: schemaDefs.schemaExtensions,
extensions: null,
assumeValid: rest.assumeValid
});
for (const extension of extensions) {
schema = extendSchema(schema, extension, {
commentDescriptions: true
});
}
const resolverMap = mergeResolvers(resolvers);
const finalResolvers = inheritResolversFromInterfaces ? extendResolversFromInterfaces(schema, resolverMap) : resolverMap;
stitchingInfo = completeStitchingInfo(stitchingInfo, finalResolvers, schema);
schema = addResolversToSchema({
schema,
defaultFieldResolver: defaultMergedResolver,
resolvers: finalResolvers,
resolverValidationOptions,
inheritResolversFromInterfaces: false,
updateResolversInPlace
});
const resolverValidationOptionsEntries = Object.entries(
resolverValidationOptions
);
if (resolverValidationOptionsEntries.length > 0 && resolverValidationOptionsEntries.some(([, o]) => o !== "ignore")) {
assertResolversPresent(schema, resolverValidationOptions);
}
addStitchingInfo(schema, stitchingInfo);
if (schemaExtensions) {
if (Array.isArray(schemaExtensions)) {
schemaExtensions = mergeExtensions(schemaExtensions);
}
applyExtensions(schema, schemaExtensions);
}
return schema;
}
const subschemaConfigTransformerPresets = [isolateComputedFieldsTransformer, splitMergedTypeEntryPointsTransformer];
function applySubschemaConfigTransforms(subschemaConfigTransforms, subschemaOrSubschemaConfig, subschemaMap, originalSubschemaMap) {
let subschemaConfig;
if (isSubschemaConfig(subschemaOrSubschemaConfig)) {
subschemaConfig = subschemaOrSubschemaConfig;
} else if (subschemaOrSubschemaConfig instanceof GraphQLSchema) {
subschemaConfig = { schema: subschemaOrSubschemaConfig };
} else {
throw new TypeError(
"Received invalid input." + inspect(subschemaOrSubschemaConfig)
);
}
const transformedSubschemaConfigs = subschemaConfigTransforms.concat(subschemaConfigTransformerPresets).reduce(
(transformedSubschemaConfigs2, subschemaConfigTransform) => transformedSubschemaConfigs2.flatMap(
(ssConfig) => subschemaConfigTransform(ssConfig)
),
[subschemaConfig]
);
const transformedSubschemas = transformedSubschemaConfigs.map(
(ssConfig) => new Subschema(ssConfig)
);
const baseSubschema = transformedSubschemas[0];
subschemaMap.set(subschemaOrSubschemaConfig, baseSubschema);
for (const subschema of transformedSubschemas) {
originalSubschemaMap.set(subschema, subschemaOrSubschemaConfig);
}
return transformedSubschemas;
}
const forwardArgsToSelectionSet = (selectionSet, mapping) => {
const selectionSetDef = parseSelectionSet(selectionSet, { noLocation: true });
return (field) => {
const selections = selectionSetDef.selections.map(
(selectionNode) => {
if (selectionNode.kind === Kind.FIELD) {
if (!mapping) {
return { ...selectionNode, arguments: field.arguments?.slice() };
} else if (selectionNode.name.value in mapping) {
const selectionArgs = mapping[selectionNode.name.value];
return {
...selectionNode,
arguments: field.arguments?.filter(
(arg) => !!selectionArgs?.includes(arg.name.value)
)
};
}
}
return selectionNode;
}
);
return { ...selectionSetDef, selections };
};
};
const defaultRelayMergeConfig = {
selectionSet: `{ id }`,
fieldName: "node",
args: ({ id }) => ({ id })
};
function handleRelaySubschemas(subschemas, getTypeNameFromId) {
const typeNames = [];
for (const subschema of subschemas) {
const nodeType = subschema.schema.getType("Node");
if (nodeType) {
if (!isInterfaceType(nodeType)) {
throw new Error(`Node type should be an interface!`);
}
const implementations = subschema.schema.getPossibleTypes(nodeType);
for (const implementedType of implementations) {
typeNames.push(implementedType.name);
subschema.merge = subschema.merge || {};
subschema.merge[implementedType.name] = defaultRelayMergeConfig;
}
}
}
const relaySubschemaConfig = {
schema: makeExecutableSchema({
typeDefs: (
/* GraphQL */
`
type Query {
node(id: ID!): Node
}
interface Node {
id: ID!
}
${typeNames.map(
(typeName) => `
type ${typeName} implements Node {
id: ID!
}
`
).join("\n")}
`
),
resolvers: {
Query: {
node: (_, { id }) => ({ id })
},
Node: {
__resolveType: ({ id }, _, info) => {
if (!getTypeNameFromId) {
const possibleTypeNames = /* @__PURE__ */ new Set();
for (const fieldNode of info.fieldNodes) {
if (fieldNode.selectionSet?.selections) {
for (const selection of fieldNode.selectionSet?.selections || []) {
switch (selection.kind) {
case Kind.FRAGMENT_SPREAD: {
const fragment = info.fragments[selection.name.value];
if (!fragment) {
throw new Error(
`Cannot find fragment ${selection.name.value}`
);
}
possibleTypeNames.add(
fragment.typeCondition.name.value
);
break;
}
case Kind.INLINE_FRAGMENT: {
const possibleTypeName = selection.typeCondition?.name.value;
if (possibleTypeName) {
possibleTypeNames.add(possibleTypeName);
}
break;
}
}
}
}
}
if (possibleTypeNames.size !== 1) {
console.warn(
`You need to define getTypeNameFromId as a parameter to handleRelaySubschemas or add a fragment for "node" operation with specific single type condition!`
);
}
return [...possibleTypeNames][0] || typeNames[0];
}
return getTypeNameFromId(id);
}
}
}
})
};
subschemas.push(relaySubschemaConfig);
return subschemas;
}
function createStitchingExecutor(stitchedSchema) {
const subschemas = [
...(stitchedSchema.extensions?.["stitchingInfo"]).subschemaMap.values()
];
return async function stitchingExecutor(executorRequest) {
const fragments = getFragmentsFromDocument(executorRequest.document);
const operation = getOperationASTFromRequest(executorRequest);
const rootType = getDefinedRootType(stitchedSchema, operation.operation);
const { fields } = collectFields(
stitchedSchema,
fragments,
executorRequest.variables,
rootType,
operation.selectionSet
);
const data = {};
for (const [fieldName, fieldNodes] of fields) {
const responseKey = fieldNodes[0]?.alias?.value ?? fieldName;
const subschemaForField = subschemas.find((subschema) => {
const subschemaSchema = isSubschemaConfig(subschema) ? subschema.schema : subschema;
const rootType2 = getDefinedRootType(
subschemaSchema,
operation.operation
);
return rootType2.getFields()[fieldName] != null;
});
let result = await delegateToSchema({
schema: subschemaForField || stitchedSchema,
rootValue: executorRequest.rootValue,
context: executorRequest.context,
info: {
schema: stitchedSchema,
fieldName,
fieldNodes,
operation,
fragments,
parentType: rootType,
returnType: rootType.getFields()[fieldName]?.type,
variableValues: executorRequest.variables
}
});
if (Array.isArray(result)) {
result = await Promise.all(result);
}
data[responseKey] = result;
}
return { data };
};
}
export { ValidationLevel, calculateSelectionScore, createMergedTypeResolver, createStitchingExecutor, forwardArgsToSelectionSet, getDefaultFieldConfigMerger, handleRelaySubschemas, isolateComputedFieldsTransformer, splitMergedTypeEntryPointsTransformer, stitchSchemas };