react-f0rm 1.1.0 → 1.2.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.
Files changed (61) hide show
  1. package/README.md +152 -32
  2. package/dist/devtools/index.cjs.js +1 -1
  3. package/dist/devtools/index.cjs.js.map +1 -1
  4. package/dist/devtools/index.d.ts +2 -2
  5. package/dist/devtools/index.mjs +1 -1
  6. package/dist/devtools/index.mjs.map +1 -1
  7. package/dist/errors-CxSjrWJO.cjs.js +2 -0
  8. package/dist/errors-CxSjrWJO.cjs.js.map +1 -0
  9. package/dist/errors-CzWtwjO0.mjs +2 -0
  10. package/dist/errors-CzWtwjO0.mjs.map +1 -0
  11. package/dist/form-CvmWHUrd.d.ts +423 -0
  12. package/dist/index.cjs.js +1 -1
  13. package/dist/index.cjs.js.map +1 -1
  14. package/dist/index.d.ts +786 -102
  15. package/dist/index.mjs +1 -1
  16. package/dist/index.mjs.map +1 -1
  17. package/dist/index.umd.js +801 -354
  18. package/dist/index.umd.js.map +1 -1
  19. package/dist/index.umd.min.js +2 -2
  20. package/dist/index.umd.min.js.map +1 -1
  21. package/dist/persist.cjs.js +2 -0
  22. package/dist/persist.cjs.js.map +1 -0
  23. package/dist/persist.d.ts +49 -0
  24. package/dist/persist.mjs +2 -0
  25. package/dist/persist.mjs.map +1 -0
  26. package/dist/resolvers/standard-schema.cjs.js +1 -1
  27. package/dist/resolvers/standard-schema.cjs.js.map +1 -1
  28. package/dist/resolvers/standard-schema.d.ts +7 -5
  29. package/dist/resolvers/standard-schema.mjs +1 -1
  30. package/dist/resolvers/standard-schema.mjs.map +1 -1
  31. package/dist/resolvers/yup.cjs.js +1 -1
  32. package/dist/resolvers/yup.cjs.js.map +1 -1
  33. package/dist/resolvers/yup.d.ts +1 -1
  34. package/dist/resolvers/yup.mjs +1 -1
  35. package/dist/resolvers/yup.mjs.map +1 -1
  36. package/dist/resolvers/zod.cjs.js +1 -1
  37. package/dist/resolvers/zod.cjs.js.map +1 -1
  38. package/dist/resolvers/zod.d.ts +1 -1
  39. package/dist/resolvers/zod.mjs +1 -1
  40. package/dist/resolvers/zod.mjs.map +1 -1
  41. package/dist/server/index.cjs.js +2 -0
  42. package/dist/server/index.cjs.js.map +1 -0
  43. package/dist/server/index.d.ts +77 -0
  44. package/dist/server/index.mjs +2 -0
  45. package/dist/server/index.mjs.map +1 -0
  46. package/dist/validate-B1Gdjeaq.mjs +2 -0
  47. package/dist/validate-B1Gdjeaq.mjs.map +1 -0
  48. package/dist/validate-CUmNZqg6.d.ts +238 -0
  49. package/dist/validate-DAfz8Nbb.cjs.js +2 -0
  50. package/dist/validate-DAfz8Nbb.cjs.js.map +1 -0
  51. package/dist/values-B1IV-6V4.mjs +2 -0
  52. package/dist/values-B1IV-6V4.mjs.map +1 -0
  53. package/dist/values-CDNAYEOB.cjs.js +2 -0
  54. package/dist/values-CDNAYEOB.cjs.js.map +1 -0
  55. package/package.json +71 -24
  56. package/dist/form-BGWPwts2.mjs +0 -2
  57. package/dist/form-BGWPwts2.mjs.map +0 -1
  58. package/dist/form-BiDaJLjD.d.ts +0 -826
  59. package/dist/form-DwuY91QB.cjs.js +0 -2
  60. package/dist/form-DwuY91QB.cjs.js.map +0 -1
  61. package/dist/validate-2XUilILy.d.ts +0 -22
package/dist/index.d.ts CHANGED
@@ -1,10 +1,473 @@
1
- import { a as FieldPath, N as Name, F as Form$1, V as ValidationMode, P as PathValueOf, b as FieldError, c as Path, O as Options } from './form-BiDaJLjD.js';
2
- export { d as FieldErrorEntry, e as FieldState, f as FormValidateFn, g as FormValidateMeta, h as FormValidationError, H as HandleSubmitOptions, i as PathValue, R as ReValidateMode, j as ResetFieldOptions, k as ResetOptions, S as SetFieldOptions, l as SetFocusOptions, m as SetServerErrorsOptions, n as VALIDATION_OUTCOME, o as ValidateResult, p as ValidationOutcome, q as changeValue, r as changeValueByPath, s as clearErrors, t as createForm, u as emitChangeByPath, v as ensureValidate, w as getDirtyFields, x as getError, y as getErrorByPath, z as getErrors, A as getFieldErrors, B as getFieldErrorsByPath, C as getFieldState, D as getFirstError, E as getTouchedFields, G as getValue, I as getValueByPath, J as getValues, K as handleSubmit, L as hasErrors, M as hasTouched, Q as hasTouchedByPath, T as incrementSubmitCount, U as isDirty, W as isTouched, X as registerFieldValidateDeps, Y as removeField, Z as removeFieldByPath, _ as reset, $ as resetField, a0 as revalidateDependentsOnChange, a1 as revalidateFormOnChange, a2 as seedValueByPath, a3 as setDisabled, a4 as setError, a5 as setErrorByPath, a6 as setFocus, a7 as setInitialValues, a8 as setIsSubmitting, a9 as setServerErrors, aa as setSubmitSuccessful, ab as setTouched, ac as setTouchedByPath, ad as setValidatingByPath, ae as setValue, af as setValueByPath, ag as trigger, ah as unregisterFieldValidateDeps, ai as unsetValidatingByPath, aj as validate } from './form-BiDaJLjD.js';
3
- import { V as Validator } from './validate-2XUilILy.js';
1
+ import { a as FieldError, F as Form$1, P as Path, b as FieldPath, c as PathSegments, d as PathValueOf, N as Name, V as ValidationMode, e as FieldErrorEntry, O as Options, f as FormEvents } from './form-CvmWHUrd.js';
2
+ export { g as FORM_ERROR, h as FormValidateFn, i as FormValidateMeta, j as PathValue, R as ReValidateMode, S as SetFocusOptions, k as SetServerErrorsOptions, l as VALIDATION_OUTCOME, m as ValidateResult, n as ValidationOutcome, o as clearErrors, p as createForm, q as getError, r as getErrorByPath, s as getErrors, t as getFieldErrors, u as getFieldErrorsByPath, v as getFirstError, w as hasErrors, x as setError, y as setErrorByPath, z as setFocus, A as setServerErrors } from './form-CvmWHUrd.js';
4
3
  import * as React from 'react';
5
4
  import { ReactNode } from 'react';
5
+ import { V as Validator } from './validate-CUmNZqg6.js';
6
+ export { F as FormValidationError, S as SyncValidator, T as TriggerOptions, a as ValidatorRegistration, e as ensureValidate, r as registerFieldValidateDeps, b as registerValidatorByPath, c as revalidateDependentsOnChange, d as revalidateFormOnChange, s as setValidatingByPath, t as trigger, u as unregisterFieldValidateDeps, f as unsetValidatingByPath, v as validate } from './validate-CUmNZqg6.js';
6
7
  import { EventEmitter } from '@for-fun/event-emitter';
7
8
 
