attaform 0.17.2 → 0.18.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 (115) hide show
  1. package/README.md +77 -36
  2. package/dist/chunks/devtools.cjs +10 -37
  3. package/dist/chunks/devtools.cjs.map +1 -1
  4. package/dist/chunks/devtools.mjs +10 -37
  5. package/dist/chunks/devtools.mjs.map +1 -1
  6. package/dist/chunks/indexeddb.cjs +4 -4
  7. package/dist/chunks/indexeddb.cjs.map +1 -1
  8. package/dist/chunks/indexeddb.mjs +1 -1
  9. package/dist/chunks/local-storage.cjs +2 -2
  10. package/dist/chunks/local-storage.cjs.map +1 -1
  11. package/dist/chunks/local-storage.mjs +1 -1
  12. package/dist/chunks/session-storage.cjs +2 -2
  13. package/dist/chunks/session-storage.cjs.map +1 -1
  14. package/dist/chunks/session-storage.mjs +1 -1
  15. package/dist/index.cjs +42 -37
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +159 -196
  18. package/dist/index.d.mts +159 -196
  19. package/dist/index.d.ts +159 -196
  20. package/dist/index.mjs +5 -7
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/nuxt.cjs +31 -40
  23. package/dist/nuxt.cjs.map +1 -1
  24. package/dist/nuxt.d.cts +8 -1
  25. package/dist/nuxt.d.mts +8 -1
  26. package/dist/nuxt.d.ts +8 -1
  27. package/dist/nuxt.mjs +32 -41
  28. package/dist/nuxt.mjs.map +1 -1
  29. package/dist/runtime/components/AttaformDevtoolsPanel.d.vue.ts +7 -0
  30. package/dist/runtime/components/AttaformDevtoolsPanel.vue +453 -0
  31. package/dist/runtime/components/AttaformDevtoolsPanel.vue.d.ts +7 -0
  32. package/dist/runtime/components/DevtoolsValueTree.d.vue.ts +37 -0
  33. package/dist/runtime/components/DevtoolsValueTree.vue +192 -0
  34. package/dist/runtime/components/DevtoolsValueTree.vue.d.ts +37 -0
  35. package/dist/runtime/plugins/attaform.cjs +17 -6
  36. package/dist/runtime/plugins/attaform.cjs.map +1 -1
  37. package/dist/runtime/plugins/attaform.mjs +15 -4
  38. package/dist/runtime/plugins/attaform.mjs.map +1 -1
  39. package/dist/shared/attaform.5UhpSVFI.cjs +63 -0
  40. package/dist/shared/attaform.5UhpSVFI.cjs.map +1 -0
  41. package/dist/shared/attaform.BDdFdjeX.mjs +57 -0
  42. package/dist/shared/attaform.BDdFdjeX.mjs.map +1 -0
  43. package/dist/shared/attaform.Bgu9l6OG.d.cts +1651 -0
  44. package/dist/shared/attaform.BmDBu4ql.d.ts +1651 -0
  45. package/dist/shared/{attaform.Dee2rU1P.cjs → attaform.BqK_L4gK.cjs} +310 -24
  46. package/dist/shared/attaform.BqK_L4gK.cjs.map +1 -0
  47. package/dist/shared/{attaform.C_5aB6EQ.d.ts → attaform.BsMdl-35.d.cts} +754 -146
  48. package/dist/shared/{attaform.C_5aB6EQ.d.mts → attaform.BsMdl-35.d.mts} +754 -146
  49. package/dist/shared/{attaform.C_5aB6EQ.d.cts → attaform.BsMdl-35.d.ts} +754 -146
  50. package/dist/shared/attaform.Bubm_slq.cjs.map +1 -1
  51. package/dist/shared/{attaform.C6lbmMUe.d.ts → attaform.C3x1hKJC.d.mts} +4 -4
  52. package/dist/shared/{attaform.CuE-bS1C.d.mts → attaform.CWs1Z3p7.d.ts} +57 -23
  53. package/dist/shared/attaform.CXpzmj38.mjs.map +1 -1
  54. package/dist/shared/{attaform.C5MH4lNh.d.mts → attaform.CjmJpfLH.d.ts} +4 -4
  55. package/dist/shared/{attaform.Drt6fivF.mjs → attaform.CtNUB9nf.mjs} +74 -76
  56. package/dist/shared/attaform.CtNUB9nf.mjs.map +1 -0
  57. package/dist/shared/{attaform.C0iFnTN0.d.ts → attaform.D-hDvb98.d.cts} +57 -23
  58. package/dist/shared/attaform.DAH3kvav.d.mts +1651 -0
  59. package/dist/shared/{attaform.BPRHR3Zs.cjs → attaform.DUHru0OF.cjs} +83 -85
  60. package/dist/shared/attaform.DUHru0OF.cjs.map +1 -0
  61. package/dist/shared/{attaform.BV40t5y2.cjs → attaform.Dlk1jMuv.cjs} +245 -108
  62. package/dist/shared/attaform.Dlk1jMuv.cjs.map +1 -0
  63. package/dist/shared/{attaform.B3ZaPIzS.mjs → attaform.DsC3rZHG.mjs} +1804 -219
  64. package/dist/shared/attaform.DsC3rZHG.mjs.map +1 -0
  65. package/dist/shared/{attaform.DtMN-MAm.d.cts → attaform.Dzi89x8N.d.cts} +4 -4
  66. package/dist/shared/{attaform.Cer8JO_P.cjs → attaform.II89Pcf4.cjs} +1860 -272
  67. package/dist/shared/attaform.II89Pcf4.cjs.map +1 -0
  68. package/dist/shared/{attaform.CIEQgJnM.mjs → attaform.Xhg0AYNa.mjs} +300 -26
  69. package/dist/shared/attaform.Xhg0AYNa.mjs.map +1 -0
  70. package/dist/shared/{attaform.CpERWz3u.mjs → attaform.Xt0A3QUd.mjs} +232 -95
  71. package/dist/shared/attaform.Xt0A3QUd.mjs.map +1 -0
  72. package/dist/shared/{attaform.CHorcsIU.d.cts → attaform.bH7WvNad.d.mts} +57 -23
  73. package/dist/vite.cjs +270 -2
  74. package/dist/vite.cjs.map +1 -1
  75. package/dist/vite.mjs +266 -2
  76. package/dist/vite.mjs.map +1 -1
  77. package/dist/zod-v3.cjs +11 -8
  78. package/dist/zod-v3.cjs.map +1 -1
  79. package/dist/zod-v3.d.cts +6 -6
  80. package/dist/zod-v3.d.mts +6 -6
  81. package/dist/zod-v3.d.ts +6 -6
  82. package/dist/zod-v3.mjs +3 -3
  83. package/dist/zod-v4.cjs +11 -8
  84. package/dist/zod-v4.cjs.map +1 -1
  85. package/dist/zod-v4.d.cts +5 -5
  86. package/dist/zod-v4.d.mts +5 -5
  87. package/dist/zod-v4.d.ts +5 -5
  88. package/dist/zod-v4.mjs +3 -3
  89. package/dist/zod.cjs +15 -16
  90. package/dist/zod.cjs.map +1 -1
  91. package/dist/zod.d.cts +127 -40
  92. package/dist/zod.d.mts +127 -40
  93. package/dist/zod.d.ts +127 -40
  94. package/dist/zod.mjs +7 -11
  95. package/dist/zod.mjs.map +1 -1
  96. package/package.json +19 -5
  97. package/dist/shared/attaform.B1jvxsOF.d.mts +0 -156
  98. package/dist/shared/attaform.B3ZaPIzS.mjs.map +0 -1
  99. package/dist/shared/attaform.BBM2muQ9.cjs +0 -101
  100. package/dist/shared/attaform.BBM2muQ9.cjs.map +0 -1
  101. package/dist/shared/attaform.BPRHR3Zs.cjs.map +0 -1
  102. package/dist/shared/attaform.BV40t5y2.cjs.map +0 -1
  103. package/dist/shared/attaform.C6qzEdIM.d.cts +0 -156
  104. package/dist/shared/attaform.C8LVFVVe.cjs +0 -32
  105. package/dist/shared/attaform.C8LVFVVe.cjs.map +0 -1
  106. package/dist/shared/attaform.CIEQgJnM.mjs.map +0 -1
  107. package/dist/shared/attaform.CTwNcpLE.d.ts +0 -156
  108. package/dist/shared/attaform.Cer8JO_P.cjs.map +0 -1
  109. package/dist/shared/attaform.CpERWz3u.mjs.map +0 -1
  110. package/dist/shared/attaform.Dee2rU1P.cjs.map +0 -1
  111. package/dist/shared/attaform.Drt6fivF.mjs.map +0 -1
  112. package/dist/shared/attaform.Vo-Kft0t.mjs +0 -29
  113. package/dist/shared/attaform.Vo-Kft0t.mjs.map +0 -1
  114. package/dist/shared/attaform.h1sq3BFu.mjs +0 -92
  115. package/dist/shared/attaform.h1sq3BFu.mjs.map +0 -1
