CI: Update Pages (2025-11-23 11:20:49)

This commit is contained in:
2025-11-23 11:20:49 +00:00
commit 24771db506
138 changed files with 7472 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
import type { Params, Query, State } from '../Types/mod.ts';
/**
* Represents the complete context for a single HTTP request,
* passed through the middleware pipeline and to the final route handler.
*
* This context object encapsulates all relevant runtime data for a request,
* including the original request, path parameters, query parameters,
* and a shared, mutable application state.
*
* @template TState Structured per-request state shared across middlewares and handlers.
* @template TParams Parsed URL path parameters, typically derived from route templates.
* @template TQuery Parsed query string parameters, preserving multi-value semantics.
*/
export interface IContext<
TState extends State = State,
TParams extends Params = Params,
TQuery extends Query = Query,
> {
/**
* The original HTTP request object as received by Deno.
* Contains all standard fields like headers, method, body, etc.
*/
req: Request;
/**
* Route parameters parsed from the URL path, based on route definitions
* that include dynamic segments (e.g., `/users/:id` → `{ id: "123" }`).
*
* These parameters are considered read-only and are set by the router.
*/
params: TParams;
/**
* Query parameters extracted from the request URL's search string.
*
* Values may occur multiple times (e.g., `?tag=ts&tag=deno`), and are therefore
* represented as either a string or an array of strings, depending on occurrence.
*
* Use this field to access filters, flags, pagination info, or similar modifiers.
*/
query: TQuery;
/**
* A typed, mutable object used to pass structured data between middlewares and handlers.
*
* This object is ideal for sharing validated input, user identity, trace information,
* or other contextual state throughout the request lifecycle.
*
* Type-safe access to fields is ensured by the generic `TState` type.
*/
state: TState;
}

View File

@@ -0,0 +1,40 @@
import type { IContext } from '../Interfaces/mod.ts';
import type { HttpErrorHandler, validHttpErrorCodes } from '../Types/mod.ts';
/**
* A mapping of HTTP status codes to their corresponding error handlers.
*
* This interface defines required handlers for common critical status codes (404 and 500)
* and allows optional handlers for all other known error codes defined in `validHttpErrorCodes`.
*
* This hybrid approach ensures predictable handling for key failure cases,
* while remaining flexible for less common codes.
*
* @template TContext - The context type used in all error handlers.
*
* @example
* ```ts
* const errorHandlers: IHttpErrorHandlers = {
* 404: (ctx) => new Response("Not Found", { status: 404 }),
* 500: (ctx, err) => {
* console.error(err);
* return new Response("Internal Server Error", { status: 500 });
* },
* 429: (ctx) => new Response("Too Many Requests", { status: 429 }),
* };
* ```
*/
export interface IHttpErrorHandlers<TContext extends IContext = IContext>
extends
Partial<
Record<
Exclude<typeof validHttpErrorCodes[number], 404 | 500>,
HttpErrorHandler<TContext>
>
> {
/** Required error handler for HTTP 404 (Not Found). */
404: HttpErrorHandler<TContext>;
/** Required error handler for HTTP 500 (Internal Server Error). */
500: HttpErrorHandler<TContext>;
}

View File

@@ -0,0 +1,49 @@
import type { IContext } from './IContext.ts';
import type { IRouteBuilder } from './IRouteBuilder.ts';
import type { IRouteDefinition } from './IRouteDefinition.ts';
/**
* The `IHttpKernel` interface defines the public API for a type-safe, middleware-driven HTTP dispatching system.
*
* Implementations of this interface are responsible for:
* - Registering routes with optional per-route context typing
* - Handling incoming requests by matching and dispatching to appropriate handlers
* - Managing the complete middleware pipeline and final response generation
*
* The kernel operates on a customizable `IContext` type to support strongly typed request parameters, state,
* and query values across the entire routing lifecycle.
*
* @typeParam TContext - The default context type used for all routes unless overridden per-route.
*/
export interface IHttpKernel<TContext extends IContext = IContext> {
/**
* Registers a new HTTP route (static or dynamic) and returns a route builder for middleware/handler chaining.
*
* This method supports contextual polymorphism via the `_TContext` type parameter, enabling fine-grained
* typing of route-specific `params`, `query`, and `state` values. The route is not registered until
* `.handle()` is called on the returned builder.
*
* @typeParam _TContext - An optional override for the context type specific to this route.
* Falls back to the global `TContext` of the kernel if omitted.
*
* @param definition - A route definition specifying the HTTP method and path or custom matcher.
* @returns A fluent builder interface to define middleware and attach a final handler.
*/
route<_TContext extends IContext = TContext>(
definition: IRouteDefinition,
): IRouteBuilder<_TContext>;
/**
* Handles an incoming HTTP request and produces a `Response`.
*
* The kernel matches the request against all registered routes by method and matcher,
* constructs a typed context, and executes the middleware/handler pipeline.
* If no route matches, a 404 error handler is invoked.
*
* This method is designed to be passed directly to `Deno.serve()` or similar server frameworks.
*
* @param request - The incoming HTTP request object.
* @returns A `Promise` resolving to a complete HTTP response.
*/
handle(request: Request): Promise<Response>;
}

