attaform 0.23.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 (85) hide show
  1. package/README.md +1 -1
  2. package/dist/chunks/devtools.cjs +1 -1
  3. package/dist/chunks/devtools.mjs +1 -1
  4. package/dist/chunks/fingerprint2.cjs +1 -1
  5. package/dist/chunks/fingerprint2.mjs +1 -1
  6. package/dist/index.cjs +3 -149
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.d.cts +6 -132
  9. package/dist/index.d.mts +6 -132
  10. package/dist/index.d.ts +6 -132
  11. package/dist/index.mjs +4 -150
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/nuxt.d.cts +1 -1
  14. package/dist/nuxt.d.mts +1 -1
  15. package/dist/nuxt.d.ts +1 -1
  16. package/dist/runtime/components/AttaformDevtoolsPanel.vue +2 -2
  17. package/dist/runtime/plugins/attaform.cjs +2 -2
  18. package/dist/runtime/plugins/attaform.mjs +2 -2
  19. package/dist/shared/{attaform.CzVta5o2.mjs → attaform.BJ_W7q3U.mjs} +8 -6
  20. package/dist/shared/attaform.BJ_W7q3U.mjs.map +1 -0
  21. package/dist/shared/{attaform.D52oJiYC.d.cts → attaform.BNmkKz0q.d.mts} +38 -6
  22. package/dist/shared/{attaform.CaYj3ZfY.cjs → attaform.BV_HyaMO.cjs} +6 -4
  23. package/dist/shared/attaform.BV_HyaMO.cjs.map +1 -0
  24. package/dist/shared/{attaform.Cmb_LCie.cjs → attaform.BnUXV01g.cjs} +4 -4
  25. package/dist/shared/attaform.BnUXV01g.cjs.map +1 -0
  26. package/dist/shared/{attaform.alpG7rT7.mjs → attaform.C-dAB90u.mjs} +4 -4
  27. package/dist/shared/attaform.C-dAB90u.mjs.map +1 -0
  28. package/dist/shared/{attaform.DsQkXE3o.cjs → attaform.C42wL7EJ.cjs} +195 -179
  29. package/dist/shared/attaform.C42wL7EJ.cjs.map +1 -0
  30. package/dist/shared/{attaform.BGMRvckW.d.ts → attaform.C6eE50re.d.ts} +18 -11
  31. package/dist/shared/{attaform.Dx9-QQE2.mjs → attaform.CAWKNCzc.mjs} +2 -2
  32. package/dist/shared/{attaform.Dx9-QQE2.mjs.map → attaform.CAWKNCzc.mjs.map} +1 -1
  33. package/dist/shared/{attaform.CtJOd7ox.mjs → attaform.CuBdtfbe.mjs} +196 -179
  34. package/dist/shared/attaform.CuBdtfbe.mjs.map +1 -0
  35. package/dist/shared/{attaform.WEwfXcHq.d.ts → attaform.CwFZGv5-.d.ts} +38 -6
  36. package/dist/shared/{attaform.DuPneYR0.d.cts → attaform.DdUYEhkV.d.cts} +18 -11
  37. package/dist/shared/attaform.DdjDqTah.d.cts +56 -0
  38. package/dist/shared/attaform.DdjDqTah.d.mts +56 -0
  39. package/dist/shared/attaform.DdjDqTah.d.ts +56 -0
  40. package/dist/shared/{attaform.BhI9Icek.mjs → attaform.Df-s8j1X.mjs} +2 -4
  41. package/dist/shared/attaform.Df-s8j1X.mjs.map +1 -0
  42. package/dist/shared/{attaform.DrY8srOp.d.mts → attaform.DiWNbKWa.d.mts} +18 -11
  43. package/dist/shared/{attaform.Db4E4IW6.cjs → attaform.DwkU0oY9.cjs} +1 -5
  44. package/dist/shared/attaform.DwkU0oY9.cjs.map +1 -0
  45. package/dist/shared/{attaform.BJnNK75Y.d.cts → attaform.GJbSmwLB.d.cts} +181 -156
  46. package/dist/shared/{attaform.BJnNK75Y.d.mts → attaform.GJbSmwLB.d.mts} +181 -156
  47. package/dist/shared/{attaform.BJnNK75Y.d.ts → attaform.GJbSmwLB.d.ts} +181 -156
  48. package/dist/shared/{attaform.DCkSNnPr.d.mts → attaform.K-3glmiT.d.cts} +38 -6
  49. package/dist/shared/{attaform.DbyTD8N2.cjs → attaform.Z1qTwOYE.cjs} +8 -6
  50. package/dist/shared/attaform.Z1qTwOYE.cjs.map +1 -0
  51. package/dist/shared/{attaform.BibT5AS_.cjs → attaform.nycEksJn.cjs} +2 -2
  52. package/dist/shared/{attaform.BibT5AS_.cjs.map → attaform.nycEksJn.cjs.map} +1 -1
  53. package/dist/shared/{attaform.Dd1Kmmaj.mjs → attaform.o95Kjd3U.mjs} +6 -4
  54. package/dist/shared/attaform.o95Kjd3U.mjs.map +1 -0
  55. package/dist/zod-v3.cjs +2 -2
  56. package/dist/zod-v3.d.cts +12 -11
  57. package/dist/zod-v3.d.mts +12 -11
  58. package/dist/zod-v3.d.ts +12 -11
  59. package/dist/zod-v3.mjs +2 -2
  60. package/dist/zod-v4.cjs +2 -2
  61. package/dist/zod-v4.d.cts +6 -5
  62. package/dist/zod-v4.d.mts +6 -5
  63. package/dist/zod-v4.d.ts +6 -5
  64. package/dist/zod-v4.mjs +2 -2
  65. package/dist/zod.cjs +5 -5
  66. package/dist/zod.cjs.map +1 -1
  67. package/dist/zod.d.cts +21 -52
  68. package/dist/zod.d.mts +21 -52
  69. package/dist/zod.d.ts +21 -52
  70. package/dist/zod.mjs +5 -5
  71. package/dist/zod.mjs.map +1 -1
  72. package/package.json +1 -1
  73. package/dist/shared/attaform.BhI9Icek.mjs.map +0 -1
  74. package/dist/shared/attaform.CaYj3ZfY.cjs.map +0 -1
  75. package/dist/shared/attaform.Cmb_LCie.cjs.map +0 -1
  76. package/dist/shared/attaform.CtJOd7ox.mjs.map +0 -1
  77. package/dist/shared/attaform.CzVta5o2.mjs.map +0 -1
  78. package/dist/shared/attaform.Db4E4IW6.cjs.map +0 -1
  79. package/dist/shared/attaform.DbyTD8N2.cjs.map +0 -1
  80. package/dist/shared/attaform.Dd1Kmmaj.mjs.map +0 -1
  81. package/dist/shared/attaform.DsQkXE3o.cjs.map +0 -1
  82. package/dist/shared/attaform.alpG7rT7.mjs.map +0 -1
  83. package/dist/shared/attaform.nf83TIR5.d.cts +0 -35
  84. package/dist/shared/attaform.nf83TIR5.d.mts +0 -35
  85. 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.Db4E4IW6.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,50 +649,6 @@ 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
