import chalk from 'chalk' ; import indent from 'indent-string' ; import leven from 'js-levenshtein' ; import type { DMMF } from '../dmmf-types' ; import Decimal from 'decimal.js' ; import type { DMMFClass } from '../dmmf' ; export interface Dictionary < T > { [ key ] : T ; } export const keyBy = ( collection , prop ) => { const acc = { } ; for ( const obj of collection ) { const key = obj [ prop ] ; acc [ key ] = obj ; } return acc ; } ; export const keyBy2 = ( collection1 , collection2 , prop ) => { const acc = { } ; for ( const obj of collection1 ) { const key = obj [ prop ] ; acc [ key ] = obj ; } for ( const obj of collection2 ) { const key = obj [ prop ] ; acc [ key ] = obj ; } return acc ; } ; export const ScalarTypeTable = { String : true , Int : true , Float : true , Boolean : true , Long : true , DateTime : true , ID : true , UUID : true , Json : true , Bytes : true , Decimal : true , BigInt : true , } ; export function isScalar ( str ) { if ( typeof str !== 'string' ) { return false ; } return ScalarTypeTable [ str ] || false ; } export const needNamespace = { Json : 'JsonValue' , Decimal : 'Decimal' , } ; export function needsNamespace ( fieldType , dmmf ) { if ( typeof fieldType === 'string' ) { if ( dmmf . datamodelEnumMap [ fieldType ] ) { return false ; } if ( GraphQLScalarToJSTypeTable [ fieldType ] ) { return Boolean ( needNamespace [ fieldType ] ) ; } } return true ; } export const GraphQLScalarToJSTypeTable = { String : 'string' , Int : 'number' , Float : 'number' , Boolean : 'boolean' , Long : 'number' , DateTime : [ 'Date' , 'string' ] , ID : 'string' , UUID : 'string' , Json : 'JsonValue' , Bytes : 'Buffer' , Decimal : [ 'Decimal' , 'number' , 'string' ] , BigInt : [ 'bigint' , 'number' ] , } ; export const JSOutputTypeToInputType = { JsonValue : 'InputJsonValue' , } ; export const JSTypeToGraphQLType = { string : 'String' , boolean : 'Boolean' , object : 'Json' , } ; export function stringifyGraphQLType ( type ) { if ( typeof type === 'string' ) { return type ; } return type . name ; } export function wrapWithList ( str , isList ) { if ( isList ) { return `List<${ str }>` ; } return str ; } const RFC_3339_REGEX = /^(\d{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60))(\.\d{1,})?(([Z])|([+|-]([01][0-9]|2[0-3]):[0-5][0-9]))$/ ; const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i ; export function getGraphQLType ( value , potentialType ? ) { if ( value === null ) { return 'null' ; } if ( Object . prototype . toString . call ( value ) === '[object BigInt]' ) { return 'BigInt' ; } if ( Decimal . isDecimal ( value ) ) { return 'Decimal' ; } if ( Buffer . isBuffer ( value ) ) { return 'Bytes' ; } if ( Array . isArray ( value ) ) { let scalarTypes = value . reduce ( ( acc , val ) => { const type = getGraphQLType ( val , potentialType ) ; if ( ! acc . includes ( type ) ) { acc . push ( type ) ; } return acc ; } , [ ] ) ; if ( scalarTypes . includes ( 'Float' ) && scalarTypes . includes ( 'Int' ) ) { scalarTypes = [ 'Float' ] ; } return `List<${ scalarTypes . join ( ' | ' ) }>` ; } const jsType = typeof value ; if ( jsType === 'number' ) { if ( Math . trunc ( value ) === value ) { return 'Int' ; } else { return 'Float' ; } } if ( Object . prototype . toString . call ( value ) === '[object Date]' ) { return 'DateTime' ; } if ( jsType === 'string' ) { if ( UUID_REGEX . test ( value ) ) { return 'UUID' ; } const date = new Date ( value ) ; if ( potentialType && typeof potentialType === 'object' && ( potentialType as DMMF . SchemaEnum ) . values && ( potentialType as DMMF . SchemaEnum ) . values . includes ( value ) ) { return potentialType . name ; } if ( date . toString ( ) === 'Invalid Date' ) { return 'String' ; } if ( RFC_3339_REGEX . test ( value ) ) { return 'DateTime' ; } } return JSTypeToGraphQLType [ jsType ] ; } export function graphQLToJSType ( gql ) { return GraphQLScalarToJSTypeTable [ gql ] ; } export function getSuggestion ( str , possibilities ) { const bestMatch = possibilities . reduce < { distance : number ; str : string | null ; } > ( ( acc , curr ) => { const distance = leven ( str , curr ) ; if ( distance < acc . distance ) { return { distance , str : curr , } ; } return acc ; } , { distance : Math . min ( Math . floor ( str . length ) * 1.1 , ... possibilities . map ( ( p ) => p . length * 3 ) ) , str : null , } ) ; return bestMatch . str ; } export function stringifyInputType ( input , greenKeys = false ) { if ( typeof input === 'string' ) { return input ; } if ( ( input as DMMF . SchemaEnum ) . values ) { return `enum ${ input . name } {\n${ indent ( ( input as DMMF . SchemaEnum ) . values . join ( ', ' ) , 2 ) }\n}` ; } else { const body = indent ( ( input as DMMF . InputType ) . fields . map ( ( arg ) => { const key = `${ arg . name }` ; const str = `${ greenKeys ? chalk . green ( key ) : key }${ arg . isRequired ? '' : '?' }: ${ chalk . white ( arg . inputTypes . map ( ( argType ) => { return wrapWithList ( argIsInputType ( argType . type ) ? argType . type . name : stringifyGraphQLType ( argType . type ) , argType . isList ) ; } ) . join ( ' | ' ) ) }` ; if ( ! arg . isRequired ) { return chalk . dim ( str ) ; } return str ; } ) . join ( '\n' ) , 2 ) ; return `${ chalk . dim ( 'type' ) } ${ chalk . bold . dim ( input . name ) } ${ chalk . dim ( '{' ) }\n${ body }\n${ chalk . dim ( '}' ) }` ; } } export function argIsInputType ( arg ) { if ( typeof arg === 'string' ) { return false ; } return true ; } export function getInputTypeName ( input ) { if ( typeof input === 'string' ) { if ( input === 'Null' ) { return 'null' ; } return input ; } return input . name ; } export function getOutputTypeName ( input ) { if ( typeof input === 'string' ) { return input ; } return input . name ; } export function inputTypeToJson ( input , isRequired , nameOnly = false ) { if ( typeof input === 'string' ) { if ( input === 'Null' ) { return 'null' ; } return input ; } if ( ( input as DMMF . SchemaEnum ) . values ) { return ( input as DMMF . SchemaEnum ) . values . join ( ' | ' ) ; } const inputType = input as DMMF . InputType ; const showDeepType = isRequired && inputType . fields . every ( ( arg ) => arg . inputTypes [ 0 ] . location === 'inputObjectTypes' || arg . inputTypes [ 1 ] ?. location === 'inputObjectTypes' ) ; if ( nameOnly ) { return getInputTypeName ( input ) ; } return inputType . fields . reduce ( ( acc , curr ) => { let str = '' ; if ( ! showDeepType && ! curr . isRequired ) { str = curr . inputTypes . map ( ( argType ) => getInputTypeName ( argType . type ) ) . join ( ' | ' ) ; } else { str = curr . inputTypes . map ( ( argInputType ) => inputTypeToJson ( argInputType . type , curr . isRequired , true ) ) . join ( ' | ' ) ; } acc [ curr . name + ( curr . isRequired ? '' : '?' ) ] = str ; return acc ; } , { } ) ; } export function destroyCircular ( from , seen = [ ] ) { const to = Array . isArray ( from ) ? [ ] : { } ; seen . push ( from ) ; for ( const key of Object . keys ( from ) ) { const value = from [ key ] ; if ( typeof value === 'function' ) { continue ; } if ( ! value || typeof value !== 'object' ) { to [ key ] = value ; continue ; } if ( seen . indexOf ( from [ key ] ) === - 1 ) { to [ key ] = destroyCircular ( from [ key ] , seen . slice ( 0 ) ) ; continue ; } to [ key ] = '[Circular]' ; } if ( typeof from . name === 'string' ) { to . name = from . name ; } if ( typeof from . message === 'string' ) { to . message = from . message ; } if ( typeof from . stack === 'string' ) { to . stack = from . stack ; } return to ; } export function unionBy < T > ( arr1 , arr2 , iteratee ) { const map = { } ; for ( const element of arr1 ) { map [ iteratee ( element ) ] = element ; } for ( const element of arr2 ) { const key = iteratee ( element ) ; if ( ! map [ key ] ) { map [ key ] = element ; } } return Object . values ( map ) ; } export function uniqBy < T > ( arr , iteratee ) { const map = { } ; for ( const element of arr ) { map [ iteratee ( element ) ] = element ; } return Object . values ( map ) ; } export function capitalize ( str ) { return str [ 0 ] . toUpperCase ( ) + str . slice ( 1 ) ; } param name export function lowerCase ( name ) { return name . substring ( 0 , 1 ) . toLowerCase ( ) + name . substring ( 1 ) ; } export function isGroupByOutputName ( type ) { return type . endsWith ( 'GroupByOutputType' ) ; } export function isSchemaEnum ( type ) { return ( typeof type === 'object' && type . name && typeof type . name === 'string' && type . values && Array . isArray ( type . values ) ) ; }