9
+ /**
10
+ * Get form values: the values Map layered over parsedValues (when a schema
11
+ * validation produced them) layered over initialValues.
12
+ *
13
+ * Merged with copy-on-write ownership tracking ({@link setOwned}): every
14
+ * distinct container on a written path is allocated once and shared by all
15
+ * paths through it, instead of re-copying the whole branch for every key.
16
+ * One owned set spans the whole merge, so containers borrowed from the
17
+ * parsedValues tree are copied before mutation exactly like initialValues
18
+ * ones. The result is a freshly merged tree per mutation, with untouched
19
+ * branches sharing references with the baseline exactly like chained
20
+ * `set` did.
21
+ *
22
+ * Memoized per form like {@link getDirtyFields}: every value write bumps a
23
+ * `version` counter ({@link bumpValuesVersion}) while reads reset it, so
24
+ * consecutive reads hand back the same reference (submit, changeValue and
25
+ * form-level validate all read the whole tree, often several times per
26
+ * interaction). Treat the result as read-only — the next read after a
27
+ * write returns a fresh tree, but between writes the cached one is shared
28
+ * with every other reader.
29
+ *
30
+ * parsedValues is the schema's complete output tree: once validation
31
+ * succeeds it replaces the initialValues baseline (fields the schema
32
+ * dropped disappear), while live edits in the values Map still win over
33
+ * both. It never affects dirty state — {@link isDirty} and
34
+ * {@link getDirtyFields} compare live edits against initialValues only,
35
+ * because parsing is not a user edit.
36
+ *
37
+ * @param form
38
+ */
39
+ declare function getValues<T extends Record<string, any> = any>(form: Form$1<T>): T;
40
+ /**
41
+ * Get field value
42
+ * @param form
43
+ * @param name
44
+ */
45
+ declare function getValue<T extends Record<string, any> = any, P extends FieldPath<T> | PathSegments = FieldPath<T> | PathSegments>(form: Form$1<T>, name: P): PathValueOf<T, P>;
46
+ /**
47
+ * Get field value by path
48
+ * @param form
49
+ * @param path
50
+ */
51
+ declare function getValueByPath({ initialValues, parsedValues, values, deleted }: Form$1, path: Path): any;
52
+ /** Options accepted by {@link setValue} / {@link setValueByPath} / {@link
53
+ * changeValue} / {@link changeValueByPath}. `shouldValidate`/`shouldTouch`
54
+ * default to `false`; omitting the options object entirely keeps the plain
55
+ * set-value behavior (no validation, no touched marking, dirty stays
56
+ * derived). */
57
+ type SetFieldOptions = {
58
+ /** Run the field's registered validator (if any) after the value lands,
59
+ * same as triggering that single field. Defaults to `false`. */
60
+ shouldValidate?: boolean;
61
+ /** Mark the field as touched. Defaults to `false`. */
62
+ shouldTouch?: boolean;
63
+ /** Land the value as a commit instead of an edit: the value becomes the
64
+ * field's dirty-comparison baseline, so `getDirtyFields`/`isDirty`/
65
+ * `getFieldState().isDirty` read the field as clean, and a later write
66
+ * dirties it only by differing from the new baseline. `true` (or
67
+ * omitting the flag) keeps the default derived behavior — dirty while
68
+ * the live value differs from initialValues. */
69
+ shouldDirty?: boolean;
70
+ };
71
+ /**
72
+ * Set field value
73
+ * @param form
74
+ * @param name
75
+ * @param value
76
+ * @param options
77
+ */
78
+ declare function setValue<T extends Record<string, any> = any, P extends FieldPath<T> | PathSegments = FieldPath<T> | PathSegments>(form: Form$1<T>, name: P, value: PathValueOf<T, P>, options?: SetFieldOptions): void;
79
+ /**
80
+ * Set field value
81
+ * @param form
82
+ * @param path
83
+ * @param value
84
+ * @param options
85
+ */
86
+ declare function setValueByPath(form: Form$1, path: Path, value: any, options?: SetFieldOptions): void;
87
+ /**
88
+ * The write of {@link setValueByPath} minus the `'change'` emit: the
89
+ * render-time {@link useField} `initialValue` seed. The field's first
90
+ * paint (SSR included — effects never run on the server) must already
91
+ * carry the value, so the write happens during render where emitting is
92
+ * illegal; the seeding field announces it from its post-commit effect
93
+ * through {@link emitChangeByPath} instead.
94
+ *
95
+ * Everything else matches a plain write: descendant keys of the seeded
96
+ * path are pruned, the branch's tombstones and committed baselines are
97
+ * revived/dropped, and both memo caches are invalidated. Like the effect
98
+ * seed it replaces, the caller guards it to paths with no value yet.
99
+ */
100
+ declare function seedValueByPath(form: Form$1, path: Path, value: any): void;
101
+ /** Announce a {@link seedValueByPath} that happened during render: the
102
+ * payload-carrying `'change'` emit {@link setValueByPath} would have
103
+ * fired, split out so it can run post-commit where emitting is safe.
104
+ * Subscribers that rendered after the seed re-read an unchanged snapshot
105
+ * and bail; subscribers from earlier commits resync. */
106
+ declare function emitChangeByPath({ emitter }: Form$1, path: Path): void;
107
+ /** Per-form registry of mounted fields' validation-mode overrides: path
108
+ * key -> the field's `mode` option (undefined = follow {@link Form.mode})
109
+ * plus an owner token so competing mounts at one path clean up safely.
110
+ * Presence of an entry is the "a field is mounted at this path" signal
111
+ * that routes {@link changeValueByPath} into the gated user-change
112
+ * pipeline ({@link userChangeByPath}). Held in a WeakMap so the Form
113
+ * shape carries only plain state fields. */
114
+ /** Snapshot of one field's aggregated state, as {@link getFieldState}
115
+ * returns it. `errors` is the stored array shared with the form — treat it
116
+ * as read-only, like every {@link getFieldErrors} result. */
117
+ type FieldState<T = any> = {
118
+ value: T;
119
+ error: FieldError | undefined;
120
+ errors: FieldError[];
121
+ isDirty: boolean;
122
+ isTouched: boolean;
123
+ isValidating: boolean;
124
+ };
125
+ /**
126
+ * Get one field's aggregated state: the layered value ({@link getValue}),
127
+ * the first error ({@link getError}) and every error ({@link
128
+ * getFieldErrors}), dirtiness, the touched flag, and whether a validator
129
+ * is in flight. `isDirty` applies the same per-field rule as {@link
130
+ * getDirtyFields}: a live value exists and differs from initialValues at
131
+ * that path (parsedValues never counts — parsing is not an edit).
132
+ *
133
+ * @param form
134
+ * @param name
135
+ */
136
+ declare function getFieldState<T extends Record<string, any> = any, P extends FieldPath<T> | PathSegments = FieldPath<T> | PathSegments>(form: Form$1<T>, name: P): FieldState<PathValueOf<T, P>>;
137
+ /**
138
+ * Remove a field: by default its live value drops out of reads and
139
+ * `getValues()` (the path is tombstoned, so it never falls back to
140
+ * initialValues), its dirty baseline, touched flag and errors are cleared.
141
+ * The keep-flags preserve slices of that state instead.
142
+ *
143
+ * @param form
144
+ * @param name
145
+ */
146
+ /**
147
+ * Options accepted by {@link removeField}. All flags default to `false` —
148
+ * the historical remove semantics (value dropped, path tombstoned, dirty
149
+ * baseline/touched/errors cleared). Names mirror react-hook-form's
150
+ * `unregister` options to ease migration; RHF's `shouldValidate` and
151
+ * `keepDefaultValue` have no counterparts (removal never validates, and
152
+ * the tombstone is exactly the "do not revive from initialValues" choice).
153
+ */
154
+ type RemoveFieldOptions = {
155
+ /** Keep the field's live value and dirty baseline instead of
156
+ * tombstoning: reads and `getValues()` keep returning the value, submit
157
+ * includes it, and dirtiness against initialValues is preserved. */
158
+ keepValue?: boolean;
159
+ /** Keep the field's dirty baseline. Implies `keepValue` — a removed
160
+ * value has nothing to be dirty about. */
161
+ keepDirty?: boolean;
162
+ /** Keep the field's touched flag instead of clearing it. */
163
+ keepTouched?: boolean;
164
+ /** Keep the field's errors instead of clearing them. */
165
+ keepError?: boolean;
166
+ };
167
+ declare function removeField(form: Form$1, name: Name, options?: RemoveFieldOptions): void;
168
+ /**
169
+ * Remove field
170
+ * @param form
171
+ * @param path
172
+ * @param options keep-flags to preserve slices of state through the removal
173
+ */
174
+ declare function removeFieldByPath(form: Form$1, path: Path, options?: RemoveFieldOptions): void;
175
+ /**
176
+ * Set form initialValues
177
+ *
178
+ * Content-based early return: a new reference with equal content (the
179
+ * re-rendered inline literal) is a no-op, so committed edits survive, while
180
+ * genuinely changed content swaps the baseline and re-seeds — live values
181
+ * and tombstones are cleared, touched flags and errors survive.
182
+ * @param form
183
+ * @param initialValues
184
+ */
185
+ declare function setInitialValues(form: Form$1, initialValues: any): void;
186
+ /** Options accepted by {@link reset}. Every flag defaults to `false` —
187
+ * omitting the object (or any flag) keeps the plain full-reset behavior.
188
+ * Names mirror react-hook-form's reset options to ease migration. */
189
+ type ResetOptions = {
190
+ /** Keep the current values of fields that are dirty — differ from the
191
+ * pre-reset initialValues (the same rule {@link getDirtyFields} applies).
192
+ * Clean fields fall back to the new initialValues as usual. */
193
+ keepDirtyValues?: boolean;
194
+ /** Keep every field's current live value instead of returning to the
195
+ * baseline (react-hook-form's `keepValues` — a strict superset of
196
+ * `keepDirtyValues`, which only preserves dirty fields' values).
197
+ * Dirtiness is recomputed against the post-reset baseline, so kept
198
+ * values that differ from a newly provided baseline count as dirty. */
199
+ keepValues?: boolean;
200
+ /** Ignore a newly provided `initialValues` argument and keep the current
201
+ * baseline — fields still return to it (react-hook-form's
202
+ * `keepDefaultValues`). */
203
+ keepDefaultValues?: boolean;
204
+ /** Keep the touched set instead of clearing it. */
205
+ keepTouched?: boolean;
206
+ /** Keep field errors instead of clearing them. */
207
+ keepErrors?: boolean;
208
+ /** Keep the submitted flag (`isSubmitSuccessful`) instead of clearing
209
+ * it. */
210
+ keepIsSubmitted?: boolean;
211
+ /** Keep `submitCount` instead of resetting it to 0. */
212
+ keepSubmitCount?: boolean;
213
+ /** Keep `isSubmitting` instead of resetting it to false. */
214
+ keepIsSubmitting?: boolean;
215
+ };
216
+ /**
217
+ * Reset form
218
+ * @param form
219
+ * @param initialValues new baseline — omitted (or undefined), the form
220
+ * keeps its current initialValues and fields simply return to them
221
+ * (react-hook-form's reset-without-values semantics)
222
+ * @param options keep-flags to preserve slices of state through the reset
223
+ */
224
+ declare function reset(form: Form$1, initialValues?: any, options?: ResetOptions): void;
225
+ /** Options accepted by {@link resetField}. The flags default to `false`;
226
+ * `value` has no default — omitted, the field falls back to initialValues;
227
+ * provided, the explicit value becomes the live value with no fallback at
228
+ * all. Mirrors react-hook-form's resetField options (`value` plays their
229
+ * `defaultValue`'s role) to ease migration. */
230
+ type ResetFieldOptions = {
231
+ /** Keep the field's touched flag instead of clearing it. */
232
+ keepTouched?: boolean;
233
+ /** Keep the field's errors instead of clearing them. */
234
+ keepErrors?: boolean;
235
+ /** Explicit post-reset value for the field — never falls back to
236
+ * initialValues. */
237
+ value?: any;
238
+ };
239
+ /**
240
+ * Reset a single field: drop its live value (reads fall back to the
241
+ * baseline — initialValues, or the schema's parsed output when one
242
+ * exists, in which case the path is removed from parsedValues and the
243
+ * initial value pinned back so the field reads initialValues again),
244
+ * clear its touched flag and errors, and revive the path's removal
245
+ * tombstones — the inverse of {@link removeFieldByPath}. Other fields
246
+ * and the submission flags are untouched; see {@link reset} for the
247
+ * form-wide counterpart.
248
+ *
249
+ * @param form
250
+ * @param name
251
+ * @param options
252
+ */
253
+ declare function resetField<T extends Record<string, any> = any, P extends FieldPath<T> | PathSegments = FieldPath<T> | PathSegments>(form: Form$1<T>, name: P, options?: ResetFieldOptions): void;
254
+
255
+ /**
256
+ * Set field touched state
257
+ * @param form
258
+ * @param name
259
+ */
260
+ declare function setTouched(form: Form$1, name: Name): void;
261
+ /**
262
+ * Set field touched state
263
+ * @param form
264
+ * @param path
265
+ */
266
+ declare function setTouchedByPath({ emitter, touched }: Form$1, path: Path): void;
267
+ /**
268
+ * Check if field has been touched
269
+ * @param form
270
+ * @param name
271
+ */
272
+ declare function hasTouched<T extends Record<string, any> = any, P extends FieldPath<T> | PathSegments = FieldPath<T> | PathSegments>(form: Form$1<T>, name: P): boolean;
273
+ /**
274
+ * Check if field has been touched
275
+ * @param form
276
+ * @param path
277
+ */
278
+ declare function hasTouchedByPath({ touched }: Form$1, path: Path): boolean;
279
+ /**
280
+ * Is dirty -- any value differs from initialValues
281
+ * @param form
282
+ */
283
+ /**
284
+ * Get touched fields as user-facing dotted paths ('a.b', 'a.0.c'), unlike
285
+ * the JSON array keys stored in the touched Set.
286
+ * @param form
287
+ * @return array of touched fields' dotted paths
288
+ */
289
+ declare function getTouchedFields({ touched }: Form$1): string[];
290
+ /**
291
+ * Is touched -- any field has been touched
292
+ * @param form
293
+ */
294
+ declare function isTouched({ touched }: Form$1): boolean;
295
+
296
+ /**
297
+ * Is dirty -- any value differs from initialValues
298
+ * @param form
299
+ */
300
+ declare function isDirty(form: Form$1): boolean;
301
+ /**
302
+ * Get dirty fields -- fields whose current value differs from initialValues.
303
+ * Keys are user-facing dotted paths ('a.b', 'a.0.c'), unlike the JSON array
304
+ * keys stored in the values Map.
305
+ * @param form
306
+ * @return object mapping each dirty field's dotted path to true; the same
307
+ * reference is returned until the dirty set actually changes
308
+ */
309
+ declare function getDirtyFields(form: Form$1): Record<string, boolean>;
310
+
311
+ /**
312
+ * Set a field value as a user change.
313
+ *
314
+ * The write rides the same gated user-change pipeline a user typing into
315
+ * the field would fire when a field is mounted on the path (registered
316
+ * through {@link registerFieldMode} — `useField` registers on mount): the
317
+ * field's effective `mode` (per-field override included) and the form's
318
+ * `reValidateMode` drive validation exactly as in {@link
319
+ * userChangeByPath}. With no mounted field on the path it degrades to a
320
+ * plain value set ({@link setValue}).
321
+ *
322
+ * This is the channel for component-library bridges that hand a control a
323
+ * plain setter bound to a field — they cannot rebuild the gating from
324
+ * public form state, because the per-field mode override and the
325
+ * live-error view that gates `reValidateMode` live in the field
326
+ * registration, not in public state.
327
+ *
328
+ * Contrast {@link setValue}: that is the imperative channel — its
329
+ * `shouldValidate` option kicks the field's validator unconditionally,
330
+ * ignoring any mode. Functional updaters are the caller's to evaluate
331
+ * ({@link getValue}).
332
+ *
333
+ * `options` carries the same {@link SetFieldOptions}: on the fallback path
334
+ * (no mounted field) they forward to {@link setValueByPath} wholesale,
335
+ * while on the mounted-field path only `shouldDirty: false` applies — the
336
+ * write lands as a commit while the field's own mode gating keeps driving
337
+ * validation, which is the point of this channel.
338
+ *
339
+ * @param form
340
+ * @param name
341
+ * @param value
342
+ * @param options
343
+ */
344
+ declare function changeValue<T extends Record<string, any> = any, P extends FieldPath<T> | PathSegments = FieldPath<T> | PathSegments>(form: Form$1<T>, name: P, value: PathValueOf<T, P>, options?: SetFieldOptions): void;
345
+ /**
346
+ * Set a field value as a user change, by parsed path
347
+ * @param form
348
+ * @param path
349
+ * @param value
350
+ * @param options
351
+ */
352
+ declare function changeValueByPath(form: Form$1, path: Path, value: any, options?: SetFieldOptions): void;
353
+ /**
354
+ * Register a mounted field's `mode` override at `path` for user-change
355
+ * gating ({@link userChangeByPath} / {@link userBlur}). Returns the
356
+ * registration token for {@link unregisterFieldMode} plus whether the
357
+ * slot was already occupied — two fields mounted at one path is almost
358
+ * always a bug: the latest mount's mode governs every user-change write
359
+ * there, which the React layer warns about in DEV.
360
+ *
361
+ * @param form
362
+ * @param path
363
+ * @param mode the field's `mode` option, or undefined to follow
364
+ * {@link Form.mode}
365
+ * @return `token` to hand to {@link unregisterFieldMode}; `displaced`
366
+ * true when a previous registration at the same path still owned
367
+ * the slot
368
+ */
369
+ declare function registerFieldMode(form: Form$1, path: Path, mode: ValidationMode | undefined): {
370
+ token: object;
371
+ displaced: boolean;
372
+ };
373
+ /** Drop a {@link registerFieldMode} registration. A later mount at the
374
+ * same path keeps its slot: only the entry owned by `token` is removed. */
375
+ declare function unregisterFieldMode(form: Form$1, path: Path, token: object): void;
376
+ /**
377
+ * Write a bound field's user change by path: the write plus the
378
+ * mode/reValidateMode-gated validation pipeline — what a bound field's
379
+ * onChange does when the user types. Reads the effective mode from the
380
+ * field-mode registry (the latest mount's override governs), so a plain
381
+ * write happens when no field is registered at `path`. Framework
382
+ * adapters (React's `useField`, a Solid binding) forward their field
383
+ * change events here.
384
+ *
385
+ * @param form
386
+ * @param path
387
+ * @param value
388
+ */
389
+ declare function userChangeByPath(form: Form$1, path: Path, value: any): void;
390
+ /**
391
+ * A bound field's blur: mark the path touched, then kick its validator
392
+ * under the blur-side gate (`mode` `'onBlur'`/`'onTouched'`/`'all'`, or
393
+ * `reValidateMode: 'onBlur'` while the field carries a live error). The
394
+ * touched marking is unconditional — a field counts as touched on blur
395
+ * regardless of mode. Framework adapters forward field blur events here.
396
+ *
397
+ * @param form
398
+ * @param path
399
+ */
400
+ declare function userBlur(form: Form$1, path: Path): void;
401
+
402
+ declare function setIsSubmitting(form: Form$1, value: boolean): void;
403
+ declare function incrementSubmitCount(form: Form$1): void;
404
+ declare function setSubmitSuccessful(form: Form$1, value: boolean): void;
405
+ /**
406
+ * Set the form-level disabled flag and emit a payload-less 'disabled'
407
+ * event — subscribed fields (useField and the components built on it)
408
+ * re-render with the merged disabled state: form flag || their own
409
+ * `disabled` option.
410
+ * @param form
411
+ * @param value
412
+ */
413
+ declare function setDisabled(form: Form$1, value: boolean): void;
414
+ /** Submit callbacks for {@link handleSubmit}. All optional — a missing
415
+ * callback is simply skipped, matching the <Form> component semantics. */
416
+ type HandleSubmitOptions<T extends Record<string, any> = any> = {
417
+ /** Called after validation passes, before onValidSubmit. */
418
+ onSubmit?: (values: T, e?: any) => void | Promise<void>;
419
+ /** Called after validation passes, following a successful onSubmit. */
420
+ onValidSubmit?: (values: T, e?: any) => void | Promise<void>;
421
+ /**
422
+ * Called when validation fails.
423
+ * @param errors array of {path, type, message} entries in insertion
424
+ * order; path is the dotted field path ('a.b', 'list.0'), type is
425
+ * the error kind ('custom' for plain string errors, 'native' for
426
+ * failed DOM constraint validation), message is the display text
427
+ * @param values current form values
428
+ */
429
+ onInvalidSubmit?: (errors: FieldErrorEntry[], values: T) => void;
430
+ /**
431
+ * Called after validation passes and the submit callbacks ran, with the
432
+ * final (schema-coerced) values — the slot <Form>'s `action` prop uses
433
+ * to dispatch React 19 server actions with FormData. Runs inside the
434
+ * same isSubmitting window and is awaited like onSubmit/onValidSubmit.
435
+ */
436
+ onAction?: (values: T, e?: any) => void | Promise<void>;
437
+ /**
438
+ * Focus the first error field after a failed submit. Defaults to true —
439
+ * only an explicit `false` disables it. When custom validation fails,
440
+ * a 'focusError' event carrying the first error's path key is emitted
441
+ * on the form (bound fields such as <Field> subscribe and focus their
442
+ * input); when native constraint validation fails, the submitted
443
+ * form's first ':invalid' control is focused directly.
444
+ */
445
+ shouldFocusError?: boolean;
446
+ };
447
+ /**
448
+ * Create an async submit handler for `form` — the headless counterpart of
449
+ * the <Form> component's onSubmit wiring.
450
+ *
451
+ * Behavior mirrors <Form> exactly: preventDefault when present, then the
452
+ * submit state machine (isSubmitting/submitCount/isSubmitSuccessful) runs
453
+ * around native constraint validation (via `e.currentTarget.checkValidity`,
454
+ * skipped when the target has no checkValidity — e.g. React Native or
455
+ * toolbar-button submits) and custom validators. Failed validation fires
456
+ * onInvalidSubmit with the flattened error entries; a passing submit runs
457
+ * onSubmit then onValidSubmit. Errors thrown by either are swallowed into
458
+ * isSubmitSuccessful=false rather than rejecting the returned promise.
459
+ * Failed validation also focuses the offending field (see
460
+ * {@link HandleSubmitOptions.shouldFocusError}).
461
+ *
462
+ * @param form form instance
463
+ * @param options submit callbacks
464
+ * @return async event handler, callable without an event object
465
+ */
466
+ declare function handleSubmit<T extends Record<string, any> = any>(form: Form$1<T>, options?: HandleSubmitOptions<T>): (e?: {
467
+ preventDefault?: () => void;
468
+ currentTarget?: any;
469
+ }) => Promise<void>;
470
+
8
471
  /** Type tag of a failed rule, as stored on the resulting FieldError. */