653
  const DEFAULT_HISTORY_MAX_SNAPSHOTS = 128;
646
654
  const RESERVED_KEY_PREFIX = "__atta:";
@@ -1033,7 +1041,7 @@ function aggregateErrorsAt(state, prefix) {
1033
1041
  const segs = paths.segmentsForPathKey(pathKey);
1034
1042
  if (segs === null) continue;
1035
1043
  if (!paths.isPathPrefix(prefix, segs)) continue;
1036
- 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;
1037
1045
  const ordinal = state.ensurePathOrdinal(pathKey);
1038
1046
  const existing = buckets.get(ordinal);
1039
1047
  if (existing === void 0) buckets.set(ordinal, [...list]);
@@ -1152,7 +1160,12 @@ function buildSurfaceProxy(opts) {
1152
1160
  const proxy = new Proxy(target, {
1153
1161
  apply(_, __, args) {
1154
1162
  const arg = args[0];
1155
- 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
+ }
1156
1169
  const { segments: argSegs } = paths.canonicalizePath(arg);
1157
1170
  return opts.resolveCallTarget(argSegs);
1158
1171
  },
@@ -1363,8 +1376,7 @@ function buildErrorsProxy(state) {
1363
1376
  return merged;
1364
1377
  }
1365
1378
  const { key } = paths.canonicalizePath(path);
1366
- const isFormLevel = key === paths.FORM_ERRORS_PATH_KEY;
1367
- const active = isFormLevel || hasAtPath(state.form.value, path);
1379
+ const active = hasAtPath(state.form.value, path);
1368
1380
  collectAtKey(key, active, merged);
1369
1381
  return merged;
1370
1382
  },
@@ -1372,37 +1384,42 @@ function buildErrorsProxy(state) {
1372
1384
  // undefined) IS the terminal.
1373
1385
  materializeContainer: (segments) => materializeErrors(state, segments),
1374
1386
  // 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.
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.
1384
1395
  isTerminalAt: (segs) => segs.length >= 1 && segs[segs.length - 1] === "",
1385
1396
  // Call-form aggregates: `form.errors(path)` returns a single
1386
- // `ValidationError[]` for any depth (leaf or container) same
1397
+ // `ValidationError[]` for any depth (leaf or container) via the
1387
1398
  // 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),
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, []),
1395
1413
  // Enumeration unions the live form-data keys at this path with the
1396
1414
  // first-child segments drawn from every error store. Without the
1397
1415
  // 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']`).
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.
1406
1423
  //
1407
1424
  // The union closes that gap so `ownKeys` agrees with the rest of
1408
1425
  // the surface. Active-path filter mirrors `resolveLeaf`:
@@ -1424,10 +1441,6 @@ function errorAwareContainerKeys(state, segments) {
1424
1441
  const walk = (store, applyActivePathFilter) => {
1425
1442
  for (const [pathKey, errors] of store) {
1426
1443
  if (errors.length === 0) continue;
1427
- if (pathKey === paths.FORM_ERRORS_PATH_KEY) {
1428
- if (segments.length === 0) keys.add("");
1429
- continue;
1430
- }
1431
1444
  const decoded = paths.segmentsForPathKey(pathKey);
1432
1445
  if (decoded === null) continue;
1433
1446
  if (decoded.length <= segments.length) continue;
@@ -1450,31 +1463,25 @@ function materializeErrors(state, containerSegments) {
1450
1463
  if (errors.length === 0) continue;
1451
1464
  const fullPath = paths.segmentsForPathKey(pathKey);
1452
1465
  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) {
1466
+ if (fullPath.length === 0) {
1467
+ if (containerSegments.length === 0) placeAt(tree, [paths.ROOT_PATH_KEY], errors);
1460
1468
  continue;
1461
1469
  }
1462
- if (applyActivePathFilter && !isSyntheticFormLevel && !hasAtPath(state.form.value, fullPath))
1463
- 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);
1464
1476
  let placePath;
1465
- if (isSyntheticFormLevel) {
1477
+ if (relativePath.length === 0) {
1466
1478
  placePath = [""];
1479
+ } else if (state.schema.isLeafAtPath(fullPath)) {
1480
+ placePath = relativePath;
1481
+ } else if (state.schema.getSlimPrimitiveTypesAtPath(fullPath).size > 0) {
1482
+ placePath = [...relativePath, ""];
1467
1483
  } 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
- }
1484
+ placePath = relativePath;
1478
1485
  }
1479
1486
  placeAt(tree, placePath, errors);
1480
1487
  }
@@ -2908,62 +2915,56 @@ function buildFormApi(state, formInstanceId, options = {}) {
2908
2915
  return true;
2909
2916
  }
2910
2917
  const errorsProxy = buildErrorsProxy(state);
2911
- function filterToOwnFormKey(errors, op) {
2912
- const own = [];
2913
- let dropped = 0;
2914
- for (const e of errors) {
2915
- if (e.formKey === state.formKey) own.push(e);
2916
- else dropped++;
2917
- }
2918
- if (paths.__DEV__ && dropped > 0) {
2919
- console.warn(
2920
- `[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.`
2921
- );
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
+ };
2922
2926
  }
2923
- 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;
2924
2935
  }
2925
- function setFieldErrors(errors) {
2926
- const preserved = state.userErrors.get(paths.FORM_ERRORS_PATH_KEY);
2927
- state.setAllUserErrors(filterToOwnFormKey(errors, "setFieldErrors"));
2928
- if (preserved !== void 0 && preserved.length > 0) {
2929
- 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;
2930
2953
  }
2954
+ const input = arg1;
2955
+ const resolved = typeof input === "function" ? input(flattenUserErrors()) : input;
2956
+ state.setAllUserErrors(normalizeErrorInputs(resolved, void 0));
2931
2957
  }
2932
- function addFieldErrors(errors) {
2933
- state.addUserErrors(filterToOwnFormKey(errors, "addFieldErrors"));
2934
- }
2935
- function clearFieldErrors(path) {
2958
+ function clearErrors(path) {
2936
2959
  if (path === void 0) {
2937
- const preserved = state.userErrors.get(paths.FORM_ERRORS_PATH_KEY);
2938
2960
  state.clearSchemaErrors();
2939
2961
  state.clearUserErrors();
2940
- if (preserved !== void 0 && preserved.length > 0) {
2941
- state.userErrors.set(paths.FORM_ERRORS_PATH_KEY, preserved);
2942
- }
2943
2962
  return;
2944
2963
  }
2945
2964
  const segments = paths.canonicalizePath(path).segments;
2946
2965
  state.clearSchemaErrors(segments);
2947
2966
  state.clearUserErrors(segments);
2948
2967
  }
2949
- function setFormErrors(errors) {
2950
- if (errors.length === 0) {
2951
- state.userErrors.delete(paths.FORM_ERRORS_PATH_KEY);
2952
- return;
2953
- }
2954
- state.userErrors.set(
2955
- paths.FORM_ERRORS_PATH_KEY,
2956
- errors.map((e) => ({
2957
- path: [...paths.FORM_ERRORS_PATH],
2958
- message: e.message,
2959
- formKey: state.formKey,
2960
- code: e.code ?? "atta:form-error"
2961
- }))
2962
- );
2963
- }
2964
- function clearFormErrors() {
2965
- state.userErrors.delete(paths.FORM_ERRORS_PATH_KEY);
2966
- }
2967
2968
  const submitting = vue.computed(() => state.submitting.value);
2968
2969
  const submissionAttempts = vue.computed(() => state.submissionAttempts.value);
2969
2970
  const submitted = vue.computed(() => state.submitted.value);
@@ -3230,14 +3231,14 @@ function buildFormApi(state, formInstanceId, options = {}) {
3230
3231
  }
3231
3232
  const EMPTY_FIELD_RECORD = Object.freeze({});
3232
3233
  function record(path) {
3233
- const { segments } = paths.canonicalizePath(path);
3234
+ const segments = path === void 0 ? [] : paths.canonicalizePath(path).segments;
3234
3235
  const value = state.getValueAtPath(segments);
3235
3236
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
3236
3237
  return EMPTY_FIELD_RECORD;
3237
3238
  }
3238
3239
  const out = {};
3239
3240
  for (const key of Object.keys(value)) {
3240
- safeAssign(out, key, callTerminal(`${path}.${key}`));
3241
+ safeAssign(out, key, callTerminal(segments.length === 0 ? key : `${path}.${key}`));
3241
3242
  }
3242
3243
  return Object.freeze(out);
3243
3244
  }
@@ -3294,11 +3295,8 @@ function buildFormApi(state, formInstanceId, options = {}) {
3294
3295
  return errorsProxy;
3295
3296
  },
3296
3297
  toRef: gated(pathToRef),
3297
- setFieldErrors: gated(setFieldErrors),
3298
- addFieldErrors: gated(addFieldErrors),
3299
- clearFieldErrors: gated(clearFieldErrors),
3300
- setFormErrors: gated(setFormErrors),
3301
- clearFormErrors: gated(clearFormErrors),
3298
+ setErrors: gated(setErrors),
3299
+ clearErrors: gated(clearErrors),
3302
3300
  get meta() {
3303
3301
  void state.activate();
3304
3302
  return formMeta;
@@ -4816,25 +4814,14 @@ function createFormStore(options) {
4816
4814
  function getValueAtPath(path) {
4817
4815
  return getAtPath(form.value, path);
4818
4816
  }
4819
- function rerouteFormLevelEntry(err) {
4820
- if (err.path.length === 0) {
4821
- return { ...err, path: [...paths.FORM_ERRORS_PATH] };
4822
- }
4823
- return err;
4824
- }
4825
- function pathKeyForEntry(err) {
4826
- if (err.path.length === 0) return paths.FORM_ERRORS_PATH_KEY;
4827
- return paths.canonicalizePath(err.path).key;
4828
- }
4829
4817
  function appendErrorsTo(map, entries) {
4830
4818
  for (const raw of entries) {
4831
- const err = rerouteFormLevelEntry(raw);
4832
- const key = pathKeyForEntry(err);
4819
+ const { key } = paths.canonicalizePath(raw.path);
4833
4820
  const current = map.get(key);
4834
4821
  if (current === void 0) {
4835
- map.set(key, [err]);
4822
+ map.set(key, [raw]);
4836
4823
  } else {
4837
- map.set(key, [...current, err]);
4824
+ map.set(key, [...current, raw]);
4838
4825
  }
4839
4826
  }
4840
4827
  }
@@ -4859,14 +4846,13 @@ function createFormStore(options) {
4859
4846
  schemaErrors.set(key, [...entries]);
4860
4847
  }
4861
4848
  function applySchemaErrorsForSubtree(path, entries) {
4862
- const parentKey = path.length === 0 ? paths.FORM_ERRORS_PATH_KEY : paths.canonicalizePath(path).key;
4849
+ const parentKey = paths.canonicalizePath(path).key;
4863
4850
  const grouped = /* @__PURE__ */ new Map();
4864
4851
  for (const raw of entries) {
4865
- const err = rerouteFormLevelEntry(raw);
4866
- const key = pathKeyForEntry(err);
4852
+ const { key } = paths.canonicalizePath(raw.path);
4867
4853
  const list = grouped.get(key);
4868
- if (list === void 0) grouped.set(key, [err]);
4869
- else list.push(err);
4854
+ if (list === void 0) grouped.set(key, [raw]);
4855
+ else list.push(raw);
4870
4856
  }
4871
4857
  if (!grouped.has(parentKey)) schemaErrors.delete(parentKey);
4872
4858
  for (const existingKey of [...schemaErrors.keys()]) {
@@ -4888,8 +4874,13 @@ function createFormStore(options) {
4888
4874
  function setAllUserErrors(entries) {
4889
4875
  replaceErrorsIn(userErrors, entries);
4890
4876
  }
4891
- function addUserErrors(entries) {
4892
- 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]);
4893
4884
  }
4894
4885
  function clearUserErrors(path) {
4895
4886
  clearErrorsIn(userErrors, path);
@@ -5083,25 +5074,25 @@ function createFormStore(options) {
5083
5074
  }
5084
5075
  }
5085
5076
  function clearHydrationFailedEntry() {
5086
- const existing = schemaErrors.get(paths.FORM_ERRORS_PATH_KEY);
5077
+ const existing = schemaErrors.get(paths.ROOT_PATH_KEY);
5087
5078
  if (existing === void 0) return;
5088
5079
  const filtered = existing.filter((e) => e.code !== AttaformErrorCode.HydrationFailed);
5089
5080
  if (filtered.length === 0) {
5090
- schemaErrors.delete(paths.FORM_ERRORS_PATH_KEY);
5081
+ schemaErrors.delete(paths.ROOT_PATH_KEY);
5091
5082
  } else {
5092
- schemaErrors.set(paths.FORM_ERRORS_PATH_KEY, filtered);
5083
+ schemaErrors.set(paths.ROOT_PATH_KEY, filtered);
5093
5084
  }
5094
5085
  }
5095
5086
  function appendHydrationFailedEntry(error) {
5096
5087
  const message = error instanceof Error ? error.message : typeof error === "string" ? error : "Hydration failed";
5097
5088
  const entry = {
5098
5089
  message,
5099
- path: [...paths.FORM_ERRORS_PATH],
5090
+ path: [...paths.ROOT_PATH],
5100
5091
  formKey,
5101
5092
  code: AttaformErrorCode.HydrationFailed
5102
5093
  };
5103
- const existing = schemaErrors.get(paths.FORM_ERRORS_PATH_KEY) ?? [];
5104
- 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]);
5105
5096
  return entry;
5106
5097
  }
5107
5098
  function reset(nextDefaultValues) {
@@ -5334,7 +5325,7 @@ function createFormStore(options) {
5334
5325
  clearSchemaErrors,
5335
5326
  applySchemaErrorsForSubtree,
5336
5327
  setAllUserErrors,
5337
- addUserErrors,
5328
+ setUserErrorsForPath,
5338
5329
  clearUserErrors,
5339
5330
  getErrorsForPath,
5340
5331
  ensurePathOrdinal,
@@ -5411,12 +5402,38 @@ function captureErrorEntries(map) {
5411
5402
  for (const [k, v] of map) out.push([k, [...v]]);
5412
5403
  return out;
5413
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
+ }
5414
5430
  function errorFieldsEqual(av, bvi) {
5415
5431
  if (av === bvi) return true;
5416
5432
  if (av.message !== bvi.message) return false;
5417
5433
  if (av.code !== bvi.code) return false;
5418
5434
  if (av.formKey !== bvi.formKey) return false;
5419
- 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);
5420
5437
  }
5421
5438
  function errorsEqual(a, b) {
5422
5439
  if (a.length !== b.length) return false;
@@ -7025,7 +7042,6 @@ exports.isPlainRecord = isPlainRecord;
7025
7042
  exports.isUnset = isUnset;
7026
7043
  exports.lazy = lazy;
7027
7044
  exports.makeDefaultDisplayState = makeDefaultDisplayState;
7028
- exports.normalizeNumericOption = normalizeNumericOption;
7029
7045
  exports.safeAssign = safeAssign;
7030
7046
  exports.safeOwnRead = safeOwnRead;
7031
7047
  exports.setAtPath = setAtPath;
@@ -7034,4 +7050,4 @@ exports.unset = unset;
7034
7050
  exports.useAbstractForm = useAbstractForm;
7035
7051
  exports.useRegister = useRegister;
7036
7052
  exports.useWizard = useWizard;
7037
- //# sourceMappingURL=attaform.DsQkXE3o.cjs.map
7053
+ //# sourceMappingURL=attaform.C42wL7EJ.cjs.map