attaform 0.20.2 → 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 (150) hide show
  1. package/dist/chunks/dev-key-collision-warnings.cjs +58 -0
  2. package/dist/chunks/dev-key-collision-warnings.cjs.map +1 -0
  3. package/dist/chunks/dev-key-collision-warnings.mjs +55 -0
  4. package/dist/chunks/dev-key-collision-warnings.mjs.map +1 -0
  5. package/dist/chunks/devtools.cjs +1 -1
  6. package/dist/chunks/devtools.mjs +1 -1
  7. package/dist/chunks/fingerprint.cjs +186 -0
  8. package/dist/chunks/fingerprint.cjs.map +1 -0
  9. package/dist/chunks/fingerprint.mjs +184 -0
  10. package/dist/chunks/fingerprint.mjs.map +1 -0
  11. package/dist/chunks/fingerprint2.cjs +162 -0
  12. package/dist/chunks/fingerprint2.cjs.map +1 -0
  13. package/dist/chunks/fingerprint2.mjs +160 -0
  14. package/dist/chunks/fingerprint2.mjs.map +1 -0
  15. package/dist/chunks/indexeddb.cjs +1 -1
  16. package/dist/chunks/indexeddb.mjs +1 -1
  17. package/dist/chunks/local-storage.cjs +1 -1
  18. package/dist/chunks/local-storage.mjs +1 -1
  19. package/dist/chunks/multi-tab-sync.cjs +367 -0
  20. package/dist/chunks/multi-tab-sync.cjs.map +1 -0
  21. package/dist/chunks/multi-tab-sync.mjs +364 -0
  22. package/dist/chunks/multi-tab-sync.mjs.map +1 -0
  23. package/dist/chunks/session-storage.cjs +1 -1
  24. package/dist/chunks/session-storage.mjs +1 -1
  25. package/dist/chunks/wire-persistence.cjs +396 -0
  26. package/dist/chunks/wire-persistence.cjs.map +1 -0
  27. package/dist/chunks/wire-persistence.mjs +394 -0
  28. package/dist/chunks/wire-persistence.mjs.map +1 -0
  29. package/dist/esbuild.cjs +28 -0
  30. package/dist/esbuild.cjs.map +1 -0
  31. package/dist/esbuild.d.cts +56 -0
  32. package/dist/esbuild.d.mts +56 -0
  33. package/dist/esbuild.d.ts +56 -0
  34. package/dist/esbuild.mjs +26 -0
  35. package/dist/esbuild.mjs.map +1 -0
  36. package/dist/index.cjs +5 -3
  37. package/dist/index.cjs.map +1 -1
  38. package/dist/index.d.cts +66 -70
  39. package/dist/index.d.mts +66 -70
  40. package/dist/index.d.ts +66 -70
  41. package/dist/index.mjs +5 -5
  42. package/dist/nuxt.d.cts +1 -1
  43. package/dist/nuxt.d.mts +1 -1
  44. package/dist/nuxt.d.ts +1 -1
  45. package/dist/rollup.cjs +24 -0
  46. package/dist/rollup.cjs.map +1 -0
  47. package/dist/rollup.d.cts +35 -0
  48. package/dist/rollup.d.mts +35 -0
  49. package/dist/rollup.d.ts +35 -0
  50. package/dist/rollup.mjs +22 -0
  51. package/dist/rollup.mjs.map +1 -0
  52. package/dist/rspack.cjs +10 -0
  53. package/dist/rspack.cjs.map +1 -0
  54. package/dist/rspack.d.cts +40 -0
  55. package/dist/rspack.d.mts +40 -0
  56. package/dist/rspack.d.ts +40 -0
  57. package/dist/rspack.mjs +8 -0
  58. package/dist/rspack.mjs.map +1 -0
  59. package/dist/runtime/plugins/attaform.cjs +2 -2
  60. package/dist/runtime/plugins/attaform.mjs +2 -2
  61. package/dist/shared/attaform.BJGA_UOS.mjs +37 -0
  62. package/dist/shared/attaform.BJGA_UOS.mjs.map +1 -0
  63. package/dist/shared/attaform.BRGIpZo4.cjs +26 -0
  64. package/dist/shared/attaform.BRGIpZo4.cjs.map +1 -0
  65. package/dist/shared/{attaform.DAKrGhxc.cjs → attaform.BSkvn43g.cjs} +101 -417
  66. package/dist/shared/attaform.BSkvn43g.cjs.map +1 -0
  67. package/dist/shared/{attaform.sWm8B15V.d.mts → attaform.BWfliRIK.d.cts} +172 -2
  68. package/dist/shared/{attaform.BGk8cfw2.mjs → attaform.Be8NZG9M.mjs} +178 -21
  69. package/dist/shared/attaform.Be8NZG9M.mjs.map +1 -0
  70. package/dist/shared/{attaform.D2SCCd4O.cjs → attaform.Bq5sX7TF.cjs} +2 -2
  71. package/dist/shared/{attaform.D2SCCd4O.cjs.map → attaform.Bq5sX7TF.cjs.map} +1 -1
  72. package/dist/shared/{attaform.ceGEAEMk.d.ts → attaform.Bv7dRDWK.d.ts} +172 -2
  73. package/dist/shared/attaform.C3Doa9Pt.mjs +24 -0
  74. package/dist/shared/attaform.C3Doa9Pt.mjs.map +1 -0
  75. package/dist/shared/{attaform.B_hph5AE.cjs → attaform.CICFZ1iS.cjs} +178 -20
  76. package/dist/shared/attaform.CICFZ1iS.cjs.map +1 -0
  77. package/dist/shared/attaform.CQN9R62B.cjs +39 -0
  78. package/dist/shared/attaform.CQN9R62B.cjs.map +1 -0
  79. package/dist/shared/{attaform.CwLjUqmQ.cjs → attaform.ClXwitZj.cjs} +735 -960
  80. package/dist/shared/attaform.ClXwitZj.cjs.map +1 -0
  81. package/dist/shared/{attaform.99cfHcIt.d.cts → attaform.D0dWZsJt.d.cts} +349 -77
  82. package/dist/shared/{attaform.99cfHcIt.d.mts → attaform.D0dWZsJt.d.mts} +349 -77
  83. package/dist/shared/{attaform.99cfHcIt.d.ts → attaform.D0dWZsJt.d.ts} +349 -77
  84. package/dist/shared/{attaform.z5j3LwJz.cjs → attaform.D32WwKk6.cjs} +216 -35
  85. package/dist/shared/attaform.D32WwKk6.cjs.map +1 -0
  86. package/dist/shared/{attaform.C5aYC_T8.mjs → attaform.DMEP_ENr.mjs} +39 -392
  87. package/dist/shared/attaform.DMEP_ENr.mjs.map +1 -0
  88. package/dist/shared/{attaform.tiWEVznj.mjs → attaform.DR6RmxWZ.mjs} +725 -962
  89. package/dist/shared/attaform.DR6RmxWZ.mjs.map +1 -0
  90. package/dist/shared/{attaform.Dt7dEcHk.mjs → attaform.DozgVlCE.mjs} +89 -405
  91. package/dist/shared/attaform.DozgVlCE.mjs.map +1 -0
  92. package/dist/shared/{attaform.DN5CvZrg.d.ts → attaform.Duecg2NO.d.mts} +2 -2
  93. package/dist/shared/attaform.DuzQYscR.d.cts +41 -0
  94. package/dist/shared/attaform.DuzQYscR.d.mts +41 -0
  95. package/dist/shared/attaform.DuzQYscR.d.ts +41 -0
  96. package/dist/shared/{attaform.BXinSW2T.d.mts → attaform.FudOcHaa.d.cts} +2 -2
  97. package/dist/shared/attaform.LEWUFqUw.cjs +54 -0
  98. package/dist/shared/attaform.LEWUFqUw.cjs.map +1 -0
  99. package/dist/shared/{attaform.CywE4y8x.d.cts → attaform.MtrpT6Ki.d.ts} +2 -2
  100. package/dist/shared/{attaform.DbRgDFa7.d.cts → attaform.NQ8mybyW.d.mts} +172 -2
  101. package/dist/shared/{attaform.Cd4AOfwu.cjs → attaform.S-pYLSo4.cjs} +68 -402
  102. package/dist/shared/attaform.S-pYLSo4.cjs.map +1 -0
  103. package/dist/shared/{attaform.CnrxbkB6.mjs → attaform.Y1ZGhM4k.mjs} +2 -2
  104. package/dist/shared/{attaform.CnrxbkB6.mjs.map → attaform.Y1ZGhM4k.mjs.map} +1 -1
  105. package/dist/shared/{attaform.QG5TG8lB.mjs → attaform.pmtahXKy.mjs} +216 -36
  106. package/dist/shared/attaform.pmtahXKy.mjs.map +1 -0
  107. package/dist/shared/attaform.sHkHv_98.mjs +51 -0
  108. package/dist/shared/attaform.sHkHv_98.mjs.map +1 -0
  109. package/dist/vite.cjs +9 -45
  110. package/dist/vite.cjs.map +1 -1
  111. package/dist/vite.d.cts +36 -0
  112. package/dist/vite.d.mts +36 -0
  113. package/dist/vite.d.ts +36 -0
  114. package/dist/vite.mjs +8 -44
  115. package/dist/vite.mjs.map +1 -1
  116. package/dist/webpack.cjs +10 -0
  117. package/dist/webpack.cjs.map +1 -0
  118. package/dist/webpack.d.cts +37 -0
  119. package/dist/webpack.d.mts +37 -0
  120. package/dist/webpack.d.ts +37 -0
  121. package/dist/webpack.mjs +8 -0
  122. package/dist/webpack.mjs.map +1 -0
  123. package/dist/zod-v3.cjs +3 -3
  124. package/dist/zod-v3.d.cts +3 -3
  125. package/dist/zod-v3.d.mts +3 -3
  126. package/dist/zod-v3.d.ts +3 -3
  127. package/dist/zod-v3.mjs +3 -3
  128. package/dist/zod-v4.cjs +3 -3
  129. package/dist/zod-v4.d.cts +4 -4
  130. package/dist/zod-v4.d.mts +4 -4
  131. package/dist/zod-v4.d.ts +4 -4
  132. package/dist/zod-v4.mjs +3 -3
  133. package/dist/zod.cjs +8 -8
  134. package/dist/zod.cjs.map +1 -1
  135. package/dist/zod.d.cts +52 -10
  136. package/dist/zod.d.mts +52 -10
  137. package/dist/zod.d.ts +52 -10
  138. package/dist/zod.mjs +6 -6
  139. package/dist/zod.mjs.map +1 -1
  140. package/package.json +19 -5
  141. package/dist/shared/attaform.BGk8cfw2.mjs.map +0 -1
  142. package/dist/shared/attaform.B_hph5AE.cjs.map +0 -1
  143. package/dist/shared/attaform.C5aYC_T8.mjs.map +0 -1
  144. package/dist/shared/attaform.Cd4AOfwu.cjs.map +0 -1
  145. package/dist/shared/attaform.CwLjUqmQ.cjs.map +0 -1
  146. package/dist/shared/attaform.DAKrGhxc.cjs.map +0 -1
  147. package/dist/shared/attaform.Dt7dEcHk.mjs.map +0 -1
  148. package/dist/shared/attaform.QG5TG8lB.mjs.map +0 -1
  149. package/dist/shared/attaform.tiWEVznj.mjs.map +0 -1
  150. package/dist/shared/attaform.z5j3LwJz.cjs.map +0 -1
@@ -1,5 +1,5 @@
1
- import { computed, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, readonly, reactive, toRaw, watch, markRaw, shallowRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, effectScope, nextTick } from 'vue';
2
- import { _ as __DEV__, j as canonicalizePath, G as segmentsForPathKey, t as isPathPrefix, b as FORM_ERRORS_PATH_KEY, S as SubmitErrorHandlerError, A as AnonPersistError, k as captureUserCallSite, I as INTERACTIVE_TAG_NAMES, r as getOrAssignElementId, e as ROOT_PATH_KEY, R as ROOT_PATH, h as allowSensitivePersist, F as FORM_ERRORS_PATH, l as coerceToPathKey, v as isSensitivePath, o as createPersistOptInRegistry, d as InvalidUseFormConfigError, q as ensureAttaformInstalled, J as useRegistry, z as kFormContext, B as kFormInstanceId, g as ReservedFormKeyError, n as createIsSensitivePath, y as kAttaformWizardActiveStepResolver, w as kAttaformAncestorWizard } from './attaform.QG5TG8lB.mjs';
1
+ import { computed, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, readonly, toRaw, reactive, watch, markRaw, shallowRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, effectScope, nextTick } from 'vue';
2
+ import { _ as __DEV__, j as canonicalizePath, G as segmentsForPathKey, t as isPathPrefix, b as FORM_ERRORS_PATH_KEY, S as SubmitErrorHandlerError, H as toError, A as AnonPersistError, k as captureUserCallSite, I as INTERACTIVE_TAG_NAMES, r as getOrAssignElementId, e as ROOT_PATH_KEY, R as ROOT_PATH, h as allowSensitivePersist, F as FORM_ERRORS_PATH, l as coerceToPathKey, v as isSensitivePath, o as createPersistOptInRegistry, d as InvalidUseFormConfigError, q as ensureAttaformInstalled, K as useRegistry, z as kFormContext, B as kFormInstanceId, g as ReservedFormKeyError, n as createIsSensitivePath, y as kAttaformWizardActiveStepResolver, w as kAttaformAncestorWizard } from './attaform.pmtahXKy.mjs';
3
3
 
4
4
  function safeAssign(target, key, value) {
5
5
  if (key === "__proto__") {
@@ -13,10 +13,14 @@ function safeAssign(target, key, value) {
13
13
  }
14
14
  target[key] = value;
15
15
  }
16
+ function isShadowedKey(key) {
17
+ return key in Object.prototype;
18
+ }
16
19
  function safeOwnRead(target, key) {
17
- if (key === "__proto__") {
18
- const desc = Object.getOwnPropertyDescriptor(target, "__proto__");
19
- return desc?.value;
20
+ if (isShadowedKey(key)) {
21
+ const desc = Object.getOwnPropertyDescriptor(target, key);
22
+ if (desc === void 0) return void 0;
23
+ return "value" in desc ? desc.value : target[key];
20
24
  }
21
25
  return target[key];
22
26
  }
@@ -35,6 +39,10 @@ function descendStep(value, segment) {
35
39
  }
36
40
  const record = value;
37
41
  const key = typeof segment === "number" ? String(segment) : segment;
42
+ if (isShadowedKey(key)) {
43
+ if (!safeOwnHas(record, key)) return NOT_FOUND;
44
+ return safeOwnRead(record, key);
45
+ }
38
46
  if (!(key in record)) return NOT_FOUND;
39
47
  return record[key];
40
48
  }
@@ -64,6 +72,7 @@ function hasAtPath(root, path) {
64
72
  return typeof last === "number" && last >= 0 && last < current.length;
65
73
  }
66
74
  const key = typeof last === "number" ? String(last) : last;
75
+ if (isShadowedKey(key)) return safeOwnHas(current, key);
67
76
  return key in current;
68
77
  }
