attaform 0.28.0 → 0.30.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/dist/abstract.d.mts +5 -5
- package/dist/abstract.mjs +4 -4
- package/dist/dev/abstract.mjs +4 -4
- package/dist/dev/chunks/dev-key-collision-warnings.mjs +47 -8
- package/dist/dev/chunks/devtools.mjs +1 -1
- package/dist/dev/directive.mjs +1 -1
- package/dist/dev/history.mjs +1 -2
- package/dist/dev/index.mjs +5 -5
- package/dist/dev/runtime/plugins/attaform.mjs +3 -3
- package/dist/dev/shared/{attaform-dev.oYrkP9dj.mjs → attaform-dev.BK6Ge2qR.mjs} +80 -341
- package/dist/dev/shared/{attaform-dev.Dr2YzS5B.mjs → attaform-dev.BQc5QV3Y.mjs} +11 -19
- package/dist/dev/shared/{attaform-dev.BR8u0YOZ.mjs → attaform-dev.BcNtyOmf.mjs} +4 -4
- package/dist/{shared/attaform.h0i6LijT.mjs → dev/shared/attaform-dev.BcYT6tFh.mjs} +152 -20
- package/dist/dev/shared/{attaform-dev.6qxD-Yqo.mjs → attaform-dev.BisFstxh.mjs} +227 -88
- package/dist/dev/shared/{attaform-dev.WmuhD_CZ.mjs → attaform-dev.CQ2rJiAX.mjs} +833 -656
- package/dist/dev/shared/{attaform-dev.P7cseiEG.mjs → attaform-dev.D6CLvaQT.mjs} +20 -11
- package/dist/dev/shared/{attaform-dev.D2I8e_Mm.mjs → attaform-dev.DTrHonpc.mjs} +225 -238
- package/dist/dev/shared/{attaform-dev.BXE2cr0L.mjs → attaform-dev.DUZckyV8.mjs} +1 -3
- package/dist/dev/zod-v3.mjs +6 -6
- package/dist/dev/zod-v4.mjs +6 -6
- package/dist/dev/zod.mjs +5 -5
- package/dist/directive.d.mts +2 -2
- package/dist/directive.mjs +1 -1
- package/dist/history.d.mts +1 -1
- package/dist/history.mjs +1 -2
- package/dist/index.d.mts +9 -7
- package/dist/index.mjs +5 -5
- package/dist/nuxt.d.mts +11 -20
- package/dist/nuxt.mjs +0 -1
- package/dist/runtime/plugins/attaform.mjs +3 -3
- package/dist/shared/{attaform.uLbun31C.mjs → attaform.B3resMQ1.mjs} +19 -11
- package/dist/shared/{attaform.Cs2Rbdlo.mjs → attaform.BK0cgu2K.mjs} +123 -139
- package/dist/{dev/shared/attaform-dev.By4PRjt0.mjs → shared/attaform.BRDDQeHV.mjs} +136 -26
- package/dist/shared/{attaform.NLmerepI.mjs → attaform.BXneBxEj.mjs} +71 -334
- package/dist/shared/{attaform.D6DCnnQu.mjs → attaform.ByWP6kSx.mjs} +4 -4
- package/dist/shared/{attaform.CJP3RkGa.mjs → attaform.CJ8DpRGj.mjs} +223 -238
- package/dist/shared/{attaform.C09fpdLS.mjs → attaform.CUfrMK7t.mjs} +227 -88
- package/dist/shared/{attaform.wfY8XFvm.mjs → attaform.CdZIEf7_.mjs} +11 -19
- package/dist/shared/{attaform.JfSWdEcb.d.mts → attaform.Clz0ArIi.d.mts} +1 -1
- package/dist/shared/{attaform.B8k8JrmK.mjs → attaform.DNJOWjQw.mjs} +826 -612
- package/dist/shared/{attaform.B4VkS43-.d.mts → attaform.Rc9u4Vfw.d.mts} +1 -1
- package/dist/shared/{attaform.D5V4tZWq.d.mts → attaform.UGyOK83T.d.mts} +200 -218
- package/dist/shared/{attaform.Dq4vWxuG.mjs → attaform.WiGdIqgx.mjs} +1 -3
- package/dist/shared/{attaform.Cgu-hdSb.d.mts → attaform.bKLzMU0o.d.mts} +321 -523
- package/dist/transforms.d.mts +0 -22
- package/dist/transforms.mjs +1 -1
- package/dist/vite.d.mts +13 -4
- package/dist/vite.mjs +8 -2
- package/dist/zod-v3.d.mts +6 -4
- package/dist/zod-v3.mjs +6 -6
- package/dist/zod-v4.d.mts +18 -31
- package/dist/zod-v4.mjs +6 -6
- package/dist/zod.d.mts +4 -4
- package/dist/zod.mjs +5 -5
- package/package.json +19 -15
- package/skills/attaform/SKILL.md +6 -3
- package/skills/attaform/references/custom-components.md +8 -3
- package/skills/attaform/references/errors.md +8 -3
- package/skills/attaform/references/saving.md +77 -0
- package/skills/attaform/references/wizards.md +10 -8
- package/dist/chunks/fingerprint.mjs +0 -183
- package/dist/chunks/fingerprint2.mjs +0 -159
- package/dist/dev/chunks/fingerprint.mjs +0 -183
- package/dist/dev/chunks/fingerprint2.mjs +0 -159
- package/dist/dev/shared/attaform-dev.BJGA_UOS.mjs +0 -36
- package/dist/shared/attaform.BJGA_UOS.mjs +0 -36
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { G as GenericForm,
|
|
2
|
+
import { G as GenericForm, l as FlatPath, s as NestedType, F as FormKey, U as UseFormConfiguration, A as AbstractSchema, aq as AcceptableDefaults, ac as ValidateOnConfig, a as UseFormReturnType } from './attaform.bKLzMU0o.mjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* The Zod v4 schema roots `useForm` accepts. A fixed-shape
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { G as GenericForm, F as FormKey, a as UseFormReturnType,
|
|
1
|
+
import { G as GenericForm, F as FormKey, a as UseFormReturnType, _ as RegisterValue, v as Path, w as PathKey, ar as DisplayCtx, as as GetDisplayState, at as DisplayMachine, au as FieldRecord, av as ErrorCell, ad as ValidationError, aw as OriginalsRecord, A as AbstractSchema, ah as WriteMeta, d as DeepPartial, ai as WriteShape, ax as AttaformDomBinding, ay as TransformAbortHolder } from './attaform.bKLzMU0o.mjs';
|
|
2
2
|
import { Ref, ComputedRef, ShallowRef, App, InjectionKey, Plugin } from 'vue';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -121,6 +121,22 @@ declare function injectForm<Form extends GenericForm, GetValueFormType extends G
|
|
|
121
121
|
* composable's `onMounted` warn fires once per instance to surface
|
|
122
122
|
* the misuse case at runtime.
|
|
123
123
|
*
|
|
124
|
+
* A wrapper meant to work both ways says so with
|
|
125
|
+
* `useRegister({ optional: true })`, which silences the unbound
|
|
126
|
+
* diagnostic and changes nothing else. Without it the diagnostic still
|
|
127
|
+
* earns its keep: a single-mode wrapper whose parent forgot
|
|
128
|
+
* `v-register` renders a field that looks fine and stores nothing.
|
|
129
|
+
*
|
|
130
|
+
* `rv?.isBound` is the test for "did a parent bind me", and it is the
|
|
131
|
+
* one to reach for: script-setup code holds the Proxy, which is an
|
|
132
|
+
* object and so always truthy, while a template holds the unwrapped
|
|
133
|
+
* ref, which is `undefined` when unbound. Those two disagree, and
|
|
134
|
+
* `isBound` reads the same on both. A dual-mode wrapper (one used
|
|
135
|
+
* both `v-register`-bound and plain `v-model`-bound) branches on it,
|
|
136
|
+
* and does not need to duplicate its element to do so: the
|
|
137
|
+
* compile-time value injection keeps an author-written `:value` /
|
|
138
|
+
* `:checked` as its unbound leg, so one element serves both (#620).
|
|
139
|
+
*
|
|
124
140
|
* Diagnostic: in dev mode, a single `console.warn` fires per instance
|
|
125
141
|
* at `onMounted` if the captured value is still `undefined` — by then
|
|
126
142
|
* the parent has had its full mount lifecycle to bind, so a missing
|
|
@@ -149,12 +165,69 @@ declare function injectForm<Form extends GenericForm, GetValueFormType extends G
|
|
|
149
165
|
* the hybrid's only `.value`. Older code that read `rv.value?.path`
|
|
150
166
|
* keeps working; new code can write `rv.path` directly.
|
|
151
167
|
*/
|
|
152
|
-
type UseRegisterReturn<V = unknown> = RegisterValue<V> & Ref<RegisterValue<V> | undefined
|
|
153
|
-
|
|
168
|
+
type UseRegisterReturn<V = unknown> = RegisterValue<V> & Ref<RegisterValue<V> | undefined> & {
|
|
169
|
+
/**
|
|
170
|
+
* Whether a parent has actually bound this wrapper. `false` until a
|
|
171
|
+
* `RegisterValue` lands, `true` once one has, and reactive in both
|
|
172
|
+
* directions: a parent that binds on a later render flips it, and
|
|
173
|
+
* so does one that stops binding.
|
|
174
|
+
*
|
|
175
|
+
* This is the test a dual-mode wrapper reaches for, and the reason
|
|
176
|
+
* it exists as a field rather than a truthiness check on the
|
|
177
|
+
* return: the composable cannot answer "is this bound" at setup
|
|
178
|
+
* time. A parent is free to bind on a later render
|
|
179
|
+
* (`v-register="ready ? form.register('x') : undefined"`), and
|
|
180
|
+
* `useRegister` has to keep serving that, so it can never return a
|
|
181
|
+
* plain `undefined` for "unbound right now". Script-setup code
|
|
182
|
+
* reading `rv` directly sees the hybrid Proxy, which is an object
|
|
183
|
+
* and therefore always truthy; a template sees the unwrapped ref,
|
|
184
|
+
* which IS `undefined` when unbound. `rv?.isBound` reads the same
|
|
185
|
+
* on both sides (#620).
|
|
186
|
+
*/
|
|
187
|
+
readonly isBound: boolean;
|
|
188
|
+
};
|
|
189
|
+
/**
|
|
190
|
+
* Options for `useRegister`.
|
|
191
|
+
*/
|
|
192
|
+
type UseRegisterOptions = {
|
|
193
|
+
/**
|
|
194
|
+
* Declares that this wrapper is used both with and without a form, so
|
|
195
|
+
* a render with no parent binding is intended rather than a mistake.
|
|
196
|
+
* Silences the unbound diagnostic; nothing else changes.
|
|
197
|
+
*
|
|
198
|
+
* The diagnostic exists because a single-mode wrapper whose parent
|
|
199
|
+
* forgot `v-register` renders a field that looks fine and stores
|
|
200
|
+
* nothing, which is an expensive afternoon. Once a wrapper is
|
|
201
|
+
* deliberately dual-mode, the same signal fires on correct code and
|
|
202
|
+
* tells the author to do something wrong, so this is the one fact
|
|
203
|
+
* that separates the two cases and only the author has it (#620).
|
|
204
|
+
*
|
|
205
|
+
* Pair it with `rv?.isBound` to branch on what actually happened.
|
|
206
|
+
*/
|
|
207
|
+
readonly optional?: boolean;
|
|
208
|
+
};
|
|
209
|
+
declare function useRegister<V = unknown>(options?: UseRegisterOptions): UseRegisterReturn<V> | undefined;
|
|
210
|
+
|
|
211
|
+
type DynamicPathSweep = {
|
|
212
|
+
/**
|
|
213
|
+
* Note that a cache has just stored an entry for `segments` under
|
|
214
|
+
* `key`. Paths a schema shape bounds are ignored, so only the
|
|
215
|
+
* candidates the sweep could ever drop are walked.
|
|
216
|
+
*/
|
|
217
|
+
readonly track: (segments: Path, key: PathKey) => void;
|
|
218
|
+
/**
|
|
219
|
+
* Register a cache's own eviction. It receives the canonical
|
|
220
|
+
* `PathKey` of a path the form no longer has, and deletes whatever it
|
|
221
|
+
* files under that path. A cache keying by more than the path (a
|
|
222
|
+
* shape sigil, say) deletes every variant; a key it never held is a
|
|
223
|
+
* no-op.
|
|
224
|
+
*/
|
|
225
|
+
readonly onEvict: (evict: (key: PathKey) => void) => void;
|
|
226
|
+
};
|
|
154
227
|
|
|
155
228
|
/**
|
|
156
|
-
* Per-form display engine: owns the clock and the timers
|
|
157
|
-
*
|
|
229
|
+
* Per-form display engine: owns the clock and the timers the display
|
|
230
|
+
* reducer's timing policy needs, so the reducer itself stays a
|
|
158
231
|
* deterministic `(prev, ctx) => next` function.
|
|
159
232
|
*
|
|
160
233
|
* It keeps a `Map` of the machines that are still *active* (a spinner is
|
|
@@ -173,15 +246,15 @@ declare function useRegister<V = unknown>(): UseRegisterReturn<V> | undefined;
|
|
|
173
246
|
* retained set is bounded by the rendered non-idle fields; none of them
|
|
174
247
|
* arm a timer, so retention costs memory, never CPU.
|
|
175
248
|
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
249
|
+
* `setTimeout` invariants on the returned `reviewAt`, enforced here rather
|
|
250
|
+
* than trusted from the reducer. A non-finite deadline is ignored rather
|
|
251
|
+
* than handed to `setTimeout`, where it coerces to 0 and spins; an over-large
|
|
252
|
+
* finite deadline is clamped below the 32-bit `setTimeout` overflow; and the
|
|
253
|
+
* timer refuses to re-arm for a deadline it just fired that has also already
|
|
254
|
+
* passed. The last of those is not belt-and-braces: the library reducer's own
|
|
255
|
+
* min-visible hold re-emits ONE fixed instant for the length of the window,
|
|
256
|
+
* which is exactly the shape that stranded a field on `'pending'` forever (see
|
|
257
|
+
* `rearm`).
|
|
185
258
|
*
|
|
186
259
|
* Background tabs: `setTimeout` is throttled to >= 1s while a tab is hidden,
|
|
187
260
|
* so a min-visible hold can overshoot. A `visibilitychange` listener bumps
|
|
@@ -215,56 +288,64 @@ type DisplayEngine = {
|
|
|
215
288
|
};
|
|
216
289
|
|
|
217
290
|
/**
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
* `
|
|
236
|
-
*
|
|
237
|
-
* `
|
|
291
|
+
* The error stores, indexed by path prefix.
|
|
292
|
+
*
|
|
293
|
+
* `aggregateErrorsAt(state, prefix)` answers "every active error at or
|
|
294
|
+
* under this path", and it used to answer it by walking every entry in
|
|
295
|
+
* all three error stores, resolving each key back to segments and
|
|
296
|
+
* testing the prefix. That is fine for one call. A table asks for one
|
|
297
|
+
* per row: `form.list('rows')` on 400 rows with 800 errors did 320,000
|
|
298
|
+
* of those tests, and a keystroke in any single row re-did all of them,
|
|
299
|
+
* costing a quarter of a second per character typed.
|
|
300
|
+
*
|
|
301
|
+
* The cost is structural, not incidental: the work is O(rows x errors)
|
|
302
|
+
* where the answer is O(errors). So the keys are collected ONCE per
|
|
303
|
+
* change to the stores, resolved to segments once, and sorted, and a
|
|
304
|
+
* query binary-searches its own window instead of scanning.
|
|
305
|
+
*
|
|
306
|
+
* ## Why a sorted key array is the right index
|
|
307
|
+
*
|
|
308
|
+
* A `PathKey` is `JSON.stringify(segments)`, so a path's descendants
|
|
309
|
+
* all begin with the parent's key minus its closing bracket, plus a
|
|
310
|
+
* comma: every key under `["rows",0]` begins `["rows",0,`. Call that
|
|
311
|
+
* the marker. Two facts make a sorted array enough:
|
|
312
|
+
*
|
|
313
|
+
* - every descendant key is >= the marker, since it begins with it;
|
|
314
|
+
* - every descendant key is <= the parent's own key, since the two
|
|
315
|
+
* agree up to the last character, where `,` (0x2C) sorts before
|
|
316
|
+
* `]` (0x5D).
|
|
317
|
+
*
|
|
318
|
+
* So `[marker, parentKey]` is a contiguous window containing every
|
|
319
|
+
* descendant and the parent itself. It is a SUPERSET, not an exact
|
|
320
|
+
* answer, and the caller still applies `isPathPrefix` to what comes
|
|
321
|
+
* back: the window narrows the scan, it does not decide membership.
|
|
322
|
+
* That split is deliberate. Membership stays where it already was, so
|
|
323
|
+
* the encoding argument above cannot quietly become the definition of
|
|
324
|
+
* a prefix.
|
|
325
|
+
*
|
|
326
|
+
* The root prefix has no marker (`[]` minus its bracket plus a comma is
|
|
327
|
+
* `[,`, which nothing begins with), and the root matches everything
|
|
328
|
+
* anyway, so it is answered by handing back the whole index.
|
|
238
329
|
*/
|
|
239
330
|
|
|
240
|
-
/**
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
*
|
|
246
|
-
* Without this helper, authoring `{ input: 'string', output:
|
|
247
|
-
* 'number', transform: (s) => ... }` against the broader
|
|
248
|
-
* `CoercionEntry` widens `s` to `string | number | boolean | ...`,
|
|
249
|
-
* forcing a cast in every transform body. `defineCoercion` is the
|
|
250
|
-
* opaque-free idiom.
|
|
251
|
-
*/
|
|
252
|
-
declare function defineCoercion<I extends SlimPrimitiveKind, O extends SlimPrimitiveKind>(entry: CoercionEntry<I, O>): CoercionEntry<I, O>;
|
|
253
|
-
/**
|
|
254
|
-
* Internal index built from a `CoercionRegistry` at config-resolve
|
|
255
|
-
* time. Keyed by `${input}->${output}` for O(1) per-keystroke
|
|
256
|
-
* dispatch. The authoring shape (array, ergonomic, type-narrowing-
|
|
257
|
-
* friendly) and the dispatch shape (Map, fast) decouple cleanly.
|
|
258
|
-
*/
|
|
259
|
-
type CoercionIndex = ReadonlyMap<`${SlimPrimitiveKind}->${SlimPrimitiveKind}`, CoercionEntry>;
|
|
260
|
-
/**
|
|
261
|
-
* The library's built-in registry. Two cells: string→number and
|
|
262
|
-
* string→boolean. Re-exported so consumers can spread it when
|
|
263
|
-
* supplying a custom registry that extends defaults.
|
|
264
|
-
*/
|
|
265
|
-
declare const defaultCoercionRules: CoercionRegistry;
|
|
331
|
+
/** One indexed path: its key, and the segments that key decodes to. */
|
|
332
|
+
type ErrorPathEntry = {
|
|
333
|
+
readonly key: PathKey;
|
|
334
|
+
readonly segments: Path;
|
|
335
|
+
};
|
|
266
336
|
|
|
267
337
|
type FormStore<F extends GenericForm, G extends GenericForm = F> = {
|
|
338
|
+
/**
|
|
339
|
+
* The form's single liveness sweep over every per-path cache. Owned by
|
|
340
|
+
* the store rather than by `buildFormApi` because the store's own
|
|
341
|
+
* per-path maps (`fields`, `originals`, `authoredPaths`,
|
|
342
|
+
* `fieldValidationState`) are the largest thing it evicts, and they
|
|
343
|
+
* exist whether or not a form API was ever built around them. Read
|
|
344
|
+
* surfaces register their own evictions into it, so one subscription
|
|
345
|
+
* and one liveness walk serve all of them. See
|
|
346
|
+
* `dynamic-path-sweep.ts`.
|
|
347
|
+
*/
|
|
348
|
+
readonly pathSweep: DynamicPathSweep;
|
|
268
349
|
readonly formKey: FormKey;
|
|
269
350
|
readonly form: Ref<F>;
|
|
270
351
|
readonly fields: Map<PathKey, FieldRecord>;
|
|
@@ -303,6 +384,22 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
|
|
|
303
384
|
* sentinel — see `docs/validation/blank.md`.
|
|
304
385
|
*/
|
|
305
386
|
readonly derivedBlankErrors: ComputedRef<ReadonlyMap<PathKey, ValidationError[]>>;
|
|
387
|
+
/**
|
|
388
|
+
* Every path carrying an error at or under `prefix`, sorted by key.
|
|
389
|
+
* `aggregateErrorsAt` takes its candidates from here instead of
|
|
390
|
+
* re-scanning all three error stores per call, which is what turns a
|
|
391
|
+
* table of N rows from O(N x errors) back into O(errors). See
|
|
392
|
+
* `error-path-index.ts`.
|
|
393
|
+
*
|
|
394
|
+
* Reading this rather than the form-global index is also what keeps
|
|
395
|
+
* containers isolated from each other. The index is one value for the
|
|
396
|
+
* whole form, so any path gaining or losing an error gives it a new
|
|
397
|
+
* identity; a container reading it directly woke on every other
|
|
398
|
+
* container's errors. Each prefix gets its own memoised `computed`
|
|
399
|
+
* that holds its previous array when its own window is unchanged, so
|
|
400
|
+
* the global change stops there. See `errorWindowAt` in the store.
|
|
401
|
+
*/
|
|
402
|
+
readonly errorWindowAt: (prefix: Path, prefixKey: PathKey) => readonly ErrorPathEntry[];
|
|
306
403
|
readonly originals: Map<PathKey, OriginalsRecord>;
|
|
307
404
|
/**
|
|
308
405
|
* Reactive set of paths whose displayed state should be EMPTY even
|
|
@@ -352,18 +449,9 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
|
|
|
352
449
|
* the eventual directive lifecycle remains the source of truth.
|
|
353
450
|
*/
|
|
354
451
|
readonly ssr: boolean;
|
|
355
|
-
/**
|
|
356
|
-
* Resolved `getDisplayState` predicate driving `field.displayState`,
|
|
357
|
-
* the `show*` booleans, and their `form.meta` rollups. Resolved once
|
|
358
|
-
* at construction via `resolveGetDisplayState(options.getDisplayState)`;
|
|
359
|
-
* `undefined` config falls through to `defaultDisplayState`. The
|
|
360
|
-
* field-state computeds read the resolved function directly on every
|
|
361
|
-
* read.
|
|
362
|
-
*/
|
|
363
|
-
readonly getDisplayState: GetDisplayState;
|
|
364
452
|
/**
|
|
365
453
|
* Per-form display engine: owns the clock and the single timer the timed
|
|
366
|
-
*
|
|
454
|
+
* display-reducer policy needs, keeping the reducer itself a
|
|
367
455
|
* pure `(prev, ctx) => next` function. The field-state computeds route
|
|
368
456
|
* every `displayState` read through `displayEngine.resolve(...)`, which
|
|
369
457
|
* threads the path's previous machine, persists or evicts the result, and
|
|
@@ -466,10 +554,22 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
|
|
|
466
554
|
* Resolves after `hydrating` flips back to `false`; consumers can
|
|
467
555
|
* `await form.rehydrate()` to gate UI on the fresh load.
|
|
468
556
|
*
|
|
469
|
-
* Does NOT touch
|
|
470
|
-
*
|
|
557
|
+
* Does NOT touch touched / submit state; chain `form.reset()` if you
|
|
558
|
+
* want a clean surface. It DOES re-seat the defaults (see
|
|
559
|
+
* `adoptResolvedDefaults`), so dirty afterwards means "differs from
|
|
560
|
+
* what the factory just returned", and `form.reset()` lands on the
|
|
561
|
+
* fresh values rather than throwing them away.
|
|
471
562
|
*/
|
|
472
563
|
rehydrate(): Promise<void>;
|
|
564
|
+
/**
|
|
565
|
+
* Adopt a resolved async-defaults value as the form's durable
|
|
566
|
+
* defaults and re-seed the dirty baseline from it. Called by the
|
|
567
|
+
* activate / rehydrate orchestrator once its factory settles.
|
|
568
|
+
* Exposed on the store (rather than imported) because
|
|
569
|
+
* `form-activation` is imported BY this module; a value import back
|
|
570
|
+
* the other way would close a cycle.
|
|
571
|
+
*/
|
|
572
|
+
adoptResolvedDefaults(value: unknown): void;
|
|
473
573
|
/**
|
|
474
574
|
* Incremented by every `reset()` call. The submit wrapper captures
|
|
475
575
|
* this at entry and skips writing `submitError` from a catch that
|
|
@@ -502,26 +602,10 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
|
|
|
502
602
|
* run yet — so without the gate, frame 1 paints the form as
|
|
503
603
|
* "valid" before the real verdict arrives a tick later.
|
|
504
604
|
*
|
|
505
|
-
* Initialized to `!strict`: non-strict consumers opt out of the
|
|
506
|
-
* validation pipeline by design, so locking them on
|
|
507
|
-
* `firstValidationDone === false` would defeat the opt-out.
|
|
508
605
|
* Reset is left untouched — the post-reset validation flips it
|
|
509
606
|
* back true on completion, same as the construction-time path.
|
|
510
607
|
*/
|
|
511
608
|
readonly firstValidationDone: Ref<boolean>;
|
|
512
|
-
/**
|
|
513
|
-
* `true` when the sub-schema rooted at `path` (or any of its
|
|
514
|
-
* descendants) declares async work — composes
|
|
515
|
-
* `schema.getSchemasAtPath(path)` with each candidate's
|
|
516
|
-
* `needsAsyncValidation()`, memoised per canonical path key for
|
|
517
|
-
* the lifetime of the FormStore. Used by `meta.valid` /
|
|
518
|
-
* `field.valid` to skip the `firstValidationDone` gate on subtrees
|
|
519
|
-
* that are fully synchronous: their verdict resolves at construction
|
|
520
|
-
* (or on the next per-field run) without waiting on a microtask, so
|
|
521
|
-
* honouring the form-wide gate would just play dumb about a known
|
|
522
|
-
* answer.
|
|
523
|
-
*/
|
|
524
|
-
pathHasAsyncValidation(path: Path): boolean;
|
|
525
609
|
/**
|
|
526
610
|
* Precomputed-key shortcut for `pathHasAsyncValidation`. The
|
|
527
611
|
* canonical key is required and must correspond to `segments`; the
|
|
@@ -741,17 +825,6 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
|
|
|
741
825
|
* source there).
|
|
742
826
|
*/
|
|
743
827
|
markConnectedOptimistically(path: Path): void;
|
|
744
|
-
/**
|
|
745
|
-
* Leaf-only pristine check. `originals` is populated via
|
|
746
|
-
* `diffAndApply`'s `added` patches, which fire only on primitive
|
|
747
|
-
* leaves — a container path (e.g. `['profile']`) that isn't in
|
|
748
|
-
* `originals` returns `true` here even when a descendant is dirty.
|
|
749
|
-
* Callers that need container semantics should either loop over
|
|
750
|
-
* leaves or walk `originals` manually. The public `getFieldState`
|
|
751
|
-
* surface is typed to accept leaf paths only, so in practice this
|
|
752
|
-
* isn't exposed to consumers.
|
|
753
|
-
*/
|
|
754
|
-
isPristineAtPath(path: Path): boolean;
|
|
755
828
|
/**
|
|
756
829
|
* Precomputed-key shortcut for `isPristineAtPath`. The canonical
|
|
757
830
|
* key is required and must correspond to `segments`; the helper
|
|
@@ -778,7 +851,6 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
|
|
|
778
851
|
*/
|
|
779
852
|
hasRemovedSubtreeUnder(path: Path): boolean;
|
|
780
853
|
getFieldRecord(path: Path): FieldRecord | undefined;
|
|
781
|
-
getOriginalAtPath(path: Path): unknown;
|
|
782
854
|
/**
|
|
783
855
|
* Cancel every in-flight field-level validation run — clears timers
|
|
784
856
|
* for debounced 'change' runs that haven't fired, latches `aborted`
|
|
@@ -803,12 +875,6 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
|
|
|
803
875
|
endTransform(key: PathKey, token: number): void;
|
|
804
876
|
/** Record a per-field normalization failure at `key` (`field.transformError`). */
|
|
805
877
|
setTransformError(key: PathKey, err: Error): void;
|
|
806
|
-
/**
|
|
807
|
-
* Abort + release every in-flight async-transform run (all paths) and
|
|
808
|
-
* clear `transformErrors`. Mirrors `cancelFieldValidation`; called by
|
|
809
|
-
* `reset()` and store teardown.
|
|
810
|
-
*/
|
|
811
|
-
cancelTransforms(): void;
|
|
812
878
|
/**
|
|
813
879
|
* Path-scoped counterpart to `cancelTransforms`: abort + release only
|
|
814
880
|
* the runs at-or-under `prefix`, clearing their `transformError`.
|
|
@@ -839,10 +905,7 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
|
|
|
839
905
|
* store's captured `debounceMs`. Used so sibling instances sharing a
|
|
840
906
|
* FormStore can each validate on their own cadence.
|
|
841
907
|
*/
|
|
842
|
-
scheduleFieldValidation(path: Path, immediate: boolean,
|
|
843
|
-
readonly mode?: ValidateOn;
|
|
844
|
-
readonly debounceMs?: number;
|
|
845
|
-
}): void;
|
|
908
|
+
scheduleFieldValidation(path: Path, immediate: boolean, instance?: WriteMeta['instance']): void;
|
|
846
909
|
/**
|
|
847
910
|
* Subscribe to every `applyFormReplacement`. Fires synchronously
|
|
848
911
|
* after `form.value` has been swapped to `next` and all field /
|
|
@@ -889,14 +952,12 @@ type FormStore<F extends GenericForm, G extends GenericForm = F> = {
|
|
|
889
952
|
*/
|
|
890
953
|
readonly modules: Map<string, unknown>;
|
|
891
954
|
/**
|
|
892
|
-
*
|
|
893
|
-
*
|
|
894
|
-
*
|
|
895
|
-
*
|
|
896
|
-
* by `buildCoerceFn` to bake the per-path coerce closure on
|
|
897
|
-
* `RegisterValue.coerce`.
|
|
955
|
+
* Whether schema-driven coercion runs for this form — `false` only
|
|
956
|
+
* when the consumer passed `useForm({ coerce: false })`. Read at
|
|
957
|
+
* `register()` time by `buildCoerceFn` to bake the per-path coerce
|
|
958
|
+
* closure on `RegisterValue.coerce`.
|
|
898
959
|
*/
|
|
899
|
-
readonly
|
|
960
|
+
readonly coerceEnabled: boolean;
|
|
900
961
|
/**
|
|
901
962
|
* Tear down non-reactive resources owned by this FormStore. Invoked
|
|
902
963
|
* by the registry when the last consumer unmounts. Cancels pending
|
|
@@ -1286,8 +1347,8 @@ type WizardOptions = {
|
|
|
1286
1347
|
/**
|
|
1287
1348
|
* When `wizard.handleSubmit` finds errors, automatically focus the
|
|
1288
1349
|
* first failing form: jump to its step and invoke its
|
|
1289
|
-
* `applyInvalidSubmitPolicy()` (
|
|
1290
|
-
* `
|
|
1350
|
+
* `applyInvalidSubmitPolicy()` (which honors that form's own
|
|
1351
|
+
* `focusOnInvalidSubmit` choice). Default `true`; pass `false` to
|
|
1291
1352
|
* keep the active step where the user left it and handle navigation
|
|
1292
1353
|
* manually in the `onError` callback.
|
|
1293
1354
|
*/
|
|
@@ -1486,6 +1547,12 @@ type WizardForms<S> = FormsRecordOf<S> & Readonly<Record<FormKey, AnyForm>>;
|
|
|
1486
1547
|
* `back()` does not pop; the trail is the audit
|
|
1487
1548
|
* log, not the back-stack.
|
|
1488
1549
|
* - `next/back/goTo` — pure navigation. Refuses while `submitting`.
|
|
1550
|
+
* The one exception is `next()` on an UNCLEARED
|
|
1551
|
+
* `gate()` step, which behaves as `tryNext()` so
|
|
1552
|
+
* wiring Next straight to it can never skip the
|
|
1553
|
+
* gate's confirmation. Once that gate clears, `next()`
|
|
1554
|
+
* is plain navigation again and does not re-submit to
|
|
1555
|
+
* re-confirm.
|
|
1489
1556
|
* - `tryNext()` — submit the active step, and once that submit
|
|
1490
1557
|
* resolves clean, advance; invalid input keeps the pin
|
|
1491
1558
|
* put under the form's standard error reveal (first
|
|
@@ -1495,6 +1562,13 @@ type WizardForms<S> = FormsRecordOf<S> & Readonly<Record<FormKey, AnyForm>>;
|
|
|
1495
1562
|
* (`@click="wizard.tryNext()"`); resolves to whether
|
|
1496
1563
|
* the pin moved. No-ops to `false` on a degenerate or
|
|
1497
1564
|
* final-step wizard (finish via `handleSubmit`).
|
|
1565
|
+
* Called while the active step's form already has a
|
|
1566
|
+
* submit in flight (the usual case being from inside
|
|
1567
|
+
* that form's own callback), it rides that submit
|
|
1568
|
+
* rather than starting a second one, so one action
|
|
1569
|
+
* costs one submission. The pin has not moved by the
|
|
1570
|
+
* time it resolves, so the answer is `false`; the
|
|
1571
|
+
* advance lands with the in-flight submit.
|
|
1498
1572
|
* - `handleSubmit(onSubmit, onError?)` — always validates the entire
|
|
1499
1573
|
* step list, from any step, and never advances the
|
|
1500
1574
|
* pin: on success it latches `done`; on any error it
|
|
@@ -1649,8 +1723,6 @@ type AttaformRegistry = {
|
|
|
1649
1723
|
readonly pendingHydration: PendingHydration;
|
|
1650
1724
|
/** `true` while running on the server during SSR; `false` on the client. */
|
|
1651
1725
|
readonly ssr: boolean;
|
|
1652
|
-
/** App-level defaults applied to every `useForm` call. */
|
|
1653
|
-
readonly defaults: AttaformDefaults;
|
|
1654
1726
|
/**
|
|
1655
1727
|
* Track a consumer of `key`. Returns a dispose function — call it
|
|
1656
1728
|
* when the consumer unmounts. The form is evicted automatically
|
|
@@ -1706,13 +1778,7 @@ declare module 'vue' {
|
|
|
1706
1778
|
}
|
|
1707
1779
|
}
|
|
1708
1780
|
/** Options for `createRegistry`. */
|
|
1709
|
-
type CreateRegistryOptions = SSRDetectOptions
|
|
1710
|
-
/**
|
|
1711
|
-
* App-level defaults applied to every `useForm` call. Per-form
|
|
1712
|
-
* options always win. Omitted is equivalent to `{}`.
|
|
1713
|
-
*/
|
|
1714
|
-
defaults?: AttaformDefaults;
|
|
1715
|
-
};
|
|
1781
|
+
type CreateRegistryOptions = SSRDetectOptions;
|
|
1716
1782
|
/**
|
|
1717
1783
|
* Create a fresh `AttaformRegistry`. `createAttaform()` calls
|
|
1718
1784
|
* this internally — most consumers never need to call it directly.
|
|
@@ -2018,34 +2084,20 @@ type AttaformPluginOptions = SSRDetectOptions & {
|
|
|
2018
2084
|
* fails silently. Pass `false` to skip the integration even in dev.
|
|
2019
2085
|
*/
|
|
2020
2086
|
devtools?: boolean;
|
|
2021
|
-
/**
|
|
2022
|
-
* App-level defaults applied to every `useForm` call in this app.
|
|
2023
|
-
* Per-form options always win. See `AttaformDefaults` for
|
|
2024
|
-
* the supported option set and the merge rules.
|
|
2025
|
-
*
|
|
2026
|
-
* ```ts
|
|
2027
|
-
* app.use(
|
|
2028
|
-
* createAttaform({
|
|
2029
|
-
* defaults: { debounceMs: 100 },
|
|
2030
|
-
* })
|
|
2031
|
-
* )
|
|
2032
|
-
* ```
|
|
2033
|
-
*/
|
|
2034
|
-
defaults?: AttaformDefaults;
|
|
2035
2087
|
};
|
|
2036
2088
|
/**
|
|
2037
2089
|
* Create the Vue plugin that installs the form library on a Vue
|
|
2038
2090
|
* application. Required only when you want app-wide options
|
|
2039
|
-
* (`
|
|
2040
|
-
*
|
|
2041
|
-
*
|
|
2091
|
+
* (`devtools: false`, `ssr: true`) — for the default setup,
|
|
2092
|
+
* `useForm` / `injectForm` / `useRegister` lazy-install the registry
|
|
2093
|
+
* on first use.
|
|
2042
2094
|
*
|
|
2043
2095
|
* ```ts
|
|
2044
2096
|
* import { createApp } from 'vue'
|
|
2045
2097
|
* import { createAttaform } from 'attaform'
|
|
2046
2098
|
*
|
|
2047
2099
|
* createApp(App)
|
|
2048
|
-
* .use(createAttaform({
|
|
2100
|
+
* .use(createAttaform({ devtools: false }))
|
|
2049
2101
|
* .mount('#app')
|
|
2050
2102
|
* ```
|
|
2051
2103
|
*
|
|
@@ -2310,75 +2362,5 @@ declare class ReservedFormKeyError extends AttaformError {
|
|
|
2310
2362
|
constructor(key: string);
|
|
2311
2363
|
}
|
|
2312
2364
|
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
*
|
|
2316
|
-
* - `showDelay` — how long in-flight work (a validation run or an async
|
|
2317
|
-
* register transform) may run before its spinner is allowed to show. Work
|
|
2318
|
-
* that settles inside this window never reveals `'pending'` at all, so a
|
|
2319
|
-
* fast (often synchronous) check does not flash a spinner on every keystroke.
|
|
2320
|
-
* - `minVisible` — once shown, the minimum time the spinner stays up. Work
|
|
2321
|
-
* that lands just past `showDelay` is held here so the spinner does not
|
|
2322
|
-
* itself flash on and immediately off.
|
|
2323
|
-
*
|
|
2324
|
-
* Both are milliseconds.
|
|
2325
|
-
*/
|
|
2326
|
-
type DisplayTimings = {
|
|
2327
|
-
readonly showDelay: number;
|
|
2328
|
-
readonly minVisible: number;
|
|
2329
|
-
};
|
|
2330
|
-
/**
|
|
2331
|
-
* Library-default anti-flash timings. `showDelay: 120` cleanly swallows
|
|
2332
|
-
* synchronous, microtask-resolved, and tiny-async work (validation runs and
|
|
2333
|
-
* async register transforms alike) so none of them flash a spinner;
|
|
2334
|
-
* `minVisible: 120` keeps a shown spinner snappy once one does land.
|
|
2335
|
-
* Retune via {@link makeDefaultDisplayState} without touching the engine.
|
|
2336
|
-
*/
|
|
2337
|
-
declare const DEFAULT_TIMINGS: DisplayTimings;
|
|
2338
|
-
/**
|
|
2339
|
-
* Build a default `getDisplayState` reducer with custom anti-flash timing.
|
|
2340
|
-
* Power users who want a tighter or looser spinner than {@link DEFAULT_TIMINGS}
|
|
2341
|
-
* pass their own `{ showDelay, minVisible }`:
|
|
2342
|
-
*
|
|
2343
|
-
* ```ts
|
|
2344
|
-
* import { makeDefaultDisplayState } from 'attaform'
|
|
2345
|
-
*
|
|
2346
|
-
* useForm({
|
|
2347
|
-
* schema,
|
|
2348
|
-
* getDisplayState: makeDefaultDisplayState({ showDelay: 50, minVisible: 200 }),
|
|
2349
|
-
* })
|
|
2350
|
-
* ```
|
|
2351
|
-
*
|
|
2352
|
-
* The returned reducer is pure: the engine injects `now` and threads the
|
|
2353
|
-
* previous machine, so the same `(prev, ctx)` always yields the same next
|
|
2354
|
-
* machine. It shapes only the display projection — `errors`, `valid`,
|
|
2355
|
-
* `validating`, and the underlying validation run exactly as before.
|
|
2356
|
-
*/
|
|
2357
|
-
declare function makeDefaultDisplayState({ showDelay, minVisible, }: DisplayTimings): GetDisplayState;
|
|
2358
|
-
/**
|
|
2359
|
-
* Library-default `getDisplayState` reducer. Resolves every path's
|
|
2360
|
-
* `field.displayState` — and thus `field.show*` and the `form.meta`
|
|
2361
|
-
* rollups — whenever the consumer has not configured an override at the
|
|
2362
|
-
* per-form or plugin level. Built from {@link DEFAULT_TIMINGS}; publicly
|
|
2363
|
-
* re-exported so an override can compose with it (a layered reducer that
|
|
2364
|
-
* special-cases a subtree but otherwise defers picks up future
|
|
2365
|
-
* refinements for free):
|
|
2366
|
-
*
|
|
2367
|
-
* ```ts
|
|
2368
|
-
* import { defaultDisplayState } from 'attaform'
|
|
2369
|
-
*
|
|
2370
|
-
* useForm({
|
|
2371
|
-
* schema,
|
|
2372
|
-
* getDisplayState: (prev, ctx) => {
|
|
2373
|
-
* const next = defaultDisplayState(prev, ctx)
|
|
2374
|
-
* return next.display === 'success' && ctx.field.path[0] === 'username'
|
|
2375
|
-
* ? { display: 'idle' }
|
|
2376
|
-
* : next
|
|
2377
|
-
* },
|
|
2378
|
-
* })
|
|
2379
|
-
* ```
|
|
2380
|
-
*/
|
|
2381
|
-
declare const defaultDisplayState: GetDisplayState;
|
|
2382
|
-
|
|
2383
|
-
export { renderAttaformState as $, createAttaform as B, DEFAULT_TIMINGS as D, createRegistry as E, defaultCoercionRules as H, defaultDisplayState as J, defineCoercion as K, escapeForInlineScript as M, gate as N, OutsideSetupError as O, getRegistryFromApp as P, hydrateAttaformState as Q, RegistryNotInstalledError as R, injectForm as T, injectWizard as V, isRegisterValue as X, kAttaformRegistry as Y, lazy as Z, makeDefaultDisplayState as _, useRegister as a0, useRegistry as a1, useWizard as a2, AttaformError as c, AttaformErrorCode as d, DEVTOOLS_WINDOW_KEY as f, InvalidPathError as i, InvalidUseFormConfigError as j, ReservedFormKeyError as k, SubmitErrorHandlerError as n, assignKey as z };
|
|
2384
|
-
export type { AttaformRegistry as A, CompiledStep as C, FormStatus as F, GateMarker as G, InjectWizardInput as I, LazyMarker as L, SerializedAttaformState as S, UseRegisterReturn as U, WizardAggregateError as W, AnyForm as a, AttaformDevtoolsBridge as b, AttaformPluginOptions as e, DisplayTimings as g, FormStatusSeed as h, SerializedFormData as l, StepSlot as m, UseWizardReturnType as o, WizardCtx as p, WizardCtxForm as q, WizardOnError as r, WizardOnSubmit as s, WizardOptions as t, WizardPersistFn as u, WizardRestoreFn as v, WizardRestoreState as w, WizardStatusesProxy as x, WizardSubmitContext as y };
|
|
2365
|
+
export { createRegistry as B, DEVTOOLS_WINDOW_KEY as D, escapeForInlineScript as E, gate as H, getRegistryFromApp as J, hydrateAttaformState as K, injectForm as M, injectWizard as N, OutsideSetupError as O, isRegisterValue as P, kAttaformRegistry as Q, RegistryNotInstalledError as R, lazy as T, renderAttaformState as V, useRegister as X, useRegistry as Y, useWizard as Z, AttaformError as c, AttaformErrorCode as d, InvalidPathError as g, InvalidUseFormConfigError as h, ReservedFormKeyError as i, SubmitErrorHandlerError as l, assignKey as y, createAttaform as z };
|
|
2366
|
+
export type { AttaformRegistry as A, CompiledStep as C, FormStatus as F, GateMarker as G, InjectWizardInput as I, LazyMarker as L, SerializedAttaformState as S, UseRegisterOptions as U, WizardAggregateError as W, AnyForm as a, AttaformDevtoolsBridge as b, AttaformPluginOptions as e, FormStatusSeed as f, SerializedFormData as j, StepSlot as k, UseRegisterReturn as m, UseWizardReturnType as n, WizardCtx as o, WizardCtxForm as p, WizardOnError as q, WizardOnSubmit as r, WizardOptions as s, WizardPersistFn as t, WizardRestoreFn as u, WizardRestoreState as v, WizardStatusesProxy as w, WizardSubmitContext as x };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as RegistryNotInstalledError, O as OutsideSetupError, g as pathKeyToDotted } from './attaform.
|
|
1
|
+
import { c as RegistryNotInstalledError, O as OutsideSetupError, g as pathKeyToDotted } from './attaform.CdZIEf7_.mjs';
|
|
2
2
|
import { shallowReactive, getCurrentInstance, inject } from 'vue';
|
|
3
3
|
|
|
4
4
|
function detectSSR(options = {}) {
|
|
@@ -17,7 +17,6 @@ const kFormContext = /* @__PURE__ */ Symbol.for("attaform:form-context");
|
|
|
17
17
|
const kFormInstanceId = /* @__PURE__ */ Symbol.for("attaform:form-instance-id");
|
|
18
18
|
function createRegistry(options = {}) {
|
|
19
19
|
const ssr = detectSSR(options);
|
|
20
|
-
const defaults = Object.freeze({ ...options.defaults ?? {} });
|
|
21
20
|
const forms = shallowReactive(/* @__PURE__ */ new Map());
|
|
22
21
|
const wizards = shallowReactive(/* @__PURE__ */ new Map());
|
|
23
22
|
const pendingHydration = shallowReactive(/* @__PURE__ */ new Map());
|
|
@@ -102,7 +101,6 @@ function createRegistry(options = {}) {
|
|
|
102
101
|
wizards,
|
|
103
102
|
pendingHydration,
|
|
104
103
|
ssr,
|
|
105
|
-
defaults,
|
|
106
104
|
trackConsumer,
|
|
107
105
|
trackWizardConsumer,
|
|
108
106
|
enqueuePrefetch,
|