import * as express from 'express' ; import * as runtypes from 'runtypes' ; import { IApiMethod , ApiMethodParams , ExtractApiMethodParams , ExtractApiMethodResult } from './ApiMethod' ; import ApiError from '../shared/ApiError' ; class Api < TMethods extends Record < string , IApiMethod < ApiMethodParams , unknown > > = { } > { constructor ( private readonly methods ) { } exec < TMethodName extends keyof TMethods > ( methodName , methodParams , request ) { return methodName in this . methods ? this . methods [ methodName ] . exec ( methodParams , request ) . catch ( err => { throw this . normalizeError ( err , methodName as string ) ; } ) as Promise < ExtractApiMethodResult < TMethods [ TMethodName ] > > : Promise . reject ( new ApiError ( ApiError . NOT_FOUND , `Method ${ methodName } not found` ) ) ; } private normalizeError ( err , methodName ) { if ( err instanceof ApiError ) { return new ApiError ( err . type , formatMethodErrorMessage ( methodName , err . message ) , err . data ) ; } if ( err instanceof runtypes . ValidationError ) { return new ApiError ( ApiError . BAD_REQUEST , formatMethodErrorMessage ( methodName , err . message ) , err ) ; } if ( err instanceof Error ) { return new ApiError ( ApiError . INTERNAL_ERROR , formatMethodErrorMessage ( methodName , err . message ) , err ) ; } if ( isErrorLike ( err ) ) { return new ApiError ( err . type , formatMethodErrorMessage ( methodName , err . message ) , err ) ; } return new ApiError ( ApiError . INTERNAL_ERROR , formatMethodErrorMessage ( methodName ) , err ) ; } } function isErrorLike ( err ) { return ( typeof err === 'object' && err !== null && typeof ( err as { type ? : unknown ; } ) . type === 'string' && ( typeof ( err as { message ? : unknown ; } ) . message === 'string' || typeof ( err as { message ? : unknown ; } ) . message === 'undefined' ) ) ; } function formatMethodErrorMessage ( methodName , errorMessage ? ) { return `${ methodName }: ${ errorMessage || 'Something went wrong' }` ; } type ExtractApiContract < TApi extends Api > = TApi extends Api < infer TMethods > ? { [ methodName in keyof TMethods ] : { params : ExtractApiMethodParams < TMethods [ methodName ] > ; result : ExtractApiMethodResult < TMethods [ methodName ] > ; } ; } : never ; export default Api ; export { ExtractApiContract } ;