attaform 0.27.5 → 0.28.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 (184) hide show
  1. package/dist/abstract.d.mts +5 -4
  2. package/dist/abstract.mjs +4 -3
  3. package/dist/chunks/fingerprint.mjs +0 -1
  4. package/dist/chunks/fingerprint2.mjs +1 -2
  5. package/dist/dev/abstract.mjs +4 -0
  6. package/dist/{chunks → dev/chunks}/dev-key-collision-warnings.mjs +0 -1
  7. package/dist/{chunks → dev/chunks}/devtools.mjs +5 -6
  8. package/dist/{chunks/fingerprint.cjs → dev/chunks/fingerprint.mjs} +7 -10
  9. package/dist/{chunks/fingerprint2.cjs → dev/chunks/fingerprint2.mjs} +32 -35
  10. package/dist/dev/directive.mjs +1 -0
  11. package/dist/dev/history.mjs +89 -0
  12. package/dist/dev/index.mjs +5 -0
  13. package/dist/dev/runtime/plugins/attaform.mjs +40 -0
  14. package/dist/{shared/attaform.f2zyNh5d.cjs → dev/shared/attaform-dev.6qxD-Yqo.mjs} +187 -43
  15. package/dist/{shared/attaform.CQN9R62B.cjs → dev/shared/attaform-dev.BJGA_UOS.mjs} +1 -4
  16. package/dist/{shared/attaform.Dc1mx48r.mjs → dev/shared/attaform-dev.BR8u0YOZ.mjs} +6 -7
  17. package/dist/dev/shared/attaform-dev.BXE2cr0L.mjs +220 -0
  18. package/dist/dev/shared/attaform-dev.By4PRjt0.mjs +248 -0
  19. package/dist/{shared/attaform.Cg_8gFQ7.mjs → dev/shared/attaform-dev.D2I8e_Mm.mjs} +132 -568
  20. package/dist/dev/shared/attaform-dev.Dr2YzS5B.mjs +197 -0
  21. package/dist/{shared/attaform.nMaz2C5P.mjs → dev/shared/attaform-dev.P7cseiEG.mjs} +178 -397
  22. package/dist/{shared/attaform.BypWgXRB.mjs → dev/shared/attaform-dev.WmuhD_CZ.mjs} +3955 -5029
  23. package/dist/{shared/attaform.Dkjh_F30.mjs → dev/shared/attaform-dev.oYrkP9dj.mjs} +160 -508
  24. package/dist/dev/zod-v3.mjs +17 -0
  25. package/dist/dev/zod-v4.mjs +16 -0
  26. package/dist/dev/zod.mjs +5 -0
  27. package/dist/directive.d.mts +20 -0
  28. package/dist/directive.mjs +1 -0
  29. package/dist/esbuild.mjs +0 -1
  30. package/dist/history.d.mts +59 -0
  31. package/dist/history.mjs +89 -0
  32. package/dist/index.d.mts +11 -5
  33. package/dist/index.mjs +5 -4
  34. package/dist/nuxt.d.mts +1 -1
  35. package/dist/nuxt.mjs +5 -2
  36. package/dist/rollup.mjs +0 -1
  37. package/dist/rspack.d.mts +6 -1
  38. package/dist/rspack.mjs +0 -1
  39. package/dist/runtime/components/AttaformDevtoolsPanel.vue +9 -8
  40. package/dist/runtime/plugins/attaform.mjs +1 -2
  41. package/dist/shared/{attaform.CIBMfxg9.d.ts → attaform.B4VkS43-.d.mts} +2 -2
  42. package/dist/shared/{attaform.Cg1rUfcF.cjs → attaform.B8k8JrmK.mjs} +3778 -5297
  43. package/dist/shared/attaform.BJGA_UOS.mjs +0 -1
  44. package/dist/shared/{attaform.CTiZ9Rrv.mjs → attaform.C09fpdLS.mjs} +172 -16
  45. package/dist/shared/{attaform.Bh8A6YAc.cjs → attaform.CJP3RkGa.mjs} +141 -590
  46. package/dist/shared/{attaform.CzL2CZ8U.d.ts → attaform.Cgu-hdSb.d.mts} +328 -78
  47. package/dist/shared/attaform.Cs2Rbdlo.mjs +0 -1
  48. package/dist/shared/{attaform.DcFDQVpa.d.ts → attaform.D5V4tZWq.d.mts} +79 -179
  49. package/dist/shared/{attaform.CF-EnyJn.cjs → attaform.D6DCnnQu.mjs} +11 -16
  50. package/dist/shared/attaform.DhnPb6nK.mjs +0 -1
  51. package/dist/shared/attaform.Dq4vWxuG.mjs +197 -0
  52. package/dist/shared/attaform.JfSWdEcb.d.mts +44 -0
  53. package/dist/shared/attaform.KvlEedlF.mjs +0 -1
  54. package/dist/shared/{attaform.Crv0RNiz.cjs → attaform.NLmerepI.mjs} +175 -557
  55. package/dist/shared/attaform.h0i6LijT.mjs +242 -0
  56. package/dist/shared/{attaform.Biytrv0-.cjs → attaform.uLbun31C.mjs} +204 -544
  57. package/dist/shared/attaform.v0fQtWyA.mjs +0 -1
  58. package/dist/shared/attaform.wfY8XFvm.mjs +197 -0
  59. package/dist/transforms.mjs +0 -1
  60. package/dist/vite.d.mts +17 -5
  61. package/dist/vite.mjs +24 -2
  62. package/dist/webpack.d.mts +6 -1
  63. package/dist/webpack.mjs +0 -1
  64. package/dist/zod-v3.d.mts +11 -6
  65. package/dist/zod-v3.mjs +17 -4
  66. package/dist/zod-v4.d.mts +12 -6
  67. package/dist/zod-v4.mjs +16 -4
  68. package/dist/zod.d.mts +4 -3
  69. package/dist/zod.mjs +5 -4
  70. package/package.json +23 -8
  71. package/skills/attaform/SKILL.md +2 -1
  72. package/dist/abstract.cjs +0 -48
  73. package/dist/abstract.cjs.map +0 -1
  74. package/dist/abstract.d.cts +0 -42
  75. package/dist/abstract.d.ts +0 -42
  76. package/dist/abstract.mjs.map +0 -1
  77. package/dist/chunks/dev-key-collision-warnings.cjs +0 -25
  78. package/dist/chunks/dev-key-collision-warnings.cjs.map +0 -1
  79. package/dist/chunks/dev-key-collision-warnings.mjs.map +0 -1
  80. package/dist/chunks/devtools.cjs +0 -157
  81. package/dist/chunks/devtools.cjs.map +0 -1
  82. package/dist/chunks/devtools.mjs.map +0 -1
  83. package/dist/chunks/fingerprint.cjs.map +0 -1
  84. package/dist/chunks/fingerprint.mjs.map +0 -1
  85. package/dist/chunks/fingerprint2.cjs.map +0 -1
  86. package/dist/chunks/fingerprint2.mjs.map +0 -1
  87. package/dist/esbuild.cjs +0 -28
  88. package/dist/esbuild.cjs.map +0 -1
  89. package/dist/esbuild.d.cts +0 -56
  90. package/dist/esbuild.d.ts +0 -56
  91. package/dist/esbuild.mjs.map +0 -1
  92. package/dist/index.cjs +0 -51
  93. package/dist/index.cjs.map +0 -1
  94. package/dist/index.d.cts +0 -249
  95. package/dist/index.d.ts +0 -249
  96. package/dist/index.mjs.map +0 -1
  97. package/dist/nuxt.cjs +0 -84
  98. package/dist/nuxt.cjs.map +0 -1
  99. package/dist/nuxt.d.cts +0 -66
  100. package/dist/nuxt.d.ts +0 -66
  101. package/dist/nuxt.mjs.map +0 -1
  102. package/dist/rollup.cjs +0 -24
  103. package/dist/rollup.cjs.map +0 -1
  104. package/dist/rollup.d.cts +0 -35
  105. package/dist/rollup.d.ts +0 -35
  106. package/dist/rollup.mjs.map +0 -1
  107. package/dist/rspack.cjs +0 -10
  108. package/dist/rspack.cjs.map +0 -1
  109. package/dist/rspack.d.cts +0 -40
  110. package/dist/rspack.d.ts +0 -40
  111. package/dist/rspack.mjs.map +0 -1
  112. package/dist/runtime/components/AttaformDevtoolsPanel.vue.d.ts +0 -7
  113. package/dist/runtime/components/DevtoolsValueTree.vue.d.ts +0 -35
  114. package/dist/runtime/plugins/attaform.cjs +0 -43
  115. package/dist/runtime/plugins/attaform.cjs.map +0 -1
  116. package/dist/runtime/plugins/attaform.d.cts +0 -5
  117. package/dist/runtime/plugins/attaform.d.ts +0 -5
  118. package/dist/runtime/plugins/attaform.mjs.map +0 -1
  119. package/dist/shared/attaform.B3abZaCV.d.cts +0 -2484
  120. package/dist/shared/attaform.BJGA_UOS.mjs.map +0 -1
  121. package/dist/shared/attaform.Bh8A6YAc.cjs.map +0 -1
  122. package/dist/shared/attaform.Biytrv0-.cjs.map +0 -1
  123. package/dist/shared/attaform.Bn0IzhD_.cjs +0 -800
  124. package/dist/shared/attaform.Bn0IzhD_.cjs.map +0 -1
  125. package/dist/shared/attaform.BypWgXRB.mjs.map +0 -1
  126. package/dist/shared/attaform.C9fPTxjB.d.cts +0 -41
  127. package/dist/shared/attaform.C9fPTxjB.d.ts +0 -41
  128. package/dist/shared/attaform.CF-EnyJn.cjs.map +0 -1
  129. package/dist/shared/attaform.CJ1Z9Mz4.d.mts +0 -2484
  130. package/dist/shared/attaform.CQN9R62B.cjs.map +0 -1
  131. package/dist/shared/attaform.CTiZ9Rrv.mjs.map +0 -1
  132. package/dist/shared/attaform.Cg1rUfcF.cjs.map +0 -1
  133. package/dist/shared/attaform.Cg_8gFQ7.mjs.map +0 -1
  134. package/dist/shared/attaform.Crv0RNiz.cjs.map +0 -1
  135. package/dist/shared/attaform.Cs2Rbdlo.mjs.map +0 -1
  136. package/dist/shared/attaform.CzL2CZ8U.d.cts +0 -4713
  137. package/dist/shared/attaform.CzL2CZ8U.d.mts +0 -4713
  138. package/dist/shared/attaform.DRUT703x.d.cts +0 -56
  139. package/dist/shared/attaform.DRUT703x.d.ts +0 -56
  140. package/dist/shared/attaform.DYwyM5Cp.d.cts +0 -232
  141. package/dist/shared/attaform.Dc1mx48r.mjs.map +0 -1
  142. package/dist/shared/attaform.DhnPb6nK.mjs.map +0 -1
  143. package/dist/shared/attaform.Dkjh_F30.mjs.map +0 -1
  144. package/dist/shared/attaform.DoQm_Vkd.cjs +0 -58
  145. package/dist/shared/attaform.DoQm_Vkd.cjs.map +0 -1
  146. package/dist/shared/attaform.DrHOqB-d.d.mts +0 -232
  147. package/dist/shared/attaform.KvlEedlF.mjs.map +0 -1
  148. package/dist/shared/attaform.RaTzp1WF.cjs +0 -26
  149. package/dist/shared/attaform.RaTzp1WF.cjs.map +0 -1
  150. package/dist/shared/attaform.f2zyNh5d.cjs.map +0 -1
  151. package/dist/shared/attaform.nMaz2C5P.mjs.map +0 -1
  152. package/dist/shared/attaform.v0fQtWyA.mjs.map +0 -1
  153. package/dist/shared/attaform.yUrz7C2z.cjs +0 -29
  154. package/dist/shared/attaform.yUrz7C2z.cjs.map +0 -1
  155. package/dist/transforms.cjs +0 -12
  156. package/dist/transforms.cjs.map +0 -1
  157. package/dist/transforms.d.cts +0 -73
  158. package/dist/transforms.d.ts +0 -73
  159. package/dist/transforms.mjs.map +0 -1
  160. package/dist/vite.cjs +0 -363
  161. package/dist/vite.cjs.map +0 -1
  162. package/dist/vite.d.cts +0 -133
  163. package/dist/vite.d.ts +0 -133
  164. package/dist/vite.mjs.map +0 -1
  165. package/dist/webpack.cjs +0 -10
  166. package/dist/webpack.cjs.map +0 -1
  167. package/dist/webpack.d.cts +0 -37
  168. package/dist/webpack.d.ts +0 -37
  169. package/dist/webpack.mjs.map +0 -1
  170. package/dist/zod-v3.cjs +0 -53
  171. package/dist/zod-v3.cjs.map +0 -1
  172. package/dist/zod-v3.d.cts +0 -246
  173. package/dist/zod-v3.d.ts +0 -246
  174. package/dist/zod-v3.mjs.map +0 -1
  175. package/dist/zod-v4.cjs +0 -55
  176. package/dist/zod-v4.cjs.map +0 -1
  177. package/dist/zod-v4.d.cts +0 -172
  178. package/dist/zod-v4.d.ts +0 -172
  179. package/dist/zod-v4.mjs.map +0 -1
  180. package/dist/zod.cjs +0 -51
  181. package/dist/zod.cjs.map +0 -1
  182. package/dist/zod.d.cts +0 -7
  183. package/dist/zod.d.ts +0 -7
  184. package/dist/zod.mjs.map +0 -1
