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
@@ -1,27 +1,5 @@
1
1
  import { NodeTransform } from '@vue/compiler-core';
2
2
 
3
- /**
4
- * Vue compiler node transform that bridges `v-register` into the
5
- * downstream binding shapes its consumers expect:
6
- *
7
- * - `<select v-register>` — injects `:value` (single-select) and
8
- * per-`<option>` `:selected` so the runtime directive can pre-mark
9
- * selected options at SSR time.
10
- * - `<MyComponent v-register>` and kebab-case custom-element hosts
11
- * — injects a `:registerValue` bridge prop so `useRegister` inside
12
- * the child sees the parent's RegisterValue (the binding the audit
13
- * called out as the transform's "fires on every component" path),
14
- * and marks any parent-authored slotted `<option>`s with `:selected`
15
- * the same way the native path does, so a `<select>` wrapped in a
16
- * styled component keeps its SSR-selected option (#394).
17
- *
18
- * Wired automatically by `attaform/vite` and `attaform/nuxt`. Use
19
- * directly only when integrating with a custom bundler.
20
- *
21
- * Renamed from `selectNodeTransform` (DIR-F6): the original name read
22
- * as a `<select>`-only transform, but the component-bridge path is
23
- * load-bearing for every `useRegister` consumer.
24
- */
25
3
  declare const componentBridgeTransform: NodeTransform;
26
4
 
27
5
  /**
@@ -1 +1 @@
1
- export { c as componentBridgeTransform, i as inputTextAreaNodeTransform, r as redundantBindingWarnTransform, v as vRegisterHintTransform, a as vRegisterPreambleTransform } from './shared/attaform.Cs2Rbdlo.mjs';
1
+ export { c as componentBridgeTransform, i as inputTextAreaNodeTransform, r as redundantBindingWarnTransform, v as vRegisterHintTransform, a as vRegisterPreambleTransform } from './shared/attaform.BK0cgu2K.mjs';
package/dist/vite.d.mts CHANGED
@@ -21,16 +21,25 @@ import { Plugin } from 'vite';
21
21
  * the auto-imports at `/zod` costs the consumer nothing and earns the
22
22
  * lean bundle for free.
23
23
  *
24
+ * The bar for a slot: a form author calls it directly from inside a
25
+ * component's `setup`. That is why `useRegister` is here. It is the only
26
+ * way to write a custom input wrapper, and it opens with
27
+ * `getCurrentInstance()`, so a component's `setup` is the one place it
28
+ * can be called at all.
29
+ *
24
30
  * Deliberately absent (reach for these with an explicit import):
25
31
  * - `useAbstractForm` — the schema-agnostic escape hatch on
26
32
  * `attaform/abstract`, an advanced surface, not the default form.
27
33
  * - `createAttaform` — the plugin install, a setup-level one-liner that
28
34
  * belongs beside the app bootstrap, not in every component's scope.
29
- * - `useRegister` and the serialize helpers — low-level plumbing.
35
+ * - `unset` / `isUnset` and the serialize helpers: plain values and
36
+ * functions with no component affinity, equally at home in a store, a
37
+ * util module, or a server route. `unset` is also too generic a name
38
+ * to spend on every app's global scope.
30
39
  *
31
- * A name earns a slot here only if a form author calls it directly on a
32
- * normal page. The manifest test pins the set so a casual addition of a
33
- * setup-level symbol to the global scope fails loudly.
40
+ * The manifest test pins the set so a casual addition of a setup-level
41
+ * symbol to the global scope fails loudly, and `auto-import-docs.test.ts`
42
+ * pins the prose that enumerates it.
34
43
  */
