import { DMMF } from '@prisma/client/runtime' ; import { inspect } from 'util' ; import { paginationStrategies , PaginationStrategy } from '../pagination' ; import { GlobalComputedInputs , GlobalMutationResolverParams , LocalComputedInputs } from '../utils' ; import { DmmfDocument } from './DmmfDocument' ; import { InternalDMMF } from './DmmfTypes' ; import { getTypeName } from './helpers' ; import { getPrismaClientDmmf } from './utils' ; export type TransformOptions = { atomicOperations ? : boolean ; globallyComputedInputs ? : GlobalComputedInputs ; paginationStrategy ? : PaginationStrategy ; } ; export const getTransformedDmmf = ( prismaClientPackagePath , options ? ) : DmmfDocument => new DmmfDocument ( transform ( getPrismaClientDmmf ( prismaClientPackagePath ) , options ) ) ; const addDefaultOptions = ( givenOptions ? ) : Required < TransformOptions > => ( { globallyComputedInputs : { } , paginationStrategy : paginationStrategies . relay , atomicOperations : true , ... givenOptions , } ) ; export function transform ( document , options ? ) { const result = { datamodel : transformDatamodel ( document . datamodel ) , schema : transformSchema ( document . schema , addDefaultOptions ( options ) ) , operations : document . mappings . modelOperations , } ; return result ; } function transformDatamodel ( datamodel ) { return { enums : datamodel . enums , models : datamodel . models . map ( ( model ) => ( { ... model , fields : model . fields . map ( ( field ) => ( { ... field , kind : field . kind === 'object' ? 'relation' : field . kind , } ) ) , } ) ) as any , } ; } const paginationArgNames = [ 'cursor' , 'take' , 'skip' ] ; type TransformConfig = Required < TransformOptions > ; function transformSchema ( schema , options ) { const enumTypes = schema . enumTypes . model ?? [ ] ; const inputTypes = schema . inputObjectTypes . model ?. map ( ( type ) => transformInputType ( type , options . globallyComputedInputs , options . atomicOperations ) ) ?? [ ] ; const outputTypes = schema . outputObjectTypes . model ?. map ( ( type ) => { return transformOutputType ( type , options ) ; } ) ?? [ ] ; enumTypes . push ( ... schema . enumTypes . prisma ) ; inputTypes . push ( ... schema . inputObjectTypes . prisma . map ( ( type ) => { return transformInputType ( type , options . globallyComputedInputs , options . atomicOperations ) ; } ) ) ; outputTypes . push ( ... schema . outputObjectTypes . prisma . map ( ( type ) => { return transformOutputType ( type , options ) ; } ) ) ; return { enums : enumTypes , inputTypes , outputTypes , } ; } function transformOutputType ( type , options ) { return { ... type , fields : type . fields . map ( ( field ) => { let args = field . args . map ( ( arg ) => transformArg ( arg , options . atomicOperations ) ) ; const argNames = args . map ( ( a ) => a . name ) ; if ( paginationArgNames . every ( ( paginationArgName ) => argNames . includes ( paginationArgName ) ) ) { args = options . paginationStrategy . transformDmmfArgs ( { args , paginationArgNames , field , } ) ; } return { name : field . name , args , outputType : { type : getTypeName ( field . outputType . type ) , kind : getKind ( field . outputType ) , isRequired : ! field . isNullable , isNullable : field . isNullable , isList : field . outputType . isList , } , } ; } ) , } ; } /**
 * Conversion from a Prisma Client arg type to a GraphQL arg type using
 * heuristics. A conversion is needed because GraphQL does not
 * support union types on args, but Prisma Client does.
 */ function transformArg ( arg , atomicOperations ) { const inputType = argTypeUnionToArgType ( arg . inputTypes , atomicOperations ) ; return { name : arg . name , inputType : { type : getTypeName ( inputType . type ) , isList : inputType . isList , kind : getKind ( inputType ) , isNullable : arg . isNullable , isRequired : arg . isRequired , } , } ; } /**
 * Prisma Client supports union types but GraphQL doesn't.
 * Because of that, we need to choose a member of the union type that we'll expose on our GraphQL schema.
 *
 * Apart from some exceptions, we're generally trying to pick the broadest member type of the union.
 */ function argTypeUnionToArgType ( argTypeContexts , atomicOperations ) { const filteredArgTypeContexts = atomicOperations === false ? argTypeContexts . filter ( ( argTypeCtx ) => ! getTypeName ( argTypeCtx . type ) . endsWith ( 'OperationsInput' ) ) : argTypeContexts ; const result = filteredArgTypeContexts . find ( ( argTypeCtx ) => isInputObjectType ( argTypeCtx ) && argTypeCtx . isList && getTypeName ( argTypeCtx . type ) . endsWith ( 'WhereInput' ) ) ?? filteredArgTypeContexts . find ( ( argTypeCtx ) => isInputObjectType ( argTypeCtx ) && getTypeName ( argTypeCtx . type ) . endsWith ( 'WhereInput' ) ) ?? filteredArgTypeContexts . find ( ( argTypeCtx ) => isInputObjectType ( argTypeCtx ) && argTypeCtx . isList ) ?? filteredArgTypeContexts . find ( ( argTypeCtx ) => isInputObjectType ( argTypeCtx ) ) ?? argTypeContexts [ 0 ] ; return result ; function isInputObjectType ( argTypeCtx ) { return argTypeCtx . location === 'inputObjectTypes' ; } } type AddComputedInputParams = { inputType : InternalDMMF . InputType ; params : GlobalMutationResolverParams ; dmmf : DmmfDocument ; locallyComputedInputs : LocalComputedInputs < any > ; } ; /** Resolver-level computed inputs aren't recursive so aren't
 *  needed for deep computed inputs.
 */ type AddDeepComputedInputsArgs = Omit < AddComputedInputParams , 'locallyComputedInputs' > & { data : any ; } ; /**
 * Recursively looks for inputs that need a value from globallyComputedInputs
 * and populates them
 */ async function addGloballyComputedInputs ( { inputType , params , dmmf , data , } ) { if ( Array . isArray ( data ) ) { return Promise . all ( data . map ( ( value ) => addGloballyComputedInputs ( { inputType , dmmf , params , data : value , } ) ) ) ; } const computedInputValues = Object . keys ( inputType . computedInputs ) . reduce ( async ( values , key ) => ( { ... ( await values ) , [ key ] : await inputType . computedInputs [ key ] ( params ) , } ) , Promise . resolve ( { } as Record < string , any > ) ) ; return Object . keys ( data ) . reduce ( async ( deeplyComputedData , fieldName ) => { const field = inputType . fields . find ( ( _ ) => _ . name === fieldName ) ! ; const fieldValue = field . inputType . kind === 'object' ? await addGloballyComputedInputs ( { inputType : dmmf . getInputType ( field . inputType . type ) , dmmf , params , data : data [ fieldName ] , } ) : data [ fieldName ] ; return { ... ( await deeplyComputedData ) , [ fieldName ] : fieldValue , } ; } , computedInputValues ) ; } export async function addComputedInputs ( { dmmf , inputType , locallyComputedInputs , params , } ) { return { ... params . args , data : { /**
             * Globally computed inputs are attached to the inputType object
             * as 'computedInputs' by the transformInputType function.
             */ ... ( await addGloballyComputedInputs ( { inputType , dmmf , params , data : params . args . data , } ) ) , ... ( await Object . entries ( locallyComputedInputs ) . reduce ( async ( args , [ fieldName , computeFieldValue ] ) => ( { ... ( await args ) , [ fieldName ] : await computeFieldValue ( params ) , } ) , Promise . resolve ( { } as Record < string , any > ) ) ) , } , } ; } function transformInputType ( inputType , globallyComputedInputs , atomicOperations ) { const fieldNames = inputType . fields . map ( ( field ) => field . name ) ; /**
     * Only global computed inputs are removed during schema transform.
     * Resolver level computed inputs are filtered as part of the
     * projecting process. They are then passed to addComputedInputs
     * at runtime so their values can be inferred alongside the
     * global values.
     */ const globallyComputedInputsInType = Object . keys ( globallyComputedInputs ) . reduce ( ( args , key ) => fieldNames . includes ( key ) ? Object . assign ( args , { [ key ] : globallyComputedInputs [ key ] } ) : args , { } as GlobalComputedInputs ) ; return { ... inputType , fields : inputType . fields . filter ( ( field ) => ! ( field . name in globallyComputedInputs ) ) . map ( ( field ) => transformArg ( field , atomicOperations ) ) , computedInputs : globallyComputedInputsInType , } ; } function getKind ( arg ) { const type = arg . type ; if ( arg . location === 'scalar' ) { return 'scalar' ; } if ( arg . location === 'enumTypes' ) { return 'enum' ; } if ( arg . location === 'inputObjectTypes' ) { return 'object' ; } if ( arg . location === 'outputObjectTypes' ) { return 'object' ; } throw new Error ( `Failed to transform DMMF into internal DMMF because cannot get arg kind for given type because it is unknown or malformed. The type data is:\n\n${ inspect ( type ) }` ) ; }