attaform 0.0.1 → 0.14.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.
- package/LICENSE +21 -0
- package/README.md +142 -2
- package/dist/chunks/devtools.cjs +179 -0
- package/dist/chunks/devtools.cjs.map +1 -0
- package/dist/chunks/devtools.mjs +177 -0
- package/dist/chunks/devtools.mjs.map +1 -0
- package/dist/chunks/indexeddb.cjs +119 -0
- package/dist/chunks/indexeddb.cjs.map +1 -0
- package/dist/chunks/indexeddb.mjs +117 -0
- package/dist/chunks/indexeddb.mjs.map +1 -0
- package/dist/chunks/local-storage.cjs +58 -0
- package/dist/chunks/local-storage.cjs.map +1 -0
- package/dist/chunks/local-storage.mjs +56 -0
- package/dist/chunks/local-storage.mjs.map +1 -0
- package/dist/chunks/session-storage.cjs +58 -0
- package/dist/chunks/session-storage.cjs.map +1 -0
- package/dist/chunks/session-storage.mjs +56 -0
- package/dist/chunks/session-storage.mjs.map +1 -0
- package/dist/index.cjs +173 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +493 -0
- package/dist/index.d.mts +493 -0
- package/dist/index.d.ts +493 -0
- package/dist/index.mjs +141 -0
- package/dist/index.mjs.map +1 -0
- package/dist/nuxt.cjs +97 -0
- package/dist/nuxt.cjs.map +1 -0
- package/dist/nuxt.d.cts +38 -0
- package/dist/nuxt.d.mts +38 -0
- package/dist/nuxt.d.ts +38 -0
- package/dist/nuxt.mjs +94 -0
- package/dist/nuxt.mjs.map +1 -0
- package/dist/runtime/plugins/attaform.cjs +32 -0
- package/dist/runtime/plugins/attaform.cjs.map +1 -0
- package/dist/runtime/plugins/attaform.d.cts +5 -0
- package/dist/runtime/plugins/attaform.d.mts +5 -0
- package/dist/runtime/plugins/attaform.d.ts +5 -0
- package/dist/runtime/plugins/attaform.mjs +30 -0
- package/dist/runtime/plugins/attaform.mjs.map +1 -0
- package/dist/shared/attaform.B5GWYl76.cjs +386 -0
- package/dist/shared/attaform.B5GWYl76.cjs.map +1 -0
- package/dist/shared/attaform.BRTxpA3q.mjs +3283 -0
- package/dist/shared/attaform.BRTxpA3q.mjs.map +1 -0
- package/dist/shared/attaform.BYc9kugA.d.ts +124 -0
- package/dist/shared/attaform.Bubm_slq.cjs +622 -0
- package/dist/shared/attaform.Bubm_slq.cjs.map +1 -0
- package/dist/shared/attaform.BwaYWtMs.d.cts +126 -0
- package/dist/shared/attaform.BwaYWtMs.d.mts +126 -0
- package/dist/shared/attaform.BwaYWtMs.d.ts +126 -0
- package/dist/shared/attaform.CNJO3mME.cjs +3295 -0
- package/dist/shared/attaform.CNJO3mME.cjs.map +1 -0
- package/dist/shared/attaform.CRgix6_n.cjs +796 -0
- package/dist/shared/attaform.CRgix6_n.cjs.map +1 -0
- package/dist/shared/attaform.CXZgUECn.d.cts +124 -0
- package/dist/shared/attaform.CXpzmj38.mjs +617 -0
- package/dist/shared/attaform.CXpzmj38.mjs.map +1 -0
- package/dist/shared/attaform.Cc93zNzD.mjs +83 -0
- package/dist/shared/attaform.Cc93zNzD.mjs.map +1 -0
- package/dist/shared/attaform.DDXrY-1Q.d.cts +2568 -0
- package/dist/shared/attaform.DDXrY-1Q.d.mts +2568 -0
- package/dist/shared/attaform.DDXrY-1Q.d.ts +2568 -0
- package/dist/shared/attaform.DOKOyb3Y.d.mts +124 -0
- package/dist/shared/attaform.DlgKK10S.mjs +789 -0
- package/dist/shared/attaform.DlgKK10S.mjs.map +1 -0
- package/dist/shared/attaform.al_rpt7_.mjs +361 -0
- package/dist/shared/attaform.al_rpt7_.mjs.map +1 -0
- package/dist/shared/attaform.xKWYHMdq.cjs +89 -0
- package/dist/shared/attaform.xKWYHMdq.cjs.map +1 -0
- package/dist/transforms.cjs +11 -0
- package/dist/transforms.cjs.map +1 -0
- package/dist/transforms.d.cts +49 -0
- package/dist/transforms.d.mts +49 -0
- package/dist/transforms.d.ts +49 -0
- package/dist/transforms.mjs +2 -0
- package/dist/transforms.mjs.map +1 -0
- package/dist/vite.cjs +39 -0
- package/dist/vite.cjs.map +1 -0
- package/dist/vite.d.cts +53 -0
- package/dist/vite.d.mts +53 -0
- package/dist/vite.d.ts +53 -0
- package/dist/vite.mjs +37 -0
- package/dist/vite.mjs.map +1 -0
- package/dist/zod-v3.cjs +1511 -0
- package/dist/zod-v3.cjs.map +1 -0
- package/dist/zod-v3.d.cts +164 -0
- package/dist/zod-v3.d.mts +164 -0
- package/dist/zod-v3.d.ts +164 -0
- package/dist/zod-v3.mjs +1504 -0
- package/dist/zod-v3.mjs.map +1 -0
- package/dist/zod.cjs +1548 -0
- package/dist/zod.cjs.map +1 -0
- package/dist/zod.d.cts +67 -0
- package/dist/zod.d.mts +67 -0
- package/dist/zod.d.ts +67 -0
- package/dist/zod.mjs +1541 -0
- package/dist/zod.mjs.map +1 -0
- package/package.json +182 -6
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
import { Plugin, App, InjectionKey } from 'vue';
|
|
2
|
+
import { A as AttaformDefaults, C as CoercionRegistry, S as SlimPrimitiveKind, a as CoercionEntry, 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, V as ValidationError, f as ApiErrorEnvelope, g as ApiErrorDetails } from './shared/attaform.DDXrY-1Q.cjs';
|
|
3
|
+
export { h as ApiErrorEntry, i as CoercionResult, j as CustomDirectiveRegisterAssignerFn, k as DefaultValuesResponse, l as FieldState, m as FieldStateLeaf, n as FieldStateMap, o as FieldStateMapEntry, p as FlatPath, q as FormErrorRecord, r as FormErrorsSurface, s as FormMeta, t as FormStorage, u as FormStorageKind, H as HandleSubmit, v as HistoryConfig, I as IsTuple, M as MetaTrackerValue, N as NestedReadType, w as NestedType, O as OnError, x as OnInvalidSubmitPolicy, y as OnSubmit, P as Path, z as PathKey, B as PendingValidationStatus, E as PersistConfig, J as PersistConfigOptions, K as PersistIncludeMode, L as ROOT_PATH, Q as ROOT_PATH_KEY, T as ReactiveValidationStatus, W as RegisterDirective, X as RegisterFlatPath, Y as RegisterOptions, Z as RegisterSelectModifier, _ as RegisterTextModifier, $ as RegisterTransform, a0 as Segment, a1 as SetValueCallback, a2 as SetValuePayload, a3 as SettledValidationStatus, a4 as SlimRuntimeOf, a5 as SubmitHandler, a6 as Unset, a7 as ValidateOn, a8 as ValidateOnConfig, a9 as ValidationResponse, aa as ValidationResponseWithoutValue, ab as WriteMeta, ac as canonicalizePath, ad as isUnset, ae as parseDottedPath, af as unset } from './shared/attaform.DDXrY-1Q.cjs';
|
|
4
|
+
export { A as AttaformErrorCode, i as injectForm, u as useRegister } from './shared/attaform.CXZgUECn.cjs';
|
|
5
|
+
export { A as AnonPersistError, a as AttaformError, I as InvalidPathError, O as OutsideSetupError, R as RegistryNotInstalledError, b as ReservedFormKeyError, S as SensitivePersistFieldError, c as SubmitErrorHandlerError } from './shared/attaform.BwaYWtMs.cjs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Portable SSR detection. The plugin captures this value at install time and
|
|
9
|
+
* exposes it via the registry so every runtime branch reads a single source
|
|
10
|
+
* of truth instead of sniffing `import.meta.*` (bundler-specific) at each
|
|
11
|
+
* call site.
|
|
12
|
+
*
|
|
13
|
+
* Consumers can override explicitly via `createAttaform({ ssr: true })`;
|
|
14
|
+
* the default heuristic handles the common Node-vs-browser split without
|
|
15
|
+
* relying on any bundler-injected flag.
|
|
16
|
+
*/
|
|
17
|
+
interface SSRDetectOptions {
|
|
18
|
+
override?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Options for `createAttaform()`.
|
|
23
|
+
*/
|
|
24
|
+
type AttaformPluginOptions = SSRDetectOptions & {
|
|
25
|
+
/**
|
|
26
|
+
* Whether to install the Vue DevTools integration. Default `true`.
|
|
27
|
+
* The DevTools peer dependency is loaded lazily — in production
|
|
28
|
+
* builds where it's absent, the import fails silently and no
|
|
29
|
+
* extra bundle is shipped. Pass `false` to skip even attempting
|
|
30
|
+
* the import.
|
|
31
|
+
*/
|
|
32
|
+
devtools?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* App-level defaults applied to every `useForm` call in this app.
|
|
35
|
+
* Per-form options always win. See `AttaformDefaults` for
|
|
36
|
+
* the supported option set and the merge rules.
|
|
37
|
+
*
|
|
38
|
+
* ```ts
|
|
39
|
+
* app.use(
|
|
40
|
+
* createAttaform({
|
|
41
|
+
* defaults: { debounceMs: 100 },
|
|
42
|
+
* })
|
|
43
|
+
* )
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
defaults?: AttaformDefaults;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Create the Vue plugin that installs the form library on a Vue
|
|
50
|
+
* application. Call once per app, then `app.use(...)` the result.
|
|
51
|
+
*
|
|
52
|
+
* ```ts
|
|
53
|
+
* import { createApp } from 'vue'
|
|
54
|
+
* import { createAttaform } from 'attaform'
|
|
55
|
+
*
|
|
56
|
+
* createApp(App)
|
|
57
|
+
* .use(createAttaform())
|
|
58
|
+
* .mount('#app')
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* Under SSR with bare Vue 3, pass `{ ssr: true }` from your server
|
|
62
|
+
* entry. Under Nuxt, install via `attaform/nuxt` instead —
|
|
63
|
+
* the Nuxt module wires both server and client automatically.
|
|
64
|
+
*
|
|
65
|
+
* Installing more than once on the same app is a no-op (the second
|
|
66
|
+
* call logs a dev-mode warning).
|
|
67
|
+
*/
|
|
68
|
+
declare function createAttaform(options?: AttaformPluginOptions): Plugin;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Schema-driven coercion of user-typed DOM values at the v-register
|
|
72
|
+
* directive layer. When the slim schema declares a numeric or
|
|
73
|
+
* boolean type at a path, the directive coerces incoming string
|
|
74
|
+
* values (`'25'` → `25`, `'true'` → `true`) before the slim-primitive
|
|
75
|
+
* gate sees the write — making the schema authoritative for storage
|
|
76
|
+
* shape and freeing consumers from sprinkling `.number` modifiers
|
|
77
|
+
* across templates.
|
|
78
|
+
*
|
|
79
|
+
* Coercion is consumer-extensible: a `CoercionRegistry` is just an
|
|
80
|
+
* `Array<CoercionEntry>` keyed at config time by `(input, output)`
|
|
81
|
+
* `SlimPrimitiveKind` literals. The library ships
|
|
82
|
+
* `defaultCoercionRules` (string→number, string→boolean) and
|
|
83
|
+
* `defineCoercion` for type-narrowed authoring; consumers spread the
|
|
84
|
+
* defaults to extend or supply their own array to replace.
|
|
85
|
+
*
|
|
86
|
+
* Coercion applies ONLY to user-typed DOM values flowing through
|
|
87
|
+
* the directive's assigner. Programmatic writes (`form.setValue`,
|
|
88
|
+
* `setValueWithInternalPath`) bypass coercion — they're authoritative
|
|
89
|
+
* writes whose strict typing is on the caller. This mirrors the
|
|
90
|
+
* `transforms` pipeline's user-input-only contract.
|
|
91
|
+
*/
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Type-narrowing helper for authoring entries. At runtime it's
|
|
95
|
+
* identity; at compile time it preserves the `input` / `output`
|
|
96
|
+
* literal types so `transform`'s parameter is narrowed to the
|
|
97
|
+
* runtime type instead of widening to `SlimRuntimeOf<SlimPrimitiveKind>`.
|
|
98
|
+
*
|
|
99
|
+
* Without this helper, authoring `{ input: 'string', output:
|
|
100
|
+
* 'number', transform: (s) => ... }` against the broader
|
|
101
|
+
* `CoercionEntry` widens `s` to `string | number | boolean | ...`,
|
|
102
|
+
* forcing a cast in every transform body. `defineCoercion` is the
|
|
103
|
+
* opaque-free idiom.
|
|
104
|
+
*/
|
|
105
|
+
declare function defineCoercion<I extends SlimPrimitiveKind, O extends SlimPrimitiveKind>(entry: CoercionEntry<I, O>): CoercionEntry<I, O>;
|
|
106
|
+
/**
|
|
107
|
+
* The library's built-in registry. Two cells: string→number and
|
|
108
|
+
* string→boolean. Re-exported so consumers can spread it when
|
|
109
|
+
* supplying a custom registry that extends defaults.
|
|
110
|
+
*/
|
|
111
|
+
declare const defaultCoercionRules: CoercionRegistry;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Per-Vue-app container for all form state instances. Each
|
|
115
|
+
* `app.use(createAttaform())` call gets its own registry,
|
|
116
|
+
* so the library runs under bare Vue 3 + SSR (via
|
|
117
|
+
* `@vue/server-renderer`) and Nuxt with the same code path.
|
|
118
|
+
*
|
|
119
|
+
* Each form's state lives in `forms: Map<FormKey, FormStore<GenericForm>>`.
|
|
120
|
+
* The type relaxation at storage time is necessary because different
|
|
121
|
+
* forms in the same app have different `Form` generics; callers recover
|
|
122
|
+
* the specific form type via `useForm`'s overloads.
|
|
123
|
+
*/
|
|
124
|
+
/**
|
|
125
|
+
* Serialised snapshot of one form's state, captured by
|
|
126
|
+
* `renderAttaformState` for SSR and replayed by
|
|
127
|
+
* `hydrateAttaformState` on the client. Round-trips through
|
|
128
|
+
* JSON-safe tuples; field references are intentionally omitted
|
|
129
|
+
* (DOM nodes don't survive serialisation).
|
|
130
|
+
*/
|
|
131
|
+
type SerializedFormData = {
|
|
132
|
+
/** The form's value at snapshot time. */
|
|
133
|
+
readonly form: unknown;
|
|
134
|
+
/**
|
|
135
|
+
* Errors produced by the schema at snapshot time. Replayed into
|
|
136
|
+
* the client form's error state at hydration; cleared on
|
|
137
|
+
* successful re-validation client-side.
|
|
138
|
+
*/
|
|
139
|
+
readonly schemaErrors: ReadonlyArray<readonly [string, unknown]>;
|
|
140
|
+
/**
|
|
141
|
+
* Errors set explicitly via `setFieldErrors` / `addFieldErrors`
|
|
142
|
+
* (typically from a server response parsed via `parseApiErrors`)
|
|
143
|
+
* at snapshot time. Replayed at hydration; persists across
|
|
144
|
+
* client-side re-validation.
|
|
145
|
+
*/
|
|
146
|
+
readonly userErrors: ReadonlyArray<readonly [string, unknown]>;
|
|
147
|
+
/** Per-field metadata (timestamps, raw values, connection flags) captured at snapshot time. */
|
|
148
|
+
readonly fields: ReadonlyArray<readonly [string, unknown]>;
|
|
149
|
+
/**
|
|
150
|
+
* Path keys that were in the form's `blankPaths` set at
|
|
151
|
+
* snapshot time. Round-trips the "displayed empty" UI state across
|
|
152
|
+
* the SSR boundary — without it, the client briefly renders
|
|
153
|
+
* `String(slim-default)` (e.g. `'0'`) for fields the server
|
|
154
|
+
* rendered as blank. Optional in the wire format so older payload
|
|
155
|
+
* shapes deserialise cleanly.
|
|
156
|
+
*/
|
|
157
|
+
readonly blankPaths?: ReadonlyArray<string>;
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* The library's per-Vue-app container. One `AttaformRegistry` is
|
|
161
|
+
* created per `app.use(createAttaform())` call.
|
|
162
|
+
*
|
|
163
|
+
* Most consumers never touch this directly — `useForm` and
|
|
164
|
+
* `injectForm` reach the registry on your behalf. Access it
|
|
165
|
+
* explicitly only when wiring SSR or a custom plugin integration.
|
|
166
|
+
*/
|
|
167
|
+
type AttaformRegistry = {
|
|
168
|
+
/** `true` while running on the server during SSR; `false` on the client. */
|
|
169
|
+
readonly isSSR: boolean;
|
|
170
|
+
/** App-level defaults applied to every `useForm` call. */
|
|
171
|
+
readonly defaults: AttaformDefaults;
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* The Vue `InjectionKey` under which the registry is provided on the
|
|
175
|
+
* app. Most consumers never need this — `useForm` and
|
|
176
|
+
* `injectForm` resolve the registry automatically.
|
|
177
|
+
*/
|
|
178
|
+
declare const kAttaformRegistry: InjectionKey<AttaformRegistry>;
|
|
179
|
+
declare module 'vue' {
|
|
180
|
+
interface App {
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/** Options for `createRegistry`. */
|
|
184
|
+
type CreateRegistryOptions = SSRDetectOptions & {
|
|
185
|
+
/**
|
|
186
|
+
* App-level defaults applied to every `useForm` call. Per-form
|
|
187
|
+
* options always win. Omitted is equivalent to `{}`.
|
|
188
|
+
*/
|
|
189
|
+
defaults?: AttaformDefaults;
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* Create a fresh `AttaformRegistry`. `createAttaform()` calls
|
|
193
|
+
* this internally — most consumers never need to call it directly.
|
|
194
|
+
* Use it when building a custom plugin that doesn't want the
|
|
195
|
+
* `createAttaform` plugin's auto-install behaviour (e.g. test
|
|
196
|
+
* harnesses, embedded apps).
|
|
197
|
+
*/
|
|
198
|
+
declare function createRegistry(options?: CreateRegistryOptions): AttaformRegistry;
|
|
199
|
+
/**
|
|
200
|
+
* Look up the current app's registry from inside a component's
|
|
201
|
+
* `setup()` (or any synchronous code on the setup call stack).
|
|
202
|
+
*
|
|
203
|
+
* Most consumers don't need this — `useForm` and `injectForm`
|
|
204
|
+
* call it on your behalf. Reach for it directly when building
|
|
205
|
+
* custom integrations that need the raw registry.
|
|
206
|
+
*
|
|
207
|
+
* Throws:
|
|
208
|
+
* - `OutsideSetupError` when called outside a Vue setup context
|
|
209
|
+
* (e.g. from an event handler or async callback). Move the call
|
|
210
|
+
* into setup, or trigger it from a child component.
|
|
211
|
+
* - `RegistryNotInstalledError` when called inside setup but the
|
|
212
|
+
* plugin wasn't installed. Add
|
|
213
|
+
* `app.use(createAttaform())` to your app entry.
|
|
214
|
+
*/
|
|
215
|
+
declare function useRegistry(): AttaformRegistry;
|
|
216
|
+
/**
|
|
217
|
+
* Look up a Vue app's registry by `App` reference. Used by
|
|
218
|
+
* SSR helpers (`renderAttaformState`, `hydrateAttaformState`) that
|
|
219
|
+
* run outside a component setup context.
|
|
220
|
+
*
|
|
221
|
+
* Throws `RegistryNotInstalledError` when the app hasn't been wired
|
|
222
|
+
* with `createAttaform()`.
|
|
223
|
+
*/
|
|
224
|
+
declare function getRegistryFromApp(app: App): AttaformRegistry;
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Serialised snapshot of every form in a Vue app, produced by
|
|
228
|
+
* `renderAttaformState` and consumed by `hydrateAttaformState`.
|
|
229
|
+
*
|
|
230
|
+
* JSON-safe — pass to `JSON.stringify`, `devalue`, or any other
|
|
231
|
+
* serialiser before embedding in your SSR payload.
|
|
232
|
+
*/
|
|
233
|
+
type SerializedAttaformState = {
|
|
234
|
+
/** Tuples of `[formKey, snapshot]` for every form in the app. */
|
|
235
|
+
readonly forms: ReadonlyArray<readonly [FormKey, SerializedFormData]>;
|
|
236
|
+
};
|
|
237
|
+
/**
|
|
238
|
+
* Snapshot every form on a Vue app for SSR. Call from your server
|
|
239
|
+
* entry after rendering the app:
|
|
240
|
+
*
|
|
241
|
+
* ```ts
|
|
242
|
+
* import { renderToString } from '@vue/server-renderer'
|
|
243
|
+
* import { renderAttaformState, escapeForInlineScript } from 'attaform'
|
|
244
|
+
*
|
|
245
|
+
* const html = await renderToString(app)
|
|
246
|
+
* const state = renderAttaformState(app)
|
|
247
|
+
* const payload = escapeForInlineScript(JSON.stringify(state))
|
|
248
|
+
*
|
|
249
|
+
* return `
|
|
250
|
+
* ${html}
|
|
251
|
+
* <script>window.__ATTAFORM_STATE__ = ${payload}</script>
|
|
252
|
+
* `
|
|
253
|
+
* ```
|
|
254
|
+
*
|
|
255
|
+
* Pair with `hydrateAttaformState` on the client to restore the
|
|
256
|
+
* forms in their server-rendered state. Nuxt users don't need this —
|
|
257
|
+
* `attaform/nuxt` wires SSR automatically.
|
|
258
|
+
*/
|
|
259
|
+
declare function renderAttaformState(app: App): SerializedAttaformState;
|
|
260
|
+
/**
|
|
261
|
+
* Restore forms from a server-rendered snapshot on the client. Call
|
|
262
|
+
* from your client entry before mounting:
|
|
263
|
+
*
|
|
264
|
+
* ```ts
|
|
265
|
+
* import { createApp } from 'vue'
|
|
266
|
+
* import { createAttaform, hydrateAttaformState } from 'attaform'
|
|
267
|
+
*
|
|
268
|
+
* const app = createApp(App).use(createAttaform())
|
|
269
|
+
* hydrateAttaformState(app, window.__ATTAFORM_STATE__)
|
|
270
|
+
* app.mount('#app')
|
|
271
|
+
* ```
|
|
272
|
+
*
|
|
273
|
+
* The next `useForm({ key })` call for each serialised form picks up
|
|
274
|
+
* the snapshot transparently — no further action is required.
|
|
275
|
+
*/
|
|
276
|
+
declare function hydrateAttaformState(app: App, payload: SerializedAttaformState): void;
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Escape a JSON string so it's safe to embed inside an inline
|
|
280
|
+
* `<script>` tag during SSR. Plain `JSON.stringify` is not safe — a
|
|
281
|
+
* form value containing the literal substring `</script>` would
|
|
282
|
+
* break out of the script tag.
|
|
283
|
+
*
|
|
284
|
+
* ```ts
|
|
285
|
+
* const payload = escapeForInlineScript(JSON.stringify(renderAttaformState(app)))
|
|
286
|
+
* // `<script>window.__ATTAFORM_STATE__ = ${payload}</script>` is safe.
|
|
287
|
+
* ```
|
|
288
|
+
*
|
|
289
|
+
* Output remains valid JSON — `JSON.parse` round-trips back to the
|
|
290
|
+
* original value on the client.
|
|
291
|
+
*/
|
|
292
|
+
declare function escapeForInlineScript(json: string): string;
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Schema-agnostic `useForm`. Accepts any object that implements
|
|
296
|
+
* `AbstractSchema` — useful when integrating a custom schema
|
|
297
|
+
* adapter or a third-party validation library.
|
|
298
|
+
*
|
|
299
|
+
* ```ts
|
|
300
|
+
* import { useForm } from 'attaform'
|
|
301
|
+
*
|
|
302
|
+
* const form = useForm({
|
|
303
|
+
* schema: myCustomAdapter,
|
|
304
|
+
* defaultValues: { name: '' },
|
|
305
|
+
* })
|
|
306
|
+
* ```
|
|
307
|
+
*
|
|
308
|
+
* Most consumers prefer a typed entry point — e.g.
|
|
309
|
+
* `attaform/zod` (v4) or `attaform/zod-v3` —
|
|
310
|
+
* which wrap the underlying library's schema with the matching
|
|
311
|
+
* adapter automatically.
|
|
312
|
+
*
|
|
313
|
+
* Returns the same form API as the typed entry points; see
|
|
314
|
+
* `UseFormReturnType` for the full surface.
|
|
315
|
+
*/
|
|
316
|
+
declare function useAbstractForm<Form extends GenericForm, GetValueFormType extends GenericForm = Form>(configuration: UseFormConfiguration<Form, GetValueFormType, AbstractSchema<Form, GetValueFormType>, DeepPartial<DefaultValuesShape<Form>>>): UseFormReturnType<Form, GetValueFormType>;
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Symbol slot used by custom directive integrations to install an
|
|
320
|
+
* assigner on the bound element. Read by the v-register directive
|
|
321
|
+
* when a DOM event fires:
|
|
322
|
+
*
|
|
323
|
+
* ```ts
|
|
324
|
+
* import { assignKey } from 'attaform'
|
|
325
|
+
* el[assignKey] = (value) => myCustomWriter(value)
|
|
326
|
+
* ```
|
|
327
|
+
*
|
|
328
|
+
* Most consumers never need this — the built-in directives wire
|
|
329
|
+
* default assigners for text inputs, checkboxes, radios, and selects.
|
|
330
|
+
*/
|
|
331
|
+
declare const assignKey: unique symbol;
|
|
332
|
+
/**
|
|
333
|
+
* Type guard for a `RegisterValue`. Returns `true` when `val` looks
|
|
334
|
+
* like the object returned from `form.register(path)`.
|
|
335
|
+
*
|
|
336
|
+
* ```ts
|
|
337
|
+
* if (isRegisterValue(slotValue)) {
|
|
338
|
+
* // slotValue.innerRef is now a Ref<unknown>
|
|
339
|
+
* }
|
|
340
|
+
* ```
|
|
341
|
+
*
|
|
342
|
+
* Useful when building wrapper components that accept either a
|
|
343
|
+
* `RegisterValue` or a plain ref via the same prop.
|
|
344
|
+
*/
|
|
345
|
+
declare function isRegisterValue<Value = unknown>(val: unknown): val is RegisterValue<Value>;
|
|
346
|
+
/**
|
|
347
|
+
* The `v-register` directive. Bind a form field to a native input,
|
|
348
|
+
* select, textarea, checkbox, or radio:
|
|
349
|
+
*
|
|
350
|
+
* ```vue
|
|
351
|
+
* <input v-register="form.register('email')" />
|
|
352
|
+
* <select v-register="form.register('country')">
|
|
353
|
+
* <option value="us">US</option>
|
|
354
|
+
* <option value="uk">UK</option>
|
|
355
|
+
* </select>
|
|
356
|
+
* ```
|
|
357
|
+
*
|
|
358
|
+
* The directive picks the right binding strategy automatically based
|
|
359
|
+
* on the element's `tagName` and `type`. Registered globally by
|
|
360
|
+
* `createAttaform()` — most consumers never import it
|
|
361
|
+
* directly, but it's exposed for advanced integrations that wire
|
|
362
|
+
* directives manually.
|
|
363
|
+
*/
|
|
364
|
+
declare const vRegister: RegisterModelDynamicCustomDirective;
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Result of `parseApiErrors`. Branch on `ok` to handle the two cases:
|
|
368
|
+
*
|
|
369
|
+
* ```ts
|
|
370
|
+
* const result = parseApiErrors(payload, { formKey: form.key })
|
|
371
|
+
* if (result.ok) {
|
|
372
|
+
* form.setFieldErrors(result.errors)
|
|
373
|
+
* } else {
|
|
374
|
+
* console.warn('Bad error payload:', result.rejected)
|
|
375
|
+
* }
|
|
376
|
+
* ```
|
|
377
|
+
*
|
|
378
|
+
* `ok: true` means the payload was recognised — `errors` may still be
|
|
379
|
+
* empty if the payload was valid but had no actual errors.
|
|
380
|
+
* `ok: false` means the payload didn't match a known shape; `rejected`
|
|
381
|
+
* carries a one-line description of why.
|
|
382
|
+
*/
|
|
383
|
+
type ParseApiErrorsResult = {
|
|
384
|
+
/** `true` when the payload was recognised; `false` when the shape was unfamiliar. */
|
|
385
|
+
readonly ok: boolean;
|
|
386
|
+
/** Errors extracted from the payload. May be empty even when `ok: true`. */
|
|
387
|
+
readonly errors: ValidationError[];
|
|
388
|
+
/** When `ok: false`, a one-line description of why the payload was rejected. */
|
|
389
|
+
readonly rejected?: string;
|
|
390
|
+
};
|
|
391
|
+
/**
|
|
392
|
+
* Options for `parseApiErrors`. The size caps protect against
|
|
393
|
+
* misbehaving or hostile servers — exceeding any cap causes the
|
|
394
|
+
* parser to reject the payload wholesale rather than partially apply.
|
|
395
|
+
*/
|
|
396
|
+
type ParseApiErrorsOptions = {
|
|
397
|
+
/**
|
|
398
|
+
* The form's identifier — pass `form.key`. Stamped on every
|
|
399
|
+
* produced `ValidationError` so errors route to the right form.
|
|
400
|
+
*/
|
|
401
|
+
readonly formKey: FormKey;
|
|
402
|
+
/**
|
|
403
|
+
* Code stamped on `ValidationError`s synthesized from bare-string
|
|
404
|
+
* entries (the Rails / DRF / Laravel `{ field: ["msg"] }` shape).
|
|
405
|
+
* Default `'api:unknown'`. Pick something more specific
|
|
406
|
+
* (`'api:server-validation'`, `'myapp:legacy'`, …) when you know
|
|
407
|
+
* the source.
|
|
408
|
+
*
|
|
409
|
+
* Structured `{ message, code }` entries forward their `code`
|
|
410
|
+
* verbatim and ignore this option.
|
|
411
|
+
*/
|
|
412
|
+
readonly defaultCode?: string;
|
|
413
|
+
/**
|
|
414
|
+
* Maximum number of distinct keys to accept. Default `1000`.
|
|
415
|
+
* Raise for trusted backends that legitimately produce more.
|
|
416
|
+
*/
|
|
417
|
+
readonly maxEntries?: number;
|
|
418
|
+
/**
|
|
419
|
+
* Maximum number of path segments per key. Default `32`. Keys
|
|
420
|
+
* deeper than this are dropped (the rest of the payload still
|
|
421
|
+
* applies if it stays under the other caps).
|
|
422
|
+
*/
|
|
423
|
+
readonly maxPathDepth?: number;
|
|
424
|
+
/**
|
|
425
|
+
* Maximum total path segments summed across every accepted key.
|
|
426
|
+
* Default `10000`. Bounds the worst-case traversal cost.
|
|
427
|
+
*/
|
|
428
|
+
readonly maxTotalSegments?: number;
|
|
429
|
+
};
|
|
430
|
+
/**
|
|
431
|
+
* Default size caps + default fallback code used by `parseApiErrors`.
|
|
432
|
+
* Conservative; pass larger values (or a more specific code) via the
|
|
433
|
+
* options bag for trusted-backend integrations.
|
|
434
|
+
*/
|
|
435
|
+
declare const PARSE_API_ERRORS_DEFAULTS: {
|
|
436
|
+
readonly maxEntries: 1000;
|
|
437
|
+
readonly maxPathDepth: 32;
|
|
438
|
+
readonly maxTotalSegments: 10000;
|
|
439
|
+
readonly defaultCode: "api:unknown";
|
|
440
|
+
};
|
|
441
|
+
/**
|
|
442
|
+
* Normalise a server-side validation error payload into
|
|
443
|
+
* `ValidationError[]`. Pair with `form.setFieldErrors` /
|
|
444
|
+
* `form.addFieldErrors` to surface server errors on the form:
|
|
445
|
+
*
|
|
446
|
+
* ```ts
|
|
447
|
+
* const response = await fetch('/api/signup', { … })
|
|
448
|
+
* if (!response.ok) {
|
|
449
|
+
* const payload = await response.json()
|
|
450
|
+
* const result = parseApiErrors(payload, { formKey: form.key })
|
|
451
|
+
* if (result.ok) form.setFieldErrors(result.errors)
|
|
452
|
+
* }
|
|
453
|
+
* ```
|
|
454
|
+
*
|
|
455
|
+
* Recognised payload shapes:
|
|
456
|
+
*
|
|
457
|
+
* - Wrapped envelope:
|
|
458
|
+
* `{ error: { details: { email: { message: 'taken', code: 'api:duplicate-email' } } } }`
|
|
459
|
+
* - Unwrapped envelope:
|
|
460
|
+
* `{ details: { email: { message: 'taken', code: 'api:duplicate-email' } } }`
|
|
461
|
+
* - Raw details record:
|
|
462
|
+
* `{ email: { message: 'taken', code: 'api:duplicate-email' } }`
|
|
463
|
+
* - **Bare-string Rails / DRF / Laravel shape:**
|
|
464
|
+
* `{ email: ['Email already taken.'], username: 'too short' }`
|
|
465
|
+
* - `null` / `undefined` — returns `{ ok: true, errors: [] }`
|
|
466
|
+
*
|
|
467
|
+
* Two entry shapes are accepted:
|
|
468
|
+
*
|
|
469
|
+
* 1. **Structured** — `{ message: string, code: string }`. The `code`
|
|
470
|
+
* is forwarded verbatim onto the produced `ValidationError`.
|
|
471
|
+
* 2. **Bare-string** — a plain string. Synthesized into
|
|
472
|
+
* `{ message: <string>, code: <defaultCode> }` where `defaultCode`
|
|
473
|
+
* comes from `options.defaultCode` (default `'api:unknown'`).
|
|
474
|
+
* Useful for the Rails / Django REST Framework / FastAPI / Laravel
|
|
475
|
+
* JSON shape that doesn't carry a per-field code.
|
|
476
|
+
*
|
|
477
|
+
* Each detail key's value can be a single entry, an array, or a mix
|
|
478
|
+
* of structured and bare-string entries; arrays expand into one
|
|
479
|
+
* `ValidationError` per entry. Pick a prefix on the server (`api:`,
|
|
480
|
+
* `auth:`, etc.) and stay consistent so error renderers can branch
|
|
481
|
+
* on `code` — or rely on `defaultCode` when the wire shape is
|
|
482
|
+
* message-only.
|
|
483
|
+
*
|
|
484
|
+
* Dotted keys (`"address.line1"`) are split into structured paths
|
|
485
|
+
* automatically. Use a custom server response shape outside these
|
|
486
|
+
* patterns? Build the `ValidationError[]` array yourself and pass
|
|
487
|
+
* it to `setFieldErrors` directly — `parseApiErrors` is just a
|
|
488
|
+
* convenience for the common shapes.
|
|
489
|
+
*/
|
|
490
|
+
declare function parseApiErrors(payload: ApiErrorEnvelope | ApiErrorDetails | null | undefined | unknown, options: ParseApiErrorsOptions): ParseApiErrorsResult;
|
|
491
|
+
|
|
492
|
+
export { AbstractSchema, ApiErrorDetails, ApiErrorEnvelope, AttaformDefaults, CoercionEntry, CoercionRegistry, DeepPartial, DefaultValuesShape, FormKey, GenericForm, PARSE_API_ERRORS_DEFAULTS, RegisterValue, SlimPrimitiveKind, UseFormConfiguration, UseFormReturnType, ValidationError, assignKey, createAttaform, createRegistry, defaultCoercionRules, defineCoercion, escapeForInlineScript, getRegistryFromApp, hydrateAttaformState, isRegisterValue, kAttaformRegistry, parseApiErrors, renderAttaformState, useAbstractForm as useForm, useRegistry, vRegister };
|
|
493
|
+
export type { AttaformPluginOptions, AttaformRegistry, ParseApiErrorsOptions, ParseApiErrorsResult, SerializedAttaformState, SerializedFormData };
|