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
@@ -1,826 +0,0 @@
1
- import { EventEmitter } from '@for-fun/event-emitter';
2
-
3
- type PathSegments = (string | number)[];
4
- type Name = string | PathSegments;
5
- type Path = {
6
- value: PathSegments;
7
- key: string;
8
- };
9
-
10
- /**
11
- * Compile-time field path utilities: `FieldPath<T>` enumerates the valid
12
- * path strings for a values shape `T` ('a', 'a.b', 'a[0]', 'a[b]', ...),
13
- * and `PathValue<T, P>` resolves the leaf type a path points at.
14
- * The grammar mirrors the paths accepted at runtime by `normalizePath`:
15
- * numeric segments are bracket-only ('a[0]', never 'a.0' — dotted
16
- * numerics throw a TypeError at runtime).
17
- */
18
- /** `true` only for the `any` type (`0 extends 1 & any`). */
19
- type IsAny<T> = 0 extends 1 & T ? true : false;
20
- type Primitive = null | undefined | string | number | boolean | symbol | bigint;
21
- /** Depth countdown: Prev[9] = 8 ... Prev[1] = 0, Prev[0] = never stops recursion. */
22
- type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
23
- /** Paths are capped at 10 segments to keep instantiation depth bounded. */
24
- type MaxDepth = 9;
25
- /**
26
- * Valid path continuations after a segment: `.k` / `[k]` / `[0]`,
27
- * optionally followed by deeper continuations into the child node.
28
- * Numeric segments are bracket-only (`.0` throws at runtime); object
29
- * keys that are themselves numeric strings likewise enumerate just the
30
- * bracket subscript.
31
- */
32
- type Continue<T, D extends number> = [D] extends [never] ? never : IsAny<T> extends true ? string : T extends Primitive | Function ? never : T extends readonly (infer U)[] ? `[${number}]` | `[${number}]${Continue<U, Prev[D]>}` : {
33
- [K in Extract<keyof T, string>]: (K extends `${number}` ? never : `.${K}`) | `[${K}]` | (K extends `${number}` ? never : `.${K}${Continue<T[K], Prev[D]>}`) | `[${K}]${Continue<T[K], Prev[D]>}`;
34
- }[Extract<keyof T, string>];
35
- /**
36
- * Every valid field path string for a values shape `T`.
37
- * @example FieldPath<{a: {b: string}}> // 'a' | 'a.b' | 'a[b]'
38
- */
39
- type FieldPath<T> = IsAny<T> extends true ? string : T extends Primitive | Function ? never : T extends readonly (infer U)[] ? `[${number}]` | `[${number}]${Continue<U, MaxDepth>}` : {
40
- [K in Extract<keyof T, string>]: K extends `${number}` ? never : K | `${K}${Continue<T[K], MaxDepth>}`;
41
- }[Extract<keyof T, string>];
42
- /** Resolve `T[K]` for one bare segment: array index -> element, object key -> value. */
43
- type Lookup<T, K extends string> = K extends `${number}` ? T extends readonly (infer U)[] ? U : never : K extends keyof T ? T[K] : never;
44
- /** One dot-separated chunk: a bare segment plus any `[k]` / `[0]` suffixes. */
45
- type ChunkValue<T, C extends string> = C extends `${infer Key}[${infer Tail}` ? ChunkSuffix<Lookup<T, Key>, `[${Tail}`> : Lookup<T, C>;
46
- type ChunkSuffix<T, S extends string> = S extends `[${infer Key}]${infer Rest}` ? Rest extends '' ? Lookup<T, Key> : PathOf<Lookup<T, Key>, Rest> : never;
47
- /** Resolve the value type the path string `P` points at inside `T`. */
48
- type PathOf<T, P extends string> = P extends '' ? never : P extends `[${infer Key}]${infer Rest}` ? Rest extends '' ? Lookup<T, Key> : PathOf<Lookup<T, Key>, Rest> : P extends `.${infer Rest}` ? PathOf<T, Rest> : P extends `${infer Chunk}.${infer Rest}` ? PathOf<ChunkValue<T, Chunk>, Rest> : ChunkValue<T, P>;
49
- /**
50
- * The value type at path `P` of a values shape `T`.
51
- * @example PathValue<{a: {b: string}}, 'a.b'> // string
52
- */
53
- type PathValue<T, P extends FieldPath<T>> = PathOf<T, P & string>;
54
- /**
55
- * The value type at path `P` of `T`, or `any` when `P` is not a known
56
- * field path (plain `string` / segment-array calls keep their old behavior).
57
- */
58
- type PathValueOf<T, P> = P extends FieldPath<T> ? PathValue<T, Extract<P, FieldPath<T>>> : any;
59
-
60
- /** A field error: `type` identifies the error kind ('custom' for plain
61
- * string errors), `message` is the display text. */
62
- interface FieldError {
63
- type: string;
64
- message: string;
65
- }
66
- /** A flattened entry from {@link getErrors}. */
67
- type FieldErrorEntry = {
68
- path: string;
69
- type: string;
70
- message: string;
71
- };
72
- /** When a field is validated:
73
- * - `'onSubmit'` (default): only on submit
74
- * - `'onBlur'`: when the field loses focus
75
- * - `'onChange'`: on every change
76
- * - `'onTouched'`: on first blur, then on every change
77
- * - `'all'`: on both change and blur
78
- */
79
- type ValidationMode = 'onSubmit' | 'onBlur' | 'onChange' | 'onTouched' | 'all';
80
- /** When a field is re-validated after it already has an error:
81
- * - `'onChange'` (default): on every change
82
- * - `'onBlur'`: when the field loses focus
83
- * - `'onSubmit'`: only on submit (no live re-validation)
84
- */
85
- type ReValidateMode = 'onChange' | 'onBlur' | 'onSubmit';
86
- /** Brand marking a form-level validate result as a structured
87
- * {@link ValidationOutcome} (parsed values and/or errors) rather than a
88
- * plain nested error record. Symbols cannot collide with user error
89
- * records, so detection is an exact `VALIDATION_OUTCOME in result`. */
90
- declare const VALIDATION_OUTCOME: unique symbol;
91
- /** Structured form-level validate result: `errors` uses the same nested
92
- * shape a plain error record uses, `values` is the schema's parsed output
93
- * (coerce/transform results included). Either side may be omitted. */
94
- type ValidationOutcome<T> = {
95
- [VALIDATION_OUTCOME]: true;
96
- errors?: Record<string, any>;
97
- values?: T;
98
- };
99
- /** What a form-level validate function may return: a plain nested error
100
- * record (flattened into field errors — the long-standing shape), or a
101
- * branded {@link ValidationOutcome} whose `values` become the form's
102
- * parsedValues baseline. */
103
- type ValidateResult<T> = Record<string, any> | ValidationOutcome<T> | Promise<Record<string, any> | ValidationOutcome<T>>;
104
- /** Context passed to a form-level `validate` function's second argument.
105
- * `signal` aborts as soon as the round is superseded — a newer round
106
- * started (which only happens under a positive `validateDebounce`, where
107
- * kicks merge into windows) — so async validators can cancel their
108
- * underlying work instead of racing a stale result home. Stale results
109
- * are dropped independently by the round gate, so validators that ignore
110
- * the signal stay correct too; the same contract field-level validators
111
- * get through their own `meta`. */
112
- type FormValidateMeta<T extends Record<string, any> = any> = {
113
- form: Form<T>;
114
- signal: AbortSignal;
115
- };
116
- /** Form-level validator: receives all values (plus {@link
117
- * FormValidateMeta} as an optional second argument) and returns a
118
- * {@link ValidateResult} — sync or async — or `undefined`/nothing when
119
- * valid (the runtime skips falsy results, so implicit-return callbacks
120
- * type-check). */
121
- type FormValidateFn<T extends Record<string, any> = any> = (values: T, meta: FormValidateMeta<T>) => ValidateResult<T> | undefined;
122
- interface Form<T extends Record<string, any> = any> {
123
- emitter: EventEmitter;
124
- mode: ValidationMode;
125
- reValidateMode: ReValidateMode;
126
- initialValues: T;
127
- values: Map<string, any>;
128
- /** Tombstones of unregistered field paths (JSON path keys): reading or
129
- * merging values must not fall back to initialValues for these paths. */
130
- deleted: Set<string>;
131
- /** Every error registered for a field, as a non-empty array (the
132
- * write-side {@link setErrorByPath} normalizes to this invariant, so
133
- * readers never need to guard against an empty list). Readers wanting
134
- * the display error take the first entry ({@link getError}); readers
135
- * wanting all of them use {@link getFieldErrors}. */
136
- errors: Map<string, FieldError[]>;
137
- touched: Set<string>;
138
- validators: Map<string, () => void>;
139
- /** Change handlers published by mounted fields (`useField`): each is the
140
- * field's own onChange — the write plus its mode/reValidateMode-gated
141
- * validation kick. Path-based writes with user-change semantics
142
- * ({@link changeValueByPath}) route through the registered handler; the
143
- * effective per-field mode and live-error view live inside the field's
144
- * closure, so this map is the only channel that can reproduce them.
145
- * Multiple fields mounted at the same path compete for the slot
146
- * last-wins — the latest mount's handler answers every user-change
147
- * write; the unmount guard in `useField` keeps a later owner's
148
- * registration intact when the earlier field goes away. */
149
- changeHandlers: Map<string, (value: any) => void>;
150
- validating: Set<string>;
151
- /** Parsed values from the last successful schema validation: the
152
- * schema's complete output tree (coerced/transformed values included).
153
- * Sits between initialValues and the values Map in {@link getValues}
154
- * until `reset`/`setInitialValues` clears it. Never affects dirty
155
- * state — that compares live edits against initialValues only. */
156
- parsedValues: T | undefined;
157
- /** Form-level validator, seeded from {@link Options.validate}. May
158
- * receive a second {@link FormValidateMeta} argument. */
159
- validate?: FormValidateFn<T>;
160
- /** Delay in milliseconds before the form-level `validate` runs; seeded
161
- * from {@link Options.validateDebounce} and fixed at create time. */
162
- validateDebounce?: number;
163
- /** Path keys (JSON-stringified segments) of the fields whose user
164
- * changes re-run the form-level `validate`; normalized from {@link
165
- * Options.validateDeps} at create time and fixed thereafter. */
166
- validateDeps?: ReadonlySet<string>;
167
- isSubmitting: boolean;
168
- submitCount: number;
169
- isSubmitSuccessful: boolean | undefined;
170
- /** Form-level disabled flag, OR-ed into every bound field's `disabled`
171
- * (form flag || the field's own option). Seeded from
172
- * {@link Options}.disabled at create time and toggled at runtime with
173
- * {@link setDisabled}, which emits a payload-less 'disabled' event so
174
- * subscribed fields re-render. */
175
- disabled: boolean;
176
- }
177
- type Options<T extends Record<string, any> = any> = {
178
- initialValues?: T;
179
- /** When fields are validated. Defaults to `'onSubmit'`. See
180
- * {@link ValidationMode}. */
181
- mode?: ValidationMode;
182
- /** When a field is re-validated after it already has an error — it only
183
- * takes effect once the field has an error. Defaults to `'onChange'`. See
184
- * {@link ReValidateMode}. */
185
- reValidateMode?: ReValidateMode;
186
- /**
187
- * Form-level validator. Returns a record of errors keyed by field path;
188
- * nested objects are flattened ('a.b' style) and array values contribute
189
- * every non-empty string they hold as separate errors (zod `flatten()`
190
- * formErrors style). Schema adapters instead return a branded
191
- * {@link ValidationOutcome}: `errors` flattens the same way, `values`
192
- * (the schema's parsed output) becomes the form's parsedValues baseline
193
- * that {@link getValues} layers over initialValues.
194
- */
195
- validate?: FormValidateFn<T>;
196
- /**
197
- * Milliseconds to debounce the form-level `validate`: kicks from
198
- * `trigger`/`ensureValidate`/submit inside the window merge into one
199
- * run, and while the timer is pending the form counts as validating,
200
- * so `trigger` and submit wait the window out — the same contract the
201
- * per-field `validateDebounce` gives field validators. The merged run
202
- * reads the values current when its timer fires. Defaults to `0`
203
- * (validate runs immediately, exactly as before this option existed).
204
- */
205
- validateDebounce?: number;
206
- /** Fields whose user changes re-run the form-level `validate` — the
207
- * cross-field dependency list (password-confirm mismatch and friends).
208
- * Each entry is a field path ('password', 'user.email', 'items.0.qty');
209
- * a user change to a listed field re-runs the form-level `validate`
210
- * under the same mode/`reValidateMode` gating the field's own
211
- * validator gets. Omit it and the form-level `validate` only runs on
212
- * `trigger`/submit, exactly as before this option existed.
213
- *
214
- * Opting in also changes what a re-run may clear: each round first
215
- * drops the errors the previous round wrote (paths it flattened onto),
216
- * so a dep change that fixes the cross-field error makes it disappear.
217
- * Errors the round never wrote — field validators', `setServerErrors`,
218
- * manual `setError` — are never touched. TanStack Form's counterpart is
219
- * `onChangeListenTo` (v1) / validator `triggers` (v2 alpha). */
220
- validateDeps?: FieldPath<T>[];
221
- /** Start the form with every bound field disabled — the flag bound
222
- * fields OR with their own `disabled` option (a field cannot opt out
223
- * of a disabled form). Toggle later with {@link setDisabled}.
224
- * Defaults to `false`. */
225
- disabled?: boolean;
226
- };
227
- /**
228
- * Create form instance
229
- * @param options
230
- * @return form instance
231
- */
232
- declare function create<T extends Record<string, any> = any>(options?: Options<T>): Form<T>;
233
- /**
234
- * Get form values: the values Map layered over parsedValues (when a schema
235
- * validation produced them) layered over initialValues.
236
- *
237
- * Merged with copy-on-write ownership tracking ({@link setOwned}): every
238
- * distinct container on a written path is allocated once and shared by all
239
- * paths through it, instead of re-copying the whole branch for every key.
240
- * One owned set spans the whole merge, so containers borrowed from the
241
- * parsedValues tree are copied before mutation exactly like initialValues
242
- * ones. The result is a freshly merged tree per mutation, with untouched
243
- * branches sharing references with the baseline exactly like chained
244
- * `set` did.
245
- *
246
- * Memoized per form like {@link getDirtyFields}: every value write bumps a
247
- * `version` counter ({@link bumpValuesVersion}) while reads reset it, so
248
- * consecutive reads hand back the same reference (submit, changeValue and
249
- * form-level validate all read the whole tree, often several times per
250
- * interaction). Treat the result as read-only — the next read after a
251
- * write returns a fresh tree, but between writes the cached one is shared
252
- * with every other reader.
253
- *
254
- * parsedValues is the schema's complete output tree: once validation
255
- * succeeds it replaces the initialValues baseline (fields the schema
256
- * dropped disappear), while live edits in the values Map still win over
257
- * both. It never affects dirty state — {@link isDirty} and
258
- * {@link getDirtyFields} compare live edits against initialValues only,
259
- * because parsing is not a user edit.
260
- *
261
- * @param form
262
- */
263
- declare function getValues<T extends Record<string, any> = any>(form: Form<T>): T;
264
- /**
265
- * Get field value
266
- * @param form
267
- * @param name
268
- */
269
- declare function getValue<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form<T>, name: P): PathValueOf<T, P>;
270
- /**
271
- * Get field value by path
272
- * @param form
273
- * @param path
274
- */
275
- declare function getValueByPath({ initialValues, parsedValues, values, deleted }: Form, path: Path): any;
276
- /** Options accepted by {@link setValue} / {@link setValueByPath} / {@link
277
- * changeValue} / {@link changeValueByPath}. `shouldValidate`/`shouldTouch`
278
- * default to `false`; omitting the options object entirely keeps the plain
279
- * set-value behavior (no validation, no touched marking, dirty stays
280
- * derived). */
281
- interface SetFieldOptions {
282
- /** Run the field's registered validator (if any) after the value lands,
283
- * same as triggering that single field. Defaults to `false`. */
284
- shouldValidate?: boolean;
285
- /** Mark the field as touched. Defaults to `false`. */
286
- shouldTouch?: boolean;
287
- /** Land the value as a commit instead of an edit: the value becomes the
288
- * field's dirty-comparison baseline, so `getDirtyFields`/`isDirty`/
289
- * `getFieldState().isDirty` read the field as clean, and a later write
290
- * dirties it only by differing from the new baseline. `true` (or
291
- * omitting the flag) keeps the default derived behavior — dirty while
292
- * the live value differs from initialValues. */
293
- shouldDirty?: boolean;
294
- }
295
- /**
296
- * Set field value
297
- * @param form
298
- * @param name
299
- * @param value
300
- * @param options
301
- */
302
- declare function setValue<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form<T>, name: P, value: PathValueOf<T, P>, options?: SetFieldOptions): void;
303
- /**
304
- * Set field value
305
- * @param form
306
- * @param path
307
- * @param value
308
- * @param options
309
- */
310
- declare function setValueByPath(form: Form, path: Path, value: any, options?: SetFieldOptions): void;
311
- /**
312
- * The write of {@link setValueByPath} minus the `'change'` emit: the
313
- * render-time {@link useField} `initialValue` seed. The field's first
314
- * paint (SSR included — effects never run on the server) must already
315
- * carry the value, so the write happens during render where emitting is
316
- * illegal; the seeding field announces it from its post-commit effect
317
- * through {@link emitChangeByPath} instead.
318
- *
319
- * Everything else matches a plain write: descendant keys of the seeded
320
- * path are pruned, the branch's tombstones and committed baselines are
321
- * revived/dropped, and both memo caches are invalidated. Like the effect
322
- * seed it replaces, the caller guards it to paths with no value yet.
323
- */
324
- declare function seedValueByPath(form: Form, path: Path, value: any): void;
325
- /** Announce a {@link seedValueByPath} that happened during render: the
326
- * payload-carrying `'change'` emit {@link setValueByPath} would have
327
- * fired, split out so it can run post-commit where emitting is safe.
328
- * Subscribers that rendered after the seed re-read an unchanged snapshot
329
- * and bail; subscribers from earlier commits resync. */
330
- declare function emitChangeByPath({ emitter }: Form, path: Path): void;
331
- /**
332
- * Set a field value as a user change.
333
- *
334
- * The write routes through the field's own onChange when one is mounted
335
- * (registered by `useField`), so it fires exactly the validation a user
336
- * typing into the field would fire: the field's effective `mode`
337
- * (per-field override included) and the form's `reValidateMode`. With no
338
- * mounted field on the path it degrades to a plain value set
339
- * ({@link setValue}).
340
- *
341
- * This is the channel for component-library bridges that hand a control a
342
- * plain setter bound to a field — they cannot rebuild the gating from
343
- * public form state, because the per-field mode override and the
344
- * live-error view that gates `reValidateMode` live inside the field's
345
- * onChange closure.
346
- *
347
- * Contrast {@link setValue}: that is the imperative channel — its
348
- * `shouldValidate` option kicks the field's validator unconditionally,
349
- * ignoring any mode. Functional updaters are the caller's to evaluate
350
- * ({@link getValue}).
351
- *
352
- * `options` carries the same {@link SetFieldOptions}: on the fallback path
353
- * (no mounted field) they forward to {@link setValueByPath} wholesale,
354
- * while on the mounted-field path only `shouldDirty: false` applies — the
355
- * write lands as a commit while the field's own mode gating keeps driving
356
- * validation, which is the point of this channel.
357
- *
358
- * @param form
359
- * @param name
360
- * @param value
361
- * @param options
362
- */
363
- declare function changeValue<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form<T>, name: P, value: PathValueOf<T, P>, options?: SetFieldOptions): void;
364
- /**
365
- * Set a field value as a user change, by parsed path
366
- * @param form
367
- * @param path
368
- * @param value
369
- * @param options
370
- */
371
- declare function changeValueByPath(form: Form, path: Path, value: any, options?: SetFieldOptions): void;
372
- /**
373
- * Get field error
374
- * @param form
375
- * @param name
376
- * @return FieldError object or undefined
377
- */
378
- declare function getError<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form<T>, name: P): FieldError | undefined;
379
- /**
380
- * Get field error by path
381
- * @param form
382
- * @param path
383
- * @return first FieldError of the field, or undefined
384
- */
385
- declare function getErrorByPath({ errors }: Form, path: Path): FieldError | undefined;
386
- /**
387
- * Get all errors of a field
388
- * @param form
389
- * @param name
390
- * @return every error registered for the field (insertion order); an empty
391
- * array when the field has none
392
- */
393
- declare function getFieldErrors<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form<T>, name: P): FieldError[];
394
- /**
395
- * Get all errors of a field by path
396
- * @param form
397
- * @param path
398
- * @return every error registered for the field (insertion order); an empty
399
- * array when the field has none
400
- */
401
- declare function getFieldErrorsByPath({ errors }: Form, path: Path): FieldError[];
402
- /**
403
- * Get all errors
404
- * @param form
405
- * @return array of {path, type, message} entries, in insertion order; path
406
- * is the user-facing dotted field path ('a.b', 'list.0'), and a
407
- * field holding several errors contributes one entry per error
408
- */
409
- declare function getErrors({ errors }: Form): FieldErrorEntry[];
410
- /**
411
- * Get first error message
412
- * @param form
413
- * @return first error's message string, or undefined when there are no errors
414
- */
415
- declare function getFirstError({ errors }: Form): string | undefined;
416
- /** Snapshot of one field's aggregated state, as {@link getFieldState}
417
- * returns it. `errors` is the stored array shared with the form — treat it
418
- * as read-only, like every {@link getFieldErrors} result. */
419
- interface FieldState<T = any> {
420
- value: T;
421
- error: FieldError | undefined;
422
- errors: FieldError[];
423
- isDirty: boolean;
424
- isTouched: boolean;
425
- isValidating: boolean;
426
- }
427
- /**
428
- * Get one field's aggregated state: the layered value ({@link getValue}),
429
- * the first error ({@link getError}) and every error ({@link
430
- * getFieldErrors}), dirtiness, the touched flag, and whether a validator
431
- * is in flight. `isDirty` applies the same per-field rule as {@link
432
- * getDirtyFields}: a live value exists and differs from initialValues at
433
- * that path (parsedValues never counts — parsing is not an edit).
434
- *
435
- * @param form
436
- * @param name
437
- */
438
- declare function getFieldState<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form<T>, name: P): FieldState<PathValueOf<T, P>>;
439
- declare function unsetValidatingByPath({ emitter, validating }: Form, path: Path): void;
440
- declare function setValidatingByPath({ emitter, validating }: Form, path: Path): void;
441
- /**
442
- * Set field error
443
- * @param form
444
- * @param name
445
- * @param error string is normalized to {type: 'custom', message}; a
446
- * FieldError object is stored as-is; an array holds several errors
447
- * (falsy items dropped, strings normalized); undefined clears
448
- */
449
- declare function setError<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form<T>, name: P, error: string | FieldError | (string | FieldError)[] | undefined): void;
450
- /**
451
- * Set field error
452
- * @param form
453
- * @param path
454
- * @param error string is normalized to {type: 'custom', message}; a
455
- * FieldError object is stored as-is; an array holds several errors
456
- * (falsy items dropped, strings normalized); undefined clears
457
- */
458
- declare function setErrorByPath({ emitter, errors }: Form, path: Path, error: string | FieldError | (string | FieldError)[] | undefined): void;
459
- /**
460
- * Clear errors
461
- * @param form
462
- * @param name a single path or a list of paths; omit to clear every error
463
- */
464
- declare function clearErrors(form: Form, name?: Name | Name[]): void;
465
- /** Options accepted by {@link setServerErrors}. */
466
- interface SetServerErrorsOptions {
467
- /** Keep existing field errors instead of clearing them first. Defaults
468
- * to `false`: a fresh server response replaces the prior error state. */
469
- keepExisting?: boolean;
470
- }
471
- /**
472
- * Land a server-side error response on the form: each entry becomes the
473
- * named field's error(s) with `type: 'server'`, ready for the same
474
- * renderError/`useError` channel client-side validation uses. Takes the
475
- * flat `Record<string, string | string[]>` shape REST APIs commonly
476
- * return (RealWorld: `422 {errors: {email: ['has already been taken']}}`)
477
- * without a hand-rolled `Object.entries` + `setError` loop.
478
- *
479
- * A string value lands as one error, a string array as several (first one
480
- * is what `getError`/`error` expose); an empty array clears that field's
481
- * errors. By default every existing error is cleared first — a fresh
482
- * response describes the current state, not a patch onto stale client
483
- * errors; pass `keepExisting: true` to layer instead.
484
- * @param form
485
- * @param errors field errors keyed by name
486
- * @param options
487
- */
488
- declare function setServerErrors(form: Form, errors: Record<string, string | string[]>, options?: SetServerErrorsOptions): void;
489
- /**
490
- * Set field touched state
491
- * @param form
492
- * @param name
493
- */
494
- declare function setTouched(form: Form, name: Name): void;
495
- /**
496
- * Set field touched state
497
- * @param form
498
- * @param path
499
- */
500
- declare function setTouchedByPath({ emitter, touched }: Form, path: Path): void;
501
- /**
502
- * Check if field has been touched
503
- * @param form
504
- * @param name
505
- */
506
- declare function hasTouched<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form<T>, name: P): boolean;
507
- /**
508
- * Check if field has been touched
509
- * @param form
510
- * @param path
511
- */
512
- declare function hasTouchedByPath({ touched }: Form, path: Path): boolean;
513
- /**
514
- * Is dirty -- any value differs from initialValues
515
- * @param form
516
- */
517
- declare function isDirty(form: Form): boolean;
518
- /**
519
- * Get dirty fields -- fields whose current value differs from initialValues.
520
- * Keys are user-facing dotted paths ('a.b', 'a.0.c'), unlike the JSON array
521
- * keys stored in the values Map.
522
- * @param form
523
- * @return object mapping each dirty field's dotted path to true; the same
524
- * reference is returned until the dirty set actually changes
525
- */
526
- declare function getDirtyFields(form: Form): Record<string, boolean>;
527
- /**
528
- * Get touched fields as user-facing dotted paths ('a.b', 'a.0.c'), unlike
529
- * the JSON array keys stored in the touched Set.
530
- * @param form
531
- * @return array of touched fields' dotted paths
532
- */
533
- declare function getTouchedFields({ touched }: Form): string[];
534
- /**
535
- * Is touched -- any field has been touched
536
- * @param form
537
- */
538
- declare function isTouched({ touched }: Form): boolean;
539
- /**
540
- * Remove field
541
- * @param form
542
- * @param name
543
- */
544
- declare function removeField(form: Form, name: Name): void;
545
- /**
546
- * Remove field
547
- * @param form
548
- * @param path
549
- */
550
- declare function removeFieldByPath(form: Form, path: Path): void;
551
- /**
552
- * Set form initialValues
553
- *
554
- * Content-based early return: a new reference with equal content (the
555
- * re-rendered inline literal) is a no-op, so committed edits survive, while
556
- * genuinely changed content swaps the baseline and re-seeds — live values
557
- * and tombstones are cleared, touched flags and errors survive.
558
- * @param form
559
- * @param initialValues
560
- */
561
- declare function setInitialValues(form: Form, initialValues: any): void;
562
- /** Options accepted by {@link reset}. Every flag defaults to `false` —
563
- * omitting the object (or any flag) keeps the plain full-reset behavior.
564
- * Names mirror react-hook-form's reset options to ease migration. */
565
- interface ResetOptions {
566
- /** Keep the current values of fields that are dirty — differ from the
567
- * pre-reset initialValues (the same rule {@link getDirtyFields} applies).
568
- * Clean fields fall back to the new initialValues as usual. */
569
- keepDirtyValues?: boolean;
570
- /** Keep the touched set instead of clearing it. */
571
- keepTouched?: boolean;
572
- /** Keep field errors instead of clearing them. */
573
- keepErrors?: boolean;
574
- /** Keep the submitted flag (`isSubmitSuccessful`) instead of clearing
575
- * it. */
576
- keepIsSubmitted?: boolean;
577
- /** Keep `submitCount` instead of resetting it to 0. */
578
- keepSubmitCount?: boolean;
579
- /** Keep `isSubmitting` instead of resetting it to false. */
580
- keepIsSubmitting?: boolean;
581
- }
582
- /**
583
- * Reset form
584
- * @param form
585
- * @param initialValues
586
- * @param options keep-flags to preserve slices of state through the reset
587
- */
588
- declare function reset(form: Form, initialValues?: any, options?: ResetOptions): void;
589
- /** Options accepted by {@link resetField}. The flags default to `false`;
590
- * `value` has no default — omitted, the field falls back to initialValues;
591
- * provided, the explicit value becomes the live value with no fallback at
592
- * all. Mirrors react-hook-form's resetField options (`value` plays their
593
- * `defaultValue`'s role) to ease migration. */
594
- interface ResetFieldOptions {
595
- /** Keep the field's touched flag instead of clearing it. */
596
- keepTouched?: boolean;
597
- /** Keep the field's errors instead of clearing them. */
598
- keepErrors?: boolean;
599
- /** Explicit post-reset value for the field — never falls back to
600
- * initialValues. */
601
- value?: any;
602
- }
603
- /**
604
- * Reset a single field: drop its live value (reads fall back to the
605
- * baseline — initialValues, or the schema's parsed output when one
606
- * exists, in which case the path is removed from parsedValues and the
607
- * initial value pinned back so the field reads initialValues again),
608
- * clear its touched flag and errors, and revive the path's removal
609
- * tombstones — the inverse of {@link removeFieldByPath}. Other fields
610
- * and the submission flags are untouched; see {@link reset} for the
611
- * form-wide counterpart.
612
- *
613
- * @param form
614
- * @param name
615
- * @param options
616
- */
617
- declare function resetField<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form<T>, name: P, options?: ResetFieldOptions): void;
618
- /**
619
- * @param form
620
- */
621
- declare function hasErrors({ errors }: Form): boolean;
622
- /**
623
- * Trigger field validation.
624
- *
625
- * Without `name` every registered field validator runs. A single `name` —
626
- * dotted string or segments array — runs only that field's validator, and
627
- * an array of names runs each one in order. An empty array is a no-op, as
628
- * is any name with no registered validator. An array argument counts as
629
- * one segments path only when it mixes in numbers (`['items', 0]`); pure
630
- * string arrays are name lists, so `['a', 'b']` triggers fields `a` and
631
- * `b`, not the nested path `a.b`.
632
- *
633
- * The returned promise waits for the triggered validation to settle —
634
- * async validators included — so their errors have already landed in
635
- * `form.errors` when it resolves. It never rejects: landing errors is the
636
- * expected outcome here, not a failure. Resolves `true` when the triggered
637
- * scope is error-free, `false` otherwise. Without `name` the scope is all
638
- * fields plus the form-level `validate` result (which runs after field
639
- * validators settle, same pipeline as {@link ensureValidate}); with `name`
640
- * only those fields' own errors count and form-level `validate` is
641
- * skipped (RHF semantics).
642
- *
643
- * Fire-and-forget callers may ignore the promise: the validator kicks
644
- * still happen synchronously, matching the pre-promise behavior.
645
- *
646
- * @param form
647
- * @param name field name(s) to trigger, or all fields when omitted
648
- * @return whether the triggered scope is error-free once validation settles
649
- */
650
- declare function trigger(form: Form, name?: Name | Name[]): Promise<boolean>;
651
- /**
652
- * Form-level twin of the gated validator kick in `useField`'s onChange:
653
- * re-run the form-level `validate` after a user change to a field listed
654
- * in `validateDeps`. Called from the field's own change pipeline (typing
655
- * and `changeValue` alike — both route through the mounted field's
656
- * onChange), so programmatic `setValue` writes do not re-run it, exactly
657
- * like they do not re-run field validators.
658
- *
659
- * The gate mirrors the per-field matrix with the *changed field's*
660
- * effective `mode` (a per-field override governs when its changes may
661
- * fire validation) and the form-level `reValidateMode` against the last
662
- * round's error footprint ({@link hasFormValidateErrors} — field
663
- * validators' errors never arm this kick):
664
- * - `mode` `'onChange'`/`'all'` — every dep change re-runs;
665
- * - `mode` `'onTouched'` — dep changes re-run once the field was touched;
666
- * - otherwise the re-run waits for `reValidateMode: 'onChange'` (the
667
- * default) while the last round's error is still live — the
668
- * submit-then-fix flow: the mismatch lands on submit, editing the
669
- * dependency re-runs the validate and clears it.
670
- * `reValidateMode: 'onBlur'`/`'onSubmit'` never re-run on a change (a
671
- * change is not a blur; submit re-runs are the submit pipeline's job).
672
- *
673
- * The kick is fire-and-forget: async round rejections are swallowed
674
- * (nothing in an event handler can await them), while a synchronous
675
- * throw inside the validate callback propagates to the caller exactly
676
- * like a field validator's does.
677
- *
678
- * A no-op unless the form set `validateDeps` listing `path` — forms
679
- * without the option pay one property check here.
680
- */
681
- declare function revalidateFormOnChange(form: Form, path: Path, mode: ValidationMode): void;
682
- /** Register one field's validateDeps declaration: `key` re-validates when
683
- * any path in `depKeys` takes a user change. Idempotent per (key, dep)
684
- * pair, so StrictMode's double effect is harmless. */
685
- declare function registerFieldValidateDeps(form: Form, key: string, depKeys: string[]): void;
686
- /** Drop one field's validateDeps registration ({@link
687
- * registerFieldValidateDeps}). Entries nobody lists anymore are removed so
688
- * the registry never outlives its fields. */
689
- declare function unregisterFieldValidateDeps(form: Form, key: string, depKeys: string[]): void;
690
- /**
691
- * Field-level twin of {@link revalidateFormOnChange}: after a user change
692
- * to `path`, re-run every field validator that declared `path` in its
693
- * `validateDeps` (useField option). Same channel, same gate: the kick
694
- * rides the changed field's own onChange pipeline (typing and
695
- * `changeValue` alike), so programmatic `setValue` writes never fire it —
696
- * exactly like field validators and the form-level `validateDeps`.
697
- *
698
- * The gate mirrors the form-level matrix with the *changed field's*
699
- * effective `mode` and the form-level `reValidateMode` against each
700
- * dependent's live error:
701
- * - `mode` `'onChange'`/`'all'` — every dep change re-runs the dependent;
702
- * - `mode` `'onTouched'` — once the changed field was touched;
703
- * - otherwise the re-run waits for `reValidateMode: 'onChange'` (the
704
- * default) while the dependent still shows an error — the
705
- * submit-then-fix flow: the mismatch lands on submit, editing the
706
- * dependency re-validates the dependent and a passing round clears it
707
- * (a field validator owns its whole key, so the re-run's result
708
- * replaces whatever the previous round wrote — the field-level shape
709
- * of the form-level footprint reclaim).
710
- *
711
- * The kick is an ordinary validator kick: the dependent's own
712
- * `validateDebounce` window applies, and a synchronous throw inside its
713
- * validate propagates to the caller like any field validator's would.
714
- *
715
- * A no-op unless some field declared `path` as a dep — forms without any
716
- * field-level `validateDeps` pay one property check here.
717
- */
718
- declare function revalidateDependentsOnChange(form: Form, path: Path, mode: ValidationMode): void;
719
- /** The Error {@link ensureValidate} rejects with: `message` is the first
720
- * error's display text ({@link getFirstError}) — the long-standing shape
721
- * — and `.errors` carries the complete flattened error list ({@link
722
- * getErrors}: `{path, type, message}` entries, dotted display paths) so
723
- * catchers can branch on types and locate fields without re-reading the
724
- * form. */
725
- type FormValidationError = Error & {
726
- errors: FieldErrorEntry[];
727
- };
728
- /**
729
- * Validate and throw if any field error.
730
- * @param form
731
- * @return resolve if no error; reject and stop validate if has an error
732
- */
733
- declare function ensureValidate(form: Form): Promise<void>;
734
- /**
735
- * Validate and return if any field error.
736
- * @param form
737
- * @return error message string or void
738
- */
739
- declare function validate(form: Form): Promise<void | string>;
740
- declare function setIsSubmitting(form: Form, value: boolean): void;
741
- declare function incrementSubmitCount(form: Form): void;
742
- declare function setSubmitSuccessful(form: Form, value: boolean): void;
743
- /**
744
- * Set the form-level disabled flag and emit a payload-less 'disabled'
745
- * event — subscribed fields (useField and the components built on it)
746
- * re-render with the merged disabled state: form flag || their own
747
- * `disabled` option.
748
- * @param form
749
- * @param value
750
- */
751
- declare function setDisabled(form: Form, value: boolean): void;
752
- /** Submit callbacks for {@link handleSubmit}. All optional — a missing
753
- * callback is simply skipped, matching the <Form> component semantics. */
754
- interface HandleSubmitOptions<T extends Record<string, any> = any> {
755
- /** Called after validation passes, before onValidSubmit. */
756
- onSubmit?: (values: T, e?: any) => void | Promise<void>;
757
- /** Called after validation passes, following a successful onSubmit. */
758
- onValidSubmit?: (values: T, e?: any) => void | Promise<void>;
759
- /**
760
- * Called when validation fails.
761
- * @param errors array of {path, type, message} entries in insertion
762
- * order; path is the dotted field path ('a.b', 'list.0'), type is
763
- * the error kind ('custom' for plain string errors, 'native' for
764
- * failed DOM constraint validation), message is the display text
765
- * @param values current form values
766
- */
767
- onInvalidSubmit?: (errors: FieldErrorEntry[], values: T) => void;
768
- /**
769
- * Focus the first error field after a failed submit. Defaults to true —
770
- * only an explicit `false` disables it. When custom validation fails,
771
- * a 'focusError' event carrying the first error's path key is emitted
772
- * on the form (bound fields such as <Field> subscribe and focus their
773
- * input); when native constraint validation fails, the submitted
774
- * form's first ':invalid' control is focused directly.
775
- */
776
- shouldFocusError?: boolean;
777
- }
778
- /**
779
- * Create an async submit handler for `form` — the headless counterpart of
780
- * the <Form> component's onSubmit wiring.
781
- *
782
- * Behavior mirrors <Form> exactly: preventDefault when present, then the
783
- * submit state machine (isSubmitting/submitCount/isSubmitSuccessful) runs
784
- * around native constraint validation (via `e.currentTarget.checkValidity`,
785
- * skipped when the target has no checkValidity — e.g. React Native or
786
- * toolbar-button submits) and custom validators. Failed validation fires
787
- * onInvalidSubmit with the flattened error entries; a passing submit runs
788
- * onSubmit then onValidSubmit. Errors thrown by either are swallowed into
789
- * isSubmitSuccessful=false rather than rejecting the returned promise.
790
- * Failed validation also focuses the offending field (see
791
- * {@link HandleSubmitOptions.shouldFocusError}).
792
- *
793
- * @param form form instance
794
- * @param options submit callbacks
795
- * @return async event handler, callable without an event object
796
- */
797
- declare function handleSubmit<T extends Record<string, any> = any>(form: Form<T>, options?: HandleSubmitOptions<T>): (e?: {
798
- preventDefault?: () => void;
799
- currentTarget?: any;
800
- }) => Promise<void>;
801
- /** Options accepted by {@link setFocus}. All flags default to `false`. */
802
- interface SetFocusOptions {
803
- /** Select the field's text after focusing it. Bound fields call
804
- * `select()` on their element; elements without one (custom `as`
805
- * components) just focus. */
806
- shouldSelect?: boolean;
807
- }
808
- /**
809
- * Programmatically focus a bound field's element (e.g. the <Field>'s
810
- * input).
811
- *
812
- * Rides the same 'focusError' event channel a failed handleSubmit uses to
813
- * focus the first errored field: the payload is the target's path key,
814
- * with the focus options as a second, backward-compatible argument (older
815
- * subscribers declared with a single `key` parameter simply ignore it).
816
- * Being event-driven, it is a silent no-op when the field is unmounted or
817
- * nothing subscribes — unknown names never throw.
818
- *
819
- * @param form form instance
820
- * @param name field name (dot path or segments path)
821
- * @param options focus options
822
- */
823
- declare function setFocus(form: Form, name: Name, options?: SetFocusOptions): void;
824
-
825
- export { resetField as $, getFieldErrors as A, getFieldErrorsByPath as B, getFieldState as C, getFirstError as D, getTouchedFields as E, getValue as G, getValueByPath as I, getValues as J, handleSubmit as K, hasErrors as L, hasTouched as M, hasTouchedByPath as Q, incrementSubmitCount as T, isDirty as U, isTouched as W, registerFieldValidateDeps as X, removeField as Y, removeFieldByPath as Z, reset as _, revalidateDependentsOnChange as a0, revalidateFormOnChange as a1, seedValueByPath as a2, setDisabled as a3, setError as a4, setErrorByPath as a5, setFocus as a6, setInitialValues as a7, setIsSubmitting as a8, setServerErrors as a9, setSubmitSuccessful as aa, setTouched as ab, setTouchedByPath as ac, setValidatingByPath as ad, setValue as ae, setValueByPath as af, trigger as ag, unregisterFieldValidateDeps as ah, unsetValidatingByPath as ai, validate as aj, VALIDATION_OUTCOME as n, changeValue as q, changeValueByPath as r, clearErrors as s, create as t, emitChangeByPath as u, ensureValidate as v, getDirtyFields as w, getError as x, getErrorByPath as y, getErrors as z };
826
- export type { Form as F, HandleSubmitOptions as H, Name as N, Options as O, PathValueOf as P, ReValidateMode as R, SetFieldOptions as S, ValidationMode as V, FieldPath as a, FieldError as b, Path as c, FieldErrorEntry as d, FieldState as e, FormValidateFn as f, FormValidateMeta as g, FormValidationError as h, PathValue as i, ResetFieldOptions as j, ResetOptions as k, SetFocusOptions as l, SetServerErrorsOptions as m, ValidateResult as o, ValidationOutcome as p };