View File

@@ -0,0 +1,10 @@
import type { ResponseDecorator } from '../Types/mod.ts';
import type { IContext } from './IContext.ts';
import type { IHttpErrorHandlers } from './IHttpErrorHandlers.ts';
import type { IRouteBuilderFactory } from './IRouteBuilder.ts';
export interface IHttpKernelConfig<TContext extends IContext = IContext> {
decorateResponse: ResponseDecorator<TContext>;
routeBuilderFactory: IRouteBuilderFactory;
httpErrorHandlers: IHttpErrorHandlers<TContext>;
}

View File

@@ -0,0 +1,64 @@
import type { Handler, HttpMethod, Middleware } from '../Types/mod.ts';
import type { IContext, IRouteMatcher } from './mod.ts';
/**
* Represents an internally registered route within the HttpKernel.
*
* Contains all data required to match an incoming request and dispatch it
* through the associated middleware chain and final handler.
*/
export interface IInternalRoute<TContext extends IContext = IContext> {
/**
* The HTTP method (e.g. 'GET', 'POST') that this route responds to.
* The method should always be in uppercase.
*/
method: HttpMethod;
/**
* A matcher function used to determine whether this route matches a given request.
*
* If the matcher returns `null`, the route does not apply to the request.
* If it returns a params object, the route is considered matched and the extracted
* parameters are passed into the request context.
*
* @param url - The parsed URL object from the incoming request.
* @param req - The original Request object.
* @returns An object with extracted path parameters, or `null` if not matched.
*/
matcher: IRouteMatcher;
/**
* An ordered list of middleware functions to be executed before the handler.
*/
middlewares: Middleware<TContext>[];
/**
* The final handler that generates the HTTP response after all middleware has run.
*/
handler: Handler<TContext>;
/**
* The fully compiled execution pipeline for this route.
*
* This function is generated at route registration time and encapsulates the
* entire middleware chain as well as the final handler. It is called by the
* HttpKernel during request dispatch when a route has been matched.
*
* Internally, `runRoute` ensures that each middleware is invoked in the correct order
* and receives a `next()` callback to pass control downstream. The final handler is
* invoked once all middleware has completed or short-circuited the pipeline.
*
* It is guaranteed that:
* - The function is statically compiled and does not perform dynamic dispatching.
* - Each middleware can only call `next()` once; repeated invocations will throw.
* - The return value is either a `Response` or a Promise resolving to one.
*
* @param ctx - The context object carrying route, request, response and other scoped data.
* @returns A `Response` object or a Promise resolving to a `Response`.
*
* @throws {Error} If a middleware calls `next()` more than once.
*/
runRoute: (
ctx: TContext,
) => Promise<Response> | Response;
}

View File

@@ -0,0 +1,39 @@
import type { Handler, Middleware } from '../Types/mod.ts';
import type { IInternalRoute } from './IInternalRoute.ts';
import type { IRouteDefinition } from './IRouteDefinition.ts';
import type { IContext } from './mod.ts';
export interface IRouteBuilderFactory<TContext extends IContext = IContext> {
new (
registerRoute: (route: IInternalRoute<TContext>) => void,
def: IRouteDefinition,
mws?: Middleware<TContext>[],
): IRouteBuilder<TContext>;
}
/**
* Provides a fluent API to build a single route configuration by chaining
* middleware and setting the final request handler.
*/
export interface IRouteBuilder<TContext extends IContext = IContext> {
/**
* Adds a middleware to the current route.
* Middleware will be executed in the order of registration.
*
* @param mw - A middleware function.
* @returns The route builder for further chaining.
*/
middleware(
mw: Middleware<TContext>,
): IRouteBuilder<TContext>;
/**
* Sets the final request handler for the route.
* Calling this finalizes the route and registers it in the kernel.
*
* @param handler - The function to execute when this route is matched.
*/
handle(
handler: Handler<TContext>,
): void;
}

View File