9
472
  type RuleType = 'required' | 'min' | 'max' | 'minLength' | 'maxLength' | 'pattern';
10
473
  /**
@@ -14,7 +477,7 @@ type RuleType = 'required' | 'min' | 'max' | 'minLength' | 'maxLength' | 'patter
14
477
  * rule name) instead of only surfacing through the browser's validity
15
478
  * bubble, so any design system can render the messages uniformly.
16
479
  */
17
- interface FieldRules {
480
+ type FieldRules = {
18
481
  /**
19
482
  * Fails on empty values: `''`, `undefined` or `null` (`0` and `false`
20
483
  * count as filled). A string is the error message; `true` uses the
@@ -41,9 +504,9 @@ interface FieldRules {
41
504
  * centralizing or localizing messages.
42
505
  */
43
506
  messages?: Partial<Record<Exclude<RuleType, 'required'>, string>>;
44
- }
507
+ };
45
508
 
46
- interface UseFieldOptions$1<TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | Name = Name> {
509
+ type UseFieldOptions$1<TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | PathSegments = FieldPath<TValues> | PathSegments> = {
47
510
  form?: Form$1<TValues>;
48
511
  name: TPath;
49
512
  initialValue?: any;
@@ -51,16 +514,22 @@ interface UseFieldOptions$1<TValues extends Record<string, any> = any, TPath ext
51
514
  validate?: Validator;
52
515
  /**
53
516
  * Declarative rules (required/min/max/minLength/maxLength/pattern),
54
- * compiled into a validator that runs before `validate` both sources'
55
- * errors merge into one FieldError[], rules errors ahead. Failures land
56
- * in the form's error state with the given or default messages.
517
+ * compiled into a synchronous validator. `required` is special: it runs
518
+ * immediately on every kick never debounced and while it fails,
519
+ * `validate` is skipped (the expensive check never sees an empty value).
520
+ * The other rules compose with `validate` — rules run first, then
521
+ * `validate` (awaited when async), merging both sources' errors with
522
+ * rules errors ahead. Failures land in the form's error state with the
523
+ * given or default messages.
57
524
  */
58
525
  rules?: FieldRules;
59
526
  /**
60
527
  * Milliseconds to debounce this field's validation kicks. Defaults to 0
61
528
  * (validate immediately); while the timer is pending the field counts as
62
529
  * validating, so `trigger`/`ensureValidate` wait out the window. Only the
63
- * last kick inside the window runs the validator.
530
+ * last kick inside the window runs the validator. The `required` rule is
531
+ * exempt: it runs synchronously on every kick, so a required failure
532
+ * shows immediately instead of waiting out the window.
64
533
  */
65
534
  validateDebounce?: number;
66
535
  /**
@@ -78,6 +547,18 @@ interface UseFieldOptions$1<TValues extends Record<string, any> = any, TPath ext
78
547
  * `disabled`. A field cannot opt out of a disabled form.
79
548
  */
80
549
  disabled?: boolean;
550
+ /**
551
+ * Uncontrolled mode: the field never subscribes to its own value, so
552
+ * typing re-renders nothing — the store still carries every write
553
+ * (getValues/submit/validation read it), and errors/touched/disabled/
554
+ * validating still re-render the field like react-hook-form's
555
+ * `register`. The result's `value` is the mount-time snapshot (initial
556
+ * value seed or baseline); reset/setInitialValues do not push into it
557
+ * or into the DOM — read live values with useValue/getValues instead.
558
+ * Attach the result with `<input defaultValue={field.value}>`-style
559
+ * binding (no `value` prop), exactly like <Field uncontrolled /> does.
560
+ */
561
+ uncontrolled?: boolean;
81
562
  /**
82
563
  * Field-level validation mode override: when given, this field validates
83
564
  * on its own schedule instead of `form.mode` — every other field keeps
@@ -111,13 +592,13 @@ interface UseFieldOptions$1<TValues extends Record<string, any> = any, TPath ext
111
592
  * the field's own path is a no-op (its own change already validates it).
112
593
  */
113
594
  validateDeps?: FieldPath<TValues>[];
114
- }
595
+ };
115
596
  /**
116
597
  * The result of {@link useField}. Deliberately a closed shape: no index
117
598
  * signature, so a typo'd property access (`field.vlaue`) is a type error
118
599
  * instead of silently reading `undefined`.
119
600
  */
