attaform 0.28.0 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/abstract.d.mts +5 -5
  2. package/dist/abstract.mjs +4 -4
  3. package/dist/chunks/fingerprint.mjs +1 -1
  4. package/dist/chunks/fingerprint2.mjs +2 -2
  5. package/dist/dev/abstract.mjs +4 -4
  6. package/dist/dev/chunks/devtools.mjs +1 -1
  7. package/dist/dev/chunks/fingerprint.mjs +1 -1
  8. package/dist/dev/chunks/fingerprint2.mjs +2 -2
  9. package/dist/dev/directive.mjs +1 -1
  10. package/dist/dev/history.mjs +1 -1
  11. package/dist/dev/index.mjs +5 -5
  12. package/dist/dev/runtime/plugins/attaform.mjs +1 -1
  13. package/dist/dev/shared/{attaform-dev.Dr2YzS5B.mjs → attaform-dev.A-TpJW0B.mjs} +11 -1
  14. package/dist/dev/shared/{attaform-dev.oYrkP9dj.mjs → attaform-dev.BO-71R54.mjs} +51 -158
  15. package/dist/dev/shared/{attaform-dev.6qxD-Yqo.mjs → attaform-dev.BgM6FdtA.mjs} +143 -36
  16. package/dist/{shared/attaform.h0i6LijT.mjs → dev/shared/attaform-dev.ButTkwHd.mjs} +149 -16
  17. package/dist/{shared/attaform.BJGA_UOS.mjs → dev/shared/attaform-dev.BxfsuwBR.mjs} +8 -0
  18. package/dist/dev/shared/{attaform-dev.P7cseiEG.mjs → attaform-dev.D8aoKb89.mjs} +13 -4
  19. package/dist/dev/shared/{attaform-dev.BXE2cr0L.mjs → attaform-dev.DD5m2W51.mjs} +1 -1
  20. package/dist/dev/shared/{attaform-dev.WmuhD_CZ.mjs → attaform-dev.DHuydLAC.mjs} +344 -96
  21. package/dist/dev/shared/{attaform-dev.BR8u0YOZ.mjs → attaform-dev.DrzObAZo.mjs} +4 -4
  22. package/dist/dev/shared/{attaform-dev.D2I8e_Mm.mjs → attaform-dev.q6UutkCx.mjs} +170 -132
  23. package/dist/dev/zod-v3.mjs +6 -6
  24. package/dist/dev/zod-v4.mjs +6 -6
  25. package/dist/dev/zod.mjs +5 -5
  26. package/dist/directive.d.mts +2 -2
  27. package/dist/directive.mjs +1 -1
  28. package/dist/history.d.mts +1 -1
  29. package/dist/history.mjs +1 -1
  30. package/dist/index.d.mts +9 -7
  31. package/dist/index.mjs +5 -5
  32. package/dist/nuxt.d.mts +10 -10
  33. package/dist/runtime/plugins/attaform.mjs +1 -1
  34. package/dist/shared/{attaform.C09fpdLS.mjs → attaform.B0dKL8ST.mjs} +143 -36
  35. package/dist/shared/{attaform.Cs2Rbdlo.mjs → attaform.BK0cgu2K.mjs} +123 -139
  36. package/dist/shared/{attaform.D6DCnnQu.mjs → attaform.BndjUUYX.mjs} +4 -4
  37. package/dist/{dev/shared/attaform-dev.BJGA_UOS.mjs → shared/attaform.BxfsuwBR.mjs} +8 -0
  38. package/dist/shared/{attaform.NLmerepI.mjs → attaform.C2MVyuxC.mjs} +42 -151
  39. package/dist/shared/{attaform.B4VkS43-.d.mts → attaform.CIWLMY-R.d.mts} +1 -1
  40. package/dist/shared/{attaform.B8k8JrmK.mjs → attaform.CZMAYwSK.mjs} +338 -95
  41. package/dist/shared/{attaform.wfY8XFvm.mjs → attaform.CZYt_5N_.mjs} +11 -1
  42. package/dist/shared/{attaform.Cgu-hdSb.d.mts → attaform.CsoeRi3P.d.mts} +224 -32
  43. package/dist/{dev/shared/attaform-dev.By4PRjt0.mjs → shared/attaform.Cvao8bW5.mjs} +132 -22
  44. package/dist/shared/{attaform.Dq4vWxuG.mjs → attaform.D0x-qSXm.mjs} +1 -1
  45. package/dist/shared/{attaform.CJP3RkGa.mjs → attaform.D1fnTifF.mjs} +168 -132
  46. package/dist/shared/{attaform.D5V4tZWq.d.mts → attaform.DDtqHYv1.d.mts} +88 -7
  47. package/dist/shared/{attaform.uLbun31C.mjs → attaform.DffRL5pP.mjs} +12 -4
  48. package/dist/shared/{attaform.JfSWdEcb.d.mts → attaform.axHXVQO7.d.mts} +1 -1
  49. package/dist/transforms.d.mts +0 -22
  50. package/dist/transforms.mjs +1 -1
  51. package/dist/vite.d.mts +13 -4
  52. package/dist/vite.mjs +3 -2
  53. package/dist/zod-v3.d.mts +6 -4
  54. package/dist/zod-v3.mjs +6 -6
  55. package/dist/zod-v4.d.mts +14 -27
  56. package/dist/zod-v4.mjs +6 -6
  57. package/dist/zod.d.mts +4 -4
  58. package/dist/zod.mjs +5 -5
  59. package/package.json +17 -14
  60. package/skills/attaform/SKILL.md +5 -2
  61. package/skills/attaform/references/custom-components.md +7 -2
  62. package/skills/attaform/references/errors.md +8 -3
  63. package/skills/attaform/references/saving.md +77 -0
  64. package/skills/attaform/references/wizards.md +10 -8
