attaform 0.21.0 → 0.21.1

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 (83) hide show
  1. package/dist/chunks/dev-key-collision-warnings.cjs +1 -1
  2. package/dist/chunks/dev-key-collision-warnings.mjs +1 -1
  3. package/dist/chunks/devtools.cjs +1 -1
  4. package/dist/chunks/devtools.mjs +1 -1
  5. package/dist/chunks/fingerprint2.cjs +1 -1
  6. package/dist/chunks/fingerprint2.mjs +1 -1
  7. package/dist/chunks/indexeddb.cjs +1 -1
  8. package/dist/chunks/indexeddb.mjs +1 -1
  9. package/dist/chunks/local-storage.cjs +1 -1
  10. package/dist/chunks/local-storage.mjs +1 -1
  11. package/dist/chunks/multi-tab-sync.cjs +2 -2
  12. package/dist/chunks/multi-tab-sync.mjs +2 -2
  13. package/dist/chunks/session-storage.cjs +1 -1
  14. package/dist/chunks/session-storage.mjs +1 -1
  15. package/dist/chunks/wire-persistence.cjs +2 -2
  16. package/dist/chunks/wire-persistence.mjs +2 -2
  17. package/dist/index.cjs +3 -3
  18. package/dist/index.d.cts +15 -14
  19. package/dist/index.d.mts +15 -14
  20. package/dist/index.d.ts +15 -14
  21. package/dist/index.mjs +5 -5
  22. package/dist/nuxt.d.cts +1 -1
  23. package/dist/nuxt.d.mts +1 -1
  24. package/dist/nuxt.d.ts +1 -1
  25. package/dist/runtime/plugins/attaform.cjs +2 -2
  26. package/dist/runtime/plugins/attaform.mjs +2 -2
  27. package/dist/shared/{attaform.BzvOdiSI.cjs → attaform.BSkvn43g.cjs} +4 -4
  28. package/dist/shared/{attaform.BzvOdiSI.cjs.map → attaform.BSkvn43g.cjs.map} +1 -1
  29. package/dist/shared/{attaform.F8LMHHWV.d.cts → attaform.BWfliRIK.d.cts} +78 -2
  30. package/dist/shared/{attaform.r3PePkDR.mjs → attaform.Be8NZG9M.mjs} +9 -3
  31. package/dist/shared/attaform.Be8NZG9M.mjs.map +1 -0
  32. package/dist/shared/{attaform.CEf6wYfD.cjs → attaform.Bq5sX7TF.cjs} +2 -2
  33. package/dist/shared/{attaform.CEf6wYfD.cjs.map → attaform.Bq5sX7TF.cjs.map} +1 -1
  34. package/dist/shared/{attaform.7lzO9pdM.d.mts → attaform.Bv7dRDWK.d.ts} +78 -2
  35. package/dist/shared/{attaform._rsCZy2j.cjs → attaform.CICFZ1iS.cjs} +9 -3
  36. package/dist/shared/attaform.CICFZ1iS.cjs.map +1 -0
  37. package/dist/shared/{attaform.BUszFoKq.cjs → attaform.ClXwitZj.cjs} +366 -63
  38. package/dist/shared/attaform.ClXwitZj.cjs.map +1 -0
  39. package/dist/shared/{attaform.B1nyO4ec.d.cts → attaform.D0dWZsJt.d.cts} +269 -49
  40. package/dist/shared/{attaform.B1nyO4ec.d.mts → attaform.D0dWZsJt.d.mts} +269 -49
  41. package/dist/shared/{attaform.B1nyO4ec.d.ts → attaform.D0dWZsJt.d.ts} +269 -49
  42. package/dist/shared/{attaform.BA3vRDos.cjs → attaform.D32WwKk6.cjs} +214 -33
  43. package/dist/shared/attaform.D32WwKk6.cjs.map +1 -0
  44. package/dist/shared/{attaform.BnK_bfcb.mjs → attaform.DMEP_ENr.mjs} +4 -4
  45. package/dist/shared/{attaform.PnqML3xW.cjs.map → attaform.DMEP_ENr.mjs.map} +1 -1
  46. package/dist/shared/{attaform.DSqO6Db7.mjs → attaform.DR6RmxWZ.mjs} +367 -64
  47. package/dist/shared/attaform.DR6RmxWZ.mjs.map +1 -0
  48. package/dist/shared/{attaform.CkjTapyq.mjs → attaform.DozgVlCE.mjs} +4 -4
  49. package/dist/shared/{attaform.CkjTapyq.mjs.map → attaform.DozgVlCE.mjs.map} +1 -1
  50. package/dist/shared/{attaform.BK1RE2ha.d.ts → attaform.Duecg2NO.d.mts} +2 -2
  51. package/dist/shared/{attaform.BDIEq9qP.d.cts → attaform.FudOcHaa.d.cts} +2 -2
  52. package/dist/shared/{attaform.BQ6drorq.d.mts → attaform.MtrpT6Ki.d.ts} +2 -2
  53. package/dist/shared/{attaform.CRsXyy-Y.d.ts → attaform.NQ8mybyW.d.mts} +78 -2
  54. package/dist/shared/{attaform.PnqML3xW.cjs → attaform.S-pYLSo4.cjs} +4 -4
  55. package/dist/shared/{attaform.BnK_bfcb.mjs.map → attaform.S-pYLSo4.cjs.map} +1 -1
  56. package/dist/shared/{attaform.ezb5Nh2t.mjs → attaform.Y1ZGhM4k.mjs} +2 -2
  57. package/dist/shared/{attaform.ezb5Nh2t.mjs.map → attaform.Y1ZGhM4k.mjs.map} +1 -1
  58. package/dist/shared/{attaform.Y_Mgg0Yp.mjs → attaform.pmtahXKy.mjs} +214 -34
  59. package/dist/shared/attaform.pmtahXKy.mjs.map +1 -0
  60. package/dist/zod-v3.cjs +3 -3
  61. package/dist/zod-v3.d.cts +3 -3
  62. package/dist/zod-v3.d.mts +3 -3
  63. package/dist/zod-v3.d.ts +3 -3
  64. package/dist/zod-v3.mjs +3 -3
  65. package/dist/zod-v4.cjs +3 -3
  66. package/dist/zod-v4.d.cts +4 -4
  67. package/dist/zod-v4.d.mts +4 -4
  68. package/dist/zod-v4.d.ts +4 -4
  69. package/dist/zod-v4.mjs +3 -3
  70. package/dist/zod.cjs +5 -5
  71. package/dist/zod.cjs.map +1 -1
  72. package/dist/zod.d.cts +52 -10
  73. package/dist/zod.d.mts +52 -10
  74. package/dist/zod.d.ts +52 -10
  75. package/dist/zod.mjs +6 -6
  76. package/dist/zod.mjs.map +1 -1
  77. package/package.json +1 -1
  78. package/dist/shared/attaform.BA3vRDos.cjs.map +0 -1
  79. package/dist/shared/attaform.BUszFoKq.cjs.map +0 -1
  80. package/dist/shared/attaform.DSqO6Db7.mjs.map +0 -1
  81. package/dist/shared/attaform.Y_Mgg0Yp.mjs.map +0 -1
  82. package/dist/shared/attaform._rsCZy2j.cjs.map +0 -1
  83. package/dist/shared/attaform.r3PePkDR.mjs.map +0 -1
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const vue = require('vue');
4
- const paths = require('./attaform.BA3vRDos.cjs');
4
+ const paths = require('./attaform.D32WwKk6.cjs');
5
5
 