120
- interface UseFieldResult<TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | Name = Name> {
601
+ type UseFieldResult<TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | PathSegments = FieldPath<TValues> | PathSegments> = {
121
602
  /** The form instance this field is bound to (explicit prop or context) —
122
603
  * handy for consumers that need direct access to the headless API. */
123
604
  form: Form$1<TValues>;
@@ -137,14 +618,24 @@ interface UseFieldResult<TValues extends Record<string, any> = any, TPath extend
137
618
  * toggled by `setDisabled`) OR-ed with this field's own `disabled`
138
619
  * option, updated live through the form's event core. */
139
620
  disabled: boolean;
140
- }
141
- declare function useField<TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | Name = Name>(options: UseFieldOptions$1<TValues, TPath>): UseFieldResult<TValues, TPath>;
621
+ /**
622
+ * Callback ref carrying the focus channel: attach it to your input
623
+ * element (`<input ref={field.focusRef} />`) so `setFocus` and a failed
624
+ * submit's first-error auto-focus (`shouldFocusError`) can focus this
625
+ * headless field — the same 'focusError' wiring `<Field>` performs for
626
+ * its own input. Without it, focus requests aimed at this field are
627
+ * silent no-ops, matching `setFocus`'s contract: focusing a field
628
+ * whose element is not bound neither throws nor focuses anything.
629
+ */
630
+ focusRef: (el: any) => void;
631
+ };
632
+ declare function useField<TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | PathSegments = FieldPath<TValues> | PathSegments>(options: UseFieldOptions$1<TValues, TPath>): UseFieldResult<TValues, TPath>;
142
633
 
