80 lines
2.2 KiB
TypeScript
80 lines
2.2 KiB
TypeScript
import "../core/metadata.ts";
|
|
import {
|
|
defineMemberMetadata,
|
|
getAllMemberMetadata,
|
|
} from "../core/metadata.ts";
|
|
import type { HttpMethod } from "../contracts/typegen/http/method.ts";
|
|
import { normalizeMethod } from "../contracts/typegen/http/method.ts";
|
|
import type { StatusCode } from "../contracts/typegen/http/respcode.ts";
|
|
|
|
/**
|
|
* `@Route()` binds a controller method to an HTTP route.
|
|
*/
|
|
export const ROUTE_METADATA_KEY = "muddle:route";
|
|
|
|
export interface RouteOptions {
|
|
/** HTTP method(s) handled by the route, defaults to `GET`. */
|
|
method?: HttpMethod | HttpMethod[];
|
|
/** Status code used when the handler returns a plain value. */
|
|
status?: StatusCode;
|
|
/** Route name, used by the fluent view helpers. */
|
|
name?: string;
|
|
/** Human-readable route description. */
|
|
description?: string;
|
|
/** Parameter description by parameter name. */
|
|
params?: Record<string, string>;
|
|
}
|
|
|
|
export interface RouteMetadata {
|
|
/** Path relative to the controller prefix. */
|
|
path: string;
|
|
method: HttpMethod;
|
|
options: RouteOptions;
|
|
/** Name of the decorated method. */
|
|
propertyKey: string;
|
|
}
|
|
|
|
function normalizeMethods(method: RouteOptions["method"]): HttpMethod[] {
|
|
if (method === undefined) return ["GET"];
|
|
const list = Array.isArray(method) ? method : [method];
|
|
return list.map((entry) => normalizeMethod(entry));
|
|
}
|
|
|
|
/** Declares one or more routes for a controller method. */
|
|
export function Route(
|
|
path: string,
|
|
options: RouteOptions = {},
|
|
): MethodDecorator {
|
|
return (target, propertyKey, descriptor) => {
|
|
for (const httpMethod of normalizeMethods(options.method)) {
|
|
defineMemberMetadata(
|
|
ROUTE_METADATA_KEY,
|
|
{
|
|
path,
|
|
method: httpMethod,
|
|
options,
|
|
propertyKey: String(propertyKey),
|
|
} satisfies RouteMetadata,
|
|
target as object,
|
|
propertyKey as string,
|
|
);
|
|
}
|
|
return descriptor;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Reads every route declared on a controller class.
|
|
*
|
|
* When a property key is given, only the routes of that method are returned.
|
|
*/
|
|
export function getRoutes(
|
|
target: object,
|
|
propertyKey?: string,
|
|
): RouteMetadata[] {
|
|
return getAllMemberMetadata(ROUTE_METADATA_KEY, target)
|
|
.filter(
|
|
(entry) => propertyKey === undefined || entry.propertyKey === propertyKey,
|
|
)
|
|
.map((entry) => entry.value as RouteMetadata);
|
|
}
|