attaform 0.21.2 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/README.md +7 -10
  2. package/dist/chunks/dev-key-collision-warnings.cjs +0 -33
  3. package/dist/chunks/dev-key-collision-warnings.cjs.map +1 -1
  4. package/dist/chunks/dev-key-collision-warnings.mjs +1 -33
  5. package/dist/chunks/dev-key-collision-warnings.mjs.map +1 -1
  6. package/dist/chunks/devtools.cjs +3 -5
  7. package/dist/chunks/devtools.cjs.map +1 -1
  8. package/dist/chunks/devtools.mjs +3 -5
  9. package/dist/chunks/devtools.mjs.map +1 -1
  10. package/dist/chunks/fingerprint2.cjs +1 -1
  11. package/dist/chunks/fingerprint2.mjs +1 -1
  12. package/dist/index.cjs +3 -5
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +6 -41
  15. package/dist/index.d.mts +6 -41
  16. package/dist/index.d.ts +6 -41
  17. package/dist/index.mjs +5 -5
  18. package/dist/nuxt.d.cts +1 -1
  19. package/dist/nuxt.d.mts +1 -1
  20. package/dist/nuxt.d.ts +1 -1
  21. package/dist/runtime/components/AttaformDevtoolsPanel.vue +3 -11
  22. package/dist/runtime/plugins/attaform.cjs +2 -2
  23. package/dist/runtime/plugins/attaform.mjs +2 -2
  24. package/dist/shared/{attaform.BGf_J22U.d.ts → attaform.BGMRvckW.d.ts} +11 -70
  25. package/dist/shared/{attaform.ory-3WhV.d.mts → attaform.BJnNK75Y.d.cts} +41 -491
  26. package/dist/shared/{attaform.ory-3WhV.d.ts → attaform.BJnNK75Y.d.mts} +41 -491
  27. package/dist/shared/{attaform.ory-3WhV.d.cts → attaform.BJnNK75Y.d.ts} +41 -491
  28. package/dist/shared/{attaform.DP-u7_tk.mjs → attaform.BhI9Icek.mjs} +17 -291
  29. package/dist/shared/attaform.BhI9Icek.mjs.map +1 -0
  30. package/dist/shared/{attaform.BwLp9KM7.cjs → attaform.BibT5AS_.cjs} +2 -2
  31. package/dist/shared/{attaform.BwLp9KM7.cjs.map → attaform.BibT5AS_.cjs.map} +1 -1
  32. package/dist/shared/{attaform.DkA5J8NW.d.cts → attaform.CO0e7YVY.d.cts} +1 -46
  33. package/dist/shared/{attaform.DkA5J8NW.d.ts → attaform.CO0e7YVY.d.mts} +1 -46
  34. package/dist/shared/{attaform.DkA5J8NW.d.mts → attaform.CO0e7YVY.d.ts} +1 -46
  35. package/dist/shared/{attaform.BwrowMp2.cjs → attaform.CaYj3ZfY.cjs} +3 -3
  36. package/dist/shared/{attaform.BwrowMp2.cjs.map → attaform.CaYj3ZfY.cjs.map} +1 -1
  37. package/dist/shared/{attaform.BBDIKtKY.cjs → attaform.Cmb_LCie.cjs} +4 -4
  38. package/dist/shared/{attaform.BVeLgfEh.mjs.map → attaform.Cmb_LCie.cjs.map} +1 -1
  39. package/dist/shared/{attaform.CTheKoTc.mjs → attaform.CtJOd7ox.mjs} +446 -525
  40. package/dist/shared/attaform.CtJOd7ox.mjs.map +1 -0
  41. package/dist/shared/{attaform.CrD73S4m.mjs → attaform.CzVta5o2.mjs} +116 -47
  42. package/dist/shared/attaform.CzVta5o2.mjs.map +1 -0
  43. package/dist/shared/{attaform.CnEl--PF.d.mts → attaform.D52oJiYC.d.cts} +1 -1
  44. package/dist/shared/{attaform.BoY6RZUl.d.cts → attaform.DCkSNnPr.d.mts} +1 -1
  45. package/dist/shared/{attaform.B5LNzqQh.cjs → attaform.Db4E4IW6.cjs} +18 -297
  46. package/dist/shared/attaform.Db4E4IW6.cjs.map +1 -0
  47. package/dist/shared/{attaform.CcnF1AKJ.cjs → attaform.DbyTD8N2.cjs} +116 -47
  48. package/dist/shared/attaform.DbyTD8N2.cjs.map +1 -0
  49. package/dist/shared/{attaform.D6GYGshL.mjs → attaform.Dd1Kmmaj.mjs} +3 -3
  50. package/dist/shared/{attaform.D6GYGshL.mjs.map → attaform.Dd1Kmmaj.mjs.map} +1 -1
  51. package/dist/shared/{attaform.BCcrLApm.d.mts → attaform.DrY8srOp.d.mts} +11 -70
  52. package/dist/shared/{attaform.D2ZuIOCf.cjs → attaform.DsQkXE3o.cjs} +445 -534
  53. package/dist/shared/attaform.DsQkXE3o.cjs.map +1 -0
  54. package/dist/shared/{attaform.BkjJfMvJ.d.cts → attaform.DuPneYR0.d.cts} +11 -70
  55. package/dist/shared/{attaform.C41gjp-a.mjs → attaform.Dx9-QQE2.mjs} +2 -2
  56. package/dist/shared/{attaform.C41gjp-a.mjs.map → attaform.Dx9-QQE2.mjs.map} +1 -1
  57. package/dist/shared/{attaform.BYgioWLF.d.ts → attaform.WEwfXcHq.d.ts} +1 -1
  58. package/dist/shared/{attaform.BVeLgfEh.mjs → attaform.alpG7rT7.mjs} +4 -4
  59. package/dist/shared/{attaform.BBDIKtKY.cjs.map → attaform.alpG7rT7.mjs.map} +1 -1
  60. package/dist/zod-v3.cjs +2 -2
  61. package/dist/zod-v3.d.cts +3 -3
  62. package/dist/zod-v3.d.mts +3 -3
  63. package/dist/zod-v3.d.ts +3 -3
  64. package/dist/zod-v3.mjs +2 -2
  65. package/dist/zod-v4.cjs +2 -2
  66. package/dist/zod-v4.d.cts +5 -5
  67. package/dist/zod-v4.d.mts +5 -5
  68. package/dist/zod-v4.d.ts +5 -5
  69. package/dist/zod-v4.mjs +2 -2
  70. package/dist/zod.cjs +5 -5
  71. package/dist/zod.d.cts +5 -5
  72. package/dist/zod.d.mts +5 -5
  73. package/dist/zod.d.ts +5 -5
  74. package/dist/zod.mjs +5 -5
  75. package/package.json +2 -2
  76. package/dist/chunks/indexeddb.cjs +0 -119
  77. package/dist/chunks/indexeddb.cjs.map +0 -1
  78. package/dist/chunks/indexeddb.mjs +0 -117
  79. package/dist/chunks/indexeddb.mjs.map +0 -1
  80. package/dist/chunks/local-storage.cjs +0 -58
  81. package/dist/chunks/local-storage.cjs.map +0 -1
  82. package/dist/chunks/local-storage.mjs +0 -56
  83. package/dist/chunks/local-storage.mjs.map +0 -1
  84. package/dist/chunks/multi-tab-sync.cjs +0 -367
  85. package/dist/chunks/multi-tab-sync.cjs.map +0 -1
  86. package/dist/chunks/multi-tab-sync.mjs +0 -364
  87. package/dist/chunks/multi-tab-sync.mjs.map +0 -1
  88. package/dist/chunks/session-storage.cjs +0 -58
  89. package/dist/chunks/session-storage.cjs.map +0 -1
  90. package/dist/chunks/session-storage.mjs +0 -56
  91. package/dist/chunks/session-storage.mjs.map +0 -1
  92. package/dist/chunks/wire-persistence.cjs +0 -396
  93. package/dist/chunks/wire-persistence.cjs.map +0 -1
  94. package/dist/chunks/wire-persistence.mjs +0 -394
  95. package/dist/chunks/wire-persistence.mjs.map +0 -1
  96. package/dist/shared/attaform.B5LNzqQh.cjs.map +0 -1
  97. package/dist/shared/attaform.CTheKoTc.mjs.map +0 -1
  98. package/dist/shared/attaform.CcnF1AKJ.cjs.map +0 -1
  99. package/dist/shared/attaform.CrD73S4m.mjs.map +0 -1
  100. package/dist/shared/attaform.D2ZuIOCf.cjs.map +0 -1
  101. package/dist/shared/attaform.DP-u7_tk.mjs.map +0 -1
@@ -1,5 +1,5 @@
1
- import { computed, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, readonly, toRaw, reactive, watch, markRaw, shallowRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, onBeforeMount, onBeforeUpdate, onMounted, effectScope, nextTick } from 'vue';
2
- import { _ as __DEV__, a as canonicalizePath, s as segmentsForPathKey, l as isPathPrefix, F as FORM_ERRORS_PATH_KEY, S as SubmitErrorHandlerError, t as toError, A as AnonPersistError, q as INTERACTIVE_TAG_NAMES, r as getOrAssignElementId, e as ROOT_PATH_KEY, R as ROOT_PATH, w as allowSensitivePersist, x as FORM_ERRORS_PATH, y as coerceToPathKey, z as isSensitivePath, B as createPersistOptInRegistry, d as InvalidUseFormConfigError, C as ensureAttaformInstalled, u as useRegistry, E as kFormContext, G as kFormInstanceId, h as ReservedFormKeyError, H as createIsSensitivePath, J as REGISTER_OWNER_MARKER, V as V_REGISTER_MARKER, k as kAttaformWizardActiveStepResolver, K as kAttaformAncestorWizard } from './attaform.DP-u7_tk.mjs';
1
+ import { computed, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, readonly, toRaw, reactive, watch, markRaw, triggerRef, shallowRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, onBeforeMount, onBeforeUpdate, onMounted, effectScope, nextTick } from 'vue';
2
+ import { o as pathsEqual, j as isPathPrefix, _ as __DEV__, a as canonicalizePath, s as segmentsForPathKey, F as FORM_ERRORS_PATH_KEY, q as keyForSegments, S as SubmitErrorHandlerError, t as toError, r as INTERACTIVE_TAG_NAMES, R as ROOT_PATH, w as FORM_ERRORS_PATH, d as ROOT_PATH_KEY, x as coerceToPathKey, b as InvalidUseFormConfigError, y as ensureAttaformInstalled, u as useRegistry, z as kFormContext, B as kFormInstanceId, f as ReservedFormKeyError, C as REGISTER_OWNER_MARKER, V as V_REGISTER_MARKER, k as kAttaformWizardActiveStepResolver, D as kAttaformAncestorWizard } from './attaform.BhI9Icek.mjs';
3
3
 
