attaform 0.30.1 → 0.31.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 (49) hide show
  1. package/dist/abstract.d.mts +5 -5
  2. package/dist/abstract.mjs +3 -3
  3. package/dist/dev/abstract.mjs +3 -3
  4. package/dist/dev/chunks/devtools.mjs +2 -2
  5. package/dist/dev/directive.mjs +1 -1
  6. package/dist/dev/history.mjs +1 -1
  7. package/dist/dev/index.mjs +4 -4
  8. package/dist/dev/runtime/plugins/attaform.mjs +1 -1
  9. package/dist/dev/shared/{attaform-dev.Chm4D6SJ.mjs → attaform-dev.B1q-x9R9.mjs} +2 -2
  10. package/dist/dev/shared/{attaform-dev.BUIFcTYf.mjs → attaform-dev.Caibt7sb.mjs} +11 -10
  11. package/dist/dev/shared/{attaform-dev.CT7MDjPy.mjs → attaform-dev.CtyqGia7.mjs} +152 -227
  12. package/dist/dev/shared/{attaform-dev.BJK1kqHY.mjs → attaform-dev.DJ3nsWw6.mjs} +74 -110
  13. package/dist/dev/shared/{attaform-dev.DuEE4GU1.mjs → attaform-dev.DlaT4xGd.mjs} +3 -3
  14. package/dist/dev/shared/{attaform-dev.BMQ3GcgD.mjs → attaform-dev.k5X47GMD.mjs} +474 -336
  15. package/dist/dev/shared/{attaform-dev.DxHdkkPG.mjs → attaform-dev.y9x9Vp9F.mjs} +206 -127
  16. package/dist/dev/zod-v3.mjs +5 -5
  17. package/dist/dev/zod-v4.mjs +5 -5
  18. package/dist/dev/zod.mjs +4 -4
  19. package/dist/directive.d.mts +2 -2
  20. package/dist/directive.mjs +1 -1
  21. package/dist/history.d.mts +1 -1
  22. package/dist/history.mjs +1 -1
  23. package/dist/index.d.mts +6 -6
  24. package/dist/index.mjs +4 -4
  25. package/dist/runtime/components/AttaformDevtoolsPanel.vue +3 -3
  26. package/dist/runtime/plugins/attaform.mjs +1 -1
  27. package/dist/shared/{attaform.CO918TdU.mjs → attaform.BWUn8Kjy.mjs} +74 -110
  28. package/dist/shared/{attaform.WiGdIqgx.mjs → attaform.C4shmwyJ.mjs} +2 -2
  29. package/dist/shared/{attaform.2FslhB9R.d.mts → attaform.CRVe8s82.d.mts} +1 -1
  30. package/dist/shared/{attaform.L0kUn3vL.d.mts → attaform.D9RqPgGu.d.mts} +1 -1
  31. package/dist/shared/{attaform.BcQwgWaq.d.mts → attaform.DAo0QBmS.d.mts} +28 -8
  32. package/dist/shared/{attaform.ZYn2ahzJ.mjs → attaform.DBCbxlyk.mjs} +59 -1
  33. package/dist/shared/{attaform.DRsANMok.mjs → attaform.DL2nl_QR.mjs} +11 -10
  34. package/dist/shared/{attaform.BIWicztt.mjs → attaform.DNPJPOO7.mjs} +204 -125
  35. package/dist/shared/{attaform.Br8EBn5B.d.mts → attaform.DZnrDrNb.d.mts} +136 -32
  36. package/dist/shared/{attaform.D_f1WgYf.mjs → attaform.DxKQsqFZ.mjs} +456 -318
  37. package/dist/shared/{attaform.B2iebkPA.mjs → attaform.L011tHgJ.mjs} +3 -3
  38. package/dist/shared/{attaform.D_pt1gOj.mjs → attaform.LUPC-2Qg.mjs} +152 -227
  39. package/dist/transforms.mjs +1 -1
  40. package/dist/vite.mjs +1 -1
  41. package/dist/zod-v3.d.mts +4 -4
  42. package/dist/zod-v3.mjs +5 -5
  43. package/dist/zod-v4.d.mts +13 -11
  44. package/dist/zod-v4.mjs +5 -5
  45. package/dist/zod.d.mts +4 -4
  46. package/dist/zod.mjs +4 -4
  47. package/package.json +1 -1
  48. package/skills/attaform/SKILL.md +1 -1
  49. package/skills/attaform/references/custom-components.md +4 -0