@@ -1,5 +1,5 @@
1
- import { G as GenericForm, F as FormKey, a as UseFormReturnType, a4 as RegisterValue, T as PathKey, k as DisplayCtx, w as GetDisplayState, l as DisplayMachine, S as Path, a9 as SlimPrimitiveKind, C as CoercionEntry, e as CoercionRegistry, af as ValidationError, A as AbstractSchema, aj as WriteMeta, h as DeepPartial, ak as WriteShape, as as TransformAbortHolder, ad as ValidateOn, d as AttaformDefaults, at as RegisterModelDynamicCustomDirective } from './attaform.CzL2CZ8U.js';
2
- import { Ref, ComputedRef, App, InjectionKey, Plugin } from 'vue';
1
+ import { G as GenericForm, F as FormKey, a as UseFormReturnType, a5 as RegisterValue, V as PathKey, k as DisplayCtx, w as GetDisplayState, l as DisplayMachine, aa as SlimPrimitiveKind, C as CoercionEntry, e as CoercionRegistry, au as FieldRecord, av as ErrorCell, ag as ValidationError, aw as OriginalsRecord, A as AbstractSchema, T as Path, ak as WriteMeta, h as DeepPartial, al as WriteShape, ax as AttaformDomBinding, ay as TransformAbortHolder, ae as ValidateOn, d as AttaformDefaults } from './attaform.Cgu-hdSb.mjs';
2
+ import { Ref, ComputedRef, ShallowRef, App, InjectionKey, Plugin } from 'vue';
3
3
 
