import { Hono, type MiddlewareHandler, type ValidationTargets } from "hono"; import type { H } from "hono/types"; import { safelyParseObjectValues } from "../utils"; import type { Endpoint, Middleware } from "./Endpoint"; import { zValidator } from "./lib/zValidator"; type RouteProxy = { [K in keyof Endpoints]: Endpoints[K]; }; export interface ClassController { getController: () => Hono; getMiddleware?: MiddlewareHandler; } /** * @deprecated */ export class Controller< Endpoints extends Record = Record, Middlewares extends Record = Record > { protected endpoints: Endpoints = {} as Endpoints; protected middlewares: Middlewares = {} as Middlewares; public prefix: string = "/"; public routes: RouteProxy; constructor( prefix: string = "/", endpoints: Endpoints = {} as Endpoints, middlewares: Middlewares = {} as Middlewares ) { this.prefix = prefix; this.endpoints = endpoints; this.middlewares = middlewares; this.routes = new Proxy( {}, { get: (_, name: string) => { return this.endpoints[name]; } } ) as RouteProxy; } add( this: Controller, name: Name, endpoint: E ): Controller> { const newEndpoints = { ...this.endpoints, [name]: endpoint } as Endpoints & Record; const newController: Controller> = new Controller< Endpoints & Record >(); newController.endpoints = newEndpoints; newController.middlewares = this.middlewares; return newController; } get(name: Name): Endpoints[Name] { return this.endpoints[name]; } honoify(_hono: Hono = new Hono()) { const hono = _hono.basePath(this.prefix); // apply middlewares for (const m_name in this.middlewares) { const middleware = this.middlewares[m_name]; if (typeof middleware === "function") { //if (isDebug()) console.log("+++ appyling middleware", m_name, middleware); hono.use(middleware); } } // apply endpoints for (const name in this.endpoints) { const endpoint = this.endpoints[name]; if (!endpoint) continue; const handlers: H[] = []; const supportedValidations: Array = ["param", "query", "json"]; // if validations are present, add them to the handlers for (const validation of supportedValidations) { if (endpoint.validation[validation]) { handlers.push(async (c, next) => { // @todo: potentially add "strict" to all schemas? const res = await zValidator( validation, endpoint.validation[validation] as any, (target, value, c) => { if (["query", "param"].includes(target)) { return safelyParseObjectValues(value); } //console.log("preprocess", target, value, c.req.raw.url); return value; } )(c, next); if (res instanceof Response && res.status === 400) { const error = await res.json(); return c.json( { error: "Validation error", target: validation, message: error }, 400 ); } return res; }); } } // add actual handler handlers.push(endpoint.toHandler()); const method = endpoint.method.toLowerCase() as | "get" | "post" | "put" | "delete" | "patch"; //if (isDebug()) console.log("--- adding", method, endpoint.path); hono[method](endpoint.path, ...handlers); } return hono; } toJSON() { const endpoints: any = {}; for (const name in this.endpoints) { const endpoint = this.endpoints[name]; if (!endpoint) continue; endpoints[name] = { method: endpoint.method, path: (this.prefix + endpoint.path).replace("//", "/") }; } return endpoints; } }