/** * Copyright (c) 2025 favewa * SPDX-License-Identifier: BSD-3-Clause */ import { DataStream } from "./http.ts"; import { Router } from "cobweb/routing"; export interface Context> { readonly request: Request; readonly info: Deno.ServeHandlerInfo; readonly url: URL; readonly method: string; readonly params: Params; readonly pattern: URLPatternResult; readonly stream: DataStream; readonly signal: AbortSignal; readonly router: Router; state: Map; } export async function createContext

>( router: Router, request: Request, info: Deno.ServeHandlerInfo, pattern: URLPatternResult, stream: DataStream, ): Promise> { return { router, request, info, url: new URL(request.url), method: request.method, params: (pattern.pathname.groups || {}) as P, pattern, stream: stream, signal: request.signal, state: new Map(), }; } export interface Handler

> { (ctx: Context

): Promise; } export interface Middleware { (ctx: Context, next: () => Promise): Promise; } export function compose( middlewares: readonly Middleware[], handler: Handler, ): Handler { if (!middlewares.length) return handler; return (ctx) => { let index = -1; async function dispatch(i: number): Promise { if (i <= index) throw new Error("next() called multiple times"); index = i; const fn = i < middlewares.length ? middlewares[i] : handler; if (!fn) throw new Error("No handler found"); const result = await fn(ctx, () => dispatch(i + 1)); if (!result) throw new Error("Handler must return Response"); return result; } return dispatch(0); }; }