4
4
  /**
5
5
  * Options accepted by `injectForm` when passing an object instead of
@@ -206,71 +206,12 @@ type DisplayEngine = {
206
206
  clear(): void;
207
207
  /** Tear down for good: `clear()` plus detaching the visibility listener. */
208
208
  dispose(): void;
209
- /** Introspection for tests: count of retained machines. */
210
- size(): number;
211
- /** Introspection for tests: whether a path currently has a retained machine. */
212
- has(key: PathKey): boolean;
213
- /** Introspection for tests: whether a deadline timer is currently armed. */
214
- hasTimer(): boolean;
215
- };
216
-
217
- /**
218
- * Per-path field status. Replaced wholesale (not mutated in place) on
219
- * every change. Five semantic groups:
220
- *
221
- * - `connected` — is a DOM element registered for this path?
222
- * - `focused` / `blurred` — DOM-state flags. `null` while no element
223
- * is connected (no DOM means the concepts don't apply); plain
224
- * booleans once connected, with the invariant `blurred === !focused`
225
- * enforced by `markFocused`.
226
- * - `touched` — focus/blur history, not DOM state. Always a plain
227
- * boolean: `false` at registration, sticky `true` after first blur,
228
- * cleared only by `form.reset()` / `form.resetField(path)`. Persists
229
- * across disconnects so v-if'd-away fields don't lose their touched
230
- * state on rehide (wizard "show review of touched fields" patterns
231
- * rely on this).
232
- * - `interacted` — value-mutation history, not DOM state. Plain
233
- * boolean: `false` at registration, sticky `true` once the user
234
- * issues a value edit through the directive's input listeners
235
- * (never on hydration, default seeding, or programmatic setValue);
236
- * cleared with `touched` by `form.reset()` / `form.resetField(path)`.
237
- * - `blurredAfterInteraction` — the first blur that follows a value
238
- * edit (the field has been edited and then left). Plain boolean,
239
- * sticky `true`. A tab-through blur with no prior edit does NOT set
240
- * it (`interacted` is still false at that blur). Composes
241
- * `interacted` with the departure; drives the default display gate.
242
- */
243
- type FieldRecord = {
244
- readonly path: Path;
245
- readonly updatedAt: string | null;
246
- readonly connected: boolean;
247
- readonly focused: boolean | null;
248
- readonly blurred: boolean | null;
249
- readonly touched: boolean;
250
- readonly interacted: boolean;
251
- readonly blurredAfterInteraction: boolean;
252
- };
253
- /** Per-path DOM element tracking. Client-only. */
254
- type ElementRecord = {
255
- /**
256
- * Original Path captured at first registration. Stored alongside the
257
- * elements Set so the DOM-order sort cache can recover the structured
258
- * Path without round-tripping through `JSON.parse(pathKey)`.
259
- */
260
- readonly path: Path;
261
- readonly elements: Set<HTMLElement>;
262
- };
263
- /**
264
- * Per-path record stored in `originals`. Pairing `segments` with the tracked
265
- * value means `dirty` and `resetField`'s container loop don't have to
266
- * `JSON.parse(pathKey)` on every iteration — the canonical Path is already
267
- * sitting next to the value it belongs to. PathKey still keys the Map (the
268
- * stable string is the only collision-free identifier), but downstream
269
- * iteration reads `segments` directly.
270
- */
271
- type OriginalsRecord = {
272
- readonly segments: Path;
273
- readonly value: unknown;
209
+ /** Introspection for tests: count of retained machines. Dev builds only. */
210
+ size?(): number;
211
+ /** Introspection for tests: whether a path has a retained machine. Dev builds only. */
212
+ has?(key: PathKey): boolean;
213
+ /** Introspection for tests: whether a deadline timer is armed. Dev builds only. */
214
+ hasTimer?(): boolean;
274
215
  };
