attaform 0.18.2 → 0.20.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 (111) 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 -7
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +77 -110
  13. package/dist/index.d.mts +77 -110
  14. package/dist/index.d.ts +77 -110
  15. package/dist/index.mjs +5 -5
  16. package/dist/nuxt.d.cts +1 -1
  17. package/dist/nuxt.d.mts +1 -1
  18. package/dist/nuxt.d.ts +1 -1
  19. package/dist/runtime/components/AttaformDevtoolsPanel.vue +2 -2
  20. package/dist/runtime/components/DevtoolsValueTree.d.vue.ts +1 -3
  21. package/dist/runtime/components/DevtoolsValueTree.vue.d.ts +1 -3
  22. package/dist/runtime/plugins/attaform.cjs +2 -2
  23. package/dist/runtime/plugins/attaform.mjs +2 -2
  24. package/dist/shared/{attaform.CDmaxrt2.mjs → attaform.BKozEdTr.mjs} +305 -178
  25. package/dist/shared/attaform.BKozEdTr.mjs.map +1 -0
  26. package/dist/shared/{attaform.Bubm_slq.cjs → attaform.BM6YD9kZ.cjs} +212 -269
  27. package/dist/shared/attaform.BM6YD9kZ.cjs.map +1 -0
  28. package/dist/shared/{attaform.5UhpSVFI.cjs → attaform.BPxsYtTe.cjs} +2 -26
  29. package/dist/shared/attaform.BPxsYtTe.cjs.map +1 -0
  30. package/dist/shared/{attaform.BqK_L4gK.cjs → attaform.BPy-4qRx.cjs} +305 -180
  31. package/dist/shared/attaform.BPy-4qRx.cjs.map +1 -0
  32. package/dist/shared/attaform.BWgAFnsj.mjs +770 -0
  33. package/dist/shared/attaform.BWgAFnsj.mjs.map +1 -0
  34. package/dist/shared/{attaform.CGX1CNpz.d.ts → attaform.Bh3ACtts.d.ts} +152 -111
  35. package/dist/shared/{attaform.CXpzmj38.mjs → attaform.BupwXkj_.mjs} +213 -270
  36. package/dist/shared/attaform.BupwXkj_.mjs.map +1 -0
  37. package/dist/shared/{attaform.Dlk1jMuv.cjs → attaform.CIn4bMsD.cjs} +263 -799
  38. package/dist/shared/attaform.CIn4bMsD.cjs.map +1 -0
  39. package/dist/shared/{attaform.CZ-XtZt_.mjs → attaform.CKFbKFb6.mjs} +2265 -1509
  40. package/dist/shared/attaform.CKFbKFb6.mjs.map +1 -0
  41. package/dist/shared/{attaform.CuN7ZhBy.d.cts → attaform.D5-1XGQU.d.cts} +152 -111
  42. package/dist/shared/{attaform.-1GQTX2T.mjs → attaform.DEBvCjeH.mjs} +257 -793
  43. package/dist/shared/attaform.DEBvCjeH.mjs.map +1 -0
  44. package/dist/shared/{attaform.II89Pcf4.cjs → attaform.DL4CQ-oW.cjs} +2270 -1514
  45. package/dist/shared/attaform.DL4CQ-oW.cjs.map +1 -0
  46. package/dist/shared/{attaform.FnEwjhvX.d.ts → attaform.DSD85fHb.d.cts} +1 -19
  47. package/dist/shared/{attaform.CRmmNAYp.d.cts → attaform.DSD85fHb.d.mts} +1 -19
  48. package/dist/shared/{attaform.D9wuTGu9.d.mts → attaform.DSD85fHb.d.ts} +1 -19
  49. package/dist/shared/{attaform.B7rzpK1U.d.cts → attaform.DkA5J8NW.d.cts} +1 -17
  50. package/dist/shared/{attaform.B7rzpK1U.d.mts → attaform.DkA5J8NW.d.mts} +1 -17
  51. package/dist/shared/{attaform.B7rzpK1U.d.ts → attaform.DkA5J8NW.d.ts} +1 -17
  52. package/dist/shared/{attaform.B957T6NU.d.ts → attaform.Dl5kDY-A.d.ts} +1 -1
  53. package/dist/shared/attaform.Dmb6itxC.cjs +781 -0
  54. package/dist/shared/attaform.Dmb6itxC.cjs.map +1 -0
  55. package/dist/shared/{attaform.M-RanbyV.d.mts → attaform.DoKXru-a.d.mts} +1 -1
  56. package/dist/shared/attaform.DvA-CJJW.mjs +1876 -0
  57. package/dist/shared/attaform.DvA-CJJW.mjs.map +1 -0
  58. package/dist/shared/{attaform.D1gzu2GL.d.mts → attaform.EMzJcQci.d.mts} +152 -111
  59. package/dist/shared/attaform.EZG6fOFb.mjs +35 -0
  60. package/dist/shared/attaform.EZG6fOFb.mjs.map +1 -0
  61. package/dist/shared/{attaform.XDjA7sRz.d.cts → attaform.GbDo_lJi.d.cts} +1 -1
  62. package/dist/shared/{attaform.Ca5_6Ky-.d.mts → attaform.SfhU0OEY.d.cts} +499 -116
  63. package/dist/shared/{attaform.Ca5_6Ky-.d.cts → attaform.SfhU0OEY.d.mts} +499 -116
  64. package/dist/shared/{attaform.Ca5_6Ky-.d.ts → attaform.SfhU0OEY.d.ts} +499 -116
  65. package/dist/shared/attaform.jgzuNZVC.cjs +1882 -0
  66. package/dist/shared/attaform.jgzuNZVC.cjs.map +1 -0
  67. package/dist/transforms.cjs +2 -2
  68. package/dist/transforms.d.cts +22 -13
  69. package/dist/transforms.d.mts +22 -13
  70. package/dist/transforms.d.ts +22 -13
  71. package/dist/transforms.mjs +1 -1
  72. package/dist/vite.cjs +8 -7
  73. package/dist/vite.cjs.map +1 -1
  74. package/dist/vite.mjs +8 -7
  75. package/dist/vite.mjs.map +1 -1
  76. package/dist/zod-v3.cjs +3 -3
  77. package/dist/zod-v3.d.cts +32 -6
  78. package/dist/zod-v3.d.mts +32 -6
  79. package/dist/zod-v3.d.ts +32 -6
  80. package/dist/zod-v3.mjs +3 -3
  81. package/dist/zod-v4.cjs +3 -3
  82. package/dist/zod-v4.d.cts +12 -8
  83. package/dist/zod-v4.d.mts +12 -8
  84. package/dist/zod-v4.d.ts +12 -8
  85. package/dist/zod-v4.mjs +3 -3
  86. package/dist/zod.cjs +8 -8
  87. package/dist/zod.cjs.map +1 -1
  88. package/dist/zod.d.cts +6 -6
  89. package/dist/zod.d.mts +6 -6
  90. package/dist/zod.d.ts +6 -6
  91. package/dist/zod.mjs +6 -6
  92. package/package.json +2 -2
  93. package/dist/shared/attaform.-1GQTX2T.mjs.map +0 -1
  94. package/dist/shared/attaform.5UhpSVFI.cjs.map +0 -1
  95. package/dist/shared/attaform.BqK_L4gK.cjs.map +0 -1
  96. package/dist/shared/attaform.Bubm_slq.cjs.map +0 -1
  97. package/dist/shared/attaform.C8CyvYa_.cjs +0 -36
  98. package/dist/shared/attaform.C8CyvYa_.cjs.map +0 -1
  99. package/dist/shared/attaform.CDmaxrt2.mjs.map +0 -1
  100. package/dist/shared/attaform.CXpzmj38.mjs.map +0 -1
  101. package/dist/shared/attaform.CZ-XtZt_.mjs.map +0 -1
  102. package/dist/shared/attaform.D13GMFgK.mjs +0 -32
  103. package/dist/shared/attaform.D13GMFgK.mjs.map +0 -1
  104. package/dist/shared/attaform.DUHru0OF.cjs +0 -1600
  105. package/dist/shared/attaform.DUHru0OF.cjs.map +0 -1
  106. package/dist/shared/attaform.Df0tU0Ut.mjs +0 -1594
  107. package/dist/shared/attaform.Df0tU0Ut.mjs.map +0 -1
  108. package/dist/shared/attaform.Dl161U6E.mjs +0 -57
  109. package/dist/shared/attaform.Dl161U6E.mjs.map +0 -1
  110. package/dist/shared/attaform.Dlk1jMuv.cjs.map +0 -1
  111. package/dist/shared/attaform.II89Pcf4.cjs.map +0 -1
