import chalk from 'chalk' ; import Decimal from 'decimal.js' ; import indent from 'indent-string' ; import stripAnsi from 'strip-ansi' ; import type { DMMFClass } from './dmmf' ; import type { DMMF } from './dmmf-types' ; import type { ArgError , AtLeastOneError , AtMostOneError , FieldError , InvalidArgError , InvalidFieldError , } from './error-types' ; import { getGraphQLType , getInputTypeName , getOutputTypeName , getSuggestion , inputTypeToJson , isGroupByOutputName , stringifyGraphQLType , stringifyInputType , unionBy , wrapWithList , } from './utils/common' ; import { deepExtend } from './utils/deep-extend' ; import { deepGet } from './utils/deep-set' ; import { filterObject } from './utils/filterObject' ; import { flatMap } from './utils/flatMap' ; import { isObject } from './utils/isObject' ; import { omit } from './utils/omit' ; import type { MissingItem , PrintJsonWithErrorsArgs } from './utils/printJsonErrors' ; import { printJsonWithErrors } from './utils/printJsonErrors' ; import { printStack } from './utils/printStack' ; import stringifyObject from './utils/stringifyObject' ; const tab = 2 ; export class Document { constructor ( public readonly type , public readonly children ) { this . type = type ; this . children = children ; } get [ Symbol . toStringTag ] ( ) { return 'Document' ; } public toString ( ) { return `${ this . type } {
${ indent ( this . children . map ( String ) . join ( '\n' ) , tab ) }
}` ; } public validate ( select ? , isTopLevelQuery = false , originalMethod ? , errorFormat ? , validationCallsite ? ) { if ( ! select ) { select = { } ; } const invalidChildren = this . children . filter ( ( child ) => child . hasInvalidChild || child . hasInvalidArg ) ; if ( invalidChildren . length === 0 ) { return ; } const fieldErrors = [ ] ; const argErrors = [ ] ; const prefix = select && select . select ? 'select' : select . include ? 'include' : undefined ; for ( const child of invalidChildren ) { const errors = child . collectErrors ( prefix ) ; fieldErrors . push ( ... errors . fieldErrors . map ( ( e ) => ( { ... e , path : isTopLevelQuery ? e . path : e . path . slice ( 1 ) , } ) ) ) ; argErrors . push ( ... errors . argErrors . map ( ( e ) => ( { ... e , path : isTopLevelQuery ? e . path : e . path . slice ( 1 ) , } ) ) ) ; } const topLevelQueryName = this . children [ 0 ] . name ; const queryName = isTopLevelQuery ? this . type : topLevelQueryName ; const keyPaths = [ ] ; const valuePaths = [ ] ; const missingItems = [ ] ; for ( const fieldError of fieldErrors ) { const path = this . normalizePath ( fieldError . path , select ) . join ( '.' ) ; if ( fieldError . error . type === 'invalidFieldName' ) { keyPaths . push ( path ) ; const fieldType = fieldError . error . outputType ; const { isInclude } = fieldError . error ; fieldType . fields . filter ( ( field ) => ( isInclude ? field . outputType . location === 'outputObjectTypes' : true ) ) . forEach ( ( field ) => { const splittedPath = path . split ( '.' ) ; missingItems . push ( { path : `${ splittedPath . slice ( 0 , splittedPath . length - 1 ) . join ( '.' ) }.${ field . name }` , type : 'true' , isRequired : false , } ) ; } ) ; } else if ( fieldError . error . type === 'includeAndSelect' ) { keyPaths . push ( 'select' ) ; keyPaths . push ( 'include' ) ; } else { valuePaths . push ( path ) ; } if ( fieldError . error . type === 'emptySelect' || fieldError . error . type === 'noTrueSelect' || fieldError . error . type === 'emptyInclude' ) { const selectPathArray = this . normalizePath ( fieldError . path , select ) ; const selectPath = selectPathArray . slice ( 0 , selectPathArray . length - 1 ) . join ( '.' ) ; const fieldType = fieldError . error . field . outputType . type as DMMF . OutputType ; fieldType . fields ?. filter ( ( field ) => fieldError . error . type === 'emptyInclude' ? field . outputType . location === 'outputObjectTypes' : true ) . forEach ( ( field ) => { missingItems . push ( { path : `${ selectPath }.${ field . name }` , type : 'true' , isRequired : false , } ) ; } ) ; } } for ( const argError of argErrors ) { const path = this . normalizePath ( argError . path , select ) . join ( '.' ) ; if ( argError . error . type === 'invalidName' ) { keyPaths . push ( path ) ; } else if ( argError . error . type !== 'missingArg' && argError . error . type !== 'atLeastOne' ) { valuePaths . push ( path ) ; } else if ( argError . error . type === 'missingArg' ) { const type = argError . error . missingArg . inputTypes . length === 1 ? argError . error . missingArg . inputTypes [ 0 ] . type : argError . error . missingArg . inputTypes . map ( ( t ) => { const inputTypeName = getInputTypeName ( t . type ) ; if ( inputTypeName === 'Null' ) { return 'null' ; } if ( t . isList ) { return inputTypeName + '[]' ; } return inputTypeName ; } ) . join ( ' | ' ) ; missingItems . push ( { path , type : inputTypeToJson ( type , true , path . split ( 'where.' ) . length === 2 ) , isRequired : argError . error . missingArg . isRequired , } ) ; } } const renderErrorStr = ( callsite ? ) => { const hasRequiredMissingArgsErrors = argErrors . some ( ( e ) => e . error . type === 'missingArg' && e . error . missingArg . isRequired ) ; const hasOptionalMissingArgsErrors = Boolean ( argErrors . find ( ( e ) => e . error . type === 'missingArg' && ! e . error . missingArg . isRequired ) ) ; const hasMissingArgsErrors = hasOptionalMissingArgsErrors || hasRequiredMissingArgsErrors ; let missingArgsLegend = '' ; if ( hasRequiredMissingArgsErrors ) { missingArgsLegend += `\n${ chalk . dim ( 'Note: Lines with ' ) }${ chalk . reset . greenBright ( '+' ) } ${ chalk . dim ( 'are required' ) }` ; } if ( hasOptionalMissingArgsErrors ) { if ( missingArgsLegend . length === 0 ) { missingArgsLegend = '\n' ; } if ( hasRequiredMissingArgsErrors ) { missingArgsLegend += chalk . dim ( `, lines with ${ chalk . green ( '?' ) } are optional` ) ; } else { missingArgsLegend += chalk . dim ( `Note: Lines with ${ chalk . green ( '?' ) } are optional` ) ; } missingArgsLegend += chalk . dim ( '.' ) ; } const relevantArgErrors = argErrors . filter ( ( e ) => e . error . type !== 'missingArg' || e . error . missingArg . isRequired ) ; let errorMessages = relevantArgErrors . map ( ( e ) => this . printArgError ( e , hasMissingArgsErrors , errorFormat === 'minimal' ) ) . join ( '\n' ) ; errorMessages += `
${ fieldErrors . map ( ( e ) => this . printFieldError ( e , missingItems , errorFormat === 'minimal' ) ) . join ( '\n' ) }` ; if ( errorFormat === 'minimal' ) { return stripAnsi ( errorMessages ) ; } const { stack , indent : indentValue , afterLines , } = printStack ( { callsite , originalMethod : originalMethod || queryName , showColors : errorFormat && errorFormat === 'pretty' , isValidationError : true , } ) ; let printJsonArgs = { ast : isTopLevelQuery ? { [ topLevelQueryName ] : select } : select , keyPaths , valuePaths , missingItems , } ; if ( originalMethod ?. endsWith ( 'aggregate' ) ) { printJsonArgs = transformAggregatePrintJsonArgs ( printJsonArgs ) ; } const errorStr = `${ stack }${ indent ( printJsonWithErrors ( printJsonArgs ) , indentValue ) . slice ( indentValue ) }${ chalk . dim ( afterLines ) }

${ errorMessages }${ missingArgsLegend }\n` ; if ( process . env . NO_COLOR || errorFormat === 'colorless' ) { return stripAnsi ( errorStr ) ; } return errorStr ; } ; const error = new PrismaClientValidationError ( renderErrorStr ( validationCallsite ) ) ; if ( process . env . NODE_ENV !== 'production' ) { Object . defineProperty ( error , 'render' , { get : ( ) => renderErrorStr , enumerable : false , } ) ; } throw error ; } protected printFieldError = ( { error } , missingItems , minimal ) => { if ( error . type === 'emptySelect' ) { const additional = minimal ? '' : ` Available options are listed in ${ chalk . greenBright . dim ( 'green' ) }.` ; return `The ${ chalk . redBright ( '`select`' ) } statement for type ${ chalk . bold ( getOutputTypeName ( error . field . outputType . type ) ) } must not be empty.${ additional }` ; } if ( error . type === 'emptyInclude' ) { if ( missingItems . length === 0 ) { return `${ chalk . bold ( getOutputTypeName ( error . field . outputType . type ) ) } does not have any relation and therefore can't have an ${ chalk . redBright ( '`include`' ) } statement.` ; } const additional = minimal ? '' : ` Available options are listed in ${ chalk . greenBright . dim ( 'green' ) }.` ; return `The ${ chalk . redBright ( '`include`' ) } statement for type ${ chalk . bold ( getOutputTypeName ( error . field . outputType . type ) ) } must not be empty.${ additional }` ; } if ( error . type === 'noTrueSelect' ) { return `The ${ chalk . redBright ( '`select`' ) } statement for type ${ chalk . bold ( getOutputTypeName ( error . field . outputType . type ) ) } needs ${ chalk . bold ( 'at least one truthy value' ) }.` ; } if ( error . type === 'includeAndSelect' ) { return `Please ${ chalk . bold ( 'either' ) } use ${ chalk . greenBright ( '`include`' ) } or ${ chalk . greenBright ( '`select`' ) }, but ${ chalk . redBright ( 'not both' ) } at the same time.` ; } if ( error . type === 'invalidFieldName' ) { const statement = error . isInclude ? 'include' : 'select' ; const wording = error . isIncludeScalar ? 'Invalid scalar' : 'Unknown' ; const additional = minimal ? '' : error . isInclude && missingItems . length === 0 ? `\nThis model has no relations, so you can't use ${ chalk . redBright ( 'include' ) } with it.` : ` Available options are listed in ${ chalk . greenBright . dim ( 'green' ) }.` ; let str = `${ wording } field ${ chalk . redBright ( `\`${ error . providedName }\`` ) } for ${ chalk . bold ( statement ) } statement on model ${ chalk . bold . white ( error . modelName ) }.${ additional }` ; if ( error . didYouMean ) { str += ` Did you mean ${ chalk . greenBright ( `\`${ error . didYouMean }\`` ) }?` ; } if ( error . isIncludeScalar ) { str += `\nNote, that ${ chalk . bold ( 'include' ) } statements only accept relation fields.` ; } return str ; } if ( error . type === 'invalidFieldType' ) { const str = `Invalid value ${ chalk . redBright ( `${ stringifyObject ( error . providedValue ) }` ) } of type ${ chalk . redBright ( getGraphQLType ( error . providedValue , undefined ) ) } for field ${ chalk . bold ( `${ error . fieldName }` ) } on model ${ chalk . bold . white ( error . modelName ) }. Expected either ${ chalk . greenBright ( 'true' ) } or ${ chalk . greenBright ( 'false' ) }.` ; return str ; } return undefined ; } ; protected printArgError = ( { error , path , id } , hasMissingItems , minimal ) => { if ( error . type === 'invalidName' ) { let str = `Unknown arg ${ chalk . redBright ( `\`${ error . providedName }\`` ) } in ${ chalk . bold ( path . join ( '.' ) ) } for type ${ chalk . bold ( error . outputType ? error . outputType . name : getInputTypeName ( error . originalType ) ) }.` ; if ( error . didYouMeanField ) { str += `\n→ Did you forget to wrap it with \`${ chalk . greenBright ( 'select' ) }\`? ${ chalk . dim ( 'e.g. ' + chalk . greenBright ( `{ select: { ${ error . providedName }: ${ error . providedValue } } }` ) ) }` ; } else if ( error . didYouMeanArg ) { str += ` Did you mean \`${ chalk . greenBright ( error . didYouMeanArg ) }\`?` ; if ( ! hasMissingItems && ! minimal ) { str += ` ${ chalk . dim ( 'Available args:' ) }\n` + stringifyInputType ( error . originalType , true ) ; } } else { if ( ( error . originalType as DMMF . InputType ) . fields . length === 0 ) { str += ` The field ${ chalk . bold ( ( error . originalType as DMMF . InputType ) . name ) } has no arguments.` ; } else if ( ! hasMissingItems && ! minimal ) { str += ` Available args:\n\n` + stringifyInputType ( error . originalType , true ) ; } } return str ; } if ( error . type === 'invalidType' ) { let valueStr = stringifyObject ( error . providedValue , { indent : '  ' } ) ; const multilineValue = valueStr . split ( '\n' ) . length > 1 ; if ( multilineValue ) { valueStr = `\n${ valueStr }\n` ; } if ( error . requiredType . bestFittingType . location === 'enumTypes' ) { return `Argument ${ chalk . bold ( error . argName ) }: Provided value ${ chalk . redBright ( valueStr ) }${ multilineValue ? '' : ' ' }of type ${ chalk . redBright ( getGraphQLType ( error . providedValue ) ) } on ${ chalk . bold ( `prisma.${ this . children [ 0 ] . name }` ) } is not a ${ chalk . greenBright ( wrapWithList ( stringifyGraphQLType ( error . requiredType . bestFittingType . location ) , error . requiredType . bestFittingType . isList ) ) }.
→ Possible values: ${ ( error . requiredType . bestFittingType . type as DMMF . SchemaEnum ) . values . map ( ( v ) => chalk . greenBright ( `${ stringifyGraphQLType ( error . requiredType . bestFittingType . type ) }.${ v }` ) ) . join ( ', ' ) }` ; } let typeStr = '.' ; if ( isInputArgType ( error . requiredType . bestFittingType . type ) ) { typeStr = ':\n' + stringifyInputType ( error . requiredType . bestFittingType . type ) ; } let expected = `${ error . requiredType . inputType . map ( ( t ) => chalk . greenBright ( wrapWithList ( stringifyGraphQLType ( t . type ) , error . requiredType . bestFittingType . isList ) ) ) . join ( ' or ' ) }${ typeStr }` ; const inputType = ( error . requiredType . inputType . length === 2 && error . requiredType . inputType . find ( ( t ) => isInputArgType ( t . type ) ) ) || null ; if ( inputType ) { expected += `\n` + stringifyInputType ( inputType . type , true ) ; } return `Argument ${ chalk . bold ( error . argName ) }: Got invalid value ${ chalk . redBright ( valueStr ) }${ multilineValue ? '' : ' ' }on ${ chalk . bold ( `prisma.${ this . children [ 0 ] . name }` ) }. Provided ${ chalk . redBright ( getGraphQLType ( error . providedValue ) ) }, expected ${ expected }` ; } if ( error . type === 'invalidNullArg' ) { const forStr = path . length === 1 && path [ 0 ] === error . name ? '' : ` for ${ chalk . bold ( `${ path . join ( '.' ) }` ) }` ; const undefinedTip = ` Please use ${ chalk . bold . greenBright ( 'undefined' ) } instead.` ; return `Argument ${ chalk . greenBright ( error . name ) }${ forStr } must not be ${ chalk . bold ( 'null' ) }.${ undefinedTip }` ; } if ( error . type === 'missingArg' ) { const forStr = path . length === 1 && path [ 0 ] === error . missingName ? '' : ` for ${ chalk . bold ( `${ path . join ( '.' ) }` ) }` ; return `Argument ${ chalk . greenBright ( error . missingName ) }${ forStr } is missing.` ; } if ( error . type === 'atLeastOne' ) { const additional = minimal ? '' : ` Available args are listed in ${ chalk . dim . green ( 'green' ) }.` ; return `Argument ${ chalk . bold ( path . join ( '.' ) ) } of type ${ chalk . bold ( error . inputType . name ) } needs ${ chalk . greenBright ( 'at least one' ) } argument.${ additional }` ; } if ( error . type === 'atMostOne' ) { const additional = minimal ? '' : ` Please choose one. ${ chalk . dim ( 'Available args:' ) } \n${ stringifyInputType ( error . inputType , true ) }` ; return `Argument ${ chalk . bold ( path . join ( '.' ) ) } of type ${ chalk . bold ( error . inputType . name ) } needs ${ chalk . greenBright ( 'exactly one' ) } argument, but you provided ${ error . providedKeys . map ( ( key ) => chalk . redBright ( key ) ) . join ( ' and ' ) }.${ additional }` ; } return undefined ; } ; param inputPath param select private normalizePath ( inputPath , select ) { const path = inputPath . slice ( ) ; const newPath = [ ] ; let key ; let pointer = select ; while ( ( key = path . shift ( ) ) !== undefined ) { if ( ! Array . isArray ( pointer ) && key === 0 ) { continue ; } if ( key === 'select' ) { if ( ! pointer [ key ] ) { pointer = pointer . include ; } else { pointer = pointer [ key ] ; } } else if ( pointer && pointer [ key ] ) { pointer = pointer [ key ] ; } newPath . push ( key ) ; } return newPath ; } } export class PrismaClientValidationError extends Error { get [ Symbol . toStringTag ] ( ) { return 'PrismaClientValidationError' ; } } export class PrismaClientConstructorValidationError extends Error { constructor ( message ) { super ( message + `\nRead more at https://pris.ly/d/client-constructor` ) ; } get [ Symbol . toStringTag ] ( ) { return 'PrismaClientConstructorValidationError' ; } } export interface FieldArgs { name : string ; schemaField ? : DMMF . SchemaField ; args ? : Args ; children ? : Field [ ] ; error ? : InvalidFieldError ; } export class Field { public readonly name : string ; public readonly args ? : Args ; public readonly children ? : Field [ ] ; public readonly error ? : InvalidFieldError ; public readonly hasInvalidChild : boolean ; public readonly hasInvalidArg : boolean ; public readonly schemaField ? : DMMF . SchemaField ; constructor ( { name , args , children , error , schemaField } ) { this . name = name ; this . args = args ; this . children = children ; this . error = error ; this . schemaField = schemaField ; this . hasInvalidChild = children ? children . some ( ( child ) => Boolean ( child . error || child . hasInvalidArg || child . hasInvalidChild ) ) : false ; this . hasInvalidArg = args ? args . hasInvalidArg : false ; } get [ Symbol . toStringTag ] ( ) { return 'Field' ; } public toString ( ) { let str = this . name ; if ( this . error ) { return str + ' # INVALID_FIELD' ; } if ( this . args && this . args . args && this . args . args . length > 0 ) { if ( this . args . args . length === 1 ) { str += `(${ this . args . toString ( ) })` ; } else { str += `(\n${ indent ( this . args . toString ( ) , tab ) }\n)` ; } } if ( this . children ) { str += ` {
${ indent ( this . children . map ( String ) . join ( '\n' ) , tab ) }
}` ; } return str ; } public collectErrors ( prefix = 'select' ) { const fieldErrors = [ ] ; const argErrors = [ ] ; if ( this . error ) { fieldErrors . push ( { path : [ this . name ] , error : this . error , } ) ; } if ( this . children ) { for ( const child of this . children ) { const errors = child . collectErrors ( prefix ) ; fieldErrors . push ( ... errors . fieldErrors . map ( ( e ) => ( { ... e , path : [ this . name , prefix , ... e . path ] , } ) ) ) ; argErrors . push ( ... errors . argErrors . map ( ( e ) => ( { ... e , path : [ this . name , prefix , ... e . path ] , } ) ) ) ; } } if ( this . args ) { argErrors . push ( ... this . args . collectErrors ( ) . map ( ( e ) => ( { ... e , path : [ this . name , ... e . path ] } ) ) ) ; } return { fieldErrors , argErrors , } ; } } export class Args { public args : Arg [ ] ; public readonly hasInvalidArg : boolean ; constructor ( args = [ ] ) { this . args = args ; this . hasInvalidArg = args ? args . some ( ( arg ) => Boolean ( arg . hasError ) ) : false ; } get [ Symbol . toStringTag ] ( ) { return 'Args' ; } public toString ( ) { if ( this . args . length === 0 ) { return '' ; } return `${ this . args . map ( ( arg ) => arg . toString ( ) ) . filter ( ( a ) => a ) . join ( '\n' ) }` ; } public collectErrors ( ) { if ( ! this . hasInvalidArg ) { return [ ] ; } return flatMap ( this . args , ( arg ) => arg . collectErrors ( ) ) ; } } param value param _ param tab function stringify ( value , inputType ? ) { if ( Buffer . isBuffer ( value ) ) { return JSON . stringify ( value . toString ( 'base64' ) ) ; } if ( Object . prototype . toString . call ( value ) === '[object BigInt]' ) { return value . toString ( ) ; } if ( typeof inputType ?. type === 'string' && inputType . type === 'Json' ) { if ( value === null ) { return 'null' ; } if ( value && value . values && value . __prismaRawParamaters__ ) { return JSON . stringify ( value . values ) ; } if ( inputType ?. isList && Array . isArray ( value ) ) { return JSON . stringify ( value . map ( ( o ) => JSON . stringify ( o ) ) ) ; } return JSON . stringify ( JSON . stringify ( value ) ) ; } if ( value === undefined ) { return null ; } if ( value === null ) { return 'null' ; } if ( Decimal . isDecimal ( value ) ) { return value . toString ( ) ; } if ( inputType ?. location === 'enumTypes' && typeof value === 'string' ) { if ( Array . isArray ( value ) ) { return `[${ value . join ( ', ' ) }]` ; } return value ; } return JSON . stringify ( value , null , 2 ) ; } interface ArgOptions { key : string ; value : ArgValue ; isEnum ? : boolean ; error ? : InvalidArgError ; schemaArg ? : DMMF . SchemaArg ; inputType ? : DMMF . SchemaArgInputType ; } export class Arg { public key : string ; public value : ArgValue ; public error ? : InvalidArgError ; public hasError : boolean ; public isEnum : boolean ; public schemaArg ? : DMMF . SchemaArg ; public isNullable : boolean ; public inputType ? : DMMF . SchemaArgInputType ; constructor ( { key , value , isEnum = false , error , schemaArg , inputType } ) { this . inputType = inputType ; this . key = key ; this . value = value ; this . isEnum = isEnum ; this . error = error ; this . schemaArg = schemaArg ; this . isNullable = schemaArg ?. inputTypes . reduce < boolean > ( ( isNullable ) => isNullable && schemaArg . isNullable , true ) || false ; this . hasError = Boolean ( error ) || ( value instanceof Args ? value . hasInvalidArg : false ) || ( Array . isArray ( value ) && value . some ( ( v ) => ( v instanceof Args ? v . hasInvalidArg : false ) ) ) ; } get [ Symbol . toStringTag ] ( ) { return 'Arg' ; } public _toString ( value , key ) { if ( typeof value === 'undefined' ) { return undefined ; } if ( value instanceof Args ) { return `${ key }: {
${ indent ( value . toString ( ) , 2 ) }
}` ; } if ( Array . isArray ( value ) ) { if ( this . inputType ?. type === 'Json' ) { return `${ key }: ${ stringify ( value , this . inputType ) }` ; } const isScalar = ! ( value as any [ ] ) . some ( ( v ) => typeof v === 'object' ) ; return `${ key }: [${ isScalar ? '' : '\n' }${ indent ( ( value as any [ ] ) . map ( ( nestedValue ) => { if ( nestedValue instanceof Args ) { return `{\n${ indent ( nestedValue . toString ( ) , tab ) }\n}` ; } return stringify ( nestedValue , this . inputType ) ; } ) . join ( `,${ isScalar ? ' ' : '\n' }` ) , isScalar ? 0 : tab ) }${ isScalar ? '' : '\n' }]` ; } return `${ key }: ${ stringify ( value , this . inputType ) }` ; } public toString ( ) { return this . _toString ( this . value , this . key ) ; } public collectErrors ( ) { if ( ! this . hasError ) { return [ ] ; } const errors = [ ] ; if ( this . error ) { const id = typeof this . inputType ?. type === 'object' ? `${ this . inputType . type . name }${ this . inputType . isList ? '[]' : '' }` : undefined ; errors . push ( { error : this . error , path : [ this . key ] , id , } ) ; } if ( Array . isArray ( this . value ) ) { errors . push ( ... ( flatMap ( this . value as any [ ] , ( val , index ) => { if ( ! val ?. collectErrors ) { return [ ] ; } return val . collectErrors ( ) . map ( ( e ) => { return { ... e , path : [ this . key , index , ... e . path ] } ; } ) ; } ) as any ) ) ; } if ( this . value instanceof Args ) { errors . push ( ... this . value . collectErrors ( ) . map ( ( e ) => ( { ... e , path : [ this . key , ... e . path ] } ) ) ) ; } return errors ; } } export type ArgValue = string | boolean | number | undefined | Args | string [ ] | boolean [ ] | number [ ] | Args [ ] | null ; export interface DocumentInput { dmmf : DMMFClass ; rootTypeName : 'query' | 'mutation' ; rootField : string ; select ? : any ; } export function makeDocument ( { dmmf , rootTypeName , rootField , select } ) { if ( ! select ) { select = { } ; } const rootType = rootTypeName === 'query' ? dmmf . queryType : dmmf . mutationType ; const fakeRootField = { args : [ ] , outputType : { isList : false , type : rootType , location : 'outputObjectTypes' , } , name : rootTypeName , } ; const children = selectionToFields ( dmmf , { [ rootField ] : select } , fakeRootField , [ rootTypeName ] ) ; return new Document ( rootTypeName , children ) as any ; } export function transformDocument ( document ) { return document ; } export function selectionToFields ( dmmf , selection , schemaField , path ) { const outputType = schemaField . outputType . type as DMMF . OutputType ; return Object . entries ( selection ) . reduce ( ( acc , [ name , value ] ) => { const field = outputType . fieldMap ? outputType . fieldMap [ name ] : outputType . fields . find ( ( f ) => f . name === name ) ; if ( ! field ) { acc . push ( new Field ( { name , children : [ ] , error : { type : 'invalidFieldName' , modelName : outputType . name , providedName : name , didYouMean : getSuggestion ( name , outputType . fields . map ( ( f ) => f . name ) ) , outputType , } , } ) ) ; return acc ; } if ( typeof value !== 'boolean' && field . outputType . location === 'scalar' && field . name !== 'executeRaw' && field . name !== 'queryRaw' && outputType . name !== 'Query' && ! name . startsWith ( 'aggregate' ) && field . name !== 'count' ) { acc . push ( new Field ( { name , children : [ ] , error : { type : 'invalidFieldType' , modelName : outputType . name , fieldName : name , providedValue : value , } , } ) ) ; return acc ; } if ( value === false ) { return acc ; } const transformedField = { name : field . name , fields : field . args , constraints : { minNumFields : null , maxNumFields : null , } , } ; const argsWithoutIncludeAndSelect = typeof value === 'object' ? omit ( value , [ 'include' , 'select' ] ) : undefined ; const args = argsWithoutIncludeAndSelect ? objectToArgs ( argsWithoutIncludeAndSelect , transformedField , [ ] , typeof field === 'string' ? undefined : ( field . outputType . type as DMMF . OutputType ) ) : undefined ; const isRelation = field . outputType . location === 'outputObjectTypes' ; if ( value ) { if ( value . select && value . include ) { acc . push ( new Field ( { name , children : [ new Field ( { name : 'include' , args : new Args ( ) , error : { type : 'includeAndSelect' , field , } , } ) , ] , } ) ) ; } else if ( value . include ) { const keys = Object . keys ( value . include ) ; if ( keys . length === 0 ) { acc . push ( new Field ( { name , children : [ new Field ( { name : 'include' , args : new Args ( ) , error : { type : 'emptyInclude' , field , } , } ) , ] , } ) ) ; return acc ; } /**
                 * Error handling for `include` statements
                 */ if ( field . outputType . location === 'outputObjectTypes' ) { const fieldOutputType = field . outputType . type as DMMF . OutputType ; const allowedKeys = fieldOutputType . fields . filter ( ( f ) => f . outputType . location === 'outputObjectTypes' ) . map ( ( f ) => f . name ) ; const invalidKeys = keys . filter ( ( key ) => ! allowedKeys . includes ( key ) ) ; if ( invalidKeys . length > 0 ) { acc . push ( ... invalidKeys . map ( ( invalidKey ) => new Field ( { name : invalidKey , children : [ new Field ( { name : invalidKey , args : new Args ( ) , error : { type : 'invalidFieldName' , modelName : fieldOutputType . name , outputType : fieldOutputType , providedName : invalidKey , didYouMean : getSuggestion ( invalidKey , allowedKeys ) || undefined , isInclude : true , isIncludeScalar : fieldOutputType . fields . some ( ( f ) => f . name === invalidKey ) , } , } ) , ] , } ) ) ) ; return acc ; } } } else if ( value . select ) { const values = Object . values ( value . select ) ; if ( values . length === 0 ) { acc . push ( new Field ( { name , children : [ new Field ( { name : 'select' , args : new Args ( ) , error : { type : 'emptySelect' , field , } , } ) , ] , } ) ) ; return acc ; } const truthyValues = values . filter ( ( v ) => v ) ; if ( truthyValues . length === 0 ) { acc . push ( new Field ( { name , children : [ new Field ( { name : 'select' , args : new Args ( ) , error : { type : 'noTrueSelect' , field , } , } ) , ] , } ) ) ; return acc ; } } } const defaultSelection = isRelation ? getDefaultSelection ( field . outputType . type as DMMF . OutputType ) : null ; let select = defaultSelection ; if ( value ) { if ( value . select ) { select = value . select ; } else if ( value . include ) { select = deepExtend ( defaultSelection , value . include ) ; } else if ( value . by && Array . isArray ( value . by ) && field . outputType . namespace === 'prisma' && field . outputType . location === 'outputObjectTypes' && isGroupByOutputName ( ( field . outputType . type as DMMF . OutputType ) . name ) ) { select = byToSelect ( value . by ) ; } } const children = select !== false && isRelation ? selectionToFields ( dmmf , select , field , [ ... path , name ] ) : undefined ; acc . push ( new Field ( { name , args , children , schemaField : field } ) ) ; return acc ; } , [ ] as Field [ ] ) ; } function byToSelect ( by ) { const obj = Object . create ( null ) ; for ( const b of by ) { obj [ b ] = true ; } return obj ; } function getDefaultSelection ( outputType ) { const acc = Object . create ( null ) ; for ( const f of outputType . fields ) { if ( f . outputType . location === 'scalar' || f . outputType . location === 'enumTypes' ) { acc [ f . name ] = true ; } } return acc ; } function getInvalidTypeArg ( key , value , arg , bestFittingType ) { const arrg = new Arg ( { key , value , isEnum : bestFittingType . location === 'enumTypes' , inputType : bestFittingType , error : { type : 'invalidType' , providedValue : value , argName : key , requiredType : { inputType : arg . inputTypes , bestFittingType , } , } , } ) ; return arrg ; } function hasCorrectScalarType ( value , arg , inputType ) { const { type , isList } = inputType ; const expectedType = wrapWithList ( stringifyGraphQLType ( type ) , isList ) ; const graphQLType = getGraphQLType ( value , type ) ; if ( graphQLType === expectedType ) { return true ; } if ( isList && graphQLType === 'List<>' ) { return true ; } if ( expectedType === 'Json' ) { return true ; } if ( graphQLType === 'Int' && expectedType === 'BigInt' ) { return true ; } if ( graphQLType === 'List<Int>' && expectedType === 'List<BigInt>' ) { return true ; } if ( graphQLType === 'List<BigInt | Int>' && expectedType === 'List<BigInt>' ) { return true ; } if ( graphQLType === 'List<Int | BigInt>' && expectedType === 'List<BigInt>' ) { return true ; } if ( ( graphQLType === 'Int' || graphQLType === 'Float' ) && expectedType === 'Decimal' ) { return true ; } if ( ( graphQLType === 'List<Int>' || graphQLType === 'List<Float>' ) && expectedType === 'List<Decimal>' ) { return true ; } if ( graphQLType === 'DateTime' && expectedType === 'String' ) { return true ; } if ( graphQLType === 'List<DateTime>' && expectedType === 'List<String>' ) { return true ; } if ( graphQLType === 'UUID' && expectedType === 'String' ) { return true ; } if ( graphQLType === 'List<UUID>' && expectedType === 'List<String>' ) { return true ; } if ( graphQLType === 'String' && expectedType === 'ID' ) { return true ; } if ( graphQLType === 'List<String>' && expectedType === 'List<ID>' ) { return true ; } if ( graphQLType === 'List<String>' && expectedType === 'List<Json>' ) { return true ; } if ( expectedType === 'List<String>' && ( graphQLType === 'List<String | UUID>' || graphQLType === 'List<UUID | String>' ) ) { return true ; } if ( graphQLType === 'Int' && expectedType === 'Float' ) { return true ; } if ( graphQLType === 'List<Int>' && expectedType === 'List<Float>' ) { return true ; } if ( graphQLType === 'Int' && expectedType === 'Long' ) { return true ; } if ( graphQLType === 'List<Int>' && expectedType === 'List<Long>' ) { return true ; } if ( graphQLType === 'String' && expectedType === 'Decimal' && /^\-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i . test ( value ) ) { return true ; } if ( ! arg . isRequired && value === null ) { return true ; } return false ; } const cleanObject = ( obj ) => filterObject ( obj , ( k , v ) => v !== undefined ) ; function valueToArg ( key , value , arg ) { /**
     * Go through the possible union input types.
     * Stop on the first successful one
     */ let maybeArg = null ; const argsWithErrors = [ ] ; for ( const inputType of arg . inputTypes ) { maybeArg = tryInferArgs ( key , value , arg , inputType ) ; if ( maybeArg ?. collectErrors ( ) . length === 0 ) { return maybeArg ; } if ( maybeArg && maybeArg ?. collectErrors ( ) ) { const argErrors = maybeArg ?. collectErrors ( ) ; if ( argErrors && argErrors . length > 0 ) { argsWithErrors . push ( { arg : maybeArg , errors : argErrors } ) ; } } } if ( maybeArg ?. hasError && argsWithErrors . length > 0 ) { const argsWithScores = argsWithErrors . map ( ( { arg , errors } ) => { const errorScores = errors . map ( ( e ) => { let score = 1 ; if ( e . error . type === 'invalidType' ) { score = 2 * Math . exp ( getDepth ( e . error . providedValue ) ) + 1 ; } score += Math . log ( e . path . length ) ; if ( e . error . type === 'missingArg' ) { if ( arg . inputType && isInputArgType ( arg . inputType . type ) && arg . inputType . type . name . includes ( 'Unchecked' ) ) { score *= 2 ; } } if ( e . error . type === 'invalidName' ) { if ( isInputArgType ( e . error . originalType ) ) { if ( e . error . originalType . name . includes ( 'Unchecked' ) ) { score *= 2 ; } } } return score ; } ) ; return { score : errors . length + sum ( errorScores ) , arg , errors , } ; } ) ; argsWithScores . sort ( ( a , b ) => ( a . score < b . score ? - 1 : 1 ) ) ; return argsWithScores [ 0 ] . arg ; } return maybeArg ; } function getDepth ( object ) { let level = 1 ; if ( ! object || typeof object !== 'object' ) { return level ; } for ( const key in object ) { if ( ! Object . prototype . hasOwnProperty . call ( object , key ) ) { continue ; } if ( typeof object [ key ] === 'object' ) { const depth = getDepth ( object [ key ] ) + 1 ; level = Math . max ( depth , level ) ; } } return level ; } function sum ( n ) { return n . reduce ( ( acc , curr ) => acc + curr , 0 ) ; } param key param value param arg param inputType function tryInferArgs ( key , value , arg , inputType ) { if ( typeof value === 'undefined' ) { if ( ! arg . isRequired ) { return null ; } return new Arg ( { key , value , isEnum : inputType . location === 'enumTypes' , inputType , error : { type : 'missingArg' , missingName : key , missingArg : arg , atLeastOne : false , atMostOne : false , } , } ) ; } const { isNullable , isRequired } = arg ; if ( value === null && ! isNullable && ! isRequired ) { const isAtLeastOne = isInputArgType ( inputType . type ) ? inputType . type . constraints . minNumFields !== null && inputType . type . constraints . minNumFields > 0 : false ; if ( ! isAtLeastOne ) { return new Arg ( { key , value , isEnum : inputType . location === 'enumTypes' , inputType , error : { type : 'invalidNullArg' , name : key , invalidType : arg . inputTypes , atLeastOne : false , atMostOne : false , } , } ) ; } } if ( ! inputType . isList ) { if ( isInputArgType ( inputType . type ) ) { if ( typeof value !== 'object' || Array . isArray ( value ) || ( inputType . location === 'inputObjectTypes' && ! isObject ( value ) ) ) { return getInvalidTypeArg ( key , value , arg , inputType ) ; } else { const val = cleanObject ( value ) ; let error ; const keys = Object . keys ( val || { } ) ; const numKeys = keys . length ; if ( numKeys === 0 && typeof inputType . type . constraints . minNumFields === 'number' && inputType . type . constraints . minNumFields > 0 ) { error = { type : 'atLeastOne' , key , inputType : inputType . type , } ; } else if ( numKeys > 1 && typeof inputType . type . constraints . maxNumFields === 'number' && inputType . type . constraints . maxNumFields < 2 ) { error = { type : 'atMostOne' , key , inputType : inputType . type , providedKeys : keys , } ; } return new Arg ( { key , value : val === null ? null : objectToArgs ( val , inputType . type , arg . inputTypes ) , isEnum : inputType . location === 'enumTypes' , error , inputType , schemaArg : arg , } ) ; } } else { return scalarToArg ( key , value , arg , inputType ) ; } } if ( ! Array . isArray ( value ) && inputType . isList ) { if ( key !== 'updateMany' ) { value = [ value ] ; } } if ( inputType . location === 'enumTypes' || inputType . location === 'scalar' ) { return scalarToArg ( key , value , arg , inputType ) ; } const argInputType = inputType . type as DMMF . InputType ; const hasAtLeastOneError = typeof argInputType . constraints ?. minNumFields === 'number' && argInputType . constraints ?. minNumFields > 0 ? Array . isArray ( value ) && value . some ( ( v ) => ! v || Object . keys ( cleanObject ( v ) ) . length === 0 ) : false ; let err = hasAtLeastOneError ? { inputType : argInputType , key , type : 'atLeastOne' , } : undefined ; if ( ! err ) { const hasOneOfError = typeof argInputType . constraints ?. maxNumFields === 'number' && argInputType . constraints ?. maxNumFields < 2 ? Array . isArray ( value ) && value . find ( ( v ) => ! v || Object . keys ( cleanObject ( v ) ) . length !== 1 ) : false ; if ( hasOneOfError ) { err = { inputType : argInputType , key , type : 'atMostOne' , providedKeys : Object . keys ( hasOneOfError ) , } ; } } if ( ! Array . isArray ( value ) ) { for ( const nestedArgInputType of arg . inputTypes ) { const args = objectToArgs ( value , nestedArgInputType . type as DMMF . InputType ) ; if ( args . collectErrors ( ) . length === 0 ) { return new Arg ( { key , value : args , isEnum : false , schemaArg : arg , inputType : nestedArgInputType , } ) ; } } } return new Arg ( { key , value : value . map ( ( v ) => { if ( inputType . isList && typeof v !== 'object' ) { return v ; } if ( typeof v !== 'object' || ! value ) { return getInvalidTypeArg ( key , v , arg , inputType ) ; } return objectToArgs ( v , argInputType ) ; } ) , isEnum : false , inputType , schemaArg : arg , error : err , } ) ; } export function isInputArgType ( argType ) { if ( typeof argType === 'string' ) { return false ; } if ( Object . hasOwnProperty . call ( argType , 'values' ) ) { return false ; } return true ; } function scalarToArg ( key , value , arg , inputType ) { if ( hasCorrectScalarType ( value , arg , inputType ) ) { return new Arg ( { key , value , isEnum : inputType . location === 'enumTypes' , schemaArg : arg , inputType , } ) ; } return getInvalidTypeArg ( key , value , arg , inputType ) ; } function objectToArgs ( initialObj , inputType , possibilities ? , outputType ? ) { const obj = cleanObject ( initialObj ) ; const { fields : args , fieldMap } = inputType ; const requiredArgs = args . map ( ( arg ) => [ arg . name , undefined ] ) ; const objEntries = Object . entries ( obj || { } ) ; const entries = unionBy ( objEntries , requiredArgs , ( a ) => a [ 0 ] ) ; const argsList = entries . reduce ( ( acc , [ argName , value ] ) => { const schemaArg = fieldMap ? fieldMap [ argName ] : args . find ( ( a ) => a . name === argName ) ; if ( ! schemaArg ) { const didYouMeanField = typeof value === 'boolean' && outputType && outputType . fields . some ( ( f ) => f . name === argName ) ? argName : null ; acc . push ( new Arg ( { key : argName , value , error : { type : 'invalidName' , providedName : argName , providedValue : value , didYouMeanField , didYouMeanArg : ( ! didYouMeanField && getSuggestion ( argName , [ ... args . map ( ( a ) => a . name ) , 'select' ] ) ) || undefined , originalType : inputType , possibilities , outputType , } , } ) ) ; return acc ; } const arg = valueToArg ( argName , value , schemaArg ) ; if ( arg ) { acc . push ( arg ) ; } return acc ; } , [ ] as Arg [ ] ) ; if ( ( typeof inputType . constraints . minNumFields === 'number' && objEntries . length < inputType . constraints . minNumFields ) || argsList . find ( ( arg ) => arg . error ?. type === 'missingArg' || arg . error ?. type === 'atLeastOne' ) ) { const optionalMissingArgs = inputType . fields . filter ( ( field ) => ! field . isRequired && obj && ( typeof obj [ field . name ] === 'undefined' || obj [ field . name ] === null ) ) ; argsList . push ( ... optionalMissingArgs . map ( ( arg ) => { const argInputType = arg . inputTypes [ 0 ] ; return new Arg ( { key : arg . name , value : undefined , isEnum : argInputType . location === 'enumTypes' , error : { type : 'missingArg' , missingName : arg . name , missingArg : arg , atLeastOne : Boolean ( inputType . constraints . minNumFields ) || false , atMostOne : inputType . constraints . maxNumFields === 1 || false , } , inputType : argInputType , } ) ; } ) ) ; } return new Args ( argsList ) ; } export interface UnpackOptions { document : Document ; path : string [ ] ; data : any ; } param options export function unpack ( { document , path , data } ) { const result = deepGet ( data , path ) ; if ( result === 'undefined' ) { return null ; } if ( typeof result !== 'object' ) { return result ; } const field = getField ( document , path ) ; return mapScalars ( { field , data : result } ) ; } export interface MapScalarsOptions { field : Field ; data : any ; } export function mapScalars ( { field , data } ) { if ( ! data || typeof data !== 'object' || ! field . children || ! field . schemaField ) { return data ; } const deserializers = { DateTime : ( value ) => new Date ( value ) , Json : ( value ) => JSON . parse ( value ) , Bytes : ( value ) => Buffer . from ( value , 'base64' ) , Decimal : ( value ) => { return new Decimal ( value ) ; } , BigInt : ( value ) => BigInt ( value ) , } ; for ( const child of field . children ) { const outputType = child . schemaField ?. outputType . type ; if ( outputType && typeof outputType === 'string' ) { const deserializer = deserializers [ outputType ] ; if ( deserializer ) { if ( Array . isArray ( data ) ) { for ( const entry of data ) { if ( typeof entry [ child . name ] !== 'undefined' && entry [ child . name ] !== null ) { if ( Array . isArray ( entry [ child . name ] ) ) { entry [ child . name ] = entry [ child . name ] . map ( deserializer ) ; } else { entry [ child . name ] = deserializer ( entry [ child . name ] ) ; } } } } else { if ( typeof data [ child . name ] !== 'undefined' && data [ child . name ] !== null ) { if ( Array . isArray ( data [ child . name ] ) ) { data [ child . name ] = data [ child . name ] . map ( deserializer ) ; } else { data [ child . name ] = deserializer ( data [ child . name ] ) ; } } } } } if ( child . schemaField && child . schemaField . outputType . location === 'outputObjectTypes' ) { if ( Array . isArray ( data ) ) { for ( const entry of data ) { mapScalars ( { field : child , data : entry [ child . name ] } ) ; } } else { mapScalars ( { field : child , data : data [ child . name ] } ) ; } } } return data ; } export function getField ( document , path ) { const todo = path . slice ( ) ; const firstElement = todo . shift ( ) ; let pointer = document . children . find ( ( c ) => c . name === firstElement ) ; if ( ! pointer ) { throw new Error ( `Could not find field ${ firstElement } in document ${ document }` ) ; } while ( todo . length > 0 ) { const key = todo . shift ( ) ; if ( ! pointer ! . children ) { throw new Error ( `Can't get children for field ${ pointer } with child ${ key }` ) ; } const child = pointer ! . children . find ( ( c ) => c . name === key ) ; if ( ! child ) { throw new Error ( `Can't find child ${ key } of field ${ pointer }` ) ; } pointer = child ; } return pointer ! ; } function removeSelectFromPath ( path ) { return path . split ( '.' ) . filter ( ( p ) => p !== 'select' ) . join ( '.' ) ; } function removeSelectFromObject ( obj ) { const type = Object . prototype . toString . call ( obj ) ; if ( type === '[object Object]' ) { const copy = { } ; for ( const key in obj ) { if ( key === 'select' ) { for ( const subKey in obj [ 'select' ] ) { copy [ subKey ] = removeSelectFromObject ( obj [ 'select' ] [ subKey ] ) ; } } else { copy [ key ] = removeSelectFromObject ( obj [ key ] ) ; } } return copy ; } return obj ; } function transformAggregatePrintJsonArgs ( { ast , keyPaths , missingItems , valuePaths , } ) { const newKeyPaths = keyPaths . map ( removeSelectFromPath ) ; const newValuePaths = valuePaths . map ( removeSelectFromPath ) ; const newMissingItems = missingItems . map ( ( item ) => ( { path : removeSelectFromPath ( item . path ) , isRequired : item . isRequired , type : item . type , } ) ) ; const newAst = removeSelectFromObject ( ast ) ; return { ast : newAst , keyPaths : newKeyPaths , missingItems : newMissingItems , valuePaths : newValuePaths , } ; }