import { ParseError } from "./errors" ; import { isNullOrUndefined , toCamelCase } from "./utility" ; import * as _ from "underscore" ; import * as UriJs from "urijs" ; /**
 * Object representation of a URI.
 */ export interface Uri { readonly authority ? : Authority ; readonly path : string ; readonly query ? : Queries ; readonly raw : string ; readonly scheme : string ; } /**
 * Supported URI schemes for Celery message brokers and result backends.
 */ export enum Scheme { Amqp = "amqp" , AmqpSecure = "amqps" , Redis = "redis" , RedisSecure = "rediss" , RedisSentinel = "sentinel" , RedisSentinelSecure = "sentinels" , RedisSocket = "redis+socket" , RedisSocketSecure = "rediss+socket" , Rpc = "rpc" , RpcSecure = "rpcs" } /**
 * For convenience.
 */ const SCHEMES = new Set < string > ( _ . values ( Scheme ) ) ; /**
 * URI authority.
 */ export interface Authority { readonly userInfo ? : UserInfo ; readonly host : string ; readonly port ? : number ; } /**
 * Parsed URI query - can be not present or present one or more times.
 */ export interface Queries { readonly [ key ] : string | Array < string > | undefined ; } /**
 * URI username and password.
 */ export interface UserInfo { readonly user : string ; readonly pass ? : string ; } param toParse returns throws export const parseUri = ( toParse ) : Uri => { const parsed = ( ( ) => { try { return UriJs . parse ( toParse ) ; } catch ( error ) { throw new ParseError ( "Celery.Uri.parse: unable to parse " + `${ toParse }: ${ error }` ) ; } } ) ( ) ; if ( isNullOrUndefined ( parsed . path ) || isNullOrUndefined ( parsed . protocol ) ) { throw new ParseError ( `Celery.Uri.parse: unable to parse ${ parsed }: ` + "missing path or protocol" ) ; } const withRequired = { path : parsed . path , raw : toParse , scheme : parsed . protocol . toLowerCase ( ) , } ; const withAuthority = addHostUserPassAndPort ( parsed , withRequired ) ; const withQuery = addQuery ( parsed , withAuthority ) ; return withQuery ; } ; param rawUri returns throws export const getScheme = ( rawUri ) : Scheme => { const SCHEME_REGEX = /^([A-Za-z][A-Za-z\d+.-]*):/ ; const SCHEME_INDEX = 1 ; const maybeMatches = rawUri . match ( SCHEME_REGEX ) ; if ( isNullOrUndefined ( maybeMatches ) ) { throw new ParseError ( `invalid uri "${ rawUri }"` ) ; } const matches = maybeMatches ; const scheme = matches [ SCHEME_INDEX ] . toLowerCase ( ) ; if ( ! SCHEMES . has ( scheme ) ) { throw new ParseError ( `unrecognized scheme "${ scheme }"` ) ; } return scheme as Scheme ; } ; /**
 * A URI as parsed by `urijs`.
 */ interface RawUri { readonly fragment ? : string ; readonly hostname ? : string ; readonly password ? : string ; readonly path : string ; readonly port ? : string ; readonly protocol : string ; readonly query ? : string ; readonly username ? : string ; } param uri param parsing returns const addHost = ( uri , parsing ) : Uri => { if ( isNullOrUndefined ( uri . hostname ) ) { return parsing ; } return { ... parsing , authority : { host : validateHost ( uri . hostname ) . toLowerCase ( ) , } , } ; } ; param uri param parsing returns const addHostAndUser = ( uri , parsing ) : Uri => { const withHost = addHost ( uri , parsing ) ; if ( isNullOrUndefined ( withHost . authority ) || isNullOrUndefined ( uri . username ) ) { return withHost ; } return { ... withHost , authority : { ... withHost . authority , userInfo : { user : uri . username , } , } , } ; } ; param uri param parsing returns const addHostUserAndPass = ( uri , parsing ) : Uri => { const withUser = addHostAndUser ( uri , parsing ) ; if ( isNullOrUndefined ( withUser . authority ) ) { return withUser ; } if ( isNullOrUndefined ( withUser . authority . userInfo ) && ! isNullOrUndefined ( uri . password ) ) { return { ... withUser , authority : { ... withUser . authority , userInfo : { pass : uri . password , user : "" , } , } , } ; } if ( isNullOrUndefined ( withUser . authority . userInfo ) || isNullOrUndefined ( uri . password ) ) { return withUser ; } return { ... withUser , authority : { ... withUser . authority , userInfo : { ... withUser . authority . userInfo , pass : uri . password , } , } , } ; } ; param uri param parsing returns const addHostUserPassAndPort = ( uri , parsing ) : Uri => { const withPass = addHostUserAndPass ( uri , parsing ) ; if ( isNullOrUndefined ( withPass . authority ) || isNullOrUndefined ( uri . port ) ) { return withPass ; } return { ... withPass , authority : { ... withPass . authority , port : parsePort ( uri . port ) , } , } ; } ; param uri param parsing returns const addQuery = ( uri , parsing ) : Uri => { const REGEX = /^[A-Za-z\d*-._+%]+=[A-Za-z\d*-._+%]*(?:&[A-Za-z\d*-._+%]+=[A-Za-z\d*-._+%+]*)*$/ ; if ( isNullOrUndefined ( uri . query ) ) { return parsing ; } if ( ! REGEX . test ( uri . query ) ) { throw new ParseError ( `query "${ uri . query }" is not a valid HTML query` ) ; } const unconverted = UriJs . parseQuery ( uri . query ) as Queries ; const camelCaseQuery = ( queries , key ) => { const cased = toCamelCase ( key ) ; if ( cased === key ) { return queries ; } const { [ key ] : _key , ... renamed } = { ... queries , [ cased ] : queries [ key ] , } ; return renamed ; } ; const query = Object . keys ( unconverted ) . reduce ( camelCaseQuery , unconverted ) ; return { ... parsing , query , } ; } ; param maybePort returns throws const parsePort = ( maybePort ) : number => { const maybeMatches = /^\d+$/ . exec ( maybePort ) ; if ( isNullOrUndefined ( maybeMatches ) ) { throw new ParseError ( `could not parse "${ maybePort }" as port` ) ; } const parsed = Number . parseInt ( maybePort , 10 ) ; return parsed ; } ; param maybeHost returns throws const validateHost = ( maybeHost ) : string => { if ( HOST_REGEX . test ( maybeHost ) === false ) { throw new ParseError ( `invalid host "${ maybeHost }"` ) ; } return maybeHost ; } ; const HOST_REGEX = /^(?:[A-Za-z\d]|[A-Za-z\d][A-Za-z\d-]{0,61}?[A-Za-z\d])(?:\.(?:[A-Za-z\d]|[A-Za-z\d][A-Za-z\d-]{0,61}?[A-Za-z\d]))*$/ ;