attaform 0.31.1 → 0.31.2

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 (44) hide show
  1. package/dist/abstract.d.mts +5 -5
  2. package/dist/abstract.mjs +3 -2
  3. package/dist/dev/abstract.mjs +3 -2
  4. package/dist/dev/directive.mjs +1 -1
  5. package/dist/dev/history.mjs +118 -11
  6. package/dist/dev/index.mjs +4 -3
  7. package/dist/dev/shared/{attaform-dev.OceE0X3v.mjs → attaform-dev.BKWPjEB2.mjs} +4 -3
  8. package/dist/dev/shared/{attaform-dev.D7lKyM_7.mjs → attaform-dev.BnLLxX2l.mjs} +3 -3
  9. package/dist/dev/shared/{attaform-dev.Cbx0AiQF.mjs → attaform-dev.CKSqs_xQ.mjs} +1 -1
  10. package/dist/dev/shared/{attaform-dev.BpVGFSFa.mjs → attaform-dev.Cf_hZfbT.mjs} +3 -3
  11. package/dist/dev/shared/{attaform-dev.3uTCYwy8.mjs → attaform-dev.DLk51W7-.mjs} +111 -909
  12. package/dist/dev/shared/{attaform-dev.CwYTWyPX.mjs → attaform-dev.DoG8mZTq.mjs} +8 -1
  13. package/dist/dev/shared/attaform-dev.G-3XDl9F.mjs +1192 -0
  14. package/dist/dev/shared/{attaform-dev.PTGM9KE1.mjs → attaform-dev.oguHqv1W.mjs} +7 -6
  15. package/dist/dev/zod-v3.mjs +5 -4
  16. package/dist/dev/zod-v4.mjs +5 -4
  17. package/dist/dev/zod.mjs +4 -3
  18. package/dist/directive.d.mts +2 -2
  19. package/dist/directive.mjs +1 -1
  20. package/dist/history.d.mts +1 -1
  21. package/dist/history.mjs +118 -11
  22. package/dist/index.d.mts +6 -6
  23. package/dist/index.mjs +4 -3
  24. package/dist/shared/{attaform.j9xI_ObC.mjs → attaform.3kV6PqVJ.mjs} +7 -6
  25. package/dist/shared/{attaform.KEfNRDot.mjs → attaform.BJeiuJE2.mjs} +3 -3
  26. package/dist/shared/{attaform._1Zp3mDG.mjs → attaform.BNUhW4qC.mjs} +8 -1
  27. package/dist/shared/{attaform.B5b-HmhH.mjs → attaform.BlAmZ-WU.mjs} +3 -3
  28. package/dist/shared/{attaform.u6jfHoed.mjs → attaform.CMzc4bPI.mjs} +4 -3
  29. package/dist/shared/attaform.CgyyJVuq.mjs +1187 -0
  30. package/dist/shared/{attaform.CYlF4P6X.d.mts → attaform.CibLYe9-.d.mts} +185 -165
  31. package/dist/shared/{attaform.DAf4ehIe.mjs → attaform.DCUJCKyG.mjs} +111 -909
  32. package/dist/shared/{attaform.DZOynI6c.d.mts → attaform.DCyZT7u-.d.mts} +1 -1
  33. package/dist/shared/{attaform.B9PYKlhB.d.mts → attaform.DD0Y8krq.d.mts} +1 -1
  34. package/dist/shared/{attaform.BPMOl562.mjs → attaform.T0YjhjPU.mjs} +1 -1
  35. package/dist/shared/{attaform.i13OEHJF.d.mts → attaform.bI_4nKZp.d.mts} +61 -3
  36. package/dist/zod-v3.d.mts +4 -4
  37. package/dist/zod-v3.mjs +5 -4
  38. package/dist/zod-v4.d.mts +6 -6
  39. package/dist/zod-v4.mjs +5 -4
  40. package/dist/zod.d.mts +4 -4
  41. package/dist/zod.mjs +4 -3
  42. package/package.json +1 -1
  43. package/dist/dev/shared/attaform-dev.n6n00owJ.mjs +0 -279
  44. package/dist/shared/attaform.BPSqtU7a.mjs +0 -274
