2022-01-20 06:54:55 +00:00
|
|
|
import {Collection, Either, ErrorWithContext, Maybe, Pipeline, PrefixTypeArray, right} from '../../util'
|
2021-06-03 03:36:25 +00:00
|
|
|
import {ResponseFactory} from '../response/ResponseFactory'
|
2022-01-19 19:24:59 +00:00
|
|
|
import {HTTPMethod, Request} from '../lifecycle/Request'
|
|
|
|
import {constructable, Constructable, Container, Instantiable, isInstantiableOf, TypedDependencyKey} from '../../di'
|
2021-06-03 03:36:25 +00:00
|
|
|
import {Middleware} from './Middleware'
|
2022-01-19 19:24:59 +00:00
|
|
|
import {Valid, Validator, ValidatorFactory} from '../../validation/Validator'
|
|
|
|
import {validateMiddleware} from '../../validation/middleware'
|
|
|
|
import {RouteGroup} from './RouteGroup'
|
2021-06-03 03:36:25 +00:00
|
|
|
import {Config} from '../../service/Config'
|
2022-01-19 19:24:59 +00:00
|
|
|
import {Application} from '../../lifecycle/Application'
|
2021-03-08 17:08:56 +00:00
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
|
|
|
* Type alias for an item that is a valid response object, or lack thereof.
|
|
|
|
*/
|
2021-03-08 17:08:56 +00:00
|
|
|
export type ResponseObject = ResponseFactory | string | number | void | any | Promise<ResponseObject>
|
2021-03-25 13:50:13 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Type alias for a function that applies a route handler to the request.
|
|
|
|
* The goal is to transform RouteHandlers to ResolvedRouteHandler.
|
|
|
|
*/
|
2021-03-08 17:08:56 +00:00
|
|
|
export type ResolvedRouteHandler = (request: Request) => ResponseObject
|
2021-03-08 15:00:43 +00:00
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
export type ParameterProvidingMiddleware<T> = (request: Request) => Either<ResponseObject, T>
|
2021-03-08 15:00:43 +00:00
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
export interface HandledRoute<TReturn extends ResponseObject, THandlerParams extends unknown[] = []> {
|
|
|
|
handler: Constructable<(...x: THandlerParams) => TReturn>
|
2021-03-08 15:00:43 +00:00
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
/**
|
|
|
|
* Set a programmatic name for this route.
|
|
|
|
* @param name
|
|
|
|
*/
|
|
|
|
alias(name: string): this
|
|
|
|
}
|
|
|
|
|
|
|
|
export class Route<TReturn extends ResponseObject, THandlerParams extends unknown[] = []> {
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Routes that have been created and registered in the application. */
|
2022-01-19 19:24:59 +00:00
|
|
|
private static registeredRoutes: Route<unknown, unknown[]>[] = []
|
2021-03-25 13:50:13 +00:00
|
|
|
|
|
|
|
/** Groups of routes that have been registered with the application. */
|
2021-03-08 15:00:43 +00:00
|
|
|
private static registeredGroups: RouteGroup[] = []
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
|
|
|
* The current nested group stack. This is used internally when compiling the routes by nested group.
|
|
|
|
* @private
|
|
|
|
*/
|
2021-03-08 15:00:43 +00:00
|
|
|
private static compiledGroupStack: RouteGroup[] = []
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Register a route group handler. */
|
2021-06-03 03:36:25 +00:00
|
|
|
public static registerGroup(group: RouteGroup): void {
|
2021-03-08 15:00:43 +00:00
|
|
|
this.registeredGroups.push(group)
|
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
2022-01-17 21:57:40 +00:00
|
|
|
* Load and compile all the registered routes and their groups, accounting
|
2021-03-25 13:50:13 +00:00
|
|
|
* for nested groups and resolving handlers.
|
|
|
|
*
|
|
|
|
* This function attempts to resolve the route handlers ahead of time to cache
|
|
|
|
* them and also expose any handler resolution errors that might happen at runtime.
|
|
|
|
*/
|
2022-01-19 19:24:59 +00:00
|
|
|
public static async compile(): Promise<Route<unknown, unknown[]>[]> {
|
2021-03-08 15:00:43 +00:00
|
|
|
let registeredRoutes = this.registeredRoutes
|
|
|
|
const registeredGroups = this.registeredGroups
|
|
|
|
|
|
|
|
this.registeredRoutes = []
|
|
|
|
this.registeredGroups = []
|
|
|
|
|
2021-03-09 15:55:18 +00:00
|
|
|
const configService = <Config> Container.getContainer().make(Config)
|
|
|
|
const globalMiddleware = configService.get('server.middleware.global', {})
|
|
|
|
|
2021-03-08 15:00:43 +00:00
|
|
|
const stack = [...this.compiledGroupStack].reverse()
|
|
|
|
for ( const route of registeredRoutes ) {
|
|
|
|
for ( const group of stack ) {
|
|
|
|
route.prepend(group.prefix)
|
2022-01-19 19:24:59 +00:00
|
|
|
group.getPreflight()
|
2022-03-29 06:14:46 +00:00
|
|
|
.each(def => route.preflight.prepend(
|
|
|
|
request => request.make<Middleware>(def, request).apply(),
|
|
|
|
))
|
2021-03-08 15:00:43 +00:00
|
|
|
}
|
2021-03-08 17:08:56 +00:00
|
|
|
|
2021-03-09 15:42:19 +00:00
|
|
|
for ( const group of this.compiledGroupStack ) {
|
2022-01-19 19:24:59 +00:00
|
|
|
group.getPostflight()
|
2022-03-29 06:14:46 +00:00
|
|
|
.each(def => route.postflight.push(
|
|
|
|
request => request.make<Middleware>(def, request).apply(),
|
|
|
|
))
|
2021-03-09 15:42:19 +00:00
|
|
|
}
|
|
|
|
|
2021-03-09 15:55:18 +00:00
|
|
|
// Add the global pre- and post- middleware
|
|
|
|
if ( Array.isArray(globalMiddleware?.pre) ) {
|
|
|
|
const globalPre = [...globalMiddleware.pre].reverse()
|
|
|
|
for ( const item of globalPre ) {
|
2022-01-19 19:24:59 +00:00
|
|
|
if ( !isInstantiableOf(item, Middleware) ) {
|
|
|
|
throw new ErrorWithContext(`Invalid global pre-middleware definition. Global middleware must be static references to Middleware implementations.`, {
|
2021-03-09 15:55:18 +00:00
|
|
|
configKey: 'server.middleware.global.pre',
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
route.preflight.prepend(request => request.make<Middleware>(item, request).apply())
|
2021-03-09 15:55:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( Array.isArray(globalMiddleware?.post) ) {
|
|
|
|
const globalPost = [...globalMiddleware.post]
|
|
|
|
for ( const item of globalPost ) {
|
2022-01-19 19:24:59 +00:00
|
|
|
if ( !isInstantiableOf(item, Middleware) ) {
|
|
|
|
throw new ErrorWithContext(`Invalid global post-middleware definition. Global middleware must be static references to Middleware implementations.`, {
|
2021-03-09 15:55:18 +00:00
|
|
|
configKey: 'server.middleware.global.post',
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
route.postflight.push(request => request.make<Middleware>(item, request).apply())
|
2021-03-09 15:55:18 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-08 15:00:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for ( const group of registeredGroups ) {
|
|
|
|
this.compiledGroupStack.push(group)
|
|
|
|
await group.group()
|
|
|
|
|
|
|
|
const childCompilation = await this.compile()
|
|
|
|
registeredRoutes = registeredRoutes.concat(childCompilation)
|
|
|
|
|
|
|
|
this.compiledGroupStack.pop()
|
|
|
|
}
|
|
|
|
|
|
|
|
return registeredRoutes
|
|
|
|
}
|
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
|
|
|
* Create a new route on the given endpoint for the given HTTP verb.
|
|
|
|
* @param method
|
2022-01-19 19:24:59 +00:00
|
|
|
* @param endpoint
|
2021-03-25 13:50:13 +00:00
|
|
|
*/
|
2022-01-19 19:24:59 +00:00
|
|
|
public static endpoint(method: HTTPMethod | HTTPMethod[], endpoint: string): Route<ResponseObject> {
|
|
|
|
return new Route(method, endpoint)
|
2021-03-08 15:00:43 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
|
|
|
* Create a new GET route on the given endpoint.
|
|
|
|
*/
|
2022-01-19 19:24:59 +00:00
|
|
|
public static get(endpoint: string): Route<ResponseObject> {
|
|
|
|
return this.endpoint('get', endpoint)
|
2021-03-08 15:00:43 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Create a new POST route on the given endpoint. */
|
2022-01-19 19:24:59 +00:00
|
|
|
public static post(endpoint: string): Route<ResponseObject> {
|
|
|
|
return this.endpoint('post', endpoint)
|
2021-03-08 15:00:43 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Create a new PUT route on the given endpoint. */
|
2022-01-19 19:24:59 +00:00
|
|
|
public static put(endpoint: string): Route<ResponseObject> {
|
|
|
|
return this.endpoint('put', endpoint)
|
2021-03-08 15:00:43 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Create a new PATCH route on the given endpoint. */
|
2022-01-19 19:24:59 +00:00
|
|
|
public static patch(endpoint: string): Route<ResponseObject> {
|
|
|
|
return this.endpoint('patch', endpoint)
|
2021-03-08 15:00:43 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Create a new DELETE route on the given endpoint. */
|
2022-01-19 19:24:59 +00:00
|
|
|
public static delete(endpoint: string): Route<ResponseObject> {
|
|
|
|
return this.endpoint('delete', endpoint)
|
2021-03-08 15:00:43 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Create a new route on all HTTP verbs, on the given endpoint. */
|
2022-01-19 19:24:59 +00:00
|
|
|
public static any(endpoint: string): Route<ResponseObject> {
|
|
|
|
return this.endpoint(['get', 'put', 'patch', 'post', 'delete'], endpoint)
|
2021-03-08 15:00:43 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Create a new route group with the given prefix. */
|
2021-06-03 03:36:25 +00:00
|
|
|
public static group(prefix: string, group: () => void | Promise<void>): RouteGroup {
|
2021-03-08 15:00:43 +00:00
|
|
|
const grp = <RouteGroup> Application.getApplication().make(RouteGroup, group, prefix)
|
|
|
|
this.registeredGroups.push(grp)
|
|
|
|
return grp
|
|
|
|
}
|
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
protected preflight: Collection<ResolvedRouteHandler> = new Collection<ResolvedRouteHandler>()
|
2021-03-25 13:50:13 +00:00
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
protected parameters: Collection<ParameterProvidingMiddleware<unknown>> = new Collection<ParameterProvidingMiddleware<unknown>>()
|
2021-03-25 13:50:13 +00:00
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
protected postflight: Collection<ResolvedRouteHandler> = new Collection<ResolvedRouteHandler>()
|
2021-03-25 13:50:13 +00:00
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
protected aliases: Collection<string> = new Collection<string>()
|
2021-03-09 15:42:19 +00:00
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
handler?: Constructable<(...x: THandlerParams) => TReturn>
|
2021-07-17 17:49:07 +00:00
|
|
|
|
2022-01-20 06:54:55 +00:00
|
|
|
protected displays: Collection<{stage: 'pre'|'post'|'handler', display: string}> = new Collection()
|
|
|
|
|
2021-03-08 15:00:43 +00:00
|
|
|
constructor(
|
|
|
|
protected method: HTTPMethod | HTTPMethod[],
|
2021-06-03 03:36:25 +00:00
|
|
|
protected route: string,
|
2022-01-19 19:24:59 +00:00
|
|
|
) {}
|
2021-03-08 15:00:43 +00:00
|
|
|
|
2021-07-17 17:49:07 +00:00
|
|
|
/**
|
|
|
|
* Set a programmatic name for this route.
|
|
|
|
* @param name
|
|
|
|
*/
|
|
|
|
public alias(name: string): this {
|
|
|
|
this.aliases.push(name)
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
2021-06-18 00:35:31 +00:00
|
|
|
/**
|
|
|
|
* Get the string-form of the route.
|
|
|
|
*/
|
|
|
|
public getRoute(): string {
|
|
|
|
return this.route
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-01-19 19:24:59 +00:00
|
|
|
* Get the string-form methods supported by the route.
|
2021-06-18 00:35:31 +00:00
|
|
|
*/
|
2022-01-19 19:24:59 +00:00
|
|
|
public getMethods(): HTTPMethod[] {
|
|
|
|
if ( !Array.isArray(this.method) ) {
|
|
|
|
return [this.method]
|
|
|
|
}
|
|
|
|
|
2021-06-18 00:35:31 +00:00
|
|
|
return this.method
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-01-19 19:24:59 +00:00
|
|
|
* Get preflight middleware for this route.
|
2021-06-18 00:35:31 +00:00
|
|
|
*/
|
2022-01-19 19:24:59 +00:00
|
|
|
public getPreflight(): Collection<ResolvedRouteHandler> {
|
|
|
|
return this.preflight.clone()
|
2021-06-18 00:35:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-01-19 19:24:59 +00:00
|
|
|
* Get postflight middleware for this route.
|
2021-06-18 00:35:31 +00:00
|
|
|
*/
|
2022-01-19 19:24:59 +00:00
|
|
|
public getPostflight(): Collection<ResolvedRouteHandler> {
|
|
|
|
return this.postflight.clone()
|
|
|
|
}
|
|
|
|
|
|
|
|
public getParameters(): Collection<ParameterProvidingMiddleware<unknown>> {
|
|
|
|
return this.parameters.clone()
|
2021-06-18 00:35:31 +00:00
|
|
|
}
|
|
|
|
|
2022-01-20 06:54:55 +00:00
|
|
|
public getDisplays(): Collection<{ stage: 'pre'|'handler'|'post', display: string }> {
|
|
|
|
return this.displays.clone()
|
|
|
|
}
|
|
|
|
|
|
|
|
public getHandlerDisplay(): Maybe<string> {
|
|
|
|
return this.displays.firstWhere('stage', '=', 'handler')?.display
|
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
|
|
|
* Returns true if this route matches the given HTTP verb and request path.
|
|
|
|
* @param method
|
|
|
|
* @param potential
|
|
|
|
*/
|
2021-03-08 15:00:43 +00:00
|
|
|
public match(method: HTTPMethod, potential: string): boolean {
|
2021-06-03 03:36:25 +00:00
|
|
|
if ( Array.isArray(this.method) && !this.method.includes(method) ) {
|
|
|
|
return false
|
|
|
|
} else if ( !Array.isArray(this.method) && this.method !== method ) {
|
|
|
|
return false
|
|
|
|
}
|
2021-03-08 15:00:43 +00:00
|
|
|
|
2021-06-03 03:36:25 +00:00
|
|
|
return Boolean(this.extract(potential))
|
2021-03-08 15:00:43 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
|
|
|
* Given a request path, try to extract this route's paramters from the path string.
|
|
|
|
*
|
|
|
|
* @example
|
|
|
|
* For route `/foo/:bar/baz` and input `/foo/bob/baz`, extracts:
|
|
|
|
*
|
|
|
|
* ```typescript
|
|
|
|
* {
|
|
|
|
* bar: 'bob'
|
|
|
|
* }
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @param potential
|
|
|
|
*/
|
2021-03-08 15:00:43 +00:00
|
|
|
public extract(potential: string): {[key: string]: string} | undefined {
|
|
|
|
const routeParts = (this.route.startsWith('/') ? this.route.substr(1) : this.route).split('/')
|
|
|
|
const potentialParts = (potential.startsWith('/') ? potential.substr(1) : potential).split('/')
|
|
|
|
|
|
|
|
const params: any = {}
|
|
|
|
let wildcardIdx = 0
|
|
|
|
|
|
|
|
for ( let i = 0; i < routeParts.length; i += 1 ) {
|
|
|
|
const part = routeParts[i]
|
|
|
|
|
|
|
|
if ( part === '**' ) {
|
|
|
|
params[wildcardIdx] = potentialParts.slice(i).join('/')
|
|
|
|
return params
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( (potentialParts.length - 1) < i ) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( part === '*' ) {
|
|
|
|
params[wildcardIdx] = potentialParts[i]
|
|
|
|
wildcardIdx += 1
|
|
|
|
} else if ( part.startsWith(':') ) {
|
|
|
|
params[part.substr(1)] = potentialParts[i]
|
|
|
|
} else if ( potentialParts[i] !== part ) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we got here, we didn't find a **
|
|
|
|
// So, if the lengths are different, fail
|
2021-06-03 03:36:25 +00:00
|
|
|
if ( routeParts.length !== potentialParts.length ) {
|
|
|
|
return
|
|
|
|
}
|
2021-03-08 15:00:43 +00:00
|
|
|
return params
|
|
|
|
}
|
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
public parameterMiddleware<T>(
|
|
|
|
handler: ParameterProvidingMiddleware<T>,
|
|
|
|
): Route<TReturn, PrefixTypeArray<T, THandlerParams>> {
|
|
|
|
const route = new Route<TReturn, PrefixTypeArray<T, THandlerParams>>(
|
|
|
|
this.method,
|
|
|
|
this.route,
|
|
|
|
)
|
2021-03-09 15:42:19 +00:00
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
route.copyFrom(this)
|
|
|
|
route.parameters.push(handler)
|
|
|
|
return route
|
2021-03-09 15:42:19 +00:00
|
|
|
}
|
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
private copyFrom(other: Route<TReturn, any>) {
|
|
|
|
this.preflight = other.preflight.clone()
|
|
|
|
this.postflight = other.postflight.clone()
|
|
|
|
this.aliases = other.aliases.clone()
|
2022-01-20 06:54:55 +00:00
|
|
|
this.displays = other.displays.clone()
|
2021-03-09 15:42:19 +00:00
|
|
|
}
|
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
public calls<TKey>(
|
|
|
|
key: TypedDependencyKey<TKey>,
|
|
|
|
selector: (x: TKey) => (...params: THandlerParams) => TReturn,
|
|
|
|
): HandledRoute<TReturn, THandlerParams> {
|
|
|
|
this.handler = constructable<TKey>(key)
|
|
|
|
.tap(inst => Function.prototype.bind.call(selector(inst), inst as any) as ((...params: THandlerParams) => TReturn))
|
2021-03-09 15:42:19 +00:00
|
|
|
|
2022-01-20 06:54:55 +00:00
|
|
|
this.displays.push({
|
|
|
|
stage: 'handler',
|
|
|
|
display: `${key.name}(${selector})`,
|
|
|
|
})
|
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
Route.registeredRoutes.push(this as unknown as Route<unknown, unknown[]>) // man this is stupid
|
|
|
|
return this as HandledRoute<TReturn, THandlerParams>
|
2021-03-09 15:42:19 +00:00
|
|
|
}
|
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
public handledBy(
|
|
|
|
handler: (...params: THandlerParams) => TReturn,
|
|
|
|
): HandledRoute<TReturn, THandlerParams> {
|
|
|
|
this.handler = Pipeline.id<Container>()
|
|
|
|
.tap(() => handler)
|
2021-03-09 15:42:19 +00:00
|
|
|
|
2022-01-20 06:54:55 +00:00
|
|
|
this.displays.push({
|
|
|
|
stage: 'handler',
|
|
|
|
display: `(closure)`,
|
|
|
|
})
|
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
Route.registeredRoutes.push(this as unknown as Route<unknown, unknown[]>)
|
|
|
|
return this as HandledRoute<TReturn, THandlerParams>
|
2021-03-09 15:42:19 +00:00
|
|
|
}
|
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
public pre(middleware: Instantiable<Middleware>): this {
|
2022-03-29 20:12:51 +00:00
|
|
|
this.preflight.prepend(request => request.make<Middleware>(middleware, request).apply())
|
2022-01-20 06:54:55 +00:00
|
|
|
this.displays.push({
|
|
|
|
stage: 'pre',
|
|
|
|
display: `${middleware.name}`,
|
|
|
|
})
|
|
|
|
|
2021-03-09 15:42:19 +00:00
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
public post(middleware: Instantiable<Middleware>): this {
|
|
|
|
this.postflight.push(request => request.make<Middleware>(middleware, request).apply())
|
2022-01-20 06:54:55 +00:00
|
|
|
this.displays.push({
|
|
|
|
stage: 'pre',
|
|
|
|
display: `${middleware.name}`,
|
|
|
|
})
|
|
|
|
|
2022-01-17 21:57:40 +00:00
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
2022-01-20 06:54:55 +00:00
|
|
|
public input<T>(validator: ValidatorFactory<T>): Route<TReturn, PrefixTypeArray<Valid<T>, THandlerParams>> {
|
2022-01-19 19:24:59 +00:00
|
|
|
if ( !(validator instanceof Validator) ) {
|
|
|
|
validator = validator()
|
2021-06-03 03:36:25 +00:00
|
|
|
}
|
2021-03-09 15:42:19 +00:00
|
|
|
|
2022-01-20 06:54:55 +00:00
|
|
|
this.displays.push({
|
|
|
|
stage: 'pre',
|
|
|
|
display: `input(${validator.constructor.name})`,
|
|
|
|
})
|
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
return this.parameterMiddleware(validateMiddleware(validator))
|
2021-03-09 15:42:19 +00:00
|
|
|
}
|
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
public passingRequest(): Route<TReturn, PrefixTypeArray<Request, THandlerParams>> {
|
|
|
|
return this.parameterMiddleware(request => right(request))
|
2021-03-09 15:42:19 +00:00
|
|
|
}
|
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
hasAlias(name: string): boolean {
|
|
|
|
return this.aliases.includes(name)
|
2021-03-08 17:08:56 +00:00
|
|
|
}
|
|
|
|
|
2022-02-23 21:15:02 +00:00
|
|
|
getAlias(): Maybe<string> {
|
|
|
|
return this.aliases.first()
|
|
|
|
}
|
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
isHandled(): this is HandledRoute<TReturn, THandlerParams> {
|
|
|
|
return Boolean(this.handler)
|
2021-03-08 15:00:43 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Cast the route to an intelligible string. */
|
2021-06-03 03:36:25 +00:00
|
|
|
toString(): string {
|
2021-03-08 15:00:43 +00:00
|
|
|
const method = Array.isArray(this.method) ? this.method : [this.method]
|
|
|
|
return `${method.join('|')} -> ${this.route}`
|
|
|
|
}
|
2022-01-19 19:24:59 +00:00
|
|
|
|
2022-01-20 06:54:55 +00:00
|
|
|
/**
|
|
|
|
* Return a new Pipe of this collection.
|
|
|
|
*/
|
|
|
|
pipeTo<TOut>(pipeline: Pipeline<this, TOut>): TOut {
|
|
|
|
return pipeline.apply(this)
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Build and apply a pipeline. */
|
|
|
|
pipe<TOut>(builder: (pipeline: Pipeline<this, this>) => Pipeline<this, TOut>): TOut {
|
|
|
|
return builder(Pipeline.id()).apply(this)
|
|
|
|
}
|
|
|
|
|
2022-01-19 19:24:59 +00:00
|
|
|
/** Prefix the route's path with the given prefix, normalizing `/` characters. */
|
|
|
|
private prepend(prefix: string): this {
|
|
|
|
if ( !prefix.endsWith('/') ) {
|
|
|
|
prefix = `${prefix}/`
|
|
|
|
}
|
|
|
|
if ( this.route.startsWith('/') ) {
|
|
|
|
this.route = this.route.substring(1)
|
|
|
|
}
|
|
|
|
this.route = `${prefix}${this.route}`
|
|
|
|
return this
|
|
|
|
}
|
2021-03-08 15:00:43 +00:00
|
|
|
}
|