diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f2483bc3..2b9895fb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,3 +32,4 @@ jobs: - run: pnpm -C examples/vue-ssr-extra test-e2e - run: pnpm -C examples/vue-ssr-extra build - run: pnpm -C examples/vue-ssr-extra test-e2e-preview + - run: pnpm -C examples/vue-ssr-extra tsc diff --git a/examples/vue-ssr-extra/src/features/server-action/client.ts b/examples/vue-ssr-extra/src/features/server-action/client.ts index 4eea3956..b986bdfd 100644 --- a/examples/vue-ssr-extra/src/features/server-action/client.ts +++ b/examples/vue-ssr-extra/src/features/server-action/client.ts @@ -1,5 +1,9 @@ import { tinyassert } from "@hiogawa/utils"; -import { ACTION_PATH, registerServerReference } from "./shared"; +import { + ACTION_PATH, + registerServerReference, + type ServerActionPayload, +} from "./shared"; export function createServerReference(id: string, name: string) { const action = async (...args: unknown[]) => { @@ -25,7 +29,7 @@ function encodeActionPayload( }; } else { return { - body: JSON.stringify({ id, name, args }), + body: JSON.stringify({ id, name, args } satisfies ServerActionPayload), headers: { "content-type": "application/json", }, diff --git a/examples/vue-ssr-extra/src/features/server-action/server.ts b/examples/vue-ssr-extra/src/features/server-action/server.ts index 864b8973..91545acf 100644 --- a/examples/vue-ssr-extra/src/features/server-action/server.ts +++ b/examples/vue-ssr-extra/src/features/server-action/server.ts @@ -1,5 +1,5 @@ import { tinyassert } from "@hiogawa/utils"; -import { ACTION_PATH, decodeActionRequest } from "./shared"; +import { ACTION_PATH, type ServerActionPayload } from "./shared"; export async function serverActionHandler({ request }: { request: Request }) { const url = new URL(request.url); @@ -34,3 +34,24 @@ async function importServerReference(id: string) { return importReference(); } } + +async function decodeActionRequest( + request: Request, +): Promise { + const contentType = request.headers.get("content-type"); + tinyassert(contentType); + if (contentType === "application/json") { + return request.json(); + } else { + const formData = await request.formData(); + const id = formData.get("__id"); + const name = formData.get("__name"); + tinyassert(typeof id === "string"); + tinyassert(typeof name === "string"); + return { + id, + name, + args: [formData], + }; + } +} diff --git a/examples/vue-ssr-extra/src/features/server-action/shared.ts b/examples/vue-ssr-extra/src/features/server-action/shared.ts index 1eadc74e..90a3870c 100644 --- a/examples/vue-ssr-extra/src/features/server-action/shared.ts +++ b/examples/vue-ssr-extra/src/features/server-action/shared.ts @@ -1,11 +1,11 @@ import { tinyassert } from "@hiogawa/utils"; -import { defineComponent, h, ref } from "vue"; +import { defineComponent, h, ref, type PropType } from "vue"; export const ACTION_PATH = "/__action"; type FormAction = (v: FormData) => Promise; -type ServerActionPayload = { +export type ServerActionPayload = { id: string; name: string; args: unknown[]; @@ -27,7 +27,7 @@ export function registerServerReference( export const Form = defineComponent({ props: { action: { - type: Function, + type: Function as PropType, required: true, }, }, @@ -84,6 +84,7 @@ export function useEnhance( const status = ref<"idle" | "pending" | "success">("idle"); const enhanced: FormAction = async (v) => { status.value = "pending"; + // TODO: what if unmounted before finishing action? const result = await action(v); options?.onSuccess(result); status.value = "success"; @@ -91,43 +92,3 @@ export function useEnhance( const newAction = registerServerReference(enhanced, meta.__id, meta.__name); return [newAction, { status }] as const; } - -export function encodeActionRequest( - id: string, - name: string, - args: unknown[], -): RequestInit { - if (args.length === 1 && args[0] instanceof FormData) { - return { - body: args[0], - }; - } else { - return { - body: JSON.stringify({ id, name, args } satisfies ServerActionPayload), - headers: { - "content-type": "application/json", - }, - }; - } -} - -export async function decodeActionRequest( - request: Request, -): Promise { - const contentType = request.headers.get("content-type"); - tinyassert(contentType); - if (contentType === "application/json") { - return request.json(); - } else { - const formData = await request.formData(); - const id = formData.get("__id"); - const name = formData.get("__name"); - tinyassert(typeof id === "string"); - tinyassert(typeof name === "string"); - return { - id, - name, - args: [formData], - }; - } -}