51 lines
1.9 KiB
TypeScript
51 lines
1.9 KiB
TypeScript
import { ZodError } from "zod";
|
|
|
|
// ── Action Result Type ───────────────────────────────────────────────
|
|
|
|
export type ActionResult<T = Record<string, unknown>> =
|
|
{ ok: true; data?: T } | { ok: false; error: string; fieldErrors?: Record<string, string[]> };
|
|
|
|
// ── Helper to build results ──────────────────────────────────────────
|
|
|
|
export function actionOk<T = Record<string, unknown>>(data?: T): ActionResult<T> {
|
|
return { ok: true, data: data ?? ({} as T) };
|
|
}
|
|
|
|
export function actionError(message: string): ActionResult<never> {
|
|
return { ok: false, error: message };
|
|
}
|
|
|
|
// ── Throwable error ──────────────────────────────────────────────────
|
|
|
|
export class ActionError extends Error {
|
|
constructor(message: string) {
|
|
super(message);
|
|
this.name = "ActionError";
|
|
}
|
|
}
|
|
|
|
// ── Error handler ────────────────────────────────────────────────────
|
|
|
|
export function handleActionError(error: unknown): ActionResult<never> {
|
|
if (error instanceof ZodError) {
|
|
return {
|
|
ok: false,
|
|
error: "Validation failed",
|
|
fieldErrors: error.flatten().fieldErrors as Record<string, string[]>,
|
|
};
|
|
}
|
|
if (error instanceof Error && error.name === "ActionError") {
|
|
return { ok: false, error: error.message };
|
|
}
|
|
// Prisma P2025
|
|
if (
|
|
error instanceof Error &&
|
|
error.constructor.name === "PrismaClientKnownRequestError" &&
|
|
(error as Error & { code?: string }).code === "P2025"
|
|
) {
|
|
return { ok: false, error: "Not found" };
|
|
}
|
|
console.error("[Action error]", error);
|
|
return { ok: false, error: "Internal server error" };
|
|
}
|