attaform 0.23.0 → 0.24.1

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 (99) 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 +5 -150
  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 +5 -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.Dx9-QQE2.mjs → attaform.0-00cYGw.mjs} +2 -2
  20. package/dist/shared/{attaform.Dx9-QQE2.mjs.map → attaform.0-00cYGw.mjs.map} +1 -1
  21. package/dist/shared/{attaform.D52oJiYC.d.cts → attaform.4zesozTg.d.mts} +38 -6
  22. package/dist/shared/{attaform.Db4E4IW6.cjs → attaform.B7UdTs_o.cjs} +79 -79
  23. package/dist/shared/attaform.B7UdTs_o.cjs.map +1 -0
  24. package/dist/shared/{attaform.BibT5AS_.cjs → attaform.BOi6n2Pn.cjs} +2 -2
  25. package/dist/shared/{attaform.BibT5AS_.cjs.map → attaform.BOi6n2Pn.cjs.map} +1 -1
  26. package/dist/shared/{attaform.DCkSNnPr.d.mts → attaform.Bk7vnQhG.d.cts} +38 -6
  27. package/dist/shared/{attaform.BGMRvckW.d.ts → attaform.Bq6Copxn.d.cts} +41 -25
  28. package/dist/shared/{attaform.DuPneYR0.d.cts → attaform.BrFPMFgi.d.ts} +41 -25
  29. package/dist/shared/{attaform.alpG7rT7.mjs → attaform.BunnTiTw.mjs} +4 -4
  30. package/dist/shared/attaform.BunnTiTw.mjs.map +1 -0
  31. package/dist/shared/{attaform.DrY8srOp.d.mts → attaform.BwAcpoRw.d.mts} +41 -25
  32. package/dist/shared/{attaform.CaYj3ZfY.cjs → attaform.C-1W0T1n.cjs} +6 -4
  33. package/dist/shared/attaform.C-1W0T1n.cjs.map +1 -0
  34. package/dist/shared/{attaform.Dd1Kmmaj.mjs → attaform.C-tQKknW.mjs} +6 -4
  35. package/dist/shared/attaform.C-tQKknW.mjs.map +1 -0
  36. package/dist/shared/{attaform.BhI9Icek.mjs → attaform.C0au8oXd.mjs} +36 -31
  37. package/dist/shared/attaform.C0au8oXd.mjs.map +1 -0
  38. package/dist/shared/attaform.CjdepGnw.cjs +27 -0
  39. package/dist/shared/attaform.CjdepGnw.cjs.map +1 -0
  40. package/dist/shared/{attaform.DbyTD8N2.cjs → attaform.Cn6JoG9o.cjs} +8 -6
  41. package/dist/shared/attaform.Cn6JoG9o.cjs.map +1 -0
  42. package/dist/shared/{attaform.Cmb_LCie.cjs → attaform.CrrIaHM8.cjs} +4 -4
  43. package/dist/shared/attaform.CrrIaHM8.cjs.map +1 -0
  44. package/dist/shared/{attaform.BJnNK75Y.d.mts → attaform.DBhrKb2j.d.cts} +206 -168
  45. package/dist/shared/{attaform.BJnNK75Y.d.ts → attaform.DBhrKb2j.d.mts} +206 -168
  46. package/dist/shared/{attaform.BJnNK75Y.d.cts → attaform.DBhrKb2j.d.ts} +206 -168
  47. package/dist/shared/{attaform.DsQkXE3o.cjs → attaform.DRQjF16I.cjs} +248 -195
  48. package/dist/shared/attaform.DRQjF16I.cjs.map +1 -0
  49. package/dist/shared/attaform.DdjDqTah.d.cts +56 -0
  50. package/dist/shared/attaform.DdjDqTah.d.mts +56 -0
  51. package/dist/shared/attaform.DdjDqTah.d.ts +56 -0
  52. package/dist/shared/{attaform.WEwfXcHq.d.ts → attaform.Df4xXKbE.d.ts} +38 -6
  53. package/dist/shared/{attaform.BFWb6hDk.mjs → attaform.DhXl0Kdr.mjs} +7 -1
  54. package/dist/shared/attaform.DhXl0Kdr.mjs.map +1 -0
  55. package/dist/shared/{attaform.CR6wGvNu.cjs → attaform.DwLw3Kzv.cjs} +7 -1
  56. package/dist/shared/attaform.DwLw3Kzv.cjs.map +1 -0
  57. package/dist/shared/{attaform.CzVta5o2.mjs → attaform.FY5r1BpA.mjs} +8 -6
  58. package/dist/shared/attaform.FY5r1BpA.mjs.map +1 -0
  59. package/dist/shared/{attaform.CtJOd7ox.mjs → attaform.NWrEGrNo.mjs} +247 -193
  60. package/dist/shared/attaform.NWrEGrNo.mjs.map +1 -0
  61. package/dist/shared/attaform.WvcckZMD.mjs +21 -0
  62. package/dist/shared/attaform.WvcckZMD.mjs.map +1 -0
  63. package/dist/transforms.cjs +1 -1
  64. package/dist/transforms.mjs +1 -1
  65. package/dist/vite.cjs +1 -1
  66. package/dist/vite.mjs +1 -1
  67. package/dist/zod-v3.cjs +2 -2
  68. package/dist/zod-v3.d.cts +12 -11
  69. package/dist/zod-v3.d.mts +12 -11
  70. package/dist/zod-v3.d.ts +12 -11
  71. package/dist/zod-v3.mjs +2 -2
  72. package/dist/zod-v4.cjs +2 -2
  73. package/dist/zod-v4.d.cts +6 -5
  74. package/dist/zod-v4.d.mts +6 -5
  75. package/dist/zod-v4.d.ts +6 -5
  76. package/dist/zod-v4.mjs +2 -2
  77. package/dist/zod.cjs +5 -5
  78. package/dist/zod.cjs.map +1 -1
  79. package/dist/zod.d.cts +21 -52
  80. package/dist/zod.d.mts +21 -52
  81. package/dist/zod.d.ts +21 -52
  82. package/dist/zod.mjs +5 -5
  83. package/dist/zod.mjs.map +1 -1
  84. package/package.json +1 -1
  85. package/dist/shared/attaform.BFWb6hDk.mjs.map +0 -1
  86. package/dist/shared/attaform.BhI9Icek.mjs.map +0 -1
  87. package/dist/shared/attaform.CR6wGvNu.cjs.map +0 -1
  88. package/dist/shared/attaform.CaYj3ZfY.cjs.map +0 -1
  89. package/dist/shared/attaform.Cmb_LCie.cjs.map +0 -1
  90. package/dist/shared/attaform.CtJOd7ox.mjs.map +0 -1
  91. package/dist/shared/attaform.CzVta5o2.mjs.map +0 -1
  92. package/dist/shared/attaform.Db4E4IW6.cjs.map +0 -1
  93. package/dist/shared/attaform.DbyTD8N2.cjs.map +0 -1
  94. package/dist/shared/attaform.Dd1Kmmaj.mjs.map +0 -1
  95. package/dist/shared/attaform.DsQkXE3o.cjs.map +0 -1
  96. package/dist/shared/attaform.alpG7rT7.mjs.map +0 -1
  97. package/dist/shared/attaform.nf83TIR5.d.cts +0 -35
  98. package/dist/shared/attaform.nf83TIR5.d.mts +0 -35
  99. package/dist/shared/attaform.nf83TIR5.d.ts +0 -35
