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 {
|
import {
|
||||||
IContext,
|
IContext,
|
||||||
IHandler,
|
|
||||||
IHttpKernel,
|
IHttpKernel,
|
||||||
IHttpKernelConfig,
|
IHttpKernelConfig,
|
||||||
IInternalRoute,
|
IInternalRoute,
|
||||||
IMiddleware,
|
|
||||||
IRouteBuilder,
|
IRouteBuilder,
|
||||||
IRouteDefinition,
|
IRouteDefinition,
|
||||||
isHandler,
|
|
||||||
isMiddleware,
|
|
||||||
} from './Interfaces/mod.ts';
|
} from './Interfaces/mod.ts';
|
||||||
import {
|
import {
|
||||||
DeepPartial,
|
DeepPartial,
|
||||||
|
Handler,
|
||||||
HTTP_404_NOT_FOUND,
|
HTTP_404_NOT_FOUND,
|
||||||
HTTP_500_INTERNAL_SERVER_ERROR,
|
HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
HttpStatusTextMap,
|
HttpStatusTextMap,
|
||||||
|
isHandler,
|
||||||
|
isMiddleware,
|
||||||
|
Middleware,
|
||||||
} from './Types/mod.ts';
|
} from './Types/mod.ts';
|
||||||
import { RouteBuilder } from './RouteBuilder.ts';
|
import { RouteBuilder } from './RouteBuilder.ts';
|
||||||
import { createEmptyContext, normalizeError } from './Utils/mod.ts';
|
import { createEmptyContext, normalizeError } from './Utils/mod.ts';
|
||||||
@@ -151,8 +151,8 @@ export class HttpKernel<TContext extends IContext = IContext>
|
|||||||
*/
|
*/
|
||||||
private async executePipeline(
|
private async executePipeline(
|
||||||
ctx: TContext,
|
ctx: TContext,
|
||||||
middleware: IMiddleware<TContext>[],
|
middleware: Middleware<TContext>[],
|
||||||
handler: IHandler<TContext>,
|
handler: Handler<TContext>,
|
||||||
): Promise<Response> {
|
): Promise<Response> {
|
||||||
const handleInternalError = (ctx: TContext, err?: unknown) =>
|
const handleInternalError = (ctx: TContext, err?: unknown) =>
|
||||||
this.cfg.httpErrorHandlers[HTTP_500_INTERNAL_SERVER_ERROR](
|
this.cfg.httpErrorHandlers[HTTP_500_INTERNAL_SERVER_ERROR](
|
||||||
|
@@ -1,6 +1,4 @@
|
|||||||
import { HttpMethod } from '../Types/mod.ts';
|
import { Handler, HttpMethod, Middleware } from '../Types/mod.ts';
|
||||||
import { IHandler } from './IHandler.ts';
|
|
||||||
import { IMiddleware } from './IMiddleware.ts';
|
|
||||||
import { IContext, IRouteMatcher } from './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.
|
* 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.
|
* 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 { IInternalRoute } from './IInternalRoute.ts';
|
||||||
import { IMiddleware } from './IMiddleware.ts';
|
|
||||||
import { IRouteDefinition } from './IRouteDefinition.ts';
|
import { IRouteDefinition } from './IRouteDefinition.ts';
|
||||||
import { IContext } from './mod.ts';
|
import { IContext } from './mod.ts';
|
||||||
|
|
||||||
@@ -8,7 +7,7 @@ export interface IRouteBuilderFactory<TContext extends IContext = IContext> {
|
|||||||
new (
|
new (
|
||||||
registerRoute: (route: IInternalRoute<TContext>) => void,
|
registerRoute: (route: IInternalRoute<TContext>) => void,
|
||||||
def: IRouteDefinition,
|
def: IRouteDefinition,
|
||||||
mws?: IMiddleware<TContext>[],
|
mws?: Middleware<TContext>[],
|
||||||
): IRouteBuilder<TContext>;
|
): IRouteBuilder<TContext>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,7 +24,7 @@ export interface IRouteBuilder<TContext extends IContext = IContext> {
|
|||||||
* @returns The route builder for further chaining.
|
* @returns The route builder for further chaining.
|
||||||
*/
|
*/
|
||||||
middleware(
|
middleware(
|
||||||
mw: IMiddleware<TContext>,
|
mw: Middleware<TContext>,
|
||||||
): IRouteBuilder<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.
|
* @param handler - The function to execute when this route is matched.
|
||||||
*/
|
*/
|
||||||
handle(
|
handle(
|
||||||
handler: IHandler<TContext>,
|
handler: Handler<TContext>,
|
||||||
): void;
|
): void;
|
||||||
}
|
}
|
||||||
|
@@ -1,14 +1,10 @@
|
|||||||
// deno-coverage-ignore-file
|
// deno-coverage-ignore-file
|
||||||
|
|
||||||
export type { IContext } from './IContext.ts';
|
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 { IHttpErrorHandlers } from './IHttpErrorHandlers.ts';
|
||||||
export type { IHttpKernel } from './IHttpKernel.ts';
|
export type { IHttpKernel } from './IHttpKernel.ts';
|
||||||
export type { IHttpKernelConfig } from './IHttpKernelConfig.ts';
|
export type { IHttpKernelConfig } from './IHttpKernelConfig.ts';
|
||||||
export type { IInternalRoute } from './IInternalRoute.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 type { IRouteBuilder, IRouteBuilderFactory } from './IRouteBuilder.ts';
|
||||||
export {
|
export {
|
||||||
isDynamicRouteDefinition,
|
isDynamicRouteDefinition,
|
||||||
|
@@ -1,12 +1,6 @@
|
|||||||
import { IRouteMatcherFactory } from './Interfaces/IRouteMatcher.ts';
|
import { IRouteMatcherFactory } from './Interfaces/IRouteMatcher.ts';
|
||||||
import {
|
import { IContext, IRouteBuilder, IRouteDefinition } from './Interfaces/mod.ts';
|
||||||
IContext,
|
import { Handler, Middleware, RegisterRoute } from './Types/mod.ts';
|
||||||
IHandler,
|
|
||||||
IMiddleware,
|
|
||||||
IRouteBuilder,
|
|
||||||
IRouteDefinition,
|
|
||||||
} from './Interfaces/mod.ts';
|
|
||||||
import { RegisterRoute } from './Types/mod.ts';
|
|
||||||
import { createRouteMatcher } from './Utils/createRouteMatcher.ts';
|
import { createRouteMatcher } from './Utils/createRouteMatcher.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -27,7 +21,7 @@ export class RouteBuilder<TContext extends IContext = IContext>
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly registerRoute: RegisterRoute<TContext>,
|
private readonly registerRoute: RegisterRoute<TContext>,
|
||||||
private readonly def: IRouteDefinition,
|
private readonly def: IRouteDefinition,
|
||||||
private readonly mws: IMiddleware<TContext>[] = [],
|
private readonly mws: Middleware<TContext>[] = [],
|
||||||
private readonly matcherFactory: IRouteMatcherFactory =
|
private readonly matcherFactory: IRouteMatcherFactory =
|
||||||
createRouteMatcher,
|
createRouteMatcher,
|
||||||
) {}
|
) {}
|
||||||
@@ -42,7 +36,7 @@ export class RouteBuilder<TContext extends IContext = IContext>
|
|||||||
* @returns A new `RouteBuilder` instance for continued chaining.
|
* @returns A new `RouteBuilder` instance for continued chaining.
|
||||||
*/
|
*/
|
||||||
middleware(
|
middleware(
|
||||||
mw: IMiddleware<TContext>,
|
mw: Middleware<TContext>,
|
||||||
): IRouteBuilder<TContext> {
|
): IRouteBuilder<TContext> {
|
||||||
return new RouteBuilder<TContext>(
|
return new RouteBuilder<TContext>(
|
||||||
this.registerRoute,
|
this.registerRoute,
|
||||||
@@ -60,7 +54,7 @@ export class RouteBuilder<TContext extends IContext = IContext>
|
|||||||
* @param handler - The final request handler for this route.
|
* @param handler - The final request handler for this route.
|
||||||
*/
|
*/
|
||||||
handle(
|
handle(
|
||||||
handler: IHandler<TContext>,
|
handler: Handler<TContext>,
|
||||||
): void {
|
): void {
|
||||||
const matcher = this.matcherFactory(this.def);
|
const matcher = this.matcherFactory(this.def);
|
||||||
this.registerRoute({
|
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.
|
* 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`.
|
* @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,
|
||||||
* Handles the request and generates a response.
|
) => Promise<Response>;
|
||||||
*
|
|
||||||
* @param ctx - The complete request context, including request metadata, route and query parameters,
|
/**
|
||||||
* and mutable state populated during the middleware phase.
|
* Represents a handler function with an associated name.
|
||||||
* @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
|
||||||
(ctx: TContext): Promise<Response>;
|
* the handler by name in your application.
|
||||||
}
|
*
|
||||||
|
* @template TContext The specific context type for this handler, including typed `state`, `params`, and `query`.
|
||||||
|
*/
|
||||||
|
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.
|
* 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>(
|
export function isHandler<TContext extends IContext = IContext>(
|
||||||
value: unknown,
|
value: unknown,
|
||||||
): value is IHandler<TContext> {
|
): value is Handler<TContext> {
|
||||||
return (
|
return (
|
||||||
typeof value === 'function' &&
|
typeof value === 'function' &&
|
||||||
value.length === 1 // ctx
|
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.
|
* 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.
|
* @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,
|
||||||
* Handles the request processing at this middleware stage.
|
next: () => Promise<Response>,
|
||||||
*
|
) => Promise<Response>;
|
||||||
* @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.
|
* Represents a middleware function with an associated name.
|
||||||
*/
|
*
|
||||||
(ctx: TContext, next: () => Promise<Response>): Promise<Response>;
|
* 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.
|
||||||
|
*/
|
||||||
|
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.
|
* 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>(
|
export function isMiddleware<TContext extends IContext = IContext>(
|
||||||
value: unknown,
|
value: unknown,
|
||||||
): value is IMiddleware<TContext> {
|
): value is Middleware<TContext> {
|
||||||
return (
|
return (
|
||||||
typeof value === 'function' &&
|
typeof value === 'function' &&
|
||||||
value.length === 2 // ctx, next
|
value.length === 2 // ctx, next
|
@@ -1,6 +1,8 @@
|
|||||||
// deno-coverage-ignore-file
|
// deno-coverage-ignore-file
|
||||||
|
|
||||||
export type { DeepPartial } from './DeepPartial.ts';
|
export type { DeepPartial } from './DeepPartial.ts';
|
||||||
|
export { isHandler } from './Handler.ts';
|
||||||
|
export type { Handler } from './Handler.ts';
|
||||||
export type { HttpErrorHandler } from './HttpErrorHandler.ts';
|
export type { HttpErrorHandler } from './HttpErrorHandler.ts';
|
||||||
export { isHttpMethod, validHttpMethods } from './HttpMethod.ts';
|
export { isHttpMethod, validHttpMethods } from './HttpMethod.ts';
|
||||||
export type { HttpMethod } from './HttpMethod.ts';
|
export type { HttpMethod } from './HttpMethod.ts';
|
||||||
@@ -34,6 +36,8 @@ export {
|
|||||||
validHttpStatusCodes,
|
validHttpStatusCodes,
|
||||||
} from './HttpStatusCode.ts';
|
} from './HttpStatusCode.ts';
|
||||||
export type { HttpStatusCode } 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 { Params } from './Params.ts';
|
||||||
export type { Query } from './Query.ts';
|
export type { Query } from './Query.ts';
|
||||||
export type { RegisterRoute } from './RegisterRoute.ts';
|
export type { RegisterRoute } from './RegisterRoute.ts';
|
||||||
|
@@ -35,7 +35,9 @@ export function createRouteMatcher(
|
|||||||
// 3b. Extract route params
|
// 3b. Extract route params
|
||||||
const params: Params = {};
|
const params: Params = {};
|
||||||
for (const [key, value] of Object.entries(result.pathname.groups)) {
|
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
|
// 3c. Extract query parameters – keep duplicates as arrays
|
||||||
|
@@ -4,17 +4,14 @@ import {
|
|||||||
assertNotEquals,
|
assertNotEquals,
|
||||||
assertThrows,
|
assertThrows,
|
||||||
} from 'https://deno.land/std@0.204.0/assert/mod.ts';
|
} from 'https://deno.land/std@0.204.0/assert/mod.ts';
|
||||||
import {
|
import { IInternalRoute, IRouteDefinition } from '../Interfaces/mod.ts';
|
||||||
IHandler,
|
|
||||||
IInternalRoute,
|
|
||||||
IMiddleware,
|
|
||||||
IRouteDefinition,
|
|
||||||
} from '../Interfaces/mod.ts';
|
|
||||||
import { RouteBuilder } from '../mod.ts';
|
import { RouteBuilder } from '../mod.ts';
|
||||||
|
import { Handler, Middleware } from '../Types/mod.ts';
|
||||||
|
|
||||||
// Dummy objects
|
// Dummy objects
|
||||||
const dummyHandler: IHandler = async () => new Response('ok');
|
// deno-lint-ignore require-await
|
||||||
const dummyMiddleware: IMiddleware = async (_, next) => await next();
|
const dummyHandler: Handler = async () => new Response('ok');
|
||||||
|
const dummyMiddleware: Middleware = async (_, next) => await next();
|
||||||
const dummyDef: IRouteDefinition = { method: 'GET', path: '/hello' };
|
const dummyDef: IRouteDefinition = { method: 'GET', path: '/hello' };
|
||||||
const dummyMatcher = () => ({ params: {} });
|
const dummyMatcher = () => ({ params: {} });
|
||||||
|
|
||||||
@@ -39,8 +36,8 @@ Deno.test('middleware: middleware is chained immutably', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Deno.test('middleware: preserves order of middleware', () => {
|
Deno.test('middleware: preserves order of middleware', () => {
|
||||||
const mw1: IMiddleware = async (_, next) => await next();
|
const mw1: Middleware = async (_, next) => await next();
|
||||||
const mw2: IMiddleware = async (_, next) => await next();
|
const mw2: Middleware = async (_, next) => await next();
|
||||||
|
|
||||||
let result: IInternalRoute | null = null as IInternalRoute | null;
|
let result: IInternalRoute | null = null as IInternalRoute | null;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user