|
@@ -0,0 +1,312 @@
|
|
|
+diff --git a/dist/hook.d.ts b/dist/hook.d.ts
|
|
|
+index 42220517df6bad298dd77f2e1961d6798ecfef0d..7b32f3634610ae20c0b108034a7da8048bc96205 100644
|
|
|
+--- a/dist/hook.d.ts
|
|
|
++++ b/dist/hook.d.ts
|
|
|
+@@ -1,5 +1,9 @@
|
|
|
+-import { z } from 'zod';
|
|
|
+-import { C as ClientCaller, H as HookCallbacks, a as HookRes } from './types-31a698ec.js';
|
|
|
++import { z } from "zod";
|
|
|
++import {
|
|
|
++ C as ClientCaller,
|
|
|
++ H as HookCallbacks,
|
|
|
++ a as HookRes,
|
|
|
++} from "./types-31a698ec.js";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Use the action from a Client Component via hook.
|
|
|
+@@ -8,14 +12,17 @@ import { C as ClientCaller, H as HookCallbacks, a as HookRes } from './types-31a
|
|
|
+ *
|
|
|
+ * {@link https://github.com/TheEdoRan/next-safe-action/tree/main/packages/next-safe-action#2-the-hook-way See an example}
|
|
|
+ */
|
|
|
+-declare const useAction: <const IV extends z.ZodTypeAny, const Data>(clientCaller: ClientCaller<IV, Data>, cb?: HookCallbacks<IV, Data> | undefined) => {
|
|
|
+- execute: (input: z.input<IV>) => void;
|
|
|
+- isExecuting: boolean;
|
|
|
+- res: HookRes<IV, Data>;
|
|
|
+- reset: () => void;
|
|
|
+- hasExecuted: boolean;
|
|
|
+- hasSucceded: boolean;
|
|
|
+- hasErrored: boolean;
|
|
|
++declare const useAction: <const IV extends z.ZodTypeAny, const Data>(
|
|
|
++ clientCaller: ClientCaller<IV, Data>,
|
|
|
++ cb?: HookCallbacks<IV, Data> | undefined
|
|
|
++) => {
|
|
|
++ execute: (input: z.input<IV>) => void;
|
|
|
++ isExecuting: boolean;
|
|
|
++ res: HookRes<IV, Data>;
|
|
|
++ reset: () => void;
|
|
|
++ hasExecuted: boolean;
|
|
|
++ hasSucceded: boolean;
|
|
|
++ hasErrored: boolean;
|
|
|
+ };
|
|
|
+ /**
|
|
|
+ * Use the action from a Client Component via hook, with optimistic data update.
|
|
|
+@@ -27,15 +34,22 @@ declare const useAction: <const IV extends z.ZodTypeAny, const Data>(clientCalle
|
|
|
+ *
|
|
|
+ * {@link https://github.com/TheEdoRan/next-safe-action/tree/main/packages/next-safe-action#optimistic-update--experimental See an example}
|
|
|
+ */
|
|
|
+-declare const useOptimisticAction: <const IV extends z.ZodTypeAny, const Data>(clientCaller: ClientCaller<IV, Data>, initialOptData: Data, cb?: HookCallbacks<IV, Data> | undefined) => {
|
|
|
+- execute: (input: z.input<IV>, newOptimisticData: Partial<Data>) => Promise<void>;
|
|
|
+- isExecuting: boolean;
|
|
|
+- res: HookRes<IV, Data>;
|
|
|
+- optimisticData: Data;
|
|
|
+- reset: () => void;
|
|
|
+- hasExecuted: boolean;
|
|
|
+- hasSucceded: boolean;
|
|
|
+- hasErrored: boolean;
|
|
|
++declare const useOptimisticAction: <const IV extends z.ZodTypeAny, const Data>(
|
|
|
++ clientCaller: ClientCaller<IV, Data>,
|
|
|
++ initialOptData: Data,
|
|
|
++ cb?: HookCallbacks<IV, Data> | undefined
|
|
|
++) => {
|
|
|
++ execute: (
|
|
|
++ input: z.input<IV>,
|
|
|
++ newOptimisticData: Partial<Data>
|
|
|
++ ) => Promise<void>;
|
|
|
++ isExecuting: boolean;
|
|
|
++ res: HookRes<IV, Data>;
|
|
|
++ optimisticData: Data;
|
|
|
++ reset: () => void;
|
|
|
++ hasExecuted: boolean;
|
|
|
++ hasSucceded: boolean;
|
|
|
++ hasErrored: boolean;
|
|
|
+ };
|
|
|
+
|
|
|
+ export { HookCallbacks, HookRes, useAction, useOptimisticAction };
|
|
|
+diff --git a/dist/hook.mjs b/dist/hook.mjs
|
|
|
+index 89d77b1da720c87508e525f09e2c0005a1986819..5b0d50450441734813511cce9c6df203104c6f66 100644
|
|
|
+--- a/dist/hook.mjs
|
|
|
++++ b/dist/hook.mjs
|
|
|
+@@ -1,3 +1,5 @@
|
|
|
++"use client";
|
|
|
++
|
|
|
+ // src/hook.ts
|
|
|
+ import {
|
|
|
+ experimental_useOptimistic,
|
|
|
+@@ -5,31 +7,34 @@ import {
|
|
|
+ useEffect,
|
|
|
+ useRef,
|
|
|
+ useState,
|
|
|
+- useTransition
|
|
|
++ useTransition,
|
|
|
+ } from "react";
|
|
|
+
|
|
|
+ // src/utils.ts
|
|
|
+-var isNextRedirectError = (e) => e instanceof Error && e.message === "NEXT_REDIRECT";
|
|
|
+-var isNextNotFoundError = (e) => e instanceof Error && e.message === "NEXT_NOT_FOUND";
|
|
|
++var isError = (e) => e instanceof Error;
|
|
|
++var isNextRedirectError = (e) => isError(e) && e.message === "NEXT_REDIRECT";
|
|
|
++var isNextNotFoundError = (e) => isError(e) && e.message === "NEXT_NOT_FOUND";
|
|
|
+
|
|
|
+ // src/hook.ts
|
|
|
+-"use client";
|
|
|
+ var getActionStatus = (res) => {
|
|
|
+ const hasSucceded = typeof res.data !== "undefined";
|
|
|
+- const hasErrored = typeof res.validationError !== "undefined" || typeof res.serverError !== "undefined" || typeof res.fetchError !== "undefined";
|
|
|
++ const hasErrored =
|
|
|
++ typeof res.validationError !== "undefined" ||
|
|
|
++ typeof res.serverError !== "undefined" ||
|
|
|
++ typeof res.fetchError !== "undefined";
|
|
|
+ const hasExecuted = hasSucceded || hasErrored;
|
|
|
+ return { hasExecuted, hasSucceded, hasErrored };
|
|
|
+ };
|
|
|
+-var useActionCallbacks = (res, hasSucceded, hasErrored, reset, cb) => {
|
|
|
++var useActionCallbacks = (input, res, hasSucceded, hasErrored, reset, cb) => {
|
|
|
+ const onSuccessRef = useRef(cb?.onSuccess);
|
|
|
+ const onErrorRef = useRef(cb?.onError);
|
|
|
+ useEffect(() => {
|
|
|
+ const onSuccess = onSuccessRef.current;
|
|
|
+ const onError = onErrorRef.current;
|
|
|
+ if (onSuccess && hasSucceded) {
|
|
|
+- onSuccess(res.data, reset);
|
|
|
++ onSuccess(res.data, reset, input);
|
|
|
+ } else if (onError && hasErrored) {
|
|
|
+- onError(res, reset);
|
|
|
++ onError(res, reset, input);
|
|
|
+ }
|
|
|
+ }, [hasErrored, hasSucceded, res, reset]);
|
|
|
+ };
|
|
|
+@@ -37,21 +42,31 @@ var useAction = (clientCaller, cb) => {
|
|
|
+ const [isExecuting, startTransition] = useTransition();
|
|
|
+ const executor = useRef(clientCaller);
|
|
|
+ const [res, setRes] = useState({});
|
|
|
++ const [input, setInput] = useState();
|
|
|
++ const onExecuteRef = useRef(cb?.onExecute);
|
|
|
+ const { hasExecuted, hasSucceded, hasErrored } = getActionStatus(res);
|
|
|
+- const execute = useCallback((input) => {
|
|
|
++ const execute = useCallback((input2) => {
|
|
|
++ setInput(input2);
|
|
|
++ const onExecute = onExecuteRef.current;
|
|
|
++ if (onExecute) {
|
|
|
++ onExecute(input2);
|
|
|
++ }
|
|
|
+ return startTransition(() => {
|
|
|
+- return executor.current(input).then((res2) => setRes(res2)).catch((e) => {
|
|
|
+- if (isNextRedirectError(e) || isNextNotFoundError(e)) {
|
|
|
+- throw e;
|
|
|
+- }
|
|
|
+- setRes({ fetchError: e });
|
|
|
+- });
|
|
|
++ return executor
|
|
|
++ .current(input2)
|
|
|
++ .then((res2) => setRes(res2))
|
|
|
++ .catch((e) => {
|
|
|
++ if (isNextRedirectError(e) || isNextNotFoundError(e)) {
|
|
|
++ throw e;
|
|
|
++ }
|
|
|
++ setRes({ fetchError: e });
|
|
|
++ });
|
|
|
+ });
|
|
|
+ }, []);
|
|
|
+ const reset = useCallback(() => {
|
|
|
+ setRes({});
|
|
|
+ }, []);
|
|
|
+- useActionCallbacks(res, hasSucceded, hasErrored, reset, cb);
|
|
|
++ useActionCallbacks(input, res, hasSucceded, hasErrored, reset, cb);
|
|
|
+ return {
|
|
|
+ execute,
|
|
|
+ isExecuting,
|
|
|
+@@ -59,34 +74,47 @@ var useAction = (clientCaller, cb) => {
|
|
|
+ reset,
|
|
|
+ hasExecuted,
|
|
|
+ hasSucceded,
|
|
|
+- hasErrored
|
|
|
++ hasErrored,
|
|
|
+ };
|
|
|
+ };
|
|
|
+ var useOptimisticAction = (clientCaller, initialOptData, cb) => {
|
|
|
+ const [res, setRes] = useState({});
|
|
|
+- const [optState, syncState] = experimental_useOptimistic({ ...initialOptData, ...res.data, __isExecuting__: false }, (state, newState) => ({
|
|
|
+- ...state,
|
|
|
+- ...newState,
|
|
|
+- __isExecuting__: true
|
|
|
+- }));
|
|
|
++ const [input, setInput] = useState();
|
|
|
++ const [optState, syncState] = experimental_useOptimistic(
|
|
|
++ { ...initialOptData, ...res.data, __isExecuting__: false },
|
|
|
++ (state, newState) => ({
|
|
|
++ ...state,
|
|
|
++ ...newState,
|
|
|
++ __isExecuting__: true,
|
|
|
++ })
|
|
|
++ );
|
|
|
+ const executor = useRef(clientCaller);
|
|
|
++ const onExecuteRef = useRef(cb?.onExecute);
|
|
|
+ const { hasExecuted, hasSucceded, hasErrored } = getActionStatus(res);
|
|
|
+ const execute = useCallback(
|
|
|
+- (input, newOptimisticData) => {
|
|
|
++ (input2, newOptimisticData) => {
|
|
|
+ syncState(newOptimisticData);
|
|
|
+- return executor.current(input).then((res2) => setRes(res2)).catch((e) => {
|
|
|
+- if (isNextRedirectError(e) || isNextNotFoundError(e)) {
|
|
|
+- throw e;
|
|
|
+- }
|
|
|
+- setRes({ fetchError: e });
|
|
|
+- });
|
|
|
++ setInput(input2);
|
|
|
++ const onExecute = onExecuteRef.current;
|
|
|
++ if (onExecute) {
|
|
|
++ onExecute(input2);
|
|
|
++ }
|
|
|
++ return executor
|
|
|
++ .current(input2)
|
|
|
++ .then((res2) => setRes(res2))
|
|
|
++ .catch((e) => {
|
|
|
++ if (isNextRedirectError(e) || isNextNotFoundError(e)) {
|
|
|
++ throw e;
|
|
|
++ }
|
|
|
++ setRes({ fetchError: e });
|
|
|
++ });
|
|
|
+ },
|
|
|
+ [syncState]
|
|
|
+ );
|
|
|
+ const reset = useCallback(() => {
|
|
|
+ setRes({});
|
|
|
+ }, []);
|
|
|
+- useActionCallbacks(res, hasSucceded, hasErrored, reset, cb);
|
|
|
++ useActionCallbacks(input, res, hasSucceded, hasErrored, reset, cb);
|
|
|
+ const { __isExecuting__, ...optimisticData } = optState;
|
|
|
+ return {
|
|
|
+ execute,
|
|
|
+@@ -97,11 +125,8 @@ var useOptimisticAction = (clientCaller, initialOptData, cb) => {
|
|
|
+ reset,
|
|
|
+ hasExecuted,
|
|
|
+ hasSucceded,
|
|
|
+- hasErrored
|
|
|
++ hasErrored,
|
|
|
+ };
|
|
|
+ };
|
|
|
+-export {
|
|
|
+- useAction,
|
|
|
+- useOptimisticAction
|
|
|
+-};
|
|
|
++export { useAction, useOptimisticAction };
|
|
|
+ //# sourceMappingURL=hook.mjs.map
|
|
|
+diff --git a/dist/types-31a698ec.d.ts b/dist/types-31a698ec.d.ts
|
|
|
+index c9339a0a530dd1825af0c6e1b4a6e9a58adc3c97..1eb8b8303d1534deee9f0084b6c62c9eabb147b2 100644
|
|
|
+--- a/dist/types-31a698ec.d.ts
|
|
|
++++ b/dist/types-31a698ec.d.ts
|
|
|
+@@ -1,30 +1,53 @@
|
|
|
+-import { z } from 'zod';
|
|
|
++import { z } from "zod";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Type of the function called from Client Components with typesafe input data for the Server Action.
|
|
|
+ */
|
|
|
+-type ClientCaller<IV extends z.ZodTypeAny, Data> = (input: z.input<IV>) => Promise<{
|
|
|
+- data?: Data;
|
|
|
+- serverError?: true;
|
|
|
+- validationError?: Partial<Record<keyof z.input<IV>, string[]>>;
|
|
|
++type ClientCaller<IV extends z.ZodTypeAny, Data> = (
|
|
|
++ input: z.input<IV>
|
|
|
++) => Promise<{
|
|
|
++ data?: Data;
|
|
|
++ serverError?: string;
|
|
|
++ validationError?: Partial<Record<keyof z.input<IV>, string[]>>;
|
|
|
+ }>;
|
|
|
+ /**
|
|
|
+ * Type of the function that executes server code when defining a new safe action.
|
|
|
+ */
|
|
|
+-type ActionServerFn<IV extends z.ZodTypeAny, Data, Context extends object> = (parsedInput: z.input<IV>, ctx: Context) => Promise<Data>;
|
|
|
++type ActionServerFn<IV extends z.ZodTypeAny, Data, Context extends object> = (
|
|
|
++ parsedInput: z.input<IV>,
|
|
|
++ ctx: Context
|
|
|
++) => Promise<Data>;
|
|
|
+ /**
|
|
|
+ * Type of `res` object returned by `useAction` and `useOptimisticAction` hooks.
|
|
|
+ */
|
|
|
+-type HookRes<IV extends z.ZodTypeAny, Data> = Awaited<ReturnType<ClientCaller<IV, Data>>> & {
|
|
|
+- fetchError?: unknown;
|
|
|
++type HookRes<IV extends z.ZodTypeAny, Data> = Awaited<
|
|
|
++ ReturnType<ClientCaller<IV, Data>>
|
|
|
++> & {
|
|
|
++ fetchError?: unknown;
|
|
|
+ };
|
|
|
+ /**
|
|
|
+ * Type of hooks callbacks (`onSuccess` and `onError`).
|
|
|
+ * These are executed when the action succeeds or fails.
|
|
|
+ */
|
|
|
+ type HookCallbacks<IV extends z.ZodTypeAny, Data> = {
|
|
|
+- onSuccess?: (data: NonNullable<Pick<HookRes<IV, Data>, "data">["data"]>, reset: () => void) => void;
|
|
|
+- onError?: (error: Omit<HookRes<IV, Data>, "data">, reset: () => void) => void;
|
|
|
++ onSuccess?: (
|
|
|
++ data: NonNullable<Pick<HookRes<IV, Data>, "data">["data"]>,
|
|
|
++ reset: () => void,
|
|
|
++ input: z.input<IV>
|
|
|
++ ) => void;
|
|
|
++ onError?: (
|
|
|
++ error: Omit<HookRes<IV, Data>, "data">,
|
|
|
++ reset: () => void,
|
|
|
++ input: z.input<IV>
|
|
|
++ ) => void;
|
|
|
++ onExecute?: (input: z.input<IV>) => unknown;
|
|
|
+ };
|
|
|
++type MaybePromise<T> = T | Promise<T>;
|
|
|
+
|
|
|
+-export { ActionServerFn as A, ClientCaller as C, HookCallbacks as H, HookRes as a };
|
|
|
++export {
|
|
|
++ ActionServerFn as A,
|
|
|
++ ClientCaller as C,
|
|
|
++ HookCallbacks as H,
|
|
|
++ MaybePromise as M,
|
|
|
++ HookRes as a,
|
|
|
++};
|