UNPKG

graphql

Version:

A Query Language and Runtime which can target any service.

120 lines 4.67 kB
import { GraphQLError } from "../../error/GraphQLError.mjs"; import { OperationTypeNode } from "../../language/ast.mjs"; import { Kind } from "../../language/kinds.mjs"; import { GraphQLDeferDirective, GraphQLIncludeDirective, GraphQLSkipDirective, GraphQLStreamDirective, } from "../../type/directives.mjs"; function ifArgumentCanBeFalse(node) { const ifArgument = node.arguments?.find((arg) => arg.name.value === 'if'); if (!ifArgument) { return false; } if (ifArgument.value.kind === Kind.BOOLEAN) { if (ifArgument.value.value) { return false; } } else if (ifArgument.value.kind !== Kind.VARIABLE) { return false; } return true; } function canBeSkippedViaSkipDirective(node) { const ifArgument = node.arguments?.find((arg) => arg.name.value === 'if'); if (!ifArgument) { return true; } if (ifArgument.value.kind === Kind.BOOLEAN) { if (ifArgument.value.value) { return true; } return false; } return true; } function canBeSkippedViaIncludeDirective(node) { const ifArgument = node.arguments?.find((arg) => arg.name.value === 'if'); if (!ifArgument) { return false; } if (ifArgument?.value.kind === Kind.BOOLEAN) { if (ifArgument.value.value) { return false; } return true; } return true; } export function DeferStreamDirectiveOnValidOperationsRule(context) { return { OperationDefinition(operation) { if (operation.operation !== OperationTypeNode.SUBSCRIPTION) { return; } const document = context.getDocument(); const fragments = new Map(); for (const definition of document.definitions) { if (definition.kind === Kind.FRAGMENT_DEFINITION) { fragments.set(definition.name.value, definition); } } const visitedFragments = new Set(); forbidUnconditionalDeferStream({ context, fragments, selectionSet: operation.selectionSet, parentNodes: [], visitedFragments, }); }, }; } function forbidUnconditionalDeferStream({ context, fragments, selectionSet, parentNodes, visitedFragments, }) { for (const selection of selectionSet.selections) { const skip = selection.directives?.find((d) => d.name.value === GraphQLSkipDirective.name); if (skip && canBeSkippedViaSkipDirective(skip)) { continue; } const include = selection.directives?.find((d) => d.name.value === GraphQLIncludeDirective.name); if (include && canBeSkippedViaIncludeDirective(include)) { continue; } for (const directive of selection.directives ?? []) { if (directive.name.value === GraphQLDeferDirective.name) { if (!ifArgumentCanBeFalse(directive)) { context.reportError(new GraphQLError('Defer directive not supported on subscription operations. Disable `@defer` by setting the `if` argument to `false`.', { nodes: [directive, ...parentNodes] })); } } else if (directive.name.value === GraphQLStreamDirective.name) { if (!ifArgumentCanBeFalse(directive)) { context.reportError(new GraphQLError('Stream directive not supported on subscription operations. Disable `@stream` by setting the `if` argument to `false`.', { nodes: [directive, ...parentNodes] })); } } } if (selection.kind === 'FragmentSpread') { const fragmentName = selection.name.value; if (visitedFragments.has(fragmentName)) { continue; } visitedFragments.add(fragmentName); const fragment = fragments.get(fragmentName); if (fragment) { forbidUnconditionalDeferStream({ context, fragments, parentNodes: [selection, ...parentNodes], selectionSet: fragment?.selectionSet, visitedFragments, }); } } else if (selection.selectionSet) { forbidUnconditionalDeferStream({ context, fragments, selectionSet: selection.selectionSet, parentNodes, visitedFragments, }); } } } //# sourceMappingURL=DeferStreamDirectiveOnValidOperationsRule.js.map