import type { GQLSubscriptionAlgebra } from './algebra' import type { AURItoFieldAlgebra, AURItoInputAlgebra, FieldAURIS, InputAURIS } from './HKT' import type { TypeResolver } from './Resolver' import type { _I, _O, ParseLiteralF, ScalarConfig, ScalarFunctions } from './Scalar' import type { AnyObjectType, FieldRecord, InputRecord } from './types' import type { __A, __E, __R } from './util' import type { Compute } from '@principia/base/util/compute' import type { _A, _E, _R, UnionToIntersection } from '@principia/base/util/types' import type { Schema } from '@principia/schema' import type { CoreURIS } from '@principia/schema/Modules' import type { ValueNode } from 'graphql' import { flow, pipe } from '@principia/base/function' import * as Sy from '@principia/base/Sync' import * as Th from '@principia/base/These' import * as S from '@principia/schema' import * as Dec from '@principia/schema/Decoder' import * as Enc from '@principia/schema/Encoder' import { valueFromASTUntyped } from 'graphql' import { createScalarTypeDefinitionNode } from './AST' import { GQLException } from './GQLException' import { GQLExtendObject, GQLInputObject, GQLInterface, GQLObject, GQLScalar, GQLSubscription, GQLUnion } from './types' interface ObjectTypeConfig { implements?: ReadonlyArray> } type BaseQuery = GQLObject<'Query', {}, {}, unknown, never, {}> export const BaseQuery: BaseQuery = new GQLObject('Query', {}) type BaseMutation = GQLObject<'Mutation', {}, {}, unknown, never, {}> export const BaseMutation: BaseMutation = new GQLObject('Mutation', {}) type BaseSubscription = GQLObject<'Subscription', {}, {}, unknown, never, {}> export const BaseSubscription: BaseSubscription = new GQLObject('Subscription', {}) export interface ObjectTypeBuilder { , C extends ObjectTypeConfig>( name: Name, fields: (t: AURItoFieldAlgebra<{}, Ctx>[FieldURI] & AURItoInputAlgebra[InputURI]) => Fields, config?: C ): GQLObject, __E, Compute, 'flat'>> } export function makeObjectTypeBuilder( interpreters: AURItoFieldAlgebra[FieldURI] & AURItoInputAlgebra[InputURI] ): () => ObjectTypeBuilder { return () => (name, fields, config) => new GQLObject(name, fields(interpreters) as any, config?.implements) } export interface ExtendObjectTypeBuilder { , Fields extends FieldRecord>( type: () => Type, fields: (t: AURItoFieldAlgebra[FieldAURI] & AURItoInputAlgebra[InputAURI]) => Fields ): GQLExtendObject & __R, _E & __E, _A & __A> } export function makeExtendObjectTypeBuilder( interpreters: AURItoFieldAlgebra[FieldAURI] & AURItoInputAlgebra[InputAURI] ): ExtendObjectTypeBuilder { return (type, fields) => new GQLExtendObject(type(), fields(interpreters)) } export interface QueryTypeBuilder { >( fields: (t: AURItoFieldAlgebra<{}, Ctx>[FieldURI] & AURItoInputAlgebra[InputURI]) => Fields ): GQLExtendObject, __E, __A> } export function makeQueryTypeBuilder( interpreters: AURItoFieldAlgebra[FieldURI] & AURItoInputAlgebra[InputURI] ) { return (): QueryTypeBuilder => (fields) => // @ts-expect-error too much generic complexity for typescript@4.5 makeExtendObjectTypeBuilder(interpreters)(() => BaseQuery, fields as any) } export interface MutationTypeBuilder { >( fields: (t: AURItoFieldAlgebra<{}, Ctx>[FieldURI] & AURItoInputAlgebra[InputURI]) => Fields ): GQLExtendObject, __E, __A> } export function makeMutationTypeBuilder( interpreters: AURItoFieldAlgebra[FieldURI] & AURItoInputAlgebra[InputURI] ): () => MutationTypeBuilder { // @ts-expect-error too much generic complexity for typescript@4.5 return () => (fields) => makeExtendObjectTypeBuilder(interpreters)(() => BaseMutation, fields as any) } export interface UnionTypeBuilder { , AnyObjectType, ...ReadonlyArray>]>( ...types: Types ): ( name: N, resolveType: TypeResolver> ) => GQLUnion> } export function makeUnionTypeBuilder(): UnionTypeBuilder { return (...types) => (name, resolveType) => new GQLUnion(name, types, resolveType) } export interface InterfaceTypeBuilder { >( name: N, fields: (t: AURItoFieldAlgebra[FieldURI] & AURItoInputAlgebra[InputURI]) => Fields, resolveType: TypeResolver> ): GQLInterface, __E, __A> } export function makeInterfaceTypeBuilder( interpreters: AURItoFieldAlgebra[FieldURI] & AURItoInputAlgebra[InputURI] ): () => InterfaceTypeBuilder { return () => (name, fields, resolveType) => new GQLInterface(name, fields(interpreters), resolveType) } export interface SubscriptionTypeBuilder { >( fields: ( t: AURItoFieldAlgebra<{}, Ctx>[FieldURI] & AURItoInputAlgebra[InputURI] & GQLSubscriptionAlgebra ) => Fields ): GQLSubscription<__R, __A> } export function makeSubscriptionTypeBuilder( interpreters: AURItoFieldAlgebra[FieldURI] & AURItoInputAlgebra[InputURI] & GQLSubscriptionAlgebra ): () => SubscriptionTypeBuilder { return () => (fields) => new GQLSubscription(fields(interpreters)) } export interface InputObjectTypeBuilder { ( name: Name, fields: (t: AURItoInputAlgebra[InputAURI]) => Fields ): GQLInputObject }> } export function makeInputObjectTypeBuilder( interpreters: AURItoInputAlgebra[InputAURI] ): InputObjectTypeBuilder { return (name, fields) => { const interpretedFields = fields(interpreters) return new GQLInputObject(name, interpretedFields as any) } } export interface ScalarTypeBuilder { >( name: Name, definition: Funcs, config?: ScalarConfig ): GQLScalar< Name, UnionToIntersection< { [K in keyof Funcs]: Funcs[K] extends (...args: any[]) => any ? [ReturnType] extends [Sy.Sync] ? R : unknown : unknown }[keyof Funcs] >, _I, _O > } export interface ScalarTypeFromModelBuilder { , O, A>( name: Name, model: Schema, config?: Config ): GQLScalar } export const makeScalarTypeBuilder: ScalarTypeBuilder = (name, definition, config) => new GQLScalar( createScalarTypeDefinitionNode({ description: config?.description, directives: config?.directives, name }), name, definition ) interface ScalarTypeFromModelConfig extends ScalarConfig { message?: string parseLiteral?: ParseLiteralF } export const makeScalarTypeFromModelBuilder: ScalarTypeFromModelBuilder = (name, model, config) => { const { parse } = S.to(Dec.Schemable)(model) const { encode } = S.to(Enc.Schemable)(model) const serialize = (u: unknown) => pipe( parse(u), Th.match( (errors) => Sy.fail( new GQLException(config?.message ?? `Invalid value ${u} provided to Scalar ${name}`, 'INVALID_INPUT', { errors }) ), Sy.succeed, (_, a) => Sy.succeed(a) ) ) const parseValue = flow(serialize, Sy.map(encode)) const parseLiteral = (valueNode: ValueNode) => pipe( valueNode, valueFromASTUntyped, parse, Th.match( (errors) => Sy.fail( new GQLException( config?.message ?? `Invalid value ${valueFromASTUntyped(valueNode)} provided to Scalar ${name}`, 'INVALID_INPUT', { errors } ) ), flow(encode, Sy.succeed), (_, a) => pipe(encode(a), Sy.succeed) ) ) return new GQLScalar( createScalarTypeDefinitionNode({ description: config?.description, directives: config?.directives, name }), name, { parseLiteral: config?.parseLiteral ?? (parseLiteral as any), parseValue, serialize } ) }