package/dist/index.d.cts CHANGED
@@ -1,187 +1,10 @@
1
- import { App, InjectionKey, Plugin } from 'vue';
2
- import { C as CoercionRegistry, S as SlimPrimitiveKind, a as CoercionEntry, A as AttaformDefaults, F as FormKey, G as GenericForm, U as UseFormConfiguration, b as AbstractSchema, D as DeepPartial, c as DefaultValuesShape, d as UseFormReturnType, R as RegisterValue, e as RegisterModelDynamicCustomDirective, f as ShouldShowErrors, V as ValidationError, g as ApiErrorEnvelope, h as ApiErrorDetails } from './shared/attaform.C_5aB6EQ.cjs';
3
- export { i as ApiErrorEntry, j as ArrayItem, k as ArrayPath, l as CoercionResult, m as CustomDirectiveRegisterAssignerFn, n as DefaultValuesResponse, o as FieldMetaPayload, p as FieldState, q as FieldStateMap, r as FieldStateMapEntry, s as FlatPath, t as FormErrorRecord, u as FormErrorsSurface, v as FormMeta, w as FormStorage, x 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, E as OnInvalidSubmitPolicy, P as OnSubmit, Q as Path, T as PathKey, W as PendingValidationStatus, X as PersistConfig, Y as PersistConfigOptions, Z as PersistIncludeMode, _ as ROOT_PATH, $ as ROOT_PATH_KEY, a0 as ReactiveValidationStatus, a1 as RegisterDirective, a2 as RegisterFlatPath, a3 as RegisterOptions, a4 as RegisterSelectModifier, a5 as RegisterTextModifier, a6 as RegisterTransform, a7 as Segment, a8 as SetValueCallback, a9 as SetValuePayload, aa as SettledValidationStatus, ab as ShouldShowErrorsConfig, ac as SlimRuntimeOf, ad as SubmitHandler, ae as Unset, af as ValidateOn, ag as ValidateOnConfig, ah as ValidationResponse, ai as ValidationResponseWithoutValue, aj as ValueOfUnion, ak as WriteMeta, al as WriteShape, am as canonicalizePath, an as isPathPrefix, ao as isUnset, ap as parseDottedPath, aq as unset } from './shared/attaform.C_5aB6EQ.cjs';
4
- export { A as AttaformErrorCode, i as injectForm, u as useRegister } from './shared/attaform.C6qzEdIM.cjs';
1
+ import { Plugin, App } from 'vue';
2
+ import { S as SSRDetectOptions, a as SerializedFormData, A as AttaformRegistry } from './shared/attaform.Bgu9l6OG.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.Bgu9l6OG.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.BsMdl-35.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.BsMdl-35.cjs';
5
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';
6
7
 
