UNPKG

@pothos/core

Version:

Pothos (formerly GiraphQL) is a plugin based schema builder for creating code-first GraphQL schemas in typescript

512 lines (511 loc) 20.7 kB
function _define_property(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } import { defaultFieldResolver, defaultTypeResolver, GraphQLBoolean, GraphQLEnumType, GraphQLFloat, GraphQLID, GraphQLInputObjectType, GraphQLInt, GraphQLInterfaceType, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLScalarType, GraphQLString, GraphQLUnionType } from 'graphql'; import { PothosError, PothosSchemaError } from './errors.js'; import { MergedPlugins } from './plugins/index.js'; import { BuiltinScalarRef } from './refs/builtin-scalar.js'; import { typeBrandKey } from './types/index.js'; import { assertNever, getTypeBrand, isThenable } from './utils/index.js'; export class BuildCache { getTypeConfig(ref, kind) { const baseConfig = this.configStore.getTypeConfig(ref, kind); if (!this.typeConfigs.has(baseConfig.name)) { this.typeConfigs.set(baseConfig.name, this.plugin.onTypeConfig(baseConfig)); } const typeConfig = this.typeConfigs.get(baseConfig.name); return typeConfig; } getInputTypeFieldConfigs(ref) { const typeConfig = this.getTypeConfig(ref, "InputObject"); const builtType = this.types.get(typeConfig.name); if (!builtType) { throw new PothosSchemaError(`Input type ${typeConfig.name} has not been built yet`); } const fields = builtType.getFields(); const fieldConfigs = {}; for (const fieldName of Object.keys(fields)) { var _fields_fieldName_extensions; fieldConfigs[fieldName] = (_fields_fieldName_extensions = fields[fieldName].extensions) === null || _fields_fieldName_extensions === void 0 ? void 0 : _fields_fieldName_extensions.pothosConfig; } return fieldConfigs; } getImplementers(iface) { if (this.implementers.has(iface.name)) { return this.implementers.get(iface.name); } const implementers = [ ...this.configStore.typeConfigs.values() ].filter((config) => config.kind === "Object" && config.interfaces.find((i) => this.configStore.getTypeConfig(i).name === iface.name)); this.implementers.set(iface.name, implementers); return implementers; } buildAll() { this.configStore.prepareForBuild(); for (const config of this.configStore.typeConfigs.values()) { if (config.kind === "Enum" || config.kind === "Scalar") { this.buildTypeFromConfig(config); } } for (const config of this.configStore.typeConfigs.values()) { if (config.kind === "InputObject") { this.buildTypeFromConfig(config); } } for (const type of this.types.values()) { if (type instanceof GraphQLInputObjectType) { type.getFields(); } } for (const config of this.configStore.typeConfigs.values()) { if (config.kind === "Interface") { this.buildTypeFromConfig(config); } } for (const config of this.configStore.typeConfigs.values()) { if (config.kind === "Object") { this.buildTypeFromConfig(config); } } for (const config of this.configStore.typeConfigs.values()) { if (config.kind === "Union") { this.buildTypeFromConfig(config); } } for (const config of this.configStore.typeConfigs.values()) { if (config.kind === "Query" || config.kind === "Mutation" || config.kind === "Subscription") { this.buildTypeFromConfig(config); } } for (const type of this.types.values()) { if (type instanceof GraphQLObjectType || type instanceof GraphQLInterfaceType) { type.getFields(); } else if (type instanceof GraphQLUnionType) { type.getTypes(); } } } buildTypeFromConfig(baseConfig) { const config = this.getTypeConfig(baseConfig.name); const { name } = config; this.typeConfigs.set(name, config); switch (config.kind) { case "Enum": this.addType(name, this.buildEnum(config)); break; case "InputObject": this.addType(name, this.buildInputObject(config)); break; case "Interface": this.addType(name, this.buildInterface(config)); break; case "Scalar": this.addType(name, this.buildScalar(config)); break; case "Union": this.addType(name, this.buildUnion(config)); break; case "Object": case "Query": case "Mutation": case "Subscription": this.addType(name, this.buildObject(config)); break; default: assertNever(config); } } addType(ref, type) { if (this.types.has(ref)) { throw new PothosSchemaError(`reference or name has already been used to create another type (${type.name})`); } this.types.set(ref, type); } buildOutputTypeParam(type) { if (type.kind === "List") { if (type.nullable) { return new GraphQLList(this.buildOutputTypeParam(type.type)); } return new GraphQLNonNull(new GraphQLList(this.buildOutputTypeParam(type.type))); } if (type.nullable) { return this.getOutputType(type.ref); } return new GraphQLNonNull(this.getOutputType(type.ref)); } buildInputTypeParam(type) { if (type.kind === "List") { if (type.required) { return new GraphQLNonNull(new GraphQLList(this.buildInputTypeParam(type.type))); } return new GraphQLList(this.buildInputTypeParam(type.type)); } if (type.required) { return new GraphQLNonNull(this.getInputType(type.ref)); } return this.getInputType(type.ref); } buildFields(fields) { const built = {}; for (const [fieldName, originalConfig] of fields) { if (!this.outputFieldConfigs.has(originalConfig)) { this.outputFieldConfigs.set(originalConfig, this.plugin.onOutputFieldConfig(originalConfig)); } const updatedConfig = this.outputFieldConfigs.get(originalConfig); if (!updatedConfig) { continue; } const config = { ...updatedConfig }; const argMap = new Map(); for (const argName of Object.keys(config.args)) { argMap.set(argName, config.args[argName]); } const args = this.buildInputFields(argMap); const argConfigs = {}; for (const argName of Object.keys(config.args)) { argConfigs[argName] = this.inputFieldConfigs.get(config.args[argName]); } config.args = argConfigs; var _config_resolve; const resolve = this.plugin.wrapResolve((_config_resolve = config.resolve) !== null && _config_resolve !== void 0 ? _config_resolve : defaultFieldResolver, config); const subscribe = this.plugin.wrapSubscribe(config.subscribe, config); var _config_resolve1; built[fieldName] = { ...config, type: this.buildOutputTypeParam(config.type), args, extensions: { ...config.extensions, pothosResolveWrapped: resolve !== ((_config_resolve1 = config.resolve) !== null && _config_resolve1 !== void 0 ? _config_resolve1 : defaultFieldResolver), pothosSubscribeWrapped: subscribe !== config.subscribe, pothosOptions: config.pothosOptions, pothosConfig: config, pothosArgMappers: config.argMappers }, resolve: resolve === defaultFieldResolver ? undefined : resolve, subscribe }; } return built; } buildInputFields(fields) { const built = {}; for (const [fieldName, originalConfig] of fields) { if (!this.inputFieldConfigs.has(originalConfig)) { this.inputFieldConfigs.set(originalConfig, this.plugin.onInputFieldConfig(originalConfig)); } const config = this.inputFieldConfigs.get(originalConfig); if (config) { built[fieldName] = { ...config, type: this.buildInputTypeParam(config.type), extensions: { ...config.extensions, pothosOptions: config.pothosOptions, pothosConfig: config } }; } } return built; } getInterfaceFields(type) { const interfaceFields = type.getInterfaces() // biome-ignore lint/performance/noAccumulatingSpread: this is fine .reduce((all, iface) => Object.assign(all, this.getFields(iface)), {}); const configs = this.configStore.getFields(type.name, "Interface"); const fields = this.buildFields(configs); return { ...interfaceFields, ...fields }; } getObjectFields(type) { const interfaceFields = type.getInterfaces() // biome-ignore lint/performance/noAccumulatingSpread: this is fine .reduce((all, iface) => Object.assign(all, this.getFields(iface)), {}); const objectFields = this.buildFields(this.configStore.getFields(type.name, "Object")); return { ...interfaceFields, ...objectFields }; } getRootFields(type) { return this.buildFields(this.configStore.getFields(type.name, "Object")); } getFields(type) { if (type instanceof GraphQLObjectType) { const config = this.configStore.getTypeConfig(type.name); if (config.kind === "Query" || config.kind === "Mutation" || config.kind === "Subscription") { return this.getRootFields(type); } return this.getObjectFields(type); } if (type instanceof GraphQLInterfaceType) { return this.getInterfaceFields(type); } throw new PothosSchemaError(`Type ${type.name} does not have fields to resolve`); } getInputFields(type) { return this.buildInputFields(this.configStore.getFields(type.name, "InputObject")); } getType(ref) { if (ref instanceof BuiltinScalarRef) { return ref.type; } const typeConfig = this.configStore.getTypeConfig(ref); const type = this.types.get(typeConfig.name); if (!type) { this.buildTypeFromConfig(typeConfig); return this.types.get(typeConfig.name); } return type; } getOutputType(ref) { const type = this.getType(ref); if (type instanceof GraphQLInputObjectType) { throw new PothosSchemaError(`Expected ${String(ref)} to be an output type but it was defined as an InputObject`); } return type; } getInputType(ref) { const type = this.getType(ref); if (!type) { throw new PothosSchemaError(`Missing implementation of for type ${String(ref)}`); } if (type instanceof GraphQLObjectType) { throw new PothosSchemaError(`Expected ${type.name} to be an input type but it was defined as a GraphQLObjectType`); } if (type instanceof GraphQLInterfaceType) { throw new PothosSchemaError(`Expected ${type.name} to be an input type but it was defined as a GraphQLInterfaceType`); } if (type instanceof GraphQLUnionType) { throw new PothosSchemaError(`Expected ${String(ref)} to be an input type but it was defined as an GraphQLUnionType`); } return type; } getTypeOfKind(ref, kind) { const type = this.getType(ref); switch (kind) { case "Object": case "Query": case "Mutation": case "Subscription": if (type instanceof GraphQLObjectType) { return type; } break; case "Interface": if (type instanceof GraphQLInterfaceType) { return type; } break; case "Union": if (type instanceof GraphQLUnionType) { return type; } break; case "Enum": if (type instanceof GraphQLEnumType) { return type; } break; case "Scalar": if (type instanceof GraphQLScalarType) { return type; } break; case "InputObject": if (type instanceof GraphQLScalarType) { return type; } break; default: break; } throw new PothosSchemaError(`Expected ${String(ref)} to be of type ${kind}`); } buildObject(config) { var _config_isTypeOf; const type = new GraphQLObjectType({ ...config, extensions: { ...config.extensions, pothosOptions: config.pothosOptions, pothosConfig: config }, fields: () => this.getFields(type), isTypeOf: config.kind === "Object" ? this.plugin.wrapIsTypeOf((_config_isTypeOf = config.isTypeOf) !== null && _config_isTypeOf !== void 0 ? _config_isTypeOf : undefined, config) : undefined, interfaces: config.kind === "Object" ? () => config.interfaces.map((iface) => this.getTypeOfKind(iface, "Interface")) : undefined }); return type; } buildInterface(config) { const resolveType = (parent, context, info) => { const typeBrand = getTypeBrand(parent); if (typeBrand) { if (typeof typeBrand === "string") { return typeBrand; } return this.getTypeConfig(typeBrand).name; } var _config_resolveType; const resolver = (_config_resolveType = config.resolveType) !== null && _config_resolveType !== void 0 ? _config_resolveType : defaultTypeResolver; return resolver(parent, context, info, type); }; const type = new GraphQLInterfaceType({ ...config, extensions: { ...config.extensions, pothosOptions: config.pothosOptions, pothosConfig: config }, interfaces: () => config.interfaces.map((iface) => this.getTypeOfKind(iface, "Interface")), fields: () => this.getFields(type), resolveType: this.plugin.wrapResolveType(resolveType, config) }); return type; } buildUnion(config) { const resolveType = (parent, context, info, type) => { if (typeof parent === "object" && parent !== null && typeBrandKey in parent) { const typeBrand = parent[typeBrandKey]; if (typeof typeBrand === "string") { return typeBrand; } return this.getTypeConfig(typeBrand).name; } if (!config.resolveType) { return defaultTypeResolver(parent, context, info, type); } const resultOrPromise = config.resolveType(parent, context, info, type); const getResult = (result) => { if (typeof result === "string" || !result) { return result; } if (result instanceof GraphQLObjectType) { return result.name; } try { const typeConfig = this.configStore.getTypeConfig(result); return typeConfig.name; } catch { // ignore } return result; }; return isThenable(resultOrPromise) ? resultOrPromise.then(getResult) : getResult(resultOrPromise); }; return new GraphQLUnionType({ ...config, extensions: { ...config.extensions, pothosOptions: config.pothosOptions, pothosConfig: config }, types: () => config.types.map((member) => this.getTypeOfKind(member, "Object")), resolveType: this.plugin.wrapResolveType(resolveType, config) }); } buildInputObject(config) { const type = new GraphQLInputObjectType({ ...config, extensions: { ...config.extensions, pothosOptions: config.pothosOptions, pothosConfig: config }, fields: () => this.getInputFields(type) }); return type; } buildScalar(config) { if (config.name === "ID") { return GraphQLID; } if (config.name === "Int") { return GraphQLInt; } if (config.name === "Float") { return GraphQLFloat; } if (config.name === "Boolean") { return GraphQLBoolean; } if (config.name === "String") { return GraphQLString; } return new GraphQLScalarType({ ...config, extensions: { ...config.extensions, pothosOptions: config.pothosOptions, pothosConfig: config } }); } buildEnum(config) { const values = {}; const configValues = typeof config.values === "function" ? config.values() : config.values; for (const key of Object.keys(config.values)) { const original = configValues[key]; if (!this.enumValueConfigs.has(original)) { this.enumValueConfigs.set(original, this.plugin.onEnumValueConfig(original)); } const valueConfig = this.enumValueConfigs.get(original); if (valueConfig) { values[key] = this.enumValueConfigs.get(original); } } return new GraphQLEnumType({ ...config, values, extensions: { ...config.extensions, pothosOptions: config.pothosOptions, pothosConfig: config } }); } constructor(builder, options) { _define_property(this, "types", new Map()); _define_property(this, "builder", void 0); _define_property(this, "plugin", void 0); _define_property(this, "options", void 0); _define_property(this, "configStore", void 0); _define_property(this, "pluginList", void 0); _define_property(this, "implementers", new Map()); _define_property(this, "typeConfigs", new Map()); _define_property(this, "enumValueConfigs", new Map()); _define_property(this, "outputFieldConfigs", new Map()); _define_property(this, "inputFieldConfigs", new Map()); this.builder = builder; this.configStore = builder.configStore; this.options = options; const plugins = {}; var _builder_options_plugins; this.pluginList = ((_builder_options_plugins = builder.options.plugins) !== null && _builder_options_plugins !== void 0 ? _builder_options_plugins : []).map((pluginName) => { const Plugin = this.builder.constructor.plugins[pluginName]; if (!Plugin) { throw new PothosError(`No plugin named ${pluginName} was registered`); } plugins[pluginName] = new Plugin(this, pluginName); return plugins[pluginName]; }); this.plugin = new MergedPlugins(this, this.pluginList); } } //# sourceMappingURL=build-cache.js.map