35
44
  /**
36
45
  * One auto-imported binding: the exported `name` and the module it is
package/dist/vite.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { r as resolveZodAliasTarget, i as isRewritableZodSpecifier } from './shared/attaform.DhnPb6nK.mjs';
2
- import { a as vRegisterPreambleTransform, r as redundantBindingWarnTransform, c as componentBridgeTransform, i as inputTextAreaNodeTransform, v as vRegisterHintTransform } from './shared/attaform.Cs2Rbdlo.mjs';
2
+ import { a as vRegisterPreambleTransform, r as redundantBindingWarnTransform, c as componentBridgeTransform, i as inputTextAreaNodeTransform, v as vRegisterHintTransform } from './shared/attaform.BK0cgu2K.mjs';
3
3
  import { parse, babelParse } from '@vue/compiler-sfc';
4
4
  import { NodeTypes } from '@vue/compiler-core';
5
5
 
@@ -11,7 +11,8 @@ const attaformAutoImports = [
11
11
  { name: "fieldMeta", from: "attaform/zod" },
12
12
  { name: "withMeta", from: "attaform/zod" },
13
13
  { name: "lazy", from: "attaform/zod" },
14
- { name: "gate", from: "attaform/zod" }
14
+ { name: "gate", from: "attaform/zod" },
15
+ { name: "useRegister", from: "attaform/zod" }
15
16
  ];
16
17
  const attaformAutoImportsMap = attaformAutoImports.reduce((map, entry) => {
17
18
  const names = map[entry.from] ?? [];
package/dist/zod-v3.d.mts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { z } from 'zod';
2
- import { G as GenericForm, F as FormKey, U as UseFormConfiguration, A as AbstractSchema, as as AcceptableDefaults, a as UseFormReturnType, af as ValidateOnConfig, at as SchemaFactoryOptions, o as FieldMetaPayload } from './shared/attaform.Cgu-hdSb.mjs';
3
- export { b as ArrayItem, c as ArrayPath, d as AttaformDefaults, C as CoercionEntry, e as CoercionRegistry, f as CoercionResult, g as CustomDirectiveRegisterAssignerFn, h as DeepPartial, D as DefaultValuesInput, i as DefaultValuesResponse, j as DefaultValuesShape, k as DisplayCtx, l as DisplayMachine, m as DisplayState, E as ErrorInput, n as ErrorsProxyShape, p as FieldState, q as FieldStateMap, r as FieldStateMapEntry, s as FlatPath, t as FormErrorRecord, u as FormErrorsSurface, v as FormMeta, w as GetDisplayState, H as HandleSubmit, x as HistoryPlugin, I as IsTuple, y as IsUnion, J as JoinSegments, z as Json, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, B as NestedType, O as OnError, P as OnInvalidSubmitPolicy, Q as OnSubmit, R as ParseOptions, S as PartialFlatPath, T as Path, V as PathKey, W as PendingValidationStatus, X as Primitive, Y as ROOT_PATH, Z as ROOT_PATH_KEY, _ as ReactiveValidationStatus, $ as RegisterDirective, a0 as RegisterFlatPath, a1 as RegisterOptions, a2 as RegisterSelectModifier, a3 as RegisterTextModifier, a4 as RegisterTransform, a5 as RegisterValue, a6 as Segment, a7 as SetValueCallback, a8 as SetValuePayload, a9 as SettledValidationStatus, aa as SlimPrimitiveKind, ab as SlimRuntimeOf, ac as SubmitHandler, ad as Unset, ae as ValidateOn, ag as ValidationError, ah as ValidationResponse, ai as ValidationResponseWithoutValue, aj as ValueOfUnion, ak as WriteMeta, al as WriteShape, am as canonicalizePath, an as isPathPrefix, ao as isUnset, ap as parseDottedPath, aq as unset } from './shared/attaform.Cgu-hdSb.mjs';
2
+ import { G as GenericForm, F as FormKey, U as UseFormConfiguration, av as AcceptableDefaults, a as UseFormReturnType, ah as ValidateOnConfig, au as SchemaFactoryOptions, o as FieldMetaPayload } from './shared/attaform.CsoeRi3P.mjs';
3
+ import { AbstractSchema } from './abstract.mjs';
4
+ export { b as ArrayItem, c as ArrayPath, d as AttaformDefaults, C as CoercionEntry, e as CoercionRegistry, f as CoercionResult, g as CustomDirectiveRegisterAssignerFn, h as DeepPartial, D as DefaultValuesInput, i as DefaultValuesResponse, j as DefaultValuesShape, k as DisplayCtx, l as DisplayMachine, m as DisplayState, E as ErrorInput, n as ErrorsProxyShape, p as FieldState, q as FieldStateMap, r as FieldStateMapEntry, s as FlatPath, t as FormErrorRecord, u as FormErrorsSurface, v as FormMeta, w as GetDisplayState, H as HandleSubmit, x as HistoryPlugin, I as IsTuple, y as IsUnion, J as JoinSegments, z as Json, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, B as NestedType, O as OnError, P as OnInvalidSubmitPolicy, Q as OnSubmit, R as ParseOptions, S as PartialFlatPath, T as Path, V as PathKey, W as PendingValidationStatus, X as Primitive, Y as ROOT_PATH, Z as ROOT_PATH_KEY, _ as ReactiveValidationStatus, $ as RecordPath, a0 as RecordValue, a1 as RegisterDirective, a2 as RegisterFlatPath, a3 as RegisterOptions, a4 as RegisterSelectModifier, a5 as RegisterTextModifier, a6 as RegisterTransform, a7 as RegisterValue, a8 as Segment, a9 as SetValueCallback, aa as SetValuePayload, ab as SettledValidationStatus, ac as SlimPrimitiveKind, ad as SlimRuntimeOf, ae as SubmitHandler, af as Unset, ag as ValidateOn, ai as ValidationError, aj as ValidationResponse, ak as ValidationResponseWithoutValue, al as ValueOfUnion, am as WriteMeta, an as WriteShape, ao as canonicalizePath, ap as isPathPrefix, aq as isUnset, ar as parseDottedPath, as as unset } from './shared/attaform.CsoeRi3P.mjs';
4
5
  import { S as SupportedRootSchema, U as UnwrapZodRoot, a as StorageShape } from './shared/attaform.DRUT703x.mjs';
5
- export { a as AnyForm, b as AttaformDevtoolsBridge, c as AttaformError, d as AttaformErrorCode, e as AttaformPluginOptions, A as AttaformRegistry, C as CompiledStep, D as DEFAULT_TIMINGS, f as DEVTOOLS_WINDOW_KEY, g as DisplayTimings, F as FormStatus, h as FormStatusSeed, G as GateMarker, I as InjectWizardInput, i as InvalidPathError, j as InvalidUseFormConfigError, L as LazyMarker, O as OutsideSetupError, R as RegistryNotInstalledError, k as ReservedFormKeyError, S as SerializedAttaformState, l as SerializedFormData, m as StepSlot, n as SubmitErrorHandlerError, U as UseRegisterReturn, o as UseWizardReturnType, W as WizardAggregateError, p as WizardCtx, q as WizardCtxForm, r as WizardOnError, s as WizardOnSubmit, t as WizardOptions, u as WizardPersistFn, v as WizardRestoreFn, w as WizardRestoreState, x as WizardStatusesProxy, y as WizardSubmitContext, z as assignKey, B as createAttaform, E as createRegistry, H as defaultCoercionRules, J as defaultDisplayState, K as defineCoercion, M as escapeForInlineScript, N as gate, P as getRegistryFromApp, Q as hydrateAttaformState, T as injectForm, V as injectWizard, X as isRegisterValue, Y as kAttaformRegistry, Z as lazy, _ as makeDefaultDisplayState, $ as renderAttaformState, a0 as useRegister, a1 as useRegistry, a2 as useWizard } from './shared/attaform.D5V4tZWq.mjs';
6
- export { v as vRegister } from './shared/attaform.JfSWdEcb.mjs';
6
+ export { a as AnyForm, b as AttaformDevtoolsBridge, c as AttaformError, d as AttaformErrorCode, e as AttaformPluginOptions, A as AttaformRegistry, C as CompiledStep, D as DEFAULT_TIMINGS, f as DEVTOOLS_WINDOW_KEY, g as DisplayTimings, F as FormStatus, h as FormStatusSeed, G as GateMarker, I as InjectWizardInput, i as InvalidPathError, j as InvalidUseFormConfigError, L as LazyMarker, O as OutsideSetupError, R as RegistryNotInstalledError, k as ReservedFormKeyError, S as SerializedAttaformState, l as SerializedFormData, m as StepSlot, n as SubmitErrorHandlerError, U as UseRegisterOptions, o as UseRegisterReturn, p as UseWizardReturnType, W as WizardAggregateError, q as WizardCtx, r as WizardCtxForm, s as WizardOnError, t as WizardOnSubmit, u as WizardOptions, v as WizardPersistFn, w as WizardRestoreFn, x as WizardRestoreState, y as WizardStatusesProxy, z as WizardSubmitContext, B as assignKey, E as createAttaform, H as createRegistry, J as defaultCoercionRules, K as defaultDisplayState, M as defineCoercion, N as escapeForInlineScript, P as gate, Q as getRegistryFromApp, T as hydrateAttaformState, V as injectForm, X as injectWizard, Y as isRegisterValue, Z as kAttaformRegistry, _ as lazy, $ as makeDefaultDisplayState, a0 as renderAttaformState, a1 as useRegister, a2 as useRegistry, a3 as useWizard } from './shared/attaform.DDtqHYv1.mjs';
7
+ export { v as vRegister } from './shared/attaform.axHXVQO7.mjs';
7
8
  import 'vue';
8
9
 
9
10
  /**
@@ -114,6 +115,7 @@ type ZodTypeMap = {
114
115
  ]>;
115
116
  ZodArray: z.ZodArray<z.ZodTypeAny>;
116
117
  ZodSet: z.ZodSet<z.ZodTypeAny>;
118
+ ZodMap: z.ZodMap<z.ZodTypeAny, z.ZodTypeAny>;
117
119
  ZodRecord: z.ZodRecord<z.ZodTypeAny, z.ZodTypeAny>;
118
120
  ZodDefault: z.ZodDefault<z.ZodTypeAny>;
119
121
  ZodOptional: z.ZodOptional<z.ZodTypeAny>;
package/dist/zod-v3.mjs CHANGED
@@ -1,9 +1,9 @@
1
- export { i as isZodSchemaType, u as useForm, z as zodAdapter } from './shared/attaform.CJP3RkGa.mjs';
2
- import { i as installingFieldMetaStore, g as getFieldMetaForSchema } from './shared/attaform.C09fpdLS.mjs';
3
- export { A as AttaformError, I as InvalidPathError, a as InvalidUseFormConfigError, O as OutsideSetupError, R as ROOT_PATH, b as ROOT_PATH_KEY, c as RegistryNotInstalledError, d as ReservedFormKeyError, S as SubmitErrorHandlerError, e as canonicalizePath, i as isPathPrefix, p as parseDottedPath } from './shared/attaform.wfY8XFvm.mjs';
4
- export { A as AttaformErrorCode, D as DEFAULT_TIMINGS, d as defaultCoercionRules, a as defaultDisplayState, b as defineCoercion, e as escapeForInlineScript, g as gate, i as injectForm, c as injectWizard, f as isUnset, l as lazy, m as makeDefaultDisplayState, u as unset, j as useRegister, k as useWizard } from './shared/attaform.B8k8JrmK.mjs';
5
- export { D as DEVTOOLS_WINDOW_KEY, c as createAttaform, a as createRegistry, g as getRegistryFromApp, h as hydrateAttaformState, k as kAttaformRegistry, r as renderAttaformState, u as useRegistry } from './shared/attaform.Dq4vWxuG.mjs';
6
- export { a as assignKey, v as vRegister } from './shared/attaform.uLbun31C.mjs';
1
+ export { i as isZodSchemaType, u as useForm, z as zodAdapter } from './shared/attaform.D1fnTifF.mjs';
2
+ import { i as installingFieldMetaStore, g as getFieldMetaForSchema } from './shared/attaform.B0dKL8ST.mjs';
3
+ export { A as AttaformError, I as InvalidPathError, a as InvalidUseFormConfigError, O as OutsideSetupError, R as ROOT_PATH, b as ROOT_PATH_KEY, c as RegistryNotInstalledError, d as ReservedFormKeyError, S as SubmitErrorHandlerError, e as canonicalizePath, i as isPathPrefix, p as parseDottedPath } from './shared/attaform.CZYt_5N_.mjs';
4
+ export { A as AttaformErrorCode, D as DEFAULT_TIMINGS, d as defaultCoercionRules, a as defaultDisplayState, b as defineCoercion, e as escapeForInlineScript, g as gate, i as injectForm, c as injectWizard, f as isUnset, l as lazy, m as makeDefaultDisplayState, u as unset, j as useRegister, k as useWizard } from './shared/attaform.CZMAYwSK.mjs';
5
+ export { D as DEVTOOLS_WINDOW_KEY, c as createAttaform, a as createRegistry, g as getRegistryFromApp, h as hydrateAttaformState, k as kAttaformRegistry, r as renderAttaformState, u as useRegistry } from './shared/attaform.D0x-qSXm.mjs';
6
+ export { a as assignKey, v as vRegister } from './shared/attaform.DffRL5pP.mjs';
7
7
  export { i as isRegisterValue } from './shared/attaform.KvlEedlF.mjs';
8
8
 
9
9
  const fieldMeta = installingFieldMetaStore;
package/dist/zod-v4.d.mts CHANGED
@@ -1,10 +1,10 @@
1
- import { S as SupportedRootSchema } from './shared/attaform.B4VkS43-.mjs';
2
- export { P as PathInput, a as PathOutput, u as useForm } from './shared/attaform.B4VkS43-.mjs';
3
- import { c as AttaformError } from './shared/attaform.D5V4tZWq.mjs';
4
- export { a as AnyForm, b as AttaformDevtoolsBridge, d as AttaformErrorCode, e as AttaformPluginOptions, A as AttaformRegistry, C as CompiledStep, D as DEFAULT_TIMINGS, f as DEVTOOLS_WINDOW_KEY, g as DisplayTimings, F as FormStatus, h as FormStatusSeed, G as GateMarker, I as InjectWizardInput, i as InvalidPathError, j as InvalidUseFormConfigError, L as LazyMarker, O as OutsideSetupError, R as RegistryNotInstalledError, k as ReservedFormKeyError, S as SerializedAttaformState, l as SerializedFormData, m as StepSlot, n as SubmitErrorHandlerError, U as UseRegisterReturn, o as UseWizardReturnType, W as WizardAggregateError, p as WizardCtx, q as WizardCtxForm, r as WizardOnError, s as WizardOnSubmit, t as WizardOptions, u as WizardPersistFn, v as WizardRestoreFn, w as WizardRestoreState, x as WizardStatusesProxy, y as WizardSubmitContext, z as assignKey, B as createAttaform, E as createRegistry, H as defaultCoercionRules, J as defaultDisplayState, K as defineCoercion, M as escapeForInlineScript, N as gate, P as getRegistryFromApp, Q as hydrateAttaformState, T as injectForm, V as injectWizard, X as isRegisterValue, Y as kAttaformRegistry, Z as lazy, _ as makeDefaultDisplayState, $ as renderAttaformState, a0 as useRegister, a1 as useRegistry, a2 as useWizard } from './shared/attaform.D5V4tZWq.mjs';
5
- import { F as FormKey, at as SchemaFactoryOptions, A as AbstractSchema, o as FieldMetaPayload } from './shared/attaform.Cgu-hdSb.mjs';
6
- export { b as ArrayItem, c as ArrayPath, d as AttaformDefaults, C as CoercionEntry, e as CoercionRegistry, f as CoercionResult, g as CustomDirectiveRegisterAssignerFn, h as DeepPartial, D as DefaultValuesInput, i as DefaultValuesResponse, j as DefaultValuesShape, k as DisplayCtx, l as DisplayMachine, m as DisplayState, E as ErrorInput, n as ErrorsProxyShape, p as FieldState, q as FieldStateMap, r as FieldStateMapEntry, s as FlatPath, t as FormErrorRecord, u as FormErrorsSurface, v as FormMeta, G as GenericForm, w as GetDisplayState, H as HandleSubmit, x as HistoryPlugin, I as IsTuple, y as IsUnion, J as JoinSegments, z as Json, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, B as NestedType, O as OnError, P as OnInvalidSubmitPolicy, Q as OnSubmit, R as ParseOptions, S as PartialFlatPath, T as Path, V as PathKey, W as PendingValidationStatus, X as Primitive, Y as ROOT_PATH, Z as ROOT_PATH_KEY, _ as ReactiveValidationStatus, $ as RegisterDirective, a0 as RegisterFlatPath, a1 as RegisterOptions, a2 as RegisterSelectModifier, a3 as RegisterTextModifier, a4 as RegisterTransform, a5 as RegisterValue, a6 as Segment, a7 as SetValueCallback, a8 as SetValuePayload, a9 as SettledValidationStatus, aa as SlimPrimitiveKind, ab as SlimRuntimeOf, ac as SubmitHandler, ad as Unset, U as UseFormConfiguration, a as UseFormReturnType, ae as ValidateOn, af as ValidateOnConfig, ag as ValidationError, ah as ValidationResponse, ai as ValidationResponseWithoutValue, aj as ValueOfUnion, ak as WriteMeta, al as WriteShape, am as canonicalizePath, an as isPathPrefix, ao as isUnset, ap as parseDottedPath, aq as unset } from './shared/attaform.Cgu-hdSb.mjs';
7
- export { v as vRegister } from './shared/attaform.JfSWdEcb.mjs';
1
+ import { S as SupportedRootSchema } from './shared/attaform.CIWLMY-R.mjs';
2
+ export { P as PathInput, a as PathOutput, u as useForm } from './shared/attaform.CIWLMY-R.mjs';
3
+ export { a as AnyForm, b as AttaformDevtoolsBridge, c as AttaformError, d as AttaformErrorCode, e as AttaformPluginOptions, A as AttaformRegistry, C as CompiledStep, D as DEFAULT_TIMINGS, f as DEVTOOLS_WINDOW_KEY, g as DisplayTimings, F as FormStatus, h as FormStatusSeed, G as GateMarker, I as InjectWizardInput, i as InvalidPathError, j as InvalidUseFormConfigError, L as LazyMarker, O as OutsideSetupError, R as RegistryNotInstalledError, k as ReservedFormKeyError, S as SerializedAttaformState, l as SerializedFormData, m as StepSlot, n as SubmitErrorHandlerError, U as UseRegisterOptions, o as UseRegisterReturn, p as UseWizardReturnType, W as WizardAggregateError, q as WizardCtx, r as WizardCtxForm, s as WizardOnError, t as WizardOnSubmit, u as WizardOptions, v as WizardPersistFn, w as WizardRestoreFn, x as WizardRestoreState, y as WizardStatusesProxy, z as WizardSubmitContext, B as assignKey, E as createAttaform, H as createRegistry, J as defaultCoercionRules, K as defaultDisplayState, M as defineCoercion, N as escapeForInlineScript, P as gate, Q as getRegistryFromApp, T as hydrateAttaformState, V as injectForm, X as injectWizard, Y as isRegisterValue, Z as kAttaformRegistry, _ as lazy, $ as makeDefaultDisplayState, a0 as renderAttaformState, a1 as useRegister, a2 as useRegistry, a3 as useWizard } from './shared/attaform.DDtqHYv1.mjs';
4
+ import { F as FormKey, au as SchemaFactoryOptions, o as FieldMetaPayload } from './shared/attaform.CsoeRi3P.mjs';
5
+ import { AbstractSchema } from './abstract.mjs';
6
+ export { b as ArrayItem, c as ArrayPath, d as AttaformDefaults, C as CoercionEntry, e as CoercionRegistry, f as CoercionResult, g as CustomDirectiveRegisterAssignerFn, h as DeepPartial, D as DefaultValuesInput, i as DefaultValuesResponse, j as DefaultValuesShape, k as DisplayCtx, l as DisplayMachine, m as DisplayState, E as ErrorInput, n as ErrorsProxyShape, p as FieldState, q as FieldStateMap, r as FieldStateMapEntry, s as FlatPath, t as FormErrorRecord, u as FormErrorsSurface, v as FormMeta, G as GenericForm, w as GetDisplayState, H as HandleSubmit, x as HistoryPlugin, I as IsTuple, y as IsUnion, J as JoinSegments, z as Json, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, B as NestedType, O as OnError, P as OnInvalidSubmitPolicy, Q as OnSubmit, R as ParseOptions, S as PartialFlatPath, T as Path, V as PathKey, W as PendingValidationStatus, X as Primitive, Y as ROOT_PATH, Z as ROOT_PATH_KEY, _ as ReactiveValidationStatus, $ as RecordPath, a0 as RecordValue, a1 as RegisterDirective, a2 as RegisterFlatPath, a3 as RegisterOptions, a4 as RegisterSelectModifier, a5 as RegisterTextModifier, a6 as RegisterTransform, a7 as RegisterValue, a8 as Segment, a9 as SetValueCallback, aa as SetValuePayload, ab as SettledValidationStatus, ac as SlimPrimitiveKind, ad as SlimRuntimeOf, ae as SubmitHandler, af as Unset, U as UseFormConfiguration, a as UseFormReturnType, ag as ValidateOn, ah as ValidateOnConfig, ai as ValidationError, aj as ValidationResponse, ak as ValidationResponseWithoutValue, al as ValueOfUnion, am as WriteMeta, an as WriteShape, ao as canonicalizePath, ap as isPathPrefix, aq as isUnset, ar as parseDottedPath, as as unset } from './shared/attaform.CsoeRi3P.mjs';
7
+ export { v as vRegister } from './shared/attaform.axHXVQO7.mjs';
8
8
  import { z } from 'zod';
9
9
  import 'vue';
10
10
 
@@ -22,28 +22,15 @@ import 'vue';
22
22
  * downstream walk so a per-form override can lift the cap without
23
23
  * touching the app-level default.
24
24
  *
25
- * Throws if the schema isn't Zod v4 or contains kinds the adapter
26
- * cannot represent (`z.promise`, `z.custom`, `z.templateLiteral`).
27
- * Recursive `z.lazy(...)` is supported — the runtime walks bound their
28
- * descent via `maxRecursionDepth`.
25
+ * Throws if the schema isn't Zod v4, or if its root is not a shape
26
+ * that can hold keys. No kind is refused anywhere BELOW the root: a
27
+ * leaf the walkers have no case for is carried opaquely rather than
28
+ * rejected, so a schema a newer Zod can parse is one Attaform can
29
+ * mount. Recursive `z.lazy(...)` is supported — the runtime walks
30
+ * bound their descent via `maxRecursionDepth`.
29
31
  */
