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,5 +1,5 @@
1
1
  import { computed, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, readonly, toRaw, reactive, watch, markRaw, triggerRef, shallowRef, getCurrentInstance, onServerPrefetch, provide, useId, inject, onBeforeMount, onBeforeUpdate, onMounted, effectScope, nextTick } from 'vue';
2
- import { o as pathsEqual, j as isPathPrefix, _ as __DEV__, a as canonicalizePath, s as segmentsForPathKey, F as FORM_ERRORS_PATH_KEY, q as keyForSegments, S as SubmitErrorHandlerError, t as toError, r as INTERACTIVE_TAG_NAMES, R as ROOT_PATH, w as FORM_ERRORS_PATH, d as ROOT_PATH_KEY, x as coerceToPathKey, b as InvalidUseFormConfigError, y as ensureAttaformInstalled, u as useRegistry, z as kFormContext, B as kFormInstanceId, f as ReservedFormKeyError, C as REGISTER_OWNER_MARKER, V as V_REGISTER_MARKER, k as kAttaformWizardActiveStepResolver, D as kAttaformAncestorWizard } from './attaform.BhI9Icek.mjs';
2
+ import { o as pathsEqual, j as isPathPrefix, _ as __DEV__, f as canonicalizePath, s as segmentsForPathKey, q as keyForSegments, b as ROOT_PATH_KEY, S as SubmitErrorHandlerError, t as toError, r as INTERACTIVE_TAG_NAMES, R as ROOT_PATH, w as coerceToPathKey, a as InvalidUseFormConfigError, x as ensureAttaformInstalled, u as useRegistry, y as kFormContext, z as kFormInstanceId, d as ReservedFormKeyError, B as REGISTER_OWNER_MARKER, V as V_REGISTER_MARKER, m as kAttaformWizardActiveStepResolver, C as kAttaformAncestorWizard } from './attaform.Df-s8j1X.mjs';
3
3
 
