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,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const vue = require('vue');
4
- const paths = require('./attaform.01iKS_lz.cjs');
4
+ const paths = require('./attaform.DwkU0oY9.cjs');
5
5
 
6
6
  function safeAssign(target, key, value) {
7
7
  if (key === "__proto__") {
@@ -542,6 +542,58 @@ function structuralSnapshot(value) {
542
542
  return out;
543
543
  }
544
544
 
545
+ const AttaformErrorCode = {
546
+ /** A required field is in the blank set — user hasn't supplied a value. */
547
+ NoValueSupplied: "atta:no-value-supplied",
548
+ /** The schema adapter's `validateAtPath` threw synchronously. */
549
+ AdapterThrew: "atta:adapter-threw",
550
+ /**
551
+ * User code inside a `z.preprocess`, `.refine`, or `.transform`
552
+ * threw (sync or async). The adapter caught the throw and surfaced
553
+ * it as a `ValidationError` at the field path so the form's normal
554
+ * error pipeline handles it instead of leaking as an unhandled
555
+ * rejection or routing through `submitError`.
556
+ */
557
+ ValidatorThrew: "atta:validator-threw",
558
+ /**
559
+ * A function-form `defaultValues` factory threw or its promise
560
+ * rejected. The runtime captures the raw error on `form.hydrateError`
561
+ * and ALSO surfaces a form-level `ValidationError` (path `[]`) so
562
+ * the standard error pipeline carries the signal. Critical for the
563
+ * SSR round-trip: `hydrateError` itself does not ride the wire
564
+ * payload, but `schemaErrors` does, so the client sees the failure
565
+ * after rehydration without an extra channel.
566
+ */
567
+ HydrationFailed: "atta:hydration-failed",
568
+ /** The supplied path didn't resolve to any node in the schema. */
569
+ PathNotFound: "atta:path-not-found",
570
+ /**
571
+ * A walked form's `activate()` (async `defaultValues` factory) threw
572
+ * during `wizard.handleSubmit`'s path walk. Surfaced as a synthetic
573
+ * `ValidationError` at the form-level path (`[]`) so the wizard's
574
+ * aggregate error pipeline can carry the failure alongside ordinary
575
+ * validation errors. The raw factory error remains on
576
+ * `form.hydrateError` for retry UX.
577
+ */
578
+ ActivationFailed: "atta:activation-failed",
579
+ /**
580
+ * Default code stamped on a manual error set through `form.setErrors`
581
+ * when the caller omits an explicit `code`. The `setErrors` input is
582
+ * lenient (`code` optional), so this is the fallback that keeps every
583
+ * produced `ValidationError` carrying a stable, branchable identifier.
584
+ * Override it per error by passing your own `code` (`api:…`, `auth:…`).
585
+ */
586
+ UserError: "atta:user-error"
587
+ };
588
+ function makeBlankRequiredError(segments, formKey) {
589
+ return {
590
+ message: "No value supplied",
591
+ path: [...segments],
592
+ formKey,
593
+ code: AttaformErrorCode.NoValueSupplied
594
+ };
595
+ }
596
+
545
597
  function isGateOpen(field, formMeta) {
546
598
  return formMeta.submissionAttempts > 0 || field.blurredAfterInteraction === true;
547
599
  }
@@ -597,54 +649,8 @@ function resolveGetDisplayState(config) {
597
649
  return config ?? defaultDisplayState;
598
650
  }
599
651
 
600
- const AttaformErrorCode = {
601
- /** A required field is in the blank set — user hasn't supplied a value. */
602
- NoValueSupplied: "atta:no-value-supplied",
603
- /** The schema adapter's `validateAtPath` threw synchronously. */
604
- AdapterThrew: "atta:adapter-threw",
605
- /**
606
- * User code inside a `z.preprocess`, `.refine`, or `.transform`
607
- * threw (sync or async). The adapter caught the throw and surfaced
608
- * it as a `ValidationError` at the field path so the form's normal
609
- * error pipeline handles it instead of leaking as an unhandled
610
- * rejection or routing through `submitError`.
611
- */
612
- ValidatorThrew: "atta:validator-threw",
613
- /**
614
- * A function-form `defaultValues` factory threw or its promise
615
- * rejected. The runtime captures the raw error on `form.hydrateError`
616
- * and ALSO surfaces a form-level `ValidationError` (path `[]`) so
617
- * the standard error pipeline carries the signal. Critical for the
618
- * SSR round-trip: `hydrateError` itself does not ride the wire
619
- * payload, but `schemaErrors` does, so the client sees the failure
620
- * after rehydration without an extra channel.
621
- */
622
- HydrationFailed: "atta:hydration-failed",
623
- /** The supplied path didn't resolve to any node in the schema. */
624
- PathNotFound: "atta:path-not-found",
625
- /**
626
- * A walked form's `activate()` (async `defaultValues` factory) threw
627
- * during `wizard.handleSubmit`'s path walk. Surfaced as a synthetic
628
- * `ValidationError` at the form-level path (`[]`) so the wizard's
629
- * aggregate error pipeline can carry the failure alongside ordinary
630
- * validation errors. The raw factory error remains on
631
- * `form.hydrateError` for retry UX.
632
- */
633
- ActivationFailed: "atta:activation-failed"
634
- };
635
- function makeBlankRequiredError(segments, formKey) {
636
- return {
637
- message: "No value supplied",
638
- path: [...segments],
639
- formKey,
640
- code: AttaformErrorCode.NoValueSupplied
641
- };
642
- }
643
-
644
652
  const DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS = 0;
645
- const DEFAULT_PERSISTENCE_DEBOUNCE_MS = 300;
646
653
  const DEFAULT_HISTORY_MAX_SNAPSHOTS = 128;
647
- const PERSISTENCE_KEY_PREFIX = "attaform:";
648
654
  const RESERVED_KEY_PREFIX = "__atta:";
649
655
  const ANONYMOUS_FORM_KEY_PREFIX = `${RESERVED_KEY_PREFIX}anon:`;
650
656
  const ANONYMOUS_WIZARD_KEY_PREFIX = `${RESERVED_KEY_PREFIX}anon-wizard:`;
@@ -1035,7 +1041,7 @@ function aggregateErrorsAt(state, prefix) {
1035
1041
  const segs = paths.segmentsForPathKey(pathKey);
1036
1042
  if (segs === null) continue;
1037
1043
  if (!paths.isPathPrefix(prefix, segs)) continue;
1038
- if (pathKey === paths.FORM_ERRORS_PATH_KEY) ; else if (segs.length > 0 && !hasAtPath(formValue, segs)) continue;
1044
+ if (segs.length > 0 && !hasAtPath(formValue, segs)) continue;
1039
1045
  const ordinal = state.ensurePathOrdinal(pathKey);
1040
1046
  const existing = buckets.get(ordinal);
1041
1047
  if (existing === void 0) buckets.set(ordinal, [...list]);
@@ -1154,7 +1160,12 @@ function buildSurfaceProxy(opts) {
1154
1160
  const proxy = new Proxy(target, {
1155
1161
  apply(_, __, args) {
1156
1162
  const arg = args[0];
1157
- if (arg === void 0) return opts.resolveCallTarget(segments);
1163
+ if (arg === void 0) {
1164
+ if (segments.length === 0 && opts.resolveRootCall !== void 0) {
1165
+ return opts.resolveRootCall();
1166
+ }
1167
+ return opts.resolveCallTarget(segments);
1168
+ }
1158
1169
  const { segments: argSegs } = paths.canonicalizePath(arg);
1159
1170
  return opts.resolveCallTarget(argSegs);
1160
1171
  },
@@ -1365,8 +1376,7 @@ function buildErrorsProxy(state) {
1365
1376
  return merged;
1366
1377
  }
1367
1378
  const { key } = paths.canonicalizePath(path);
1368
- const isFormLevel = key === paths.FORM_ERRORS_PATH_KEY;
1369
- const active = isFormLevel || hasAtPath(state.form.value, path);
1379
+ const active = hasAtPath(state.form.value, path);
1370
1380
  collectAtKey(key, active, merged);
1371
1381
  return merged;
1372
1382
  },
@@ -1374,37 +1384,42 @@ function buildErrorsProxy(state) {
1374
1384
  // undefined) IS the terminal.
1375
1385
  materializeContainer: (segments) => materializeErrors(state, segments),
1376
1386
  // Any path ending in `''` is a meaningful terminal at the proxy
1377
- // layer: at root it's the form-level bucket; at depth >= 1 it's
1378
- // the container-self sentinel that surfaces cross-field refines
1379
- // and container-targeted marks. `resolveLeaf` translates `[...,
1380
- // '']` lookups to the parent container path before querying the
1381
- // stores. When a schema legitimately owns a `''` field, the
1382
- // literal leaf and any container-self errors share the slot
1383
- // (errors concatenate) — vanishingly rare, accepted as the
1384
- // ergonomic cost of one unified sentinel convention at every
1385
- // depth.
1387
+ // layer. A bare `['']` is the literal root `''` field; at depth
1388
+ // >= 1 a trailing `''` is the container-self sentinel that surfaces
1389
+ // cross-field refines and container-targeted marks (`resolveLeaf`
1390
+ // translates `[..., '']` to the parent container path before
1391
+ // querying the stores). When a schema legitimately owns a `''`
1392
+ // field, the literal leaf and any container-self errors share the
1393
+ // slot (errors concatenate) — vanishingly rare, accepted as the
1394
+ // ergonomic cost of one unified sentinel convention at depth >= 1.
1386
1395
  isTerminalAt: (segs) => segs.length >= 1 && segs[segs.length - 1] === "",
1387
1396
  // Call-form aggregates: `form.errors(path)` returns a single
1388
- // `ValidationError[]` for any depth (leaf or container) same
1397
+ // `ValidationError[]` for any depth (leaf or container) via the
1389
1398
  // shared `aggregateErrorsAt` helper that `form.meta.errors` and
1390
- // `form.fields(path).errors` use, so the three surfaces never
1391
- // drift. Empty results return `undefined`, matching the leaf
1392
- // proxy's pre-existing semantic (`form.errors.email === undefined`
1393
- // when valid) so consumer code that branches on truthiness keeps
1394
- // working the call-form just extends that semantic to
1395
- // containers and dynamic paths.
1396
- resolveCallTarget: (path) => aggregateErrorsAt(state, path),
1399
+ // `form.fields(path).errors` also use, so the surfaces never drift.
1400
+ //
1401
+ // The root is the one carve-out. An EXPLICIT root path
1402
+ // (`errors([])`) returns ONLY the global `[]` bucket (root
1403
+ // `.refine()`, hydration failures, `setErrors`) via
1404
+ // `getErrorsForPath`, giving consumers a dedicated channel for
1405
+ // global messages undiluted by field errors. The no-arg `errors()`
1406
+ // instead resolves the FULL aggregate through `resolveRootCall`
1407
+ // below (identical to `meta.errors`).
1408
+ resolveCallTarget: (path) => path.length === 0 ? state.getErrorsForPath([]) : aggregateErrorsAt(state, path),
1409
+ // No-arg `errors()` = the whole-form aggregate, matching
1410
+ // `meta.errors`. Distinct from `errors([])` (global bucket only);
1411
+ // see `resolveCallTarget`.
1412
+ resolveRootCall: () => aggregateErrorsAt(state, []),
1397
1413
  // Enumeration unions the live form-data keys at this path with the
1398
1414
  // first-child segments drawn from every error store. Without the
1399
1415
  // union, `Object.keys(form.errors)` / `{...form.errors}` /
1400
- // `v-for="(errs, k) in form.errors"` silently dropped two
1401
- // important error classes that the dot / call / JSON.stringify
1402
- // surfaces already exposed:
1403
- //
1404
- // - **Form-level** errors at the synthetic `['']` path (set via
1405
- // `setFormErrors` or root cross-field refines).
1406
- // - **Server-only** errors at a key the schema doesn't know
1407
- // about (`['ghost']`, `['address', 'ghost']`).
1416
+ // `v-for="(errs, k) in form.errors"` would silently drop
1417
+ // **server-only** errors at a key the schema doesn't know about
1418
+ // (`['ghost']`, `['address', 'ghost']`) that the dot / call /
1419
+ // JSON.stringify surfaces already expose. Global errors at the root
1420
+ // `[]` are NOT a child key and never enumerate here (read them via
1421
+ // `errors([])` / `meta.errors`); a literal `''` field enumerates
1422
+ // under the key `''` like any other field.
1408
1423
  //
1409
1424
  // The union closes that gap so `ownKeys` agrees with the rest of
1410
1425
  // the surface. Active-path filter mirrors `resolveLeaf`:
@@ -1426,10 +1441,6 @@ function errorAwareContainerKeys(state, segments) {
1426
1441
  const walk = (store, applyActivePathFilter) => {
1427
1442
  for (const [pathKey, errors] of store) {
1428
1443
  if (errors.length === 0) continue;
1429
- if (pathKey === paths.FORM_ERRORS_PATH_KEY) {
1430
- if (segments.length === 0) keys.add("");
1431
- continue;
1432
- }
1433
1444
  const decoded = paths.segmentsForPathKey(pathKey);
1434
1445
  if (decoded === null) continue;
1435
1446
  if (decoded.length <= segments.length) continue;
@@ -1452,31 +1463,25 @@ function materializeErrors(state, containerSegments) {
1452
1463
  if (errors.length === 0) continue;
1453
1464
  const fullPath = paths.segmentsForPathKey(pathKey);
1454
1465
  if (fullPath === null) continue;
1455
- const isSyntheticFormLevel = fullPath.length === 1 && fullPath[0] === "";
1456
- if (!isSyntheticFormLevel) {
1457
- if (fullPath.length < containerSegments.length) continue;
1458
- for (let i = 0; i < containerSegments.length; i++) {
1459
- if (fullPath[i] !== containerSegments[i]) continue entries;
1460
- }
1461
- } else if (containerSegments.length !== 0) {
1466
+ if (fullPath.length === 0) {
1467
+ if (containerSegments.length === 0) placeAt(tree, [paths.ROOT_PATH_KEY], errors);
1462
1468
  continue;
1463
1469
  }
1464
- if (applyActivePathFilter && !isSyntheticFormLevel && !hasAtPath(state.form.value, fullPath))
1465
- continue;
1470
+ if (fullPath.length < containerSegments.length) continue;
1471
+ for (let i = 0; i < containerSegments.length; i++) {
1472
+ if (fullPath[i] !== containerSegments[i]) continue entries;
1473
+ }
1474
+ if (applyActivePathFilter && !hasAtPath(state.form.value, fullPath)) continue;
1475
+ const relativePath = fullPath.slice(containerSegments.length);
1466
1476
  let placePath;
1467
- if (isSyntheticFormLevel) {
1477
+ if (relativePath.length === 0) {
1468
1478
  placePath = [""];
1479
+ } else if (state.schema.isLeafAtPath(fullPath)) {
1480
+ placePath = relativePath;
1481
+ } else if (state.schema.getSlimPrimitiveTypesAtPath(fullPath).size > 0) {
1482
+ placePath = [...relativePath, ""];
1469
1483
  } else {
1470
- const relativePath = fullPath.slice(containerSegments.length);
1471
- if (relativePath.length === 0) {
1472
- placePath = [""];
1473
- } else if (state.schema.isLeafAtPath(fullPath)) {
1474
- placePath = relativePath;
1475
- } else if (state.schema.getSlimPrimitiveTypesAtPath(fullPath).size > 0) {
1476
- placePath = [...relativePath, ""];
1477
- } else {
1478
- placePath = relativePath;
1479
- }
1484
+ placePath = relativePath;
1480
1485
  }
1481
1486
  placeAt(tree, placePath, errors);
1482
1487
  }
@@ -1515,9 +1520,8 @@ function buildFieldArrayApi(state) {
1515
1520
  return Array.isArray(current) ? current.slice() : [];
1516
1521
  }
1517
1522
  function writeArray(path, next, arrayOp) {
1518
- const { segments, key } = paths.canonicalizePath(path);
1523
+ const { segments } = paths.canonicalizePath(path);
1519
1524
  const meta = {
1520
- persist: state.persistOptIns.hasAnyOptInForPath(key),
1521
1525
  ...arrayOp !== void 0 ? { arrayOp } : {}
1522
1526
  };
1523
1527
  return state.setValueAtPath(segments, next, meta);
@@ -1739,121 +1743,6 @@ function walk$1(value, basePath, schema, snapshotFieldStateAt) {
1739
1743
  return result;
1740
1744
  }
1741
1745
 
1742
- const PERSISTENCE_MODULE_KEY = "persistence";
1743
- async function getStorageAdapter(storage) {
1744
- if (typeof storage === "object") return storage;
1745
- switch (storage) {
1746
- case "local": {
1747
- const { createLocalStorageAdapter } = await import('../chunks/local-storage.cjs');
1748
- return createLocalStorageAdapter();
1749
- }
1750
- case "session": {
1751
- const { createSessionStorageAdapter } = await import('../chunks/session-storage.cjs');
1752
- return createSessionStorageAdapter();
1753
- }
1754
- case "indexeddb": {
1755
- const { createIndexedDbAdapter } = await import('../chunks/indexeddb.cjs');
1756
- return createIndexedDbAdapter();
1757
- }
1758
- }
1759
- }
1760
- function resolveStorageKeyBase(config, formKey) {
1761
- return config.key ?? `${PERSISTENCE_KEY_PREFIX}${formKey}`;
1762
- }
1763
- async function removeMatchingKeys(adapter, base, keepKey) {
1764
- let keys;
1765
- try {
1766
- keys = await adapter.listKeys(base);
1767
- } catch {
1768
- return;
1769
- }
1770
- for (const key of keys) {
1771
- if (key === keepKey) continue;
1772
- if (key === base || key.startsWith(`${base}:`)) {
1773
- void adapter.removeItem(key).catch(() => void 0);
1774
- }
1775
- }
1776
- }
1777
- async function cleanupOrphanKeys(adapter, base, currentKey) {
1778
- await removeMatchingKeys(adapter, base, currentKey);
1779
- }
1780
- const STANDARD_STORAGE_KINDS = ["local", "session", "indexeddb"];
1781
- function normalizePersistConfig(input) {
1782
- if (typeof input === "string") return { storage: input };
1783
- if ("storage" in input) return input;
1784
- return { storage: input };
1785
- }
1786
- async function sweepAllOrphansAcrossStandardStores(base) {
1787
- for (const kind of STANDARD_STORAGE_KINDS) {
1788
- try {
1789
- const adapter = await getStorageAdapter(kind);
1790
- await removeMatchingKeys(adapter, base);
1791
- } catch {
1792
- }
1793
- }
1794
- }
1795
- async function sweepNonConfiguredStandardStoresForOrphans(configured, base) {
1796
- const configuredKind = typeof configured === "string" ? configured : null;
1797
- for (const kind of STANDARD_STORAGE_KINDS) {
1798
- if (kind === configuredKind) continue;
1799
- try {
1800
- const adapter = await getStorageAdapter(kind);
1801
- await removeMatchingKeys(adapter, base);
1802
- } catch {
1803
- }
1804
- }
1805
- }
1806
- function mergeSparseHydration(schemaDefaults, sparse, schema) {
1807
- return mergeDeep(schemaDefaults, sparse, [], schema);
1808
- }
1809
- function mergeDeep(target, source, path, schema) {
1810
- if (source === void 0) return target;
1811
- if (source === null || typeof source !== "object") return source;
1812
- if (Array.isArray(source)) return source;
1813
- if (!isPlainRecord(source)) return source;
1814
- if (schema !== void 0) {
1815
- const du = schema.getUnionDiscriminatorAtPath(path);
1816
- if (du !== void 0) return mergeDuAwareKeys(source, path, schema, du);
1817
- }
1818
- return mergeObjectKeys(target, source, path, schema);
1819
- }
1820
- function mergeDuAwareKeys(source, path, schema, du) {
1821
- const sourceDisc = source[du.discriminatorKey];
1822
- if (sourceDisc !== void 0 && !du.isVariantSelected(sourceDisc)) {
1823
- return { [du.discriminatorKey]: sourceDisc };
1824
- }
1825
- if (sourceDisc !== void 0) {
1826
- const variantDefault = du.getVariantDefault(sourceDisc);
1827
- if (isPlainRecord(variantDefault)) {
1828
- return mergeVariantKeys(source, variantDefault, path, schema, du);
1829
- }
1830
- }
1831
- return {};
1832
- }
1833
- function mergeVariantKeys(source, variantDefault, path, schema, du) {
1834
- const out = { ...variantDefault };
1835
- for (const key of Object.keys(source)) {
1836
- if (!safeOwnHas(variantDefault, key) && key !== du.discriminatorKey) continue;
1837
- safeAssign(
1838
- out,
1839
- key,
1840
- mergeDeep(safeOwnRead(out, key), safeOwnRead(source, key), [...path, key], schema)
1841
- );
1842
- }
1843
- return out;
1844
- }
1845
- function mergeObjectKeys(target, source, path, schema) {
1846
- const out = isPlainRecord(target) ? { ...target } : {};
1847
- for (const key of Object.keys(source)) {
1848
- safeAssign(
1849
- out,
1850
- key,
1851
- mergeDeep(safeOwnRead(out, key), safeOwnRead(source, key), [...path, key], schema)
1852
- );
1853
- }
1854
- return out;
1855
- }
1856
-
1857
1746
  const warnedNoScopeStores = paths.__DEV__ ? /* @__PURE__ */ new WeakSet() : null;
1858
1747
  function buildProcessForm(state, formInstanceId, options = {}) {
1859
1748
  const invalidPolicy = options.onInvalidSubmit ?? "focus-first-error";
@@ -2111,44 +2000,6 @@ function applyInvalidSubmitPolicy(state, formInstanceId, policy) {
2111
2000
  target.element.focus({ preventScroll: true });
2112
2001
  }
2113
2002
 
2114
- function captureUserCallSite() {
2115
- const raw = new Error().stack;
2116
- if (typeof raw !== "string") return void 0;
2117
- const lines = raw.split("\n");
2118
- for (let i = 1; i < lines.length; i++) {
2119
- const frame = lines[i];
2120
- if (frame === void 0) continue;
2121
- if (/attaform[/-]forms?/i.test(frame)) continue;
2122
- if (/\bforms\.[A-Za-z0-9_-]+\.m?js\b/.test(frame)) continue;
2123
- const trimmed = frame.trim();
2124
- if (trimmed.length === 0) continue;
2125
- return shortenSourceFrame(trimmed);
2126
- }
2127
- return void 0;
2128
- }
2129
- function shortenSourceFrame(frame) {
2130
- const match = /(?:^|\s|\()([^\s()]+):(\d+):\d+\)?$/.exec(frame);
2131
- if (match === null) return frame;
2132
- const [, urlOrPath, line] = match;
2133
- if (urlOrPath === void 0 || line === void 0) return frame;
2134
- let path = urlOrPath;
2135
- path = path.replace(/^[a-z]+:\/\/[^/]+\//i, "");
2136
- path = path.replace(/^_nuxt\//, "");
2137
- path = path.replace(/^\//, "");
2138
- return `(${path}:${line})`;
2139
- }
2140
-
2141
- function extractSchemaFields(schema) {
2142
- try {
2143
- const root = schema.getDefaultAtPath([]);
2144
- if (root !== null && typeof root === "object" && !Array.isArray(root)) {
2145
- return Object.keys(root);
2146
- }
2147
- } catch {
2148
- }
2149
- return [];
2150
- }
2151
-
2152
2003
  const warnedRejections = paths.__DEV__ ? /* @__PURE__ */ new WeakMap() : null;
2153
2004
  function shouldWarnOnce$1(store, key) {
2154
2005
  if (warnedRejections === null) return false;
@@ -2478,16 +2329,7 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2478
2329
  const slimTypes = state.schema.getSlimPrimitiveTypesAtPath(segments);
2479
2330
  const acceptsUndefined = slimTypes.has("undefined");
2480
2331
  const acceptsString = slimTypes.has("string");
2481
- const persist = options?.persist === true;
2482
- const acknowledgeSensitive = options?.acknowledgeSensitive === true;
2483
- const multiTab = options?.multiTab !== false;
2484
2332
  const transforms = options?.transforms ?? EMPTY_TRANSFORMS;
2485
- const markNoSync = !multiTab ? () => {
2486
- state.incrementNoSyncOptOut(pathKey);
2487
- } : void 0;
2488
- const unmarkNoSync = !multiTab ? () => {
2489
- state.decrementNoSyncOptOut(pathKey);
2490
- } : void 0;
2491
2333
  const coerce = buildCoerceFn(
2492
2334
  state.schema,
2493
2335
  segments,
@@ -2498,18 +2340,10 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2498
2340
  segments,
2499
2341
  coerceIndex
2500
2342
  );
2501
- if (persist && !state.ssr && !state.modules.has(PERSISTENCE_MODULE_KEY)) {
2502
- throw new paths.AnonPersistError({
2503
- cause: "register-without-config",
2504
- schemaFields: extractSchemaFields(state.schema),
2505
- callSite: captureUserCallSite()
2506
- });
2507
- }
2508
2343
  const { aria } = computeFieldIdentity(formInstanceId, state.formKey, pathKey);
2509
2344
  const isRequired = state.schema.isRequiredAtPath(segments);
2510
2345
  const ariaEnabled = options?.autoAria ?? formAutoAria;
2511
2346
  const ariaDisplayState = getDisplayStateAt !== void 0 ? vue.computed(() => getDisplayStateAt(segments)) : void 0;
2512
- let boundElement = null;
2513
2347
  const internalRv = {
2514
2348
  innerRef,
2515
2349
  displayValue,
@@ -2519,8 +2353,7 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2519
2353
  segments,
2520
2354
  slimDefault,
2521
2355
  withInstanceMeta({
2522
- blank: true,
2523
- persist
2356
+ blank: true
2524
2357
  })
2525
2358
  );
2526
2359
  },
@@ -2528,7 +2361,6 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2528
2361
  state.markInteracted(segments);
2529
2362
  },
2530
2363
  registerElement: (element) => {
2531
- boundElement = element;
2532
2364
  if (!paths.INTERACTIVE_TAG_NAMES.has(element.tagName)) return;
2533
2365
  const added = state.registerElement(segments, element, formInstanceId);
2534
2366
  if (added) attachFocusListeners(state, segments, element, instanceMeta);
@@ -2536,11 +2368,9 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2536
2368
  deregisterElement: (element) => {
2537
2369
  detachFocusListeners(element);
2538
2370
  state.deregisterElement(segments, element);
2539
- if (boundElement === element) boundElement = null;
2540
2371
  },
2541
2372
  setValueWithInternalPath: (value, meta) => {
2542
- const resolvedMeta = meta === void 0 && boundElement !== null ? { persist: state.persistOptIns.hasOptIn(paths.getOrAssignElementId(boundElement), pathKey) } : meta;
2543
- return state.setValueAtPath(segments, value, withInstanceMeta(resolvedMeta));
2373
+ return state.setValueAtPath(segments, value, withInstanceMeta(meta));
2544
2374
  },
2545
2375
  // Called by the `vRegisterHint` compile-time transform's wrapping
2546
2376
  // IIFE on every server-side render of `<element v-register="…">`.
@@ -2578,15 +2408,6 @@ function buildRegister(state, formInstanceId, instanceConfig) {
2578
2408
  segments: Object.freeze(segments.slice()),
2579
2409
  formKey: state.formKey,
2580
2410
  formInstanceId,
2581
- // --- Persistence opt-in (internal; the directive is the only
2582
- // legitimate consumer) ---
2583
- persist,
2584
- acknowledgeSensitive,
2585
- persistOptIns: state.persistOptIns,
2586
- isSensitivePath: state.isSensitivePath,
2587
- multiTab,
2588
- ...markNoSync !== void 0 ? { markNoSync } : {},
2589
- ...unmarkNoSync !== void 0 ? { unmarkNoSync } : {},
2590
2411
  transforms,
2591
2412
  coerce,
2592
2413
  ...coerceElement !== void 0 ? { coerceElement } : {},
@@ -3023,12 +2844,10 @@ function buildFormApi(state, formInstanceId, options = {}) {
3023
2844
  const segments = paths.canonicalizePath(pathInput).segments;
3024
2845
  return vue.computed(() => getAtPath(state.form.value, segments));
3025
2846
  }
3026
- function setValueImpl(pathOrValue, maybeValue, maybeOptions) {
2847
+ function setValueImpl(pathOrValue, maybeValue) {
3027
2848
  const argc = arguments.length;
3028
2849
  const isPathForm = argc >= 2 && (typeof pathOrValue === "string" || Array.isArray(pathOrValue));
3029
- const options2 = isPathForm ? maybeOptions : argc >= 2 ? maybeValue : void 0;
3030
- const silent = options2?.silent === true;
3031
- const writeMeta = (extra) => withInstanceMeta(silent ? { ...extra, silent: true } : extra);
2850
+ const writeMeta = (extra) => withInstanceMeta(extra);
3032
2851
  if (!isPathForm) {
3033
2852
  const next = typeof pathOrValue === "function" ? pathOrValue(structuralSnapshot(state.form.value)) : pathOrValue;
3034
2853
  const walked2 = walkUnsetSentinels(
@@ -3096,62 +2915,56 @@ function buildFormApi(state, formInstanceId, options = {}) {
3096
2915
  return true;
3097
2916
  }
3098
2917
  const errorsProxy = buildErrorsProxy(state);
3099
- function filterToOwnFormKey(errors, op) {
3100
- const own = [];
3101
- let dropped = 0;
3102
- for (const e of errors) {
3103
- if (e.formKey === state.formKey) own.push(e);
3104
- else dropped++;
3105
- }
3106
- if (paths.__DEV__ && dropped > 0) {
3107
- console.warn(
3108
- `[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.`
3109
- );
2918
+ function normalizeErrorInput(item, scope) {
2919
+ if (item instanceof Error) {
2920
+ return {
2921
+ message: item.message.length > 0 ? item.message : "Unknown error",
2922
+ path: scope !== void 0 ? [...scope] : [],
2923
+ formKey: state.formKey,
2924
+ code: AttaformErrorCode.UserError
2925
+ };
3110
2926
  }
3111
- return own;
2927
+ const entry = {
2928
+ message: typeof item.message === "string" && item.message.length > 0 ? item.message : "Unknown error",
2929
+ path: scope !== void 0 ? [...scope] : Array.isArray(item.path) ? [...item.path] : [],
2930
+ formKey: state.formKey,
2931
+ code: typeof item.code === "string" && item.code.length > 0 ? item.code : AttaformErrorCode.UserError
2932
+ };
2933
+ if (item.data !== void 0) entry.data = item.data;
2934
+ return entry;
3112
2935
  }
3113
- function setFieldErrors(errors) {
3114
- const preserved = state.userErrors.get(paths.FORM_ERRORS_PATH_KEY);
3115
- state.setAllUserErrors(filterToOwnFormKey(errors, "setFieldErrors"));
3116
- if (preserved !== void 0 && preserved.length > 0) {
3117
- state.userErrors.set(paths.FORM_ERRORS_PATH_KEY, preserved);
2936
+ function normalizeErrorInputs(input, scope) {
2937
+ const items = Array.isArray(input) ? input : [input];
2938
+ return items.map((item) => normalizeErrorInput(item, scope));
2939
+ }
2940
+ function flattenUserErrors() {
2941
+ const all = [];
2942
+ for (const errs of state.userErrors.values()) all.push(...errs);
2943
+ return all;
2944
+ }
2945
+ function setErrors(arg1, arg2) {
2946
+ const isScoped = arguments.length >= 2 && (typeof arg1 === "string" || Array.isArray(arg1));
2947
+ if (isScoped) {
2948
+ const { segments, key } = paths.canonicalizePath(arg1);
2949
+ const input2 = arg2;
2950
+ const resolved2 = typeof input2 === "function" ? input2((state.userErrors.get(key) ?? []).slice()) : input2;
2951
+ state.setUserErrorsForPath(segments, normalizeErrorInputs(resolved2, segments));
2952
+ return;
3118
2953
  }
2954
+ const input = arg1;
2955
+ const resolved = typeof input === "function" ? input(flattenUserErrors()) : input;
2956
+ state.setAllUserErrors(normalizeErrorInputs(resolved, void 0));
3119
2957
  }
3120
- function addFieldErrors(errors) {
3121
- state.addUserErrors(filterToOwnFormKey(errors, "addFieldErrors"));
3122
- }
3123
- function clearFieldErrors(path) {
2958
+ function clearErrors(path) {
3124
2959
  if (path === void 0) {
3125
- const preserved = state.userErrors.get(paths.FORM_ERRORS_PATH_KEY);
3126
2960
  state.clearSchemaErrors();
3127
2961
  state.clearUserErrors();
3128
- if (preserved !== void 0 && preserved.length > 0) {
3129
- state.userErrors.set(paths.FORM_ERRORS_PATH_KEY, preserved);
3130
- }
3131
2962
  return;
3132
2963
  }
3133
2964
  const segments = paths.canonicalizePath(path).segments;
3134
2965
  state.clearSchemaErrors(segments);
3135
2966
  state.clearUserErrors(segments);
3136
2967
  }
3137
- function setFormErrors(errors) {
3138
- if (errors.length === 0) {
3139
- state.userErrors.delete(paths.FORM_ERRORS_PATH_KEY);
3140
- return;
3141
- }
3142
- state.userErrors.set(
3143
- paths.FORM_ERRORS_PATH_KEY,
3144
- errors.map((e) => ({
3145
- path: [...paths.FORM_ERRORS_PATH],
3146
- message: e.message,
3147
- formKey: state.formKey,
3148
- code: e.code ?? "atta:form-error"
3149
- }))
3150
- );
3151
- }
3152
- function clearFormErrors() {
3153
- state.userErrors.delete(paths.FORM_ERRORS_PATH_KEY);
3154
- }
3155
2968
  const submitting = vue.computed(() => state.submitting.value);
3156
2969
  const submissionAttempts = vue.computed(() => state.submissionAttempts.value);
3157
2970
  const submitted = vue.computed(() => state.submitted.value);
@@ -3320,7 +3133,6 @@ function buildFormApi(state, formInstanceId, options = {}) {
3320
3133
  instanceId: formInstanceId
3321
3134
  })
3322
3135
  );
3323
- const persistenceHandle = state.modules.get(PERSISTENCE_MODULE_KEY);
3324
3136
  const reset = (nextDefaultValues) => {
3325
3137
  if (nextDefaultValues === void 0) {
3326
3138
  state.reset();
@@ -3335,16 +3147,10 @@ function buildFormApi(state, formInstanceId, options = {}) {
3335
3147
  state.originalBlankPaths.add(pathKey);
3336
3148
  }
3337
3149
  }
3338
- if (persistenceHandle !== void 0) {
3339
- void persistenceHandle.ready.then((m) => m?.clearPersistedDraft()).catch(() => void 0);
3340
- }
3341
3150
  };
3342
3151
  const resetField = (pathInput) => {
3343
3152
  const segments = paths.canonicalizePath(pathInput).segments;
3344
3153
  state.resetField(segments);
3345
- if (persistenceHandle !== void 0) {
3346
- void persistenceHandle.ready.then((m) => m?.clearPersistedDraft(segments)).catch(() => void 0);
3347
- }
3348
3154
  };
3349
3155
  function clear(pathInput) {
3350
3156
  if (pathInput === void 0) {
@@ -3352,31 +3158,6 @@ function buildFormApi(state, formInstanceId, options = {}) {
3352
3158
  }
3353
3159
  return setValueImpl(pathInput, unset);
3354
3160
  }
3355
- const persist = async (pathInput, options2) => {
3356
- const segments = paths.canonicalizePath(pathInput).segments;
3357
- if (!paths.allowSensitivePersist(
3358
- segments,
3359
- options2?.acknowledgeSensitive === true,
3360
- state.isSensitivePath
3361
- )) {
3362
- return;
3363
- }
3364
- if (persistenceHandle === void 0) return;
3365
- const persistence = await persistenceHandle.ready;
3366
- if (persistence === void 0) return;
3367
- await persistence.writePathImmediately(segments);
3368
- };
3369
- const clearPersistedDraft = async (pathInput) => {
3370
- if (persistenceHandle === void 0) return;
3371
- const persistence = await persistenceHandle.ready;
3372
- if (persistence === void 0) return;
3373
- if (pathInput === void 0) {
3374
- await persistence.clearPersistedDraft();
3375
- return;
3376
- }
3377
- const segments = paths.canonicalizePath(pathInput).segments;
3378
- await persistence.clearPersistedDraft(segments);
3379
- };
3380
3161
  function touch(pathInput) {
3381
3162
  const segments = pathInput === void 0 ? paths.ROOT_PATH : paths.canonicalizePath(pathInput).segments;
3382
3163
  state.touchAtPath(segments);
@@ -3450,29 +3231,17 @@ function buildFormApi(state, formInstanceId, options = {}) {
3450
3231
  }
3451
3232
  const EMPTY_FIELD_RECORD = Object.freeze({});
3452
3233
  function record(path) {
3453
- const { segments } = paths.canonicalizePath(path);
3234
+ const segments = path === void 0 ? [] : paths.canonicalizePath(path).segments;
3454
3235
  const value = state.getValueAtPath(segments);
3455
3236
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
3456
3237
  return EMPTY_FIELD_RECORD;
3457
3238
  }
3458
3239
  const out = {};
3459
3240
  for (const key of Object.keys(value)) {
3460
- safeAssign(out, key, callTerminal(`${path}.${key}`));
3241
+ safeAssign(out, key, callTerminal(segments.length === 0 ? key : `${path}.${key}`));
3461
3242
  }
3462
3243
  return Object.freeze(out);
3463
3244
  }
3464
- const formHandle = {
3465
- current: void 0
3466
- };
3467
- function onChangeImpl(a, b, c) {
3468
- const sourced = typeof b === "function";
3469
- const source = sourced ? a : void 0;
3470
- const handler = sourced ? b : a;
3471
- const options2 = sourced ? c : b;
3472
- const stop = state.registerOnChange(source, handler, options2, () => formHandle.current);
3473
- if (vue.getCurrentScope() !== void 0) vue.onScopeDispose(stop);
3474
- return stop;
3475
- }
3476
3245
  const api = {
3477
3246
  handleSubmit: gated(handleSubmit),
3478
3247
  // Callable readonly Proxies (`values`, `fields`, `errors`) and the
@@ -3526,11 +3295,8 @@ function buildFormApi(state, formInstanceId, options = {}) {
3526
3295
  return errorsProxy;
3527
3296
  },
3528
3297
  toRef: gated(pathToRef),
3529
- setFieldErrors: gated(setFieldErrors),
3530
- addFieldErrors: gated(addFieldErrors),
3531
- clearFieldErrors: gated(clearFieldErrors),
3532
- setFormErrors: gated(setFormErrors),
3533
- clearFormErrors: gated(clearFormErrors),
3298
+ setErrors: gated(setErrors),
3299
+ clearErrors: gated(clearErrors),
3534
3300
  get meta() {
3535
3301
  void state.activate();
3536
3302
  return formMeta;
@@ -3538,8 +3304,6 @@ function buildFormApi(state, formInstanceId, options = {}) {
3538
3304
  reset: gated(reset),
3539
3305
  resetField: gated(resetField),
3540
3306
  clear: gated(clear),
3541
- persist: gated(persist),
3542
- clearPersistedDraft: gated(clearPersistedDraft),
3543
3307
  focusFirstError: gated(focusFirstError),
3544
3308
  scrollToFirstError: gated(scrollToFirstError),
3545
3309
  applyInvalidSubmitPolicy: gated(applyInvalidSubmitPolicyPublic),
@@ -3560,10 +3324,8 @@ function buildFormApi(state, formInstanceId, options = {}) {
3560
3324
  get blankPaths() {
3561
3325
  void state.activate();
3562
3326
  return blankPathsView;
3563
- },
3564
- onChange: onChangeImpl
3327
+ }
3565
3328
  };
3566
- formHandle.current = api;
3567
3329
  return api;
3568
3330
  }
3569
3331
 
@@ -4075,168 +3837,55 @@ function createArrayBookkeeping(deps) {
4075
3837
  };
4076
3838
  }
4077
3839
 
4078
- const NOOP_STOP = () => {
4079
- };
4080
- function isThenable(value) {
4081
- return value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
4082
- }
4083
- function isSuppressed(meta) {
4084
- return meta?.hydration === true || meta?.crossTab === true || meta?.silent === true;
4085
- }
4086
- function canonicalizeSourceList(raw) {
4087
- const list = typeof raw === "string" ? [raw] : raw;
4088
- const out = [];
4089
- const seen = /* @__PURE__ */ new Set();
4090
- for (const entry of list) {
4091
- const { segments, key } = paths.canonicalizePath(entry);
4092
- if (seen.has(key)) continue;
4093
- seen.add(key);
4094
- out.push({ segments, key, dotted: paths.segmentsToDotted(segments) });
4095
- }
4096
- return out;
3840
+ function mergeSparseHydration(schemaDefaults, sparse, schema) {
3841
+ return mergeDeep(schemaDefaults, sparse, [], schema);
4097
3842
  }
4098
- function makeResolver(source) {
4099
- if (source === void 0) {
4100
- const root = [{ segments: paths.ROOT_PATH, key: paths.ROOT_PATH_KEY, dotted: "" }];
4101
- return () => root;
4102
- }
4103
- if (typeof source !== "function" && !vue.isRef(source)) {
4104
- const fixed = canonicalizeSourceList(source);
4105
- return () => fixed;
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);
4106
3851
  }
4107
- return () => canonicalizeSourceList(vue.toValue(source));
3852
+ return mergeObjectKeys(target, source, path, schema);
4108
3853
  }
4109
- function createOnChangeRegistry(deps) {
4110
- const handlers = /* @__PURE__ */ new Set();
4111
- function isCurrent(reg, key, token) {
4112
- return reg.runs.get(key)?.token === token;
4113
- }
4114
- function routeError(reg, error, source, changed, value, previous, attempt, token) {
4115
- if (reg.onError === void 0) {
4116
- if (paths.__DEV__) {
4117
- console.error(
4118
- `[attaform] onChange handler threw for path '${source.dotted}' \u2014 error swallowed. Pass { onError } to handle it. Original error:`,
4119
- error
4120
- );
4121
- }
4122
- return;
4123
- }
4124
- const retry = () => {
4125
- if (!isCurrent(reg, source.key, token)) return;
4126
- runHandler(reg, source, changed, value, previous, attempt + 1);
4127
- };
4128
- const errCtx = {
4129
- path: source.dotted,
4130
- value,
4131
- attempt,
4132
- retry,
4133
- form: reg.getForm()
4134
- };
4135
- try {
4136
- reg.onError(error, errCtx);
4137
- } catch (err) {
4138
- if (paths.__DEV__) console.error("[attaform] onChange onError threw:", err);
4139
- }
4140
- }
4141
- function runHandler(reg, source, changed, value, previous, attempt) {
4142
- const prior = reg.runs.get(source.key);
4143
- if (prior) prior.controller.abort();
4144
- const controller = new AbortController();
4145
- const token = ++reg.seq;
4146
- reg.runs.set(source.key, { token, controller });
4147
- const ctx = {
4148
- path: source.dotted,
4149
- previous,
4150
- signal: controller.signal,
4151
- attempt,
4152
- form: reg.getForm(),
4153
- changed
4154
- };
4155
- let result;
4156
- try {
4157
- result = reg.handler(value, ctx);
4158
- } catch (error) {
4159
- routeError(reg, error, source, changed, value, previous, attempt, token);
4160
- return;
4161
- }
4162
- if (isThenable(result)) {
4163
- result.then(void 0, (error) => {
4164
- if (isCurrent(reg, source.key, token)) {
4165
- routeError(reg, error, source, changed, value, previous, attempt, token);
4166
- }
4167
- });
4168
- }
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 };
4169
3858
  }
4170
- function dispatch(patches, meta) {
4171
- if (handlers.size === 0 || isSuppressed(meta)) return;
4172
- for (const reg of handlers) {
4173
- let sources;
4174
- try {
4175
- sources = reg.resolve();
4176
- } catch (error) {
4177
- if (paths.__DEV__) console.error("[attaform] onChange source getter threw:", error);
4178
- continue;
4179
- }
4180
- for (const source of sources) {
4181
- let changed;
4182
- for (const patch of patches) {
4183
- if (paths.isPathPrefix(source.segments, patch.path) || paths.isPathPrefix(patch.path, source.segments)) {
4184
- (changed ?? (changed = [])).push(paths.segmentsToDotted(patch.path));
4185
- }
4186
- }
4187
- if (changed === void 0) continue;
4188
- const value = deps.getValueAtPath(source.segments);
4189
- const previous = reg.previous.has(source.key) ? reg.previous.get(source.key) : value;
4190
- reg.previous.set(source.key, value);
4191
- runHandler(reg, source, changed, value, previous, 0);
4192
- }
3859
+ if (sourceDisc !== void 0) {
3860
+ const variantDefault = du.getVariantDefault(sourceDisc);
3861
+ if (isPlainRecord(variantDefault)) {
3862
+ return mergeVariantKeys(source, variantDefault, path, schema, du);
4193
3863
  }
4194
3864
  }
4195
- function register(source, handler, options, getForm) {
4196
- if (deps.ssr) return NOOP_STOP;
4197
- const reg = {
4198
- handler,
4199
- onError: options?.onError,
4200
- getForm,
4201
- resolve: makeResolver(source),
4202
- previous: /* @__PURE__ */ new Map(),
4203
- runs: /* @__PURE__ */ new Map(),
4204
- seq: 0
4205
- };
4206
- try {
4207
- for (const { key, segments } of reg.resolve()) {
4208
- reg.previous.set(key, deps.getValueAtPath(segments));
4209
- }
4210
- } catch (error) {
4211
- if (paths.__DEV__) console.error("[attaform] onChange source getter threw at registration:", error);
4212
- }
4213
- handlers.add(reg);
4214
- let stopped = false;
4215
- return () => {
4216
- if (stopped) return;
4217
- stopped = true;
4218
- handlers.delete(reg);
4219
- for (const run of reg.runs.values()) run.controller.abort();
4220
- reg.runs.clear();
4221
- reg.previous.clear();
4222
- };
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
+ );
4223
3876
  }
4224
- function dispose() {
4225
- for (const reg of handlers) {
4226
- for (const run of reg.runs.values()) run.controller.abort();
4227
- reg.runs.clear();
4228
- reg.previous.clear();
4229
- }
4230
- handlers.clear();
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
+ );
4231
3887
  }
4232
- return {
4233
- get active() {
4234
- return handlers.size > 0;
4235
- },
4236
- register,
4237
- dispatch,
4238
- dispose
4239
- };
3888
+ return out;
4240
3889
  }
4241
3890
 
4242
3891
  function isHydratedFieldRecord(value) {
@@ -4372,27 +4021,8 @@ function createFormStore(options) {
4372
4021
  const formChangeListeners = /* @__PURE__ */ new Set();
4373
4022
  const submitSuccessListeners = /* @__PURE__ */ new Set();
4374
4023
  const resetListeners = /* @__PURE__ */ new Set();
4375
- const onChangeRegistry = createOnChangeRegistry({ getValueAtPath, ssr });
4376
- const persistOptIns = paths.createPersistOptInRegistry();
4377
- const noSyncPaths = /* @__PURE__ */ new Set();
4378
- const noSyncPathCounts = /* @__PURE__ */ new Map();
4379
- function incrementNoSyncOptOut(path) {
4380
- const next = (noSyncPathCounts.get(path) ?? 0) + 1;
4381
- noSyncPathCounts.set(path, next);
4382
- if (next === 1) noSyncPaths.add(path);
4383
- }
4384
- function decrementNoSyncOptOut(path) {
4385
- const current = noSyncPathCounts.get(path) ?? 0;
4386
- if (current <= 1) {
4387
- noSyncPathCounts.delete(path);
4388
- noSyncPaths.delete(path);
4389
- return;
4390
- }
4391
- noSyncPathCounts.set(path, current - 1);
4392
- }
4393
4024
  const coerceIndex = resolveCoercionIndex(options.coerce);
4394
4025
  const resolvedGetDisplayState = resolveGetDisplayState(options.getDisplayState);
4395
- const resolvedIsSensitivePath = options.isSensitivePath ?? paths.isSensitivePath;
4396
4026
  const cleanupHooks = [];
4397
4027
  const modules = /* @__PURE__ */ new Map();
4398
4028
  const fieldValidatingSince = vue.reactive(/* @__PURE__ */ new Map());
@@ -4740,7 +4370,6 @@ function createFormStore(options) {
4740
4370
  console.error("[attaform] onFormChange threw:", err);
4741
4371
  }
4742
4372
  }
4743
- if (onChangeRegistry.active) onChangeRegistry.dispatch(patches, meta);
4744
4373
  }
4745
4374
  function applyFormReplacementWithPath(next, meta, arrayOpPath) {
4746
4375
  const prev = form.value;
@@ -5181,33 +4810,18 @@ function createFormStore(options) {
5181
4810
  formChangeListeners.clear();
5182
4811
  submitSuccessListeners.clear();
5183
4812
  resetListeners.clear();
5184
- onChangeRegistry.dispose();
5185
- persistOptIns.clear();
5186
- noSyncPaths.clear();
5187
- noSyncPathCounts.clear();
5188
4813
  }
5189
4814
  function getValueAtPath(path) {
5190
4815
  return getAtPath(form.value, path);
5191
4816
  }
5192
- function rerouteFormLevelEntry(err) {
5193
- if (err.path.length === 0) {
5194
- return { ...err, path: [...paths.FORM_ERRORS_PATH] };
5195
- }
5196
- return err;
5197
- }
5198
- function pathKeyForEntry(err) {
5199
- if (err.path.length === 0) return paths.FORM_ERRORS_PATH_KEY;
5200
- return paths.canonicalizePath(err.path).key;
5201
- }
5202
4817
  function appendErrorsTo(map, entries) {
5203
4818
  for (const raw of entries) {
5204
- const err = rerouteFormLevelEntry(raw);
5205
- const key = pathKeyForEntry(err);
4819
+ const { key } = paths.canonicalizePath(raw.path);
5206
4820
  const current = map.get(key);
5207
4821
  if (current === void 0) {
5208
- map.set(key, [err]);
4822
+ map.set(key, [raw]);
5209
4823
  } else {
5210
- map.set(key, [...current, err]);
4824
+ map.set(key, [...current, raw]);
5211
4825
  }
5212
4826
  }
5213
4827
  }
@@ -5232,14 +4846,13 @@ function createFormStore(options) {
5232
4846
  schemaErrors.set(key, [...entries]);
5233
4847
  }
5234
4848
  function applySchemaErrorsForSubtree(path, entries) {
5235
- const parentKey = path.length === 0 ? paths.FORM_ERRORS_PATH_KEY : paths.canonicalizePath(path).key;
4849
+ const parentKey = paths.canonicalizePath(path).key;
5236
4850
  const grouped = /* @__PURE__ */ new Map();
5237
4851
  for (const raw of entries) {
5238
- const err = rerouteFormLevelEntry(raw);
5239
- const key = pathKeyForEntry(err);
4852
+ const { key } = paths.canonicalizePath(raw.path);
5240
4853
  const list = grouped.get(key);
5241
- if (list === void 0) grouped.set(key, [err]);
5242
- else list.push(err);
4854
+ if (list === void 0) grouped.set(key, [raw]);
4855
+ else list.push(raw);
5243
4856
  }
5244
4857
  if (!grouped.has(parentKey)) schemaErrors.delete(parentKey);
5245
4858
  for (const existingKey of [...schemaErrors.keys()]) {
@@ -5261,8 +4874,13 @@ function createFormStore(options) {
5261
4874
  function setAllUserErrors(entries) {
5262
4875
  replaceErrorsIn(userErrors, entries);
5263
4876
  }
5264
- function addUserErrors(entries) {
5265
- appendErrorsTo(userErrors, entries);
4877
+ function setUserErrorsForPath(path, entries) {
4878
+ const { key } = paths.canonicalizePath(path);
4879
+ if (entries.length === 0) {
4880
+ userErrors.delete(key);
4881
+ return;
4882
+ }
4883
+ userErrors.set(key, [...entries]);
5266
4884
  }
5267
4885
  function clearUserErrors(path) {
5268
4886
  clearErrorsIn(userErrors, path);
@@ -5456,25 +5074,25 @@ function createFormStore(options) {
5456
5074
  }
5457
5075
  }
5458
5076
  function clearHydrationFailedEntry() {
5459
- const existing = schemaErrors.get(paths.FORM_ERRORS_PATH_KEY);
5077
+ const existing = schemaErrors.get(paths.ROOT_PATH_KEY);
5460
5078
  if (existing === void 0) return;
5461
5079
  const filtered = existing.filter((e) => e.code !== AttaformErrorCode.HydrationFailed);
5462
5080
  if (filtered.length === 0) {
5463
- schemaErrors.delete(paths.FORM_ERRORS_PATH_KEY);
5081
+ schemaErrors.delete(paths.ROOT_PATH_KEY);
5464
5082
  } else {
5465
- schemaErrors.set(paths.FORM_ERRORS_PATH_KEY, filtered);
5083
+ schemaErrors.set(paths.ROOT_PATH_KEY, filtered);
5466
5084
  }
5467
5085
  }
5468
5086
  function appendHydrationFailedEntry(error) {
5469
5087
  const message = error instanceof Error ? error.message : typeof error === "string" ? error : "Hydration failed";
5470
5088
  const entry = {
5471
5089
  message,
5472
- path: [...paths.FORM_ERRORS_PATH],
5090
+ path: [...paths.ROOT_PATH],
5473
5091
  formKey,
5474
5092
  code: AttaformErrorCode.HydrationFailed
5475
5093
  };
5476
- const existing = schemaErrors.get(paths.FORM_ERRORS_PATH_KEY) ?? [];
5477
- schemaErrors.set(paths.FORM_ERRORS_PATH_KEY, [...existing, entry]);
5094
+ const existing = schemaErrors.get(paths.ROOT_PATH_KEY) ?? [];
5095
+ schemaErrors.set(paths.ROOT_PATH_KEY, [...existing, entry]);
5478
5096
  return entry;
5479
5097
  }
5480
5098
  function reset(nextDefaultValues) {
@@ -5487,7 +5105,7 @@ function createFormStore(options) {
5487
5105
  });
5488
5106
  const next = resetResponse.data;
5489
5107
  rebuildAuthoredPaths(resetSource, next);
5490
- applyFormReplacement(next, { silent: true });
5108
+ applyFormReplacement(next);
5491
5109
  arrayIdentity.rebaselineAll();
5492
5110
  originals.clear();
5493
5111
  diffAndApply({}, next, [], (patch) => {
@@ -5707,7 +5325,7 @@ function createFormStore(options) {
5707
5325
  clearSchemaErrors,
5708
5326
  applySchemaErrorsForSubtree,
5709
5327
  setAllUserErrors,
5710
- addUserErrors,
5328
+ setUserErrorsForPath,
5711
5329
  clearUserErrors,
5712
5330
  getErrorsForPath,
5713
5331
  ensurePathOrdinal,
@@ -5733,7 +5351,6 @@ function createFormStore(options) {
5733
5351
  settleTransforms,
5734
5352
  scheduleFieldValidation,
5735
5353
  onFormChange,
5736
- registerOnChange: onChangeRegistry.register,
5737
5354
  onSubmitSuccess,
5738
5355
  onReset,
5739
5356
  emitSubmitSuccess,
@@ -5741,11 +5358,6 @@ function createFormStore(options) {
5741
5358
  registerDrain,
5742
5359
  awaitPendingWrites,
5743
5360
  modules,
5744
- persistOptIns,
5745
- isSensitivePath: resolvedIsSensitivePath,
5746
- noSyncPaths,
5747
- incrementNoSyncOptOut,
5748
- decrementNoSyncOptOut,
5749
5361
  coerceIndex,
5750
5362
  blankPaths,
5751
5363
  originalBlankPaths,
@@ -5753,6 +5365,33 @@ function createFormStore(options) {
5753
5365
  };
5754
5366
  }
5755
5367
 
5368
+ function captureUserCallSite() {
5369
+ const raw = new Error().stack;
5370
+ if (typeof raw !== "string") return void 0;
5371
+ const lines = raw.split("\n");
5372
+ for (let i = 1; i < lines.length; i++) {
5373
+ const frame = lines[i];
5374
+ if (frame === void 0) continue;
5375
+ if (/attaform[/-]forms?/i.test(frame)) continue;
5376
+ if (/\bforms\.[A-Za-z0-9_-]+\.m?js\b/.test(frame)) continue;
5377
+ const trimmed = frame.trim();
5378
+ if (trimmed.length === 0) continue;
5379
+ return shortenSourceFrame(trimmed);
5380
+ }
5381
+ return void 0;
5382
+ }
5383
+ function shortenSourceFrame(frame) {
5384
+ const match = /(?:^|\s|\()([^\s()]+):(\d+):\d+\)?$/.exec(frame);
5385
+ if (match === null) return frame;
5386
+ const [, urlOrPath, line] = match;
5387
+ if (urlOrPath === void 0 || line === void 0) return frame;
5388
+ let path = urlOrPath;
5389
+ path = path.replace(/^[a-z]+:\/\/[^/]+\//i, "");
5390
+ path = path.replace(/^_nuxt\//, "");
5391
+ path = path.replace(/^\//, "");
5392
+ return `(${path}:${line})`;
5393
+ }
5394
+
5756
5395
  function getComputedSchema(formKey, schemaOrCallback, options) {
5757
5396
  if (typeof schemaOrCallback === "function") return schemaOrCallback(formKey, options);
5758
5397
  return schemaOrCallback;
@@ -5763,12 +5402,38 @@ function captureErrorEntries(map) {
5763
5402
  for (const [k, v] of map) out.push([k, [...v]]);
5764
5403
  return out;
5765
5404
  }
5405
+ function dataEqual(a, b) {
5406
+ if (a === b) return true;
5407
+ if (a === null || b === null || a === void 0 || b === void 0) return false;
5408
+ if (typeof a !== typeof b) return false;
5409
+ if (Array.isArray(a)) {
5410
+ if (!Array.isArray(b) || a.length !== b.length) return false;
5411
+ for (let i = 0; i < a.length; i++) {
5412
+ if (!dataEqual(a[i], b[i])) return false;
5413
+ }
5414
+ return true;
5415
+ }
5416
+ if (Array.isArray(b)) return false;
5417
+ if (typeof a === "object") {
5418
+ const ao = a;
5419
+ const bo = b;
5420
+ const keys = Object.keys(ao);
5421
+ if (keys.length !== Object.keys(bo).length) return false;
5422
+ for (const k of keys) {
5423
+ if (!Object.prototype.hasOwnProperty.call(bo, k)) return false;
5424
+ if (!dataEqual(ao[k], bo[k])) return false;
5425
+ }
5426
+ return true;
5427
+ }
5428
+ return false;
5429
+ }
5766
5430
  function errorFieldsEqual(av, bvi) {
5767
5431
  if (av === bvi) return true;
5768
5432
  if (av.message !== bvi.message) return false;
5769
5433
  if (av.code !== bvi.code) return false;
5770
5434
  if (av.formKey !== bvi.formKey) return false;
5771
- return av.path === bvi.path || paths.pathsEqual(av.path, bvi.path);
5435
+ if (av.path !== bvi.path && !paths.pathsEqual(av.path, bvi.path)) return false;
5436
+ return dataEqual(av.data, bvi.data);
5772
5437
  }
5773
5438
  function errorsEqual(a, b) {
5774
5439
  if (a.length !== b.length) return false;
@@ -5862,10 +5527,6 @@ function createHistoryModule(state, config) {
5862
5527
  clear();
5863
5528
  return;
5864
5529
  }
5865
- if (meta?.crossTab === true) {
5866
- currentSnapshot.value = captureSnapshot();
5867
- return;
5868
- }
5869
5530
  const newSnap = captureSnapshot();
5870
5531
  const prevSnap = currentSnapshot.value;
5871
5532
  const formPatches = [];
@@ -5886,9 +5547,7 @@ function createHistoryModule(state, config) {
5886
5547
  suppressNext = true;
5887
5548
  state.blankPaths.clear();
5888
5549
  for (const key of snap.blankPaths) state.blankPaths.add(key);
5889
- state.applyFormReplacement(snap.form, {
5890
- persist: !state.persistOptIns.isEmpty()
5891
- });
5550
+ state.applyFormReplacement(snap.form);
5892
5551
  const schemaFlat = snap.schemaErrors.flatMap(([, errs]) => errs);
5893
5552
  const userFlat = snap.userErrors.flatMap(([, errs]) => errs);
5894
5553
  state.setAllSchemaErrors(schemaFlat);
@@ -5939,28 +5598,6 @@ function createHistoryModule(state, config) {
5939
5598
  };
5940
5599
  }
5941
5600
 
5942
- const warned = /* @__PURE__ */ new Set();
5943
- function warnOnceInsecureContext(feature) {
5944
- if (!paths.__DEV__) return;
5945
- if (warned.has(feature)) return;
5946
- warned.add(feature);
5947
- const message = featureMessage(feature);
5948
- console.warn(`[attaform] ${message}`);
5949
- }
5950
- function featureMessage(feature) {
5951
- switch (feature) {
5952
- case "multiTab":
5953
- 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.";
5954
- case "persist:local":
5955
- 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.";
5956
- case "persist:session":
5957
- 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.";
5958
- }
5959
- }
5960
- function isSecureContext() {
5961
- return typeof window !== "undefined" && window.isSecureContext === true;
5962
- }
5963
-
5964
5601
  function resolveTrichotomy(input) {
5965
5602
  if (typeof input === "function") {
5966
5603
  return { kind: "async", factory: input };
@@ -5989,18 +5626,10 @@ function useAbstractForm(configuration, options) {
5989
5626
  defaultValue: DEFAULT_MAX_RECURSION_DEPTH
5990
5627
  });
5991
5628
  const resolvedSchema = getComputedSchema(key, configuration.schema, { maxRecursionDepth });
5992
- if (configuration.persist !== void 0 && configuration.key === void 0) {
5993
- throw new paths.AnonPersistError({
5994
- cause: "no-key",
5995
- schemaFields: extractSchemaFields(resolvedSchema),
5996
- callSite: captureUserCallSite()
5997
- });
5998
- }
5999
5629
  const existing = registry.forms.get(key);
6000
5630
  if (paths.__DEV__ && existing !== void 0) {
6001
5631
  void import('../chunks/dev-key-collision-warnings.cjs').then((m) => {
6002
5632
  void m.warnOnSchemaFingerprintMismatch(key, existing.schema, resolvedSchema);
6003
- m.warnOnPersistDivergence(key, existing, configuration.persist);
6004
5633
  });
6005
5634
  }
6006
5635
  const hadPendingHydration = registry.pendingHydration.has(key);
@@ -6028,85 +5657,6 @@ function useAbstractForm(configuration, options) {
6028
5657
  const releaseConsumer = registry.trackConsumer(key);
6029
5658
  vue.onScopeDispose(releaseConsumer);
6030
5659
  }
6031
- const persistDisabledByAnonRule = merged.persist !== void 0 && enforceAnonPersistRule(state.formKey, registry.ssr);
6032
- if (existing === void 0 && !registry.ssr) {
6033
- if (merged.persist !== void 0 && !persistDisabledByAnonRule) {
6034
- const resolvedPersist = normalizePersistConfig(merged.persist);
6035
- const storageKind = resolvedPersist.storage;
6036
- const isBuiltinStorage = typeof storageKind === "string";
6037
- const secureContextOk = !isBuiltinStorage || isSecureContext();
6038
- if (!secureContextOk) {
6039
- const feature = storageKind === "session" ? "persist:session" : "persist:local";
6040
- warnOnceInsecureContext(feature);
6041
- void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
6042
- } else {
6043
- const persistenceBase = resolveStorageKeyBase(resolvedPersist, state.formKey);
6044
- void sweepNonConfiguredStandardStoresForOrphans(resolvedPersist.storage, persistenceBase);
6045
- const adapterPromise = getStorageAdapter(resolvedPersist.storage);
6046
- let persistDisposed = false;
6047
- state.registerCleanup(() => {
6048
- persistDisposed = true;
6049
- });
6050
- const ready = (async () => {
6051
- try {
6052
- const [{ wirePersistence }, fingerprintToken] = await Promise.all([
6053
- import('../chunks/wire-persistence.cjs'),
6054
- resolvePersistFingerprintToken(state)
6055
- ]);
6056
- if (persistDisposed) return void 0;
6057
- const persistenceModule = wirePersistence(
6058
- state,
6059
- resolvedPersist,
6060
- adapterPromise,
6061
- fingerprintToken
6062
- );
6063
- state.registerDrain(() => persistenceModule.awaitPendingWrites());
6064
- state.registerCleanup(() => persistenceModule.dispose());
6065
- return persistenceModule;
6066
- } catch {
6067
- return void 0;
6068
- }
6069
- })();
6070
- const persistenceHandle = { config: resolvedPersist, ready };
6071
- state.modules.set(PERSISTENCE_MODULE_KEY, persistenceHandle);
6072
- }
6073
- } else {
6074
- void sweepAllOrphansAcrossStandardStores(`${PERSISTENCE_KEY_PREFIX}${state.formKey}`);
6075
- }
6076
- }
6077
- if (existing === void 0 && merged.multiTab === true && configuration.key !== void 0 && !registry.ssr) {
6078
- const hasBroadcastChannel = typeof BroadcastChannel !== "undefined";
6079
- const secureContext = isSecureContext();
6080
- if (hasBroadcastChannel && secureContext) {
6081
- let formDisposed = false;
6082
- state.registerCleanup(() => {
6083
- formDisposed = true;
6084
- });
6085
- void (async () => {
6086
- try {
6087
- const [{ createMultiTabSyncModule, MULTI_TAB_SYNC_MODULE_KEY }, fingerprint] = await Promise.all([import('../chunks/multi-tab-sync.cjs'), state.schema.fingerprint()]);
6088
- if (formDisposed) return;
6089
- const channelName = `attaform:sync:${state.formKey}:${hashStableString(fingerprint)}`;
6090
- const syncModule = createMultiTabSyncModule(state, channelName, {
6091
- isSensitivePath: state.isSensitivePath,
6092
- noSyncPaths: state.noSyncPaths,
6093
- validateForm: (form) => {
6094
- const result = state.schema.validateAtPath(form, void 0, { sync: true });
6095
- if (result instanceof Promise) return;
6096
- if (!result.success) {
6097
- throw new Error("attaform multi-tab sync: post-apply schema validation failed");
6098
- }
6099
- }
6100
- });
6101
- state.modules.set(MULTI_TAB_SYNC_MODULE_KEY, syncModule);
6102
- state.registerCleanup(() => syncModule.dispose());
6103
- } catch {
6104
- }
6105
- })();
6106
- } else if (hasBroadcastChannel && !secureContext) {
6107
- warnOnceInsecureContext("multiTab");
6108
- }
6109
- }
6110
5660
  if (existing === void 0 && merged.history !== void 0) {
6111
5661
  const historyModule = createHistoryModule(state, merged.history);
6112
5662
  state.modules.set(HISTORY_MODULE_KEY, historyModule);
@@ -6148,14 +5698,6 @@ function useAbstractForm(configuration, options) {
6148
5698
  apiOptions.autoAria = merged.autoAria;
6149
5699
  }
6150
5700
  const api = buildFormApi(state, formInstanceId, apiOptions);
6151
- const onChangeConfig = configuration.onChange;
6152
- if (onChangeConfig !== void 0) {
6153
- const handler = typeof onChangeConfig === "function" ? onChangeConfig : onChangeConfig.handler;
6154
- const onError = typeof onChangeConfig === "function" ? void 0 : onChangeConfig.onError;
6155
- const options2 = onError === void 0 ? void 0 : { onError };
6156
- const stop = state.registerOnChange(void 0, handler, options2, () => api);
6157
- if (vue.getCurrentScope() !== void 0) vue.onScopeDispose(stop);
6158
- }
6159
5701
  return api;
6160
5702
  }
6161
5703
  function mergeWithDefaults(defaults, configuration) {
@@ -6168,8 +5710,6 @@ function mergeWithDefaults(defaults, configuration) {
6168
5710
  const debounceMs = configuration.debounceMs ?? defaults.debounceMs;
6169
5711
  const getDisplayState = configuration.getDisplayState ?? defaults.getDisplayState;
6170
5712
  const maxRecursionDepth = configuration.maxRecursionDepth ?? defaults.maxRecursionDepth;
6171
- const sensitiveNames = configuration.sensitiveNames ?? defaults.sensitiveNames;
6172
- const multiTab = configuration.multiTab ?? defaults.multiTab;
6173
5713
  const autoAria = configuration.autoAria ?? defaults.autoAria;
6174
5714
  return {
6175
5715
  ...configuration,
@@ -6182,8 +5722,6 @@ function mergeWithDefaults(defaults, configuration) {
6182
5722
  ...debounceMs === void 0 ? {} : { debounceMs },
6183
5723
  ...getDisplayState === void 0 ? {} : { getDisplayState },
6184
5724
  ...maxRecursionDepth === void 0 ? {} : { maxRecursionDepth },
6185
- ...sensitiveNames === void 0 ? {} : { sensitiveNames },
6186
- ...multiTab === void 0 ? {} : { multiTab },
6187
5725
  ...autoAria === void 0 ? {} : { autoAria }
6188
5726
  };
6189
5727
  }
@@ -6199,8 +5737,6 @@ function buildFreshState(key, schema, configuration, registry) {
6199
5737
  if (pending === void 0) {
6200
5738
  initialBlankPaths = walked.paths;
6201
5739
  }
6202
- const resolvedSensitiveNames = configuration.sensitiveNames;
6203
- const resolvedIsSensitivePath = resolvedSensitiveNames === void 0 ? void 0 : paths.createIsSensitivePath(resolvedSensitiveNames);
6204
5740
  const createOptions = {
6205
5741
  formKey: key,
6206
5742
  schema,
@@ -6228,8 +5764,7 @@ function buildFreshState(key, schema, configuration, registry) {
6228
5764
  ...configuration.rememberVariants !== void 0 ? { rememberVariants: configuration.rememberVariants } : {},
6229
5765
  ...configuration.coerce !== void 0 ? { coerce: configuration.coerce } : {},
6230
5766
  ...configuration.getDisplayState !== void 0 ? { getDisplayState: configuration.getDisplayState } : {},
6231
- ...initialBlankPaths !== void 0 ? { initialBlankPaths } : {},
6232
- ...resolvedIsSensitivePath !== void 0 ? { isSensitivePath: resolvedIsSensitivePath } : {}
5767
+ ...initialBlankPaths !== void 0 ? { initialBlankPaths } : {}
6233
5768
  };
6234
5769
  const state = createFormStore(createOptions);
6235
5770
  registry.forms.set(
@@ -6268,34 +5803,6 @@ function resolveFormKey(key) {
6268
5803
  }
6269
5804
  return `${ANONYMOUS_FORM_KEY_PREFIX}${anonCounter++}`;
6270
5805
  }
6271
- async function resolvePersistFingerprintToken(state) {
6272
- try {
6273
- return hashStableString(await state.schema.fingerprint());
6274
- } catch (err) {
6275
- if (paths.__DEV__) {
6276
- console.warn(
6277
- `[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.`
6278
- );
6279
- }
6280
- return "unfingerprinted";
6281
- }
6282
- }
6283
- const warnedAnonPersistKeys = /* @__PURE__ */ new Set();
6284
- function enforceAnonPersistRule(formKey, ssr) {
6285
- if (!formKey.startsWith(ANONYMOUS_FORM_KEY_PREFIX)) return false;
6286
- if (paths.__DEV__)
6287
- throw new paths.AnonPersistError({
6288
- cause: "no-key",
6289
- callSite: captureUserCallSite()
6290
- });
6291
- if (!ssr && !warnedAnonPersistKeys.has(formKey)) {
6292
- warnedAnonPersistKeys.add(formKey);
6293
- console.warn(
6294
- "[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: '...' })"
6295
- );
6296
- }
6297
- return true;
6298
- }
6299
5806
 
6300
5807
  let injectedInstanceCounter = 0;
6301
5808
  function injectForm(input) {
@@ -7523,16 +7030,10 @@ function warnIfAmbientWizardProviderHadDuplicates() {
7523
7030
  }
7524
7031
 
7525
7032
  exports.AttaformErrorCode = AttaformErrorCode;
7526
- exports.DEFAULT_PERSISTENCE_DEBOUNCE_MS = DEFAULT_PERSISTENCE_DEBOUNCE_MS;
7527
7033
  exports.DEFAULT_TIMINGS = DEFAULT_TIMINGS;
7528
- exports.PERSISTENCE_MODULE_KEY = PERSISTENCE_MODULE_KEY;
7529
- exports.applyPatchesForward = applyPatchesForward;
7530
- exports.cleanupOrphanKeys = cleanupOrphanKeys;
7531
7034
  exports.defaultCoercionRules = defaultCoercionRules;
7532
7035
  exports.defaultDisplayState = defaultDisplayState;
7533
7036
  exports.defineCoercion = defineCoercion;
7534
- exports.deleteAtPath = deleteAtPath;
7535
- exports.diffAndApply = diffAndApply;
7536
7037
  exports.getAtPath = getAtPath;
7537
7038
  exports.humanize = humanize;
7538
7039
  exports.injectForm = injectForm;
@@ -7541,17 +7042,12 @@ exports.isPlainRecord = isPlainRecord;
7541
7042
  exports.isUnset = isUnset;
7542
7043
  exports.lazy = lazy;
7543
7044
  exports.makeDefaultDisplayState = makeDefaultDisplayState;
7544
- exports.mergeSparseHydration = mergeSparseHydration;
7545
- exports.normalizeNumericOption = normalizeNumericOption;
7546
- exports.normalizePersistConfig = normalizePersistConfig;
7547
- exports.resolveStorageKeyBase = resolveStorageKeyBase;
7548
7045
  exports.safeAssign = safeAssign;
7549
7046
  exports.safeOwnRead = safeOwnRead;
7550
7047
  exports.setAtPath = setAtPath;
7551
7048
  exports.slimKindOf = slimKindOf;
7552
- exports.structuralSnapshot = structuralSnapshot;
7553
7049
  exports.unset = unset;
7554
7050
  exports.useAbstractForm = useAbstractForm;
7555
7051
  exports.useRegister = useRegister;
7556
7052
  exports.useWizard = useWizard;
7557
- //# sourceMappingURL=attaform.CjMcwV7W.cjs.map
7053
+ //# sourceMappingURL=attaform.C42wL7EJ.cjs.map