30
32
  declare function zodV4Adapter<FormSchema extends SupportedRootSchema, Form extends z.input<FormSchema>, GetValueFormType extends z.output<FormSchema> = z.output<FormSchema>>(rootSchema: FormSchema): (formKey: FormKey, options: SchemaFactoryOptions) => AbstractSchema<Form, GetValueFormType>;
31
33
 
32
- /**
33
- * Thrown when a Zod schema includes a kind the form library cannot
34
- * represent: `z.promise`, `z.custom`, or `z.templateLiteral`.
35
- *
36
- * The error message includes the dotted path of the offending node
37
- * so you can locate it without traversing the whole schema.
38
- *
39
- * Recursive `z.lazy(...)` is supported, not unsupported: the
40
- * construction-time walk stops descending on the second encounter
41
- * of the same getter, and downstream walks cap their descent via
42
- * `maxRecursionDepth`.
43
- */
44
- declare class UnsupportedSchemaError extends AttaformError {
45
- }
46
-
47
34
  /**
48
35
  * The single file that reads Zod v4's internal `def` shape. Every other
49
36
  * file in the zod-v4 adapter uses these public-shaped accessors — future
@@ -174,5 +161,5 @@ declare const fieldMeta: ZodFieldMetaRegistry;
174
161
  */
