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