attaform 0.27.5 → 0.27.6

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 (62) hide show
  1. package/dist/abstract.cjs +1 -1
  2. package/dist/abstract.d.cts +4 -4
  3. package/dist/abstract.d.mts +4 -4
  4. package/dist/abstract.d.ts +4 -4
  5. package/dist/abstract.mjs +1 -1
  6. package/dist/chunks/fingerprint2.cjs +1 -1
  7. package/dist/chunks/fingerprint2.mjs +1 -1
  8. package/dist/index.cjs +2 -2
  9. package/dist/index.d.cts +5 -5
  10. package/dist/index.d.mts +5 -5
  11. package/dist/index.d.ts +5 -5
  12. package/dist/index.mjs +2 -2
  13. package/dist/nuxt.d.cts +1 -1
  14. package/dist/nuxt.d.mts +1 -1
  15. package/dist/nuxt.d.ts +1 -1
  16. package/dist/shared/{attaform.Cg_8gFQ7.mjs → attaform.B1ZYt2Li.mjs} +3 -3
  17. package/dist/shared/{attaform.Cg_8gFQ7.mjs.map → attaform.B1ZYt2Li.mjs.map} +1 -1
  18. package/dist/shared/{attaform.CF-EnyJn.cjs → attaform.B9Wyhfro.cjs} +4 -4
  19. package/dist/shared/{attaform.CF-EnyJn.cjs.map → attaform.B9Wyhfro.cjs.map} +1 -1
  20. package/dist/shared/{attaform.DrHOqB-d.d.mts → attaform.BGDV42hI.d.cts} +1 -1
  21. package/dist/shared/{attaform.DcFDQVpa.d.ts → attaform.CHlJLAiv.d.mts} +11 -1
  22. package/dist/shared/{attaform.BypWgXRB.mjs → attaform.CKH2lGdL.mjs} +40 -2
  23. package/dist/shared/attaform.CKH2lGdL.mjs.map +1 -0
  24. package/dist/shared/{attaform.CTiZ9Rrv.mjs → attaform.CoR4mWfv.mjs} +2 -2
  25. package/dist/shared/{attaform.CTiZ9Rrv.mjs.map → attaform.CoR4mWfv.mjs.map} +1 -1
  26. package/dist/shared/{attaform.f2zyNh5d.cjs → attaform.D6tk90Rc.cjs} +2 -2
  27. package/dist/shared/{attaform.f2zyNh5d.cjs.map → attaform.D6tk90Rc.cjs.map} +1 -1
  28. package/dist/shared/{attaform.Bh8A6YAc.cjs → attaform.D8kliaKw.cjs} +3 -3
  29. package/dist/shared/{attaform.Bh8A6YAc.cjs.map → attaform.D8kliaKw.cjs.map} +1 -1
  30. package/dist/shared/{attaform.DYwyM5Cp.d.cts → attaform.DLknk9nF.d.mts} +1 -1
  31. package/dist/shared/{attaform.Dkjh_F30.mjs → attaform.DMxM2kol.mjs} +3 -3
  32. package/dist/shared/{attaform.Dkjh_F30.mjs.map → attaform.DMxM2kol.mjs.map} +1 -1
  33. package/dist/shared/{attaform.Dc1mx48r.mjs → attaform.DYEr28Pt.mjs} +4 -4
  34. package/dist/shared/{attaform.Dc1mx48r.mjs.map → attaform.DYEr28Pt.mjs.map} +1 -1
  35. package/dist/shared/{attaform.Crv0RNiz.cjs → attaform.DdQN7sgN.cjs} +3 -3
  36. package/dist/shared/{attaform.Crv0RNiz.cjs.map → attaform.DdQN7sgN.cjs.map} +1 -1
  37. package/dist/shared/{attaform.CzL2CZ8U.d.ts → attaform.DniawQLP.d.cts} +60 -10
  38. package/dist/shared/{attaform.CzL2CZ8U.d.mts → attaform.DniawQLP.d.mts} +60 -10
  39. package/dist/shared/{attaform.CzL2CZ8U.d.cts → attaform.DniawQLP.d.ts} +60 -10
  40. package/dist/shared/{attaform.Cg1rUfcF.cjs → attaform.DrvMscNX.cjs} +40 -2
  41. package/dist/shared/attaform.DrvMscNX.cjs.map +1 -0
  42. package/dist/shared/{attaform.B3abZaCV.d.cts → attaform.F2Q8iv38.d.ts} +11 -1
  43. package/dist/shared/{attaform.CJ1Z9Mz4.d.mts → attaform.FmqKO1Ex.d.cts} +11 -1
  44. package/dist/shared/{attaform.CIBMfxg9.d.ts → attaform.Fp3sCrVs.d.ts} +1 -1
  45. package/dist/zod-v3.cjs +2 -2
  46. package/dist/zod-v3.d.cts +3 -3
  47. package/dist/zod-v3.d.mts +3 -3
  48. package/dist/zod-v3.d.ts +3 -3
  49. package/dist/zod-v3.mjs +2 -2
  50. package/dist/zod-v4.cjs +2 -2
  51. package/dist/zod-v4.d.cts +6 -6
  52. package/dist/zod-v4.d.mts +6 -6
  53. package/dist/zod-v4.d.ts +6 -6
  54. package/dist/zod-v4.mjs +2 -2
  55. package/dist/zod.cjs +2 -2
  56. package/dist/zod.d.cts +3 -3
  57. package/dist/zod.d.mts +3 -3
  58. package/dist/zod.d.ts +3 -3
  59. package/dist/zod.mjs +2 -2
  60. package/package.json +1 -1
  61. package/dist/shared/attaform.BypWgXRB.mjs.map +0 -1
  62. package/dist/shared/attaform.Cg1rUfcF.cjs.map +0 -1
