import decamelize from 'decamelize' ; import { ColumnDefinitions , ColumnDefinition } from './operations/tablesTypes' ; import { Name , Type , Value } from './operations/generalTypes' ; import { MigrationOptions , Literal , RunnerOption } from './types' ; import { FunctionParam , FunctionParamType } from './operations/functionsTypes' ; import { PgLiteral } from '.' ; const identity = < T > ( v ) => v ; const quote = ( str ) => `"${ str }"` ; export const createSchemalize = ( shouldDecamelize , shouldQuote ) => { const transform = [ shouldDecamelize ? decamelize : identity , shouldQuote ? quote : identity ] . reduce ( ( acc , fn ) => fn === identity ? acc : ( x ) => acc ( fn ( x ) ) ) ; return ( v ) => { if ( typeof v === 'object' ) { const { schema , name } = v ; return ( schema ? `${ transform ( schema ) }.` : '' ) + transform ( name ) ; } return transform ( v ) ; } ; } ; export class StringIdGenerator { private ids : number [ ] = [ 0 ] ; constructor ( private readonly chars = 'abcdefghijklmnopqrstuvwxyz' ) { } next ( ) { const idsChars = this . ids . map ( ( id ) => this . chars [ id ] ) ; this . increment ( ) ; return idsChars . join ( '' ) ; } private increment ( ) { for ( let i = this . ids . length - 1 ; i >= 0 ; i -= 1 ) { this . ids [ i ] += 1 ; if ( this . ids [ i ] < this . chars . length ) { return ; } this . ids [ i ] = 0 ; } this . ids . unshift ( 0 ) ; } } const isPgLiteral = ( val ) : val is PgLiteral => typeof val === 'object' && val !== null && 'literal' in val && ( val as { literal : unknown ; } ) . literal === true ; export const escapeValue = ( val ) : string | number => { if ( val === null ) { return 'NULL' ; } if ( typeof val === 'boolean' ) { return val . toString ( ) ; } if ( typeof val === 'string' ) { let dollars ; const ids = new StringIdGenerator ( ) ; let index ; do { index = ids . next ( ) ; dollars = `$pg${ index }$` ; } while ( val . indexOf ( dollars ) >= 0 ) ; return `${ dollars }${ val }${ dollars }` ; } if ( typeof val === 'number' ) { return val ; } if ( Array . isArray ( val ) ) { const arrayStr = val . map ( escapeValue ) . join ( ',' ) . replace ( /ARRAY/g , '' ) ; return `ARRAY[${ arrayStr }]` ; } if ( isPgLiteral ( val ) ) { return val . value ; } return '' ; } ; export const createTransformer = ( literal ) => ( s , d ? ) => Object . keys ( d || { } ) . reduce ( ( str , p ) => { const v = d ?. [ p ] ; return str . replace ( new RegExp ( `{${ p }}` , 'g' ) , v === undefined ? '' : typeof v === 'string' || ( typeof v === 'object' && v !== null && 'name' in v ) ? literal ( v ) : String ( escapeValue ( v ) ) ) ; } , s ) ; export const getSchemas = ( schema ? ) : string [ ] => { const schemas = ( Array . isArray ( schema ) ? schema : [ schema ] ) . filter ( ( s ) : s is string => typeof s === 'string' && s . length > 0 ) ; return schemas . length > 0 ? schemas : [ 'public' ] ; } ; export const getMigrationTableSchema = ( options ) : string => options . migrationsSchema !== undefined ? options . migrationsSchema : getSchemas ( options . schema ) [ 0 ] ; const typeAdapters = { int : 'integer' , string : 'text' , float : 'real' , double : 'double precision' , datetime : 'timestamp' , bool : 'boolean' , } as const ; const defaultTypeShorthands = { id : { type : 'serial' , primaryKey : true } , } ; export const applyTypeAdapters = ( type ) : string => type in typeAdapters ? typeAdapters [ type as keyof typeof typeAdapters ] : type ; const toType = ( type ) : ColumnDefinition => ( typeof type === 'string' ? { type } : type ) ; const removeType = ( { type , ... rest } ) => rest ; export const applyType = ( type , extendingTypeShorthands = { } ) : ColumnDefinition & FunctionParamType => { const typeShorthands = { ... defaultTypeShorthands , ... extendingTypeShorthands , } ; const options = toType ( type ) ; let ext = null ; const types = [ options . type ] ; while ( typeShorthands [ types [ types . length - 1 ] ] ) { ext = { ... toType ( typeShorthands [ types [ types . length - 1 ] ] ) , ... ( ext === null ? { } : removeType ( ext ) ) , } ; if ( types . includes ( ext . type ) ) { throw new Error ( `Shorthands contain cyclic dependency: ${ types . join ( ', ' ) }, ${ ext . type }` ) ; } else { types . push ( ext . type ) ; } } return { ... ext , ... options , type : applyTypeAdapters ( ext ?. type ?? options . type ) , } ; } ; const formatParam = ( mOptions ) => ( param ) => { const { mode , name , type , default : defaultValue } = applyType ( param , mOptions . typeShorthands ) ; const options = [ ] ; if ( mode ) { options . push ( mode ) ; } if ( name ) { options . push ( mOptions . literal ( name ) ) ; } if ( type ) { options . push ( type ) ; } if ( defaultValue ) { options . push ( `DEFAULT ${ escapeValue ( defaultValue ) }` ) ; } return options . join ( ' ' ) ; } ; export const formatParams = ( params , mOptions ) => `(${ params . map ( formatParam ( mOptions ) ) . join ( ', ' ) })` ; export const makeComment = ( object , name , text ? ) => { const cmt = escapeValue ( text || null ) ; return `COMMENT ON ${ object } ${ name } IS ${ cmt };` ; } ; export const formatLines = ( lines , replace = '  ' , separator = ',' ) => lines . map ( ( line ) => line . replace ( /(?:\r\n|\r|\n)+/g , ' ' ) ) . join ( `${ separator }\n` ) . replace ( /^/gm , replace ) ; export function intersection < T > ( list1 , list2 ) { return list1 . filter ( ( element ) => list2 . includes ( element ) ) ; }