package/dist/index.d.mts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { Plugin, App } from 'vue';
2
- import { S as SSRDetectOptions, d as SerializedFormData, c as AttaformRegistry } from './shared/attaform.D1gzu2GL.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.D1gzu2GL.mjs';
4
- import { f as AttaformDefaults, s as FormKey, G as GenericForm, ai as UseFormConfiguration, A as AbstractSchema, j as DefaultValuesInput, aj as UseFormReturnType, a6 as RegisterValue, a1 as RegisterModelDynamicCustomDirective, a8 as Segment, ac as ShouldShowErrors, am as ValidationError, c as ApiErrorEnvelope, a as ApiErrorDetails } from './shared/attaform.Ca5_6Ky-.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, E as ErrorsProxyShape, F as FieldMetaPayload, m as FieldState, n as FieldStateMap, o as FieldStateMapEntry, p as FlatPath, q as FormErrorRecord, r as FormErrorsSurface, t as FormMeta, u as FormStorage, v as FormStorageKind, H as HandleSubmit, w as HistoryConfig, I as IsTuple, x as IsUnion, J as JoinSegments, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, y as NestedType, O as OnError, z as OnInvalidSubmitPolicy, B as OnSubmit, P as PartialFlatPath, Q as Path, R as PathKey, S as PendingValidationStatus, T as PersistConfig, U as PersistConfigOptions, V as PersistIncludeMode, X as Primitive, Y as ROOT_PATH, Z as ROOT_PATH_KEY, _ as ReactiveValidationStatus, $ as RegisterDirective, a0 as RegisterFlatPath, a2 as RegisterOptions, a3 as RegisterSelectModifier, a4 as RegisterTextModifier, a5 as RegisterTransform, a9 as SetValueCallback, aa as SetValuePayload, ab as SettledValidationStatus, ad as ShouldShowErrorsConfig, 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.Ca5_6Ky-.mjs';
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';
2
+ import { S as SSRDetectOptions, c as SerializedFormData, b as AttaformRegistry } from './shared/attaform.EMzJcQci.mjs';
3
+ export { A as AnyForm, a as AttaformErrorCode, C as CompiledStep, F as FormStatus, I as InjectWizardInput, L as LazyMarker, d as StepSlot, U as UseRegisterReturn, e as UseWizardReturnType, W as WizardAggregateError, f 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.EMzJcQci.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, x as GetDisplayState, am as ValidationError, c as ApiErrorEnvelope, a as ApiErrorDetails } from './shared/attaform.SfhU0OEY.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, aa as Segment, 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.SfhU0OEY.mjs';
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 SubmitErrorHandlerError } from './shared/attaform.DkA5J8NW.mjs';
7
7
 
8
8
  /**
9
9
  * Options for `createAttaform()`.
@@ -217,39 +217,13 @@ declare const vRegister: RegisterModelDynamicCustomDirective;
217
217
  * the Nuxt DevTools (overlay) panel wired up via `../../nuxt.ts` +
218
218
  * `../pages/_attaform_devtools.vue`.
219
219
  *
220
- * Centralizing the redaction policy and the window-bridge contract here
221
- * keeps both surfaces aligned: a future tightening of the sensitive-name
222
- * heuristic, or a new field added to the bridge, lands in one file.
220
+ * Houses the window-bridge contract both surfaces consume so a new
221
+ * bridge field lands in one file. Both surfaces render RAW form values
222
+ * by design DevTools is a dev-only surface, and redaction across every
223
+ * place a value surfaces is impractical security theater rather than a
224
+ * real safeguard.
223
225
  */
224
226
 