@@ -1,7 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  const vue = require('vue');
4
- const paths = require('./attaform.Db4E4IW6.cjs');
4
+ const paths = require('./attaform.B7UdTs_o.cjs');
5
+ const registerProtocol = require('./attaform.CjdepGnw.cjs');
5
6
 
6
7
  function safeAssign(target, key, value) {
7
8
  if (key === "__proto__") {
@@ -542,6 +543,58 @@ function structuralSnapshot(value) {
542
543
  return out;
543
544
  }
544
545
 
546
+ const AttaformErrorCode = {
547
+ /** A required field is in the blank set — user hasn't supplied a value. */
548
+ NoValueSupplied: "atta:no-value-supplied",
549
+ /** The schema adapter's `validateAtPath` threw synchronously. */
550
+ AdapterThrew: "atta:adapter-threw",
551
+ /**
552
+ * User code inside a `z.preprocess`, `.refine`, or `.transform`
553
+ * threw (sync or async). The adapter caught the throw and surfaced
554
+ * it as a `ValidationError` at the field path so the form's normal
555
+ * error pipeline handles it instead of leaking as an unhandled
556
+ * rejection or routing through `submitError`.
557
+ */
558
+ ValidatorThrew: "atta:validator-threw",
559
+ /**
560
+ * A function-form `defaultValues` factory threw or its promise
561
+ * rejected. The runtime captures the raw error on `form.hydrateError`
562
+ * and ALSO surfaces a form-level `ValidationError` (path `[]`) so
563
+ * the standard error pipeline carries the signal. Critical for the
564
+ * SSR round-trip: `hydrateError` itself does not ride the wire
565
+ * payload, but `schemaErrors` does, so the client sees the failure
566
+ * after rehydration without an extra channel.
567
+ */
568
+ HydrationFailed: "atta:hydration-failed",
569
+ /** The supplied path didn't resolve to any node in the schema. */
570
+ PathNotFound: "atta:path-not-found",
571
+ /**
572
+ * A walked form's `activate()` (async `defaultValues` factory) threw
573
+ * during `wizard.handleSubmit`'s path walk. Surfaced as a synthetic
574
+ * `ValidationError` at the form-level path (`[]`) so the wizard's
575
+ * aggregate error pipeline can carry the failure alongside ordinary
576
+ * validation errors. The raw factory error remains on
577
+ * `form.hydrateError` for retry UX.
578
+ */
579
+ ActivationFailed: "atta:activation-failed",
580
+ /**
581
+ * Default code stamped on a manual error set through `form.setErrors`
582
+ * when the caller omits an explicit `code`. The `setErrors` input is
583
+ * lenient (`code` optional), so this is the fallback that keeps every
584
+ * produced `ValidationError` carrying a stable, branchable identifier.
585
+ * Override it per error by passing your own `code` (`api:…`, `auth:…`).
586
+ */
587
+ UserError: "atta:user-error"
588
+ };
589
+ function makeBlankRequiredError(segments, formKey) {
590
+ return {
591
+ message: "No value supplied",
592
+ path: [...segments],
593
+ formKey,
594
+ code: AttaformErrorCode.NoValueSupplied
595
+ };
596
+ }
597
+
545
598
  function isGateOpen(field, formMeta) {
546
599
  return formMeta.submissionAttempts > 0 || field.blurredAfterInteraction === true;
547
600
  }
@@ -597,50 +650,6 @@ function resolveGetDisplayState(config) {
597
650
  return config ?? defaultDisplayState;
598
651
  }
599
652
 
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
653
  const DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS = 0;
645
654
  const DEFAULT_HISTORY_MAX_SNAPSHOTS = 128;
646
655
  const RESERVED_KEY_PREFIX = "__atta:";
@@ -1033,7 +1042,7 @@ function aggregateErrorsAt(state, prefix) {
1033
1042
  const segs = paths.segmentsForPathKey(pathKey);
1034
1043
  if (segs === null) continue;
1035
1044
  if (!paths.isPathPrefix(prefix, segs)) continue;
1036
- if (pathKey === paths.FORM_ERRORS_PATH_KEY) ; else if (segs.length > 0 && !hasAtPath(formValue, segs)) continue;
1045
+ if (segs.length > 0 && !hasAtPath(formValue, segs)) continue;
1037
1046
  const ordinal = state.ensurePathOrdinal(pathKey);
1038
1047
  const existing = buckets.get(ordinal);
1039
1048
  if (existing === void 0) buckets.set(ordinal, [...list]);
@@ -1152,7 +1161,12 @@ function buildSurfaceProxy(opts) {
1152
1161
  const proxy = new Proxy(target, {
1153
1162
  apply(_, __, args) {
1154
1163
  const arg = args[0];
1155
- if (arg === void 0) return opts.resolveCallTarget(segments);
1164
+ if (arg === void 0) {
1165
+ if (segments.length === 0 && opts.resolveRootCall !== void 0) {
1166
+ return opts.resolveRootCall();
1167
+ }
1168
+ return opts.resolveCallTarget(segments);
1169
+ }
1156
1170
  const { segments: argSegs } = paths.canonicalizePath(arg);
1157
1171
  return opts.resolveCallTarget(argSegs);
1158
1172
  },
@@ -1363,8 +1377,7 @@ function buildErrorsProxy(state) {
1363
1377
  return merged;
1364
1378
  }
1365
1379
  const { key } = paths.canonicalizePath(path);
1366
- const isFormLevel = key === paths.FORM_ERRORS_PATH_KEY;
1367
- const active = isFormLevel || hasAtPath(state.form.value, path);
1380
+ const active = hasAtPath(state.form.value, path);
1368
1381
  collectAtKey(key, active, merged);
1369
1382
  return merged;
1370
1383
  },
@@ -1372,37 +1385,42 @@ function buildErrorsProxy(state) {
1372
1385
  // undefined) IS the terminal.
1373
1386
  materializeContainer: (segments) => materializeErrors(state, segments),
1374
1387
  // 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.
1388
+ // layer. A bare `['']` is the literal root `''` field; at depth
1389
+ // >= 1 a trailing `''` is the container-self sentinel that surfaces
1390
+ // cross-field refines and container-targeted marks (`resolveLeaf`
1391
+ // translates `[..., '']` to the parent container path before
1392
+ // querying the stores). When a schema legitimately owns a `''`
1393
+ // field, the literal leaf and any container-self errors share the
1394
+ // slot (errors concatenate) — vanishingly rare, accepted as the
1395
+ // ergonomic cost of one unified sentinel convention at depth >= 1.
1384
1396
  isTerminalAt: (segs) => segs.length >= 1 && segs[segs.length - 1] === "",
1385
1397
  // Call-form aggregates: `form.errors(path)` returns a single
1386
- // `ValidationError[]` for any depth (leaf or container) same
1398
+ // `ValidationError[]` for any depth (leaf or container) via the
1387
1399
  // 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),
1400
+ // `form.fields(path).errors` also use, so the surfaces never drift.
1401
+ //
1402
+ // The root is the one carve-out. An EXPLICIT root path
1403
+ // (`errors([])`) returns ONLY the global `[]` bucket (root
1404
+ // `.refine()`, hydration failures, `setErrors`) via
1405
+ // `getErrorsForPath`, giving consumers a dedicated channel for
1406
+ // global messages undiluted by field errors. The no-arg `errors()`
1407
+ // instead resolves the FULL aggregate through `resolveRootCall`
1408
+ // below (identical to `meta.errors`).
1409
+ resolveCallTarget: (path) => path.length === 0 ? state.getErrorsForPath([]) : aggregateErrorsAt(state, path),
1410
+ // No-arg `errors()` = the whole-form aggregate, matching
1411
+ // `meta.errors`. Distinct from `errors([])` (global bucket only);
1412
+ // see `resolveCallTarget`.
1413
+ resolveRootCall: () => aggregateErrorsAt(state, []),
1395
1414
  // Enumeration unions the live form-data keys at this path with the
1396
1415
  // first-child segments drawn from every error store. Without the
1397
1416
  // 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']`).
1417
+ // `v-for="(errs, k) in form.errors"` would silently drop
1418
+ // **server-only** errors at a key the schema doesn't know about
1419
+ // (`['ghost']`, `['address', 'ghost']`) that the dot / call /
1420
+ // JSON.stringify surfaces already expose. Global errors at the root
1421
+ // `[]` are NOT a child key and never enumerate here (read them via
1422
+ // `errors([])` / `meta.errors`); a literal `''` field enumerates
1423
+ // under the key `''` like any other field.
1406
1424
  //
1407
1425
  // The union closes that gap so `ownKeys` agrees with the rest of
1408
1426
  // the surface. Active-path filter mirrors `resolveLeaf`:
@@ -1424,10 +1442,6 @@ function errorAwareContainerKeys(state, segments) {
1424
1442
  const walk = (store, applyActivePathFilter) => {
1425
1443
  for (const [pathKey, errors] of store) {
1426
1444
  if (errors.length === 0) continue;
1427
- if (pathKey === paths.FORM_ERRORS_PATH_KEY) {
1428
- if (segments.length === 0) keys.add("");
1429
- continue;
1430
- }
1431
1445
  const decoded = paths.segmentsForPathKey(pathKey);
1432
1446
  if (decoded === null) continue;
1433
1447
  if (decoded.length <= segments.length) continue;
@@ -1450,31 +1464,25 @@ function materializeErrors(state, containerSegments) {
1450
1464
  if (errors.length === 0) continue;
1451
1465
  const fullPath = paths.segmentsForPathKey(pathKey);
1452
1466
  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) {
1467
+ if (fullPath.length === 0) {
1468
+ if (containerSegments.length === 0) placeAt(tree, [paths.ROOT_PATH_KEY], errors);
1460
1469
  continue;
1461
1470
  }
1462
- if (applyActivePathFilter && !isSyntheticFormLevel && !hasAtPath(state.form.value, fullPath))
1463
- continue;
1471
+ if (fullPath.length < containerSegments.length) continue;
1472
+ for (let i = 0; i < containerSegments.length; i++) {
1473
+ if (fullPath[i] !== containerSegments[i]) continue entries;
1474
+ }
1475
+ if (applyActivePathFilter && !hasAtPath(state.form.value, fullPath)) continue;
1476
+ const relativePath = fullPath.slice(containerSegments.length);
1464
1477
  let placePath;
1465
- if (isSyntheticFormLevel) {
1478
+ if (relativePath.length === 0) {
1466
1479
  placePath = [""];
1480
+ } else if (state.schema.isLeafAtPath(fullPath)) {
1481
+ placePath = relativePath;
1482
+ } else if (state.schema.getSlimPrimitiveTypesAtPath(fullPath).size > 0) {
1483
+ placePath = [...relativePath, ""];
1467
1484
  } 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
- }
1485
+ placePath = relativePath;
1478
1486
  }
1479
1487
  placeAt(tree, placePath, errors);
1480
1488
  }
@@ -1914,6 +1922,19 @@ function buildProcessForm(state, formInstanceId, options = {}) {
1914
1922
  state.clearSchemaErrors();
1915
1923
  }
1916
1924
  await onSubmit(merged.data);
1925
+ if (state.userErrors.size > 0) {
1926
+ if (state.submissionGeneration.value === genAtEntry) {
1927
+ applyInvalidSubmitPolicy(state, formInstanceId, invalidPolicy);
1928
+ }
1929
+ if (onError !== void 0) {
1930
+ try {
1931
+ await onError(Array.from(state.userErrors.values()).flat());
1932
+ } catch (cause) {
1933
+ throw new paths.SubmitErrorHandlerError("User-provided onError threw", { cause });
1934
+ }
1935
+ }
1936
+ return;
1937
+ }
1917
1938
  if (state.submissionGeneration.value === genAtEntry) {
1918
1939
  state.submitted.value = true;
1919
1940
  }
@@ -2908,62 +2929,56 @@ function buildFormApi(state, formInstanceId, options = {}) {
2908
2929
  return true;
2909
2930
  }
2910
2931
  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
- );
2932
+ function normalizeErrorInput(item, scope) {
2933
+ if (item instanceof Error) {
2934
+ return {
2935
+ message: item.message.length > 0 ? item.message : "Unknown error",
2936
+ path: scope !== void 0 ? [...scope] : [],
2937
+ formKey: state.formKey,
2938
+ code: AttaformErrorCode.UserError
2939
+ };
2922
2940
  }
2923
- return own;
2941
+ const entry = {
2942
+ message: typeof item.message === "string" && item.message.length > 0 ? item.message : "Unknown error",
2943
+ path: scope !== void 0 ? [...scope] : Array.isArray(item.path) ? [...item.path] : [],
2944
+ formKey: state.formKey,
2945
+ code: typeof item.code === "string" && item.code.length > 0 ? item.code : AttaformErrorCode.UserError
2946
+ };
2947
+ if (item.data !== void 0) entry.data = item.data;
2948
+ return entry;
2924
2949
  }
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);
2950
+ function normalizeErrorInputs(input, scope) {
2951
+ const items = Array.isArray(input) ? input : [input];
2952
+ return items.map((item) => normalizeErrorInput(item, scope));
2953
+ }
2954
+ function flattenUserErrors() {
2955
+ const all = [];
2956
+ for (const errs of state.userErrors.values()) all.push(...errs);
2957
+ return all;
2958
+ }
2959
+ function setErrors(arg1, arg2) {
2960
+ const isScoped = arguments.length >= 2 && (typeof arg1 === "string" || Array.isArray(arg1));
2961
+ if (isScoped) {
2962
+ const { segments, key } = paths.canonicalizePath(arg1);
2963
+ const input2 = arg2;
2964
+ const resolved2 = typeof input2 === "function" ? input2((state.userErrors.get(key) ?? []).slice()) : input2;
2965
+ state.setUserErrorsForPath(segments, normalizeErrorInputs(resolved2, segments));
2966
+ return;
2930
2967
  }
2968
+ const input = arg1;
2969
+ const resolved = typeof input === "function" ? input(flattenUserErrors()) : input;
2970
+ state.setAllUserErrors(normalizeErrorInputs(resolved, void 0));
2931
2971
  }
2932
- function addFieldErrors(errors) {
2933
- state.addUserErrors(filterToOwnFormKey(errors, "addFieldErrors"));
2934
- }
2935
- function clearFieldErrors(path) {
2972
+ function clearErrors(path) {
2936
2973
  if (path === void 0) {
2937
- const preserved = state.userErrors.get(paths.FORM_ERRORS_PATH_KEY);
2938
2974
  state.clearSchemaErrors();
2939
2975
  state.clearUserErrors();
2940
- if (preserved !== void 0 && preserved.length > 0) {
2941
- state.userErrors.set(paths.FORM_ERRORS_PATH_KEY, preserved);
2942
- }
2943
2976
  return;
2944
2977
  }
2945
2978
  const segments = paths.canonicalizePath(path).segments;
2946
2979
  state.clearSchemaErrors(segments);
2947
2980
  state.clearUserErrors(segments);
2948
2981
  }
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
2982
  const submitting = vue.computed(() => state.submitting.value);
2968
2983
  const submissionAttempts = vue.computed(() => state.submissionAttempts.value);
2969
2984
  const submitted = vue.computed(() => state.submitted.value);
@@ -3230,14 +3245,14 @@ function buildFormApi(state, formInstanceId, options = {}) {
3230
3245
  }
3231
3246
  const EMPTY_FIELD_RECORD = Object.freeze({});
3232
3247
  function record(path) {
3233
- const { segments } = paths.canonicalizePath(path);
3248
+ const segments = path === void 0 ? [] : paths.canonicalizePath(path).segments;
3234
3249
  const value = state.getValueAtPath(segments);
3235
3250
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
3236
3251
  return EMPTY_FIELD_RECORD;
3237
3252
  }
3238
3253
  const out = {};
3239
3254
  for (const key of Object.keys(value)) {
3240
- safeAssign(out, key, callTerminal(`${path}.${key}`));
3255
+ safeAssign(out, key, callTerminal(segments.length === 0 ? key : `${path}.${key}`));
3241
3256
  }
3242
3257
  return Object.freeze(out);
3243
3258
  }
@@ -3294,11 +3309,8 @@ function buildFormApi(state, formInstanceId, options = {}) {
3294
3309
  return errorsProxy;
3295
3310
  },
3296
3311
  toRef: gated(pathToRef),
3297
- setFieldErrors: gated(setFieldErrors),
3298
- addFieldErrors: gated(addFieldErrors),
3299
- clearFieldErrors: gated(clearFieldErrors),
3300
- setFormErrors: gated(setFormErrors),
3301
- clearFormErrors: gated(clearFormErrors),
3312
+ setErrors: gated(setErrors),
3313
+ clearErrors: gated(clearErrors),
3302
3314
  get meta() {
3303
3315
  void state.activate();
3304
3316
  return formMeta;
@@ -4816,25 +4828,14 @@ function createFormStore(options) {
4816
4828
  function getValueAtPath(path) {
4817
4829
  return getAtPath(form.value, path);
4818
4830
  }
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
4831
  function appendErrorsTo(map, entries) {
4830
4832
  for (const raw of entries) {
4831
- const err = rerouteFormLevelEntry(raw);
4832
- const key = pathKeyForEntry(err);
4833
+ const { key } = paths.canonicalizePath(raw.path);
4833
4834
  const current = map.get(key);
4834
4835
  if (current === void 0) {
4835
- map.set(key, [err]);
4836
+ map.set(key, [raw]);
4836
4837
  } else {
4837
- map.set(key, [...current, err]);
4838
+ map.set(key, [...current, raw]);
4838
4839
  }
4839
4840
  }
4840
4841
  }
@@ -4859,14 +4860,13 @@ function createFormStore(options) {
4859
4860
  schemaErrors.set(key, [...entries]);
4860
4861
  }
4861
4862
  function applySchemaErrorsForSubtree(path, entries) {
4862
- const parentKey = path.length === 0 ? paths.FORM_ERRORS_PATH_KEY : paths.canonicalizePath(path).key;
4863
+ const parentKey = paths.canonicalizePath(path).key;
4863
4864
  const grouped = /* @__PURE__ */ new Map();
4864
4865
  for (const raw of entries) {
4865
- const err = rerouteFormLevelEntry(raw);
4866
- const key = pathKeyForEntry(err);
4866
+ const { key } = paths.canonicalizePath(raw.path);
4867
4867
  const list = grouped.get(key);
4868
- if (list === void 0) grouped.set(key, [err]);
4869
- else list.push(err);
4868
+ if (list === void 0) grouped.set(key, [raw]);
4869
+ else list.push(raw);
4870
4870
  }
4871
4871
  if (!grouped.has(parentKey)) schemaErrors.delete(parentKey);
4872
4872
  for (const existingKey of [...schemaErrors.keys()]) {
@@ -4888,8 +4888,13 @@ function createFormStore(options) {
4888
4888
  function setAllUserErrors(entries) {
4889
4889
  replaceErrorsIn(userErrors, entries);
4890
4890
  }
4891
- function addUserErrors(entries) {
4892
- appendErrorsTo(userErrors, entries);
4891
+ function setUserErrorsForPath(path, entries) {
4892
+ const { key } = paths.canonicalizePath(path);
4893
+ if (entries.length === 0) {
4894
+ userErrors.delete(key);
4895
+ return;
4896
+ }
4897
+ userErrors.set(key, [...entries]);
4893
4898
  }
4894
4899
  function clearUserErrors(path) {
4895
4900
  clearErrorsIn(userErrors, path);
@@ -5083,25 +5088,25 @@ function createFormStore(options) {
5083
5088
  }
5084
5089
  }
5085
5090
  function clearHydrationFailedEntry() {
5086
- const existing = schemaErrors.get(paths.FORM_ERRORS_PATH_KEY);
5091
+ const existing = schemaErrors.get(paths.ROOT_PATH_KEY);
5087
5092
  if (existing === void 0) return;
5088
5093
  const filtered = existing.filter((e) => e.code !== AttaformErrorCode.HydrationFailed);
5089
5094
  if (filtered.length === 0) {
5090
- schemaErrors.delete(paths.FORM_ERRORS_PATH_KEY);
5095
+ schemaErrors.delete(paths.ROOT_PATH_KEY);
5091
5096
  } else {
5092
- schemaErrors.set(paths.FORM_ERRORS_PATH_KEY, filtered);
5097
+ schemaErrors.set(paths.ROOT_PATH_KEY, filtered);
5093
5098
  }
5094
5099
  }
5095
5100
  function appendHydrationFailedEntry(error) {
5096
5101
  const message = error instanceof Error ? error.message : typeof error === "string" ? error : "Hydration failed";
5097
5102
  const entry = {
5098
5103
  message,
5099
- path: [...paths.FORM_ERRORS_PATH],
5104
+ path: [...paths.ROOT_PATH],
5100
5105
  formKey,
5101
5106
  code: AttaformErrorCode.HydrationFailed
5102
5107
  };
5103
- const existing = schemaErrors.get(paths.FORM_ERRORS_PATH_KEY) ?? [];
5104
- schemaErrors.set(paths.FORM_ERRORS_PATH_KEY, [...existing, entry]);
5108
+ const existing = schemaErrors.get(paths.ROOT_PATH_KEY) ?? [];
5109
+ schemaErrors.set(paths.ROOT_PATH_KEY, [...existing, entry]);
5105
5110
  return entry;
5106
5111
  }
5107
5112
  function reset(nextDefaultValues) {
@@ -5334,7 +5339,7 @@ function createFormStore(options) {
5334
5339
  clearSchemaErrors,
5335
5340
  applySchemaErrorsForSubtree,
5336
5341
  setAllUserErrors,
5337
- addUserErrors,
5342
+ setUserErrorsForPath,
5338
5343
  clearUserErrors,
5339
5344
  getErrorsForPath,
5340
5345
  ensurePathOrdinal,
@@ -5411,12 +5416,38 @@ function captureErrorEntries(map) {
5411
5416
  for (const [k, v] of map) out.push([k, [...v]]);
5412
5417
  return out;
5413
5418
  }
5419
+ function dataEqual(a, b) {
5420
+ if (a === b) return true;
5421
+ if (a === null || b === null || a === void 0 || b === void 0) return false;
5422
+ if (typeof a !== typeof b) return false;
5423
+ if (Array.isArray(a)) {
5424
+ if (!Array.isArray(b) || a.length !== b.length) return false;
5425
+ for (let i = 0; i < a.length; i++) {
5426
+ if (!dataEqual(a[i], b[i])) return false;
5427
+ }
5428
+ return true;
5429
+ }
5430
+ if (Array.isArray(b)) return false;
5431
+ if (typeof a === "object") {
5432
+ const ao = a;
5433
+ const bo = b;
5434
+ const keys = Object.keys(ao);
5435
+ if (keys.length !== Object.keys(bo).length) return false;
5436
+ for (const k of keys) {
5437
+ if (!Object.prototype.hasOwnProperty.call(bo, k)) return false;
5438
+ if (!dataEqual(ao[k], bo[k])) return false;
5439
+ }
5440
+ return true;
5441
+ }
5442
+ return false;
5443
+ }
5414
5444
  function errorFieldsEqual(av, bvi) {
5415
5445
  if (av === bvi) return true;
5416
5446
  if (av.message !== bvi.message) return false;
5417
5447
  if (av.code !== bvi.code) return false;
5418
5448
  if (av.formKey !== bvi.formKey) return false;
5419
- return av.path === bvi.path || paths.pathsEqual(av.path, bvi.path);
5449
+ if (av.path !== bvi.path && !paths.pathsEqual(av.path, bvi.path)) return false;
5450
+ return dataEqual(av.data, bvi.data);
5420
5451
  }
5421
5452
  function errorsEqual(a, b) {
5422
5453
  if (a.length !== b.length) return false;
@@ -5906,7 +5937,7 @@ function useRegister() {
5906
5937
  const dirs = instance.vnode.dirs;
5907
5938
  if (dirs !== null && dirs !== void 0) {
5908
5939
  for (const dir of dirs) {
5909
- const marked = dir.dir?.[paths.V_REGISTER_MARKER];
5940
+ const marked = dir.dir?.[registerProtocol.V_REGISTER_MARKER];
5910
5941
  if (marked === true) {
5911
5942
  capturedRegisterValue.value = dir.value;
5912
5943
  break;
@@ -5922,7 +5953,7 @@ function useRegister() {
5922
5953
  vue.onMounted(() => {
5923
5954
  const el = instance.vnode.el;
5924
5955
  if (el !== null && el !== void 0 && typeof el === "object") {
5925
- el[paths.REGISTER_OWNER_MARKER] = true;
5956
+ el[registerProtocol.REGISTER_OWNER_MARKER] = true;
5926
5957
  }
5927
5958
  if (capturedRegisterValue.value === void 0) {
5928
5959
  warnNoParentRV(instance);
@@ -6721,6 +6752,30 @@ function useWizard(options) {
6721
6752
  }
6722
6753
  return out;
6723
6754
  }
6755
+ function collectCallbackErrors(keys) {
6756
+ const out = [];
6757
+ for (const key of keys) {
6758
+ const store = registry.forms.get(key);
6759
+ if (store === void 0) continue;
6760
+ for (const errs of store.userErrors.values()) {
6761
+ for (const err of errs) out.push(toWizardAggregateError(err, key));
6762
+ }
6763
+ }
6764
+ return out;
6765
+ }
6766
+ async function focusFirstWizardError(errors) {
6767
+ if (options.focusFirstError === false) return;
6768
+ const firstFailedKey = errors[0]?.formKey;
6769
+ if (firstFailedKey === void 0 || !isCompiledKey(firstFailedKey)) return;
6770
+ moveTo(firstFailedKey);
6771
+ await vue.nextTick();
6772
+ const failedForm = formsRecord.value[firstFailedKey];
6773
+ if (failedForm === void 0) return;
6774
+ const failedSource = asSubmissionSource(failedForm);
6775
+ if (typeof failedSource.applyInvalidSubmitPolicy === "function") {
6776
+ failedSource.applyInvalidSubmitPolicy();
6777
+ }
6778
+ }
6724
6779
  function handleSubmit(onSubmit, onError) {
6725
6780
  return async function submitHandler(event) {
6726
6781
  if (event !== void 0 && typeof event.preventDefault === "function") {
@@ -6783,6 +6838,18 @@ function useWizard(options) {
6783
6838
  }
6784
6839
  const ctx = buildSubmitContext(valuesMap, currentKey, final);
6785
6840
  await onSubmit(ctx);
6841
+ const callbackErrors = collectCallbackErrors(results.keys());
6842
+ if (callbackErrors.length > 0) {
6843
+ await focusFirstWizardError(callbackErrors);
6844
+ if (onError !== void 0) {
6845
+ try {
6846
+ await onError(callbackErrors);
6847
+ } catch (cause) {
6848
+ throw new paths.SubmitErrorHandlerError("User-provided onError threw", { cause });
6849
+ }
6850
+ }
6851
+ return;
6852
+ }
6786
6853
  if (final) {
6787
6854
  done.value = true;
6788
6855
  } else {
@@ -6792,20 +6859,7 @@ function useWizard(options) {
6792
6859
  if (target !== void 0) moveTo(target.key);
6793
6860
  }
6794
6861
  } else {
6795
- if (options.focusFirstError !== false) {
6796
- const firstFailedKey = errors[0]?.formKey;
6797
- if (firstFailedKey !== void 0 && isCompiledKey(firstFailedKey)) {
6798
- moveTo(firstFailedKey);
6799
- await vue.nextTick();
6800
- const failedForm = formsRecord.value[firstFailedKey];
6801
- if (failedForm !== void 0) {
6802
- const failedSource = asSubmissionSource(failedForm);
6803
- if (typeof failedSource.applyInvalidSubmitPolicy === "function") {
6804
- failedSource.applyInvalidSubmitPolicy();
6805
- }
6806
- }
6807
- }
6808
- }
6862
+ await focusFirstWizardError(errors);
6809
6863
  if (onError !== void 0) {
6810
6864
  try {
6811
6865
  await onError(errors);
@@ -7025,7 +7079,6 @@ exports.isPlainRecord = isPlainRecord;
7025
7079
  exports.isUnset = isUnset;
7026
7080
  exports.lazy = lazy;
7027
7081
  exports.makeDefaultDisplayState = makeDefaultDisplayState;
7028
- exports.normalizeNumericOption = normalizeNumericOption;
7029
7082
  exports.safeAssign = safeAssign;
7030
7083
  exports.safeOwnRead = safeOwnRead;
7031
7084
  exports.setAtPath = setAtPath;
@@ -7034,4 +7087,4 @@ exports.unset = unset;
7034
7087
  exports.useAbstractForm = useAbstractForm;
7035
7088
  exports.useRegister = useRegister;
7036
7089
  exports.useWizard = useWizard;
7037
- //# sourceMappingURL=attaform.DsQkXE3o.cjs.map
7090
+ //# sourceMappingURL=attaform.DRQjF16I.cjs.map