175
162
  declare function withMeta<S extends z.ZodType>(schema: S, payload: FieldMetaPayload): S;
176
163
 
177
- export { AttaformError, FieldMetaPayload, FormKey, UnsupportedSchemaError, assertZodVersion, fieldMeta, kindOf, withMeta, zodV4Adapter as zodAdapter };
164
+ export { FieldMetaPayload, FormKey, assertZodVersion, fieldMeta, kindOf, withMeta, zodV4Adapter as zodAdapter };
178
165
  export type { ZodKind };
package/dist/zod-v4.mjs CHANGED
@@ -1,9 +1,9 @@
1
- export { U as UnsupportedSchemaError, a as assertZodVersion, k as kindOf, u as useForm, z as zodAdapter } from './shared/attaform.NLmerepI.mjs';
2
- import { i as installingFieldMetaStore, g as getFieldMetaForSchema } from './shared/attaform.C09fpdLS.mjs';
3
- export { A as AttaformError, I as InvalidPathError, a as InvalidUseFormConfigError, O as OutsideSetupError, R as ROOT_PATH, b as ROOT_PATH_KEY, c as RegistryNotInstalledError, d as ReservedFormKeyError, S as SubmitErrorHandlerError, e as canonicalizePath, i as isPathPrefix, p as parseDottedPath } from './shared/attaform.wfY8XFvm.mjs';
4
- export { A as AttaformErrorCode, D as DEFAULT_TIMINGS, d as defaultCoercionRules, a as defaultDisplayState, b as defineCoercion, e as escapeForInlineScript, g as gate, i as injectForm, c as injectWizard, f as isUnset, l as lazy, m as makeDefaultDisplayState, u as unset, j as useRegister, k as useWizard } from './shared/attaform.B8k8JrmK.mjs';
5
- export { D as DEVTOOLS_WINDOW_KEY, c as createAttaform, a as createRegistry, g as getRegistryFromApp, h as hydrateAttaformState, k as kAttaformRegistry, r as renderAttaformState, u as useRegistry } from './shared/attaform.Dq4vWxuG.mjs';
6
- export { a as assignKey, v as vRegister } from './shared/attaform.uLbun31C.mjs';
1
+ export { a as assertZodVersion, k as kindOf, u as useForm, z as zodAdapter } from './shared/attaform.C2MVyuxC.mjs';
2
+ import { i as installingFieldMetaStore, g as getFieldMetaForSchema } from './shared/attaform.B0dKL8ST.mjs';
3
+ export { A as AttaformError, I as InvalidPathError, a as InvalidUseFormConfigError, O as OutsideSetupError, R as ROOT_PATH, b as ROOT_PATH_KEY, c as RegistryNotInstalledError, d as ReservedFormKeyError, S as SubmitErrorHandlerError, e as canonicalizePath, i as isPathPrefix, p as parseDottedPath } from './shared/attaform.CZYt_5N_.mjs';
4
+ export { A as AttaformErrorCode, D as DEFAULT_TIMINGS, d as defaultCoercionRules, a as defaultDisplayState, b as defineCoercion, e as escapeForInlineScript, g as gate, i as injectForm, c as injectWizard, f as isUnset, l as lazy, m as makeDefaultDisplayState, u as unset, j as useRegister, k as useWizard } from './shared/attaform.CZMAYwSK.mjs';
5
+ export { D as DEVTOOLS_WINDOW_KEY, c as createAttaform, a as createRegistry, g as getRegistryFromApp, h as hydrateAttaformState, k as kAttaformRegistry, r as renderAttaformState, u as useRegistry } from './shared/attaform.D0x-qSXm.mjs';
6
+ export { a as assignKey, v as vRegister } from './shared/attaform.DffRL5pP.mjs';
7
7
  export { i as isRegisterValue } from './shared/attaform.KvlEedlF.mjs';
8
8
 
9
9
  const fieldMeta = installingFieldMetaStore;