7
- /**
8
- * Schema-driven coercion of user-typed DOM values at the v-register
9
- * directive layer. When the slim schema declares a numeric or
10
- * boolean type at a path, the directive coerces incoming string
11
- * values (`'25'` → `25`, `'true'` → `true`) before the slim-primitive
12
- * gate sees the write — making the schema authoritative for storage
13
- * shape and freeing consumers from sprinkling `.number` modifiers
14
- * across templates.
15
- *
16
- * Coercion is consumer-extensible: a `CoercionRegistry` is just an
17
- * `Array<CoercionEntry>` keyed at config time by `(input, output)`
18
- * `SlimPrimitiveKind` literals. The library ships
19
- * `defaultCoercionRules` (string→number, string→boolean) and
20
- * `defineCoercion` for type-narrowed authoring; consumers spread the
21
- * defaults to extend or supply their own array to replace.
22
- *
23
- * Coercion applies ONLY to user-typed DOM values flowing through
24
- * the directive's assigner. Programmatic writes (`form.setValue`,
25
- * `setValueWithInternalPath`) bypass coercion — they're authoritative
26
- * writes whose strict typing is on the caller. This mirrors the
27
- * `transforms` pipeline's user-input-only contract.
28
- */
29
-
30
- /**
31
- * Type-narrowing helper for authoring entries. At runtime it's
32
- * identity; at compile time it preserves the `input` / `output`
33
- * literal types so `transform`'s parameter is narrowed to the
34
- * runtime type instead of widening to `SlimRuntimeOf<SlimPrimitiveKind>`.
35
- *
36
- * Without this helper, authoring `{ input: 'string', output:
37
- * 'number', transform: (s) => ... }` against the broader
38
- * `CoercionEntry` widens `s` to `string | number | boolean | ...`,
39
- * forcing a cast in every transform body. `defineCoercion` is the
40
- * opaque-free idiom.
41
- */
42
- declare function defineCoercion<I extends SlimPrimitiveKind, O extends SlimPrimitiveKind>(entry: CoercionEntry<I, O>): CoercionEntry<I, O>;
43
- /**
44
- * The library's built-in registry. Two cells: string→number and
45
- * string→boolean. Re-exported so consumers can spread it when
46
- * supplying a custom registry that extends defaults.
47
- */
48
- declare const defaultCoercionRules: CoercionRegistry;
49
-
50
- /**
51
- * Portable SSR detection. The plugin captures this value at install time and
52
- * exposes it via the registry so every runtime branch reads a single source
53
- * of truth instead of sniffing `import.meta.*` (bundler-specific) at each
54
- * call site.
55
- *
56
- * Consumers can override the heuristic explicitly via
57
- * `createAttaform({ ssr: true })`; the default handles the common
58
- * Node-vs-browser split without relying on any bundler-injected flag.
59
- */
60
- interface SSRDetectOptions {
61
- /**
62
- * Force SSR-vs-client mode, bypassing the `typeof window` heuristic.
63
- * `true` activates the SSR code paths (no devtools, no persistence
64
- * wiring, payload serialisation enabled); `false` forces client mode.
65
- * The Nuxt plugin sets this from `import.meta.server` so SSR detection
66
- * never depends on whether `window` is polyfilled. Tests that need to
67
- * exercise the SSR code paths under jsdom pass `ssr: true`.
68
- */
69
- ssr?: boolean;
70
- }
71
-
72
- /**
73
- * Per-Vue-app container for all form state instances. Each
74
- * `app.use(createAttaform())` call gets its own registry,
75
- * so the library runs under bare Vue 3 + SSR (via
76
- * `@vue/server-renderer`) and Nuxt with the same code path.
77
- *
78
- * Each form's state lives in `forms: Map<FormKey, FormStore<GenericForm>>`.
79
- * The type relaxation at storage time is necessary because different
80
- * forms in the same app have different `Form` generics; callers recover
81
- * the specific form type via `useForm`'s overloads.
82
- */
83
- /**
84
- * Serialised snapshot of one form's state, captured by
85
- * `renderAttaformState` for SSR and replayed by
86
- * `hydrateAttaformState` on the client. Round-trips through
87
- * JSON-safe tuples; field references are intentionally omitted
88
- * (DOM nodes don't survive serialisation).
89
- */
90
- type SerializedFormData = {
91
- /** The form's value at snapshot time. */
92
- readonly form: unknown;
93
- /**
94
- * Errors produced by the schema at snapshot time. Replayed into
95
- * the client form's error state at hydration; cleared on
96
- * successful re-validation client-side.
97
- */
98
- readonly schemaErrors: ReadonlyArray<readonly [string, unknown]>;
99
- /**
100
- * Errors set explicitly via `setFieldErrors` / `addFieldErrors`
101
- * (typically from a server response parsed via `parseApiErrors`)
102
- * at snapshot time. Replayed at hydration; persists across
103
- * client-side re-validation.
104
- */
105
- readonly userErrors: ReadonlyArray<readonly [string, unknown]>;
106
- /** Per-field metadata (timestamps, raw values, connection flags) captured at snapshot time. */
107
- readonly fields: ReadonlyArray<readonly [string, unknown]>;
108
- /**
109
- * Path keys that were in the form's `blankPaths` set at
110
- * snapshot time. Round-trips the "displayed empty" UI state across
111
- * the SSR boundary — without it, the client briefly renders
112
- * `String(slim-default)` (e.g. `'0'`) for fields the server
113
- * rendered as blank. Optional in the wire format so older payload
114
- * shapes deserialise cleanly.
115
- */
116
- readonly blankPaths?: ReadonlyArray<string>;
117
- };
118
- /**
119
- * The library's per-Vue-app container. One `AttaformRegistry` is
120
- * created per `app.use(createAttaform())` call.
121
- *
122
- * Most consumers never touch this directly — `useForm` and
123
- * `injectForm` reach the registry on your behalf. Access it
124
- * explicitly only when wiring SSR or a custom plugin integration.
125
- */
126
- type AttaformRegistry = {
127
- /** `true` while running on the server during SSR; `false` on the client. */
128
- readonly ssr: boolean;
129
- /** App-level defaults applied to every `useForm` call. */
130
- readonly defaults: AttaformDefaults;
131
- };
132
- /**
133
- * The Vue `InjectionKey` under which the registry is provided on the
134
- * app. Most consumers never need this — `useForm` and
135
- * `injectForm` resolve the registry automatically.
136
- */
137
- declare const kAttaformRegistry: InjectionKey<AttaformRegistry>;
138
- declare module 'vue' {
139
- interface App {
140
- }
141
- }
142
- /** Options for `createRegistry`. */
143
- type CreateRegistryOptions = SSRDetectOptions & {
144
- /**
145
- * App-level defaults applied to every `useForm` call. Per-form
146
- * options always win. Omitted is equivalent to `{}`.
147
- */
148
- defaults?: AttaformDefaults;
149
- };
150
- /**
151
- * Create a fresh `AttaformRegistry`. `createAttaform()` calls
152
- * this internally — most consumers never need to call it directly.
153
- * Use it when building a custom plugin that doesn't want the
154
- * `createAttaform` plugin's auto-install behaviour (e.g. test
155
- * harnesses, embedded apps).
156
- */
157
- declare function createRegistry(options?: CreateRegistryOptions): AttaformRegistry;
158
- /**
159
- * Look up the current app's registry from inside a component's
160
- * `setup()` (or any synchronous code on the setup call stack).
161
- *
162
- * Most consumers don't need this — `useForm` and `injectForm`
163
- * call it on your behalf. Reach for it directly when building
164
- * custom integrations that need the raw registry.
165
- *
166
- * Throws:
167
- * - `OutsideSetupError` when called outside a Vue setup context
168
- * (e.g. from an event handler or async callback). Move the call
169
- * into setup, or trigger it from a child component.
170
- * - `RegistryNotInstalledError` when called inside setup but the
171
- * plugin wasn't installed. Add
172
- * `app.use(createAttaform())` to your app entry.
173
- */
174
- declare function useRegistry(): AttaformRegistry;
175
- /**
176
- * Look up a Vue app's registry by `App` reference. Used by
177
- * SSR helpers (`renderAttaformState`, `hydrateAttaformState`) that
178
- * run outside a component setup context.
179
- *
180
- * Throws `RegistryNotInstalledError` when the app hasn't been wired
181
- * with `createAttaform()`.
182
- */
183
- declare function getRegistryFromApp(app: App): AttaformRegistry;
184
-
185
8
  /**
186
9
  * Options for `createAttaform()`.
187
10
  */
