attaform 0.16.3 → 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 (57) hide show
  1. package/dist/chunks/devtools.cjs +1 -1
  2. package/dist/chunks/devtools.mjs +1 -1
  3. package/dist/index.cjs +3 -2
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.cts +33 -4
  6. package/dist/index.d.mts +33 -4
  7. package/dist/index.d.ts +33 -4
  8. package/dist/index.mjs +3 -3
  9. package/dist/nuxt.d.cts +1 -1
  10. package/dist/nuxt.d.mts +1 -1
  11. package/dist/nuxt.d.ts +1 -1
  12. package/dist/shared/{attaform.lFNwBcA3.d.ts → attaform.CCQkY4Ta.d.ts} +1 -1
  13. package/dist/shared/{attaform.c_NzdRyc.cjs → attaform.CIwZtbGV.cjs} +6 -2
  14. package/dist/shared/attaform.CIwZtbGV.cjs.map +1 -0
  15. package/dist/shared/{attaform.Bls_kFR6.d.mts → attaform.CMRmwGDt.d.cts} +1 -1
  16. package/dist/shared/{attaform._EqYNPYF.d.ts → attaform.CU3JperC.d.cts} +172 -14
  17. package/dist/shared/{attaform._EqYNPYF.d.cts → attaform.CU3JperC.d.mts} +172 -14
  18. package/dist/shared/{attaform._EqYNPYF.d.mts → attaform.CU3JperC.d.ts} +172 -14
  19. package/dist/shared/{attaform.DLnKT7wk.d.cts → attaform.CXMOheyZ.d.mts} +1 -1
  20. package/dist/shared/{attaform.jrxE_xZw.mjs → attaform.DZRj9s0s.mjs} +5 -3
  21. package/dist/shared/attaform.DZRj9s0s.mjs.map +1 -0
  22. package/dist/shared/{attaform.KrNw10aW.cjs → attaform.Dd_pWnmn.cjs} +12 -13
  23. package/dist/shared/attaform.Dd_pWnmn.cjs.map +1 -0
  24. package/dist/shared/{attaform.DILbdvfo.mjs → attaform.DyV1O4tI.mjs} +111 -22
  25. package/dist/shared/attaform.DyV1O4tI.mjs.map +1 -0
  26. package/dist/shared/{attaform.CFA6y0KF.mjs → attaform.UA19EF3J.mjs} +12 -13
  27. package/dist/shared/attaform.UA19EF3J.mjs.map +1 -0
  28. package/dist/shared/{attaform.C9Ph2SMx.cjs → attaform.fegmBJaq.cjs} +111 -21
  29. package/dist/shared/attaform.fegmBJaq.cjs.map +1 -0
  30. package/dist/shared/{attaform.XYOMTvuO.mjs → attaform.g7rfuXdz.mjs} +7 -3
  31. package/dist/shared/attaform.g7rfuXdz.mjs.map +1 -0
  32. package/dist/shared/{attaform.DGuGGNg9.cjs → attaform.keLBaHB6.cjs} +7 -3
  33. package/dist/shared/attaform.keLBaHB6.cjs.map +1 -0
  34. package/dist/zod-v3.cjs +2 -2
  35. package/dist/zod-v3.d.cts +3 -3
  36. package/dist/zod-v3.d.mts +3 -3
  37. package/dist/zod-v3.d.ts +3 -3
  38. package/dist/zod-v3.mjs +2 -2
  39. package/dist/zod-v4.cjs +2 -2
  40. package/dist/zod-v4.d.cts +3 -3
  41. package/dist/zod-v4.d.mts +3 -3
  42. package/dist/zod-v4.d.ts +3 -3
  43. package/dist/zod-v4.mjs +2 -2
  44. package/dist/zod.cjs +3 -3
  45. package/dist/zod.d.cts +3 -3
  46. package/dist/zod.d.mts +3 -3
  47. package/dist/zod.d.ts +3 -3
  48. package/dist/zod.mjs +3 -3
  49. package/package.json +1 -1
  50. package/dist/shared/attaform.C9Ph2SMx.cjs.map +0 -1
  51. package/dist/shared/attaform.CFA6y0KF.mjs.map +0 -1
  52. package/dist/shared/attaform.DGuGGNg9.cjs.map +0 -1
  53. package/dist/shared/attaform.DILbdvfo.mjs.map +0 -1
  54. package/dist/shared/attaform.KrNw10aW.cjs.map +0 -1
  55. package/dist/shared/attaform.XYOMTvuO.mjs.map +0 -1
  56. package/dist/shared/attaform.c_NzdRyc.cjs.map +0 -1
  57. package/dist/shared/attaform.jrxE_xZw.mjs.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { computed, ref, watchEffect, getCurrentScope, onScopeDispose, shallowReadonly, readonly, reactive, watch, markRaw, shallowRef, getCurrentInstance, provide, useId, toRaw, inject } from 'vue';