275
216
 
276
217
  /**
@@ -327,20 +268,21 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
327
268
  readonly formKey: FormKey;
328
269
  readonly form: Ref<F>;
329
270
  readonly fields: Map<PathKey, FieldRecord>;
330
- readonly elements: Map<PathKey, ElementRecord>;
331
- /**
332
- * Schema-driven errors. Written ONLY by the schema validation pipeline:
333
- * `scheduleFieldValidation`, `handleSubmit`, the construction-time seed,
334
- * history restore, and hydration. Cleared by `reset` / `resetField` and by
335
- * a successful submit. `setErrors` / `clearErrors` do NOT touch this Map.
336
- */
337
- readonly schemaErrors: Map<PathKey, ValidationError[]>;
338
271
  /**
339
- * User-injected errors. Written ONLY by the `setErrors` / `clearErrors`
340
- * API surface (and history / hydration replay). Survives schema revalidation and
272
+ * The tagged error store: one cell per error-bearing path, each cell
273
+ * segregating its two sources. The `schema` side is written ONLY by
274
+ * the validation pipeline (`scheduleFieldValidation`, `handleSubmit`,
275
+ * the construction-time seed, history restore, hydration) and cleared
276
+ * by `reset` / `resetField` and a successful submit; the `user` side
277
+ * is written ONLY by the `setErrors` / `clearErrors` API surface (and
278
+ * history / hydration replay) and survives schema revalidation and
341
279
  * successful submits — the consumer owns its lifetime explicitly.
280
+ * A key exists iff a side is non-empty; cells are replaced, never
281
+ * mutated, so Vue's per-key Map tracking fires for either side's
282
+ * change. Derived blank entries are NOT stored here — they synthesize
283
+ * at read (`derivedBlankErrors`).
342
284
  */
343
- readonly userErrors: Map<PathKey, ValidationError[]>;
285
+ readonly errorCells: Map<PathKey, ErrorCell>;
344
286
  /**
345
287
  * Reactively-derived "No value supplied" errors. Pure function of
346
288
  * `(blankPaths, schema.isRequiredAtPath)` — no writers, no clears.
@@ -538,7 +480,7 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
538
480
  readonly submissionGeneration: Ref<number>;
539
481
  /**
540
482
  * Counts in-flight validation calls across every `validate()` ref and
541
- * every `validateAsync(...)` / `handleSubmit` pre-check. `validating`
483
+ * every committing `parse(...)` / `handleSubmit` pre-check. `validating`
542
484
  * on the public API mirrors `activeValidations.value > 0`. Tracked
543
485
  * separately from submissions because a validate-while-submitting
544
486
  * (e.g. a debounced field check overlapping a submit) needs to show
@@ -598,7 +540,7 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
598
540
  * call) and decremented in the matching `.finally` — so the per-path
599
541
  * bookkeeping is exactly co-extensive with the form-wide counter for
600
542
  * the field-scheduled branch. Whole-form `validate()` /
601
- * `validateAsync()` runs touch `activeValidations` only; they don't
543
+ * `parse()` runs touch `activeValidations` only; they don't
602
544
  * have a single field path and so don't contribute here.
603
545
  *
604
546
  * Counter (not Set) because two runs for the same path can briefly
@@ -687,7 +629,7 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
687
629
  /**
688
630
  * Stable identity for the array element at `path`. An array element
689
631
  * (numeric last segment) carries its allocated identity token,
690
- * maintained by `array-identity.ts` across structural mutations.
632
+ * maintained by the arrays engine across structural mutations.
691
633
  * Empty for any non-array-element path: a record entry, a
692
634
  * fixed-object field, a container, or the root. Backs `FieldState.key`.
693
635
  */
@@ -700,7 +642,7 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
700
642
  /**
701
643
  * Replace `schemaErrors` under `path` with `errors`, keying each
702
644
  * error by its OWN absolute path. Used by validation pipelines
703
- * (scheduleFieldValidation, validateAsync, handleSubmit, reset)
645
+ * (scheduleFieldValidation, the committing parse, handleSubmit, reset)
704
646
  * to commit a parse result wholesale — entries not in the new
705
647
  * pass get dropped from the subtree, surviving keys update in
706
648
  * place to preserve insertion order. Pass `path === []` for the
@@ -711,10 +653,18 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
711
653
  setUserErrorsForPath(path: Path, errors: readonly ValidationError[]): void;
712
654
  clearUserErrors(path?: Path): void;
713
655
  /**
714
- * Merged read — returns `[...schemaErrors[path], ...userErrors[path]]`.
715
- * Schema errors come first (structural validation before business logic),
716
- * matching the iteration order for `getFirstErrorElement` and the
717
- * top-level `errors` drillable Proxy.
656
+ * Rebuild the whole tagged store from a snapshot's `[key, cell]`
657
+ * entries — the history ring buffer's restore road. Entry arrays are
658
+ * cloned in, so the snapshot stays detached from the live store;
659
+ * cells with both sides empty are skipped.
660
+ */
661
+ restoreErrorCells(entries: ReadonlyArray<readonly [PathKey, ErrorCell]>): void;
662
+ /**
663
+ * Merged read — the cell at `path` in schema -> blank -> user order.
664
+ * Schema errors come first (structural validation before business
665
+ * logic), the derived blank entry synthesizes between, and user
666
+ * entries close, matching the iteration order for
667
+ * `getFirstErrorElement` and the top-level `errors` drillable Proxy.
718
668
  */
719
669
  getErrorsForPath(path: Path): ValidationError[];
720
670
  /**
@@ -729,15 +679,29 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
729
679
  */
730
680
  ensurePathOrdinal(key: PathKey): number;
