diff --git a/src/runtime/app.ts b/src/runtime/app.ts index 98d40fa913..d8327dab2c 100644 --- a/src/runtime/app.ts +++ b/src/runtime/app.ts @@ -80,7 +80,12 @@ function createNitroApp(): NitroApp { const regularHandlers = handlers.filter((h) => !h.formAction); if (formActionsHandlers.length > 0) { - const renderer = handlers.find(({ route }) => route === "/**"); + const renderer = regularHandlers.find(({ route }) => route === "/**"); + if (!renderer) { + throw new Error( + "[Form Actions]: Missing renderer. Please properly set `nitro.options.renderer`." + ); + } const rendererHandler = lazyEventHandler(renderer.handler); for (const h of formActionsHandlers) { diff --git a/test/fixture/actions/action.ts b/test/fixture/actions/action.ts index fe577f6fc2..a30ab2a1c3 100644 --- a/test/fixture/actions/action.ts +++ b/test/fixture/actions/action.ts @@ -3,28 +3,55 @@ import { type EventHandler, type H3Event, defineEventHandler, - readBody -} from 'h3' + readBody, +} from "h3"; interface Actions { - [key: string]: EventHandler + [key: string]: EventHandler; } -function defineFormActions (actions: Actions) { +function defineFormActions(actions: Actions) { return (event: H3Event) => { - const action = Object.keys(getQuery(event))[0] - const handler = action ? actions[action] : Object.values(actions)[0] - return defineEventHandler(handler(event)) + const action = Object.keys(getQuery(event))[0]; + const handler = action ? actions[action] : Object.values(actions)[0]; + return defineEventHandler(handler(event)); + }; +} + +async function respondWithResponse(event: H3Event, response: Response) { + // @ts-expect-error + for (const [key, value] of response.headers) { + event.node.res.setHeader(key, value); } + + if (response.body) { + const contentType = response.headers.get("Content-Type") || ""; + if (contentType.includes("text") || contentType.includes("json")) { + for await (const chunk of response.body as unknown as AsyncIterable) { + const stringChunk = new TextDecoder().decode(chunk); // Convert chunk to string + event.node.res.write(stringChunk); + } + } else { + // for binary data like images, videos, etc. + for await (const chunk of response.body as unknown as AsyncIterable) { + event.node.res.write(chunk); + } + } + } + return event.node.res.end(); } function actionResponse(event: H3Event, data: any, action?: any) { - return { data, action } + // return { data, action }; + return respondWithResponse( + event, + new Response(JSON.stringify({ data, action })) + ); } export default defineFormActions({ - default: async event => { - const body = await readBody(event) - return actionResponse(event, { ...body }) - } -}) + default: async (event) => { + const body = await readBody(event); + return actionResponse(event, { ...body }); + }, +});