import { decamelizeKeys } from 'xcase' ; import FormData from 'form-data' ; import { stringify } from 'qs' ; export interface Constructable < T = any > { new ( ... args ) : T ; } export interface RequesterType { get ( endpoint , options ? ) : Promise < any > ; post ( endpoint , options ? ) : Promise < any > ; put ( endpoint , options ? ) : Promise < any > ; delete ( endpoint , options ? ) : Promise < any > ; stream ? ( endpoint , options ? ) : NodeJS . ReadableStream ; } export type DefaultResourceOptions = { headers : { [ header ] : string ; } ; requestTimeout : number ; url : string ; rejectUnauthorized : boolean ; } ; export type DefaultRequestOptions = { body ? : FormData | Record < string , unknown > ; query ? : Record < string , unknown > ; sudo ? : string ; method ? : string ; } ; export type DefaultRequestReturn = { headers : Record < string , string > | Headers ; timeout ? : number ; method : string ; searchParams ? : string ; prefixUrl ? : string ; body ? : string | FormData ; } ; export function formatQuery ( params = { } ) { const decamelized = decamelizeKeys ( params ) ; return stringify ( decamelized , { arrayFormat : 'brackets' } ) ; } export type OptionsHandlerFn = ( serviceOptions : DefaultResourceOptions , requestOptions : DefaultRequestOptions ) => DefaultRequestReturn ; export function defaultOptionsHandler ( resourceOptions , { body , query , sudo , method = 'get' } = { } ) { const { headers : preconfiguredHeaders , requestTimeout , url } = resourceOptions ; const headers = { ... preconfiguredHeaders } ; let bod ; if ( sudo ) headers . sudo = sudo ; if ( typeof body === 'object' && body . constructor . name !== 'FormData' ) { bod = JSON . stringify ( decamelizeKeys ( body ) ) ; headers [ 'content-type' ] = 'application/json' ; } else { bod = body as FormData ; } return { headers , timeout : requestTimeout , method , searchParams : formatQuery ( query ) , prefixUrl : url , body : bod , } ; } export type RequestHandlerFn = ( endpoint : string , options ? : Record < string , unknown > ) => any | Promise < { body : Record < string , unknown > | Record < string , unknown > [ ] ; headers : Record < string , unknown > | Headers ; status : number ; } > ; export function createRequesterFn ( optionsHandler , requestHandler ) { const methods = [ 'get' , 'post' , 'put' , 'delete' , 'stream' ] ; return ( serviceOptions ) => { const requester = { } as RequesterType ; methods . forEach ( ( m ) => { requester [ m ] = ( endpoint , options ) => { const requestOptions = optionsHandler ( serviceOptions , { ... options , method : m } ) ; return requestHandler ( endpoint , requestOptions ) ; } ; } ) ; return requester ; } ; } function extendClass < T extends Constructable > ( Base , customConfig ) { return class extends Base { constructor ( ... options ) { const [ config , ... opts ] = options ; super ( { ... customConfig , ... config } , ... opts ) ; } } ; } export function presetResourceArguments < T > ( resources , customConfig = { } ) { const updated = { } ; Object . entries ( resources ) . filter ( ( [ , s ] ) => typeof s === 'function' ) . forEach ( ( [ k , r ] ) => { updated [ k ] = extendClass ( r , customConfig ) ; } ) ; return updated as T ; }