@@ -0,0 +1,91 @@
import { type HttpMethod, isHttpMethod } from '../Types/mod.ts';
import type { IRouteMatcher } from './IRouteMatcher.ts';
/**
* Defines a static route using a path pattern with optional parameters.
*
* Suitable for conventional routes like "/users/:id", which can be parsed
* into named parameters using a path-matching library.
*/
export interface IStaticRouteDefinition {
/**
* The HTTP method this route should match (e.g. "GET", "POST").
*/
method: HttpMethod;
/**
* A static path pattern for the route, which may include named parameters
* (e.g. "/caches/:id"). Internally, this can be converted to a regex matcher.
*/
path: string;
}
/**
* Defines a dynamic route using a custom matcher function instead of a static path.
*
* Useful for complex URL structures that cannot easily be expressed using a static pattern,
* such as routes with variable prefixes or conditional segment logic.
*/
export interface IDynamicRouteDefinition {
/**
* The HTTP method this route should match (e.g. "GET", "POST").
*/
method: HttpMethod;
/**
* A custom matcher function that receives the parsed URL and raw request.
* If the function returns `null`, the route does not match.
* If the function returns a params object, the route is considered matched.
*/
matcher: IRouteMatcher;
}
/**
* A route definition can either be a conventional static route with a path pattern,
* or a dynamic route with a custom matcher function for advanced matching logic.
*/
export type IRouteDefinition = IStaticRouteDefinition | IDynamicRouteDefinition;
/**
* Type guard to check whether a route definition is a valid static route definition.
*
* Ensures that the object:
* - has a `method` property of type `HttpMethod`
* - has a `path` property of type `string`
* - does NOT have a `matcher` function (to avoid ambiguous mixed types)
*/
export function isStaticRouteDefinition(
def: IRouteDefinition,
): def is IStaticRouteDefinition {
return (
def &&
typeof def === 'object' &&
'method' in def &&
isHttpMethod(def.method) &&
'path' in def &&
typeof (def as { path?: unknown }).path === 'string' &&
!('matcher' in def)
);
}
/**
* Type guard to check whether a route definition is a valid dynamic route definition.
*
* Ensures that the object:
* - has a `method` property of type `HttpMethod`
* - has a `matcher` property of type `function`
* - does NOT have a `path` property (to avoid ambiguous mixed types)
*/
export function isDynamicRouteDefinition(
def: IRouteDefinition,
): def is IDynamicRouteDefinition {
return (
def &&
typeof def === 'object' &&
'method' in def &&
isHttpMethod(def.method) &&
'matcher' in def &&
typeof (def as { matcher?: unknown }).matcher === 'function' &&
!('path' in def)
);
}

View File

@@ -0,0 +1,6 @@
import type { Params, Query } from '../Types/mod.ts';
export interface IRouteMatch {
params?: Params;
query?: Query;
}

View File

@@ -0,0 +1,35 @@
import type { IRouteDefinition } from './IRouteDefinition.ts';
import type { IRouteMatch } from './IRouteMatch.ts';
/**
* Defines a route matcher function that evaluates whether a route applies to a given request.
*
* If the route matches, the matcher returns an object containing extracted route parameters.
* Otherwise, it returns `null`.
*/
export interface IRouteMatcher {
/**
* Evaluates whether the given URL and request match a defined route.
*
* @param url - The full URL of the incoming request.
* @param req - The raw Request object (may be used for context or headers).
* @returns An object containing path parameters if matched, or `null` if not matched.
*/
(url: URL, req: Request): null | IRouteMatch;
}
/**
* Represents a factory for creating route matcher functions from route definitions.
*
* This allows the matcher logic to be injected or replaced (e.g. for testing,
* pattern libraries, or advanced routing scenarios).
*/
export interface IRouteMatcherFactory {
/**
* Creates a matcher function based on a given route definition.
*
* @param def - The route definition (static or dynamic).
* @returns A matcher function that checks if a request matches and extracts parameters.
*/
(def: IRouteDefinition): IRouteMatcher;
}

View File

@@ -0,0 +1,43 @@
import { assertEquals } from 'https://deno.land/std@0.204.0/assert/mod.ts';
import {
type IRouteDefinition,
isDynamicRouteDefinition,
isStaticRouteDefinition,
} from '../IRouteDefinition.ts';
Deno.test('isStaticRouteDefinition returns true for static route', () => {
const staticDef: IRouteDefinition = {
method: 'GET',
path: '/users/:id',
};
assertEquals(isStaticRouteDefinition(staticDef), true);
assertEquals(isDynamicRouteDefinition(staticDef), false);
});
Deno.test('isDynamicRouteDefinition returns true for dynamic route', () => {
const dynamicDef: IRouteDefinition = {
method: 'POST',
matcher: (_url, _req) => ({ params: {} }),
};
assertEquals(isDynamicRouteDefinition(dynamicDef), true);
assertEquals(isStaticRouteDefinition(dynamicDef), false);
});
Deno.test('isStaticRouteDefinition returns false for invalid object', () => {
const invalidDef = {
method: 'GET',
} as unknown as IRouteDefinition;
assertEquals(isStaticRouteDefinition(invalidDef), false);
});
Deno.test('isDynamicRouteDefinition returns false for object with no matcher', () => {
const def = {
method: 'DELETE',
path: '/something',
};
assertEquals(isDynamicRouteDefinition(def as IRouteDefinition), false);
});

View File

@@ -0,0 +1,19 @@
// deno-coverage-ignore-file
export type { IContext } from './IContext.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 type { IRouteBuilder, IRouteBuilderFactory } from './IRouteBuilder.ts';
export {
isDynamicRouteDefinition,
isStaticRouteDefinition,
} from './IRouteDefinition.ts';
export type {
IDynamicRouteDefinition,
IRouteDefinition,
IStaticRouteDefinition,
} from './IRouteDefinition.ts';
export type { IRouteMatch } from './IRouteMatch.ts';
export type { IRouteMatcher, IRouteMatcherFactory } from './IRouteMatcher.ts';