@@ -3637,12 +3637,20 @@ type FieldStateMap<Form extends GenericForm> = LeafWalker<Form, 'field'> & {
3637
3637
  */
3638
3638
  <P extends string>(path: P): FieldCallResult<Form, P>;
3639
3639
  /**
3640
- * Tuple-segment form. Returns the typed `FieldStateMapEntry` for
3641
- * the resolved path when the tuple resolves to a known path.
3642
- * Equivalent to `form.fields[a][b][...]` but useful when the path
3643
- * is built from variables.
3640
+ * Tuple-segment form. The segment-array spelling of the string
3641
+ * call-form above, for a path built from variables — the two resolve
3642
+ * to the same proxy, so `form.fields(['users', i, 'email'])` and
3643
+ * `form.fields(\`users.${i}.email\`)` are interchangeable.
3644
+ *
3645
+ * Resolves to that path's `FieldState`, which at a container is its
3646
+ * rolled-up aggregate (`displayState`, `valid`, `dirty`, ... all
3647
+ * exist regardless of the value type), exactly as the string form
3648
+ * does. Note this is the CALL form: it hands back the path's state,
3649
+ * not a drillable sub-proxy. To walk into a container's children use
3650
+ * dot/bracket access (`form.fields.users[i].email`), which is what
3651
+ * descends.
3644
3652
  */
3645
- <const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown : never)): FieldStateMapEntry<NestedType<Form, JoinSegments<S>>>;
3653
+ <const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown : never)): FieldState<NestedType<Form, JoinSegments<S>>>;
3646
3654
  /**
3647
3655
  * Dynamic-array fallback for callers passing `Path`-typed (runtime)
3648
3656
  * segment arrays — e.g. forwarding `RegisterValue.segments` to
@@ -4583,11 +4591,8 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
4583
4591
  */
4584
4592
  applyInvalidSubmitPolicy: (policy?: OnInvalidSubmitPolicy) => void;
