UNPKG

@graphql-tools/delegate

Version:

A set of utils for faster development of GraphQL tools

1,492 lines (1,478 loc) • 92.8 kB
'use strict'; var utils = require('@graphql-tools/utils'); var graphql = require('graphql'); var promiseHelpers = require('@whatwg-node/promise-helpers'); var executor = require('@graphql-tools/executor'); var batchExecute = require('@graphql-tools/batch-execute'); var repeater = require('@repeaterjs/repeater'); var merge = require('dset/merge'); const applySchemaTransforms = utils.memoize2(function applySchemaTransforms2(originalWrappingSchema, subschemaConfig) { const schemaTransforms = subschemaConfig.transforms; if (schemaTransforms == null) { return originalWrappingSchema; } return schemaTransforms.reduce( (schema, transform) => transform.transformSchema?.(schema, subschemaConfig) || schema, originalWrappingSchema ); }); function isSubschema(value) { return Boolean(value.transformedSchema); } class Subschema { name; schema; executor; batch; batchingOptions; createProxyingResolver; transforms; _transformedSchema; merge; constructor(config) { this.name = config.name; this.schema = config.schema; this.executor = config.executor; this.batch = config.batch; this.batchingOptions = config.batchingOptions; this.createProxyingResolver = config.createProxyingResolver; this.transforms = config.transforms ?? []; this.merge = config.merge; } get transformedSchema() { if (!this._transformedSchema) { if (globalThis.process?.env?.["DEBUG"] != null) { console.warn( "Transformed schema is not set yet. Returning a dummy one." ); } this._transformedSchema = applySchemaTransforms(this.schema, this); } return this._transformedSchema; } set transformedSchema(value) { this._transformedSchema = value; } } const prototypePollutingKeys = [ "__proto__", "constructor", "prototype" ]; function isPrototypePollutingKey(key) { return prototypePollutingKeys.includes(key); } const leftOverByDelegationPlan = /* @__PURE__ */ new WeakMap(); const PLAN_LEFT_OVER = Symbol("PLAN_LEFT_OVER"); function getPlanLeftOverFromParent(parent) { if (parent != null && typeof parent === "object") { return parent[PLAN_LEFT_OVER]; } return void 0; } const UNPATHED_ERRORS_SYMBOL = Symbol.for("subschemaErrors"); const OBJECT_SUBSCHEMA_SYMBOL = Symbol.for("initialSubschema"); const FIELD_SUBSCHEMA_MAP_SYMBOL = Symbol.for("subschemaMap"); function isExternalObject(data) { return data[UNPATHED_ERRORS_SYMBOL] !== void 0; } function annotateExternalObject(object, errors, subschema, subschemaMap) { Object.defineProperties(object, { [OBJECT_SUBSCHEMA_SYMBOL]: { value: subschema, writable: true }, [FIELD_SUBSCHEMA_MAP_SYMBOL]: { value: subschemaMap, writable: true }, [UNPATHED_ERRORS_SYMBOL]: { value: errors, writable: true } }); return object; } function getSubschema(object, responseKey) { return object[FIELD_SUBSCHEMA_MAP_SYMBOL]?.[responseKey] ?? object[OBJECT_SUBSCHEMA_SYMBOL]; } function getUnpathedErrors(object) { return object[UNPATHED_ERRORS_SYMBOL]; } const EMPTY_ARRAY = []; const EMPTY_OBJECT = /* @__PURE__ */ Object.create(null); const getActualFieldNodes = utils.memoize1(function(fieldNode) { return [fieldNode]; }); function mergeFields(mergedTypeInfo, object, sourceSubschema, context, info) { const delegationMaps = mergedTypeInfo.delegationPlanBuilder( info.schema, sourceSubschema, info.variableValues != null && Object.keys(info.variableValues).length > 0 ? info.variableValues : EMPTY_OBJECT, info.fragments != null && Object.keys(info.fragments).length > 0 ? info.fragments : EMPTY_OBJECT, info.fieldNodes?.length ? info.fieldNodes.length === 1 && info.fieldNodes[0] ? getActualFieldNodes(info.fieldNodes[0]) : info.fieldNodes : EMPTY_ARRAY, context, info ); const leftOver = leftOverByDelegationPlan.get(delegationMaps); if (leftOver) { object[PLAN_LEFT_OVER] = leftOver; } return promiseHelpers.handleMaybePromise( () => utils.promiseReduce( delegationMaps, (_, delegationMap) => executeDelegationStage( mergedTypeInfo, delegationMap, object, context, info ), void 0 ), () => object ); } function handleResolverResult(resolverResult, subschema, selectionSet, object, combinedFieldSubschemaMap, info, path, combinedErrors) { if (resolverResult instanceof Error || resolverResult == null) { const schema = subschema.transformedSchema || info.schema; const type = schema.getType(object.__typename); const { fields } = utils.collectFields( schema, info.fragments, info.variableValues, type, selectionSet ); const nullResult = {}; for (const [responseKey, fieldNodes] of fields) { const combinedPath = [...path, responseKey]; if (resolverResult instanceof graphql.GraphQLError) { if (resolverResult.message.includes( "Cannot return null for non-nullable field" )) { nullResult[responseKey] = null; } else { nullResult[responseKey] = utils.relocatedError( resolverResult, combinedPath ); } } else if (resolverResult instanceof Error) { nullResult[responseKey] = graphql.locatedError( resolverResult, fieldNodes, combinedPath ); } else { nullResult[responseKey] = null; } } resolverResult = nullResult; } else { if (resolverResult[UNPATHED_ERRORS_SYMBOL]) { combinedErrors.push(...resolverResult[UNPATHED_ERRORS_SYMBOL]); } } const objectSubschema = resolverResult[OBJECT_SUBSCHEMA_SYMBOL]; const fieldSubschemaMap = resolverResult[FIELD_SUBSCHEMA_MAP_SYMBOL]; for (const responseKey in resolverResult) { if (isPrototypePollutingKey(responseKey)) { continue; } const existingPropValue = object[responseKey]; const sourcePropValue = resolverResult[responseKey]; if (responseKey === "__typename" && existingPropValue !== sourcePropValue && graphql.isAbstractType(subschema.transformedSchema.getType(sourcePropValue))) { continue; } if (sourcePropValue != null || existingPropValue == null) { if (existingPropValue != null && typeof existingPropValue === "object" && !(existingPropValue instanceof Error) && Object.keys(existingPropValue).length > 0) { if (Array.isArray(existingPropValue) && Array.isArray(sourcePropValue) && existingPropValue.length === sourcePropValue.length) { object[responseKey] = existingPropValue.map( (existingElement, index) => sourcePropValue instanceof Error ? existingElement : utils.mergeDeep( [existingElement, sourcePropValue[index]], void 0, true, true ) ); } else if (!(sourcePropValue instanceof Error)) { object[responseKey] = utils.mergeDeep( [existingPropValue, sourcePropValue], void 0, true, true ); } } else { object[responseKey] = sourcePropValue; } } combinedFieldSubschemaMap[responseKey] = fieldSubschemaMap?.[responseKey] ?? objectSubschema ?? subschema; } } function executeDelegationStage(mergedTypeInfo, delegationMap, object, context, info) { const combinedErrors = object[UNPATHED_ERRORS_SYMBOL]; const path = utils.pathToArray(info.path); const combinedFieldSubschemaMap = object[FIELD_SUBSCHEMA_MAP_SYMBOL]; const jobs = []; for (const [subschema, selectionSet] of delegationMap) { const schema = subschema.transformedSchema || info.schema; const type = schema.getType(object.__typename); const resolver = mergedTypeInfo.resolvers.get(subschema); if (resolver) { try { const resolverResult$ = resolver( object, context, info, subschema, selectionSet, void 0, type ); if (promiseHelpers.isPromise(resolverResult$)) { jobs.push( resolverResult$.then( (resolverResult) => handleResolverResult( resolverResult, subschema, selectionSet, object, combinedFieldSubschemaMap, info, path, combinedErrors ), (error) => handleResolverResult( error, subschema, selectionSet, object, combinedFieldSubschemaMap, info, path, combinedErrors ) ) ); } else { handleResolverResult( resolverResult$, subschema, selectionSet, object, combinedFieldSubschemaMap, info, path, combinedErrors ); } } catch (error) { handleResolverResult( error, subschema, selectionSet, object, combinedFieldSubschemaMap, info, path, combinedErrors ); } } } if (jobs.length) { if (jobs.length === 1) { return jobs[0]; } return Promise.all(jobs); } } function resolveExternalValue(result, unpathedErrors, subschema, context, info, returnType = getReturnType$1(info), skipTypeMerging) { const type = graphql.getNullableType(returnType); if (result instanceof Error) { return result; } if (result == null) { return reportUnpathedErrorsViaNull(unpathedErrors); } if (graphql.isLeafType(type)) { try { return type.parseValue(result); } catch { return null; } } else if (graphql.isCompositeType(type)) { return promiseHelpers.handleMaybePromise( () => resolveExternalObject( type, result, unpathedErrors, subschema, context, info, skipTypeMerging ), (result2) => { if (info && graphql.isAbstractType(type)) { if (result2.__typename != null) { const resolvedType = info.schema.getType(result2.__typename); if (!resolvedType) { return null; } } return result2; } return result2; } ); } else if (graphql.isListType(type)) { if (Array.isArray(result)) { return resolveExternalList( type, result, unpathedErrors, subschema, context, info, skipTypeMerging ); } return resolveExternalValue( result, unpathedErrors, subschema, context, info, type.ofType, skipTypeMerging ); } } function resolveExternalObject(type, object, unpathedErrors, subschema, context, info, skipTypeMerging) { if (!isExternalObject(object)) { annotateExternalObject( object, unpathedErrors, subschema, /* @__PURE__ */ Object.create(null) ); } if (skipTypeMerging || info == null) { return object; } const stitchingInfo = info.schema.extensions?.["stitchingInfo"]; if (stitchingInfo == null) { return object; } let mergedTypeInfo; const possibleTypeNames = [object.__typename, type.name]; for (const possibleTypeName of possibleTypeNames) { if (possibleTypeName != null && stitchingInfo.mergedTypes[possibleTypeName]?.targetSubschemas?.get( subschema )?.length) { mergedTypeInfo = stitchingInfo.mergedTypes[possibleTypeName]; break; } } if (!mergedTypeInfo) { return object; } return mergeFields( mergedTypeInfo, object, subschema, context, info ); } function resolveExternalList(type, list, unpathedErrors, subschema, context, info, skipTypeMerging) { return list.map( (listMember) => resolveExternalValue( listMember, unpathedErrors, subschema, context, info, type.ofType, skipTypeMerging ) ); } const reportedErrors = /* @__PURE__ */ new WeakMap(); function reportUnpathedErrorsViaNull(unpathedErrors) { if (unpathedErrors.length) { const unreportedErrors = []; for (const error of unpathedErrors) { if (!reportedErrors.has(error)) { unreportedErrors.push(error); reportedErrors.set(error, true); } } if (unreportedErrors.length) { const unreportedError = unreportedErrors[0]; if (unreportedErrors.length === 1 && unreportedError) { return graphql.locatedError( unreportedError, void 0, unreportedError.path ); } return new AggregateError( unreportedErrors.map( (e) => ( // We cast path as any for GraphQL.js 14 compat // locatedError path argument must be defined, but it is just forwarded to a constructor that allows a undefined value // https://github.com/graphql/graphql-js/blob/b4bff0ba9c15c9d7245dd68556e754c41f263289/src/error/locatedError.js#L25 // https://github.com/graphql/graphql-js/blob/b4bff0ba9c15c9d7245dd68556e754c41f263289/src/error/GraphQLError.js#L19 graphql.locatedError(e, void 0, unreportedError?.path) ) ), unreportedErrors.map((error) => error.message).join(", \n") ); } } return null; } function getReturnType$1(info) { if (info == null) { throw new Error(`Return type cannot be inferred without a source schema.`); } return info.returnType; } function checkResultAndHandleErrors(result = { data: null, errors: [] }, delegationContext) { const { context, info, fieldName: responseKey = getResponseKey(info), subschema, returnType = getReturnType(info), skipTypeMerging, onLocatedError } = delegationContext; const { data, unpathedErrors } = mergeDataAndErrors( result.data == null ? void 0 : result.data[responseKey], result.errors == null ? [] : result.errors, info != null && info.path ? graphql.responsePathAsArray(info.path) : void 0, onLocatedError ); return resolveExternalValue( data, unpathedErrors, subschema, context, info, returnType, skipTypeMerging ); } function mergeDataAndErrors(data, errors, path, onLocatedError, index = 1) { if (data == null) { if (!errors.length) { return { data: null, unpathedErrors: [] }; } if (errors.length === 1 && errors[0]) { const error = onLocatedError ? onLocatedError(errors[0]) : errors[0]; const newPath = path === void 0 ? error.path : !error.path ? path : path.concat(error.path.slice(1)); return { data: utils.relocatedError(errors[0], newPath), unpathedErrors: [] }; } const combinedError = new AggregateError( errors.map( (e) => ( // We cast path as any for GraphQL.js 14 compat // locatedError path argument must be defined, but it is just forwarded to a constructor that allows a undefined value // https://github.com/graphql/graphql-js/blob/b4bff0ba9c15c9d7245dd68556e754c41f263289/src/error/locatedError.js#L25 // https://github.com/graphql/graphql-js/blob/b4bff0ba9c15c9d7245dd68556e754c41f263289/src/error/GraphQLError.js#L19 graphql.locatedError(e, void 0, path) ) ), errors.map((error) => error.message).join(", \n") ); return { data: combinedError, unpathedErrors: [] }; } if (!errors.length) { return { data, unpathedErrors: [] }; } const unpathedErrors = []; const errorMap = /* @__PURE__ */ new Map(); for (const error of errors) { const pathSegment = error.path?.[index]; if (pathSegment != null) { let pathSegmentErrors = errorMap.get(pathSegment); if (pathSegmentErrors === void 0) { pathSegmentErrors = [error]; errorMap.set(pathSegment, pathSegmentErrors); } else { pathSegmentErrors.push(error); } } else { unpathedErrors.push(error); } } for (const [pathSegment, pathSegmentErrors] of errorMap) { if (data[pathSegment] !== void 0) { const { data: newData, unpathedErrors: newErrors } = mergeDataAndErrors( data[pathSegment], pathSegmentErrors, path, onLocatedError, index + 1 ); data[pathSegment] = newData; unpathedErrors.push(...newErrors); } else { unpathedErrors.push(...pathSegmentErrors); } } return { data, unpathedErrors }; } function getResponseKey(info) { if (info == null) { throw new Error( `Data cannot be extracted from result without an explicit key or source schema.` ); } return utils.getResponseKeyFromInfo(info); } function getReturnType(info) { if (info == null) { throw new Error(`Return type cannot be inferred without a source schema.`); } return info.returnType; } function getDocumentMetadata(document) { const operations = []; const fragments = []; const fragmentNames = /* @__PURE__ */ new Set(); for (let i = 0; i < document.definitions.length; i++) { const def = document.definitions[i]; if (def?.kind === graphql.Kind.FRAGMENT_DEFINITION) { fragments.push(def); fragmentNames.add(def.name.value); } else if (def?.kind === graphql.Kind.OPERATION_DEFINITION) { operations.push(def); } } return { operations, fragments, fragmentNames }; } const getTypeInfo = utils.memoize1(function getTypeInfo2(schema) { return new graphql.TypeInfo(schema); }); const getTypeInfoWithType = utils.memoize2(function getTypeInfoWithType2(schema, type) { return graphql.versionInfo.major < 16 ? new graphql.TypeInfo(schema, void 0, type) : new graphql.TypeInfo(schema, type); }); function updateArgument(argumentNodes, variableDefinitionsMap, variableValues, argName, varName, type, value) { argumentNodes[argName] = { kind: graphql.Kind.ARGUMENT, name: { kind: graphql.Kind.NAME, value: argName }, value: { kind: graphql.Kind.VARIABLE, name: { kind: graphql.Kind.NAME, value: varName } } }; variableDefinitionsMap[varName] = { kind: graphql.Kind.VARIABLE_DEFINITION, variable: { kind: graphql.Kind.VARIABLE, name: { kind: graphql.Kind.NAME, value: varName } }, type: utils.astFromType(type) }; if (value !== void 0) { variableValues[varName] = value; return; } if (varName in variableValues) { delete variableValues[varName]; } } function createVariableNameGenerator(variableDefinitionMap) { let varCounter = 0; return (argName) => { let varName; do { varName = varCounter === 0 ? argName : `_v${varCounter.toString()}_${argName}`; varCounter++; } while (varName in variableDefinitionMap); return varName; }; } function finalizeGatewayDocument(targetSchema, fragments, operations, onOverlappingAliases, delegationContext) { let usedVariables = []; let usedFragments = []; const newOperations = []; let newFragments = []; const validFragments = []; const validFragmentsWithType = /* @__PURE__ */ Object.create(null); for (const fragment of fragments) { const typeName = fragment.typeCondition.name.value; const type = targetSchema.getType(typeName); if (type != null) { validFragments.push(fragment); validFragmentsWithType[fragment.name.value] = type; } } let fragmentSet = /* @__PURE__ */ Object.create(null); for (const operation of operations) { const type = utils.getDefinedRootType(targetSchema, operation.operation); const { selectionSet, usedFragments: operationUsedFragments, usedVariables: operationUsedVariables } = finalizeSelectionSet( targetSchema, type, validFragmentsWithType, operation.selectionSet, onOverlappingAliases ); usedFragments = union(usedFragments, operationUsedFragments); const { usedVariables: collectedUsedVariables, newFragments: collectedNewFragments, fragmentSet: collectedFragmentSet } = collectFragmentVariables( targetSchema, fragmentSet, validFragments, validFragmentsWithType, usedFragments, onOverlappingAliases ); const operationOrFragmentVariables = union( operationUsedVariables, collectedUsedVariables ); usedVariables = union(usedVariables, operationOrFragmentVariables); newFragments = collectedNewFragments; fragmentSet = collectedFragmentSet; const variableDefinitions = (operation.variableDefinitions ?? []).filter( (variable) => operationOrFragmentVariables.indexOf(variable.variable.name.value) !== -1 ); if (operation.operation === "subscription") { selectionSet.selections = selectionSet.selections.filter( (selection) => selection.kind !== graphql.Kind.FIELD || selection.name.value !== "__typename" ); } if (selectionSet.selections.length === 1 && selectionSet.selections[0] && selectionSet.selections[0].kind === graphql.Kind.FIELD && selectionSet.selections[0].name.value === "__typename") { continue; } newOperations.push({ kind: graphql.Kind.OPERATION_DEFINITION, operation: operation.operation, name: operation.name, directives: operation.directives, variableDefinitions, selectionSet }); } if (!newOperations.length) { throw utils.createGraphQLError( "Failed to create a gateway request. The request must contain at least one operation.", { extensions: { [executor.CRITICAL_ERROR]: true } } ); } let newDocument = { kind: graphql.Kind.DOCUMENT, definitions: [...newOperations, ...newFragments] }; const stitchingInfo = delegationContext.info?.schema?.extensions?.["stitchingInfo"]; if (stitchingInfo != null) { const typeInfo = getTypeInfo(targetSchema); newDocument = graphql.visit( newDocument, graphql.visitWithTypeInfo(typeInfo, { [graphql.Kind.FIELD](fieldNode) { const parentType = typeInfo.getParentType(); if (parentType) { const parentTypeName = parentType.name; const typeConfig = stitchingInfo?.mergedTypes?.[parentTypeName]; if (typeConfig) { const providedSelectionsByField = typeConfig?.providedSelectionsByField?.get( delegationContext.subschema ); if (providedSelectionsByField) { const providedSelection = providedSelectionsByField[fieldNode.name.value]; if (providedSelection) { return { ...fieldNode, selectionSet: { kind: graphql.Kind.SELECTION_SET, selections: [ ...providedSelection.selections, ...fieldNode.selectionSet?.selections ?? [] ] } }; } } } } return fieldNode; } }) ); } return { usedVariables, newDocument }; } function finalizeGatewayRequest(originalRequest, delegationContext, onOverlappingAliases) { let { document, variables } = originalRequest; let { operations, fragments } = getDocumentMetadata(document); const { targetSchema, args } = delegationContext; if (args) { const requestWithNewVariables = addVariablesToRootFields( targetSchema, operations, args ); operations = requestWithNewVariables.newOperations; variables = Object.assign( {}, variables ?? {}, requestWithNewVariables.newVariables ); } const { usedVariables, newDocument } = finalizeGatewayDocument( targetSchema, fragments, operations, onOverlappingAliases, delegationContext ); const newVariables = {}; if (variables != null) { for (const variableName of usedVariables) { const variableValue = variables[variableName]; if (variableValue !== void 0) { newVariables[variableName] = variableValue; } } } return { ...originalRequest, document: newDocument, variables: newVariables }; } function isTypeNameField(selection) { return selection.kind === graphql.Kind.FIELD && !selection.alias && selection.name.value === "__typename"; } function filterTypenameFields(selections) { let hasTypeNameField = false; const filteredSelections = selections.filter((selection) => { if (isTypeNameField(selection)) { hasTypeNameField = true; return false; } return true; }); return { hasTypeNameField, selections: filteredSelections }; } function addVariablesToRootFields(targetSchema, operations, args) { const newVariables = /* @__PURE__ */ Object.create(null); const newOperations = operations.map((operation) => { const variableDefinitionMap = (operation.variableDefinitions ?? []).reduce( (prev, def) => ({ ...prev, [def.variable.name.value]: def }), {} ); const type = utils.getDefinedRootType(targetSchema, operation.operation); const newSelections = []; for (const selection of operation.selectionSet.selections) { if (selection.kind === graphql.Kind.FIELD) { const argumentNodes = selection.arguments ?? []; const argumentNodeMap = argumentNodes.reduce( (prev, argument) => ({ ...prev, [argument.name.value]: argument }), {} ); const targetField = type.getFields()[selection.name.value]; if (targetField != null) { updateArguments( targetField, argumentNodeMap, variableDefinitionMap, newVariables, args ); } newSelections.push({ ...selection, arguments: Object.values(argumentNodeMap) }); } else { newSelections.push(selection); } } const newSelectionSet = { kind: graphql.Kind.SELECTION_SET, selections: newSelections }; return { ...operation, variableDefinitions: Object.values(variableDefinitionMap), selectionSet: newSelectionSet }; }); return { newOperations, newVariables }; } function updateArguments(targetField, argumentNodeMap, variableDefinitionMap, variableValues, newArgs) { const generateVariableName = createVariableNameGenerator( variableDefinitionMap ); for (const argument of targetField.args) { const argName = argument.name; const argType = argument.type; if (argName in newArgs) { updateArgument( argumentNodeMap, variableDefinitionMap, variableValues, argName, generateVariableName(argName), argType, utils.serializeInputValue(argType, newArgs[argName]) ); } } } function collectFragmentVariables(targetSchema, fragmentSet, validFragments, validFragmentsWithType, usedFragments, onOverlappingAliases) { let remainingFragments = usedFragments.slice(); let usedVariables = []; const newFragments = []; while (remainingFragments.length !== 0) { const nextFragmentName = remainingFragments.pop(); const fragment = validFragments.find( (fr) => fr.name.value === nextFragmentName ); if (fragment != null) { const name = nextFragmentName; const typeName = fragment.typeCondition.name.value; const type = targetSchema.getType(typeName); if (type == null) { throw new Error( `Fragment reference type "${typeName}", but the type is not contained within the target schema.` ); } const { selectionSet, usedFragments: fragmentUsedFragments, usedVariables: fragmentUsedVariables } = finalizeSelectionSet( targetSchema, type, validFragmentsWithType, fragment.selectionSet, onOverlappingAliases ); remainingFragments = union(remainingFragments, fragmentUsedFragments); usedVariables = union(usedVariables, fragmentUsedVariables); if (name && !(name in fragmentSet)) { fragmentSet[name] = true; newFragments.push({ kind: graphql.Kind.FRAGMENT_DEFINITION, name: { kind: graphql.Kind.NAME, value: name }, typeCondition: fragment.typeCondition, selectionSet }); } } } return { usedVariables, newFragments, fragmentSet }; } const filteredSelectionSetVisitorKeys = { SelectionSet: ["selections"], Field: ["selectionSet"], InlineFragment: ["selectionSet"], FragmentDefinition: ["selectionSet"] }; const variablesVisitorKeys = { SelectionSet: ["selections"], Field: ["arguments", "directives", "selectionSet"], Argument: ["value"], InlineFragment: ["directives", "selectionSet"], FragmentSpread: ["directives"], FragmentDefinition: ["selectionSet"], ObjectValue: ["fields"], ObjectField: ["name", "value"], Directive: ["arguments"], ListValue: ["values"] }; function finalizeSelectionSet(schema, type, validFragments, selectionSet, onOverlappingAliases) { const usedFragments = []; const usedVariables = []; const typeInfo = getTypeInfoWithType(schema, type); const seenNonNullableMap = /* @__PURE__ */ new WeakMap(); const seenNullableMap = /* @__PURE__ */ new WeakMap(); const filteredSelectionSet = graphql.visit( selectionSet, graphql.visitWithTypeInfo(typeInfo, { [graphql.Kind.FIELD]: { enter: (node) => { const parentType = typeInfo.getParentType(); if (graphql.isObjectType(parentType) || graphql.isInterfaceType(parentType)) { const field = typeInfo.getFieldDef(); if (!field) { return null; } const args = field.args != null ? field.args : []; const argsMap = /* @__PURE__ */ Object.create(null); for (const arg of args) { argsMap[arg.name] = arg; } if (node.arguments != null) { const newArgs = []; for (const arg of node.arguments) { if (arg.name.value in argsMap) { newArgs.push(arg); } } if (newArgs.length !== node.arguments.length) { return { ...node, arguments: newArgs }; } } } if (graphql.isUnionType(parentType) && typeInfo.getType() == null) { const possibleTypeNames = []; const fieldName = node.name.value; for (const memberType of parentType.getTypes()) { const memberFields = memberType.getFields(); const possibleField = memberFields[fieldName]; if (possibleField != null) { const namedType = graphql.getNamedType(possibleField.type); if (node.selectionSet?.selections?.length && graphql.isLeafType(namedType)) { continue; } if (!node.selectionSet?.selections?.length && graphql.isCompositeType(namedType)) { continue; } possibleTypeNames.push(memberType.name); } } if (possibleTypeNames.length > 0) { return possibleTypeNames.map((possibleTypeName) => ({ kind: graphql.Kind.INLINE_FRAGMENT, typeCondition: { kind: graphql.Kind.NAMED_TYPE, name: { kind: graphql.Kind.NAME, value: possibleTypeName } }, selectionSet: { kind: graphql.Kind.SELECTION_SET, selections: [node] } })); } } return void 0; }, leave: (node) => { const type2 = typeInfo.getType(); if (type2 == null) { return null; } const namedType = graphql.getNamedType(type2); if (schema.getType(namedType.name) == null) { return null; } if (graphql.isObjectType(namedType) || graphql.isInterfaceType(namedType)) { const selections = node.selectionSet != null ? node.selectionSet.selections : null; if (selections == null || selections.length === 0) { return null; } } return void 0; } }, [graphql.Kind.FRAGMENT_SPREAD]: { enter: (node) => { if (!(node.name.value in validFragments)) { return null; } const parentType = typeInfo.getParentType(); const innerType = validFragments[node.name.value]; if (!utils.implementsAbstractType(schema, parentType, innerType)) { return null; } usedFragments.push(node.name.value); return void 0; } }, [graphql.Kind.SELECTION_SET]: { enter: (node, _key, _parent, _path) => { const parentType = typeInfo.getParentType(); const { hasTypeNameField, selections } = filterTypenameFields( node.selections ); if (hasTypeNameField || parentType != null && graphql.isAbstractType(parentType)) { selections.unshift({ kind: graphql.Kind.FIELD, name: { kind: graphql.Kind.NAME, value: "__typename" } }); } return { ...node, selections }; } }, [graphql.Kind.INLINE_FRAGMENT]: { enter: (node) => { if (node.typeCondition != null) { const parentType = typeInfo.getParentType(); const innerType = schema.getType(node.typeCondition.name.value); if (graphql.isUnionType(parentType) && parentType.getTypes().some((t) => t.name === innerType?.name)) { return node; } if (!utils.implementsAbstractType(schema, parentType, innerType)) { return null; } } return void 0; }, leave: (selection, _key, parent) => { if (!selection.selectionSet?.selections?.length) { return null; } if (Array.isArray(parent)) { const selectionTypeName = selection.typeCondition?.name.value; if (selectionTypeName) { const selectionType = schema.getType(selectionTypeName); if (selectionType && "getFields" in selectionType) { const selectionTypeFields = selectionType.getFields(); let seenNonNullable = seenNonNullableMap.get(parent); if (!seenNonNullable) { seenNonNullable = /* @__PURE__ */ new Set(); seenNonNullableMap.set(parent, seenNonNullable); } let seenNullable = seenNullableMap.get(parent); if (!seenNullable) { seenNullable = /* @__PURE__ */ new Set(); seenNullableMap.set(parent, seenNullable); } selection = { ...selection, selectionSet: { ...selection.selectionSet, selections: selection.selectionSet.selections.map( (subSelection) => { if (subSelection.kind === graphql.Kind.FIELD) { const fieldName = subSelection.name.value; if (!subSelection.alias) { const field = selectionTypeFields[fieldName]; if (field) { let currentNullable; if (graphql.isNullableType(field.type)) { seenNullable.add(fieldName); currentNullable = true; } else { seenNonNullable.add(fieldName); currentNullable = false; } if (seenNullable.has(fieldName) && seenNonNullable.has(fieldName)) { onOverlappingAliases(); return { ...subSelection, alias: { kind: graphql.Kind.NAME, value: currentNullable ? `_nullable_${fieldName}` : `_nonNullable_${fieldName}` } }; } } } } return subSelection; } ) } }; } } } const { selections } = filterTypenameFields( selection.selectionSet.selections ); if (selections.length === 0) { return null; } return { ...selection, selectionSet: { ...selection.selectionSet, selections }, // @defer is not available for the communication between the gw and subgraph directives: selection.directives?.filter?.( (directive) => directive.name.value !== "defer" ) }; } } }), // visitorKeys argument usage a la https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-source-graphql/src/batching/merge-queries.js // empty keys cannot be removed only because of typescript errors // will hopefully be fixed in future version of graphql-js to be optional filteredSelectionSetVisitorKeys ); graphql.visit( filteredSelectionSet, { [graphql.Kind.VARIABLE]: (variableNode) => { usedVariables.push(variableNode.name.value); } }, // visitorKeys argument usage a la https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-source-graphql/src/batching/merge-queries.js // empty keys cannot be removed only because of typescript errors // will hopefully be fixed in future version of graphql-js to be optional variablesVisitorKeys ); return { selectionSet: filteredSelectionSet, usedFragments, usedVariables }; } function union(...arrays) { const cache = /* @__PURE__ */ Object.create(null); const result = []; for (const array of arrays) { for (const item of array) { if (!(item in cache)) { cache[item] = true; result.push(item); } } } return result; } function prepareGatewayDocument(originalDocument, transformedSchema, returnType, infoSchema) { const wrappedConcreteTypesDocument = wrapConcreteTypes( returnType, transformedSchema, originalDocument ); if (infoSchema == null) { return wrappedConcreteTypesDocument; } const visitedSelections = /* @__PURE__ */ new WeakSet(); const { possibleTypesMap, reversePossibleTypesMap: reversePossibleTypesMap2, interfaceExtensionsMap, fieldNodesByType, fieldNodesByField, dynamicSelectionSetsByField } = getSchemaMetaData(infoSchema, transformedSchema); const { operations, fragments, fragmentNames } = getDocumentMetadata( wrappedConcreteTypesDocument ); const { expandedFragments, fragmentReplacements } = getExpandedFragments( fragments, fragmentNames, possibleTypesMap ); const typeInfo = getTypeInfo(transformedSchema); const expandedDocument = { kind: graphql.Kind.DOCUMENT, definitions: [...operations, ...fragments, ...expandedFragments] }; const visitorKeyMap = { Document: ["definitions"], OperationDefinition: ["selectionSet"], SelectionSet: ["selections"], Field: ["selectionSet"], InlineFragment: ["selectionSet"], FragmentDefinition: ["selectionSet"] }; return graphql.visit( expandedDocument, graphql.visitWithTypeInfo(typeInfo, { [graphql.Kind.SELECTION_SET]: (node) => visitSelectionSet( node, fragmentReplacements, transformedSchema, typeInfo, possibleTypesMap, reversePossibleTypesMap2, interfaceExtensionsMap, fieldNodesByType, fieldNodesByField, dynamicSelectionSetsByField, infoSchema, visitedSelections ) }), // visitorKeys argument usage a la https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-source-graphql/src/batching/merge-queries.js // empty keys cannot be removed only because of typescript errors // will hopefully be fixed in future version of graphql-js to be optional visitorKeyMap ); } const getExtraPossibleTypesFn = utils.memoize2(function getExtraPossibleTypes(transformedSchema, infoSchema) { const extraPossiblesTypesMap = /* @__PURE__ */ new Map(); return function getExtraPossibleTypes2(typeName) { let extraTypesForSubschema = extraPossiblesTypesMap.get(typeName); if (!extraTypesForSubschema) { extraTypesForSubschema = /* @__PURE__ */ new Set(); const gatewayType = infoSchema.getType(typeName); const subschemaType = transformedSchema.getType(typeName); if (graphql.isAbstractType(gatewayType) && graphql.isAbstractType(subschemaType)) { const possibleTypes = infoSchema.getPossibleTypes(gatewayType); const possibleTypesInSubschema = transformedSchema.getPossibleTypes(subschemaType); for (const possibleType of possibleTypes) { const possibleTypeInSubschema = transformedSchema.getType( possibleType.name ); if (!possibleTypeInSubschema) { continue; } if (possibleTypeInSubschema && possibleTypesInSubschema.some((t) => t.name === possibleType.name)) { continue; } extraTypesForSubschema.add(possibleType.name); } } extraPossiblesTypesMap.set(typeName, extraTypesForSubschema); } return extraTypesForSubschema; }; }); function visitSelectionSet(node, fragmentReplacements, transformedSchema, typeInfo, possibleTypesMap, reversePossibleTypesMap2, interfaceExtensionsMap, fieldNodesByType, fieldNodesByField, dynamicSelectionSetsByField, infoSchema, visitedSelections) { const newSelections = /* @__PURE__ */ new Set(); const maybeType = typeInfo.getParentType(); if (maybeType != null) { const parentType = graphql.getNamedType(maybeType); const parentTypeName = parentType.name; const fieldNodes = fieldNodesByType[parentTypeName]; if (fieldNodes) { for (const fieldNode of fieldNodes) { newSelections.add(fieldNode); } } const interfaceExtensions = interfaceExtensionsMap[parentType.name]; const interfaceExtensionFields = []; for (const selection of node.selections) { if (selection.kind === graphql.Kind.INLINE_FRAGMENT) { if (selection.typeCondition != null) { if (!visitedSelections.has(selection)) { visitedSelections.add(selection); const typeName = selection.typeCondition.name.value; const getExtraPossibleTypes2 = getExtraPossibleTypesFn( transformedSchema, infoSchema ); const extraPossibleTypes = getExtraPossibleTypes2(typeName); for (const extraPossibleTypeName of extraPossibleTypes) { newSelections.add({ ...selection, typeCondition: { kind: graphql.Kind.NAMED_TYPE, name: { kind: graphql.Kind.NAME, value: extraPossibleTypeName } } }); } const typeInSubschema = transformedSchema.getType(typeName); if (graphql.isObjectType(typeInSubschema) || graphql.isInterfaceType(typeInSubschema)) { const fieldMap = typeInSubschema.getFields(); for (const subSelection of selection.selectionSet.selections) { if (subSelection.kind === graphql.Kind.FIELD) { const fieldName = subSelection.name.value; const field = fieldMap[fieldName]; if (!field) { newSelections.add(subSelection); } } } } else if (!typeInSubschema) { for (const subSelection of selection.selectionSet.selections) { newSelections.add(subSelection); } } } const possibleTypes = possibleTypesMap[selection.typeCondition.name.value]; if (possibleTypes == null) { const fieldNodesForTypeName = fieldNodesByField[parentTypeName]?.["__typename"]; if (fieldNodesForTypeName) { for (const fieldNode of fieldNodesForTypeName) { newSelections.add(fieldNode); } } newSelections.add(selection); continue; } for (const possibleTypeName of possibleTypes) { const maybePossibleType = transformedSchema.getType(possibleTypeName); if (maybePossibleType != null && utils.implementsAbstractType( transformedSchema, parentType, maybePossibleType )) { newSelections.add( generateInlineFragment( possibleTypeName, selection.selectionSet ) ); } } if (possibleTypes.length === 0) { newSelections.add(selection); } } else { newSelections.add(selection); } } else if (selection.kind === graphql.Kind.FRAGMENT_SPREAD) { const fragmentName = selection.name.value; if (!fragmentReplacements[fragmentName]) { newSelections.add(selection); continue; } for (const replacement of fragmentReplacements[fragmentName]) { const typeName = replacement.typeName; const maybeReplacementType = transformedSchema.getType(typeName); if (maybeReplacementType != null && utils.implementsAbstractType(transformedSchema, parentType, maybeType)) { newSelections.add({ kind: graphql.Kind.FRAGMENT_SPREAD, name: { kind: graphql.Kind.NAME, value: replacement.fragmentName } }); } } } else { const fieldName = selection.name.value; if (graphql.isAbstractType(parentType)) { const fieldNodesForTypeName = fieldNodesByField[parentTypeName]?.["__typename"]; if (fieldNodesForTypeName) { for (const fieldNode of fieldNodesForTypeName) { newSelections.add(fieldNode); } } } const fieldNodesMapForType = fieldNodesByField[parentTypeName]; if (fieldNodesMapForType) { addDependenciesNestedly( selection, /* @__PURE__ */ new Set(), fieldNodesMapForType, newSelections ); } const dynamicSelectionSets = dynamicSelectionSetsByField[parentTypeName]?.[fieldName]; if (dynamicSelectionSets != null) { for (const selectionSetFn of dynamicSelectionSets) { const selectionSet = selectionSetFn(selection); if (selectionSet != null) { for (const selection2 of selectionSet.selections) { newSelections.add(selection2); } } } } if (interfaceExtensions?.[fieldName]) { interfaceExtensionFields.push(selection); } else { newSelections.add(selection); } } } if (reversePossibleTypesMap2[parentType.name]) { newSelections.add({ kind: graphql.Kind.FIELD, name: { kind: graphql.Kind.NAME, value: "__typename" } }); } if (interfaceExtensionFields.length) { const possibleTypes = possibleTypesMap[parentType.name]; if (possibleTypes != null) { for (const possibleType of possibleTypes) { newSelections.add( generateInlineFragment(possibleType, { kind: graphql.Kind.SELECTION_SET, selections: interfaceExtensionFields }) ); } } } return { ...node, selections: Array.from(newSelections) }; } return node; } function addDependenciesNestedly(fieldNode, seenFieldNames, fieldNodesByField, newSelections) { if (seenFieldNames.has(fieldNode.name.value)) { return; } seenFieldNames.add(fieldNode.name.value); const fieldNodes = fieldNodesByField[fieldNode.name.value]; if (fieldNodes != null) { for (const nestedFieldNode of fieldNodes) { newSelections.add(nestedFieldNode); addDependenciesNestedly( nestedFieldNode, seenFieldNames, fieldNodesByField, newSelections ); } } } function generateInlineFragment(typeName, selectionSet) { return { kind: graphql.Kind.INLINE_FRAGMENT, typeCondition: { kind: graphql.Kind.NAMED_TYPE, name: { kind: graphql.Kind.NAME, value: ty