@@ -888,8 +888,6 @@ type SchemaParseErrorWithData<TData> = {
888
888
  };
889
889
  /** Settled schema-level verdict. Discriminate on `success`. */
890
890
  type SchemaParseResult<TData> = SchemaParseSuccess<TData> | SchemaParseErrorWithData<TData> | SchemaParseErrorWithoutData;
891
- /** Schema-level `getDefaultValues` verdict: defaults always come back. */
892
- type SchemaDefaultsResult<TData> = SchemaParseSuccess<TData> | SchemaParseErrorWithData<TData>;
893
891
  /** Settled validation result when the form or subtree parsed successfully. */
894
892
  type ValidationResponseSuccess<TData> = SchemaParseSuccess<TData> & {
895
893
  /** The form this verdict belongs to. Stamped by the store, not the schema. */
@@ -977,7 +975,31 @@ type GetDefaultValuesConfig<Form> = {
977
975
  * library.
978
976
  */
979
977
  type AbstractSchema<Form, GetValueFormType> = {
980
- getDefaultValues(config: GetDefaultValuesConfig<Form>): SchemaDefaultsResult<Form>;
978
+ /**
979
+ * The form's starting value: the schema's defaults, with
980
+ * `config.constraints` merged over them and any shape the merge broke
981
+ * repaired. Derivation only. Nothing is validated here, so no
982
+ * refinement, check or transform runs; `validateSync` is the verdict.
983
+ */
984
+ getDefaultValues(config: GetDefaultValuesConfig<Form>): Form;
985
+ /**
986
+ * The schema's synchronous verdict on `data` as a whole form, as a
987
+ * list of failures: empty when the data passes.
988
+ *
989
+ * The runtime seeds the schema error channel with it whenever it
990
+ * establishes a baseline: on the starting value, the first time
991
+ * anything reads or writes the form's errors, and on `reset()`. A form
992
+ * starting from invalid defaults therefore reads invalid from its
993
+ * first render.
994
+ * A hydrating client never calls it, since the server's verdict
995
+ * arrives with the payload.
996
+ *
997
+ * Adapters MUST NOT throw: a refinement that throws, or a schema whose
998
+ * verdict resolves only through a Promise, returns `[]` and leaves the
999
+ * verdict to the async pass that `needsAsyncValidation` queues. An
1000
+ * adapter with no synchronous parse returns `[]` unconditionally.
1001
+ */
1002
+ validateSync(data: unknown): ValidationError[];
981
1003
  /**
982
1004
  * The schema-prescribed default at `path`. The runtime uses it to
983
1005
  * fill structural gaps, so every `setValue` write leaves the form
@@ -1325,8 +1347,8 @@ type AbstractSchema<Form, GetValueFormType> = {
1325
1347
  /**
1326
1348
  * Whether `validateAtPath` MAY have to run asynchronously to surface
1327
1349
  * every error this schema can produce. The runtime asks at
1328
- * construction: `false` means the construction-time sync seed is
1329
- * authoritative and no extra microtask is spent, `true` queues a
1350
+ * construction: `false` means the synchronous verdict on the starting
1351
+ * value is authoritative and no extra microtask is spent, `true` queues a
1330
1352
  * one-shot full-form async pass so async-only verdicts (refinements,
1331
1353
  * transforms or pipes that resolve only through a Promise) surface
1332
1354
  * without waiting for a user mutation.
@@ -1338,7 +1360,10 @@ type AbstractSchema<Form, GetValueFormType> = {
1338
1360
  * For a per-path query, compose with `getSchemasAtPath(path)`: each
1339
1361
  * candidate sub-schema exposes its own `needsAsyncValidation`, so
1340
1362
  * "does the cargo subtree contain async work?" is the union of the
1341
- * per-candidate answers, with no separate top-level overload.
1363
+ * per-candidate answers, with no separate top-level overload. A root
1364
+ * that answers `false` answers for every path, and the runtime then
1365
+ * asks no sub-schema, so never answer `false` at the root of a tree
1366
+ * where a sub-schema would answer `true`.
1342
1367
  */
1343
1368
  needsAsyncValidation?(): boolean;
1344
1369
  /**
@@ -1378,6 +1403,20 @@ type AbstractSchema<Form, GetValueFormType> = {
1378
1403
  * that disables variant reshape and stub correction for the form.
1379
1404
  */
1380
1405
  hasDiscriminatedUnions?(): boolean;
1406
+ /**
1407
+ * Whether `isPreprocessOrCoerceLeaf` can answer `true` at any path:
1408
+ * the schema holds a preprocess-style normalizer or a coerce
1409
+ * primitive somewhere. The store reads it once at construction.
1410
+ * `false` lets it skip recording which paths the consumer authored,
1411
+ * a set that is only read at such leaves.
1412
+ *
1413
+ * Optional, and an adapter that omits it is treated as holding one,
1414
+ * which keeps that bookkeeping on. Never return `false` for a schema
1415
+ * where `isPreprocessOrCoerceLeaf` answers `true` anywhere: verdicts
1416
+ * at a path the consumer authored with `undefined` would then stay
1417
+ * hidden.
1418
+ */
1419
+ hasPreprocessOrCoerce?(): boolean;
1381
1420
  };
1382
1421
  /**
1383
1422
  * A discriminated union as the adapter reports it: the discriminator
@@ -1583,7 +1622,7 @@ type WriteMeta = {
1583
1622
  type HistoryKernel = {
1584
1623
  readonly form: Ref<unknown>;
1585
1624
  readonly blankPaths: Set<PathKey>;
1586
- readonly errorCells: Map<PathKey, ErrorCell>;
1625
+ readonly errorCells: () => Map<PathKey, ErrorCell>;
1587
1626
  onFormChange(listener: (next: unknown, meta?: WriteMeta) => void): () => void;
1588
1627
  applyFormReplacement(next: unknown): void;
1589
1628
  restoreErrorCells(entries: ReadonlyArray<readonly [PathKey, ErrorCell]>): void;
@@ -2184,6 +2223,32 @@ type AttaformDomBinding = {
2184
2223
  path: Path;
2185
2224
  element: HTMLElement;
2186
2225
  } | null;
2226
+ readonly hostChannel: HostChannel;
2227
+ };
2228
+ /**
2229
+ * The write policy for a `v-register` component host, held on the DOM
2230
+ * binding so it stays in the directive cluster's lazy graph. Applies the
2231
+ * binding site's modifiers, runs register `transforms` and schema
2232
+ * coercion, and buffers `.lazy` writes while focus is inside the host.
2233
+ *
2234
+ * - `write` commits a value the host emitted through
2235
+ * `update:modelValue`.
2236
+ * - `writeInput` takes an `input` emit and commits it only when the
2237
+ * payload carries the live value as an own `value` property.
2238
+ * - `markEditing` records focus entering or leaving the host. Leaving
2239
+ * commits a buffered `.lazy` value, or the trimmed form under `.trim`.
2240
+ * - `holdsDraft` reports a buffered `.lazy` value that storage has not
2241
+ * moved past, the edit a re-render must leave on screen.
2242
+ * - `release` drops any buffered value when the host unmounts.
2243
+ *
2244
+ * @internal
2245
+ */
2246
+ type HostChannel = {
2247
+ readonly write: (rv: RegisterValue, value: unknown, modifiers: HostModifiers) => boolean;
2248
+ readonly writeInput: (rv: RegisterValue, payload: unknown, modifiers: HostModifiers) => boolean;
2249
+ readonly markEditing: (rv: RegisterValue, editing: boolean, modifiers: HostModifiers) => void;
2250
+ readonly holdsDraft: (rv: RegisterValue) => boolean;
2251
+ readonly release: (rv: RegisterValue) => void;
2187
2252
  };
2188
2253
  /**
2189
2254
  * Factory the directive and `useRegister` inject through
@@ -2244,16 +2309,39 @@ type RegisterValue<Value = unknown> = Readonly<{
2244
2309
  setValueWithInternalPath: (value: unknown, meta?: WriteMeta) => boolean;
2245
2310
  /**
2246
2311
  * Commit a value emitted by a third-party component bound through
2247
- * `v-register`'s compile-time v-model desugar. Writes the component's
2248
- * typed model output as authoritative, with no coercion, AND marks
2249
- * the field interacted, since a v-model host has no DOM input
2250
- * listener to flip the sticky `interacted` bit. The injected
2251
- * `onUpdate:modelValue` handler is the only caller. `true` when the
2252
- * write was accepted.
2312
+ * `v-register`'s compile-time v-model desugar, and mark the field
2313
+ * interacted, since a v-model host has no DOM input listener to flip
2314
+ * the sticky `interacted` bit. The write takes the same path a native
2315
+ * input's does: the binding site's `.number` / `.trim` / `.lazy`
2316
+ * modifiers, then register `transforms`, then schema coercion. The
2317
+ * injected `onUpdate:modelValue` handler is the caller, passing the
2318
+ * modifiers as a static literal. `true` when the write was accepted
2319
+ * or buffered.
2253
2320
  *
2254
2321
  * @internal
2255
2322
  */
2256
- setValueFromHost: (value: unknown) => boolean;
2323
+ setValueFromHost: (value: unknown, modifiers: HostModifiers) => boolean;
2324
+ /**
2325
+ * Commit the live value from a component host's `input` emit. A
2326
+ * payload that is a non-Event object owning a `value` property (the
2327
+ * shape PrimeVue's InputNumber reports per keystroke) writes that
2328
+ * value through `setValueFromHost`; anything else, a native `Event`
2329
+ * reaching a fallthrough listener included, is ignored and returns
2330
+ * `false`. The injected `onInput` handler is the only caller.
2331
+ *
2332
+ * @internal
2333
+ */
2334
+ setValueFromHostInput: (payload: unknown, modifiers: HostModifiers) => boolean;
2335
+ /**
2336
+ * The final step of a component-host write, after modifiers,
2337
+ * transforms and coercion: an empty signal (`''`, `null` or
2338
+ * `undefined`) the field's schema does not admit marks the field
2339
+ * blank, and anything else writes through. Called by the host write
2340
+ * channel.
2341
+ *
2342
+ * @internal
2343
+ */
2344
+ commitFromHost: (value: unknown) => boolean;
2257
2345
  /**
2258
2346
  * Mark this field DOM-connected during SSR, so a server-rendered
2259
2347
  * template reading `form.fields.<path>.connected` does not flicker on
@@ -2312,11 +2400,12 @@ type RegisterValue<Value = unknown> = Readonly<{
2312
2400
  * every later registration.
2313
2401
  *
2314
2402
  * Optional, so a hand-rolled RegisterValue owning its own element
2315
- * handling need not declare it.
2403
+ * handling need not declare it. Returns the armed binding, which is how
2404
+ * the directive reaches the host write channel.
2316
2405
  *
2317
2406
  * @internal
2318
2407
  */
2319
- ensureDomBinding?: (factory: DomBindingFactory) => void;
2408
+ ensureDomBinding?: (factory: DomBindingFactory) => AttaformDomBinding;
2320
2409
  /**
2321
2410
  * Canonical JSON-encoded path key for this binding, such as
2322
2411
  * `'["items",0,"name"]'`. Useful for stable Map and Set keys, log
@@ -2576,9 +2665,10 @@ type CustomRegisterDirective<T, Modifiers extends string = string> = ObjectDirec
2576
2665
  /**
2577
2666
  * Snapshot of the last `value.innerRef.value` reference the
2578
2667
  * directive's DOM-sync (setSelected / setChecked / radio
2579
- * `el.checked = …`) was applied for. Used by every input
2580
- * directive's `updated` / `beforeUpdate` to skip the per-render
2581
- * DOM sync when the model is identity-unchanged, so a parent
2668
+ * `el.checked = …` / text `el.value = …`) was applied for. Used by
2669
+ * every input directive's `updated` / `beforeUpdate` to skip the
2670
+ * per-render DOM sync when the model is identity-unchanged (for
2671
+ * text, a focused `.lazy` edit is held through it), so a parent
2582
2672
  * re-render (a typed character in a sibling, an async-validation
2583
2673
  * tick, any reactive read) cannot clobber an in-progress user
2584
2674
  * interaction. Identity comparison is sound because every form
@@ -2604,16 +2694,18 @@ type CustomRegisterDirective<T, Modifiers extends string = string> = ObjectDirec
2604
2694
  }, RegisterValue | undefined, Modifiers, string>;
2605
2695
  /**
2606
2696
  * Modifier names supported by `v-register` on `<input type="text">`,
2607
- * `<input type="number">`, and `<textarea>`. Mirrors Vue's
2608
- * `v-model` modifier semantics on the same elements; combine freely
2609
- * (`<input v-register.lazy.trim.number="..." />`).
2697
+ * `<input type="number">`, `<textarea>`, and component hosts. Mirrors
2698
+ * Vue's `v-model` modifier semantics on the same elements; combine
2699
+ * freely (`<input v-register.lazy.trim.number="..." />`).
2610
2700
  */
2611
2701
  type RegisterTextModifier =
2612
2702
  /**
2613
2703
  * Write on `change` (blur) instead of `input`. The reactive
2614
2704
  * model only updates after the user tabs/clicks out of the
2615
2705
  * field. IME composition handlers are skipped under `.lazy`, since
2616
- * composition events do not gate writes.
2706
+ * composition events do not gate writes. On a component host the
2707
+ * last value emitted while focus is inside the host commits when
2708
+ * focus leaves it.
2617
2709
  */
2618
2710
  'lazy'
2619
2711
  /**
@@ -2621,18 +2713,30 @@ type RegisterTextModifier =
2621
2713
  * the user's raw input (whitespace included) while they're
2622
2714
  * typing; on `change` (blur / commit) the value is trimmed
2623
2715
  * once and written back to both the model and the visible DOM.
2624
- * Combine with `.lazy` to skip the mid-typing writes entirely.
2716
+ * Combine with `.lazy` to skip the mid-typing writes entirely. On a
2717
+ * component host the trimmed value commits when focus leaves the
2718
+ * host.
2625
2719
  */
2626
2720
  | 'trim'
2627
2721
  /**
2628
- * Cast the value with `parseFloat` before writing. A value that
2629
- * does not parse as a number (`'abc'`) passes through unchanged,
2630
- * and the slim-primitive gate then sees a string heading for a
2631
- * numeric slot and rejects the write. Auto-applied for
2632
- * `<input type="number">`, where an explicit `.number` is
2633
- * redundant.
2722
+ * Cast the value with `parseFloat` before writing. An empty field,
2723
+ * or text that does not parse as a number (`'abc'`), marks the field
2724
+ * blank instead of writing a string. On a component host the cast
2725
+ * applies to string emits, and a value the component already typed
2726
+ * passes through. Auto-applied for `<input type="number">`, where an
2727
+ * explicit `.number` is redundant.
2634
2728
  */
2635
2729
  | 'number';
2730
+ /**
2731
+ * The `v-register` modifiers a component host honours, carried from the
2732
+ * binding site to the host write channel as a static literal that the
2733
+ * compile-time bridge transform emits. Shaped like Vue's directive
2734
+ * modifiers, so the directive passes a binding's modifiers to the channel
2735
+ * unchanged.
2736
+ *
2737
+ * @internal
2738
+ */
2739
+ type HostModifiers = Readonly<Partial<Record<RegisterTextModifier, boolean>>>;
2636
2740
  /**
2637
2741
  * v-register directive variant for `<input type="text">`,
2638
2742
  * `<input type="number">` and `<textarea>`. Supports the `.lazy`,
@@ -4549,5 +4653,5 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
4549
4653
  blankPaths: ComputedRef<BlankPathsView>;
4550
4654
  };
4551
4655
 
4552
- export { ROOT_PATH as R, canonicalizePath as aj, isPathPrefix as ak, isUnset as al, parseDottedPath as am, unset as an, ROOT_PATH_KEY as z };
4553
- export type { SchemaDefaultsResult as $, AbstractSchema as A, ReactiveValidationStatus as B, CustomDirectiveRegisterAssignerFn as C, DefaultValuesInput as D, ErrorInput as E, FormKey as F, GenericForm as G, HandleSubmit as H, IsTuple as I, JoinSegments as J, KeyofUnion as K, LiftedValueShape as L, MetaTrackerValue as M, NestedReadType as N, OnError as O, ParseOptions as P, RecordPath as Q, RecordValue as S, RegisterDirective as T, UseFormConfiguration as U, RegisterFlatPath as V, RegisterOptions as W, RegisterSelectModifier as X, RegisterTextModifier as Y, RegisterTransform as Z, RegisterValue as _, UseFormReturnType as a, SchemaParseErrorWithData as a0, SchemaParseErrorWithoutData as a1, SchemaParseResult as a2, SchemaParseSuccess as a3, Segment as a4, SetValueCallback as a5, SetValuePayload as a6, SettledValidationStatus as a7, SlimPrimitiveKind as a8, SubmitHandler as a9, Unset as aa, ValidateOn as ab, ValidateOnConfig as ac, ValidationError as ad, ValidationResponse as ae, ValidationResponseWithoutValue as af, ValueOfUnion as ag, WriteMeta as ah, WriteShape as ai, RegisterModelDynamicCustomDirective as ao, SchemaFactoryOptions as ap, AcceptableDefaults as aq, DisplayCtx as ar, GetDisplayState as as, DisplayMachine as at, FieldRecord as au, ErrorCell as av, OriginalsRecord as aw, AttaformDomBinding as ax, TransformAbortHolder as ay, ArrayItem as b, ArrayPath as c, DeepPartial as d, DefaultValuesShape as e, DisplayState as f, ErrorsProxyShape as g, FieldMetaPayload as h, FieldState as i, FieldStateMap as j, FieldStateMapEntry as k, FlatPath as l, FormErrorRecord as m, FormErrorsSurface as n, FormMeta as o, HistoryPlugin as p, IsUnion as q, Json as r, NestedType as s, OnSubmit as t, PartialFlatPath as u, Path as v, PathKey as w, PendingValidationStatus as x, Primitive as y };
4656
+ export { ROOT_PATH as R, canonicalizePath as ai, isPathPrefix as aj, isUnset as ak, parseDottedPath as al, unset as am, ROOT_PATH_KEY as z };
4657
+ export type { SchemaParseErrorWithData as $, AbstractSchema as A, ReactiveValidationStatus as B, CustomDirectiveRegisterAssignerFn as C, DefaultValuesInput as D, ErrorInput as E, FormKey as F, GenericForm as G, HandleSubmit as H, IsTuple as I, JoinSegments as J, KeyofUnion as K, LiftedValueShape as L, MetaTrackerValue as M, NestedReadType as N, OnError as O, ParseOptions as P, RecordPath as Q, RecordValue as S, RegisterDirective as T, UseFormConfiguration as U, RegisterFlatPath as V, RegisterOptions as W, RegisterSelectModifier as X, RegisterTextModifier as Y, RegisterTransform as Z, RegisterValue as _, UseFormReturnType as a, SchemaParseErrorWithoutData as a0, SchemaParseResult as a1, SchemaParseSuccess as a2, Segment as a3, SetValueCallback as a4, SetValuePayload as a5, SettledValidationStatus as a6, SlimPrimitiveKind as a7, SubmitHandler as a8, Unset as a9, ValidateOn as aa, ValidateOnConfig as ab, ValidationError as ac, ValidationResponse as ad, ValidationResponseWithoutValue as ae, ValueOfUnion as af, WriteMeta as ag, WriteShape as ah, RegisterModelDynamicCustomDirective as an, SchemaFactoryOptions as ao, AcceptableDefaults as ap, DisplayCtx as aq, GetDisplayState as ar, DisplayMachine as as, FieldRecord as at, ErrorCell as au, OriginalsRecord as av, AttaformDomBinding as aw, TransformAbortHolder as ax, ArrayItem as b, ArrayPath as c, DeepPartial as d, DefaultValuesShape as e, DisplayState as f, ErrorsProxyShape as g, FieldMetaPayload as h, FieldState as i, FieldStateMap as j, FieldStateMapEntry as k, FlatPath as l, FormErrorRecord as m, FormErrorsSurface as n, FormMeta as o, HistoryPlugin as p, IsUnion as q, Json as r, NestedType as s, OnSubmit as t, PartialFlatPath as u, Path as v, PathKey as w, PendingValidationStatus as x, Primitive as y };