@@ -79,6 +79,16 @@ function groupErrorsByKey(entries) {
79
79
  }
80
80
 
81
81
  const INTEGER_SEGMENT = /^(?:0|[1-9]\d*)$/;
82
+ const SET_MEMBER_SEGMENT = "__atta:member";
83
+ function mapSegmentKeys(value) {
84
+ const keys = [];
85
+ for (const key of value.keys()) {
86
+ if (typeof key === "string") keys.push(key);
87
+ else if (typeof key === "number" && Number.isInteger(key) && key >= 0) keys.push(key);
88
+ else return null;
89
+ }
90
+ return keys;
91
+ }
82
92
  function normalizeSegment(raw) {
83
93
  if (typeof raw === "number") {
84
94
  if (!Number.isInteger(raw) || raw < 0) {
@@ -194,4 +204,4 @@ function pathsEqual(a, b) {
194
204
  return true;
195
205
  }
196
206
 
197
- export { AttaformError as A, InvalidPathError as I, OutsideSetupError as O, ROOT_PATH as R, SubmitErrorHandlerError as S, InvalidUseFormConfigError as a, ROOT_PATH_KEY as b, RegistryNotInstalledError as c, ReservedFormKeyError as d, canonicalizePath as e, pathsEqual as f, pathKeyToDotted as g, cellEntriesFor as h, isPathPrefix as i, groupErrorsByKey as j, keyForSegments as k, normalizeErrorInputs as l, coerceToPathKey as m, normalizeErrorInput as n, parseDottedPath as p, segmentsForPathKey as s, toError as t };
207
+ export { AttaformError as A, InvalidPathError as I, OutsideSetupError as O, ROOT_PATH as R, SubmitErrorHandlerError as S, InvalidUseFormConfigError as a, ROOT_PATH_KEY as b, RegistryNotInstalledError as c, ReservedFormKeyError as d, canonicalizePath as e, pathsEqual as f, pathKeyToDotted as g, SET_MEMBER_SEGMENT as h, isPathPrefix as i, cellEntriesFor as j, keyForSegments as k, groupErrorsByKey as l, mapSegmentKeys as m, normalizeErrorInput as n, normalizeErrorInputs as o, parseDottedPath as p, coerceToPathKey as q, segmentsForPathKey as s, toError as t };
@@ -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 ? NestedType<Form, P> extends readonly unknown[] ? P : never : never;
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 ArrayItem<Form, Path extends ArrayPath<Form>> = NestedType<Form, Path> extends ReadonlyArray<infer Item> ? Item : never;
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 ? NestedType<Form, P> extends readonly unknown[] ? never : NestedType<Form, P> extends Record<string, unknown> ? string extends keyof NestedType<Form, P> ? P : never : never : never;
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>> = NestedType<Form, Path> extends Record<string, infer Value> ? Value : never;
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."
@@ -1088,6 +1175,28 @@ type AbstractSchema<Form, GetValueFormType> = {
1088
1175
  * the result by canonicalized path key.
1089
1176
  */
1090
1177
  isPreprocessOrCoerceLeaf(path: Path): boolean;
1178
+ /**
1179
+ * Reports whether `path` resolves to an opaque leaf: a schema that
1180
+ * declares a value without describing its shape. In Zod v4 that's
1181
+ * `z.any()`, `z.unknown()`, and `z.custom(...)` (the kind both
1182
+ * `z.instanceof(X)` and `z.custom<T>()` compile to); in Zod v3 it's
1183
+ * `z.any()` / `z.unknown()`, with `z.instanceof(X)` peeling through
1184
+ * `ZodEffects` to `ZodAny`.
1185
+ *
1186
+ * Consulted by the slim-primitive write gate. An opaque leaf admits
1187
+ * every kind, INCLUDING containers, and it declares no sub-paths.
1188
+ * Without this the gate accepts an array at the leaf and then walks
1189
+ * into it, checking `files.0` against a schema that never declared
1190
+ * that path; the empty accept set there reads as "not in your
1191
+ * schema" and no-ops the whole write (#542).
1192
+ *
1193
+ * Exact-path semantic, unlike `isPreprocessOrCoerceLeaf`: it answers
1194
+ * for the node at `path` only. Descendants stay unwritable on their
1195
+ * own, because the schema genuinely does not declare them and
1196
+ * fabricating them would put phantom nodes on `form.fields`. Writing
1197
+ * the whole value at the opaque leaf is the supported move.
1198
+ */
1199
+ isOpaqueLeafAtPath(path: Path): boolean;
1091
1200
  /**
1092
1201
  * Distinguish a tuple (fixed-length, position-typed) from an
1093
1202
  * unbounded array at `path`. The runtime calls this on every
@@ -1133,6 +1242,29 @@ type AbstractSchema<Form, GetValueFormType> = {
1133
1242
  * `false`.
1134
1243
  */
1135
1244
  isFixedObjectAtPath(path: Path): boolean;
1245
+ /**
1246
+ * How the container at `path` spells the keys of its own entries:
1247
+ * `'number'` for an array or a tuple, `'string'` for an object or a
1248
+ * record, and for a `z.map` the kind its declared key type accepts.
1249
+ * `undefined` for anything else, a leaf and an undeclared path
1250
+ * included.
1251
+ *
1252
+ * The write walkers consult it at a `Map`, where a new entry has to
1253
+ * be filed under a key of the declared type and the path segment
1254
+ * alone cannot say which: an integer-looking segment canonicalises
1255
+ * to a number, so `scores.42` against a `z.map(z.string(), V)` would
1256
+ * otherwise take the number `42` as its key and fail the map's own
1257
+ * parse. An entry the map already holds needs no ruling — its
1258
+ * existing key wins.
1259
+ *
1260
+ * A map whose key type is neither string-ish nor number-ish, or
1261
+ * which admits both, reports `undefined`: no segment can spell such
1262
+ * a key, so the map stays a whole value with no addressable entries.
1263
+ *
1264
+ * Adapters MAY cache per path; the runtime calls this on the write
1265
+ * path.
1266
+ */
1267
+ entryKeyKindAtPath(path: Path): 'string' | 'number' | undefined;
1136
1268
  /**
1137
1269
  * Return every sub-schema that could resolve at the given structured
1138
1270
  * path. Multiple results are only expected for discriminated / union
@@ -1203,9 +1335,12 @@ type AbstractSchema<Form, GetValueFormType> = {
1203
1335
  */
1204
1336
  getSlimPrimitiveTypesAtPath(path: Path): Set<SlimPrimitiveKind>;
1205
1337
  /**
1206
- * Return `true` iff `path` resolves to a **leaf** in the schema — a
1207
- * path whose slim primitive set contains only primitive kinds (no
1208
- * `object`, `array`, `map`, `set`). The runtime proxies (`form.values`,
1338
+ * Return `true` iff `path` resolves to a **leaf** in the schema: a
1339
+ * path the schema declares no sub-paths under. That is every path
1340
+ * whose slim primitive set contains only primitive kinds (no
1341
+ * `object`, `array`, `map`, `set`), plus the opaque leaves, which
1342
+ * admit every kind and are classified by kind instead (below). The
1343
+ * runtime proxies (`form.values`,
1209
1344
  * `form.errors`, `form.fields`) query this at every step to decide
1210
1345
  * between **descend into a sub-proxy** (container) and **terminate
1211
1346
  * with a leaf value** (leaf).
@@ -1223,6 +1358,14 @@ type AbstractSchema<Form, GetValueFormType> = {
1223
1358
  * date/function) → `true`. `'date'` counts as a leaf (don't drill
1224
1359
  * into `Date`). `'function'` is a leaf for the same reason — opaque
1225
1360
  * value.
1361
+ * - **Opaque leaf** (`z.any()`, `z.unknown()`, `z.custom()`, and the
1362
+ * `z.instanceof(X)` that compiles to it) → `true`, and it answers
1363
+ * ahead of the kind test. An opaque leaf admits every kind
1364
+ * INCLUDING the container ones, so reading it off the slim
1365
+ * primitive set alone classifies it as a container and puts
1366
+ * phantom nodes under it on `form.fields` — see
1367
+ * `isOpaqueLeafAtPath`. What leafness asks is whether the schema
1368
+ * declares sub-paths here, and an opaque leaf declares none.
1226
1369
  * - **Optional / Nullable / Default / Catch** wrappers transparent —
1227
1370
  * adds `'null'` / `'undefined'` to the inner kind set without
1228
1371
  * changing the leaf classification.
@@ -3213,9 +3356,11 @@ declare module 'vue' {
3213
3356
  * form.setValue((prev) => ({ ...prev, name: 'Ada' }))
3214
3357
  * ```
3215
3358
  *
3216
- * The library fills any missing structural slots (e.g. nested
3217
- * objects) against the schema's defaults after the callback returns,
3218
- * so partial returns are safe.
3359
+ * A whole-form callback replaces the whole form. Any key the return
3360
+ * omits is refilled from the SCHEMA's declared defaults (the slim
3361
+ * value where none is declared), so an omitted key is not carried
3362
+ * over from the previous value or from `defaultValues` — spread
3363
+ * `prev` to keep the rest of the form.
3219
3364
  */
3220
3365
  type SetValueCallback<Read, Write = Read> = (prev: Read) => Read | Write;
3221
3366
  /**
@@ -3848,7 +3993,7 @@ type FieldStateMap<Form extends GenericForm> = LeafWalker<Form, 'field'> & {
3848
3993
  * dot/bracket access (`form.fields.users[i].email`), which is what
3849
3994
  * descends.
3850
3995
  */
3851
- <const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown : never)): FieldState<NestedType<Form, JoinSegments<S>>>;
3996
+ <const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown : SegmentPathRejection<JoinSegments<S>>)): FieldState<NestedType<Form, JoinSegments<S>>>;
3852
3997
  /**
3853
3998
  * Dynamic-array fallback for callers passing `Path`-typed (runtime)
3854
3999
  * segment arrays — e.g. forwarding `RegisterValue.segments` to
@@ -3919,7 +4064,7 @@ type FormErrorsSurface<Form> = ErrorsProxyShape<Form> & {
3919
4064
  * tuples that don't resolve to a known path fail at the call site.
3920
4065
  * Dynamic `Path`-typed inputs hit the untyped fallback overload below.
3921
4066
  */