225
- declare const REDACTED = "[redacted]";
226
- /**
227
- * Walk `value` and replace any leaf whose enclosing path matches the
228
- * sensitive-name heuristic with the string `'[redacted]'`. Returns a
229
- * new tree (no mutation of the input). Object keys + array indices are
230
- * preserved; only the leaf payloads change.
231
- *
232
- * Applied to BOTH devtools surfaces' Form-value rendering AND every
233
- * timeline event payload — leaks via either surface are treatable as
234
- * "any developer with the panel open during user testing can read a
235
- * customer's password," which is exactly the failure mode the
236
- * sensitive-name guard exists to prevent on the storage side.
237
- *
238
- * Leaves whose path doesn't match a pattern pass through untouched.
239
- * `acknowledgeSensitive: true` on persistence does NOT bypass this — if
240
- * the consumer opted into persisting the value, they still shouldn't
241
- * see it in DevTools timelines that grow unbounded.
242
- *
243
- * Implementation note: tracks an `inSensitiveSubtree` flag through the
244
- * recursion instead of allocating a fresh path array per node + calling
245
- * `isSensitivePath` per leaf. Once any ancestor segment matches the
246
- * heuristic, the flag stays set for every descendant — the leaf simply
247
- * returns `REDACTED` without re-scanning the path. For a 100-leaf form:
248
- * ~100 path allocations + ~100 full-path regex sweeps → 0 path
249
- * allocations + ~100 single-segment regex sweeps, with whole-subtree
250
- * short-circuit when sensitive ancestors are found early.
251
- */
252
- declare function redactSensitiveLeaves(value: unknown, matchSensitive: (segment: Segment) => boolean): unknown;
253
227
  /**
254
228
  * Property key on `window` that the Nuxt-side dev plugin attaches the
255
229
  * bridge object to. The iframe-mounted overlay panel reads
@@ -287,86 +261,79 @@ declare global {
287
261
  }
288
262
 
289
263
  /**
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.
264
+ * Library-default `getDisplayState` heuristic. Resolves every path's
265
+ * `field.displayState` and thus `field.show*` and the `form.meta`
266
+ * rollups — whenever the consumer has not configured an override at the
267
+ * per-form or plugin level.
268
+ *
269
+ * One timing gate, then precedence:
270
+ *
271
+ * 1. **Timing gate.** `gateOpen` once the form has been submitted
272
+ * (`submissionAttempts > 0`) OR the field has been edited and then
273
+ * left (`blurredAfterInteraction === true`). Before the gate opens
274
+ * the verdict is `'idle'` regardless of errors. This is the "reward
275
+ * early, punish late" rule:
276
+ * - A clean tab-through never engages. `blurredAfterInteraction`
277
+ * only flips on a blur that follows an edit, so visiting a field
278
+ * and moving on without editing it stays quiet until a submit
279
+ * forces the issue, even if the field was tabbed through before.
280
+ * - The first pass stays quiet. Editing alone (`interacted`) does
281
+ * not open the gate; the error reveals only once the user
282
+ * finishes that pass and leaves the field, never mid-entry.
283
+ * - Recovery is live. The bit is sticky and carries no not-focused
284
+ * term, so once a field has been revealed it stays open through a
285
+ * re-focus: a shown error clears (or greens) the instant the
286
+ * value becomes valid, without forcing another blur.
287
+ *
288
+ * The submit arm covers `form.handleSubmit` directly and
289
+ * `wizard.handleSubmit` (which bumps `submissionAttempts` on the
290
+ * active form at intermediate steps and on every form at the final
291
+ * step, lighting up the whole flow at once).
292
+ *
293
+ * 2. **Pending.** With the gate open, a per-field run in flight
294
+ * (`validating === true`) wins: the verdict in `errors` is stale by
295
+ * definition, so surface `'pending'` (a spinner) rather than a
296
+ * possibly-wrong error or success. Containers roll `validating` up as
297
+ * a disjunction, so any descendant under revalidation reads
298
+ * `'pending'` at the container too.
299
+ *
300
+ * 3. **Error.** An own-path error (one whose path equals the field's own
301
+ * path) resolves to `'error'`. The own-path filter means a container
302
+ * never duplicates an error a more-specific descendant already
303
+ * renders; aggregate banners bind to `form.meta.errorCount` instead.
304
+ *
305
+ * 4. **Success.** No error, `valid === true`, and the green check is
306
+ * earned: the field is non-blank and `dirty` (its value diverges from
307
+ * the hydration original). Gating success on `dirty && !blank` keeps
308
+ * the check meaningful an empty field that happens to pass, a
309
+ * pre-filled field merely tabbed through, and the post-submit flood of
310
+ * every valid field all stay `'idle'` rather than greening for free.
311
+ * `valid` already gates async schemas on the form-wide first
312
+ * validation pass, so success never fires before the first verdict
313
+ * lands.
314
+ *
315
+ * 5. **Idle.** Anything else gate open but not validating, no own-path
316
+ * error, and either not yet `valid` or valid-but-unearned (blank or
317
+ * unchanged) stays `'idle'`.
353
318
  *
354
319
  * 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:
320
+ * copy-pasting the rule body a layered predicate that special-cases a
321
+ * subtree but otherwise defers picks up future refinements
322
+ * automatically:
358
323
  *
359
324
  * ```ts
360
- * import { defaultShouldShowErrors } from 'attaform'
325
+ * import { defaultDisplayState } from 'attaform'
361
326
  *
362
327
  * useForm({
363
328
  * schema,
364
- * shouldShowErrors: (field, formMeta) =>
365
- * field.path[0] === 'urgent' || defaultShouldShowErrors(field, formMeta),
329
+ * getDisplayState: (field, formMeta) => {
330
+ * const state = defaultDisplayState(field, formMeta)
331
+ * return field.path[0] === 'username' && state === 'success' ? 'idle' : state
332
+ * },
366
333
  * })
367
334
  * ```
368
335
  */
369
- declare const defaultShouldShowErrors: ShouldShowErrors;
336
+ declare const defaultDisplayState: GetDisplayState;
370
337
 
371
338
  /**
372
339
  * The library's built-in conservative set of identifier name stems that
@@ -391,9 +358,9 @@ declare const defaultShouldShowErrors: ShouldShowErrors;
391
358
  * })
392
359
  * ```
393
360
  *
394
- * The same resolved predicate gates persistence writes, multi-tab sync
395
- * broadcasts, AND the DevTools redact walk — one source of truth for
396
- * "what counts as sensitive" across every surface.
361
+ * The same resolved predicate gates persistence writes and multi-tab
362
+ * sync broadcasts — one source of truth for "what counts as sensitive"
363
+ * across those surfaces. (DevTools renders raw values by design.)
397
364
  *
398
365
  * **Non-goals.** This is not a soundness guarantee. Adversarial paths
399
366
  * (`'sensitive_data'`, `'CCV'` instead of `'CVV'`) can slip through.
@@ -529,5 +496,5 @@ declare const PARSE_API_ERRORS_DEFAULTS: {
529
496
  */
