react-form-action 2.8.0 → 2.9.1

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 CHANGED
@@ -17,10 +17,11 @@ End-to-end typesafe success, error & validation state control for Next.js form a
17
17
  - ✅ Define bindable arguments with the `.args([])` method.
18
18
  - ✅ Reuse error handling with the `.error(handler)`.
19
19
 
20
- **React Context access with the `<Action action={myFormAction} />` component**
20
+ **React Context access with the `<Action action={myFormAction} args={[...]} />` component**
21
21
 
22
- - ✅ The `useActionState()` accessible via the `useActionContext()` hook.
22
+ - ✅ The `React.useActionState()` accessible via the `useActionContext()` hook.
23
23
  - ✅ Computes progress flags like `isInvalid`, `isSuccess` based on the envelope type.
24
+ - ✅ Binds the action with the arguments from the _args_ prop.
24
25
 
25
26
  **Context-bound `<Form />` component**
26
27
 
@@ -193,16 +194,17 @@ export const signUp = authAction
193
194
  The `formAction` builder supports action arguments binding:
194
195
 
195
196
  ```ts
196
- // app/[locale]/update-user/[userId]/action.tsx
197
197
  import { formAction } from "react-form-action";
198
198
 
199
- export const updateUser = formAction
200
- .args([z.string().uuid(), z.enum("fr", "en")])
201
- .run(async ({ args: [userId] }) => {
202
- return userId;
203
- // ^? string
199
+ const i18nAction = formAction.args([z.enum("fr", "en")]);
200
+
201
+ export const updateUser = i18nAction
202
+ .args([z.string().uuid()])
203
+ .run(async ({ args: [_, userId] }) => {
204
+ // ^? ["fr" | "en", string]
205
+ // ...
204
206
  })
205
- .error(({ args: [_, locale] }) => {
207
+ .error(({ args: [locale] }) => {
206
208
  return locale === "fr"
207
209
  ? "Échec de la mise à jour de l'utilisateur"
208
210
  : "Failed to update user";
@@ -210,26 +212,20 @@ export const updateUser = formAction
210
212
  ```
211
213
 