4585
4593
  /**
4586
- * Programmatically mark fields as touched — the sticky flag the
4587
- * standard "show errors after interaction" pattern reads. Closes
4588
- * the gap when fields are populated without a DOM gesture (post-
4589
- * import, paste, autofill, server-seeded values you want to
4590
- * validate immediately).
4594
+ * Programmatically mark fields as `touched` — the descriptive
4595
+ * "this field was visited" flag.
4591
4596
  *
4592
4597
  * ```ts
4593
4598
  * form.touch('email') // one leaf
@@ -4596,12 +4601,57 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
4596
4601
  * form.touch() // every leaf in the form
4597
4602
  * ```
4598
4603
  *
4604
+ * **This does not reveal errors under the default display
4605
+ * heuristic.** `touched` also flips on a bare focus → blur with no
4606
+ * edit, so the library-default gate deliberately ignores it and
4607
+ * reads `blurredAfterInteraction` instead — the stricter bit that
4608
+ * only a blur *following* an edit sets. Reach for `touch()` when a
4609
+ * custom `getDisplayState` reducer or your analytics reads
4610
+ * `touched`; reach for {@link UseFormReturnType.interact} to make
4611
+ * seeded or imported values surface their errors.
4612
+ *
4599
4613
  * Pure flag write — does not mutate value, focused, blurred, or
4600
4614
  * trigger validation. Idempotent: re-calling on an already-touched
4601
4615
  * field is a no-op. Touched is sticky-true; pair with
4602
4616
  * `form.reset()` / `form.resetField()` to clear.
4603
4617
  */
4604
4618
  touch: (path?: FlatPath<Form> | (string | number)[]) => void;
4619
+ /**
4620
+ * Simulate a complete focus → edit → blur over every leaf under
4621
+ * `path` (the whole form when omitted), so values that arrived
4622
+ * without a DOM gesture reveal their errors under the default
4623
+ * display heuristic.
4624
+ *
4625
+ * ```ts
4626
+ * form.interact('profile') // arm one subtree
4627
+ * await form.interact(['members', 2]) // errors committed on resolve
4628
+ * form.interact() // every leaf in the form
4629
+ * ```
4630
+ *
4631
+ * Flips the full interaction ladder (`touched`, `interacted`, and
4632
+ * `blurredAfterInteraction`) and runs the subtree's validation, so
4633
+ * the default gate — `submissionAttempts > 0 ||
4634
+ * blurredAfterInteraction` — opens through its front door. Use it
4635
+ * to arm one region's errors without a form-wide submit, which
4636
+ * would light up every other field on the page: a field-array row
4637
+ * edited in a modal, a server-seeded section, a pasted import.
4638
+ *
4639
+ * Walks schema leaves, so it reaches fields that are currently
4640
+ * `v-if`'d away or were never mounted; the flags are sticky, so
4641
+ * such a subtree stays revealed when it remounts.
4642
+ *
4643
+ * Flags land synchronously — the returned promise resolves once the
4644
+ * subtree's validation has committed, so an awaiting caller can read
4645
+ * `showErrors` / `errors` immediately after. It never rejects, and
4646
+ * ignoring it is fine.
4647
+ *
4648
+ * No-op on a disabled form, matching every other interaction-
4649
+ * lifecycle write. Does not mutate value, `focused`, or `blurred`:
4650
+ * those are DOM-owned, and the display gate reads none of them.
4651
+ * Sticky like `touched`; pair with `form.reset()` /
4652
+ * `form.resetField()` to clear.
4653
+ */
4654
+ interact: (path?: FlatPath<Form> | (string | number)[]) => Promise<void>;
4605
4655
  /**
4606
4656
  * Append `value` to the array at `path`.
4607
4657
  *
@@ -3637,12 +3637,20 @@ type FieldStateMap<Form extends GenericForm> = LeafWalker<Form, 'field'> & {
3637
3637
  */