530
497
  declare function parseApiErrors(payload: ApiErrorEnvelope | ApiErrorDetails | null | undefined | unknown, options: ParseApiErrorsOptions): ParseApiErrorsResult;
531
498
 
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 };
499
+ export { AbstractSchema, ApiErrorDetails, ApiErrorEnvelope, AttaformDefaults, AttaformRegistry, DEFAULT_SENSITIVE_NAMES, DEVTOOLS_WINDOW_KEY, DefaultValuesInput, FormKey, GenericForm, GetDisplayState, PARSE_API_ERRORS_DEFAULTS, RegisterValue, SerializedFormData, UseFormConfiguration, UseFormReturnType, ValidationError, assignKey, createAttaform, defaultDisplayState, escapeForInlineScript, hydrateAttaformState, isRegisterValue, parseApiErrors, renderAttaformState, useAbstractForm as useForm, vRegister };
533
500
  export type { AttaformDevtoolsBridge, AttaformPluginOptions, ParseApiErrorsOptions, ParseApiErrorsResult, SerializedAttaformState };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { Plugin, App } from 'vue';
2
- import { S as SSRDetectOptions, d as SerializedFormData, c as AttaformRegistry } from './shared/attaform.CGX1CNpz.js';
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.CGX1CNpz.js';
4
- import { f as AttaformDefaults, s as FormKey, G as GenericForm, ai as UseFormConfiguration, A as AbstractSchema, j as DefaultValuesInput, aj as UseFormReturnType, a6 as RegisterValue, a1 as RegisterModelDynamicCustomDirective, a8 as Segment, ac as ShouldShowErrors, am as ValidationError, c as ApiErrorEnvelope, a as ApiErrorDetails } from './shared/attaform.Ca5_6Ky-.js';
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, E as ErrorsProxyShape, F as FieldMetaPayload, m as FieldState, n as FieldStateMap, o as FieldStateMapEntry, p as FlatPath, q as FormErrorRecord, r as FormErrorsSurface, t as FormMeta, u as FormStorage, v as FormStorageKind, H as HandleSubmit, w as HistoryConfig, I as IsTuple, x as IsUnion, J as JoinSegments, K as KeyofUnion, L as LiftedValueShape, M as MetaTrackerValue, N as NestedReadType, y as NestedType, O as OnError, z as OnInvalidSubmitPolicy, B as OnSubmit, P as PartialFlatPath, Q as Path, R as PathKey, S as PendingValidationStatus, T as PersistConfig, U as PersistConfigOptions, V as PersistIncludeMode, X as Primitive, Y as ROOT_PATH, Z as ROOT_PATH_KEY, _ as ReactiveValidationStatus, $ as RegisterDirective, a0 as RegisterFlatPath, a2 as RegisterOptions, a3 as RegisterSelectModifier, a4 as RegisterTextModifier, a5 as RegisterTransform, a9 as SetValueCallback, aa as SetValuePayload, ab as SettledValidationStatus, ad as ShouldShowErrorsConfig, 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.Ca5_6Ky-.js';
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.js';
2
+ import { S as SSRDetectOptions, c as SerializedFormData, b as AttaformRegistry } from './shared/attaform.Bh3ACtts.js';
3
+ export { A as AnyForm, a as AttaformErrorCode, C as CompiledStep, F as FormStatus, I as InjectWizardInput, L as LazyMarker, d as StepSlot, U as UseRegisterReturn, e as UseWizardReturnType, W as WizardAggregateError, f 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.Bh3ACtts.js';
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, x as GetDisplayState, am as ValidationError, c as ApiErrorEnvelope, a as ApiErrorDetails } from './shared/attaform.SfhU0OEY.js';
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, aa as Segment, 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.SfhU0OEY.js';
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 SubmitErrorHandlerError } from './shared/attaform.DkA5J8NW.js';
7
7
 
8
8
  /**
9
9
  * Options for `createAttaform()`.
@@ -217,39 +217,13 @@ declare const vRegister: RegisterModelDynamicCustomDirective;
217
217
  * the Nuxt DevTools (overlay) panel wired up via `../../nuxt.ts` +
218
218
  * `../pages/_attaform_devtools.vue`.
219
219
  *
220
- * Centralizing the redaction policy and the window-bridge contract here
221
- * keeps both surfaces aligned: a future tightening of the sensitive-name
222
- * heuristic, or a new field added to the bridge, lands in one file.
220
+ * Houses the window-bridge contract both surfaces consume so a new
221
+ * bridge field lands in one file. Both surfaces render RAW form values
222
+ * by design DevTools is a dev-only surface, and redaction across every
223
+ * place a value surfaces is impractical security theater rather than a
224
+ * real safeguard.
223
225
  */
224
226
 