@@ -326,7 +149,19 @@ declare function escapeForInlineScript(json: string): string;
326
149
  * Returns the same form API as the typed entry points; see
327
150
  * `UseFormReturnType` for the full surface.
328
151
  */
329
- declare function useAbstractForm<Form extends GenericForm, GetValueFormType extends GenericForm = Form, ReadForm extends GenericForm = Form>(configuration: UseFormConfiguration<Form, GetValueFormType, AbstractSchema<Form, GetValueFormType>, DeepPartial<DefaultValuesShape<Form>>>): UseFormReturnType<Form, GetValueFormType, ReadForm>;
152
+ declare function useAbstractForm<Form extends GenericForm, GetValueFormType extends GenericForm = Form, ReadForm extends GenericForm = Form, K extends FormKey = FormKey>(configuration: UseFormConfiguration<Form, GetValueFormType, AbstractSchema<Form, GetValueFormType>, DefaultValuesInput<Form>, K>,
153
+ /**
154
+ * Internal escape hatch for callers that already hold a registry
155
+ * reference and need to construct a form outside Vue's setup context
156
+ * (e.g. the wizard's lazy noop builder, which runs inside a
157
+ * `computed` re-eval). Passing this skips the strict `useRegistry()`
158
+ * call; everything else (FormStore allocation, registry presence,
159
+ * consumer ref-counting via `onScopeDispose`) goes through the same
160
+ * path eager calls follow. Not part of the public surface.
161
+ */
162
+ options?: {
163
+ readonly registry?: AttaformRegistry;
164
+ }): UseFormReturnType<Form, GetValueFormType, ReadForm, K>;
330
165
 
