UNPKG

@quell/server

Version:

Quell is an open-source NPM package providing a light-weight caching layer implementation and cache invalidation for GraphQL responses on both the client- and server-side. Use Quell to prevent redundant client-side API requests and to minimize costly serv

205 lines (204 loc) 7.39 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateMutationMap = exports.getFieldsMap = exports.getMutationMap = exports.getQueryMap = exports.anySchemaToQuellSchema = void 0; const graphql_1 = require("graphql"); /** * Converts any GraphQL schema to a standardized format that Quell can process * using GraphQL's introspection system. * * @param schema - Any valid GraphQL schema * @returns A standardized GraphQL schema that Quell can work with */ function anySchemaToQuellSchema(schema) { // console.log('+++QUELL+++ Standardizing schema for Quell') // Step 1: Get standardized introspection result const introspectionResult = (0, graphql_1.graphqlSync)({ schema, source: (0, graphql_1.getIntrospectionQuery)(), }); // Type assertion to handle the data property if (!introspectionResult.data) { throw new Error("Schema introspection failed"); } // Step 2: Build a client schema from the introspection result const standardizedSchema = (0, graphql_1.buildClientSchema)(introspectionResult.data); // Step 3: Return the standardized schema return standardizedSchema; } exports.anySchemaToQuellSchema = anySchemaToQuellSchema; /** * Gets the name of a GraphQL type, handling wrapped types like List and NonNull * * @param type - Any GraphQL type * @returns The name of the type, or a string representation if no name is available */ function getTypeName(type) { // Handle named types directly if ("name" in type && typeof type.name === "string") { return type.name; } // Handle wrapped types (NonNull, List) if ("ofType" in type && type.ofType) { return getTypeName(type.ofType); } // Fallback for other cases return String(type); } /** * Safely checks if a type is a List type */ function isList(type) { return type.toString().startsWith("[") && type.toString().endsWith("]"); } /** * Generates a map of queries to GraphQL object types using the public GraphQL API. * This works with any GraphQL schema regardless of how it was created. * * @param schema - Any valid GraphQL schema * @returns Map of queries to their return types */ function getQueryMap(schema) { const queryMap = {}; // Get the query type from the schema const queryType = schema.getQueryType(); if (!queryType) return queryMap; // Get all fields from the query type const fields = queryType.getFields(); // Process each query field for (const queryName in fields) { const field = fields[queryName]; let returnType = ""; // Check if field type is a list type if (isList(field.type)) { returnType = [getTypeName(field.type)]; } else { returnType = getTypeName(field.type); } queryMap[queryName] = returnType; } return queryMap; } exports.getQueryMap = getQueryMap; /** * Generates a map of mutations to GraphQL object types using the public GraphQL API. * This works with any GraphQL schema regardless of how it was created. * * @param schema - Any valid GraphQL schema * @returns Map of mutations to their affected types */ function getMutationMap(schema) { const mutationMap = {}; // Get the mutation type from the schema const mutationType = schema.getMutationType(); if (!mutationType) return mutationMap; // Get all fields from the mutation type const fields = mutationType.getFields(); // Process each mutation field for (const mutationName in fields) { const field = fields[mutationName]; let returnType = ""; // Check if field type is a list type if (isList(field.type)) { returnType = [getTypeName(field.type)]; } else { returnType = getTypeName(field.type); } mutationMap[mutationName] = returnType; } return mutationMap; } exports.getMutationMap = getMutationMap; /** * Generates a map of fields to GraphQL types using the public GraphQL API. * This works with any GraphQL schema regardless of how it was created. * * @param schema - Any valid GraphQL schema * @returns Map of fields to their GraphQL types */ function getFieldsMap(schema) { var _a, _b, _c; const fieldsMap = {}; // Get the type map from the schema using the public API const typeMap = schema.getTypeMap(); // GraphQL built-in types to exclude const builtInTypes = [ "String", "Int", "Float", "Boolean", "ID", "Query", "__Type", "__Field", "__EnumValue", "__DirectiveLocation", "__Schema", "__TypeKind", "__InputValue", "__Directive", ]; // Get query type name to exclude const queryTypeName = ((_a = schema.getQueryType()) === null || _a === void 0 ? void 0 : _a.name) || ""; const mutationTypeName = ((_b = schema.getMutationType()) === null || _b === void 0 ? void 0 : _b.name) || ""; const subscriptionTypeName = ((_c = schema.getSubscriptionType()) === null || _c === void 0 ? void 0 : _c.name) || ""; // Filter out built-in types and root operation types const customTypes = Object.keys(typeMap).filter((type) => !builtInTypes.includes(type) && type !== queryTypeName && type !== mutationTypeName && type !== subscriptionTypeName && !type.startsWith("__")); // Process each custom type for (const typeName of customTypes) { const type = typeMap[typeName]; // Skip if not an object type with fields if (!("getFields" in type) || typeof type.getFields !== "function") continue; const fieldsObj = {}; const fields = type.getFields(); // Process each field in the type for (const fieldName in fields) { const field = fields[fieldName]; const key = field.name; // Get the field type name safely fieldsObj[key] = getTypeName(field.type); } // Only add types that have fields if (Object.keys(fieldsObj).length > 0) { fieldsMap[typeName] = fieldsObj; } } return fieldsMap; } exports.getFieldsMap = getFieldsMap; /** * Determines which schema types are affected by a given mutation. * This is useful for intelligent cache invalidation. * * @param schema - Any valid GraphQL schema * @returns Map of mutations to the types they affect */ function generateMutationMap(schema) { const mutationMap = {}; const mutationType = schema.getMutationType(); if (!mutationType) return mutationMap; const mutationFields = mutationType.getFields(); for (const mutationName in mutationFields) { const mutation = mutationFields[mutationName]; // Get the return type name safely const typeName = getTypeName(mutation.type); // Start with the direct return type const affectedTypes = [typeName]; // For more sophisticated mapping, we could analyze: // 1. Arguments that reference other types // 2. Related types based on schema structure // 3. Custom directives that specify cache dependencies mutationMap[mutationName] = affectedTypes; } return mutationMap; } exports.generateMutationMap = generateMutationMap;