4
4
  function safeAssign(target, key, value) {
5
5
  if (key === "__proto__") {
@@ -34,7 +34,7 @@ function descendStep(value, segment) {
34
34
  if (typeof value !== "object") return NOT_FOUND;
35
35
  if (Array.isArray(value)) {
36
36
  if (typeof segment !== "number") return NOT_FOUND;
37
- if (segment < 0 || segment >= value.length) return NOT_FOUND;
37
+ if (!(segment in value)) return NOT_FOUND;
38
38
  return value[segment];
39
39
  }
40
40
  const record = value;
@@ -69,7 +69,7 @@ function hasAtPath(root, path) {
69
69
  if (current === null || current === void 0) return false;
70
70
  if (typeof current !== "object") return false;
71
71
  if (Array.isArray(current)) {
72
- return typeof last === "number" && last >= 0 && last < current.length;
72
+ return typeof last === "number" && last in current;
73
73
  }
74
74
  const key = typeof last === "number" ? String(last) : last;
75
75
  if (isShadowedKey(key)) return safeOwnHas(current, key);
@@ -98,6 +98,31 @@ function setAtPathOffset(root, path, value, offset) {
98
98
  safeAssign(rec, head, setAtPathOffset(safeOwnRead(rec, head), path, value, nextOffset));
99
99
  return rec;
100
100
  }
101
+ const NO_IN_PLACE = { applied: false };
102
+ function tryInPlaceLeafWrite(root, path, value) {
103
+ if (path.length === 0) return NO_IN_PLACE;
104
+ let node = root;
105
+ for (let i = 0; i < path.length; i++) {
106
+ const seg = path[i];
107
+ if (Array.isArray(node)) {
108
+ if (typeof seg !== "number" || seg < 0 || seg >= node.length) return NO_IN_PLACE;
109
+ } else if (isPlainRecord(node)) {
110
+ if (typeof seg !== "string" || isShadowedKey(seg) || !(seg in node)) return NO_IN_PLACE;
111
+ } else {
112
+ return NO_IN_PLACE;
113
+ }
114
+ const container = node;
115
+ if (i < path.length - 1) {
116
+ node = container[seg];
117
+ continue;
118
+ }
119
+ const old = container[seg];
120
+ if (isPlainRecord(old) || Array.isArray(old)) return NO_IN_PLACE;
121
+ container[seg] = value;
122
+ return { applied: true, old };
123
+ }
124
+ return NO_IN_PLACE;
125
+ }
101
126
  function deleteAtPath(root, path) {
102
127
  return deleteAtPathOffset(root, path, 0);
103
128
  }
@@ -401,7 +426,7 @@ function diffObjectsLockstep(oldRec, newRec, prefix, visit) {
401
426
  diffAndApply(oldRec[k], newRec[k], appendSegment(prefix, k), visit);
402
427
  }
403
428
  }
404
- function applyChangedKeys(target, source) {
429
+ function applyChangedKeys(target, source, arrayOpPath, currentPath) {
405
430
  if (!isDescendable(target) || !isDescendable(source)) return false;
406
431
  const targetIsArray = Array.isArray(target);
407
432
  const sourceIsArray = Array.isArray(source);
@@ -419,11 +444,27 @@ function applyChangedKeys(target, source) {
419
444
  if (targetIsArray) {
420
445
  const t = target;
421
446
  const s = source;
422
- if (t.length > s.length) t.length = s.length;
423
- for (const idx of changedFirstSegments) {
424
- if (typeof idx === "symbol") continue;
425
- const i = typeof idx === "number" ? idx : Number(idx);
426
- t[i] = s[i];
447
+ if (arrayOpPath === null || pathsEqual(currentPath, arrayOpPath)) {
448
+ if (t.length > s.length) t.length = s.length;
449
+ for (const idx of changedFirstSegments) {
450
+ if (typeof idx === "symbol") continue;
451
+ const i = typeof idx === "number" ? idx : Number(idx);
452
+ if (i >= s.length) continue;
453
+ t[i] = s[i];
454
+ }
455
+ } else {
456
+ for (const idx of changedFirstSegments) {
457
+ if (typeof idx === "symbol") continue;
458
+ const i = typeof idx === "number" ? idx : Number(idx);
459
+ if (i >= s.length) continue;
460
+ const childPath = appendSegment(currentPath, i);
461
+ const curEl = t[i];
462
+ const nextEl = s[i];
463
+ if (isPathPrefix(childPath, arrayOpPath) && isDescendable(curEl) && isDescendable(nextEl) && applyChangedKeys(curEl, nextEl, arrayOpPath, childPath)) {
464
+ continue;
465
+ }
466
+ t[i] = nextEl;
467
+ }
427
468
  }
428
469
  } else {
429
470
  const t = target;
@@ -435,7 +476,14 @@ function applyChangedKeys(target, source) {
435
476
  for (const k of changedFirstSegments) {
436
477
  if (typeof k === "symbol") continue;
437
478
  const key = String(k);
438
- safeAssign(t, key, safeOwnRead(s, key));
479
+ const nextVal = safeOwnRead(s, key);
480
+ if (arrayOpPath !== null) {
481
+ const curVal = safeOwnRead(t, key);
482
+ if (isDescendable(curVal) && isDescendable(nextVal) && applyChangedKeys(curVal, nextVal, arrayOpPath, appendSegment(currentPath, key))) {
483
+ continue;
484
+ }
485
+ }
486
+ safeAssign(t, key, nextVal);
439
487
  }
440
488
  }
441
489
  return true;
@@ -547,10 +595,52 @@ function resolveGetDisplayState(config) {
547
595
  return config ?? defaultDisplayState;
548
596
  }
549
597
 
598
+ const AttaformErrorCode = {
599
+ /** A required field is in the blank set — user hasn't supplied a value. */
600
+ NoValueSupplied: "atta:no-value-supplied",
601
+ /** The schema adapter's `validateAtPath` threw synchronously. */
602
+ AdapterThrew: "atta:adapter-threw",
603
+ /**
604
+ * User code inside a `z.preprocess`, `.refine`, or `.transform`
605
+ * threw (sync or async). The adapter caught the throw and surfaced
606
+ * it as a `ValidationError` at the field path so the form's normal
607
+ * error pipeline handles it instead of leaking as an unhandled
608
+ * rejection or routing through `submitError`.
609
+ */
610
+ ValidatorThrew: "atta:validator-threw",
611
+ /**
612
+ * A function-form `defaultValues` factory threw or its promise
613
+ * rejected. The runtime captures the raw error on `form.hydrateError`
614
+ * and ALSO surfaces a form-level `ValidationError` (path `[]`) so
615
+ * the standard error pipeline carries the signal. Critical for the
616
+ * SSR round-trip: `hydrateError` itself does not ride the wire
617
+ * payload, but `schemaErrors` does, so the client sees the failure
618
+ * after rehydration without an extra channel.
619
+ */
620
+ HydrationFailed: "atta:hydration-failed",
621
+ /** The supplied path didn't resolve to any node in the schema. */
622
+ PathNotFound: "atta:path-not-found",
623
+ /**
624
+ * A walked form's `activate()` (async `defaultValues` factory) threw
625
+ * during `wizard.handleSubmit`'s path walk. Surfaced as a synthetic
626
+ * `ValidationError` at the form-level path (`[]`) so the wizard's
627
+ * aggregate error pipeline can carry the failure alongside ordinary
628
+ * validation errors. The raw factory error remains on
629
+ * `form.hydrateError` for retry UX.
630
+ */
631
+ ActivationFailed: "atta:activation-failed"
632
+ };
633
+ function makeBlankRequiredError(segments, formKey) {
634
+ return {
635
+ message: "No value supplied",
636
+ path: [...segments],
637
+ formKey,
638
+ code: AttaformErrorCode.NoValueSupplied
639
+ };
640
+ }
641
+
550
642
  const DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS = 0;
551
- const DEFAULT_PERSISTENCE_DEBOUNCE_MS = 300;
552
643
  const DEFAULT_HISTORY_MAX_SNAPSHOTS = 128;
553
- const PERSISTENCE_KEY_PREFIX = "attaform:";
554
644
  const RESERVED_KEY_PREFIX = "__atta:";
555
645
  const ANONYMOUS_FORM_KEY_PREFIX = `${RESERVED_KEY_PREFIX}anon:`;
556
646
  const ANONYMOUS_WIZARD_KEY_PREFIX = `${RESERVED_KEY_PREFIX}anon-wizard:`;
@@ -660,7 +750,7 @@ function buildLeafFieldStateBase(state, segments, key, formInstanceId) {
660
750
  const original = state.originals.get(key)?.value;
661
751
  const pristine = state.isPristineAtPath(segments);
662
752
  const schemaForKey = state.schemaErrors.get(key);
663
- const blankForKey = state.derivedBlankErrors.value.get(key);
753
+ const blankForKey = state.blankPaths.has(key) && state.schema.isRequiredAtPath(segments) ? [makeBlankRequiredError(segments, state.formKey)] : void 0;
664
754
  const userForKey = state.userErrors.get(key);
665
755
  const errors = [];
666
756
  if (schemaForKey !== void 0) errors.push(...schemaForKey);
@@ -724,9 +814,32 @@ function buildLeafFieldState(state, segments, key, formInstanceId, getFormMetaBa
724
814
  getDisplayState
725
815
  );
726
816
  }
817
+ function visitActiveLeafPaths(value, base, visit) {
818
+ if (Array.isArray(value)) {
819
+ for (let i = 0; i < value.length; i++) {
820
+ const child = value[i];
821
+ if (Array.isArray(child) || isPlainRecord(child)) {
822
+ visitActiveLeafPaths(child, [...base, i], visit);
823
+ } else {
824
+ visit([...base, i]);
825
+ }
826
+ }
827
+ return;
828
+ }
829
+ if (isPlainRecord(value)) {
830
+ for (const k of Object.keys(value)) {
831
+ const child = value[k];
832
+ if (Array.isArray(child) || isPlainRecord(child)) {
833
+ visitActiveLeafPaths(child, [...base, k], visit);
834
+ } else {
835
+ visit([...base, k]);
836
+ }
837
+ }
838
+ }
839
+ }
727
840
  function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
728
841
  const formValue = state.form.value;
729
- const value = state.getValueAtPath(segments);
842
+ const value = getAtPath(formValue, segments);
730
843
  const original = state.originals.get(key)?.value;
731
844
  let pristine = true;
732
845
  let blank = true;
@@ -745,12 +858,16 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
745
858
  let asyncPending = false;
746
859
  const submissionAttempts = state.submissionAttempts.value;
747
860
  const blurredLeafSegments = [];
748
- for (const [leafKey, entry] of state.originals) {
749
- if (!isPathPrefix(segments, entry.segments)) continue;
750
- if (segments.length === entry.segments.length) continue;
751
- if (!hasAtPath(formValue, entry.segments)) continue;
861
+ const descendantLeaves = [];
862
+ visitActiveLeafPaths(value, segments, (leafSegments) => {
863
+ const { key: leafKey } = keyForSegments(leafSegments);
864
+ const entry = state.originals.get(leafKey);
865
+ if (entry === void 0) return;
866
+ descendantLeaves.push({ key: leafKey, segments: entry.segments });
867
+ });
868
+ for (const { key: leafKey, segments: leafSeg } of descendantLeaves) {
752
869
  const leafRecord = state.fields.get(leafKey);
753
- if (!state.isPristineAtPathByKey(leafKey, entry.segments)) {
870
+ if (!state.isPristineAtPathByKey(leafKey, leafSeg)) {
754
871
  pristine = false;
755
872
  dirty = true;
756
873
  }
@@ -761,7 +878,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
761
878
  if (leafRecord?.interacted === true) interacted = true;
762
879
  if (leafRecord?.blurredAfterInteraction === true) {
763
880
  blurredAfterInteraction = true;
764
- blurredLeafSegments.push(entry.segments);
881
+ blurredLeafSegments.push(leafSeg);
765
882
  }
766
883
  if (leafRecord?.connected === true) connected = true;
767
884
  if ((state.fieldValidationCounts.get(leafKey) ?? 0) > 0) {
@@ -778,7 +895,7 @@ function buildContainerFieldStateBase(state, segments, key, formInstanceId) {
778
895
  if (leafGateOpen && since !== void 0 && (transformingSince === null || since < transformingSince))
779
896
  transformingSince = since;
780
897
  }
781
- if (state.pathHasAsyncValidationByKey(leafKey, entry.segments)) asyncPending = true;
898
+ if (state.pathHasAsyncValidationByKey(leafKey, leafSeg)) asyncPending = true;
782
899
  const ts = leafRecord?.updatedAt;
783
900
  if (ts !== void 0 && ts !== null) {
784
901
  if (updatedAt === null || ts > updatedAt) updatedAt = ts;
@@ -1394,9 +1511,8 @@ function buildFieldArrayApi(state) {
1394
1511
  return Array.isArray(current) ? current.slice() : [];
1395
1512
  }
1396
1513
  function writeArray(path, next, arrayOp) {
1397
- const { segments, key } = canonicalizePath(path);
1514
+ const { segments } = canonicalizePath(path);
1398
1515
  const meta = {
1399
- persist: state.persistOptIns.hasAnyOptInForPath(key),
1400
1516
  ...arrayOp !== void 0 ? { arrayOp } : {}
1401
1517
  };
1402
1518
  return state.setValueAtPath(segments, next, meta);
@@ -1405,7 +1521,7 @@ function buildFieldArrayApi(state) {
1405
1521
  append(path, value) {
1406
1522
  const next = readArray(path);
1407
1523
  next.push(value);
1408
- return writeArray(path, next);
1524
+ return writeArray(path, next, { kind: "insert", index: next.length - 1 });
1409
1525
  },
1410
1526
  prepend(path, value) {
1411
1527
  const next = readArray(path);
@@ -1618,157 +1734,6 @@ function walk$1(value, basePath, schema, snapshotFieldStateAt) {
1618
1734
  return result;
1619
1735
  }
1620
1736
 
1621
- const PERSISTENCE_MODULE_KEY = "persistence";
1622
- async function getStorageAdapter(storage) {
1623
- if (typeof storage === "object") return storage;
1624
- switch (storage) {
1625
- case "local": {
1626
- const { createLocalStorageAdapter } = await import('../chunks/local-storage.mjs');
1627
- return createLocalStorageAdapter();
1628
- }
1629
- case "session": {
1630
- const { createSessionStorageAdapter } = await import('../chunks/session-storage.mjs');
1631
- return createSessionStorageAdapter();
1632
- }
1633
- case "indexeddb": {
1634
- const { createIndexedDbAdapter } = await import('../chunks/indexeddb.mjs');
1635
- return createIndexedDbAdapter();
1636
- }
1637
- }
1638
- }
1639
- function resolveStorageKeyBase(config, formKey) {
1640
- return config.key ?? `${PERSISTENCE_KEY_PREFIX}${formKey}`;
1641
- }
1642
- async function removeMatchingKeys(adapter, base, keepKey) {
1643
- let keys;
1644
- try {
1645
- keys = await adapter.listKeys(base);
1646
- } catch {
1647
- return;
1648
- }
1649
- for (const key of keys) {
1650
- if (key === keepKey) continue;
1651
- if (key === base || key.startsWith(`${base}:`)) {
1652
- void adapter.removeItem(key).catch(() => void 0);
1653
- }
1654
- }
1655
- }
1656
- async function cleanupOrphanKeys(adapter, base, currentKey) {
1657
- await removeMatchingKeys(adapter, base, currentKey);
1658
- }
1659
- const STANDARD_STORAGE_KINDS = ["local", "session", "indexeddb"];
1660
- function normalizePersistConfig(input) {
1661
- if (typeof input === "string") return { storage: input };
1662
- if ("storage" in input) return input;
1663
- return { storage: input };
1664
- }
1665
- async function sweepAllOrphansAcrossStandardStores(base) {
1666
- for (const kind of STANDARD_STORAGE_KINDS) {
1667
- try {
1668
- const adapter = await getStorageAdapter(kind);
1669
- await removeMatchingKeys(adapter, base);
1670
- } catch {
1671
- }
1672
- }
1673
- }
1674
- async function sweepNonConfiguredStandardStoresForOrphans(configured, base) {
1675
- const configuredKind = typeof configured === "string" ? configured : null;
1676
- for (const kind of STANDARD_STORAGE_KINDS) {
1677
- if (kind === configuredKind) continue;
1678
- try {
1679
- const adapter = await getStorageAdapter(kind);
1680
- await removeMatchingKeys(adapter, base);
1681
- } catch {
1682
- }
1683
- }
1684
- }
1685
- function mergeSparseHydration(schemaDefaults, sparse, schema) {
1686
- return mergeDeep(schemaDefaults, sparse, [], schema);
1687
- }
1688
- function mergeDeep(target, source, path, schema) {
1689
- if (source === void 0) return target;
1690
- if (source === null || typeof source !== "object") return source;
1691
- if (Array.isArray(source)) return source;
1692
- if (!isPlainRecord(source)) return source;
1693
- if (schema !== void 0) {
1694
- const du = schema.getUnionDiscriminatorAtPath(path);
1695
- if (du !== void 0) return mergeDuAwareKeys(source, path, schema, du);
1696
- }
1697
- return mergeObjectKeys(target, source, path, schema);
1698
- }
1699
- function mergeDuAwareKeys(source, path, schema, du) {
1700
- const sourceDisc = source[du.discriminatorKey];
1701
- if (sourceDisc !== void 0 && !du.isVariantSelected(sourceDisc)) {
1702
- return { [du.discriminatorKey]: sourceDisc };
1703
- }
1704
- if (sourceDisc !== void 0) {
1705
- const variantDefault = du.getVariantDefault(sourceDisc);
1706
- if (isPlainRecord(variantDefault)) {
1707
- return mergeVariantKeys(source, variantDefault, path, schema, du);
1708
- }
1709
- }
1710
- return {};
1711
- }
1712
- function mergeVariantKeys(source, variantDefault, path, schema, du) {
1713
- const out = { ...variantDefault };
1714
- for (const key of Object.keys(source)) {
1715
- if (!safeOwnHas(variantDefault, key) && key !== du.discriminatorKey) continue;
1716
- safeAssign(
1717
- out,
1718
- key,
1719
- mergeDeep(safeOwnRead(out, key), safeOwnRead(source, key), [...path, key], schema)
1720
- );
1721
- }
1722
- return out;
1723
- }
1724
- function mergeObjectKeys(target, source, path, schema) {
1725
- const out = isPlainRecord(target) ? { ...target } : {};
1726
- for (const key of Object.keys(source)) {
1727
- safeAssign(
1728
- out,
1729
- key,
1730
- mergeDeep(safeOwnRead(out, key), safeOwnRead(source, key), [...path, key], schema)
1731
- );
1732
- }
1733
- return out;
1734
- }
1735
-
1736
- const AttaformErrorCode = {
1737
- /** A required field is in the blank set — user hasn't supplied a value. */
1738
- NoValueSupplied: "atta:no-value-supplied",
1739
- /** The schema adapter's `validateAtPath` threw synchronously. */
1740
- AdapterThrew: "atta:adapter-threw",
1741
- /**
1742
- * User code inside a `z.preprocess`, `.refine`, or `.transform`
1743
- * threw (sync or async). The adapter caught the throw and surfaced
1744
- * it as a `ValidationError` at the field path so the form's normal
1745
- * error pipeline handles it instead of leaking as an unhandled
1746
- * rejection or routing through `submitError`.
1747
- */
1748
- ValidatorThrew: "atta:validator-threw",
1749
- /**
1750
- * A function-form `defaultValues` factory threw or its promise
1751
- * rejected. The runtime captures the raw error on `form.hydrateError`
1752
- * and ALSO surfaces a form-level `ValidationError` (path `[]`) so
1753
- * the standard error pipeline carries the signal. Critical for the
1754
- * SSR round-trip: `hydrateError` itself does not ride the wire
1755
- * payload, but `schemaErrors` does, so the client sees the failure
1756
- * after rehydration without an extra channel.
1757
- */
1758
- HydrationFailed: "atta:hydration-failed",
1759
- /** The supplied path didn't resolve to any node in the schema. */
1760
- PathNotFound: "atta:path-not-found",
1761
- /**
1762
- * A walked form's `activate()` (async `defaultValues` factory) threw
1763
- * during `wizard.handleSubmit`'s path walk. Surfaced as a synthetic
1764
- * `ValidationError` at the form-level path (`[]`) so the wizard's
1765
- * aggregate error pipeline can carry the failure alongside ordinary
1766
- * validation errors. The raw factory error remains on
1767
- * `form.hydrateError` for retry UX.
1768
- */
1769
- ActivationFailed: "atta:activation-failed"
1770
- };
1771
-
1772
1737
  const warnedNoScopeStores = __DEV__ ? /* @__PURE__ */ new WeakSet() : null;
1773
1738
  function buildProcessForm(state, formInstanceId, options = {}) {
1774
1739
  const invalidPolicy = options.onInvalidSubmit ?? "focus-first-error";
@@ -2026,44 +1991,6 @@ function applyInvalidSubmitPolicy(state, formInstanceId, policy) {
2026
1991
  target.element.focus({ preventScroll: true });
2027
1992
  }
2028
1993
 
2029
- function captureUserCallSite() {
2030
- const raw = new Error().stack;
2031
- if (typeof raw !== "string") return void 0;
2032
- const lines = raw.split("\n");
2033
- for (let i = 1; i < lines.length; i++) {
2034
- const frame = lines[i];
2035
- if (frame === void 0) continue;
2036
- if (/attaform[/-]forms?/i.test(frame)) continue;
2037
- if (/\bforms\.[A-Za-z0-9_-]+\.m?js\b/.test(frame)) continue;
2038
- const trimmed = frame.trim();
2039
- if (trimmed.length === 0) continue;
2040
- return shortenSourceFrame(trimmed);
2041
- }
2042
- return void 0;
2043
- }
2044
- function shortenSourceFrame(frame) {
2045
- const match = /(?:^|\s|\()([^\s()]+):(\d+):\d+\)?$/.exec(frame);
2046
- if (match === null) return frame;
2047
- const [, urlOrPath, line] = match;
2048
- if (urlOrPath === void 0 || line === void 0) return frame;
2049
- let path = urlOrPath;
2050
- path = path.replace(/^[a-z]+:\/\/[^/]+\//i, "");
2051
- path = path.replace(/^_nuxt\//, "");
2052
- path = path.replace(/^\//, "");
2053
- return `(${path}:${line})`;
2054
- }
2055
-
2056
- function extractSchemaFields(schema) {
2057
- try {
2058
- const root = schema.getDefaultAtPath([]);
2059
- if (root !== null && typeof root === "object" && !Array.isArray(root)) {
2060
- return Object.keys(root);
2061
- }
2062
- } catch {
2063
- }
2064
- return [];
2065
- }
2066
-
2067
1994
  const warnedRejections = __DEV__ ? /* @__PURE__ */ new WeakMap() : null;
2068
1995
  function shouldWarnOnce$1(store, key) {
2069
1996
  if (warnedRejections === null) return false;
@@ -2393,16 +2320,7 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2393
2320
  const slimTypes = state.schema.getSlimPrimitiveTypesAtPath(segments);
2394
2321
  const acceptsUndefined = slimTypes.has("undefined");
2395
2322
  const acceptsString = slimTypes.has("string");
2396
- const persist = options?.persist === true;
2397
- const acknowledgeSensitive = options?.acknowledgeSensitive === true;
2398
- const multiTab = options?.multiTab !== false;
2399
2323
  const transforms = options?.transforms ?? EMPTY_TRANSFORMS;
2400
- const markNoSync = !multiTab ? () => {
2401
- state.incrementNoSyncOptOut(pathKey);
2402
- } : void 0;
2403
- const unmarkNoSync = !multiTab ? () => {
2404
- state.decrementNoSyncOptOut(pathKey);
2405
- } : void 0;
2406
2324
  const coerce = buildCoerceFn(
2407
2325
  state.schema,
2408
2326
  segments,
@@ -2413,18 +2331,10 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2413
2331
  segments,
2414
2332
  coerceIndex
2415
2333
  );
2416
- if (persist && !state.ssr && !state.modules.has(PERSISTENCE_MODULE_KEY)) {
2417
- throw new AnonPersistError({
2418
- cause: "register-without-config",
2419
- schemaFields: extractSchemaFields(state.schema),
2420
- callSite: captureUserCallSite()
2421
- });
2422
- }
2423
2334
  const { aria } = computeFieldIdentity(formInstanceId, state.formKey, pathKey);
2424
2335
  const isRequired = state.schema.isRequiredAtPath(segments);
2425
2336
  const ariaEnabled = options?.autoAria ?? formAutoAria;
2426
2337
  const ariaDisplayState = getDisplayStateAt !== void 0 ? computed(() => getDisplayStateAt(segments)) : void 0;
2427
- let boundElement = null;
2428
2338
  const internalRv = {
2429
2339
  innerRef,
2430
2340
  displayValue,
@@ -2434,8 +2344,7 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2434
2344
  segments,
2435
2345
  slimDefault,
2436
2346
  withInstanceMeta({
2437
- blank: true,
2438
- persist
2347
+ blank: true
2439
2348
  })
2440
2349
  );
2441
2350
  },
@@ -2443,7 +2352,6 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2443
2352
  state.markInteracted(segments);
2444
2353
  },
2445
2354
  registerElement: (element) => {
2446
- boundElement = element;
2447
2355
  if (!INTERACTIVE_TAG_NAMES.has(element.tagName)) return;
2448
2356
  const added = state.registerElement(segments, element, formInstanceId);
2449
2357
  if (added) attachFocusListeners(state, segments, element, instanceMeta);
@@ -2451,11 +2359,9 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2451
2359
  deregisterElement: (element) => {
2452
2360
  detachFocusListeners(element);
2453
2361
  state.deregisterElement(segments, element);
2454
- if (boundElement === element) boundElement = null;
2455
2362
  },
2456
2363
  setValueWithInternalPath: (value, meta) => {
2457
- const resolvedMeta = meta === void 0 && boundElement !== null ? { persist: state.persistOptIns.hasOptIn(getOrAssignElementId(boundElement), pathKey) } : meta;
2458
- return state.setValueAtPath(segments, value, withInstanceMeta(resolvedMeta));
2364
+ return state.setValueAtPath(segments, value, withInstanceMeta(meta));
2459
2365
  },
2460
2366
  // Called by the `vRegisterHint` compile-time transform's wrapping
2461
2367
  // IIFE on every server-side render of `<element v-register="…">`.
@@ -2493,15 +2399,6 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2493
2399
  segments: Object.freeze(segments.slice()),
2494
2400
  formKey: state.formKey,
2495
2401
  formInstanceId,
2496
- // --- Persistence opt-in (internal; the directive is the only
2497
- // legitimate consumer) ---
2498
- persist,
2499
- acknowledgeSensitive,
2500
- persistOptIns: state.persistOptIns,
2501
- isSensitivePath: state.isSensitivePath,
2502
- multiTab,
2503
- ...markNoSync !== void 0 ? { markNoSync } : {},
2504
- ...unmarkNoSync !== void 0 ? { unmarkNoSync } : {},
2505
2402
  transforms,
2506
2403
  coerce,
2507
2404
  ...coerceElement !== void 0 ? { coerceElement } : {},
@@ -2794,19 +2691,116 @@ function buildFormApi(state, formInstanceId, options = {}) {
2794
2691
  }
2795
2692
  };
2796
2693
  const getFormMetaBase = () => {
2797
- const { base: rootBase } = buildContainerFieldStateBase(
2694
+ let rollup;
2695
+ const rootBase = () => rollup ?? (rollup = buildContainerFieldStateBase(
2798
2696
  state,
2799
2697
  ROOT_PATH,
2800
2698
  ROOT_PATH_KEY,
2801
2699
  formInstanceId
2802
- );
2700
+ ).base);
2803
2701
  return {
2804
- ...rootBase,
2702
+ // Rollup-derived (FieldStateBase) — the whole rollup builds once, on the
2703
+ // first access of any of these.
2704
+ get value() {
2705
+ return rootBase().value;
2706
+ },
2707
+ get original() {
2708
+ return rootBase().original;
2709
+ },
2710
+ get pristine() {
2711
+ return rootBase().pristine;
2712
+ },
2713
+ get dirty() {
2714
+ return rootBase().dirty;
2715
+ },
2716
+ get focused() {
2717
+ return rootBase().focused;
2718
+ },
2719
+ get blurred() {
2720
+ return rootBase().blurred;
2721
+ },
2722
+ get touched() {
2723
+ return rootBase().touched;
2724
+ },
2725
+ get interacted() {
2726
+ return rootBase().interacted;
2727
+ },
2728
+ get blurredAfterInteraction() {
2729
+ return rootBase().blurredAfterInteraction;
2730
+ },
2731
+ get connected() {
2732
+ return rootBase().connected;
2733
+ },
2734
+ get element() {
2735
+ return rootBase().element;
2736
+ },
2737
+ get elements() {
2738
+ return rootBase().elements;
2739
+ },
2740
+ get updatedAt() {
2741
+ return rootBase().updatedAt;
2742
+ },
2743
+ get errors() {
2744
+ return rootBase().errors;
2745
+ },
2746
+ get validating() {
2747
+ return rootBase().validating;
2748
+ },
2749
+ get valid() {
2750
+ return rootBase().valid;
2751
+ },
2752
+ get transforming() {
2753
+ return rootBase().transforming;
2754
+ },
2755
+ get busy() {
2756
+ return rootBase().busy;
2757
+ },
2758
+ get transformError() {
2759
+ return rootBase().transformError;
2760
+ },
2761
+ get path() {
2762
+ return rootBase().path;
2763
+ },
2764
+ get id() {
2765
+ return rootBase().id;
2766
+ },
2767
+ get aria() {
2768
+ return rootBase().aria;
2769
+ },
2770
+ get key() {
2771
+ return rootBase().key;
2772
+ },
2773
+ get blank() {
2774
+ return rootBase().blank;
2775
+ },
2776
+ get label() {
2777
+ return rootBase().label;
2778
+ },
2779
+ get description() {
2780
+ return rootBase().description;
2781
+ },
2782
+ get placeholder() {
2783
+ return rootBase().placeholder;
2784
+ },
2785
+ get meta() {
2786
+ return rootBase().meta;
2787
+ },
2788
+ get errorCount() {
2789
+ return rootBase().errors.length;
2790
+ },
2791
+ // Form-level scalars — EAGER reads, tracked on every field-state eval.
2792
+ // They are O(1) refs that never change on a keystroke, so tracking them
2793
+ // per field costs nothing on the hot path. Kept eager (NOT lazy like the
2794
+ // rollup) because behaviors beyond the predicate's own output depend on
2795
+ // every field re-evaluating when they flip — most notably, the display
2796
+ // engine is cleared on submit (revealing held spinners), and that
2797
+ // imperative reset only becomes visible if `submitting` is a tracked dep
2798
+ // of each field. Matches the pre-bust dependency set for these scalars
2799
+ // exactly.
2805
2800
  submitting: state.submitting.value,
2806
2801
  submissionAttempts: state.submissionAttempts.value,
2807
2802
  departAttempts: state.departAttempts.value,
2808
2803
  submitError: state.submitError.value,
2809
- errorCount: rootBase.errors.length,
2810
2804
  submitted: state.submitted.value,
2811
2805
  instanceId: formInstanceId
2812
2806
  };
@@ -2842,13 +2836,16 @@ function buildFormApi(state, formInstanceId, options = {}) {
2842
2836
  return computed(() => getAtPath(state.form.value, segments));
2843
2837
  }
2844
2838
  function setValueImpl(pathOrValue, maybeValue) {
2845
- if (arguments.length === 1) {
2839
+ const argc = arguments.length;
2840
+ const isPathForm = argc >= 2 && (typeof pathOrValue === "string" || Array.isArray(pathOrValue));
2841
+ const writeMeta = (extra) => withInstanceMeta(extra);
2842
+ if (!isPathForm) {
2846
2843
  const next = typeof pathOrValue === "function" ? pathOrValue(structuralSnapshot(state.form.value)) : pathOrValue;
2847
2844
  const walked2 = walkUnsetSentinels(
2848
2845
  next,
2849
2846
  state.schema
2850
2847
  );
2851
- const ok2 = state.setValueAtPath([], walked2.cleanedValues, withInstanceMeta());
2848
+ const ok2 = state.setValueAtPath([], walked2.cleanedValues, writeMeta());
2852
2849
  if (!ok2) return false;
2853
2850
  reMarkBlanksAfterSubstitution(walked2.paths);
2854
2851
  return true;
@@ -2862,7 +2859,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2862
2859
  if (parentDU?.discriminatorKey === last) {
2863
2860
  const slimDefault = state.schema.getEmptyValueAtPath(segments);
2864
2861
  const blank = blankForKind(slimDefault);
2865
- return state.setValueAtPath(segments, blank, withInstanceMeta({ blank: true }));
2862
+ return state.setValueAtPath(segments, blank, writeMeta({ blank: true }));
2866
2863
  }
2867
2864
  }
2868
2865
  const blankPaths = [];
@@ -2873,9 +2870,9 @@ function buildFormApi(state, formInstanceId, options = {}) {
2873
2870
  );
2874
2871
  const segmentsKey = canonicalizePath(segments).key;
2875
2872
  if (blankPaths.length === 1 && blankPaths[0] === segmentsKey) {
2876
- return state.setValueAtPath(segments, expanded, withInstanceMeta({ blank: true }));
2873
+ return state.setValueAtPath(segments, expanded, writeMeta({ blank: true }));
2877
2874
  }
2878
- const ok2 = state.setValueAtPath(segments, expanded, withInstanceMeta());
2875
+ const ok2 = state.setValueAtPath(segments, expanded, writeMeta());
2879
2876
  if (!ok2) return false;
2880
2877
  for (const pathKey of blankPaths) {
2881
2878
  const blankSegments = segmentsForPathKey(pathKey);
@@ -2883,7 +2880,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2883
2880
  state.setValueAtPath(
2884
2881
  blankSegments,
2885
2882
  state.getValueAtPath(blankSegments),
2886
- withInstanceMeta({ blank: true })
2883
+ writeMeta({ blank: true })
2887
2884
  );
2888
2885
  }
2889
2886
  return true;
@@ -2903,7 +2900,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2903
2900
  segments,
2904
2901
  state.schema
2905
2902
  );
2906
- const ok = state.setValueAtPath(segments, walked.cleanedValues, withInstanceMeta());
2903
+ const ok = state.setValueAtPath(segments, walked.cleanedValues, writeMeta());
2907
2904
  if (!ok) return false;
2908
2905
  reMarkBlanksAfterSubstitution(walked.paths);
2909
2906
  return true;
@@ -3133,7 +3130,6 @@ function buildFormApi(state, formInstanceId, options = {}) {
3133
3130
  instanceId: formInstanceId
3134
3131
  })
3135
3132
  );
3136
- const persistenceHandle = state.modules.get(PERSISTENCE_MODULE_KEY);
3137
3133
  const reset = (nextDefaultValues) => {
3138
3134
  if (nextDefaultValues === void 0) {
3139
3135
  state.reset();
@@ -3148,16 +3144,10 @@ function buildFormApi(state, formInstanceId, options = {}) {
3148
3144
  state.originalBlankPaths.add(pathKey);
3149
3145
  }
3150
3146
  }
3151
- if (persistenceHandle !== void 0) {
3152
- void persistenceHandle.ready.then((m) => m?.clearPersistedDraft()).catch(() => void 0);
3153
- }
3154
3147
  };
3155
3148
  const resetField = (pathInput) => {
3156
3149
  const segments = canonicalizePath(pathInput).segments;
3157
3150
  state.resetField(segments);
3158
- if (persistenceHandle !== void 0) {
3159
- void persistenceHandle.ready.then((m) => m?.clearPersistedDraft(segments)).catch(() => void 0);
3160
- }
3161
3151
  };
3162
3152
  function clear(pathInput) {
3163
3153
  if (pathInput === void 0) {
@@ -3165,31 +3155,6 @@ function buildFormApi(state, formInstanceId, options = {}) {
3165
3155
  }
3166
3156
  return setValueImpl(pathInput, unset);
3167
3157
  }
3168
- const persist = async (pathInput, options2) => {
3169
- const segments = canonicalizePath(pathInput).segments;
3170
- if (!allowSensitivePersist(
3171
- segments,
3172
- options2?.acknowledgeSensitive === true,
3173
- state.isSensitivePath
3174
- )) {
3175
- return;
3176
- }
3177
- if (persistenceHandle === void 0) return;
3178
- const persistence = await persistenceHandle.ready;
3179
- if (persistence === void 0) return;
3180
- await persistence.writePathImmediately(segments);
3181
- };
3182
- const clearPersistedDraft = async (pathInput) => {
3183
- if (persistenceHandle === void 0) return;
3184
- const persistence = await persistenceHandle.ready;
3185
- if (persistence === void 0) return;
3186
- if (pathInput === void 0) {
3187
- await persistence.clearPersistedDraft();
3188
- return;
3189
- }
3190
- const segments = canonicalizePath(pathInput).segments;
3191
- await persistence.clearPersistedDraft(segments);
3192
- };
3193
3158
  function touch(pathInput) {
3194
3159
  const segments = pathInput === void 0 ? ROOT_PATH : canonicalizePath(pathInput).segments;
3195
3160
  state.touchAtPath(segments);
@@ -3274,7 +3239,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
3274
3239
  }
3275
3240
  return Object.freeze(out);
3276
3241
  }
3277
- return {
3242
+ const api = {
3278
3243
  handleSubmit: gated(handleSubmit),
3279
3244
  // Callable readonly Proxies (`values`, `fields`, `errors`) and the
3280
3245
  // reactive containers (`meta`, `history`, `blankPaths`) are exposed
@@ -3339,8 +3304,6 @@ function buildFormApi(state, formInstanceId, options = {}) {
3339
3304
  reset: gated(reset),
3340
3305
  resetField: gated(resetField),
3341
3306
  clear: gated(clear),
3342
- persist: gated(persist),
3343
- clearPersistedDraft: gated(clearPersistedDraft),
3344
3307
  focusFirstError: gated(focusFirstError),
3345
3308
  scrollToFirstError: gated(scrollToFirstError),
3346
3309
  applyInvalidSubmitPolicy: gated(applyInvalidSubmitPolicyPublic),
@@ -3363,6 +3326,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
3363
3326
  return blankPathsView;
3364
3327
  }
3365
3328
  };
3329
+ return api;
3366
3330
  }
3367
3331
 
3368
3332
  const IDLE = Object.freeze({ display: "idle" });
@@ -3789,6 +3753,7 @@ function createArrayBookkeeping(deps) {
3789
3753
  originals,
3790
3754
  blankPaths,
3791
3755
  originalBlankPaths,
3756
+ authoredPaths,
3792
3757
  fieldValidationCounts,
3793
3758
  fieldValidatingSince,
3794
3759
  fieldValidationState,
@@ -3816,6 +3781,7 @@ function createArrayBookkeeping(deps) {
3816
3781
  }));
3817
3782
  migrateSetSubtree(blankPaths, arrayPath, remap);
3818
3783
  migrateSetSubtree(originalBlankPaths, arrayPath, remap);
3784
+ migrateSetSubtree(authoredPaths, arrayPath, remap);
3819
3785
  migrateMapSubtree(fieldValidatingSince, arrayPath, remap, (since) => since);
3820
3786
  migrateMapSubtree(fieldValidationCounts, arrayPath, remap, (count) => count);
3821
3787
  arrayIdentity.applyRemap(arrayPath, remap);
@@ -3859,7 +3825,7 @@ function createArrayBookkeeping(deps) {
3859
3825
  activeValidations.value = Math.max(0, activeValidations.value - 1);
3860
3826
  decFieldValidation(key);
3861
3827
  }
3862
- entry.controller.abort();
3828
+ entry.aborted = true;
3863
3829
  fieldValidationState.delete(key);
3864
3830
  }
3865
3831
  }
@@ -3871,6 +3837,57 @@ function createArrayBookkeeping(deps) {
3871
3837
  };
3872
3838
  }
3873
3839
 
3840
+ function mergeSparseHydration(schemaDefaults, sparse, schema) {
3841
+ return mergeDeep(schemaDefaults, sparse, [], schema);
3842
+ }
3843
+ function mergeDeep(target, source, path, schema) {
3844
+ if (source === void 0) return target;
3845
+ if (source === null || typeof source !== "object") return source;
3846
+ if (Array.isArray(source)) return source;
3847
+ if (!isPlainRecord(source)) return source;
3848
+ if (schema !== void 0) {
3849
+ const du = schema.getUnionDiscriminatorAtPath(path);
3850
+ if (du !== void 0) return mergeDuAwareKeys(source, path, schema, du);
3851
+ }
3852
+ return mergeObjectKeys(target, source, path, schema);
3853
+ }
3854
+ function mergeDuAwareKeys(source, path, schema, du) {
3855
+ const sourceDisc = source[du.discriminatorKey];
3856
+ if (sourceDisc !== void 0 && !du.isVariantSelected(sourceDisc)) {
3857
+ return { [du.discriminatorKey]: sourceDisc };
3858
+ }
3859
+ if (sourceDisc !== void 0) {
3860
+ const variantDefault = du.getVariantDefault(sourceDisc);
3861
+ if (isPlainRecord(variantDefault)) {
3862
+ return mergeVariantKeys(source, variantDefault, path, schema, du);
3863
+ }
3864
+ }
3865
+ return {};
3866
+ }
3867
+ function mergeVariantKeys(source, variantDefault, path, schema, du) {
3868
+ const out = { ...variantDefault };
3869
+ for (const key of Object.keys(source)) {
3870
+ if (!safeOwnHas(variantDefault, key) && key !== du.discriminatorKey) continue;
3871
+ safeAssign(
3872
+ out,
3873
+ key,
3874
+ mergeDeep(safeOwnRead(out, key), safeOwnRead(source, key), [...path, key], schema)
3875
+ );
3876
+ }
3877
+ return out;
3878
+ }
3879
+ function mergeObjectKeys(target, source, path, schema) {
3880
+ const out = isPlainRecord(target) ? { ...target } : {};
3881
+ for (const key of Object.keys(source)) {
3882
+ safeAssign(
3883
+ out,
3884
+ key,
3885
+ mergeDeep(safeOwnRead(out, key), safeOwnRead(source, key), [...path, key], schema)
3886
+ );
3887
+ }
3888
+ return out;
3889
+ }
3890
+
3874
3891
  function isHydratedFieldRecord(value) {
3875
3892
  if (typeof value !== "object" || value === null) return false;
3876
3893
  const r = value;
@@ -3955,6 +3972,17 @@ function walkAuthoredFromConstraints(value, prefix, out) {
3955
3972
  }
3956
3973
  }
3957
3974
  }
3975
+ function freshElementIndices(op) {
3976
+ switch (op.kind) {
3977
+ case "insert":
3978
+ case "replace-at":
3979
+ return [op.index];
3980
+ case "remove":
3981
+ case "swap":
3982
+ case "move":
3983
+ return [];
3984
+ }
3985
+ }
3958
3986
  function walkAuthoredFromSchemaDiff(withDefaults, withoutDefaults, prefix, out) {
3959
3987
  if (isPlainRecord(withDefaults) && isPlainRecord(withoutDefaults)) {
3960
3988
  const left = withDefaults;
@@ -3993,26 +4021,8 @@ function createFormStore(options) {
3993
4021
  const formChangeListeners = /* @__PURE__ */ new Set();
3994
4022
  const submitSuccessListeners = /* @__PURE__ */ new Set();
3995
4023
  const resetListeners = /* @__PURE__ */ new Set();
3996
- const persistOptIns = createPersistOptInRegistry();
3997
- const noSyncPaths = /* @__PURE__ */ new Set();
3998
- const noSyncPathCounts = /* @__PURE__ */ new Map();
3999
- function incrementNoSyncOptOut(path) {
4000
- const next = (noSyncPathCounts.get(path) ?? 0) + 1;
4001
- noSyncPathCounts.set(path, next);
4002
- if (next === 1) noSyncPaths.add(path);
4003
- }
4004
- function decrementNoSyncOptOut(path) {
4005
- const current = noSyncPathCounts.get(path) ?? 0;
4006
- if (current <= 1) {
4007
- noSyncPathCounts.delete(path);
4008
- noSyncPaths.delete(path);
4009
- return;
4010
- }
4011
- noSyncPathCounts.set(path, current - 1);
4012
- }
4013
4024
  const coerceIndex = resolveCoercionIndex(options.coerce);
4014
4025
  const resolvedGetDisplayState = resolveGetDisplayState(options.getDisplayState);
4015
- const resolvedIsSensitivePath = options.isSensitivePath ?? isSensitivePath;
4016
4026
  const cleanupHooks = [];
4017
4027
  const modules = /* @__PURE__ */ new Map();
4018
4028
  const fieldValidatingSince = reactive(/* @__PURE__ */ new Map());
@@ -4031,11 +4041,8 @@ function createFormStore(options) {
4031
4041
  if (constraints !== void 0) {
4032
4042
  walkAuthoredFromConstraints(constraints, [], authoredPaths);
4033
4043
  }
4034
- const slimResponse = schema.getDefaultValues({
4035
- useDefaultSchemaValues: false,
4036
- strict
4037
- });
4038
- walkAuthoredFromSchemaDiff(schemaWithDefaultsData, slimResponse.data, [], authoredPaths);
4044
+ const slimBaseline = schema.getEmptyValueAtPath([]);
4045
+ walkAuthoredFromSchemaDiff(schemaWithDefaultsData, slimBaseline, [], authoredPaths);
4039
4046
  }
4040
4047
  rebuildAuthoredPaths(defaultValues, schemaInitialData);
4041
4048
  function filterAuthoredErrors(errors) {
@@ -4054,7 +4061,7 @@ function createFormStore(options) {
4054
4061
  });
4055
4062
  const form = ref(stubbedInitialData);
4056
4063
  const arrayIdentity = createArrayIdentity((arraySegs) => {
4057
- const v = getAtPath(form.value, arraySegs);
4064
+ const v = getAtPath(toRaw(form.value), arraySegs);
4058
4065
  return Array.isArray(v) ? v.length : 0;
4059
4066
  });
4060
4067
  function arrayElementKey(path) {
@@ -4097,14 +4104,7 @@ function createFormStore(options) {
4097
4104
  const segments = segmentsForPathKey(pathKey);
4098
4105
  if (segments === null) continue;
4099
4106
  if (!schema.isRequiredAtPath(segments)) continue;
4100
- result.set(pathKey, [
4101
- {
4102
- message: "No value supplied",
4103
- path: [...segments],
4104
- formKey,
4105
- code: AttaformErrorCode.NoValueSupplied
4106
- }
4107
- ]);
4107
+ result.set(pathKey, [makeBlankRequiredError(segments, formKey)]);
4108
4108
  }
4109
4109
  return result;
4110
4110
  });
@@ -4344,6 +4344,7 @@ function createFormStore(options) {
4344
4344
  originals,
4345
4345
  blankPaths,
4346
4346
  originalBlankPaths,
4347
+ authoredPaths,
4347
4348
  fieldValidationCounts,
4348
4349
  fieldValidatingSince,
4349
4350
  fieldValidationState,
@@ -4353,17 +4354,8 @@ function createFormStore(options) {
4353
4354
  touchFieldRecord,
4354
4355
  decFieldValidation
4355
4356
  });
4356
- function applyFormReplacement(next, meta) {
4357
- const prev = form.value;
4358
- if (Object.is(prev, next)) return;
4357
+ function commitWritePatches(patches, meta) {
4359
4358
  const now = (/* @__PURE__ */ new Date()).toISOString();
4360
- const patches = [];
4361
- diffAndApply(prev, next, [], (patch) => {
4362
- patches.push(patch);
4363
- });
4364
- if (!applyChangedKeys(prev, next)) {
4365
- form.value = next;
4366
- }
4367
4359
  for (const patch of patches) {
4368
4360
  const { key } = canonicalizePath(patch.path);
4369
4361
  if (patch.kind === "added" && !originals.has(key)) {
@@ -4379,9 +4371,61 @@ function createFormStore(options) {
4379
4371
  }
4380
4372
  }
4381
4373
  }
4374
+ function applyFormReplacementWithPath(next, meta, arrayOpPath) {
4375
+ const prev = form.value;
4376
+ if (Object.is(prev, next)) return;
4377
+ const patches = [];
4378
+ diffAndApply(prev, next, [], (patch) => {
4379
+ patches.push(patch);
4380
+ });
4381
+ if (!applyChangedKeys(prev, next, arrayOpPath, [])) {
4382
+ form.value = next;
4383
+ } else if (patches.some(
4384
+ (p) => p.path.length > 0 && typeof p.path[0] === "string" && isShadowedKey(p.path[0])
4385
+ )) {
4386
+ triggerRef(form);
4387
+ }
4388
+ commitWritePatches(patches, meta);
4389
+ }
4390
+ function applyFormReplacement(next, meta) {
4391
+ applyFormReplacementWithPath(next, meta, null);
4392
+ }
4393
+ function applyTargetedWrite(path, completedValue, meta) {
4394
+ const result = tryInPlaceLeafWrite(form.value, path, completedValue);
4395
+ if (!result.applied) {
4396
+ applyFormReplacementWithPath(
4397
+ setAtPathWithSchemaFill(form.value, schema, path, completedValue),
4398
+ meta,
4399
+ meta?.arrayOp !== void 0 ? path : null
4400
+ );
4401
+ return;
4402
+ }
4403
+ const patches = [];
4404
+ diffAndApply(result.old, completedValue, path, (patch) => {
4405
+ patches.push(patch);
4406
+ });
4407
+ commitWritePatches(patches, meta);
4408
+ }
4382
4409
  function setValueAtPath(path, value, meta) {
4383
- value = stripSymbolsDeep(value);
4384
- if (!isSlimPrimitiveValid(schema, form, path, value)) {
4410
+ if (meta?.arrayOp !== void 0 && Array.isArray(value)) {
4411
+ for (const idx of freshElementIndices(meta.arrayOp)) {
4412
+ value[idx] = stripSymbolsDeep(value[idx]);
4413
+ }
4414
+ } else {
4415
+ value = stripSymbolsDeep(value);
4416
+ }
4417
+ let slimOk = true;
4418
+ if (meta?.arrayOp !== void 0 && Array.isArray(value)) {
4419
+ for (const idx of freshElementIndices(meta.arrayOp)) {
4420
+ if (!isSlimPrimitiveValid(schema, form, [...path, idx], value[idx])) {
4421
+ slimOk = false;
4422
+ break;
4423
+ }
4424
+ }
4425
+ } else {
4426
+ slimOk = isSlimPrimitiveValid(schema, form, path, value);
4427
+ }
4428
+ if (!slimOk) {
4385
4429
  return false;
4386
4430
  }
4387
4431
  if (path.length >= 2) {
@@ -4474,22 +4518,37 @@ function createFormStore(options) {
4474
4518
  }
4475
4519
  }
4476
4520
  }
4521
+ const currentValue = getAtPath(form.value, path);
4477
4522
  const pathKey = canonicalizePath(path).key;
4478
4523
  if (meta?.blank === true) {
4479
4524
  blankPaths.add(pathKey);
4480
4525
  } else {
4481
4526
  if (blankPaths.has(pathKey)) blankPaths.delete(pathKey);
4482
- if (meta?.arrayOp === void 0) {
4527
+ if (meta?.arrayOp === void 0 && (isPlainRecord(currentValue) || Array.isArray(currentValue))) {
4483
4528
  for (const existingKey of [...blankPaths]) {
4484
4529
  if (isPathKeyUnder(existingKey, path)) blankPaths.delete(existingKey);
4485
4530
  }
4486
4531
  }
4487
4532
  }
4488
4533
  const wasAuthoredBefore = authoredPaths.has(pathKey);
4489
- walkAuthoredFromConstraints(value, path, authoredPaths);
4534
+ if (meta?.arrayOp !== void 0 && Array.isArray(value)) {
4535
+ if (path.length > 0) authoredPaths.add(pathKey);
4536
+ for (const idx of freshElementIndices(meta.arrayOp)) {
4537
+ walkAuthoredFromConstraints(value[idx], [...path, idx], authoredPaths);
4538
+ }
4539
+ } else {
4540
+ walkAuthoredFromConstraints(value, path, authoredPaths);
4541
+ }
4490
4542
  const newlyAuthored = !wasAuthoredBefore && authoredPaths.has(pathKey);
4491
- const completedValue = mergeStructural(schema, path, value);
4492
- const currentValue = getAtPath(form.value, path);
4543
+ let completedValue;
4544
+ if (meta?.arrayOp !== void 0 && Array.isArray(value)) {
4545
+ for (const idx of freshElementIndices(meta.arrayOp)) {
4546
+ value[idx] = mergeStructural(schema, [...path, idx], value[idx]);
4547
+ }
4548
+ completedValue = value;
4549
+ } else {
4550
+ completedValue = mergeStructural(schema, path, value);
4551
+ }
4493
4552
  if (Object.is(currentValue, completedValue)) {
4494
4553
  if (newlyAuthored && schema.isPreprocessOrCoerceLeaf(path)) {
4495
4554
  const modeForAuthoringTransition = meta?.instance?.validateOn ?? fieldValidationMode;
@@ -4503,8 +4562,7 @@ function createFormStore(options) {
4503
4562
  return true;
4504
4563
  }
4505
4564
  const oldArrayLength = Array.isArray(currentValue) ? currentValue.length : 0;
4506
- const nextForm = setAtPathWithSchemaFill(form.value, schema, path, completedValue);
4507
- applyFormReplacement(nextForm, meta);
4565
+ applyTargetedWrite(path, completedValue, meta);
4508
4566
  if (meta?.arrayOp !== void 0) {
4509
4567
  const remap = remapForOp(meta.arrayOp, oldArrayLength);
4510
4568
  arrayBookkeeping.migrateElementState(path, remap);
@@ -4587,7 +4645,7 @@ function createFormStore(options) {
4587
4645
  const prevValidation = fieldValidationState.get(parentKey2);
4588
4646
  if (prevValidation !== void 0) {
4589
4647
  if (prevValidation.timer !== null) clearTimeout(prevValidation.timer);
4590
- prevValidation.controller.abort();
4648
+ prevValidation.aborted = true;
4591
4649
  fieldValidationState.delete(parentKey2);
4592
4650
  }
4593
4651
  appliedSync = true;
@@ -4611,15 +4669,19 @@ function createFormStore(options) {
4611
4669
  const prev = fieldValidationState.get(key);
4612
4670
  if (prev !== void 0) {
4613
4671
  if (prev.timer !== null) clearTimeout(prev.timer);
4614
- prev.controller.abort();
4672
+ prev.aborted = true;
4615
4673
  }
4616
- const controller = new AbortController();
4617
- const fresh = { controller, timer: null, settled: false, released: false };
4674
+ const fresh = {
4675
+ aborted: false,
4676
+ timer: null,
4677
+ settled: false,
4678
+ released: false
4679
+ };
4618
4680
  fieldValidationState.set(key, fresh);
4619
4681
  const myEpoch = ++scheduleEpoch;
4620
4682
  const run = () => {
4621
4683
  fresh.timer = null;
4622
- if (controller.signal.aborted) return;
4684
+ if (fresh.aborted) return;
4623
4685
  let activeIncremented = false;
4624
4686
  try {
4625
4687
  activeValidations.value += 1;
@@ -4636,7 +4698,7 @@ function createFormStore(options) {
4636
4698
  const dataAtScope = subtreeScope ? getAtPath(form.value, path) : form.value;
4637
4699
  const scopeKey = subtreeScope ? canonicalizePath(path).key : ROOT_PATH_KEY;
4638
4700
  void Promise.resolve().then(() => schema.validateAtPath(dataAtScope, scopePath)).then((response) => {
4639
- if (controller.signal.aborted) return;
4701
+ if (fresh.aborted) return;
4640
4702
  if (myEpoch <= lastCommittedEpoch) return;
4641
4703
  lastCommittedEpoch = myEpoch;
4642
4704
  if (effectiveMode === "blur") {
@@ -4673,7 +4735,7 @@ function createFormStore(options) {
4673
4735
  activeValidations.value = Math.max(0, activeValidations.value - 1);
4674
4736
  decFieldValidation(pkey);
4675
4737
  }
4676
- entry.controller.abort();
4738
+ entry.aborted = true;
4677
4739
  }
4678
4740
  fieldValidationState.clear();
4679
4741
  }
@@ -4689,7 +4751,7 @@ function createFormStore(options) {
4689
4751
  decFieldValidation(key);
4690
4752
  entry.released = true;
4691
4753
  }
4692
- entry.controller.abort();
4754
+ entry.aborted = true;
4693
4755
  fieldValidationState.delete(key);
4694
4756
  }
4695
4757
  }
@@ -4748,9 +4810,6 @@ function createFormStore(options) {
4748
4810
  formChangeListeners.clear();
4749
4811
  submitSuccessListeners.clear();
4750
4812
  resetListeners.clear();
4751
- persistOptIns.clear();
4752
- noSyncPaths.clear();
4753
- noSyncPathCounts.clear();
4754
4813
  }
4755
4814
  function getValueAtPath(path) {
4756
4815
  return getAtPath(form.value, path);
@@ -5306,11 +5365,6 @@ function createFormStore(options) {
5306
5365
  registerDrain,
5307
5366
  awaitPendingWrites,
5308
5367
  modules,
5309
- persistOptIns,
5310
- isSensitivePath: resolvedIsSensitivePath,
5311
- noSyncPaths,
5312
- incrementNoSyncOptOut,
5313
- decrementNoSyncOptOut,
5314
5368
  coerceIndex,
5315
5369
  blankPaths,
5316
5370
  originalBlankPaths,
@@ -5318,6 +5372,33 @@ function createFormStore(options) {
5318
5372
  };
5319
5373
  }
5320
5374
 
5375
+ function captureUserCallSite() {
5376
+ const raw = new Error().stack;
5377
+ if (typeof raw !== "string") return void 0;
5378
+ const lines = raw.split("\n");
5379
+ for (let i = 1; i < lines.length; i++) {
5380
+ const frame = lines[i];
5381
+ if (frame === void 0) continue;
5382
+ if (/attaform[/-]forms?/i.test(frame)) continue;
5383
+ if (/\bforms\.[A-Za-z0-9_-]+\.m?js\b/.test(frame)) continue;
5384
+ const trimmed = frame.trim();
5385
+ if (trimmed.length === 0) continue;
5386
+ return shortenSourceFrame(trimmed);
5387
+ }
5388
+ return void 0;
5389
+ }
5390
+ function shortenSourceFrame(frame) {
5391
+ const match = /(?:^|\s|\()([^\s()]+):(\d+):\d+\)?$/.exec(frame);
5392
+ if (match === null) return frame;
5393
+ const [, urlOrPath, line] = match;
5394
+ if (urlOrPath === void 0 || line === void 0) return frame;
5395
+ let path = urlOrPath;
5396
+ path = path.replace(/^[a-z]+:\/\/[^/]+\//i, "");
5397
+ path = path.replace(/^_nuxt\//, "");
5398
+ path = path.replace(/^\//, "");
5399
+ return `(${path}:${line})`;
5400
+ }
5401
+
5321
5402
  function getComputedSchema(formKey, schemaOrCallback, options) {
5322
5403
  if (typeof schemaOrCallback === "function") return schemaOrCallback(formKey, options);
5323
5404
  return schemaOrCallback;
@@ -5328,13 +5409,6 @@ function captureErrorEntries(map) {
5328
5409
  for (const [k, v] of map) out.push([k, [...v]]);
5329
5410
  return out;
5330
5411
  }
5331
- function pathsEqual(a, b) {
5332
- if (a.length !== b.length) return false;
5333
- for (let j = 0; j < a.length; j++) {
5334
- if (a[j] !== b[j]) return false;
5335
- }
5336
- return true;
5337
- }
5338
5412
  function errorFieldsEqual(av, bvi) {
5339
5413
  if (av === bvi) return true;
5340
5414
  if (av.message !== bvi.message) return false;
@@ -5434,10 +5508,6 @@ function createHistoryModule(state, config) {
5434
5508
  clear();
5435
5509
  return;
5436
5510
  }
5437
- if (meta?.crossTab === true) {
5438
- currentSnapshot.value = captureSnapshot();
5439
- return;
5440
- }
5441
5511
  const newSnap = captureSnapshot();
5442
5512
  const prevSnap = currentSnapshot.value;
5443
5513
  const formPatches = [];
@@ -5458,9 +5528,7 @@ function createHistoryModule(state, config) {
5458
5528
  suppressNext = true;
5459
5529
  state.blankPaths.clear();
5460
5530
  for (const key of snap.blankPaths) state.blankPaths.add(key);
5461
- state.applyFormReplacement(snap.form, {
5462
- persist: !state.persistOptIns.isEmpty()
5463
- });
5531
+ state.applyFormReplacement(snap.form);
5464
5532
  const schemaFlat = snap.schemaErrors.flatMap(([, errs]) => errs);
5465
5533
  const userFlat = snap.userErrors.flatMap(([, errs]) => errs);
5466
5534
  state.setAllSchemaErrors(schemaFlat);
@@ -5511,28 +5579,6 @@ function createHistoryModule(state, config) {
5511
5579
  };
5512
5580
  }
5513
5581
 
5514
- const warned = /* @__PURE__ */ new Set();
5515
- function warnOnceInsecureContext(feature) {
5516
- if (!__DEV__) return;
5517
- if (warned.has(feature)) return;
5518
- warned.add(feature);
5519
- const message = featureMessage(feature);
5520
- console.warn(`[attaform] ${message}`);
5521
- }
5522
- function featureMessage(feature) {
5523
- switch (feature) {
5524
- case "multiTab":
5525
- return "Multi-tab sync requires a secure context (HTTPS or localhost). Plain HTTP on a real hostname is interceptable by network observers, so the sync module is disabled. Serve over HTTPS in production (or develop on `localhost`) to enable cross-tab synchronisation. Use `multiTab: false` on `useForm` to silence this warning.";
5526
- case "persist:local":
5527
- return "Built-in `persist: 'local'` storage requires a secure context (HTTPS or localhost). Plain HTTP on a real hostname is MITM-interceptable, so the persistence layer is disabled. Serve over HTTPS to enable localStorage persistence, or pass a custom storage adapter to opt out of the secure-context gate.";
5528
- case "persist:session":
5529
- return "Built-in `persist: 'session'` storage requires a secure context (HTTPS or localhost). Plain HTTP on a real hostname is MITM-interceptable, so the persistence layer is disabled. Serve over HTTPS to enable sessionStorage persistence, or pass a custom storage adapter to opt out of the secure-context gate.";
5530
- }
5531
- }
5532
- function isSecureContext() {
5533
- return typeof window !== "undefined" && window.isSecureContext === true;
5534
- }
5535
-
5536
5582
  function resolveTrichotomy(input) {
5537
5583
  if (typeof input === "function") {
5538
5584
  return { kind: "async", factory: input };
@@ -5561,18 +5607,10 @@ function useAbstractForm(configuration, options) {
5561
5607
  defaultValue: DEFAULT_MAX_RECURSION_DEPTH
5562
5608
  });
5563
5609
  const resolvedSchema = getComputedSchema(key, configuration.schema, { maxRecursionDepth });
5564
- if (configuration.persist !== void 0 && configuration.key === void 0) {
5565
- throw new AnonPersistError({
5566
- cause: "no-key",
5567
- schemaFields: extractSchemaFields(resolvedSchema),
5568
- callSite: captureUserCallSite()
5569
- });
5570
- }
5571
5610
  const existing = registry.forms.get(key);
5572
5611
  if (__DEV__ && existing !== void 0) {
5573
5612
  void import('../chunks/dev-key-collision-warnings.mjs').then((m) => {
5574
5613
  void m.warnOnSchemaFingerprintMismatch(key, existing.schema, resolvedSchema);
5575
- m.warnOnPersistDivergence(key, existing, configuration.persist);
5576
5614
  });
5577
5615
  }
5578
5616
  const hadPendingHydration = registry.pendingHydration.has(key);
@@ -5600,85 +5638,6 @@ function useAbstractForm(configuration, options) {
5600
5638
  const releaseConsumer = registry.trackConsumer(key);
5601
5639
  onScopeDispose(releaseConsumer);
5602
5640
  }
5603
- const persistDisabledByAnonRule = merged.persist !== void 0 && enforceAnonPersistRule(state.formKey, registry.ssr);
5604
- if (existing === void 0 && !registry.ssr) {
5605
- if (merged.persist !== void 0 && !persistDisabledByAnonRule) {
5606
- const resolvedPersist = normalizePersistConfig(merged.persist);
5607
- const storageKind = resolvedPersist.storage;
5608
- const isBuiltinStorage = typeof storageKind === "string";
5609
- const secureContextOk = !isBuiltinStorage || isSecureContext();
5610
- if (!secureContextOk) {
5611
- const feature = storageKind === "session" ? "persist:session" : "persist:local";
5612
- warnOnceInsecureContext(feature);
5613
- void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
5614
- } else {
5615
- const persistenceBase = resolveStorageKeyBase(resolvedPersist, state.formKey);
5616
- void sweepNonConfiguredStandardStoresForOrphans(resolvedPersist.storage, persistenceBase);
5617
- const adapterPromise = getStorageAdapter(resolvedPersist.storage);
5618
- let persistDisposed = false;
5619
- state.registerCleanup(() => {
5620
- persistDisposed = true;
5621
- });
5622
- const ready = (async () => {
5623
- try {
5624
- const [{ wirePersistence }, fingerprintToken] = await Promise.all([
5625
- import('../chunks/wire-persistence.mjs'),
5626
- resolvePersistFingerprintToken(state)
5627
- ]);
5628
- if (persistDisposed) return void 0;
5629
- const persistenceModule = wirePersistence(
5630
- state,
5631
- resolvedPersist,
5632
- adapterPromise,
5633
- fingerprintToken
5634
- );
5635
- state.registerDrain(() => persistenceModule.awaitPendingWrites());
5636
- state.registerCleanup(() => persistenceModule.dispose());
5637
- return persistenceModule;
5638
- } catch {
5639
- return void 0;
5640
- }
5641
- })();
5642
- const persistenceHandle = { config: resolvedPersist, ready };
5643
- state.modules.set(PERSISTENCE_MODULE_KEY, persistenceHandle);
5644
- }
5645
- } else {
5646
- void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
5647
- }
5648
- }
5649
- if (existing === void 0 && merged.multiTab === true && configuration.key !== void 0 && !registry.ssr) {
5650
- const hasBroadcastChannel = typeof BroadcastChannel !== "undefined";
5651
- const secureContext = isSecureContext();
5652
- if (hasBroadcastChannel && secureContext) {
5653
- let formDisposed = false;
5654
- state.registerCleanup(() => {
5655
- formDisposed = true;
5656
- });
5657
- void (async () => {
5658
- try {
5659
- const [{ createMultiTabSyncModule, MULTI_TAB_SYNC_MODULE_KEY }, fingerprint] = await Promise.all([import('../chunks/multi-tab-sync.mjs'), state.schema.fingerprint()]);
5660
- if (formDisposed) return;
5661
- const channelName = `attaform:sync:${state.formKey}:${hashStableString(fingerprint)}`;
5662
- const syncModule = createMultiTabSyncModule(state, channelName, {
5663
- isSensitivePath: state.isSensitivePath,
5664
- noSyncPaths: state.noSyncPaths,
5665
- validateForm: (form) => {
5666
- const result = state.schema.validateAtPath(form, void 0, { sync: true });
5667
- if (result instanceof Promise) return;
5668
- if (!result.success) {
5669
- throw new Error("attaform multi-tab sync: post-apply schema validation failed");
5670
- }
5671
- }
5672
- });
5673
- state.modules.set(MULTI_TAB_SYNC_MODULE_KEY, syncModule);
5674
- state.registerCleanup(() => syncModule.dispose());
5675
- } catch {
5676
- }
5677
- })();
5678
- } else if (hasBroadcastChannel && !secureContext) {
5679
- warnOnceInsecureContext("multiTab");
5680
- }
5681
- }
5682
5641
  if (existing === void 0 && merged.history !== void 0) {
5683
5642
  const historyModule = createHistoryModule(state, merged.history);
5684
5643
  state.modules.set(HISTORY_MODULE_KEY, historyModule);
@@ -5719,11 +5678,8 @@ function useAbstractForm(configuration, options) {
5719
5678
  if (merged.autoAria !== void 0) {
5720
5679
  apiOptions.autoAria = merged.autoAria;
5721
5680
  }
5722
- return buildFormApi(
5723
- state,
5724
- formInstanceId,
5725
- apiOptions
5726
- );
5681
+ const api = buildFormApi(state, formInstanceId, apiOptions);
5682
+ return api;
5727
5683
  }
5728
5684
  function mergeWithDefaults(defaults, configuration) {
5729
5685
  const strict = configuration.strict ?? defaults.strict;
@@ -5735,8 +5691,6 @@ function mergeWithDefaults(defaults, configuration) {
5735
5691
  const debounceMs = configuration.debounceMs ?? defaults.debounceMs;
5736
5692
  const getDisplayState = configuration.getDisplayState ?? defaults.getDisplayState;
5737
5693
  const maxRecursionDepth = configuration.maxRecursionDepth ?? defaults.maxRecursionDepth;
5738
- const sensitiveNames = configuration.sensitiveNames ?? defaults.sensitiveNames;
5739
- const multiTab = configuration.multiTab ?? defaults.multiTab;
5740
5694
  const autoAria = configuration.autoAria ?? defaults.autoAria;
5741
5695
  return {
5742
5696
  ...configuration,
@@ -5749,8 +5703,6 @@ function mergeWithDefaults(defaults, configuration) {
5749
5703
  ...debounceMs === void 0 ? {} : { debounceMs },
5750
5704
  ...getDisplayState === void 0 ? {} : { getDisplayState },
5751
5705
  ...maxRecursionDepth === void 0 ? {} : { maxRecursionDepth },
5752
- ...sensitiveNames === void 0 ? {} : { sensitiveNames },
5753
- ...multiTab === void 0 ? {} : { multiTab },
5754
5706
  ...autoAria === void 0 ? {} : { autoAria }
5755
5707
  };
5756
5708
  }
@@ -5766,8 +5718,6 @@ function buildFreshState(key, schema, configuration, registry) {
5766
5718
  if (pending === void 0) {
5767
5719
  initialBlankPaths = walked.paths;
5768
5720
  }
5769
- const resolvedSensitiveNames = configuration.sensitiveNames;
5770
- const resolvedIsSensitivePath = resolvedSensitiveNames === void 0 ? void 0 : createIsSensitivePath(resolvedSensitiveNames);
5771
5721
  const createOptions = {
5772
5722
  formKey: key,
5773
5723
  schema,
@@ -5795,8 +5745,7 @@ function buildFreshState(key, schema, configuration, registry) {
5795
5745
  ...configuration.rememberVariants !== void 0 ? { rememberVariants: configuration.rememberVariants } : {},
5796
5746
  ...configuration.coerce !== void 0 ? { coerce: configuration.coerce } : {},
5797
5747
  ...configuration.getDisplayState !== void 0 ? { getDisplayState: configuration.getDisplayState } : {},
5798
- ...initialBlankPaths !== void 0 ? { initialBlankPaths } : {},
5799
- ...resolvedIsSensitivePath !== void 0 ? { isSensitivePath: resolvedIsSensitivePath } : {}
5748
+ ...initialBlankPaths !== void 0 ? { initialBlankPaths } : {}
5800
5749
  };
5801
5750
  const state = createFormStore(createOptions);
5802
5751
  registry.forms.set(
@@ -5835,34 +5784,6 @@ function resolveFormKey(key) {
5835
5784
  }
5836
5785
  return `${ANONYMOUS_FORM_KEY_PREFIX}${anonCounter++}`;
5837
5786
  }
5838
- async function resolvePersistFingerprintToken(state) {
5839
- try {
5840
- return hashStableString(await state.schema.fingerprint());
5841
- } catch (err) {
5842
- if (__DEV__) {
5843
- console.warn(
5844
- `[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.`
5845
- );
5846
- }
5847
- return "unfingerprinted";
5848
- }
5849
- }
5850
- const warnedAnonPersistKeys = /* @__PURE__ */ new Set();
5851
- function enforceAnonPersistRule(formKey, ssr) {
5852
- if (!formKey.startsWith(ANONYMOUS_FORM_KEY_PREFIX)) return false;
5853
- if (__DEV__)
5854
- throw new AnonPersistError({
5855
- cause: "no-key",
5856
- callSite: captureUserCallSite()
5857
- });
5858
- if (!ssr && !warnedAnonPersistKeys.has(formKey)) {
5859
- warnedAnonPersistKeys.add(formKey);
5860
- console.warn(
5861
- "[attaform] persist: ignored \u2014 anonymous useForm() can't safely persist (key drift + cross-form collision risk).\n Persistence is disabled for this form; the app keeps working.\n Fix: useForm({ schema, key: 'login', persist: '...' })"
5862
- );
5863
- }
5864
- return true;
5865
- }
5866
5787
 
5867
5788
  let injectedInstanceCounter = 0;
5868
5789
  function injectForm(input) {
@@ -7089,5 +7010,5 @@ function warnIfAmbientWizardProviderHadDuplicates() {
7089
7010
  }
7090
7011
  }
7091
7012
 
7092
- export { AttaformErrorCode as A, deleteAtPath as B, safeOwnRead as C, DEFAULT_TIMINGS as D, humanize as E, PERSISTENCE_MODULE_KEY as P, injectWizard as a, isUnset as b, useRegister as c, useWizard as d, isPlainRecord as e, safeAssign as f, diffAndApply as g, slimKindOf as h, injectForm as i, applyPatchesForward as j, normalizeNumericOption as k, lazy as l, defaultCoercionRules as m, normalizePersistConfig as n, defaultDisplayState as o, defineCoercion as p, makeDefaultDisplayState as q, useAbstractForm as r, structuralSnapshot as s, getAtPath as t, unset as u, setAtPath as v, resolveStorageKeyBase as w, DEFAULT_PERSISTENCE_DEBOUNCE_MS as x, cleanupOrphanKeys as y, mergeSparseHydration as z };
7093
- //# sourceMappingURL=attaform.CTheKoTc.mjs.map
7013
+ export { AttaformErrorCode as A, DEFAULT_TIMINGS as D, injectWizard as a, isUnset as b, useRegister as c, useWizard as d, defaultCoercionRules as e, defaultDisplayState as f, defineCoercion as g, useAbstractForm as h, injectForm as i, isPlainRecord as j, safeAssign as k, lazy as l, makeDefaultDisplayState as m, normalizeNumericOption as n, slimKindOf as o, humanize as p, getAtPath as q, setAtPath as r, safeOwnRead as s, unset as u };
7014
+ //# sourceMappingURL=attaform.CtJOd7ox.mjs.map