refactor(types): unify handler and middleware definitions
- Consolidates `Handler` and `Middleware` types under `Types` module - Replaces `IHandler` and `IMiddleware` interfaces with typed functions - Simplifies imports and improves code organization - Enhances debugging with named handlers and middlewares
This commit is contained in:
@@ -1,20 +1,20 @@
|
||||
import {
|
||||
IContext,
|
||||
IHandler,
|
||||
IHttpKernel,
|
||||
IHttpKernelConfig,
|
||||
IInternalRoute,
|
||||
IMiddleware,
|
||||
IRouteBuilder,
|
||||
IRouteDefinition,
|
||||
isHandler,
|
||||
isMiddleware,
|
||||
} from './Interfaces/mod.ts';
|
||||
import {
|
||||
DeepPartial,
|
||||
Handler,
|
||||
HTTP_404_NOT_FOUND,
|
||||
HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
HttpStatusTextMap,
|
||||
isHandler,
|
||||
isMiddleware,
|
||||
Middleware,
|
||||
} from './Types/mod.ts';
|
||||
import { RouteBuilder } from './RouteBuilder.ts';
|
||||
import { createEmptyContext, normalizeError } from './Utils/mod.ts';
|
||||
@@ -151,8 +151,8 @@ export class HttpKernel<TContext extends IContext = IContext>
|
||||
*/
|
||||
private async executePipeline(
|
||||
ctx: TContext,
|
||||
middleware: IMiddleware<TContext>[],
|
||||
handler: IHandler<TContext>,
|
||||
middleware: Middleware<TContext>[],
|
||||
handler: Handler<TContext>,
|
||||
): Promise<Response> {
|
||||
const handleInternalError = (ctx: TContext, err?: unknown) =>
|
||||
this.cfg.httpErrorHandlers[HTTP_500_INTERNAL_SERVER_ERROR](
|
||||
|
@@ -1,6 +1,4 @@
|
||||
import { HttpMethod } from '../Types/mod.ts';
|
||||
import { IHandler } from './IHandler.ts';
|
||||
import { IMiddleware } from './IMiddleware.ts';
|
||||
import { Handler, HttpMethod, Middleware } from '../Types/mod.ts';
|
||||
import { IContext, IRouteMatcher } from './mod.ts';
|
||||
|
||||
/**
|
||||
@@ -32,10 +30,10 @@ export interface IInternalRoute<TContext extends IContext = IContext> {
|
||||
/**
|
||||
* An ordered list of middleware functions to be executed before the handler.
|
||||
*/
|
||||
middlewares: IMiddleware<TContext>[];
|
||||
middlewares: Middleware<TContext>[];
|
||||
|
||||
/**
|
||||
* The final handler that generates the HTTP response after all middleware has run.
|
||||
*/
|
||||
handler: IHandler<TContext>;
|
||||
handler: Handler<TContext>;
|
||||
}
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import { IHandler } from './IHandler.ts';
|
||||
import { Handler, Middleware } from '../Types/mod.ts';
|
||||
import { IInternalRoute } from './IInternalRoute.ts';
|
||||
import { IMiddleware } from './IMiddleware.ts';
|
||||
import { IRouteDefinition } from './IRouteDefinition.ts';
|
||||
import { IContext } from './mod.ts';
|
||||
|
||||
@@ -8,7 +7,7 @@ export interface IRouteBuilderFactory<TContext extends IContext = IContext> {
|
||||
new (
|
||||
registerRoute: (route: IInternalRoute<TContext>) => void,
|
||||
def: IRouteDefinition,
|
||||
mws?: IMiddleware<TContext>[],
|
||||
mws?: Middleware<TContext>[],
|
||||
): IRouteBuilder<TContext>;
|
||||
}
|
||||
|
||||
@@ -25,7 +24,7 @@ export interface IRouteBuilder<TContext extends IContext = IContext> {
|
||||
* @returns The route builder for further chaining.
|
||||
*/
|
||||
middleware(
|
||||
mw: IMiddleware<TContext>,
|
||||
mw: Middleware<TContext>,
|
||||
): IRouteBuilder<TContext>;
|
||||
|
||||
/**
|
||||
@@ -35,6 +34,6 @@ export interface IRouteBuilder<TContext extends IContext = IContext> {
|
||||
* @param handler - The function to execute when this route is matched.
|
||||
*/
|
||||
handle(
|
||||
handler: IHandler<TContext>,
|
||||
handler: Handler<TContext>,
|
||||
): void;
|
||||
}
|
||||
|
@@ -1,14 +1,10 @@
|
||||
// deno-coverage-ignore-file
|
||||
|
||||
export type { IContext } from './IContext.ts';
|
||||
export { isHandler } from './IHandler.ts';
|
||||
export type { IHandler } from './IHandler.ts';
|
||||
export type { IHttpErrorHandlers } from './IHttpErrorHandlers.ts';
|
||||
export type { IHttpKernel } from './IHttpKernel.ts';
|
||||
export type { IHttpKernelConfig } from './IHttpKernelConfig.ts';
|
||||
export type { IInternalRoute } from './IInternalRoute.ts';
|
||||
export { isMiddleware } from './IMiddleware.ts';
|
||||
export type { IMiddleware } from './IMiddleware.ts';
|
||||
export type { IRouteBuilder, IRouteBuilderFactory } from './IRouteBuilder.ts';
|
||||
export {
|
||||
isDynamicRouteDefinition,
|
||||
|
@@ -1,12 +1,6 @@
|
||||
import { IRouteMatcherFactory } from './Interfaces/IRouteMatcher.ts';
|
||||
import {
|
||||
IContext,
|
||||
IHandler,
|
||||
IMiddleware,
|
||||
IRouteBuilder,
|
||||
IRouteDefinition,
|
||||
} from './Interfaces/mod.ts';
|
||||
import { RegisterRoute } from './Types/mod.ts';
|
||||
import { IContext, IRouteBuilder, IRouteDefinition } from './Interfaces/mod.ts';
|
||||
import { Handler, Middleware, RegisterRoute } from './Types/mod.ts';
|
||||
import { createRouteMatcher } from './Utils/createRouteMatcher.ts';
|
||||
|
||||
/**
|
||||
@@ -27,7 +21,7 @@ export class RouteBuilder<TContext extends IContext = IContext>
|
||||
constructor(
|
||||
private readonly registerRoute: RegisterRoute<TContext>,
|
||||
private readonly def: IRouteDefinition,
|
||||
private readonly mws: IMiddleware<TContext>[] = [],
|
||||
private readonly mws: Middleware<TContext>[] = [],
|
||||
private readonly matcherFactory: IRouteMatcherFactory =
|
||||
createRouteMatcher,
|
||||
) {}
|
||||
@@ -42,7 +36,7 @@ export class RouteBuilder<TContext extends IContext = IContext>
|
||||
* @returns A new `RouteBuilder` instance for continued chaining.
|
||||
*/
|
||||
middleware(
|
||||
mw: IMiddleware<TContext>,
|
||||
mw: Middleware<TContext>,
|
||||
): IRouteBuilder<TContext> {
|
||||
return new RouteBuilder<TContext>(
|
||||
this.registerRoute,
|
||||
@@ -60,7 +54,7 @@ export class RouteBuilder<TContext extends IContext = IContext>
|
||||
* @param handler - The final request handler for this route.
|
||||
*/
|
||||
handle(
|
||||
handler: IHandler<TContext>,
|
||||
handler: Handler<TContext>,
|
||||
): void {
|
||||
const matcher = this.matcherFactory(this.def);
|
||||
this.registerRoute({
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { IContext } from './IContext.ts';
|
||||
import { IContext } from '../Interfaces/mod.ts';
|
||||
|
||||
/**
|
||||
* Represents a final request handler responsible for producing an HTTP response.
|
||||
@@ -11,16 +11,23 @@ import { IContext } from './IContext.ts';
|
||||
*
|
||||
* @template TContext The specific context type for this handler, including typed `state`, `params`, and `query`.
|
||||
*/
|
||||
export interface IHandler<TContext extends IContext = IContext> {
|
||||
type Handler<TContext extends IContext = IContext> = (
|
||||
ctx: TContext,
|
||||
) => Promise<Response>;
|
||||
|
||||
/**
|
||||
* Handles the request and generates a response.
|
||||
* Represents a handler function with an associated name.
|
||||
*
|
||||
* @param ctx - The complete request context, including request metadata, route and query parameters,
|
||||
* and mutable state populated during the middleware phase.
|
||||
* @returns A `Promise` resolving to an HTTP `Response` to be sent to the client.
|
||||
* This is useful for debugging, logging, or when you need to reference
|
||||
* the handler by name in your application.
|
||||
*
|
||||
* @template TContext The specific context type for this handler, including typed `state`, `params`, and `query`.
|
||||
*/
|
||||
(ctx: TContext): Promise<Response>;
|
||||
}
|
||||
type NamedHandler<TContext extends IContext = IContext> =
|
||||
& Handler<TContext>
|
||||
& { name?: string };
|
||||
|
||||
export type { NamedHandler as Handler };
|
||||
|
||||
/**
|
||||
* Type guard to determine whether a given value is a valid `IHandler` function.
|
||||
@@ -42,7 +49,7 @@ export interface IHandler<TContext extends IContext = IContext> {
|
||||
*/
|
||||
export function isHandler<TContext extends IContext = IContext>(
|
||||
value: unknown,
|
||||
): value is IHandler<TContext> {
|
||||
): value is Handler<TContext> {
|
||||
return (
|
||||
typeof value === 'function' &&
|
||||
value.length === 1 // ctx
|
@@ -1,4 +1,4 @@
|
||||
import { IContext } from './IContext.ts';
|
||||
import { IContext } from '../Interfaces/IContext.ts';
|
||||
|
||||
/**
|
||||
* Represents a middleware function in the HTTP request pipeline.
|
||||
@@ -13,16 +13,24 @@ import { IContext } from './IContext.ts';
|
||||
*
|
||||
* @template TContext The specific context type for this middleware, including state, params, and query information.
|
||||
*/
|
||||
export interface IMiddleware<TContext extends IContext = IContext> {
|
||||
type Middleware<TContext extends IContext = IContext> = (
|
||||
ctx: TContext,
|
||||
next: () => Promise<Response>,
|
||||
) => Promise<Response>;
|
||||
|
||||
/**
|
||||
* Handles the request processing at this middleware stage.
|
||||
* Represents a middleware function with an associated name.
|
||||
*
|
||||
* @param ctx - The full request context, containing request, params, query, and typed state.
|
||||
* @param next - A continuation function that executes the next middleware or handler in the pipeline.
|
||||
* @returns A `Promise` resolving to an HTTP `Response`, either from this middleware or downstream.
|
||||
* This is useful for debugging, logging, or when you need to reference
|
||||
* the middleware by name in your application.
|
||||
*
|
||||
* @template TContext The specific context type for this middleware, including state, params, and query information.
|
||||
*/
|
||||
(ctx: TContext, next: () => Promise<Response>): Promise<Response>;
|
||||
}
|
||||
type NamedMiddleware<TContext extends IContext = IContext> =
|
||||
& Middleware<TContext>
|
||||
& { name?: string };
|
||||
|
||||
export type { NamedMiddleware as Middleware };
|
||||
|
||||
/**
|
||||
* Type guard to verify whether a given value is a valid `IMiddleware` function.
|
||||
@@ -35,7 +43,7 @@ export interface IMiddleware<TContext extends IContext = IContext> {
|
||||
*/
|
||||
export function isMiddleware<TContext extends IContext = IContext>(
|
||||
value: unknown,
|
||||
): value is IMiddleware<TContext> {
|
||||
): value is Middleware<TContext> {
|
||||
return (
|
||||
typeof value === 'function' &&
|
||||
value.length === 2 // ctx, next
|
@@ -1,6 +1,8 @@
|
||||
// deno-coverage-ignore-file
|
||||
|
||||
export type { DeepPartial } from './DeepPartial.ts';
|
||||
export { isHandler } from './Handler.ts';
|
||||
export type { Handler } from './Handler.ts';
|
||||
export type { HttpErrorHandler } from './HttpErrorHandler.ts';
|
||||
export { isHttpMethod, validHttpMethods } from './HttpMethod.ts';
|
||||
export type { HttpMethod } from './HttpMethod.ts';
|
||||
@@ -34,6 +36,8 @@ export {
|
||||
validHttpStatusCodes,
|
||||
} from './HttpStatusCode.ts';
|
||||
export type { HttpStatusCode } from './HttpStatusCode.ts';
|
||||
export { isMiddleware } from './Middleware.ts';
|
||||
export type { Middleware } from './Middleware.ts';
|
||||
export type { Params } from './Params.ts';
|
||||
export type { Query } from './Query.ts';
|
||||
export type { RegisterRoute } from './RegisterRoute.ts';
|
||||
|
@@ -35,7 +35,9 @@ export function createRouteMatcher(
|
||||
// 3b. Extract route params
|
||||
const params: Params = {};
|
||||
for (const [key, value] of Object.entries(result.pathname.groups)) {
|
||||
params[key] = value ?? ''; // null → empty string
|
||||
if (value) {
|
||||
params[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// 3c. Extract query parameters – keep duplicates as arrays
|
||||
|
@@ -4,17 +4,14 @@ import {
|
||||
assertNotEquals,
|
||||
assertThrows,
|
||||
} from 'https://deno.land/std@0.204.0/assert/mod.ts';
|
||||
import {
|
||||
IHandler,
|
||||
IInternalRoute,
|
||||
IMiddleware,
|
||||
IRouteDefinition,
|
||||
} from '../Interfaces/mod.ts';
|
||||
import { IInternalRoute, IRouteDefinition } from '../Interfaces/mod.ts';
|
||||
import { RouteBuilder } from '../mod.ts';
|
||||
import { Handler, Middleware } from '../Types/mod.ts';
|
||||
|
||||
// Dummy objects
|
||||
const dummyHandler: IHandler = async () => new Response('ok');
|
||||
const dummyMiddleware: IMiddleware = async (_, next) => await next();
|
||||
// deno-lint-ignore require-await
|
||||
const dummyHandler: Handler = async () => new Response('ok');
|
||||
const dummyMiddleware: Middleware = async (_, next) => await next();
|
||||
const dummyDef: IRouteDefinition = { method: 'GET', path: '/hello' };
|
||||
const dummyMatcher = () => ({ params: {} });
|
||||
|
||||
@@ -39,8 +36,8 @@ Deno.test('middleware: middleware is chained immutably', () => {
|
||||
});
|
||||
|
||||
Deno.test('middleware: preserves order of middleware', () => {
|
||||
const mw1: IMiddleware = async (_, next) => await next();
|
||||
const mw2: IMiddleware = async (_, next) => await next();
|
||||
const mw1: Middleware = async (_, next) => await next();
|
||||
const mw2: Middleware = async (_, next) => await next();
|
||||
|
||||
let result: IInternalRoute | null = null as IInternalRoute | null;
|
||||
|
||||
|
Reference in New Issue
Block a user