3638
3638
  <P extends string>(path: P): FieldCallResult<Form, P>;
3639
3639
  /**
3640
- * Tuple-segment form. Returns the typed `FieldStateMapEntry` for
3641
- * the resolved path when the tuple resolves to a known path.
3642
- * Equivalent to `form.fields[a][b][...]` but useful when the path
3643
- * is built from variables.
3640
+ * Tuple-segment form. The segment-array spelling of the string
3641
+ * call-form above, for a path built from variables — the two resolve
3642
+ * to the same proxy, so `form.fields(['users', i, 'email'])` and
3643
+ * `form.fields(\`users.${i}.email\`)` are interchangeable.
3644
+ *
3645
+ * Resolves to that path's `FieldState`, which at a container is its
3646
+ * rolled-up aggregate (`displayState`, `valid`, `dirty`, ... all
3647
+ * exist regardless of the value type), exactly as the string form
3648
+ * does. Note this is the CALL form: it hands back the path's state,
3649
+ * not a drillable sub-proxy. To walk into a container's children use
3650
+ * dot/bracket access (`form.fields.users[i].email`), which is what
3651
+ * descends.
3644
3652
  */
3645
- <const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown : never)): FieldStateMapEntry<NestedType<Form, JoinSegments<S>>>;
3653
+ <const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown : never)): FieldState<NestedType<Form, JoinSegments<S>>>;
3646
3654
  /**
3647
3655
  * Dynamic-array fallback for callers passing `Path`-typed (runtime)
3648
3656
  * segment arrays — e.g. forwarding `RegisterValue.segments` to
@@ -4583,11 +4591,8 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
4583
4591
  */
4584
4592
  applyInvalidSubmitPolicy: (policy?: OnInvalidSubmitPolicy) => void;
4585
4593
  /**
4586
- * Programmatically mark fields as touched — the sticky flag the
4587
- * standard "show errors after interaction" pattern reads. Closes
4588
- * the gap when fields are populated without a DOM gesture (post-
4589
- * import, paste, autofill, server-seeded values you want to
4590
- * validate immediately).
4594
+ * Programmatically mark fields as `touched` — the descriptive
4595
+ * "this field was visited" flag.
4591
4596
  *
4592
4597
  * ```ts
4593
4598
  * form.touch('email') // one leaf
@@ -4596,12 +4601,57 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
4596
4601
  * form.touch() // every leaf in the form
4597
4602
  * ```
4598
4603
  *
4604
+ * **This does not reveal errors under the default display
4605
+ * heuristic.** `touched` also flips on a bare focus → blur with no
4606
+ * edit, so the library-default gate deliberately ignores it and
4607
+ * reads `blurredAfterInteraction` instead — the stricter bit that
4608
+ * only a blur *following* an edit sets. Reach for `touch()` when a
4609
+ * custom `getDisplayState` reducer or your analytics reads
4610
+ * `touched`; reach for {@link UseFormReturnType.interact} to make
4611
+ * seeded or imported values surface their errors.
4612
+ *
4599
4613
  * Pure flag write — does not mutate value, focused, blurred, or
4600
4614
  * trigger validation. Idempotent: re-calling on an already-touched
4601
4615
  * field is a no-op. Touched is sticky-true; pair with
4602
4616
  * `form.reset()` / `form.resetField()` to clear.
4603
4617
  */
4604
4618
  touch: (path?: FlatPath<Form> | (string | number)[]) => void;