212
214
  ```tsx
213
- // app/[locale]/update-user/[userId]/page.tsx
214
215
  import { Action } from "react-form-action/client";
216
+ import { locale } from "next/root-params";
215
217
 
216
- import { locale as localeParam } from "next/root-params";
217
218
  import { updateUser } from "./action";
218
219
  import { UpdateUserForm } from "./form";
219
220
 
220
221
  export default function Page({
221
222
  params,
222
223
  }: PageProps<"/[locale]/update-user/[userId]">) {
223
- const locale = await localeParam();
224
224
  const { userId } = await params;
225
225
 
226
226
  return (
227
- <Action
228
- action={updateUser}
229
- args={[userId, await localeParam()]}
230
- initialData=""
231
- >
232
- <SubscribeForm />
227
+ <Action action={updateUser} args={[await locale(), userId]} initialData="">
228
+ <UpdateUserForm />
233
229
  </Action>
234
230
  );
235
231
  }
package/dist/client.d.mts CHANGED
@@ -3,7 +3,7 @@ import React__default, { PropsWithChildren, JSX } from 'react';
3
3
  import { F as FormAction, A as ActionState, I as InitialState, a as InvalidState, b as FailureState, S as SuccessState } from './createFormAction-DjhozgiG.mjs';
4
4
  import { $ZodErrorTree } from 'zod/v4/core';
5
5
 
6
- type ActionProps<Data, Error, ValidationError, Arguments extends unknown[] = []> = PropsWithChildren<Arguments extends [unknown] ? {
6
+ type ActionProps<Data, Error, ValidationError, Arguments extends unknown[] = []> = PropsWithChildren<Arguments extends [unknown, ...unknown[]] ? {
7
7
  action: FormAction<Data, Error, ValidationError, FormData, Arguments>;
8
8
  args: Arguments;
9
9
  initialData: Data;
package/dist/client.mjs CHANGED
@@ -1 +1 @@
1
- "use client";import{a as f}from"./chunk-LPB4R2UB.mjs";import S from"react";import{useActionState as F,createContext as P,use as y}from"react";var c={isInitial:!1,isInvalid:!1,isFailure:!1,isSuccess:!1},E=P(null);function s(e){let t=y(E);if(!t)throw new Error("ActionContext must be initialized before use. Is your useActionContext hook wrapped with an <Action> Component?");return t}function v({children:e,action:t,initialData:a,args:i,permalink:r}){let o=i?t.bind(null,...i):t,[n,l,d]=F(o,f(a),r),u=n.type==="initial"?{...n,...c,action:l,isPending:d,isInitial:!0}:n.type==="invalid"?{...n,...c,action:l,isPending:d,isInvalid:!0}:n.type==="failure"?{...n,...c,action:l,isPending:d,isFailure:!0}:{...n,...c,action:l,isPending:d,isSuccess:!0};return S.createElement(E,{value:u},e)}import h,{startTransition as g}from"react";function I({autoReset:e=!1,...t}){let{action:a}=s();return h.createElement("form",{...e?{action:a}:{onSubmit:r=>{r.preventDefault();let o=r.currentTarget;g(()=>{a(new FormData(o))})}},...t})}import m from"react";import x from"react";var C=".",p=Object.freeze({errors:Object.freeze([])});function A({errors:e,name:t,children:a=({error:i})=>x.createElement(x.Fragment,null,i)}){if(t.length===0)return a({name:t,error:e.errors[0],errors:e.errors});let i=t.split(C),r=e;for(;i.length;){let o=i.shift();if(!r.properties){r=p;break}r=r.properties[o]??p}return a({name:t,error:r.errors[0],errors:r.errors})}function J(e){function t({name:r,children:o}){let{isInvalid:n,validationError:l}=s(e);return m.createElement(A,{errors:l??p,name:r},o??(({error:u})=>n&&m.createElement(m.Fragment,null,u)))}function a({children:r}){let{isSuccess:o,data:n}=s(e);return typeof r=="function"?r(o?{isSuccess:o,data:n}:{isSuccess:o,data:n}):o&&r}function i({children:r}){let{isInvalid:o,validationError:n}=s(e);return typeof r=="function"?r(o?{isInvalid:o,validationError:n}:{isInvalid:o,validationError:n}):o&&r}return{FieldError:t,Success:a,Invalid:i}}function q({children:e}){let{isPending:t}=s();return typeof e=="function"?e({isPending:t}):t&&e}export{v as Action,I as Form,q as Pending,A as ZodFieldError,J as createComponents,p as noError,s as useActionContext};
1
+ "use client";import{a as f}from"./chunk-LPB4R2UB.mjs";import S from"react";import{useActionState as F,createContext as P,use as y}from"react";var c={isInitial:!1,isInvalid:!1,isFailure:!1,isSuccess:!1},E=P(null);function s(e){let t=y(E);if(!t)throw new Error("ActionContext must be initialized before use. Is your useActionContext hook wrapped with an <Action> Component?");return t}function v({children:e,action:t,initialData:a,args:i,permalink:r}){let o=i?t.bind(null,...i):t,[n,l,d]=F(o,f(a),r),u=n.type==="initial"?{...n,...c,action:l,isPending:d,isInitial:!0}:n.type==="invalid"?{...n,...c,action:l,isPending:d,isInvalid:!0}:n.type==="failure"?{...n,...c,action:l,isPending:d,isFailure:!0}:{...n,...c,action:l,isPending:d,isSuccess:!0};return S.createElement(E,{value:u},e)}import h,{startTransition as g}from"react";function I({autoReset:e=!1,...t}){let{action:a}=s();return h.createElement("form",{...e?{action:a}:{onSubmit:r=>{r.preventDefault();let o=r.currentTarget;g(()=>{a(new FormData(o))})}},...t})}import m from"react";import x from"react";var k=".",p=Object.freeze({errors:Object.freeze([])});function A({errors:e,name:t,children:a=({error:i})=>x.createElement(x.Fragment,null,i)}){if(t.length===0)return a({name:t,error:e.errors[0],errors:e.errors});let i=t.split(k),r=e;for(;i.length;){let o=i.shift();if(!r.properties){r=p;break}r=r.properties[o]??p}return a({name:t,error:r.errors[0],errors:r.errors})}function J(e){function t({name:r,children:o}){let{isInvalid:n,validationError:l}=s(e);return m.createElement(A,{errors:l??p,name:r},o??(({error:u})=>n&&m.createElement(m.Fragment,null,u)))}function a({children:r}){let{isSuccess:o,data:n}=s(e);return typeof r=="function"?r(o?{isSuccess:o,data:n}:{isSuccess:o,data:n}):o&&r}function i({children:r}){let{isInvalid:o,validationError:n}=s(e);return typeof r=="function"?r(o?{isInvalid:o,validationError:n}:{isInvalid:o,validationError:n}):o&&r}return{FieldError:t,Success:a,Invalid:i}}function q({children:e}){let{isPending:t}=s();return typeof e=="function"?e({isPending:t}):t&&e}export{v as Action,I as Form,q as Pending,A as ZodFieldError,J as createComponents,p as noError,s as useActionContext};
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { A as ActionState, a as InvalidState, b as FailureState, S as SuccessState } from './createFormAction-DjhozgiG.mjs';
2
2
  export { F as FormAction, I as InitialState, c as createFormAction, i as initial } from './createFormAction-DjhozgiG.mjs';
3
- import { ZodType, ZodTuple, ZodObject, z } from 'zod/v4';
4
- import { $ZodShape, $ZodErrorTree } from 'zod/v4/core';
3
+ import { ZodType, z, ZodTuple, ZodObject } from 'zod/v4';
4
+ import { $ZodType, $ZodShape, $ZodErrorTree } from 'zod/v4/core';
5
5
 
6
6
  declare const emptyInput: unique symbol;
7
7
  type EmptyInput = typeof emptyInput;
@@ -9,44 +9,44 @@ type Flatten<T> = Identity<{
9
9
  [K in keyof T]: T[K];
10
10
  }>;
11
11
  type Identity<T> = T;
12
- type ErrorHandler<Context, Err, Args extends ZodTuple> = (params: {
12
+ type ErrorHandler<Context, Err, Args> = (params: {
13
13
  error: unknown;
14
14
  ctx: Context;
15
- args: z.infer<Args>;
15
+ args: Args;
16
16
  }) => Promise<Err>;
17
17
  type InitialContext = Record<"formData", FormData>;
18
18
  /**
19
19
  * Action without schema has no input.
20
20
  */
21
- type Action<Data, Context, Args extends ZodTuple> = (params: {
22
- args: z.infer<Args>;
21
+ type Action<Data, Context, Args extends unknown[]> = (params: {
22
+ args: Args;
23
23
  ctx: Context;
24
24
  }) => Promise<Data>;
25
25
  /**
26
26
  * Action with schema will get parsed formData as the input.
27
27
  */
28
- type SchemaAction<Data, Context, Args extends ZodTuple, Schema extends ZodType> = (params: {
29
- args: z.infer<Args>;
28
+ type SchemaAction<Data, Context, Args extends unknown[], Schema extends ZodType> = (params: {
29
+ args: Args;
30
30
  ctx: Context;
31
- input: z.infer<Schema>;
31
+ input: z.output<Schema>;
32
32
  }) => Promise<Data>;
33
- type FormActionBuilder<Schema extends ZodType | EmptyInput, TErr = unknown, Context = InitialContext, Args extends ZodTuple = ZodTuple<[], null>> = {
34
- args: <T extends [ZodType, ...ZodType[]]>(args: T) => FormActionBuilder<Schema, TErr, Context, ZodTuple<T, null>>;
35
- input: <T extends ZodObject>(newInput: T extends ZodObject ? T : "The schema output must be an object.") => FormActionBuilder<Schema extends ZodObject<infer O1 extends $ZodShape> ? T extends ZodObject<infer O2 extends $ZodShape> ? ZodObject<O1 & O2> : never : T, TErr, Context, Args>;
33
+ type FormActionBuilder<Schema extends ZodType | EmptyInput, Args extends $ZodType[] = [], TErr = unknown, Context = InitialContext, PlainArgs extends unknown[] = z.output<ZodTuple<Args, null>>> = {
34
+ args: <T extends [$ZodType, ...$ZodType[]]>(args: T) => FormActionBuilder<Schema, [...Args, ...T], TErr, Context>;
35
+ input: <T extends ZodObject>(newInput: T extends ZodObject ? T : "The schema output must be an object.") => FormActionBuilder<Schema extends ZodObject<infer O1 extends $ZodShape> ? T extends ZodObject<infer O2 extends $ZodShape> ? ZodObject<O1 & O2> : never : T, Args, TErr, Context>;
36
36
  /**
37
37
  * A finishing call on the builder completing your action.
38
38
  * @param action async function to execute. It will receive parsed formData as input, when the builder was initialized with a schema.
39
39
  * @returns runnable server action exportable from a module.
40
40
  */
41
- run: Schema extends ZodType ? <Data>(action: SchemaAction<Data, Flatten<Context>, Args, Schema>) => (...args: [
42
- ...z.infer<Args>,
43
- state: ActionState<Data, TErr, $ZodErrorTree<z.output<Schema>> & $ZodErrorTree<z.output<Args>>>,
41
+ run: Schema extends ZodType ? <Data>(action: SchemaAction<Data, Flatten<Context>, PlainArgs, Schema>) => (...args: [
42
+ ...PlainArgs,
43
+ state: ActionState<Data, TErr, $ZodErrorTree<z.output<Schema>> & $ZodErrorTree<PlainArgs>>,
44
44
  payload: FormData
45
- ]) => Promise<Flatten<InvalidState<$ZodErrorTree<z.output<Schema>> & $ZodErrorTree<z.output<Args>>> | FailureState<TErr> | SuccessState<Data>>> : <Data>(action: Action<Data, Flatten<Context>, Args>) => (...args: [
46
- ...z.infer<Args>,
47
- state: ActionState<Data, TErr, $ZodErrorTree<z.output<Args>>>,
45
+ ]) => Promise<Flatten<InvalidState<$ZodErrorTree<z.output<Schema>> & $ZodErrorTree<PlainArgs>> | FailureState<TErr> | SuccessState<Data>>> : <Data>(action: Action<Data, Flatten<Context>, PlainArgs>) => (...args: [
46
+ ...PlainArgs,
47
+ state: ActionState<Data, TErr, $ZodErrorTree<PlainArgs>>,
48
48
  payload: FormData
49
- ]) => Promise<Flatten<InvalidState<$ZodErrorTree<z.output<Args>>> | FailureState<TErr> | SuccessState<Data>>>;
49
+ ]) => Promise<Flatten<InvalidState<$ZodErrorTree<PlainArgs>> | FailureState<TErr> | SuccessState<Data>>>;
50
50
  /**
51
51
  * A chainable context enhancing helper.
52
52
  * @param middleware A function which receives the current context, and returns an object which will be added to the context.
@@ -54,15 +54,15 @@ type FormActionBuilder<Schema extends ZodType | EmptyInput, TErr = unknown, Cont
54
54
  */
55
55
  use: <NewContext extends Record<string, unknown>>(middleware: ({ ctx }: {
56
56
  ctx: Context;
57
- }) => Promise<NewContext>) => FormActionBuilder<Schema, TErr, Context & NewContext, Args>;
57
+ }) => Promise<NewContext>) => FormActionBuilder<Schema, Args, TErr, Context & NewContext>;
58
58
  /**
59
59
  * A chainable error handler to handle errors thrown while running the action passed to .run().
60
60
  * It does not receive errors thrown from the context/middleware.
61
61
  * @param processError A function which receives action's errors.
62
62
  * @returns FormActionBuilder
63
63
  */
64
- error: <TErr>(processError: ErrorHandler<Context, TErr, Args>) => FormActionBuilder<Schema, TErr, Context, Args>;
64
+ error: <TErr>(processError: ErrorHandler<Context, TErr, PlainArgs>) => FormActionBuilder<Schema, Args, TErr, Context>;
65
65
  };
66
- declare const formAction: FormActionBuilder<typeof emptyInput, unknown, InitialContext, z.ZodTuple<[], null>>;
66
+ declare const formAction: FormActionBuilder<typeof emptyInput, [], unknown, InitialContext, []>;
67
67
 
68
68
  export { ActionState, FailureState, InvalidState, SuccessState, formAction };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{a as h,b as f}from"./chunk-LPB4R2UB.mjs";import{z as T}from"zod/v4";import{zfd as C}from"zod-form-data";var m=Symbol();function c(e,u=[],n,a){async function A(t){let r={formData:t};for(let d of u){let i=await d({ctx:r});r={...r,...i}}return r}return{args(t){return c(e,u,n,T.tuple(t))},input(t){if(e===m)return c(t,u,n,a);if(e._zod.def.checks)throw new Error("Previous input is not augmentable because it contains an effect.");if(t._zod.def.checks)throw new Error("Your input contains effect which prevents merging it with the previous inputs.");if(e._zod.def.type==="object"){let r=T.object({...e.shape,...t.shape});return c(r,u)}else throw Error("Merging inputs works only for object schemas without effects.")},use(t){return c(e,[...u,t],n,a)},error(t){return c(e,u,t,a)},run:e===m?t=>f(({success:r,failure:d,invalid:i},...s)=>async(E,g)=>{let p=await A(g);if(a){let o=a.safeParse(s);if(!o.success)return i(T.treeifyError(o.error))}try{return r(await t({args:s,ctx:p}))}catch(o){if(n)return d(await n({args:s,error:o,ctx:p}));throw o}}):e===m?void 0:t=>f(({success:r,failure:d,invalid:i},...s)=>{let E=C.formData(e);return async(g,p)=>{let o=await A(p);if(a){let x=a.safeParse(s);if(!x.success)return i(T.treeifyError(x.error))}let l=E.safeParse(p);if(!l.success)return i(T.treeifyError(l.error));let y=l.data;try{return r(await t({args:s,input:y,ctx:o}))}catch(x){if(n)return d(await n({args:s,error:x,ctx:o}));throw x}}})}}var j=c(m);export{f as createFormAction,j as formAction,h as initial};
1
+ import{a as w,b as A}from"./chunk-LPB4R2UB.mjs";import{z as c}from"zod/v4";import{zfd as C}from"zod-form-data";var T=Symbol();function d(r,i=[],a,n){async function f(t){let e={formData:t};for(let p of i){let u=await p({ctx:e});e={...e,...u}}return e}return{args(t){let e=n?[...n,...t]:t;return d(r,i,a,e)},input(t){if(r===T)return d(t,i,a,n);if(r._zod.def.checks)throw new Error("Previous input is not augmentable because it contains an effect.");if(t._zod.def.checks)throw new Error("Your input contains effect which prevents merging it with the previous inputs.");if(r._zod.def.type==="object"){let e=c.object({...r.shape,...t.shape});return d(e,i)}else throw Error("Merging inputs works only for object schemas without effects.")},use(t){return d(r,[...i,t],a,n)},error(t){return d(r,i,t,n)},run:r===T?t=>A(({success:e,failure:p,invalid:u},...s)=>async(y,g)=>{let x=await f(g);if(n){let o=c.tuple(n).safeParse(s);if(!o.success)return u(c.treeifyError(o.error))}try{return e(await t({args:s,ctx:x}))}catch(o){if(a)return p(await a({args:s,error:o,ctx:x}));throw o}}):r===T?void 0:t=>A(({success:e,failure:p,invalid:u},...s)=>{let y=C.formData(r);return async(g,x)=>{let o=await f(x);if(n){let l=c.tuple(n).safeParse(s);if(!l.success)return u(c.treeifyError(l.error))}let m=y.safeParse(x);if(!m.success)return u(c.treeifyError(m.error));let E=m.data;try{return e(await t({args:s,input:E,ctx:o}))}catch(l){if(a)return p(await a({args:s,error:l,ctx:o}));throw l}}})}}var k=d(T);export{A as createFormAction,k as formAction,w as initial};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-form-action",
3
- "version": "2.8.0",
3
+ "version": "2.9.1",
4
4
  "description": "State management helpers for the react form actions.",
5
5
  "repository": {
6
6
  "type": "git",