attaform 0.18.1 → 0.19.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 (71) hide show
  1. package/README.md +3 -0
  2. package/dist/chunks/devtools.cjs +1 -1
  3. package/dist/chunks/devtools.mjs +1 -1
  4. package/dist/chunks/indexeddb.cjs +1 -1
  5. package/dist/chunks/indexeddb.mjs +1 -1
  6. package/dist/chunks/local-storage.cjs +1 -1
  7. package/dist/chunks/local-storage.mjs +1 -1
  8. package/dist/chunks/session-storage.cjs +1 -1
  9. package/dist/chunks/session-storage.mjs +1 -1
  10. package/dist/index.cjs +4 -4
  11. package/dist/index.d.cts +68 -75
  12. package/dist/index.d.mts +68 -75
  13. package/dist/index.d.ts +68 -75
  14. package/dist/index.mjs +5 -5
  15. package/dist/nuxt.d.cts +1 -1
  16. package/dist/nuxt.d.mts +1 -1
  17. package/dist/nuxt.d.ts +1 -1
  18. package/dist/runtime/plugins/attaform.cjs +2 -2
  19. package/dist/runtime/plugins/attaform.mjs +2 -2
  20. package/dist/shared/{attaform.DsC3rZHG.mjs → attaform.BTi-PsHr.mjs} +544 -134
  21. package/dist/shared/attaform.BTi-PsHr.mjs.map +1 -0
  22. package/dist/shared/{attaform.iTqxvl-P.d.mts → attaform.BTpuvGec.d.ts} +46 -13
  23. package/dist/shared/{attaform.BqK_L4gK.cjs → attaform.BqEfHpVB.cjs} +119 -1
  24. package/dist/shared/attaform.BqEfHpVB.cjs.map +1 -0
  25. package/dist/shared/{attaform.DK9aj0N8.d.ts → attaform.BtBmfLQN.d.mts} +46 -13
  26. package/dist/shared/{attaform.Dj9mwbaV.d.mts → attaform.C0uGZQ4M.d.cts} +365 -86
  27. package/dist/shared/{attaform.Dj9mwbaV.d.ts → attaform.C0uGZQ4M.d.mts} +365 -86
  28. package/dist/shared/{attaform.Dj9mwbaV.d.cts → attaform.C0uGZQ4M.d.ts} +365 -86
  29. package/dist/shared/{attaform.II89Pcf4.cjs → attaform.C1msmO2v.cjs} +544 -134
  30. package/dist/shared/attaform.C1msmO2v.cjs.map +1 -0
  31. package/dist/shared/{attaform.tts_OM7j.d.cts → attaform.CBjmobqk.d.cts} +1 -1
  32. package/dist/shared/{attaform.2b7M2mww.d.mts → attaform.CJ-e9gYI.d.ts} +1 -1
  33. package/dist/shared/{attaform.tsNFcEW7.d.ts → attaform.CRNA0vrd.d.mts} +1 -1
  34. package/dist/shared/{attaform.BDdFdjeX.mjs → attaform.Cghpuav8.mjs} +3 -3
  35. package/dist/shared/{attaform.BDdFdjeX.mjs.map → attaform.Cghpuav8.mjs.map} +1 -1
  36. package/dist/shared/{attaform.CtNUB9nf.mjs → attaform.CiMqJHDm.mjs} +3 -3
  37. package/dist/shared/{attaform.CtNUB9nf.mjs.map → attaform.CiMqJHDm.mjs.map} +1 -1
  38. package/dist/shared/{attaform.5UhpSVFI.cjs → attaform.CoxJ8Qm8.cjs} +2 -2
  39. package/dist/shared/{attaform.5UhpSVFI.cjs.map → attaform.CoxJ8Qm8.cjs.map} +1 -1
  40. package/dist/shared/{attaform.Xhg0AYNa.mjs → attaform.CrpjyXdO.mjs} +120 -2
  41. package/dist/shared/attaform.CrpjyXdO.mjs.map +1 -0
  42. package/dist/shared/{attaform.DF8wo-ry.d.ts → attaform.D4I63aBV.d.ts} +1 -1
  43. package/dist/shared/{attaform.DVLB6CAn.d.mts → attaform.DXYHL99q.d.mts} +1 -1
  44. package/dist/shared/{attaform.Dlk1jMuv.cjs → attaform.JBx8cfMA.cjs} +3 -3
  45. package/dist/shared/{attaform.Dlk1jMuv.cjs.map → attaform.JBx8cfMA.cjs.map} +1 -1
  46. package/dist/shared/{attaform.DUHru0OF.cjs → attaform.OznWyOPy.cjs} +3 -3
  47. package/dist/shared/{attaform.DUHru0OF.cjs.map → attaform.OznWyOPy.cjs.map} +1 -1
  48. package/dist/shared/{attaform.M33WKVV4.d.cts → attaform.QvygsFGh.d.cts} +1 -1
  49. package/dist/shared/{attaform.Xt0A3QUd.mjs → attaform.a3uBo-gw.mjs} +3 -3
  50. package/dist/shared/{attaform.Xt0A3QUd.mjs.map → attaform.a3uBo-gw.mjs.map} +1 -1
  51. package/dist/shared/{attaform.DoSuaKMd.d.cts → attaform.ePUcKxId.d.cts} +46 -13
  52. package/dist/zod-v3.cjs +3 -3
  53. package/dist/zod-v3.d.cts +4 -4
  54. package/dist/zod-v3.d.mts +4 -4
  55. package/dist/zod-v3.d.ts +4 -4
  56. package/dist/zod-v3.mjs +3 -3
  57. package/dist/zod-v4.cjs +3 -3
  58. package/dist/zod-v4.d.cts +4 -4
  59. package/dist/zod-v4.d.mts +4 -4
  60. package/dist/zod-v4.d.ts +4 -4
  61. package/dist/zod-v4.mjs +3 -3
  62. package/dist/zod.cjs +4 -4
  63. package/dist/zod.d.cts +6 -6
  64. package/dist/zod.d.mts +6 -6
  65. package/dist/zod.d.ts +6 -6
  66. package/dist/zod.mjs +5 -5
  67. package/package.json +5 -5
  68. package/dist/shared/attaform.BqK_L4gK.cjs.map +0 -1
  69. package/dist/shared/attaform.DsC3rZHG.mjs.map +0 -1
  70. package/dist/shared/attaform.II89Pcf4.cjs.map +0 -1
  71. package/dist/shared/attaform.Xhg0AYNa.mjs.map +0 -1