package/dist/zod.d.mts CHANGED
@@ -1,8 +1,8 @@
1
- export { a as AnyForm, b as AttaformDevtoolsBridge, c as AttaformError, d as AttaformErrorCode, e as AttaformPluginOptions, A as AttaformRegistry, C as CompiledStep, D as DEFAULT_TIMINGS, f as DEVTOOLS_WINDOW_KEY, g as DisplayTimings, F as FormStatus, h as FormStatusSeed, G as GateMarker, I as InjectWizardInput, i as InvalidPathError, j as InvalidUseFormConfigError, L as LazyMarker, O as OutsideSetupError, R as RegistryNotInstalledError, k as ReservedFormKeyError, S as SerializedAttaformState, l as SerializedFormData, m as StepSlot, n as SubmitErrorHandlerError, U as UseRegisterReturn, o as UseWizardReturnType, W as WizardAggregateError, p as WizardCtx, q as WizardCtxForm, r as WizardOnError, s as WizardOnSubmit, t as WizardOptions, u as WizardPersistFn, v as WizardRestoreFn, w as WizardRestoreState, x as WizardStatusesProxy, y as WizardSubmitContext, z as assignKey, B as createAttaform, E as createRegistry, H as defaultCoercionRules, J as defaultDisplayState, K as defineCoercion, M as escapeForInlineScript, N as gate, P as getRegistryFromApp, Q as hydrateAttaformState, T as injectForm, V as injectWizard, X as isRegisterValue, Y as kAttaformRegistry, Z as lazy, _ as makeDefaultDisplayState, $ as renderAttaformState, a0 as useRegister, a1 as useRegistry, a2 as useWizard } from './shared/attaform.D5V4tZWq.mjs';
2
- export { b as ArrayItem, c as ArrayPath, d as AttaformDefaults, C as CoercionEntry, e as CoercionRegistry, f as CoercionResult, g as CustomDirectiveRegisterAssignerFn, h as DeepPartial, D as DefaultValuesInput, i as DefaultValuesResponse, j as DefaultValuesShape, k as DisplayCtx, l as DisplayMachine, m as DisplayState, E as ErrorInput, n as ErrorsProxyShape, o as FieldMetaPayload, p as FieldState, q as FieldStateMap, r as FieldStateMapEntry, s as FlatPath, t as FormErrorRecord, u as FormErrorsSurface, F as FormKey, v as FormMeta, G as GenericForm, w as GetDisplayState, H as HandleSubmit, x as HistoryPlugin, I as IsTuple, y as IsUnion, J as JoinSegments, z as Json, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, B as NestedType, O as OnError, P as OnInvalidSubmitPolicy, Q as OnSubmit, R as ParseOptions, S as PartialFlatPath, T as Path, V as PathKey, W as PendingValidationStatus, X as Primitive, Y as ROOT_PATH, Z as ROOT_PATH_KEY, _ as ReactiveValidationStatus, $ as RegisterDirective, a0 as RegisterFlatPath, a1 as RegisterOptions, a2 as RegisterSelectModifier, a3 as RegisterTextModifier, a4 as RegisterTransform, a5 as RegisterValue, a6 as Segment, a7 as SetValueCallback, a8 as SetValuePayload, a9 as SettledValidationStatus, aa as SlimPrimitiveKind, ab as SlimRuntimeOf, ac as SubmitHandler, ad as Unset, U as UseFormConfiguration, a as UseFormReturnType, ae as ValidateOn, af as ValidateOnConfig, ag as ValidationError, ah as ValidationResponse, ai as ValidationResponseWithoutValue, aj as ValueOfUnion, ak as WriteMeta, al as WriteShape, am as canonicalizePath, an as isPathPrefix, ao as isUnset, ap as parseDottedPath, aq as unset } from './shared/attaform.Cgu-hdSb.mjs';
3
- export { v as vRegister } from './shared/attaform.JfSWdEcb.mjs';
1
+ export { a as AnyForm, b as AttaformDevtoolsBridge, c as AttaformError, d as AttaformErrorCode, e as AttaformPluginOptions, A as AttaformRegistry, C as CompiledStep, D as DEFAULT_TIMINGS, f as DEVTOOLS_WINDOW_KEY, g as DisplayTimings, F as FormStatus, h as FormStatusSeed, G as GateMarker, I as InjectWizardInput, i as InvalidPathError, j as InvalidUseFormConfigError, L as LazyMarker, O as OutsideSetupError, R as RegistryNotInstalledError, k as ReservedFormKeyError, S as SerializedAttaformState, l as SerializedFormData, m as StepSlot, n as SubmitErrorHandlerError, U as UseRegisterOptions, o as UseRegisterReturn, p as UseWizardReturnType, W as WizardAggregateError, q as WizardCtx, r as WizardCtxForm, s as WizardOnError, t as WizardOnSubmit, u as WizardOptions, v as WizardPersistFn, w as WizardRestoreFn, x as WizardRestoreState, y as WizardStatusesProxy, z as WizardSubmitContext, B as assignKey, E as createAttaform, H as createRegistry, J as defaultCoercionRules, K as defaultDisplayState, M as defineCoercion, N as escapeForInlineScript, P as gate, Q as getRegistryFromApp, T as hydrateAttaformState, V as injectForm, X as injectWizard, Y as isRegisterValue, Z as kAttaformRegistry, _ as lazy, $ as makeDefaultDisplayState, a0 as renderAttaformState, a1 as useRegister, a2 as useRegistry, a3 as useWizard } from './shared/attaform.DDtqHYv1.mjs';
2
+ export { b as ArrayItem, c as ArrayPath, d as AttaformDefaults, C as CoercionEntry, e as CoercionRegistry, f as CoercionResult, g as CustomDirectiveRegisterAssignerFn, h as DeepPartial, D as DefaultValuesInput, i as DefaultValuesResponse, j as DefaultValuesShape, k as DisplayCtx, l as DisplayMachine, m as DisplayState, E as ErrorInput, n as ErrorsProxyShape, o as FieldMetaPayload, p as FieldState, q as FieldStateMap, r as FieldStateMapEntry, s as FlatPath, t as FormErrorRecord, u as FormErrorsSurface, F as FormKey, v as FormMeta, G as GenericForm, w as GetDisplayState, H as HandleSubmit, x as HistoryPlugin, I as IsTuple, y as IsUnion, J as JoinSegments, z as Json, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, B as NestedType, O as OnError, P as OnInvalidSubmitPolicy, Q as OnSubmit, R as ParseOptions, S as PartialFlatPath, T as Path, V as PathKey, W as PendingValidationStatus, X as Primitive, Y as ROOT_PATH, Z as ROOT_PATH_KEY, _ as ReactiveValidationStatus, $ as RecordPath, a0 as RecordValue, a1 as RegisterDirective, a2 as RegisterFlatPath, a3 as RegisterOptions, a4 as RegisterSelectModifier, a5 as RegisterTextModifier, a6 as RegisterTransform, a7 as RegisterValue, a8 as Segment, a9 as SetValueCallback, aa as SetValuePayload, ab as SettledValidationStatus, ac as SlimPrimitiveKind, ad as SlimRuntimeOf, ae as SubmitHandler, af as Unset, U as UseFormConfiguration, a as UseFormReturnType, ag as ValidateOn, ah as ValidateOnConfig, ai as ValidationError, aj as ValidationResponse, ak as ValidationResponseWithoutValue, al as ValueOfUnion, am as WriteMeta, an as WriteShape, ao as canonicalizePath, ap as isPathPrefix, aq as isUnset, ar as parseDottedPath, as as unset } from './shared/attaform.CsoeRi3P.mjs';
3
+ export { v as vRegister } from './shared/attaform.axHXVQO7.mjs';
4
4
  export { UseFormConfig, UseFormConfigV3, UseFormConfigV4, UseFormReturn, UseFormReturnV3, UseFormReturnV4, fieldMeta, useForm, withMeta } from './index.mjs';
5
- export { P as PathInput, a as PathOutput } from './shared/attaform.B4VkS43-.mjs';
5
+ export { P as PathInput, a as PathOutput } from './shared/attaform.CIWLMY-R.mjs';
6
6
  import 'vue';
7
7
  import './shared/attaform.DRUT703x.mjs';
8
8
  import 'zod';
package/dist/zod.mjs CHANGED
@@ -1,6 +1,6 @@
1
- export { A as AttaformError, I as InvalidPathError, a as InvalidUseFormConfigError, O as OutsideSetupError, R as ROOT_PATH, b as ROOT_PATH_KEY, c as RegistryNotInstalledError, d as ReservedFormKeyError, S as SubmitErrorHandlerError, e as canonicalizePath, i as isPathPrefix, p as parseDottedPath } from './shared/attaform.wfY8XFvm.mjs';
2
- export { A as AttaformErrorCode, D as DEFAULT_TIMINGS, d as defaultCoercionRules, a as defaultDisplayState, b as defineCoercion, e as escapeForInlineScript, g as gate, i as injectForm, c as injectWizard, f as isUnset, l as lazy, m as makeDefaultDisplayState, u as unset, j as useRegister, k as useWizard } from './shared/attaform.B8k8JrmK.mjs';
3
- export { D as DEVTOOLS_WINDOW_KEY, c as createAttaform, a as createRegistry, g as getRegistryFromApp, h as hydrateAttaformState, k as kAttaformRegistry, r as renderAttaformState, u as useRegistry } from './shared/attaform.Dq4vWxuG.mjs';
4
- export { a as assignKey, v as vRegister } from './shared/attaform.uLbun31C.mjs';
5
- export { f as fieldMeta, u as useForm, w as withMeta } from './shared/attaform.D6DCnnQu.mjs';
1
+ export { A as AttaformError, I as InvalidPathError, a as InvalidUseFormConfigError, O as OutsideSetupError, R as ROOT_PATH, b as ROOT_PATH_KEY, c as RegistryNotInstalledError, d as ReservedFormKeyError, S as SubmitErrorHandlerError, e as canonicalizePath, i as isPathPrefix, p as parseDottedPath } from './shared/attaform.CZYt_5N_.mjs';
2
+ export { A as AttaformErrorCode, D as DEFAULT_TIMINGS, d as defaultCoercionRules, a as defaultDisplayState, b as defineCoercion, e as escapeForInlineScript, g as gate, i as injectForm, c as injectWizard, f as isUnset, l as lazy, m as makeDefaultDisplayState, u as unset, j as useRegister, k as useWizard } from './shared/attaform.CZMAYwSK.mjs';
3
+ export { D as DEVTOOLS_WINDOW_KEY, c as createAttaform, a as createRegistry, g as getRegistryFromApp, h as hydrateAttaformState, k as kAttaformRegistry, r as renderAttaformState, u as useRegistry } from './shared/attaform.D0x-qSXm.mjs';
4
+ export { a as assignKey, v as vRegister } from './shared/attaform.DffRL5pP.mjs';
5
+ export { f as fieldMeta, u as useForm, w as withMeta } from './shared/attaform.BndjUUYX.mjs';
6
6
  export { i as isRegisterValue } from './shared/attaform.KvlEedlF.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "attaform",
3
- "version": "0.28.0",
3
+ "version": "0.29.0",
4
4
  "types": "./dist/index.d.mts",