731
681
  /**
732
- * Register `element` as a binding for `path`, tagged with the calling
733
- * `useForm()` instance's `formInstanceId`. The ID is the disambiguator
734
- * used by `getFirstErrorElement` to scope focus / scroll to elements
735
- * THIS form instance owns — important when two `useForm()` calls share
736
- * a `key` (e.g. sidebar + main rendering the same form), since both
737
- * write into one shared element store.
682
+ * The store's DOM slice, `null` until the directive cluster or
683
+ * `useRegister` arms it via `RegisterValue.ensureDomBinding`. Element
684
+ * registration, host anchors, and first-error focus resolution all
685
+ * live behind it; eager readers treat `null` as an empty registry.
686
+ */
687
+ readonly domBinding: ShallowRef<AttaformDomBinding | null>;
688
+ /**
689
+ * Field-record connect transition, driven by the DOM binding on
690
+ * element attach / host connect (and by the SSR-only
691
+ * `markConnectedOptimistically`): `connected: true`, with
692
+ * `focused` / `blurred` lifted from `null` to optimistic booleans
693
+ * only when currently null — an existing boolean from an early focus
694
+ * event is never clobbered.
695
+ */
696
+ noteDomConnected(path: Path): void;
697
+ /**
698
+ * Field-record disconnect transition, driven by the DOM binding when
699
+ * a path's last element detaches or its host disconnects:
700
+ * `connected: false`, `focused` / `blurred` back to `null` (DOM-state
701
+ * properties are meaningless with nothing attached; interaction
702
+ * history stays).
738
703
  */
739
- registerElement(path: Path, element: HTMLElement, formInstanceId: string): boolean;
740
- deregisterElement(path: Path, element: HTMLElement): number;
704
+ noteDomDisconnected(path: Path): void;
741
705
  /**
742
706
  * Optional `meta.instance` carries per-`useForm()`-instance overrides
743
707
  * for `validateOn` / `debounceMs` so the blur-trigger respects the
@@ -758,6 +722,16 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
758
722
  * does not mutate value / focused / blurred or trigger validation.
759
723
  */
760
724
  touchAtPath(segments: Path): void;
725
+ /**
726
+ * Walk every active-variant leaf under `segments` and flip the full
727
+ * interaction ladder (`touched` / `interacted` /
728
+ * `blurredAfterInteraction`), as though the user had focused,
729
+ * edited, and left each one. Powers `form.interact(path?)`.
730
+ * Idempotent; does not mutate value / focused / blurred. Returns
731
+ * whether any leaf resolved, so the caller can skip validation and
732
+ * dev-warn on an empty path.
733
+ */
734
+ interactAtPath(segments: Path): boolean;
761
735
  /**
762
736
  * SSR-only optimistic mark: flip `connected: true` on the field
763
737
  * record without an actual DOM element. Called by the `vRegisterHint`
@@ -767,20 +741,6 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
767
741
  * source there).
768
742
  */
769
743
  markConnectedOptimistically(path: Path): void;
770
- /**
771
- * Client-side connected mark for a `v-register` component host that binds
772
- * value via the v-model channel but has no single inner control to latch
773
- * (a composite widget, or none found). The directive calls it on mount
774
- * (`true`) and unmount (`false`). Distinct from the SSR-only
775
- * `markConnectedOptimistically`; idempotent on the leading edge.
776
- *
777
- * `hostEl` (the host root) is recorded on connect as this path's
778
- * focus-first-error anchor, scoped to `formInstanceId` the same way a
779
- * registered control is. A no-latch host enters no `elements` record, so
780
- * without this it is absent from `getFirstErrorElement`'s walk; the anchor
781
- * lets that walk resolve a focus target from the host subtree.
782
- */
783
- markHostConnected(path: Path, connected: boolean, hostEl: HTMLElement, formInstanceId: string): void;
784
744
  /**
785
745
  * Leaf-only pristine check. `originals` is populated via
786
746
  * `diffAndApply`'s `added` patches, which fire only on primitive
@@ -819,33 +779,6 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
819
779
  hasRemovedSubtreeUnder(path: Path): boolean;
820
780
  getFieldRecord(path: Path): FieldRecord | undefined;
821
781
  getOriginalAtPath(path: Path): unknown;
822
- /**
823
- * Returns the first errored field's first connected, visible DOM
824
- * element scoped to `formInstanceId` — the target that
825
- * `focusFirstError` / `scrollToFirstError` act on. "First" is
826
- * VISUAL-first (DOM-tree order via `compareDocumentPosition`), not
827
- * schema-declaration order, so a field rendered above another in the
828
- * template focuses first regardless of which one the schema declared
829
- * earlier. CSS `order:` flexbox/grid reordering is NOT respected
830
- * (DOM-tree order wins) — documented as a tradeoff against forcing
831
- * sync layout on every comparison.
832
- *
833
- * The `formInstanceId` filter scopes focus to elements registered
834
- * through THIS form instance. When two `useForm({ key })` calls share
835
- * a key, both register into the same element store; without the
836
- * filter, the sidebar form's submit could focus the main form's
837
- * input. With it, each `useForm()` callsite focuses only its own
838
- * elements.
839
- *
840
- * Returns `null` when every errored path has no currently-attached
841
- * element registered to this instance (fields behind `v-if="false"`,
842
- * unmounted components, or a hidden `display:none` parent). Callers
843
- * get the choice of no-op or a dev-only warning.
844
- */
845
- getFirstErrorElement(formInstanceId: string): {
846
- path: Path;
847
- element: HTMLElement;
848
- } | null;
849
782
  /**
850
783
  * Cancel every in-flight field-level validation run — clears timers
851
784
  * for debounced 'change' runs that haven't fired, latches `aborted`
@@ -948,21 +881,7 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
948
881
  */
949
882
  registerCleanup(fn: () => void): void;
