module import { BadRequestHttpException } from '@senhung/http-exceptions' ; import * as express from 'express' ; import { ApiDefinition , EndpointDefinition , buildGenericPathname , ExtractRuntimeType , deserialize } from 'rest-ts-core' ; import { Tuple2Dict } from 'rest-ts-core' ; /**
 * A promise of T or just T.
 */ type PromiseOrValue < T > = PromiseLike < T > | T ; /**
 * An express Request with proper typings.
 */ interface TypedRequest < T extends EndpointDefinition > extends express . Request { body : ExtractRuntimeType < T [ 'body' ] > ; params : Tuple2Dict < T [ 'params' ] > ; query : ExtractRuntimeType < T [ 'query' ] > ; } param req param res return export type RouteHandler < T extends EndpointDefinition > = ( req : TypedRequest < T > , res : express . Response ) => PromiseOrValue < ExtractRuntimeType < T [ 'response' ] > > ; type RouterDefinition < T extends ApiDefinition > = { [ K in keyof T ] : RouteHandler < T [ K ] [ 'def' ] > ; } ; type BuiltRouter < T extends ApiDefinition > = { _def : RouterDefinition < T > ; } ; interface ERROR_ALREADY_DEFINED { } ; type RouteHandlerBuilder < Api extends ApiDefinition , Built extends RouterDefinition < any > , K extends keyof Api , RemainingKeys extends keyof Api > = < H extends RouteHandler < Api [ K ] [ 'def' ] > > ( handler : H ) => RouterBuilder < Api , Built & { [ T in K ] : H ; } , RemainingKeys extends K ? never : RemainingKeys > ; type RouterBuilder < T extends ApiDefinition , Built extends RouterDefinition < any > , RemainingKeys extends keyof T > = { [ K in RemainingKeys ] : K extends keyof Built ? ERROR_ALREADY_DEFINED : RouteHandlerBuilder < T , Built , K , RemainingKeys > ; } & { _def : Built ; } ; param apiDefinition param cb export function buildRouter < T extends ApiDefinition > ( apiDefinition , cb ) { const builder = { _def : { } } as any ; Object . keys ( apiDefinition ) . forEach ( ( i ) => { builder [ i ] = ( handler ) => { builder . _def [ i ] = handler ; return builder ; } ; } ) ; return createRouter ( apiDefinition , cb ( builder ) . _def ) ; } /**
 * Alternate way to create a router.
 *
 * You should use  buildRouter whenever possible. It provides more safety and plays better with IDEs than
 * this method.
 *
 * This function works similarly to  buildRouter except that you pass a simple object hash and don't use a builder.
 * Each property of the hash is a route handler for the endpoint of the same name.
 *
 * Example:
 * ```ts
 * const router = createRouter(myCustomAPI, {
 *      listPublications: (req, res) => {
 *          return requestDatabaseForPublications({category: req.params.category})
 *      },
 *      addPublication: async (req, res) => {
 *          const saved = await attemptSavePublication(req.body);
 *          return { id: saved.id };
 *      },
 *      removePublication: async (req, res) => {
 *          await removePublication({ id: req.params.id });
 *          return 'OK';
 *      }
 * );
 * ```
 *  param apiDefinition param hash export function createRouter < T extends ApiDefinition > ( apiDefinition , hash ) { const router = express . Router ( ) ; Object . keys ( apiDefinition ) . forEach ( ( i ) => { const endpoint = apiDefinition [ i ] ; const def = endpoint . def ; const path = buildGenericPathname ( def ) ; switch ( endpoint . def . method ) { case 'GET' : router . get ( path , makeHandler ( def , hash [ i ] . bind ( hash ) ) ) ; break ; case 'POST' : router . post ( path , makeHandler ( def , hash [ i ] . bind ( hash ) ) ) ; break ; case 'PUT' : router . put ( path , makeHandler ( def , hash [ i ] . bind ( hash ) ) ) ; break ; case 'PATCH' : router . patch ( path , makeHandler ( def , hash [ i ] . bind ( hash ) ) ) ; break ; case 'DELETE' : router . delete ( path , makeHandler ( def , hash [ i ] . bind ( hash ) ) ) ; break ; default : ensureSwitchIsExhaustive ( endpoint . def . method ) ; } } ) ; return router ; } function makeHandler < T extends EndpointDefinition > ( def , fn ) { return ( req , res , next ) => { ( async ( ) => { const saneReq = sanitizeIncomingRequest ( def , req ) ; const data = await Promise . resolve ( fn ( saneReq , res ) ) ; if ( data !== undefined && ! res . headersSent ) { res . send ( data ) ; } else { next ( ) ; } } ) ( ) . catch ( next ) ; } ; } function sanitizeIncomingRequest < T extends EndpointDefinition > ( def , req ) { if ( req . body != null ) { try { req . body = def . body == null ? null : deserialize ( def . body , req . body ) ; } catch ( e ) { throw new BadRequestHttpException ( e ) ; } } if ( req . query != null ) { try { req . query = def . query == null ? null : deserialize ( def . query , req . query ) ; } catch ( e ) { throw new BadRequestHttpException ( e ) ; } } return req as TypedRequest < T > ; } function ensureSwitchIsExhaustive ( _t ) { }