4619
+ /**
4620
+ * Simulate a complete focus → edit → blur over every leaf under
4621
+ * `path` (the whole form when omitted), so values that arrived
4622
+ * without a DOM gesture reveal their errors under the default
4623
+ * display heuristic.
4624
+ *
4625
+ * ```ts
4626
+ * form.interact('profile') // arm one subtree
4627
+ * await form.interact(['members', 2]) // errors committed on resolve
4628
+ * form.interact() // every leaf in the form
4629
+ * ```
4630
+ *
4631
+ * Flips the full interaction ladder (`touched`, `interacted`, and
4632
+ * `blurredAfterInteraction`) and runs the subtree's validation, so
4633
+ * the default gate — `submissionAttempts > 0 ||
4634
+ * blurredAfterInteraction` — opens through its front door. Use it
4635
+ * to arm one region's errors without a form-wide submit, which
4636
+ * would light up every other field on the page: a field-array row
4637
+ * edited in a modal, a server-seeded section, a pasted import.
4638
+ *
4639
+ * Walks schema leaves, so it reaches fields that are currently
4640
+ * `v-if`'d away or were never mounted; the flags are sticky, so
4641
+ * such a subtree stays revealed when it remounts.
4642
+ *
4643
+ * Flags land synchronously — the returned promise resolves once the
4644
+ * subtree's validation has committed, so an awaiting caller can read
4645
+ * `showErrors` / `errors` immediately after. It never rejects, and
4646
+ * ignoring it is fine.
4647
+ *
4648
+ * No-op on a disabled form, matching every other interaction-
4649
+ * lifecycle write. Does not mutate value, `focused`, or `blurred`:
4650
+ * those are DOM-owned, and the display gate reads none of them.
4651
+ * Sticky like `touched`; pair with `form.reset()` /
4652
+ * `form.resetField()` to clear.
4653
+ */
4654
+ interact: (path?: FlatPath<Form> | (string | number)[]) => Promise<void>;
4605
4655
  /**
4606
4656
  * Append `value` to the array at `path`.
4607
4657
  *
@@ -3637,12 +3637,20 @@ type FieldStateMap<Form extends GenericForm> = LeafWalker<Form, 'field'> & {
3637
3637
  */
3638
3638
  <P extends string>(path: P): FieldCallResult<Form, P>;
3639
3639
  /**
3640
- * Tuple-segment form. Returns the typed `FieldStateMapEntry` for
3641
- * the resolved path when the tuple resolves to a known path.
3642
- * Equivalent to `form.fields[a][b][...]` but useful when the path
3643
- * is built from variables.
3640
+ * Tuple-segment form. The segment-array spelling of the string
3641
+ * call-form above, for a path built from variables — the two resolve
3642
+ * to the same proxy, so `form.fields(['users', i, 'email'])` and
3643
+ * `form.fields(\`users.${i}.email\`)` are interchangeable.
3644
+ *
3645
+ * Resolves to that path's `FieldState`, which at a container is its
3646
+ * rolled-up aggregate (`displayState`, `valid`, `dirty`, ... all
3647
+ * exist regardless of the value type), exactly as the string form
3648
+ * does. Note this is the CALL form: it hands back the path's state,
3649
+ * not a drillable sub-proxy. To walk into a container's children use
3650
+ * dot/bracket access (`form.fields.users[i].email`), which is what
3651
+ * descends.
3644
3652
  */
3645
- <const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown : never)): FieldStateMapEntry<NestedType<Form, JoinSegments<S>>>;
3653
+ <const S extends ReadonlyArray<string | number>>(segments: S & ([JoinSegments<S>] extends [FlatPath<Form>] ? unknown : never)): FieldState<NestedType<Form, JoinSegments<S>>>;
3646
3654
  /**
3647
3655
  * Dynamic-array fallback for callers passing `Path`-typed (runtime)
3648
3656
  * segment arrays — e.g. forwarding `RegisterValue.segments` to
@@ -4583,11 +4591,8 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
4583
4591
  */
4584
4592
  applyInvalidSubmitPolicy: (policy?: OnInvalidSubmitPolicy) => void;
