@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
JavaScript
"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;