UNPKG

@envelop/generic-auth

Version:

This plugin allows you to implement custom authentication flow by providing a custom user resolver based on the original HTTP request. The resolved user is injected into the GraphQL execution `context`, and you can use it in your resolvers to fetch the cu

176 lines (175 loc) • 7.44 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.removeEmptyOrUnusedNodes = removeEmptyOrUnusedNodes; /** * Sanitizes a GraphQL document node by removing empty and unused nodes. * This includes: * - Empty inline fragments * - Fields with empty selection sets * - Fragment spreads referencing empty fragments * - Unused fragment definitions * * The sanitization is performed iteratively until the document stabilizes, * ensuring that cascading cleanups (e.g., removing an empty inline fragment * that causes a parent field to become empty) are handled correctly. */ function removeEmptyOrUnusedNodes(documentNode) { let hasChanged = true; let document = { ...documentNode, definitions: [...documentNode.definitions], }; // Keep sanitizing until no more changes occur while (hasChanged) { hasChanged = false; // Create a map of all fragment definitions for quick lookup const fragmentMap = new Map(); for (const definition of document.definitions) { if (definition.kind === 'FragmentDefinition') { fragmentMap.set(definition.name.value, definition); } } // Sanitize all definitions (both operations and fragments) document.definitions = document.definitions.map(def => { if (def.kind === 'OperationDefinition') { const sanitized = sanitizeSelectionSet(def.selectionSet, fragmentMap); if (sanitized.changed) { hasChanged = true; } return { ...def, selectionSet: sanitized.selectionSet }; } else if (def.kind === 'FragmentDefinition') { const sanitized = sanitizeSelectionSet(def.selectionSet, fragmentMap); if (sanitized.changed) { hasChanged = true; } return { ...def, selectionSet: sanitized.selectionSet }; } return def; }); // Rebuild fragment map after potential sanitization const updatedFragmentMap = new Map(); for (const definition of document.definitions) { if (definition.kind === 'FragmentDefinition') { updatedFragmentMap.set(definition.name.value, definition); } } // Collect all fragments that are actually used const usedFragmentNames = new Set(); for (const definition of document.definitions) { if (definition.kind === 'OperationDefinition') { findUsedFragments(definition.selectionSet, updatedFragmentMap, usedFragmentNames); } else if (definition.kind === 'FragmentDefinition') { // Fragments can also reference other fragments findUsedFragments(definition.selectionSet, updatedFragmentMap, usedFragmentNames); } } // Remove unused fragment definitions const filteredDefinitions = document.definitions.filter(def => { if (def.kind === 'FragmentDefinition') { return usedFragmentNames.has(def.name.value); } return true; }); // Track if any fragments were removed if (filteredDefinitions.length !== document.definitions.length) { hasChanged = true; } document = { ...document, definitions: filteredDefinitions }; } return document; } /** * Sanitizes a selection set by removing empty selections. * Performs sanitization bottom-up (recursively sanitizes child selection sets first). */ function sanitizeSelectionSet(selectionSet, fragmentMap) { let selections = [...selectionSet.selections]; let anyChanged = false; // Recursively sanitize nested selection sets first (bottom-up approach) selections = selections.map(selection => { if (selection.kind === 'Field') { if (selection.selectionSet) { const sanitizedChildSet = sanitizeSelectionSet(selection.selectionSet, fragmentMap); if (sanitizedChildSet.changed) { anyChanged = true; } return { ...selection, selectionSet: sanitizedChildSet.selectionSet }; } } else if (selection.kind === 'InlineFragment') { const sanitizedChildSet = sanitizeSelectionSet(selection.selectionSet, fragmentMap); if (sanitizedChildSet.changed) { anyChanged = true; } return { ...selection, selectionSet: sanitizedChildSet.selectionSet }; } return selection; }); // Remove empty inline fragments const beforeInlineFilterLength = selections.length; selections = selections.filter(selection => { if (selection.kind === 'InlineFragment') { return selection.selectionSet.selections.length > 0; } return true; }); if (selections.length !== beforeInlineFilterLength) { anyChanged = true; } // Remove fields with empty selection sets (scalar fields have no selectionSet) const beforeFieldFilterLength = selections.length; selections = selections.filter(selection => { if (selection.kind === 'Field') { return !selection.selectionSet || selection.selectionSet.selections.length > 0; } return true; }); if (selections.length !== beforeFieldFilterLength) { anyChanged = true; } // Remove fragment spreads that reference empty fragments const beforeSpreadFilterLength = selections.length; selections = selections.filter(selection => { if (selection.kind === 'FragmentSpread') { const fragment = fragmentMap.get(selection.name.value); return !fragment || fragment.selectionSet.selections.length > 0; } return true; }); if (selections.length !== beforeSpreadFilterLength) { anyChanged = true; } // Return new selection set if anything changed or selections were modified if (anyChanged || selections.length !== selectionSet.selections.length) { return { selectionSet: { ...selectionSet, selections }, changed: true, }; } return { selectionSet, changed: false }; } /** * Recursively collects all fragment names that are used in a selection set. * This includes fragments referenced directly and fragments referenced by other fragments. */ function findUsedFragments(selectionSet, fragmentMap, usedFragments) { for (const selection of selectionSet.selections) { if (selection.kind === 'Field' && selection.selectionSet) { findUsedFragments(selection.selectionSet, fragmentMap, usedFragments); } else if (selection.kind === 'InlineFragment' && selection.selectionSet) { findUsedFragments(selection.selectionSet, fragmentMap, usedFragments); } else if (selection.kind === 'FragmentSpread') { const fragmentName = selection.name.value; usedFragments.add(fragmentName); // Also include fragments referenced by this fragment (recursive) const fragment = fragmentMap.get(fragmentName); if (fragment?.selectionSet) { findUsedFragments(fragment.selectionSet, fragmentMap, usedFragments); } } } }