2
- import { s as segmentsForPathKey, i as isPathPrefix, c as canonicalizePath, a as ROOT_PATH_KEY } from './attaform.jrxE_xZw.mjs';
2
+ import { s as segmentsForPathKey, i as isPathPrefix, F as FORM_ERRORS_PATH_KEY, c as canonicalizePath, R as ROOT_PATH, b as FORM_ERRORS_PATH } from './attaform.DZRj9s0s.mjs';
3
3
  import { _ as __DEV__, e as SubmitErrorHandlerError, A as AnonPersistError, m as captureUserCallSite, n as enforceSensitiveCheck, o as createPersistOptInRegistry, b as InvalidUseFormConfigError, p as ensureAttaformInstalled, l as useRegistry, q as kFormContext, r as kFormInstanceId, d as ReservedFormKeyError } from './attaform.BfMxsfmE.mjs';
4
4
 
5
5
  function isDescendable(value) {
@@ -413,20 +413,20 @@ function setAtPathWithSchemaFillImpl(root, schema, fullPath, value, startIdx) {
413
413
  return rec;
414
414
  }
415
415
 
416
- function buildFieldStateAccessor(state) {
416
+ function buildFieldStateAccessor(state, getFormMetaBase) {
417
417
  const cache = /* @__PURE__ */ new Map();
418
418
  return function getFieldState(pathInput) {
419
419
  const { segments, key } = canonicalizePath(pathInput);
420
420
  const cached = cache.get(key);
421
421
  if (cached !== void 0) return cached;
422
422
  const c = computed(
423
- () => state.schema.isLeafAtPath(segments) ? buildLeafFieldState(state, segments, key) : buildContainerFieldState(state, segments)
423
+ () => state.schema.isLeafAtPath(segments) ? buildLeafFieldState(state, segments, key, getFormMetaBase) : buildContainerFieldState(state, segments, key, getFormMetaBase)
424
424
  );
425
425
  cache.set(key, c);
426
426
  return c;
427
427
  };
428
428
  }
429
- function buildLeafFieldState(state, segments, key) {
429
+ function buildLeafFieldStateBase(state, segments, key) {
430
430
  const record = state.fields.get(key);
431
431
  const value = state.getValueAtPath(segments);
432
432
  const original = state.originals.get(key)?.value;
@@ -470,7 +470,11 @@ function buildLeafFieldState(state, segments, key) {
470
470
  meta: resolved.meta
471
471
  };
472
472
  }
473
- function buildContainerFieldState(state, segments, _key) {
473
+ function buildLeafFieldState(state, segments, key, getFormMetaBase) {
474
+ const base = buildLeafFieldStateBase(state, segments, key);
475
+ return decorateWithDerivedProps(base, state, getFormMetaBase);
476
+ }
477
+ function buildContainerFieldStateBase(state, segments, _key) {
474
478
  const formValue = state.form.value;
475
479
  const value = state.getValueAtPath(segments);
476
480
  const original = state.originals.get(canonicalizePath(segments).key)?.value;
@@ -536,6 +540,15 @@ function buildContainerFieldState(state, segments, _key) {
536
540
  meta: resolved.meta
537
541
  };
538
542
  }
543
+ function buildContainerFieldState(state, segments, key, getFormMetaBase) {
544
+ const base = buildContainerFieldStateBase(state, segments);
545
+ return decorateWithDerivedProps(base, state, getFormMetaBase);
546
+ }
547
+ function decorateWithDerivedProps(base, state, getFormMetaBase) {
548
+ const firstError = base.errors[0];
549
+ const showErrors = base.errors.length > 0 && state.shouldShowErrors(base, getFormMetaBase());
550
+ return { ...base, showErrors, firstError };
551
+ }
539
552
  function aggregateErrorsAt(state, prefix) {
540
553
  const formValue = state.form.value;
541
554
  const buckets = /* @__PURE__ */ new Map();
@@ -545,7 +558,7 @@ function aggregateErrorsAt(state, prefix) {
545
558
  const segs = segmentsForPathKey(pathKey);
546
559
  if (segs === null) continue;
547
560
  if (!isPathPrefix(prefix, segs)) continue;
548
- if (segs.length > 0 && !hasAtPath(formValue, segs)) continue;
561
+ if (pathKey === FORM_ERRORS_PATH_KEY) ; else if (segs.length > 0 && !hasAtPath(formValue, segs)) continue;
549
562
  const ordinal = state.ensurePathOrdinal(pathKey);
550
563
  const existing = buckets.get(ordinal);
551
564
  if (existing === void 0) buckets.set(ordinal, [...list]);
@@ -866,6 +879,8 @@ const FIELD_STATE_KEYS = /* @__PURE__ */ new Set([
866
879
  "errors",
867
880
  "validating",
868
881
  "valid",
882
+ "showErrors",
883
+ "firstError",
869
884
  "path",
870
885
  "blank",
871
886
  "label",
@@ -873,8 +888,8 @@ const FIELD_STATE_KEYS = /* @__PURE__ */ new Set([
873
888
  "placeholder",
874
889
  "meta"
875
890
  ]);
876
- function buildFieldStateProxy(state) {
877
- const getFieldStateAt = buildFieldStateAccessor(state);
891
+ function buildFieldStateProxy(state, getFormMetaBase) {
892
+ const getFieldStateAt = buildFieldStateAccessor(state, getFormMetaBase);
878
893
  const snapshotFieldStateAt = (path) => {
879
894
  const view = getFieldStateAt(path).value;
880
895
  const snapshot = {};
@@ -2073,13 +2088,13 @@ function buildFormApi(state, formInstanceId, options = {}) {
2073
2088
  }
2074
2089
  function setFormErrors(errors) {
2075
2090
  if (errors.length === 0) {
2076
- state.userErrors.delete(ROOT_PATH_KEY);
2091
+ state.userErrors.delete(FORM_ERRORS_PATH_KEY);
2077
2092
  return;
2078
2093
  }
2079
2094
  state.userErrors.set(
2080
- ROOT_PATH_KEY,
2095
+ FORM_ERRORS_PATH_KEY,
2081
2096
  errors.map((e) => ({
2082
- path: [],
2097
+ path: [...FORM_ERRORS_PATH],
2083
2098
  message: e.message,
2084
2099
  formKey: state.formKey,
2085
2100
  code: e.code ?? "atta:form-error"
@@ -2087,7 +2102,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2087
2102
  );
2088
2103
  }
2089
2104
  function clearFormErrors() {
2090
- state.userErrors.delete(ROOT_PATH_KEY);
2105
+ state.userErrors.delete(FORM_ERRORS_PATH_KEY);
2091
2106
  }
2092
2107
  const submitting = computed(() => state.submitting.value);
2093
2108
  const submitCount = computed(() => state.submitCount.value);
@@ -2105,7 +2120,20 @@ function buildFormApi(state, formInstanceId, options = {}) {
2105
2120
  const metaErrors = computed(
2106
2121
  () => aggregateErrorsAt(state, [])
2107
2122
  );
2108
- const getRootFieldStateAt = buildFieldStateAccessor(state);
2123
+ const getFormMetaBase = () => {
2124
+ const rootBase = buildContainerFieldStateBase(state, ROOT_PATH);
2125
+ return {
2126
+ ...rootBase,
2127
+ submitting: state.submitting.value,
2128
+ submitCount: state.submitCount.value,
2129
+ submitError: state.submitError.value,
2130
+ canUndo: canUndo.value,
2131
+ canRedo: canRedo.value,
2132
+ historySize: historySize.value,
2133
+ instanceId: formInstanceId
2134
+ };
2135
+ };
2136
+ const getRootFieldStateAt = buildFieldStateAccessor(state, getFormMetaBase);
2109
2137
  const rootFieldState = getRootFieldStateAt([]);
2110
2138
  const formMeta = readonly(
2111
2139
  reactive({
@@ -2141,6 +2169,13 @@ function buildFormApi(state, formInstanceId, options = {}) {
2141
2169
  // keep the explicit form-level computation for the gate.
2142
2170
  valid,
2143
2171
  errors: metaErrors,
2172
+ // `showErrors` / `firstError` flow through the same root
2173
+ // field-state computed as the rest of the FieldState surface,
2174
+ // so `form.meta.showErrors` matches `form.fields().showErrors`
2175
+ // exactly — the predicate runs once at the root and the result
2176
+ // is shared.
2177
+ showErrors: computed(() => rootFieldState.value.showErrors),
2178
+ firstError: computed(() => rootFieldState.value.firstError),
2144
2179
  path: computed(() => rootFieldState.value.path),
2145
2180
  blank: computed(() => rootFieldState.value.blank),
2146
2181
  label: computed(() => rootFieldState.value.label),
@@ -2207,6 +2242,10 @@ function buildFormApi(state, formInstanceId, options = {}) {
2207
2242
  const segments = canonicalizePath(pathInput).segments;
2208
2243
  await persistence.clearPersistedDraft(segments);
2209
2244
  };
2245
+ function touch(pathInput) {
2246
+ const segments = pathInput === void 0 ? ROOT_PATH : canonicalizePath(pathInput).segments;
2247
+ state.touchAtPath(segments);
2248
+ }
2210
2249
  const focusFirstError = (options2) => {
2211
2250
  const target = state.getFirstErrorElement(formInstanceId);
2212
2251
  if (target === null) return false;
@@ -2224,7 +2263,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2224
2263
  return readonlySetSnapshot(state.blankPaths);
2225
2264
  });
2226
2265
  const valuesProxy = buildValuesProxy(state.form);
2227
- const fieldStateProxy = buildFieldStateProxy(state);
2266
+ const fieldStateProxy = buildFieldStateProxy(state, getFormMetaBase);
2228
2267
  return {
2229
2268
  handleSubmit,
2230
2269
  // `values` is the callable readonly Proxy. Each `get` trap reads
@@ -2253,6 +2292,7 @@ function buildFormApi(state, formInstanceId, options = {}) {
2253
2292
  clearPersistedDraft,
2254
2293
  focusFirstError,
2255
2294
  scrollToFirstError,
2295
+ touch,
2256
2296
  undo,
2257
2297
  redo,
2258
2298
  append: fieldArrays.append,
@@ -2266,6 +2306,16 @@ function buildFormApi(state, formInstanceId, options = {}) {
2266
2306
  };
2267
2307
  }
2268
2308
 
2309
+ const defaultShouldShowErrors = (field, formMeta) => formMeta.submitCount > 0 || field.touched === true && field.dirty;
2310
+ const SHOW_ALWAYS = () => true;
2311
+ const SHOW_NEVER = () => false;
2312
+ function resolveShouldShowErrors(config) {
2313
+ if (config === void 0) return defaultShouldShowErrors;
2314
+ if (config === true) return SHOW_ALWAYS;
2315
+ if (config === false) return SHOW_NEVER;
2316
+ return config;
2317
+ }
2318
+
2269
2319
  function isHydratedFieldRecord(value) {
2270
2320
  if (typeof value !== "object" || value === null) return false;
2271
2321
  const r = value;
@@ -2310,6 +2360,9 @@ function createFormStore(options) {
2310
2360
  const resetListeners = /* @__PURE__ */ new Set();
2311
2361
  const persistOptIns = createPersistOptInRegistry();
2312
2362
  const coerceIndex = resolveCoercionIndex(options.coerce);
2363
+ const resolvedShouldShowErrors = resolveShouldShowErrors(
2364
+ options.shouldShowErrors
2365
+ );
2313
2366
  const cleanupHooks = [];
2314
2367
  const modules = /* @__PURE__ */ new Map();
2315
2368
  const completedConstraints = defaultValues === void 0 ? void 0 : mergeStructural(schema, [], defaultValues);
@@ -2739,9 +2792,20 @@ function createFormStore(options) {
2739
2792
  function getValueAtPath(path) {
2740
2793
  return getAtPath(form.value, path);
2741
2794
  }
2795
+ function rerouteFormLevelEntry(err) {
2796
+ if (err.path.length === 0) {
2797
+ return { ...err, path: [...FORM_ERRORS_PATH] };
2798
+ }
2799
+ return err;
2800
+ }
2801
+ function pathKeyForEntry(err) {
2802
+ if (err.path.length === 0) return FORM_ERRORS_PATH_KEY;
2803
+ return canonicalizePath(err.path).key;
2804
+ }
2742
2805
  function appendErrorsTo(map, entries) {
2743
- for (const err of entries) {
2744
- const { key } = canonicalizePath(err.path);
2806
+ for (const raw of entries) {
2807
+ const err = rerouteFormLevelEntry(raw);
2808
+ const key = pathKeyForEntry(err);
2745
2809
  const current = map.get(key);
2746
2810
  if (current === void 0) {
2747
2811
  map.set(key, [err]);
@@ -2771,16 +2835,18 @@ function createFormStore(options) {
2771
2835
  schemaErrors.set(key, [...entries]);
2772
2836
  }
2773
2837
  function applySchemaErrorsForSubtree(path, entries) {
2774
- const { key: parentKey } = canonicalizePath(path);
2838
+ const parentKey = path.length === 0 ? FORM_ERRORS_PATH_KEY : canonicalizePath(path).key;
2775
2839
  const grouped = /* @__PURE__ */ new Map();
2776
- for (const err of entries) {
2777
- const { key } = canonicalizePath(err.path);
2840
+ for (const raw of entries) {
2841
+ const err = rerouteFormLevelEntry(raw);
2842
+ const key = pathKeyForEntry(err);
2778
2843
  const list = grouped.get(key);
2779
2844
  if (list === void 0) grouped.set(key, [err]);
2780
2845
  else list.push(err);
2781
2846
  }
2782
2847
  if (!grouped.has(parentKey)) schemaErrors.delete(parentKey);
2783
2848
  for (const existingKey of [...schemaErrors.keys()]) {
2849
+ if (existingKey === parentKey) continue;
2784
2850
  if (isPathKeyUnder(existingKey, path) && !grouped.has(existingKey)) {
2785
2851
  schemaErrors.delete(existingKey);
2786
2852
  }
@@ -2877,6 +2943,24 @@ function createFormStore(options) {
2877
2943
  const { key } = canonicalizePath(path);
2878
2944
  touchFieldRecord(key, path, { touched: true });
2879
2945
  }
2946
+ function touchAtPath(segments) {
2947
+ const formValue = form.value;
2948
+ let touchedAny = false;
2949
+ for (const [, entry] of originals) {
2950
+ if (!isPathPrefix(segments, entry.segments)) continue;
2951
+ if (!hasAtPath(formValue, entry.segments)) continue;
2952
+ touchedAny = true;
2953
+ const leafKey = canonicalizePath(entry.segments).key;
2954
+ const current = fields.get(leafKey);
2955
+ if (current?.touched === true) continue;
2956
+ touchFieldRecord(leafKey, entry.segments, { touched: true });
2957
+ }
2958
+ if (!touchedAny && __DEV__) {
2959
+ console.warn(
2960
+ `[attaform] form.touch(): no fields resolved at path ${JSON.stringify(segments)}. Check the path matches an existing field or container.`
2961
+ );
2962
+ }
2963
+ }
2880
2964
  function reset(nextDefaultValues) {
2881
2965
  const next = schema.getDefaultValues({
2882
2966
  useDefaultSchemaValues: true,
@@ -3053,6 +3137,7 @@ function createFormStore(options) {
3053
3137
  originals,
3054
3138
  schema,
3055
3139
  ssr,
3140
+ shouldShowErrors: resolvedShouldShowErrors,
3056
3141
  submitting,
3057
3142
  activeSubmissions,
3058
3143
  submitCount,
@@ -3079,6 +3164,7 @@ function createFormStore(options) {
3079
3164
  deregisterElement,
3080
3165
  markFocused,
3081
3166
  markTouched,
3167
+ touchAtPath,
3082
3168
  markConnectedOptimistically,
3083
3169
  isPristineAtPath,
3084
3170
  getFieldRecord,
@@ -3271,6 +3357,7 @@ function mergeWithDefaults(defaults, configuration) {
3271
3357
  const coerce = configuration.coerce ?? defaults.coerce;
3272
3358
  const validateOn = configuration.validateOn ?? defaults.validateOn;
3273
3359
  const debounceMs = configuration.debounceMs ?? defaults.debounceMs;
3360
+ const shouldShowErrors = configuration.shouldShowErrors ?? defaults.shouldShowErrors;
3274
3361
  return {
3275
3362
  ...configuration,
3276
3363
  ...strict === void 0 ? {} : { strict },
@@ -3279,7 +3366,8 @@ function mergeWithDefaults(defaults, configuration) {
3279
3366
  ...rememberVariants === void 0 ? {} : { rememberVariants },
3280
3367
  ...coerce === void 0 ? {} : { coerce },
3281
3368
  ...validateOn === void 0 ? {} : { validateOn },
3282
- ...debounceMs === void 0 ? {} : { debounceMs }
3369
+ ...debounceMs === void 0 ? {} : { debounceMs },
3370
+ ...shouldShowErrors === void 0 ? {} : { shouldShowErrors }
3283
3371
  };
3284
3372
  }
3285
3373
  const HISTORY_MODULE_KEY = "history";
@@ -3302,6 +3390,7 @@ function buildFreshState(key, schema, configuration, registry) {
3302
3390
  ssr: registry.ssr,
3303
3391
  ...configuration.rememberVariants !== void 0 ? { rememberVariants: configuration.rememberVariants } : {},
3304
3392
  ...configuration.coerce !== void 0 ? { coerce: configuration.coerce } : {},
3393
+ ...configuration.shouldShowErrors !== void 0 ? { shouldShowErrors: configuration.shouldShowErrors } : {},
3305
3394
  ...initialBlankPaths !== void 0 ? { initialBlankPaths } : {}
3306
3395
  };
3307
3396
  const state = createFormStore(createOptions);
@@ -3682,5 +3771,5 @@ function warnIfAmbientProviderHadDuplicates() {
3682
3771
  }
3683
3772
  }
3684
3773
 
3685
- export { AttaformErrorCode as A, isUnset as a, defineCoercion as b, useAbstractForm as c, defaultCoercionRules as d, setAtPath as e, isPlainRecord as f, getAtPath as g, humanize as h, injectForm as i, slimKindOf as s, unset as u };
3686
- //# sourceMappingURL=attaform.DILbdvfo.mjs.map
3774
+ export { AttaformErrorCode as A, isUnset as a, defaultShouldShowErrors as b, defineCoercion as c, defaultCoercionRules as d, useAbstractForm as e, setAtPath as f, getAtPath as g, humanize as h, injectForm as i, isPlainRecord as j, slimKindOf as s, unset as u };
3775
+ //# sourceMappingURL=attaform.DyV1O4tI.mjs.map