69
78
  function isPlainRecord(value) {
@@ -458,17 +467,57 @@ function structuralSnapshot(value) {
458
467
  return out;
459
468
  }
460
469
 
461
- const defaultDisplayState = (field, formMeta) => {
462
- const gateOpen = formMeta.submissionAttempts > 0 || field.blurredAfterInteraction === true;
463
- if (!gateOpen) return "idle";
464
- if (field.validating === true) return "pending";
470
+ function isGateOpen(field, formMeta) {
471
+ return formMeta.submissionAttempts > 0 || field.blurredAfterInteraction === true;
472
+ }
473
+ function computeVerdict(field, formMeta) {
474
+ if (!isGateOpen(field, formMeta)) return "idle";
465
475
  const hasOwnError = field.errors.some(
466
476
  (e) => e.path.length === field.path.length && e.path.every((s, i) => s === field.path[i])
467
477
  );
468
478
  if (hasOwnError) return "error";
469
479
  if (field.valid === true && field.blank !== true && field.dirty === true) return "success";
470
480
  return "idle";
471
- };
481
+ }
482
+ function earliestNonNull(a, b) {
483
+ if (a === null) return b;
484
+ if (b === null) return a;
485
+ return a < b ? a : b;
486
+ }
487
+ const DEFAULT_TIMINGS = { showDelay: 120, minVisible: 120 };
488
+ const FOCUS_OUT_GRACE = 16;
489
+ const defaultFamily = /* @__PURE__ */ new WeakSet();
490
+ function isDefaultDisplayState(fn) {
491
+ return defaultFamily.has(fn);
492
+ }
493
+ function makeDefaultDisplayState({
494
+ showDelay,
495
+ minVisible
496
+ }) {
497
+ const reducer = (prev, { field, formMeta, validatingSince, transformingSince, now }) => {
498
+ const verdict = computeVerdict(field, formMeta);
499
+ if (!isGateOpen(field, formMeta)) return { display: verdict };
500
+ const inFlightSince = earliestNonNull(validatingSince, transformingSince);
501
+ if (inFlightSince === null) {
502
+ if (prev.display === "pending") {
503
+ const shownAt = prev.pendingShownAt ?? now;
504
+ if (now < shownAt + minVisible)
505
+ return { display: "pending", pendingShownAt: shownAt, reviewAt: shownAt + minVisible };
506
+ }
507
+ return { display: verdict };
508
+ }
509
+ if (prev.display === "pending")
510
+ return { display: "pending", pendingShownAt: prev.pendingShownAt ?? now };
511
+ const window = field.focused === false ? Math.min(showDelay, FOCUS_OUT_GRACE) : showDelay;
512
+ if (now - inFlightSince < window) {
513
+ return { display: prev.display, reviewAt: inFlightSince + window };
514
+ }
515
+ return { display: "pending", pendingShownAt: now, reviewAt: now + minVisible };
516
+ };
517
+ defaultFamily.add(reducer);
518
+ return reducer;
519
+ }
520
+ const defaultDisplayState = makeDefaultDisplayState(DEFAULT_TIMINGS);
472
521
  function resolveGetDisplayState(config) {
473
522
  return config ?? defaultDisplayState;
474
523
  }
@@ -587,6 +636,8 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
587
636
  if (blankForKey !== void 0) errors.push(...blankForKey);
588
637
  if (userForKey !== void 0) errors.push(...userForKey);
589
638
  const validating = (state.fieldValidationCounts.get(key) ?? 0) > 0;
639
+ const transforming = (state.fieldTransformCounts.get(key) ?? 0) > 0;
640
+ const transformError = state.transformErrors.get(key) ?? null;
590
641
  const gated = state.pathHasAsyncValidation(segments) && !state.firstValidationDone.value;
591
642
  const isOrphan = segments.length > 0 && !hasAtPath(state.form.value, segments) && isUnderStubAncestor(state, segments);
592
643
  const valid = !gated && errors.length === 0 && !validating && !isOrphan;
@@ -613,6 +664,9 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
613
664
  errors,
614
665
  validating,
615
666
  valid,
667
+ transforming,
668
+ busy: transforming || validating,
669
+ transformError,
616
670
  path: segments,
617
671
  ...computeFieldIdentity(formInstanceId, state.formKey, key),
618
672
  key: state.arrayElementKey(segments),
@@ -625,7 +679,21 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
625
679
  }
626
680
  function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBase, getDisplayState) {
627
681
  const base = buildLeafFieldStateBase(state, segments, key, formInstanceId);
628
- return decorateWithDerivedProps(base, state, getFormMetaBase, getDisplayState);
682
+ const validatingSince = state.fieldValidatingSince.get(key) ?? null;
683
+ const transformingSince = state.fieldTransformingSince.get(key) ?? null;
684
+ return decorateWithDerivedProps(
685
+ base,
686
+ state,
687
+ getFormMetaBase,
688
+ key,
689
+ validatingSince,
690
+ transformingSince,
691
+ false,
692
+ // revealedDescendantError: leaves have no descendants
693
+ false,
694
+ // isRoot: a leaf is never the form root
695
+ getDisplayState
696
+ );
629
697
  }
630
698
  function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
631
699
  const formValue = state.form.value;
@@ -641,8 +709,13 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
641
709
  let blurredAfterInteraction = false;
642
710
  let connected = false;
643
711
  let validating = false;
712
+ let validatingSince = null;
713
+ let transforming = false;
714
+ let transformingSince = null;
644
715
  let updatedAt = null;
645
716
  let asyncPending = false;
717
+ const submissionAttempts = state.submissionAttempts.value;
718
+ const blurredLeafSegments = [];
646
719
  for (const [leafKey, entry] of state.originals) {
647
720
  if (!isPathPrefix(segments, entry.segments)) continue;
648
721
  if (segments.length === entry.segments.length) continue;
@@ -657,9 +730,25 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
657
730
  if (leafRecord?.blurred === true) blurred = true;
658
731
  if (leafRecord?.touched === true) touched = true;
659
732
  if (leafRecord?.interacted === true) interacted = true;
660
- if (leafRecord?.blurredAfterInteraction === true) blurredAfterInteraction = true;
733
+ if (leafRecord?.blurredAfterInteraction === true) {
734
+ blurredAfterInteraction = true;
735
+ blurredLeafSegments.push(entry.segments);
736
+ }
661
737
  if (leafRecord?.connected === true) connected = true;
662
- if ((state.fieldValidationCounts.get(leafKey) ?? 0) > 0) validating = true;
738
+ if ((state.fieldValidationCounts.get(leafKey) ?? 0) > 0) {
739
+ validating = true;
740
+ const leafGateOpen = submissionAttempts > 0 || leafRecord?.blurredAfterInteraction === true;
741
+ const since = state.fieldValidatingSince.get(leafKey);
742
+ if (leafGateOpen && since !== void 0 && (validatingSince === null || since < validatingSince))
743
+ validatingSince = since;
744
+ }
745
+ if ((state.fieldTransformCounts.get(leafKey) ?? 0) > 0) {
746
+ transforming = true;
747
+ const leafGateOpen = submissionAttempts > 0 || leafRecord?.blurredAfterInteraction === true;
748
+ const since = state.fieldTransformingSince.get(leafKey);
749
+ if (leafGateOpen && since !== void 0 && (transformingSince === null || since < transformingSince))
750
+ transformingSince = since;
751
+ }
663
752
  if (state.pathHasAsyncValidationByKey(leafKey, entry.segments)) asyncPending = true;
664
753
  const ts = leafRecord?.updatedAt;
665
754
  if (ts !== void 0 && ts !== null) {
@@ -671,50 +760,101 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
671
760
  dirty = true;
672
761
  }
673
762
  const errors = aggregateErrorsAt(state, segments);
763
+ const revealedDescendantError = errors.length > 0 && errors.some((e) => {
764
+ const ePath = e.path;
765
+ if (ePath.length === segments.length && ePath.every((s, i) => s === segments[i])) return false;
766
+ if (submissionAttempts > 0) return true;
767
+ if (ePath.length === 1 && ePath[0] === "") return blurredAfterInteraction;
768
+ return blurredLeafSegments.some((s) => isPathPrefix(ePath, s));
769
+ });
674
770
  if (!asyncPending && state.pathHasAsyncValidation(segments)) asyncPending = true;
771
+ if ((state.fieldValidationCounts.get(key) ?? 0) > 0) {
772
+ validating = true;
773
+ const since = state.fieldValidatingSince.get(key);
774
+ if (since !== void 0 && (validatingSince === null || since < validatingSince))
775
+ validatingSince = since;
776
+ }
777
+ if ((state.fieldTransformCounts.get(key) ?? 0) > 0) {
778
+ transforming = true;
779
+ const since = state.fieldTransformingSince.get(key);
780
+ if (since !== void 0 && (transformingSince === null || since < transformingSince))
781
+ transformingSince = since;
782
+ }
783
+ const ownTransformError = state.transformErrors.get(key) ?? null;
675
784
  const gated = asyncPending && !state.firstValidationDone.value;
676
785
  const valid = !gated && errors.length === 0 && !validating;
677
786
  const resolved = state.schema.getFieldMetaAtPath ? state.schema.getFieldMetaAtPath(segments) : EMPTY_RESOLVED_FIELD_META;
678
787
  const lastSegment = segments.length === 0 ? "" : segments[segments.length - 1] ?? "";
679
788
  const label = resolved.label || humanize(lastSegment);
680
789
  return {
681
- value,
682
- original,
683
- pristine,
684
- dirty,
685
- focused,
686
- blurred,
687
- touched,
688
- interacted,
689
- blurredAfterInteraction,
690
- connected,
691
- element: null,
692
- elements: EMPTY_ELEMENTS,
693
- updatedAt,
694
- errors,
695
- validating,
696
- valid,
697
- path: segments,
698
- ...computeFieldIdentity(formInstanceId, state.formKey, key),
699
- key: state.arrayElementKey(segments),
700
- blank,
701
- label,
702
- description: resolved.description,
703
- placeholder: resolved.placeholder,
704
- meta: resolved.meta
790
+ base: {
791
+ value,
792
+ original,
793
+ pristine,
794
+ dirty,
795
+ focused,
796
+ blurred,
797
+ touched,
798
+ interacted,
799
+ blurredAfterInteraction,
800
+ connected,
801
+ element: null,
802
+ elements: EMPTY_ELEMENTS,
803
+ updatedAt,
804
+ errors,
805
+ validating,
806
+ valid,
807
+ transforming,
808
+ busy: transforming || validating,
809
+ // A container surfaces its OWN transform failure (a transform registered
810
+ // on the container path, e.g. a file normalizer) but never rolls up a
811
+ // descendant leaf's failure — that stays a per-field channel.
812
+ transformError: ownTransformError,
813
+ path: segments,
814
+ ...computeFieldIdentity(formInstanceId, state.formKey, key),
815
+ key: state.arrayElementKey(segments),
816
+ blank,
817
+ label,
818
+ description: resolved.description,
819
+ placeholder: resolved.placeholder,
820
+ meta: resolved.meta
821
+ },
822
+ validatingSince,
823
+ transformingSince,
824
+ revealedDescendantError
705
825
  };
706
826
  }
707
827
  function buildContainerFieldState(state, segments, key, formInstanceId, getFormMetaBase, getDisplayState) {
708
- const base = buildContainerFieldStateBase(state, segments, key, formInstanceId);
709
- return decorateWithDerivedProps(base, state, getFormMetaBase, getDisplayState);
828
+ const { base, validatingSince, transformingSince, revealedDescendantError } = buildContainerFieldStateBase(state, segments, key, formInstanceId);
829
+ return decorateWithDerivedProps(
830
+ base,
831
+ state,
832
+ getFormMetaBase,
833
+ key,
834
+ validatingSince,
835
+ transformingSince,
836
+ revealedDescendantError,
837
+ segments.length === 0,
838
+ getDisplayState
839
+ );
710
840
  }
