diff --git a/src/event/utils.ts b/src/event/utils.ts index 30886384..53c26b2f 100644 --- a/src/event/utils.ts +++ b/src/event/utils.ts @@ -3,12 +3,18 @@ import type { LazyEventHandler, EventHandlerRequest, EventHandlerResponse, + EventHandlerObject, } from "../types"; +import type { H3Event } from "./event"; export function defineEventHandler< Request extends EventHandlerRequest = EventHandlerRequest, - Response = any, ->(handler: EventHandler): EventHandler; + Response = EventHandlerResponse, +>( + handler: + | EventHandler + | EventHandlerObject, +): EventHandler; // TODO: remove when appropriate // This signature provides backwards compatibility with previous signature where first generic was return type export function defineEventHandler< @@ -26,10 +32,41 @@ export function defineEventHandler< export function defineEventHandler< Request extends EventHandlerRequest = EventHandlerRequest, Response = EventHandlerResponse, ->(handler: EventHandler): EventHandler { - handler.__is_handler__ = true; - return handler; +>( + handler: + | EventHandler + | EventHandlerObject, +): EventHandler { + // Function Syntax + if (typeof handler === "function") { + return Object.assign(handler, { __is_handler__: true }); + } + // Object Syntax + const _handler: EventHandler = (event) => { + return _callHandler(event, handler); + }; + return Object.assign(_handler, { __is_handler__: true }); +} + +async function _callHandler(event: H3Event, handler: EventHandlerObject) { + if (handler.before) { + for (const hook of handler.before) { + await hook(event); + if (event.handled) { + return; + } + } + } + const body = await handler.handler(event); + const response = { body }; + if (handler.after) { + for (const hook of handler.after) { + await hook(event, response); + } + } + return response.body; } + export const eventHandler = defineEventHandler; export function isEventHandler(input: any): input is EventHandler { diff --git a/src/types.ts b/src/types.ts index 62022fc3..32758386 100644 --- a/src/types.ts +++ b/src/types.ts @@ -62,6 +62,18 @@ export interface EventHandler< (event: H3Event): Response; } +export type EventHandlerObject< + Request extends EventHandlerRequest = EventHandlerRequest, + Response extends EventHandlerResponse = EventHandlerResponse, +> = { + handler: EventHandler; + before?: ((event: H3Event) => void | Promise)[]; + after?: (( + event: H3Event, + response: { body?: Response }, + ) => void | Promise)[]; +}; + export type LazyEventHandler = () => EventHandler | Promise; export type RequestHeaders = { [name: string]: string | undefined }; diff --git a/test/types.test-d.ts b/test/types.test-d.ts index f2029085..68c346fe 100644 --- a/test/types.test-d.ts +++ b/test/types.test-d.ts @@ -11,6 +11,29 @@ import { describe("types", () => { describe("eventHandler", () => { + it("object syntax definitions", async () => { + const handler = eventHandler({ + before: [ + (event) => { + expectTypeOf(event).toEqualTypeOf(); + }, + ], + async handler(event) { + expectTypeOf(event).toEqualTypeOf(); + + const body = await readBody(event); + // TODO: Default to unknown in next major version + expectTypeOf(body).toBeAny(); + + return { + foo: "bar", + }; + }, + }); + expectTypeOf(await handler({} as H3Event)).toEqualTypeOf<{ + foo: string; + }>(); + }); it("return type (inferred)", () => { const handler = eventHandler(() => { return {