attaform 0.22.0 → 0.24.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 (114) hide show
  1. package/README.md +8 -11
  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 -151
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +7 -168
  15. package/dist/index.d.mts +7 -168
  16. package/dist/index.d.ts +7 -168
  17. package/dist/index.mjs +4 -150
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/nuxt.d.cts +1 -1
  20. package/dist/nuxt.d.mts +1 -1
  21. package/dist/nuxt.d.ts +1 -1
  22. package/dist/runtime/components/AttaformDevtoolsPanel.vue +5 -13
  23. package/dist/runtime/plugins/attaform.cjs +2 -2
  24. package/dist/runtime/plugins/attaform.mjs +2 -2
  25. package/dist/shared/{attaform.DgCfLqay.mjs → attaform.BJ_W7q3U.mjs} +8 -6
  26. package/dist/shared/attaform.BJ_W7q3U.mjs.map +1 -0
  27. package/dist/shared/{attaform.aekT7mMx.d.cts → attaform.BNmkKz0q.d.mts} +38 -6
  28. package/dist/shared/{attaform.Q3eAD2wD.cjs → attaform.BV_HyaMO.cjs} +6 -4
  29. package/dist/shared/attaform.BV_HyaMO.cjs.map +1 -0
  30. package/dist/shared/{attaform.AyujQoHp.cjs → attaform.BnUXV01g.cjs} +4 -4
  31. package/dist/shared/attaform.BnUXV01g.cjs.map +1 -0
  32. package/dist/shared/{attaform.DNuiFCXG.mjs → attaform.C-dAB90u.mjs} +4 -4
  33. package/dist/shared/attaform.C-dAB90u.mjs.map +1 -0
  34. package/dist/shared/{attaform.CjMcwV7W.cjs → attaform.C42wL7EJ.cjs} +272 -776
  35. package/dist/shared/attaform.C42wL7EJ.cjs.map +1 -0
  36. package/dist/shared/{attaform.D4XYaasQ.d.ts → attaform.C6eE50re.d.ts} +27 -87
  37. package/dist/shared/{attaform.6xE0Lcfd.mjs → attaform.CAWKNCzc.mjs} +2 -2
  38. package/dist/shared/{attaform.6xE0Lcfd.mjs.map → attaform.CAWKNCzc.mjs.map} +1 -1
  39. package/dist/shared/{attaform.DkA5J8NW.d.cts → attaform.CO0e7YVY.d.cts} +1 -46
  40. package/dist/shared/{attaform.DkA5J8NW.d.ts → attaform.CO0e7YVY.d.mts} +1 -46
  41. package/dist/shared/{attaform.DkA5J8NW.d.mts → attaform.CO0e7YVY.d.ts} +1 -46
  42. package/dist/shared/{attaform.CsB-iKbU.mjs → attaform.CuBdtfbe.mjs} +274 -767
  43. package/dist/shared/attaform.CuBdtfbe.mjs.map +1 -0
  44. package/dist/shared/{attaform.FN0vaQAg.d.mts → attaform.CwFZGv5-.d.ts} +38 -6
  45. package/dist/shared/{attaform.BGwNZ9GV.d.cts → attaform.DdUYEhkV.d.cts} +27 -87
  46. package/dist/shared/attaform.DdjDqTah.d.cts +56 -0
  47. package/dist/shared/attaform.DdjDqTah.d.mts +56 -0
  48. package/dist/shared/attaform.DdjDqTah.d.ts +56 -0
  49. package/dist/shared/{attaform.BKFwekY2.mjs → attaform.Df-s8j1X.mjs} +3 -289
  50. package/dist/shared/attaform.Df-s8j1X.mjs.map +1 -0
  51. package/dist/shared/{attaform.CCCeEPwa.d.mts → attaform.DiWNbKWa.d.mts} +27 -87
  52. package/dist/shared/{attaform.01iKS_lz.cjs → attaform.DwkU0oY9.cjs} +2 -298
  53. package/dist/shared/attaform.DwkU0oY9.cjs.map +1 -0
  54. package/dist/shared/{attaform.DvUH4a3o.d.ts → attaform.GJbSmwLB.d.cts} +224 -824
  55. package/dist/shared/{attaform.DvUH4a3o.d.cts → attaform.GJbSmwLB.d.mts} +224 -824
  56. package/dist/shared/{attaform.DvUH4a3o.d.mts → attaform.GJbSmwLB.d.ts} +224 -824
  57. package/dist/shared/{attaform.DUMWQefY.d.ts → attaform.K-3glmiT.d.cts} +38 -6
  58. package/dist/shared/{attaform.C-RtnCJM.cjs → attaform.Z1qTwOYE.cjs} +8 -6
  59. package/dist/shared/attaform.Z1qTwOYE.cjs.map +1 -0
  60. package/dist/shared/{attaform.CRzpFCjV.cjs → attaform.nycEksJn.cjs} +2 -2
  61. package/dist/shared/{attaform.CRzpFCjV.cjs.map → attaform.nycEksJn.cjs.map} +1 -1
  62. package/dist/shared/{attaform.DCjgGir_.mjs → attaform.o95Kjd3U.mjs} +6 -4
  63. package/dist/shared/attaform.o95Kjd3U.mjs.map +1 -0
  64. package/dist/zod-v3.cjs +2 -2
  65. package/dist/zod-v3.d.cts +12 -11
  66. package/dist/zod-v3.d.mts +12 -11
  67. package/dist/zod-v3.d.ts +12 -11
  68. package/dist/zod-v3.mjs +2 -2
  69. package/dist/zod-v4.cjs +2 -2
  70. package/dist/zod-v4.d.cts +7 -6
  71. package/dist/zod-v4.d.mts +7 -6
  72. package/dist/zod-v4.d.ts +7 -6
  73. package/dist/zod-v4.mjs +2 -2
  74. package/dist/zod.cjs +5 -5
  75. package/dist/zod.cjs.map +1 -1
  76. package/dist/zod.d.cts +21 -52
  77. package/dist/zod.d.mts +21 -52
  78. package/dist/zod.d.ts +21 -52
  79. package/dist/zod.mjs +5 -5
  80. package/dist/zod.mjs.map +1 -1
  81. package/package.json +1 -1
  82. package/dist/chunks/indexeddb.cjs +0 -119
  83. package/dist/chunks/indexeddb.cjs.map +0 -1
  84. package/dist/chunks/indexeddb.mjs +0 -117
  85. package/dist/chunks/indexeddb.mjs.map +0 -1
  86. package/dist/chunks/local-storage.cjs +0 -58
  87. package/dist/chunks/local-storage.cjs.map +0 -1
  88. package/dist/chunks/local-storage.mjs +0 -56
  89. package/dist/chunks/local-storage.mjs.map +0 -1
  90. package/dist/chunks/multi-tab-sync.cjs +0 -367
  91. package/dist/chunks/multi-tab-sync.cjs.map +0 -1
  92. package/dist/chunks/multi-tab-sync.mjs +0 -364
  93. package/dist/chunks/multi-tab-sync.mjs.map +0 -1
  94. package/dist/chunks/session-storage.cjs +0 -58
  95. package/dist/chunks/session-storage.cjs.map +0 -1
  96. package/dist/chunks/session-storage.mjs +0 -56
  97. package/dist/chunks/session-storage.mjs.map +0 -1
  98. package/dist/chunks/wire-persistence.cjs +0 -396
  99. package/dist/chunks/wire-persistence.cjs.map +0 -1
  100. package/dist/chunks/wire-persistence.mjs +0 -394
  101. package/dist/chunks/wire-persistence.mjs.map +0 -1
  102. package/dist/shared/attaform.01iKS_lz.cjs.map +0 -1
  103. package/dist/shared/attaform.AyujQoHp.cjs.map +0 -1
  104. package/dist/shared/attaform.BKFwekY2.mjs.map +0 -1
  105. package/dist/shared/attaform.C-RtnCJM.cjs.map +0 -1
  106. package/dist/shared/attaform.CjMcwV7W.cjs.map +0 -1
  107. package/dist/shared/attaform.CsB-iKbU.mjs.map +0 -1
  108. package/dist/shared/attaform.DCjgGir_.mjs.map +0 -1
  109. package/dist/shared/attaform.DNuiFCXG.mjs.map +0 -1
  110. package/dist/shared/attaform.DgCfLqay.mjs.map +0 -1
  111. package/dist/shared/attaform.Q3eAD2wD.cjs.map +0 -1
  112. package/dist/shared/attaform.nf83TIR5.d.cts +0 -35
  113. package/dist/shared/attaform.nf83TIR5.d.mts +0 -35
  114. package/dist/shared/attaform.nf83TIR5.d.ts +0 -35
@@ -1,5 +1,5 @@
1
- import { computed, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, readonly, toRaw, reactive, isRef, toValue, watch, markRaw, triggerRef, shallowRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, onBeforeMount, onBeforeUpdate, onMounted, effectScope, nextTick } from 'vue';
2
- import { q as pathsEqual, l as isPathPrefix, _ as __DEV__, a as canonicalizePath, s as segmentsForPathKey, F as FORM_ERRORS_PATH_KEY, r as keyForSegments, S as SubmitErrorHandlerError, t as toError, A as AnonPersistError, w as INTERACTIVE_TAG_NAMES, x as getOrAssignElementId, R as ROOT_PATH, y as allowSensitivePersist, z as FORM_ERRORS_PATH, e as ROOT_PATH_KEY, B as segmentsToDotted, C as coerceToPathKey, E as isSensitivePath, G as createPersistOptInRegistry, d as InvalidUseFormConfigError, H as ensureAttaformInstalled, u as useRegistry, J as kFormContext, K as kFormInstanceId, h as ReservedFormKeyError, L as createIsSensitivePath, M as REGISTER_OWNER_MARKER, V as V_REGISTER_MARKER, k as kAttaformWizardActiveStepResolver, N as kAttaformAncestorWizard } from './attaform.BKFwekY2.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__, f as canonicalizePath, s as segmentsForPathKey, q as keyForSegments, b as ROOT_PATH_KEY, S as SubmitErrorHandlerError, t as toError, r as INTERACTIVE_TAG_NAMES, R as ROOT_PATH, w as coerceToPathKey, a as InvalidUseFormConfigError, x as ensureAttaformInstalled, u as useRegistry, y as kFormContext, z as kFormInstanceId, d as ReservedFormKeyError, B as REGISTER_OWNER_MARKER, V as V_REGISTER_MARKER, m as kAttaformWizardActiveStepResolver, C as kAttaformAncestorWizard } from './attaform.Df-s8j1X.mjs';
3
3
 