4585
4593
  /**
4586
- * Programmatically mark fields as touched — the sticky flag the
4587
- * standard "show errors after interaction" pattern reads. Closes
4588
- * the gap when fields are populated without a DOM gesture (post-
4589
- * import, paste, autofill, server-seeded values you want to
4590
- * validate immediately).
4594
+ * Programmatically mark fields as `touched` — the descriptive
4595
+ * "this field was visited" flag.
4591
4596
  *
4592
4597
  * ```ts
4593
4598
  * form.touch('email') // one leaf
@@ -4596,12 +4601,57 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
4596
4601
  * form.touch() // every leaf in the form
4597
4602
  * ```
4598
4603
  *
4604
+ * **This does not reveal errors under the default display
4605
+ * heuristic.** `touched` also flips on a bare focus → blur with no
4606
+ * edit, so the library-default gate deliberately ignores it and
4607
+ * reads `blurredAfterInteraction` instead — the stricter bit that
4608
+ * only a blur *following* an edit sets. Reach for `touch()` when a
4609
+ * custom `getDisplayState` reducer or your analytics reads
4610
+ * `touched`; reach for {@link UseFormReturnType.interact} to make
4611
+ * seeded or imported values surface their errors.
4612
+ *
4599
4613
  * Pure flag write — does not mutate value, focused, blurred, or
4600
4614
  * trigger validation. Idempotent: re-calling on an already-touched
4601
4615
  * field is a no-op. Touched is sticky-true; pair with
4602
4616
  * `form.reset()` / `form.resetField()` to clear.
4603
4617
  */
4604
4618
  touch: (path?: FlatPath<Form> | (string | number)[]) => void;
