UNPKG

@graphql-inspector/core

Version:

Tooling for GraphQL. Compare GraphQL Schemas, check documents, find breaking changes, find similar types.

102 lines (101 loc) 3.45 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.validateQueryDepth = validateQueryDepth; exports.calculateDepth = calculateDepth; exports.countDepth = countDepth; const graphql_1 = require("graphql"); function validateQueryDepth({ source, doc, maxDepth, fragmentGraph, }) { try { calculateDepth({ node: doc, currentDepth: 0, maxDepth, getFragment(name) { return fragmentGraph.getNodeData(name); }, }); } catch (errorOrNode) { if (errorOrNode instanceof Error) { throw errorOrNode; } const node = errorOrNode; return new graphql_1.GraphQLError(`Query exceeds maximum depth of ${maxDepth}`, node, source, node.loc?.start ? [node.loc.start] : undefined); } } function calculateDepth({ node, currentDepth, maxDepth, getFragment, }) { if (maxDepth && currentDepth > maxDepth) { throw node; } switch (node.kind) { case graphql_1.Kind.FIELD: { if (node.name.value.startsWith('__') || !node.selectionSet) { return 0; } const maxInnerDepth = calculateDepth({ node: node.selectionSet, currentDepth: currentDepth + 1, maxDepth, getFragment, }); return 1 + maxInnerDepth; } case graphql_1.Kind.SELECTION_SET: { return Math.max(...node.selections.map(selection => { return calculateDepth({ node: selection, currentDepth, maxDepth, getFragment, }); })); } case graphql_1.Kind.DOCUMENT: { return Math.max(...node.definitions.map(def => { return calculateDepth({ node: def, currentDepth, maxDepth, getFragment, }); })); } case graphql_1.Kind.OPERATION_DEFINITION: case graphql_1.Kind.INLINE_FRAGMENT: case graphql_1.Kind.FRAGMENT_DEFINITION: { return Math.max(...node.selectionSet.selections.map(selection => { return calculateDepth({ node: selection, currentDepth, maxDepth, getFragment, }); })); } case graphql_1.Kind.FRAGMENT_SPREAD: return calculateDepth({ node: getFragment(node.name.value), currentDepth, maxDepth, getFragment, }); default: { throw new Error(`Couldn't handle ${node.kind}`); } } } function countDepth(node, parentDepth, getFragmentReference) { let depth = parentDepth; if ('selectionSet' in node && node.selectionSet) { for (const child of node.selectionSet.selections) { depth = Math.max(depth, countDepth(child, parentDepth + 1, getFragmentReference)); } } if (node.kind === graphql_1.Kind.FRAGMENT_SPREAD) { const fragment = getFragmentReference(node.name.value); if (fragment) { depth = Math.max(depth, countDepth(fragment, parentDepth + 1, getFragmentReference)); } } return depth; }