3922
- <const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown : never)): readonly ValidationError[];
4067
+ <const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown : SegmentPathRejection<JoinSegments<S>>)): readonly ValidationError[];
3923
4068
  (segments: ReadonlyArray<string | number>): readonly ValidationError[];
3924
4069
  /**
3925
4070
  * No-arg call returns the whole-form error aggregate, the same as
@@ -3964,6 +4109,27 @@ type ErrorsProxyShape<T> = LeafWalker<T, 'errors', false>;
3964
4109
  * form.values() // the whole form value (root)
3965
4110
  * ```
3966
4111
  *
4112
+ * The two shapes answer different questions. Dot access is the
4113
+ * REACTIVE view: per-key tracking, no copying, always the live value.
4114
+ * The call form returns a SNAPSHOT: a detached plain object that keeps
4115
+ * what it held at capture time, so it is the shape to hand an async
4116
+ * call, a serialiser, or a diff.
4117
+ *
4118
+ * ```ts
4119
+ * const before = form.values() // detached; survives later writes
4120
+ * await api.save(before) // cannot mutate underneath the call
4121
+ * form.values.email // reactive read; use this in computed
4122
+ * ```
4123
+ *
4124
+ * Prefer dot access inside a `computed`, `watchEffect`, or template:
4125
+ * it tracks the one key you read, where the call form depends on the
4126
+ * whole form and re-runs the consumer on any change.
4127
+ *
4128
+ * The copy is deep across plain objects and arrays. Non-plain
4129
+ * instances (Map, Set, File, Date) are shared by reference, matching
4130
+ * `JSON.stringify` behaviour; `structuredClone(form.values())` gives a
4131
+ * fully detached copy when one is needed.
4132
+ *
3967
4133
  * Single-bracket dotted access (`form.values['address.city']`) is
3968
4134
  * intentionally NOT supported — JS object semantics treat the dotted
3969
4135
  * string as a single key. Use chained dot/bracket or the callable
@@ -4007,7 +4173,9 @@ type ValuesSurface<F> = Readonly<LiftedValueShape<F>> & {
4007
4173
  * `form.fields.<path>`; this is the aggregate view across the
4008
4174
  * whole form.
4009
4175
  *
4010
- * Read-only at runtime — assignments throw. Destructuring snapshots
4176
+ * Read-only at runtime — an assignment is refused with a dev-console
4177
+ * warning and never lands (Attaform does not throw from a read
4178
+ * surface). Destructuring snapshots
4011
4179
  * the current values; use `toRefs()` if you need reactive handles
4012
4180
  * to individual fields.
4013
4181
  */
