react-f0rm 1.2.0 → 1.4.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 (98) hide show
  1. package/README.md +90 -1062
  2. package/devtools.d.ts +1 -0
  3. package/devtools.js +1 -0
  4. package/dist/array-3hAjl6Bz.d.ts +160 -0
  5. package/dist/devtools/index.cjs.js +1 -1
  6. package/dist/devtools/index.cjs.js.map +1 -1
  7. package/dist/devtools/index.d.cts +34 -0
  8. package/dist/devtools/index.d.mts +34 -0
  9. package/dist/devtools/index.d.ts +3 -2
  10. package/dist/devtools/index.mjs +1 -1
  11. package/dist/devtools/index.mjs.map +1 -1
  12. package/dist/errors-8oPux7GR.cjs.js +2 -0
  13. package/dist/errors-8oPux7GR.cjs.js.map +1 -0
  14. package/dist/errors-ClH3eV1R.mjs +2 -0
  15. package/dist/errors-ClH3eV1R.mjs.map +1 -0
  16. package/dist/index.cjs.js +1 -1
  17. package/dist/index.cjs.js.map +1 -1
  18. package/dist/index.d.cts +1833 -0
  19. package/dist/index.d.mts +1833 -0
  20. package/dist/index.d.ts +678 -128
  21. package/dist/index.mjs +1 -1
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/index.umd.js +1210 -480
  24. package/dist/index.umd.js.map +1 -1
  25. package/dist/index.umd.min.js +2 -2
  26. package/dist/index.umd.min.js.map +1 -1
  27. package/dist/persist.cjs.js +1 -1
  28. package/dist/persist.cjs.js.map +1 -1
  29. package/dist/persist.d.cts +49 -0
  30. package/dist/persist.d.mts +49 -0
  31. package/dist/persist.mjs +1 -1
  32. package/dist/persist.mjs.map +1 -1
  33. package/dist/resolvers/standard-schema.cjs.js +1 -1
  34. package/dist/resolvers/standard-schema.cjs.js.map +1 -1
  35. package/dist/resolvers/standard-schema.d.cts +2 -0
  36. package/dist/resolvers/standard-schema.d.mts +2 -0
  37. package/dist/resolvers/standard-schema.d.ts +1 -67
  38. package/dist/resolvers/standard-schema.mjs +1 -1
  39. package/dist/resolvers/standard-schema.mjs.map +1 -1
  40. package/dist/resolvers/yup.cjs.js +1 -1
  41. package/dist/resolvers/yup.cjs.js.map +1 -1
  42. package/dist/resolvers/yup.d.cts +6 -0
  43. package/dist/resolvers/yup.d.mts +6 -0
  44. package/dist/resolvers/yup.d.ts +0 -1
  45. package/dist/resolvers/yup.mjs +1 -1
  46. package/dist/resolvers/yup.mjs.map +1 -1
  47. package/dist/resolvers/zod.cjs.js +1 -1
  48. package/dist/resolvers/zod.cjs.js.map +1 -1
  49. package/dist/resolvers/zod.d.cts +6 -0
  50. package/dist/resolvers/zod.d.mts +6 -0
  51. package/dist/resolvers/zod.d.ts +0 -1
  52. package/dist/resolvers/zod.mjs +1 -1
  53. package/dist/resolvers/zod.mjs.map +1 -1
  54. package/dist/server/index.cjs.js +1 -1
  55. package/dist/server/index.cjs.js.map +1 -1
  56. package/dist/server/index.d.cts +106 -0
  57. package/dist/server/index.d.mts +106 -0
  58. package/dist/server/index.d.ts +31 -2
  59. package/dist/server/index.mjs +1 -1
  60. package/dist/server/index.mjs.map +1 -1
  61. package/dist/standard-schema-CYbnugL7.d.ts +1184 -0
  62. package/dist/standardSchema-BpZvg0tZ.mjs +2 -0
  63. package/dist/standardSchema-BpZvg0tZ.mjs.map +1 -0
  64. package/dist/standardSchema-CxV_DhBO.cjs.js +2 -0
  65. package/dist/standardSchema-CxV_DhBO.cjs.js.map +1 -0
  66. package/dist/validate-BEjNsNvd.mjs +2 -0
  67. package/dist/validate-BEjNsNvd.mjs.map +1 -0
  68. package/dist/validate-BvN6hiEG.cjs.js +2 -0
  69. package/dist/validate-BvN6hiEG.cjs.js.map +1 -0
  70. package/dist/values-Bod74Kzu.mjs +2 -0
  71. package/dist/values-Bod74Kzu.mjs.map +1 -0
  72. package/dist/values-DKpOCD5J.cjs.js +2 -0
  73. package/dist/values-DKpOCD5J.cjs.js.map +1 -0
  74. package/package.json +90 -34
  75. package/persist.d.ts +1 -0
  76. package/persist.js +1 -0
  77. package/resolvers/standard-schema.d.ts +1 -0
  78. package/resolvers/standard-schema.js +1 -0
  79. package/resolvers/yup.d.ts +1 -0
  80. package/resolvers/yup.js +1 -0
  81. package/resolvers/zod.d.ts +1 -0
  82. package/resolvers/zod.js +1 -0
  83. package/server.d.ts +1 -0
  84. package/server.js +1 -0
  85. package/dist/errors-CxSjrWJO.cjs.js +0 -2
  86. package/dist/errors-CxSjrWJO.cjs.js.map +0 -1
  87. package/dist/errors-CzWtwjO0.mjs +0 -2
  88. package/dist/errors-CzWtwjO0.mjs.map +0 -1
  89. package/dist/form-CvmWHUrd.d.ts +0 -423
  90. package/dist/validate-B1Gdjeaq.mjs +0 -2
  91. package/dist/validate-B1Gdjeaq.mjs.map +0 -1
  92. package/dist/validate-CUmNZqg6.d.ts +0 -238
  93. package/dist/validate-DAfz8Nbb.cjs.js +0 -2
  94. package/dist/validate-DAfz8Nbb.cjs.js.map +0 -1
  95. package/dist/values-B1IV-6V4.mjs +0 -2
  96. package/dist/values-B1IV-6V4.mjs.map +0 -1
  97. package/dist/values-CDNAYEOB.cjs.js +0 -2
  98. package/dist/values-CDNAYEOB.cjs.js.map +0 -1