5
5
  "files": [
6
6
  "dist",
@@ -130,17 +130,18 @@
130
130
  "@commitlint/config-conventional": "^21.0.1",
131
131
  "@eslint/js": "^10.0.1",
132
132
  "@fast-check/vitest": "^0.4.0",
133
+ "@nuxt/devtools": "^3.4.2",
133
134
  "@nuxt/devtools-kit": "^3.2.0",
134
135
  "@nuxt/kit": "^4.4.2",
135
136
  "@nuxt/schema": "^4.4.2",
136
137
  "@nuxt/test-utils": "^4.0.2",
137
- "@nuxt/ui": "^4.8.0",
138
+ "@nuxt/ui": "^4.11.1",
138
139
  "@rollup/plugin-alias": "^6.0.0",
139
140
  "@size-limit/preset-small-lib": "^12.1.0",
140
- "@types/jsdom": "^28.0.1",
141
- "@types/node": "^25.6.0",
141
+ "@types/jsdom": "^30.0.0",
142
+ "@types/node": "^26.5.1",
142
143
  "@vitejs/plugin-vue": "^6.0.6",
143
- "@vitest/coverage-v8": "^4.1.5",
144
+ "@vitest/coverage-v8": "^4.1.11",
144
145
  "@vue/compiler-core": "^3.5.33",
145
146
  "@vue/compiler-sfc": "^3.5.33",
146
147
  "@vue/devtools-api": "^8.1.1",
@@ -157,19 +158,20 @@
157
158
  "fast-check": "^4.7.0",
158
159
  "globals": "^17.5.0",
159
160
  "husky": "^9.1.7",
160
- "jsdom": "^29.0.2",
161
+ "jsdom": "^30.0.1",
161
162
  "lint-staged": "^17.0.4",
162
163
  "nuxt": "^4.5.2",
163
164
  "prettier": "^3.8.3",
164
- "primevue": "^4.5.5",
165
- "reka-ui": "^2.9.10",
165
+ "primevue": "4.5.5",
166
+ "reka-ui": "^2.10.4",
167
+ "rollup": "^4.63.1",
166
168
  "rollup-plugin-dts": "^6.4.1",
167
169
  "size-limit": "^12.1.0",
168
170
  "typescript": "6.0.3",
169
171
  "typescript-eslint": "^8.59.0",
170
172
  "unbuild": "^3.6.1",
171
- "vite": "^8.0.10",
172
- "vitest": "^4.1.5",
173
+ "vite": "^8.3.0",
174
+ "vitest": "^4.1.11",
173
175
  "vue": "^3.5.33",
174
176
  "vue-eslint-parser": "^10.4.0",
175
177
  "vue-tsc": "^3.2.7",
@@ -208,11 +210,11 @@
208
210
  "dev:prepare": "unbuild --stub && nuxi prepare apps/site",
209
211
  "dev:site": "pnpm --filter attaform-site dev",
210
212
  "build:site": "pnpm --filter attaform-site build",
211
- "lint": "eslint --cache --concurrency=auto . && pnpm format:check",
212
- "lint:fix": "eslint --cache --concurrency=auto --fix . && pnpm format",
213
+ "lint": "eslint --cache --concurrency=auto --max-warnings 0 . && pnpm format:check",
214
+ "lint:fix": "eslint --cache --concurrency=auto --max-warnings 0 --fix . && pnpm format",
213
215
  "format": "prettier --write '**/*.{ts,js,vue,css,scss,md,json,yml,yaml}'",
214
216
  "format:check": "prettier --check '**/*.{ts,js,vue,css,scss,md,json,yml,yaml}'",
215
- "check": "pnpm lint && pnpm typecheck && pnpm check:site && pnpm test && pnpm test:nuxt-ui && pnpm check:size && pnpm check:tarball && pnpm check:eager && pnpm check:bench && pnpm check:coverage",
217
+ "check": "pnpm check:security-floors && pnpm lint && pnpm typecheck && pnpm check:site && pnpm test && pnpm test:nuxt-ui && pnpm check:size && pnpm check:tarball && pnpm check:eager && pnpm check:bench && pnpm check:coverage",
216
218
  "fix": "pnpm lint:fix",
217
219
  "typecheck": "tsc --noEmit",
218
220
  "prepack:watch": "chokidar 'src/**/*' -c 'pnpm prepack' --initial",
@@ -222,6 +224,7 @@
222
224
  "test:types": "pnpm dev:prepare && vue-tsc --noEmit && cd apps/site && vue-tsc --noEmit",
223
225
  "test:watch": "node scripts/run-with-webstorage-flag.mjs vitest watch",
224
226
  "bench": "node scripts/run-with-webstorage-flag.mjs vitest bench --run",
227
+ "check:security-floors": "node scripts/check-security-floors.mjs",
225
228
  "check:size": "pnpm prepack && size-limit",
226
229
  "check:tarball": "node scripts/check-tarball-size.mjs",
227
230
  "check:eager": "node scripts/check-eager-size.mjs",
@@ -231,6 +234,6 @@
231
234
  "check:doc-snippets": "node scripts/check-doc-snippets.mjs",
232
235
  "check:site": "pnpm dev:prepare && pnpm --filter attaform-site typecheck",
233
236
  "fallow": "DO_NOT_TRACK=1 FALLOW_TELEMETRY=0 npx --yes fallow@2.88.3 --score",
234
- "version": "node scripts/promote-changelog.mjs && (node scripts/generate-release-notes.mjs || echo '[version] release-notes step failed; continuing') && node scripts/sync-doc-versions.mjs && git add CHANGELOG.md RELEASES.md docs/scorecard/cii-best-practices-answers.md"
237
+ "version": "node scripts/promote-changelog.mjs && (node scripts/generate-release-notes.mjs || echo '[version] release-notes step failed; continuing') && node scripts/sync-doc-versions.mjs && node scripts/build-bestpractices-json.mjs && git add CHANGELOG.md RELEASES.md docs/scorecard/cii-best-practices-answers.md .bestpractices.json"
235
238
  }
236
239
  }
@@ -24,13 +24,14 @@ import {
24
24
  fieldMeta,
25
25
  withMeta,
26
26
  lazy,
27
+ gate,
27
28
  } from 'attaform'