143
- interface FieldArrayItem {
634
+ type FieldArrayItem = {
144
635
  id: string;
145
636
  index: number;
146
- }
147
- interface UseFieldArrayResult {
637
+ };
638
+ type UseFieldArrayResult = {
148
639
  fields: FieldArrayItem[];
149
640
  append: (value: any) => void;
150
641
  prepend: (value: any) => void;
@@ -154,7 +645,7 @@ interface UseFieldArrayResult {
154
645
  move: (from: number, to: number) => void;
155
646
  replace: (values: any[]) => void;
156
647
  update: (index: number, value: any) => void;
157
- }
648
+ };
158
649
  declare function useFieldArray(options: {
159
650
  name: Name;
160
651
  form?: Form$1;
@@ -165,7 +656,7 @@ declare function useFieldArray(options: {
165
656
  * `name` (path key) so nested fields can build on it —
166
657
  * `useField({name: ['tags', item.index, 'label']})`.
167
658
  */
168
- interface UseFieldArrayItemResult<TValue = any> {
659
+ type UseFieldArrayItemResult<TValue = any> = {
169
660
  value: TValue;
170
661
  /** Overwrite this row's value in place (same array-layer write as
171
662
  * `update(index, value)`: the row's id and position are kept). */
@@ -183,7 +674,7 @@ interface UseFieldArrayItemResult<TValue = any> {
183
674
  * undefined and nothing writes the dangling path. */
184
675
  index: number;
185
676
  form: Form$1;
186
- }
677
+ };
187
678
  /**
188
679
  * Subscribe to a single row of a {@link useFieldArray} field — the
189
680
  * per-item counterpart `useFieldArray` alone cannot offer. `useFieldArray`
@@ -215,8 +706,8 @@ declare function useFieldArrayItem<TValue = any>(options: {
215
706
  form?: Form$1;
216
707
  }): UseFieldArrayItemResult<TValue>;
217
708
 
218
- declare const FormContext: React.Context<any>;
219
- declare const FormProvider: React.Provider<any>;
709
+ declare const FormContext: React.Context<Form$1<any> | null>;
710
+ declare const FormProvider: React.Provider<Form$1<any> | null>;
220
711
  /**
221
712
  * Read the form from the module-level {@link FormContext}. Pass the values
222
713
  * shape — `useFormContext<Values>()` — to get a fully typed `Form<Values>`
@@ -249,7 +740,7 @@ declare function createFormContext<TValues extends Record<string, any> = any>():
249
740
  children: ReactNode;
250
741
  }) => ReactNode;
251
742
  useFormContext: () => Form$1<TValues>;
252
- useField: <TPath extends FieldPath<TValues> | Name = Name>(options: {
743
+ useField: <TPath extends FieldPath<TValues> | PathSegments = FieldPath<TValues> | PathSegments>(options: {
253
744
  name: TPath;
254
745
  } & Omit<UseFieldOptions$1<TValues, TPath>, "form">) => UseFieldResult<TValues, TPath>;
255
746
  useFieldArray: (options: {
@@ -264,6 +755,69 @@ declare const CheckboxGroupContext: React.Context<any>;
264
755
  declare const CheckboxGroupProvider: React.Provider<any>;
265
756
  declare function useCheckboxGroupContext(): any;
266
757
 
758
+ /** Subscription granularity for {@link onPathEvent}.
759
+ * - `'leaf'`: the subscriber reads exactly one key ({@link
760
+ * useValueByPath}); only writes at that key or above it can change what
761
+ * it reads.
762
+ * - `'branch'`: the subscriber aggregates a whole subtree below a key
763
+ * ({@link useFieldArray}); descendant writes matter too. */
764
+ type WatchScope = 'leaf' | 'branch';
765
+ /** Events {@link subscribe} can watch. `'errors'` and `'touched'` are
766
+ * stored per exact key, so they match exact keys ({@link onKeyEvent});
767
+ * `'change'`, `'validating'`, `'submitting'`, `'submitCount'`,
768
+ * `'disabled'` and `'submitSuccessful'` go through
769
+ * {@link onPathEvent}. `'validating'` carries paths (one per async
770
+ * validator round) and matches by path exactly like `'change'`;
771
+ * `'submitting'`, `'submitCount'`, `'disabled'` and
772
+ * `'submitSuccessful'` are payload-less broadcasts, so `name` never
773
+ * narrows them — every subscriber hears every emission. */
774
+ type SubscribeEvent = 'change' | 'errors' | 'touched' | 'validating' | 'submitting' | 'submitCount' | 'submitSuccessful' | 'disabled' | 'loading';
775
+ /** Options accepted by {@link subscribe}. */
776
+ type SubscribeOptions = {
777
+ /** Path (or list of paths) to watch. Omit to receive every emission of
778
+ * `event`, payload-less broadcasts included. A single segments path
779
+ * (`['tags', 0]`) and a list of names (`['tags', 'user.name']`) are told
780
+ * apart by the same rule `trigger` uses: only a segments path can hold
781
+ * a number. */
782
+ name?: Name | Name[];
783
+ /** Event to watch. Defaults to `'change'`. */
784
+ event?: SubscribeEvent;
785
+ /** Which writes around `name` are relevant — `'leaf'` or `'branch'`.
786
+ * Only meaningful for the path-carrying events `'change'` and
787
+ * `'validating'`: `'errors'`/`'touched'` match exact keys and
788
+ * `'submitting'`/`'submitCount'`/`'disabled'`/`'submitSuccessful'`
789
+ * are payload-less. Defaults to `'branch'` — the intuitive linkage
790
+ * semantics, where subscribing to `'tags'` means the whole branch. */
791
+ scope?: WatchScope;
792
+ /** Invoked with no arguments after each matching emission. Read fresh
793
+ * state through the `get*` readers inside it. */
794
+ callback: () => void;
795
+ };
796
+ /**
797
+ * Subscribe to form events imperatively — the non-render counterpart of
798
+ * the `use*` hooks: linkages and side effects (province changed → clear
799
+ * city, autosave, analytics) run without mounting a watching component.
800
+ *
801
+ * Without `name`, `callback` fires on every `event` emission, payload-less
802
+ * broadcasts (reset, setInitialValues) included. With `name`, matching
803
+ * follows the event's shape: `'errors'`/`'touched'` match the exact key
804
+ * ({@link onKeyEvent}) — another field's error never wakes this
805
+ * subscriber — while `'change'`/`'validating'`/`'submitting'`/
806
+ * `'submitCount'`/`'disabled'`/`'submitSuccessful'` go through
807
+ * {@link onPathEvent}, so the default `'branch'` scope wakes a `'tags'`
808
+ * subscriber when any `tags.*` descendant is written. `'validating'`
809
+ * carries a path per validator round and narrows by path like
810
+ * `'change'`; `'disabled'`/`'submitSuccessful'` (like `'submitting'`)
811
+ * are payload-less broadcasts that every named subscriber receives. A
812
+ * `name` array builds one subscription per path and the returned
813
+ * function unsubscribes them all.
814
+ *
815
+ * @param form the form to watch
816
+ * @param options event, name(s), scope and callback
817
+ * @return unsubscribe function
818
+ */
819
+ declare function subscribe(form: Form$1, options: SubscribeOptions): () => void;
820
+
267
821
  /**
268
822
  * Create a form instance bound to this component.
269
823
  *
@@ -279,6 +833,23 @@ declare function useCheckboxGroupContext(): any;
279
833
  declare function useForm<T extends Record<string, any> = any>(options?: Options<T> & {
280
834
  values?: T;
281
835
  }): Form$1<T>;
836
+ /**
837
+ * Shared core of {@link useWatch} and the path-scoped hooks: a
838
+ * useSyncExternalStore binding over a custom event subscription.
839
+ * `subscribeFactory` receives the invalidate callback (drop the snapshot
840
+ * cache, then notify React) and returns its unsubscribe function, so the
841
+ * core stays identical whether the subscription is global or scoped to
842
+ * one path.
843
+ *
844
+ * The optional `isEqual` comparator redirects `invalidate`: instead of
845
+ * dropping the cache and waking React unconditionally, an event first
846
+ * recomputes the getter and asks `isEqual` whether anything observable
847
+ * changed — an equal verdict keeps the cached snapshot and skips the
848
+ * notify entirely (no render at all), an unequal one stores the fresh
849
+ * snapshot and notifies. Omitted, the historical drop-and-notify pipeline
850
+ * runs byte-for-byte unchanged.
851
+ */
852
+ declare function useWatchCore<T>(subscribeFactory: (invalidate: () => void) => () => void, getter: () => T, isEqual?: (prev: T, next: T) => boolean): T;
282
853
  /**
283
854
  * Subscribe to a form event and keep the component's snapshot of `getter()`
284
855
  * in sync with the form state.
@@ -286,12 +857,28 @@ declare function useForm<T extends Record<string, any> = any>(options?: Options<
286
857
  * Built on useSyncExternalStore, so snapshots taken while React renders are
287
858
  * guaranteed consistent (no tearing under concurrent rendering) and changes
288
859
  * emitted before the subscription effect runs are still picked up.
860
+ *
861
+ * The first argument is the form for the unified `fn(form, ...)` context
862
+ * shape every hook shares; the raw emitter form remains accepted for
863
+ * back-compat and for subscription sources that are not a full form.
864
+ *
865
+ * By default the re-render surface is the event's own scope: every emit
866
+ * the subscription hears drops the snapshot cache and wakes React, which
867
+ * then bails out when the recomputed snapshot is reference-identical
868
+ * (Object.is) — the path/leaf scoping every built-in reader relies on.
869
+ * The optional `isEqual` comparator exists for wide-scope getters that
870
+ * return a fresh reference per call (a whole-values selector, say): each
871
+ * event recomputes the getter and asks `isEqual` whether the result is
872
+ * observably the same, and an equal verdict skips notifying React
873
+ * altogether — no render, not even a bailed-out one. An unequal verdict
874
+ * stores the new snapshot and re-renders. Same contract as TanStack's
875
+ * `useSelector` compare. Omitted, behavior is unchanged.
289
876
  */
290
- declare function useWatch<T>(emitter: EventEmitter, event: string, getter: () => T): T;
877
+ declare function useWatch<T>(formOrEmitter: Form$1 | EventEmitter<FormEvents>, event: SubscribeEvent, getter: () => T, isEqual?: (prev: T, next: T) => boolean): T;
291
878
  /**
292
879
  * Get field value state
293
880
  */
294
- declare function useValue<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form$1<T>, name: P): PathValueOf<T, P>;
881
+ declare function useValue<T extends Record<string, any> = any, P extends FieldPath<T> | PathSegments = FieldPath<T> | PathSegments>(form: Form$1<T>, name: P): PathValueOf<T, P>;
295
882
  /**
296
883
  * Get field value state by path
297
884
  */
@@ -299,7 +886,7 @@ declare function useValueByPath(form: Form$1, path: Path): any;
299
886
  /**
300
887
  * Get field touched state
301
888
  */
302
- declare function useTouched<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form$1<T>, name: P): boolean;
889
+ declare function useTouched<T extends Record<string, any> = any, P extends FieldPath<T> | PathSegments = FieldPath<T> | PathSegments>(form: Form$1<T>, name: P): boolean;
303
890
  /**
304
891
  * Get field touched state by path
305
892
  */
@@ -308,7 +895,7 @@ declare function useTouchedByPath(form: Form$1, path: Path): boolean;
308
895
  * Get field error message state
309
896
  * @return current error's message string (display text), or undefined
310
897
  */
311
- declare function useError<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form$1<T>, name: P): string | undefined;
898
+ declare function useError<T extends Record<string, any> = any, P extends FieldPath<T> | PathSegments = FieldPath<T> | PathSegments>(form: Form$1<T>, name: P): string | undefined;
312
899
  /**
313
900
  * Get field error state by path
314
901
  * @return current FieldError object ({type, message}), or undefined
@@ -319,7 +906,7 @@ declare function useErrorByPath(form: Form$1, path: Path): FieldError | undefine
319
906
  * @return every error registered for the field (insertion order); an empty
320
907
  * array when the field has none
321
908
  */
322
- declare function useFieldErrors<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form$1<T>, name: P): FieldError[];
909
+ declare function useFieldErrors<T extends Record<string, any> = any, P extends FieldPath<T> | PathSegments = FieldPath<T> | PathSegments>(form: Form$1<T>, name: P): FieldError[];
323
910
  /**
324
911
  * Get all field errors by path
325
912
  * @return every error registered for the field (insertion order); an empty
@@ -337,8 +924,60 @@ declare function useDirtyFields(form: Form$1): Record<string, boolean>;
337
924
  * paths ('a.b', 'a.0.c'); recalculated after 'touched' events
338
925
  */
339
926
  declare function useTouchedFields(form: Form$1): string[];
927
+ /**
928
+ * Aggregate snapshot of the whole form's state flags — the one-subscription
929
+ * counterpart of react-hook-form's `formState` object (no errors object;
930
+ * per-field error state stays with `useError`/`useFieldErrors`, and
931
+ * `hasErrors`/`isValid` cover the whole-form questions).
932
+ *
933
+ * Recomputed on every state-bearing event; the field-wise comparator keeps
934
+ * the returned reference stable while nothing observably changed, so
935
+ * `useFormState(form).isDirty` re-renders no more often than the dedicated
936
+ * {@link useIsDirty}. Cheaper than calling the granular hooks one by one
937
+ * (one subscription and one snapshot instead of one per flag).
938
+ */
939
+ type FormState = {
940
+ /** Any live value differs from its baseline (see {@link isDirty}). */
941
+ isDirty: boolean;
942
+ /** Dirty fields keyed by user-facing dotted path ('a.b', 'a.0.c'). */
943
+ dirtyFields: Record<string, boolean>;
944
+ /** At least one field is touched. */
945
+ isTouched: boolean;
946
+ /** Touched fields' user-facing dotted paths. */
947
+ touchedFields: string[];
948
+ /** Any error registered (field or form level). */
949
+ hasErrors: boolean;
950
+ /** No errors are registered — react-hook-form's `isValid` semantics.
951
+ * In-flight validation is NOT factored in ({@link isValidating} is the
952
+ * separate signal; async rounds temporarily pass this flag like RHF's). */
953
+ isValid: boolean;
954
+ isSubmitting: boolean;
955
+ /** Any validation round is running (field, form-level, or a pending
956
+ * debounce window). */
957
+ isValidating: boolean;
958
+ isSubmitSuccessful: boolean | undefined;
959
+ submitCount: number;
960
+ /** Async initialValues still pending ({@link Form.isLoading}). */
961
+ isLoading: boolean;
962
+ /** The form-level disabled flag (fields OR their own `disabled`). */
963
+ disabled: boolean;
964
+ };
965
+ declare function useFormState(form: Form$1): FormState;
340
966
  declare function useHasErrors(form: Form$1): boolean;
967
+ /**
968
+ * Get whether the form currently has no errors — react-hook-form's
969
+ * `formState.isValid` counterpart. Subscribes to the `'errors'` event only;
970
+ * in-flight validation does not flip it (see {@link useIsValidating}).
971
+ */
972
+ declare function useIsValid(form: Form$1): boolean;
341
973
  declare function useIsSubmitting(form: Form$1): boolean;
974
+ /**
975
+ * Get whether an async {@link Options.initialValues} source is still
976
+ * pending — the flag a loading skeleton or a disabled submit button gates
977
+ * on until the resolved baseline lands. Subscribes to the 'loading' event
978
+ * the core emits around the resolution cycle.
979
+ */
980
+ declare function useIsLoading(form: Form$1): boolean;
342
981
  /**
343
982
  * Get whether the form accepts a submit right now:
344
983
  * `!isSubmitting && !hasErrors`. This is the single flag a submit
@@ -351,59 +990,52 @@ declare function useIsSubmitting(form: Form$1): boolean;
351
990
  */
352
991
  declare function useCanSubmit(form: Form$1): boolean;
353
992
  declare function useSubmitCount(form: Form$1): number;
354
-
355
- /** Subscription granularity for {@link onPathEvent}.
356
- * - `'leaf'`: the subscriber reads exactly one key ({@link
357
- * useValueByPath}); only writes at that key or above it can change what
358
- * it reads.
359
- * - `'branch'`: the subscriber aggregates a whole subtree below a key
360
- * ({@link useFieldArray}); descendant writes matter too. */
361
- type WatchScope = 'leaf' | 'branch';
362
- /** Events {@link subscribe} can watch. `'errors'` and `'touched'` are
363
- * stored per exact key, so they match exact keys ({@link onKeyEvent});
364
- * `'change'`, `'submitting'` and `'submitCount'` carry paths and go
365
- * through {@link onPathEvent}. */
366
- type SubscribeEvent = 'change' | 'errors' | 'touched' | 'submitting' | 'submitCount';
367
- /** Options accepted by {@link subscribe}. */
368
- type SubscribeOptions = {
369
- /** Path (or list of paths) to watch. Omit to receive every emission of
370
- * `event`, payload-less broadcasts included. A single segments path
371
- * (`['tags', 0]`) and a list of names (`['tags', 'user.name']`) are told
372
- * apart by the same rule `trigger` uses: only a segments path can hold
373
- * a number. */
374
- name?: Name | Name[];
375
- /** Event to watch. Defaults to `'change'`. */
376
- event?: SubscribeEvent;
377
- /** Which writes around `name` are relevant — `'leaf'` or `'branch'`.
378
- * Only meaningful for `'change'`: `'errors'`/`'touched'` match exact
379
- * keys and `'submitting'`/`'submitCount'` are payload-less. Defaults to
380
- * `'branch'` — the intuitive linkage semantics, where subscribing to
381
- * `'tags'` means the whole branch. */
382
- scope?: WatchScope;
383
- /** Invoked with no arguments after each matching emission. Read fresh
384
- * state through the `get*` readers inside it. */
385
- callback: () => void;
386
- };
387
993
  /**
388
- * Subscribe to form events imperatively the non-render counterpart of
389
- * the `use*` hooks: linkages and side effects (province changed → clear
390
- * city, autosave, analytics) run without mounting a watching component.
391
- *
392
- * Without `name`, `callback` fires on every `event` emission, payload-less
393
- * broadcasts (reset, setInitialValues) included. With `name`, matching
394
- * follows the event's shape: `'errors'`/`'touched'` match the exact key
395
- * ({@link onKeyEvent}) another field's error never wakes this
396
- * subscriber while `'change'`/`'submitting'`/`'submitCount'` go through
397
- * {@link onPathEvent}, so the default `'branch'` scope wakes a `'tags'`
398
- * subscriber when any `tags.*` descendant is written. A `name` array
399
- * builds one subscription per path and the returned function unsubscribes
400
- * them all.
401
- *
402
- * @param form the form to watch
403
- * @param options event, name(s), scope and callback
404
- * @return unsubscribe function
994
+ * Get whether any validation round is currently running: a field
995
+ * validator's pending `validateDebounce` window, an async field validator
996
+ * still in flight, or the form-level validate's debounce window / in-flight
997
+ * round — every one of them holds a key in `form.validating`, and the
998
+ * 'validating' events they emit (field rounds with a path payload, the
999
+ * form-level round as a payload-less broadcast) are what this subscribes
1000
+ * to. The boolean snapshot is Object.is-stable, so churn among the marks
1001
+ * (a second field opening while the first settles) costs no render while
1002
+ * the flag holds. This is the flag a submit button disables itself on, or
1003
+ * spins a spinner with, through the pre-submit validation pass it flips
1004
+ * true the moment the first round opens and back false when the last one
1005
+ * settles.
405
1006
  */
406
- declare function subscribe(form: Form$1, options: SubscribeOptions): () => void;
1007
+ declare function useIsValidating(form: Form$1): boolean;
1008
+ /**
1009
+ * Get whether the last submit succeeded: `true` once a submit's validation
1010
+ * and `onSubmit` completed without throwing, `false` after a failed submit
1011
+ * (validation rejection or a thrown callback) and before any submit ran —
1012
+ * the falsy reading of the undefined initial/reset state. Subscribes to
1013
+ * the 'submitSuccessful' event the core's setSubmitSuccessful emits, so
1014
+ * the flag flips in the same tick the outcome lands: the usual consumers
1015
+ * are a success banner and a redirect-on-success effect.
1016
+ */
1017
+ declare function useIsSubmitSuccessful(form: Form$1): boolean;
1018
+ /**
1019
+ * Get the form-level error message: the first error stored under the
1020
+ * reserved {@link FORM_ERROR} key, as display text — or undefined while
1021
+ * the slot is clean. That key is where a form-level `validate` record's
1022
+ * `_form` entry lands and where the Standard Schema adapter drops
1023
+ * path-less issues, so errors that belong to no single field still have a
1024
+ * reader. The classic usage renders it once above the submit button —
1025
+ * `useFormError(form) || null` — and the imperative twin is
1026
+ * `getError(form, FORM_ERROR)`.
1027
+ */
1028
+ declare function useFormError(form: Form$1): string | undefined;
1029
+ /**
1030
+ * Get every form-level error: all errors stored under the reserved
1031
+ * {@link FORM_ERROR} key (insertion order), an empty array when the slot
1032
+ * is clean. The plural twin of {@link useFormError} for forms that stack
1033
+ * several form-level issues — each path-less Standard Schema issue lands
1034
+ * in this slot. The array reference is stable between unrelated events
1035
+ * (the stored array or a shared empty constant), so consumers can memo on
1036
+ * it; the imperative counterpart is `getFieldErrors(form, FORM_ERROR)`.
1037
+ */
1038
+ declare function useFormErrors(form: Form$1): FieldError[];
407
1039
 
408
1040
  /**
409
1041
  * Props for <Form>.
@@ -419,7 +1051,7 @@ declare function subscribe(form: Form$1, options: SubscribeOptions): () => void;
419
1051
  * The submit flow itself lives in the headless `handleSubmit` (see form.ts);
420
1052
  * this component is a thin wrapper that binds it to the rendered <form>.
421
1053
  */
422
- interface FormProps<T extends Record<string, any> = any> extends Omit<React.FormHTMLAttributes<HTMLFormElement>, 'onSubmit'> {
1054
+ type FormProps<T extends Record<string, any> = any> = Omit<React.FormHTMLAttributes<HTMLFormElement>, 'onSubmit'> & {
423
1055
  form?: Form$1<T>;
424
1056
  /**
425
1057
  * Provide into an isolated context from `createFormContext()` instead of
@@ -431,7 +1063,18 @@ interface FormProps<T extends Record<string, any> = any> extends Omit<React.Form
431
1063
  * the form lands in the module-level FormContext as before.
432
1064
  */
433
1065
  context?: React.Context<Form$1<any> | null>;
434
- initialValues?: T;
1066
+ /**
1067
+ * The values baseline — a sync object, a Promise, or a thunk returning
1068
+ * either ({@link Options.initialValues}). Async sources render the form
1069
+ * empty and gate on `form.isLoading` until they resolve.
1070
+ */
1071
+ initialValues?: T | Promise<T> | (() => T | Promise<T>);
1072
+ /**
1073
+ * Form-level default for a bound field's unmount behavior
1074
+ * ({@link Options.shouldUnregister}): `true` (the default) tombstones
1075
+ * unmounted fields, `false` keeps their values.
1076
+ */
1077
+ shouldUnregister?: boolean;
435
1078
  /**
436
1079
  * Controlled external values. When the `values` reference changes, the
437
1080
  * new object is synced into the form (via setInitialValues semantics):
@@ -446,6 +1089,16 @@ interface FormProps<T extends Record<string, any> = any> extends Omit<React.Form
446
1089
  onSubmit?: (values: T, e: React.FormEvent) => void | Promise<void>;
447
1090
  /** May be async, same as onSubmit. */
448
1091
  onValidSubmit?: (values: T, e: React.FormEvent) => void | Promise<void>;
1092
+ /**
1093
+ * React 19 Server Action target: after validation passes (and after
1094
+ * onSubmit/onValidSubmit), the validated, schema-coerced values are
1095
+ * converted to FormData ({@link formDataFromValues} — files, arrays and
1096
+ * nested objects included) and dispatched to this callback, e.g.
1097
+ * `action={createUser}` for a server action or
1098
+ * `action={formData => startTransition(() => dispatch(formData))}` in a
1099
+ * useActionState bridge. `isSubmitting` covers the whole flight.
1100
+ */
1101
+ action?: (formData: FormData) => void | Promise<void>;
449
1102
  /**
450
1103
  * Called when validation fails.
451
1104
  * @param errors array of {path, type, message} entries in insertion
@@ -466,8 +1119,31 @@ interface FormProps<T extends Record<string, any> = any> extends Omit<React.Form
466
1119
  * false to disable.
467
1120
  */
468
1121
  shouldFocusError?: boolean;
469
- }
470
- declare function Form<T extends Record<string, any> = any>({ form: f1, context, initialValues, values, onSubmit, onValidSubmit, onInvalidSubmit, shouldFocusError, ...props }: FormProps<T>): React.JSX.Element;
1122
+ };
1123
+ declare function Form<T extends Record<string, any> = any>({ form: f1, context, initialValues, values, shouldUnregister, onSubmit, onValidSubmit, onInvalidSubmit, action, shouldFocusError, ...props }: FormProps<T>): React.JSX.Element;
1124
+
1125
+ /**
1126
+ * Props for {@link FormField}: every {@link UseFieldOptions} option plus a
1127
+ * render-prop `children` receiving the bound field result.
1128
+ */
1129
+ type FormFieldProps<TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | PathSegments = FieldPath<TValues> | PathSegments> = UseFieldOptions$1<TValues, TPath> & {
1130
+ children: (field: UseFieldResult<TValues, TPath>) => React.ReactNode;
1131
+ };
1132
+ /**
1133
+ * Headless field bound through a render prop — the non-hook counterpart of
1134
+ * {@link useField} for class components, callback-style consumers and
1135
+ * library bridges where a hook cannot be called (Formik `<Field>` /
1136
+ * TanStack `form.Field` shape).
1137
+ *
1138
+ * The child function receives the full {@link UseFieldResult} — value,
1139
+ * error(s), onChange/onBlur, `disabled`, `focusRef`, the bound `form` —
1140
+ * and renders whatever UI it wants; nothing is rendered when it returns
1141
+ * null/undefined, so it can also gate conditional layout. Resolves its
1142
+ * form from the module-level `FormContext` or the explicit `form` option
1143
+ * (per-instance contexts from `createFormContext()` keep using their own
1144
+ * `useField` hook).
1145
+ */
1146
+ declare function FormField<TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | PathSegments = FieldPath<TValues> | PathSegments>({ children, ...options }: FormFieldProps<TValues, TPath>): React.ReactNode;
471
1147
 
472
1148
  /**
473
1149
  * Props shared by Field/Checkbox/Select. Generic so a typed form flows into
@@ -477,7 +1153,7 @@ declare function Form<T extends Record<string, any> = any>({ form: f1, context,
477
1153
  * bare `<Field name="x" />` (context-resolved, untyped) call sites exactly
478
1154
  * as permissive as before.
479
1155
  */
480
- interface UseFieldOptions<TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | Name = Name> {
1156
+ type UseFieldOptions<TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | PathSegments = FieldPath<TValues> | PathSegments> = {
481
1157
  form?: Form$1<TValues>;
482
1158
  name?: TPath;
483
1159
  initialValue?: any;
@@ -532,9 +1208,18 @@ interface UseFieldOptions<TValues extends Record<string, any> = any, TPath exten
532
1208
  * the DOM element.
533
1209
  */
534
1210
  mode?: ValidationMode;
1211
+ /**
1212
+ * Uncontrolled mode: render the element with `defaultValue` instead of
1213
+ * `value` — typing re-renders nothing (the store still carries every
1214
+ * write; errors/touched/disabled still re-render the field). The
1215
+ * snapshot is pinned at mount; reset does not clear the DOM element
1216
+ * (read live values with useValue/getValues). Passed through to
1217
+ * useField, never spread onto the DOM element.
1218
+ */
1219
+ uncontrolled?: boolean;
535
1220
  [key: string]: any;
536
- }
537
- interface FieldProps<TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | Name = Name> extends UseFieldOptions<TValues, TPath> {
1221
+ };
1222
+ type FieldProps<TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | PathSegments = FieldPath<TValues> | PathSegments> = UseFieldOptions<TValues, TPath> & {
538
1223
  as?: React.ComponentType<any>;
539
1224
  asProps?: Record<string, any>;
540
1225
  eventToValue?: (e: any) => any;
@@ -549,7 +1234,7 @@ interface FieldProps<TValues extends Record<string, any> = any, TPath extends Fi
549
1234
  * wiring for free.
550
1235
  */
551
1236
  renderError?: (error: string, id: string) => React.ReactNode;
552
- }
1237
+ };
553
1238
  /**
554
1239
  * The error-message element id a field's `aria-describedby` points at —
555
1240
  * `fieldErrorId('a[0].b')` is `'a-0-b'`, the same id `Field`'s built-in
@@ -568,32 +1253,31 @@ declare function fieldErrorId(name: Name): string;
568
1253
  * same types, and the inline form trips no-use-before-define on the type
569
1254
  * parameters.
570
1255
  */
571
- interface FieldComponent {
572
- <TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | Name = Name>(props: FieldProps<TValues, TPath> & React.RefAttributes<HTMLInputElement>): React.ReactElement | null;
573
- }
1256
+ type FieldComponent = {
1257
+ <TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | PathSegments = FieldPath<TValues> | PathSegments>(props: FieldProps<TValues, TPath> & React.RefAttributes<HTMLInputElement>): React.ReactElement | null;
1258
+ };
574
1259
  declare const Field: FieldComponent;
575
- interface CheckboxProps<TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | Name = Name> extends UseFieldOptions<TValues, TPath> {
576
- }
1260
+ type CheckboxProps<TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | PathSegments = FieldPath<TValues> | PathSegments> = UseFieldOptions<TValues, TPath>;
577
1261
  /**
578
1262
  * Callable shape of {@link Checkbox}: the same form-typed `validate`
579
1263
  * inference contract as {@link FieldComponent}.
580
1264
  */
581
- interface CheckboxComponent {
582
- <TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | Name = Name>(props: CheckboxProps<TValues, TPath> & React.RefAttributes<HTMLInputElement>): React.ReactElement | null;
583
- }
1265
+ type CheckboxComponent = {
1266
+ <TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | PathSegments = FieldPath<TValues> | PathSegments>(props: CheckboxProps<TValues, TPath> & React.RefAttributes<HTMLInputElement>): React.ReactElement | null;
1267
+ };
584
1268
  declare const Checkbox: CheckboxComponent;
585
- interface SelectProps<TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | Name = Name> extends UseFieldOptions<TValues, TPath> {
1269
+ type SelectProps<TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | PathSegments = FieldPath<TValues> | PathSegments> = UseFieldOptions<TValues, TPath> & {
586
1270
  multiple?: boolean;
587
1271
  children?: React.ReactNode;
588
- }
1272
+ };
589
1273
  /**
590
1274
  * Callable shape of {@link Select}: the same form-typed `validate`
591
1275
  * inference contract as {@link FieldComponent}.
592
1276
  */
593
- interface SelectComponent {
594
- <TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | Name = Name>(props: SelectProps<TValues, TPath> & React.RefAttributes<HTMLSelectElement>): React.ReactElement | null;
595
- }
1277
+ type SelectComponent = {
1278
+ <TValues extends Record<string, any> = any, TPath extends FieldPath<TValues> | PathSegments = FieldPath<TValues> | PathSegments>(props: SelectProps<TValues, TPath> & React.RefAttributes<HTMLSelectElement>): React.ReactElement | null;
1279
+ };
596
1280
  declare const Select: SelectComponent;
597
1281
 
598
- export { Checkbox, CheckboxGroupContext, CheckboxGroupProvider, Field, FieldError, FieldPath, Form, FormContext, Form$1 as FormInstance, FormProvider, Name, Options, Path, PathValueOf, Select, ValidationMode, Validator, createFormContext, fieldErrorId, subscribe, useCanSubmit, useCheckboxGroupContext, useDirtyFields, useError, useErrorByPath, useField, useFieldArray, useFieldArrayItem, useFieldErrors, useFieldErrorsByPath, useForm, useFormContext, useHasErrors, useIsDirty, useIsSubmitting, useSubmitCount, useTouched, useTouchedByPath, useTouchedFields, useValue, useValueByPath, useWatch };
599
- export type { FieldRules, SubscribeEvent, SubscribeOptions, WatchScope };
1282
+ export { Checkbox, CheckboxGroupContext, CheckboxGroupProvider, Field, FieldError, FieldErrorEntry, FieldPath, Form, FormContext, FormEvents, FormField, Form$1 as FormInstance, FormProvider, Name, Options, Path, PathSegments, PathValueOf, Select, ValidationMode, Validator, changeValue, changeValueByPath, createFormContext, emitChangeByPath, fieldErrorId, getDirtyFields, getFieldState, getTouchedFields, getValue, getValueByPath, getValues, handleSubmit, hasTouched, hasTouchedByPath, incrementSubmitCount, isDirty, isTouched, registerFieldMode, removeField, removeFieldByPath, reset, resetField, seedValueByPath, setDisabled, setInitialValues, setIsSubmitting, setSubmitSuccessful, setTouched, setTouchedByPath, setValue, setValueByPath, subscribe, unregisterFieldMode, useCanSubmit, useCheckboxGroupContext, useDirtyFields, useError, useErrorByPath, useField, useFieldArray, useFieldArrayItem, useFieldErrors, useFieldErrorsByPath, useForm, useFormContext, useFormError, useFormErrors, useFormState, useHasErrors, useIsDirty, useIsLoading, useIsSubmitSuccessful, useIsSubmitting, useIsValid, useIsValidating, useSubmitCount, useTouched, useTouchedByPath, useTouchedFields, useValue, useValueByPath, useWatch, useWatchCore, userBlur, userChangeByPath };
1283
+ export type { FieldRules, FieldState, FormState, HandleSubmitOptions, RemoveFieldOptions, ResetFieldOptions, ResetOptions, SetFieldOptions, SubscribeEvent, SubscribeOptions, WatchScope };