331
166
  /**
332
167
  * Symbol slot used by custom directive integrations to install an
@@ -370,28 +205,156 @@ declare function isRegisterValue<Value = unknown>(val: unknown): val is Register
370
205
  *
371
206
  * The directive picks the right binding strategy automatically based
372
207
  * on the element's `tagName` and `type`. Registered globally by
373
- * `createAttaform()` most consumers never import it
374
- * directly, but it's exposed for advanced integrations that wire
375
- * directives manually.
208
+ * `createAttaform()`. Most consumers never import it directly, but
209
+ * it's exposed for advanced integrations that wire directives
210
+ * manually.
376
211
  */
377
212
  declare const vRegister: RegisterModelDynamicCustomDirective;
378
213
 
214
+ /**
215
+ * Shared building blocks for Attaform's two devtools surfaces — the Vue
216
+ * DevTools (Chrome-extension) inspector wired up in `./devtools.ts`, and
217
+ * the Nuxt DevTools (overlay) panel wired up via `../../nuxt.ts` +
218
+ * `../pages/_attaform_devtools.vue`.
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.
223
+ */
224
+
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
+ /**
254
+ * Property key on `window` that the Nuxt-side dev plugin attaches the
255
+ * bridge object to. The iframe-mounted overlay panel reads
256
+ * `window.parent[DEVTOOLS_WINDOW_KEY]` to reach the host app's registry.
257
+ *
258
+ * Underscored + namespaced to make accidental collision with consumer
259
+ * globals vanishingly unlikely. Stable across versions — bumping it
260
+ * would silently disconnect older library builds from newer overlay
261
+ * panels in the same browser tab during a library upgrade.
262
+ */
263
+ declare const DEVTOOLS_WINDOW_KEY = "__attaform_devtools__";
264
+ /**
265
+ * Shape of the object the host plugin attaches to `window` in dev mode.
266
+ * The iframe overlay panel reads this to discover the live registry and
267
+ * render its forms.
268
+ *
269
+ * Single-registry assumption: the latest `createAttaform()` install
270
+ * wins. Multi-app pages (rare; typically only seen in micro-frontend
271
+ * setups) will only see one app's forms in the panel. Documented but
272
+ * not actively supported — the alternative (a Set of registries with
273
+ * union-rendering) is a future call if a real consumer hits it.
274
+ */
275
+ interface AttaformDevtoolsBridge {
276
+ registry: AttaformRegistry;
277
+ /**
278
+ * The library version, surfaced in the panel's footer for support /
279
+ * bug-report context. Read from `package.json` at host-plugin init.
280
+ */
281
+ version: string;
282
+ }
283
+ declare global {
284
+ interface Window {
285
+ [DEVTOOLS_WINDOW_KEY]?: AttaformDevtoolsBridge;
286
+ }
287
+ }
288
+
379
289
  /**
380
290
  * Library-default heuristic for `shouldShowErrors`. Drives
381
291
  * `field.showErrors` and `form.meta.showErrors` whenever the consumer
382
292
  * has not configured an override at either the plugin or per-form
383
293
  * level.
384
294
  *
385
- * Reads "show errors after the first submit attempt, OR after the
386
- * user has interacted (`touched`) and made a change (`dirty`)." The
387
- * framework already gates on `errors.length > 0` before invoking the
388
- * predicate, so the body only decides *when* to surface existing
389
- * errors not whether errors exist.
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.
390
353
  *
391
354
  * Public re-export so adopters can compose with this without
392
- * copy-pasting the rule body. A layered predicate that adds a
393
- * special case but otherwise defers to the library default picks up
394
- * future heuristic refinements automatically:
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:
395
358
  *
396
359
  * ```ts
397
360
  * import { defaultShouldShowErrors } from 'attaform'
@@ -566,5 +529,5 @@ declare const PARSE_API_ERRORS_DEFAULTS: {
566
529
  */