package/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
  [![npm downloads][npm-downloads-src]][npm-downloads-href]
5
5
  [![License][license-src]][license-href]
6
6
  [![Node.js Test Suite](https://github.com/attaform/attaform/actions/workflows/matrix.yml/badge.svg)](https://github.com/attaform/attaform/actions/workflows/matrix.yml)
7
+ [![OpenSSF Scorecard][scorecard-src]][scorecard-href]
7
8
  [![Nuxt][nuxt-src]][nuxt-href]
8
9
 
9
10
  A type-safe, schema-driven form library for Vue 3 and Nuxt with first-class Zod support.
@@ -192,5 +193,7 @@ MIT; see [LICENSE](./LICENSE).
192
193
  [npm-downloads-href]: https://npm.chart.dev/attaform
193
194
  [license-src]: https://img.shields.io/npm/l/attaform.svg?style=flat&colorA=020420&colorB=00DC82
194
195
  [license-href]: https://npmjs.com/package/attaform
196
+ [scorecard-src]: https://img.shields.io/ossf-scorecard/github.com/attaform/Attaform?label=OpenSSF%20Scorecard&style=flat&colorA=020420&colorB=00DC82
197
+ [scorecard-href]: https://securityscorecards.dev/viewer/?uri=github.com/attaform/Attaform
195
198
  [nuxt-src]: https://img.shields.io/badge/Nuxt-020420?logo=nuxt.js
196
199
  [nuxt-href]: https://nuxt.com
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const paths = require('../shared/attaform.BqK_L4gK.cjs');
3
+ const paths = require('../shared/attaform.BqEfHpVB.cjs');
4
4
 
5
5
  const INSPECTOR_ID = "attaform";
6
6
  const TIMELINE_LAYER_ID = "attaform:events";
@@ -1,4 +1,4 @@
1
- import { a as canonicalizePath } from '../shared/attaform.Xhg0AYNa.mjs';
1
+ import { i as canonicalizePath } from '../shared/attaform.CrpjyXdO.mjs';
2
2
 
3
3
  const INSPECTOR_ID = "attaform";
4
4
  const TIMELINE_LAYER_ID = "attaform:events";
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const paths = require('../shared/attaform.BqK_L4gK.cjs');
3
+ const paths = require('../shared/attaform.BqEfHpVB.cjs');
4
4
 
5
5
  const DB_NAME = "attaform";
6
6
  const STORE_NAME = "kv";
@@ -1,4 +1,4 @@
1
- import { _ as __DEV__ } from '../shared/attaform.Xhg0AYNa.mjs';
1
+ import { _ as __DEV__ } from '../shared/attaform.CrpjyXdO.mjs';
2
2
 
3
3
  const DB_NAME = "attaform";
4
4
  const STORE_NAME = "kv";
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const paths = require('../shared/attaform.BqK_L4gK.cjs');
3
+ const paths = require('../shared/attaform.BqEfHpVB.cjs');
4
4
 
5
5
  function createLocalStorageAdapter() {
6
6
  const available = typeof localStorage !== "undefined";
@@ -1,4 +1,4 @@
1
- import { _ as __DEV__ } from '../shared/attaform.Xhg0AYNa.mjs';
1
+ import { _ as __DEV__ } from '../shared/attaform.CrpjyXdO.mjs';
2
2
 
3
3
  function createLocalStorageAdapter() {
4
4
  const available = typeof localStorage !== "undefined";
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const paths = require('../shared/attaform.BqK_L4gK.cjs');
3
+ const paths = require('../shared/attaform.BqEfHpVB.cjs');
4
4
 
5
5
  function createSessionStorageAdapter() {
6
6
  const available = typeof sessionStorage !== "undefined";
@@ -1,4 +1,4 @@
1
- import { _ as __DEV__ } from '../shared/attaform.Xhg0AYNa.mjs';
1
+ import { _ as __DEV__ } from '../shared/attaform.CrpjyXdO.mjs';
2
2
 
3
3
  function createSessionStorageAdapter() {
4
4
  const available = typeof sessionStorage !== "undefined";
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- const paths = require('./shared/attaform.BqK_L4gK.cjs');
4
- const devtoolsShared = require('./shared/attaform.5UhpSVFI.cjs');
5
- const injectWizard = require('./shared/attaform.II89Pcf4.cjs');
3
+ const paths = require('./shared/attaform.BqEfHpVB.cjs');
4
+ const devtoolsShared = require('./shared/attaform.CoxJ8Qm8.cjs');
5
+ const injectWizard = require('./shared/attaform.C1msmO2v.cjs');
6
6
 
7
7
  function escapeForInlineScript(json) {
8
8
  return json.replace(/[<>&\u2028\u2029]/g, (char) => {
@@ -185,7 +185,7 @@ exports.redactSensitiveLeaves = devtoolsShared.redactSensitiveLeaves;
185
185
  exports.renderAttaformState = devtoolsShared.renderAttaformState;
186
186
  exports.AttaformErrorCode = injectWizard.AttaformErrorCode;
187
187
  exports.defaultCoercionRules = injectWizard.defaultCoercionRules;
188
- exports.defaultShouldShowErrors = injectWizard.defaultShouldShowErrors;
188
+ exports.defaultDisplayState = injectWizard.defaultDisplayState;
189
189
  exports.defineCoercion = injectWizard.defineCoercion;
190
190
  exports.injectForm = injectWizard.injectForm;
191
191
  exports.injectWizard = injectWizard.injectWizard;
package/dist/index.d.cts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { Plugin, App } from 'vue';
2
- import { S as SSRDetectOptions, a as SerializedFormData, A as AttaformRegistry } from './shared/attaform.DoSuaKMd.cjs';
3
- export { b as AggregateError, c as AnyForm, d as AttaformErrorCode, C as CompiledStep, F as FormStatus, I as InjectWizardInput, L as LazyMarker, e as StepSlot, U as UseRegisterReturn, f as UseWizardReturnType, W as WizardCtx, g as WizardCtxForm, h as WizardOnError, i as WizardOnSubmit, j as WizardOptions, k as WizardPersistFn, l as WizardRestoreFn, m as WizardRestoreState, n as WizardStatusesProxy, o as WizardSubmitContext, p as createRegistry, q as defaultCoercionRules, r as defineCoercion, s as getRegistryFromApp, t as injectForm, u as injectWizard, v as kAttaformRegistry, w as lazy, x as useRegister, y as useRegistry, z as useWizard } from './shared/attaform.DoSuaKMd.cjs';
4
- import { A as AttaformDefaults, F as FormKey, G as GenericForm, U as UseFormConfiguration, a as AbstractSchema, D as DefaultValuesInput, b as UseFormReturnType, R as RegisterValue, c as RegisterModelDynamicCustomDirective, S as Segment, d as ShouldShowErrors, V as ValidationError, e as ApiErrorEnvelope, f as ApiErrorDetails } from './shared/attaform.Dj9mwbaV.cjs';
5
- export { g as ApiErrorEntry, h as ArrayItem, i as ArrayPath, C as CoercionEntry, j as CoercionRegistry, k as CoercionResult, l as CustomDirectiveRegisterAssignerFn, m as DeepPartial, n as DefaultValuesResponse, o as DefaultValuesShape, E as ErrorsProxyShape, p as FieldMetaPayload, q as FieldState, r as FieldStateMap, s as FieldStateMapEntry, t as FlatPath, u as FormErrorRecord, v as FormErrorsSurface, w as FormMeta, x as FormStorage, y as FormStorageKind, H as HandleSubmit, z as HistoryConfig, I as IsTuple, B as IsUnion, J as JoinSegments, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, O as NestedType, P as OnError, Q as OnInvalidSubmitPolicy, T as OnSubmit, W as PartialFlatPath, X as Path, Y as PathKey, Z as PendingValidationStatus, _ as PersistConfig, $ as PersistConfigOptions, a0 as PersistIncludeMode, a1 as Primitive, a2 as ROOT_PATH, a3 as ROOT_PATH_KEY, a4 as ReactiveValidationStatus, a5 as RegisterDirective, a6 as RegisterFlatPath, a7 as RegisterOptions, a8 as RegisterSelectModifier, a9 as RegisterTextModifier, aa as RegisterTransform, ab as SetValueCallback, ac as SetValuePayload, ad as SettledValidationStatus, ae as ShouldShowErrorsConfig, af as SlimPrimitiveKind, ag as SlimRuntimeOf, ah as SubmitHandler, ai as Unset, aj as ValidateOn, ak as ValidateOnConfig, al as ValidationResponse, am as ValidationResponseWithoutValue, an as ValueOfUnion, ao as WriteMeta, ap as WriteShape, aq as canonicalizePath, ar as isPathPrefix, as as isUnset, at as parseDottedPath, au as unset } from './shared/attaform.Dj9mwbaV.cjs';
2
+ import { S as SSRDetectOptions, d as SerializedFormData, c as AttaformRegistry } from './shared/attaform.ePUcKxId.cjs';
3
+ export { A as AggregateError, a as AnyForm, b as AttaformErrorCode, C as CompiledStep, F as FormStatus, I as InjectWizardInput, L as LazyMarker, e as StepSlot, U as UseRegisterReturn, f as UseWizardReturnType, W as WizardCtx, g as WizardCtxForm, h as WizardOnError, i as WizardOnSubmit, j as WizardOptions, k as WizardPersistFn, l as WizardRestoreFn, m as WizardRestoreState, n as WizardStatusesProxy, o as WizardSubmitContext, p as createRegistry, q as defaultCoercionRules, r as defineCoercion, s as getRegistryFromApp, t as injectForm, u as injectWizard, v as kAttaformRegistry, w as lazy, x as useRegister, y as useRegistry, z as useWizard } from './shared/attaform.ePUcKxId.cjs';
4
+ import { f as AttaformDefaults, t as FormKey, G as GenericForm, ai as UseFormConfiguration, A as AbstractSchema, j as DefaultValuesInput, aj as UseFormReturnType, a8 as RegisterValue, a3 as RegisterModelDynamicCustomDirective, aa as Segment, x as GetDisplayState, am as ValidationError, c as ApiErrorEnvelope, a as ApiErrorDetails } from './shared/attaform.C0uGZQ4M.cjs';
5
+ export { b as ApiErrorEntry, d as ArrayItem, e as ArrayPath, C as CoercionEntry, g as CoercionRegistry, h as CoercionResult, i as CustomDirectiveRegisterAssignerFn, D as DeepPartial, k as DefaultValuesResponse, l as DefaultValuesShape, m as DisplayState, E as ErrorsProxyShape, F as FieldMetaPayload, n as FieldState, o as FieldStateMap, p as FieldStateMapEntry, q as FlatPath, r as FormErrorRecord, s as FormErrorsSurface, u as FormMeta, v as FormStorage, w as FormStorageKind, H as HandleSubmit, y as HistoryConfig, I as IsTuple, z as IsUnion, J as JoinSegments, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, B as NestedType, O as OnError, P as OnInvalidSubmitPolicy, Q as OnSubmit, R as PartialFlatPath, S as Path, T as PathKey, U as PendingValidationStatus, V as PersistConfig, W as PersistConfigOptions, X as PersistIncludeMode, Z as Primitive, _ as ROOT_PATH, $ as ROOT_PATH_KEY, a0 as ReactiveValidationStatus, a1 as RegisterDirective, a2 as RegisterFlatPath, a4 as RegisterOptions, a5 as RegisterSelectModifier, a6 as RegisterTextModifier, a7 as RegisterTransform, ab as SetValueCallback, ac as SetValuePayload, ad as SettledValidationStatus, ae as SlimPrimitiveKind, af as SlimRuntimeOf, ag as SubmitHandler, ah as Unset, ak as ValidateOn, al as ValidateOnConfig, an as ValidationResponse, ao as ValidationResponseWithoutValue, ap as ValueOfUnion, aq as WriteMeta, ar as WriteShape, as as canonicalizePath, at as isPathPrefix, au as isUnset, av as parseDottedPath, aw as unset } from './shared/attaform.C0uGZQ4M.cjs';
6
6
  export { A as AnonPersistError, a as AttaformError, I as InvalidPathError, b as InvalidUseFormConfigError, O as OutsideSetupError, R as RegistryNotInstalledError, c as ReservedFormKeyError, S as SensitivePersistFieldError, d as SubmitErrorHandlerError } from './shared/attaform.B7rzpK1U.cjs';
7
7
 
8
8
  /**
@@ -287,86 +287,79 @@ declare global {
287
287
  }
288
288
 
289
289
  /**
290
- * Library-default heuristic for `shouldShowErrors`. Drives
291
- * `field.showErrors` and `form.meta.showErrors` whenever the consumer
292
- * has not configured an override at either the plugin or per-form
293
- * level.
294
- *
295
- * Four clauses: the first two are hard gates; clause 3 is an
296
- * aggressive override after the first submit attempt; clause 4 is
297
- * the pre-submit timing gate.
298
- *
299
- * 1. **Own-path filter.** The field must have at least one error whose
300
- * path equals the field's own path. Leaves always satisfy this when
301
- * they have errors. Containers (intermediate or root) only satisfy
302
- * it for errors that point directly at them, so a UI rendering
303
- * `field.showErrors` at a container never duplicates errors that a
304
- * more-specific descendant will already render. Aggregate banners
305
- * that want "any error anywhere passed the gate" should bind to
306
- * `form.meta.errorCount > 0` (paired with whatever timing signal
307
- * fits), not to `form.meta.showErrors`.
308
- *
309
- * 2. **Not currently validating.** While the field is mid-revalidation
310
- * (`field.validating === true`) the verdict in `field.errors` is
311
- * stale by definition. The error itself stays in the store under
312
- * the stale-while-revalidate contract so the surface doesn't
313
- * flicker to empty, but the UX gate hides it: the application is
314
- * actively re-checking, so the message would mis-narrate the
315
- * state of the world. Containers roll up `validating` as a
316
- * disjunction, so any descendant under revalidation hides the
317
- * container's `showErrors` too. The error returns the moment the
318
- * new verdict lands and `validating` flips back to false.
319
- *
320
- * 3. **Post-submit override.** Once `formMeta.submissionAttempts > 0`
321
- * the heuristic surfaces every own-path error unconditionally on
322
- * the field axis (subject only to the two gates above). The
323
- * counter covers two distinct gestures:
324
- *
325
- * - `form.handleSubmit` directly, or `wizard.handleSubmit` at an
326
- * intermediate step. The user asked this specific form to
327
- * commit; transient mid-edit hiding is no longer appropriate.
328
- *
329
- * - `wizard.handleSubmit` at the final step. The wizard validates
330
- * every form in parallel and bumps each one's `submissionAttempts`,
331
- * so a `Finish`-button submission reveals errors across the
332
- * whole multistep flow at once. That's the review-surface UX:
333
- * by the time the user tries to commit the wizard, every
334
- * blocking error lights up regardless of which step it lives on.
335
- *
336
- * The arm deliberately covers focused, pristine, and untouched
337
- * fields, so the next paint after the user tried to progress lights
338
- * up every problem the validator found.
339
- *
340
- * 4. **Pre-submit timing gate.** Before the first submit attempt,
341
- * show once the user has touched the field (sticky-true after the
342
- * first blur) AND is not currently focused on it. The not-focused
343
- * half hides transient errors while the user is actively editing
344
- * the field; they reappear when the user blurs (or focuses a
345
- * sibling). This deliberately includes blur-without-typing on a
346
- * required field (touched flips on blur regardless of `dirty`), so
347
- * a user who visits an empty required field and moves on sees the
348
- * error.
349
- *
350
- * The framework already gates on `errors.length > 0` before invoking
351
- * the predicate, so the body only decides *when* to surface existing
352
- * errors, not whether errors exist.
290
+ * Library-default `getDisplayState` heuristic. Resolves every path's
291
+ * `field.displayState` and thus `field.show*` and the `form.meta`
292
+ * rollups — whenever the consumer has not configured an override at the
293
+ * per-form or plugin level.
294
+ *
295
+ * One timing gate, then precedence:
296
+ *
297
+ * 1. **Timing gate.** `gateOpen` once the form has been submitted
298
+ * (`submissionAttempts > 0`) OR the field has been edited and then
299
+ * left (`blurredAfterInteraction === true`). Before the gate opens
300
+ * the verdict is `'idle'` regardless of errors. This is the "reward
301
+ * early, punish late" rule:
302
+ * - A clean tab-through never engages. `blurredAfterInteraction`
303
+ * only flips on a blur that follows an edit, so visiting a field
304
+ * and moving on without editing it stays quiet until a submit
305
+ * forces the issue, even if the field was tabbed through before.
306
+ * - The first pass stays quiet. Editing alone (`interacted`) does
307
+ * not open the gate; the error reveals only once the user
308
+ * finishes that pass and leaves the field, never mid-entry.
309
+ * - Recovery is live. The bit is sticky and carries no not-focused
310
+ * term, so once a field has been revealed it stays open through a
311
+ * re-focus: a shown error clears (or greens) the instant the
312
+ * value becomes valid, without forcing another blur.
313
+ *
314
+ * The submit arm covers `form.handleSubmit` directly and
315
+ * `wizard.handleSubmit` (which bumps `submissionAttempts` on the
316
+ * active form at intermediate steps and on every form at the final
317
+ * step, lighting up the whole flow at once).
318
+ *
319
+ * 2. **Pending.** With the gate open, a per-field run in flight
320
+ * (`validating === true`) wins: the verdict in `errors` is stale by
321
+ * definition, so surface `'pending'` (a spinner) rather than a
322
+ * possibly-wrong error or success. Containers roll `validating` up as
323
+ * a disjunction, so any descendant under revalidation reads
324
+ * `'pending'` at the container too.
325
+ *
326
+ * 3. **Error.** An own-path error (one whose path equals the field's own
327
+ * path) resolves to `'error'`. The own-path filter means a container
328
+ * never duplicates an error a more-specific descendant already
329
+ * renders; aggregate banners bind to `form.meta.errorCount` instead.
330
+ *
331
+ * 4. **Success.** No error, `valid === true`, and the green check is
332
+ * earned: the field is non-blank and `dirty` (its value diverges from
333
+ * the hydration original). Gating success on `dirty && !blank` keeps
334
+ * the check meaningful an empty field that happens to pass, a
335
+ * pre-filled field merely tabbed through, and the post-submit flood of
336
+ * every valid field all stay `'idle'` rather than greening for free.
337
+ * `valid` already gates async schemas on the form-wide first
338
+ * validation pass, so success never fires before the first verdict
339
+ * lands.
340
+ *
341
+ * 5. **Idle.** Anything else gate open but not validating, no own-path
342
+ * error, and either not yet `valid` or valid-but-unearned (blank or
343
+ * unchanged) stays `'idle'`.
353
344
  *
354
345
  * Public re-export so adopters can compose with this without
355
- * copy-pasting the rule body. A layered predicate that adds a special
356
- * case but otherwise defers to the library default picks up future
357
- * heuristic refinements automatically:
346
+ * copy-pasting the rule body a layered predicate that special-cases a
347
+ * subtree but otherwise defers picks up future refinements
348
+ * automatically:
358
349
  *
359
350
  * ```ts
360
- * import { defaultShouldShowErrors } from 'attaform'
351
+ * import { defaultDisplayState } from 'attaform'
361
352
  *
362
353
  * useForm({
363
354
  * schema,
364
- * shouldShowErrors: (field, formMeta) =>
365
- * field.path[0] === 'urgent' || defaultShouldShowErrors(field, formMeta),
355
+ * getDisplayState: (field, formMeta) => {
356
+ * const state = defaultDisplayState(field, formMeta)
357
+ * return field.path[0] === 'username' && state === 'success' ? 'idle' : state
358
+ * },
366
359
  * })
367
360
  * ```
368
361
  */
369
- declare const defaultShouldShowErrors: ShouldShowErrors;
362
+ declare const defaultDisplayState: GetDisplayState;
370
363
 
371
364
  /**
372
365
  * The library's built-in conservative set of identifier name stems that
@@ -529,5 +522,5 @@ declare const PARSE_API_ERRORS_DEFAULTS: {
529
522
  */
530
523
  declare function parseApiErrors(payload: ApiErrorEnvelope | ApiErrorDetails | null | undefined | unknown, options: ParseApiErrorsOptions): ParseApiErrorsResult;
531
524
 
532
- export { AbstractSchema, ApiErrorDetails, ApiErrorEnvelope, AttaformDefaults, AttaformRegistry, DEFAULT_SENSITIVE_NAMES, DEVTOOLS_WINDOW_KEY, DefaultValuesInput, FormKey, GenericForm, PARSE_API_ERRORS_DEFAULTS, REDACTED, RegisterValue, Segment, SerializedFormData, ShouldShowErrors, UseFormConfiguration, UseFormReturnType, ValidationError, assignKey, createAttaform, defaultShouldShowErrors, escapeForInlineScript, hydrateAttaformState, isRegisterValue, parseApiErrors, redactSensitiveLeaves, renderAttaformState, useAbstractForm as useForm, vRegister };
525
+ export { AbstractSchema, ApiErrorDetails, ApiErrorEnvelope, AttaformDefaults, AttaformRegistry, DEFAULT_SENSITIVE_NAMES, DEVTOOLS_WINDOW_KEY, DefaultValuesInput, FormKey, GenericForm, GetDisplayState, PARSE_API_ERRORS_DEFAULTS, REDACTED, RegisterValue, Segment, SerializedFormData, UseFormConfiguration, UseFormReturnType, ValidationError, assignKey, createAttaform, defaultDisplayState, escapeForInlineScript, hydrateAttaformState, isRegisterValue, parseApiErrors, redactSensitiveLeaves, renderAttaformState, useAbstractForm as useForm, vRegister };
533
526
  export type { AttaformDevtoolsBridge, AttaformPluginOptions, ParseApiErrorsOptions, ParseApiErrorsResult, SerializedAttaformState };
package/dist/index.d.mts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { Plugin, App } from 'vue';
2
- import { S as SSRDetectOptions, a as SerializedFormData, A as AttaformRegistry } from './shared/attaform.iTqxvl-P.mjs';
3
- export { b as AggregateError, c as AnyForm, d as AttaformErrorCode, C as CompiledStep, F as FormStatus, I as InjectWizardInput, L as LazyMarker, e as StepSlot, U as UseRegisterReturn, f as UseWizardReturnType, W as WizardCtx, g as WizardCtxForm, h as WizardOnError, i as WizardOnSubmit, j as WizardOptions, k as WizardPersistFn, l as WizardRestoreFn, m as WizardRestoreState, n as WizardStatusesProxy, o as WizardSubmitContext, p as createRegistry, q as defaultCoercionRules, r as defineCoercion, s as getRegistryFromApp, t as injectForm, u as injectWizard, v as kAttaformRegistry, w as lazy, x as useRegister, y as useRegistry, z as useWizard } from './shared/attaform.iTqxvl-P.mjs';
4
- import { A as AttaformDefaults, F as FormKey, G as GenericForm, U as UseFormConfiguration, a as AbstractSchema, D as DefaultValuesInput, b as UseFormReturnType, R as RegisterValue, c as RegisterModelDynamicCustomDirective, S as Segment, d as ShouldShowErrors, V as ValidationError, e as ApiErrorEnvelope, f as ApiErrorDetails } from './shared/attaform.Dj9mwbaV.mjs';
5
- export { g as ApiErrorEntry, h as ArrayItem, i as ArrayPath, C as CoercionEntry, j as CoercionRegistry, k as CoercionResult, l as CustomDirectiveRegisterAssignerFn, m as DeepPartial, n as DefaultValuesResponse, o as DefaultValuesShape, E as ErrorsProxyShape, p as FieldMetaPayload, q as FieldState, r as FieldStateMap, s as FieldStateMapEntry, t as FlatPath, u as FormErrorRecord, v as FormErrorsSurface, w as FormMeta, x as FormStorage, y as FormStorageKind, H as HandleSubmit, z as HistoryConfig, I as IsTuple, B as IsUnion, J as JoinSegments, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, O as NestedType, P as OnError, Q as OnInvalidSubmitPolicy, T as OnSubmit, W as PartialFlatPath, X as Path, Y as PathKey, Z as PendingValidationStatus, _ as PersistConfig, $ as PersistConfigOptions, a0 as PersistIncludeMode, a1 as Primitive, a2 as ROOT_PATH, a3 as ROOT_PATH_KEY, a4 as ReactiveValidationStatus, a5 as RegisterDirective, a6 as RegisterFlatPath, a7 as RegisterOptions, a8 as RegisterSelectModifier, a9 as RegisterTextModifier, aa as RegisterTransform, ab as SetValueCallback, ac as SetValuePayload, ad as SettledValidationStatus, ae as ShouldShowErrorsConfig, af as SlimPrimitiveKind, ag as SlimRuntimeOf, ah as SubmitHandler, ai as Unset, aj as ValidateOn, ak as ValidateOnConfig, al as ValidationResponse, am as ValidationResponseWithoutValue, an as ValueOfUnion, ao as WriteMeta, ap as WriteShape, aq as canonicalizePath, ar as isPathPrefix, as as isUnset, at as parseDottedPath, au as unset } from './shared/attaform.Dj9mwbaV.mjs';
2
+ import { S as SSRDetectOptions, d as SerializedFormData, c as AttaformRegistry } from './shared/attaform.BtBmfLQN.mjs';
3
+ export { A as AggregateError, a as AnyForm, b as AttaformErrorCode, C as CompiledStep, F as FormStatus, I as InjectWizardInput, L as LazyMarker, e as StepSlot, U as UseRegisterReturn, f as UseWizardReturnType, W as WizardCtx, g as WizardCtxForm, h as WizardOnError, i as WizardOnSubmit, j as WizardOptions, k as WizardPersistFn, l as WizardRestoreFn, m as WizardRestoreState, n as WizardStatusesProxy, o as WizardSubmitContext, p as createRegistry, q as defaultCoercionRules, r as defineCoercion, s as getRegistryFromApp, t as injectForm, u as injectWizard, v as kAttaformRegistry, w as lazy, x as useRegister, y as useRegistry, z as useWizard } from './shared/attaform.BtBmfLQN.mjs';
4
+ import { f as AttaformDefaults, t as FormKey, G as GenericForm, ai as UseFormConfiguration, A as AbstractSchema, j as DefaultValuesInput, aj as UseFormReturnType, a8 as RegisterValue, a3 as RegisterModelDynamicCustomDirective, aa as Segment, x as GetDisplayState, am as ValidationError, c as ApiErrorEnvelope, a as ApiErrorDetails } from './shared/attaform.C0uGZQ4M.mjs';
5
+ export { b as ApiErrorEntry, d as ArrayItem, e as ArrayPath, C as CoercionEntry, g as CoercionRegistry, h as CoercionResult, i as CustomDirectiveRegisterAssignerFn, D as DeepPartial, k as DefaultValuesResponse, l as DefaultValuesShape, m as DisplayState, E as ErrorsProxyShape, F as FieldMetaPayload, n as FieldState, o as FieldStateMap, p as FieldStateMapEntry, q as FlatPath, r as FormErrorRecord, s as FormErrorsSurface, u as FormMeta, v as FormStorage, w as FormStorageKind, H as HandleSubmit, y as HistoryConfig, I as IsTuple, z as IsUnion, J as JoinSegments, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, B as NestedType, O as OnError, P as OnInvalidSubmitPolicy, Q as OnSubmit, R as PartialFlatPath, S as Path, T as PathKey, U as PendingValidationStatus, V as PersistConfig, W as PersistConfigOptions, X as PersistIncludeMode, Z as Primitive, _ as ROOT_PATH, $ as ROOT_PATH_KEY, a0 as ReactiveValidationStatus, a1 as RegisterDirective, a2 as RegisterFlatPath, a4 as RegisterOptions, a5 as RegisterSelectModifier, a6 as RegisterTextModifier, a7 as RegisterTransform, ab as SetValueCallback, ac as SetValuePayload, ad as SettledValidationStatus, ae as SlimPrimitiveKind, af as SlimRuntimeOf, ag as SubmitHandler, ah as Unset, ak as ValidateOn, al as ValidateOnConfig, an as ValidationResponse, ao as ValidationResponseWithoutValue, ap as ValueOfUnion, aq as WriteMeta, ar as WriteShape, as as canonicalizePath, at as isPathPrefix, au as isUnset, av as parseDottedPath, aw as unset } from './shared/attaform.C0uGZQ4M.mjs';
6
6
  export { A as AnonPersistError, a as AttaformError, I as InvalidPathError, b as InvalidUseFormConfigError, O as OutsideSetupError, R as RegistryNotInstalledError, c as ReservedFormKeyError, S as SensitivePersistFieldError, d as SubmitErrorHandlerError } from './shared/attaform.B7rzpK1U.mjs';
7
7
 
8
8
  /**
@@ -287,86 +287,79 @@ declare global {
287
287
  }
288
288
 
289
289
  /**
290
- * Library-default heuristic for `shouldShowErrors`. Drives
291
- * `field.showErrors` and `form.meta.showErrors` whenever the consumer
292
- * has not configured an override at either the plugin or per-form
293
- * level.
294
- *
295
- * Four clauses: the first two are hard gates; clause 3 is an
296
- * aggressive override after the first submit attempt; clause 4 is
297
- * the pre-submit timing gate.
298
- *
299
- * 1. **Own-path filter.** The field must have at least one error whose
300
- * path equals the field's own path. Leaves always satisfy this when
301
- * they have errors. Containers (intermediate or root) only satisfy
302
- * it for errors that point directly at them, so a UI rendering
303
- * `field.showErrors` at a container never duplicates errors that a
304
- * more-specific descendant will already render. Aggregate banners
305
- * that want "any error anywhere passed the gate" should bind to
306
- * `form.meta.errorCount > 0` (paired with whatever timing signal
307
- * fits), not to `form.meta.showErrors`.
308
- *
309
- * 2. **Not currently validating.** While the field is mid-revalidation
310
- * (`field.validating === true`) the verdict in `field.errors` is
311
- * stale by definition. The error itself stays in the store under
312
- * the stale-while-revalidate contract so the surface doesn't
313
- * flicker to empty, but the UX gate hides it: the application is
314
- * actively re-checking, so the message would mis-narrate the
315
- * state of the world. Containers roll up `validating` as a
316
- * disjunction, so any descendant under revalidation hides the
317
- * container's `showErrors` too. The error returns the moment the
318
- * new verdict lands and `validating` flips back to false.
319
- *
320
- * 3. **Post-submit override.** Once `formMeta.submissionAttempts > 0`
321
- * the heuristic surfaces every own-path error unconditionally on
322
- * the field axis (subject only to the two gates above). The
323
- * counter covers two distinct gestures:
324
- *
325
- * - `form.handleSubmit` directly, or `wizard.handleSubmit` at an
326
- * intermediate step. The user asked this specific form to
327
- * commit; transient mid-edit hiding is no longer appropriate.
328
- *
329
- * - `wizard.handleSubmit` at the final step. The wizard validates
330
- * every form in parallel and bumps each one's `submissionAttempts`,
331
- * so a `Finish`-button submission reveals errors across the
332
- * whole multistep flow at once. That's the review-surface UX:
333
- * by the time the user tries to commit the wizard, every
334
- * blocking error lights up regardless of which step it lives on.
335
- *
336
- * The arm deliberately covers focused, pristine, and untouched
337
- * fields, so the next paint after the user tried to progress lights
338
- * up every problem the validator found.
339
- *
340
- * 4. **Pre-submit timing gate.** Before the first submit attempt,
341
- * show once the user has touched the field (sticky-true after the
342
- * first blur) AND is not currently focused on it. The not-focused
343
- * half hides transient errors while the user is actively editing
344
- * the field; they reappear when the user blurs (or focuses a
345
- * sibling). This deliberately includes blur-without-typing on a
346
- * required field (touched flips on blur regardless of `dirty`), so
347
- * a user who visits an empty required field and moves on sees the
348
- * error.
349
- *
350
- * The framework already gates on `errors.length > 0` before invoking
351
- * the predicate, so the body only decides *when* to surface existing
352
- * errors, not whether errors exist.
290
+ * Library-default `getDisplayState` heuristic. Resolves every path's
291
+ * `field.displayState` and thus `field.show*` and the `form.meta`
292
+ * rollups — whenever the consumer has not configured an override at the
293
+ * per-form or plugin level.
294
+ *
295
+ * One timing gate, then precedence:
296
+ *
297
+ * 1. **Timing gate.** `gateOpen` once the form has been submitted
298
+ * (`submissionAttempts > 0`) OR the field has been edited and then
299
+ * left (`blurredAfterInteraction === true`). Before the gate opens
300
+ * the verdict is `'idle'` regardless of errors. This is the "reward
301
+ * early, punish late" rule:
302
+ * - A clean tab-through never engages. `blurredAfterInteraction`
303
+ * only flips on a blur that follows an edit, so visiting a field
304
+ * and moving on without editing it stays quiet until a submit
305
+ * forces the issue, even if the field was tabbed through before.
306
+ * - The first pass stays quiet. Editing alone (`interacted`) does
307
+ * not open the gate; the error reveals only once the user
308
+ * finishes that pass and leaves the field, never mid-entry.
309
+ * - Recovery is live. The bit is sticky and carries no not-focused
310
+ * term, so once a field has been revealed it stays open through a
311
+ * re-focus: a shown error clears (or greens) the instant the
312
+ * value becomes valid, without forcing another blur.
313
+ *
314
+ * The submit arm covers `form.handleSubmit` directly and
315
+ * `wizard.handleSubmit` (which bumps `submissionAttempts` on the
316
+ * active form at intermediate steps and on every form at the final
317
+ * step, lighting up the whole flow at once).
318
+ *
319
+ * 2. **Pending.** With the gate open, a per-field run in flight
320
+ * (`validating === true`) wins: the verdict in `errors` is stale by
321
+ * definition, so surface `'pending'` (a spinner) rather than a
322
+ * possibly-wrong error or success. Containers roll `validating` up as
323
+ * a disjunction, so any descendant under revalidation reads
324
+ * `'pending'` at the container too.
325
+ *
326
+ * 3. **Error.** An own-path error (one whose path equals the field's own
327
+ * path) resolves to `'error'`. The own-path filter means a container
328
+ * never duplicates an error a more-specific descendant already
329
+ * renders; aggregate banners bind to `form.meta.errorCount` instead.
330
+ *
331
+ * 4. **Success.** No error, `valid === true`, and the green check is
332
+ * earned: the field is non-blank and `dirty` (its value diverges from
333
+ * the hydration original). Gating success on `dirty && !blank` keeps
334
+ * the check meaningful an empty field that happens to pass, a
335
+ * pre-filled field merely tabbed through, and the post-submit flood of
336
+ * every valid field all stay `'idle'` rather than greening for free.
337
+ * `valid` already gates async schemas on the form-wide first
338
+ * validation pass, so success never fires before the first verdict
339
+ * lands.
340
+ *
341
+ * 5. **Idle.** Anything else gate open but not validating, no own-path
342
+ * error, and either not yet `valid` or valid-but-unearned (blank or
343
+ * unchanged) stays `'idle'`.
353
344
  *
354
345
  * Public re-export so adopters can compose with this without
355
- * copy-pasting the rule body. A layered predicate that adds a special
356
- * case but otherwise defers to the library default picks up future
357
- * heuristic refinements automatically:
346
+ * copy-pasting the rule body a layered predicate that special-cases a
347
+ * subtree but otherwise defers picks up future refinements
348
+ * automatically:
358
349
  *
359
350
  * ```ts
360
- * import { defaultShouldShowErrors } from 'attaform'
351
+ * import { defaultDisplayState } from 'attaform'
361
352
  *
362
353
  * useForm({
363
354
  * schema,
364
- * shouldShowErrors: (field, formMeta) =>
365
- * field.path[0] === 'urgent' || defaultShouldShowErrors(field, formMeta),
355
+ * getDisplayState: (field, formMeta) => {
356
+ * const state = defaultDisplayState(field, formMeta)
357
+ * return field.path[0] === 'username' && state === 'success' ? 'idle' : state
358
+ * },
366
359
  * })
367
360
  * ```
368
361
  */
369
- declare const defaultShouldShowErrors: ShouldShowErrors;
362
+ declare const defaultDisplayState: GetDisplayState;
370
363
 
371
364
  /**
372
365
  * The library's built-in conservative set of identifier name stems that
@@ -529,5 +522,5 @@ declare const PARSE_API_ERRORS_DEFAULTS: {
529
522
  */
530
523
  declare function parseApiErrors(payload: ApiErrorEnvelope | ApiErrorDetails | null | undefined | unknown, options: ParseApiErrorsOptions): ParseApiErrorsResult;
531
524
 
532
- export { AbstractSchema, ApiErrorDetails, ApiErrorEnvelope, AttaformDefaults, AttaformRegistry, DEFAULT_SENSITIVE_NAMES, DEVTOOLS_WINDOW_KEY, DefaultValuesInput, FormKey, GenericForm, PARSE_API_ERRORS_DEFAULTS, REDACTED, RegisterValue, Segment, SerializedFormData, ShouldShowErrors, UseFormConfiguration, UseFormReturnType, ValidationError, assignKey, createAttaform, defaultShouldShowErrors, escapeForInlineScript, hydrateAttaformState, isRegisterValue, parseApiErrors, redactSensitiveLeaves, renderAttaformState, useAbstractForm as useForm, vRegister };
525
+ export { AbstractSchema, ApiErrorDetails, ApiErrorEnvelope, AttaformDefaults, AttaformRegistry, DEFAULT_SENSITIVE_NAMES, DEVTOOLS_WINDOW_KEY, DefaultValuesInput, FormKey, GenericForm, GetDisplayState, PARSE_API_ERRORS_DEFAULTS, REDACTED, RegisterValue, Segment, SerializedFormData, UseFormConfiguration, UseFormReturnType, ValidationError, assignKey, createAttaform, defaultDisplayState, escapeForInlineScript, hydrateAttaformState, isRegisterValue, parseApiErrors, redactSensitiveLeaves, renderAttaformState, useAbstractForm as useForm, vRegister };
533
526
  export type { AttaformDevtoolsBridge, AttaformPluginOptions, ParseApiErrorsOptions, ParseApiErrorsResult, SerializedAttaformState };