@formisch/react 0.4.6 → 0.6.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 CHANGED
@@ -9,6 +9,7 @@ Formisch is also available for [Preact][formisch-preact], [Qwik][formisch-qwik],
9
9
  - Small bundle size starting at 2.5 kB
10
10
  - Schema-based validation with Valibot
11
11
  - Type safety with autocompletion in editor
12
+ - Open source and fully tested with 100 % coverage
12
13
  - It's fast – re-renders only if necessary
13
14
  - Minimal, readable and well thought out API
14
15
  - Supports all native HTML form fields
@@ -59,7 +60,13 @@ In addition, Formisch offers several functions (we call them "methods") that can
59
60
 
60
61
  ## Comparison
61
62
 
62
- What makes Formisch unique is its framework-agnostic core, which is fully native to the framework you are using. It works by inserting framework-specific reactivity blocks when the core package is built. The result is a small bundle size and native performance for any UI update. This feature, along with a few others, distinguishes Formisch from other form libraries. My vision for Formisch is to create a framework-agnostic platform similar to [Vite](https://vite.dev/), but for forms.
63
+ What makes Formisch unique is its framework-agnostic core, which is fully native to the framework you are using. It works by inserting framework-specific reactivity blocks when the core package is built, giving you native performance for any UI update. A modular methods API keeps bundles starting at just ~2.5 kB by only including the methods you import, and end-to-end type safety covers deeply nested paths and field arrays with TypeScript inference that stays fast even as forms grow.
64
+
65
+ For a side-by-side look at how Formisch compares to React Hook Form and TanStack Form, see the [comparison guide](https://formisch.dev/react/guides/comparison/).
66
+
67
+ ## Vision
68
+
69
+ My vision for Formisch is to create a framework-agnostic platform similar to [Vite](https://vite.dev/), but for forms — a shared core that lets the same mental model and codebase work natively across every modern UI framework.
63
70
 
64
71
  ## Partners
65
72
 
package/dist/index.d.ts CHANGED
@@ -8,6 +8,31 @@ import { ChangeEventHandler, FocusEventHandler, FormEvent, FormHTMLAttributes, R
8
8
  * Schema type.
9
9
  */
10
10
  type Schema = v.GenericSchema | v.GenericSchemaAsync;
11
+ /**
12
+ * Object schema type.
13
+ */
14
+ type ObjectSchema = v.LooseObjectSchema<v.ObjectEntries, v.ErrorMessage<v.LooseObjectIssue> | undefined> | v.ObjectSchema<v.ObjectEntries, v.ErrorMessage<v.ObjectIssue> | undefined> | v.StrictObjectSchema<v.ObjectEntries, v.ErrorMessage<v.StrictObjectIssue> | undefined> | v.VariantSchema<string, v.VariantOptions<string>, v.ErrorMessage<v.VariantIssue> | undefined>;
15
+ /**
16
+ * Object schema async type.
17
+ */
18
+ type ObjectSchemaAsync = v.LooseObjectSchemaAsync<v.ObjectEntriesAsync, v.ErrorMessage<v.LooseObjectIssue> | undefined> | v.ObjectSchemaAsync<v.ObjectEntriesAsync, v.ErrorMessage<v.ObjectIssue> | undefined> | v.StrictObjectSchemaAsync<v.ObjectEntriesAsync, v.ErrorMessage<v.StrictObjectIssue> | undefined> | v.VariantSchemaAsync<string, v.VariantOptionsAsync<string>, v.ErrorMessage<v.VariantIssue> | undefined>;
19
+ /**
20
+ * Object root schema type.
21
+ */
22
+ type ObjectRootSchema = ObjectSchema | v.IntersectSchema<ObjectSchema[], v.ErrorMessage<v.IntersectIssue> | undefined> | v.UnionSchema<ObjectSchema[], v.ErrorMessage<v.UnionIssue<v.BaseIssue<unknown>>> | undefined>;
23
+ /**
24
+ * Object root schema async type.
25
+ */
26
+ type ObjectRootSchemaAsync = ObjectSchemaAsync | v.IntersectSchemaAsync<(ObjectSchema | ObjectSchemaAsync)[], v.ErrorMessage<v.IntersectIssue> | undefined> | v.UnionSchemaAsync<(ObjectSchema | ObjectSchemaAsync)[], v.ErrorMessage<v.UnionIssue<v.BaseIssue<unknown>>> | undefined>;
27
+ /**
28
+ * Form schema type.
29
+ *
30
+ * Hint: Forms must have an object root, so only object schemas (sync or async),
31
+ * combinators (intersect, union, variant) whose options resolve to objects, and
32
+ * `lazy` schemas wrapping any of these are allowed at the top level. Use
33
+ * {@link Schema} for nested field schemas.
34
+ */
35
+ type FormSchema = ObjectRootSchema | ObjectRootSchemaAsync | v.LazySchema<ObjectRootSchema> | v.LazySchemaAsync<ObjectRootSchema | ObjectRootSchemaAsync>;
11
36
  //#endregion
12
37
  //#region src/types/signal/signal.d.ts
13
38
  /**
@@ -47,6 +72,13 @@ interface InternalBaseStore {
47
72
  schema: Schema;
48
73
  /**
49
74
  * The initial elements of the field.
75
+ *
76
+ * Hint: This may look unused, but do not remove it. `copyItemState` and
77
+ * `swapItemState` move the `elements` reference between field stores when
78
+ * array items are inserted, moved, removed or swapped, and `reset` restores
79
+ * each field's original element via `elements = initialElements`. Without it,
80
+ * focus and file reset target the wrong element after a reorder followed by a
81
+ * reset.
50
82
  */
51
83
  initialElements: FieldElement[];
52
84
  /**
@@ -224,7 +256,7 @@ type ValidationMode = "initial" | "touch" | "input" | "change" | "blur" | "submi
224
256
  /**
225
257
  * Form config interface.
226
258
  */
227
- interface FormConfig<TSchema extends Schema = Schema> {
259
+ interface FormConfig<TSchema extends FormSchema = FormSchema> {
228
260
  /**
229
261
  * The schema of the form.
230
262
  */
@@ -245,7 +277,7 @@ interface FormConfig<TSchema extends Schema = Schema> {
245
277
  /**
246
278
  * Internal form store interface.
247
279
  */
248
- interface InternalFormStore<TSchema extends Schema = Schema> extends InternalObjectStore {
280
+ interface InternalFormStore<TSchema extends FormSchema = FormSchema> extends InternalObjectStore {
249
281
  /**
250
282
  * The element of the form.
251
283
  */
@@ -282,7 +314,7 @@ interface InternalFormStore<TSchema extends Schema = Schema> extends InternalObj
282
314
  /**
283
315
  * Base form store interface.
284
316
  */
285
- interface BaseFormStore<TSchema extends Schema = Schema> {
317
+ interface BaseFormStore<TSchema extends FormSchema = FormSchema> {
286
318
  /**
287
319
  * The internal form store.
288
320
  *
@@ -295,11 +327,11 @@ interface BaseFormStore<TSchema extends Schema = Schema> {
295
327
  /**
296
328
  * Submit handler type.
297
329
  */
298
- type SubmitHandler<TSchema extends Schema> = (output: v.InferOutput<TSchema>) => MaybePromise<unknown>;
330
+ type SubmitHandler<TSchema extends FormSchema> = (output: v.InferOutput<TSchema>) => MaybePromise<unknown>;
299
331
  /**
300
332
  * Submit event handler type.
301
333
  */
302
- type SubmitEventHandler<TSchema extends Schema> = (output: v.InferOutput<TSchema>, event: FormEvent<HTMLFormElement>) => MaybePromise<unknown>;
334
+ type SubmitEventHandler<TSchema extends FormSchema> = (output: v.InferOutput<TSchema>, event: FormEvent<HTMLFormElement>) => MaybePromise<unknown>;
303
335
  //#endregion
304
336
  //#region src/types/path/path.d.ts
305
337
  /**
@@ -370,7 +402,10 @@ type ExactRequired<TValue> = TValue extends Record<PropertyKey, unknown> ? IsExa
370
402
  */
371
403
  type PathValue<TValue, TPath extends Path> = TPath extends readonly [infer TKey, ...infer TRest extends Path] ? TKey extends ExactKeysOf<ExactRequired<TValue>> ? PathValue<PropertiesOf<ExactRequired<TValue>>[TKey], TRest> : unknown : TValue;
372
404
  /**
373
- * Checks whether a value is an array or contains one anywhere in its shape.
405
+ * Checks whether a value is a dynamic array or contains one anywhere in its
406
+ * shape. A fixed-length tuple is not itself a dynamic array, but it counts when
407
+ * it contains one, so paths can still navigate through tuples to reach nested
408
+ * arrays.
374
409
  *
375
410
  * Hint: The inner conditionals (`TValue extends readonly unknown[]` and
376
411
  * `TValue extends Record<PropertyKey, unknown>`) distribute over union members,
@@ -381,7 +416,7 @@ type PathValue<TValue, TPath extends Path> = TPath extends readonly [infer TKey,
381
416
  * `true extends ...`, which is `true` whenever at least one union member
382
417
  * contributed `true`.
383
418
  */
384
- type IsOrHasArray<TValue> = true extends (IsAny<TValue> extends true ? false : TValue extends readonly unknown[] ? true : TValue extends Record<PropertyKey, unknown> ? { [TKey in keyof TValue]: IsOrHasArray<TValue[TKey]> }[keyof TValue] : false) ? true : false;
419
+ type IsOrHasArray<TValue> = true extends (IsAny<TValue> extends true ? false : TValue extends readonly unknown[] ? number extends TValue["length"] ? true : IsOrHasArray<TValue[number]> : TValue extends Record<PropertyKey, unknown> ? { [TKey in keyof TValue]: IsOrHasArray<TValue[TKey]> }[keyof TValue] : false) ? true : false;
385
420
  /**
386
421
  * Extracts the exact keys of a tuple, array or object that contain arrays.
387
422
  */
@@ -396,12 +431,34 @@ type PropertiesOfArrayPath<TValue> = { [TKey in ExactKeysOfArrayPath<TValue>]: T
396
431
  /**
397
432
  * Lazily evaluates only the first valid array path segment based on the given value.
398
433
  */
399
- type LazyArrayPath<TValue, TPathToCheck extends Path, TValidPath extends Path = readonly []> = TPathToCheck extends readonly [] ? TValue extends readonly unknown[] ? TValidPath : readonly [...TValidPath, ExactKeysOfArrayPath<TValue>] : TPathToCheck extends readonly [infer TFirstKey extends ExactKeysOfArrayPath<TValue>, ...infer TPathRest extends Path] ? LazyArrayPath<Required<PropertiesOfArrayPath<TValue>[TFirstKey]>, TPathRest, readonly [...TValidPath, TFirstKey]> : IsNever<ExactKeysOfArrayPath<TValue>> extends false ? readonly [...TValidPath, ExactKeysOfArrayPath<TValue>] : never;
434
+ type LazyArrayPath<TValue, TPathToCheck extends Path, TValidPath extends Path = readonly []> = TPathToCheck extends readonly [] ? TValue extends readonly unknown[] ? number extends TValue["length"] ? TValidPath : IsNever<ExactKeysOfArrayPath<TValue>> extends false ? readonly [...TValidPath, ExactKeysOfArrayPath<TValue>] : never : readonly [...TValidPath, ExactKeysOfArrayPath<TValue>] : TPathToCheck extends readonly [infer TFirstKey extends ExactKeysOfArrayPath<TValue>, ...infer TPathRest extends Path] ? LazyArrayPath<Required<PropertiesOfArrayPath<TValue>[TFirstKey]>, TPathRest, readonly [...TValidPath, TFirstKey]> : IsNever<ExactKeysOfArrayPath<TValue>> extends false ? readonly [...TValidPath, ExactKeysOfArrayPath<TValue>] : never;
400
435
  /**
401
436
  * Returns the path if valid, otherwise the first possible valid array path
402
437
  * based on the given value.
403
438
  */
404
439
  type ValidArrayPath<TValue, TPath extends RequiredPath> = TPath extends LazyArrayPath<Required<TValue>, TPath> ? TPath : LazyArrayPath<Required<TValue>, TPath>;
440
+ /**
441
+ * Recursive helper for `DirtyPath` that prepends `TKey` to each deeper path,
442
+ * or falls through to `never` when the child is not an object.
443
+ */
444
+ type DeepDirtyPath<TChild, TKey$1 extends PathKey, TDepth extends 0[]> = TChild extends Record<PropertyKey, unknown> ? readonly [TKey$1, ...DirtyPath<TChild, [...TDepth, 0]>] : never;
445
+ /**
446
+ * Returns the union of all `RequiredPath`s that `getDirtyPaths` can emit
447
+ * for a given input type. Object fields contribute their own path and the
448
+ * paths of their descendants; arrays and tuples are atomic and contribute
449
+ * only their own path, because dirty arrays are returned as complete units.
450
+ *
451
+ * Narrowing is exact for the first 5 levels of nesting; deeper paths fall
452
+ * back to `RequiredPath` to keep the result a complete superset of any
453
+ * path the runtime can address.
454
+ *
455
+ * Hint: Arrays and tuples are atomic because they don't structurally
456
+ * extend `Record<PropertyKey, unknown>` and so fall through to `never`
457
+ * via `DeepDirtyPath` — no explicit array check is needed. `TDepth` is
458
+ * a tuple-length counter capped at 5 to bound TypeScript instantiation
459
+ * cost.
460
+ */
461
+ type DirtyPath<TValue, TDepth extends 0[] = []> = TDepth["length"] extends 5 ? RequiredPath : TValue extends Record<PropertyKey, unknown> ? { [TKey in ExactKeysOf<TValue>]: readonly [TKey] | DeepDirtyPath<NonNullable<PropertiesOf<TValue>[TKey]>, TKey, TDepth> }[ExactKeysOf<TValue>] : never;
405
462
  //#endregion
406
463
  //#region src/array/copyItemState/copyItemState.d.ts
407
464
  /**
@@ -419,21 +476,21 @@ type ValidArrayPath<TValue, TPath extends RequiredPath> = TPath extends LazyArra
419
476
  /**
420
477
  * Focus field config interface.
421
478
  */
422
- interface FocusFieldConfig<TSchema extends Schema, TFieldPath extends RequiredPath> {
479
+ interface FocusFieldConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> {
423
480
  /**
424
481
  * The path to the field to focus.
425
482
  */
426
483
  readonly path: ValidPath<v.InferInput<TSchema>, TFieldPath>;
427
484
  }
428
485
  /**
429
- * Focuses the first input element of a field. This is useful for
486
+ * Focuses the first focusable input element of a field. This is useful for
430
487
  * programmatically setting focus to a specific field, such as after
431
488
  * validation errors or user interactions.
432
489
  *
433
490
  * @param form The form store containing the field.
434
491
  * @param config The focus field configuration.
435
492
  */
436
- declare function focus<TSchema extends Schema, TFieldPath extends RequiredPath>(form: BaseFormStore<TSchema>, config: FocusFieldConfig<TSchema, TFieldPath>): void;
493
+ declare function focus<TSchema extends FormSchema, TFieldPath extends RequiredPath>(form: BaseFormStore<TSchema>, config: FocusFieldConfig<TSchema, TFieldPath>): void;
437
494
  //#endregion
438
495
  //#region src/getAllErrors/getAllErrors.d.ts
439
496
  /**
@@ -447,6 +504,93 @@ declare function focus<TSchema extends Schema, TFieldPath extends RequiredPath>(
447
504
  */
448
505
  declare function getAllErrors(form: BaseFormStore): [string, ...string[]] | null;
449
506
  //#endregion
507
+ //#region src/getDirtyInput/getDirtyInput.d.ts
508
+ /**
509
+ * Get form dirty input config interface.
510
+ */
511
+ interface GetFormDirtyInputConfig {
512
+ /**
513
+ * The path to a field. Leave undefined to get the dirty input of the entire
514
+ * form.
515
+ */
516
+ readonly path?: undefined;
517
+ }
518
+ /**
519
+ * Get field dirty input config interface.
520
+ */
521
+ interface GetFieldDirtyInputConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> {
522
+ /**
523
+ * The path to the field to retrieve the dirty input from.
524
+ */
525
+ readonly path: ValidPath<v.InferInput<TSchema>, TFieldPath>;
526
+ }
527
+ /**
528
+ * Retrieves only the dirty input values of a specific field or the entire
529
+ * form. Arrays are treated as atomic and returned in full if any item is
530
+ * dirty, while object keys without a dirty descendant are omitted. Returns
531
+ * `undefined` if no field in the inspected subtree is dirty.
532
+ *
533
+ * @param form The form store to retrieve dirty input from.
534
+ *
535
+ * @returns The dirty input of the form or specified field, or `undefined`.
536
+ */
537
+ declare function getDirtyInput<TSchema extends FormSchema>(form: BaseFormStore<TSchema>): DeepPartial<v.InferInput<TSchema>> | undefined;
538
+ /**
539
+ * Retrieves only the dirty input values of a specific field or the entire
540
+ * form. Arrays are treated as atomic and returned in full if any item is
541
+ * dirty, while object keys without a dirty descendant are omitted. Returns
542
+ * `undefined` if no field in the inspected subtree is dirty.
543
+ *
544
+ * @param form The form store to retrieve dirty input from.
545
+ * @param config The get dirty input configuration.
546
+ *
547
+ * @returns The dirty input of the form or specified field, or `undefined`.
548
+ */
549
+ declare function getDirtyInput<TSchema extends FormSchema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? GetFieldDirtyInputConfig<TSchema, TFieldPath> : GetFormDirtyInputConfig): DeepPartial<TFieldPath extends RequiredPath ? PathValue<v.InferInput<TSchema>, TFieldPath> : v.InferInput<TSchema>> | undefined;
550
+ //#endregion
551
+ //#region src/getDirtyPaths/getDirtyPaths.d.ts
552
+ /**
553
+ * Get form dirty paths config interface.
554
+ */
555
+ interface GetFormDirtyPathsConfig {
556
+ /**
557
+ * The path to a field. Leave undefined to inspect the entire form.
558
+ */
559
+ readonly path?: undefined;
560
+ }
561
+ /**
562
+ * Get field dirty paths config interface.
563
+ */
564
+ interface GetFieldDirtyPathsConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> {
565
+ /**
566
+ * The path to the field to inspect.
567
+ */
568
+ readonly path: ValidPath<v.InferInput<TSchema>, TFieldPath>;
569
+ }
570
+ /**
571
+ * Returns a list of paths to the dirty fields of a specific field or the
572
+ * entire form. Arrays are treated as atomic and contribute only their own
573
+ * path if any item is dirty, while object branches are recursed into. Returns
574
+ * an empty list if no field in the inspected subtree is dirty.
575
+ *
576
+ * @param form The form store to inspect.
577
+ *
578
+ * @returns The list of paths to the dirty fields.
579
+ */
580
+ declare function getDirtyPaths<TSchema extends FormSchema>(form: BaseFormStore<TSchema>): DirtyPath<v.InferInput<TSchema>>[];
581
+ /**
582
+ * Returns a list of paths to the dirty fields of a specific field or the
583
+ * entire form. Arrays are treated as atomic and contribute only their own
584
+ * path if any item is dirty, while object branches are recursed into. Returns
585
+ * an empty list if no field in the inspected subtree is dirty.
586
+ *
587
+ * @param form The form store to inspect.
588
+ * @param config The get dirty paths configuration.
589
+ *
590
+ * @returns The list of paths to the dirty fields.
591
+ */
592
+ declare function getDirtyPaths<TSchema extends FormSchema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? GetFieldDirtyPathsConfig<TSchema, TFieldPath> : GetFormDirtyPathsConfig): DirtyPath<v.InferInput<TSchema>>[];
593
+ //#endregion
450
594
  //#region src/getErrors/getErrors.d.ts
451
595
  /**
452
596
  * Get form errors config interface.
@@ -460,7 +604,7 @@ interface GetFormErrorsConfig {
460
604
  /**
461
605
  * Get field errors config interface.
462
606
  */
463
- interface GetFieldErrorsConfig<TSchema extends Schema, TFieldPath extends RequiredPath> {
607
+ interface GetFieldErrorsConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> {
464
608
  /**
465
609
  * The path to the field to retrieve errors from.
466
610
  */
@@ -475,7 +619,7 @@ interface GetFieldErrorsConfig<TSchema extends Schema, TFieldPath extends Requir
475
619
  *
476
620
  * @returns A non-empty array of error messages, or null if no errors exist.
477
621
  */
478
- declare function getErrors<TSchema extends Schema>(form: BaseFormStore<TSchema>): [string, ...string[]] | null;
622
+ declare function getErrors<TSchema extends FormSchema>(form: BaseFormStore<TSchema>): [string, ...string[]] | null;
479
623
  /**
480
624
  * Retrieves error messages from the form. When called without a config,
481
625
  * returns form-level errors. When called with a path, returns errors for
@@ -486,7 +630,7 @@ declare function getErrors<TSchema extends Schema>(form: BaseFormStore<TSchema>)
486
630
  *
487
631
  * @returns A non-empty array of error messages, or null if no errors exist.
488
632
  */
489
- declare function getErrors<TSchema extends Schema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? GetFieldErrorsConfig<TSchema, TFieldPath> : GetFormErrorsConfig): [string, ...string[]] | null;
633
+ declare function getErrors<TSchema extends FormSchema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? GetFieldErrorsConfig<TSchema, TFieldPath> : GetFormErrorsConfig): [string, ...string[]] | null;
490
634
  //#endregion
491
635
  //#region src/getInput/getInput.d.ts
492
636
  /**
@@ -501,7 +645,7 @@ interface GetFormInputConfig {
501
645
  /**
502
646
  * Get field input config interface.
503
647
  */
504
- interface GetFieldInputConfig<TSchema extends Schema, TFieldPath extends RequiredPath> {
648
+ interface GetFieldInputConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> {
505
649
  /**
506
650
  * The path to the field to retrieve input from.
507
651
  */
@@ -515,7 +659,7 @@ interface GetFieldInputConfig<TSchema extends Schema, TFieldPath extends Require
515
659
  *
516
660
  * @returns The partial input values of the form or the specified field.
517
661
  */
518
- declare function getInput<TSchema extends Schema>(form: BaseFormStore<TSchema>): PartialValues<v.InferInput<TSchema>>;
662
+ declare function getInput<TSchema extends FormSchema>(form: BaseFormStore<TSchema>): PartialValues<v.InferInput<TSchema>>;
519
663
  /**
520
664
  * Retrieves the current input value of a specific field or the entire form.
521
665
  * Returns a partial object as not all fields may have been set.
@@ -525,7 +669,7 @@ declare function getInput<TSchema extends Schema>(form: BaseFormStore<TSchema>):
525
669
  *
526
670
  * @returns The partial input values of the form or the specified field.
527
671
  */
528
- declare function getInput<TSchema extends Schema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? GetFieldInputConfig<TSchema, TFieldPath> : GetFormInputConfig): PartialValues<TFieldPath extends RequiredPath ? PathValue<v.InferInput<TSchema>, TFieldPath> : v.InferInput<TSchema>>;
672
+ declare function getInput<TSchema extends FormSchema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? GetFieldInputConfig<TSchema, TFieldPath> : GetFormInputConfig): PartialValues<TFieldPath extends RequiredPath ? PathValue<v.InferInput<TSchema>, TFieldPath> : v.InferInput<TSchema>>;
529
673
  //#endregion
530
674
  //#region src/handleSubmit/handleSubmit.react.d.ts
531
675
  /**
@@ -538,7 +682,7 @@ declare function getInput<TSchema extends Schema, TFieldPath extends RequiredPat
538
682
  *
539
683
  * @returns A submit event handler function to attach to the form element.
540
684
  */
541
- declare function handleSubmit<TSchema extends Schema>(form: BaseFormStore<TSchema>, handler: SubmitHandler<TSchema>): () => Promise<void>;
685
+ declare function handleSubmit<TSchema extends FormSchema>(form: BaseFormStore<TSchema>, handler: SubmitHandler<TSchema>): () => Promise<void>;
542
686
  /**
543
687
  * Creates a submit event handler for the form that prevents default browser
544
688
  * submission, validates the form input, and calls the provided handler if
@@ -549,13 +693,13 @@ declare function handleSubmit<TSchema extends Schema>(form: BaseFormStore<TSchem
549
693
  *
550
694
  * @returns A submit event handler function to attach to the form element.
551
695
  */
552
- declare function handleSubmit<TSchema extends Schema>(form: BaseFormStore<TSchema>, handler: SubmitEventHandler<TSchema>): (event: FormEvent<HTMLFormElement>) => Promise<void>;
696
+ declare function handleSubmit<TSchema extends FormSchema>(form: BaseFormStore<TSchema>, handler: SubmitEventHandler<TSchema>): (event: FormEvent<HTMLFormElement>) => Promise<void>;
553
697
  //#endregion
554
698
  //#region src/insert/insert.d.ts
555
699
  /**
556
700
  * Insert array field config interface.
557
701
  */
558
- interface InsertConfig<TSchema extends Schema, TFieldArrayPath extends RequiredPath> {
702
+ interface InsertConfig<TSchema extends FormSchema, TFieldArrayPath extends RequiredPath> {
559
703
  /**
560
704
  * The path to the field array to insert into.
561
705
  */
@@ -576,13 +720,13 @@ interface InsertConfig<TSchema extends Schema, TFieldArrayPath extends RequiredP
576
720
  * @param form The form store containing the field array.
577
721
  * @param config The insert configuration specifying the path, index, and initial value.
578
722
  */
579
- declare function insert<TSchema extends Schema, TFieldArrayPath extends RequiredPath>(form: BaseFormStore<TSchema>, config: InsertConfig<TSchema, TFieldArrayPath>): void;
723
+ declare function insert<TSchema extends FormSchema, TFieldArrayPath extends RequiredPath>(form: BaseFormStore<TSchema>, config: InsertConfig<TSchema, TFieldArrayPath>): void;
580
724
  //#endregion
581
725
  //#region src/move/move.d.ts
582
726
  /**
583
727
  * Move array field config interface.
584
728
  */
585
- interface MoveConfig<TSchema extends Schema, TFieldArrayPath extends RequiredPath> {
729
+ interface MoveConfig<TSchema extends FormSchema, TFieldArrayPath extends RequiredPath> {
586
730
  /**
587
731
  * The path to the field array to move an item within.
588
732
  */
@@ -603,13 +747,38 @@ interface MoveConfig<TSchema extends Schema, TFieldArrayPath extends RequiredPat
603
747
  * @param form The form store containing the field array.
604
748
  * @param config The move configuration specifying the path and source/destination indices.
605
749
  */
606
- declare function move<TSchema extends Schema, TFieldArrayPath extends RequiredPath>(form: BaseFormStore<TSchema>, config: MoveConfig<TSchema, TFieldArrayPath>): void;
750
+ declare function move<TSchema extends FormSchema, TFieldArrayPath extends RequiredPath>(form: BaseFormStore<TSchema>, config: MoveConfig<TSchema, TFieldArrayPath>): void;
751
+ //#endregion
752
+ //#region src/pickDirty/pickDirty.d.ts
753
+ /**
754
+ * Pick dirty config interface.
755
+ */
756
+ interface PickDirtyConfig<TValue extends object> {
757
+ /**
758
+ * The value to filter down to its dirty parts. Must be structurally
759
+ * compatible with the form's schema.
760
+ */
761
+ readonly from: TValue;
762
+ }
763
+ /**
764
+ * Picks only the dirty parts of the given value, using the form's dirty fields
765
+ * as a structural mask. Arrays are treated as atomic and object keys without a
766
+ * dirty descendant are omitted. Returns `undefined` if no field is dirty.
767
+ * Useful for filtering a validated output down to its changed parts before
768
+ * submitting.
769
+ *
770
+ * @param form The form store providing the dirty mask.
771
+ * @param config The pick dirty configuration.
772
+ *
773
+ * @returns The dirty parts of the value, or `undefined`.
774
+ */
775
+ declare function pickDirty<TSchema extends FormSchema, TValue extends object>(form: BaseFormStore<TSchema>, config: PickDirtyConfig<TValue>): DeepPartial<TValue> | undefined;
607
776
  //#endregion
608
777
  //#region src/remove/remove.d.ts
609
778
  /**
610
779
  * Remove array field config interface.
611
780
  */
612
- interface RemoveConfig<TSchema extends Schema, TFieldArrayPath extends RequiredPath> {
781
+ interface RemoveConfig<TSchema extends FormSchema, TFieldArrayPath extends RequiredPath> {
613
782
  /**
614
783
  * The path to the field array to remove an item from.
615
784
  */
@@ -626,13 +795,13 @@ interface RemoveConfig<TSchema extends Schema, TFieldArrayPath extends RequiredP
626
795
  * @param form The form store containing the field array.
627
796
  * @param config The remove configuration specifying the path and index.
628
797
  */
629
- declare function remove<TSchema extends Schema, TFieldArrayPath extends RequiredPath>(form: BaseFormStore<TSchema>, config: RemoveConfig<TSchema, TFieldArrayPath>): void;
798
+ declare function remove<TSchema extends FormSchema, TFieldArrayPath extends RequiredPath>(form: BaseFormStore<TSchema>, config: RemoveConfig<TSchema, TFieldArrayPath>): void;
630
799
  //#endregion
631
800
  //#region src/replace/replace.d.ts
632
801
  /**
633
802
  * Replace array field config interface.
634
803
  */
635
- interface ReplaceConfig<TSchema extends Schema, TFieldArrayPath extends RequiredPath> {
804
+ interface ReplaceConfig<TSchema extends FormSchema, TFieldArrayPath extends RequiredPath> {
636
805
  /**
637
806
  * The path to the field array to replace an item within.
638
807
  */
@@ -652,7 +821,7 @@ interface ReplaceConfig<TSchema extends Schema, TFieldArrayPath extends Required
652
821
  * @param form The form store containing the field array.
653
822
  * @param config The replace configuration specifying the path, index, and initial input.
654
823
  */
655
- declare function replace<TSchema extends Schema, TFieldArrayPath extends RequiredPath>(form: BaseFormStore<TSchema>, config: ReplaceConfig<TSchema, TFieldArrayPath>): void;
824
+ declare function replace<TSchema extends FormSchema, TFieldArrayPath extends RequiredPath>(form: BaseFormStore<TSchema>, config: ReplaceConfig<TSchema, TFieldArrayPath>): void;
656
825
  //#endregion
657
826
  //#region src/reset/reset.d.ts
658
827
  /**
@@ -675,7 +844,7 @@ interface ResetBaseConfig {
675
844
  /**
676
845
  * Reset form config interface.
677
846
  */
678
- interface ResetFormConfig<TSchema extends Schema> extends ResetBaseConfig {
847
+ interface ResetFormConfig<TSchema extends FormSchema> extends ResetBaseConfig {
679
848
  /**
680
849
  * The path to a field. Leave undefined to reset the entire form.
681
850
  */
@@ -693,7 +862,7 @@ interface ResetFormConfig<TSchema extends Schema> extends ResetBaseConfig {
693
862
  /**
694
863
  * Reset field config interface.
695
864
  */
696
- interface ResetFieldConfig<TSchema extends Schema, TFieldPath extends RequiredPath> extends ResetBaseConfig {
865
+ interface ResetFieldConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> extends ResetBaseConfig {
697
866
  /**
698
867
  * The path to the field to reset.
699
868
  */
@@ -720,7 +889,7 @@ declare function reset(form: BaseFormStore): void;
720
889
  * @param form The form store to reset.
721
890
  * @param config The reset configuration specifying what to reset and what to keep.
722
891
  */
723
- declare function reset<TSchema extends Schema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? ResetFieldConfig<TSchema, TFieldPath> : ResetFormConfig<TSchema>): void;
892
+ declare function reset<TSchema extends FormSchema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? ResetFieldConfig<TSchema, TFieldPath> : ResetFormConfig<TSchema>): void;
724
893
  //#endregion
725
894
  //#region src/setErrors/setErrors.d.ts
726
895
  /**
@@ -739,7 +908,7 @@ interface SetFormErrorsConfig {
739
908
  /**
740
909
  * Set field errors config interface.
741
910
  */
742
- interface SetFieldErrorsConfig<TSchema extends Schema, TFieldPath extends RequiredPath> {
911
+ interface SetFieldErrorsConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> {
743
912
  /**
744
913
  * The path to the field to set errors on.
745
914
  */
@@ -757,13 +926,13 @@ interface SetFieldErrorsConfig<TSchema extends Schema, TFieldPath extends Requir
757
926
  * @param form The form store to set errors on.
758
927
  * @param config The set errors configuration specifying the path and error messages.
759
928
  */
760
- declare function setErrors<TSchema extends Schema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? SetFieldErrorsConfig<TSchema, TFieldPath> : SetFormErrorsConfig): void;
929
+ declare function setErrors<TSchema extends FormSchema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? SetFieldErrorsConfig<TSchema, TFieldPath> : SetFormErrorsConfig): void;
761
930
  //#endregion
762
931
  //#region src/setInput/setInput.d.ts
763
932
  /**
764
933
  * Set form input config interface.
765
934
  */
766
- interface SetFormInputConfig<TSchema extends Schema> {
935
+ interface SetFormInputConfig<TSchema extends FormSchema> {
767
936
  /**
768
937
  * The path to a field. Leave undefined to set the entire form input.
769
938
  */
@@ -776,7 +945,7 @@ interface SetFormInputConfig<TSchema extends Schema> {
776
945
  /**
777
946
  * Set field input config interface.
778
947
  */
779
- interface SetFieldInputConfig<TSchema extends Schema, TFieldPath extends RequiredPath> {
948
+ interface SetFieldInputConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> {
780
949
  /**
781
950
  * The path to the field to set input on.
782
951
  */
@@ -796,7 +965,7 @@ interface SetFieldInputConfig<TSchema extends Schema, TFieldPath extends Require
796
965
  * @param form The form store to set input on.
797
966
  * @param config The set form input configuration specifying the new input values.
798
967
  */
799
- declare function setInput<TSchema extends Schema>(form: BaseFormStore<TSchema>, config: SetFormInputConfig<TSchema>): void;
968
+ declare function setInput<TSchema extends FormSchema>(form: BaseFormStore<TSchema>, config: SetFormInputConfig<TSchema>): void;
800
969
  /**
801
970
  * Sets the input value of a specific field or the entire form. This updates
802
971
  * the field value(s) and triggers validation if required by the form's
@@ -805,7 +974,7 @@ declare function setInput<TSchema extends Schema>(form: BaseFormStore<TSchema>,
805
974
  * @param form The form store to set input on.
806
975
  * @param config The set input configuration specifying the path and new value.
807
976
  */
808
- declare function setInput<TSchema extends Schema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? SetFieldInputConfig<TSchema, TFieldPath> : SetFormInputConfig<TSchema>): void;
977
+ declare function setInput<TSchema extends FormSchema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? SetFieldInputConfig<TSchema, TFieldPath> : SetFormInputConfig<TSchema>): void;
809
978
  //#endregion
810
979
  //#region src/submit/submit.d.ts
811
980
  /**
@@ -820,7 +989,7 @@ declare function submit(form: BaseFormStore): void;
820
989
  /**
821
990
  * Swap array field config interface.
822
991
  */
823
- interface SwapConfig<TSchema extends Schema, TFieldArrayPath extends RequiredPath> {
992
+ interface SwapConfig<TSchema extends FormSchema, TFieldArrayPath extends RequiredPath> {
824
993
  /**
825
994
  * The path to the field array to swap items within.
826
995
  */
@@ -840,7 +1009,7 @@ interface SwapConfig<TSchema extends Schema, TFieldArrayPath extends RequiredPat
840
1009
  * @param form The form store containing the field array.
841
1010
  * @param config The swap configuration specifying the path and indices to swap.
842
1011
  */
843
- declare function swap<TSchema extends Schema, TFieldArrayPath extends RequiredPath>(form: BaseFormStore<TSchema>, config: SwapConfig<TSchema, TFieldArrayPath>): void;
1012
+ declare function swap<TSchema extends FormSchema, TFieldArrayPath extends RequiredPath>(form: BaseFormStore<TSchema>, config: SwapConfig<TSchema, TFieldArrayPath>): void;
844
1013
  //#endregion
845
1014
  //#region src/validate/validate.d.ts
846
1015
  /**
@@ -862,7 +1031,7 @@ interface ValidateFormConfig {
862
1031
  *
863
1032
  * @returns A promise resolving to the validation result.
864
1033
  */
865
- declare function validate<TSchema extends Schema>(form: BaseFormStore<TSchema>, config?: ValidateFormConfig): Promise<v.SafeParseResult<TSchema>>;
1034
+ declare function validate<TSchema extends FormSchema>(form: BaseFormStore<TSchema>, config?: ValidateFormConfig): Promise<v.SafeParseResult<TSchema>>;
866
1035
  //#endregion
867
1036
  //#endregion
868
1037
  //#region src/types/field.d.ts
@@ -898,7 +1067,7 @@ interface FieldElementProps {
898
1067
  /**
899
1068
  * Field store interface.
900
1069
  */
901
- interface FieldStore<TSchema extends Schema = Schema, TFieldPath extends RequiredPath = RequiredPath> {
1070
+ interface FieldStore<TSchema extends FormSchema = FormSchema, TFieldPath extends RequiredPath = RequiredPath> {
902
1071
  /**
903
1072
  * The path to the field within the form.
904
1073
  */
@@ -935,7 +1104,7 @@ interface FieldStore<TSchema extends Schema = Schema, TFieldPath extends Require
935
1104
  /**
936
1105
  * Field array store interface.
937
1106
  */
938
- interface FieldArrayStore<TSchema extends Schema = Schema, TFieldArrayPath extends RequiredPath = RequiredPath> {
1107
+ interface FieldArrayStore<TSchema extends FormSchema = FormSchema, TFieldArrayPath extends RequiredPath = RequiredPath> {
939
1108
  /**
940
1109
  * The path to the array field within the form.
941
1110
  */
@@ -966,7 +1135,7 @@ interface FieldArrayStore<TSchema extends Schema = Schema, TFieldArrayPath exten
966
1135
  /**
967
1136
  * Form store interface.
968
1137
  */
969
- interface FormStore<TSchema extends Schema = Schema> extends BaseFormStore<TSchema> {
1138
+ interface FormStore<TSchema extends FormSchema = FormSchema> extends BaseFormStore<TSchema> {
970
1139
  /**
971
1140
  * Whether the form is currently submitting.
972
1141
  */
@@ -1004,7 +1173,7 @@ interface FormStore<TSchema extends Schema = Schema> extends BaseFormStore<TSche
1004
1173
  /**
1005
1174
  * Field component props interface.
1006
1175
  */
1007
- interface FieldProps<TSchema extends Schema = Schema, TFieldPath extends RequiredPath = RequiredPath> {
1176
+ interface FieldProps<TSchema extends FormSchema = FormSchema, TFieldPath extends RequiredPath = RequiredPath> {
1008
1177
  /**
1009
1178
  * The form store to which the field belongs.
1010
1179
  */
@@ -1027,7 +1196,7 @@ interface FieldProps<TSchema extends Schema = Schema, TFieldPath extends Require
1027
1196
  *
1028
1197
  * @returns The UI of the field to be rendered.
1029
1198
  */
1030
- declare function Field<TSchema extends Schema, TFieldPath extends RequiredPath>({
1199
+ declare function Field<TSchema extends FormSchema, TFieldPath extends RequiredPath>({
1031
1200
  of,
1032
1201
  path,
1033
1202
  children
@@ -1037,7 +1206,7 @@ declare function Field<TSchema extends Schema, TFieldPath extends RequiredPath>(
1037
1206
  /**
1038
1207
  * FieldArray component props interface.
1039
1208
  */
1040
- interface FieldArrayProps<TSchema extends Schema = Schema, TFieldArrayPath extends RequiredPath = RequiredPath> {
1209
+ interface FieldArrayProps<TSchema extends FormSchema = FormSchema, TFieldArrayPath extends RequiredPath = RequiredPath> {
1041
1210
  /**
1042
1211
  * The form store to which the field array belongs.
1043
1212
  */
@@ -1061,7 +1230,7 @@ interface FieldArrayProps<TSchema extends Schema = Schema, TFieldArrayPath exten
1061
1230
  *
1062
1231
  * @returns The UI of the field array to be rendered.
1063
1232
  */
1064
- declare function FieldArray<TSchema extends Schema, TFieldArrayPath extends RequiredPath>({
1233
+ declare function FieldArray<TSchema extends FormSchema, TFieldArrayPath extends RequiredPath>({
1065
1234
  of,
1066
1235
  path,
1067
1236
  children
@@ -1071,7 +1240,7 @@ declare function FieldArray<TSchema extends Schema, TFieldArrayPath extends Requ
1071
1240
  /**
1072
1241
  * Form component props type.
1073
1242
  */
1074
- type FormProps<TSchema extends Schema = Schema> = Omit<FormHTMLAttributes<HTMLFormElement>, "onSubmit" | "novalidate" | "noValidate"> & {
1243
+ type FormProps<TSchema extends FormSchema = FormSchema> = Omit<FormHTMLAttributes<HTMLFormElement>, "onSubmit" | "novalidate" | "noValidate"> & {
1075
1244
  /**
1076
1245
  * The form store instance.
1077
1246
  */
@@ -1089,13 +1258,13 @@ type FormProps<TSchema extends Schema = Schema> = Omit<FormHTMLAttributes<HTMLFo
1089
1258
  *
1090
1259
  * @returns The a native form element.
1091
1260
  */
1092
- declare function Form<TSchema extends Schema>(props: FormProps<TSchema>): ReactElement;
1261
+ declare function Form<TSchema extends FormSchema>(props: FormProps<TSchema>): ReactElement;
1093
1262
  //#endregion
1094
1263
  //#region src/hooks/useField/useField.d.ts
1095
1264
  /**
1096
1265
  * Use field config interface.
1097
1266
  */
1098
- interface UseFieldConfig<TSchema extends Schema = Schema, TFieldPath extends RequiredPath = RequiredPath> {
1267
+ interface UseFieldConfig<TSchema extends FormSchema = FormSchema, TFieldPath extends RequiredPath = RequiredPath> {
1099
1268
  /**
1100
1269
  * The path to the field within the form schema.
1101
1270
  */
@@ -1109,13 +1278,13 @@ interface UseFieldConfig<TSchema extends Schema = Schema, TFieldPath extends Req
1109
1278
  *
1110
1279
  * @returns The field store with reactive properties and element props.
1111
1280
  */
1112
- declare function useField<TSchema extends Schema, TFieldPath extends RequiredPath>(form: FormStore<TSchema>, config: UseFieldConfig<TSchema, TFieldPath>): FieldStore<TSchema, TFieldPath>;
1281
+ declare function useField<TSchema extends FormSchema, TFieldPath extends RequiredPath>(form: FormStore<TSchema>, config: UseFieldConfig<TSchema, TFieldPath>): FieldStore<TSchema, TFieldPath>;
1113
1282
  //#endregion
1114
1283
  //#region src/hooks/useFieldArray/useFieldArray.d.ts
1115
1284
  /**
1116
1285
  * Use field array config interface.
1117
1286
  */
1118
- interface UseFieldArrayConfig<TSchema extends Schema = Schema, TFieldArrayPath extends RequiredPath = RequiredPath> {
1287
+ interface UseFieldArrayConfig<TSchema extends FormSchema = FormSchema, TFieldArrayPath extends RequiredPath = RequiredPath> {
1119
1288
  /**
1120
1289
  * The path to the array field within the form schema.
1121
1290
  */
@@ -1129,7 +1298,7 @@ interface UseFieldArrayConfig<TSchema extends Schema = Schema, TFieldArrayPath e
1129
1298
  *
1130
1299
  * @returns The field array store with reactive properties for array management.
1131
1300
  */
1132
- declare function useFieldArray<TSchema extends Schema, TFieldArrayPath extends RequiredPath>(form: FormStore<TSchema>, config: UseFieldArrayConfig<TSchema, TFieldArrayPath>): FieldArrayStore<TSchema, TFieldArrayPath>;
1301
+ declare function useFieldArray<TSchema extends FormSchema, TFieldArrayPath extends RequiredPath>(form: FormStore<TSchema>, config: UseFieldArrayConfig<TSchema, TFieldArrayPath>): FieldArrayStore<TSchema, TFieldArrayPath>;
1133
1302
  //#endregion
1134
1303
  //#region src/hooks/useForm/useForm.d.ts
1135
1304
  /**
@@ -1140,6 +1309,6 @@ declare function useFieldArray<TSchema extends Schema, TFieldArrayPath extends R
1140
1309
  *
1141
1310
  * @returns The form store with reactive properties.
1142
1311
  */
1143
- declare function useForm<TSchema extends Schema>(config: FormConfig<TSchema>): FormStore<TSchema>;
1312
+ declare function useForm<TSchema extends FormSchema>(config: FormConfig<TSchema>): FormStore<TSchema>;
1144
1313
  //#endregion
1145
- export { type DeepPartial, Field, FieldArray, FieldArrayProps, FieldArrayStore, type FieldElement, FieldElementProps, FieldProps, FieldStore, FocusFieldConfig, Form, type FormConfig, FormProps, FormStore, GetFieldErrorsConfig, GetFieldInputConfig, GetFormErrorsConfig, GetFormInputConfig, InsertConfig, MoveConfig, type PartialValues, type PathValue, RemoveConfig, ReplaceConfig, type RequiredPath, ResetFieldConfig, ResetFormConfig, type Schema, SetFieldErrorsConfig, type SetFieldInputConfig, SetFormErrorsConfig, type SetFormInputConfig, type SubmitEventHandler, type SubmitHandler, SwapConfig, UseFieldArrayConfig, UseFieldConfig, type ValidArrayPath, type ValidPath, ValidateFormConfig, type ValidationMode, focus, getAllErrors, getErrors, getInput, handleSubmit, insert, move, remove, replace, reset, setErrors, setInput, submit, swap, useField, useFieldArray, useForm, validate };
1314
+ export { type DeepPartial, Field, FieldArray, FieldArrayProps, FieldArrayStore, type FieldElement, FieldElementProps, FieldProps, FieldStore, FocusFieldConfig, Form, type FormConfig, FormProps, type FormSchema, FormStore, GetFieldDirtyInputConfig, GetFieldDirtyPathsConfig, GetFieldErrorsConfig, GetFieldInputConfig, GetFormDirtyInputConfig, GetFormDirtyPathsConfig, GetFormErrorsConfig, GetFormInputConfig, InsertConfig, MoveConfig, type PartialValues, type PathValue, PickDirtyConfig, RemoveConfig, ReplaceConfig, type RequiredPath, ResetFieldConfig, ResetFormConfig, type Schema, SetFieldErrorsConfig, type SetFieldInputConfig, SetFormErrorsConfig, type SetFormInputConfig, type SubmitEventHandler, type SubmitHandler, SwapConfig, UseFieldArrayConfig, UseFieldConfig, type ValidArrayPath, type ValidPath, ValidateFormConfig, type ValidationMode, focus, getAllErrors, getDirtyInput, getDirtyPaths, getErrors, getInput, handleSubmit, insert, move, pickDirty, remove, replace, reset, setErrors, setInput, submit, swap, useField, useFieldArray, useForm, validate };
package/dist/index.js CHANGED
@@ -228,31 +228,45 @@ function copyItemState(fromInternalFieldStore, toInternalFieldStore) {
228
228
  * form reset functionality.
229
229
  *
230
230
  * @param internalFieldStore The field store to reset.
231
- * @param initialInput The new input value (can be any type including array or object).
231
+ * @param input The new input value (can be any type including array or object).
232
+ * @param keepStart Whether to keep `startInput` and `startItems` as the dirty
233
+ * baseline instead of resetting them to the new input. Used when a field store
234
+ * is reused for an in-place edit so its dirty state is detected correctly.
232
235
  */
233
- function resetItemState(internalFieldStore, initialInput) {
236
+ function resetItemState(internalFieldStore, input, keepStart = false) {
234
237
  batch(() => {
235
- internalFieldStore.elements = [];
238
+ const elements = [];
239
+ if (internalFieldStore.elements === internalFieldStore.initialElements) internalFieldStore.initialElements = elements;
240
+ internalFieldStore.elements = elements;
236
241
  internalFieldStore.errors.value = null;
237
242
  internalFieldStore.isTouched.value = false;
238
243
  internalFieldStore.isDirty.value = false;
239
244
  if (internalFieldStore.kind === "array" || internalFieldStore.kind === "object") {
240
- const objectInput = initialInput == null ? initialInput : true;
241
- internalFieldStore.startInput.value = objectInput;
245
+ const objectInput = input == null ? input : true;
246
+ if (!keepStart) internalFieldStore.startInput.value = objectInput;
242
247
  internalFieldStore.input.value = objectInput;
243
- if (internalFieldStore.kind === "array") if (initialInput) {
244
- const newItems = initialInput.map(createId);
245
- internalFieldStore.startItems.value = newItems;
248
+ if (internalFieldStore.kind === "array") if (input) {
249
+ const length = internalFieldStore.schema.type === "array" ? input.length : internalFieldStore.children.length;
250
+ const newItems = Array.from({ length }, createId);
251
+ if (!keepStart) internalFieldStore.startItems.value = newItems;
246
252
  internalFieldStore.items.value = newItems;
247
- for (let index = 0; index < initialInput.length; index++) if (internalFieldStore.children[index]) resetItemState(internalFieldStore.children[index], initialInput[index]);
253
+ let path;
254
+ for (let index = 0; index < length; index++) if (internalFieldStore.children[index]) resetItemState(internalFieldStore.children[index], input[index], keepStart);
255
+ else {
256
+ path ??= JSON.parse(internalFieldStore.name);
257
+ internalFieldStore.children[index] = {};
258
+ path.push(index);
259
+ initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, input[index], path);
260
+ path.pop();
261
+ }
248
262
  } else {
249
- internalFieldStore.startItems.value = [];
263
+ if (!keepStart) internalFieldStore.startItems.value = [];
250
264
  internalFieldStore.items.value = [];
251
265
  }
252
- else for (const key in internalFieldStore.children) resetItemState(internalFieldStore.children[key], initialInput?.[key]);
266
+ else for (const key in internalFieldStore.children) resetItemState(internalFieldStore.children[key], input?.[key], keepStart);
253
267
  } else {
254
- internalFieldStore.startInput.value = initialInput;
255
- internalFieldStore.input.value = initialInput;
268
+ if (!keepStart) internalFieldStore.startInput.value = input;
269
+ internalFieldStore.input.value = input;
256
270
  }
257
271
  });
258
272
  }
@@ -319,6 +333,85 @@ function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
319
333
  });
320
334
  }
321
335
  /**
336
+ * Focuses the first focusable element of a field store. The elements are tried
337
+ * in order and the first one that actually receives focus wins, so detached,
338
+ * disabled or hidden elements are skipped. The browser decides focusability,
339
+ * which is read back via the element's root `activeElement` so elements in a
340
+ * shadow root or another document are handled correctly.
341
+ *
342
+ * Hint: A `display: none` or `hidden` element is correctly skipped in real
343
+ * browsers, but jsdom has no layout and focuses it anyway, so that case cannot
344
+ * be covered by unit tests.
345
+ *
346
+ * @param internalFieldStore The field store to focus.
347
+ *
348
+ * @returns Whether an element was focused.
349
+ */
350
+ function focusFieldElement(internalFieldStore) {
351
+ for (const element of internalFieldStore.elements) {
352
+ element.focus();
353
+ if (element.getRootNode().activeElement === element) return true;
354
+ }
355
+ return false;
356
+ }
357
+ /**
358
+ * Returns whether the specified boolean property is true for the field store
359
+ * or any of its nested children. Recursively checks arrays and objects.
360
+ *
361
+ * @param internalFieldStore The field store to check.
362
+ * @param type The boolean property type to check.
363
+ *
364
+ * @returns Whether the property is true.
365
+ */
366
+ /* @__NO_SIDE_EFFECTS__ */
367
+ function getFieldBool(internalFieldStore, type) {
368
+ if (internalFieldStore[type].value) return true;
369
+ if (internalFieldStore.kind === "array") {
370
+ for (let index = 0; index < internalFieldStore.items.value.length; index++) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[index], type)) return true;
371
+ return false;
372
+ }
373
+ if (internalFieldStore.kind == "object") {
374
+ for (const key in internalFieldStore.children) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[key], type)) return true;
375
+ return false;
376
+ }
377
+ return false;
378
+ }
379
+ /**
380
+ * Returns only the dirty input of the field store. Arrays are treated as
381
+ * atomic and returned in full if any item is dirty, while object keys without
382
+ * a dirty descendant are omitted. Returns `undefined` if no descendant is
383
+ * dirty.
384
+ *
385
+ * @param internalFieldStore The field store to get dirty input from.
386
+ * @param dirtyOnly Whether to only include dirty fields. Defaults to `true`.
387
+ *
388
+ * @returns The dirty input, or `undefined` if no descendant is dirty.
389
+ */
390
+ /* @__NO_SIDE_EFFECTS__ */
391
+ function getDirtyFieldInput(internalFieldStore, dirtyOnly = true) {
392
+ if (dirtyOnly && !/* @__PURE__ */ getFieldBool(internalFieldStore, "isDirty")) return;
393
+ if (internalFieldStore.kind === "array") {
394
+ if (internalFieldStore.input.value) {
395
+ const value = [];
396
+ for (let index = 0; index < internalFieldStore.items.value.length; index++) value[index] = /* @__PURE__ */ getDirtyFieldInput(internalFieldStore.children[index], false);
397
+ return value;
398
+ }
399
+ return internalFieldStore.input.value;
400
+ }
401
+ if (internalFieldStore.kind === "object") {
402
+ if (internalFieldStore.input.value) {
403
+ const value = {};
404
+ for (const key in internalFieldStore.children) {
405
+ const child = internalFieldStore.children[key];
406
+ if (!dirtyOnly || /* @__PURE__ */ getFieldBool(child, "isDirty")) value[key] = /* @__PURE__ */ getDirtyFieldInput(child, dirtyOnly);
407
+ }
408
+ return value;
409
+ }
410
+ return internalFieldStore.input.value;
411
+ }
412
+ return internalFieldStore.input.value;
413
+ }
414
+ /**
322
415
  * Returns the current input of the field store. For arrays and objects,
323
416
  * recursively collects input from all children. Returns `null` or `undefined`
324
417
  * for nullish array/object inputs, or the primitive value for value fields.
@@ -375,28 +468,6 @@ function getElementInput(element, internalFieldStore) {
375
468
  return element.value;
376
469
  }
377
470
  /**
378
- * Returns whether the specified boolean property is true for the field store
379
- * or any of its nested children. Recursively checks arrays and objects.
380
- *
381
- * @param internalFieldStore The field store to check.
382
- * @param type The boolean property type to check.
383
- *
384
- * @returns Whether the property is true.
385
- */
386
- /* @__NO_SIDE_EFFECTS__ */
387
- function getFieldBool(internalFieldStore, type) {
388
- if (internalFieldStore[type].value) return true;
389
- if (internalFieldStore.kind === "array") {
390
- for (let index = 0; index < internalFieldStore.items.value.length; index++) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[index], type)) return true;
391
- return false;
392
- }
393
- if (internalFieldStore.kind == "object") {
394
- for (const key in internalFieldStore.children) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[key], type)) return true;
395
- return false;
396
- }
397
- return false;
398
- }
399
- /**
400
471
  * Returns the field store at the specified path by traversing the form store's
401
472
  * children hierarchy.
402
473
  *
@@ -421,11 +492,9 @@ function getFieldStore(internalFormStore, path) {
421
492
  */
422
493
  function setFieldBool(internalFieldStore, type, bool) {
423
494
  batch(() => {
424
- if (internalFieldStore.kind === "array") {
425
- internalFieldStore[type].value = bool;
426
- for (let index = 0; index < untrack(() => internalFieldStore.items.value).length; index++) setFieldBool(internalFieldStore.children[index], type, bool);
427
- } else if (internalFieldStore.kind == "object") for (const key in internalFieldStore.children) setFieldBool(internalFieldStore.children[key], type, bool);
428
- else internalFieldStore[type].value = bool;
495
+ internalFieldStore[type].value = bool;
496
+ if (internalFieldStore.kind === "array") for (let index = 0; index < untrack(() => internalFieldStore.items.value).length; index++) setFieldBool(internalFieldStore.children[index], type, bool);
497
+ else if (internalFieldStore.kind === "object") for (const key in internalFieldStore.children) setFieldBool(internalFieldStore.children[key], type, bool);
429
498
  });
430
499
  }
431
500
  /**
@@ -440,20 +509,20 @@ function setNestedInput(internalFieldStore, input) {
440
509
  if (internalFieldStore.kind === "array") {
441
510
  const arrayInput = input ?? [];
442
511
  const items = internalFieldStore.items.value;
443
- if (arrayInput.length < items.length) internalFieldStore.items.value = items.slice(0, arrayInput.length);
444
- else if (arrayInput.length > items.length) {
445
- if (arrayInput.length > internalFieldStore.children.length) {
446
- const path = JSON.parse(internalFieldStore.name);
447
- for (let index = internalFieldStore.children.length; index < arrayInput.length; index++) {
448
- internalFieldStore.children[index] = {};
449
- path.push(index);
450
- initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, arrayInput[index], path);
451
- path.pop();
452
- }
512
+ const length = internalFieldStore.schema.type === "array" ? arrayInput.length : internalFieldStore.children.length;
513
+ if (length < items.length) internalFieldStore.items.value = items.slice(0, length);
514
+ else if (length > items.length) {
515
+ const path = JSON.parse(internalFieldStore.name);
516
+ for (let index = items.length; index < length; index++) if (internalFieldStore.children[index]) resetItemState(internalFieldStore.children[index], arrayInput[index], true);
517
+ else {
518
+ internalFieldStore.children[index] = {};
519
+ path.push(index);
520
+ initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, arrayInput[index], path);
521
+ path.pop();
453
522
  }
454
- internalFieldStore.items.value = [...items, ...arrayInput.slice(items.length).map(createId)];
523
+ internalFieldStore.items.value = [...items, ...Array.from({ length: length - items.length }, createId)];
455
524
  }
456
- for (let index = 0; index < arrayInput.length; index++) setNestedInput(internalFieldStore.children[index], arrayInput[index]);
525
+ for (let index = 0; index < length; index++) setNestedInput(internalFieldStore.children[index], arrayInput[index]);
457
526
  internalFieldStore.input.value = input == null ? input : true;
458
527
  internalFieldStore.isDirty.value = internalFieldStore.startInput.value !== internalFieldStore.input.value || internalFieldStore.startItems.value.length !== internalFieldStore.items.value.length;
459
528
  } else if (internalFieldStore.kind === "object") {
@@ -497,21 +566,22 @@ function setFieldInput(internalFormStore, path, input) {
497
566
  function setInitialFieldInput(internalFieldStore, initialInput) {
498
567
  batch(() => {
499
568
  if (internalFieldStore.kind === "array") {
500
- internalFieldStore.input.value = initialInput == null ? initialInput : true;
569
+ internalFieldStore.initialInput.value = initialInput == null ? initialInput : true;
501
570
  const initialArrayInput = initialInput ?? [];
502
- if (initialArrayInput.length > internalFieldStore.children.length) {
571
+ const length = internalFieldStore.schema.type === "array" ? initialArrayInput.length : internalFieldStore.children.length;
572
+ if (length > internalFieldStore.children.length) {
503
573
  const path = JSON.parse(internalFieldStore.name);
504
- for (let index = internalFieldStore.children.length; index < initialArrayInput.length; index++) {
574
+ for (let index = internalFieldStore.children.length; index < length; index++) {
505
575
  internalFieldStore.children[index] = {};
506
576
  path.push(index);
507
577
  initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, initialArrayInput[index], path);
508
578
  path.pop();
509
579
  }
510
580
  }
511
- internalFieldStore.initialItems.value = initialArrayInput.map(createId);
581
+ internalFieldStore.initialItems.value = Array.from({ length }, createId);
512
582
  for (let index = 0; index < internalFieldStore.children.length; index++) setInitialFieldInput(internalFieldStore.children[index], initialArrayInput[index]);
513
583
  } else if (internalFieldStore.kind === "object") {
514
- internalFieldStore.input.value = initialInput == null ? initialInput : true;
584
+ internalFieldStore.initialInput.value = initialInput == null ? initialInput : true;
515
585
  for (const key in internalFieldStore.children) setInitialFieldInput(internalFieldStore.children[key], initialInput?.[key]);
516
586
  } else internalFieldStore.initialInput.value = initialInput;
517
587
  });
@@ -563,44 +633,49 @@ function createFormStore(config, parse) {
563
633
  async function validateFormInput(internalFormStore, config) {
564
634
  internalFormStore.validators++;
565
635
  internalFormStore.isValidating.value = true;
566
- const result = await internalFormStore.parse(untrack(() => /* @__PURE__ */ getFieldInput(internalFormStore)));
567
- let rootErrors;
568
- let nestedErrors;
569
- if (result.issues) {
570
- nestedErrors = {};
571
- for (const issue of result.issues) if (issue.path) {
572
- const path = [];
573
- for (const pathItem of issue.path) {
574
- const key = pathItem.key;
575
- const keyType = typeof key;
576
- const itemType = pathItem.type;
577
- if (keyType !== "string" && keyType !== "number" || itemType === "map" || itemType === "set") break;
578
- path.push(key);
579
- }
580
- const name = JSON.stringify(path);
581
- const fieldErrors = nestedErrors[name];
582
- if (fieldErrors) fieldErrors.push(issue.message);
583
- else nestedErrors[name] = [issue.message];
584
- } else if (rootErrors) rootErrors.push(issue.message);
585
- else rootErrors = [issue.message];
586
- }
587
- let shouldFocus = config?.shouldFocus ?? false;
588
- batch(() => {
589
- walkFieldStore(internalFormStore, (internalFieldStore) => {
590
- if (internalFieldStore.name === "[]") internalFieldStore.errors.value = rootErrors ?? null;
591
- else {
592
- const fieldErrors = nestedErrors?.[internalFieldStore.name] ?? null;
593
- internalFieldStore.errors.value = fieldErrors;
594
- if (shouldFocus && fieldErrors) {
595
- internalFieldStore.elements[0]?.focus();
596
- shouldFocus = false;
636
+ try {
637
+ const result = await internalFormStore.parse(untrack(() => /* @__PURE__ */ getFieldInput(internalFormStore)));
638
+ let rootErrors;
639
+ let nestedErrors;
640
+ if (result.issues) {
641
+ nestedErrors = {};
642
+ for (const issue of result.issues) if (issue.path) {
643
+ const path = [];
644
+ for (const pathItem of issue.path) {
645
+ const key = pathItem.key;
646
+ const keyType = typeof key;
647
+ const itemType = pathItem.type;
648
+ if (keyType !== "string" && keyType !== "number" || itemType === "map" || itemType === "set") break;
649
+ path.push(key);
597
650
  }
598
- }
651
+ const name = JSON.stringify(path);
652
+ const fieldErrors = nestedErrors[name];
653
+ if (fieldErrors) fieldErrors.push(issue.message);
654
+ else nestedErrors[name] = [issue.message];
655
+ } else if (rootErrors) rootErrors.push(issue.message);
656
+ else rootErrors = [issue.message];
657
+ }
658
+ let shouldFocus = config?.shouldFocus ?? false;
659
+ batch(() => {
660
+ walkFieldStore(internalFormStore, (internalFieldStore) => {
661
+ if (internalFieldStore.name === "[]") internalFieldStore.errors.value = rootErrors ?? null;
662
+ else {
663
+ const fieldErrors = nestedErrors?.[internalFieldStore.name] ?? null;
664
+ internalFieldStore.errors.value = fieldErrors;
665
+ if (shouldFocus && fieldErrors && focusFieldElement(internalFieldStore)) shouldFocus = false;
666
+ }
667
+ });
668
+ internalFormStore.validators--;
669
+ internalFormStore.isValidating.value = internalFormStore.validators > 0;
599
670
  });
600
- internalFormStore.validators--;
601
- internalFormStore.isValidating.value = internalFormStore.validators > 0;
602
- });
603
- return result;
671
+ return result;
672
+ } catch (error) {
673
+ batch(() => {
674
+ internalFormStore.validators--;
675
+ internalFormStore.isValidating.value = internalFormStore.validators > 0;
676
+ });
677
+ throw error;
678
+ }
604
679
  }
605
680
  /**
606
681
  * Validates the form input if required based on the validation mode and form
@@ -622,7 +697,7 @@ const INTERNAL = "~internal";
622
697
  //#endregion
623
698
  //#region ../../packages/methods/dist/index.react.js
624
699
  /**
625
- * Focuses the first input element of a field. This is useful for
700
+ * Focuses the first focusable input element of a field. This is useful for
626
701
  * programmatically setting focus to a specific field, such as after
627
702
  * validation errors or user interactions.
628
703
  *
@@ -630,7 +705,7 @@ const INTERNAL = "~internal";
630
705
  * @param config The focus field configuration.
631
706
  */
632
707
  function focus(form, config) {
633
- getFieldStore(form[INTERNAL], config.path).elements[0]?.focus();
708
+ focusFieldElement(getFieldStore(form[INTERNAL], config.path));
634
709
  }
635
710
  /**
636
711
  * Retrieves all error messages from all fields in the form by walking through
@@ -653,6 +728,17 @@ function getAllErrors(form) {
653
728
  return allErrors;
654
729
  }
655
730
  /* @__NO_SIDE_EFFECTS__ */
731
+ function getDirtyInput(form, config) {
732
+ return getDirtyFieldInput(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL]);
733
+ }
734
+ /* @__NO_SIDE_EFFECTS__ */
735
+ function getDirtyPaths(form, config) {
736
+ config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL];
737
+ const paths = [];
738
+ config?.path && [...config.path];
739
+ return paths;
740
+ }
741
+ /* @__NO_SIDE_EFFECTS__ */
656
742
  function getErrors(form, config) {
657
743
  return (config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL]).errors.value;
658
744
  }
@@ -750,6 +836,48 @@ function move(form, config) {
750
836
  });
751
837
  }
752
838
  /**
839
+ * Picks only the dirty parts of the given value, using the form's dirty fields
840
+ * as a structural mask. Arrays are treated as atomic and object keys without a
841
+ * dirty descendant are omitted. Returns `undefined` if no field is dirty.
842
+ * Useful for filtering a validated output down to its changed parts before
843
+ * submitting.
844
+ *
845
+ * @param form The form store providing the dirty mask.
846
+ * @param config The pick dirty configuration.
847
+ *
848
+ * @returns The dirty parts of the value, or `undefined`.
849
+ */
850
+ /* @__NO_SIDE_EFFECTS__ */
851
+ function pickDirty(form, config) {
852
+ if (!getFieldBool(form[INTERNAL], "isDirty")) return;
853
+ const result = /* @__PURE__ */ pickFieldValue(form[INTERNAL], config.from);
854
+ return Object.keys(result).length ? result : void 0;
855
+ }
856
+ /**
857
+ * Recursively picks the dirty parts of a value using the field store as a
858
+ * structural mask, reading from the supplied value rather than the form's own
859
+ * input. Objects with non-nullish input recurse into their dirty children that
860
+ * are present in the value, while arrays, primitives, nullish-cleared fields
861
+ * and shape-diverging values are returned as-is.
862
+ *
863
+ * @param internalFieldStore The field store used as the dirty mask.
864
+ * @param value The value to pick the dirty parts from.
865
+ *
866
+ * @returns The dirty parts of the value.
867
+ */
868
+ /* @__NO_SIDE_EFFECTS__ */
869
+ function pickFieldValue(internalFieldStore, value) {
870
+ if (internalFieldStore.kind === "object" && internalFieldStore.input.value && value && typeof value === "object" && !Array.isArray(value)) {
871
+ const result = {};
872
+ for (const key in internalFieldStore.children) {
873
+ const child = internalFieldStore.children[key];
874
+ if (getFieldBool(child, "isDirty") && key in value) result[key] = /* @__PURE__ */ pickFieldValue(child, value[key]);
875
+ }
876
+ return result;
877
+ }
878
+ return value;
879
+ }
880
+ /**
753
881
  * Removes an item from a field array at the specified index. All items after
754
882
  * the removed item are shifted down by one index.
755
883
  *
@@ -913,7 +1041,9 @@ function useField(form, config) {
913
1041
  const internalFieldStore = getFieldStore(internalFormStore, config.path);
914
1042
  useEffect(() => {
915
1043
  return () => {
916
- internalFieldStore.elements = internalFieldStore.elements.filter((element) => element.isConnected);
1044
+ const elements = internalFieldStore.elements.filter((element) => element.isConnected);
1045
+ if (internalFieldStore.elements === internalFieldStore.initialElements) internalFieldStore.initialElements = elements;
1046
+ internalFieldStore.elements = elements;
917
1047
  };
918
1048
  }, [internalFieldStore]);
919
1049
  return useMemo(() => ({
@@ -1070,4 +1200,4 @@ function Form({ of, onSubmit, ...other }) {
1070
1200
  }
1071
1201
 
1072
1202
  //#endregion
1073
- export { Field, FieldArray, Form, focus, getAllErrors, getErrors, getInput, handleSubmit, insert, move, remove, replace, reset, setErrors, setInput, submit, swap, useField, useFieldArray, useForm, validate };
1203
+ export { Field, FieldArray, Form, focus, getAllErrors, getDirtyInput, getDirtyPaths, getErrors, getInput, handleSubmit, insert, move, pickDirty, remove, replace, reset, setErrors, setInput, submit, swap, useField, useFieldArray, useForm, validate };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@formisch/react",
3
3
  "description": "The lightweight, schema-first, and fully type-safe form library for React",
4
- "version": "0.4.6",
4
+ "version": "0.6.0",
5
5
  "license": "MIT",
6
6
  "author": "Fabian Hiller",
7
7
  "homepage": "https://formisch.dev",
@@ -51,7 +51,7 @@
51
51
  "@types/react": "^19.2.5",
52
52
  "@types/react-dom": "^19.2.3",
53
53
  "@vitejs/plugin-react": "^5.1.1",
54
- "@vitest/coverage-v8": "^3.2.4",
54
+ "@vitest/coverage-v8": "^4.1.7",
55
55
  "eslint": "^9.39.1",
56
56
  "eslint-plugin-react-hooks": "^7.0.1",
57
57
  "eslint-plugin-react-refresh": "^0.4.24",
@@ -62,13 +62,13 @@
62
62
  "tsdown": "^0.16.8",
63
63
  "typescript": "~5.9.3",
64
64
  "vite": "^7.2.4",
65
- "vitest": "^3.2.4"
65
+ "vitest": "^4.1.7"
66
66
  },
67
67
  "peerDependencies": {
68
68
  "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
69
69
  "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
70
70
  "typescript": ">=5",
71
- "valibot": "^1.0.0"
71
+ "valibot": "^1.4.1"
72
72
  },
73
73
  "peerDependenciesMeta": {
74
74
  "typescript": {