6
6
  function safeAssign(target, key, value) {
7
7
  if (key === "__proto__") {
@@ -15,10 +15,14 @@ function safeAssign(target, key, value) {
15
15
  }
16
16
  target[key] = value;
17
17
  }
18
+ function isShadowedKey(key) {
19
+ return key in Object.prototype;
20
+ }
18
21
  function safeOwnRead(target, key) {
19
- if (key === "__proto__") {
20
- const desc = Object.getOwnPropertyDescriptor(target, "__proto__");
21
- return desc?.value;
22
+ if (isShadowedKey(key)) {
23
+ const desc = Object.getOwnPropertyDescriptor(target, key);
24
+ if (desc === void 0) return void 0;
25
+ return "value" in desc ? desc.value : target[key];
22
26
  }
23
27
  return target[key];
24
28
  }
@@ -37,6 +41,10 @@ function descendStep(value, segment) {
37
41
  }
38
42
  const record = value;
39
43
  const key = typeof segment === "number" ? String(segment) : segment;
44
+ if (isShadowedKey(key)) {
45
+ if (!safeOwnHas(record, key)) return NOT_FOUND;
46
+ return safeOwnRead(record, key);
47
+ }
40
48
  if (!(key in record)) return NOT_FOUND;
41
49
  return record[key];
42
50
  }
@@ -66,6 +74,7 @@ function hasAtPath(root, path) {
66
74
  return typeof last === "number" && last >= 0 && last < current.length;
67
75
  }
68
76
  const key = typeof last === "number" ? String(last) : last;
77
+ if (isShadowedKey(key)) return safeOwnHas(current, key);
69
78
  return key in current;
70
79
  }