950
883
  /**
951
- * Register an async drain function. Called by the registry before
952
- * `dispose()` so async background work — chiefly the persistence
953
- * layer's debounced storage writes — has a chance to settle without
954
- * losing the last keystroke. Each registered function is awaited in
955
- * parallel; failures are swallowed to keep eviction reliable.
956
- */
957
- registerDrain(fn: () => Promise<void>): void;
958
- /**
959
- * Drain async work registered via `registerDrain`. Resolves once
960
- * every registered drain has settled (in parallel). Safe to call
961
- * repeatedly — registered drains decide their own idempotency.
962
- */
963
- awaitPendingWrites(): Promise<void>;
964
- /**
965
- * Cache for per-state modules (history, persistence) that must
884
+ * Cache for per-state modules (chiefly history) that must
966
885
  * outlive any single consumer. Subsequent `useForm` / `injectForm`
967
886
  * calls for the same key read from this map so the public API shape
968
887
  * is identical regardless of mount order. Keyed by a string identifier
@@ -1773,13 +1692,6 @@ type AttaformRegistry = {
1773
1692
  * @internal
1774
1693
  */
1775
1694
  readonly shouldPrefetch: (key: FormKey) => boolean;
1776
- /**
1777
- * Wait for all pending persistence writes across every live form
1778
- * to settle. Useful for SSR shutdown and integration tests that
1779
- * need a deterministic teardown.
1780
- * @internal
1781
- */
1782
- readonly shutdown: () => Promise<void>;
1783
1695
  };
1784
1696
  /**
1785
1697
  * The Vue `InjectionKey` under which the registry is provided on the
@@ -2157,6 +2069,8 @@ declare function createAttaform(options?: AttaformPluginOptions): Plugin;
2157
2069
  * serialiser before embedding in your SSR payload.
2158
2070
  */
2159
2071
  type SerializedAttaformState = {
2072
+ /** Envelope version — see {@link ATTAFORM_STATE_VERSION}. */
2073
+ readonly v: number;
2160
2074
  /** Tuples of `[formKey, snapshot]` for every form in the app. */
2161
2075
  readonly forms: ReadonlyArray<readonly [FormKey, SerializedFormData]>;
2162
2076
  };
@@ -2196,10 +2110,16 @@ declare function renderAttaformState(app: App): SerializedAttaformState;
2196
2110
  * app.mount('#app')