4
4
  function safeAssign(target, key, value) {
5
5
  if (key === "__proto__") {
@@ -540,6 +540,58 @@ function structuralSnapshot(value) {
540
540
  return out;
541
541
  }
542
542
 
543
+ const AttaformErrorCode = {
544
+ /** A required field is in the blank set — user hasn't supplied a value. */
545
+ NoValueSupplied: "atta:no-value-supplied",
546
+ /** The schema adapter's `validateAtPath` threw synchronously. */
547
+ AdapterThrew: "atta:adapter-threw",
548
+ /**
549
+ * User code inside a `z.preprocess`, `.refine`, or `.transform`
550
+ * threw (sync or async). The adapter caught the throw and surfaced
551
+ * it as a `ValidationError` at the field path so the form's normal
552
+ * error pipeline handles it instead of leaking as an unhandled
553
+ * rejection or routing through `submitError`.
554
+ */
555
+ ValidatorThrew: "atta:validator-threw",
556
+ /**
557
+ * A function-form `defaultValues` factory threw or its promise
558
+ * rejected. The runtime captures the raw error on `form.hydrateError`
559
+ * and ALSO surfaces a form-level `ValidationError` (path `[]`) so
560
+ * the standard error pipeline carries the signal. Critical for the
561
+ * SSR round-trip: `hydrateError` itself does not ride the wire
562
+ * payload, but `schemaErrors` does, so the client sees the failure
563
+ * after rehydration without an extra channel.
564
+ */
565
+ HydrationFailed: "atta:hydration-failed",
566
+ /** The supplied path didn't resolve to any node in the schema. */
567
+ PathNotFound: "atta:path-not-found",
568
+ /**
569
+ * A walked form's `activate()` (async `defaultValues` factory) threw
570
+ * during `wizard.handleSubmit`'s path walk. Surfaced as a synthetic
571
+ * `ValidationError` at the form-level path (`[]`) so the wizard's
572
+ * aggregate error pipeline can carry the failure alongside ordinary
573
+ * validation errors. The raw factory error remains on
574
+ * `form.hydrateError` for retry UX.
575
+ */
576
+ ActivationFailed: "atta:activation-failed",
577
+ /**
578
+ * Default code stamped on a manual error set through `form.setErrors`
579
+ * when the caller omits an explicit `code`. The `setErrors` input is
580
+ * lenient (`code` optional), so this is the fallback that keeps every
581
+ * produced `ValidationError` carrying a stable, branchable identifier.
582
+ * Override it per error by passing your own `code` (`api:…`, `auth:…`).
583
+ */
584
+ UserError: "atta:user-error"
585
+ };
586
+ function makeBlankRequiredError(segments, formKey) {
587
+ return {
588
+ message: "No value supplied",
589
+ path: [...segments],
590
+ formKey,
591
+ code: AttaformErrorCode.NoValueSupplied
592
+ };
593
+ }
594
+
543
595
  function isGateOpen(field, formMeta) {
544
596
  return formMeta.submissionAttempts > 0 || field.blurredAfterInteraction === true;
545
597
  }
@@ -595,50 +647,6 @@ function resolveGetDisplayState(config) {
595
647
  return config ?? defaultDisplayState;
596
648
  }
597
649
 
598
- const AttaformErrorCode = {
599
- /** A required field is in the blank set — user hasn't supplied a value. */
600
- NoValueSupplied: "atta:no-value-supplied",
601
- /** The schema adapter's `validateAtPath` threw synchronously. */
602
- AdapterThrew: "atta:adapter-threw",
603
- /**
604
- * User code inside a `z.preprocess`, `.refine`, or `.transform`
605
- * threw (sync or async). The adapter caught the throw and surfaced
606
- * it as a `ValidationError` at the field path so the form's normal
607
- * error pipeline handles it instead of leaking as an unhandled
608
- * rejection or routing through `submitError`.
609
- */
610
- ValidatorThrew: "atta:validator-threw",
611
- /**
612
- * A function-form `defaultValues` factory threw or its promise
613
- * rejected. The runtime captures the raw error on `form.hydrateError`
614
- * and ALSO surfaces a form-level `ValidationError` (path `[]`) so
615
- * the standard error pipeline carries the signal. Critical for the
616
- * SSR round-trip: `hydrateError` itself does not ride the wire
617
- * payload, but `schemaErrors` does, so the client sees the failure
618
- * after rehydration without an extra channel.
619
- */
620
- HydrationFailed: "atta:hydration-failed",
621
- /** The supplied path didn't resolve to any node in the schema. */
622
- PathNotFound: "atta:path-not-found",
623
- /**
624
- * A walked form's `activate()` (async `defaultValues` factory) threw
625
- * during `wizard.handleSubmit`'s path walk. Surfaced as a synthetic
626
- * `ValidationError` at the form-level path (`[]`) so the wizard's
627
- * aggregate error pipeline can carry the failure alongside ordinary
628
- * validation errors. The raw factory error remains on
629
- * `form.hydrateError` for retry UX.
630
- */
631
- ActivationFailed: "atta:activation-failed"
632
- };
633
- function makeBlankRequiredError(segments, formKey) {
634
- return {
635
- message: "No value supplied",
636
- path: [...segments],
637
- formKey,
638
- code: AttaformErrorCode.NoValueSupplied
639
- };
640
- }
641
-
642
650
  const DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS = 0;
643
651
  const DEFAULT_HISTORY_MAX_SNAPSHOTS = 128;
644
652
  const RESERVED_KEY_PREFIX = "__atta:";
@@ -1031,7 +1039,7 @@ function aggregateErrorsAt(state, prefix) {
1031
1039
  const segs = segmentsForPathKey(pathKey);
1032
1040
  if (segs === null) continue;
1033
1041
  if (!isPathPrefix(prefix, segs)) continue;
1034
- if (pathKey === FORM_ERRORS_PATH_KEY) ; else if (segs.length > 0 && !hasAtPath(formValue, segs)) continue;
1042
+ if (segs.length > 0 && !hasAtPath(formValue, segs)) continue;
1035
1043
  const ordinal = state.ensurePathOrdinal(pathKey);
1036
1044
  const existing = buckets.get(ordinal);
1037
1045
  if (existing === void 0) buckets.set(ordinal, [...list]);
@@ -1150,7 +1158,12 @@ function buildSurfaceProxy(opts) {
1150
1158
  const proxy = new Proxy(target, {
1151
1159
  apply(_, __, args) {
1152
1160
  const arg = args[0];
1153
- if (arg === void 0) return opts.resolveCallTarget(segments);
1161
+ if (arg === void 0) {
1162
+ if (segments.length === 0 && opts.resolveRootCall !== void 0) {
1163
+ return opts.resolveRootCall();
1164
+ }
1165
+ return opts.resolveCallTarget(segments);
1166
+ }
1154
1167
  const { segments: argSegs } = canonicalizePath(arg);
1155
1168
  return opts.resolveCallTarget(argSegs);
1156
1169
  },
@@ -1361,8 +1374,7 @@ function buildErrorsProxy(state) {
1361
1374
  return merged;
1362
1375
  }
1363
1376
  const { key } = canonicalizePath(path);
1364
- const isFormLevel = key === FORM_ERRORS_PATH_KEY;
1365
- const active = isFormLevel || hasAtPath(state.form.value, path);
1377
+ const active = hasAtPath(state.form.value, path);
1366
1378
  collectAtKey(key, active, merged);
1367
1379
  return merged;
1368
1380
  },
@@ -1370,37 +1382,42 @@ function buildErrorsProxy(state) {
1370
1382
  // undefined) IS the terminal.
1371
1383
  materializeContainer: (segments) => materializeErrors(state, segments),
1372
1384
  // Any path ending in `''` is a meaningful terminal at the proxy
1373
- // layer: at root it's the form-level bucket; at depth >= 1 it's
1374
- // the container-self sentinel that surfaces cross-field refines
1375
- // and container-targeted marks. `resolveLeaf` translates `[...,
1376
- // '']` lookups to the parent container path before querying the
1377
- // stores. When a schema legitimately owns a `''` field, the
1378
- // literal leaf and any container-self errors share the slot
1379
- // (errors concatenate) — vanishingly rare, accepted as the
1380
- // ergonomic cost of one unified sentinel convention at every
1381
- // depth.
1385
+ // layer. A bare `['']` is the literal root `''` field; at depth
1386
+ // >= 1 a trailing `''` is the container-self sentinel that surfaces
1387
+ // cross-field refines and container-targeted marks (`resolveLeaf`
1388
+ // translates `[..., '']` to the parent container path before
1389
+ // querying the stores). When a schema legitimately owns a `''`
1390
+ // field, the literal leaf and any container-self errors share the
1391
+ // slot (errors concatenate) — vanishingly rare, accepted as the
1392
+ // ergonomic cost of one unified sentinel convention at depth >= 1.
1382
1393
  isTerminalAt: (segs) => segs.length >= 1 && segs[segs.length - 1] === "",
1383
1394
  // Call-form aggregates: `form.errors(path)` returns a single
1384
- // `ValidationError[]` for any depth (leaf or container) same
1395
+ // `ValidationError[]` for any depth (leaf or container) via the
1385
1396
  // shared `aggregateErrorsAt` helper that `form.meta.errors` and
1386
- // `form.fields(path).errors` use, so the three surfaces never
1387
- // drift. Empty results return `undefined`, matching the leaf
1388
- // proxy's pre-existing semantic (`form.errors.email === undefined`
1389
- // when valid) so consumer code that branches on truthiness keeps
1390
- // working the call-form just extends that semantic to
1391
- // containers and dynamic paths.
1392
- resolveCallTarget: (path) => aggregateErrorsAt(state, path),
1397
+ // `form.fields(path).errors` also use, so the surfaces never drift.
1398
+ //
1399
+ // The root is the one carve-out. An EXPLICIT root path
1400
+ // (`errors([])`) returns ONLY the global `[]` bucket (root
1401
+ // `.refine()`, hydration failures, `setErrors`) via
1402
+ // `getErrorsForPath`, giving consumers a dedicated channel for
1403
+ // global messages undiluted by field errors. The no-arg `errors()`
1404
+ // instead resolves the FULL aggregate through `resolveRootCall`
1405
+ // below (identical to `meta.errors`).
1406
+ resolveCallTarget: (path) => path.length === 0 ? state.getErrorsForPath([]) : aggregateErrorsAt(state, path),
1407
+ // No-arg `errors()` = the whole-form aggregate, matching
1408
+ // `meta.errors`. Distinct from `errors([])` (global bucket only);
1409
+ // see `resolveCallTarget`.
1410
+ resolveRootCall: () => aggregateErrorsAt(state, []),
1393
1411
  // Enumeration unions the live form-data keys at this path with the
1394
1412
  // first-child segments drawn from every error store. Without the
1395
1413
  // union, `Object.keys(form.errors)` / `{...form.errors}` /
1396
- // `v-for="(errs, k) in form.errors"` silently dropped two
1397
- // important error classes that the dot / call / JSON.stringify
1398
- // surfaces already exposed:
1399
- //
1400
- // - **Form-level** errors at the synthetic `['']` path (set via
1401
- // `setFormErrors` or root cross-field refines).
1402
- // - **Server-only** errors at a key the schema doesn't know
1403
- // about (`['ghost']`, `['address', 'ghost']`).
1414
+ // `v-for="(errs, k) in form.errors"` would silently drop
1415
+ // **server-only** errors at a key the schema doesn't know about
1416
+ // (`['ghost']`, `['address', 'ghost']`) that the dot / call /
1417
+ // JSON.stringify surfaces already expose. Global errors at the root
1418
+ // `[]` are NOT a child key and never enumerate here (read them via
1419
+ // `errors([])` / `meta.errors`); a literal `''` field enumerates
1420
+ // under the key `''` like any other field.
1404
1421
  //
1405
1422
  // The union closes that gap so `ownKeys` agrees with the rest of
1406
1423
  // the surface. Active-path filter mirrors `resolveLeaf`:
@@ -1422,10 +1439,6 @@ function errorAwareContainerKeys(state, segments) {
1422
1439
  const walk = (store, applyActivePathFilter) => {
1423
1440
  for (const [pathKey, errors] of store) {
1424
1441
  if (errors.length === 0) continue;
1425
- if (pathKey === FORM_ERRORS_PATH_KEY) {
1426
- if (segments.length === 0) keys.add("");
1427
- continue;
1428
- }
1429
1442
  const decoded = segmentsForPathKey(pathKey);
1430
1443
  if (decoded === null) continue;
1431
1444
  if (decoded.length <= segments.length) continue;
@@ -1448,31 +1461,25 @@ function materializeErrors(state, containerSegments) {
1448
1461
  if (errors.length === 0) continue;
1449
1462
  const fullPath = segmentsForPathKey(pathKey);
1450
1463
  if (fullPath === null) continue;
1451
- const isSyntheticFormLevel = fullPath.length === 1 && fullPath[0] === "";
1452
- if (!isSyntheticFormLevel) {
1453
- if (fullPath.length < containerSegments.length) continue;
1454
- for (let i = 0; i < containerSegments.length; i++) {
1455
- if (fullPath[i] !== containerSegments[i]) continue entries;
1456
- }
1457
- } else if (containerSegments.length !== 0) {
1464
+ if (fullPath.length === 0) {
1465
+ if (containerSegments.length === 0) placeAt(tree, [ROOT_PATH_KEY], errors);
1458
1466
  continue;
1459
1467
  }
1460
- if (applyActivePathFilter && !isSyntheticFormLevel && !hasAtPath(state.form.value, fullPath))
1461
- continue;
1468
+ if (fullPath.length < containerSegments.length) continue;
1469
+ for (let i = 0; i < containerSegments.length; i++) {
1470
+ if (fullPath[i] !== containerSegments[i]) continue entries;
1471
+ }
1472
+ if (applyActivePathFilter && !hasAtPath(state.form.value, fullPath)) continue;
1473
+ const relativePath = fullPath.slice(containerSegments.length);
1462
1474
  let placePath;
1463
- if (isSyntheticFormLevel) {
1475
+ if (relativePath.length === 0) {
1464
1476
  placePath = [""];
1477
+ } else if (state.schema.isLeafAtPath(fullPath)) {
1478
+ placePath = relativePath;
1479
+ } else if (state.schema.getSlimPrimitiveTypesAtPath(fullPath).size > 0) {
1480
+ placePath = [...relativePath, ""];
1465
1481
  } else {
1466
- const relativePath = fullPath.slice(containerSegments.length);
1467
- if (relativePath.length === 0) {
1468
- placePath = [""];
1469
- } else if (state.schema.isLeafAtPath(fullPath)) {
1470
- placePath = relativePath;
1471
- } else if (state.schema.getSlimPrimitiveTypesAtPath(fullPath).size > 0) {
1472
- placePath = [...relativePath, ""];
1473
- } else {
1474
- placePath = relativePath;
1475
- }
1482
+ placePath = relativePath;
1476
1483
  }
1477
1484
  placeAt(tree, placePath, errors);
1478
1485
  }
@@ -2906,62 +2913,56 @@ function buildFormApi(state, formInstanceId, options = {}) {
2906
2913
  return true;
2907
2914
  }
2908
2915
  const errorsProxy = buildErrorsProxy(state);
2909
- function filterToOwnFormKey(errors, op) {
2910
- const own = [];
2911
- let dropped = 0;
2912
- for (const e of errors) {
2913
- if (e.formKey === state.formKey) own.push(e);
2914
- else dropped++;
2915
- }
2916
- if (__DEV__ && dropped > 0) {
2917
- console.warn(
2918
- `[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.`
2919
- );
2916
+ function normalizeErrorInput(item, scope) {
2917
+ if (item instanceof Error) {
2918
+ return {
2919
+ message: item.message.length > 0 ? item.message : "Unknown error",
2920
+ path: scope !== void 0 ? [...scope] : [],
2921
+ formKey: state.formKey,
2922
+ code: AttaformErrorCode.UserError
2923
+ };
2920
2924
  }
2921
- return own;
2925
+ const entry = {
2926
+ message: typeof item.message === "string" && item.message.length > 0 ? item.message : "Unknown error",
2927
+ path: scope !== void 0 ? [...scope] : Array.isArray(item.path) ? [...item.path] : [],
2928
+ formKey: state.formKey,
2929
+ code: typeof item.code === "string" && item.code.length > 0 ? item.code : AttaformErrorCode.UserError
2930
+ };
2931
+ if (item.data !== void 0) entry.data = item.data;
2932
+ return entry;
2922
2933
  }
2923
- function setFieldErrors(errors) {
2924
- const preserved = state.userErrors.get(FORM_ERRORS_PATH_KEY);
2925
- state.setAllUserErrors(filterToOwnFormKey(errors, "setFieldErrors"));
2926
- if (preserved !== void 0 && preserved.length > 0) {
2927
- state.userErrors.set(FORM_ERRORS_PATH_KEY, preserved);
2934
+ function normalizeErrorInputs(input, scope) {
2935
+ const items = Array.isArray(input) ? input : [input];
2936
+ return items.map((item) => normalizeErrorInput(item, scope));
2937
+ }
2938
+ function flattenUserErrors() {
2939
+ const all = [];
2940
+ for (const errs of state.userErrors.values()) all.push(...errs);
2941
+ return all;
2942
+ }
2943
+ function setErrors(arg1, arg2) {
2944
+ const isScoped = arguments.length >= 2 && (typeof arg1 === "string" || Array.isArray(arg1));
2945
+ if (isScoped) {
2946
+ const { segments, key } = canonicalizePath(arg1);
2947
+ const input2 = arg2;
2948
+ const resolved2 = typeof input2 === "function" ? input2((state.userErrors.get(key) ?? []).slice()) : input2;
2949
+ state.setUserErrorsForPath(segments, normalizeErrorInputs(resolved2, segments));
2950
+ return;
2928
2951
  }
2952
+ const input = arg1;
2953
+ const resolved = typeof input === "function" ? input(flattenUserErrors()) : input;
2954
+ state.setAllUserErrors(normalizeErrorInputs(resolved, void 0));
2929
2955
  }
2930
- function addFieldErrors(errors) {
2931
- state.addUserErrors(filterToOwnFormKey(errors, "addFieldErrors"));
2932
- }
2933
- function clearFieldErrors(path) {
2956
+ function clearErrors(path) {
2934
2957
  if (path === void 0) {
2935
- const preserved = state.userErrors.get(FORM_ERRORS_PATH_KEY);
2936
2958
  state.clearSchemaErrors();
2937
2959
  state.clearUserErrors();
2938
- if (preserved !== void 0 && preserved.length > 0) {
2939
- state.userErrors.set(FORM_ERRORS_PATH_KEY, preserved);
2940
- }
2941
2960
  return;
2942
2961
  }
2943
2962
  const segments = canonicalizePath(path).segments;
2944
2963
  state.clearSchemaErrors(segments);
2945
2964
  state.clearUserErrors(segments);
2946
2965
  }
2947
- function setFormErrors(errors) {
2948
- if (errors.length === 0) {
2949
- state.userErrors.delete(FORM_ERRORS_PATH_KEY);
2950
- return;
2951
- }
2952
- state.userErrors.set(
2953
- FORM_ERRORS_PATH_KEY,
2954
- errors.map((e) => ({
2955
- path: [...FORM_ERRORS_PATH],
2956
- message: e.message,
2957
- formKey: state.formKey,
2958
- code: e.code ?? "atta:form-error"
2959
- }))
2960
- );
2961
- }
2962
- function clearFormErrors() {
2963
- state.userErrors.delete(FORM_ERRORS_PATH_KEY);
2964
- }
2965
2966
  const submitting = computed(() => state.submitting.value);
2966
2967
  const submissionAttempts = computed(() => state.submissionAttempts.value);
2967
2968
  const submitted = computed(() => state.submitted.value);
@@ -3228,14 +3229,14 @@ function buildFormApi(state, formInstanceId, options = {}) {
3228
3229
  }
3229
3230
  const EMPTY_FIELD_RECORD = Object.freeze({});
3230
3231
  function record(path) {
3231
- const { segments } = canonicalizePath(path);
3232
+ const segments = path === void 0 ? [] : canonicalizePath(path).segments;
3232
3233
  const value = state.getValueAtPath(segments);
3233
3234
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
3234
3235
  return EMPTY_FIELD_RECORD;
3235
3236
  }
3236
3237
  const out = {};
3237
3238
  for (const key of Object.keys(value)) {
3238
- safeAssign(out, key, callTerminal(`${path}.${key}`));
3239
+ safeAssign(out, key, callTerminal(segments.length === 0 ? key : `${path}.${key}`));
3239
3240
  }
3240
3241
  return Object.freeze(out);
3241
3242
  }
@@ -3292,11 +3293,8 @@ function buildFormApi(state, formInstanceId, options = {}) {
3292
3293
  return errorsProxy;
3293
3294
  },
3294
3295
  toRef: gated(pathToRef),
3295
- setFieldErrors: gated(setFieldErrors),
3296
- addFieldErrors: gated(addFieldErrors),
3297
- clearFieldErrors: gated(clearFieldErrors),
3298
- setFormErrors: gated(setFormErrors),
3299
- clearFormErrors: gated(clearFormErrors),
3296
+ setErrors: gated(setErrors),
3297
+ clearErrors: gated(clearErrors),
3300
3298
  get meta() {
3301
3299
  void state.activate();
3302
3300
  return formMeta;
@@ -4814,25 +4812,14 @@ function createFormStore(options) {
4814
4812
  function getValueAtPath(path) {
4815
4813
  return getAtPath(form.value, path);
4816
4814
  }
4817
- function rerouteFormLevelEntry(err) {
4818
- if (err.path.length === 0) {
4819
- return { ...err, path: [...FORM_ERRORS_PATH] };
4820
- }
4821
- return err;
4822
- }
4823
- function pathKeyForEntry(err) {
4824
- if (err.path.length === 0) return FORM_ERRORS_PATH_KEY;
4825
- return canonicalizePath(err.path).key;
4826
- }
4827
4815
  function appendErrorsTo(map, entries) {
4828
4816
  for (const raw of entries) {
4829
- const err = rerouteFormLevelEntry(raw);
4830
- const key = pathKeyForEntry(err);
4817
+ const { key } = canonicalizePath(raw.path);
4831
4818
  const current = map.get(key);
4832
4819
  if (current === void 0) {
4833
- map.set(key, [err]);
4820
+ map.set(key, [raw]);
4834
4821
  } else {
4835
- map.set(key, [...current, err]);
4822
+ map.set(key, [...current, raw]);
4836
4823
  }
4837
4824
  }
4838
4825
  }
@@ -4857,14 +4844,13 @@ function createFormStore(options) {
4857
4844
  schemaErrors.set(key, [...entries]);
4858
4845
  }
4859
4846
  function applySchemaErrorsForSubtree(path, entries) {
4860
- const parentKey = path.length === 0 ? FORM_ERRORS_PATH_KEY : canonicalizePath(path).key;
4847
+ const parentKey = canonicalizePath(path).key;
4861
4848
  const grouped = /* @__PURE__ */ new Map();
4862
4849
  for (const raw of entries) {
4863
- const err = rerouteFormLevelEntry(raw);
4864
- const key = pathKeyForEntry(err);
4850
+ const { key } = canonicalizePath(raw.path);
4865
4851
  const list = grouped.get(key);
4866
- if (list === void 0) grouped.set(key, [err]);
4867
- else list.push(err);
4852
+ if (list === void 0) grouped.set(key, [raw]);
4853
+ else list.push(raw);
4868
4854
  }
4869
4855
  if (!grouped.has(parentKey)) schemaErrors.delete(parentKey);
4870
4856
  for (const existingKey of [...schemaErrors.keys()]) {
@@ -4886,8 +4872,13 @@ function createFormStore(options) {
4886
4872
  function setAllUserErrors(entries) {
4887
4873
  replaceErrorsIn(userErrors, entries);
4888
4874
  }
4889
- function addUserErrors(entries) {
4890
- appendErrorsTo(userErrors, entries);
4875
+ function setUserErrorsForPath(path, entries) {
4876
+ const { key } = canonicalizePath(path);
4877
+ if (entries.length === 0) {
4878
+ userErrors.delete(key);
4879
+ return;
4880
+ }
4881
+ userErrors.set(key, [...entries]);
4891
4882
  }
4892
4883
  function clearUserErrors(path) {
4893
4884
  clearErrorsIn(userErrors, path);
@@ -5081,25 +5072,25 @@ function createFormStore(options) {
5081
5072
  }
5082
5073
  }
5083
5074
  function clearHydrationFailedEntry() {
5084
- const existing = schemaErrors.get(FORM_ERRORS_PATH_KEY);
5075
+ const existing = schemaErrors.get(ROOT_PATH_KEY);
5085
5076
  if (existing === void 0) return;
5086
5077
  const filtered = existing.filter((e) => e.code !== AttaformErrorCode.HydrationFailed);
5087
5078
  if (filtered.length === 0) {
5088
- schemaErrors.delete(FORM_ERRORS_PATH_KEY);
5079
+ schemaErrors.delete(ROOT_PATH_KEY);
5089
5080
  } else {
5090
- schemaErrors.set(FORM_ERRORS_PATH_KEY, filtered);
5081
+ schemaErrors.set(ROOT_PATH_KEY, filtered);
5091
5082
  }
5092
5083
  }
5093
5084
  function appendHydrationFailedEntry(error) {
5094
5085
  const message = error instanceof Error ? error.message : typeof error === "string" ? error : "Hydration failed";
5095
5086
  const entry = {
5096
5087
  message,
5097
- path: [...FORM_ERRORS_PATH],
5088
+ path: [...ROOT_PATH],
5098
5089
  formKey,
5099
5090
  code: AttaformErrorCode.HydrationFailed
5100
5091
  };
5101
- const existing = schemaErrors.get(FORM_ERRORS_PATH_KEY) ?? [];
5102
- schemaErrors.set(FORM_ERRORS_PATH_KEY, [...existing, entry]);
5092
+ const existing = schemaErrors.get(ROOT_PATH_KEY) ?? [];
5093
+ schemaErrors.set(ROOT_PATH_KEY, [...existing, entry]);
5103
5094
  return entry;
5104
5095
  }
5105
5096
  function reset(nextDefaultValues) {
@@ -5332,7 +5323,7 @@ function createFormStore(options) {
5332
5323
  clearSchemaErrors,
5333
5324
  applySchemaErrorsForSubtree,
5334
5325
  setAllUserErrors,
5335
- addUserErrors,
5326
+ setUserErrorsForPath,
5336
5327
  clearUserErrors,
5337
5328
  getErrorsForPath,
5338
5329
  ensurePathOrdinal,
@@ -5409,12 +5400,38 @@ function captureErrorEntries(map) {
5409
5400
  for (const [k, v] of map) out.push([k, [...v]]);
5410
5401
  return out;
5411
5402
  }
5403
+ function dataEqual(a, b) {
5404
+ if (a === b) return true;
5405
+ if (a === null || b === null || a === void 0 || b === void 0) return false;
5406
+ if (typeof a !== typeof b) return false;
5407
+ if (Array.isArray(a)) {
5408
+ if (!Array.isArray(b) || a.length !== b.length) return false;
5409
+ for (let i = 0; i < a.length; i++) {
5410
+ if (!dataEqual(a[i], b[i])) return false;
5411
+ }
5412
+ return true;
5413
+ }
5414
+ if (Array.isArray(b)) return false;
5415
+ if (typeof a === "object") {
5416
+ const ao = a;
5417
+ const bo = b;
5418
+ const keys = Object.keys(ao);
5419
+ if (keys.length !== Object.keys(bo).length) return false;
5420
+ for (const k of keys) {
5421
+ if (!Object.prototype.hasOwnProperty.call(bo, k)) return false;
5422
+ if (!dataEqual(ao[k], bo[k])) return false;
5423
+ }
5424
+ return true;
5425
+ }
5426
+ return false;
5427
+ }
5412
5428
  function errorFieldsEqual(av, bvi) {
5413
5429
  if (av === bvi) return true;
5414
5430
  if (av.message !== bvi.message) return false;
5415
5431
  if (av.code !== bvi.code) return false;
5416
5432
  if (av.formKey !== bvi.formKey) return false;
5417
- return av.path === bvi.path || pathsEqual(av.path, bvi.path);
5433
+ if (av.path !== bvi.path && !pathsEqual(av.path, bvi.path)) return false;
5434
+ return dataEqual(av.data, bvi.data);
5418
5435
  }
5419
5436
  function errorsEqual(a, b) {
5420
5437
  if (a.length !== b.length) return false;
@@ -7010,5 +7027,5 @@ function warnIfAmbientWizardProviderHadDuplicates() {
7010
7027
  }
7011
7028
  }
7012
7029
 
7013
- export { AttaformErrorCode as A, DEFAULT_TIMINGS as D, injectWizard as a, isUnset as b, useRegister as c, useWizard as d, defaultCoercionRules as e, defaultDisplayState as f, defineCoercion as g, useAbstractForm as h, injectForm as i, isPlainRecord as j, safeAssign as k, lazy as l, makeDefaultDisplayState as m, normalizeNumericOption as n, slimKindOf as o, humanize as p, getAtPath as q, setAtPath as r, safeOwnRead as s, unset as u };
7014
- //# sourceMappingURL=attaform.CtJOd7ox.mjs.map
7030
+ export { AttaformErrorCode as A, DEFAULT_TIMINGS as D, injectWizard as a, isUnset as b, useRegister as c, useWizard as d, defaultCoercionRules as e, defaultDisplayState as f, defineCoercion as g, useAbstractForm as h, injectForm as i, isPlainRecord as j, safeAssign as k, lazy as l, makeDefaultDisplayState as m, slimKindOf as n, humanize as o, getAtPath as p, setAtPath as q, safeOwnRead as s, unset as u };
7031
+ //# sourceMappingURL=attaform.CuBdtfbe.mjs.map