@@ -4351,7 +4519,7 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
4351
4519
  * `form.setValue([prefix, 'line1'], 'value')`. The resolved leaf
4352
4520
  * type is exact, matching the dotted-string form.
4353
4521
  */
4354
- <const S extends ReadonlyArray<string | number>, Value extends PathSetValuePayload<NestedType<Form, JoinSegments<S>>>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown : never), value: Value): boolean;
4522
+ <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
4523
  };
4356
4524
  /**
4357
4525
  * Reactive validation status. Re-runs whenever the form (or the
@@ -4463,7 +4631,7 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
4463
4631
  */
4464
4632
  register: {
4465
4633
  <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 : never), options?: RegisterOptions): RegisterValue<NestedReadType<WriteShape<ReadForm>, JoinSegments<S>>>;
4634
+ <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
4635
  };
4468
4636
  /**
4469
4637
  * The form's identifier — either the explicit `key` passed to
@@ -4544,8 +4712,12 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
4544
4712
  *
4545
4713
  * Resolves after `hydrating` flips back to `false`. Throws
4546
4714
  * synchronously when the form was constructed with a plain-value
4547
- * `defaultValues` (nothing to re-fire). Does NOT clear dirty /
4548
- * touched / submit state — chain `form.reset()` for that.
4715
+ * `defaultValues` (nothing to re-fire).
4716
+ *
4717
+ * The payload becomes the form's defaults, the same way a
4718
+ * `reset(next)` argument does, so `form.reset()` afterwards lands on
4719
+ * the values just fetched. Does NOT clear touched / submit state;
4720
+ * chain `form.reset()` for that.
4549
4721
  */
