attaform 0.24.4 → 0.26.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 (92) hide show
  1. package/dist/chunks/devtools.cjs +1 -1
  2. package/dist/chunks/devtools.mjs +1 -1
  3. package/dist/chunks/fingerprint2.cjs +1 -1
  4. package/dist/chunks/fingerprint2.mjs +1 -1
  5. package/dist/index.cjs +4 -4
  6. package/dist/index.d.cts +5 -5
  7. package/dist/index.d.mts +5 -5
  8. package/dist/index.d.ts +5 -5
  9. package/dist/index.mjs +4 -4
  10. package/dist/nuxt.d.cts +1 -1
  11. package/dist/nuxt.d.mts +1 -1
  12. package/dist/nuxt.d.ts +1 -1
  13. package/dist/runtime/plugins/attaform.cjs +2 -2
  14. package/dist/runtime/plugins/attaform.mjs +2 -2
  15. package/dist/shared/{attaform.CO0e7YVY.d.cts → attaform.55hFR_cl.d.ts} +3 -0
  16. package/dist/shared/{attaform.Ib7oSpxj.d.cts → attaform.B71rfNL5.d.mts} +1 -1
  17. package/dist/shared/{attaform.DTn1J0Eq.d.mts → attaform.BFfY7HRI.d.cts} +1 -1
  18. package/dist/shared/{attaform.CAVK56f8.cjs → attaform.BPl-ECRT.cjs} +49 -2
  19. package/dist/shared/attaform.BPl-ECRT.cjs.map +1 -0
  20. package/dist/shared/{attaform.CBMI57RP.cjs → attaform.BQflyVOL.cjs} +3 -3
  21. package/dist/shared/{attaform.CBMI57RP.cjs.map → attaform.BQflyVOL.cjs.map} +1 -1
  22. package/dist/shared/{attaform.DWKcw1ud.cjs → attaform.BRzKBj2g.cjs} +101 -4
  23. package/dist/shared/attaform.BRzKBj2g.cjs.map +1 -0
  24. package/dist/shared/{attaform.EeVCVnoH.d.ts → attaform.Be5otUVw.d.ts} +1 -1
  25. package/dist/shared/{attaform.iJ4y3sHU.mjs → attaform.BoYqnIPI.mjs} +223 -81
  26. package/dist/shared/attaform.BoYqnIPI.mjs.map +1 -0
  27. package/dist/shared/{attaform.B1N47eDM.d.ts → attaform.CEY_WLRJ.d.mts} +127 -56
  28. package/dist/shared/{attaform.CwqTCeTZ.mjs → attaform.CSXDW2tO.mjs} +4 -4
  29. package/dist/shared/{attaform.CwqTCeTZ.mjs.map → attaform.CSXDW2tO.mjs.map} +1 -1
  30. package/dist/shared/{attaform.lcTiMoma.mjs → attaform.CXVmZtFq.mjs} +3 -3
  31. package/dist/shared/{attaform.lcTiMoma.mjs.map → attaform.CXVmZtFq.mjs.map} +1 -1
  32. package/dist/shared/{attaform.CE-KJZ0j.cjs → attaform.Cs0UZ7q6.cjs} +4 -4
  33. package/dist/shared/{attaform.CE-KJZ0j.cjs.map → attaform.Cs0UZ7q6.cjs.map} +1 -1
  34. package/dist/shared/{attaform.AkX9wzfY.cjs → attaform.D51IwDtP.cjs} +2 -2
  35. package/dist/shared/{attaform.AkX9wzfY.cjs.map → attaform.D51IwDtP.cjs.map} +1 -1
  36. package/dist/shared/{attaform.QFrCtJBO.d.mts → attaform.DBNjHgNj.d.cts} +86 -17
  37. package/dist/shared/{attaform.QFrCtJBO.d.cts → attaform.DBNjHgNj.d.mts} +86 -17
  38. package/dist/shared/{attaform.QFrCtJBO.d.ts → attaform.DBNjHgNj.d.ts} +86 -17
  39. package/dist/shared/{attaform.DwsfkfSY.mjs → attaform.DGhIix5D.mjs} +48 -3
  40. package/dist/shared/attaform.DGhIix5D.mjs.map +1 -0
  41. package/dist/shared/{attaform.CO0e7YVY.d.mts → attaform.DJRz_bzM.d.cts} +3 -0
  42. package/dist/shared/{attaform.ChY7tIrn.mjs → attaform.DSOIz08n.mjs} +4 -4
  43. package/dist/shared/{attaform.ChY7tIrn.mjs.map → attaform.DSOIz08n.mjs.map} +1 -1
  44. package/dist/shared/{attaform.Buedl_UL.cjs → attaform.Du9RZp8m.cjs} +223 -81
  45. package/dist/shared/attaform.Du9RZp8m.cjs.map +1 -0
  46. package/dist/shared/{attaform.CO0e7YVY.d.ts → attaform.JPsaqjLl.d.mts} +3 -0
  47. package/dist/shared/{attaform.CqW4gF_Z.d.cts → attaform.MgaYKlPP.d.cts} +127 -56
  48. package/dist/shared/{attaform.CZpUm-2l.mjs → attaform.NqYEzRcM.mjs} +2 -2
  49. package/dist/shared/{attaform.CZpUm-2l.mjs.map → attaform.NqYEzRcM.mjs.map} +1 -1
  50. package/dist/shared/{attaform.WvcckZMD.mjs → attaform.Vp2-CWZB.mjs} +3 -2
  51. package/dist/shared/attaform.Vp2-CWZB.mjs.map +1 -0
  52. package/dist/shared/{attaform.CjdepGnw.cjs → attaform.ZXZ6dH72.cjs} +3 -1
  53. package/dist/shared/attaform.ZXZ6dH72.cjs.map +1 -0
  54. package/dist/shared/{attaform.f0l5ydy1.d.mts → attaform.ceYeVkjn.d.ts} +127 -56
  55. package/dist/shared/{attaform.QwLSdxuk.mjs → attaform.k9V0py0s.mjs} +101 -5
  56. package/dist/shared/attaform.k9V0py0s.mjs.map +1 -0
  57. package/dist/shared/{attaform.Bnm0FQaH.cjs → attaform.tZ0YKisf.cjs} +4 -4
  58. package/dist/shared/{attaform.Bnm0FQaH.cjs.map → attaform.tZ0YKisf.cjs.map} +1 -1
  59. package/dist/transforms.cjs +2 -1
  60. package/dist/transforms.cjs.map +1 -1
  61. package/dist/transforms.d.cts +13 -1
  62. package/dist/transforms.d.mts +13 -1
  63. package/dist/transforms.d.ts +13 -1
  64. package/dist/transforms.mjs +1 -1
  65. package/dist/vite.cjs +2 -1
  66. package/dist/vite.cjs.map +1 -1
  67. package/dist/vite.mjs +2 -1
  68. package/dist/vite.mjs.map +1 -1
  69. package/dist/zod-v3.cjs +2 -2
  70. package/dist/zod-v3.d.cts +3 -3
  71. package/dist/zod-v3.d.mts +3 -3
  72. package/dist/zod-v3.d.ts +3 -3
  73. package/dist/zod-v3.mjs +2 -2
  74. package/dist/zod-v4.cjs +2 -2
  75. package/dist/zod-v4.d.cts +6 -6
  76. package/dist/zod-v4.d.mts +6 -6
  77. package/dist/zod-v4.d.ts +6 -6
  78. package/dist/zod-v4.mjs +2 -2
  79. package/dist/zod.cjs +5 -5
  80. package/dist/zod.d.cts +5 -5
  81. package/dist/zod.d.mts +5 -5
  82. package/dist/zod.d.ts +5 -5
  83. package/dist/zod.mjs +5 -5
  84. package/package.json +1 -1
  85. package/dist/shared/attaform.Buedl_UL.cjs.map +0 -1
  86. package/dist/shared/attaform.CAVK56f8.cjs.map +0 -1
  87. package/dist/shared/attaform.CjdepGnw.cjs.map +0 -1
  88. package/dist/shared/attaform.DWKcw1ud.cjs.map +0 -1
  89. package/dist/shared/attaform.DwsfkfSY.mjs.map +0 -1
  90. package/dist/shared/attaform.QwLSdxuk.mjs.map +0 -1
  91. package/dist/shared/attaform.WvcckZMD.mjs.map +0 -1
  92. package/dist/shared/attaform.iJ4y3sHU.mjs.map +0 -1
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  const vue = require('vue');
4
- const paths = require('./attaform.CAVK56f8.cjs');
5
- const registerProtocol = require('./attaform.CjdepGnw.cjs');
4
+ const paths = require('./attaform.BPl-ECRT.cjs');
5
+ const registerProtocol = require('./attaform.ZXZ6dH72.cjs');
6
6
 