225
- declare const REDACTED = "[redacted]";
226
- /**
227
- * Walk `value` and replace any leaf whose enclosing path matches the
228
- * sensitive-name heuristic with the string `'[redacted]'`. Returns a
229
- * new tree (no mutation of the input). Object keys + array indices are
230
- * preserved; only the leaf payloads change.
231
- *
232
- * Applied to BOTH devtools surfaces' Form-value rendering AND every
233
- * timeline event payload — leaks via either surface are treatable as
234
- * "any developer with the panel open during user testing can read a
235
- * customer's password," which is exactly the failure mode the
236
- * sensitive-name guard exists to prevent on the storage side.
237
- *
238
- * Leaves whose path doesn't match a pattern pass through untouched.
239
- * `acknowledgeSensitive: true` on persistence does NOT bypass this — if
240
- * the consumer opted into persisting the value, they still shouldn't
241
- * see it in DevTools timelines that grow unbounded.
242
- *
243
- * Implementation note: tracks an `inSensitiveSubtree` flag through the
244
- * recursion instead of allocating a fresh path array per node + calling
245
- * `isSensitivePath` per leaf. Once any ancestor segment matches the
246
- * heuristic, the flag stays set for every descendant — the leaf simply
247
- * returns `REDACTED` without re-scanning the path. For a 100-leaf form:
248
- * ~100 path allocations + ~100 full-path regex sweeps → 0 path
249
- * allocations + ~100 single-segment regex sweeps, with whole-subtree
250
- * short-circuit when sensitive ancestors are found early.
251
- */
252
- declare function redactSensitiveLeaves(value: unknown, matchSensitive: (segment: Segment) => boolean): unknown;
253
227
  /**
254
228
  * Property key on `window` that the Nuxt-side dev plugin attaches the
255
229
  * bridge object to. The iframe-mounted overlay panel reads
@@ -287,86 +261,79 @@ declare global {
287
261
  }
288
262
 
289
263
  /**
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.
264
+ * Library-default `getDisplayState` heuristic. Resolves every path's
265
+ * `field.displayState` and thus `field.show*` and the `form.meta`
266
+ * rollups — whenever the consumer has not configured an override at the
267
+ * per-form or plugin level.
268
+ *
269
+ * One timing gate, then precedence:
270
+ *
271
+ * 1. **Timing gate.** `gateOpen` once the form has been submitted
272
+ * (`submissionAttempts > 0`) OR the field has been edited and then
273
+ * left (`blurredAfterInteraction === true`). Before the gate opens
274
+ * the verdict is `'idle'` regardless of errors. This is the "reward
275
+ * early, punish late" rule:
276
+ * - A clean tab-through never engages. `blurredAfterInteraction`
277
+ * only flips on a blur that follows an edit, so visiting a field
278
+ * and moving on without editing it stays quiet until a submit
279
+ * forces the issue, even if the field was tabbed through before.
280
+ * - The first pass stays quiet. Editing alone (`interacted`) does
281
+ * not open the gate; the error reveals only once the user
282
+ * finishes that pass and leaves the field, never mid-entry.
283
+ * - Recovery is live. The bit is sticky and carries no not-focused
284
+ * term, so once a field has been revealed it stays open through a
285
+ * re-focus: a shown error clears (or greens) the instant the
286
+ * value becomes valid, without forcing another blur.
287
+ *
288
+ * The submit arm covers `form.handleSubmit` directly and
289
+ * `wizard.handleSubmit` (which bumps `submissionAttempts` on the
290
+ * active form at intermediate steps and on every form at the final
291
+ * step, lighting up the whole flow at once).
292
+ *
293
+ * 2. **Pending.** With the gate open, a per-field run in flight
294
+ * (`validating === true`) wins: the verdict in `errors` is stale by
295
+ * definition, so surface `'pending'` (a spinner) rather than a
296
+ * possibly-wrong error or success. Containers roll `validating` up as
297
+ * a disjunction, so any descendant under revalidation reads
298
+ * `'pending'` at the container too.
299
+ *
300
+ * 3. **Error.** An own-path error (one whose path equals the field's own
301
+ * path) resolves to `'error'`. The own-path filter means a container
302
+ * never duplicates an error a more-specific descendant already
303
+ * renders; aggregate banners bind to `form.meta.errorCount` instead.
304
+ *
305
+ * 4. **Success.** No error, `valid === true`, and the green check is
306
+ * earned: the field is non-blank and `dirty` (its value diverges from
307
+ * the hydration original). Gating success on `dirty && !blank` keeps
308
+ * the check meaningful an empty field that happens to pass, a
309
+ * pre-filled field merely tabbed through, and the post-submit flood of
310
+ * every valid field all stay `'idle'` rather than greening for free.
311
+ * `valid` already gates async schemas on the form-wide first
312
+ * validation pass, so success never fires before the first verdict
313
+ * lands.
314
+ *
315
+ * 5. **Idle.** Anything else gate open but not validating, no own-path
316
+ * error, and either not yet `valid` or valid-but-unearned (blank or
317
+ * unchanged) stays `'idle'`.
353
318
  *
354
319
  * 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:
320
+ * copy-pasting the rule body a layered predicate that special-cases a
321
+ * subtree but otherwise defers picks up future refinements
322
+ * automatically:
358
323
  *
359
324
  * ```ts
360
- * import { defaultShouldShowErrors } from 'attaform'
325
+ * import { defaultDisplayState } from 'attaform'
361
326
  *
362
327
  * useForm({
363
328
  * schema,
364
- * shouldShowErrors: (field, formMeta) =>
365
- * field.path[0] === 'urgent' || defaultShouldShowErrors(field, formMeta),
329
+ * getDisplayState: (field, formMeta) => {
330
+ * const state = defaultDisplayState(field, formMeta)
331
+ * return field.path[0] === 'username' && state === 'success' ? 'idle' : state
332
+ * },
366
333
  * })
367
334
  * ```
368
335
  */
369
- declare const defaultShouldShowErrors: ShouldShowErrors;
336
+ declare const defaultDisplayState: GetDisplayState;
370
337
 
371
338
  /**
372
339
  * The library's built-in conservative set of identifier name stems that
@@ -391,9 +358,9 @@ declare const defaultShouldShowErrors: ShouldShowErrors;
391
358
  * })
392
359
  * ```
393
360
  *
394
- * The same resolved predicate gates persistence writes, multi-tab sync
395
- * broadcasts, AND the DevTools redact walk — one source of truth for
396
- * "what counts as sensitive" across every surface.
361
+ * The same resolved predicate gates persistence writes and multi-tab
362
+ * sync broadcasts — one source of truth for "what counts as sensitive"
363
+ * across those surfaces. (DevTools renders raw values by design.)
397
364
  *
398
365
  * **Non-goals.** This is not a soundness guarantee. Adversarial paths
399
366
  * (`'sensitive_data'`, `'CCV'` instead of `'CVV'`) can slip through.
@@ -529,5 +496,5 @@ declare const PARSE_API_ERRORS_DEFAULTS: {
529
496
  */
530
497
  declare function parseApiErrors(payload: ApiErrorEnvelope | ApiErrorDetails | null | undefined | unknown, options: ParseApiErrorsOptions): ParseApiErrorsResult;
