react-form-action 1.5.0 → 2.0.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
@@ -1,28 +1,29 @@
1
1
  # react-form-action
2
2
 
3
- End-to-end typesafe success, error & validation state control for Next.js 14 form actions.
3
+ End-to-end typesafe success, error & validation state control for Next.js form actions.
4
4
 
5
5
  ## Features
6
6
 
7
7
  **Action Creator**
8
8
 
9
- - ✅ Provides `"invalid" | "success" | "failure"` response objects.
9
+ - ✅ Provides envelope objects with `"initial" | "invalid" | "success" | "failure"` response types.
10
10
  - ✅ Define generic payload for each of the response type.
11
11
 
12
- **Form Action builder**
12
+ **tRPC-like Form Action builder**
13
13
 
14
- - ✅ tRPC-like builder API for `.input(zodSchema)` & context `.use(middleware)`
15
- - ✅ Parses `formData` with [`zod-form-data`](https://www.npmjs.com/package/zod-form-data)
14
+ - ✅ Define payload schema with the `.input(zodSchema)` to validate the `formData`
15
+ - ✅ Reuse business logic with the `.use(middleware)` method.
16
+ - ✅ Reuse error handling with the `.error(handler)`.
16
17
 
17
- **Stateful `<Form />`**
18
+ **React Context access with the `<Action action={myFormAction} />` component**
18
19
 
19
- - ✅ `<Form />` component reads the action's response.
20
- - ✅ Computes progress meta-state like `isInvalid`, `isSuccess` and more.
20
+ - ✅ The `useActionState()` accessible via the `useActionContext()` hook.
21
+ - ✅ Computes progress flags like `isInvalid`, `isSuccess` based on the envelope type.
21
22
 
22
- **Context**
23
+ **Context-bound `<Form />` component**
23
24
 
24
- - ✅ Access the action state via `useFormContext()` hook.
25
- - ✅ Keeps JSX markup free of render-props.
25
+ - ✅ Reads the `action` from the `<Action />` context.
26
+ - ✅ Opt-out from the default form reset after action submit.
26
27
 
27
28
  ## Install
28
29
 
@@ -34,10 +35,93 @@ npm i react-form-action zod-form-data
34
35
  <img alt="NPM Version" src="https://img.shields.io/npm/v/react-form-action?style=for-the-badge&labelColor=24292e">
35
36
  </a>
36
37
 
38
+ ## Getting Started
39
+
40
+ #### 1️⃣ Create a Server Action
41
+
42
+ ```tsx
43
+ // app/subscribe/action.ts
44
+ "use server";
45
+
46
+ import { formAction } from "react-form-action";
47
+ import { z } from "zod";
48
+
49
+ export const subscribeAction = formAction
50
+ .input(z.object({ email: z.string().email() }))
51
+ .run(async ({ input }) => {
52
+ return input.email;
53
+ });
54
+ ```
55
+
56
+ #### 2️⃣ Create a Client Form Component
57
+
58
+ ```tsx
59
+ // app/subscribe/SubscribeForm.tsx
60
+ "use client";
61
+
62
+ import {
63
+ Action,
64
+ Form,
65
+ Pending,
66
+ useActionContext,
67
+ } from "react-form-action/client";
68
+
69
+ import { subscribeAction } from "./action";
70
+
71
+ const { FieldError } = createComponents(subscribeAction);
72
+
73
+ export function SubscribeForm() {
74
+ return (
75
+ <Action action={subscribeAction} initialData="">
76
+ <SuccessMessage />
77
+ <Form>
78
+ <input name="email" />
79
+ {/*💡 The name prop supports autocompletion */}
80
+ <FieldError name="email" />
81
+ </Form>
82
+ <SubmitButton />
83
+ <Pending>Please wait...</Pending>
84
+ </Action>
85
+ );
86
+ }
87
+
88
+ function SuccessMessage() {
89
+ // Pass action to cast the type of the "data" value
90
+ const { isSuccess, data } = useActionContext(subscribeAction);
91
+
92
+ return isSuccess && <p>Email {data} was registered.</p>;
93
+ }
94
+
95
+ function SubmitButton() {
96
+ // no need for action when reading a generic value
97
+ const { isPending } = useActionContext();
98
+
99
+ return (
100
+ <button type="submit" disabled={isPending}>
101
+ {isPending ? "Submitting..." : "Submit"}
102
+ </button>
103
+ );
104
+ }
105
+ ```
106
+
107
+ #### 3️⃣ Render the form on a Page
108
+
109
+ ```tsx
110
+ // app/subscribe/page.tsx
111
+
112
+ import { SubscribeForm } from "./SubscribeForm";
113
+
114
+ export function Page() {
115
+ return <SubscribeForm />;
116
+ }
117
+ ```
118
+
37
119
  ## Usage
38
120
 
39
121
  ### `formAction` builder
40
122
 
123
+ The [`zod-form-data`](https://www.npmjs.com/package/zod-form-data) powered action builder.
124
+
41
125
  ```ts
42
126
  // app/actions/auth.ts
43
127
  "use server";
@@ -105,7 +189,9 @@ export const signUp = authAction
105
189
  });
106
190
  ```
107
191
 
108
- ### Server Action (usable in Next.js)
192
+ ### Action Creator
193
+
194
+ Low-level action creator, which provides the `success`, `failure` and `invalid` envelope constructors. With the `createFormAction` you must handle the native `FormData` by yourself.
109
195
 
110
196
  ```ts
111
197
  "use server";
@@ -170,61 +256,21 @@ export const updateUser = createFormAction<
170
256
  );
171
257
  ```
172
258
 
173
- ### `<Form>` Component
174
-
175
- ```tsx
176
- "use client";
177
- // Form connects the formAction to the formState & provides the meta state props via render prop
178
- import { Form } from "react-form-action/client";
179
-
180
- import { updateUser } from "@/actions";
259
+ ### Action Context
181
260
 
182
- export function UpdateUserForm() {
183
- return (
184
- <Form action={updateUser} initialData="foobar">
185
- {({
186
- error,
187
- data,
188
- validationError,
189
- isPending,
190
- isFailure,
191
- isInvalid,
192
- isSuccess,
193
- isInitial,
194
- }) => (
195
- <>
196
- {/* safely access the data or error by checking the mutually exclusive boolean flags: */}
197
- {isSuccess && <p className="success-message">{data}</p>}
198
- {isFailure && <p className="error-message">{error.message}</p>}
199
- <input type="text" name="name" disabled={isPending} />
200
- {isInvalid && (
201
- <span className="input-error">{validationError.name}</span>
202
- )}
203
-
204
- <button disabled={isPending}>
205
- {isPending ? "Submitting..." : "Submit"}
206
- </button>
207
- </>
208
- )}
209
- </Form>
210
- );
211
- }
212
- ```
213
-
214
- ### Context Form
261
+ The `<Action>` components enables you to access your `action`'s state with the `useActionContext()` hook:
215
262
 
216
263
  ```tsx
217
- // Example route: /app/auth/signup/SignUpForm.tsx
264
+ // 👉 Define standalone client form component (e.g. /app/auth/signup/SignUpForm.tsx)
218
265
  "use client";
219
266
 
220
- import { createForm, Pending, useFormContext } from "react-form-action/client";
221
- import { signupAction } from "./actions";
267
+ import { Action, Form, useActionContext } from "react-form-action/client";
268
+ import type { PropsWithChildren } from "react";
222
269
 
223
- // This call will succeed only in client component
224
- const { Form } = createForm(signupAction);
270
+ import { signupAction } from "./action";
225
271
 
226
- function Error() {
227
- // read any state from the FormContext:
272
+ function Pending({ children }: PropsWithChildren) {
273
+ // read any state from the ActionContext:
228
274
  const {
229
275
  error,
230
276
  data,
@@ -233,36 +279,64 @@ function Error() {
233
279
  isFailure,
234
280
  isInvalid,
235
281
  isSuccess,
236
- isInitial
237
- } = useFormContext();
282
+ isInitial,
283
+ } = useActionContext();
238
284
 
239
- return isFailure && "Failed to submit". // use the error somehow
285
+ return isPending && children;
240
286
  }
241
287
 
242
- // render this form on your RSC page (/app/auth/signup/page.tsx)
288
+ // 💡 render this form on your RSC page (/app/auth/signup/page.tsx)
243
289
  export function SignupForm() {
244
- // This Form does not use render props.
245
290
  return (
246
- <Form>
247
- <input name="email" />
248
- <input name="password" />
291
+ <Action action={signupAction}>
292
+ <Form>
293
+ <input name="email" />
294
+ <input name="password" />
295
+ </Form>
296
+ {/* 🎆 Read the pending state outside the <Form> */}
249
297
  <Pending>
250
- {/* This renders only when the action is pending. */}
298
+ {/* This renders only when the action is pending. 😎 */}
251
299
  <p>Please wait...</p>
252
300
  </Pending>
253
- </Form>
301
+ </Action>
302
+ );
303
+ }
304
+ ```
305
+
306
+ ### `<Form>` Component
307
+
308
+ The `<form>` submits the action in `onSubmit` handler to [prevent automatic form reset](https://github.com/facebook/react/issues/29034).
309
+ Pass `autoReset` prop to use the `action` prop instead and keep the default reset.
310
+
311
+ ```tsx
312
+ "use client";
313
+
314
+ import { Action, Form } from "react-form-action/client";
315
+
316
+ import { updateUser } from "./action";
317
+
318
+ export function UpdateUserForm() {
319
+ return (
320
+ <Action action={updateUser}>
321
+ <Form autoReset>{/* ... */}</Form>
322
+ </Action>
254
323
  );
255
324
  }
256
325
  ```
257
326
 
258
- ### `<ZodFieldError>` Component
327
+ ### Context Bound Components `createComponents()`
328
+
329
+ Use the `createComponents(action)` helper to create components which use the ActionContext and have types bound to the action type.
259
330
 
260
- Actions created with `formAction` builder will have the `validationError` of [`ZodFormattedError`](https://zod.dev/ERROR_HANDLING?id=formatting-errors) type.
261
- To easily access the nested error, use the helper `<ZodFieldError>` component:
331
+ ### `<FielError>` Component
262
332
 
263
333
  ```tsx
264
334
  "use client";
265
- import { Form, ZodFieldError } from "react-form-action/client";
335
+
336
+ // ⚠️ createComponents is usable only in "use client" components
337
+ import { Form, createComponents } from "react-form-action/client";
338
+
339
+ import { authAction } from "./actions";
266
340
 
267
341
  export const signUp = authAction
268
342
  .input(
@@ -280,51 +354,47 @@ export const signUp = authAction
280
354
  })
281
355
  )
282
356
  .run(async ({ ctx, input }) => {
283
- // implementation
357
+ return null;
284
358
  });
285
359
 
360
+ // 🌟 The FieldError is now bound do the signUp input schema which allows autocompletion for its "name" prop
361
+ // ⚠️ Usable only with actions created with the formAction builder
362
+ const { FieldError } = createComponents(signUp);
363
+
286
364
  export function SignUpForm() {
287
365
  return (
288
- <Form action={signUp} initialData="">
289
- {({ isInvalid, validationError }) =>
290
- {/* Render ZodFieldError behind the isInvalid flag to narrow type (omits the possibility of null) */}
291
- isInvalid && (
292
- <>
293
- {/*
294
- When the "name" prop is ommited, the top-level error will be rendered e.g.:
295
- "Passwords don't match"
296
- */}
297
- <ZodFieldError errors={validationError} />
298
- {/* Access fields by their name: */}
299
- <ZodFieldError errors={validationError} name="password" />
300
- {/* Access nested fields by dot access notation: */}
301
- <ZodFieldError errors={validationError} name="user.email" />
302
- </>
303
- )
304
- }
305
- </Form>
366
+ <Action action={signUp} initialData={null}>
367
+ <Form>
368
+ {/* 1️⃣ When the "name" prop is ommited, the top-level error will be rendered e.g.:
369
+ "Passwords don't match" */}
370
+ <FieldError />
371
+ {/* 2️⃣ Access fields by their name: */}
372
+ <FieldError name="password" />
373
+ {/* 3️⃣ Access nested fields by dot access notation: */}
374
+ <FieldError name="user.email" />
375
+ </Form>
376
+ </Action>
306
377
  );
307
378
  }
308
379
  ```
309
380
 
310
- ### Bonus `<FormStatus>`
381
+ ### `<Pending>`
311
382
 
312
- The `useFormStatus` hook data exposed via render prop:
383
+ Render children when the action is pending:
313
384
 
314
385
  ```tsx
315
- import { FormStatus } from "react-form-action/client";
386
+ import { Action, Pending } from "react-form-action/client";
316
387
 
317
- // <FormStatus> alleviates the need to create a separate <SubmitButton> component using the useFormStatus hook
388
+ import { Spinner } from "./components";
318
389
 
319
390
  return function MyForm() {
320
391
  return (
321
- <form action={action}>
322
- <FormStatus>
323
- {({ pending }) => (
324
- <button type="submit">{pending ? "Submitting..." : "Submit"} </button>
325
- )}
326
- </FormStatus>
327
- </form>
392
+ <Action action={action}>
393
+ {/* 👉 Unlike the React.useFormStatus() hook, we don't need here the <form> element at all. */}
394
+ <Pending>
395
+ <Spinner />
396
+ </Pending>
397
+ </Action>
328
398
  );
329
399
  };
330
400
  ```
@@ -0,0 +1 @@
1
+ function l(a){return{type:"initial",data:a,error:null,validationError:null}}function n(a){function t(r){return{type:"success",data:r,error:null,validationError:null}}function o(r){return{type:"failure",data:null,error:r,validationError:null}}function i(r){return{type:"invalid",data:null,error:null,validationError:r}}return(r,e)=>a({success:t,failure:o,invalid:i})(r,e)}export{l as a,n as b};
package/dist/client.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
- import React__default, { PropsWithChildren, FormHTMLAttributes } from 'react';
3
- import { b as FormAction, A as ActionState, a as InitialState, I as InvalidState, F as FailureState, S as SuccessState } from './createFormAction-4e719362.js';
2
+ import React__default, { PropsWithChildren, JSX } from 'react';
3
+ import { b as FormAction, A as ActionState, a as InitialState, I as InvalidState, F as FailureState, S as SuccessState } from './createFormAction-277e6e21.js';
4
4
  import { ZodFormattedError } from 'zod';
5
5
  import { RenderProp } from 'react-render-prop-type';
6
6
  import { useFormStatus } from 'react-dom';
@@ -30,10 +30,10 @@ type SpreadActionContext<Data = unknown, Error = unknown, ValidationError = unkn
30
30
  /**
31
31
  * A hook to consume the form action state from the context.
32
32
  */
33
- declare function useActionContext<Data, Error, ValidationError>(): SpreadActionContext<Data, Error, ValidationError>;
33
+ declare function useActionContext<Data, Error, ValidationError>(action?: FormAction<Data, Error, ValidationError>): SpreadActionContext<Data, Error, ValidationError>;
34
34
  declare function Action<Data, Error, ValidationError>({ children, action: formAction, initialData, permalink, }: ActionProps<Data, Error, ValidationError>): React__default.JSX.Element;
35
35
 
36
- type ActionFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, "action"> & {
36
+ type FormProps = Omit<JSX.IntrinsicElements["form"], "action"> & {
37
37
  /**
38
38
  * Opt-in into automatic form reset by using the form "action" prop.
39
39
  * By default, the onSubmit with a custom transition which opts-out of the implicit form reset.
@@ -42,7 +42,7 @@ type ActionFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, "action"> & {
42
42
  */
43
43
  autoReset?: boolean;
44
44
  };
45
- declare function ActionForm({ autoReset, ...props }: ActionFormProps): React__default.JSX.Element;
45
+ declare function Form({ autoReset, ...props }: FormProps): JSX.Element;
46
46
 
47
47
  declare const SEPARATOR: ".";
48
48
  type Paths<Err> = {
@@ -66,41 +66,6 @@ declare function createComponents<Data, Error, ValidationError extends ZodFormat
66
66
  }>) => false | React__default.JSX.Element;
67
67
  };
68
68
 
69
- type FormStateProps<Data, Error, ValidationError, Payload> = {
70
- action: (state: ActionState<Data, Error, ValidationError>, payload: Payload) => Promise<ActionState<Data, Error, ValidationError>>;
71
- initialData: Data;
72
- permalink?: string;
73
- /**
74
- * Opt-in into automatic form reset by using the form "action" prop.
75
- * By default, the onSubmit with a custom transition which opts-out of the implicit form reset.
76
- * See for more. https://github.com/facebook/react/issues/29034
77
- * @default false
78
- */
79
- autoReset?: boolean;
80
- };
81
- declare function initial<Data>(data: Data): InitialState<Data>;
82
- type FormStatusFlags<T extends ActionState<unknown, unknown, unknown>["type"] | unknown = unknown> = {
83
- isInitial: T extends "initial" ? true : false;
84
- isInvalid: T extends "invalid" ? true : false;
85
- isFailure: T extends "failure" ? true : false;
86
- isSuccess: T extends "success" ? true : false;
87
- };
88
- type FormMetaState<T extends ActionState<unknown, unknown, unknown>> = T & FormStatusFlags<T["type"]> & {
89
- isPending: boolean;
90
- };
91
- type FormProps<Data, Error, ValidationError> = Omit<FormHTMLAttributes<HTMLFormElement>, "action" | "children"> & FormStateProps<Data, Error, ValidationError, FormData> & RenderProp<FormMetaState<InitialState<Data>> | FormMetaState<InvalidState<ValidationError>> | FormMetaState<FailureState<Error>> | FormMetaState<SuccessState<Data>>>;
92
- declare function Form<Data, Error, ValidationError>({ children, action, initialData, permalink, autoReset, ...props }: FormProps<Data, Error, ValidationError>): React__default.JSX.Element;
93
-
94
- /**
95
- * A context exposing the form action state.
96
- * @private You don't need to use this component directly. Use the `createForm` function instead.
97
- */
98
- declare const FormContext: React.Context<FormMetaState<ActionState<unknown, unknown, unknown>> | null>;
99
- /**
100
- * A hook to consume the form action state from the context.
101
- */
102
- declare const useFormContext: () => FormMetaState<ActionState<unknown, unknown, unknown>>;
103
-
104
69
  declare function FormStatus({ children, }: RenderProp<ReturnType<typeof useFormStatus>>): React.ReactNode;
105
70
 
106
71
  /**
@@ -108,4 +73,4 @@ declare function FormStatus({ children, }: RenderProp<ReturnType<typeof useFormS
108
73
  */
109
74
  declare function Pending({ children }: PropsWithChildren): React.ReactNode;
110
75
 
111
- export { Action, ActionContextState, ActionForm, ActionFormProps, ActionProps, Form, FormContext, FormMetaState, FormProps, FormStateProps, FormStatus, InferZodErrorPaths, Paths, Pending, ZodFieldError, ZodFieldErrorProps, createComponents, initial, useActionContext, useFormContext };
76
+ export { Action, ActionContextState, ActionProps, Form, FormProps, FormStatus, InferZodErrorPaths, Paths, Pending, ZodFieldError, ZodFieldErrorProps, createComponents, useActionContext };
package/dist/client.mjs CHANGED
@@ -1 +1 @@
1
- "use client";import y from"react";import{createContext as A,use as P}from"react";import{useActionState as D}from"react";function k(t){return{type:"initial",data:t,error:null,validationError:null}}var u={isInitial:!1,isInvalid:!1,isFailure:!1,isSuccess:!1},d=A(null);function s(){let t=P(d);if(!t)throw new Error("ActionContext must be initialized before use. Is your useActionContext hook wrapped with an <Action> Component?");return t}function j({children:t,action:o,initialData:n,permalink:a}){let[r,i,e]=D(o,k(n),a),l=r.type==="initial"?{...r,...u,action:i,isPending:e,isInitial:!0}:r.type==="invalid"?{...r,...u,action:i,isPending:e,isInvalid:!0}:r.type==="failure"?{...r,...u,action:i,isPending:e,isFailure:!0}:{...r,...u,action:i,isPending:e,isSuccess:!0};return y.createElement(d,{value:l},t)}import T,{startTransition as w}from"react";function B({autoReset:t=!1,...o}){let{action:n}=s();return T.createElement("form",{...t?{action:n}:{onSubmit:r=>{r.preventDefault();let i=r.currentTarget;w(()=>{n(new FormData(i))})}},...o})}import v from"react";import f from"react";var C=".",h=Object.freeze({_errors:Object.freeze([])});function E({errors:t,name:o="",children:n=({errors:a})=>f.createElement(f.Fragment,null,a[0])}){if(o.length===0)return n({errors:t._errors});let a=o.split(C),r=t;for(;a.length;){let i=a.shift();r=r[i]??h}return n({errors:r._errors})}function Y(t){function o({name:n}){let{isInvalid:a,validationError:r}=s();return a&&v.createElement(E,{errors:r,name:n})}return{FieldError:o}}import I,{useTransition as g}from"react";import{useActionState as b}from"react";function V(t){return{type:"initial",data:t,error:null,validationError:null}}var m={isInitial:!1,isInvalid:!1,isFailure:!1,isSuccess:!1};function et({children:t,action:o,initialData:n,permalink:a,autoReset:r=!1,...i}){let[e,l,c]=b(o,V(n),a),[,S]=g(),F=e.type==="initial"?{...e,...m,isPending:c,isInitial:!0}:e.type==="invalid"?{...e,...m,isPending:c,isInvalid:!0}:e.type==="failure"?{...e,...m,isPending:c,isFailure:!0}:{...e,...m,isPending:c,isSuccess:!0};return I.createElement("form",{...r?{action:l}:{onSubmit:p=>{p.preventDefault();let x=p.currentTarget;S(()=>{l(new FormData(x))})}},...i},t(F))}import{createContext as M,use as Z}from"react";var R=M(null),it=()=>{let t=Z(R);if(!t)throw new Error("Form Context must be initialized before use.");return t};import{useFormStatus as H}from"react-dom";function ct({children:t}){return t(H())}function pt({children:t}){let{isPending:o}=s();return o&&t}export{j as Action,B as ActionForm,et as Form,R as FormContext,ct as FormStatus,pt as Pending,E as ZodFieldError,Y as createComponents,V as initial,s as useActionContext,it as useFormContext};
1
+ "use client";import{a as c}from"./chunk-HL47DG4L.mjs";import f from"react";import{createContext as E,use as x}from"react";import{useActionState as S}from"react";var p={isInitial:!1,isInvalid:!1,isFailure:!1,isSuccess:!1},m=E(null);function a(t){let o=x(m);if(!o)throw new Error("ActionContext must be initialized before use. Is your useActionContext hook wrapped with an <Action> Component?");return o}function v({children:t,action:o,initialData:e,permalink:n}){let[r,i,s]=S(o,c(e),n),d=r.type==="initial"?{...r,...p,action:i,isPending:s,isInitial:!0}:r.type==="invalid"?{...r,...p,action:i,isPending:s,isInvalid:!0}:r.type==="failure"?{...r,...p,action:i,isPending:s,isFailure:!0}:{...r,...p,action:i,isPending:s,isSuccess:!0};return f.createElement(m,{value:d},t)}import y,{startTransition as A}from"react";function V({autoReset:t=!1,...o}){let{action:e}=a();return y.createElement("form",{...t?{action:e}:{onSubmit:r=>{r.preventDefault();let i=r.currentTarget;A(()=>{e(new FormData(i))})}},...o})}import h from"react";import l from"react";var F=".",P=Object.freeze({_errors:Object.freeze([])});function u({errors:t,name:o="",children:e=({errors:n})=>l.createElement(l.Fragment,null,n[0])}){if(o.length===0)return e({errors:t._errors});let n=o.split(F),r=t;for(;n.length;){let i=n.shift();r=r[i]??P}return e({errors:r._errors})}function $(t){function o({name:e}){let{isInvalid:n,validationError:r}=a(t);return n&&h.createElement(u,{errors:r,name:e})}return{FieldError:o}}import{useFormStatus as C}from"react-dom";function X({children:t}){return t(C())}function q({children:t}){let{isPending:o}=a();return o&&t}export{v as Action,V as Form,X as FormStatus,q as Pending,u as ZodFieldError,$ as createComponents,a as useActionContext};
@@ -24,10 +24,11 @@ type SuccessState<T> = {
24
24
  };
25
25
  type ActionState<Data, Error, ValidationError = Record<string, never>> = InitialState<Data> | InvalidState<ValidationError> | FailureState<Error> | SuccessState<Data>;
26
26
  type FormAction<Data, Error = Data, ValidationError = Record<string, never>, Payload = FormData> = (state: ActionState<Data, Error, ValidationError>, payload: Payload) => Promise<ActionState<Data, Error, ValidationError>>;
27
+ declare function initial<Data>(data: Data): InitialState<Data>;
27
28
  declare function createFormAction<Data, Error = Data, ValidationError = Record<string, never>, Payload = FormData>(formAction: (params: {
28
29
  success: (data: Data) => SuccessState<Data>;
29
30
  failure: (error: Error) => FailureState<Error>;
30
31
  invalid: (validationError: ValidationError) => InvalidState<ValidationError>;
31
32
  }) => FormAction<Data, Error, ValidationError, Payload>): (state: ActionState<Data, Error, ValidationError>, payload: Payload) => Promise<ActionState<Data, Error, ValidationError>>;
32
33
 
33
- export { ActionState as A, FailureState as F, InvalidState as I, SuccessState as S, InitialState as a, FormAction as b, createFormAction as c };
34
+ export { ActionState as A, FailureState as F, InvalidState as I, SuccessState as S, InitialState as a, FormAction as b, createFormAction as c, initial as i };
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as ActionState, I as InvalidState, F as FailureState, S as SuccessState } from './createFormAction-4e719362.js';
2
- export { b as FormAction, a as InitialState, c as createFormAction } from './createFormAction-4e719362.js';
1
+ import { A as ActionState, I as InvalidState, F as FailureState, S as SuccessState } from './createFormAction-277e6e21.js';
2
+ export { b as FormAction, a as InitialState, c as createFormAction, i as initial } from './createFormAction-277e6e21.js';
3
3
  import { ZodTypeAny, ZodType, ZodObject, ZodRawShape, objectUtil, z, ZodSchema, ZodEffects } from 'zod';
4
4
 
5
5
  declare const emptyInput: unique symbol;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- function y(e){function o(n){return{type:"success",data:n,error:null,validationError:null}}function a(n){return{type:"failure",data:null,error:n,validationError:null}}function s(n){return{type:"invalid",data:null,error:null,validationError:n}}return(n,S)=>e({success:o,failure:a,invalid:s})(n,S)}import{ZodFirstPartyTypeKind as C}from"zod";import{zfd as w}from"zod-form-data";var l=Symbol();function d(e,o=[],a){async function s(t){let r={formData:t};for(let i of o){let u=await i({ctx:r});r={...r,...u}}return r}return{input(t){if(e===l)return d(t,o,a);if(e._def.effect)throw new Error("Previous input is not augmentable because it contains an effect.");if(t._def.effect)throw new Error("Your input contains effect which prevents merging it with the previous inputs.");if(e._def.typeName===C.ZodObject){let r=e.merge(t);return d(r,o)}else throw Error("Merging inputs works only for object schemas without effects.")},use(t){return d(e,[...o,t],a)},error(t){return d(e,o,t)},run:e===l?t=>y(({success:r,failure:i})=>async(u,x)=>{let m=await s(x);try{return r(await t({ctx:m}))}catch(c){if(a)return i(a({error:c,ctx:m}));throw c}}):e===l?void 0:t=>y(({success:r,failure:i,invalid:u})=>{let x=w.formData(e);return async(m,c)=>{let f=await s(c),p=x.safeParse(c);if(!p.success)return u(p.error.format());let h=p.data;try{return r(await t({input:h,ctx:f}))}catch(E){if(a)return i(a({error:E,ctx:f}));throw E}}})}}var P=d(l);export{y as createFormAction,P as formAction};
1
+ import{a as A,b as p}from"./chunk-HL47DG4L.mjs";import{ZodFirstPartyTypeKind as C}from"zod";import{zfd as w}from"zod-form-data";var x=Symbol();function i(n,o=[],r){async function f(t){let e={formData:t};for(let a of o){let s=await a({ctx:e});e={...e,...s}}return e}return{input(t){if(n===x)return i(t,o,r);if(n._def.effect)throw new Error("Previous input is not augmentable because it contains an effect.");if(t._def.effect)throw new Error("Your input contains effect which prevents merging it with the previous inputs.");if(n._def.typeName===C.ZodObject){let e=n.merge(t);return i(e,o)}else throw Error("Merging inputs works only for object schemas without effects.")},use(t){return i(n,[...o,t],r)},error(t){return i(n,o,t)},run:n===x?t=>p(({success:e,failure:a})=>async(s,m)=>{let d=await f(m);try{return e(await t({ctx:d}))}catch(c){if(r)return a(r({error:c,ctx:d}));throw c}}):n===x?void 0:t=>p(({success:e,failure:a,invalid:s})=>{let m=w.formData(n);return async(d,c)=>{let y=await f(c),u=m.safeParse(c);if(!u.success)return s(u.error.format());let h=u.data;try{return e(await t({input:h,ctx:y}))}catch(S){if(r)return a(r({error:S,ctx:y}));throw S}}})}}var N=i(x);export{p as createFormAction,N as formAction,A as initial};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-form-action",
3
- "version": "1.5.0",
3
+ "version": "2.0.1",
4
4
  "description": "State management helpers for the react form actions.",
5
5
  "repository": {
6
6
  "type": "git",