28
29
  ```
29
30
 
30
31
  - **Do not import `useForm` from `attaform/abstract`.** That entry is the bring-your-own-adapter escape hatch: it exports `useAbstractForm`, which needs a schema adapter wired by hand. A Zod project wants `useForm` from `attaform`. This mismatch is the single most common first-try mistake.
31
32
  - Types come from the same barrel: `import type { AnyForm, WizardCtx, Json, ValidationError } from 'attaform'`.
32
33
  - `attaform/zod` is the same surface named explicitly; `attaform/zod-v3` and `attaform/zod-v4` pin a Zod major. Any of these works; default to `attaform`.
33
- - In **Nuxt** with the module installed (`attaform/nuxt`), this surface auto-imports and the `v-register` directive is bound into each compiled template, so a component needs no import lines at all. In **plain Vite**, add the Attaform plugin from `attaform/vite` (it binds `v-register` the same way) plus the auto-import preset it exports. Without either build plugin, register the directive once per app: `import { installVRegister } from 'attaform/directive'` then `installVRegister(app)`.
34
+ - In **Nuxt** with the module installed (`attaform/nuxt`), the whole block above auto-imports and the `v-register` directive is bound into each compiled template, so a component needs no import lines at all. Writing them anyway is always safe and always portable: an explicit import beats an auto-import of the same name, which is why every snippet below keeps its import line. In **plain Vite**, add the Attaform plugin from `attaform/vite` (it binds `v-register` the same way) plus the auto-import preset it exports. Without either build plugin, register the directive once per app: `import { installVRegister } from 'attaform/directive'` then `installVRegister(app)`.
34
35
  - Undo/redo ships from its own entry: `import { historyPlugin } from 'attaform/history'`, then `useForm({ schema, history: historyPlugin({ max: 200 }) })` unlocks a live `form.history` namespace (`undo` / `redo` / `clear` / `canUndo` / `canRedo` / `size`). Without the plugin the namespace is inert stubs.
35
36
 
36
37
  ## Build a form
@@ -86,7 +87,8 @@ Prefer an explicit `key` (`useForm({ schema, key: 'sign-in' })`): it makes the f
86
87
  - **Reach with `?.` on injected forms.** `injectForm()` and `injectWizard()` return `T | null`; chain optional access (`form?.register('email')`) at every consumption site.
87
88
  - **Put labels on the schema, not the template.** `z.string().register(fieldMeta, { label: 'Email' })`; read it back through `form.fields.email.label` (resolved, with a humanized-path fallback).
88
89
  - **Native inputs first.** Bind `<input>`, `<select>`, `<textarea>` with `v-register`. Reach for `useRegister` only inside a custom input component. See `references/custom-components.md`.
89
- - **`v-register` alone does binding, SSR value injection, and ARIA.** Do not stack `@change` handlers, reset-signal props, or watchers on top of it. If a control seems to need that scaffolding, the idiomatic shape is being missed.
90
+ - **`v-register` alone does binding, SSR value injection, and ARIA.** Do not stack a second _writer_ on it: no `v-model`, no handler that writes the field back, no reset-signal prop. If a control seems to need that scaffolding, the idiomatic shape is being missed.
91
+ - **Observing the field is fine, and is how a per-interaction save is built.** A `@change` that only reads, or a `watch(form.toRef('path'), ...)`, sits beside `v-register` without fighting it: the directive writes the field before your handler runs, so the value you read is the committed one. Reach for this whenever a surface persists each decision as it is made rather than on submit. See `references/saving.md`.
90
92
 
91
93
  ## Wizards
92
94
 
@@ -120,6 +122,7 @@ Load one when the task reaches its area:
120
122
  - **`references/custom-components.md`**: wrapping an input or binding a third-party component. `useRegister`, the three orthogonal primitives, attribute fallthrough, and the optional `form-key` prop.
121
123
  - **`references/ssr.md`**: debugging SSR and hydration. Why the value injection is a build-time transform, how to confirm it faithfully, and the test traps to avoid.
122
124
  - **`references/validation.md`**: designing the schema. Client-is-UX / server-is-truth, keeping closed sets in sync, and why a clearable edit field is a required string.
125
+ - **`references/saving.md`**: persisting each decision as it is made. Choosing between an observing listener and a `watch`, the ordering that makes a listener safe, and the four things a production autosave adds.
123
126
 
124
127
  ## Going deeper
125
128
 
@@ -49,11 +49,16 @@ For a **compound** component that binds _multiple_ paths (a date range exposing
49
49
 
50
50
  - Read the display signals straight in the template: `field.showErrors` gates the error row, `field.showPending` gates an async "checking" indicator (it is anti-flash timed), `field.firstError?.message` is the text.
51
51
  - `v-register`'s `autoAria` (on by default) keeps `aria-invalid`, `aria-busy`, and `aria-required` in sync. It wires the _error_ id, and only while the field is in its error state, so author `aria-describedby` yourself if a _static_ hint should stay associated too.
52
- - **`v-register` alone does binding, SSR value injection, and ARIA.** Do not stack `@change` handlers, `:reset-signal` props, redundant directive imports, or watchers on top of it. If a control seems to need that scaffolding, find the idiomatic shape rather than hand-rolling around it.
52
+ - **`v-register` alone does binding, SSR value injection, and ARIA.** Do not stack a second _writer_ on it: no `v-model`, no handler that writes the field back, no `:reset-signal` prop, no redundant directive import. If a control seems to need that scaffolding, find the idiomatic shape rather than hand-rolling around it.
53
+ - **An _observer_ is not a second writer.** A `@change` that only reads, or a `watch(form.toRef('path'), ...)`, is supported beside `v-register`. The directive attaches its own listener in the `created` hook, before Vue applies yours, so it writes the field first and your handler reads committed state. That is the shape for a surface that saves each decision as it is made instead of on submit; see `references/saving.md`.
53
54
 
54
55
  ## Third-party components
55
56
 
56
- `v-register` binds a third-party component host, not just a native element, as long as the component renders a real form control and forwards attributes to it. The directive marks the host and injects the same binding, SSR, and ARIA it gives a native input.
57
+ `v-register` binds a third-party component host, not just a native element. The directive marks the host and injects the same binding, SSR, and ARIA it gives a native input.
58
+
59
+ When the component renders exactly one real form control and forwards attributes to it, that control becomes the field's anchor: focus and blur tracking, ARIA, and the invalid-submit focus target all land on it. A **composite** widget (a PIN input of several boxes) or a **control-less** one (a slider built from `<div>`s) binds too; the directive declines the single-element latch and tracks focus at the widget root instead. Do not reach for an escape hatch on account of either.
60
+
61
+ The one shape that loses the directive half is a component whose root is a **fragment**, because Vue hands a runtime directive only to a single-element root. The value channel still works there; wrap it in an element you control to get the rest back.
57
62
 
58
63
  ## Do not re-declare native attributes as props
59
64
 
@@ -17,7 +17,8 @@ const onSubmit = form.handleSubmit(async (values) => {
17
17
  })
18
18
  ```
19
19
 
20
- - `setErrors(errors | updater | (path, errors))` is a **whole-layer replace**, so `clearErrors()` at the top of a fresh attempt drops errors a previous submit set. `clearErrors(path?)` scopes the clear.
20
+ - `setErrors(errors | updater | (path, errors))` is a **whole-layer replace** of the user layer, so `clearErrors()` at the top of a fresh attempt drops errors a previous submit set. `clearErrors(path?)` scopes the clear.
21
+ - `clearErrors` clears the **schema layer too**, at whatever path it targets, not just the user one. Inside a submit callback that costs nothing, since validation has already passed to get there, and with always-on validation the schema half re-populates on the next mutation if the value is still wrong. Pass `setErrors([])` instead when you mean to empty the user layer alone.
21
22
  - `handleSubmit` focuses the first offending field for user-set errors exactly as it does for client-invalid fields, so a `setErrors` call inside the callback needs no separate focus step.
22
23
 
23
24
  ## The `ValidationError` envelope
@@ -27,7 +28,11 @@ If your backend emits Attaform's `ValidationError` shape, it pipes straight into
27
28
  ```ts
28
29
  import type { ValidationError, Json } from 'attaform'
29
30
 
30
- // { message: string; path: (string | number)[]; code?: string; data?: Json | null }
31
+ // ValidationError, what you READ BACK:
32
+ // { message: string; path: (string | number)[]; code: string; data?: Json | null }
33
+ // What setErrors ACCEPTS is looser: every field optional, plus a bare
34
+ // Error. A missing message becomes 'Unknown error', a missing path is
35
+ // [] (form level), a missing code is 'atta:user-error'.
31
36
  ```
32
37
 
33
38
  - One entry per message at `path: [field]`; a form-level error is `path: []`; a dotted key splits into segments.
@@ -55,7 +60,7 @@ A form-level banner wants the root's **own** bucket, not the aggregate:
55
60
  const banner = computed(() => form.meta.firstOwnError)