531
498
 
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 };
499
+ export { AbstractSchema, ApiErrorDetails, ApiErrorEnvelope, AttaformDefaults, AttaformRegistry, DEFAULT_SENSITIVE_NAMES, DEVTOOLS_WINDOW_KEY, DefaultValuesInput, FormKey, GenericForm, GetDisplayState, PARSE_API_ERRORS_DEFAULTS, RegisterValue, SerializedFormData, UseFormConfiguration, UseFormReturnType, ValidationError, assignKey, createAttaform, defaultDisplayState, escapeForInlineScript, hydrateAttaformState, isRegisterValue, parseApiErrors, renderAttaformState, useAbstractForm as useForm, vRegister };
533
500
  export type { AttaformDevtoolsBridge, AttaformPluginOptions, ParseApiErrorsOptions, ParseApiErrorsResult, SerializedAttaformState };
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
- import { i as canonicalizePath, I as InvalidPathError } from './shared/attaform.CDmaxrt2.mjs';
2
- export { A as AnonPersistError, a as AttaformError, D as DEFAULT_SENSITIVE_NAMES, c as InvalidUseFormConfigError, O as OutsideSetupError, R as ROOT_PATH, d as ROOT_PATH_KEY, e as RegistryNotInstalledError, f as ReservedFormKeyError, S as SensitivePersistFieldError, g as SubmitErrorHandlerError, h as assignKey, l as createAttaform, o as createRegistry, s as getRegistryFromApp, t as isPathPrefix, u as isRegisterValue, x as kAttaformRegistry, C as parseDottedPath, J as useRegister, K as useRegistry, L as vRegister } from './shared/attaform.CDmaxrt2.mjs';
3
- export { D as DEVTOOLS_WINDOW_KEY, R as REDACTED, h as hydrateAttaformState, r as redactSensitiveLeaves, a as renderAttaformState } from './shared/attaform.Dl161U6E.mjs';
4
- import { n as normalizeNumericOption } from './shared/attaform.CZ-XtZt_.mjs';
5
- export { A as AttaformErrorCode, d as defaultCoercionRules, a as defaultShouldShowErrors, b as defineCoercion, i as injectForm, c as injectWizard, f as isUnset, l as lazy, u as unset, k as useForm, m as useWizard } from './shared/attaform.CZ-XtZt_.mjs';
1
+ import { j as canonicalizePath, c as InvalidPathError } from './shared/attaform.BKozEdTr.mjs';
2
+ export { A as AnonPersistError, a as AttaformError, D as DEFAULT_SENSITIVE_NAMES, d as InvalidUseFormConfigError, O as OutsideSetupError, R as ROOT_PATH, e as ROOT_PATH_KEY, f as RegistryNotInstalledError, g as ReservedFormKeyError, S as SubmitErrorHandlerError, i as assignKey, m as createAttaform, p as createRegistry, r as getRegistryFromApp, s as isPathPrefix, t as isRegisterValue, w as kAttaformRegistry, B as parseDottedPath, G as useRegister, H as useRegistry, J as vRegister } from './shared/attaform.BKozEdTr.mjs';
3
+ export { D as DEVTOOLS_WINDOW_KEY, h as hydrateAttaformState, r as renderAttaformState } from './shared/attaform.EZG6fOFb.mjs';
4
+ import { n as normalizeNumericOption } from './shared/attaform.CKFbKFb6.mjs';
5
+ export { A as AttaformErrorCode, d as defaultCoercionRules, a as defaultDisplayState, b as defineCoercion, i as injectForm, c as injectWizard, f as isUnset, l as lazy, u as unset, k as useForm, m as useWizard } from './shared/attaform.CKFbKFb6.mjs';
6
6
 