567
530
  declare function parseApiErrors(payload: ApiErrorEnvelope | ApiErrorDetails | null | undefined | unknown, options: ParseApiErrorsOptions): ParseApiErrorsResult;
568
531
 
569
- export { AbstractSchema, ApiErrorDetails, ApiErrorEnvelope, AttaformDefaults, CoercionEntry, CoercionRegistry, DEFAULT_SENSITIVE_NAMES, DeepPartial, DefaultValuesShape, FormKey, GenericForm, PARSE_API_ERRORS_DEFAULTS, RegisterValue, ShouldShowErrors, SlimPrimitiveKind, UseFormConfiguration, UseFormReturnType, ValidationError, assignKey, createAttaform, createRegistry, defaultCoercionRules, defaultShouldShowErrors, defineCoercion, escapeForInlineScript, getRegistryFromApp, hydrateAttaformState, isRegisterValue, kAttaformRegistry, parseApiErrors, renderAttaformState, useAbstractForm as useForm, useRegistry, vRegister };
570
- export type { AttaformPluginOptions, AttaformRegistry, ParseApiErrorsOptions, ParseApiErrorsResult, SerializedAttaformState, SerializedFormData };
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 };
533
+ export type { AttaformDevtoolsBridge, AttaformPluginOptions, ParseApiErrorsOptions, ParseApiErrorsResult, SerializedAttaformState };