7
7
  function safeAssign(target, key, value) {
8
8
  if (key === "__proto__") {
@@ -584,7 +584,18 @@ const AttaformErrorCode = {
584
584
  * produced `ValidationError` carrying a stable, branchable identifier.
585
585
  * Override it per error by passing your own `code` (`api:…`, `auth:…`).
586
586
  */
587
- UserError: "atta:user-error"
587
+ UserError: "atta:user-error",
588
+ /**
589
+ * Stamped on a `ValidationError` synthesized from a throw or rejection
590
+ * out of a `handleSubmit` `onSubmit` callback. The raw error also lands
591
+ * on `form.meta.submitError` (a raw-`Error` inspection channel); this
592
+ * code marks the copy piped into the user-error layer so the failure
593
+ * also surfaces on `form.errors` / `meta.ownErrors` / `firstOwnError`
594
+ * where the UI already reads. A throw carrying its own `path` / `code`
595
+ * keeps them; a bare `Error` or a non-Error throw defaults here at the
596
+ * form-level path `[]`.
597
+ */
598
+ SubmitError: "atta:submit-error"
588
599
  };
589
600
  function makeBlankRequiredError(segments, formKey) {
590
601
  return {
@@ -792,6 +803,11 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
792
803
  elements: elementsArr,
793
804
  updatedAt: record?.updatedAt ?? null,
794
805
  errors,
806
+ // A leaf has no descendants, so its own-bucket errors ARE its subtree
807
+ // errors. Reuse the same array reference: referential `ownErrors ===
808
+ // errors`, and no second read of the whole-form `derivedBlankErrors`
809
+ // map (preserves the P3 blank-reactivity optimization above).
810
+ ownErrors: errors,
795
811
  validating,
796
812
  valid,
797
813
  transforming,
@@ -970,6 +986,12 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
970
986
  elements: EMPTY_ELEMENTS,
971
987
  updatedAt,
972
988
  errors,
989
+ // The container's OWN bucket only (this exact path), excluding every
990
+ // descendant that `errors` rolls up: a cross-field `.refine()` here,
991
+ // or `setErrors` pinned at this node. The same raw union the leaf
992
+ // builds from the three stores; the container already depends on all
993
+ // three via `aggregateErrorsAt`, so there's no new reactivity cost.
994
+ ownErrors: state.getErrorsForPath(segments),
973
995
  validating,
974
996
  valid,
975
997
  transforming,
@@ -1008,6 +1030,7 @@ function buildContainerFieldState(state, segments, key, formInstanceId, getFormM
1008
1030
  }
1009
1031
  function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingSince, transformingSince, revealedDescendantError, isRoot, getDisplayState) {
1010
1032
  const firstError = base.errors[0];
1033
+ const firstOwnError = base.ownErrors[0];
1011
1034
  const predicate = getDisplayState ?? state.getDisplayState;
1012
1035
  const formMeta = getFormMetaBase();
1013
1036
  const ctx = {
@@ -1043,7 +1066,8 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingS
1043
1066
  showPending: displayState === "pending",
1044
1067
  showSuccess: displayState === "success",
1045
1068
  showIdle: displayState === "idle",
1046
- firstError
1069
+ firstError,
1070
+ firstOwnError
1047
1071
  };
1048
1072
  }
1049
1073
  function aggregateErrorsAt(state, prefix) {
@@ -1412,17 +1436,15 @@ function buildErrorsProxy(state) {
1412
1436
  // shared `aggregateErrorsAt` helper that `form.meta.errors` and
1413
1437
  // `form.fields(path).errors` also use, so the surfaces never drift.
1414
1438
  //
1415
- // The root is the one carve-out. An EXPLICIT root path
1416
- // (`errors([])`) returns ONLY the global `[]` bucket (root
1417
- // `.refine()`, hydration failures, `setErrors`) via
1418
- // `getErrorsForPath`, giving consumers a dedicated channel for
1419
- // global messages undiluted by field errors. The no-arg `errors()`
1420
- // instead resolves the FULL aggregate through `resolveRootCall`
1421
- // below (identical to `meta.errors`).
1422
- resolveCallTarget: (path) => path.length === 0 ? state.getErrorsForPath([]) : aggregateErrorsAt(state, path),
1439
+ // The root is uniform with every other path: `errors([])` is the
1440
+ // subtree aggregate at `[]`, which is the whole form, identical to
1441
+ // the no-arg `errors()` and to `meta.errors`. The root `[]` bucket
1442
+ // alone (root `.refine()`, hydration failures, path-less
1443
+ // `setErrors`) is read through `meta.ownErrors` / `meta.firstOwnError`,
1444
+ // the dedicated own-bucket accessor, not a call-form carve-out.
1445
+ resolveCallTarget: (path) => aggregateErrorsAt(state, path),
1423
1446
  // No-arg `errors()` = the whole-form aggregate, matching
1424
- // `meta.errors`. Distinct from `errors([])` (global bucket only);
1425
- // see `resolveCallTarget`.
1447
+ // `meta.errors` and `errors([])`. All three are the same aggregate.
1426
1448
  resolveRootCall: () => aggregateErrorsAt(state, []),
1427
1449
  // Enumeration unions the live form-data keys at this path with the
1428
1450
  // first-child segments drawn from every error store. Without the
@@ -1606,6 +1628,7 @@ const FIELD_STATE_KEYS = /* @__PURE__ */ new Set([
1606
1628
  "elements",
1607
1629
  "updatedAt",
1608
1630
  "errors",
1631
+ "ownErrors",
1609
1632
  "validating",
1610
1633
  "valid",
1611
1634
  "transforming",
@@ -1617,6 +1640,7 @@ const FIELD_STATE_KEYS = /* @__PURE__ */ new Set([
1617
1640
  "showSuccess",
1618
1641
  "showIdle",
1619
1642
  "firstError",
1643
+ "firstOwnError",
1620
1644
  "path",
1621
1645
  "id",
1622
1646
  "aria",
@@ -1758,6 +1782,32 @@ function walk$1(value, basePath, schema, snapshotFieldStateAt) {
1758
1782
  }
1759
1783
 
1760
1784
  const warnedNoScopeStores = paths.__DEV__ ? /* @__PURE__ */ new WeakSet() : null;
1785
+ function isErrorInputLike(value) {
1786
+ if (value instanceof Error) return true;
1787
+ if (typeof value !== "object" || value === null) return false;
1788
+ const shape = value;
1789
+ return typeof shape.message === "string" && shape.message.length > 0 || Array.isArray(shape.path);
1790
+ }
1791
+ function deriveSubmitErrors(err, formKey) {
1792
+ if (Array.isArray(err) && err.length > 0 && err.every(isErrorInputLike)) {
1793
+ return {
1794
+ entries: err.map(
1795
+ (item) => paths.normalizeErrorInput(item, void 0, formKey, AttaformErrorCode.SubmitError)
1796
+ ),
1797
+ messageless: false
1798
+ };
1799
+ }
1800
+ if (isErrorInputLike(err)) {
1801
+ return {
1802
+ entries: [paths.normalizeErrorInput(err, void 0, formKey, AttaformErrorCode.SubmitError)],
1803
+ messageless: false
1804
+ };
1805
+ }
1806
+ return {
1807
+ entries: [paths.normalizeErrorInput({}, void 0, formKey, AttaformErrorCode.SubmitError)],
1808
+ messageless: true
1809
+ };
1810
+ }
1761
1811
  function buildProcessForm(state, formInstanceId, options = {}) {
1762
1812
  const invalidPolicy = options.onInvalidSubmit ?? "focus-first-error";
1763
1813
  function validate(pathInput) {
@@ -1939,13 +1989,6 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1939
1989
  if (state.submissionGeneration.value === genAtEntry) {
1940
1990
  applyInvalidSubmitPolicy(state, formInstanceId, invalidPolicy);
1941
1991
  }
1942
- if (onError !== void 0) {
1943
- try {
1944
- await onError(Array.from(state.userErrors.values()).flat());
1945
- } catch (cause) {
1946
- throw new paths.SubmitErrorHandlerError("User-provided onError threw", { cause });
1947
- }
1948
- }
1949
1992
  return;
1950
1993
  }
1951
1994
  if (state.submissionGeneration.value === genAtEntry) {
@@ -1955,6 +1998,25 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1955
1998
  } catch (err) {
1956
1999
  if (state.submissionGeneration.value === genAtEntry) {
1957
2000
  state.submitError.value = paths.toError(err);
2001
+ if (!(err instanceof paths.SubmitErrorHandlerError)) {
2002
+ const { entries, messageless } = deriveSubmitErrors(err, state.formKey);
2003
+ const byPath = /* @__PURE__ */ new Map();
2004
+ for (const entry of entries) {
2005
+ const { key } = paths.canonicalizePath(entry.path);
2006
+ const bucket = byPath.get(key);
2007
+ if (bucket === void 0) byPath.set(key, { segments: entry.path, entries: [entry] });
2008
+ else bucket.entries.push(entry);
2009
+ }
2010
+ for (const { segments, entries: bucket } of byPath.values()) {
2011
+ state.setUserErrorsForPath(segments, bucket);
2012
+ }
2013
+ applyInvalidSubmitPolicy(state, formInstanceId, invalidPolicy);
2014
+ if (paths.__DEV__ && messageless) {
2015
+ console.warn(
2016
+ "[attaform] handleSubmit callback threw a non-Error value; throw an Error or a ValidationError ({ message, path? }) so the failure surfaces with a usable message."
2017
+ );
2018
+ }
2019
+ }
1958
2020
  }
1959
2021
  } finally {
1960
2022
  if (!validationSettled) {
@@ -2020,11 +2082,11 @@ function applyInvalidSubmitPolicy(state, formInstanceId, policy) {
2020
2082
  return;
2021
2083
  }
2022
2084
  if (policy === "focus-first-error") {
2023
- target.element.focus();
2085
+ target.element.focus({ focusVisible: true });
2024
2086
  return;
2025
2087
  }
2026
2088
  target.element.scrollIntoView();
2027
- target.element.focus({ preventScroll: true });
2089
+ target.element.focus({ preventScroll: true, focusVisible: true });
2028
2090
  }
2029
2091
 
2030
2092
  const warnedRejections = paths.__DEV__ ? /* @__PURE__ */ new WeakMap() : null;
@@ -2816,6 +2878,9 @@ function buildFormApi(state, formInstanceId, options = {}) {
2816
2878
  get errors() {
2817
2879
  return rootBase().errors;
2818
2880
  },
2881
+ get ownErrors() {
2882
+ return rootBase().ownErrors;
2883
+ },
2819
2884
  get validating() {
2820
2885
  return rootBase().validating;
2821
2886
  },
@@ -2979,28 +3044,6 @@ function buildFormApi(state, formInstanceId, options = {}) {
2979
3044
  return true;
2980
3045
  }
2981
3046
  const errorsProxy = buildErrorsProxy(state);
2982
- function normalizeErrorInput(item, scope) {
2983
- if (item instanceof Error) {
2984
- return {
2985
- message: item.message.length > 0 ? item.message : "Unknown error",
2986
- path: scope !== void 0 ? [...scope] : [],
2987
- formKey: state.formKey,
2988
- code: AttaformErrorCode.UserError
2989
- };
2990
- }
2991
- const entry = {
2992
- message: typeof item.message === "string" && item.message.length > 0 ? item.message : "Unknown error",
2993
- path: scope !== void 0 ? [...scope] : Array.isArray(item.path) ? [...item.path] : [],
2994
- formKey: state.formKey,
2995
- code: typeof item.code === "string" && item.code.length > 0 ? item.code : AttaformErrorCode.UserError
2996
- };
2997
- if (item.data !== void 0) entry.data = item.data;
2998
- return entry;
2999
- }
3000
- function normalizeErrorInputs(input, scope) {
3001
- const items = Array.isArray(input) ? input : [input];
3002
- return items.map((item) => normalizeErrorInput(item, scope));
3003
- }
3004
3047
  function flattenUserErrors() {
3005
3048
  const all = [];
3006
3049
  for (const errs of state.userErrors.values()) all.push(...errs);
@@ -3012,12 +3055,17 @@ function buildFormApi(state, formInstanceId, options = {}) {
3012
3055
  const { segments, key } = paths.canonicalizePath(arg1);
3013
3056
  const input2 = arg2;
3014
3057
  const resolved2 = typeof input2 === "function" ? input2((state.userErrors.get(key) ?? []).slice()) : input2;
3015
- state.setUserErrorsForPath(segments, normalizeErrorInputs(resolved2, segments));
3058
+ state.setUserErrorsForPath(
3059
+ segments,
3060
+ paths.normalizeErrorInputs(resolved2, segments, state.formKey, AttaformErrorCode.UserError)
3061
+ );
3016
3062
  return;
3017
3063
  }
3018
3064
  const input = arg1;
3019
3065
  const resolved = typeof input === "function" ? input(flattenUserErrors()) : input;
3020
- state.setAllUserErrors(normalizeErrorInputs(resolved, void 0));
3066
+ state.setAllUserErrors(
3067
+ paths.normalizeErrorInputs(resolved, void 0, state.formKey, AttaformErrorCode.UserError)
3068
+ );
3021
3069
  }
3022
3070
  function clearErrors(path) {
3023
3071
  if (path === void 0) {
@@ -3112,6 +3160,17 @@ function buildFormApi(state, formInstanceId, options = {}) {
3112
3160
  // keep the explicit form-level computation for the gate.
3113
3161
  valid,
3114
3162
  errors: metaErrors,
3163
+ // The root `[]` bucket alone: form-level errors from a root
3164
+ // `.refine()` or a path-less `setErrors`, excluding every field error
3165
+ // that `errors` (the whole-form aggregate) rolls up. This is the
3166
+ // banner accessor. Mirrors `rootFieldState` rather than taking a
3167
+ // dedicated computed: `getFieldState([])` routes a container root
3168
+ // through `getErrorsForPath([])` and a primitive root through its own
3169
+ // bucket, and both equal the root bucket, so the root field state is
3170
+ // already the single source of truth.
3171
+ get ownErrors() {
3172
+ return rootFieldState.value.ownErrors;
3173
+ },
3115
3174
  // Whole-form transforming mirrors the global `activeTransforms`
3116
3175
  // counter ORed with any per-leaf transform in flight (the root
3117
3176
  // rollup), exactly as `validating` composes its lifecycle and
@@ -3150,6 +3209,11 @@ function buildFormApi(state, formInstanceId, options = {}) {
3150
3209
  get firstError() {
3151
3210
  return rootFieldState.value.firstError;
3152
3211
  },
3212
+ // `ownErrors[0]`: the form's first top-level error. Pair it with
3213
+ // `ownErrors` for the form-level banner.
3214
+ get firstOwnError() {
3215
+ return rootFieldState.value.firstOwnError;
3216
+ },
3153
3217
  get path() {
3154
3218
  return rootFieldState.value.path;
3155
3219
  },
@@ -3229,7 +3293,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
3229
3293
  const focusFirstError = (options2) => {
3230
3294
  const target = state.getFirstErrorElement(formInstanceId);
3231
3295
  if (target === null) return false;
3232
- target.element.focus(options2);
3296
+ target.element.focus({ focusVisible: true, ...options2 });
3233
3297
  return true;
3234
3298
  };
3235
3299
  const scrollToFirstError = (options2) => {
@@ -5366,10 +5430,7 @@ function createFormStore(options) {
5366
5430
  if (elementToFormInstance.get(entry.element) !== formInstanceId) continue;
5367
5431
  if (!entry.element.isConnected) continue;
5368
5432
  if (entry.element.offsetParent === null) continue;
5369
- const { key } = paths.canonicalizePath(entry.path);
5370
- const hasSchemaErr = (schemaErrors.get(key)?.length ?? 0) > 0;
5371
- const hasUserErr = (userErrors.get(key)?.length ?? 0) > 0;
5372
- if (!hasSchemaErr && !hasUserErr) continue;
5433
+ if (getErrorsForPath(entry.path).length === 0) continue;
5373
5434
  return { path: entry.path, element: entry.element };
5374
5435
  }
5375
5436
  return null;
@@ -5949,7 +6010,7 @@ function resolveState(key, registry) {
5949
6010
  if (key !== void 0) {
5950
6011
  const stored = registry.forms.get(key);
5951
6012
  if (stored === void 0) {
5952
- warnMiss$1(`no form registered for key '${key}'`, registry.ssr);
6013
+ warnMiss$1(`no form registered for key '${key}'`, registry.ssr, registry.forms);
5953
6014
  return null;
5954
6015
  }
5955
6016
  return stored;
@@ -5959,12 +6020,22 @@ function resolveState(key, registry) {
5959
6020
  warnIfAmbientProviderHadDuplicates();
5960
6021
  return ambient;
5961
6022
  }
5962
- function warnMiss$1(detail, ssr) {
6023
+ function availableKeysHint$1(forms) {
6024
+ const addressable = [...forms.keys()].filter((key) => !key.startsWith(RESERVED_KEY_PREFIX));
6025
+ if (addressable.length === 0) return void 0;
6026
+ return `Registered keys: ${addressable.map((key) => `'${key}'`).join(", ")}.`;
6027
+ }
6028
+ function warnMiss$1(detail, ssr, forms) {
5963
6029
  if (!paths.__DEV__ || ssr) return;
5964
6030
  const frame = captureUserCallSite();
5965
- console.warn(
5966
- `[attaform] injectForm: ${detail}. Returning null.` + (frame !== void 0 ? ` ${frame}` : "")
6031
+ const parts = [`[attaform] injectForm: ${detail}. Returning null.`];
6032
+ const keys = availableKeysHint$1(forms);
6033
+ if (keys !== void 0) parts.push(keys);
6034
+ parts.push(
6035
+ `A form created by a child or sibling component is not registered until that component's own setup runs, which happens after this point. Lift its useForm({ key }) call to a common ancestor, or read the form after mount.`
5967
6036
  );
6037
+ if (frame !== void 0) parts.push(frame);
6038
+ console.warn(parts.join(" "));
5968
6039
  }
5969
6040
  function warnIfAmbientProviderHadDuplicates() {
5970
6041
  if (!paths.__DEV__ || ambientProvideHistory === null) return;
@@ -6224,6 +6295,9 @@ function asStatusSource(form) {
6224
6295
  function asSubmissionSource(form) {
6225
6296
  return form;
6226
6297
  }
6298
+ function asHandleSubmitSource(form) {
6299
+ return form;
6300
+ }
6227
6301
  function useWizard(options) {
6228
6302
  const rawSteps = Array.isArray(options.steps) ? options.steps : [];
6229
6303
  if (rawSteps.length === 0 && paths.__DEV__) {
@@ -6318,6 +6392,7 @@ function useWizard(options) {
6318
6392
  }
6319
6393
  };
6320
6394
  function resolveSlot(slot, index, ctx) {
6395
+ if (slot === void 0 || slot === null) return void 0;
6321
6396
  if (typeof slot === "string") {
6322
6397
  return getOrBuildNoop(slot);
6323
6398
  }
@@ -6333,7 +6408,7 @@ function useWizard(options) {
6333
6408
  return void 0;
6334
6409
  }
6335
6410
  function resolveSlotResult(result) {
6336
- if (result === void 0) return void 0;
6411
+ if (result === void 0 || result === null) return void 0;
6337
6412
  if (typeof result === "string") {
6338
6413
  return getOrBuildNoop(result);
6339
6414
  }
@@ -6401,6 +6476,50 @@ function useWizard(options) {
6401
6476
  const first = list[0];
6402
6477
  return first === void 0 ? void 0 : first.form;
6403
6478
  });
6479
+ vue.watch(compiledSteps, (list, prevList) => {
6480
+ const key = activeKey.value;
6481
+ if (key === "") return;
6482
+ if (list.some((step) => step.key === key)) return;
6483
+ if (list.length === 0) {
6484
+ activeKey.value = "";
6485
+ return;
6486
+ }
6487
+ const oldIndex = prevList.findIndex((step) => step.key === key);
6488
+ const slid = list[oldIndex < 0 ? 0 : Math.min(oldIndex, list.length - 1)];
6489
+ if (slid !== void 0) activeKey.value = slid.key;
6490
+ });
6491
+ const activeFormFacade = new Proxy({}, {
6492
+ get(_target, prop) {
6493
+ const form = activeForm.value;
6494
+ if (form === void 0) return void 0;
6495
+ if (prop === "handleSubmit") {
6496
+ const lateBound = (onValid, onInvalid) => {
6497
+ return (event) => {
6498
+ const current = activeForm.value;
6499
+ if (current === void 0) return Promise.resolve();
6500
+ return asHandleSubmitSource(current).handleSubmit(onValid, onInvalid)(event);
6501
+ };
6502
+ };
6503
+ return lateBound;
6504
+ }
6505
+ return Reflect.get(form, prop, form);
6506
+ },
6507
+ has(_target, prop) {
6508
+ const form = activeForm.value;
6509
+ return form === void 0 ? false : Reflect.has(form, prop);
6510
+ },
6511
+ ownKeys(_target) {
6512
+ const form = activeForm.value;
6513
+ return form === void 0 ? [] : Reflect.ownKeys(form);
6514
+ },
6515
+ getOwnPropertyDescriptor(_target, prop) {
6516
+ const form = activeForm.value;
6517
+ if (form === void 0) return void 0;
6518
+ const descriptor = Reflect.getOwnPropertyDescriptor(form, prop);
6519
+ if (descriptor === void 0) return void 0;
6520
+ return { ...descriptor, configurable: true };
6521
+ }
6522
+ });
6404
6523
  const isFinalStep = vue.computed(() => {
6405
6524
  const list = compiledSteps.value;
6406
6525
  const idx = activeIndex.value;
@@ -6765,6 +6884,39 @@ function useWizard(options) {
6765
6884
  const target = list[idx + 1];
6766
6885
  moveTo(target.key);
6767
6886
  }
6887
+ async function tryNext() {
6888
+ if (submitting.value) {
6889
+ if (paths.__DEV__) {
6890
+ console.warn(`[attaform] wizard.tryNext(): blocked while a submit is in flight.`);
6891
+ }
6892
+ return false;
6893
+ }
6894
+ const list = compiledSteps.value;
6895
+ if (list.length === 0) {
6896
+ if (paths.__DEV__) {
6897
+ console.warn(`[attaform] wizard.tryNext(): wizard has no compiled steps; no-op.`);
6898
+ }
6899
+ return false;
6900
+ }
6901
+ const idx = activeIndex.value;
6902
+ if (idx < 0 || idx >= list.length - 1) {
6903
+ if (paths.__DEV__) {
6904
+ console.warn(
6905
+ `[attaform] wizard.tryNext(): already on the final step ("${activeKey.value}"). Use wizard.handleSubmit() to submit.`
6906
+ );
6907
+ }
6908
+ return false;
6909
+ }
6910
+ const form = activeForm.value;
6911
+ if (form === void 0) return false;
6912
+ let advanced = false;
6913
+ await asHandleSubmitSource(form).handleSubmit(() => {
6914
+ const before = activeKey.value;
6915
+ next();
6916
+ advanced = activeKey.value !== before;
6917
+ })();
6918
+ return advanced;
6919
+ }
6768
6920
  function back() {
6769
6921
  if (submitting.value) {
6770
6922
  if (paths.__DEV__) {
@@ -6901,20 +7053,13 @@ function useWizard(options) {
6901
7053
  const final = isFinalStep.value;
6902
7054
  const list = compiledSteps.value;
6903
7055
  const results = /* @__PURE__ */ new Map();
6904
- if (final) {
6905
- await Promise.all(
6906
- list.map(async (step) => {
6907
- registry.forms.get(step.key)?.clearUserErrors();
6908
- const result = await processOne(step.form);
6909
- results.set(step.key, result);
6910
- })
6911
- );
6912
- } else {
6913
- const active = activeForm.value;
6914
- registry.forms.get(active.key)?.clearUserErrors();
6915
- const result = await processOne(active);
6916
- results.set(active.key, result);
6917
- }
7056
+ await Promise.all(
7057
+ list.map(async (step) => {
7058
+ registry.forms.get(step.key)?.clearUserErrors();
7059
+ const result = await processOne(step.form);
7060
+ results.set(step.key, result);
7061
+ })
7062
+ );
6918
7063
  for (const key of results.keys()) {
6919
7064
  const store = registry.forms.get(key);
6920
7065
  if (store !== void 0) {
@@ -6949,14 +7094,7 @@ function useWizard(options) {
6949
7094
  }
6950
7095
  return;
6951
7096
  }
6952
- if (final) {
6953
- done.value = true;
6954
- } else {
6955
- recordDeparture(currentKey);
6956
- const idx = activeIndex.value;
6957
- const target = list[idx + 1];
6958
- if (target !== void 0) moveTo(target.key);
6959
- }
7097
+ done.value = true;
6960
7098
  } else {
6961
7099
  await focusFirstWizardError(errors);
6962
7100
  if (onError !== void 0) {
@@ -7006,13 +7144,14 @@ function useWizard(options) {
7006
7144
  next,
7007
7145
  back,
7008
7146
  goTo,
7147
+ tryNext,
7009
7148
  handleSubmit,
7010
7149
  reset,
7011
7150
  get currentStep() {
7012
7151
  return currentStep.value;
7013
7152
  },
7014
7153
  get activeForm() {
7015
- return activeForm.value;
7154
+ return activeForm.value === void 0 ? void 0 : activeFormFacade;
7016
7155
  },
7017
7156
  get activeIndex() {
7018
7157
  return activeIndex.value;
@@ -7144,6 +7283,9 @@ function warnMiss(detail, ssr, hint) {
7144
7283
  const frame = captureUserCallSite();
7145
7284
  const parts = [`[attaform] injectWizard: ${detail}. Returning null.`];
7146
7285
  if (hint !== void 0) parts.push(hint);
7286
+ parts.push(
7287
+ `A wizard created by a child or sibling component is not registered until that component's own setup runs, which happens after this point. Lift its useWizard({ key }) call to a common ancestor, or read the wizard after mount.`
7288
+ );
7147
7289
  if (frame !== void 0) parts.push(frame);
7148
7290
  console.warn(parts.join(" "));
7149
7291
  }
@@ -7186,4 +7328,4 @@ exports.unset = unset;
7186
7328
  exports.useAbstractForm = useAbstractForm;
7187
7329
  exports.useRegister = useRegister;
7188
7330
  exports.useWizard = useWizard;
7189
- //# sourceMappingURL=attaform.Buedl_UL.cjs.map
7331
+ //# sourceMappingURL=attaform.Du9RZp8m.cjs.map