4550
4722
  rehydrate(): Promise<void>;
4551
4723
  /**
@@ -4603,7 +4775,7 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
4603
4775
  */
4604
4776
  toRef: {
4605
4777
  <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 : never)): Readonly<Ref<NestedReadType<WriteShape<ReadForm>, JoinSegments<S>>>>;
4778
+ <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
4779
  };
4608
4780
  /**
4609
4781
  * Set the form's manual error layer. One surface for server-side
@@ -4673,10 +4845,10 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
4673
4845
  */
4674
4846
  meta: FormMeta<Form>;
4675
4847
  /**
4676
- * Restore the form to its initial state. Without arguments,
4677
- * re-applies the schema defaults (and any `defaultValues` passed
4678
- * to `useForm`). Pass `nextDefaultValues` to seed the reset with
4679
- * a fresh set of overrides.
4848
+ * Restore the form to its defaults. Without arguments, re-applies
4849
+ * whatever the defaults currently are: `useForm({ defaultValues })`
4850
+ * on a fresh form, or the last values a `reset(next)` or an async
4851
+ * `defaultValues` factory settled on.
4680
4852
  *
4681
4853
  * Resets:
4682
4854
  * - the form value back to defaults;
@@ -4685,6 +4857,26 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
4685
4857
  * - touched / focused / blurred per-field flags;
4686
4858
  * - submission state (`submitting` / `submissionAttempts` /
4687
4859
  * `submitted` / `submitError`).
4860
+ *
4861
+ * `nextDefaultValues` RE-SEATS the defaults rather than applying for
4862
+ * one call, so the form has a single set of defaults that `reset()`,
4863
+ * `resetField(path)`, and `dirty` all read. That is what makes the
4864
+ * save-then-discard shape work: reset from the resource the server
4865
+ * returned, and a later Discard returns to the save instead of
4866
+ * rolling back across it.
4867
+ *
4868
+ * ```ts
4869
+ * const saved = await api.save(form.values())
4870
+ * form.reset(toValues(saved)) // the saved resource is now the baseline
4871
+ * // …later…
4872
+ * form.reset() // back to the saved resource
4873
+ * ```
4874
+ *
4875
+ * The argument is sparse: it folds over the defaults already in
4876
+ * force, so paths it does not name keep the value they had, and
4877
+ * `reset({})` changes nothing. Arrays are replaced wholesale rather
4878
+ * than merged element-wise. Pass `unset` at a path to withdraw its
4879
+ * value and mark it blank.
4688
4880
  */