7
7
  function escapeForInlineScript(json) {
8
8
  return json.replace(/[<>&\u2028\u2029]/g, (char) => {
package/dist/nuxt.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
- import { f as AttaformDefaults } from './shared/attaform.Ca5_6Ky-.cjs';
2
+ import { f as AttaformDefaults } from './shared/attaform.SfhU0OEY.cjs';
3
3
  import 'vue';
4
4
 
5
5
  /**
package/dist/nuxt.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
- import { f as AttaformDefaults } from './shared/attaform.Ca5_6Ky-.mjs';
2
+ import { f as AttaformDefaults } from './shared/attaform.SfhU0OEY.mjs';
3
3
  import 'vue';
4
4
 
5
5
  /**
package/dist/nuxt.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
- import { f as AttaformDefaults } from './shared/attaform.Ca5_6Ky-.js';
2
+ import { f as AttaformDefaults } from './shared/attaform.SfhU0OEY.js';
3
3
  import 'vue';
4
4
 
5
5
  /**
@@ -58,7 +58,7 @@ function humanizePathKey(key) {
58
58
  try {
59
59
  const parsed = JSON.parse(key);
60
60
  if (Array.isArray(parsed)) {
61
- return parsed.map((seg) => typeof seg === "number" ? String(seg) : String(seg)).join(".");
61
+ return parsed.map((seg) => String(seg)).join(".");
62
62
  }
63
63
  } catch {
64
64
  }
@@ -449,5 +449,5 @@ onUnmounted(() => {
449
449
  </template>
450
450
 
451
451
  <style scoped>
452
- .atf-panel{--atf-bg:#0f172a;--atf-bg-elev:#111c33;--atf-fg:#e2e8f0;--atf-fg-muted:#94a3b8;--atf-border:rgba(148,163,184,.12);--atf-border-strong:rgba(148,163,184,.2);--atf-accent:#5b8def;--atf-key:#93c5fd;--atf-string:#86efac;--atf-number:#fbbf24;--atf-boolean:#f472b6;--atf-redacted:#f87171;--atf-muted:#64748b;--atf-row-hover:hsla(0,0%,100%,.04);--atf-error-bg:rgba(248,113,113,.1);--atf-warn-bg:rgba(251,191,36,.1);background:var(--atf-bg);color:var(--atf-fg);display:flex;flex-direction:column;font-family:system-ui,-apple-system,Segoe UI,sans-serif;font-size:13px;height:100vh;line-height:1.5}@media (prefers-color-scheme:light){.atf-panel{--atf-bg:#fff;--atf-bg-elev:#f8fafc;--atf-fg:#0f172a;--atf-fg-muted:#64748b;--atf-border:rgba(15,23,42,.08);--atf-border-strong:rgba(15,23,42,.16);--atf-key:#2563eb;--atf-string:#16a34a;--atf-number:#d97706;--atf-boolean:#db2777;--atf-redacted:#dc2626;--atf-muted:#94a3b8;--atf-row-hover:rgba(15,23,42,.04);--atf-error-bg:rgba(220,38,38,.08);--atf-warn-bg:rgba(217,119,6,.08)}}.atf-header{background:var(--atf-bg-elev);border-bottom:1px solid var(--atf-border);flex:0 0 auto;padding:.75rem 1rem}.atf-brand{align-items:center;display:flex;gap:.5rem}.atf-logo{display:block;height:22px;width:22px}.atf-title{font-size:14px;font-weight:600}.atf-version{color:var(--atf-fg-muted);font-family:ui-monospace,monospace;font-size:11px}.atf-body{display:grid;flex:1 1 auto;grid-template-columns:200px 1fr;min-height:0}.atf-sidebar{border-right:1px solid var(--atf-border);overflow-y:auto;padding:.75rem 0}.atf-sidebar-title{align-items:center;color:var(--atf-fg-muted);display:flex;font-size:11px;gap:.4em;letter-spacing:.05em;padding:0 1rem .5rem;text-transform:uppercase}.atf-count{background:var(--atf-border-strong);border-radius:999px;color:var(--atf-fg);font-size:10px;padding:0 .4em}.atf-empty{color:var(--atf-fg-muted);list-style:none;margin:0;padding:0 1rem}.atf-empty small{display:block;font-size:11px;margin-top:.4rem}.atf-empty code{background:var(--atf-border);border-radius:3px;font-size:11px;padding:.05em .35em}.atf-form-list{list-style:none;margin:0;padding:0}.atf-form-item{cursor:pointer;font-family:ui-monospace,monospace;font-size:12px;padding:.4rem 1rem;-webkit-user-select:none;-moz-user-select:none;user-select:none}.atf-form-item:hover{background:var(--atf-row-hover)}.atf-form-item.active{background:rgba(91,141,239,.12);border-left:2px solid var(--atf-accent);color:var(--atf-key);padding-left:calc(1rem - 2px)}.atf-detail{overflow-y:auto;padding:1rem 1.25rem}.atf-empty-detail{color:var(--atf-fg-muted);padding:3rem 0;text-align:center}.atf-section{margin-bottom:1.25rem}.atf-section-title{align-items:center;color:var(--atf-fg-muted);display:flex;font-size:11px;font-weight:600;gap:.5em;letter-spacing:.06em;margin:0 0 .5rem;text-transform:uppercase}.atf-section-body{background:var(--atf-bg-elev);border:1px solid var(--atf-border);border-radius:6px;padding:.6rem .8rem}.atf-badge{border-radius:999px;font-size:10px;font-weight:600;letter-spacing:.04em;padding:0 .45em}.atf-badge-error{background:var(--atf-error-bg);color:var(--atf-redacted)}.atf-badge-warn{background:var(--atf-warn-bg);color:var(--atf-number)}.atf-empty-list{color:var(--atf-fg-muted);font-size:12px;margin:0}.atf-error-list{list-style:none;margin:0;padding:0}.atf-error-list>li+li{border-top:1px solid var(--atf-border);margin-top:.6rem;padding-top:.6rem}.atf-path{color:var(--atf-key);display:block;font-family:ui-monospace,monospace;font-size:12px;margin-bottom:.25rem}.atf-error-messages{color:var(--atf-redacted);font-size:12px;list-style:none;margin:0;padding:0}.atf-error-messages>li+li{margin-top:.2rem}.atf-aggregates{display:grid;font-family:ui-monospace,monospace;font-size:12px;gap:.35rem .75rem;grid-template-columns:max-content 1fr;margin:0}.atf-aggregates dt{color:var(--atf-key)}.atf-aggregates dd{color:var(--atf-fg);margin:0}.atf-badge-neutral{background:var(--atf-border-strong);color:var(--atf-fg)}.atf-clear-btn{background:transparent;border:1px solid var(--atf-border);border-radius:4px;cursor:pointer;font:inherit;font-size:11px;padding:.1rem .5rem}.atf-clear-btn,.atf-section-hint{color:var(--atf-fg-muted);margin-left:auto}.atf-section-hint{font-size:10px;font-weight:400;letter-spacing:0;text-transform:none}.atf-fg-muted{color:var(--atf-fg-muted)}.atf-clear-btn:hover{border-color:var(--atf-border-strong);color:var(--atf-fg)}.atf-timeline{list-style:none;margin:0;max-height:18rem;overflow-y:auto;padding:0}.atf-timeline-entry{border-top:1px solid var(--atf-border)}.atf-timeline-entry:first-child{border-top:0}.atf-timeline-row{align-items:baseline;cursor:pointer;display:grid;font-family:ui-monospace,monospace;font-size:11px;gap:.6rem;grid-template-columns:7.5rem 8rem 1fr auto;padding:.4rem 0}.atf-timeline-row:hover{background:var(--atf-row-hover)}.atf-timeline-time{color:var(--atf-fg-muted)}.atf-timeline-type{color:var(--atf-key);font-weight:600}.atf-timeline-form{color:var(--atf-fg);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.atf-timeline-caret{color:var(--atf-fg-muted);text-align:center;width:1em}.atf-timeline-entry.atf-timeline-submit .atf-timeline-type{color:var(--atf-string)}.atf-timeline-entry.atf-timeline-reset .atf-timeline-type{color:var(--atf-redacted)}.atf-timeline-detail{border-top:1px dashed var(--atf-border);margin-top:-1px;padding:.4rem 0 .6rem 7.5rem}
452
+ .atf-panel{--atf-bg:#0f172a;--atf-bg-elev:#111c33;--atf-fg:#e2e8f0;--atf-fg-muted:#94a3b8;--atf-border:rgba(148,163,184,.12);--atf-border-strong:rgba(148,163,184,.2);--atf-accent:#5b8def;--atf-key:#93c5fd;--atf-string:#86efac;--atf-number:#fbbf24;--atf-boolean:#f472b6;--atf-danger:#f87171;--atf-muted:#64748b;--atf-row-hover:hsla(0,0%,100%,.04);--atf-error-bg:rgba(248,113,113,.1);--atf-warn-bg:rgba(251,191,36,.1);background:var(--atf-bg);color:var(--atf-fg);display:flex;flex-direction:column;font-family:system-ui,-apple-system,Segoe UI,sans-serif;font-size:13px;height:100vh;line-height:1.5}@media (prefers-color-scheme:light){.atf-panel{--atf-bg:#fff;--atf-bg-elev:#f8fafc;--atf-fg:#0f172a;--atf-fg-muted:#64748b;--atf-border:rgba(15,23,42,.08);--atf-border-strong:rgba(15,23,42,.16);--atf-key:#2563eb;--atf-string:#16a34a;--atf-number:#d97706;--atf-boolean:#db2777;--atf-danger:#dc2626;--atf-muted:#94a3b8;--atf-row-hover:rgba(15,23,42,.04);--atf-error-bg:rgba(220,38,38,.08);--atf-warn-bg:rgba(217,119,6,.08)}}.atf-header{background:var(--atf-bg-elev);border-bottom:1px solid var(--atf-border);flex:0 0 auto;padding:.75rem 1rem}.atf-brand{align-items:center;display:flex;gap:.5rem}.atf-logo{display:block;height:22px;width:22px}.atf-title{font-size:14px;font-weight:600}.atf-version{color:var(--atf-fg-muted);font-family:ui-monospace,monospace;font-size:11px}.atf-body{display:grid;flex:1 1 auto;grid-template-columns:200px 1fr;min-height:0}.atf-sidebar{border-right:1px solid var(--atf-border);overflow-y:auto;padding:.75rem 0}.atf-sidebar-title{align-items:center;color:var(--atf-fg-muted);display:flex;font-size:11px;gap:.4em;letter-spacing:.05em;padding:0 1rem .5rem;text-transform:uppercase}.atf-count{background:var(--atf-border-strong);border-radius:999px;color:var(--atf-fg);font-size:10px;padding:0 .4em}.atf-empty{color:var(--atf-fg-muted);list-style:none;margin:0;padding:0 1rem}.atf-empty small{display:block;font-size:11px;margin-top:.4rem}.atf-empty code{background:var(--atf-border);border-radius:3px;font-size:11px;padding:.05em .35em}.atf-form-list{list-style:none;margin:0;padding:0}.atf-form-item{cursor:pointer;font-family:ui-monospace,monospace;font-size:12px;padding:.4rem 1rem;-webkit-user-select:none;-moz-user-select:none;user-select:none}.atf-form-item:hover{background:var(--atf-row-hover)}.atf-form-item.active{background:rgba(91,141,239,.12);border-left:2px solid var(--atf-accent);color:var(--atf-key);padding-left:calc(1rem - 2px)}.atf-detail{overflow-y:auto;padding:1rem 1.25rem}.atf-empty-detail{color:var(--atf-fg-muted);padding:3rem 0;text-align:center}.atf-section{margin-bottom:1.25rem}.atf-section-title{align-items:center;color:var(--atf-fg-muted);display:flex;font-size:11px;font-weight:600;gap:.5em;letter-spacing:.06em;margin:0 0 .5rem;text-transform:uppercase}.atf-section-body{background:var(--atf-bg-elev);border:1px solid var(--atf-border);border-radius:6px;padding:.6rem .8rem}.atf-badge{border-radius:999px;font-size:10px;font-weight:600;letter-spacing:.04em;padding:0 .45em}.atf-badge-error{background:var(--atf-error-bg);color:var(--atf-danger)}.atf-badge-warn{background:var(--atf-warn-bg);color:var(--atf-number)}.atf-empty-list{color:var(--atf-fg-muted);font-size:12px;margin:0}.atf-error-list{list-style:none;margin:0;padding:0}.atf-error-list>li+li{border-top:1px solid var(--atf-border);margin-top:.6rem;padding-top:.6rem}.atf-path{color:var(--atf-key);display:block;font-family:ui-monospace,monospace;font-size:12px;margin-bottom:.25rem}.atf-error-messages{color:var(--atf-danger);font-size:12px;list-style:none;margin:0;padding:0}.atf-error-messages>li+li{margin-top:.2rem}.atf-aggregates{display:grid;font-family:ui-monospace,monospace;font-size:12px;gap:.35rem .75rem;grid-template-columns:max-content 1fr;margin:0}.atf-aggregates dt{color:var(--atf-key)}.atf-aggregates dd{color:var(--atf-fg);margin:0}.atf-badge-neutral{background:var(--atf-border-strong);color:var(--atf-fg)}.atf-clear-btn{background:transparent;border:1px solid var(--atf-border);border-radius:4px;cursor:pointer;font:inherit;font-size:11px;padding:.1rem .5rem}.atf-clear-btn,.atf-section-hint{color:var(--atf-fg-muted);margin-left:auto}.atf-section-hint{font-size:10px;font-weight:400;letter-spacing:0;text-transform:none}.atf-fg-muted{color:var(--atf-fg-muted)}.atf-clear-btn:hover{border-color:var(--atf-border-strong);color:var(--atf-fg)}.atf-timeline{list-style:none;margin:0;max-height:18rem;overflow-y:auto;padding:0}.atf-timeline-entry{border-top:1px solid var(--atf-border)}.atf-timeline-entry:first-child{border-top:0}.atf-timeline-row{align-items:baseline;cursor:pointer;display:grid;font-family:ui-monospace,monospace;font-size:11px;gap:.6rem;grid-template-columns:7.5rem 8rem 1fr auto;padding:.4rem 0}.atf-timeline-row:hover{background:var(--atf-row-hover)}.atf-timeline-time{color:var(--atf-fg-muted)}.atf-timeline-type{color:var(--atf-key);font-weight:600}.atf-timeline-form{color:var(--atf-fg);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.atf-timeline-caret{color:var(--atf-fg-muted);text-align:center;width:1em}.atf-timeline-entry.atf-timeline-submit .atf-timeline-type{color:var(--atf-string)}.atf-timeline-entry.atf-timeline-reset .atf-timeline-type{color:var(--atf-danger)}.atf-timeline-detail{border-top:1px dashed var(--atf-border);margin-top:-1px;padding:.4rem 0 .6rem 7.5rem}
453
453
  </style>
@@ -11,9 +11,7 @@ type __VLS_Props = {
11
11
  path?: ReadonlyArray<string | number>;
12
12
  /**
13
13
  * Edit-mode toggle. When `true` and `onEdit` is wired, leaf cells
14
- * become click-to-edit. Sensitive (redacted) leaves stay read-only
15
- * regardless — overwriting with the literal `[redacted]` string
16
- * would destroy the real value.
14
+ * become click-to-edit. Values render raw (DevTools does not redact).
17
15
  */
18
16
  editable?: boolean | undefined;
19
17
  onEdit?: ((path: ReadonlyArray<string | number>, next: unknown) => void) | undefined;
@@ -11,9 +11,7 @@ type __VLS_Props = {
11
11
  path?: ReadonlyArray<string | number>;
12
12
  /**
13
13
  * Edit-mode toggle. When `true` and `onEdit` is wired, leaf cells
14
- * become click-to-edit. Sensitive (redacted) leaves stay read-only
15
- * regardless — overwriting with the literal `[redacted]` string
16
- * would destroy the real value.
14
+ * become click-to-edit. Values render raw (DevTools does not redact).
17
15
  */
18
16
  editable?: boolean | undefined;
19
17
  onEdit?: ((path: ReadonlyArray<string | number>, next: unknown) => void) | undefined;