import { Spec , ValidatorSpec } from './types' ; import { EnvError } from './errors' ; const isFQDN = ( input ) => { if ( ! input . length ) return false ; const parts = input . split ( '.' ) ; for ( let part , i = 0 ; i < parts . length ; i ++ ) { part = parts [ i ] ; if ( ! /^[a-z\u00a1-\uffff0-9-]+$/i . test ( part ) ) return false ; if ( /[\uff01-\uff5e]/ . test ( part ) ) return false ; if ( part [ 0 ] === '-' || part [ part . length - 1 ] === '-' ) return false ; } return true ; } ; const ipv4Regex = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/ ; const ipv6Regex = /([a-f0-9]+:+)+[a-f0-9]+/ ; const isIP = ( input ) => { if ( ! input . length ) return false ; return ipv4Regex . test ( input ) || ipv6Regex . test ( input ) ; } ; const EMAIL_REGEX = /^[^@\s]+@[^@\s]+\.[^@\s]+$/ ; export const makeValidator = < T > ( parseFn ) => { return function ( spec ? ) : ValidatorSpec < T > { return { ... spec , _parse : parseFn } ; } ; } ; export function bool < T extends boolean = boolean > ( spec ? ) { return makeValidator ( ( input ) => { switch ( input ) { case true : case 'true' : case 't' : case '1' : return true as T ; case false : case 'false' : case 'f' : case '0' : return false as T ; default : throw new EnvError ( `Invalid bool input: "${ input }"` ) ; } } ) ( spec ) ; } export function num < T extends number = number > ( spec ? ) { return makeValidator ( ( input ) => { const coerced = parseFloat ( input ) ; if ( Number . isNaN ( coerced ) ) throw new EnvError ( `Invalid number input: "${ input }"` ) ; return coerced as T ; } ) ( spec ) ; } export function str < T extends string = string > ( spec ? ) { return makeValidator ( ( input ) => { if ( typeof input === 'string' ) return input as T ; throw new EnvError ( `Not a string: "${ input }"` ) ; } ) ( spec ) ; } export function email < T extends string = string > ( spec ? ) { return makeValidator ( ( x ) => { if ( EMAIL_REGEX . test ( x ) ) return x as T ; throw new EnvError ( `Invalid email address: "${ x }"` ) ; } ) ( spec ) ; } export function host < T extends string = string > ( spec ? ) { return makeValidator ( ( input ) => { if ( ! isFQDN ( input ) && ! isIP ( input ) ) { throw new EnvError ( `Invalid host (domain or ip): "${ input }"` ) ; } return input as T ; } ) ( spec ) ; } export function port < T extends number = number > ( spec ? ) { return makeValidator ( ( input ) => { const coerced = + input ; if ( Number . isNaN ( coerced ) || `${ coerced }` !== `${ input }` || coerced % 1 !== 0 || coerced < 1 || coerced > 65535 ) { throw new EnvError ( `Invalid port input: "${ input }"` ) ; } return coerced as T ; } ) ( spec ) ; } export function url < T extends string = string > ( spec ? ) { return makeValidator ( ( x ) => { try { new URL ( x ) ; return x as T ; } catch ( e ) { throw new EnvError ( `Invalid url: "${ x }"` ) ; } } ) ( spec ) ; } export function json < T = any > ( spec ? ) { return makeValidator < T > ( ( x ) => { try { return JSON . parse ( x ) as T ; } catch ( e ) { throw new EnvError ( `Invalid json: "${ x }"` ) ; } } ) ( spec ) ; }