4689
4881
  reset: (nextDefaultValues?: DefaultValuesInput<Form>) => void;
4690
4882
  /**
@@ -4739,7 +4931,7 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
4739
4931
  clear: {
4740
4932
  (): boolean;
4741
4933
  <Path extends FlatPath<Form> | ''>(path: Path): boolean;
4742
- <const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form> | ''] ? unknown : never)): boolean;
4934
+ <const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form> | ''] ? unknown : SegmentPathRejection<JoinSegments<S>>)): boolean;
4743
4935
  };
4744
4936
  /**
4745
4937
  * Consolidated undo/redo namespace — `form.history.{undo, redo,
@@ -4959,5 +5151,5 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
4959
5151
  blankPaths: ComputedRef<BlankPathsView>;
4960
5152
  };
4961
5153
 
4962
- export { ROOT_PATH as Y, ROOT_PATH_KEY as Z, canonicalizePath as am, isPathPrefix as an, isUnset as ao, parseDottedPath as ap, unset as aq };
4963
- export type { RegisterDirective as $, AbstractSchema as A, NestedType as B, CoercionEntry 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, OnInvalidSubmitPolicy as P, OnSubmit as Q, ParseOptions as R, PartialFlatPath as S, Path as T, UseFormConfiguration as U, PathKey as V, PendingValidationStatus as W, Primitive as X, ReactiveValidationStatus as _, UseFormReturnType as a, RegisterFlatPath as a0, RegisterOptions as a1, RegisterSelectModifier as a2, RegisterTextModifier as a3, RegisterTransform as a4, RegisterValue as a5, Segment as a6, SetValueCallback as a7, SetValuePayload as a8, SettledValidationStatus as a9, SlimPrimitiveKind as aa, SlimRuntimeOf as ab, SubmitHandler as ac, Unset as ad, ValidateOn as ae, ValidateOnConfig as af, ValidationError as ag, ValidationResponse as ah, ValidationResponseWithoutValue as ai, ValueOfUnion as aj, WriteMeta as ak, WriteShape as al, RegisterModelDynamicCustomDirective as ar, AcceptableDefaults as as, SchemaFactoryOptions as at, FieldRecord as au, ErrorCell as av, OriginalsRecord as aw, AttaformDomBinding as ax, TransformAbortHolder as ay, ArrayItem as b, ArrayPath as c, AttaformDefaults as d, CoercionRegistry as e, CoercionResult as f, CustomDirectiveRegisterAssignerFn as g, DeepPartial as h, DefaultValuesResponse as i, DefaultValuesShape as j, DisplayCtx as k, DisplayMachine as l, DisplayState as m, ErrorsProxyShape as n, FieldMetaPayload as o, FieldState as p, FieldStateMap as q, FieldStateMapEntry as r, FlatPath as s, FormErrorRecord as t, FormErrorsSurface as u, FormMeta as v, GetDisplayState as w, HistoryPlugin as x, IsUnion as y, Json as z };
5154
+ export { ROOT_PATH as Y, ROOT_PATH_KEY as Z, canonicalizePath as ao, isPathPrefix as ap, isUnset as aq, parseDottedPath as ar, unset as as };
5155
+ export type { RecordPath as $, AbstractSchema as A, NestedType as B, CoercionEntry 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, OnInvalidSubmitPolicy as P, OnSubmit as Q, ParseOptions as R, PartialFlatPath as S, Path as T, UseFormConfiguration as U, PathKey as V, PendingValidationStatus as W, Primitive as X, ReactiveValidationStatus as _, UseFormReturnType as a, RecordValue as a0, RegisterDirective as a1, RegisterFlatPath as a2, RegisterOptions as a3, RegisterSelectModifier as a4, RegisterTextModifier as a5, RegisterTransform as a6, RegisterValue as a7, Segment as a8, SetValueCallback as a9, TransformAbortHolder as aA, SetValuePayload as aa, SettledValidationStatus as ab, SlimPrimitiveKind as ac, SlimRuntimeOf as ad, SubmitHandler as ae, Unset as af, ValidateOn as ag, ValidateOnConfig as ah, ValidationError as ai, ValidationResponse as aj, ValidationResponseWithoutValue as ak, ValueOfUnion as al, WriteMeta as am, WriteShape as an, RegisterModelDynamicCustomDirective as at, SchemaFactoryOptions as au, AcceptableDefaults as av, FieldRecord as aw, ErrorCell as ax, OriginalsRecord as ay, AttaformDomBinding as az, ArrayItem as b, ArrayPath as c, AttaformDefaults as d, CoercionRegistry as e, CoercionResult as f, CustomDirectiveRegisterAssignerFn as g, DeepPartial as h, DefaultValuesResponse as i, DefaultValuesShape as j, DisplayCtx as k, DisplayMachine as l, DisplayState as m, ErrorsProxyShape as n, FieldMetaPayload as o, FieldState as p, FieldStateMap as q, FieldStateMapEntry as r, FlatPath as s, FormErrorRecord as t, FormErrorsSurface as u, FormMeta as v, GetDisplayState as w, HistoryPlugin as x, IsUnion as y, Json as z };