@@ -1,160 +1,6 @@
1
- import { G as GenericForm, F as FormKey, a as UseFormReturnType, _ as RegisterValue, v as Path, w as PathKey, aq as DisplayCtx, ar as GetDisplayState, as as DisplayMachine, at as FieldRecord, au as ErrorCell, ac as ValidationError, av as OriginalsRecord, A as AbstractSchema, ag as WriteMeta, d as DeepPartial, ah as WriteShape, aw as StructureSnapshot, ax as AttaformDomBinding, ay as TransformAbortHolder } from './attaform.i13OEHJF.mjs';
1
+ import { v as Path, w as PathKey, aq as DisplayCtx, ar as GetDisplayState, as as DisplayMachine, G as GenericForm, F as FormKey, at as FieldRecord, au as ErrorCell, ac as ValidationError, av as OriginalsRecord, A as AbstractSchema, ag as WriteMeta, d as DeepPartial, ah as WriteShape, aw as StructureSnapshot, ax as IndexRemap, ay as AttaformDomBinding, az as TransformAbortHolder, a as UseFormReturnType, _ as RegisterValue } from './attaform.bI_4nKZp.mjs';
2
2
  import { Ref, ComputedRef, ShallowRef, App, InjectionKey, Plugin } from 'vue';
3
3
 
