attaform 0.28.0 → 0.30.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/dist/abstract.d.mts +5 -5
- package/dist/abstract.mjs +4 -4
- package/dist/dev/abstract.mjs +4 -4
- package/dist/dev/chunks/dev-key-collision-warnings.mjs +47 -8
- package/dist/dev/chunks/devtools.mjs +1 -1
- package/dist/dev/directive.mjs +1 -1
- package/dist/dev/history.mjs +1 -2
- package/dist/dev/index.mjs +5 -5
- package/dist/dev/runtime/plugins/attaform.mjs +3 -3
- package/dist/dev/shared/{attaform-dev.oYrkP9dj.mjs → attaform-dev.BK6Ge2qR.mjs} +80 -341
- package/dist/dev/shared/{attaform-dev.Dr2YzS5B.mjs → attaform-dev.BQc5QV3Y.mjs} +11 -19
- package/dist/dev/shared/{attaform-dev.BR8u0YOZ.mjs → attaform-dev.BcNtyOmf.mjs} +4 -4
- package/dist/{shared/attaform.h0i6LijT.mjs → dev/shared/attaform-dev.BcYT6tFh.mjs} +152 -20
- package/dist/dev/shared/{attaform-dev.6qxD-Yqo.mjs → attaform-dev.BisFstxh.mjs} +227 -88
- package/dist/dev/shared/{attaform-dev.WmuhD_CZ.mjs → attaform-dev.CQ2rJiAX.mjs} +833 -656
- package/dist/dev/shared/{attaform-dev.P7cseiEG.mjs → attaform-dev.D6CLvaQT.mjs} +20 -11
- package/dist/dev/shared/{attaform-dev.D2I8e_Mm.mjs → attaform-dev.DTrHonpc.mjs} +225 -238
- package/dist/dev/shared/{attaform-dev.BXE2cr0L.mjs → attaform-dev.DUZckyV8.mjs} +1 -3
- package/dist/dev/zod-v3.mjs +6 -6
- package/dist/dev/zod-v4.mjs +6 -6
- package/dist/dev/zod.mjs +5 -5
- package/dist/directive.d.mts +2 -2
- package/dist/directive.mjs +1 -1
- package/dist/history.d.mts +1 -1
- package/dist/history.mjs +1 -2
- package/dist/index.d.mts +9 -7
- package/dist/index.mjs +5 -5
- package/dist/nuxt.d.mts +11 -20
- package/dist/nuxt.mjs +0 -1
- package/dist/runtime/plugins/attaform.mjs +3 -3
- package/dist/shared/{attaform.uLbun31C.mjs → attaform.B3resMQ1.mjs} +19 -11
- package/dist/shared/{attaform.Cs2Rbdlo.mjs → attaform.BK0cgu2K.mjs} +123 -139
- package/dist/{dev/shared/attaform-dev.By4PRjt0.mjs → shared/attaform.BRDDQeHV.mjs} +136 -26
- package/dist/shared/{attaform.NLmerepI.mjs → attaform.BXneBxEj.mjs} +71 -334
- package/dist/shared/{attaform.D6DCnnQu.mjs → attaform.ByWP6kSx.mjs} +4 -4
- package/dist/shared/{attaform.CJP3RkGa.mjs → attaform.CJ8DpRGj.mjs} +223 -238
- package/dist/shared/{attaform.C09fpdLS.mjs → attaform.CUfrMK7t.mjs} +227 -88
- package/dist/shared/{attaform.wfY8XFvm.mjs → attaform.CdZIEf7_.mjs} +11 -19
- package/dist/shared/{attaform.JfSWdEcb.d.mts → attaform.Clz0ArIi.d.mts} +1 -1
- package/dist/shared/{attaform.B8k8JrmK.mjs → attaform.DNJOWjQw.mjs} +826 -612
- package/dist/shared/{attaform.B4VkS43-.d.mts → attaform.Rc9u4Vfw.d.mts} +1 -1
- package/dist/shared/{attaform.D5V4tZWq.d.mts → attaform.UGyOK83T.d.mts} +200 -218
- package/dist/shared/{attaform.Dq4vWxuG.mjs → attaform.WiGdIqgx.mjs} +1 -3
- package/dist/shared/{attaform.Cgu-hdSb.d.mts → attaform.bKLzMU0o.d.mts} +321 -523
- package/dist/transforms.d.mts +0 -22
- package/dist/transforms.mjs +1 -1
- package/dist/vite.d.mts +13 -4
- package/dist/vite.mjs +8 -2
- package/dist/zod-v3.d.mts +6 -4
- package/dist/zod-v3.mjs +6 -6
- package/dist/zod-v4.d.mts +18 -31
- package/dist/zod-v4.mjs +6 -6
- package/dist/zod.d.mts +4 -4
- package/dist/zod.mjs +5 -5
- package/package.json +19 -15
- package/skills/attaform/SKILL.md +6 -3
- package/skills/attaform/references/custom-components.md +8 -3
- package/skills/attaform/references/errors.md +8 -3
- package/skills/attaform/references/saving.md +77 -0
- package/skills/attaform/references/wizards.md +10 -8
- package/dist/chunks/fingerprint.mjs +0 -183
- package/dist/chunks/fingerprint2.mjs +0 -159
- package/dist/dev/chunks/fingerprint.mjs +0 -183
- package/dist/dev/chunks/fingerprint2.mjs +0 -159
- package/dist/dev/shared/attaform-dev.BJGA_UOS.mjs +0 -36
- package/dist/shared/attaform.BJGA_UOS.mjs +0 -36
|
@@ -359,6 +359,48 @@ type JoinSegments<S extends ReadonlyArray<string | number>, Acc extends string =
|
|
|
359
359
|
infer Head extends string | number,
|
|
360
360
|
...infer Rest extends ReadonlyArray<string | number>
|
|
361
361
|
] ? Acc extends '' ? JoinSegments<Rest, `${Head}`> : JoinSegments<Rest, `${Acc}.${Head}`> : Acc;
|
|
362
|
+
/**
|
|
363
|
+
* The advice half of a segment-array rejection, used when `S` never
|
|
364
|
+
* inferred a tuple at all.
|
|
365
|
+
*
|
|
366
|
+
* A caller who passes a plain `string` reaches this branch, because
|
|
367
|
+
* inference falls back to the constraint and `JoinSegments` of a
|
|
368
|
+
* non-tuple array is `''`. Naming a path would be a lie there, so the
|
|
369
|
+
* message names the remedies instead. This is the case behind #568: a
|
|
370
|
+
* row component received its path prefix as an untyped `string` prop,
|
|
371
|
+
* every concatenation widened to `string`, and the only signal was
|
|
372
|
+
* `segments: never`.
|
|
373
|
+
*/
|
|
374
|
+
type PlainStringAdvice = 'attaform: a plain string cannot be checked against the schema. Pass a literal path, type the dynamic prefix, or use the segment-array form.';
|
|
375
|
+
/**
|
|
376
|
+
* Why a segment array was rejected, as a type the compiler prints.
|
|
377
|
+
*
|
|
378
|
+
* Every segment-array overload brands its parameter
|
|
379
|
+
* `S & (<joined path is valid> ? unknown : <this>)`. The brand used to
|
|
380
|
+
* be `never`, and `S & never` is `never`, so TypeScript reported the
|
|
381
|
+
* parameter as `never` and said nothing about what was wrong. A string
|
|
382
|
+
* literal intersects without collapsing, so the sentence survives into
|
|
383
|
+
* the diagnostic and the argument keeps its inferred tuple type:
|
|
384
|
+
*
|
|
385
|
+
* ```
|
|
386
|
+
* Argument of type '["boxes", 3, "nope"]' is not assignable to parameter
|
|
387
|
+
* of type 'readonly ["boxes", 3, "nope"] &
|
|
388
|
+
* "attaform: 'boxes.3.nope' is not a path in this form's schema"'.
|
|
389
|
+
* ```
|
|
390
|
+
*
|
|
391
|
+
* Nothing can satisfy the brand, so a wrong path is still a compile
|
|
392
|
+
* error; only the message changes.
|
|
393
|
+
*/
|
|
394
|
+
type SegmentPathRejection<Joined extends string> = [Joined] extends [''] ? PlainStringAdvice : `attaform: '${Joined}' is not a path in this form's schema`;
|
|
395
|
+
/**
|
|
396
|
+
* `SegmentPathRejection` for `register`, whose accepted set is
|
|
397
|
+
* `RegisterFlatPath` rather than `FlatPath`.
|
|
398
|
+
*
|
|
399
|
+
* The distinction is worth its own sentence: a container path is in the
|
|
400
|
+
* schema and still not registrable, so "not a path in this form's
|
|
401
|
+
* schema" would be false where it is most likely to be read.
|
|
402
|
+
*/
|
|
403
|
+
type SegmentRegisterRejection<Joined extends string> = [Joined] extends [''] ? PlainStringAdvice : `attaform: '${Joined}' is not a registrable path. v-register binds a leaf input, so container paths are excluded.`;
|
|
362
404
|
/**
|
|
363
405
|
* `true` when `T` is a union (multiple members), `false` when it's a
|
|
364
406
|
* single type. Used to gate non-homomorphic mapped-type forms so
|
|
@@ -523,44 +565,89 @@ type IsTuple<T extends readonly unknown[]> = number extends T['length'] ? false
|
|
|
523
565
|
* runtime possibility of a missing array position.
|
|
524
566
|
*/
|
|
525
567
|
type NestedReadType<RootValue, FlattenedPath extends string> = NestedTypeBuilder<RootValue, FlattenedPath, true>;
|
|
568
|
+
/**
|
|
569
|
+
* The array a path actually addresses, nullish stripped, or `never`
|
|
570
|
+
* when the path does not address an array. Shared by `ArrayPath`
|
|
571
|
+
* (membership) and `ArrayItem` (element type) so the filter and the
|
|
572
|
+
* extractor cannot disagree about what counts as one.
|
|
573
|
+
*
|
|
574
|
+
* The strip is the load-bearing part. `Form` is the schema's INPUT
|
|
575
|
+
* shape, so `z.array(row).default([])` resolves to `row[] | undefined`,
|
|
576
|
+
* and `NestedType` tags a discriminated union's per-variant keys the
|
|
577
|
+
* same way on purpose: that tagging is what keeps `NestedType` in
|
|
578
|
+
* lockstep with `FlatPath`, so every path `FlatPath` offers resolves to
|
|
579
|
+
* a useful value type. A bare `extends readonly unknown[]` predicate
|
|
580
|
+
* read the tag as "not an array", which is how every optional,
|
|
581
|
+
* defaulted, nullable, and DU-variant array fell out of the field-array
|
|
582
|
+
* helpers while the runtime went on accepting all of them (#541).
|
|
583
|
+
*
|
|
584
|
+
* `extends infer Leaf` binds the stripped leaf once so the guards below
|
|
585
|
+
* do not re-instantiate `NestedType`, and the tuple wraps suppress
|
|
586
|
+
* distribution so a union leaf is judged whole: `A[] | B[]` is an
|
|
587
|
+
* array, `A[] | string` is not.
|
|
588
|
+
*
|
|
589
|
+
* Exported so `rollup-plugin-dts` keeps it as a named alias in the
|
|
590
|
+
* bundled `.d.ts` instead of inlining the body at every reference.
|
|
591
|
+
* Consumers reach for `ArrayPath` / `ArrayItem`; this alias is not part
|
|
592
|
+
* of the stable surface.
|
|
593
|
+
*/
|
|
594
|
+
type ArrayLeafOf<Form, P extends string> = Exclude<NestedType<Form, P>, undefined | null> extends infer Leaf ? [Leaf] extends [never] ? never : [Leaf] extends [readonly unknown[]] ? Leaf : never : never;
|
|
526
595
|
/**
|
|
527
596
|
* Filter FlatPath<Form> down to the subset of paths whose resolved leaf
|
|
528
597
|
* is an array. Used by the typed field-array helpers (append / remove /
|
|
529
598
|
* swap / ...) so those helpers only accept paths that actually address
|
|
530
599
|
* an array — calling `append('email', ...)` on a `{ email: string }`
|
|
531
|
-
* is a compile error.
|
|
600
|
+
* is a compile error. Optionality is not a disqualifier; see
|
|
601
|
+
* `ArrayLeafOf` for what "is an array" means here.
|
|
532
602
|
*
|
|
533
603
|
* `P extends string` re-triggers distribution over the `FlatPath<Form>`
|
|
534
604
|
* union so the conditional evaluates per member. Without it, the
|
|
535
605
|
* branch would reduce against the union as a whole and collapse to
|
|
536
606
|
* `never` whenever a single member failed the predicate.
|
|
537
607
|
*/
|
|
538
|
-
type ArrayPath<Form, P extends FlatPath<Form> = FlatPath<Form>> = P extends string ?
|
|
608
|
+
type ArrayPath<Form, P extends FlatPath<Form> = FlatPath<Form>> = P extends string ? [ArrayLeafOf<Form, P>] extends [never] ? never : P : never;
|
|
539
609
|
/**
|
|
540
610
|
* Extract the element type of the array addressed by `Path`. Callers
|
|
541
611
|
* constrain `Path extends ArrayPath<Form>` so this is always well-defined.
|
|
612
|
+
* Rides on `ArrayLeafOf`, the same alias `ArrayPath` admits the path
|
|
613
|
+
* with, so an accepted path never hands its helper a `never` value slot.
|
|
614
|
+
*/
|
|
615
|
+
type ArrayItem<Form, Path extends ArrayPath<Form>> = ArrayLeafOf<Form, Path> extends ReadonlyArray<infer Item> ? Item : never;
|
|
616
|
+
/**
|
|
617
|
+
* The record a path actually addresses, nullish stripped, or `never`
|
|
618
|
+
* when the path does not address one. Shared by `RecordPath`
|
|
619
|
+
* (membership) and `RecordValue` (value type).
|
|
620
|
+
*
|
|
621
|
+
* `string extends keyof Leaf` is the index-signature probe: it holds for
|
|
622
|
+
* `Record<string, V>` (`keyof` is `string`) and fails for a fixed object
|
|
623
|
+
* (`keyof` is the literal key union). The leading array guard keeps
|
|
624
|
+
* arrays (which also satisfy the object check) out of the record set.
|
|
625
|
+
*
|
|
626
|
+
* Nullish is stripped first for the reason `ArrayLeafOf` gives, with an
|
|
627
|
+
* extra bite here: `keyof (Record<string, V> | undefined)` is `never`,
|
|
628
|
+
* so on a `.default({})` or `.optional()` record the probe could not
|
|
629
|
+
* fire at all and `form.record(path)` rejected the path (#541).
|
|
630
|
+
*
|
|
631
|
+
* Exported for the same `.d.ts` reason as `ArrayLeafOf`, and equally
|
|
632
|
+
* not part of the stable surface.
|
|
542
633
|
*/
|
|
543
|
-
type
|
|
634
|
+
type RecordLeafOf<Form, P extends string> = Exclude<NestedType<Form, P>, undefined | null> extends infer Leaf ? [Leaf] extends [never] ? never : [Leaf] extends [readonly unknown[]] ? never : [Leaf] extends [Record<string, unknown>] ? string extends keyof Leaf ? Leaf : never : never : never;
|
|
544
635
|
/**
|
|
545
636
|
* Companion to `ArrayPath`: filter `FlatPath<Form>` down to the subset
|
|
546
637
|
* of paths whose resolved leaf is a record (an object with an open
|
|
547
638
|
* string-keyed index signature, e.g. `z.record(z.string(), V)`). A
|
|
548
639
|
* fixed-shape object (`z.object({ ... })`) is excluded — its keys are
|
|
549
640
|
* statically known, so it has no `string` index signature.
|
|
550
|
-
*
|
|
551
|
-
* `string extends keyof T` is the index-signature probe: it holds for
|
|
552
|
-
* `Record<string, V>` (`keyof` is `string`) and fails for a fixed object
|
|
553
|
-
* (`keyof` is the literal key union). The leading array guard keeps
|
|
554
|
-
* arrays (which also satisfy the object check) out of the record set.
|
|
555
641
|
*/
|
|
556
|
-
type RecordPath<Form, P extends FlatPath<Form> = FlatPath<Form>> = P extends string ?
|
|
642
|
+
type RecordPath<Form, P extends FlatPath<Form> = FlatPath<Form>> = P extends string ? [RecordLeafOf<Form, P>] extends [never] ? never : P : never;
|
|
557
643
|
/**
|
|
558
644
|
* Value type of the record addressed by `Path` — the `V` in a
|
|
559
645
|
* `Record<string, V>`. Callers constrain `Path extends RecordPath<Form>`,
|
|
560
646
|
* so the leaf is always an open string-keyed record and this is
|
|
561
|
-
* well-defined.
|
|
647
|
+
* well-defined. Rides on `RecordLeafOf`, the same alias `RecordPath`
|
|
648
|
+
* admits the path with, so the two cannot disagree.
|
|
562
649
|
*/
|
|
563
|
-
type RecordValue<Form, Path extends RecordPath<Form>> =
|
|
650
|
+
type RecordValue<Form, Path extends RecordPath<Form>> = RecordLeafOf<Form, Path> extends Record<string, infer Value> ? Value : never;
|
|
564
651
|
/**
|
|
565
652
|
* Widens primitive-literal leaves to their primitive supertype to
|
|
566
653
|
* match the runtime "slim-primitive write contract."
|
|
@@ -751,12 +838,12 @@ type AcceptableDefaultsOf<DVI, SchemaInput> = DVI | SchemaInput | (() => DVI | S
|
|
|
751
838
|
type FormKey = string;
|
|
752
839
|
/**
|
|
753
840
|
* Per-form options threaded from `useForm` into the adapter factory.
|
|
754
|
-
* Today carries
|
|
755
|
-
*
|
|
756
|
-
*
|
|
841
|
+
* Today carries `maxRecursionDepth` so adapter walks can cap their
|
|
842
|
+
* descent through recursive schemas; future per-form runtime knobs
|
|
843
|
+
* land here too.
|
|
757
844
|
*/
|
|
758
845
|
interface SchemaFactoryOptions {
|
|
759
|
-
/**
|
|
846
|
+
/** Recursion ceiling for walks through recursive schemas. */
|
|
760
847
|
maxRecursionDepth: number;
|
|
761
848
|
}
|
|
762
849
|
/**
|
|
@@ -861,27 +948,49 @@ type ErrorInput = Error | {
|
|
|
861
948
|
code?: string;
|
|
862
949
|
data?: Json | null;
|
|
863
950
|
};
|
|
864
|
-
/**
|
|
865
|
-
|
|
951
|
+
/**
|
|
952
|
+
* A parse verdict as an `AbstractSchema` produces it.
|
|
953
|
+
*
|
|
954
|
+
* A schema knows nothing about which form asked: it is a pure function
|
|
955
|
+
* of the schema it wraps, which is what lets one `AbstractSchema` serve
|
|
956
|
+
* every form built on the same schema. The owning store stamps its own
|
|
957
|
+
* `formKey` on the way out, producing the `ValidationResponse*` arms
|
|
958
|
+
* below. Adapter authors implement THESE.
|
|
959
|
+
*/
|
|
960
|
+
type SchemaParseSuccess<TData> = {
|
|
866
961
|
/** The parsed value at the validated subtree (whole form when `validate()` was called without a path). */
|
|
867
962
|
data: TData;
|
|
868
963
|
errors: undefined;
|
|
869
964
|
success: true;
|
|
870
|
-
formKey: FormKey;
|
|
871
965
|
};
|
|
872
|
-
/**
|
|
873
|
-
type
|
|
966
|
+
/** Schema-level verdict when no data could be produced (e.g. a top-level type mismatch). */
|
|
967
|
+
type SchemaParseErrorWithoutData = {
|
|
874
968
|
data: undefined;
|
|
875
969
|
/** Non-empty list of failures. */
|
|
876
970
|
errors: ValidationError[];
|
|
877
971
|
success: false;
|
|
878
|
-
formKey: FormKey;
|
|
879
972
|
};
|
|
880
|
-
/**
|
|
881
|
-
type
|
|
973
|
+
/** Schema-level verdict when the parser produced partial data alongside failures. */
|
|
974
|
+
type SchemaParseErrorWithData<TData> = {
|
|
882
975
|
data: TData;
|
|
883
976
|
errors: ValidationError[];
|
|
884
977
|
success: false;
|
|
978
|
+
};
|
|
979
|
+
/** Settled schema-level verdict. Discriminate on `success`. */
|
|
980
|
+
type SchemaParseResult<TData> = SchemaParseSuccess<TData> | SchemaParseErrorWithData<TData> | SchemaParseErrorWithoutData;
|
|
981
|
+
/** Schema-level `getDefaultValues` verdict: defaults always come back. */
|
|
982
|
+
type SchemaDefaultsResult<TData> = SchemaParseSuccess<TData> | SchemaParseErrorWithData<TData>;
|
|
983
|
+
/** Settled validation result when the form (or subtree) parsed successfully. */
|
|
984
|
+
type ValidationResponseSuccess<TData> = SchemaParseSuccess<TData> & {
|
|
985
|
+
/** The form this verdict belongs to. Stamped by the store, not the schema. */
|
|
986
|
+
formKey: FormKey;
|
|
987
|
+
};
|
|
988
|
+
/** Settled validation result when no data could be produced (e.g. a top-level type mismatch). */
|
|
989
|
+
type ValidationResponseErrorWithoutData = SchemaParseErrorWithoutData & {
|
|
990
|
+
formKey: FormKey;
|
|
991
|
+
};
|
|
992
|
+
/** Settled validation result when the parser produced partial data alongside failures. */
|
|
993
|
+
type ValidationResponseErrorWithData<TData> = SchemaParseErrorWithData<TData> & {
|
|
885
994
|
formKey: FormKey;
|
|
886
995
|
};
|
|
887
996
|
/**
|
|
@@ -896,12 +1005,6 @@ type ValidationResponseErrorWithData<TData> = {
|
|
|
896
1005
|
* ```
|
|
897
1006
|
*/
|
|
898
1007
|
type ValidationResponse<TData> = ValidationResponseSuccess<TData> | ValidationResponseErrorWithData<TData> | ValidationResponseErrorWithoutData;
|
|
899
|
-
/**
|
|
900
|
-
* Result of resolving the form's default values. Always returns at
|
|
901
|
-
* least the shape derived from the schema; `errors` carry any
|
|
902
|
-
* failures from validating those defaults against the schema.
|
|
903
|
-
*/
|
|
904
|
-
type DefaultValuesResponse<TData> = ValidationResponseSuccess<TData> | ValidationResponseErrorWithData<TData>;
|
|
905
1008
|
/**
|
|
906
1009
|
* Trimmed `ValidationResponse` that omits the `data` payload. Used by
|
|
907
1010
|
* the reactive `validate()` status, whose consumers only need the
|
|
@@ -949,21 +1052,13 @@ type ValidateOptions = {
|
|
|
949
1052
|
/**
|
|
950
1053
|
* Configuration passed to `AbstractSchema.getDefaultValues`. Adapters
|
|
951
1054
|
* receive `useDefaultSchemaValues` (honor `.default(x)` wrappers vs.
|
|
952
|
-
* empty/falsy fallbacks)
|
|
953
|
-
*
|
|
954
|
-
*
|
|
955
|
-
*
|
|
956
|
-
* service contract.
|
|
1055
|
+
* empty/falsy fallbacks) and an optional `constraints` overlay merged
|
|
1056
|
+
* into the derived defaults so the runtime can stamp user-supplied
|
|
1057
|
+
* defaults at construction. Exported so adapter authors can
|
|
1058
|
+
* co-implement the service contract.
|
|
957
1059
|
*/
|
|
958
1060
|
type GetDefaultValuesConfig<Form> = {
|
|
959
1061
|
useDefaultSchemaValues: boolean;
|
|
960
|
-
/**
|
|
961
|
-
* Whether to keep schema refinements when deriving slim defaults.
|
|
962
|
-
* `true` (default) — preserve refinements; `false` — strip them so
|
|
963
|
-
* placeholder data lands without immediate construction-time
|
|
964
|
-
* errors. Mirrors `useForm({ strict })`.
|
|
965
|
-
*/
|
|
966
|
-
strict?: boolean;
|
|
967
1062
|
constraints?: DeepPartial<WriteShape<Form>> | undefined;
|
|
968
1063
|
};
|
|
969
1064
|
/**
|
|
@@ -977,45 +1072,7 @@ type GetDefaultValuesConfig<Form> = {
|
|
|
977
1072
|
* adding support for a new schema library (Valibot, ArkType, custom).
|
|
978
1073
|
*/
|
|
979
1074
|
type AbstractSchema<Form, GetValueFormType> = {
|
|
980
|
-
|
|
981
|
-
* Structural fingerprint of the schema. Same shape → same string;
|
|
982
|
-
* different shape → (best-effort) different string.
|
|
983
|
-
*
|
|
984
|
-
* Resolves a `Promise` so adapters can defer the structural walk (and
|
|
985
|
-
* its `canonicalStringify` helper) onto a dynamic import. The framework
|
|
986
|
-
* only ever needs the fingerprint for the dev-only shared-key schema
|
|
987
|
-
* mismatch warning, so none of those bytes belong on the eager
|
|
988
|
-
* `useForm` path.
|
|
989
|
-
*
|
|
990
|
-
* The library uses this to detect schema mismatches at a shared
|
|
991
|
-
* form key: two `useForm({ key: 'x', schema })` calls are allowed
|
|
992
|
-
* to land on the same `FormStore` (the "shared store" semantic),
|
|
993
|
-
* but only when their schemas agree. If the second call's
|
|
994
|
-
* fingerprint differs from the first's, the library emits a
|
|
995
|
-
* dev-mode warning — the first call's schema stays canonical and
|
|
996
|
-
* the second call's schema is silently ignored.
|
|
997
|
-
*
|
|
998
|
-
* Guarantees adapter authors should provide:
|
|
999
|
-
* - **Determinism:** equal shapes at different memory addresses
|
|
1000
|
-
* must produce the same fingerprint. Referential equality fails
|
|
1001
|
-
* 99% of the time across files, so reference-identity is not a
|
|
1002
|
-
* substitute.
|
|
1003
|
-
* - **Key-order-insensitivity** for record-like shapes (object,
|
|
1004
|
-
* struct) — two shapes with the same keys but different iteration
|
|
1005
|
-
* order must match.
|
|
1006
|
-
* - **Order-insensitivity for unbounded unions** — `a | b` and
|
|
1007
|
-
* `b | a` must match (the set of members is what matters, not
|
|
1008
|
-
* their source order).
|
|
1009
|
-
*
|
|
1010
|
-
* Compromises adapter authors may accept:
|
|
1011
|
-
* - Function-valued metadata (refinements, transforms, lazy
|
|
1012
|
-
* defaults) is not stably hashable. Represent it as an opaque
|
|
1013
|
-
* sentinel; two schemas differing only in refinement logic will
|
|
1014
|
-
* look identical. The warning is a footgun catcher, not a
|
|
1015
|
-
* soundness guarantee.
|
|
1016
|
-
*/
|
|
1017
|
-
fingerprint(): Promise<string>;
|
|
1018
|
-
getDefaultValues(config: GetDefaultValuesConfig<Form>): DefaultValuesResponse<Form>;
|
|
1075
|
+
getDefaultValues(config: GetDefaultValuesConfig<Form>): SchemaDefaultsResult<Form>;
|
|
1019
1076
|
/**
|
|
1020
1077
|
* Return the schema-prescribed default value at the given path. The
|
|
1021
1078
|
* runtime uses this to fill structural gaps so every `setValue` write
|
|
@@ -1088,6 +1145,28 @@ type AbstractSchema<Form, GetValueFormType> = {
|
|
|
1088
1145
|
* the result by canonicalized path key.
|
|
1089
1146
|
*/
|
|
1090
1147
|
isPreprocessOrCoerceLeaf(path: Path): boolean;
|
|
1148
|
+
/**
|
|
1149
|
+
* Reports whether `path` resolves to an opaque leaf: a schema that
|
|
1150
|
+
* declares a value without describing its shape. In Zod v4 that's
|
|
1151
|
+
* `z.any()`, `z.unknown()`, and `z.custom(...)` (the kind both
|
|
1152
|
+
* `z.instanceof(X)` and `z.custom<T>()` compile to); in Zod v3 it's
|
|
1153
|
+
* `z.any()` / `z.unknown()`, with `z.instanceof(X)` peeling through
|
|
1154
|
+
* `ZodEffects` to `ZodAny`.
|
|
1155
|
+
*
|
|
1156
|
+
* Consulted by the slim-primitive write gate. An opaque leaf admits
|
|
1157
|
+
* every kind, INCLUDING containers, and it declares no sub-paths.
|
|
1158
|
+
* Without this the gate accepts an array at the leaf and then walks
|
|
1159
|
+
* into it, checking `files.0` against a schema that never declared
|
|
1160
|
+
* that path; the empty accept set there reads as "not in your
|
|
1161
|
+
* schema" and no-ops the whole write (#542).
|
|
1162
|
+
*
|
|
1163
|
+
* Exact-path semantic, unlike `isPreprocessOrCoerceLeaf`: it answers
|
|
1164
|
+
* for the node at `path` only. Descendants stay unwritable on their
|
|
1165
|
+
* own, because the schema genuinely does not declare them and
|
|
1166
|
+
* fabricating them would put phantom nodes on `form.fields`. Writing
|
|
1167
|
+
* the whole value at the opaque leaf is the supported move.
|
|
1168
|
+
*/
|
|
1169
|
+
isOpaqueLeafAtPath(path: Path): boolean;
|
|
1091
1170
|
/**
|
|
1092
1171
|
* Distinguish a tuple (fixed-length, position-typed) from an
|
|
1093
1172
|
* unbounded array at `path`. The runtime calls this on every
|
|
@@ -1133,6 +1212,29 @@ type AbstractSchema<Form, GetValueFormType> = {
|
|
|
1133
1212
|
* `false`.
|
|
1134
1213
|
*/
|
|
1135
1214
|
isFixedObjectAtPath(path: Path): boolean;
|
|
1215
|
+
/**
|
|
1216
|
+
* How the container at `path` spells the keys of its own entries:
|
|
1217
|
+
* `'number'` for an array or a tuple, `'string'` for an object or a
|
|
1218
|
+
* record, and for a `z.map` the kind its declared key type accepts.
|
|
1219
|
+
* `undefined` for anything else, a leaf and an undeclared path
|
|
1220
|
+
* included.
|
|
1221
|
+
*
|
|
1222
|
+
* The write walkers consult it at a `Map`, where a new entry has to
|
|
1223
|
+
* be filed under a key of the declared type and the path segment
|
|
1224
|
+
* alone cannot say which: an integer-looking segment canonicalises
|
|
1225
|
+
* to a number, so `scores.42` against a `z.map(z.string(), V)` would
|
|
1226
|
+
* otherwise take the number `42` as its key and fail the map's own
|
|
1227
|
+
* parse. An entry the map already holds needs no ruling — its
|
|
1228
|
+
* existing key wins.
|
|
1229
|
+
*
|
|
1230
|
+
* A map whose key type is neither string-ish nor number-ish, or
|
|
1231
|
+
* which admits both, reports `undefined`: no segment can spell such
|
|
1232
|
+
* a key, so the map stays a whole value with no addressable entries.
|
|
1233
|
+
*
|
|
1234
|
+
* Adapters MAY cache per path; the runtime calls this on the write
|
|
1235
|
+
* path.
|
|
1236
|
+
*/
|
|
1237
|
+
entryKeyKindAtPath(path: Path): 'string' | 'number' | undefined;
|
|
1136
1238
|
/**
|
|
1137
1239
|
* Return every sub-schema that could resolve at the given structured
|
|
1138
1240
|
* path. Multiple results are only expected for discriminated / union
|
|
@@ -1170,7 +1272,7 @@ type AbstractSchema<Form, GetValueFormType> = {
|
|
|
1170
1272
|
* returned as a `success: false` response with a populated
|
|
1171
1273
|
* `errors` array.
|
|
1172
1274
|
*/
|
|
1173
|
-
validateAtPath(data: unknown, path: Path | undefined, options?: ValidateOptions): MaybePromise<
|
|
1275
|
+
validateAtPath(data: unknown, path: Path | undefined, options?: ValidateOptions): MaybePromise<SchemaParseResult<GetValueFormType>>;
|
|
1174
1276
|
/**
|
|
1175
1277
|
* Sync sister to `getSchemasAtPath` / `validateAtPath`. Returns the
|
|
1176
1278
|
* set of primitive `typeof`-style kinds the path's leaf schema
|
|
@@ -1201,11 +1303,14 @@ type AbstractSchema<Form, GetValueFormType> = {
|
|
|
1201
1303
|
* - For nullable / optional wrappers: adds `'null'` / `'undefined'`
|
|
1202
1304
|
* to the inner's set.
|
|
1203
1305
|
*/
|
|
1204
|
-
getSlimPrimitiveTypesAtPath(path: Path):
|
|
1306
|
+
getSlimPrimitiveTypesAtPath(path: Path): ReadonlySet<SlimPrimitiveKind>;
|
|
1205
1307
|
/**
|
|
1206
|
-
* Return `true` iff `path` resolves to a **leaf** in the schema
|
|
1207
|
-
* path
|
|
1208
|
-
*
|
|
1308
|
+
* Return `true` iff `path` resolves to a **leaf** in the schema: a
|
|
1309
|
+
* path the schema declares no sub-paths under. That is every path
|
|
1310
|
+
* whose slim primitive set contains only primitive kinds (no
|
|
1311
|
+
* `object`, `array`, `map`, `set`), plus the opaque leaves, which
|
|
1312
|
+
* admit every kind and are classified by kind instead (below). The
|
|
1313
|
+
* runtime proxies (`form.values`,
|
|
1209
1314
|
* `form.errors`, `form.fields`) query this at every step to decide
|
|
1210
1315
|
* between **descend into a sub-proxy** (container) and **terminate
|
|
1211
1316
|
* with a leaf value** (leaf).
|
|
@@ -1223,6 +1328,14 @@ type AbstractSchema<Form, GetValueFormType> = {
|
|
|
1223
1328
|
* date/function) → `true`. `'date'` counts as a leaf (don't drill
|
|
1224
1329
|
* into `Date`). `'function'` is a leaf for the same reason — opaque
|
|
1225
1330
|
* value.
|
|
1331
|
+
* - **Opaque leaf** (`z.any()`, `z.unknown()`, `z.custom()`, and the
|
|
1332
|
+
* `z.instanceof(X)` that compiles to it) → `true`, and it answers
|
|
1333
|
+
* ahead of the kind test. An opaque leaf admits every kind
|
|
1334
|
+
* INCLUDING the container ones, so reading it off the slim
|
|
1335
|
+
* primitive set alone classifies it as a container and puts
|
|
1336
|
+
* phantom nodes under it on `form.fields` — see
|
|
1337
|
+
* `isOpaqueLeafAtPath`. What leafness asks is whether the schema
|
|
1338
|
+
* declares sub-paths here, and an opaque leaf declares none.
|
|
1226
1339
|
* - **Optional / Nullable / Default / Catch** wrappers transparent —
|
|
1227
1340
|
* adds `'null'` / `'undefined'` to the inner kind set without
|
|
1228
1341
|
* changing the leaf classification.
|
|
@@ -1470,38 +1583,6 @@ type SettledValidationStatus<Form> = {
|
|
|
1470
1583
|
* `pending` to switch between in-flight and settled states.
|
|
1471
1584
|
*/
|
|
1472
1585
|
type ReactiveValidationStatus<Form> = PendingValidationStatus | SettledValidationStatus<Form>;
|
|
1473
|
-
/**
|
|
1474
|
-
* What to do when a submit attempt fails validation. The library can
|
|
1475
|
-
* focus and/or scroll the first errored field into view without you
|
|
1476
|
-
* wiring an `onError` callback yourself. Defaults to
|
|
1477
|
-
* `'focus-first-error'` because moving keyboard / screen-reader focus
|
|
1478
|
-
* to the broken field on submit is an accessibility baseline; opt out
|
|
1479
|
-
* with `'none'` if you're managing focus elsewhere.
|
|
1480
|
-
*
|
|
1481
|
-
* - `'focus-first-error'` (default): focus the first errored field's
|
|
1482
|
-
* first visible element. Modern browsers scroll the focused element
|
|
1483
|
-
* into view by default; pair with `'both'` if you want an explicit
|
|
1484
|
-
* scroll alongside.
|
|
1485
|
-
* - `'scroll-to-first-error'`: scroll that element into view without
|
|
1486
|
-
* moving focus.
|
|
1487
|
-
* - `'both'`: scroll first, then focus (with `preventScroll: true` so
|
|
1488
|
-
* the browser doesn't undo the explicit scroll).
|
|
1489
|
-
* - `'none'`: no automatic UI nudge; the dev handles focus / scroll
|
|
1490
|
-
* manually via `form.focusFirstError()` or `form.scrollToFirstError()`
|
|
1491
|
-
* from an `onError` callback.
|
|
1492
|
-
*
|
|
1493
|
-
* Both focusing policies request `focusVisible: true`, so the browser
|
|
1494
|
-
* paints a focus ring even though the move is programmatic. Where a UA
|
|
1495
|
-
* doesn't yet support that hint, non-text controls (radio, checkbox,
|
|
1496
|
-
* custom widgets) may end up focused without a visible ring after a
|
|
1497
|
-
* pointer-driven submit; if you target those browsers, pair the policy
|
|
1498
|
-
* with your own indicator (e.g. a `:focus-within` ring on the option
|
|
1499
|
-
* wrapper).
|
|
1500
|
-
*
|
|
1501
|
-
* If no errored field has a currently mounted, visible element, the
|
|
1502
|
-
* policy silently no-ops.
|
|
1503
|
-
*/
|
|
1504
|
-
type OnInvalidSubmitPolicy = 'none' | 'focus-first-error' | 'scroll-to-first-error' | 'both';
|
|
1505
1586
|
/**
|
|
1506
1587
|
* When per-field VALIDATION runs. Only validation timing varies per
|
|
1507
1588
|
* mode; storage commit timing is the directive's concern (the
|
|
@@ -1695,8 +1776,7 @@ type HistoryModule = {
|
|
|
1695
1776
|
* and `undo()` cannot reach the transient pre-hydration default.
|
|
1696
1777
|
*
|
|
1697
1778
|
* One plugin instance is a reusable configuration, not per-form state:
|
|
1698
|
-
* passing the same instance to several forms
|
|
1699
|
-
* `createAttaform({ defaults: { history } })`) gives each form its own
|
|
1779
|
+
* passing the same instance to several forms gives each form its own
|
|
1700
1780
|
* independent chain.
|
|
1701
1781
|
*/
|
|
1702
1782
|
type HistoryPlugin = {
|
|
@@ -1780,11 +1860,9 @@ type UseFormConfiguration<Form extends GenericForm, GetValueFormType, Schema ext
|
|
|
1780
1860
|
*
|
|
1781
1861
|
* For schemas that depend on the form's identity or per-form
|
|
1782
1862
|
* options, pass a factory `(key, options) => schema` instead — the
|
|
1783
|
-
* library calls it once per form
|
|
1784
|
-
*
|
|
1785
|
-
*
|
|
1786
|
-
* use it to thread the resolved recursion cap into the adapter
|
|
1787
|
-
* closure.
|
|
1863
|
+
* library calls it once per form. Most adapters ignore the options
|
|
1864
|
+
* argument; the typed Zod entry points use it to thread the
|
|
1865
|
+
* recursion cap into the adapter closure.
|
|
1788
1866
|
*/
|
|
1789
1867
|
schema: Schema | ((key: FormKey, options: SchemaFactoryOptions) => Schema);
|
|
1790
1868
|
/**
|
|
@@ -1842,40 +1920,29 @@ type UseFormConfiguration<Form extends GenericForm, GetValueFormType, Schema ext
|
|
|
1842
1920
|
*/
|
|
1843
1921
|
defaultValues?: DefaultValues | (() => DefaultValues) | (() => Promise<DefaultValues>);
|
|
1844
1922
|
/**
|
|
1845
|
-
*
|
|
1846
|
-
*
|
|
1923
|
+
* Move keyboard / screen-reader focus to the first errored field when
|
|
1924
|
+
* a submit attempt fails validation. Fires after errors are populated
|
|
1925
|
+
* and before your `onError` callback runs.
|
|
1847
1926
|
*
|
|
1848
|
-
*
|
|
1849
|
-
*
|
|
1850
|
-
*
|
|
1851
|
-
*
|
|
1852
|
-
*
|
|
1853
|
-
*
|
|
1854
|
-
* multi-step wizards, field arrays seeded with placeholder
|
|
1855
|
-
* rows, or any form intentionally mounting with incomplete data.
|
|
1927
|
+
* Default `true`: focusing the broken field on submit is an
|
|
1928
|
+
* accessibility baseline, and modern browsers scroll the focused
|
|
1929
|
+
* element into view as part of the move. Pass `false` when you drive
|
|
1930
|
+
* the nudge yourself from an `onError` callback with
|
|
1931
|
+
* `form.focusFirstError()` or `form.scrollToFirstError()`, both of
|
|
1932
|
+
* which stay available either way.
|
|
1856
1933
|
*
|
|
1857
|
-
*
|
|
1858
|
-
*
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
*
|
|
1863
|
-
*
|
|
1864
|
-
* Default `'focus-first-error'`, which moves keyboard / screen-reader
|
|
1865
|
-
* focus to the broken field as an accessibility baseline.
|
|
1866
|
-
*
|
|
1867
|
-
* - `'focus-first-error'` (default): focus the first errored field's
|
|
1868
|
-
* first visible element.
|
|
1869
|
-
* - `'scroll-to-first-error'`: scroll it into view without focusing.
|
|
1870
|
-
* - `'both'`: scroll, then focus.
|
|
1871
|
-
* - `'none'`: opt out entirely; handle focus / scroll yourself in an
|
|
1872
|
-
* `onError` callback via `form.focusFirstError()` or
|
|
1873
|
-
* `form.scrollToFirstError()`.
|
|
1934
|
+
* The focus requests `focusVisible: true`, so the browser paints a
|
|
1935
|
+
* focus ring even though the move is programmatic. Where a UA doesn't
|
|
1936
|
+
* yet support that hint, non-text controls (radio, checkbox, custom
|
|
1937
|
+
* widgets) may end up focused without a visible ring after a
|
|
1938
|
+
* pointer-driven submit; if you target those browsers, pair this with
|
|
1939
|
+
* your own indicator (e.g. a `:focus-within` ring on the option
|
|
1940
|
+
* wrapper).
|
|
1874
1941
|
*
|
|
1875
|
-
* If no errored field has a currently-mounted, visible element,
|
|
1876
|
-
*
|
|
1942
|
+
* If no errored field has a currently-mounted, visible element, the
|
|
1943
|
+
* focus silently no-ops.
|
|
1877
1944
|
*/
|
|
1878
|
-
|
|
1945
|
+
focusOnInvalidSubmit?: boolean;
|
|
1879
1946
|
/**
|
|
1880
1947
|
* Freeze the form's data. When this resolves truthy every value write
|
|
1881
1948
|
* no-ops at the store's write chokepoint (programmatic `setValue`, the
|
|
@@ -1893,7 +1960,6 @@ type UseFormConfiguration<Form extends GenericForm, GetValueFormType, Schema ext
|
|
|
1893
1960
|
*
|
|
1894
1961
|
* Accepts a boolean, a ref, a computed, or a getter, read live so the
|
|
1895
1962
|
* freeze tracks a reactive source. `undefined` resolves to `false`.
|
|
1896
|
-
* Falls back to `AttaformDefaults.disabled`.
|
|
1897
1963
|
*
|
|
1898
1964
|
* A shared FormStore resolves `disabled` from its first `useForm({ key })`
|
|
1899
1965
|
* call; a later caller passing a different value is ignored.
|
|
@@ -1960,68 +2026,18 @@ type UseFormConfiguration<Form extends GenericForm, GetValueFormType, Schema ext
|
|
|
1960
2026
|
rememberVariants?: boolean;
|
|
1961
2027
|
/**
|
|
1962
2028
|
* Schema-driven coercion of user-typed DOM values at the v-register
|
|
1963
|
-
* directive layer.
|
|
1964
|
-
* `AttaformDefaults.coerce`.
|
|
2029
|
+
* directive layer.
|
|
1965
2030
|
*
|
|
1966
|
-
*
|
|
1967
|
-
*
|
|
1968
|
-
*
|
|
1969
|
-
*
|
|
1970
|
-
* extend.
|
|
2031
|
+
* Two rules ship: string→number and string→boolean, each firing
|
|
2032
|
+
* only when the schema declares that single type at the path.
|
|
2033
|
+
* Defaults to on; `false` disables coercion form-wide and the slim
|
|
2034
|
+
* gate rejects mismatches instead.
|
|
1971
2035
|
*
|
|
1972
2036
|
* Coercion applies ONLY to user-typed DOM values. Programmatic
|
|
1973
2037
|
* writes (`form.setValue`, `setValueWithInternalPath`) are NEVER
|
|
1974
2038
|
* coerced.
|
|
1975
2039
|
*/
|
|
1976
|
-
coerce?: boolean
|
|
1977
|
-
/**
|
|
1978
|
-
* Per-form override of the `getDisplayState` heuristic that drives
|
|
1979
|
-
* `field.displayState` and the `show*` booleans (and their `form.meta`
|
|
1980
|
-
* rollups). Falls back to `AttaformDefaults.getDisplayState`, then to
|
|
1981
|
-
* the library default (`defaultDisplayState`). See
|
|
1982
|
-
* `AttaformDefaults.getDisplayState` for the resolution rules and
|
|
1983
|
-
* predicate signature.
|
|
1984
|
-
*/
|
|
1985
|
-
getDisplayState?: GetDisplayState;
|
|
1986
|
-
/**
|
|
1987
|
-
* Recursion ceiling for schema walks that descend through recursive
|
|
1988
|
-
* schemas (Zod's `z.lazy(...)` today). Default `64`. Per-form value
|
|
1989
|
-
* overrides `AttaformDefaults.maxRecursionDepth`, which overrides
|
|
1990
|
-
* the library default.
|
|
1991
|
-
*
|
|
1992
|
-
* Schemas that don't include a recursive boundary ignore this knob
|
|
1993
|
-
* entirely — it's read only at the descent step through a recursive
|
|
1994
|
-
* wrapper. Set it on the specific form whose schema is recursive
|
|
1995
|
-
* (a comment tree, a category tree, a nested-rule editor):
|
|
1996
|
-
*
|
|
1997
|
-
* ```ts
|
|
1998
|
-
* useForm({ schema: commentTreeSchema, maxRecursionDepth: 128 })
|
|
1999
|
-
* ```
|
|
2000
|
-
*
|
|
2001
|
-
* Past the cap, the slim-primitive type gate falls back to permissive
|
|
2002
|
-
* (write-time type checks skip; full schema validation still runs).
|
|
2003
|
-
* Storage and reads work at any depth; only the per-write type gate
|
|
2004
|
-
* stops short of the cap. Raise the cap if you regularly edit nodes
|
|
2005
|
-
* beyond the default depth.
|
|
2006
|
-
*
|
|
2007
|
-
* See `AttaformDefaults.maxRecursionDepth` for the resolution rules
|
|
2008
|
-
* and the broader description of where the cap is read.
|
|
2009
|
-
*/
|
|
2010
|
-
maxRecursionDepth?: number;
|
|
2011
|
-
/**
|
|
2012
|
-
* Whether `v-register` automatically manages aria attributes
|
|
2013
|
-
* (`aria-invalid`, `aria-busy`, `aria-required`, `aria-describedby`)
|
|
2014
|
-
* from the field's display state. **Defaults to `true`.**
|
|
2015
|
-
*
|
|
2016
|
-
* **Resolution order (per-register override > per-form > global > library):**
|
|
2017
|
-
*
|
|
2018
|
-
* register(path, { autoAria }) > useForm({ autoAria }) > AttaformDefaults.autoAria > library default (`true`)
|
|
2019
|
-
*
|
|
2020
|
-
* Set `false` to leave all aria wiring to your own markup form-wide.
|
|
2021
|
-
* Any aria attribute you author yourself is always left untouched,
|
|
2022
|
-
* independent of this flag.
|
|
2023
|
-
*/
|
|
2024
|
-
autoAria?: boolean;
|
|
2040
|
+
coerce?: boolean;
|
|
2025
2041
|
/**
|
|
2026
2042
|
* @internal
|
|
2027
2043
|
* SSR prefetch mark — set by the `attaform/vite` compile-time
|
|
@@ -2038,176 +2054,6 @@ type UseFormConfiguration<Form extends GenericForm, GetValueFormType, Schema ext
|
|
|
2038
2054
|
*/
|
|
2039
2055
|
__ssrAccessed?: boolean;
|
|
2040
2056
|
};
|
|
2041
|
-
/**
|
|
2042
|
-
* App-level defaults applied to every `useForm` call. Set these once
|
|
2043
|
-
* per app via `createAttaform({ defaults })` (bare Vue) or
|
|
2044
|
-
* `attaform.defaults` (Nuxt module).
|
|
2045
|
-
*
|
|
2046
|
-
* Resolution order (per-form wins):
|
|
2047
|
-
*
|
|
2048
|
-
* useForm({ ... }) > createAttaform({ defaults }) > library default
|
|
2049
|
-
*
|
|
2050
|
-
* `validateOn` and `debounceMs` resolve per-field — set the debounce
|
|
2051
|
-
* globally while still overriding the trigger per form:
|
|
2052
|
-
*
|
|
2053
|
-
* ```ts
|
|
2054
|
-
* createAttaform({
|
|
2055
|
-
* defaults: { debounceMs: 100 },
|
|
2056
|
-
* })
|
|
2057
|
-
* // later
|
|
2058
|
-
* useForm({ schema, validateOn: 'blur' })
|
|
2059
|
-
* // → { validateOn: 'blur', debounceMs: <ignored under blur> }
|
|
2060
|
-
* ```
|
|
2061
|
-
*
|
|
2062
|
-
* Note: per the discriminated union, `debounceMs` only takes effect
|
|
2063
|
-
* when `validateOn` is `'change'` (or omitted). Setting it as an
|
|
2064
|
-
* app-level default is fine — forms that switch to `'blur'` /
|
|
2065
|
-
* `'submit'` simply ignore the inherited `debounceMs`.
|
|
2066
|
-
*
|
|
2067
|
-
* `schema`, `key`, and `defaultValues` are not configurable here —
|
|
2068
|
-
* they belong on the per-form call.
|
|
2069
|
-
*/
|
|
2070
|
-
type AttaformDefaults = {
|
|
2071
|
-
/** Default for `useForm({ strict })`. Default `true`. */
|
|
2072
|
-
strict?: boolean;
|
|
2073
|
-
/** Default for `useForm({ onInvalidSubmit })`. */
|
|
2074
|
-
onInvalidSubmit?: OnInvalidSubmitPolicy;
|
|
2075
|
-
/** Default for `useForm({ validateOn })` — when validation runs. */
|
|
2076
|
-
validateOn?: ValidateOn;
|
|
2077
|
-
/**
|
|
2078
|
-
* Default for `useForm({ debounceMs })` — ms to wait after the last
|
|
2079
|
-
* input event before re-running validation. Only meaningful when
|
|
2080
|
-
* `validateOn` resolves to `'change'`. Default `0` (synchronous).
|
|
2081
|
-
*/
|
|
2082
|
-
debounceMs?: number;
|
|
2083
|
-
/** Default for `useForm({ history })` — a `historyPlugin()` instance. */
|
|
2084
|
-
history?: HistoryPlugin;
|
|
2085
|
-
/** Default for `useForm({ rememberVariants })`. */
|
|
2086
|
-
rememberVariants?: boolean;
|
|
2087
|
-
/** Default for `useForm({ disabled })` — freeze the form's data. */
|
|
2088
|
-
disabled?: MaybeRefOrGetter<boolean | undefined>;
|
|
2089
|
-
/**
|
|
2090
|
-
* Default for `useForm({ coerce })`. Schema-driven coercion of
|
|
2091
|
-
* user-typed DOM values at the v-register directive layer.
|
|
2092
|
-
*
|
|
2093
|
-
* - `true` (default) — runs the built-in `defaultCoercionRules`
|
|
2094
|
-
* (`string→number`, `string→boolean`).
|
|
2095
|
-
* - `false` — disables coercion globally; the slim-primitive gate
|
|
2096
|
-
* rejects type mismatches with its existing dev-warn instead.
|
|
2097
|
-
* - `CoercionRegistry` — a custom array of `CoercionEntry` records.
|
|
2098
|
-
* Spread `defaultCoercionRules` to extend rather than replace:
|
|
2099
|
-
* `[...defaultCoercionRules, defineCoercion({ ... })]`.
|
|
2100
|
-
*
|
|
2101
|
-
* Coercion applies ONLY to user-typed DOM values flowing through
|
|
2102
|
-
* the directive's assigner. Programmatic writes (`form.setValue`,
|
|
2103
|
-
* `setValueWithInternalPath`) are NEVER coerced — they're
|
|
2104
|
-
* authoritative writes whose strict typing is on the caller.
|
|
2105
|
-
*/
|
|
2106
|
-
coerce?: boolean | CoercionRegistry;
|
|
2107
|
-
/**
|
|
2108
|
-
* Default for `useForm({ getDisplayState })`. The centralised
|
|
2109
|
-
* heuristic that resolves every path's `field.displayState` — and thus
|
|
2110
|
-
* the `show*` booleans and their `form.meta` rollups — to one of
|
|
2111
|
-
* `'idle' | 'pending' | 'error' | 'success'`.
|
|
2112
|
-
*
|
|
2113
|
-
* Resolution order (per-form wins):
|
|
2114
|
-
*
|
|
2115
|
-
* useForm({ getDisplayState }) > AttaformDefaults > library default
|
|
2116
|
-
*
|
|
2117
|
-
* The library default opens one timing gate, then resolves by
|
|
2118
|
-
* precedence: gate closed → `'idle'`; a run in flight → a delayed
|
|
2119
|
-
* `'pending'` (held briefly to smooth fast validations, then held a
|
|
2120
|
-
* minimum so it never flashes); an own-path error → `'error'`;
|
|
2121
|
-
* otherwise earned `valid` → `'success'`, else `'idle'`. The gate opens
|
|
2122
|
-
* after the first submit attempt OR once the field is edited and left:
|
|
2123
|
-
*
|
|
2124
|
-
* ```ts
|
|
2125
|
-
* (prev, ctx) => {
|
|
2126
|
-
* const gateOpen =
|
|
2127
|
-
* ctx.formMeta.submissionAttempts > 0 ||
|
|
2128
|
-
* ctx.field.blurredAfterInteraction === true
|
|
2129
|
-
* if (!gateOpen) return { display: 'idle' }
|
|
2130
|
-
* // ...timed 'pending' while validating; own-path error → 'error';
|
|
2131
|
-
* // earned valid → 'success'; else 'idle'
|
|
2132
|
-
* }
|
|
2133
|
-
* ```
|
|
2134
|
-
*
|
|
2135
|
-
* Compose with the library default via the public `defaultDisplayState`
|
|
2136
|
-
* export, or retune its timing via `makeDefaultDisplayState`. The
|
|
2137
|
-
* reducer runs on every field-state read, so it owns the
|
|
2138
|
-
* idle / pending / error / success decision outright.
|
|
2139
|
-
*
|
|
2140
|
-
* The reducer's `ctx.field` / `ctx.formMeta` are `Omit`'d of the
|
|
2141
|
-
* derived `displayState` / `show*` / `firstError` keys (see
|
|
2142
|
-
* `FieldStateDerivedKey`) to prevent a self-referential reducer.
|
|
2143
|
-
*/
|
|
2144
|
-
getDisplayState?: GetDisplayState;
|
|
2145
|
-
/**
|
|
2146
|
-
* Default for `useForm({ maxRecursionDepth })`. Recursion ceiling
|
|
2147
|
-
* for schema walks that descend through recursive schemas (Zod's
|
|
2148
|
-
* `z.lazy(...)` today, equivalent constructs in any future adapter).
|
|
2149
|
-
* Library default: `64`.
|
|
2150
|
-
*
|
|
2151
|
-
* Resolution order (per-form wins):
|
|
2152
|
-
*
|
|
2153
|
-
* useForm({ maxRecursionDepth }) > AttaformDefaults > library default (64)
|
|
2154
|
-
*
|
|
2155
|
-
* Read at every step of a schema walk that crosses a recursive
|
|
2156
|
-
* boundary — default-value derivation at construction, slim-primitive
|
|
2157
|
-
* type gates on each write, path-by-path schema resolution. Walks
|
|
2158
|
-
* track their descent depth and switch to a permissive fallback once
|
|
2159
|
-
* `depth > maxRecursionDepth`.
|
|
2160
|
-
*
|
|
2161
|
-
* "Permissive fallback" means storage and reads keep working at any
|
|
2162
|
-
* depth; only the per-write type gate stops checking past the cap.
|
|
2163
|
-
* Full schema validation (`parse`, `handleSubmit`) still runs
|
|
2164
|
-
* against the real schema, so refinement errors at any depth still
|
|
2165
|
-
* surface — the cap only affects the *write-time gate*.
|
|
2166
|
-
*
|
|
2167
|
-
* Forms with no recursive schemas ignore this entirely — the cap is
|
|
2168
|
-
* read only at the descent step through a recursive wrapper. Setting
|
|
2169
|
-
* it app-wide is the right move when you have multiple recursive
|
|
2170
|
-
* forms that should share one ceiling:
|
|
2171
|
-
*
|
|
2172
|
-
* ```ts
|
|
2173
|
-
* createAttaform({
|
|
2174
|
-
* defaults: { maxRecursionDepth: 128 },
|
|
2175
|
-
* })
|
|
2176
|
-
* ```
|
|
2177
|
-
*
|
|
2178
|
-
* Per-form override stays available for the one tree-shaped form
|
|
2179
|
-
* whose depth is unusual:
|
|
2180
|
-
*
|
|
2181
|
-
* ```ts
|
|
2182
|
-
* useForm({ schema: deepCategoryTreeSchema, maxRecursionDepth: 256 })
|
|
2183
|
-
* ```
|
|
2184
|
-
*
|
|
2185
|
-
* Setting this app-wide costs nothing for non-recursive forms — the
|
|
2186
|
-
* walks that read the cap never run for them.
|
|
2187
|
-
*
|
|
2188
|
-
* Pass `Infinity` to disable the cap entirely. Walks will then
|
|
2189
|
-
* descend through recursive boundaries until they terminate
|
|
2190
|
-
* structurally; a schema with no structural terminator will exhaust
|
|
2191
|
-
* the JS call stack. Reserve for schemas whose authors are
|
|
2192
|
-
* confident the recursion is bounded by the actual data shape.
|
|
2193
|
-
*/
|
|
2194
|
-
maxRecursionDepth?: number;
|
|
2195
|
-
/**
|
|
2196
|
-
* App-wide default for `useForm({ autoAria })`. Library default is
|
|
2197
|
-
* `true`: `v-register` keeps `aria-invalid` / `aria-busy` /
|
|
2198
|
-
* `aria-required` / `aria-describedby` in sync with each field's
|
|
2199
|
-
* display state out of the box.
|
|
2200
|
-
*
|
|
2201
|
-
* **Resolution order (per-form wins):**
|
|
2202
|
-
*
|
|
2203
|
-
* useForm({ autoAria }) > AttaformDefaults.autoAria > library default (`true`)
|
|
2204
|
-
*
|
|
2205
|
-
* Set `false` once at the plugin level to make every form manage its
|
|
2206
|
-
* own aria markup. Authored aria attributes are always preserved
|
|
2207
|
-
* regardless of this setting.
|
|
2208
|
-
*/
|
|
2209
|
-
autoAria?: boolean;
|
|
2210
|
-
};
|
|
2211
2057
|
/**
|
|
2212
2058
|
* Callback invoked by `handleSubmit` after the form parses successfully.
|
|
2213
2059
|
* Receives the strictly-typed parsed value — refinements have run, so
|
|
@@ -2220,7 +2066,7 @@ type OnSubmit<Form extends GenericForm> = (form: Form) => void | Promise<void>;
|
|
|
2220
2066
|
* the user-error layer (the `setErrors(...); return` server-rejection
|
|
2221
2067
|
* pattern). Receives the full list of errors. Bind this when you want to
|
|
2222
2068
|
* react to submit failures explicitly (alongside or instead of the
|
|
2223
|
-
* automatic `
|
|
2069
|
+
* automatic `focusOnInvalidSubmit` nudge).
|
|
2224
2070
|
*/
|
|
2225
2071
|
type OnError = (error: ValidationError[]) => void | Promise<void>;
|
|
2226
2072
|
/**
|
|
@@ -2274,11 +2120,11 @@ type DisplayMachine = {
|
|
|
2274
2120
|
readonly pendingShownAt?: number;
|
|
2275
2121
|
};
|
|
2276
2122
|
/**
|
|
2277
|
-
* Inputs to
|
|
2278
|
-
*
|
|
2279
|
-
*
|
|
2280
|
-
* `FieldStateDerivedKey` — so
|
|
2281
|
-
* form a cycle),
|
|
2123
|
+
* Inputs to the display reducer. `field` and `formMeta` are the
|
|
2124
|
+
* reactive snapshots it resolves against (minus the derived
|
|
2125
|
+
* `displayState` / `show*` / `firstError` keys — see
|
|
2126
|
+
* `FieldStateDerivedKey` — so the reducer can never read its own output
|
|
2127
|
+
* and form a cycle), joined by:
|
|
2282
2128
|
*
|
|
2283
2129
|
* - `validatingSince` — `Date.now()` at which the field's current
|
|
2284
2130
|
* validation streak opened, or `null` when nothing is in flight. This,
|
|
@@ -2306,26 +2152,9 @@ type DisplayCtx = {
|
|
|
2306
2152
|
* returns the next machine; the engine owns the clock and the timers, the
|
|
2307
2153
|
* reducer owns the timing policy. Runs on every field-state read (and
|
|
2308
2154
|
* again whenever a `reviewAt` deadline fires), so the whole app's
|
|
2309
|
-
* validation-display behavior flows from this one function
|
|
2310
|
-
*
|
|
2311
|
-
*
|
|
2312
|
-
* layered reducer can compose with it, and `makeDefaultDisplayState`
|
|
2313
|
-
* builds a default with custom anti-flash timings:
|
|
2314
|
-
*
|
|
2315
|
-
* ```ts
|
|
2316
|
-
* import { defaultDisplayState } from 'attaform'
|
|
2317
|
-
*
|
|
2318
|
-
* useForm({
|
|
2319
|
-
* schema,
|
|
2320
|
-
* // Defer to the default everywhere, but never show a success check on `username`.
|
|
2321
|
-
* getDisplayState: (prev, ctx) => {
|
|
2322
|
-
* const next = defaultDisplayState(prev, ctx)
|
|
2323
|
-
* return next.display === 'success' && ctx.field.path[0] === 'username'
|
|
2324
|
-
* ? { display: 'idle' }
|
|
2325
|
-
* : next
|
|
2326
|
-
* },
|
|
2327
|
-
* })
|
|
2328
|
-
* ```
|
|
2155
|
+
* validation-display behavior flows from this one function
|
|
2156
|
+
* (`core/display-state.ts`).
|
|
2157
|
+
* @internal
|
|
2329
2158
|
*/
|
|
2330
2159
|
type GetDisplayState = (prev: DisplayMachine, ctx: DisplayCtx) => DisplayMachine;
|
|
2331
2160
|
/**
|
|
@@ -2461,60 +2290,6 @@ type RegisterFlatPath<Form> = Form extends unknown ? FlatPathBuilder<Form, 'regi
|
|
|
2461
2290
|
* `transformError` channel above, not a throw into the host app.)
|
|
2462
2291
|
*/
|
|
2463
2292
|
type RegisterTransform = (value: unknown, ctx?: TransformContext) => unknown;
|
|
2464
|
-
/**
|
|
2465
|
-
* Runtime type for a slim primitive kind. Used to narrow the
|
|
2466
|
-
* `transform` parameter and return value on a `CoercionEntry` so
|
|
2467
|
-
* authors writing rules don't have to cast `unknown`.
|
|
2468
|
-
*
|
|
2469
|
-
* Exhaustive over `SlimPrimitiveKind` — adding a new kind to that
|
|
2470
|
-
* union must add a corresponding branch here.
|
|
2471
|
-
*/
|
|
2472
|
-
type SlimRuntimeOf<K extends SlimPrimitiveKind> = K extends 'string' ? string : K extends 'number' ? number : K extends 'boolean' ? boolean : K extends 'bigint' ? bigint : K extends 'date' ? Date : K extends 'null' ? null : K extends 'undefined' ? undefined : K extends 'array' ? readonly unknown[] : K extends 'set' ? ReadonlySet<unknown> : K extends 'map' ? ReadonlyMap<unknown, unknown> : K extends 'object' ? Record<string, unknown> : K extends 'symbol' ? symbol : K extends 'function' ? (...args: never[]) => unknown : never;
|
|
2473
|
-
/**
|
|
2474
|
-
* Outcome of a coercion attempt.
|
|
2475
|
-
*
|
|
2476
|
-
* - `coerced: true` — the rule produced `value`, which the directive
|
|
2477
|
-
* forwards to the slim gate (the gate may still reject if the
|
|
2478
|
-
* value doesn't satisfy the path's accept set).
|
|
2479
|
-
* - `coerced: false` — the rule decided it can't coerce this input.
|
|
2480
|
-
* The directive passes the original value through; the slim gate
|
|
2481
|
-
* decides downstream.
|
|
2482
|
-
*
|
|
2483
|
-
* Discriminated rather than `O | undefined` so rules with
|
|
2484
|
-
* `output: 'undefined'` or `output: 'null'` don't conflict with the
|
|
2485
|
-
* "skip" signal.
|
|
2486
|
-
*/
|
|
2487
|
-
type CoercionResult<O> = {
|
|
2488
|
-
coerced: true;
|
|
2489
|
-
value: O;
|
|
2490
|
-
} | {
|
|
2491
|
-
coerced: false;
|
|
2492
|
-
};
|
|
2493
|
-
/**
|
|
2494
|
-
* A single coercion rule. `input` and `output` are
|
|
2495
|
-
* `SlimPrimitiveKind` literals; `transform` receives a value already
|
|
2496
|
-
* narrowed to `SlimRuntimeOf<input>` and returns
|
|
2497
|
-
* `CoercionResult<SlimRuntimeOf<output>>`.
|
|
2498
|
-
*
|
|
2499
|
-
* Rules MUST be sync. They SHOULD NOT throw — wrap internal
|
|
2500
|
-
* try/catch when the conversion can fail (e.g. `BigInt(s)` throws
|
|
2501
|
-
* for non-numeric strings). The library wraps each invocation in
|
|
2502
|
-
* try/catch as defense in depth; throws are caught, logged once per
|
|
2503
|
-
* `(input, output)`, and the original value passes through.
|
|
2504
|
-
*/
|
|
2505
|
-
type CoercionEntry<I extends SlimPrimitiveKind = SlimPrimitiveKind, O extends SlimPrimitiveKind = SlimPrimitiveKind> = {
|
|
2506
|
-
readonly input: I;
|
|
2507
|
-
readonly output: O;
|
|
2508
|
-
readonly transform: (value: SlimRuntimeOf<I>) => CoercionResult<SlimRuntimeOf<O>>;
|
|
2509
|
-
};
|
|
2510
|
-
/**
|
|
2511
|
-
* A registry is an ordered array of `CoercionEntry` records.
|
|
2512
|
-
* Consumers compose by spreading `defaultCoercionRules` and
|
|
2513
|
-
* appending their own entries. Order is observable only when two
|
|
2514
|
-
* entries share the same `(input, output)` pair — the library emits
|
|
2515
|
-
* a one-shot dev-warn and the LATER entry wins.
|
|
2516
|
-
*/
|
|
2517
|
-
type CoercionRegistry = readonly CoercionEntry[];
|
|
2518
2293
|
/**
|
|
2519
2294
|
* Options for `register(path, options)`. Per-field configuration
|
|
2520
2295
|
* applied at the binding's own call site.
|
|
@@ -2552,22 +2327,6 @@ type RegisterOptions = {
|
|
|
2552
2327
|
* instead — see the "Custom assigners" section in the API docs.
|
|
2553
2328
|
*/
|
|
2554
2329
|
transforms?: ReadonlyArray<RegisterTransform>;
|
|
2555
|
-
/**
|
|
2556
|
-
* Per-binding override for automatic aria management, the narrowest
|
|
2557
|
-
* tier of the `autoAria` cascade. By default the directive keeps
|
|
2558
|
-
* `aria-invalid` / `aria-busy` / `aria-required` / `aria-describedby`
|
|
2559
|
-
* in sync with the field's display state. Pass `autoAria: false` to
|
|
2560
|
-
* leave every aria attribute on this element to you (the directive
|
|
2561
|
-
* still manages value binding and registration), or `autoAria: true`
|
|
2562
|
-
* to re-enable management on one binding even when the form set
|
|
2563
|
-
* `useForm({ autoAria: false })`.
|
|
2564
|
-
*
|
|
2565
|
-
* Overrides `useForm({ autoAria })` and
|
|
2566
|
-
* `createAttaform({ defaults: { autoAria } })`. Writing an aria
|
|
2567
|
-
* attribute yourself also locks the directive out of that one
|
|
2568
|
-
* attribute, regardless of this flag.
|
|
2569
|
-
*/
|
|
2570
|
-
autoAria?: boolean;
|
|
2571
2330
|
};
|
|
2572
2331
|
/**
|
|
2573
2332
|
* The narrow kernel surface the DOM binding drives. Structurally a
|
|
@@ -2905,14 +2664,6 @@ type RegisterValue<Value = unknown> = Readonly<{
|
|
|
2905
2664
|
* @internal
|
|
2906
2665
|
*/
|
|
2907
2666
|
isRequired?: boolean;
|
|
2908
|
-
/**
|
|
2909
|
-
* Whether the directive should auto-manage aria attributes for this
|
|
2910
|
-
* binding. Resolves the per-register `autoAria` override against the
|
|
2911
|
-
* form-level value: `options.autoAria ?? formAutoAria`. The directive
|
|
2912
|
-
* treats an absent value as off.
|
|
2913
|
-
* @internal
|
|
2914
|
-
*/
|
|
2915
|
-
ariaEnabled?: boolean;
|
|
2916
2667
|
/**
|
|
2917
2668
|
* The gated display-state verdict for this path, reusing the same
|
|
2918
2669
|
* field-state identity as `form.fields`. The directive watches it to
|
|
@@ -3213,9 +2964,11 @@ declare module 'vue' {
|
|
|
3213
2964
|
* form.setValue((prev) => ({ ...prev, name: 'Ada' }))
|
|
3214
2965
|
* ```
|
|
3215
2966
|
*
|
|
3216
|
-
*
|
|
3217
|
-
*
|
|
3218
|
-
* so
|
|
2967
|
+
* A whole-form callback replaces the whole form. Any key the return
|
|
2968
|
+
* omits is refilled from the SCHEMA's declared defaults (the slim
|
|
2969
|
+
* value where none is declared), so an omitted key is not carried
|
|
2970
|
+
* over from the previous value or from `defaultValues` — spread
|
|
2971
|
+
* `prev` to keep the rest of the form.
|
|
3219
2972
|
*/
|
|
3220
2973
|
type SetValueCallback<Read, Write = Read> = (prev: Read) => Read | Write;
|
|
3221
2974
|
/**
|
|
@@ -3459,11 +3212,11 @@ type FieldState<Value = unknown> = {
|
|
|
3459
3212
|
* <FieldStatusIcon :state="form.fields.email.displayState" />
|
|
3460
3213
|
* ```
|
|
3461
3214
|
*
|
|
3462
|
-
* Resolved by the
|
|
3463
|
-
*
|
|
3464
|
-
* `
|
|
3465
|
-
*
|
|
3466
|
-
*
|
|
3215
|
+
* Resolved by the display heuristic: gate closed → `'idle'`; a run
|
|
3216
|
+
* in flight → a delayed `'pending'`; an own-path error → `'error'`;
|
|
3217
|
+
* otherwise earned `valid` → `'success'`, else `'idle'`. The gate
|
|
3218
|
+
* opens after the first submit attempt OR once the field is edited
|
|
3219
|
+
* and left.
|
|
3467
3220
|
*
|
|
3468
3221
|
* Available on container paths too: `form.fields.users[0].displayState`
|
|
3469
3222
|
* rolls up over the row's descendants.
|
|
@@ -3848,7 +3601,7 @@ type FieldStateMap<Form extends GenericForm> = LeafWalker<Form, 'field'> & {
|
|
|
3848
3601
|
* dot/bracket access (`form.fields.users[i].email`), which is what
|
|
3849
3602
|
* descends.
|
|
3850
3603
|
*/
|
|
3851
|
-
<const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown :
|
|
3604
|
+
<const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown : SegmentPathRejection<JoinSegments<S>>)): FieldState<NestedType<Form, JoinSegments<S>>>;
|
|
3852
3605
|
/**
|
|
3853
3606
|
* Dynamic-array fallback for callers passing `Path`-typed (runtime)
|
|
3854
3607
|
* segment arrays — e.g. forwarding `RegisterValue.segments` to
|
|
@@ -3919,7 +3672,7 @@ type FormErrorsSurface<Form> = ErrorsProxyShape<Form> & {
|
|
|
3919
3672
|
* tuples that don't resolve to a known path fail at the call site.
|
|
3920
3673
|
* Dynamic `Path`-typed inputs hit the untyped fallback overload below.
|
|
3921
3674
|
*/
|
|
3922
|
-
<const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown :
|
|
3675
|
+
<const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown : SegmentPathRejection<JoinSegments<S>>)): readonly ValidationError[];
|
|
3923
3676
|
(segments: ReadonlyArray<string | number>): readonly ValidationError[];
|
|
3924
3677
|
/**
|
|
3925
3678
|
* No-arg call returns the whole-form error aggregate, the same as
|
|
@@ -3964,6 +3717,27 @@ type ErrorsProxyShape<T> = LeafWalker<T, 'errors', false>;
|
|
|
3964
3717
|
* form.values() // the whole form value (root)
|
|
3965
3718
|
* ```
|
|
3966
3719
|
*
|
|
3720
|
+
* The two shapes answer different questions. Dot access is the
|
|
3721
|
+
* REACTIVE view: per-key tracking, no copying, always the live value.
|
|
3722
|
+
* The call form returns a SNAPSHOT: a detached plain object that keeps
|
|
3723
|
+
* what it held at capture time, so it is the shape to hand an async
|
|
3724
|
+
* call, a serialiser, or a diff.
|
|
3725
|
+
*
|
|
3726
|
+
* ```ts
|
|
3727
|
+
* const before = form.values() // detached; survives later writes
|
|
3728
|
+
* await api.save(before) // cannot mutate underneath the call
|
|
3729
|
+
* form.values.email // reactive read; use this in computed
|
|
3730
|
+
* ```
|
|
3731
|
+
*
|
|
3732
|
+
* Prefer dot access inside a `computed`, `watchEffect`, or template:
|
|
3733
|
+
* it tracks the one key you read, where the call form depends on the
|
|
3734
|
+
* whole form and re-runs the consumer on any change.
|
|
3735
|
+
*
|
|
3736
|
+
* The copy is deep across plain objects and arrays. Non-plain
|
|
3737
|
+
* instances (Map, Set, File, Date) are shared by reference, matching
|
|
3738
|
+
* `JSON.stringify` behaviour; `structuredClone(form.values())` gives a
|
|
3739
|
+
* fully detached copy when one is needed.
|
|
3740
|
+
*
|
|
3967
3741
|
* Single-bracket dotted access (`form.values['address.city']`) is
|
|
3968
3742
|
* intentionally NOT supported — JS object semantics treat the dotted
|
|
3969
3743
|
* string as a single key. Use chained dot/bracket or the callable
|
|
@@ -4007,7 +3781,9 @@ type ValuesSurface<F> = Readonly<LiftedValueShape<F>> & {
|
|
|
4007
3781
|
* `form.fields.<path>`; this is the aggregate view across the
|
|
4008
3782
|
* whole form.
|
|
4009
3783
|
*
|
|
4010
|
-
* Read-only at runtime —
|
|
3784
|
+
* Read-only at runtime — an assignment is refused with a dev-console
|
|
3785
|
+
* warning and never lands (Attaform does not throw from a read
|
|
3786
|
+
* surface). Destructuring snapshots
|
|
4011
3787
|
* the current values; use `toRefs()` if you need reactive handles
|
|
4012
3788
|
* to individual fields.
|
|
4013
3789
|
*/
|
|
@@ -4032,9 +3808,8 @@ type FormMeta<F = unknown> = FieldState<F> & {
|
|
|
4032
3808
|
* the counter at its prior value.
|
|
4033
3809
|
*
|
|
4034
3810
|
* Pure introspection counter — useful for "this form has been
|
|
4035
|
-
* visited and left" UX (analytics, prior-step badges
|
|
4036
|
-
*
|
|
4037
|
-
* default `getDisplayState` heuristic. The reveal-on-submit story
|
|
3811
|
+
* visited and left" UX (analytics, prior-step badges) but does NOT
|
|
3812
|
+
* drive the display heuristic. The reveal-on-submit story
|
|
4038
3813
|
* runs entirely through `submissionAttempts`, which
|
|
4039
3814
|
* `wizard.handleSubmit` bumps on every form (it always validates the
|
|
4040
3815
|
* whole step list).
|
|
@@ -4351,7 +4126,7 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
4351
4126
|
* `form.setValue([prefix, 'line1'], 'value')`. The resolved leaf
|
|
4352
4127
|
* type is exact, matching the dotted-string form.
|
|
4353
4128
|
*/
|
|
4354
|
-
<const S extends ReadonlyArray<string | number>, Value extends PathSetValuePayload<NestedType<Form, JoinSegments<S>>>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown :
|
|
4129
|
+
<const S extends ReadonlyArray<string | number>, Value extends PathSetValuePayload<NestedType<Form, JoinSegments<S>>>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown : SegmentPathRejection<JoinSegments<S>>), value: Value): boolean;
|
|
4355
4130
|
};
|
|
4356
4131
|
/**
|
|
4357
4132
|
* Reactive validation status. Re-runs whenever the form (or the
|
|
@@ -4463,7 +4238,7 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
4463
4238
|
*/
|
|
4464
4239
|
register: {
|
|
4465
4240
|
<Path extends RegisterFlatPath<Form>>(path: Path, options?: RegisterOptions): RegisterValue<NestedReadType<WriteShape<ReadForm>, Path>>;
|
|
4466
|
-
<const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [RegisterFlatPath<Form>] ? unknown :
|
|
4241
|
+
<const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [RegisterFlatPath<Form>] ? unknown : SegmentRegisterRejection<JoinSegments<S>>), options?: RegisterOptions): RegisterValue<NestedReadType<WriteShape<ReadForm>, JoinSegments<S>>>;
|
|
4467
4242
|
};
|
|
4468
4243
|
/**
|
|
4469
4244
|
* The form's identifier — either the explicit `key` passed to
|
|
@@ -4544,8 +4319,12 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
4544
4319
|
*
|
|
4545
4320
|
* Resolves after `hydrating` flips back to `false`. Throws
|
|
4546
4321
|
* synchronously when the form was constructed with a plain-value
|
|
4547
|
-
* `defaultValues` (nothing to re-fire).
|
|
4548
|
-
*
|
|
4322
|
+
* `defaultValues` (nothing to re-fire).
|
|
4323
|
+
*
|
|
4324
|
+
* The payload becomes the form's defaults, the same way a
|
|
4325
|
+
* `reset(next)` argument does, so `form.reset()` afterwards lands on
|
|
4326
|
+
* the values just fetched. Does NOT clear touched / submit state;
|
|
4327
|
+
* chain `form.reset()` for that.
|
|
4549
4328
|
*/
|
|
4550
4329
|
rehydrate(): Promise<void>;
|
|
4551
4330
|
/**
|
|
@@ -4603,7 +4382,7 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
4603
4382
|
*/
|
|
4604
4383
|
toRef: {
|
|
4605
4384
|
<Path extends FlatPath<Form>>(path: Path): Readonly<Ref<NestedReadType<WriteShape<ReadForm>, Path>>>;
|
|
4606
|
-
<const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown :
|
|
4385
|
+
<const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown : SegmentPathRejection<JoinSegments<S>>)): Readonly<Ref<NestedReadType<WriteShape<ReadForm>, JoinSegments<S>>>>;
|
|
4607
4386
|
};
|
|
4608
4387
|
/**
|
|
4609
4388
|
* Set the form's manual error layer. One surface for server-side
|
|
@@ -4673,10 +4452,10 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
4673
4452
|
*/
|
|
4674
4453
|
meta: FormMeta<Form>;
|
|
4675
4454
|
/**
|
|
4676
|
-
* Restore the form to its
|
|
4677
|
-
*
|
|
4678
|
-
*
|
|
4679
|
-
*
|
|
4455
|
+
* Restore the form to its defaults. Without arguments, re-applies
|
|
4456
|
+
* whatever the defaults currently are: `useForm({ defaultValues })`
|
|
4457
|
+
* on a fresh form, or the last values a `reset(next)` or an async
|
|
4458
|
+
* `defaultValues` factory settled on.
|
|
4680
4459
|
*
|
|
4681
4460
|
* Resets:
|
|
4682
4461
|
* - the form value back to defaults;
|
|
@@ -4685,6 +4464,26 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
4685
4464
|
* - touched / focused / blurred per-field flags;
|
|
4686
4465
|
* - submission state (`submitting` / `submissionAttempts` /
|
|
4687
4466
|
* `submitted` / `submitError`).
|
|
4467
|
+
*
|
|
4468
|
+
* `nextDefaultValues` RE-SEATS the defaults rather than applying for
|
|
4469
|
+
* one call, so the form has a single set of defaults that `reset()`,
|
|
4470
|
+
* `resetField(path)`, and `dirty` all read. That is what makes the
|
|
4471
|
+
* save-then-discard shape work: reset from the resource the server
|
|
4472
|
+
* returned, and a later Discard returns to the save instead of
|
|
4473
|
+
* rolling back across it.
|
|
4474
|
+
*
|
|
4475
|
+
* ```ts
|
|
4476
|
+
* const saved = await api.save(form.values())
|
|
4477
|
+
* form.reset(toValues(saved)) // the saved resource is now the baseline
|
|
4478
|
+
* // …later…
|
|
4479
|
+
* form.reset() // back to the saved resource
|
|
4480
|
+
* ```
|
|
4481
|
+
*
|
|
4482
|
+
* The argument is sparse: it folds over the defaults already in
|
|
4483
|
+
* force, so paths it does not name keep the value they had, and
|
|
4484
|
+
* `reset({})` changes nothing. Arrays are replaced wholesale rather
|
|
4485
|
+
* than merged element-wise. Pass `unset` at a path to withdraw its
|
|
4486
|
+
* value and mark it blank.
|
|
4688
4487
|
*/
|
|
4689
4488
|
reset: (nextDefaultValues?: DefaultValuesInput<Form>) => void;
|
|
4690
4489
|
/**
|
|
@@ -4739,7 +4538,7 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
4739
4538
|
clear: {
|
|
4740
4539
|
(): boolean;
|
|
4741
4540
|
<Path extends FlatPath<Form> | ''>(path: Path): boolean;
|
|
4742
|
-
<const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form> | ''] ? unknown :
|
|
4541
|
+
<const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form> | ''] ? unknown : SegmentPathRejection<JoinSegments<S>>)): boolean;
|
|
4743
4542
|
};
|
|
4744
4543
|
/**
|
|
4745
4544
|
* Consolidated undo/redo namespace — `form.history.{undo, redo,
|
|
@@ -4776,20 +4575,19 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
4776
4575
|
*/
|
|
4777
4576
|
scrollToFirstError: (options?: ScrollIntoViewOptions) => boolean;
|
|
4778
4577
|
/**
|
|
4779
|
-
*
|
|
4780
|
-
*
|
|
4781
|
-
*
|
|
4782
|
-
* `useForm({ onInvalidSubmit })` (or `'focus-first-error'` when
|
|
4783
|
-
* omitted). Pass an explicit policy to override for one call.
|
|
4578
|
+
* Run the form's own invalid-submit nudge imperatively: exactly what
|
|
4579
|
+
* `handleSubmit` does after a failed submit, but available standalone
|
|
4580
|
+
* and honoring this form's `useForm({ focusOnInvalidSubmit })` choice.
|
|
4784
4581
|
*
|
|
4785
4582
|
* Used by `useWizard` after navigating to the first failing form
|
|
4786
4583
|
* during `wizard.handleSubmit`, so the failing form's own configured
|
|
4787
|
-
*
|
|
4584
|
+
* behavior fires once its DOM is in view.
|
|
4788
4585
|
*
|
|
4789
|
-
* No-op when
|
|
4790
|
-
*
|
|
4586
|
+
* No-op when this form opted out, or when no errored field is
|
|
4587
|
+
* currently registered. Use `focusFirstError()` to focus regardless
|
|
4588
|
+
* of the configured choice.
|
|
4791
4589
|
*/
|
|
4792
|
-
applyInvalidSubmitPolicy: (
|
|
4590
|
+
applyInvalidSubmitPolicy: () => void;
|
|
4793
4591
|
/**
|
|
4794
4592
|
* Programmatically mark fields as `touched` — the descriptive
|
|
4795
4593
|
* "this field was visited" flag.
|
|
@@ -4805,9 +4603,9 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
4805
4603
|
* heuristic.** `touched` also flips on a bare focus → blur with no
|
|
4806
4604
|
* edit, so the library-default gate deliberately ignores it and
|
|
4807
4605
|
* reads `blurredAfterInteraction` instead — the stricter bit that
|
|
4808
|
-
* only a blur *following* an edit sets. Reach for `touch()` when
|
|
4809
|
-
*
|
|
4810
|
-
*
|
|
4606
|
+
* only a blur *following* an edit sets. Reach for `touch()` when
|
|
4607
|
+
* your own analytics reads `touched`; reach for
|
|
4608
|
+
* {@link UseFormReturnType.interact} to make
|
|
4811
4609
|
* seeded or imported values surface their errors.
|
|
4812
4610
|
*
|
|
4813
4611
|
* Pure flag write — does not mutate value, focused, blurred, or
|
|
@@ -4959,5 +4757,5 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
4959
4757
|
blankPaths: ComputedRef<BlankPathsView>;
|
|
4960
4758
|
};
|
|
4961
4759
|
|
|
4962
|
-
export { ROOT_PATH as
|
|
4963
|
-
export type {
|
|
4760
|
+
export { ROOT_PATH as R, canonicalizePath as aj, isPathPrefix as ak, isUnset as al, parseDottedPath as am, unset as an, ROOT_PATH_KEY as z };
|
|
4761
|
+
export type { SchemaDefaultsResult as $, AbstractSchema as A, ReactiveValidationStatus as B, CustomDirectiveRegisterAssignerFn as C, DefaultValuesInput as D, ErrorInput as E, FormKey as F, GenericForm as G, HandleSubmit as H, IsTuple as I, JoinSegments as J, KeyofUnion as K, LiftedValueShape as L, MetaTrackerValue as M, NestedReadType as N, OnError as O, ParseOptions as P, RecordPath as Q, RecordValue as S, RegisterDirective as T, UseFormConfiguration as U, RegisterFlatPath as V, RegisterOptions as W, RegisterSelectModifier as X, RegisterTextModifier as Y, RegisterTransform as Z, RegisterValue as _, UseFormReturnType as a, SchemaParseErrorWithData as a0, SchemaParseErrorWithoutData as a1, SchemaParseResult as a2, SchemaParseSuccess as a3, Segment as a4, SetValueCallback as a5, SetValuePayload as a6, SettledValidationStatus as a7, SlimPrimitiveKind as a8, SubmitHandler as a9, Unset as aa, ValidateOn as ab, ValidateOnConfig as ac, ValidationError as ad, ValidationResponse as ae, ValidationResponseWithoutValue as af, ValueOfUnion as ag, WriteMeta as ah, WriteShape as ai, RegisterModelDynamicCustomDirective as ao, SchemaFactoryOptions as ap, AcceptableDefaults as aq, DisplayCtx as ar, GetDisplayState as as, DisplayMachine as at, FieldRecord as au, ErrorCell as av, OriginalsRecord as aw, AttaformDomBinding as ax, TransformAbortHolder as ay, ArrayItem as b, ArrayPath as c, DeepPartial as d, DefaultValuesShape as e, DisplayState as f, ErrorsProxyShape as g, FieldMetaPayload as h, FieldState as i, FieldStateMap as j, FieldStateMapEntry as k, FlatPath as l, FormErrorRecord as m, FormErrorsSurface as n, FormMeta as o, HistoryPlugin as p, IsUnion as q, Json as r, NestedType as s, OnSubmit as t, PartialFlatPath as u, Path as v, PathKey as w, PendingValidationStatus as x, Primitive as y };
|