@@ -0,0 +1,1184 @@
1
+ import { EventEmitter } from '@for-fun/event-emitter';
2
+
3
+ /** A single Standard Schema issue. */
4
+ type StandardSchemaIssue = {
5
+ readonly message: string;
6
+ readonly path?: ReadonlyArray<PropertyKey | {
7
+ readonly key: PropertyKey;
8
+ }> | undefined;
9
+ };
10
+ /** The union Standard Schema's `validate` resolves to. */
11
+ type StandardSchemaResult<Output = unknown> = {
12
+ readonly value: Output;
13
+ readonly issues?: undefined;
14
+ } | {
15
+ readonly issues: ReadonlyArray<StandardSchemaIssue>;
16
+ };
17
+ /**
18
+ * Structural Standard Schema v1. `types` is optional in the spec but
19
+ * implemented by zod v3.24+/v4, valibot v1 and arktype — it is what
20
+ * {@link InferSchemaValues} and `createForm`'s `TValues` inference read.
21
+ */
22
+ type StandardSchemaV1<Input = unknown, Output = Input> = {
23
+ readonly '~standard': {
24
+ readonly version: 1;
25
+ readonly vendor: string;
26
+ readonly validate: (value: Input) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;
27
+ readonly types?: {
28
+ readonly input: Input;
29
+ readonly output: Output;
30
+ };
31
+ };
32
+ };
33
+ /**
34
+ * The values type a Standard Schema produces: the `Output` of its
35
+ * `~standard.types`, resolved structurally without importing the schema
36
+ * library. Coercions/transforms land in the output — `z.coerce.date()`
37
+ * infers `Date`, not `string`. `never` when the schema does not expose
38
+ * `types` (pass an explicit `TValues` then).
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * const schema = z.object({email: z.string().email()});
43
+ * type Values = InferSchemaValues<typeof schema>; // {email: string}
44
+ * const form = useForm<InferSchemaValues<typeof schema>>();
45
+ * ```
46
+ */
47
+ type InferSchemaValues<S> = S extends {
48
+ readonly '~standard': {
49
+ readonly types: {
50
+ readonly output: infer Out;
51
+ };
52
+ };
53
+ } ? Out : never;
54
+ /**
55
+ * Does the value implement the Standard Schema v1 props? A plain boolean
56
+ * predicate, not a type guard: narrowing a
57
+ * `Fn | StandardSchemaV1<In, Out>` union through the guard fails either
58
+ * way (a generics-narrowing guard is contravariance-blocked, an
59
+ * intersection guard keeps both members) — call sites cast after the
60
+ * check instead.
61
+ */
62
+ declare function hasStandardProps(schema: any): boolean;
63
+ /**
64
+ * Field-level Standard Schema adapter: validate a single value with any
65
+ * schema implementing '~standard' and map every issue to a FieldError, so
66
+ * a value breaking several rules surfaces all of them (setErrorByPath
67
+ * stores the array; error/errorObject readers still see the first).
68
+ * The schema's parsed output is validation-only here — field validators
69
+ * never rewrite the value; form-level schemas own coercion.
70
+ */
71
+ declare function schemaToFieldValidator(schema: StandardSchemaV1<any, any>): Validator;
72
+ /**
73
+ * Form-level Standard Schema adapter: validate the whole values object and
74
+ * return a ValidationOutcome. On failure `errors` carries the nested shape
75
+ * Options.validate expects ({a: {b: FieldError[]}}; ensureValidate flattens
76
+ * it back to per-field errors, keeping every issue of a path). Issues
77
+ * without a path are form-level errors and land on the FORM_ERROR key.
78
+ * On success `values` carries the schema's parsed output (coerce/transform
79
+ * results included), which the form stores as its parsedValues baseline —
80
+ * the layer getValues reads above initialValues.
81
+ *
82
+ * `createForm({validate: schema})` wraps the schema through this adapter
83
+ * automatically (no resolver import needed), and `TValues` infers from
84
+ * the schema's output type.
85
+ */
86
+ declare function schemaToFormValidator<T extends Record<string, any>>(schema: StandardSchemaV1<any, T>): (values: T) => Promise<ValidationOutcome<T>>;
87
+
88
+ type PathSegments = (string | number)[];
89
+ type Name = string | PathSegments;
90
+ type Path = {
91
+ value: PathSegments;
92
+ key: string;
93
+ };
94
+
95
+ /**
96
+ * Compile-time field path utilities: `FieldPath<T>` enumerates the valid
97
+ * path strings for a values shape `T` ('a', 'a.b', 'a[0]', 'a[b]', ...),
98
+ * and `PathValue<T, P>` resolves the leaf type a path points at.
99
+ * The grammar mirrors the paths accepted at runtime by `normalizePath`:
100
+ * numeric segments are bracket-only ('a[0]', never 'a.0' — dotted
101
+ * numerics throw a TypeError at runtime).
102
+ */
103
+ /** `true` only for the `any` type (`0 extends 1 & any`). */
104
+ type IsAny<T> = 0 extends 1 & T ? true : false;
105
+ type Primitive = null | undefined | string | number | boolean | symbol | bigint;
106
+ /**
107
+ * Opt-in registry of value types the path types treat as opaque leaves.
108
+ * `FieldPath<T>` stops descending into a registered type — a `Date`,
109
+ * `Dayjs` or class instance in the values shape is a value, not a field
110
+ * tree — and `PathValue` resolves the field itself to that type. Merge
111
+ * entries in via declaration merging:
112
+ *
113
+ * ```ts
114
+ * // app.d.ts
115
+ * declare module 'react-f0rm' {
116
+ * interface OpaqueTypes {
117
+ * dayjs: Dayjs;
118
+ * date: Date;
119
+ * }
120
+ * }
121
+ * ```
122
+ *
123
+ * Empty by default: every object-typed leaf keeps its historical,
124
+ * navigable behavior (react-hook-form's same-named registry is opt-in
125
+ * too). `keyof` of an empty interface is `never`, so the registry reads
126
+ * as a no-op until an entry lands.
127
+ */
128
+ interface OpaqueTypes {
129
+ }
130
+ /** The union of every leaf type registered in {@link OpaqueTypes} —
131
+ * `never` until an application merges entries in. */
132
+ type OpaqueLeaf = OpaqueTypes[keyof OpaqueTypes];
133
+ /** Depth countdown: Prev[9] = 8 ... Prev[1] = 0, Prev[0] = never stops recursion. */
134
+ type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
135
+ /** Paths are capped at 10 segments to keep instantiation depth bounded. */
136
+ type MaxDepth = 9;
137
+ /**
138
+ * Valid path continuations after a segment: `.k` / `[k]` / `[0]`,
139
+ * optionally followed by deeper continuations into the child node.
140
+ * Numeric segments are bracket-only (`.0` throws at runtime); object
141
+ * keys that are themselves numeric strings likewise enumerate just the
142
+ * bracket subscript.
143
+ */
144
+ type Continue<T, D extends number> = [D] extends [never] ? never : IsAny<T> extends true ? string : T extends OpaqueLeaf ? never : T extends Primitive | Function ? never : T extends readonly (infer U)[] ? `[${number}]` | `[${number}]${Continue<U, Prev[D]>}` : {
145
+ [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]>}`;
146
+ }[Extract<keyof T, string>];
147
+ /**
148
+ * Every valid field path string for a values shape `T`.
149
+ *
150
+ * Enumeration is capped at 10 segments ({@link MaxDepth}) to keep
151
+ * instantiation depth bounded: paths below the cap are not part of this
152
+ * type, so their call sites fall back to `any` through
153
+ * {@link PathValueOf} instead of erroring. Types registered in
154
+ * {@link OpaqueTypes} stop recursion entirely — register deep object
155
+ * leaves (Date, Dayjs, class instances) there instead of relying on the
156
+ * cap.
157
+ * @example FieldPath<{a: {b: string}}> // 'a' | 'a.b' | 'a[b]'
158
+ */
159
+ type FieldPath<T> = IsAny<T> extends true ? string : T extends OpaqueLeaf ? never : T extends Primitive | Function ? never : T extends readonly (infer U)[] ? `[${number}]` | `[${number}]${Continue<U, MaxDepth>}` : {
160
+ [K in Extract<keyof T, string>]: K extends `${number}` ? never : K | `${K}${Continue<T[K], MaxDepth>}`;
161
+ }[Extract<keyof T, string>];
162
+ /** Resolve `T[K]` for one bare segment: array index -> element, object key -> value. */
163
+ type Lookup<T, K extends string> = K extends `${number}` ? T extends readonly (infer U)[] ? U : never : K extends keyof T ? T[K] : never;
164
+ /** One dot-separated chunk: a bare segment plus any `[k]` / `[0]` suffixes. */
165
+ type ChunkValue<T, C extends string> = C extends `${infer Key}[${infer Tail}` ? ChunkSuffix<Lookup<T, Key>, `[${Tail}`> : Lookup<T, C>;
166
+ type ChunkSuffix<T, S extends string> = S extends `[${infer Key}]${infer Rest}` ? Rest extends '' ? Lookup<T, Key> : PathOf<Lookup<T, Key>, Rest> : never;
167
+ /** Resolve the value type the path string `P` points at inside `T`. */
168
+ 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>;
169
+ /**
170
+ * The value type at path `P` of a values shape `T`.
171
+ * @example PathValue<{a: {b: string}}, 'a.b'> // string
172
+ */
173
+ type PathValue<T, P extends FieldPath<T>> = PathOf<T, P & string>;
174
+ /**
175
+ * The value type at path `P` of `T`, or `any` when `P` is not a known
176
+ * field path (plain `string` / segment-array calls keep their old behavior).
177
+ */
178
+ type PathValueOf<T, P> = P extends FieldPath<T> ? PathValue<T, Extract<P, FieldPath<T>>> : any;
179
+ /**
180
+ * The element type of the array a path points at inside a values shape:
181
+ * `ArrayItemOf<{tags: Item[]}, 'tags'>` is `Item`. Non-array leaves
182
+ * resolve to the leaf type itself; unknown paths (plain `string`,
183
+ * segment arrays) fall back to `any` — the same wide-name escape hatch
184
+ * {@link PathValueOf} keeps.
185
+ */
186
+ type ArrayItemOf<T, P> = P extends FieldPath<T> ? PathValue<T, Extract<P, FieldPath<T>>> extends readonly (infer U)[] ? U : PathValue<T, Extract<P, FieldPath<T>>> : any;
187
+
188
+ /** Type tag of a failed rule, as stored on the resulting FieldError. */
189
+ type RuleType = 'required' | 'min' | 'max' | 'minLength' | 'maxLength' | 'pattern';
190
+ /**
191
+ * Declarative field rules — a subset of react-hook-form's `register` rules.
192
+ *
193
+ * Failed rules land in the form's error state as FieldErrors (`type` is the
194
+ * rule name) instead of only surfacing through the browser's validity
195
+ * bubble, so any design system can render the messages uniformly.
196
+ */
197
+ type FieldRules = {
198
+ /**
199
+ * Fails on empty values: `''`, `undefined`, `null` or an empty array
200
+ * (`0` and `false` count as filled) — react-hook-form's `required`
201
+ * semantics. A string is the error message; `true` uses the default.
202
+ * When it fails, the remaining rules are skipped — an empty value
203
+ * reports only its required error.
204
+ */
205
+ required?: string | true;
206
+ /** Fails when `Number(value)` is below this bound; `NaN` values skip. */
207
+ min?: number;
208
+ /** Fails when `Number(value)` is above this bound; `NaN` values skip. */
209
+ max?: number;
210
+ /** Fails when a string value is shorter than this, or an array has
211
+ * fewer entries; other values skip. */
212
+ minLength?: number;
213
+ /** Fails when a string value is longer than this, or an array has more
214
+ * entries; other values skip. */
215
+ maxLength?: number;
216
+ /** Fails when the value does not match `pattern.value`. */
217
+ pattern?: {
218
+ value: RegExp;
219
+ message: string;
220
+ };
221
+ /**
222
+ * Custom rule callbacks — react-hook-form's `register({validate})`
223
+ * shape: one function, or a record of named functions. Each runs after
224
+ * the declarative checks, and only when they passed (`required` failing
225
+ * short-circuits the rest, RHF's first-error semantics). A returned
226
+ * error keeps its message; its `type` becomes the record key
227
+ * (`'validate'` for the single-function form) so consumers can switch
228
+ * on `error.type`. Sync-only — async checks belong in the field's
229
+ * `validate` option.
230
+ */
231
+ validate?: SyncValidator | Record<string, SyncValidator>;
232
+ /**
233
+ * Overrides the message per rule type — `min`, `max`, `minLength`,
234
+ * `maxLength` defaults and pattern's inline `message` alike — e.g. for
235
+ * centralizing or localizing messages.
236
+ */
237
+ messages?: Partial<Record<Exclude<RuleType, 'required'>, string>>;
238
+ };
239
+
240
+ /** Options accepted by {@link setFocus}. All flags default to `false`. */
241
+ type SetFocusOptions = {
242
+ /** Select the field's text after focusing it. Bound fields call
243
+ * `select()` on their element; elements without one (custom `as`
244
+ * components) just focus. */
245
+ shouldSelect?: boolean;
246
+ };
247
+ /**
248
+ * Programmatically focus a bound field's element (e.g. the <Field>'s
249
+ * input).
250
+ *
251
+ * Rides the same 'focusError' event channel a failed handleSubmit uses to
252
+ * focus the first errored field: the payload is the target's path key,
253
+ * with the focus options as a second, backward-compatible argument (older
254
+ * subscribers declared with a single `key` parameter simply ignore it).
255
+ * Being event-driven, it is a silent no-op when the field is unmounted or
256
+ * nothing subscribes — unknown names never throw.
257
+ *
258
+ * @param form form instance
259
+ * @param name field name (dot path or segments path)
260
+ * @param options focus options
261
+ */
262
+ declare function setFocus(form: Form, name: Name, options?: SetFocusOptions): void;
263
+
264
+ /** Reserved top-level path segment for form-level errors. The Standard
265
+ * Schema form-level adapter lands path-less issues under this key; the
266
+ * exported constant replaces the magic string, and readers consume it via
267
+ * getError(form, FORM_ERROR) / getFieldErrors(form, FORM_ERROR). */
268
+ declare const FORM_ERROR = "_form";
269
+ /** When a field is validated:
270
+ * - `'onSubmit'` (default): only on submit
271
+ * - `'onBlur'`: when the field loses focus
272
+ * - `'onChange'`: on every change
273
+ * - `'onTouched'`: on first blur, then on every change
274
+ * - `'all'`: on both change and blur
275
+ */
276
+ /** Brand marking a form-level validate result as a structured
277
+ * {@link ValidationOutcome} (parsed values and/or errors) rather than a
278
+ * plain nested error record. Symbols cannot collide with user error
279
+ * records, so detection is an exact `VALIDATION_OUTCOME in result`. */
280
+ declare const VALIDATION_OUTCOME: unique symbol;
281
+ /** Structured form-level validate result: `errors` uses the same nested
282
+ * shape a plain error record uses, `values` is the schema's parsed output
283
+ * (coerce/transform results included). Either side may be omitted. */
284
+ /**
285
+ * Get field error
286
+ * @param form
287
+ * @param name
288
+ * @return FieldError object or undefined
289
+ */
290
+ declare function getError<T extends Record<string, any> = any, P extends FieldPath<T> | PathSegments = FieldPath<T> | PathSegments>(form: Form<T>, name: P): FieldError | undefined;
291
+ /**
292
+ * Get field error by path
293
+ * @param form
294
+ * @param path
295
+ * @return first FieldError of the field, or undefined
296
+ */
297
+ declare function getErrorByPath({ errors }: Form, path: Path): FieldError | undefined;
298
+ /**
299
+ * Get all errors of a field
300
+ * @param form
301
+ * @param name
302
+ * @return every error registered for the field (insertion order); an empty
303
+ * array when the field has none
304
+ */
305
+ declare function getFieldErrors<T extends Record<string, any> = any, P extends FieldPath<T> | PathSegments = FieldPath<T> | PathSegments>(form: Form<T>, name: P): FieldError[];
306
+ /**
307
+ * Get all errors of a field by path
308
+ * @param form
309
+ * @param path
310
+ * @return every error registered for the field (insertion order); an empty
311
+ * array when the field has none
312
+ */
313
+ declare function getFieldErrorsByPath({ errors }: Form, path: Path): FieldError[];
314
+ /**
315
+ * Get all errors
316
+ * @param form
317
+ * @return array of {path, type, message} entries, in insertion order; path
318
+ * is the user-facing dotted field path ('a.b', 'list.0'), and a
319
+ * field holding several errors contributes one entry per error
320
+ */
321
+ declare function getErrors({ errors }: Form): FieldErrorEntry[];
322
+ /**
323
+ * Convert a {@link FieldPath} string to the key form error records use at
324
+ * runtime: paths spell array access with brackets ('items[0].name') while
325
+ * {@link getErrorsRecord} keys are dot-joined segments ('items.0.name').
326
+ * The declared keys follow the runtime, so typed reads
327
+ * (`errors['items.0.name']`) match what the record actually holds. Quoted
328
+ * segments ('items["0"]') drop their quotes like the parser does.
329
+ *
330
+ * Public so consumers can name the key style in their own types —
331
+ * `const key: DottedPath<'items[0].name'> = 'items.0.name'`.
332
+ */
333
+ type DottedPath<P extends string> = P extends `${infer H}[${infer N}]${infer R}` ? `${H extends '' ? '' : `${H}.`}${N extends `"${infer K}"` | `'${infer K}'` ? K : N}${DottedPath<R>}` : P;
334
+ /**
335
+ * Every error as one record keyed by user-facing dotted path, typed
336
+ * against the values shape — react-hook-form's `FieldErrors<T>` shape
337
+ * (per-key values are optional there too). Keys follow the runtime form:
338
+ * dotted paths ('a.b', 'list.0'), plus the {@link FORM_ERROR} slot for
339
+ * form-level errors. Values are the stored FieldError[] arrays shared
340
+ * with the form, so treat the whole result as read-only.
341
+ */
342
+ type FieldErrors<T extends Record<string, any> = any> = Partial<Record<DottedPath<Extract<FieldPath<T>, string>>, FieldError[]>> & {
343
+ [FORM_ERROR]?: FieldError[];
344
+ };
345
+ /** `true` only for the `any` type (the same probe {@link FieldPath}
346
+ * uses), so the tree of an untyped form degrades to `any` instead of an
347
+ * infinite mapped-type expansion. */
348
+ type IsAnyTree<T> = 0 extends 1 & T ? true : false;
349
+ type TreePrimitive = null | undefined | string | number | boolean | symbol | bigint;
350
+ /** One level of the nested error tree: arrays become arrays of the item's
351
+ * tree, objects recurse per key, everything else — primitives, functions,
352
+ * registered {@link OpaqueTypes} leaves, and the common opaque DOM/date
353
+ * containers (Date, File, FileList, Map, Set) — is a leaf holding the
354
+ * stored FieldError[] (shared with the form — treat as read-only). */
355
+ type FieldErrorsTreeNode<T> = IsAnyTree<T> extends true ? any : T extends OpaqueTypes[keyof OpaqueTypes] ? FieldError[] : T extends TreePrimitive | Function | Date | File | FileList | Map<any, any> | Set<any> ? FieldError[] : T extends ReadonlyArray<infer E> ? FieldErrorsTreeNode<E>[] : {
356
+ [K in keyof T]?: FieldErrorsTreeNode<T[K]>;
357
+ };
358
+ /**
359
+ * Every error as one nested object following the values tree —
360
+ * react-hook-form's `formState.errors` shape with typed optional chains
361
+ * (`errors.items?.[0]?.name`), the readable counterpart of
362
+ * {@link FieldErrors}' flat dotted keys. Array positions become array
363
+ * indices (holes stay absent), object fields become optional keys,
364
+ * leaves hold the stored FieldError[] arrays shared with the form — treat
365
+ * the whole result as read-only. The {@link FORM_ERROR} slot holds
366
+ * form-level errors at the top level.
367
+ *
368
+ * One conflict is resolved by insertion order: a row-level error at
369
+ * `items[0]` and a field error at `items[0].name` cannot both occupy the
370
+ * `items[0]` slot, so whichever landed later owns it (react-hook-form's
371
+ * nested `setError` clobbers the same way). The flat record never
372
+ * conflicts — read it when both coexist.
373
+ */
374
+ type FieldErrorsTree<T = any> = FieldErrorsTreeNode<T> & {
375
+ [FORM_ERROR]?: FieldError[];
376
+ };
377
+ /**
378
+ * Get every error as one record keyed by user-facing dotted path
379
+ * ('a.b', 'list.0') — react-hook-form's `formState.errors` shape. Values
380
+ * are the stored FieldError[] arrays shared with the form, so treat the
381
+ * whole result as read-only. Memoized per form with the same
382
+ * version-bump/read pattern {@link getValues} uses: every error write
383
+ * bumps {@link bumpErrorsVersion}, consecutive reads hand back one stable
384
+ * reference, so {@link useErrors} / `useFormState().errors` only re-render
385
+ * when an error actually changed.
386
+ *
387
+ * @param form
388
+ */
389
+ declare function getErrorsRecord<T extends Record<string, any> = any>(form: Form<T>): FieldErrors<T>;
390
+ /**
391
+ * Get every error as one nested object following the values tree
392
+ * (`errors.items[0].name` reads — the shape react-hook-form's
393
+ * `formState.errors` uses), the optional-chaining counterpart of
394
+ * {@link getErrorsRecord}'s flat dotted keys. Array positions become
395
+ * array indices, leaves hold the stored FieldError[] arrays shared with
396
+ * the form — treat the whole result as read-only. Memoized alongside the
397
+ * record through the same version-bump/read pattern, so {@link
398
+ * useErrorsTree} only re-renders when an error actually changed.
399
+ *
400
+ * @param form
401
+ */
402
+ declare function getErrorsTree<T extends Record<string, any> = any>(form: Form<T>): FieldErrorsTree<T>;
403
+ /**
404
+ * Convert a field path (bracket spelling, the `name` every API takes) into
405
+ * the dotted key {@link getErrorsRecord} / {@link getErrors} use:
406
+ * `'items[0].name'` → `'items.0.name'`. Useful for error-record reads —
407
+ * `errors[fieldPathToDottedKey(name)]` — and for centralizing the
408
+ * translation when the two spellings meet in one component.
409
+ */
410
+ declare function fieldPathToDottedKey(name: Name): string;
411
+ /**
412
+ * Convert a dotted errors-record key back into bracket path spelling:
413
+ * `'items.0.name'` → `'items[0].name'` — the form every API takes, so the
414
+ * result feeds straight into `getError(form, …)` / `<Field name=…>`.
415
+ *
416
+ * Numeric-shaped segments become bracket segments; everything else stays
417
+ * dot-joined. Inherently lossy by the record's own convention: the dotted
418
+ * key cannot distinguish a segment containing a literal dot (a quoted path
419
+ * like `a["b.c"]` joins to the same `'a.b.c'` as `a.b.c`) — for such paths
420
+ * keep the bracket spelling and use {@link fieldPathToDottedKey} for the
421
+ * record read instead.
422
+ */
423
+ declare function dottedKeyToFieldPath(key: string): string;
424
+ /**
425
+ * Get first error message
426
+ * @param form
427
+ * @return first error's message string, or undefined when there are no errors
428
+ */
429
+ declare function getFirstError({ errors }: Form): string | undefined;
430
+ /** Snapshot of one field's aggregated state, as {@link getFieldState}
431
+ * returns it. `errors` is the stored array shared with the form — treat it
432
+ * as read-only, like every {@link getFieldErrors} result. */
433
+ /** Options accepted by {@link setError}. */
434
+ type SetErrorOptions = {
435
+ /**
436
+ * Focus the named field's element after the error lands (react-hook-form's
437
+ * `setError` `shouldFocus`). Rides the same 'focusError' channel
438
+ * `setFocus` and a failed submit's auto-focus use: only mounted bound
439
+ * fields react, unmounted ones are silent no-ops.
440
+ */
441
+ shouldFocus?: boolean;
442
+ };
443
+ /**
444
+ * Set field error
445
+ * @param form
446
+ * @param name
447
+ * @param error string is normalized to {type: 'custom', message}; a
448
+ * FieldError object is stored as-is; an array holds several errors
449
+ * (falsy items dropped, strings normalized); undefined clears
450
+ * @param options {@link SetErrorOptions} — `shouldFocus` focuses the field
451
+ * after the error lands
452
+ */
453
+ declare function setError<T extends Record<string, any> = any, P extends FieldPath<T> | PathSegments = FieldPath<T> | PathSegments>(form: Form<T>, name: P, error: string | FieldError | (string | FieldError)[] | undefined, options?: SetErrorOptions): void;
454
+ /**
455
+ * Set field error
456
+ * @param form
457
+ * @param path
458
+ * @param error string is normalized to {type: 'custom', message}; a
459
+ * FieldError object is stored as-is; an array holds several errors
460
+ * (falsy items dropped, strings normalized); undefined clears
461
+ * @param options {@link SetErrorOptions} — `shouldFocus` focuses the field
462
+ * after the error lands
463
+ */
464
+ declare function setErrorByPath(form: Form, path: Path, error: string | FieldError | (string | FieldError)[] | undefined, options?: SetErrorOptions): void;
465
+ /**
466
+ * Clear errors
467
+ * @param form
468
+ * @param name a single path or a list of paths; omit to clear every error
469
+ */
470
+ declare function clearErrors(form: Form, name?: Name | Name[]): void;
471
+ /** Options accepted by {@link setServerErrors}. */
472
+ type SetServerErrorsOptions = {
473
+ /** Keep existing field errors instead of clearing them first. Defaults
474
+ * to `false`: a fresh server response replaces the prior error state. */
475
+ keepExisting?: boolean;
476
+ };
477
+ /**
478
+ * Land a server-side error response on the form: each entry becomes the
479
+ * named field's error(s) with `type: 'server'`, ready for the same
480
+ * renderError/`useError` channel client-side validation uses. Takes the
481
+ * flat `Record<string, string | string[]>` shape REST APIs commonly
482
+ * return (RealWorld: `422 {errors: {email: ['has already been taken']}}`)
483
+ * without a hand-rolled `Object.entries` + `setError` loop.
484
+ *
485
+ * A string value lands as one error, a string array as several (first one
486
+ * is what `getError`/`error` expose); an empty array clears that field's
487
+ * errors. By default every existing error is cleared first — a fresh
488
+ * response describes the current state, not a patch onto stale client
489
+ * errors; pass `keepExisting: true` to layer instead.
490
+ * @param form
491
+ * @param errors field errors keyed by name
492
+ * @param options
493
+ */
494
+ declare function setServerErrors(form: Form, errors: Record<string, string | string[]>, options?: SetServerErrorsOptions): void;
495
+ /**
496
+ * Drop every `type: 'server'` error from the form — the round-trip state
497
+ * a previous submit landed. {@link handleSubmit} runs this before its
498
+ * validation round so a retry is judged on the fresh attempt, not on the
499
+ * server's verdict for the last payload (client errors are untouched:
500
+ * they describe the current form state). Emits payload-less 'errors' when
501
+ * anything changed.
502
+ */
503
+ declare function clearServerErrors(form: Form): void;
504
+ /**
505
+ * Set field touched state
506
+ * @param form
507
+ * @param name
508
+ */
509
+ /**
510
+ * @param form
511
+ */
512
+ declare function hasErrors({ errors }: Form): boolean;
513
+
514
+ declare function unsetValidatingByPath({ emitter, validating }: Form, path: Path): void;
515
+ declare function setValidatingByPath({ emitter, validating }: Form, path: Path): void;
516
+ /**
517
+ * Field validator. Returns an error (a string, a FieldError, or an array
518
+ * mixing both) or undefined when valid; may return a Promise for async
519
+ * validation.
520
+ *
521
+ * The second argument carries the validation context. `meta.signal` is
522
+ * aborted as soon as the round is superseded — a newer round started, or
523
+ * the field unregistered — so async validators can cancel their underlying
524
+ * work (fetch, timers) instead of racing a stale result home. Stale
525
+ * results are dropped independently by the registration's lock
526
+ * ({@link registerValidatorByPath}), so validators that ignore the signal
527
+ * stay correct too. Validators written against the older two-argument
528
+ * signature keep working.
529
+ */
530
+ type Validator = (value: any, meta: {
531
+ form: Form;
532
+ path: Path;
533
+ signal: AbortSignal;
534
+ }) => string | FieldError | (string | FieldError)[] | undefined | Promise<string | FieldError | (string | FieldError)[] | undefined>;
535
+ /**
536
+ * Synchronous pre-validator for {@link registerValidatorByPath}'s `sync`
537
+ * accessor — declarative `required` rules compiled by `rulesToValidator`
538
+ * in practice, but any sync-only check works. Runs on every kick, never
539
+ * debounced: its errors land immediately and, while present,
540
+ * short-circuit the debounced validator for that kick (the expensive
541
+ * check never sees a value the gate already rejects). Must be synchronous
542
+ * — unlike a {@link Validator} it may not return a Promise — and its meta
543
+ * carries no `signal`: there is nothing to abort in a synchronous check.
544
+ */
545
+ type SyncValidator = (value: any, meta: {
546
+ form: Form;
547
+ path: Path;
548
+ }) => string | FieldError | (string | FieldError)[] | undefined;
549
+ /** Live options for {@link registerValidatorByPath}: read at every kick
550
+ * through accessors, so callers (React's `useValidate`) can swap the
551
+ * validator/debounce/sync-gate per render without re-subscribing the
552
+ * registration mid-flight. */
553
+ type ValidatorRegistration = {
554
+ /** Current debounced validator (or undefined — a sync-only
555
+ * registration). */
556
+ validate: () => Validator | undefined;
557
+ /** Debounce delay in milliseconds; 0 (default) runs immediately. */
558
+ debounce: () => number;
559
+ /** Synchronous pre-validator, run on every kick (never debounced). */
560
+ sync: () => SyncValidator | undefined;
561
+ /** Whether the debounced validator still runs when the sync gate
562
+ * failed — TanStack Form's `asyncAlways`: the gate's errors land
563
+ * immediately (never debounced), then the validator's own result lands
564
+ * alongside them (per-source semantics) instead of the gate
565
+ * short-circuiting the whole kick. Optional — absent means false
566
+ * (gate failure owns the kick's outcome), so pre-existing
567
+ * framework-free registrations keep working unchanged. */
568
+ asyncAlways?: () => boolean;
569
+ };
570
+ /**
571
+ * Register a field validator's kick at `path` in {@link Form.validators}
572
+ * — the framework-free machinery behind `useValidate`. Returns a
573
+ * disposer that drops the registration and cancels any pending debounce
574
+ * window or in-flight round (its signal aborts and its validating mark
575
+ * is released).
576
+ *
577
+ * Contract of the registered kick (the same contract `trigger` /
578
+ * `ensureValidate` rely on when they run every entry, and the
579
+ * user-change gate relies on when it runs the changed path's entry):
580
+ * - the `sync` gate runs immediately on every kick — never debounced —
581
+ * and while it returns errors, the debounced validator is skipped for
582
+ * that kick and any pending window or in-flight round is superseded —
583
+ * unless `asyncAlways` is set, in which case the validator still runs
584
+ * and its result lands alongside the gate's errors (per-source);
585
+ * - a positive `debounce` merges kicks inside the window: only the last
586
+ * one runs the validator, and while the timer is pending the field
587
+ * counts as validating so `trigger`/`ensureValidate` wait it out;
588
+ * - async results land under a lock: a superseded round's outcome —
589
+ * rejection included — is dropped, and only the owning round releases
590
+ * the validating mark;
591
+ * - a synchronous throw inside the validator propagates to the caller
592
+ * (the validating mark is not left stuck behind it).
593
+ *
594
+ * Registering at a path already registered by another mount replaces it
595
+ * (last-wins, the historical `useValidate` behavior); the disposer drops
596
+ * its own registration unconditionally.
597
+ *
598
+ * @param form
599
+ * @param path
600
+ * @param registration live validator/debounce/sync accessors
601
+ * @return disposer: unregister and cancel pending work
602
+ */
603
+ declare function registerValidatorByPath(form: Form, path: Path, registration: ValidatorRegistration): () => void;
604
+ /**
605
+ * Set field error
606
+ * @param form
607
+ * @param name
608
+ * @param error string is normalized to {type: 'custom', message}; a
609
+ * FieldError object is stored as-is; an array holds several errors
610
+ * (falsy items dropped, strings normalized); undefined clears
611
+ */
612
+ /** Options accepted by {@link trigger}. `shouldTouch` defaults to `false`;
613
+ * omitting the options object entirely keeps the plain validate-only
614
+ * behavior, so the historical two-argument calls are untouched. */
615
+ type TriggerOptions = {
616
+ /** Mark every path in the triggered scope as touched — even when
617
+ * validation fails — once the round settles. Mirrors react-hook-form's
618
+ * trigger `shouldTouch`. Defaults to `false`. */
619
+ shouldTouch?: boolean;
620
+ /**
621
+ * Focus the first errored field in the triggered scope once the round
622
+ * settles (and only when the round left errors) — react-hook-form's
623
+ * trigger `shouldFocus` counterpart. Rides the 'focusError' event
624
+ * channel like a failed submit's auto-focus: only mounted bound fields
625
+ * react, unmounted ones are silent no-ops. Without `name` the first key
626
+ * of the errors Map wins (the same rule handleSubmit applies); with
627
+ * `name` the first errored triggered key does. Defaults to `false`.
628
+ */
629
+ shouldFocus?: boolean;
630
+ };
631
+ /**
632
+ * Trigger field validation.
633
+ *
634
+ * Without `name` every registered field validator runs. A single `name` —
635
+ * dotted string or segments array — runs only that field's validator, and
636
+ * an array of names runs each one in order. An empty array is a no-op, as
637
+ * is any name with no registered validator. An array argument counts as
638
+ * one segments path only when it mixes in numbers (`['items', 0]`); pure
639
+ * string arrays are name lists, so `['a', 'b']` triggers fields `a` and
640
+ * `b`, not the nested path `a.b`.
641
+ *
642
+ * `options.shouldTouch` marks the triggered scope — the given names, or
643
+ * every registered field when `name` is omitted — as touched after the
644
+ * round settles, whether validation passed or failed. The wait/settle
645
+ * logic is untouched: the marking rides on top of the settled round, so
646
+ * subscribers observe errors and touched together rather than mid-flight.
647
+ *
648
+ * The returned promise waits for the triggered validation to settle —
649
+ * async validators included — so their errors have already landed in
650
+ * `form.errors` when it resolves. It never rejects: landing errors is the
651
+ * expected outcome here, not a failure. Resolves `true` when the triggered
652
+ * scope is error-free, `false` otherwise. Without `name` the scope is all
653
+ * fields plus the form-level `validate` result (which runs after field
654
+ * validators settle, same pipeline as {@link ensureValidate}); with `name`
655
+ * only those fields' own errors count and form-level `validate` is
656
+ * skipped (RHF semantics).
657
+ *
658
+ * Fire-and-forget callers may ignore the promise: the validator kicks
659
+ * still happen synchronously, matching the pre-promise behavior.
660
+ *
661
+ * @param form
662
+ * @param name field name(s) to trigger, or all fields when omitted
663
+ * @param options extra behavior toggles ({@link TriggerOptions}); omitted,
664
+ * validation alone runs — no touched marking
665
+ * @return whether the triggered scope is error-free once validation settles
666
+ */
667
+ declare function trigger(form: Form, name?: Name | Name[], options?: TriggerOptions): Promise<boolean>;
668
+ /**
669
+ * Run the form-level `validate` and land its result, honoring the form's
670
+ * `validateDebounce` option.
671
+ *
672
+ * Undebounced (`0`/undefined) the caller's await *is* the validate call —
673
+ * the long-standing pipeline, unchanged: no validating mark, no round
674
+ * gating, immediate values snapshot, rejection propagating to the caller.
675
+ *
676
+ * Debounced, the kick opens (or restarts — kicks inside the window merge)
677
+ * a window during which the form counts as validating, so `trigger` /
678
+ * `ensureValidate` / submit wait the window out exactly like a field's
679
+ * `validateDebounce` window. When the timer fires, the round reads the
680
+ * then-current values, supersedes (aborts) any in-flight round, and lands
681
+ * its result. The returned promise settles once the window group's final
682
+ * round has landed — rejecting when that round's validate callback threw
683
+ * or its promise rejected, mirroring the undebounced propagation — so
684
+ * merged callers all observe the same outcome.
685
+ *
686
+ * Only called under `if (form.validate)`. Public: the form-level-only
687
+ * round (field validators excluded) — `useForm`'s `validateOnMount` run
688
+ * and cross-cutting re-checks use it; `trigger`/`ensureValidate` compose
689
+ * it after the field validators settle.
690
+ */
691
+ declare function runFormValidate(form: Form): Promise<void>;
692
+ /**
693
+ * Form-level twin of the gated validator kick in `useField`'s onChange:
694
+ * re-run the form-level `validate` after a user change to a field listed
695
+ * in `validateDeps`. Called from the field's own change pipeline (typing
696
+ * and `changeValue` alike — both route through the mounted field's
697
+ * onChange), so programmatic `setValue` writes do not re-run it, exactly
698
+ * like they do not re-run field validators.
699
+ *
700
+ * The gate mirrors the per-field matrix with the *changed field's*
701
+ * effective `mode` (a per-field override governs when its changes may
702
+ * fire validation) and the form-level `reValidateMode` against the last
703
+ * round's error footprint ({@link hasFormValidateErrors} — field
704
+ * validators' errors never arm this kick):
705
+ * - `mode` `'onChange'`/`'all'` — every dep change re-runs;
706
+ * - `mode` `'onTouched'` — dep changes re-run once the field was touched;
707
+ * - otherwise the re-run waits for `reValidateMode: 'onChange'` (the
708
+ * default) while the last round's error is still live — the
709
+ * submit-then-fix flow: the mismatch lands on submit, editing the
710
+ * dependency re-runs the validate and clears it.
711
+ * `reValidateMode: 'onBlur'`/`'onSubmit'` never re-run on a change (a
712
+ * change is not a blur; submit re-runs are the submit pipeline's job).
713
+ *
714
+ * The kick is fire-and-forget: async round rejections are swallowed
715
+ * (nothing in an event handler can await them), while a synchronous
716
+ * throw inside the validate callback propagates to the caller exactly
717
+ * like a field validator's does.
718
+ *
719
+ * A no-op unless the form set `validateDeps` listing `path` — forms
720
+ * without the option pay one property check here.
721
+ */
722
+ declare function revalidateFormOnChange(form: Form, path: Path, mode: ValidationMode): void;
723
+ /** Register one field's validateDeps declaration: `key` re-validates when
724
+ * any path in `depKeys` takes a user change. Idempotent per (key, dep)
725
+ * pair, so StrictMode's double effect is harmless. */
726
+ declare function registerFieldValidateDeps(form: Form, key: string, depKeys: string[]): void;
727
+ /** Drop one field's validateDeps registration ({@link
728
+ * registerFieldValidateDeps}). Entries nobody lists anymore are removed so
729
+ * the registry never outlives its fields. */
730
+ declare function unregisterFieldValidateDeps(form: Form, key: string, depKeys: string[]): void;
731
+ /**
732
+ * Field-level twin of {@link revalidateFormOnChange}: after a user change
733
+ * to `path`, re-run every field validator that declared `path` in its
734
+ * `validateDeps` (useField option). Same channel, same gate: the kick
735
+ * rides the changed field's own onChange pipeline (typing and
736
+ * `changeValue` alike), so programmatic `setValue` writes never fire it —
737
+ * exactly like field validators and the form-level `validateDeps`.
738
+ *
739
+ * The gate mirrors the form-level matrix with the *changed field's*
740
+ * effective `mode` and the form-level `reValidateMode` against each
741
+ * dependent's live error:
742
+ * - `mode` `'onChange'`/`'all'` — every dep change re-runs the dependent;
743
+ * - `mode` `'onTouched'` — once the changed field was touched;
744
+ * - otherwise the re-run waits for `reValidateMode: 'onChange'` (the
745
+ * default) while the dependent still shows an error — the
746
+ * submit-then-fix flow: the mismatch lands on submit, editing the
747
+ * dependency re-validates the dependent and a passing round clears it
748
+ * (a field validator owns its whole key, so the re-run's result
749
+ * replaces whatever the previous round wrote — the field-level shape
750
+ * of the form-level footprint reclaim).
751
+ *
752
+ * The kick is an ordinary validator kick: the dependent's own
753
+ * `validateDebounce` window applies, and a synchronous throw inside its
754
+ * validate propagates to the caller like any field validator's would.
755
+ *
756
+ * A no-op unless some field declared `path` as a dep — forms without any
757
+ * field-level `validateDeps` pay one property check here.
758
+ */
759
+ declare function revalidateDependentsOnChange(form: Form, path: Path, mode: ValidationMode): void;
760
+ /** The Error {@link ensureValidate} rejects with: `message` is the first
761
+ * error's display text ({@link getFirstError}) — the long-standing shape
762
+ * — and `.errors` carries the complete flattened error list ({@link
763
+ * getErrors}: `{path, type, message}` entries, dotted display paths) so
764
+ * catchers can branch on types and locate fields without re-reading the
765
+ * form. */
766
+ type FormValidationError = Error & {
767
+ errors: FieldErrorEntry[];
768
+ };
769
+ /**
770
+ * Validate and throw if any field error.
771
+ * @param form
772
+ * @return resolve if no error; reject and stop validate if has an error
773
+ */
774
+ declare function ensureValidate(form: Form): Promise<void>;
775
+ /**
776
+ * Validate and return if any field error.
777
+ * @param form
778
+ * @return error message string or void
779
+ */
780
+ declare function validate(form: Form): Promise<void | string>;
781
+
782
+ /** A field error: `type` identifies the error kind ('custom' for plain
783
+ * string errors), `message` is the display text. */
784
+ type FieldError = {
785
+ type: string;
786
+ message: string;
787
+ };
788
+ /** A flattened entry from {@link getErrors}. */
789
+ type FieldErrorEntry = {
790
+ path: string;
791
+ type: string;
792
+ message: string;
793
+ };
794
+ /** When a field is validated:
795
+ * - `'onSubmit'` (default): only on submit
796
+ * - `'onBlur'`: when the field loses focus
797
+ * - `'onChange'`: on every change
798
+ * - `'onTouched'`: on first blur, then on every change
799
+ * - `'all'`: on both change and blur
800
+ */
801
+ type ValidationMode = 'onSubmit' | 'onBlur' | 'onChange' | 'onTouched' | 'all';
802
+ /** When a field is re-validated after it already has an error:
803
+ * - `'onChange'` (default): on every change
804
+ * - `'onBlur'`: when the field loses focus
805
+ * - `'onSubmit'`: only on submit (no live re-validation)
806
+ */
807
+ type ReValidateMode = 'onChange' | 'onBlur' | 'onSubmit';
808
+ /** When the form-level {@link Options.validate} re-runs outside
809
+ * submit/`trigger`/`validateOnMount`:
810
+ * - `'onSubmit'` (default): only on submit/trigger — cross-field linkage
811
+ * goes through {@link Options.validateDeps} instead
812
+ * - `'onChange'`: every user change to a bound field re-runs it
813
+ * - `'onBlur'`: every user blur of a bound field re-runs it
814
+ *
815
+ * TanStack Form's `validators.onChange`/`validators.onBlur` counterpart:
816
+ * a cadence declaration instead of enumerating deps. The re-run rides the
817
+ * changed field's own user-change pipeline (typing and `changeValue`
818
+ * alike, never programmatic `setValue`), honors {@link
819
+ * Options.validateDebounce}, and reuses the round-scoped error footprint —
820
+ * a passing re-run clears what the previous round wrote.
821
+ */
822
+ type FormValidateMode = 'onSubmit' | 'onChange' | 'onBlur';
823
+ /**
824
+ * Options accepted by {@link Form.register} — the non-hook binding for
825
+ * uncontrolled fields (react-hook-form's `register` contract: the bound
826
+ * element never re-renders; the store carries every write).
827
+ */
828
+ type RegisterOptions = {
829
+ /**
830
+ * Field-level validation mode for this binding (see {@link
831
+ * ValidationMode}): typing gates on it exactly like a mounted
832
+ * `useField`. Defaults to the form's `mode`.
833
+ */
834
+ mode?: ValidationMode;
835
+ /**
836
+ * Unmount behavior: `true` (the default, this library's historical
837
+ * default) tombstones the path when the element unmounts, `false` keeps
838
+ * the value. Falls back to the form-level
839
+ * `createForm({shouldUnregister})` when omitted.
840
+ */
841
+ shouldUnregister?: boolean;
842
+ /**
843
+ * DOM event → value extractor for the returned `onChange`. Defaults to
844
+ * the element's own protocol: `target.files` for file inputs,
845
+ * `target.checked` for checkboxes, `target.valueAsNumber` /
846
+ * `target.valueAsDate` under those flags, `target.value` otherwise —
847
+ * the same extraction `<Field>` performs.
848
+ */
849
+ eventToValue?: (e: any) => any;
850
+ /** Store `e.target.valueAsNumber` instead of the string value
851
+ * (`<input type="number">`, RHF's `register({valueAsNumber})`). An
852
+ * explicit `eventToValue` takes precedence. */
853
+ valueAsNumber?: boolean;
854
+ /** Store `e.target.valueAsDate` instead of the string value (RHF's
855
+ * `register({valueAsDate})`). An explicit `eventToValue` takes
856
+ * precedence; combining with `valueAsNumber` is a TypeError. */
857
+ valueAsDate?: boolean;
858
+ /**
859
+ * Declarative rules for this binding — the same {@link FieldRules}
860
+ * `useField`/`<Field>` take (`required` runs as the synchronous gate,
861
+ * `validate` callbacks included). Wired through the framework-free
862
+ * `registerValidatorByPath`, so `trigger`/submit/`mode` gating see it
863
+ * exactly like a hook-registered validator. `validateDebounce` is
864
+ * fixed at 0 — the rules run immediately on every kick.
865
+ */
866
+ rules?: FieldRules;
867
+ };
868
+ /**
869
+ * What {@link Form.register} returns: spread these props onto an
870
+ * uncontrolled DOM element (`<input {...form.register('name')} />`).
871
+ * The bound element never re-renders — read live state through
872
+ * `useValue`/`useError`/`getValues`, exactly like react-hook-form's
873
+ * `register` contract. `name` is the store's path key; `onChange` writes
874
+ * the extracted value through the gated user-change pipeline; `ref`
875
+ * attaches the element (seeds its initial DOM content into the store,
876
+ * wires the 'focusError' channel and bulk-reset DOM sync) and detaches it
877
+ * on unmount (tombstone unless `shouldUnregister: false`).
878
+ */
879
+ type RegisterProps = {
880
+ name: string;
881
+ onChange: (e: any) => void;
882
+ onBlur: () => void;
883
+ ref: (el: any) => void;
884
+ };
885
+ /** Structured form-level validate result: `errors` uses the same nested
886
+ * shape a plain error record uses, `values` is the schema's parsed output
887
+ * (coerce/transform results included). Either side may be omitted.
888
+ *
889
+ * The brand constant itself lives in the errors module (the leaf module of
890
+ * the core dependency graph — every consumer imports it from there) and is
891
+ * re-exported below with `export *`. */
892
+ type ValidationOutcome<T> = {
893
+ [VALIDATION_OUTCOME]: true;
894
+ errors?: Record<string, any>;
895
+ values?: T;
896
+ };
897
+ /** What a form-level validate function may return: a plain nested error
898
+ * record (flattened into field errors — the long-standing shape), or a
899
+ * branded {@link ValidationOutcome} whose `values` become the form's
900
+ * parsedValues baseline. */
901
+ type ValidateResult<T> = Record<string, any> | ValidationOutcome<T> | Promise<Record<string, any> | ValidationOutcome<T>>;
902
+ /** Context passed to a form-level `validate` function's second argument.
903
+ * `signal` aborts as soon as the round is superseded — a newer round
904
+ * started (which only happens under a positive `validateDebounce`, where
905
+ * kicks merge into windows) — so async validators can cancel their
906
+ * underlying work instead of racing a stale result home. Stale results
907
+ * are dropped independently by the round gate, so validators that ignore
908
+ * the signal stay correct too; the same contract field-level validators
909
+ * get through their own `meta`. */
910
+ type FormValidateMeta<T extends Record<string, any> = any> = {
911
+ form: Form<T>;
912
+ signal: AbortSignal;
913
+ };
914
+ /** Form-level validator: receives all values (plus {@link
915
+ * FormValidateMeta} as an optional second argument) and returns a
916
+ * {@link ValidateResult} — sync or async — or `undefined`/nothing when
917
+ * valid (the runtime skips falsy results, so implicit-return callbacks
918
+ * type-check). */
919
+ type FormValidateFn<T extends Record<string, any> = any> = (values: T, meta: FormValidateMeta<T>) => ValidateResult<T> | undefined;
920
+ /**
921
+ * The emitter event table for {@link Form.emitter}: each event's payload
922
+ * tuple. Path-carrying events declare an optional single `Path` payload —
923
+ * emit sites send it for single-field mutations and omit it for bulk
924
+ * payload-less broadcasts (reset, setInitialValues, clear-all), both of
925
+ * which subscribers handle. `focusError` carries the target's path key
926
+ * plus optional {@link SetFocusOptions}.
927
+ */
928
+ type FormEvents = ['change', [path?: Path]] | ['errors', [path?: Path]] | ['touched', [path?: Path]] | ['validating', [path?: Path]] | ['submitting', []] | ['submitCount', []] | ['submitSuccessful', []] | ['reset', []] | ['disabled', [path?: Path]] | ['status', []] | ['loading', []] | ['focusError', [key: string, options?: SetFocusOptions]];
929
+ type Form<T extends Record<string, any> = any> = {
930
+ emitter: EventEmitter<FormEvents>;
931
+ mode: ValidationMode;
932
+ reValidateMode: ReValidateMode;
933
+ initialValues: T;
934
+ values: Map<string, any>;
935
+ /** Tombstones of unregistered field paths (JSON path keys): reading or
936
+ * merging values must not fall back to initialValues for these paths. */
937
+ deleted: Set<string>;
938
+ /** Every error registered for a field, as a non-empty array (the
939
+ * write-side {@link setErrorByPath} normalizes to this invariant, so
940
+ * readers never need to guard against an empty list). Readers wanting
941
+ * the display error take the first entry ({@link getError}); readers
942
+ * wanting all of them use {@link getFieldErrors}. */
943
+ errors: Map<string, FieldError[]>;
944
+ touched: Set<string>;
945
+ /** Per-field validation kicks, registered by {@link
946
+ * registerValidatorByPath} (`useValidate` is the React-side
947
+ * registration): each is the field's debounce/lock-aware kick —
948
+ * invoking it validates the field's current value. `trigger` /
949
+ * `ensureValidate` run every entry; the user-change gate ({@link
950
+ * userChangeByPath}) runs the entry at the changed path. */
951
+ validators: Map<string, () => void>;
952
+ validating: Set<string>;
953
+ /** Parsed values from the last successful schema validation: the
954
+ * schema's complete output tree (coerced/transformed values included).
955
+ * Sits between initialValues and the values Map in {@link getValues}
956
+ * until `reset`/`setInitialValues` clears it. Never affects dirty
957
+ * state — that compares live edits against initialValues only. */
958
+ parsedValues: T | undefined;
959
+ /** Form-level validator, seeded from {@link Options.validate}. May
960
+ * receive a second {@link FormValidateMeta} argument. */
961
+ validate?: FormValidateFn<T>;
962
+ /** Delay in milliseconds before the form-level `validate` runs; seeded
963
+ * from {@link Options.validateDebounce} and fixed at create time. */
964
+ validateDebounce?: number;
965
+ /** Path keys (JSON-stringified segments) of the fields whose user
966
+ * changes re-run the form-level `validate`; normalized from {@link
967
+ * Options.validateDeps} at create time and fixed thereafter. */
968
+ validateDeps?: ReadonlySet<string>;
969
+ /** When the form-level `validate` re-runs outside submit/trigger —
970
+ * the cadence declared by {@link Options.validateMode}, seeded at
971
+ * create time and fixed thereafter ('onSubmit' by default; with
972
+ * 'onChange'/'onBlur' every user change/blur to a bound field re-runs
973
+ * it, no dep list required). */
974
+ validateMode: FormValidateMode;
975
+ isSubmitting: boolean;
976
+ /** Whether a submit has been attempted — set by `handleSubmit` on every
977
+ * attempt (validation outcome aside), cleared by `reset`.
978
+ * `useFormState().isSubmitted` reads it (react-hook-form's
979
+ * `formState.isSubmitted` semantics). */
980
+ isSubmitted: boolean;
981
+ submitCount: number;
982
+ isSubmitSuccessful: boolean | undefined;
983
+ /** True while an async {@link Options.initialValues} source (a Promise,
984
+ * or a thunk returning one) is still pending — the form starts empty
985
+ * and the resolved values become the baseline via setInitialValues when
986
+ * it lands. Flips through the payload-less 'loading' event
987
+ * (`useIsLoading` / `useFormState().isLoading`). */
988
+ isLoading: boolean;
989
+ /** Form-level default for a bound field's unmount behavior, seeded from
990
+ * {@link Options.shouldUnregister}: `true` (the default) tombstones an
991
+ * unmounted field, `false` keeps its value (react-hook-form's
992
+ * `shouldUnregister` semantics). A field's own `shouldUnregister` option
993
+ * overrides this. */
994
+ shouldUnregister?: boolean;
995
+ /** Form-level disabled flag, OR-ed into every bound field's `disabled`
996
+ * (form flag || the field's own option). Seeded from
997
+ * {@link Options}.disabled at create time and toggled at runtime with
998
+ * {@link setDisabled}, which emits a payload-less 'disabled' event so
999
+ * subscribed fields re-render. */
1000
+ disabled: boolean;
1001
+ /** Form-level default for mount validation, seeded from
1002
+ * {@link Options.validateOnMount}: `true` makes every mounted field
1003
+ * with a validator kick once after mount (deferred until an async
1004
+ * {@link Options.initialValues} source lands), and makes `useForm` run
1005
+ * the form-level `validate` once. A field's own `validateOnMount`
1006
+ * option overrides this flag in either direction. */
1007
+ validateOnMount: boolean;
1008
+ /** Form-level default for {@link UseValidateOptions.asyncAlways}:
1009
+ * whether a field's debounced validator still runs when its `required`
1010
+ * gate failed. A field's own `asyncAlways` option overrides this flag
1011
+ * in either direction. Seeded from {@link Options.asyncAlways}. */
1012
+ asyncAlways: boolean;
1013
+ /**
1014
+ * Whether native constraint validation gates submission (the submitted
1015
+ * element's checkValidity, skipped for targets without it — React
1016
+ * Native, toolbar buttons) and skips a bound `<Field>`'s custom
1017
+ * validators on a native-failing kick. Seeded from
1018
+ * {@link Options.shouldUseNativeValidation} — default `true`; a submit
1019
+ * may override per attempt via
1020
+ * {@link HandleSubmitOptions.shouldUseNativeValidation}.
1021
+ */
1022
+ shouldUseNativeValidation: boolean;
1023
+ /**
1024
+ * User-owned metadata slot for non-field state — session flags, server
1025
+ * backfill that belongs to no field, step indices (Formik's `status`
1026
+ * role). Written with {@link setStatus}, which emits the payload-less
1027
+ * 'status' event; read directly or reactively through {@link useStatus}.
1028
+ * Starts `undefined`.
1029
+ */
1030
+ status: any;
1031
+ /**
1032
+ * Non-hook field binding — react-hook-form's `register` contract:
1033
+ * spread the returned props onto an uncontrolled DOM element
1034
+ * (`<input {...form.register('name')} />`) and the element never
1035
+ * re-renders, while the store carries every write and `trigger`/submit
1036
+ * validate it. Seeding, the 'focusError' channel, bulk-reset DOM sync
1037
+ * and unmount tombstoning ride the `ref` callback's attach/detach —
1038
+ * no React state involved, so `register` works anywhere (dynamic
1039
+ * lists, conditional fields, non-React adapters). See {@link
1040
+ * RegisterOptions} / {@link RegisterProps}.
1041
+ */
1042
+ register: (name: Name, options?: RegisterOptions) => RegisterProps;
1043
+ };
1044
+ type Options<T extends Record<string, any> = any> = {
1045
+ /**
1046
+ * The values baseline. Sync objects seed immediately (SSR renders
1047
+ * them). Async sources — a Promise, or a thunk returning a value or
1048
+ * Promise (react-hook-form's async `defaultValues` shape) — start the
1049
+ * form empty with `isLoading: true` and land the resolved values as
1050
+ * the baseline via setInitialValues once they resolve: value
1051
+ * subscribers re-sync, dirty/touched state starts clean, and a later
1052
+ * `reset()` returns to the resolved baseline. A rejected source flips
1053
+ * isLoading back to false, keeps the form empty, and logs the error in
1054
+ * DEV — attach a `.catch` on the source itself to handle it. The thunk
1055
+ * runs at create time: keep its identity stable (module scope or
1056
+ * useMemo) when passing it inline, and note StrictMode double-invokes
1057
+ * it in development, like every render-phase call.
1058
+ */
1059
+ initialValues?: T | Promise<T> | (() => T | Promise<T>);
1060
+ /** When fields are validated. Defaults to `'onSubmit'`. See
1061
+ * {@link ValidationMode}. */
1062
+ mode?: ValidationMode;
1063
+ /** When a field is re-validated after it already has an error — it only
1064
+ * takes effect once the field has an error. Defaults to `'onChange'`. See
1065
+ * {@link ReValidateMode}. */
1066
+ reValidateMode?: ReValidateMode;
1067
+ /**
1068
+ * Form-level validator. Returns a record of errors keyed by field path;
1069
+ * nested objects are flattened ('a.b' style) and array values contribute
1070
+ * every non-empty string they hold as separate errors (zod `flatten()`
1071
+ * formErrors style). Schema adapters instead return a branded
1072
+ * {@link ValidationOutcome}: `errors` flattens the same way, `values`
1073
+ * (the schema's parsed output) becomes the form's parsedValues baseline
1074
+ * that {@link getValues} layers over initialValues.
1075
+ *
1076
+ * Alternatively pass a Standard Schema v1 object directly (zod
1077
+ * v3.24+/v4, valibot v1, arktype, …) — it is wrapped into a form-level
1078
+ * validator automatically, no resolver import needed, and `TValues`
1079
+ * infers from the schema's output type:
1080
+ * `createForm({validate: schema})` → `Form<InferSchemaValues<typeof
1081
+ * schema>>`.
1082
+ */
1083
+ validate?: FormValidateFn<T> | StandardSchemaV1<unknown, T>;
1084
+ /**
1085
+ * Milliseconds to debounce the form-level `validate`: kicks from
1086
+ * `trigger`/`ensureValidate`/submit inside the window merge into one
1087
+ * run, and while the timer is pending the form counts as validating,
1088
+ * so `trigger` and submit wait the window out — the same contract the
1089
+ * per-field `validateDebounce` gives field validators. The merged run
1090
+ * reads the values current when its timer fires. Defaults to `0`
1091
+ * (validate runs immediately, exactly as before this option existed).
1092
+ */
1093
+ validateDebounce?: number;
1094
+ /** Fields whose user changes re-run the form-level `validate` — the
1095
+ * cross-field dependency list (password-confirm mismatch and friends).
1096
+ * Each entry is a field path ('password', 'user.email', 'items.0.qty');
1097
+ * a user change to a listed field re-runs the form-level `validate`
1098
+ * under the same mode/`reValidateMode` gating the field's own
1099
+ * validator gets. Omit it and the form-level `validate` only runs on
1100
+ * `trigger`/submit, exactly as before this option existed.
1101
+ *
1102
+ * Opting in also changes what a re-run may clear: each round first
1103
+ * drops the errors the previous round wrote (paths it flattened onto),
1104
+ * so a dep change that fixes the cross-field error makes it disappear.
1105
+ * Errors the round never wrote — field validators', `setServerErrors`,
1106
+ * manual `setError` — are never touched. TanStack Form's counterpart is
1107
+ * `onChangeListenTo` (v1) / validator `triggers` (v2 alpha). */
1108
+ validateDeps?: FieldPath<T>[];
1109
+ /**
1110
+ * When the form-level `validate` re-runs outside submit/`trigger`/
1111
+ * `validateOnMount` — a cadence declaration instead of enumerating
1112
+ * {@link Options.validateDeps}. `'onSubmit'` (the default) keeps the
1113
+ * historical behavior (submit/trigger only, deps for cross-field
1114
+ * linkage). `'onChange'` re-runs the form-level validate on every user
1115
+ * change to a bound field; `'onBlur'` on every user blur. The re-run
1116
+ * rides the changed field's own user-change pipeline (typing and
1117
+ * `changeValue` alike, never programmatic `setValue`), honors
1118
+ * {@link Options.validateDebounce}, and clears what the previous round
1119
+ * wrote — TanStack Form's `validators.onChange`/`validators.onBlur`
1120
+ * counterpart. See {@link FormValidateMode}.
1121
+ */
1122
+ validateMode?: FormValidateMode;
1123
+ /**
1124
+ * Form-level default for a bound field's unmount behavior. `true` (the
1125
+ * default) tombstones an unmounted field — it drops out of
1126
+ * `getValues()` instead of reviving its initial value (this library's
1127
+ * historical default); `false` keeps the value, matching
1128
+ * react-hook-form's `shouldUnregister`. A field's own
1129
+ * `useField({shouldUnregister})` option overrides the form-level flag
1130
+ * in either direction.
1131
+ */
1132
+ shouldUnregister?: boolean;
1133
+ /** Start the form with every bound field disabled — the flag bound
1134
+ * fields OR with their own `disabled` option (a field cannot opt out
1135
+ * of a disabled form). Toggle later with {@link setDisabled}.
1136
+ * Defaults to `false`. */
1137
+ disabled?: boolean;
1138
+ /**
1139
+ * Form-level default for field validation's `asyncAlways`: when true,
1140
+ * a field whose `required` gate failed still runs its debounced
1141
+ * validator (the gate's errors land immediately, the validator's own
1142
+ * result lands alongside them per-source). TanStack Form's
1143
+ * `asyncAlways` counterpart. A field's own
1144
+ * `useField({asyncAlways})` option overrides the form-level flag in
1145
+ * either direction. Defaults to `false`.
1146
+ */
1147
+ asyncAlways?: boolean;
1148
+ /**
1149
+ * Whether native constraint validation gates submission and skips a
1150
+ * bound `<Field>`'s custom validators when its native constraints fail
1151
+ * that kick (react-hook-form's `shouldUseNativeValidation`): pass
1152
+ * `false` for forms where custom validators are the only source of
1153
+ * truth — the browser's checkValidity/reportValidity gate (and the
1154
+ * per-kick native gate in `<Field>`) stop running, while declarative
1155
+ * `rules` keep producing store-side errors and native constraint
1156
+ * attributes keep rendering for a11y. Defaults to `true`. Fixed at
1157
+ * create time; a single submit may override it through
1158
+ * {@link HandleSubmitOptions.shouldUseNativeValidation}.
1159
+ */
1160
+ shouldUseNativeValidation?: boolean;
1161
+ /**
1162
+ * Validate on mount: `true` makes every mounted field with a validator
1163
+ * (declarative `rules` or a `validate` callback) run it once after
1164
+ * mount, instead of waiting for the first submit/change — errors show
1165
+ * immediately for an untouched form (Formik's `validateOnMount` /
1166
+ * TanStack Form's per-field `validateOnMount`). The form-level
1167
+ * `validate` also runs once after mount. Mount kicks are deferred
1168
+ * while an async `initialValues` source is still pending: validating
1169
+ * the empty shell would land spurious required errors, so the kicks
1170
+ * fire after the resolved baseline lands instead. A field's own
1171
+ * `useField({validateOnMount})` option overrides the form-level flag
1172
+ * in either direction. Defaults to `false`.
1173
+ */
1174
+ validateOnMount?: boolean;
1175
+ };
1176
+ /**
1177
+ * Create form instance
1178
+ * @param options
1179
+ * @return form instance
1180
+ */
1181
+ declare function create<T extends Record<string, any> = any>(options?: Options<T>): Form<T>;
1182
+
1183
+ export { registerFieldValidateDeps as $, clearErrors as C, clearServerErrors as E, create as G, dottedKeyToFieldPath as H, ensureValidate as J, fieldPathToDottedKey as K, getError as L, getErrorByPath as M, getErrors as Q, getErrorsRecord as U, getErrorsTree as W, getFieldErrors as X, getFieldErrorsByPath as Y, getFirstError as Z, hasErrors as _, registerValidatorByPath as a0, revalidateDependentsOnChange as a1, revalidateFormOnChange as a2, runFormValidate as a3, setError as a4, setErrorByPath as a5, setFocus as a6, setServerErrors as a7, setValidatingByPath as a8, trigger as a9, unregisterFieldValidateDeps as aa, unsetValidatingByPath as ab, validate as ac, hasStandardProps as ae, schemaToFormValidator as af, schemaToFieldValidator as ag, VALIDATION_OUTCOME as b, FORM_ERROR as n };
1184
+ export type { ArrayItemOf as A, ValidatorRegistration as B, DottedPath as D, Form as F, InferSchemaValues as I, Name as N, Options as O, PathSegments as P, RegisterOptions as R, StandardSchemaV1 as S, TriggerOptions as T, Validator as V, FieldErrorEntry as a, StandardSchemaIssue as ad, ValidationOutcome as c, FieldPath as d, Path as e, FieldError as f, PathValueOf as g, ValidationMode as h, RegisterProps as i, FieldRules as j, FieldErrors as k, FieldErrorsTree as l, FormEvents as m, FormValidateFn as o, FormValidateMeta as p, FormValidateMode as q, FormValidationError as r, OpaqueTypes as s, PathValue as t, ReValidateMode as u, SetErrorOptions as v, SetFocusOptions as w, SetServerErrorsOptions as x, SyncValidator as y, ValidateResult as z };