2197
2111
  * ```
2198
2112
  *
2113
+ * Accepts `unknown` because the payload crosses the wire untyped
2114
+ * (an inline `<script>` tag, `JSON.parse`): the envelope is
2115
+ * validated at runtime, and anything that isn't a version-matched
2116
+ * `renderAttaformState` snapshot is skipped wholesale, so forms
2117
+ * construct fresh from their schemas instead.
2118
+ *
2199
2119
  * The next `useForm({ key })` call for each serialised form picks up
2200
2120
  * the snapshot transparently — no further action is required.
2201
2121
  */
2202
- declare function hydrateAttaformState(app: App, payload: SerializedAttaformState): void;
2122
+ declare function hydrateAttaformState(app: App, payload: unknown): void;
2203
2123
 
2204
2124
  /**
2205
2125
  * Escape a JSON string so it's safe to embed inside an inline
@@ -2232,26 +2152,6 @@ declare function escapeForInlineScript(json: string): string;
2232
2152
  */
2233
2153
  declare const assignKey: unique symbol;
2234
2154
 
2235
- /**
2236
- * The `v-register` directive. Bind a form field to a native input,
2237
- * select, textarea, checkbox, or radio:
2238
- *
2239
- * ```vue
2240
- * <input v-register="form.register('email')" />
2241
- * <select v-register="form.register('country')">
2242
- * <option value="us">US</option>
2243
- * <option value="uk">UK</option>
2244
- * </select>
2245
- * ```
2246
- *
2247
- * The directive picks the right binding strategy automatically based
2248
- * on the element's `tagName` and `type`. Registered globally by
2249
- * `createAttaform()`. Most consumers never import it directly, but
2250
- * it's exposed for advanced integrations that wire directives
2251
- * manually.
2252
- */
2253
- declare const vRegister: RegisterModelDynamicCustomDirective;
2254
-
2255
2155
  /**
2256
2156
  * Type guard for a `RegisterValue`. Returns `true` when `val` looks
2257
2157
  * like the object returned from `form.register(path)`.
@@ -2480,5 +2380,5 @@ declare function makeDefaultDisplayState({ showDelay, minVisible, }: DisplayTimi
2480
2380
  */
2481
2381
  declare const defaultDisplayState: GetDisplayState;
2482
2382
 
2483
- export { renderAttaformState as $, createAttaform as B, DEFAULT_TIMINGS as D, createRegistry as E, defaultCoercionRules as H, defaultDisplayState as J, defineCoercion as K, escapeForInlineScript as M, gate as N, OutsideSetupError as O, getRegistryFromApp as P, hydrateAttaformState as Q, RegistryNotInstalledError as R, injectForm as T, injectWizard as V, isRegisterValue as X, kAttaformRegistry as Y, lazy as Z, makeDefaultDisplayState as _, useRegister as a0, useRegistry as a1, useWizard as a2, vRegister as a3, AttaformError as c, AttaformErrorCode as d, DEVTOOLS_WINDOW_KEY as f, InvalidPathError as i, InvalidUseFormConfigError as j, ReservedFormKeyError as k, SubmitErrorHandlerError as n, assignKey as z };
2383
+ export { renderAttaformState as $, createAttaform as B, DEFAULT_TIMINGS as D, createRegistry as E, defaultCoercionRules as H, defaultDisplayState as J, defineCoercion as K, escapeForInlineScript as M, gate as N, OutsideSetupError as O, getRegistryFromApp as P, hydrateAttaformState as Q, RegistryNotInstalledError as R, injectForm as T, injectWizard as V, isRegisterValue as X, kAttaformRegistry as Y, lazy as Z, makeDefaultDisplayState as _, useRegister as a0, useRegistry as a1, useWizard as a2, AttaformError as c, AttaformErrorCode as d, DEVTOOLS_WINDOW_KEY as f, InvalidPathError as i, InvalidUseFormConfigError as j, ReservedFormKeyError as k, SubmitErrorHandlerError as n, assignKey as z };
2484
2384
  export type { AttaformRegistry as A, CompiledStep as C, FormStatus as F, GateMarker as G, InjectWizardInput as I, LazyMarker as L, SerializedAttaformState as S, UseRegisterReturn as U, WizardAggregateError as W, AnyForm as a, AttaformDevtoolsBridge as b, AttaformPluginOptions as e, DisplayTimings as g, FormStatusSeed as h, SerializedFormData as l, StepSlot as m, UseWizardReturnType as o, WizardCtx as p, WizardCtxForm as q, WizardOnError as r, WizardOnSubmit as s, WizardOptions as t, WizardPersistFn as u, WizardRestoreFn as v, WizardRestoreState as w, WizardStatusesProxy as x, WizardSubmitContext as y };
@@ -1,9 +1,7 @@
1
- 'use strict';
2
-
3
- const devtoolsShared = require('./attaform.Biytrv0-.cjs');
4
- const useForm$1 = require('./attaform.Bh8A6YAc.cjs');
5
- const index = require('./attaform.Crv0RNiz.cjs');
6
- const walkFieldMeta = require('./attaform.f2zyNh5d.cjs');
1
+ import { a as InvalidUseFormConfigError } from './attaform.wfY8XFvm.mjs';
2
+ import { u as useForm$2 } from './attaform.CJP3RkGa.mjs';
3
+ import { u as useForm$1 } from './attaform.NLmerepI.mjs';
4
+ import { i as installingFieldMetaStore, g as getFieldMetaForSchema } from './attaform.C09fpdLS.mjs';
7
5
 
8
6
  function isZodV4SchemaShape(value) {
9
7
  if (typeof value !== "object" || value === null) return false;
@@ -14,21 +12,21 @@ function isZodV4SchemaShape(value) {
14
12
 
15
13
  function useForm(configuration) {
16
14
  if (configuration === void 0 || configuration === null || configuration.schema === void 0) {
17
- throw new devtoolsShared.InvalidUseFormConfigError();
15
+ throw new InvalidUseFormConfigError();
18
16
  }
19
17
  const { schema } = configuration;
20
18
  if (isZodV4SchemaShape(schema)) {
21
- return index.useForm(configuration);
19
+ return useForm$1(configuration);
22
20
  }
23
- return useForm$1.useForm(configuration);
21
+ return useForm$2(configuration);
24
22
  }
25
23
 
26
- const fieldMeta = walkFieldMeta.fieldMetaStore;
24
+ const fieldMeta = installingFieldMetaStore;
27
25
  function withMeta(schema, payload) {
28
26
  const target = schema;
29
- const existing = walkFieldMeta.getFieldMetaForSchema(target) ?? {};
27
+ const existing = getFieldMetaForSchema(target) ?? {};
30
28
  const cloned = cloneSchema(schema);
31
- walkFieldMeta.fieldMetaStore.add(cloned, { ...existing, ...payload });
29
+ installingFieldMetaStore.add(cloned, { ...existing, ...payload });
32
30
  return cloned;
33
31
  }
34
32
  function cloneSchema(schema) {
@@ -40,7 +38,4 @@ function cloneSchema(schema) {
40
38
  return new Ctor(candidate._def);
41
39
  }
42
40
 
43
- exports.fieldMeta = fieldMeta;
44
- exports.useForm = useForm;
45
- exports.withMeta = withMeta;
46
- //# sourceMappingURL=attaform.CF-EnyJn.cjs.map
41
+ export { fieldMeta as f, useForm as u, withMeta as w };
@@ -51,4 +51,3 @@ function resolveZodAliasTarget(consumerRootDir, tag, resolveZodAlias, warnState)
51
51
  }
52
52
 
53
53
  export { REWRITABLE_ZOD_SPECIFIER_FILTER as R, isRewritableZodSpecifier as i, resolveZodAliasTarget as r };
54
- //# sourceMappingURL=attaform.DhnPb6nK.mjs.map
@@ -0,0 +1,197 @@
1
+ import { c as RegistryNotInstalledError, O as OutsideSetupError, g as pathKeyToDotted } from './attaform.wfY8XFvm.mjs';
2
+ import { shallowReactive, getCurrentInstance, inject } from 'vue';
3
+
4
+ function detectSSR(options = {}) {
5
+ if (options.ssr !== void 0) return options.ssr;
6
+ return typeof window === "undefined" && typeof document === "undefined";
7
+ }
8
+
9
+ const kAttaformRegistry = /* @__PURE__ */ Symbol.for("attaform:registry");
10
+ const kAttaformAncestorWizard = /* @__PURE__ */ Symbol.for(
11
+ "attaform:ancestor-wizard"
12
+ );
13
+ const kAttaformWizardActiveStepResolver = /* @__PURE__ */ Symbol.for(
14
+ "attaform:wizard-active-step-resolver"
15
+ );
16
+ const kFormContext = /* @__PURE__ */ Symbol.for("attaform:form-context");
17
+ const kFormInstanceId = /* @__PURE__ */ Symbol.for("attaform:form-instance-id");
18
+ function createRegistry(options = {}) {
19
+ const ssr = detectSSR(options);
20
+ const defaults = Object.freeze({ ...options.defaults ?? {} });
21
+ const forms = shallowReactive(/* @__PURE__ */ new Map());
22
+ const wizards = shallowReactive(/* @__PURE__ */ new Map());
23
+ const pendingHydration = shallowReactive(/* @__PURE__ */ new Map());
24
+ const consumers = /* @__PURE__ */ new Map();
25
+ const pendingEvictions = /* @__PURE__ */ new Map();
26
+ function cancelPendingEviction(key) {
27
+ const pending = pendingEvictions.get(key);
28
+ if (pending === void 0) return;
29
+ pending.cancelled = true;
30
+ pendingEvictions.delete(key);
31
+ }
32
+ function trackConsumer(key) {
33
+ cancelPendingEviction(key);
34
+ consumers.set(key, (consumers.get(key) ?? 0) + 1);
35
+ let disposed = false;
36
+ return () => {
37
+ if (disposed) return;
38
+ disposed = true;
39
+ const remaining = (consumers.get(key) ?? 1) - 1;
40
+ if (remaining > 0) {
41
+ consumers.set(key, remaining);
42
+ return;
43
+ }
44
+ consumers.delete(key);
45
+ const token = { cancelled: false };
46
+ pendingEvictions.set(key, token);
47
+ queueMicrotask(() => {
48
+ if (token.cancelled) return;
49
+ if (pendingEvictions.get(key) !== token) return;
50
+ pendingEvictions.delete(key);
51
+ const state = forms.get(key);
52
+ forms.delete(key);
53
+ if (state === void 0) return;
54
+ state.dispose();
55
+ });
56
+ };
57
+ }
58
+ const wizardConsumers = /* @__PURE__ */ new Map();
59
+ const pendingWizardEvictions = /* @__PURE__ */ new Map();
60
+ function cancelPendingWizardEviction(key) {
61
+ const pending = pendingWizardEvictions.get(key);
62
+ if (pending === void 0) return;
63
+ pending.cancelled = true;
64
+ pendingWizardEvictions.delete(key);
65
+ }
66
+ function trackWizardConsumer(key) {
67
+ cancelPendingWizardEviction(key);
68
+ wizardConsumers.set(key, (wizardConsumers.get(key) ?? 0) + 1);
69
+ let disposed = false;
70
+ return () => {
71
+ if (disposed) return;
72
+ disposed = true;
73
+ const remaining = (wizardConsumers.get(key) ?? 1) - 1;
74
+ if (remaining > 0) {
75
+ wizardConsumers.set(key, remaining);
76
+ return;
77
+ }
78
+ wizardConsumers.delete(key);
79
+ const token = { cancelled: false };
80
+ pendingWizardEvictions.set(key, token);
81
+ queueMicrotask(() => {
82
+ if (token.cancelled) return;
83
+ if (pendingWizardEvictions.get(key) !== token) return;
84
+ pendingWizardEvictions.delete(key);
85
+ wizards.delete(key);
86
+ });
87
+ };
88
+ }
89
+ const prefetchEnqueued = /* @__PURE__ */ new Set();
90
+ const prefetchSkipped = /* @__PURE__ */ new Set();
91
+ function enqueuePrefetch(key) {
92
+ prefetchEnqueued.add(key);
93
+ }
94
+ function skipPrefetch(key) {
95
+ prefetchSkipped.add(key);
96
+ }
97
+ function shouldPrefetch(key) {
98
+ return prefetchEnqueued.has(key) && !prefetchSkipped.has(key);
99
+ }
100
+ return {
101
+ forms,
102
+ wizards,
103
+ pendingHydration,
104
+ ssr,
105
+ defaults,
106
+ trackConsumer,
107
+ trackWizardConsumer,
108
+ enqueuePrefetch,
109
+ skipPrefetch,
110
+ shouldPrefetch
111
+ };
112
+ }
113
+ function useRegistry() {
114
+ const instance = getCurrentInstance();
115
+ if (instance === null) {
116
+ throw new OutsideSetupError();
117
+ }
118
+ const registry = inject(kAttaformRegistry, null);
119
+ if (registry === null) {
120
+ throw new RegistryNotInstalledError();
121
+ }
122
+ return registry;
123
+ }
124
+ function getRegistryFromApp(app) {
125
+ const registry = app._attaform;
126
+ if (registry === void 0) {
127
+ throw new RegistryNotInstalledError();
128
+ }
129
+ return registry;
130
+ }
131
+ function attachRegistryToApp(app, registry) {
132
+ app.provide(kAttaformRegistry, registry);
133
+ app._attaform = registry;
134
+ }
135
+
136
+ function installAttaformOnApp(app, options, source) {
137
+ if (app._attaform !== void 0) {
138
+ return app._attaform;
139
+ }
140
+ const registry = createRegistry(options);
141
+ attachRegistryToApp(app, registry);
142
+ return registry;
143
+ }
144
+ function ensureAttaformInstalled(app) {
145
+ return installAttaformOnApp(app, {});
146
+ }
147
+ function createAttaform(options = {}) {
148
+ const plugin = {
149
+ install(app) {
150
+ installAttaformOnApp(app, options);
151
+ }
152
+ };
153
+ return plugin;
154
+ }
155
+
156
+ const ATTAFORM_STATE_VERSION = 1;
157
+ function renderAttaformState(app) {
158
+ const registry = getRegistryFromApp(app);
159
+ const forms = [];
160
+ for (const [key, state] of registry.forms) {
161
+ const transientList = [];
162
+ for (const pk of state.blankPaths) {
163
+ const d = pathKeyToDotted(pk);
164
+ if (d !== null) transientList.push(d);
165
+ }
166
+ forms.push([
167
+ key,
168
+ {
169
+ form: state.form.value,
170
+ // The wire shape stays two per-source arrays; the tagged store
171
+ // splits at the boundary and the client replays each side into
172
+ // its cell half.
173
+ schemaErrors: [...state.errorCells.entries()].filter(([, cell]) => cell.schema.length > 0).map(([key2, cell]) => [key2, cell.schema]),
174
+ userErrors: [...state.errorCells.entries()].filter(([, cell]) => cell.user.length > 0).map(([key2, cell]) => [key2, cell.user]),
175
+ fields: Array.from(state.fields.entries()),
176
+ ...transientList.length > 0 ? { blankPaths: transientList } : {}
177
+ }
178
+ ]);
179
+ }
180
+ return { v: ATTAFORM_STATE_VERSION, forms };
181
+ }
182
+ function hydrateAttaformState(app, payload) {
183
+ if (!isHydratableEnvelope(payload)) {
184
+ return;
185
+ }
186
+ const registry = getRegistryFromApp(app);
187
+ for (const [key, data] of payload.forms) {
188
+ registry.pendingHydration.set(key, data);
189
+ }
190
+ }
191
+ function isHydratableEnvelope(payload) {
192
+ return payload !== null && typeof payload === "object" && "v" in payload && payload.v === ATTAFORM_STATE_VERSION && "forms" in payload && Array.isArray(payload.forms);
193
+ }
194
+
195
+ const DEVTOOLS_WINDOW_KEY = "__attaform_devtools__";
196
+
197
+ export { DEVTOOLS_WINDOW_KEY as D, createRegistry as a, kAttaformWizardActiveStepResolver as b, createAttaform as c, kFormContext as d, ensureAttaformInstalled as e, kFormInstanceId as f, getRegistryFromApp as g, hydrateAttaformState as h, kAttaformAncestorWizard as i, kAttaformRegistry as k, renderAttaformState as r, useRegistry as u };