@reaxon/hook-form-effector 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dovran Jorayev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # @reaxon/hook-form-effector
2
+
3
+ [Effector](https://effector.dev) bindings for
4
+ [react-hook-form](https://react-hook-form.com)'s headless `createFormControl`.
5
+ Form logic (fields, validation, submit) stays in react-hook-form; this package
6
+ lets Effector models read derived form state and drive the control with
7
+ effects, scope-safe.
8
+
9
+ ```sh
10
+ pnpm add @reaxon/hook-form-effector effector react-hook-form
11
+ ```
12
+
13
+ ## API
14
+
15
+ ### `formState({ form, setup, teardown? })`
16
+
17
+ Mirrors a form control into a store of `{ values, ...FormState }`. `setup`
18
+ starts the subscription (fire it under a scope, e.g. on dialog open);
19
+ `teardown` stops it. The store is seeded on attach, so a `reset` that ran in
20
+ the same wave as `setup` is never missed.
21
+
22
+ ```ts
23
+ import { createEvent, fork, allSettled } from 'effector';
24
+ import { createFormControl } from 'react-hook-form';
25
+ import { formState } from '@reaxon/hook-form-effector';
26
+
27
+ const form = createFormControl<{ name: string }>({ defaultValues: { name: '' } });
28
+ const opened = createEvent();
29
+ const $form = formState({ form, setup: opened });
30
+
31
+ const scope = fork();
32
+ await allSettled(opened, { scope });
33
+ scope.getState($form).values.name; // ''
34
+ ```
35
+
36
+ ### Effect factories over the control
37
+
38
+ - `formResetApi(form, emptyValues)` — `form.reset(emptyValues)`; pins the
39
+ pristine values because RHF's `reset(values)` replaces `defaultValues`.
40
+ - `formPrefillApi(form)` — `form.reset(payload)`, e.g. an edit prefill.
41
+ - `formSetApi(form)` — `form.setValue(name, value, options)`.
42
+ - `formSetErrorApi(form)` — `form.setError(name, error, options)` for each
43
+ entry of a `FormFieldError[]` payload, e.g. to attach a server's field
44
+ errors after a failed submit.
45
+
46
+ Each accepts either a form control or a `Store` holding one.
47
+
48
+ ## Effector plugin setup (SSR / `fork`)
49
+
50
+ Every export of this package is a factory: it creates stores, events and
51
+ effects on each call. For those units to get stable SIDs, which `fork` and
52
+ `serialize` need for SSR and for hydration, list the package in the
53
+ `factories` option of the Effector compiler plugin. Client-only apps that
54
+ never serialize a scope can skip this.
55
+
56
+ Babel (`effector/babel-plugin`):
57
+
58
+ ```json
59
+ {
60
+ "plugins": [
61
+ ["effector/babel-plugin", { "factories": ["@reaxon/hook-form-effector"] }]
62
+ ]
63
+ }
64
+ ```
65
+
66
+ SWC (`@effector/swc-plugin`), e.g. in `.swcrc` or Next.js `experimental.swcPlugins`:
67
+
68
+ ```json
69
+ ["@effector/swc-plugin", { "factories": ["@reaxon/hook-form-effector"] }]
70
+ ```
71
+
72
+ Vite with `@vitejs/plugin-react`:
73
+
74
+ ```ts
75
+ react({
76
+ babel: {
77
+ plugins: [
78
+ ["effector/babel-plugin", { factories: ["@reaxon/hook-form-effector"] }],
79
+ ],
80
+ },
81
+ });
82
+ ```
83
+
84
+ ## Peer dependencies
85
+
86
+ - `effector` `^23`
87
+ - `react-hook-form` `^7.72` (`createFormControl` exists since 7.55, but the `form.*` error names and the `isSubmitted` subscription flag this package relies on typecheck from 7.72.0)
88
+
89
+ ## License
90
+
91
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,121 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let effector = require("effector");
3
+ //#region src/lib.ts
4
+ var storify = (value, config = { serialize: "ignore" }) => effector.is.store(value) ? value : (0, effector.createStore)(value, config);
5
+ //#endregion
6
+ //#region src/form-state.ts
7
+ var noop = () => {};
8
+ /**
9
+ * Mirror a react-hook-form `createFormControl` instance into an Effector store.
10
+ *
11
+ * The form control itself is a client-only UI controller (held in a
12
+ * `serialize: "ignore"` store). `setup` starts the subscription — fire it under
13
+ * a scope, e.g. on dialog open — and the optional `teardown` stops it. The
14
+ * returned store always holds the latest form state plus current `values`.
15
+ *
16
+ * Form *logic* (fields, validation, submit) stays in the UI via RHF; this store
17
+ * exists so Effector can read derived state such as `isValid`/`isSubmitting`
18
+ * (e.g. to drive a submit button's disabled state) without duplicating logic.
19
+ */
20
+ var formState = (config) => {
21
+ const $form = storify(config.form, {
22
+ serialize: "ignore",
23
+ name: "form"
24
+ });
25
+ const $unsubscribe = (0, effector.createStore)(noop, { serialize: "ignore" });
26
+ const $formState = (0, effector.createStore)({ values: $form.defaultState.getValues() });
27
+ const updated = (0, effector.createEvent)();
28
+ const subscribeFx = (0, effector.attach)({
29
+ name: "formSubscribeFx",
30
+ source: $unsubscribe,
31
+ effect: (unsubscribe, form) => {
32
+ const onUpdate = (0, effector.scopeBind)(updated, { safe: true });
33
+ unsubscribe();
34
+ queueMicrotask(() => onUpdate({ values: form.getValues() }));
35
+ return form.subscribe({
36
+ formState: {
37
+ values: true,
38
+ errors: true,
39
+ isDirty: true,
40
+ isValid: true,
41
+ isValidating: true,
42
+ isSubmitted: true,
43
+ touchedFields: true,
44
+ dirtyFields: true
45
+ },
46
+ callback: (updates) => queueMicrotask(() => onUpdate(updates))
47
+ });
48
+ }
49
+ });
50
+ const unsubscribeFx = (0, effector.attach)({
51
+ name: "formUnsubscribeFx",
52
+ source: $unsubscribe,
53
+ effect: (unsubscribe) => unsubscribe()
54
+ });
55
+ (0, effector.sample)({
56
+ clock: [config.setup, $form],
57
+ source: $form,
58
+ target: subscribeFx
59
+ });
60
+ (0, effector.sample)({
61
+ clock: subscribeFx.doneData,
62
+ target: $unsubscribe
63
+ });
64
+ (0, effector.sample)({
65
+ clock: updated,
66
+ target: $formState
67
+ });
68
+ if (config.teardown) (0, effector.sample)({
69
+ clock: config.teardown,
70
+ target: unsubscribeFx
71
+ });
72
+ return $formState;
73
+ };
74
+ //#endregion
75
+ //#region src/form-api.ts
76
+ /**
77
+ * form.reset(emptyValues) — back to the pristine form. The empty values
78
+ * are pinned at creation instead of using a bare form.reset(): RHF's
79
+ * reset(values) REPLACES defaultValues, so after a prefill
80
+ * ([[formPrefillApi]]) a bare reset() would restore the prefilled values,
81
+ * not the pristine form.
82
+ */
83
+ var formResetApi = (form, emptyValues) => (0, effector.attach)({
84
+ name: "formResetFx",
85
+ source: storify(form),
86
+ effect: (form) => form.reset(emptyValues)
87
+ });
88
+ /**
89
+ * form.reset(values) with the payload — e.g. an edit prefill. Replaces
90
+ * RHF defaultValues (RHF semantics), which is why formResetApi exists.
91
+ */
92
+ var formPrefillApi = (form) => (0, effector.attach)({
93
+ name: "formPrefillFx",
94
+ source: storify(form),
95
+ effect: (form, values) => form.reset(values)
96
+ });
97
+ /** form.setValue(name, value, options) with the payload. */
98
+ var formSetApi = (form) => (0, effector.attach)({
99
+ name: "formSetFx",
100
+ source: storify(form),
101
+ effect: (form, { name, value, options }) => form.setValue(name, value, options)
102
+ });
103
+ /**
104
+ * `form.setError` bound to the form: apply a list of field errors, e.g. a
105
+ * server's 422 response mapped to `type: "server"` input errors.
106
+ */
107
+ var formSetErrorApi = (form) => (0, effector.attach)({
108
+ name: "formSetErrorFx",
109
+ source: storify(form),
110
+ effect: (form, fields) => {
111
+ for (const fieldError of fields) form.setError(fieldError.name, fieldError.error, fieldError.options);
112
+ }
113
+ });
114
+ //#endregion
115
+ exports.formPrefillApi = formPrefillApi;
116
+ exports.formResetApi = formResetApi;
117
+ exports.formSetApi = formSetApi;
118
+ exports.formSetErrorApi = formSetErrorApi;
119
+ exports.formState = formState;
120
+
121
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/lib.ts","../src/form-state.ts","../src/form-api.ts"],"sourcesContent":["import { createStore, is, Json, Store } from \"effector\";\n\nexport const storify = <V, SerializedState extends Json = Json>(\n value: V | Store<V>,\n config: {\n skipVoid?: boolean;\n name?: string;\n sid?: string;\n updateFilter?: (update: V, current: V) => boolean;\n serialize?:\n | \"ignore\"\n | {\n write: (state: V) => SerializedState;\n read: (json: SerializedState) => V;\n };\n } = { serialize: \"ignore\" },\n): Store<V> => (is.store(value) ? value : createStore(value, config));\n","import {\n attach,\n createEvent,\n createStore,\n sample,\n scopeBind,\n type Event,\n type Store\n} from \"effector\";\nimport {\n createFormControl,\n type FieldValues,\n type FormState,\n} from \"react-hook-form\";\nimport { storify } from \"./lib\";\n\n/** Flattens an intersection so hovers and error messages show one object type. */\nexport type Prettify<T> = {\n [K in keyof T]: T[K];\n} & {};\n\nconst noop = () => {};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyFormContext = any;\n\nexport type FormControl<\n TFieldValues extends FieldValues = FieldValues,\n TContext = AnyFormContext,\n TTransformedValues = TFieldValues,\n> = Prettify<\n ReturnType<\n typeof createFormControl<TFieldValues, TContext, TTransformedValues>\n >\n>;\n\nexport type FormStateWithValues<\n TFieldValues extends FieldValues = FieldValues,\n> = Prettify<{ values: TFieldValues } & Partial<FormState<TFieldValues>>>;\n\n/**\n * Mirror a react-hook-form `createFormControl` instance into an Effector store.\n *\n * The form control itself is a client-only UI controller (held in a\n * `serialize: \"ignore\"` store). `setup` starts the subscription — fire it under\n * a scope, e.g. on dialog open — and the optional `teardown` stops it. The\n * returned store always holds the latest form state plus current `values`.\n *\n * Form *logic* (fields, validation, submit) stays in the UI via RHF; this store\n * exists so Effector can read derived state such as `isValid`/`isSubmitting`\n * (e.g. to drive a submit button's disabled state) without duplicating logic.\n */\nexport const formState = <\n TFieldValues extends FieldValues = FieldValues,\n TContext = AnyFormContext,\n TTransformedValues = TFieldValues,\n>(config: {\n form:\n | FormControl<TFieldValues, TContext, TTransformedValues>\n | Store<FormControl<TFieldValues, TContext, TTransformedValues>>;\n setup: Event<unknown>;\n teardown?: Event<unknown>;\n}) => {\n const $form = storify(config.form, { serialize: \"ignore\", name: \"form\" });\n\n const $unsubscribe = createStore(noop, { serialize: \"ignore\" });\n const $formState = createStore<FormStateWithValues<TFieldValues>>({\n values: $form.defaultState.getValues() as TFieldValues,\n });\n\n const updated = createEvent<FormStateWithValues<TFieldValues>>();\n\n const subscribeFx = attach({\n name: \"formSubscribeFx\",\n source: $unsubscribe,\n effect: (\n unsubscribe,\n form: FormControl<TFieldValues, TContext, TTransformedValues>,\n ) => {\n const onUpdate = scopeBind(updated, { safe: true });\n\n unsubscribe();\n\n // Attach snapshot: a reset that ran BEFORE this subscription attached\n // notified nobody — read the current values so the mirror can never\n // start stale. Values only: the control exposes no derived flags;\n // those arrive with RHF's next real notification.\n queueMicrotask(() => onUpdate({ values: form.getValues() }));\n\n return form.subscribe({\n // Opt into the slices we mirror — `subscribe` only invokes the callback\n // for formState parts it is told to track (`values` included).\n formState: {\n values: true,\n errors: true,\n isDirty: true,\n isValid: true,\n isValidating: true,\n isSubmitted: true,\n touchedFields: true,\n dirtyFields: true,\n },\n // RHF notifies subscribers SYNCHRONOUSLY, including from <Controller>\n // field registration which runs during React render. Forwarding into\n // effector right there makes every useUnit subscriber setState during\n // another component's render (React error). One microtask defers the\n // mirror out of the render phase; ordering between updates is kept.\n callback: (updates) => queueMicrotask(() => onUpdate(updates)),\n });\n },\n });\n const unsubscribeFx = attach({\n name: \"formUnsubscribeFx\",\n source: $unsubscribe,\n effect: (unsubscribe) => unsubscribe(),\n });\n\n sample({ clock: [config.setup, $form], source: $form, target: subscribeFx });\n sample({ clock: subscribeFx.doneData, target: $unsubscribe });\n sample({ clock: updated, target: $formState });\n\n if (config.teardown) {\n sample({ clock: config.teardown, target: unsubscribeFx });\n }\n\n return $formState;\n};\n","import { attach, type Store } from \"effector\";\nimport type {\n ErrorOption,\n FieldPath,\n FieldValues,\n Path,\n PathValue,\n SetValueConfig,\n} from \"react-hook-form\";\nimport type { FormControl } from \"./form-state\";\nimport { storify } from \"./lib\";\n\n/**\n * One `formSetApi` write. The name/value pairing is deliberately as loose\n * as RHF's own runtime (the value type is the union over all paths), so\n * callsites that pick the field dynamically stay cast-free.\n */\nexport type SetValuePayload<Values extends FieldValues> = {\n name: Path<Values>;\n value: PathValue<Values, Path<Values>>;\n options?: SetValueConfig;\n};\n\n/*\n * One factory per RHF call models actually make — each returns a single\n * effect over the form control, replacing the per-model\n * `attach({ source: $form, ... })` boilerplate. Granular on purpose: a\n * model declares exactly the effects it uses, under its own names, and\n * two forms in one module never fight over destructure aliases. Extend\n * the set when a new call pattern becomes common.\n */\n\n\n/**\n * form.reset(emptyValues) — back to the pristine form. The empty values\n * are pinned at creation instead of using a bare form.reset(): RHF's\n * reset(values) REPLACES defaultValues, so after a prefill\n * ([[formPrefillApi]]) a bare reset() would restore the prefilled values,\n * not the pristine form.\n */\nexport const formResetApi = <Values extends FieldValues>(\n form: FormControl<Values> | Store<FormControl<Values>>,\n emptyValues?: Values,\n) =>\n attach({\n name: \"formResetFx\",\n source: storify(form),\n effect: (form) => form.reset(emptyValues),\n });\n\n/**\n * form.reset(values) with the payload — e.g. an edit prefill. Replaces\n * RHF defaultValues (RHF semantics), which is why formResetApi exists.\n */\nexport const formPrefillApi = <Values extends FieldValues>(\n form: FormControl<Values> | Store<FormControl<Values>>,\n) =>\n attach({\n name: \"formPrefillFx\",\n source: storify(form),\n effect: (form, values: Values) => form.reset(values),\n });\n\n/** form.setValue(name, value, options) with the payload. */\nexport const formSetApi = <Values extends FieldValues>(\n form: FormControl<Values> | Store<FormControl<Values>>,\n) =>\n attach({\n name: \"formSetFx\",\n source: storify(form),\n effect: (form, { name, value, options }: SetValuePayload<Values>) =>\n form.setValue(name, value, options),\n });\n\n\nexport type FormFieldError<Values extends FieldValues> = {\n name:\n | \"form\"\n | \"root\"\n | `root.${string}`\n | FieldPath<Values>\n | `form.${string}`;\n error: ErrorOption;\n options?: { shouldFocus: boolean };\n};\n\n\n/**\n * `form.setError` bound to the form: apply a list of field errors, e.g. a\n * server's 422 response mapped to `type: \"server\"` input errors.\n */\nexport const formSetErrorApi = <Values extends FieldValues>(\n form: FormControl<Values> | Store<FormControl<Values>>,\n) =>\n attach({\n name: \"formSetErrorFx\",\n source: storify(form),\n effect: (form, fields: FormFieldError<Values>[]) => {\n for (const fieldError of fields) {\n form.setError(fieldError.name, fieldError.error, fieldError.options);\n }\n },\n });\n\n"],"mappings":";;;AAEA,IAAa,WACX,OACA,SAWI,EAAE,WAAW,SAAS,MACZ,SAAA,GAAG,MAAM,KAAK,IAAI,SAAA,GAAQ,SAAA,YAAA,CAAY,OAAO,MAAM;;;ACKnE,IAAM,aAAa,CAAC;;;;;;;;;;;;;AA+BpB,IAAa,aAIX,WAMI;CACJ,MAAM,QAAQ,QAAQ,OAAO,MAAM;EAAE,WAAW;EAAU,MAAM;CAAO,CAAC;CAExE,MAAM,gBAAA,GAAe,SAAA,YAAA,CAAY,MAAM,EAAE,WAAW,SAAS,CAAC;CAC9D,MAAM,cAAA,GAAa,SAAA,YAAA,CAA+C,EAChE,QAAQ,MAAM,aAAa,UAAU,EACvC,CAAC;CAED,MAAM,WAAA,GAAU,SAAA,YAAA,CAA+C;CAE/D,MAAM,eAAA,GAAc,SAAA,OAAA,CAAO;EACzB,MAAM;EACN,QAAQ;EACR,SACE,aACA,SACG;GACH,MAAM,YAAA,GAAW,SAAA,UAAA,CAAU,SAAS,EAAE,MAAM,KAAK,CAAC;GAElD,YAAY;GAMZ,qBAAqB,SAAS,EAAE,QAAQ,KAAK,UAAU,EAAE,CAAC,CAAC;GAE3D,OAAO,KAAK,UAAU;IAGpB,WAAW;KACT,QAAQ;KACR,QAAQ;KACR,SAAS;KACT,SAAS;KACT,cAAc;KACd,aAAa;KACb,eAAe;KACf,aAAa;IACf;IAMA,WAAW,YAAY,qBAAqB,SAAS,OAAO,CAAC;GAC/D,CAAC;EACH;CACF,CAAC;CACD,MAAM,iBAAA,GAAgB,SAAA,OAAA,CAAO;EAC3B,MAAM;EACN,QAAQ;EACR,SAAS,gBAAgB,YAAY;CACvC,CAAC;CAED,CAAA,GAAA,SAAA,OAAA,CAAO;EAAE,OAAO,CAAC,OAAO,OAAO,KAAK;EAAG,QAAQ;EAAO,QAAQ;CAAY,CAAC;CAC3E,CAAA,GAAA,SAAA,OAAA,CAAO;EAAE,OAAO,YAAY;EAAU,QAAQ;CAAa,CAAC;CAC5D,CAAA,GAAA,SAAA,OAAA,CAAO;EAAE,OAAO;EAAS,QAAQ;CAAW,CAAC;CAE7C,IAAI,OAAO,UACT,CAAA,GAAA,SAAA,OAAA,CAAO;EAAE,OAAO,OAAO;EAAU,QAAQ;CAAc,CAAC;CAG1D,OAAO;AACT;;;;;;;;;;ACtFA,IAAa,gBACX,MACA,iBAAA,GAEA,SAAA,OAAA,CAAO;CACL,MAAM;CACN,QAAQ,QAAQ,IAAI;CACpB,SAAS,SAAS,KAAK,MAAM,WAAW;AAC1C,CAAC;;;;;AAMH,IAAa,kBACX,UAAA,GAEA,SAAA,OAAA,CAAO;CACL,MAAM;CACN,QAAQ,QAAQ,IAAI;CACpB,SAAS,MAAM,WAAmB,KAAK,MAAM,MAAM;AACrD,CAAC;;AAGH,IAAa,cACX,UAAA,GAEA,SAAA,OAAA,CAAO;CACL,MAAM;CACN,QAAQ,QAAQ,IAAI;CACpB,SAAS,MAAM,EAAE,MAAM,OAAO,cAC5B,KAAK,SAAS,MAAM,OAAO,OAAO;AACtC,CAAC;;;;;AAmBH,IAAa,mBACX,UAAA,GAEA,SAAA,OAAA,CAAO;CACL,MAAM;CACN,QAAQ,QAAQ,IAAI;CACpB,SAAS,MAAM,WAAqC;EAClD,KAAK,MAAM,cAAc,QACvB,KAAK,SAAS,WAAW,MAAM,WAAW,OAAO,WAAW,OAAO;CAEvE;AACF,CAAC"}
@@ -0,0 +1,108 @@
1
+ import { createFormControl } from 'react-hook-form';
2
+ import { DeepMap } from 'react-hook-form';
3
+ import { DeepPartial } from 'react-hook-form';
4
+ import { Effect } from 'effector';
5
+ import { ErrorOption } from 'react-hook-form';
6
+ import { Event as Event_2 } from 'effector';
7
+ import { FieldErrors } from 'react-hook-form';
8
+ import { FieldPath } from 'react-hook-form';
9
+ import { FieldValues } from 'react-hook-form';
10
+ import { FormState } from 'react-hook-form';
11
+ import { Path } from 'react-hook-form';
12
+ import { PathValue } from 'react-hook-form';
13
+ import { SetValueConfig } from 'react-hook-form';
14
+ import { Store } from 'effector';
15
+ import { StoreWritable } from 'effector';
16
+
17
+ declare type AnyFormContext = any;
18
+
19
+ export declare type FormControl<TFieldValues extends FieldValues = FieldValues, TContext = AnyFormContext, TTransformedValues = TFieldValues> = Prettify<ReturnType<typeof createFormControl<TFieldValues, TContext, TTransformedValues>>>;
20
+
21
+ export declare type FormFieldError<Values extends FieldValues> = {
22
+ name: "form" | "root" | `root.${string}` | FieldPath<Values> | `form.${string}`;
23
+ error: ErrorOption;
24
+ options?: {
25
+ shouldFocus: boolean;
26
+ };
27
+ };
28
+
29
+ /**
30
+ * form.reset(values) with the payload — e.g. an edit prefill. Replaces
31
+ * RHF defaultValues (RHF semantics), which is why formResetApi exists.
32
+ */
33
+ export declare const formPrefillApi: <Values extends FieldValues>(form: FormControl<Values> | Store<FormControl<Values>>) => Effect<Values, void, Error>;
34
+
35
+ /**
36
+ * form.reset(emptyValues) — back to the pristine form. The empty values
37
+ * are pinned at creation instead of using a bare form.reset(): RHF's
38
+ * reset(values) REPLACES defaultValues, so after a prefill
39
+ * ([[formPrefillApi]]) a bare reset() would restore the prefilled values,
40
+ * not the pristine form.
41
+ */
42
+ export declare const formResetApi: <Values extends FieldValues>(form: FormControl<Values> | Store<FormControl<Values>>, emptyValues?: Values) => Effect<void, void, Error>;
43
+
44
+ /** form.setValue(name, value, options) with the payload. */
45
+ export declare const formSetApi: <Values extends FieldValues>(form: FormControl<Values> | Store<FormControl<Values>>) => Effect<SetValuePayload<Values>, void, Error>;
46
+
47
+ /**
48
+ * `form.setError` bound to the form: apply a list of field errors, e.g. a
49
+ * server's 422 response mapped to `type: "server"` input errors.
50
+ */
51
+ export declare const formSetErrorApi: <Values extends FieldValues>(form: FormControl<Values> | Store<FormControl<Values>>) => Effect<FormFieldError<Values>[], void, Error>;
52
+
53
+ /**
54
+ * Mirror a react-hook-form `createFormControl` instance into an Effector store.
55
+ *
56
+ * The form control itself is a client-only UI controller (held in a
57
+ * `serialize: "ignore"` store). `setup` starts the subscription — fire it under
58
+ * a scope, e.g. on dialog open — and the optional `teardown` stops it. The
59
+ * returned store always holds the latest form state plus current `values`.
60
+ *
61
+ * Form *logic* (fields, validation, submit) stays in the UI via RHF; this store
62
+ * exists so Effector can read derived state such as `isValid`/`isSubmitting`
63
+ * (e.g. to drive a submit button's disabled state) without duplicating logic.
64
+ */
65
+ export declare const formState: <TFieldValues extends FieldValues = FieldValues, TContext = AnyFormContext, TTransformedValues = TFieldValues>(config: {
66
+ form: FormControl<TFieldValues, TContext, TTransformedValues> | Store<FormControl<TFieldValues, TContext, TTransformedValues>>;
67
+ setup: Event_2<unknown>;
68
+ teardown?: Event_2<unknown>;
69
+ }) => StoreWritable<{
70
+ values: TFieldValues;
71
+ isDirty?: boolean | undefined;
72
+ isLoading?: boolean | undefined;
73
+ isSubmitted?: boolean | undefined;
74
+ isSubmitSuccessful?: boolean | undefined;
75
+ isSubmitting?: boolean | undefined;
76
+ isValidating?: boolean | undefined;
77
+ isValid?: boolean | undefined;
78
+ disabled?: boolean | undefined;
79
+ submitCount?: number | undefined;
80
+ defaultValues?: Readonly< DeepPartial<TFieldValues>> | undefined;
81
+ dirtyFields?: Partial<Readonly< DeepMap<DeepPartial<TFieldValues>, boolean>>> | undefined;
82
+ touchedFields?: Partial<Readonly< DeepMap<DeepPartial<TFieldValues>, boolean>>> | undefined;
83
+ validatingFields?: Partial<Readonly< DeepMap<DeepPartial<TFieldValues>, boolean>>> | undefined;
84
+ errors?: FieldErrors<TFieldValues> | undefined;
85
+ isReady?: boolean | undefined;
86
+ }>;
87
+
88
+ export declare type FormStateWithValues<TFieldValues extends FieldValues = FieldValues> = Prettify<{
89
+ values: TFieldValues;
90
+ } & Partial<FormState<TFieldValues>>>;
91
+
92
+ /** Flattens an intersection so hovers and error messages show one object type. */
93
+ declare type Prettify<T> = {
94
+ [K in keyof T]: T[K];
95
+ } & {};
96
+
97
+ /**
98
+ * One `formSetApi` write. The name/value pairing is deliberately as loose
99
+ * as RHF's own runtime (the value type is the union over all paths), so
100
+ * callsites that pick the field dynamically stay cast-free.
101
+ */
102
+ export declare type SetValuePayload<Values extends FieldValues> = {
103
+ name: Path<Values>;
104
+ value: PathValue<Values, Path<Values>>;
105
+ options?: SetValueConfig;
106
+ };
107
+
108
+ export { }
@@ -0,0 +1,108 @@
1
+ import { createFormControl } from 'react-hook-form';
2
+ import { DeepMap } from 'react-hook-form';
3
+ import { DeepPartial } from 'react-hook-form';
4
+ import { Effect } from 'effector';
5
+ import { ErrorOption } from 'react-hook-form';
6
+ import { Event as Event_2 } from 'effector';
7
+ import { FieldErrors } from 'react-hook-form';
8
+ import { FieldPath } from 'react-hook-form';
9
+ import { FieldValues } from 'react-hook-form';
10
+ import { FormState } from 'react-hook-form';
11
+ import { Path } from 'react-hook-form';
12
+ import { PathValue } from 'react-hook-form';
13
+ import { SetValueConfig } from 'react-hook-form';
14
+ import { Store } from 'effector';
15
+ import { StoreWritable } from 'effector';
16
+
17
+ declare type AnyFormContext = any;
18
+
19
+ export declare type FormControl<TFieldValues extends FieldValues = FieldValues, TContext = AnyFormContext, TTransformedValues = TFieldValues> = Prettify<ReturnType<typeof createFormControl<TFieldValues, TContext, TTransformedValues>>>;
20
+
21
+ export declare type FormFieldError<Values extends FieldValues> = {
22
+ name: "form" | "root" | `root.${string}` | FieldPath<Values> | `form.${string}`;
23
+ error: ErrorOption;
24
+ options?: {
25
+ shouldFocus: boolean;
26
+ };
27
+ };
28
+
29
+ /**
30
+ * form.reset(values) with the payload — e.g. an edit prefill. Replaces
31
+ * RHF defaultValues (RHF semantics), which is why formResetApi exists.
32
+ */
33
+ export declare const formPrefillApi: <Values extends FieldValues>(form: FormControl<Values> | Store<FormControl<Values>>) => Effect<Values, void, Error>;
34
+
35
+ /**
36
+ * form.reset(emptyValues) — back to the pristine form. The empty values
37
+ * are pinned at creation instead of using a bare form.reset(): RHF's
38
+ * reset(values) REPLACES defaultValues, so after a prefill
39
+ * ([[formPrefillApi]]) a bare reset() would restore the prefilled values,
40
+ * not the pristine form.
41
+ */
42
+ export declare const formResetApi: <Values extends FieldValues>(form: FormControl<Values> | Store<FormControl<Values>>, emptyValues?: Values) => Effect<void, void, Error>;
43
+
44
+ /** form.setValue(name, value, options) with the payload. */
45
+ export declare const formSetApi: <Values extends FieldValues>(form: FormControl<Values> | Store<FormControl<Values>>) => Effect<SetValuePayload<Values>, void, Error>;
46
+
47
+ /**
48
+ * `form.setError` bound to the form: apply a list of field errors, e.g. a
49
+ * server's 422 response mapped to `type: "server"` input errors.
50
+ */
51
+ export declare const formSetErrorApi: <Values extends FieldValues>(form: FormControl<Values> | Store<FormControl<Values>>) => Effect<FormFieldError<Values>[], void, Error>;
52
+
53
+ /**
54
+ * Mirror a react-hook-form `createFormControl` instance into an Effector store.
55
+ *
56
+ * The form control itself is a client-only UI controller (held in a
57
+ * `serialize: "ignore"` store). `setup` starts the subscription — fire it under
58
+ * a scope, e.g. on dialog open — and the optional `teardown` stops it. The
59
+ * returned store always holds the latest form state plus current `values`.
60
+ *
61
+ * Form *logic* (fields, validation, submit) stays in the UI via RHF; this store
62
+ * exists so Effector can read derived state such as `isValid`/`isSubmitting`
63
+ * (e.g. to drive a submit button's disabled state) without duplicating logic.
64
+ */
65
+ export declare const formState: <TFieldValues extends FieldValues = FieldValues, TContext = AnyFormContext, TTransformedValues = TFieldValues>(config: {
66
+ form: FormControl<TFieldValues, TContext, TTransformedValues> | Store<FormControl<TFieldValues, TContext, TTransformedValues>>;
67
+ setup: Event_2<unknown>;
68
+ teardown?: Event_2<unknown>;
69
+ }) => StoreWritable<{
70
+ values: TFieldValues;
71
+ isDirty?: boolean | undefined;
72
+ isLoading?: boolean | undefined;
73
+ isSubmitted?: boolean | undefined;
74
+ isSubmitSuccessful?: boolean | undefined;
75
+ isSubmitting?: boolean | undefined;
76
+ isValidating?: boolean | undefined;
77
+ isValid?: boolean | undefined;
78
+ disabled?: boolean | undefined;
79
+ submitCount?: number | undefined;
80
+ defaultValues?: Readonly< DeepPartial<TFieldValues>> | undefined;
81
+ dirtyFields?: Partial<Readonly< DeepMap<DeepPartial<TFieldValues>, boolean>>> | undefined;
82
+ touchedFields?: Partial<Readonly< DeepMap<DeepPartial<TFieldValues>, boolean>>> | undefined;
83
+ validatingFields?: Partial<Readonly< DeepMap<DeepPartial<TFieldValues>, boolean>>> | undefined;
84
+ errors?: FieldErrors<TFieldValues> | undefined;
85
+ isReady?: boolean | undefined;
86
+ }>;
87
+
88
+ export declare type FormStateWithValues<TFieldValues extends FieldValues = FieldValues> = Prettify<{
89
+ values: TFieldValues;
90
+ } & Partial<FormState<TFieldValues>>>;
91
+
92
+ /** Flattens an intersection so hovers and error messages show one object type. */
93
+ declare type Prettify<T> = {
94
+ [K in keyof T]: T[K];
95
+ } & {};
96
+
97
+ /**
98
+ * One `formSetApi` write. The name/value pairing is deliberately as loose
99
+ * as RHF's own runtime (the value type is the union over all paths), so
100
+ * callsites that pick the field dynamically stay cast-free.
101
+ */
102
+ export declare type SetValuePayload<Values extends FieldValues> = {
103
+ name: Path<Values>;
104
+ value: PathValue<Values, Path<Values>>;
105
+ options?: SetValueConfig;
106
+ };
107
+
108
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,116 @@
1
+ import { attach, createEvent, createStore, is, sample, scopeBind } from "effector";
2
+ //#region src/lib.ts
3
+ var storify = (value, config = { serialize: "ignore" }) => is.store(value) ? value : createStore(value, config);
4
+ //#endregion
5
+ //#region src/form-state.ts
6
+ var noop = () => {};
7
+ /**
8
+ * Mirror a react-hook-form `createFormControl` instance into an Effector store.
9
+ *
10
+ * The form control itself is a client-only UI controller (held in a
11
+ * `serialize: "ignore"` store). `setup` starts the subscription — fire it under
12
+ * a scope, e.g. on dialog open — and the optional `teardown` stops it. The
13
+ * returned store always holds the latest form state plus current `values`.
14
+ *
15
+ * Form *logic* (fields, validation, submit) stays in the UI via RHF; this store
16
+ * exists so Effector can read derived state such as `isValid`/`isSubmitting`
17
+ * (e.g. to drive a submit button's disabled state) without duplicating logic.
18
+ */
19
+ var formState = (config) => {
20
+ const $form = storify(config.form, {
21
+ serialize: "ignore",
22
+ name: "form"
23
+ });
24
+ const $unsubscribe = createStore(noop, { serialize: "ignore" });
25
+ const $formState = createStore({ values: $form.defaultState.getValues() });
26
+ const updated = createEvent();
27
+ const subscribeFx = attach({
28
+ name: "formSubscribeFx",
29
+ source: $unsubscribe,
30
+ effect: (unsubscribe, form) => {
31
+ const onUpdate = scopeBind(updated, { safe: true });
32
+ unsubscribe();
33
+ queueMicrotask(() => onUpdate({ values: form.getValues() }));
34
+ return form.subscribe({
35
+ formState: {
36
+ values: true,
37
+ errors: true,
38
+ isDirty: true,
39
+ isValid: true,
40
+ isValidating: true,
41
+ isSubmitted: true,
42
+ touchedFields: true,
43
+ dirtyFields: true
44
+ },
45
+ callback: (updates) => queueMicrotask(() => onUpdate(updates))
46
+ });
47
+ }
48
+ });
49
+ const unsubscribeFx = attach({
50
+ name: "formUnsubscribeFx",
51
+ source: $unsubscribe,
52
+ effect: (unsubscribe) => unsubscribe()
53
+ });
54
+ sample({
55
+ clock: [config.setup, $form],
56
+ source: $form,
57
+ target: subscribeFx
58
+ });
59
+ sample({
60
+ clock: subscribeFx.doneData,
61
+ target: $unsubscribe
62
+ });
63
+ sample({
64
+ clock: updated,
65
+ target: $formState
66
+ });
67
+ if (config.teardown) sample({
68
+ clock: config.teardown,
69
+ target: unsubscribeFx
70
+ });
71
+ return $formState;
72
+ };
73
+ //#endregion
74
+ //#region src/form-api.ts
75
+ /**
76
+ * form.reset(emptyValues) — back to the pristine form. The empty values
77
+ * are pinned at creation instead of using a bare form.reset(): RHF's
78
+ * reset(values) REPLACES defaultValues, so after a prefill
79
+ * ([[formPrefillApi]]) a bare reset() would restore the prefilled values,
80
+ * not the pristine form.
81
+ */
82
+ var formResetApi = (form, emptyValues) => attach({
83
+ name: "formResetFx",
84
+ source: storify(form),
85
+ effect: (form) => form.reset(emptyValues)
86
+ });
87
+ /**
88
+ * form.reset(values) with the payload — e.g. an edit prefill. Replaces
89
+ * RHF defaultValues (RHF semantics), which is why formResetApi exists.
90
+ */
91
+ var formPrefillApi = (form) => attach({
92
+ name: "formPrefillFx",
93
+ source: storify(form),
94
+ effect: (form, values) => form.reset(values)
95
+ });
96
+ /** form.setValue(name, value, options) with the payload. */
97
+ var formSetApi = (form) => attach({
98
+ name: "formSetFx",
99
+ source: storify(form),
100
+ effect: (form, { name, value, options }) => form.setValue(name, value, options)
101
+ });
102
+ /**
103
+ * `form.setError` bound to the form: apply a list of field errors, e.g. a
104
+ * server's 422 response mapped to `type: "server"` input errors.
105
+ */
106
+ var formSetErrorApi = (form) => attach({
107
+ name: "formSetErrorFx",
108
+ source: storify(form),
109
+ effect: (form, fields) => {
110
+ for (const fieldError of fields) form.setError(fieldError.name, fieldError.error, fieldError.options);
111
+ }
112
+ });
113
+ //#endregion
114
+ export { formPrefillApi, formResetApi, formSetApi, formSetErrorApi, formState };
115
+
116
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/lib.ts","../src/form-state.ts","../src/form-api.ts"],"sourcesContent":["import { createStore, is, Json, Store } from \"effector\";\n\nexport const storify = <V, SerializedState extends Json = Json>(\n value: V | Store<V>,\n config: {\n skipVoid?: boolean;\n name?: string;\n sid?: string;\n updateFilter?: (update: V, current: V) => boolean;\n serialize?:\n | \"ignore\"\n | {\n write: (state: V) => SerializedState;\n read: (json: SerializedState) => V;\n };\n } = { serialize: \"ignore\" },\n): Store<V> => (is.store(value) ? value : createStore(value, config));\n","import {\n attach,\n createEvent,\n createStore,\n sample,\n scopeBind,\n type Event,\n type Store\n} from \"effector\";\nimport {\n createFormControl,\n type FieldValues,\n type FormState,\n} from \"react-hook-form\";\nimport { storify } from \"./lib\";\n\n/** Flattens an intersection so hovers and error messages show one object type. */\nexport type Prettify<T> = {\n [K in keyof T]: T[K];\n} & {};\n\nconst noop = () => {};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyFormContext = any;\n\nexport type FormControl<\n TFieldValues extends FieldValues = FieldValues,\n TContext = AnyFormContext,\n TTransformedValues = TFieldValues,\n> = Prettify<\n ReturnType<\n typeof createFormControl<TFieldValues, TContext, TTransformedValues>\n >\n>;\n\nexport type FormStateWithValues<\n TFieldValues extends FieldValues = FieldValues,\n> = Prettify<{ values: TFieldValues } & Partial<FormState<TFieldValues>>>;\n\n/**\n * Mirror a react-hook-form `createFormControl` instance into an Effector store.\n *\n * The form control itself is a client-only UI controller (held in a\n * `serialize: \"ignore\"` store). `setup` starts the subscription — fire it under\n * a scope, e.g. on dialog open — and the optional `teardown` stops it. The\n * returned store always holds the latest form state plus current `values`.\n *\n * Form *logic* (fields, validation, submit) stays in the UI via RHF; this store\n * exists so Effector can read derived state such as `isValid`/`isSubmitting`\n * (e.g. to drive a submit button's disabled state) without duplicating logic.\n */\nexport const formState = <\n TFieldValues extends FieldValues = FieldValues,\n TContext = AnyFormContext,\n TTransformedValues = TFieldValues,\n>(config: {\n form:\n | FormControl<TFieldValues, TContext, TTransformedValues>\n | Store<FormControl<TFieldValues, TContext, TTransformedValues>>;\n setup: Event<unknown>;\n teardown?: Event<unknown>;\n}) => {\n const $form = storify(config.form, { serialize: \"ignore\", name: \"form\" });\n\n const $unsubscribe = createStore(noop, { serialize: \"ignore\" });\n const $formState = createStore<FormStateWithValues<TFieldValues>>({\n values: $form.defaultState.getValues() as TFieldValues,\n });\n\n const updated = createEvent<FormStateWithValues<TFieldValues>>();\n\n const subscribeFx = attach({\n name: \"formSubscribeFx\",\n source: $unsubscribe,\n effect: (\n unsubscribe,\n form: FormControl<TFieldValues, TContext, TTransformedValues>,\n ) => {\n const onUpdate = scopeBind(updated, { safe: true });\n\n unsubscribe();\n\n // Attach snapshot: a reset that ran BEFORE this subscription attached\n // notified nobody — read the current values so the mirror can never\n // start stale. Values only: the control exposes no derived flags;\n // those arrive with RHF's next real notification.\n queueMicrotask(() => onUpdate({ values: form.getValues() }));\n\n return form.subscribe({\n // Opt into the slices we mirror — `subscribe` only invokes the callback\n // for formState parts it is told to track (`values` included).\n formState: {\n values: true,\n errors: true,\n isDirty: true,\n isValid: true,\n isValidating: true,\n isSubmitted: true,\n touchedFields: true,\n dirtyFields: true,\n },\n // RHF notifies subscribers SYNCHRONOUSLY, including from <Controller>\n // field registration which runs during React render. Forwarding into\n // effector right there makes every useUnit subscriber setState during\n // another component's render (React error). One microtask defers the\n // mirror out of the render phase; ordering between updates is kept.\n callback: (updates) => queueMicrotask(() => onUpdate(updates)),\n });\n },\n });\n const unsubscribeFx = attach({\n name: \"formUnsubscribeFx\",\n source: $unsubscribe,\n effect: (unsubscribe) => unsubscribe(),\n });\n\n sample({ clock: [config.setup, $form], source: $form, target: subscribeFx });\n sample({ clock: subscribeFx.doneData, target: $unsubscribe });\n sample({ clock: updated, target: $formState });\n\n if (config.teardown) {\n sample({ clock: config.teardown, target: unsubscribeFx });\n }\n\n return $formState;\n};\n","import { attach, type Store } from \"effector\";\nimport type {\n ErrorOption,\n FieldPath,\n FieldValues,\n Path,\n PathValue,\n SetValueConfig,\n} from \"react-hook-form\";\nimport type { FormControl } from \"./form-state\";\nimport { storify } from \"./lib\";\n\n/**\n * One `formSetApi` write. The name/value pairing is deliberately as loose\n * as RHF's own runtime (the value type is the union over all paths), so\n * callsites that pick the field dynamically stay cast-free.\n */\nexport type SetValuePayload<Values extends FieldValues> = {\n name: Path<Values>;\n value: PathValue<Values, Path<Values>>;\n options?: SetValueConfig;\n};\n\n/*\n * One factory per RHF call models actually make — each returns a single\n * effect over the form control, replacing the per-model\n * `attach({ source: $form, ... })` boilerplate. Granular on purpose: a\n * model declares exactly the effects it uses, under its own names, and\n * two forms in one module never fight over destructure aliases. Extend\n * the set when a new call pattern becomes common.\n */\n\n\n/**\n * form.reset(emptyValues) — back to the pristine form. The empty values\n * are pinned at creation instead of using a bare form.reset(): RHF's\n * reset(values) REPLACES defaultValues, so after a prefill\n * ([[formPrefillApi]]) a bare reset() would restore the prefilled values,\n * not the pristine form.\n */\nexport const formResetApi = <Values extends FieldValues>(\n form: FormControl<Values> | Store<FormControl<Values>>,\n emptyValues?: Values,\n) =>\n attach({\n name: \"formResetFx\",\n source: storify(form),\n effect: (form) => form.reset(emptyValues),\n });\n\n/**\n * form.reset(values) with the payload — e.g. an edit prefill. Replaces\n * RHF defaultValues (RHF semantics), which is why formResetApi exists.\n */\nexport const formPrefillApi = <Values extends FieldValues>(\n form: FormControl<Values> | Store<FormControl<Values>>,\n) =>\n attach({\n name: \"formPrefillFx\",\n source: storify(form),\n effect: (form, values: Values) => form.reset(values),\n });\n\n/** form.setValue(name, value, options) with the payload. */\nexport const formSetApi = <Values extends FieldValues>(\n form: FormControl<Values> | Store<FormControl<Values>>,\n) =>\n attach({\n name: \"formSetFx\",\n source: storify(form),\n effect: (form, { name, value, options }: SetValuePayload<Values>) =>\n form.setValue(name, value, options),\n });\n\n\nexport type FormFieldError<Values extends FieldValues> = {\n name:\n | \"form\"\n | \"root\"\n | `root.${string}`\n | FieldPath<Values>\n | `form.${string}`;\n error: ErrorOption;\n options?: { shouldFocus: boolean };\n};\n\n\n/**\n * `form.setError` bound to the form: apply a list of field errors, e.g. a\n * server's 422 response mapped to `type: \"server\"` input errors.\n */\nexport const formSetErrorApi = <Values extends FieldValues>(\n form: FormControl<Values> | Store<FormControl<Values>>,\n) =>\n attach({\n name: \"formSetErrorFx\",\n source: storify(form),\n effect: (form, fields: FormFieldError<Values>[]) => {\n for (const fieldError of fields) {\n form.setError(fieldError.name, fieldError.error, fieldError.options);\n }\n },\n });\n\n"],"mappings":";;AAEA,IAAa,WACX,OACA,SAWI,EAAE,WAAW,SAAS,MACZ,GAAG,MAAM,KAAK,IAAI,QAAQ,YAAY,OAAO,MAAM;;;ACKnE,IAAM,aAAa,CAAC;;;;;;;;;;;;;AA+BpB,IAAa,aAIX,WAMI;CACJ,MAAM,QAAQ,QAAQ,OAAO,MAAM;EAAE,WAAW;EAAU,MAAM;CAAO,CAAC;CAExE,MAAM,eAAe,YAAY,MAAM,EAAE,WAAW,SAAS,CAAC;CAC9D,MAAM,aAAa,YAA+C,EAChE,QAAQ,MAAM,aAAa,UAAU,EACvC,CAAC;CAED,MAAM,UAAU,YAA+C;CAE/D,MAAM,cAAc,OAAO;EACzB,MAAM;EACN,QAAQ;EACR,SACE,aACA,SACG;GACH,MAAM,WAAW,UAAU,SAAS,EAAE,MAAM,KAAK,CAAC;GAElD,YAAY;GAMZ,qBAAqB,SAAS,EAAE,QAAQ,KAAK,UAAU,EAAE,CAAC,CAAC;GAE3D,OAAO,KAAK,UAAU;IAGpB,WAAW;KACT,QAAQ;KACR,QAAQ;KACR,SAAS;KACT,SAAS;KACT,cAAc;KACd,aAAa;KACb,eAAe;KACf,aAAa;IACf;IAMA,WAAW,YAAY,qBAAqB,SAAS,OAAO,CAAC;GAC/D,CAAC;EACH;CACF,CAAC;CACD,MAAM,gBAAgB,OAAO;EAC3B,MAAM;EACN,QAAQ;EACR,SAAS,gBAAgB,YAAY;CACvC,CAAC;CAED,OAAO;EAAE,OAAO,CAAC,OAAO,OAAO,KAAK;EAAG,QAAQ;EAAO,QAAQ;CAAY,CAAC;CAC3E,OAAO;EAAE,OAAO,YAAY;EAAU,QAAQ;CAAa,CAAC;CAC5D,OAAO;EAAE,OAAO;EAAS,QAAQ;CAAW,CAAC;CAE7C,IAAI,OAAO,UACT,OAAO;EAAE,OAAO,OAAO;EAAU,QAAQ;CAAc,CAAC;CAG1D,OAAO;AACT;;;;;;;;;;ACtFA,IAAa,gBACX,MACA,gBAEA,OAAO;CACL,MAAM;CACN,QAAQ,QAAQ,IAAI;CACpB,SAAS,SAAS,KAAK,MAAM,WAAW;AAC1C,CAAC;;;;;AAMH,IAAa,kBACX,SAEA,OAAO;CACL,MAAM;CACN,QAAQ,QAAQ,IAAI;CACpB,SAAS,MAAM,WAAmB,KAAK,MAAM,MAAM;AACrD,CAAC;;AAGH,IAAa,cACX,SAEA,OAAO;CACL,MAAM;CACN,QAAQ,QAAQ,IAAI;CACpB,SAAS,MAAM,EAAE,MAAM,OAAO,cAC5B,KAAK,SAAS,MAAM,OAAO,OAAO;AACtC,CAAC;;;;;AAmBH,IAAa,mBACX,SAEA,OAAO;CACL,MAAM;CACN,QAAQ,QAAQ,IAAI;CACpB,SAAS,MAAM,WAAqC;EAClD,KAAK,MAAM,cAAc,QACvB,KAAK,SAAS,WAAW,MAAM,WAAW,OAAO,WAAW,OAAO;CAEvE;AACF,CAAC"}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@reaxon/hook-form-effector",
3
+ "version": "0.1.0",
4
+ "description": "Effector bindings for react-hook-form: mirror form state into stores and drive the form control with effects",
5
+ "keywords": [
6
+ "effector",
7
+ "react-hook-form",
8
+ "forms",
9
+ "state-management"
10
+ ],
11
+ "license": "MIT",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/dovranJorayev/reaxon.git",
15
+ "directory": "packages/hook-form-effector"
16
+ },
17
+ "homepage": "https://github.com/dovranJorayev/reaxon/tree/main/packages/hook-form-effector#readme",
18
+ "bugs": "https://github.com/dovranJorayev/reaxon/issues",
19
+ "type": "module",
20
+ "sideEffects": false,
21
+ "main": "./dist/index.cjs",
22
+ "module": "./dist/index.js",
23
+ "types": "./dist/index.d.ts",
24
+ "exports": {
25
+ ".": {
26
+ "import": {
27
+ "types": "./dist/index.d.ts",
28
+ "default": "./dist/index.js"
29
+ },
30
+ "require": {
31
+ "types": "./dist/index.d.cts",
32
+ "default": "./dist/index.cjs"
33
+ }
34
+ },
35
+ "./package.json": "./package.json"
36
+ },
37
+ "files": [
38
+ "dist",
39
+ "README.md",
40
+ "CHANGELOG.md"
41
+ ],
42
+ "publishConfig": {
43
+ "access": "public",
44
+ "provenance": true
45
+ },
46
+ "engines": {
47
+ "node": ">=18"
48
+ },
49
+ "peerDependencies": {
50
+ "effector": "^23",
51
+ "react-hook-form": "^7.72.0"
52
+ },
53
+ "devDependencies": {
54
+ "@types/react": "19.2.18",
55
+ "effector": "23.4.4",
56
+ "react": "19.2.8",
57
+ "react-hook-form": "7.79.0"
58
+ },
59
+ "scripts": {
60
+ "build": "vite build",
61
+ "test": "vitest run",
62
+ "test:watch": "vitest",
63
+ "typecheck": "tsc --noEmit"
64
+ }
65
+ }