attaform 0.18.2 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/chunks/devtools.cjs +1 -1
- package/dist/chunks/devtools.mjs +1 -1
- package/dist/chunks/indexeddb.cjs +1 -1
- package/dist/chunks/indexeddb.mjs +1 -1
- package/dist/chunks/local-storage.cjs +1 -1
- package/dist/chunks/local-storage.mjs +1 -1
- package/dist/chunks/session-storage.cjs +1 -1
- package/dist/chunks/session-storage.mjs +1 -1
- package/dist/index.cjs +4 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +77 -110
- package/dist/index.d.mts +77 -110
- package/dist/index.d.ts +77 -110
- package/dist/index.mjs +5 -5
- package/dist/nuxt.d.cts +1 -1
- package/dist/nuxt.d.mts +1 -1
- package/dist/nuxt.d.ts +1 -1
- package/dist/runtime/components/AttaformDevtoolsPanel.vue +2 -2
- package/dist/runtime/components/DevtoolsValueTree.d.vue.ts +1 -3
- package/dist/runtime/components/DevtoolsValueTree.vue.d.ts +1 -3
- package/dist/runtime/plugins/attaform.cjs +2 -2
- package/dist/runtime/plugins/attaform.mjs +2 -2
- package/dist/shared/{attaform.CDmaxrt2.mjs → attaform.BKozEdTr.mjs} +305 -178
- package/dist/shared/attaform.BKozEdTr.mjs.map +1 -0
- package/dist/shared/{attaform.Bubm_slq.cjs → attaform.BM6YD9kZ.cjs} +212 -269
- package/dist/shared/attaform.BM6YD9kZ.cjs.map +1 -0
- package/dist/shared/{attaform.5UhpSVFI.cjs → attaform.BPxsYtTe.cjs} +2 -26
- package/dist/shared/attaform.BPxsYtTe.cjs.map +1 -0
- package/dist/shared/{attaform.BqK_L4gK.cjs → attaform.BPy-4qRx.cjs} +305 -180
- package/dist/shared/attaform.BPy-4qRx.cjs.map +1 -0
- package/dist/shared/attaform.BWgAFnsj.mjs +770 -0
- package/dist/shared/attaform.BWgAFnsj.mjs.map +1 -0
- package/dist/shared/{attaform.CGX1CNpz.d.ts → attaform.Bh3ACtts.d.ts} +152 -111
- package/dist/shared/{attaform.CXpzmj38.mjs → attaform.BupwXkj_.mjs} +213 -270
- package/dist/shared/attaform.BupwXkj_.mjs.map +1 -0
- package/dist/shared/{attaform.Dlk1jMuv.cjs → attaform.CIn4bMsD.cjs} +263 -799
- package/dist/shared/attaform.CIn4bMsD.cjs.map +1 -0
- package/dist/shared/{attaform.CZ-XtZt_.mjs → attaform.CKFbKFb6.mjs} +2265 -1509
- package/dist/shared/attaform.CKFbKFb6.mjs.map +1 -0
- package/dist/shared/{attaform.CuN7ZhBy.d.cts → attaform.D5-1XGQU.d.cts} +152 -111
- package/dist/shared/{attaform.-1GQTX2T.mjs → attaform.DEBvCjeH.mjs} +257 -793
- package/dist/shared/attaform.DEBvCjeH.mjs.map +1 -0
- package/dist/shared/{attaform.II89Pcf4.cjs → attaform.DL4CQ-oW.cjs} +2270 -1514
- package/dist/shared/attaform.DL4CQ-oW.cjs.map +1 -0
- package/dist/shared/{attaform.FnEwjhvX.d.ts → attaform.DSD85fHb.d.cts} +1 -19
- package/dist/shared/{attaform.CRmmNAYp.d.cts → attaform.DSD85fHb.d.mts} +1 -19
- package/dist/shared/{attaform.D9wuTGu9.d.mts → attaform.DSD85fHb.d.ts} +1 -19
- package/dist/shared/{attaform.B7rzpK1U.d.cts → attaform.DkA5J8NW.d.cts} +1 -17
- package/dist/shared/{attaform.B7rzpK1U.d.mts → attaform.DkA5J8NW.d.mts} +1 -17
- package/dist/shared/{attaform.B7rzpK1U.d.ts → attaform.DkA5J8NW.d.ts} +1 -17
- package/dist/shared/{attaform.B957T6NU.d.ts → attaform.Dl5kDY-A.d.ts} +1 -1
- package/dist/shared/attaform.Dmb6itxC.cjs +781 -0
- package/dist/shared/attaform.Dmb6itxC.cjs.map +1 -0
- package/dist/shared/{attaform.M-RanbyV.d.mts → attaform.DoKXru-a.d.mts} +1 -1
- package/dist/shared/attaform.DvA-CJJW.mjs +1876 -0
- package/dist/shared/attaform.DvA-CJJW.mjs.map +1 -0
- package/dist/shared/{attaform.D1gzu2GL.d.mts → attaform.EMzJcQci.d.mts} +152 -111
- package/dist/shared/attaform.EZG6fOFb.mjs +35 -0
- package/dist/shared/attaform.EZG6fOFb.mjs.map +1 -0
- package/dist/shared/{attaform.XDjA7sRz.d.cts → attaform.GbDo_lJi.d.cts} +1 -1
- package/dist/shared/{attaform.Ca5_6Ky-.d.mts → attaform.SfhU0OEY.d.cts} +499 -116
- package/dist/shared/{attaform.Ca5_6Ky-.d.cts → attaform.SfhU0OEY.d.mts} +499 -116
- package/dist/shared/{attaform.Ca5_6Ky-.d.ts → attaform.SfhU0OEY.d.ts} +499 -116
- package/dist/shared/attaform.jgzuNZVC.cjs +1882 -0
- package/dist/shared/attaform.jgzuNZVC.cjs.map +1 -0
- package/dist/transforms.cjs +2 -2
- package/dist/transforms.d.cts +22 -13
- package/dist/transforms.d.mts +22 -13
- package/dist/transforms.d.ts +22 -13
- package/dist/transforms.mjs +1 -1
- package/dist/vite.cjs +8 -7
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.mjs +8 -7
- package/dist/vite.mjs.map +1 -1
- package/dist/zod-v3.cjs +3 -3
- package/dist/zod-v3.d.cts +32 -6
- package/dist/zod-v3.d.mts +32 -6
- package/dist/zod-v3.d.ts +32 -6
- package/dist/zod-v3.mjs +3 -3
- package/dist/zod-v4.cjs +3 -3
- package/dist/zod-v4.d.cts +12 -8
- package/dist/zod-v4.d.mts +12 -8
- package/dist/zod-v4.d.ts +12 -8
- package/dist/zod-v4.mjs +3 -3
- package/dist/zod.cjs +8 -8
- package/dist/zod.cjs.map +1 -1
- package/dist/zod.d.cts +6 -6
- package/dist/zod.d.mts +6 -6
- package/dist/zod.d.ts +6 -6
- package/dist/zod.mjs +6 -6
- package/package.json +2 -2
- package/dist/shared/attaform.-1GQTX2T.mjs.map +0 -1
- package/dist/shared/attaform.5UhpSVFI.cjs.map +0 -1
- package/dist/shared/attaform.BqK_L4gK.cjs.map +0 -1
- package/dist/shared/attaform.Bubm_slq.cjs.map +0 -1
- package/dist/shared/attaform.C8CyvYa_.cjs +0 -36
- package/dist/shared/attaform.C8CyvYa_.cjs.map +0 -1
- package/dist/shared/attaform.CDmaxrt2.mjs.map +0 -1
- package/dist/shared/attaform.CXpzmj38.mjs.map +0 -1
- package/dist/shared/attaform.CZ-XtZt_.mjs.map +0 -1
- package/dist/shared/attaform.D13GMFgK.mjs +0 -32
- package/dist/shared/attaform.D13GMFgK.mjs.map +0 -1
- package/dist/shared/attaform.DUHru0OF.cjs +0 -1600
- package/dist/shared/attaform.DUHru0OF.cjs.map +0 -1
- package/dist/shared/attaform.Df0tU0Ut.mjs +0 -1594
- package/dist/shared/attaform.Df0tU0Ut.mjs.map +0 -1
- package/dist/shared/attaform.Dl161U6E.mjs +0 -57
- package/dist/shared/attaform.Dl161U6E.mjs.map +0 -1
- package/dist/shared/attaform.Dlk1jMuv.cjs.map +0 -1
- package/dist/shared/attaform.II89Pcf4.cjs.map +0 -1
|
@@ -136,6 +136,28 @@ declare function isUnset(value: unknown): value is Unset;
|
|
|
136
136
|
type GenericForm = Record<string, unknown>;
|
|
137
137
|
/** Internal helper — `true` when `T` is an object or array. */
|
|
138
138
|
type IsObjectOrArray<T> = T extends GenericForm ? true : T extends Array<unknown> ? true : false;
|
|
139
|
+
/**
|
|
140
|
+
* Shared recursion body backing `PartialFlatPath` and `RegisterFlatPath`.
|
|
141
|
+
* One walk over `Form`; `Mode` controls whether intermediate container
|
|
142
|
+
* paths are emitted alongside their reachable leaves.
|
|
143
|
+
*
|
|
144
|
+
* - `'partial'`: emit every container path (object containers, nested-
|
|
145
|
+
* object array roots, array-of-object element containers). Used by
|
|
146
|
+
* `setValue` / `form.values.<path>` / every read-side API that needs
|
|
147
|
+
* to address a container.
|
|
148
|
+
* - `'register'`: skip container paths. `v-register` binds onto a
|
|
149
|
+
* leaf-backing native element (`<input>`, `<select>`, `<textarea>`),
|
|
150
|
+
* so container paths aren't registrable. Primitive arrays still
|
|
151
|
+
* admit the array-root path under both modes (multi-select and
|
|
152
|
+
* grouped-checkbox bindings register onto the array itself).
|
|
153
|
+
*
|
|
154
|
+
* Both walkers were independent recursions before; threading `Mode`
|
|
155
|
+
* through one body keeps the surface guarded by a single source of
|
|
156
|
+
* truth, and `PartialFlatPath` / `RegisterFlatPath` stay byte-
|
|
157
|
+
* identical to the prior hand-written walks (see
|
|
158
|
+
* `test/types/flat-path-walker.test.ts`).
|
|
159
|
+
*/
|
|
160
|
+
type FlatPathBuilder<Form, Mode extends 'partial' | 'register', Key extends keyof Form = keyof Form> = IsObjectOrArray<Form> extends true ? Key extends string ? Form[Key] extends infer Value ? Value extends Array<infer ArrayItem> ? IsObjectOrArray<ArrayItem> extends true ? Mode extends 'partial' ? `${Key}` | `${Key}.${number}` | `${Key}.${number}.${FlatPathBuilder<ArrayItem, Mode>}` : `${Key}.${number}.${FlatPathBuilder<ArrayItem, Mode>}` : `${Key}` | `${Key}.${number}` : Value extends GenericForm ? Mode extends 'partial' ? `${Key}` | `${Key}.${FlatPathBuilder<Value, Mode>}` : `${Key}.${FlatPathBuilder<Value, Mode>}` : `${Key}` : never : Key extends number ? `${Key}` | (Form[Key] extends GenericForm ? `${Key}.${FlatPathBuilder<Form[Key], Mode>}` : Form[Key] extends Array<infer ArrayItem> ? IsObjectOrArray<ArrayItem> extends true ? Mode extends 'partial' ? `${Key}.${number}` | `${Key}.${number}.${FlatPathBuilder<ArrayItem, Mode>}` : `${Key}.${number}.${FlatPathBuilder<ArrayItem, Mode>}` : `${Key}.${number}` : never) : never : never;
|
|
139
161
|
/**
|
|
140
162
|
* Implementation detail backing `FlatPath` in its default
|
|
141
163
|
* (partial-path) mode. Exported so `rollup-plugin-dts` preserves it
|
|
@@ -146,8 +168,7 @@ type IsObjectOrArray<T> = T extends GenericForm ? true : T extends Array<unknown
|
|
|
146
168
|
* when multiple complex forms share a scope. Consumers should reach
|
|
147
169
|
* for `FlatPath` instead; this alias is not part of the stable surface.
|
|
148
170
|
*/
|
|
149
|
-
type PartialFlatPath<Form, Key extends keyof Form = keyof Form> =
|
|
150
|
-
type CompleteFlatPath<Form, Key extends keyof Form = keyof Form> = IsObjectOrArray<Form> extends true ? Key extends string ? Form[Key] extends infer Value ? Value extends Array<infer ArrayItem> ? `${Key}.${number}.${CompleteFlatPath<ArrayItem>}` : Value extends GenericForm ? `${Key}.${CompleteFlatPath<Value>}` : `${Key}` : never : Key extends number ? `${Key}` | (Form[Key] extends GenericForm ? `${Key}.${CompleteFlatPath<Form[Key]>}` : Form[Key] extends Array<infer ArrayItem> ? IsObjectOrArray<ArrayItem> extends true ? `${Key}.${number}.${CompleteFlatPath<ArrayItem>}` : `${Key}.${number}` : never) : never : never;
|
|
171
|
+
type PartialFlatPath<Form, Key extends keyof Form = keyof Form> = FlatPathBuilder<Form, 'partial', Key>;
|
|
151
172
|
/**
|
|
152
173
|
* Union of dotted-string paths reachable inside `Form`, e.g. for
|
|
153
174
|
* `{ user: { email: string }, items: string[] }`:
|
|
@@ -157,11 +178,8 @@ type CompleteFlatPath<Form, Key extends keyof Form = keyof Form> = IsObjectOrArr
|
|
|
157
178
|
* Used by every path-addressed API (`setValue(path, value)`,
|
|
158
179
|
* `register(path)`, `toRef(path)`, etc.) so paths autocomplete in
|
|
159
180
|
* the IDE and typos compile-error.
|
|
160
|
-
*
|
|
161
|
-
* Set `ForceFullPath` to `true` to restrict to leaf paths only
|
|
162
|
-
* (no intermediate container paths).
|
|
163
181
|
*/
|
|
164
|
-
type FlatPath<Form, Key extends keyof Form = keyof Form
|
|
182
|
+
type FlatPath<Form, Key extends keyof Form = keyof Form> = PartialFlatPath<Form, Key>;
|
|
165
183
|
/**
|
|
166
184
|
* Convert a tuple of path segments to its dotted-string equivalent.
|
|
167
185
|
*
|
|
@@ -251,6 +269,31 @@ type LiftedValueShape<T> = [T] extends [
|
|
|
251
269
|
type DeepPartial<T> = T extends Primitive ? T : T extends Array<infer ArrayItem> ? DeepPartial<ArrayItem>[] : T extends object ? {
|
|
252
270
|
[Key in keyof T]?: DeepPartial<T[Key]>;
|
|
253
271
|
} : T;
|
|
272
|
+
/**
|
|
273
|
+
* Shared descent body backing `NestedType` and `NestedReadType`. The
|
|
274
|
+
* recursion is identical — segment-by-segment, distributing over
|
|
275
|
+
* union members via `KeyofUnion` / `ValueOfUnion`. The two walkers
|
|
276
|
+
* diverge only at the leaf:
|
|
277
|
+
*
|
|
278
|
+
* - `TaintArrayCrossings extends false` (`NestedType` mode): leaves
|
|
279
|
+
* are returned untouched. Used by strict write-side APIs that need
|
|
280
|
+
* the exact resolved type (`setValue`'s value parameter,
|
|
281
|
+
* `form.fields.<path>`'s state map).
|
|
282
|
+
* - `TaintArrayCrossings extends true` (`NestedReadType` mode): leaves
|
|
283
|
+
* are widened with `| undefined` whenever any segment in the walk
|
|
284
|
+
* was numeric (array index). Reflects the runtime possibility of an
|
|
285
|
+
* out-of-bounds read.
|
|
286
|
+
*
|
|
287
|
+
* `_Tainted` propagates the array-crossing under taint mode; under
|
|
288
|
+
* strict mode it stays `false` through every recursion arm. Both
|
|
289
|
+
* arms strip nullishness at the root (`_RootValue = NonNullable<…>`)
|
|
290
|
+
* — the prior `NestedType.FilterOutNullishTypesDuringRecursion` flag
|
|
291
|
+
* was vestigial, never overridden from the outside.
|
|
292
|
+
*
|
|
293
|
+
* Not part of the stable consumer surface — reach for `NestedType` or
|
|
294
|
+
* `NestedReadType` directly.
|
|
295
|
+
*/
|
|
296
|
+
type NestedTypeBuilder<RootValue, FlattenedPath extends string, TaintArrayCrossings extends boolean, _Tainted extends boolean = false, _RootValue = NonNullable<RootValue>> = IsObjectOrArray<_RootValue> extends false ? never : FlattenedPath extends `${infer Key}.${infer Rest}` ? Key extends `${number}` ? Key extends KeyofUnion<_RootValue> ? NestedTypeBuilder<ValueOfUnion<_RootValue, Key>, Rest, TaintArrayCrossings, TaintArrayCrossings extends true ? true : _Tainted> : Key extends `${infer NumericKey extends number}` ? NumericKey extends KeyofUnion<_RootValue> ? NestedTypeBuilder<ValueOfUnion<_RootValue, NumericKey>, Rest, TaintArrayCrossings, TaintArrayCrossings extends true ? true : _Tainted> : never : never : Key extends KeyofUnion<_RootValue> ? NestedTypeBuilder<ValueOfUnion<_RootValue, Key>, Rest, TaintArrayCrossings, _Tainted> : never : FlattenedPath extends `${number}` ? FlattenedPath extends KeyofUnion<_RootValue> ? TaintArrayCrossings extends true ? ValueOfUnion<_RootValue, FlattenedPath> | undefined : ValueOfUnion<_RootValue, FlattenedPath> : FlattenedPath extends `${infer NumericKey extends number}` ? NumericKey extends KeyofUnion<_RootValue> ? TaintArrayCrossings extends true ? ValueOfUnion<_RootValue, NumericKey> | undefined : ValueOfUnion<_RootValue, NumericKey> : never : never : FlattenedPath extends KeyofUnion<_RootValue> ? _Tainted extends true ? ValueOfUnion<_RootValue, FlattenedPath> | undefined : ValueOfUnion<_RootValue, FlattenedPath> : never;
|
|
254
297
|
/**
|
|
255
298
|
* Resolve the type at a dotted-string path inside `RootValue`. Used
|
|
256
299
|
* by the strict (write-side) APIs to derive the type at a path:
|
|
@@ -264,11 +307,16 @@ type DeepPartial<T> = T extends Primitive ? T : T extends Array<infer ArrayItem>
|
|
|
264
307
|
* useful value type (vs. silently collapsing to `never` because
|
|
265
308
|
* `keyof (A|B|C)` would be the intersection of all variants' keys).
|
|
266
309
|
*
|
|
310
|
+
* Composed over `NestedTypeBuilder` with array-crossing tainting OFF,
|
|
311
|
+
* so leaves return their exact resolved type. The companion
|
|
312
|
+
* `NestedReadType` shares the same recursion body but enables
|
|
313
|
+
* tainting for read-side APIs.
|
|
314
|
+
*
|
|
267
315
|
* TypeScript caps conditional-type recursion at around 50 levels;
|
|
268
316
|
* paths deeper than that resolve to `never`. Real form schemas
|
|
269
317
|
* never reach this depth.
|
|
270
318
|
*/
|
|
271
|
-
type NestedType<RootValue, FlattenedPath extends string
|
|
319
|
+
type NestedType<RootValue, FlattenedPath extends string> = NestedTypeBuilder<RootValue, FlattenedPath, false>;
|
|
272
320
|
/**
|
|
273
321
|
* Implementation-detail primitive-leaf marker used by `DeepPartial`
|
|
274
322
|
* and sibling structural walkers. Exported so the bundled `.d.ts`
|
|
@@ -300,7 +348,7 @@ type IsTuple<T extends readonly unknown[]> = number extends T['length'] ? false
|
|
|
300
348
|
* `register(path).innerRef` so the compile-time type honours the
|
|
301
349
|
* runtime possibility of a missing array position.
|
|
302
350
|
*/
|
|
303
|
-
type NestedReadType<RootValue, FlattenedPath extends string
|
|
351
|
+
type NestedReadType<RootValue, FlattenedPath extends string> = NestedTypeBuilder<RootValue, FlattenedPath, true>;
|
|
304
352
|
/**
|
|
305
353
|
* Filter FlatPath<Form> down to the subset of paths whose resolved leaf
|
|
306
354
|
* is an array. Used by the typed field-array helpers (append / remove /
|
|
@@ -319,6 +367,26 @@ type ArrayPath<Form, P extends FlatPath<Form> = FlatPath<Form>> = P extends stri
|
|
|
319
367
|
* constrain `Path extends ArrayPath<Form>` so this is always well-defined.
|
|
320
368
|
*/
|
|
321
369
|
type ArrayItem<Form, Path extends ArrayPath<Form>> = NestedType<Form, Path> extends ReadonlyArray<infer Item> ? Item : never;
|
|
370
|
+
/**
|
|
371
|
+
* Companion to `ArrayPath`: filter `FlatPath<Form>` down to the subset
|
|
372
|
+
* of paths whose resolved leaf is a record (an object with an open
|
|
373
|
+
* string-keyed index signature, e.g. `z.record(z.string(), V)`). A
|
|
374
|
+
* fixed-shape object (`z.object({ ... })`) is excluded — its keys are
|
|
375
|
+
* statically known, so it has no `string` index signature.
|
|
376
|
+
*
|
|
377
|
+
* `string extends keyof T` is the index-signature probe: it holds for
|
|
378
|
+
* `Record<string, V>` (`keyof` is `string`) and fails for a fixed object
|
|
379
|
+
* (`keyof` is the literal key union). The leading array guard keeps
|
|
380
|
+
* arrays (which also satisfy the object check) out of the record set.
|
|
381
|
+
*/
|
|
382
|
+
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;
|
|
383
|
+
/**
|
|
384
|
+
* Value type of the record addressed by `Path` — the `V` in a
|
|
385
|
+
* `Record<string, V>`. Callers constrain `Path extends RecordPath<Form>`,
|
|
386
|
+
* so the leaf is always an open string-keyed record and this is
|
|
387
|
+
* well-defined.
|
|
388
|
+
*/
|
|
389
|
+
type RecordValue<Form, Path extends RecordPath<Form>> = NestedType<Form, Path> extends Record<string, infer Value> ? Value : never;
|
|
322
390
|
/**
|
|
323
391
|
* Widens primitive-literal leaves to their primitive supertype to
|
|
324
392
|
* match the runtime "slim-primitive write contract."
|
|
@@ -368,6 +436,28 @@ type WriteShape<T> = T extends string | number | boolean | bigint | symbol | nul
|
|
|
368
436
|
} : Array<WriteShape<U>> : T extends object ? {
|
|
369
437
|
[K in keyof T]: WriteShape<T[K]>;
|
|
370
438
|
} : T;
|
|
439
|
+
/**
|
|
440
|
+
* Walk `T` and add `| Unset` at every primitive leaf (except symbol /
|
|
441
|
+
* null / undefined), every opaque leaf (`Date`, `RegExp`, `Map`,
|
|
442
|
+
* `Set`, functions), and every container position (object, tuple,
|
|
443
|
+
* array). The recursion topology mirrors `WriteShape<T>` exactly —
|
|
444
|
+
* `DefaultValuesShape<T>` is then a 1-line composition.
|
|
445
|
+
*
|
|
446
|
+
* Symbol / null / undefined leaves pass through untouched so the
|
|
447
|
+
* runtime sentinel doesn't pollute leaf semantics it has no business
|
|
448
|
+
* carrying. Container positions widen so a single `unset` at any
|
|
449
|
+
* level recursively marks every descendant primitive blank.
|
|
450
|
+
*
|
|
451
|
+
* Not part of the stable consumer-facing surface — reach for
|
|
452
|
+
* `DefaultValuesShape` instead.
|
|
453
|
+
*/
|
|
454
|
+
type AugmentWithUnset<T> = T extends string | number | boolean | bigint ? T | Unset : T extends symbol | null | undefined ? T : T extends Date | RegExp | Map<unknown, unknown> | Set<unknown> | ((...args: never) => unknown) ? T | Unset : T extends readonly [unknown, ...unknown[]] ? {
|
|
455
|
+
-readonly [K in keyof T]: AugmentWithUnset<T[K]>;
|
|
456
|
+
} | Unset : T extends ReadonlyArray<infer U> ? IsTuple<T> extends true ? {
|
|
457
|
+
-readonly [K in keyof T]: AugmentWithUnset<T[K]>;
|
|
458
|
+
} | Unset : Array<AugmentWithUnset<U>> | Unset : T extends object ? {
|
|
459
|
+
[K in keyof T]: AugmentWithUnset<T[K]>;
|
|
460
|
+
} | Unset : T;
|
|
371
461
|
/**
|
|
372
462
|
* Like `WriteShape<T>`, but additionally widens every primitive leaf
|
|
373
463
|
* (`string`, `number`, `boolean`, `bigint`) to admit `Unset` — the
|
|
@@ -381,8 +471,10 @@ type WriteShape<T> = T extends string | number | boolean | bigint | symbol | nul
|
|
|
381
471
|
* descendant blank, so `defaultValues: { profile: unset }` and
|
|
382
472
|
* `setValue('cargo', unset)` typecheck cleanly.
|
|
383
473
|
*
|
|
384
|
-
*
|
|
385
|
-
*
|
|
474
|
+
* Composed as `AugmentWithUnset<WriteShape<T>>`: stage 1 widens
|
|
475
|
+
* primitive literals, stage 2 adds `| Unset` everywhere — the prior
|
|
476
|
+
* inline walker hand-synced this in a single body. The composition
|
|
477
|
+
* stays compatible at every nested position; tuple positions,
|
|
386
478
|
* unbounded arrays, and nested records all flow through unchanged.
|
|
387
479
|
*
|
|
388
480
|
* Example:
|
|
@@ -393,13 +485,7 @@ type WriteShape<T> = T extends string | number | boolean | bigint | symbol | nul
|
|
|
393
485
|
* Used by `UseFormConfiguration.defaultValues`, `setValue`'s value
|
|
394
486
|
* parameter, and `reset`'s parameter.
|
|
395
487
|
*/
|
|
396
|
-
type DefaultValuesShape<T> =
|
|
397
|
-
-readonly [K in keyof T]: DefaultValuesShape<T[K]>;
|
|
398
|
-
} | Unset : T extends ReadonlyArray<infer U> ? IsTuple<T> extends true ? {
|
|
399
|
-
-readonly [K in keyof T]: DefaultValuesShape<T[K]>;
|
|
400
|
-
} | Unset : Array<DefaultValuesShape<U>> | Unset : T extends object ? {
|
|
401
|
-
[K in keyof T]: DefaultValuesShape<T[K]>;
|
|
402
|
-
} | Unset : T;
|
|
488
|
+
type DefaultValuesShape<T> = AugmentWithUnset<WriteShape<T>>;
|
|
403
489
|
/**
|
|
404
490
|
* Single-walker fusion of `DeepPartial` and `DefaultValuesShape` — the
|
|
405
491
|
* type accepted at `defaultValues`, `reset()`'s parameter, and every
|
|
@@ -693,6 +779,15 @@ type MaybePromise<T> = T | Promise<T>;
|
|
|
693
779
|
type ValidateOptions = {
|
|
694
780
|
sync?: boolean;
|
|
695
781
|
};
|
|
782
|
+
/**
|
|
783
|
+
* Configuration passed to `AbstractSchema.getDefaultValues`. Adapters
|
|
784
|
+
* receive `useDefaultSchemaValues` (honor `.default(x)` wrappers vs.
|
|
785
|
+
* empty/falsy fallbacks), an optional `strict` mode (refinement
|
|
786
|
+
* preservation), and an optional `constraints` overlay merged into the
|
|
787
|
+
* derived defaults so the runtime can stamp user-supplied defaults at
|
|
788
|
+
* construction. Exported so adapter authors can co-implement the
|
|
789
|
+
* service contract.
|
|
790
|
+
*/
|
|
696
791
|
type GetDefaultValuesConfig<Form> = {
|
|
697
792
|
useDefaultSchemaValues: boolean;
|
|
698
793
|
/**
|
|
@@ -1072,6 +1167,32 @@ type AbstractSchema<Form, GetValueFormType> = {
|
|
|
1072
1167
|
* answers without a separate top-level overload.
|
|
1073
1168
|
*/
|
|
1074
1169
|
needsAsyncValidation?(): boolean;
|
|
1170
|
+
/**
|
|
1171
|
+
* Return `true` iff the schema carries a refine / check / transform
|
|
1172
|
+
* at any NON-LEAF position — a container node (object / array /
|
|
1173
|
+
* tuple / union / intersection / record / map / set) or the root
|
|
1174
|
+
* itself. False means every check this schema runs is leaf-local,
|
|
1175
|
+
* so a per-keystroke `validateAtPath(form, leafPath)` catches the
|
|
1176
|
+
* same verdicts as a whole-form pass — no ancestor refine reads
|
|
1177
|
+
* the form's wider state.
|
|
1178
|
+
*
|
|
1179
|
+
* The runtime uses this at the per-keystroke schedule to scope
|
|
1180
|
+
* field-level validation to the changed subtree when it can,
|
|
1181
|
+
* falling back to a whole-form pass when an ancestor refine
|
|
1182
|
+
* (cross-field equality, sum constraints, etc.) could be moved
|
|
1183
|
+
* by a leaf write. Optional. The runtime treats a missing
|
|
1184
|
+
* implementation as `() => true` — conservative whole-form,
|
|
1185
|
+
* preserving correctness for adapters that don't yet model
|
|
1186
|
+
* container-refine detection.
|
|
1187
|
+
*
|
|
1188
|
+
* Detection is best-effort: false negatives (returning `true`
|
|
1189
|
+
* when no container refine exists) only lose a perf win and
|
|
1190
|
+
* still validate correctly; false positives (returning `false`
|
|
1191
|
+
* when a container refine exists) would let an ancestor verdict
|
|
1192
|
+
* go stale and are the real risk — implementations should bias
|
|
1193
|
+
* toward returning `true` when in doubt.
|
|
1194
|
+
*/
|
|
1195
|
+
hasContainerOrRootRefine?(): boolean;
|
|
1075
1196
|
};
|
|
1076
1197
|
/**
|
|
1077
1198
|
* Adapter-returned info for a discriminated union — its discriminator
|
|
@@ -1308,20 +1429,24 @@ type WriteMeta = {
|
|
|
1308
1429
|
*/
|
|
1309
1430
|
readonly skipDiscriminatorReshape?: boolean;
|
|
1310
1431
|
/**
|
|
1311
|
-
*
|
|
1312
|
-
*
|
|
1313
|
-
*
|
|
1314
|
-
*
|
|
1315
|
-
*
|
|
1316
|
-
*
|
|
1432
|
+
* Records an array structural mutation precisely enough to replay the
|
|
1433
|
+
* exact index permutation it produced, set by `field-arrays.ts`
|
|
1434
|
+
* helpers. `setValueAtPath` uses it to surgically clear variant memory
|
|
1435
|
+
* for the indices the operation invalidated. Without this hint, a raw
|
|
1436
|
+
* whole-array `setValue(arrayPath, [...])` clears all memory under the
|
|
1437
|
+
* array (the runtime can't tell which indices stayed put). Internal —
|
|
1438
|
+
* don't set from consumer code.
|
|
1317
1439
|
*/
|
|
1318
1440
|
readonly arrayOp?: {
|
|
1319
|
-
readonly kind: '
|
|
1441
|
+
readonly kind: 'insert';
|
|
1442
|
+
readonly index: number;
|
|
1443
|
+
} | {
|
|
1444
|
+
readonly kind: 'remove';
|
|
1320
1445
|
readonly index: number;
|
|
1321
1446
|
} | {
|
|
1322
|
-
readonly kind: '
|
|
1323
|
-
readonly
|
|
1324
|
-
readonly
|
|
1447
|
+
readonly kind: 'move';
|
|
1448
|
+
readonly from: number;
|
|
1449
|
+
readonly to: number;
|
|
1325
1450
|
} | {
|
|
1326
1451
|
readonly kind: 'swap';
|
|
1327
1452
|
readonly a: number;
|
|
@@ -1736,16 +1861,14 @@ type UseFormConfiguration<Form extends GenericForm, GetValueFormType, Schema ext
|
|
|
1736
1861
|
*/
|
|
1737
1862
|
coerce?: boolean | CoercionRegistry;
|
|
1738
1863
|
/**
|
|
1739
|
-
* Per-form override of the `
|
|
1740
|
-
* `field.
|
|
1741
|
-
* `AttaformDefaults.
|
|
1742
|
-
* (`
|
|
1743
|
-
* for the resolution rules and
|
|
1744
|
-
*
|
|
1745
|
-
* Boolean shorthand: `true` → always show *when errors exist*;
|
|
1746
|
-
* `false` → never show.
|
|
1864
|
+
* Per-form override of the `getDisplayState` heuristic that drives
|
|
1865
|
+
* `field.displayState` and the `show*` booleans (and their `form.meta`
|
|
1866
|
+
* rollups). Falls back to `AttaformDefaults.getDisplayState`, then to
|
|
1867
|
+
* the library default (`defaultDisplayState`). See
|
|
1868
|
+
* `AttaformDefaults.getDisplayState` for the resolution rules and
|
|
1869
|
+
* predicate signature.
|
|
1747
1870
|
*/
|
|
1748
|
-
|
|
1871
|
+
getDisplayState?: GetDisplayState;
|
|
1749
1872
|
/**
|
|
1750
1873
|
* Recursion ceiling for schema walks that descend through recursive
|
|
1751
1874
|
* schemas (Zod's `z.lazy(...)` today). Default `64`. Per-form value
|
|
@@ -1773,8 +1896,9 @@ type UseFormConfiguration<Form extends GenericForm, GetValueFormType, Schema ext
|
|
|
1773
1896
|
maxRecursionDepth?: number;
|
|
1774
1897
|
/**
|
|
1775
1898
|
* Override the path-segment name stems treated as sensitive for this
|
|
1776
|
-
* form. Sensitive paths are excluded from persistence writes
|
|
1777
|
-
* multi-tab sync broadcasts
|
|
1899
|
+
* form. Sensitive paths are excluded from persistence writes and
|
|
1900
|
+
* multi-tab sync broadcasts. (DevTools renders raw values by design;
|
|
1901
|
+
* it does not redact.)
|
|
1778
1902
|
*
|
|
1779
1903
|
* Resolution: per-form value (this field) > global default
|
|
1780
1904
|
* (`createAttaform({ defaults: { sensitiveNames } })`) > library
|
|
@@ -1818,6 +1942,20 @@ type UseFormConfiguration<Form extends GenericForm, GetValueFormType, Schema ext
|
|
|
1818
1942
|
* channel would be solo by construction.
|
|
1819
1943
|
*/
|
|
1820
1944
|
multiTab?: boolean;
|
|
1945
|
+
/**
|
|
1946
|
+
* Whether `v-register` automatically manages aria attributes
|
|
1947
|
+
* (`aria-invalid`, `aria-busy`, `aria-required`, `aria-describedby`)
|
|
1948
|
+
* from the field's display state. **Defaults to `true`.**
|
|
1949
|
+
*
|
|
1950
|
+
* **Resolution order (per-register override > per-form > global > library):**
|
|
1951
|
+
*
|
|
1952
|
+
* register(path, { autoAria }) > useForm({ autoAria }) > AttaformDefaults.autoAria > library default (`true`)
|
|
1953
|
+
*
|
|
1954
|
+
* Set `false` to leave all aria wiring to your own markup form-wide.
|
|
1955
|
+
* Any aria attribute you author yourself is always left untouched,
|
|
1956
|
+
* independent of this flag.
|
|
1957
|
+
*/
|
|
1958
|
+
autoAria?: boolean;
|
|
1821
1959
|
/**
|
|
1822
1960
|
* @internal
|
|
1823
1961
|
* SSR prefetch mark — set by the `attaform/vite` compile-time
|
|
@@ -1899,33 +2037,41 @@ type AttaformDefaults = {
|
|
|
1899
2037
|
*/
|
|
1900
2038
|
coerce?: boolean | CoercionRegistry;
|
|
1901
2039
|
/**
|
|
1902
|
-
* Default for `useForm({
|
|
1903
|
-
* that
|
|
1904
|
-
*
|
|
2040
|
+
* Default for `useForm({ getDisplayState })`. The centralised
|
|
2041
|
+
* heuristic that resolves every path's `field.displayState` — and thus
|
|
2042
|
+
* the `show*` booleans and their `form.meta` rollups — to one of
|
|
2043
|
+
* `'idle' | 'pending' | 'error' | 'success'`.
|
|
1905
2044
|
*
|
|
1906
2045
|
* Resolution order (per-form wins):
|
|
1907
2046
|
*
|
|
1908
|
-
* useForm({
|
|
2047
|
+
* useForm({ getDisplayState }) > AttaformDefaults > library default
|
|
1909
2048
|
*
|
|
1910
|
-
* The library default
|
|
1911
|
-
*
|
|
2049
|
+
* The library default opens one timing gate, then resolves by
|
|
2050
|
+
* precedence: gate closed → `'idle'`; a run in flight → `'pending'`;
|
|
2051
|
+
* an own-path error → `'error'`; otherwise `valid` → `'success'`, else
|
|
2052
|
+
* `'idle'`. The gate opens after the first submit attempt OR once the
|
|
2053
|
+
* field is touched and not currently focused:
|
|
1912
2054
|
*
|
|
1913
2055
|
* ```ts
|
|
1914
|
-
* (field, formMeta) =>
|
|
1915
|
-
*
|
|
2056
|
+
* (field, formMeta) => {
|
|
2057
|
+
* const gateOpen =
|
|
2058
|
+
* formMeta.submissionAttempts > 0 ||
|
|
2059
|
+
* (field.touched === true && field.focused !== true)
|
|
2060
|
+
* if (!gateOpen) return 'idle'
|
|
2061
|
+
* if (field.validating === true) return 'pending'
|
|
2062
|
+
* // ...own-path error → 'error'; valid → 'success'; else 'idle'
|
|
2063
|
+
* }
|
|
1916
2064
|
* ```
|
|
1917
2065
|
*
|
|
1918
|
-
* Compose with the library default via the public
|
|
1919
|
-
*
|
|
1920
|
-
*
|
|
1921
|
-
* predicate is invoked only when `errors.length > 0`, so authors
|
|
1922
|
-
* don't re-check inside.
|
|
2066
|
+
* Compose with the library default via the public `defaultDisplayState`
|
|
2067
|
+
* export. The predicate runs on every field-state read, so it owns the
|
|
2068
|
+
* idle / pending / error / success decision outright.
|
|
1923
2069
|
*
|
|
1924
|
-
* The predicate's args are `Omit`'d of `
|
|
1925
|
-
*
|
|
1926
|
-
*
|
|
2070
|
+
* The predicate's args are `Omit`'d of the derived `displayState` /
|
|
2071
|
+
* `show*` / `firstError` keys (see `FieldStateDerivedKey`) to prevent
|
|
2072
|
+
* a self-referential predicate.
|
|
1927
2073
|
*/
|
|
1928
|
-
|
|
2074
|
+
getDisplayState?: GetDisplayState;
|
|
1929
2075
|
/**
|
|
1930
2076
|
* Default for `useForm({ maxRecursionDepth })`. Recursion ceiling
|
|
1931
2077
|
* for schema walks that descend through recursive schemas (Zod's
|
|
@@ -1978,9 +2124,10 @@ type AttaformDefaults = {
|
|
|
1978
2124
|
maxRecursionDepth?: number;
|
|
1979
2125
|
/**
|
|
1980
2126
|
* Override the path-segment name stems treated as sensitive.
|
|
1981
|
-
* Sensitive paths are excluded from persistence writes
|
|
1982
|
-
* sync broadcasts
|
|
1983
|
-
*
|
|
2127
|
+
* Sensitive paths are excluded from persistence writes and multi-tab
|
|
2128
|
+
* sync broadcasts — one configurable source of truth across those
|
|
2129
|
+
* surfaces. (DevTools renders raw values by design; it does not
|
|
2130
|
+
* redact.)
|
|
1984
2131
|
*
|
|
1985
2132
|
* Library default is `DEFAULT_SENSITIVE_NAMES` (exported from
|
|
1986
2133
|
* `attaform`); compose to extend:
|
|
@@ -2025,6 +2172,21 @@ type AttaformDefaults = {
|
|
|
2025
2172
|
* the multi-tab-sync recipe's Security section for the threat model.
|
|
2026
2173
|
*/
|
|
2027
2174
|
multiTab?: boolean;
|
|
2175
|
+
/**
|
|
2176
|
+
* App-wide default for `useForm({ autoAria })`. Library default is
|
|
2177
|
+
* `true`: `v-register` keeps `aria-invalid` / `aria-busy` /
|
|
2178
|
+
* `aria-required` / `aria-describedby` in sync with each field's
|
|
2179
|
+
* display state out of the box.
|
|
2180
|
+
*
|
|
2181
|
+
* **Resolution order (per-form wins):**
|
|
2182
|
+
*
|
|
2183
|
+
* useForm({ autoAria }) > AttaformDefaults.autoAria > library default (`true`)
|
|
2184
|
+
*
|
|
2185
|
+
* Set `false` once at the plugin level to make every form manage its
|
|
2186
|
+
* own aria markup. Authored aria attributes are always preserved
|
|
2187
|
+
* regardless of this setting.
|
|
2188
|
+
*/
|
|
2189
|
+
autoAria?: boolean;
|
|
2028
2190
|
};
|
|
2029
2191
|
/**
|
|
2030
2192
|
* Callback invoked by `handleSubmit` after the form parses successfully.
|
|
@@ -2040,45 +2202,59 @@ type OnSubmit<Form extends GenericForm> = (form: Form) => void | Promise<void>;
|
|
|
2040
2202
|
*/
|
|
2041
2203
|
type OnError = (error: ValidationError[]) => void | Promise<void>;
|
|
2042
2204
|
/**
|
|
2043
|
-
*
|
|
2044
|
-
*
|
|
2045
|
-
*
|
|
2046
|
-
*
|
|
2047
|
-
*
|
|
2048
|
-
*
|
|
2049
|
-
*
|
|
2050
|
-
*
|
|
2051
|
-
*
|
|
2052
|
-
*
|
|
2053
|
-
*
|
|
2054
|
-
*
|
|
2055
|
-
*
|
|
2056
|
-
*
|
|
2057
|
-
*
|
|
2205
|
+
* The display-state verdict at a path: the single signal a UI needs to
|
|
2206
|
+
* decide what (if anything) to surface about validation right now.
|
|
2207
|
+
* Rolled up at containers and at the form root (`form.meta.displayState`).
|
|
2208
|
+
*
|
|
2209
|
+
* - `'idle'` — nothing to surface. Either pre-interaction (the timing
|
|
2210
|
+
* gate hasn't opened) or gate-open with no verdict worth showing.
|
|
2211
|
+
* - `'pending'` — a validation run is in flight at this path; the prior
|
|
2212
|
+
* verdict is stale. Drive a spinner / "Checking…" affordance.
|
|
2213
|
+
* - `'error'` — a blocking error the timing gate has cleared for display.
|
|
2214
|
+
* - `'success'` — validation passed and the gate has cleared a positive
|
|
2215
|
+
* confirmation (the green-check pattern).
|
|
2216
|
+
*
|
|
2217
|
+
* The four `show*` booleans on `FieldState` are sugar over this enum
|
|
2218
|
+
* (`showErrors === (displayState === 'error')`, and so on), so they can
|
|
2219
|
+
* never contradict it.
|
|
2220
|
+
*/
|
|
2221
|
+
type DisplayState = 'idle' | 'pending' | 'error' | 'success';
|
|
2222
|
+
/**
|
|
2223
|
+
* Keys on `FieldState` layered on FROM the display-state predicate
|
|
2224
|
+
* (plus `firstError`, computed alongside them). `Omit`'d from the
|
|
2225
|
+
* predicate's arguments so a predicate cannot read its own output and
|
|
2226
|
+
* form a cycle — enforced at the type level AND at runtime: the base
|
|
2227
|
+
* objects passed in literally lack these keys, so an `as` cast in TS
|
|
2228
|
+
* or a vanilla-JS caller still can't reach them. `FieldStateBase` /
|
|
2229
|
+
* `FormMetaBase` (field-state-api.ts) omit the same set in lockstep.
|
|
2230
|
+
*/
|
|
2231
|
+
type FieldStateDerivedKey = 'displayState' | 'showErrors' | 'showPending' | 'showSuccess' | 'showIdle' | 'firstError';
|
|
2232
|
+
/**
|
|
2233
|
+
* Predicate that resolves a path's `displayState`. Receives the field's
|
|
2234
|
+
* reactive state plus the form's reactive meta (both minus the derived
|
|
2235
|
+
* `displayState` / `show*` / `firstError` keys — see `FieldStateDerivedKey`)
|
|
2236
|
+
* and returns the single enum verdict; the `show*` booleans derive from
|
|
2237
|
+
* the result. Runs unconditionally on every field-state read, so the
|
|
2238
|
+
* idle / pending / error / success decision lives in exactly one place
|
|
2239
|
+
* and the whole app's validation-display behavior flows from it.
|
|
2240
|
+
*
|
|
2241
|
+
* The library default — `defaultDisplayState` — is publicly exported so
|
|
2242
|
+
* a layered predicate can compose with it:
|
|
2058
2243
|
*
|
|
2059
2244
|
* ```ts
|
|
2060
|
-
* import {
|
|
2245
|
+
* import { defaultDisplayState } from 'attaform'
|
|
2061
2246
|
*
|
|
2062
2247
|
* useForm({
|
|
2063
2248
|
* schema,
|
|
2064
|
-
*
|
|
2065
|
-
*
|
|
2249
|
+
* // Defer to the default everywhere, but never show a success check on `username`.
|
|
2250
|
+
* getDisplayState: (field, formMeta) => {
|
|
2251
|
+
* const state = defaultDisplayState(field, formMeta)
|
|
2252
|
+
* return field.path[0] === 'username' && state === 'success' ? 'idle' : state
|
|
2253
|
+
* },
|
|
2066
2254
|
* })
|
|
2067
2255
|
* ```
|
|
2068
2256
|
*/
|
|
2069
|
-
type
|
|
2070
|
-
/**
|
|
2071
|
-
* Configuration shape for `shouldShowErrors`. A predicate function or
|
|
2072
|
-
* a boolean shorthand:
|
|
2073
|
-
*
|
|
2074
|
-
* - `true` — always show errors (when any exist).
|
|
2075
|
-
* - `false` — never show errors.
|
|
2076
|
-
* - function — custom predicate, see `ShouldShowErrors`.
|
|
2077
|
-
*
|
|
2078
|
-
* Resolved through three tiers (per-form > plugin defaults > library
|
|
2079
|
-
* default).
|
|
2080
|
-
*/
|
|
2081
|
-
type ShouldShowErrorsConfig = ShouldShowErrors | boolean;
|
|
2257
|
+
type GetDisplayState = (field: Omit<FieldState, FieldStateDerivedKey>, formMeta: Omit<FormMeta, FieldStateDerivedKey>) => DisplayState;
|
|
2082
2258
|
/**
|
|
2083
2259
|
* Submit handler returned by `handleSubmit(onSubmit, onError)`. Bind
|
|
2084
2260
|
* it to a `<form>`:
|
|
@@ -2162,7 +2338,7 @@ type MetaTrackerValue = {
|
|
|
2162
2338
|
*/
|
|
2163
2339
|
blank: boolean;
|
|
2164
2340
|
};
|
|
2165
|
-
type RegisterFlatPath<Form, Key extends keyof Form = keyof Form> =
|
|
2341
|
+
type RegisterFlatPath<Form, Key extends keyof Form = keyof Form> = FlatPathBuilder<Form, 'register', Key>;
|
|
2166
2342
|
/**
|
|
2167
2343
|
* Sync transformation applied to a field's value as user input flows
|
|
2168
2344
|
* from DOM through the directive's assigner. Composes left-to-right
|
|
@@ -2268,9 +2444,10 @@ type RegisterOptions = {
|
|
|
2268
2444
|
* If multiple inputs bind to the same path, the path keeps
|
|
2269
2445
|
* persisting as long as any opted-in input is mounted.
|
|
2270
2446
|
*
|
|
2271
|
-
*
|
|
2272
|
-
*
|
|
2273
|
-
* `acknowledgeSensitive: true` is also set
|
|
2447
|
+
* When the path looks sensitive (password / cvv / ssn / token /
|
|
2448
|
+
* etc.) the opt-in is skipped with a one-shot dev warning unless
|
|
2449
|
+
* `acknowledgeSensitive: true` is also set — the field simply isn't
|
|
2450
|
+
* persisted (the secure default). It never throws.
|
|
2274
2451
|
*/
|
|
2275
2452
|
persist?: boolean;
|
|
2276
2453
|
/**
|
|
@@ -2330,6 +2507,22 @@ type RegisterOptions = {
|
|
|
2330
2507
|
* instead — see the "Custom assigners" section in the API docs.
|
|
2331
2508
|
*/
|
|
2332
2509
|
transforms?: ReadonlyArray<RegisterTransform>;
|
|
2510
|
+
/**
|
|
2511
|
+
* Per-binding override for automatic aria management, the narrowest
|
|
2512
|
+
* tier of the `autoAria` cascade. By default the directive keeps
|
|
2513
|
+
* `aria-invalid` / `aria-busy` / `aria-required` / `aria-describedby`
|
|
2514
|
+
* in sync with the field's display state. Pass `autoAria: false` to
|
|
2515
|
+
* leave every aria attribute on this element to you (the directive
|
|
2516
|
+
* still manages value binding and registration), or `autoAria: true`
|
|
2517
|
+
* to re-enable management on one binding even when the form set
|
|
2518
|
+
* `useForm({ autoAria: false })`.
|
|
2519
|
+
*
|
|
2520
|
+
* Overrides `useForm({ autoAria })` and
|
|
2521
|
+
* `createAttaform({ defaults: { autoAria } })`. Writing an aria
|
|
2522
|
+
* attribute yourself also locks the directive out of that one
|
|
2523
|
+
* attribute, regardless of this flag.
|
|
2524
|
+
*/
|
|
2525
|
+
autoAria?: boolean;
|
|
2333
2526
|
};
|
|
2334
2527
|
/**
|
|
2335
2528
|
* The object returned by `form.register(path)`. Pass it to a native
|
|
@@ -2439,7 +2632,7 @@ type RegisterValue<Value = unknown> = Readonly<{
|
|
|
2439
2632
|
/**
|
|
2440
2633
|
* Resolved sensitive-path predicate honoring this form's
|
|
2441
2634
|
* `sensitiveNames` cascade. The directive calls this through
|
|
2442
|
-
* `
|
|
2635
|
+
* `allowSensitivePersist` when a `register('path', { persist: true })`
|
|
2443
2636
|
* binding mounts so a per-form custom list (e.g. extending with
|
|
2444
2637
|
* `'mrn'`) gates persistence enrolment correctly.
|
|
2445
2638
|
* @internal
|
|
@@ -2530,6 +2723,16 @@ type RegisterValue<Value = unknown> = Readonly<{
|
|
|
2530
2723
|
* @internal
|
|
2531
2724
|
*/
|
|
2532
2725
|
markBlank: () => boolean;
|
|
2726
|
+
/**
|
|
2727
|
+
* Flip this field's sticky `interacted` flag — the signal that the
|
|
2728
|
+
* user has issued at least one value edit here (an insert or a
|
|
2729
|
+
* delete). Called by the directive's input / change listeners on
|
|
2730
|
+
* every genuine user input; never by hydration or programmatic
|
|
2731
|
+
* writes. Idempotent (the store skips the write once set). Don't
|
|
2732
|
+
* call from consumer code.
|
|
2733
|
+
* @internal
|
|
2734
|
+
*/
|
|
2735
|
+
markInteracted: () => void;
|
|
2533
2736
|
/**
|
|
2534
2737
|
* `true` when the schema's slim primitive set at this path includes
|
|
2535
2738
|
* `'undefined'` — i.e. the leaf was declared `.optional()` (or as
|
|
@@ -2561,6 +2764,42 @@ type RegisterValue<Value = unknown> = Readonly<{
|
|
|
2561
2764
|
* @internal
|
|
2562
2765
|
*/
|
|
2563
2766
|
acceptsString: boolean;
|
|
2767
|
+
/**
|
|
2768
|
+
* The field's aria satellite ids, mirroring `FieldState.aria`. The
|
|
2769
|
+
* directive points `aria-describedby` at `errorId` while the field
|
|
2770
|
+
* is in its error state. Optional so hand-rolled `RegisterValue`
|
|
2771
|
+
* mocks don't have to declare it; the directive skips aria wiring
|
|
2772
|
+
* when absent.
|
|
2773
|
+
* @internal
|
|
2774
|
+
*/
|
|
2775
|
+
aria?: {
|
|
2776
|
+
readonly errorId: string;
|
|
2777
|
+
readonly descriptionId: string;
|
|
2778
|
+
};
|
|
2779
|
+
/**
|
|
2780
|
+
* Whether the schema marks this path required, from
|
|
2781
|
+
* `schema.isRequiredAtPath(segments)`. Drives `aria-required`.
|
|
2782
|
+
* Optional for the same mock-tolerance reason as `aria`.
|
|
2783
|
+
* @internal
|
|
2784
|
+
*/
|
|
2785
|
+
isRequired?: boolean;
|
|
2786
|
+
/**
|
|
2787
|
+
* Whether the directive should auto-manage aria attributes for this
|
|
2788
|
+
* binding. Resolves the per-register `autoAria` override against the
|
|
2789
|
+
* form-level value: `options.autoAria ?? formAutoAria`. The directive
|
|
2790
|
+
* treats an absent value as off.
|
|
2791
|
+
* @internal
|
|
2792
|
+
*/
|
|
2793
|
+
ariaEnabled?: boolean;
|
|
2794
|
+
/**
|
|
2795
|
+
* The gated display-state verdict for this path, reusing the same
|
|
2796
|
+
* field-state identity as `form.fields`. The directive watches it to
|
|
2797
|
+
* keep `aria-invalid` / `aria-busy` / `aria-describedby` in lockstep
|
|
2798
|
+
* with the visible error state, even on async ticks with no parent
|
|
2799
|
+
* re-render. Optional; the directive skips aria wiring when absent.
|
|
2800
|
+
* @internal
|
|
2801
|
+
*/
|
|
2802
|
+
ariaDisplayState?: Readonly<Ref<DisplayState>>;
|
|
2564
2803
|
}>;
|
|
2565
2804
|
/**
|
|
2566
2805
|
* Custom assigner installed on an element via the directive's
|
|
@@ -2876,6 +3115,32 @@ type FieldState<Value = unknown> = {
|
|
|
2876
3115
|
readonly focused: boolean | null;
|
|
2877
3116
|
readonly blurred: boolean | null;
|
|
2878
3117
|
readonly touched: boolean;
|
|
3118
|
+
/**
|
|
3119
|
+
* `true` once the user has issued at least one value edit on this
|
|
3120
|
+
* field through `v-register` (an insert or a delete), sticky
|
|
3121
|
+
* thereafter and preserved across disconnects. Distinct from
|
|
3122
|
+
* `dirty`: typing `"a"` then deleting it back to empty leaves the
|
|
3123
|
+
* field net-unchanged (`dirty: false`) yet `interacted: true`.
|
|
3124
|
+
* Distinct from `touched`: tabbing through a field without editing
|
|
3125
|
+
* flips `touched` but never `interacted`. Set only by user input,
|
|
3126
|
+
* never by hydration or programmatic `setValue`; cleared by
|
|
3127
|
+
* `form.reset()` / `form.resetField(path)`. Containers roll it up as
|
|
3128
|
+
* a disjunction (any descendant interacted).
|
|
3129
|
+
*/
|
|
3130
|
+
readonly interacted: boolean;
|
|
3131
|
+
/**
|
|
3132
|
+
* `true` once the user has blurred this field after editing it: the
|
|
3133
|
+
* first time they edit a value and then leave. Sticky thereafter and
|
|
3134
|
+
* preserved across disconnects; a tab-through with no edit never sets
|
|
3135
|
+
* it (`interacted` is still false at that blur). It composes
|
|
3136
|
+
* `interacted` with the departure and drives the default display gate,
|
|
3137
|
+
* so errors reveal once the user finishes a pass and leaves, then stay
|
|
3138
|
+
* visible through a re-focus to be fixed live. Set only by user
|
|
3139
|
+
* input/blur, never by hydration or programmatic writes; cleared by
|
|
3140
|
+
* `form.reset()` / `form.resetField(path)`. Containers roll it up as a
|
|
3141
|
+
* disjunction.
|
|
3142
|
+
*/
|
|
3143
|
+
readonly blurredAfterInteraction: boolean;
|
|
2879
3144
|
readonly connected: boolean;
|
|
2880
3145
|
/**
|
|
2881
3146
|
* The first DOM element bound to this path via `v-register`, or
|
|
@@ -2932,9 +3197,29 @@ type FieldState<Value = unknown> = {
|
|
|
2932
3197
|
*/
|
|
2933
3198
|
readonly valid: boolean;
|
|
2934
3199
|
/**
|
|
2935
|
-
*
|
|
2936
|
-
*
|
|
2937
|
-
*
|
|
3200
|
+
* The single display-state verdict at this path: `'idle'`,
|
|
3201
|
+
* `'pending'`, `'error'`, or `'success'`. The source of truth the
|
|
3202
|
+
* four `show*` booleans below derive from. Bind it directly when one
|
|
3203
|
+
* branch over the set reads cleaner than four flags:
|
|
3204
|
+
*
|
|
3205
|
+
* ```vue
|
|
3206
|
+
* <FieldStatusIcon :state="form.fields.email.displayState" />
|
|
3207
|
+
* ```
|
|
3208
|
+
*
|
|
3209
|
+
* Resolved by the `getDisplayState` heuristic:
|
|
3210
|
+
* `useForm({ getDisplayState })` →
|
|
3211
|
+
* `createAttaform({ defaults: { getDisplayState } })` → library
|
|
3212
|
+
* default (`defaultDisplayState`). Override per form, app-wide, or
|
|
3213
|
+
* compose with `defaultDisplayState` for a layered predicate.
|
|
3214
|
+
*
|
|
3215
|
+
* Available on container paths too: `form.fields.users[0].displayState`
|
|
3216
|
+
* rolls up over the row's descendants.
|
|
3217
|
+
*/
|
|
3218
|
+
readonly displayState: DisplayState;
|
|
3219
|
+
/**
|
|
3220
|
+
* `displayState === 'error'`. The centralised "render this field's
|
|
3221
|
+
* errors right now?" gate, so templates avoid re-spelling the
|
|
3222
|
+
* heuristic at every error site:
|
|
2938
3223
|
*
|
|
2939
3224
|
* ```vue
|
|
2940
3225
|
* <span v-if="form.fields.email.showErrors">
|
|
@@ -2942,20 +3227,29 @@ type FieldState<Value = unknown> = {
|
|
|
2942
3227
|
* </span>
|
|
2943
3228
|
* ```
|
|
2944
3229
|
*
|
|
2945
|
-
*
|
|
2946
|
-
*
|
|
2947
|
-
*
|
|
2948
|
-
* after touched-and-dirty). Override per form, app-wide, or
|
|
2949
|
-
* compose with `defaultShouldShowErrors` for a layered predicate.
|
|
2950
|
-
*
|
|
2951
|
-
* Falls back to `false` whenever there are no errors — the gate
|
|
2952
|
-
* skips the predicate entirely in that case.
|
|
2953
|
-
*
|
|
2954
|
-
* Available on container paths too: `form.fields.users[0].showErrors`
|
|
2955
|
-
* aggregates over the row's descendants (any descendant with a
|
|
2956
|
-
* qualifying error flips the container on).
|
|
3230
|
+
* Kept plural to match `errors` / `firstError`. On container paths it
|
|
3231
|
+
* rolls up over descendants (any descendant resolving to `'error'`
|
|
3232
|
+
* flips the container on).
|
|
2957
3233
|
*/
|
|
2958
3234
|
readonly showErrors: boolean;
|
|
3235
|
+
/**
|
|
3236
|
+
* `displayState === 'pending'`. A per-field validation run is in
|
|
3237
|
+
* flight at this path and the prior verdict is stale; drive a spinner
|
|
3238
|
+
* or a "Checking…" affordance.
|
|
3239
|
+
*/
|
|
3240
|
+
readonly showPending: boolean;
|
|
3241
|
+
/**
|
|
3242
|
+
* `displayState === 'success'`. Validation has passed and the timing
|
|
3243
|
+
* gate has cleared a positive confirmation; drive the green-check
|
|
3244
|
+
* pattern.
|
|
3245
|
+
*/
|
|
3246
|
+
readonly showSuccess: boolean;
|
|
3247
|
+
/**
|
|
3248
|
+
* `displayState === 'idle'`. Nothing to surface yet — pre-interaction,
|
|
3249
|
+
* or gate-open with no verdict worth showing. Read it to suppress
|
|
3250
|
+
* helper text the moment any other signal takes over.
|
|
3251
|
+
*/
|
|
3252
|
+
readonly showIdle: boolean;
|
|
2959
3253
|
/**
|
|
2960
3254
|
* The first `ValidationError` at this path in the deterministic
|
|
2961
3255
|
* schema-declaration order — equivalent to `errors[0]`, exposed as
|
|
@@ -2977,6 +3271,53 @@ type FieldState<Value = unknown> = {
|
|
|
2977
3271
|
*/
|
|
2978
3272
|
readonly firstError: ValidationError | undefined;
|
|
2979
3273
|
readonly path: ReadonlyArray<string | number>;
|
|
3274
|
+
/**
|
|
3275
|
+
* Stable, SSR-safe DOM id for this field, unique across every mount
|
|
3276
|
+
* on the page. Derived from the form's key and this path, folded with
|
|
3277
|
+
* the form's per-mount `instanceId` so two simultaneous mounts of the
|
|
3278
|
+
* same keyed form never collide. Bind it to wire a label and its
|
|
3279
|
+
* input without inventing your own id:
|
|
3280
|
+
*
|
|
3281
|
+
* ```vue
|
|
3282
|
+
* <label :for="form.fields.email.id">Email</label>
|
|
3283
|
+
* <input :id="form.fields.email.id" v-register="form.register('email')" />
|
|
3284
|
+
* ```
|
|
3285
|
+
*
|
|
3286
|
+
* Treat as identity, not state: stable for the path across the form's
|
|
3287
|
+
* lifetime, opaque, not meant to be parsed.
|
|
3288
|
+
*/
|
|
3289
|
+
readonly id: string;
|
|
3290
|
+
/**
|
|
3291
|
+
* Satellite ids derived from {@link id} for the elements that
|
|
3292
|
+
* describe this field. Wire them to an error node and a description
|
|
3293
|
+
* node so assistive tech announces them with the input. The
|
|
3294
|
+
* `v-register` directive points `aria-describedby` at `errorId`
|
|
3295
|
+
* automatically while the field is in its error state; you render the
|
|
3296
|
+
* matching element and id it:
|
|
3297
|
+
*
|
|
3298
|
+
* ```vue
|
|
3299
|
+
* <input v-register="form.register('email')" />
|
|
3300
|
+
* <span :id="form.fields.email.aria.errorId" v-if="form.fields.email.showErrors">
|
|
3301
|
+
* {{ form.fields.email.firstError?.message }}
|
|
3302
|
+
* </span>
|
|
3303
|
+
* ```
|
|
3304
|
+
*
|
|
3305
|
+
* `descriptionId` is for opt-in help text; chain it into your own
|
|
3306
|
+
* `aria-describedby` when you render a persistent description element.
|
|
3307
|
+
*/
|
|
3308
|
+
readonly aria: {
|
|
3309
|
+
readonly errorId: string;
|
|
3310
|
+
readonly descriptionId: string;
|
|
3311
|
+
};
|
|
3312
|
+
/**
|
|
3313
|
+
* Stable identity for this field as an element of its parent array,
|
|
3314
|
+
* suitable as a Vue `:key` when iterating array elements. An allocated
|
|
3315
|
+
* token (not derived from the element's value) that follows the
|
|
3316
|
+
* element across inserts, removals, moves, and swaps, so a row keeps
|
|
3317
|
+
* its component instance across a reorder. Empty for fields that are
|
|
3318
|
+
* not array elements. Treat as opaque identity, not state.
|
|
3319
|
+
*/
|
|
3320
|
+
readonly key: string;
|
|
2980
3321
|
readonly blank: boolean;
|
|
2981
3322
|
/**
|
|
2982
3323
|
* Presentational label for this field. Resolves through the
|
|
@@ -3422,8 +3763,8 @@ type FormMeta<F = unknown> = FieldState<F> & {
|
|
|
3422
3763
|
*
|
|
3423
3764
|
* Pure introspection counter — useful for "this form has been
|
|
3424
3765
|
* visited and left" UX (analytics, prior-step badges, layered
|
|
3425
|
-
* `
|
|
3426
|
-
* default `
|
|
3766
|
+
* `getDisplayState` predicates) but does NOT drive the library's
|
|
3767
|
+
* default `getDisplayState` heuristic. The reveal-on-submit story
|
|
3427
3768
|
* runs entirely through `submissionAttempts`, which
|
|
3428
3769
|
* `wizard.handleSubmit` bumps on the active form at intermediate
|
|
3429
3770
|
* steps and on every form at the final step.
|
|
@@ -4110,9 +4451,9 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
4110
4451
|
* paths in the persisted draft are preserved (this is a merge,
|
|
4111
4452
|
* not a replace).
|
|
4112
4453
|
*
|
|
4113
|
-
*
|
|
4114
|
-
*
|
|
4115
|
-
* `useForm({ persist })` wasn't configured.
|
|
4454
|
+
* For sensitive-looking paths, warns once and no-ops unless you pass
|
|
4455
|
+
* `{ acknowledgeSensitive: true }` — it never throws. Also a no-op
|
|
4456
|
+
* when `useForm({ persist })` wasn't configured.
|
|
4116
4457
|
*/
|
|
4117
4458
|
persist: (path: FlatPath<Form>, options?: {
|
|
4118
4459
|
acknowledgeSensitive?: boolean;
|
|
@@ -4217,6 +4558,48 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
4217
4558
|
move: <Path extends ArrayPath<Form>>(path: Path, from: number, to: number) => void;
|
|
4218
4559
|
/** Replace the element at `index` with `value`. No-op when out of range. */
|
|
4219
4560
|
replace: <Path extends ArrayPath<Form>>(path: Path, index: number, value: ArrayItem<Form, Path>) => void;
|
|
4561
|
+
/**
|
|
4562
|
+
* Read-only, reactive view of the array at `path` as one `FieldState`
|
|
4563
|
+
* per element, in index order. Each entry carries its element `key`,
|
|
4564
|
+
* an allocated identity token, so a `v-for` keyed by it keeps a row's
|
|
4565
|
+
* component instance across an insert, removal, move, or swap:
|
|
4566
|
+
*
|
|
4567
|
+
* ```vue
|
|
4568
|
+
* <div v-for="(row, i) in form.list('contacts')" :key="row.key">
|
|
4569
|
+
* <input v-register="form.register(`contacts.${i}.name`)" />
|
|
4570
|
+
* <p v-if="row.showErrors">{{ row.firstError?.message }}</p>
|
|
4571
|
+
* </div>
|
|
4572
|
+
* ```
|
|
4573
|
+
*
|
|
4574
|
+
* Entries are the same field states `form.fields` exposes, so reads
|
|
4575
|
+
* stay live. `form.fields(path)` remains the single aggregated
|
|
4576
|
+
* container for the whole array; `list` is the per-element view.
|
|
4577
|
+
* For a record, reach for `record`, which keys each entry by its own
|
|
4578
|
+
* key.
|
|
4579
|
+
*/
|
|
4580
|
+
list: <Path extends ArrayPath<Form>>(path: Path) => readonly FieldState<ArrayItem<Form, Path>>[];
|
|
4581
|
+
/**
|
|
4582
|
+
* Read-only, reactive view of the record at `path` as one `FieldState`
|
|
4583
|
+
* per entry, keyed by the entry's own key. Where `list` hands back an
|
|
4584
|
+
* ordered array for an array path, `record` hands back a keyed object
|
|
4585
|
+
* for a record path, so you iterate it by key:
|
|
4586
|
+
*
|
|
4587
|
+
* ```vue
|
|
4588
|
+
* <div v-for="(field, key) in form.record('scoresByTeam')" :key="key">
|
|
4589
|
+
* <label>{{ key }}</label>
|
|
4590
|
+
* <input v-register="form.register(`scoresByTeam.${key}`)" />
|
|
4591
|
+
* <p v-if="field.showErrors">{{ field.firstError?.message }}</p>
|
|
4592
|
+
* </div>
|
|
4593
|
+
* ```
|
|
4594
|
+
*
|
|
4595
|
+
* Entries are the same field states `form.fields` exposes, so reads
|
|
4596
|
+
* stay live, and the keyed shape mirrors the record's own keys: an
|
|
4597
|
+
* entry appears once you write its key (`form.setValue`) and drops
|
|
4598
|
+
* when the key leaves. `form.fields(path)` remains the single
|
|
4599
|
+
* aggregated container for the whole record; `record` is the
|
|
4600
|
+
* per-entry view.
|
|
4601
|
+
*/
|
|
4602
|
+
record: <Path extends RecordPath<Form>>(path: Path) => Readonly<Record<string, FieldState<RecordValue<Form, Path>>>>;
|
|
4220
4603
|
/**
|
|
4221
4604
|
* Read-only view of the form's blank path set. Reactive — Vue 3.5
|
|
4222
4605
|
* tracks `.has()` / `for..of` / size accesses, so consumers can drive
|
|
@@ -4244,5 +4627,5 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
4244
4627
|
blankPaths: ComputedRef<BlankPathsView>;
|
|
4245
4628
|
};
|
|
4246
4629
|
|
|
4247
|
-
export {
|
|
4248
|
-
export type {
|
|
4630
|
+
export { ROOT_PATH_KEY as $, ROOT_PATH as _, canonicalizePath as as, isPathPrefix as at, isUnset as au, parseDottedPath as av, unset as aw };
|
|
4631
|
+
export type { AbstractSchema as A, NestedType as B, CoercionEntry as C, DeepPartial as D, ErrorsProxyShape as E, FieldMetaPayload 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, PartialFlatPath as R, Path as S, PathKey as T, PendingValidationStatus as U, PersistConfig as V, PersistConfigOptions as W, PersistIncludeMode as X, PersistOptInRegistry as Y, Primitive as Z, ApiErrorDetails as a, ReactiveValidationStatus as a0, RegisterDirective as a1, RegisterFlatPath as a2, RegisterModelDynamicCustomDirective as a3, RegisterOptions as a4, RegisterSelectModifier as a5, RegisterTextModifier as a6, RegisterTransform as a7, RegisterValue as a8, SchemaFactoryOptions as a9, Segment as aa, SetValueCallback as ab, SetValuePayload as ac, SettledValidationStatus as ad, SlimPrimitiveKind as ae, SlimRuntimeOf as af, SubmitHandler as ag, Unset as ah, UseFormConfiguration as ai, UseFormReturnType as aj, ValidateOn as ak, ValidateOnConfig as al, ValidationError as am, ValidationResponse as an, ValidationResponseWithoutValue as ao, ValueOfUnion as ap, WriteMeta as aq, WriteShape as ar, ApiErrorEntry as b, ApiErrorEnvelope as c, ArrayItem as d, ArrayPath as e, AttaformDefaults as f, CoercionRegistry as g, CoercionResult as h, CustomDirectiveRegisterAssignerFn as i, DefaultValuesInput as j, DefaultValuesResponse as k, DefaultValuesShape as l, DisplayState as m, FieldState as n, FieldStateMap as o, FieldStateMapEntry as p, FlatPath as q, FormErrorRecord as r, FormErrorsSurface as s, FormKey as t, FormMeta as u, FormStorage as v, FormStorageKind as w, GetDisplayState as x, HistoryConfig as y, IsUnion as z };
|