attaform 0.30.0 → 0.31.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 +15 -21
- package/dist/abstract.mjs +3 -3
- package/dist/dev/abstract.mjs +4 -4
- package/dist/dev/chunks/devtools.mjs +4 -4
- package/dist/dev/directive.mjs +1 -1
- package/dist/dev/history.mjs +2 -2
- package/dist/dev/index.mjs +5 -5
- package/dist/dev/runtime/plugins/attaform.mjs +1 -1
- package/dist/dev/shared/{attaform-dev.DUZckyV8.mjs → attaform-dev.B1q-x9R9.mjs} +4 -4
- package/dist/dev/shared/{attaform-dev.BcYT6tFh.mjs → attaform-dev.CZz4GYZ6.mjs} +21 -9
- package/dist/dev/shared/{attaform-dev.BisFstxh.mjs → attaform-dev.Caibt7sb.mjs} +16 -15
- package/dist/dev/shared/{attaform-dev.DTrHonpc.mjs → attaform-dev.CtyqGia7.mjs} +176 -260
- package/dist/dev/shared/{attaform-dev.BK6Ge2qR.mjs → attaform-dev.DJ3nsWw6.mjs} +86 -122
- package/dist/dev/shared/{attaform-dev.BcNtyOmf.mjs → attaform-dev.DlaT4xGd.mjs} +4 -4
- package/dist/dev/shared/{attaform-dev.D6CLvaQT.mjs → attaform-dev.k5X47GMD.mjs} +520 -395
- package/dist/dev/shared/{attaform-dev.CQ2rJiAX.mjs → attaform-dev.y9x9Vp9F.mjs} +324 -234
- package/dist/dev/shared/{attaform-dev.BQc5QV3Y.mjs → attaform-dev.zlvDr0WB.mjs} +2 -2
- 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 +4 -4
- package/dist/directive.mjs +1 -1
- package/dist/esbuild.d.mts +11 -0
- package/dist/esbuild.mjs +1 -1
- package/dist/history.d.mts +3 -32
- package/dist/history.mjs +2 -2
- package/dist/index.d.mts +67 -70
- package/dist/index.mjs +4 -4
- package/dist/nuxt.d.mts +4 -3
- package/dist/nuxt.mjs +3 -6
- package/dist/rollup.d.mts +11 -0
- package/dist/rollup.mjs +1 -1
- package/dist/rspack.d.mts +19 -11
- package/dist/rspack.mjs +1 -1
- package/dist/runtime/components/AttaformDevtoolsPanel.vue +15 -15
- package/dist/runtime/components/DevtoolsValueTree.vue +2 -2
- package/dist/runtime/plugins/attaform.mjs +1 -1
- package/dist/shared/{attaform.BRDDQeHV.mjs → attaform.8mfKZCwH.mjs} +2 -2
- package/dist/shared/{attaform.DRUT703x.d.mts → attaform.B-Tb62qV.d.mts} +12 -12
- package/dist/shared/{attaform.v0fQtWyA.mjs → attaform.B1rH9ht6.mjs} +1 -1
- package/dist/shared/{attaform.DhnPb6nK.mjs → attaform.BAf3UNn4.mjs} +1 -1
- package/dist/shared/{attaform.BXneBxEj.mjs → attaform.BWUn8Kjy.mjs} +85 -121
- package/dist/shared/{attaform.WiGdIqgx.mjs → attaform.C4shmwyJ.mjs} +2 -2
- package/dist/shared/{attaform.Rc9u4Vfw.d.mts → attaform.CRVe8s82.d.mts} +83 -95
- package/dist/shared/attaform.D9RqPgGu.d.mts +42 -0
- package/dist/shared/attaform.DAo0QBmS.d.mts +2173 -0
- package/dist/shared/{attaform.BK0cgu2K.mjs → attaform.DBCbxlyk.mjs} +62 -4
- package/dist/shared/{attaform.CUfrMK7t.mjs → attaform.DL2nl_QR.mjs} +15 -14
- package/dist/shared/{attaform.DNJOWjQw.mjs → attaform.DNPJPOO7.mjs} +299 -218
- package/dist/shared/attaform.DZnrDrNb.d.mts +4657 -0
- package/dist/shared/{attaform.B3resMQ1.mjs → attaform.DxKQsqFZ.mjs} +496 -371
- package/dist/shared/{attaform.ByWP6kSx.mjs → attaform.L011tHgJ.mjs} +3 -3
- package/dist/shared/{attaform.CJ8DpRGj.mjs → attaform.LUPC-2Qg.mjs} +175 -257
- package/dist/transforms.d.mts +153 -26
- package/dist/transforms.mjs +1 -1
- package/dist/vite.d.mts +29 -52
- package/dist/vite.mjs +3 -3
- package/dist/webpack.d.mts +20 -12
- package/dist/webpack.mjs +1 -1
- package/dist/zod-v3.d.mts +90 -96
- package/dist/zod-v3.mjs +5 -5
- package/dist/zod-v4.d.mts +120 -118
- package/dist/zod-v4.mjs +5 -5
- package/dist/zod.d.mts +5 -5
- package/dist/zod.mjs +4 -4
- package/package.json +7 -6
- package/skills/attaform/SKILL.md +1 -1
- package/skills/attaform/references/custom-components.md +4 -0
- package/dist/shared/attaform.Clz0ArIi.d.mts +0 -44
- package/dist/shared/attaform.UGyOK83T.d.mts +0 -2366
- package/dist/shared/attaform.bKLzMU0o.d.mts +0 -4761
|
@@ -5,7 +5,7 @@ import { computed, onUnmounted, ref, watch } from 'vue'
|
|
|
5
5
|
// can resolve through the consumer's node_modules → `dist/index.mjs`,
|
|
6
6
|
// rather than through brittle relative paths into the rollup-bundled
|
|
7
7
|
// shared chunks (which mkdist doesn't co-locate). Type-only imports of
|
|
8
|
-
// internal types stay relative
|
|
8
|
+
// internal types stay relative: they're erased at compile time and
|
|
9
9
|
// don't need to resolve at the consumer's runtime.
|
|
10
10
|
import { type AttaformDevtoolsBridge, canonicalizePath, type Segment } from 'attaform'
|
|
11
11
|
import type { FormStore } from '../core/create-form-store'
|
|
@@ -18,7 +18,7 @@ import { computed, onUnmounted, ref, watch } from 'vue'
|
|
|
18
18
|
}>()
|
|
19
19
|
|
|
20
20
|
// Cross-iframe reactivity bridge. Vue's reactivity system is module-
|
|
21
|
-
// scoped
|
|
21
|
+
// scoped: the host's `@vue/reactivity` instance and the panel's are
|
|
22
22
|
// different copies of the same module, each with its own targetMap.
|
|
23
23
|
// When the panel reads `host.form.value` in a `computed`, the proxy's
|
|
24
24
|
// get trap runs in the host's tracking context (the function lives
|
|
@@ -31,7 +31,7 @@ import { computed, onUnmounted, ref, watch } from 'vue'
|
|
|
31
31
|
// (`onFormChange` / `onSubmitSuccess` / `onReset`), plus a 250ms
|
|
32
32
|
// polling fallback for state that changes outside those events (user
|
|
33
33
|
// errors via `setErrors`, submit-lifecycle flags). The panel's
|
|
34
|
-
// own reactivity then re-evaluates everything in one pass
|
|
34
|
+
// own reactivity then re-evaluates everything in one pass, cheap
|
|
35
35
|
// because the underlying reads are direct property accesses.
|
|
36
36
|
const updateTick = ref(0)
|
|
37
37
|
|
|
@@ -72,7 +72,7 @@ import { computed, onUnmounted, ref, watch } from 'vue'
|
|
|
72
72
|
void updateTick.value
|
|
73
73
|
const form = activeForm.value
|
|
74
74
|
if (form === null) return []
|
|
75
|
-
return Array.from(form.errorCells.entries())
|
|
75
|
+
return Array.from(form.errorCells().entries())
|
|
76
76
|
.filter(([, cell]) => cell.schema.length > 0)
|
|
77
77
|
.map(([key, cell]) => [key, cell.schema] as const)
|
|
78
78
|
})
|
|
@@ -81,7 +81,7 @@ import { computed, onUnmounted, ref, watch } from 'vue'
|
|
|
81
81
|
void updateTick.value
|
|
82
82
|
const form = activeForm.value
|
|
83
83
|
if (form === null) return []
|
|
84
|
-
return Array.from(form.errorCells.entries())
|
|
84
|
+
return Array.from(form.errorCells().entries())
|
|
85
85
|
.filter(([, cell]) => cell.user.length > 0)
|
|
86
86
|
.map(([key, cell]) => [key, cell.user] as const)
|
|
87
87
|
})
|
|
@@ -120,7 +120,7 @@ import { computed, onUnmounted, ref, watch } from 'vue'
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
/**
|
|
123
|
-
* Render a JS value as a debug-friendly string with no masking
|
|
123
|
+
* Render a JS value as a debug-friendly string with no masking,
|
|
124
124
|
* `null` / `undefined` show as their literal names, booleans and
|
|
125
125
|
* numbers as-is, strings bare (no surrounding quotes), everything
|
|
126
126
|
* else JSON-stringified. Devtools is for inspecting state, not for
|
|
@@ -169,13 +169,13 @@ import { computed, onUnmounted, ref, watch } from 'vue'
|
|
|
169
169
|
/**
|
|
170
170
|
* Raw field data at the selected path, composed from `FormStore`
|
|
171
171
|
* primitives. The callable `form.fields(path)` proxy lives on the
|
|
172
|
-
* public `useForm` return, not on `FormStore
|
|
172
|
+
* public `useForm` return, not on `FormStore`: the bridge exposes
|
|
173
173
|
* the store, so we synthesise the same data from `fields.get(key)`
|
|
174
174
|
* + the error Maps + an inline value walk.
|
|
175
175
|
*
|
|
176
176
|
* Returns the raw `FieldRecord` (updatedAt / focused / blurred /
|
|
177
177
|
* touched / connected) rather than the wrapped `FieldState`
|
|
178
|
-
* surface. Sufficient for inspection
|
|
178
|
+
* surface. Sufficient for inspection: the full aggregated
|
|
179
179
|
* FieldState would require either lifting the surface-proxy into
|
|
180
180
|
* `FormStore` (architectural change) or rebuilding the aggregation
|
|
181
181
|
* walker in the panel (duplication).
|
|
@@ -211,7 +211,7 @@ import { computed, onUnmounted, ref, watch } from 'vue'
|
|
|
211
211
|
| undefined) ?? null
|
|
212
212
|
|
|
213
213
|
// Inline path walk (avoids importing path-walker through the
|
|
214
|
-
// bridge
|
|
214
|
+
// bridge: it lives in the host's shared chunk).
|
|
215
215
|
let value: unknown = form.form.value
|
|
216
216
|
for (const seg of path) {
|
|
217
217
|
if (value === null || typeof value !== 'object') {
|
|
@@ -221,7 +221,7 @@ import { computed, onUnmounted, ref, watch } from 'vue'
|
|
|
221
221
|
value = (value as Record<string | number, unknown>)[seg]
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
const cell = form.errorCells.get(canonicalKey)
|
|
224
|
+
const cell = form.errorCells().get(canonicalKey)
|
|
225
225
|
const schemaEntries =
|
|
226
226
|
(cell?.schema as ReadonlyArray<{ message: string; code: string }> | undefined) ?? []
|
|
227
227
|
const userEntries =
|
|
@@ -259,7 +259,7 @@ import { computed, onUnmounted, ref, watch } from 'vue'
|
|
|
259
259
|
const { segments: canonicalPath } = canonicalizePath(rawPath as readonly Segment[])
|
|
260
260
|
form.setValueAtPath(canonicalPath, next)
|
|
261
261
|
// The host's setValueAtPath fires `onFormChange` listeners, which
|
|
262
|
-
// bumps our updateTick (see subscribeForm)
|
|
262
|
+
// bumps our updateTick (see subscribeForm): that refreshes the
|
|
263
263
|
// panel's view of the new value on the next microtask.
|
|
264
264
|
} catch (err) {
|
|
265
265
|
// Surface cross-iframe write failures (e.g., type-instance checks
|
|
@@ -285,7 +285,7 @@ import { computed, onUnmounted, ref, watch } from 'vue'
|
|
|
285
285
|
|
|
286
286
|
/**
|
|
287
287
|
* Hard cap on the in-memory event log. Sized for a debugging session,
|
|
288
|
-
* not an audit log
|
|
288
|
+
* not an audit log, older events fall off the back when capacity
|
|
289
289
|
* fills. Tunable later if real consumers ask for more.
|
|
290
290
|
*/
|
|
291
291
|
const MAX_TIMELINE_EVENTS = 200
|
|
@@ -325,7 +325,7 @@ import { computed, onUnmounted, ref, watch } from 'vue'
|
|
|
325
325
|
|
|
326
326
|
function subscribeForm(key: FormKey, form: FormStore<GenericForm>): void {
|
|
327
327
|
if (subscribers.has(key)) return
|
|
328
|
-
// Deep-clone the form value at the moment of fire
|
|
328
|
+
// Deep-clone the form value at the moment of fire, FormStore
|
|
329
329
|
// mutates form data in place, so a stored reference would update
|
|
330
330
|
// every existing timeline entry whenever the form changed again
|
|
331
331
|
// ("type a, delete a, both timeline events show the empty value"
|
|
@@ -336,7 +336,7 @@ import { computed, onUnmounted, ref, watch } from 'vue'
|
|
|
336
336
|
try {
|
|
337
337
|
return structuredClone(form.form.value)
|
|
338
338
|
} catch {
|
|
339
|
-
// Non-cloneable values (functions, Symbols, Vue refs, etc.)
|
|
339
|
+
// Non-cloneable values (functions, Symbols, Vue refs, etc.),
|
|
340
340
|
// fall back to the live reference. Worst case: that one entry
|
|
341
341
|
// still shows the current state, same as before this fix.
|
|
342
342
|
return form.form.value
|
|
@@ -379,7 +379,7 @@ import { computed, onUnmounted, ref, watch } from 'vue'
|
|
|
379
379
|
)
|
|
380
380
|
|
|
381
381
|
// Polling fallback for state that changes outside the `onFormChange` /
|
|
382
|
-
// `onSubmitSuccess` / `onReset` event surface
|
|
382
|
+
// `onSubmitSuccess` / `onReset` event surface, user errors injected
|
|
383
383
|
// via `setErrors`, submit-lifecycle flags between events, or
|
|
384
384
|
// new forms registered in the host's registry. 120ms is faster than
|
|
385
385
|
// a human can notice between an input event and a visible panel
|
|
@@ -100,7 +100,7 @@ import { computed, nextTick, ref } from 'vue'
|
|
|
100
100
|
// Array indices arrive as stringified numbers from `Object.entries` /
|
|
101
101
|
// `array.map`. Coerce back to numbers when the parent is an array
|
|
102
102
|
// so `canonicalizePath` on the consumer side gets a structured Path
|
|
103
|
-
// with the right shape
|
|
103
|
+
// with the right shape, `["users", 0, "name"]`, not
|
|
104
104
|
// `["users", "0", "name"]`.
|
|
105
105
|
if (type.value === 'array') return [...path.value, Number(rawKey)]
|
|
106
106
|
return [...path.value, rawKey]
|
|
@@ -159,7 +159,7 @@ import { computed, nextTick, ref } from 'vue'
|
|
|
159
159
|
|
|
160
160
|
// Selection (Field-state inspector hook). Canonical key uses JSON-
|
|
161
161
|
// serialised path-array form so two arrays with the same shape produce
|
|
162
|
-
// the same string
|
|
162
|
+
// the same string, direct equality check.
|
|
163
163
|
const ownKey = computed(() => JSON.stringify(path.value))
|
|
164
164
|
const isSelected = computed(
|
|
165
165
|
() =>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineNuxtPlugin, useRuntimeConfig, useRoute } from 'nuxt/app';
|
|
2
|
-
import { c as createAttaform, b as kAttaformWizardActiveStepResolver, r as renderAttaformState, h as hydrateAttaformState, g as getRegistryFromApp, D as DEVTOOLS_WINDOW_KEY } from '../../shared/attaform.
|
|
2
|
+
import { c as createAttaform, b as kAttaformWizardActiveStepResolver, r as renderAttaformState, h as hydrateAttaformState, g as getRegistryFromApp, D as DEVTOOLS_WINDOW_KEY } from '../../shared/attaform.C4shmwyJ.mjs';
|
|
3
3
|
|
|
4
4
|
var attaform_default = defineNuxtPlugin({
|
|
5
5
|
// `enforce: 'pre'` makes the "we run before any component's setup" claim
|
|
@@ -21,7 +21,7 @@ function pickDefined(bag) {
|
|
|
21
21
|
return out;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
function
|
|
24
|
+
function callConsumerFn(fn, fallback, site, detail) {
|
|
25
25
|
try {
|
|
26
26
|
return fn();
|
|
27
27
|
} catch (err) {
|
|
@@ -355,4 +355,4 @@ function structuralSnapshot(value) {
|
|
|
355
355
|
return out;
|
|
356
356
|
}
|
|
357
357
|
|
|
358
|
-
export { ANONYMOUS_FORM_KEY_PREFIX as A, DEFAULT_HISTORY_MAX_SNAPSHOTS as D, RESERVED_KEY_PREFIX as R, safeOwnRead as a, safeAssign as b,
|
|
358
|
+
export { ANONYMOUS_FORM_KEY_PREFIX as A, DEFAULT_HISTORY_MAX_SNAPSHOTS as D, RESERVED_KEY_PREFIX as R, safeOwnRead as a, safeAssign as b, callConsumerFn as c, safeOwnHas as d, consumerHas as e, spreadConsumerRecord as f, copyConsumerArray as g, consumerKeys as h, isShadowedKey as i, readConsumerProp as j, diffAndApply as k, DEFAULT_FIELD_VALIDATION_DEBOUNCE_MS as l, consumerSymbolKeys as m, normalizeNumericOption as n, applyChangedKeys as o, pickDefined as p, DEFAULT_MAX_RECURSION_DEPTH as q, readConsumerIndex as r, structuralSnapshot as s, ANONYMOUS_WIZARD_KEY_PREFIX as t };
|
|
@@ -18,19 +18,19 @@ import { z } from 'zod';
|
|
|
18
18
|
type SupportedRootSchema = z.ZodObject<z.ZodRawShape> | z.ZodRecord<z.ZodTypeAny, z.ZodTypeAny> | z.ZodDiscriminatedUnion<string, readonly z.ZodDiscriminatedUnionOption<string>[]>;
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
* v3 mirror of
|
|
22
|
-
* to `z.output<Shape[K]
|
|
23
|
-
* Zod's own recursion
|
|
24
|
-
* containers
|
|
25
|
-
*
|
|
26
|
-
* pre-transform input
|
|
27
|
-
*
|
|
21
|
+
* The v3 mirror of Zod v4's `StorageShape`. Each top-level key defaults
|
|
22
|
+
* to `z.output<Shape[K]>`, the post-init view, where defaults have fired
|
|
23
|
+
* and Zod's own recursion has peeled nested defaults inside structural
|
|
24
|
+
* containers. A transform or pipe carrier (`ZodEffects`, `ZodPipeline`)
|
|
25
|
+
* falls back to `z.input<Shape[K]>`, storage there holding the
|
|
26
|
+
* pre-transform input: a transform runs at submission or validation, not
|
|
27
|
+
* at the write boundary.
|
|
28
28
|
*
|
|
29
|
-
* v3 quirk: `ZodEffects` covers BOTH `.transform()` and
|
|
30
|
-
* `z.preprocess()` at the TS level
|
|
31
|
-
*
|
|
32
|
-
* `
|
|
33
|
-
*
|
|
29
|
+
* A v3 quirk sharpens this: `ZodEffects` covers BOTH `.transform()` and
|
|
30
|
+
* `z.preprocess()` at the TS level, v3 carrying no separate preprocess
|
|
31
|
+
* class. So deferring to `z.input` there makes a top-level
|
|
32
|
+
* `z.preprocess(fn, T)` leaf read as the preprocess input, commonly
|
|
33
|
+
* `unknown`; reach for the
|
|
34
34
|
* `AbstractSchema` escape hatch if a stronger type is needed.
|
|
35
35
|
* Transforms preserve their pre-transform input shape, which matches
|
|
36
36
|
* storage.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as resolveZodAliasTarget, i as isRewritableZodSpecifier } from './attaform.
|
|
1
|
+
import { r as resolveZodAliasTarget, i as isRewritableZodSpecifier } from './attaform.BAf3UNn4.mjs';
|
|
2
2
|
|
|
3
3
|
const TAP_NAME = "attaform";
|
|
4
4
|
function createWebpackFamilyPlugin(tag, options) {
|
|
@@ -32,7 +32,7 @@ function missingZodError(tag) {
|
|
|
32
32
|
return `[${tag}] zod is not installed. attaform requires zod as a peer dependency. Install \`zod@^3\` or \`zod@^4\`, OR pass \`attaform({ resolveZodAlias: false })\` to keep the runtime-dispatch unified entry (and silence this check).`;
|
|
33
33
|
}
|
|
34
34
|
function unclassifiableZodWarning(tag) {
|
|
35
|
-
return `[${tag}] Could not classify the installed Zod major (corrupted package.json, monorepo edge case, or an unexpected version string). Falling through to runtime dispatch
|
|
35
|
+
return `[${tag}] Could not classify the installed Zod major (corrupted package.json, monorepo edge case, or an unexpected version string). Falling through to runtime dispatch, so both Zod adapters will ship in the bundle. Pass \`attaform({ resolveZodAlias: false })\` to silence this warning.`;
|
|
36
36
|
}
|
|
37
37
|
function resolveZodAliasTarget(consumerRootDir, tag, resolveZodAlias, warnState) {
|
|
38
38
|
if (!resolveZodAlias) return null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { j as humanize, c as useAbstractForm } from './attaform.
|
|
1
|
+
import { j as humanize, c as useAbstractForm } from './attaform.DNPJPOO7.mjs';
|
|
2
2
|
import { A as AttaformError, e as canonicalizePath, a as InvalidUseFormConfigError } from './attaform.CdZIEf7_.mjs';
|
|
3
|
-
import { s as slimPrimitivesWalk, d as deriveDefaultWalk, m as mergeDeep, f as fixStructuralDefaults, w as walkPathSegments, c as createAbstractSchema, a as createSharedSchemaStore, b as buildFieldMetaPathMap, e as getFieldMetaListForSchema, g as getFieldMetaForSchema } from './attaform.
|
|
4
|
-
import { c as
|
|
3
|
+
import { s as slimPrimitivesWalk, d as deriveDefaultWalk, m as mergeDeep, f as fixStructuralDefaults, w as walkPathSegments, c as createAbstractSchema, a as createSharedSchemaStore, b as buildFieldMetaPathMap, e as getFieldMetaListForSchema, g as getFieldMetaForSchema } from './attaform.DL2nl_QR.mjs';
|
|
4
|
+
import { c as callConsumerFn } from './attaform.8mfKZCwH.mjs';
|
|
5
5
|
|
|
6
6
|
function readDef(schema) {
|
|
7
7
|
if (schema === null || typeof schema !== "object") return void 0;
|
|
@@ -119,9 +119,7 @@ function isCoercePrimitive(schema) {
|
|
|
119
119
|
return readDef(schema)?.coerce === true;
|
|
120
120
|
}
|
|
121
121
|
function isPreprocessNode(schema) {
|
|
122
|
-
|
|
123
|
-
const pipeIn = unwrapPipeIn(schema);
|
|
124
|
-
return pipeIn !== void 0 && kindOf(pipeIn) === "transform";
|
|
122
|
+
return kindOf(schema) === "pipe" && kindOf(readDef(schema)?.in) === "transform";
|
|
125
123
|
}
|
|
126
124
|
function unwrapPipeOut(schema) {
|
|
127
125
|
const def = readDef(schema);
|
|
@@ -131,7 +129,7 @@ function unwrapLazy(schema) {
|
|
|
131
129
|
const def = readDef(schema);
|
|
132
130
|
const getter = def?.getter;
|
|
133
131
|
if (typeof getter !== "function") return void 0;
|
|
134
|
-
return
|
|
132
|
+
return callConsumerFn(() => getter(), void 0);
|
|
135
133
|
}
|
|
136
134
|
function getIntersectionLeft(schema) {
|
|
137
135
|
const def = readDef(schema);
|
|
@@ -145,7 +143,7 @@ function getCatchDefault(schema) {
|
|
|
145
143
|
const def = readDef(schema);
|
|
146
144
|
const cv = def?.catchValue;
|
|
147
145
|
if (typeof cv !== "function") return void 0;
|
|
148
|
-
return
|
|
146
|
+
return callConsumerFn(
|
|
149
147
|
() => cv({ error: new Error("atta:default-values"), input: void 0 }),
|
|
150
148
|
void 0);
|
|
151
149
|
}
|
|
@@ -155,7 +153,7 @@ function hasCatchValue(schema) {
|
|
|
155
153
|
}
|
|
156
154
|
function getDefaultValue(schema) {
|
|
157
155
|
const def = readDef(schema);
|
|
158
|
-
return
|
|
156
|
+
return callConsumerFn(() => def?.defaultValue, void 0);
|
|
159
157
|
}
|
|
160
158
|
function getNativeEnumValues(_schema) {
|
|
161
159
|
return void 0;
|
|
@@ -200,60 +198,6 @@ const DESCEND_SINGLE = [
|
|
|
200
198
|
];
|
|
201
199
|
const DESCEND_RECORD = ["shape", "entries"];
|
|
202
200
|
const DESCEND_LIST = ["options", "items"];
|
|
203
|
-
function walkSchemaTree(schema, visit, seen) {
|
|
204
|
-
const visited = seen ?? /* @__PURE__ */ new WeakSet();
|
|
205
|
-
const candidate = schema;
|
|
206
|
-
if (typeof candidate !== "object" || candidate === null) return false;
|
|
207
|
-
if (visited.has(candidate)) return false;
|
|
208
|
-
visited.add(candidate);
|
|
209
|
-
if (visit(schema)) return true;
|
|
210
|
-
const def = readDef(schema);
|
|
211
|
-
if (def === void 0) return false;
|
|
212
|
-
const slots = def;
|
|
213
|
-
for (const key of DESCEND_SINGLE) {
|
|
214
|
-
const child = slots[key];
|
|
215
|
-
if (child !== void 0 && walkSchemaTree(child, visit, visited)) return true;
|
|
216
|
-
}
|
|
217
|
-
for (const key of DESCEND_RECORD) {
|
|
218
|
-
const record = slots[key];
|
|
219
|
-
if (record !== void 0) {
|
|
220
|
-
for (const sub of Object.values(record)) {
|
|
221
|
-
if (walkSchemaTree(sub, visit, visited)) return true;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
for (const key of DESCEND_LIST) {
|
|
226
|
-
const list = slots[key];
|
|
227
|
-
if (list !== void 0) {
|
|
228
|
-
for (const sub of list) {
|
|
229
|
-
if (walkSchemaTree(sub, visit, visited)) return true;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
if (typeof def.getter === "function") {
|
|
234
|
-
try {
|
|
235
|
-
const inner = def.getter();
|
|
236
|
-
if (walkSchemaTree(inner, visit, visited)) return true;
|
|
237
|
-
} catch {
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
return false;
|
|
241
|
-
}
|
|
242
|
-
function containsDiscriminatedUnion(schema, seen) {
|
|
243
|
-
return walkSchemaTree(schema, (node) => kindOf(node) === "discriminated-union", seen);
|
|
244
|
-
}
|
|
245
|
-
function containsAsyncRefine(schema, seen) {
|
|
246
|
-
return walkSchemaTree(
|
|
247
|
-
schema,
|
|
248
|
-
(node) => {
|
|
249
|
-
for (const check of getChecks(node)) {
|
|
250
|
-
if (isAsyncCheck(check)) return true;
|
|
251
|
-
}
|
|
252
|
-
return false;
|
|
253
|
-
},
|
|
254
|
-
seen
|
|
255
|
-
);
|
|
256
|
-
}
|
|
257
201
|
const CONTAINER_SLOTS = [
|
|
258
202
|
"shape",
|
|
259
203
|
"entries",
|
|
@@ -265,31 +209,55 @@ const CONTAINER_SLOTS = [
|
|
|
265
209
|
"left",
|
|
266
210
|
"right"
|
|
267
211
|
];
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
);
|
|
212
|
+
const capabilityMemo = /* @__PURE__ */ new WeakMap();
|
|
213
|
+
function capabilitiesOf(schema) {
|
|
214
|
+
let capabilities = capabilityMemo.get(schema);
|
|
215
|
+
if (capabilities === void 0) {
|
|
216
|
+
capabilities = scanCapabilities(schema);
|
|
217
|
+
const key = schema;
|
|
218
|
+
if (typeof key === "object" && key !== null) capabilityMemo.set(key, capabilities);
|
|
219
|
+
}
|
|
220
|
+
return capabilities;
|
|
221
|
+
}
|
|
222
|
+
function scanCapabilities(schema) {
|
|
223
|
+
const capabilities = {
|
|
224
|
+
asyncRefine: false,
|
|
225
|
+
asyncTransform: false,
|
|
226
|
+
discriminatedUnion: false,
|
|
227
|
+
containerOrRootRefine: false,
|
|
228
|
+
preprocessOrCoerce: false
|
|
229
|
+
};
|
|
230
|
+
walkCapabilities(schema, capabilities, /* @__PURE__ */ new WeakSet());
|
|
231
|
+
return capabilities;
|
|
232
|
+
}
|
|
233
|
+
function walkCapabilities(node, capabilities, visited) {
|
|
234
|
+
if (typeof node !== "object" || node === null || visited.has(node)) return;
|
|
235
|
+
visited.add(node);
|
|
236
|
+
const def = readDef(node);
|
|
237
|
+
if (def === void 0) return;
|
|
238
|
+
const checks = getChecks(node);
|
|
239
|
+
if (checks.some(isAsyncCheck)) capabilities.asyncRefine = true;
|
|
240
|
+
if (checks.length > 0 && CONTAINER_SLOTS.some((key) => def[key] !== void 0)) {
|
|
241
|
+
capabilities.containerOrRootRefine = true;
|
|
242
|
+
}
|
|
243
|
+
if (typeof def.transform === "function" && def.transform.constructor.name === "AsyncFunction") {
|
|
244
|
+
capabilities.asyncTransform = true;
|
|
245
|
+
}
|
|
246
|
+
if (kindOf(node) === "discriminated-union") capabilities.discriminatedUnion = true;
|
|
247
|
+
if (isCoercePrimitive(node) || isPreprocessNode(node)) capabilities.preprocessOrCoerce = true;
|
|
248
|
+
for (const key of DESCEND_SINGLE) walkCapabilities(def[key], capabilities, visited);
|
|
249
|
+
for (const key of DESCEND_RECORD) {
|
|
250
|
+
for (const sub of Object.values(def[key] ?? {})) walkCapabilities(sub, capabilities, visited);
|
|
251
|
+
}
|
|
252
|
+
for (const key of DESCEND_LIST) {
|
|
253
|
+
for (const sub of def[key] ?? []) walkCapabilities(sub, capabilities, visited);
|
|
254
|
+
}
|
|
255
|
+
if (typeof def.getter === "function") {
|
|
256
|
+
try {
|
|
257
|
+
walkCapabilities(def.getter(), capabilities, visited);
|
|
258
|
+
} catch {
|
|
259
|
+
}
|
|
260
|
+
}
|
|
293
261
|
}
|
|
294
262
|
function isAsyncCheck(check) {
|
|
295
263
|
if (typeof check !== "object" || check === null) return false;
|
|
@@ -335,10 +303,10 @@ function zodIssuesToValidationErrors(issues) {
|
|
|
335
303
|
}
|
|
336
304
|
return {
|
|
337
305
|
message: issue.message,
|
|
338
|
-
// Adapter-side paths stay schema-
|
|
339
|
-
//
|
|
340
|
-
//
|
|
341
|
-
//
|
|
306
|
+
// Adapter-side paths stay schema-RELATIVE: the validation pipeline
|
|
307
|
+
// in `core/create-form-store.ts` prepends the parent path to
|
|
308
|
+
// absolutise, then routes form-level entries, those whose absolute
|
|
309
|
+
// path is empty, to the empty-string bucket at storage time.
|
|
342
310
|
path: issue.path.map((seg) => typeof seg === "number" ? seg : String(seg)),
|
|
343
311
|
code
|
|
344
312
|
};
|
|
@@ -354,10 +322,7 @@ const V4_INTROSPECTOR = {
|
|
|
354
322
|
getLiteralValues,
|
|
355
323
|
isPreprocessNode,
|
|
356
324
|
isCoercePrimitive,
|
|
357
|
-
|
|
358
|
-
containsAsyncTransform,
|
|
359
|
-
containsDiscriminatedUnion,
|
|
360
|
-
hasContainerOrRootRefine,
|
|
325
|
+
capabilitiesOf,
|
|
361
326
|
// Walker accessors (D2 / D3 / D5).
|
|
362
327
|
getArrayElement: (schema) => {
|
|
363
328
|
if (kindOf(schema) !== "array") return void 0;
|
|
@@ -384,7 +349,7 @@ const V4_INTROSPECTOR = {
|
|
|
384
349
|
};
|
|
385
350
|
|
|
386
351
|
function slimPrimitivesOf(schema, maxRecursionDepth) {
|
|
387
|
-
return
|
|
352
|
+
return slimPrimitivesWalk(schema, V4_INTROSPECTOR, maxRecursionDepth);
|
|
388
353
|
}
|
|
389
354
|
|
|
390
355
|
function deriveDefault(schema, useDefault, maxRecursionDepth) {
|
|
@@ -533,13 +498,14 @@ const sharedV4Schemas = createSharedSchemaStore();
|
|
|
533
498
|
function buildV4Services() {
|
|
534
499
|
return {
|
|
535
500
|
getNestedSchemasAtPath: (schema, path, maxRecursionDepth) => getNestedZodSchemasAtPath(schema, path, maxRecursionDepth),
|
|
536
|
-
//
|
|
537
|
-
//
|
|
538
|
-
//
|
|
501
|
+
// No pre-strip for the slim-mode walk: the path walker peels every
|
|
502
|
+
// transparent wrapper inline, so the slim and unstripped walks
|
|
503
|
+
// coincide.
|
|
539
504
|
getNestedSchemasInSlimMode: (schema, path, maxRecursionDepth) => getNestedZodSchemasAtPath(schema, path, maxRecursionDepth),
|
|
540
505
|
slimPrimitivesOf: (schema, maxRecursionDepth) => slimPrimitivesOf(schema, maxRecursionDepth),
|
|
541
506
|
deriveDefault: (schema, useDefault, maxRecursionDepth) => deriveDefault(schema, useDefault, maxRecursionDepth),
|
|
542
507
|
runGetDefaults: (schema, config, maxRecursionDepth) => runGetDefaultsV4(schema, config, maxRecursionDepth),
|
|
508
|
+
validateSync: (schema, data) => validateSyncV4(schema, data),
|
|
543
509
|
unwrapStructuralWrappers: (schema) => unwrapStructuralWrappers(schema),
|
|
544
510
|
unwrapToDiscriminatedUnion: (schema) => unwrapToDiscriminatedUnion(schema),
|
|
545
511
|
peelAllWrappers: (schema) => peelAllWrappers(schema),
|
|
@@ -558,37 +524,35 @@ function buildV4Services() {
|
|
|
558
524
|
};
|
|
559
525
|
}
|
|
560
526
|
function runGetDefaultsV4(rootSchema, config, maxRecursionDepth) {
|
|
561
|
-
|
|
527
|
+
return getDefaultValuesFromZodSchema({
|
|
562
528
|
schema: rootSchema,
|
|
563
529
|
useDefaultSchemaValues: config.useDefaultSchemaValues,
|
|
564
530
|
constraints: config.constraints,
|
|
565
531
|
maxRecursionDepth
|
|
566
|
-
});
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
532
|
+
}).data;
|
|
533
|
+
}
|
|
534
|
+
const verdictRan = /* @__PURE__ */ new WeakMap();
|
|
535
|
+
function validateSyncV4(rootSchema, data) {
|
|
536
|
+
const { asyncRefine, asyncTransform } = capabilitiesOf(rootSchema);
|
|
537
|
+
if (asyncRefine || asyncTransform) return [];
|
|
570
538
|
try {
|
|
571
|
-
const
|
|
572
|
-
if (
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
return {
|
|
576
|
-
data,
|
|
577
|
-
errors: zodIssuesToValidationErrors(parseResult.error.issues),
|
|
578
|
-
success: false
|
|
579
|
-
};
|
|
539
|
+
const jitless = !verdictRan.has(rootSchema);
|
|
540
|
+
if (jitless) verdictRan.set(rootSchema, true);
|
|
541
|
+
const parseResult = rootSchema.safeParse(data, { jitless });
|
|
542
|
+
return parseResult.success ? [] : zodIssuesToValidationErrors(parseResult.error.issues);
|
|
580
543
|
} catch {
|
|
581
|
-
return
|
|
544
|
+
return [];
|
|
582
545
|
}
|
|
583
546
|
}
|
|
584
547
|
function buildSubSchemaStubV4(schema, maxRecursionDepth) {
|
|
585
548
|
return {
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
}
|
|
549
|
+
// Async work of either kind, as the root's `needsAsyncValidation`
|
|
550
|
+
// counts it: the field's `valid` waits for the first async pass.
|
|
551
|
+
needsAsyncValidation: () => {
|
|
552
|
+
const { asyncRefine, asyncTransform } = scanCapabilities(schema);
|
|
553
|
+
return asyncRefine || asyncTransform;
|
|
554
|
+
},
|
|
555
|
+
getDefaultValues: () => deriveDefault(schema, true, maxRecursionDepth),
|
|
592
556
|
getSchemasAtPath: () => [],
|
|
593
557
|
validateAtPath: async (data) => {
|
|
594
558
|
const result = await schema.safeParseAsync(data);
|
|
@@ -168,8 +168,8 @@ function renderAttaformState(app) {
|
|
|
168
168
|
// The wire shape stays two per-source arrays; the tagged store
|
|
169
169
|
// splits at the boundary and the client replays each side into
|
|
170
170
|
// its cell half.
|
|
171
|
-
schemaErrors: [...state.errorCells.entries()].filter(([, cell]) => cell.schema.length > 0).map(([key2, cell]) => [key2, cell.schema]),
|
|
172
|
-
userErrors: [...state.errorCells.entries()].filter(([, cell]) => cell.user.length > 0).map(([key2, cell]) => [key2, cell.user]),
|
|
171
|
+
schemaErrors: [...state.errorCells().entries()].filter(([, cell]) => cell.schema.length > 0).map(([key2, cell]) => [key2, cell.schema]),
|
|
172
|
+
userErrors: [...state.errorCells().entries()].filter(([, cell]) => cell.user.length > 0).map(([key2, cell]) => [key2, cell.user]),
|
|
173
173
|
fields: Array.from(state.fields.entries()),
|
|
174
174
|
...transientList.length > 0 ? { blankPaths: transientList } : {}
|
|
175
175
|
}
|