56
61
  ```
57
62
 
58
- Do not use `form.errors([])` for a banner: `errors(path)` uniformly means "path plus descendants", so `errors([])` is the whole-form aggregate and would surface individual field errors in the summary. `form.meta.firstOwnError` is the correct root-only read. The same own axis surfaces a container-level `.refine()` error, for example `form.fields.address.firstOwnError`.
63
+ Do not use `form.errors([])` for a banner: `errors(path)` uniformly means "path plus descendants", so `errors([])` is the whole-form aggregate and would surface individual field errors in the summary. `form.meta.firstOwnError` is the correct root-only read. The same own axis surfaces a container-level `.refine()` error, through the CALL form: `form.fields('address').firstOwnError`. Dot access on a container (`form.fields.address`) is navigation toward leaves and carries no error state of its own, so the dotted spelling reads `undefined` no matter what the container holds.
59
64
 
60
65
  ## One normalizer, always non-empty
61
66
 
@@ -0,0 +1,77 @@
1
+ # Saving as the user goes
2
+
3
+ A surface that persists each decision as it is made, instead of on a submit, is a supported shape. It needs no scaffolding around `v-register` and no redesign toward a batch Save button. Two shapes fit it, and the one to reach for follows what the reaction belongs to.
4
+
5
+ - The reaction belongs to the **interaction** (one pick is one whole decision: a `<select>`, a checkbox, a radio, a star rating) → a plain listener beside `v-register`.
6
+ - The reaction belongs to the **value** (typing, or anything that must also catch a programmatic write) → `watch(form.toRef(path), ...)`.
7
+
8
+ The deciding fact when a form both saves per change and hydrates itself from a saved record: a DOM listener never sees `form.setValue` or `form.reset()`, because neither fires a DOM event. Only the watch does.
9
+
10
+ ## The listener shape
11
+
12
+ ```vue
13
+ <script setup lang="ts">
14
+ import { z } from 'zod'
15
+ import { useForm } from 'attaform'
16
+
17
+ const schema = z.object({ choice: z.string() })
18
+ const form = useForm({ schema })
19
+
20
+ async function save() {
21
+ // Already the newly picked value: the directive wrote it first.
22
+ await fetch('/api/choice', {
23
+ method: 'PATCH',
24
+ body: JSON.stringify({ value: form.values.choice }),
25
+ })
26
+ }
27
+ </script>
28
+
29
+ <template>
30
+ <select v-register="form.register('choice')" @change="save">
31
+ <option value="yes">Yes</option>
32
+ <option value="no">No</option>
33
+ </select>
34
+ </template>
35
+ ```
36
+
37
+ `v-register` attaches its own listener in Vue's `created` hook, which runs before Vue applies the handlers you wrote. The directive's listener is therefore registered first, fires first, and has written the field by the time yours is called, so the handler reads committed state rather than the previous value. That ordering is a pinned contract, not an accident of timing, and it holds for `@change`, `@input`, a `v-register.lazy` control, a checkbox, and a radio.
38
+
39
+ An observing handler is **not** a second writer. Nothing is stacked, and neither the compile-time nor the runtime redundant-binding guard flags it. What stays forbidden is a second _writer_: a `v-model`, a handler that writes the field back, a reset-signal prop.
40
+
41
+ ## The watch shape
42
+
43
+ ```ts
44
+ import { watch } from 'vue'
45
+ import { z } from 'zod'
46
+ import { useForm } from 'attaform'
47
+
48
+ const schema = z.object({ bio: z.string() })
49
+ const form = useForm({ schema })
50
+
51
+ async function save(value: string): Promise<void> {
52
+ await fetch('/api/bio', { method: 'PATCH', body: JSON.stringify({ value }) })
53
+ }
54
+
55
+ // `form.toRef(path)` is a read-only ref over that path's value, so this
56
+ // fires on every committed change, including `setValue` and `reset`.
57
+ watch(form.toRef('bio'), (value) => {
58
+ void save(value)
59
+ })
60
+ ```
61
+
62
+ Registered inside `setup`, so the watchers stop on unmount with no manual teardown. Writing the same value back is a no-op in Attaform, so a watcher never fires for a write that changed nothing. For a whole container path, add `{ deep: true }`: an in-place leaf edit keeps the container's reference stable.
63
+
64
+ ## What a real one adds
65
+
66
+ The full recipe at https://attaform.dev/docs/cross-cutting-state/autosave is copy-paste and carries the parts a production autosave needs. Reproduce these rather than shipping the bare watch above:
67
+
68
+ - **Debounce per path**, because typing is continuous and a keystroke is not a decision. For a per-interaction surface the wait is pointless: keep the recipe and pass `{ debounceMs: 0 }`.
69
+ - **A validity gate.** Run `form.parse(path, { commit: true })` and skip the write when it fails, so an invalid value never reaches the server and async refinements run as part of the same check. Turn it off per path for a true draft save.
70
+ - **Abort on supersede.** One `AbortController` per path, handed to `fetch` as its `signal`, so a slow request cancels when a newer edit lands and the latest write wins.
71
+ - **A pause for your own writes.** This is the one that bites. A watch reacts to every change including yours, so hydrating ten fields from a saved record echoes ten saves straight back at the server you just loaded from. Wrap a hydrating `setValue` or `reset` so the watchers skip it. The pause belongs to your composable, never to the form.
72
+
73
+ ## Do not trade away the rendering to get the saving
74
+
75
+ Both shapes leave `v-register` in place, so the control keeps its binding, its ARIA, and its SSR value injection. That last one is worth naming, because a row of server-rendered `<select>` elements is where it shows: a selection is a DOM property rather than markup, so the server can only express it as `selected` on the right `<option>`, and hand-binding that per call site is a step every new row can forget. `v-register` emits it for every option it owns.
76
+
77
+ Saving per decision never costs you that. Guidance that implies otherwise, and sends an author to a batch submit to keep their server-rendered state correct, is wrong.
@@ -12,6 +12,8 @@ A step slot is one of:
12
12
  - a **function** returning any of those, for runtime branching,
13
13
  - a `lazy((ctx) => ...)`-wrapped function, which memoizes its resolution and re-fires only when its own tracked reads change.
14
14
 
15
+ `gate(slot)` wraps any of them rather than being a kind of its own: it marks that position a hard prerequisite and leaves how the slot resolves alone. A function slot may return one, so whether a position is a gate can itself be a runtime decision.
16
+
15
17
  ```ts
16
18
  import { useForm, useWizard } from 'attaform'
17
19
 
@@ -24,7 +26,7 @@ const wizard = useWizard({ key: 'onboarding', steps: [account, 'review', profile
24
26
  ## Navigation vs submission
25
27
 
26
28
  - **`wizard.tryNext(): Promise<boolean>`** is the gated Next. It validates the active step and advances only on a clean pass, revealing that step's errors in place otherwise. It resolves to whether it advanced, and it is inline-bindable: `@click="wizard.tryNext()"`.
27
- - **`wizard.next()` / `wizard.back()` / `wizard.goTo(key)`** are positional moves with no validation gate. Use them for a Back button or a jump; use `tryNext` for a forward move that should validate.
29
+ - **`wizard.next()` / `wizard.back()` / `wizard.goTo(key)`** are positional moves with no validation gate. Use them for a Back button or a jump; use `tryNext` for a forward move that should validate. Two exceptions: they refuse while a submit is in flight, and `next()` on an **uncleared `gate()` step behaves as `tryNext()`**, so wiring Next straight to it can never skip the gate's confirmation. Once that gate clears, `next()` is plain navigation again and does not re-submit.
28
30
  - **`wizard.handleSubmit(onSubmit, onError?)`** validates **every** step from any position and calls `onSubmit` once with all forms' values. It **never advances**. Wire it to the final Submit.
29
31
 
30
32
  For a forward move that runs a custom callback before advancing, compose the step form's own submit with `next`:
@@ -58,9 +60,9 @@ const onComplete = wizard.handleSubmit(async (ctx) => {
58
60
 
59
61
  The wizard handle exposes the flow's rolled-up state, all reactive:
60
62
 
61
- - `wizard.forms[key]`: the live form handle for a step (a facade typed as a form).
63
+ - `wizard.forms[key]`: the step's own form handle, identity-equal to the `useForm` ref that was slotted in.
62
64
  - `wizard.allValues` / `wizard.allErrors`: every form's values / aggregate errors, keyed.
63
- - `wizard.activeForm`: the currently active step's form facade.
65
+ - `wizard.activeForm`: a live facade over whichever step is active, so a handler captured once retargets as the pin moves. Not the handle itself: use `wizard.forms[key]` for that.
64
66
  - `wizard.statuses`: per-form status. Plus `wizard.progress`, `wizard.canAdvance`, `wizard.canGoBack`, `wizard.isFinalStep`, `wizard.visited`.
65
67
 
66
68
  ## A declarative step registry
@@ -94,11 +96,11 @@ const wizard = useWizard({
94
96
 
95
97
  Everything derives from the registry: the step slots, the titles, any persisted key list. Add, remove, reorder, or gate a step by editing the registry alone. Do not add speculative registry fields with no consumer; add a hook when the second consumer arrives.
96
98
 
97
- ## Keyed injection resolves by tree and timing
99
+ ## Keyed injection resolves by name and timing
98
100
 
99
- A string key passed to `injectForm('account')` or `injectWizard('onboarding')` _reads_ like a global address but resolves by the **caller's component-tree position and mount timing**, not as a global lookup:
101
+ A string key passed to `injectForm('account')` or `injectWizard('onboarding')` is a name in Attaform's app-level registry, not a location in the component tree:
100
102
 
101
- - `provide` / `inject` is descendant-only, so a form created in a **leaf** is unreachable from an **ancestor**.
102
- - A keyed form is not resolvable until the creating component's `setup` has run, so mount order matters.
103
+ - **Position-independent.** The lookup reaches any component in the app: a sibling branch, a floating toolbar, a teleported modal. Extracting a wrapper or changing how deep a component sits never changes what a key resolves to. The no-key ambient form is the opposite, and is the one that runs on `provide` / `inject`: it reaches descendants of the owning `useForm` only.
104
+ - **Time-dependent.** The registry entry appears when the owner's own `useForm({ key })` / `useWizard({ key })` setup runs, and not before.
103
105
 
104
- The consequence: a purely structural refactor can silently break a lookup. The stable shape is to **lift a shared form's creation to the coordinating ancestor** (the component that owns the wizard) and have leaves `injectForm(key)` it downward by a prop key. Always chain `?.` on the result, which is `T | null`.
106
+ Together they give one rule: **resolve downward.** Vue runs a parent's setup before its children's, so a descendant reaching an ancestor's keyed form always resolves, while an ancestor reaching a descendant's gets `null`. The stable shape is to **lift a shared form's creation to the coordinating ancestor** (the component that owns the wizard) and reach for it at or below that point. Always chain `?.` on the result, which is `T | null`.