711
- function decorateWithDerivedProps(base, state, getFormMetaBase, getDisplayState) {
841
+ function decorateWithDerivedProps(base, state, getFormMetaBase, key, validatingSince, transformingSince, revealedDescendantError, isRoot, getDisplayState) {
712
842
  const firstError = base.errors[0];
713
843
  const predicate = getDisplayState ?? state.getDisplayState;
714
844
  const formMeta = getFormMetaBase();
715
- let displayState;
845
+ const ctx = {
846
+ field: base,
847
+ formMeta,
848
+ validatingSince,
849
+ transformingSince,
850
+ // The engine's clock. Frozen to 0 under SSR (no clock, nothing in
851
+ // flight) so the reducer returns the plain verdict and hydration matches.
852
+ now: state.ssr ? 0 : Date.now()
853
+ };
854
+ let machine;
855
+ let rollupApplies = isDefaultDisplayState(predicate);
716
856
  try {
717
- displayState = predicate(base, formMeta);
857
+ machine = state.displayEngine.resolve(key, ctx, predicate);
718
858
  } catch (err) {
719
859
  if (__DEV__ && !warnedDisplayStatePredicates.has(predicate)) {
720
860
  warnedDisplayStatePredicates.add(predicate);
@@ -723,8 +863,11 @@ function decorateWithDerivedProps(base, state, getFormMetaBase, getDisplayState)
723
863
  err
724
864
  );
725
865
  }
726
- displayState = defaultDisplayState(base, formMeta);
866
+ machine = state.displayEngine.resolve(key, ctx, defaultDisplayState);
867
+ rollupApplies = true;
727
868
  }
869
+ const submitValidating = rollupApplies && isRoot && state.submitting.value && state.activeValidations.value > 0;
870
+ const displayState = machine.display === "pending" || submitValidating ? "pending" : rollupApplies && revealedDescendantError ? "error" : machine.display;
728
871
  return {
729
872
  ...base,
730
873
  displayState,
@@ -770,6 +913,15 @@ function liveKeysAtPath(state, segments) {
770
913
  if (typeof value === "object") return Object.keys(value);
771
914
  return [];
772
915
  }
916
+ function liveContainerHasKey(state, segments, key) {
917
+ const value = getAtPath(state.form.value, segments);
918
+ if (value === null || value === void 0 || typeof value !== "object") return false;
919
+ if (Array.isArray(value)) {
920
+ const index = Number(key);
921
+ return Number.isInteger(index) && index >= 0 && index < value.length && String(index) === key;
922
+ }
923
+ return Object.hasOwn(value, key);
924
+ }
773
925
  function isArrayPath(state, segments) {
774
926
  if (segments.length === 0) return false;
775
927
  return Array.isArray(getAtPath(state.form.value, segments));
@@ -798,6 +950,21 @@ const INTEGER_SEGMENT = /^(?:0|[1-9]\d*)$/;
798
950
  function keyToSegment(key) {
799
951
  return INTEGER_SEGMENT.test(key) ? Number(key) : key;
800
952
  }
953
+ function callableInvokeShim(method, surface, getDescent) {
954
+ const fnMethod = Reflect.get(Function.prototype, method);
955
+ return new Proxy((() => {
956
+ }), {
957
+ apply: (_target, _thisArg, args) => Reflect.apply(fnMethod, surface, args),
958
+ get: (_target, key) => Reflect.get(getDescent(), key),
959
+ has: (_target, key) => Reflect.has(getDescent(), key),
960
+ ownKeys: () => Reflect.ownKeys(getDescent()),
961
+ getOwnPropertyDescriptor: (_target, key) => {
962
+ const descriptor = Reflect.getOwnPropertyDescriptor(getDescent(), key);
963
+ if (descriptor !== void 0) descriptor.configurable = true;
964
+ return descriptor;
965
+ }
966
+ });
967
+ }
801
968
  function buildSurfaceProxy(opts) {
802
969
  const containerCache = /* @__PURE__ */ new Map();
803
970
  const leafViewCache = /* @__PURE__ */ new Map();
@@ -824,6 +991,8 @@ function buildSurfaceProxy(opts) {
824
991
  const cacheKey = `${JSON.stringify(segments)}+${isArrayLike ? "A" : "O"}`;
825
992
  const existing = containerCache.get(cacheKey);
826
993
  if (existing !== void 0) return existing;
994
+ const isFixedObject = opts.schema.isFixedObjectAtPath(segments);
995
+ const containerHasKey = (k) => opts.containerHasOwnKey !== void 0 ? opts.containerHasOwnKey(segments, k) : opts.containerOwnKeys?.(segments).includes(k) === true;
827
996
  const snapshotContainer = () => opts.materializeContainer === void 0 ? {} : opts.materializeContainer(segments);
828
997
  const {
829
998
  toString: containerToString,
@@ -831,8 +1000,9 @@ function buildSurfaceProxy(opts) {
831
1000
  toJSON: containerToJSON,
832
1001
  toPrimitive: containerToPrimitive
833
1002
  } = makeReadonlyCoercion(snapshotContainer);
834
- const target = isArrayLike ? [] : (() => {
835
- });
1003
+ const isRoot = segments.length === 0;
1004
+ const target = isRoot ? (() => {
1005
+ }) : isArrayLike ? [] : {};
836
1006
  const proxy = new Proxy(target, {
837
1007
  apply(_, __, args) {
838
1008
  const arg = args[0];
@@ -863,7 +1033,16 @@ function buildSurfaceProxy(opts) {
863
1033
  return key === "toString" ? containerToString : containerValueOf;
864
1034
  }
865
1035
  }
866
- return descendOrTerminate(childSegs);
1036
+ if (key === "hasOwnProperty" && !schemaHasPath(childSegs)) {
1037
+ return Object.prototype.hasOwnProperty;
1038
+ }
1039
+ if (isRoot && (key === "call" || key === "apply" || key === "bind")) {
1040
+ return callableInvokeShim(key, proxy, () => descendOrTerminate(childSegs));
1041
+ }
1042
+ if (opts.isTerminalAt?.(childSegs) === true || isFixedObject && schemaHasPath(childSegs) || containerHasKey(key)) {
1043
+ return descendOrTerminate(childSegs);
1044
+ }
1045
+ return void 0;
867
1046
  },
868
1047
  has(_, key) {
869
1048
  if (typeof key === "symbol") return Reflect.has(target, key);
@@ -948,15 +1127,8 @@ function buildSurfaceProxy(opts) {
948
1127
  toJSON: leafToJSONHandler,
949
1128
  toPrimitive: leafToPrimitive
950
1129
  } = makeReadonlyCoercion(snapshotLeaf);
951
- const target = (() => {
952
- });
1130
+ const target = {};
953
1131
  const proxy = new Proxy(target, {
954
- apply(_, __, args) {
955
- const arg = args[0];
956
- if (arg === void 0) return opts.resolveCallTarget(segments);
957
- const { segments: argSegs } = canonicalizePath(arg);
958
- return opts.resolveCallTarget(argSegs);
959
- },
960
1132
  get(_, key) {
961
1133
  if (typeof key === "symbol") {
962
1134
  if (key === Symbol.toPrimitive) return leafToPrimitive;
@@ -966,6 +1138,9 @@ function buildSurfaceProxy(opts) {
966
1138
  if (key === "toString") return leafToString;
967
1139
  if (key === "valueOf") return leafValueOf;
968
1140
  if (key === "toJSON") return leafToJSONHandler;
1141
+ if (key === "hasOwnProperty" && !schemaHasPath([...segments, keyToSegment(key)])) {
1142
+ return Object.prototype.hasOwnProperty;
1143
+ }
969
1144
  if (leafKeys.has(key)) {
970
1145
  const leaf = opts.resolveLeaf(segments);
971
1146
  return readLeafKey(leaf, key);
@@ -978,8 +1153,8 @@ function buildSurfaceProxy(opts) {
978
1153
  return true;
979
1154
  },
980
1155
  // Iteration: leaf-views expose the leaf-key set so
981
- // `JSON.stringify(form.fields.email)` produces a FieldState
982
- // snapshot rather than the function-target placeholder.
1156
+ // `Object.keys(form.fields.email)` / spread enumerate the
1157
+ // FieldState props. (`JSON.stringify` routes through `toJSON` above.)
983
1158
  ownKeys: () => Array.from(leafKeys),
984
1159
  getOwnPropertyDescriptor(_, key) {
985
1160
  if (typeof key !== "string") return void 0;
@@ -1088,6 +1263,12 @@ function buildErrorsProxy(state) {
1088
1263
  // library-produced verdicts (schema + derived-blank) at unreachable
1089
1264
  // paths stay hidden; user-supplied errors are unconditional.
1090
1265
  containerOwnKeys: (segments) => errorAwareContainerKeys(state, segments),
1266
+ // Fast path: a key the live form data holds short-circuits before the
1267
+ // O(n) error-store scan, so iterating `form.errors.<array>` over live
1268
+ // indices stays linear. The scan still runs for a key with no live
1269
+ // home — a server error at a non-schema key (`form.errors.ghost`) —
1270
+ // so it keeps surfacing while a genuinely-absent key reads undefined.
1271
+ containerHasOwnKey: (segments, key) => liveContainerHasKey(state, segments, key) || errorAwareContainerKeys(state, segments).includes(key),
1091
1272
  isArrayContainer: (segments) => isArrayPath(state, segments)
1092
1273
  });
1093
1274
  }
@@ -1261,6 +1442,9 @@ const FIELD_STATE_KEYS = /* @__PURE__ */ new Set([
1261
1442
  "errors",
1262
1443
  "validating",
1263
1444
  "valid",
1445
+ "transforming",
1446
+ "busy",
1447
+ "transformError",
1264
1448
  "displayState",
1265
1449
  "showErrors",
1266
1450
  "showPending",
@@ -1363,14 +1547,22 @@ function buildFieldStateProxy(state, formInstanceId, getFormMetaBase, options) {
1363
1547
  terminalCache.set(cacheKey, proxy);
1364
1548
  return proxy;
1365
1549
  }
1550
+ const surfaceSchema = state.schema;
1366
1551
  return buildSurfaceProxy({
1367
- schema: state.schema,
1552
+ schema: surfaceSchema,
1368
1553
  resolveLeaf: (path) => getFieldStateAt(path),
1369
1554
  leafKeys: FIELD_STATE_KEYS,
1370
1555
  readLeafKey: (computed, key) => computed.value[key],
1371
1556
  materializeContainer: (segments) => materializeFields(state, segments, snapshotFieldStateAt),
1372
- resolveCallTarget: (path) => fieldStateTerminalAt(path),
1557
+ // `form.fields(path)` resolves a FieldState for any path the SCHEMA
1558
+ // declares — a leaf, a container, an inactive discriminated-union
1559
+ // variant key, or an out-of-bounds array index (the element schema
1560
+ // admits any index). A path the schema doesn't have is a typo, not a
1561
+ // field, so it reads `undefined` rather than a phantom stub. The
1562
+ // empty path (`form.fields()`) is the root object, always valid.
1563
+ resolveCallTarget: (path) => surfaceSchema.getSlimPrimitiveTypesAtPath(path).size > 0 ? fieldStateTerminalAt(path) : void 0,
1373
1564
  containerOwnKeys: (segments) => liveKeysAtPath(state, segments),
1565
+ containerHasOwnKey: (segments, key) => liveContainerHasKey(state, segments, key),
1374
1566
  isArrayContainer: (segments) => isArrayPath(state, segments)
1375
1567
  });
1376
1568
  }
@@ -1417,90 +1609,6 @@ async function getStorageAdapter(storage) {
1417
1609
  }
1418
1610
  }
1419
1611
  }
1420
- const PERSISTED_ENVELOPE_VERSION = 6;
1421
- function readPersistedPayload(value) {
1422
- if (value === null || value === void 0 || typeof value !== "object") return null;
1423
- const envelope = value;
1424
- if (typeof envelope.v !== "number") return null;
1425
- if (envelope.v !== PERSISTED_ENVELOPE_VERSION) {
1426
- warnVersionMismatch(envelope.v);
1427
- return null;
1428
- }
1429
- if (envelope.data === void 0 || typeof envelope.data !== "object") return null;
1430
- return envelope;
1431
- }
1432
- const warnedVersions = __DEV__ ? /* @__PURE__ */ new Set() : null;
1433
- function warnVersionMismatch(observedVersion) {
1434
- if (warnedVersions === null) return;
1435
- if (warnedVersions.has(observedVersion)) return;
1436
- warnedVersions.add(observedVersion);
1437
- console.warn(
1438
- `[attaform] Dropping persisted draft \u2014 envelope v=${observedVersion}, but this version of the library expects v=${PERSISTED_ENVELOPE_VERSION}. The persisted shape changed across releases; older drafts can't be restored. New drafts saved this session will use the current envelope.`
1439
- );
1440
- }
1441
- function buildPersistedPayload(form, include, schemaErrors, userErrors, blankPaths) {
1442
- let transientList;
1443
- if (blankPaths !== void 0 && blankPaths.size > 0) {
1444
- transientList = [...blankPaths];
1445
- }
1446
- if (include === "form") {
1447
- if (transientList === void 0) return { v: PERSISTED_ENVELOPE_VERSION, data: { form } };
1448
- return {
1449
- v: PERSISTED_ENVELOPE_VERSION,
1450
- data: { form, blankPaths: transientList }
1451
- };
1452
- }
1453
- return {
1454
- v: PERSISTED_ENVELOPE_VERSION,
1455
- data: {
1456
- form,
1457
- schemaErrors: [...schemaErrors.entries()].map(([k, v]) => [k, [...v]]),
1458
- userErrors: [...userErrors.entries()].map(([k, v]) => [k, [...v]]),
1459
- ...transientList !== void 0 ? { blankPaths: transientList } : {}
1460
- }
1461
- };
1462
- }
1463
- function createDebouncedWriter(write, debounceMs) {
1464
- let timer = null;
1465
- let pending = null;
1466
- let writeGeneration = 0;
1467
- function runWrite() {
1468
- const gen = ++writeGeneration;
1469
- pending = write().finally(() => {
1470
- if (writeGeneration === gen) pending = null;
1471
- });
1472
- }
1473
- function schedule() {
1474
- if (timer !== null) clearTimeout(timer);
1475
- if (debounceMs === 0) {
1476
- runWrite();
1477
- return;
1478
- }
1479
- timer = setTimeout(() => {
1480
- timer = null;
1481
- runWrite();
1482
- }, debounceMs);
1483
- }
1484
- async function flush() {
1485
- if (timer !== null) {
1486
- clearTimeout(timer);
1487
- timer = null;
1488
- runWrite();
1489
- }
1490
- while (pending !== null) {
1491
- const awaited = pending;
1492
- await awaited;
1493
- if (pending === awaited) break;
1494
- }
1495
- }
1496
- function cancel() {
1497
- if (timer !== null) {
1498
- clearTimeout(timer);
1499
- timer = null;
1500
- }
1501
- }
1502
- return { schedule, flush, cancel };
1503
- }
1504
1612
  function resolveStorageKeyBase(config, formKey) {
1505
1613
  return config.key ?? `${PERSISTENCE_KEY_PREFIX}${formKey}`;
1506
1614
  }
@@ -1547,47 +1655,6 @@ async function sweepNonConfiguredStandardStoresForOrphans(configured, base) {
1547
1655
  }
1548
1656
  }
1549
1657
  }
1550
- function pluckPaths(form, pathKeys) {
1551
- let sparse = void 0;
1552
- for (const pathKey of pathKeys) {
1553
- const segments = segmentsForPathKey(pathKey);
1554
- if (segments === null) continue;
1555
- const value = getAtPath(form, segments);
1556
- if (value === void 0) continue;
1557
- sparse = setAtPath(sparse ?? {}, segments, value);
1558
- }
1559
- return sparse ?? {};
1560
- }
1561
- function stripUnacknowledgedSensitiveLeaves(form, optedInPaths, isSensitivePath) {
1562
- const acknowledgedSensitive = [];
1563
- for (const key of optedInPaths) {
1564
- const segs = segmentsForPathKey(key);
1565
- if (segs !== null && isSensitivePath(segs)) acknowledgedSensitive.push(segs);
1566
- }
1567
- const coveredByAcknowledged = (path) => acknowledgedSensitive.some((prefix) => isPathPrefix(prefix, path));
1568
- const walk = (path, value) => {
1569
- if (path.length > 0 && isSensitivePath(path) && !coveredByAcknowledged(path)) {
1570
- return void 0;
1571
- }
1572
- if (value === null || typeof value !== "object") return value;
1573
- if (Array.isArray(value)) return value.map((item, i) => walk([...path, i], item));
1574
- if (!isPlainRecord(value)) return value;
1575
- const out = {};
1576
- for (const key of Object.keys(value)) {
1577
- const walked = walk([...path, key], value[key]);
1578
- if (walked !== void 0) safeAssign(out, key, walked);
1579
- }
1580
- return out;
1581
- };
1582
- return walk([], form);
1583
- }
1584
- function filterErrorsByPaths(errors, pathKeys) {
1585
- const out = /* @__PURE__ */ new Map();
1586
- for (const [key, value] of errors) {
1587
- if (pathKeys.has(key)) out.set(key, value);
1588
- }
1589
- return out;
1590
- }
1591
1658
  function mergeSparseHydration(schemaDefaults, sparse, schema) {
1592
1659
  return mergeDeep(schemaDefaults, sparse, [], schema);
1593
1660
  }
@@ -1771,7 +1838,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1771
1838
  if (!result.ok) return result.error;
1772
1839
  return stripData(composeWithDerivedBlank(result.refinement, result.segments));
1773
1840
  }
1774
- async function process(pathInput) {
1841
+ async function parse(pathInput) {
1775
1842
  const result = await runImperativeValidation(pathInput, {
1776
1843
  cancelInFlight: false,
1777
1844
  commitToSchemaErrors: false
@@ -1824,7 +1891,10 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1824
1891
  state.activeSubmissions.value += 1;
1825
1892
  state.submitting.value = true;
1826
1893
  state.submitError.value = null;
1894
+ state.clearUserErrors();
1895
+ while (state.activeTransforms.value > 0) await state.settleTransforms();
1827
1896
  state.cancelFieldValidation();
1897
+ state.displayEngine.clear();
1828
1898
  state.activeValidations.value += 1;
1829
1899
  const refinement = await runRefinementValidation(state.form.value, void 0);
1830
1900
  const merged = composeWithDerivedBlank(refinement, void 0);
@@ -1861,9 +1931,8 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1861
1931
  state.emitSubmitSuccess();
1862
1932
  } catch (err) {
1863
1933
  if (state.submissionGeneration.value === genAtEntry) {
1864
- state.submitError.value = err;
1934
+ state.submitError.value = toError(err);
1865
1935
  }
1866
- throw err;
1867
1936
  } finally {
1868
1937
  if (!validationSettled) {
1869
1938
  state.activeValidations.value = Math.max(0, state.activeValidations.value - 1);
@@ -1877,7 +1946,7 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1877
1946
  };
1878
1947
  return submitHandler;
1879
1948
  };
1880
- return { validate, validateAsync, process, handleSubmit };
1949
+ return { validate, validateAsync, parse, handleSubmit };
1881
1950
  }
1882
1951
  function toSegments(pathInput) {
1883
1952
  return canonicalizePath(pathInput).segments;
@@ -2350,6 +2419,23 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2350
2419
  markConnectedOptimistically: () => {
2351
2420
  state.markConnectedOptimistically(segments);
2352
2421
  },
2422
+ // --- Async transform lifecycle (internal; the directive's
2423
+ // deferred orchestrator is the only legitimate consumer). Thin
2424
+ // path-bound delegates to the store's per-path token / counter
2425
+ // machinery — same pattern as `markBlank` / `setValueWithInternalPath`,
2426
+ // so the directive (which holds only this RegisterValue, never the
2427
+ // store) can drive the busy/discard/error bookkeeping. ---
2428
+ beginTransform: (holder) => state.beginTransform(pathKey, holder),
2429
+ isCurrentTransform: (token) => state.isCurrentTransform(pathKey, token),
2430
+ endTransform: (token) => state.endTransform(pathKey, token),
2431
+ setTransformError: (err) => state.setTransformError(pathKey, err),
2432
+ // Synchronous read of "is a transform in flight at this path". The
2433
+ // orchestrator's `beginTransform` bumps the count before the
2434
+ // listener's force-sync block runs, so the directive reads this to
2435
+ // skip reverting the DOM to stale storage mid-flight.
2436
+ get transforming() {
2437
+ return (state.fieldTransformCounts.get(pathKey) ?? 0) > 0;
2438
+ },
2353
2439
  path: pathKey,
2354
2440
  // Frozen so a wrapper component can pass `rv.segments` directly
2355
2441
  // to `form.fields(...)` without defensive copying — and so test
@@ -2548,7 +2634,9 @@ function expandUnsetAt(segments, schema, paths) {
2548
2634
  function buildCallableReadonlySnapshotProxy(opts) {
2549
2635
  const target = (() => {
2550
2636
  });
2551
- const { toString, valueOf, toJSON, toPrimitive } = makeReadonlyCoercion(opts.snapshot);
2637
+ const { toString, valueOf, toJSON, toPrimitive } = makeReadonlyCoercion(
2638
+ opts.coercionSnapshot ?? opts.snapshot
2639
+ );
2552
2640
  const callResolve = opts.resolveCall ?? ((arg) => opts.resolveKey(String(arg)));
2553
2641
  return new Proxy(target, {
2554
2642
  apply(_, __, args) {
@@ -2597,27 +2685,52 @@ function buildCallableReadonlySnapshotProxy(opts) {
2597
2685
  });
2598
2686
  }
2599
2687
 
2688
+ function materializeFormValue(node) {
2689
+ if (node === null || typeof node !== "object") return node;
2690
+ if (Array.isArray(node)) {
2691
+ const out2 = new Array(node.length);
2692
+ for (let i = 0; i < node.length; i++) out2[i] = materializeFormValue(node[i]);
2693
+ return out2;
2694
+ }
2695
+ if (!isPlainRecord(node)) return toRaw(node);
2696
+ const rec = node;
2697
+ const out = {};
2698
+ for (const key of Object.keys(rec)) {
2699
+ safeAssign(out, key, materializeFormValue(safeOwnRead(rec, key)));
2700
+ }
2701
+ return out;
2702
+ }
2600
2703
  function buildValuesProxy(form) {
2601
2704
  const inner = computed(() => readonly(form.value));
2602
2705
  return buildCallableReadonlySnapshotProxy({
2603
2706
  surface: "form.values",
2604
2707
  snapshot: () => inner.value,
2708
+ // Faithful, reactivity-preserving serialisation: walk the reactive
2709
+ // proxy with own-safe reads so `JSON.stringify(form.values)` /
2710
+ // `String(form.values)` reflect the stored data — including a field
2711
+ // literally named `hasOwnProperty` that Vue would otherwise shim —
2712
+ // while still tracking the per-key reads that drive re-render.
2713
+ coercionSnapshot: () => materializeFormValue(inner.value),
2605
2714
  // Read through the readonly proxy at access time so Vue's
2606
2715
  // dependency tracking lands inside the consumer's active effect
2607
2716
  // — `inner.value[key]` is what triggers per-key tracking.
2608
- resolveKey: (key) => inner.value[key],
2609
- // Dynamic path: walk segments through the readonly proxy. Each
2610
- // step reads through the proxy's own get traps so dependency
2611
- // tracking propagates at every level.
2612
- resolveCall: (arg) => {
2613
- const { segments } = canonicalizePath(arg);
2614
- let cursor = inner.value;
2615
- for (const seg of segments) {
2616
- if (cursor === null || cursor === void 0) return void 0;
2617
- cursor = cursor[seg];
2618
- }
2619
- return cursor;
2620
- },
2717
+ //
2718
+ // Prototype-shadowed names (`hasOwnProperty`, `constructor`, …) read
2719
+ // off the RAW target instead: own-shadows-inherited semantics still
2720
+ // hold (a data field by that name returns its stored value), but
2721
+ // when there's no such field the inherited member resolves — so
2722
+ // `form.values.hasOwnProperty('x')` keeps working as the real
2723
+ // method. The raw read dodges Vue's `hasOwnProperty` proxy shim,
2724
+ // which would otherwise mask a data field of that name. (`toString`
2725
+ // / `valueOf` / `toJSON` never reach here the base get trap
2726
+ // intercepts them as coercion handlers first.)
2727
+ resolveKey: (key) => isShadowedKey(key) ? toRaw(inner.value)[key] : inner.value[key],
2728
+ // Dynamic path: walk segments through the readonly proxy with the
2729
+ // same own-property-safe descent the rest of the runtime uses
2730
+ // (`getAtPath`), so `form.values('a.hasOwnProperty')` resolves the
2731
+ // stored value. Per-level reads still propagate Vue's tracking for
2732
+ // ordinary keys.
2733
+ resolveCall: (arg) => getAtPath(inner.value, canonicalizePath(arg).segments),
2621
2734
  ownKeys: () => Reflect.ownKeys(inner.value),
2622
2735
  hasKey: (key) => Reflect.has(inner.value, key),
2623
2736
  describeKey: (key) => {
@@ -2641,7 +2754,12 @@ function buildFormApi(state, formInstanceId, options = {}) {
2641
2754
  return meta === void 0 ? { instance: instanceMeta } : { ...meta, instance: instanceMeta };
2642
2755
  };
2643
2756
  const getFormMetaBase = () => {
2644
- const rootBase = buildContainerFieldStateBase(state, ROOT_PATH, ROOT_PATH_KEY, formInstanceId);
2757
+ const { base: rootBase } = buildContainerFieldStateBase(
2758
+ state,
2759
+ ROOT_PATH,
2760
+ ROOT_PATH_KEY,
2761
+ formInstanceId
2762
+ );
2645
2763
  return {
2646
2764
  ...rootBase,
2647
2765
  submitting: state.submitting.value,
@@ -2673,12 +2791,12 @@ function buildFormApi(state, formInstanceId, options = {}) {
2673
2791
  const {
2674
2792
  validate: validateBuilt,
2675
2793
  validateAsync: validateAsyncBuilt,
2676
- process: processBuilt,
2794
+ parse: parseBuilt,
2677
2795
  handleSubmit
2678
2796
  } = buildProcessForm(state, formInstanceId, processOptions);
2679
2797
  const validate = (pathInput) => validateBuilt(pathInput);
2680
2798
  const validateAsync = (pathInput) => validateAsyncBuilt(pathInput);
2681
- const process = (pathInput) => processBuilt(pathInput);
2799
+ const parse = (pathInput) => parseBuilt(pathInput);
2682
2800
  function pathToRef(pathInput) {
2683
2801
  const segments = canonicalizePath(pathInput).segments;
2684
2802
  return computed(() => getAtPath(state.form.value, segments));
@@ -2906,6 +3024,21 @@ function buildFormApi(state, formInstanceId, options = {}) {
2906
3024
  // keep the explicit form-level computation for the gate.
2907
3025
  valid,
2908
3026
  errors: metaErrors,
3027
+ // Whole-form transforming mirrors the global `activeTransforms`
3028
+ // counter ORed with any per-leaf transform in flight (the root
3029
+ // rollup), exactly as `validating` composes its lifecycle and
3030
+ // per-field sources. `busy` is the union of both work signals at
3031
+ // the form level. `transformError` is leaf-only, so the root
3032
+ // rollup reads it as `null` (kept for FieldState-shape parity).
3033
+ transforming: computed(
3034
+ () => state.activeTransforms.value > 0 || rootFieldState.value.transforming
3035
+ ),
3036
+ busy: computed(
3037
+ () => state.activeValidations.value > 0 || state.activeTransforms.value > 0 || rootFieldState.value.validating || rootFieldState.value.transforming
3038
+ ),
3039
+ get transformError() {
3040
+ return rootFieldState.value.transformError;
3041
+ },
2909
3042
  // `displayState` / the `show*` booleans / `firstError` flow
2910
3043
  // through the same root field-state computed as the rest of the
2911
3044
  // FieldState surface, so `form.meta.displayState` matches
@@ -2976,7 +3109,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2976
3109
  instanceId: formInstanceId
2977
3110
  })
2978
3111
  );
2979
- const persistence = state.modules.get(PERSISTENCE_MODULE_KEY);
3112
+ const persistenceHandle = state.modules.get(PERSISTENCE_MODULE_KEY);
2980
3113
  const reset = (nextDefaultValues) => {
2981
3114
  if (nextDefaultValues === void 0) {
2982
3115
  state.reset();
@@ -2991,15 +3124,15 @@ function buildFormApi(state, formInstanceId, options = {}) {
2991
3124
  state.originalBlankPaths.add(pathKey);
2992
3125
  }
2993
3126
  }
2994
- if (persistence !== void 0) {
2995
- void persistence.clearPersistedDraft().catch(() => void 0);
3127
+ if (persistenceHandle !== void 0) {
3128
+ void persistenceHandle.ready.then((m) => m?.clearPersistedDraft()).catch(() => void 0);
2996
3129
  }
2997
3130
  };
2998
3131
  const resetField = (pathInput) => {
2999
3132
  const segments = canonicalizePath(pathInput).segments;
3000
3133
  state.resetField(segments);
3001
- if (persistence !== void 0) {
3002
- void persistence.clearPersistedDraft(segments).catch(() => void 0);
3134
+ if (persistenceHandle !== void 0) {
3135
+ void persistenceHandle.ready.then((m) => m?.clearPersistedDraft(segments)).catch(() => void 0);
3003
3136
  }
3004
3137
  };
3005
3138
  function clear(pathInput) {
@@ -3017,10 +3150,14 @@ function buildFormApi(state, formInstanceId, options = {}) {
3017
3150
  )) {
3018
3151
  return;
3019
3152
  }
3153
+ if (persistenceHandle === void 0) return;
3154
+ const persistence = await persistenceHandle.ready;
3020
3155
  if (persistence === void 0) return;
3021
3156
  await persistence.writePathImmediately(segments);
3022
3157
  };
3023
3158
  const clearPersistedDraft = async (pathInput) => {
3159
+ if (persistenceHandle === void 0) return;
3160
+ const persistence = await persistenceHandle.ready;
3024
3161
  if (persistence === void 0) return;
3025
3162
  if (pathInput === void 0) {
3026
3163
  await persistence.clearPersistedDraft();
@@ -3130,7 +3267,8 @@ function buildFormApi(state, formInstanceId, options = {}) {
3130
3267
  setValue: gated(setValueImpl),
3131
3268
  validate: gated(validate),
3132
3269
  validateAsync: gated(validateAsync),
3133
- process: gated(process),
3270
+ parse: gated(parse),
3271
+ settleTransforms: gated(state.settleTransforms),
3134
3272
  register: gated(register),
3135
3273
  key: state.formKey,
3136
3274
  // Auto-unwrapping views over the per-store async-defaults lifecycle
@@ -3203,6 +3341,93 @@ function buildFormApi(state, formInstanceId, options = {}) {
3203
3341
  };
3204
3342
  }
3205
3343
 
3344
+ const IDLE = Object.freeze({ display: "idle" });
3345
+ const MAX_DELAY = 2147483647;
3346
+ function createDisplayEngine(ssr) {
3347
+ const machines = /* @__PURE__ */ new Map();
3348
+ const tick = ref(0);
3349
+ let timer = null;
3350
+ let timerTarget = null;
3351
+ let lastFiredTarget = null;
3352
+ function clearTimer() {
3353
+ if (timer !== null) {
3354
+ clearTimeout(timer);
3355
+ timer = null;
3356
+ timerTarget = null;
3357
+ }
3358
+ }
3359
+ function nearestReviewAt() {
3360
+ let min = null;
3361
+ for (const m of machines.values()) {
3362
+ if (m.reviewAt === void 0 || !Number.isFinite(m.reviewAt)) continue;
3363
+ if (min === null || m.reviewAt < min) min = m.reviewAt;
3364
+ }
3365
+ return min;
3366
+ }
3367
+ function rearm(now) {
3368
+ const target = nearestReviewAt();
3369
+ if (target === null) {
3370
+ clearTimer();
3371
+ return;
3372
+ }
3373
+ if (timer !== null && timerTarget === target) return;
3374
+ if (target === lastFiredTarget) {
3375
+ clearTimer();
3376
+ return;
3377
+ }
3378
+ clearTimer();
3379
+ timerTarget = target;
3380
+ timer = setTimeout(
3381
+ () => {
3382
+ timer = null;
3383
+ timerTarget = null;
3384
+ lastFiredTarget = target;
3385
+ tick.value++;
3386
+ },
3387
+ Math.min(MAX_DELAY, Math.max(0, target - now))
3388
+ );
3389
+ }
3390
+ function resolve(key, ctx, reducer) {
3391
+ void tick.value;
3392
+ const prev = machines.get(key) ?? IDLE;
3393
+ const machine = reducer(prev, ctx);
3394
+ if (ssr) return machine;
3395
+ const active = machine.display !== "idle" || machine.reviewAt !== void 0 && Number.isFinite(machine.reviewAt);
3396
+ if (active) machines.set(key, machine);
3397
+ else machines.delete(key);
3398
+ rearm(ctx.now);
3399
+ return machine;
3400
+ }
3401
+ function clear() {
3402
+ machines.clear();
3403
+ clearTimer();
3404
+ lastFiredTarget = null;
3405
+ }
3406
+ let detachVisibility = null;
3407
+ if (!ssr && typeof document !== "undefined") {
3408
+ const onVisible = () => {
3409
+ if (document.visibilityState === "visible" && machines.size > 0) tick.value++;
3410
+ };
3411
+ document.addEventListener("visibilitychange", onVisible);
3412
+ detachVisibility = () => document.removeEventListener("visibilitychange", onVisible);
3413
+ }
3414
+ function dispose() {
3415
+ clear();
3416
+ if (detachVisibility !== null) {
3417
+ detachVisibility();
3418
+ detachVisibility = null;
3419
+ }
3420
+ }
3421
+ return {
3422
+ resolve,
3423
+ clear,
3424
+ dispose,
3425
+ size: () => machines.size,
3426
+ has: (key) => machines.has(key),
3427
+ hasTimer: () => timer !== null
3428
+ };
3429
+ }
3430
+
3206
3431
  function applyDuStubs(schema, data, options = {}) {
3207
3432
  const warned = options.warn === true ? /* @__PURE__ */ new Set() : void 0;
3208
3433
  return walkDuStubs(schema, data, options.basePath ?? [], warned);
@@ -3541,6 +3766,7 @@ function createArrayBookkeeping(deps) {
3541
3766
  blankPaths,
3542
3767
  originalBlankPaths,
3543
3768
  fieldValidationCounts,
3769
+ fieldValidatingSince,
3544
3770
  fieldValidationState,
3545
3771
  schemaErrors,
3546
3772
  activeValidations,
@@ -3566,6 +3792,7 @@ function createArrayBookkeeping(deps) {
3566
3792
  }));
3567
3793
  migrateSetSubtree(blankPaths, arrayPath, remap);
3568
3794
  migrateSetSubtree(originalBlankPaths, arrayPath, remap);
3795
+ migrateMapSubtree(fieldValidatingSince, arrayPath, remap, (since) => since);
3569
3796
  migrateMapSubtree(fieldValidationCounts, arrayPath, remap, (count) => count);
3570
3797
  arrayIdentity.applyRemap(arrayPath, remap);
3571
3798
  }
@@ -3625,6 +3852,18 @@ function isHydratedFieldRecord(value) {
3625
3852
  const r = value;
3626
3853
  return Array.isArray(r.path) && (typeof r.updatedAt === "string" || r.updatedAt === null) && typeof r.connected === "boolean" && (typeof r.focused === "boolean" || r.focused === null) && (typeof r.blurred === "boolean" || r.blurred === null) && typeof r.touched === "boolean" && typeof r.interacted === "boolean" && typeof r.blurredAfterInteraction === "boolean";
3627
3854
  }
3855
+ function withClearedHistoryFlags(record, now) {
3856
+ return {
3857
+ path: record.path,
3858
+ updatedAt: now,
3859
+ connected: record.connected,
3860
+ focused: record.focused,
3861
+ blurred: record.blurred,
3862
+ touched: false,
3863
+ interacted: false,
3864
+ blurredAfterInteraction: false
3865
+ };
3866
+ }
3628
3867
  function isHydratedValidationErrorArray(value) {
3629
3868
  if (!Array.isArray(value)) return false;
3630
3869
  for (const entry of value) {
@@ -3752,6 +3991,9 @@ function createFormStore(options) {
3752
3991
  const resolvedIsSensitivePath = options.isSensitivePath ?? isSensitivePath;
3753
3992
  const cleanupHooks = [];
3754
3993
  const modules = /* @__PURE__ */ new Map();
3994
+ const fieldValidatingSince = reactive(/* @__PURE__ */ new Map());
3995
+ const displayEngine = createDisplayEngine(ssr);
3996
+ registerCleanup(() => displayEngine.dispose());
3755
3997
  const completedConstraints = defaultValues === void 0 ? void 0 : mergeStructural(schema, [], defaultValues);
3756
3998
  const schemaResponse = schema.getDefaultValues({
3757
3999
  useDefaultSchemaValues: true,
@@ -3880,12 +4122,118 @@ function createFormStore(options) {
3880
4122
  }
3881
4123
  const fieldValidationCounts = reactive(/* @__PURE__ */ new Map());
3882
4124
  function incFieldValidation(key) {
3883
- fieldValidationCounts.set(key, (fieldValidationCounts.get(key) ?? 0) + 1);
4125
+ fieldValidatingSince.set(key, ssr ? 0 : Date.now());
4126
+ const prevCount = fieldValidationCounts.get(key) ?? 0;
4127
+ fieldValidationCounts.set(key, prevCount + 1);
3884
4128
  }
3885
4129
  function decFieldValidation(key) {
3886
4130
  const next = (fieldValidationCounts.get(key) ?? 0) - 1;
3887
- if (next <= 0) fieldValidationCounts.delete(key);
3888
- else fieldValidationCounts.set(key, next);
4131
+ if (next <= 0) {
4132
+ fieldValidationCounts.delete(key);
4133
+ fieldValidatingSince.delete(key);
4134
+ } else {
4135
+ fieldValidationCounts.set(key, next);
4136
+ }
4137
+ }
4138
+ const fieldTransformCounts = reactive(/* @__PURE__ */ new Map());
4139
+ const fieldTransformingSince = reactive(/* @__PURE__ */ new Map());
4140
+ const transformErrors = reactive(/* @__PURE__ */ new Map());
4141
+ const activeTransforms = ref(0);
4142
+ const transformRuns = /* @__PURE__ */ new Map();
4143
+ let transformTokenSeq = 0;
4144
+ const transformWaiters = [];
4145
+ function incFieldTransform(key) {
4146
+ fieldTransformingSince.set(key, ssr ? 0 : Date.now());
4147
+ fieldTransformCounts.set(key, (fieldTransformCounts.get(key) ?? 0) + 1);
4148
+ }
4149
+ function decFieldTransform(key) {
4150
+ const next = (fieldTransformCounts.get(key) ?? 0) - 1;
4151
+ if (next <= 0) {
4152
+ fieldTransformCounts.delete(key);
4153
+ fieldTransformingSince.delete(key);
4154
+ } else {
4155
+ fieldTransformCounts.set(key, next);
4156
+ }
4157
+ }
4158
+ function flushSettledTransformWaiters() {
4159
+ if (transformWaiters.length === 0) return;
4160
+ const globalIdle = activeTransforms.value === 0;
4161
+ for (let i = transformWaiters.length - 1; i >= 0; i--) {
4162
+ const w = transformWaiters[i];
4163
+ if (w === void 0) continue;
4164
+ const idle = w.key === null ? globalIdle : (fieldTransformCounts.get(w.key) ?? 0) === 0;
4165
+ if (idle) {
4166
+ transformWaiters.splice(i, 1);
4167
+ w.resolve();
4168
+ }
4169
+ }
4170
+ }
4171
+ function releaseTransformRun(key, run) {
4172
+ if (run.released) return;
4173
+ run.released = true;
4174
+ run.holder.aborted = true;
4175
+ run.holder.controller?.abort();
4176
+ activeTransforms.value = Math.max(0, activeTransforms.value - 1);
4177
+ decFieldTransform(key);
4178
+ }
4179
+ function beginTransform(key, holder) {
4180
+ const prior = transformRuns.get(key);
4181
+ if (prior !== void 0) releaseTransformRun(key, prior);
4182
+ const token = ++transformTokenSeq;
4183
+ transformRuns.set(key, { token, holder, released: false });
4184
+ incFieldTransform(key);
4185
+ activeTransforms.value += 1;
4186
+ if (transformErrors.has(key)) transformErrors.delete(key);
4187
+ return token;
4188
+ }
4189
+ function isCurrentTransform(key, token) {
4190
+ return transformRuns.get(key)?.token === token;
4191
+ }
4192
+ function endTransform(key, token) {
4193
+ const run = transformRuns.get(key);
4194
+ if (run?.token === token) {
4195
+ if (!run.released) {
4196
+ activeTransforms.value = Math.max(0, activeTransforms.value - 1);
4197
+ decFieldTransform(key);
4198
+ }
4199
+ transformRuns.delete(key);
4200
+ }
4201
+ flushSettledTransformWaiters();
4202
+ }
4203
+ function setTransformError(key, err) {
4204
+ transformErrors.set(key, err);
4205
+ }
4206
+ function cancelTransforms() {
4207
+ for (const [key, run] of [...transformRuns]) {
4208
+ releaseTransformRun(key, run);
4209
+ transformRuns.delete(key);
4210
+ }
4211
+ if (transformErrors.size > 0) transformErrors.clear();
4212
+ flushSettledTransformWaiters();
4213
+ }
4214
+ function cancelTransformsUnder(prefix) {
4215
+ for (const [key, run] of [...transformRuns]) {
4216
+ const segs = segmentsForPathKey(key);
4217
+ if (segs === null) continue;
4218
+ if (!isPathPrefix(prefix, segs)) continue;
4219
+ releaseTransformRun(key, run);
4220
+ transformRuns.delete(key);
4221
+ transformErrors.delete(key);
4222
+ }
4223
+ flushSettledTransformWaiters();
4224
+ }
4225
+ function settleTransforms(path) {
4226
+ if (path === void 0) {
4227
+ if (activeTransforms.value === 0) return Promise.resolve();
4228
+ return new Promise((resolve) => {
4229
+ transformWaiters.push({ key: null, resolve });
4230
+ });
4231
+ }
4232
+ const { key } = canonicalizePath(path);
4233
+ if ((fieldTransformCounts.get(key) ?? 0) === 0) return Promise.resolve();
4234
+ return new Promise((resolve) => {
4235
+ transformWaiters.push({ key, resolve });
4236
+ });
3889
4237
  }
3890
4238
  const initStamp = (/* @__PURE__ */ new Date()).toISOString();
3891
4239
  diffAndApply({}, schemaInitialData, [], (patch) => {
@@ -3973,6 +4321,7 @@ function createFormStore(options) {
3973
4321
  blankPaths,
3974
4322
  originalBlankPaths,
3975
4323
  fieldValidationCounts,
4324
+ fieldValidatingSince,
3976
4325
  fieldValidationState,
3977
4326
  schemaErrors,
3978
4327
  activeValidations,
@@ -4035,6 +4384,7 @@ function createFormStore(options) {
4035
4384
  }
4036
4385
  }
4037
4386
  }
4387
+ if (transformRuns.size !== 0) cancelTransformsUnder(path);
4038
4388
  if (meta?.skipDiscriminatorReshape !== true) {
4039
4389
  if (path.length > 0) {
4040
4390
  const last = path[path.length - 1];
@@ -4240,7 +4590,7 @@ function createFormStore(options) {
4240
4590
  prev.controller.abort();
4241
4591
  }
4242
4592
  const controller = new AbortController();
4243
- const fresh = { controller, timer: null, settled: false };
4593
+ const fresh = { controller, timer: null, settled: false, released: false };
4244
4594
  fieldValidationState.set(key, fresh);
4245
4595
  const myEpoch = ++scheduleEpoch;
4246
4596
  const run = () => {
@@ -4278,8 +4628,10 @@ function createFormStore(options) {
4278
4628
  applySchemaErrorsForSubtree(scopePath ?? [], restamped);
4279
4629
  }).catch(() => {
4280
4630
  }).finally(() => {
4281
- activeValidations.value = Math.max(0, activeValidations.value - 1);
4282
- decFieldValidation(key);
4631
+ if (!fresh.released) {
4632
+ activeValidations.value = Math.max(0, activeValidations.value - 1);
4633
+ decFieldValidation(key);
4634
+ }
4283
4635
  fresh.settled = true;
4284
4636
  });
4285
4637
  };
@@ -4301,6 +4653,22 @@ function createFormStore(options) {
4301
4653
  }
4302
4654
  fieldValidationState.clear();
4303
4655
  }
4656
+ function cancelFieldValidationUnder(prefix) {
4657
+ for (const [key, entry] of [...fieldValidationState]) {
4658
+ const segs = segmentsForPathKey(key);
4659
+ if (segs === null) continue;
4660
+ if (!isPathPrefix(prefix, segs)) continue;
4661
+ if (entry.timer !== null) {
4662
+ clearTimeout(entry.timer);
4663
+ } else if (!entry.settled && !entry.released) {
4664
+ activeValidations.value = Math.max(0, activeValidations.value - 1);
4665
+ decFieldValidation(key);
4666
+ entry.released = true;
4667
+ }
4668
+ entry.controller.abort();
4669
+ fieldValidationState.delete(key);
4670
+ }
4671
+ }
4304
4672
  function onFormChange(listener) {
4305
4673
  formChangeListeners.add(listener);
4306
4674
  return () => {
@@ -4351,6 +4719,8 @@ function createFormStore(options) {
4351
4719
  drainHooks.length = 0;
4352
4720
  modules.clear();
4353
4721
  cancelFieldValidation();
4722
+ cancelTransforms();
4723
+ fieldValidatingSince.clear();
4354
4724
  formChangeListeners.clear();
4355
4725
  submitSuccessListeners.clear();
4356
4726
  resetListeners.clear();
@@ -4486,6 +4856,7 @@ function createFormStore(options) {
4486
4856
  if (remaining === 0) {
4487
4857
  elements.delete(key);
4488
4858
  touchFieldRecord(key, path, { connected: false, focused: null, blurred: null });
4859
+ if (transformRuns.size !== 0) cancelTransformsUnder(path);
4489
4860
  }
4490
4861
  return remaining;
4491
4862
  }
@@ -4697,16 +5068,7 @@ function createFormStore(options) {
4697
5068
  }
4698
5069
  const now = (/* @__PURE__ */ new Date()).toISOString();
4699
5070
  for (const [pathKey, record] of fields) {
4700
- fields.set(pathKey, {
4701
- path: record.path,
4702
- updatedAt: now,
4703
- connected: record.connected,
4704
- focused: record.focused,
4705
- blurred: record.blurred,
4706
- touched: false,
4707
- interacted: false,
4708
- blurredAfterInteraction: false
4709
- });
5071
+ fields.set(pathKey, withClearedHistoryFlags(record, now));
4710
5072
  }
4711
5073
  submissionGeneration.value += 1;
4712
5074
  submitting.value = false;
@@ -4716,6 +5078,9 @@ function createFormStore(options) {
4716
5078
  submitError.value = null;
4717
5079
  departAttempts.value = 0;
4718
5080
  cancelFieldValidation();
5081
+ cancelTransforms();
5082
+ displayEngine.clear();
5083
+ fieldValidatingSince.clear();
4719
5084
  pathSnapshots.clear();
4720
5085
  scheduleEpoch = 0;
4721
5086
  lastCommittedEpoch = 0;
@@ -4731,6 +5096,13 @@ function createFormStore(options) {
4731
5096
  function resetField(path) {
4732
5097
  const { key: targetKey, segments: targetSegments } = canonicalizePath(path);
4733
5098
  variantMemory.clearUnderPath(targetSegments);
5099
+ cancelFieldValidationUnder(targetSegments);
5100
+ cancelTransformsUnder(targetSegments);
5101
+ for (const [snapKey] of [...pathSnapshots]) {
5102
+ const segs = segmentsForPathKey(snapKey);
5103
+ if (segs === null) continue;
5104
+ if (isPathPrefix(targetSegments, segs)) pathSnapshots.delete(snapKey);
5105
+ }
4734
5106
  const leafEntry = originals.get(targetKey);
4735
5107
  if (leafEntry !== void 0) {
4736
5108
  const wrote = setValueAtPath(targetSegments, leafEntry.value);
@@ -4780,16 +5152,7 @@ function createFormStore(options) {
4780
5152
  function clearFieldRecordFlags(pathKey) {
4781
5153
  const record = fields.get(pathKey);
4782
5154
  if (record === void 0) return;
4783
- fields.set(pathKey, {
4784
- path: record.path,
4785
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
4786
- connected: record.connected,
4787
- focused: record.focused,
4788
- blurred: record.blurred,
4789
- touched: false,
4790
- interacted: false,
4791
- blurredAfterInteraction: false
4792
- });
5155
+ fields.set(pathKey, withClearedHistoryFlags(record, (/* @__PURE__ */ new Date()).toISOString()));
4793
5156
  }
4794
5157
  function isPristineAtPath(path) {
4795
5158
  const { key, segments } = canonicalizePath(path);
@@ -4869,6 +5232,12 @@ function createFormStore(options) {
4869
5232
  pathHasAsyncValidation,
4870
5233
  pathHasAsyncValidationByKey,
4871
5234
  fieldValidationCounts,
5235
+ fieldValidatingSince,
5236
+ fieldTransformCounts,
5237
+ fieldTransformingSince,
5238
+ transformErrors,
5239
+ activeTransforms,
5240
+ displayEngine,
4872
5241
  applyFormReplacement,
4873
5242
  setValueAtPath,
4874
5243
  getValueAtPath,
@@ -4897,6 +5266,13 @@ function createFormStore(options) {
4897
5266
  getOriginalAtPath,
4898
5267
  getFirstErrorElement,
4899
5268
  cancelFieldValidation,
5269
+ beginTransform,
5270
+ isCurrentTransform,
5271
+ endTransform,
5272
+ setTransformError,
5273
+ cancelTransforms,
5274
+ cancelTransformsUnder,
5275
+ settleTransforms,
4900
5276
  scheduleFieldValidation,
4901
5277
  onFormChange,
4902
5278
  onSubmitSuccess,
@@ -4953,7 +5329,7 @@ function errorsEqual(a, b) {
4953
5329
  }
4954
5330
  return true;
4955
5331
  }
4956
- function diffBlankPaths$1(prev, curr) {
5332
+ function diffBlankPaths(prev, curr) {
4957
5333
  const added = [];
4958
5334
  const removed = [];
4959
5335
  for (const k of curr) if (!prev.has(k)) added.push(k);
@@ -5041,7 +5417,7 @@ function createHistoryModule(state, config) {
5041
5417
  diffAndApply(prevSnap.form, newSnap.form, [], (p) => formPatches.push(p));
5042
5418
  const prevBlankSet = new Set(prevSnap.blankPaths);
5043
5419
  const currBlankSet = new Set(newSnap.blankPaths);
5044
- const blankDiff = diffBlankPaths$1(prevBlankSet, currBlankSet);
5420
+ const blankDiff = diffBlankPaths(prevBlankSet, currBlankSet);
5045
5421
  const delta = {
5046
5422
  formPatches,
5047
5423
  blankPathsAdded: blankDiff.added,
@@ -5108,643 +5484,6 @@ function createHistoryModule(state, config) {
5108
5484
  };
5109
5485
  }
5110
5486
 
5111
- function wirePersistence(state, config) {
5112
- let fingerprint;
5113
- try {
5114
- fingerprint = hashStableString(state.schema.fingerprint());
5115
- } catch (err) {
5116
- if (__DEV__) {
5117
- console.warn(
5118
- `[attaform] Could not fingerprint the schema for form '${state.formKey}': ${err instanceof Error ? err.message : String(err)}. Persistence falls back to a fingerprint-free key, so a schema change won't auto-invalidate a saved draft.`
5119
- );
5120
- }
5121
- fingerprint = "unfingerprinted";
5122
- }
5123
- const base = resolveStorageKeyBase(config, state.formKey);
5124
- const key = `${base}:${fingerprint}`;
5125
- const debounceMs = normalizeNumericOption({
5126
- value: config.debounceMs ?? DEFAULT_PERSISTENCE_DEBOUNCE_MS,
5127
- source: "useForm.persist.debounceMs",
5128
- allowInfinity: false,
5129
- min: 0,
5130
- defaultValue: DEFAULT_PERSISTENCE_DEBOUNCE_MS
5131
- });
5132
- const include = config.include ?? "form";
5133
- const clearOnSubmitSuccess = config.clearOnSubmitSuccess ?? true;
5134
- const adapterPromise = getStorageAdapter(config.storage);
5135
- let disposed = false;
5136
- const isDisposed = () => disposed;
5137
- let inFlightFinalFlush = null;
5138
- let pendingOptedInPaths = null;
5139
- const writer = createDebouncedWriter(async () => {
5140
- const optedInPaths = pendingOptedInPaths ?? new Set(state.persistOptIns.optedInPaths());
5141
- pendingOptedInPaths = null;
5142
- const adapter = await adapterPromise;
5143
- if (isDisposed()) return;
5144
- if (optedInPaths.size === 0) {
5145
- await adapter.removeItem(key);
5146
- return;
5147
- }
5148
- const rawForm = toRaw(state.form.value);
5149
- const filteredForm = stripUnacknowledgedSensitiveLeaves(
5150
- pluckPaths(rawForm, optedInPaths),
5151
- optedInPaths,
5152
- state.isSensitivePath
5153
- );
5154
- const filteredSchemaErrors = filterErrorsByPaths(state.schemaErrors, optedInPaths);
5155
- const filteredUserErrors = filterErrorsByPaths(state.userErrors, optedInPaths);
5156
- const filteredTransientEmpty = /* @__PURE__ */ new Set();
5157
- for (const tk of state.blankPaths) {
5158
- if (optedInPaths.has(tk)) filteredTransientEmpty.add(tk);
5159
- }
5160
- const payload = buildPersistedPayload(
5161
- filteredForm,
5162
- include,
5163
- filteredSchemaErrors,
5164
- filteredUserErrors,
5165
- filteredTransientEmpty
5166
- );
5167
- await adapter.setItem(key, payload);
5168
- }, debounceMs);
5169
- const unsubscribeChange = state.onFormChange((_next, meta) => {
5170
- if (isDisposed() || inFlightFinalFlush !== null) return;
5171
- if (meta?.crossTab === true) return;
5172
- if (meta?.persist !== true) return;
5173
- pendingOptedInPaths = new Set(state.persistOptIns.optedInPaths());
5174
- writer.schedule();
5175
- });
5176
- const unsubscribeSuccess = clearOnSubmitSuccess ? state.onSubmitSuccess(() => {
5177
- if (isDisposed()) return;
5178
- void (async () => {
5179
- await writer.flush();
5180
- if (isDisposed()) return;
5181
- const adapter = await adapterPromise;
5182
- if (isDisposed()) return;
5183
- await adapter.removeItem(key);
5184
- })();
5185
- }) : () => void 0;
5186
- const handlePageHide = () => {
5187
- if (isDisposed()) return;
5188
- void writer.flush();
5189
- };
5190
- if (typeof window !== "undefined") {
5191
- window.addEventListener("pagehide", handlePageHide);
5192
- }
5193
- void (async () => {
5194
- const adapter = await adapterPromise;
5195
- if (isDisposed()) return;
5196
- void cleanupOrphanKeys(adapter, base, key);
5197
- try {
5198
- const raw = await adapter.getItem(key);
5199
- const payload = readPersistedPayload(raw);
5200
- if (payload === null) {
5201
- if (raw !== null && raw !== void 0) {
5202
- await adapter.removeItem(key);
5203
- }
5204
- return;
5205
- }
5206
- if (isDisposed()) return;
5207
- const merged = mergeSparseHydration(
5208
- toRaw(state.form.value),
5209
- payload.data.form,
5210
- state.schema
5211
- );
5212
- state.applyFormReplacement(merged, { hydration: true });
5213
- const persistedLeafPaths = collectPersistedLeafPaths(payload.data.form);
5214
- for (const k of persistedLeafPaths) {
5215
- state.blankPaths.delete(k);
5216
- state.originalBlankPaths.delete(k);
5217
- }
5218
- for (const k of payload.data.blankPaths ?? []) {
5219
- state.blankPaths.add(k);
5220
- state.originalBlankPaths.add(k);
5221
- }
5222
- if (include === "form+errors") {
5223
- if (payload.data.schemaErrors !== void 0) {
5224
- const flat = payload.data.schemaErrors.flatMap(([, errs]) => errs);
5225
- state.setAllSchemaErrors(flat);
5226
- }
5227
- if (payload.data.userErrors !== void 0) {
5228
- const flat = payload.data.userErrors.flatMap(([, errs]) => errs);
5229
- state.setAllUserErrors(flat);
5230
- }
5231
- }
5232
- state.scheduleFieldValidation(
5233
- [],
5234
- true
5235
- /* immediate */
5236
- );
5237
- } catch {
5238
- }
5239
- })();
5240
- async function writePathImmediately(path) {
5241
- if (isDisposed()) return;
5242
- await writer.flush();
5243
- if (isDisposed()) return;
5244
- const adapter = await adapterPromise;
5245
- if (isDisposed()) return;
5246
- const raw = await adapter.getItem(key);
5247
- const existing = readPersistedPayload(raw);
5248
- const value = getAtPath(toRaw(state.form.value), path);
5249
- const nextForm = setAtPath(existing?.data.form ?? {}, path, value);
5250
- const { key: pathKey } = canonicalizePath(path);
5251
- const transientSet = new Set(
5252
- (existing?.data.blankPaths ?? []).filter(
5253
- (k) => k !== pathKey && !isDescendantPathKey(k, pathKey)
5254
- )
5255
- );
5256
- for (const liveKey of state.blankPaths) {
5257
- if (liveKey === pathKey || isDescendantPathKey(liveKey, pathKey)) {
5258
- transientSet.add(liveKey);
5259
- }
5260
- }
5261
- if (include === "form") {
5262
- await adapter.setItem(
5263
- key,
5264
- buildPersistedPayload(nextForm, "form", /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map(), transientSet)
5265
- );
5266
- return;
5267
- }
5268
- const schemaMap = new Map(existing?.data.schemaErrors ?? []);
5269
- const userMap = new Map(existing?.data.userErrors ?? []);
5270
- const currentSchema = state.schemaErrors.get(pathKey);
5271
- const currentUser = state.userErrors.get(pathKey);
5272
- if (currentSchema !== void 0 && currentSchema.length > 0) {
5273
- schemaMap.set(pathKey, [...currentSchema]);
5274
- } else {
5275
- schemaMap.delete(pathKey);
5276
- }
5277
- if (currentUser !== void 0 && currentUser.length > 0) {
5278
- userMap.set(pathKey, [...currentUser]);
5279
- } else {
5280
- userMap.delete(pathKey);
5281
- }
5282
- await adapter.setItem(
5283
- key,
5284
- buildPersistedPayload(nextForm, "form+errors", schemaMap, userMap, transientSet)
5285
- );
5286
- }
5287
- async function clearPersistedDraft(path) {
5288
- if (isDisposed()) return;
5289
- await writer.flush();
5290
- if (isDisposed()) return;
5291
- const adapter = await adapterPromise;
5292
- if (isDisposed()) return;
5293
- if (path === void 0) {
5294
- await adapter.removeItem(key);
5295
- return;
5296
- }
5297
- const raw = await adapter.getItem(key);
5298
- const existing = readPersistedPayload(raw);
5299
- if (existing === null) return;
5300
- const nextForm = deleteAtPath(existing.data.form, path);
5301
- if (isEmptyContainer(nextForm)) {
5302
- await adapter.removeItem(key);
5303
- return;
5304
- }
5305
- const { key: pathKey } = canonicalizePath(path);
5306
- const transientSet = new Set(
5307
- (existing.data.blankPaths ?? []).filter(
5308
- (k) => k !== pathKey && !isDescendantPathKey(k, pathKey)
5309
- )
5310
- );
5311
- if (include === "form") {
5312
- await adapter.setItem(
5313
- key,
5314
- buildPersistedPayload(nextForm, "form", /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map(), transientSet)
5315
- );
5316
- return;
5317
- }
5318
- const schemaErrors = (existing.data.schemaErrors ?? []).filter(([k]) => k !== pathKey);
5319
- const userErrors = (existing.data.userErrors ?? []).filter(([k]) => k !== pathKey);
5320
- const schemaMap = new Map(schemaErrors.map(([k, v]) => [k, [...v]]));
5321
- const userMap = new Map(userErrors.map(([k, v]) => [k, [...v]]));
5322
- await adapter.setItem(
5323
- key,
5324
- buildPersistedPayload(nextForm, "form+errors", schemaMap, userMap, transientSet)
5325
- );
5326
- }
5327
- function awaitPendingWrites() {
5328
- if (inFlightFinalFlush !== null) return inFlightFinalFlush;
5329
- if (isDisposed()) return Promise.resolve();
5330
- return writer.flush().catch(() => void 0);
5331
- }
5332
- function dispose() {
5333
- if (isDisposed() || inFlightFinalFlush !== null) return;
5334
- unsubscribeChange();
5335
- unsubscribeSuccess();
5336
- if (typeof window !== "undefined") {
5337
- window.removeEventListener("pagehide", handlePageHide);
5338
- }
5339
- inFlightFinalFlush = writer.flush().catch(() => void 0).finally(() => {
5340
- disposed = true;
5341
- inFlightFinalFlush = null;
5342
- });
5343
- }
5344
- return {
5345
- wiredConfig: config,
5346
- writePathImmediately,
5347
- clearPersistedDraft,
5348
- awaitPendingWrites,
5349
- dispose
5350
- };
5351
- }
5352
- function isEmptyContainer(value) {
5353
- if (value === void 0 || value === null) return true;
5354
- if (Array.isArray(value)) return value.length === 0;
5355
- if (isPlainRecord(value)) return Object.keys(value).length === 0;
5356
- return false;
5357
- }
5358
- function collectPersistedLeafPaths(form) {
5359
- const out = [];
5360
- walk(form, []);
5361
- return out;
5362
- function walk(node, prefix) {
5363
- if (Array.isArray(node)) {
5364
- for (let i = 0; i < node.length; i++) {
5365
- walk(node[i], [...prefix, i]);
5366
- }
5367
- return;
5368
- }
5369
- if (isPlainRecord(node)) {
5370
- for (const key of Object.keys(node)) {
5371
- walk(node[key], [...prefix, key]);
5372
- }
5373
- return;
5374
- }
5375
- if (prefix.length === 0) return;
5376
- out.push(canonicalizePath(prefix).key);
5377
- }
5378
- }
5379
- function isDescendantPathKey(candidate, ancestor) {
5380
- if (candidate.length <= ancestor.length) return false;
5381
- if (!ancestor.endsWith("]")) return false;
5382
- const childPrefix = `${ancestor.slice(0, -1)},`;
5383
- return candidate.startsWith(childPrefix);
5384
- }
5385
-
5386
- const PROTOCOL_VERSION = 1;
5387
- const JOIN_COLLECTION_WINDOW_MS = 50;
5388
- const SNAPSHOT_TIMEOUT_MS = 200;
5389
- const MAX_LEADER_ATTEMPTS = 3;
5390
- const SNAPSHOT_RESPONSE_MIN_INTERVAL_MS = 500;
5391
- function isFileLikeValue(value) {
5392
- if (typeof File !== "undefined" && value instanceof File) return true;
5393
- if (typeof Blob !== "undefined" && value instanceof Blob) return true;
5394
- return false;
5395
- }
5396
- function isInboundShapeAcceptable(schema, path, value) {
5397
- if (isFileLikeValue(value)) return false;
5398
- let kind;
5399
- if (Array.isArray(value)) {
5400
- kind = "array";
5401
- } else if (value !== null && typeof value === "object" && isPlainRecord(value)) {
5402
- kind = "object";
5403
- } else {
5404
- kind = slimKindOf(value);
5405
- }
5406
- const accepted = schema.getSlimPrimitiveTypesAtPath(path);
5407
- if (!accepted.has(kind)) return false;
5408
- if (Array.isArray(value)) {
5409
- for (let i = 0; i < value.length; i++) {
5410
- if (!isInboundShapeAcceptable(schema, [...path, i], value[i])) return false;
5411
- }
5412
- return true;
5413
- }
5414
- if (isPlainRecord(value)) {
5415
- for (const key of Object.keys(value)) {
5416
- if (!isInboundShapeAcceptable(schema, [...path, key], value[key])) return false;
5417
- }
5418
- return true;
5419
- }
5420
- return true;
5421
- }
5422
- function diffBlankPaths(prev, curr) {
5423
- const added = [];
5424
- const removed = [];
5425
- const prevSet = new Set(prev);
5426
- for (const k of curr) if (!prevSet.has(k)) added.push(k);
5427
- for (const k of prev) if (!curr.has(k)) removed.push(k);
5428
- return { added, removed };
5429
- }
5430
- function snapshotForm(form) {
5431
- return structuralSnapshot(form);
5432
- }
5433
- function stripSensitivePathsDeep(value, pathSoFar, isSensitivePath) {
5434
- if (isFileLikeValue(value)) return void 0;
5435
- if (value === null || typeof value !== "object") return value;
5436
- if (Array.isArray(value)) {
5437
- return value.map((item, i) => stripSensitivePathsDeep(item, [...pathSoFar, i], isSensitivePath));
5438
- }
5439
- const proto = Object.getPrototypeOf(value);
5440
- if (proto !== Object.prototype && proto !== null) return value;
5441
- const out = {};
5442
- const src = value;
5443
- for (const key of Object.keys(src)) {
5444
- const childPath = [...pathSoFar, key];
5445
- if (isSensitivePath(childPath)) {
5446
- safeAssign(out, key, void 0);
5447
- continue;
5448
- }
5449
- safeAssign(out, key, stripSensitivePathsDeep(src[key], childPath, isSensitivePath));
5450
- }
5451
- return out;
5452
- }
5453
- function isStringArray(value) {
5454
- return Array.isArray(value) && value.every((item) => typeof item === "string");
5455
- }
5456
- function isPatchArray(value) {
5457
- return Array.isArray(value) && value.every(
5458
- (p) => p !== null && typeof p === "object" && Array.isArray(p.path)
5459
- );
5460
- }
5461
- function isValidSyncMessage(data) {
5462
- if (data === null || typeof data !== "object") return false;
5463
- const m = data;
5464
- if (m["v"] !== PROTOCOL_VERSION) return false;
5465
- if (typeof m["senderId"] !== "string") return false;
5466
- if (typeof m["kind"] !== "string") return false;
5467
- switch (m["kind"]) {
5468
- case "hello":
5469
- case "announce":
5470
- return true;
5471
- case "requestSnapshot":
5472
- return typeof m["targetId"] === "string";
5473
- case "snapshot":
5474
- return isStringArray(m["blankPaths"]) && "form" in m;
5475
- case "patches":
5476
- return isPatchArray(m["formPatches"]) && isStringArray(m["blankPathsAdded"]) && isStringArray(m["blankPathsRemoved"]);
5477
- default:
5478
- return false;
5479
- }
5480
- }
5481
- function generateSenderId() {
5482
- try {
5483
- return globalThis.crypto.randomUUID();
5484
- } catch {
5485
- return `atta-${Math.random().toString(36).slice(2)}-${Date.now().toString(36)}`;
5486
- }
5487
- }
5488
- function createMultiTabSyncModule(state, channelName, options) {
5489
- if (typeof BroadcastChannel === "undefined") {
5490
- return {
5491
- dispose: () => void 0,
5492
- lifecycle: () => "established",
5493
- senderId: "",
5494
- channelName
5495
- };
5496
- }
5497
- let channel;
5498
- try {
5499
- channel = new BroadcastChannel(channelName);
5500
- } catch {
5501
- return {
5502
- dispose: () => void 0,
5503
- lifecycle: () => "established",
5504
- senderId: "",
5505
- channelName
5506
- };
5507
- }
5508
- const senderId = generateSenderId();
5509
- let lifecycle = "joining";
5510
- let disposed = false;
5511
- const peerIds = /* @__PURE__ */ new Set();
5512
- const lastSnapshotResponseAt = /* @__PURE__ */ new Map();
5513
- let joinCollectionTimer = null;
5514
- let snapshotTimeoutTimer = null;
5515
- let leaderAttempts = 0;
5516
- let prior = {
5517
- form: snapshotForm(state.form.value),
5518
- blankPathsSnapshot: [...state.blankPaths]
5519
- };
5520
- function safePost(msg) {
5521
- if (disposed) return;
5522
- try {
5523
- channel.postMessage(msg);
5524
- } catch {
5525
- }
5526
- }
5527
- function refreshPrior() {
5528
- prior = {
5529
- form: snapshotForm(state.form.value),
5530
- blankPathsSnapshot: [...state.blankPaths]
5531
- };
5532
- }
5533
- function isPathLocallySuppressed(path) {
5534
- if (options.isSensitivePath(path)) return true;
5535
- const { key } = canonicalizePath([...path]);
5536
- if (options.noSyncPaths.has(key)) return true;
5537
- return false;
5538
- }
5539
- function postPatches() {
5540
- if (lifecycle !== "established") return;
5541
- const next = snapshotForm(state.form.value);
5542
- const rawPatches = [];
5543
- diffAndApply(prior.form, next, [], (p) => rawPatches.push(p));
5544
- const safePatches = [];
5545
- for (const p of rawPatches) {
5546
- if (isPathLocallySuppressed(p.path)) continue;
5547
- if ("value" in p && isFileLikeValue(p.value)) continue;
5548
- safePatches.push(p);
5549
- }
5550
- const { added, removed } = diffBlankPaths(prior.blankPathsSnapshot, state.blankPaths);
5551
- if (safePatches.length === 0 && added.length === 0 && removed.length === 0) {
5552
- prior = { form: next, blankPathsSnapshot: [...state.blankPaths] };
5553
- return;
5554
- }
5555
- safePost({
5556
- v: PROTOCOL_VERSION,
5557
- kind: "patches",
5558
- senderId,
5559
- formPatches: safePatches,
5560
- blankPathsAdded: added,
5561
- blankPathsRemoved: removed
5562
- });
5563
- prior = { form: next, blankPathsSnapshot: [...state.blankPaths] };
5564
- }
5565
- const unsubscribeChange = state.onFormChange((_next, meta) => {
5566
- if (disposed) return;
5567
- if (lifecycle !== "established") return;
5568
- if (meta?.crossTab === true) return;
5569
- if (meta?.hydration === true) {
5570
- refreshPrior();
5571
- return;
5572
- }
5573
- postPatches();
5574
- });
5575
- function applyIncomingForm(form, blankPaths) {
5576
- state.blankPaths.clear();
5577
- for (const k of blankPaths) state.blankPaths.add(k);
5578
- state.applyFormReplacement(form, { crossTab: true, persist: false });
5579
- refreshPrior();
5580
- }
5581
- function handlePatches(msg) {
5582
- if (lifecycle !== "established") return;
5583
- const safePatches = [];
5584
- for (const p of msg.formPatches) {
5585
- if (!Array.isArray(p.path)) continue;
5586
- if (isPathLocallySuppressed(p.path)) continue;
5587
- if ("value" in p && !isInboundShapeAcceptable(state.schema, p.path, p.value)) {
5588
- continue;
5589
- }
5590
- safePatches.push(p);
5591
- }
5592
- const safeBlankAdded = [];
5593
- for (const k of msg.blankPathsAdded) {
5594
- const segs = canonicalizePath(k).segments;
5595
- if (isPathLocallySuppressed(segs)) continue;
5596
- safeBlankAdded.push(k);
5597
- }
5598
- const safeBlankRemoved = [];
5599
- for (const k of msg.blankPathsRemoved) {
5600
- const segs = canonicalizePath(k).segments;
5601
- if (isPathLocallySuppressed(segs)) continue;
5602
- safeBlankRemoved.push(k);
5603
- }
5604
- if (safePatches.length === 0 && safeBlankAdded.length === 0 && safeBlankRemoved.length === 0) {
5605
- return;
5606
- }
5607
- const candidate = applyPatchesForward(state.form.value, safePatches);
5608
- try {
5609
- options.validateForm(state.form.value);
5610
- try {
5611
- options.validateForm(candidate);
5612
- } catch {
5613
- return;
5614
- }
5615
- } catch {
5616
- }
5617
- const nextBlankPaths = new Set(state.blankPaths);
5618
- for (const k of safeBlankRemoved) nextBlankPaths.delete(k);
5619
- for (const k of safeBlankAdded) nextBlankPaths.add(k);
5620
- applyIncomingForm(candidate, [...nextBlankPaths]);
5621
- }
5622
- function handleSnapshot(msg) {
5623
- if (lifecycle !== "joining") return;
5624
- if (!isInboundShapeAcceptable(state.schema, [], msg.form)) return;
5625
- if (snapshotTimeoutTimer !== null) {
5626
- clearTimeout(snapshotTimeoutTimer);
5627
- snapshotTimeoutTimer = null;
5628
- }
5629
- if (joinCollectionTimer !== null) {
5630
- clearTimeout(joinCollectionTimer);
5631
- joinCollectionTimer = null;
5632
- }
5633
- applyIncomingForm(msg.form, msg.blankPaths);
5634
- lifecycle = "established";
5635
- peerIds.clear();
5636
- }
5637
- function respondToHello() {
5638
- safePost({ v: PROTOCOL_VERSION, kind: "announce", senderId });
5639
- }
5640
- function respondToSnapshotRequest(requesterId) {
5641
- const now = Date.now();
5642
- const last = lastSnapshotResponseAt.get(requesterId);
5643
- if (last !== void 0 && now - last < SNAPSHOT_RESPONSE_MIN_INTERVAL_MS) return;
5644
- lastSnapshotResponseAt.set(requesterId, now);
5645
- const scrubbedForm = stripSensitivePathsDeep(state.form.value, [], options.isSensitivePath);
5646
- safePost({
5647
- v: PROTOCOL_VERSION,
5648
- kind: "snapshot",
5649
- senderId,
5650
- form: scrubbedForm,
5651
- blankPaths: [...state.blankPaths]
5652
- });
5653
- }
5654
- channel.onmessage = (event) => {
5655
- if (disposed) return;
5656
- try {
5657
- const data = event.data;
5658
- if (!isValidSyncMessage(data)) return;
5659
- const msg = data;
5660
- if (msg.senderId === senderId) return;
5661
- switch (msg.kind) {
5662
- case "hello":
5663
- if (lifecycle !== "established") return;
5664
- respondToHello();
5665
- break;
5666
- case "announce":
5667
- if (lifecycle === "joining") peerIds.add(msg.senderId);
5668
- break;
5669
- case "requestSnapshot":
5670
- if (lifecycle !== "established") return;
5671
- if (msg.targetId !== senderId) return;
5672
- respondToSnapshotRequest(msg.senderId);
5673
- break;
5674
- case "snapshot":
5675
- handleSnapshot(msg);
5676
- break;
5677
- case "patches":
5678
- handlePatches(msg);
5679
- break;
5680
- }
5681
- } catch {
5682
- }
5683
- };
5684
- function electLeaderAndRequest() {
5685
- if (disposed) return;
5686
- if (peerIds.size === 0) {
5687
- lifecycle = "established";
5688
- refreshPrior();
5689
- return;
5690
- }
5691
- const sorted = [...peerIds].sort();
5692
- const leaderId = sorted[0];
5693
- peerIds.delete(leaderId);
5694
- leaderAttempts++;
5695
- safePost({
5696
- v: PROTOCOL_VERSION,
5697
- kind: "requestSnapshot",
5698
- senderId,
5699
- targetId: leaderId
5700
- });
5701
- snapshotTimeoutTimer = setTimeout(() => {
5702
- snapshotTimeoutTimer = null;
5703
- if (disposed) return;
5704
- if (lifecycle === "established") return;
5705
- if (leaderAttempts >= MAX_LEADER_ATTEMPTS || peerIds.size === 0) {
5706
- lifecycle = "established";
5707
- refreshPrior();
5708
- return;
5709
- }
5710
- electLeaderAndRequest();
5711
- }, SNAPSHOT_TIMEOUT_MS);
5712
- }
5713
- function joinFlow() {
5714
- safePost({ v: PROTOCOL_VERSION, kind: "hello", senderId });
5715
- joinCollectionTimer = setTimeout(() => {
5716
- joinCollectionTimer = null;
5717
- if (disposed) return;
5718
- if (lifecycle === "established") return;
5719
- electLeaderAndRequest();
5720
- }, JOIN_COLLECTION_WINDOW_MS);
5721
- }
5722
- joinFlow();
5723
- return {
5724
- dispose: () => {
5725
- if (disposed) return;
5726
- disposed = true;
5727
- if (joinCollectionTimer !== null) {
5728
- clearTimeout(joinCollectionTimer);
5729
- joinCollectionTimer = null;
5730
- }
5731
- if (snapshotTimeoutTimer !== null) {
5732
- clearTimeout(snapshotTimeoutTimer);
5733
- snapshotTimeoutTimer = null;
5734
- }
5735
- unsubscribeChange();
5736
- try {
5737
- channel.close();
5738
- } catch {
5739
- }
5740
- },
5741
- lifecycle: () => lifecycle,
5742
- senderId,
5743
- channelName
5744
- };
5745
- }
5746
- const MULTI_TAB_SYNC_MODULE_KEY = "multiTabSync";
5747
-
5748
5487
  const warned = /* @__PURE__ */ new Set();
5749
5488
  function warnOnceInsecureContext(feature) {
5750
5489
  if (!__DEV__) return;
@@ -5804,8 +5543,10 @@ function useAbstractForm(configuration, options) {
5804
5543
  }
5805
5544
  const existing = registry.forms.get(key);
5806
5545
  if (__DEV__ && existing !== void 0) {
5807
- warnOnSchemaFingerprintMismatch(key, existing.schema, resolvedSchema);
5808
- warnOnPersistDivergence(key, existing, configuration.persist);
5546
+ void import('../chunks/dev-key-collision-warnings.mjs').then((m) => {
5547
+ void m.warnOnSchemaFingerprintMismatch(key, existing.schema, resolvedSchema);
5548
+ m.warnOnPersistDivergence(key, existing, configuration.persist);
5549
+ });
5809
5550
  }
5810
5551
  const hadPendingHydration = registry.pendingHydration.has(key);
5811
5552
  const state = existing ?? buildFreshState(key, resolvedSchema, merged, registry);
@@ -5846,10 +5587,33 @@ function useAbstractForm(configuration, options) {
5846
5587
  } else {
5847
5588
  const persistenceBase = resolveStorageKeyBase(resolvedPersist, state.formKey);
5848
5589
  void sweepNonConfiguredStandardStoresForOrphans(resolvedPersist.storage, persistenceBase);
5849
- const persistenceModule = wirePersistence(state, resolvedPersist);
5850
- state.modules.set(PERSISTENCE_MODULE_KEY, persistenceModule);
5851
- state.registerDrain(() => persistenceModule.awaitPendingWrites());
5852
- state.registerCleanup(() => persistenceModule.dispose());
5590
+ const adapterPromise = getStorageAdapter(resolvedPersist.storage);
5591
+ let persistDisposed = false;
5592
+ state.registerCleanup(() => {
5593
+ persistDisposed = true;
5594
+ });
5595
+ const ready = (async () => {
5596
+ try {
5597
+ const [{ wirePersistence }, fingerprintToken] = await Promise.all([
5598
+ import('../chunks/wire-persistence.mjs'),
5599
+ resolvePersistFingerprintToken(state)
5600
+ ]);
5601
+ if (persistDisposed) return void 0;
5602
+ const persistenceModule = wirePersistence(
5603
+ state,
5604
+ resolvedPersist,
5605
+ adapterPromise,
5606
+ fingerprintToken
5607
+ );
5608
+ state.registerDrain(() => persistenceModule.awaitPendingWrites());
5609
+ state.registerCleanup(() => persistenceModule.dispose());
5610
+ return persistenceModule;
5611
+ } catch {
5612
+ return void 0;
5613
+ }
5614
+ })();
5615
+ const persistenceHandle = { config: resolvedPersist, ready };
5616
+ state.modules.set(PERSISTENCE_MODULE_KEY, persistenceHandle);
5853
5617
  }
5854
5618
  } else {
5855
5619
  void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
@@ -5859,27 +5623,31 @@ function useAbstractForm(configuration, options) {
5859
5623
  const hasBroadcastChannel = typeof BroadcastChannel !== "undefined";
5860
5624
  const secureContext = isSecureContext();
5861
5625
  if (hasBroadcastChannel && secureContext) {
5862
- let channelName;
5863
- try {
5864
- channelName = `attaform:sync:${state.formKey}:${hashStableString(state.schema.fingerprint())}`;
5865
- } catch {
5866
- channelName = null;
5867
- }
5868
- if (channelName !== null) {
5869
- const syncModule = createMultiTabSyncModule(state, channelName, {
5870
- isSensitivePath: state.isSensitivePath,
5871
- noSyncPaths: state.noSyncPaths,
5872
- validateForm: (form) => {
5873
- const result = state.schema.validateAtPath(form, void 0, { sync: true });
5874
- if (result instanceof Promise) return;
5875
- if (!result.success) {
5876
- throw new Error("attaform multi-tab sync: post-apply schema validation failed");
5626
+ let formDisposed = false;
5627
+ state.registerCleanup(() => {
5628
+ formDisposed = true;
5629
+ });
5630
+ void (async () => {
5631
+ try {
5632
+ const [{ createMultiTabSyncModule, MULTI_TAB_SYNC_MODULE_KEY }, fingerprint] = await Promise.all([import('../chunks/multi-tab-sync.mjs'), state.schema.fingerprint()]);
5633
+ if (formDisposed) return;
5634
+ const channelName = `attaform:sync:${state.formKey}:${hashStableString(fingerprint)}`;
5635
+ const syncModule = createMultiTabSyncModule(state, channelName, {
5636
+ isSensitivePath: state.isSensitivePath,
5637
+ noSyncPaths: state.noSyncPaths,
5638
+ validateForm: (form) => {
5639
+ const result = state.schema.validateAtPath(form, void 0, { sync: true });
5640
+ if (result instanceof Promise) return;
5641
+ if (!result.success) {
5642
+ throw new Error("attaform multi-tab sync: post-apply schema validation failed");
5643
+ }
5877
5644
  }
5878
- }
5879
- });
5880
- state.modules.set(MULTI_TAB_SYNC_MODULE_KEY, syncModule);
5881
- state.registerCleanup(() => syncModule.dispose());
5882
- }
5645
+ });
5646
+ state.modules.set(MULTI_TAB_SYNC_MODULE_KEY, syncModule);
5647
+ state.registerCleanup(() => syncModule.dispose());
5648
+ } catch {
5649
+ }
5650
+ })();
5883
5651
  } else if (hasBroadcastChannel && !secureContext) {
5884
5652
  warnOnceInsecureContext("multiTab");
5885
5653
  }
@@ -6040,55 +5808,17 @@ function resolveFormKey(key) {
6040
5808
  }
6041
5809
  return `${ANONYMOUS_FORM_KEY_PREFIX}${anonCounter++}`;
6042
5810
  }
6043
- function warnOnSchemaFingerprintMismatch(key, existing, incoming) {
6044
- let existingFp;
6045
- let incomingFp;
5811
+ async function resolvePersistFingerprintToken(state) {
6046
5812
  try {
6047
- existingFp = existing.fingerprint();
6048
- incomingFp = incoming.fingerprint();
6049
- } catch (error) {
6050
- console.error(
6051
- `[attaform] fingerprint() threw for key "${key}"; skipping mismatch check.`,
6052
- error
6053
- );
6054
- return;
6055
- }
6056
- if (existingFp === incomingFp) return;
6057
- console.warn(
6058
- `[attaform] useForm() calls with key "${key}" use different schemas; first wins, second is ignored. Use identical schemas or unique keys.
6059
- existing: ${existingFp}
6060
- incoming: ${incomingFp}`
6061
- );
6062
- }
6063
- function warnOnPersistDivergence(key, existing, incomingPersist) {
6064
- if (incomingPersist === void 0) return;
6065
- const wired = existing.modules.get(PERSISTENCE_MODULE_KEY);
6066
- const incomingNormalized = normalizePersistConfig(incomingPersist);
6067
- if (wired === void 0) {
6068
- console.warn(
6069
- `[attaform] useForm({ key: "${key}" }) passed a persist config but the first useForm({ key }) call didn't wire persistence; the new config is silently dropped. Pass persist on the first call, or remove persist here to make the inheritance explicit.`
6070
- );
6071
- return;
5813
+ return hashStableString(await state.schema.fingerprint());
5814
+ } catch (err) {
5815
+ if (__DEV__) {
5816
+ console.warn(
5817
+ `[attaform] Could not fingerprint the schema for form '${state.formKey}': ${err instanceof Error ? err.message : String(err)}. Persistence falls back to a fingerprint-free key, so a schema change won't auto-invalidate a saved draft.`
5818
+ );
5819
+ }
5820
+ return "unfingerprinted";
6072
5821
  }
6073
- if (persistConfigsEquivalent(wired.wiredConfig, incomingNormalized)) return;
6074
- console.warn(
6075
- `[attaform] useForm({ key: "${key}" }) passed a persist config that differs from the first useForm({ key }) call's; first wins, this one is ignored.
6076
- wired: ${describePersist(wired.wiredConfig)}
6077
- incoming: ${describePersist(incomingNormalized)}`
6078
- );
6079
- }
6080
- function persistConfigsEquivalent(a, b) {
6081
- if (a.storage !== b.storage) return false;
6082
- if ((a.key ?? void 0) !== (b.key ?? void 0)) return false;
6083
- if ((a.debounceMs ?? void 0) !== (b.debounceMs ?? void 0)) return false;
6084
- return true;
6085
- }
6086
- function describePersist(config) {
6087
- const storage = typeof config.storage === "string" ? config.storage : "custom-adapter";
6088
- const parts = [`storage=${storage}`];
6089
- if (config.key !== void 0) parts.push(`key=${config.key}`);
6090
- if (config.debounceMs !== void 0) parts.push(`debounceMs=${config.debounceMs}`);
6091
- return `{ ${parts.join(", ")} }`;
6092
5822
  }
6093
5823
  const warnedAnonPersistKeys = /* @__PURE__ */ new Set();
6094
5824
  function enforceAnonPersistRule(formKey, ssr) {
@@ -6185,6 +5915,8 @@ function isLazyMarker(value) {
6185
5915
  }
6186
5916
 
6187
5917
  const NOOP_WIZARD_HISTORY = {
5918
+ push() {
5919
+ },
6188
5920
  replace() {
6189
5921
  },
6190
5922
  read() {
@@ -6199,6 +5931,9 @@ function createWizardHistory(param) {
6199
5931
  if (typeof window === "undefined") return NOOP_WIZARD_HISTORY;
6200
5932
  const subscribers = [];
6201
5933
  let disposed = false;
5934
+ function currentKey() {
5935
+ return new URL(window.location.href).searchParams.get(param) ?? void 0;
5936
+ }
6202
5937
  function buildUrl(key) {
6203
5938
  const url = new URL(window.location.href);
6204
5939
  url.searchParams.set(param, key);
@@ -6206,25 +5941,29 @@ function createWizardHistory(param) {
6206
5941
  }
6207
5942
  function handlePopstate() {
6208
5943
  if (disposed) return;
6209
- const url = new URL(window.location.href);
6210
- const value = url.searchParams.get(param) ?? void 0;
5944
+ const value = currentKey();
6211
5945
  for (const subscriber of subscribers) subscriber(value);
6212
5946
  }
6213
5947
  window.addEventListener("popstate", handlePopstate);
6214
- function safeReplaceState(key) {
5948
+ function safeWrite(key, mode) {
6215
5949
  try {
6216
- window.history.replaceState({}, "", buildUrl(key));
5950
+ if (mode === "push") window.history.pushState({}, "", buildUrl(key));
5951
+ else window.history.replaceState({}, "", buildUrl(key));
6217
5952
  } catch {
6218
5953
  }
6219
5954
  }
6220
5955
  return {
5956
+ push(key) {
5957
+ if (disposed) return;
5958
+ if (currentKey() === key) return;
5959
+ safeWrite(key, "push");
5960
+ },
6221
5961
  replace(key) {
6222
5962
  if (disposed) return;
6223
- safeReplaceState(key);
5963
+ safeWrite(key, "replace");
6224
5964
  },
6225
5965
  read() {
6226
- const url = new URL(window.location.href);
6227
- return url.searchParams.get(param) ?? void 0;
5966
+ return currentKey();
6228
5967
  },
6229
5968
  subscribe(callback) {
6230
5969
  if (disposed) return;
@@ -6256,12 +5995,13 @@ function buildNoopWizardSchema(formKey) {
6256
5995
  formKey
6257
5996
  };
6258
5997
  return {
6259
- fingerprint: () => NOOP_FINGERPRINT,
5998
+ fingerprint: () => Promise.resolve(NOOP_FINGERPRINT),
6260
5999
  getDefaultValues: () => defaultsResponse,
6261
6000
  getDefaultAtPath: () => void 0,
6262
6001
  getEmptyValueAtPath: () => void 0,
6263
6002
  isPreprocessOrCoerceLeaf: () => false,
6264
6003
  arrayShapeAtPath: () => void 0,
6004
+ isFixedObjectAtPath: (path) => path.length === 0,
6265
6005
  getSchemasAtPath: () => [],
6266
6006
  validateAtPath: () => success,
6267
6007
  getSlimPrimitiveTypesAtPath: () => new Set(EMPTY_SLIM_KINDS),
@@ -6715,7 +6455,10 @@ function useWizard(options) {
6715
6455
  };
6716
6456
  const persistCallback = options.persist === false ? void 0 : options.persist !== void 0 ? options.persist : (state) => {
6717
6457
  if (state.step === void 0) return;
6718
- historyHandle.replace(state.step);
6458
+ const current = historyHandle.read();
6459
+ const effectiveCurrent = current !== void 0 && isCompiledKey(current) ? current : firstKey();
6460
+ if (state.step === effectiveCurrent) historyHandle.replace(state.step);
6461
+ else historyHandle.push(state.step);
6719
6462
  };
6720
6463
  function isCompiledKey(key) {
6721
6464
  const list = compiledSteps.value;
@@ -6796,6 +6539,7 @@ function useWizard(options) {
6796
6539
  }
6797
6540
  const submitting = ref(false);
6798
6541
  const submissionAttempts = ref(0);
6542
+ const submitError = ref(null);
6799
6543
  const done = ref(false);
6800
6544
  function activateForm(form) {
6801
6545
  const source = asSubmissionSource(form);
@@ -6923,7 +6667,7 @@ function useWizard(options) {
6923
6667
  formKey: form.key
6924
6668
  };
6925
6669
  }
6926
- return full.process();
6670
+ return full.parse();
6927
6671
  }
6928
6672
  function collectErrors(results) {
6929
6673
  const out = [];
@@ -6954,6 +6698,7 @@ function useWizard(options) {
6954
6698
  return;
6955
6699
  }
6956
6700
  submitting.value = true;
6701
+ submitError.value = null;
6957
6702
  try {
6958
6703
  const currentKey = activeKey.value;
6959
6704
  const final = isFinalStep.value;
@@ -6962,18 +6707,24 @@ function useWizard(options) {
6962
6707
  if (final) {
6963
6708
  await Promise.all(
6964
6709
  list.map(async (step) => {
6710
+ registry.forms.get(step.key)?.clearUserErrors();
6965
6711
  const result = await processOne(step.form);
6966
6712
  results.set(step.key, result);
6967
6713
  })
6968
6714
  );
6969
6715
  } else {
6970
6716
  const active = activeForm.value;
6717
+ registry.forms.get(active.key)?.clearUserErrors();
6971
6718
  const result = await processOne(active);
6972
6719
  results.set(active.key, result);
6973
6720
  }
6974
6721
  for (const key of results.keys()) {
6975
6722
  const store = registry.forms.get(key);
6976
- if (store !== void 0) store.submissionAttempts.value += 1;
6723
+ if (store !== void 0) {
6724
+ store.submissionAttempts.value += 1;
6725
+ store.cancelFieldValidation();
6726
+ store.displayEngine.clear();
6727
+ }
6977
6728
  }
6978
6729
  submissionAttempts.value += 1;
6979
6730
  const errors = collectErrors(results);
@@ -6998,7 +6749,6 @@ function useWizard(options) {
6998
6749
  if (target !== void 0) moveTo(target.key);
6999
6750
  }
7000
6751
  } else {
7001
- if (onError !== void 0) await onError(errors);
7002
6752
  if (options.focusFirstError !== false) {
7003
6753
  const firstFailedKey = errors[0]?.formKey;
7004
6754
  if (firstFailedKey !== void 0 && isCompiledKey(firstFailedKey)) {
@@ -7013,7 +6763,16 @@ function useWizard(options) {
7013
6763
  }
7014
6764
  }
7015
6765
  }
6766
+ if (onError !== void 0) {
6767
+ try {
6768
+ await onError(errors);
6769
+ } catch (cause) {
6770
+ throw new SubmitErrorHandlerError("User-provided onError threw", { cause });
6771
+ }
6772
+ }
7016
6773
  }
6774
+ } catch (err) {
6775
+ submitError.value = toError(err);
7017
6776
  } finally {
7018
6777
  submitting.value = false;
7019
6778
  }
@@ -7022,6 +6781,7 @@ function useWizard(options) {
7022
6781
  function reset() {
7023
6782
  submissionAttempts.value = 0;
7024
6783
  done.value = false;
6784
+ submitError.value = null;
7025
6785
  lazyEpoch.value += 1;
7026
6786
  for (const step of compiledSteps.value) {
7027
6787
  const full = asSubmissionSource(step.form);
@@ -7097,6 +6857,9 @@ function useWizard(options) {
7097
6857
  get submissionAttempts() {
7098
6858
  return submissionAttempts.value;
7099
6859
  },
6860
+ get submitError() {
6861
+ return submitError.value;
6862
+ },
7100
6863
  get visited() {
7101
6864
  return visited.value;
7102
6865
  }
@@ -7206,5 +6969,5 @@ function warnIfAmbientWizardProviderHadDuplicates() {
7206
6969
  }
7207
6970
  }
7208
6971
 
7209
- export { AttaformErrorCode as A, defaultDisplayState as a, defineCoercion as b, injectWizard as c, defaultCoercionRules as d, isPlainRecord as e, isUnset as f, getAtPath as g, humanize as h, injectForm as i, safeOwnRead as j, setAtPath as k, lazy as l, slimKindOf as m, normalizeNumericOption as n, useAbstractForm as o, useWizard as p, safeAssign as s, unset as u };
7210
- //# sourceMappingURL=attaform.tiWEVznj.mjs.map
6972
+ export { AttaformErrorCode as A, useAbstractForm as B, useWizard as C, DEFAULT_PERSISTENCE_DEBOUNCE_MS as D, PERSISTENCE_MODULE_KEY as P, DEFAULT_TIMINGS as a, applyPatchesForward as b, cleanupOrphanKeys as c, defaultCoercionRules as d, defaultDisplayState as e, defineCoercion as f, deleteAtPath as g, diffAndApply as h, getAtPath as i, humanize as j, injectForm as k, injectWizard as l, isPlainRecord as m, isUnset as n, lazy as o, makeDefaultDisplayState as p, mergeSparseHydration as q, normalizeNumericOption as r, normalizePersistConfig as s, resolveStorageKeyBase as t, safeAssign as u, safeOwnRead as v, setAtPath as w, slimKindOf as x, structuralSnapshot as y, unset as z };
6973
+ //# sourceMappingURL=attaform.DR6RmxWZ.mjs.map