react-f0rm 1.1.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +242 -39
- package/dist/devtools/index.cjs.js +1 -1
- package/dist/devtools/index.cjs.js.map +1 -1
- package/dist/devtools/index.d.ts +2 -2
- package/dist/devtools/index.mjs +1 -1
- package/dist/devtools/index.mjs.map +1 -1
- package/dist/errors-BKrUdpfI.cjs.js +2 -0
- package/dist/errors-BKrUdpfI.cjs.js.map +1 -0
- package/dist/errors-CrQBddrJ.mjs +2 -0
- package/dist/errors-CrQBddrJ.mjs.map +1 -0
- package/dist/form-CeKSBs31.d.ts +486 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +1097 -118
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +1082 -544
- package/dist/index.umd.js.map +1 -1
- package/dist/index.umd.min.js +2 -2
- package/dist/index.umd.min.js.map +1 -1
- package/dist/persist.cjs.js +2 -0
- package/dist/persist.cjs.js.map +1 -0
- package/dist/persist.d.ts +49 -0
- package/dist/persist.mjs +2 -0
- package/dist/persist.mjs.map +1 -0
- package/dist/resolvers/standard-schema.cjs.js +1 -1
- package/dist/resolvers/standard-schema.cjs.js.map +1 -1
- package/dist/resolvers/standard-schema.d.ts +7 -5
- package/dist/resolvers/standard-schema.mjs +1 -1
- package/dist/resolvers/standard-schema.mjs.map +1 -1
- package/dist/resolvers/yup.cjs.js +1 -1
- package/dist/resolvers/yup.cjs.js.map +1 -1
- package/dist/resolvers/yup.d.ts +1 -1
- package/dist/resolvers/yup.mjs +1 -1
- package/dist/resolvers/yup.mjs.map +1 -1
- package/dist/resolvers/zod.cjs.js +1 -1
- package/dist/resolvers/zod.cjs.js.map +1 -1
- package/dist/resolvers/zod.d.ts +1 -1
- package/dist/resolvers/zod.mjs +1 -1
- package/dist/resolvers/zod.mjs.map +1 -1
- package/dist/server/index.cjs.js +2 -0
- package/dist/server/index.cjs.js.map +1 -0
- package/dist/server/index.d.ts +77 -0
- package/dist/server/index.mjs +2 -0
- package/dist/server/index.mjs.map +1 -0
- package/dist/validate-CNtuUhmk.mjs +2 -0
- package/dist/validate-CNtuUhmk.mjs.map +1 -0
- package/dist/validate-Cl4ksNFu.cjs.js +2 -0
- package/dist/validate-Cl4ksNFu.cjs.js.map +1 -0
- package/dist/validate-nksgv1pR.d.ts +272 -0
- package/dist/values-Cu6awQOJ.cjs.js +2 -0
- package/dist/values-Cu6awQOJ.cjs.js.map +1 -0
- package/dist/values-DRY-a32G.mjs +2 -0
- package/dist/values-DRY-a32G.mjs.map +1 -0
- package/package.json +87 -29
- package/dist/form-2_tBkEXU.mjs +0 -2
- package/dist/form-2_tBkEXU.mjs.map +0 -1
- package/dist/form-BiDaJLjD.d.ts +0 -826
- package/dist/form-BwLNQ6WB.cjs.js +0 -2
- package/dist/form-BwLNQ6WB.cjs.js.map +0 -1
- package/dist/validate-2XUilILy.d.ts +0 -22
|
@@ -0,0 +1,486 @@
|
|
|
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
|
+
/** Options accepted by {@link setFocus}. All flags default to `false`. */
|
|
61
|
+
type SetFocusOptions = {
|
|
62
|
+
/** Select the field's text after focusing it. Bound fields call
|
|
63
|
+
* `select()` on their element; elements without one (custom `as`
|
|
64
|
+
* components) just focus. */
|
|
65
|
+
shouldSelect?: boolean;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Programmatically focus a bound field's element (e.g. the <Field>'s
|
|
69
|
+
* input).
|
|
70
|
+
*
|
|
71
|
+
* Rides the same 'focusError' event channel a failed handleSubmit uses to
|
|
72
|
+
* focus the first errored field: the payload is the target's path key,
|
|
73
|
+
* with the focus options as a second, backward-compatible argument (older
|
|
74
|
+
* subscribers declared with a single `key` parameter simply ignore it).
|
|
75
|
+
* Being event-driven, it is a silent no-op when the field is unmounted or
|
|
76
|
+
* nothing subscribes — unknown names never throw.
|
|
77
|
+
*
|
|
78
|
+
* @param form form instance
|
|
79
|
+
* @param name field name (dot path or segments path)
|
|
80
|
+
* @param options focus options
|
|
81
|
+
*/
|
|
82
|
+
declare function setFocus(form: Form, name: Name, options?: SetFocusOptions): void;
|
|
83
|
+
|
|
84
|
+
/** Reserved top-level path segment for form-level errors. The Standard
|
|
85
|
+
* Schema form-level adapter lands path-less issues under this key; the
|
|
86
|
+
* exported constant replaces the magic string, and readers consume it via
|
|
87
|
+
* getError(form, FORM_ERROR) / getFieldErrors(form, FORM_ERROR). */
|
|
88
|
+
declare const FORM_ERROR = "_form";
|
|
89
|
+
/** When a field is validated:
|
|
90
|
+
* - `'onSubmit'` (default): only on submit
|
|
91
|
+
* - `'onBlur'`: when the field loses focus
|
|
92
|
+
* - `'onChange'`: on every change
|
|
93
|
+
* - `'onTouched'`: on first blur, then on every change
|
|
94
|
+
* - `'all'`: on both change and blur
|
|
95
|
+
*/
|
|
96
|
+
/** Brand marking a form-level validate result as a structured
|
|
97
|
+
* {@link ValidationOutcome} (parsed values and/or errors) rather than a
|
|
98
|
+
* plain nested error record. Symbols cannot collide with user error
|
|
99
|
+
* records, so detection is an exact `VALIDATION_OUTCOME in result`. */
|
|
100
|
+
declare const VALIDATION_OUTCOME: unique symbol;
|
|
101
|
+
/** Structured form-level validate result: `errors` uses the same nested
|
|
102
|
+
* shape a plain error record uses, `values` is the schema's parsed output
|
|
103
|
+
* (coerce/transform results included). Either side may be omitted. */
|
|
104
|
+
/**
|
|
105
|
+
* Get field error
|
|
106
|
+
* @param form
|
|
107
|
+
* @param name
|
|
108
|
+
* @return FieldError object or undefined
|
|
109
|
+
*/
|
|
110
|
+
declare function getError<T extends Record<string, any> = any, P extends FieldPath<T> | PathSegments = FieldPath<T> | PathSegments>(form: Form<T>, name: P): FieldError | undefined;
|
|
111
|
+
/**
|
|
112
|
+
* Get field error by path
|
|
113
|
+
* @param form
|
|
114
|
+
* @param path
|
|
115
|
+
* @return first FieldError of the field, or undefined
|
|
116
|
+
*/
|
|
117
|
+
declare function getErrorByPath({ errors }: Form, path: Path): FieldError | undefined;
|
|
118
|
+
/**
|
|
119
|
+
* Get all errors of a field
|
|
120
|
+
* @param form
|
|
121
|
+
* @param name
|
|
122
|
+
* @return every error registered for the field (insertion order); an empty
|
|
123
|
+
* array when the field has none
|
|
124
|
+
*/
|
|
125
|
+
declare function getFieldErrors<T extends Record<string, any> = any, P extends FieldPath<T> | PathSegments = FieldPath<T> | PathSegments>(form: Form<T>, name: P): FieldError[];
|
|
126
|
+
/**
|
|
127
|
+
* Get all errors of a field by path
|
|
128
|
+
* @param form
|
|
129
|
+
* @param path
|
|
130
|
+
* @return every error registered for the field (insertion order); an empty
|
|
131
|
+
* array when the field has none
|
|
132
|
+
*/
|
|
133
|
+
declare function getFieldErrorsByPath({ errors }: Form, path: Path): FieldError[];
|
|
134
|
+
/**
|
|
135
|
+
* Get all errors
|
|
136
|
+
* @param form
|
|
137
|
+
* @return array of {path, type, message} entries, in insertion order; path
|
|
138
|
+
* is the user-facing dotted field path ('a.b', 'list.0'), and a
|
|
139
|
+
* field holding several errors contributes one entry per error
|
|
140
|
+
*/
|
|
141
|
+
declare function getErrors({ errors }: Form): FieldErrorEntry[];
|
|
142
|
+
/**
|
|
143
|
+
* Get first error message
|
|
144
|
+
* @param form
|
|
145
|
+
* @return first error's message string, or undefined when there are no errors
|
|
146
|
+
*/
|
|
147
|
+
declare function getFirstError({ errors }: Form): string | undefined;
|
|
148
|
+
/** Snapshot of one field's aggregated state, as {@link getFieldState}
|
|
149
|
+
* returns it. `errors` is the stored array shared with the form — treat it
|
|
150
|
+
* as read-only, like every {@link getFieldErrors} result. */
|
|
151
|
+
/** Options accepted by {@link setError}. */
|
|
152
|
+
type SetErrorOptions = {
|
|
153
|
+
/**
|
|
154
|
+
* Focus the named field's element after the error lands (react-hook-form's
|
|
155
|
+
* `setError` `shouldFocus`). Rides the same 'focusError' channel
|
|
156
|
+
* `setFocus` and a failed submit's auto-focus use: only mounted bound
|
|
157
|
+
* fields react, unmounted ones are silent no-ops.
|
|
158
|
+
*/
|
|
159
|
+
shouldFocus?: boolean;
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* Set field error
|
|
163
|
+
* @param form
|
|
164
|
+
* @param name
|
|
165
|
+
* @param error string is normalized to {type: 'custom', message}; a
|
|
166
|
+
* FieldError object is stored as-is; an array holds several errors
|
|
167
|
+
* (falsy items dropped, strings normalized); undefined clears
|
|
168
|
+
* @param options {@link SetErrorOptions} — `shouldFocus` focuses the field
|
|
169
|
+
* after the error lands
|
|
170
|
+
*/
|
|
171
|
+
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;
|
|
172
|
+
/**
|
|
173
|
+
* Set field error
|
|
174
|
+
* @param form
|
|
175
|
+
* @param path
|
|
176
|
+
* @param error string is normalized to {type: 'custom', message}; a
|
|
177
|
+
* FieldError object is stored as-is; an array holds several errors
|
|
178
|
+
* (falsy items dropped, strings normalized); undefined clears
|
|
179
|
+
* @param options {@link SetErrorOptions} — `shouldFocus` focuses the field
|
|
180
|
+
* after the error lands
|
|
181
|
+
*/
|
|
182
|
+
declare function setErrorByPath(form: Form, path: Path, error: string | FieldError | (string | FieldError)[] | undefined, options?: SetErrorOptions): void;
|
|
183
|
+
/**
|
|
184
|
+
* Clear errors
|
|
185
|
+
* @param form
|
|
186
|
+
* @param name a single path or a list of paths; omit to clear every error
|
|
187
|
+
*/
|
|
188
|
+
declare function clearErrors(form: Form, name?: Name | Name[]): void;
|
|
189
|
+
/** Options accepted by {@link setServerErrors}. */
|
|
190
|
+
type SetServerErrorsOptions = {
|
|
191
|
+
/** Keep existing field errors instead of clearing them first. Defaults
|
|
192
|
+
* to `false`: a fresh server response replaces the prior error state. */
|
|
193
|
+
keepExisting?: boolean;
|
|
194
|
+
};
|
|
195
|
+
/**
|
|
196
|
+
* Land a server-side error response on the form: each entry becomes the
|
|
197
|
+
* named field's error(s) with `type: 'server'`, ready for the same
|
|
198
|
+
* renderError/`useError` channel client-side validation uses. Takes the
|
|
199
|
+
* flat `Record<string, string | string[]>` shape REST APIs commonly
|
|
200
|
+
* return (RealWorld: `422 {errors: {email: ['has already been taken']}}`)
|
|
201
|
+
* without a hand-rolled `Object.entries` + `setError` loop.
|
|
202
|
+
*
|
|
203
|
+
* A string value lands as one error, a string array as several (first one
|
|
204
|
+
* is what `getError`/`error` expose); an empty array clears that field's
|
|
205
|
+
* errors. By default every existing error is cleared first — a fresh
|
|
206
|
+
* response describes the current state, not a patch onto stale client
|
|
207
|
+
* errors; pass `keepExisting: true` to layer instead.
|
|
208
|
+
* @param form
|
|
209
|
+
* @param errors field errors keyed by name
|
|
210
|
+
* @param options
|
|
211
|
+
*/
|
|
212
|
+
declare function setServerErrors(form: Form, errors: Record<string, string | string[]>, options?: SetServerErrorsOptions): void;
|
|
213
|
+
/**
|
|
214
|
+
* Set field touched state
|
|
215
|
+
* @param form
|
|
216
|
+
* @param name
|
|
217
|
+
*/
|
|
218
|
+
/**
|
|
219
|
+
* @param form
|
|
220
|
+
*/
|
|
221
|
+
declare function hasErrors({ errors }: Form): boolean;
|
|
222
|
+
|
|
223
|
+
/** A field error: `type` identifies the error kind ('custom' for plain
|
|
224
|
+
* string errors), `message` is the display text. */
|
|
225
|
+
type FieldError = {
|
|
226
|
+
type: string;
|
|
227
|
+
message: string;
|
|
228
|
+
};
|
|
229
|
+
/** A flattened entry from {@link getErrors}. */
|
|
230
|
+
type FieldErrorEntry = {
|
|
231
|
+
path: string;
|
|
232
|
+
type: string;
|
|
233
|
+
message: string;
|
|
234
|
+
};
|
|
235
|
+
/** When a field is validated:
|
|
236
|
+
* - `'onSubmit'` (default): only on submit
|
|
237
|
+
* - `'onBlur'`: when the field loses focus
|
|
238
|
+
* - `'onChange'`: on every change
|
|
239
|
+
* - `'onTouched'`: on first blur, then on every change
|
|
240
|
+
* - `'all'`: on both change and blur
|
|
241
|
+
*/
|
|
242
|
+
type ValidationMode = 'onSubmit' | 'onBlur' | 'onChange' | 'onTouched' | 'all';
|
|
243
|
+
/** When a field is re-validated after it already has an error:
|
|
244
|
+
* - `'onChange'` (default): on every change
|
|
245
|
+
* - `'onBlur'`: when the field loses focus
|
|
246
|
+
* - `'onSubmit'`: only on submit (no live re-validation)
|
|
247
|
+
*/
|
|
248
|
+
type ReValidateMode = 'onChange' | 'onBlur' | 'onSubmit';
|
|
249
|
+
/** Structured form-level validate result: `errors` uses the same nested
|
|
250
|
+
* shape a plain error record uses, `values` is the schema's parsed output
|
|
251
|
+
* (coerce/transform results included). Either side may be omitted.
|
|
252
|
+
*
|
|
253
|
+
* The brand constant itself lives in the errors module (the leaf module of
|
|
254
|
+
* the core dependency graph — every consumer imports it from there) and is
|
|
255
|
+
* re-exported below with `export *`. */
|
|
256
|
+
type ValidationOutcome<T> = {
|
|
257
|
+
[VALIDATION_OUTCOME]: true;
|
|
258
|
+
errors?: Record<string, any>;
|
|
259
|
+
values?: T;
|
|
260
|
+
};
|
|
261
|
+
/** What a form-level validate function may return: a plain nested error
|
|
262
|
+
* record (flattened into field errors — the long-standing shape), or a
|
|
263
|
+
* branded {@link ValidationOutcome} whose `values` become the form's
|
|
264
|
+
* parsedValues baseline. */
|
|
265
|
+
type ValidateResult<T> = Record<string, any> | ValidationOutcome<T> | Promise<Record<string, any> | ValidationOutcome<T>>;
|
|
266
|
+
/** Context passed to a form-level `validate` function's second argument.
|
|
267
|
+
* `signal` aborts as soon as the round is superseded — a newer round
|
|
268
|
+
* started (which only happens under a positive `validateDebounce`, where
|
|
269
|
+
* kicks merge into windows) — so async validators can cancel their
|
|
270
|
+
* underlying work instead of racing a stale result home. Stale results
|
|
271
|
+
* are dropped independently by the round gate, so validators that ignore
|
|
272
|
+
* the signal stay correct too; the same contract field-level validators
|
|
273
|
+
* get through their own `meta`. */
|
|
274
|
+
type FormValidateMeta<T extends Record<string, any> = any> = {
|
|
275
|
+
form: Form<T>;
|
|
276
|
+
signal: AbortSignal;
|
|
277
|
+
};
|
|
278
|
+
/** Form-level validator: receives all values (plus {@link
|
|
279
|
+
* FormValidateMeta} as an optional second argument) and returns a
|
|
280
|
+
* {@link ValidateResult} — sync or async — or `undefined`/nothing when
|
|
281
|
+
* valid (the runtime skips falsy results, so implicit-return callbacks
|
|
282
|
+
* type-check). */
|
|
283
|
+
type FormValidateFn<T extends Record<string, any> = any> = (values: T, meta: FormValidateMeta<T>) => ValidateResult<T> | undefined;
|
|
284
|
+
/**
|
|
285
|
+
* The emitter event table for {@link Form.emitter}: each event's payload
|
|
286
|
+
* tuple. Path-carrying events declare an optional single `Path` payload —
|
|
287
|
+
* emit sites send it for single-field mutations and omit it for bulk
|
|
288
|
+
* payload-less broadcasts (reset, setInitialValues, clear-all), both of
|
|
289
|
+
* which subscribers handle. `focusError` carries the target's path key
|
|
290
|
+
* plus optional {@link SetFocusOptions}.
|
|
291
|
+
*/
|
|
292
|
+
type FormEvents = ['change', [path?: Path]] | ['errors', [path?: Path]] | ['touched', [path?: Path]] | ['validating', [path?: Path]] | ['submitting', []] | ['submitCount', []] | ['submitSuccessful', []] | ['reset', []] | ['disabled', []] | ['status', []] | ['loading', []] | ['focusError', [key: string, options?: SetFocusOptions]];
|
|
293
|
+
type Form<T extends Record<string, any> = any> = {
|
|
294
|
+
emitter: EventEmitter<FormEvents>;
|
|
295
|
+
mode: ValidationMode;
|
|
296
|
+
reValidateMode: ReValidateMode;
|
|
297
|
+
initialValues: T;
|
|
298
|
+
values: Map<string, any>;
|
|
299
|
+
/** Tombstones of unregistered field paths (JSON path keys): reading or
|
|
300
|
+
* merging values must not fall back to initialValues for these paths. */
|
|
301
|
+
deleted: Set<string>;
|
|
302
|
+
/** Every error registered for a field, as a non-empty array (the
|
|
303
|
+
* write-side {@link setErrorByPath} normalizes to this invariant, so
|
|
304
|
+
* readers never need to guard against an empty list). Readers wanting
|
|
305
|
+
* the display error take the first entry ({@link getError}); readers
|
|
306
|
+
* wanting all of them use {@link getFieldErrors}. */
|
|
307
|
+
errors: Map<string, FieldError[]>;
|
|
308
|
+
touched: Set<string>;
|
|
309
|
+
/** Per-field validation kicks, registered by {@link
|
|
310
|
+
* registerValidatorByPath} (`useValidate` is the React-side
|
|
311
|
+
* registration): each is the field's debounce/lock-aware kick —
|
|
312
|
+
* invoking it validates the field's current value. `trigger` /
|
|
313
|
+
* `ensureValidate` run every entry; the user-change gate ({@link
|
|
314
|
+
* userChangeByPath}) runs the entry at the changed path. */
|
|
315
|
+
validators: Map<string, () => void>;
|
|
316
|
+
validating: Set<string>;
|
|
317
|
+
/** Parsed values from the last successful schema validation: the
|
|
318
|
+
* schema's complete output tree (coerced/transformed values included).
|
|
319
|
+
* Sits between initialValues and the values Map in {@link getValues}
|
|
320
|
+
* until `reset`/`setInitialValues` clears it. Never affects dirty
|
|
321
|
+
* state — that compares live edits against initialValues only. */
|
|
322
|
+
parsedValues: T | undefined;
|
|
323
|
+
/** Form-level validator, seeded from {@link Options.validate}. May
|
|
324
|
+
* receive a second {@link FormValidateMeta} argument. */
|
|
325
|
+
validate?: FormValidateFn<T>;
|
|
326
|
+
/** Delay in milliseconds before the form-level `validate` runs; seeded
|
|
327
|
+
* from {@link Options.validateDebounce} and fixed at create time. */
|
|
328
|
+
validateDebounce?: number;
|
|
329
|
+
/** Path keys (JSON-stringified segments) of the fields whose user
|
|
330
|
+
* changes re-run the form-level `validate`; normalized from {@link
|
|
331
|
+
* Options.validateDeps} at create time and fixed thereafter. */
|
|
332
|
+
validateDeps?: ReadonlySet<string>;
|
|
333
|
+
isSubmitting: boolean;
|
|
334
|
+
/** Whether a submit has been attempted — set by `handleSubmit` on every
|
|
335
|
+
* attempt (validation outcome aside), cleared by `reset`.
|
|
336
|
+
* `useFormState().isSubmitted` reads it (react-hook-form's
|
|
337
|
+
* `formState.isSubmitted` semantics). */
|
|
338
|
+
isSubmitted: boolean;
|
|
339
|
+
submitCount: number;
|
|
340
|
+
isSubmitSuccessful: boolean | undefined;
|
|
341
|
+
/** True while an async {@link Options.initialValues} source (a Promise,
|
|
342
|
+
* or a thunk returning one) is still pending — the form starts empty
|
|
343
|
+
* and the resolved values become the baseline via setInitialValues when
|
|
344
|
+
* it lands. Flips through the payload-less 'loading' event
|
|
345
|
+
* (`useIsLoading` / `useFormState().isLoading`). */
|
|
346
|
+
isLoading: boolean;
|
|
347
|
+
/** Form-level default for a bound field's unmount behavior, seeded from
|
|
348
|
+
* {@link Options.shouldUnregister}: `true` (the default) tombstones an
|
|
349
|
+
* unmounted field, `false` keeps its value (react-hook-form's
|
|
350
|
+
* `shouldUnregister` semantics). A field's own `shouldUnregister` option
|
|
351
|
+
* overrides this. */
|
|
352
|
+
shouldUnregister?: boolean;
|
|
353
|
+
/** Form-level disabled flag, OR-ed into every bound field's `disabled`
|
|
354
|
+
* (form flag || the field's own option). Seeded from
|
|
355
|
+
* {@link Options}.disabled at create time and toggled at runtime with
|
|
356
|
+
* {@link setDisabled}, which emits a payload-less 'disabled' event so
|
|
357
|
+
* subscribed fields re-render. */
|
|
358
|
+
disabled: boolean;
|
|
359
|
+
/** Form-level default for mount validation, seeded from
|
|
360
|
+
* {@link Options.validateOnMount}: `true` makes every mounted field
|
|
361
|
+
* with a validator kick once after mount (deferred until an async
|
|
362
|
+
* {@link Options.initialValues} source lands), and makes `useForm` run
|
|
363
|
+
* the form-level `validate` once. A field's own `validateOnMount`
|
|
364
|
+
* option overrides this flag in either direction. */
|
|
365
|
+
validateOnMount: boolean;
|
|
366
|
+
/** Form-level default for {@link UseValidateOptions.asyncAlways}:
|
|
367
|
+
* whether a field's debounced validator still runs when its `required`
|
|
368
|
+
* gate failed. A field's own `asyncAlways` option overrides this flag
|
|
369
|
+
* in either direction. Seeded from {@link Options.asyncAlways}. */
|
|
370
|
+
asyncAlways: boolean;
|
|
371
|
+
/**
|
|
372
|
+
* User-owned metadata slot for non-field state — session flags, server
|
|
373
|
+
* backfill that belongs to no field, step indices (Formik's `status`
|
|
374
|
+
* role). Written with {@link setStatus}, which emits the payload-less
|
|
375
|
+
* 'status' event; read directly or reactively through {@link useStatus}.
|
|
376
|
+
* Starts `undefined`.
|
|
377
|
+
*/
|
|
378
|
+
status: any;
|
|
379
|
+
};
|
|
380
|
+
type Options<T extends Record<string, any> = any> = {
|
|
381
|
+
/**
|
|
382
|
+
* The values baseline. Sync objects seed immediately (SSR renders
|
|
383
|
+
* them). Async sources — a Promise, or a thunk returning a value or
|
|
384
|
+
* Promise (react-hook-form's async `defaultValues` shape) — start the
|
|
385
|
+
* form empty with `isLoading: true` and land the resolved values as
|
|
386
|
+
* the baseline via setInitialValues once they resolve: value
|
|
387
|
+
* subscribers re-sync, dirty/touched state starts clean, and a later
|
|
388
|
+
* `reset()` returns to the resolved baseline. A rejected source flips
|
|
389
|
+
* isLoading back to false, keeps the form empty, and logs the error in
|
|
390
|
+
* DEV — attach a `.catch` on the source itself to handle it. The thunk
|
|
391
|
+
* runs at create time: keep its identity stable (module scope or
|
|
392
|
+
* useMemo) when passing it inline, and note StrictMode double-invokes
|
|
393
|
+
* it in development, like every render-phase call.
|
|
394
|
+
*/
|
|
395
|
+
initialValues?: T | Promise<T> | (() => T | Promise<T>);
|
|
396
|
+
/** When fields are validated. Defaults to `'onSubmit'`. See
|
|
397
|
+
* {@link ValidationMode}. */
|
|
398
|
+
mode?: ValidationMode;
|
|
399
|
+
/** When a field is re-validated after it already has an error — it only
|
|
400
|
+
* takes effect once the field has an error. Defaults to `'onChange'`. See
|
|
401
|
+
* {@link ReValidateMode}. */
|
|
402
|
+
reValidateMode?: ReValidateMode;
|
|
403
|
+
/**
|
|
404
|
+
* Form-level validator. Returns a record of errors keyed by field path;
|
|
405
|
+
* nested objects are flattened ('a.b' style) and array values contribute
|
|
406
|
+
* every non-empty string they hold as separate errors (zod `flatten()`
|
|
407
|
+
* formErrors style). Schema adapters instead return a branded
|
|
408
|
+
* {@link ValidationOutcome}: `errors` flattens the same way, `values`
|
|
409
|
+
* (the schema's parsed output) becomes the form's parsedValues baseline
|
|
410
|
+
* that {@link getValues} layers over initialValues.
|
|
411
|
+
*/
|
|
412
|
+
validate?: FormValidateFn<T>;
|
|
413
|
+
/**
|
|
414
|
+
* Milliseconds to debounce the form-level `validate`: kicks from
|
|
415
|
+
* `trigger`/`ensureValidate`/submit inside the window merge into one
|
|
416
|
+
* run, and while the timer is pending the form counts as validating,
|
|
417
|
+
* so `trigger` and submit wait the window out — the same contract the
|
|
418
|
+
* per-field `validateDebounce` gives field validators. The merged run
|
|
419
|
+
* reads the values current when its timer fires. Defaults to `0`
|
|
420
|
+
* (validate runs immediately, exactly as before this option existed).
|
|
421
|
+
*/
|
|
422
|
+
validateDebounce?: number;
|
|
423
|
+
/** Fields whose user changes re-run the form-level `validate` — the
|
|
424
|
+
* cross-field dependency list (password-confirm mismatch and friends).
|
|
425
|
+
* Each entry is a field path ('password', 'user.email', 'items.0.qty');
|
|
426
|
+
* a user change to a listed field re-runs the form-level `validate`
|
|
427
|
+
* under the same mode/`reValidateMode` gating the field's own
|
|
428
|
+
* validator gets. Omit it and the form-level `validate` only runs on
|
|
429
|
+
* `trigger`/submit, exactly as before this option existed.
|
|
430
|
+
*
|
|
431
|
+
* Opting in also changes what a re-run may clear: each round first
|
|
432
|
+
* drops the errors the previous round wrote (paths it flattened onto),
|
|
433
|
+
* so a dep change that fixes the cross-field error makes it disappear.
|
|
434
|
+
* Errors the round never wrote — field validators', `setServerErrors`,
|
|
435
|
+
* manual `setError` — are never touched. TanStack Form's counterpart is
|
|
436
|
+
* `onChangeListenTo` (v1) / validator `triggers` (v2 alpha). */
|
|
437
|
+
validateDeps?: FieldPath<T>[];
|
|
438
|
+
/**
|
|
439
|
+
* Form-level default for a bound field's unmount behavior. `true` (the
|
|
440
|
+
* default) tombstones an unmounted field — it drops out of
|
|
441
|
+
* `getValues()` instead of reviving its initial value (this library's
|
|
442
|
+
* historical default); `false` keeps the value, matching
|
|
443
|
+
* react-hook-form's `shouldUnregister`. A field's own
|
|
444
|
+
* `useField({shouldUnregister})` option overrides the form-level flag
|
|
445
|
+
* in either direction.
|
|
446
|
+
*/
|
|
447
|
+
shouldUnregister?: boolean;
|
|
448
|
+
/** Start the form with every bound field disabled — the flag bound
|
|
449
|
+
* fields OR with their own `disabled` option (a field cannot opt out
|
|
450
|
+
* of a disabled form). Toggle later with {@link setDisabled}.
|
|
451
|
+
* Defaults to `false`. */
|
|
452
|
+
disabled?: boolean;
|
|
453
|
+
/**
|
|
454
|
+
* Form-level default for field validation's `asyncAlways`: when true,
|
|
455
|
+
* a field whose `required` gate failed still runs its debounced
|
|
456
|
+
* validator (the gate's errors land immediately, the validator's own
|
|
457
|
+
* result lands alongside them per-source). TanStack Form's
|
|
458
|
+
* `asyncAlways` counterpart. A field's own
|
|
459
|
+
* `useField({asyncAlways})` option overrides the form-level flag in
|
|
460
|
+
* either direction. Defaults to `false`.
|
|
461
|
+
*/
|
|
462
|
+
asyncAlways?: boolean;
|
|
463
|
+
/**
|
|
464
|
+
* Validate on mount: `true` makes every mounted field with a validator
|
|
465
|
+
* (declarative `rules` or a `validate` callback) run it once after
|
|
466
|
+
* mount, instead of waiting for the first submit/change — errors show
|
|
467
|
+
* immediately for an untouched form (Formik's `validateOnMount` /
|
|
468
|
+
* TanStack Form's per-field `validateOnMount`). The form-level
|
|
469
|
+
* `validate` also runs once after mount. Mount kicks are deferred
|
|
470
|
+
* while an async `initialValues` source is still pending: validating
|
|
471
|
+
* the empty shell would land spurious required errors, so the kicks
|
|
472
|
+
* fire after the resolved baseline lands instead. A field's own
|
|
473
|
+
* `useField({validateOnMount})` option overrides the form-level flag
|
|
474
|
+
* in either direction. Defaults to `false`.
|
|
475
|
+
*/
|
|
476
|
+
validateOnMount?: boolean;
|
|
477
|
+
};
|
|
478
|
+
/**
|
|
479
|
+
* Create form instance
|
|
480
|
+
* @param options
|
|
481
|
+
* @return form instance
|
|
482
|
+
*/
|
|
483
|
+
declare function create<T extends Record<string, any> = any>(options?: Options<T>): Form<T>;
|
|
484
|
+
|
|
485
|
+
export { setFocus as A, setServerErrors as B, FORM_ERROR as g, VALIDATION_OUTCOME as m, clearErrors as p, create as q, getError as r, getErrorByPath as s, getErrors as t, getFieldErrors as u, getFieldErrorsByPath as v, getFirstError as w, hasErrors as x, setError as y, setErrorByPath as z };
|
|
486
|
+
export type { Form as F, Name as N, Options as O, Path as P, ReValidateMode as R, SetErrorOptions as S, ValidationMode as V, FieldError as a, FieldPath as b, PathSegments as c, PathValueOf as d, FieldErrorEntry as e, FormEvents as f, FormValidateFn as h, FormValidateMeta as i, PathValue as j, SetFocusOptions as k, SetServerErrorsOptions as l, ValidateResult as n, ValidationOutcome as o };
|