import { Key , ParseOptions , pathToRegexp , TokensToRegexpOptions } from 'path-to-regexp' ; import { Context } from './context.js' ; import { composeMiddleware , Middleware } from './middleware.js' ; export type PathToRegExpOptions = TokensToRegexpOptions & ParseOptions ; export const Method = ( method ) => { method = method . toUpperCase ( ) ; return ( { req } ) => req . method === method ; } ; export const Get = Method ( 'get' ) ; export const Post = Method ( 'post' ) ; export const Put = Method ( 'put' ) ; export const Patch = Method ( 'patch' ) ; export const Delete = Method ( 'delete' ) ; export const Head = Method ( 'head' ) ; export const Options = Method ( 'options' ) ; export const Header = ( header , value ) => { value = value . toLowerCase ( ) ; return ( { req } ) => req . headers . get ( header ) === value ; } ; export const Host = ( host ) => Header ( 'host' , host ) ; export const Referer = ( host ) => Header ( 'referer' , host ) ; export const Path = ( pattern , options ? ) => { const keys = [ ] ; const regExp = pathToRegexp ( pattern , keys , options ) ; return ( { req : { url , params } } ) => { const match = url . pathname . match ( regExp ) ; if ( ! match ) { return false ; } collectParameters ( keys , match , params ) ; return true ; } ; } ; export type RouteCondition = ( context : Context ) => boolean ; export interface Route { conditions : RouteCondition [ ] ; middleware : Middleware ; } export interface RouterOptions { pathToRegExpOptions ? : PathToRegExpOptions ; } export const defaultRouterOptions = { pathToRegExpOptions : { strict : true } } ; export class Router { private readonly routes : Route [ ] ; constructor ( private readonly options = defaultRouterOptions ) { this . routes = [ ] ; } public get ( pathname , ... middleware ) { const opts = this . options . pathToRegExpOptions ; return this . compose ( [ Get , Path ( pathname , opts ) ] , ... middleware ) ; } public post ( pathname , ... middleware ) { const opts = this . options . pathToRegExpOptions ; return this . compose ( [ Post , Path ( pathname , opts ) ] , ... middleware ) ; } public put ( pathname , ... middleware ) { const opts = this . options . pathToRegExpOptions ; return this . compose ( [ Put , Path ( pathname , opts ) ] , ... middleware ) ; } public patch ( pathname , ... middleware ) { const opts = this . options . pathToRegExpOptions ; return this . compose ( [ Patch , Path ( pathname , opts ) ] , ... middleware ) ; } public delete ( pathname , ... middleware ) { const opts = this . options . pathToRegExpOptions ; return this . compose ( [ Delete , Path ( pathname , opts ) ] , ... middleware ) ; } public head ( pathname , ... middleware ) { const opts = this . options . pathToRegExpOptions ; return this . compose ( [ Head , Path ( pathname , opts ) ] , ... middleware ) ; } public all ( pathname , ... middleware ) { const opts = this . options . pathToRegExpOptions ; return this . compose ( [ Path ( pathname , opts ) ] , ... middleware ) ; } public middleware : Middleware = async ( ctx , next ) => { const resolved = this . resolve ( ctx ) ; if ( resolved ) { await resolved . middleware ( ctx , next ) ; } else { await next ( ) ; } } ; public compose ( conditions , ... middleware ) { this . routes . push ( { conditions , middleware : composeMiddleware ( middleware ) } ) ; return this ; } private resolve ( ctx ) { return this . routes . find ( ( { conditions } ) => conditions . length === 0 || conditions . every ( c => c ( ctx ) ) ) ; } } function collectParameters ( keys , match , params ) { for ( let i = 1 ; i < match . length ; i ++ ) { const name = keys [ i - 1 ] . name ; const value = match [ i ] ; if ( ! value ) { continue ; } params [ name ] = decodeURIComponent ( value ) ; } }