4
4
  function safeAssign(target, key, value) {
5
5
  if (key === "__proto__") {
@@ -540,6 +540,58 @@ function structuralSnapshot(value) {
540
540
  return out;
541
541
  }
542
542
 
543
+ const AttaformErrorCode = {
544
+ /** A required field is in the blank set — user hasn't supplied a value. */
545
+ NoValueSupplied: "atta:no-value-supplied",
546
+ /** The schema adapter's `validateAtPath` threw synchronously. */
547
+ AdapterThrew: "atta:adapter-threw",
548
+ /**
549
+ * User code inside a `z.preprocess`, `.refine`, or `.transform`
550
+ * threw (sync or async). The adapter caught the throw and surfaced
551
+ * it as a `ValidationError` at the field path so the form's normal
552
+ * error pipeline handles it instead of leaking as an unhandled
553
+ * rejection or routing through `submitError`.
554
+ */
555
+ ValidatorThrew: "atta:validator-threw",
556
+ /**
557
+ * A function-form `defaultValues` factory threw or its promise
558
+ * rejected. The runtime captures the raw error on `form.hydrateError`
559
+ * and ALSO surfaces a form-level `ValidationError` (path `[]`) so
560
+ * the standard error pipeline carries the signal. Critical for the
561
+ * SSR round-trip: `hydrateError` itself does not ride the wire
562
+ * payload, but `schemaErrors` does, so the client sees the failure
563
+ * after rehydration without an extra channel.
564
+ */
565
+ HydrationFailed: "atta:hydration-failed",
566
+ /** The supplied path didn't resolve to any node in the schema. */
567
+ PathNotFound: "atta:path-not-found",
568
+ /**
569
+ * A walked form's `activate()` (async `defaultValues` factory) threw
570
+ * during `wizard.handleSubmit`'s path walk. Surfaced as a synthetic
571
+ * `ValidationError` at the form-level path (`[]`) so the wizard's
572
+ * aggregate error pipeline can carry the failure alongside ordinary
573
+ * validation errors. The raw factory error remains on
574
+ * `form.hydrateError` for retry UX.
575
+ */
576
+ ActivationFailed: "atta:activation-failed",
577
+ /**
578
+ * Default code stamped on a manual error set through `form.setErrors`
579
+ * when the caller omits an explicit `code`. The `setErrors` input is
580
+ * lenient (`code` optional), so this is the fallback that keeps every
581
+ * produced `ValidationError` carrying a stable, branchable identifier.
582
+ * Override it per error by passing your own `code` (`api:…`, `auth:…`).
583
+ */
584
+ UserError: "atta:user-error"
585
+ };
586
+ function makeBlankRequiredError(segments, formKey) {
587
+ return {
588
+ message: "No value supplied",
589
+ path: [...segments],
590
+ formKey,
591
+ code: AttaformErrorCode.NoValueSupplied
592
+ };
593
+ }
594
+
543
595
  function isGateOpen(field, formMeta) {
544
596
  return formMeta.submissionAttempts > 0 || field.blurredAfterInteraction === true;
545
597
  }
@@ -595,54 +647,8 @@ function resolveGetDisplayState(config) {
595
647
  return config ?? defaultDisplayState;
596
648
  }
597
649
 
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
-
642
650
  const DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS = 0;
643
- const DEFAULT_PERSISTENCE_DEBOUNCE_MS = 300;
644
651
  const DEFAULT_HISTORY_MAX_SNAPSHOTS = 128;
645
- const PERSISTENCE_KEY_PREFIX = "attaform:";
646
652
  const RESERVED_KEY_PREFIX = "__atta:";
647
653
  const ANONYMOUS_FORM_KEY_PREFIX = `${RESERVED_KEY_PREFIX}anon:`;
648
654
  const ANONYMOUS_WIZARD_KEY_PREFIX = `${RESERVED_KEY_PREFIX}anon-wizard:`;
@@ -1033,7 +1039,7 @@ function aggregateErrorsAt(state, prefix) {
1033
1039
  const segs = segmentsForPathKey(pathKey);
1034
1040
  if (segs === null) continue;
1035
1041
  if (!isPathPrefix(prefix, segs)) continue;
1036
- if (pathKey === FORM_ERRORS_PATH_KEY) ; else if (segs.length > 0 && !hasAtPath(formValue, segs)) continue;
1042
+ if (segs.length > 0 && !hasAtPath(formValue, segs)) continue;
1037
1043
  const ordinal = state.ensurePathOrdinal(pathKey);
1038
1044
  const existing = buckets.get(ordinal);
1039
1045
  if (existing === void 0) buckets.set(ordinal, [...list]);
@@ -1152,7 +1158,12 @@ function buildSurfaceProxy(opts) {
1152
1158
  const proxy = new Proxy(target, {
1153
1159
  apply(_, __, args) {
1154
1160
  const arg = args[0];
1155
- if (arg === void 0) return opts.resolveCallTarget(segments);
1161
+ if (arg === void 0) {
1162
+ if (segments.length === 0 && opts.resolveRootCall !== void 0) {
1163
+ return opts.resolveRootCall();
1164
+ }
1165
+ return opts.resolveCallTarget(segments);
1166
+ }
1156
1167
  const { segments: argSegs } = canonicalizePath(arg);
1157
1168
  return opts.resolveCallTarget(argSegs);
1158
1169
  },
@@ -1363,8 +1374,7 @@ function buildErrorsProxy(state) {
1363
1374
  return merged;
1364
1375
  }
1365
1376
  const { key } = canonicalizePath(path);
1366
- const isFormLevel = key === FORM_ERRORS_PATH_KEY;
1367
- const active = isFormLevel || hasAtPath(state.form.value, path);
1377
+ const active = hasAtPath(state.form.value, path);
1368
1378
  collectAtKey(key, active, merged);
1369
1379
  return merged;
1370
1380
  },
@@ -1372,37 +1382,42 @@ function buildErrorsProxy(state) {
1372
1382
  // undefined) IS the terminal.
1373
1383
  materializeContainer: (segments) => materializeErrors(state, segments),
1374
1384
  // Any path ending in `''` is a meaningful terminal at the proxy
1375
- // layer: at root it's the form-level bucket; at depth >= 1 it's
1376
- // the container-self sentinel that surfaces cross-field refines
1377
- // and container-targeted marks. `resolveLeaf` translates `[...,
1378
- // '']` lookups to the parent container path before querying the
1379
- // stores. When a schema legitimately owns a `''` field, the
1380
- // literal leaf and any container-self errors share the slot
1381
- // (errors concatenate) — vanishingly rare, accepted as the
1382
- // ergonomic cost of one unified sentinel convention at every
1383
- // depth.
1385
+ // layer. A bare `['']` is the literal root `''` field; at depth
1386
+ // >= 1 a trailing `''` is the container-self sentinel that surfaces
1387
+ // cross-field refines and container-targeted marks (`resolveLeaf`
1388
+ // translates `[..., '']` to the parent container path before
1389
+ // querying the stores). When a schema legitimately owns a `''`
1390
+ // field, the literal leaf and any container-self errors share the
1391
+ // slot (errors concatenate) — vanishingly rare, accepted as the
1392
+ // ergonomic cost of one unified sentinel convention at depth >= 1.
1384
1393
  isTerminalAt: (segs) => segs.length >= 1 && segs[segs.length - 1] === "",
1385
1394
  // Call-form aggregates: `form.errors(path)` returns a single
1386
- // `ValidationError[]` for any depth (leaf or container) same
1395
+ // `ValidationError[]` for any depth (leaf or container) via the
1387
1396
  // shared `aggregateErrorsAt` helper that `form.meta.errors` and
1388
- // `form.fields(path).errors` use, so the three surfaces never
1389
- // drift. Empty results return `undefined`, matching the leaf
1390
- // proxy's pre-existing semantic (`form.errors.email === undefined`
1391
- // when valid) so consumer code that branches on truthiness keeps
1392
- // working the call-form just extends that semantic to
1393
- // containers and dynamic paths.
1394
- resolveCallTarget: (path) => aggregateErrorsAt(state, path),
1397
+ // `form.fields(path).errors` also use, so the surfaces never drift.
1398
+ //
1399
+ // The root is the one carve-out. An EXPLICIT root path
1400
+ // (`errors([])`) returns ONLY the global `[]` bucket (root
1401
+ // `.refine()`, hydration failures, `setErrors`) via
1402
+ // `getErrorsForPath`, giving consumers a dedicated channel for
1403
+ // global messages undiluted by field errors. The no-arg `errors()`
1404
+ // instead resolves the FULL aggregate through `resolveRootCall`
1405
+ // below (identical to `meta.errors`).
1406
+ resolveCallTarget: (path) => path.length === 0 ? state.getErrorsForPath([]) : aggregateErrorsAt(state, path),
1407
+ // No-arg `errors()` = the whole-form aggregate, matching
1408
+ // `meta.errors`. Distinct from `errors([])` (global bucket only);
1409
+ // see `resolveCallTarget`.
1410
+ resolveRootCall: () => aggregateErrorsAt(state, []),
1395
1411
  // Enumeration unions the live form-data keys at this path with the
1396
1412
  // first-child segments drawn from every error store. Without the
1397
1413
  // union, `Object.keys(form.errors)` / `{...form.errors}` /
1398
- // `v-for="(errs, k) in form.errors"` silently dropped two
1399
- // important error classes that the dot / call / JSON.stringify
1400
- // surfaces already exposed:
1401
- //
1402
- // - **Form-level** errors at the synthetic `['']` path (set via
1403
- // `setFormErrors` or root cross-field refines).
1404
- // - **Server-only** errors at a key the schema doesn't know
1405
- // about (`['ghost']`, `['address', 'ghost']`).
1414
+ // `v-for="(errs, k) in form.errors"` would silently drop
1415
+ // **server-only** errors at a key the schema doesn't know about
1416
+ // (`['ghost']`, `['address', 'ghost']`) that the dot / call /
1417
+ // JSON.stringify surfaces already expose. Global errors at the root
1418
+ // `[]` are NOT a child key and never enumerate here (read them via
1419
+ // `errors([])` / `meta.errors`); a literal `''` field enumerates
1420
+ // under the key `''` like any other field.
1406
1421
  //
1407
1422
  // The union closes that gap so `ownKeys` agrees with the rest of
1408
1423
  // the surface. Active-path filter mirrors `resolveLeaf`:
@@ -1424,10 +1439,6 @@ function errorAwareContainerKeys(state, segments) {
1424
1439
  const walk = (store, applyActivePathFilter) => {
1425
1440
  for (const [pathKey, errors] of store) {
1426
1441
  if (errors.length === 0) continue;
1427
- if (pathKey === FORM_ERRORS_PATH_KEY) {
1428
- if (segments.length === 0) keys.add("");
1429
- continue;
1430
- }
1431
1442
  const decoded = segmentsForPathKey(pathKey);
1432
1443
  if (decoded === null) continue;
1433
1444
  if (decoded.length <= segments.length) continue;
@@ -1450,31 +1461,25 @@ function materializeErrors(state, containerSegments) {
1450
1461
  if (errors.length === 0) continue;
1451
1462
  const fullPath = segmentsForPathKey(pathKey);
1452
1463
  if (fullPath === null) continue;
1453
- const isSyntheticFormLevel = fullPath.length === 1 && fullPath[0] === "";
1454
- if (!isSyntheticFormLevel) {
1455
- if (fullPath.length < containerSegments.length) continue;
1456
- for (let i = 0; i < containerSegments.length; i++) {
1457
- if (fullPath[i] !== containerSegments[i]) continue entries;
1458
- }
1459
- } else if (containerSegments.length !== 0) {
1464
+ if (fullPath.length === 0) {
1465
+ if (containerSegments.length === 0) placeAt(tree, [ROOT_PATH_KEY], errors);
1460
1466
  continue;
1461
1467
  }
1462
- if (applyActivePathFilter && !isSyntheticFormLevel && !hasAtPath(state.form.value, fullPath))
1463
- continue;
1468
+ if (fullPath.length < containerSegments.length) continue;
1469
+ for (let i = 0; i < containerSegments.length; i++) {
1470
+ if (fullPath[i] !== containerSegments[i]) continue entries;
1471
+ }
1472
+ if (applyActivePathFilter && !hasAtPath(state.form.value, fullPath)) continue;
1473
+ const relativePath = fullPath.slice(containerSegments.length);
1464
1474
  let placePath;
1465
- if (isSyntheticFormLevel) {
1475
+ if (relativePath.length === 0) {
1466
1476
  placePath = [""];
1477
+ } else if (state.schema.isLeafAtPath(fullPath)) {
1478
+ placePath = relativePath;
1479
+ } else if (state.schema.getSlimPrimitiveTypesAtPath(fullPath).size > 0) {
1480
+ placePath = [...relativePath, ""];
1467
1481
  } else {
1468
- const relativePath = fullPath.slice(containerSegments.length);
1469
- if (relativePath.length === 0) {
1470
- placePath = [""];
1471
- } else if (state.schema.isLeafAtPath(fullPath)) {
1472
- placePath = relativePath;
1473
- } else if (state.schema.getSlimPrimitiveTypesAtPath(fullPath).size > 0) {
1474
- placePath = [...relativePath, ""];
1475
- } else {
1476
- placePath = relativePath;
1477
- }
1482
+ placePath = relativePath;
1478
1483
  }
1479
1484
  placeAt(tree, placePath, errors);
1480
1485
  }
@@ -1513,9 +1518,8 @@ function buildFieldArrayApi(state) {
1513
1518
  return Array.isArray(current) ? current.slice() : [];
1514
1519
  }
1515
1520
  function writeArray(path, next, arrayOp) {
1516
- const { segments, key } = canonicalizePath(path);
1521
+ const { segments } = canonicalizePath(path);
1517
1522
  const meta = {
1518
- persist: state.persistOptIns.hasAnyOptInForPath(key),
1519
1523
  ...arrayOp !== void 0 ? { arrayOp } : {}
1520
1524
  };
1521
1525
  return state.setValueAtPath(segments, next, meta);
@@ -1737,121 +1741,6 @@ function walk$1(value, basePath, schema, snapshotFieldStateAt) {
1737
1741
  return result;
1738
1742
  }
1739
1743
 
1740
- const PERSISTENCE_MODULE_KEY = "persistence";
1741
- async function getStorageAdapter(storage) {
1742
- if (typeof storage === "object") return storage;
1743
- switch (storage) {
1744
- case "local": {
1745
- const { createLocalStorageAdapter } = await import('../chunks/local-storage.mjs');
1746
- return createLocalStorageAdapter();
1747
- }
1748
- case "session": {
1749
- const { createSessionStorageAdapter } = await import('../chunks/session-storage.mjs');
1750
- return createSessionStorageAdapter();
1751
- }
1752
- case "indexeddb": {
1753
- const { createIndexedDbAdapter } = await import('../chunks/indexeddb.mjs');
1754
- return createIndexedDbAdapter();
1755
- }
1756
- }
1757
- }
1758
- function resolveStorageKeyBase(config, formKey) {
1759
- return config.key ?? `${PERSISTENCE_KEY_PREFIX}${formKey}`;
1760
- }
1761
- async function removeMatchingKeys(adapter, base, keepKey) {
1762
- let keys;
1763
- try {
1764
- keys = await adapter.listKeys(base);
1765
- } catch {
1766
- return;
1767
- }
1768
- for (const key of keys) {
1769
- if (key === keepKey) continue;
1770
- if (key === base || key.startsWith(`${base}:`)) {
1771
- void adapter.removeItem(key).catch(() => void 0);
1772
- }
1773
- }
1774
- }
1775
- async function cleanupOrphanKeys(adapter, base, currentKey) {
1776
- await removeMatchingKeys(adapter, base, currentKey);
1777
- }
1778
- const STANDARD_STORAGE_KINDS = ["local", "session", "indexeddb"];
1779
- function normalizePersistConfig(input) {
1780
- if (typeof input === "string") return { storage: input };
1781
- if ("storage" in input) return input;
1782
- return { storage: input };
1783
- }
1784
- async function sweepAllOrphansAcrossStandardStores(base) {
1785
- for (const kind of STANDARD_STORAGE_KINDS) {
1786
- try {
1787
- const adapter = await getStorageAdapter(kind);
1788
- await removeMatchingKeys(adapter, base);
1789
- } catch {
1790
- }
1791
- }
1792
- }
1793
- async function sweepNonConfiguredStandardStoresForOrphans(configured, base) {
1794
- const configuredKind = typeof configured === "string" ? configured : null;
1795
- for (const kind of STANDARD_STORAGE_KINDS) {
1796
- if (kind === configuredKind) continue;
1797
- try {
1798
- const adapter = await getStorageAdapter(kind);
1799
- await removeMatchingKeys(adapter, base);
1800
- } catch {
1801
- }
1802
- }
1803
- }
1804
- function mergeSparseHydration(schemaDefaults, sparse, schema) {
1805
- return mergeDeep(schemaDefaults, sparse, [], schema);
1806
- }
1807
- function mergeDeep(target, source, path, schema) {
1808
- if (source === void 0) return target;
1809
- if (source === null || typeof source !== "object") return source;
1810
- if (Array.isArray(source)) return source;
1811
- if (!isPlainRecord(source)) return source;
1812
- if (schema !== void 0) {
1813
- const du = schema.getUnionDiscriminatorAtPath(path);
1814
- if (du !== void 0) return mergeDuAwareKeys(source, path, schema, du);
1815
- }
1816
- return mergeObjectKeys(target, source, path, schema);
1817
- }
1818
- function mergeDuAwareKeys(source, path, schema, du) {
1819
- const sourceDisc = source[du.discriminatorKey];
1820
- if (sourceDisc !== void 0 && !du.isVariantSelected(sourceDisc)) {
1821
- return { [du.discriminatorKey]: sourceDisc };
1822
- }
1823
- if (sourceDisc !== void 0) {
1824
- const variantDefault = du.getVariantDefault(sourceDisc);
1825
- if (isPlainRecord(variantDefault)) {
1826
- return mergeVariantKeys(source, variantDefault, path, schema, du);
1827
- }
1828
- }
1829
- return {};
1830
- }
1831
- function mergeVariantKeys(source, variantDefault, path, schema, du) {
1832
- const out = { ...variantDefault };
1833
- for (const key of Object.keys(source)) {
1834
- if (!safeOwnHas(variantDefault, key) && key !== du.discriminatorKey) continue;
1835
- safeAssign(
1836
- out,
1837
- key,
1838
- mergeDeep(safeOwnRead(out, key), safeOwnRead(source, key), [...path, key], schema)
1839
- );
1840
- }
1841
- return out;
1842
- }
1843
- function mergeObjectKeys(target, source, path, schema) {
1844
- const out = isPlainRecord(target) ? { ...target } : {};
1845
- for (const key of Object.keys(source)) {
1846
- safeAssign(
1847
- out,
1848
- key,
1849
- mergeDeep(safeOwnRead(out, key), safeOwnRead(source, key), [...path, key], schema)
1850
- );
1851
- }
1852
- return out;
1853
- }
1854
-
1855
1744
  const warnedNoScopeStores = __DEV__ ? /* @__PURE__ */ new WeakSet() : null;
1856
1745
  function buildProcessForm(state, formInstanceId, options = {}) {
1857
1746
  const invalidPolicy = options.onInvalidSubmit ?? "focus-first-error";
@@ -2109,44 +1998,6 @@ function applyInvalidSubmitPolicy(state, formInstanceId, policy) {
2109
1998
  target.element.focus({ preventScroll: true });
2110
1999
  }
2111
2000
 
2112
- function captureUserCallSite() {
2113
- const raw = new Error().stack;
2114
- if (typeof raw !== "string") return void 0;
2115
- const lines = raw.split("\n");
2116
- for (let i = 1; i < lines.length; i++) {
2117
- const frame = lines[i];
2118
- if (frame === void 0) continue;
2119
- if (/attaform[/-]forms?/i.test(frame)) continue;
2120
- if (/\bforms\.[A-Za-z0-9_-]+\.m?js\b/.test(frame)) continue;
2121
- const trimmed = frame.trim();
2122
- if (trimmed.length === 0) continue;
2123
- return shortenSourceFrame(trimmed);
2124
- }
2125
- return void 0;
2126
- }
2127
- function shortenSourceFrame(frame) {
2128
- const match = /(?:^|\s|\()([^\s()]+):(\d+):\d+\)?$/.exec(frame);
2129
- if (match === null) return frame;
2130
- const [, urlOrPath, line] = match;
2131
- if (urlOrPath === void 0 || line === void 0) return frame;
2132
- let path = urlOrPath;
2133
- path = path.replace(/^[a-z]+:\/\/[^/]+\//i, "");
2134
- path = path.replace(/^_nuxt\//, "");
2135
- path = path.replace(/^\//, "");
2136
- return `(${path}:${line})`;
2137
- }
2138
-
2139
- function extractSchemaFields(schema) {
2140
- try {
2141
- const root = schema.getDefaultAtPath([]);
2142
- if (root !== null && typeof root === "object" && !Array.isArray(root)) {
2143
- return Object.keys(root);
2144
- }
2145
- } catch {
2146
- }
2147
- return [];
2148
- }
2149
-
2150
2001
  const warnedRejections = __DEV__ ? /* @__PURE__ */ new WeakMap() : null;
2151
2002
  function shouldWarnOnce$1(store, key) {
2152
2003
  if (warnedRejections === null) return false;
@@ -2476,16 +2327,7 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2476
2327
  const slimTypes = state.schema.getSlimPrimitiveTypesAtPath(segments);
2477
2328
  const acceptsUndefined = slimTypes.has("undefined");
2478
2329
  const acceptsString = slimTypes.has("string");
2479
- const persist = options?.persist === true;
2480
- const acknowledgeSensitive = options?.acknowledgeSensitive === true;
2481
- const multiTab = options?.multiTab !== false;
2482
2330
  const transforms = options?.transforms ?? EMPTY_TRANSFORMS;
2483
- const markNoSync = !multiTab ? () => {
2484
- state.incrementNoSyncOptOut(pathKey);
2485
- } : void 0;
2486
- const unmarkNoSync = !multiTab ? () => {
2487
- state.decrementNoSyncOptOut(pathKey);
2488
- } : void 0;
2489
2331
  const coerce = buildCoerceFn(
2490
2332
  state.schema,
2491
2333
  segments,
@@ -2496,18 +2338,10 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2496
2338
  segments,
2497
2339
  coerceIndex
2498
2340
  );
2499
- if (persist && !state.ssr && !state.modules.has(PERSISTENCE_MODULE_KEY)) {
2500
- throw new AnonPersistError({
2501
- cause: "register-without-config",
2502
- schemaFields: extractSchemaFields(state.schema),
2503
- callSite: captureUserCallSite()
2504
- });
2505
- }
2506
2341
  const { aria } = computeFieldIdentity(formInstanceId, state.formKey, pathKey);
2507
2342
  const isRequired = state.schema.isRequiredAtPath(segments);
2508
2343
  const ariaEnabled = options?.autoAria ?? formAutoAria;
2509
2344
  const ariaDisplayState = getDisplayStateAt !== void 0 ? computed(() => getDisplayStateAt(segments)) : void 0;
2510
- let boundElement = null;
2511
2345
  const internalRv = {
2512
2346
  innerRef,
2513
2347
  displayValue,
@@ -2517,8 +2351,7 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2517
2351
  segments,
2518
2352
  slimDefault,
2519
2353
  withInstanceMeta({
2520
- blank: true,
2521
- persist
2354
+ blank: true
2522
2355
  })
2523
2356
  );
2524
2357
  },
@@ -2526,7 +2359,6 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2526
2359
  state.markInteracted(segments);
2527
2360
  },
2528
2361
  registerElement: (element) => {
2529
- boundElement = element;
2530
2362
  if (!INTERACTIVE_TAG_NAMES.has(element.tagName)) return;
2531
2363
  const added = state.registerElement(segments, element, formInstanceId);
2532
2364
  if (added) attachFocusListeners(state, segments, element, instanceMeta);
@@ -2534,11 +2366,9 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2534
2366
  deregisterElement: (element) => {
2535
2367
  detachFocusListeners(element);
2536
2368
  state.deregisterElement(segments, element);
2537
- if (boundElement === element) boundElement = null;
2538
2369
  },
2539
2370
  setValueWithInternalPath: (value, meta) => {
2540
- const resolvedMeta = meta === void 0 && boundElement !== null ? { persist: state.persistOptIns.hasOptIn(getOrAssignElementId(boundElement), pathKey) } : meta;
2541
- return state.setValueAtPath(segments, value, withInstanceMeta(resolvedMeta));
2371
+ return state.setValueAtPath(segments, value, withInstanceMeta(meta));
2542
2372
  },
2543
2373
  // Called by the `vRegisterHint` compile-time transform's wrapping
2544
2374
  // IIFE on every server-side render of `<element v-register="…">`.
@@ -2576,15 +2406,6 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2576
2406
  segments: Object.freeze(segments.slice()),
2577
2407
  formKey: state.formKey,
2578
2408
  formInstanceId,
2579
- // --- Persistence opt-in (internal; the directive is the only
2580
- // legitimate consumer) ---
2581
- persist,
2582
- acknowledgeSensitive,
2583
- persistOptIns: state.persistOptIns,
2584
- isSensitivePath: state.isSensitivePath,
2585
- multiTab,
2586
- ...markNoSync !== void 0 ? { markNoSync } : {},
2587
- ...unmarkNoSync !== void 0 ? { unmarkNoSync } : {},
2588
2409
  transforms,
2589
2410
  coerce,
2590
2411
  ...coerceElement !== void 0 ? { coerceElement } : {},
@@ -3021,12 +2842,10 @@ function buildFormApi(state, formInstanceId, options = {}) {
3021
2842
  const segments = canonicalizePath(pathInput).segments;
3022
2843
  return computed(() => getAtPath(state.form.value, segments));
3023
2844
  }
3024
- function setValueImpl(pathOrValue, maybeValue, maybeOptions) {
2845
+ function setValueImpl(pathOrValue, maybeValue) {
3025
2846
  const argc = arguments.length;
3026
2847
  const isPathForm = argc >= 2 && (typeof pathOrValue === "string" || Array.isArray(pathOrValue));
3027
- const options2 = isPathForm ? maybeOptions : argc >= 2 ? maybeValue : void 0;
3028
- const silent = options2?.silent === true;
3029
- const writeMeta = (extra) => withInstanceMeta(silent ? { ...extra, silent: true } : extra);
2848
+ const writeMeta = (extra) => withInstanceMeta(extra);
3030
2849
  if (!isPathForm) {
3031
2850
  const next = typeof pathOrValue === "function" ? pathOrValue(structuralSnapshot(state.form.value)) : pathOrValue;
3032
2851
  const walked2 = walkUnsetSentinels(
@@ -3094,62 +2913,56 @@ function buildFormApi(state, formInstanceId, options = {}) {
3094
2913
  return true;
3095
2914
  }
3096
2915
  const errorsProxy = buildErrorsProxy(state);
3097
- function filterToOwnFormKey(errors, op) {
3098
- const own = [];
3099
- let dropped = 0;
3100
- for (const e of errors) {
3101
- if (e.formKey === state.formKey) own.push(e);
3102
- else dropped++;
3103
- }
3104
- if (__DEV__ && dropped > 0) {
3105
- console.warn(
3106
- `[attaform] ${op}: dropped ${dropped} error(s) with non-matching formKey (this form's key is "${String(state.formKey)}"). Errors are scoped to the form that produced them \u2014 pass them to the matching form instance.`
3107
- );
2916
+ function normalizeErrorInput(item, scope) {
2917
+ if (item instanceof Error) {
2918
+ return {
2919
+ message: item.message.length > 0 ? item.message : "Unknown error",
2920
+ path: scope !== void 0 ? [...scope] : [],
2921
+ formKey: state.formKey,
2922
+ code: AttaformErrorCode.UserError
2923
+ };
3108
2924
  }
3109
- return own;
2925
+ const entry = {
2926
+ message: typeof item.message === "string" && item.message.length > 0 ? item.message : "Unknown error",
2927
+ path: scope !== void 0 ? [...scope] : Array.isArray(item.path) ? [...item.path] : [],
2928
+ formKey: state.formKey,
2929
+ code: typeof item.code === "string" && item.code.length > 0 ? item.code : AttaformErrorCode.UserError
2930
+ };
2931
+ if (item.data !== void 0) entry.data = item.data;
2932
+ return entry;
3110
2933
  }
3111
- function setFieldErrors(errors) {
3112
- const preserved = state.userErrors.get(FORM_ERRORS_PATH_KEY);
3113
- state.setAllUserErrors(filterToOwnFormKey(errors, "setFieldErrors"));
3114
- if (preserved !== void 0 && preserved.length > 0) {
3115
- state.userErrors.set(FORM_ERRORS_PATH_KEY, preserved);
2934
+ function normalizeErrorInputs(input, scope) {
2935
+ const items = Array.isArray(input) ? input : [input];
2936
+ return items.map((item) => normalizeErrorInput(item, scope));
2937
+ }
2938
+ function flattenUserErrors() {
2939
+ const all = [];
2940
+ for (const errs of state.userErrors.values()) all.push(...errs);
2941
+ return all;
2942
+ }
2943
+ function setErrors(arg1, arg2) {
2944
+ const isScoped = arguments.length >= 2 && (typeof arg1 === "string" || Array.isArray(arg1));
2945
+ if (isScoped) {
2946
+ const { segments, key } = canonicalizePath(arg1);
2947
+ const input2 = arg2;
2948
+ const resolved2 = typeof input2 === "function" ? input2((state.userErrors.get(key) ?? []).slice()) : input2;
2949
+ state.setUserErrorsForPath(segments, normalizeErrorInputs(resolved2, segments));
2950
+ return;
3116
2951
  }
2952
+ const input = arg1;
2953
+ const resolved = typeof input === "function" ? input(flattenUserErrors()) : input;
2954
+ state.setAllUserErrors(normalizeErrorInputs(resolved, void 0));
3117
2955
  }
3118
- function addFieldErrors(errors) {
3119
- state.addUserErrors(filterToOwnFormKey(errors, "addFieldErrors"));
3120
- }
3121
- function clearFieldErrors(path) {
2956
+ function clearErrors(path) {
3122
2957
  if (path === void 0) {
3123
- const preserved = state.userErrors.get(FORM_ERRORS_PATH_KEY);
3124
2958
  state.clearSchemaErrors();
3125
2959
  state.clearUserErrors();
3126
- if (preserved !== void 0 && preserved.length > 0) {
3127
- state.userErrors.set(FORM_ERRORS_PATH_KEY, preserved);
3128
- }
3129
2960
  return;
3130
2961
  }
3131
2962
  const segments = canonicalizePath(path).segments;
3132
2963
  state.clearSchemaErrors(segments);
3133
2964
  state.clearUserErrors(segments);
3134
2965
  }
3135
- function setFormErrors(errors) {
3136
- if (errors.length === 0) {
3137
- state.userErrors.delete(FORM_ERRORS_PATH_KEY);
3138
- return;
3139
- }
3140
- state.userErrors.set(
3141
- FORM_ERRORS_PATH_KEY,
3142
- errors.map((e) => ({
3143
- path: [...FORM_ERRORS_PATH],
3144
- message: e.message,
3145
- formKey: state.formKey,
3146
- code: e.code ?? "atta:form-error"
3147
- }))
3148
- );
3149
- }
3150
- function clearFormErrors() {
3151
- state.userErrors.delete(FORM_ERRORS_PATH_KEY);
3152
- }
3153
2966
  const submitting = computed(() => state.submitting.value);
3154
2967
  const submissionAttempts = computed(() => state.submissionAttempts.value);
3155
2968
  const submitted = computed(() => state.submitted.value);
@@ -3318,7 +3131,6 @@ function buildFormApi(state, formInstanceId, options = {}) {
3318
3131
  instanceId: formInstanceId
3319
3132
  })
3320
3133
  );
3321
- const persistenceHandle = state.modules.get(PERSISTENCE_MODULE_KEY);
3322
3134
  const reset = (nextDefaultValues) => {
3323
3135
  if (nextDefaultValues === void 0) {
3324
3136
  state.reset();
@@ -3333,16 +3145,10 @@ function buildFormApi(state, formInstanceId, options = {}) {
3333
3145
  state.originalBlankPaths.add(pathKey);
3334
3146
  }
3335
3147
  }
3336
- if (persistenceHandle !== void 0) {
3337
- void persistenceHandle.ready.then((m) => m?.clearPersistedDraft()).catch(() => void 0);
3338
- }
3339
3148
  };
3340
3149
  const resetField = (pathInput) => {
3341
3150
  const segments = canonicalizePath(pathInput).segments;
3342
3151
  state.resetField(segments);
3343
- if (persistenceHandle !== void 0) {
3344
- void persistenceHandle.ready.then((m) => m?.clearPersistedDraft(segments)).catch(() => void 0);
3345
- }
3346
3152
  };
3347
3153
  function clear(pathInput) {
3348
3154
  if (pathInput === void 0) {
@@ -3350,31 +3156,6 @@ function buildFormApi(state, formInstanceId, options = {}) {
3350
3156
  }
3351
3157
  return setValueImpl(pathInput, unset);
3352
3158
  }
3353
- const persist = async (pathInput, options2) => {
3354
- const segments = canonicalizePath(pathInput).segments;
3355
- if (!allowSensitivePersist(
3356
- segments,
3357
- options2?.acknowledgeSensitive === true,
3358
- state.isSensitivePath
3359
- )) {
3360
- return;
3361
- }
3362
- if (persistenceHandle === void 0) return;
3363
- const persistence = await persistenceHandle.ready;
3364
- if (persistence === void 0) return;
3365
- await persistence.writePathImmediately(segments);
3366
- };
3367
- const clearPersistedDraft = async (pathInput) => {
3368
- if (persistenceHandle === void 0) return;
3369
- const persistence = await persistenceHandle.ready;
3370
- if (persistence === void 0) return;
3371
- if (pathInput === void 0) {
3372
- await persistence.clearPersistedDraft();
3373
- return;
3374
- }
3375
- const segments = canonicalizePath(pathInput).segments;
3376
- await persistence.clearPersistedDraft(segments);
3377
- };
3378
3159
  function touch(pathInput) {
3379
3160
  const segments = pathInput === void 0 ? ROOT_PATH : canonicalizePath(pathInput).segments;
3380
3161
  state.touchAtPath(segments);
@@ -3448,29 +3229,17 @@ function buildFormApi(state, formInstanceId, options = {}) {
3448
3229
  }
3449
3230
  const EMPTY_FIELD_RECORD = Object.freeze({});
3450
3231
  function record(path) {
3451
- const { segments } = canonicalizePath(path);
3232
+ const segments = path === void 0 ? [] : canonicalizePath(path).segments;
3452
3233
  const value = state.getValueAtPath(segments);
3453
3234
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
3454
3235
  return EMPTY_FIELD_RECORD;
3455
3236
  }
3456
3237
  const out = {};
3457
3238
  for (const key of Object.keys(value)) {
3458
- safeAssign(out, key, callTerminal(`${path}.${key}`));
3239
+ safeAssign(out, key, callTerminal(segments.length === 0 ? key : `${path}.${key}`));
3459
3240
  }
3460
3241
  return Object.freeze(out);
3461
3242
  }
3462
- const formHandle = {
3463
- current: void 0
3464
- };
3465
- function onChangeImpl(a, b, c) {
3466
- const sourced = typeof b === "function";
3467
- const source = sourced ? a : void 0;
3468
- const handler = sourced ? b : a;
3469
- const options2 = sourced ? c : b;
3470
- const stop = state.registerOnChange(source, handler, options2, () => formHandle.current);
3471
- if (getCurrentScope() !== void 0) onScopeDispose(stop);
3472
- return stop;
3473
- }
3474
3243
  const api = {
3475
3244
  handleSubmit: gated(handleSubmit),
3476
3245
  // Callable readonly Proxies (`values`, `fields`, `errors`) and the
@@ -3524,11 +3293,8 @@ function buildFormApi(state, formInstanceId, options = {}) {
3524
3293
  return errorsProxy;
3525
3294
  },
3526
3295
  toRef: gated(pathToRef),
3527
- setFieldErrors: gated(setFieldErrors),
3528
- addFieldErrors: gated(addFieldErrors),
3529
- clearFieldErrors: gated(clearFieldErrors),
3530
- setFormErrors: gated(setFormErrors),
3531
- clearFormErrors: gated(clearFormErrors),
3296
+ setErrors: gated(setErrors),
3297
+ clearErrors: gated(clearErrors),
3532
3298
  get meta() {
3533
3299
  void state.activate();
3534
3300
  return formMeta;
@@ -3536,8 +3302,6 @@ function buildFormApi(state, formInstanceId, options = {}) {
3536
3302
  reset: gated(reset),
3537
3303
  resetField: gated(resetField),
3538
3304
  clear: gated(clear),
3539
- persist: gated(persist),
3540
- clearPersistedDraft: gated(clearPersistedDraft),
3541
3305
  focusFirstError: gated(focusFirstError),
3542
3306
  scrollToFirstError: gated(scrollToFirstError),
3543
3307
  applyInvalidSubmitPolicy: gated(applyInvalidSubmitPolicyPublic),
@@ -3558,10 +3322,8 @@ function buildFormApi(state, formInstanceId, options = {}) {
3558
3322
  get blankPaths() {
3559
3323
  void state.activate();
3560
3324
  return blankPathsView;
3561
- },
3562
- onChange: onChangeImpl
3325
+ }
3563
3326
  };
3564
- formHandle.current = api;
3565
3327
  return api;
3566
3328
  }
3567
3329
 
@@ -4073,168 +3835,55 @@ function createArrayBookkeeping(deps) {
4073
3835
  };
4074
3836
  }
4075
3837
 
4076
- const NOOP_STOP = () => {
4077
- };
4078
- function isThenable(value) {
4079
- return value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
4080
- }
4081
- function isSuppressed(meta) {
4082
- return meta?.hydration === true || meta?.crossTab === true || meta?.silent === true;
4083
- }
4084
- function canonicalizeSourceList(raw) {
4085
- const list = typeof raw === "string" ? [raw] : raw;
4086
- const out = [];
4087
- const seen = /* @__PURE__ */ new Set();
4088
- for (const entry of list) {
4089
- const { segments, key } = canonicalizePath(entry);
4090
- if (seen.has(key)) continue;
4091
- seen.add(key);
4092
- out.push({ segments, key, dotted: segmentsToDotted(segments) });
4093
- }
4094
- return out;
3838
+ function mergeSparseHydration(schemaDefaults, sparse, schema) {
3839
+ return mergeDeep(schemaDefaults, sparse, [], schema);
4095
3840
  }
4096
- function makeResolver(source) {
4097
- if (source === void 0) {
4098
- const root = [{ segments: ROOT_PATH, key: ROOT_PATH_KEY, dotted: "" }];
4099
- return () => root;
4100
- }
4101
- if (typeof source !== "function" && !isRef(source)) {
4102
- const fixed = canonicalizeSourceList(source);
4103
- return () => fixed;
3841
+ function mergeDeep(target, source, path, schema) {
3842
+ if (source === void 0) return target;
3843
+ if (source === null || typeof source !== "object") return source;
3844
+ if (Array.isArray(source)) return source;
3845
+ if (!isPlainRecord(source)) return source;
3846
+ if (schema !== void 0) {
3847
+ const du = schema.getUnionDiscriminatorAtPath(path);
3848
+ if (du !== void 0) return mergeDuAwareKeys(source, path, schema, du);
4104
3849
  }
4105
- return () => canonicalizeSourceList(toValue(source));
3850
+ return mergeObjectKeys(target, source, path, schema);
4106
3851
  }
4107
- function createOnChangeRegistry(deps) {
4108
- const handlers = /* @__PURE__ */ new Set();
4109
- function isCurrent(reg, key, token) {
4110
- return reg.runs.get(key)?.token === token;
4111
- }
4112
- function routeError(reg, error, source, changed, value, previous, attempt, token) {
4113
- if (reg.onError === void 0) {
4114
- if (__DEV__) {
4115
- console.error(
4116
- `[attaform] onChange handler threw for path '${source.dotted}' \u2014 error swallowed. Pass { onError } to handle it. Original error:`,
4117
- error
4118
- );
4119
- }
4120
- return;
4121
- }
4122
- const retry = () => {
4123
- if (!isCurrent(reg, source.key, token)) return;
4124
- runHandler(reg, source, changed, value, previous, attempt + 1);
4125
- };
4126
- const errCtx = {
4127
- path: source.dotted,
4128
- value,
4129
- attempt,
4130
- retry,
4131
- form: reg.getForm()
4132
- };
4133
- try {
4134
- reg.onError(error, errCtx);
4135
- } catch (err) {
4136
- if (__DEV__) console.error("[attaform] onChange onError threw:", err);
4137
- }
4138
- }
4139
- function runHandler(reg, source, changed, value, previous, attempt) {
4140
- const prior = reg.runs.get(source.key);
4141
- if (prior) prior.controller.abort();
4142
- const controller = new AbortController();
4143
- const token = ++reg.seq;
4144
- reg.runs.set(source.key, { token, controller });
4145
- const ctx = {
4146
- path: source.dotted,
4147
- previous,
4148
- signal: controller.signal,
4149
- attempt,
4150
- form: reg.getForm(),
4151
- changed
4152
- };
4153
- let result;
4154
- try {
4155
- result = reg.handler(value, ctx);
4156
- } catch (error) {
4157
- routeError(reg, error, source, changed, value, previous, attempt, token);
4158
- return;
4159
- }
4160
- if (isThenable(result)) {
4161
- result.then(void 0, (error) => {
4162
- if (isCurrent(reg, source.key, token)) {
4163
- routeError(reg, error, source, changed, value, previous, attempt, token);
4164
- }
4165
- });
4166
- }
3852
+ function mergeDuAwareKeys(source, path, schema, du) {
3853
+ const sourceDisc = source[du.discriminatorKey];
3854
+ if (sourceDisc !== void 0 && !du.isVariantSelected(sourceDisc)) {
3855
+ return { [du.discriminatorKey]: sourceDisc };
4167
3856
  }
4168
- function dispatch(patches, meta) {
4169
- if (handlers.size === 0 || isSuppressed(meta)) return;
4170
- for (const reg of handlers) {
4171
- let sources;
4172
- try {
4173
- sources = reg.resolve();
4174
- } catch (error) {
4175
- if (__DEV__) console.error("[attaform] onChange source getter threw:", error);
4176
- continue;
4177
- }
4178
- for (const source of sources) {
4179
- let changed;
4180
- for (const patch of patches) {
4181
- if (isPathPrefix(source.segments, patch.path) || isPathPrefix(patch.path, source.segments)) {
4182
- (changed ?? (changed = [])).push(segmentsToDotted(patch.path));
4183
- }
4184
- }
4185
- if (changed === void 0) continue;
4186
- const value = deps.getValueAtPath(source.segments);
4187
- const previous = reg.previous.has(source.key) ? reg.previous.get(source.key) : value;
4188
- reg.previous.set(source.key, value);
4189
- runHandler(reg, source, changed, value, previous, 0);
4190
- }
3857
+ if (sourceDisc !== void 0) {
3858
+ const variantDefault = du.getVariantDefault(sourceDisc);
3859
+ if (isPlainRecord(variantDefault)) {
3860
+ return mergeVariantKeys(source, variantDefault, path, schema, du);
4191
3861
  }
4192
3862
  }
4193
- function register(source, handler, options, getForm) {
4194
- if (deps.ssr) return NOOP_STOP;
4195
- const reg = {
4196
- handler,
4197
- onError: options?.onError,
4198
- getForm,
4199
- resolve: makeResolver(source),
4200
- previous: /* @__PURE__ */ new Map(),
4201
- runs: /* @__PURE__ */ new Map(),
4202
- seq: 0
4203
- };
4204
- try {
4205
- for (const { key, segments } of reg.resolve()) {
4206
- reg.previous.set(key, deps.getValueAtPath(segments));
4207
- }
4208
- } catch (error) {
4209
- if (__DEV__) console.error("[attaform] onChange source getter threw at registration:", error);
4210
- }
4211
- handlers.add(reg);
4212
- let stopped = false;
4213
- return () => {
4214
- if (stopped) return;
4215
- stopped = true;
4216
- handlers.delete(reg);
4217
- for (const run of reg.runs.values()) run.controller.abort();
4218
- reg.runs.clear();
4219
- reg.previous.clear();
4220
- };
3863
+ return {};
3864
+ }
3865
+ function mergeVariantKeys(source, variantDefault, path, schema, du) {
3866
+ const out = { ...variantDefault };
3867
+ for (const key of Object.keys(source)) {
3868
+ if (!safeOwnHas(variantDefault, key) && key !== du.discriminatorKey) continue;
3869
+ safeAssign(
3870
+ out,
3871
+ key,
3872
+ mergeDeep(safeOwnRead(out, key), safeOwnRead(source, key), [...path, key], schema)
3873
+ );
4221
3874
  }
4222
- function dispose() {
4223
- for (const reg of handlers) {
4224
- for (const run of reg.runs.values()) run.controller.abort();
4225
- reg.runs.clear();
4226
- reg.previous.clear();
4227
- }
4228
- handlers.clear();
3875
+ return out;
3876
+ }
3877
+ function mergeObjectKeys(target, source, path, schema) {
3878
+ const out = isPlainRecord(target) ? { ...target } : {};
3879
+ for (const key of Object.keys(source)) {
3880
+ safeAssign(
3881
+ out,
3882
+ key,
3883
+ mergeDeep(safeOwnRead(out, key), safeOwnRead(source, key), [...path, key], schema)
3884
+ );
4229
3885
  }
4230
- return {
4231
- get active() {
4232
- return handlers.size > 0;
4233
- },
4234
- register,
4235
- dispatch,
4236
- dispose
4237
- };
3886
+ return out;
4238
3887
  }
4239
3888
 
4240
3889
  function isHydratedFieldRecord(value) {
@@ -4370,27 +4019,8 @@ function createFormStore(options) {
4370
4019
  const formChangeListeners = /* @__PURE__ */ new Set();
4371
4020
  const submitSuccessListeners = /* @__PURE__ */ new Set();
4372
4021
  const resetListeners = /* @__PURE__ */ new Set();
4373
- const onChangeRegistry = createOnChangeRegistry({ getValueAtPath, ssr });
4374
- const persistOptIns = createPersistOptInRegistry();
4375
- const noSyncPaths = /* @__PURE__ */ new Set();
4376
- const noSyncPathCounts = /* @__PURE__ */ new Map();
4377
- function incrementNoSyncOptOut(path) {
4378
- const next = (noSyncPathCounts.get(path) ?? 0) + 1;
4379
- noSyncPathCounts.set(path, next);
4380
- if (next === 1) noSyncPaths.add(path);
4381
- }
4382
- function decrementNoSyncOptOut(path) {
4383
- const current = noSyncPathCounts.get(path) ?? 0;
4384
- if (current <= 1) {
4385
- noSyncPathCounts.delete(path);
4386
- noSyncPaths.delete(path);
4387
- return;
4388
- }
4389
- noSyncPathCounts.set(path, current - 1);
4390
- }
4391
4022
  const coerceIndex = resolveCoercionIndex(options.coerce);
4392
4023
  const resolvedGetDisplayState = resolveGetDisplayState(options.getDisplayState);
4393
- const resolvedIsSensitivePath = options.isSensitivePath ?? isSensitivePath;
4394
4024
  const cleanupHooks = [];
4395
4025
  const modules = /* @__PURE__ */ new Map();
4396
4026
  const fieldValidatingSince = reactive(/* @__PURE__ */ new Map());
@@ -4738,7 +4368,6 @@ function createFormStore(options) {
4738
4368
  console.error("[attaform] onFormChange threw:", err);
4739
4369
  }
4740
4370
  }
4741
- if (onChangeRegistry.active) onChangeRegistry.dispatch(patches, meta);
4742
4371
  }
4743
4372
  function applyFormReplacementWithPath(next, meta, arrayOpPath) {
4744
4373
  const prev = form.value;
@@ -5179,33 +4808,18 @@ function createFormStore(options) {
5179
4808
  formChangeListeners.clear();
5180
4809
  submitSuccessListeners.clear();
5181
4810
  resetListeners.clear();
5182
- onChangeRegistry.dispose();
5183
- persistOptIns.clear();
5184
- noSyncPaths.clear();
5185
- noSyncPathCounts.clear();
5186
4811
  }
5187
4812
  function getValueAtPath(path) {
5188
4813
  return getAtPath(form.value, path);
5189
4814
  }
5190
- function rerouteFormLevelEntry(err) {
5191
- if (err.path.length === 0) {
5192
- return { ...err, path: [...FORM_ERRORS_PATH] };
5193
- }
5194
- return err;
5195
- }
5196
- function pathKeyForEntry(err) {
5197
- if (err.path.length === 0) return FORM_ERRORS_PATH_KEY;
5198
- return canonicalizePath(err.path).key;
5199
- }
5200
4815
  function appendErrorsTo(map, entries) {
5201
4816
  for (const raw of entries) {
5202
- const err = rerouteFormLevelEntry(raw);
5203
- const key = pathKeyForEntry(err);
4817
+ const { key } = canonicalizePath(raw.path);
5204
4818
  const current = map.get(key);
5205
4819
  if (current === void 0) {
5206
- map.set(key, [err]);
4820
+ map.set(key, [raw]);
5207
4821
  } else {
5208
- map.set(key, [...current, err]);
4822
+ map.set(key, [...current, raw]);
5209
4823
  }
5210
4824
  }
5211
4825
  }
@@ -5230,14 +4844,13 @@ function createFormStore(options) {
5230
4844
  schemaErrors.set(key, [...entries]);
5231
4845
  }
5232
4846
  function applySchemaErrorsForSubtree(path, entries) {
5233
- const parentKey = path.length === 0 ? FORM_ERRORS_PATH_KEY : canonicalizePath(path).key;
4847
+ const parentKey = canonicalizePath(path).key;
5234
4848
  const grouped = /* @__PURE__ */ new Map();
5235
4849
  for (const raw of entries) {
5236
- const err = rerouteFormLevelEntry(raw);
5237
- const key = pathKeyForEntry(err);
4850
+ const { key } = canonicalizePath(raw.path);
5238
4851
  const list = grouped.get(key);
5239
- if (list === void 0) grouped.set(key, [err]);
5240
- else list.push(err);
4852
+ if (list === void 0) grouped.set(key, [raw]);
4853
+ else list.push(raw);
5241
4854
  }
5242
4855
  if (!grouped.has(parentKey)) schemaErrors.delete(parentKey);
5243
4856
  for (const existingKey of [...schemaErrors.keys()]) {
@@ -5259,8 +4872,13 @@ function createFormStore(options) {
5259
4872
  function setAllUserErrors(entries) {
5260
4873
  replaceErrorsIn(userErrors, entries);
5261
4874
  }
5262
- function addUserErrors(entries) {
5263
- appendErrorsTo(userErrors, entries);
4875
+ function setUserErrorsForPath(path, entries) {
4876
+ const { key } = canonicalizePath(path);
4877
+ if (entries.length === 0) {
4878
+ userErrors.delete(key);
4879
+ return;
4880
+ }
4881
+ userErrors.set(key, [...entries]);
5264
4882
  }
5265
4883
  function clearUserErrors(path) {
5266
4884
  clearErrorsIn(userErrors, path);
@@ -5454,25 +5072,25 @@ function createFormStore(options) {
5454
5072
  }
5455
5073
  }
5456
5074
  function clearHydrationFailedEntry() {
5457
- const existing = schemaErrors.get(FORM_ERRORS_PATH_KEY);
5075
+ const existing = schemaErrors.get(ROOT_PATH_KEY);
5458
5076
  if (existing === void 0) return;
5459
5077
  const filtered = existing.filter((e) => e.code !== AttaformErrorCode.HydrationFailed);
5460
5078
  if (filtered.length === 0) {
5461
- schemaErrors.delete(FORM_ERRORS_PATH_KEY);
5079
+ schemaErrors.delete(ROOT_PATH_KEY);
5462
5080
  } else {
5463
- schemaErrors.set(FORM_ERRORS_PATH_KEY, filtered);
5081
+ schemaErrors.set(ROOT_PATH_KEY, filtered);
5464
5082
  }
5465
5083
  }
5466
5084
  function appendHydrationFailedEntry(error) {
5467
5085
  const message = error instanceof Error ? error.message : typeof error === "string" ? error : "Hydration failed";
5468
5086
  const entry = {
5469
5087
  message,
5470
- path: [...FORM_ERRORS_PATH],
5088
+ path: [...ROOT_PATH],
5471
5089
  formKey,
5472
5090
  code: AttaformErrorCode.HydrationFailed
5473
5091
  };
5474
- const existing = schemaErrors.get(FORM_ERRORS_PATH_KEY) ?? [];
5475
- schemaErrors.set(FORM_ERRORS_PATH_KEY, [...existing, entry]);
5092
+ const existing = schemaErrors.get(ROOT_PATH_KEY) ?? [];
5093
+ schemaErrors.set(ROOT_PATH_KEY, [...existing, entry]);
5476
5094
  return entry;
5477
5095
  }
5478
5096
  function reset(nextDefaultValues) {
@@ -5485,7 +5103,7 @@ function createFormStore(options) {
5485
5103
  });
5486
5104
  const next = resetResponse.data;
5487
5105
  rebuildAuthoredPaths(resetSource, next);
5488
- applyFormReplacement(next, { silent: true });
5106
+ applyFormReplacement(next);
5489
5107
  arrayIdentity.rebaselineAll();
5490
5108
  originals.clear();
5491
5109
  diffAndApply({}, next, [], (patch) => {
@@ -5705,7 +5323,7 @@ function createFormStore(options) {
5705
5323
  clearSchemaErrors,
5706
5324
  applySchemaErrorsForSubtree,
5707
5325
  setAllUserErrors,
5708
- addUserErrors,
5326
+ setUserErrorsForPath,
5709
5327
  clearUserErrors,
5710
5328
  getErrorsForPath,
5711
5329
  ensurePathOrdinal,
@@ -5731,7 +5349,6 @@ function createFormStore(options) {
5731
5349
  settleTransforms,
5732
5350
  scheduleFieldValidation,
5733
5351
  onFormChange,
5734
- registerOnChange: onChangeRegistry.register,
5735
5352
  onSubmitSuccess,
5736
5353
  onReset,
5737
5354
  emitSubmitSuccess,
@@ -5739,11 +5356,6 @@ function createFormStore(options) {
5739
5356
  registerDrain,
5740
5357
  awaitPendingWrites,
5741
5358
  modules,
5742
- persistOptIns,
5743
- isSensitivePath: resolvedIsSensitivePath,
5744
- noSyncPaths,
5745
- incrementNoSyncOptOut,
5746
- decrementNoSyncOptOut,
5747
5359
  coerceIndex,
5748
5360
  blankPaths,
5749
5361
  originalBlankPaths,
@@ -5751,6 +5363,33 @@ function createFormStore(options) {
5751
5363
  };
5752
5364
  }
5753
5365
 
5366
+ function captureUserCallSite() {
5367
+ const raw = new Error().stack;
5368
+ if (typeof raw !== "string") return void 0;
5369
+ const lines = raw.split("\n");
5370
+ for (let i = 1; i < lines.length; i++) {
5371
+ const frame = lines[i];
5372
+ if (frame === void 0) continue;
5373
+ if (/attaform[/-]forms?/i.test(frame)) continue;
5374
+ if (/\bforms\.[A-Za-z0-9_-]+\.m?js\b/.test(frame)) continue;
5375
+ const trimmed = frame.trim();
5376
+ if (trimmed.length === 0) continue;
5377
+ return shortenSourceFrame(trimmed);
5378
+ }
5379
+ return void 0;
5380
+ }
5381
+ function shortenSourceFrame(frame) {
5382
+ const match = /(?:^|\s|\()([^\s()]+):(\d+):\d+\)?$/.exec(frame);
5383
+ if (match === null) return frame;
5384
+ const [, urlOrPath, line] = match;
5385
+ if (urlOrPath === void 0 || line === void 0) return frame;
5386
+ let path = urlOrPath;
5387
+ path = path.replace(/^[a-z]+:\/\/[^/]+\//i, "");
5388
+ path = path.replace(/^_nuxt\//, "");
5389
+ path = path.replace(/^\//, "");
5390
+ return `(${path}:${line})`;
5391
+ }
5392
+
5754
5393
  function getComputedSchema(formKey, schemaOrCallback, options) {
5755
5394
  if (typeof schemaOrCallback === "function") return schemaOrCallback(formKey, options);
5756
5395
  return schemaOrCallback;
@@ -5761,12 +5400,38 @@ function captureErrorEntries(map) {
5761
5400
  for (const [k, v] of map) out.push([k, [...v]]);
5762
5401
  return out;
5763
5402
  }
5403
+ function dataEqual(a, b) {
5404
+ if (a === b) return true;
5405
+ if (a === null || b === null || a === void 0 || b === void 0) return false;
5406
+ if (typeof a !== typeof b) return false;
5407
+ if (Array.isArray(a)) {
5408
+ if (!Array.isArray(b) || a.length !== b.length) return false;
5409
+ for (let i = 0; i < a.length; i++) {
5410
+ if (!dataEqual(a[i], b[i])) return false;
5411
+ }
5412
+ return true;
5413
+ }
5414
+ if (Array.isArray(b)) return false;
5415
+ if (typeof a === "object") {
5416
+ const ao = a;
5417
+ const bo = b;
5418
+ const keys = Object.keys(ao);
5419
+ if (keys.length !== Object.keys(bo).length) return false;
5420
+ for (const k of keys) {
5421
+ if (!Object.prototype.hasOwnProperty.call(bo, k)) return false;
5422
+ if (!dataEqual(ao[k], bo[k])) return false;
5423
+ }
5424
+ return true;
5425
+ }
5426
+ return false;
5427
+ }
5764
5428
  function errorFieldsEqual(av, bvi) {
5765
5429
  if (av === bvi) return true;
5766
5430
  if (av.message !== bvi.message) return false;
5767
5431
  if (av.code !== bvi.code) return false;
5768
5432
  if (av.formKey !== bvi.formKey) return false;
5769
- return av.path === bvi.path || pathsEqual(av.path, bvi.path);
5433
+ if (av.path !== bvi.path && !pathsEqual(av.path, bvi.path)) return false;
5434
+ return dataEqual(av.data, bvi.data);
5770
5435
  }
5771
5436
  function errorsEqual(a, b) {
5772
5437
  if (a.length !== b.length) return false;
@@ -5860,10 +5525,6 @@ function createHistoryModule(state, config) {
5860
5525
  clear();
5861
5526
  return;
5862
5527
  }
5863
- if (meta?.crossTab === true) {
5864
- currentSnapshot.value = captureSnapshot();
5865
- return;
5866
- }
5867
5528
  const newSnap = captureSnapshot();
5868
5529
  const prevSnap = currentSnapshot.value;
5869
5530
  const formPatches = [];
@@ -5884,9 +5545,7 @@ function createHistoryModule(state, config) {
5884
5545
  suppressNext = true;
5885
5546
  state.blankPaths.clear();
5886
5547
  for (const key of snap.blankPaths) state.blankPaths.add(key);
5887
- state.applyFormReplacement(snap.form, {
5888
- persist: !state.persistOptIns.isEmpty()
5889
- });
5548
+ state.applyFormReplacement(snap.form);
5890
5549
  const schemaFlat = snap.schemaErrors.flatMap(([, errs]) => errs);
5891
5550
  const userFlat = snap.userErrors.flatMap(([, errs]) => errs);
5892
5551
  state.setAllSchemaErrors(schemaFlat);
@@ -5937,28 +5596,6 @@ function createHistoryModule(state, config) {
5937
5596
  };
5938
5597
  }
5939
5598
 
5940
- const warned = /* @__PURE__ */ new Set();
5941
- function warnOnceInsecureContext(feature) {
5942
- if (!__DEV__) return;
5943
- if (warned.has(feature)) return;
5944
- warned.add(feature);
5945
- const message = featureMessage(feature);
5946
- console.warn(`[attaform] ${message}`);
5947
- }
5948
- function featureMessage(feature) {
5949
- switch (feature) {
5950
- case "multiTab":
5951
- 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.";
5952
- case "persist:local":
5953
- 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.";
5954
- case "persist:session":
5955
- 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.";
5956
- }
5957
- }
5958
- function isSecureContext() {
5959
- return typeof window !== "undefined" && window.isSecureContext === true;
5960
- }
5961
-
5962
5599
  function resolveTrichotomy(input) {
5963
5600
  if (typeof input === "function") {
5964
5601
  return { kind: "async", factory: input };
@@ -5987,18 +5624,10 @@ function useAbstractForm(configuration, options) {
5987
5624
  defaultValue: DEFAULT_MAX_RECURSION_DEPTH
5988
5625
  });
5989
5626
  const resolvedSchema = getComputedSchema(key, configuration.schema, { maxRecursionDepth });
5990
- if (configuration.persist !== void 0 && configuration.key === void 0) {
5991
- throw new AnonPersistError({
5992
- cause: "no-key",
5993
- schemaFields: extractSchemaFields(resolvedSchema),
5994
- callSite: captureUserCallSite()
5995
- });
5996
- }
5997
5627
  const existing = registry.forms.get(key);
5998
5628
  if (__DEV__ && existing !== void 0) {
5999
5629
  void import('../chunks/dev-key-collision-warnings.mjs').then((m) => {
6000
5630
  void m.warnOnSchemaFingerprintMismatch(key, existing.schema, resolvedSchema);
6001
- m.warnOnPersistDivergence(key, existing, configuration.persist);
6002
5631
  });
6003
5632
  }
6004
5633
  const hadPendingHydration = registry.pendingHydration.has(key);
@@ -6026,85 +5655,6 @@ function useAbstractForm(configuration, options) {
6026
5655
  const releaseConsumer = registry.trackConsumer(key);
6027
5656
  onScopeDispose(releaseConsumer);
6028
5657
  }
6029
- const persistDisabledByAnonRule = merged.persist !== void 0 && enforceAnonPersistRule(state.formKey, registry.ssr);
6030
- if (existing === void 0 && !registry.ssr) {
6031
- if (merged.persist !== void 0 && !persistDisabledByAnonRule) {
6032
- const resolvedPersist = normalizePersistConfig(merged.persist);
6033
- const storageKind = resolvedPersist.storage;
6034
- const isBuiltinStorage = typeof storageKind === "string";
6035
- const secureContextOk = !isBuiltinStorage || isSecureContext();
6036
- if (!secureContextOk) {
6037
- const feature = storageKind === "session" ? "persist:session" : "persist:local";
6038
- warnOnceInsecureContext(feature);
6039
- void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
6040
- } else {
6041
- const persistenceBase = resolveStorageKeyBase(resolvedPersist, state.formKey);
6042
- void sweepNonConfiguredStandardStoresForOrphans(resolvedPersist.storage, persistenceBase);
6043
- const adapterPromise = getStorageAdapter(resolvedPersist.storage);
6044
- let persistDisposed = false;
6045
- state.registerCleanup(() => {
6046
- persistDisposed = true;
6047
- });
6048
- const ready = (async () => {
6049
- try {
6050
- const [{ wirePersistence }, fingerprintToken] = await Promise.all([
6051
- import('../chunks/wire-persistence.mjs'),
6052
- resolvePersistFingerprintToken(state)
6053
- ]);
6054
- if (persistDisposed) return void 0;
6055
- const persistenceModule = wirePersistence(
6056
- state,
6057
- resolvedPersist,
6058
- adapterPromise,
6059
- fingerprintToken
6060
- );
6061
- state.registerDrain(() => persistenceModule.awaitPendingWrites());
6062
- state.registerCleanup(() => persistenceModule.dispose());
6063
- return persistenceModule;
6064
- } catch {
6065
- return void 0;
6066
- }
6067
- })();
6068
- const persistenceHandle = { config: resolvedPersist, ready };
6069
- state.modules.set(PERSISTENCE_MODULE_KEY, persistenceHandle);
6070
- }
6071
- } else {
6072
- void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
6073
- }
6074
- }
6075
- if (existing === void 0 && merged.multiTab === true && configuration.key !== void 0 && !registry.ssr) {
6076
- const hasBroadcastChannel = typeof BroadcastChannel !== "undefined";
6077
- const secureContext = isSecureContext();
6078
- if (hasBroadcastChannel && secureContext) {
6079
- let formDisposed = false;
6080
- state.registerCleanup(() => {
6081
- formDisposed = true;
6082
- });
6083
- void (async () => {
6084
- try {
6085
- const [{ createMultiTabSyncModule, MULTI_TAB_SYNC_MODULE_KEY }, fingerprint] = await Promise.all([import('../chunks/multi-tab-sync.mjs'), state.schema.fingerprint()]);
6086
- if (formDisposed) return;
6087
- const channelName = `attaform:sync:${state.formKey}:${hashStableString(fingerprint)}`;
6088
- const syncModule = createMultiTabSyncModule(state, channelName, {
6089
- isSensitivePath: state.isSensitivePath,
6090
- noSyncPaths: state.noSyncPaths,
6091
- validateForm: (form) => {
6092
- const result = state.schema.validateAtPath(form, void 0, { sync: true });
6093
- if (result instanceof Promise) return;
6094
- if (!result.success) {
6095
- throw new Error("attaform multi-tab sync: post-apply schema validation failed");
6096
- }
6097
- }
6098
- });
6099
- state.modules.set(MULTI_TAB_SYNC_MODULE_KEY, syncModule);
6100
- state.registerCleanup(() => syncModule.dispose());
6101
- } catch {
6102
- }
6103
- })();
6104
- } else if (hasBroadcastChannel && !secureContext) {
6105
- warnOnceInsecureContext("multiTab");
6106
- }
6107
- }
6108
5658
  if (existing === void 0 && merged.history !== void 0) {
6109
5659
  const historyModule = createHistoryModule(state, merged.history);
6110
5660
  state.modules.set(HISTORY_MODULE_KEY, historyModule);
@@ -6146,14 +5696,6 @@ function useAbstractForm(configuration, options) {
6146
5696
  apiOptions.autoAria = merged.autoAria;
6147
5697
  }
6148
5698
  const api = buildFormApi(state, formInstanceId, apiOptions);
6149
- const onChangeConfig = configuration.onChange;
6150
- if (onChangeConfig !== void 0) {
6151
- const handler = typeof onChangeConfig === "function" ? onChangeConfig : onChangeConfig.handler;
6152
- const onError = typeof onChangeConfig === "function" ? void 0 : onChangeConfig.onError;
6153
- const options2 = onError === void 0 ? void 0 : { onError };
6154
- const stop = state.registerOnChange(void 0, handler, options2, () => api);
6155
- if (getCurrentScope() !== void 0) onScopeDispose(stop);
6156
- }
6157
5699
  return api;
6158
5700
  }
6159
5701
  function mergeWithDefaults(defaults, configuration) {
@@ -6166,8 +5708,6 @@ function mergeWithDefaults(defaults, configuration) {
6166
5708
  const debounceMs = configuration.debounceMs ?? defaults.debounceMs;
6167
5709
  const getDisplayState = configuration.getDisplayState ?? defaults.getDisplayState;
6168
5710
  const maxRecursionDepth = configuration.maxRecursionDepth ?? defaults.maxRecursionDepth;
6169
- const sensitiveNames = configuration.sensitiveNames ?? defaults.sensitiveNames;
6170
- const multiTab = configuration.multiTab ?? defaults.multiTab;
6171
5711
  const autoAria = configuration.autoAria ?? defaults.autoAria;
6172
5712
  return {
6173
5713
  ...configuration,
@@ -6180,8 +5720,6 @@ function mergeWithDefaults(defaults, configuration) {
6180
5720
  ...debounceMs === void 0 ? {} : { debounceMs },
6181
5721
  ...getDisplayState === void 0 ? {} : { getDisplayState },
6182
5722
  ...maxRecursionDepth === void 0 ? {} : { maxRecursionDepth },
6183
- ...sensitiveNames === void 0 ? {} : { sensitiveNames },
6184
- ...multiTab === void 0 ? {} : { multiTab },
6185
5723
  ...autoAria === void 0 ? {} : { autoAria }
6186
5724
  };
6187
5725
  }
@@ -6197,8 +5735,6 @@ function buildFreshState(key, schema, configuration, registry) {
6197
5735
  if (pending === void 0) {
6198
5736
  initialBlankPaths = walked.paths;
6199
5737
  }
6200
- const resolvedSensitiveNames = configuration.sensitiveNames;
6201
- const resolvedIsSensitivePath = resolvedSensitiveNames === void 0 ? void 0 : createIsSensitivePath(resolvedSensitiveNames);
6202
5738
  const createOptions = {
6203
5739
  formKey: key,
6204
5740
  schema,
@@ -6226,8 +5762,7 @@ function buildFreshState(key, schema, configuration, registry) {
6226
5762
  ...configuration.rememberVariants !== void 0 ? { rememberVariants: configuration.rememberVariants } : {},
6227
5763
  ...configuration.coerce !== void 0 ? { coerce: configuration.coerce } : {},
6228
5764
  ...configuration.getDisplayState !== void 0 ? { getDisplayState: configuration.getDisplayState } : {},
6229
- ...initialBlankPaths !== void 0 ? { initialBlankPaths } : {},
6230
- ...resolvedIsSensitivePath !== void 0 ? { isSensitivePath: resolvedIsSensitivePath } : {}
5765
+ ...initialBlankPaths !== void 0 ? { initialBlankPaths } : {}
6231
5766
  };
6232
5767
  const state = createFormStore(createOptions);
6233
5768
  registry.forms.set(
@@ -6266,34 +5801,6 @@ function resolveFormKey(key) {
6266
5801
  }
6267
5802
  return `${ANONYMOUS_FORM_KEY_PREFIX}${anonCounter++}`;
6268
5803
  }
6269
- async function resolvePersistFingerprintToken(state) {
6270
- try {
6271
- return hashStableString(await state.schema.fingerprint());
6272
- } catch (err) {
6273
- if (__DEV__) {
6274
- console.warn(
6275
- `[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.`
6276
- );
6277
- }
6278
- return "unfingerprinted";
6279
- }
6280
- }
6281
- const warnedAnonPersistKeys = /* @__PURE__ */ new Set();
6282
- function enforceAnonPersistRule(formKey, ssr) {
6283
- if (!formKey.startsWith(ANONYMOUS_FORM_KEY_PREFIX)) return false;
6284
- if (__DEV__)
6285
- throw new AnonPersistError({
6286
- cause: "no-key",
6287
- callSite: captureUserCallSite()
6288
- });
6289
- if (!ssr && !warnedAnonPersistKeys.has(formKey)) {
6290
- warnedAnonPersistKeys.add(formKey);
6291
- console.warn(
6292
- "[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: '...' })"
6293
- );
6294
- }
6295
- return true;
6296
- }
6297
5804
 
6298
5805
  let injectedInstanceCounter = 0;
6299
5806
  function injectForm(input) {
@@ -7520,5 +7027,5 @@ function warnIfAmbientWizardProviderHadDuplicates() {
7520
7027
  }
7521
7028
  }
7522
7029
 
7523
- 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 };
7524
- //# sourceMappingURL=attaform.CsB-iKbU.mjs.map
7030
+ 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, slimKindOf as n, humanize as o, getAtPath as p, setAtPath as q, safeOwnRead as s, unset as u };
7031
+ //# sourceMappingURL=attaform.CuBdtfbe.mjs.map