attaform 0.16.2 → 0.16.4

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 (67) hide show
  1. package/README.md +7 -7
  2. package/dist/chunks/devtools.cjs +1 -1
  3. package/dist/chunks/devtools.mjs +1 -1
  4. package/dist/index.cjs +3 -2
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.cts +33 -4
  7. package/dist/index.d.mts +33 -4
  8. package/dist/index.d.ts +33 -4
  9. package/dist/index.mjs +3 -3
  10. package/dist/nuxt.d.cts +1 -1
  11. package/dist/nuxt.d.mts +1 -1
  12. package/dist/nuxt.d.ts +1 -1
  13. package/dist/shared/attaform.C8CyvYa_.cjs +36 -0
  14. package/dist/shared/attaform.C8CyvYa_.cjs.map +1 -0
  15. package/dist/shared/{attaform.CWCx2r0x.d.ts → attaform.CCQkY4Ta.d.ts} +1 -1
  16. package/dist/shared/{attaform.c_NzdRyc.cjs → attaform.CIwZtbGV.cjs} +6 -2
  17. package/dist/shared/attaform.CIwZtbGV.cjs.map +1 -0
  18. package/dist/shared/{attaform.CVv9Oh0a.d.mts → attaform.CMRmwGDt.d.cts} +1 -1
  19. package/dist/shared/{attaform.0Gxd_OOx.d.cts → attaform.CU3JperC.d.cts} +193 -27
  20. package/dist/shared/{attaform.0Gxd_OOx.d.mts → attaform.CU3JperC.d.mts} +193 -27
  21. package/dist/shared/{attaform.0Gxd_OOx.d.ts → attaform.CU3JperC.d.ts} +193 -27
  22. package/dist/shared/{attaform.Dq5BabH1.d.cts → attaform.CXMOheyZ.d.mts} +1 -1
  23. package/dist/shared/attaform.D13GMFgK.mjs +32 -0
  24. package/dist/shared/attaform.D13GMFgK.mjs.map +1 -0
  25. package/dist/shared/{attaform.jrxE_xZw.mjs → attaform.DZRj9s0s.mjs} +5 -3
  26. package/dist/shared/attaform.DZRj9s0s.mjs.map +1 -0
  27. package/dist/shared/{attaform.Bp1c-uGF.cjs → attaform.Dd_pWnmn.cjs} +17 -29
  28. package/dist/shared/attaform.Dd_pWnmn.cjs.map +1 -0
  29. package/dist/shared/{attaform.DILbdvfo.mjs → attaform.DyV1O4tI.mjs} +111 -22
  30. package/dist/shared/attaform.DyV1O4tI.mjs.map +1 -0
  31. package/dist/shared/{attaform.DdnithOf.mjs → attaform.UA19EF3J.mjs} +17 -29
  32. package/dist/shared/attaform.UA19EF3J.mjs.map +1 -0
  33. package/dist/shared/{attaform.C9Ph2SMx.cjs → attaform.fegmBJaq.cjs} +111 -21
  34. package/dist/shared/attaform.fegmBJaq.cjs.map +1 -0
  35. package/dist/shared/{attaform.CvOXSpCb.mjs → attaform.g7rfuXdz.mjs} +13 -16
  36. package/dist/shared/attaform.g7rfuXdz.mjs.map +1 -0
  37. package/dist/shared/{attaform.DfrYByDj.cjs → attaform.keLBaHB6.cjs} +13 -16
  38. package/dist/shared/attaform.keLBaHB6.cjs.map +1 -0
  39. package/dist/zod-v3.cjs +2 -2
  40. package/dist/zod-v3.d.cts +19 -14
  41. package/dist/zod-v3.d.mts +19 -14
  42. package/dist/zod-v3.d.ts +19 -14
  43. package/dist/zod-v3.mjs +2 -2
  44. package/dist/zod-v4.cjs +2 -2
  45. package/dist/zod-v4.d.cts +94 -5
  46. package/dist/zod-v4.d.mts +94 -5
  47. package/dist/zod-v4.d.ts +94 -5
  48. package/dist/zod-v4.mjs +2 -2
  49. package/dist/zod.cjs +23 -5
  50. package/dist/zod.cjs.map +1 -1
  51. package/dist/zod.d.cts +51 -7
  52. package/dist/zod.d.mts +51 -7
  53. package/dist/zod.d.ts +51 -7
  54. package/dist/zod.mjs +22 -5
  55. package/dist/zod.mjs.map +1 -1
  56. package/package.json +1 -1
  57. package/dist/shared/attaform.BAuJTWuT.d.mts +0 -84
  58. package/dist/shared/attaform.Bp1c-uGF.cjs.map +0 -1
  59. package/dist/shared/attaform.C9Ph2SMx.cjs.map +0 -1
  60. package/dist/shared/attaform.CvOXSpCb.mjs.map +0 -1
  61. package/dist/shared/attaform.DILbdvfo.mjs.map +0 -1
  62. package/dist/shared/attaform.DdnithOf.mjs.map +0 -1
  63. package/dist/shared/attaform.DfrYByDj.cjs.map +0 -1
  64. package/dist/shared/attaform.c_NzdRyc.cjs.map +0 -1
  65. package/dist/shared/attaform.jrxE_xZw.mjs.map +0 -1
  66. package/dist/shared/attaform.ls_7jBYc.d.ts +0 -84
  67. package/dist/shared/attaform.xIcmqscx.d.cts +0 -84
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const vue = require('vue');
4
- const paths = require('./attaform.c_NzdRyc.cjs');
4
+ const paths = require('./attaform.CIwZtbGV.cjs');
5
5
  const plugin = require('./attaform.rIRYSUI1.cjs');
