import SchemaBuilder, { type InterfaceParam, type ObjectParam, type OutputRef, PothosSchemaError, type SchemaTypes, type ShapeFromTypeParam, } from '@pothos/core'; import { ImplementableLoadableNodeRef, LoadableNodeRef } from './refs/index.js'; import { ImplementableLoadableInterfaceRef } from './refs/interface.js'; import { ImplementableLoadableObjectRef } from './refs/object.js'; import { LoadableUnionRef } from './refs/union.js'; import type { DataloaderKey, DataloaderObjectTypeOptions, LoadableInterfaceOptions, LoadableNodeOptions, LoadableUnionOptions, ShapeFromLoadResult, } from './types.js'; import { dataloaderGetter } from './util.js'; const schemaBuilderProto = SchemaBuilder.prototype as PothosSchemaTypes.SchemaBuilder; schemaBuilderProto.loadableObjectRef = function loadableObjectRef(name, options) { return new ImplementableLoadableObjectRef(this, name, options as never); }; schemaBuilderProto.loadableInterfaceRef = function loadableInterfaceRef(name, options) { return new ImplementableLoadableInterfaceRef(this, name, options as never); }; schemaBuilderProto.loadableNodeRef = function loadableNodeRef(name, options) { return new ImplementableLoadableNodeRef(this, name, options as never); }; schemaBuilderProto.loadableObject = function loadableObject< LoadResult, Key extends DataloaderKey, const Interfaces extends InterfaceParam[], NameOrRef extends ObjectParam | string, CacheKey = Key, Shape = ShapeFromLoadResult, >( nameOrRef: NameOrRef, options: DataloaderObjectTypeOptions< SchemaTypes, LoadResult, Key, Interfaces, NameOrRef, CacheKey, Shape >, ) { const name = typeof nameOrRef === 'string' ? nameOrRef : ((options as { name?: string }).name ?? (nameOrRef as { name: string }).name); const ref = new ImplementableLoadableObjectRef( this, name, options as never, ); ref.implement(options); if (typeof nameOrRef !== 'string') { this.configStore.associateParamWithRef(nameOrRef, ref); } return ref; }; schemaBuilderProto.loadableInterface = function loadableInterface< LoadResult, Key extends DataloaderKey, const Interfaces extends InterfaceParam[], NameOrRef extends InterfaceParam | string, CacheKey = Key, Shape = ShapeFromLoadResult, >( nameOrRef: NameOrRef, options: LoadableInterfaceOptions< SchemaTypes, LoadResult, Key, Interfaces, NameOrRef, CacheKey, Shape >, ) { const name = typeof nameOrRef === 'string' ? nameOrRef : ((options as { name?: string }).name ?? (nameOrRef as { name: string }).name); const ref = new ImplementableLoadableInterfaceRef( this, name, options as never, ); ref.implement(options); if (typeof nameOrRef !== 'string') { this.configStore.associateParamWithRef(nameOrRef, ref); } return ref; }; schemaBuilderProto.loadableUnion = function loadableUnion< Key extends DataloaderKey, Member extends ObjectParam, CacheKey = Key, Shape = ShapeFromTypeParam, >( name: string, { load, toKey, sort, cacheResolved, loaderOptions, ...options }: LoadableUnionOptions, ) { const getDataloader = dataloaderGetter(loaderOptions, load, toKey, sort); const ref = new LoadableUnionRef(name, getDataloader); const unionRef = this.unionType(name, { ...options, extensions: { getDataloader, cacheResolved: typeof cacheResolved === 'function' ? cacheResolved : cacheResolved && toKey, }, }); this.configStore.associateParamWithRef(ref, unionRef); return ref; }; const TloadableNode = schemaBuilderProto.loadableNode; schemaBuilderProto.loadableNode = function loadableNode< LoadResult extends NameOrRef extends ObjectParam ? ShapeFromTypeParam | Error : unknown, const Interfaces extends InterfaceParam[], NameOrRef extends ObjectParam | string, IDShape extends bigint | number | string = string, Key extends bigint | number | string = IDShape, CacheKey = Key, Shape = ShapeFromLoadResult, >( this: PothosSchemaTypes.SchemaBuilder, nameOrRef: NameOrRef, options: LoadableNodeOptions< SchemaTypes, LoadResult, Interfaces, NameOrRef, IDShape, Key, CacheKey, Shape >, ) { if ( typeof (this as PothosSchemaTypes.SchemaBuilder & Record) .nodeInterfaceRef !== 'function' ) { throw new PothosSchemaError( 'builder.loadableNode requires @pothos/plugin-relay to be installed', ); } const name = typeof nameOrRef === 'string' ? nameOrRef : ((options as { name?: string }).name ?? (nameOrRef as { name: string }).name); const ref = new LoadableNodeRef( this, name, options as never, ); (this as typeof this & { node: (ref: unknown, options: unknown) => void }).node(ref, { ...options, extensions: { ...options.extensions, pothosParseGlobalID: options.id.parse, getDataloader: ref.getDataloader, cacheResolved: typeof options.cacheResolved === 'function' ? options.cacheResolved : options.cacheResolved && options.toKey, }, loadManyWithoutCache: (ids: Key[], context: SchemaTypes['Context']) => ref.getDataloader(context).loadMany(ids), isTypeOf: options.isTypeOf ?? (typeof nameOrRef === 'function' ? (maybeNode: unknown) => { if (!maybeNode) { return false; } if (maybeNode instanceof (nameOrRef as Function)) { return true; } const proto = Object.getPrototypeOf(maybeNode) as { constructor: unknown }; try { if (proto?.constructor) { const config = this.configStore.getTypeConfig(proto.constructor as OutputRef); return config.name === name; } } catch { // ignore } return false; } : undefined), }); if (typeof nameOrRef !== 'string') { this.configStore.associateParamWithRef(nameOrRef, ref); } return ref; } as unknown as typeof TloadableNode;