import { HttpError } from './http-error' ; import { HttpParams } from './http-params' ; type Url = string ; export type BeforeHook = ( request : Request , options : Options ) => void ; export type AfterHook = ( response : Response & { result : unknown ; } , options : Options ) => void ; export type Options = { baseUrl : Url ; hooks : { before : BeforeHook [ ] ; after : AfterHook [ ] ; } ; defaults ? : RequestInit ; } ; export type CreateOptions = Partial < Omit < Options , 'hooks' > & { hooks : { before ? : BeforeHook [ ] ; after ? : AfterHook [ ] ; } ; } > ; const defaultOptions = { baseUrl : '' , hooks : { before : [ ] , after : [ ] } } ; export class HttpClient { private readonly defaultHeaders = new Headers ( { Accept : 'application/json' , 'Content-Type' : 'application/json' } ) ; static create ( { baseUrl = defaultOptions . baseUrl , hooks = { ... defaultOptions . hooks } , defaults } = defaultOptions ) { const defaultBeforeHooks = hooks ?. before ?? [ ] ; const defaultAfterHooks = hooks ?. after ?? [ ] ; return new HttpClient ( { baseUrl , hooks : { before : defaultBeforeHooks , after : defaultAfterHooks } , defaults } ) ; } private constructor ( private readonly options ) { } async get < Result > ( url , httpParams ? ) { return this . sendRequest ( url , { method : 'GET' } , httpParams ) ; } async post < Result , Body > ( url , body , httpParams ? ) { return this . sendRequest ( url , { method : 'POST' , body : this . getParsedBody ( body ) } , httpParams ) ; } async put < Result , Body > ( url , body , httpParams ? ) { return this . sendRequest ( url , { method : 'PUT' , body : this . getParsedBody ( body ) } , httpParams ) ; } async delete < Result > ( url , httpParams ? ) { return this . sendRequest ( url , { method : 'DELETE' } , httpParams ) ; } private async sendRequest < Result > ( url , options , httpParams ? ) { const request = this . getRequest ( url , httpParams ) ; this . options . hooks . before . forEach ( hook => hook ( request , this . options ) ) ; const response = await fetch ( request , { ... this . options . defaults , ... options } ) ; const result = await response . json ( ) ; this . options . hooks . after . forEach ( hook => hook ( { ... response , result } , this . options ) ) ; return this . getResponse ( response ) ; } private getRequest ( url , httpParams ) { return new Request ( this . getUrl ( url , httpParams ) , { headers : this . defaultHeaders } ) ; } private getUrl ( url , params ? ) { let fullUrl = this . options . baseUrl === '' ? url : this . options . baseUrl + '/' + url ; if ( params !== undefined ) { fullUrl += `?${ params . toString ( ) }` ; } return new URL ( fullUrl ) . toString ( ) ; } private getParsedBody < Body > ( body ) { return JSON . stringify ( body ) ; } private async getResponse < Result > ( response ) { if ( ! response . ok ) { throw new HttpError ( { name : response . status . toString ( ) , message : response . statusText } ) ; } const json = await response . json ( ) ; return json as Result ; } }