6
6
 
7
7
  function isDescendable(value) {
@@ -415,20 +415,20 @@ function setAtPathWithSchemaFillImpl(root, schema, fullPath, value, startIdx) {
415
415
  return rec;
416
416
  }
417
417
 
418
- function buildFieldStateAccessor(state) {
418
+ function buildFieldStateAccessor(state, getFormMetaBase) {
419
419
  const cache = /* @__PURE__ */ new Map();
420
420
  return function getFieldState(pathInput) {
421
421
  const { segments, key } = paths.canonicalizePath(pathInput);
422
422
  const cached = cache.get(key);
423
423
  if (cached !== void 0) return cached;
424
424
  const c = vue.computed(
425
- () => state.schema.isLeafAtPath(segments) ? buildLeafFieldState(state, segments, key) : buildContainerFieldState(state, segments)
425
+ () => state.schema.isLeafAtPath(segments) ? buildLeafFieldState(state, segments, key, getFormMetaBase) : buildContainerFieldState(state, segments, key, getFormMetaBase)
426
426
  );
427
427
  cache.set(key, c);
428
428
  return c;
429
429
  };
430
430
  }
431
- function buildLeafFieldState(state, segments, key) {
431
+ function buildLeafFieldStateBase(state, segments, key) {
432
432
  const record = state.fields.get(key);
433
433
  const value = state.getValueAtPath(segments);
434
434
  const original = state.originals.get(key)?.value;
@@ -472,7 +472,11 @@ function buildLeafFieldState(state, segments, key) {
472
472
  meta: resolved.meta
473
473
  };
474
474
  }
475
- function buildContainerFieldState(state, segments, _key) {
475
+ function buildLeafFieldState(state, segments, key, getFormMetaBase) {
476
+ const base = buildLeafFieldStateBase(state, segments, key);
477
+ return decorateWithDerivedProps(base, state, getFormMetaBase);
478
+ }
479
+ function buildContainerFieldStateBase(state, segments, _key) {
476
480
  const formValue = state.form.value;
477
481
  const value = state.getValueAtPath(segments);
478
482
  const original = state.originals.get(paths.canonicalizePath(segments).key)?.value;
@@ -538,6 +542,15 @@ function buildContainerFieldState(state, segments, _key) {
538
542
  meta: resolved.meta
539
543
  };
540
544
  }
545
+ function buildContainerFieldState(state, segments, key, getFormMetaBase) {
546
+ const base = buildContainerFieldStateBase(state, segments);
547
+ return decorateWithDerivedProps(base, state, getFormMetaBase);
548
+ }
549
+ function decorateWithDerivedProps(base, state, getFormMetaBase) {
550
+ const firstError = base.errors[0];
551
+ const showErrors = base.errors.length > 0 && state.shouldShowErrors(base, getFormMetaBase());
552
+ return { ...base, showErrors, firstError };
553
+ }
541
554
  function aggregateErrorsAt(state, prefix) {
542
555
  const formValue = state.form.value;
543
556
  const buckets = /* @__PURE__ */ new Map();
@@ -547,7 +560,7 @@ function aggregateErrorsAt(state, prefix) {
547
560
  const segs = paths.segmentsForPathKey(pathKey);
548
561
  if (segs === null) continue;
549
562
  if (!paths.isPathPrefix(prefix, segs)) continue;
550
- if (segs.length > 0 && !hasAtPath(formValue, segs)) continue;
563
+ if (pathKey === paths.FORM_ERRORS_PATH_KEY) ; else if (segs.length > 0 && !hasAtPath(formValue, segs)) continue;
551
564
  const ordinal = state.ensurePathOrdinal(pathKey);
552
565
  const existing = buckets.get(ordinal);
553
566
  if (existing === void 0) buckets.set(ordinal, [...list]);
@@ -868,6 +881,8 @@ const FIELD_STATE_KEYS = /* @__PURE__ */ new Set([
868
881
  "errors",
869
882
  "validating",
870
883
  "valid",
884
+ "showErrors",
885
+ "firstError",
871
886
  "path",
872
887
  "blank",
873
888
  "label",
@@ -875,8 +890,8 @@ const FIELD_STATE_KEYS = /* @__PURE__ */ new Set([
875
890
  "placeholder",
876
891
  "meta"
877
892
  ]);
878
- function buildFieldStateProxy(state) {
879
- const getFieldStateAt = buildFieldStateAccessor(state);
893
+ function buildFieldStateProxy(state, getFormMetaBase) {
894
+ const getFieldStateAt = buildFieldStateAccessor(state, getFormMetaBase);
880
895
  const snapshotFieldStateAt = (path) => {
881
896
  const view = getFieldStateAt(path).value;
882
897
  const snapshot = {};
@@ -2075,13 +2090,13 @@ function buildFormApi(state, formInstanceId, options = {}) {
2075
2090
  }
2076
2091
  function setFormErrors(errors) {
2077
2092
  if (errors.length === 0) {
2078
- state.userErrors.delete(paths.ROOT_PATH_KEY);
2093
+ state.userErrors.delete(paths.FORM_ERRORS_PATH_KEY);
2079
2094
  return;
2080
2095
  }
2081
2096
  state.userErrors.set(
2082
- paths.ROOT_PATH_KEY,
2097
+ paths.FORM_ERRORS_PATH_KEY,
2083
2098
  errors.map((e) => ({
2084
- path: [],
2099
+ path: [...paths.FORM_ERRORS_PATH],
2085
2100
  message: e.message,
2086
2101
  formKey: state.formKey,
2087
2102
  code: e.code ?? "atta:form-error"
@@ -2089,7 +2104,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2089
2104
  );
2090
2105
  }
2091
2106
  function clearFormErrors() {
2092
- state.userErrors.delete(paths.ROOT_PATH_KEY);
2107
+ state.userErrors.delete(paths.FORM_ERRORS_PATH_KEY);
2093
2108
  }
2094
2109
  const submitting = vue.computed(() => state.submitting.value);
2095
2110
  const submitCount = vue.computed(() => state.submitCount.value);
@@ -2107,7 +2122,20 @@ function buildFormApi(state, formInstanceId, options = {}) {
2107
2122
  const metaErrors = vue.computed(
2108
2123
  () => aggregateErrorsAt(state, [])
2109
2124
  );
2110
- const getRootFieldStateAt = buildFieldStateAccessor(state);
2125
+ const getFormMetaBase = () => {
2126
+ const rootBase = buildContainerFieldStateBase(state, paths.ROOT_PATH);
2127
+ return {
2128
+ ...rootBase,
2129
+ submitting: state.submitting.value,
2130
+ submitCount: state.submitCount.value,
2131
+ submitError: state.submitError.value,
2132
+ canUndo: canUndo.value,
2133
+ canRedo: canRedo.value,
2134
+ historySize: historySize.value,
2135
+ instanceId: formInstanceId
2136
+ };
2137
+ };
2138
+ const getRootFieldStateAt = buildFieldStateAccessor(state, getFormMetaBase);
2111
2139
  const rootFieldState = getRootFieldStateAt([]);
2112
2140
  const formMeta = vue.readonly(
2113
2141
  vue.reactive({
@@ -2143,6 +2171,13 @@ function buildFormApi(state, formInstanceId, options = {}) {
2143
2171
  // keep the explicit form-level computation for the gate.
2144
2172
  valid,
2145
2173
  errors: metaErrors,
2174
+ // `showErrors` / `firstError` flow through the same root
2175
+ // field-state computed as the rest of the FieldState surface,
2176
+ // so `form.meta.showErrors` matches `form.fields().showErrors`
2177
+ // exactly — the predicate runs once at the root and the result
2178
+ // is shared.
2179
+ showErrors: vue.computed(() => rootFieldState.value.showErrors),
2180
+ firstError: vue.computed(() => rootFieldState.value.firstError),
2146
2181
  path: vue.computed(() => rootFieldState.value.path),
2147
2182
  blank: vue.computed(() => rootFieldState.value.blank),
2148
2183
  label: vue.computed(() => rootFieldState.value.label),
@@ -2209,6 +2244,10 @@ function buildFormApi(state, formInstanceId, options = {}) {
2209
2244
  const segments = paths.canonicalizePath(pathInput).segments;
2210
2245
  await persistence.clearPersistedDraft(segments);
2211
2246
  };
2247
+ function touch(pathInput) {
2248
+ const segments = pathInput === void 0 ? paths.ROOT_PATH : paths.canonicalizePath(pathInput).segments;
2249
+ state.touchAtPath(segments);
2250
+ }
2212
2251
  const focusFirstError = (options2) => {
2213
2252
  const target = state.getFirstErrorElement(formInstanceId);
2214
2253
  if (target === null) return false;
@@ -2226,7 +2265,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2226
2265
  return readonlySetSnapshot(state.blankPaths);
2227
2266
  });
2228
2267
  const valuesProxy = buildValuesProxy(state.form);
2229
- const fieldStateProxy = buildFieldStateProxy(state);
2268
+ const fieldStateProxy = buildFieldStateProxy(state, getFormMetaBase);
2230
2269
  return {
2231
2270
  handleSubmit,
2232
2271
  // `values` is the callable readonly Proxy. Each `get` trap reads
@@ -2255,6 +2294,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2255
2294
  clearPersistedDraft,
2256
2295
  focusFirstError,
2257
2296
  scrollToFirstError,
2297
+ touch,
2258
2298
  undo,
2259
2299
  redo,
2260
2300
  append: fieldArrays.append,
@@ -2268,6 +2308,16 @@ function buildFormApi(state, formInstanceId, options = {}) {
2268
2308
  };
2269
2309
  }
2270
2310
 
2311
+ const defaultShouldShowErrors = (field, formMeta) => formMeta.submitCount > 0 || field.touched === true && field.dirty;
2312
+ const SHOW_ALWAYS = () => true;
2313
+ const SHOW_NEVER = () => false;
2314
+ function resolveShouldShowErrors(config) {
2315
+ if (config === void 0) return defaultShouldShowErrors;
2316
+ if (config === true) return SHOW_ALWAYS;
2317
+ if (config === false) return SHOW_NEVER;
2318
+ return config;
2319
+ }
2320
+
2271
2321
  function isHydratedFieldRecord(value) {
2272
2322
  if (typeof value !== "object" || value === null) return false;
2273
2323
  const r = value;
@@ -2312,6 +2362,9 @@ function createFormStore(options) {
2312
2362
  const resetListeners = /* @__PURE__ */ new Set();
2313
2363
  const persistOptIns = plugin.createPersistOptInRegistry();
2314
2364
  const coerceIndex = resolveCoercionIndex(options.coerce);
2365
+ const resolvedShouldShowErrors = resolveShouldShowErrors(
2366
+ options.shouldShowErrors
2367
+ );
2315
2368
  const cleanupHooks = [];
2316
2369
  const modules = /* @__PURE__ */ new Map();
2317
2370
  const completedConstraints = defaultValues === void 0 ? void 0 : mergeStructural(schema, [], defaultValues);
@@ -2741,9 +2794,20 @@ function createFormStore(options) {
2741
2794
  function getValueAtPath(path) {
2742
2795
  return getAtPath(form.value, path);
2743
2796
  }
2797
+ function rerouteFormLevelEntry(err) {
2798
+ if (err.path.length === 0) {
2799
+ return { ...err, path: [...paths.FORM_ERRORS_PATH] };
2800
+ }
2801
+ return err;
2802
+ }
2803
+ function pathKeyForEntry(err) {
2804
+ if (err.path.length === 0) return paths.FORM_ERRORS_PATH_KEY;
2805
+ return paths.canonicalizePath(err.path).key;
2806
+ }
2744
2807
  function appendErrorsTo(map, entries) {
2745
- for (const err of entries) {
2746
- const { key } = paths.canonicalizePath(err.path);
2808
+ for (const raw of entries) {
2809
+ const err = rerouteFormLevelEntry(raw);
2810
+ const key = pathKeyForEntry(err);
2747
2811
  const current = map.get(key);
2748
2812
  if (current === void 0) {
2749
2813
  map.set(key, [err]);
@@ -2773,16 +2837,18 @@ function createFormStore(options) {
2773
2837
  schemaErrors.set(key, [...entries]);
2774
2838
  }
2775
2839
  function applySchemaErrorsForSubtree(path, entries) {
2776
- const { key: parentKey } = paths.canonicalizePath(path);
2840
+ const parentKey = path.length === 0 ? paths.FORM_ERRORS_PATH_KEY : paths.canonicalizePath(path).key;
2777
2841
  const grouped = /* @__PURE__ */ new Map();
2778
- for (const err of entries) {
2779
- const { key } = paths.canonicalizePath(err.path);
2842
+ for (const raw of entries) {
2843
+ const err = rerouteFormLevelEntry(raw);
2844
+ const key = pathKeyForEntry(err);
2780
2845
  const list = grouped.get(key);
2781
2846
  if (list === void 0) grouped.set(key, [err]);
2782
2847
  else list.push(err);
2783
2848
  }
2784
2849
  if (!grouped.has(parentKey)) schemaErrors.delete(parentKey);
2785
2850
  for (const existingKey of [...schemaErrors.keys()]) {
2851
+ if (existingKey === parentKey) continue;
2786
2852
  if (isPathKeyUnder(existingKey, path) && !grouped.has(existingKey)) {
2787
2853
  schemaErrors.delete(existingKey);
2788
2854
  }
@@ -2879,6 +2945,24 @@ function createFormStore(options) {
2879
2945
  const { key } = paths.canonicalizePath(path);
2880
2946
  touchFieldRecord(key, path, { touched: true });
2881
2947
  }
2948
+ function touchAtPath(segments) {
2949
+ const formValue = form.value;
2950
+ let touchedAny = false;
2951
+ for (const [, entry] of originals) {
2952
+ if (!isPathPrefix(segments, entry.segments)) continue;
2953
+ if (!hasAtPath(formValue, entry.segments)) continue;
2954
+ touchedAny = true;
2955
+ const leafKey = paths.canonicalizePath(entry.segments).key;
2956
+ const current = fields.get(leafKey);
2957
+ if (current?.touched === true) continue;
2958
+ touchFieldRecord(leafKey, entry.segments, { touched: true });
2959
+ }
2960
+ if (!touchedAny && plugin.__DEV__) {
2961
+ console.warn(
2962
+ `[attaform] form.touch(): no fields resolved at path ${JSON.stringify(segments)}. Check the path matches an existing field or container.`
2963
+ );
2964
+ }
2965
+ }
2882
2966
  function reset(nextDefaultValues) {
2883
2967
  const next = schema.getDefaultValues({
2884
2968
  useDefaultSchemaValues: true,
@@ -3055,6 +3139,7 @@ function createFormStore(options) {
3055
3139
  originals,
3056
3140
  schema,
3057
3141
  ssr,
3142
+ shouldShowErrors: resolvedShouldShowErrors,
3058
3143
  submitting,
3059
3144
  activeSubmissions,
3060
3145
  submitCount,
@@ -3081,6 +3166,7 @@ function createFormStore(options) {
3081
3166
  deregisterElement,
3082
3167
  markFocused,
3083
3168
  markTouched,
3169
+ touchAtPath,
3084
3170
  markConnectedOptimistically,
3085
3171
  isPristineAtPath,
3086
3172
  getFieldRecord,
@@ -3273,6 +3359,7 @@ function mergeWithDefaults(defaults, configuration) {
3273
3359
  const coerce = configuration.coerce ?? defaults.coerce;
3274
3360
  const validateOn = configuration.validateOn ?? defaults.validateOn;
3275
3361
  const debounceMs = configuration.debounceMs ?? defaults.debounceMs;
3362
+ const shouldShowErrors = configuration.shouldShowErrors ?? defaults.shouldShowErrors;
3276
3363
  return {
3277
3364
  ...configuration,
3278
3365
  ...strict === void 0 ? {} : { strict },
@@ -3281,7 +3368,8 @@ function mergeWithDefaults(defaults, configuration) {
3281
3368
  ...rememberVariants === void 0 ? {} : { rememberVariants },
3282
3369
  ...coerce === void 0 ? {} : { coerce },
3283
3370
  ...validateOn === void 0 ? {} : { validateOn },
3284
- ...debounceMs === void 0 ? {} : { debounceMs }
3371
+ ...debounceMs === void 0 ? {} : { debounceMs },
3372
+ ...shouldShowErrors === void 0 ? {} : { shouldShowErrors }
3285
3373
  };
3286
3374
  }
3287
3375
  const HISTORY_MODULE_KEY = "history";
@@ -3304,6 +3392,7 @@ function buildFreshState(key, schema, configuration, registry) {
3304
3392
  ssr: registry.ssr,
3305
3393
  ...configuration.rememberVariants !== void 0 ? { rememberVariants: configuration.rememberVariants } : {},
3306
3394
  ...configuration.coerce !== void 0 ? { coerce: configuration.coerce } : {},
3395
+ ...configuration.shouldShowErrors !== void 0 ? { shouldShowErrors: configuration.shouldShowErrors } : {},
3307
3396
  ...initialBlankPaths !== void 0 ? { initialBlankPaths } : {}
3308
3397
  };
3309
3398
  const state = createFormStore(createOptions);
@@ -3686,6 +3775,7 @@ function warnIfAmbientProviderHadDuplicates() {
3686
3775
 
3687
3776
  exports.AttaformErrorCode = AttaformErrorCode;
3688
3777
  exports.defaultCoercionRules = defaultCoercionRules;
3778
+ exports.defaultShouldShowErrors = defaultShouldShowErrors;
3689
3779
  exports.defineCoercion = defineCoercion;
3690
3780
  exports.getAtPath = getAtPath;
3691
3781
  exports.humanize = humanize;
@@ -3696,4 +3786,4 @@ exports.setAtPath = setAtPath;
3696
3786
  exports.slimKindOf = slimKindOf;
3697
3787
  exports.unset = unset;
3698
3788
  exports.useAbstractForm = useAbstractForm;
3699
- //# sourceMappingURL=attaform.C9Ph2SMx.cjs.map
3789
+ //# sourceMappingURL=attaform.fegmBJaq.cjs.map