interface ServerActionError { message: string; code?: string; /** First field with an error, as a dotted path such as `address.city`. */ field?: string; /** Messages per field, keyed by dotted path (`items.0.name`). Absent when no field failed. */ fields?: Record; /** Messages that belong to the whole input, such as a failed cross-field refinement. */ formErrors?: string[]; statusCode?: number; shouldRedirect?: boolean; redirectTo?: string; } type RedirectConfig = { url: string; replace?: boolean; delay?: number; }; type ServerActionResponse = { success: true; data: T; redirect?: string | RedirectConfig; } | { success: false; error: ServerActionError; }; type ServerAction = TInput extends void ? () => Promise> : (input: TInput) => Promise>; /** * What a hook's `execute` resolves to. * * - `success`: `data` holds the result, even when it is `undefined`. * - `error`: `serverError` holds the action's error (`validationErrors` its * field messages), or `fetchError` the message of a network failure. * - `navigation`: the action navigated away (`redirect()`, `notFound()`…). */ interface ActionResult { status: "success" | "error" | "navigation"; data?: TOutput; serverError?: ServerActionError; validationErrors?: Record; fetchError?: string; } /** * Shows toast messages for the hooks. Sonner's `toast` (and most toast * libraries') satisfies it as-is: ``. */ interface ToastAdapter { success(message: string): unknown; error(message: string): unknown; } export type { ActionResult as A, ServerAction as S, ToastAdapter as T, ServerActionResponse as a, ServerActionError as b };