4619
+ /**
4620
+ * Simulate a complete focus → edit → blur over every leaf under
4621
+ * `path` (the whole form when omitted), so values that arrived
4622
+ * without a DOM gesture reveal their errors under the default
4623
+ * display heuristic.
4624
+ *
4625
+ * ```ts
4626
+ * form.interact('profile') // arm one subtree
4627
+ * await form.interact(['members', 2]) // errors committed on resolve
4628
+ * form.interact() // every leaf in the form
4629
+ * ```
4630
+ *
4631
+ * Flips the full interaction ladder (`touched`, `interacted`, and
4632
+ * `blurredAfterInteraction`) and runs the subtree's validation, so
4633
+ * the default gate — `submissionAttempts > 0 ||
4634
+ * blurredAfterInteraction` — opens through its front door. Use it
4635
+ * to arm one region's errors without a form-wide submit, which
4636
+ * would light up every other field on the page: a field-array row
4637
+ * edited in a modal, a server-seeded section, a pasted import.
4638
+ *
4639
+ * Walks schema leaves, so it reaches fields that are currently
4640
+ * `v-if`'d away or were never mounted; the flags are sticky, so
4641
+ * such a subtree stays revealed when it remounts.
4642
+ *
4643
+ * Flags land synchronously — the returned promise resolves once the
4644
+ * subtree's validation has committed, so an awaiting caller can read
4645
+ * `showErrors` / `errors` immediately after. It never rejects, and
4646
+ * ignoring it is fine.
4647
+ *
4648
+ * No-op on a disabled form, matching every other interaction-
4649
+ * lifecycle write. Does not mutate value, `focused`, or `blurred`:
4650
+ * those are DOM-owned, and the display gate reads none of them.
4651
+ * Sticky like `touched`; pair with `form.reset()` /
4652
+ * `form.resetField()` to clear.
4653
+ */
4654
+ interact: (path?: FlatPath<Form> | (string | number)[]) => Promise<void>;
4605
4655
  /**
4606
4656
  * Append `value` to the array at `path`.
4607
4657
  *
@@ -626,7 +626,9 @@ function computeVerdict(field, formMeta) {
626
626
  (e) => e.path.length === field.path.length && e.path.every((s, i) => s === field.path[i])
627
627
  );
628
628
  if (hasOwnError) return "error";
629
- if (field.valid === true && field.blank !== true && field.dirty === true) return "success";
629
+ if (field.valid === true && field.blank !== true && (field.dirty === true || field.interacted === true)) {
630
+ return "success";
631
+ }
630
632
  return "idle";
631
633
  }
632
634
  function earliestNonNull(a, b) {
@@ -3314,6 +3316,14 @@ function buildFormApi(state, formInstanceId, options = {}) {
3314
3316
  const segments = pathInput === void 0 ? devtoolsShared.ROOT_PATH : devtoolsShared.canonicalizePath(pathInput).segments;
3315
3317
  state.touchAtPath(segments);
3316
3318
  }
3319
+ async function interact(pathInput) {
3320
+ const segments = pathInput === void 0 ? devtoolsShared.ROOT_PATH : devtoolsShared.canonicalizePath(pathInput).segments;
3321
+ if (!state.interactAtPath(segments)) return;
3322
+ try {
3323
+ await validateAsyncBuilt(pathInput === void 0 ? void 0 : segments);
3324
+ } catch {
3325
+ }
3326
+ }
3317
3327
  const focusFirstError = (options2) => {
3318
3328
  const target = state.getFirstErrorElement(formInstanceId);
3319
3329
  if (target === null) return false;
@@ -3460,6 +3470,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
3460
3470
  scrollToFirstError: gated(scrollToFirstError),
3461
3471
  applyInvalidSubmitPolicy: gated(applyInvalidSubmitPolicyPublic),
3462
3472
  touch: gated(touch),
3473
+ interact: gated(interact),
3463
3474
  get history() {
3464
3475
  void state.activate();
3465
3476
  return formHistory;
@@ -5275,6 +5286,32 @@ function createFormStore(options) {
5275
5286
  );
5276
5287
  }
5277
5288
  }
5289
+ function interactAtPath(segments) {
5290
+ if (effectiveDisabled.value) return false;
5291
+ const formValue = form.value;
5292
+ let interactedAny = false;
5293
+ for (const [, entry] of originals) {
5294
+ if (!devtoolsShared.isPathPrefix(segments, entry.segments)) continue;
5295
+ if (!hasAtPath(formValue, entry.segments)) continue;
5296
+ interactedAny = true;
5297
+ const leafKey = devtoolsShared.canonicalizePath(entry.segments).key;
5298
+ const current = fields.get(leafKey);
5299
+ if (current?.touched === true && current.interacted === true && current.blurredAfterInteraction === true) {
5300
+ continue;
5301
+ }
5302
+ touchFieldRecord(leafKey, entry.segments, {
5303
+ touched: true,
5304
+ interacted: true,
5305
+ blurredAfterInteraction: true
5306
+ });
5307
+ }
5308
+ if (!interactedAny && devtoolsShared.__DEV__) {
5309
+ console.warn(
5310
+ `[attaform] form.interact(): no fields resolved at path ${JSON.stringify(segments)}. Check the path matches an existing field or container.`
5311
+ );
5312
+ }
5313
+ return interactedAny;
5314
+ }
5278
5315
  function rehydrate() {
5279
5316
  const factory = defaultValuesFactory.value;
5280
5317
  if (factory === void 0) {
@@ -5629,6 +5666,7 @@ function createFormStore(options) {
5629
5666
  markFocused,
5630
5667
  markInteracted,
5631
5668
  touchAtPath,
5669
+ interactAtPath,
5632
5670
  markConnectedOptimistically,
5633
5671
  markHostConnected,
5634
5672
  isPristineAtPath,
@@ -7656,4 +7694,4 @@ exports.unset = unset;
7656
7694
  exports.useAbstractForm = useAbstractForm;
7657
7695
  exports.useRegister = useRegister;
7658
7696
  exports.useWizard = useWizard;
7659
- //# sourceMappingURL=attaform.Cg1rUfcF.cjs.map
7697
+ //# sourceMappingURL=attaform.DrvMscNX.cjs.map