react-form-action 2.6.0 β†’ 2.8.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 CHANGED
@@ -49,7 +49,7 @@ import { formAction } from "react-form-action";
49
49
  import { z } from "zod";
50
50
 
51
51
  export const subscribeAction = formAction
52
- .input(z.object({ email: z.string().email() }))
52
+ .input(z.object({ email: z.email() }))
53
53
  .run(async ({ input }) => {
54
54
  return input.email;
55
55
  });
@@ -138,7 +138,7 @@ const i18nMiddleware = async () => {
138
138
  const authAction = formAction
139
139
  .use(i18nMiddleware)
140
140
  .use(async ({ ctx: { t } }) =>
141
- console.log("πŸŽ‰ context enhanced by previous middlewares πŸŽ‰", t)
141
+ console.log("πŸŽ‰ context enhanced by previous middlewares πŸŽ‰", t),
142
142
  )
143
143
  .error(async ({ error }) => {
144
144
  if (error instanceof DbError) {
@@ -151,7 +151,7 @@ const authAction = formAction
151
151
  });
152
152
 
153
153
  export const signIn = authAction
154
- .input(z.object({ email: z.string().email() }))
154
+ .input(z.object({ email: z.email() }))
155
155
  // πŸŽ‰ extend the previous input (only without refinements and transforms)
156
156
  .input(z.object({ password: z.string() }))
157
157
  .run(async ({ ctx: { t }, input: { email, password } }) => {
@@ -166,14 +166,14 @@ export const signUp = authAction
166
166
  .input(
167
167
  z
168
168
  .object({
169
- email: z.string().email(),
169
+ email: z.email(),
170
170
  password: z.string(),
171
171
  confirm: z.string(),
172
172
  })
173
173
  .refine((data) => data.password === data.confirm, {
174
174
  message: "Passwords don't match",
175
175
  path: ["confirm"],
176
- })
176
+ }),
177
177
  ) // if using refinement, only one input call is permited, as schema with ZodEffects is not extendable.
178
178
  .run(async ({ ctx: { t }, input: { email, password } }) => {
179
179
  // πŸŽ‰ passwords match!
@@ -193,35 +193,42 @@ export const signUp = authAction
193
193
  The `formAction` builder supports action arguments binding:
194
194
 
195
195
  ```ts
196
- // app/update-user/[userId]/action.tsx
196
+ // app/[locale]/update-user/[userId]/action.tsx
197
197
  import { formAction } from "react-form-action";
198
198
 
199
199
  export const updateUser = formAction
200
- .args([z.string().uuid()])
200
+ .args([z.string().uuid(), z.enum("fr", "en")])
201
201
  .run(async ({ args: [userId] }) => {
202
202
  return userId;
203
203
  // ^? string
204
+ })
205
+ .error(({ args: [_, locale] }) => {
206
+ return locale === "fr"
207
+ ? "Γ‰chec de la mise Γ  jour de l'utilisateur"
208
+ : "Failed to update user";
204
209
  });
205
210
  ```
206
211
 
207
212
  ```tsx
208
- // app/update-user/[userId]/page.tsx
213
+ // app/[locale]/update-user/[userId]/page.tsx
209
214
  import { Action } from "react-form-action/client";
210
215
 
216
+ import { locale as localeParam } from "next/root-params";
211
217
  import { updateUser } from "./action";
212
218
  import { UpdateUserForm } from "./form";
213
219
 
214
220
  export default function Page({
215
221
  params,
216
- }: {
217
- params: Promise<{ userId: string }>;
218
- }) {
222
+ }: PageProps<"/[locale]/update-user/[userId]">) {
223
+ const locale = await localeParam();
219
224
  const { userId } = await params;
220
225
 
221
- const action = updateUser.bind(null, userId);
222
-
223
226
  return (
224
- <Action action={action} initialData="">
227
+ <Action
228
+ action={updateUser}
229
+ args={[userId, await localeParam()]}
230
+ initialData=""
231
+ >
225
232
  <SubscribeForm />
226
233
  </Action>
227
234
  );
@@ -291,7 +298,7 @@ export const updateUser = createFormAction<
291
298
 
292
299
  return failure({ message: "Failed to update user." });
293
300
  }
294
- }
301
+ },
295
302
  );
296
303
  ```
297
304
 
@@ -301,7 +308,7 @@ The action creator supports arguments binding:
301
308
  export const updateUser = createFormAction(
302
309
  (
303
310
  { success, failure, invalid },
304
- userId: string /* Here you can specify multiple arguments */
311
+ userId: string /* Here you can specify multiple arguments */,
305
312
  ) =>
306
313
  async (prevState, formData) => {
307
314
  try {
@@ -327,7 +334,7 @@ export const updateUser = createFormAction(
327
334
  } catch (error) {
328
335
  // handle error
329
336
  }
330
- }
337
+ },
331
338
  );
332
339
 
333
340
  // call bind as usuall, the "123" becomes the "userId"
@@ -421,7 +428,7 @@ export const signUpAction = authAction
421
428
  z
422
429
  .object({
423
430
  user: z.object({
424
- email: z.string().email(),
431
+ email: z.email(),
425
432
  name: z.string(),
426
433
  }),
427
434
  password: z.string().min(8),
@@ -429,7 +436,7 @@ export const signUpAction = authAction
429
436
  })
430
437
  .refine((data) => data.password === data.confirm, {
431
438
  message: "Passwords don't match",
432
- })
439
+ }),
433
440
  )
434
441
  .run(async ({ ctx, input }) => {
435
442
  return null;
package/dist/client.d.mts CHANGED
@@ -3,10 +3,16 @@ 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> = PropsWithChildren<{
7
- action: FormAction<Data, Error, ValidationError>;
6
+ type ActionProps<Data, Error, ValidationError, Arguments extends unknown[] = []> = PropsWithChildren<Arguments extends [unknown] ? {
7
+ action: FormAction<Data, Error, ValidationError, FormData, Arguments>;
8
+ args: Arguments;
8
9
  initialData: Data;
9
10
  permalink?: string;
11
+ } : {
12
+ action: FormAction<Data, Error, ValidationError, FormData, Arguments>;
13
+ initialData: Data;
14
+ args?: undefined;
15
+ permalink?: string;
10
16
  }>;
11
17
  type ActionStatusFlags<T extends ActionState<unknown, unknown, unknown>["type"] | unknown = unknown> = {
12
18
  isInitial: T extends "initial" ? true : false;
@@ -28,8 +34,8 @@ type SpreadActionContext<Data = unknown, Error = unknown, ValidationError = unkn
28
34
  /**
29
35
  * A hook to consume the form action state from the context.
30
36
  */
31
- declare function useActionContext<Data, Error, ValidationError>(action?: FormAction<Data, Error, ValidationError>): SpreadActionContext<Data, Error, ValidationError>;
32
- declare function Action<Data, Error, ValidationError>({ children, action: formAction, initialData, permalink, }: ActionProps<Data, Error, ValidationError>): React__default.JSX.Element;
37
+ declare function useActionContext<Data, Error, ValidationError, Args extends unknown[] = []>(action?: FormAction<Data, Error, ValidationError, FormData, Args>): SpreadActionContext<Data, Error, ValidationError>;
38
+ declare function Action<Data, Error, ValidationError, Args extends unknown[] = []>({ children, action: formAction, initialData, args, permalink, }: ActionProps<Data, Error, ValidationError, Args>): React__default.JSX.Element;
33
39
 
34
40
  type FormProps = Omit<JSX.IntrinsicElements["form"], "action"> & {
35
41
  /**
@@ -65,7 +71,7 @@ declare function ZodFieldError<Errors extends $ZodErrorTree<object>, Name extend
65
71
  /**
66
72
  * Creates a typed components for actions created with the formAction builder.
67
73
  */
68
- declare function createComponents<Data, Error, ValidationError extends $ZodErrorTree<any>>(action: FormAction<Data, Error, ValidationError>): {
74
+ declare function createComponents<Data, Error, ValidationError extends $ZodErrorTree<any>, Args extends unknown[] = []>(action: FormAction<Data, Error, ValidationError, FormData, Args>): {
69
75
  FieldError: <Name extends "" | InferZodErrorPaths<ValidationError>>({ name, children, }: {
70
76
  name: Name;
71
77
  children?: (props: ZodFieldErrorChildrenProps<Name>) => React__default.ReactNode;
@@ -79,6 +85,15 @@ declare function createComponents<Data, Error, ValidationError extends $ZodError
79
85
  data: Data;
80
86
  }) => React__default.ReactNode;
81
87
  }) => React__default.ReactNode;
88
+ Invalid: ({ children, }: PropsWithChildren | {
89
+ children?: (props: {
90
+ isInvalid: false;
91
+ validationError: null;
92
+ } | {
93
+ isInvalid: true;
94
+ validationError: ValidationError;
95
+ }) => React__default.ReactNode;
96
+ }) => React__default.ReactNode;
82
97
  };
83
98
 
84
99
  /**
package/dist/client.mjs CHANGED
@@ -1 +1 @@
1
- "use client";import{a as u}from"./chunk-LPB4R2UB.mjs";import S from"react";import{createContext as A,use as P}from"react";import{useActionState as y}from"react";var c={isInitial:!1,isInvalid:!1,isFailure:!1,isSuccess:!1},m=A(null);function a(o){let t=P(m);if(!t)throw new Error("ActionContext must be initialized before use. Is your useActionContext hook wrapped with an <Action> Component?");return t}function b({children:o,action:t,initialData:i,permalink:e}){let[r,n,s]=y(t,u(i),e),d=r.type==="initial"?{...r,...c,action:n,isPending:s,isInitial:!0}:r.type==="invalid"?{...r,...c,action:n,isPending:s,isInvalid:!0}:r.type==="failure"?{...r,...c,action:n,isPending:s,isFailure:!0}:{...r,...c,action:n,isPending:s,isSuccess:!0};return S.createElement(m,{value:d},o)}import h,{startTransition as F}from"react";function I({autoReset:o=!1,...t}){let{action:i}=a();return h.createElement("form",{...o?{action:i}:{onSubmit:r=>{r.preventDefault();let n=r.currentTarget;F(()=>{i(new FormData(n))})}},...t})}import p from"react";import f from"react";var C=".",l=Object.freeze({errors:Object.freeze([])});function E({errors:o,name:t,children:i=({error:e})=>f.createElement(f.Fragment,null,e)}){if(t.length===0)return i({name:t,error:o.errors[0],errors:o.errors});let e=t.split(C),r=o;for(;e.length;){let n=e.shift();if(!r.properties){r=l;break}r=r.properties[n]??l}return i({name:t,error:r.errors[0],errors:r.errors})}function J(o){function t({name:e,children:r}){let{isInvalid:n,validationError:s}=a(o);return p.createElement(E,{errors:s??l,name:e},r??(({error:x})=>n&&p.createElement(p.Fragment,null,x)))}function i({children:e}){let{isSuccess:r,data:n}=a(o);return typeof e=="function"?e(r?{isSuccess:r,data:n}:{isSuccess:r,data:n}):r&&e}return{FieldError:t,Success:i}}function q({children:o}){let{isPending:t}=a();return typeof o=="function"?o({isPending:t}):t&&o}export{b as Action,I as Form,q as Pending,E as ZodFieldError,J as createComponents,l as noError,a 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 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};
package/dist/index.d.mts CHANGED
@@ -9,9 +9,10 @@ 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> = (params: {
12
+ type ErrorHandler<Context, Err, Args extends ZodTuple> = (params: {
13
13
  error: unknown;
14
14
  ctx: Context;
15
+ args: z.infer<Args>;
15
16
  }) => Promise<Err>;
16
17
  type InitialContext = Record<"formData", FormData>;
17
18
  /**
@@ -60,7 +61,7 @@ type FormActionBuilder<Schema extends ZodType | EmptyInput, TErr = unknown, Cont
60
61
  * @param processError A function which receives action's errors.
61
62
  * @returns FormActionBuilder
62
63
  */
63
- error: <TErr>(processError: ErrorHandler<Context, TErr>) => FormActionBuilder<Schema, TErr, Context, Args>;
64
+ error: <TErr>(processError: ErrorHandler<Context, TErr, Args>) => FormActionBuilder<Schema, TErr, Context, Args>;
64
65
  };
65
66
  declare const formAction: FormActionBuilder<typeof emptyInput, unknown, InitialContext, z.ZodTuple<[], null>>;
66
67
 
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 g}from"zod-form-data";var m=Symbol();function c(e,s=[],n,a){async function A(t){let r={formData:t};for(let i of s){let u=await i({ctx:r});r={...r,...u}}return r}return{args(t){return c(e,s,n,T.tuple(t))},input(t){if(e===m)return c(t,s,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,s)}else throw Error("Merging inputs works only for object schemas without effects.")},use(t){return c(e,[...s,t],n,a)},error(t){return c(e,s,t,a)},run:e===m?t=>f(({success:r,failure:i,invalid:u},...d)=>async(E,y)=>{let p=await A(y);if(a){let o=a.safeParse(d);if(!o.success)return u(T.treeifyError(o.error))}try{return r(await t({args:d,ctx:p}))}catch(o){if(n)return i(await n({error:o,ctx:p}));throw o}}):e===m?void 0:t=>f(({success:r,failure:i,invalid:u},...d)=>{let E=g.formData(e);return async(y,p)=>{let o=await A(p);if(a){let x=a.safeParse(d);if(!x.success)return u(T.treeifyError(x.error))}let l=E.safeParse(p);if(!l.success)return u(T.treeifyError(l.error));let C=l.data;try{return r(await t({args:d,input:C,ctx:o}))}catch(x){if(n)return i(await n({error:x,ctx:o}));throw x}}})}}var j=c(m);export{f as createFormAction,j as formAction,h as initial};
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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-form-action",
3
- "version": "2.6.0",
3
+ "version": "2.8.0",
4
4
  "description": "State management helpers for the react form actions.",
5
5
  "repository": {
6
6
  "type": "git",