UNPKG

@akala/core

Version:
162 lines (151 loc) 7.45 kB
import { type SpecialNextParam, convertToMiddleware } from '../middlewares/shared.js'; import { MiddlewareCompositeAsync } from '../middlewares/composite-async.js'; import { MiddlewareRouteAsync } from './route-async.js'; import { each } from '../each.js'; import type { RouterOptions, Routes } from './router.js'; import type { Routable, RouteBuilderArguments } from './route.js'; import type { MiddlewareAsync } from '../middlewares/shared.js' import type { UriTemplate } from '../uri-template/index.js'; export type RouteBuilderAsync<T extends [Routable, ...unknown[]], TSpecialNextParam extends SpecialNextParam = SpecialNextParam> = (...args: RouteBuilderArguments) => MiddlewareRouteAsync<T, TSpecialNextParam>; /** * Dynamically applies asynchronous routes to a middleware composite. * * This function recursively processes route definitions and attaches them to the parent middleware chain. * * @template T - The context type containing the routable request and parameters. * @template TSpecialNextParam - The type for special next parameters (defaults to {@link SpecialNextParam}) * @param {Routes<T, Promise<unknown>, TSpecialNextParam>} routes - Route definitions mapping URI templates to handler functions or nested route objects * @param {MiddlewareCompositeAsync<T, TSpecialNextParam> & { route: RouteBuilderAsync<T, TSpecialNextParam> }} [parent] - Parent middleware container to attach routes to * @returns {MiddlewareCompositeAsync<T, TSpecialNextParam>} Configured middleware composite with attached routes */ export function useRoutesAsync<T extends [Routable, ...unknown[]], TSpecialNextParam extends SpecialNextParam = SpecialNextParam>( routes: Routes<T, Promise<unknown>, TSpecialNextParam>, parent?: MiddlewareCompositeAsync<T, TSpecialNextParam> & { route: RouteBuilderAsync<T, TSpecialNextParam> } ): MiddlewareCompositeAsync<T, TSpecialNextParam> { if (!parent) parent = Object.assign(new MiddlewareCompositeAsync<T, TSpecialNextParam>('byroutes'), { route(...args: RouteBuilderArguments) { return new MiddlewareRouteAsync<T, TSpecialNextParam>(...args) } }); each(routes, (route: ((...args: T) => Promise<unknown>) | Routes<T, Promise<unknown>, TSpecialNextParam>, match) => { if (typeof match == 'number') return; const routed = new MiddlewareRouteAsync<T, TSpecialNextParam>(match as string); if (typeof (route) == 'object') { useRoutesAsync(route, routed); } else routed.useMiddleware(convertToMiddleware<T, TSpecialNextParam>(route)); parent.useMiddleware(routed); }); return parent; } /** * Base class for defining asynchronous routing middleware chains. * * Extends the middleware composite to provide route configuration capabilities for asynchronous handlers. * * @template T - The context type containing the routable request and parameters * @template TSpecialNextParam - The type for special next parameters (defaults to {@link SpecialNextParam}) * @extends {MiddlewareCompositeAsync<T, TSpecialNextParam>} * @implements {MiddlewareAsync<T, TSpecialNextParam>} */ export class RouterAsync< T extends [{ path: string; params?: Record<string, unknown> }, ...unknown[]], TSpecialNextParam extends SpecialNextParam = SpecialNextParam > extends MiddlewareCompositeAsync<T, TSpecialNextParam> implements MiddlewareAsync<T, TSpecialNextParam> { /** * Creates a new RouterAsync instance with optional configuration. * * @param {RouterOptions} [options] - Configuration options including middleware priority and name */ constructor(options?: RouterOptions) { super(options?.name); } /** * Creates a new route configuration chain for defining route handlers. * * @function route * @param {...RouteBuilderArguments} args - Route configuration parameters (path template, HTTP method, etc.) * @returns {MiddlewareRouteAsync<T, TSpecialNextParam>} Newly created route configuration instance */ public route(...args: RouteBuilderArguments): MiddlewareRouteAsync<T, TSpecialNextParam> { return new MiddlewareRouteAsync<T, TSpecialNextParam>(...args); } /** * Attaches a collection of route definitions to the router. * * @param {Routes<T, Promise<unknown>, TSpecialNextParam>} routes - Route definitions to be applied * @returns {this} Current router instance for method chaining */ public useRoutes(routes: Routes<T, Promise<unknown>, TSpecialNextParam>): this { useRoutesAsync(routes, this); return this; } /** * Adds middleware to the router's chain either globally or for a specific route. * * @function useMiddleware * @param {string | UriTemplate | MiddlewareAsync<T, TSpecialNextParam>} routeOrMiddleware - * Route definition (string/URI template) or middleware function * @param {...MiddlewareAsync<T, TSpecialNextParam>} middlewares - Additional middleware functions * @returns {this} Current router instance for method chaining */ public useMiddleware(...middlewares: MiddlewareAsync<T, TSpecialNextParam>[]): this; public useMiddleware( routeOrMiddleware: string | UriTemplate | MiddlewareAsync<T, TSpecialNextParam>, ...middlewares: MiddlewareAsync<T, TSpecialNextParam>[] ): this; public useMiddleware( routeOrMiddleware: string | UriTemplate | MiddlewareAsync<T, TSpecialNextParam>, ...middlewares: MiddlewareAsync<T, TSpecialNextParam>[] ): this { if (typeof routeOrMiddleware === 'string' || Array.isArray(routeOrMiddleware)) { const routed = new MiddlewareRouteAsync<T, TSpecialNextParam>(routeOrMiddleware); routed.useMiddleware(...middlewares); super.useMiddleware(routed); } else super.useMiddleware(routeOrMiddleware, ...middlewares); return this; } /** * Registers route handlers or middleware functions. * * @function use * @param {(string | UriTemplate | ((...args: T) => Promise<unknown>))} routeOrHandler - * Route definition (string/URI template) or handler function * @param {...((...args: T) => Promise<unknown>)} handlers - Additional handler functions * @returns {this} Current router instance for method chaining */ public use(routeOrHandler: string | UriTemplate, ...handlers: ((...args: T) => Promise<unknown>)[]): this; public use(...handlers: ((...args: T) => Promise<unknown>)[]): this; public use(routeOrHandler: string | UriTemplate | ((...args: T) => Promise<unknown>), ...handlers: ((...args: T) => Promise<unknown>)[]): this { if (typeof routeOrHandler === 'string' || Array.isArray(routeOrHandler)) { const routed = new MiddlewareRouteAsync<T, TSpecialNextParam>(routeOrHandler); routed.use(...handlers); super.useMiddleware(routed); return this; } else return super.use(routeOrHandler, ...handlers); } } // // create Router#VERB functions // methods.concat('all').forEach(function (method) // { // Router.prototype[method] = function (path) // { // var route = this.route(path) // route[method].apply(route, slice.call(arguments, 1)) // return this // } // })