71
80
  function isPlainRecord(value) {
@@ -472,7 +481,12 @@ function computeVerdict(field, formMeta) {
472
481
  if (field.valid === true && field.blank !== true && field.dirty === true) return "success";
473
482
  return "idle";
474
483
  }
475
- const DEFAULT_TIMINGS = { showDelay: 100, minVisible: 120 };
484
+ function earliestNonNull(a, b) {
485
+ if (a === null) return b;
486
+ if (b === null) return a;
487
+ return a < b ? a : b;
488
+ }
489
+ const DEFAULT_TIMINGS = { showDelay: 120, minVisible: 120 };
476
490
  const FOCUS_OUT_GRACE = 16;
477
491
  const defaultFamily = /* @__PURE__ */ new WeakSet();
478
492
  function isDefaultDisplayState(fn) {
@@ -482,10 +496,11 @@ function makeDefaultDisplayState({
482
496
  showDelay,
483
497
  minVisible
484
498
  }) {
485
- const reducer = (prev, { field, formMeta, validatingSince, now }) => {
499
+ const reducer = (prev, { field, formMeta, validatingSince, transformingSince, now }) => {
486
500
  const verdict = computeVerdict(field, formMeta);
487
501
  if (!isGateOpen(field, formMeta)) return { display: verdict };
488
- if (validatingSince === null) {
502
+ const inFlightSince = earliestNonNull(validatingSince, transformingSince);
503
+ if (inFlightSince === null) {
489
504
  if (prev.display === "pending") {
490
505
  const shownAt = prev.pendingShownAt ?? now;
491
506
  if (now < shownAt + minVisible)
@@ -496,8 +511,8 @@ function makeDefaultDisplayState({
496
511
  if (prev.display === "pending")
497
512
  return { display: "pending", pendingShownAt: prev.pendingShownAt ?? now };
498
513
  const window = field.focused === false ? Math.min(showDelay, FOCUS_OUT_GRACE) : showDelay;
499
- if (now - validatingSince < window) {
500
- return { display: prev.display, reviewAt: validatingSince + window };
514
+ if (now - inFlightSince < window) {
515
+ return { display: prev.display, reviewAt: inFlightSince + window };
501
516
  }
502
517
  return { display: "pending", pendingShownAt: now, reviewAt: now + minVisible };
503
518
  };
@@ -623,6 +638,8 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
623
638
  if (blankForKey !== void 0) errors.push(...blankForKey);
624
639
  if (userForKey !== void 0) errors.push(...userForKey);
625
640
  const validating = (state.fieldValidationCounts.get(key) ?? 0) > 0;
641
+ const transforming = (state.fieldTransformCounts.get(key) ?? 0) > 0;
642
+ const transformError = state.transformErrors.get(key) ?? null;
626
643
  const gated = state.pathHasAsyncValidation(segments) && !state.firstValidationDone.value;
627
644
  const isOrphan = segments.length > 0 && !hasAtPath(state.form.value, segments) && isUnderStubAncestor(state, segments);
628
645
  const valid = !gated && errors.length === 0 && !validating && !isOrphan;
@@ -649,6 +666,9 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
649
666
  errors,
650
667
  validating,
651
668
  valid,
669
+ transforming,
670
+ busy: transforming || validating,
671
+ transformError,
652
672
  path: segments,
653
673
  ...computeFieldIdentity(formInstanceId, state.formKey, key),
654
674
  key: state.arrayElementKey(segments),
@@ -662,12 +682,14 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
662
682
  function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase, getDisplayState) {
663
683
  const base = buildLeafFieldStateBase(state, segments, key, formInstanceId);
664
684
  const validatingSince = state.fieldValidatingSince.get(key) ?? null;
685
+ const transformingSince = state.fieldTransformingSince.get(key) ?? null;
665
686
  return decorateWithDerivedProps(
666
687
  base,
667
688
  state,
668
689
  getFormMetaBase,
669
690
  key,
670
691
  validatingSince,
692
+ transformingSince,
671
693
  false,
672
694
  // revealedDescendantError: leaves have no descendants
673
695
  false,
@@ -690,6 +712,8 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
690
712
  let connected = false;
691
713
  let validating = false;
692
714
  let validatingSince = null;
715
+ let transforming = false;
716
+ let transformingSince = null;
693
717
  let updatedAt = null;
694
718
  let asyncPending = false;
695
719
  const submissionAttempts = state.submissionAttempts.value;
@@ -720,6 +744,13 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
720
744
  if (leafGateOpen && since !== void 0 && (validatingSince === null || since < validatingSince))
721
745
  validatingSince = since;
722
746
  }
747
+ if ((state.fieldTransformCounts.get(leafKey) ?? 0) > 0) {
748
+ transforming = true;
749
+ const leafGateOpen = submissionAttempts > 0 || leafRecord?.blurredAfterInteraction === true;
750
+ const since = state.fieldTransformingSince.get(leafKey);
751
+ if (leafGateOpen && since !== void 0 && (transformingSince === null || since < transformingSince))
752
+ transformingSince = since;
753
+ }
723
754
  if (state.pathHasAsyncValidationByKey(leafKey, entry.segments)) asyncPending = true;
724
755
  const ts = leafRecord?.updatedAt;
725
756
  if (ts !== void 0 && ts !== null) {
@@ -739,6 +770,19 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
739
770
  return blurredLeafSegments.some((s) => paths.isPathPrefix(ePath, s));
740
771
  });
741
772
  if (!asyncPending && state.pathHasAsyncValidation(segments)) asyncPending = true;
773
+ if ((state.fieldValidationCounts.get(key) ?? 0) > 0) {
774
+ validating = true;
775
+ const since = state.fieldValidatingSince.get(key);
776
+ if (since !== void 0 && (validatingSince === null || since < validatingSince))
777
+ validatingSince = since;
778
+ }
779
+ if ((state.fieldTransformCounts.get(key) ?? 0) > 0) {
780
+ transforming = true;
781
+ const since = state.fieldTransformingSince.get(key);
782
+ if (since !== void 0 && (transformingSince === null || since < transformingSince))
783
+ transformingSince = since;
784
+ }
785
+ const ownTransformError = state.transformErrors.get(key) ?? null;
742
786
  const gated = asyncPending && !state.firstValidationDone.value;
743
787
  const valid = !gated && errors.length === 0 && !validating;
744
788
  const resolved = state.schema.getFieldMetaAtPath ? state.schema.getFieldMetaAtPath(segments) : EMPTY_RESOLVED_FIELD_META;
@@ -762,6 +806,12 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
762
806
  errors,
763
807
  validating,
764
808
  valid,
809
+ transforming,
810
+ busy: transforming || validating,
811
+ // A container surfaces its OWN transform failure (a transform registered
812
+ // on the container path, e.g. a file normalizer) but never rolls up a
813
+ // descendant leaf's failure — that stays a per-field channel.
814
+ transformError: ownTransformError,
765
815
  path: segments,
766
816
  ...computeFieldIdentity(formInstanceId, state.formKey, key),
767
817
  key: state.arrayElementKey(segments),
@@ -772,28 +822,25 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
772
822
  meta: resolved.meta
773
823
  },
774
824
  validatingSince,
825
+ transformingSince,
775
826
  revealedDescendantError
776
827
  };
777
828
  }
778
829
  function buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase, getDisplayState) {
779
- const { base, validatingSince, revealedDescendantError } = buildContainerFieldStateBase(
780
- state,
781
- segments,
782
- key,
783
- formInstanceId
784
- );
830
+ const { base, validatingSince, transformingSince, revealedDescendantError } = buildContainerFieldStateBase(state, segments, key, formInstanceId);
785
831
  return decorateWithDerivedProps(
786
832
  base,
787
833
  state,
788
834
  getFormMetaBase,
789
835
  key,
790
836
  validatingSince,
837
+ transformingSince,
791
838
  revealedDescendantError,
792
839
  segments.length === 0,
793
840
  getDisplayState
794
841
  );
795
842
  }
796
- function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingSince, revealedDescendantError, isRoot, getDisplayState) {
843
+ function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingSince, transformingSince, revealedDescendantError, isRoot, getDisplayState) {
797
844
  const firstError = base.errors[0];
798
845
  const predicate = getDisplayState ?? state.getDisplayState;
799
846
  const formMeta = getFormMetaBase();
@@ -801,6 +848,7 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingS
801
848
  field: base,
802
849
  formMeta,
803
850
  validatingSince,
851
+ transformingSince,
804
852
  // The engine's clock. Frozen to 0 under SSR (no clock, nothing in
805
853
  // flight) so the reducer returns the plain verdict and hydration matches.
806
854
  now: state.ssr ? 0 : Date.now()
@@ -867,6 +915,15 @@ function liveKeysAtPath(state, segments) {
867
915
  if (typeof value === "object") return Object.keys(value);
868
916
  return [];
869
917
  }
918
+ function liveContainerHasKey(state, segments, key) {
919
+ const value = getAtPath(state.form.value, segments);
920
+ if (value === null || value === void 0 || typeof value !== "object") return false;
921
+ if (Array.isArray(value)) {
922
+ const index = Number(key);
923
+ return Number.isInteger(index) && index >= 0 && index < value.length && String(index) === key;
924
+ }
925
+ return Object.hasOwn(value, key);
926
+ }
870
927
  function isArrayPath(state, segments) {
871
928
  if (segments.length === 0) return false;
872
929
  return Array.isArray(getAtPath(state.form.value, segments));
@@ -895,6 +952,21 @@ const INTEGER_SEGMENT = /^(?:0|[1-9]\d*)$/;
895
952
  function keyToSegment(key) {
896
953
  return INTEGER_SEGMENT.test(key) ? Number(key) : key;
897
954
  }
955
+ function callableInvokeShim(method, surface, getDescent) {
956
+ const fnMethod = Reflect.get(Function.prototype, method);
957
+ return new Proxy((() => {
958
+ }), {
959
+ apply: (_target, _thisArg, args) => Reflect.apply(fnMethod, surface, args),
960
+ get: (_target, key) => Reflect.get(getDescent(), key),
961
+ has: (_target, key) => Reflect.has(getDescent(), key),
962
+ ownKeys: () => Reflect.ownKeys(getDescent()),
963
+ getOwnPropertyDescriptor: (_target, key) => {
964
+ const descriptor = Reflect.getOwnPropertyDescriptor(getDescent(), key);
965
+ if (descriptor !== void 0) descriptor.configurable = true;
966
+ return descriptor;
967
+ }
968
+ });
969
+ }
898
970
  function buildSurfaceProxy(opts) {
899
971
  const containerCache = /* @__PURE__ */ new Map();
900
972
  const leafViewCache = /* @__PURE__ */ new Map();
@@ -921,6 +993,8 @@ function buildSurfaceProxy(opts) {
921
993
  const cacheKey = `${JSON.stringify(segments)}+${isArrayLike ? "A" : "O"}`;
922
994
  const existing = containerCache.get(cacheKey);
923
995
  if (existing !== void 0) return existing;
996
+ const isFixedObject = opts.schema.isFixedObjectAtPath(segments);
997
+ const containerHasKey = (k) => opts.containerHasOwnKey !== void 0 ? opts.containerHasOwnKey(segments, k) : opts.containerOwnKeys?.(segments).includes(k) === true;
924
998
  const snapshotContainer = () => opts.materializeContainer === void 0 ? {} : opts.materializeContainer(segments);
925
999
  const {
926
1000
  toString: containerToString,
@@ -928,8 +1002,9 @@ function buildSurfaceProxy(opts) {
928
1002
  toJSON: containerToJSON,
929
1003
  toPrimitive: containerToPrimitive
930
1004
  } = makeReadonlyCoercion(snapshotContainer);
931
- const target = isArrayLike ? [] : (() => {
932
- });
1005
+ const isRoot = segments.length === 0;
1006
+ const target = isRoot ? (() => {
1007
+ }) : isArrayLike ? [] : {};
933
1008
  const proxy = new Proxy(target, {
934
1009
  apply(_, __, args) {
935
1010
  const arg = args[0];
@@ -960,7 +1035,16 @@ function buildSurfaceProxy(opts) {
960
1035
  return key === "toString" ? containerToString : containerValueOf;
961
1036
  }
962
1037
  }
963
- return descendOrTerminate(childSegs);
1038
+ if (key === "hasOwnProperty" && !schemaHasPath(childSegs)) {
1039
+ return Object.prototype.hasOwnProperty;
1040
+ }
1041
+ if (isRoot && (key === "call" || key === "apply" || key === "bind")) {
1042
+ return callableInvokeShim(key, proxy, () => descendOrTerminate(childSegs));
1043
+ }
1044
+ if (opts.isTerminalAt?.(childSegs) === true || isFixedObject && schemaHasPath(childSegs) || containerHasKey(key)) {
1045
+ return descendOrTerminate(childSegs);
1046
+ }
1047
+ return void 0;
964
1048
  },
965
1049
  has(_, key) {
966
1050
  if (typeof key === "symbol") return Reflect.has(target, key);
@@ -1045,15 +1129,8 @@ function buildSurfaceProxy(opts) {
1045
1129
  toJSON: leafToJSONHandler,
1046
1130
  toPrimitive: leafToPrimitive
1047
1131
  } = makeReadonlyCoercion(snapshotLeaf);
1048
- const target = (() => {
1049
- });
1132
+ const target = {};
1050
1133
  const proxy = new Proxy(target, {
1051
- apply(_, __, args) {
1052
- const arg = args[0];
1053
- if (arg === void 0) return opts.resolveCallTarget(segments);
1054
- const { segments: argSegs } = paths.canonicalizePath(arg);
1055
- return opts.resolveCallTarget(argSegs);
1056
- },
1057
1134
  get(_, key) {
1058
1135
  if (typeof key === "symbol") {
1059
1136
  if (key === Symbol.toPrimitive) return leafToPrimitive;
@@ -1063,6 +1140,9 @@ function buildSurfaceProxy(opts) {
1063
1140
  if (key === "toString") return leafToString;
1064
1141
  if (key === "valueOf") return leafValueOf;
1065
1142
  if (key === "toJSON") return leafToJSONHandler;
1143
+ if (key === "hasOwnProperty" && !schemaHasPath([...segments, keyToSegment(key)])) {
1144
+ return Object.prototype.hasOwnProperty;
1145
+ }
1066
1146
  if (leafKeys.has(key)) {
1067
1147
  const leaf = opts.resolveLeaf(segments);
1068
1148
  return readLeafKey(leaf, key);
@@ -1075,8 +1155,8 @@ function buildSurfaceProxy(opts) {
1075
1155
  return true;
1076
1156
  },
1077
1157
  // Iteration: leaf-views expose the leaf-key set so
1078
- // `JSON.stringify(form.fields.email)` produces a FieldState
1079
- // snapshot rather than the function-target placeholder.
1158
+ // `Object.keys(form.fields.email)` / spread enumerate the
1159
+ // FieldState props. (`JSON.stringify` routes through `toJSON` above.)
1080
1160
  ownKeys: () => Array.from(leafKeys),
1081
1161
  getOwnPropertyDescriptor(_, key) {
1082
1162
  if (typeof key !== "string") return void 0;
@@ -1185,6 +1265,12 @@ function buildErrorsProxy(state) {
1185
1265
  // library-produced verdicts (schema + derived-blank) at unreachable
1186
1266
  // paths stay hidden; user-supplied errors are unconditional.
1187
1267
  containerOwnKeys: (segments) => errorAwareContainerKeys(state, segments),
1268
+ // Fast path: a key the live form data holds short-circuits before the
1269
+ // O(n) error-store scan, so iterating `form.errors.<array>` over live
1270
+ // indices stays linear. The scan still runs for a key with no live
1271
+ // home — a server error at a non-schema key (`form.errors.ghost`) —
1272
+ // so it keeps surfacing while a genuinely-absent key reads undefined.
1273
+ containerHasOwnKey: (segments, key) => liveContainerHasKey(state, segments, key) || errorAwareContainerKeys(state, segments).includes(key),
1188
1274
  isArrayContainer: (segments) => isArrayPath(state, segments)
1189
1275
  });
1190
1276
  }
@@ -1358,6 +1444,9 @@ const FIELD_STATE_KEYS = /* @__PURE__ */ new Set([
1358
1444
  "errors",
1359
1445
  "validating",
1360
1446
  "valid",
1447
+ "transforming",
1448
+ "busy",
1449
+ "transformError",
1361
1450
  "displayState",
1362
1451
  "showErrors",
1363
1452
  "showPending",
@@ -1460,14 +1549,22 @@ function buildFieldStateProxy(state, formInstanceId, getFormMetaBase, options) {
1460
1549
  terminalCache.set(cacheKey, proxy);
1461
1550
  return proxy;
1462
1551
  }
1552
+ const surfaceSchema = state.schema;
1463
1553
  return buildSurfaceProxy({
1464
- schema: state.schema,
1554
+ schema: surfaceSchema,
1465
1555
  resolveLeaf: (path) => getFieldStateAt(path),
1466
1556
  leafKeys: FIELD_STATE_KEYS,
1467
1557
  readLeafKey: (computed, key) => computed.value[key],
1468
1558
  materializeContainer: (segments) => materializeFields(state, segments, snapshotFieldStateAt),
1469
- resolveCallTarget: (path) => fieldStateTerminalAt(path),
1559
+ // `form.fields(path)` resolves a FieldState for any path the SCHEMA
1560
+ // declares — a leaf, a container, an inactive discriminated-union
1561
+ // variant key, or an out-of-bounds array index (the element schema
1562
+ // admits any index). A path the schema doesn't have is a typo, not a
1563
+ // field, so it reads `undefined` rather than a phantom stub. The
1564
+ // empty path (`form.fields()`) is the root object, always valid.
1565
+ resolveCallTarget: (path) => surfaceSchema.getSlimPrimitiveTypesAtPath(path).size > 0 ? fieldStateTerminalAt(path) : void 0,
1470
1566
  containerOwnKeys: (segments) => liveKeysAtPath(state, segments),
1567
+ containerHasOwnKey: (segments, key) => liveContainerHasKey(state, segments, key),
1471
1568
  isArrayContainer: (segments) => isArrayPath(state, segments)
1472
1569
  });
1473
1570
  }
@@ -1743,7 +1840,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1743
1840
  if (!result.ok) return result.error;
1744
1841
  return stripData(composeWithDerivedBlank(result.refinement, result.segments));
1745
1842
  }
1746
- async function process(pathInput) {
1843
+ async function parse(pathInput) {
1747
1844
  const result = await runImperativeValidation(pathInput, {
1748
1845
  cancelInFlight: false,
1749
1846
  commitToSchemaErrors: false
@@ -1796,6 +1893,8 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1796
1893
  state.activeSubmissions.value += 1;
1797
1894
  state.submitting.value = true;
1798
1895
  state.submitError.value = null;
1896
+ state.clearUserErrors();
1897
+ while (state.activeTransforms.value > 0) await state.settleTransforms();
1799
1898
  state.cancelFieldValidation();
1800
1899
  state.displayEngine.clear();
1801
1900
  state.activeValidations.value += 1;
@@ -1834,9 +1933,8 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1834
1933
  state.emitSubmitSuccess();
1835
1934
  } catch (err) {
1836
1935
  if (state.submissionGeneration.value === genAtEntry) {
1837
- state.submitError.value = err;
1936
+ state.submitError.value = paths.toError(err);
1838
1937
  }
1839
- throw err;
1840
1938
  } finally {
1841
1939
  if (!validationSettled) {
1842
1940
  state.activeValidations.value = Math.max(0, state.activeValidations.value - 1);
@@ -1850,7 +1948,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1850
1948
  };
1851
1949
  return submitHandler;
1852
1950
  };
1853
- return { validate, validateAsync, process, handleSubmit };
1951
+ return { validate, validateAsync, parse, handleSubmit };
1854
1952
  }
1855
1953
  function toSegments(pathInput) {
1856
1954
  return paths.canonicalizePath(pathInput).segments;
@@ -2323,6 +2421,23 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2323
2421
  markConnectedOptimistically: () => {
2324
2422
  state.markConnectedOptimistically(segments);
2325
2423
  },
2424
+ // --- Async transform lifecycle (internal; the directive's
2425
+ // deferred orchestrator is the only legitimate consumer). Thin
2426
+ // path-bound delegates to the store's per-path token / counter
2427
+ // machinery — same pattern as `markBlank` / `setValueWithInternalPath`,
2428
+ // so the directive (which holds only this RegisterValue, never the
2429
+ // store) can drive the busy/discard/error bookkeeping. ---
2430
+ beginTransform: (holder) => state.beginTransform(pathKey, holder),
2431
+ isCurrentTransform: (token) => state.isCurrentTransform(pathKey, token),
2432
+ endTransform: (token) => state.endTransform(pathKey, token),
2433
+ setTransformError: (err) => state.setTransformError(pathKey, err),
2434
+ // Synchronous read of "is a transform in flight at this path". The
2435
+ // orchestrator's `beginTransform` bumps the count before the
2436
+ // listener's force-sync block runs, so the directive reads this to
2437
+ // skip reverting the DOM to stale storage mid-flight.
2438
+ get transforming() {
2439
+ return (state.fieldTransformCounts.get(pathKey) ?? 0) > 0;
2440
+ },
2326
2441
  path: pathKey,
2327
2442
  // Frozen so a wrapper component can pass `rv.segments` directly
2328
2443
  // to `form.fields(...)` without defensive copying — and so test
@@ -2521,7 +2636,9 @@ function expandUnsetAt(segments, schema, paths$1) {
2521
2636
  function buildCallableReadonlySnapshotProxy(opts) {
2522
2637
  const target = (() => {
2523
2638
  });
2524
- const { toString, valueOf, toJSON, toPrimitive } = makeReadonlyCoercion(opts.snapshot);
2639
+ const { toString, valueOf, toJSON, toPrimitive } = makeReadonlyCoercion(
2640
+ opts.coercionSnapshot ?? opts.snapshot
2641
+ );
2525
2642
  const callResolve = opts.resolveCall ?? ((arg) => opts.resolveKey(String(arg)));
2526
2643
  return new Proxy(target, {
2527
2644
  apply(_, __, args) {
@@ -2570,27 +2687,52 @@ function buildCallableReadonlySnapshotProxy(opts) {
2570
2687
  });
2571
2688
  }
2572
2689
 
2690
+ function materializeFormValue(node) {
2691
+ if (node === null || typeof node !== "object") return node;
2692
+ if (Array.isArray(node)) {
2693
+ const out2 = new Array(node.length);
2694
+ for (let i = 0; i < node.length; i++) out2[i] = materializeFormValue(node[i]);
2695
+ return out2;
2696
+ }
2697
+ if (!isPlainRecord(node)) return vue.toRaw(node);
2698
+ const rec = node;
2699
+ const out = {};
2700
+ for (const key of Object.keys(rec)) {
2701
+ safeAssign(out, key, materializeFormValue(safeOwnRead(rec, key)));
2702
+ }
2703
+ return out;
2704
+ }
2573
2705
  function buildValuesProxy(form) {
2574
2706
  const inner = vue.computed(() => vue.readonly(form.value));
2575
2707
  return buildCallableReadonlySnapshotProxy({
2576
2708
  surface: "form.values",
2577
2709
  snapshot: () => inner.value,
2710
+ // Faithful, reactivity-preserving serialisation: walk the reactive
2711
+ // proxy with own-safe reads so `JSON.stringify(form.values)` /
2712
+ // `String(form.values)` reflect the stored data — including a field
2713
+ // literally named `hasOwnProperty` that Vue would otherwise shim —
2714
+ // while still tracking the per-key reads that drive re-render.
2715
+ coercionSnapshot: () => materializeFormValue(inner.value),
2578
2716
  // Read through the readonly proxy at access time so Vue's
2579
2717
  // dependency tracking lands inside the consumer's active effect
2580
2718
  // — `inner.value[key]` is what triggers per-key tracking.
2581
- resolveKey: (key) => inner.value[key],
2582
- // Dynamic path: walk segments through the readonly proxy. Each
2583
- // step reads through the proxy's own get traps so dependency
2584
- // tracking propagates at every level.
2585
- resolveCall: (arg) => {
2586
- const { segments } = paths.canonicalizePath(arg);
2587
- let cursor = inner.value;
2588
- for (const seg of segments) {
2589
- if (cursor === null || cursor === void 0) return void 0;
2590
- cursor = cursor[seg];
2591
- }
2592
- return cursor;
2593
- },
2719
+ //
2720
+ // Prototype-shadowed names (`hasOwnProperty`, `constructor`, …) read
2721
+ // off the RAW target instead: own-shadows-inherited semantics still
2722
+ // hold (a data field by that name returns its stored value), but
2723
+ // when there's no such field the inherited member resolves — so
2724
+ // `form.values.hasOwnProperty('x')` keeps working as the real
2725
+ // method. The raw read dodges Vue's `hasOwnProperty` proxy shim,
2726
+ // which would otherwise mask a data field of that name. (`toString`
2727
+ // / `valueOf` / `toJSON` never reach here the base get trap
2728
+ // intercepts them as coercion handlers first.)
2729
+ resolveKey: (key) => isShadowedKey(key) ? vue.toRaw(inner.value)[key] : inner.value[key],
2730
+ // Dynamic path: walk segments through the readonly proxy with the
2731
+ // same own-property-safe descent the rest of the runtime uses
2732
+ // (`getAtPath`), so `form.values('a.hasOwnProperty')` resolves the
2733
+ // stored value. Per-level reads still propagate Vue's tracking for
2734
+ // ordinary keys.
2735
+ resolveCall: (arg) => getAtPath(inner.value, paths.canonicalizePath(arg).segments),
2594
2736
  ownKeys: () => Reflect.ownKeys(inner.value),
2595
2737
  hasKey: (key) => Reflect.has(inner.value, key),
2596
2738
  describeKey: (key) => {
@@ -2651,12 +2793,12 @@ function buildFormApi(state, formInstanceId, options = {}) {
2651
2793
  const {
2652
2794
  validate: validateBuilt,
2653
2795
  validateAsync: validateAsyncBuilt,
2654
- process: processBuilt,
2796
+ parse: parseBuilt,
2655
2797
  handleSubmit
2656
2798
  } = buildProcessForm(state, formInstanceId, processOptions);
2657
2799
  const validate = (pathInput) => validateBuilt(pathInput);
2658
2800
  const validateAsync = (pathInput) => validateAsyncBuilt(pathInput);
2659
- const process = (pathInput) => processBuilt(pathInput);
2801
+ const parse = (pathInput) => parseBuilt(pathInput);
2660
2802
  function pathToRef(pathInput) {
2661
2803
  const segments = paths.canonicalizePath(pathInput).segments;
2662
2804
  return vue.computed(() => getAtPath(state.form.value, segments));
@@ -2884,6 +3026,21 @@ function buildFormApi(state, formInstanceId, options = {}) {
2884
3026
  // keep the explicit form-level computation for the gate.
2885
3027
  valid,
2886
3028
  errors: metaErrors,
3029
+ // Whole-form transforming mirrors the global `activeTransforms`
3030
+ // counter ORed with any per-leaf transform in flight (the root
3031
+ // rollup), exactly as `validating` composes its lifecycle and
3032
+ // per-field sources. `busy` is the union of both work signals at
3033
+ // the form level. `transformError` is leaf-only, so the root
3034
+ // rollup reads it as `null` (kept for FieldState-shape parity).
3035
+ transforming: vue.computed(
3036
+ () => state.activeTransforms.value > 0 || rootFieldState.value.transforming
3037
+ ),
3038
+ busy: vue.computed(
3039
+ () => state.activeValidations.value > 0 || state.activeTransforms.value > 0 || rootFieldState.value.validating || rootFieldState.value.transforming
3040
+ ),
3041
+ get transformError() {
3042
+ return rootFieldState.value.transformError;
3043
+ },
2887
3044
  // `displayState` / the `show*` booleans / `firstError` flow
2888
3045
  // through the same root field-state computed as the rest of the
2889
3046
  // FieldState surface, so `form.meta.displayState` matches
@@ -3112,7 +3269,8 @@ function buildFormApi(state, formInstanceId, options = {}) {
3112
3269
  setValue: gated(setValueImpl),
3113
3270
  validate: gated(validate),
3114
3271
  validateAsync: gated(validateAsync),
3115
- process: gated(process),
3272
+ parse: gated(parse),
3273
+ settleTransforms: gated(state.settleTransforms),
3116
3274
  register: gated(register),
3117
3275
  key: state.formKey,
3118
3276
  // Auto-unwrapping views over the per-store async-defaults lifecycle
@@ -3979,6 +4137,106 @@ function createFormStore(options) {
3979
4137
  fieldValidationCounts.set(key, next);
3980
4138
  }
3981
4139
  }
4140
+ const fieldTransformCounts = vue.reactive(/* @__PURE__ */ new Map());
4141
+ const fieldTransformingSince = vue.reactive(/* @__PURE__ */ new Map());
4142
+ const transformErrors = vue.reactive(/* @__PURE__ */ new Map());
4143
+ const activeTransforms = vue.ref(0);
4144
+ const transformRuns = /* @__PURE__ */ new Map();
4145
+ let transformTokenSeq = 0;
4146
+ const transformWaiters = [];
4147
+ function incFieldTransform(key) {
4148
+ fieldTransformingSince.set(key, ssr ? 0 : Date.now());
4149
+ fieldTransformCounts.set(key, (fieldTransformCounts.get(key) ?? 0) + 1);
4150
+ }
4151
+ function decFieldTransform(key) {
4152
+ const next = (fieldTransformCounts.get(key) ?? 0) - 1;
4153
+ if (next <= 0) {
4154
+ fieldTransformCounts.delete(key);
4155
+ fieldTransformingSince.delete(key);
4156
+ } else {
4157
+ fieldTransformCounts.set(key, next);
4158
+ }
4159
+ }
4160
+ function flushSettledTransformWaiters() {
4161
+ if (transformWaiters.length === 0) return;
4162
+ const globalIdle = activeTransforms.value === 0;
4163
+ for (let i = transformWaiters.length - 1; i >= 0; i--) {
4164
+ const w = transformWaiters[i];
4165
+ if (w === void 0) continue;
4166
+ const idle = w.key === null ? globalIdle : (fieldTransformCounts.get(w.key) ?? 0) === 0;
4167
+ if (idle) {
4168
+ transformWaiters.splice(i, 1);
4169
+ w.resolve();
4170
+ }
4171
+ }
4172
+ }
4173
+ function releaseTransformRun(key, run) {
4174
+ if (run.released) return;
4175
+ run.released = true;
4176
+ run.holder.aborted = true;
4177
+ run.holder.controller?.abort();
4178
+ activeTransforms.value = Math.max(0, activeTransforms.value - 1);
4179
+ decFieldTransform(key);
4180
+ }
4181
+ function beginTransform(key, holder) {
4182
+ const prior = transformRuns.get(key);
4183
+ if (prior !== void 0) releaseTransformRun(key, prior);
4184
+ const token = ++transformTokenSeq;
4185
+ transformRuns.set(key, { token, holder, released: false });
4186
+ incFieldTransform(key);
4187
+ activeTransforms.value += 1;
4188
+ if (transformErrors.has(key)) transformErrors.delete(key);
4189
+ return token;
4190
+ }
4191
+ function isCurrentTransform(key, token) {
4192
+ return transformRuns.get(key)?.token === token;
4193
+ }
4194
+ function endTransform(key, token) {
4195
+ const run = transformRuns.get(key);
4196
+ if (run?.token === token) {
4197
+ if (!run.released) {
4198
+ activeTransforms.value = Math.max(0, activeTransforms.value - 1);
4199
+ decFieldTransform(key);
4200
+ }
4201
+ transformRuns.delete(key);
4202
+ }
4203
+ flushSettledTransformWaiters();
4204
+ }
4205
+ function setTransformError(key, err) {
4206
+ transformErrors.set(key, err);
4207
+ }
4208
+ function cancelTransforms() {
4209
+ for (const [key, run] of [...transformRuns]) {
4210
+ releaseTransformRun(key, run);
4211
+ transformRuns.delete(key);
4212
+ }
4213
+ if (transformErrors.size > 0) transformErrors.clear();
4214
+ flushSettledTransformWaiters();
4215
+ }
4216
+ function cancelTransformsUnder(prefix) {
4217
+ for (const [key, run] of [...transformRuns]) {
4218
+ const segs = paths.segmentsForPathKey(key);
4219
+ if (segs === null) continue;
4220
+ if (!paths.isPathPrefix(prefix, segs)) continue;
4221
+ releaseTransformRun(key, run);
4222
+ transformRuns.delete(key);
4223
+ transformErrors.delete(key);
4224
+ }
4225
+ flushSettledTransformWaiters();
4226
+ }
4227
+ function settleTransforms(path) {
4228
+ if (path === void 0) {
4229
+ if (activeTransforms.value === 0) return Promise.resolve();
4230
+ return new Promise((resolve) => {
4231
+ transformWaiters.push({ key: null, resolve });
4232
+ });
4233
+ }
4234
+ const { key } = paths.canonicalizePath(path);
4235
+ if ((fieldTransformCounts.get(key) ?? 0) === 0) return Promise.resolve();
4236
+ return new Promise((resolve) => {
4237
+ transformWaiters.push({ key, resolve });
4238
+ });
4239
+ }
3982
4240
  const initStamp = (/* @__PURE__ */ new Date()).toISOString();
3983
4241
  diffAndApply({}, schemaInitialData, [], (patch) => {
3984
4242
  if (patch.kind !== "added") return;
@@ -4128,6 +4386,7 @@ function createFormStore(options) {
4128
4386
  }
4129
4387
  }
4130
4388
  }
4389
+ if (transformRuns.size !== 0) cancelTransformsUnder(path);
4131
4390
  if (meta?.skipDiscriminatorReshape !== true) {
4132
4391
  if (path.length > 0) {
4133
4392
  const last = path[path.length - 1];
@@ -4462,6 +4721,7 @@ function createFormStore(options) {
4462
4721
  drainHooks.length = 0;
4463
4722
  modules.clear();
4464
4723
  cancelFieldValidation();
4724
+ cancelTransforms();
4465
4725
  fieldValidatingSince.clear();
4466
4726
  formChangeListeners.clear();
4467
4727
  submitSuccessListeners.clear();
@@ -4598,6 +4858,7 @@ function createFormStore(options) {
4598
4858
  if (remaining === 0) {
4599
4859
  elements.delete(key);
4600
4860
  touchFieldRecord(key, path, { connected: false, focused: null, blurred: null });
4861
+ if (transformRuns.size !== 0) cancelTransformsUnder(path);
4601
4862
  }
4602
4863
  return remaining;
4603
4864
  }
@@ -4819,6 +5080,7 @@ function createFormStore(options) {
4819
5080
  submitError.value = null;
4820
5081
  departAttempts.value = 0;
4821
5082
  cancelFieldValidation();
5083
+ cancelTransforms();
4822
5084
  displayEngine.clear();
4823
5085
  fieldValidatingSince.clear();
4824
5086
  pathSnapshots.clear();
@@ -4837,6 +5099,7 @@ function createFormStore(options) {
4837
5099
  const { key: targetKey, segments: targetSegments } = paths.canonicalizePath(path);
4838
5100
  variantMemory.clearUnderPath(targetSegments);
4839
5101
  cancelFieldValidationUnder(targetSegments);
5102
+ cancelTransformsUnder(targetSegments);
4840
5103
  for (const [snapKey] of [...pathSnapshots]) {
4841
5104
  const segs = paths.segmentsForPathKey(snapKey);
4842
5105
  if (segs === null) continue;
@@ -4972,6 +5235,10 @@ function createFormStore(options) {
4972
5235
  pathHasAsyncValidationByKey,
4973
5236
  fieldValidationCounts,
4974
5237
  fieldValidatingSince,
5238
+ fieldTransformCounts,
5239
+ fieldTransformingSince,
5240
+ transformErrors,
5241
+ activeTransforms,
4975
5242
  displayEngine,
4976
5243
  applyFormReplacement,
4977
5244
  setValueAtPath,
@@ -5001,6 +5268,13 @@ function createFormStore(options) {
5001
5268
  getOriginalAtPath,
5002
5269
  getFirstErrorElement,
5003
5270
  cancelFieldValidation,
5271
+ beginTransform,
5272
+ isCurrentTransform,
5273
+ endTransform,
5274
+ setTransformError,
5275
+ cancelTransforms,
5276
+ cancelTransformsUnder,
5277
+ settleTransforms,
5004
5278
  scheduleFieldValidation,
5005
5279
  onFormChange,
5006
5280
  onSubmitSuccess,
@@ -5643,6 +5917,8 @@ function isLazyMarker(value) {
5643
5917
  }
5644
5918
 
5645
5919
  const NOOP_WIZARD_HISTORY = {
5920
+ push() {
5921
+ },
5646
5922
  replace() {
5647
5923
  },
5648
5924
  read() {
@@ -5657,6 +5933,9 @@ function createWizardHistory(param) {
5657
5933
  if (typeof window === "undefined") return NOOP_WIZARD_HISTORY;
5658
5934
  const subscribers = [];
5659
5935
  let disposed = false;
5936
+ function currentKey() {
5937
+ return new URL(window.location.href).searchParams.get(param) ?? void 0;
5938
+ }
5660
5939
  function buildUrl(key) {
5661
5940
  const url = new URL(window.location.href);
5662
5941
  url.searchParams.set(param, key);
@@ -5664,25 +5943,29 @@ function createWizardHistory(param) {
5664
5943
  }
5665
5944
  function handlePopstate() {
5666
5945
  if (disposed) return;
5667
- const url = new URL(window.location.href);
5668
- const value = url.searchParams.get(param) ?? void 0;
5946
+ const value = currentKey();
5669
5947
  for (const subscriber of subscribers) subscriber(value);
5670
5948
  }
5671
5949
  window.addEventListener("popstate", handlePopstate);
5672
- function safeReplaceState(key) {
5950
+ function safeWrite(key, mode) {
5673
5951
  try {
5674
- window.history.replaceState({}, "", buildUrl(key));
5952
+ if (mode === "push") window.history.pushState({}, "", buildUrl(key));
5953
+ else window.history.replaceState({}, "", buildUrl(key));
5675
5954
  } catch {
5676
5955
  }
5677
5956
  }
5678
5957
  return {
5958
+ push(key) {
5959
+ if (disposed) return;
5960
+ if (currentKey() === key) return;
5961
+ safeWrite(key, "push");
5962
+ },
5679
5963
  replace(key) {
5680
5964
  if (disposed) return;
5681
- safeReplaceState(key);
5965
+ safeWrite(key, "replace");
5682
5966
  },
5683
5967
  read() {
5684
- const url = new URL(window.location.href);
5685
- return url.searchParams.get(param) ?? void 0;
5968
+ return currentKey();
5686
5969
  },
5687
5970
  subscribe(callback) {
5688
5971
  if (disposed) return;
@@ -5720,6 +6003,7 @@ function buildNoopWizardSchema(formKey) {
5720
6003
  getEmptyValueAtPath: () => void 0,
5721
6004
  isPreprocessOrCoerceLeaf: () => false,
5722
6005
  arrayShapeAtPath: () => void 0,
6006
+ isFixedObjectAtPath: (path) => path.length === 0,
5723
6007
  getSchemasAtPath: () => [],
5724
6008
  validateAtPath: () => success,
5725
6009
  getSlimPrimitiveTypesAtPath: () => new Set(EMPTY_SLIM_KINDS),
@@ -6173,7 +6457,10 @@ function useWizard(options) {
6173
6457
  };
6174
6458
  const persistCallback = options.persist === false ? void 0 : options.persist !== void 0 ? options.persist : (state) => {
6175
6459
  if (state.step === void 0) return;
6176
- historyHandle.replace(state.step);
6460
+ const current = historyHandle.read();
6461
+ const effectiveCurrent = current !== void 0 && isCompiledKey(current) ? current : firstKey();
6462
+ if (state.step === effectiveCurrent) historyHandle.replace(state.step);
6463
+ else historyHandle.push(state.step);
6177
6464
  };
6178
6465
  function isCompiledKey(key) {
6179
6466
  const list = compiledSteps.value;
@@ -6254,6 +6541,7 @@ function useWizard(options) {
6254
6541
  }
6255
6542
  const submitting = vue.ref(false);
6256
6543
  const submissionAttempts = vue.ref(0);
6544
+ const submitError = vue.ref(null);
6257
6545
  const done = vue.ref(false);
6258
6546
  function activateForm(form) {
6259
6547
  const source = asSubmissionSource(form);
@@ -6381,7 +6669,7 @@ function useWizard(options) {
6381
6669
  formKey: form.key
6382
6670
  };
6383
6671
  }
6384
- return full.process();
6672
+ return full.parse();
6385
6673
  }
6386
6674
  function collectErrors(results) {
6387
6675
  const out = [];
@@ -6412,6 +6700,7 @@ function useWizard(options) {
6412
6700
  return;
6413
6701
  }
6414
6702
  submitting.value = true;
6703
+ submitError.value = null;
6415
6704
  try {
6416
6705
  const currentKey = activeKey.value;
6417
6706
  const final = isFinalStep.value;
@@ -6420,12 +6709,14 @@ function useWizard(options) {
6420
6709
  if (final) {
6421
6710
  await Promise.all(
6422
6711
  list.map(async (step) => {
6712
+ registry.forms.get(step.key)?.clearUserErrors();
6423
6713
  const result = await processOne(step.form);
6424
6714
  results.set(step.key, result);
6425
6715
  })
6426
6716
  );
6427
6717
  } else {
6428
6718
  const active = activeForm.value;
6719
+ registry.forms.get(active.key)?.clearUserErrors();
6429
6720
  const result = await processOne(active);
6430
6721
  results.set(active.key, result);
6431
6722
  }
@@ -6460,7 +6751,6 @@ function useWizard(options) {
6460
6751
  if (target !== void 0) moveTo(target.key);
6461
6752
  }
6462
6753
  } else {
6463
- if (onError !== void 0) await onError(errors);
6464
6754
  if (options.focusFirstError !== false) {
6465
6755
  const firstFailedKey = errors[0]?.formKey;
6466
6756
  if (firstFailedKey !== void 0 && isCompiledKey(firstFailedKey)) {
@@ -6475,7 +6765,16 @@ function useWizard(options) {
6475
6765
  }
6476
6766
  }
6477
6767
  }
6768
+ if (onError !== void 0) {
6769
+ try {
6770
+ await onError(errors);
6771
+ } catch (cause) {
6772
+ throw new paths.SubmitErrorHandlerError("User-provided onError threw", { cause });
6773
+ }
6774
+ }
6478
6775
  }
6776
+ } catch (err) {
6777
+ submitError.value = paths.toError(err);
6479
6778
  } finally {
6480
6779
  submitting.value = false;
6481
6780
  }
@@ -6484,6 +6783,7 @@ function useWizard(options) {
6484
6783
  function reset() {
6485
6784
  submissionAttempts.value = 0;
6486
6785
  done.value = false;
6786
+ submitError.value = null;
6487
6787
  lazyEpoch.value += 1;
6488
6788
  for (const step of compiledSteps.value) {
6489
6789
  const full = asSubmissionSource(step.form);
@@ -6559,6 +6859,9 @@ function useWizard(options) {
6559
6859
  get submissionAttempts() {
6560
6860
  return submissionAttempts.value;
6561
6861
  },
6862
+ get submitError() {
6863
+ return submitError.value;
6864
+ },
6562
6865
  get visited() {
6563
6866
  return visited.value;
6564
6867
  }
@@ -6699,4 +7002,4 @@ exports.structuralSnapshot = structuralSnapshot;
6699
7002
  exports.unset = unset;
6700
7003
  exports.useAbstractForm = useAbstractForm;
6701
7004
  exports.useWizard = useWizard;
6702
- //# sourceMappingURL=attaform.BUszFoKq.cjs.map
7005
+ //# sourceMappingURL=attaform.ClXwitZj.cjs.map