4
- /** Options accepted by `injectForm` in place of a bare key string. */
5
- type InjectFormInput = {
6
- readonly key?: FormKey;
7
- /**
8
- * Set by the Vite transform when this `injectForm` call site sits in
9
- * a component whose template / script reads the form's reactive
10
- * state. On the server, this enqueues the form for SSR prefetch and
11
- * wires `onServerPrefetch` so the descendant awaits the activation
12
- * promise before its render emits HTML.
13
- *
14
- * @internal Transform-emitted. Manual use is the documented escape
15
- * hatch when the transform can't reach the reference (dynamic
16
- * property access, untransformed bundlers).
17
- */
18
- readonly __ssrAccessed?: boolean;
19
- };
20
- /**
21
- * Reach an existing form from a descendant component without threading
22
- * it through props. The counterpart to `useForm`: `useForm` creates and
23
- * provides, `injectForm` looks up.
24
- *
25
- * ```ts
26
- * // The nearest ancestor's anonymous useForm() call.
27
- * const form = injectForm<SignupShape>()
28
- *
29
- * // A specific form by key, from anywhere in the app.
30
- * const cart = injectForm<CartShape>('cart')
31
- * ```
32
- *
33
- * A keyed call is a registry lookup, so it is independent of
34
- * component-tree position. A no-key call takes the closest ambient
35
- * ancestor and inherits its `formInstanceId`; only an anonymous
36
- * `useForm()` fills that ambient slot, so a keyed form is reachable
37
- * only by its key.
38
- *
39
- * Returns `null` when nothing matches, so narrow before use:
40
- *
41
- * ```ts
42
- * const form = injectForm<Shape>('signup')
43
- * if (!form) return
44
- * form.register('email')
45
- * ```
46
- *
47
- * A keyed miss warns in dev with the registered keys and the call site.
48
- * Pass the `Form` generic explicitly, since Vue's provide/inject erases
49
- * generics. The form stays alive for this component's lifetime and is
50
- * cleaned up once every consumer has unmounted.
51
- *
52
- * Components whose nearest `useForm`-calling ancestor is the same one get
53
- * the same form object for the same form: `injectForm('cart')` in two of
54
- * its descendants returns one form, with one set of field caches.
55
- */
56
- declare function injectForm<Form extends GenericForm, GetValueFormType extends GenericForm = Form>(input?: FormKey | InjectFormInput): UseFormReturnType<Form, GetValueFormType> | null;
57
-
58
- /**
59
- * Return type of `useRegister()`. Hybrid of `RegisterValue<V>` (so
60
- * `rv.path` / `rv.segments` / `rv.formKey` etc. work directly in
61
- * script setup) and `Ref<RegisterValue<V> | undefined>` (so Vue's
62
- * template auto-unwrap surfaces the underlying RV to `v-register`
63
- * and the directive's path-migration diff sees the real RV across
64
- * renders).
65
- *
66
- * The two surfaces don't clash at the type level: `RegisterValue`
67
- * doesn't carry a `value` field, and `Ref<T>`'s `value: T` becomes
68
- * the hybrid's only `.value`. Older code that read `rv.value?.path`
69
- * keeps working; new code can write `rv.path` directly.
70
- */
71
- type UseRegisterReturn<V = unknown> = RegisterValue<V> & Ref<RegisterValue<V> | undefined> & {
72
- /**
73
- * Whether a parent has actually bound this wrapper. `false` until a
74
- * `RegisterValue` lands, `true` once one has, and reactive in both
75
- * directions: a parent that binds on a later render flips it, and
76
- * so does one that stops binding.
77
- *
78
- * This is the test a dual-mode wrapper reaches for, and the reason
79
- * it exists as a field rather than a truthiness check on the
80
- * return: the composable cannot answer "is this bound" at setup
81
- * time. A parent is free to bind on a later render
82
- * (`v-register="ready ? form.register('x') : undefined"`), and
83
- * `useRegister` has to keep serving that, so it can never return a
84
- * plain `undefined` for "unbound right now". Script-setup code
85
- * reading `rv` directly sees the hybrid Proxy, which is an object
86
- * and therefore always truthy; a template sees the unwrapped ref,
87
- * which IS `undefined` when unbound. `rv?.isBound` reads the same
88
- * on both sides (#620).
89
- */
90
- readonly isBound: boolean;
91
- };
92
- /**
93
- * Options for `useRegister`.
94
- */
95
- type UseRegisterOptions = {
96
- /**
97
- * Declares that this wrapper is used both with and without a form, so
98
- * a render with no parent binding is intended rather than a mistake.
99
- * Silences the unbound diagnostic; nothing else changes.
100
- *
101
- * The diagnostic exists because a single-mode wrapper whose parent
102
- * forgot `v-register` renders a field that looks fine and stores
103
- * nothing, which is an expensive afternoon. Once a wrapper is
104
- * deliberately dual-mode, the same signal fires on correct code and
105
- * tells the author to do something wrong, so this is the one fact
106
- * that separates the two cases and only the author has it (#620).
107
- *
108
- * Pair it with `rv?.isBound` to branch on what actually happened.
109
- */
110
- readonly optional?: boolean;
111
- };
112
- /**
113
- * Re-bind a parent's `v-register` onto an inner native element. Use it
114
- * inside a component that wraps a single form field whose root is NOT
115
- * the input itself, such as a labelled row that renders `<label>`
116
- * around the input.
117
- *
118
- * ```vue
119
- * <!-- Parent -->
120
- * <MyInput v-register="form.register('email')" />
121
- *
122
- * <!-- MyInput.vue -->
123
- * <script setup lang="ts">
124
- * import { useRegister } from 'attaform'
125
- * const rv = useRegister()
126
- * </script>
127
- *
128
- * <template>
129
- * <label class="field">
130
- * <span>Email</span>
131
- * <input v-register="rv" />
132
- * </label>
133
- * </template>
134
- * ```
135
- *
136
- * The return is a hybrid: `v-register="rv"` hands the directive the
137
- * parent's own `RegisterValue`, and `rv.path` / `rv.segments` /
138
- * `rv.formKey` / `rv.innerRef` read directly in script setup with no
139
- * `.value` unwrap. Reads inside a `computed` or `watchEffect` re-run
140
- * when the parent rebinds to a different path.
141
- *
142
- * Modifiers on the parent's binding apply at the inner control, so
143
- * `<MyInput v-register.trim="form.register('email')" />` trims there,
144
- * together with any the wrapper writes on its own `v-register`.
145
- *
146
- * When no parent bound, every field reads `undefined`, so reach for
147
- * `rv?.path`. `v-register="rv"` is still safe. A wrapper that is meant
148
- * to render both with and without a form declares it with
149
- * `useRegister({ optional: true })` and branches on `rv?.isBound`.
150
- *
151
- * When the wrapper's root IS the input, Vue's attribute fallthrough
152
- * already binds it and `useRegister` is unnecessary. For a wrapper
153
- * that binds several fields, use `injectForm<Form>(key?)` and call
154
- * `ctx.register(...)` directly.
155
- */
156
- declare function useRegister<V = unknown>(options?: UseRegisterOptions): UseRegisterReturn<V> | undefined;
157
-
158
4
  type DynamicPathSweep = {
159
5
  /**
160
6
  * Note that a cache has just stored an entry for `segments` under
@@ -617,6 +463,13 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
617
463
  */
618
464
  setValueAtPath(path: Path, value: unknown, meta?: WriteMeta): boolean;
619
465
  getValueAtPath(path: Path): unknown;
466
+ /**
467
+ * The value `path` started as, for a container's `original`: the baseline
468
+ * at `path`, each array index followed to the slot its element held there.
469
+ * `undefined` for an element added since and for a path the baseline lacks.
470
+ * Frozen, since every read shares it.
471
+ */
472
+ getOriginalAtPath(path: Path): unknown;
620
473
  /**
621
474
  * Stable identity for the array element at `path`, the token the array
622
475
  * engine maintains across structural mutations. Empty for anything that is
@@ -659,8 +512,20 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
659
512
  * Put a `captureStructure()` back, before the value it was taken with is
660
513
  * restored, so the write that restores it compares each row against its
661
514
  * own baseline. Every reading it can change is woken.
515
+ *
516
+ * The field records follow their rows: each of `remaps` moves the records
517
+ * under its array's elements, in order, handing a removed element's
518
+ * history to the array and its ancestors, and each of `returning`, a
519
+ * record of a row the restore brings back, takes its place at its path,
520
+ * its element not yet on the page.
662
521
  */
663
- restoreStructure(snapshot: StructureSnapshot): void;
522
+ restoreStructure(snapshot: StructureSnapshot, remaps: ReadonlyArray<readonly [Path, IndexRemap]>, returning: readonly FieldRecord[]): void;
523
+ /**
524
+ * Hears the records a removal releases while a history module is attached,
525
+ * so an undo can bring a row back with its interaction history. Set by the
526
+ * module, so a form without one never carries it.
527
+ */
528
+ noteReleased?: ((records: readonly FieldRecord[]) => void) | undefined;
664
529
  /**
665
530
  * Merged read of the cell at `path`, in schema -> blank -> user order:
666
531
  * structural validation first, the synthesized blank entry between, user
@@ -715,18 +580,19 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
715
580
  */
716
581
  markInteracted(path: Path): void;
717
582
  /**
718
- * Walk every active-variant leaf under `segments` and flip `touched: true`.
719
- * Powers `form.touch(path?)`. Idempotent, and touches neither value nor
720
- * `focused` / `blurred`, nor does it trigger validation.
583
+ * Walk every active-variant leaf under `segments`, and every container there
584
+ * holding no leaf, and flip `touched: true`. Powers `form.touch(path?)`.
585
+ * Idempotent, and touches neither value nor `focused` / `blurred`, nor does
586
+ * it trigger validation.
721
587
  */
722
588
  touchAtPath(segments: Path): void;
723
589
  /**
724
- * Walk every active-variant leaf under `segments` and flip the whole
725
- * interaction ladder (`touched` / `interacted` / `blurredAfterInteraction`),
726
- * as though the user had focused, edited and left each one. Powers
727
- * `form.interact(path?)`. Idempotent, and leaves value / `focused` /
728
- * `blurred` alone. Returns whether any leaf resolved, so the caller can skip
729
- * validation and dev-warn on an empty path.
590
+ * Walk every active-variant leaf under `segments`, and every container there
591
+ * holding no leaf, and flip the whole interaction ladder (`touched` /
592
+ * `interacted` / `blurredAfterInteraction`), as though the user had focused,
593
+ * edited and left each one. Powers `form.interact(path?)`. Idempotent, and
594
+ * leaves value / `focused` / `blurred` alone. Returns whether anything
595
+ * resolved, so the caller can skip validation on an unresolved path.
730
596
  */
731
597
  interactAtPath(segments: Path): boolean;
732
598
  /**
@@ -870,6 +736,160 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
870
736
  dispose(): void;
871
737
  };
872
738
 
739
+ /** Options accepted by `injectForm` in place of a bare key string. */
740
+ type InjectFormInput = {
741
+ readonly key?: FormKey;
742
+ /**
743
+ * Set by the Vite transform when this `injectForm` call site sits in
744
+ * a component whose template / script reads the form's reactive
745
+ * state. On the server, this enqueues the form for SSR prefetch and
746
+ * wires `onServerPrefetch` so the descendant awaits the activation
747
+ * promise before its render emits HTML.
748
+ *
749
+ * @internal Transform-emitted. Manual use is the documented escape
750
+ * hatch when the transform can't reach the reference (dynamic
751
+ * property access, untransformed bundlers).
752
+ */
753
+ readonly __ssrAccessed?: boolean;
754
+ };
755
+ /**
756
+ * Reach an existing form from a descendant component without threading
757
+ * it through props. The counterpart to `useForm`: `useForm` creates and
758
+ * provides, `injectForm` looks up.
759
+ *
760
+ * ```ts
761
+ * // The nearest ancestor's anonymous useForm() call.
762
+ * const form = injectForm<SignupShape>()
763
+ *
764
+ * // A specific form by key, from anywhere in the app.
765
+ * const cart = injectForm<CartShape>('cart')
766
+ * ```
767
+ *
768
+ * A keyed call is a registry lookup, so it is independent of
769
+ * component-tree position. A no-key call takes the closest ambient
770
+ * ancestor and inherits its `formInstanceId`; only an anonymous
771
+ * `useForm()` fills that ambient slot, so a keyed form is reachable
772
+ * only by its key.
773
+ *
774
+ * Returns `null` when nothing matches, so narrow before use:
775
+ *
776
+ * ```ts
777
+ * const form = injectForm<Shape>('signup')
778
+ * if (!form) return
779
+ * form.register('email')
780
+ * ```
781
+ *
782
+ * A keyed miss warns in dev with the registered keys and the call site.
783
+ * Pass the `Form` generic explicitly, since Vue's provide/inject erases
784
+ * generics. The form stays alive for this component's lifetime and is
785
+ * cleaned up once every consumer has unmounted.
786
+ *
787
+ * Components whose nearest `useForm`-calling ancestor is the same one get
788
+ * the same form object for the same form: `injectForm('cart')` in two of
789
+ * its descendants returns one form, with one set of field caches.
790
+ */
791
+ declare function injectForm<Form extends GenericForm, GetValueFormType extends GenericForm = Form>(input?: FormKey | InjectFormInput): UseFormReturnType<Form, GetValueFormType> | null;
792
+
793
+ /**
794
+ * Return type of `useRegister()`. Hybrid of `RegisterValue<V>` (so
795
+ * `rv.path` / `rv.segments` / `rv.formKey` etc. work directly in
796
+ * script setup) and `Ref<RegisterValue<V> | undefined>` (so Vue's
797
+ * template auto-unwrap surfaces the underlying RV to `v-register`
798
+ * and the directive's path-migration diff sees the real RV across
799
+ * renders).
800
+ *
801
+ * The two surfaces don't clash at the type level: `RegisterValue`
802
+ * doesn't carry a `value` field, and `Ref<T>`'s `value: T` becomes
803
+ * the hybrid's only `.value`. Older code that read `rv.value?.path`
804
+ * keeps working; new code can write `rv.path` directly.
805
+ */
806
+ type UseRegisterReturn<V = unknown> = RegisterValue<V> & Ref<RegisterValue<V> | undefined> & {
807
+ /**
808
+ * Whether a parent has actually bound this wrapper. `false` until a
809
+ * `RegisterValue` lands, `true` once one has, and reactive in both
810
+ * directions: a parent that binds on a later render flips it, and
811
+ * so does one that stops binding.
812
+ *
813
+ * This is the test a dual-mode wrapper reaches for, and the reason
814
+ * it exists as a field rather than a truthiness check on the
815
+ * return: the composable cannot answer "is this bound" at setup
816
+ * time. A parent is free to bind on a later render
817
+ * (`v-register="ready ? form.register('x') : undefined"`), and
818
+ * `useRegister` has to keep serving that, so it can never return a
819
+ * plain `undefined` for "unbound right now". Script-setup code
820
+ * reading `rv` directly sees the hybrid Proxy, which is an object
821
+ * and therefore always truthy; a template sees the unwrapped ref,
822
+ * which IS `undefined` when unbound. `rv?.isBound` reads the same
823
+ * on both sides (#620).
824
+ */
825
+ readonly isBound: boolean;
826
+ };
827
+ /**
828
+ * Options for `useRegister`.
829
+ */
830
+ type UseRegisterOptions = {
831
+ /**
832
+ * Declares that this wrapper is used both with and without a form, so
833
+ * a render with no parent binding is intended rather than a mistake.
834
+ * Silences the unbound diagnostic; nothing else changes.
835
+ *
836
+ * The diagnostic exists because a single-mode wrapper whose parent
837
+ * forgot `v-register` renders a field that looks fine and stores
838
+ * nothing, which is an expensive afternoon. Once a wrapper is
839
+ * deliberately dual-mode, the same signal fires on correct code and
840
+ * tells the author to do something wrong, so this is the one fact
841
+ * that separates the two cases and only the author has it (#620).
842
+ *
843
+ * Pair it with `rv?.isBound` to branch on what actually happened.
844
+ */
845
+ readonly optional?: boolean;
846
+ };
847
+ /**
848
+ * Re-bind a parent's `v-register` onto an inner native element. Use it
849
+ * inside a component that wraps a single form field whose root is NOT
850
+ * the input itself, such as a labelled row that renders `<label>`
851
+ * around the input.
852
+ *
853
+ * ```vue
854
+ * <!-- Parent -->
855
+ * <MyInput v-register="form.register('email')" />
856
+ *
857
+ * <!-- MyInput.vue -->
858
+ * <script setup lang="ts">
859
+ * import { useRegister } from 'attaform'
860
+ * const rv = useRegister()
861
+ * </script>
862
+ *
863
+ * <template>
864
+ * <label class="field">
865
+ * <span>Email</span>
866
+ * <input v-register="rv" />
867
+ * </label>
868
+ * </template>
869
+ * ```
870
+ *
871
+ * The return is a hybrid: `v-register="rv"` hands the directive the
872
+ * parent's own `RegisterValue`, and `rv.path` / `rv.segments` /
873
+ * `rv.formKey` / `rv.innerRef` read directly in script setup with no
874
+ * `.value` unwrap. Reads inside a `computed` or `watchEffect` re-run
875
+ * when the parent rebinds to a different path.
876
+ *
877
+ * Modifiers on the parent's binding apply at the inner control, so
878
+ * `<MyInput v-register.trim="form.register('email')" />` trims there,
879
+ * together with any the wrapper writes on its own `v-register`.
880
+ *
881
+ * When no parent bound, every field reads `undefined`, so reach for
882
+ * `rv?.path`. `v-register="rv"` is still safe. A wrapper that is meant
883
+ * to render both with and without a form declares it with
884
+ * `useRegister({ optional: true })` and branches on `rv?.isBound`.
885
+ *
886
+ * When the wrapper's root IS the input, Vue's attribute fallthrough
887
+ * already binds it and `useRegister` is unnecessary. For a wrapper
888
+ * that binds several fields, use `injectForm<Form>(key?)` and call
889
+ * `ctx.register(...)` directly.
890
+ */
891
+ declare function useRegister<V = unknown>(options?: UseRegisterOptions): UseRegisterReturn<V> | undefined;
892
+
873
893
  /**
874
894
  * Public types for `useWizard`, the multistep-form orchestrator.
875
895
  *