next-action-forge 0.2.9 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -1
- package/dist/hooks/index.d.mts +3 -1
- package/dist/hooks/index.d.ts +3 -1
- package/dist/hooks/index.js +17 -2
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +17 -2
- package/dist/hooks/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ A powerful, type-safe toolkit for Next.js server actions with Zod validation and
|
|
|
13
13
|
- ⚡ **Zero config** - works out of the box
|
|
14
14
|
- 🎨 **Custom error handling** with flexible error transformation
|
|
15
15
|
- 🦆 **Duck-typed errors** - Any error with `toServerActionError()` method is automatically handled
|
|
16
|
+
- 🚦 **Server-driven redirects** - Declarative redirect configuration with full hook support
|
|
16
17
|
- 📋 **Smart FormData parsing** - Handles arrays, checkboxes, and files correctly
|
|
17
18
|
- 🍞 **Persistent toast messages** - Toast notifications survive page redirects (v0.2.0+)
|
|
18
19
|
- ✅ **React 19 & Next.js 15** compatible
|
|
@@ -193,9 +194,10 @@ export const updateProfileAction = actionClient
|
|
|
193
194
|
});
|
|
194
195
|
```
|
|
195
196
|
|
|
196
|
-
Client-side usage:
|
|
197
|
+
Client-side usage with hooks:
|
|
197
198
|
|
|
198
199
|
```tsx
|
|
200
|
+
// With useServerAction
|
|
199
201
|
const { execute } = useServerAction(logoutAction, {
|
|
200
202
|
onSuccess: (data) => {
|
|
201
203
|
// This runs BEFORE the redirect
|
|
@@ -204,6 +206,18 @@ const { execute } = useServerAction(logoutAction, {
|
|
|
204
206
|
preventRedirect: true, // Optionally prevent automatic redirect
|
|
205
207
|
redirectDelay: 1000, // Global delay for all redirects
|
|
206
208
|
});
|
|
209
|
+
|
|
210
|
+
// With useFormAction (works the same!)
|
|
211
|
+
const { form, onSubmit } = useFormAction({
|
|
212
|
+
action: loginAction, // Action with .redirect() configuration
|
|
213
|
+
schema: LoginRequestSchema,
|
|
214
|
+
onSuccess: (data) => {
|
|
215
|
+
// This also runs BEFORE the redirect
|
|
216
|
+
toast.success("Welcome back!");
|
|
217
|
+
},
|
|
218
|
+
preventRedirect: false, // Allow redirects (default)
|
|
219
|
+
redirectDelay: 500, // Override default delay
|
|
220
|
+
});
|
|
207
221
|
```
|
|
208
222
|
|
|
209
223
|
### Form Actions
|
package/dist/hooks/index.d.mts
CHANGED
|
@@ -90,6 +90,8 @@ interface UseFormActionOptions<TFieldValues extends FieldValues, TOutput> {
|
|
|
90
90
|
resetOnSuccess?: boolean;
|
|
91
91
|
showSuccessToast?: boolean | string | ((data: TOutput) => string);
|
|
92
92
|
showErrorToast?: boolean | string | ((error: ServerActionError) => string);
|
|
93
|
+
preventRedirect?: boolean;
|
|
94
|
+
redirectDelay?: number;
|
|
93
95
|
}
|
|
94
96
|
interface UseFormActionReturn<TFieldValues extends FieldValues, TOutput> {
|
|
95
97
|
form: UseFormReturn<TFieldValues>;
|
|
@@ -99,7 +101,7 @@ interface UseFormActionReturn<TFieldValues extends FieldValues, TOutput> {
|
|
|
99
101
|
reset: () => void;
|
|
100
102
|
handleSubmit: (data: TFieldValues) => Promise<void>;
|
|
101
103
|
}
|
|
102
|
-
declare function useFormAction<TFieldValues extends FieldValues = FieldValues, TOutput = void>({ action, schema, defaultValues, mode, transformData, onSuccess, onError, resetOnSuccess, showSuccessToast, showErrorToast, }: UseFormActionOptions<TFieldValues, TOutput>): UseFormActionReturn<TFieldValues, TOutput>;
|
|
104
|
+
declare function useFormAction<TFieldValues extends FieldValues = FieldValues, TOutput = void>({ action, schema, defaultValues, mode, transformData, onSuccess, onError, resetOnSuccess, showSuccessToast, showErrorToast, preventRedirect, redirectDelay, }: UseFormActionOptions<TFieldValues, TOutput>): UseFormActionReturn<TFieldValues, TOutput>;
|
|
103
105
|
|
|
104
106
|
declare function ToastRestorer(): null;
|
|
105
107
|
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -90,6 +90,8 @@ interface UseFormActionOptions<TFieldValues extends FieldValues, TOutput> {
|
|
|
90
90
|
resetOnSuccess?: boolean;
|
|
91
91
|
showSuccessToast?: boolean | string | ((data: TOutput) => string);
|
|
92
92
|
showErrorToast?: boolean | string | ((error: ServerActionError) => string);
|
|
93
|
+
preventRedirect?: boolean;
|
|
94
|
+
redirectDelay?: number;
|
|
93
95
|
}
|
|
94
96
|
interface UseFormActionReturn<TFieldValues extends FieldValues, TOutput> {
|
|
95
97
|
form: UseFormReturn<TFieldValues>;
|
|
@@ -99,7 +101,7 @@ interface UseFormActionReturn<TFieldValues extends FieldValues, TOutput> {
|
|
|
99
101
|
reset: () => void;
|
|
100
102
|
handleSubmit: (data: TFieldValues) => Promise<void>;
|
|
101
103
|
}
|
|
102
|
-
declare function useFormAction<TFieldValues extends FieldValues = FieldValues, TOutput = void>({ action, schema, defaultValues, mode, transformData, onSuccess, onError, resetOnSuccess, showSuccessToast, showErrorToast, }: UseFormActionOptions<TFieldValues, TOutput>): UseFormActionReturn<TFieldValues, TOutput>;
|
|
104
|
+
declare function useFormAction<TFieldValues extends FieldValues = FieldValues, TOutput = void>({ action, schema, defaultValues, mode, transformData, onSuccess, onError, resetOnSuccess, showSuccessToast, showErrorToast, preventRedirect, redirectDelay, }: UseFormActionOptions<TFieldValues, TOutput>): UseFormActionReturn<TFieldValues, TOutput>;
|
|
103
105
|
|
|
104
106
|
declare function ToastRestorer(): null;
|
|
105
107
|
|
package/dist/hooks/index.js
CHANGED
|
@@ -250,6 +250,7 @@ function useOptimisticAction(initialState, action, options) {
|
|
|
250
250
|
|
|
251
251
|
// src/hooks/use-form-action.ts
|
|
252
252
|
var import_react3 = require("react");
|
|
253
|
+
var import_navigation2 = require("next/navigation");
|
|
253
254
|
var import_react_hook_form = require("react-hook-form");
|
|
254
255
|
var import_zod = require("@hookform/resolvers/zod");
|
|
255
256
|
var import_sonner2 = require("sonner");
|
|
@@ -283,8 +284,11 @@ function useFormAction({
|
|
|
283
284
|
onError,
|
|
284
285
|
resetOnSuccess = false,
|
|
285
286
|
showSuccessToast = false,
|
|
286
|
-
showErrorToast = true
|
|
287
|
+
showErrorToast = true,
|
|
288
|
+
preventRedirect = false,
|
|
289
|
+
redirectDelay = 0
|
|
287
290
|
}) {
|
|
291
|
+
const router = (0, import_navigation2.useRouter)();
|
|
288
292
|
const form = (0, import_react_hook_form.useForm)({
|
|
289
293
|
resolver: schema ? (0, import_zod.zodResolver)(schema) : void 0,
|
|
290
294
|
defaultValues,
|
|
@@ -333,8 +337,19 @@ function useFormAction({
|
|
|
333
337
|
form.reset();
|
|
334
338
|
}
|
|
335
339
|
onSuccess?.(actionState.data);
|
|
340
|
+
if (actionState.redirect && !preventRedirect) {
|
|
341
|
+
const redirectConfig = typeof actionState.redirect === "string" ? { url: actionState.redirect } : actionState.redirect;
|
|
342
|
+
const delay = redirectConfig.delay ?? redirectDelay;
|
|
343
|
+
setTimeout(() => {
|
|
344
|
+
if (redirectConfig.replace) {
|
|
345
|
+
router.replace(redirectConfig.url);
|
|
346
|
+
} else {
|
|
347
|
+
router.push(redirectConfig.url);
|
|
348
|
+
}
|
|
349
|
+
}, delay);
|
|
350
|
+
}
|
|
336
351
|
}
|
|
337
|
-
}, [actionState, form, onError, onSuccess, resetOnSuccess, showErrorToast, showSuccessToast]);
|
|
352
|
+
}, [actionState, form, onError, onSuccess, resetOnSuccess, showErrorToast, showSuccessToast, preventRedirect, redirectDelay, router]);
|
|
338
353
|
const handleSubmit = async (data) => {
|
|
339
354
|
form.clearErrors();
|
|
340
355
|
const formData = transformData ? transformData(data) : objectToFormData(data);
|
package/dist/hooks/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/hooks/index.ts","../../src/hooks/use-server-action.ts","../../src/next/errors/redirect.ts","../../src/next/errors/http-access-fallback.ts","../../src/next/errors/index.ts","../../src/hooks/use-optimistic-action.ts","../../src/hooks/use-form-action.ts","../../src/hooks/toast-restorer.tsx"],"sourcesContent":["export * from \"./use-server-action\";\nexport * from \"./use-optimistic-action\";\nexport * from \"./use-form-action\";\nexport { ToastRestorer } from \"./toast-restorer\";","import { useCallback, useState, useTransition } from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport { toast } from \"sonner\";\nimport type { ServerAction, ActionResult, ServerActionResponse } from \"../types\";\nimport { isNextNavigationError } from \"../next/errors\";\n\nexport interface UseServerActionOptions<TOutput> {\n onSuccess?: (data: TOutput) => void | Promise<void>;\n onError?: (error: ActionResult<TOutput>) => void | Promise<void>;\n \n successMessage?: string | ((data: TOutput) => string);\n errorMessage?: string | ((error: ActionResult<TOutput>) => string);\n showSuccessToast?: boolean;\n showErrorToast?: boolean;\n \n redirectOnAuthError?: boolean;\n preventRedirect?: boolean;\n redirectDelay?: number;\n}\n\nexport interface UseServerActionReturn<TInput, TOutput> {\n execute: TInput extends void \n ? () => Promise<ActionResult<TOutput>>\n : (input: TInput) => Promise<ActionResult<TOutput>>;\n \n result: ActionResult<TOutput> | undefined;\n isExecuting: boolean;\n hasSucceeded: boolean;\n hasErrored: boolean;\n \n reset: () => void;\n}\n\n// Overload for void actions\nexport function useServerAction<TOutput>(\n action: ServerAction<void, TOutput>,\n options?: UseServerActionOptions<TOutput>\n): UseServerActionReturn<void, TOutput>;\n\n// Overload for actions with input\nexport function useServerAction<TInput, TOutput>(\n action: ServerAction<TInput, TOutput>,\n options?: UseServerActionOptions<TOutput>\n): UseServerActionReturn<TInput, TOutput>;\n\n// Implementation\nexport function useServerAction<TInput, TOutput>(\n action: ServerAction<TInput, TOutput>,\n options: UseServerActionOptions<TOutput> = {}\n): UseServerActionReturn<TInput, TOutput> {\n const router = useRouter();\n const [result, setResult] = useState<ActionResult<TOutput>>();\n const [isExecuting, setIsExecuting] = useState(false);\n const [, startTransition] = useTransition();\n \n const {\n onSuccess,\n onError,\n successMessage,\n errorMessage,\n showSuccessToast = false,\n showErrorToast = true,\n redirectOnAuthError = true,\n preventRedirect = false,\n redirectDelay = 0,\n } = options;\n \n const hasSucceeded = !!result?.data;\n const hasErrored = !!result?.serverError || !!result?.validationErrors || !!result?.fetchError;\n \n const reset = useCallback(() => {\n setResult(undefined);\n }, []);\n \n const execute = useCallback(\n async (input?: TInput): Promise<ActionResult<TOutput>> => {\n setIsExecuting(true);\n \n try {\n const response = await (input === undefined \n ? (action as () => Promise<ServerActionResponse<TOutput>>)()\n : (action as (input: TInput) => Promise<ServerActionResponse<TOutput>>)(input));\n \n let actionResult: ActionResult<TOutput>;\n \n if (response.success) {\n actionResult = { data: response.data };\n \n if (process.env.NODE_ENV === 'development') {\n console.log('[useServerAction] Success response:', response);\n console.log('[useServerAction] Has redirect?', !!response.redirect);\n }\n \n if (showSuccessToast && successMessage) {\n const message = typeof successMessage === \"function\" \n ? successMessage(response.data) \n : successMessage;\n toast.success(message);\n }\n \n await onSuccess?.(response.data);\n \n // Handle redirect if present in response\n if (response.redirect && !preventRedirect) {\n const redirectConfig = typeof response.redirect === 'string'\n ? { url: response.redirect }\n : response.redirect;\n \n const delay = redirectConfig.delay ?? redirectDelay;\n \n setTimeout(() => {\n if (redirectConfig.replace) {\n router.replace(redirectConfig.url);\n } else {\n router.push(redirectConfig.url);\n }\n }, delay);\n }\n \n } else if (!response.success && response.error) {\n actionResult = {\n serverError: response.error,\n validationErrors: response.error.fields,\n };\n \n // Debug logging\n if (process.env.NODE_ENV === 'development') {\n console.log('[useServerAction] Error response:', response.error);\n console.log('[useServerAction] Should redirect?', response.error.shouldRedirect);\n console.log('[useServerAction] Redirect to:', response.error.redirectTo);\n }\n \n if (redirectOnAuthError && response.error.shouldRedirect) {\n // Handle toast before redirect\n if (showErrorToast) {\n const message = errorMessage \n ? (typeof errorMessage === \"function\" \n ? errorMessage(actionResult) \n : errorMessage)\n : response.error.message || \"An error occurred\";\n \n // Always save to localStorage for redirect cases\n // This ensures compatibility with both current and future sonner versions\n if (typeof window !== 'undefined') {\n try {\n const storageKey = 'sonner-toasts';\n const existingToasts = JSON.parse(\n localStorage.getItem(storageKey) || '[]'\n );\n \n // Add our toast in the same format as the PR\n const persistentToast = {\n id: Date.now(),\n type: 'error',\n message,\n persistent: true,\n createdAt: Date.now()\n };\n \n existingToasts.push(persistentToast);\n localStorage.setItem(storageKey, JSON.stringify(existingToasts));\n } catch (err) {\n console.error('Failed to persist toast:', err);\n }\n }\n \n // Always show the toast (with persistent if supported)\n (toast.error as any)(message, { persistent: true });\n }\n \n router.push(response.error.redirectTo || \"/login\");\n return actionResult;\n }\n \n if (showErrorToast) {\n const message = errorMessage \n ? (typeof errorMessage === \"function\" \n ? errorMessage(actionResult) \n : errorMessage)\n : response.error.message || \"An error occurred\";\n toast.error(message);\n }\n \n await onError?.(actionResult);\n } else {\n actionResult = { fetchError: \"Unexpected response format\" };\n }\n \n setResult(actionResult);\n return actionResult;\n \n } catch (error) {\n // Check if this is a Next.js navigation error that should be re-thrown\n if (isNextNavigationError(error)) {\n // Re-throw navigation errors to let Next.js handle them\n throw error;\n }\n \n const fetchError = error instanceof Error ? error.message : \"Network error\";\n const actionResult: ActionResult<TOutput> = { fetchError };\n \n setResult(actionResult);\n \n if (showErrorToast) {\n toast.error(fetchError);\n }\n \n await onError?.(actionResult);\n return actionResult;\n } finally {\n setIsExecuting(false);\n }\n },\n [action, router, showSuccessToast, successMessage, showErrorToast, errorMessage, redirectOnAuthError, onSuccess, onError]\n );\n \n const executeWithTransition = useCallback(\n (...args: TInput extends void ? [] : [input: TInput]): Promise<ActionResult<TOutput>> => {\n return new Promise((resolve) => {\n startTransition(async () => {\n const result = await execute(args[0] as TInput);\n resolve(result);\n });\n });\n },\n [execute]\n ) as TInput extends void \n ? () => Promise<ActionResult<TOutput>>\n : (input: TInput) => Promise<ActionResult<TOutput>>;\n \n return {\n execute: executeWithTransition,\n result,\n isExecuting,\n hasSucceeded,\n hasErrored,\n reset,\n };\n}\n\n","// Comes from: https://github.com/vercel/next.js/blob/canary/packages/next/src/client/components/redirect-error.ts\n\nenum RedirectStatusCode {\n\tSeeOther = 303,\n\tTemporaryRedirect = 307,\n\tPermanentRedirect = 308,\n}\n\nconst REDIRECT_ERROR_CODE = \"NEXT_REDIRECT\";\n\nenum RedirectType {\n\tpush = \"push\",\n\treplace = \"replace\",\n}\n\nexport type RedirectError = Error & {\n\tdigest: `${typeof REDIRECT_ERROR_CODE};${RedirectType};${string};${RedirectStatusCode};`;\n};\n\n/**\n * Checks an error to determine if it's an error generated by the\n * `redirect(url)` helper.\n *\n * @param error the error that may reference a redirect error\n * @returns true if the error is a redirect error\n */\nexport function isRedirectError(error: unknown): error is RedirectError {\n\tif (typeof error !== \"object\" || error === null || !(\"digest\" in error) || typeof error.digest !== \"string\") {\n\t\treturn false;\n\t}\n\n\tconst digest = error.digest.split(\";\");\n\tconst [errorCode, type] = digest;\n\tconst destination = digest.slice(2, -2).join(\";\");\n\tconst status = digest.at(-2);\n\n\tconst statusCode = Number(status);\n\n\treturn (\n\t\terrorCode === REDIRECT_ERROR_CODE &&\n\t\t(type === \"replace\" || type === \"push\") &&\n\t\ttypeof destination === \"string\" &&\n\t\t!isNaN(statusCode) &&\n\t\tstatusCode in RedirectStatusCode\n\t);\n}","// Comes from https://github.com/vercel/next.js/blob/canary/packages/next/src/client/components/http-access-fallback/http-access-fallback.ts\n\nconst HTTPAccessErrorStatus = {\n\tNOT_FOUND: 404,\n\tFORBIDDEN: 403,\n\tUNAUTHORIZED: 401,\n};\n\nconst ALLOWED_CODES = new Set(Object.values(HTTPAccessErrorStatus));\n\nconst HTTP_ERROR_FALLBACK_ERROR_CODE = \"NEXT_HTTP_ERROR_FALLBACK\";\n\nexport type HTTPAccessFallbackError = Error & {\n\tdigest: `${typeof HTTP_ERROR_FALLBACK_ERROR_CODE};${string}`;\n};\n\n/**\n * Checks an error to determine if it's an error generated by\n * the HTTP navigation APIs `notFound()`, `forbidden()` or `unauthorized()`.\n *\n * @param error the error that may reference a HTTP access error\n * @returns true if the error is a HTTP access error\n */\nexport function isHTTPAccessFallbackError(error: unknown): error is HTTPAccessFallbackError {\n\tif (typeof error !== \"object\" || error === null || !(\"digest\" in error) || typeof error.digest !== \"string\") {\n\t\treturn false;\n\t}\n\tconst [prefix, httpStatus] = error.digest.split(\";\");\n\n\treturn prefix === HTTP_ERROR_FALLBACK_ERROR_CODE && ALLOWED_CODES.has(Number(httpStatus));\n}\n\nexport function getAccessFallbackHTTPStatus(error: HTTPAccessFallbackError): number {\n\tconst httpStatus = error.digest.split(\";\")[1];\n\treturn Number(httpStatus);\n}","import { isRedirectError } from \"./redirect\";\nimport { isHTTPAccessFallbackError, getAccessFallbackHTTPStatus } from \"./http-access-fallback\";\n\nexport { isRedirectError } from \"./redirect\";\nexport type { RedirectError } from \"./redirect\";\n\nexport { isHTTPAccessFallbackError, getAccessFallbackHTTPStatus } from \"./http-access-fallback\";\nexport type { HTTPAccessFallbackError } from \"./http-access-fallback\";\n\n/**\n * Checks if the error is a navigation error that should be re-thrown\n * This includes redirect errors and HTTP access errors (notFound, forbidden, unauthorized)\n */\nexport function isNextNavigationError(error: unknown): boolean {\n\treturn isRedirectError(error) || isHTTPAccessFallbackError(error);\n}\n\n/**\n * Checks if the error is a notFound error\n * Note: Next.js implements notFound() using HTTP_ERROR_FALLBACK with status 404,\n * not as a separate error type like NEXT_REDIRECT\n */\nexport function isNotFoundError(error: unknown): boolean {\n\treturn isHTTPAccessFallbackError(error) && getAccessFallbackHTTPStatus(error as any) === 404;\n}","import { useOptimistic, useTransition } from \"react\";\nimport { useServerAction, type UseServerActionOptions } from \"./use-server-action\";\nimport type { ServerAction, ActionResult } from \"../types\";\n\nexport interface UseOptimisticActionOptions<TInput, TOutput, TOptimistic> extends UseServerActionOptions<TOutput> {\n /**\n * Function to update the optimistic state\n */\n updateFn: TInput extends void \n ? (current: TOptimistic) => TOptimistic\n : (current: TOptimistic, input: TInput) => TOptimistic;\n}\n\nexport interface UseOptimisticActionReturn<TInput, TOutput, TOptimistic> {\n /**\n * The optimistic state\n */\n optimisticState: TOptimistic;\n \n /**\n * Execute the action with optimistic update\n */\n execute: TInput extends void \n ? () => Promise<ActionResult<TOutput>>\n : (input: TInput) => Promise<ActionResult<TOutput>>;\n \n /**\n * Loading state\n */\n isExecuting: boolean;\n \n /**\n * Success state\n */\n hasSucceeded: boolean;\n \n /**\n * Error state\n */\n hasErrored: boolean;\n \n /**\n * Last result\n */\n result: ActionResult<TOutput> | undefined;\n \n /**\n * Reset state\n */\n reset: () => void;\n}\n\n/**\n * Hook for server actions with optimistic updates\n * \n * @example\n * ```typescript\n * const { optimisticState, execute } = useOptimisticAction(\n * currentTodos,\n * addTodoAction,\n * {\n * updateFn: (todos, newTodo) => [...todos, newTodo],\n * onSuccess: (savedTodo) => {\n * // Update with server response if needed\n * }\n * }\n * );\n * ```\n */\nexport function useOptimisticAction<TInput, TOutput, TOptimistic>(\n initialState: TOptimistic,\n action: ServerAction<TInput, TOutput>,\n options: UseOptimisticActionOptions<TInput, TOutput, TOptimistic>\n): UseOptimisticActionReturn<TInput, TOutput, TOptimistic> {\n const [optimisticState, addOptimisticUpdate] = useOptimistic(\n initialState,\n options.updateFn\n );\n \n const [, startTransition] = useTransition();\n \n // Extract updateFn from options to pass the rest to useServerAction\n const { updateFn: _, ...serverActionOptions } = options;\n \n const {\n execute: executeAction,\n isExecuting,\n hasSucceeded,\n hasErrored,\n result,\n reset\n } = useServerAction(action, serverActionOptions);\n \n const execute = (async (input?: TInput): Promise<ActionResult<TOutput>> => {\n // Apply optimistic update\n startTransition(() => {\n if (input !== undefined) {\n addOptimisticUpdate(input);\n } else {\n addOptimisticUpdate(undefined as any);\n }\n });\n \n // Execute the server action\n return executeAction(input as any);\n }) as any;\n \n return {\n optimisticState,\n execute,\n isExecuting,\n hasSucceeded,\n hasErrored,\n result,\n reset\n };\n}","import { useActionState, useEffect, useTransition } from \"react\";\nimport type { UseFormProps, UseFormReturn, Path, FieldValues, DefaultValues } from \"react-hook-form\";\nimport { useForm } from \"react-hook-form\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { toast } from \"sonner\";\nimport type { z } from \"zod\";\nimport type { ServerActionResponse, ServerActionError } from \"../types\";\n\nexport interface UseFormActionOptions<TFieldValues extends FieldValues, TOutput> {\n // Required - the server action to execute (form action format)\n action: (prevState: ServerActionResponse<TOutput>, formData: FormData) => Promise<ServerActionResponse<TOutput>>;\n \n // Optional Zod schema for validation\n schema?: z.ZodTypeAny;\n \n // React Hook Form options\n defaultValues?: DefaultValues<TFieldValues>;\n mode?: UseFormProps<TFieldValues>[\"mode\"];\n \n // Transform function if you need custom FormData creation\n transformData?: (data: TFieldValues) => FormData;\n \n // Callbacks\n onSuccess?: (data: TOutput) => void | Promise<void>;\n onError?: (error: ServerActionError) => void;\n \n // Behavior options\n resetOnSuccess?: boolean;\n showSuccessToast?: boolean | string | ((data: TOutput) => string);\n showErrorToast?: boolean | string | ((error: ServerActionError) => string);\n}\n\nexport interface UseFormActionReturn<TFieldValues extends FieldValues, TOutput> {\n // React Hook Form instance\n form: UseFormReturn<TFieldValues>;\n \n // Submit handler (pre-bound with handleSubmit)\n onSubmit: (e?: React.BaseSyntheticEvent) => void;\n \n // Loading state (combines isPending and isTransitioning)\n isSubmitting: boolean;\n \n // The current action state\n actionState: ServerActionResponse<TOutput>;\n \n // Utilities\n reset: () => void;\n \n // Raw handlers if needed\n handleSubmit: (data: TFieldValues) => Promise<void>;\n}\n\nfunction isSuccessResponse<T>(response: ServerActionResponse<T>): response is { success: true; data: T } {\n return response.success === true;\n}\n\nfunction objectToFormData(obj: any): FormData {\n const formData = new FormData();\n \n Object.entries(obj).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n if (value instanceof File || value instanceof Blob) {\n formData.append(key, value);\n } else if (Array.isArray(value)) {\n value.forEach((item) => formData.append(`${key}[]`, String(item)));\n } else if (typeof value === \"object\") {\n formData.append(key, JSON.stringify(value));\n } else {\n formData.append(key, String(value));\n }\n }\n });\n \n return formData;\n}\n\nexport function useFormAction<TFieldValues extends FieldValues = FieldValues, TOutput = void>({\n action,\n schema,\n defaultValues,\n mode = \"onChange\",\n transformData,\n onSuccess,\n onError,\n resetOnSuccess = false,\n showSuccessToast = false,\n showErrorToast = true,\n}: UseFormActionOptions<TFieldValues, TOutput>): UseFormActionReturn<TFieldValues, TOutput> {\n // 1. Setup form with React Hook Form\n const form = useForm<TFieldValues>({\n resolver: schema ? (zodResolver as any)(schema) : undefined,\n defaultValues,\n mode,\n } as UseFormProps<TFieldValues>);\n \n // 2. Setup server action state with useActionState\n const initialState: ServerActionResponse<TOutput> = { success: true, data: undefined as TOutput };\n const [actionState, formAction, isPending] = useActionState(action, initialState);\n const [isTransitioning, startTransition] = useTransition();\n \n // 3. Handle server errors and success\n useEffect(() => {\n if (!isSuccessResponse(actionState) && actionState.error) {\n const { error } = actionState;\n \n // Map field errors to form\n if (error.fields) {\n Object.entries(error.fields).forEach(([field, messages]) => {\n if (Array.isArray(messages) && messages.length > 0) {\n form.setError(field as Path<TFieldValues>, {\n type: \"server\",\n message: messages[0],\n });\n }\n });\n }\n \n // Single field error\n if (error.field && error.message && !error.fields) {\n form.setError(error.field as Path<TFieldValues>, {\n type: \"server\",\n message: error.message,\n });\n }\n \n // Global error (no specific field)\n if (error.message && !error.field && !error.fields) {\n if (showErrorToast) {\n const message = typeof showErrorToast === \"function\" \n ? showErrorToast(error)\n : typeof showErrorToast === \"string\"\n ? showErrorToast\n : error.message;\n toast.error(message);\n }\n \n // Also set on root for inline display\n form.setError(\"root\", {\n type: \"server\",\n message: error.message,\n });\n }\n \n // Call error callback\n onError?.(error);\n }\n \n // Handle success\n if (isSuccessResponse(actionState) && actionState.data !== undefined) {\n if (showSuccessToast) {\n const message = typeof showSuccessToast === \"function\"\n ? showSuccessToast(actionState.data)\n : typeof showSuccessToast === \"string\"\n ? showSuccessToast\n : \"Success!\";\n toast.success(message);\n }\n \n if (resetOnSuccess) {\n form.reset();\n }\n \n // Call success callback\n onSuccess?.(actionState.data);\n }\n }, [actionState, form, onError, onSuccess, resetOnSuccess, showErrorToast, showSuccessToast]);\n \n // 4. Submit handler\n const handleSubmit = async (data: TFieldValues): Promise<void> => {\n // Clear any previous errors\n form.clearErrors();\n \n // Transform to FormData\n const formData = transformData \n ? transformData(data)\n : objectToFormData(data);\n \n // Execute with transition for better UX\n startTransition(() => {\n formAction(formData);\n });\n };\n \n // 5. Combined loading state\n const isSubmitting = isPending || isTransitioning;\n \n // 6. Pre-bound submit handler\n const onSubmit = (e?: React.BaseSyntheticEvent): void => {\n e?.preventDefault();\n void form.handleSubmit(handleSubmit)(e!);\n };\n \n return {\n form,\n onSubmit,\n isSubmitting,\n actionState,\n reset: form.reset,\n handleSubmit,\n };\n}","\"use client\";\n\nimport { useEffect } from \"react\";\nimport { toast } from \"sonner\";\n\nexport function ToastRestorer() {\n useEffect(() => {\n // Only run on client side\n if (typeof window === \"undefined\") return;\n \n const storageKey = \"sonner-toasts\";\n \n try {\n const storedToasts = localStorage.getItem(storageKey);\n \n if (storedToasts) {\n const persistentToasts = JSON.parse(storedToasts);\n \n if (Array.isArray(persistentToasts) && persistentToasts.length > 0) {\n // Clear the storage immediately to prevent duplicate toasts\n localStorage.removeItem(storageKey);\n \n // Filter out old toasts (older than 30 seconds)\n const recentToasts = persistentToasts.filter(\n (t: any) => Date.now() - (t.createdAt || 0) < 30000\n );\n \n // Restore toasts with staggered animation like in the PR\n recentToasts.forEach((persistedToast: any, index: number) => {\n // Skip loading toasts as per the PR implementation\n if (persistedToast.type === \"loading\") return;\n \n setTimeout(() => {\n const toastFunction = toast[persistedToast.type as keyof typeof toast];\n \n if (typeof toastFunction === \"function\") {\n // Check if current sonner supports persistent\n if (toastFunction.length >= 2) {\n // Use persistent if available\n (toastFunction as any)(persistedToast.message, { \n persistent: true,\n id: persistedToast.id \n });\n } else {\n // Fallback to normal toast\n toastFunction(persistedToast.message);\n }\n }\n }, index * 150); // Staggered delay like in the PR\n });\n }\n }\n } catch (error) {\n console.error(\"Failed to restore toasts:\", error);\n // Clean up on error\n localStorage.removeItem(storageKey);\n }\n }, []);\n \n return null;\n}"],"mappings":"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","names":["RedirectStatusCode","result","import_react","import_react","import_sonner","import_react","import_sonner"]}
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/index.ts","../../src/hooks/use-server-action.ts","../../src/next/errors/redirect.ts","../../src/next/errors/http-access-fallback.ts","../../src/next/errors/index.ts","../../src/hooks/use-optimistic-action.ts","../../src/hooks/use-form-action.ts","../../src/hooks/toast-restorer.tsx"],"sourcesContent":["export * from \"./use-server-action\";\nexport * from \"./use-optimistic-action\";\nexport * from \"./use-form-action\";\nexport { ToastRestorer } from \"./toast-restorer\";","import { useCallback, useState, useTransition } from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport { toast } from \"sonner\";\nimport type { ServerAction, ActionResult, ServerActionResponse } from \"../types\";\nimport { isNextNavigationError } from \"../next/errors\";\n\nexport interface UseServerActionOptions<TOutput> {\n onSuccess?: (data: TOutput) => void | Promise<void>;\n onError?: (error: ActionResult<TOutput>) => void | Promise<void>;\n \n successMessage?: string | ((data: TOutput) => string);\n errorMessage?: string | ((error: ActionResult<TOutput>) => string);\n showSuccessToast?: boolean;\n showErrorToast?: boolean;\n \n redirectOnAuthError?: boolean;\n preventRedirect?: boolean;\n redirectDelay?: number;\n}\n\nexport interface UseServerActionReturn<TInput, TOutput> {\n execute: TInput extends void \n ? () => Promise<ActionResult<TOutput>>\n : (input: TInput) => Promise<ActionResult<TOutput>>;\n \n result: ActionResult<TOutput> | undefined;\n isExecuting: boolean;\n hasSucceeded: boolean;\n hasErrored: boolean;\n \n reset: () => void;\n}\n\n// Overload for void actions\nexport function useServerAction<TOutput>(\n action: ServerAction<void, TOutput>,\n options?: UseServerActionOptions<TOutput>\n): UseServerActionReturn<void, TOutput>;\n\n// Overload for actions with input\nexport function useServerAction<TInput, TOutput>(\n action: ServerAction<TInput, TOutput>,\n options?: UseServerActionOptions<TOutput>\n): UseServerActionReturn<TInput, TOutput>;\n\n// Implementation\nexport function useServerAction<TInput, TOutput>(\n action: ServerAction<TInput, TOutput>,\n options: UseServerActionOptions<TOutput> = {}\n): UseServerActionReturn<TInput, TOutput> {\n const router = useRouter();\n const [result, setResult] = useState<ActionResult<TOutput>>();\n const [isExecuting, setIsExecuting] = useState(false);\n const [, startTransition] = useTransition();\n \n const {\n onSuccess,\n onError,\n successMessage,\n errorMessage,\n showSuccessToast = false,\n showErrorToast = true,\n redirectOnAuthError = true,\n preventRedirect = false,\n redirectDelay = 0,\n } = options;\n \n const hasSucceeded = !!result?.data;\n const hasErrored = !!result?.serverError || !!result?.validationErrors || !!result?.fetchError;\n \n const reset = useCallback(() => {\n setResult(undefined);\n }, []);\n \n const execute = useCallback(\n async (input?: TInput): Promise<ActionResult<TOutput>> => {\n setIsExecuting(true);\n \n try {\n const response = await (input === undefined \n ? (action as () => Promise<ServerActionResponse<TOutput>>)()\n : (action as (input: TInput) => Promise<ServerActionResponse<TOutput>>)(input));\n \n let actionResult: ActionResult<TOutput>;\n \n if (response.success) {\n actionResult = { data: response.data };\n \n if (process.env.NODE_ENV === 'development') {\n console.log('[useServerAction] Success response:', response);\n console.log('[useServerAction] Has redirect?', !!response.redirect);\n }\n \n if (showSuccessToast && successMessage) {\n const message = typeof successMessage === \"function\" \n ? successMessage(response.data) \n : successMessage;\n toast.success(message);\n }\n \n await onSuccess?.(response.data);\n \n // Handle redirect if present in response\n if (response.redirect && !preventRedirect) {\n const redirectConfig = typeof response.redirect === 'string'\n ? { url: response.redirect }\n : response.redirect;\n \n const delay = redirectConfig.delay ?? redirectDelay;\n \n setTimeout(() => {\n if (redirectConfig.replace) {\n router.replace(redirectConfig.url);\n } else {\n router.push(redirectConfig.url);\n }\n }, delay);\n }\n \n } else if (!response.success && response.error) {\n actionResult = {\n serverError: response.error,\n validationErrors: response.error.fields,\n };\n \n // Debug logging\n if (process.env.NODE_ENV === 'development') {\n console.log('[useServerAction] Error response:', response.error);\n console.log('[useServerAction] Should redirect?', response.error.shouldRedirect);\n console.log('[useServerAction] Redirect to:', response.error.redirectTo);\n }\n \n if (redirectOnAuthError && response.error.shouldRedirect) {\n // Handle toast before redirect\n if (showErrorToast) {\n const message = errorMessage \n ? (typeof errorMessage === \"function\" \n ? errorMessage(actionResult) \n : errorMessage)\n : response.error.message || \"An error occurred\";\n \n // Always save to localStorage for redirect cases\n // This ensures compatibility with both current and future sonner versions\n if (typeof window !== 'undefined') {\n try {\n const storageKey = 'sonner-toasts';\n const existingToasts = JSON.parse(\n localStorage.getItem(storageKey) || '[]'\n );\n \n // Add our toast in the same format as the PR\n const persistentToast = {\n id: Date.now(),\n type: 'error',\n message,\n persistent: true,\n createdAt: Date.now()\n };\n \n existingToasts.push(persistentToast);\n localStorage.setItem(storageKey, JSON.stringify(existingToasts));\n } catch (err) {\n console.error('Failed to persist toast:', err);\n }\n }\n \n // Always show the toast (with persistent if supported)\n (toast.error as any)(message, { persistent: true });\n }\n \n router.push(response.error.redirectTo || \"/login\");\n return actionResult;\n }\n \n if (showErrorToast) {\n const message = errorMessage \n ? (typeof errorMessage === \"function\" \n ? errorMessage(actionResult) \n : errorMessage)\n : response.error.message || \"An error occurred\";\n toast.error(message);\n }\n \n await onError?.(actionResult);\n } else {\n actionResult = { fetchError: \"Unexpected response format\" };\n }\n \n setResult(actionResult);\n return actionResult;\n \n } catch (error) {\n // Check if this is a Next.js navigation error that should be re-thrown\n if (isNextNavigationError(error)) {\n // Re-throw navigation errors to let Next.js handle them\n throw error;\n }\n \n const fetchError = error instanceof Error ? error.message : \"Network error\";\n const actionResult: ActionResult<TOutput> = { fetchError };\n \n setResult(actionResult);\n \n if (showErrorToast) {\n toast.error(fetchError);\n }\n \n await onError?.(actionResult);\n return actionResult;\n } finally {\n setIsExecuting(false);\n }\n },\n [action, router, showSuccessToast, successMessage, showErrorToast, errorMessage, redirectOnAuthError, onSuccess, onError]\n );\n \n const executeWithTransition = useCallback(\n (...args: TInput extends void ? [] : [input: TInput]): Promise<ActionResult<TOutput>> => {\n return new Promise((resolve) => {\n startTransition(async () => {\n const result = await execute(args[0] as TInput);\n resolve(result);\n });\n });\n },\n [execute]\n ) as TInput extends void \n ? () => Promise<ActionResult<TOutput>>\n : (input: TInput) => Promise<ActionResult<TOutput>>;\n \n return {\n execute: executeWithTransition,\n result,\n isExecuting,\n hasSucceeded,\n hasErrored,\n reset,\n };\n}\n\n","// Comes from: https://github.com/vercel/next.js/blob/canary/packages/next/src/client/components/redirect-error.ts\n\nenum RedirectStatusCode {\n\tSeeOther = 303,\n\tTemporaryRedirect = 307,\n\tPermanentRedirect = 308,\n}\n\nconst REDIRECT_ERROR_CODE = \"NEXT_REDIRECT\";\n\nenum RedirectType {\n\tpush = \"push\",\n\treplace = \"replace\",\n}\n\nexport type RedirectError = Error & {\n\tdigest: `${typeof REDIRECT_ERROR_CODE};${RedirectType};${string};${RedirectStatusCode};`;\n};\n\n/**\n * Checks an error to determine if it's an error generated by the\n * `redirect(url)` helper.\n *\n * @param error the error that may reference a redirect error\n * @returns true if the error is a redirect error\n */\nexport function isRedirectError(error: unknown): error is RedirectError {\n\tif (typeof error !== \"object\" || error === null || !(\"digest\" in error) || typeof error.digest !== \"string\") {\n\t\treturn false;\n\t}\n\n\tconst digest = error.digest.split(\";\");\n\tconst [errorCode, type] = digest;\n\tconst destination = digest.slice(2, -2).join(\";\");\n\tconst status = digest.at(-2);\n\n\tconst statusCode = Number(status);\n\n\treturn (\n\t\terrorCode === REDIRECT_ERROR_CODE &&\n\t\t(type === \"replace\" || type === \"push\") &&\n\t\ttypeof destination === \"string\" &&\n\t\t!isNaN(statusCode) &&\n\t\tstatusCode in RedirectStatusCode\n\t);\n}","// Comes from https://github.com/vercel/next.js/blob/canary/packages/next/src/client/components/http-access-fallback/http-access-fallback.ts\n\nconst HTTPAccessErrorStatus = {\n\tNOT_FOUND: 404,\n\tFORBIDDEN: 403,\n\tUNAUTHORIZED: 401,\n};\n\nconst ALLOWED_CODES = new Set(Object.values(HTTPAccessErrorStatus));\n\nconst HTTP_ERROR_FALLBACK_ERROR_CODE = \"NEXT_HTTP_ERROR_FALLBACK\";\n\nexport type HTTPAccessFallbackError = Error & {\n\tdigest: `${typeof HTTP_ERROR_FALLBACK_ERROR_CODE};${string}`;\n};\n\n/**\n * Checks an error to determine if it's an error generated by\n * the HTTP navigation APIs `notFound()`, `forbidden()` or `unauthorized()`.\n *\n * @param error the error that may reference a HTTP access error\n * @returns true if the error is a HTTP access error\n */\nexport function isHTTPAccessFallbackError(error: unknown): error is HTTPAccessFallbackError {\n\tif (typeof error !== \"object\" || error === null || !(\"digest\" in error) || typeof error.digest !== \"string\") {\n\t\treturn false;\n\t}\n\tconst [prefix, httpStatus] = error.digest.split(\";\");\n\n\treturn prefix === HTTP_ERROR_FALLBACK_ERROR_CODE && ALLOWED_CODES.has(Number(httpStatus));\n}\n\nexport function getAccessFallbackHTTPStatus(error: HTTPAccessFallbackError): number {\n\tconst httpStatus = error.digest.split(\";\")[1];\n\treturn Number(httpStatus);\n}","import { isRedirectError } from \"./redirect\";\nimport { isHTTPAccessFallbackError, getAccessFallbackHTTPStatus } from \"./http-access-fallback\";\n\nexport { isRedirectError } from \"./redirect\";\nexport type { RedirectError } from \"./redirect\";\n\nexport { isHTTPAccessFallbackError, getAccessFallbackHTTPStatus } from \"./http-access-fallback\";\nexport type { HTTPAccessFallbackError } from \"./http-access-fallback\";\n\n/**\n * Checks if the error is a navigation error that should be re-thrown\n * This includes redirect errors and HTTP access errors (notFound, forbidden, unauthorized)\n */\nexport function isNextNavigationError(error: unknown): boolean {\n\treturn isRedirectError(error) || isHTTPAccessFallbackError(error);\n}\n\n/**\n * Checks if the error is a notFound error\n * Note: Next.js implements notFound() using HTTP_ERROR_FALLBACK with status 404,\n * not as a separate error type like NEXT_REDIRECT\n */\nexport function isNotFoundError(error: unknown): boolean {\n\treturn isHTTPAccessFallbackError(error) && getAccessFallbackHTTPStatus(error as any) === 404;\n}","import { useOptimistic, useTransition } from \"react\";\nimport { useServerAction, type UseServerActionOptions } from \"./use-server-action\";\nimport type { ServerAction, ActionResult } from \"../types\";\n\nexport interface UseOptimisticActionOptions<TInput, TOutput, TOptimistic> extends UseServerActionOptions<TOutput> {\n /**\n * Function to update the optimistic state\n */\n updateFn: TInput extends void \n ? (current: TOptimistic) => TOptimistic\n : (current: TOptimistic, input: TInput) => TOptimistic;\n}\n\nexport interface UseOptimisticActionReturn<TInput, TOutput, TOptimistic> {\n /**\n * The optimistic state\n */\n optimisticState: TOptimistic;\n \n /**\n * Execute the action with optimistic update\n */\n execute: TInput extends void \n ? () => Promise<ActionResult<TOutput>>\n : (input: TInput) => Promise<ActionResult<TOutput>>;\n \n /**\n * Loading state\n */\n isExecuting: boolean;\n \n /**\n * Success state\n */\n hasSucceeded: boolean;\n \n /**\n * Error state\n */\n hasErrored: boolean;\n \n /**\n * Last result\n */\n result: ActionResult<TOutput> | undefined;\n \n /**\n * Reset state\n */\n reset: () => void;\n}\n\n/**\n * Hook for server actions with optimistic updates\n * \n * @example\n * ```typescript\n * const { optimisticState, execute } = useOptimisticAction(\n * currentTodos,\n * addTodoAction,\n * {\n * updateFn: (todos, newTodo) => [...todos, newTodo],\n * onSuccess: (savedTodo) => {\n * // Update with server response if needed\n * }\n * }\n * );\n * ```\n */\nexport function useOptimisticAction<TInput, TOutput, TOptimistic>(\n initialState: TOptimistic,\n action: ServerAction<TInput, TOutput>,\n options: UseOptimisticActionOptions<TInput, TOutput, TOptimistic>\n): UseOptimisticActionReturn<TInput, TOutput, TOptimistic> {\n const [optimisticState, addOptimisticUpdate] = useOptimistic(\n initialState,\n options.updateFn\n );\n \n const [, startTransition] = useTransition();\n \n // Extract updateFn from options to pass the rest to useServerAction\n const { updateFn: _, ...serverActionOptions } = options;\n \n const {\n execute: executeAction,\n isExecuting,\n hasSucceeded,\n hasErrored,\n result,\n reset\n } = useServerAction(action, serverActionOptions);\n \n const execute = (async (input?: TInput): Promise<ActionResult<TOutput>> => {\n // Apply optimistic update\n startTransition(() => {\n if (input !== undefined) {\n addOptimisticUpdate(input);\n } else {\n addOptimisticUpdate(undefined as any);\n }\n });\n \n // Execute the server action\n return executeAction(input as any);\n }) as any;\n \n return {\n optimisticState,\n execute,\n isExecuting,\n hasSucceeded,\n hasErrored,\n result,\n reset\n };\n}","import { useActionState, useEffect, useTransition } from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport type { UseFormProps, UseFormReturn, Path, FieldValues, DefaultValues } from \"react-hook-form\";\nimport { useForm } from \"react-hook-form\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { toast } from \"sonner\";\nimport type { z } from \"zod\";\nimport type { ServerActionResponse, ServerActionError, RedirectConfig } from \"../types\";\n\nexport interface UseFormActionOptions<TFieldValues extends FieldValues, TOutput> {\n // Required - the server action to execute (form action format)\n action: (prevState: ServerActionResponse<TOutput>, formData: FormData) => Promise<ServerActionResponse<TOutput>>;\n \n // Optional Zod schema for validation\n schema?: z.ZodTypeAny;\n \n // React Hook Form options\n defaultValues?: DefaultValues<TFieldValues>;\n mode?: UseFormProps<TFieldValues>[\"mode\"];\n \n // Transform function if you need custom FormData creation\n transformData?: (data: TFieldValues) => FormData;\n \n // Callbacks\n onSuccess?: (data: TOutput) => void | Promise<void>;\n onError?: (error: ServerActionError) => void;\n \n // Behavior options\n resetOnSuccess?: boolean;\n showSuccessToast?: boolean | string | ((data: TOutput) => string);\n showErrorToast?: boolean | string | ((error: ServerActionError) => string);\n \n // Redirect options (same as useServerAction)\n preventRedirect?: boolean;\n redirectDelay?: number;\n}\n\nexport interface UseFormActionReturn<TFieldValues extends FieldValues, TOutput> {\n // React Hook Form instance\n form: UseFormReturn<TFieldValues>;\n \n // Submit handler (pre-bound with handleSubmit)\n onSubmit: (e?: React.BaseSyntheticEvent) => void;\n \n // Loading state (combines isPending and isTransitioning)\n isSubmitting: boolean;\n \n // The current action state\n actionState: ServerActionResponse<TOutput>;\n \n // Utilities\n reset: () => void;\n \n // Raw handlers if needed\n handleSubmit: (data: TFieldValues) => Promise<void>;\n}\n\nfunction isSuccessResponse<T>(response: ServerActionResponse<T>): response is { success: true; data: T; redirect?: string | RedirectConfig } {\n return response.success === true;\n}\n\nfunction objectToFormData(obj: any): FormData {\n const formData = new FormData();\n \n Object.entries(obj).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n if (value instanceof File || value instanceof Blob) {\n formData.append(key, value);\n } else if (Array.isArray(value)) {\n value.forEach((item) => formData.append(`${key}[]`, String(item)));\n } else if (typeof value === \"object\") {\n formData.append(key, JSON.stringify(value));\n } else {\n formData.append(key, String(value));\n }\n }\n });\n \n return formData;\n}\n\nexport function useFormAction<TFieldValues extends FieldValues = FieldValues, TOutput = void>({\n action,\n schema,\n defaultValues,\n mode = \"onChange\",\n transformData,\n onSuccess,\n onError,\n resetOnSuccess = false,\n showSuccessToast = false,\n showErrorToast = true,\n preventRedirect = false,\n redirectDelay = 0,\n}: UseFormActionOptions<TFieldValues, TOutput>): UseFormActionReturn<TFieldValues, TOutput> {\n const router = useRouter();\n // 1. Setup form with React Hook Form\n const form = useForm<TFieldValues>({\n resolver: schema ? (zodResolver as any)(schema) : undefined,\n defaultValues,\n mode,\n } as UseFormProps<TFieldValues>);\n \n // 2. Setup server action state with useActionState\n const initialState: ServerActionResponse<TOutput> = { success: true, data: undefined as TOutput };\n const [actionState, formAction, isPending] = useActionState(action, initialState);\n const [isTransitioning, startTransition] = useTransition();\n \n // 3. Handle server errors and success\n useEffect(() => {\n if (!isSuccessResponse(actionState) && actionState.error) {\n const { error } = actionState;\n \n // Map field errors to form\n if (error.fields) {\n Object.entries(error.fields).forEach(([field, messages]) => {\n if (Array.isArray(messages) && messages.length > 0) {\n form.setError(field as Path<TFieldValues>, {\n type: \"server\",\n message: messages[0],\n });\n }\n });\n }\n \n // Single field error\n if (error.field && error.message && !error.fields) {\n form.setError(error.field as Path<TFieldValues>, {\n type: \"server\",\n message: error.message,\n });\n }\n \n // Global error (no specific field)\n if (error.message && !error.field && !error.fields) {\n if (showErrorToast) {\n const message = typeof showErrorToast === \"function\" \n ? showErrorToast(error)\n : typeof showErrorToast === \"string\"\n ? showErrorToast\n : error.message;\n toast.error(message);\n }\n \n // Also set on root for inline display\n form.setError(\"root\", {\n type: \"server\",\n message: error.message,\n });\n }\n \n // Call error callback\n onError?.(error);\n }\n \n // Handle success\n if (isSuccessResponse(actionState) && actionState.data !== undefined) {\n if (showSuccessToast) {\n const message = typeof showSuccessToast === \"function\"\n ? showSuccessToast(actionState.data)\n : typeof showSuccessToast === \"string\"\n ? showSuccessToast\n : \"Success!\";\n toast.success(message);\n }\n \n if (resetOnSuccess) {\n form.reset();\n }\n \n // Call success callback\n onSuccess?.(actionState.data);\n \n // Handle redirect if present in response\n if (actionState.redirect && !preventRedirect) {\n const redirectConfig = typeof actionState.redirect === 'string'\n ? { url: actionState.redirect }\n : actionState.redirect;\n \n const delay = redirectConfig.delay ?? redirectDelay;\n \n setTimeout(() => {\n if (redirectConfig.replace) {\n router.replace(redirectConfig.url);\n } else {\n router.push(redirectConfig.url);\n }\n }, delay);\n }\n }\n }, [actionState, form, onError, onSuccess, resetOnSuccess, showErrorToast, showSuccessToast, preventRedirect, redirectDelay, router]);\n \n // 4. Submit handler\n const handleSubmit = async (data: TFieldValues): Promise<void> => {\n // Clear any previous errors\n form.clearErrors();\n \n // Transform to FormData\n const formData = transformData \n ? transformData(data)\n : objectToFormData(data);\n \n // Execute with transition for better UX\n startTransition(() => {\n formAction(formData);\n });\n };\n \n // 5. Combined loading state\n const isSubmitting = isPending || isTransitioning;\n \n // 6. Pre-bound submit handler\n const onSubmit = (e?: React.BaseSyntheticEvent): void => {\n e?.preventDefault();\n void form.handleSubmit(handleSubmit)(e!);\n };\n \n return {\n form,\n onSubmit,\n isSubmitting,\n actionState,\n reset: form.reset,\n handleSubmit,\n };\n}","\"use client\";\n\nimport { useEffect } from \"react\";\nimport { toast } from \"sonner\";\n\nexport function ToastRestorer() {\n useEffect(() => {\n // Only run on client side\n if (typeof window === \"undefined\") return;\n \n const storageKey = \"sonner-toasts\";\n \n try {\n const storedToasts = localStorage.getItem(storageKey);\n \n if (storedToasts) {\n const persistentToasts = JSON.parse(storedToasts);\n \n if (Array.isArray(persistentToasts) && persistentToasts.length > 0) {\n // Clear the storage immediately to prevent duplicate toasts\n localStorage.removeItem(storageKey);\n \n // Filter out old toasts (older than 30 seconds)\n const recentToasts = persistentToasts.filter(\n (t: any) => Date.now() - (t.createdAt || 0) < 30000\n );\n \n // Restore toasts with staggered animation like in the PR\n recentToasts.forEach((persistedToast: any, index: number) => {\n // Skip loading toasts as per the PR implementation\n if (persistedToast.type === \"loading\") return;\n \n setTimeout(() => {\n const toastFunction = toast[persistedToast.type as keyof typeof toast];\n \n if (typeof toastFunction === \"function\") {\n // Check if current sonner supports persistent\n if (toastFunction.length >= 2) {\n // Use persistent if available\n (toastFunction as any)(persistedToast.message, { \n persistent: true,\n id: persistedToast.id \n });\n } else {\n // Fallback to normal toast\n toastFunction(persistedToast.message);\n }\n }\n }, index * 150); // Staggered delay like in the PR\n });\n }\n }\n } catch (error) {\n console.error(\"Failed to restore toasts:\", error);\n // Clean up on error\n localStorage.removeItem(storageKey);\n }\n }, []);\n \n return null;\n}"],"mappings":"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","names":["RedirectStatusCode","result","import_react","import_react","import_navigation","import_sonner","import_react","import_sonner"]}
|
package/dist/hooks/index.mjs
CHANGED
|
@@ -221,6 +221,7 @@ function useOptimisticAction(initialState, action, options) {
|
|
|
221
221
|
|
|
222
222
|
// src/hooks/use-form-action.ts
|
|
223
223
|
import { useActionState, useEffect, useTransition as useTransition3 } from "react";
|
|
224
|
+
import { useRouter as useRouter2 } from "next/navigation";
|
|
224
225
|
import { useForm } from "react-hook-form";
|
|
225
226
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
226
227
|
import { toast as toast2 } from "sonner";
|
|
@@ -254,8 +255,11 @@ function useFormAction({
|
|
|
254
255
|
onError,
|
|
255
256
|
resetOnSuccess = false,
|
|
256
257
|
showSuccessToast = false,
|
|
257
|
-
showErrorToast = true
|
|
258
|
+
showErrorToast = true,
|
|
259
|
+
preventRedirect = false,
|
|
260
|
+
redirectDelay = 0
|
|
258
261
|
}) {
|
|
262
|
+
const router = useRouter2();
|
|
259
263
|
const form = useForm({
|
|
260
264
|
resolver: schema ? zodResolver(schema) : void 0,
|
|
261
265
|
defaultValues,
|
|
@@ -304,8 +308,19 @@ function useFormAction({
|
|
|
304
308
|
form.reset();
|
|
305
309
|
}
|
|
306
310
|
onSuccess?.(actionState.data);
|
|
311
|
+
if (actionState.redirect && !preventRedirect) {
|
|
312
|
+
const redirectConfig = typeof actionState.redirect === "string" ? { url: actionState.redirect } : actionState.redirect;
|
|
313
|
+
const delay = redirectConfig.delay ?? redirectDelay;
|
|
314
|
+
setTimeout(() => {
|
|
315
|
+
if (redirectConfig.replace) {
|
|
316
|
+
router.replace(redirectConfig.url);
|
|
317
|
+
} else {
|
|
318
|
+
router.push(redirectConfig.url);
|
|
319
|
+
}
|
|
320
|
+
}, delay);
|
|
321
|
+
}
|
|
307
322
|
}
|
|
308
|
-
}, [actionState, form, onError, onSuccess, resetOnSuccess, showErrorToast, showSuccessToast]);
|
|
323
|
+
}, [actionState, form, onError, onSuccess, resetOnSuccess, showErrorToast, showSuccessToast, preventRedirect, redirectDelay, router]);
|
|
309
324
|
const handleSubmit = async (data) => {
|
|
310
325
|
form.clearErrors();
|
|
311
326
|
const formData = transformData ? transformData(data) : objectToFormData(data);
|
package/dist/hooks/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/hooks/use-server-action.ts","../../src/next/errors/redirect.ts","../../src/next/errors/http-access-fallback.ts","../../src/next/errors/index.ts","../../src/hooks/use-optimistic-action.ts","../../src/hooks/use-form-action.ts","../../src/hooks/toast-restorer.tsx"],"sourcesContent":["import { useCallback, useState, useTransition } from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport { toast } from \"sonner\";\nimport type { ServerAction, ActionResult, ServerActionResponse } from \"../types\";\nimport { isNextNavigationError } from \"../next/errors\";\n\nexport interface UseServerActionOptions<TOutput> {\n onSuccess?: (data: TOutput) => void | Promise<void>;\n onError?: (error: ActionResult<TOutput>) => void | Promise<void>;\n \n successMessage?: string | ((data: TOutput) => string);\n errorMessage?: string | ((error: ActionResult<TOutput>) => string);\n showSuccessToast?: boolean;\n showErrorToast?: boolean;\n \n redirectOnAuthError?: boolean;\n preventRedirect?: boolean;\n redirectDelay?: number;\n}\n\nexport interface UseServerActionReturn<TInput, TOutput> {\n execute: TInput extends void \n ? () => Promise<ActionResult<TOutput>>\n : (input: TInput) => Promise<ActionResult<TOutput>>;\n \n result: ActionResult<TOutput> | undefined;\n isExecuting: boolean;\n hasSucceeded: boolean;\n hasErrored: boolean;\n \n reset: () => void;\n}\n\n// Overload for void actions\nexport function useServerAction<TOutput>(\n action: ServerAction<void, TOutput>,\n options?: UseServerActionOptions<TOutput>\n): UseServerActionReturn<void, TOutput>;\n\n// Overload for actions with input\nexport function useServerAction<TInput, TOutput>(\n action: ServerAction<TInput, TOutput>,\n options?: UseServerActionOptions<TOutput>\n): UseServerActionReturn<TInput, TOutput>;\n\n// Implementation\nexport function useServerAction<TInput, TOutput>(\n action: ServerAction<TInput, TOutput>,\n options: UseServerActionOptions<TOutput> = {}\n): UseServerActionReturn<TInput, TOutput> {\n const router = useRouter();\n const [result, setResult] = useState<ActionResult<TOutput>>();\n const [isExecuting, setIsExecuting] = useState(false);\n const [, startTransition] = useTransition();\n \n const {\n onSuccess,\n onError,\n successMessage,\n errorMessage,\n showSuccessToast = false,\n showErrorToast = true,\n redirectOnAuthError = true,\n preventRedirect = false,\n redirectDelay = 0,\n } = options;\n \n const hasSucceeded = !!result?.data;\n const hasErrored = !!result?.serverError || !!result?.validationErrors || !!result?.fetchError;\n \n const reset = useCallback(() => {\n setResult(undefined);\n }, []);\n \n const execute = useCallback(\n async (input?: TInput): Promise<ActionResult<TOutput>> => {\n setIsExecuting(true);\n \n try {\n const response = await (input === undefined \n ? (action as () => Promise<ServerActionResponse<TOutput>>)()\n : (action as (input: TInput) => Promise<ServerActionResponse<TOutput>>)(input));\n \n let actionResult: ActionResult<TOutput>;\n \n if (response.success) {\n actionResult = { data: response.data };\n \n if (process.env.NODE_ENV === 'development') {\n console.log('[useServerAction] Success response:', response);\n console.log('[useServerAction] Has redirect?', !!response.redirect);\n }\n \n if (showSuccessToast && successMessage) {\n const message = typeof successMessage === \"function\" \n ? successMessage(response.data) \n : successMessage;\n toast.success(message);\n }\n \n await onSuccess?.(response.data);\n \n // Handle redirect if present in response\n if (response.redirect && !preventRedirect) {\n const redirectConfig = typeof response.redirect === 'string'\n ? { url: response.redirect }\n : response.redirect;\n \n const delay = redirectConfig.delay ?? redirectDelay;\n \n setTimeout(() => {\n if (redirectConfig.replace) {\n router.replace(redirectConfig.url);\n } else {\n router.push(redirectConfig.url);\n }\n }, delay);\n }\n \n } else if (!response.success && response.error) {\n actionResult = {\n serverError: response.error,\n validationErrors: response.error.fields,\n };\n \n // Debug logging\n if (process.env.NODE_ENV === 'development') {\n console.log('[useServerAction] Error response:', response.error);\n console.log('[useServerAction] Should redirect?', response.error.shouldRedirect);\n console.log('[useServerAction] Redirect to:', response.error.redirectTo);\n }\n \n if (redirectOnAuthError && response.error.shouldRedirect) {\n // Handle toast before redirect\n if (showErrorToast) {\n const message = errorMessage \n ? (typeof errorMessage === \"function\" \n ? errorMessage(actionResult) \n : errorMessage)\n : response.error.message || \"An error occurred\";\n \n // Always save to localStorage for redirect cases\n // This ensures compatibility with both current and future sonner versions\n if (typeof window !== 'undefined') {\n try {\n const storageKey = 'sonner-toasts';\n const existingToasts = JSON.parse(\n localStorage.getItem(storageKey) || '[]'\n );\n \n // Add our toast in the same format as the PR\n const persistentToast = {\n id: Date.now(),\n type: 'error',\n message,\n persistent: true,\n createdAt: Date.now()\n };\n \n existingToasts.push(persistentToast);\n localStorage.setItem(storageKey, JSON.stringify(existingToasts));\n } catch (err) {\n console.error('Failed to persist toast:', err);\n }\n }\n \n // Always show the toast (with persistent if supported)\n (toast.error as any)(message, { persistent: true });\n }\n \n router.push(response.error.redirectTo || \"/login\");\n return actionResult;\n }\n \n if (showErrorToast) {\n const message = errorMessage \n ? (typeof errorMessage === \"function\" \n ? errorMessage(actionResult) \n : errorMessage)\n : response.error.message || \"An error occurred\";\n toast.error(message);\n }\n \n await onError?.(actionResult);\n } else {\n actionResult = { fetchError: \"Unexpected response format\" };\n }\n \n setResult(actionResult);\n return actionResult;\n \n } catch (error) {\n // Check if this is a Next.js navigation error that should be re-thrown\n if (isNextNavigationError(error)) {\n // Re-throw navigation errors to let Next.js handle them\n throw error;\n }\n \n const fetchError = error instanceof Error ? error.message : \"Network error\";\n const actionResult: ActionResult<TOutput> = { fetchError };\n \n setResult(actionResult);\n \n if (showErrorToast) {\n toast.error(fetchError);\n }\n \n await onError?.(actionResult);\n return actionResult;\n } finally {\n setIsExecuting(false);\n }\n },\n [action, router, showSuccessToast, successMessage, showErrorToast, errorMessage, redirectOnAuthError, onSuccess, onError]\n );\n \n const executeWithTransition = useCallback(\n (...args: TInput extends void ? [] : [input: TInput]): Promise<ActionResult<TOutput>> => {\n return new Promise((resolve) => {\n startTransition(async () => {\n const result = await execute(args[0] as TInput);\n resolve(result);\n });\n });\n },\n [execute]\n ) as TInput extends void \n ? () => Promise<ActionResult<TOutput>>\n : (input: TInput) => Promise<ActionResult<TOutput>>;\n \n return {\n execute: executeWithTransition,\n result,\n isExecuting,\n hasSucceeded,\n hasErrored,\n reset,\n };\n}\n\n","// Comes from: https://github.com/vercel/next.js/blob/canary/packages/next/src/client/components/redirect-error.ts\n\nenum RedirectStatusCode {\n\tSeeOther = 303,\n\tTemporaryRedirect = 307,\n\tPermanentRedirect = 308,\n}\n\nconst REDIRECT_ERROR_CODE = \"NEXT_REDIRECT\";\n\nenum RedirectType {\n\tpush = \"push\",\n\treplace = \"replace\",\n}\n\nexport type RedirectError = Error & {\n\tdigest: `${typeof REDIRECT_ERROR_CODE};${RedirectType};${string};${RedirectStatusCode};`;\n};\n\n/**\n * Checks an error to determine if it's an error generated by the\n * `redirect(url)` helper.\n *\n * @param error the error that may reference a redirect error\n * @returns true if the error is a redirect error\n */\nexport function isRedirectError(error: unknown): error is RedirectError {\n\tif (typeof error !== \"object\" || error === null || !(\"digest\" in error) || typeof error.digest !== \"string\") {\n\t\treturn false;\n\t}\n\n\tconst digest = error.digest.split(\";\");\n\tconst [errorCode, type] = digest;\n\tconst destination = digest.slice(2, -2).join(\";\");\n\tconst status = digest.at(-2);\n\n\tconst statusCode = Number(status);\n\n\treturn (\n\t\terrorCode === REDIRECT_ERROR_CODE &&\n\t\t(type === \"replace\" || type === \"push\") &&\n\t\ttypeof destination === \"string\" &&\n\t\t!isNaN(statusCode) &&\n\t\tstatusCode in RedirectStatusCode\n\t);\n}","// Comes from https://github.com/vercel/next.js/blob/canary/packages/next/src/client/components/http-access-fallback/http-access-fallback.ts\n\nconst HTTPAccessErrorStatus = {\n\tNOT_FOUND: 404,\n\tFORBIDDEN: 403,\n\tUNAUTHORIZED: 401,\n};\n\nconst ALLOWED_CODES = new Set(Object.values(HTTPAccessErrorStatus));\n\nconst HTTP_ERROR_FALLBACK_ERROR_CODE = \"NEXT_HTTP_ERROR_FALLBACK\";\n\nexport type HTTPAccessFallbackError = Error & {\n\tdigest: `${typeof HTTP_ERROR_FALLBACK_ERROR_CODE};${string}`;\n};\n\n/**\n * Checks an error to determine if it's an error generated by\n * the HTTP navigation APIs `notFound()`, `forbidden()` or `unauthorized()`.\n *\n * @param error the error that may reference a HTTP access error\n * @returns true if the error is a HTTP access error\n */\nexport function isHTTPAccessFallbackError(error: unknown): error is HTTPAccessFallbackError {\n\tif (typeof error !== \"object\" || error === null || !(\"digest\" in error) || typeof error.digest !== \"string\") {\n\t\treturn false;\n\t}\n\tconst [prefix, httpStatus] = error.digest.split(\";\");\n\n\treturn prefix === HTTP_ERROR_FALLBACK_ERROR_CODE && ALLOWED_CODES.has(Number(httpStatus));\n}\n\nexport function getAccessFallbackHTTPStatus(error: HTTPAccessFallbackError): number {\n\tconst httpStatus = error.digest.split(\";\")[1];\n\treturn Number(httpStatus);\n}","import { isRedirectError } from \"./redirect\";\nimport { isHTTPAccessFallbackError, getAccessFallbackHTTPStatus } from \"./http-access-fallback\";\n\nexport { isRedirectError } from \"./redirect\";\nexport type { RedirectError } from \"./redirect\";\n\nexport { isHTTPAccessFallbackError, getAccessFallbackHTTPStatus } from \"./http-access-fallback\";\nexport type { HTTPAccessFallbackError } from \"./http-access-fallback\";\n\n/**\n * Checks if the error is a navigation error that should be re-thrown\n * This includes redirect errors and HTTP access errors (notFound, forbidden, unauthorized)\n */\nexport function isNextNavigationError(error: unknown): boolean {\n\treturn isRedirectError(error) || isHTTPAccessFallbackError(error);\n}\n\n/**\n * Checks if the error is a notFound error\n * Note: Next.js implements notFound() using HTTP_ERROR_FALLBACK with status 404,\n * not as a separate error type like NEXT_REDIRECT\n */\nexport function isNotFoundError(error: unknown): boolean {\n\treturn isHTTPAccessFallbackError(error) && getAccessFallbackHTTPStatus(error as any) === 404;\n}","import { useOptimistic, useTransition } from \"react\";\nimport { useServerAction, type UseServerActionOptions } from \"./use-server-action\";\nimport type { ServerAction, ActionResult } from \"../types\";\n\nexport interface UseOptimisticActionOptions<TInput, TOutput, TOptimistic> extends UseServerActionOptions<TOutput> {\n /**\n * Function to update the optimistic state\n */\n updateFn: TInput extends void \n ? (current: TOptimistic) => TOptimistic\n : (current: TOptimistic, input: TInput) => TOptimistic;\n}\n\nexport interface UseOptimisticActionReturn<TInput, TOutput, TOptimistic> {\n /**\n * The optimistic state\n */\n optimisticState: TOptimistic;\n \n /**\n * Execute the action with optimistic update\n */\n execute: TInput extends void \n ? () => Promise<ActionResult<TOutput>>\n : (input: TInput) => Promise<ActionResult<TOutput>>;\n \n /**\n * Loading state\n */\n isExecuting: boolean;\n \n /**\n * Success state\n */\n hasSucceeded: boolean;\n \n /**\n * Error state\n */\n hasErrored: boolean;\n \n /**\n * Last result\n */\n result: ActionResult<TOutput> | undefined;\n \n /**\n * Reset state\n */\n reset: () => void;\n}\n\n/**\n * Hook for server actions with optimistic updates\n * \n * @example\n * ```typescript\n * const { optimisticState, execute } = useOptimisticAction(\n * currentTodos,\n * addTodoAction,\n * {\n * updateFn: (todos, newTodo) => [...todos, newTodo],\n * onSuccess: (savedTodo) => {\n * // Update with server response if needed\n * }\n * }\n * );\n * ```\n */\nexport function useOptimisticAction<TInput, TOutput, TOptimistic>(\n initialState: TOptimistic,\n action: ServerAction<TInput, TOutput>,\n options: UseOptimisticActionOptions<TInput, TOutput, TOptimistic>\n): UseOptimisticActionReturn<TInput, TOutput, TOptimistic> {\n const [optimisticState, addOptimisticUpdate] = useOptimistic(\n initialState,\n options.updateFn\n );\n \n const [, startTransition] = useTransition();\n \n // Extract updateFn from options to pass the rest to useServerAction\n const { updateFn: _, ...serverActionOptions } = options;\n \n const {\n execute: executeAction,\n isExecuting,\n hasSucceeded,\n hasErrored,\n result,\n reset\n } = useServerAction(action, serverActionOptions);\n \n const execute = (async (input?: TInput): Promise<ActionResult<TOutput>> => {\n // Apply optimistic update\n startTransition(() => {\n if (input !== undefined) {\n addOptimisticUpdate(input);\n } else {\n addOptimisticUpdate(undefined as any);\n }\n });\n \n // Execute the server action\n return executeAction(input as any);\n }) as any;\n \n return {\n optimisticState,\n execute,\n isExecuting,\n hasSucceeded,\n hasErrored,\n result,\n reset\n };\n}","import { useActionState, useEffect, useTransition } from \"react\";\nimport type { UseFormProps, UseFormReturn, Path, FieldValues, DefaultValues } from \"react-hook-form\";\nimport { useForm } from \"react-hook-form\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { toast } from \"sonner\";\nimport type { z } from \"zod\";\nimport type { ServerActionResponse, ServerActionError } from \"../types\";\n\nexport interface UseFormActionOptions<TFieldValues extends FieldValues, TOutput> {\n // Required - the server action to execute (form action format)\n action: (prevState: ServerActionResponse<TOutput>, formData: FormData) => Promise<ServerActionResponse<TOutput>>;\n \n // Optional Zod schema for validation\n schema?: z.ZodTypeAny;\n \n // React Hook Form options\n defaultValues?: DefaultValues<TFieldValues>;\n mode?: UseFormProps<TFieldValues>[\"mode\"];\n \n // Transform function if you need custom FormData creation\n transformData?: (data: TFieldValues) => FormData;\n \n // Callbacks\n onSuccess?: (data: TOutput) => void | Promise<void>;\n onError?: (error: ServerActionError) => void;\n \n // Behavior options\n resetOnSuccess?: boolean;\n showSuccessToast?: boolean | string | ((data: TOutput) => string);\n showErrorToast?: boolean | string | ((error: ServerActionError) => string);\n}\n\nexport interface UseFormActionReturn<TFieldValues extends FieldValues, TOutput> {\n // React Hook Form instance\n form: UseFormReturn<TFieldValues>;\n \n // Submit handler (pre-bound with handleSubmit)\n onSubmit: (e?: React.BaseSyntheticEvent) => void;\n \n // Loading state (combines isPending and isTransitioning)\n isSubmitting: boolean;\n \n // The current action state\n actionState: ServerActionResponse<TOutput>;\n \n // Utilities\n reset: () => void;\n \n // Raw handlers if needed\n handleSubmit: (data: TFieldValues) => Promise<void>;\n}\n\nfunction isSuccessResponse<T>(response: ServerActionResponse<T>): response is { success: true; data: T } {\n return response.success === true;\n}\n\nfunction objectToFormData(obj: any): FormData {\n const formData = new FormData();\n \n Object.entries(obj).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n if (value instanceof File || value instanceof Blob) {\n formData.append(key, value);\n } else if (Array.isArray(value)) {\n value.forEach((item) => formData.append(`${key}[]`, String(item)));\n } else if (typeof value === \"object\") {\n formData.append(key, JSON.stringify(value));\n } else {\n formData.append(key, String(value));\n }\n }\n });\n \n return formData;\n}\n\nexport function useFormAction<TFieldValues extends FieldValues = FieldValues, TOutput = void>({\n action,\n schema,\n defaultValues,\n mode = \"onChange\",\n transformData,\n onSuccess,\n onError,\n resetOnSuccess = false,\n showSuccessToast = false,\n showErrorToast = true,\n}: UseFormActionOptions<TFieldValues, TOutput>): UseFormActionReturn<TFieldValues, TOutput> {\n // 1. Setup form with React Hook Form\n const form = useForm<TFieldValues>({\n resolver: schema ? (zodResolver as any)(schema) : undefined,\n defaultValues,\n mode,\n } as UseFormProps<TFieldValues>);\n \n // 2. Setup server action state with useActionState\n const initialState: ServerActionResponse<TOutput> = { success: true, data: undefined as TOutput };\n const [actionState, formAction, isPending] = useActionState(action, initialState);\n const [isTransitioning, startTransition] = useTransition();\n \n // 3. Handle server errors and success\n useEffect(() => {\n if (!isSuccessResponse(actionState) && actionState.error) {\n const { error } = actionState;\n \n // Map field errors to form\n if (error.fields) {\n Object.entries(error.fields).forEach(([field, messages]) => {\n if (Array.isArray(messages) && messages.length > 0) {\n form.setError(field as Path<TFieldValues>, {\n type: \"server\",\n message: messages[0],\n });\n }\n });\n }\n \n // Single field error\n if (error.field && error.message && !error.fields) {\n form.setError(error.field as Path<TFieldValues>, {\n type: \"server\",\n message: error.message,\n });\n }\n \n // Global error (no specific field)\n if (error.message && !error.field && !error.fields) {\n if (showErrorToast) {\n const message = typeof showErrorToast === \"function\" \n ? showErrorToast(error)\n : typeof showErrorToast === \"string\"\n ? showErrorToast\n : error.message;\n toast.error(message);\n }\n \n // Also set on root for inline display\n form.setError(\"root\", {\n type: \"server\",\n message: error.message,\n });\n }\n \n // Call error callback\n onError?.(error);\n }\n \n // Handle success\n if (isSuccessResponse(actionState) && actionState.data !== undefined) {\n if (showSuccessToast) {\n const message = typeof showSuccessToast === \"function\"\n ? showSuccessToast(actionState.data)\n : typeof showSuccessToast === \"string\"\n ? showSuccessToast\n : \"Success!\";\n toast.success(message);\n }\n \n if (resetOnSuccess) {\n form.reset();\n }\n \n // Call success callback\n onSuccess?.(actionState.data);\n }\n }, [actionState, form, onError, onSuccess, resetOnSuccess, showErrorToast, showSuccessToast]);\n \n // 4. Submit handler\n const handleSubmit = async (data: TFieldValues): Promise<void> => {\n // Clear any previous errors\n form.clearErrors();\n \n // Transform to FormData\n const formData = transformData \n ? transformData(data)\n : objectToFormData(data);\n \n // Execute with transition for better UX\n startTransition(() => {\n formAction(formData);\n });\n };\n \n // 5. Combined loading state\n const isSubmitting = isPending || isTransitioning;\n \n // 6. Pre-bound submit handler\n const onSubmit = (e?: React.BaseSyntheticEvent): void => {\n e?.preventDefault();\n void form.handleSubmit(handleSubmit)(e!);\n };\n \n return {\n form,\n onSubmit,\n isSubmitting,\n actionState,\n reset: form.reset,\n handleSubmit,\n };\n}","\"use client\";\n\nimport { useEffect } from \"react\";\nimport { toast } from \"sonner\";\n\nexport function ToastRestorer() {\n useEffect(() => {\n // Only run on client side\n if (typeof window === \"undefined\") return;\n \n const storageKey = \"sonner-toasts\";\n \n try {\n const storedToasts = localStorage.getItem(storageKey);\n \n if (storedToasts) {\n const persistentToasts = JSON.parse(storedToasts);\n \n if (Array.isArray(persistentToasts) && persistentToasts.length > 0) {\n // Clear the storage immediately to prevent duplicate toasts\n localStorage.removeItem(storageKey);\n \n // Filter out old toasts (older than 30 seconds)\n const recentToasts = persistentToasts.filter(\n (t: any) => Date.now() - (t.createdAt || 0) < 30000\n );\n \n // Restore toasts with staggered animation like in the PR\n recentToasts.forEach((persistedToast: any, index: number) => {\n // Skip loading toasts as per the PR implementation\n if (persistedToast.type === \"loading\") return;\n \n setTimeout(() => {\n const toastFunction = toast[persistedToast.type as keyof typeof toast];\n \n if (typeof toastFunction === \"function\") {\n // Check if current sonner supports persistent\n if (toastFunction.length >= 2) {\n // Use persistent if available\n (toastFunction as any)(persistedToast.message, { \n persistent: true,\n id: persistedToast.id \n });\n } else {\n // Fallback to normal toast\n toastFunction(persistedToast.message);\n }\n }\n }, index * 150); // Staggered delay like in the PR\n });\n }\n }\n } catch (error) {\n console.error(\"Failed to restore toasts:\", error);\n // Clean up on error\n localStorage.removeItem(storageKey);\n }\n }, []);\n \n return null;\n}"],"mappings":"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","names":["RedirectStatusCode","result","useTransition","useTransition","useTransition","toast","useEffect","toast"]}
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/use-server-action.ts","../../src/next/errors/redirect.ts","../../src/next/errors/http-access-fallback.ts","../../src/next/errors/index.ts","../../src/hooks/use-optimistic-action.ts","../../src/hooks/use-form-action.ts","../../src/hooks/toast-restorer.tsx"],"sourcesContent":["import { useCallback, useState, useTransition } from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport { toast } from \"sonner\";\nimport type { ServerAction, ActionResult, ServerActionResponse } from \"../types\";\nimport { isNextNavigationError } from \"../next/errors\";\n\nexport interface UseServerActionOptions<TOutput> {\n onSuccess?: (data: TOutput) => void | Promise<void>;\n onError?: (error: ActionResult<TOutput>) => void | Promise<void>;\n \n successMessage?: string | ((data: TOutput) => string);\n errorMessage?: string | ((error: ActionResult<TOutput>) => string);\n showSuccessToast?: boolean;\n showErrorToast?: boolean;\n \n redirectOnAuthError?: boolean;\n preventRedirect?: boolean;\n redirectDelay?: number;\n}\n\nexport interface UseServerActionReturn<TInput, TOutput> {\n execute: TInput extends void \n ? () => Promise<ActionResult<TOutput>>\n : (input: TInput) => Promise<ActionResult<TOutput>>;\n \n result: ActionResult<TOutput> | undefined;\n isExecuting: boolean;\n hasSucceeded: boolean;\n hasErrored: boolean;\n \n reset: () => void;\n}\n\n// Overload for void actions\nexport function useServerAction<TOutput>(\n action: ServerAction<void, TOutput>,\n options?: UseServerActionOptions<TOutput>\n): UseServerActionReturn<void, TOutput>;\n\n// Overload for actions with input\nexport function useServerAction<TInput, TOutput>(\n action: ServerAction<TInput, TOutput>,\n options?: UseServerActionOptions<TOutput>\n): UseServerActionReturn<TInput, TOutput>;\n\n// Implementation\nexport function useServerAction<TInput, TOutput>(\n action: ServerAction<TInput, TOutput>,\n options: UseServerActionOptions<TOutput> = {}\n): UseServerActionReturn<TInput, TOutput> {\n const router = useRouter();\n const [result, setResult] = useState<ActionResult<TOutput>>();\n const [isExecuting, setIsExecuting] = useState(false);\n const [, startTransition] = useTransition();\n \n const {\n onSuccess,\n onError,\n successMessage,\n errorMessage,\n showSuccessToast = false,\n showErrorToast = true,\n redirectOnAuthError = true,\n preventRedirect = false,\n redirectDelay = 0,\n } = options;\n \n const hasSucceeded = !!result?.data;\n const hasErrored = !!result?.serverError || !!result?.validationErrors || !!result?.fetchError;\n \n const reset = useCallback(() => {\n setResult(undefined);\n }, []);\n \n const execute = useCallback(\n async (input?: TInput): Promise<ActionResult<TOutput>> => {\n setIsExecuting(true);\n \n try {\n const response = await (input === undefined \n ? (action as () => Promise<ServerActionResponse<TOutput>>)()\n : (action as (input: TInput) => Promise<ServerActionResponse<TOutput>>)(input));\n \n let actionResult: ActionResult<TOutput>;\n \n if (response.success) {\n actionResult = { data: response.data };\n \n if (process.env.NODE_ENV === 'development') {\n console.log('[useServerAction] Success response:', response);\n console.log('[useServerAction] Has redirect?', !!response.redirect);\n }\n \n if (showSuccessToast && successMessage) {\n const message = typeof successMessage === \"function\" \n ? successMessage(response.data) \n : successMessage;\n toast.success(message);\n }\n \n await onSuccess?.(response.data);\n \n // Handle redirect if present in response\n if (response.redirect && !preventRedirect) {\n const redirectConfig = typeof response.redirect === 'string'\n ? { url: response.redirect }\n : response.redirect;\n \n const delay = redirectConfig.delay ?? redirectDelay;\n \n setTimeout(() => {\n if (redirectConfig.replace) {\n router.replace(redirectConfig.url);\n } else {\n router.push(redirectConfig.url);\n }\n }, delay);\n }\n \n } else if (!response.success && response.error) {\n actionResult = {\n serverError: response.error,\n validationErrors: response.error.fields,\n };\n \n // Debug logging\n if (process.env.NODE_ENV === 'development') {\n console.log('[useServerAction] Error response:', response.error);\n console.log('[useServerAction] Should redirect?', response.error.shouldRedirect);\n console.log('[useServerAction] Redirect to:', response.error.redirectTo);\n }\n \n if (redirectOnAuthError && response.error.shouldRedirect) {\n // Handle toast before redirect\n if (showErrorToast) {\n const message = errorMessage \n ? (typeof errorMessage === \"function\" \n ? errorMessage(actionResult) \n : errorMessage)\n : response.error.message || \"An error occurred\";\n \n // Always save to localStorage for redirect cases\n // This ensures compatibility with both current and future sonner versions\n if (typeof window !== 'undefined') {\n try {\n const storageKey = 'sonner-toasts';\n const existingToasts = JSON.parse(\n localStorage.getItem(storageKey) || '[]'\n );\n \n // Add our toast in the same format as the PR\n const persistentToast = {\n id: Date.now(),\n type: 'error',\n message,\n persistent: true,\n createdAt: Date.now()\n };\n \n existingToasts.push(persistentToast);\n localStorage.setItem(storageKey, JSON.stringify(existingToasts));\n } catch (err) {\n console.error('Failed to persist toast:', err);\n }\n }\n \n // Always show the toast (with persistent if supported)\n (toast.error as any)(message, { persistent: true });\n }\n \n router.push(response.error.redirectTo || \"/login\");\n return actionResult;\n }\n \n if (showErrorToast) {\n const message = errorMessage \n ? (typeof errorMessage === \"function\" \n ? errorMessage(actionResult) \n : errorMessage)\n : response.error.message || \"An error occurred\";\n toast.error(message);\n }\n \n await onError?.(actionResult);\n } else {\n actionResult = { fetchError: \"Unexpected response format\" };\n }\n \n setResult(actionResult);\n return actionResult;\n \n } catch (error) {\n // Check if this is a Next.js navigation error that should be re-thrown\n if (isNextNavigationError(error)) {\n // Re-throw navigation errors to let Next.js handle them\n throw error;\n }\n \n const fetchError = error instanceof Error ? error.message : \"Network error\";\n const actionResult: ActionResult<TOutput> = { fetchError };\n \n setResult(actionResult);\n \n if (showErrorToast) {\n toast.error(fetchError);\n }\n \n await onError?.(actionResult);\n return actionResult;\n } finally {\n setIsExecuting(false);\n }\n },\n [action, router, showSuccessToast, successMessage, showErrorToast, errorMessage, redirectOnAuthError, onSuccess, onError]\n );\n \n const executeWithTransition = useCallback(\n (...args: TInput extends void ? [] : [input: TInput]): Promise<ActionResult<TOutput>> => {\n return new Promise((resolve) => {\n startTransition(async () => {\n const result = await execute(args[0] as TInput);\n resolve(result);\n });\n });\n },\n [execute]\n ) as TInput extends void \n ? () => Promise<ActionResult<TOutput>>\n : (input: TInput) => Promise<ActionResult<TOutput>>;\n \n return {\n execute: executeWithTransition,\n result,\n isExecuting,\n hasSucceeded,\n hasErrored,\n reset,\n };\n}\n\n","// Comes from: https://github.com/vercel/next.js/blob/canary/packages/next/src/client/components/redirect-error.ts\n\nenum RedirectStatusCode {\n\tSeeOther = 303,\n\tTemporaryRedirect = 307,\n\tPermanentRedirect = 308,\n}\n\nconst REDIRECT_ERROR_CODE = \"NEXT_REDIRECT\";\n\nenum RedirectType {\n\tpush = \"push\",\n\treplace = \"replace\",\n}\n\nexport type RedirectError = Error & {\n\tdigest: `${typeof REDIRECT_ERROR_CODE};${RedirectType};${string};${RedirectStatusCode};`;\n};\n\n/**\n * Checks an error to determine if it's an error generated by the\n * `redirect(url)` helper.\n *\n * @param error the error that may reference a redirect error\n * @returns true if the error is a redirect error\n */\nexport function isRedirectError(error: unknown): error is RedirectError {\n\tif (typeof error !== \"object\" || error === null || !(\"digest\" in error) || typeof error.digest !== \"string\") {\n\t\treturn false;\n\t}\n\n\tconst digest = error.digest.split(\";\");\n\tconst [errorCode, type] = digest;\n\tconst destination = digest.slice(2, -2).join(\";\");\n\tconst status = digest.at(-2);\n\n\tconst statusCode = Number(status);\n\n\treturn (\n\t\terrorCode === REDIRECT_ERROR_CODE &&\n\t\t(type === \"replace\" || type === \"push\") &&\n\t\ttypeof destination === \"string\" &&\n\t\t!isNaN(statusCode) &&\n\t\tstatusCode in RedirectStatusCode\n\t);\n}","// Comes from https://github.com/vercel/next.js/blob/canary/packages/next/src/client/components/http-access-fallback/http-access-fallback.ts\n\nconst HTTPAccessErrorStatus = {\n\tNOT_FOUND: 404,\n\tFORBIDDEN: 403,\n\tUNAUTHORIZED: 401,\n};\n\nconst ALLOWED_CODES = new Set(Object.values(HTTPAccessErrorStatus));\n\nconst HTTP_ERROR_FALLBACK_ERROR_CODE = \"NEXT_HTTP_ERROR_FALLBACK\";\n\nexport type HTTPAccessFallbackError = Error & {\n\tdigest: `${typeof HTTP_ERROR_FALLBACK_ERROR_CODE};${string}`;\n};\n\n/**\n * Checks an error to determine if it's an error generated by\n * the HTTP navigation APIs `notFound()`, `forbidden()` or `unauthorized()`.\n *\n * @param error the error that may reference a HTTP access error\n * @returns true if the error is a HTTP access error\n */\nexport function isHTTPAccessFallbackError(error: unknown): error is HTTPAccessFallbackError {\n\tif (typeof error !== \"object\" || error === null || !(\"digest\" in error) || typeof error.digest !== \"string\") {\n\t\treturn false;\n\t}\n\tconst [prefix, httpStatus] = error.digest.split(\";\");\n\n\treturn prefix === HTTP_ERROR_FALLBACK_ERROR_CODE && ALLOWED_CODES.has(Number(httpStatus));\n}\n\nexport function getAccessFallbackHTTPStatus(error: HTTPAccessFallbackError): number {\n\tconst httpStatus = error.digest.split(\";\")[1];\n\treturn Number(httpStatus);\n}","import { isRedirectError } from \"./redirect\";\nimport { isHTTPAccessFallbackError, getAccessFallbackHTTPStatus } from \"./http-access-fallback\";\n\nexport { isRedirectError } from \"./redirect\";\nexport type { RedirectError } from \"./redirect\";\n\nexport { isHTTPAccessFallbackError, getAccessFallbackHTTPStatus } from \"./http-access-fallback\";\nexport type { HTTPAccessFallbackError } from \"./http-access-fallback\";\n\n/**\n * Checks if the error is a navigation error that should be re-thrown\n * This includes redirect errors and HTTP access errors (notFound, forbidden, unauthorized)\n */\nexport function isNextNavigationError(error: unknown): boolean {\n\treturn isRedirectError(error) || isHTTPAccessFallbackError(error);\n}\n\n/**\n * Checks if the error is a notFound error\n * Note: Next.js implements notFound() using HTTP_ERROR_FALLBACK with status 404,\n * not as a separate error type like NEXT_REDIRECT\n */\nexport function isNotFoundError(error: unknown): boolean {\n\treturn isHTTPAccessFallbackError(error) && getAccessFallbackHTTPStatus(error as any) === 404;\n}","import { useOptimistic, useTransition } from \"react\";\nimport { useServerAction, type UseServerActionOptions } from \"./use-server-action\";\nimport type { ServerAction, ActionResult } from \"../types\";\n\nexport interface UseOptimisticActionOptions<TInput, TOutput, TOptimistic> extends UseServerActionOptions<TOutput> {\n /**\n * Function to update the optimistic state\n */\n updateFn: TInput extends void \n ? (current: TOptimistic) => TOptimistic\n : (current: TOptimistic, input: TInput) => TOptimistic;\n}\n\nexport interface UseOptimisticActionReturn<TInput, TOutput, TOptimistic> {\n /**\n * The optimistic state\n */\n optimisticState: TOptimistic;\n \n /**\n * Execute the action with optimistic update\n */\n execute: TInput extends void \n ? () => Promise<ActionResult<TOutput>>\n : (input: TInput) => Promise<ActionResult<TOutput>>;\n \n /**\n * Loading state\n */\n isExecuting: boolean;\n \n /**\n * Success state\n */\n hasSucceeded: boolean;\n \n /**\n * Error state\n */\n hasErrored: boolean;\n \n /**\n * Last result\n */\n result: ActionResult<TOutput> | undefined;\n \n /**\n * Reset state\n */\n reset: () => void;\n}\n\n/**\n * Hook for server actions with optimistic updates\n * \n * @example\n * ```typescript\n * const { optimisticState, execute } = useOptimisticAction(\n * currentTodos,\n * addTodoAction,\n * {\n * updateFn: (todos, newTodo) => [...todos, newTodo],\n * onSuccess: (savedTodo) => {\n * // Update with server response if needed\n * }\n * }\n * );\n * ```\n */\nexport function useOptimisticAction<TInput, TOutput, TOptimistic>(\n initialState: TOptimistic,\n action: ServerAction<TInput, TOutput>,\n options: UseOptimisticActionOptions<TInput, TOutput, TOptimistic>\n): UseOptimisticActionReturn<TInput, TOutput, TOptimistic> {\n const [optimisticState, addOptimisticUpdate] = useOptimistic(\n initialState,\n options.updateFn\n );\n \n const [, startTransition] = useTransition();\n \n // Extract updateFn from options to pass the rest to useServerAction\n const { updateFn: _, ...serverActionOptions } = options;\n \n const {\n execute: executeAction,\n isExecuting,\n hasSucceeded,\n hasErrored,\n result,\n reset\n } = useServerAction(action, serverActionOptions);\n \n const execute = (async (input?: TInput): Promise<ActionResult<TOutput>> => {\n // Apply optimistic update\n startTransition(() => {\n if (input !== undefined) {\n addOptimisticUpdate(input);\n } else {\n addOptimisticUpdate(undefined as any);\n }\n });\n \n // Execute the server action\n return executeAction(input as any);\n }) as any;\n \n return {\n optimisticState,\n execute,\n isExecuting,\n hasSucceeded,\n hasErrored,\n result,\n reset\n };\n}","import { useActionState, useEffect, useTransition } from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport type { UseFormProps, UseFormReturn, Path, FieldValues, DefaultValues } from \"react-hook-form\";\nimport { useForm } from \"react-hook-form\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { toast } from \"sonner\";\nimport type { z } from \"zod\";\nimport type { ServerActionResponse, ServerActionError, RedirectConfig } from \"../types\";\n\nexport interface UseFormActionOptions<TFieldValues extends FieldValues, TOutput> {\n // Required - the server action to execute (form action format)\n action: (prevState: ServerActionResponse<TOutput>, formData: FormData) => Promise<ServerActionResponse<TOutput>>;\n \n // Optional Zod schema for validation\n schema?: z.ZodTypeAny;\n \n // React Hook Form options\n defaultValues?: DefaultValues<TFieldValues>;\n mode?: UseFormProps<TFieldValues>[\"mode\"];\n \n // Transform function if you need custom FormData creation\n transformData?: (data: TFieldValues) => FormData;\n \n // Callbacks\n onSuccess?: (data: TOutput) => void | Promise<void>;\n onError?: (error: ServerActionError) => void;\n \n // Behavior options\n resetOnSuccess?: boolean;\n showSuccessToast?: boolean | string | ((data: TOutput) => string);\n showErrorToast?: boolean | string | ((error: ServerActionError) => string);\n \n // Redirect options (same as useServerAction)\n preventRedirect?: boolean;\n redirectDelay?: number;\n}\n\nexport interface UseFormActionReturn<TFieldValues extends FieldValues, TOutput> {\n // React Hook Form instance\n form: UseFormReturn<TFieldValues>;\n \n // Submit handler (pre-bound with handleSubmit)\n onSubmit: (e?: React.BaseSyntheticEvent) => void;\n \n // Loading state (combines isPending and isTransitioning)\n isSubmitting: boolean;\n \n // The current action state\n actionState: ServerActionResponse<TOutput>;\n \n // Utilities\n reset: () => void;\n \n // Raw handlers if needed\n handleSubmit: (data: TFieldValues) => Promise<void>;\n}\n\nfunction isSuccessResponse<T>(response: ServerActionResponse<T>): response is { success: true; data: T; redirect?: string | RedirectConfig } {\n return response.success === true;\n}\n\nfunction objectToFormData(obj: any): FormData {\n const formData = new FormData();\n \n Object.entries(obj).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n if (value instanceof File || value instanceof Blob) {\n formData.append(key, value);\n } else if (Array.isArray(value)) {\n value.forEach((item) => formData.append(`${key}[]`, String(item)));\n } else if (typeof value === \"object\") {\n formData.append(key, JSON.stringify(value));\n } else {\n formData.append(key, String(value));\n }\n }\n });\n \n return formData;\n}\n\nexport function useFormAction<TFieldValues extends FieldValues = FieldValues, TOutput = void>({\n action,\n schema,\n defaultValues,\n mode = \"onChange\",\n transformData,\n onSuccess,\n onError,\n resetOnSuccess = false,\n showSuccessToast = false,\n showErrorToast = true,\n preventRedirect = false,\n redirectDelay = 0,\n}: UseFormActionOptions<TFieldValues, TOutput>): UseFormActionReturn<TFieldValues, TOutput> {\n const router = useRouter();\n // 1. Setup form with React Hook Form\n const form = useForm<TFieldValues>({\n resolver: schema ? (zodResolver as any)(schema) : undefined,\n defaultValues,\n mode,\n } as UseFormProps<TFieldValues>);\n \n // 2. Setup server action state with useActionState\n const initialState: ServerActionResponse<TOutput> = { success: true, data: undefined as TOutput };\n const [actionState, formAction, isPending] = useActionState(action, initialState);\n const [isTransitioning, startTransition] = useTransition();\n \n // 3. Handle server errors and success\n useEffect(() => {\n if (!isSuccessResponse(actionState) && actionState.error) {\n const { error } = actionState;\n \n // Map field errors to form\n if (error.fields) {\n Object.entries(error.fields).forEach(([field, messages]) => {\n if (Array.isArray(messages) && messages.length > 0) {\n form.setError(field as Path<TFieldValues>, {\n type: \"server\",\n message: messages[0],\n });\n }\n });\n }\n \n // Single field error\n if (error.field && error.message && !error.fields) {\n form.setError(error.field as Path<TFieldValues>, {\n type: \"server\",\n message: error.message,\n });\n }\n \n // Global error (no specific field)\n if (error.message && !error.field && !error.fields) {\n if (showErrorToast) {\n const message = typeof showErrorToast === \"function\" \n ? showErrorToast(error)\n : typeof showErrorToast === \"string\"\n ? showErrorToast\n : error.message;\n toast.error(message);\n }\n \n // Also set on root for inline display\n form.setError(\"root\", {\n type: \"server\",\n message: error.message,\n });\n }\n \n // Call error callback\n onError?.(error);\n }\n \n // Handle success\n if (isSuccessResponse(actionState) && actionState.data !== undefined) {\n if (showSuccessToast) {\n const message = typeof showSuccessToast === \"function\"\n ? showSuccessToast(actionState.data)\n : typeof showSuccessToast === \"string\"\n ? showSuccessToast\n : \"Success!\";\n toast.success(message);\n }\n \n if (resetOnSuccess) {\n form.reset();\n }\n \n // Call success callback\n onSuccess?.(actionState.data);\n \n // Handle redirect if present in response\n if (actionState.redirect && !preventRedirect) {\n const redirectConfig = typeof actionState.redirect === 'string'\n ? { url: actionState.redirect }\n : actionState.redirect;\n \n const delay = redirectConfig.delay ?? redirectDelay;\n \n setTimeout(() => {\n if (redirectConfig.replace) {\n router.replace(redirectConfig.url);\n } else {\n router.push(redirectConfig.url);\n }\n }, delay);\n }\n }\n }, [actionState, form, onError, onSuccess, resetOnSuccess, showErrorToast, showSuccessToast, preventRedirect, redirectDelay, router]);\n \n // 4. Submit handler\n const handleSubmit = async (data: TFieldValues): Promise<void> => {\n // Clear any previous errors\n form.clearErrors();\n \n // Transform to FormData\n const formData = transformData \n ? transformData(data)\n : objectToFormData(data);\n \n // Execute with transition for better UX\n startTransition(() => {\n formAction(formData);\n });\n };\n \n // 5. Combined loading state\n const isSubmitting = isPending || isTransitioning;\n \n // 6. Pre-bound submit handler\n const onSubmit = (e?: React.BaseSyntheticEvent): void => {\n e?.preventDefault();\n void form.handleSubmit(handleSubmit)(e!);\n };\n \n return {\n form,\n onSubmit,\n isSubmitting,\n actionState,\n reset: form.reset,\n handleSubmit,\n };\n}","\"use client\";\n\nimport { useEffect } from \"react\";\nimport { toast } from \"sonner\";\n\nexport function ToastRestorer() {\n useEffect(() => {\n // Only run on client side\n if (typeof window === \"undefined\") return;\n \n const storageKey = \"sonner-toasts\";\n \n try {\n const storedToasts = localStorage.getItem(storageKey);\n \n if (storedToasts) {\n const persistentToasts = JSON.parse(storedToasts);\n \n if (Array.isArray(persistentToasts) && persistentToasts.length > 0) {\n // Clear the storage immediately to prevent duplicate toasts\n localStorage.removeItem(storageKey);\n \n // Filter out old toasts (older than 30 seconds)\n const recentToasts = persistentToasts.filter(\n (t: any) => Date.now() - (t.createdAt || 0) < 30000\n );\n \n // Restore toasts with staggered animation like in the PR\n recentToasts.forEach((persistedToast: any, index: number) => {\n // Skip loading toasts as per the PR implementation\n if (persistedToast.type === \"loading\") return;\n \n setTimeout(() => {\n const toastFunction = toast[persistedToast.type as keyof typeof toast];\n \n if (typeof toastFunction === \"function\") {\n // Check if current sonner supports persistent\n if (toastFunction.length >= 2) {\n // Use persistent if available\n (toastFunction as any)(persistedToast.message, { \n persistent: true,\n id: persistedToast.id \n });\n } else {\n // Fallback to normal toast\n toastFunction(persistedToast.message);\n }\n }\n }, index * 150); // Staggered delay like in the PR\n });\n }\n }\n } catch (error) {\n console.error(\"Failed to restore toasts:\", error);\n // Clean up on error\n localStorage.removeItem(storageKey);\n }\n }, []);\n \n return null;\n}"],"mappings":"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","names":["RedirectStatusCode","result","useTransition","useTransition","useTransition","useRouter","toast","useEffect","toast"]}
|